Snap for 6479434 from ec8543f174757776c0f57b8544613abc0a351010 to mainline-release

Change-Id: I0eb755b4e5781327cc3a736d65bd849cd37b0930
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..c772c32
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,26 @@
+# Force LF checkout for all source files
+*.bin binary
+*.c text eol=lf
+*.cc text eol=lf
+*.cpp text eol=lf
+*.gn text eol=lf
+*.gni text eol=lf
+*.h text eol=lf
+*.in text eol=lf
+*.js text eol=lf
+*.md text eol=lf
+*.mm text eol=lf
+*.sh text eol=lf
+*.txt text eol=lf
+*.xml text eol=lf
+.clang-format text eol=lf
+.gitattributes text eol=lf
+.gitignore text eol=lf
+.vpython text eol=lf
+codereview.settings text eol=lf
+DEPS text eol=lf
+LICENSE text eol=lf
+OWNERS text eol=lf
+
+# Skip Tricium on expectation files.
+*_expected.txt -tricium
diff --git a/.gn b/.gn
index 41187fd..c974357 100644
--- a/.gn
+++ b/.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 buildconfig = "//build/config/BUILDCONFIG.gn"
-secondary_source = "//build/secondary/"
 
 default_args = {
   v8_extra_library_files = []
@@ -11,7 +10,26 @@
 
   # Turns on compiler optimizations in V8 in Debug build.
   v8_optimized_debug = true
-
-  # PDFium does not want to switch to C++14 yet.
-  use_cxx11 = true
 }
+
+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/*",
+]
diff --git a/.vpython b/.vpython
new file mode 100644
index 0000000..efe51d4
--- /dev/null
+++ b/.vpython
@@ -0,0 +1,38 @@
+# 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/AUTHORS b/AUTHORS
index ee6fc93..3c97237 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -19,6 +19,8 @@
 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>
@@ -31,19 +33,27 @@
 Matt Giuca <mgiuca@chromium.org>
 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>
+Ralf Sippl <ralf.sippl@gmail.com>
 Raymes Khoury <raymes@chromium.org>
 Reid Kleckner <rnk@chromium.org>
+Ryan Harrison <rharrison@chromium.org>
 Ryan Wiley <wileyrr@gmail.com>
 Robert Sesek <rsesek@chromium.org>
 Sam Clegg <sbc@chromium.org>
 Thomas Sepez <tsepez@chromium.org>
 Wang Qing <wangqing-hf@loongson.cn>
+Zhuo Qingliang <zhuo.dev@gmail.com>
 
 Collabora Ltd. <*@collabora.co.uk>
 DocsCorp Pty Ltd. <*@docscorp.com>
+Dropbox <*@dropbox.com>
 Foxit Software Inc <*@foxitsoftware.com>
 Google Inc. <*@google.com>
 LG Electronics, Inc. <*@lge.com>
 Loongson Technology Corporation Limited. <*@loongson.cn>
+Microsoft <*@microsoft.com>
+PSPDFKit GmbH <*@pspdfkit.com>
diff --git a/Android.bp b/Android.bp
index 550d934..5759212 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6,22 +6,16 @@
         "-fprefetch-loop-arrays",
         "-fexceptions",
 
-        "-Wextra",
-        "-Wall",
-        "-Wno-non-virtual-dtor",
-        "-Wno-null-pointer-arithmetic",
-        "-Wno-unused-parameter",
-        "-Wno-sign-compare",
-        "-Wno-missing-field-initializers",
         "-Wno-implicit-fallthrough",
-        // cpdf_renderstatus.cpp:1768, cpdf_variabletext.cpp:320,
-        // and cpwl_edit_impl.cpp:1825 have -Wimplicit-fallthrough.
-        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-non-virtual-dtor",
+        "-Wno-unused-parameter",
 
         // The new pass manager causes non-deterministic build result.
         // http://b/135660678
         "-fno-experimental-new-pass-manager",
 
+        // pdfium_common_config
         "-DOPJ_STATIC",
         "-DPNG_PREFIX",
         "-DPNG_USE_READ_MACROS",
@@ -33,6 +27,10 @@
         },
     },
 
+    include_dirs: [
+        "external/freetype/include",
+    ],
+
     header_libs: [
         "pdfium-headers",
         "pdfium-third-party-headers"
@@ -41,13 +39,15 @@
 
 cc_defaults {
     name: "pdfium-core",
-    cflags: [
-        "-DV8_DEPRECATION_WARNINGS",
-    ],
 
     defaults: [
         "pdfium-common"
-    ]
+    ],
+
+    exclude_srcs: [
+        "**/*_unittest.cpp",
+        "**/*_embeddertest.cpp",
+    ],
 }
 
 
@@ -61,19 +61,54 @@
     export_include_dirs: ["third_party"],
 }
 
-build = [
-    "pdfiumfdrm.bp",
-    "pdfiumfpdfapi.bp",
-    "pdfiumfpdfdoc.bp",
-    "pdfiumfpdftext.bp",
-    "pdfiumfxcodec.bp",
-    "pdfiumfxcrt.bp",
-    "pdfiumfxge.bp",
-    "pdfiumpwl.bp",
-    "pdfiumformfiller.bp",
+cc_library_shared {
+    name: "libpdfium",
+    defaults: ["pdfium-core"],
 
-    "pdfiumfxjs.bp",
-    "pdfium.bp",
-]
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    whole_static_libs: [
+        "libpdfium-fpdfsdk",
+    ],
+
+    // Transitivity is not supported for static libraries (yet).
+    // Lists the whole transitivity closure here.
+    static_libs: [
+        "libpdfium-agg",
+        "libpdfium-cmaps",
+        "libpdfium-edit",
+        "libpdfium-fdrm",
+        "libpdfium-font",
+        "libpdfium-formfiller",
+        "libpdfium-fpdfdoc",
+        "libpdfium-fpdftext",
+        "libpdfium-fxcodec",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+        "libpdfium-fxjs",
+        "libpdfium-libopenjpeg2",
+        "libpdfium-page",
+        "libpdfium-parser",
+        "libpdfium-pwl",
+        "libpdfium-render",
+        "libpdfium-skia_shared",
+        "libpdfium-third_party-base",
+        "libpdfium-lcms2",
+    ],
+
+    // TODO: figure out why turning on exceptions requires manually linking libdl
+    shared_libs: [
+        "libandroidicu",
+        "libdl",
+        "libft2",
+        "libjpeg",
+        "libz",
+    ],
+
+    export_include_dirs: ["public"],
+
+}
 
 subdirs = ["third_party"]
diff --git a/BUILD.gn b/BUILD.gn
index c771b81..62f09e5 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/freetype/freetype.gni")
-import("//build/config/jumbo.gni")
 import("//testing/test.gni")
 import("pdfium.gni")
 
@@ -21,29 +19,16 @@
   ldflags = []
   include_dirs = [ "." ]
   defines = [
-    "OPJ_STATIC",
     "PNG_PREFIX",
     "PNG_USE_READ_MACROS",
   ]
 
-  if (pdf_enable_v8) {
-    defines += [ "PDF_ENABLE_V8" ]
+  if (!use_system_libopenjpeg2) {
+    defines += [ "OPJ_STATIC" ]
   }
 
-  if (pdf_enable_xfa) {
-    defines += [ "PDF_ENABLE_XFA" ]
-    if (pdf_enable_xfa_bmp) {
-      defines += [ "PDF_ENABLE_XFA_BMP" ]
-    }
-    if (pdf_enable_xfa_gif) {
-      defines += [ "PDF_ENABLE_XFA_GIF" ]
-    }
-    if (pdf_enable_xfa_png) {
-      defines += [ "PDF_ENABLE_XFA_PNG" ]
-    }
-    if (pdf_enable_xfa_tiff) {
-      defines += [ "PDF_ENABLE_XFA_TIFF" ]
-    }
+  if (pdf_enable_click_logging) {
+    defines += [ "PDF_ENABLE_CLICK_LOGGING" ]
   }
 
   if (pdf_use_skia) {
@@ -54,29 +39,53 @@
     defines += [ "_SKIA_SUPPORT_PATHS_" ]
   }
 
-  if (pdf_use_win32_gdi) {
-    defines += [ "PDFIUM_PRINT_TEXT_WITH_GDI" ]
-  }
-
-  if (use_coverage && is_clang) {
-    cflags += [
-      "--coverage",
-      "-g",
-      "-O0",
-    ]
-    ldflags += [ "--coverage" ]
-  }
-
   if (is_win) {
     # Assume UTF-8 by default to avoid code page dependencies.
     cflags += [ "/utf-8" ]
   }
 }
 
+config("pdfium_implementation_config") {
+  defines = [ "FPDF_IMPLEMENTATION" ]
+  visibility = [ ":pdfium_public_headers" ]
+}
+
+config("pdfium_public_config") {
+  defines = []
+
+  if (pdf_enable_v8) {
+    defines += [ "PDF_ENABLE_V8" ]
+
+    if (pdf_enable_xfa) {
+      defines += [ "PDF_ENABLE_XFA" ]
+      if (pdf_enable_xfa_bmp) {
+        defines += [ "PDF_ENABLE_XFA_BMP" ]
+      }
+      if (pdf_enable_xfa_gif) {
+        defines += [ "PDF_ENABLE_XFA_GIF" ]
+      }
+      if (pdf_enable_xfa_png) {
+        defines += [ "PDF_ENABLE_XFA_PNG" ]
+      }
+      if (pdf_enable_xfa_tiff) {
+        defines += [ "PDF_ENABLE_XFA_TIFF" ]
+      }
+    }
+  }
+
+  if (pdf_use_win32_gdi) {
+    defines += [ "PDFIUM_PRINT_TEXT_WITH_GDI" ]
+  }
+}
+
 config("pdfium_core_config") {
   cflags = []
-  configs = [ ":pdfium_common_config" ]
-  defines = [ "V8_DEPRECATION_WARNINGS" ]
+  configs = [
+    ":pdfium_common_config",
+    ":pdfium_public_config",
+    "//build/config/compiler:noshadowing",
+  ]
+  defines = []
   if (is_linux) {
     if (current_cpu == "x64") {
       defines += [ "_FX_CPU_=_FX_X64_" ]
@@ -87,78 +96,17 @@
   }
   if (is_win) {
     cflags += [
-      "/wd4267",
       "/wd4324",
       "/wd4577",
     ]
   }
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
-config("xfa_warnings") {
-  visibility = [ ":*" ]
-  if (is_posix && !is_clang) {  # When GCC.
-    cflags = [ "-Wno-strict-overflow" ]
-  }
-}
-
-jumbo_static_library("pdfium") {
+source_set("pdfium_public_headers_impl") {
   sources = [
-    "fpdfsdk/cba_annotiterator.cpp",
-    "fpdfsdk/cba_annotiterator.h",
-    "fpdfsdk/cfx_systemhandler.cpp",
-    "fpdfsdk/cfx_systemhandler.h",
-    "fpdfsdk/cpdfsdk_annot.cpp",
-    "fpdfsdk/cpdfsdk_annot.h",
-    "fpdfsdk/cpdfsdk_annothandlermgr.cpp",
-    "fpdfsdk/cpdfsdk_annothandlermgr.h",
-    "fpdfsdk/cpdfsdk_annotiteration.cpp",
-    "fpdfsdk/cpdfsdk_annotiteration.h",
-    "fpdfsdk/cpdfsdk_baannot.cpp",
-    "fpdfsdk/cpdfsdk_baannot.h",
-    "fpdfsdk/cpdfsdk_baannothandler.cpp",
-    "fpdfsdk/cpdfsdk_baannothandler.h",
-    "fpdfsdk/cpdfsdk_datetime.cpp",
-    "fpdfsdk/cpdfsdk_datetime.h",
-    "fpdfsdk/cpdfsdk_formfillenvironment.cpp",
-    "fpdfsdk/cpdfsdk_formfillenvironment.h",
-    "fpdfsdk/cpdfsdk_interform.cpp",
-    "fpdfsdk/cpdfsdk_interform.h",
-    "fpdfsdk/cpdfsdk_pageview.cpp",
-    "fpdfsdk/cpdfsdk_pageview.h",
-    "fpdfsdk/cpdfsdk_widget.cpp",
-    "fpdfsdk/cpdfsdk_widget.h",
-    "fpdfsdk/cpdfsdk_widgethandler.cpp",
-    "fpdfsdk/cpdfsdk_widgethandler.h",
-    "fpdfsdk/fpdf_dataavail.cpp",
-    "fpdfsdk/fpdf_ext.cpp",
-    "fpdfsdk/fpdf_flatten.cpp",
-    "fpdfsdk/fpdf_progressive.cpp",
-    "fpdfsdk/fpdf_searchex.cpp",
-    "fpdfsdk/fpdf_structtree.cpp",
-    "fpdfsdk/fpdf_sysfontinfo.cpp",
-    "fpdfsdk/fpdf_transformpage.cpp",
-    "fpdfsdk/fpdfannot.cpp",
-    "fpdfsdk/fpdfattachment.cpp",
-    "fpdfsdk/fpdfcatalog.cpp",
-    "fpdfsdk/fpdfdoc.cpp",
-    "fpdfsdk/fpdfeditimg.cpp",
-    "fpdfsdk/fpdfeditpage.cpp",
-    "fpdfsdk/fpdfeditpath.cpp",
-    "fpdfsdk/fpdfedittext.cpp",
-    "fpdfsdk/fpdfformfill.cpp",
-    "fpdfsdk/fpdfppo.cpp",
-    "fpdfsdk/fpdfsave.cpp",
-    "fpdfsdk/fpdftext.cpp",
-    "fpdfsdk/fpdfview.cpp",
-    "fpdfsdk/fsdk_actionhandler.cpp",
-    "fpdfsdk/fsdk_actionhandler.h",
-    "fpdfsdk/fsdk_filewriteadapter.cpp",
-    "fpdfsdk/fsdk_filewriteadapter.h",
-    "fpdfsdk/fsdk_pauseadapter.cpp",
-    "fpdfsdk/fsdk_pauseadapter.h",
-    "fpdfsdk/pdfsdk_fieldaction.cpp",
-    "fpdfsdk/pdfsdk_fieldaction.h",
     "public/cpp/fpdf_deleters.h",
+    "public/cpp/fpdf_scopers.h",
     "public/fpdf_annot.h",
     "public/fpdf_attachment.h",
     "public/fpdf_catalog.h",
@@ -169,6 +117,7 @@
     "public/fpdf_flatten.h",
     "public/fpdf_formfill.h",
     "public/fpdf_fwlevent.h",
+    "public/fpdf_javascript.h",
     "public/fpdf_ppo.h",
     "public/fpdf_progressive.h",
     "public/fpdf_save.h",
@@ -179,37 +128,47 @@
     "public/fpdf_transformpage.h",
     "public/fpdfview.h",
   ]
+}
 
+group("pdfium_public_headers") {
+  public_deps = [ ":pdfium_public_headers_impl" ]
+  public_configs = [
+    ":pdfium_public_config",
+    ":pdfium_implementation_config",
+  ]
+}
+
+component("pdfium") {
   libs = []
   configs += [ ":pdfium_core_config" ]
+  public_configs = [ ":pdfium_public_config" ]
 
   deps = [
-    ":fdrm",
-    ":formfiller",
-    ":fpdfapi",
-    ":fpdfdoc",
-    ":fpdftext",
-    ":fxcodec",
-    ":fxcrt",
-    ":fxge",
-    ":fxjs",
-    ":pwl",
-    "third_party:bigint",
+    "constants",
+    "core/fpdfapi/page",
+    "core/fpdfapi/parser",
+    "core/fpdfdoc",
+    "core/fxcodec",
+    "core/fxcrt",
+    "core/fxge",
+    "fpdfsdk",
+    "fpdfsdk/formfiller",
+    "fxjs",
     "third_party:pdfium_base",
+    "third_party:skia_shared",
   ]
 
   public_deps = [
-    ":fxcrt",
+    ":pdfium_public_headers_impl",
+    "core/fxcrt",
   ]
-  if (pdf_enable_xfa) {
-    sources += [
-      "fpdfsdk/cpdfsdk_xfawidget.cpp",
-      "fpdfsdk/cpdfsdk_xfawidget.h",
-      "fpdfsdk/cpdfsdk_xfawidgethandler.cpp",
-      "fpdfsdk/cpdfsdk_xfawidgethandler.h",
-    ]
 
-    deps += [ ":fpdfxfa" ]
+  if (pdf_enable_xfa) {
+    deps += [
+      "fpdfsdk/fpdfxfa",
+      "xfa/fxfa",
+      "xfa/fxfa/parser",
+    ]
   }
 
   if (is_win) {
@@ -228,53 +187,13 @@
   }
 
   if (pdf_is_complete_lib) {
+    static_component_type = "static_library"
     complete_static_lib = true
+    configs -= [ "//build/config/compiler:thin_archive" ]
   }
-}
 
-jumbo_static_library("test_support") {
-  testonly = true
-  sources = [
-    "testing/fx_string_testhelpers.cpp",
-    "testing/fx_string_testhelpers.h",
-    "testing/test_support.cpp",
-    "testing/test_support.h",
-    "testing/utils/path_service.cpp",
-    "testing/utils/path_service.h",
-  ]
-  data = [
-    "testing/resources/",
-  ]
-  deps = [
-    ":pdfium",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-  include_dirs = []
-  if (pdf_enable_v8) {
-    deps += [
-      "//v8",
-      "//v8:v8_libplatform",
-    ]
-    include_dirs += [
-      "//v8",
-      "//v8/include",
-    ]
-    configs += [ "//v8:external_startup_data" ]
-  }
-  configs += [ ":pdfium_core_config" ]
-}
-
-jumbo_static_library("image_diff") {
-  testonly = true
-  sources = [
-    "testing/image_diff/image_diff_png.cpp",
-    "testing/image_diff/image_diff_png.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = []
-  if (!pdf_enable_xfa) {
-    deps += [ "third_party:png" ]
+  if (is_component_build) {
+    deps += [ "testing/fuzzers:fuzzer_impls" ]
   }
 }
 
@@ -285,2723 +204,170 @@
   "//:gn_visibility",
 ]
 
-jumbo_static_library("fdrm") {
-  sources = [
-    "core/fdrm/crypto/fx_crypt.cpp",
-    "core/fdrm/crypto/fx_crypt.h",
-    "core/fdrm/crypto/fx_crypt_aes.cpp",
-    "core/fdrm/crypto/fx_crypt_sha.cpp",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = [
-    ":fxcrt",
-  ]
-}
-
-jumbo_static_library("fpdfdoc") {
-  sources = [
-    "core/fpdfdoc/cline.cpp",
-    "core/fpdfdoc/cline.h",
-    "core/fpdfdoc/cpdf_aaction.cpp",
-    "core/fpdfdoc/cpdf_aaction.h",
-    "core/fpdfdoc/cpdf_action.cpp",
-    "core/fpdfdoc/cpdf_action.h",
-    "core/fpdfdoc/cpdf_actionfields.cpp",
-    "core/fpdfdoc/cpdf_actionfields.h",
-    "core/fpdfdoc/cpdf_annot.cpp",
-    "core/fpdfdoc/cpdf_annot.h",
-    "core/fpdfdoc/cpdf_annotlist.cpp",
-    "core/fpdfdoc/cpdf_annotlist.h",
-    "core/fpdfdoc/cpdf_apsettings.cpp",
-    "core/fpdfdoc/cpdf_apsettings.h",
-    "core/fpdfdoc/cpdf_bookmark.cpp",
-    "core/fpdfdoc/cpdf_bookmark.h",
-    "core/fpdfdoc/cpdf_bookmarktree.cpp",
-    "core/fpdfdoc/cpdf_bookmarktree.h",
-    "core/fpdfdoc/cpdf_defaultappearance.cpp",
-    "core/fpdfdoc/cpdf_defaultappearance.h",
-    "core/fpdfdoc/cpdf_dest.cpp",
-    "core/fpdfdoc/cpdf_dest.h",
-    "core/fpdfdoc/cpdf_docjsactions.cpp",
-    "core/fpdfdoc/cpdf_docjsactions.h",
-    "core/fpdfdoc/cpdf_filespec.cpp",
-    "core/fpdfdoc/cpdf_filespec.h",
-    "core/fpdfdoc/cpdf_formcontrol.cpp",
-    "core/fpdfdoc/cpdf_formcontrol.h",
-    "core/fpdfdoc/cpdf_formfield.cpp",
-    "core/fpdfdoc/cpdf_formfield.h",
-    "core/fpdfdoc/cpdf_iconfit.cpp",
-    "core/fpdfdoc/cpdf_iconfit.h",
-    "core/fpdfdoc/cpdf_interform.cpp",
-    "core/fpdfdoc/cpdf_interform.h",
-    "core/fpdfdoc/cpdf_link.cpp",
-    "core/fpdfdoc/cpdf_link.h",
-    "core/fpdfdoc/cpdf_linklist.cpp",
-    "core/fpdfdoc/cpdf_linklist.h",
-    "core/fpdfdoc/cpdf_metadata.cpp",
-    "core/fpdfdoc/cpdf_metadata.h",
-    "core/fpdfdoc/cpdf_nametree.cpp",
-    "core/fpdfdoc/cpdf_nametree.h",
-    "core/fpdfdoc/cpdf_numbertree.cpp",
-    "core/fpdfdoc/cpdf_numbertree.h",
-    "core/fpdfdoc/cpdf_occontext.cpp",
-    "core/fpdfdoc/cpdf_occontext.h",
-    "core/fpdfdoc/cpdf_pagelabel.cpp",
-    "core/fpdfdoc/cpdf_pagelabel.h",
-    "core/fpdfdoc/cpdf_structelement.cpp",
-    "core/fpdfdoc/cpdf_structelement.h",
-    "core/fpdfdoc/cpdf_structtree.cpp",
-    "core/fpdfdoc/cpdf_structtree.h",
-    "core/fpdfdoc/cpdf_variabletext.cpp",
-    "core/fpdfdoc/cpdf_variabletext.h",
-    "core/fpdfdoc/cpdf_viewerpreferences.cpp",
-    "core/fpdfdoc/cpdf_viewerpreferences.h",
-    "core/fpdfdoc/cpvt_floatrect.h",
-    "core/fpdfdoc/cpvt_fontmap.cpp",
-    "core/fpdfdoc/cpvt_fontmap.h",
-    "core/fpdfdoc/cpvt_generateap.cpp",
-    "core/fpdfdoc/cpvt_generateap.h",
-    "core/fpdfdoc/cpvt_line.h",
-    "core/fpdfdoc/cpvt_lineinfo.h",
-    "core/fpdfdoc/cpvt_word.h",
-    "core/fpdfdoc/cpvt_wordinfo.cpp",
-    "core/fpdfdoc/cpvt_wordinfo.h",
-    "core/fpdfdoc/cpvt_wordplace.h",
-    "core/fpdfdoc/cpvt_wordrange.h",
-    "core/fpdfdoc/csection.cpp",
-    "core/fpdfdoc/csection.h",
-    "core/fpdfdoc/ctypeset.cpp",
-    "core/fpdfdoc/ctypeset.h",
-    "core/fpdfdoc/ipdf_formnotify.h",
-    "core/fpdfdoc/ipvt_fontmap.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = [
-    ":fxcrt",
-  ]
-}
-
-jumbo_static_library("fpdfapi") {
-  sources = [
-    "core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp",
-    "core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp",
-    "core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp",
-    "core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp",
-    "core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp",
-    "core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp",
-    "core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp",
-    "core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp",
-    "core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp",
-    "core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp",
-    "core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp",
-    "core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp",
-    "core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp",
-    "core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp",
-    "core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp",
-    "core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp",
-    "core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp",
-    "core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp",
-    "core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp",
-    "core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp",
-    "core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp",
-    "core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp",
-    "core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp",
-    "core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp",
-    "core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp",
-    "core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp",
-    "core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp",
-    "core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp",
-    "core/fpdfapi/cmaps/Japan1/H_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp",
-    "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp",
-    "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp",
-    "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp",
-    "core/fpdfapi/cmaps/Japan1/V_1.cpp",
-    "core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp",
-    "core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp",
-    "core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp",
-    "core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp",
-    "core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp",
-    "core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp",
-    "core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp",
-    "core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp",
-    "core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp",
-    "core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp",
-    "core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp",
-    "core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp",
-    "core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp",
-    "core/fpdfapi/cmaps/cmap_int.h",
-    "core/fpdfapi/cmaps/fpdf_cmaps.cpp",
-    "core/fpdfapi/cpdf_modulemgr.cpp",
-    "core/fpdfapi/cpdf_modulemgr.h",
-    "core/fpdfapi/cpdf_pagerendercontext.cpp",
-    "core/fpdfapi/cpdf_pagerendercontext.h",
-    "core/fpdfapi/edit/cpdf_creator.cpp",
-    "core/fpdfapi/edit/cpdf_creator.h",
-    "core/fpdfapi/edit/cpdf_encryptor.cpp",
-    "core/fpdfapi/edit/cpdf_encryptor.h",
-    "core/fpdfapi/edit/cpdf_flateencoder.cpp",
-    "core/fpdfapi/edit/cpdf_flateencoder.h",
-    "core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp",
-    "core/fpdfapi/edit/cpdf_pagecontentgenerator.h",
-    "core/fpdfapi/font/cfx_cttgsubtable.cpp",
-    "core/fpdfapi/font/cfx_cttgsubtable.h",
-    "core/fpdfapi/font/cfx_stockfontarray.cpp",
-    "core/fpdfapi/font/cfx_stockfontarray.h",
-    "core/fpdfapi/font/cpdf_cid2unicodemap.cpp",
-    "core/fpdfapi/font/cpdf_cid2unicodemap.h",
-    "core/fpdfapi/font/cpdf_cidfont.cpp",
-    "core/fpdfapi/font/cpdf_cidfont.h",
-    "core/fpdfapi/font/cpdf_cmap.cpp",
-    "core/fpdfapi/font/cpdf_cmap.h",
-    "core/fpdfapi/font/cpdf_cmapmanager.cpp",
-    "core/fpdfapi/font/cpdf_cmapmanager.h",
-    "core/fpdfapi/font/cpdf_cmapparser.cpp",
-    "core/fpdfapi/font/cpdf_cmapparser.h",
-    "core/fpdfapi/font/cpdf_font.cpp",
-    "core/fpdfapi/font/cpdf_font.h",
-    "core/fpdfapi/font/cpdf_fontencoding.cpp",
-    "core/fpdfapi/font/cpdf_fontencoding.h",
-    "core/fpdfapi/font/cpdf_fontglobals.cpp",
-    "core/fpdfapi/font/cpdf_fontglobals.h",
-    "core/fpdfapi/font/cpdf_simplefont.cpp",
-    "core/fpdfapi/font/cpdf_simplefont.h",
-    "core/fpdfapi/font/cpdf_tounicodemap.cpp",
-    "core/fpdfapi/font/cpdf_tounicodemap.h",
-    "core/fpdfapi/font/cpdf_truetypefont.cpp",
-    "core/fpdfapi/font/cpdf_truetypefont.h",
-    "core/fpdfapi/font/cpdf_type1font.cpp",
-    "core/fpdfapi/font/cpdf_type1font.h",
-    "core/fpdfapi/font/cpdf_type3char.cpp",
-    "core/fpdfapi/font/cpdf_type3char.h",
-    "core/fpdfapi/font/cpdf_type3font.cpp",
-    "core/fpdfapi/font/cpdf_type3font.h",
-    "core/fpdfapi/page/cpdf_allstates.cpp",
-    "core/fpdfapi/page/cpdf_allstates.h",
-    "core/fpdfapi/page/cpdf_clippath.cpp",
-    "core/fpdfapi/page/cpdf_clippath.h",
-    "core/fpdfapi/page/cpdf_color.cpp",
-    "core/fpdfapi/page/cpdf_color.h",
-    "core/fpdfapi/page/cpdf_colorspace.cpp",
-    "core/fpdfapi/page/cpdf_colorspace.h",
-    "core/fpdfapi/page/cpdf_colorstate.cpp",
-    "core/fpdfapi/page/cpdf_colorstate.h",
-    "core/fpdfapi/page/cpdf_contentmark.cpp",
-    "core/fpdfapi/page/cpdf_contentmark.h",
-    "core/fpdfapi/page/cpdf_contentmarkitem.cpp",
-    "core/fpdfapi/page/cpdf_contentmarkitem.h",
-    "core/fpdfapi/page/cpdf_contentparser.cpp",
-    "core/fpdfapi/page/cpdf_contentparser.h",
-    "core/fpdfapi/page/cpdf_countedobject.h",
-    "core/fpdfapi/page/cpdf_devicecs.cpp",
-    "core/fpdfapi/page/cpdf_devicecs.h",
-    "core/fpdfapi/page/cpdf_docpagedata.cpp",
-    "core/fpdfapi/page/cpdf_docpagedata.h",
-    "core/fpdfapi/page/cpdf_expintfunc.cpp",
-    "core/fpdfapi/page/cpdf_expintfunc.h",
-    "core/fpdfapi/page/cpdf_form.cpp",
-    "core/fpdfapi/page/cpdf_form.h",
-    "core/fpdfapi/page/cpdf_formobject.cpp",
-    "core/fpdfapi/page/cpdf_formobject.h",
-    "core/fpdfapi/page/cpdf_function.cpp",
-    "core/fpdfapi/page/cpdf_function.h",
-    "core/fpdfapi/page/cpdf_generalstate.cpp",
-    "core/fpdfapi/page/cpdf_generalstate.h",
-    "core/fpdfapi/page/cpdf_graphicstates.cpp",
-    "core/fpdfapi/page/cpdf_graphicstates.h",
-    "core/fpdfapi/page/cpdf_iccprofile.cpp",
-    "core/fpdfapi/page/cpdf_iccprofile.h",
-    "core/fpdfapi/page/cpdf_image.cpp",
-    "core/fpdfapi/page/cpdf_image.h",
-    "core/fpdfapi/page/cpdf_imageobject.cpp",
-    "core/fpdfapi/page/cpdf_imageobject.h",
-    "core/fpdfapi/page/cpdf_meshstream.cpp",
-    "core/fpdfapi/page/cpdf_meshstream.h",
-    "core/fpdfapi/page/cpdf_page.cpp",
-    "core/fpdfapi/page/cpdf_page.h",
-    "core/fpdfapi/page/cpdf_pagemodule.cpp",
-    "core/fpdfapi/page/cpdf_pagemodule.h",
-    "core/fpdfapi/page/cpdf_pageobject.cpp",
-    "core/fpdfapi/page/cpdf_pageobject.h",
-    "core/fpdfapi/page/cpdf_pageobjectholder.cpp",
-    "core/fpdfapi/page/cpdf_pageobjectholder.h",
-    "core/fpdfapi/page/cpdf_pageobjectlist.cpp",
-    "core/fpdfapi/page/cpdf_pageobjectlist.h",
-    "core/fpdfapi/page/cpdf_path.cpp",
-    "core/fpdfapi/page/cpdf_path.h",
-    "core/fpdfapi/page/cpdf_pathobject.cpp",
-    "core/fpdfapi/page/cpdf_pathobject.h",
-    "core/fpdfapi/page/cpdf_pattern.cpp",
-    "core/fpdfapi/page/cpdf_pattern.h",
-    "core/fpdfapi/page/cpdf_patterncs.cpp",
-    "core/fpdfapi/page/cpdf_patterncs.h",
-    "core/fpdfapi/page/cpdf_psengine.cpp",
-    "core/fpdfapi/page/cpdf_psengine.h",
-    "core/fpdfapi/page/cpdf_psfunc.cpp",
-    "core/fpdfapi/page/cpdf_psfunc.h",
-    "core/fpdfapi/page/cpdf_sampledfunc.cpp",
-    "core/fpdfapi/page/cpdf_sampledfunc.h",
-    "core/fpdfapi/page/cpdf_shadingobject.cpp",
-    "core/fpdfapi/page/cpdf_shadingobject.h",
-    "core/fpdfapi/page/cpdf_shadingpattern.cpp",
-    "core/fpdfapi/page/cpdf_shadingpattern.h",
-    "core/fpdfapi/page/cpdf_stitchfunc.cpp",
-    "core/fpdfapi/page/cpdf_stitchfunc.h",
-    "core/fpdfapi/page/cpdf_streamcontentparser.cpp",
-    "core/fpdfapi/page/cpdf_streamcontentparser.h",
-    "core/fpdfapi/page/cpdf_streamparser.cpp",
-    "core/fpdfapi/page/cpdf_streamparser.h",
-    "core/fpdfapi/page/cpdf_textobject.cpp",
-    "core/fpdfapi/page/cpdf_textobject.h",
-    "core/fpdfapi/page/cpdf_textstate.cpp",
-    "core/fpdfapi/page/cpdf_textstate.h",
-    "core/fpdfapi/page/cpdf_tilingpattern.cpp",
-    "core/fpdfapi/page/cpdf_tilingpattern.h",
-    "core/fpdfapi/parser/cfdf_document.cpp",
-    "core/fpdfapi/parser/cfdf_document.h",
-    "core/fpdfapi/parser/cpdf_array.cpp",
-    "core/fpdfapi/parser/cpdf_array.h",
-    "core/fpdfapi/parser/cpdf_boolean.cpp",
-    "core/fpdfapi/parser/cpdf_boolean.h",
-    "core/fpdfapi/parser/cpdf_cross_ref_avail.cpp",
-    "core/fpdfapi/parser/cpdf_cross_ref_avail.h",
-    "core/fpdfapi/parser/cpdf_crypto_handler.cpp",
-    "core/fpdfapi/parser/cpdf_crypto_handler.h",
-    "core/fpdfapi/parser/cpdf_data_avail.cpp",
-    "core/fpdfapi/parser/cpdf_data_avail.h",
-    "core/fpdfapi/parser/cpdf_dictionary.cpp",
-    "core/fpdfapi/parser/cpdf_dictionary.h",
-    "core/fpdfapi/parser/cpdf_document.cpp",
-    "core/fpdfapi/parser/cpdf_document.h",
-    "core/fpdfapi/parser/cpdf_hint_tables.cpp",
-    "core/fpdfapi/parser/cpdf_hint_tables.h",
-    "core/fpdfapi/parser/cpdf_indirect_object_holder.cpp",
-    "core/fpdfapi/parser/cpdf_indirect_object_holder.h",
-    "core/fpdfapi/parser/cpdf_linearized_header.cpp",
-    "core/fpdfapi/parser/cpdf_linearized_header.h",
-    "core/fpdfapi/parser/cpdf_name.cpp",
-    "core/fpdfapi/parser/cpdf_name.h",
-    "core/fpdfapi/parser/cpdf_null.cpp",
-    "core/fpdfapi/parser/cpdf_null.h",
-    "core/fpdfapi/parser/cpdf_number.cpp",
-    "core/fpdfapi/parser/cpdf_number.h",
-    "core/fpdfapi/parser/cpdf_object.cpp",
-    "core/fpdfapi/parser/cpdf_object.h",
-    "core/fpdfapi/parser/cpdf_object_avail.cpp",
-    "core/fpdfapi/parser/cpdf_object_avail.h",
-    "core/fpdfapi/parser/cpdf_object_walker.cpp",
-    "core/fpdfapi/parser/cpdf_object_walker.h",
-    "core/fpdfapi/parser/cpdf_page_object_avail.cpp",
-    "core/fpdfapi/parser/cpdf_page_object_avail.h",
-    "core/fpdfapi/parser/cpdf_parser.cpp",
-    "core/fpdfapi/parser/cpdf_parser.h",
-    "core/fpdfapi/parser/cpdf_read_validator.cpp",
-    "core/fpdfapi/parser/cpdf_read_validator.h",
-    "core/fpdfapi/parser/cpdf_reference.cpp",
-    "core/fpdfapi/parser/cpdf_reference.h",
-    "core/fpdfapi/parser/cpdf_security_handler.cpp",
-    "core/fpdfapi/parser/cpdf_security_handler.h",
-    "core/fpdfapi/parser/cpdf_simple_parser.cpp",
-    "core/fpdfapi/parser/cpdf_simple_parser.h",
-    "core/fpdfapi/parser/cpdf_stream.cpp",
-    "core/fpdfapi/parser/cpdf_stream.h",
-    "core/fpdfapi/parser/cpdf_stream_acc.cpp",
-    "core/fpdfapi/parser/cpdf_stream_acc.h",
-    "core/fpdfapi/parser/cpdf_string.cpp",
-    "core/fpdfapi/parser/cpdf_string.h",
-    "core/fpdfapi/parser/cpdf_syntax_parser.cpp",
-    "core/fpdfapi/parser/cpdf_syntax_parser.h",
-    "core/fpdfapi/parser/fpdf_parser_decode.cpp",
-    "core/fpdfapi/parser/fpdf_parser_decode.h",
-    "core/fpdfapi/parser/fpdf_parser_utility.cpp",
-    "core/fpdfapi/parser/fpdf_parser_utility.h",
-    "core/fpdfapi/render/cpdf_charposlist.cpp",
-    "core/fpdfapi/render/cpdf_charposlist.h",
-    "core/fpdfapi/render/cpdf_devicebuffer.cpp",
-    "core/fpdfapi/render/cpdf_devicebuffer.h",
-    "core/fpdfapi/render/cpdf_dibsource.cpp",
-    "core/fpdfapi/render/cpdf_dibsource.h",
-    "core/fpdfapi/render/cpdf_dibtransferfunc.cpp",
-    "core/fpdfapi/render/cpdf_dibtransferfunc.h",
-    "core/fpdfapi/render/cpdf_docrenderdata.cpp",
-    "core/fpdfapi/render/cpdf_docrenderdata.h",
-    "core/fpdfapi/render/cpdf_imagecacheentry.cpp",
-    "core/fpdfapi/render/cpdf_imagecacheentry.h",
-    "core/fpdfapi/render/cpdf_imageloader.cpp",
-    "core/fpdfapi/render/cpdf_imageloader.h",
-    "core/fpdfapi/render/cpdf_imagerenderer.cpp",
-    "core/fpdfapi/render/cpdf_imagerenderer.h",
-    "core/fpdfapi/render/cpdf_pagerendercache.cpp",
-    "core/fpdfapi/render/cpdf_pagerendercache.h",
-    "core/fpdfapi/render/cpdf_progressiverenderer.cpp",
-    "core/fpdfapi/render/cpdf_progressiverenderer.h",
-    "core/fpdfapi/render/cpdf_rendercontext.cpp",
-    "core/fpdfapi/render/cpdf_rendercontext.h",
-    "core/fpdfapi/render/cpdf_renderoptions.cpp",
-    "core/fpdfapi/render/cpdf_renderoptions.h",
-    "core/fpdfapi/render/cpdf_renderstatus.cpp",
-    "core/fpdfapi/render/cpdf_renderstatus.h",
-    "core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp",
-    "core/fpdfapi/render/cpdf_scaledrenderbuffer.h",
-    "core/fpdfapi/render/cpdf_textrenderer.cpp",
-    "core/fpdfapi/render/cpdf_textrenderer.h",
-    "core/fpdfapi/render/cpdf_transferfunc.cpp",
-    "core/fpdfapi/render/cpdf_transferfunc.h",
-    "core/fpdfapi/render/cpdf_type3cache.cpp",
-    "core/fpdfapi/render/cpdf_type3cache.h",
-    "core/fpdfapi/render/cpdf_type3glyphs.cpp",
-    "core/fpdfapi/render/cpdf_type3glyphs.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = [
-    ":fxcrt",
-    "third_party:lcms2",
-  ]
-}
-
-jumbo_static_library("fpdftext") {
-  sources = [
-    "core/fpdftext/cpdf_linkextract.cpp",
-    "core/fpdftext/cpdf_linkextract.h",
-    "core/fpdftext/cpdf_textpage.cpp",
-    "core/fpdftext/cpdf_textpage.h",
-    "core/fpdftext/cpdf_textpagefind.cpp",
-    "core/fpdftext/cpdf_textpagefind.h",
-    "core/fpdftext/unicodenormalizationdata.cpp",
-    "core/fpdftext/unicodenormalizationdata.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = [
-    ":fxcrt",
-  ]
-}
-
-jumbo_static_library("fxcodec") {
-  sources = [
-    "core/fxcodec/JBig2_DocumentContext.h",
-    "core/fxcodec/codec/ccodec_basicmodule.h",
-    "core/fxcodec/codec/ccodec_faxmodule.h",
-    "core/fxcodec/codec/ccodec_flatemodule.h",
-    "core/fxcodec/codec/ccodec_iccmodule.h",
-    "core/fxcodec/codec/ccodec_jbig2module.h",
-    "core/fxcodec/codec/ccodec_jpegmodule.h",
-    "core/fxcodec/codec/ccodec_jpxmodule.h",
-    "core/fxcodec/codec/ccodec_scanlinedecoder.cpp",
-    "core/fxcodec/codec/ccodec_scanlinedecoder.h",
-    "core/fxcodec/codec/cjpx_decoder.h",
-    "core/fxcodec/codec/codec_int.h",
-    "core/fxcodec/codec/fx_codec.cpp",
-    "core/fxcodec/codec/fx_codec_fax.cpp",
-    "core/fxcodec/codec/fx_codec_flate.cpp",
-    "core/fxcodec/codec/fx_codec_icc.cpp",
-    "core/fxcodec/codec/fx_codec_jbig.cpp",
-    "core/fxcodec/codec/fx_codec_jpeg.cpp",
-    "core/fxcodec/codec/fx_codec_jpx_opj.cpp",
-    "core/fxcodec/fx_codec.h",
-    "core/fxcodec/fx_codec_def.h",
-    "core/fxcodec/jbig2/JBig2_ArithDecoder.cpp",
-    "core/fxcodec/jbig2/JBig2_ArithDecoder.h",
-    "core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp",
-    "core/fxcodec/jbig2/JBig2_ArithIntDecoder.h",
-    "core/fxcodec/jbig2/JBig2_BitStream.cpp",
-    "core/fxcodec/jbig2/JBig2_BitStream.h",
-    "core/fxcodec/jbig2/JBig2_Context.cpp",
-    "core/fxcodec/jbig2/JBig2_Context.h",
-    "core/fxcodec/jbig2/JBig2_Define.h",
-    "core/fxcodec/jbig2/JBig2_GrdProc.cpp",
-    "core/fxcodec/jbig2/JBig2_GrdProc.h",
-    "core/fxcodec/jbig2/JBig2_GrrdProc.cpp",
-    "core/fxcodec/jbig2/JBig2_GrrdProc.h",
-    "core/fxcodec/jbig2/JBig2_HtrdProc.cpp",
-    "core/fxcodec/jbig2/JBig2_HtrdProc.h",
-    "core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp",
-    "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h",
-    "core/fxcodec/jbig2/JBig2_HuffmanTable.cpp",
-    "core/fxcodec/jbig2/JBig2_HuffmanTable.h",
-    "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.cpp",
-    "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h",
-    "core/fxcodec/jbig2/JBig2_Image.cpp",
-    "core/fxcodec/jbig2/JBig2_Image.h",
-    "core/fxcodec/jbig2/JBig2_Page.h",
-    "core/fxcodec/jbig2/JBig2_PatternDict.cpp",
-    "core/fxcodec/jbig2/JBig2_PatternDict.h",
-    "core/fxcodec/jbig2/JBig2_PddProc.cpp",
-    "core/fxcodec/jbig2/JBig2_PddProc.h",
-    "core/fxcodec/jbig2/JBig2_SddProc.cpp",
-    "core/fxcodec/jbig2/JBig2_SddProc.h",
-    "core/fxcodec/jbig2/JBig2_Segment.cpp",
-    "core/fxcodec/jbig2/JBig2_Segment.h",
-    "core/fxcodec/jbig2/JBig2_SymbolDict.cpp",
-    "core/fxcodec/jbig2/JBig2_SymbolDict.h",
-    "core/fxcodec/jbig2/JBig2_TrdProc.cpp",
-    "core/fxcodec/jbig2/JBig2_TrdProc.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  include_dirs = []
-  deps = [
-    ":fxcrt",
-    "third_party:fx_libopenjpeg",
-    "third_party:lcms2",
-    "third_party:zlib",
-    "//third_party:jpeg",
-  ]
-
-  if (pdf_enable_xfa) {
-    sources += [
-      "core/fxcodec/codec/ccodec_bmpmodule.cpp",
-      "core/fxcodec/codec/ccodec_bmpmodule.h",
-      "core/fxcodec/codec/ccodec_gifmodule.cpp",
-      "core/fxcodec/codec/ccodec_gifmodule.h",
-      "core/fxcodec/codec/ccodec_pngmodule.cpp",
-      "core/fxcodec/codec/ccodec_pngmodule.h",
-      "core/fxcodec/codec/ccodec_progressivedecoder.h",
-      "core/fxcodec/codec/ccodec_tiffmodule.cpp",
-      "core/fxcodec/codec/ccodec_tiffmodule.h",
-      "core/fxcodec/codec/fx_codec_progress.cpp",
-      "core/fxcodec/gif/cfx_gif.cpp",
-      "core/fxcodec/gif/cfx_gif.h",
-      "core/fxcodec/gif/cfx_gifcontext.cpp",
-      "core/fxcodec/gif/cfx_gifcontext.h",
-      "core/fxcodec/gif/cfx_lzwdecompressor.cpp",
-      "core/fxcodec/gif/cfx_lzwdecompressor.h",
-      "core/fxcodec/lbmp/fx_bmp.cpp",
-      "core/fxcodec/lbmp/fx_bmp.h",
-    ]
-    deps += [
-      "third_party:fx_tiff",
-      "third_party:png",
-    ]
-  }
-  if (is_posix) {
-    # core/fxcodec/fx_libopenjpeg/src/fx_mct.c does an pointer-to-int
-    # conversion to check that an address is 16-bit aligned (benign).
-    cflags_c = [ "-Wno-pointer-to-int-cast" ]
-  }
-}
-
-config("fxge_warnings") {
-  if (is_clang) {
-    cflags = [
-      # http://code.google.com/p/pdfium/issues/detail?id=188
-      "-Wno-switch",
-    ]
-  }
-}
-
-jumbo_static_library("fxcrt") {
-  sources = [
-    "core/fxcrt/autorestorer.h",
-    "core/fxcrt/bytestring.cpp",
-    "core/fxcrt/bytestring.h",
-    "core/fxcrt/cfx_binarybuf.cpp",
-    "core/fxcrt/cfx_binarybuf.h",
-    "core/fxcrt/cfx_bitstream.cpp",
-    "core/fxcrt/cfx_bitstream.h",
-    "core/fxcrt/cfx_datetime.cpp",
-    "core/fxcrt/cfx_datetime.h",
-    "core/fxcrt/cfx_fileaccess_posix.cpp",
-    "core/fxcrt/cfx_fileaccess_posix.h",
-    "core/fxcrt/cfx_fileaccess_windows.cpp",
-    "core/fxcrt/cfx_fileaccess_windows.h",
-    "core/fxcrt/cfx_fixedbufgrow.h",
-    "core/fxcrt/cfx_memorystream.cpp",
-    "core/fxcrt/cfx_memorystream.h",
-    "core/fxcrt/cfx_seekablemultistream.cpp",
-    "core/fxcrt/cfx_seekablemultistream.h",
-    "core/fxcrt/cfx_utf8decoder.cpp",
-    "core/fxcrt/cfx_utf8decoder.h",
-    "core/fxcrt/cfx_widetextbuf.cpp",
-    "core/fxcrt/cfx_widetextbuf.h",
-    "core/fxcrt/fx_bidi.cpp",
-    "core/fxcrt/fx_bidi.h",
-    "core/fxcrt/fx_codepage.h",
-    "core/fxcrt/fx_coordinates.cpp",
-    "core/fxcrt/fx_coordinates.h",
-    "core/fxcrt/fx_extension.cpp",
-    "core/fxcrt/fx_extension.h",
-    "core/fxcrt/fx_memory.cpp",
-    "core/fxcrt/fx_memory.h",
-    "core/fxcrt/fx_random.cpp",
-    "core/fxcrt/fx_random.h",
-    "core/fxcrt/fx_safe_types.h",
-    "core/fxcrt/fx_stream.cpp",
-    "core/fxcrt/fx_stream.h",
-    "core/fxcrt/fx_string.cpp",
-    "core/fxcrt/fx_string.h",
-    "core/fxcrt/fx_system.cpp",
-    "core/fxcrt/fx_system.h",
-    "core/fxcrt/fx_ucddata.cpp",
-    "core/fxcrt/fx_ucddata.h",
-    "core/fxcrt/fx_unicode.cpp",
-    "core/fxcrt/fx_unicode.h",
-    "core/fxcrt/ifx_fileaccess.h",
-    "core/fxcrt/ifx_pauseindicator.h",
-    "core/fxcrt/maybe_owned.h",
-    "core/fxcrt/observable.h",
-    "core/fxcrt/retain_ptr.h",
-    "core/fxcrt/shared_copy_on_write.h",
-    "core/fxcrt/string_data_template.h",
-    "core/fxcrt/string_pool_template.h",
-    "core/fxcrt/string_view_template.h",
-    "core/fxcrt/unowned_ptr.h",
-    "core/fxcrt/weak_ptr.h",
-    "core/fxcrt/widestring.cpp",
-    "core/fxcrt/widestring.h",
-    "core/fxcrt/xml/cxml_attritem.cpp",
-    "core/fxcrt/xml/cxml_attritem.h",
-    "core/fxcrt/xml/cxml_content.cpp",
-    "core/fxcrt/xml/cxml_content.h",
-    "core/fxcrt/xml/cxml_databufacc.cpp",
-    "core/fxcrt/xml/cxml_databufacc.h",
-    "core/fxcrt/xml/cxml_element.cpp",
-    "core/fxcrt/xml/cxml_element.h",
-    "core/fxcrt/xml/cxml_object.cpp",
-    "core/fxcrt/xml/cxml_object.h",
-    "core/fxcrt/xml/cxml_parser.cpp",
-    "core/fxcrt/xml/cxml_parser.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  visibility += [ "third_party:*" ]
-  deps = [
-    "third_party:pdfium_base",
-  ]
+group("pdfium_unittest_deps") {
+  testonly = true
   public_deps = [
-    ":freetype_common",
-    "third_party:pdfium_base",
-  ]
-
-  if (pdf_enable_xfa) {
-    sources += [
-      "core/fxcrt/cfx_blockbuffer.cpp",
-      "core/fxcrt/cfx_blockbuffer.h",
-      "core/fxcrt/cfx_char.cpp",
-      "core/fxcrt/cfx_char.h",
-      "core/fxcrt/cfx_checksumcontext.cpp",
-      "core/fxcrt/cfx_checksumcontext.h",
-      "core/fxcrt/cfx_decimal.cpp",
-      "core/fxcrt/cfx_decimal.h",
-      "core/fxcrt/cfx_seekablestreamproxy.cpp",
-      "core/fxcrt/cfx_seekablestreamproxy.h",
-      "core/fxcrt/css/cfx_css.h",
-      "core/fxcrt/css/cfx_csscolorvalue.cpp",
-      "core/fxcrt/css/cfx_csscolorvalue.h",
-      "core/fxcrt/css/cfx_csscomputedstyle.cpp",
-      "core/fxcrt/css/cfx_csscomputedstyle.h",
-      "core/fxcrt/css/cfx_csscustomproperty.cpp",
-      "core/fxcrt/css/cfx_csscustomproperty.h",
-      "core/fxcrt/css/cfx_cssdatatable.cpp",
-      "core/fxcrt/css/cfx_cssdatatable.h",
-      "core/fxcrt/css/cfx_cssdeclaration.cpp",
-      "core/fxcrt/css/cfx_cssdeclaration.h",
-      "core/fxcrt/css/cfx_cssenumvalue.cpp",
-      "core/fxcrt/css/cfx_cssenumvalue.h",
-      "core/fxcrt/css/cfx_cssexttextbuf.cpp",
-      "core/fxcrt/css/cfx_cssexttextbuf.h",
-      "core/fxcrt/css/cfx_cssnumbervalue.cpp",
-      "core/fxcrt/css/cfx_cssnumbervalue.h",
-      "core/fxcrt/css/cfx_csspropertyholder.cpp",
-      "core/fxcrt/css/cfx_csspropertyholder.h",
-      "core/fxcrt/css/cfx_cssrulecollection.cpp",
-      "core/fxcrt/css/cfx_cssrulecollection.h",
-      "core/fxcrt/css/cfx_cssselector.cpp",
-      "core/fxcrt/css/cfx_cssselector.h",
-      "core/fxcrt/css/cfx_cssstringvalue.cpp",
-      "core/fxcrt/css/cfx_cssstringvalue.h",
-      "core/fxcrt/css/cfx_cssstylerule.cpp",
-      "core/fxcrt/css/cfx_cssstylerule.h",
-      "core/fxcrt/css/cfx_cssstyleselector.cpp",
-      "core/fxcrt/css/cfx_cssstyleselector.h",
-      "core/fxcrt/css/cfx_cssstylesheet.cpp",
-      "core/fxcrt/css/cfx_cssstylesheet.h",
-      "core/fxcrt/css/cfx_csssyntaxparser.cpp",
-      "core/fxcrt/css/cfx_csssyntaxparser.h",
-      "core/fxcrt/css/cfx_csstextbuf.cpp",
-      "core/fxcrt/css/cfx_csstextbuf.h",
-      "core/fxcrt/css/cfx_cssvalue.cpp",
-      "core/fxcrt/css/cfx_cssvalue.h",
-      "core/fxcrt/css/cfx_cssvaluelist.cpp",
-      "core/fxcrt/css/cfx_cssvaluelist.h",
-      "core/fxcrt/css/cfx_cssvaluelistparser.cpp",
-      "core/fxcrt/css/cfx_cssvaluelistparser.h",
-      "core/fxcrt/fx_arabic.cpp",
-      "core/fxcrt/fx_arabic.h",
-      "core/fxcrt/ifx_locale.h",
-      "core/fxcrt/xml/cfx_saxcontext.cpp",
-      "core/fxcrt/xml/cfx_saxcontext.h",
-      "core/fxcrt/xml/cfx_saxreader.cpp",
-      "core/fxcrt/xml/cfx_saxreader.h",
-      "core/fxcrt/xml/cfx_saxreaderhandler.cpp",
-      "core/fxcrt/xml/cfx_saxreaderhandler.h",
-      "core/fxcrt/xml/cfx_xmlattributenode.cpp",
-      "core/fxcrt/xml/cfx_xmlattributenode.h",
-      "core/fxcrt/xml/cfx_xmlchardata.cpp",
-      "core/fxcrt/xml/cfx_xmlchardata.h",
-      "core/fxcrt/xml/cfx_xmldoc.cpp",
-      "core/fxcrt/xml/cfx_xmldoc.h",
-      "core/fxcrt/xml/cfx_xmlelement.cpp",
-      "core/fxcrt/xml/cfx_xmlelement.h",
-      "core/fxcrt/xml/cfx_xmlinstruction.cpp",
-      "core/fxcrt/xml/cfx_xmlinstruction.h",
-      "core/fxcrt/xml/cfx_xmlnode.cpp",
-      "core/fxcrt/xml/cfx_xmlnode.h",
-      "core/fxcrt/xml/cfx_xmlparser.cpp",
-      "core/fxcrt/xml/cfx_xmlparser.h",
-      "core/fxcrt/xml/cfx_xmlsyntaxparser.cpp",
-      "core/fxcrt/xml/cfx_xmlsyntaxparser.h",
-      "core/fxcrt/xml/cfx_xmltext.cpp",
-      "core/fxcrt/xml/cfx_xmltext.h",
-    ]
-  }
-}
-
-jumbo_static_library("fxge") {
-  sources = [
-    "core/fxge/android/cfpf_skiabufferfont.cpp",
-    "core/fxge/android/cfpf_skiabufferfont.h",
-    "core/fxge/android/cfpf_skiadevicemodule.cpp",
-    "core/fxge/android/cfpf_skiadevicemodule.h",
-    "core/fxge/android/cfpf_skiafilefont.cpp",
-    "core/fxge/android/cfpf_skiafilefont.h",
-    "core/fxge/android/cfpf_skiafont.cpp",
-    "core/fxge/android/cfpf_skiafont.h",
-    "core/fxge/android/cfpf_skiafontdescriptor.cpp",
-    "core/fxge/android/cfpf_skiafontdescriptor.h",
-    "core/fxge/android/cfpf_skiafontmgr.cpp",
-    "core/fxge/android/cfpf_skiafontmgr.h",
-    "core/fxge/android/cfpf_skiapathfont.cpp",
-    "core/fxge/android/cfpf_skiapathfont.h",
-    "core/fxge/android/cfx_androidfontinfo.cpp",
-    "core/fxge/android/cfx_androidfontinfo.h",
-    "core/fxge/android/fx_android_imp.cpp",
-    "core/fxge/cfx_cliprgn.cpp",
-    "core/fxge/cfx_cliprgn.h",
-    "core/fxge/cfx_color.cpp",
-    "core/fxge/cfx_color.h",
-    "core/fxge/cfx_defaultrenderdevice.h",
-    "core/fxge/cfx_facecache.cpp",
-    "core/fxge/cfx_facecache.h",
-    "core/fxge/cfx_folderfontinfo.cpp",
-    "core/fxge/cfx_folderfontinfo.h",
-    "core/fxge/cfx_font.cpp",
-    "core/fxge/cfx_fontcache.cpp",
-    "core/fxge/cfx_fontcache.h",
-    "core/fxge/cfx_fontmapper.cpp",
-    "core/fxge/cfx_fontmapper.h",
-    "core/fxge/cfx_fontmgr.cpp",
-    "core/fxge/cfx_fontmgr.h",
-    "core/fxge/cfx_gemodule.cpp",
-    "core/fxge/cfx_gemodule.h",
-    "core/fxge/cfx_graphstate.cpp",
-    "core/fxge/cfx_graphstate.h",
-    "core/fxge/cfx_graphstatedata.cpp",
-    "core/fxge/cfx_graphstatedata.h",
-    "core/fxge/cfx_pathdata.cpp",
-    "core/fxge/cfx_pathdata.h",
-    "core/fxge/cfx_renderdevice.cpp",
-    "core/fxge/cfx_renderdevice.h",
-    "core/fxge/cfx_substfont.cpp",
-    "core/fxge/cfx_substfont.h",
-    "core/fxge/cfx_unicodeencoding.cpp",
-    "core/fxge/cfx_unicodeencoding.h",
-    "core/fxge/cfx_windowsrenderdevice.h",
-    "core/fxge/cttfontdesc.cpp",
-    "core/fxge/cttfontdesc.h",
-    "core/fxge/dib/cfx_bitmapcomposer.cpp",
-    "core/fxge/dib/cfx_bitmapcomposer.h",
-    "core/fxge/dib/cfx_bitmapstorer.cpp",
-    "core/fxge/dib/cfx_bitmapstorer.h",
-    "core/fxge/dib/cfx_dibextractor.cpp",
-    "core/fxge/dib/cfx_dibextractor.h",
-    "core/fxge/dib/cfx_dibitmap.cpp",
-    "core/fxge/dib/cfx_dibitmap.h",
-    "core/fxge/dib/cfx_dibsource.cpp",
-    "core/fxge/dib/cfx_dibsource.h",
-    "core/fxge/dib/cfx_filtereddib.cpp",
-    "core/fxge/dib/cfx_filtereddib.h",
-    "core/fxge/dib/cfx_imagerenderer.cpp",
-    "core/fxge/dib/cfx_imagerenderer.h",
-    "core/fxge/dib/cfx_imagestretcher.cpp",
-    "core/fxge/dib/cfx_imagestretcher.h",
-    "core/fxge/dib/cfx_imagetransformer.cpp",
-    "core/fxge/dib/cfx_imagetransformer.h",
-    "core/fxge/dib/cfx_scanlinecompositor.cpp",
-    "core/fxge/dib/cfx_scanlinecompositor.h",
-    "core/fxge/dib/cstretchengine.cpp",
-    "core/fxge/dib/cstretchengine.h",
-    "core/fxge/dib/fx_dib_main.cpp",
-    "core/fxge/dib/ifx_scanlinecomposer.h",
-    "core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitFixed.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSans.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSerif.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp",
-    "core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp",
-    "core/fxge/fontdata/chromefontdata/chromefontdata.h",
-    "core/fxge/freetype/fx_freetype.cpp",
-    "core/fxge/fx_dib.h",
-    "core/fxge/fx_font.h",
-    "core/fxge/fx_freetype.h",
-    "core/fxge/fx_ge_fontmap.cpp",
-    "core/fxge/fx_ge_linux.cpp",
-    "core/fxge/fx_ge_text.cpp",
-    "core/fxge/ifx_renderdevicedriver.cpp",
-    "core/fxge/ifx_renderdevicedriver.h",
-    "core/fxge/ifx_systemfontinfo.h",
-    "core/fxge/win32/cfx_windowsdib.h",
-  ]
-
-  configs += [
-    ":fxge_warnings",
-    ":pdfium_core_config",
-  ]
-
-  deps = [
-    ":fxcrt",
-  ]
-
-  defines = [ "DEFINE_PS_TABLES" ]
-  if (is_component_build || use_system_freetype) {
-    # ft_adobe_glyph_list is not exported from the Freetype shared library so we
-    # need it defined in component builds and builds using system freetype.
-    defines += [ "DEFINE_PS_TABLES_DATA" ]
-  }
-
-  if (pdf_enable_xfa) {
-    sources += [
-      "core/fxge/cfx_unicodeencodingex.cpp",
-      "core/fxge/cfx_unicodeencodingex.h",
-    ]
-  }
-
-  if (pdf_use_skia || pdf_use_skia_paths) {
-    sources += [ "core/fxge/skia/fx_skia_device.cpp" ]
-    deps += [ "//skia" ]
-  } else {
-    sources += [
-      "core/fxge/agg/fx_agg_driver.cpp",
-      "core/fxge/agg/fx_agg_driver.h",
-    ]
-    deps += [ "third_party:fx_agg" ]
-  }
-
-  if (is_mac) {
-    sources += [ "core/fxge/apple/fx_apple_platform.cpp" ]
-  }
-
-  if (is_win) {
-    sources += [
-      "core/fxge/win32/cfx_psrenderer.cpp",
-      "core/fxge/win32/cfx_psrenderer.h",
-      "core/fxge/win32/cpsoutput.cpp",
-      "core/fxge/win32/cpsoutput.h",
-      "core/fxge/win32/dwrite_int.h",
-      "core/fxge/win32/fx_win32_device.cpp",
-      "core/fxge/win32/fx_win32_dib.cpp",
-      "core/fxge/win32/fx_win32_dwrite.cpp",
-      "core/fxge/win32/fx_win32_gdipext.cpp",
-      "core/fxge/win32/fx_win32_print.cpp",
-      "core/fxge/win32/win32_int.h",
-    ]
-    configs -= [ "//build/config/win:lean_and_mean" ]
-  }
-
-  if (is_mac) {
-    sources += [
-      "core/fxge/apple/apple_int.h",
-      "core/fxge/apple/fx_mac_imp.cpp",
-      "core/fxge/apple/fx_quartz_device.cpp",
-    ]
-  }
-}
-
-jumbo_static_library("pwl") {
-  sources = [
-    "fpdfsdk/pwl/cpwl_appstream.cpp",
-    "fpdfsdk/pwl/cpwl_appstream.h",
-    "fpdfsdk/pwl/cpwl_button.cpp",
-    "fpdfsdk/pwl/cpwl_button.h",
-    "fpdfsdk/pwl/cpwl_caret.cpp",
-    "fpdfsdk/pwl/cpwl_caret.h",
-    "fpdfsdk/pwl/cpwl_combo_box.cpp",
-    "fpdfsdk/pwl/cpwl_combo_box.h",
-    "fpdfsdk/pwl/cpwl_edit.cpp",
-    "fpdfsdk/pwl/cpwl_edit.h",
-    "fpdfsdk/pwl/cpwl_edit_ctrl.cpp",
-    "fpdfsdk/pwl/cpwl_edit_ctrl.h",
-    "fpdfsdk/pwl/cpwl_edit_impl.cpp",
-    "fpdfsdk/pwl/cpwl_edit_impl.h",
-    "fpdfsdk/pwl/cpwl_font_map.cpp",
-    "fpdfsdk/pwl/cpwl_font_map.h",
-    "fpdfsdk/pwl/cpwl_icon.cpp",
-    "fpdfsdk/pwl/cpwl_icon.h",
-    "fpdfsdk/pwl/cpwl_list_box.cpp",
-    "fpdfsdk/pwl/cpwl_list_box.h",
-    "fpdfsdk/pwl/cpwl_list_impl.cpp",
-    "fpdfsdk/pwl/cpwl_list_impl.h",
-    "fpdfsdk/pwl/cpwl_scroll_bar.cpp",
-    "fpdfsdk/pwl/cpwl_scroll_bar.h",
-    "fpdfsdk/pwl/cpwl_special_button.cpp",
-    "fpdfsdk/pwl/cpwl_special_button.h",
-    "fpdfsdk/pwl/cpwl_timer.cpp",
-    "fpdfsdk/pwl/cpwl_timer.h",
-    "fpdfsdk/pwl/cpwl_timer_handler.cpp",
-    "fpdfsdk/pwl/cpwl_timer_handler.h",
-    "fpdfsdk/pwl/cpwl_wnd.cpp",
-    "fpdfsdk/pwl/cpwl_wnd.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = [
-    ":fxcrt",
-  ]
-}
-
-jumbo_static_library("fxjs") {
-  sources = [
-    "fxjs/ijs_event_context.h",
-    "fxjs/ijs_runtime.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = [
-    ":fxcrt",
-  ]
-
-  if (pdf_enable_v8) {
-    sources += [
-      "fxjs/CJX_Define.h",
-      "fxjs/JS_Define.cpp",
-      "fxjs/JS_Define.h",
-      "fxjs/cjs_annot.cpp",
-      "fxjs/cjs_annot.h",
-      "fxjs/cjs_app.cpp",
-      "fxjs/cjs_app.h",
-      "fxjs/cjs_border.cpp",
-      "fxjs/cjs_border.h",
-      "fxjs/cjs_color.cpp",
-      "fxjs/cjs_color.h",
-      "fxjs/cjs_console.cpp",
-      "fxjs/cjs_console.h",
-      "fxjs/cjs_delaydata.cpp",
-      "fxjs/cjs_delaydata.h",
-      "fxjs/cjs_display.cpp",
-      "fxjs/cjs_display.h",
-      "fxjs/cjs_document.cpp",
-      "fxjs/cjs_document.h",
-      "fxjs/cjs_embedobj.cpp",
-      "fxjs/cjs_embedobj.h",
-      "fxjs/cjs_event.cpp",
-      "fxjs/cjs_event.h",
-      "fxjs/cjs_event_context.cpp",
-      "fxjs/cjs_event_context.h",
-      "fxjs/cjs_eventhandler.cpp",
-      "fxjs/cjs_eventhandler.h",
-      "fxjs/cjs_field.cpp",
-      "fxjs/cjs_field.h",
-      "fxjs/cjs_font.cpp",
-      "fxjs/cjs_font.h",
-      "fxjs/cjs_global.cpp",
-      "fxjs/cjs_global.h",
-      "fxjs/cjs_globalarrays.cpp",
-      "fxjs/cjs_globalarrays.h",
-      "fxjs/cjs_globalconsts.cpp",
-      "fxjs/cjs_globalconsts.h",
-      "fxjs/cjs_globaldata.cpp",
-      "fxjs/cjs_globaldata.h",
-      "fxjs/cjs_globalvariablearray.cpp",
-      "fxjs/cjs_globalvariablearray.h",
-      "fxjs/cjs_highlight.cpp",
-      "fxjs/cjs_highlight.h",
-      "fxjs/cjs_icon.cpp",
-      "fxjs/cjs_icon.h",
-      "fxjs/cjs_keyvalue.cpp",
-      "fxjs/cjs_keyvalue.h",
-      "fxjs/cjs_object.cpp",
-      "fxjs/cjs_object.h",
-      "fxjs/cjs_position.cpp",
-      "fxjs/cjs_position.h",
-      "fxjs/cjs_printparamsobj.cpp",
-      "fxjs/cjs_printparamsobj.h",
-      "fxjs/cjs_publicmethods.cpp",
-      "fxjs/cjs_publicmethods.h",
-      "fxjs/cjs_report.cpp",
-      "fxjs/cjs_report.h",
-      "fxjs/cjs_return.cpp",
-      "fxjs/cjs_return.h",
-      "fxjs/cjs_runtime.cpp",
-      "fxjs/cjs_runtime.h",
-      "fxjs/cjs_scalehow.cpp",
-      "fxjs/cjs_scalehow.h",
-      "fxjs/cjs_scalewhen.cpp",
-      "fxjs/cjs_scalewhen.h",
-      "fxjs/cjs_style.cpp",
-      "fxjs/cjs_style.h",
-      "fxjs/cjs_timerobj.cpp",
-      "fxjs/cjs_timerobj.h",
-      "fxjs/cjs_util.cpp",
-      "fxjs/cjs_util.h",
-      "fxjs/cjs_v8.cpp",
-      "fxjs/cjs_v8.h",
-      "fxjs/cjs_zoomtype.cpp",
-      "fxjs/cjs_zoomtype.h",
-      "fxjs/fxjs_v8.cpp",
-      "fxjs/fxjs_v8.h",
-      "fxjs/global_timer.cpp",
-      "fxjs/global_timer.h",
-      "fxjs/js_resources.cpp",
-      "fxjs/js_resources.h",
-    ]
-    deps += [
-      ":fxcrt",
-      "//v8",
-      "//v8:v8_libplatform",
-    ]
-    configs += [ "//v8:external_startup_data" ]
-    include_dirs = [
-      "//v8",
-      "//v8/include",
-    ]
-    public_deps = [
-      "//v8",
-    ]
-
-    if (pdf_enable_xfa) {
-      sources += [
-        "fxjs/cfxjse_arguments.cpp",
-        "fxjs/cfxjse_arguments.h",
-        "fxjs/cfxjse_class.cpp",
-        "fxjs/cfxjse_class.h",
-        "fxjs/cfxjse_context.cpp",
-        "fxjs/cfxjse_context.h",
-        "fxjs/cfxjse_engine.cpp",
-        "fxjs/cfxjse_engine.h",
-        "fxjs/cfxjse_formcalc_context.cpp",
-        "fxjs/cfxjse_formcalc_context.h",
-        "fxjs/cfxjse_isolatetracker.h",
-        "fxjs/cfxjse_resolveprocessor.cpp",
-        "fxjs/cfxjse_resolveprocessor.h",
-        "fxjs/cfxjse_runtimedata.cpp",
-        "fxjs/cfxjse_runtimedata.h",
-        "fxjs/cfxjse_value.cpp",
-        "fxjs/cfxjse_value.h",
-        "fxjs/fxjse.h",
-        "fxjs/xfa/cjx_arc.cpp",
-        "fxjs/xfa/cjx_arc.h",
-        "fxjs/xfa/cjx_area.cpp",
-        "fxjs/xfa/cjx_area.h",
-        "fxjs/xfa/cjx_assist.cpp",
-        "fxjs/xfa/cjx_assist.h",
-        "fxjs/xfa/cjx_barcode.cpp",
-        "fxjs/xfa/cjx_barcode.h",
-        "fxjs/xfa/cjx_bind.cpp",
-        "fxjs/xfa/cjx_bind.h",
-        "fxjs/xfa/cjx_binditems.cpp",
-        "fxjs/xfa/cjx_binditems.h",
-        "fxjs/xfa/cjx_bookend.cpp",
-        "fxjs/xfa/cjx_bookend.h",
-        "fxjs/xfa/cjx_boolean.cpp",
-        "fxjs/xfa/cjx_boolean.h",
-        "fxjs/xfa/cjx_border.cpp",
-        "fxjs/xfa/cjx_border.h",
-        "fxjs/xfa/cjx_break.cpp",
-        "fxjs/xfa/cjx_break.h",
-        "fxjs/xfa/cjx_breakafter.cpp",
-        "fxjs/xfa/cjx_breakafter.h",
-        "fxjs/xfa/cjx_breakbefore.cpp",
-        "fxjs/xfa/cjx_breakbefore.h",
-        "fxjs/xfa/cjx_button.cpp",
-        "fxjs/xfa/cjx_button.h",
-        "fxjs/xfa/cjx_calculate.cpp",
-        "fxjs/xfa/cjx_calculate.h",
-        "fxjs/xfa/cjx_caption.cpp",
-        "fxjs/xfa/cjx_caption.h",
-        "fxjs/xfa/cjx_certificate.cpp",
-        "fxjs/xfa/cjx_certificate.h",
-        "fxjs/xfa/cjx_certificates.cpp",
-        "fxjs/xfa/cjx_certificates.h",
-        "fxjs/xfa/cjx_checkbutton.cpp",
-        "fxjs/xfa/cjx_checkbutton.h",
-        "fxjs/xfa/cjx_choicelist.cpp",
-        "fxjs/xfa/cjx_choicelist.h",
-        "fxjs/xfa/cjx_color.cpp",
-        "fxjs/xfa/cjx_color.h",
-        "fxjs/xfa/cjx_comb.cpp",
-        "fxjs/xfa/cjx_comb.h",
-        "fxjs/xfa/cjx_command.cpp",
-        "fxjs/xfa/cjx_command.h",
-        "fxjs/xfa/cjx_connect.cpp",
-        "fxjs/xfa/cjx_connect.h",
-        "fxjs/xfa/cjx_connectstring.cpp",
-        "fxjs/xfa/cjx_connectstring.h",
-        "fxjs/xfa/cjx_container.cpp",
-        "fxjs/xfa/cjx_container.h",
-        "fxjs/xfa/cjx_content.cpp",
-        "fxjs/xfa/cjx_content.h",
-        "fxjs/xfa/cjx_contentarea.cpp",
-        "fxjs/xfa/cjx_contentarea.h",
-        "fxjs/xfa/cjx_corner.cpp",
-        "fxjs/xfa/cjx_corner.h",
-        "fxjs/xfa/cjx_datavalue.cpp",
-        "fxjs/xfa/cjx_datavalue.h",
-        "fxjs/xfa/cjx_datawindow.cpp",
-        "fxjs/xfa/cjx_datawindow.h",
-        "fxjs/xfa/cjx_date.cpp",
-        "fxjs/xfa/cjx_date.h",
-        "fxjs/xfa/cjx_datetime.cpp",
-        "fxjs/xfa/cjx_datetime.h",
-        "fxjs/xfa/cjx_datetimeedit.cpp",
-        "fxjs/xfa/cjx_datetimeedit.h",
-        "fxjs/xfa/cjx_decimal.cpp",
-        "fxjs/xfa/cjx_decimal.h",
-        "fxjs/xfa/cjx_defaultui.cpp",
-        "fxjs/xfa/cjx_defaultui.h",
-        "fxjs/xfa/cjx_delete.cpp",
-        "fxjs/xfa/cjx_delete.h",
-        "fxjs/xfa/cjx_delta.cpp",
-        "fxjs/xfa/cjx_delta.h",
-        "fxjs/xfa/cjx_deltas.cpp",
-        "fxjs/xfa/cjx_deltas.h",
-        "fxjs/xfa/cjx_desc.cpp",
-        "fxjs/xfa/cjx_desc.h",
-        "fxjs/xfa/cjx_digestmethod.cpp",
-        "fxjs/xfa/cjx_digestmethod.h",
-        "fxjs/xfa/cjx_digestmethods.cpp",
-        "fxjs/xfa/cjx_digestmethods.h",
-        "fxjs/xfa/cjx_draw.cpp",
-        "fxjs/xfa/cjx_draw.h",
-        "fxjs/xfa/cjx_edge.cpp",
-        "fxjs/xfa/cjx_edge.h",
-        "fxjs/xfa/cjx_encoding.cpp",
-        "fxjs/xfa/cjx_encoding.h",
-        "fxjs/xfa/cjx_encodings.cpp",
-        "fxjs/xfa/cjx_encodings.h",
-        "fxjs/xfa/cjx_encrypt.cpp",
-        "fxjs/xfa/cjx_encrypt.h",
-        "fxjs/xfa/cjx_event.cpp",
-        "fxjs/xfa/cjx_event.h",
-        "fxjs/xfa/cjx_eventpseudomodel.cpp",
-        "fxjs/xfa/cjx_eventpseudomodel.h",
-        "fxjs/xfa/cjx_exclgroup.cpp",
-        "fxjs/xfa/cjx_exclgroup.h",
-        "fxjs/xfa/cjx_exdata.cpp",
-        "fxjs/xfa/cjx_exdata.h",
-        "fxjs/xfa/cjx_execute.cpp",
-        "fxjs/xfa/cjx_execute.h",
-        "fxjs/xfa/cjx_exobject.cpp",
-        "fxjs/xfa/cjx_exobject.h",
-        "fxjs/xfa/cjx_extras.cpp",
-        "fxjs/xfa/cjx_extras.h",
-        "fxjs/xfa/cjx_field.cpp",
-        "fxjs/xfa/cjx_field.h",
-        "fxjs/xfa/cjx_fill.cpp",
-        "fxjs/xfa/cjx_fill.h",
-        "fxjs/xfa/cjx_filter.cpp",
-        "fxjs/xfa/cjx_filter.h",
-        "fxjs/xfa/cjx_float.cpp",
-        "fxjs/xfa/cjx_float.h",
-        "fxjs/xfa/cjx_font.cpp",
-        "fxjs/xfa/cjx_font.h",
-        "fxjs/xfa/cjx_form.cpp",
-        "fxjs/xfa/cjx_form.h",
-        "fxjs/xfa/cjx_format.cpp",
-        "fxjs/xfa/cjx_format.h",
-        "fxjs/xfa/cjx_handler.cpp",
-        "fxjs/xfa/cjx_handler.h",
-        "fxjs/xfa/cjx_hostpseudomodel.cpp",
-        "fxjs/xfa/cjx_hostpseudomodel.h",
-        "fxjs/xfa/cjx_image.cpp",
-        "fxjs/xfa/cjx_image.h",
-        "fxjs/xfa/cjx_imageedit.cpp",
-        "fxjs/xfa/cjx_imageedit.h",
-        "fxjs/xfa/cjx_insert.cpp",
-        "fxjs/xfa/cjx_insert.h",
-        "fxjs/xfa/cjx_instancemanager.cpp",
-        "fxjs/xfa/cjx_instancemanager.h",
-        "fxjs/xfa/cjx_integer.cpp",
-        "fxjs/xfa/cjx_integer.h",
-        "fxjs/xfa/cjx_issuers.cpp",
-        "fxjs/xfa/cjx_issuers.h",
-        "fxjs/xfa/cjx_items.cpp",
-        "fxjs/xfa/cjx_items.h",
-        "fxjs/xfa/cjx_keep.cpp",
-        "fxjs/xfa/cjx_keep.h",
-        "fxjs/xfa/cjx_keyusage.cpp",
-        "fxjs/xfa/cjx_keyusage.h",
-        "fxjs/xfa/cjx_layoutpseudomodel.cpp",
-        "fxjs/xfa/cjx_layoutpseudomodel.h",
-        "fxjs/xfa/cjx_line.cpp",
-        "fxjs/xfa/cjx_line.h",
-        "fxjs/xfa/cjx_linear.cpp",
-        "fxjs/xfa/cjx_linear.h",
-        "fxjs/xfa/cjx_list.cpp",
-        "fxjs/xfa/cjx_list.h",
-        "fxjs/xfa/cjx_logpseudomodel.cpp",
-        "fxjs/xfa/cjx_logpseudomodel.h",
-        "fxjs/xfa/cjx_manifest.cpp",
-        "fxjs/xfa/cjx_manifest.h",
-        "fxjs/xfa/cjx_map.cpp",
-        "fxjs/xfa/cjx_map.h",
-        "fxjs/xfa/cjx_margin.cpp",
-        "fxjs/xfa/cjx_margin.h",
-        "fxjs/xfa/cjx_mdp.cpp",
-        "fxjs/xfa/cjx_mdp.h",
-        "fxjs/xfa/cjx_medium.cpp",
-        "fxjs/xfa/cjx_medium.h",
-        "fxjs/xfa/cjx_message.cpp",
-        "fxjs/xfa/cjx_message.h",
-        "fxjs/xfa/cjx_model.cpp",
-        "fxjs/xfa/cjx_model.h",
-        "fxjs/xfa/cjx_node.cpp",
-        "fxjs/xfa/cjx_node.h",
-        "fxjs/xfa/cjx_numericedit.cpp",
-        "fxjs/xfa/cjx_numericedit.h",
-        "fxjs/xfa/cjx_object.cpp",
-        "fxjs/xfa/cjx_object.h",
-        "fxjs/xfa/cjx_occur.cpp",
-        "fxjs/xfa/cjx_occur.h",
-        "fxjs/xfa/cjx_oid.cpp",
-        "fxjs/xfa/cjx_oid.h",
-        "fxjs/xfa/cjx_oids.cpp",
-        "fxjs/xfa/cjx_oids.h",
-        "fxjs/xfa/cjx_operation.cpp",
-        "fxjs/xfa/cjx_operation.h",
-        "fxjs/xfa/cjx_overflow.cpp",
-        "fxjs/xfa/cjx_overflow.h",
-        "fxjs/xfa/cjx_packet.cpp",
-        "fxjs/xfa/cjx_packet.h",
-        "fxjs/xfa/cjx_pagearea.cpp",
-        "fxjs/xfa/cjx_pagearea.h",
-        "fxjs/xfa/cjx_pageset.cpp",
-        "fxjs/xfa/cjx_pageset.h",
-        "fxjs/xfa/cjx_para.cpp",
-        "fxjs/xfa/cjx_para.h",
-        "fxjs/xfa/cjx_password.cpp",
-        "fxjs/xfa/cjx_password.h",
-        "fxjs/xfa/cjx_passwordedit.cpp",
-        "fxjs/xfa/cjx_passwordedit.h",
-        "fxjs/xfa/cjx_pattern.cpp",
-        "fxjs/xfa/cjx_pattern.h",
-        "fxjs/xfa/cjx_picture.cpp",
-        "fxjs/xfa/cjx_picture.h",
-        "fxjs/xfa/cjx_query.cpp",
-        "fxjs/xfa/cjx_query.h",
-        "fxjs/xfa/cjx_radial.cpp",
-        "fxjs/xfa/cjx_radial.h",
-        "fxjs/xfa/cjx_reason.cpp",
-        "fxjs/xfa/cjx_reason.h",
-        "fxjs/xfa/cjx_reasons.cpp",
-        "fxjs/xfa/cjx_reasons.h",
-        "fxjs/xfa/cjx_recordset.cpp",
-        "fxjs/xfa/cjx_recordset.h",
-        "fxjs/xfa/cjx_rectangle.cpp",
-        "fxjs/xfa/cjx_rectangle.h",
-        "fxjs/xfa/cjx_ref.cpp",
-        "fxjs/xfa/cjx_ref.h",
-        "fxjs/xfa/cjx_rootelement.cpp",
-        "fxjs/xfa/cjx_rootelement.h",
-        "fxjs/xfa/cjx_script.cpp",
-        "fxjs/xfa/cjx_script.h",
-        "fxjs/xfa/cjx_select.cpp",
-        "fxjs/xfa/cjx_select.h",
-        "fxjs/xfa/cjx_setproperty.cpp",
-        "fxjs/xfa/cjx_setproperty.h",
-        "fxjs/xfa/cjx_signature.cpp",
-        "fxjs/xfa/cjx_signature.h",
-        "fxjs/xfa/cjx_signatureproperties.cpp",
-        "fxjs/xfa/cjx_signatureproperties.h",
-        "fxjs/xfa/cjx_signaturepseudomodel.cpp",
-        "fxjs/xfa/cjx_signaturepseudomodel.h",
-        "fxjs/xfa/cjx_signdata.cpp",
-        "fxjs/xfa/cjx_signdata.h",
-        "fxjs/xfa/cjx_signing.cpp",
-        "fxjs/xfa/cjx_signing.h",
-        "fxjs/xfa/cjx_soapaction.cpp",
-        "fxjs/xfa/cjx_soapaction.h",
-        "fxjs/xfa/cjx_soapaddress.cpp",
-        "fxjs/xfa/cjx_soapaddress.h",
-        "fxjs/xfa/cjx_solid.cpp",
-        "fxjs/xfa/cjx_solid.h",
-        "fxjs/xfa/cjx_source.cpp",
-        "fxjs/xfa/cjx_source.h",
-        "fxjs/xfa/cjx_sourceset.cpp",
-        "fxjs/xfa/cjx_sourceset.h",
-        "fxjs/xfa/cjx_speak.cpp",
-        "fxjs/xfa/cjx_speak.h",
-        "fxjs/xfa/cjx_stipple.cpp",
-        "fxjs/xfa/cjx_stipple.h",
-        "fxjs/xfa/cjx_subform.cpp",
-        "fxjs/xfa/cjx_subform.h",
-        "fxjs/xfa/cjx_subformset.cpp",
-        "fxjs/xfa/cjx_subformset.h",
-        "fxjs/xfa/cjx_subjectdn.cpp",
-        "fxjs/xfa/cjx_subjectdn.h",
-        "fxjs/xfa/cjx_subjectdns.cpp",
-        "fxjs/xfa/cjx_subjectdns.h",
-        "fxjs/xfa/cjx_submit.cpp",
-        "fxjs/xfa/cjx_submit.h",
-        "fxjs/xfa/cjx_template.cpp",
-        "fxjs/xfa/cjx_template.h",
-        "fxjs/xfa/cjx_text.cpp",
-        "fxjs/xfa/cjx_text.h",
-        "fxjs/xfa/cjx_textedit.cpp",
-        "fxjs/xfa/cjx_textedit.h",
-        "fxjs/xfa/cjx_textnode.cpp",
-        "fxjs/xfa/cjx_textnode.h",
-        "fxjs/xfa/cjx_time.cpp",
-        "fxjs/xfa/cjx_time.h",
-        "fxjs/xfa/cjx_timestamp.cpp",
-        "fxjs/xfa/cjx_timestamp.h",
-        "fxjs/xfa/cjx_tooltip.cpp",
-        "fxjs/xfa/cjx_tooltip.h",
-        "fxjs/xfa/cjx_traversal.cpp",
-        "fxjs/xfa/cjx_traversal.h",
-        "fxjs/xfa/cjx_traverse.cpp",
-        "fxjs/xfa/cjx_traverse.h",
-        "fxjs/xfa/cjx_tree.cpp",
-        "fxjs/xfa/cjx_tree.h",
-        "fxjs/xfa/cjx_treelist.cpp",
-        "fxjs/xfa/cjx_treelist.h",
-        "fxjs/xfa/cjx_ui.cpp",
-        "fxjs/xfa/cjx_ui.h",
-        "fxjs/xfa/cjx_update.cpp",
-        "fxjs/xfa/cjx_update.h",
-        "fxjs/xfa/cjx_uri.cpp",
-        "fxjs/xfa/cjx_uri.h",
-        "fxjs/xfa/cjx_user.cpp",
-        "fxjs/xfa/cjx_user.h",
-        "fxjs/xfa/cjx_validate.cpp",
-        "fxjs/xfa/cjx_validate.h",
-        "fxjs/xfa/cjx_value.cpp",
-        "fxjs/xfa/cjx_value.h",
-        "fxjs/xfa/cjx_variables.cpp",
-        "fxjs/xfa/cjx_variables.h",
-        "fxjs/xfa/cjx_wsdladdress.cpp",
-        "fxjs/xfa/cjx_wsdladdress.h",
-        "fxjs/xfa/cjx_wsdlconnection.cpp",
-        "fxjs/xfa/cjx_wsdlconnection.h",
-        "fxjs/xfa/cjx_xfa.cpp",
-        "fxjs/xfa/cjx_xfa.h",
-        "fxjs/xfa/cjx_xmlconnection.cpp",
-        "fxjs/xfa/cjx_xmlconnection.h",
-        "fxjs/xfa/cjx_xsdconnection.cpp",
-        "fxjs/xfa/cjx_xsdconnection.h",
-      ]
-    }
-  } else {
-    sources += [
-      "fxjs/cjs_event_context_stub.cpp",
-      "fxjs/cjs_event_context_stub.h",
-      "fxjs/cjs_runtimestub.cpp",
-    ]
-  }
-}
-
-jumbo_static_library("formfiller") {
-  sources = [
-    "fpdfsdk/formfiller/cba_fontmap.cpp",
-    "fpdfsdk/formfiller/cba_fontmap.h",
-    "fpdfsdk/formfiller/cffl_button.cpp",
-    "fpdfsdk/formfiller/cffl_button.h",
-    "fpdfsdk/formfiller/cffl_checkbox.cpp",
-    "fpdfsdk/formfiller/cffl_checkbox.h",
-    "fpdfsdk/formfiller/cffl_combobox.cpp",
-    "fpdfsdk/formfiller/cffl_combobox.h",
-    "fpdfsdk/formfiller/cffl_formfiller.cpp",
-    "fpdfsdk/formfiller/cffl_formfiller.h",
-    "fpdfsdk/formfiller/cffl_interactiveformfiller.cpp",
-    "fpdfsdk/formfiller/cffl_interactiveformfiller.h",
-    "fpdfsdk/formfiller/cffl_listbox.cpp",
-    "fpdfsdk/formfiller/cffl_listbox.h",
-    "fpdfsdk/formfiller/cffl_pushbutton.cpp",
-    "fpdfsdk/formfiller/cffl_pushbutton.h",
-    "fpdfsdk/formfiller/cffl_radiobutton.cpp",
-    "fpdfsdk/formfiller/cffl_radiobutton.h",
-    "fpdfsdk/formfiller/cffl_textfield.cpp",
-    "fpdfsdk/formfiller/cffl_textfield.h",
-    "fpdfsdk/formfiller/cffl_textobject.cpp",
-    "fpdfsdk/formfiller/cffl_textobject.h",
-  ]
-  configs += [ ":pdfium_core_config" ]
-  deps = [
-    ":fxcrt",
-  ]
-}
-
-if (pdf_enable_xfa) {
-  jumbo_static_library("fpdfxfa") {
-    sources = [
-      "fpdfsdk/fpdfxfa/cpdfxfa_context.cpp",
-      "fpdfsdk/fpdfxfa/cpdfxfa_context.h",
-      "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp",
-      "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h",
-      "fpdfsdk/fpdfxfa/cpdfxfa_page.cpp",
-      "fpdfsdk/fpdfxfa/cpdfxfa_page.h",
-      "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.cpp",
-      "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h",
-    ]
-    deps = [
-      ":fxcrt",
-      ":fxjs",
-      ":xfa",
-    ]
-    configs += [ ":pdfium_core_config" ]
-  }
-
-  jumbo_static_library("fxbarcode") {
-    sources = [
-      "fxbarcode/BC_Library.cpp",
-      "fxbarcode/BC_Library.h",
-      "fxbarcode/BC_TwoDimWriter.cpp",
-      "fxbarcode/BC_TwoDimWriter.h",
-      "fxbarcode/BC_UtilCodingConvert.cpp",
-      "fxbarcode/BC_UtilCodingConvert.h",
-      "fxbarcode/BC_Utils.cpp",
-      "fxbarcode/BC_Writer.cpp",
-      "fxbarcode/BC_Writer.h",
-      "fxbarcode/cbc_codabar.cpp",
-      "fxbarcode/cbc_codabar.h",
-      "fxbarcode/cbc_code128.cpp",
-      "fxbarcode/cbc_code128.h",
-      "fxbarcode/cbc_code39.cpp",
-      "fxbarcode/cbc_code39.h",
-      "fxbarcode/cbc_codebase.cpp",
-      "fxbarcode/cbc_codebase.h",
-      "fxbarcode/cbc_datamatrix.cpp",
-      "fxbarcode/cbc_datamatrix.h",
-      "fxbarcode/cbc_ean13.cpp",
-      "fxbarcode/cbc_ean13.h",
-      "fxbarcode/cbc_ean8.cpp",
-      "fxbarcode/cbc_ean8.h",
-      "fxbarcode/cbc_onecode.cpp",
-      "fxbarcode/cbc_onecode.h",
-      "fxbarcode/cbc_pdf417i.cpp",
-      "fxbarcode/cbc_pdf417i.h",
-      "fxbarcode/cbc_qrcode.cpp",
-      "fxbarcode/cbc_qrcode.h",
-      "fxbarcode/cbc_upca.cpp",
-      "fxbarcode/cbc_upca.h",
-      "fxbarcode/common/BC_CommonBitArray.cpp",
-      "fxbarcode/common/BC_CommonBitArray.h",
-      "fxbarcode/common/BC_CommonBitMatrix.cpp",
-      "fxbarcode/common/BC_CommonBitMatrix.h",
-      "fxbarcode/common/BC_CommonByteArray.cpp",
-      "fxbarcode/common/BC_CommonByteArray.h",
-      "fxbarcode/common/BC_CommonByteMatrix.cpp",
-      "fxbarcode/common/BC_CommonByteMatrix.h",
-      "fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp",
-      "fxbarcode/common/reedsolomon/BC_ReedSolomon.h",
-      "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp",
-      "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h",
-      "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp",
-      "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h",
-      "fxbarcode/datamatrix/BC_ASCIIEncoder.cpp",
-      "fxbarcode/datamatrix/BC_ASCIIEncoder.h",
-      "fxbarcode/datamatrix/BC_Base256Encoder.cpp",
-      "fxbarcode/datamatrix/BC_Base256Encoder.h",
-      "fxbarcode/datamatrix/BC_C40Encoder.cpp",
-      "fxbarcode/datamatrix/BC_C40Encoder.h",
-      "fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp",
-      "fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h",
-      "fxbarcode/datamatrix/BC_DataMatrixWriter.cpp",
-      "fxbarcode/datamatrix/BC_DataMatrixWriter.h",
-      "fxbarcode/datamatrix/BC_DefaultPlacement.cpp",
-      "fxbarcode/datamatrix/BC_DefaultPlacement.h",
-      "fxbarcode/datamatrix/BC_EdifactEncoder.cpp",
-      "fxbarcode/datamatrix/BC_EdifactEncoder.h",
-      "fxbarcode/datamatrix/BC_Encoder.cpp",
-      "fxbarcode/datamatrix/BC_Encoder.h",
-      "fxbarcode/datamatrix/BC_EncoderContext.cpp",
-      "fxbarcode/datamatrix/BC_EncoderContext.h",
-      "fxbarcode/datamatrix/BC_ErrorCorrection.cpp",
-      "fxbarcode/datamatrix/BC_ErrorCorrection.h",
-      "fxbarcode/datamatrix/BC_HighLevelEncoder.cpp",
-      "fxbarcode/datamatrix/BC_HighLevelEncoder.h",
-      "fxbarcode/datamatrix/BC_SymbolInfo.cpp",
-      "fxbarcode/datamatrix/BC_SymbolInfo.h",
-      "fxbarcode/datamatrix/BC_TextEncoder.cpp",
-      "fxbarcode/datamatrix/BC_TextEncoder.h",
-      "fxbarcode/datamatrix/BC_X12Encoder.cpp",
-      "fxbarcode/datamatrix/BC_X12Encoder.h",
-      "fxbarcode/oned/BC_OneDimWriter.cpp",
-      "fxbarcode/oned/BC_OneDimWriter.h",
-      "fxbarcode/oned/BC_OnedCodaBarWriter.cpp",
-      "fxbarcode/oned/BC_OnedCodaBarWriter.h",
-      "fxbarcode/oned/BC_OnedCode128Writer.cpp",
-      "fxbarcode/oned/BC_OnedCode128Writer.h",
-      "fxbarcode/oned/BC_OnedCode39Writer.cpp",
-      "fxbarcode/oned/BC_OnedCode39Writer.h",
-      "fxbarcode/oned/BC_OnedEAN13Writer.cpp",
-      "fxbarcode/oned/BC_OnedEAN13Writer.h",
-      "fxbarcode/oned/BC_OnedEAN8Writer.cpp",
-      "fxbarcode/oned/BC_OnedEAN8Writer.h",
-      "fxbarcode/oned/BC_OnedEANChecksum.cpp",
-      "fxbarcode/oned/BC_OnedEANChecksum.h",
-      "fxbarcode/oned/BC_OnedUPCAWriter.cpp",
-      "fxbarcode/oned/BC_OnedUPCAWriter.h",
-      "fxbarcode/pdf417/BC_PDF417.cpp",
-      "fxbarcode/pdf417/BC_PDF417.h",
-      "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp",
-      "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h",
-      "fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp",
-      "fxbarcode/pdf417/BC_PDF417BarcodeRow.h",
-      "fxbarcode/pdf417/BC_PDF417Compaction.cpp",
-      "fxbarcode/pdf417/BC_PDF417Compaction.h",
-      "fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp",
-      "fxbarcode/pdf417/BC_PDF417ErrorCorrection.h",
-      "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp",
-      "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h",
-      "fxbarcode/pdf417/BC_PDF417Writer.cpp",
-      "fxbarcode/pdf417/BC_PDF417Writer.h",
-      "fxbarcode/qrcode/BC_QRCodeWriter.cpp",
-      "fxbarcode/qrcode/BC_QRCodeWriter.h",
-      "fxbarcode/qrcode/BC_QRCoder.cpp",
-      "fxbarcode/qrcode/BC_QRCoder.h",
-      "fxbarcode/qrcode/BC_QRCoderBitVector.cpp",
-      "fxbarcode/qrcode/BC_QRCoderBitVector.h",
-      "fxbarcode/qrcode/BC_QRCoderBlockPair.cpp",
-      "fxbarcode/qrcode/BC_QRCoderBlockPair.h",
-      "fxbarcode/qrcode/BC_QRCoderECBlocks.cpp",
-      "fxbarcode/qrcode/BC_QRCoderECBlocks.h",
-      "fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp",
-      "fxbarcode/qrcode/BC_QRCoderECBlocksData.h",
-      "fxbarcode/qrcode/BC_QRCoderEncoder.cpp",
-      "fxbarcode/qrcode/BC_QRCoderEncoder.h",
-      "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp",
-      "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h",
-      "fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp",
-      "fxbarcode/qrcode/BC_QRCoderMaskUtil.h",
-      "fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp",
-      "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h",
-      "fxbarcode/qrcode/BC_QRCoderMode.cpp",
-      "fxbarcode/qrcode/BC_QRCoderMode.h",
-      "fxbarcode/qrcode/BC_QRCoderVersion.cpp",
-      "fxbarcode/qrcode/BC_QRCoderVersion.h",
-      "fxbarcode/utils.h",
-    ]
-    deps = [
-      ":fxcrt",
-    ]
-    configs += [ ":pdfium_core_config" ]
-  }
-
-  # TODO(crbug.com/pdfium/964): Support jumbo builds.
-  static_library("xfa") {
-    sources = [
-      "xfa/fde/cfde_texteditengine.cpp",
-      "xfa/fde/cfde_texteditengine.h",
-      "xfa/fde/cfde_textout.cpp",
-      "xfa/fde/cfde_textout.h",
-      "xfa/fde/cfde_wordbreak_data.cpp",
-      "xfa/fde/cfde_wordbreak_data.h",
-      "xfa/fgas/crt/cfgas_formatstring.cpp",
-      "xfa/fgas/crt/cfgas_formatstring.h",
-      "xfa/fgas/font/cfgas_defaultfontmanager.cpp",
-      "xfa/fgas/font/cfgas_defaultfontmanager.h",
-      "xfa/fgas/font/cfgas_fontmgr.cpp",
-      "xfa/fgas/font/cfgas_fontmgr.h",
-      "xfa/fgas/font/cfgas_gefont.cpp",
-      "xfa/fgas/font/cfgas_gefont.h",
-      "xfa/fgas/font/cfgas_pdffontmgr.cpp",
-      "xfa/fgas/font/cfgas_pdffontmgr.h",
-      "xfa/fgas/font/fgas_fontutils.cpp",
-      "xfa/fgas/font/fgas_fontutils.h",
-      "xfa/fgas/layout/cfx_break.cpp",
-      "xfa/fgas/layout/cfx_break.h",
-      "xfa/fgas/layout/cfx_breakline.cpp",
-      "xfa/fgas/layout/cfx_breakline.h",
-      "xfa/fgas/layout/cfx_breakpiece.cpp",
-      "xfa/fgas/layout/cfx_breakpiece.h",
-      "xfa/fgas/layout/cfx_linebreak.cpp",
-      "xfa/fgas/layout/cfx_linebreak.h",
-      "xfa/fgas/layout/cfx_rtfbreak.cpp",
-      "xfa/fgas/layout/cfx_rtfbreak.h",
-      "xfa/fgas/layout/cfx_txtbreak.cpp",
-      "xfa/fgas/layout/cfx_txtbreak.h",
-      "xfa/fwl/cfwl_app.cpp",
-      "xfa/fwl/cfwl_app.h",
-      "xfa/fwl/cfwl_barcode.cpp",
-      "xfa/fwl/cfwl_barcode.h",
-      "xfa/fwl/cfwl_caret.cpp",
-      "xfa/fwl/cfwl_caret.h",
-      "xfa/fwl/cfwl_checkbox.cpp",
-      "xfa/fwl/cfwl_checkbox.h",
-      "xfa/fwl/cfwl_combobox.cpp",
-      "xfa/fwl/cfwl_combobox.h",
-      "xfa/fwl/cfwl_comboboxproxy.cpp",
-      "xfa/fwl/cfwl_comboboxproxy.h",
-      "xfa/fwl/cfwl_comboedit.cpp",
-      "xfa/fwl/cfwl_comboedit.h",
-      "xfa/fwl/cfwl_combolist.cpp",
-      "xfa/fwl/cfwl_combolist.h",
-      "xfa/fwl/cfwl_datetimeedit.cpp",
-      "xfa/fwl/cfwl_datetimeedit.h",
-      "xfa/fwl/cfwl_datetimepicker.cpp",
-      "xfa/fwl/cfwl_datetimepicker.h",
-      "xfa/fwl/cfwl_edit.cpp",
-      "xfa/fwl/cfwl_edit.h",
-      "xfa/fwl/cfwl_event.cpp",
-      "xfa/fwl/cfwl_event.h",
-      "xfa/fwl/cfwl_eventcheckword.cpp",
-      "xfa/fwl/cfwl_eventcheckword.h",
-      "xfa/fwl/cfwl_eventmouse.cpp",
-      "xfa/fwl/cfwl_eventmouse.h",
-      "xfa/fwl/cfwl_eventscroll.cpp",
-      "xfa/fwl/cfwl_eventscroll.h",
-      "xfa/fwl/cfwl_eventselectchanged.cpp",
-      "xfa/fwl/cfwl_eventselectchanged.h",
-      "xfa/fwl/cfwl_eventtarget.cpp",
-      "xfa/fwl/cfwl_eventtarget.h",
-      "xfa/fwl/cfwl_eventtextchanged.cpp",
-      "xfa/fwl/cfwl_eventtextchanged.h",
-      "xfa/fwl/cfwl_eventvalidate.cpp",
-      "xfa/fwl/cfwl_eventvalidate.h",
-      "xfa/fwl/cfwl_form.cpp",
-      "xfa/fwl/cfwl_form.h",
-      "xfa/fwl/cfwl_formproxy.cpp",
-      "xfa/fwl/cfwl_formproxy.h",
-      "xfa/fwl/cfwl_listbox.cpp",
-      "xfa/fwl/cfwl_listbox.h",
-      "xfa/fwl/cfwl_listitem.cpp",
-      "xfa/fwl/cfwl_listitem.h",
-      "xfa/fwl/cfwl_message.cpp",
-      "xfa/fwl/cfwl_message.h",
-      "xfa/fwl/cfwl_messagekey.cpp",
-      "xfa/fwl/cfwl_messagekey.h",
-      "xfa/fwl/cfwl_messagekillfocus.cpp",
-      "xfa/fwl/cfwl_messagekillfocus.h",
-      "xfa/fwl/cfwl_messagemouse.cpp",
-      "xfa/fwl/cfwl_messagemouse.h",
-      "xfa/fwl/cfwl_messagemousewheel.cpp",
-      "xfa/fwl/cfwl_messagemousewheel.h",
-      "xfa/fwl/cfwl_messagesetfocus.cpp",
-      "xfa/fwl/cfwl_messagesetfocus.h",
-      "xfa/fwl/cfwl_monthcalendar.cpp",
-      "xfa/fwl/cfwl_monthcalendar.h",
-      "xfa/fwl/cfwl_notedriver.cpp",
-      "xfa/fwl/cfwl_notedriver.h",
-      "xfa/fwl/cfwl_noteloop.cpp",
-      "xfa/fwl/cfwl_noteloop.h",
-      "xfa/fwl/cfwl_picturebox.cpp",
-      "xfa/fwl/cfwl_picturebox.h",
-      "xfa/fwl/cfwl_pushbutton.cpp",
-      "xfa/fwl/cfwl_pushbutton.h",
-      "xfa/fwl/cfwl_scrollbar.cpp",
-      "xfa/fwl/cfwl_scrollbar.h",
-      "xfa/fwl/cfwl_themebackground.h",
-      "xfa/fwl/cfwl_themepart.cpp",
-      "xfa/fwl/cfwl_themepart.h",
-      "xfa/fwl/cfwl_themetext.h",
-      "xfa/fwl/cfwl_timer.cpp",
-      "xfa/fwl/cfwl_timer.h",
-      "xfa/fwl/cfwl_timerinfo.cpp",
-      "xfa/fwl/cfwl_timerinfo.h",
-      "xfa/fwl/cfwl_widget.cpp",
-      "xfa/fwl/cfwl_widget.h",
-      "xfa/fwl/cfwl_widgetmgr.cpp",
-      "xfa/fwl/cfwl_widgetmgr.h",
-      "xfa/fwl/cfwl_widgetproperties.cpp",
-      "xfa/fwl/cfwl_widgetproperties.h",
-      "xfa/fwl/cfx_barcode.cpp",
-      "xfa/fwl/cfx_barcode.h",
-      "xfa/fwl/fwl_widgetdef.h",
-      "xfa/fwl/fwl_widgethit.h",
-      "xfa/fwl/ifwl_adaptertimermgr.h",
-      "xfa/fwl/ifwl_themeprovider.h",
-      "xfa/fwl/ifwl_widgetdelegate.h",
-      "xfa/fwl/theme/cfwl_barcodetp.cpp",
-      "xfa/fwl/theme/cfwl_barcodetp.h",
-      "xfa/fwl/theme/cfwl_carettp.cpp",
-      "xfa/fwl/theme/cfwl_carettp.h",
-      "xfa/fwl/theme/cfwl_checkboxtp.cpp",
-      "xfa/fwl/theme/cfwl_checkboxtp.h",
-      "xfa/fwl/theme/cfwl_comboboxtp.cpp",
-      "xfa/fwl/theme/cfwl_comboboxtp.h",
-      "xfa/fwl/theme/cfwl_datetimepickertp.cpp",
-      "xfa/fwl/theme/cfwl_datetimepickertp.h",
-      "xfa/fwl/theme/cfwl_edittp.cpp",
-      "xfa/fwl/theme/cfwl_edittp.h",
-      "xfa/fwl/theme/cfwl_listboxtp.cpp",
-      "xfa/fwl/theme/cfwl_listboxtp.h",
-      "xfa/fwl/theme/cfwl_monthcalendartp.cpp",
-      "xfa/fwl/theme/cfwl_monthcalendartp.h",
-      "xfa/fwl/theme/cfwl_pictureboxtp.cpp",
-      "xfa/fwl/theme/cfwl_pictureboxtp.h",
-      "xfa/fwl/theme/cfwl_pushbuttontp.cpp",
-      "xfa/fwl/theme/cfwl_pushbuttontp.h",
-      "xfa/fwl/theme/cfwl_scrollbartp.cpp",
-      "xfa/fwl/theme/cfwl_scrollbartp.h",
-      "xfa/fwl/theme/cfwl_utils.h",
-      "xfa/fwl/theme/cfwl_widgettp.cpp",
-      "xfa/fwl/theme/cfwl_widgettp.h",
-      "xfa/fxfa/cxfa_eventparam.cpp",
-      "xfa/fxfa/cxfa_eventparam.h",
-      "xfa/fxfa/cxfa_ffapp.cpp",
-      "xfa/fxfa/cxfa_ffapp.h",
-      "xfa/fxfa/cxfa_ffarc.cpp",
-      "xfa/fxfa/cxfa_ffarc.h",
-      "xfa/fxfa/cxfa_ffbarcode.cpp",
-      "xfa/fxfa/cxfa_ffbarcode.h",
-      "xfa/fxfa/cxfa_ffcheckbutton.cpp",
-      "xfa/fxfa/cxfa_ffcheckbutton.h",
-      "xfa/fxfa/cxfa_ffcombobox.cpp",
-      "xfa/fxfa/cxfa_ffcombobox.h",
-      "xfa/fxfa/cxfa_ffdatetimeedit.cpp",
-      "xfa/fxfa/cxfa_ffdatetimeedit.h",
-      "xfa/fxfa/cxfa_ffdoc.cpp",
-      "xfa/fxfa/cxfa_ffdoc.h",
-      "xfa/fxfa/cxfa_ffdocview.cpp",
-      "xfa/fxfa/cxfa_ffdocview.h",
-      "xfa/fxfa/cxfa_ffdraw.cpp",
-      "xfa/fxfa/cxfa_ffdraw.h",
-      "xfa/fxfa/cxfa_ffexclgroup.cpp",
-      "xfa/fxfa/cxfa_ffexclgroup.h",
-      "xfa/fxfa/cxfa_fffield.cpp",
-      "xfa/fxfa/cxfa_fffield.h",
-      "xfa/fxfa/cxfa_ffimage.cpp",
-      "xfa/fxfa/cxfa_ffimage.h",
-      "xfa/fxfa/cxfa_ffimageedit.cpp",
-      "xfa/fxfa/cxfa_ffimageedit.h",
-      "xfa/fxfa/cxfa_ffline.cpp",
-      "xfa/fxfa/cxfa_ffline.h",
-      "xfa/fxfa/cxfa_fflistbox.cpp",
-      "xfa/fxfa/cxfa_fflistbox.h",
-      "xfa/fxfa/cxfa_ffnotify.cpp",
-      "xfa/fxfa/cxfa_ffnotify.h",
-      "xfa/fxfa/cxfa_ffnumericedit.cpp",
-      "xfa/fxfa/cxfa_ffnumericedit.h",
-      "xfa/fxfa/cxfa_ffpageview.cpp",
-      "xfa/fxfa/cxfa_ffpageview.h",
-      "xfa/fxfa/cxfa_ffpasswordedit.cpp",
-      "xfa/fxfa/cxfa_ffpasswordedit.h",
-      "xfa/fxfa/cxfa_ffpushbutton.cpp",
-      "xfa/fxfa/cxfa_ffpushbutton.h",
-      "xfa/fxfa/cxfa_ffrectangle.cpp",
-      "xfa/fxfa/cxfa_ffrectangle.h",
-      "xfa/fxfa/cxfa_ffsignature.cpp",
-      "xfa/fxfa/cxfa_ffsignature.h",
-      "xfa/fxfa/cxfa_ffsubform.cpp",
-      "xfa/fxfa/cxfa_ffsubform.h",
-      "xfa/fxfa/cxfa_fftext.cpp",
-      "xfa/fxfa/cxfa_fftext.h",
-      "xfa/fxfa/cxfa_fftextedit.cpp",
-      "xfa/fxfa/cxfa_fftextedit.h",
-      "xfa/fxfa/cxfa_ffwidget.cpp",
-      "xfa/fxfa/cxfa_ffwidget.h",
-      "xfa/fxfa/cxfa_ffwidgethandler.cpp",
-      "xfa/fxfa/cxfa_ffwidgethandler.h",
-      "xfa/fxfa/cxfa_fontmgr.cpp",
-      "xfa/fxfa/cxfa_fontmgr.h",
-      "xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp",
-      "xfa/fxfa/cxfa_fwladapterwidgetmgr.h",
-      "xfa/fxfa/cxfa_fwltheme.cpp",
-      "xfa/fxfa/cxfa_fwltheme.h",
-      "xfa/fxfa/cxfa_imagerenderer.cpp",
-      "xfa/fxfa/cxfa_imagerenderer.h",
-      "xfa/fxfa/cxfa_linkuserdata.cpp",
-      "xfa/fxfa/cxfa_linkuserdata.h",
-      "xfa/fxfa/cxfa_loadercontext.cpp",
-      "xfa/fxfa/cxfa_loadercontext.h",
-      "xfa/fxfa/cxfa_pieceline.cpp",
-      "xfa/fxfa/cxfa_pieceline.h",
-      "xfa/fxfa/cxfa_rendercontext.cpp",
-      "xfa/fxfa/cxfa_rendercontext.h",
-      "xfa/fxfa/cxfa_textlayout.cpp",
-      "xfa/fxfa/cxfa_textlayout.h",
-      "xfa/fxfa/cxfa_textparsecontext.cpp",
-      "xfa/fxfa/cxfa_textparsecontext.h",
-      "xfa/fxfa/cxfa_textparser.cpp",
-      "xfa/fxfa/cxfa_textparser.h",
-      "xfa/fxfa/cxfa_textpiece.cpp",
-      "xfa/fxfa/cxfa_textpiece.h",
-      "xfa/fxfa/cxfa_textprovider.cpp",
-      "xfa/fxfa/cxfa_textprovider.h",
-      "xfa/fxfa/cxfa_texttabstopscontext.cpp",
-      "xfa/fxfa/cxfa_texttabstopscontext.h",
-      "xfa/fxfa/cxfa_textuserdata.cpp",
-      "xfa/fxfa/cxfa_textuserdata.h",
-      "xfa/fxfa/cxfa_widgetacc.cpp",
-      "xfa/fxfa/cxfa_widgetacc.h",
-      "xfa/fxfa/cxfa_widgetacciterator.cpp",
-      "xfa/fxfa/cxfa_widgetacciterator.h",
-      "xfa/fxfa/fm2js/cxfa_fmexpression.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmexpression.h",
-      "xfa/fxfa/fm2js/cxfa_fmlexer.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmlexer.h",
-      "xfa/fxfa/fm2js/cxfa_fmparser.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmparser.h",
-      "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h",
-      "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h",
-      "xfa/fxfa/fxfa.h",
-      "xfa/fxfa/fxfa_basic.h",
-      "xfa/fxfa/parser/cscript_datawindow.cpp",
-      "xfa/fxfa/parser/cscript_datawindow.h",
-      "xfa/fxfa/parser/cscript_eventpseudomodel.cpp",
-      "xfa/fxfa/parser/cscript_eventpseudomodel.h",
-      "xfa/fxfa/parser/cscript_hostpseudomodel.cpp",
-      "xfa/fxfa/parser/cscript_hostpseudomodel.h",
-      "xfa/fxfa/parser/cscript_layoutpseudomodel.cpp",
-      "xfa/fxfa/parser/cscript_layoutpseudomodel.h",
-      "xfa/fxfa/parser/cscript_logpseudomodel.cpp",
-      "xfa/fxfa/parser/cscript_logpseudomodel.h",
-      "xfa/fxfa/parser/cscript_signaturepseudomodel.cpp",
-      "xfa/fxfa/parser/cscript_signaturepseudomodel.h",
-      "xfa/fxfa/parser/cxfa_accessiblecontent.cpp",
-      "xfa/fxfa/parser/cxfa_accessiblecontent.h",
-      "xfa/fxfa/parser/cxfa_acrobat.cpp",
-      "xfa/fxfa/parser/cxfa_acrobat.h",
-      "xfa/fxfa/parser/cxfa_acrobat7.cpp",
-      "xfa/fxfa/parser/cxfa_acrobat7.h",
-      "xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp",
-      "xfa/fxfa/parser/cxfa_adbe_jsconsole.h",
-      "xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp",
-      "xfa/fxfa/parser/cxfa_adbe_jsdebugger.h",
-      "xfa/fxfa/parser/cxfa_addsilentprint.cpp",
-      "xfa/fxfa/parser/cxfa_addsilentprint.h",
-      "xfa/fxfa/parser/cxfa_addviewerpreferences.cpp",
-      "xfa/fxfa/parser/cxfa_addviewerpreferences.h",
-      "xfa/fxfa/parser/cxfa_adjustdata.cpp",
-      "xfa/fxfa/parser/cxfa_adjustdata.h",
-      "xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp",
-      "xfa/fxfa/parser/cxfa_adobeextensionlevel.h",
-      "xfa/fxfa/parser/cxfa_agent.cpp",
-      "xfa/fxfa/parser/cxfa_agent.h",
-      "xfa/fxfa/parser/cxfa_alwaysembed.cpp",
-      "xfa/fxfa/parser/cxfa_alwaysembed.h",
-      "xfa/fxfa/parser/cxfa_amd.cpp",
-      "xfa/fxfa/parser/cxfa_amd.h",
-      "xfa/fxfa/parser/cxfa_appearancefilter.cpp",
-      "xfa/fxfa/parser/cxfa_appearancefilter.h",
-      "xfa/fxfa/parser/cxfa_arc.cpp",
-      "xfa/fxfa/parser/cxfa_arc.h",
-      "xfa/fxfa/parser/cxfa_area.cpp",
-      "xfa/fxfa/parser/cxfa_area.h",
-      "xfa/fxfa/parser/cxfa_arraynodelist.cpp",
-      "xfa/fxfa/parser/cxfa_arraynodelist.h",
-      "xfa/fxfa/parser/cxfa_assist.cpp",
-      "xfa/fxfa/parser/cxfa_assist.h",
-      "xfa/fxfa/parser/cxfa_attachnodelist.cpp",
-      "xfa/fxfa/parser/cxfa_attachnodelist.h",
-      "xfa/fxfa/parser/cxfa_attributes.cpp",
-      "xfa/fxfa/parser/cxfa_attributes.h",
-      "xfa/fxfa/parser/cxfa_autosave.cpp",
-      "xfa/fxfa/parser/cxfa_autosave.h",
-      "xfa/fxfa/parser/cxfa_barcode.cpp",
-      "xfa/fxfa/parser/cxfa_barcode.h",
-      "xfa/fxfa/parser/cxfa_base.cpp",
-      "xfa/fxfa/parser/cxfa_base.h",
-      "xfa/fxfa/parser/cxfa_batchoutput.cpp",
-      "xfa/fxfa/parser/cxfa_batchoutput.h",
-      "xfa/fxfa/parser/cxfa_behavioroverride.cpp",
-      "xfa/fxfa/parser/cxfa_behavioroverride.h",
-      "xfa/fxfa/parser/cxfa_bind.cpp",
-      "xfa/fxfa/parser/cxfa_bind.h",
-      "xfa/fxfa/parser/cxfa_binditems.cpp",
-      "xfa/fxfa/parser/cxfa_binditems.h",
-      "xfa/fxfa/parser/cxfa_bookend.cpp",
-      "xfa/fxfa/parser/cxfa_bookend.h",
-      "xfa/fxfa/parser/cxfa_boolean.cpp",
-      "xfa/fxfa/parser/cxfa_boolean.h",
-      "xfa/fxfa/parser/cxfa_border.cpp",
-      "xfa/fxfa/parser/cxfa_border.h",
-      "xfa/fxfa/parser/cxfa_box.cpp",
-      "xfa/fxfa/parser/cxfa_box.h",
-      "xfa/fxfa/parser/cxfa_break.cpp",
-      "xfa/fxfa/parser/cxfa_break.h",
-      "xfa/fxfa/parser/cxfa_breakafter.cpp",
-      "xfa/fxfa/parser/cxfa_breakafter.h",
-      "xfa/fxfa/parser/cxfa_breakbefore.cpp",
-      "xfa/fxfa/parser/cxfa_breakbefore.h",
-      "xfa/fxfa/parser/cxfa_button.cpp",
-      "xfa/fxfa/parser/cxfa_button.h",
-      "xfa/fxfa/parser/cxfa_cache.cpp",
-      "xfa/fxfa/parser/cxfa_cache.h",
-      "xfa/fxfa/parser/cxfa_calculate.cpp",
-      "xfa/fxfa/parser/cxfa_calculate.h",
-      "xfa/fxfa/parser/cxfa_calendarsymbols.cpp",
-      "xfa/fxfa/parser/cxfa_calendarsymbols.h",
-      "xfa/fxfa/parser/cxfa_caption.cpp",
-      "xfa/fxfa/parser/cxfa_caption.h",
-      "xfa/fxfa/parser/cxfa_certificate.cpp",
-      "xfa/fxfa/parser/cxfa_certificate.h",
-      "xfa/fxfa/parser/cxfa_certificates.cpp",
-      "xfa/fxfa/parser/cxfa_certificates.h",
-      "xfa/fxfa/parser/cxfa_change.cpp",
-      "xfa/fxfa/parser/cxfa_change.h",
-      "xfa/fxfa/parser/cxfa_checkbutton.cpp",
-      "xfa/fxfa/parser/cxfa_checkbutton.h",
-      "xfa/fxfa/parser/cxfa_choicelist.cpp",
-      "xfa/fxfa/parser/cxfa_choicelist.h",
-      "xfa/fxfa/parser/cxfa_color.cpp",
-      "xfa/fxfa/parser/cxfa_color.h",
-      "xfa/fxfa/parser/cxfa_comb.cpp",
-      "xfa/fxfa/parser/cxfa_comb.h",
-      "xfa/fxfa/parser/cxfa_command.cpp",
-      "xfa/fxfa/parser/cxfa_command.h",
-      "xfa/fxfa/parser/cxfa_common.cpp",
-      "xfa/fxfa/parser/cxfa_common.h",
-      "xfa/fxfa/parser/cxfa_compress.cpp",
-      "xfa/fxfa/parser/cxfa_compress.h",
-      "xfa/fxfa/parser/cxfa_compression.cpp",
-      "xfa/fxfa/parser/cxfa_compression.h",
-      "xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp",
-      "xfa/fxfa/parser/cxfa_compresslogicalstructure.h",
-      "xfa/fxfa/parser/cxfa_compressobjectstream.cpp",
-      "xfa/fxfa/parser/cxfa_compressobjectstream.h",
-      "xfa/fxfa/parser/cxfa_config.cpp",
-      "xfa/fxfa/parser/cxfa_config.h",
-      "xfa/fxfa/parser/cxfa_conformance.cpp",
-      "xfa/fxfa/parser/cxfa_conformance.h",
-      "xfa/fxfa/parser/cxfa_connect.cpp",
-      "xfa/fxfa/parser/cxfa_connect.h",
-      "xfa/fxfa/parser/cxfa_connectionset.cpp",
-      "xfa/fxfa/parser/cxfa_connectionset.h",
-      "xfa/fxfa/parser/cxfa_connectstring.cpp",
-      "xfa/fxfa/parser/cxfa_connectstring.h",
-      "xfa/fxfa/parser/cxfa_containerlayoutitem.cpp",
-      "xfa/fxfa/parser/cxfa_containerlayoutitem.h",
-      "xfa/fxfa/parser/cxfa_contentarea.cpp",
-      "xfa/fxfa/parser/cxfa_contentarea.h",
-      "xfa/fxfa/parser/cxfa_contentcopy.cpp",
-      "xfa/fxfa/parser/cxfa_contentcopy.h",
-      "xfa/fxfa/parser/cxfa_contentlayoutitem.cpp",
-      "xfa/fxfa/parser/cxfa_contentlayoutitem.h",
-      "xfa/fxfa/parser/cxfa_copies.cpp",
-      "xfa/fxfa/parser/cxfa_copies.h",
-      "xfa/fxfa/parser/cxfa_corner.cpp",
-      "xfa/fxfa/parser/cxfa_corner.h",
-      "xfa/fxfa/parser/cxfa_creator.cpp",
-      "xfa/fxfa/parser/cxfa_creator.h",
-      "xfa/fxfa/parser/cxfa_currencysymbol.cpp",
-      "xfa/fxfa/parser/cxfa_currencysymbol.h",
-      "xfa/fxfa/parser/cxfa_currencysymbols.cpp",
-      "xfa/fxfa/parser/cxfa_currencysymbols.h",
-      "xfa/fxfa/parser/cxfa_currentpage.cpp",
-      "xfa/fxfa/parser/cxfa_currentpage.h",
-      "xfa/fxfa/parser/cxfa_data.cpp",
-      "xfa/fxfa/parser/cxfa_data.h",
-      "xfa/fxfa/parser/cxfa_dataexporter.cpp",
-      "xfa/fxfa/parser/cxfa_dataexporter.h",
-      "xfa/fxfa/parser/cxfa_datagroup.cpp",
-      "xfa/fxfa/parser/cxfa_datagroup.h",
-      "xfa/fxfa/parser/cxfa_dataimporter.cpp",
-      "xfa/fxfa/parser/cxfa_dataimporter.h",
-      "xfa/fxfa/parser/cxfa_datamodel.cpp",
-      "xfa/fxfa/parser/cxfa_datamodel.h",
-      "xfa/fxfa/parser/cxfa_datavalue.cpp",
-      "xfa/fxfa/parser/cxfa_datavalue.h",
-      "xfa/fxfa/parser/cxfa_date.cpp",
-      "xfa/fxfa/parser/cxfa_date.h",
-      "xfa/fxfa/parser/cxfa_datepattern.cpp",
-      "xfa/fxfa/parser/cxfa_datepattern.h",
-      "xfa/fxfa/parser/cxfa_datepatterns.cpp",
-      "xfa/fxfa/parser/cxfa_datepatterns.h",
-      "xfa/fxfa/parser/cxfa_datetime.cpp",
-      "xfa/fxfa/parser/cxfa_datetime.h",
-      "xfa/fxfa/parser/cxfa_datetimeedit.cpp",
-      "xfa/fxfa/parser/cxfa_datetimeedit.h",
-      "xfa/fxfa/parser/cxfa_datetimesymbols.cpp",
-      "xfa/fxfa/parser/cxfa_datetimesymbols.h",
-      "xfa/fxfa/parser/cxfa_day.cpp",
-      "xfa/fxfa/parser/cxfa_day.h",
-      "xfa/fxfa/parser/cxfa_daynames.cpp",
-      "xfa/fxfa/parser/cxfa_daynames.h",
-      "xfa/fxfa/parser/cxfa_debug.cpp",
-      "xfa/fxfa/parser/cxfa_debug.h",
-      "xfa/fxfa/parser/cxfa_decimal.cpp",
-      "xfa/fxfa/parser/cxfa_decimal.h",
-      "xfa/fxfa/parser/cxfa_defaulttypeface.cpp",
-      "xfa/fxfa/parser/cxfa_defaulttypeface.h",
-      "xfa/fxfa/parser/cxfa_defaultui.cpp",
-      "xfa/fxfa/parser/cxfa_defaultui.h",
-      "xfa/fxfa/parser/cxfa_delete.cpp",
-      "xfa/fxfa/parser/cxfa_delete.h",
-      "xfa/fxfa/parser/cxfa_delta.cpp",
-      "xfa/fxfa/parser/cxfa_delta.h",
-      "xfa/fxfa/parser/cxfa_deltas.cpp",
-      "xfa/fxfa/parser/cxfa_deltas.h",
-      "xfa/fxfa/parser/cxfa_desc.cpp",
-      "xfa/fxfa/parser/cxfa_desc.h",
-      "xfa/fxfa/parser/cxfa_destination.cpp",
-      "xfa/fxfa/parser/cxfa_destination.h",
-      "xfa/fxfa/parser/cxfa_digestmethod.cpp",
-      "xfa/fxfa/parser/cxfa_digestmethod.h",
-      "xfa/fxfa/parser/cxfa_digestmethods.cpp",
-      "xfa/fxfa/parser/cxfa_digestmethods.h",
-      "xfa/fxfa/parser/cxfa_document.cpp",
-      "xfa/fxfa/parser/cxfa_document.h",
-      "xfa/fxfa/parser/cxfa_document_parser.cpp",
-      "xfa/fxfa/parser/cxfa_document_parser.h",
-      "xfa/fxfa/parser/cxfa_documentassembly.cpp",
-      "xfa/fxfa/parser/cxfa_documentassembly.h",
-      "xfa/fxfa/parser/cxfa_draw.cpp",
-      "xfa/fxfa/parser/cxfa_draw.h",
-      "xfa/fxfa/parser/cxfa_driver.cpp",
-      "xfa/fxfa/parser/cxfa_driver.h",
-      "xfa/fxfa/parser/cxfa_dsigdata.cpp",
-      "xfa/fxfa/parser/cxfa_dsigdata.h",
-      "xfa/fxfa/parser/cxfa_duplexoption.cpp",
-      "xfa/fxfa/parser/cxfa_duplexoption.h",
-      "xfa/fxfa/parser/cxfa_dynamicrender.cpp",
-      "xfa/fxfa/parser/cxfa_dynamicrender.h",
-      "xfa/fxfa/parser/cxfa_edge.cpp",
-      "xfa/fxfa/parser/cxfa_edge.h",
-      "xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp",
-      "xfa/fxfa/parser/cxfa_effectiveinputpolicy.h",
-      "xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp",
-      "xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h",
-      "xfa/fxfa/parser/cxfa_embed.cpp",
-      "xfa/fxfa/parser/cxfa_embed.h",
-      "xfa/fxfa/parser/cxfa_encoding.cpp",
-      "xfa/fxfa/parser/cxfa_encoding.h",
-      "xfa/fxfa/parser/cxfa_encodings.cpp",
-      "xfa/fxfa/parser/cxfa_encodings.h",
-      "xfa/fxfa/parser/cxfa_encrypt.cpp",
-      "xfa/fxfa/parser/cxfa_encrypt.h",
-      "xfa/fxfa/parser/cxfa_encryption.cpp",
-      "xfa/fxfa/parser/cxfa_encryption.h",
-      "xfa/fxfa/parser/cxfa_encryptionlevel.cpp",
-      "xfa/fxfa/parser/cxfa_encryptionlevel.h",
-      "xfa/fxfa/parser/cxfa_encryptionmethod.cpp",
-      "xfa/fxfa/parser/cxfa_encryptionmethod.h",
-      "xfa/fxfa/parser/cxfa_encryptionmethods.cpp",
-      "xfa/fxfa/parser/cxfa_encryptionmethods.h",
-      "xfa/fxfa/parser/cxfa_enforce.cpp",
-      "xfa/fxfa/parser/cxfa_enforce.h",
-      "xfa/fxfa/parser/cxfa_equate.cpp",
-      "xfa/fxfa/parser/cxfa_equate.h",
-      "xfa/fxfa/parser/cxfa_equaterange.cpp",
-      "xfa/fxfa/parser/cxfa_equaterange.h",
-      "xfa/fxfa/parser/cxfa_era.cpp",
-      "xfa/fxfa/parser/cxfa_era.h",
-      "xfa/fxfa/parser/cxfa_eranames.cpp",
-      "xfa/fxfa/parser/cxfa_eranames.h",
-      "xfa/fxfa/parser/cxfa_event.cpp",
-      "xfa/fxfa/parser/cxfa_event.h",
-      "xfa/fxfa/parser/cxfa_exclgroup.cpp",
-      "xfa/fxfa/parser/cxfa_exclgroup.h",
-      "xfa/fxfa/parser/cxfa_exclude.cpp",
-      "xfa/fxfa/parser/cxfa_exclude.h",
-      "xfa/fxfa/parser/cxfa_excludens.cpp",
-      "xfa/fxfa/parser/cxfa_excludens.h",
-      "xfa/fxfa/parser/cxfa_exdata.cpp",
-      "xfa/fxfa/parser/cxfa_exdata.h",
-      "xfa/fxfa/parser/cxfa_execute.cpp",
-      "xfa/fxfa/parser/cxfa_execute.h",
-      "xfa/fxfa/parser/cxfa_exobject.cpp",
-      "xfa/fxfa/parser/cxfa_exobject.h",
-      "xfa/fxfa/parser/cxfa_extras.cpp",
-      "xfa/fxfa/parser/cxfa_extras.h",
-      "xfa/fxfa/parser/cxfa_field.cpp",
-      "xfa/fxfa/parser/cxfa_field.h",
-      "xfa/fxfa/parser/cxfa_fill.cpp",
-      "xfa/fxfa/parser/cxfa_fill.h",
-      "xfa/fxfa/parser/cxfa_filter.cpp",
-      "xfa/fxfa/parser/cxfa_filter.h",
-      "xfa/fxfa/parser/cxfa_fliplabel.cpp",
-      "xfa/fxfa/parser/cxfa_fliplabel.h",
-      "xfa/fxfa/parser/cxfa_float.cpp",
-      "xfa/fxfa/parser/cxfa_float.h",
-      "xfa/fxfa/parser/cxfa_font.cpp",
-      "xfa/fxfa/parser/cxfa_font.h",
-      "xfa/fxfa/parser/cxfa_fontinfo.cpp",
-      "xfa/fxfa/parser/cxfa_fontinfo.h",
-      "xfa/fxfa/parser/cxfa_form.cpp",
-      "xfa/fxfa/parser/cxfa_form.h",
-      "xfa/fxfa/parser/cxfa_format.cpp",
-      "xfa/fxfa/parser/cxfa_format.h",
-      "xfa/fxfa/parser/cxfa_formfieldfilling.cpp",
-      "xfa/fxfa/parser/cxfa_formfieldfilling.h",
-      "xfa/fxfa/parser/cxfa_groupparent.cpp",
-      "xfa/fxfa/parser/cxfa_groupparent.h",
-      "xfa/fxfa/parser/cxfa_handler.cpp",
-      "xfa/fxfa/parser/cxfa_handler.h",
-      "xfa/fxfa/parser/cxfa_hyphenation.cpp",
-      "xfa/fxfa/parser/cxfa_hyphenation.h",
-      "xfa/fxfa/parser/cxfa_ifempty.cpp",
-      "xfa/fxfa/parser/cxfa_ifempty.h",
-      "xfa/fxfa/parser/cxfa_image.cpp",
-      "xfa/fxfa/parser/cxfa_image.h",
-      "xfa/fxfa/parser/cxfa_imageedit.cpp",
-      "xfa/fxfa/parser/cxfa_imageedit.h",
-      "xfa/fxfa/parser/cxfa_includexdpcontent.cpp",
-      "xfa/fxfa/parser/cxfa_includexdpcontent.h",
-      "xfa/fxfa/parser/cxfa_incrementalload.cpp",
-      "xfa/fxfa/parser/cxfa_incrementalload.h",
-      "xfa/fxfa/parser/cxfa_incrementalmerge.cpp",
-      "xfa/fxfa/parser/cxfa_incrementalmerge.h",
-      "xfa/fxfa/parser/cxfa_insert.cpp",
-      "xfa/fxfa/parser/cxfa_insert.h",
-      "xfa/fxfa/parser/cxfa_instancemanager.cpp",
-      "xfa/fxfa/parser/cxfa_instancemanager.h",
-      "xfa/fxfa/parser/cxfa_integer.cpp",
-      "xfa/fxfa/parser/cxfa_integer.h",
-      "xfa/fxfa/parser/cxfa_interactive.cpp",
-      "xfa/fxfa/parser/cxfa_interactive.h",
-      "xfa/fxfa/parser/cxfa_issuers.cpp",
-      "xfa/fxfa/parser/cxfa_issuers.h",
-      "xfa/fxfa/parser/cxfa_itemlayoutprocessor.cpp",
-      "xfa/fxfa/parser/cxfa_itemlayoutprocessor.h",
-      "xfa/fxfa/parser/cxfa_items.cpp",
-      "xfa/fxfa/parser/cxfa_items.h",
-      "xfa/fxfa/parser/cxfa_jog.cpp",
-      "xfa/fxfa/parser/cxfa_jog.h",
-      "xfa/fxfa/parser/cxfa_keep.cpp",
-      "xfa/fxfa/parser/cxfa_keep.h",
-      "xfa/fxfa/parser/cxfa_keyusage.cpp",
-      "xfa/fxfa/parser/cxfa_keyusage.h",
-      "xfa/fxfa/parser/cxfa_labelprinter.cpp",
-      "xfa/fxfa/parser/cxfa_labelprinter.h",
-      "xfa/fxfa/parser/cxfa_layout.cpp",
-      "xfa/fxfa/parser/cxfa_layout.h",
-      "xfa/fxfa/parser/cxfa_layoutcontext.h",
-      "xfa/fxfa/parser/cxfa_layoutitem.cpp",
-      "xfa/fxfa/parser/cxfa_layoutitem.h",
-      "xfa/fxfa/parser/cxfa_layoutpagemgr.cpp",
-      "xfa/fxfa/parser/cxfa_layoutpagemgr.h",
-      "xfa/fxfa/parser/cxfa_layoutprocessor.cpp",
-      "xfa/fxfa/parser/cxfa_layoutprocessor.h",
-      "xfa/fxfa/parser/cxfa_level.cpp",
-      "xfa/fxfa/parser/cxfa_level.h",
-      "xfa/fxfa/parser/cxfa_line.cpp",
-      "xfa/fxfa/parser/cxfa_line.h",
-      "xfa/fxfa/parser/cxfa_linear.cpp",
-      "xfa/fxfa/parser/cxfa_linear.h",
-      "xfa/fxfa/parser/cxfa_linearized.cpp",
-      "xfa/fxfa/parser/cxfa_linearized.h",
-      "xfa/fxfa/parser/cxfa_list.cpp",
-      "xfa/fxfa/parser/cxfa_list.h",
-      "xfa/fxfa/parser/cxfa_locale.cpp",
-      "xfa/fxfa/parser/cxfa_locale.h",
-      "xfa/fxfa/parser/cxfa_localemgr.cpp",
-      "xfa/fxfa/parser/cxfa_localemgr.h",
-      "xfa/fxfa/parser/cxfa_localeset.cpp",
-      "xfa/fxfa/parser/cxfa_localeset.h",
-      "xfa/fxfa/parser/cxfa_localevalue.cpp",
-      "xfa/fxfa/parser/cxfa_localevalue.h",
-      "xfa/fxfa/parser/cxfa_lockdocument.cpp",
-      "xfa/fxfa/parser/cxfa_lockdocument.h",
-      "xfa/fxfa/parser/cxfa_log.cpp",
-      "xfa/fxfa/parser/cxfa_log.h",
-      "xfa/fxfa/parser/cxfa_manifest.cpp",
-      "xfa/fxfa/parser/cxfa_manifest.h",
-      "xfa/fxfa/parser/cxfa_map.cpp",
-      "xfa/fxfa/parser/cxfa_map.h",
-      "xfa/fxfa/parser/cxfa_margin.cpp",
-      "xfa/fxfa/parser/cxfa_margin.h",
-      "xfa/fxfa/parser/cxfa_mdp.cpp",
-      "xfa/fxfa/parser/cxfa_mdp.h",
-      "xfa/fxfa/parser/cxfa_measurement.cpp",
-      "xfa/fxfa/parser/cxfa_measurement.h",
-      "xfa/fxfa/parser/cxfa_medium.cpp",
-      "xfa/fxfa/parser/cxfa_medium.h",
-      "xfa/fxfa/parser/cxfa_mediuminfo.cpp",
-      "xfa/fxfa/parser/cxfa_mediuminfo.h",
-      "xfa/fxfa/parser/cxfa_meridiem.cpp",
-      "xfa/fxfa/parser/cxfa_meridiem.h",
-      "xfa/fxfa/parser/cxfa_meridiemnames.cpp",
-      "xfa/fxfa/parser/cxfa_meridiemnames.h",
-      "xfa/fxfa/parser/cxfa_message.cpp",
-      "xfa/fxfa/parser/cxfa_message.h",
-      "xfa/fxfa/parser/cxfa_messaging.cpp",
-      "xfa/fxfa/parser/cxfa_messaging.h",
-      "xfa/fxfa/parser/cxfa_mode.cpp",
-      "xfa/fxfa/parser/cxfa_mode.h",
-      "xfa/fxfa/parser/cxfa_modifyannots.cpp",
-      "xfa/fxfa/parser/cxfa_modifyannots.h",
-      "xfa/fxfa/parser/cxfa_month.cpp",
-      "xfa/fxfa/parser/cxfa_month.h",
-      "xfa/fxfa/parser/cxfa_monthnames.cpp",
-      "xfa/fxfa/parser/cxfa_monthnames.h",
-      "xfa/fxfa/parser/cxfa_msgid.cpp",
-      "xfa/fxfa/parser/cxfa_msgid.h",
-      "xfa/fxfa/parser/cxfa_nameattr.cpp",
-      "xfa/fxfa/parser/cxfa_nameattr.h",
-      "xfa/fxfa/parser/cxfa_neverembed.cpp",
-      "xfa/fxfa/parser/cxfa_neverembed.h",
-      "xfa/fxfa/parser/cxfa_node.cpp",
-      "xfa/fxfa/parser/cxfa_node.h",
-      "xfa/fxfa/parser/cxfa_node_statics.cpp",
-      "xfa/fxfa/parser/cxfa_nodehelper.cpp",
-      "xfa/fxfa/parser/cxfa_nodehelper.h",
-      "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h",
-      "xfa/fxfa/parser/cxfa_nodelocale.cpp",
-      "xfa/fxfa/parser/cxfa_nodelocale.h",
-      "xfa/fxfa/parser/cxfa_numberofcopies.cpp",
-      "xfa/fxfa/parser/cxfa_numberofcopies.h",
-      "xfa/fxfa/parser/cxfa_numberpattern.cpp",
-      "xfa/fxfa/parser/cxfa_numberpattern.h",
-      "xfa/fxfa/parser/cxfa_numberpatterns.cpp",
-      "xfa/fxfa/parser/cxfa_numberpatterns.h",
-      "xfa/fxfa/parser/cxfa_numbersymbol.cpp",
-      "xfa/fxfa/parser/cxfa_numbersymbol.h",
-      "xfa/fxfa/parser/cxfa_numbersymbols.cpp",
-      "xfa/fxfa/parser/cxfa_numbersymbols.h",
-      "xfa/fxfa/parser/cxfa_numericedit.cpp",
-      "xfa/fxfa/parser/cxfa_numericedit.h",
-      "xfa/fxfa/parser/cxfa_object.cpp",
-      "xfa/fxfa/parser/cxfa_object.h",
-      "xfa/fxfa/parser/cxfa_occur.cpp",
-      "xfa/fxfa/parser/cxfa_occur.h",
-      "xfa/fxfa/parser/cxfa_oid.cpp",
-      "xfa/fxfa/parser/cxfa_oid.h",
-      "xfa/fxfa/parser/cxfa_oids.cpp",
-      "xfa/fxfa/parser/cxfa_oids.h",
-      "xfa/fxfa/parser/cxfa_openaction.cpp",
-      "xfa/fxfa/parser/cxfa_openaction.h",
-      "xfa/fxfa/parser/cxfa_operation.cpp",
-      "xfa/fxfa/parser/cxfa_operation.h",
-      "xfa/fxfa/parser/cxfa_output.cpp",
-      "xfa/fxfa/parser/cxfa_output.h",
-      "xfa/fxfa/parser/cxfa_outputbin.cpp",
-      "xfa/fxfa/parser/cxfa_outputbin.h",
-      "xfa/fxfa/parser/cxfa_outputxsl.cpp",
-      "xfa/fxfa/parser/cxfa_outputxsl.h",
-      "xfa/fxfa/parser/cxfa_overflow.cpp",
-      "xfa/fxfa/parser/cxfa_overflow.h",
-      "xfa/fxfa/parser/cxfa_overprint.cpp",
-      "xfa/fxfa/parser/cxfa_overprint.h",
-      "xfa/fxfa/parser/cxfa_packet.cpp",
-      "xfa/fxfa/parser/cxfa_packet.h",
-      "xfa/fxfa/parser/cxfa_packets.cpp",
-      "xfa/fxfa/parser/cxfa_packets.h",
-      "xfa/fxfa/parser/cxfa_pagearea.cpp",
-      "xfa/fxfa/parser/cxfa_pagearea.h",
-      "xfa/fxfa/parser/cxfa_pageoffset.cpp",
-      "xfa/fxfa/parser/cxfa_pageoffset.h",
-      "xfa/fxfa/parser/cxfa_pagerange.cpp",
-      "xfa/fxfa/parser/cxfa_pagerange.h",
-      "xfa/fxfa/parser/cxfa_pageset.cpp",
-      "xfa/fxfa/parser/cxfa_pageset.h",
-      "xfa/fxfa/parser/cxfa_pagination.cpp",
-      "xfa/fxfa/parser/cxfa_pagination.h",
-      "xfa/fxfa/parser/cxfa_paginationoverride.cpp",
-      "xfa/fxfa/parser/cxfa_paginationoverride.h",
-      "xfa/fxfa/parser/cxfa_para.cpp",
-      "xfa/fxfa/parser/cxfa_para.h",
-      "xfa/fxfa/parser/cxfa_part.cpp",
-      "xfa/fxfa/parser/cxfa_part.h",
-      "xfa/fxfa/parser/cxfa_password.cpp",
-      "xfa/fxfa/parser/cxfa_password.h",
-      "xfa/fxfa/parser/cxfa_passwordedit.cpp",
-      "xfa/fxfa/parser/cxfa_passwordedit.h",
-      "xfa/fxfa/parser/cxfa_pattern.cpp",
-      "xfa/fxfa/parser/cxfa_pattern.h",
-      "xfa/fxfa/parser/cxfa_pcl.cpp",
-      "xfa/fxfa/parser/cxfa_pcl.h",
-      "xfa/fxfa/parser/cxfa_pdf.cpp",
-      "xfa/fxfa/parser/cxfa_pdf.h",
-      "xfa/fxfa/parser/cxfa_pdfa.cpp",
-      "xfa/fxfa/parser/cxfa_pdfa.h",
-      "xfa/fxfa/parser/cxfa_permissions.cpp",
-      "xfa/fxfa/parser/cxfa_permissions.h",
-      "xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp",
-      "xfa/fxfa/parser/cxfa_picktraybypdfsize.h",
-      "xfa/fxfa/parser/cxfa_picture.cpp",
-      "xfa/fxfa/parser/cxfa_picture.h",
-      "xfa/fxfa/parser/cxfa_plaintextmetadata.cpp",
-      "xfa/fxfa/parser/cxfa_plaintextmetadata.h",
-      "xfa/fxfa/parser/cxfa_presence.cpp",
-      "xfa/fxfa/parser/cxfa_presence.h",
-      "xfa/fxfa/parser/cxfa_present.cpp",
-      "xfa/fxfa/parser/cxfa_present.h",
-      "xfa/fxfa/parser/cxfa_print.cpp",
-      "xfa/fxfa/parser/cxfa_print.h",
-      "xfa/fxfa/parser/cxfa_printername.cpp",
-      "xfa/fxfa/parser/cxfa_printername.h",
-      "xfa/fxfa/parser/cxfa_printhighquality.cpp",
-      "xfa/fxfa/parser/cxfa_printhighquality.h",
-      "xfa/fxfa/parser/cxfa_printscaling.cpp",
-      "xfa/fxfa/parser/cxfa_printscaling.h",
-      "xfa/fxfa/parser/cxfa_producer.cpp",
-      "xfa/fxfa/parser/cxfa_producer.h",
-      "xfa/fxfa/parser/cxfa_proto.cpp",
-      "xfa/fxfa/parser/cxfa_proto.h",
-      "xfa/fxfa/parser/cxfa_ps.cpp",
-      "xfa/fxfa/parser/cxfa_ps.h",
-      "xfa/fxfa/parser/cxfa_psmap.cpp",
-      "xfa/fxfa/parser/cxfa_psmap.h",
-      "xfa/fxfa/parser/cxfa_query.cpp",
-      "xfa/fxfa/parser/cxfa_query.h",
-      "xfa/fxfa/parser/cxfa_radial.cpp",
-      "xfa/fxfa/parser/cxfa_radial.h",
-      "xfa/fxfa/parser/cxfa_range.cpp",
-      "xfa/fxfa/parser/cxfa_range.h",
-      "xfa/fxfa/parser/cxfa_reason.cpp",
-      "xfa/fxfa/parser/cxfa_reason.h",
-      "xfa/fxfa/parser/cxfa_reasons.cpp",
-      "xfa/fxfa/parser/cxfa_reasons.h",
-      "xfa/fxfa/parser/cxfa_record.cpp",
-      "xfa/fxfa/parser/cxfa_record.h",
-      "xfa/fxfa/parser/cxfa_recordset.cpp",
-      "xfa/fxfa/parser/cxfa_recordset.h",
-      "xfa/fxfa/parser/cxfa_rectangle.cpp",
-      "xfa/fxfa/parser/cxfa_rectangle.h",
-      "xfa/fxfa/parser/cxfa_ref.cpp",
-      "xfa/fxfa/parser/cxfa_ref.h",
-      "xfa/fxfa/parser/cxfa_relevant.cpp",
-      "xfa/fxfa/parser/cxfa_relevant.h",
-      "xfa/fxfa/parser/cxfa_rename.cpp",
-      "xfa/fxfa/parser/cxfa_rename.h",
-      "xfa/fxfa/parser/cxfa_renderpolicy.cpp",
-      "xfa/fxfa/parser/cxfa_renderpolicy.h",
-      "xfa/fxfa/parser/cxfa_rootelement.cpp",
-      "xfa/fxfa/parser/cxfa_rootelement.h",
-      "xfa/fxfa/parser/cxfa_runscripts.cpp",
-      "xfa/fxfa/parser/cxfa_runscripts.h",
-      "xfa/fxfa/parser/cxfa_script.cpp",
-      "xfa/fxfa/parser/cxfa_script.h",
-      "xfa/fxfa/parser/cxfa_scriptmodel.cpp",
-      "xfa/fxfa/parser/cxfa_scriptmodel.h",
-      "xfa/fxfa/parser/cxfa_select.cpp",
-      "xfa/fxfa/parser/cxfa_select.h",
-      "xfa/fxfa/parser/cxfa_setproperty.cpp",
-      "xfa/fxfa/parser/cxfa_setproperty.h",
-      "xfa/fxfa/parser/cxfa_severity.cpp",
-      "xfa/fxfa/parser/cxfa_severity.h",
-      "xfa/fxfa/parser/cxfa_sharptext.cpp",
-      "xfa/fxfa/parser/cxfa_sharptext.h",
-      "xfa/fxfa/parser/cxfa_sharpxhtml.cpp",
-      "xfa/fxfa/parser/cxfa_sharpxhtml.h",
-      "xfa/fxfa/parser/cxfa_sharpxml.cpp",
-      "xfa/fxfa/parser/cxfa_sharpxml.h",
-      "xfa/fxfa/parser/cxfa_signature.cpp",
-      "xfa/fxfa/parser/cxfa_signature.h",
-      "xfa/fxfa/parser/cxfa_signatureproperties.cpp",
-      "xfa/fxfa/parser/cxfa_signatureproperties.h",
-      "xfa/fxfa/parser/cxfa_signdata.cpp",
-      "xfa/fxfa/parser/cxfa_signdata.h",
-      "xfa/fxfa/parser/cxfa_signing.cpp",
-      "xfa/fxfa/parser/cxfa_signing.h",
-      "xfa/fxfa/parser/cxfa_silentprint.cpp",
-      "xfa/fxfa/parser/cxfa_silentprint.h",
-      "xfa/fxfa/parser/cxfa_simple_parser.cpp",
-      "xfa/fxfa/parser/cxfa_simple_parser.h",
-      "xfa/fxfa/parser/cxfa_soapaction.cpp",
-      "xfa/fxfa/parser/cxfa_soapaction.h",
-      "xfa/fxfa/parser/cxfa_soapaddress.cpp",
-      "xfa/fxfa/parser/cxfa_soapaddress.h",
-      "xfa/fxfa/parser/cxfa_solid.cpp",
-      "xfa/fxfa/parser/cxfa_solid.h",
-      "xfa/fxfa/parser/cxfa_source.cpp",
-      "xfa/fxfa/parser/cxfa_source.h",
-      "xfa/fxfa/parser/cxfa_sourceset.cpp",
-      "xfa/fxfa/parser/cxfa_sourceset.h",
-      "xfa/fxfa/parser/cxfa_speak.cpp",
-      "xfa/fxfa/parser/cxfa_speak.h",
-      "xfa/fxfa/parser/cxfa_staple.cpp",
-      "xfa/fxfa/parser/cxfa_staple.h",
-      "xfa/fxfa/parser/cxfa_startnode.cpp",
-      "xfa/fxfa/parser/cxfa_startnode.h",
-      "xfa/fxfa/parser/cxfa_startpage.cpp",
-      "xfa/fxfa/parser/cxfa_startpage.h",
-      "xfa/fxfa/parser/cxfa_stipple.cpp",
-      "xfa/fxfa/parser/cxfa_stipple.h",
-      "xfa/fxfa/parser/cxfa_stroke.cpp",
-      "xfa/fxfa/parser/cxfa_stroke.h",
-      "xfa/fxfa/parser/cxfa_subform.cpp",
-      "xfa/fxfa/parser/cxfa_subform.h",
-      "xfa/fxfa/parser/cxfa_subformset.cpp",
-      "xfa/fxfa/parser/cxfa_subformset.h",
-      "xfa/fxfa/parser/cxfa_subjectdn.cpp",
-      "xfa/fxfa/parser/cxfa_subjectdn.h",
-      "xfa/fxfa/parser/cxfa_subjectdns.cpp",
-      "xfa/fxfa/parser/cxfa_subjectdns.h",
-      "xfa/fxfa/parser/cxfa_submit.cpp",
-      "xfa/fxfa/parser/cxfa_submit.h",
-      "xfa/fxfa/parser/cxfa_submitformat.cpp",
-      "xfa/fxfa/parser/cxfa_submitformat.h",
-      "xfa/fxfa/parser/cxfa_submiturl.cpp",
-      "xfa/fxfa/parser/cxfa_submiturl.h",
-      "xfa/fxfa/parser/cxfa_subsetbelow.cpp",
-      "xfa/fxfa/parser/cxfa_subsetbelow.h",
-      "xfa/fxfa/parser/cxfa_suppressbanner.cpp",
-      "xfa/fxfa/parser/cxfa_suppressbanner.h",
-      "xfa/fxfa/parser/cxfa_tagged.cpp",
-      "xfa/fxfa/parser/cxfa_tagged.h",
-      "xfa/fxfa/parser/cxfa_template.cpp",
-      "xfa/fxfa/parser/cxfa_template.h",
-      "xfa/fxfa/parser/cxfa_templatecache.cpp",
-      "xfa/fxfa/parser/cxfa_templatecache.h",
-      "xfa/fxfa/parser/cxfa_text.cpp",
-      "xfa/fxfa/parser/cxfa_text.h",
-      "xfa/fxfa/parser/cxfa_textedit.cpp",
-      "xfa/fxfa/parser/cxfa_textedit.h",
-      "xfa/fxfa/parser/cxfa_thisproxy.cpp",
-      "xfa/fxfa/parser/cxfa_thisproxy.h",
-      "xfa/fxfa/parser/cxfa_threshold.cpp",
-      "xfa/fxfa/parser/cxfa_threshold.h",
-      "xfa/fxfa/parser/cxfa_time.cpp",
-      "xfa/fxfa/parser/cxfa_time.h",
-      "xfa/fxfa/parser/cxfa_timepattern.cpp",
-      "xfa/fxfa/parser/cxfa_timepattern.h",
-      "xfa/fxfa/parser/cxfa_timepatterns.cpp",
-      "xfa/fxfa/parser/cxfa_timepatterns.h",
-      "xfa/fxfa/parser/cxfa_timestamp.cpp",
-      "xfa/fxfa/parser/cxfa_timestamp.h",
-      "xfa/fxfa/parser/cxfa_timezoneprovider.cpp",
-      "xfa/fxfa/parser/cxfa_timezoneprovider.h",
-      "xfa/fxfa/parser/cxfa_to.cpp",
-      "xfa/fxfa/parser/cxfa_to.h",
-      "xfa/fxfa/parser/cxfa_tooltip.cpp",
-      "xfa/fxfa/parser/cxfa_tooltip.h",
-      "xfa/fxfa/parser/cxfa_trace.cpp",
-      "xfa/fxfa/parser/cxfa_trace.h",
-      "xfa/fxfa/parser/cxfa_transform.cpp",
-      "xfa/fxfa/parser/cxfa_transform.h",
-      "xfa/fxfa/parser/cxfa_traversal.cpp",
-      "xfa/fxfa/parser/cxfa_traversal.h",
-      "xfa/fxfa/parser/cxfa_traverse.cpp",
-      "xfa/fxfa/parser/cxfa_traverse.h",
-      "xfa/fxfa/parser/cxfa_traversestrategy_contentareacontainerlayoutitem.h",
-      "xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h",
-      "xfa/fxfa/parser/cxfa_traversestrategy_layoutitem.h",
-      "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h",
-      "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h",
-      "xfa/fxfa/parser/cxfa_treelist.cpp",
-      "xfa/fxfa/parser/cxfa_treelist.h",
-      "xfa/fxfa/parser/cxfa_type.cpp",
-      "xfa/fxfa/parser/cxfa_type.h",
-      "xfa/fxfa/parser/cxfa_typeface.cpp",
-      "xfa/fxfa/parser/cxfa_typeface.h",
-      "xfa/fxfa/parser/cxfa_typefaces.cpp",
-      "xfa/fxfa/parser/cxfa_typefaces.h",
-      "xfa/fxfa/parser/cxfa_ui.cpp",
-      "xfa/fxfa/parser/cxfa_ui.h",
-      "xfa/fxfa/parser/cxfa_update.cpp",
-      "xfa/fxfa/parser/cxfa_update.h",
-      "xfa/fxfa/parser/cxfa_uri.cpp",
-      "xfa/fxfa/parser/cxfa_uri.h",
-      "xfa/fxfa/parser/cxfa_user.cpp",
-      "xfa/fxfa/parser/cxfa_user.h",
-      "xfa/fxfa/parser/cxfa_validate.cpp",
-      "xfa/fxfa/parser/cxfa_validate.h",
-      "xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp",
-      "xfa/fxfa/parser/cxfa_validateapprovalsignatures.h",
-      "xfa/fxfa/parser/cxfa_validationmessaging.cpp",
-      "xfa/fxfa/parser/cxfa_validationmessaging.h",
-      "xfa/fxfa/parser/cxfa_value.cpp",
-      "xfa/fxfa/parser/cxfa_value.h",
-      "xfa/fxfa/parser/cxfa_variables.cpp",
-      "xfa/fxfa/parser/cxfa_variables.h",
-      "xfa/fxfa/parser/cxfa_version.cpp",
-      "xfa/fxfa/parser/cxfa_version.h",
-      "xfa/fxfa/parser/cxfa_versioncontrol.cpp",
-      "xfa/fxfa/parser/cxfa_versioncontrol.h",
-      "xfa/fxfa/parser/cxfa_viewerpreferences.cpp",
-      "xfa/fxfa/parser/cxfa_viewerpreferences.h",
-      "xfa/fxfa/parser/cxfa_webclient.cpp",
-      "xfa/fxfa/parser/cxfa_webclient.h",
-      "xfa/fxfa/parser/cxfa_whitespace.cpp",
-      "xfa/fxfa/parser/cxfa_whitespace.h",
-      "xfa/fxfa/parser/cxfa_window.cpp",
-      "xfa/fxfa/parser/cxfa_window.h",
-      "xfa/fxfa/parser/cxfa_wsdladdress.cpp",
-      "xfa/fxfa/parser/cxfa_wsdladdress.h",
-      "xfa/fxfa/parser/cxfa_wsdlconnection.cpp",
-      "xfa/fxfa/parser/cxfa_wsdlconnection.h",
-      "xfa/fxfa/parser/cxfa_xdc.cpp",
-      "xfa/fxfa/parser/cxfa_xdc.h",
-      "xfa/fxfa/parser/cxfa_xdp.cpp",
-      "xfa/fxfa/parser/cxfa_xdp.h",
-      "xfa/fxfa/parser/cxfa_xfa.cpp",
-      "xfa/fxfa/parser/cxfa_xfa.h",
-      "xfa/fxfa/parser/cxfa_xmlconnection.cpp",
-      "xfa/fxfa/parser/cxfa_xmlconnection.h",
-      "xfa/fxfa/parser/cxfa_xmllocale.cpp",
-      "xfa/fxfa/parser/cxfa_xmllocale.h",
-      "xfa/fxfa/parser/cxfa_xsdconnection.cpp",
-      "xfa/fxfa/parser/cxfa_xsdconnection.h",
-      "xfa/fxfa/parser/cxfa_xsl.cpp",
-      "xfa/fxfa/parser/cxfa_xsl.h",
-      "xfa/fxfa/parser/cxfa_zpl.cpp",
-      "xfa/fxfa/parser/cxfa_zpl.h",
-      "xfa/fxfa/parser/xfa_basic_data.h",
-      "xfa/fxfa/parser/xfa_basic_data_element_script.cpp",
-      "xfa/fxfa/parser/xfa_basic_data_enum.cpp",
-      "xfa/fxfa/parser/xfa_document_datamerger_imp.cpp",
-      "xfa/fxfa/parser/xfa_document_datamerger_imp.h",
-      "xfa/fxfa/parser/xfa_resolvenode_rs.h",
-      "xfa/fxfa/parser/xfa_utils.cpp",
-      "xfa/fxfa/parser/xfa_utils.h",
-      "xfa/fxgraphics/cxfa_gecolor.cpp",
-      "xfa/fxgraphics/cxfa_gecolor.h",
-      "xfa/fxgraphics/cxfa_gepath.cpp",
-      "xfa/fxgraphics/cxfa_gepath.h",
-      "xfa/fxgraphics/cxfa_gepattern.cpp",
-      "xfa/fxgraphics/cxfa_gepattern.h",
-      "xfa/fxgraphics/cxfa_geshading.cpp",
-      "xfa/fxgraphics/cxfa_geshading.h",
-      "xfa/fxgraphics/cxfa_graphics.cpp",
-      "xfa/fxgraphics/cxfa_graphics.h",
-    ]
-    include_dirs = [ "." ]
-    deps = [
-      ":fxbarcode",
-      ":fxcrt",
-      ":fxjs",
-    ]
-    configs += [
-      ":pdfium_core_config",
-      ":xfa_warnings",
-    ]
-  }
-}
-
-test("pdfium_unittests") {
-  sources = [
-    "core/fdrm/crypto/fx_crypt_unittest.cpp",
-    "core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp",
-    "core/fpdfapi/font/cpdf_cmapparser_unittest.cpp",
-    "core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp",
-    "core/fpdfapi/page/cpdf_devicecs_unittest.cpp",
-    "core/fpdfapi/page/cpdf_psengine_unittest.cpp",
-    "core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp",
-    "core/fpdfapi/page/cpdf_streamparser_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_array_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_document_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_object_avail_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_object_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_object_walker_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_parser_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_read_validator_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp",
-    "core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp",
-    "core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp",
-    "core/fpdfdoc/cpdf_dest_unittest.cpp",
-    "core/fpdfdoc/cpdf_filespec_unittest.cpp",
-    "core/fpdfdoc/cpdf_formfield_unittest.cpp",
-    "core/fpdfdoc/cpdf_nametree_unittest.cpp",
-    "core/fpdftext/cpdf_linkextract_unittest.cpp",
-    "core/fxcodec/codec/fx_codec_a85_unittest.cpp",
-    "core/fxcodec/codec/fx_codec_jpx_unittest.cpp",
-    "core/fxcodec/codec/fx_codec_rle_unittest.cpp",
-    "core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp",
-    "core/fxcodec/jbig2/JBig2_Image_unittest.cpp",
-    "core/fxcrt/bytestring_unittest.cpp",
-    "core/fxcrt/cfx_seekablemultistream_unittest.cpp",
-    "core/fxcrt/fx_bidi_unittest.cpp",
-    "core/fxcrt/fx_coordinates_unittest.cpp",
-    "core/fxcrt/fx_extension_unittest.cpp",
-    "core/fxcrt/fx_memory_unittest.cpp",
-    "core/fxcrt/fx_random_unittest.cpp",
-    "core/fxcrt/fx_string_unittest.cpp",
-    "core/fxcrt/fx_system_unittest.cpp",
-    "core/fxcrt/maybe_owned_unittest.cpp",
-    "core/fxcrt/observable_unittest.cpp",
-    "core/fxcrt/retain_ptr_unittest.cpp",
-    "core/fxcrt/shared_copy_on_write_unittest.cpp",
-    "core/fxcrt/string_pool_template_unittest.cpp",
-    "core/fxcrt/unowned_ptr_unittest.cpp",
-    "core/fxcrt/weak_ptr_unittest.cpp",
-    "core/fxcrt/widestring_unittest.cpp",
-    "core/fxge/dib/cstretchengine_unittest.cpp",
-    "fpdfsdk/fpdfcatalog_unittest.cpp",
-    "fpdfsdk/fpdfdoc_unittest.cpp",
-    "fpdfsdk/fpdfeditimg_unittest.cpp",
-    "testing/unit_test_main.cpp",
-  ]
-  deps = [
-    ":pdfium",
-    ":test_support",
+    "core/fxcrt",
+    "testing:test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
-  include_dirs = []
-  if (pdf_enable_xfa) {
-    sources += [
-      "core/fxcodec/gif/cfx_gifcontext_unittest.cpp",
-      "core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp",
-      "core/fxcrt/css/cfx_cssdeclaration_unittest.cpp",
-      "core/fxcrt/css/cfx_cssstylesheet_unittest.cpp",
-      "core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp",
-      "core/fxcrt/xml/cfx_saxreader_unittest.cpp",
-      "core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp",
-      "fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp",
-      "fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp",
-      "fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp",
-      "fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp",
-      "fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp",
-      "fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp",
-      "fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp",
-      "xfa/fde/cfde_texteditengine_unittest.cpp",
-      "xfa/fgas/crt/cfgas_formatstring_unittest.cpp",
-      "xfa/fgas/layout/cfx_rtfbreak_unittest.cpp",
-      "xfa/fwl/cfx_barcode_unittest.cpp",
-      "xfa/fxfa/cxfa_ffbarcode_unittest.cpp",
-      "xfa/fxfa/cxfa_textparser_unittest.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp",
-      "xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp",
-      "xfa/fxfa/parser/cxfa_node_unittest.cpp",
-      "xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp",
-      "xfa/fxfa/parser/xfa_utils_unittest.cpp",
-    ]
-  }
-  if (pdf_use_skia || pdf_use_skia_paths) {
-    sources += [ "core/fxge/skia/fx_skia_device_unittest.cpp" ]
-    deps += [ "//skia" ]
-  }
-  if (pdf_enable_v8) {
-    sources += [
-      "fxjs/cjs_publicmethods_unittest.cpp",
-      "fxjs/cjs_util_unittest.cpp",
-    ]
-    include_dirs += [
-      "//v8",
-      "//v8/include",
-    ]
-  }
+  visibility += [
+    "core/*",
+    "fpdfsdk/*",
+    "fxbarcode/*",
+    "fxjs/*",
+    "xfa/*",
+  ]
+}
+
+test("pdfium_unittests") {
+  testonly = true
+  sources = [ "testing/unit_test_main.cpp" ]
+  deps = [
+    "core/fdrm:unittests",
+    "core/fpdfapi/edit:unittests",
+    "core/fpdfapi/font:unittests",
+    "core/fpdfapi/page:unittests",
+    "core/fpdfapi/parser:unittests",
+    "core/fpdfapi/render:unittests",
+    "core/fpdfdoc:unittests",
+    "core/fpdftext:unittests",
+    "core/fxcodec:unittests",
+    "core/fxcrt",
+    "core/fxcrt:unittests",
+    "core/fxge:unittests",
+    "fpdfsdk:unittests",
+    "testing:test_support",
+    "testing:unit_test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
   configs += [ ":pdfium_core_config" ]
+
   if (is_android) {
     use_raw_android_executable = true
   }
+
+  if (pdf_enable_v8) {
+    configs += [ "//v8:external_startup_data" ]
+    deps += [
+      "fxjs:unittests",
+      "//v8",
+    ]
+  }
+
+  if (pdf_enable_xfa) {
+    deps += [
+      "fxbarcode:unittests",
+      "xfa/fde:unittests",
+      "xfa/fgas:unittests",
+      "xfa/fgas/layout:unittests",
+      "xfa/fxfa:unittests",
+      "xfa/fxfa/fm2js:unittests",
+      "xfa/fxfa/parser:unittests",
+    ]
+  }
+}
+
+group("pdfium_embeddertest_deps") {
+  testonly = true
+  public_deps = [
+    ":pdfium_public_headers",
+    "core/fxcrt",
+    "testing:embedder_test_support",
+    "testing:test_support",
+    "third_party:pdfium_base_test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  visibility += [
+    "core/*",
+    "fpdfsdk/*",
+    "fxjs/*",
+    "xfa/*",
+  ]
 }
 
 test("pdfium_embeddertests") {
-  sources = [
-    "core/fpdfapi/edit/cpdf_creator_embeddertest.cpp",
-    "core/fpdfapi/page/cpdf_stitchfunc_embeddertest.cpp",
-    "core/fpdfapi/parser/cpdf_parser_embeddertest.cpp",
-    "core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp",
-    "core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp",
-    "core/fpdfapi/render/fpdf_render_loadimage_embeddertest.cpp",
-    "core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp",
-    "core/fxcodec/codec/fx_codec_embeddertest.cpp",
-    "core/fxge/fx_ge_text_embeddertest.cpp",
-    "fpdfsdk/fpdf_dataavail_embeddertest.cpp",
-    "fpdfsdk/fpdf_flatten_embeddertest.cpp",
-    "fpdfsdk/fpdf_structtree_embeddertest.cpp",
-    "fpdfsdk/fpdfannot_embeddertest.cpp",
-    "fpdfsdk/fpdfattachment_embeddertest.cpp",
-    "fpdfsdk/fpdfdoc_embeddertest.cpp",
-    "fpdfsdk/fpdfedit_embeddertest.cpp",
-    "fpdfsdk/fpdfeditpath_embeddertest.cpp",
-    "fpdfsdk/fpdfext_embeddertest.cpp",
-    "fpdfsdk/fpdfformfill_embeddertest.cpp",
-    "fpdfsdk/fpdfppo_embeddertest.cpp",
-    "fpdfsdk/fpdfsave_embeddertest.cpp",
-    "fpdfsdk/fpdftext_embeddertest.cpp",
-    "fpdfsdk/fpdfview_c_api_test.c",
-    "fpdfsdk/fpdfview_c_api_test.h",
-    "fpdfsdk/fpdfview_embeddertest.cpp",
-    "fpdfsdk/fsdk_baseform_embeddertest.cpp",
-    "fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp",
-    "fpdfsdk/pwl/cpwl_edit_embeddertest.cpp",
-    "testing/embedder_test.cpp",
-    "testing/embedder_test.h",
-    "testing/embedder_test_main.cpp",
-    "testing/embedder_test_mock_delegate.h",
-    "testing/embedder_test_timer_handling_delegate.h",
-    "testing/fake_file_access.cpp",
-    "testing/fake_file_access.h",
-    "testing/range_set.cpp",
-    "testing/range_set.h",
-  ]
+  testonly = true
+  sources = [ "testing/embedder_test_main.cpp" ]
   deps = [
-    ":image_diff",
-    ":pdfium",
-    ":test_support",
+    "core/fpdfapi/edit:embeddertests",
+    "core/fpdfapi/parser:embeddertests",
+    "core/fpdfapi/render:embeddertests",
+    "core/fxcodec:embeddertests",
+    "core/fxcrt",
+    "core/fxge:embeddertests",
+    "fpdfsdk:embeddertests",
+    "fpdfsdk/pwl:embeddertests",
+    "testing:test_support",
+    "testing/image_diff",
     "//testing/gmock",
     "//testing/gtest",
   ]
   include_dirs = [ "testing/gmock/include" ]
   configs += [ ":pdfium_core_config" ]
 
-  if (pdf_enable_v8) {
-    sources += [
-      "fxjs/cjs_publicmethods_embeddertest.cpp",
-      "fxjs/fxjs_v8_embeddertest.cpp",
-      "testing/js_embedder_test.cpp",
-      "testing/js_embedder_test.h",
-    ]
-    deps += [ ":fxjs" ]
-    configs += [ "//v8:external_startup_data" ]
-  }
-  if (pdf_enable_xfa) {
-    sources += [
-      "fxjs/cfxjse_formcalc_context_embeddertest.cpp",
-      "testing/xfa_js_embedder_test.cpp",
-      "testing/xfa_js_embedder_test.h",
-      "xfa/fwl/cfwl_edit_embeddertest.cpp",
-      "xfa/fxfa/parser/cxfa_simple_parser_embeddertest.cpp",
-    ]
-  }
   if (is_android) {
     ignore_all_data_deps = true
     use_raw_android_executable = true
   }
+
+  if (pdf_enable_v8) {
+    deps += [
+      "fxjs:embeddertests",
+      "//v8",
+    ]
+    configs += [ "//v8:external_startup_data" ]
+  }
+
+  if (pdf_enable_xfa) {
+    deps += [
+      "fpdfsdk/fpdfxfa:embeddertests",
+      "xfa/fwl:embeddertests",
+      "xfa/fxfa/layout:embeddertests",
+      "xfa/fxfa/parser:embeddertests",
+    ]
+  }
+}
+
+executable("pdfium_diff") {
+  testonly = true
+  sources = [ "testing/image_diff/image_diff.cpp" ]
+  deps = [
+    ":pdfium",
+    "core/fxcrt",
+    "testing/image_diff",
+    "//build/win:default_exe_manifest",
+  ]
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+  configs += [ ":pdfium_core_config" ]
 }
 
 if (pdf_is_standalone) {
   source_set("samples") {
     testonly = true
-    deps = [
-      "//samples",
-    ]
-  }
-
-  executable("pdfium_diff") {
-    testonly = true
-    sources = [
-      "testing/image_diff/image_diff.cpp",
-    ]
-    deps = [
-      ":image_diff",
-      ":pdfium",
-      "//build/config:exe_and_shlib_deps",
-      "//build/win:default_exe_manifest",
-    ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ ":pdfium_core_config" ]
+    deps = [ "//samples" ]
   }
 
   group("fuzzers") {
     testonly = true
-    deps = [
-      "//testing/libfuzzer",
-    ]
+    deps = [ "//testing/fuzzers" ]
   }
 }
 
 group("pdfium_all") {
   testonly = true
   deps = [
+    ":pdfium_diff",
     ":pdfium_embeddertests",
     ":pdfium_unittests",
   ]
   if (pdf_is_standalone) {
     deps += [
       ":fuzzers",
-      ":pdfium_diff",
       ":samples",
     ]
   }
diff --git a/DEPS b/DEPS
index 7abc60f..a939282 100644
--- a/DEPS
+++ b/DEPS
@@ -7,35 +7,33 @@
   # purposes.
   'checkout_configuration': 'default',
 
-  # TODO(dpranke): change to != "small" once != is supported.
-  'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"',
+  'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small"',
 
   'chromium_git': 'https://chromium.googlesource.com',
   'pdfium_git': 'https://pdfium.googlesource.com',
 
-  'android_ndk_revision': 'd57523210239b867fa4fb9d05c2aacc3f1802fe0',
-  'binutils_revision': 'e146228c20af6af922887d0be2d3641cbffb33c5',
-  'build_revision': '097c79babc9b2ce40b61c3e33da1c6681acf837c',
-  'buildtools_revision': 'b36c7b60ed73919b157c7d23ec5fce2aa69ab05e',
-  'catapult_revision': 'd624b3ced2c81d4fb4ea98a8dbb4532272cc1e0a',
-  'clang_revision': 'ac1e5f78c39dc2a262af149404dd36c140eecdc1',
-  'cygwin_revision': 'c89e446b273697fadf3a10ff1007a97c0b7de6df',
-  'depot_tools_revision': '2e8d8348b8574f06c26dbf3ef959b5df11ba5148',
-  'freetype_revision': '2c048a8a622e9f44f255aa3316026f124ac9ecbc',
-  'gmock_revision': '29763965ab52f24565299976b936d1265cb6a271',
-  'gtest_revision': '8245545b6dc9c4703e6496d1efd19e975ad2b038',
-  'icu_revision': 'e3b480d3be4446ea17011c0cdc9c4cd380a5c58f',
-  'instrumented_lib_revision': '28417458ac4dc79f68915079d0f283f682504cc0',
-  'jinja2_revision': 'd34383206fa42d52faa10bb9931d6d538f3a57e0',
-  'jpeg_turbo_revision': '7260e4d8b8e1e40b17f03fafdf1cd83296900f76',
+  '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': '3d6228da656a3974d72223a702e6804df494703e',
-  'skia_revision': '40ca2087ef0752d78fd2e0995471102fe96fe9fe',
-  'tools_memory_revision': '427f10475e1a8d72424c29d00bf689122b738e5d',
-  'trace_event_revision': '0e9a47d74970bee1bbfc063c47215406f8918699',
-  'v8_revision': '0c287882ea233f299a91f6b72b56d8faaecf52c0',
-  'yasm_source_revision': 'b98114e18d8b9b84586b10d24353ab8616d4c5fc',
-  'zlib_revision': '91155b5d0737713fc7f0499cf0ba2c4f2af24014',
+  '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',
 }
 
 deps = {
@@ -47,22 +45,26 @@
     Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'),
 
   "buildtools":
-    Var('chromium_git') + "/chromium/buildtools.git@" +
+    Var('chromium_git') + "/chromium/src/buildtools.git@" +
         Var('buildtools_revision'),
 
   "testing/corpus":
     Var('pdfium_git') + "/pdfium_tests@" + Var('pdfium_tests_revision'),
 
-  "testing/gmock":
-    Var('chromium_git') + "/external/googlemock.git@" + Var('gmock_revision'),
-
-  "testing/gtest":
-    Var('chromium_git') + "/external/googletest.git@" + Var('gtest_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'),
+    'condition': 'checkout_android',
+  },
+
   'third_party/depot_tools':
     Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' +
         Var('depot_tools_revision'),
@@ -71,6 +73,10 @@
     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' + '@' +
+        Var('gtest_revision'),
+
   "third_party/icu":
     Var('chromium_git') + "/chromium/deps/icu.git@" + Var('icu_revision'),
 
@@ -105,10 +111,9 @@
   "tools/clang":
     Var('chromium_git') + "/chromium/src/tools/clang@" +  Var('clang_revision'),
 
-  # TODO(GYP): Remove this when no tools rely on GYP anymore.
-  "tools/gyp":
-    Var('chromium_git') + '/external/gyp.git@' +
-        'eb296f67da078ec01f5e3a9ea9cdc6d26d680161',
+  "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@" +
@@ -118,21 +123,6 @@
     Var('chromium_git') + "/v8/v8.git@" + Var('v8_revision'),
 }
 
-deps_os = {
-  "android": {
-    "third_party/android_ndk":
-      Var('chromium_git') + "/android_ndk.git@" + Var('android_ndk_revision'),
-    "third_party/catapult":
-      Var('chromium_git') +
-          "/external/github.com/catapult-project/catapult.git@" +
-          Var('catapult_revision'),
-  },
-  "win": {
-    "v8/third_party/cygwin":
-      Var('chromium_git') + "/chromium/deps/cygwin@" + Var('cygwin_revision'),
-  },
-}
-
 recursedeps = [
   # buildtools provides clang_format, libc++, and libc++abi
   'buildtools',
@@ -141,6 +131,8 @@
 include_rules = [
   # Basic stuff that everyone can use.
   # Note: public is not here because core cannot depend on public.
+  '+build/build_config.h',
+  '+constants',
   '+testing',
   '+third_party/base',
 ]
@@ -154,39 +146,33 @@
 
 hooks = [
   {
-    'name': 'gn_win',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=win32',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'pdfium/buildtools/win/gn.exe.sha1',
-    ],
-  },
-  {
-    'name': 'gn_mac',
+    # Case-insensitivity for the Win SDK. Must run before win_toolchain below.
+    'name': 'ciopfs_linux',
     'pattern': '.',
-    'action': [ 'download_from_google_storage',
+    'condition': 'checkout_win and host_os == "linux"',
+    'action': [ 'python',
+                'pdfium/third_party/depot_tools/download_from_google_storage.py',
                 '--no_resume',
-                '--platform=darwin',
                 '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'pdfium/buildtools/mac/gn.sha1',
-    ],
+                '--bucket', 'chromium-browser-clang/ciopfs',
+                '-s', 'pdfium/build/ciopfs.sha1',
+    ]
   },
   {
-    'name': 'gn_linux64',
+    # Update the Windows toolchain if necessary.  Must run before 'clang' below.
+    'name': 'win_toolchain',
     'pattern': '.',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=linux*',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'pdfium/buildtools/linux64/gn.sha1',
-    ],
+    'condition': 'checkout_win',
+    'action': ['python', 'pdfium/build/vs_toolchain.py', 'update', '--force'],
   },
-  # Pull clang-format binaries using checked-in hashes.
   {
+    # Update the Mac toolchain if necessary.
+    'name': 'mac_toolchain',
+    'pattern': '.',
+    'action': ['python', 'pdfium/build/mac_toolchain.py'],
+  },
+  {
+    # Pull clang-format binaries using checked-in hashes.
     'name': 'clang_format_win',
     'pattern': '.',
     'action': [ 'download_from_google_storage',
@@ -220,7 +206,7 @@
     ],
   },
   {
-    # Pull clang
+    # Note: On Win, this should run after win_toolchain, as it may use it.
     'name': 'clang',
     'pattern': '.',
     'action': ['python',
@@ -237,26 +223,39 @@
     ],
   },
   {
-    # Downloads the current stable linux sysroot to build/linux/ if needed.
-    # This sysroot updates at about the same rate that the chrome build deps
-    # change.
-    'name': 'sysroot',
+    'name': 'sysroot_arm',
     'pattern': '.',
+    'condition': 'checkout_linux and checkout_arm',
     'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
-               '--running-as-hook'],
+               '--arch=arm'],
   },
   {
-    # Update the Windows toolchain if necessary.
-    'name': 'win_toolchain',
+    'name': 'sysroot_arm64',
     'pattern': '.',
-    'condition': 'checkout_win',
-    'action': ['vpython', 'pdfium/build/vs_toolchain.py', 'update', '--force'],
+    'condition': 'checkout_linux and checkout_arm64',
+    'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
+               '--arch=arm64'],
   },
   {
-    # Update the Mac toolchain if necessary.
-    'name': 'mac_toolchain',
+    'name': 'sysroot_x86',
     'pattern': '.',
-    'action': ['python', 'pdfium/build/mac_toolchain.py'],
+    'condition': 'checkout_linux and (checkout_x86 or checkout_x64)',
+    'action': ['python', 'pdfium/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',
+               '--arch=mips'],
+  },
+  {
+    'name': 'sysroot_x64',
+    'pattern': '.',
+    'condition': 'checkout_linux and checkout_x64',
+    'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py',
+               '--arch=x64'],
   },
   {
     'name': 'msan_chained_origins',
@@ -282,4 +281,11 @@
                 "-s", "pdfium/third_party/instrumented_libraries/binaries/msan-no-origins-trusty.tgz.sha1",
               ],
   },
+  {
+    # Update LASTCHANGE.
+    'name': 'lastchange',
+    'pattern': '.',
+    'action': ['python', 'pdfium/build/util/lastchange.py',
+               '-o', 'pdfium/build/util/LASTCHANGE'],
+  },
 ]
diff --git a/LICENSE b/LICENSE
index 8b4ed6d..bb158cb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -25,3 +25,206 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        https://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       https://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index a7db123..9ad4abe 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -45,8 +45,21 @@
   # eval-ed and thus doesn't have __file__.
   original_sys_path = sys.path
   try:
-    sys.path = sys.path + [input_api.os_path.join(
-        input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
+    def GenerateCheckdepsPath(base_path):
+      return input_api.os_path.join(base_path, 'buildtools', 'checkdeps')
+
+    presubmit_path = input_api.PresubmitLocalPath()
+    presubmit_parent_path = input_api.os_path.dirname(presubmit_path)
+    not_standalone_pdfium = \
+        input_api.os_path.basename(presubmit_parent_path) == "third_party" and \
+        input_api.os_path.basename(presubmit_path) == "pdfium"
+
+    sys.path.append(GenerateCheckdepsPath(presubmit_path))
+    if not_standalone_pdfium:
+      presubmit_grandparent_path = input_api.os_path.dirname(
+          presubmit_parent_path)
+      sys.path.append(GenerateCheckdepsPath(presubmit_grandparent_path))
+
     import checkdeps
     from cpp_checker import CppChecker
     from rules import Rule
@@ -257,6 +270,8 @@
   tests_added = []
   results = []
   for f in input_api.AffectedFiles():
+    if f.Action() == 'D':
+      continue
     if not f.LocalPath().startswith(('testing/resources/pixel/',
         'testing/resources/javascript/')):
       continue
@@ -293,11 +308,14 @@
   return results
 
 def CheckChangeOnUpload(input_api, output_api):
+  cpp_source_filter = lambda x: input_api.FilterSourceFile(
+      x, white_list=(r'\.(?:c|cc|cpp|h)$',))
+
   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, None, LINT_FILTERS)
+      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)
diff --git a/README.md b/README.md
index 62efdbc..a79e18d 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,10 @@
 
 ## Prerequisites
 
-Get the chromium depot tools via the instructions at
-http://www.chromium.org/developers/how-tos/install-depot-tools (this provides
-the gclient utility needed below).
+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.
 
 Also install Python, Subversion, and Git and make sure they're in your path.
 
@@ -14,13 +15,14 @@
 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)
+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.
+Compilation is done through Ninja, **not** Visual Studio.
 
 ### CPU Architectures supported
 
@@ -42,8 +44,9 @@
 Once you've done this, the toolchain will be installed automatically for
 you in the [Generate the build files](#GenBuild) step below.
 
-The toolchain will be in `depot_tools\win_toolchain\vs_files\<hash>`, and windbg
-can be found in `depot_tools\win_toolchain\vs_files\<hash>\win_sdk\Debuggers`.
+The toolchain will be in `depot_tools\win_toolchain\vs_files\<hash>`, and
+windbg can be found in
+`depot_tools\win_toolchain\vs_files\<hash>\win_sdk\Debuggers`.
 
 If you want the IDE for debugging and editing, you will need to install
 it separately, but this is optional and not needed for building PDFium.
@@ -62,7 +65,8 @@
 cd pdfium
 ```
 
-Additional build dependencies need to be installed by running:
+Additional build dependencies need to be installed by running the following from
+the `pdfium` directory.
 
 ```
 ./build/install-build-deps.sh
@@ -70,8 +74,7 @@
 
 ## Generate the build files
 
-We use GN to generate the build files and
-[Ninja](http://martine.github.io/ninja/)
+We use 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.
 
@@ -83,25 +86,40 @@
 
 Configuration is done by executing `gn args <directory>` to configure the build.
 This will launch an editor in which you can set the following arguments.
+By convention, `<directory>` should be named `out/foo`, and some tools / test
+support code only works if one follows this convention.
 A typical `<directory>` name is `out/Debug`.
 
 ```
 use_goma = true  # Googlers only. Make sure goma is installed and running first.
 is_debug = true  # Enable debugging features.
 
-pdf_use_skia = false  # Set true to enable experimental skia backend.
-pdf_use_skia_paths = false  # Set true to enable experimental skia backend (paths only).
+# 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 (must be false)
+is_component_build = false # Disable component build (Though it should work)
 
 clang_use_chrome_plugins = false  # Currently must be false.
 ```
 
-Note, you must set `pdf_is_standalone = true` if you want the sample
-applications like `pdfium_test` to build.
+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.
 
 When complete the arguments will be stored in `<directory>/args.gn`, and
 GN will automatically use the new arguments to generate build files.
@@ -117,9 +135,10 @@
 ## Running the sample program
 
 The pdfium\_test program supports reading, parsing, and rasterizing the pages of
-a .pdf file to .ppm or .png output image files (windows supports two other
+a .pdf file to .ppm or .png output image files (Windows supports two other
 formats). For example: `<directory>/pdfium_test --ppm path/to/myfile.pdf`. Note
 that this will write output images to `path/to/myfile.pdf.<n>.ppm`.
+Run `pdfium_test --help` to see all the options.
 
 ## Testing
 
@@ -136,15 +155,85 @@
 you see failures, it can be a good idea to run the tests on the tip-of-tree
 checkout to see if the same failures appear.
 
+### Pixel Tests
+
+If your change affects rendering, a pixel test should be added. Simply add a
+`.in` or `.pdf` file in `testing/resources/pixel` and the pixel runner will
+pick it up at the next run.
+
+Make sure that your test case doesn't have any copyright issues. It should also
+be a minimal test case focusing on the bug that renders the same way in many
+PDF viewers. Try to avoid binary data in streams by using the `ASCIIHexDecode`
+simply because it makes the PDF more readable in a text editor.
+
+To try out your new test, you can call the `run_pixel_tests.py` script:
+
+```bash
+$ ./testing/tools/run_pixel_tests.py your_new_file.in
+```
+
+To generate the expected image, you can use the `make_expected.sh` script:
+
+```bash
+$ ./testing/tools/make_expected.sh your_new_file.pdf
+```
+
+Please make sure to have `optipng` installed which optimized the file size of
+the resulting png.
+
+### `.in` files
+
+`.in` files are PDF template files. PDF files contain many byte offsets that
+have to be kept correct or the file won't be valid. The template makes this
+easier by replacing the byte offsets with certain keywords.
+
+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).
+
+To transform this into a PDF, you can use the `fixup_pdf_template.py` tool:
+
+```bash
+$ ./testing/tools/fixup_pdf_template.py your_file.in
+```
+
+This will create a `your_file.pdf` in the same directory as `your_file.in`.
+
+There is no official style guide for the .in file, but a consistent style is
+preferred simply to help with readability. If possible, object numbers should
+be consecutive and `/Type` and `/SubType` should be on top of a dictionary to
+make object identification easier.
+
+## 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.
+
+Outside of the public/ directory, code may change at any time, and embedders
+should not directly call these routines.
+
 ## Code Coverage
 
 Code coverage reports for PDFium can be generated in Linux development
 environments. Details can be found [here](/docs/code-coverage.md).
 
+Chromium provides code coverage reports for PDFium
+[here](https://chromium-coverage.appspot.com/). PDFium is located in
+`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 at
-http://build.chromium.org/p/client.pdfium/console
+The current health of the source tree can be found
+[here](https://ci.chromium.org/p/pdfium/g/main/console).
 
 ## Community
 
@@ -159,19 +248,25 @@
 ## Bugs
 
  We use this
-[bug tracker](https://code.google.com/p/pdfium/issues/list), but for security
-bugs, please use [Chromium's security bug template]
-(https://code.google.com/p/chromium/issues/entry?template=Security%20Bug)
+[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](http://dev.chromium.org/developers/contributing-code)
+[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 tool for code reviews, and credentials for
-the tool need to be generated before uploading a CL.
-3. PDFium is currently holding at C++11 compatibility, rejecting features that
-are only present in C++14 (onto which Chromium is now slowly moving).
+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.
diff --git a/build/build_config.h b/build/build_config.h
new file mode 100644
index 0000000..8165eee
--- /dev/null
+++ b/build/build_config.h
@@ -0,0 +1,7 @@
+#define OS_POSIX
+#define OS_ANDROID
+#define USE_SYSTEM_ICUUC
+#define USE_SYSTEM_LIBJPEG
+#define USE_SYSTEM_ZLIB
+// Makes base/logging.h and base/bits.h work.
+#define COMPILER_GCC
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index af65788..bb15253 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -16,8 +16,8 @@
 
 # Support different NDK locations in non-Chromium builds.
 default_android_ndk_root = "//third_party/android_ndk"
-default_android_ndk_version = "r12b"
-default_android_ndk_major_version = 12
+default_android_ndk_version = "r16"
+default_android_ndk_major_version = 16
 
 # PDFium builds don't support building java targets.
 enable_java_templates = false
diff --git a/constants/Android.bp b/constants/Android.bp
new file mode 100644
index 0000000..23c9755
--- /dev/null
+++ b/constants/Android.bp
@@ -0,0 +1,6 @@
+cc_library_headers {
+    name: "libpdfium-constants",
+    export_include_dirs: ["."],
+    visibility: ["//external/pdfium:__subpackages__"],
+}
+
diff --git a/constants/BUILD.gn b/constants/BUILD.gn
new file mode 100644
index 0000000..4c10fed
--- /dev/null
+++ b/constants/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../pdfium.gni")
+
+source_set("constants") {
+  sources = [
+    "annotation_common.h",
+    "annotation_flags.h",
+    "form_fields.h",
+    "form_flags.h",
+    "page_object.h",
+    "stream_dict_common.h",
+    "transparency.h",
+  ]
+}
diff --git a/constants/annotation_common.h b/constants/annotation_common.h
new file mode 100644
index 0000000..471d244
--- /dev/null
+++ b/constants/annotation_common.h
@@ -0,0 +1,32 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_ANNOTATION_COMMON_H_
+#define CONSTANTS_ANNOTATION_COMMON_H_
+
+namespace pdfium {
+namespace annotation {
+
+// PDF 1.7 spec, table 8.15.
+// Entries common to all annotation dictionaries.
+
+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";
+
+}  // namespace annotation
+}  // namespace pdfium
+
+#endif  // CONSTANTS_ANNOTATION_COMMON_H_
diff --git a/constants/annotation_flags.h b/constants/annotation_flags.h
new file mode 100644
index 0000000..d2731da
--- /dev/null
+++ b/constants/annotation_flags.h
@@ -0,0 +1,26 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_ANNOTATION_FLAGS_H_
+#define CONSTANTS_ANNOTATION_FLAGS_H_
+
+namespace pdfium {
+namespace annotation_flags {
+
+// PDF 1.7 spec, table 8.16.
+constexpr uint32_t kInvisible = 1 << 0;
+constexpr uint32_t kHidden = 1 << 1;
+constexpr uint32_t kPrint = 1 << 2;
+constexpr uint32_t kNoZoom = 1 << 3;
+constexpr uint32_t kNoRotate = 1 << 4;
+constexpr uint32_t kNoView = 1 << 5;
+constexpr uint32_t kReadOnly = 1 << 6;
+constexpr uint32_t kLocked = 1 << 7;
+constexpr uint32_t kToggleNoView = 1 << 8;
+constexpr uint32_t kLockedContents = 1 << 9;
+
+}  // namespace annotation_flags
+}  // namespace pdfium
+
+#endif  // CONSTANTS_ANNOTATION_FLAGS_H_
diff --git a/constants/form_fields.h b/constants/form_fields.h
new file mode 100644
index 0000000..5b7c169
--- /dev/null
+++ b/constants/form_fields.h
@@ -0,0 +1,33 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_FORM_FIELDS_H_
+#define CONSTANTS_FORM_FIELDS_H_
+
+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";
+
+// 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";
+
+}  // namespace form_fields
+}  // namespace pdfium
+
+#endif  // CONSTANTS_FORM_FIELDS_H_
diff --git a/constants/form_flags.h b/constants/form_flags.h
new file mode 100644
index 0000000..148bb4c
--- /dev/null
+++ b/constants/form_flags.h
@@ -0,0 +1,46 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_FORM_FLAGS_H_
+#define CONSTANTS_FORM_FLAGS_H_
+
+namespace pdfium {
+namespace form_flags {
+
+// PDF 1.7 spec, table 8.70.
+// Field flags common to all field types.
+constexpr uint32_t kReadOnly = 1 << 0;
+constexpr uint32_t kRequired = 1 << 1;
+constexpr uint32_t kNoExport = 1 << 2;
+
+// PDF 1.7 spec, table 8.75.
+// Field flags specific to button fields.
+constexpr uint32_t kButtonNoToggleToOff = 1 << 14;
+constexpr uint32_t kButtonRadio = 1 << 15;
+constexpr uint32_t kButtonPushbutton = 1 << 16;
+constexpr uint32_t kButtonRadiosInUnison = 1 << 25;
+
+// PDF 1.7 spec, table 8.77.
+// Field flags specific to text fields.
+constexpr uint32_t kTextMultiline = 1 << 12;
+constexpr uint32_t kTextPassword = 1 << 13;
+constexpr uint32_t kTextFileSelect = 1 << 20;
+constexpr uint32_t kTextDoNotSpellCheck = 1 << 22;
+constexpr uint32_t kTextDoNotScroll = 1 << 23;
+constexpr uint32_t kTextComb = 1 << 24;
+constexpr uint32_t kTextRichText = 1 << 25;
+
+// PDF 1.7 spec, table 8.79.
+// Field flags specific to choice fields.
+constexpr uint32_t kChoiceCombo = 1 << 17;
+constexpr uint32_t kChoiceEdit = 1 << 18;
+constexpr uint32_t kChoiceSort = 1 << 19;
+constexpr uint32_t kChoiceMultiSelect = 1 << 21;
+constexpr uint32_t kChoiceDoNotSpellCheck = 1 << 22;
+constexpr uint32_t kChoiceCommitOnSelChange = 1 << 26;
+
+}  // namespace form_flags
+}  // namespace pdfium
+
+#endif  // CONSTANTS_FORM_FLAGS_H_
diff --git a/constants/page_object.h b/constants/page_object.h
new file mode 100644
index 0000000..8a41b8c
--- /dev/null
+++ b/constants/page_object.h
@@ -0,0 +1,28 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_PAGE_OBJECT_H_
+#define CONSTANTS_PAGE_OBJECT_H_
+
+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";
+
+}  // namespace page_object
+}  // namespace pdfium
+
+#endif  // CONSTANTS_PAGE_OBJECT_H_
diff --git a/constants/stream_dict_common.h b/constants/stream_dict_common.h
new file mode 100644
index 0000000..fc12622
--- /dev/null
+++ b/constants/stream_dict_common.h
@@ -0,0 +1,27 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_STREAM_DICT_COMMON_H_
+#define 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".
+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";
+
+}  // namespace stream
+}  // namespace pdfium
+
+#endif  // CONSTANTS_STREAM_DICT_COMMON_H_
diff --git a/constants/transparency.h b/constants/transparency.h
new file mode 100644
index 0000000..6532868
--- /dev/null
+++ b/constants/transparency.h
@@ -0,0 +1,55 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONSTANTS_TRANSPARENCY_H_
+#define CONSTANTS_TRANSPARENCY_H_
+
+namespace pdfium {
+namespace transparency {
+
+// PDF 1.7 spec, table 7.2.
+// Standard separable blend modes.
+
+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";
+
+// PDF 1.7 spec, table 7.3.
+// Standard nonseparable blend modes.
+
+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";
+
+}  // namespace transparency
+}  // namespace pdfium
+
+#endif  // CONSTANTS_TRANSPARENCY_H_
diff --git a/core/fdrm/Android.bp b/core/fdrm/Android.bp
new file mode 100644
index 0000000..c35475a
--- /dev/null
+++ b/core/fdrm/Android.bp
@@ -0,0 +1,19 @@
+cc_library_static {
+    name: "libpdfium-fdrm",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    static_libs: [
+        "libpdfium-fxcrt",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+
+    include_dirs: [
+        "external/freetype/include",
+        "external/freetype/include/freetype",
+    ],
+}
diff --git a/core/fdrm/BUILD.gn b/core/fdrm/BUILD.gn
new file mode 100644
index 0000000..888bb92
--- /dev/null
+++ b/core/fdrm/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+source_set("fdrm") {
+  sources = [
+    "fx_crypt.cpp",
+    "fx_crypt.h",
+    "fx_crypt_aes.cpp",
+    "fx_crypt_sha.cpp",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  deps = [ "../fxcrt" ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [ "fx_crypt_unittest.cpp" ]
+  deps = [ ":fdrm" ]
+  pdfium_root_dir = "../../"
+}
diff --git a/core/fdrm/crypto/fx_crypt.cpp b/core/fdrm/crypto/fx_crypt.cpp
deleted file mode 100644
index d53fa6b..0000000
--- a/core/fdrm/crypto/fx_crypt.cpp
+++ /dev/null
@@ -1,235 +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/fdrm/crypto/fx_crypt.h"
-
-#include <utility>
-
-#define GET_UINT32(n, b, i)                            \
-  {                                                    \
-    (n) = (uint32_t)((uint8_t*)b)[(i)] |               \
-          (((uint32_t)((uint8_t*)b)[(i) + 1]) << 8) |  \
-          (((uint32_t)((uint8_t*)b)[(i) + 2]) << 16) | \
-          (((uint32_t)((uint8_t*)b)[(i) + 3]) << 24);  \
-  }
-#define PUT_UINT32(n, b, i)                                   \
-  {                                                           \
-    (((uint8_t*)b)[(i)]) = (uint8_t)(((n)) & 0xFF);           \
-    (((uint8_t*)b)[(i) + 1]) = (uint8_t)(((n) >> 8) & 0xFF);  \
-    (((uint8_t*)b)[(i) + 2]) = (uint8_t)(((n) >> 16) & 0xFF); \
-    (((uint8_t*)b)[(i) + 3]) = (uint8_t)(((n) >> 24) & 0xFF); \
-  }
-
-namespace {
-
-const uint8_t md5_padding[64] = {
-    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
-void md5_process(CRYPT_md5_context* ctx, const uint8_t data[64]) {
-  uint32_t A, B, C, D, X[16];
-  GET_UINT32(X[0], data, 0);
-  GET_UINT32(X[1], data, 4);
-  GET_UINT32(X[2], data, 8);
-  GET_UINT32(X[3], data, 12);
-  GET_UINT32(X[4], data, 16);
-  GET_UINT32(X[5], data, 20);
-  GET_UINT32(X[6], data, 24);
-  GET_UINT32(X[7], data, 28);
-  GET_UINT32(X[8], data, 32);
-  GET_UINT32(X[9], data, 36);
-  GET_UINT32(X[10], data, 40);
-  GET_UINT32(X[11], data, 44);
-  GET_UINT32(X[12], data, 48);
-  GET_UINT32(X[13], data, 52);
-  GET_UINT32(X[14], data, 56);
-  GET_UINT32(X[15], data, 60);
-#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);
-  P(C, D, A, B, 2, 17, 0x242070DB);
-  P(B, C, D, A, 3, 22, 0xC1BDCEEE);
-  P(A, B, C, D, 4, 7, 0xF57C0FAF);
-  P(D, A, B, C, 5, 12, 0x4787C62A);
-  P(C, D, A, B, 6, 17, 0xA8304613);
-  P(B, C, D, A, 7, 22, 0xFD469501);
-  P(A, B, C, D, 8, 7, 0x698098D8);
-  P(D, A, B, C, 9, 12, 0x8B44F7AF);
-  P(C, D, A, B, 10, 17, 0xFFFF5BB1);
-  P(B, C, D, A, 11, 22, 0x895CD7BE);
-  P(A, B, C, D, 12, 7, 0x6B901122);
-  P(D, A, B, C, 13, 12, 0xFD987193);
-  P(C, D, A, B, 14, 17, 0xA679438E);
-  P(B, C, D, A, 15, 22, 0x49B40821);
-#undef F
-#define F(x, y, z) (y ^ (z & (x ^ y)))
-  P(A, B, C, D, 1, 5, 0xF61E2562);
-  P(D, A, B, C, 6, 9, 0xC040B340);
-  P(C, D, A, B, 11, 14, 0x265E5A51);
-  P(B, C, D, A, 0, 20, 0xE9B6C7AA);
-  P(A, B, C, D, 5, 5, 0xD62F105D);
-  P(D, A, B, C, 10, 9, 0x02441453);
-  P(C, D, A, B, 15, 14, 0xD8A1E681);
-  P(B, C, D, A, 4, 20, 0xE7D3FBC8);
-  P(A, B, C, D, 9, 5, 0x21E1CDE6);
-  P(D, A, B, C, 14, 9, 0xC33707D6);
-  P(C, D, A, B, 3, 14, 0xF4D50D87);
-  P(B, C, D, A, 8, 20, 0x455A14ED);
-  P(A, B, C, D, 13, 5, 0xA9E3E905);
-  P(D, A, B, C, 2, 9, 0xFCEFA3F8);
-  P(C, D, A, B, 7, 14, 0x676F02D9);
-  P(B, C, D, A, 12, 20, 0x8D2A4C8A);
-#undef F
-#define F(x, y, z) (x ^ y ^ z)
-  P(A, B, C, D, 5, 4, 0xFFFA3942);
-  P(D, A, B, C, 8, 11, 0x8771F681);
-  P(C, D, A, B, 11, 16, 0x6D9D6122);
-  P(B, C, D, A, 14, 23, 0xFDE5380C);
-  P(A, B, C, D, 1, 4, 0xA4BEEA44);
-  P(D, A, B, C, 4, 11, 0x4BDECFA9);
-  P(C, D, A, B, 7, 16, 0xF6BB4B60);
-  P(B, C, D, A, 10, 23, 0xBEBFBC70);
-  P(A, B, C, D, 13, 4, 0x289B7EC6);
-  P(D, A, B, C, 0, 11, 0xEAA127FA);
-  P(C, D, A, B, 3, 16, 0xD4EF3085);
-  P(B, C, D, A, 6, 23, 0x04881D05);
-  P(A, B, C, D, 9, 4, 0xD9D4D039);
-  P(D, A, B, C, 12, 11, 0xE6DB99E5);
-  P(C, D, A, B, 15, 16, 0x1FA27CF8);
-  P(B, C, D, A, 2, 23, 0xC4AC5665);
-#undef F
-#define F(x, y, z) (y ^ (x | ~z))
-  P(A, B, C, D, 0, 6, 0xF4292244);
-  P(D, A, B, C, 7, 10, 0x432AFF97);
-  P(C, D, A, B, 14, 15, 0xAB9423A7);
-  P(B, C, D, A, 5, 21, 0xFC93A039);
-  P(A, B, C, D, 12, 6, 0x655B59C3);
-  P(D, A, B, C, 3, 10, 0x8F0CCC92);
-  P(C, D, A, B, 10, 15, 0xFFEFF47D);
-  P(B, C, D, A, 1, 21, 0x85845DD1);
-  P(A, B, C, D, 8, 6, 0x6FA87E4F);
-  P(D, A, B, C, 15, 10, 0xFE2CE6E0);
-  P(C, D, A, B, 6, 15, 0xA3014314);
-  P(B, C, D, A, 13, 21, 0x4E0811A1);
-  P(A, B, C, D, 4, 6, 0xF7537E82);
-  P(D, A, B, C, 11, 10, 0xBD3AF235);
-  P(C, D, A, B, 2, 15, 0x2AD7D2BB);
-  P(B, C, D, A, 9, 21, 0xEB86D391);
-#undef F
-  ctx->state[0] += A;
-  ctx->state[1] += B;
-  ctx->state[2] += C;
-  ctx->state[3] += D;
-}
-
-}  // namespace
-
-void CRYPT_ArcFourSetup(CRYPT_rc4_context* s,
-                        const uint8_t* key,
-                        uint32_t length) {
-  s->x = 0;
-  s->y = 0;
-  for (int i = 0; i < kRC4ContextPermutationLength; ++i)
-    s->m[i] = i;
-
-  int j = 0;
-  for (int i = 0; i < kRC4ContextPermutationLength; ++i) {
-    j = (j + s->m[i] + (length ? key[i % length] : 0)) & 0xFF;
-    std::swap(s->m[i], s->m[j]);
-  }
-}
-
-void CRYPT_ArcFourCrypt(CRYPT_rc4_context* s, uint8_t* data, uint32_t length) {
-  for (uint32_t i = 0; i < length; ++i) {
-    s->x = (s->x + 1) & 0xFF;
-    s->y = (s->y + s->m[s->x]) & 0xFF;
-    std::swap(s->m[s->x], s->m[s->y]);
-    data[i] ^= s->m[(s->m[s->x] + s->m[s->y]) & 0xFF];
-  }
-}
-
-void CRYPT_ArcFourCryptBlock(uint8_t* pData,
-                             uint32_t size,
-                             const uint8_t* key,
-                             uint32_t keylen) {
-  CRYPT_rc4_context s;
-  CRYPT_ArcFourSetup(&s, key, keylen);
-  CRYPT_ArcFourCrypt(&s, pData, size);
-}
-
-void CRYPT_MD5Start(CRYPT_md5_context* ctx) {
-  ctx->total[0] = 0;
-  ctx->total[1] = 0;
-  ctx->state[0] = 0x67452301;
-  ctx->state[1] = 0xEFCDAB89;
-  ctx->state[2] = 0x98BADCFE;
-  ctx->state[3] = 0x10325476;
-}
-
-void CRYPT_MD5Update(CRYPT_md5_context* ctx,
-                     const uint8_t* input,
-                     uint32_t length) {
-  uint32_t left, fill;
-  if (!length) {
-    return;
-  }
-  left = (ctx->total[0] >> 3) & 0x3F;
-  fill = 64 - left;
-  ctx->total[0] += length << 3;
-  ctx->total[1] += length >> 29;
-  ctx->total[0] &= 0xFFFFFFFF;
-  ctx->total[1] += ctx->total[0] < length << 3;
-  if (left && length >= fill) {
-    memcpy(ctx->buffer + left, input, fill);
-    md5_process(ctx, ctx->buffer);
-    length -= fill;
-    input += fill;
-    left = 0;
-  }
-  while (length >= 64) {
-    md5_process(ctx, input);
-    length -= 64;
-    input += 64;
-  }
-  if (length) {
-    memcpy(ctx->buffer + left, input, length);
-  }
-}
-
-void CRYPT_MD5Finish(CRYPT_md5_context* ctx, uint8_t digest[16]) {
-  uint32_t last, padn;
-  uint8_t msglen[8];
-  PUT_UINT32(ctx->total[0], msglen, 0);
-  PUT_UINT32(ctx->total[1], msglen, 4);
-  last = (ctx->total[0] >> 3) & 0x3F;
-  padn = (last < 56) ? (56 - last) : (120 - last);
-  CRYPT_MD5Update(ctx, md5_padding, padn);
-  CRYPT_MD5Update(ctx, msglen, 8);
-  PUT_UINT32(ctx->state[0], digest, 0);
-  PUT_UINT32(ctx->state[1], digest, 4);
-  PUT_UINT32(ctx->state[2], digest, 8);
-  PUT_UINT32(ctx->state[3], digest, 12);
-}
-
-void CRYPT_MD5Generate(const uint8_t* input,
-                       uint32_t length,
-                       uint8_t digest[16]) {
-  CRYPT_md5_context ctx;
-  CRYPT_MD5Start(&ctx);
-  CRYPT_MD5Update(&ctx, input, length);
-  CRYPT_MD5Finish(&ctx, digest);
-}
diff --git a/core/fdrm/crypto/fx_crypt.h b/core/fdrm/crypto/fx_crypt.h
deleted file mode 100644
index 4f6717f..0000000
--- a/core/fdrm/crypto/fx_crypt.h
+++ /dev/null
@@ -1,118 +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_FDRM_CRYPTO_FX_CRYPT_H_
-#define CORE_FDRM_CRYPTO_FX_CRYPT_H_
-
-#include "core/fxcrt/fx_system.h"
-
-constexpr int32_t kRC4ContextPermutationLength = 256;
-struct CRYPT_rc4_context {
-  int32_t x;
-  int32_t y;
-  int32_t m[kRC4ContextPermutationLength];
-};
-
-#define MAX_NR 14
-#define MAX_NB 8
-struct CRYPT_aes_context {
-  void (*encrypt)(CRYPT_aes_context* ctx, unsigned int* block);
-  void (*decrypt)(CRYPT_aes_context* ctx, unsigned int* block);
-  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];
-};
-
-struct CRYPT_md5_context {
-  uint32_t total[2];
-  uint32_t state[4];
-  uint8_t buffer[64];
-};
-
-struct CRYPT_sha1_context {
-  unsigned int h[5];
-  unsigned char block[64];
-  int blkused;
-  unsigned int lenhi;
-  unsigned int lenlo;
-};
-
-struct CRYPT_sha2_context {
-  uint64_t total[2];
-  uint64_t state[8];
-  uint8_t buffer[128];
-};
-
-void CRYPT_ArcFourCryptBlock(uint8_t* data,
-                             uint32_t size,
-                             const uint8_t* key,
-                             uint32_t keylen);
-void CRYPT_ArcFourSetup(CRYPT_rc4_context* context,
-                        const uint8_t* key,
-                        uint32_t length);
-void CRYPT_ArcFourCrypt(CRYPT_rc4_context* context,
-                        uint8_t* data,
-                        uint32_t size);
-
-void CRYPT_AESSetKey(CRYPT_aes_context* context,
-                     uint32_t blocklen,
-                     const uint8_t* key,
-                     uint32_t keylen,
-                     bool bEncrypt);
-void CRYPT_AESSetIV(CRYPT_aes_context* context, const uint8_t* iv);
-void CRYPT_AESDecrypt(CRYPT_aes_context* context,
-                      uint8_t* dest,
-                      const uint8_t* src,
-                      uint32_t size);
-void CRYPT_AESEncrypt(CRYPT_aes_context* context,
-                      uint8_t* dest,
-                      const uint8_t* src,
-                      uint32_t size);
-
-void CRYPT_MD5Start(CRYPT_md5_context* context);
-void CRYPT_MD5Update(CRYPT_md5_context* context,
-                     const uint8_t* data,
-                     uint32_t size);
-void CRYPT_MD5Finish(CRYPT_md5_context* context, uint8_t digest[16]);
-void CRYPT_MD5Generate(const uint8_t* data, uint32_t size, uint8_t digest[16]);
-
-void CRYPT_SHA1Start(CRYPT_sha1_context* context);
-void CRYPT_SHA1Update(CRYPT_sha1_context* context,
-                      const uint8_t* data,
-                      uint32_t size);
-void CRYPT_SHA1Finish(CRYPT_sha1_context* context, uint8_t digest[20]);
-void CRYPT_SHA1Generate(const uint8_t* data, uint32_t size, uint8_t digest[20]);
-
-void CRYPT_SHA256Start(CRYPT_sha2_context* context);
-void CRYPT_SHA256Update(CRYPT_sha2_context* context,
-                        const uint8_t* data,
-                        uint32_t size);
-void CRYPT_SHA256Finish(CRYPT_sha2_context* context, uint8_t digest[32]);
-void CRYPT_SHA256Generate(const uint8_t* data,
-                          uint32_t size,
-                          uint8_t digest[32]);
-
-void CRYPT_SHA384Start(CRYPT_sha2_context* context);
-void CRYPT_SHA384Update(CRYPT_sha2_context* context,
-                        const uint8_t* data,
-                        uint32_t size);
-void CRYPT_SHA384Finish(CRYPT_sha2_context* context, uint8_t digest[48]);
-void CRYPT_SHA384Generate(const uint8_t* data,
-                          uint32_t size,
-                          uint8_t digest[48]);
-
-void CRYPT_SHA512Start(CRYPT_sha2_context* context);
-void CRYPT_SHA512Update(CRYPT_sha2_context* context,
-                        const uint8_t* data,
-                        uint32_t size);
-void CRYPT_SHA512Finish(CRYPT_sha2_context* context, uint8_t digest[64]);
-void CRYPT_SHA512Generate(const uint8_t* data,
-                          uint32_t size,
-                          uint8_t digest[64]);
-
-#endif  // CORE_FDRM_CRYPTO_FX_CRYPT_H_
diff --git a/core/fdrm/crypto/fx_crypt_aes.cpp b/core/fdrm/crypto/fx_crypt_aes.cpp
deleted file mode 100644
index 94d66d0..0000000
--- a/core/fdrm/crypto/fx_crypt_aes.cpp
+++ /dev/null
@@ -1,831 +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/fdrm/crypto/fx_crypt.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);                 \
-    (cp)[2] = (value) >> 8;            \
-    (cp)[1] = (value) >> 16;           \
-    (cp)[0] = (value) >> 24;           \
-  } while (0)
-
-namespace {
-
-const unsigned char Sbox[256] = {
-    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
-    0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
-    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
-    0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
-    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
-    0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
-    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
-    0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
-    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
-    0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
-    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
-    0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
-    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
-    0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
-    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
-    0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
-    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
-    0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
-    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
-    0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
-    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
-    0xb0, 0x54, 0xbb, 0x16};
-const unsigned char Sboxinv[256] = {
-    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
-    0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
-    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
-    0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
-    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
-    0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
-    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
-    0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
-    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
-    0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
-    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
-    0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
-    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
-    0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
-    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
-    0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
-    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
-    0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
-    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
-    0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
-    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
-    0x55, 0x21, 0x0c, 0x7d};
-const unsigned int E0[256] = {
-    0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd,
-    0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
-    0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d,
-    0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
-    0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7,
-    0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
-    0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4,
-    0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
-    0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1,
-    0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
-    0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e,
-    0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
-    0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e,
-    0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
-    0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46,
-    0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
-    0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7,
-    0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
-    0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe,
-    0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
-    0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a,
-    0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
-    0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2,
-    0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
-    0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e,
-    0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
-    0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256,
-    0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
-    0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4,
-    0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
-    0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa,
-    0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
-    0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1,
-    0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
-    0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42,
-    0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
-    0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158,
-    0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
-    0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22,
-    0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
-    0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631,
-    0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
-    0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
-};
-const unsigned int E1[256] = {
-    0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b,
-    0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b,
-    0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282,
-    0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
-    0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4,
-    0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,
-    0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5,
-    0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
-    0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696,
-    0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,
-    0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383,
-    0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
-    0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3,
-    0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded,
-    0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb,
-    0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
-    0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d,
-    0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f,
-    0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3,
-    0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
-    0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff,
-    0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,
-    0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7,
-    0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
-    0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a,
-    0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,
-    0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232,
-    0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
-    0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595,
-    0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,
-    0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656,
-    0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
-    0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6,
-    0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,
-    0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e,
-    0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
-    0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1,
-    0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,
-    0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e,
-    0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
-    0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6,
-    0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,
-    0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,
-};
-const unsigned int E2[256] = {
-    0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b,
-    0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b,
-    0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82,
-    0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
-    0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4,
-    0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,
-    0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5,
-    0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
-    0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796,
-    0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,
-    0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83,
-    0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
-    0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3,
-    0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed,
-    0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb,
-    0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
-    0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d,
-    0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f,
-    0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3,
-    0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
-    0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff,
-    0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec,
-    0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7,
-    0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
-    0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a,
-    0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,
-    0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432,
-    0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
-    0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195,
-    0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,
-    0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56,
-    0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
-    0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6,
-    0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,
-    0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e,
-    0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
-    0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1,
-    0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,
-    0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e,
-    0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
-    0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6,
-    0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f,
-    0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,
-};
-const unsigned int E3[256] = {
-    0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6,
-    0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56,
-    0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f,
-    0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
-    0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753,
-    0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,
-    0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451,
-    0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
-    0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137,
-    0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,
-    0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d,
-    0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
-    0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd,
-    0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1,
-    0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d,
-    0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
-    0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a,
-    0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe,
-    0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d,
-    0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
-    0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5,
-    0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3,
-    0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255,
-    0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
-    0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54,
-    0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,
-    0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664,
-    0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
-    0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431,
-    0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,
-    0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac,
-    0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
-    0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157,
-    0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,
-    0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c,
-    0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
-    0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899,
-    0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,
-    0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c,
-    0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
-    0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7,
-    0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e,
-    0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,
-};
-const unsigned int D0[256] = {
-    0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1,
-    0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
-    0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67,
-    0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
-    0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3,
-    0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
-    0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182,
-    0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
-    0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2,
-    0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
-    0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492,
-    0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
-    0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa,
-    0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
-    0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997,
-    0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
-    0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48,
-    0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,
-    0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f,
-    0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
-    0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad,
-    0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
-    0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc,
-    0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
-    0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3,
-    0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
-    0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1,
-    0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
-    0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8,
-    0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
-    0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4,
-    0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
-    0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331,
-    0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
-    0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d,
-    0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
-    0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252,
-    0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
-    0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f,
-    0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
-    0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c,
-    0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,
-    0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742,
-};
-const unsigned int D1[256] = {
-    0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45,
-    0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c,
-    0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b,
-    0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
-    0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421,
-    0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971,
-    0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31,
-    0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
-    0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02,
-    0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708,
-    0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4,
-    0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
-    0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef,
-    0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd,
-    0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9,
-    0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
-    0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed,
-    0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39,
-    0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7,
-    0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
-    0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7,
-    0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60,
-    0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6,
-    0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
-    0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230,
-    0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964,
-    0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1,
-    0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
-    0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8,
-    0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512,
-    0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918,
-    0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,
-    0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23,
-    0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8,
-    0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0,
-    0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
-    0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2,
-    0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c,
-    0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273,
-    0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
-    0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225,
-    0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1,
-    0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857,
-};
-const unsigned int D2[256] = {
-    0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d,
-    0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502,
-    0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba,
-    0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
-    0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874,
-    0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9,
-    0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a,
-    0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
-    0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b,
-    0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337,
-    0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779,
-    0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
-    0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060,
-    0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6,
-    0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd,
-    0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
-    0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b,
-    0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d,
-    0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357,
-    0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
-    0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b,
-    0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f,
-    0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23,
-    0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
-    0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2,
-    0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9,
-    0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938,
-    0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
-    0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8,
-    0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25,
-    0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59,
-    0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
-    0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f,
-    0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7,
-    0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef,
-    0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
-    0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db,
-    0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13,
-    0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2,
-    0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
-    0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2,
-    0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456,
-    0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8,
-};
-const unsigned int D3[256] = {
-    0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f,
-    0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5,
-    0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725,
-    0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
-    0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358,
-    0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27,
-    0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5,
-    0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,
-    0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272,
-    0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3,
-    0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7,
-    0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,
-    0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40,
-    0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d,
-    0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6,
-    0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
-    0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832,
-    0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736,
-    0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93,
-    0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
-    0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2,
-    0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3,
-    0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb,
-    0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
-    0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc,
-    0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247,
-    0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9,
-    0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
-    0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890,
-    0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf,
-    0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e,
-    0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
-    0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a,
-    0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533,
-    0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43,
-    0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
-    0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292,
-    0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb,
-    0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55,
-    0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
-    0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc,
-    0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064,
-    0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,
-};
-#define ADD_ROUND_KEY_4                                                       \
-  (block[0] ^= *keysched++, block[1] ^= *keysched++, block[2] ^= *keysched++, \
-   block[3] ^= *keysched++)
-#define ADD_ROUND_KEY_6                                                       \
-  (block[0] ^= *keysched++, block[1] ^= *keysched++, block[2] ^= *keysched++, \
-   block[3] ^= *keysched++, block[4] ^= *keysched++, block[5] ^= *keysched++)
-#define ADD_ROUND_KEY_8                                                       \
-  (block[0] ^= *keysched++, block[1] ^= *keysched++, block[2] ^= *keysched++, \
-   block[3] ^= *keysched++, block[4] ^= *keysched++, block[5] ^= *keysched++, \
-   block[6] ^= *keysched++, block[7] ^= *keysched++)
-#define MOVEWORD(i) (block[i] = newstate[i])
-#undef MAKEWORD
-#define MAKEWORD(i)                                         \
-  (newstate[i] = (E0[(block[i] >> 24) & 0xFF] ^             \
-                  E1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \
-                  E2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^  \
-                  E3[block[(i + C3) % Nb] & 0xFF]))
-#define LASTWORD(i)                                                  \
-  (newstate[i] = (Sbox[(block[i] >> 24) & 0xFF] << 24) |             \
-                 (Sbox[(block[(i + C1) % Nb] >> 16) & 0xFF] << 16) | \
-                 (Sbox[(block[(i + C2) % Nb] >> 8) & 0xFF] << 8) |   \
-                 (Sbox[(block[(i + C3) % Nb]) & 0xFF]))
-
-void aes_encrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) {
-  int i;
-  const int C1 = 1, C2 = 2, C3 = 3, 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);
-    MOVEWORD(0);
-    MOVEWORD(1);
-    MOVEWORD(2);
-    MOVEWORD(3);
-  }
-  ADD_ROUND_KEY_4;
-  LASTWORD(0);
-  LASTWORD(1);
-  LASTWORD(2);
-  LASTWORD(3);
-  MOVEWORD(0);
-  MOVEWORD(1);
-  MOVEWORD(2);
-  MOVEWORD(3);
-  ADD_ROUND_KEY_4;
-}
-
-void aes_encrypt_nb_6(CRYPT_aes_context* ctx, unsigned int* block) {
-  int i;
-  const int C1 = 1, C2 = 2, C3 = 3, Nb = 6;
-  unsigned int* keysched = ctx->keysched;
-  unsigned int newstate[6];
-  for (i = 0; i < ctx->Nr - 1; i++) {
-    ADD_ROUND_KEY_6;
-    MAKEWORD(0);
-    MAKEWORD(1);
-    MAKEWORD(2);
-    MAKEWORD(3);
-    MAKEWORD(4);
-    MAKEWORD(5);
-    MOVEWORD(0);
-    MOVEWORD(1);
-    MOVEWORD(2);
-    MOVEWORD(3);
-    MOVEWORD(4);
-    MOVEWORD(5);
-  }
-  ADD_ROUND_KEY_6;
-  LASTWORD(0);
-  LASTWORD(1);
-  LASTWORD(2);
-  LASTWORD(3);
-  LASTWORD(4);
-  LASTWORD(5);
-  MOVEWORD(0);
-  MOVEWORD(1);
-  MOVEWORD(2);
-  MOVEWORD(3);
-  MOVEWORD(4);
-  MOVEWORD(5);
-  ADD_ROUND_KEY_6;
-}
-
-void aes_encrypt_nb_8(CRYPT_aes_context* ctx, unsigned int* block) {
-  int i;
-  const int C1 = 1, C2 = 3, C3 = 4, Nb = 8;
-  unsigned int* keysched = ctx->keysched;
-  unsigned int newstate[8];
-  for (i = 0; i < ctx->Nr - 1; i++) {
-    ADD_ROUND_KEY_8;
-    MAKEWORD(0);
-    MAKEWORD(1);
-    MAKEWORD(2);
-    MAKEWORD(3);
-    MAKEWORD(4);
-    MAKEWORD(5);
-    MAKEWORD(6);
-    MAKEWORD(7);
-    MOVEWORD(0);
-    MOVEWORD(1);
-    MOVEWORD(2);
-    MOVEWORD(3);
-    MOVEWORD(4);
-    MOVEWORD(5);
-    MOVEWORD(6);
-    MOVEWORD(7);
-  }
-  ADD_ROUND_KEY_8;
-  LASTWORD(0);
-  LASTWORD(1);
-  LASTWORD(2);
-  LASTWORD(3);
-  LASTWORD(4);
-  LASTWORD(5);
-  LASTWORD(6);
-  LASTWORD(7);
-  MOVEWORD(0);
-  MOVEWORD(1);
-  MOVEWORD(2);
-  MOVEWORD(3);
-  MOVEWORD(4);
-  MOVEWORD(5);
-  MOVEWORD(6);
-  MOVEWORD(7);
-  ADD_ROUND_KEY_8;
-}
-#undef MAKEWORD
-#undef LASTWORD
-#define MAKEWORD(i)                                         \
-  (newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^             \
-                  D1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \
-                  D2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^  \
-                  D3[block[(i + C3) % Nb] & 0xFF]))
-#define LASTWORD(i)                                                     \
-  (newstate[i] = (Sboxinv[(block[i] >> 24) & 0xFF] << 24) |             \
-                 (Sboxinv[(block[(i + C1) % Nb] >> 16) & 0xFF] << 16) | \
-                 (Sboxinv[(block[(i + C2) % Nb] >> 8) & 0xFF] << 8) |   \
-                 (Sboxinv[(block[(i + C3) % Nb]) & 0xFF]))
-
-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;
-  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);
-    MOVEWORD(0);
-    MOVEWORD(1);
-    MOVEWORD(2);
-    MOVEWORD(3);
-  }
-  ADD_ROUND_KEY_4;
-  LASTWORD(0);
-  LASTWORD(1);
-  LASTWORD(2);
-  LASTWORD(3);
-  MOVEWORD(0);
-  MOVEWORD(1);
-  MOVEWORD(2);
-  MOVEWORD(3);
-  ADD_ROUND_KEY_4;
-}
-
-void aes_decrypt_nb_6(CRYPT_aes_context* ctx, unsigned int* block) {
-  int i;
-  const int C1 = 6 - 1, C2 = 6 - 2, C3 = 6 - 3, Nb = 6;
-  unsigned int* keysched = ctx->invkeysched;
-  unsigned int newstate[6];
-  for (i = 0; i < ctx->Nr - 1; i++) {
-    ADD_ROUND_KEY_6;
-    MAKEWORD(0);
-    MAKEWORD(1);
-    MAKEWORD(2);
-    MAKEWORD(3);
-    MAKEWORD(4);
-    MAKEWORD(5);
-    MOVEWORD(0);
-    MOVEWORD(1);
-    MOVEWORD(2);
-    MOVEWORD(3);
-    MOVEWORD(4);
-    MOVEWORD(5);
-  }
-  ADD_ROUND_KEY_6;
-  LASTWORD(0);
-  LASTWORD(1);
-  LASTWORD(2);
-  LASTWORD(3);
-  LASTWORD(4);
-  LASTWORD(5);
-  MOVEWORD(0);
-  MOVEWORD(1);
-  MOVEWORD(2);
-  MOVEWORD(3);
-  MOVEWORD(4);
-  MOVEWORD(5);
-  ADD_ROUND_KEY_6;
-}
-
-void aes_decrypt_nb_8(CRYPT_aes_context* ctx, unsigned int* block) {
-  int i;
-  const int C1 = 8 - 1, C2 = 8 - 3, C3 = 8 - 4, Nb = 8;
-  unsigned int* keysched = ctx->invkeysched;
-  unsigned int newstate[8];
-  for (i = 0; i < ctx->Nr - 1; i++) {
-    ADD_ROUND_KEY_8;
-    MAKEWORD(0);
-    MAKEWORD(1);
-    MAKEWORD(2);
-    MAKEWORD(3);
-    MAKEWORD(4);
-    MAKEWORD(5);
-    MAKEWORD(6);
-    MAKEWORD(7);
-    MOVEWORD(0);
-    MOVEWORD(1);
-    MOVEWORD(2);
-    MOVEWORD(3);
-    MOVEWORD(4);
-    MOVEWORD(5);
-    MOVEWORD(6);
-    MOVEWORD(7);
-  }
-  ADD_ROUND_KEY_8;
-  LASTWORD(0);
-  LASTWORD(1);
-  LASTWORD(2);
-  LASTWORD(3);
-  LASTWORD(4);
-  LASTWORD(5);
-  LASTWORD(6);
-  LASTWORD(7);
-  MOVEWORD(0);
-  MOVEWORD(1);
-  MOVEWORD(2);
-  MOVEWORD(3);
-  MOVEWORD(4);
-  MOVEWORD(5);
-  MOVEWORD(6);
-  MOVEWORD(7);
-  ADD_ROUND_KEY_8;
-}
-#undef MAKEWORD
-#undef LASTWORD
-void aes_setup(CRYPT_aes_context* ctx,
-               int blocklen,
-               const unsigned char* key,
-               int keylen) {
-  int i, j, Nk, rconst;
-  ASSERT(blocklen == 16 || blocklen == 24 || blocklen == 32);
-  ASSERT(keylen == 16 || keylen == 24 || keylen == 32);
-  Nk = keylen / 4;
-  ctx->Nb = blocklen / 4;
-  ctx->Nr = 6 + (ctx->Nb > Nk ? ctx->Nb : Nk);
-  if (ctx->Nb == 8) {
-    ctx->encrypt = aes_encrypt_nb_8, ctx->decrypt = aes_decrypt_nb_8;
-  } else if (ctx->Nb == 6) {
-    ctx->encrypt = aes_encrypt_nb_6, ctx->decrypt = aes_decrypt_nb_6;
-  } else if (ctx->Nb == 4) {
-    ctx->encrypt = aes_encrypt_nb_4, ctx->decrypt = aes_decrypt_nb_4;
-  }
-  rconst = 1;
-  for (i = 0; i < (ctx->Nr + 1) * ctx->Nb; i++) {
-    if (i < Nk) {
-      ctx->keysched[i] = GET_32BIT_MSB_FIRST(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;
-        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;
-        temp = Sbox[a];
-        temp = (temp << 8) | Sbox[b];
-        temp = (temp << 8) | Sbox[c];
-        temp = (temp << 8) | Sbox[d];
-      }
-      ctx->keysched[i] = ctx->keysched[i - Nk] ^ temp;
-    }
-  }
-  for (i = 0; i <= ctx->Nr; i++) {
-    for (j = 0; j < ctx->Nb; j++) {
-      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;
-        temp = D0[Sbox[a]];
-        temp ^= D1[Sbox[b]];
-        temp ^= D2[Sbox[c]];
-        temp ^= D3[Sbox[d]];
-      }
-      ctx->invkeysched[i * ctx->Nb + j] = temp;
-    }
-  }
-}
-void aes_decrypt(CRYPT_aes_context* ctx, unsigned int* block) {
-  ctx->decrypt(ctx, block);
-}
-void aes_decrypt_cbc(unsigned char* dest,
-                     const unsigned char* src,
-                     int len,
-                     CRYPT_aes_context* ctx) {
-  unsigned int iv[4], x[4], ct[4];
-  int i;
-  ASSERT((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);
-    }
-    aes_decrypt(ctx, x);
-    for (i = 0; i < 4; i++) {
-      PUT_32BIT_MSB_FIRST(dest + 4 * i, iv[i] ^ x[i]);
-      iv[i] = ct[i];
-    }
-    dest += 16;
-    src += 16;
-    len -= 16;
-  }
-  memcpy(ctx->iv, iv, sizeof(iv));
-}
-
-void aes_encrypt(CRYPT_aes_context* ctx, unsigned int* block) {
-  ctx->encrypt(ctx, block);
-}
-
-void aes_encrypt_cbc(unsigned char* dest,
-                     const unsigned char* src,
-                     int len,
-                     CRYPT_aes_context* ctx) {
-  unsigned int iv[4];
-  int i;
-  ASSERT((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);
-    }
-    aes_encrypt(ctx, iv);
-    for (i = 0; i < 4; i++) {
-      PUT_32BIT_MSB_FIRST(dest + 4 * i, iv[i]);
-    }
-    dest += 16;
-    src += 16;
-    len -= 16;
-  }
-  memcpy(ctx->iv, iv, sizeof(iv));
-}
-
-}  // namespace
-
-void CRYPT_AESSetKey(CRYPT_aes_context* context,
-                     uint32_t blocklen,
-                     const uint8_t* key,
-                     uint32_t keylen,
-                     bool bEncrypt) {
-  aes_setup(context, blocklen, 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);
-}
-
-void CRYPT_AESDecrypt(CRYPT_aes_context* context,
-                      uint8_t* dest,
-                      const uint8_t* src,
-                      uint32_t len) {
-  aes_decrypt_cbc(dest, src, len, context);
-}
-
-void CRYPT_AESEncrypt(CRYPT_aes_context* context,
-                      uint8_t* dest,
-                      const uint8_t* src,
-                      uint32_t len) {
-  aes_encrypt_cbc(dest, src, len, context);
-}
diff --git a/core/fdrm/crypto/fx_crypt_sha.cpp b/core/fdrm/crypto/fx_crypt_sha.cpp
deleted file mode 100644
index 55b885d..0000000
--- a/core/fdrm/crypto/fx_crypt_sha.cpp
+++ /dev/null
@@ -1,636 +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/fdrm/crypto/fx_crypt.h"
-
-#define rol(x, y) (((x) << (y)) | (((unsigned int)x) >> (32 - y)))
-
-#define SHA_GET_UINT32(n, b, i)                                         \
-  {                                                                     \
-    (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \
-          ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]);     \
-  }
-#define SHA_PUT_UINT32(n, b, i)          \
-  {                                      \
-    (b)[(i)] = (uint8_t)((n) >> 24);     \
-    (b)[(i) + 1] = (uint8_t)((n) >> 16); \
-    (b)[(i) + 2] = (uint8_t)((n) >> 8);  \
-    (b)[(i) + 3] = (uint8_t)((n));       \
-  }
-
-#define SHA384_F0(x, y, z) ((x & y) | (z & (x | y)))
-#define SHA384_F1(x, y, z) (z ^ (x & (y ^ z)))
-#define SHA384_SHR(x, n) (x >> n)
-#define SHA384_ROTR(x, n) (SHA384_SHR(x, n) | x << (64 - n))
-#define SHA384_S0(x) (SHA384_ROTR(x, 1) ^ SHA384_ROTR(x, 8) ^ SHA384_SHR(x, 7))
-#define SHA384_S1(x) \
-  (SHA384_ROTR(x, 19) ^ SHA384_ROTR(x, 61) ^ SHA384_SHR(x, 6))
-#define SHA384_S2(x) \
-  (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_R(t) \
-  (W[t] = SHA384_S1(W[t - 2]) + W[t - 7] + SHA384_S0(W[t - 15]) + W[t - 16])
-
-#define GET_FX_64WORD(n, b, i)                                              \
-  {                                                                         \
-    (n) = ((uint64_t)(b)[(i)] << 56) | ((uint64_t)(b)[(i) + 1] << 48) |     \
-          ((uint64_t)(b)[(i) + 2] << 40) | ((uint64_t)(b)[(i) + 3] << 32) | \
-          ((uint64_t)(b)[(i) + 4] << 24) | ((uint64_t)(b)[(i) + 5] << 16) | \
-          ((uint64_t)(b)[(i) + 6] << 8) | ((uint64_t)(b)[(i) + 7]);         \
-  }
-#define PUT_UINT64(n, b, i)              \
-  {                                      \
-    (b)[(i)] = (uint8_t)((n) >> 56);     \
-    (b)[(i) + 1] = (uint8_t)((n) >> 48); \
-    (b)[(i) + 2] = (uint8_t)((n) >> 40); \
-    (b)[(i) + 3] = (uint8_t)((n) >> 32); \
-    (b)[(i) + 4] = (uint8_t)((n) >> 24); \
-    (b)[(i) + 5] = (uint8_t)((n) >> 16); \
-    (b)[(i) + 6] = (uint8_t)((n) >> 8);  \
-    (b)[(i) + 7] = (uint8_t)((n));       \
-  }
-
-#define SHR(x, n) ((x & 0xFFFFFFFF) >> n)
-#define ROTR(x, n) (SHR(x, n) | (x << (32 - n)))
-#define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
-#define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
-#define S2(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
-#define S3(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
-#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;                       \
-  }
-
-namespace {
-
-void SHA_Core_Init(unsigned int h[5]) {
-  h[0] = 0x67452301;
-  h[1] = 0xefcdab89;
-  h[2] = 0x98badcfe;
-  h[3] = 0x10325476;
-  h[4] = 0xc3d2e1f0;
-}
-
-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];
-  }
-  for (t = 16; t < 80; t++) {
-    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];
-  for (t = 0; t < 20; t++) {
-    unsigned int tmp = rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999;
-    e = d;
-    d = c;
-    c = rol(b, 30);
-    b = a;
-    a = tmp;
-  }
-  for (t = 20; t < 40; t++) {
-    unsigned int tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0x6ed9eba1;
-    e = d;
-    d = c;
-    c = rol(b, 30);
-    b = a;
-    a = tmp;
-  }
-  for (t = 40; t < 60; t++) {
-    unsigned int tmp =
-        rol(a, 5) + ((b & c) | (b & d) | (c & d)) + e + w[t] + 0x8f1bbcdc;
-    e = d;
-    d = c;
-    c = rol(b, 30);
-    b = a;
-    a = tmp;
-  }
-  for (t = 60; t < 80; t++) {
-    unsigned int tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0xca62c1d6;
-    e = d;
-    d = c;
-    c = rol(b, 30);
-    b = a;
-    a = tmp;
-  }
-  digest[0] += a;
-  digest[1] += b;
-  digest[2] += c;
-  digest[3] += d;
-  digest[4] += e;
-}
-
-void sha256_process(CRYPT_sha2_context* ctx, const uint8_t data[64]) {
-  uint32_t W[64];
-  SHA_GET_UINT32(W[0], data, 0);
-  SHA_GET_UINT32(W[1], data, 4);
-  SHA_GET_UINT32(W[2], data, 8);
-  SHA_GET_UINT32(W[3], data, 12);
-  SHA_GET_UINT32(W[4], data, 16);
-  SHA_GET_UINT32(W[5], data, 20);
-  SHA_GET_UINT32(W[6], data, 24);
-  SHA_GET_UINT32(W[7], data, 28);
-  SHA_GET_UINT32(W[8], data, 32);
-  SHA_GET_UINT32(W[9], data, 36);
-  SHA_GET_UINT32(W[10], data, 40);
-  SHA_GET_UINT32(W[11], data, 44);
-  SHA_GET_UINT32(W[12], data, 48);
-  SHA_GET_UINT32(W[13], data, 52);
-  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];
-  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);
-  PS(F, G, H, A, B, C, D, E, W[3], 0xE9B5DBA5);
-  PS(E, F, G, H, A, B, C, D, W[4], 0x3956C25B);
-  PS(D, E, F, G, H, A, B, C, W[5], 0x59F111F1);
-  PS(C, D, E, F, G, H, A, B, W[6], 0x923F82A4);
-  PS(B, C, D, E, F, G, H, A, W[7], 0xAB1C5ED5);
-  PS(A, B, C, D, E, F, G, H, W[8], 0xD807AA98);
-  PS(H, A, B, C, D, E, F, G, W[9], 0x12835B01);
-  PS(G, H, A, B, C, D, E, F, W[10], 0x243185BE);
-  PS(F, G, H, A, B, C, D, E, W[11], 0x550C7DC3);
-  PS(E, F, G, H, A, B, C, D, W[12], 0x72BE5D74);
-  PS(D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE);
-  PS(C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7);
-  PS(B, C, D, E, F, G, H, A, W[15], 0xC19BF174);
-  PS(A, B, C, D, E, F, G, H, R(16), 0xE49B69C1);
-  PS(H, A, B, C, D, E, F, G, R(17), 0xEFBE4786);
-  PS(G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6);
-  PS(F, G, H, A, B, C, D, E, R(19), 0x240CA1CC);
-  PS(E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F);
-  PS(D, E, F, G, H, A, B, C, R(21), 0x4A7484AA);
-  PS(C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC);
-  PS(B, C, D, E, F, G, H, A, R(23), 0x76F988DA);
-  PS(A, B, C, D, E, F, G, H, R(24), 0x983E5152);
-  PS(H, A, B, C, D, E, F, G, R(25), 0xA831C66D);
-  PS(G, H, A, B, C, D, E, F, R(26), 0xB00327C8);
-  PS(F, G, H, A, B, C, D, E, R(27), 0xBF597FC7);
-  PS(E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3);
-  PS(D, E, F, G, H, A, B, C, R(29), 0xD5A79147);
-  PS(C, D, E, F, G, H, A, B, R(30), 0x06CA6351);
-  PS(B, C, D, E, F, G, H, A, R(31), 0x14292967);
-  PS(A, B, C, D, E, F, G, H, R(32), 0x27B70A85);
-  PS(H, A, B, C, D, E, F, G, R(33), 0x2E1B2138);
-  PS(G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC);
-  PS(F, G, H, A, B, C, D, E, R(35), 0x53380D13);
-  PS(E, F, G, H, A, B, C, D, R(36), 0x650A7354);
-  PS(D, E, F, G, H, A, B, C, R(37), 0x766A0ABB);
-  PS(C, D, E, F, G, H, A, B, R(38), 0x81C2C92E);
-  PS(B, C, D, E, F, G, H, A, R(39), 0x92722C85);
-  PS(A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1);
-  PS(H, A, B, C, D, E, F, G, R(41), 0xA81A664B);
-  PS(G, H, A, B, C, D, E, F, R(42), 0xC24B8B70);
-  PS(F, G, H, A, B, C, D, E, R(43), 0xC76C51A3);
-  PS(E, F, G, H, A, B, C, D, R(44), 0xD192E819);
-  PS(D, E, F, G, H, A, B, C, R(45), 0xD6990624);
-  PS(C, D, E, F, G, H, A, B, R(46), 0xF40E3585);
-  PS(B, C, D, E, F, G, H, A, R(47), 0x106AA070);
-  PS(A, B, C, D, E, F, G, H, R(48), 0x19A4C116);
-  PS(H, A, B, C, D, E, F, G, R(49), 0x1E376C08);
-  PS(G, H, A, B, C, D, E, F, R(50), 0x2748774C);
-  PS(F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5);
-  PS(E, F, G, H, A, B, C, D, R(52), 0x391C0CB3);
-  PS(D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A);
-  PS(C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F);
-  PS(B, C, D, E, F, G, H, A, R(55), 0x682E6FF3);
-  PS(A, B, C, D, E, F, G, H, R(56), 0x748F82EE);
-  PS(H, A, B, C, D, E, F, G, R(57), 0x78A5636F);
-  PS(G, H, A, B, C, D, E, F, R(58), 0x84C87814);
-  PS(F, G, H, A, B, C, D, E, R(59), 0x8CC70208);
-  PS(E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA);
-  PS(D, E, F, G, H, A, B, C, R(61), 0xA4506CEB);
-  PS(C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7);
-  PS(B, C, D, E, F, G, H, A, R(63), 0xC67178F2);
-  ctx->state[0] += A;
-  ctx->state[1] += B;
-  ctx->state[2] += C;
-  ctx->state[3] += D;
-  ctx->state[4] += E;
-  ctx->state[5] += F;
-  ctx->state[6] += G;
-  ctx->state[7] += H;
-}
-
-const uint8_t sha256_padding[64] = {
-    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
-const uint8_t sha384_padding[128] = {
-    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
-uint64_t const constants[] = {
-    0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,
-    0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
-    0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,
-    0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
-    0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
-    0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
-    0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,
-    0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
-    0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,
-    0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
-    0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,
-    0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
-    0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,
-    0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
-    0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
-    0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
-    0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,
-    0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
-    0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,
-    0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
-    0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,
-    0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
-    0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,
-    0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
-    0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
-    0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
-    0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL,
-};
-
-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];
-  GET_FX_64WORD(W[0], data, 0);
-  GET_FX_64WORD(W[1], data, 8);
-  GET_FX_64WORD(W[2], data, 16);
-  GET_FX_64WORD(W[3], data, 24);
-  GET_FX_64WORD(W[4], data, 32);
-  GET_FX_64WORD(W[5], data, 40);
-  GET_FX_64WORD(W[6], data, 48);
-  GET_FX_64WORD(W[7], data, 56);
-  GET_FX_64WORD(W[8], data, 64);
-  GET_FX_64WORD(W[9], data, 72);
-  GET_FX_64WORD(W[10], data, 80);
-  GET_FX_64WORD(W[11], data, 88);
-  GET_FX_64WORD(W[12], data, 96);
-  GET_FX_64WORD(W[13], data, 104);
-  GET_FX_64WORD(W[14], data, 112);
-  GET_FX_64WORD(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];
-  for (int i = 0; i < 10; ++i) {
-    uint64_t temp[8];
-    if (i < 2) {
-      temp[0] = W[i * 8];
-      temp[1] = W[i * 8 + 1];
-      temp[2] = W[i * 8 + 2];
-      temp[3] = W[i * 8 + 3];
-      temp[4] = W[i * 8 + 4];
-      temp[5] = W[i * 8 + 5];
-      temp[6] = W[i * 8 + 6];
-      temp[7] = W[i * 8 + 7];
-    } else {
-      temp[0] = SHA384_R(i * 8);
-      temp[1] = SHA384_R(i * 8 + 1);
-      temp[2] = SHA384_R(i * 8 + 2);
-      temp[3] = SHA384_R(i * 8 + 3);
-      temp[4] = SHA384_R(i * 8 + 4);
-      temp[5] = SHA384_R(i * 8 + 5);
-      temp[6] = SHA384_R(i * 8 + 6);
-      temp[7] = SHA384_R(i * 8 + 7);
-    }
-    SHA384_P(A, B, C, D, E, F, G, H, temp[0], constants[i * 8]);
-    SHA384_P(H, A, B, C, D, E, F, G, temp[1], constants[i * 8 + 1]);
-    SHA384_P(G, H, A, B, C, D, E, F, temp[2], constants[i * 8 + 2]);
-    SHA384_P(F, G, H, A, B, C, D, E, temp[3], constants[i * 8 + 3]);
-    SHA384_P(E, F, G, H, A, B, C, D, temp[4], constants[i * 8 + 4]);
-    SHA384_P(D, E, F, G, H, A, B, C, temp[5], constants[i * 8 + 5]);
-    SHA384_P(C, D, E, F, G, H, A, B, temp[6], constants[i * 8 + 6]);
-    SHA384_P(B, C, D, E, F, G, H, A, temp[7], constants[i * 8 + 7]);
-  }
-  ctx->state[0] += A;
-  ctx->state[1] += B;
-  ctx->state[2] += C;
-  ctx->state[3] += D;
-  ctx->state[4] += E;
-  ctx->state[5] += F;
-  ctx->state[6] += G;
-  ctx->state[7] += H;
-}
-
-}  // namespace
-
-void CRYPT_SHA1Start(CRYPT_sha1_context* s) {
-  SHA_Core_Init(s->h);
-  s->blkused = 0;
-  s->lenhi = s->lenlo = 0;
-}
-
-void CRYPT_SHA1Update(CRYPT_sha1_context* s,
-                      const uint8_t* data,
-                      uint32_t size) {
-  unsigned char* q = (unsigned char*)data;
-  unsigned int wordblock[16];
-  int len = size;
-  unsigned int lenw = len;
-  int i;
-  s->lenlo += lenw;
-  s->lenhi += (s->lenlo < lenw);
-  if (s->blkused && s->blkused + len < 64) {
-    memcpy(s->block + s->blkused, q, len);
-    s->blkused += len;
-  } else {
-    while (s->blkused + len >= 64) {
-      memcpy(s->block + s->blkused, q, 64 - s->blkused);
-      q += 64 - s->blkused;
-      len -= 64 - s->blkused;
-      for (i = 0; i < 16; i++) {
-        wordblock[i] = (((unsigned int)s->block[i * 4 + 0]) << 24) |
-                       (((unsigned int)s->block[i * 4 + 1]) << 16) |
-                       (((unsigned int)s->block[i * 4 + 2]) << 8) |
-                       (((unsigned int)s->block[i * 4 + 3]) << 0);
-      }
-      SHATransform(s->h, wordblock);
-      s->blkused = 0;
-    }
-    memcpy(s->block, q, len);
-    s->blkused = len;
-  }
-}
-
-void CRYPT_SHA1Finish(CRYPT_sha1_context* s, uint8_t digest[20]) {
-  int i;
-  int pad;
-  unsigned char c[64];
-  unsigned int lenhi, lenlo;
-  if (s->blkused >= 56) {
-    pad = 56 + 64 - s->blkused;
-  } else {
-    pad = 56 - s->blkused;
-  }
-  lenhi = (s->lenhi << 3) | (s->lenlo >> (32 - 3));
-  lenlo = (s->lenlo << 3);
-  memset(c, 0, pad);
-  c[0] = 0x80;
-  CRYPT_SHA1Update(s, c, pad);
-  c[0] = (lenhi >> 24) & 0xFF;
-  c[1] = (lenhi >> 16) & 0xFF;
-  c[2] = (lenhi >> 8) & 0xFF;
-  c[3] = (lenhi >> 0) & 0xFF;
-  c[4] = (lenlo >> 24) & 0xFF;
-  c[5] = (lenlo >> 16) & 0xFF;
-  c[6] = (lenlo >> 8) & 0xFF;
-  c[7] = (lenlo >> 0) & 0xFF;
-  CRYPT_SHA1Update(s, c, 8);
-  for (i = 0; i < 5; i++) {
-    digest[i * 4] = (s->h[i] >> 24) & 0xFF;
-    digest[i * 4 + 1] = (s->h[i] >> 16) & 0xFF;
-    digest[i * 4 + 2] = (s->h[i] >> 8) & 0xFF;
-    digest[i * 4 + 3] = (s->h[i]) & 0xFF;
-  }
-}
-void CRYPT_SHA1Generate(const uint8_t* data,
-                        uint32_t size,
-                        uint8_t digest[20]) {
-  CRYPT_sha1_context s;
-  CRYPT_SHA1Start(&s);
-  CRYPT_SHA1Update(&s, data, size);
-  CRYPT_SHA1Finish(&s, digest);
-}
-void CRYPT_SHA256Start(CRYPT_sha2_context* ctx) {
-  ctx->total[0] = 0;
-  ctx->total[1] = 0;
-  ctx->state[0] = 0x6A09E667;
-  ctx->state[1] = 0xBB67AE85;
-  ctx->state[2] = 0x3C6EF372;
-  ctx->state[3] = 0xA54FF53A;
-  ctx->state[4] = 0x510E527F;
-  ctx->state[5] = 0x9B05688C;
-  ctx->state[6] = 0x1F83D9AB;
-  ctx->state[7] = 0x5BE0CD19;
-}
-
-void CRYPT_SHA256Update(CRYPT_sha2_context* ctx,
-                        const uint8_t* input,
-                        uint32_t length) {
-  if (!length)
-    return;
-
-  uint32_t left = ctx->total[0] & 0x3F;
-  uint32_t fill = 64 - left;
-  ctx->total[0] += length;
-  ctx->total[0] &= 0xFFFFFFFF;
-  if (ctx->total[0] < length)
-    ctx->total[1]++;
-
-  if (left && length >= fill) {
-    memcpy(ctx->buffer + left, input, fill);
-    sha256_process(ctx, ctx->buffer);
-    length -= fill;
-    input += fill;
-    left = 0;
-  }
-  while (length >= 64) {
-    sha256_process(ctx, input);
-    length -= 64;
-    input += 64;
-  }
-  if (length)
-    memcpy(ctx->buffer + left, input, length);
-}
-
-void CRYPT_SHA256Finish(CRYPT_sha2_context* ctx, uint8_t digest[32]) {
-  uint8_t msglen[8];
-  uint32_t high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
-  uint32_t low = (ctx->total[0] << 3);
-  SHA_PUT_UINT32(high, msglen, 0);
-  SHA_PUT_UINT32(low, msglen, 4);
-  uint32_t last = ctx->total[0] & 0x3F;
-  uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
-  CRYPT_SHA256Update(ctx, sha256_padding, padn);
-  CRYPT_SHA256Update(ctx, msglen, 8);
-  SHA_PUT_UINT32(ctx->state[0], digest, 0);
-  SHA_PUT_UINT32(ctx->state[1], digest, 4);
-  SHA_PUT_UINT32(ctx->state[2], digest, 8);
-  SHA_PUT_UINT32(ctx->state[3], digest, 12);
-  SHA_PUT_UINT32(ctx->state[4], digest, 16);
-  SHA_PUT_UINT32(ctx->state[5], digest, 20);
-  SHA_PUT_UINT32(ctx->state[6], digest, 24);
-  SHA_PUT_UINT32(ctx->state[7], digest, 28);
-}
-
-void CRYPT_SHA256Generate(const uint8_t* data,
-                          uint32_t size,
-                          uint8_t digest[32]) {
-  CRYPT_sha2_context ctx;
-  CRYPT_SHA256Start(&ctx);
-  CRYPT_SHA256Update(&ctx, data, size);
-  CRYPT_SHA256Finish(&ctx, digest);
-}
-
-void CRYPT_SHA384Start(CRYPT_sha2_context* ctx) {
-  if (!ctx)
-    return;
-
-  memset(ctx, 0, sizeof(CRYPT_sha2_context));
-  ctx->state[0] = 0xcbbb9d5dc1059ed8ULL;
-  ctx->state[1] = 0x629a292a367cd507ULL;
-  ctx->state[2] = 0x9159015a3070dd17ULL;
-  ctx->state[3] = 0x152fecd8f70e5939ULL;
-  ctx->state[4] = 0x67332667ffc00b31ULL;
-  ctx->state[5] = 0x8eb44a8768581511ULL;
-  ctx->state[6] = 0xdb0c2e0d64f98fa7ULL;
-  ctx->state[7] = 0x47b5481dbefa4fa4ULL;
-}
-
-void CRYPT_SHA384Update(CRYPT_sha2_context* ctx,
-                        const uint8_t* input,
-                        uint32_t length) {
-  if (!length)
-    return;
-
-  uint32_t left = static_cast<uint32_t>(ctx->total[0]) & 0x7F;
-  uint32_t fill = 128 - left;
-  ctx->total[0] += length;
-  if (ctx->total[0] < length)
-    ctx->total[1]++;
-
-  if (left && length >= fill) {
-    memcpy(ctx->buffer + left, input, fill);
-    sha384_process(ctx, ctx->buffer);
-    length -= fill;
-    input += fill;
-    left = 0;
-  }
-  while (length >= 128) {
-    sha384_process(ctx, input);
-    length -= 128;
-    input += 128;
-  }
-  if (length)
-    memcpy(ctx->buffer + left, input, length);
-}
-
-void CRYPT_SHA384Finish(CRYPT_sha2_context* ctx, uint8_t digest[48]) {
-  uint32_t last, padn;
-  uint8_t msglen[16];
-  memset(msglen, 0, 16);
-  uint64_t high, low;
-  high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
-  low = (ctx->total[0] << 3);
-  PUT_UINT64(high, msglen, 0);
-  PUT_UINT64(low, msglen, 8);
-  last = (uint32_t)ctx->total[0] & 0x7F;
-  padn = (last < 112) ? (112 - last) : (240 - last);
-  CRYPT_SHA384Update(ctx, sha384_padding, padn);
-  CRYPT_SHA384Update(ctx, msglen, 16);
-  PUT_UINT64(ctx->state[0], digest, 0);
-  PUT_UINT64(ctx->state[1], digest, 8);
-  PUT_UINT64(ctx->state[2], digest, 16);
-  PUT_UINT64(ctx->state[3], digest, 24);
-  PUT_UINT64(ctx->state[4], digest, 32);
-  PUT_UINT64(ctx->state[5], digest, 40);
-}
-
-void CRYPT_SHA384Generate(const uint8_t* data,
-                          uint32_t size,
-                          uint8_t digest[64]) {
-  CRYPT_sha2_context context;
-  CRYPT_SHA384Start(&context);
-  CRYPT_SHA384Update(&context, data, size);
-  CRYPT_SHA384Finish(&context, digest);
-}
-
-void CRYPT_SHA512Start(CRYPT_sha2_context* ctx) {
-  if (!ctx)
-    return;
-
-  memset(ctx, 0, sizeof(CRYPT_sha2_context));
-  ctx->state[0] = 0x6a09e667f3bcc908ULL;
-  ctx->state[1] = 0xbb67ae8584caa73bULL;
-  ctx->state[2] = 0x3c6ef372fe94f82bULL;
-  ctx->state[3] = 0xa54ff53a5f1d36f1ULL;
-  ctx->state[4] = 0x510e527fade682d1ULL;
-  ctx->state[5] = 0x9b05688c2b3e6c1fULL;
-  ctx->state[6] = 0x1f83d9abfb41bd6bULL;
-  ctx->state[7] = 0x5be0cd19137e2179ULL;
-}
-
-void CRYPT_SHA512Update(CRYPT_sha2_context* ctx,
-                        const uint8_t* data,
-                        uint32_t size) {
-  CRYPT_SHA384Update(ctx, data, size);
-}
-
-void CRYPT_SHA512Finish(CRYPT_sha2_context* ctx, uint8_t digest[64]) {
-  uint32_t last, padn;
-  uint8_t msglen[16];
-  memset(msglen, 0, 16);
-  uint64_t high, low;
-  high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
-  low = (ctx->total[0] << 3);
-  PUT_UINT64(high, msglen, 0);
-  PUT_UINT64(low, msglen, 8);
-  last = (uint32_t)ctx->total[0] & 0x7F;
-  padn = (last < 112) ? (112 - last) : (240 - last);
-  CRYPT_SHA512Update(ctx, sha384_padding, padn);
-  CRYPT_SHA512Update(ctx, msglen, 16);
-  PUT_UINT64(ctx->state[0], digest, 0);
-  PUT_UINT64(ctx->state[1], digest, 8);
-  PUT_UINT64(ctx->state[2], digest, 16);
-  PUT_UINT64(ctx->state[3], digest, 24);
-  PUT_UINT64(ctx->state[4], digest, 32);
-  PUT_UINT64(ctx->state[5], digest, 40);
-  PUT_UINT64(ctx->state[6], digest, 48);
-  PUT_UINT64(ctx->state[7], digest, 56);
-}
-
-void CRYPT_SHA512Generate(const uint8_t* data,
-                          uint32_t size,
-                          uint8_t digest[64]) {
-  CRYPT_sha2_context context;
-  CRYPT_SHA512Start(&context);
-  CRYPT_SHA512Update(&context, data, size);
-  CRYPT_SHA512Finish(&context, digest);
-}
diff --git a/core/fdrm/crypto/fx_crypt_unittest.cpp b/core/fdrm/crypto/fx_crypt_unittest.cpp
deleted file mode 100644
index e7f05b2..0000000
--- a/core/fdrm/crypto/fx_crypt_unittest.cpp
+++ /dev/null
@@ -1,503 +0,0 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Originally from chromium's /src/base/md5_unittest.cc.
-
-#include "core/fdrm/crypto/fx_crypt.h"
-
-#include <memory>
-#include <string>
-
-#include "core/fxcrt/fx_memory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-namespace {
-
-std::string CRYPT_MD5String(const char* str) {
-  return GenerateMD5Base16(reinterpret_cast<const uint8_t*>(str), strlen(str));
-}
-
-void CheckArcFourContext(const CRYPT_rc4_context& context,
-                         int32_t expected_x,
-                         int32_t expected_y,
-                         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)
-    EXPECT_EQ(expected_permutation[i], context.m[i]) << i;
-}
-
-}  // namespace
-
-TEST(FXCRYPT, CryptToBase16) {
-  uint8_t data[] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
-                    0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e};
-
-  std::string actual = CryptToBase16(data);
-  std::string expected = "d41d8cd98f00b204e9800998ecf8427e";
-
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, MD5GenerateEmtpyData) {
-  uint8_t digest[16];
-  const char data[] = "";
-  uint32_t length = static_cast<uint32_t>(strlen(data));
-
-  CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(data), length, digest);
-
-  uint8_t expected[] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
-                        0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e};
-
-  for (int i = 0; i < 16; ++i)
-    EXPECT_EQ(expected[i], digest[i]);
-}
-
-TEST(FXCRYPT, MD5GenerateOneByteData) {
-  uint8_t digest[16];
-  const char data[] = "a";
-  uint32_t length = static_cast<uint32_t>(strlen(data));
-
-  CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(data), length, digest);
-
-  uint8_t expected[] = {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
-                        0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61};
-
-  for (int i = 0; i < 16; ++i)
-    EXPECT_EQ(expected[i], digest[i]);
-}
-
-TEST(FXCRYPT, MD5GenerateLongData) {
-  const uint32_t length = 10 * 1024 * 1024 + 1;
-  std::unique_ptr<char[]> data(new char[length]);
-
-  for (uint32_t i = 0; i < length; ++i)
-    data[i] = i & 0xFF;
-
-  uint8_t digest[16];
-  CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(data.get()), length,
-                    digest);
-
-  uint8_t expected[] = {0x90, 0xbd, 0x6a, 0xd9, 0x0a, 0xce, 0xf5, 0xad,
-                        0xaa, 0x92, 0x20, 0x3e, 0x21, 0xc7, 0xa1, 0x3e};
-
-  for (int i = 0; i < 16; ++i)
-    EXPECT_EQ(expected[i], digest[i]);
-}
-
-TEST(FXCRYPT, ContextWithEmptyData) {
-  CRYPT_md5_context ctx;
-  CRYPT_MD5Start(&ctx);
-
-  uint8_t digest[16];
-  CRYPT_MD5Finish(&ctx, digest);
-
-  uint8_t expected[] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
-                        0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e};
-
-  for (int i = 0; i < 16; ++i)
-    EXPECT_EQ(expected[i], digest[i]);
-}
-
-TEST(FXCRYPT, ContextWithLongData) {
-  CRYPT_md5_context ctx;
-  CRYPT_MD5Start(&ctx);
-
-  const uint32_t length = 10 * 1024 * 1024 + 1;
-  std::unique_ptr<uint8_t[]> data(new uint8_t[length]);
-
-  for (uint32_t i = 0; i < length; ++i)
-    data[i] = i & 0xFF;
-
-  uint32_t total = 0;
-  while (total < length) {
-    uint32_t len = 4097;  // intentionally not 2^k.
-    if (len > length - total)
-      len = length - total;
-
-    CRYPT_MD5Update(&ctx, data.get() + total, len);
-    total += len;
-  }
-
-  EXPECT_EQ(length, total);
-
-  uint8_t digest[16];
-  CRYPT_MD5Finish(&ctx, digest);
-
-  uint8_t expected[] = {0x90, 0xbd, 0x6a, 0xd9, 0x0a, 0xce, 0xf5, 0xad,
-                        0xaa, 0x92, 0x20, 0x3e, 0x21, 0xc7, 0xa1, 0x3e};
-
-  for (int i = 0; i < 16; ++i)
-    EXPECT_EQ(expected[i], digest[i]);
-}
-
-// Example data from http://www.ietf.org/rfc/rfc1321.txt A.5 Test Suite
-TEST(FXCRYPT, MD5StringTestSuite1) {
-  std::string actual = CRYPT_MD5String("");
-  std::string expected = "d41d8cd98f00b204e9800998ecf8427e";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, MD5StringTestSuite2) {
-  std::string actual = CRYPT_MD5String("a");
-  std::string expected = "0cc175b9c0f1b6a831c399e269772661";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, MD5StringTestSuite3) {
-  std::string actual = CRYPT_MD5String("abc");
-  std::string expected = "900150983cd24fb0d6963f7d28e17f72";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, MD5StringTestSuite4) {
-  std::string actual = CRYPT_MD5String("message digest");
-  std::string expected = "f96b697d7cb7938d525a2f31aaf161d0";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, MD5StringTestSuite5) {
-  std::string actual = CRYPT_MD5String("abcdefghijklmnopqrstuvwxyz");
-  std::string expected = "c3fcd3d76192e4007dfb496cca67e13b";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, MD5StringTestSuite6) {
-  std::string actual = CRYPT_MD5String(
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-      "abcdefghijklmnopqrstuvwxyz"
-      "0123456789");
-  std::string expected = "d174ab98d277d9f5a5611c2c9f419d9f";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, MD5StringTestSuite7) {
-  std::string actual = CRYPT_MD5String(
-      "12345678901234567890"
-      "12345678901234567890"
-      "12345678901234567890"
-      "12345678901234567890");
-  std::string expected = "57edf4a22be3c955ac49da2e2107b67a";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, ContextWithStringData) {
-  CRYPT_md5_context ctx;
-  CRYPT_MD5Start(&ctx);
-  CRYPT_MD5Update(&ctx, reinterpret_cast<const uint8_t*>("abc"), 3);
-
-  uint8_t digest[16];
-  CRYPT_MD5Finish(&ctx, digest);
-
-  std::string actual = CryptToBase16(digest);
-  std::string expected = "900150983cd24fb0d6963f7d28e17f72";
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(FXCRYPT, Sha256TestB1) {
-  // Example B.1 from FIPS 180-2: one-block message.
-  const char* input = "abc";
-  const uint8_t expected[32] = {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
-                                0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
-                                0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
-                                0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
-  uint8_t actual[32];
-  CRYPT_SHA256Generate(reinterpret_cast<const uint8_t*>(input), strlen(input),
-                       actual);
-  for (size_t i = 0; i < 32; ++i)
-    EXPECT_EQ(expected[i], actual[i]) << " at byte " << i;
-}
-
-TEST(FXCRYPT, Sha256TestB2) {
-  // Example B.2 from FIPS 180-2: multi-block message.
-  const char* input =
-      "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
-  const uint8_t expected[32] = {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
-                                0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
-                                0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
-                                0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1};
-  uint8_t actual[32];
-  CRYPT_SHA256Generate(reinterpret_cast<const uint8_t*>(input), strlen(input),
-                       actual);
-  for (size_t i = 0; i < 32; ++i)
-    EXPECT_EQ(expected[i], actual[i]) << " at byte " << i;
-}
-
-TEST(FXCRYPT, CRYPT_ArcFourSetup) {
-  {
-    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};
-    CRYPT_rc4_context context;
-    CRYPT_ArcFourSetup(&context, nullptr, 0);
-    CheckArcFourContext(context, 0, 0, kNullPermutation);
-  }
-  {
-    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};
-    CRYPT_rc4_context context;
-    const uint8_t kFooBar[] = "foobar";
-    CRYPT_ArcFourSetup(&context, kFooBar, FX_ArraySize(kFooBar) - 1);
-    CheckArcFourContext(context, 0, 0, kFoobarPermutation);
-  }
-}
-
-TEST(FXCRYPT, CRYPT_ArcFourCrypt) {
-  const uint8_t kDataShort[] = "The Quick Fox Jumped Over The Lazy Brown Dog.";
-  const uint8_t kDataLong[] =
-      "The Quick Fox Jumped Over The Lazy Brown Dog.\n"
-      "1234567890123456789012345678901234567890123456789012345678901234567890\n"
-      "1234567890123456789012345678901234567890123456789012345678901234567890\n"
-      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
-      "!@#$%^&*()[]{};':\",.<>/?\\|\r\t\n";
-  {
-    CRYPT_rc4_context context;
-    CRYPT_ArcFourSetup(&context, nullptr, 0);
-
-    uint8_t data_short[FX_ArraySize(kDataShort)];
-    memcpy(data_short, kDataShort, FX_ArraySize(kDataShort));
-    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),
-        "data_short mismatch");
-    CRYPT_ArcFourCrypt(&context, data_short, FX_ArraySize(data_short));
-    for (size_t i = 0; i < FX_ArraySize(data_short); ++i)
-      EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i;
-
-    const uint8_t kPermutation[kRC4ContextPermutationLength] = {
-        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,
-        50,  132, 12,  160, 145, 250, 214, 76,  123, 35,  27,  249, 203, 127,
-        64,  62,  33,  60,  248, 85,  177, 6,   142, 83,  110, 140, 41,  135,
-        196, 238, 156, 91,  141, 67,  5,   185, 131, 63,  137, 43,  172, 121,
-        70,  134, 237, 130, 25,  44,  153, 166, 78,  201, 42,  119, 215, 7,
-        126, 114, 97,  11,  53,  4,   254, 45,  102, 133, 230, 88,  193, 129,
-        18,  124, 84,  108, 239, 189, 152, 120, 115, 207, 234, 176, 86,  157,
-        164, 187, 71,  1,   15,  58,  29,  21,  46,  23,  247, 162, 95,  229,
-        13,  226, 159, 175, 56,  100, 96,  202, 101, 178, 154, 47,  205, 106,
-        148, 104, 93,  112, 26,  165, 128, 246, 146, 218, 66,  211, 65,  90,
-        252, 19,  40,  49,  223, 174, 255, 51,  77,  227, 48,  220, 168, 118,
-        224, 98,  75,  105, 125, 199, 73,  82,  57,  181, 81,  173, 68,  52,
-        232, 22,  2,   216, 113, 30,  109, 163, 92,  61,  14,  36,  38,  225,
-        79,  231, 170, 240, 20,  219, 204, 161, 180, 188, 116, 190, 241, 197,
-        179, 87,  74,  147, 80,  54,  69,  16,  167, 222, 136, 245, 55,  182,
-        3,   24,  209, 251, 59,  28,  111, 89,  195, 155, 194, 107, 233, 34,
-        117, 184, 31,  39};
-    CheckArcFourContext(context, 46, 135, kPermutation);
-  }
-  {
-    CRYPT_rc4_context context;
-    CRYPT_ArcFourSetup(&context, nullptr, 0);
-
-    uint8_t data_long[FX_ArraySize(kDataLong)];
-    memcpy(data_long, kDataLong, FX_ArraySize(kDataLong));
-    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,
-        66,  182, 19,  255, 210, 181, 85,  69,  31,  240, 206, 171, 97,  62,
-        202, 172, 30,  246, 19,  43,  184, 0,   173, 27,  140, 90,  167, 240,
-        122, 125, 184, 49,  149, 71,  63,  104, 171, 144, 242, 106, 121, 124,
-        209, 149, 61,  1,   66,  186, 252, 47,  51,  170, 253, 75,  95,  41,
-        203, 28,  197, 174, 144, 209, 166, 98,  142, 125, 44,  5,   147, 42,
-        73,  178, 119, 90,  253, 69,  103, 178, 15,  136, 51,  112, 39,  81,
-        37,  111, 129, 232, 106, 159, 126, 142, 120, 124, 48,  140, 253, 12,
-        223, 208, 106, 76,  60,  238, 5,   162, 100, 226, 251, 156, 169, 35,
-        193, 10,  242, 210, 20,  96,  37,  84,  99,  183, 179, 203, 62,  122,
-        54,  6,   51,  239, 142, 250, 238, 41,  223, 58,  48,  101, 29,  187,
-        43,  235, 3,   5,   176, 33,  14,  171, 36,  26,  234, 207, 105, 79,
-        69,  126, 82,  183, 105, 228, 31,  173, 8,   240, 99,  5,   147, 206,
-        215, 140, 48,  190, 165, 50,  41,  232, 29,  105, 156, 64,  229, 165,
-        12,  64,  163, 255, 146, 108, 212, 125, 142, 101, 13,  99,  174, 10,
-        160, 68,  196, 120, 110, 201, 254, 158, 97,  215, 0,   207, 90,  23,
-        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");
-    CRYPT_ArcFourCrypt(&context, data_long, FX_ArraySize(data_long));
-    for (size_t i = 0; i < FX_ArraySize(data_long); ++i)
-      EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i;
-
-    const uint8_t kPermutation[kRC4ContextPermutationLength] = {
-        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,
-        104, 132, 12,  170, 205, 114, 7,   105, 37,  83,  78,  134, 236, 70,
-        197, 122, 177, 202, 39,  195, 30,  3,   86,  127, 74,  106, 68,  91,
-        110, 121, 208, 25,  56,  6,   28,  225, 163, 193, 166, 244, 119, 34,
-        23,  88,  108, 123, 162, 159, 242, 61,  230, 227, 254, 14,  4,   156,
-        161, 44,  58,  153, 33,  143, 129, 232, 182, 152, 76,  168, 238, 239,
-        185, 219, 233, 16,  188, 45,  40,  35,  103, 99,  89,  157, 241, 245,
-        192, 180, 248, 8,   85,  231, 146, 154, 252, 181, 107, 126, 98,  80,
-        102, 165, 199, 94,  49,  255, 18,  204, 216, 77,  20,  187, 145, 125,
-        1,   247, 79,  26,  207, 81,  117, 179, 186, 38,  175, 19,  139, 138,
-        149, 54,  64,  109, 249, 135, 142, 118, 17,  13,  201, 184, 55,  224,
-        209, 155, 113, 218, 82,  131, 178, 253, 140, 226, 43,  42,  24,  29,
-        229, 200, 137, 240, 203, 167, 95,  148, 15,  176, 60,  75,  53,  41,
-        150, 112, 160, 96,  22,  10,  234, 116, 130, 158, 214, 36,  9,   67,
-        198, 194, 191, 100, 124, 147, 32,  183, 120, 246, 51,  141, 46,  251,
-        92,  223, 133, 63,  0,   71,  48,  128, 220, 90,  62,  136, 2,   5,
-        69,  57,  151, 84};
-    CheckArcFourContext(context, 15, 222, kPermutation);
-  }
-  {
-    CRYPT_rc4_context context;
-    const uint8_t kFooBar[] = "foobar";
-    CRYPT_ArcFourSetup(&context, kFooBar, FX_ArraySize(kFooBar) - 1);
-
-    uint8_t data_short[FX_ArraySize(kDataShort)];
-    memcpy(data_short, kDataShort, FX_ArraySize(kDataShort));
-    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),
-        "data_short mismatch");
-    CRYPT_ArcFourCrypt(&context, data_short, FX_ArraySize(data_short));
-    for (size_t i = 0; i < FX_ArraySize(data_short); ++i)
-      EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i;
-
-    const uint8_t kPermutation[kRC4ContextPermutationLength] = {
-        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,
-        42,  81,  117, 56,  181, 252, 113, 210, 217, 62,  218, 129, 61,  33,
-        128, 9,   153, 59,  43,  13,  206, 48,  131, 18,  213, 118, 173, 122,
-        80,  172, 177, 105, 148, 207, 186, 5,   85,  32,  68,  215, 19,  84,
-        169, 209, 150, 7,   133, 63,  147, 93,  26,  130, 60,  145, 250, 57,
-        24,  247, 200, 127, 136, 66,  112, 107, 212, 154, 70,  170, 185, 138,
-        248, 236, 88,  86,  44,  216, 241, 35,  100, 151, 78,  74,  119, 55,
-        245, 46,  199, 208, 229, 16,  249, 149, 53,  157, 201, 234, 58,  28,
-        142, 238, 182, 163, 179, 144, 12,  114, 176, 10,  183, 239, 104, 40,
-        73,  101, 137, 69,  221, 134, 165, 188, 25,  87,  1,   91,  2,   171,
-        232, 34,  162, 166, 160, 126, 225, 167, 123, 197, 223, 195, 22,  203,
-        189, 244, 103, 139, 222, 175, 23,  143, 152, 192, 21,  231, 228, 132,
-        30,  204, 158, 240, 120, 98,  89,  121, 135, 251, 168, 4,   161, 3,
-        8,   230, 52,  219, 214, 242, 36,  97,  15,  155, 65,  187, 116, 76,
-        159, 67,  211, 20,  178, 146, 202, 11,  164, 226, 184, 50,  77,  174,
-        71,  233, 235, 198, 95,  51,  110, 255, 92,  72,  115, 106, 47,  94,
-        29,  39,  14,  111};
-    CheckArcFourContext(context, 46, 39, kPermutation);
-  }
-  {
-    CRYPT_rc4_context context;
-    const uint8_t kFooBar[] = "foobar";
-    CRYPT_ArcFourSetup(&context, kFooBar, FX_ArraySize(kFooBar) - 1);
-
-    uint8_t data_long[FX_ArraySize(kDataLong)];
-    memcpy(data_long, kDataLong, FX_ArraySize(kDataLong));
-    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,
-        240, 123, 192, 102, 174, 167, 105, 187, 202, 70,  121, 81,  17,  30,
-        5,   138, 116, 172, 169, 50,  160, 116, 237, 117, 108, 241, 127, 61,
-        83,  45,  77,  176, 0,   106, 191, 221, 132, 143, 219, 94,  2,   235,
-        204, 166, 201, 139, 140, 163, 104, 115, 48,  37,  18,  114, 168, 49,
-        235, 163, 179, 131, 182, 218, 120, 200, 9,   90,  60,  47,  55,  235,
-        135, 37,  21,  170, 48,  112, 185, 169, 43,  233, 88,  134, 117, 126,
-        248, 40,  176, 248, 30,  131, 108, 43,  139, 68,  232, 219, 7,   39,
-        223, 45,  199, 243, 54,  171, 31,  37,  161, 24,  38,  251, 13,  144,
-        106, 215, 179, 203, 5,   253, 25,  32,  25,  146, 109, 193, 143, 141,
-        177, 226, 134, 222, 95,  79,  156, 202, 240, 34,  153, 145, 169, 150,
-        231, 63,  113, 242, 156, 39,  136, 249, 108, 50,  181, 22,  22,  180,
-        57,  76,  69,  62,  254, 47,  141, 249, 235, 90,  25,  34,  40,  194,
-        66,  86,  110, 192, 235, 191, 205, 133, 91,  32,  104, 65,  43,  36,
-        140, 36,  228, 156, 105, 251, 169, 168, 203, 189, 238, 221, 64,  200,
-        68,  137, 153, 9,   183, 84,  153, 140, 239, 0,   15,  50,  126, 145,
-        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");
-    CRYPT_ArcFourCrypt(&context, data_long, FX_ArraySize(data_long));
-    for (size_t i = 0; i < FX_ArraySize(data_long); ++i)
-      EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i;
-
-    const uint8_t kPermutation[kRC4ContextPermutationLength] = {
-        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,
-        42,  45,  236, 56,  230, 194, 178, 213, 120, 116, 7,   164, 33,  107,
-        189, 20,  133, 114, 173, 161, 59,  128, 3,   238, 65,  69,  144, 179,
-        44,  35,  8,   163, 252, 195, 160, 197, 204, 28,  34,  129, 67,  89,
-        22,  149, 199, 131, 182, 46,  250, 222, 155, 104, 10,  32,  139, 245,
-        90,  41,  132, 224, 83,  242, 135, 75,  74,  61,  62,  141, 43,  127,
-        255, 91,  170, 78,  157, 101, 243, 216, 254, 156, 229, 118, 174, 147,
-        103, 76,  196, 145, 134, 94,  205, 146, 202, 98,  100, 106, 232, 177,
-        187, 13,  80,  137, 151, 11,  82,  40,  167, 175, 25,  219, 168, 240,
-        99,  55,  4,   19,  180, 2,   203, 18,  171, 154, 113, 117, 6,   185,
-        172, 186, 237, 223, 233, 244, 217, 191, 190, 198, 97,  165, 220, 9,
-        214, 150, 184, 143, 206, 24,  209, 207, 36,  142, 87,  15,  159, 71,
-        84,  162, 169, 86,  48,  47,  140, 215, 241, 235, 158, 14,  26,  248,
-        138, 119, 212, 39,  88,  121, 96,  109, 29,  66,  136, 102, 225, 92,
-        201, 126, 122, 192, 60,  0,   64,  239, 183, 37,  57,  63,  234, 181,
-        153, 52,  176, 112, 93,  79,  77,  115, 231, 30,  95,  251, 211, 68,
-        105, 85,  247, 152};
-    CheckArcFourContext(context, 15, 68, kPermutation);
-  }
-}
-
-TEST(FXCRYPT, Sha512Test) {
-  const char* const input =
-      "This is a simple test. To see whether it is getting correct value.";
-  const uint8_t expected[64] = {
-      0x86, 0xB5, 0x05, 0x63, 0xA2, 0x6F, 0xD6, 0xFA, 0xEB, 0x9B, 0xC3,
-      0xBB, 0x9E, 0xB7, 0x03, 0x82, 0xB6, 0x50, 0x55, 0x6B, 0x90, 0x69,
-      0xD0, 0xA7, 0x53, 0x0A, 0x34, 0xDD, 0xEA, 0x11, 0xCC, 0x91, 0x5C,
-      0xC7, 0x93, 0xCA, 0xAE, 0x30, 0xD1, 0x96, 0xBE, 0xD0, 0x35, 0x21,
-      0x4A, 0xC6, 0x42, 0x56, 0x0C, 0xA3, 0x00, 0x69, 0x44, 0x77, 0xCC,
-      0x3E, 0xD4, 0xD6, 0x10, 0x31, 0xC6, 0xC0, 0x58, 0xCF};
-  uint8_t actual[64];
-  CRYPT_SHA512Generate(reinterpret_cast<const uint8_t*>(input), strlen(input),
-                       actual);
-  for (size_t i = 0; i < 64; ++i)
-    EXPECT_EQ(expected[i], actual[i]) << " at byte " << i;
-}
diff --git a/core/fdrm/fx_crypt.cpp b/core/fdrm/fx_crypt.cpp
new file mode 100644
index 0000000..7cc0bc0
--- /dev/null
+++ b/core/fdrm/fx_crypt.cpp
@@ -0,0 +1,231 @@
+// 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/fdrm/fx_crypt.h"
+
+#include <utility>
+
+#define GET_UINT32(n, b, i)                            \
+  {                                                    \
+    (n) = (uint32_t)((uint8_t*)b)[(i)] |               \
+          (((uint32_t)((uint8_t*)b)[(i) + 1]) << 8) |  \
+          (((uint32_t)((uint8_t*)b)[(i) + 2]) << 16) | \
+          (((uint32_t)((uint8_t*)b)[(i) + 3]) << 24);  \
+  }
+#define PUT_UINT32(n, b, i)                                   \
+  {                                                           \
+    (((uint8_t*)b)[(i)]) = (uint8_t)(((n)) & 0xFF);           \
+    (((uint8_t*)b)[(i) + 1]) = (uint8_t)(((n) >> 8) & 0xFF);  \
+    (((uint8_t*)b)[(i) + 2]) = (uint8_t)(((n) >> 16) & 0xFF); \
+    (((uint8_t*)b)[(i) + 3]) = (uint8_t)(((n) >> 24) & 0xFF); \
+  }
+
+namespace {
+
+const uint8_t md5_padding[64] = {
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+void md5_process(CRYPT_md5_context* ctx, const uint8_t data[64]) {
+  uint32_t A, B, C, D, X[16];
+  GET_UINT32(X[0], data, 0);
+  GET_UINT32(X[1], data, 4);
+  GET_UINT32(X[2], data, 8);
+  GET_UINT32(X[3], data, 12);
+  GET_UINT32(X[4], data, 16);
+  GET_UINT32(X[5], data, 20);
+  GET_UINT32(X[6], data, 24);
+  GET_UINT32(X[7], data, 28);
+  GET_UINT32(X[8], data, 32);
+  GET_UINT32(X[9], data, 36);
+  GET_UINT32(X[10], data, 40);
+  GET_UINT32(X[11], data, 44);
+  GET_UINT32(X[12], data, 48);
+  GET_UINT32(X[13], data, 52);
+  GET_UINT32(X[14], data, 56);
+  GET_UINT32(X[15], data, 60);
+#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);
+  P(C, D, A, B, 2, 17, 0x242070DB);
+  P(B, C, D, A, 3, 22, 0xC1BDCEEE);
+  P(A, B, C, D, 4, 7, 0xF57C0FAF);
+  P(D, A, B, C, 5, 12, 0x4787C62A);
+  P(C, D, A, B, 6, 17, 0xA8304613);
+  P(B, C, D, A, 7, 22, 0xFD469501);
+  P(A, B, C, D, 8, 7, 0x698098D8);
+  P(D, A, B, C, 9, 12, 0x8B44F7AF);
+  P(C, D, A, B, 10, 17, 0xFFFF5BB1);
+  P(B, C, D, A, 11, 22, 0x895CD7BE);
+  P(A, B, C, D, 12, 7, 0x6B901122);
+  P(D, A, B, C, 13, 12, 0xFD987193);
+  P(C, D, A, B, 14, 17, 0xA679438E);
+  P(B, C, D, A, 15, 22, 0x49B40821);
+#undef F
+#define F(x, y, z) (y ^ (z & (x ^ y)))
+  P(A, B, C, D, 1, 5, 0xF61E2562);
+  P(D, A, B, C, 6, 9, 0xC040B340);
+  P(C, D, A, B, 11, 14, 0x265E5A51);
+  P(B, C, D, A, 0, 20, 0xE9B6C7AA);
+  P(A, B, C, D, 5, 5, 0xD62F105D);
+  P(D, A, B, C, 10, 9, 0x02441453);
+  P(C, D, A, B, 15, 14, 0xD8A1E681);
+  P(B, C, D, A, 4, 20, 0xE7D3FBC8);
+  P(A, B, C, D, 9, 5, 0x21E1CDE6);
+  P(D, A, B, C, 14, 9, 0xC33707D6);
+  P(C, D, A, B, 3, 14, 0xF4D50D87);
+  P(B, C, D, A, 8, 20, 0x455A14ED);
+  P(A, B, C, D, 13, 5, 0xA9E3E905);
+  P(D, A, B, C, 2, 9, 0xFCEFA3F8);
+  P(C, D, A, B, 7, 14, 0x676F02D9);
+  P(B, C, D, A, 12, 20, 0x8D2A4C8A);
+#undef F
+#define F(x, y, z) (x ^ y ^ z)
+  P(A, B, C, D, 5, 4, 0xFFFA3942);
+  P(D, A, B, C, 8, 11, 0x8771F681);
+  P(C, D, A, B, 11, 16, 0x6D9D6122);
+  P(B, C, D, A, 14, 23, 0xFDE5380C);
+  P(A, B, C, D, 1, 4, 0xA4BEEA44);
+  P(D, A, B, C, 4, 11, 0x4BDECFA9);
+  P(C, D, A, B, 7, 16, 0xF6BB4B60);
+  P(B, C, D, A, 10, 23, 0xBEBFBC70);
+  P(A, B, C, D, 13, 4, 0x289B7EC6);
+  P(D, A, B, C, 0, 11, 0xEAA127FA);
+  P(C, D, A, B, 3, 16, 0xD4EF3085);
+  P(B, C, D, A, 6, 23, 0x04881D05);
+  P(A, B, C, D, 9, 4, 0xD9D4D039);
+  P(D, A, B, C, 12, 11, 0xE6DB99E5);
+  P(C, D, A, B, 15, 16, 0x1FA27CF8);
+  P(B, C, D, A, 2, 23, 0xC4AC5665);
+#undef F
+#define F(x, y, z) (y ^ (x | ~z))
+  P(A, B, C, D, 0, 6, 0xF4292244);
+  P(D, A, B, C, 7, 10, 0x432AFF97);
+  P(C, D, A, B, 14, 15, 0xAB9423A7);
+  P(B, C, D, A, 5, 21, 0xFC93A039);
+  P(A, B, C, D, 12, 6, 0x655B59C3);
+  P(D, A, B, C, 3, 10, 0x8F0CCC92);
+  P(C, D, A, B, 10, 15, 0xFFEFF47D);
+  P(B, C, D, A, 1, 21, 0x85845DD1);
+  P(A, B, C, D, 8, 6, 0x6FA87E4F);
+  P(D, A, B, C, 15, 10, 0xFE2CE6E0);
+  P(C, D, A, B, 6, 15, 0xA3014314);
+  P(B, C, D, A, 13, 21, 0x4E0811A1);
+  P(A, B, C, D, 4, 6, 0xF7537E82);
+  P(D, A, B, C, 11, 10, 0xBD3AF235);
+  P(C, D, A, B, 2, 15, 0x2AD7D2BB);
+  P(B, C, D, A, 9, 21, 0xEB86D391);
+#undef F
+  ctx->state[0] += A;
+  ctx->state[1] += B;
+  ctx->state[2] += C;
+  ctx->state[3] += D;
+}
+
+}  // namespace
+
+void CRYPT_ArcFourSetup(CRYPT_rc4_context* context,
+                        pdfium::span<const uint8_t> key) {
+  context->x = 0;
+  context->y = 0;
+  for (int i = 0; i < kRC4ContextPermutationLength; ++i)
+    context->m[i] = i;
+
+  int j = 0;
+  for (int i = 0; i < kRC4ContextPermutationLength; ++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]);
+  }
+}
+
+void CRYPT_ArcFourCrypt(CRYPT_rc4_context* context,
+                        pdfium::span<uint8_t> data) {
+  for (auto& datum : data) {
+    context->x = (context->x + 1) & 0xFF;
+    context->y = (context->y + context->m[context->x]) & 0xFF;
+    std::swap(context->m[context->x], context->m[context->y]);
+    datum ^=
+        context->m[(context->m[context->x] + context->m[context->y]) & 0xFF];
+  }
+}
+
+void CRYPT_ArcFourCryptBlock(pdfium::span<uint8_t> data,
+                             pdfium::span<const uint8_t> key) {
+  CRYPT_rc4_context s;
+  CRYPT_ArcFourSetup(&s, key);
+  CRYPT_ArcFourCrypt(&s, data);
+}
+
+CRYPT_md5_context CRYPT_MD5Start() {
+  CRYPT_md5_context context;
+  context.total[0] = 0;
+  context.total[1] = 0;
+  context.state[0] = 0x67452301;
+  context.state[1] = 0xEFCDAB89;
+  context.state[2] = 0x98BADCFE;
+  context.state[3] = 0x10325476;
+  return context;
+}
+
+void CRYPT_MD5Update(CRYPT_md5_context* context,
+                     pdfium::span<const uint8_t> data) {
+  if (data.empty())
+    return;
+
+  uint32_t left = (context->total[0] >> 3) & 0x3F;
+  uint32_t fill = 64 - left;
+  context->total[0] += data.size() << 3;
+  context->total[1] += data.size() >> 29;
+  context->total[0] &= 0xFFFFFFFF;
+  context->total[1] += context->total[0] < data.size() << 3;
+  if (left && data.size() >= fill) {
+    auto next_data = data.subspan(fill);
+    memcpy(context->buffer + left, data.data(), fill);
+    md5_process(context, context->buffer);
+    left = 0;
+    data = next_data;
+  }
+  while (data.size() >= 64) {
+    auto next_data = data.subspan(64);
+    md5_process(context, data.data());
+    data = next_data;
+  }
+  size_t remaining = data.size();
+  if (remaining)
+    memcpy(context->buffer + left, data.data(), remaining);
+}
+
+void CRYPT_MD5Finish(CRYPT_md5_context* context, uint8_t digest[16]) {
+  uint8_t msglen[8];
+  PUT_UINT32(context->total[0], msglen, 0);
+  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, msglen);
+  PUT_UINT32(context->state[0], digest, 0);
+  PUT_UINT32(context->state[1], digest, 4);
+  PUT_UINT32(context->state[2], digest, 8);
+  PUT_UINT32(context->state[3], digest, 12);
+}
+
+void CRYPT_MD5Generate(pdfium::span<const uint8_t> data, uint8_t digest[16]) {
+  CRYPT_md5_context ctx = CRYPT_MD5Start();
+  CRYPT_MD5Update(&ctx, data);
+  CRYPT_MD5Finish(&ctx, digest);
+}
diff --git a/core/fdrm/fx_crypt.h b/core/fdrm/fx_crypt.h
new file mode 100644
index 0000000..f3a91f4
--- /dev/null
+++ b/core/fdrm/fx_crypt.h
@@ -0,0 +1,109 @@
+// 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_FDRM_FX_CRYPT_H_
+#define CORE_FDRM_FX_CRYPT_H_
+
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
+
+constexpr int32_t kRC4ContextPermutationLength = 256;
+struct CRYPT_rc4_context {
+  int32_t x;
+  int32_t y;
+  int32_t m[kRC4ContextPermutationLength];
+};
+
+#define MAX_NR 14
+#define MAX_NB 8
+struct CRYPT_aes_context {
+  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];
+};
+
+struct CRYPT_md5_context {
+  uint32_t total[2];
+  uint32_t state[4];
+  uint8_t buffer[64];
+};
+
+struct CRYPT_sha1_context {
+  uint64_t total_bytes;
+  uint32_t blkused;  // Constrained to [0, 64).
+  uint32_t h[5];
+  uint8_t block[64];
+};
+
+struct CRYPT_sha2_context {
+  uint64_t total_bytes;
+  uint64_t state[8];
+  uint8_t buffer[128];
+};
+
+void CRYPT_ArcFourCryptBlock(pdfium::span<uint8_t> data,
+                             pdfium::span<const uint8_t> key);
+void CRYPT_ArcFourSetup(CRYPT_rc4_context* context,
+                        pdfium::span<const uint8_t> key);
+void CRYPT_ArcFourCrypt(CRYPT_rc4_context* context, pdfium::span<uint8_t> data);
+
+void CRYPT_AESSetKey(CRYPT_aes_context* context,
+                     const uint8_t* key,
+                     uint32_t keylen,
+                     bool bEncrypt);
+void CRYPT_AESSetIV(CRYPT_aes_context* context, const uint8_t* iv);
+void CRYPT_AESDecrypt(CRYPT_aes_context* context,
+                      uint8_t* dest,
+                      const uint8_t* src,
+                      uint32_t size);
+void CRYPT_AESEncrypt(CRYPT_aes_context* context,
+                      uint8_t* dest,
+                      const uint8_t* src,
+                      uint32_t size);
+
+CRYPT_md5_context CRYPT_MD5Start();
+void CRYPT_MD5Update(CRYPT_md5_context* context,
+                     pdfium::span<const uint8_t> data);
+void CRYPT_MD5Finish(CRYPT_md5_context* context, uint8_t digest[16]);
+void CRYPT_MD5Generate(pdfium::span<const uint8_t> data, uint8_t digest[16]);
+
+void CRYPT_SHA1Start(CRYPT_sha1_context* context);
+void CRYPT_SHA1Update(CRYPT_sha1_context* context,
+                      const uint8_t* data,
+                      uint32_t size);
+void CRYPT_SHA1Finish(CRYPT_sha1_context* context, uint8_t digest[20]);
+void CRYPT_SHA1Generate(const uint8_t* data, uint32_t size, uint8_t digest[20]);
+
+void CRYPT_SHA256Start(CRYPT_sha2_context* context);
+void CRYPT_SHA256Update(CRYPT_sha2_context* context,
+                        const uint8_t* data,
+                        uint32_t size);
+void CRYPT_SHA256Finish(CRYPT_sha2_context* context, uint8_t digest[32]);
+void CRYPT_SHA256Generate(const uint8_t* data,
+                          uint32_t size,
+                          uint8_t digest[32]);
+
+void CRYPT_SHA384Start(CRYPT_sha2_context* context);
+void CRYPT_SHA384Update(CRYPT_sha2_context* context,
+                        const uint8_t* data,
+                        uint32_t size);
+void CRYPT_SHA384Finish(CRYPT_sha2_context* context, uint8_t digest[48]);
+void CRYPT_SHA384Generate(const uint8_t* data,
+                          uint32_t size,
+                          uint8_t digest[48]);
+
+void CRYPT_SHA512Start(CRYPT_sha2_context* context);
+void CRYPT_SHA512Update(CRYPT_sha2_context* context,
+                        const uint8_t* data,
+                        uint32_t size);
+void CRYPT_SHA512Finish(CRYPT_sha2_context* context, uint8_t digest[64]);
+void CRYPT_SHA512Generate(const uint8_t* data,
+                          uint32_t size,
+                          uint8_t digest[64]);
+
+#endif  // CORE_FDRM_FX_CRYPT_H_
diff --git a/core/fdrm/fx_crypt_aes.cpp b/core/fdrm/fx_crypt_aes.cpp
new file mode 100644
index 0000000..d4e446f
--- /dev/null
+++ b/core/fdrm/fx_crypt_aes.cpp
@@ -0,0 +1,655 @@
+// 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/fdrm/fx_crypt.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);                 \
+    (cp)[2] = (value) >> 8;            \
+    (cp)[1] = (value) >> 16;           \
+    (cp)[0] = (value) >> 24;           \
+  } while (0)
+
+namespace {
+
+const unsigned char Sbox[256] = {
+    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
+    0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
+    0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
+    0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
+    0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
+    0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
+    0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
+    0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
+    0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
+    0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
+    0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
+    0xb0, 0x54, 0xbb, 0x16};
+const unsigned char Sboxinv[256] = {
+    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
+    0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
+    0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
+    0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
+    0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
+    0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
+    0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
+    0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
+    0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
+    0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
+    0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
+    0x55, 0x21, 0x0c, 0x7d};
+const unsigned int E0[256] = {
+    0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd,
+    0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
+    0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d,
+    0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
+    0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7,
+    0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
+    0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4,
+    0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
+    0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1,
+    0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
+    0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e,
+    0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
+    0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e,
+    0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
+    0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46,
+    0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
+    0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7,
+    0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
+    0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe,
+    0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
+    0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a,
+    0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
+    0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2,
+    0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
+    0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e,
+    0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
+    0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256,
+    0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
+    0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4,
+    0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
+    0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa,
+    0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
+    0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1,
+    0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
+    0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42,
+    0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
+    0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158,
+    0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
+    0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22,
+    0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
+    0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631,
+    0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
+    0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
+};
+const unsigned int E1[256] = {
+    0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b,
+    0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b,
+    0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282,
+    0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
+    0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4,
+    0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,
+    0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5,
+    0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
+    0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696,
+    0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,
+    0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383,
+    0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
+    0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3,
+    0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded,
+    0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb,
+    0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
+    0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d,
+    0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f,
+    0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3,
+    0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
+    0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff,
+    0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,
+    0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7,
+    0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
+    0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a,
+    0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,
+    0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232,
+    0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
+    0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595,
+    0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,
+    0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656,
+    0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
+    0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6,
+    0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,
+    0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e,
+    0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
+    0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1,
+    0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,
+    0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e,
+    0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
+    0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6,
+    0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,
+    0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,
+};
+const unsigned int E2[256] = {
+    0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b,
+    0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b,
+    0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82,
+    0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
+    0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4,
+    0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,
+    0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5,
+    0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
+    0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796,
+    0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,
+    0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83,
+    0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
+    0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3,
+    0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed,
+    0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb,
+    0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
+    0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d,
+    0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f,
+    0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3,
+    0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
+    0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff,
+    0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec,
+    0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7,
+    0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
+    0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a,
+    0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,
+    0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432,
+    0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
+    0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195,
+    0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,
+    0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56,
+    0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
+    0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6,
+    0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,
+    0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e,
+    0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
+    0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1,
+    0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,
+    0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e,
+    0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
+    0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6,
+    0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f,
+    0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,
+};
+const unsigned int E3[256] = {
+    0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6,
+    0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56,
+    0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f,
+    0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
+    0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753,
+    0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,
+    0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451,
+    0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
+    0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137,
+    0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,
+    0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d,
+    0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
+    0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd,
+    0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1,
+    0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d,
+    0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
+    0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a,
+    0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe,
+    0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d,
+    0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
+    0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5,
+    0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3,
+    0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255,
+    0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
+    0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54,
+    0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,
+    0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664,
+    0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
+    0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431,
+    0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,
+    0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac,
+    0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
+    0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157,
+    0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,
+    0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c,
+    0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
+    0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899,
+    0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,
+    0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c,
+    0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
+    0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7,
+    0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e,
+    0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,
+};
+const unsigned int D0[256] = {
+    0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1,
+    0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
+    0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67,
+    0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
+    0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3,
+    0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
+    0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182,
+    0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
+    0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2,
+    0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
+    0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492,
+    0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
+    0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa,
+    0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
+    0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997,
+    0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
+    0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48,
+    0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,
+    0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f,
+    0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
+    0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad,
+    0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
+    0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc,
+    0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
+    0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3,
+    0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
+    0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1,
+    0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
+    0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8,
+    0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
+    0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4,
+    0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
+    0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331,
+    0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
+    0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d,
+    0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
+    0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252,
+    0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
+    0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f,
+    0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
+    0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c,
+    0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,
+    0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742,
+};
+const unsigned int D1[256] = {
+    0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45,
+    0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c,
+    0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b,
+    0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
+    0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421,
+    0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971,
+    0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31,
+    0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
+    0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02,
+    0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708,
+    0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4,
+    0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
+    0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef,
+    0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd,
+    0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9,
+    0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
+    0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed,
+    0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39,
+    0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7,
+    0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
+    0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7,
+    0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60,
+    0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6,
+    0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
+    0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230,
+    0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964,
+    0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1,
+    0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
+    0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8,
+    0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512,
+    0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918,
+    0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,
+    0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23,
+    0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8,
+    0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0,
+    0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
+    0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2,
+    0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c,
+    0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273,
+    0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
+    0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225,
+    0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1,
+    0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857,
+};
+const unsigned int D2[256] = {
+    0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d,
+    0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502,
+    0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba,
+    0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
+    0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874,
+    0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9,
+    0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a,
+    0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
+    0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b,
+    0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337,
+    0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779,
+    0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
+    0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060,
+    0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6,
+    0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd,
+    0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
+    0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b,
+    0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d,
+    0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357,
+    0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
+    0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b,
+    0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f,
+    0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23,
+    0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
+    0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2,
+    0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9,
+    0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938,
+    0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
+    0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8,
+    0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25,
+    0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59,
+    0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
+    0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f,
+    0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7,
+    0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef,
+    0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
+    0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db,
+    0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13,
+    0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2,
+    0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
+    0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2,
+    0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456,
+    0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8,
+};
+const unsigned int D3[256] = {
+    0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f,
+    0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5,
+    0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725,
+    0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
+    0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358,
+    0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27,
+    0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5,
+    0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,
+    0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272,
+    0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3,
+    0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7,
+    0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,
+    0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40,
+    0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d,
+    0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6,
+    0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
+    0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832,
+    0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736,
+    0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93,
+    0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
+    0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2,
+    0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3,
+    0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb,
+    0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
+    0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc,
+    0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247,
+    0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9,
+    0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
+    0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890,
+    0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf,
+    0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e,
+    0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
+    0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a,
+    0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533,
+    0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43,
+    0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
+    0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292,
+    0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb,
+    0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55,
+    0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
+    0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc,
+    0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064,
+    0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,
+};
+#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)                                         \
+  (newstate[i] = (E0[(block[i] >> 24) & 0xFF] ^             \
+                  E1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \
+                  E2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^  \
+                  E3[block[(i + C3) % Nb] & 0xFF]))
+#define LASTWORD(i)                                                  \
+  (newstate[i] = (Sbox[(block[i] >> 24) & 0xFF] << 24) |             \
+                 (Sbox[(block[(i + C1) % Nb] >> 16) & 0xFF] << 16) | \
+                 (Sbox[(block[(i + C2) % Nb] >> 8) & 0xFF] << 8) |   \
+                 (Sbox[(block[(i + C3) % Nb]) & 0xFF]))
+
+void aes_encrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) {
+  int i;
+  const int C1 = 1, C2 = 2, C3 = 3, 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);
+    MOVEWORD(0);
+    MOVEWORD(1);
+    MOVEWORD(2);
+    MOVEWORD(3);
+  }
+  ADD_ROUND_KEY_4;
+  LASTWORD(0);
+  LASTWORD(1);
+  LASTWORD(2);
+  LASTWORD(3);
+  MOVEWORD(0);
+  MOVEWORD(1);
+  MOVEWORD(2);
+  MOVEWORD(3);
+  ADD_ROUND_KEY_4;
+}
+#undef MAKEWORD
+#undef LASTWORD
+
+#define MAKEWORD(i)                                         \
+  (newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^             \
+                  D1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \
+                  D2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^  \
+                  D3[block[(i + C3) % Nb] & 0xFF]))
+#define LASTWORD(i)                                                     \
+  (newstate[i] = (Sboxinv[(block[i] >> 24) & 0xFF] << 24) |             \
+                 (Sboxinv[(block[(i + C1) % Nb] >> 16) & 0xFF] << 16) | \
+                 (Sboxinv[(block[(i + C2) % Nb] >> 8) & 0xFF] << 8) |   \
+                 (Sboxinv[(block[(i + C3) % Nb]) & 0xFF]))
+
+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;
+  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);
+    MOVEWORD(0);
+    MOVEWORD(1);
+    MOVEWORD(2);
+    MOVEWORD(3);
+  }
+  ADD_ROUND_KEY_4;
+  LASTWORD(0);
+  LASTWORD(1);
+  LASTWORD(2);
+  LASTWORD(3);
+  MOVEWORD(0);
+  MOVEWORD(1);
+  MOVEWORD(2);
+  MOVEWORD(3);
+  ADD_ROUND_KEY_4;
+}
+#undef MAKEWORD
+#undef LASTWORD
+
+void aes_setup(CRYPT_aes_context* ctx, const unsigned char* key, int keylen) {
+  ASSERT(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);
+    } 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;
+        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;
+        temp = Sbox[a];
+        temp = (temp << 8) | Sbox[b];
+        temp = (temp << 8) | Sbox[c];
+        temp = (temp << 8) | Sbox[d];
+      }
+      ctx->keysched[i] = ctx->keysched[i - Nk] ^ temp;
+    }
+  }
+  for (int i = 0; i <= ctx->Nr; i++) {
+    for (int j = 0; j < ctx->Nb; j++) {
+      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;
+        temp = D0[Sbox[a]];
+        temp ^= D1[Sbox[b]];
+        temp ^= D2[Sbox[c]];
+        temp ^= D3[Sbox[d]];
+      }
+      ctx->invkeysched[i * ctx->Nb + j] = temp;
+    }
+  }
+}
+
+void aes_decrypt(CRYPT_aes_context* ctx, unsigned int* block) {
+  aes_decrypt_nb_4(ctx, block);
+}
+
+void aes_decrypt_cbc(unsigned char* dest,
+                     const unsigned char* src,
+                     int len,
+                     CRYPT_aes_context* ctx) {
+  unsigned int iv[4], x[4], ct[4];
+  int i;
+  ASSERT((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);
+    }
+    aes_decrypt(ctx, x);
+    for (i = 0; i < 4; i++) {
+      PUT_32BIT_MSB_FIRST(dest + 4 * i, iv[i] ^ x[i]);
+      iv[i] = ct[i];
+    }
+    dest += 16;
+    src += 16;
+    len -= 16;
+  }
+  memcpy(ctx->iv, iv, sizeof(iv));
+}
+
+void aes_encrypt(CRYPT_aes_context* ctx, unsigned int* block) {
+  aes_encrypt_nb_4(ctx, block);
+}
+
+void aes_encrypt_cbc(unsigned char* dest,
+                     const unsigned char* src,
+                     int len,
+                     CRYPT_aes_context* ctx) {
+  unsigned int iv[4];
+  int i;
+  ASSERT((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);
+    }
+    aes_encrypt(ctx, iv);
+    for (i = 0; i < 4; i++) {
+      PUT_32BIT_MSB_FIRST(dest + 4 * i, iv[i]);
+    }
+    dest += 16;
+    src += 16;
+    len -= 16;
+  }
+  memcpy(ctx->iv, iv, sizeof(iv));
+}
+
+}  // namespace
+
+void CRYPT_AESSetKey(CRYPT_aes_context* context,
+                     const uint8_t* key,
+                     uint32_t keylen,
+                     bool bEncrypt) {
+  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);
+}
+
+void CRYPT_AESDecrypt(CRYPT_aes_context* context,
+                      uint8_t* dest,
+                      const uint8_t* src,
+                      uint32_t size) {
+  aes_decrypt_cbc(dest, src, size, context);
+}
+
+void CRYPT_AESEncrypt(CRYPT_aes_context* context,
+                      uint8_t* dest,
+                      const uint8_t* src,
+                      uint32_t size) {
+  aes_encrypt_cbc(dest, src, size, context);
+}
diff --git a/core/fdrm/fx_crypt_sha.cpp b/core/fdrm/fx_crypt_sha.cpp
new file mode 100644
index 0000000..0371685
--- /dev/null
+++ b/core/fdrm/fx_crypt_sha.cpp
@@ -0,0 +1,607 @@
+// 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/fdrm/fx_crypt.h"
+
+#define SHA_GET_UINT32(n, b, i)                                         \
+  {                                                                     \
+    (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \
+          ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]);     \
+  }
+#define SHA_PUT_UINT32(n, b, i)          \
+  {                                      \
+    (b)[(i)] = (uint8_t)((n) >> 24);     \
+    (b)[(i) + 1] = (uint8_t)((n) >> 16); \
+    (b)[(i) + 2] = (uint8_t)((n) >> 8);  \
+    (b)[(i) + 3] = (uint8_t)((n));       \
+  }
+#define SHA_GET_UINT64(n, b, i)                                             \
+  {                                                                         \
+    (n) = ((uint64_t)(b)[(i)] << 56) | ((uint64_t)(b)[(i) + 1] << 48) |     \
+          ((uint64_t)(b)[(i) + 2] << 40) | ((uint64_t)(b)[(i) + 3] << 32) | \
+          ((uint64_t)(b)[(i) + 4] << 24) | ((uint64_t)(b)[(i) + 5] << 16) | \
+          ((uint64_t)(b)[(i) + 6] << 8) | ((uint64_t)(b)[(i) + 7]);         \
+  }
+#define SHA_PUT_UINT64(n, b, i)          \
+  {                                      \
+    (b)[(i)] = (uint8_t)((n) >> 56);     \
+    (b)[(i) + 1] = (uint8_t)((n) >> 48); \
+    (b)[(i) + 2] = (uint8_t)((n) >> 40); \
+    (b)[(i) + 3] = (uint8_t)((n) >> 32); \
+    (b)[(i) + 4] = (uint8_t)((n) >> 24); \
+    (b)[(i) + 5] = (uint8_t)((n) >> 16); \
+    (b)[(i) + 6] = (uint8_t)((n) >> 8);  \
+    (b)[(i) + 7] = (uint8_t)((n));       \
+  }
+
+#define SHA384_F0(x, y, z) ((x & y) | (z & (x | y)))
+#define SHA384_F1(x, y, z) (z ^ (x & (y ^ z)))
+#define SHA384_SHR(x, n) (x >> n)
+#define SHA384_ROTR(x, n) (SHA384_SHR(x, n) | x << (64 - n))
+#define SHA384_S0(x) (SHA384_ROTR(x, 1) ^ SHA384_ROTR(x, 8) ^ SHA384_SHR(x, 7))
+#define SHA384_S1(x) \
+  (SHA384_ROTR(x, 19) ^ SHA384_ROTR(x, 61) ^ SHA384_SHR(x, 6))
+#define SHA384_S2(x) \
+  (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_R(t) \
+  (W[t] = SHA384_S1(W[t - 2]) + W[t - 7] + SHA384_S0(W[t - 15]) + W[t - 16])
+
+#define rol(x, y) (((x) << (y)) | (((unsigned int)x) >> (32 - y)))
+#define SHR(x, n) ((x & 0xFFFFFFFF) >> n)
+#define ROTR(x, n) (SHR(x, n) | (x << (32 - n)))
+#define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
+#define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
+#define S2(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define S3(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#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;                       \
+  }
+
+namespace {
+
+void SHA_Core_Init(unsigned int h[5]) {
+  h[0] = 0x67452301;
+  h[1] = 0xefcdab89;
+  h[2] = 0x98badcfe;
+  h[3] = 0x10325476;
+  h[4] = 0xc3d2e1f0;
+}
+
+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];
+  }
+  for (t = 16; t < 80; t++) {
+    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];
+  for (t = 0; t < 20; t++) {
+    unsigned int tmp = rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999;
+    e = d;
+    d = c;
+    c = rol(b, 30);
+    b = a;
+    a = tmp;
+  }
+  for (t = 20; t < 40; t++) {
+    unsigned int tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0x6ed9eba1;
+    e = d;
+    d = c;
+    c = rol(b, 30);
+    b = a;
+    a = tmp;
+  }
+  for (t = 40; t < 60; t++) {
+    unsigned int tmp =
+        rol(a, 5) + ((b & c) | (b & d) | (c & d)) + e + w[t] + 0x8f1bbcdc;
+    e = d;
+    d = c;
+    c = rol(b, 30);
+    b = a;
+    a = tmp;
+  }
+  for (t = 60; t < 80; t++) {
+    unsigned int tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0xca62c1d6;
+    e = d;
+    d = c;
+    c = rol(b, 30);
+    b = a;
+    a = tmp;
+  }
+  digest[0] += a;
+  digest[1] += b;
+  digest[2] += c;
+  digest[3] += d;
+  digest[4] += e;
+}
+
+void sha256_process(CRYPT_sha2_context* ctx, const uint8_t data[64]) {
+  uint32_t W[64];
+  SHA_GET_UINT32(W[0], data, 0);
+  SHA_GET_UINT32(W[1], data, 4);
+  SHA_GET_UINT32(W[2], data, 8);
+  SHA_GET_UINT32(W[3], data, 12);
+  SHA_GET_UINT32(W[4], data, 16);
+  SHA_GET_UINT32(W[5], data, 20);
+  SHA_GET_UINT32(W[6], data, 24);
+  SHA_GET_UINT32(W[7], data, 28);
+  SHA_GET_UINT32(W[8], data, 32);
+  SHA_GET_UINT32(W[9], data, 36);
+  SHA_GET_UINT32(W[10], data, 40);
+  SHA_GET_UINT32(W[11], data, 44);
+  SHA_GET_UINT32(W[12], data, 48);
+  SHA_GET_UINT32(W[13], data, 52);
+  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];
+  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);
+  PS(F, G, H, A, B, C, D, E, W[3], 0xE9B5DBA5);
+  PS(E, F, G, H, A, B, C, D, W[4], 0x3956C25B);
+  PS(D, E, F, G, H, A, B, C, W[5], 0x59F111F1);
+  PS(C, D, E, F, G, H, A, B, W[6], 0x923F82A4);
+  PS(B, C, D, E, F, G, H, A, W[7], 0xAB1C5ED5);
+  PS(A, B, C, D, E, F, G, H, W[8], 0xD807AA98);
+  PS(H, A, B, C, D, E, F, G, W[9], 0x12835B01);
+  PS(G, H, A, B, C, D, E, F, W[10], 0x243185BE);
+  PS(F, G, H, A, B, C, D, E, W[11], 0x550C7DC3);
+  PS(E, F, G, H, A, B, C, D, W[12], 0x72BE5D74);
+  PS(D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE);
+  PS(C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7);
+  PS(B, C, D, E, F, G, H, A, W[15], 0xC19BF174);
+  PS(A, B, C, D, E, F, G, H, R(16), 0xE49B69C1);
+  PS(H, A, B, C, D, E, F, G, R(17), 0xEFBE4786);
+  PS(G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6);
+  PS(F, G, H, A, B, C, D, E, R(19), 0x240CA1CC);
+  PS(E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F);
+  PS(D, E, F, G, H, A, B, C, R(21), 0x4A7484AA);
+  PS(C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC);
+  PS(B, C, D, E, F, G, H, A, R(23), 0x76F988DA);
+  PS(A, B, C, D, E, F, G, H, R(24), 0x983E5152);
+  PS(H, A, B, C, D, E, F, G, R(25), 0xA831C66D);
+  PS(G, H, A, B, C, D, E, F, R(26), 0xB00327C8);
+  PS(F, G, H, A, B, C, D, E, R(27), 0xBF597FC7);
+  PS(E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3);
+  PS(D, E, F, G, H, A, B, C, R(29), 0xD5A79147);
+  PS(C, D, E, F, G, H, A, B, R(30), 0x06CA6351);
+  PS(B, C, D, E, F, G, H, A, R(31), 0x14292967);
+  PS(A, B, C, D, E, F, G, H, R(32), 0x27B70A85);
+  PS(H, A, B, C, D, E, F, G, R(33), 0x2E1B2138);
+  PS(G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC);
+  PS(F, G, H, A, B, C, D, E, R(35), 0x53380D13);
+  PS(E, F, G, H, A, B, C, D, R(36), 0x650A7354);
+  PS(D, E, F, G, H, A, B, C, R(37), 0x766A0ABB);
+  PS(C, D, E, F, G, H, A, B, R(38), 0x81C2C92E);
+  PS(B, C, D, E, F, G, H, A, R(39), 0x92722C85);
+  PS(A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1);
+  PS(H, A, B, C, D, E, F, G, R(41), 0xA81A664B);
+  PS(G, H, A, B, C, D, E, F, R(42), 0xC24B8B70);
+  PS(F, G, H, A, B, C, D, E, R(43), 0xC76C51A3);
+  PS(E, F, G, H, A, B, C, D, R(44), 0xD192E819);
+  PS(D, E, F, G, H, A, B, C, R(45), 0xD6990624);
+  PS(C, D, E, F, G, H, A, B, R(46), 0xF40E3585);
+  PS(B, C, D, E, F, G, H, A, R(47), 0x106AA070);
+  PS(A, B, C, D, E, F, G, H, R(48), 0x19A4C116);
+  PS(H, A, B, C, D, E, F, G, R(49), 0x1E376C08);
+  PS(G, H, A, B, C, D, E, F, R(50), 0x2748774C);
+  PS(F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5);
+  PS(E, F, G, H, A, B, C, D, R(52), 0x391C0CB3);
+  PS(D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A);
+  PS(C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F);
+  PS(B, C, D, E, F, G, H, A, R(55), 0x682E6FF3);
+  PS(A, B, C, D, E, F, G, H, R(56), 0x748F82EE);
+  PS(H, A, B, C, D, E, F, G, R(57), 0x78A5636F);
+  PS(G, H, A, B, C, D, E, F, R(58), 0x84C87814);
+  PS(F, G, H, A, B, C, D, E, R(59), 0x8CC70208);
+  PS(E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA);
+  PS(D, E, F, G, H, A, B, C, R(61), 0xA4506CEB);
+  PS(C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7);
+  PS(B, C, D, E, F, G, H, A, R(63), 0xC67178F2);
+  ctx->state[0] += A;
+  ctx->state[1] += B;
+  ctx->state[2] += C;
+  ctx->state[3] += D;
+  ctx->state[4] += E;
+  ctx->state[5] += F;
+  ctx->state[6] += G;
+  ctx->state[7] += H;
+}
+
+const uint8_t sha256_padding[64] = {
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const uint8_t sha384_padding[128] = {
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+uint64_t const constants[] = {
+    0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,
+    0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+    0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,
+    0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+    0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
+    0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+    0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,
+    0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+    0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,
+    0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+    0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,
+    0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+    0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,
+    0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+    0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
+    0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+    0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,
+    0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+    0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,
+    0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+    0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,
+    0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+    0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,
+    0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+    0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
+    0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+    0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL,
+};
+
+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);
+  SHA_GET_UINT64(W[2], data, 16);
+  SHA_GET_UINT64(W[3], data, 24);
+  SHA_GET_UINT64(W[4], data, 32);
+  SHA_GET_UINT64(W[5], data, 40);
+  SHA_GET_UINT64(W[6], data, 48);
+  SHA_GET_UINT64(W[7], data, 56);
+  SHA_GET_UINT64(W[8], data, 64);
+  SHA_GET_UINT64(W[9], data, 72);
+  SHA_GET_UINT64(W[10], data, 80);
+  SHA_GET_UINT64(W[11], data, 88);
+  SHA_GET_UINT64(W[12], data, 96);
+  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];
+  for (int i = 0; i < 10; ++i) {
+    uint64_t temp[8];
+    if (i < 2) {
+      temp[0] = W[i * 8];
+      temp[1] = W[i * 8 + 1];
+      temp[2] = W[i * 8 + 2];
+      temp[3] = W[i * 8 + 3];
+      temp[4] = W[i * 8 + 4];
+      temp[5] = W[i * 8 + 5];
+      temp[6] = W[i * 8 + 6];
+      temp[7] = W[i * 8 + 7];
+    } else {
+      temp[0] = SHA384_R(i * 8);
+      temp[1] = SHA384_R(i * 8 + 1);
+      temp[2] = SHA384_R(i * 8 + 2);
+      temp[3] = SHA384_R(i * 8 + 3);
+      temp[4] = SHA384_R(i * 8 + 4);
+      temp[5] = SHA384_R(i * 8 + 5);
+      temp[6] = SHA384_R(i * 8 + 6);
+      temp[7] = SHA384_R(i * 8 + 7);
+    }
+    SHA384_P(A, B, C, D, E, F, G, H, temp[0], constants[i * 8]);
+    SHA384_P(H, A, B, C, D, E, F, G, temp[1], constants[i * 8 + 1]);
+    SHA384_P(G, H, A, B, C, D, E, F, temp[2], constants[i * 8 + 2]);
+    SHA384_P(F, G, H, A, B, C, D, E, temp[3], constants[i * 8 + 3]);
+    SHA384_P(E, F, G, H, A, B, C, D, temp[4], constants[i * 8 + 4]);
+    SHA384_P(D, E, F, G, H, A, B, C, temp[5], constants[i * 8 + 5]);
+    SHA384_P(C, D, E, F, G, H, A, B, temp[6], constants[i * 8 + 6]);
+    SHA384_P(B, C, D, E, F, G, H, A, temp[7], constants[i * 8 + 7]);
+  }
+  ctx->state[0] += A;
+  ctx->state[1] += B;
+  ctx->state[2] += C;
+  ctx->state[3] += D;
+  ctx->state[4] += E;
+  ctx->state[5] += F;
+  ctx->state[6] += G;
+  ctx->state[7] += H;
+}
+
+}  // namespace
+
+void CRYPT_SHA1Start(CRYPT_sha1_context* context) {
+  SHA_Core_Init(context->h);
+  context->total_bytes = 0;
+  context->blkused = 0;
+}
+
+void CRYPT_SHA1Update(CRYPT_sha1_context* context,
+                      const uint8_t* data,
+                      uint32_t size) {
+  context->total_bytes += size;
+  if (context->blkused && size < 64 - context->blkused) {
+    memcpy(context->block + context->blkused, data, size);
+    context->blkused += size;
+    return;
+  }
+  uint32_t wordblock[16];
+  while (size >= 64 - context->blkused) {
+    memcpy(context->block + context->blkused, data, 64 - context->blkused);
+    data += 64 - context->blkused;
+    size -= 64 - context->blkused;
+    for (int i = 0; i < 16; i++) {
+      wordblock[i] = (((uint32_t)context->block[i * 4 + 0]) << 24) |
+                     (((uint32_t)context->block[i * 4 + 1]) << 16) |
+                     (((uint32_t)context->block[i * 4 + 2]) << 8) |
+                     (((uint32_t)context->block[i * 4 + 3]) << 0);
+    }
+    SHATransform(context->h, wordblock);
+    context->blkused = 0;
+  }
+  memcpy(context->block, data, size);
+  context->blkused = size;
+}
+
+void CRYPT_SHA1Finish(CRYPT_sha1_context* context, uint8_t digest[20]) {
+  uint64_t total_bits = 8 * context->total_bytes;  // Prior to padding.
+  uint8_t c[64];
+  uint8_t pad;
+  if (context->blkused >= 56) {
+    pad = 56 + 64 - context->blkused;
+  } else {
+    pad = 56 - context->blkused;
+  }
+  memset(c, 0, pad);
+  c[0] = 0x80;
+  CRYPT_SHA1Update(context, c, pad);
+  c[0] = (total_bits >> 56) & 0xFF;
+  c[1] = (total_bits >> 48) & 0xFF;
+  c[2] = (total_bits >> 40) & 0xFF;
+  c[3] = (total_bits >> 32) & 0xFF;
+  c[4] = (total_bits >> 24) & 0xFF;
+  c[5] = (total_bits >> 16) & 0xFF;
+  c[6] = (total_bits >> 8) & 0xFF;
+  c[7] = (total_bits >> 0) & 0xFF;
+  CRYPT_SHA1Update(context, c, 8);
+  for (int i = 0; i < 5; i++) {
+    digest[i * 4] = (context->h[i] >> 24) & 0xFF;
+    digest[i * 4 + 1] = (context->h[i] >> 16) & 0xFF;
+    digest[i * 4 + 2] = (context->h[i] >> 8) & 0xFF;
+    digest[i * 4 + 3] = (context->h[i]) & 0xFF;
+  }
+}
+
+void CRYPT_SHA1Generate(const uint8_t* data,
+                        uint32_t size,
+                        uint8_t digest[20]) {
+  CRYPT_sha1_context s;
+  CRYPT_SHA1Start(&s);
+  CRYPT_SHA1Update(&s, data, size);
+  CRYPT_SHA1Finish(&s, digest);
+}
+
+void CRYPT_SHA256Start(CRYPT_sha2_context* context) {
+  context->total_bytes = 0;
+  context->state[0] = 0x6A09E667;
+  context->state[1] = 0xBB67AE85;
+  context->state[2] = 0x3C6EF372;
+  context->state[3] = 0xA54FF53A;
+  context->state[4] = 0x510E527F;
+  context->state[5] = 0x9B05688C;
+  context->state[6] = 0x1F83D9AB;
+  context->state[7] = 0x5BE0CD19;
+  memset(context->buffer, 0, sizeof(context->buffer));
+}
+
+void CRYPT_SHA256Update(CRYPT_sha2_context* context,
+                        const uint8_t* data,
+                        uint32_t size) {
+  if (!size)
+    return;
+
+  uint32_t left = context->total_bytes & 0x3F;
+  uint32_t fill = 64 - left;
+  context->total_bytes += size;
+  if (left && size >= fill) {
+    memcpy(context->buffer + left, data, fill);
+    sha256_process(context, context->buffer);
+    size -= fill;
+    data += fill;
+    left = 0;
+  }
+  while (size >= 64) {
+    sha256_process(context, data);
+    size -= 64;
+    data += 64;
+  }
+  if (size)
+    memcpy(context->buffer + left, data, size);
+}
+
+void CRYPT_SHA256Finish(CRYPT_sha2_context* context, uint8_t digest[32]) {
+  uint8_t msglen[8];
+  uint64_t total_bits = 8 * context->total_bytes;  // Prior to padding.
+  SHA_PUT_UINT64(total_bits, msglen, 0);
+  uint32_t last = context->total_bytes & 0x3F;
+  uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
+  CRYPT_SHA256Update(context, sha256_padding, padn);
+  CRYPT_SHA256Update(context, msglen, 8);
+  SHA_PUT_UINT32(context->state[0], digest, 0);
+  SHA_PUT_UINT32(context->state[1], digest, 4);
+  SHA_PUT_UINT32(context->state[2], digest, 8);
+  SHA_PUT_UINT32(context->state[3], digest, 12);
+  SHA_PUT_UINT32(context->state[4], digest, 16);
+  SHA_PUT_UINT32(context->state[5], digest, 20);
+  SHA_PUT_UINT32(context->state[6], digest, 24);
+  SHA_PUT_UINT32(context->state[7], digest, 28);
+}
+
+void CRYPT_SHA256Generate(const uint8_t* data,
+                          uint32_t size,
+                          uint8_t digest[32]) {
+  CRYPT_sha2_context ctx;
+  CRYPT_SHA256Start(&ctx);
+  CRYPT_SHA256Update(&ctx, data, size);
+  CRYPT_SHA256Finish(&ctx, digest);
+}
+
+void CRYPT_SHA384Start(CRYPT_sha2_context* context) {
+  context->total_bytes = 0;
+  context->state[0] = 0xcbbb9d5dc1059ed8ULL;
+  context->state[1] = 0x629a292a367cd507ULL;
+  context->state[2] = 0x9159015a3070dd17ULL;
+  context->state[3] = 0x152fecd8f70e5939ULL;
+  context->state[4] = 0x67332667ffc00b31ULL;
+  context->state[5] = 0x8eb44a8768581511ULL;
+  context->state[6] = 0xdb0c2e0d64f98fa7ULL;
+  context->state[7] = 0x47b5481dbefa4fa4ULL;
+  memset(context->buffer, 0, sizeof(context->buffer));
+}
+
+void CRYPT_SHA384Update(CRYPT_sha2_context* context,
+                        const uint8_t* data,
+                        uint32_t size) {
+  if (!size)
+    return;
+
+  uint32_t left = context->total_bytes & 0x7F;
+  uint32_t fill = 128 - left;
+  context->total_bytes += size;
+  if (left && size >= fill) {
+    memcpy(context->buffer + left, data, fill);
+    sha384_process(context, context->buffer);
+    size -= fill;
+    data += fill;
+    left = 0;
+  }
+  while (size >= 128) {
+    sha384_process(context, data);
+    size -= 128;
+    data += 128;
+  }
+  if (size)
+    memcpy(context->buffer + left, data, size);
+}
+
+void CRYPT_SHA384Finish(CRYPT_sha2_context* context, uint8_t digest[48]) {
+  uint8_t msglen[16];
+  uint64_t total_bits = 8 * context->total_bytes;  // Prior to padding.
+  SHA_PUT_UINT64(0ULL, msglen, 0);
+  SHA_PUT_UINT64(total_bits, msglen, 8);
+  uint32_t last = context->total_bytes & 0x7F;
+  uint32_t padn = (last < 112) ? (112 - last) : (240 - last);
+  CRYPT_SHA384Update(context, sha384_padding, padn);
+  CRYPT_SHA384Update(context, msglen, 16);
+  SHA_PUT_UINT64(context->state[0], digest, 0);
+  SHA_PUT_UINT64(context->state[1], digest, 8);
+  SHA_PUT_UINT64(context->state[2], digest, 16);
+  SHA_PUT_UINT64(context->state[3], digest, 24);
+  SHA_PUT_UINT64(context->state[4], digest, 32);
+  SHA_PUT_UINT64(context->state[5], digest, 40);
+}
+
+void CRYPT_SHA384Generate(const uint8_t* data,
+                          uint32_t size,
+                          uint8_t digest[64]) {
+  CRYPT_sha2_context context;
+  CRYPT_SHA384Start(&context);
+  CRYPT_SHA384Update(&context, data, size);
+  CRYPT_SHA384Finish(&context, digest);
+}
+
+void CRYPT_SHA512Start(CRYPT_sha2_context* context) {
+  context->total_bytes = 0;
+  context->state[0] = 0x6a09e667f3bcc908ULL;
+  context->state[1] = 0xbb67ae8584caa73bULL;
+  context->state[2] = 0x3c6ef372fe94f82bULL;
+  context->state[3] = 0xa54ff53a5f1d36f1ULL;
+  context->state[4] = 0x510e527fade682d1ULL;
+  context->state[5] = 0x9b05688c2b3e6c1fULL;
+  context->state[6] = 0x1f83d9abfb41bd6bULL;
+  context->state[7] = 0x5be0cd19137e2179ULL;
+  memset(context->buffer, 0, sizeof(context->buffer));
+}
+
+void CRYPT_SHA512Update(CRYPT_sha2_context* context,
+                        const uint8_t* data,
+                        uint32_t size) {
+  CRYPT_SHA384Update(context, data, size);
+}
+
+void CRYPT_SHA512Finish(CRYPT_sha2_context* context, uint8_t digest[64]) {
+  uint8_t msglen[16];
+  uint64_t total_bits = 8 * context->total_bytes;
+  SHA_PUT_UINT64(0ULL, msglen, 0);
+  SHA_PUT_UINT64(total_bits, msglen, 8);
+  uint32_t last = context->total_bytes & 0x7F;
+  uint32_t padn = (last < 112) ? (112 - last) : (240 - last);
+  CRYPT_SHA512Update(context, sha384_padding, padn);
+  CRYPT_SHA512Update(context, msglen, 16);
+  SHA_PUT_UINT64(context->state[0], digest, 0);
+  SHA_PUT_UINT64(context->state[1], digest, 8);
+  SHA_PUT_UINT64(context->state[2], digest, 16);
+  SHA_PUT_UINT64(context->state[3], digest, 24);
+  SHA_PUT_UINT64(context->state[4], digest, 32);
+  SHA_PUT_UINT64(context->state[5], digest, 40);
+  SHA_PUT_UINT64(context->state[6], digest, 48);
+  SHA_PUT_UINT64(context->state[7], digest, 56);
+}
+
+void CRYPT_SHA512Generate(const uint8_t* data,
+                          uint32_t size,
+                          uint8_t digest[64]) {
+  CRYPT_sha2_context context;
+  CRYPT_SHA512Start(&context);
+  CRYPT_SHA512Update(&context, data, size);
+  CRYPT_SHA512Finish(&context, digest);
+}
diff --git a/core/fdrm/fx_crypt_unittest.cpp b/core/fdrm/fx_crypt_unittest.cpp
new file mode 100644
index 0000000..de76cd0
--- /dev/null
+++ b/core/fdrm/fx_crypt_unittest.cpp
@@ -0,0 +1,640 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fdrm/fx_crypt.h"
+
+#include <memory>
+#include <string>
+
+#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));
+}
+
+void CheckArcFourContext(const CRYPT_rc4_context& context,
+                         int32_t expected_x,
+                         int32_t expected_y,
+                         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)
+    EXPECT_EQ(expected_permutation[i], context.m[i]) << i;
+}
+
+}  // namespace
+
+// Originally from chromium's /src/base/md5_unittest.cc.
+TEST(FXCRYPT, CryptToBase16) {
+  static constexpr uint8_t kData[] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00,
+                                      0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98,
+                                      0xec, 0xf8, 0x42, 0x7e};
+
+  std::string actual = CryptToBase16(kData);
+  std::string expected = "d41d8cd98f00b204e9800998ecf8427e";
+
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, MD5GenerateEmtpyData) {
+  uint8_t digest[16];
+  CRYPT_MD5Generate({}, digest);
+
+  static constexpr uint8_t kExpected[] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00,
+                                          0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98,
+                                          0xec, 0xf8, 0x42, 0x7e};
+
+  for (int i = 0; i < 16; ++i)
+    EXPECT_EQ(kExpected[i], digest[i]);
+}
+
+TEST(FXCRYPT, MD5GenerateOneByteData) {
+  uint8_t digest[16];
+  CRYPT_MD5Generate(pdfium::as_bytes(pdfium::make_span("a", 1)), digest);
+
+  static constexpr uint8_t kExpected[] = {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1,
+                                          0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2,
+                                          0x69, 0x77, 0x26, 0x61};
+
+  for (int i = 0; i < 16; ++i)
+    EXPECT_EQ(kExpected[i], digest[i]);
+}
+
+TEST(FXCRYPT, MD5GenerateLongData) {
+  const uint32_t length = 10 * 1024 * 1024 + 1;
+  std::vector<uint8_t> data(length);
+
+  for (uint32_t i = 0; i < length; ++i)
+    data[i] = i & 0xFF;
+
+  uint8_t digest[16];
+  CRYPT_MD5Generate(data, digest);
+
+  static constexpr uint8_t kExpected[] = {0x90, 0xbd, 0x6a, 0xd9, 0x0a, 0xce,
+                                          0xf5, 0xad, 0xaa, 0x92, 0x20, 0x3e,
+                                          0x21, 0xc7, 0xa1, 0x3e};
+
+  for (int i = 0; i < 16; ++i)
+    EXPECT_EQ(kExpected[i], digest[i]);
+}
+
+TEST(FXCRYPT, ContextWithEmptyData) {
+  CRYPT_md5_context ctx = CRYPT_MD5Start();
+
+  uint8_t digest[16];
+  CRYPT_MD5Finish(&ctx, digest);
+
+  static constexpr uint8_t kExpected[] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00,
+                                          0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98,
+                                          0xec, 0xf8, 0x42, 0x7e};
+
+  for (int i = 0; i < 16; ++i)
+    EXPECT_EQ(kExpected[i], digest[i]);
+}
+
+TEST(FXCRYPT, ContextWithLongData) {
+  CRYPT_md5_context ctx = CRYPT_MD5Start();
+
+  const uint32_t length = 10 * 1024 * 1024 + 1;
+  std::vector<uint8_t> data(length);
+
+  for (uint32_t i = 0; i < length; ++i)
+    data[i] = i & 0xFF;
+
+  pdfium::span<const uint8_t> data_span = pdfium::make_span(data);
+  uint32_t total = 0;
+  while (total < length) {
+    constexpr uint32_t kChunkLen = 4097;  // intentionally not 2^k.
+    uint32_t len = std::min(kChunkLen, length - total);
+    CRYPT_MD5Update(&ctx, data_span.subspan(total, len));
+    total += len;
+  }
+
+  EXPECT_EQ(length, total);
+
+  uint8_t digest[16];
+  CRYPT_MD5Finish(&ctx, digest);
+
+  static constexpr uint8_t kExpected[] = {0x90, 0xbd, 0x6a, 0xd9, 0x0a, 0xce,
+                                          0xf5, 0xad, 0xaa, 0x92, 0x20, 0x3e,
+                                          0x21, 0xc7, 0xa1, 0x3e};
+
+  for (int i = 0; i < 16; ++i)
+    EXPECT_EQ(kExpected[i], digest[i]);
+}
+
+// Example data from http://www.ietf.org/rfc/rfc1321.txt A.5 Test Suite
+TEST(FXCRYPT, MD5StringTestSuite1) {
+  std::string actual = CRYPT_MD5String("");
+  std::string expected = "d41d8cd98f00b204e9800998ecf8427e";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, MD5StringTestSuite2) {
+  std::string actual = CRYPT_MD5String("a");
+  std::string expected = "0cc175b9c0f1b6a831c399e269772661";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, MD5StringTestSuite3) {
+  std::string actual = CRYPT_MD5String("abc");
+  std::string expected = "900150983cd24fb0d6963f7d28e17f72";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, MD5StringTestSuite4) {
+  std::string actual = CRYPT_MD5String("message digest");
+  std::string expected = "f96b697d7cb7938d525a2f31aaf161d0";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, MD5StringTestSuite5) {
+  std::string actual = CRYPT_MD5String("abcdefghijklmnopqrstuvwxyz");
+  std::string expected = "c3fcd3d76192e4007dfb496cca67e13b";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, MD5StringTestSuite6) {
+  std::string actual = CRYPT_MD5String(
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+      "abcdefghijklmnopqrstuvwxyz"
+      "0123456789");
+  std::string expected = "d174ab98d277d9f5a5611c2c9f419d9f";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, MD5StringTestSuite7) {
+  std::string actual = CRYPT_MD5String(
+      "12345678901234567890"
+      "12345678901234567890"
+      "12345678901234567890"
+      "12345678901234567890");
+  std::string expected = "57edf4a22be3c955ac49da2e2107b67a";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, ContextWithStringData) {
+  CRYPT_md5_context ctx = CRYPT_MD5Start();
+  CRYPT_MD5Update(&ctx, pdfium::as_bytes(pdfium::make_span("abc", 3)));
+
+  uint8_t digest[16];
+  CRYPT_MD5Finish(&ctx, digest);
+
+  std::string actual = CryptToBase16(digest);
+  std::string expected = "900150983cd24fb0d6963f7d28e17f72";
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(FXCRYPT, Sha1Empty) {
+  static const char kInput[] = "";
+  static const uint8_t kExpected[] = {0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b,
+                                      0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60,
+                                      0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09};
+  uint8_t actual[20];
+  CRYPT_SHA1Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
+                     actual);
+
+  for (size_t i = 0; i < FX_ArraySize(kExpected); i++)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+// Originally from chromium's /src/base/sha1_unittest.cc
+TEST(FXCRYPT, Sha1TestA1) {
+  // Example A.1 from FIPS 180-2: one-block message.
+  static const char kInput[] = "abc";
+  static const uint8_t kExpected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81,
+                                      0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50,
+                                      0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
+  uint8_t actual[20];
+  CRYPT_SHA1Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
+                     actual);
+
+  for (size_t i = 0; i < FX_ArraySize(kExpected); i++)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+TEST(FXCRYPT, Sha1TestA2) {
+  // Example A.2 from FIPS 180-2: multi-block message.
+  static const char kInput[] =
+      "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+  static const uint8_t kExpected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2,
+                                      0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51,
+                                      0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1};
+
+  uint8_t actual[20];
+  CRYPT_SHA1Generate(reinterpret_cast<const uint8_t*>(kInput), strlen(kInput),
+                     actual);
+
+  for (size_t i = 0; i < FX_ArraySize(kExpected); i++)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+TEST(FXCRYPT, Sha256Empty) {
+  static const char kInput[] = "";
+  static const uint8_t kExpected[32] = {
+      0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
+      0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
+      0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+TEST(FXCRYPT, Sha256TestB1) {
+  // Example B.1 from FIPS 180-2: one-block message.
+  static const char kInput[] = "abc";
+  static const uint8_t kExpected[32] = {
+      0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40,
+      0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17,
+      0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+TEST(FXCRYPT, Sha256TestB2) {
+  // Example B.2 from FIPS 180-2: multi-block message.
+  static const char kInput[] =
+      "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+  static const uint8_t kExpected[32] = {
+      0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26,
+      0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff,
+      0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1};
+  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)
+    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};
+    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};
+    CRYPT_rc4_context context;
+    static const uint8_t kFooBar[] = "foobar";
+    CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1});
+    CheckArcFourContext(context, 0, 0, kFoobarPermutation);
+  }
+}
+
+TEST(FXCRYPT, CRYPT_ArcFourCrypt) {
+  static const uint8_t kDataShort[] =
+      "The Quick Fox Jumped Over The Lazy Brown Dog.";
+  static const uint8_t kDataLong[] =
+      "The Quick Fox Jumped Over The Lazy Brown Dog.\n"
+      "1234567890123456789012345678901234567890123456789012345678901234567890\n"
+      "1234567890123456789012345678901234567890123456789012345678901234567890\n"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
+      "!@#$%^&*()[]{};':\",.<>/?\\|\r\t\n";
+  {
+    CRYPT_rc4_context context;
+    CRYPT_ArcFourSetup(&context, {});
+
+    uint8_t data_short[FX_ArraySize(kDataShort)];
+    memcpy(data_short, kDataShort, FX_ArraySize(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),
+        "data_short mismatch");
+    CRYPT_ArcFourCrypt(&context, data_short);
+    for (size_t i = 0; i < FX_ArraySize(data_short); ++i)
+      EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i;
+
+    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+        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,
+        50,  132, 12,  160, 145, 250, 214, 76,  123, 35,  27,  249, 203, 127,
+        64,  62,  33,  60,  248, 85,  177, 6,   142, 83,  110, 140, 41,  135,
+        196, 238, 156, 91,  141, 67,  5,   185, 131, 63,  137, 43,  172, 121,
+        70,  134, 237, 130, 25,  44,  153, 166, 78,  201, 42,  119, 215, 7,
+        126, 114, 97,  11,  53,  4,   254, 45,  102, 133, 230, 88,  193, 129,
+        18,  124, 84,  108, 239, 189, 152, 120, 115, 207, 234, 176, 86,  157,
+        164, 187, 71,  1,   15,  58,  29,  21,  46,  23,  247, 162, 95,  229,
+        13,  226, 159, 175, 56,  100, 96,  202, 101, 178, 154, 47,  205, 106,
+        148, 104, 93,  112, 26,  165, 128, 246, 146, 218, 66,  211, 65,  90,
+        252, 19,  40,  49,  223, 174, 255, 51,  77,  227, 48,  220, 168, 118,
+        224, 98,  75,  105, 125, 199, 73,  82,  57,  181, 81,  173, 68,  52,
+        232, 22,  2,   216, 113, 30,  109, 163, 92,  61,  14,  36,  38,  225,
+        79,  231, 170, 240, 20,  219, 204, 161, 180, 188, 116, 190, 241, 197,
+        179, 87,  74,  147, 80,  54,  69,  16,  167, 222, 136, 245, 55,  182,
+        3,   24,  209, 251, 59,  28,  111, 89,  195, 155, 194, 107, 233, 34,
+        117, 184, 31,  39};
+    CheckArcFourContext(context, 46, 135, kPermutation);
+  }
+  {
+    CRYPT_rc4_context context;
+    CRYPT_ArcFourSetup(&context, {});
+
+    uint8_t data_long[FX_ArraySize(kDataLong)];
+    memcpy(data_long, kDataLong, FX_ArraySize(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,
+        66,  182, 19,  255, 210, 181, 85,  69,  31,  240, 206, 171, 97,  62,
+        202, 172, 30,  246, 19,  43,  184, 0,   173, 27,  140, 90,  167, 240,
+        122, 125, 184, 49,  149, 71,  63,  104, 171, 144, 242, 106, 121, 124,
+        209, 149, 61,  1,   66,  186, 252, 47,  51,  170, 253, 75,  95,  41,
+        203, 28,  197, 174, 144, 209, 166, 98,  142, 125, 44,  5,   147, 42,
+        73,  178, 119, 90,  253, 69,  103, 178, 15,  136, 51,  112, 39,  81,
+        37,  111, 129, 232, 106, 159, 126, 142, 120, 124, 48,  140, 253, 12,
+        223, 208, 106, 76,  60,  238, 5,   162, 100, 226, 251, 156, 169, 35,
+        193, 10,  242, 210, 20,  96,  37,  84,  99,  183, 179, 203, 62,  122,
+        54,  6,   51,  239, 142, 250, 238, 41,  223, 58,  48,  101, 29,  187,
+        43,  235, 3,   5,   176, 33,  14,  171, 36,  26,  234, 207, 105, 79,
+        69,  126, 82,  183, 105, 228, 31,  173, 8,   240, 99,  5,   147, 206,
+        215, 140, 48,  190, 165, 50,  41,  232, 29,  105, 156, 64,  229, 165,
+        12,  64,  163, 255, 146, 108, 212, 125, 142, 101, 13,  99,  174, 10,
+        160, 68,  196, 120, 110, 201, 254, 158, 97,  215, 0,   207, 90,  23,
+        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");
+    CRYPT_ArcFourCrypt(&context, data_long);
+    for (size_t i = 0; i < FX_ArraySize(data_long); ++i)
+      EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i;
+
+    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+        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,
+        104, 132, 12,  170, 205, 114, 7,   105, 37,  83,  78,  134, 236, 70,
+        197, 122, 177, 202, 39,  195, 30,  3,   86,  127, 74,  106, 68,  91,
+        110, 121, 208, 25,  56,  6,   28,  225, 163, 193, 166, 244, 119, 34,
+        23,  88,  108, 123, 162, 159, 242, 61,  230, 227, 254, 14,  4,   156,
+        161, 44,  58,  153, 33,  143, 129, 232, 182, 152, 76,  168, 238, 239,
+        185, 219, 233, 16,  188, 45,  40,  35,  103, 99,  89,  157, 241, 245,
+        192, 180, 248, 8,   85,  231, 146, 154, 252, 181, 107, 126, 98,  80,
+        102, 165, 199, 94,  49,  255, 18,  204, 216, 77,  20,  187, 145, 125,
+        1,   247, 79,  26,  207, 81,  117, 179, 186, 38,  175, 19,  139, 138,
+        149, 54,  64,  109, 249, 135, 142, 118, 17,  13,  201, 184, 55,  224,
+        209, 155, 113, 218, 82,  131, 178, 253, 140, 226, 43,  42,  24,  29,
+        229, 200, 137, 240, 203, 167, 95,  148, 15,  176, 60,  75,  53,  41,
+        150, 112, 160, 96,  22,  10,  234, 116, 130, 158, 214, 36,  9,   67,
+        198, 194, 191, 100, 124, 147, 32,  183, 120, 246, 51,  141, 46,  251,
+        92,  223, 133, 63,  0,   71,  48,  128, 220, 90,  62,  136, 2,   5,
+        69,  57,  151, 84};
+    CheckArcFourContext(context, 15, 222, kPermutation);
+  }
+  {
+    CRYPT_rc4_context context;
+    static const uint8_t kFooBar[] = "foobar";
+    CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1});
+
+    uint8_t data_short[FX_ArraySize(kDataShort)];
+    memcpy(data_short, kDataShort, FX_ArraySize(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),
+        "data_short mismatch");
+    CRYPT_ArcFourCrypt(&context, data_short);
+    for (size_t i = 0; i < FX_ArraySize(data_short); ++i)
+      EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i;
+
+    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+        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,
+        42,  81,  117, 56,  181, 252, 113, 210, 217, 62,  218, 129, 61,  33,
+        128, 9,   153, 59,  43,  13,  206, 48,  131, 18,  213, 118, 173, 122,
+        80,  172, 177, 105, 148, 207, 186, 5,   85,  32,  68,  215, 19,  84,
+        169, 209, 150, 7,   133, 63,  147, 93,  26,  130, 60,  145, 250, 57,
+        24,  247, 200, 127, 136, 66,  112, 107, 212, 154, 70,  170, 185, 138,
+        248, 236, 88,  86,  44,  216, 241, 35,  100, 151, 78,  74,  119, 55,
+        245, 46,  199, 208, 229, 16,  249, 149, 53,  157, 201, 234, 58,  28,
+        142, 238, 182, 163, 179, 144, 12,  114, 176, 10,  183, 239, 104, 40,
+        73,  101, 137, 69,  221, 134, 165, 188, 25,  87,  1,   91,  2,   171,
+        232, 34,  162, 166, 160, 126, 225, 167, 123, 197, 223, 195, 22,  203,
+        189, 244, 103, 139, 222, 175, 23,  143, 152, 192, 21,  231, 228, 132,
+        30,  204, 158, 240, 120, 98,  89,  121, 135, 251, 168, 4,   161, 3,
+        8,   230, 52,  219, 214, 242, 36,  97,  15,  155, 65,  187, 116, 76,
+        159, 67,  211, 20,  178, 146, 202, 11,  164, 226, 184, 50,  77,  174,
+        71,  233, 235, 198, 95,  51,  110, 255, 92,  72,  115, 106, 47,  94,
+        29,  39,  14,  111};
+    CheckArcFourContext(context, 46, 39, kPermutation);
+  }
+  {
+    CRYPT_rc4_context context;
+    static const uint8_t kFooBar[] = "foobar";
+    CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1});
+
+    uint8_t data_long[FX_ArraySize(kDataLong)];
+    memcpy(data_long, kDataLong, FX_ArraySize(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,
+        240, 123, 192, 102, 174, 167, 105, 187, 202, 70,  121, 81,  17,  30,
+        5,   138, 116, 172, 169, 50,  160, 116, 237, 117, 108, 241, 127, 61,
+        83,  45,  77,  176, 0,   106, 191, 221, 132, 143, 219, 94,  2,   235,
+        204, 166, 201, 139, 140, 163, 104, 115, 48,  37,  18,  114, 168, 49,
+        235, 163, 179, 131, 182, 218, 120, 200, 9,   90,  60,  47,  55,  235,
+        135, 37,  21,  170, 48,  112, 185, 169, 43,  233, 88,  134, 117, 126,
+        248, 40,  176, 248, 30,  131, 108, 43,  139, 68,  232, 219, 7,   39,
+        223, 45,  199, 243, 54,  171, 31,  37,  161, 24,  38,  251, 13,  144,
+        106, 215, 179, 203, 5,   253, 25,  32,  25,  146, 109, 193, 143, 141,
+        177, 226, 134, 222, 95,  79,  156, 202, 240, 34,  153, 145, 169, 150,
+        231, 63,  113, 242, 156, 39,  136, 249, 108, 50,  181, 22,  22,  180,
+        57,  76,  69,  62,  254, 47,  141, 249, 235, 90,  25,  34,  40,  194,
+        66,  86,  110, 192, 235, 191, 205, 133, 91,  32,  104, 65,  43,  36,
+        140, 36,  228, 156, 105, 251, 169, 168, 203, 189, 238, 221, 64,  200,
+        68,  137, 153, 9,   183, 84,  153, 140, 239, 0,   15,  50,  126, 145,
+        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");
+    CRYPT_ArcFourCrypt(&context, data_long);
+    for (size_t i = 0; i < FX_ArraySize(data_long); ++i)
+      EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i;
+
+    static const uint8_t kPermutation[kRC4ContextPermutationLength] = {
+        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,
+        42,  45,  236, 56,  230, 194, 178, 213, 120, 116, 7,   164, 33,  107,
+        189, 20,  133, 114, 173, 161, 59,  128, 3,   238, 65,  69,  144, 179,
+        44,  35,  8,   163, 252, 195, 160, 197, 204, 28,  34,  129, 67,  89,
+        22,  149, 199, 131, 182, 46,  250, 222, 155, 104, 10,  32,  139, 245,
+        90,  41,  132, 224, 83,  242, 135, 75,  74,  61,  62,  141, 43,  127,
+        255, 91,  170, 78,  157, 101, 243, 216, 254, 156, 229, 118, 174, 147,
+        103, 76,  196, 145, 134, 94,  205, 146, 202, 98,  100, 106, 232, 177,
+        187, 13,  80,  137, 151, 11,  82,  40,  167, 175, 25,  219, 168, 240,
+        99,  55,  4,   19,  180, 2,   203, 18,  171, 154, 113, 117, 6,   185,
+        172, 186, 237, 223, 233, 244, 217, 191, 190, 198, 97,  165, 220, 9,
+        214, 150, 184, 143, 206, 24,  209, 207, 36,  142, 87,  15,  159, 71,
+        84,  162, 169, 86,  48,  47,  140, 215, 241, 235, 158, 14,  26,  248,
+        138, 119, 212, 39,  88,  121, 96,  109, 29,  66,  136, 102, 225, 92,
+        201, 126, 122, 192, 60,  0,   64,  239, 183, 37,  57,  63,  234, 181,
+        153, 52,  176, 112, 93,  79,  77,  115, 231, 30,  95,  251, 211, 68,
+        105, 85,  247, 152};
+    CheckArcFourContext(context, 15, 68, kPermutation);
+  }
+}
+
+TEST(FXCRYPT, Sha384Empty) {
+  static const char kInput[] = "";
+  static const uint8_t kExpected[48] = {
+      0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e,
+      0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43,
+      0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf,
+      0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b};
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+// Verified against echo -n "..." | openssl sha384
+TEST(FXCRYPT, Sha384Test) {
+  static const char kInput[] =
+      "This is a simple test. To see whether it is getting correct value.";
+  static const uint8_t kExpected[48] = {
+      0x95, 0x54, 0xff, 0xd3, 0x89, 0xf0, 0xd6, 0x42, 0xe9, 0x33, 0xfe, 0x4c,
+      0x07, 0x81, 0x19, 0xca, 0xcb, 0xb3, 0x14, 0x46, 0xd8, 0xbd, 0xa4, 0xf4,
+      0x12, 0xd5, 0x54, 0x03, 0x79, 0x28, 0xe5, 0xdc, 0x12, 0xa5, 0x1b, 0xe9,
+      0xfe, 0x59, 0x25, 0x3c, 0x92, 0x30, 0x5e, 0xe5, 0x0e, 0x03, 0x58, 0x07};
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+// Verified against echo -n "..." | openssl sha384
+TEST(FXCRYPT, Sha384Pad112) {
+  static const char kInput[] =
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+  static const uint8_t kExpected[48] = {
+      0x18, 0x7d, 0x4e, 0x07, 0xcb, 0x30, 0x61, 0x03, 0xc6, 0x99, 0x67, 0xbf,
+      0x54, 0x4d, 0x0d, 0xfb, 0xe9, 0x04, 0x25, 0x77, 0x59, 0x9c, 0x73, 0xc3,
+      0x30, 0xab, 0xc0, 0xcb, 0x64, 0xc6, 0x12, 0x36, 0xd5, 0xed, 0x56, 0x5e,
+      0xe1, 0x91, 0x19, 0xd8, 0xc3, 0x17, 0x79, 0xa3, 0x8f, 0x79, 0x1f, 0xcd};
+  uint8_t actual[48];
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+TEST(FXCRYPT, Sha512Empty) {
+  static const char kInput[] = "";
+  static const uint8_t kExpected[64] = {
+      0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28,
+      0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57,
+      0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47,
+      0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2,
+      0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a,
+      0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e};
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+// Verified against echo -n "..." | openssl sha512
+TEST(FXCRYPT, Sha512Test) {
+  static const char kInput[] =
+      "This is a simple test. To see whether it is getting correct value.";
+  static const uint8_t kExpected[64] = {
+      0x86, 0xB5, 0x05, 0x63, 0xA2, 0x6F, 0xD6, 0xFA, 0xEB, 0x9B, 0xC3,
+      0xBB, 0x9E, 0xB7, 0x03, 0x82, 0xB6, 0x50, 0x55, 0x6B, 0x90, 0x69,
+      0xD0, 0xA7, 0x53, 0x0A, 0x34, 0xDD, 0xEA, 0x11, 0xCC, 0x91, 0x5C,
+      0xC7, 0x93, 0xCA, 0xAE, 0x30, 0xD1, 0x96, 0xBE, 0xD0, 0x35, 0x21,
+      0x4A, 0xC6, 0x42, 0x56, 0x0C, 0xA3, 0x00, 0x69, 0x44, 0x77, 0xCC,
+      0x3E, 0xD4, 0xD6, 0x10, 0x31, 0xC6, 0xC0, 0x58, 0xCF};
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
+
+// Verified against echo -n "..." | openssl sha512
+TEST(FXCRYPT, Sha512Pad112) {
+  static const char kInput[] =
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+  static const uint8_t kExpected[64] = {
+      0xc0, 0x1d, 0x08, 0x0e, 0xfd, 0x49, 0x27, 0x76, 0xa1, 0xc4, 0x3b,
+      0xd2, 0x3d, 0xd9, 0x9d, 0x0a, 0x2e, 0x62, 0x6d, 0x48, 0x1e, 0x16,
+      0x78, 0x2e, 0x75, 0xd5, 0x4c, 0x25, 0x03, 0xb5, 0xdc, 0x32, 0xbd,
+      0x05, 0xf0, 0xf1, 0xba, 0x33, 0xe5, 0x68, 0xb8, 0x8f, 0xd2, 0xd9,
+      0x70, 0x92, 0x9b, 0x71, 0x9e, 0xcb, 0xb1, 0x52, 0xf5, 0x8f, 0x13,
+      0x0a, 0x40, 0x7c, 0x88, 0x30, 0x60, 0x4b, 0x70, 0xca};
+  uint8_t actual[64];
+  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)
+    EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i;
+}
diff --git a/core/fpdfapi/cmaps/Android.bp b/core/fpdfapi/cmaps/Android.bp
new file mode 100644
index 0000000..e2d5067
--- /dev/null
+++ b/core/fpdfapi/cmaps/Android.bp
@@ -0,0 +1,14 @@
+cc_library_static {
+    name: "libpdfium-cmaps",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    static_libs: [
+        "libpdfium-fxcrt",
+    ],
+
+    srcs: [
+        "**/*.cpp",
+    ],
+}
diff --git a/core/fpdfapi/cmaps/BUILD.gn b/core/fpdfapi/cmaps/BUILD.gn
new file mode 100644
index 0000000..e7fddbf
--- /dev/null
+++ b/core/fpdfapi/cmaps/BUILD.gn
@@ -0,0 +1,76 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+
+source_set("cmaps") {
+  sources = [
+    "CNS1/Adobe-CNS1-UCS2_5.cpp",
+    "CNS1/B5pc-H_0.cpp",
+    "CNS1/B5pc-V_0.cpp",
+    "CNS1/CNS-EUC-H_0.cpp",
+    "CNS1/CNS-EUC-V_0.cpp",
+    "CNS1/ETen-B5-H_0.cpp",
+    "CNS1/ETen-B5-V_0.cpp",
+    "CNS1/ETenms-B5-H_0.cpp",
+    "CNS1/ETenms-B5-V_0.cpp",
+    "CNS1/HKscs-B5-H_5.cpp",
+    "CNS1/HKscs-B5-V_5.cpp",
+    "CNS1/UniCNS-UCS2-H_3.cpp",
+    "CNS1/UniCNS-UCS2-V_3.cpp",
+    "CNS1/UniCNS-UTF16-H_0.cpp",
+    "CNS1/cmaps_cns1.cpp",
+    "GB1/Adobe-GB1-UCS2_5.cpp",
+    "GB1/GB-EUC-H_0.cpp",
+    "GB1/GB-EUC-V_0.cpp",
+    "GB1/GBK-EUC-H_2.cpp",
+    "GB1/GBK-EUC-V_2.cpp",
+    "GB1/GBK2K-H_5.cpp",
+    "GB1/GBK2K-V_5.cpp",
+    "GB1/GBKp-EUC-H_2.cpp",
+    "GB1/GBKp-EUC-V_2.cpp",
+    "GB1/GBpc-EUC-H_0.cpp",
+    "GB1/GBpc-EUC-V_0.cpp",
+    "GB1/UniGB-UCS2-H_4.cpp",
+    "GB1/UniGB-UCS2-V_4.cpp",
+    "GB1/cmaps_gb1.cpp",
+    "Japan1/83pv-RKSJ-H_1.cpp",
+    "Japan1/90ms-RKSJ-H_2.cpp",
+    "Japan1/90ms-RKSJ-V_2.cpp",
+    "Japan1/90msp-RKSJ-H_2.cpp",
+    "Japan1/90msp-RKSJ-V_2.cpp",
+    "Japan1/90pv-RKSJ-H_1.cpp",
+    "Japan1/Add-RKSJ-H_1.cpp",
+    "Japan1/Add-RKSJ-V_1.cpp",
+    "Japan1/Adobe-Japan1-UCS2_4.cpp",
+    "Japan1/EUC-H_1.cpp",
+    "Japan1/EUC-V_1.cpp",
+    "Japan1/Ext-RKSJ-H_2.cpp",
+    "Japan1/Ext-RKSJ-V_2.cpp",
+    "Japan1/H_1.cpp",
+    "Japan1/UniJIS-UCS2-HW-H_4.cpp",
+    "Japan1/UniJIS-UCS2-HW-V_4.cpp",
+    "Japan1/UniJIS-UCS2-H_4.cpp",
+    "Japan1/UniJIS-UCS2-V_4.cpp",
+    "Japan1/V_1.cpp",
+    "Japan1/cmaps_japan1.cpp",
+    "Korea1/Adobe-Korea1-UCS2_2.cpp",
+    "Korea1/KSC-EUC-H_0.cpp",
+    "Korea1/KSC-EUC-V_0.cpp",
+    "Korea1/KSCms-UHC-HW-H_1.cpp",
+    "Korea1/KSCms-UHC-HW-V_1.cpp",
+    "Korea1/KSCms-UHC-H_1.cpp",
+    "Korea1/KSCms-UHC-V_1.cpp",
+    "Korea1/KSCpc-EUC-H_0.cpp",
+    "Korea1/UniKS-UCS2-H_1.cpp",
+    "Korea1/UniKS-UCS2-V_1.cpp",
+    "Korea1/UniKS-UTF16-H_0.cpp",
+    "Korea1/cmaps_korea1.cpp",
+    "fpdf_cmaps.cpp",
+    "fpdf_cmaps.h",
+  ]
+  configs += [ "../../../:pdfium_core_config" ]
+  deps = [ "../../fxcrt" ]
+  visibility = [ "../../../*" ]
+}
diff --git a/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp b/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp
index e02f9ed..3cc5573 100644
--- a/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp
+++ b/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp
@@ -7,5 +7,7 @@
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
 const uint16_t g_FXCMAP_ETenms_B5_H_0[1 * 3] = {
-    0x0020, 0x007E, 0x0001,
+    0x0020,
+    0x007E,
+    0x0001,
 };
diff --git a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp
index 5076fcd..810104e 100644
--- a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp
+++ b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp
@@ -6,12 +6,9 @@
 
 #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h"
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_fontglobals.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fxcrt/fx_memory.h"
 
-static const FXCMAP_CMap g_FXCMAP_CNS1_cmaps[] = {
+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,
@@ -38,11 +35,4 @@
      FXCMAP_CMap::Range, -1},
 };
 
-void CPDF_ModuleMgr::LoadEmbeddedCNS1CMaps() {
-  CPDF_FontGlobals* pFontGlobals =
-      CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-  pFontGlobals->SetEmbeddedCharset(CIDSET_CNS1, g_FXCMAP_CNS1_cmaps,
-                                   FX_ArraySize(g_FXCMAP_CNS1_cmaps));
-  pFontGlobals->SetEmbeddedToUnicode(CIDSET_CNS1, g_FXCMAP_CNS1CID2Unicode_5,
-                                     19088);
-}
+const size_t g_FXCMAP_CNS1_cmaps_size = FX_ArraySize(g_FXCMAP_CNS1_cmaps);
diff --git a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h
index 627feb7..64f6b8b 100644
--- a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h
+++ b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h
@@ -7,9 +7,7 @@
 #ifndef CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_
 #define CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_
 
-#include <stdint.h>
-
-#include "core/fpdfapi/cmaps/cmap_int.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[];
@@ -26,6 +24,8 @@
 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[];
+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;
 
 #endif  // CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_
diff --git a/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp b/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp
index f35161a..a598091 100644
--- a/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp
+++ b/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp
@@ -6,12 +6,9 @@
 
 #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h"
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_fontglobals.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fxcrt/fx_memory.h"
 
-static const FXCMAP_CMap g_FXCMAP_GB1_cmaps[] = {
+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,
@@ -38,11 +35,4 @@
      FXCMAP_CMap::Range, -1},
 };
 
-void CPDF_ModuleMgr::LoadEmbeddedGB1CMaps() {
-  CPDF_FontGlobals* pFontGlobals =
-      CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-  pFontGlobals->SetEmbeddedCharset(CIDSET_GB1, g_FXCMAP_GB1_cmaps,
-                                   FX_ArraySize(g_FXCMAP_GB1_cmaps));
-  pFontGlobals->SetEmbeddedToUnicode(CIDSET_GB1, g_FXCMAP_GB1CID2Unicode_5,
-                                     30284);
-}
+const size_t g_FXCMAP_GB1_cmaps_size = FX_ArraySize(g_FXCMAP_GB1_cmaps);
diff --git a/core/fpdfapi/cmaps/GB1/cmaps_gb1.h b/core/fpdfapi/cmaps/GB1/cmaps_gb1.h
index a475af1..7873f64 100644
--- a/core/fpdfapi/cmaps/GB1/cmaps_gb1.h
+++ b/core/fpdfapi/cmaps/GB1/cmaps_gb1.h
@@ -7,9 +7,7 @@
 #ifndef CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_
 #define CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_
 
-#include <stdint.h>
-
-#include "core/fpdfapi/cmaps/cmap_int.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[];
@@ -24,6 +22,8 @@
 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[];
+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;
 
 #endif  // CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_
diff --git a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp
index f640fdd..5a48c77 100644
--- a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp
+++ b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp
@@ -6,12 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h"
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_fontglobals.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fxcrt/fx_memory.h"
 
-static const FXCMAP_CMap g_FXCMAP_Japan1_cmaps[] = {
+const FXCMAP_CMap g_FXCMAP_Japan1_cmaps[] = {
     {"83pv-RKSJ-H", g_FXCMAP_83pv_RKSJ_H_1, nullptr, 222, 0, FXCMAP_CMap::Range,
      0},
     {"90ms-RKSJ-H", g_FXCMAP_90ms_RKSJ_H_2, nullptr, 171, 0, FXCMAP_CMap::Range,
@@ -50,11 +47,4 @@
      FXCMAP_CMap::Single, -1},
 };
 
-void CPDF_ModuleMgr::LoadEmbeddedJapan1CMaps() {
-  CPDF_FontGlobals* pFontGlobals =
-      CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-  pFontGlobals->SetEmbeddedCharset(CIDSET_JAPAN1, g_FXCMAP_Japan1_cmaps,
-                                   FX_ArraySize(g_FXCMAP_Japan1_cmaps));
-  pFontGlobals->SetEmbeddedToUnicode(CIDSET_JAPAN1,
-                                     g_FXCMAP_Japan1CID2Unicode_4, 15444);
-}
+const size_t g_FXCMAP_Japan1_cmaps_size = FX_ArraySize(g_FXCMAP_Japan1_cmaps);
diff --git a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h
index 27777f2..267a9fc 100644
--- a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h
+++ b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h
@@ -7,7 +7,7 @@
 #ifndef CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_
 #define CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_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[];
@@ -30,6 +30,8 @@
 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[];
+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;
 
 #endif  // CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_
diff --git a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp
index daec825..7362ff8 100644
--- a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp
+++ b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp
@@ -6,12 +6,9 @@
 
 #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h"
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_fontglobals.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fxcrt/fx_memory.h"
 
-static const FXCMAP_CMap g_FXCMAP_Korea1_cmaps[] = {
+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,
@@ -34,11 +31,4 @@
      FXCMAP_CMap::Range, -1},
 };
 
-void CPDF_ModuleMgr::LoadEmbeddedKorea1CMaps() {
-  CPDF_FontGlobals* pFontGlobals =
-      CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-  pFontGlobals->SetEmbeddedCharset(CIDSET_KOREA1, g_FXCMAP_Korea1_cmaps,
-                                   FX_ArraySize(g_FXCMAP_Korea1_cmaps));
-  pFontGlobals->SetEmbeddedToUnicode(CIDSET_KOREA1,
-                                     g_FXCMAP_Korea1CID2Unicode_2, 18352);
-}
+const size_t g_FXCMAP_Korea1_cmaps_size = FX_ArraySize(g_FXCMAP_Korea1_cmaps);
diff --git a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h
index 5005ff2..d54d156 100644
--- a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h
+++ b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h
@@ -7,7 +7,7 @@
 #ifndef CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_
 #define CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_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[];
@@ -19,6 +19,8 @@
 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[];
+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;
 
 #endif  // CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_
diff --git a/core/fpdfapi/cmaps/cmap_int.h b/core/fpdfapi/cmaps/cmap_int.h
deleted file mode 100644
index 27f9617..0000000
--- a/core/fpdfapi/cmaps/cmap_int.h
+++ /dev/null
@@ -1,38 +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_FPDFAPI_CMAPS_CMAP_INT_H_
-#define CORE_FPDFAPI_CMAPS_CMAP_INT_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-
-struct FXCMAP_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 };
-
-  const char* m_Name;
-  const uint16_t* m_pWordMap;
-  const FXCMAP_DWordCIDMap* m_pDWordMap;
-  uint16_t m_WordCount;
-  uint16_t m_DWordCount;
-  MapType m_WordMapType;
-  int8_t m_UseOffset;
-};
-
-const FXCMAP_CMap* FPDFAPI_FindEmbeddedCMap(const ByteString& name,
-                                            int charset,
-                                            int coding);
-uint16_t FPDFAPI_CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode);
-uint32_t FPDFAPI_CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid);
-
-#endif  // CORE_FPDFAPI_CMAPS_CMAP_INT_H_
diff --git a/core/fpdfapi/cmaps/fpdf_cmaps.cpp b/core/fpdfapi/cmaps/fpdf_cmaps.cpp
index bba8d21..5c89faf 100644
--- a/core/fpdfapi/cmaps/fpdf_cmaps.cpp
+++ b/core/fpdfapi/cmaps/fpdf_cmaps.cpp
@@ -4,39 +4,39 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
+#include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 
 #include <algorithm>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_fontglobals.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
-
 namespace {
 
+struct SingleCmap {
+  uint16_t code;
+  uint16_t cid;
+};
+
+struct RangeCmap {
+  uint16_t low;
+  uint16_t high;
+  uint16_t cid;
+};
+
 const FXCMAP_CMap* FindNextCMap(const FXCMAP_CMap* pMap) {
   return pMap->m_UseOffset ? pMap + pMap->m_UseOffset : nullptr;
 }
 
 }  // namespace
 
-const FXCMAP_CMap* FPDFAPI_FindEmbeddedCMap(const ByteString& bsName,
-                                            int charset,
-                                            int coding) {
-  CPDF_FontGlobals* pFontGlobals =
-      CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-
-  const FXCMAP_CMap* pCMaps;
-  uint32_t count;
-  std::tie(count, pCMaps) = pFontGlobals->GetEmbeddedCharset(charset);
-  for (uint32_t i = 0; i < count; i++) {
+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 FPDFAPI_CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) {
+uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) {
   ASSERT(pMap);
   const uint16_t loword = static_cast<uint16_t>(charcode);
   if (charcode >> 16) {
@@ -62,44 +62,44 @@
     return 0;
   }
 
-  while (pMap) {
-    if (!pMap->m_pWordMap)
-      return 0;
-    if (pMap->m_WordMapType == FXCMAP_CMap::Single) {
-      struct SingleCmap {
-        uint16_t code;
-        uint16_t cid;
-      };
-      const auto* begin = reinterpret_cast<const SingleCmap*>(pMap->m_pWordMap);
-      const auto* end = begin + pMap->m_WordCount;
-      const auto* found = std::lower_bound(
-          begin, end, loword, [](const SingleCmap& element, uint16_t code) {
-            return element.code < code;
-          });
-      if (found != end && found->code == loword)
-        return found->cid;
-    } else {
-      ASSERT(pMap->m_WordMapType == FXCMAP_CMap::Range);
-      struct RangeCmap {
-        uint16_t low;
-        uint16_t high;
-        uint16_t cid;
-      };
-      const auto* begin = reinterpret_cast<const RangeCmap*>(pMap->m_pWordMap);
-      const auto* end = begin + pMap->m_WordCount;
-      const auto* found = std::lower_bound(
-          begin, end, loword, [](const RangeCmap& element, uint16_t code) {
-            return element.high < code;
-          });
-      if (found != end && loword >= found->low && loword <= found->high)
-        return found->cid + loword - found->low;
+  while (pMap && pMap->m_pWordMap) {
+    switch (pMap->m_WordMapType) {
+      case FXCMAP_CMap::Single: {
+        const auto* begin =
+            reinterpret_cast<const SingleCmap*>(pMap->m_pWordMap);
+        const auto* end = begin + pMap->m_WordCount;
+        const auto* found = std::lower_bound(
+            begin, end, loword, [](const SingleCmap& element, uint16_t code) {
+              return element.code < code;
+            });
+        if (found != end && found->code == loword)
+          return found->cid;
+        break;
+      }
+      case FXCMAP_CMap::Range: {
+        const auto* begin =
+            reinterpret_cast<const RangeCmap*>(pMap->m_pWordMap);
+        const auto* end = begin + pMap->m_WordCount;
+        const auto* found = std::lower_bound(
+            begin, end, loword, [](const RangeCmap& element, uint16_t code) {
+              return element.high < code;
+            });
+        if (found != end && loword >= found->low && loword <= found->high)
+          return found->cid + loword - found->low;
+        break;
+      }
+      default: {
+        NOTREACHED();
+        break;
+      }
     }
     pMap = FindNextCMap(pMap);
   }
+
   return 0;
 }
 
-uint32_t FPDFAPI_CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid) {
+uint32_t CharCodeFromCID(const FXCMAP_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
@@ -107,24 +107,31 @@
   // second while loop.)
   ASSERT(pMap);
   while (pMap) {
-    if (pMap->m_WordMapType == FXCMAP_CMap::Single) {
-      const uint16_t* pCur = pMap->m_pWordMap;
-      const uint16_t* pEnd = pMap->m_pWordMap + pMap->m_WordCount * 2;
-      while (pCur < pEnd) {
-        if (pCur[1] == cid)
-          return pCur[0];
-
-        pCur += 2;
+    switch (pMap->m_WordMapType) {
+      case FXCMAP_CMap::Single: {
+        const auto* pCur =
+            reinterpret_cast<const SingleCmap*>(pMap->m_pWordMap);
+        const auto* pEnd = pCur + pMap->m_WordCount;
+        while (pCur < pEnd) {
+          if (pCur->cid == cid)
+            return pCur->code;
+          ++pCur;
+        }
+        break;
       }
-    } else {
-      ASSERT(pMap->m_WordMapType == FXCMAP_CMap::Range);
-      const uint16_t* pCur = pMap->m_pWordMap;
-      const uint16_t* pEnd = pMap->m_pWordMap + pMap->m_WordCount * 3;
-      while (pCur < pEnd) {
-        if (cid >= pCur[2] && cid <= pCur[2] + pCur[1] - pCur[0])
-          return pCur[0] + cid - pCur[2];
-
-        pCur += 3;
+      case FXCMAP_CMap::Range: {
+        const auto* pCur = reinterpret_cast<const RangeCmap*>(pMap->m_pWordMap);
+        const auto* pEnd = pCur + pMap->m_WordCount;
+        while (pCur < pEnd) {
+          if (cid >= pCur->cid && cid <= pCur->cid + pCur->high - pCur->low)
+            return pCur->low + cid - pCur->cid;
+          ++pCur;
+        }
+        break;
+      }
+      default: {
+        NOTREACHED();
+        break;
       }
     }
     pMap = FindNextCMap(pMap);
diff --git a/core/fpdfapi/cmaps/fpdf_cmaps.h b/core/fpdfapi/cmaps/fpdf_cmaps.h
new file mode 100644
index 0000000..2c7548a
--- /dev/null
+++ b/core/fpdfapi/cmaps/fpdf_cmaps.h
@@ -0,0 +1,39 @@
+// 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_FPDFAPI_CMAPS_FPDF_CMAPS_H_
+#define CORE_FPDFAPI_CMAPS_FPDF_CMAPS_H_
+
+#include <stdint.h>
+
+#include "core/fxcrt/fx_string.h"
+#include "third_party/base/span.h"
+
+struct FXCMAP_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 };
+
+  const char* m_Name;                     // Raw, POD struct.
+  const uint16_t* m_pWordMap;             // Raw, POD struct.
+  const FXCMAP_DWordCIDMap* m_pDWordMap;  // Raw, POD struct.
+  uint16_t m_WordCount;
+  uint16_t m_DWordCount;
+  MapType 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);
+
+#endif  // CORE_FPDFAPI_CMAPS_FPDF_CMAPS_H_
diff --git a/core/fpdfapi/cpdf_modulemgr.cpp b/core/fpdfapi/cpdf_modulemgr.cpp
deleted file mode 100644
index 678997a..0000000
--- a/core/fpdfapi/cpdf_modulemgr.cpp
+++ /dev/null
@@ -1,114 +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/fpdfapi/cpdf_modulemgr.h"
-
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
-#include "core/fxcodec/fx_codec.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef PDF_ENABLE_XFA_BMP
-#include "core/fxcodec/codec/ccodec_bmpmodule.h"
-#endif
-
-#ifdef PDF_ENABLE_XFA_GIF
-#include "core/fxcodec/codec/ccodec_gifmodule.h"
-#endif
-
-#ifdef PDF_ENABLE_XFA_PNG
-#include "core/fxcodec/codec/ccodec_pngmodule.h"
-#endif
-
-#ifdef PDF_ENABLE_XFA_TIFF
-#include "core/fxcodec/codec/ccodec_tiffmodule.h"
-#endif
-
-namespace {
-
-CPDF_ModuleMgr* g_pDefaultMgr = nullptr;
-
-}  // namespace
-
-// static
-CPDF_ModuleMgr* CPDF_ModuleMgr::Get() {
-  if (!g_pDefaultMgr)
-    g_pDefaultMgr = new CPDF_ModuleMgr;
-  return g_pDefaultMgr;
-}
-
-// static
-void CPDF_ModuleMgr::Destroy() {
-  delete g_pDefaultMgr;
-  g_pDefaultMgr = nullptr;
-}
-
-CPDF_ModuleMgr::CPDF_ModuleMgr() {}
-
-CPDF_ModuleMgr::~CPDF_ModuleMgr() {}
-
-void CPDF_ModuleMgr::Init() {
-  InitCodecModule();
-  InitPageModule();
-  LoadEmbeddedMaps();
-  LoadCodecModules();
-}
-
-void CPDF_ModuleMgr::LoadEmbeddedMaps() {
-  LoadEmbeddedGB1CMaps();
-  LoadEmbeddedJapan1CMaps();
-  LoadEmbeddedCNS1CMaps();
-  LoadEmbeddedKorea1CMaps();
-}
-
-void CPDF_ModuleMgr::LoadCodecModules() {
-#ifdef PDF_ENABLE_XFA_BMP
-  m_pCodecModule->SetBmpModule(pdfium::MakeUnique<CCodec_BmpModule>());
-#endif
-
-#ifdef PDF_ENABLE_XFA_GIF
-  m_pCodecModule->SetGifModule(pdfium::MakeUnique<CCodec_GifModule>());
-#endif
-
-#ifdef PDF_ENABLE_XFA_PNG
-  m_pCodecModule->SetPngModule(pdfium::MakeUnique<CCodec_PngModule>());
-#endif
-
-#ifdef PDF_ENABLE_XFA_TIFF
-  m_pCodecModule->SetTiffModule(pdfium::MakeUnique<CCodec_TiffModule>());
-#endif
-}
-
-void CPDF_ModuleMgr::InitCodecModule() {
-  m_pCodecModule = pdfium::MakeUnique<CCodec_ModuleMgr>();
-}
-
-void CPDF_ModuleMgr::InitPageModule() {
-  m_pPageModule = pdfium::MakeUnique<CPDF_PageModule>();
-}
-
-CCodec_FaxModule* CPDF_ModuleMgr::GetFaxModule() {
-  return m_pCodecModule->GetFaxModule();
-}
-
-CCodec_JpegModule* CPDF_ModuleMgr::GetJpegModule() {
-  return m_pCodecModule->GetJpegModule();
-}
-
-CCodec_JpxModule* CPDF_ModuleMgr::GetJpxModule() {
-  return m_pCodecModule->GetJpxModule();
-}
-
-CCodec_Jbig2Module* CPDF_ModuleMgr::GetJbig2Module() {
-  return m_pCodecModule->GetJbig2Module();
-}
-
-CCodec_IccModule* CPDF_ModuleMgr::GetIccModule() {
-  return m_pCodecModule->GetIccModule();
-}
-
-CCodec_FlateModule* CPDF_ModuleMgr::GetFlateModule() {
-  return m_pCodecModule->GetFlateModule();
-}
diff --git a/core/fpdfapi/cpdf_modulemgr.h b/core/fpdfapi/cpdf_modulemgr.h
deleted file mode 100644
index 6b83bc7..0000000
--- a/core/fpdfapi/cpdf_modulemgr.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_CPDF_MODULEMGR_H_
-#define CORE_FPDFAPI_CPDF_MODULEMGR_H_
-
-#include <memory>
-#include <utility>
-
-class CCodec_FaxModule;
-class CCodec_FlateModule;
-class CCodec_IccModule;
-class CCodec_Jbig2Module;
-class CCodec_JpegModule;
-class CCodec_JpxModule;
-class CCodec_ModuleMgr;
-class CPDF_PageModule;
-
-class CFSDK_UnsupportInfo_Adapter {
- public:
-  explicit CFSDK_UnsupportInfo_Adapter(void* unsp_info)
-      : m_unsp_info(unsp_info) {}
-
-  void* GetUnspInfo() const { return m_unsp_info; }
-
- private:
-  void* const m_unsp_info;
-};
-
-class CPDF_ModuleMgr {
- public:
-  static CPDF_ModuleMgr* Get();
-  static void Destroy();
-  static const int kFileBufSize = 512;
-
-  void Init();
-
-  void SetUnsupportInfoAdapter(
-      std::unique_ptr<CFSDK_UnsupportInfo_Adapter> pAdapter) {
-    m_pUnsupportInfoAdapter = std::move(pAdapter);
-  }
-  CFSDK_UnsupportInfo_Adapter* GetUnsupportInfoAdapter() const {
-    return m_pUnsupportInfoAdapter.get();
-  }
-
-  CCodec_ModuleMgr* GetCodecModule() const { return m_pCodecModule.get(); }
-  CPDF_PageModule* GetPageModule() const { return m_pPageModule.get(); }
-
-  CCodec_FaxModule* GetFaxModule();
-  CCodec_JpegModule* GetJpegModule();
-  CCodec_JpxModule* GetJpxModule();
-  CCodec_Jbig2Module* GetJbig2Module();
-  CCodec_IccModule* GetIccModule();
-  CCodec_FlateModule* GetFlateModule();
-
- private:
-  CPDF_ModuleMgr();
-  ~CPDF_ModuleMgr();
-
-  void InitCodecModule();
-  void InitPageModule();
-  void LoadEmbeddedMaps();
-  void LoadCodecModules();
-
-  void LoadEmbeddedGB1CMaps();
-  void LoadEmbeddedCNS1CMaps();
-  void LoadEmbeddedJapan1CMaps();
-  void LoadEmbeddedKorea1CMaps();
-
-  std::unique_ptr<CCodec_ModuleMgr> m_pCodecModule;
-  std::unique_ptr<CPDF_PageModule> m_pPageModule;
-  std::unique_ptr<CFSDK_UnsupportInfo_Adapter> m_pUnsupportInfoAdapter;
-};
-
-#endif  // CORE_FPDFAPI_CPDF_MODULEMGR_H_
diff --git a/core/fpdfapi/cpdf_pagerendercontext.cpp b/core/fpdfapi/cpdf_pagerendercontext.cpp
deleted file mode 100644
index 39a881c..0000000
--- a/core/fpdfapi/cpdf_pagerendercontext.cpp
+++ /dev/null
@@ -1,18 +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/cpdf_pagerendercontext.h"
-
-#include "core/fpdfapi/render/cpdf_progressiverenderer.h"
-#include "core/fpdfapi/render/cpdf_rendercontext.h"
-#include "core/fpdfapi/render/cpdf_renderoptions.h"
-#include "core/fpdfdoc/cpdf_annotlist.h"
-#include "core/fpdfdoc/cpdf_occontext.h"
-#include "core/fxge/cfx_renderdevice.h"
-
-CPDF_PageRenderContext::CPDF_PageRenderContext() {}
-
-CPDF_PageRenderContext::~CPDF_PageRenderContext() {}
diff --git a/core/fpdfapi/cpdf_pagerendercontext.h b/core/fpdfapi/cpdf_pagerendercontext.h
deleted file mode 100644
index 5cbcdb7..0000000
--- a/core/fpdfapi/cpdf_pagerendercontext.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_CPDF_PAGERENDERCONTEXT_H_
-#define CORE_FPDFAPI_CPDF_PAGERENDERCONTEXT_H_
-
-#include <memory>
-
-class CFX_RenderDevice;
-class CPDF_AnnotList;
-class CPDF_ProgressiveRenderer;
-class CPDF_RenderContext;
-class CPDF_RenderOptions;
-
-// Everything about rendering is put here: for OOM recovery
-class CPDF_PageRenderContext {
- public:
-  CPDF_PageRenderContext();
-  ~CPDF_PageRenderContext();
-
-  // Specific destruction order required.
-  std::unique_ptr<CPDF_AnnotList> m_pAnnots;
-  std::unique_ptr<CPDF_RenderOptions> m_pOptions;
-  std::unique_ptr<CFX_RenderDevice> m_pDevice;
-  std::unique_ptr<CPDF_RenderContext> m_pContext;
-  std::unique_ptr<CPDF_ProgressiveRenderer> m_pRenderer;
-};
-
-#endif  // CORE_FPDFAPI_CPDF_PAGERENDERCONTEXT_H_
diff --git a/core/fpdfapi/edit/Android.bp b/core/fpdfapi/edit/Android.bp
new file mode 100644
index 0000000..bbd2b63
--- /dev/null
+++ b/core/fpdfapi/edit/Android.bp
@@ -0,0 +1,22 @@
+cc_library_static {
+    name: "libpdfium-edit",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-fxcrt",
+        "libpdfium-font",
+        "libpdfium-page",
+        "libpdfium-parser",
+        "libpdfium-skia_shared",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/core/fpdfapi/edit/BUILD.gn b/core/fpdfapi/edit/BUILD.gn
new file mode 100644
index 0000000..cda86e63
--- /dev/null
+++ b/core/fpdfapi/edit/BUILD.gn
@@ -0,0 +1,49 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+source_set("edit") {
+  sources = [
+    "cpdf_contentstream_write_utils.cpp",
+    "cpdf_contentstream_write_utils.h",
+    "cpdf_creator.cpp",
+    "cpdf_creator.h",
+    "cpdf_pagecontentgenerator.cpp",
+    "cpdf_pagecontentgenerator.h",
+    "cpdf_pagecontentmanager.cpp",
+    "cpdf_pagecontentmanager.h",
+    "cpdf_stringarchivestream.cpp",
+    "cpdf_stringarchivestream.h",
+  ]
+  configs += [ "../../../:pdfium_core_config" ]
+  deps = [
+    "../../../constants",
+    "../../../third_party:skia_shared",
+    "../../fxcrt",
+    "../font",
+    "../page",
+    "../parser",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [ "cpdf_pagecontentgenerator_unittest.cpp" ]
+  deps = [
+    ":edit",
+    "../../fxge",
+    "../font",
+    "../page",
+    "../parser",
+    "../render",
+  ]
+  pdfium_root_dir = "../../../"
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "cpdf_creator_embeddertest.cpp" ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/core/fpdfapi/edit/DEPS b/core/fpdfapi/edit/DEPS
new file mode 100644
index 0000000..bcfd0a2
--- /dev/null
+++ b/core/fpdfapi/edit/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "cpdf_contentstream_write_utils.cpp": [
+    '+third_party/skia_shared',
+  ]
+}
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
new file mode 100644
index 0000000..28165b1
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
+
+#include "third_party/skia_shared/SkFloatToDecimal.h"
+
+std::ostream& WriteFloat(std::ostream& stream, float value) {
+  char buffer[pdfium::skia::kMaximumSkFloatToDecimalLength];
+  unsigned size = pdfium::skia::SkFloatToDecimal(value, buffer);
+  stream.write(buffer, size);
+  return stream;
+}
+
+std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix) {
+  WriteFloat(ar, matrix.a) << " ";
+  WriteFloat(ar, matrix.b) << " ";
+  WriteFloat(ar, matrix.c) << " ";
+  WriteFloat(ar, matrix.d) << " ";
+  WriteFloat(ar, matrix.e) << " ";
+  WriteFloat(ar, matrix.f);
+  return ar;
+}
+
+std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point) {
+  WriteFloat(ar, point.x) << " ";
+  WriteFloat(ar, point.y);
+  return ar;
+}
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
new file mode 100644
index 0000000..3e14c9f
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
@@ -0,0 +1,16 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
+#define CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
+
+#include <ostream>
+
+#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);
+
+#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 e31da13..65ddc45 100644
--- a/core/fpdfapi/edit/cpdf_creator.cpp
+++ b/core/fpdfapi/edit/cpdf_creator.cpp
@@ -8,32 +8,39 @@
 
 #include <algorithm>
 
-#include "core/fpdfapi/edit/cpdf_encryptor.h"
-#include "core/fpdfapi/edit/cpdf_flateencoder.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_crypto_handler.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_encryptor.h"
+#include "core/fpdfapi/parser/cpdf_flateencoder.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #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/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/cpdf_syntax_parser.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.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"
 
 namespace {
 
 const size_t kArchiveBufferSize = 32768;
 
-class CFX_FileBufferArchive : public IFX_ArchiveStream {
+class CFX_FileBufferArchive final : public IFX_ArchiveStream {
  public:
-  explicit CFX_FileBufferArchive(const RetainPtr<IFX_WriteStream>& archive);
+  explicit CFX_FileBufferArchive(
+      const 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(const ByteStringView& str) override;
+  bool WriteString(ByteStringView str) override;
 
   FX_FILESIZE CurrentOffset() const override { return offset_; }
 
@@ -42,12 +49,12 @@
 
   FX_FILESIZE offset_;
   size_t current_length_;
-  std::vector<uint8_t> buffer_;
-  RetainPtr<IFX_WriteStream> backing_file_;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> buffer_;
+  RetainPtr<IFX_RetainableWriteStream> backing_file_;
 };
 
 CFX_FileBufferArchive::CFX_FileBufferArchive(
-    const RetainPtr<IFX_WriteStream>& file)
+    const RetainPtr<IFX_RetainableWriteStream>& file)
     : offset_(0),
       current_length_(0),
       buffer_(kArchiveBufferSize),
@@ -70,7 +77,8 @@
 }
 
 bool CFX_FileBufferArchive::WriteBlock(const void* pBuf, size_t size) {
-  ASSERT(pBuf && size > 0);
+  ASSERT(pBuf);
+  ASSERT(size > 0);
 
   const uint8_t* buffer = reinterpret_cast<const uint8_t*>(pBuf);
   size_t temp_size = size;
@@ -86,7 +94,7 @@
     buffer += buf_size;
   }
 
-  pdfium::base::CheckedNumeric<FX_FILESIZE> safe_offset = offset_;
+  FX_SAFE_FILESIZE safe_offset = offset_;
   safe_offset += size;
   if (!safe_offset.IsValid())
     return false;
@@ -105,215 +113,58 @@
   return WriteBlock(buf, strlen(buf));
 }
 
-bool CFX_FileBufferArchive::WriteString(const ByteStringView& str) {
+bool CFX_FileBufferArchive::WriteString(ByteStringView str) {
   return WriteBlock(str.raw_str(), str.GetLength());
 }
 
-std::vector<uint8_t> GenerateFileID(uint32_t dwSeed1, uint32_t dwSeed2) {
-  std::vector<uint8_t> buffer(sizeof(uint32_t) * 4);
-  uint32_t* pBuffer = reinterpret_cast<uint32_t*>(buffer.data());
-  void* pContext = FX_Random_MT_Start(dwSeed1);
-  for (int i = 0; i < 2; ++i)
-    *pBuffer++ = FX_Random_MT_Generate(pContext);
-
-  FX_Random_MT_Close(pContext);
-  pContext = FX_Random_MT_Start(dwSeed2);
-  for (int i = 0; i < 2; ++i)
-    *pBuffer++ = FX_Random_MT_Generate(pContext);
-
-  FX_Random_MT_Close(pContext);
-  return buffer;
+ByteString GenerateFileID(uint32_t dwSeed1, uint32_t dwSeed2) {
+  uint32_t buffer[4];
+  void* pContext1 = FX_Random_MT_Start(dwSeed1);
+  void* pContext2 = FX_Random_MT_Start(dwSeed2);
+  buffer[0] = FX_Random_MT_Generate(pContext1);
+  buffer[1] = FX_Random_MT_Generate(pContext1);
+  buffer[2] = FX_Random_MT_Generate(pContext2);
+  buffer[3] = FX_Random_MT_Generate(pContext2);
+  FX_Random_MT_Close(pContext1);
+  FX_Random_MT_Close(pContext2);
+  return ByteString(pdfium::as_bytes<uint32_t>(buffer));
 }
 
-int32_t OutputIndex(IFX_ArchiveStream* archive, FX_FILESIZE offset) {
-  if (!archive->WriteByte(static_cast<uint8_t>(offset >> 24)) ||
-      !archive->WriteByte(static_cast<uint8_t>(offset >> 16)) ||
-      !archive->WriteByte(static_cast<uint8_t>(offset >> 8)) ||
-      !archive->WriteByte(static_cast<uint8_t>(offset)) ||
-      !archive->WriteByte(0)) {
-    return -1;
-  }
-  return 0;
+bool OutputIndex(IFX_ArchiveStream* archive, FX_FILESIZE offset) {
+  return archive->WriteByte(static_cast<uint8_t>(offset >> 24)) &&
+         archive->WriteByte(static_cast<uint8_t>(offset >> 16)) &&
+         archive->WriteByte(static_cast<uint8_t>(offset >> 8)) &&
+         archive->WriteByte(static_cast<uint8_t>(offset)) &&
+         archive->WriteByte(0);
 }
 
 }  // namespace
 
 CPDF_Creator::CPDF_Creator(CPDF_Document* pDoc,
-                           const RetainPtr<IFX_WriteStream>& archive)
+                           const RetainPtr<IFX_RetainableWriteStream>& archive)
     : m_pDocument(pDoc),
       m_pParser(pDoc->GetParser()),
-      m_bSecurityChanged(false),
       m_pEncryptDict(m_pParser ? m_pParser->GetEncryptDict() : nullptr),
-      m_dwEncryptObjNum(0),
       m_pSecurityHandler(m_pParser ? m_pParser->GetSecurityHandler() : nullptr),
-      m_pMetadata(nullptr),
       m_dwLastObjNum(m_pDocument->GetLastObjNum()),
-      m_Archive(pdfium::MakeUnique<CFX_FileBufferArchive>(archive)),
-      m_SavedOffset(0),
-      m_iStage(-1),
-      m_dwFlags(0),
-      m_CurObjNum(0),
-      m_XrefStart(0),
-      m_pIDArray(nullptr),
-      m_FileVersion(0) {}
+      m_Archive(pdfium::MakeUnique<CFX_FileBufferArchive>(archive)) {}
 
 CPDF_Creator::~CPDF_Creator() {}
 
-bool CPDF_Creator::WriteStream(const CPDF_Object* pStream,
-                               uint32_t objnum,
-                               CPDF_CryptoHandler* pCrypto) {
-  CPDF_FlateEncoder encoder(const_cast<CPDF_Stream*>(pStream->AsStream()),
-                            pStream != m_pMetadata);
-  CPDF_Encryptor encryptor(pCrypto, objnum, encoder.GetData(),
-                           encoder.GetSize());
-  if (static_cast<uint32_t>(encoder.GetDict()->GetIntegerFor("Length")) !=
-      encryptor.GetSize()) {
-    encoder.CloneDict();
-    encoder.GetDict()->SetNewFor<CPDF_Number>(
-        "Length", static_cast<int>(encryptor.GetSize()));
-  }
-
-  if (!WriteDirectObj(objnum, encoder.GetDict(), true) ||
-      !m_Archive->WriteString("stream\r\n")) {
-    return false;
-  }
-
-  // Allow for empty streams.
-  if (encryptor.GetSize() > 0 &&
-      !m_Archive->WriteBlock(encryptor.GetData(), encryptor.GetSize())) {
-    return false;
-  }
-
-  return m_Archive->WriteString("\r\nendstream");
-}
-
 bool CPDF_Creator::WriteIndirectObj(uint32_t objnum, const CPDF_Object* pObj) {
   if (!m_Archive->WriteDWord(objnum) || !m_Archive->WriteString(" 0 obj\r\n"))
     return false;
 
-  if (pObj->IsStream()) {
-    CPDF_CryptoHandler* pHandler =
-        pObj != m_pMetadata ? GetCryptoHandler() : nullptr;
-    if (!WriteStream(pObj, objnum, pHandler))
-      return false;
-  } else if (!WriteDirectObj(objnum, pObj, true)) {
+  std::unique_ptr<CPDF_Encryptor> encryptor;
+  if (GetCryptoHandler() && pObj != m_pEncryptDict)
+    encryptor = pdfium::MakeUnique<CPDF_Encryptor>(GetCryptoHandler(), objnum);
+
+  if (!pObj->WriteTo(m_Archive.get(), encryptor.get()))
     return false;
-  }
 
   return m_Archive->WriteString("\r\nendobj\r\n");
 }
 
-bool CPDF_Creator::WriteDirectObj(uint32_t objnum,
-                                  const CPDF_Object* pObj,
-                                  bool bEncrypt) {
-  switch (pObj->GetType()) {
-    case CPDF_Object::BOOLEAN:
-    case CPDF_Object::NAME:
-    case CPDF_Object::NULLOBJ:
-    case CPDF_Object::NUMBER:
-    case CPDF_Object::REFERENCE:
-      if (!pObj->WriteTo(m_Archive.get()))
-        return false;
-      break;
-
-    case CPDF_Object::STRING: {
-      ByteString str = pObj->GetString();
-      bool bHex = pObj->AsString()->IsHex();
-      if (!GetCryptoHandler() || !bEncrypt) {
-        if (!pObj->WriteTo(m_Archive.get()))
-          return false;
-        break;
-      }
-      CPDF_Encryptor encryptor(GetCryptoHandler(), objnum,
-                               (uint8_t*)str.c_str(), str.GetLength());
-      ByteString content = PDF_EncodeString(
-          ByteString(encryptor.GetData(), encryptor.GetSize()), bHex);
-      if (!m_Archive->WriteString(content.AsStringView()))
-        return false;
-      break;
-    }
-    case CPDF_Object::STREAM: {
-      CPDF_FlateEncoder encoder(const_cast<CPDF_Stream*>(pObj->AsStream()),
-                                true);
-      CPDF_Encryptor encryptor(GetCryptoHandler(), objnum, encoder.GetData(),
-                               encoder.GetSize());
-      if (static_cast<uint32_t>(encoder.GetDict()->GetIntegerFor("Length")) !=
-          encryptor.GetSize()) {
-        encoder.CloneDict();
-        encoder.GetDict()->SetNewFor<CPDF_Number>(
-            "Length", static_cast<int>(encryptor.GetSize()));
-      }
-      if (!WriteDirectObj(objnum, encoder.GetDict(), true) ||
-          !m_Archive->WriteString("stream\r\n") ||
-          !m_Archive->WriteBlock(encryptor.GetData(), encryptor.GetSize()) ||
-          !m_Archive->WriteString("\r\nendstream")) {
-        return false;
-      }
-
-      break;
-    }
-    case CPDF_Object::ARRAY: {
-      if (!m_Archive->WriteString("["))
-        return false;
-
-      const CPDF_Array* p = pObj->AsArray();
-      for (size_t i = 0; i < p->GetCount(); i++) {
-        CPDF_Object* pElement = p->GetObjectAt(i);
-        if (!pElement->IsInline()) {
-          if (!m_Archive->WriteString(" ") ||
-              !m_Archive->WriteDWord(pElement->GetObjNum()) ||
-              !m_Archive->WriteString(" 0 R")) {
-            return false;
-          }
-        } else if (!WriteDirectObj(objnum, pElement, true)) {
-          return false;
-        }
-      }
-      if (!m_Archive->WriteString("]"))
-        return false;
-      break;
-    }
-    case CPDF_Object::DICTIONARY: {
-      if (!GetCryptoHandler() || pObj == m_pEncryptDict) {
-        if (!pObj->WriteTo(m_Archive.get()))
-          return false;
-        break;
-      }
-
-      if (!m_Archive->WriteString("<<"))
-        return false;
-
-      const CPDF_Dictionary* p = pObj->AsDictionary();
-      bool bSignDict = p->IsSignatureDict();
-      for (const auto& it : *p) {
-        bool bSignValue = false;
-        const ByteString& key = it.first;
-        CPDF_Object* pValue = it.second.get();
-        if (!m_Archive->WriteString("/") ||
-            !m_Archive->WriteString(PDF_NameEncode(key).AsStringView())) {
-          return false;
-        }
-
-        if (bSignDict && key == "Contents")
-          bSignValue = true;
-        if (!pValue->IsInline()) {
-          if (!m_Archive->WriteString(" ") ||
-              !m_Archive->WriteDWord(pValue->GetObjNum()) ||
-              !m_Archive->WriteString(" 0 R ")) {
-            return false;
-          }
-        } else if (!WriteDirectObj(objnum, pValue, !bSignValue)) {
-          return false;
-        }
-      }
-      if (!m_Archive->WriteString(">>"))
-        return false;
-      break;
-    }
-  }
-  return true;
-}
-
 bool CPDF_Creator::WriteOldIndirectObject(uint32_t objnum) {
   if (m_pParser->IsObjectFreeOrNull(objnum))
     return true;
@@ -359,31 +210,10 @@
   return true;
 }
 
-void CPDF_Creator::InitOldObjNumOffsets() {
-  if (!m_pParser)
-    return;
-
-  uint32_t dwStart = 0;
-  uint32_t dwEnd = m_pParser->GetLastObjNum();
-  while (dwStart <= dwEnd) {
-    while (dwStart <= dwEnd && m_pParser->IsObjectFreeOrNull(dwStart))
-      dwStart++;
-
-    if (dwStart > dwEnd)
-      break;
-
-    uint32_t j = dwStart;
-    while (j <= dwEnd && !m_pParser->IsObjectFreeOrNull(j))
-      j++;
-
-    dwStart = j;
-  }
-}
-
 void CPDF_Creator::InitNewObjNumOffsets() {
   for (const auto& pair : *m_pDocument) {
     const uint32_t objnum = pair.first;
-    if (IsIncremental() ||
+    if (m_IsIncremental ||
         pair.second->GetObjNum() == CPDF_Object::kInvalidObjNum) {
       continue;
     }
@@ -397,22 +227,20 @@
   }
 }
 
-int32_t CPDF_Creator::WriteDoc_Stage1() {
-  ASSERT(m_iStage > -1 || m_iStage < 20);
-  if (m_iStage == 0) {
-    if (!m_pParser)
-      m_dwFlags &= ~FPDFCREATE_INCREMENTAL;
-    if (m_bSecurityChanged && IsOriginal())
-      m_dwFlags &= ~FPDFCREATE_INCREMENTAL;
+CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage1() {
+  ASSERT(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 = pDict ? pDict->GetDirectObjectFor("Metadata") : nullptr;
-    m_iStage = 10;
+    m_pMetadata.Reset(pDict ? pDict->GetDirectObjectFor("Metadata") : nullptr);
+    m_iStage = Stage::kWriteHeader10;
   }
-  if (m_iStage == 10) {
-    if (!IsIncremental()) {
+  if (m_iStage == Stage::kWriteHeader10) {
+    if (!m_IsIncremental) {
       if (!m_Archive->WriteString("%PDF-1."))
-        return -1;
+        return Stage::kInvalid;
 
       int32_t version = 7;
       if (m_FileVersion)
@@ -422,37 +250,32 @@
 
       if (!m_Archive->WriteDWord(version % 10) ||
           !m_Archive->WriteString("\r\n%\xA1\xB3\xC5\xD7\r\n")) {
-        return -1;
+        return Stage::kInvalid;
       }
-
-      InitOldObjNumOffsets();
-      m_iStage = 20;
+      m_iStage = Stage::kInitWriteObjs20;
     } else {
-      m_SavedOffset = m_pParser->GetFileAccess()->GetSize();
-      m_iStage = 15;
+      m_SavedOffset = m_pParser->GetSyntax()->GetDocumentSize();
+      m_iStage = Stage::kWriteIncremental15;
     }
   }
-  if (m_iStage == 15) {
-    if (IsOriginal() && m_SavedOffset > 0) {
-      RetainPtr<IFX_SeekableReadStream> pSrcFile = m_pParser->GetFileAccess();
-      std::vector<uint8_t> buffer(4096);
+  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) {
-        uint32_t block_size = src_size > 4096 ? 4096 : src_size;
-        if (!pSrcFile->ReadBlock(buffer.data(),
-                                 m_Archive->CurrentOffset() - src_size,
-                                 block_size)) {
-          return -1;
+        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 -1;
+          return Stage::kInvalid;
 
         src_size -= block_size;
       }
     }
-    if (IsOriginal() && m_pParser->GetLastXRefOffset() == 0) {
-      InitOldObjNumOffsets();
-
+    if (m_IsOriginal && m_pParser->GetLastXRefOffset() == 0) {
       for (uint32_t num = 0; num <= m_pParser->GetLastObjNum(); ++num) {
         if (m_pParser->IsObjectFreeOrNull(num))
           continue;
@@ -460,84 +283,85 @@
         m_ObjectOffsets[num] = m_pParser->GetObjectPositionOrZero(num);
       }
     }
-    m_iStage = 20;
+    m_iStage = Stage::kInitWriteObjs20;
   }
   InitNewObjNumOffsets();
   return m_iStage;
 }
 
-int32_t CPDF_Creator::WriteDoc_Stage2() {
-  ASSERT(m_iStage >= 20 || m_iStage < 30);
-  if (m_iStage == 20) {
-    if (!IsIncremental() && m_pParser) {
+CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage2() {
+  ASSERT(m_iStage >= Stage::kInitWriteObjs20 ||
+         m_iStage < Stage::kInitWriteXRefs80);
+  if (m_iStage == Stage::kInitWriteObjs20) {
+    if (!m_IsIncremental && m_pParser) {
       m_CurObjNum = 0;
-      m_iStage = 21;
+      m_iStage = Stage::kWriteOldObjs21;
     } else {
-      m_iStage = 25;
+      m_iStage = Stage::kInitWriteNewObjs25;
     }
   }
-  if (m_iStage == 21) {
+  if (m_iStage == Stage::kWriteOldObjs21) {
     if (!WriteOldObjs())
-      return -1;
+      return Stage::kInvalid;
 
-    m_iStage = 25;
+    m_iStage = Stage::kInitWriteNewObjs25;
   }
-  if (m_iStage == 25) {
+  if (m_iStage == Stage::kInitWriteNewObjs25) {
     m_CurObjNum = 0;
-    m_iStage = 26;
+    m_iStage = Stage::kWriteNewObjs26;
   }
-  if (m_iStage == 26) {
+  if (m_iStage == Stage::kWriteNewObjs26) {
     if (!WriteNewObjs())
-      return -1;
+      return Stage::kInvalid;
 
-    m_iStage = 27;
+    m_iStage = Stage::kWriteEncryptDict27;
   }
-  if (m_iStage == 27) {
+  if (m_iStage == Stage::kWriteEncryptDict27) {
     if (m_pEncryptDict && m_pEncryptDict->IsInline()) {
       m_dwLastObjNum += 1;
       FX_FILESIZE saveOffset = m_Archive->CurrentOffset();
       if (!WriteIndirectObj(m_dwLastObjNum, m_pEncryptDict.Get()))
-        return -1;
+        return Stage::kInvalid;
 
       m_ObjectOffsets[m_dwLastObjNum] = saveOffset;
-      m_dwEncryptObjNum = m_dwLastObjNum;
-      if (IsIncremental())
+      if (m_IsIncremental)
         m_NewObjNumArray.push_back(m_dwLastObjNum);
     }
-    m_iStage = 80;
+    m_iStage = Stage::kInitWriteXRefs80;
   }
   return m_iStage;
 }
 
-int32_t CPDF_Creator::WriteDoc_Stage3() {
-  ASSERT(m_iStage >= 80 || m_iStage < 90);
+CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage3() {
+  ASSERT(m_iStage >= Stage::kInitWriteXRefs80 ||
+         m_iStage < Stage::kWriteTrailerAndFinish90);
 
   uint32_t dwLastObjNum = m_dwLastObjNum;
-  if (m_iStage == 80) {
+  if (m_iStage == Stage::kInitWriteXRefs80) {
     m_XrefStart = m_Archive->CurrentOffset();
-    if (!IsIncremental() || !m_pParser->IsXRefStream()) {
-      if (!IsIncremental() || m_pParser->GetLastXRefOffset() == 0) {
+    if (!m_IsIncremental || !m_pParser->IsXRefStream()) {
+      if (!m_IsIncremental || m_pParser->GetLastXRefOffset() == 0) {
         ByteString str;
         str = pdfium::ContainsKey(m_ObjectOffsets, 1)
                   ? "xref\r\n"
                   : "xref\r\n0 1\r\n0000000000 65535 f\r\n";
         if (!m_Archive->WriteString(str.AsStringView()))
-          return -1;
+          return Stage::kInvalid;
 
         m_CurObjNum = 1;
-        m_iStage = 81;
+        m_iStage = Stage::kWriteXrefsNotIncremental81;
       } else {
         if (!m_Archive->WriteString("xref\r\n"))
-          return -1;
+          return Stage::kInvalid;
 
         m_CurObjNum = 0;
-        m_iStage = 82;
+        m_iStage = Stage::kWriteXrefsIncremental82;
       }
     } else {
-      m_iStage = 90;
+      m_iStage = Stage::kWriteTrailerAndFinish90;
     }
   }
-  if (m_iStage == 81) {
+  if (m_iStage == Stage::kWriteXrefsNotIncremental81) {
     ByteString str;
     uint32_t i = m_CurObjNum;
     uint32_t j;
@@ -557,20 +381,20 @@
       else
         str = ByteString::Format("%d %d\r\n", i, j - i);
 
-      if (!m_Archive->WriteBlock(str.c_str(), str.GetLength()))
-        return -1;
+      if (!m_Archive->WriteString(str.AsStringView()))
+        return Stage::kInvalid;
 
       while (i < j) {
         str = ByteString::Format("%010d 00000 n\r\n", m_ObjectOffsets[i++]);
-        if (!m_Archive->WriteBlock(str.c_str(), str.GetLength()))
-          return -1;
+        if (!m_Archive->WriteString(str.AsStringView()))
+          return Stage::kInvalid;
       }
       if (i > dwLastObjNum)
         break;
     }
-    m_iStage = 90;
+    m_iStage = Stage::kWriteTrailerAndFinish90;
   }
-  if (m_iStage == 82) {
+  if (m_iStage == Stage::kWriteXrefsIncremental82) {
     ByteString str;
     uint32_t iCount = pdfium::CollectionSize<uint32_t>(m_NewObjNumArray);
     uint32_t i = m_CurObjNum;
@@ -591,133 +415,129 @@
       else
         str = ByteString::Format("%d %d\r\n", objnum, j - i);
 
-      if (!m_Archive->WriteBlock(str.c_str(), str.GetLength()))
-        return -1;
+      if (!m_Archive->WriteString(str.AsStringView()))
+        return Stage::kInvalid;
 
       while (i < j) {
         objnum = m_NewObjNumArray[i++];
         str = ByteString::Format("%010d 00000 n\r\n", m_ObjectOffsets[objnum]);
-        if (!m_Archive->WriteBlock(str.c_str(), str.GetLength()))
-          return -1;
+        if (!m_Archive->WriteString(str.AsStringView()))
+          return Stage::kInvalid;
       }
     }
-    m_iStage = 90;
+    m_iStage = Stage::kWriteTrailerAndFinish90;
   }
   return m_iStage;
 }
 
-int32_t CPDF_Creator::WriteDoc_Stage4() {
-  ASSERT(m_iStage >= 90);
+CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() {
+  ASSERT(m_iStage >= Stage::kWriteTrailerAndFinish90);
 
-  bool bXRefStream = IsIncremental() && m_pParser->IsXRefStream();
+  bool bXRefStream = m_IsIncremental && m_pParser->IsXRefStream();
   if (!bXRefStream) {
     if (!m_Archive->WriteString("trailer\r\n<<"))
-      return -1;
+      return Stage::kInvalid;
   } else {
     if (!m_Archive->WriteDWord(m_pDocument->GetLastObjNum() + 1) ||
         !m_Archive->WriteString(" 0 obj <<")) {
-      return -1;
+      return Stage::kInvalid;
     }
   }
 
   if (m_pParser) {
-    std::unique_ptr<CPDF_Dictionary> p = m_pParser->GetCombinedTrailer();
-    for (const auto& it : *p) {
+    RetainPtr<CPDF_Dictionary> p = m_pParser->GetCombinedTrailer();
+    CPDF_DictionaryLocker locker(p.Get());
+    for (const auto& it : locker) {
       const ByteString& key = it.first;
-      CPDF_Object* pValue = it.second.get();
+      CPDF_Object* pValue = it.second.Get();
       if (key == "Encrypt" || key == "Size" || key == "Filter" ||
           key == "Index" || key == "Length" || key == "Prev" || key == "W" ||
-          key == "XRefStm" || key == "ID") {
+          key == "XRefStm" || key == "ID" || key == "DecodeParms" ||
+          key == "Type") {
         continue;
       }
       if (!m_Archive->WriteString(("/")) ||
           !m_Archive->WriteString(PDF_NameEncode(key).AsStringView())) {
-        return -1;
+        return Stage::kInvalid;
       }
-      if (!pValue->IsInline()) {
-        if (!m_Archive->WriteString(" ") ||
-            !m_Archive->WriteDWord(pValue->GetObjNum()) ||
-            !m_Archive->WriteString(" 0 R ")) {
-          return -1;
-        }
-      } else if (!pValue->WriteTo(m_Archive.get())) {
-        return -1;
-      }
+      if (!pValue->WriteTo(m_Archive.get(), nullptr))
+        return Stage::kInvalid;
     }
   } else {
     if (!m_Archive->WriteString("\r\n/Root ") ||
         !m_Archive->WriteDWord(m_pDocument->GetRoot()->GetObjNum()) ||
         !m_Archive->WriteString(" 0 R\r\n")) {
-      return -1;
+      return Stage::kInvalid;
     }
     if (m_pDocument->GetInfo()) {
       if (!m_Archive->WriteString("/Info ") ||
           !m_Archive->WriteDWord(m_pDocument->GetInfo()->GetObjNum()) ||
           !m_Archive->WriteString(" 0 R\r\n")) {
-        return -1;
+        return Stage::kInvalid;
       }
     }
   }
   if (m_pEncryptDict) {
     if (!m_Archive->WriteString("/Encrypt"))
-      return -1;
+      return Stage::kInvalid;
 
     uint32_t dwObjNum = m_pEncryptDict->GetObjNum();
     if (dwObjNum == 0)
       dwObjNum = m_pDocument->GetLastObjNum() + 1;
     if (!m_Archive->WriteString(" ") || !m_Archive->WriteDWord(dwObjNum) ||
         !m_Archive->WriteString(" 0 R ")) {
-      return -1;
+      return Stage::kInvalid;
     }
   }
 
   if (!m_Archive->WriteString("/Size ") ||
       !m_Archive->WriteDWord(m_dwLastObjNum + (bXRefStream ? 2 : 1))) {
-    return -1;
+    return Stage::kInvalid;
   }
-  if (IsIncremental()) {
+  if (m_IsIncremental) {
     FX_FILESIZE prev = m_pParser->GetLastXRefOffset();
     if (prev) {
       if (!m_Archive->WriteString("/Prev "))
-        return -1;
+        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)))
-        return -1;
+        return Stage::kInvalid;
     }
   }
   if (m_pIDArray) {
     if (!m_Archive->WriteString(("/ID")) ||
-        !m_pIDArray->WriteTo(m_Archive.get())) {
-      return -1;
+        !m_pIDArray->WriteTo(m_Archive.get(), nullptr)) {
+      return Stage::kInvalid;
     }
   }
   if (!bXRefStream) {
     if (!m_Archive->WriteString(">>"))
-      return -1;
+      return Stage::kInvalid;
   } else {
     if (!m_Archive->WriteString("/W[0 4 1]/Index["))
-      return -1;
-    if (IsIncremental() && m_pParser && m_pParser->GetLastXRefOffset() == 0) {
+      return Stage::kInvalid;
+    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))
           continue;
         if (!m_Archive->WriteDWord(i) || !m_Archive->WriteString(" 1 "))
-          return -1;
+          return Stage::kInvalid;
       }
       if (!m_Archive->WriteString("]/Length ") ||
           !m_Archive->WriteDWord(m_dwLastObjNum * 5) ||
           !m_Archive->WriteString(">>stream\r\n")) {
-        return -1;
+        return Stage::kInvalid;
       }
       for (i = 0; i < m_dwLastObjNum; i++) {
         auto it = m_ObjectOffsets.find(i);
         if (it == m_ObjectOffsets.end())
           continue;
-        OutputIndex(m_Archive.get(), it->second);
+        if (!OutputIndex(m_Archive.get(), it->second))
+          return Stage::kInvalid;
       }
     } else {
       size_t count = m_NewObjNumArray.size();
@@ -725,100 +545,104 @@
       for (i = 0; i < count; i++) {
         if (!m_Archive->WriteDWord(m_NewObjNumArray[i]) ||
             !m_Archive->WriteString(" 1 ")) {
-          return -1;
+          return Stage::kInvalid;
         }
       }
       if (!m_Archive->WriteString("]/Length ") ||
           !m_Archive->WriteDWord(count * 5) ||
           !m_Archive->WriteString(">>stream\r\n")) {
-        return -1;
+        return Stage::kInvalid;
       }
-      for (i = 0; i < count; ++i)
-        OutputIndex(m_Archive.get(), m_ObjectOffsets[m_NewObjNumArray[i]]);
+      for (i = 0; i < count; ++i) {
+        if (!OutputIndex(m_Archive.get(), m_ObjectOffsets[m_NewObjNumArray[i]]))
+          return Stage::kInvalid;
+      }
     }
     if (!m_Archive->WriteString("\r\nendstream"))
-      return -1;
+      return Stage::kInvalid;
   }
 
   if (!m_Archive->WriteString("\r\nstartxref\r\n"))
-    return -1;
+    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)) ||
       !m_Archive->WriteString("\r\n%%EOF\r\n")) {
-    return -1;
+    return Stage::kInvalid;
   }
 
-  m_iStage = 100;
+  m_iStage = Stage::kComplete100;
   return m_iStage;
 }
 
 bool CPDF_Creator::Create(uint32_t flags) {
-  m_dwFlags = flags;
-  m_iStage = 0;
+  m_IsIncremental = !!(flags & FPDFCREATE_INCREMENTAL);
+  m_IsOriginal = !(flags & FPDFCREATE_NO_ORIGINAL);
+
+  m_iStage = Stage::kInit0;
   m_dwLastObjNum = m_pDocument->GetLastObjNum();
   m_ObjectOffsets.clear();
   m_NewObjNumArray.clear();
 
   InitID();
-  return Continue() > -1;
+  return Continue();
 }
 
 void CPDF_Creator::InitID() {
-  const CPDF_Array* pOldIDArray = m_pParser ? m_pParser->GetIDArray() : nullptr;
+  ASSERT(!m_pIDArray);
 
-  bool idArrayPreExisting = !!m_pIDArray;
-  if (!idArrayPreExisting) {
-    m_pIDArray = pdfium::MakeUnique<CPDF_Array>();
-    CPDF_Object* pID1 = pOldIDArray ? pOldIDArray->GetObjectAt(0) : nullptr;
-    if (pID1) {
-      m_pIDArray->Add(pID1->Clone());
-    } else {
-      std::vector<uint8_t> buffer =
-          GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum);
-      ByteString bsBuffer(buffer.data(), buffer.size());
-      m_pIDArray->AddNew<CPDF_String>(bsBuffer, true);
-    }
+  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;
+  if (pID1) {
+    m_pIDArray->Add(pID1->Clone());
+  } else {
+    ByteString bsBuffer =
+        GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum);
+    m_pIDArray->AddNew<CPDF_String>(bsBuffer, true);
   }
 
   if (pOldIDArray) {
-    CPDF_Object* pID2 = pOldIDArray->GetObjectAt(1);
-    if (IsIncremental() && m_pEncryptDict && pID2) {
+    const CPDF_Object* pID2 = pOldIDArray->GetObjectAt(1);
+    if (m_IsIncremental && m_pEncryptDict && pID2) {
       m_pIDArray->Add(pID2->Clone());
       return;
     }
-    std::vector<uint8_t> buffer =
+    ByteString bsBuffer =
         GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum);
-    ByteString bsBuffer(buffer.data(), buffer.size());
     m_pIDArray->AddNew<CPDF_String>(bsBuffer, true);
     return;
   }
 
   m_pIDArray->Add(m_pIDArray->GetObjectAt(0)->Clone());
-  if (m_pEncryptDict && !pOldIDArray && m_pParser && !idArrayPreExisting) {
-    if (m_pEncryptDict->GetStringFor("Filter") == "Standard") {
-      ByteString user_pass = m_pParser->GetPassword();
-      m_pSecurityHandler = pdfium::MakeUnique<CPDF_SecurityHandler>();
-      m_pSecurityHandler->OnCreate(m_pEncryptDict.Get(), m_pIDArray.get(),
-                                   user_pass);
+  if (m_pEncryptDict) {
+    ASSERT(m_pParser);
+    int revision = m_pEncryptDict->GetIntegerFor("R");
+    if ((revision == 2 || revision == 3) &&
+        m_pEncryptDict->GetStringFor("Filter") == "Standard") {
+      m_pNewEncryptDict = ToDictionary(m_pEncryptDict->Clone());
+      m_pEncryptDict = m_pNewEncryptDict;
+      m_pSecurityHandler = pdfium::MakeRetain<CPDF_SecurityHandler>();
+      m_pSecurityHandler->OnCreate(m_pNewEncryptDict.Get(), m_pIDArray.Get(),
+                                   m_pParser->GetEncodedPassword());
       m_bSecurityChanged = true;
     }
   }
 }
 
-int32_t CPDF_Creator::Continue() {
-  if (m_iStage < 0)
-    return m_iStage;
+bool CPDF_Creator::Continue() {
+  if (m_iStage < Stage::kInit0)
+    return false;
 
-  int32_t iRet = 0;
-  while (m_iStage < 100) {
-    if (m_iStage < 20)
+  Stage iRet = Stage::kInit0;
+  while (m_iStage < Stage::kComplete100) {
+    if (m_iStage < Stage::kInitWriteObjs20)
       iRet = WriteDoc_Stage1();
-    else if (m_iStage < 30)
+    else if (m_iStage < Stage::kInitWriteXRefs80)
       iRet = WriteDoc_Stage2();
-    else if (m_iStage < 90)
+    else if (m_iStage < Stage::kWriteTrailerAndFinish90)
       iRet = WriteDoc_Stage3();
     else
       iRet = WriteDoc_Stage4();
@@ -827,11 +651,12 @@
       break;
   }
 
-  if (iRet < 1 || m_iStage == 100) {
-    m_iStage = -1;
-    return iRet > 99 ? 0 : (iRet < 1 ? -1 : iRet);
+  if (iRet <= Stage::kInit0 || m_iStage == Stage::kComplete100) {
+    m_iStage = Stage::kInvalid;
+    return iRet > Stage::kInit0;
   }
-  return m_iStage;
+
+  return m_iStage > Stage::kInvalid;
 }
 
 bool CPDF_Creator::SetFileVersion(int32_t fileVersion) {
@@ -845,6 +670,7 @@
   m_pSecurityHandler.Reset();
   m_bSecurityChanged = true;
   m_pEncryptDict = nullptr;
+  m_pNewEncryptDict.Reset();
 }
 
 CPDF_CryptoHandler* CPDF_Creator::GetCryptoHandler() {
diff --git a/core/fpdfapi/edit/cpdf_creator.h b/core/fpdfapi/edit/cpdf_creator.h
index 4c1d0f1..d85d8cf 100644
--- a/core/fpdfapi/edit/cpdf_creator.h
+++ b/core/fpdfapi/edit/cpdf_creator.h
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/maybe_owned.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
@@ -29,77 +28,69 @@
 
 class CPDF_Creator {
  public:
-  explicit CPDF_Creator(CPDF_Document* pDoc,
-                        const RetainPtr<IFX_WriteStream>& archive);
+  CPDF_Creator(CPDF_Document* pDoc,
+               const RetainPtr<IFX_RetainableWriteStream>& archive);
   ~CPDF_Creator();
 
   void RemoveSecurity();
   bool Create(uint32_t flags);
-  int32_t Continue();
   bool SetFileVersion(int32_t fileVersion);
 
-  IFX_ArchiveStream* GetArchive() { return m_Archive.get(); }
-
-  uint32_t GetNextObjectNumber() { return ++m_dwLastObjNum; }
-  uint32_t GetLastObjectNumber() const { return m_dwLastObjNum; }
-  CPDF_CryptoHandler* GetCryptoHandler();
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
-  CPDF_Array* GetIDArray() const { return m_pIDArray.get(); }
-  CPDF_Dictionary* GetEncryptDict() const { return m_pEncryptDict.Get(); }
-  uint32_t GetEncryptObjectNumber() const { return m_dwEncryptObjNum; }
-
-  uint32_t GetObjectOffset(uint32_t objnum) { return m_ObjectOffsets[objnum]; }
-  bool HasObjectNumber(uint32_t objnum) {
-    return m_ObjectOffsets.find(objnum) != m_ObjectOffsets.end();
-  }
-  void SetObjectOffset(uint32_t objnum, FX_FILESIZE offset) {
-    m_ObjectOffsets[objnum] = offset;
-  }
-  bool IsIncremental() const { return !!(m_dwFlags & FPDFCREATE_INCREMENTAL); }
-  bool IsOriginal() const { return !(m_dwFlags & FPDFCREATE_NO_ORIGINAL); }
-
  private:
+  enum class Stage {
+    kInvalid = -1,
+    kInit0 = 0,
+    kWriteHeader10 = 10,
+    kWriteIncremental15 = 15,
+    kInitWriteObjs20 = 20,
+    kWriteOldObjs21 = 21,
+    kInitWriteNewObjs25 = 25,
+    kWriteNewObjs26 = 26,
+    kWriteEncryptDict27 = 27,
+    kInitWriteXRefs80 = 80,
+    kWriteXrefsNotIncremental81 = 81,
+    kWriteXrefsIncremental82 = 82,
+    kWriteTrailerAndFinish90 = 90,
+    kComplete100 = 100,
+  };
+
+  bool Continue();
   void Clear();
 
-  void InitOldObjNumOffsets();
   void InitNewObjNumOffsets();
   void InitID();
 
-  int32_t WriteDoc_Stage1();
-  int32_t WriteDoc_Stage2();
-  int32_t WriteDoc_Stage3();
-  int32_t WriteDoc_Stage4();
+  CPDF_Creator::Stage WriteDoc_Stage1();
+  CPDF_Creator::Stage WriteDoc_Stage2();
+  CPDF_Creator::Stage WriteDoc_Stage3();
+  CPDF_Creator::Stage WriteDoc_Stage4();
 
   bool WriteOldIndirectObject(uint32_t objnum);
   bool WriteOldObjs();
   bool WriteNewObjs();
-  bool WriteDirectObj(uint32_t objnum, const CPDF_Object* pObj, bool bEncrypt);
   bool WriteIndirectObj(uint32_t objnum, const CPDF_Object* pObj);
 
-  bool WriteStream(const CPDF_Object* pStream,
-                   uint32_t objnum,
-                   CPDF_CryptoHandler* pCrypto);
-
-  bool IsXRefNeedEnd();
+  CPDF_CryptoHandler* GetCryptoHandler();
 
   UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<CPDF_Parser> const m_pParser;
-  bool m_bSecurityChanged;
-  UnownedPtr<CPDF_Dictionary> m_pEncryptDict;
-  uint32_t m_dwEncryptObjNum;
-  fxcrt::MaybeOwned<CPDF_SecurityHandler> m_pSecurityHandler;
-  UnownedPtr<CPDF_Object> m_pMetadata;
+  UnownedPtr<const 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;
-  int32_t m_iStage;
-  uint32_t m_dwFlags;
-  uint32_t m_CurObjNum;
-  FX_FILESIZE m_XrefStart;
+  FX_FILESIZE m_SavedOffset = 0;
+  Stage m_iStage = Stage::kInvalid;
+  uint32_t m_CurObjNum = 0;
+  FX_FILESIZE m_XrefStart = 0;
   std::map<uint32_t, FX_FILESIZE> m_ObjectOffsets;
   std::vector<uint32_t> m_NewObjNumArray;  // Sorted, ascending.
-  std::unique_ptr<CPDF_Array> m_pIDArray;
-  int32_t m_FileVersion;
+  RetainPtr<CPDF_Array> m_pIDArray;
+  int32_t m_FileVersion = 0;
+  bool m_bSecurityChanged = false;
+  bool m_IsIncremental = false;
+  bool m_IsOriginal = false;
 };
 
 #endif  // CORE_FPDFAPI_EDIT_CPDF_CREATOR_H_
diff --git a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp
index def7d50..9d849c1 100644
--- a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp
+++ b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp
@@ -2,16 +2,21 @@
 // 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 "core/fxcrt/fx_system.h"
+#include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_annot.h"
 #include "public/fpdf_edit.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
+#include "testing/fake_file_access.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/file_util.h"
 
 class CPDF_CreatorEmbedderTest : public EmbedderTest {};
 
@@ -25,11 +30,11 @@
   {
     // Do some read only operations.
     ASSERT_GE(1, FPDF_GetPageCount(document()));
-    FPDF_PAGE page = FPDF_LoadPage(document(), 0);
+    FPDF_PAGE page = LoadPage(0);
     ASSERT_TRUE(page);
-    FPDF_BITMAP new_bitmap =
-        RenderPageWithFlags(page, form_handle(), FPDF_ANNOT);
-    FPDFBitmap_Destroy(new_bitmap);
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    EXPECT_EQ(595, FPDFBitmap_GetWidth(bitmap.get()));
+    EXPECT_EQ(842, FPDFBitmap_GetHeight(bitmap.get()));
     UnloadPage(page);
   }
 
@@ -41,3 +46,48 @@
   // The sizes of saved docs should be equal.
   EXPECT_EQ(saved_doc_1.size(), saved_doc_2.size());
 }
+
+TEST_F(CPDF_CreatorEmbedderTest, BUG_873) {
+  EXPECT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  // Cannot match second part of the ID since it is randomly generated.
+  std::string saved_data = GetString();
+  const char kTrailerBeforeSecondID[] =
+      "trailer\r\n<</Info 9 0 R /Root 11 0 R /Size "
+      "36/ID[<D889EB6B9ADF88E5EDA7DC08FE85978B><";
+  ASSERT_THAT(saved_data, testing::HasSubstr(kTrailerBeforeSecondID));
+  size_t trailer_start = saved_data.find(kTrailerBeforeSecondID);
+  constexpr size_t kIdLen = 32;
+  size_t trailer_continuation =
+      trailer_start + strlen(kTrailerBeforeSecondID) + kIdLen;
+  std::string data_after_second_id = saved_data.substr(trailer_continuation);
+  EXPECT_THAT(data_after_second_id, testing::StartsWith(">]>>\r\n"));
+}
+
+TEST_F(CPDF_CreatorEmbedderTest, SaveLinearizedInfo) {
+  FileAccessForTesting file_acc("linearized.pdf");
+  FakeFileAccess fake_acc(&file_acc);
+
+  avail_ = FPDFAvail_Create(fake_acc.GetFileAvail(), fake_acc.GetFileAccess());
+  while (PDF_DATA_AVAIL !=
+         FPDFAvail_IsDocAvail(avail_, fake_acc.GetDownloadHints())) {
+    fake_acc.SetRequestedDataAvailable();
+  }
+
+  document_ = FPDFAvail_GetDocument(avail_, nullptr);
+  ASSERT_TRUE(document_);
+
+  // Load second page, to parse additional crossref sections.
+  while (PDF_DATA_AVAIL !=
+         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));
+  const std::string saved_doc = GetString();
+
+  EXPECT_THAT(saved_doc, ::testing::HasSubstr("/Info"));
+}
diff --git a/core/fpdfapi/edit/cpdf_encryptor.cpp b/core/fpdfapi/edit/cpdf_encryptor.cpp
deleted file mode 100644
index 858b5b6..0000000
--- a/core/fpdfapi/edit/cpdf_encryptor.cpp
+++ /dev/null
@@ -1,32 +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/edit/cpdf_encryptor.h"
-#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
-
-CPDF_Encryptor::CPDF_Encryptor(CPDF_CryptoHandler* pHandler,
-                               int objnum,
-                               uint8_t* src_data,
-                               uint32_t src_size)
-    : m_pData(nullptr), m_dwSize(0), m_bNewBuf(false) {
-  if (src_size == 0)
-    return;
-
-  if (!pHandler) {
-    m_pData = (uint8_t*)src_data;
-    m_dwSize = src_size;
-    return;
-  }
-  m_dwSize = pHandler->EncryptGetSize(objnum, 0, src_data, src_size);
-  m_pData = FX_Alloc(uint8_t, m_dwSize);
-  pHandler->EncryptContent(objnum, 0, src_data, src_size, m_pData, m_dwSize);
-  m_bNewBuf = true;
-}
-
-CPDF_Encryptor::~CPDF_Encryptor() {
-  if (m_bNewBuf)
-    FX_Free(m_pData);
-}
diff --git a/core/fpdfapi/edit/cpdf_encryptor.h b/core/fpdfapi/edit/cpdf_encryptor.h
deleted file mode 100644
index 00c9337..0000000
--- a/core/fpdfapi/edit/cpdf_encryptor.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_EDIT_CPDF_ENCRYPTOR_H_
-#define CORE_FPDFAPI_EDIT_CPDF_ENCRYPTOR_H_
-
-#include <stdint.h>
-
-class CPDF_CryptoHandler;
-
-class CPDF_Encryptor {
- public:
-  CPDF_Encryptor(CPDF_CryptoHandler* pHandler,
-                 int objnum,
-                 uint8_t* src_data,
-                 uint32_t src_size);
-  ~CPDF_Encryptor();
-
-  uint32_t GetSize() const { return m_dwSize; }
-  uint8_t* GetData() const { return m_pData; }
-
- private:
-  uint8_t* m_pData;
-  uint32_t m_dwSize;
-  bool m_bNewBuf;
-};
-
-#endif  // CORE_FPDFAPI_EDIT_CPDF_ENCRYPTOR_H_
diff --git a/core/fpdfapi/edit/cpdf_flateencoder.cpp b/core/fpdfapi/edit/cpdf_flateencoder.cpp
deleted file mode 100644
index 555da84..0000000
--- a/core/fpdfapi/edit/cpdf_flateencoder.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfapi/edit/cpdf_flateencoder.h"
-
-#include <memory>
-
-#include "core/fpdfapi/parser/cpdf_name.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
-
-CPDF_FlateEncoder::CPDF_FlateEncoder(CPDF_Stream* pStream, bool bFlateEncode)
-    : m_dwSize(0), m_pAcc(pdfium::MakeRetain<CPDF_StreamAcc>(pStream)) {
-  m_pAcc->LoadAllDataRaw();
-
-  bool bHasFilter = pStream && pStream->HasFilter();
-  if (bHasFilter && !bFlateEncode) {
-    auto pDestAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-    pDestAcc->LoadAllDataFiltered();
-
-    m_dwSize = pDestAcc->GetSize();
-    m_pData = pDestAcc->DetachData();
-    m_pDict = ToDictionary(pStream->GetDict()->Clone());
-    m_pDict->RemoveFor("Filter");
-    return;
-  }
-  if (bHasFilter || !bFlateEncode) {
-    m_pData = const_cast<uint8_t*>(m_pAcc->GetData());
-    m_dwSize = m_pAcc->GetSize();
-    m_pDict = pStream->GetDict();
-    return;
-  }
-
-  // TODO(thestig): Move to Init() and check return value.
-  uint8_t* buffer = nullptr;
-  ::FlateEncode(m_pAcc->GetData(), m_pAcc->GetSize(), &buffer, &m_dwSize);
-
-  m_pData = std::unique_ptr<uint8_t, FxFreeDeleter>(buffer);
-  m_pDict = ToDictionary(pStream->GetDict()->Clone());
-  m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(m_dwSize));
-  m_pDict->SetNewFor<CPDF_Name>("Filter", "FlateDecode");
-  m_pDict->RemoveFor("DecodeParms");
-}
-
-CPDF_FlateEncoder::~CPDF_FlateEncoder() {}
-
-void CPDF_FlateEncoder::CloneDict() {
-  if (m_pDict.IsOwned())
-    return;
-
-  m_pDict = ToDictionary(m_pDict->Clone());
-  ASSERT(m_pDict.IsOwned());
-}
diff --git a/core/fpdfapi/edit/cpdf_flateencoder.h b/core/fpdfapi/edit/cpdf_flateencoder.h
deleted file mode 100644
index 887816c..0000000
--- a/core/fpdfapi/edit/cpdf_flateencoder.h
+++ /dev/null
@@ -1,37 +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_EDIT_CPDF_FLATEENCODER_H_
-#define CORE_FPDFAPI_EDIT_CPDF_FLATEENCODER_H_
-
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/maybe_owned.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CPDF_Stream;
-
-class CPDF_FlateEncoder {
- public:
-  CPDF_FlateEncoder(CPDF_Stream* pStream, bool bFlateEncode);
-  ~CPDF_FlateEncoder();
-
-  void CloneDict();
-
-  uint32_t GetSize() const { return m_dwSize; }
-  uint8_t* GetData() const { return m_pData.Get(); }
-
-  CPDF_Dictionary* GetDict() { return m_pDict.Get(); }
-
- private:
-  uint32_t m_dwSize;
-  MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
-  MaybeOwned<CPDF_Dictionary> m_pDict;
-  RetainPtr<CPDF_StreamAcc> m_pAcc;
-};
-
-#endif  // CORE_FPDFAPI_EDIT_CPDF_FLATEENCODER_H_
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 8a08a84..77d23a2 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -6,10 +6,18 @@
 
 #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
 
+#include <map>
+#include <memory>
+#include <set>
 #include <tuple>
 #include <utility>
 
-#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
+#include "core/fpdfapi/edit/cpdf_pagecontentmanager.h"
+#include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
+#include "core/fpdfapi/font/cpdf_truetypefont.h"
+#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_image.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
@@ -25,19 +33,15 @@
 #include "core/fpdfapi/parser/cpdf_reference.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 "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 namespace {
 
-std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix) {
-  ar << matrix.a << " " << matrix.b << " " << matrix.c << " " << matrix.d << " "
-     << matrix.e << " " << matrix.f;
-  return ar;
-}
-
 bool GetColor(const CPDF_Color* pColor, float* rgb) {
   int intRGB[3];
-  if (!pColor ||
-      pColor->GetColorSpace() != CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB) ||
+  if (!pColor || !pColor->IsColorSpaceRGB() ||
       !pColor->GetRGB(&intRGB[0], &intRGB[1], &intRGB[2])) {
     return false;
   }
@@ -51,8 +55,8 @@
 
 CPDF_PageContentGenerator::CPDF_PageContentGenerator(
     CPDF_PageObjectHolder* pObjHolder)
-    : m_pObjHolder(pObjHolder), m_pDocument(pObjHolder->m_pDocument.Get()) {
-  for (const auto& pObj : *pObjHolder->GetPageObjectList()) {
+    : m_pObjHolder(pObjHolder), m_pDocument(pObjHolder->GetDocument()) {
+  for (const auto& pObj : *pObjHolder) {
     if (pObj)
       m_pageObjects.emplace_back(pObj.get());
   }
@@ -63,71 +67,121 @@
 void CPDF_PageContentGenerator::GenerateContent() {
   ASSERT(m_pObjHolder->IsPage());
 
-  CPDF_Document* pDoc = m_pDocument.Get();
-  std::ostringstream buf;
+  std::map<int32_t, std::unique_ptr<std::ostringstream>> stream =
+      GenerateModifiedStreams();
 
-  // Set the default graphic state values
-  buf << "q\n";
-  if (!m_pObjHolder->GetLastCTM().IsIdentity())
-    buf << m_pObjHolder->GetLastCTM().GetInverse() << " cm\n";
-  ProcessDefaultGraphics(&buf);
+  UpdateContentStreams(&stream);
+}
 
-  // Process the page objects
-  if (!ProcessPageObjects(&buf))
-    return;
+std::map<int32_t, std::unique_ptr<std::ostringstream>>
+CPDF_PageContentGenerator::GenerateModifiedStreams() {
+  // Make sure default graphics are created.
+  GetOrCreateDefaultGraphics();
 
-  // Return graphics to original state
-  buf << "Q\n";
+  // Figure out which streams are dirty.
+  std::set<int32_t> all_dirty_streams;
+  for (auto& pPageObj : m_pageObjects) {
+    if (pPageObj->IsDirty())
+      all_dirty_streams.insert(pPageObj->GetContentStream());
+  }
+  std::set<int32_t> marked_dirty_streams = m_pObjHolder->TakeDirtyStreams();
+  all_dirty_streams.insert(marked_dirty_streams.begin(),
+                           marked_dirty_streams.end());
 
-  // Add buffer to a stream in page's 'Contents'
-  CPDF_Dictionary* pPageDict = m_pObjHolder->m_pFormDict.Get();
-  CPDF_Object* pContent =
-      pPageDict ? pPageDict->GetObjectFor("Contents") : nullptr;
-  CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>();
-  pStream->SetData(&buf);
-  if (pContent) {
-    CPDF_Array* pArray = ToArray(pContent);
-    if (pArray) {
-      pArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
-      return;
-    }
-    CPDF_Reference* pReference = ToReference(pContent);
-    if (!pReference) {
-      pPageDict->SetNewFor<CPDF_Reference>("Contents", m_pDocument.Get(),
-                                           pStream->GetObjNum());
-      return;
-    }
-    CPDF_Object* pDirectObj = pReference->GetDirect();
-    if (!pDirectObj) {
-      pPageDict->SetNewFor<CPDF_Reference>("Contents", m_pDocument.Get(),
-                                           pStream->GetObjNum());
-      return;
-    }
-    CPDF_Array* pObjArray = pDirectObj->AsArray();
-    if (pObjArray) {
-      pObjArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
-      return;
-    }
-    if (pDirectObj->IsStream()) {
-      CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>();
-      pContentArray->AddNew<CPDF_Reference>(pDoc, pDirectObj->GetObjNum());
-      pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
-      pPageDict->SetNewFor<CPDF_Reference>("Contents", pDoc,
-                                           pContentArray->GetObjNum());
-      return;
+  // Start regenerating dirty streams.
+  std::map<int32_t, std::unique_ptr<std::ostringstream>> streams;
+  std::set<int32_t> empty_streams;
+  std::unique_ptr<const CPDF_ContentMarks> empty_content_marks =
+      pdfium::MakeUnique<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>();
+
+    // Set the default graphic state values
+    *buf << "q\n";
+    if (!m_pObjHolder->GetLastCTM().IsIdentity())
+      *buf << m_pObjHolder->GetLastCTM().GetInverse() << " cm\n";
+
+    ProcessDefaultGraphics(buf.get());
+
+    streams[dirty_stream] = std::move(buf);
+    empty_streams.insert(dirty_stream);
+    current_content_marks[dirty_stream] = empty_content_marks.get();
+  }
+
+  // Process the page objects, write into each dirty stream.
+  for (auto& pPageObj : m_pageObjects) {
+    int stream_index = pPageObj->GetContentStream();
+    auto it = streams.find(stream_index);
+    if (it == streams.end())
+      continue;
+
+    std::ostringstream* buf = it->second.get();
+    empty_streams.erase(stream_index);
+    current_content_marks[stream_index] = ProcessContentMarks(
+        buf, pPageObj.Get(), current_content_marks[stream_index]);
+    ProcessPageObject(buf, pPageObj.Get());
+  }
+
+  // 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)) {
+      // Clear to show that this stream needs to be deleted.
+      buf->str("");
+    } else {
+      FinishMarks(buf, current_content_marks[dirty_stream]);
+
+      // Return graphics to original state
+      *buf << "Q\n";
     }
   }
-  pPageDict->SetNewFor<CPDF_Reference>("Contents", m_pDocument.Get(),
-                                       pStream->GetObjNum());
+
+  return streams;
+}
+
+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;
+
+  CPDF_PageContentManager page_content_manager(m_pObjHolder.Get());
+
+  for (auto& pair : *new_stream_data) {
+    int32_t stream_index = pair.first;
+    std::ostringstream* buf = pair.second.get();
+
+    if (stream_index == CPDF_PageObject::kNoContentStream) {
+      int new_stream_index = page_content_manager.AddStream(buf);
+      UpdateStreamlessPageObjects(new_stream_index);
+      continue;
+    }
+
+    CPDF_Stream* old_stream =
+        page_content_manager.GetStreamByIndex(stream_index);
+    ASSERT(old_stream);
+
+    // 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);
+  }
+
+  page_content_manager.ExecuteScheduledRemovals();
 }
 
 ByteString CPDF_PageContentGenerator::RealizeResource(
-    uint32_t dwResourceObjNum,
-    const ByteString& bsType) {
-  ASSERT(dwResourceObjNum);
+    const CPDF_Object* pResource,
+    const ByteString& bsType) const {
+  ASSERT(pResource);
   if (!m_pObjHolder->m_pResources) {
-    m_pObjHolder->m_pResources = m_pDocument->NewIndirect<CPDF_Dictionary>();
-    m_pObjHolder->m_pFormDict->SetNewFor<CPDF_Reference>(
+    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());
   }
@@ -145,28 +199,109 @@
     idnum++;
   }
   pResList->SetNewFor<CPDF_Reference>(name, m_pDocument.Get(),
-                                      dwResourceObjNum);
+                                      pResource->GetObjNum());
   return name;
 }
 
 bool CPDF_PageContentGenerator::ProcessPageObjects(std::ostringstream* buf) {
   bool bDirty = false;
+  std::unique_ptr<const CPDF_ContentMarks> empty_content_marks =
+      pdfium::MakeUnique<CPDF_ContentMarks>();
+  const CPDF_ContentMarks* content_marks = empty_content_marks.get();
+
   for (auto& pPageObj : m_pageObjects) {
     if (m_pObjHolder->IsPage() && !pPageObj->IsDirty())
       continue;
 
     bDirty = true;
-    if (CPDF_ImageObject* pImageObject = pPageObj->AsImage())
-      ProcessImage(buf, pImageObject);
-    else if (CPDF_PathObject* pPathObj = pPageObj->AsPath())
-      ProcessPath(buf, pPathObj);
-    else if (CPDF_TextObject* pTextObj = pPageObj->AsText())
-      ProcessText(buf, pTextObj);
-    pPageObj->SetDirty(false);
+    content_marks = ProcessContentMarks(buf, pPageObj.Get(), content_marks);
+    ProcessPageObject(buf, pPageObj.Get());
   }
+  FinishMarks(buf, content_marks);
   return bDirty;
 }
 
+void CPDF_PageContentGenerator::UpdateStreamlessPageObjects(
+    int new_content_stream_index) {
+  for (auto& pPageObj : m_pageObjects) {
+    if (pPageObj->GetContentStream() == CPDF_PageObject::kNoContentStream)
+      pPageObj->SetContentStream(new_content_stream_index);
+  }
+}
+
+const CPDF_ContentMarks* CPDF_PageContentGenerator::ProcessContentMarks(
+    std::ostringstream* buf,
+    const CPDF_PageObject* pPageObj,
+    const CPDF_ContentMarks* pPrev) {
+  const CPDF_ContentMarks* pNext = &pPageObj->m_ContentMarks;
+
+  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
+  // bottom, but since the EMC operators do not identify which mark they are
+  // closing, it does not matter.
+  for (size_t i = first_different; i < pPrev->CountItems(); ++i)
+    *buf << "EMC\n";
+
+  // Open all marks that are in next but not in prev.
+  for (size_t i = first_different; i < pNext->CountItems(); ++i) {
+    const CPDF_ContentMarkItem* item = pNext->GetItem(i);
+
+    // Write mark tag.
+    *buf << "/" << PDF_NameEncode(item->GetName()) << " ";
+
+    // If there are no parameters, write a BMC (begin marked content) operator.
+    if (item->GetParamType() == CPDF_ContentMarkItem::kNone) {
+      *buf << "BMC\n";
+      continue;
+    }
+
+    // If there are parameters, write properties, direct or indirect.
+    switch (item->GetParamType()) {
+      case CPDF_ContentMarkItem::kDirectDict: {
+        CPDF_StringArchiveStream archive_stream(buf);
+        item->GetParam()->WriteTo(&archive_stream, nullptr);
+        *buf << " ";
+        break;
+      }
+      case CPDF_ContentMarkItem::kPropertiesDict: {
+        *buf << "/" << item->GetPropertyName() << " ";
+        break;
+      }
+      default:
+        NOTREACHED();
+        break;
+    }
+
+    // Write BDC (begin dictionary content) operator.
+    *buf << "BDC\n";
+  }
+
+  return pNext;
+}
+
+void CPDF_PageContentGenerator::FinishMarks(
+    std::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
+  // closing, it does not matter.
+  for (size_t i = 0; i < pContentMarks->CountItems(); ++i)
+    *buf << "EMC\n";
+}
+
+void CPDF_PageContentGenerator::ProcessPageObject(std::ostringstream* buf,
+                                                  CPDF_PageObject* pPageObj) {
+  if (CPDF_ImageObject* pImageObject = pPageObj->AsImage())
+    ProcessImage(buf, pImageObject);
+  else if (CPDF_PathObject* pPathObj = pPageObj->AsPath())
+    ProcessPath(buf, pPathObj);
+  else if (CPDF_TextObject* pTextObj = pPageObj->AsText())
+    ProcessText(buf, pTextObj);
+  pPageObj->SetDirty(false);
+}
+
 void CPDF_PageContentGenerator::ProcessImage(std::ostringstream* buf,
                                              CPDF_ImageObject* pImageObj) {
   if ((pImageObj->matrix().a == 0 && pImageObj->matrix().b == 0) ||
@@ -187,10 +322,11 @@
   if (bWasInline)
     pImage->ConvertStreamToIndirectObject();
 
-  uint32_t dwObjNum = pStream->GetObjNum();
-  ByteString name = RealizeResource(dwObjNum, "XObject");
-  if (bWasInline)
-    pImageObj->SetImage(m_pDocument->GetPageData()->GetImage(dwObjNum));
+  ByteString name = RealizeResource(pStream, "XObject");
+  if (bWasInline) {
+    auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
+    pImageObj->SetImage(pPageData->GetImage(pStream->GetObjNum()));
+  }
 
   *buf << "/" << PDF_NameEncode(name) << " Do Q\n";
 }
@@ -210,18 +346,19 @@
                                             CPDF_PathObject* pPathObj) {
   ProcessGraphics(buf, pPathObj);
 
-  *buf << pPathObj->m_Matrix << " cm ";
+  *buf << pPathObj->matrix() << " cm ";
 
-  auto& pPoints = pPathObj->m_Path.GetPoints();
-  if (pPathObj->m_Path.IsRect()) {
+  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.x << " " << pPoints[0].m_Point.y << " " << diff.x
-         << " " << diff.y << " re";
+    *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.x << " " << pPoints[i].m_Point.y;
+
+      *buf << pPoints[i].m_Point;
+
       FXPT_TYPE pointType = pPoints[i].m_Type;
       if (pointType == FXPT_TYPE::MoveTo) {
         *buf << " m";
@@ -236,21 +373,21 @@
           *buf << " h";
           break;
         }
-        *buf << " " << pPoints[i + 1].m_Point.x << " "
-             << pPoints[i + 1].m_Point.y << " " << pPoints[i + 2].m_Point.x
-             << " " << pPoints[i + 2].m_Point.y << " c";
+        *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->m_FillType == 0)
-    *buf << (pPathObj->m_bStroke ? " S" : " n");
-  else if (pPathObj->m_FillType == FXFILL_WINDING)
-    *buf << (pPathObj->m_bStroke ? " B" : " f");
-  else if (pPathObj->m_FillType == FXFILL_ALTERNATE)
-    *buf << (pPathObj->m_bStroke ? " B*" : " f*");
+  if (pPathObj->has_no_filltype())
+    *buf << (pPathObj->stroke() ? " S" : " n");
+  else if (pPathObj->has_winding_filltype())
+    *buf << (pPathObj->stroke() ? " B" : " f");
+  else if (pPathObj->has_alternate_filltype())
+    *buf << (pPathObj->stroke() ? " B*" : " f*");
   *buf << " Q\n";
 }
 
@@ -277,7 +414,7 @@
   }
   float lineWidth = pPageObj->m_GraphState.GetLineWidth();
   if (lineWidth != 1.0f)
-    *buf << lineWidth << " w ";
+    WriteFloat(*buf, lineWidth) << " w ";
   CFX_GraphStateData::LineCap lineCap = pPageObj->m_GraphState.GetLineCap();
   if (lineCap != CFX_GraphStateData::LineCapButt)
     *buf << static_cast<int>(lineCap) << " J ";
@@ -290,8 +427,7 @@
   graphD.strokeAlpha = pPageObj->m_GeneralState.GetStrokeAlpha();
   graphD.blendType = pPageObj->m_GeneralState.GetBlendType();
   if (graphD.fillAlpha == 1.0f && graphD.strokeAlpha == 1.0f &&
-      (graphD.blendType == FXDIB_BLEND_UNSUPPORTED ||
-       graphD.blendType == FXDIB_BLEND_NORMAL)) {
+      graphD.blendType == BlendMode::kNormal) {
     return;
   }
 
@@ -300,21 +436,19 @@
   if (it != m_pObjHolder->m_GraphicsMap.end()) {
     name = it->second;
   } else {
-    auto gsDict = pdfium::MakeUnique<CPDF_Dictionary>();
+    auto gsDict = pdfium::MakeRetain<CPDF_Dictionary>();
     if (graphD.fillAlpha != 1.0f)
       gsDict->SetNewFor<CPDF_Number>("ca", graphD.fillAlpha);
 
     if (graphD.strokeAlpha != 1.0f)
       gsDict->SetNewFor<CPDF_Number>("CA", graphD.strokeAlpha);
 
-    if (graphD.blendType != FXDIB_BLEND_UNSUPPORTED &&
-        graphD.blendType != FXDIB_BLEND_NORMAL) {
+    if (graphD.blendType != BlendMode::kNormal) {
       gsDict->SetNewFor<CPDF_Name>("BM",
                                    pPageObj->m_GeneralState.GetBlendMode());
     }
-    CPDF_Object* pDict = m_pDocument->AddIndirectObject(std::move(gsDict));
-    uint32_t dwObjNum = pDict->GetObjNum();
-    name = RealizeResource(dwObjNum, "ExtGState");
+    CPDF_Object* pDict = m_pDocument->AddIndirectObject(gsDict);
+    name = RealizeResource(pDict, "ExtGState");
     m_pObjHolder->m_GraphicsMap[graphD] = name;
   }
   *buf << "/" << PDF_NameEncode(name) << " gs ";
@@ -325,25 +459,30 @@
   *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 ";
+}
+
+ByteString CPDF_PageContentGenerator::GetOrCreateDefaultGraphics() const {
   GraphicsData defaultGraphics;
   defaultGraphics.fillAlpha = 1.0f;
   defaultGraphics.strokeAlpha = 1.0f;
-  defaultGraphics.blendType = FXDIB_BLEND_NORMAL;
+  defaultGraphics.blendType = BlendMode::kNormal;
   auto it = m_pObjHolder->m_GraphicsMap.find(defaultGraphics);
-  ByteString name;
-  if (it != m_pObjHolder->m_GraphicsMap.end()) {
-    name = it->second;
-  } else {
-    auto gsDict = pdfium::MakeUnique<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(std::move(gsDict));
-    uint32_t dwObjNum = pDict->GetObjNum();
-    name = RealizeResource(dwObjNum, "ExtGState");
-    m_pObjHolder->m_GraphicsMap[defaultGraphics] = name;
-  }
-  *buf << "/" << PDF_NameEncode(name).c_str() << " gs ";
+
+  // If default graphics already exists, return it.
+  if (it != m_pObjHolder->m_GraphicsMap.end())
+    return it->second;
+
+  // 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;
+  return name;
 }
 
 // This method adds text to the buffer, BT begins the text object, ET ends it.
@@ -354,39 +493,47 @@
                                             CPDF_TextObject* pTextObj) {
   ProcessGraphics(buf, pTextObj);
   *buf << "BT " << pTextObj->GetTextMatrix() << " Tm ";
-  CPDF_Font* pFont = pTextObj->GetFont();
+  RetainPtr<CPDF_Font> pFont(pTextObj->GetFont());
   if (!pFont)
     pFont = CPDF_Font::GetStockFont(m_pDocument.Get(), "Helvetica");
-  FontData fontD;
-  if (pFont->IsType1Font())
-    fontD.type = "Type1";
-  else if (pFont->IsTrueTypeFont())
-    fontD.type = "TrueType";
-  else if (pFont->IsCIDFont())
-    fontD.type = "Type0";
-  else
+
+  FontData data;
+  const CPDF_FontEncoding* pEncoding = nullptr;
+  if (pFont->IsType1Font()) {
+    data.type = "Type1";
+    pEncoding = pFont->AsType1Font()->GetEncoding();
+  } else if (pFont->IsTrueTypeFont()) {
+    data.type = "TrueType";
+    pEncoding = pFont->AsTrueTypeFont()->GetEncoding();
+  } else if (pFont->IsCIDFont()) {
+    data.type = "Type0";
+  } else {
     return;
-  fontD.baseFont = pFont->GetBaseFont();
-  auto it = m_pObjHolder->m_FontsMap.find(fontD);
+  }
+  data.baseFont = pFont->GetBaseFontName();
+  auto it = m_pObjHolder->m_FontsMap.find(data);
   ByteString dictName;
   if (it != m_pObjHolder->m_FontsMap.end()) {
     dictName = it->second;
   } else {
-    uint32_t dwObjNum = pFont->GetFontDict()->GetObjNum();
-    if (!dwObjNum) {
+    CPDF_Object* pIndirectFont = pFont->GetFontDict();
+    if (pIndirectFont->IsInline()) {
       // In this case we assume it must be a standard font
-      auto fontDict = pdfium::MakeUnique<CPDF_Dictionary>();
-      fontDict->SetNewFor<CPDF_Name>("Type", "Font");
-      fontDict->SetNewFor<CPDF_Name>("Subtype", fontD.type);
-      fontDict->SetNewFor<CPDF_Name>("BaseFont", fontD.baseFont);
-      CPDF_Object* pDict = m_pDocument->AddIndirectObject(std::move(fontDict));
-      dwObjNum = pDict->GetObjNum();
+      auto pFontDict = pdfium::MakeRetain<CPDF_Dictionary>();
+      pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
+      pFontDict->SetNewFor<CPDF_Name>("Subtype", data.type);
+      pFontDict->SetNewFor<CPDF_Name>("BaseFont", data.baseFont);
+      if (pEncoding) {
+        pFontDict->SetFor("Encoding",
+                          pEncoding->Realize(m_pDocument->GetByteStringPool()));
+      }
+      pIndirectFont = m_pDocument->AddIndirectObject(pFontDict);
     }
-    dictName = RealizeResource(dwObjNum, "Font");
-    m_pObjHolder->m_FontsMap[fontD] = dictName;
+    dictName = RealizeResource(pIndirectFont, "Font");
+    m_pObjHolder->m_FontsMap[data] = dictName;
   }
-  *buf << "/" << PDF_NameEncode(dictName) << " " << pTextObj->GetFontSize()
-       << " Tf ";
+  *buf << "/" << PDF_NameEncode(dictName) << " ";
+  WriteFloat(*buf, pTextObj->GetFontSize()) << " Tf ";
   ByteString text;
   for (uint32_t charcode : pTextObj->GetCharCodes()) {
     if (charcode != CPDF_Font::kInvalidCharCode)
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
index efc5739..40d19ae 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
@@ -7,6 +7,8 @@
 #ifndef CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
 #define CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
 
+#include <map>
+#include <memory>
 #include <sstream>
 #include <vector>
 
@@ -14,8 +16,10 @@
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
 
+class CPDF_ContentMarks;
 class CPDF_Document;
 class CPDF_ImageObject;
+class CPDF_Object;
 class CPDF_PageObject;
 class CPDF_PageObjectHolder;
 class CPDF_PathObject;
@@ -32,13 +36,34 @@
  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);
-  ByteString RealizeResource(uint32_t dwResourceObjNum,
-                             const ByteString& bsType);
+  ByteString GetOrCreateDefaultGraphics() const;
+  ByteString RealizeResource(const CPDF_Object* pResource,
+                             const ByteString& bsType) const;
+  const CPDF_ContentMarks* ProcessContentMarks(std::ostringstream* buf,
+                                               const CPDF_PageObject* pPageObj,
+                                               const CPDF_ContentMarks* pPrev);
+  void FinishMarks(std::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();
+
+  // Add buffer as a stream in page's 'Contents'
+  void UpdateContentStreams(
+      std::map<int32_t, std::unique_ptr<std::ostringstream>>* new_stream_data);
+
+  // Set 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);
 
   UnownedPtr<CPDF_PageObjectHolder> const m_pObjHolder;
   UnownedPtr<CPDF_Document> const m_pDocument;
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index a0db869..62c3df5 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -7,27 +7,30 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
 #include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#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/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 "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
 
 class CPDF_PageContentGeneratorTest : public testing::Test {
  protected:
-  void SetUp() override { CPDF_ModuleMgr::Get()->Init(); }
-
-  void TearDown() override {
-    CPDF_ModuleMgr::Destroy();
-  }
+  void SetUp() override { CPDF_PageModule::Create(); }
+  void TearDown() override { CPDF_PageModule::Destroy(); }
 
   void TestProcessPath(CPDF_PageContentGenerator* pGen,
                        std::ostringstream* buf,
@@ -50,98 +53,171 @@
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessRect) {
   auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->m_Path.AppendRect(10, 5, 13, 30);
-  pPathObj->m_FillType = FXFILL_ALTERNATE;
-  pPathObj->m_bStroke = true;
+  pPathObj->set_stroke(true);
+  pPathObj->set_filltype(FXFILL_ALTERNATE);
+  pPathObj->path().AppendRect(10, 5, 13, 30);
 
-  auto pTestPage = pdfium::MakeUnique<CPDF_Page>(nullptr, nullptr, false);
-  CPDF_PageContentGenerator generator(pTestPage.get());
+  auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto pTestPage =
+      pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+  CPDF_PageContentGenerator generator(pTestPage.Get());
   std::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->m_Path.AppendPoint(CFX_PointF(0, 0), FXPT_TYPE::MoveTo, false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(5.2f, 0), FXPT_TYPE::LineTo, false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(5.2f, 3.78f), FXPT_TYPE::LineTo,
+  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->m_Path.AppendPoint(CFX_PointF(0, 3.78f), FXPT_TYPE::LineTo, true);
-  pPathObj->m_FillType = 0;
-  pPathObj->m_bStroke = false;
+  pPathObj->path().AppendPoint(CFX_PointF(0, 3.78f), FXPT_TYPE::LineTo, true);
   buf.str("");
-
   TestProcessPath(&generator, &buf, pPathObj.get());
-  EXPECT_EQ("q 1 0 0 1 0 0 cm 0 0 5.2 3.78 re n Q\n", ByteString(buf));
+  EXPECT_EQ("q 1 0 0 1 0 0 cm 0 0 5.1999998 3.78 re n Q\n", ByteString(buf));
+}
+
+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);
+  {
+    auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+    pPathObj->set_filltype(FXFILL_WINDING);
+
+    // Test code in ProcessPath that generates re operator
+    pPathObj->path().AppendRect(0.000000000000000000001,
+                                0.000000000000000000001, 100, 100);
+
+    pPathObj->m_ColorState.SetFillColor(pCS, rgb);
+    pPathObj->m_ColorState.SetStrokeColor(pCS, rgb);
+    pPathObj->m_GraphState.SetLineWidth(200000000000000000001.0);
+    pPathObj->Transform(CFX_Matrix(1, 0, 0, 1, 0.000000000000000000001,
+                                   200000000000000.000002));
+
+    auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    auto pTestPage =
+        pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+    CPDF_PageContentGenerator generator(pTestPage.Get());
+    std::ostringstream buf;
+    TestProcessPath(&generator, &buf, pPathObj.get());
+    EXPECT_EQ(
+        "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 200000000000000000000 w"
+        " 1 0 0 1 .00000000000000000000099999997 200000000000000 cm .000000000"
+        "00000000000099999997 .00000000000000000000099999997 100 100 re f Q\n",
+        ByteString(buf));
+  }
+
+  {
+    // Test code in ProcessPath that handles bezier operator
+    auto pPathObj = pdfium::MakeUnique<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->path().AppendPoint(CFX_PointF(0.000000000000000000001f, 4.67f),
+                                 FXPT_TYPE::MoveTo, false);
+    pPathObj->path().AppendPoint(
+        CFX_PointF(0.000000000000000000001, 100000000000000.000002),
+        FXPT_TYPE::LineTo, false);
+    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_PointF(53.4f, 5000000000000000000.00000000000000004),
+        FXPT_TYPE::BezierTo, true);
+    auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+    auto pTestPage =
+        pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+    CPDF_PageContentGenerator generator(pTestPage.Get());
+    std::ostringstream buf;
+
+    TestProcessPath(&generator, &buf, pPathObj.get());
+    EXPECT_EQ(
+        "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 2 w 1 0 0 1 432 4999999"
+        "90000000 cm .00000000000000000000099999997 4.6700001 m .0000000000000"
+        "0000000099999997 100000000000000 l .000000000000099999998 3.1500001 3"
+        ".5699999 2.98 53.400002 5000000000000000000 c h f Q\n",
+        ByteString(buf));
+  }
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessPath) {
   auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->m_Path.AppendPoint(CFX_PointF(3.102f, 4.67f), FXPT_TYPE::MoveTo,
+  pPathObj->set_filltype(FXFILL_WINDING);
+  pPathObj->path().AppendPoint(CFX_PointF(3.102f, 4.67f), FXPT_TYPE::MoveTo,
                                false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(5.45f, 0.29f), FXPT_TYPE::LineTo,
+  pPathObj->path().AppendPoint(CFX_PointF(5.45f, 0.29f), FXPT_TYPE::LineTo,
                                false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(4.24f, 3.15f), FXPT_TYPE::BezierTo,
+  pPathObj->path().AppendPoint(CFX_PointF(4.24f, 3.15f), FXPT_TYPE::BezierTo,
                                false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(4.65f, 2.98f), FXPT_TYPE::BezierTo,
+  pPathObj->path().AppendPoint(CFX_PointF(4.65f, 2.98f), FXPT_TYPE::BezierTo,
                                false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(3.456f, 0.24f), FXPT_TYPE::BezierTo,
+  pPathObj->path().AppendPoint(CFX_PointF(3.456f, 0.24f), FXPT_TYPE::BezierTo,
                                false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(10.6f, 11.15f), FXPT_TYPE::LineTo,
+  pPathObj->path().AppendPoint(CFX_PointF(10.6f, 11.15f), FXPT_TYPE::LineTo,
                                false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(11, 12.5f), FXPT_TYPE::LineTo, false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(11.46f, 12.67f), FXPT_TYPE::BezierTo,
+  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->m_Path.AppendPoint(CFX_PointF(11.84f, 12.96f), FXPT_TYPE::BezierTo,
+  pPathObj->path().AppendPoint(CFX_PointF(11.84f, 12.96f), FXPT_TYPE::BezierTo,
                                false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(12, 13.64f), FXPT_TYPE::BezierTo,
+  pPathObj->path().AppendPoint(CFX_PointF(12, 13.64f), FXPT_TYPE::BezierTo,
                                true);
-  pPathObj->m_FillType = FXFILL_WINDING;
-  pPathObj->m_bStroke = false;
 
-  auto pTestPage = pdfium::MakeUnique<CPDF_Page>(nullptr, nullptr, false);
-  CPDF_PageContentGenerator generator(pTestPage.get());
+  auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto pTestPage =
+      pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict.Get());
+  CPDF_PageContentGenerator generator(pTestPage.Get());
   std::ostringstream buf;
   TestProcessPath(&generator, &buf, pPathObj.get());
   EXPECT_EQ(
-      "q 1 0 0 1 0 0 cm 3.102 4.67 m 5.45 0.29 l 4.24 3.15 4.65 2.98 3.456 0.24"
-      " c 10.6 11.15 l 11 12.5 l 11.46 12.67 11.84 12.96 12 13.64 c h f Q\n",
+      "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4499998 .28999999 l 4.2399998 "
+      "3.1500001 4.6500001 2.98 3.4560001 .23999999 c 10.6000004 11.149999"
+      "6 l 11 12.5 l 11.46 12.6700001 11.8400002 12.96 12 13.6400003 c h f"
+      " Q\n",
       ByteString(buf));
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessGraphics) {
   auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->m_Path.AppendPoint(CFX_PointF(1, 2), FXPT_TYPE::MoveTo, false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(3, 4), FXPT_TYPE::LineTo, false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(5, 6), FXPT_TYPE::LineTo, true);
-  pPathObj->m_FillType = FXFILL_WINDING;
-  pPathObj->m_bStroke = true;
+  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);
 
-  float rgb[3] = {0.5f, 0.7f, 0.35f};
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-  pPathObj->m_ColorState.SetFillColor(pCS, rgb, 3);
+  static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  pPathObj->m_ColorState.SetFillColor(pCS, rgb);
 
-  float rgb2[3] = {1, 0.9f, 0};
-  pPathObj->m_ColorState.SetStrokeColor(pCS, rgb2, 3);
+  static const std::vector<float> rgb2 = {1, 0.9f, 0};
+  pPathObj->m_ColorState.SetStrokeColor(pCS, rgb2);
   pPathObj->m_GeneralState.SetFillAlpha(0.5f);
   pPathObj->m_GeneralState.SetStrokeAlpha(0.8f);
 
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
+  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>());
+
   pDoc->CreateNewDoc();
   CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0);
-  auto pTestPage = pdfium::MakeUnique<CPDF_Page>(pDoc.get(), pPageDict, false);
-  CPDF_PageContentGenerator generator(pTestPage.get());
+  auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
+  CPDF_PageContentGenerator generator(pTestPage.Get());
   std::ostringstream buf;
   TestProcessPath(&generator, &buf, pPathObj.get());
   ByteString pathString(buf);
 
   // Color RGB values used are integers divided by 255.
   EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /",
-            pathString.Left(48));
+            pathString.First(48));
   EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n",
-            pathString.Right(43));
-  ASSERT_TRUE(pathString.GetLength() > 91);
-  CPDF_Dictionary* externalGS = TestGetResource(
-      &generator, "ExtGState", pathString.Mid(48, pathString.GetLength() - 91));
+            pathString.Last(43));
+  ASSERT_GT(pathString.GetLength(), 91U);
+  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"));
@@ -152,33 +228,38 @@
   TestProcessPath(&generator, &buf, pPathObj.get());
   ByteString pathString2(buf);
   EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG 10.5 w /",
-            pathString2.Left(55));
+            pathString2.First(55));
   EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n",
-            pathString2.Right(43));
+            pathString2.Last(43));
 
   // Compare with the previous (should use same dictionary for gs)
   EXPECT_EQ(pathString.GetLength() + 7, pathString2.GetLength());
-  EXPECT_EQ(pathString.Mid(48, pathString.GetLength() - 76),
-            pathString2.Mid(55, pathString2.GetLength() - 83));
+  EXPECT_EQ(pathString.Substr(48, pathString.GetLength() - 76),
+            pathString2.Substr(55, pathString2.GetLength() - 83));
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessStandardText) {
   // Checking font whose font dictionary is not yet indirect object.
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
+  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>());
+
   pDoc->CreateNewDoc();
   CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0);
-  auto pTestPage = pdfium::MakeUnique<CPDF_Page>(pDoc.get(), pPageDict, false);
-  CPDF_PageContentGenerator generator(pTestPage.get());
+  auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
+  CPDF_PageContentGenerator generator(pTestPage.Get());
   auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
-  CPDF_Font* pFont = CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman");
+  RetainPtr<CPDF_Font> pFont =
+      CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman");
   pTextObj->m_TextState.SetFont(pFont);
   pTextObj->m_TextState.SetFontSize(10.0f);
-  float rgb[3] = {0.5f, 0.7f, 0.35f};
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-  pTextObj->m_ColorState.SetFillColor(pCS, rgb, 3);
 
-  float rgb2[3] = {1, 0.9f, 0};
-  pTextObj->m_ColorState.SetStrokeColor(pCS, rgb2, 3);
+  static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  pTextObj->m_ColorState.SetFillColor(pCS, rgb);
+
+  static const std::vector<float> rgb2 = {1, 0.9f, 0};
+  pTextObj->m_ColorState.SetStrokeColor(pCS, rgb2);
   pTextObj->m_GeneralState.SetFillAlpha(0.5f);
   pTextObj->m_GeneralState.SetStrokeAlpha(0.8f);
   pTextObj->Transform(CFX_Matrix(1, 0, 0, 1, 100, 100));
@@ -192,12 +273,12 @@
   auto secondResourceAt = textString.ReverseFind('/');
   ASSERT_TRUE(secondResourceAt.has_value());
   secondResourceAt = secondResourceAt.value() + 1;
-  ByteString firstString = textString.Left(firstResourceAt.value());
+  ByteString firstString = textString.First(firstResourceAt.value());
   ByteString midString =
-      textString.Mid(firstResourceAt.value(),
-                     secondResourceAt.value() - firstResourceAt.value());
+      textString.Substr(firstResourceAt.value(),
+                        secondResourceAt.value() - firstResourceAt.value());
   ByteString lastString =
-      textString.Right(textString.GetLength() - secondResourceAt.value());
+      textString.Last(textString.GetLength() - secondResourceAt.value());
   // q and Q must be outside the BT .. ET operations
   ByteString compareString1 =
       "q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /";
@@ -207,18 +288,18 @@
   EXPECT_LT(compareString1.GetLength() + compareString2.GetLength() +
                 compareString3.GetLength(),
             textString.GetLength());
-  EXPECT_EQ(compareString1, firstString.Left(compareString1.GetLength()));
-  EXPECT_EQ(compareString2, midString.Right(compareString2.GetLength()));
-  EXPECT_EQ(compareString3, lastString.Right(compareString3.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(
       &generator, "ExtGState",
-      midString.Left(midString.GetLength() - compareString2.GetLength()));
+      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(
       &generator, "Font",
-      lastString.Left(lastString.GetLength() - compareString3.GetLength()));
+      lastString.First(lastString.GetLength() - compareString3.GetLength()));
   ASSERT_TRUE(fontDict);
   EXPECT_EQ("Font", fontDict->GetStringFor("Type"));
   EXPECT_EQ("Type1", fontDict->GetStringFor("Subtype"));
@@ -227,11 +308,14 @@
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessText) {
   // Checking font whose font dictionary is already an indirect object.
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
+  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>());
   pDoc->CreateNewDoc();
+
   CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0);
-  auto pTestPage = pdfium::MakeUnique<CPDF_Page>(pDoc.get(), pPageDict, false);
-  CPDF_PageContentGenerator generator(pTestPage.get());
+  auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
+  CPDF_PageContentGenerator generator(pTestPage.Get());
 
   std::ostringstream buf;
   {
@@ -240,20 +324,21 @@
     CPDF_Dictionary* pDict = pDoc->NewIndirect<CPDF_Dictionary>();
     pDict->SetNewFor<CPDF_Name>("Type", "Font");
     pDict->SetNewFor<CPDF_Name>("Subtype", "TrueType");
-    CPDF_Font* pFont = CPDF_Font::GetStockFont(pDoc.get(), "Arial");
-    pDict->SetNewFor<CPDF_Name>("BaseFont", pFont->GetBaseFont());
+
+    RetainPtr<CPDF_Font> pFont = CPDF_Font::GetStockFont(pDoc.get(), "Arial");
+    pDict->SetNewFor<CPDF_Name>("BaseFont", pFont->GetBaseFontName());
 
     CPDF_Dictionary* pDesc = pDoc->NewIndirect<CPDF_Dictionary>();
     pDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
-    pDesc->SetNewFor<CPDF_Name>("FontName", pFont->GetBaseFont());
+    pDesc->SetNewFor<CPDF_Name>("FontName", pFont->GetBaseFontName());
     pDict->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc.get(),
                                      pDesc->GetObjNum());
 
-    CPDF_Font* loadedFont = pDoc->LoadFont(pDict);
-    pTextObj->m_TextState.SetFont(loadedFont);
+    RetainPtr<CPDF_Font> pLoadedFont =
+        CPDF_DocPageData::FromDocument(pDoc.get())->GetFont(pDict);
+    pTextObj->m_TextState.SetFont(pLoadedFont);
     pTextObj->m_TextState.SetFontSize(15.5f);
     pTextObj->SetText("I am indirect");
-
     TestProcessText(&generator, &buf, pTextObj.get());
   }
 
@@ -261,21 +346,21 @@
   auto firstResourceAt = textString.Find('/');
   ASSERT_TRUE(firstResourceAt.has_value());
   firstResourceAt = firstResourceAt.value() + 1;
-  ByteString firstString = textString.Left(firstResourceAt.value());
+  ByteString firstString = textString.First(firstResourceAt.value());
   ByteString lastString =
-      textString.Right(textString.GetLength() - firstResourceAt.value());
+      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";
   EXPECT_LT(compareString1.GetLength() + compareString2.GetLength(),
             textString.GetLength());
-  EXPECT_EQ(compareString1, textString.Left(compareString1.GetLength()));
-  EXPECT_EQ(compareString2, textString.Right(compareString2.GetLength()));
+  EXPECT_EQ(compareString1, textString.First(compareString1.GetLength()));
+  EXPECT_EQ(compareString2, textString.Last(compareString2.GetLength()));
   CPDF_Dictionary* fontDict = TestGetResource(
       &generator, "Font",
-      textString.Mid(compareString1.GetLength(),
-                     textString.GetLength() - compareString1.GetLength() -
-                         compareString2.GetLength()));
+      textString.Substr(compareString1.GetLength(),
+                        textString.GetLength() - compareString1.GetLength() -
+                            compareString2.GetLength()));
   ASSERT_TRUE(fontDict);
   EXPECT_TRUE(fontDict->GetObjNum());
   EXPECT_EQ("Font", fontDict->GetStringFor("Type"));
@@ -289,16 +374,19 @@
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessEmptyForm) {
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
+  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>());
   pDoc->CreateNewDoc();
-  auto pDict = pdfium::MakeUnique<CPDF_Dictionary>();
-  auto pStream = pdfium::MakeUnique<CPDF_Stream>(nullptr, 0, std::move(pDict));
+  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  auto pStream = pdfium::MakeRetain<CPDF_Stream>(nullptr, 0, std::move(pDict));
 
   // Create an empty form.
   auto pTestForm =
-      pdfium::MakeUnique<CPDF_Form>(pDoc.get(), nullptr, pStream.get());
+      pdfium::MakeUnique<CPDF_Form>(pDoc.get(), nullptr, pStream.Get());
   pTestForm->ParseContent();
-  ASSERT_TRUE(pTestForm->IsParsed());
+  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());
@@ -308,26 +396,32 @@
 }
 
 TEST_F(CPDF_PageContentGeneratorTest, ProcessFormWithPath) {
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
+  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>());
   pDoc->CreateNewDoc();
-  auto pDict = pdfium::MakeUnique<CPDF_Dictionary>();
+  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>();
   const char content[] =
-      "q 1 0 0 1 0 0 cm 3.102 4.67 m 5.45 0.29 l 4.24 3.15 4.65 2.98 3.456 "
-      "0.24 c 3.102 4.67 l h f Q\n";
+      "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::MakeUnique<CPDF_Stream>(std::move(buf), buf_len,
+  auto pStream = pdfium::MakeRetain<CPDF_Stream>(std::move(buf), buf_len,
                                                  std::move(pDict));
 
   // Create a form with a non-empty stream.
   auto pTestForm =
-      pdfium::MakeUnique<CPDF_Form>(pDoc.get(), nullptr, pStream.get());
+      pdfium::MakeUnique<CPDF_Form>(pDoc.get(), nullptr, pStream.Get());
   pTestForm->ParseContent();
-  ASSERT_TRUE(pTestForm->IsParsed());
+  ASSERT_EQ(CPDF_PageObjectHolder::ParseState::kParsed,
+            pTestForm->GetParseState());
 
   CPDF_PageContentGenerator generator(pTestForm.get());
   std::ostringstream process_buf;
   generator.ProcessPageObjects(&process_buf);
-  EXPECT_EQ(content, ByteString(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"
+      "99999 4.6500001 2.98 3.4560001 .24000001 c 3.102 4.6700001 l h f Q\n",
+      ByteString(process_buf).c_str());
 }
diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
new file mode 100644
index 0000000..35a8acd
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
@@ -0,0 +1,152 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/edit/cpdf_pagecontentmanager.h"
+
+#include <map>
+#include <numeric>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_pageobject.h"
+#include "core/fpdfapi/page/cpdf_pageobjectholder.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+
+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);
+  if (contents_array) {
+    contents_array_.Reset(contents_array);
+    return;
+  }
+
+  CPDF_Reference* contents_reference = ToReference(contents_obj);
+  if (contents_reference) {
+    CPDF_Object* indirect_obj = contents_reference->GetDirect();
+    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());
+  }
+}
+
+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;
+}
+
+size_t CPDF_PageContentManager::AddStream(std::ostringstream* buf) {
+  CPDF_Stream* new_stream = doc_->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());
+
+    CPDF_Dictionary* page_dict = obj_holder_->GetDict();
+    page_dict->SetNewFor<CPDF_Reference>("Contents", doc_.Get(),
+                                         new_contents_array->GetObjNum());
+    contents_array_.Reset(new_contents_array);
+    contents_stream_ = nullptr;
+    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;
+  }
+
+  // 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(),
+                                       new_stream->GetObjNum());
+  contents_stream_.Reset(new_stream);
+  return 0;
+}
+
+void CPDF_PageContentManager::ScheduleRemoveStreamByIndex(size_t stream_index) {
+  streams_to_remove_.insert(stream_index);
+}
+
+void CPDF_PageContentManager::ExecuteScheduledRemovals() {
+  // 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());
+
+  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.
+  }
+
+  streams_to_remove_.clear();
+}
diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.h b/core/fpdfapi/edit/cpdf_pagecontentmanager.h
new file mode 100644
index 0000000..2e2b225
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.h
@@ -0,0 +1,50 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTMANAGER_H_
+#define CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTMANAGER_H_
+
+#include <set>
+#include <sstream>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CPDF_Array;
+class CPDF_Document;
+class CPDF_Object;
+class CPDF_Stream;
+class CPDF_PageObjectHolder;
+
+class CPDF_PageContentManager {
+ public:
+  explicit CPDF_PageContentManager(const CPDF_PageObjectHolder* obj_holder);
+  ~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);
+
+  // Schedule the removal of the Content stream at a given index. It will be
+  // removed when ExecuteScheduledRemovals() is called.
+  void ScheduleRemoveStreamByIndex(size_t stream_index);
+
+  // Remove 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_;
+  std::set<size_t> streams_to_remove_;
+};
+
+#endif  // CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTMANAGER_H_
diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
new file mode 100644
index 0000000..4840db8
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
@@ -0,0 +1,35 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
+
+CPDF_StringArchiveStream::CPDF_StringArchiveStream(std::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;
+}
+
+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());
+  return true;
+}
diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.h b/core/fpdfapi/edit/cpdf_stringarchivestream.h
new file mode 100644
index 0000000..59d168f
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.h
@@ -0,0 +1,26 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_
+#define CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_
+
+#include "core/fxcrt/fx_stream.h"
+
+class CPDF_StringArchiveStream final : public IFX_ArchiveStream {
+ public:
+  explicit CPDF_StringArchiveStream(std::ostringstream* stream);
+  ~CPDF_StringArchiveStream() override;
+
+  // IFX_ArchiveStream
+  bool WriteByte(uint8_t byte) override;
+  bool WriteDWord(uint32_t i) override;
+  FX_FILESIZE CurrentOffset() const override;
+  bool WriteBlock(const void* pData, size_t size) override;
+  bool WriteString(ByteStringView str) override;
+
+ private:
+  std::ostringstream* stream_;
+};
+
+#endif  // CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_
diff --git a/core/fpdfapi/font/Android.bp b/core/fpdfapi/font/Android.bp
new file mode 100644
index 0000000..c4054f4
--- /dev/null
+++ b/core/fpdfapi/font/Android.bp
@@ -0,0 +1,17 @@
+cc_library_static {
+    name: "libpdfium-font",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    static_libs: [
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+        "libpdfium-cmaps",
+        "libpdfium-parser",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/core/fpdfapi/font/BUILD.gn b/core/fpdfapi/font/BUILD.gn
new file mode 100644
index 0000000..c30269a
--- /dev/null
+++ b/core/fpdfapi/font/BUILD.gn
@@ -0,0 +1,69 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+source_set("font") {
+  sources = [
+    "cfx_cttgsubtable.cpp",
+    "cfx_cttgsubtable.h",
+    "cfx_stockfontarray.cpp",
+    "cfx_stockfontarray.h",
+    "cpdf_cid2unicodemap.cpp",
+    "cpdf_cid2unicodemap.h",
+    "cpdf_cidfont.cpp",
+    "cpdf_cidfont.h",
+    "cpdf_cmap.cpp",
+    "cpdf_cmap.h",
+    "cpdf_cmapmanager.cpp",
+    "cpdf_cmapmanager.h",
+    "cpdf_cmapparser.cpp",
+    "cpdf_cmapparser.h",
+    "cpdf_font.cpp",
+    "cpdf_font.h",
+    "cpdf_fontencoding.cpp",
+    "cpdf_fontencoding.h",
+    "cpdf_fontglobals.cpp",
+    "cpdf_fontglobals.h",
+    "cpdf_simplefont.cpp",
+    "cpdf_simplefont.h",
+    "cpdf_tounicodemap.cpp",
+    "cpdf_tounicodemap.h",
+    "cpdf_truetypefont.cpp",
+    "cpdf_truetypefont.h",
+    "cpdf_type1font.cpp",
+    "cpdf_type1font.h",
+    "cpdf_type3char.cpp",
+    "cpdf_type3char.h",
+    "cpdf_type3font.cpp",
+    "cpdf_type3font.h",
+  ]
+  configs += [ "../../../:pdfium_core_config" ]
+  deps = [
+    "../../fxcrt",
+    "../../fxge",
+    "../cmaps",
+    "../parser",
+  ]
+  if (is_mac) {
+    libs = [ "CoreFoundation.framework" ]
+  }
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cpdf_cidfont_unittest.cpp",
+    "cpdf_cmapparser_unittest.cpp",
+    "cpdf_tounicodemap_unittest.cpp",
+  ]
+  deps = [
+    ":font",
+    "../page",
+    "../parser",
+    "../render",
+  ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/core/fpdfapi/font/cfx_cttgsubtable.cpp b/core/fpdfapi/font/cfx_cttgsubtable.cpp
index ae868ca..49f93dc 100644
--- a/core/fpdfapi/font/cfx_cttgsubtable.cpp
+++ b/core/fpdfapi/font/cfx_cttgsubtable.cpp
@@ -8,13 +8,46 @@
 
 #include <utility>
 
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/cfx_fontmapper.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
-CFX_CTTGSUBTable::CFX_CTTGSUBTable() : m_bFeautureMapLoad(false) {}
+namespace {
 
-CFX_CTTGSUBTable::~CFX_CTTGSUBTable() {}
+bool IsVerticalFeatureTag(uint32_t tag) {
+  static constexpr uint32_t kTags[] = {
+      CFX_FontMapper::MakeTag('v', 'r', 't', '2'),
+      CFX_FontMapper::MakeTag('v', 'e', 'r', 't'),
+  };
+  return tag == kTags[0] || tag == kTags[1];
+}
+
+}  // namespace
+
+CFX_CTTGSUBTable::CFX_CTTGSUBTable(FT_Bytes gsub) {
+  if (!LoadGSUBTable(gsub))
+    return;
+
+  for (const TScriptRecord& script : ScriptList) {
+    for (const auto& record : script.LangSysRecords) {
+      for (uint16_t index : record.FeatureIndices) {
+        if (IsVerticalFeatureTag(FeatureList[index].FeatureTag))
+          m_featureSet.insert(index);
+      }
+    }
+  }
+  if (!m_featureSet.empty())
+    return;
+
+  int i = 0;
+  for (const TFeatureRecord& feature : FeatureList) {
+    if (IsVerticalFeatureTag(feature.FeatureTag))
+      m_featureSet.insert(i);
+    ++i;
+  }
+}
+
+CFX_CTTGSUBTable::~CFX_CTTGSUBTable() = default;
 
 bool CFX_CTTGSUBTable::LoadGSUBTable(FT_Bytes gsub) {
   if ((gsub[0] << 24u | gsub[1] << 16u | gsub[2] << 8u | gsub[3]) != 0x00010000)
@@ -24,61 +57,33 @@
                &gsub[gsub[8] << 8 | gsub[9]]);
 }
 
-bool CFX_CTTGSUBTable::GetVerticalGlyph(uint32_t glyphnum,
-                                        uint32_t* vglyphnum) {
-  uint32_t tag[] = {
-      (uint8_t)'v' << 24 | (uint8_t)'r' << 16 | (uint8_t)'t' << 8 |
-          (uint8_t)'2',
-      (uint8_t)'v' << 24 | (uint8_t)'e' << 16 | (uint8_t)'r' << 8 |
-          (uint8_t)'t',
-  };
-  if (!m_bFeautureMapLoad) {
-    for (const TScriptRecord& script : ScriptList) {
-      for (const auto& record : script.LangSysRecords) {
-        for (const auto& index : record.FeatureIndices) {
-          if (FeatureList[index].FeatureTag == tag[0] ||
-              FeatureList[index].FeatureTag == tag[1]) {
-            m_featureSet.insert(index);
-          }
-        }
-      }
-    }
-    if (m_featureSet.empty()) {
-      int i = 0;
-      for (const TFeatureRecord& feature : FeatureList) {
-        if (feature.FeatureTag == tag[0] || feature.FeatureTag == tag[1])
-          m_featureSet.insert(i);
-        ++i;
-      }
-    }
-    m_bFeautureMapLoad = true;
+uint32_t CFX_CTTGSUBTable::GetVerticalGlyph(uint32_t glyphnum) const {
+  uint32_t vglyphnum = 0;
+  for (uint32_t item : m_featureSet) {
+    if (GetVerticalGlyphSub(FeatureList[item], glyphnum, &vglyphnum))
+      break;
   }
-  for (const auto& item : m_featureSet) {
-    if (GetVerticalGlyphSub(glyphnum, vglyphnum, &FeatureList[item])) {
-      return true;
-    }
-  }
-  return false;
+  return vglyphnum;
 }
 
-bool CFX_CTTGSUBTable::GetVerticalGlyphSub(uint32_t glyphnum,
-                                           uint32_t* vglyphnum,
-                                           TFeatureRecord* Feature) {
-  for (int index : Feature->LookupListIndices) {
+bool CFX_CTTGSUBTable::GetVerticalGlyphSub(const TFeatureRecord& feature,
+                                           uint32_t glyphnum,
+                                           uint32_t* vglyphnum) const {
+  for (int index : feature.LookupListIndices) {
     if (!pdfium::IndexInBounds(LookupList, index))
       continue;
     if (LookupList[index].LookupType == 1 &&
-        GetVerticalGlyphSub2(glyphnum, vglyphnum, &LookupList[index])) {
+        GetVerticalGlyphSub2(LookupList[index], glyphnum, vglyphnum)) {
       return true;
     }
   }
   return false;
 }
 
-bool CFX_CTTGSUBTable::GetVerticalGlyphSub2(uint32_t glyphnum,
-                                            uint32_t* vglyphnum,
-                                            TLookup* Lookup) {
-  for (const auto& subTable : Lookup->SubTables) {
+bool CFX_CTTGSUBTable::GetVerticalGlyphSub2(const TLookup& lookup,
+                                            uint32_t glyphnum,
+                                            uint32_t* vglyphnum) const {
+  for (const auto& subTable : lookup.SubTables) {
     switch (subTable->SubstFormat) {
       case 1: {
         auto* tbl1 = static_cast<TSubTable1*>(subTable.get());
diff --git a/core/fpdfapi/font/cfx_cttgsubtable.h b/core/fpdfapi/font/cfx_cttgsubtable.h
index fc1caf5..54e16b4 100644
--- a/core/fpdfapi/font/cfx_cttgsubtable.h
+++ b/core/fpdfapi/font/cfx_cttgsubtable.h
@@ -17,11 +17,10 @@
 
 class CFX_CTTGSUBTable {
  public:
-  CFX_CTTGSUBTable();
+  explicit CFX_CTTGSUBTable(FT_Bytes gsub);
   ~CFX_CTTGSUBTable();
 
-  bool LoadGSUBTable(FT_Bytes gsub);
-  bool GetVerticalGlyph(uint32_t glyphnum, uint32_t* vglyphnum);
+  uint32_t GetVerticalGlyph(uint32_t glyphnum) const;
 
  private:
   struct TLangSysRecord {
@@ -61,18 +60,18 @@
   };
 
   struct TCoverageFormatBase {
-    virtual ~TCoverageFormatBase() {}
+    virtual ~TCoverageFormatBase() = default;
     uint16_t CoverageFormat;
   };
 
-  struct TCoverageFormat1 : public TCoverageFormatBase {
+  struct TCoverageFormat1 final : public TCoverageFormatBase {
     TCoverageFormat1();
     ~TCoverageFormat1() override;
 
     std::vector<uint16_t> GlyphArray;
   };
 
-  struct TCoverageFormat2 : public TCoverageFormatBase {
+  struct TCoverageFormat2 final : public TCoverageFormatBase {
     TCoverageFormat2();
     ~TCoverageFormat2() override;
 
@@ -95,14 +94,14 @@
     uint16_t SubstFormat;
   };
 
-  struct TSubTable1 : public TSubTableBase {
+  struct TSubTable1 final : public TSubTableBase {
     TSubTable1();
     ~TSubTable1() override;
 
     int16_t DeltaGlyphID;
   };
 
-  struct TSubTable2 : public TSubTableBase {
+  struct TSubTable2 final : public TSubTableBase {
     TSubTable2();
     ~TSubTable2() override;
 
@@ -118,6 +117,7 @@
     std::vector<std::unique_ptr<TSubTableBase>> SubTables;
   };
 
+  bool LoadGSUBTable(FT_Bytes gsub);
   bool Parse(FT_Bytes scriptlist, FT_Bytes featurelist, FT_Bytes lookuplist);
   void ParseScriptList(FT_Bytes raw);
   void ParseScript(FT_Bytes raw, TScriptRecord* rec);
@@ -133,12 +133,12 @@
   void ParseSingleSubstFormat1(FT_Bytes raw, TSubTable1* rec);
   void ParseSingleSubstFormat2(FT_Bytes raw, TSubTable2* rec);
 
-  bool GetVerticalGlyphSub(uint32_t glyphnum,
-                           uint32_t* vglyphnum,
-                           TFeatureRecord* Feature);
-  bool GetVerticalGlyphSub2(uint32_t glyphnum,
-                            uint32_t* vglyphnum,
-                            TLookup* Lookup);
+  bool GetVerticalGlyphSub(const TFeatureRecord& feature,
+                           uint32_t glyphnum,
+                           uint32_t* vglyphnum) const;
+  bool GetVerticalGlyphSub2(const TLookup& lookup,
+                            uint32_t glyphnum,
+                            uint32_t* vglyphnum) const;
   int GetCoverageIndex(TCoverageFormatBase* Coverage, uint32_t g) const;
 
   uint8_t GetUInt8(FT_Bytes& p) const;
@@ -148,7 +148,6 @@
   uint32_t GetUInt32(FT_Bytes& p) const;
 
   std::set<uint32_t> m_featureSet;
-  bool m_bFeautureMapLoad;
   std::vector<TScriptRecord> ScriptList;
   std::vector<TFeatureRecord> FeatureList;
   std::vector<TLookup> LookupList;
diff --git a/core/fpdfapi/font/cfx_stockfontarray.cpp b/core/fpdfapi/font/cfx_stockfontarray.cpp
index 0808471..a8d8597 100644
--- a/core/fpdfapi/font/cfx_stockfontarray.cpp
+++ b/core/fpdfapi/font/cfx_stockfontarray.cpp
@@ -9,27 +9,31 @@
 #include <memory>
 #include <utility>
 
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fxcrt/fx_memory.h"
 
-CFX_StockFontArray::CFX_StockFontArray() {}
+CFX_StockFontArray::CFX_StockFontArray() = default;
 
 CFX_StockFontArray::~CFX_StockFontArray() {
   for (size_t i = 0; i < FX_ArraySize(m_StockFonts); ++i) {
-    if (m_StockFonts[i])
-      delete m_StockFonts[i]->GetFontDict();
+    if (m_StockFonts[i]) {
+      RetainPtr<CPDF_Dictionary> destroy(m_StockFonts[i]->GetFontDict());
+      m_StockFonts[i]->ClearFontDict();
+    }
   }
 }
 
-CPDF_Font* CFX_StockFontArray::GetFont(uint32_t index) const {
-  if (index >= FX_ArraySize(m_StockFonts))
-    return nullptr;
-  return m_StockFonts[index].get();
+RetainPtr<CPDF_Font> CFX_StockFontArray::GetFont(
+    CFX_FontMapper::StandardFont index) const {
+  if (index < FX_ArraySize(m_StockFonts))
+    return m_StockFonts[index];
+  NOTREACHED();
+  return nullptr;
 }
 
-CPDF_Font* CFX_StockFontArray::SetFont(uint32_t index,
-                                       std::unique_ptr<CPDF_Font> pFont) {
-  CPDF_Font* result = pFont.get();
+void CFX_StockFontArray::SetFont(CFX_FontMapper::StandardFont index,
+                                 const RetainPtr<CPDF_Font>& pFont) {
   if (index < FX_ArraySize(m_StockFonts))
-    m_StockFonts[index] = std::move(pFont);
-  return result;
+    m_StockFonts[index] = pFont;
 }
diff --git a/core/fpdfapi/font/cfx_stockfontarray.h b/core/fpdfapi/font/cfx_stockfontarray.h
index 50a13c1..5e54704 100644
--- a/core/fpdfapi/font/cfx_stockfontarray.h
+++ b/core/fpdfapi/font/cfx_stockfontarray.h
@@ -9,19 +9,22 @@
 
 #include <memory>
 
-#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_fontmapper.h"
+
+class CPDF_Font;
 
 class CFX_StockFontArray {
  public:
   CFX_StockFontArray();
   ~CFX_StockFontArray();
 
-  // Takes ownership of |pFont|, returns unowned pointer to it.
-  CPDF_Font* SetFont(uint32_t index, std::unique_ptr<CPDF_Font> pFont);
-  CPDF_Font* GetFont(uint32_t index) const;
+  RetainPtr<CPDF_Font> GetFont(CFX_FontMapper::StandardFont index) const;
+  void SetFont(CFX_FontMapper::StandardFont index,
+               const RetainPtr<CPDF_Font>& pFont);
 
  private:
-  std::unique_ptr<CPDF_Font> m_StockFonts[14];
+  RetainPtr<CPDF_Font> m_StockFonts[14];
 };
 
 #endif  // CORE_FPDFAPI_FONT_CFX_STOCKFONTARRAY_H_
diff --git a/core/fpdfapi/font/cpdf_cid2unicodemap.cpp b/core/fpdfapi/font/cpdf_cid2unicodemap.cpp
index 6508564..abb23ac 100644
--- a/core/fpdfapi/font/cpdf_cid2unicodemap.cpp
+++ b/core/fpdfapi/font/cpdf_cid2unicodemap.cpp
@@ -6,37 +6,21 @@
 
 #include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_cmapmanager.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/font/cpdf_fontglobals.h"
 
-CPDF_CID2UnicodeMap::CPDF_CID2UnicodeMap() {
-  m_EmbeddedCount = 0;
+CPDF_CID2UnicodeMap::CPDF_CID2UnicodeMap(CIDSet charset)
+    : m_Charset(charset),
+      m_pEmbeddedMap(
+          CPDF_FontGlobals::GetInstance()->GetEmbeddedToUnicode(m_Charset)) {}
+
+CPDF_CID2UnicodeMap::~CPDF_CID2UnicodeMap() = default;
+
+bool CPDF_CID2UnicodeMap::IsLoaded() const {
+  return !m_pEmbeddedMap.empty();
 }
 
-CPDF_CID2UnicodeMap::~CPDF_CID2UnicodeMap() {}
-
-bool CPDF_CID2UnicodeMap::IsLoaded() {
-  return m_EmbeddedCount != 0;
-}
-
-wchar_t CPDF_CID2UnicodeMap::UnicodeFromCID(uint16_t CID) {
-  if (m_Charset == CIDSET_UNICODE) {
-    return CID;
-  }
-  if (CID < m_EmbeddedCount) {
-    return m_pEmbeddedMap[CID];
-  }
-  return 0;
-}
-
-void CPDF_CID2UnicodeMap::Load(CPDF_CMapManager* pMgr,
-                               CIDSet charset,
-                               bool bPromptCJK) {
-  m_Charset = charset;
-
-  CPDF_FontGlobals* pFontGlobals =
-      CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-  std::tie(m_EmbeddedCount, m_pEmbeddedMap) =
-      pFontGlobals->GetEmbeddedToUnicode(charset);
+wchar_t CPDF_CID2UnicodeMap::UnicodeFromCID(uint16_t cid) const {
+  if (m_Charset == CIDSET_UNICODE)
+    return cid;
+  return cid < m_pEmbeddedMap.size() ? m_pEmbeddedMap[cid] : 0;
 }
diff --git a/core/fpdfapi/font/cpdf_cid2unicodemap.h b/core/fpdfapi/font/cpdf_cid2unicodemap.h
index 2c3fe5b..e556917 100644
--- a/core/fpdfapi/font/cpdf_cid2unicodemap.h
+++ b/core/fpdfapi/font/cpdf_cid2unicodemap.h
@@ -8,22 +8,19 @@
 #define CORE_FPDFAPI_FONT_CPDF_CID2UNICODEMAP_H_
 
 #include "core/fpdfapi/font/cpdf_cidfont.h"
-
-class CPDF_CMapManager;
+#include "third_party/base/span.h"
 
 class CPDF_CID2UnicodeMap {
  public:
-  CPDF_CID2UnicodeMap();
+  explicit CPDF_CID2UnicodeMap(CIDSet charset);
   ~CPDF_CID2UnicodeMap();
 
-  bool IsLoaded();
-  void Load(CPDF_CMapManager* pMgr, CIDSet charset, bool bPromptCJK);
-  wchar_t UnicodeFromCID(uint16_t CID);
+  bool IsLoaded() const;
+  wchar_t UnicodeFromCID(uint16_t cid) const;
 
  private:
-  CIDSet m_Charset;
-  const uint16_t* m_pEmbeddedMap;
-  uint32_t m_EmbeddedCount;
+  const CIDSet m_Charset;
+  const pdfium::span<const uint16_t> m_pEmbeddedMap;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_CID2UNICODEMAP_H_
diff --git a/core/fpdfapi/font/cpdf_cidfont.cpp b/core/fpdfapi/font/cpdf_cidfont.cpp
index 21f8add..0c02b9e 100644
--- a/core/fpdfapi/font/cpdf_cidfont.cpp
+++ b/core/fpdfapi/font/cpdf_cidfont.cpp
@@ -10,19 +10,21 @@
 #include <limits>
 #include <vector>
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "build/build_config.h"
+#include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 #include "core/fpdfapi/font/cfx_cttgsubtable.h"
 #include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
 #include "core/fpdfapi/font/cpdf_cmap.h"
 #include "core/fpdfapi/font/cpdf_cmapparser.h"
 #include "core/fpdfapi/font/cpdf_fontencoding.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#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_acc.h"
+#include "core/fxge/fx_font.h"
 #include "third_party/base/numerics/safe_math.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 #include "third_party/base/stl_util.h"
 
 namespace {
@@ -118,14 +120,13 @@
 };
 
 // Boundary values to avoid integer overflow when multiplied by 1000.
-const long kMinCBox = -2147483;
-const long kMaxCBox = 2147483;
+constexpr long kMinCBox = -2147483;
+constexpr long kMaxCBox = 2147483;
 
-CPDF_FontGlobals* GetFontGlobals() {
-  return CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-}
+// Boundary value to avoid integer overflow when adding 1/64th of the value.
+constexpr int kMaxRectTop = 2114445437;
 
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
 
 bool IsValidEmbeddedCharcodeFromUnicodeCharset(CIDSet charset) {
   switch (charset) {
@@ -146,16 +147,13 @@
   if (!IsValidEmbeddedCharcodeFromUnicodeCharset(charset))
     return 0;
 
-  uint16_t cid = FPDFAPI_CIDFromCharCode(pEmbedMap, charcode);
+  uint16_t cid = CIDFromCharCode(pEmbedMap, charcode);
   if (!cid)
     return 0;
 
-  const uint16_t* map;
-  uint32_t count;
-  std::tie(count, map) = GetFontGlobals()->GetEmbeddedToUnicode(charset);
-  if (map && cid < count)
-    return map[cid];
-  return 0;
+  pdfium::span<const uint16_t> map =
+      CPDF_FontGlobals::GetInstance()->GetEmbeddedToUnicode(charset);
+  return cid < map.size() ? map[cid] : 0;
 }
 
 uint32_t EmbeddedCharcodeFromUnicode(const FXCMAP_CMap* pEmbedMap,
@@ -164,15 +162,11 @@
   if (!IsValidEmbeddedCharcodeFromUnicodeCharset(charset))
     return 0;
 
-  const uint16_t* map;
-  uint32_t count;
-  std::tie(count, map) = GetFontGlobals()->GetEmbeddedToUnicode(charset);
-  if (!map)
-    return 0;
-
-  for (uint32_t i = 0; i < count; ++i) {
+  pdfium::span<const uint16_t> map =
+      CPDF_FontGlobals::GetInstance()->GetEmbeddedToUnicode(charset);
+  for (uint32_t i = 0; i < map.size(); ++i) {
     if (map[i] == unicode) {
-      uint32_t charCode = FPDFAPI_CharCodeFromCID(pEmbedMap, i);
+      uint32_t charCode = CharCodeFromCID(pEmbedMap, i);
       if (charCode)
         return charCode;
     }
@@ -180,31 +174,31 @@
   return 0;
 }
 
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#endif  // !defined(OS_WIN)
 
-void FT_UseCIDCharmap(FXFT_Face face, int coding) {
+void FT_UseCIDCharmap(FXFT_FaceRec* face, int coding) {
   int encoding;
   switch (coding) {
     case CIDCODING_GB:
-      encoding = FXFT_ENCODING_GB2312;
+      encoding = FT_ENCODING_GB2312;
       break;
     case CIDCODING_BIG5:
-      encoding = FXFT_ENCODING_BIG5;
+      encoding = FT_ENCODING_BIG5;
       break;
     case CIDCODING_JIS:
-      encoding = FXFT_ENCODING_SJIS;
+      encoding = FT_ENCODING_SJIS;
       break;
     case CIDCODING_KOREA:
-      encoding = FXFT_ENCODING_JOHAB;
+      encoding = FT_ENCODING_JOHAB;
       break;
     default:
-      encoding = FXFT_ENCODING_UNICODE;
+      encoding = FT_ENCODING_UNICODE;
   }
   int err = FXFT_Select_Charmap(face, encoding);
   if (err)
-    err = FXFT_Select_Charmap(face, FXFT_ENCODING_UNICODE);
+    err = FXFT_Select_Charmap(face, FT_ENCODING_UNICODE);
   if (err && FXFT_Get_Face_Charmaps(face))
-    FXFT_Set_Charmap(face, *FXFT_Get_Face_Charmaps(face));
+    FT_Set_Charmap(face, *FXFT_Get_Face_Charmaps(face));
 }
 
 bool IsMetricForCID(const uint32_t* pEntry, uint16_t CID) {
@@ -213,11 +207,8 @@
 
 }  // namespace
 
-CPDF_CIDFont::CPDF_CIDFont()
-    : m_pCID2UnicodeMap(nullptr),
-      m_bCIDIsGID(false),
-      m_bAnsiWidthsFixed(false),
-      m_bAdobeCourierStd(false) {
+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);
 }
@@ -266,7 +257,7 @@
   if (m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded() && m_pCMap->IsLoaded())
     return m_pCID2UnicodeMap->UnicodeFromCID(CIDFromCharCode(charcode));
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   wchar_t unicode;
   int charsize = 1;
   if (charcode > 255) {
@@ -314,7 +305,7 @@
     return static_cast<uint32_t>(unicode);
   if (m_pCMap->GetCoding() == CIDCODING_CID)
     return 0;
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   uint8_t buffer[32];
   int ret = FXSYS_WideCharToMultiByte(
       g_CharsetCPs[m_pCMap->GetCoding()], 0, &unicode, 1,
@@ -338,25 +329,22 @@
     return true;
   }
 
-  CPDF_Array* pFonts = m_pFontDict->GetArrayFor("DescendantFonts");
-  if (!pFonts || pFonts->GetCount() != 1)
+  const CPDF_Array* pFonts = m_pFontDict->GetArrayFor("DescendantFonts");
+  if (!pFonts || pFonts->size() != 1)
     return false;
 
-  CPDF_Dictionary* pCIDFontDict = pFonts->GetDictAt(0);
+  const CPDF_Dictionary* pCIDFontDict = pFonts->GetDictAt(0);
   if (!pCIDFontDict)
     return false;
 
-  m_BaseFont = pCIDFontDict->GetStringFor("BaseFont");
-  if ((m_BaseFont.Compare("CourierStd") == 0 ||
-       m_BaseFont.Compare("CourierStd-Bold") == 0 ||
-       m_BaseFont.Compare("CourierStd-BoldOblique") == 0 ||
-       m_BaseFont.Compare("CourierStd-Oblique") == 0) &&
+  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_Dictionary* pFontDesc = pCIDFontDict->GetDictFor("FontDescriptor");
-  if (pFontDesc)
-    LoadFontDescriptor(pFontDesc);
 
   CPDF_Object* pEncoding = m_pFontDict->GetDirectObjectFor("Encoding");
   if (!pEncoding)
@@ -365,62 +353,54 @@
   ByteString subtype = pCIDFontDict->GetStringFor("Subtype");
   m_bType1 = (subtype == "CIDFontType0");
 
-  CPDF_CMapManager* manager = GetFontGlobals()->GetCMapManager();
+  CPDF_CMapManager* manager = CPDF_FontGlobals::GetInstance()->GetCMapManager();
   if (pEncoding->IsName()) {
     ByteString cmap = pEncoding->GetString();
-    bool bPromptCJK = m_pFontFile && m_bType1;
-    m_pCMap = manager->GetPredefinedCMap(cmap, bPromptCJK);
-    if (!m_pCMap)
-      return false;
+    m_pCMap = manager->GetPredefinedCMap(cmap);
   } else if (CPDF_Stream* pStream = pEncoding->AsStream()) {
     auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
     pAcc->LoadAllDataFiltered();
-    m_pCMap = pdfium::MakeRetain<CPDF_CMap>();
-    m_pCMap->LoadEmbedded(pAcc->GetData(), pAcc->GetSize());
+    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) {
-    CPDF_Dictionary* pCIDInfo = pCIDFontDict->GetDictFor("CIDSystemInfo");
+    const CPDF_Dictionary* pCIDInfo = pCIDFontDict->GetDictFor("CIDSystemInfo");
     if (pCIDInfo) {
       m_Charset = CPDF_CMapParser::CharsetFromOrdering(
           pCIDInfo->GetStringFor("Ordering").AsStringView());
     }
   }
   if (m_Charset != CIDSET_UNKNOWN) {
-    bool bPromptCJK = !m_pFontFile && (m_pCMap->GetCoding() == CIDCODING_CID ||
-                                       pCIDFontDict->KeyExist("W"));
-    m_pCID2UnicodeMap = manager->GetCID2UnicodeMap(m_Charset, bPromptCJK);
+    m_pCID2UnicodeMap = manager->GetCID2UnicodeMap(m_Charset);
   }
-  if (m_Font.GetFace()) {
+  if (m_Font.GetFaceRec()) {
     if (m_bType1)
-      FXFT_Select_Charmap(m_Font.GetFace(), FXFT_ENCODING_UNICODE);
+      FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE);
     else
-      FT_UseCIDCharmap(m_Font.GetFace(), m_pCMap->GetCoding());
+      FT_UseCIDCharmap(m_Font.GetFaceRec(), m_pCMap->GetCoding());
   }
   m_DefaultWidth = pCIDFontDict->GetIntegerFor("DW", 1000);
-  CPDF_Array* pWidthArray = pCIDFontDict->GetArrayFor("W");
+  const CPDF_Array* pWidthArray = pCIDFontDict->GetArrayFor("W");
   if (pWidthArray)
     LoadMetricsArray(pWidthArray, &m_WidthList, 1);
   if (!IsEmbedded())
     LoadSubstFont();
 
-  if (m_pFontFile) {
-    CPDF_Object* pmap = pCIDFontDict->GetDirectObjectFor("CIDToGIDMap");
-    if (pmap) {
-      if (CPDF_Stream* pStream = pmap->AsStream()) {
-        m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-        m_pStreamAcc->LoadAllDataFiltered();
-      } else if (pmap->GetString() == "Identity") {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-        if (m_pFontFile)
-          m_bCIDIsGID = true;
-#else
-        m_bCIDIsGID = true;
-#endif
-      }
+  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;
     }
   }
 
@@ -429,13 +409,10 @@
     pWidthArray = pCIDFontDict->GetArrayFor("W2");
     if (pWidthArray)
       LoadMetricsArray(pWidthArray, &m_VertMetrics, 3);
-    CPDF_Array* pDefaultArray = pCIDFontDict->GetArrayFor("DW2");
+    const CPDF_Array* pDefaultArray = pCIDFontDict->GetArrayFor("DW2");
     if (pDefaultArray) {
       m_DefaultVY = pDefaultArray->GetIntegerAt(0);
       m_DefaultW1 = pDefaultArray->GetIntegerAt(1);
-    } else {
-      m_DefaultVY = 880;
-      m_DefaultW1 = -1000;
     }
   }
   return true;
@@ -448,23 +425,23 @@
   FX_RECT rect;
   bool bVert = false;
   int glyph_index = GlyphFromCharCode(charcode, &bVert);
-  FXFT_Face face = m_Font.GetFace();
+  FXFT_FaceRec* face = m_Font.GetFaceRec();
   if (face) {
     if (FXFT_Is_Face_Tricky(face)) {
-      int err = FXFT_Load_Glyph(face, glyph_index,
-                                FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
+      int err =
+          FT_Load_Glyph(face, glyph_index, FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
       if (!err) {
-        FXFT_Glyph glyph;
-        err = FXFT_Get_Glyph(((FXFT_Face)face)->glyph, &glyph);
+        FT_Glyph glyph;
+        err = FT_Get_Glyph(face->glyph, &glyph);
         if (!err) {
-          FXFT_BBox cbox;
-          FXFT_Glyph_Get_CBox(glyph, FXFT_GLYPH_BBOX_PIXELS, &cbox);
+          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 = ((FXFT_Face)face)->size->metrics.x_ppem;
-          int pixel_size_y = ((FXFT_Face)face)->size->metrics.y_ppem;
+          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 {
@@ -477,11 +454,11 @@
                               static_cast<int>(FXFT_Get_Face_Ascender(face)));
           rect.bottom = std::max(
               rect.bottom, static_cast<int>(FXFT_Get_Face_Descender(face)));
-          FXFT_Done_Glyph(glyph);
+          FT_Done_Glyph(glyph);
         }
       }
     } else {
-      int err = FXFT_Load_Glyph(face, glyph_index, FXFT_LOAD_NO_SCALE);
+      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),
@@ -491,7 +468,10 @@
                        TT2PDF(FXFT_Get_Glyph_HoriBearingY(face) -
                                   FXFT_Get_Glyph_Height(face),
                               face));
-        rect.top += rect.top / 64;
+        if (rect.top <= kMaxRectTop)
+          rect.top += rect.top / 64;
+        else
+          rect.top = std::numeric_limits<int>::max();
       }
     }
   }
@@ -514,7 +494,7 @@
   return rect;
 }
 
-int CPDF_CIDFont::GetCharWidthF(uint32_t charcode) {
+uint32_t CPDF_CIDFont::GetCharWidthF(uint32_t charcode) {
   if (charcode < 0x80 && m_bAnsiWidthsFixed)
     return (charcode >= 32 && charcode < 127) ? 500 : 0;
 
@@ -524,7 +504,7 @@
   for (size_t i = 0; i < size; i += 3) {
     const uint32_t* pEntry = pList + i;
     if (IsMetricForCID(pEntry, cid))
-      return static_cast<int>(pEntry[2]);
+      return pEntry[2];
   }
   return m_DefaultWidth;
 }
@@ -573,8 +553,8 @@
   if (pVertGlyph)
     *pVertGlyph = false;
 
-  FXFT_Face face = m_Font.GetFace();
-  int index = FXFT_Get_Char_Index(face, unicode);
+  FXFT_FaceRec* face = m_Font.GetFaceRec();
+  int index = FT_Get_Char_Index(face, unicode);
   if (unicode == 0x2502)
     return index;
 
@@ -586,24 +566,22 @@
 
   if (!m_Font.GetSubData()) {
     unsigned long length = 0;
-    int error = FXFT_Load_Sfnt_Table(face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0,
-                                     nullptr, &length);
+    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 = FXFT_Load_Sfnt_Table(face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0,
-                                   m_Font.GetSubData(), nullptr);
+  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_pTTGSUBTable->LoadGSUBTable((FT_Bytes)m_Font.GetSubData());
+  m_pTTGSUBTable = pdfium::MakeUnique<CFX_CTTGSUBTable>(m_Font.GetSubData());
   return GetVerticalGlyph(index, pVertGlyph);
 }
 
 int CPDF_CIDFont::GetVerticalGlyph(int index, bool* pVertGlyph) {
-  uint32_t vindex = 0;
-  m_pTTGSUBTable->GetVerticalGlyph(index, &vindex);
+  uint32_t vindex = m_pTTGSUBTable->GetVerticalGlyph(index);
   if (!vindex)
     return index;
 
@@ -617,13 +595,11 @@
   if (pVertGlyph)
     *pVertGlyph = false;
 
-  if (!m_pFontFile && !m_pStreamAcc) {
+  if (!m_pFontFile && (!m_pStreamAcc || m_pCID2UnicodeMap)) {
     uint16_t cid = CIDFromCharCode(charcode);
     wchar_t unicode = 0;
     if (m_bCIDIsGID) {
-#if _FX_PLATFORM_ != _FX_PLATFORM_APPLE_
-      return cid;
-#else
+#if defined(OS_MACOSX)
       if (FontStyleIsSymbolic(m_Flags))
         return cid;
 
@@ -632,6 +608,8 @@
         return cid;
 
       unicode = uni_str[0];
+#else
+      return cid;
 #endif
     } else {
       if (cid && m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded())
@@ -644,7 +622,7 @@
           unicode = unicode_str[0];
       }
     }
-    FXFT_Face face = m_Font.GetFace();
+    FXFT_FaceRec* face = m_Font.GetFaceRec();
     if (unicode == 0) {
       if (!m_bAdobeCourierStd)
         return charcode ? static_cast<int>(charcode) : -1;
@@ -668,16 +646,16 @@
         return charcode ? static_cast<int>(charcode) : -1;
 
       if (iBaseEncoding == PDFFONT_ENCODING_STANDARD)
-        return FXFT_Get_Char_Index(face, name_unicode);
+        return FT_Get_Char_Index(face, name_unicode);
 
       if (iBaseEncoding == PDFFONT_ENCODING_WINANSI) {
-        index = FXFT_Get_Char_Index(face, name_unicode);
+        index = FT_Get_Char_Index(face, name_unicode);
       } else {
         ASSERT(iBaseEncoding == PDFFONT_ENCODING_MACROMAN);
         uint32_t maccode =
-            FT_CharCodeFromUnicode(FXFT_ENCODING_APPLE_ROMAN, name_unicode);
-        index = maccode ? FXFT_Get_Char_Index(face, maccode)
-                        : FXFT_Get_Name_Index(face, const_cast<char*>(name));
+            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;
@@ -686,7 +664,7 @@
     if (m_Charset == CIDSET_JAPAN1) {
       if (unicode == '\\') {
         unicode = '/';
-#if _FX_PLATFORM_ != _FX_PLATFORM_APPLE_
+#if !defined(OS_MACOSX)
       } else if (unicode == 0xa5) {
         unicode = 0x5c;
 #endif
@@ -695,7 +673,7 @@
     if (!face)
       return unicode;
 
-    int err = FXFT_Select_Charmap(face, FXFT_ENCODING_UNICODE);
+    int err = FXFT_Select_Charmap(face, FT_ENCODING_UNICODE);
     if (err) {
       int i;
       for (i = 0; i < FXFT_Get_Face_CharmapCount(face); i++) {
@@ -704,12 +682,12 @@
             static_cast<wchar_t>(charcode));
         if (ret == 0)
           continue;
-        FXFT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[i]);
+        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) {
-        FXFT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]);
+        FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]);
         unicode = static_cast<wchar_t>(charcode);
       }
     }
@@ -720,7 +698,7 @@
     return unicode;
   }
 
-  if (!m_Font.GetFace())
+  if (!m_Font.GetFaceRec())
     return -1;
 
   uint16_t cid = CIDFromCharCode(charcode);
@@ -730,11 +708,11 @@
     if (m_pFontFile && m_pCMap->IsDirectCharcodeToCIDTableIsEmpty())
       return cid;
     if (m_pCMap->GetCoding() == CIDCODING_UNKNOWN ||
-        !FXFT_Get_Face_Charmap(m_Font.GetFace())) {
+        !FXFT_Get_Face_Charmap(m_Font.GetFaceRec())) {
       return cid;
     }
-    if (FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmap(m_Font.GetFace())) ==
-        FXFT_ENCODING_UNICODE) {
+    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;
@@ -751,18 +729,17 @@
   return pdata[0] * 256 + pdata[1];
 }
 
-uint32_t CPDF_CIDFont::GetNextChar(const char* pString,
-                                   int nStrLen,
-                                   int& offset) const {
-  return m_pCMap->GetNextChar(pString, nStrLen, offset);
+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);
 }
 
-int CPDF_CIDFont::CountChar(const char* pString, int size) const {
-  return m_pCMap->CountChar(pString, size);
+size_t CPDF_CIDFont::CountChar(ByteStringView pString) const {
+  return m_pCMap->CountChar(pString);
 }
 
 int CPDF_CIDFont::AppendChar(char* str, uint32_t charcode) const {
@@ -778,33 +755,33 @@
 void CPDF_CIDFont::LoadSubstFont() {
   pdfium::base::CheckedNumeric<int> safeStemV(m_StemV);
   safeStemV *= 5;
-  m_Font.LoadSubst(m_BaseFont, !m_bType1, m_Flags,
+  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(CPDF_Array* pArray,
+void CPDF_CIDFont::LoadMetricsArray(const CPDF_Array* pArray,
                                     std::vector<uint32_t>* result,
                                     int nElements) {
   int width_status = 0;
   int iCurElement = 0;
   uint32_t first_code = 0;
   uint32_t last_code = 0;
-  for (size_t i = 0; i < pArray->GetCount(); i++) {
-    CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
+  for (size_t i = 0; i < pArray->size(); i++) {
+    const CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
     if (!pObj)
       continue;
 
-    if (CPDF_Array* pObjArray = pObj->AsArray()) {
+    if (const CPDF_Array* pObjArray = pObj->AsArray()) {
       if (width_status != 1)
         return;
       if (first_code >
-          std::numeric_limits<uint32_t>::max() - pObjArray->GetCount()) {
+          std::numeric_limits<uint32_t>::max() - pObjArray->size()) {
         width_status = 0;
         continue;
       }
 
-      for (size_t j = 0; j < pObjArray->GetCount(); j += nElements) {
+      for (size_t j = 0; j < pObjArray->size(); j += nElements) {
         result->push_back(first_code);
         result->push_back(first_code);
         for (int k = 0; k < nElements; k++)
@@ -840,22 +817,19 @@
 }
 
 void CPDF_CIDFont::LoadGB2312() {
-  m_BaseFont = m_pFontDict->GetStringFor("BaseFont");
-  CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
+  m_BaseFontName = m_pFontDict->GetStringFor("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");
   if (pFontDesc)
     LoadFontDescriptor(pFontDesc);
 
-  m_Charset = CIDSET_GB1;
-  m_bType1 = false;
-
-  CPDF_CMapManager* manager = GetFontGlobals()->GetCMapManager();
-  m_pCMap = manager->GetPredefinedCMap("GBK-EUC-H", false);
-  m_pCID2UnicodeMap = manager->GetCID2UnicodeMap(m_Charset, false);
   if (!IsEmbedded())
     LoadSubstFont();
-
   CheckFontMetrics();
-  m_DefaultWidth = 1000;
   m_bAnsiWidthsFixed = true;
 }
 
diff --git a/core/fpdfapi/font/cpdf_cidfont.h b/core/fpdfapi/font/cpdf_cidfont.h
index f6ff83d..ce00e1d 100644
--- a/core/fpdfapi/font/cpdf_cidfont.h
+++ b/core/fpdfapi/font/cpdf_cidfont.h
@@ -32,9 +32,11 @@
 class CPDF_CMap;
 class CPDF_StreamAcc;
 
-class CPDF_CIDFont : public CPDF_Font {
+class CPDF_CIDFont final : public CPDF_Font {
  public:
-  CPDF_CIDFont();
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_CIDFont() override;
 
   static float CIDTransformToFloat(uint8_t ch);
@@ -44,12 +46,10 @@
   const CPDF_CIDFont* AsCIDFont() const override;
   CPDF_CIDFont* AsCIDFont() override;
   int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) override;
-  int GetCharWidthF(uint32_t charcode) override;
+  uint32_t GetCharWidthF(uint32_t charcode) override;
   FX_RECT GetCharBBox(uint32_t charcode) override;
-  uint32_t GetNextChar(const char* pString,
-                       int nStrLen,
-                       int& offset) const override;
-  int CountChar(const char* pString, int size) const override;
+  uint32_t GetNextChar(ByteStringView pString, size_t* pOffset) const override;
+  size_t CountChar(ByteStringView pString) const override;
   int AppendChar(char* str, uint32_t charcode) const override;
   bool IsVertWriting() const override;
   bool IsUnicodeCompatible() const override;
@@ -63,31 +63,33 @@
   void GetVertOrigin(uint16_t CID, short& vx, short& vy) const;
   int GetCharSize(uint32_t charcode) const;
 
- protected:
+ private:
+  CPDF_CIDFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+
   void LoadGB2312();
   int GetGlyphIndex(uint32_t unicodeb, bool* pVertGlyph);
   int GetVerticalGlyph(int index, bool* pVertGlyph);
-  void LoadMetricsArray(CPDF_Array* pArray,
+  void LoadMetricsArray(const CPDF_Array* pArray,
                         std::vector<uint32_t>* result,
                         int nElements);
   void LoadSubstFont();
   wchar_t GetUnicodeFromCharCode(uint32_t charcode) const;
 
-  RetainPtr<CPDF_CMap> m_pCMap;
-  UnownedPtr<CPDF_CID2UnicodeMap> m_pCID2UnicodeMap;
-  CIDSet m_Charset;
-  bool m_bType1;
-  bool m_bCIDIsGID;
-  uint16_t m_DefaultWidth;
+  RetainPtr<const CPDF_CMap> m_pCMap;
+  UnownedPtr<const CPDF_CID2UnicodeMap> m_pCID2UnicodeMap;
   RetainPtr<CPDF_StreamAcc> m_pStreamAcc;
-  bool m_bAnsiWidthsFixed;
-  FX_RECT m_CharBBox[256];
-  std::vector<uint32_t> m_WidthList;
-  short m_DefaultVY;
-  short m_DefaultW1;
-  std::vector<uint32_t> m_VertMetrics;
-  bool m_bAdobeCourierStd;
   std::unique_ptr<CFX_CTTGSUBTable> m_pTTGSUBTable;
+  bool m_bType1 = false;
+  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;
+  FX_RECT m_CharBBox[256];
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_CIDFONT_H_
diff --git a/core/fpdfapi/font/cpdf_cidfont_unittest.cpp b/core/fpdfapi/font/cpdf_cidfont_unittest.cpp
new file mode 100644
index 0000000..225ea3a
--- /dev/null
+++ b/core/fpdfapi/font/cpdf_cidfont_unittest.cpp
@@ -0,0 +1,58 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/font/cpdf_cidfont.h"
+
+#include <utility>
+
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.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(); }
+};
+
+TEST_F(CPDF_CIDFontTest, BUG_920636) {
+  CPDF_Document doc(pdfium::MakeUnique<CPDF_DocRenderData>(),
+                    pdfium::MakeUnique<CPDF_DocPageData>());
+  auto font_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  font_dict->SetNewFor<CPDF_Name>("Encoding", "Identity−H");
+
+  {
+    auto descendant_fonts = pdfium::MakeRetain<CPDF_Array>();
+    {
+      auto descendant_font = pdfium::MakeRetain<CPDF_Dictionary>();
+      descendant_font->SetNewFor<CPDF_Name>("BaseFont", "CourierStd");
+      descendant_fonts->Add(std::move(descendant_font));
+    }
+    font_dict->SetFor("DescendantFonts", std::move(descendant_fonts));
+  }
+
+  auto font = pdfium::MakeRetain<CPDF_CIDFont>(&doc, font_dict.Get());
+  ASSERT_TRUE(font->Load());
+
+  // It would be nice if we can test more values here. However, the glyph
+  // indices are sometimes machine dependent.
+  struct {
+    uint32_t charcode;
+    int glyph;
+  } static constexpr kTestCases[] = {
+      {0, 31},
+      {256, 287},
+      {34661, 34692},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    EXPECT_EQ(test_case.glyph,
+              font->GlyphFromCharCode(test_case.charcode, nullptr));
+  }
+}
diff --git a/core/fpdfapi/font/cpdf_cmap.cpp b/core/fpdfapi/font/cpdf_cmap.cpp
index a654fcc..844bc5f 100644
--- a/core/fpdfapi/font/cpdf_cmap.cpp
+++ b/core/fpdfapi/font/cpdf_cmap.cpp
@@ -10,9 +10,9 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/font/cpdf_cmapmanager.h"
+#include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 #include "core/fpdfapi/font/cpdf_cmapparser.h"
+#include "core/fpdfapi/font/cpdf_fontglobals.h"
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 
 namespace {
@@ -23,7 +23,7 @@
 };
 
 struct PredefinedCMap {
-  const char* m_pName;
+  const char* m_pName;  // Raw, POD struct.
   CIDSet m_Charset;
   CIDCoding m_Coding;
   CPDF_CMap::CodingScheme m_CodingScheme;
@@ -31,7 +31,7 @@
   ByteRange m_LeadingSegs[2];
 };
 
-const PredefinedCMap g_PredefinedCMaps[] = {
+constexpr PredefinedCMap kPredefinedCMaps[] = {
     {"GB-EUC",
      CIDSET_GB1,
      CIDCODING_GB,
@@ -181,6 +181,26 @@
     {"UniKS-UTF16", CIDSET_KOREA1, CIDCODING_UTF16, CPDF_CMap::TwoBytes, 0, {}},
 };
 
+const PredefinedCMap* GetPredefinedCMap(ByteStringView cmapid) {
+  if (cmapid.GetLength() > 2)
+    cmapid = cmapid.First(cmapid.GetLength() - 2);
+  for (const auto& map : kPredefinedCMaps) {
+    if (cmapid == map.m_pName)
+      return &map;
+  }
+  return nullptr;
+}
+
+std::vector<bool> LoadLeadingSegments(const PredefinedCMap& map) {
+  std::vector<bool> segments(256);
+  for (uint32_t i = 0; i < map.m_LeadingSegCount; ++i) {
+    const ByteRange& seg = map.m_LeadingSegs[i];
+    for (int b = seg.m_First; b <= seg.m_Last; ++b)
+      segments[b] = true;
+  }
+  return segments;
+}
+
 int CheckFourByteCodeRange(uint8_t* codes,
                            size_t size,
                            const std::vector<CPDF_CMap::CodeRange>& ranges) {
@@ -237,87 +257,53 @@
 
 }  // namespace
 
-CPDF_CMap::CPDF_CMap()
-    : m_bLoaded(false),
-      m_bVertical(false),
-      m_Charset(CIDSET_UNKNOWN),
-      m_CodingScheme(TwoBytes),
-      m_Coding(CIDCODING_UNKNOWN),
-      m_pEmbedMap(nullptr) {}
-
-CPDF_CMap::~CPDF_CMap() {}
-
-void CPDF_CMap::LoadPredefined(CPDF_CMapManager* pMgr,
-                               const ByteString& bsName,
-                               bool bPromptCJK) {
-  m_PredefinedCMap = bsName;
-  if (m_PredefinedCMap == "Identity-H" || m_PredefinedCMap == "Identity-V") {
+CPDF_CMap::CPDF_CMap(ByteStringView bsPredefinedName)
+    : m_bVertical(bsPredefinedName.Back() == 'V') {
+  if (bsPredefinedName == "Identity-H" || bsPredefinedName == "Identity-V") {
     m_Coding = CIDCODING_CID;
-    m_bVertical = bsName.Last() == 'V';
     m_bLoaded = true;
     return;
   }
-  ByteString cmapid = m_PredefinedCMap;
-  m_bVertical = cmapid.Last() == 'V';
-  if (cmapid.GetLength() > 2) {
-    cmapid = cmapid.Left(cmapid.GetLength() - 2);
-  }
-  const PredefinedCMap* map = nullptr;
-  for (size_t i = 0; i < FX_ArraySize(g_PredefinedCMaps); ++i) {
-    if (cmapid == ByteStringView(g_PredefinedCMaps[i].m_pName)) {
-      map = &g_PredefinedCMaps[i];
-      break;
-    }
-  }
+
+  const PredefinedCMap* map = GetPredefinedCMap(bsPredefinedName);
   if (!map)
     return;
 
   m_Charset = map->m_Charset;
   m_Coding = map->m_Coding;
   m_CodingScheme = map->m_CodingScheme;
-  if (m_CodingScheme == MixedTwoBytes) {
-    m_MixedTwoByteLeadingBytes = std::vector<bool>(256);
-    for (uint32_t i = 0; i < map->m_LeadingSegCount; ++i) {
-      const ByteRange& seg = map->m_LeadingSegs[i];
-      for (int b = seg.m_First; b <= seg.m_Last; ++b)
-        m_MixedTwoByteLeadingBytes[b] = true;
-    }
-  }
-  m_pEmbedMap = FPDFAPI_FindEmbeddedCMap(bsName, m_Charset, m_Coding);
+  if (m_CodingScheme == MixedTwoBytes)
+    m_MixedTwoByteLeadingBytes = LoadLeadingSegments(*map);
+  m_pEmbedMap = FindEmbeddedCMap(
+      CPDF_FontGlobals::GetInstance()->GetEmbeddedCharset(m_Charset),
+      bsPredefinedName);
   if (!m_pEmbedMap)
     return;
 
   m_bLoaded = true;
 }
 
-void CPDF_CMap::LoadEmbedded(const uint8_t* pData, uint32_t size) {
-  m_DirectCharcodeToCIDTable = std::vector<uint16_t>(65536);
+CPDF_CMap::CPDF_CMap(pdfium::span<const uint8_t> spEmbeddedData)
+    : m_DirectCharcodeToCIDTable(65536) {
   CPDF_CMapParser parser(this);
-  CPDF_SimpleParser syntax(pData, size);
+  CPDF_SimpleParser syntax(spEmbeddedData);
   while (1) {
     ByteStringView word = syntax.GetWord();
-    if (word.IsEmpty()) {
+    if (word.IsEmpty())
       break;
-    }
+
     parser.ParseWord(word);
   }
-  if (m_CodingScheme == MixedFourBytes && parser.HasAdditionalMappings()) {
-    m_AdditionalCharcodeToCIDMappings = parser.TakeAdditionalMappings();
-    std::sort(
-        m_AdditionalCharcodeToCIDMappings.begin(),
-        m_AdditionalCharcodeToCIDMappings.end(),
-        [](const CPDF_CMap::CIDRange& arg1, const CPDF_CMap::CIDRange& arg2) {
-          return arg1.m_EndCode < arg2.m_EndCode;
-        });
-  }
 }
 
+CPDF_CMap::~CPDF_CMap() = default;
+
 uint16_t CPDF_CMap::CIDFromCharCode(uint32_t charcode) const {
   if (m_Coding == CIDCODING_CID)
     return static_cast<uint16_t>(charcode);
 
   if (m_pEmbedMap)
-    return FPDFAPI_CIDFromCharCode(m_pEmbedMap, charcode);
+    return ::CIDFromCharCode(m_pEmbedMap.Get(), charcode);
 
   if (m_DirectCharcodeToCIDTable.empty())
     return static_cast<uint16_t>(charcode);
@@ -337,28 +323,29 @@
   return it->m_StartCID + charcode - it->m_StartCode;
 }
 
-uint32_t CPDF_CMap::GetNextChar(const char* pString,
-                                int nStrLen,
-                                int& offset) const {
-  auto* pBytes = reinterpret_cast<const uint8_t*>(pString);
+uint32_t CPDF_CMap::GetNextChar(ByteStringView pString, size_t* pOffset) const {
+  size_t& offset = *pOffset;
+  auto pBytes = pString.raw_span();
   switch (m_CodingScheme) {
     case OneByte: {
-      return pBytes[offset++];
+      return offset < pBytes.size() ? pBytes[offset++] : 0;
     }
     case TwoBytes: {
-      uint8_t byte1 = pBytes[offset++];
-      return 256 * byte1 + pBytes[offset++];
+      uint8_t byte1 = offset < pBytes.size() ? pBytes[offset++] : 0;
+      uint8_t byte2 = offset < pBytes.size() ? pBytes[offset++] : 0;
+      return 256 * byte1 + byte2;
     }
     case MixedTwoBytes: {
-      uint8_t byte1 = pBytes[offset++];
+      uint8_t byte1 = offset < pBytes.size() ? pBytes[offset++] : 0;
       if (!m_MixedTwoByteLeadingBytes[byte1])
         return byte1;
-      return 256 * byte1 + pBytes[offset++];
+      uint8_t byte2 = offset < pBytes.size() ? pBytes[offset++] : 0;
+      return 256 * byte1 + byte2;
     }
     case MixedFourBytes: {
       uint8_t codes[4];
       int char_size = 1;
-      codes[0] = pBytes[offset++];
+      codes[0] = offset < pBytes.size() ? pBytes[offset++] : 0;
       while (1) {
         int ret = CheckFourByteCodeRange(codes, char_size,
                                          m_MixedFourByteLeadingRanges);
@@ -370,7 +357,7 @@
             charcode = (charcode << 8) + codes[i];
           return charcode;
         }
-        if (char_size == 4 || offset == nStrLen)
+        if (char_size == 4 || offset == pBytes.size())
           return 0;
         codes[char_size++] = pBytes[offset++];
       }
@@ -402,33 +389,32 @@
   return 1;
 }
 
-int CPDF_CMap::CountChar(const char* pString, int size) const {
+size_t CPDF_CMap::CountChar(ByteStringView pString) const {
   switch (m_CodingScheme) {
     case OneByte:
-      return size;
+      return pString.GetLength();
     case TwoBytes:
-      return (size + 1) / 2;
+      return (pString.GetLength() + 1) / 2;
     case MixedTwoBytes: {
-      int count = 0;
-      for (int i = 0; i < size; i++) {
+      size_t count = 0;
+      for (size_t i = 0; i < pString.GetLength(); i++) {
         count++;
-        if (m_MixedTwoByteLeadingBytes[reinterpret_cast<const uint8_t*>(
-                pString)[i]]) {
+        if (m_MixedTwoByteLeadingBytes[pString[i]])
           i++;
-        }
       }
       return count;
     }
     case MixedFourBytes: {
-      int count = 0, offset = 0;
-      while (offset < size) {
-        GetNextChar(pString, size, offset);
+      size_t count = 0;
+      size_t offset = 0;
+      while (offset < pString.GetLength()) {
+        GetNextChar(pString, &offset);
         count++;
       }
       return count;
     }
   }
-  return size;
+  return pString.GetLength();
 }
 
 int CPDF_CMap::AppendChar(char* str, uint32_t charcode) const {
@@ -478,3 +464,20 @@
   }
   return 0;
 }
+
+void CPDF_CMap::SetAdditionalMappings(std::vector<CIDRange> mappings) {
+  ASSERT(m_AdditionalCharcodeToCIDMappings.empty());
+  if (m_CodingScheme != MixedFourBytes || mappings.empty())
+    return;
+
+  std::sort(
+      mappings.begin(), mappings.end(),
+      [](const CPDF_CMap::CIDRange& arg1, const CPDF_CMap::CIDRange& arg2) {
+        return arg1.m_EndCode < arg2.m_EndCode;
+      });
+  m_AdditionalCharcodeToCIDMappings = std::move(mappings);
+}
+
+void CPDF_CMap::SetMixedFourByteLeadingRanges(std::vector<CodeRange> ranges) {
+  m_MixedFourByteLeadingRanges = std::move(ranges);
+}
diff --git a/core/fpdfapi/font/cpdf_cmap.h b/core/fpdfapi/font/cpdf_cmap.h
index 98a7728..e2caf8d 100644
--- a/core/fpdfapi/font/cpdf_cmap.h
+++ b/core/fpdfapi/font/cpdf_cmap.h
@@ -11,8 +11,8 @@
 
 #include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/span.h"
 
-class CPDF_CMapManager;
 struct FXCMAP_CMap;
 
 enum CIDCoding : uint8_t {
@@ -26,7 +26,7 @@
   CIDCODING_UTF16,
 };
 
-class CPDF_CMap : public Retainable {
+class CPDF_CMap final : public Retainable {
  public:
   enum CodingScheme : uint8_t {
     OneByte,
@@ -50,29 +50,23 @@
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  void LoadPredefined(CPDF_CMapManager* pMgr,
-                      const ByteString& name,
-                      bool bPromptCJK);
-  void LoadEmbedded(const uint8_t* pData, uint32_t dwSize);
-
   bool IsLoaded() const { return m_bLoaded; }
   bool IsVertWriting() const { return m_bVertical; }
 
   uint16_t CIDFromCharCode(uint32_t charcode) const;
 
   int GetCharSize(uint32_t charcode) const;
-  uint32_t GetNextChar(const char* pString, int nStrLen, int& offset) const;
-  int CountChar(const char* pString, int size) const;
+  uint32_t GetNextChar(ByteStringView pString, size_t* pOffset) const;
+  size_t CountChar(ByteStringView pString) const;
   int AppendChar(char* str, uint32_t charcode) const;
 
   void SetVertical(bool vert) { m_bVertical = vert; }
   void SetCodingScheme(CodingScheme scheme) { m_CodingScheme = scheme; }
-  void SetMixedFourByteLeadingRanges(std::vector<CodeRange> range) {
-    m_MixedFourByteLeadingRanges = range;
-  }
+  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; }
+  const FXCMAP_CMap* GetEmbedMap() const { return m_pEmbedMap.Get(); }
   CIDSet GetCharset() const { return m_Charset; }
   void SetCharset(CIDSet set) { m_Charset = set; }
 
@@ -84,20 +78,20 @@
   }
 
  private:
-  CPDF_CMap();
+  explicit CPDF_CMap(ByteStringView bsPredefinedName);
+  explicit CPDF_CMap(pdfium::span<const uint8_t> spEmbeddedData);
   ~CPDF_CMap() override;
 
-  ByteString m_PredefinedCMap;
-  bool m_bLoaded;
-  bool m_bVertical;
-  CIDSet m_Charset;
-  CodingScheme m_CodingScheme;
-  int m_Coding;
+  bool m_bLoaded = false;
+  bool m_bVertical = false;
+  CIDSet m_Charset = CIDSET_UNKNOWN;
+  CodingScheme m_CodingScheme = TwoBytes;
+  int m_Coding = CIDCODING_UNKNOWN;
   std::vector<bool> m_MixedTwoByteLeadingBytes;
   std::vector<CodeRange> m_MixedFourByteLeadingRanges;
   std::vector<uint16_t> m_DirectCharcodeToCIDTable;
   std::vector<CIDRange> m_AdditionalCharcodeToCIDMappings;
-  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
index 53183af..726b648 100644
--- a/core/fpdfapi/font/cpdf_cmapmanager.cpp
+++ b/core/fpdfapi/font/cpdf_cmapmanager.cpp
@@ -10,49 +10,39 @@
 
 #include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
 #include "core/fpdfapi/font/cpdf_cmap.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/ptr_util.h"
 
-CPDF_CMapManager::CPDF_CMapManager() {}
+namespace {
 
-CPDF_CMapManager::~CPDF_CMapManager() {}
+RetainPtr<const CPDF_CMap> LoadPredefinedCMap(ByteStringView name) {
+  if (!name.IsEmpty() && name[0] == '/')
+    name = name.Last(name.GetLength() - 1);
+  return pdfium::MakeRetain<CPDF_CMap>(name);
+}
 
-RetainPtr<CPDF_CMap> CPDF_CMapManager::GetPredefinedCMap(const ByteString& name,
-                                                         bool bPromptCJK) {
+}  // 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<CPDF_CMap> pCMap = LoadPredefinedCMap(name, bPromptCJK);
+  RetainPtr<const CPDF_CMap> pCMap = LoadPredefinedCMap(name.AsStringView());
   if (!name.IsEmpty())
     m_CMaps[name] = pCMap;
 
   return pCMap;
 }
 
-RetainPtr<CPDF_CMap> CPDF_CMapManager::LoadPredefinedCMap(
-    const ByteString& name,
-    bool bPromptCJK) {
-  const char* pname = name.c_str();
-  if (*pname == '/')
-    pname++;
-
-  auto pCMap = pdfium::MakeRetain<CPDF_CMap>();
-  pCMap->LoadPredefined(this, pname, bPromptCJK);
-  return pCMap;
-}
-
-CPDF_CID2UnicodeMap* CPDF_CMapManager::GetCID2UnicodeMap(CIDSet charset,
-                                                         bool bPromptCJK) {
-  if (!m_CID2UnicodeMaps[charset])
-    m_CID2UnicodeMaps[charset] = LoadCID2UnicodeMap(charset, bPromptCJK);
-
+CPDF_CID2UnicodeMap* CPDF_CMapManager::GetCID2UnicodeMap(CIDSet charset) {
+  if (!m_CID2UnicodeMaps[charset]) {
+    m_CID2UnicodeMaps[charset] =
+        pdfium::MakeUnique<CPDF_CID2UnicodeMap>(charset);
+  }
   return m_CID2UnicodeMaps[charset].get();
 }
-
-std::unique_ptr<CPDF_CID2UnicodeMap> CPDF_CMapManager::LoadCID2UnicodeMap(
-    CIDSet charset,
-    bool bPromptCJK) {
-  auto pMap = pdfium::MakeUnique<CPDF_CID2UnicodeMap>();
-  pMap->Load(this, charset, bPromptCJK);
-  return pMap;
-}
diff --git a/core/fpdfapi/font/cpdf_cmapmanager.h b/core/fpdfapi/font/cpdf_cmapmanager.h
index 089eb3d..bc8d4e9 100644
--- a/core/fpdfapi/font/cpdf_cmapmanager.h
+++ b/core/fpdfapi/font/cpdf_cmapmanager.h
@@ -19,18 +19,12 @@
   CPDF_CMapManager();
   ~CPDF_CMapManager();
 
-  RetainPtr<CPDF_CMap> GetPredefinedCMap(const ByteString& name,
-                                         bool bPromptCJK);
-  CPDF_CID2UnicodeMap* GetCID2UnicodeMap(CIDSet charset, bool bPromptCJK);
+  RetainPtr<const CPDF_CMap> GetPredefinedCMap(const ByteString& name);
+  CPDF_CID2UnicodeMap* GetCID2UnicodeMap(CIDSet charset);
 
  private:
-  RetainPtr<CPDF_CMap> LoadPredefinedCMap(const ByteString& name,
-                                          bool bPromptCJK);
-  std::unique_ptr<CPDF_CID2UnicodeMap> LoadCID2UnicodeMap(CIDSet charset,
-                                                          bool bPromptCJK);
-
-  std::map<ByteString, RetainPtr<CPDF_CMap>> m_CMaps;
-  std::unique_ptr<CPDF_CID2UnicodeMap> m_CID2UnicodeMaps[6];
+  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 a88448c..d1fe51c 100644
--- a/core/fpdfapi/font/cpdf_cmapparser.cpp
+++ b/core/fpdfapi/font/cpdf_cmapparser.cpp
@@ -8,137 +8,143 @@
 
 #include <vector>
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #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"
 
 namespace {
 
-const char* const g_CharsetNames[CIDSET_NUM_SETS] = {nullptr,  "GB1",    "CNS1",
-                                                     "Japan1", "Korea1", "UCS"};
-
-CIDSet CIDSetFromSizeT(size_t index) {
-  if (index >= CIDSET_NUM_SETS) {
-    NOTREACHED();
-    return CIDSET_UNKNOWN;
-  }
-  return static_cast<CIDSet>(index);
-}
-
-ByteStringView CMap_GetString(const ByteStringView& word) {
+ByteStringView CMap_GetString(ByteStringView word) {
   if (word.GetLength() <= 2)
     return ByteStringView();
-  return word.Right(word.GetLength() - 2);
+  return word.Last(word.GetLength() - 2);
 }
 
 }  // namespace
 
-CPDF_CMapParser::CPDF_CMapParser(CPDF_CMap* pCMap)
-    : m_pCMap(pCMap), m_Status(0), m_CodeSeq(0) {}
+CPDF_CMapParser::CPDF_CMapParser(CPDF_CMap* pCMap) : m_pCMap(pCMap) {}
 
-CPDF_CMapParser::~CPDF_CMapParser() {}
+CPDF_CMapParser::~CPDF_CMapParser() {
+  m_pCMap->SetAdditionalMappings(std::move(m_AdditionalCharcodeToCIDMappings));
+  m_pCMap->SetMixedFourByteLeadingRanges(std::move(m_Ranges));
+}
 
-void CPDF_CMapParser::ParseWord(const ByteStringView& word) {
-  if (word.IsEmpty()) {
-    return;
-  }
+void CPDF_CMapParser::ParseWord(ByteStringView word) {
+  ASSERT(!word.IsEmpty());
+
   if (word == "begincidchar") {
-    m_Status = 1;
+    m_Status = kProcessingCidChar;
     m_CodeSeq = 0;
   } else if (word == "begincidrange") {
-    m_Status = 2;
+    m_Status = kProcessingCidRange;
     m_CodeSeq = 0;
   } else if (word == "endcidrange" || word == "endcidchar") {
-    m_Status = 0;
+    m_Status = kStart;
   } else if (word == "/WMode") {
-    m_Status = 6;
+    m_Status = kProcessingWMode;
   } else if (word == "/Registry") {
-    m_Status = 3;
+    m_Status = kProcessingRegistry;
   } else if (word == "/Ordering") {
-    m_Status = 4;
+    m_Status = kProcessingOrdering;
   } else if (word == "/Supplement") {
-    m_Status = 5;
+    m_Status = kProcessingSupplement;
   } else if (word == "begincodespacerange") {
-    m_Status = 7;
+    m_Status = kProcessingCodeSpaceRange;
     m_CodeSeq = 0;
   } else if (word == "usecmap") {
-  } else if (m_Status == 1 || m_Status == 2) {
-    m_CodePoints[m_CodeSeq] = GetCode(word);
-    m_CodeSeq++;
-    uint32_t StartCode, EndCode;
-    uint16_t StartCID;
-    if (m_Status == 1) {
-      if (m_CodeSeq < 2) {
-        return;
-      }
-      EndCode = StartCode = m_CodePoints[0];
-      StartCID = (uint16_t)m_CodePoints[1];
-    } else {
-      if (m_CodeSeq < 3) {
-        return;
-      }
-      StartCode = m_CodePoints[0];
-      EndCode = m_CodePoints[1];
-      StartCID = (uint16_t)m_CodePoints[2];
-    }
-    if (EndCode < 0x10000) {
-      for (uint32_t code = StartCode; code <= EndCode; code++) {
-        m_pCMap->SetDirectCharcodeToCIDTable(
-            code, static_cast<uint16_t>(StartCID + code - StartCode));
-      }
-    } else {
-      m_AdditionalCharcodeToCIDMappings.push_back(
-          {StartCode, EndCode, StartCID});
-    }
-    m_CodeSeq = 0;
-  } else if (m_Status == 3) {
-    m_Status = 0;
-  } else if (m_Status == 4) {
+  } else if (m_Status == kProcessingCidChar) {
+    HandleCid(word);
+  } else if (m_Status == kProcessingCidRange) {
+    HandleCid(word);
+  } else if (m_Status == kProcessingRegistry) {
+    m_Status = kStart;
+  } else if (m_Status == kProcessingOrdering) {
     m_pCMap->SetCharset(CharsetFromOrdering(CMap_GetString(word)));
-    m_Status = 0;
-  } else if (m_Status == 5) {
-    m_Status = 0;
-  } else if (m_Status == 6) {
+    m_Status = kStart;
+  } else if (m_Status == kProcessingSupplement) {
+    m_Status = kStart;
+  } else if (m_Status == kProcessingWMode) {
     m_pCMap->SetVertical(GetCode(word) != 0);
-    m_Status = 0;
-  } else if (m_Status == 7) {
-    if (word == "endcodespacerange") {
-      size_t nSegs = m_CodeRanges.size();
-      if (nSegs == 1) {
-        m_pCMap->SetCodingScheme((m_CodeRanges[0].m_CharSize == 2)
-                                     ? CPDF_CMap::TwoBytes
-                                     : CPDF_CMap::OneByte);
-      } else if (nSegs > 1) {
-        m_pCMap->SetCodingScheme(CPDF_CMap::MixedFourBytes);
-        m_pCMap->SetMixedFourByteLeadingRanges(m_CodeRanges);
-      }
-      m_Status = 0;
-    } else {
-      if (word.GetLength() == 0 || word[0] != '<') {
-        return;
-      }
-      if (m_CodeSeq % 2) {
-        CPDF_CMap::CodeRange range;
-        if (GetCodeRange(range, m_LastWord.AsStringView(), word))
-          m_CodeRanges.push_back(range);
-      }
-      m_CodeSeq++;
-    }
+    m_Status = kStart;
+  } else if (m_Status == kProcessingCodeSpaceRange) {
+    HandleCodeSpaceRange(word);
   }
   m_LastWord = word;
 }
 
-uint32_t CPDF_CMapParser::GetCode(const ByteStringView& word) const {
+void CPDF_CMapParser::HandleCid(ByteStringView word) {
+  ASSERT(m_Status == kProcessingCidChar || m_Status == kProcessingCidRange);
+  bool bChar = m_Status == kProcessingCidChar;
+
+  m_CodePoints[m_CodeSeq] = GetCode(word);
+  m_CodeSeq++;
+  int nRequiredCodePoints = bChar ? 2 : 3;
+  if (m_CodeSeq < nRequiredCodePoints)
+    return;
+
+  uint32_t StartCode = m_CodePoints[0];
+  uint32_t EndCode;
+  uint16_t StartCID;
+  if (bChar) {
+    EndCode = StartCode;
+    StartCID = static_cast<uint16_t>(m_CodePoints[1]);
+  } else {
+    EndCode = m_CodePoints[1];
+    StartCID = static_cast<uint16_t>(m_CodePoints[2]);
+  }
+  if (EndCode < 0x10000) {
+    for (uint32_t code = StartCode; code <= EndCode; code++) {
+      m_pCMap->SetDirectCharcodeToCIDTable(
+          code, static_cast<uint16_t>(StartCID + code - StartCode));
+    }
+  } else {
+    m_AdditionalCharcodeToCIDMappings.push_back({StartCode, EndCode, StartCID});
+  }
+  m_CodeSeq = 0;
+}
+
+void CPDF_CMapParser::HandleCodeSpaceRange(ByteStringView word) {
+  if (word != "endcodespacerange") {
+    if (word.IsEmpty() || word[0] != '<')
+      return;
+
+    if (m_CodeSeq % 2) {
+      Optional<CPDF_CMap::CodeRange> range =
+          GetCodeRange(m_LastWord.AsStringView(), word);
+      if (range.has_value())
+        m_PendingRanges.push_back(range.value());
+    }
+    m_CodeSeq++;
+    return;
+  }
+
+  size_t nSegs = m_Ranges.size() + m_PendingRanges.size();
+  if (nSegs == 1) {
+    const auto& first_range =
+        !m_Ranges.empty() ? m_Ranges[0] : m_PendingRanges[0];
+    m_pCMap->SetCodingScheme(first_range.m_CharSize == 2 ? CPDF_CMap::TwoBytes
+                                                         : CPDF_CMap::OneByte);
+  } else if (nSegs > 1) {
+    m_pCMap->SetCodingScheme(CPDF_CMap::MixedFourBytes);
+    m_Ranges.reserve(nSegs);
+    std::move(m_PendingRanges.begin(), m_PendingRanges.end(),
+              std::back_inserter(m_Ranges));
+    m_PendingRanges.clear();
+  }
+  m_Status = kStart;
+}
+
+// static
+uint32_t CPDF_CMapParser::GetCode(ByteStringView word) {
   if (word.IsEmpty())
     return 0;
 
-  pdfium::base::CheckedNumeric<uint32_t> num = 0;
+  FX_SAFE_UINT32 num = 0;
   if (word[0] == '<') {
     for (size_t i = 1; i < word.GetLength() && std::isxdigit(word[i]); ++i) {
       num = num * 16 + FXSYS_HexCharToInt(word[i]);
@@ -156,22 +162,24 @@
   return num.ValueOrDie();
 }
 
-bool CPDF_CMapParser::GetCodeRange(CPDF_CMap::CodeRange& range,
-                                   const ByteStringView& first,
-                                   const ByteStringView& second) const {
-  if (first.GetLength() == 0 || first[0] != '<')
-    return false;
+// static
+Optional<CPDF_CMap::CodeRange> CPDF_CMapParser::GetCodeRange(
+    ByteStringView first,
+    ByteStringView second) {
+  if (first.IsEmpty() || first[0] != '<')
+    return pdfium::nullopt;
 
   size_t i;
   for (i = 1; i < first.GetLength(); ++i) {
-    if (first[i] == '>') {
+    if (first[i] == '>')
       break;
-    }
   }
-  range.m_CharSize = (i - 1) / 2;
-  if (range.m_CharSize > 4)
-    return false;
+  size_t char_size = (i - 1) / 2;
+  if (char_size > 4)
+    return pdfium::nullopt;
 
+  CPDF_CMap::CodeRange range;
+  range.m_CharSize = char_size;
   for (i = 0; i < range.m_CharSize; ++i) {
     uint8_t digit1 = first[i * 2 + 1];
     uint8_t digit2 = first[i * 2 + 2];
@@ -181,19 +189,26 @@
 
   size_t size = second.GetLength();
   for (i = 0; i < range.m_CharSize; ++i) {
-    uint8_t digit1 = (i * 2 + 1 < size) ? second[i * 2 + 1] : '0';
-    uint8_t digit2 = (i * 2 + 2 < size) ? second[i * 2 + 2] : '0';
+    size_t i1 = i * 2 + 1;
+    size_t i2 = i1 + 1;
+    uint8_t digit1 = i1 < size ? second[i1] : '0';
+    uint8_t digit2 = i2 < size ? second[i2] : '0';
     range.m_Upper[i] =
         FXSYS_HexCharToInt(digit1) * 16 + FXSYS_HexCharToInt(digit2);
   }
-  return true;
+  return range;
 }
 
 // static
-CIDSet CPDF_CMapParser::CharsetFromOrdering(const ByteStringView& ordering) {
-  for (size_t charset = 1; charset < FX_ArraySize(g_CharsetNames); ++charset) {
-    if (ordering == g_CharsetNames[charset])
-      return CIDSetFromSizeT(charset);
+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,
+                "Too many CID sets");
+
+  for (size_t charset = 1; charset < FX_ArraySize(kCharsetNames); ++charset) {
+    if (ordering == kCharsetNames[charset])
+      return static_cast<CIDSet>(charset);
   }
   return CIDSET_UNKNOWN;
 }
diff --git a/core/fpdfapi/font/cpdf_cmapparser.h b/core/fpdfapi/font/cpdf_cmapparser.h
index fc46c58..b2454af 100644
--- a/core/fpdfapi/font/cpdf_cmapparser.h
+++ b/core/fpdfapi/font/cpdf_cmapparser.h
@@ -7,43 +7,53 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_CMAPPARSER_H_
 #define CORE_FPDFAPI_FONT_CPDF_CMAPPARSER_H_
 
-#include <map>
 #include <utility>
 #include <vector>
 
 #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"
 
 class CPDF_CMapParser {
  public:
-  explicit CPDF_CMapParser(CPDF_CMap* pMap);
+  explicit CPDF_CMapParser(CPDF_CMap* pCMap);
   ~CPDF_CMapParser();
 
-  void ParseWord(const ByteStringView& str);
-  bool HasAdditionalMappings() const {
-    return !m_AdditionalCharcodeToCIDMappings.empty();
-  }
-  std::vector<CPDF_CMap::CIDRange> TakeAdditionalMappings() {
-    return std::move(m_AdditionalCharcodeToCIDMappings);
-  }
+  void ParseWord(ByteStringView word);
 
-  uint32_t GetCode(const ByteStringView& word) const;
-  bool GetCodeRange(CPDF_CMap::CodeRange& range,
-                    const ByteStringView& first,
-                    const ByteStringView& second) const;
-
-  static CIDSet CharsetFromOrdering(const ByteStringView& ordering);
+  static CIDSet CharsetFromOrdering(ByteStringView ordering);
 
  private:
+  friend class cpdf_cmapparser_GetCode_Test;
+  friend class cpdf_cmapparser_GetCodeRange_Test;
 
+  enum Status {
+    kStart,
+    kProcessingCidChar,
+    kProcessingCidRange,
+    kProcessingRegistry,
+    kProcessingOrdering,
+    kProcessingSupplement,
+    kProcessingWMode,
+    kProcessingCodeSpaceRange,
+  };
+
+  void HandleCid(ByteStringView word);
+  void HandleCodeSpaceRange(ByteStringView word);
+
+  static uint32_t GetCode(ByteStringView word);
+  static Optional<CPDF_CMap::CodeRange> GetCodeRange(ByteStringView first,
+                                                     ByteStringView second);
+
+  Status m_Status = kStart;
+  int m_CodeSeq = 0;
   UnownedPtr<CPDF_CMap> const m_pCMap;
-  int m_Status;
-  int m_CodeSeq;
-  uint32_t m_CodePoints[4];
-  std::vector<CPDF_CMap::CodeRange> m_CodeRanges;
+  std::vector<CPDF_CMap::CodeRange> m_Ranges;
+  std::vector<CPDF_CMap::CodeRange> m_PendingRanges;
   std::vector<CPDF_CMap::CIDRange> m_AdditionalCharcodeToCIDMappings;
   ByteString m_LastWord;
+  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 1a36eb7..da654f3 100644
--- a/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp
+++ b/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp
@@ -8,7 +8,7 @@
 
 namespace {
 
-bool uint_ranges_equal(uint8_t* a, uint8_t* b, size_t count) {
+bool uint_ranges_equal(const uint8_t* a, const uint8_t* b, size_t count) {
   for (size_t i = 0; i < count; ++i) {
     if (a[i] != b[i])
       return false;
@@ -19,55 +19,57 @@
 }  // namespace
 
 TEST(cpdf_cmapparser, GetCode) {
-  CPDF_CMapParser parser(nullptr);
+  EXPECT_EQ(0u, CPDF_CMapParser::GetCode(""));
+  EXPECT_EQ(0u, CPDF_CMapParser::GetCode("<"));
+  EXPECT_EQ(194u, CPDF_CMapParser::GetCode("<c2"));
+  EXPECT_EQ(162u, CPDF_CMapParser::GetCode("<A2"));
+  EXPECT_EQ(2802u, CPDF_CMapParser::GetCode("<Af2"));
+  EXPECT_EQ(162u, CPDF_CMapParser::GetCode("<A2z"));
 
-  EXPECT_EQ(0u, parser.GetCode(""));
-  EXPECT_EQ(0u, parser.GetCode("<"));
-  EXPECT_EQ(194u, parser.GetCode("<c2"));
-  EXPECT_EQ(162u, parser.GetCode("<A2"));
-  EXPECT_EQ(2802u, parser.GetCode("<Af2"));
-  EXPECT_EQ(162u, parser.GetCode("<A2z"));
+  EXPECT_EQ(12u, CPDF_CMapParser::GetCode("12"));
+  EXPECT_EQ(12u, CPDF_CMapParser::GetCode("12d"));
+  EXPECT_EQ(128u, CPDF_CMapParser::GetCode("128"));
 
-  EXPECT_EQ(12u, parser.GetCode("12"));
-  EXPECT_EQ(12u, parser.GetCode("12d"));
-  EXPECT_EQ(128u, parser.GetCode("128"));
-
-  EXPECT_EQ(4294967295u, parser.GetCode("<FFFFFFFF"));
+  EXPECT_EQ(4294967295u, CPDF_CMapParser::GetCode("<FFFFFFFF"));
 
   // Overflow a uint32_t.
-  EXPECT_EQ(0u, parser.GetCode("<100000000"));
+  EXPECT_EQ(0u, CPDF_CMapParser::GetCode("<100000000"));
 }
 
 TEST(cpdf_cmapparser, GetCodeRange) {
-  CPDF_CMapParser parser(nullptr);
-  CPDF_CMap::CodeRange range;
+  Optional<CPDF_CMap::CodeRange> range;
 
   // Must start with a <
-  EXPECT_FALSE(parser.GetCodeRange(range, "", ""));
-  EXPECT_FALSE(parser.GetCodeRange(range, "A", ""));
+  range = CPDF_CMapParser::GetCodeRange("", "");
+  EXPECT_FALSE(range.has_value());
+  range = CPDF_CMapParser::GetCodeRange("A", "");
+  EXPECT_FALSE(range.has_value());
 
   // m_CharSize must be <= 4
-  EXPECT_FALSE(parser.GetCodeRange(range, "<aaaaaaaaaa>", ""));
-  EXPECT_EQ(5u, range.m_CharSize);
+  range = CPDF_CMapParser::GetCodeRange("<aaaaaaaaaa>", "");
+  EXPECT_FALSE(range.has_value());
 
-  EXPECT_TRUE(parser.GetCodeRange(range, "<12345678>", "<87654321>"));
-  EXPECT_EQ(4u, range.m_CharSize);
+  range = CPDF_CMapParser::GetCodeRange("<12345678>", "<87654321>");
+  ASSERT_TRUE(range.has_value());
+  ASSERT_EQ(4u, range.value().m_CharSize);
   {
-    uint8_t lower[4] = {18, 52, 86, 120};
-    uint8_t upper[4] = {135, 101, 67, 33};
-    EXPECT_TRUE(uint_ranges_equal(lower, range.m_Lower, range.m_CharSize));
-    EXPECT_TRUE(uint_ranges_equal(upper, range.m_Upper, range.m_CharSize));
+    constexpr uint8_t kLower[4] = {18, 52, 86, 120};
+    constexpr uint8_t kUpper[4] = {135, 101, 67, 33};
+    EXPECT_TRUE(uint_ranges_equal(kLower, range.value().m_Lower, 4));
+    EXPECT_TRUE(uint_ranges_equal(kUpper, range.value().m_Upper, 4));
   }
 
   // Hex characters
-  EXPECT_TRUE(parser.GetCodeRange(range, "<a1>", "<F3>"));
-  EXPECT_EQ(1u, range.m_CharSize);
-  EXPECT_EQ(161, range.m_Lower[0]);
-  EXPECT_EQ(243, range.m_Upper[0]);
+  range = CPDF_CMapParser::GetCodeRange("<a1>", "<F3>");
+  ASSERT_TRUE(range.has_value());
+  ASSERT_EQ(1u, range.value().m_CharSize);
+  EXPECT_EQ(161, range.value().m_Lower[0]);
+  EXPECT_EQ(243, range.value().m_Upper[0]);
 
   // The second string should return 0's if it is shorter
-  EXPECT_TRUE(parser.GetCodeRange(range, "<a1>", ""));
-  EXPECT_EQ(1u, range.m_CharSize);
-  EXPECT_EQ(161, range.m_Lower[0]);
-  EXPECT_EQ(0, range.m_Upper[0]);
+  range = CPDF_CMapParser::GetCodeRange("<a1>", "");
+  ASSERT_TRUE(range.has_value());
+  ASSERT_EQ(1u, range.value().m_CharSize);
+  EXPECT_EQ(161, range.value().m_Lower[0]);
+  EXPECT_EQ(0, range.value().m_Upper[0]);
 }
diff --git a/core/fpdfapi/font/cpdf_font.cpp b/core/fpdfapi/font/cpdf_font.cpp
index 8d35739..326bc6e 100644
--- a/core/fpdfapi/font/cpdf_font.cpp
+++ b/core/fpdfapi/font/cpdf_font.cpp
@@ -11,19 +11,23 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "build/build_config.h"
+#include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_fontencoding.h"
+#include "core/fpdfapi/font/cpdf_fontglobals.h"
+#include "core/fpdfapi/font/cpdf_tounicodemap.h"
 #include "core/fpdfapi/font/cpdf_truetypefont.h"
 #include "core/fpdfapi/font/cpdf_type1font.h"
 #include "core/fpdfapi/font/cpdf_type3font.h"
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxge/cfx_fontmapper.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"
@@ -31,34 +35,20 @@
 
 namespace {
 
-const uint8_t kChineseFontNames[][5] = {{0xCB, 0xCE, 0xCC, 0xE5, 0x00},
-                                        {0xBF, 0xAC, 0xCC, 0xE5, 0x00},
-                                        {0xBA, 0xDA, 0xCC, 0xE5, 0x00},
-                                        {0xB7, 0xC2, 0xCB, 0xCE, 0x00},
-                                        {0xD0, 0xC2, 0xCB, 0xCE, 0x00}};
-
-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;
-}
+constexpr size_t kChineseFontNameSize = 4;
+const uint8_t kChineseFontNames[][kChineseFontNameSize] = {
+    {0xCB, 0xCE, 0xCC, 0xE5},
+    {0xBF, 0xAC, 0xCC, 0xE5},
+    {0xBA, 0xDA, 0xCC, 0xE5},
+    {0xB7, 0xC2, 0xCB, 0xCE},
+    {0xD0, 0xC2, 0xCB, 0xCE}};
 
 }  // namespace
 
-CPDF_Font::CPDF_Font()
-    : m_pFontFile(nullptr),
-      m_pFontDict(nullptr),
-      m_bToUnicodeLoaded(false),
-      m_Flags(0),
-      m_StemV(0),
-      m_Ascent(0),
-      m_Descent(0),
-      m_ItalicAngle(0) {}
+CPDF_Font::CPDF_Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict)
+    : m_pDocument(pDocument),
+      m_pFontDict(pFontDict),
+      m_BaseFontName(pFontDict->GetStringFor("BaseFont")) {}
 
 CPDF_Font::~CPDF_Font() {
   if (m_pFontFile) {
@@ -122,13 +112,17 @@
   return false;
 }
 
-int CPDF_Font::CountChar(const char* pString, int size) const {
-  return size;
+size_t CPDF_Font::CountChar(ByteStringView pString) const {
+  return pString.GetLength();
 }
 
+#if defined(OS_MACOSX)
 int CPDF_Font::GlyphFromCharCodeExt(uint32_t charcode) {
   return GlyphFromCharCode(charcode, nullptr);
 }
+#endif
+
+void CPDF_Font::WillBeDestroyed() {}
 
 bool CPDF_Font::IsVertWriting() const {
   const CPDF_CIDFont* pCIDFont = AsCIDFont();
@@ -164,7 +158,7 @@
   return true;
 }
 
-void CPDF_Font::LoadFontDescriptor(CPDF_Dictionary* pFontDesc) {
+void CPDF_Font::LoadFontDescriptor(const CPDF_Dictionary* pFontDesc) {
   m_Flags = pFontDesc->GetIntegerFor("Flags", FXFONT_NONSYMBOLIC);
   int ItalicAngle = 0;
   bool bExistItalicAngle = false;
@@ -200,7 +194,7 @@
   }
   if (m_Descent > 10)
     m_Descent = -m_Descent;
-  CPDF_Array* pBBox = pFontDesc->GetArrayFor("FontBBox");
+  const CPDF_Array* pBBox = pFontDesc->GetArrayFor("FontBBox");
   if (pBBox) {
     m_FontBBox.left = pBBox->GetIntegerAt(0);
     m_FontBBox.bottom = pBBox->GetIntegerAt(1);
@@ -208,7 +202,7 @@
     m_FontBBox.top = pBBox->GetIntegerAt(3);
   }
 
-  CPDF_Stream* pFontFile = pFontDesc->GetStreamFor("FontFile");
+  const CPDF_Stream* pFontFile = pFontDesc->GetStreamFor("FontFile");
   if (!pFontFile)
     pFontFile = pFontDesc->GetStreamFor("FontFile2");
   if (!pFontFile)
@@ -216,15 +210,13 @@
   if (!pFontFile)
     return;
 
-  m_pFontFile = m_pDocument->LoadFontFile(pFontFile);
+  auto* pData = m_pDocument->GetPageData();
+  m_pFontFile = pData->GetFontFileStreamAcc(pFontFile);
   if (!m_pFontFile)
     return;
 
-  const uint8_t* pFontData = m_pFontFile->GetData();
-  uint32_t dwFontSize = m_pFontFile->GetSize();
-  if (!m_Font.LoadEmbedded(pFontData, dwFontSize)) {
-    m_pDocument->GetPageData()->MaybePurgeFontFileStreamAcc(
-        m_pFontFile->GetStream()->AsStream());
+  if (!m_Font.LoadEmbedded(m_pFontFile->GetSpan(), IsVertWriting())) {
+    pData->MaybePurgeFontFileStreamAcc(m_pFontFile->GetStream()->AsStream());
     m_pFontFile = nullptr;
   }
 }
@@ -232,7 +224,7 @@
 void CPDF_Font::CheckFontMetrics() {
   if (m_FontBBox.top == 0 && m_FontBBox.bottom == 0 && m_FontBBox.left == 0 &&
       m_FontBBox.right == 0) {
-    FXFT_Face face = m_Font.GetFace();
+    FXFT_FaceRec* face = m_Font.GetFaceRec();
     if (face) {
       m_FontBBox.left = TT2PDF(FXFT_Get_Face_xMin(face), face);
       m_FontBBox.bottom = TT2PDF(FXFT_Get_Face_yMin(face), face);
@@ -277,150 +269,83 @@
 
 void CPDF_Font::LoadUnicodeMap() const {
   m_bToUnicodeLoaded = true;
-  CPDF_Stream* pStream = m_pFontDict->GetStreamFor("ToUnicode");
-  if (!pStream) {
+  const CPDF_Stream* pStream = m_pFontDict->GetStreamFor("ToUnicode");
+  if (!pStream)
     return;
-  }
-  m_pToUnicodeMap = pdfium::MakeUnique<CPDF_ToUnicodeMap>();
-  m_pToUnicodeMap->Load(pStream);
+
+  m_pToUnicodeMap = pdfium::MakeUnique<CPDF_ToUnicodeMap>(pStream);
 }
 
-int CPDF_Font::GetStringWidth(const char* pString, int size) {
-  int offset = 0;
-  int width = 0;
-  while (offset < size) {
-    uint32_t charcode = GetNextChar(pString, size, offset);
-    width += GetCharWidthF(charcode);
-  }
+uint32_t CPDF_Font::GetStringWidth(ByteStringView pString) {
+  size_t offset = 0;
+  uint32_t width = 0;
+  while (offset < pString.GetLength())
+    width += GetCharWidthF(GetNextChar(pString, &offset));
   return width;
 }
 
 // static
-CPDF_Font* CPDF_Font::GetStockFont(CPDF_Document* pDoc,
-                                   const ByteStringView& name) {
+RetainPtr<CPDF_Font> CPDF_Font::GetStockFont(CPDF_Document* pDoc,
+                                             ByteStringView name) {
   ByteString fontname(name);
-  int font_id = PDF_GetStandardFontName(&fontname);
-  if (font_id < 0)
+  Optional<CFX_FontMapper::StandardFont> font_id =
+      CFX_FontMapper::GetStandardFontName(&fontname);
+  if (!font_id.has_value())
     return nullptr;
 
-  CPDF_FontGlobals* pFontGlobals =
-      CPDF_ModuleMgr::Get()->GetPageModule()->GetFontGlobals();
-  CPDF_Font* pFont = pFontGlobals->Find(pDoc, font_id);
+  auto* pFontGlobals = CPDF_FontGlobals::GetInstance();
+  RetainPtr<CPDF_Font> pFont = pFontGlobals->Find(pDoc, font_id.value());
   if (pFont)
     return pFont;
 
-  CPDF_Dictionary* pDict = new CPDF_Dictionary(pDoc->GetByteStringPool());
+  auto pDict = pDoc->New<CPDF_Dictionary>();
   pDict->SetNewFor<CPDF_Name>("Type", "Font");
   pDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
   pDict->SetNewFor<CPDF_Name>("BaseFont", fontname);
   pDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
-  return pFontGlobals->Set(pDoc, font_id, CPDF_Font::Create(nullptr, pDict));
+  pFont = CPDF_Font::Create(nullptr, pDict.Get(), nullptr);
+  pFontGlobals->Set(pDoc, font_id.value(), pFont);
+  return pFont;
 }
 
-std::unique_ptr<CPDF_Font> CPDF_Font::Create(CPDF_Document* pDoc,
-                                             CPDF_Dictionary* pFontDict) {
+// static
+RetainPtr<CPDF_Font> CPDF_Font::Create(CPDF_Document* pDoc,
+                                       CPDF_Dictionary* pFontDict,
+                                       FormFactoryIface* pFactory) {
   ByteString type = pFontDict->GetStringFor("Subtype");
-  std::unique_ptr<CPDF_Font> pFont;
+  RetainPtr<CPDF_Font> pFont;
   if (type == "TrueType") {
-    ByteString tag = pFontDict->GetStringFor("BaseFont").Left(4);
+    ByteString tag = pFontDict->GetStringFor("BaseFont").First(4);
     for (size_t i = 0; i < FX_ArraySize(kChineseFontNames); ++i) {
-      if (tag == ByteString(kChineseFontNames[i], 4)) {
-        CPDF_Dictionary* pFontDesc = pFontDict->GetDictFor("FontDescriptor");
+      if (tag == ByteString(kChineseFontNames[i], kChineseFontNameSize)) {
+        const CPDF_Dictionary* pFontDesc =
+            pFontDict->GetDictFor("FontDescriptor");
         if (!pFontDesc || !pFontDesc->KeyExist("FontFile2"))
-          pFont = pdfium::MakeUnique<CPDF_CIDFont>();
+          pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, pFontDict);
         break;
       }
     }
     if (!pFont)
-      pFont = pdfium::MakeUnique<CPDF_TrueTypeFont>();
+      pFont = pdfium::MakeRetain<CPDF_TrueTypeFont>(pDoc, pFontDict);
   } else if (type == "Type3") {
-    pFont = pdfium::MakeUnique<CPDF_Type3Font>();
+    pFont = pdfium::MakeRetain<CPDF_Type3Font>(pDoc, pFontDict, pFactory);
   } else if (type == "Type0") {
-    pFont = pdfium::MakeUnique<CPDF_CIDFont>();
+    pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, pFontDict);
   } else {
-    pFont = pdfium::MakeUnique<CPDF_Type1Font>();
+    pFont = pdfium::MakeRetain<CPDF_Type1Font>(pDoc, pFontDict);
   }
-  pFont->m_pFontDict = pFontDict;
-  pFont->m_pDocument = pDoc;
-  pFont->m_BaseFont = pFontDict->GetStringFor("BaseFont");
-  return pFont->Load() ? std::move(pFont) : nullptr;
+  if (!pFont->Load())
+    return nullptr;
+
+  return pFont;
 }
 
-uint32_t CPDF_Font::GetNextChar(const char* pString,
-                                int nStrLen,
-                                int& offset) const {
-  if (offset < 0 || nStrLen < 1) {
+uint32_t CPDF_Font::GetNextChar(ByteStringView pString, size_t* pOffset) const {
+  if (pString.IsEmpty())
     return 0;
-  }
-  uint8_t ch = offset < nStrLen ? pString[offset++] : pString[nStrLen - 1];
-  return static_cast<uint32_t>(ch);
-}
 
-void CPDF_Font::LoadPDFEncoding(CPDF_Object* pEncoding,
-                                int& iBaseEncoding,
-                                std::vector<ByteString>* pCharNames,
-                                bool bEmbedded,
-                                bool bTrueType) {
-  if (!pEncoding) {
-    if (m_BaseFont == "Symbol") {
-      iBaseEncoding = bTrueType ? PDFFONT_ENCODING_MS_SYMBOL
-                                : PDFFONT_ENCODING_ADOBE_SYMBOL;
-    } else if (!bEmbedded && iBaseEncoding == PDFFONT_ENCODING_BUILTIN) {
-      iBaseEncoding = PDFFONT_ENCODING_WINANSI;
-    }
-    return;
-  }
-  if (pEncoding->IsName()) {
-    if (iBaseEncoding == PDFFONT_ENCODING_ADOBE_SYMBOL ||
-        iBaseEncoding == PDFFONT_ENCODING_ZAPFDINGBATS) {
-      return;
-    }
-    if (FontStyleIsSymbolic(m_Flags) && m_BaseFont == "Symbol") {
-      if (!bTrueType)
-        iBaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL;
-      return;
-    }
-    ByteString bsEncoding = pEncoding->GetString();
-    if (bsEncoding.Compare("MacExpertEncoding") == 0) {
-      bsEncoding = "WinAnsiEncoding";
-    }
-    GetPredefinedEncoding(bsEncoding, &iBaseEncoding);
-    return;
-  }
-
-  CPDF_Dictionary* pDict = pEncoding->AsDictionary();
-  if (!pDict)
-    return;
-
-  if (iBaseEncoding != PDFFONT_ENCODING_ADOBE_SYMBOL &&
-      iBaseEncoding != PDFFONT_ENCODING_ZAPFDINGBATS) {
-    ByteString bsEncoding = pDict->GetStringFor("BaseEncoding");
-    if (bTrueType && bsEncoding.Compare("MacExpertEncoding") == 0)
-      bsEncoding = "WinAnsiEncoding";
-    GetPredefinedEncoding(bsEncoding, &iBaseEncoding);
-  }
-  if ((!bEmbedded || bTrueType) && iBaseEncoding == PDFFONT_ENCODING_BUILTIN)
-    iBaseEncoding = PDFFONT_ENCODING_STANDARD;
-
-  CPDF_Array* pDiffs = pDict->GetArrayFor("Differences");
-  if (!pDiffs)
-    return;
-
-  pCharNames->resize(256);
-  uint32_t cur_code = 0;
-  for (uint32_t i = 0; i < pDiffs->GetCount(); i++) {
-    CPDF_Object* pElement = pDiffs->GetDirectObjectAt(i);
-    if (!pElement)
-      continue;
-
-    if (CPDF_Name* pName = pElement->AsName()) {
-      if (cur_code < 256)
-        (*pCharNames)[cur_code] = pName->GetString();
-      cur_code++;
-    } else {
-      cur_code = pElement->GetInteger();
-    }
-  }
+  size_t& offset = *pOffset;
+  return offset < pString.GetLength() ? pString[offset++] : pString.Back();
 }
 
 bool CPDF_Font::IsStandardFont() const {
@@ -428,19 +353,16 @@
     return false;
   if (m_pFontFile)
     return false;
-  if (AsType1Font()->GetBase14Font() < 0)
-    return false;
-  return true;
+  return AsType1Font()->IsBase14Font();
 }
 
+// static
 const char* CPDF_Font::GetAdobeCharName(
     int iBaseEncoding,
     const std::vector<ByteString>& charnames,
-    int charcode) {
-  if (charcode < 0 || charcode >= 256) {
-    NOTREACHED();
+    uint32_t charcode) {
+  if (charcode >= 256)
     return nullptr;
-  }
 
   if (!charnames.empty() && !charnames[charcode].IsEmpty())
     return charnames[charcode].c_str();
@@ -448,15 +370,21 @@
   const char* name = nullptr;
   if (iBaseEncoding)
     name = PDF_CharNameFromPredefinedCharSet(iBaseEncoding, charcode);
-  return name && name[0] ? name : nullptr;
+  if (!name)
+    return nullptr;
+
+  ASSERT(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;
+    safeWeight *= 5;
     m_FontFallbacks[0]->LoadSubst("Arial", IsTrueTypeFont(), m_Flags,
-                                  m_StemV * 5, m_ItalicAngle, 0,
-                                  IsVertWriting());
+                                  safeWeight.ValueOrDefault(FXFONT_FW_NORMAL),
+                                  m_ItalicAngle, 0, IsVertWriting());
   }
   return 0;
 }
@@ -468,7 +396,7 @@
   WideString str = UnicodeFromCharCode(charcode);
   uint32_t unicode = !str.IsEmpty() ? str[0] : charcode;
   int glyph =
-      FXFT_Get_Char_Index(m_FontFallbacks[fallbackFont]->GetFace(), unicode);
+      FT_Get_Char_Index(m_FontFallbacks[fallbackFont]->GetFaceRec(), unicode);
   if (glyph == 0)
     return -1;
 
@@ -482,7 +410,7 @@
 }
 
 // static
-int CPDF_Font::TT2PDF(int m, FXFT_Face face) {
+int CPDF_Font::TT2PDF(int m, FXFT_FaceRec* face) {
   int upm = FXFT_Get_Face_UnitsPerEM(face);
   if (upm == 0)
     return m;
@@ -494,16 +422,25 @@
 }
 
 // static
-bool CPDF_Font::FT_UseTTCharmap(FXFT_Face face,
+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) {
-      FXFT_Set_Charmap(face, pCharMap[i]);
+      FT_Set_Charmap(face, pCharMap[i]);
       return true;
     }
   }
   return false;
 }
+
+int CPDF_Font::GetFontWeight() const {
+  pdfium::base::CheckedNumeric<int> safeStemV(m_StemV);
+  if (m_StemV < 140)
+    safeStemV *= 5;
+  else
+    safeStemV = safeStemV * 4 + 140;
+  return safeStemV.ValueOrDefault(FXFONT_FW_NORMAL);
+}
diff --git a/core/fpdfapi/font/cpdf_font.h b/core/fpdfapi/font/cpdf_font.h
index 0468bcb..db776fd 100644
--- a/core/fpdfapi/font/cpdf_font.h
+++ b/core/fpdfapi/font/cpdf_font.h
@@ -8,35 +8,65 @@
 #define CORE_FPDFAPI_FONT_CPDF_FONT_H_
 
 #include <memory>
+#include <utility>
 #include <vector>
 
-#include "core/fpdfapi/font/cpdf_tounicodemap.h"
+#include "build/build_config.h"
+#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/fx_font.h"
 
+class CFX_DIBitmap;
 class CFX_SubstFont;
 class CPDF_CIDFont;
-class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_Object;
 class CPDF_TrueTypeFont;
 class CPDF_Type1Font;
+class CPDF_Type3Char;
 class CPDF_Type3Font;
 class CPDF_ToUnicodeMap;
 
-class CPDF_Font {
+class CPDF_Font : public Retainable, public Observable {
  public:
-  static std::unique_ptr<CPDF_Font> Create(CPDF_Document* pDoc,
-                                           CPDF_Dictionary* pFontDict);
-  static CPDF_Font* GetStockFont(CPDF_Document* pDoc,
-                                 const ByteStringView& fontname);
+  // Callback mechanism for Type3 fonts to get pixels from forms.
+  class FormIface {
+   public:
+    virtual ~FormIface() {}
+
+    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>>
+    GetBitmapAndMatrixFromSoleImageOfForm() const = 0;
+  };
+
+  // Callback mechanism for Type3 fonts to get new forms from upper layers.
+  class FormFactoryIface {
+   public:
+    virtual ~FormFactoryIface() {}
+
+    virtual std::unique_ptr<FormIface> CreateForm(
+        CPDF_Document* pDocument,
+        CPDF_Dictionary* pPageResources,
+        CPDF_Stream* pFormStream) = 0;
+  };
+
   static const uint32_t kInvalidCharCode = static_cast<uint32_t>(-1);
 
-  virtual ~CPDF_Font();
+  // |pFactory| only required for Type3 fonts.
+  static RetainPtr<CPDF_Font> Create(CPDF_Document* pDoc,
+                                     CPDF_Dictionary* pFontDict,
+                                     FormFactoryIface* pFactory);
+  static RetainPtr<CPDF_Font> GetStockFont(CPDF_Document* pDoc,
+                                           ByteStringView fontname);
+
+  ~CPDF_Font() override;
 
   virtual bool IsType1Font() const;
   virtual bool IsTrueTypeFont() const;
@@ -51,77 +81,81 @@
   virtual const CPDF_CIDFont* AsCIDFont() const;
   virtual CPDF_CIDFont* AsCIDFont();
 
+  virtual void WillBeDestroyed();
   virtual bool IsVertWriting() const;
   virtual bool IsUnicodeCompatible() const;
-  virtual uint32_t GetNextChar(const char* pString,
-                               int nStrLen,
-                               int& offset) const;
-  virtual int CountChar(const char* pString, int size) const;
+  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)
   virtual int GlyphFromCharCodeExt(uint32_t charcode);
+#endif
   virtual WideString UnicodeFromCharCode(uint32_t charcode) const;
   virtual uint32_t CharCodeFromUnicode(wchar_t Unicode) const;
   virtual bool HasFontWidths() const;
 
-  const ByteString& GetBaseFont() const { return m_BaseFont; }
+  ByteString GetBaseFontName() const { return m_BaseFontName; }
   CFX_SubstFont* GetSubstFont() const { return m_Font.GetSubstFont(); }
   bool IsEmbedded() const { return IsType3Font() || m_pFontFile != nullptr; }
-  CPDF_Dictionary* GetFontDict() const { return m_pFontDict; }
+  CPDF_Dictionary* GetFontDict() const { return m_pFontDict.Get(); }
+  void ClearFontDict() { m_pFontDict = nullptr; }
   bool IsStandardFont() const;
-  FXFT_Face GetFace() const { return m_Font.GetFace(); }
+  bool HasFace() const { return !!m_Font.GetFaceRec(); }
   void AppendChar(ByteString* str, uint32_t charcode) const;
 
-  void GetFontBBox(FX_RECT& rect) const { rect = m_FontBBox; }
+  const FX_RECT& GetFontBBox() const { return m_FontBBox; }
   int GetTypeAscent() const { return m_Ascent; }
   int GetTypeDescent() const { return m_Descent; }
-  int GetStringWidth(const char* pString, int size);
+  uint32_t GetStringWidth(ByteStringView pString);
   uint32_t FallbackFontFromCharcode(uint32_t charcode);
   int FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode);
+  int GetFontFlags() const { return m_Flags; }
+  int GetFontWeight() const;
 
-  virtual int GetCharWidthF(uint32_t charcode) = 0;
+  virtual uint32_t 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(); }
+
   CFX_Font* GetFont() { return &m_Font; }
   const CFX_Font* GetFont() const { return &m_Font; }
+
   CFX_Font* GetFontFallback(int position);
 
  protected:
-  CPDF_Font();
+  CPDF_Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
 
-  static int TT2PDF(int m, FXFT_Face face);
-  static bool FT_UseTTCharmap(FXFT_Face face, int platform_id, int encoding_id);
+  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,
+                                      const std::vector<ByteString>& charnames,
+                                      uint32_t charcode);
 
   virtual bool Load() = 0;
 
   void LoadUnicodeMap() const;  // logically const only.
-  void LoadPDFEncoding(CPDF_Object* pEncoding,
-                       int& iBaseEncoding,
-                       std::vector<ByteString>* pCharNames,
-                       bool bEmbedded,
-                       bool bTrueType);
-  void LoadFontDescriptor(CPDF_Dictionary* pDict);
+  void LoadFontDescriptor(const CPDF_Dictionary* pFontDesc);
   void CheckFontMetrics();
 
-  const char* GetAdobeCharName(int iBaseEncoding,
-                               const std::vector<ByteString>& charnames,
-                               int charcode);
-
-  UnownedPtr<CPDF_Document> m_pDocument;
+  UnownedPtr<CPDF_Document> const m_pDocument;
   CFX_Font m_Font;
   std::vector<std::unique_ptr<CFX_Font>> m_FontFallbacks;
-  ByteString m_BaseFont;
   RetainPtr<CPDF_StreamAcc> m_pFontFile;
-  CPDF_Dictionary* m_pFontDict;
+  RetainPtr<CPDF_Dictionary> m_pFontDict;
+  ByteString m_BaseFontName;
   mutable std::unique_ptr<CPDF_ToUnicodeMap> m_pToUnicodeMap;
-  mutable bool m_bToUnicodeLoaded;
-  int m_Flags;
+  mutable bool m_bToUnicodeLoaded = false;
+  int m_Flags = 0;
+  int m_StemV = 0;
+  int m_Ascent = 0;
+  int m_Descent = 0;
+  int m_ItalicAngle = 0;
   FX_RECT m_FontBBox;
-  int m_StemV;
-  int m_Ascent;
-  int m_Descent;
-  int m_ItalicAngle;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_FONT_H_
diff --git a/core/fpdfapi/font/cpdf_fontencoding.cpp b/core/fpdfapi/font/cpdf_fontencoding.cpp
index 921d584..a4e687e 100644
--- a/core/fpdfapi/font/cpdf_fontencoding.cpp
+++ b/core/fpdfapi/font/cpdf_fontencoding.cpp
@@ -13,42 +13,44 @@
 #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/fx_font.h"
 #include "core/fxge/fx_freetype.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
-const uint16_t MSSymbolEncoding[256] = {
+const uint16_t MSSymbolEncoding[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,
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 32,     33,     8704,   35,
-    8707,   37,     38,     8715,   40,     41,     8727,   43,     44,
-    8722,   46,     47,     48,     49,     50,     51,     52,     53,
-    54,     55,     56,     57,     58,     59,     60,     61,     62,
-    63,     8773,   913,    914,    935,    916,    917,    934,    915,
-    919,    921,    977,    922,    923,    924,    925,    927,    928,
-    920,    929,    931,    932,    933,    962,    937,    926,    936,
-    918,    91,     8756,   93,     8869,   95,     8254,   945,    946,
-    967,    948,    949,    966,    947,    951,    953,    981,    954,
-    955,    956,    957,    959,    960,    952,    961,    963,    964,
-    965,    982,    969,    958,    968,    950,    123,    124,    125,
-    8764,   0,      0,      0,      0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0021, 0x2200, 0x0023,
+    0x2203, 0x0025, 0x0026, 0x220b, 0x0028, 0x0029, 0x2217, 0x002b, 0x002c,
+    0x2212, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+    0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e,
+    0x003f, 0x2245, 0x0391, 0x0392, 0x03a7, 0x0394, 0x0395, 0x03a6, 0x0393,
+    0x0397, 0x0399, 0x03d1, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f, 0x03a0,
+    0x0398, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03c2, 0x03a9, 0x039e, 0x03a8,
+    0x0396, 0x005b, 0x2234, 0x005d, 0x22a5, 0x005f, 0x203e, 0x03b1, 0x03b2,
+    0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3, 0x03b7, 0x03b9, 0x03d5, 0x03ba,
+    0x03bb, 0x03bc, 0x03bd, 0x03bf, 0x03c0, 0x03b8, 0x03c1, 0x03c3, 0x03c4,
+    0x03c5, 0x03d6, 0x03c9, 0x03be, 0x03c8, 0x03b6, 0x007b, 0x007c, 0x007d,
+    0x223c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 978,
-    8242,   8804,   8725,   8734,   402,    9827,   9830,   9828,   9824,
-    8596,   8592,   8593,   8594,   8595,   176,    177,    8243,   8805,
-    215,    8733,   8706,   8729,   247,    8800,   8801,   8776,   8943,
-    0,      0,      8629,   0,      8465,   8476,   8472,   8855,   8853,
-    8709,   8745,   8746,   8835,   8839,   8836,   8834,   8838,   8712,
-    8713,   8736,   8711,   174,    169,    8482,   8719,   8730,   8901,
-    172,    8743,   8744,   8660,   8656,   8657,   8658,   8659,   9674,
-    9001,   0,      0,      0,      8721,   0,      0,      0,      0,
-    0,      0,      0,      0,      0,      0,      0x0000, 9002,   8747,
-    8992,   0,      8993,   0,      0,      0,      0,      0,      0,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03d2,
+    0x2032, 0x2264, 0x2215, 0x221e, 0x0192, 0x2663, 0x2666, 0x2664, 0x2660,
+    0x2194, 0x2190, 0x2191, 0x2192, 0x2193, 0x00b0, 0x00b1, 0x2033, 0x2265,
+    0x00d7, 0x221d, 0x2202, 0x2219, 0x00f7, 0x2260, 0x2261, 0x2248, 0x22ef,
+    0x0000, 0x0000, 0x21b5, 0x0000, 0x2111, 0x211c, 0x2118, 0x2297, 0x2295,
+    0x2205, 0x2229, 0x222a, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208,
+    0x2209, 0x2220, 0x2207, 0x00ae, 0x00a9, 0x2122, 0x220f, 0x221a, 0x22c5,
+    0x00ac, 0x2227, 0x2228, 0x21d4, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x25ca,
+    0x2329, 0x0000, 0x0000, 0x0000, 0x2211, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x232a, 0x222b,
+    0x2320, 0x0000, 0x2321, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
     0x0000, 0x0000, 0x0000, 0x0000};
 
-const uint16_t StandardEncoding[256] = {
+const uint16_t StandardEncoding[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,
@@ -79,7 +81,7 @@
     0x0000, 0x0000, 0x0131, 0x0000, 0x0000, 0x0142, 0x00f8, 0x0153, 0x00df,
     0x0000, 0x0000, 0x0000, 0x0000};
 
-const uint16_t MacRomanEncoding[256] = {
+const uint16_t MacRomanEncoding[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,
@@ -110,7 +112,7 @@
     0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da,
     0x00b8, 0x02dd, 0x02db, 0x02c7};
 
-const uint16_t AdobeWinAnsiEncoding[256] = {
+const uint16_t AdobeWinAnsiEncoding[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,
@@ -141,7 +143,7 @@
     0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb,
     0x00fc, 0x00fd, 0x00fe, 0x00ff};
 
-const uint16_t MacExpertEncoding[256] = {
+const uint16_t MacExpertEncoding[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,
@@ -172,7 +174,7 @@
     0xf6f4, 0xf7af, 0xf6ea, 0x207f, 0xf6ef, 0xf6e2, 0xf6e8, 0xf6f7, 0xf6fc,
     0x0000, 0x0000, 0x0000, 0x0000};
 
-const uint16_t AdobeSymbolEncoding[256] = {
+const uint16_t AdobeSymbolEncoding[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,
@@ -204,7 +206,7 @@
     0xF8FC, 0xF8FD, 0xF8FE, 0x0000,
 };
 
-const uint16_t ZapfEncoding[256] = {
+const uint16_t ZapfEncoding[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,
@@ -236,7 +238,14 @@
     0x27BC, 0x27BD, 0x27BE, 0x0000,
 };
 
-const char* const StandardEncodingNames[224] = {
+constexpr size_t kEncodingTableFirstChar = 32;
+constexpr size_t kEncodingNamesTableSize =
+    CPDF_FontEncoding::kEncodingTableSize - kEncodingTableFirstChar;
+constexpr size_t kPDFDocEncodingTableFirstChar = 24;
+constexpr size_t kPDFDocEncodingNamesTableSize =
+    CPDF_FontEncoding::kEncodingTableSize - kPDFDocEncodingTableFirstChar;
+
+const char* const StandardEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "quotedbl",
@@ -463,7 +472,7 @@
     nullptr,
 };
 
-const char* const AdobeWinAnsiEncodingNames[224] = {
+const char* const AdobeWinAnsiEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "quotedbl",
@@ -690,7 +699,7 @@
     "ydieresis",
 };
 
-const char* const MacRomanEncodingNames[224] = {
+const char* const MacRomanEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "quotedbl",
@@ -917,7 +926,7 @@
     "caron",
 };
 
-const char* const MacExpertEncodingNames[224] = {
+const char* const MacExpertEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclamsmall",
     "Hungarumlautsmall",
@@ -1144,7 +1153,7 @@
     nullptr,
 };
 
-const char* const PDFDocEncodingNames[232] = {
+const char* const PDFDocEncodingNames[kPDFDocEncodingNamesTableSize] = {
     "breve",
     "caron",
     "circumflex",
@@ -1379,7 +1388,7 @@
     "ydieresis",
 };
 
-const char* const AdobeSymbolEncodingNames[224] = {
+const char* const AdobeSymbolEncodingNames[kEncodingNamesTableSize] = {
     "space",
     "exclam",
     "universal",
@@ -1606,7 +1615,7 @@
     nullptr,
 };
 
-const char* const ZapfEncodingNames[224] = {
+const char* const ZapfEncodingNames[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",
@@ -1637,48 +1646,45 @@
     "a186",  "a195",  "a187",  "a188",  "a189",  "a190",  "a191",  nullptr};
 
 uint32_t PDF_FindCode(const uint16_t* pCodes, uint16_t unicode) {
-  for (uint32_t i = 0; i < 256; i++)
+  for (size_t i = 0; i < CPDF_FontEncoding::kEncodingTableSize; i++) {
     if (pCodes[i] == unicode)
       return i;
+  }
   return 0;
 }
 
 }  // namespace
 
-CPDF_FontEncoding::CPDF_FontEncoding() {
-  memset(m_Unicodes, 0, sizeof(m_Unicodes));
-}
-
 int CPDF_FontEncoding::CharCodeFromUnicode(wchar_t unicode) const {
-  for (int i = 0; i < 256; i++)
-    if (m_Unicodes[i] == unicode) {
+  for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) {
+    if (m_Unicodes[i] == unicode)
       return i;
-    }
+  }
   return -1;
 }
 
 CPDF_FontEncoding::CPDF_FontEncoding(int PredefinedEncoding) {
   const uint16_t* pSrc = PDF_UnicodesForPredefinedCharSet(PredefinedEncoding);
-  if (!pSrc) {
-    memset(m_Unicodes, 0, sizeof(m_Unicodes));
-  } else {
-    for (int i = 0; i < 256; i++)
+  if (pSrc) {
+    for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++)
       m_Unicodes[i] = pSrc[i];
+  } else {
+    memset(m_Unicodes, 0, sizeof(m_Unicodes));
   }
 }
 
-bool CPDF_FontEncoding::IsIdentical(CPDF_FontEncoding* pAnother) const {
+bool CPDF_FontEncoding::IsIdentical(const CPDF_FontEncoding* pAnother) const {
   return memcmp(m_Unicodes, pAnother->m_Unicodes, sizeof(m_Unicodes)) == 0;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_FontEncoding::Realize(
-    WeakPtr<ByteStringPool> pPool) {
+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);
     bool match = true;
-    for (int i = 0; i < 256; ++i) {
+    for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) {
       if (m_Unicodes[i] != pSrc[i]) {
         match = false;
         break;
@@ -1690,47 +1696,50 @@
     }
   }
   if (predefined) {
+    const char* pName;
     if (predefined == PDFFONT_ENCODING_WINANSI)
-      return pdfium::MakeUnique<CPDF_Name>(pPool, "WinAnsiEncoding");
-    if (predefined == PDFFONT_ENCODING_MACROMAN)
-      return pdfium::MakeUnique<CPDF_Name>(pPool, "MacRomanEncoding");
-    if (predefined == PDFFONT_ENCODING_MACEXPERT)
-      return pdfium::MakeUnique<CPDF_Name>(pPool, "MacExpertEncoding");
+      pName = "WinAnsiEncoding";
+    else if (predefined == PDFFONT_ENCODING_MACROMAN)
+      pName = "MacRomanEncoding";
+    else if (predefined == PDFFONT_ENCODING_MACEXPERT)
+      pName = "MacExpertEncoding";
+    else
+      return nullptr;
 
-    return nullptr;
+    return pdfium::MakeRetain<CPDF_Name>(pPool, pName);
   }
   const uint16_t* pStandard =
       PDF_UnicodesForPredefinedCharSet(PDFFONT_ENCODING_WINANSI);
-  auto pDiff = pdfium::MakeUnique<CPDF_Array>();
-  for (int i = 0; i < 256; i++) {
+  auto pDiff = pdfium::MakeRetain<CPDF_Array>();
+  for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) {
     if (pStandard[i] == m_Unicodes[i])
       continue;
 
-    pDiff->AddNew<CPDF_Number>(i);
+    pDiff->AddNew<CPDF_Number>(static_cast<int>(i));
     pDiff->AddNew<CPDF_Name>(PDF_AdobeNameFromUnicode(m_Unicodes[i]));
   }
 
-  auto pDict = pdfium::MakeUnique<CPDF_Dictionary>(pPool);
+  auto pDict = pdfium::MakeRetain<CPDF_Dictionary>(pPool);
   pDict->SetNewFor<CPDF_Name>("BaseEncoding", "WinAnsiEncoding");
-  pDict->SetFor("Differences", std::move(pDiff));
-  return std::move(pDict);
+  pDict->SetFor("Differences", pDiff);
+  return pDict;
 }
 
 uint32_t FT_CharCodeFromUnicode(int encoding, wchar_t unicode) {
   switch (encoding) {
-    case FXFT_ENCODING_UNICODE:
+    case FT_ENCODING_UNICODE:
       return unicode;
-    case FXFT_ENCODING_ADOBE_STANDARD:
+    case FT_ENCODING_ADOBE_STANDARD:
       return PDF_FindCode(StandardEncoding, unicode);
-    case FXFT_ENCODING_ADOBE_EXPERT:
+    case FT_ENCODING_ADOBE_EXPERT:
       return PDF_FindCode(MacExpertEncoding, unicode);
-    case FXFT_ENCODING_ADOBE_LATIN_1:
+    case FT_ENCODING_ADOBE_LATIN_1:
       return PDF_FindCode(AdobeWinAnsiEncoding, unicode);
-    case FXFT_ENCODING_APPLE_ROMAN:
+    case FT_ENCODING_APPLE_ROMAN:
       return PDF_FindCode(MacRomanEncoding, unicode);
-    case FXFT_ENCODING_ADOBE_CUSTOM:
+    case FT_ENCODING_ADOBE_CUSTOM:
       return PDF_FindCode(PDFDocEncoding, unicode);
-    case FXFT_ENCODING_MS_SYMBOL:
+    case FT_ENCODING_MS_SYMBOL:
       return PDF_FindCode(MSSymbolEncoding, unicode);
   }
   return 0;
@@ -1757,27 +1766,17 @@
   return nullptr;
 }
 
-wchar_t PDF_UnicodeFromAdobeName(const char* name) {
-  return (wchar_t)(FXFT_unicode_from_adobe_name(name) & 0x7FFFFFFF);
-}
-
-ByteString PDF_AdobeNameFromUnicode(wchar_t unicode) {
-  char glyph_name[64];
-  FXFT_adobe_name_from_unicode(glyph_name, unicode);
-  return ByteString(glyph_name);
-}
-
 const char* PDF_CharNameFromPredefinedCharSet(int encoding, uint8_t charcode) {
   if (encoding == PDFFONT_ENCODING_PDFDOC) {
-    if (charcode < 24)
+    if (charcode < kPDFDocEncodingTableFirstChar)
       return nullptr;
 
-    charcode -= 24;
+    charcode -= kPDFDocEncodingTableFirstChar;
   } else {
-    if (charcode < 32)
+    if (charcode < kEncodingTableFirstChar)
       return nullptr;
 
-    charcode -= 32;
+    charcode -= kEncodingTableFirstChar;
   }
   switch (encoding) {
     case PDFFONT_ENCODING_WINANSI:
@@ -1800,15 +1799,15 @@
 
 wchar_t FT_UnicodeFromCharCode(int encoding, uint32_t charcode) {
   switch (encoding) {
-    case FXFT_ENCODING_UNICODE:
+    case FT_ENCODING_UNICODE:
       return (uint16_t)charcode;
-    case FXFT_ENCODING_ADOBE_STANDARD:
+    case FT_ENCODING_ADOBE_STANDARD:
       return StandardEncoding[(uint8_t)charcode];
-    case FXFT_ENCODING_ADOBE_EXPERT:
+    case FT_ENCODING_ADOBE_EXPERT:
       return MacExpertEncoding[(uint8_t)charcode];
-    case FXFT_ENCODING_ADOBE_LATIN_1:
+    case FT_ENCODING_ADOBE_LATIN_1:
       return AdobeWinAnsiEncoding[(uint8_t)charcode];
-    case FXFT_ENCODING_APPLE_ROMAN:
+    case FT_ENCODING_APPLE_ROMAN:
       return MacRomanEncoding[(uint8_t)charcode];
     case PDFFONT_ENCODING_PDFDOC:
       return PDFDocEncoding[(uint8_t)charcode];
diff --git a/core/fpdfapi/font/cpdf_fontencoding.h b/core/fpdfapi/font/cpdf_fontencoding.h
index 040708f..1bfb0d5 100644
--- a/core/fpdfapi/font/cpdf_fontencoding.h
+++ b/core/fpdfapi/font/cpdf_fontencoding.h
@@ -26,9 +26,6 @@
 uint32_t FT_CharCodeFromUnicode(int encoding, wchar_t unicode);
 wchar_t FT_UnicodeFromCharCode(int encoding, uint32_t charcode);
 
-wchar_t PDF_UnicodeFromAdobeName(const char* name);
-ByteString PDF_AdobeNameFromUnicode(wchar_t unicode);
-
 const uint16_t* PDF_UnicodesForPredefinedCharSet(int encoding);
 const char* PDF_CharNameFromPredefinedCharSet(int encoding, uint8_t charcode);
 
@@ -36,12 +33,11 @@
 
 class CPDF_FontEncoding {
  public:
-  CPDF_FontEncoding();
+  static constexpr size_t kEncodingTableSize = 256;
+
   explicit CPDF_FontEncoding(int PredefinedEncoding);
 
-  void LoadEncoding(CPDF_Object* pEncoding);
-
-  bool IsIdentical(CPDF_FontEncoding* pAnother) const;
+  bool IsIdentical(const CPDF_FontEncoding* pAnother) const;
 
   wchar_t UnicodeFromCharCode(uint8_t charcode) const {
     return m_Unicodes[charcode];
@@ -52,10 +48,10 @@
     m_Unicodes[charcode] = unicode;
   }
 
-  std::unique_ptr<CPDF_Object> Realize(WeakPtr<ByteStringPool> pPool);
+  RetainPtr<CPDF_Object> Realize(WeakPtr<ByteStringPool> pPool) const;
 
- public:
-  wchar_t m_Unicodes[256];
+ private:
+  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 ddf87b1..10b6c5e 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.cpp
+++ b/core/fpdfapi/font/cpdf_fontglobals.cpp
@@ -6,34 +6,98 @@
 
 #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/parser/cpdf_document.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
+namespace {
+
+CPDF_FontGlobals* g_FontGlobals = nullptr;
+
+}  // namespace
+
+// static
+void CPDF_FontGlobals::Create() {
+  ASSERT(!g_FontGlobals);
+  g_FontGlobals = new CPDF_FontGlobals();
+}
+
+// static
+void CPDF_FontGlobals::Destroy() {
+  ASSERT(g_FontGlobals);
+  delete g_FontGlobals;
+  g_FontGlobals = nullptr;
+}
+
+// static
+CPDF_FontGlobals* CPDF_FontGlobals::GetInstance() {
+  ASSERT(g_FontGlobals);
+  return g_FontGlobals;
+}
+
 CPDF_FontGlobals::CPDF_FontGlobals() {
   memset(m_EmbeddedCharsets, 0, sizeof(m_EmbeddedCharsets));
   memset(m_EmbeddedToUnicodes, 0, sizeof(m_EmbeddedToUnicodes));
 }
 
-CPDF_FontGlobals::~CPDF_FontGlobals() {}
+CPDF_FontGlobals::~CPDF_FontGlobals() = default;
 
-CPDF_Font* CPDF_FontGlobals::Find(CPDF_Document* pDoc, uint32_t index) {
-  auto it = m_StockMap.find(pDoc);
-  if (it == m_StockMap.end())
-    return nullptr;
-  return it->second ? it->second->GetFont(index) : nullptr;
+void CPDF_FontGlobals::LoadEmbeddedMaps() {
+  LoadEmbeddedGB1CMaps();
+  LoadEmbeddedCNS1CMaps();
+  LoadEmbeddedJapan1CMaps();
+  LoadEmbeddedKorea1CMaps();
 }
 
-CPDF_Font* CPDF_FontGlobals::Set(CPDF_Document* pDoc,
-                                 uint32_t index,
-                                 std::unique_ptr<CPDF_Font> pFont) {
+RetainPtr<CPDF_Font> CPDF_FontGlobals::Find(
+    CPDF_Document* pDoc,
+    CFX_FontMapper::StandardFont index) {
+  auto it = m_StockMap.find(pDoc);
+  if (it == m_StockMap.end() || !it->second)
+    return nullptr;
+
+  return it->second->GetFont(index);
+}
+
+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>();
-  return m_StockMap[pDoc]->SetFont(index, std::move(pFont));
+  m_StockMap[pDoc]->SetFont(index, pFont);
 }
 
 void CPDF_FontGlobals::Clear(CPDF_Document* pDoc) {
   m_StockMap.erase(pDoc);
 }
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
diff --git a/core/fpdfapi/font/cpdf_fontglobals.h b/core/fpdfapi/font/cpdf_fontglobals.h
index a47dfa1..c09f29c 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.h
+++ b/core/fpdfapi/font/cpdf_fontglobals.h
@@ -9,55 +9,59 @@
 
 #include <map>
 #include <memory>
-#include <utility>
 
-#include "core/fpdfapi/cmaps/cmap_int.h"
-#include "core/fpdfapi/font/cfx_stockfontarray.h"
+#include "core/fpdfapi/cmaps/fpdf_cmaps.h"
 #include "core/fpdfapi/font/cpdf_cmapmanager.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_fontmapper.h"
+#include "third_party/base/span.h"
+
+class CFX_StockFontArray;
 
 class CPDF_FontGlobals {
  public:
-  CPDF_FontGlobals();
-  ~CPDF_FontGlobals();
+  // Per-process singleton which must be managed by callers.
+  static void Create();
+  static void Destroy();
+  static CPDF_FontGlobals* GetInstance();
+
+  // Caller must load the maps before using font globals.
+  void LoadEmbeddedMaps();
 
   void Clear(CPDF_Document* pDoc);
-  CPDF_Font* Find(CPDF_Document* pDoc, uint32_t index);
+  RetainPtr<CPDF_Font> Find(CPDF_Document* pDoc,
+                            CFX_FontMapper::StandardFont index);
+  void Set(CPDF_Document* pDoc,
+           CFX_FontMapper::StandardFont index,
+           const RetainPtr<CPDF_Font>& pFont);
 
-  // Takes ownership of |pFont|, returns unowned pointer to it.
-  CPDF_Font* Set(CPDF_Document* key,
-                 uint32_t index,
-                 std::unique_ptr<CPDF_Font> pFont);
-
-  void SetEmbeddedCharset(size_t idx, const FXCMAP_CMap* map, uint32_t count) {
-    m_EmbeddedCharsets[idx].m_pMapList = map;
-    m_EmbeddedCharsets[idx].m_Count = count;
+  void SetEmbeddedCharset(size_t idx, pdfium::span<const FXCMAP_CMap> map) {
+    m_EmbeddedCharsets[idx] = map;
   }
-  std::pair<uint32_t, const FXCMAP_CMap*> GetEmbeddedCharset(size_t idx) const {
-    return {m_EmbeddedCharsets[idx].m_Count,
-            m_EmbeddedCharsets[idx].m_pMapList.Get()};
+  pdfium::span<const FXCMAP_CMap> GetEmbeddedCharset(size_t idx) const {
+    return m_EmbeddedCharsets[idx];
   }
-  void SetEmbeddedToUnicode(size_t idx, const uint16_t* map, uint32_t count) {
-    m_EmbeddedToUnicodes[idx].m_pMap = map;
-    m_EmbeddedToUnicodes[idx].m_Count = count;
+  void SetEmbeddedToUnicode(size_t idx, pdfium::span<const uint16_t> map) {
+    m_EmbeddedToUnicodes[idx] = map;
   }
-  std::pair<uint32_t, const uint16_t*> GetEmbeddedToUnicode(size_t idx) {
-    return {m_EmbeddedToUnicodes[idx].m_Count,
-            m_EmbeddedToUnicodes[idx].m_pMap};
+  pdfium::span<const uint16_t> GetEmbeddedToUnicode(size_t idx) {
+    return m_EmbeddedToUnicodes[idx];
   }
 
   CPDF_CMapManager* GetCMapManager() { return &m_CMapManager; }
 
  private:
-  CPDF_CMapManager m_CMapManager;
-  struct {
-    UnownedPtr<const FXCMAP_CMap> m_pMapList;
-    uint32_t m_Count;
-  } m_EmbeddedCharsets[CIDSET_NUM_SETS];
-  struct {
-    const uint16_t* m_pMap;
-    uint32_t m_Count;
-  } m_EmbeddedToUnicodes[CIDSET_NUM_SETS];
+  CPDF_FontGlobals();
+  ~CPDF_FontGlobals();
 
+  void LoadEmbeddedGB1CMaps();
+  void LoadEmbeddedCNS1CMaps();
+  void LoadEmbeddedJapan1CMaps();
+  void LoadEmbeddedKorea1CMaps();
+
+  CPDF_CMapManager m_CMapManager;
+  pdfium::span<const FXCMAP_CMap> m_EmbeddedCharsets[CIDSET_NUM_SETS];
+  pdfium::span<const uint16_t> m_EmbeddedToUnicodes[CIDSET_NUM_SETS];
   std::map<CPDF_Document*, std::unique_ptr<CFX_StockFontArray>> m_StockMap;
 };
 
diff --git a/core/fpdfapi/font/cpdf_simplefont.cpp b/core/fpdfapi/font/cpdf_simplefont.cpp
index 92965b0..c0fd4e0 100644
--- a/core/fpdfapi/font/cpdf_simplefont.cpp
+++ b/core/fpdfapi/font/cpdf_simplefont.cpp
@@ -8,18 +8,36 @@
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fxge/fx_font.h"
 #include "core/fxge/fx_freetype.h"
 #include "third_party/base/numerics/safe_math.h"
 
-CPDF_SimpleFont::CPDF_SimpleFont() : m_BaseEncoding(PDFFONT_ENCODING_BUILTIN) {
+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;
+}
+
+}  // namespace
+
+CPDF_SimpleFont::CPDF_SimpleFont(CPDF_Document* pDocument,
+                                 CPDF_Dictionary* pFontDict)
+    : CPDF_Font(pDocument, pFontDict) {
   memset(m_CharWidth, 0xff, sizeof(m_CharWidth));
   memset(m_GlyphIndex, 0xff, sizeof(m_GlyphIndex));
-  memset(m_ExtGID, 0xff, sizeof(m_ExtGID));
   for (size_t i = 0; i < FX_ArraySize(m_CharBBox); ++i)
     m_CharBBox[i] = FX_RECT(-1, -1, -1, -1);
 }
 
-CPDF_SimpleFont::~CPDF_SimpleFont() {}
+CPDF_SimpleFont::~CPDF_SimpleFont() = default;
 
 int CPDF_SimpleFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) {
   if (pVertGlyph)
@@ -29,14 +47,14 @@
     return -1;
 
   int index = m_GlyphIndex[charcode];
-  if (index == 0xffff || (index == 0 && IsTrueTypeFont()))
+  if (index == 0xffff)
     return -1;
 
   return index;
 }
 
 void CPDF_SimpleFont::LoadCharMetrics(int charcode) {
-  if (!m_Font.GetFace())
+  if (!m_Font.GetFaceRec())
     return;
 
   if (charcode < 0 || charcode > 0xff) {
@@ -53,10 +71,10 @@
     }
     return;
   }
-  FXFT_Face face = m_Font.GetFace();
-  int err = FXFT_Load_Glyph(
-      face, glyph_index,
-      FXFT_LOAD_NO_SCALE | FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
+  FXFT_FaceRec* face = m_Font.GetFaceRec();
+  int err =
+      FT_Load_Glyph(face, glyph_index,
+                    FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
   if (err)
     return;
 
@@ -80,7 +98,72 @@
   }
 }
 
-int CPDF_SimpleFont::GetCharWidthF(uint32_t charcode) {
+void CPDF_SimpleFont::LoadPDFEncoding(bool bEmbedded, bool bTrueType) {
+  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;
+    }
+    return;
+  }
+  if (pEncoding->IsName()) {
+    if (m_BaseEncoding == PDFFONT_ENCODING_ADOBE_SYMBOL ||
+        m_BaseEncoding == PDFFONT_ENCODING_ZAPFDINGBATS) {
+      return;
+    }
+    if (FontStyleIsSymbolic(m_Flags) && m_BaseFontName == "Symbol") {
+      if (!bTrueType)
+        m_BaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL;
+      return;
+    }
+    ByteString bsEncoding = pEncoding->GetString();
+    if (bsEncoding.Compare("MacExpertEncoding") == 0) {
+      bsEncoding = "WinAnsiEncoding";
+    }
+    GetPredefinedEncoding(bsEncoding, &m_BaseEncoding);
+    return;
+  }
+
+  const CPDF_Dictionary* pDict = pEncoding->AsDictionary();
+  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";
+    GetPredefinedEncoding(bsEncoding, &m_BaseEncoding);
+  }
+  if ((!bEmbedded || bTrueType) && m_BaseEncoding == PDFFONT_ENCODING_BUILTIN)
+    m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
+
+  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();
+    }
+  }
+}
+
+uint32_t CPDF_SimpleFont::GetCharWidthF(uint32_t charcode) {
   if (charcode > 0xff)
     charcode = 0;
 
@@ -104,11 +187,11 @@
 }
 
 bool CPDF_SimpleFont::LoadCommon() {
-  CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
+  const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
   if (pFontDesc) {
     LoadFontDescriptor(pFontDesc);
   }
-  CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths");
+  const CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths");
   m_bUseFontWidth = !pWidthArray;
   if (pWidthArray) {
     if (pFontDesc && pFontDesc->KeyExist("MissingWidth")) {
@@ -120,8 +203,8 @@
     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->GetCount())
-        width_end = width_start + pWidthArray->GetCount() - 1;
+      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++)
@@ -129,23 +212,22 @@
     }
   }
   if (m_pFontFile) {
-    if (m_BaseFont.GetLength() > 8 && m_BaseFont[7] == '+')
-      m_BaseFont = m_BaseFont.Right(m_BaseFont.GetLength() - 8);
+    if (m_BaseFontName.GetLength() > 8 && m_BaseFontName[7] == '+')
+      m_BaseFontName = m_BaseFontName.Last(m_BaseFontName.GetLength() - 8);
   } else {
     LoadSubstFont();
   }
   if (!FontStyleIsSymbolic(m_Flags))
     m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
-  CPDF_Object* pEncoding = m_pFontDict->GetDirectObjectFor("Encoding");
-  LoadPDFEncoding(pEncoding, m_BaseEncoding, &m_CharNames, !!m_pFontFile,
-                  m_Font.IsTTFont());
+  LoadPDFEncoding(!!m_pFontFile, m_Font.IsTTFont());
   LoadGlyphMap();
   m_CharNames.clear();
-  if (!m_Font.GetFace())
+  if (!m_Font.GetFaceRec())
     return true;
 
   if (FontStyleIsAllCaps(m_Flags)) {
-    unsigned char kLowercases[][2] = {{'a', 'z'}, {0xe0, 0xf6}, {0xf8, 0xfd}};
+    static const unsigned char kLowercases[][2] = {
+        {'a', 'z'}, {0xe0, 0xf6}, {0xf8, 0xfd}};
     for (size_t range = 0; range < FX_ArraySize(kLowercases); ++range) {
       const auto& lower = kLowercases[range];
       for (int i = lower[0]; i <= lower[1]; ++i) {
@@ -180,14 +262,8 @@
     if (i == 256 && width)
       m_Flags |= FXFONT_FIXED_PITCH;
   }
-  pdfium::base::CheckedNumeric<int> safeStemV(m_StemV);
-  if (m_StemV < 140)
-    safeStemV *= 5;
-  else
-    safeStemV = safeStemV * 4 + 140;
-  m_Font.LoadSubst(m_BaseFont, IsTrueTypeFont(), m_Flags,
-                   safeStemV.ValueOrDefault(FXFONT_FW_NORMAL), m_ItalicAngle, 0,
-                   false);
+  m_Font.LoadSubst(m_BaseFontName, IsTrueTypeFont(), m_Flags, GetFontWeight(),
+                   m_ItalicAngle, 0, false);
 }
 
 bool CPDF_SimpleFont::IsUnicodeCompatible() const {
diff --git a/core/fpdfapi/font/cpdf_simplefont.h b/core/fpdfapi/font/cpdf_simplefont.h
index 5291211..11359c3 100644
--- a/core/fpdfapi/font/cpdf_simplefont.h
+++ b/core/fpdfapi/font/cpdf_simplefont.h
@@ -16,36 +16,37 @@
 
 class CPDF_SimpleFont : public CPDF_Font {
  public:
-  CPDF_SimpleFont();
   ~CPDF_SimpleFont() override;
 
   // CPDF_Font
-  int GetCharWidthF(uint32_t charcode) override;
+  uint32_t GetCharWidthF(uint32_t charcode) override;
   FX_RECT GetCharBBox(uint32_t charcode) override;
   int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) override;
   bool IsUnicodeCompatible() const override;
   WideString UnicodeFromCharCode(uint32_t charcode) const override;
   uint32_t CharCodeFromUnicode(wchar_t Unicode) const override;
 
-  CPDF_FontEncoding* GetEncoding() { return &m_Encoding; }
+  const CPDF_FontEncoding* GetEncoding() const { return &m_Encoding; }
 
   bool HasFontWidths() const override;
 
  protected:
+  CPDF_SimpleFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+
   virtual void LoadGlyphMap() = 0;
 
   bool LoadCommon();
   void LoadSubstFont();
   void LoadCharMetrics(int charcode);
+  void LoadPDFEncoding(bool bEmbedded, bool bTrueType);
 
-  CPDF_FontEncoding m_Encoding;
-  uint16_t m_GlyphIndex[256];
-  uint16_t m_ExtGID[256];
+  CPDF_FontEncoding m_Encoding{PDFFONT_ENCODING_BUILTIN};
+  int m_BaseEncoding = PDFFONT_ENCODING_BUILTIN;
+  bool m_bUseFontWidth;
   std::vector<ByteString> m_CharNames;
-  int m_BaseEncoding;
+  uint16_t m_GlyphIndex[256];
   uint16_t m_CharWidth[256];
   FX_RECT m_CharBBox[256];
-  bool m_bUseFontWidth;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_SIMPLEFONT_H_
diff --git a/core/fpdfapi/font/cpdf_tounicodemap.cpp b/core/fpdfapi/font/cpdf_tounicodemap.cpp
index 0b746d7..1872d5d 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap.cpp
@@ -6,77 +6,24 @@
 
 #include "core/fpdfapi/font/cpdf_tounicodemap.h"
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include <utility>
+
 #include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/font/cpdf_fontglobals.h"
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
-WideString CPDF_ToUnicodeMap::Lookup(uint32_t charcode) const {
-  auto it = m_Map.find(charcode);
-  if (it != m_Map.end()) {
-    uint32_t value = it->second;
-    wchar_t unicode = (wchar_t)(value & 0xffff);
-    if (unicode != 0xffff) {
-      return unicode;
-    }
-    const wchar_t* buf = m_MultiCharBuf.GetBuffer();
-    uint32_t buf_len = m_MultiCharBuf.GetLength();
-    if (!buf || buf_len == 0) {
-      return WideString();
-    }
-    uint32_t index = value >> 16;
-    if (index >= buf_len) {
-      return WideString();
-    }
-    uint32_t len = buf[index];
-    if (index + len < index || index + len >= buf_len) {
-      return WideString();
-    }
-    return WideString(buf + index + 1, len);
-  }
-  if (m_pBaseMap) {
-    return m_pBaseMap->UnicodeFromCID((uint16_t)charcode);
-  }
-  return WideString();
-}
+namespace {
 
-uint32_t CPDF_ToUnicodeMap::ReverseLookup(wchar_t unicode) const {
-  for (const auto& pair : m_Map) {
-    if (pair.second == static_cast<uint32_t>(unicode))
-      return pair.first;
-  }
-  return 0;
-}
-
-// Static.
-uint32_t CPDF_ToUnicodeMap::StringToCode(const ByteStringView& str) {
-  int len = str.GetLength();
-  if (len == 0)
-    return 0;
-
-  uint32_t result = 0;
-  if (str[0] == '<') {
-    for (int i = 1; i < len && std::isxdigit(str[i]); ++i)
-      result = result * 16 + FXSYS_HexCharToInt(str.CharAt(i));
-    return result;
-  }
-
-  for (int i = 0; i < len && std::isdigit(str[i]); ++i)
-    result = result * 10 + FXSYS_DecimalCharToInt(str.CharAt(i));
-
-  return result;
-}
-
-static WideString StringDataAdd(WideString str) {
+WideString StringDataAdd(WideString str) {
   WideString ret;
-  int len = str.GetLength();
   wchar_t value = 1;
-  for (int i = len - 1; i >= 0; --i) {
-    wchar_t ch = str[i] + value;
-    if (ch < str[i]) {
+  for (size_t i = str.GetLength(); i > 0; --i) {
+    wchar_t ch = str[i - 1] + value;
+    if (ch < str[i - 1]) {
       ret.InsertAtFront(0);
     } else {
       ret.InsertAtFront(ch);
@@ -88,145 +35,191 @@
   return ret;
 }
 
-// Static.
-WideString CPDF_ToUnicodeMap::StringToWideString(const ByteStringView& str) {
-  int len = str.GetLength();
-  if (len == 0)
+}  // namespace
+
+CPDF_ToUnicodeMap::CPDF_ToUnicodeMap(const CPDF_Stream* pStream) {
+  Load(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()) {
+    if (!m_pBaseMap)
+      return WideString();
+    return m_pBaseMap->UnicodeFromCID(static_cast<uint16_t>(charcode));
+  }
+
+  uint32_t value = it->second;
+  wchar_t unicode = static_cast<wchar_t>(value & 0xffff);
+  if (unicode != 0xffff)
+    return 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]));
+}
+
+uint32_t CPDF_ToUnicodeMap::ReverseLookup(wchar_t unicode) const {
+  for (const auto& pair : m_Map) {
+    if (pair.second == static_cast<uint32_t>(unicode))
+      return pair.first;
+  }
+  return 0;
+}
+
+// static
+pdfium::Optional<uint32_t> CPDF_ToUnicodeMap::StringToCode(ByteStringView str) {
+  size_t len = str.GetLength();
+  if (len <= 2 || str[0] != '<' || str[len - 1] != '>')
+    return pdfium::nullopt;
+
+  FX_SAFE_UINT32 code = 0;
+  for (char c : str.Substr(1, len - 2)) {
+    if (!FXSYS_IsHexDigit(c))
+      return pdfium::nullopt;
+
+    code = code * 16 + FXSYS_HexCharToInt(c);
+    if (!code.IsValid())
+      return pdfium::nullopt;
+  }
+  return pdfium::Optional<uint32_t>(code.ValueOrDie());
+}
+
+// static
+WideString CPDF_ToUnicodeMap::StringToWideString(ByteStringView str) {
+  size_t len = str.GetLength();
+  if (len <= 2 || str[0] != '<' || str[len - 1] != '>')
     return WideString();
 
   WideString result;
-  if (str[0] == '<') {
-    int byte_pos = 0;
-    wchar_t ch = 0;
-    for (int i = 1; i < len && std::isxdigit(str[i]); ++i) {
-      ch = ch * 16 + FXSYS_HexCharToInt(str[i]);
-      byte_pos++;
-      if (byte_pos == 4) {
-        result += ch;
-        byte_pos = 0;
-        ch = 0;
-      }
+  int byte_pos = 0;
+  wchar_t ch = 0;
+  for (char c : str.Substr(1, len - 2)) {
+    if (!FXSYS_IsHexDigit(c))
+      break;
+
+    ch = ch * 16 + FXSYS_HexCharToInt(c);
+    byte_pos++;
+    if (byte_pos == 4) {
+      result += ch;
+      byte_pos = 0;
+      ch = 0;
     }
-    return result;
   }
   return result;
 }
 
-CPDF_ToUnicodeMap::CPDF_ToUnicodeMap() : m_pBaseMap(nullptr) {}
+void CPDF_ToUnicodeMap::Load(const CPDF_Stream* pStream) {
+  CIDSet cid_set = CIDSET_UNKNOWN;
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+  pAcc->LoadAllDataFiltered();
+  CPDF_SimpleParser parser(pAcc->GetSpan());
+  while (1) {
+    ByteStringView word = parser.GetWord();
+    if (word.IsEmpty())
+      break;
 
-CPDF_ToUnicodeMap::~CPDF_ToUnicodeMap() {}
+    if (word == "beginbfchar")
+      HandleBeginBFChar(&parser);
+    else if (word == "beginbfrange")
+      HandleBeginBFRange(&parser);
+    else if (word == "/Adobe-Korea1-UCS2")
+      cid_set = CIDSET_KOREA1;
+    else if (word == "/Adobe-Japan1-UCS2")
+      cid_set = CIDSET_JAPAN1;
+    else if (word == "/Adobe-CNS1-UCS2")
+      cid_set = CIDSET_CNS1;
+    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);
+  }
+}
 
-uint32_t CPDF_ToUnicodeMap::GetUnicode() {
+void CPDF_ToUnicodeMap::HandleBeginBFChar(CPDF_SimpleParser* pParser) {
+  while (1) {
+    ByteStringView word = pParser->GetWord();
+    if (word.IsEmpty() || word == "endbfchar")
+      return;
+
+    pdfium::Optional<uint32_t> code = StringToCode(word);
+    if (!code.has_value())
+      return;
+
+    SetCode(code.value(), StringToWideString(pParser->GetWord()));
+  }
+}
+
+void CPDF_ToUnicodeMap::HandleBeginBFRange(CPDF_SimpleParser* pParser) {
+  while (1) {
+    ByteStringView lowcode_str = pParser->GetWord();
+    if (lowcode_str.IsEmpty() || lowcode_str == "endbfrange")
+      return;
+
+    pdfium::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);
+    if (!highcode_opt.has_value())
+      return;
+
+    uint32_t lowcode = lowcode_opt.value();
+    uint32_t highcode = (lowcode & 0xffffff00) | (highcode_opt.value() & 0xff);
+
+    ByteStringView start = pParser->GetWord();
+    if (start == "[") {
+      for (uint32_t code = lowcode; code <= highcode; code++)
+        SetCode(code, StringToWideString(pParser->GetWord()));
+      pParser->GetWord();
+      continue;
+    }
+
+    WideString destcode = StringToWideString(start);
+    if (destcode.GetLength() == 1) {
+      pdfium::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++;
+    } else {
+      for (uint32_t code = lowcode; code <= highcode; code++) {
+        WideString retcode =
+            code == lowcode ? destcode : StringDataAdd(destcode);
+        m_Map[code] = GetUnicode();
+        m_MultiCharBuf.AppendChar(retcode.GetLength());
+        m_MultiCharBuf << retcode;
+        destcode = std::move(retcode);
+      }
+    }
+  }
+}
+
+uint32_t CPDF_ToUnicodeMap::GetUnicode() const {
   FX_SAFE_UINT32 uni = m_MultiCharBuf.GetLength();
   uni = uni * 0x10000 + 0xffff;
   return uni.ValueOrDefault(0);
 }
 
-void CPDF_ToUnicodeMap::Load(CPDF_Stream* pStream) {
-  CIDSet cid_set = CIDSET_UNKNOWN;
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-  pAcc->LoadAllDataFiltered();
-  CPDF_SimpleParser parser(pAcc->GetData(), pAcc->GetSize());
-  while (1) {
-    ByteStringView word = parser.GetWord();
-    if (word.IsEmpty()) {
-      break;
-    }
-    if (word == "beginbfchar") {
-      while (1) {
-        word = parser.GetWord();
-        if (word.IsEmpty() || word == "endbfchar") {
-          break;
-        }
-        uint32_t srccode = StringToCode(word);
-        word = parser.GetWord();
-        WideString destcode = StringToWideString(word);
-        int len = destcode.GetLength();
-        if (len == 0) {
-          continue;
-        }
-        if (len == 1) {
-          m_Map[srccode] = destcode[0];
-        } else {
-          m_Map[srccode] = GetUnicode();
-          m_MultiCharBuf.AppendChar(destcode.GetLength());
-          m_MultiCharBuf << destcode;
-        }
-      }
-    } else if (word == "beginbfrange") {
-      while (1) {
-        ByteString low, high;
-        low = parser.GetWord();
-        if (low.IsEmpty() || low == "endbfrange") {
-          break;
-        }
-        high = parser.GetWord();
-        uint32_t lowcode = StringToCode(low.AsStringView());
-        uint32_t highcode =
-            (lowcode & 0xffffff00) | (StringToCode(high.AsStringView()) & 0xff);
-        if (highcode == (uint32_t)-1) {
-          break;
-        }
-        ByteString start(parser.GetWord());
-        if (start == "[") {
-          for (uint32_t code = lowcode; code <= highcode; code++) {
-            ByteString dest(parser.GetWord());
-            WideString destcode = StringToWideString(dest.AsStringView());
-            int len = destcode.GetLength();
-            if (len == 0) {
-              continue;
-            }
-            if (len == 1) {
-              m_Map[code] = destcode[0];
-            } else {
-              m_Map[code] = GetUnicode();
-              m_MultiCharBuf.AppendChar(destcode.GetLength());
-              m_MultiCharBuf << destcode;
-            }
-          }
-          parser.GetWord();
-        } else {
-          WideString destcode = StringToWideString(start.AsStringView());
-          int len = destcode.GetLength();
-          uint32_t value = 0;
-          if (len == 1) {
-            value = StringToCode(start.AsStringView());
-            for (uint32_t code = lowcode; code <= highcode; code++) {
-              m_Map[code] = value++;
-            }
-          } else {
-            for (uint32_t code = lowcode; code <= highcode; code++) {
-              WideString retcode;
-              if (code == lowcode) {
-                retcode = destcode;
-              } else {
-                retcode = StringDataAdd(destcode);
-              }
-              m_Map[code] = GetUnicode();
-              m_MultiCharBuf.AppendChar(retcode.GetLength());
-              m_MultiCharBuf << retcode;
-              destcode = retcode;
-            }
-          }
-        }
-      }
-    } else if (word == "/Adobe-Korea1-UCS2") {
-      cid_set = CIDSET_KOREA1;
-    } else if (word == "/Adobe-Japan1-UCS2") {
-      cid_set = CIDSET_JAPAN1;
-    } else if (word == "/Adobe-CNS1-UCS2") {
-      cid_set = CIDSET_CNS1;
-    } else if (word == "/Adobe-GB1-UCS2") {
-      cid_set = CIDSET_GB1;
-    }
-  }
-  if (cid_set) {
-    m_pBaseMap = CPDF_ModuleMgr::Get()
-                     ->GetPageModule()
-                     ->GetFontGlobals()
-                     ->GetCMapManager()
-                     ->GetCID2UnicodeMap(cid_set, false);
+void CPDF_ToUnicodeMap::SetCode(uint32_t srccode, WideString destcode) {
+  size_t len = destcode.GetLength();
+  if (len == 0)
+    return;
+
+  if (len == 1) {
+    m_Map[srccode] = destcode[0];
   } else {
-    m_pBaseMap = nullptr;
+    m_Map[srccode] = GetUnicode();
+    m_MultiCharBuf.AppendChar(len);
+    m_MultiCharBuf << destcode;
   }
 }
diff --git a/core/fpdfapi/font/cpdf_tounicodemap.h b/core/fpdfapi/font/cpdf_tounicodemap.h
index 62fc470..9eaf625 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap.h
+++ b/core/fpdfapi/font/cpdf_tounicodemap.h
@@ -9,19 +9,18 @@
 
 #include <map>
 
-#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_CID2UnicodeMap;
+class CPDF_SimpleParser;
+class CPDF_Stream;
 
 class CPDF_ToUnicodeMap {
  public:
-  CPDF_ToUnicodeMap();
+  explicit CPDF_ToUnicodeMap(const CPDF_Stream* pStream);
   ~CPDF_ToUnicodeMap();
 
-  void Load(CPDF_Stream* pStream);
-
   WideString Lookup(uint32_t charcode) const;
   uint32_t ReverseLookup(wchar_t unicode) const;
 
@@ -29,13 +28,17 @@
   friend class cpdf_tounicodemap_StringToCode_Test;
   friend class cpdf_tounicodemap_StringToWideString_Test;
 
-  static uint32_t StringToCode(const ByteStringView& str);
-  static WideString StringToWideString(const ByteStringView& str);
+  static pdfium::Optional<uint32_t> StringToCode(ByteStringView str);
+  static WideString StringToWideString(ByteStringView str);
 
-  uint32_t GetUnicode();
+  void Load(const CPDF_Stream* pStream);
+  void HandleBeginBFChar(CPDF_SimpleParser* pParser);
+  void HandleBeginBFRange(CPDF_SimpleParser* pParser);
+  uint32_t GetUnicode() const;
+  void SetCode(uint32_t srccode, WideString destcode);
 
   std::map<uint32_t, uint32_t> m_Map;
-  UnownedPtr<CPDF_CID2UnicodeMap> m_pBaseMap;
+  UnownedPtr<const CPDF_CID2UnicodeMap> m_pBaseMap;
   CFX_WideTextBuf m_MultiCharBuf;
 };
 
diff --git a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
index 4a5dc25..7dacc5a 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
@@ -4,28 +4,46 @@
 
 #include "core/fpdfapi/font/cpdf_tounicodemap.h"
 
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(cpdf_tounicodemap, StringToCode) {
-  EXPECT_EQ(0u, CPDF_ToUnicodeMap::StringToCode(""));
-  EXPECT_EQ(194u, CPDF_ToUnicodeMap::StringToCode("<c2"));
-  EXPECT_EQ(162u, CPDF_ToUnicodeMap::StringToCode("<A2"));
-  EXPECT_EQ(2802u, CPDF_ToUnicodeMap::StringToCode("<Af2"));
-  EXPECT_EQ(12u, CPDF_ToUnicodeMap::StringToCode("12"));
-  EXPECT_EQ(128u, CPDF_ToUnicodeMap::StringToCode("128"));
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<0001>"), testing::Optional(1u));
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<c2>"), testing::Optional(194u));
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<A2>"), testing::Optional(162u));
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<Af2>"),
+              testing::Optional(2802u));
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<FFFFFFFF>"),
+              testing::Optional(4294967295u));
+
+  // Integer overflow
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<100000000>").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<1abcdFFFF>").has_value());
+
+  // Invalid string
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<>").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("12").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<12").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("12>").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<1-7>").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("00AB").has_value());
+  EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<00NN>").has_value());
 }
 
 TEST(cpdf_tounicodemap, StringToWideString) {
   EXPECT_EQ(L"", CPDF_ToUnicodeMap::StringToWideString(""));
   EXPECT_EQ(L"", CPDF_ToUnicodeMap::StringToWideString("1234"));
-
   EXPECT_EQ(L"", CPDF_ToUnicodeMap::StringToWideString("<c2"));
+  EXPECT_EQ(L"", CPDF_ToUnicodeMap::StringToWideString("<c2D2"));
+  EXPECT_EQ(L"", CPDF_ToUnicodeMap::StringToWideString("c2ab>"));
 
   WideString res = L"\xc2ab";
-  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2ab"));
-  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2abab"));
-  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2ab 1234"));
+  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2ab>"));
+  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2abab>"));
+  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2ab 1234>"));
 
   res += L"\xfaab";
-  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2abFaAb"));
+  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2abFaAb>"));
+  EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("<c2abFaAb12>"));
 }
diff --git a/core/fpdfapi/font/cpdf_truetypefont.cpp b/core/fpdfapi/font/cpdf_truetypefont.cpp
index 91c238a..d107ed0 100644
--- a/core/fpdfapi/font/cpdf_truetypefont.cpp
+++ b/core/fpdfapi/font/cpdf_truetypefont.cpp
@@ -15,7 +15,11 @@
 
 }  // namespace
 
-CPDF_TrueTypeFont::CPDF_TrueTypeFont() {}
+CPDF_TrueTypeFont::CPDF_TrueTypeFont(CPDF_Document* pDocument,
+                                     CPDF_Dictionary* pFontDict)
+    : CPDF_SimpleFont(pDocument, pFontDict) {}
+
+CPDF_TrueTypeFont::~CPDF_TrueTypeFont() = default;
 
 bool CPDF_TrueTypeFont::IsTrueTypeFont() const {
   return true;
@@ -34,19 +38,20 @@
 }
 
 void CPDF_TrueTypeFont::LoadGlyphMap() {
-  if (!m_Font.GetFace())
+  FXFT_FaceRec* face = m_Font.GetFaceRec();
+  if (!face)
     return;
 
   int baseEncoding = m_BaseEncoding;
-  if (m_pFontFile && m_Font.GetFace()->num_charmaps > 0 &&
+  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(m_Font.GetFace()); i++) {
-      int platform_id = FXFT_Get_Charmap_PlatformID(
-          FXFT_Get_Face_Charmaps(m_Font.GetFace())[i]);
+    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) {
@@ -65,8 +70,8 @@
         baseEncoding == PDFFONT_ENCODING_WINANSI) &&
        m_CharNames.empty()) ||
       FontStyleIsNonSymbolic(m_Flags)) {
-    if (!FXFT_Has_Glyph_Names(m_Font.GetFace()) &&
-        (!m_Font.GetFace()->num_charmaps || !m_Font.GetFace()->charmaps)) {
+    if (!FXFT_Has_Glyph_Names(face) &&
+        (!face->num_charmaps || !face->charmaps)) {
       int nStartChar = m_pFontDict->GetIntegerFor("FirstChar");
       if (nStartChar < 0 || nStartChar > 255)
         return;
@@ -79,48 +84,46 @@
         m_GlyphIndex[charcode] = nGlyph;
       return;
     }
-    bool bMSUnicode = FT_UseTTCharmap(m_Font.GetFace(), 3, 1);
+    bool bMSUnicode = FT_UseTTCharmap(face, 3, 1);
     bool bMacRoman = false;
     bool bMSSymbol = false;
     if (!bMSUnicode) {
       if (FontStyleIsNonSymbolic(m_Flags)) {
-        bMacRoman = FT_UseTTCharmap(m_Font.GetFace(), 1, 0);
-        bMSSymbol = !bMacRoman && FT_UseTTCharmap(m_Font.GetFace(), 3, 0);
+        bMacRoman = FT_UseTTCharmap(face, 1, 0);
+        bMSSymbol = !bMacRoman && FT_UseTTCharmap(face, 3, 0);
       } else {
-        bMSSymbol = FT_UseTTCharmap(m_Font.GetFace(), 3, 0);
-        bMacRoman = !bMSSymbol && FT_UseTTCharmap(m_Font.GetFace(), 1, 0);
+        bMSSymbol = FT_UseTTCharmap(face, 3, 0);
+        bMacRoman = !bMSSymbol && FT_UseTTCharmap(face, 1, 0);
       }
     }
     bool bToUnicode = m_pFontDict->KeyExist("ToUnicode");
-    for (int charcode = 0; charcode < 256; charcode++) {
+    for (uint32_t charcode = 0; charcode < 256; charcode++) {
       const char* name = GetAdobeCharName(baseEncoding, m_CharNames, charcode);
       if (!name) {
         m_GlyphIndex[charcode] =
-            m_pFontFile ? FXFT_Get_Char_Index(m_Font.GetFace(), charcode) : -1;
+            m_pFontFile ? FT_Get_Char_Index(face, charcode) : -1;
         continue;
       }
-      m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
+      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] =
-              FXFT_Get_Char_Index(m_Font.GetFace(), unicode);
+          m_GlyphIndex[charcode] = FT_Get_Char_Index(face, unicode);
           if (m_GlyphIndex[charcode])
             break;
         }
-      } else if (m_Encoding.m_Unicodes[charcode]) {
+      } else if (m_Encoding.UnicodeFromCharCode(charcode)) {
         if (bMSUnicode) {
-          m_GlyphIndex[charcode] = FXFT_Get_Char_Index(
-              m_Font.GetFace(), m_Encoding.m_Unicodes[charcode]);
+          m_GlyphIndex[charcode] =
+              FT_Get_Char_Index(face, m_Encoding.UnicodeFromCharCode(charcode));
         } else if (bMacRoman) {
-          uint32_t maccode = FT_CharCodeFromUnicode(
-              FXFT_ENCODING_APPLE_ROMAN, m_Encoding.m_Unicodes[charcode]);
+          uint32_t maccode =
+              FT_CharCodeFromUnicode(FT_ENCODING_APPLE_ROMAN,
+                                     m_Encoding.UnicodeFromCharCode(charcode));
           if (!maccode) {
-            m_GlyphIndex[charcode] =
-                FXFT_Get_Name_Index(m_Font.GetFace(), (char*)name);
+            m_GlyphIndex[charcode] = FXFT_Get_Name_Index(face, name);
           } else {
-            m_GlyphIndex[charcode] =
-                FXFT_Get_Char_Index(m_Font.GetFace(), maccode);
+            m_GlyphIndex[charcode] = FT_Get_Char_Index(face, maccode);
           }
         }
       }
@@ -129,29 +132,27 @@
         continue;
       }
       if (strcmp(name, ".notdef") == 0) {
-        m_GlyphIndex[charcode] = FXFT_Get_Char_Index(m_Font.GetFace(), 32);
+        m_GlyphIndex[charcode] = FT_Get_Char_Index(face, 32);
         continue;
       }
-      m_GlyphIndex[charcode] =
-          FXFT_Get_Name_Index(m_Font.GetFace(), (char*)name);
+      m_GlyphIndex[charcode] = FXFT_Get_Name_Index(face, name);
       if (m_GlyphIndex[charcode] != 0 || !bToUnicode)
         continue;
 
       WideString wsUnicode = UnicodeFromCharCode(charcode);
       if (!wsUnicode.IsEmpty()) {
-        m_GlyphIndex[charcode] =
-            FXFT_Get_Char_Index(m_Font.GetFace(), wsUnicode[0]);
-        m_Encoding.m_Unicodes[charcode] = wsUnicode[0];
+        m_GlyphIndex[charcode] = FT_Get_Char_Index(face, wsUnicode[0]);
+        m_Encoding.SetUnicode(charcode, wsUnicode[0]);
       }
     }
     return;
   }
-  if (FT_UseTTCharmap(m_Font.GetFace(), 3, 0)) {
+  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] = FXFT_Get_Char_Index(m_Font.GetFace(), unicode);
+        m_GlyphIndex[charcode] = FT_Get_Char_Index(face, unicode);
         if (m_GlyphIndex[charcode]) {
           bFound = true;
           break;
@@ -160,27 +161,28 @@
     }
     if (bFound) {
       if (baseEncoding != PDFFONT_ENCODING_BUILTIN) {
-        for (int charcode = 0; charcode < 256; charcode++) {
+        for (uint32_t charcode = 0; charcode < 256; charcode++) {
           const char* name =
               GetAdobeCharName(baseEncoding, m_CharNames, charcode);
           if (name)
-            m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
+            m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
         }
-      } else if (FT_UseTTCharmap(m_Font.GetFace(), 1, 0)) {
+      } else if (FT_UseTTCharmap(face, 1, 0)) {
         for (int charcode = 0; charcode < 256; charcode++) {
-          m_Encoding.m_Unicodes[charcode] =
-              FT_UnicodeFromCharCode(FXFT_ENCODING_APPLE_ROMAN, charcode);
+          m_Encoding.SetUnicode(
+              charcode,
+              FT_UnicodeFromCharCode(FT_ENCODING_APPLE_ROMAN, charcode));
         }
       }
       return;
     }
   }
-  if (FT_UseTTCharmap(m_Font.GetFace(), 1, 0)) {
+  if (FT_UseTTCharmap(face, 1, 0)) {
     bool bFound = false;
     for (int charcode = 0; charcode < 256; charcode++) {
-      m_GlyphIndex[charcode] = FXFT_Get_Char_Index(m_Font.GetFace(), charcode);
-      m_Encoding.m_Unicodes[charcode] =
-          FT_UnicodeFromCharCode(FXFT_ENCODING_APPLE_ROMAN, 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;
       }
@@ -188,21 +190,21 @@
     if (m_pFontFile || bFound)
       return;
   }
-  if (FXFT_Select_Charmap(m_Font.GetFace(), FXFT_ENCODING_UNICODE) == 0) {
+  if (FXFT_Select_Charmap(face, FT_ENCODING_UNICODE) == 0) {
     bool bFound = false;
     const uint16_t* pUnicodes = PDF_UnicodesForPredefinedCharSet(baseEncoding);
-    for (int charcode = 0; charcode < 256; charcode++) {
+    for (uint32_t charcode = 0; charcode < 256; charcode++) {
       if (m_pFontFile) {
-        m_Encoding.m_Unicodes[charcode] = charcode;
+        m_Encoding.SetUnicode(charcode, charcode);
       } else {
         const char* name = GetAdobeCharName(0, m_CharNames, charcode);
         if (name)
-          m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
+          m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
         else if (pUnicodes)
-          m_Encoding.m_Unicodes[charcode] = pUnicodes[charcode];
+          m_Encoding.SetUnicode(charcode, pUnicodes[charcode]);
       }
-      m_GlyphIndex[charcode] = FXFT_Get_Char_Index(
-          m_Font.GetFace(), m_Encoding.m_Unicodes[charcode]);
+      m_GlyphIndex[charcode] =
+          FT_Get_Char_Index(face, m_Encoding.UnicodeFromCharCode(charcode));
       if (m_GlyphIndex[charcode])
         bFound = true;
     }
diff --git a/core/fpdfapi/font/cpdf_truetypefont.h b/core/fpdfapi/font/cpdf_truetypefont.h
index 6a5e0fc..caa1625 100644
--- a/core/fpdfapi/font/cpdf_truetypefont.h
+++ b/core/fpdfapi/font/cpdf_truetypefont.h
@@ -10,16 +10,21 @@
 #include "core/fpdfapi/font/cpdf_simplefont.h"
 #include "core/fxcrt/fx_system.h"
 
-class CPDF_TrueTypeFont : public CPDF_SimpleFont {
+class CPDF_TrueTypeFont final : public CPDF_SimpleFont {
  public:
-  CPDF_TrueTypeFont();
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  ~CPDF_TrueTypeFont() override;
 
   // CPDF_Font:
   bool IsTrueTypeFont() const override;
   const CPDF_TrueTypeFont* AsTrueTypeFont() const override;
   CPDF_TrueTypeFont* AsTrueTypeFont() override;
 
- protected:
+ private:
+  CPDF_TrueTypeFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+
   // CPDF_Font:
   bool Load() override;
 
diff --git a/core/fpdfapi/font/cpdf_type1font.cpp b/core/fpdfapi/font/cpdf_type1font.cpp
index 77987bb..04dd7c2 100644
--- a/core/fpdfapi/font/cpdf_type1font.cpp
+++ b/core/fpdfapi/font/cpdf_type1font.cpp
@@ -8,20 +8,23 @@
 
 #include <algorithm>
 
+#include "build/build_config.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/fx_font.h"
 #include "core/fxge/fx_freetype.h"
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
 #include "core/fxge/apple/apple_int.h"
 #endif
 
 namespace {
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
 struct GlyphNameMap {
-  const char* m_pStrAdobe;
-  const char* m_pStrUnicode;
+  const char* m_pStrAdobe;    // Raw, POD struct.
+  const char* m_pStrUnicode;  // Raw, POD struct.
 };
 
 const GlyphNameMap g_GlyphNameSubsts[] = {{"ff", "uniFB00"},
@@ -38,29 +41,37 @@
   return nullptr;
 }
 
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#endif  // defined(OS_MACOSX)
 
-bool FT_UseType1Charmap(FXFT_Face face) {
+bool FT_UseType1Charmap(FXFT_FaceRec* face) {
   if (FXFT_Get_Face_CharmapCount(face) == 0) {
     return false;
   }
   if (FXFT_Get_Face_CharmapCount(face) == 1 &&
       FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[0]) ==
-          FXFT_ENCODING_UNICODE) {
+          FT_ENCODING_UNICODE) {
     return false;
   }
   if (FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[0]) ==
-      FXFT_ENCODING_UNICODE) {
-    FXFT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[1]);
+      FT_ENCODING_UNICODE) {
+    FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[1]);
   } else {
-    FXFT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]);
+    FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]);
   }
   return true;
 }
 
 }  // namespace
 
-CPDF_Type1Font::CPDF_Type1Font() : m_Base14Font(-1) {}
+CPDF_Type1Font::CPDF_Type1Font(CPDF_Document* pDocument,
+                               CPDF_Dictionary* pFontDict)
+    : CPDF_SimpleFont(pDocument, pFontDict) {
+#if defined(OS_MACOSX)
+  memset(m_ExtGID, 0xff, sizeof(m_ExtGID));
+#endif
+}
+
+CPDF_Type1Font::~CPDF_Type1Font() = default;
 
 bool CPDF_Type1Font::IsType1Font() const {
   return true;
@@ -75,68 +86,70 @@
 }
 
 bool CPDF_Type1Font::Load() {
-  m_Base14Font = PDF_GetStandardFontName(&m_BaseFont);
-  if (m_Base14Font >= 0) {
-    CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
-    if (pFontDesc && pFontDesc->KeyExist("Flags"))
-      m_Flags = pFontDesc->GetIntegerFor("Flags");
-    else
-      m_Flags = m_Base14Font >= 12 ? FXFONT_SYMBOLIC : FXFONT_NONSYMBOLIC;
+  m_Base14Font = CFX_FontMapper::GetStandardFontName(&m_BaseFontName);
+  if (!IsBase14Font())
+    return LoadCommon();
 
-    if (m_Base14Font < 4) {
-      for (int i = 0; i < 256; i++)
-        m_CharWidth[i] = 600;
-    }
-    if (m_Base14Font == 12)
-      m_BaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL;
-    else if (m_Base14Font == 13)
-      m_BaseEncoding = PDFFONT_ENCODING_ZAPFDINGBATS;
-    else if (FontStyleIsNonSymbolic(m_Flags))
-      m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
+  const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor");
+  if (pFontDesc && pFontDesc->KeyExist("Flags")) {
+    m_Flags = pFontDesc->GetIntegerFor("Flags");
+  } else if (IsSymbolicFont()) {
+    m_Flags = FXFONT_SYMBOLIC;
+  } else {
+    m_Flags = FXFONT_NONSYMBOLIC;
   }
+  if (IsFixedFont()) {
+    for (int i = 0; i < 256; i++)
+      m_CharWidth[i] = 600;
+  }
+  if (m_Base14Font == CFX_FontMapper::kSymbol)
+    m_BaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL;
+  else if (m_Base14Font == CFX_FontMapper::kDingbats)
+    m_BaseEncoding = PDFFONT_ENCODING_ZAPFDINGBATS;
+  else if (FontStyleIsNonSymbolic(m_Flags))
+    m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
   return LoadCommon();
 }
 
+#if defined(OS_MACOSX)
 int CPDF_Type1Font::GlyphFromCharCodeExt(uint32_t charcode) {
-  if (charcode > 0xff) {
+  if (charcode > 0xff)
     return -1;
-  }
-  int index = m_ExtGID[(uint8_t)charcode];
-  if (index == 0xffff) {
-    return -1;
-  }
-  return index;
+
+  int index = m_ExtGID[static_cast<uint8_t>(charcode)];
+  return index != 0xffff ? index : -1;
 }
+#endif
 
 void CPDF_Type1Font::LoadGlyphMap() {
-  if (!m_Font.GetFace())
+  if (!m_Font.GetFaceRec())
     return;
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
   bool bCoreText = true;
   CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatformData())
+      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
           ->m_quartz2d;
   if (!m_Font.GetPlatformFont()) {
     if (m_Font.GetPsName() == "DFHeiStd-W5")
       bCoreText = false;
 
-    m_Font.SetPlatformFont(
-        quartz2d.CreateFont(m_Font.GetFontData(), m_Font.GetSize()));
+    pdfium::span<const uint8_t> span = m_Font.GetFontSpan();
+    m_Font.SetPlatformFont(quartz2d.CreateFont(span.data(), span.size()));
     if (!m_Font.GetPlatformFont())
       bCoreText = false;
   }
 #endif
-  if (!IsEmbedded() && (m_Base14Font < 12) && m_Font.IsTTFont()) {
-    if (FT_UseTTCharmap(m_Font.GetFace(), 3, 0)) {
+  if (!IsEmbedded() && !IsSymbolicFont() && m_Font.IsTTFont()) {
+    if (FT_UseTTCharmap(m_Font.GetFaceRec(), 3, 0)) {
       bool bGotOne = false;
-      for (int charcode = 0; charcode < 256; charcode++) {
+      for (uint32_t charcode = 0; charcode < 256; 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] =
-              FXFT_Get_Char_Index(m_Font.GetFace(), unicode);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+              FT_Get_Char_Index(m_Font.GetFaceRec(), unicode);
+#if defined(OS_MACOSX)
           CalcExtGID(charcode);
 #endif
           if (m_GlyphIndex[charcode]) {
@@ -146,58 +159,58 @@
         }
       }
       if (bGotOne) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
         if (!bCoreText)
           memcpy(m_ExtGID, m_GlyphIndex, 256);
 #endif
         return;
       }
     }
-    FXFT_Select_Charmap(m_Font.GetFace(), FXFT_ENCODING_UNICODE);
+    FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE);
     if (m_BaseEncoding == 0)
       m_BaseEncoding = PDFFONT_ENCODING_STANDARD;
 
-    for (int charcode = 0; charcode < 256; charcode++) {
+    for (uint32_t charcode = 0; charcode < 256; charcode++) {
       const char* name =
           GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
       if (!name)
         continue;
 
-      m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
-      m_GlyphIndex[charcode] = FXFT_Get_Char_Index(
-          m_Font.GetFace(), m_Encoding.m_Unicodes[charcode]);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+      m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
+      m_GlyphIndex[charcode] = FT_Get_Char_Index(
+          m_Font.GetFaceRec(), m_Encoding.UnicodeFromCharCode(charcode));
+#if defined(OS_MACOSX)
       CalcExtGID(charcode);
 #endif
       if (m_GlyphIndex[charcode] == 0 && strcmp(name, ".notdef") == 0) {
-        m_Encoding.m_Unicodes[charcode] = 0x20;
-        m_GlyphIndex[charcode] = FXFT_Get_Char_Index(m_Font.GetFace(), 0x20);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+        m_Encoding.SetUnicode(charcode, 0x20);
+        m_GlyphIndex[charcode] = FT_Get_Char_Index(m_Font.GetFaceRec(), 0x20);
+#if defined(OS_MACOSX)
         CalcExtGID(charcode);
 #endif
       }
     }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
     if (!bCoreText)
       memcpy(m_ExtGID, m_GlyphIndex, 256);
 #endif
     return;
   }
-  FT_UseType1Charmap(m_Font.GetFace());
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+  FT_UseType1Charmap(m_Font.GetFaceRec());
+#if defined(OS_MACOSX)
   if (bCoreText) {
     if (FontStyleIsSymbolic(m_Flags)) {
-      for (int charcode = 0; charcode < 256; charcode++) {
+      for (uint32_t charcode = 0; charcode < 256; charcode++) {
         const char* name =
             GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
         if (name) {
-          m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
+          m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
           m_GlyphIndex[charcode] =
-              FXFT_Get_Name_Index(m_Font.GetFace(), const_cast<char*>(name));
+              FXFT_Get_Name_Index(m_Font.GetFaceRec(), name);
           SetExtGID(name, charcode);
         } else {
           m_GlyphIndex[charcode] =
-              FXFT_Get_Char_Index(m_Font.GetFace(), charcode);
+              FT_Get_Char_Index(m_Font.GetFaceRec(), charcode);
           wchar_t unicode = 0;
           if (m_GlyphIndex[charcode]) {
             unicode =
@@ -205,13 +218,13 @@
           }
           char name_glyph[256];
           memset(name_glyph, 0, sizeof(name_glyph));
-          FXFT_Get_Glyph_Name(m_Font.GetFace(), m_GlyphIndex[charcode],
-                              name_glyph, 256);
+          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);
 
-          m_Encoding.m_Unicodes[charcode] = unicode;
+          m_Encoding.SetUnicode(charcode, unicode);
           SetExtGID(name_glyph, charcode);
         }
       }
@@ -219,68 +232,65 @@
     }
 
     bool bUnicode =
-        FXFT_Select_Charmap(m_Font.GetFace(), FXFT_ENCODING_UNICODE) == 0;
-    for (int charcode = 0; charcode < 256; charcode++) {
+        FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE) == 0;
+    for (uint32_t charcode = 0; charcode < 256; charcode++) {
       const char* name =
           GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
       if (!name)
         continue;
 
-      m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
+      m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
       const char* pStrUnicode = GlyphNameRemap(name);
-      if (pStrUnicode &&
-          FXFT_Get_Name_Index(m_Font.GetFace(), const_cast<char*>(name)) == 0) {
+      if (pStrUnicode && FXFT_Get_Name_Index(m_Font.GetFaceRec(), name) == 0) {
         name = pStrUnicode;
       }
-      m_GlyphIndex[charcode] =
-          FXFT_Get_Name_Index(m_Font.GetFace(), const_cast<char*>(name));
+      m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name);
       SetExtGID(name, charcode);
       if (m_GlyphIndex[charcode] != 0)
         continue;
 
       if (strcmp(name, ".notdef") != 0 && strcmp(name, "space") != 0) {
-        m_GlyphIndex[charcode] = FXFT_Get_Char_Index(
-            m_Font.GetFace(),
-            bUnicode ? m_Encoding.m_Unicodes[charcode] : charcode);
+        m_GlyphIndex[charcode] = FT_Get_Char_Index(
+            m_Font.GetFaceRec(),
+            bUnicode ? m_Encoding.UnicodeFromCharCode(charcode) : charcode);
         CalcExtGID(charcode);
       } else {
-        m_Encoding.m_Unicodes[charcode] = 0x20;
+        m_Encoding.SetUnicode(charcode, 0x20);
         m_GlyphIndex[charcode] =
-            bUnicode ? FXFT_Get_Char_Index(m_Font.GetFace(), 0x20) : 0xffff;
+            bUnicode ? FT_Get_Char_Index(m_Font.GetFaceRec(), 0x20) : 0xffff;
         CalcExtGID(charcode);
       }
     }
     return;
   }
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#endif  // defined(OS_MACOSX)
   if (FontStyleIsSymbolic(m_Flags)) {
     for (int charcode = 0; charcode < 256; charcode++) {
       const char* name =
           GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);
       if (name) {
-        m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
-        m_GlyphIndex[charcode] =
-            FXFT_Get_Name_Index(m_Font.GetFace(), const_cast<char*>(name));
+        m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
+        m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name);
       } else {
         m_GlyphIndex[charcode] =
-            FXFT_Get_Char_Index(m_Font.GetFace(), charcode);
+            FT_Get_Char_Index(m_Font.GetFaceRec(), 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));
-            FXFT_Get_Glyph_Name(m_Font.GetFace(), m_GlyphIndex[charcode],
-                                name_glyph, 256);
+            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);
           }
-          m_Encoding.m_Unicodes[charcode] = unicode;
+          m_Encoding.SetUnicode(charcode, unicode);
         }
       }
     }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
     if (!bCoreText)
       memcpy(m_ExtGID, m_GlyphIndex, 256);
 
@@ -289,35 +299,44 @@
   }
 
   bool bUnicode =
-      FXFT_Select_Charmap(m_Font.GetFace(), FXFT_ENCODING_UNICODE) == 0;
+      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);
     if (!name)
       continue;
 
-    m_Encoding.m_Unicodes[charcode] = PDF_UnicodeFromAdobeName(name);
-    m_GlyphIndex[charcode] =
-        FXFT_Get_Name_Index(m_Font.GetFace(), const_cast<char*>(name));
+    m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name));
+    m_GlyphIndex[charcode] = FXFT_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] = FXFT_Get_Char_Index(
-          m_Font.GetFace(),
-          bUnicode ? m_Encoding.m_Unicodes[charcode] : charcode);
+      m_GlyphIndex[charcode] = FT_Get_Char_Index(
+          m_Font.GetFaceRec(),
+          bUnicode ? m_Encoding.UnicodeFromCharCode(charcode) : charcode);
     } else {
-      m_Encoding.m_Unicodes[charcode] = 0x20;
+      m_Encoding.SetUnicode(charcode, 0x20);
       m_GlyphIndex[charcode] = 0xffff;
     }
   }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
   if (!bCoreText)
     memcpy(m_ExtGID, m_GlyphIndex, 256);
 #endif
 }
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-void CPDF_Type1Font::SetExtGID(const char* name, int charcode) {
+bool CPDF_Type1Font::IsSymbolicFont() const {
+  return m_Base14Font.has_value() &&
+         CFX_FontMapper::IsSymbolicFont(m_Base14Font.value());
+}
+
+bool CPDF_Type1Font::IsFixedFont() const {
+  return m_Base14Font.has_value() &&
+         CFX_FontMapper::IsFixedFont(m_Base14Font.value());
+}
+
+#if defined(OS_MACOSX)
+void CPDF_Type1Font::SetExtGID(const char* name, uint32_t charcode) {
   CFStringRef name_ct = CFStringCreateWithCStringNoCopy(
       kCFAllocatorDefault, name, kCFStringEncodingASCII, kCFAllocatorNull);
   m_ExtGID[charcode] =
@@ -326,11 +345,11 @@
     CFRelease(name_ct);
 }
 
-void CPDF_Type1Font::CalcExtGID(int charcode) {
+void CPDF_Type1Font::CalcExtGID(uint32_t charcode) {
   char name_glyph[256];
-  FXFT_Get_Glyph_Name(m_Font.GetFace(), m_GlyphIndex[charcode], name_glyph,
-                      256);
+  FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode], name_glyph,
+                    256);
   name_glyph[255] = 0;
   SetExtGID(name_glyph, charcode);
 }
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#endif  // defined(OS_MACOSX)
diff --git a/core/fpdfapi/font/cpdf_type1font.h b/core/fpdfapi/font/cpdf_type1font.h
index 76c4962..79dfe31 100644
--- a/core/fpdfapi/font/cpdf_type1font.h
+++ b/core/fpdfapi/font/cpdf_type1font.h
@@ -7,34 +7,48 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_
 #define CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_
 
+#include "build/build_config.h"
 #include "core/fpdfapi/font/cpdf_simplefont.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_fontmapper.h"
 
-class CPDF_Type1Font : public CPDF_SimpleFont {
+class CPDF_Type1Font final : public CPDF_SimpleFont {
  public:
-  CPDF_Type1Font();
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  ~CPDF_Type1Font() override;
 
   // CPDF_Font:
   bool IsType1Font() const override;
   const CPDF_Type1Font* AsType1Font() const override;
   CPDF_Type1Font* AsType1Font() override;
+#if defined(OS_MACOSX)
   int GlyphFromCharCodeExt(uint32_t charcode) override;
+#endif
 
-  int GetBase14Font() const { return m_Base14Font; }
+  bool IsBase14Font() const { return m_Base14Font.has_value(); }
 
  private:
+  CPDF_Type1Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict);
+
   // CPDF_Font:
   bool Load() override;
 
   // CPDF_SimpleFont:
   void LoadGlyphMap() override;
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  void SetExtGID(const char* name, int charcode);
-  void CalcExtGID(int charcode);
+  bool IsSymbolicFont() const;
+  bool IsFixedFont() const;
+
+#if defined(OS_MACOSX)
+  void SetExtGID(const char* name, uint32_t charcode);
+  void CalcExtGID(uint32_t charcode);
+
+  uint16_t m_ExtGID[256];
 #endif
 
-  int m_Base14Font;
+  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 8a89582..3ad9bba 100644
--- a/core/fpdfapi/font/cpdf_type3char.cpp
+++ b/core/fpdfapi/font/cpdf_type3char.cpp
@@ -8,12 +8,9 @@
 
 #include <utility>
 
-#include "core/fpdfapi/page/cpdf_form.h"
-#include "core/fpdfapi/page/cpdf_image.h"
-#include "core/fpdfapi/page/cpdf_imageobject.h"
-#include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_dib.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -21,10 +18,9 @@
 
 }  // namespace
 
-CPDF_Type3Char::CPDF_Type3Char(std::unique_ptr<CPDF_Form> pForm)
-    : m_pForm(std::move(pForm)) {}
+CPDF_Type3Char::CPDF_Type3Char() = default;
 
-CPDF_Type3Char::~CPDF_Type3Char() {}
+CPDF_Type3Char::~CPDF_Type3Char() = default;
 
 // static
 float CPDF_Type3Char::TextUnitToGlyphUnit(float fTextUnit) {
@@ -36,33 +32,18 @@
   pRect->Scale(kTextUnitInGlyphUnit);
 }
 
-bool CPDF_Type3Char::LoadBitmap(CPDF_RenderContext* pContext) {
+bool CPDF_Type3Char::LoadBitmapFromSoleImageOfForm() {
   if (m_pBitmap || !m_pForm)
     return true;
 
-  if (m_pForm->GetPageObjectList()->size() != 1 || m_bColored)
+  if (m_bColored)
     return false;
 
-  auto& pPageObj = m_pForm->GetPageObjectList()->front();
-  if (!pPageObj->IsImage())
+  auto result = m_pForm->GetBitmapAndMatrixFromSoleImageOfForm();
+  if (!result.has_value())
     return false;
 
-  m_ImageMatrix = pPageObj->AsImage()->matrix();
-  {
-    // |pSource| actually gets assigned a CPDF_DIBSource, which has pointers
-    // into objects owned by |m_pForm|. Make sure it is out of scope before
-    // clearing the form.
-    RetainPtr<CFX_DIBSource> pSource =
-        pPageObj->AsImage()->GetImage()->LoadDIBSource();
-
-    // Clone() is non-virtual, and can't be overloaded by CPDF_DIBSource to
-    // return a clone of the subclass as one would typically expect from a
-    // such a method. Instead, it only clones the CFX_DIBSource, none of whose
-    // members point to objects owned by the form. As a result, |m_pBitmap|
-    // may outlive |m_pForm|.
-    if (pSource)
-      m_pBitmap = pSource->Clone(nullptr);
-  }
+  std::tie(m_pBitmap, m_ImageMatrix) = result.value();
   m_pForm.reset();
   return true;
 }
@@ -70,19 +51,25 @@
 void CPDF_Type3Char::InitializeFromStreamData(bool bColored,
                                               const float* pData) {
   m_bColored = bColored;
-  m_Width = FXSYS_round(TextUnitToGlyphUnit(pData[0]));
-  m_BBox.left = FXSYS_round(TextUnitToGlyphUnit(pData[2]));
-  m_BBox.bottom = FXSYS_round(TextUnitToGlyphUnit(pData[3]));
-  m_BBox.right = FXSYS_round(TextUnitToGlyphUnit(pData[4]));
-  m_BBox.top = FXSYS_round(TextUnitToGlyphUnit(pData[5]));
+  m_Width = FXSYS_roundf(TextUnitToGlyphUnit(pData[0]));
+  m_BBox.left = FXSYS_roundf(TextUnitToGlyphUnit(pData[2]));
+  m_BBox.bottom = FXSYS_roundf(TextUnitToGlyphUnit(pData[3]));
+  m_BBox.right = FXSYS_roundf(TextUnitToGlyphUnit(pData[4]));
+  m_BBox.top = FXSYS_roundf(TextUnitToGlyphUnit(pData[5]));
 }
 
-void CPDF_Type3Char::Transform(const CFX_Matrix& matrix) {
+void CPDF_Type3Char::WillBeDestroyed() {
+  // Break cycles.
+  m_pForm.reset();
+}
+
+void CPDF_Type3Char::Transform(CPDF_Font::FormIface* pForm,
+                               const CFX_Matrix& matrix) {
   m_Width = m_Width * matrix.GetXUnit() + 0.5f;
 
   CFX_FloatRect char_rect;
   if (m_BBox.right <= m_BBox.left || m_BBox.bottom >= m_BBox.top) {
-    char_rect = form()->CalcBoundingBox();
+    char_rect = pForm->CalcBoundingBox();
     TextUnitRectToGlyphUnitRect(&char_rect);
   } else {
     char_rect = CFX_FloatRect(m_BBox);
@@ -91,8 +78,8 @@
   m_BBox = matrix.TransformRect(char_rect).ToRoundedFxRect();
 }
 
-void CPDF_Type3Char::ResetForm() {
-  m_pForm.reset();
+void CPDF_Type3Char::SetForm(std::unique_ptr<CPDF_Font::FormIface> pForm) {
+  m_pForm = std::move(pForm);
 }
 
 RetainPtr<CFX_DIBitmap> CPDF_Type3Char::GetBitmap() {
diff --git a/core/fpdfapi/font/cpdf_type3char.h b/core/fpdfapi/font/cpdf_type3char.h
index c9c5555..ca83452 100644
--- a/core/fpdfapi/font/cpdf_type3char.h
+++ b/core/fpdfapi/font/cpdf_type3char.h
@@ -8,45 +8,45 @@
 #define CORE_FPDFAPI_FONT_CPDF_TYPE3CHAR_H_
 
 #include <memory>
+#include <utility>
 
+#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"
 
 class CFX_DIBitmap;
-class CPDF_Form;
-class CPDF_RenderContext;
 
 class CPDF_Type3Char {
  public:
-  explicit CPDF_Type3Char(std::unique_ptr<CPDF_Form> pForm);
+  CPDF_Type3Char();
   ~CPDF_Type3Char();
 
   static float TextUnitToGlyphUnit(float fTextUnit);
   static void TextUnitRectToGlyphUnitRect(CFX_FloatRect* pRect);
 
-  bool LoadBitmap(CPDF_RenderContext* pContext);
-
+  bool LoadBitmapFromSoleImageOfForm();
   void InitializeFromStreamData(bool bColored, const float* pData);
-  void Transform(const CFX_Matrix& matrix);
-  void ResetForm();
+  void Transform(CPDF_Font::FormIface* pForm, const CFX_Matrix& matrix);
+  void WillBeDestroyed();
 
   RetainPtr<CFX_DIBitmap> GetBitmap();
   const RetainPtr<CFX_DIBitmap>& GetBitmap() const;
 
-  const CPDF_Form* form() const { return m_pForm.get(); }
-  CPDF_Form* form() { return m_pForm.get(); }
-
   bool colored() const { return m_bColored; }
-  int width() const { return m_Width; }
+  uint32_t width() const { return m_Width; }
   const CFX_Matrix& matrix() const { return m_ImageMatrix; }
   const FX_RECT& bbox() const { return m_BBox; }
 
+  const CPDF_Font::FormIface* form() const { return m_pForm.get(); }
+  void SetForm(std::unique_ptr<CPDF_Font::FormIface> pForm);
+
  private:
-  std::unique_ptr<CPDF_Form> m_pForm;
+  std::unique_ptr<CPDF_Font::FormIface> m_pForm;
   RetainPtr<CFX_DIBitmap> m_pBitmap;
   bool m_bColored = false;
-  int m_Width = 0;
+  uint32_t 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 c328c10..eae08ea 100644
--- a/core/fpdfapi/font/cpdf_type3font.cpp
+++ b/core/fpdfapi/font/cpdf_type3font.cpp
@@ -10,12 +10,12 @@
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_type3char.h"
-#include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/autorestorer.h"
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -23,11 +23,15 @@
 
 }  // namespace
 
-CPDF_Type3Font::CPDF_Type3Font() {
+CPDF_Type3Font::CPDF_Type3Font(CPDF_Document* pDocument,
+                               CPDF_Dictionary* pFontDict,
+                               FormFactoryIface* pFormFactory)
+    : CPDF_SimpleFont(pDocument, pFontDict), m_pFormFactory(pFormFactory) {
+  ASSERT(GetDocument());
   memset(m_CharWidthL, 0, sizeof(m_CharWidthL));
 }
 
-CPDF_Type3Font::~CPDF_Type3Font() {}
+CPDF_Type3Font::~CPDF_Type3Font() = default;
 
 bool CPDF_Type3Font::IsType3Font() const {
   return true;
@@ -41,9 +45,18 @@
   return this;
 }
 
+void CPDF_Type3Font::WillBeDestroyed() {
+  // Last reference to |this| may be through one of its CPDF_Type3Chars.
+  RetainPtr<CPDF_Font> protector(this);
+  for (const auto& item : m_CacheMap) {
+    if (item.second)
+      item.second->WillBeDestroyed();
+  }
+}
+
 bool CPDF_Type3Font::Load() {
-  m_pFontResources = m_pFontDict->GetDictFor("Resources");
-  CPDF_Array* pMatrix = m_pFontDict->GetArrayFor("FontMatrix");
+  m_pFontResources.Reset(m_pFontDict->GetDictFor("Resources"));
+  const CPDF_Array* pMatrix = m_pFontDict->GetArrayFor("FontMatrix");
   float xscale = 1.0f;
   float yscale = 1.0f;
   if (pMatrix) {
@@ -52,7 +65,7 @@
     yscale = m_FontMatrix.d;
   }
 
-  CPDF_Array* pBBox = m_pFontDict->GetArrayFor("FontBBox");
+  const CPDF_Array* pBBox = m_pFontDict->GetArrayFor("FontBBox");
   if (pBBox) {
     CFX_FloatRect box(
         pBBox->GetNumberAt(0) * xscale, pBBox->GetNumberAt(1) * yscale,
@@ -64,21 +77,20 @@
   static constexpr size_t kCharLimit = FX_ArraySize(m_CharWidthL);
   int StartChar = m_pFontDict->GetIntegerFor("FirstChar");
   if (StartChar >= 0 && static_cast<size_t>(StartChar) < kCharLimit) {
-    CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths");
+    const CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths");
     if (pWidthArray) {
-      size_t count = std::min(pWidthArray->GetCount(), kCharLimit);
+      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_round(CPDF_Type3Char::TextUnitToGlyphUnit(
+            FXSYS_roundf(CPDF_Type3Char::TextUnitToGlyphUnit(
                 pWidthArray->GetNumberAt(i) * xscale));
       }
     }
   }
-  m_pCharProcs = m_pFontDict->GetDictFor("CharProcs");
-  CPDF_Object* pEncoding = m_pFontDict->GetDirectObjectFor("Encoding");
-  if (pEncoding)
-    LoadPDFEncoding(pEncoding, m_BaseEncoding, &m_CharNames, false, false);
+  m_pCharProcs.Reset(m_pFontDict->GetDictFor("CharProcs"));
+  if (m_pFontDict->GetDirectObjectFor("Encoding"))
+    LoadPDFEncoding(false, false);
   return true;
 }
 
@@ -100,36 +112,42 @@
   if (!name)
     return nullptr;
 
-  CPDF_Stream* pStream =
-      ToStream(m_pCharProcs ? m_pCharProcs->GetDirectObjectFor(name) : nullptr);
+  if (!m_pCharProcs)
+    return nullptr;
+
+  CPDF_Stream* pStream = ToStream(m_pCharProcs->GetDirectObjectFor(name));
   if (!pStream)
     return nullptr;
 
-  auto pNewChar =
-      pdfium::MakeUnique<CPDF_Type3Char>(pdfium::MakeUnique<CPDF_Form>(
-          m_pDocument.Get(),
-          m_pFontResources ? m_pFontResources.Get() : m_pPageResources.Get(),
-          pStream, nullptr));
+  std::unique_ptr<CPDF_Font::FormIface> pForm = m_pFormFactory->CreateForm(
+      m_pDocument.Get(),
+      m_pFontResources ? m_pFontResources.Get() : m_pPageResources.Get(),
+      pStream);
+
+  auto pNewChar = pdfium::MakeUnique<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
   // a cache hit.
-  m_CharLoadingDepth++;
-  pNewChar->form()->ParseContentWithParams(nullptr, nullptr, pNewChar.get(), 0);
-  m_CharLoadingDepth--;
+  {
+    AutoRestorer<int> restorer(&m_CharLoadingDepth);
+    m_CharLoadingDepth++;
+    pForm->ParseContentForType3Char(pNewChar.get());
+  }
   it = m_CacheMap.find(charcode);
   if (it != m_CacheMap.end())
     return it->second.get();
 
-  pNewChar->Transform(m_FontMatrix);
+  pNewChar->Transform(pForm.get(), m_FontMatrix);
+  if (pForm->HasPageObjects())
+    pNewChar->SetForm(std::move(pForm));
+
+  CPDF_Type3Char* pCachedChar = pNewChar.get();
   m_CacheMap[charcode] = std::move(pNewChar);
-  CPDF_Type3Char* pCachedChar = m_CacheMap[charcode].get();
-  if (pCachedChar->form()->GetPageObjectList()->empty())
-    pCachedChar->ResetForm();
   return pCachedChar;
 }
 
-int CPDF_Type3Font::GetCharWidthF(uint32_t charcode) {
+uint32_t CPDF_Type3Font::GetCharWidthF(uint32_t charcode) {
   if (charcode >= FX_ArraySize(m_CharWidthL))
     charcode = 0;
 
diff --git a/core/fpdfapi/font/cpdf_type3font.h b/core/fpdfapi/font/cpdf_type3font.h
index 3f2e018..1ef469b 100644
--- a/core/fpdfapi/font/cpdf_type3font.h
+++ b/core/fpdfapi/font/cpdf_type3font.h
@@ -13,47 +13,56 @@
 #include "core/fpdfapi/font/cpdf_simplefont.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Dictionary;
+class CPDF_Document;
+class CPDF_Stream;
 class CPDF_Type3Char;
 
-class CPDF_Type3Font : public CPDF_SimpleFont {
+class CPDF_Type3Font final : public CPDF_SimpleFont {
  public:
-  CPDF_Type3Font();
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_Type3Font() override;
 
   // CPDF_Font:
   bool IsType3Font() const override;
   const CPDF_Type3Font* AsType3Font() const override;
   CPDF_Type3Font* AsType3Font() override;
-  int GetCharWidthF(uint32_t charcode) override;
+  void WillBeDestroyed() override;
+  uint32_t GetCharWidthF(uint32_t charcode) override;
   FX_RECT GetCharBBox(uint32_t charcode) override;
 
   void SetPageResources(CPDF_Dictionary* pResources) {
-    m_pPageResources = pResources;
+    m_pPageResources.Reset(pResources);
   }
   CPDF_Type3Char* LoadChar(uint32_t charcode);
   void CheckType3FontMetrics();
 
   CFX_Matrix& GetFontMatrix() { return m_FontMatrix; }
 
- protected:
-  CFX_Matrix m_FontMatrix;
-
  private:
+  CPDF_Type3Font(CPDF_Document* pDocument,
+                 CPDF_Dictionary* pFontDict,
+                 FormFactoryIface* pFormFactory);
+
   // CPDF_Font:
   bool Load() override;
 
   // CPDF_SimpleFont:
   void LoadGlyphMap() override;
 
-  int m_CharWidthL[256];
-  UnownedPtr<CPDF_Dictionary> m_pCharProcs;
-  UnownedPtr<CPDF_Dictionary> m_pPageResources;
-  UnownedPtr<CPDF_Dictionary> m_pFontResources;
-  std::map<uint32_t, std::unique_ptr<CPDF_Type3Char>> m_CacheMap;
   // The depth char loading is in, to avoid recurive calling LoadChar().
   int m_CharLoadingDepth = 0;
+  CFX_Matrix m_FontMatrix;
+  UnownedPtr<FormFactoryIface> const m_pFormFactory;
+  RetainPtr<CPDF_Dictionary> m_pCharProcs;
+  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];
 };
 
 #endif  // CORE_FPDFAPI_FONT_CPDF_TYPE3FONT_H_
diff --git a/core/fpdfapi/page/Android.bp b/core/fpdfapi/page/Android.bp
new file mode 100644
index 0000000..650e4f8
--- /dev/null
+++ b/core/fpdfapi/page/Android.bp
@@ -0,0 +1,22 @@
+cc_library_static {
+    name: "libpdfium-page",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-fxcodec",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+        "libpdfium-font",
+        "libpdfium-parser",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/core/fpdfapi/page/BUILD.gn b/core/fpdfapi/page/BUILD.gn
new file mode 100644
index 0000000..2ab0b72
--- /dev/null
+++ b/core/fpdfapi/page/BUILD.gn
@@ -0,0 +1,128 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+source_set("page") {
+  sources = [
+    "cpdf_allstates.cpp",
+    "cpdf_allstates.h",
+    "cpdf_annotcontext.cpp",
+    "cpdf_annotcontext.h",
+    "cpdf_clippath.cpp",
+    "cpdf_clippath.h",
+    "cpdf_color.cpp",
+    "cpdf_color.h",
+    "cpdf_colorspace.cpp",
+    "cpdf_colorspace.h",
+    "cpdf_colorstate.cpp",
+    "cpdf_colorstate.h",
+    "cpdf_contentmarkitem.cpp",
+    "cpdf_contentmarkitem.h",
+    "cpdf_contentmarks.cpp",
+    "cpdf_contentmarks.h",
+    "cpdf_contentparser.cpp",
+    "cpdf_contentparser.h",
+    "cpdf_devicecs.cpp",
+    "cpdf_devicecs.h",
+    "cpdf_dib.cpp",
+    "cpdf_dib.h",
+    "cpdf_docpagedata.cpp",
+    "cpdf_docpagedata.h",
+    "cpdf_expintfunc.cpp",
+    "cpdf_expintfunc.h",
+    "cpdf_form.cpp",
+    "cpdf_form.h",
+    "cpdf_formobject.cpp",
+    "cpdf_formobject.h",
+    "cpdf_function.cpp",
+    "cpdf_function.h",
+    "cpdf_generalstate.cpp",
+    "cpdf_generalstate.h",
+    "cpdf_graphicstates.cpp",
+    "cpdf_graphicstates.h",
+    "cpdf_iccprofile.cpp",
+    "cpdf_iccprofile.h",
+    "cpdf_image.cpp",
+    "cpdf_image.h",
+    "cpdf_imageobject.cpp",
+    "cpdf_imageobject.h",
+    "cpdf_meshstream.cpp",
+    "cpdf_meshstream.h",
+    "cpdf_occontext.cpp",
+    "cpdf_occontext.h",
+    "cpdf_page.cpp",
+    "cpdf_page.h",
+    "cpdf_pagemodule.cpp",
+    "cpdf_pagemodule.h",
+    "cpdf_pageobject.cpp",
+    "cpdf_pageobject.h",
+    "cpdf_pageobjectholder.cpp",
+    "cpdf_pageobjectholder.h",
+    "cpdf_path.cpp",
+    "cpdf_path.h",
+    "cpdf_pathobject.cpp",
+    "cpdf_pathobject.h",
+    "cpdf_pattern.cpp",
+    "cpdf_pattern.h",
+    "cpdf_patterncs.cpp",
+    "cpdf_patterncs.h",
+    "cpdf_psengine.cpp",
+    "cpdf_psengine.h",
+    "cpdf_psfunc.cpp",
+    "cpdf_psfunc.h",
+    "cpdf_sampledfunc.cpp",
+    "cpdf_sampledfunc.h",
+    "cpdf_shadingobject.cpp",
+    "cpdf_shadingobject.h",
+    "cpdf_shadingpattern.cpp",
+    "cpdf_shadingpattern.h",
+    "cpdf_stitchfunc.cpp",
+    "cpdf_stitchfunc.h",
+    "cpdf_streamcontentparser.cpp",
+    "cpdf_streamcontentparser.h",
+    "cpdf_streamparser.cpp",
+    "cpdf_streamparser.h",
+    "cpdf_textobject.cpp",
+    "cpdf_textobject.h",
+    "cpdf_textstate.cpp",
+    "cpdf_textstate.h",
+    "cpdf_tilingpattern.cpp",
+    "cpdf_tilingpattern.h",
+    "cpdf_transferfunc.cpp",
+    "cpdf_transferfunc.h",
+    "cpdf_transferfuncdib.cpp",
+    "cpdf_transferfuncdib.h",
+    "cpdf_transparency.cpp",
+    "cpdf_transparency.h",
+    "ipdf_page.h",
+  ]
+  configs += [ "../../../:pdfium_core_config" ]
+  deps = [
+    "../../../constants",
+    "../../fxcodec",
+    "../../fxcrt",
+    "../../fxge",
+    "../font",
+    "../parser",
+  ]
+  allow_circular_includes_from = []
+  if (pdf_use_skia || pdf_use_skia_paths) {
+    allow_circular_includes_from += [ "../../fxge" ]
+  }
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cpdf_devicecs_unittest.cpp",
+    "cpdf_pageobjectholder_unittest.cpp",
+    "cpdf_psengine_unittest.cpp",
+    "cpdf_streamcontentparser_unittest.cpp",
+    "cpdf_streamparser_unittest.cpp",
+  ]
+  deps = [ ":page" ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/core/fpdfapi/page/cpdf_allstates.cpp b/core/fpdfapi/page/cpdf_allstates.cpp
index afc87a2..a12ce33 100644
--- a/core/fpdfapi/page/cpdf_allstates.cpp
+++ b/core/fpdfapi/page/cpdf_allstates.cpp
@@ -7,18 +7,22 @@
 #include "core/fpdfapi/page/cpdf_allstates.h"
 
 #include <algorithm>
+#include <utility>
+#include <vector>
 
+#include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 #include "core/fpdfapi/page/cpdf_streamcontentparser.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxge/cfx_graphstatedata.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/stl_util.h"
 
-CPDF_AllStates::CPDF_AllStates()
-    : m_TextLeading(0), m_TextRise(0), m_TextHorzScale(1.0f) {}
+CPDF_AllStates::CPDF_AllStates() = default;
 
-CPDF_AllStates::~CPDF_AllStates() {}
+CPDF_AllStates::~CPDF_AllStates() = default;
 
 void CPDF_AllStates::Copy(const CPDF_AllStates& src) {
   CopyStates(src);
@@ -32,15 +36,19 @@
   m_TextHorzScale = src.m_TextHorzScale;
 }
 
-void CPDF_AllStates::SetLineDash(CPDF_Array* pArray, float phase, float scale) {
-  m_GraphState.SetLineDash(pArray, phase, scale);
+void CPDF_AllStates::SetLineDash(const CPDF_Array* pArray,
+                                 float phase,
+                                 float scale) {
+  std::vector<float> dashes = ReadArrayElementsToVector(pArray, pArray->size());
+  m_GraphState.SetLineDash(std::move(dashes), phase, scale);
 }
 
 void CPDF_AllStates::ProcessExtGS(CPDF_Dictionary* pGS,
                                   CPDF_StreamContentParser* pParser) {
-  for (const auto& it : *pGS) {
+  CPDF_DictionaryLocker locker(pGS);
+  for (const auto& it : locker) {
     const ByteString& key_str = it.first;
-    CPDF_Object* pElement = it.second.get();
+    CPDF_Object* pElement = it.second.Get();
     CPDF_Object* pObject = pElement ? pElement->GetDirect() : nullptr;
     if (!pObject)
       continue;
@@ -89,6 +97,7 @@
         if (pGS->KeyExist("TR2")) {
           continue;
         }
+        FALLTHROUGH;
       case FXBSTR_ID('T', 'R', '2', 0):
         m_GeneralState.SetTR(pObject && !pObject->IsName() ? pObject : nullptr);
         break;
@@ -96,7 +105,7 @@
         CPDF_Array* pArray = pObject->AsArray();
         m_GeneralState.SetBlendMode(pArray ? pArray->GetStringAt(0)
                                            : pObject->GetString());
-        if (m_GeneralState.GetBlendType() > FXDIB_BLEND_MULTIPLY)
+        if (m_GeneralState.GetBlendType() > BlendMode::kMultiply)
           pParser->GetPageObjectHolder()->SetBackgroundAlphaNeeded(true);
         break;
       }
@@ -131,6 +140,7 @@
         if (pGS->KeyExist("BG2")) {
           continue;
         }
+        FALLTHROUGH;
       case FXBSTR_ID('B', 'G', '2', 0):
         m_GeneralState.SetBG(pObject);
         break;
@@ -138,6 +148,7 @@
         if (pGS->KeyExist("UCR2")) {
           continue;
         }
+        FALLTHROUGH;
       case FXBSTR_ID('U', 'C', 'R', '2'):
         m_GeneralState.SetUCR(pObject);
         break;
diff --git a/core/fpdfapi/page/cpdf_allstates.h b/core/fpdfapi/page/cpdf_allstates.h
index 730003a..dc6f17f 100644
--- a/core/fpdfapi/page/cpdf_allstates.h
+++ b/core/fpdfapi/page/cpdf_allstates.h
@@ -15,23 +15,23 @@
 class CPDF_Dictionary;
 class CPDF_StreamContentParser;
 
-class CPDF_AllStates : public CPDF_GraphicStates {
+class CPDF_AllStates final : public CPDF_GraphicStates {
  public:
   CPDF_AllStates();
   ~CPDF_AllStates() override;
 
   void Copy(const CPDF_AllStates& src);
   void ProcessExtGS(CPDF_Dictionary* pGS, CPDF_StreamContentParser* pParser);
-  void SetLineDash(CPDF_Array*, float, float scale);
+  void SetLineDash(const CPDF_Array* pArray, float phase, float scale);
 
   CFX_Matrix m_TextMatrix;
   CFX_Matrix m_CTM;
   CFX_Matrix m_ParentMatrix;
   CFX_PointF m_TextPos;
   CFX_PointF m_TextLinePos;
-  float m_TextLeading;
-  float m_TextRise;
-  float m_TextHorzScale;
+  float m_TextLeading = 0.0f;
+  float m_TextRise = 0.0f;
+  float m_TextHorzScale = 1.0f;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_ALLSTATES_H_
diff --git a/core/fpdfapi/page/cpdf_annotcontext.cpp b/core/fpdfapi/page/cpdf_annotcontext.cpp
new file mode 100644
index 0000000..1078e46
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_annotcontext.cpp
@@ -0,0 +1,35 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/page/cpdf_annotcontext.h"
+
+#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"
+
+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() = default;
+
+void CPDF_AnnotContext::SetForm(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());
+
+  m_pAnnotForm = pdfium::MakeUnique<CPDF_Form>(
+      m_pPage->GetDocument(), m_pPage->m_pResources.Get(), pStream);
+  m_pAnnotForm->ParseContent();
+}
diff --git a/core/fpdfapi/page/cpdf_annotcontext.h b/core/fpdfapi/page/cpdf_annotcontext.h
new file mode 100644
index 0000000..ee9f3fc
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_annotcontext.h
@@ -0,0 +1,41 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_PAGE_CPDF_ANNOTCONTEXT_H_
+#define CORE_FPDFAPI_PAGE_CPDF_ANNOTCONTEXT_H_
+
+#include <memory>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CPDF_Dictionary;
+class CPDF_Form;
+class CPDF_Page;
+class CPDF_Stream;
+
+class CPDF_AnnotContext {
+ public:
+  CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict, CPDF_Page* pPage);
+  ~CPDF_AnnotContext();
+
+  void SetForm(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(); }
+
+  // Never nullptr.
+  CPDF_Page* GetPage() const { return m_pPage.Get(); }
+
+ private:
+  std::unique_ptr<CPDF_Form> m_pAnnotForm;
+  RetainPtr<CPDF_Dictionary> const m_pAnnotDict;
+  UnownedPtr<CPDF_Page> const m_pPage;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_ANNOTCONTEXT_H_
diff --git a/core/fpdfapi/page/cpdf_clippath.cpp b/core/fpdfapi/page/cpdf_clippath.cpp
index 465a2d3..53b602d 100644
--- a/core/fpdfapi/page/cpdf_clippath.cpp
+++ b/core/fpdfapi/page/cpdf_clippath.cpp
@@ -8,16 +8,15 @@
 
 #include <utility>
 
-#include "core/fpdfapi/page/cpdf_path.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
 
-#define FPDF_CLIPPATH_MAX_TEXTS 1024
+CPDF_ClipPath::CPDF_ClipPath() = default;
 
-CPDF_ClipPath::CPDF_ClipPath() {}
+CPDF_ClipPath::CPDF_ClipPath(const CPDF_ClipPath& that) = default;
 
-CPDF_ClipPath::CPDF_ClipPath(const CPDF_ClipPath& that) : m_Ref(that.m_Ref) {}
+CPDF_ClipPath& CPDF_ClipPath::operator=(const CPDF_ClipPath& that) = default;
 
-CPDF_ClipPath::~CPDF_ClipPath() {}
+CPDF_ClipPath::~CPDF_ClipPath() = default;
 
 size_t CPDF_ClipPath::GetPathCount() const {
   return m_Ref.GetObject()->m_PathAndTypeList.size();
@@ -55,21 +54,21 @@
   bool bLayerStarted = false;
   for (size_t i = 0; i < GetTextCount(); ++i) {
     CPDF_TextObject* pTextObj = GetText(i);
-    if (!pTextObj) {
-      if (!bStarted) {
+    if (pTextObj) {
+      if (bLayerStarted) {
+        layer_rect.Union(CFX_FloatRect(pTextObj->GetBBox()));
+      } else {
+        layer_rect = CFX_FloatRect(pTextObj->GetBBox());
+        bLayerStarted = true;
+      }
+    } else {
+      if (bStarted) {
+        rect.Intersect(layer_rect);
+      } else {
         rect = layer_rect;
         bStarted = true;
-      } else {
-        rect.Intersect(layer_rect);
       }
       bLayerStarted = false;
-    } else {
-      if (!bLayerStarted) {
-        layer_rect = CFX_FloatRect(pTextObj->GetBBox(nullptr));
-        bLayerStarted = true;
-      } else {
-        layer_rect.Union(CFX_FloatRect(pTextObj->GetBBox(nullptr)));
-      }
     }
   }
   return rect;
@@ -93,8 +92,9 @@
 
 void CPDF_ClipPath::AppendTexts(
     std::vector<std::unique_ptr<CPDF_TextObject>>* pTexts) {
+  constexpr size_t kMaxTextObjects = 1024;
   PathData* pData = m_Ref.GetPrivateCopy();
-  if (pData->m_TextList.size() + pTexts->size() <= FPDF_CLIPPATH_MAX_TEXTS) {
+  if (pData->m_TextList.size() + pTexts->size() <= kMaxTextObjects) {
     for (size_t i = 0; i < pTexts->size(); i++)
       pData->m_TextList.push_back(std::move((*pTexts)[i]));
     pData->m_TextList.push_back(nullptr);
@@ -102,17 +102,26 @@
   pTexts->clear();
 }
 
+void CPDF_ClipPath::CopyClipPath(const CPDF_ClipPath& that) {
+  if (*this == that || !that.HasRef())
+    return;
+
+  for (size_t i = 0; i < that.GetPathCount(); ++i)
+    AppendPath(that.GetPath(i), that.GetClipType(i), /*bAutoMerge=*/false);
+}
+
 void CPDF_ClipPath::Transform(const CFX_Matrix& matrix) {
   PathData* pData = m_Ref.GetPrivateCopy();
   for (auto& obj : pData->m_PathAndTypeList)
-    obj.first.Transform(&matrix);
+    obj.first.Transform(matrix);
+
   for (auto& text : pData->m_TextList) {
     if (text)
       text->Transform(matrix);
   }
 }
 
-CPDF_ClipPath::PathData::PathData() {}
+CPDF_ClipPath::PathData::PathData() = default;
 
 CPDF_ClipPath::PathData::PathData(const PathData& that) {
   m_PathAndTypeList = that.m_PathAndTypeList;
@@ -124,4 +133,8 @@
   }
 }
 
-CPDF_ClipPath::PathData::~PathData() {}
+CPDF_ClipPath::PathData::~PathData() = default;
+
+RetainPtr<CPDF_ClipPath::PathData> CPDF_ClipPath::PathData::Clone() const {
+  return pdfium::MakeRetain<CPDF_ClipPath::PathData>(*this);
+}
diff --git a/core/fpdfapi/page/cpdf_clippath.h b/core/fpdfapi/page/cpdf_clippath.h
index 91a25cd..aff2839 100644
--- a/core/fpdfapi/page/cpdf_clippath.h
+++ b/core/fpdfapi/page/cpdf_clippath.h
@@ -15,13 +15,13 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/shared_copy_on_write.h"
 
-class CPDF_Path;
 class CPDF_TextObject;
 
 class CPDF_ClipPath {
  public:
   CPDF_ClipPath();
   CPDF_ClipPath(const CPDF_ClipPath& that);
+  CPDF_ClipPath& operator=(const CPDF_ClipPath& that);
   ~CPDF_ClipPath();
 
   void Emplace() { m_Ref.Emplace(); }
@@ -41,19 +41,26 @@
   CFX_FloatRect GetClipBox() const;
   void AppendPath(CPDF_Path path, uint8_t type, bool bAutoMerge);
   void AppendTexts(std::vector<std::unique_ptr<CPDF_TextObject>>* pTexts);
+  void CopyClipPath(const CPDF_ClipPath& that);
   void Transform(const CFX_Matrix& matrix);
 
  private:
-  class PathData : public Retainable {
+  class PathData final : public Retainable {
    public:
-    using PathAndTypeData = std::pair<CPDF_Path, uint8_t>;
+    template <typename T, typename... Args>
+    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-    PathData();
-    PathData(const PathData& that);
-    ~PathData() override;
+    RetainPtr<PathData> Clone() const;
+
+    using PathAndTypeData = std::pair<CPDF_Path, uint8_t>;
 
     std::vector<PathAndTypeData> m_PathAndTypeList;
     std::vector<std::unique_ptr<CPDF_TextObject>> m_TextList;
+
+   private:
+    PathData();
+    PathData(const PathData& that);
+    ~PathData() override;
   };
 
   SharedCopyOnWrite<PathData> m_Ref;
diff --git a/core/fpdfapi/page/cpdf_color.cpp b/core/fpdfapi/page/cpdf_color.cpp
index d364e8e..cfa6422 100644
--- a/core/fpdfapi/page/cpdf_color.cpp
+++ b/core/fpdfapi/page/cpdf_color.cpp
@@ -7,153 +7,90 @@
 #include "core/fpdfapi/page/cpdf_color.h"
 
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
+#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"
 
-CPDF_Color::CPDF_Color() {}
+CPDF_Color::CPDF_Color() = default;
 
-CPDF_Color::~CPDF_Color() {
-  ReleaseBuffer();
-  ReleaseColorSpace();
+CPDF_Color::CPDF_Color(const CPDF_Color& that) {
+  *this = that;
 }
 
+CPDF_Color::~CPDF_Color() = default;
+
 bool CPDF_Color::IsPattern() const {
   return m_pCS && IsPatternInternal();
 }
 
-void CPDF_Color::ReleaseBuffer() {
-  if (!m_pBuffer)
-    return;
-
-  if (IsPatternInternal()) {
-    PatternValue* pvalue = reinterpret_cast<PatternValue*>(m_pBuffer);
-    CPDF_Pattern* pPattern =
-        pvalue->m_pCountedPattern ? pvalue->m_pCountedPattern->get() : nullptr;
-    if (pPattern) {
-      CPDF_DocPageData* pPageData = pPattern->document()->GetPageData();
-      if (pPageData)
-        pPageData->ReleasePattern(pPattern->pattern_obj());
-    }
-  }
-  FX_Free(m_pBuffer);
-  m_pBuffer = nullptr;
-}
-
-void CPDF_Color::ReleaseColorSpace() {
-  if (!m_pCS)
-    return;
-
-  CPDF_Document* pDoc = m_pCS->GetDocument();
-  if (!pDoc)
-    return;
-
-  auto* pPageData = pDoc->GetPageData();
-  if (pPageData)
-    pPageData->ReleaseColorSpace(m_pCS->GetArray());
-
-  m_pCS = nullptr;
-}
-
 bool CPDF_Color::IsPatternInternal() const {
   return m_pCS->GetFamily() == PDFCS_PATTERN;
 }
 
-void CPDF_Color::SetColorSpace(CPDF_ColorSpace* pCS) {
-  if (m_pCS == pCS) {
-    if (!m_pBuffer)
-      m_pBuffer = pCS->CreateBuf();
-
-    ReleaseColorSpace();
-    m_pCS = pCS;
-    return;
-  }
-  ReleaseBuffer();
-  ReleaseColorSpace();
-
+void CPDF_Color::SetColorSpace(const RetainPtr<CPDF_ColorSpace>& pCS) {
   m_pCS = pCS;
-  if (pCS) {
-    m_pBuffer = pCS->CreateBuf();
-    pCS->GetDefaultColor(m_pBuffer);
+  if (IsPatternInternal()) {
+    m_Buffer.clear();
+    m_pValue = pdfium::MakeUnique<PatternValue>();
+  } else {
+    m_Buffer = pCS->CreateBufAndSetDefaultColor();
+    m_pValue.reset();
   }
 }
 
-void CPDF_Color::SetValue(const float* comps) {
-  if (!m_pBuffer)
-    return;
-  if (!IsPatternInternal())
-    memcpy(m_pBuffer, comps, m_pCS->CountComponents() * sizeof(float));
+void CPDF_Color::SetValueForNonPattern(const std::vector<float>& values) {
+  ASSERT(!IsPatternInternal());
+  ASSERT(m_pCS->CountComponents() <= values.size());
+  m_Buffer = values;
 }
 
-void CPDF_Color::SetValue(CPDF_Pattern* pPattern,
-                          const float* comps,
-                          uint32_t ncomps) {
-  if (ncomps > kMaxPatternColorComps)
+void CPDF_Color::SetValueForPattern(const RetainPtr<CPDF_Pattern>& pPattern,
+                                    const std::vector<float>& values) {
+  if (values.size() > kMaxPatternColorComps)
     return;
 
-  if (!IsPattern()) {
-    FX_Free(m_pBuffer);
-    m_pCS = CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
-    m_pBuffer = m_pCS->CreateBuf();
-  }
+  if (!IsPattern())
+    SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN));
 
-  CPDF_DocPageData* pDocPageData = nullptr;
-  PatternValue* pvalue = reinterpret_cast<PatternValue*>(m_pBuffer);
-  if (pvalue->m_pPattern) {
-    pDocPageData = pvalue->m_pPattern->document()->GetPageData();
-    pDocPageData->ReleasePattern(pvalue->m_pPattern->pattern_obj());
-  }
-  pvalue->m_nComps = ncomps;
-  pvalue->m_pPattern = pPattern;
-  if (ncomps)
-    memcpy(pvalue->m_Comps, comps, ncomps * sizeof(float));
-
-  pvalue->m_pCountedPattern = nullptr;
-  if (pPattern) {
-    if (!pDocPageData)
-      pDocPageData = pPattern->document()->GetPageData();
-
-    pvalue->m_pCountedPattern =
-        pDocPageData->FindPatternPtr(pPattern->pattern_obj());
-  }
+  m_pValue->SetPattern(pPattern);
+  m_pValue->SetComps(values);
 }
 
-void CPDF_Color::Copy(const CPDF_Color* pSrc) {
-  ReleaseBuffer();
-  ReleaseColorSpace();
-  m_pCS = pSrc->m_pCS;
-  if (!m_pCS)
-    return;
+CPDF_Color& CPDF_Color::operator=(const CPDF_Color& that) {
+  if (this == &that)
+    return *this;
 
-  CPDF_Document* pDoc = m_pCS->GetDocument();
-  CPDF_Array* pArray = m_pCS->GetArray();
-  if (pDoc && pArray) {
-    m_pCS = pDoc->GetPageData()->GetCopiedColorSpace(pArray);
-    if (!m_pCS)
-      return;
-  }
-  m_pBuffer = m_pCS->CreateBuf();
-  memcpy(m_pBuffer, pSrc->m_pBuffer, m_pCS->GetBufSize());
-  if (!IsPatternInternal())
-    return;
+  m_Buffer = that.m_Buffer;
+  m_pValue = that.m_pValue ? pdfium::MakeUnique<PatternValue>(*that.m_pValue)
+                           : nullptr;
+  m_pCS = that.m_pCS;
+  return *this;
+}
 
-  PatternValue* pValue = reinterpret_cast<PatternValue*>(m_pBuffer);
-  CPDF_Pattern* pPattern = pValue->m_pPattern;
-  if (!pPattern)
-    return;
+uint32_t CPDF_Color::CountComponents() const {
+  return m_pCS->CountComponents();
+}
 
-  pValue->m_pPattern = pPattern->document()->GetPageData()->GetPattern(
-      pPattern->pattern_obj(), false, pPattern->parent_matrix());
+bool CPDF_Color::IsColorSpaceRGB() const {
+  return m_pCS == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
 }
 
 bool CPDF_Color::GetRGB(int* R, int* G, int* B) const {
-  if (!m_pCS || !m_pBuffer)
-    return false;
-
   float r = 0.0f;
   float g = 0.0f;
   float b = 0.0f;
-  if (!m_pCS->GetRGB(m_pBuffer, &r, &g, &b))
+  bool result = false;
+  if (IsPatternInternal()) {
+    if (m_pValue) {
+      const CPDF_PatternCS* pPatternCS = m_pCS->AsPatternCS();
+      result = pPatternCS->GetPatternRGB(*m_pValue, &r, &g, &b);
+    }
+  } else {
+    if (!m_Buffer.empty())
+      result = m_pCS->GetRGB(m_Buffer.data(), &r, &g, &b);
+  }
+  if (!result)
     return false;
 
   *R = static_cast<int32_t>(r * 255 + 0.5f);
@@ -163,9 +100,6 @@
 }
 
 CPDF_Pattern* CPDF_Color::GetPattern() const {
-  if (!m_pBuffer || !IsPatternInternal())
-    return nullptr;
-
-  PatternValue* pvalue = reinterpret_cast<PatternValue*>(m_pBuffer);
-  return pvalue->m_pPattern;
+  ASSERT(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 2eac7cf..b533d08 100644
--- a/core/fpdfapi/page/cpdf_color.h
+++ b/core/fpdfapi/page/cpdf_color.h
@@ -7,39 +7,44 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_COLOR_H_
 #define CORE_FPDFAPI_PAGE_CPDF_COLOR_H_
 
-#include "core/fpdfapi/page/cpdf_colorspace.h"
-#include "core/fxcrt/fx_system.h"
+#include <memory>
+#include <vector>
 
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_ColorSpace;
 class CPDF_Pattern;
+class PatternValue;
 
 class CPDF_Color {
  public:
   CPDF_Color();
+  CPDF_Color(const CPDF_Color& that);
+
   ~CPDF_Color();
 
-  bool IsNull() const { return !m_pBuffer; }
+  CPDF_Color& operator=(const CPDF_Color& that);
+
+  bool IsNull() const { return m_Buffer.empty() && !m_pValue; }
   bool IsPattern() const;
-
-  void Copy(const CPDF_Color* pSrc);
-
-  void SetColorSpace(CPDF_ColorSpace* pCS);
-  void SetValue(const float* comp);
-  void SetValue(CPDF_Pattern* pPattern, const float* comp, uint32_t ncomps);
-
+  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);
+  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;
-  const CPDF_ColorSpace* GetColorSpace() const { return m_pCS; }
 
  protected:
-  void ReleaseBuffer();
-  void ReleaseColorSpace();
   bool IsPatternInternal() const;
 
-  // TODO(thestig): Convert this to a smart pointer or vector.
-  // |m_pBuffer| is created by |m_pCS|, so if it is non-null, then so is
-  // |m_pCS|.
-  float* m_pBuffer = nullptr;
-  CPDF_ColorSpace* m_pCS = nullptr;
+  std::vector<float> m_Buffer;             // Used for non-pattern colorspaces.
+  std::unique_ptr<PatternValue> m_pValue;  // Used for pattern colorspaces.
+  RetainPtr<CPDF_ColorSpace> m_pCS;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_COLOR_H_
diff --git a/core/fpdfapi/page/cpdf_colorspace.cpp b/core/fpdfapi/page/cpdf_colorspace.cpp
index fde0ed4..fd4a2ad 100644
--- a/core/fpdfapi/page/cpdf_colorspace.cpp
+++ b/core/fpdfapi/page/cpdf_colorspace.cpp
@@ -12,7 +12,6 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
 #include "core/fpdfapi/page/cpdf_devicecs.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_function.h"
@@ -28,11 +27,11 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_action.h"
-#include "core/fxcodec/codec/ccodec_iccmodule.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/cfx_fixedbufgrow.h"
+#include "core/fxcodec/icc/iccmodule.h"
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/maybe_owned.h"
 #include "third_party/base/stl_util.h"
 
@@ -71,16 +70,53 @@
     250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255,
 };
 
-class CPDF_CalGray : public CPDF_ColorSpace {
+constexpr size_t kBlackWhitePointCount = 3;
+
+void GetDefaultBlackPoint(float* pPoints) {
+  static constexpr float kDefaultValue = 0.0f;
+  for (size_t i = 0; i < kBlackWhitePointCount; ++i)
+    pPoints[i] = kDefaultValue;
+}
+
+void GetBlackPoint(const CPDF_Dictionary* pDict, float* pPoints) {
+  const CPDF_Array* pParam = pDict->GetArrayFor("BlackPoint");
+  if (!pParam || pParam->size() != kBlackWhitePointCount) {
+    GetDefaultBlackPoint(pPoints);
+    return;
+  }
+
+  // Check to make sure all values are non-negative.
+  for (size_t i = 0; i < kBlackWhitePointCount; ++i) {
+    pPoints[i] = pParam->GetNumberAt(i);
+    if (pPoints[i] < 0) {
+      GetDefaultBlackPoint(pPoints);
+      return;
+    }
+  }
+}
+
+bool GetWhitePoint(const CPDF_Dictionary* pDict, float* pPoints) {
+  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);
+  return pPoints[0] > 0.0f && pPoints[1] == 1.0f && pPoints[2] > 0.0f;
+}
+
+class CPDF_CalGray final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_CalGray(CPDF_Document* pDoc);
-  ~CPDF_CalGray() override {}
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  ~CPDF_CalGray() override;
 
   // CPDF_ColorSpace:
+  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
   uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
-  bool GetRGB(float* pBuf, float* R, float* G, float* B) const override;
+                  const CPDF_Array* pArray,
+                  std::set<const CPDF_Object*>* pVisited) override;
   void TranslateImageLine(uint8_t* pDestBuf,
                           const uint8_t* pSrcBuf,
                           int pixels,
@@ -89,74 +125,90 @@
                           bool bTransMask) const override;
 
  private:
-  float m_WhitePoint[3];
-  float m_BlackPoint[3];
-  float m_Gamma;
+  static constexpr float kDefaultGamma = 1.0f;
+
+  explicit CPDF_CalGray(CPDF_Document* pDoc);
+
+  float m_Gamma = kDefaultGamma;
+  float m_WhitePoint[kBlackWhitePointCount];
+  float m_BlackPoint[kBlackWhitePointCount];
 };
 
-class CPDF_CalRGB : public CPDF_ColorSpace {
+class CPDF_CalRGB final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_CalRGB(CPDF_Document* pDoc);
-  ~CPDF_CalRGB() override {}
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
+  ~CPDF_CalRGB() override;
 
-  bool GetRGB(float* pBuf, float* R, float* G, float* B) const 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,
                           int pixels,
                           int image_width,
                           int image_height,
                           bool bTransMask) const override;
+  uint32_t v_Load(CPDF_Document* pDoc,
+                  const CPDF_Array* pArray,
+                  std::set<const CPDF_Object*>* pVisited) override;
 
-  float m_WhitePoint[3];
-  float m_BlackPoint[3];
-  float m_Gamma[3];
-  float m_Matrix[9];
-  bool m_bGamma;
-  bool m_bMatrix;
+ private:
+  static constexpr size_t kGammaCount = 3;
+  static constexpr size_t kMatrixCount = 9;
+
+  explicit CPDF_CalRGB(CPDF_Document* pDoc);
+
+  float m_WhitePoint[kBlackWhitePointCount];
+  float m_BlackPoint[kBlackWhitePointCount];
+  float m_Gamma[kGammaCount];
+  float m_Matrix[kMatrixCount];
+  bool m_bGamma = false;
+  bool m_bMatrix = false;
 };
 
-class CPDF_LabCS : public CPDF_ColorSpace {
+class CPDF_LabCS final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_LabCS(CPDF_Document* pDoc);
-  ~CPDF_LabCS() override {}
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
+  ~CPDF_LabCS() override;
 
+  // CPDF_ColorSpace:
+  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
                        float* max) const override;
-  bool GetRGB(float* pBuf, float* R, float* G, float* B) const override;
-
   void TranslateImageLine(uint8_t* pDestBuf,
                           const uint8_t* pSrcBuf,
                           int pixels,
                           int image_width,
                           int image_height,
                           bool bTransMask) const override;
+  uint32_t v_Load(CPDF_Document* pDoc,
+                  const CPDF_Array* pArray,
+                  std::set<const CPDF_Object*>* pVisited) override;
 
-  float m_WhitePoint[3];
-  float m_BlackPoint[3];
-  float m_Ranges[4];
+ private:
+  static constexpr size_t kRangesCount = 4;
+
+  explicit CPDF_LabCS(CPDF_Document* pDoc);
+
+  float m_WhitePoint[kBlackWhitePointCount];
+  float m_BlackPoint[kBlackWhitePointCount];
+  float m_Ranges[kRangesCount];
 };
 
-class CPDF_ICCBasedCS : public CPDF_ColorSpace {
+class CPDF_ICCBasedCS final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_ICCBasedCS(CPDF_Document* pDoc);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_ICCBasedCS() override;
 
   // CPDF_ColorSpace:
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
-  bool GetRGB(float* pBuf, float* R, float* G, float* B) const override;
+  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,
@@ -164,86 +216,104 @@
                           int image_width,
                           int image_height,
                           bool bTransMask) const override;
-
-  bool IsSRGB() const { return m_pProfile->IsSRGB(); }
+  bool IsNormal() const override;
+  uint32_t v_Load(CPDF_Document* pDoc,
+                  const CPDF_Array* pArray,
+                  std::set<const CPDF_Object*>* pVisited) override;
 
  private:
+  explicit CPDF_ICCBasedCS(CPDF_Document* pDoc);
+
   // If no valid ICC profile or using sRGB, try looking for an alternate.
   bool FindAlternateProfile(CPDF_Document* pDoc,
-                            CPDF_Dictionary* pDict,
-                            std::set<CPDF_Object*>* pVisited,
+                            const CPDF_Dictionary* pDict,
+                            std::set<const CPDF_Object*>* pVisited,
                             uint32_t nExpectedComponents);
-  static CPDF_ColorSpace* GetStockAlternateProfile(uint32_t nComponents);
-  static bool IsValidComponents(int32_t nComps);
-  static std::vector<float> GetRanges(CPDF_Dictionary* pDict,
+  static RetainPtr<CPDF_ColorSpace> GetStockAlternateProfile(
+      uint32_t nComponents);
+  static std::vector<float> GetRanges(const CPDF_Dictionary* pDict,
                                       uint32_t nComponents);
 
-  MaybeOwned<CPDF_ColorSpace> m_pAlterCS;
+  RetainPtr<CPDF_ColorSpace> m_pAlterCS;
   RetainPtr<CPDF_IccProfile> m_pProfile;
-  mutable std::unique_ptr<uint8_t, FxFreeDeleter> m_pCache;
+  mutable std::vector<uint8_t> m_pCache;
   std::vector<float> m_pRanges;
 };
 
-class CPDF_IndexedCS : public CPDF_ColorSpace {
+class CPDF_IndexedCS final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_IndexedCS(CPDF_Document* pDoc);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_IndexedCS() override;
 
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
-
-  bool GetRGB(float* pBuf, float* R, float* G, float* B) const 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;
 
-  CPDF_ColorSpace* m_pBaseCS = nullptr;
-  UnownedPtr<CPDF_CountedColorSpace> m_pCountedBaseCS;
+ private:
+  explicit CPDF_IndexedCS(CPDF_Document* pDoc);
+
+  RetainPtr<CPDF_ColorSpace> m_pBaseCS;
   uint32_t m_nBaseComponents = 0;
   int m_MaxIndex = 0;
   ByteString m_Table;
-  float* m_pCompMinMax = nullptr;
+  std::vector<float> m_pCompMinMax;
 };
 
-class CPDF_SeparationCS : public CPDF_ColorSpace {
+class CPDF_SeparationCS final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_SeparationCS(CPDF_Document* pDoc);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_SeparationCS() override;
 
   // CPDF_ColorSpace:
+  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
                        float* max) const override;
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
-  bool GetRGB(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;
 
-  std::unique_ptr<CPDF_ColorSpace> m_pAltCS;
-  std::unique_ptr<CPDF_Function> m_pFunc;
+ private:
   enum { None, All, Colorant } m_Type;
+
+  explicit CPDF_SeparationCS(CPDF_Document* pDoc);
+
+  RetainPtr<CPDF_ColorSpace> m_pAltCS;
+  std::unique_ptr<const CPDF_Function> m_pFunc;
 };
 
-class CPDF_DeviceNCS : public CPDF_ColorSpace {
+class CPDF_DeviceNCS final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_DeviceNCS(CPDF_Document* pDoc);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_DeviceNCS() override;
 
   // CPDF_ColorSpace:
+  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
                        float* max) const override;
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
-  bool GetRGB(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;
 
-  std::unique_ptr<CPDF_ColorSpace> m_pAltCS;
-  std::unique_ptr<CPDF_Function> m_pFunc;
+ private:
+  explicit CPDF_DeviceNCS(CPDF_Document* pDoc);
+
+  RetainPtr<CPDF_ColorSpace> m_pAltCS;
+  std::unique_ptr<const CPDF_Function> m_pFunc;
 };
 
 class Vector_3by1 {
@@ -346,9 +416,12 @@
   // The following RGB_xyz is based on
   // sRGB value {Rx,Ry}={0.64, 0.33}, {Gx,Gy}={0.30, 0.60}, {Bx,By}={0.15, 0.06}
 
-  float Rx = 0.64f, Ry = 0.33f;
-  float Gx = 0.30f, Gy = 0.60f;
-  float Bx = 0.15f, By = 0.06f;
+  constexpr float Rx = 0.64f;
+  constexpr float Ry = 0.33f;
+  constexpr float Gx = 0.30f;
+  constexpr float Gy = 0.60f;
+  constexpr float Bx = 0.15f;
+  constexpr float By = 0.06f;
   Matrix_3by3 RGB_xyz(Rx, Gx, Bx, Ry, Gy, By, 1 - Rx - Ry, 1 - Gx - Gy,
                       1 - Bx - By);
   Vector_3by1 whitePoint(Xw, Yw, Zw);
@@ -367,8 +440,22 @@
 
 }  // namespace
 
+PatternValue::PatternValue() {
+  std::fill(std::begin(m_Comps), std::end(m_Comps), 0.0f);
+}
+
+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));
+}
+
 // static
-CPDF_ColorSpace* CPDF_ColorSpace::ColorspaceFromName(const ByteString& name) {
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::ColorspaceFromName(
+    const ByteString& name) {
   if (name == "DeviceRGB" || name == "RGB")
     return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
   if (name == "DeviceGray" || name == "G")
@@ -381,88 +468,94 @@
 }
 
 // static
-CPDF_ColorSpace* CPDF_ColorSpace::GetStockCS(int family) {
-  return CPDF_ModuleMgr::Get()->GetPageModule()->GetStockCS(family);
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::GetStockCS(int family) {
+  return CPDF_PageModule::GetInstance()->GetStockCS(family);
 }
 
 // static
-std::unique_ptr<CPDF_ColorSpace> CPDF_ColorSpace::Load(CPDF_Document* pDoc,
-                                                       CPDF_Object* pObj) {
-  std::set<CPDF_Object*> visited;
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::Load(CPDF_Document* pDoc,
+                                                 CPDF_Object* pObj) {
+  std::set<const CPDF_Object*> visited;
   return Load(pDoc, pObj, &visited);
 }
 
 // static
-std::unique_ptr<CPDF_ColorSpace> CPDF_ColorSpace::Load(
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::Load(
     CPDF_Document* pDoc,
-    CPDF_Object* pObj,
-    std::set<CPDF_Object*>* pVisited) {
+    const CPDF_Object* pObj,
+    std::set<const CPDF_Object*>* pVisited) {
   if (!pObj)
     return nullptr;
 
-  if (pObj->IsName()) {
-    return std::unique_ptr<CPDF_ColorSpace>(
-        ColorspaceFromName(pObj->GetString()));
-  }
-  if (CPDF_Stream* pStream = pObj->AsStream()) {
-    CPDF_Dictionary* pDict = pStream->GetDict();
+  if (pdfium::ContainsKey(*pVisited, pObj))
+    return nullptr;
+
+  pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pObj);
+
+  if (pObj->IsName())
+    return ColorspaceFromName(pObj->GetString());
+
+  if (const CPDF_Stream* pStream = pObj->AsStream()) {
+    const CPDF_Dictionary* pDict = pStream->GetDict();
     if (!pDict)
       return nullptr;
 
-    for (const auto& it : *pDict) {
-      std::unique_ptr<CPDF_ColorSpace> pRet;
-      CPDF_Object* pValue = it.second.get();
-      if (ToName(pValue))
-        pRet.reset(ColorspaceFromName(pValue->GetString()));
-      if (pRet)
-        return pRet;
+    CPDF_DictionaryLocker locker(pDict);
+    for (const auto& it : locker) {
+      CPDF_Name* pValue = ToName(it.second.Get());
+      if (pValue) {
+        RetainPtr<CPDF_ColorSpace> pRet =
+            ColorspaceFromName(pValue->GetString());
+        if (pRet)
+          return pRet;
+      }
     }
     return nullptr;
   }
 
-  CPDF_Array* pArray = pObj->AsArray();
+  const CPDF_Array* pArray = pObj->AsArray();
   if (!pArray || pArray->IsEmpty())
     return nullptr;
 
-  CPDF_Object* pFamilyObj = pArray->GetDirectObjectAt(0);
+  const CPDF_Object* pFamilyObj = pArray->GetDirectObjectAt(0);
   if (!pFamilyObj)
     return nullptr;
 
   ByteString familyname = pFamilyObj->GetString();
-  if (pArray->GetCount() == 1)
-    return std::unique_ptr<CPDF_ColorSpace>(ColorspaceFromName(familyname));
+  if (pArray->size() == 1)
+    return ColorspaceFromName(familyname);
 
-  std::unique_ptr<CPDF_ColorSpace> pCS;
+  RetainPtr<CPDF_ColorSpace> pCS;
   switch (familyname.GetID()) {
     case FXBSTR_ID('C', 'a', 'l', 'G'):
-      pCS.reset(new CPDF_CalGray(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_CalGray>(pDoc);
       break;
     case FXBSTR_ID('C', 'a', 'l', 'R'):
-      pCS.reset(new CPDF_CalRGB(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_CalRGB>(pDoc);
       break;
     case FXBSTR_ID('L', 'a', 'b', 0):
-      pCS.reset(new CPDF_LabCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_LabCS>(pDoc);
       break;
     case FXBSTR_ID('I', 'C', 'C', 'B'):
-      pCS.reset(new CPDF_ICCBasedCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_ICCBasedCS>(pDoc);
       break;
     case FXBSTR_ID('I', 'n', 'd', 'e'):
     case FXBSTR_ID('I', 0, 0, 0):
-      pCS.reset(new CPDF_IndexedCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_IndexedCS>(pDoc);
       break;
     case FXBSTR_ID('S', 'e', 'p', 'a'):
-      pCS.reset(new CPDF_SeparationCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_SeparationCS>(pDoc);
       break;
     case FXBSTR_ID('D', 'e', 'v', 'i'):
-      pCS.reset(new CPDF_DeviceNCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_DeviceNCS>(pDoc);
       break;
     case FXBSTR_ID('P', 'a', 't', 't'):
-      pCS.reset(new CPDF_PatternCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_PatternCS>(pDoc);
       break;
     default:
       return nullptr;
   }
-  pCS->m_pArray = pArray;
+  pCS->m_pArray.Reset(pArray);
   pCS->m_nComponents = pCS->v_Load(pDoc, pArray, pVisited);
   if (pCS->m_nComponents == 0)
     return nullptr;
@@ -470,35 +563,36 @@
   return pCS;
 }
 
-void CPDF_ColorSpace::Release() {
-  if (this == GetStockCS(PDFCS_DEVICERGB) ||
-      this == GetStockCS(PDFCS_DEVICEGRAY) ||
-      this == GetStockCS(PDFCS_DEVICECMYK) ||
-      this == GetStockCS(PDFCS_PATTERN)) {
-    return;
+// static
+uint32_t CPDF_ColorSpace::ComponentsForFamily(int family) {
+  switch (family) {
+    case PDFCS_DEVICEGRAY:
+      return 1;
+    case PDFCS_DEVICERGB:
+      return 3;
+    case PDFCS_DEVICECMYK:
+      return 4;
+    default:
+      NOTREACHED();
+      return 4;
   }
-  delete this;
 }
 
-int CPDF_ColorSpace::GetBufSize() const {
-  if (m_Family == PDFCS_PATTERN)
-    return sizeof(PatternValue);
-  return m_nComponents * sizeof(float);
+// static
+bool CPDF_ColorSpace::IsValidIccComponents(int components) {
+  return components == 1 || components == 3 || components == 4;
 }
 
-float* CPDF_ColorSpace::CreateBuf() {
-  int size = GetBufSize();
-  return reinterpret_cast<float*>(FX_Alloc(uint8_t, size));
-}
-
-void CPDF_ColorSpace::GetDefaultColor(float* buf) const {
-  if (!buf || m_Family == PDFCS_PATTERN)
-    return;
+std::vector<float> CPDF_ColorSpace::CreateBufAndSetDefaultColor() const {
+  ASSERT(m_Family != PDFCS_PATTERN);
 
   float min;
   float max;
+  std::vector<float> buf(m_nComponents);
   for (uint32_t i = 0; i < m_nComponents; i++)
     GetDefaultValue(i, &buf[i], &min, &max);
+
+  return buf;
 }
 
 uint32_t CPDF_ColorSpace::CountComponents() const {
@@ -520,8 +614,7 @@
                                          int image_width,
                                          int image_height,
                                          bool bTransMask) const {
-  CFX_FixedBufGrow<float, 16> srcbuf(m_nComponents);
-  float* src = srcbuf;
+  std::vector<float> src(m_nComponents);
   float R;
   float G;
   float B;
@@ -529,7 +622,7 @@
   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, &R, &G, &B);
+    GetRGB(src.data(), &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);
@@ -543,10 +636,34 @@
     m_dwStdConversion--;
 }
 
+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;
+}
+
+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;
+}
+
 CPDF_ColorSpace::CPDF_ColorSpace(CPDF_Document* pDoc, int family)
     : m_pDocument(pDoc), m_Family(family) {}
 
-CPDF_ColorSpace::~CPDF_ColorSpace() {}
+CPDF_ColorSpace::~CPDF_ColorSpace() = default;
 
 void CPDF_ColorSpace::SetComponentsForStockCS(uint32_t nComponents) {
   ASSERT(!m_pDocument);  // Stock colorspace is not associated with a document.
@@ -556,28 +673,30 @@
 CPDF_CalGray::CPDF_CalGray(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_CALGRAY) {}
 
+CPDF_CalGray::~CPDF_CalGray() = default;
+
 uint32_t CPDF_CalGray::v_Load(CPDF_Document* pDoc,
-                              CPDF_Array* pArray,
-                              std::set<CPDF_Object*>* pVisited) {
-  CPDF_Dictionary* pDict = pArray->GetDictAt(1);
+                              const CPDF_Array* pArray,
+                              std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Dictionary* pDict = pArray->GetDictAt(1);
   if (!pDict)
     return 0;
 
-  CPDF_Array* pParam = pDict->GetArrayFor("WhitePoint");
-  for (int i = 0; i < 3; i++)
-    m_WhitePoint[i] = pParam ? pParam->GetNumberAt(i) : 0;
+  if (!GetWhitePoint(pDict, m_WhitePoint))
+    return 0;
 
-  pParam = pDict->GetArrayFor("BlackPoint");
-  for (int i = 0; i < 3; i++)
-    m_BlackPoint[i] = pParam ? pParam->GetNumberAt(i) : 0;
+  GetBlackPoint(pDict, m_BlackPoint);
 
   m_Gamma = pDict->GetNumberFor("Gamma");
   if (m_Gamma == 0)
-    m_Gamma = 1.0f;
+    m_Gamma = kDefaultGamma;
   return 1;
 }
 
-bool CPDF_CalGray::GetRGB(float* pBuf, float* R, float* G, float* B) const {
+bool CPDF_CalGray::GetRGB(const float* pBuf,
+                          float* R,
+                          float* G,
+                          float* B) const {
   *R = *pBuf;
   *G = *pBuf;
   *B = *pBuf;
@@ -600,42 +719,40 @@
 CPDF_CalRGB::CPDF_CalRGB(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_CALRGB) {}
 
+CPDF_CalRGB::~CPDF_CalRGB() = default;
+
 uint32_t CPDF_CalRGB::v_Load(CPDF_Document* pDoc,
-                             CPDF_Array* pArray,
-                             std::set<CPDF_Object*>* pVisited) {
-  CPDF_Dictionary* pDict = pArray->GetDictAt(1);
+                             const CPDF_Array* pArray,
+                             std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Dictionary* pDict = pArray->GetDictAt(1);
   if (!pDict)
     return 0;
 
-  CPDF_Array* pParam = pDict->GetArrayFor("WhitePoint");
-  for (int i = 0; i < 3; i++)
-    m_WhitePoint[i] = pParam ? pParam->GetNumberAt(i) : 0;
+  if (!GetWhitePoint(pDict, m_WhitePoint))
+    return 0;
 
-  pParam = pDict->GetArrayFor("BlackPoint");
-  for (int i = 0; i < 3; i++)
-    m_BlackPoint[i] = pParam ? pParam->GetNumberAt(i) : 0;
+  GetBlackPoint(pDict, m_BlackPoint);
 
-  pParam = pDict->GetArrayFor("Gamma");
+  const CPDF_Array* pParam = pDict->GetArrayFor("Gamma");
   if (pParam) {
     m_bGamma = true;
-    for (int i = 0; i < 3; i++)
+    for (size_t i = 0; i < FX_ArraySize(m_Gamma); ++i)
       m_Gamma[i] = pParam->GetNumberAt(i);
-  } else {
-    m_bGamma = false;
   }
 
   pParam = pDict->GetArrayFor("Matrix");
   if (pParam) {
     m_bMatrix = true;
-    for (int i = 0; i < 9; i++)
+    for (size_t i = 0; i < FX_ArraySize(m_Matrix); ++i)
       m_Matrix[i] = pParam->GetNumberAt(i);
-  } else {
-    m_bMatrix = false;
   }
   return 3;
 }
 
-bool CPDF_CalRGB::GetRGB(float* pBuf, float* R, float* G, float* B) const {
+bool CPDF_CalRGB::GetRGB(const float* pBuf,
+                         float* R,
+                         float* G,
+                         float* B) const {
   float A_ = pBuf[0];
   float B_ = pBuf[1];
   float C_ = pBuf[2];
@@ -678,19 +795,21 @@
       Cal[1] = static_cast<float>(pSrcBuf[1]) / 255;
       Cal[2] = static_cast<float>(pSrcBuf[0]) / 255;
       GetRGB(Cal, &R, &G, &B);
-      pDestBuf[0] = FXSYS_round(B * 255);
-      pDestBuf[1] = FXSYS_round(G * 255);
-      pDestBuf[2] = FXSYS_round(R * 255);
+      pDestBuf[0] = FXSYS_roundf(B * 255);
+      pDestBuf[1] = FXSYS_roundf(G * 255);
+      pDestBuf[2] = FXSYS_roundf(R * 255);
       pSrcBuf += 3;
       pDestBuf += 3;
     }
   }
-  ReverseRGB(pDestBuf, pSrcBuf, pixels);
+  fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
 }
 
 CPDF_LabCS::CPDF_LabCS(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_LAB) {}
 
+CPDF_LabCS::~CPDF_LabCS() = default;
+
 void CPDF_LabCS::GetDefaultValue(int iComponent,
                                  float* value,
                                  float* min,
@@ -709,28 +828,28 @@
 }
 
 uint32_t CPDF_LabCS::v_Load(CPDF_Document* pDoc,
-                            CPDF_Array* pArray,
-                            std::set<CPDF_Object*>* pVisited) {
-  CPDF_Dictionary* pDict = pArray->GetDictAt(1);
+                            const CPDF_Array* pArray,
+                            std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Dictionary* pDict = pArray->GetDictAt(1);
   if (!pDict)
     return 0;
 
-  CPDF_Array* pParam = pDict->GetArrayFor("WhitePoint");
-  for (int i = 0; i < 3; i++)
-    m_WhitePoint[i] = pParam ? pParam->GetNumberAt(i) : 0;
+  if (!GetWhitePoint(pDict, m_WhitePoint))
+    return 0;
 
-  pParam = pDict->GetArrayFor("BlackPoint");
-  for (int i = 0; i < 3; i++)
-    m_BlackPoint[i] = pParam ? pParam->GetNumberAt(i) : 0;
+  GetBlackPoint(pDict, m_BlackPoint);
 
-  pParam = pDict->GetArrayFor("Range");
-  const float kDefaultRanges[4] = {-100.0f, 100.0f, -100.0f, 100.0f};
-  for (size_t i = 0; i < FX_ArraySize(kDefaultRanges); i++)
+  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),
+                "Range size mismatch");
+  for (size_t i = 0; i < FX_ArraySize(kDefaultRanges); ++i)
     m_Ranges[i] = pParam ? pParam->GetNumberAt(i) : kDefaultRanges[i];
   return 3;
 }
 
-bool CPDF_LabCS::GetRGB(float* pBuf, float* R, float* G, float* B) const {
+bool CPDF_LabCS::GetRGB(const float* pBuf, float* R, float* G, float* B) const {
   float Lstar = pBuf[0];
   float astar = pBuf[1];
   float bstar = pBuf[2];
@@ -786,33 +905,25 @@
 CPDF_ICCBasedCS::CPDF_ICCBasedCS(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_ICCBASED) {}
 
-CPDF_ICCBasedCS::~CPDF_ICCBasedCS() {
-  if (m_pProfile && m_pDocument) {
-    CPDF_Stream* pStream = m_pProfile->GetStream();
-    m_pProfile.Reset();  // Give up our reference first.
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData)
-      pPageData->MaybePurgeIccProfile(pStream);
-  }
-}
+CPDF_ICCBasedCS::~CPDF_ICCBasedCS() = default;
 
 uint32_t CPDF_ICCBasedCS::v_Load(CPDF_Document* pDoc,
-                                 CPDF_Array* pArray,
-                                 std::set<CPDF_Object*>* pVisited) {
-  CPDF_Stream* pStream = pArray->GetStreamAt(1);
+                                 const CPDF_Array* pArray,
+                                 std::set<const CPDF_Object*>* pVisited) {
+  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.
-  CPDF_Dictionary* pDict = pStream->GetDict();
+  const CPDF_Dictionary* pDict = pStream->GetDict();
   int32_t nDictComponents = pDict ? pDict->GetIntegerFor("N") : 0;
-  if (!IsValidComponents(nDictComponents))
+  if (!IsValidIccComponents(nDictComponents))
     return 0;
 
   uint32_t nComponents = static_cast<uint32_t>(nDictComponents);
-  m_pProfile = pDoc->LoadIccProfile(pStream);
+  m_pProfile = CPDF_DocPageData::FromDocument(pDoc)->GetIccProfile(pStream);
   if (!m_pProfile)
     return 0;
 
@@ -838,9 +949,12 @@
   return nComponents;
 }
 
-bool CPDF_ICCBasedCS::GetRGB(float* pBuf, float* R, float* G, float* B) const {
+bool CPDF_ICCBasedCS::GetRGB(const float* pBuf,
+                             float* R,
+                             float* G,
+                             float* B) const {
   ASSERT(m_pProfile);
-  if (IsSRGB()) {
+  if (m_pProfile->IsSRGB()) {
     *R = pBuf[0];
     *G = pBuf[1];
     *B = pBuf[2];
@@ -848,9 +962,7 @@
   }
   if (m_pProfile->transform()) {
     float rgb[3];
-    CCodec_IccModule* pIccModule = CPDF_ModuleMgr::Get()->GetIccModule();
-    pIccModule->SetComponents(CountComponents());
-    pIccModule->Translate(m_pProfile->transform(), pBuf, rgb);
+    IccModule::Translate(m_pProfile->transform(), CountComponents(), pBuf, rgb);
     *R = rgb[0];
     *G = rgb[1];
     *B = rgb[2];
@@ -878,8 +990,8 @@
                                          int image_width,
                                          int image_height,
                                          bool bTransMask) const {
-  if (IsSRGB()) {
-    ReverseRGB(pDestBuf, pSrcBuf, pixels);
+  if (m_pProfile->IsSRGB()) {
+    fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
     return;
   }
   if (!m_pProfile->transform()) {
@@ -892,7 +1004,7 @@
 
   // |nMaxColors| will not overflow since |nComponents| is limited in size.
   const uint32_t nComponents = CountComponents();
-  ASSERT(IsValidComponents(nComponents));
+  ASSERT(IsValidIccComponents(nComponents));
   int nMaxColors = 1;
   for (uint32_t i = 0; i < nComponents; i++)
     nMaxColors *= 52;
@@ -905,29 +1017,27 @@
       bTranslate = nPixelCount.ValueOrDie() < nMaxColors * 3 / 2;
   }
   if (bTranslate) {
-    CPDF_ModuleMgr::Get()->GetIccModule()->TranslateScanline(
-        m_pProfile->transform(), pDestBuf, pSrcBuf, pixels);
+    IccModule::TranslateScanline(m_pProfile->transform(), pDestBuf, pSrcBuf,
+                                 pixels);
     return;
   }
 
-  if (!m_pCache) {
-    m_pCache.reset(FX_Alloc2D(uint8_t, nMaxColors, 3));
-    std::unique_ptr<uint8_t, FxFreeDeleter> temp_src(
-        FX_Alloc2D(uint8_t, nMaxColors, nComponents));
-    uint8_t* pSrc = temp_src.get();
+  if (m_pCache.empty()) {
+    m_pCache = pdfium::Vector2D<uint8_t>(nMaxColors, 3);
+    auto temp_src = pdfium::Vector2D<uint8_t>(nMaxColors, nComponents);
+    size_t src_index = 0;
     for (int i = 0; i < nMaxColors; i++) {
       uint32_t color = i;
       uint32_t order = nMaxColors / 52;
       for (uint32_t c = 0; c < nComponents; c++) {
-        *pSrc++ = static_cast<uint8_t>(color / order * 5);
+        temp_src[src_index++] = static_cast<uint8_t>(color / order * 5);
         color %= order;
         order /= 52;
       }
     }
-    CPDF_ModuleMgr::Get()->GetIccModule()->TranslateScanline(
-        m_pProfile->transform(), m_pCache.get(), temp_src.get(), nMaxColors);
+    IccModule::TranslateScanline(m_pProfile->transform(), m_pCache.data(),
+                                 temp_src.data(), nMaxColors);
   }
-  uint8_t* pCachePtr = m_pCache.get();
   for (int i = 0; i < pixels; i++) {
     int index = 0;
     for (uint32_t c = 0; c < nComponents; c++) {
@@ -935,17 +1045,28 @@
       pSrcBuf++;
     }
     index *= 3;
-    *pDestBuf++ = pCachePtr[index];
-    *pDestBuf++ = pCachePtr[index + 1];
-    *pDestBuf++ = pCachePtr[index + 2];
+    *pDestBuf++ = m_pCache[index];
+    *pDestBuf++ = m_pCache[index + 1];
+    *pDestBuf++ = m_pCache[index + 2];
   }
 }
 
-bool CPDF_ICCBasedCS::FindAlternateProfile(CPDF_Document* pDoc,
-                                           CPDF_Dictionary* pDict,
-                                           std::set<CPDF_Object*>* pVisited,
-                                           uint32_t nExpectedComponents) {
-  CPDF_Object* pAlterCSObj = pDict->GetDirectObjectFor("Alternate");
+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();
+  return false;
+}
+
+bool CPDF_ICCBasedCS::FindAlternateProfile(
+    CPDF_Document* pDoc,
+    const CPDF_Dictionary* pDict,
+    std::set<const CPDF_Object*>* pVisited,
+    uint32_t nExpectedComponents) {
+  const CPDF_Object* pAlterCSObj = pDict->GetDirectObjectFor("Alternate");
   if (!pAlterCSObj)
     return false;
 
@@ -964,7 +1085,7 @@
 }
 
 // static
-CPDF_ColorSpace* CPDF_ICCBasedCS::GetStockAlternateProfile(
+RetainPtr<CPDF_ColorSpace> CPDF_ICCBasedCS::GetStockAlternateProfile(
     uint32_t nComponents) {
   if (nComponents == 1)
     return GetStockCS(PDFCS_DEVICEGRAY);
@@ -977,23 +1098,16 @@
 }
 
 // static
-bool CPDF_ICCBasedCS::IsValidComponents(int32_t nComps) {
-  return nComps == 1 || nComps == 3 || nComps == 4;
-}
-
-// static
-std::vector<float> CPDF_ICCBasedCS::GetRanges(CPDF_Dictionary* pDict,
+std::vector<float> CPDF_ICCBasedCS::GetRanges(const CPDF_Dictionary* pDict,
                                               uint32_t nComponents) {
-  ASSERT(IsValidComponents(nComponents));
+  ASSERT(IsValidIccComponents(nComponents));
 
   std::vector<float> ranges;
-  ranges.reserve(nComponents * 2);
-  CPDF_Array* pRanges = pDict->GetArrayFor("Range");
+  const CPDF_Array* pRanges = pDict->GetArrayFor("Range");
   if (pRanges) {
-    for (uint32_t i = 0; i < nComponents * 2; i++) {
-      ranges.push_back(pRanges->GetNumberAt(i));
-    }
+    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);
@@ -1005,27 +1119,19 @@
 CPDF_IndexedCS::CPDF_IndexedCS(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_INDEXED) {}
 
-CPDF_IndexedCS::~CPDF_IndexedCS() {
-  FX_Free(m_pCompMinMax);
-  CPDF_ColorSpace* pCS = m_pCountedBaseCS ? m_pCountedBaseCS->get() : nullptr;
-  if (pCS && m_pDocument) {
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData)
-      pPageData->ReleaseColorSpace(pCS->GetArray());
-  }
-}
+CPDF_IndexedCS::~CPDF_IndexedCS() = default;
 
 uint32_t CPDF_IndexedCS::v_Load(CPDF_Document* pDoc,
-                                CPDF_Array* pArray,
-                                std::set<CPDF_Object*>* pVisited) {
-  if (pArray->GetCount() < 4)
+                                const CPDF_Array* pArray,
+                                std::set<const CPDF_Object*>* pVisited) {
+  if (pArray->size() < 4)
     return 0;
 
-  CPDF_Object* pBaseObj = pArray->GetDirectObjectAt(1);
+  const CPDF_Object* pBaseObj = pArray->GetDirectObjectAt(1);
   if (pBaseObj == m_pArray)
     return 0;
 
-  CPDF_DocPageData* pDocPageData = pDoc->GetPageData();
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc);
   m_pBaseCS = pDocPageData->GetColorSpaceGuarded(pBaseObj, nullptr, pVisited);
   if (!m_pBaseCS)
     return 0;
@@ -1036,9 +1142,8 @@
   if (family == PDFCS_INDEXED || family == PDFCS_PATTERN)
     return 0;
 
-  m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
   m_nBaseComponents = m_pBaseCS->CountComponents();
-  m_pCompMinMax = FX_Alloc2D(float, m_nBaseComponents, 2);
+  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],
@@ -1047,21 +1152,24 @@
   }
   m_MaxIndex = pArray->GetIntegerAt(2);
 
-  CPDF_Object* pTableObj = pArray->GetDirectObjectAt(3);
+  const CPDF_Object* pTableObj = pArray->GetDirectObjectAt(3);
   if (!pTableObj)
     return 0;
 
-  if (CPDF_String* pString = pTableObj->AsString()) {
+  if (const CPDF_String* pString = pTableObj->AsString()) {
     m_Table = pString->GetString();
-  } else if (CPDF_Stream* pStream = pTableObj->AsStream()) {
+  } else if (const CPDF_Stream* pStream = pTableObj->AsStream()) {
     auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
     pAcc->LoadAllDataFiltered();
-    m_Table = ByteStringView(pAcc->GetData(), pAcc->GetSize());
+    m_Table = ByteStringView(pAcc->GetSpan());
   }
   return 1;
 }
 
-bool CPDF_IndexedCS::GetRGB(float* pBuf, float* R, float* G, float* B) const {
+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;
@@ -1077,15 +1185,15 @@
       return false;
     }
   }
-  CFX_FixedBufGrow<float, 16> Comps(m_nBaseComponents);
-  float* comps = Comps;
+  std::vector<float> comps(m_nBaseComponents);
   const uint8_t* pTable = m_Table.raw_str();
-  for (uint32_t i = 0; i < m_nBaseComponents; i++) {
+  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);
+  ASSERT(m_nBaseComponents == m_pBaseCS->CountComponents());
+  return m_pBaseCS->GetRGB(comps.data(), R, G, B);
 }
 
 void CPDF_IndexedCS::EnableStdConversion(bool bEnabled) {
@@ -1097,7 +1205,7 @@
 CPDF_SeparationCS::CPDF_SeparationCS(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_SEPARATION) {}
 
-CPDF_SeparationCS::~CPDF_SeparationCS() {}
+CPDF_SeparationCS::~CPDF_SeparationCS() = default;
 
 void CPDF_SeparationCS::GetDefaultValue(int iComponent,
                                         float* value,
@@ -1109,8 +1217,8 @@
 }
 
 uint32_t CPDF_SeparationCS::v_Load(CPDF_Document* pDoc,
-                                   CPDF_Array* pArray,
-                                   std::set<CPDF_Object*>* pVisited) {
+                                   const CPDF_Array* pArray,
+                                   std::set<const CPDF_Object*>* pVisited) {
   ByteString name = pArray->GetStringAt(1);
   if (name == "None") {
     m_Type = None;
@@ -1118,7 +1226,7 @@
   }
 
   m_Type = Colorant;
-  CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2);
+  const CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2);
   if (pAltCS == m_pArray)
     return 0;
 
@@ -1129,7 +1237,7 @@
   if (m_pAltCS->IsSpecial())
     return 0;
 
-  CPDF_Object* pFuncObj = pArray->GetDirectObjectAt(3);
+  const CPDF_Object* pFuncObj = pArray->GetDirectObjectAt(3);
   if (pFuncObj && !pFuncObj->IsName()) {
     auto pFunc = CPDF_Function::Load(pFuncObj);
     if (pFunc && pFunc->CountOutputs() >= m_pAltCS->CountComponents())
@@ -1138,7 +1246,7 @@
   return 1;
 }
 
-bool CPDF_SeparationCS::GetRGB(float* pBuf,
+bool CPDF_SeparationCS::GetRGB(const float* pBuf,
                                float* R,
                                float* G,
                                float* B) const {
@@ -1150,20 +1258,20 @@
       return false;
 
     int nComps = m_pAltCS->CountComponents();
-    CFX_FixedBufGrow<float, 16> results(nComps);
+    std::vector<float> results(nComps);
     for (int i = 0; i < nComps; i++)
       results[i] = *pBuf;
-    return m_pAltCS->GetRGB(results, R, G, B);
+    return m_pAltCS->GetRGB(results.data(), R, G, B);
   }
 
-  CFX_FixedBufGrow<float, 16> results(m_pFunc->CountOutputs());
+  // 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;
-  m_pFunc->Call(pBuf, 1, results, &nresults);
-  if (nresults == 0)
+  if (!m_pFunc->Call(pBuf, 1, results.data(), &nresults) || nresults == 0)
     return false;
 
   if (m_pAltCS)
-    return m_pAltCS->GetRGB(results, R, G, B);
+    return m_pAltCS->GetRGB(results.data(), R, G, B);
 
   R = 0;
   G = 0;
@@ -1180,7 +1288,7 @@
 CPDF_DeviceNCS::CPDF_DeviceNCS(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_DEVICEN) {}
 
-CPDF_DeviceNCS::~CPDF_DeviceNCS() {}
+CPDF_DeviceNCS::~CPDF_DeviceNCS() = default;
 
 void CPDF_DeviceNCS::GetDefaultValue(int iComponent,
                                      float* value,
@@ -1192,13 +1300,13 @@
 }
 
 uint32_t CPDF_DeviceNCS::v_Load(CPDF_Document* pDoc,
-                                CPDF_Array* pArray,
-                                std::set<CPDF_Object*>* pVisited) {
-  CPDF_Array* pObj = ToArray(pArray->GetDirectObjectAt(1));
+                                const CPDF_Array* pArray,
+                                std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Array* pObj = ToArray(pArray->GetDirectObjectAt(1));
   if (!pObj)
     return 0;
 
-  CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2);
+  const CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2);
   if (!pAltCS || pAltCS == m_pArray)
     return 0;
 
@@ -1213,20 +1321,25 @@
   if (m_pFunc->CountOutputs() < m_pAltCS->CountComponents())
     return 0;
 
-  return pObj->GetCount();
+  return pObj->size();
 }
 
-bool CPDF_DeviceNCS::GetRGB(float* pBuf, float* R, float* G, float* B) const {
+bool CPDF_DeviceNCS::GetRGB(const float* pBuf,
+                            float* R,
+                            float* G,
+                            float* B) const {
   if (!m_pFunc)
     return false;
 
-  CFX_FixedBufGrow<float, 16> results(m_pFunc->CountOutputs());
+  // 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;
-  m_pFunc->Call(pBuf, CountComponents(), results, &nresults);
-  if (nresults == 0)
+  if (!m_pFunc->Call(pBuf, CountComponents(), results.data(), &nresults) ||
+      nresults == 0) {
     return false;
+  }
 
-  return m_pAltCS->GetRGB(results, R, G, B);
+  return m_pAltCS->GetRGB(results.data(), R, G, B);
 }
 
 void CPDF_DeviceNCS::EnableStdConversion(bool bEnabled) {
diff --git a/core/fpdfapi/page/cpdf_colorspace.h b/core/fpdfapi/page/cpdf_colorspace.h
index 7f3a739..75928d2 100644
--- a/core/fpdfapi/page/cpdf_colorspace.h
+++ b/core/fpdfapi/page/cpdf_colorspace.h
@@ -7,13 +7,18 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_
 
+#include <array>
 #include <memory>
 #include <set>
+#include <vector>
 
 #include "core/fpdfapi/page/cpdf_pattern.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 "third_party/base/span.h"
 
 #define PDFCS_DEVICEGRAY 1
 #define PDFCS_DEVICERGB 2
@@ -30,32 +35,51 @@
 class CPDF_Array;
 class CPDF_Document;
 class CPDF_Object;
+class CPDF_PatternCS;
 
 constexpr size_t kMaxPatternColorComps = 16;
 
-struct PatternValue {
-  CPDF_Pattern* m_pPattern;
-  CPDF_CountedPattern* m_pCountedPattern;
-  int m_nComps;
-  float m_Comps[kMaxPatternColorComps];
+class PatternValue {
+ public:
+  PatternValue();
+  PatternValue(const PatternValue& that);
+  ~PatternValue();
+
+  void SetComps(pdfium::span<const float> comps);
+  pdfium::span<const float> GetComps() const {
+    // TODO(tsepez): update span.h from base for implicit std::array ctor.
+    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;
+  }
+
+ private:
+  RetainPtr<CPDF_Pattern> m_pRetainedPattern;
+  std::array<float, kMaxPatternColorComps> m_Comps;
 };
 
-class CPDF_ColorSpace {
+class CPDF_ColorSpace : public Retainable, public Observable {
  public:
-  static CPDF_ColorSpace* GetStockCS(int Family);
-  static CPDF_ColorSpace* ColorspaceFromName(const ByteString& name);
-  static std::unique_ptr<CPDF_ColorSpace> Load(CPDF_Document* pDoc,
-                                               CPDF_Object* pCSObj);
-  static std::unique_ptr<CPDF_ColorSpace> Load(
+  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);
+  static RetainPtr<CPDF_ColorSpace> Load(
       CPDF_Document* pDoc,
-      CPDF_Object* pCSObj,
-      std::set<CPDF_Object*>* pVisited);
+      const CPDF_Object* pObj,
+      std::set<const CPDF_Object*>* pVisited);
+  static uint32_t ComponentsForFamily(int family);
+  static bool IsValidIccComponents(int components);
 
-  void Release();
+  const CPDF_Array* GetArray() const { return m_pArray.Get(); }
+  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
 
-  int GetBufSize() const;
-  float* CreateBuf();
-  void GetDefaultColor(float* buf) const;
+  // 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; }
   bool IsSpecial() const {
@@ -63,13 +87,15 @@
            GetFamily() == PDFCS_INDEXED || GetFamily() == PDFCS_PATTERN;
   }
 
+  virtual bool GetRGB(const float* pBuf,
+                      float* R,
+                      float* G,
+                      float* B) const = 0;
+
   virtual void GetDefaultValue(int iComponent,
                                float* value,
                                float* min,
                                float* max) const;
-
-  virtual bool GetRGB(float* pBuf, float* R, float* G, float* B) const = 0;
-
   virtual void TranslateImageLine(uint8_t* dest_buf,
                                   const uint8_t* src_buf,
                                   int pixels,
@@ -77,45 +103,38 @@
                                   int image_height,
                                   bool bTransMask) const;
   virtual void EnableStdConversion(bool bEnabled);
+  virtual bool IsNormal() const;
 
-  CPDF_Array* GetArray() const { return m_pArray.Get(); }
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
+  // Returns |this| as a CPDF_PatternCS* if |this| is a pattern.
+  virtual CPDF_PatternCS* AsPatternCS();
+  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;
 
  protected:
   CPDF_ColorSpace(CPDF_Document* pDoc, int family);
-  virtual ~CPDF_ColorSpace();
+  ~CPDF_ColorSpace() override;
 
   // Returns the number of components, or 0 on failure.
   virtual uint32_t v_Load(CPDF_Document* pDoc,
-                          CPDF_Array* pArray,
-                          std::set<CPDF_Object*>* pVisited) = 0;
+                          const CPDF_Array* pArray,
+                          std::set<const CPDF_Object*>* pVisited) = 0;
 
   // Stock colorspaces are not loaded normally. This initializes their
   // components count.
   void SetComponentsForStockCS(uint32_t nComponents);
 
   UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<CPDF_Array> m_pArray;
+  RetainPtr<const CPDF_Array> m_pArray;
   const int m_Family;
   uint32_t m_dwStdConversion = 0;
 
  private:
   uint32_t m_nComponents = 0;
 };
-using CPDF_CountedColorSpace = CPDF_CountedObject<CPDF_ColorSpace>;
-
-namespace std {
-
-// Make std::unique_ptr<CPDF_ColorSpace> call Release() rather than
-// simply deleting the object.
-template <>
-struct default_delete<CPDF_ColorSpace> {
-  void operator()(CPDF_ColorSpace* pColorSpace) const {
-    if (pColorSpace)
-      pColorSpace->Release();
-  }
-};
-
-}  // namespace std
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_
diff --git a/core/fpdfapi/page/cpdf_colorstate.cpp b/core/fpdfapi/page/cpdf_colorstate.cpp
index 693fcf1..50dbb71 100644
--- a/core/fpdfapi/page/cpdf_colorstate.cpp
+++ b/core/fpdfapi/page/cpdf_colorstate.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_colorstate.h"
 
+#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"
@@ -25,20 +26,20 @@
   m_Ref.GetPrivateCopy()->SetDefault();
 }
 
-uint32_t CPDF_ColorState::GetFillRGB() const {
-  return m_Ref.GetObject()->m_FillRGB;
+FX_COLORREF CPDF_ColorState::GetFillColorRef() const {
+  return m_Ref.GetObject()->m_FillColorRef;
 }
 
-void CPDF_ColorState::SetFillRGB(uint32_t rgb) {
-  m_Ref.GetPrivateCopy()->m_FillRGB = rgb;
+void CPDF_ColorState::SetFillColorRef(FX_COLORREF colorref) {
+  m_Ref.GetPrivateCopy()->m_FillColorRef = colorref;
 }
 
-uint32_t CPDF_ColorState::GetStrokeRGB() const {
-  return m_Ref.GetObject()->m_StrokeRGB;
+FX_COLORREF CPDF_ColorState::GetStrokeColorRef() const {
+  return m_Ref.GetObject()->m_StrokeColorRef;
 }
 
-void CPDF_ColorState::SetStrokeRGB(uint32_t rgb) {
-  m_Ref.GetPrivateCopy()->m_StrokeRGB = rgb;
+void CPDF_ColorState::SetStrokeColorRef(FX_COLORREF colorref) {
+  m_Ref.GetPrivateCopy()->m_StrokeColorRef = colorref;
 }
 
 const CPDF_Color* CPDF_ColorState::GetFillColor() const {
@@ -69,90 +70,92 @@
   return pColor && !pColor->IsNull();
 }
 
-void CPDF_ColorState::SetFillColor(CPDF_ColorSpace* pCS,
-                                   float* pValue,
-                                   uint32_t nValues) {
+void CPDF_ColorState::SetFillColor(const RetainPtr<CPDF_ColorSpace>& pCS,
+                                   const std::vector<float>& values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  SetColor(pData->m_FillColor, pData->m_FillRGB, pCS, pValue, nValues);
+  SetColor(pCS, values, &pData->m_FillColor, &pData->m_FillColorRef);
 }
 
-void CPDF_ColorState::SetStrokeColor(CPDF_ColorSpace* pCS,
-                                     float* pValue,
-                                     uint32_t nValues) {
+void CPDF_ColorState::SetStrokeColor(const RetainPtr<CPDF_ColorSpace>& pCS,
+                                     const std::vector<float>& values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  SetColor(pData->m_StrokeColor, pData->m_StrokeRGB, pCS, pValue, nValues);
+  SetColor(pCS, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef);
 }
 
-void CPDF_ColorState::SetFillPattern(CPDF_Pattern* pPattern,
-                                     float* pValue,
-                                     uint32_t nValues) {
+void CPDF_ColorState::SetFillPattern(const RetainPtr<CPDF_Pattern>& pPattern,
+                                     const std::vector<float>& values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  pData->m_FillColor.SetValue(pPattern, pValue, nValues);
-  int R;
-  int G;
-  int B;
-  bool ret = pData->m_FillColor.GetRGB(&R, &G, &B);
-  if (CPDF_TilingPattern* pTilingPattern = pPattern->AsTilingPattern()) {
-    if (!ret && pTilingPattern->colored()) {
-      pData->m_FillRGB = 0x00BFBFBF;
-      return;
-    }
-  }
-  pData->m_FillRGB = ret ? FXSYS_RGB(R, G, B) : 0xFFFFFFFF;
+  SetPattern(pPattern, values, &pData->m_FillColor, &pData->m_FillColorRef);
 }
 
-void CPDF_ColorState::SetStrokePattern(CPDF_Pattern* pPattern,
-                                       float* pValue,
-                                       uint32_t nValues) {
+void CPDF_ColorState::SetStrokePattern(const RetainPtr<CPDF_Pattern>& pPattern,
+                                       const std::vector<float>& values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
-  pData->m_StrokeColor.SetValue(pPattern, pValue, nValues);
-  int R;
-  int G;
-  int B;
-  bool ret = pData->m_StrokeColor.GetRGB(&R, &G, &B);
-  if (CPDF_TilingPattern* pTilingPattern = pPattern->AsTilingPattern()) {
-    if (!ret && pTilingPattern->colored()) {
-      pData->m_StrokeRGB = 0x00BFBFBF;
-      return;
-    }
-  }
-  pData->m_StrokeRGB =
-      pData->m_StrokeColor.GetRGB(&R, &G, &B) ? FXSYS_RGB(R, G, B) : 0xFFFFFFFF;
+  SetPattern(pPattern, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef);
 }
 
-void CPDF_ColorState::SetColor(CPDF_Color& color,
-                               uint32_t& rgb,
-                               CPDF_ColorSpace* pCS,
-                               float* pValue,
-                               uint32_t nValues) {
+void CPDF_ColorState::SetColor(const RetainPtr<CPDF_ColorSpace>& pCS,
+                               const std::vector<float>& values,
+                               CPDF_Color* color,
+                               FX_COLORREF* colorref) {
+  ASSERT(color);
+  ASSERT(colorref);
+
   if (pCS)
-    color.SetColorSpace(pCS);
-  else if (color.IsNull())
-    color.SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY));
+    color->SetColorSpace(pCS);
+  else if (color->IsNull())
+    color->SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY));
 
-  if (color.GetColorSpace()->CountComponents() > nValues)
+  if (color->CountComponents() > values.size())
     return;
 
-  color.SetValue(pValue);
+  if (!color->IsPattern())
+    color->SetValueForNonPattern(values);
   int R;
   int G;
   int B;
-  rgb = color.GetRGB(&R, &G, &B) ? FXSYS_RGB(R, G, B) : 0xFFFFFFFF;
+  *colorref = color->GetRGB(&R, &G, &B) ? FXSYS_BGR(B, G, R) : 0xFFFFFFFF;
 }
 
-CPDF_ColorState::ColorData::ColorData() : m_FillRGB(0), m_StrokeRGB(0) {}
+void CPDF_ColorState::SetPattern(const RetainPtr<CPDF_Pattern>& pPattern,
+                                 const std::vector<float>& values,
+                                 CPDF_Color* color,
+                                 FX_COLORREF* colorref) {
+  ASSERT(color);
+  ASSERT(colorref);
+
+  color->SetValueForPattern(pPattern, 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;
+    }
+  }
+  *colorref = ret ? FXSYS_BGR(B, G, R) : 0xFFFFFFFF;
+}
+
+CPDF_ColorState::ColorData::ColorData() = default;
 
 CPDF_ColorState::ColorData::ColorData(const ColorData& src)
-    : m_FillRGB(src.m_FillRGB), m_StrokeRGB(src.m_StrokeRGB) {
-  m_FillColor.Copy(&src.m_FillColor);
-  m_StrokeColor.Copy(&src.m_StrokeColor);
-}
+    : m_FillColorRef(src.m_FillColorRef),
+      m_StrokeColorRef(src.m_StrokeColorRef),
+      m_FillColor(src.m_FillColor),
+      m_StrokeColor(src.m_StrokeColor) {}
 
-CPDF_ColorState::ColorData::~ColorData() {}
+CPDF_ColorState::ColorData::~ColorData() = default;
 
 void CPDF_ColorState::ColorData::SetDefault() {
-  m_FillRGB = 0;
-  m_StrokeRGB = 0;
+  m_FillColorRef = 0;
+  m_StrokeColorRef = 0;
   m_FillColor.SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY));
   m_StrokeColor.SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY));
 }
+
+RetainPtr<CPDF_ColorState::ColorData> CPDF_ColorState::ColorData::Clone()
+    const {
+  return pdfium::MakeRetain<CPDF_ColorState::ColorData>(*this);
+}
diff --git a/core/fpdfapi/page/cpdf_colorstate.h b/core/fpdfapi/page/cpdf_colorstate.h
index 9619051..f0f6ebd 100644
--- a/core/fpdfapi/page/cpdf_colorstate.h
+++ b/core/fpdfapi/page/cpdf_colorstate.h
@@ -7,9 +7,13 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_COLORSTATE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_COLORSTATE_H_
 
+#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"
 
 class CPDF_Color;
 class CPDF_ColorSpace;
@@ -24,11 +28,11 @@
   void Emplace();
   void SetDefault();
 
-  uint32_t GetFillRGB() const;
-  void SetFillRGB(uint32_t rgb);
+  FX_COLORREF GetFillColorRef() const;
+  void SetFillColorRef(FX_COLORREF colorref);
 
-  uint32_t GetStrokeRGB() const;
-  void SetStrokeRGB(uint32_t rgb);
+  FX_COLORREF GetStrokeColorRef() const;
+  void SetStrokeColorRef(FX_COLORREF colorref);
 
   const CPDF_Color* GetFillColor() const;
   CPDF_Color* GetMutableFillColor();
@@ -38,33 +42,46 @@
   CPDF_Color* GetMutableStrokeColor();
   bool HasStrokeColor() const;
 
-  void SetFillColor(CPDF_ColorSpace* pCS, float* pValue, uint32_t nValues);
-  void SetStrokeColor(CPDF_ColorSpace* pCS, float* pValue, uint32_t nValues);
-  void SetFillPattern(CPDF_Pattern* pattern, float* pValue, uint32_t nValues);
-  void SetStrokePattern(CPDF_Pattern* pattern, float* pValue, uint32_t nValues);
+  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);
 
   bool HasRef() const { return !!m_Ref; }
 
  private:
-  class ColorData : public Retainable {
+  class ColorData final : public Retainable {
    public:
-    ColorData();
-    ColorData(const ColorData& src);
-    ~ColorData() override;
+    template <typename T, typename... Args>
+    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+    RetainPtr<ColorData> Clone() const;
 
     void SetDefault();
 
-    uint32_t m_FillRGB;
-    uint32_t m_StrokeRGB;
+    FX_COLORREF m_FillColorRef = 0;
+    FX_COLORREF m_StrokeColorRef = 0;
     CPDF_Color m_FillColor;
     CPDF_Color m_StrokeColor;
+
+   private:
+    ColorData();
+    ColorData(const ColorData& src);
+    ~ColorData() override;
   };
 
-  void SetColor(CPDF_Color& color,
-                uint32_t& rgb,
-                CPDF_ColorSpace* pCS,
-                float* pValue,
-                uint32_t nValues);
+  void SetColor(const RetainPtr<CPDF_ColorSpace>& pCS,
+                const std::vector<float>& values,
+                CPDF_Color* color,
+                FX_COLORREF* colorref);
+  void SetPattern(const RetainPtr<CPDF_Pattern>& pPattern,
+                  const std::vector<float>& values,
+                  CPDF_Color* color,
+                  FX_COLORREF* colorref);
 
   SharedCopyOnWrite<ColorData> m_Ref;
 };
diff --git a/core/fpdfapi/page/cpdf_contentmark.cpp b/core/fpdfapi/page/cpdf_contentmark.cpp
deleted file mode 100644
index c54ef55..0000000
--- a/core/fpdfapi/page/cpdf_contentmark.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fpdfapi/page/cpdf_contentmark.h"
-
-#include <utility>
-
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-
-CPDF_ContentMark::CPDF_ContentMark() {}
-
-CPDF_ContentMark::CPDF_ContentMark(const CPDF_ContentMark& that)
-    : m_Ref(that.m_Ref) {}
-
-CPDF_ContentMark::~CPDF_ContentMark() {}
-
-size_t CPDF_ContentMark::CountItems() const {
-  return m_Ref.GetObject()->CountItems();
-}
-
-const CPDF_ContentMarkItem& CPDF_ContentMark::GetItem(size_t i) const {
-  return m_Ref.GetObject()->GetItem(i);
-}
-
-int CPDF_ContentMark::GetMarkedContentID() const {
-  const MarkData* pData = m_Ref.GetObject();
-  return pData ? pData->GetMarkedContentID() : -1;
-}
-
-void CPDF_ContentMark::AddMark(const ByteString& name,
-                               CPDF_Dictionary* pDict,
-                               bool bDirect) {
-  m_Ref.GetPrivateCopy()->AddMark(name, pDict, bDirect);
-}
-
-void CPDF_ContentMark::DeleteLastMark() {
-  m_Ref.GetPrivateCopy()->DeleteLastMark();
-  if (CountItems() == 0)
-    m_Ref.SetNull();
-}
-
-CPDF_ContentMark::MarkData::MarkData() {}
-
-CPDF_ContentMark::MarkData::MarkData(const MarkData& src)
-    : m_Marks(src.m_Marks) {}
-
-CPDF_ContentMark::MarkData::~MarkData() {}
-
-size_t CPDF_ContentMark::MarkData::CountItems() const {
-  return m_Marks.size();
-}
-
-const CPDF_ContentMarkItem& CPDF_ContentMark::MarkData::GetItem(
-    size_t index) const {
-  return m_Marks[index];
-}
-
-int CPDF_ContentMark::MarkData::GetMarkedContentID() const {
-  for (const auto& mark : m_Marks) {
-    const CPDF_Dictionary* pDict = mark.GetParam();
-    if (pDict && pDict->KeyExist("MCID"))
-      return pDict->GetIntegerFor("MCID");
-  }
-  return -1;
-}
-
-void CPDF_ContentMark::MarkData::AddMark(const ByteString& name,
-                                         CPDF_Dictionary* pDict,
-                                         bool bDirect) {
-  CPDF_ContentMarkItem item;
-  item.SetName(name);
-  if (pDict) {
-    if (bDirect)
-      item.SetDirectDict(ToDictionary(pDict->Clone()));
-    else
-      item.SetPropertiesDict(pDict);
-  }
-  m_Marks.push_back(std::move(item));
-}
-
-void CPDF_ContentMark::MarkData::DeleteLastMark() {
-  if (!m_Marks.empty())
-    m_Marks.pop_back();
-}
diff --git a/core/fpdfapi/page/cpdf_contentmark.h b/core/fpdfapi/page/cpdf_contentmark.h
deleted file mode 100644
index 78a9486..0000000
--- a/core/fpdfapi/page/cpdf_contentmark.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_PAGE_CPDF_CONTENTMARK_H_
-#define CORE_FPDFAPI_PAGE_CPDF_CONTENTMARK_H_
-
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_contentmarkitem.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/shared_copy_on_write.h"
-
-class CPDF_Dictionary;
-
-class CPDF_ContentMark {
- public:
-  CPDF_ContentMark();
-  CPDF_ContentMark(const CPDF_ContentMark& that);
-  ~CPDF_ContentMark();
-
-  int GetMarkedContentID() const;
-  size_t CountItems() const;
-  const CPDF_ContentMarkItem& GetItem(size_t i) const;
-
-  void AddMark(const ByteString& name, CPDF_Dictionary* pDict, bool bDirect);
-  void DeleteLastMark();
-
-  bool HasRef() const { return !!m_Ref; }
-
- private:
-  class MarkData : public Retainable {
-   public:
-    MarkData();
-    MarkData(const MarkData& src);
-    ~MarkData() override;
-
-    size_t CountItems() const;
-    const CPDF_ContentMarkItem& GetItem(size_t index) const;
-
-    int GetMarkedContentID() const;
-    void AddMark(const ByteString& name,
-                 CPDF_Dictionary* pDict,
-                 bool bDictNeedClone);
-    void DeleteLastMark();
-
-   private:
-    std::vector<CPDF_ContentMarkItem> m_Marks;
-  };
-
-  SharedCopyOnWrite<MarkData> m_Ref;
-};
-
-#endif  // CORE_FPDFAPI_PAGE_CPDF_CONTENTMARK_H_
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.cpp b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
index de720b3..a361e5b 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.cpp
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
@@ -10,43 +10,42 @@
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 
-CPDF_ContentMarkItem::CPDF_ContentMarkItem()
-    : m_ParamType(None), m_pPropertiesDict(nullptr) {}
-
-CPDF_ContentMarkItem::CPDF_ContentMarkItem(const CPDF_ContentMarkItem& that)
-    : m_MarkName(that.m_MarkName),
-      m_ParamType(that.m_ParamType),
-      m_pPropertiesDict(that.m_pPropertiesDict) {
-  if (that.m_pDirectDict)
-    m_pDirectDict = ToDictionary(that.m_pDirectDict->Clone());
-}
+CPDF_ContentMarkItem::CPDF_ContentMarkItem(ByteString name)
+    : m_MarkName(std::move(name)) {}
 
 CPDF_ContentMarkItem::~CPDF_ContentMarkItem() {}
 
-CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() const {
+const CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() const {
   switch (m_ParamType) {
-    case PropertiesDict:
-      return m_pPropertiesDict.Get();
-    case DirectDict:
-      return m_pDirectDict.get();
-    case None:
+    case kPropertiesDict:
+      return m_pPropertiesHolder->GetDictFor(m_PropertyName);
+    case kDirectDict:
+      return m_pDirectDict.Get();
+    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 {
-  CPDF_Dictionary* pDict = GetParam();
+  const CPDF_Dictionary* pDict = GetParam();
   return pDict && pDict->KeyExist("MCID");
 }
 
-void CPDF_ContentMarkItem::SetDirectDict(
-    std::unique_ptr<CPDF_Dictionary> pDict) {
-  m_ParamType = DirectDict;
+void CPDF_ContentMarkItem::SetDirectDict(RetainPtr<CPDF_Dictionary> pDict) {
+  m_ParamType = kDirectDict;
   m_pDirectDict = std::move(pDict);
 }
 
-void CPDF_ContentMarkItem::SetPropertiesDict(CPDF_Dictionary* pDict) {
-  m_ParamType = PropertiesDict;
-  m_pPropertiesDict = pDict;
+void CPDF_ContentMarkItem::SetPropertiesHolder(
+    CPDF_Dictionary* pHolder,
+    const ByteString& property_name) {
+  m_ParamType = kPropertiesDict;
+  m_pPropertiesHolder.Reset(pHolder);
+  m_PropertyName = property_name;
 }
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.h b/core/fpdfapi/page/cpdf_contentmarkitem.h
index ea89606..15a34e9 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.h
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.h
@@ -9,37 +9,36 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
 
-class CPDF_ContentMarkItem {
+class CPDF_ContentMarkItem final : public Retainable {
  public:
-  enum ParamType { None, PropertiesDict, DirectDict };
+  enum ParamType { kNone, kPropertiesDict, kDirectDict };
 
-  CPDF_ContentMarkItem();
-  CPDF_ContentMarkItem(const CPDF_ContentMarkItem& that);
-  ~CPDF_ContentMarkItem();
+  explicit CPDF_ContentMarkItem(ByteString name);
+  ~CPDF_ContentMarkItem() override;
 
-  CPDF_ContentMarkItem& operator=(CPDF_ContentMarkItem&& other) = default;
-
-  ByteString GetName() const { return m_MarkName; }
+  const ByteString& GetName() const { return m_MarkName; }
   ParamType GetParamType() const { return m_ParamType; }
-  CPDF_Dictionary* GetParam() const;
+  const CPDF_Dictionary* GetParam() const;
+  CPDF_Dictionary* GetParam();
+  const ByteString& GetPropertyName() const { return m_PropertyName; }
   bool HasMCID() const;
 
-  void SetName(const ByteString& name) { m_MarkName = name; }
-  void SetDirectDict(std::unique_ptr<CPDF_Dictionary> pDict);
-  void SetPropertiesDict(CPDF_Dictionary* pDict);
+  void SetDirectDict(RetainPtr<CPDF_Dictionary> pDict);
+  void SetPropertiesHolder(CPDF_Dictionary* pHolder,
+                           const ByteString& property_name);
 
  private:
+  ParamType m_ParamType = kNone;
   ByteString m_MarkName;
-  ParamType m_ParamType;
-  UnownedPtr<CPDF_Dictionary> m_pPropertiesDict;
-  std::unique_ptr<CPDF_Dictionary> m_pDirectDict;
+  ByteString m_PropertyName;
+  RetainPtr<CPDF_Dictionary> m_pPropertiesHolder;
+  RetainPtr<CPDF_Dictionary> m_pDirectDict;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKITEM_H_
diff --git a/core/fpdfapi/page/cpdf_contentmarks.cpp b/core/fpdfapi/page/cpdf_contentmarks.cpp
new file mode 100644
index 0000000..0b95ac5
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_contentmarks.cpp
@@ -0,0 +1,172 @@
+// 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/page/cpdf_contentmarks.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "third_party/base/ptr_util.h"
+
+CPDF_ContentMarks::CPDF_ContentMarks() {}
+
+CPDF_ContentMarks::~CPDF_ContentMarks() {}
+
+std::unique_ptr<CPDF_ContentMarks> CPDF_ContentMarks::Clone() {
+  auto result = pdfium::MakeUnique<CPDF_ContentMarks>();
+  if (m_pMarkData)
+    result->m_pMarkData = pdfium::MakeRetain<MarkData>(*m_pMarkData);
+  return result;
+}
+
+size_t CPDF_ContentMarks::CountItems() const {
+  return m_pMarkData ? m_pMarkData->CountItems() : 0;
+}
+
+bool CPDF_ContentMarks::ContainsItem(const CPDF_ContentMarkItem* pItem) const {
+  return m_pMarkData && m_pMarkData->ContainsItem(pItem);
+}
+
+CPDF_ContentMarkItem* CPDF_ContentMarks::GetItem(size_t index) {
+  return const_cast<CPDF_ContentMarkItem*>(
+      static_cast<const CPDF_ContentMarks*>(this)->GetItem(index));
+}
+
+const CPDF_ContentMarkItem* CPDF_ContentMarks::GetItem(size_t index) const {
+  ASSERT(index < CountItems());
+  return m_pMarkData->GetItem(index);
+}
+
+int CPDF_ContentMarks::GetMarkedContentID() const {
+  return m_pMarkData ? m_pMarkData->GetMarkedContentID() : -1;
+}
+
+void CPDF_ContentMarks::AddMark(ByteString name) {
+  EnsureMarkDataExists();
+  m_pMarkData->AddMark(std::move(name));
+}
+
+void CPDF_ContentMarks::AddMarkWithDirectDict(ByteString name,
+                                              CPDF_Dictionary* pDict) {
+  EnsureMarkDataExists();
+  m_pMarkData->AddMarkWithDirectDict(std::move(name), pDict);
+}
+
+void CPDF_ContentMarks::AddMarkWithPropertiesHolder(
+    const ByteString& name,
+    CPDF_Dictionary* pDict,
+    const ByteString& property_name) {
+  EnsureMarkDataExists();
+  m_pMarkData->AddMarkWithPropertiesHolder(name, pDict, property_name);
+}
+
+bool CPDF_ContentMarks::RemoveMark(CPDF_ContentMarkItem* pMarkItem) {
+  return m_pMarkData && m_pMarkData->RemoveMark(pMarkItem);
+}
+
+void CPDF_ContentMarks::EnsureMarkDataExists() {
+  if (!m_pMarkData)
+    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)
+    return CountItems();
+
+  size_t min_len = std::min(CountItems(), other->CountItems());
+
+  for (size_t i = 0; i < min_len; ++i) {
+    if (GetItem(i) != other->GetItem(i))
+      return i;
+  }
+  return min_len;
+}
+
+CPDF_ContentMarks::MarkData::MarkData() {}
+
+CPDF_ContentMarks::MarkData::MarkData(const MarkData& src)
+    : m_Marks(src.m_Marks) {}
+
+CPDF_ContentMarks::MarkData::~MarkData() {}
+
+size_t CPDF_ContentMarks::MarkData::CountItems() const {
+  return m_Marks.size();
+}
+
+bool CPDF_ContentMarks::MarkData::ContainsItem(
+    const CPDF_ContentMarkItem* pItem) const {
+  for (const auto pMark : m_Marks) {
+    if (pMark == pItem)
+      return true;
+  }
+  return false;
+}
+
+CPDF_ContentMarkItem* CPDF_ContentMarks::MarkData::GetItem(size_t index) {
+  return m_Marks[index].Get();
+}
+
+const CPDF_ContentMarkItem* CPDF_ContentMarks::MarkData::GetItem(
+    size_t index) const {
+  return m_Marks[index].Get();
+}
+
+int CPDF_ContentMarks::MarkData::GetMarkedContentID() const {
+  for (const auto pMark : m_Marks) {
+    const CPDF_Dictionary* pDict = pMark->GetParam();
+    if (pDict && pDict->KeyExist("MCID"))
+      return pDict->GetIntegerFor("MCID");
+  }
+  return -1;
+}
+
+void CPDF_ContentMarks::MarkData::AddMark(ByteString name) {
+  auto pItem = pdfium::MakeRetain<CPDF_ContentMarkItem>(std::move(name));
+  m_Marks.push_back(pItem);
+}
+
+void CPDF_ContentMarks::MarkData::AddMarkWithDirectDict(
+    ByteString name,
+    CPDF_Dictionary* pDict) {
+  auto pItem = pdfium::MakeRetain<CPDF_ContentMarkItem>(std::move(name));
+  pItem->SetDirectDict(ToDictionary(pDict->Clone()));
+  m_Marks.push_back(pItem);
+}
+
+void CPDF_ContentMarks::MarkData::AddMarkWithPropertiesHolder(
+    const ByteString& name,
+    CPDF_Dictionary* pDict,
+    const ByteString& property_name) {
+  auto pItem = pdfium::MakeRetain<CPDF_ContentMarkItem>(name);
+  pItem->SetPropertiesHolder(pDict, property_name);
+  m_Marks.push_back(pItem);
+}
+
+bool CPDF_ContentMarks::MarkData::RemoveMark(CPDF_ContentMarkItem* pMarkItem) {
+  for (auto it = m_Marks.begin(); it != m_Marks.end(); ++it) {
+    if (*it == pMarkItem) {
+      m_Marks.erase(it);
+      return true;
+    }
+  }
+  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
new file mode 100644
index 0000000..7bb25ec
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_contentmarks.h
@@ -0,0 +1,72 @@
+// 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_PAGE_CPDF_CONTENTMARKS_H_
+#define CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKS_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;
+
+class CPDF_ContentMarks {
+ public:
+  CPDF_ContentMarks();
+  ~CPDF_ContentMarks();
+
+  std::unique_ptr<CPDF_ContentMarks> Clone();
+  int GetMarkedContentID() const;
+  size_t CountItems() const;
+  bool ContainsItem(const CPDF_ContentMarkItem* pItem) const;
+
+  // The returned pointer is never null.
+  CPDF_ContentMarkItem* GetItem(size_t index);
+  const CPDF_ContentMarkItem* GetItem(size_t index) const;
+
+  void AddMark(ByteString name);
+  void AddMarkWithDirectDict(ByteString name, CPDF_Dictionary* pDict);
+  void AddMarkWithPropertiesHolder(const ByteString& name,
+                                   CPDF_Dictionary* pDict,
+                                   const ByteString& property_name);
+  bool RemoveMark(CPDF_ContentMarkItem* pMarkItem);
+  void DeleteLastMark();
+  size_t FindFirstDifference(const CPDF_ContentMarks* other) const;
+
+ private:
+  class MarkData final : public Retainable {
+   public:
+    MarkData();
+    MarkData(const MarkData& src);
+    ~MarkData() override;
+
+    size_t CountItems() const;
+    bool ContainsItem(const CPDF_ContentMarkItem* pItem) const;
+    CPDF_ContentMarkItem* GetItem(size_t index);
+    const CPDF_ContentMarkItem* GetItem(size_t index) const;
+
+    int GetMarkedContentID() const;
+    void AddMark(ByteString name);
+    void AddMarkWithDirectDict(ByteString name, CPDF_Dictionary* pDict);
+    void AddMarkWithPropertiesHolder(const ByteString& name,
+                                     CPDF_Dictionary* pDict,
+                                     const ByteString& property_name);
+    bool RemoveMark(CPDF_ContentMarkItem* pMarkItem);
+    void DeleteLastMark();
+
+   private:
+    std::vector<RetainPtr<CPDF_ContentMarkItem>> m_Marks;
+  };
+
+  void EnsureMarkDataExists();
+
+  RetainPtr<MarkData> m_pMarkData;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKS_H_
diff --git a/core/fpdfapi/page/cpdf_contentparser.cpp b/core/fpdfapi/page/cpdf_contentparser.cpp
index 93151af..5b3efe1 100644
--- a/core/fpdfapi/page/cpdf_contentparser.cpp
+++ b/core/fpdfapi/page/cpdf_contentparser.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_contentparser.h"
 
+#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"
@@ -17,91 +18,86 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/ifx_pauseindicator.h"
+#include "core/fxcrt/pauseindicator_iface.h"
+#include "core/fxge/render_defines.h"
 #include "third_party/base/ptr_util.h"
 
-#define PARSE_STEP_LIMIT 100
-
 CPDF_ContentParser::CPDF_ContentParser(CPDF_Page* pPage)
-    : m_InternalStage(STAGE_GETCONTENT), m_pObjectHolder(pPage) {
-  if (!pPage || !pPage->m_pDocument || !pPage->m_pFormDict) {
-    m_bIsDone = true;
+    : m_CurrentStage(Stage::kGetContent), m_pObjectHolder(pPage) {
+  ASSERT(pPage);
+  if (!pPage->GetDocument()) {
+    m_CurrentStage = Stage::kComplete;
     return;
   }
 
-  CPDF_Object* pContent = pPage->m_pFormDict->GetDirectObjectFor("Contents");
+  CPDF_Object* pContent =
+      pPage->GetDict()->GetDirectObjectFor(pdfium::page_object::kContents);
   if (!pContent) {
-    m_bIsDone = true;
+    HandlePageContentFailure();
     return;
   }
+
   CPDF_Stream* pStream = pContent->AsStream();
   if (pStream) {
-    m_pSingleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-    m_pSingleStream->LoadAllDataFiltered();
+    HandlePageContentStream(pStream);
     return;
   }
+
   CPDF_Array* pArray = pContent->AsArray();
-  if (!pArray) {
-    m_bIsDone = true;
+  if (pArray && HandlePageContentArray(pArray))
     return;
-  }
-  m_nStreams = pArray->GetCount();
-  if (!m_nStreams) {
-    m_bIsDone = true;
-    return;
-  }
-  m_StreamArray.resize(m_nStreams);
+
+  HandlePageContentFailure();
 }
 
 CPDF_ContentParser::CPDF_ContentParser(CPDF_Form* pForm,
-                                       CPDF_AllStates* pGraphicStates,
+                                       const CPDF_AllStates* pGraphicStates,
                                        const CFX_Matrix* pParentMatrix,
                                        CPDF_Type3Char* pType3Char,
-                                       std::set<const uint8_t*>* parsedSet)
-    : m_InternalStage(STAGE_PARSE),
+                                       std::set<const uint8_t*>* pParsedSet)
+    : m_CurrentStage(Stage::kParse),
       m_pObjectHolder(pForm),
       m_pType3Char(pType3Char) {
-  CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrixFor("Matrix");
+  ASSERT(pForm);
+  CFX_Matrix form_matrix = pForm->GetDict()->GetMatrixFor("Matrix");
   if (pGraphicStates)
     form_matrix.Concat(pGraphicStates->m_CTM);
 
-  CPDF_Array* pBBox = pForm->m_pFormDict->GetArrayFor("BBox");
+  CPDF_Array* pBBox = pForm->GetDict()->GetArrayFor("BBox");
   CFX_FloatRect form_bbox;
   CPDF_Path ClipPath;
   if (pBBox) {
     form_bbox = pBBox->GetRect();
     ClipPath.Emplace();
-    ClipPath.AppendRect(form_bbox.left, form_bbox.bottom, form_bbox.right,
-                        form_bbox.top);
-    ClipPath.Transform(&form_matrix);
+    ClipPath.AppendFloatRect(form_bbox);
+    ClipPath.Transform(form_matrix);
     if (pParentMatrix)
-      ClipPath.Transform(pParentMatrix);
+      ClipPath.Transform(*pParentMatrix);
 
     form_bbox = form_matrix.TransformRect(form_bbox);
     if (pParentMatrix)
       form_bbox = pParentMatrix->TransformRect(form_bbox);
   }
 
-  CPDF_Dictionary* pResources = pForm->m_pFormDict->GetDictFor("Resources");
+  CPDF_Dictionary* pResources = pForm->GetDict()->GetDictFor("Resources");
   m_pParser = pdfium::MakeUnique<CPDF_StreamContentParser>(
-      pForm->m_pDocument.Get(), pForm->m_pPageResources.Get(),
+      pForm->GetDocument(), pForm->m_pPageResources.Get(),
       pForm->m_pResources.Get(), pParentMatrix, pForm, pResources, form_bbox,
-      pGraphicStates, parsedSet);
+      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);
   }
-  if (pForm->m_iTransparency & PDFTRANS_GROUP) {
+  if (pForm->GetTransparency().IsGroup()) {
     CPDF_GeneralState* pState = &m_pParser->GetCurStates()->m_GeneralState;
-    pState->SetBlendType(FXDIB_BLEND_NORMAL);
+    pState->SetBlendType(BlendMode::kNormal);
     pState->SetStrokeAlpha(1.0f);
     pState->SetFillAlpha(1.0f);
     pState->SetSoftMask(nullptr);
   }
-  m_pSingleStream =
-      pdfium::MakeRetain<CPDF_StreamAcc>(pForm->m_pFormStream.Get());
+  m_pSingleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pForm->GetStream());
   m_pSingleStream->LoadAllDataFiltered();
   m_pData.Reset(m_pSingleStream->GetData());
   m_Size = m_pSingleStream->GetSize();
@@ -109,99 +105,146 @@
 
 CPDF_ContentParser::~CPDF_ContentParser() {}
 
-bool CPDF_ContentParser::Continue(IFX_PauseIndicator* pPause) {
-  if (m_bIsDone)
+// Returning |true| means that there is more content to be processed and
+// Continue() should be called again. Returning |false| means that we've
+// completed the parse and Continue() is complete.
+bool CPDF_ContentParser::Continue(PauseIndicatorIface* pPause) {
+  while (m_CurrentStage == Stage::kGetContent) {
+    m_CurrentStage = GetContent();
+    if (pPause && pPause->NeedToPauseNow())
+      return true;
+  }
+
+  if (m_CurrentStage == Stage::kPrepareContent)
+    m_CurrentStage = PrepareContent();
+
+  while (m_CurrentStage == Stage::kParse) {
+    m_CurrentStage = Parse();
+    if (pPause && pPause->NeedToPauseNow())
+      return true;
+  }
+
+  if (m_CurrentStage == Stage::kCheckClip)
+    m_CurrentStage = CheckClip();
+
+  ASSERT(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(
+      pContent ? pContent->GetDirectObjectAt(m_CurrentOffset) : nullptr);
+  m_StreamArray[m_CurrentOffset] =
+      pdfium::MakeRetain<CPDF_StreamAcc>(pStreamObj);
+  m_StreamArray[m_CurrentOffset]->LoadAllDataFiltered();
+  m_CurrentOffset++;
+
+  return m_CurrentOffset == m_nStreams ? Stage::kPrepareContent
+                                       : Stage::kGetContent;
+}
+
+CPDF_ContentParser::Stage CPDF_ContentParser::PrepareContent() {
+  m_CurrentOffset = 0;
+
+  if (m_StreamArray.empty()) {
+    m_pData.Reset(m_pSingleStream->GetData());
+    m_Size = m_pSingleStream->GetSize();
+    return Stage::kParse;
+  }
+
+  FX_SAFE_UINT32 safeSize = 0;
+  for (const auto& stream : m_StreamArray) {
+    m_StreamSegmentOffsets.push_back(safeSize.ValueOrDie());
+
+    safeSize += stream->GetSize();
+    safeSize += 1;
+    if (!safeSize.IsValid())
+      return Stage::kComplete;
+  }
+
+  m_Size = safeSize.ValueOrDie();
+  m_pData.Reset(
+      std::unique_ptr<uint8_t, FxFreeDeleter>(FX_Alloc(uint8_t, m_Size)));
+
+  uint32_t pos = 0;
+  for (const auto& stream : m_StreamArray) {
+    memcpy(m_pData.Get() + pos, stream->GetData(), stream->GetSize());
+    pos += stream->GetSize();
+    m_pData.Get()[pos++] = ' ';
+  }
+  m_StreamArray.clear();
+
+  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_pParser->GetCurStates()->m_ColorState.SetDefault();
+  }
+  if (m_CurrentOffset >= m_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,
+                                      kParseStepLimit, m_StreamSegmentOffsets);
+  return Stage::kParse;
+}
+
+CPDF_ContentParser::Stage CPDF_ContentParser::CheckClip() {
+  if (m_pType3Char) {
+    m_pType3Char->InitializeFromStreamData(m_pParser->IsColored(),
+                                           m_pParser->GetType3Data());
+  }
+
+  for (auto& pObj : *m_pObjectHolder) {
+    if (!pObj->m_ClipPath.HasRef())
+      continue;
+    if (pObj->m_ClipPath.GetPathCount() != 1)
+      continue;
+    if (pObj->m_ClipPath.GetTextCount() > 0)
+      continue;
+
+    CPDF_Path ClipPath = pObj->m_ClipPath.GetPath(0);
+    if (!ClipPath.IsRect() || pObj->IsShading())
+      continue;
+
+    CFX_PointF point0 = ClipPath.GetPoint(0);
+    CFX_PointF point2 = ClipPath.GetPoint(2);
+    CFX_FloatRect old_rect(point0.x, point0.y, point2.x, point2.y);
+    if (old_rect.Contains(pObj->GetRect()))
+      pObj->m_ClipPath.SetNull();
+  }
+  return Stage::kComplete;
+}
+
+void CPDF_ContentParser::HandlePageContentStream(CPDF_Stream* pStream) {
+  m_pSingleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+  m_pSingleStream->LoadAllDataFiltered();
+  m_CurrentStage = Stage::kPrepareContent;
+}
+
+bool CPDF_ContentParser::HandlePageContentArray(CPDF_Array* pArray) {
+  m_nStreams = pArray->size();
+  if (m_nStreams == 0)
     return false;
 
-  while (!m_bIsDone) {
-    if (m_InternalStage == STAGE_GETCONTENT) {
-      if (m_CurrentOffset == m_nStreams) {
-        if (!m_StreamArray.empty()) {
-          FX_SAFE_UINT32 safeSize = 0;
-          for (const auto& stream : m_StreamArray) {
-            safeSize += stream->GetSize();
-            safeSize += 1;
-          }
-          if (!safeSize.IsValid()) {
-            m_bIsDone = true;
-            return false;
-          }
-          m_Size = safeSize.ValueOrDie();
-          m_pData.Reset(std::unique_ptr<uint8_t, FxFreeDeleter>(
-              FX_Alloc(uint8_t, m_Size)));
-          uint32_t pos = 0;
-          for (const auto& stream : m_StreamArray) {
-            memcpy(m_pData.Get() + pos, stream->GetData(), stream->GetSize());
-            pos += stream->GetSize();
-            m_pData.Get()[pos++] = ' ';
-          }
-          m_StreamArray.clear();
-        } else {
-          m_pData.Reset(m_pSingleStream->GetData());
-          m_Size = m_pSingleStream->GetSize();
-        }
-        m_InternalStage = STAGE_PARSE;
-        m_CurrentOffset = 0;
-      } else {
-        CPDF_Array* pContent =
-            m_pObjectHolder->m_pFormDict->GetArrayFor("Contents");
-        CPDF_Stream* pStreamObj = ToStream(
-            pContent ? pContent->GetDirectObjectAt(m_CurrentOffset) : nullptr);
-        m_StreamArray[m_CurrentOffset] =
-            pdfium::MakeRetain<CPDF_StreamAcc>(pStreamObj);
-        m_StreamArray[m_CurrentOffset]->LoadAllDataFiltered();
-        m_CurrentOffset++;
-      }
-    }
-    if (m_InternalStage == STAGE_PARSE) {
-      if (!m_pParser) {
-        m_parsedSet = pdfium::MakeUnique<std::set<const uint8_t*>>();
-        m_pParser = pdfium::MakeUnique<CPDF_StreamContentParser>(
-            m_pObjectHolder->m_pDocument.Get(),
-            m_pObjectHolder->m_pPageResources.Get(), nullptr, nullptr,
-            m_pObjectHolder.Get(), m_pObjectHolder->m_pResources.Get(),
-            m_pObjectHolder->m_BBox, nullptr, m_parsedSet.get());
-        m_pParser->GetCurStates()->m_ColorState.SetDefault();
-      }
-      if (m_CurrentOffset >= m_Size) {
-        m_InternalStage = STAGE_CHECKCLIP;
-      } else {
-        m_CurrentOffset +=
-            m_pParser->Parse(m_pData.Get() + m_CurrentOffset,
-                             m_Size - m_CurrentOffset, PARSE_STEP_LIMIT);
-      }
-    }
-    if (m_InternalStage == STAGE_CHECKCLIP) {
-      if (m_pType3Char) {
-        m_pType3Char->InitializeFromStreamData(m_pParser->IsColored(),
-                                               m_pParser->GetType3Data());
-      }
-
-      for (auto& pObj : *m_pObjectHolder->GetPageObjectList()) {
-        if (!pObj->m_ClipPath.HasRef())
-          continue;
-        if (pObj->m_ClipPath.GetPathCount() != 1)
-          continue;
-        if (pObj->m_ClipPath.GetTextCount() > 0)
-          continue;
-
-        CPDF_Path ClipPath = pObj->m_ClipPath.GetPath(0);
-        if (!ClipPath.IsRect() || pObj->IsShading())
-          continue;
-
-        CFX_PointF point0 = ClipPath.GetPoint(0);
-        CFX_PointF point2 = ClipPath.GetPoint(2);
-        CFX_FloatRect old_rect(point0.x, point0.y, point2.x, point2.y);
-        CFX_FloatRect obj_rect(pObj->m_Left, pObj->m_Bottom, pObj->m_Right,
-                               pObj->m_Top);
-        if (old_rect.Contains(obj_rect))
-          pObj->m_ClipPath.SetNull();
-      }
-      m_bIsDone = true;
-      return false;
-    }
-    if (pPause && pPause->NeedToPauseNow())
-      break;
-  }
+  m_StreamArray.resize(m_nStreams);
   return true;
 }
+
+void CPDF_ContentParser::HandlePageContentFailure() {
+  m_CurrentStage = Stage::kComplete;
+}
diff --git a/core/fpdfapi/page/cpdf_contentparser.h b/core/fpdfapi/page/cpdf_contentparser.h
index c65da6d..2f9a44b 100644
--- a/core/fpdfapi/page/cpdf_contentparser.h
+++ b/core/fpdfapi/page/cpdf_contentparser.h
@@ -11,56 +11,70 @@
 #include <set>
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 #include "core/fpdfapi/page/cpdf_streamcontentparser.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/maybe_owned.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_AllStates;
+class CPDF_Array;
 class CPDF_Form;
 class CPDF_Page;
+class CPDF_PageObjectHolder;
+class CPDF_Stream;
 class CPDF_StreamAcc;
 class CPDF_Type3Char;
+class PauseIndicatorIface;
 
 class CPDF_ContentParser {
  public:
   explicit CPDF_ContentParser(CPDF_Page* pPage);
   CPDF_ContentParser(CPDF_Form* pForm,
-                     CPDF_AllStates* pGraphicStates,
+                     const CPDF_AllStates* pGraphicStates,
                      const CFX_Matrix* pParentMatrix,
                      CPDF_Type3Char* pType3Char,
-                     std::set<const uint8_t*>* parsedSet);
+                     std::set<const uint8_t*>* pParsedSet);
   ~CPDF_ContentParser();
 
   const CPDF_AllStates* GetCurStates() const {
     return m_pParser ? m_pParser->GetCurStates() : nullptr;
   }
   // Returns whether to continue or not.
-  bool Continue(IFX_PauseIndicator* pPause);
+  bool Continue(PauseIndicatorIface* pPause);
 
  private:
-  enum InternalStage {
-    STAGE_GETCONTENT = 1,
-    STAGE_PARSE,
-    STAGE_CHECKCLIP,
+  enum class Stage : uint8_t {
+    kGetContent = 1,
+    kPrepareContent,
+    kParse,
+    kCheckClip,
+    kComplete,
   };
 
-  bool m_bIsDone = false;
-  InternalStage m_InternalStage;
+  Stage GetContent();
+  Stage PrepareContent();
+  Stage Parse();
+  Stage CheckClip();
+
+  void HandlePageContentStream(CPDF_Stream* pStream);
+  bool HandlePageContentArray(CPDF_Array* pArray);
+  void HandlePageContentFailure();
+
+  Stage m_CurrentStage;
   UnownedPtr<CPDF_PageObjectHolder> const m_pObjectHolder;
   UnownedPtr<CPDF_Type3Char> m_pType3Char;  // Only used when parsing forms.
-  uint32_t m_nStreams = 0;
   RetainPtr<CPDF_StreamAcc> m_pSingleStream;
   std::vector<RetainPtr<CPDF_StreamAcc>> m_StreamArray;
+  std::vector<uint32_t> m_StreamSegmentOffsets;
   MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
+  uint32_t m_nStreams = 0;
   uint32_t m_Size = 0;
   uint32_t m_CurrentOffset = 0;
 
   // Only used when parsing pages.
-  std::unique_ptr<std::set<const uint8_t*>> m_parsedSet;
+  std::unique_ptr<std::set<const uint8_t*>> m_pParsedSet;
 
-  // |m_pParser| has a reference to |m_parsedSet|, so must be below and thus
+  // |m_pParser| has a reference to |m_pParsedSet|, so must be below and thus
   // destroyed first.
   std::unique_ptr<CPDF_StreamContentParser> m_pParser;
 };
diff --git a/core/fpdfapi/page/cpdf_countedobject.h b/core/fpdfapi/page/cpdf_countedobject.h
deleted file mode 100644
index cb39616..0000000
--- a/core/fpdfapi/page/cpdf_countedobject.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_PAGE_CPDF_COUNTEDOBJECT_H_
-#define CORE_FPDFAPI_PAGE_CPDF_COUNTEDOBJECT_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-
-template <class T>
-class CPDF_CountedObject {
- public:
-  explicit CPDF_CountedObject(std::unique_ptr<T> ptr)
-      : m_nCount(1), m_pObj(ptr.release()) {}
-  void reset(std::unique_ptr<T> ptr) {  // CAUTION: tosses prior ref counts.
-    m_nCount = 1;
-    m_pObj = ptr.release();
-  }
-  void clear() {  // Now you're all weak ptrs ...
-    // Guard against accidental re-entry.
-    T* pObj = m_pObj;
-    m_pObj = nullptr;
-    delete pObj;
-  }
-  T* get() const { return m_pObj; }
-  T* AddRef() {
-    ASSERT(m_pObj);
-    ++m_nCount;
-    return m_pObj;
-  }
-  void RemoveRef() {
-    if (m_nCount)
-      --m_nCount;
-  }
-  size_t use_count() const { return m_nCount; }
-
- protected:
-  size_t m_nCount;
-  T* m_pObj;
-};
-
-#endif  // CORE_FPDFAPI_PAGE_CPDF_COUNTEDOBJECT_H_
diff --git a/core/fpdfapi/page/cpdf_devicecs.cpp b/core/fpdfapi/page/cpdf_devicecs.cpp
index 9eb4123..e557516 100644
--- a/core/fpdfapi/page/cpdf_devicecs.cpp
+++ b/core/fpdfapi/page/cpdf_devicecs.cpp
@@ -16,6 +16,7 @@
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #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"
 
@@ -27,51 +28,27 @@
 
 }  // namespace
 
-uint32_t ComponentsForFamily(int family) {
-  if (family == PDFCS_DEVICERGB)
-    return 3;
-  if (family == PDFCS_DEVICEGRAY)
-    return 1;
-  ASSERT(family == PDFCS_DEVICECMYK);
-  return 4;
-}
-
-void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels) {
-  if (pDestBuf == pSrcBuf) {
-    for (int i = 0; i < pixels; i++) {
-      uint8_t temp = pDestBuf[2];
-      pDestBuf[2] = pDestBuf[0];
-      pDestBuf[0] = temp;
-      pDestBuf += 3;
-    }
-  } else {
-    for (int i = 0; i < pixels; i++) {
-      *pDestBuf++ = pSrcBuf[2];
-      *pDestBuf++ = pSrcBuf[1];
-      *pDestBuf++ = pSrcBuf[0];
-      pSrcBuf += 3;
-    }
-  }
-}
-
 CPDF_DeviceCS::CPDF_DeviceCS(int family) : CPDF_ColorSpace(nullptr, family) {
   ASSERT(family == PDFCS_DEVICEGRAY || family == PDFCS_DEVICERGB ||
          family == PDFCS_DEVICECMYK);
   SetComponentsForStockCS(ComponentsForFamily(GetFamily()));
 }
 
-CPDF_DeviceCS::~CPDF_DeviceCS() {}
+CPDF_DeviceCS::~CPDF_DeviceCS() = default;
 
 uint32_t CPDF_DeviceCS::v_Load(CPDF_Document* pDoc,
-                               CPDF_Array* pArray,
-                               std::set<CPDF_Object*>* pVisited) {
+                               const CPDF_Array* pArray,
+                               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;
 }
 
-bool CPDF_DeviceCS::GetRGB(float* pBuf, float* R, float* G, float* B) const {
+bool CPDF_DeviceCS::GetRGB(const float* pBuf,
+                           float* R,
+                           float* G,
+                           float* B) const {
   switch (m_Family) {
     case PDFCS_DEVICEGRAY:
       *R = NormalizeChannel(*pBuf);
@@ -116,7 +93,7 @@
       }
       break;
     case PDFCS_DEVICERGB:
-      ReverseRGB(pDestBuf, pSrcBuf, pixels);
+      fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
       break;
     case PDFCS_DEVICECMYK:
       if (bTransMask) {
diff --git a/core/fpdfapi/page/cpdf_devicecs.h b/core/fpdfapi/page/cpdf_devicecs.h
index 65e8ec8..c8e256c 100644
--- a/core/fpdfapi/page/cpdf_devicecs.h
+++ b/core/fpdfapi/page/cpdf_devicecs.h
@@ -10,23 +10,29 @@
 #include <set>
 
 #include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fxcrt/retain_ptr.h"
 
-class CPDF_DeviceCS : public CPDF_ColorSpace {
+class CPDF_DeviceCS final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_DeviceCS(int family);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_DeviceCS() override;
 
   // CPDF_ColorSpace:
-  uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
-  bool GetRGB(float* pBuf, float* R, float* G, float* B) const override;
+  bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override;
   void TranslateImageLine(uint8_t* pDestBuf,
                           const uint8_t* pSrcBuf,
                           int pixels,
                           int image_width,
                           int image_height,
                           bool bTransMask) const override;
+  uint32_t v_Load(CPDF_Document* pDoc,
+                  const CPDF_Array* pArray,
+                  std::set<const CPDF_Object*>* pVisited) override;
+
+ private:
+  explicit CPDF_DeviceCS(int 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 a171d01..fbf2558 100644
--- a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
@@ -5,46 +5,48 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/fpdfapi/page/cpdf_devicecs.h"
+
+#include "core/fxcrt/retain_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CPDF_DeviceCSTest, GetRGBFromGray) {
   float R;
   float G;
   float B;
-  CPDF_DeviceCS device_gray(PDFCS_DEVICEGRAY);
+  auto device_gray = pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICEGRAY);
 
   // Test normal values. For gray, only first value from buf should be used.
   float buf[3] = {0.43f, 0.11f, 0.34f};
-  ASSERT_TRUE(device_gray.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.43f, R);
   EXPECT_FLOAT_EQ(0.43f, G);
   EXPECT_FLOAT_EQ(0.43f, B);
   buf[0] = 0.872f;
-  ASSERT_TRUE(device_gray.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.872f, R);
   EXPECT_FLOAT_EQ(0.872f, G);
   EXPECT_FLOAT_EQ(0.872f, B);
 
   // Test boundary values
   buf[0] = {0.0f};
-  ASSERT_TRUE(device_gray.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.0f, R);
   EXPECT_FLOAT_EQ(0.0f, G);
   EXPECT_FLOAT_EQ(0.0f, B);
   buf[0] = 1.0f;
-  ASSERT_TRUE(device_gray.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(1.0f, R);
   EXPECT_FLOAT_EQ(1.0f, G);
   EXPECT_FLOAT_EQ(1.0f, B);
 
   // Test out of range values
   buf[0] = -0.01f;
-  ASSERT_TRUE(device_gray.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.0f, R);
   EXPECT_FLOAT_EQ(0.0f, G);
   EXPECT_FLOAT_EQ(0.0f, B);
   buf[0] = 12.5f;
-  ASSERT_TRUE(device_gray.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(1.0f, R);
   EXPECT_FLOAT_EQ(1.0f, G);
   EXPECT_FLOAT_EQ(1.0f, B);
@@ -54,18 +56,18 @@
   float R;
   float G;
   float B;
-  CPDF_DeviceCS device_rgb(PDFCS_DEVICERGB);
+  auto device_rgb = pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICERGB);
 
   // Test normal values
   float buf[3] = {0.13f, 1.0f, 0.652f};
-  ASSERT_TRUE(device_rgb.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_rgb->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.13f, R);
   EXPECT_FLOAT_EQ(1.0f, G);
   EXPECT_FLOAT_EQ(0.652f, B);
   buf[0] = 0.0f;
   buf[1] = 0.52f;
   buf[2] = 0.78f;
-  ASSERT_TRUE(device_rgb.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_rgb->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.0f, R);
   EXPECT_FLOAT_EQ(0.52f, G);
   EXPECT_FLOAT_EQ(0.78f, B);
@@ -73,7 +75,7 @@
   // Test out of range values
   buf[0] = -10.5f;
   buf[1] = 100.0f;
-  ASSERT_TRUE(device_rgb.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_rgb->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.0f, R);
   EXPECT_FLOAT_EQ(1.0f, G);
   EXPECT_FLOAT_EQ(0.78f, B);
@@ -83,23 +85,23 @@
   float R;
   float G;
   float B;
-  CPDF_DeviceCS device_cmyk(PDFCS_DEVICECMYK);
+  auto device_cmyk = pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICECMYK);
 
   // Test normal values
   float buf[4] = {0.6f, 0.5f, 0.3f, 0.9f};
-  ASSERT_TRUE(device_cmyk.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.0627451f, R);
   EXPECT_FLOAT_EQ(0.0627451f, G);
   EXPECT_FLOAT_EQ(0.10588236f, B);
   buf[0] = 0.15f;
   buf[2] = 0.0f;
-  ASSERT_TRUE(device_cmyk.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.2f, R);
   EXPECT_FLOAT_EQ(0.0862745f, G);
   EXPECT_FLOAT_EQ(0.16470589f, B);
   buf[2] = 1.0f;
   buf[3] = 0.0f;
-  ASSERT_TRUE(device_cmyk.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.85098046f, R);
   EXPECT_FLOAT_EQ(0.552941f, G);
   EXPECT_FLOAT_EQ(0.15686275f, B);
@@ -107,7 +109,7 @@
   // Test out of range values
   buf[2] = 1.5f;
   buf[3] = -0.6f;
-  ASSERT_TRUE(device_cmyk.GetRGB(buf, &R, &G, &B));
+  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
   EXPECT_FLOAT_EQ(0.85098046f, R);
   EXPECT_FLOAT_EQ(0.552941f, G);
   EXPECT_FLOAT_EQ(0.15686275f, B);
diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp
new file mode 100644
index 0000000..6976a0d
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -0,0 +1,1410 @@
+// 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/page/cpdf_dib.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_image.h"
+#include "core/fpdfapi/page/cpdf_imageobject.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/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/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/fx_safe_types.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+constexpr int kMaxImageDimension = 0x01FFFF;
+
+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);
+  unsigned int byte = pData[bitpos / 8];
+  if (nbits == 8)
+    return byte;
+
+  if (nbits == 16)
+    return byte * 256 + pData[bitpos / 8 + 1];
+
+  return (byte >> (8 - nbits - (bitpos % 8))) & ((1 << nbits) - 1);
+}
+
+bool GetBitValue(const uint8_t* pSrc, uint32_t pos) {
+  return pSrc[pos / 8] & (1 << (7 - pos % 8));
+}
+
+// Just to sanity check and filter out obvious bad values.
+bool IsMaybeValidBitsPerComponent(int bpc) {
+  return bpc >= 0 && bpc <= 16;
+}
+
+bool IsAllowedBitsPerComponent(int bpc) {
+  return bpc == 1 || bpc == 2 || bpc == 4 || bpc == 8 || bpc == 16;
+}
+
+bool IsColorIndexOutOfBounds(uint8_t index, const DIB_COMP_DATA& comp_datum) {
+  return index < comp_datum.m_ColorKeyMin || index > comp_datum.m_ColorKeyMax;
+}
+
+bool AreColorIndicesOutOfBounds(const uint8_t* indices,
+                                const DIB_COMP_DATA* comp_data,
+                                size_t count) {
+  for (size_t i = 0; i < count; ++i) {
+    if (IsColorIndexOutOfBounds(indices[i], comp_data[i]))
+      return true;
+  }
+  return false;
+}
+
+int CalculateBitsPerPixel(uint32_t bpc, uint32_t comps) {
+  // TODO(thestig): Can |bpp| be 0 here? Add an ASSERT() or handle it?
+  uint32_t bpp = bpc * comps;
+  if (bpp == 1)
+    return 1;
+  if (bpp <= 8)
+    return 8;
+  return 24;
+}
+
+CJPX_Decoder::ColorSpaceOption ColorSpaceOptionFromColorSpace(
+    CPDF_ColorSpace* pCS) {
+  if (!pCS)
+    return CJPX_Decoder::kNoColorSpace;
+  if (pCS->GetFamily() == PDFCS_INDEXED)
+    return CJPX_Decoder::kIndexedColorSpace;
+  return CJPX_Decoder::kNormalColorSpace;
+}
+
+enum class JpxDecodeAction {
+  kFail,
+  kDoNothing,
+  kUseRgb,
+  kUseCmyk,
+};
+
+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())
+      return JpxDecodeAction::kFail;
+
+    if (pdf_colorspace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB))
+      return JpxDecodeAction::kUseRgb;
+
+    return JpxDecodeAction::kDoNothing;
+  }
+
+  // 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:
+      return JpxDecodeAction::kUseRgb;
+
+    case 4:
+      return JpxDecodeAction::kUseCmyk;
+
+    default:
+      return JpxDecodeAction::kDoNothing;
+  }
+}
+
+}  // namespace
+
+CPDF_DIB::CPDF_DIB() = default;
+
+CPDF_DIB::~CPDF_DIB() = default;
+
+bool CPDF_DIB::Load(CPDF_Document* pDoc, const CPDF_Stream* pStream) {
+  if (!pStream)
+    return false;
+
+  m_pDocument = pDoc;
+  m_pDict.Reset(pStream->GetDict());
+  if (!m_pDict)
+    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;
+}
+
+bool CPDF_DIB::ContinueToLoadMask() {
+  if (m_bImageMask) {
+    SetMaskProperties();
+  } else {
+    if (!m_bpc || !m_nComponents)
+      return false;
+
+    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()));
+  if (m_pColorSpace && m_bStdCS) {
+    m_pColorSpace->EnableStdConversion(true);
+  }
+  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;
+}
+
+CPDF_DIB::LoadState CPDF_DIB::StartLoadDIBBase(
+    CPDF_Document* pDoc,
+    const CPDF_Stream* pStream,
+    bool bHasMask,
+    const CPDF_Dictionary* pFormResources,
+    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);
+  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)) {
+    return LoadState::kFail;
+  }
+  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();
+  if (iCreatedDecoder == LoadState::kFail)
+    return LoadState::kFail;
+
+  if (!ContinueToLoadMask())
+    return LoadState::kFail;
+
+  LoadState iLoadedMask = m_bHasMask ? StartLoadMask() : LoadState::kSuccess;
+  if (iCreatedDecoder == LoadState::kContinue ||
+      iLoadedMask == LoadState::kContinue) {
+    return LoadState::kContinue;
+  }
+
+  ASSERT(iCreatedDecoder == LoadState::kSuccess);
+  ASSERT(iLoadedMask == LoadState::kSuccess);
+  if (m_pColorSpace && m_bStdCS)
+    m_pColorSpace->EnableStdConversion(false);
+  return LoadState::kSuccess;
+}
+
+CPDF_DIB::LoadState CPDF_DIB::ContinueLoadDIBBase(PauseIndicatorIface* pPause) {
+  if (m_Status == LoadState::kContinue)
+    return ContinueLoadMaskDIB(pPause);
+
+  ByteString decoder = m_pStreamAcc->GetImageDecoder();
+  if (decoder == "JPXDecode")
+    return LoadState::kFail;
+
+  if (decoder != "JBIG2Decode")
+    return LoadState::kSuccess;
+
+  if (m_Status == LoadState::kFail)
+    return LoadState::kFail;
+
+  FXCODEC_STATUS iDecodeStatus;
+  Jbig2Module* pJbig2Module =
+      fxcodec::ModuleMgr::GetInstance()->GetJbig2Module();
+  if (!m_pJbig2Context) {
+    m_pJbig2Context = pdfium::MakeUnique<Jbig2Context>();
+    if (m_pStreamAcc->GetImageParam()) {
+      const CPDF_Stream* pGlobals =
+          m_pStreamAcc->GetImageParam()->GetStreamFor("JBIG2Globals");
+      if (pGlobals) {
+        m_pGlobalAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pGlobals);
+        m_pGlobalAcc->LoadAllDataFiltered();
+      }
+    }
+    uint32_t nSrcObjNum = 0;
+    pdfium::span<const uint8_t> pSrcSpan;
+    if (m_pStreamAcc) {
+      pSrcSpan = m_pStreamAcc->GetSpan();
+      if (m_pStreamAcc->GetStream())
+        nSrcObjNum = m_pStreamAcc->GetStream()->GetObjNum();
+    }
+    uint32_t nGlobalObjNum = 0;
+    pdfium::span<const uint8_t> pGlobalSpan;
+    if (m_pGlobalAcc) {
+      pGlobalSpan = m_pGlobalAcc->GetSpan();
+      if (m_pGlobalAcc->GetStream())
+        nGlobalObjNum = m_pGlobalAcc->GetStream()->GetObjNum();
+    }
+    iDecodeStatus = pJbig2Module->StartDecode(
+        m_pJbig2Context.get(), m_pDocument->CodecContext(), m_Width, m_Height,
+        pSrcSpan, nSrcObjNum, pGlobalSpan, nGlobalObjNum,
+        m_pCachedBitmap->GetBuffer(), m_pCachedBitmap->GetPitch(), pPause);
+  } else {
+    iDecodeStatus = pJbig2Module->ContinueDecode(m_pJbig2Context.get(), pPause);
+  }
+
+  if (iDecodeStatus < 0) {
+    m_pJbig2Context.reset();
+    m_pCachedBitmap.Reset();
+    m_pGlobalAcc.Reset();
+    return LoadState::kFail;
+  }
+  if (iDecodeStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+    return LoadState::kContinue;
+
+  LoadState iContinueStatus = LoadState::kSuccess;
+  if (m_bHasMask) {
+    if (ContinueLoadMaskDIB(pPause) == LoadState::kContinue) {
+      iContinueStatus = LoadState::kContinue;
+      m_Status = LoadState::kContinue;
+    }
+  }
+  if (iContinueStatus == LoadState::kContinue)
+    return LoadState::kContinue;
+
+  if (m_pColorSpace && m_bStdCS)
+    m_pColorSpace->EnableStdConversion(false);
+  return iContinueStatus;
+}
+
+bool CPDF_DIB::LoadColorInfo(const CPDF_Dictionary* pFormResources,
+                             const CPDF_Dictionary* pPageResources) {
+  m_bpc_orig = m_pDict->GetIntegerFor("BitsPerComponent");
+  if (!IsMaybeValidBitsPerComponent(m_bpc_orig))
+    return false;
+
+  if (m_pDict->GetIntegerFor("ImageMask"))
+    m_bImageMask = true;
+
+  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;
+        }
+      }
+    }
+    m_bImageMask = true;
+    m_bpc = m_nComponents = 1;
+    const CPDF_Array* pDecode = m_pDict->GetArrayFor("Decode");
+    m_bDefaultDecode = !pDecode || !pDecode->GetIntegerAt(0);
+    return true;
+  }
+
+  const CPDF_Object* pCSObj = m_pDict->GetDirectObjectFor("ColorSpace");
+  if (!pCSObj)
+    return false;
+
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
+  if (pFormResources)
+    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pFormResources);
+  if (!m_pColorSpace)
+    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, 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
+  // decoded.
+  m_nComponents = m_pColorSpace->CountComponents();
+  m_Family = m_pColorSpace->GetFamily();
+  if (m_Family == PDFCS_ICCBASED && pCSObj->IsName()) {
+    ByteString cs = pCSObj->GetString();
+    if (cs == "DeviceGray")
+      m_nComponents = 1;
+    else if (cs == "DeviceRGB")
+      m_nComponents = 3;
+    else if (cs == "DeviceCMYK")
+      m_nComponents = 4;
+  }
+  ValidateDictParam();
+  return GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey);
+}
+
+bool CPDF_DIB::GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey) {
+  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");
+  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_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)
+        def_max = max_data;
+      if (def_min != m_CompData[i].m_DecodeMin || def_max != max)
+        *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)
+        m_CompData[i].m_DecodeStep = max_data;
+      m_CompData[i].m_DecodeStep =
+          (m_CompData[i].m_DecodeStep - m_CompData[i].m_DecodeMin) / max_data;
+    }
+  }
+  if (m_pDict->KeyExist("SMask"))
+    return true;
+
+  const CPDF_Object* pMask = m_pDict->GetDirectObjectFor("Mask");
+  if (!pMask)
+    return true;
+
+  if (const CPDF_Array* pArray = pMask->AsArray()) {
+    if (pArray->size() >= m_nComponents * 2) {
+      for (uint32_t i = 0; i < m_nComponents; i++) {
+        int min_num = pArray->GetIntegerAt(i * 2);
+        int max_num = pArray->GetIntegerAt(i * 2 + 1);
+        m_CompData[i].m_ColorKeyMin = std::max(min_num, 0);
+        m_CompData[i].m_ColorKeyMax = std::min(max_num, max_data);
+      }
+    }
+    *bColorKey = true;
+  }
+  return true;
+}
+
+CPDF_DIB::LoadState CPDF_DIB::CreateDecoder() {
+  ByteString decoder = m_pStreamAcc->GetImageDecoder();
+  if (decoder.IsEmpty())
+    return LoadState::kSuccess;
+
+  if (m_bDoBpcCheck && m_bpc == 0)
+    return LoadState::kFail;
+
+  if (decoder == "JPXDecode") {
+    m_pCachedBitmap = LoadJpxBitmap();
+    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_pCachedBitmap.Reset();
+      return LoadState::kFail;
+    }
+    m_Status = LoadState::kSuccess;
+    return LoadState::kContinue;
+  }
+
+  pdfium::span<const uint8_t> src_span = m_pStreamAcc->GetSpan();
+  const CPDF_Dictionary* pParams = m_pStreamAcc->GetImageParam();
+  if (decoder == "CCITTFaxDecode") {
+    m_pDecoder = CreateFaxDecoder(src_span, m_Width, m_Height, pParams);
+  } else if (decoder == "FlateDecode") {
+    m_pDecoder = CreateFlateDecoder(src_span, m_Width, m_Height, m_nComponents,
+                                    m_bpc, pParams);
+  } else if (decoder == "RunLengthDecode") {
+    m_pDecoder = BasicModule::CreateRunLengthDecoder(
+        src_span, m_Width, m_Height, m_nComponents, m_bpc);
+  } else if (decoder == "DCTDecode") {
+    if (!CreateDCTDecoder(src_span, pParams))
+      return LoadState::kFail;
+  }
+  if (!m_pDecoder)
+    return LoadState::kFail;
+
+  FX_SAFE_UINT32 requested_pitch =
+      fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width);
+  if (!requested_pitch.IsValid())
+    return LoadState::kFail;
+  FX_SAFE_UINT32 provided_pitch = fxcodec::CalculatePitch8(
+      m_pDecoder->GetBPC(), m_pDecoder->CountComps(), m_pDecoder->GetWidth());
+  if (!provided_pitch.IsValid())
+    return LoadState::kFail;
+  if (provided_pitch.ValueOrDie() < requested_pitch.ValueOrDie())
+    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(
+      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);
+  if (!info_opt.has_value())
+    return false;
+
+  const JpegModule::JpegImageInfo& info = info_opt.value();
+  m_Width = info.width;
+  m_Height = info.height;
+
+  if (!CPDF_Image::IsValidJpegComponent(info.num_components) ||
+      !CPDF_Image::IsValidJpegBitsPerComponent(info.bits_per_components)) {
+    return false;
+  }
+
+  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);
+    return true;
+  }
+
+  m_nComponents = static_cast<uint32_t>(info.num_components);
+  m_CompData.clear();
+  if (m_pColorSpace) {
+    uint32_t colorspace_comps = m_pColorSpace->CountComponents();
+    switch (m_Family) {
+      case PDFCS_DEVICEGRAY:
+      case PDFCS_DEVICERGB:
+      case PDFCS_DEVICECMYK: {
+        uint32_t dwMinComps = CPDF_ColorSpace::ComponentsForFamily(m_Family);
+        if (colorspace_comps < dwMinComps || m_nComponents < dwMinComps)
+          return false;
+        break;
+      }
+      case PDFCS_LAB: {
+        if (m_nComponents != 3 || colorspace_comps < 3)
+          return false;
+        break;
+      }
+      case PDFCS_ICCBASED: {
+        if (!CPDF_ColorSpace::IsValidIccComponents(colorspace_comps) ||
+            !CPDF_ColorSpace::IsValidIccComponents(m_nComponents) ||
+            colorspace_comps < m_nComponents) {
+          return false;
+        }
+        break;
+      }
+      default: {
+        if (colorspace_comps != m_nComponents)
+          return false;
+        break;
+      }
+    }
+  } else {
+    if (m_Family == PDFCS_LAB && m_nComponents != 3)
+      return false;
+  }
+  if (!GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey))
+    return false;
+
+  m_bpc = info.bits_per_components;
+  m_pDecoder = pJpegModule->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()));
+  if (!decoder)
+    return nullptr;
+
+  if (!decoder->StartDecode())
+    return nullptr;
+
+  CJPX_Decoder::JpxImageInfo image_info = decoder->GetInfo();
+  if (static_cast<int>(image_info.width) < m_Width ||
+      static_cast<int>(image_info.height) < m_Height) {
+    return nullptr;
+  }
+
+  RetainPtr<CPDF_ColorSpace> original_colorspace = m_pColorSpace;
+  bool swap_rgb = false;
+  switch (GetJpxDecodeAction(image_info.components, m_pColorSpace.Get())) {
+    case JpxDecodeAction::kFail:
+      return nullptr;
+
+    case JpxDecodeAction::kDoNothing:
+      break;
+
+    case JpxDecodeAction::kUseRgb:
+      DCHECK(image_info.components >= 3);
+      swap_rgb = true;
+      m_pColorSpace = nullptr;
+      break;
+
+    case JpxDecodeAction::kUseCmyk:
+      m_pColorSpace = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+      break;
+  }
+
+  // If |original_colorspace| exists, then LoadColorInfo() already set
+  // |m_nComponents|.
+  if (original_colorspace) {
+    DCHECK_NE(0, m_nComponents);
+  } else {
+    DCHECK_EQ(0, m_nComponents);
+    m_nComponents = image_info.components;
+  }
+
+  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;
+  } else {
+    image_info.width = (image_info.width * image_info.components + 2) / 3;
+    format = FXDIB_Rgb;
+  }
+
+  auto result_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!result_bitmap->Create(image_info.width, image_info.height, format))
+    return nullptr;
+
+  result_bitmap->Clear(0xFFFFFFFF);
+  if (!decoder->Decode(result_bitmap->GetBuffer(), result_bitmap->GetPitch(),
+                       swap_rgb)) {
+    return nullptr;
+  }
+
+  if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED &&
+      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);
+      for (uint32_t col = 0; col < image_info.width; ++col) {
+        *scanline = (*scanline) >> scale;
+        ++scanline;
+      }
+    }
+  }
+  m_bpc = 8;
+  return result_bitmap;
+}
+
+CPDF_DIB::LoadState CPDF_DIB::StartLoadMask() {
+  m_MatteColor = 0XFFFFFFFF;
+  RetainPtr<const CPDF_Stream> mask(m_pDict->GetStreamFor("SMask"));
+  if (!mask) {
+    mask.Reset(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 &&
+      pMatte->size() == m_nComponents &&
+      m_pColorSpace->CountComponents() <= m_nComponents) {
+    std::vector<float> colors =
+        ReadArrayElementsToVector(pMatte, m_nComponents);
+
+    float R;
+    float G;
+    float B;
+    m_pColorSpace->GetRGB(colors.data(), &R, &G, &B);
+    m_MatteColor = ArgbEncode(0, FXSYS_roundf(R * 255), FXSYS_roundf(G * 255),
+                              FXSYS_roundf(B * 255));
+  }
+  return StartLoadMaskDIB(std::move(mask));
+}
+
+CPDF_DIB::LoadState CPDF_DIB::ContinueLoadMaskDIB(PauseIndicatorIface* pPause) {
+  if (!m_pMask)
+    return LoadState::kSuccess;
+
+  LoadState ret = m_pMask->ContinueLoadDIBBase(pPause);
+  if (ret == LoadState::kContinue)
+    return LoadState::kContinue;
+
+  if (m_pColorSpace && m_bStdCS)
+    m_pColorSpace->EnableStdConversion(false);
+
+  if (ret == LoadState::kFail) {
+    m_pMask.Reset();
+    return LoadState::kFail;
+  }
+  return LoadState::kSuccess;
+}
+
+RetainPtr<CPDF_DIB> CPDF_DIB::DetachMask() {
+  return std::move(m_pMask);
+}
+
+bool CPDF_DIB::IsJBigImage() const {
+  return m_pStreamAcc->GetImageDecoder() == "JBIG2Decode";
+}
+
+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);
+  if (ret == LoadState::kContinue) {
+    if (m_Status == LoadState::kFail)
+      m_Status = LoadState::kContinue;
+    return LoadState::kContinue;
+  }
+  if (ret == LoadState::kFail)
+    m_pMask.Reset();
+  return LoadState::kSuccess;
+}
+
+void CPDF_DIB::LoadPalette() {
+  if (!m_pColorSpace || m_Family == PDFCS_PATTERN)
+    return;
+
+  if (m_bpc == 0)
+    return;
+
+  // Use FX_SAFE_UINT32 just to be on the safe side, in case |m_bpc| or
+  // |m_nComponents| somehow gets a bad value.
+  FX_SAFE_UINT32 safe_bits = m_bpc;
+  safe_bits *= m_nComponents;
+  uint32_t bits = safe_bits.ValueOrDefault(255);
+  if (bits > 8)
+    return;
+
+  if (bits == 1) {
+    if (m_bDefaultDecode &&
+        (m_Family == PDFCS_DEVICEGRAY || m_Family == PDFCS_DEVICERGB)) {
+      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];
+
+    float R = 0.0f;
+    float G = 0.0f;
+    float B = 0.0f;
+    m_pColorSpace->GetRGB(color_values, &R, &G, &B);
+
+    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));
+    if (argb0 != 0xFF000000 || argb1 != 0xFFFFFFFF) {
+      SetPaletteArgb(0, argb0);
+      SetPaletteArgb(1, argb1);
+    }
+    return;
+  }
+  if (m_bpc == 8 && m_bDefaultDecode &&
+      m_pColorSpace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY)) {
+    return;
+  }
+
+  int palette_count = 1 << bits;
+  // Using at least 16 elements due to the call m_pColorSpace->GetRGB().
+  std::vector<float> color_values(std::max(m_nComponents, 16u));
+  for (int i = 0; i < palette_count; i++) {
+    int color_data = i;
+    for (uint32_t j = 0; j < m_nComponents; j++) {
+      int encoded_component = color_data % (1 << m_bpc);
+      color_data /= 1 << m_bpc;
+      color_values[j] = m_CompData[j].m_DecodeMin +
+                        m_CompData[j].m_DecodeStep * encoded_component;
+    }
+    float R = 0;
+    float G = 0;
+    float B = 0;
+    if (m_nComponents == 1 && m_Family == PDFCS_ICCBASED &&
+        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);
+    } else {
+      m_pColorSpace->GetRGB(color_values.data(), &R, &G, &B);
+    }
+    SetPaletteArgb(i, ArgbEncode(255, FXSYS_roundf(R * 255),
+                                 FXSYS_roundf(G * 255), FXSYS_roundf(B * 255)));
+  }
+}
+
+void CPDF_DIB::ValidateDictParam() {
+  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;
+      }
+    }
+  }
+
+  if (!IsAllowedBitsPerComponent(m_bpc))
+    m_bpc = 0;
+}
+
+void CPDF_DIB::TranslateScanline24bpp(uint8_t* dest_scan,
+                                      const uint8_t* src_scan) const {
+  if (m_bpc == 0)
+    return;
+
+  if (TranslateScanline24bppDefaultDecode(dest_scan, src_scan))
+    return;
+
+  // Using at least 16 elements due to the call m_pColorSpace->GetRGB().
+  std::vector<float> color_values(std::max(m_nComponents, 16u));
+  float R = 0.0f;
+  float G = 0.0f;
+  float B = 0.0f;
+  uint64_t src_bit_pos = 0;
+  uint64_t src_byte_pos = 0;
+  size_t dest_byte_pos = 0;
+  const bool bpp8 = m_bpc == 8;
+  for (int column = 0; column < m_Width; column++) {
+    for (uint32_t color = 0; color < m_nComponents; color++) {
+      if (bpp8) {
+        uint8_t data = src_scan[src_byte_pos++];
+        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);
+        color_values[color] = m_CompData[color].m_DecodeMin +
+                              m_CompData[color].m_DecodeStep * data;
+        src_bit_pos += m_bpc;
+      }
+    }
+
+    if (TransMask()) {
+      float k = 1.0f - color_values[3];
+      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);
+    }
+    R = pdfium::clamp(R, 0.0f, 1.0f);
+    G = pdfium::clamp(G, 0.0f, 1.0f);
+    B = pdfium::clamp(B, 0.0f, 1.0f);
+    dest_scan[dest_byte_pos] = static_cast<uint8_t>(B * 255);
+    dest_scan[dest_byte_pos + 1] = static_cast<uint8_t>(G * 255);
+    dest_scan[dest_byte_pos + 2] = static_cast<uint8_t>(R * 255);
+    dest_byte_pos += 3;
+  }
+}
+
+bool CPDF_DIB::TranslateScanline24bppDefaultDecode(
+    uint8_t* dest_scan,
+    const uint8_t* src_scan) const {
+  if (!m_bDefaultDecode)
+    return false;
+
+  if (m_Family != PDFCS_DEVICERGB && m_Family != PDFCS_CALRGB) {
+    if (m_bpc != 8)
+      return false;
+
+    if (m_nComponents == m_pColorSpace->CountComponents()) {
+      m_pColorSpace->TranslateImageLine(dest_scan, src_scan, m_Width, m_Width,
+                                        m_Height, TransMask());
+    }
+    return true;
+  }
+
+  if (m_nComponents != 3)
+    return true;
+
+  const uint8_t* src_pos = src_scan;
+  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;
+        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;
+        src_pos += 6;
+      }
+      break;
+    default:
+      const unsigned int max_data = (1 << m_bpc) - 1;
+      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);
+        src_bit_pos += m_bpc;
+        unsigned int G = GetBits8(src_scan, src_bit_pos, m_bpc);
+        src_bit_pos += m_bpc;
+        unsigned int B = GetBits8(src_scan, 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_byte_pos += 3;
+      }
+      break;
+  }
+  return true;
+}
+
+uint8_t* CPDF_DIB::GetBuffer() const {
+  return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() : nullptr;
+}
+
+const uint8_t* CPDF_DIB::GetScanline(int line) const {
+  if (m_bpc == 0)
+    return nullptr;
+
+  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 uint8_t* pSrcLine = nullptr;
+  if (m_pCachedBitmap && src_pitch_value <= m_pCachedBitmap->GetPitch()) {
+    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;
+  }
+
+  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();
+    }
+
+    if (!m_bColorKey) {
+      memcpy(m_pLineBuf.get(), pSrcLine, src_pitch_value);
+      return m_pLineBuf.get();
+    }
+
+    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++;
+    }
+    return m_pMaskedLine.get();
+  }
+  if (m_bpc * m_nComponents <= 8) {
+    if (m_bpc == 8) {
+      memcpy(m_pLineBuf.get(), pSrcLine, 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);
+          color_index |= data << (color * m_bpc);
+          src_bit_pos += m_bpc;
+        }
+        m_pLineBuf.get()[col] = color_index;
+      }
+    }
+    if (!m_bColorKey)
+      return m_pLineBuf.get();
+
+    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;
+      }
+      *pDestPixel = IsColorIndexOutOfBounds(index, m_CompData[0]) ? 0xFF : 0;
+      pDestPixel++;
+    }
+    return m_pMaskedLine.get();
+  }
+  if (m_bColorKey) {
+    if (m_nComponents == 3 && m_bpc == 8) {
+      uint8_t* alpha_channel = m_pMaskedLine.get() + 3;
+      for (int col = 0; col < m_Width; col++) {
+        const uint8_t* pPixel = pSrcLine + col * 3;
+        alpha_channel[col * 4] =
+            AreColorIndicesOutOfBounds(pPixel, m_CompData.data(), 3) ? 0xFF : 0;
+      }
+    } else {
+      memset(m_pMaskedLine.get(), 0xFF, m_Pitch);
+    }
+  }
+  if (m_pColorSpace) {
+    TranslateScanline24bpp(m_pLineBuf.get(), pSrcLine);
+    pSrcLine = m_pLineBuf.get();
+  }
+  if (!m_bColorKey)
+    return pSrcLine;
+
+  const uint8_t* pSrcPixel = pSrcLine;
+  uint8_t* pDestPixel = m_pMaskedLine.get();
+  for (int col = 0; col < m_Width; col++) {
+    *pDestPixel++ = *pSrcPixel++;
+    *pDestPixel++ = *pSrcPixel++;
+    *pDestPixel++ = *pSrcPixel++;
+    pDestPixel++;
+  }
+  return m_pMaskedLine.get();
+}
+
+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);
+    }
+  }
+}
+
+bool CPDF_DIB::TransMask() const {
+  return m_bLoadMask && m_GroupFamily == PDFCS_DEVICECMYK &&
+         m_Family == PDFCS_DEVICECMYK;
+}
+
+void CPDF_DIB::SetMaskProperties() {
+  m_bpp = 1;
+  m_bpc = 1;
+  m_nComponents = 1;
+  m_AlphaFlag = 1;
+}
diff --git a/core/fpdfapi/page/cpdf_dib.h b/core/fpdfapi/page/cpdf_dib.h
new file mode 100644
index 0000000..6960cea
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_dib.h
@@ -0,0 +1,161 @@
+// 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_PAGE_CPDF_DIB_H_
+#define CORE_FPDFAPI_PAGE_CPDF_DIB_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/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "third_party/base/span.h"
+
+class CPDF_Dictionary;
+class CPDF_Document;
+class CPDF_Stream;
+class CPDF_StreamAcc;
+
+struct DIB_COMP_DATA {
+  float m_DecodeMin;
+  float m_DecodeStep;
+  int m_ColorKeyMin;
+  int m_ColorKeyMax;
+};
+
+namespace fxcodec {
+class Jbig2Context;
+class ScanlineDecoder;
+}  // namespace fxcodec
+
+constexpr size_t kHugeImageSize = 60000000;
+
+class CPDF_DIB final : public CFX_DIBBase {
+ 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);
+
+  // CFX_DIBBase:
+  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;
+
+  RetainPtr<CPDF_ColorSpace> GetColorSpace() const { return m_pColorSpace; }
+  uint32_t GetMatteColor() const { return m_MatteColor; }
+
+  LoadState StartLoadDIBBase(CPDF_Document* pDoc,
+                             const CPDF_Stream* pStream,
+                             bool bHasMask,
+                             const CPDF_Dictionary* pFormResources,
+                             CPDF_Dictionary* pPageResources,
+                             bool bStdCS,
+                             uint32_t GroupFamily,
+                             bool bLoadMask);
+  LoadState ContinueLoadDIBBase(PauseIndicatorIface* pPause);
+  RetainPtr<CPDF_DIB> DetachMask();
+
+  bool IsJBigImage() const;
+
+ private:
+  CPDF_DIB();
+  ~CPDF_DIB() override;
+
+  LoadState StartLoadMask();
+  LoadState StartLoadMaskDIB(RetainPtr<const CPDF_Stream> mask);
+  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();
+  void LoadPalette();
+  LoadState CreateDecoder();
+  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;
+  bool TransMask() const;
+  void SetMaskProperties();
+
+  UnownedPtr<CPDF_Document> m_pDocument;
+  RetainPtr<const CPDF_Stream> 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;
+  uint32_t m_MatteColor = 0;
+  LoadState m_Status = LoadState::kFail;
+  bool m_bLoadMask = false;
+  bool m_bDefaultDecode = true;
+  bool m_bImageMask = false;
+  bool m_bDoBpcCheck = true;
+  bool m_bColorKey = false;
+  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;
+  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;
+
+  // Must come after |m_pCachedBitmap|.
+  std::unique_ptr<fxcodec::Jbig2Context> m_pJbig2Context;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_DIB_H_
diff --git a/core/fpdfapi/page/cpdf_docpagedata.cpp b/core/fpdfapi/page/cpdf_docpagedata.cpp
index 0d2a918..9c8bc92 100644
--- a/core/fpdfapi/page/cpdf_docpagedata.cpp
+++ b/core/fpdfapi/page/cpdf_docpagedata.cpp
@@ -10,10 +10,11 @@
 #include <memory>
 #include <set>
 #include <utility>
+#include <vector>
 
-#include "core/fdrm/crypto/fx_crypt.h"
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "build/build_config.h"
 #include "core/fpdfapi/font/cpdf_type1font.h"
+#include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_iccprofile.h"
 #include "core/fpdfapi/page/cpdf_image.h"
 #include "core/fpdfapi/page/cpdf_pagemodule.h"
@@ -22,139 +23,185 @@
 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_safe_types.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"
 
-CPDF_DocPageData::CPDF_DocPageData(CPDF_Document* pPDFDoc)
-    : m_bForceClear(false), m_pPDFDoc(pPDFDoc) {
-  assert(m_pPDFDoc);
+namespace {
+
+void InsertWidthArrayImpl(std::vector<int> widths, CPDF_Array* pWidthArray) {
+  size_t i;
+  for (i = 1; i < widths.size(); i++) {
+    if (widths[i] != widths[0])
+      break;
+  }
+  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]);
+    return;
+  }
+  CPDF_Array* pWidthArray1 = pWidthArray->AddNew<CPDF_Array>();
+  for (int w : widths)
+    pWidthArray1->AddNew<CPDF_Number>(w);
 }
 
+#if defined(OS_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());
+  InsertWidthArrayImpl(std::move(widths), pWidthArray);
+}
+
+ByteString GetPSNameFromTT(HDC hDC) {
+  ByteString result;
+  DWORD size = ::GetFontData(hDC, 'eman', 0, nullptr, 0);
+  if (size != GDI_ERROR) {
+    LPBYTE buffer = FX_Alloc(BYTE, size);
+    ::GetFontData(hDC, 'eman', 0, buffer, size);
+    result = GetNameFromTT({buffer, size}, 6);
+    FX_Free(buffer);
+  }
+  return result;
+}
+#endif  // defined(OS_WIN)
+
+void InsertWidthArray1(CFX_Font* pFont,
+                       CFX_UnicodeEncoding* pEncoding,
+                       wchar_t start,
+                       wchar_t end,
+                       CPDF_Array* pWidthArray) {
+  std::vector<int> widths(end - start + 1);
+  for (size_t i = 0; i < widths.size(); ++i) {
+    int glyph_index = pEncoding->GlyphFromCharCode(start + i);
+    widths[i] = pFont->GetGlyphWidth(glyph_index);
+  }
+  InsertWidthArrayImpl(std::move(widths), pWidthArray);
+}
+
+int CalculateFlags(bool bold,
+                   bool italic,
+                   bool fixedPitch,
+                   bool serif,
+                   bool script,
+                   bool symbolic) {
+  int flags = 0;
+  if (bold)
+    flags |= FXFONT_FORCE_BOLD;
+  if (italic)
+    flags |= FXFONT_ITALIC;
+  if (fixedPitch)
+    flags |= FXFONT_FIXED_PITCH;
+  if (serif)
+    flags |= FXFONT_SERIF;
+  if (script)
+    flags |= FXFONT_SCRIPT;
+  if (symbolic)
+    flags |= FXFONT_SYMBOLIC;
+  else
+    flags |= FXFONT_NONSYMBOLIC;
+  return flags;
+}
+
+void ProcessNonbCJK(CPDF_Dictionary* pBaseDict,
+                    bool bold,
+                    bool italic,
+                    ByteString basefont,
+                    RetainPtr<CPDF_Array> pWidths) {
+  if (bold && italic)
+    basefont += ",BoldItalic";
+  else if (bold)
+    basefont += ",Bold";
+  else if (italic)
+    basefont += ",Italic";
+  pBaseDict->SetNewFor<CPDF_Name>("Subtype", "TrueType");
+  pBaseDict->SetNewFor<CPDF_Name>("BaseFont", basefont);
+  pBaseDict->SetNewFor<CPDF_Number>("FirstChar", 32);
+  pBaseDict->SetNewFor<CPDF_Number>("LastChar", 255);
+  pBaseDict->SetFor("Widths", pWidths);
+}
+
+RetainPtr<CPDF_Dictionary> CalculateFontDesc(CPDF_Document* pDoc,
+                                             ByteString basefont,
+                                             int flags,
+                                             int italicangle,
+                                             int ascend,
+                                             int descend,
+                                             RetainPtr<CPDF_Array> bbox,
+                                             int32_t stemV) {
+  auto pFontDesc = pDoc->New<CPDF_Dictionary>();
+  pFontDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
+  pFontDesc->SetNewFor<CPDF_Name>("FontName", basefont);
+  pFontDesc->SetNewFor<CPDF_Number>("Flags", flags);
+  pFontDesc->SetFor("FontBBox", bbox);
+  pFontDesc->SetNewFor<CPDF_Number>("ItalicAngle", italicangle);
+  pFontDesc->SetNewFor<CPDF_Number>("Ascent", ascend);
+  pFontDesc->SetNewFor<CPDF_Number>("Descent", descend);
+  pFontDesc->SetNewFor<CPDF_Number>("StemV", stemV);
+  return pFontDesc;
+}
+
+}  // namespace
+
+// static
+CPDF_DocPageData* CPDF_DocPageData::FromDocument(const CPDF_Document* pDoc) {
+  return static_cast<CPDF_DocPageData*>(pDoc->GetPageData());
+}
+
+CPDF_DocPageData::CPDF_DocPageData() = default;
+
 CPDF_DocPageData::~CPDF_DocPageData() {
-  Clear(false);
-  Clear(true);
-
-  for (auto& it : m_PatternMap)
-    delete it.second;
-  m_PatternMap.clear();
-
-  for (auto& it : m_FontMap)
-    delete it.second;
-  m_FontMap.clear();
-
-  for (auto& it : m_ColorSpaceMap)
-    delete it.second;
-  m_ColorSpaceMap.clear();
-}
-
-void CPDF_DocPageData::Clear(bool bForceRelease) {
-  m_bForceClear = bForceRelease;
-
-  // This is needed because if |bForceRelease| is true we will destroy any
-  // pattern we see regardless of the ref-count. The tiling pattern owns a
-  // Form object which owns a ShadingObject. The ShadingObject has an unowned
-  // pointer to a ShadingPattern. The ShadingPattern is owned by the
-  // DocPageData. So, we loop through and clear any tiling patterns before we
-  // do the same for any shading patterns, otherwise we may free the
-  // ShadingPattern before the ShadingObject and trigger an unowned pointer
-  // probe warning.
-  for (auto& it : m_PatternMap) {
-    CPDF_CountedPattern* ptData = it.second;
-    if (!ptData->get() || !ptData->get()->AsTilingPattern())
-      continue;
-    if (bForceRelease || ptData->use_count() < 2)
-      ptData->clear();
-  }
-
-  for (auto& it : m_PatternMap) {
-    CPDF_CountedPattern* ptData = it.second;
-    if (!ptData->get())
-      continue;
-    if (bForceRelease || ptData->use_count() < 2)
-      ptData->clear();
-  }
-
   for (auto& it : m_FontMap) {
-    CPDF_CountedFont* fontData = it.second;
-    if (!fontData->get())
-      continue;
-    if (bForceRelease || fontData->use_count() < 2) {
-      fontData->clear();
-    }
+    if (it.second)
+      it.second->WillBeDestroyed();
   }
-
-  for (auto& it : m_ColorSpaceMap) {
-    CPDF_CountedColorSpace* csData = it.second;
-    if (!csData->get())
-      continue;
-    if (bForceRelease || csData->use_count() < 2) {
-      csData->get()->Release();
-      csData->reset(nullptr);
-    }
-  }
-
-  for (auto it = m_IccProfileMap.begin(); it != m_IccProfileMap.end();) {
-    auto curr_it = it++;
-    if (bForceRelease || curr_it->second->HasOneRef()) {
-      for (auto hash_it = m_HashProfileMap.begin();
-           hash_it != m_HashProfileMap.end(); ++hash_it) {
-        if (curr_it->first == hash_it->second) {
-          m_HashProfileMap.erase(hash_it);
-          break;
-        }
-      }
-      m_IccProfileMap.erase(curr_it);
-    }
-  }
-
-  for (auto it = m_FontFileMap.begin(); it != m_FontFileMap.end();) {
-    auto curr_it = it++;
-    if (bForceRelease || curr_it->second->HasOneRef())
-      m_FontFileMap.erase(curr_it);
-  }
-
-  m_ImageMap.clear();
 }
 
-CPDF_Font* CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) {
+void CPDF_DocPageData::ClearStockFont() {
+  CPDF_PageModule::GetInstance()->ClearStockFont(GetDocument());
+}
+
+RetainPtr<CPDF_Font> CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) {
   if (!pFontDict)
     return nullptr;
 
-  CPDF_CountedFont* pFontData = nullptr;
   auto it = m_FontMap.find(pFontDict);
-  if (it != m_FontMap.end()) {
-    pFontData = it->second;
-    if (pFontData->get()) {
-      return pFontData->AddRef();
-    }
-  }
-  std::unique_ptr<CPDF_Font> pFont =
-      CPDF_Font::Create(m_pPDFDoc.Get(), pFontDict);
+  if (it != m_FontMap.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
+
+  RetainPtr<CPDF_Font> pFont =
+      CPDF_Font::Create(GetDocument(), pFontDict, this);
   if (!pFont)
     return nullptr;
 
-  if (pFontData) {
-    pFontData->reset(std::move(pFont));
-  } else {
-    pFontData = new CPDF_CountedFont(std::move(pFont));
-    m_FontMap[pFontDict] = pFontData;
-  }
-  return pFontData->AddRef();
+  m_FontMap[pFontDict].Reset(pFont.Get());
+  return pFont;
 }
 
-CPDF_Font* CPDF_DocPageData::GetStandardFont(const ByteString& fontName,
-                                             CPDF_FontEncoding* pEncoding) {
+RetainPtr<CPDF_Font> CPDF_DocPageData::GetStandardFont(
+    const ByteString& fontName,
+    const CPDF_FontEncoding* pEncoding) {
   if (fontName.IsEmpty())
     return nullptr;
 
   for (auto& it : m_FontMap) {
-    CPDF_CountedFont* fontData = it.second;
-    CPDF_Font* pFont = fontData->get();
+    CPDF_Font* pFont = it.second.Get();
     if (!pFont)
       continue;
-    if (pFont->GetBaseFont() != fontName)
+    if (pFont->GetBaseFontName() != fontName)
       continue;
     if (pFont->IsEmbedded())
       continue;
@@ -167,84 +214,74 @@
     if (pEncoding && !pT1Font->GetEncoding()->IsIdentical(pEncoding))
       continue;
 
-    return fontData->AddRef();
+    return pdfium::WrapRetain(pFont);
   }
 
-  CPDF_Dictionary* pDict = m_pPDFDoc->NewIndirect<CPDF_Dictionary>();
+  CPDF_Dictionary* pDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
   pDict->SetNewFor<CPDF_Name>("Type", "Font");
   pDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
   pDict->SetNewFor<CPDF_Name>("BaseFont", fontName);
   if (pEncoding) {
     pDict->SetFor("Encoding",
-                  pEncoding->Realize(m_pPDFDoc->GetByteStringPool()));
+                  pEncoding->Realize(GetDocument()->GetByteStringPool()));
   }
 
-  std::unique_ptr<CPDF_Font> pFont = CPDF_Font::Create(m_pPDFDoc.Get(), pDict);
+  // Note: NULL FormFactoryIface OK since known Type1 font from above.
+  RetainPtr<CPDF_Font> pFont = CPDF_Font::Create(GetDocument(), pDict, nullptr);
   if (!pFont)
     return nullptr;
 
-  CPDF_CountedFont* fontData = new CPDF_CountedFont(std::move(pFont));
-  m_FontMap[pDict] = fontData;
-  return fontData->AddRef();
+  m_FontMap[pDict].Reset(pFont.Get());
+  return pFont;
 }
 
-void CPDF_DocPageData::ReleaseFont(const CPDF_Dictionary* pFontDict) {
-  if (!pFontDict)
-    return;
-
-  auto it = m_FontMap.find(pFontDict);
-  if (it == m_FontMap.end())
-    return;
-
-  CPDF_CountedFont* pFontData = it->second;
-  if (!pFontData->get())
-    return;
-
-  pFontData->RemoveRef();
-  if (pFontData->use_count() > 1)
-    return;
-
-  // We have font data only in m_FontMap cache. Clean it.
-  pFontData->clear();
-}
-
-CPDF_ColorSpace* CPDF_DocPageData::GetColorSpace(
-    CPDF_Object* pCSObj,
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpace(
+    const CPDF_Object* pCSObj,
     const CPDF_Dictionary* pResources) {
-  std::set<CPDF_Object*> visited;
+  std::set<const CPDF_Object*> visited;
   return GetColorSpaceGuarded(pCSObj, pResources, &visited);
 }
 
-CPDF_ColorSpace* CPDF_DocPageData::GetColorSpaceGuarded(
-    CPDF_Object* pCSObj,
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpaceGuarded(
+    const CPDF_Object* pCSObj,
     const CPDF_Dictionary* pResources,
-    std::set<CPDF_Object*>* pVisited) {
+    std::set<const CPDF_Object*>* pVisited) {
+  std::set<const CPDF_Object*> visitedLocal;
+  return GetColorSpaceInternal(pCSObj, pResources, pVisited, &visitedLocal);
+}
+
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpaceInternal(
+    const CPDF_Object* pCSObj,
+    const CPDF_Dictionary* pResources,
+    std::set<const CPDF_Object*>* pVisited,
+    std::set<const CPDF_Object*>* pVisitedInternal) {
   if (!pCSObj)
     return nullptr;
 
-  if (pdfium::ContainsKey(*pVisited, pCSObj))
+  if (pdfium::ContainsKey(*pVisitedInternal, pCSObj))
     return nullptr;
 
-  pdfium::ScopedSetInsertion<CPDF_Object*> insertion(pVisited, pCSObj);
+  pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisitedInternal,
+                                                           pCSObj);
 
   if (pCSObj->IsName()) {
     ByteString name = pCSObj->GetString();
-    CPDF_ColorSpace* pCS = CPDF_ColorSpace::ColorspaceFromName(name);
+    RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::ColorspaceFromName(name);
     if (!pCS && pResources) {
-      CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace");
+      const CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace");
       if (pList) {
-        return GetColorSpaceGuarded(pList->GetDirectObjectFor(name), nullptr,
-                                    pVisited);
+        return GetColorSpaceInternal(pList->GetDirectObjectFor(name), nullptr,
+                                     pVisited, pVisitedInternal);
       }
     }
     if (!pCS || !pResources)
       return pCS;
 
-    CPDF_Dictionary* pColorSpaces = pResources->GetDictFor("ColorSpace");
+    const CPDF_Dictionary* pColorSpaces = pResources->GetDictFor("ColorSpace");
     if (!pColorSpaces)
       return pCS;
 
-    CPDF_Object* pDefaultCS = nullptr;
+    const CPDF_Object* pDefaultCS = nullptr;
     switch (pCS->GetFamily()) {
       case PDFCS_DEVICERGB:
         pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultRGB");
@@ -259,135 +296,65 @@
     if (!pDefaultCS)
       return pCS;
 
-    return GetColorSpaceGuarded(pDefaultCS, nullptr, pVisited);
+    return GetColorSpaceInternal(pDefaultCS, nullptr, pVisited,
+                                 pVisitedInternal);
   }
 
-  CPDF_Array* pArray = pCSObj->AsArray();
+  const CPDF_Array* pArray = pCSObj->AsArray();
   if (!pArray || pArray->IsEmpty())
     return nullptr;
 
-  if (pArray->GetCount() == 1) {
-    return GetColorSpaceGuarded(pArray->GetDirectObjectAt(0), pResources,
-                                pVisited);
+  if (pArray->size() == 1) {
+    return GetColorSpaceInternal(pArray->GetDirectObjectAt(0), pResources,
+                                 pVisited, pVisitedInternal);
   }
 
-  CPDF_CountedColorSpace* csData = nullptr;
   auto it = m_ColorSpaceMap.find(pCSObj);
-  if (it != m_ColorSpaceMap.end()) {
-    csData = it->second;
-    if (csData->get()) {
-      return csData->AddRef();
-    }
-  }
+  if (it != m_ColorSpaceMap.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
 
-  std::unique_ptr<CPDF_ColorSpace> pCS =
-      CPDF_ColorSpace::Load(m_pPDFDoc.Get(), pArray, pVisited);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::Load(GetDocument(), pArray, pVisited);
   if (!pCS)
     return nullptr;
 
-  if (csData) {
-    csData->reset(std::move(pCS));
-  } else {
-    csData = new CPDF_CountedColorSpace(std::move(pCS));
-    m_ColorSpaceMap[pCSObj] = csData;
-  }
-  return csData->AddRef();
+  m_ColorSpaceMap[pCSObj].Reset(pCS.Get());
+  return pCS;
 }
 
-CPDF_ColorSpace* CPDF_DocPageData::GetCopiedColorSpace(CPDF_Object* pCSObj) {
-  if (!pCSObj)
-    return nullptr;
-
-  auto it = m_ColorSpaceMap.find(pCSObj);
-  if (it != m_ColorSpaceMap.end())
-    return it->second->AddRef();
-
-  return nullptr;
-}
-
-void CPDF_DocPageData::ReleaseColorSpace(const CPDF_Object* pColorSpace) {
-  if (!pColorSpace)
-    return;
-
-  auto it = m_ColorSpaceMap.find(pColorSpace);
-  if (it == m_ColorSpaceMap.end())
-    return;
-
-  CPDF_CountedColorSpace* pCountedColorSpace = it->second;
-  if (!pCountedColorSpace->get())
-    return;
-
-  pCountedColorSpace->RemoveRef();
-  if (pCountedColorSpace->use_count() > 1)
-    return;
-
-  // We have item only in m_ColorSpaceMap cache. Clean it.
-  pCountedColorSpace->get()->Release();
-  pCountedColorSpace->reset(nullptr);
-}
-
-CPDF_Pattern* CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj,
-                                           bool bShading,
-                                           const CFX_Matrix& matrix) {
+RetainPtr<CPDF_Pattern> CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj,
+                                                     bool bShading,
+                                                     const CFX_Matrix& matrix) {
   if (!pPatternObj)
     return nullptr;
 
-  CPDF_CountedPattern* ptData = nullptr;
   auto it = m_PatternMap.find(pPatternObj);
-  if (it != m_PatternMap.end()) {
-    ptData = it->second;
-    if (ptData->get()) {
-      return ptData->AddRef();
-    }
-  }
-  std::unique_ptr<CPDF_Pattern> pPattern;
+  if (it != m_PatternMap.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
+
+  RetainPtr<CPDF_Pattern> pPattern;
   if (bShading) {
-    pPattern = pdfium::MakeUnique<CPDF_ShadingPattern>(
-        m_pPDFDoc.Get(), pPatternObj, true, matrix);
+    pPattern = pdfium::MakeRetain<CPDF_ShadingPattern>(
+        GetDocument(), pPatternObj, true, matrix);
   } else {
     CPDF_Dictionary* pDict = pPatternObj->GetDict();
     if (!pDict)
       return nullptr;
 
     int type = pDict->GetIntegerFor("PatternType");
-    if (type == CPDF_Pattern::TILING) {
-      pPattern = pdfium::MakeUnique<CPDF_TilingPattern>(m_pPDFDoc.Get(),
+    if (type == CPDF_Pattern::kTiling) {
+      pPattern = pdfium::MakeRetain<CPDF_TilingPattern>(GetDocument(),
                                                         pPatternObj, matrix);
-    } else if (type == CPDF_Pattern::SHADING) {
-      pPattern = pdfium::MakeUnique<CPDF_ShadingPattern>(
-          m_pPDFDoc.Get(), pPatternObj, false, matrix);
+    } else if (type == CPDF_Pattern::kShading) {
+      pPattern = pdfium::MakeRetain<CPDF_ShadingPattern>(
+          GetDocument(), pPatternObj, false, matrix);
     } else {
       return nullptr;
     }
   }
 
-  if (ptData) {
-    ptData->reset(std::move(pPattern));
-  } else {
-    ptData = new CPDF_CountedPattern(std::move(pPattern));
-    m_PatternMap[pPatternObj] = ptData;
-  }
-  return ptData->AddRef();
-}
-
-void CPDF_DocPageData::ReleasePattern(const CPDF_Object* pPatternObj) {
-  if (!pPatternObj)
-    return;
-
-  auto it = m_PatternMap.find(pPatternObj);
-  if (it == m_PatternMap.end())
-    return;
-
-  CPDF_CountedPattern* pPattern = it->second;
-  if (!pPattern->get())
-    return;
-
-  pPattern->RemoveRef();
-  if (pPattern->use_count() > 1)
-    return;
-
-  // We have item only in m_PatternMap cache. Clean it.
-  pPattern->clear();
+  m_PatternMap[pPatternObj].Reset(pPattern.Get());
+  return pPattern;
 }
 
 RetainPtr<CPDF_Image> CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) {
@@ -396,7 +363,7 @@
   if (it != m_ImageMap.end())
     return it->second;
 
-  auto pImage = pdfium::MakeRetain<CPDF_Image>(m_pPDFDoc.Get(), dwStreamObjNum);
+  auto pImage = pdfium::MakeRetain<CPDF_Image>(GetDocument(), dwStreamObjNum);
   m_ImageMap[dwStreamObjNum] = pImage;
   return pImage;
 }
@@ -409,56 +376,52 @@
 }
 
 RetainPtr<CPDF_IccProfile> CPDF_DocPageData::GetIccProfile(
-    CPDF_Stream* pProfileStream) {
+    const CPDF_Stream* pProfileStream) {
   if (!pProfileStream)
     return nullptr;
 
   auto it = m_IccProfileMap.find(pProfileStream);
-  if (it != m_IccProfileMap.end())
-    return it->second;
+  if (it != m_IccProfileMap.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
 
   auto pAccessor = pdfium::MakeRetain<CPDF_StreamAcc>(pProfileStream);
   pAccessor->LoadAllDataFiltered();
 
-  uint8_t digest[20];
-  CRYPT_SHA1Generate(pAccessor->GetData(), pAccessor->GetSize(), digest);
-
-  ByteString bsDigest(digest, 20);
+  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);
-    if (it_copied_stream != m_IccProfileMap.end())
-      return it_copied_stream->second;
+    auto it_copied_stream = m_IccProfileMap.find(hash_it->second.Get());
+    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->GetData(), pAccessor->GetSize());
-  m_IccProfileMap[pProfileStream] = pProfile;
-  m_HashProfileMap[bsDigest] = pProfileStream;
+  auto pProfile =
+      pdfium::MakeRetain<CPDF_IccProfile>(pProfileStream, pAccessor->GetSpan());
+  m_IccProfileMap[pProfileStream].Reset(pProfile.Get());
+  m_HashProfileMap[bsDigest].Reset(pProfileStream);
   return pProfile;
 }
 
-void CPDF_DocPageData::MaybePurgeIccProfile(CPDF_Stream* pProfileStream) {
-  ASSERT(pProfileStream);
-  auto it = m_IccProfileMap.find(pProfileStream);
-  if (it != m_IccProfileMap.end() && it->second->HasOneRef())
-    m_IccProfileMap.erase(it);
-}
-
 RetainPtr<CPDF_StreamAcc> CPDF_DocPageData::GetFontFileStreamAcc(
-    CPDF_Stream* pFontStream) {
+    const CPDF_Stream* pFontStream) {
   ASSERT(pFontStream);
   auto it = m_FontFileMap.find(pFontStream);
   if (it != m_FontFileMap.end())
     return it->second;
 
-  CPDF_Dictionary* pFontDict = pFontStream->GetDict();
-  int32_t org_size = pFontDict->GetIntegerFor("Length1") +
-                     pFontDict->GetIntegerFor("Length2") +
-                     pFontDict->GetIntegerFor("Length3");
-  org_size = std::max(org_size, 0);
+  const CPDF_Dictionary* pFontDict = pFontStream->GetDict();
+  int32_t len1 = pFontDict->GetIntegerFor("Length1");
+  int32_t len2 = pFontDict->GetIntegerFor("Length2");
+  int32_t len3 = pFontDict->GetIntegerFor("Length3");
+  uint32_t org_size = 0;
+  if (len1 >= 0 && len2 >= 0 && len3 >= 0) {
+    FX_SAFE_UINT32 safe_org_size = len1;
+    safe_org_size += len2;
+    safe_org_size += len3;
+    org_size = safe_org_size.ValueOrDefault(0);
+  }
 
   auto pFontAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pFontStream);
-  pFontAcc->LoadAllData(false, org_size, false);
+  pFontAcc->LoadAllDataFilteredWithEstimatedSize(org_size);
   m_FontFileMap[pFontStream] = pFontAcc;
   return pFontAcc;
 }
@@ -473,20 +436,277 @@
     m_FontFileMap.erase(it);
 }
 
-CPDF_CountedColorSpace* CPDF_DocPageData::FindColorSpacePtr(
-    CPDF_Object* pCSObj) const {
-  if (!pCSObj)
-    return nullptr;
-
-  auto it = m_ColorSpaceMap.find(pCSObj);
-  return it != m_ColorSpaceMap.end() ? it->second : nullptr;
+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);
 }
 
-CPDF_CountedPattern* CPDF_DocPageData::FindPatternPtr(
-    CPDF_Object* pPatternObj) const {
-  if (!pPatternObj)
+RetainPtr<CPDF_Font> CPDF_DocPageData::AddStandardFont(
+    const ByteString& fontName,
+    const CPDF_FontEncoding* pEncoding) {
+  ByteString mutable_name(fontName);
+  if (!CFX_FontMapper::GetStandardFontName(&mutable_name))
+    return nullptr;
+  return GetStandardFont(mutable_name, pEncoding);
+}
+
+RetainPtr<CPDF_Font> CPDF_DocPageData::AddFont(std::unique_ptr<CFX_Font> pFont,
+                                               int charset) {
+  if (!pFont)
     return nullptr;
 
-  auto it = m_PatternMap.find(pPatternObj);
-  return it != m_PatternMap.end() ? it->second : nullptr;
+  const bool bCJK = FX_CharSetIsCJK(charset);
+  ByteString basefont = pFont->GetFamilyName();
+  basefont.Replace(" ", "");
+  int flags =
+      CalculateFlags(pFont->IsBold(), pFont->IsItalic(), pFont->IsFixedWidth(),
+                     false, false, charset == FX_CHARSET_Symbol);
+
+  CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  pBaseDict->SetNewFor<CPDF_Name>("Type", "Font");
+  auto pEncoding = pdfium::MakeUnique<CFX_UnicodeEncoding>(pFont.get());
+  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);
+    }
+    if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Default ||
+        charset == FX_CHARSET_Symbol) {
+      pBaseDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
+      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);
+      }
+    } else {
+      size_t i = CalculateEncodingDict(charset, pBaseDict);
+      if (i < FX_ArraySize(g_FX_CharsetUnicodes)) {
+        const uint16_t* pUnicodes = g_FX_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);
+        }
+      }
+    }
+    ProcessNonbCJK(pBaseDict, pFont->IsBold(), pFont->IsItalic(), basefont,
+                   std::move(pWidths));
+  } else {
+    pFontDict = ProcessbCJK(
+        pBaseDict, charset, basefont,
+        [&pFont, &pEncoding](wchar_t start, wchar_t end, CPDF_Array* widthArr) {
+          InsertWidthArray1(pFont.get(), pEncoding.get(), start, end, widthArr);
+        });
+  }
+  int italicangle = pFont->GetSubstFontItalicAngle();
+  FX_RECT bbox;
+  pFont->GetBBox(&bbox);
+  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);
+  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);
+    uint32_t glyph = pEncoding->GlyphFromCharCode(stem_chars[0]);
+    nStemV = pFont->GetGlyphWidth(glyph);
+    for (size_t i = 1; i < count; i++) {
+      glyph = pEncoding->GlyphFromCharCode(stem_chars[i]);
+      int width = pFont->GetGlyphWidth(glyph);
+      if (width > 0 && width < nStemV)
+        nStemV = width;
+    }
+  }
+  CPDF_Dictionary* pFontDesc =
+      ToDictionary(GetDocument()->AddIndirectObject(CalculateFontDesc(
+          GetDocument(), basefont, flags, italicangle, pFont->GetAscent(),
+          pFont->GetDescent(), std::move(pBBox), nStemV)));
+  pFontDict->SetNewFor<CPDF_Reference>("FontDescriptor", GetDocument(),
+                                       pFontDesc->GetObjNum());
+  return GetFont(pBaseDict);
+}
+
+#if defined(OS_WIN)
+RetainPtr<CPDF_Font> CPDF_DocPageData::AddWindowsFont(LOGFONTA* pLogFont) {
+  pLogFont->lfHeight = -1000;
+  pLogFont->lfWidth = 0;
+  HGDIOBJ hFont = CreateFontIndirectA(pLogFont);
+  HDC hDC = CreateCompatibleDC(nullptr);
+  hFont = SelectObject(hDC, hFont);
+  int tm_size = GetOutlineTextMetrics(hDC, 0, nullptr);
+  if (tm_size == 0) {
+    hFont = SelectObject(hDC, hFont);
+    DeleteObject(hFont);
+    DeleteDC(hDC);
+    return nullptr;
+  }
+
+  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);
+
+  const bool bCJK = FX_CharSetIsCJK(pLogFont->lfCharSet);
+  ByteString basefont;
+  if (bCJK)
+    basefont = GetPSNameFromTT(hDC);
+
+  if (basefont.IsEmpty())
+    basefont = pLogFont->lfFaceName;
+
+  int italicangle = ptm->otmItalicAngle / 10;
+  int ascend = ptm->otmrcFontBox.top;
+  int descend = ptm->otmrcFontBox.bottom;
+  int capheight = ptm->otmsCapEmHeight;
+  int bbox[4] = {ptm->otmrcFontBox.left, ptm->otmrcFontBox.bottom,
+                 ptm->otmrcFontBox.right, ptm->otmrcFontBox.top};
+  FX_Free(tm_buf);
+  basefont.Replace(" ", "");
+  CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  pBaseDict->SetNewFor<CPDF_Name>("Type", "Font");
+  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");
+    } else {
+      CalculateEncodingDict(pLogFont->lfCharSet, pBaseDict);
+    }
+    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]);
+    ProcessNonbCJK(pBaseDict, pLogFont->lfWeight > FW_MEDIUM,
+                   pLogFont->lfItalic != 0, basefont, std::move(pWidths));
+  } else {
+    pFontDict =
+        ProcessbCJK(pBaseDict, pLogFont->lfCharSet, 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]);
+  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()));
+  hFont = SelectObject(hDC, hFont);
+  DeleteObject(hFont);
+  DeleteDC(hDC);
+  return GetFont(pBaseDict);
+}
+#endif  //  defined(OS_WIN)
+
+size_t CPDF_DocPageData::CalculateEncodingDict(int 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)
+      break;
+  }
+  if (i == FX_ArraySize(g_FX_CharsetUnicodes))
+    return i;
+
+  CPDF_Dictionary* pEncodingDict =
+      GetDocument()->NewIndirect<CPDF_Dictionary>();
+  pEncodingDict->SetNewFor<CPDF_Name>("BaseEncoding", "WinAnsiEncoding");
+
+  CPDF_Array* pArray = pEncodingDict->SetNewFor<CPDF_Array>("Differences");
+  pArray->AddNew<CPDF_Number>(128);
+
+  const uint16_t* pUnicodes = g_FX_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);
+  }
+  pBaseDict->SetNewFor<CPDF_Reference>("Encoding", GetDocument(),
+                                       pEncodingDict->GetObjNum());
+  return i;
+}
+
+CPDF_Dictionary* CPDF_DocPageData::ProcessbCJK(
+    CPDF_Dictionary* pBaseDict,
+    int charset,
+    ByteString basefont,
+    std::function<void(wchar_t, wchar_t, CPDF_Array*)> Insert) {
+  CPDF_Dictionary* pFontDict = GetDocument()->NewIndirect<CPDF_Dictionary>();
+  ByteString cmap;
+  ByteString ordering;
+  int supplement = 0;
+  CPDF_Array* pWidthArray = pFontDict->SetNewFor<CPDF_Array>("W");
+  switch (charset) {
+    case FX_CHARSET_ChineseTraditional:
+      cmap = "ETenms-B5-H";
+      ordering = "CNS1";
+      supplement = 4;
+      pWidthArray->AddNew<CPDF_Number>(1);
+      Insert(0x20, 0x7e, pWidthArray);
+      break;
+    case FX_CHARSET_ChineseSimplified:
+      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);
+      break;
+    case FX_CHARSET_Hangul:
+      cmap = "KSCms-UHC-H";
+      ordering = "Korea1";
+      supplement = 2;
+      pWidthArray->AddNew<CPDF_Number>(1);
+      Insert(0x20, 0x7e, pWidthArray);
+      break;
+    case FX_CHARSET_ShiftJIS:
+      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);
+      break;
+  }
+  pBaseDict->SetNewFor<CPDF_Name>("Subtype", "Type0");
+  pBaseDict->SetNewFor<CPDF_Name>("BaseFont", basefont);
+  pBaseDict->SetNewFor<CPDF_Name>("Encoding", cmap);
+  pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
+  pFontDict->SetNewFor<CPDF_Name>("Subtype", "CIDFontType2");
+  pFontDict->SetNewFor<CPDF_Name>("BaseFont", basefont);
+
+  CPDF_Dictionary* 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());
+  return pFontDict;
 }
diff --git a/core/fpdfapi/page/cpdf_docpagedata.h b/core/fpdfapi/page/cpdf_docpagedata.h
index 02107aa..854d80d 100644
--- a/core/fpdfapi/page/cpdf_docpagedata.h
+++ b/core/fpdfapi/page/cpdf_docpagedata.h
@@ -8,16 +8,20 @@
 #define CORE_FPDFAPI_PAGE_CPDF_DOCPAGEDATA_H_
 
 #include <map>
+#include <memory>
 #include <set>
 
+#include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fpdfapi/parser/cpdf_document.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;
-class CPDF_Document;
-class CPDF_Font;
 class CPDF_FontEncoding;
 class CPDF_IccProfile;
 class CPDF_Image;
@@ -26,57 +30,89 @@
 class CPDF_Stream;
 class CPDF_StreamAcc;
 
-class CPDF_DocPageData {
+class CPDF_DocPageData : public CPDF_Document::PageDataIface,
+                         public CPDF_Font::FormFactoryIface {
  public:
-  explicit CPDF_DocPageData(CPDF_Document* pPDFDoc);
-  ~CPDF_DocPageData();
+  static CPDF_DocPageData* FromDocument(const CPDF_Document* pDoc);
 
-  void Clear(bool bRelease = false);
+  CPDF_DocPageData();
+  ~CPDF_DocPageData() override;
+
+  // CPDF_Document::PageDataIface:
+  void ClearStockFont() override;
+  RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
+      const CPDF_Stream* pFontStream) override;
+  void MaybePurgeFontFileStreamAcc(const CPDF_Stream* pFontStream) override;
+
+  // CPDF_Font::FormFactoryIFace:
+  std::unique_ptr<CPDF_Font::FormIface> CreateForm(
+      CPDF_Document* pDocument,
+      CPDF_Dictionary* pPageResources,
+      CPDF_Stream* pFormStream) override;
+
   bool IsForceClear() const { return m_bForceClear; }
 
-  CPDF_Font* GetFont(CPDF_Dictionary* pFontDict);
-  CPDF_Font* GetStandardFont(const ByteString& fontName,
-                             CPDF_FontEncoding* pEncoding);
-  void ReleaseFont(const CPDF_Dictionary* pFontDict);
+  RetainPtr<CPDF_Font> AddFont(std::unique_ptr<CFX_Font> pFont, int charset);
+  RetainPtr<CPDF_Font> GetFont(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)
+  RetainPtr<CPDF_Font> AddWindowsFont(LOGFONTA* pLogFont);
+#endif
 
-  CPDF_ColorSpace* GetColorSpace(CPDF_Object* pCSObj,
-                                 const CPDF_Dictionary* pResources);
-  CPDF_ColorSpace* GetColorSpaceGuarded(CPDF_Object* pCSObj,
-                                        const CPDF_Dictionary* pResources,
-                                        std::set<CPDF_Object*>* pVisited);
+  // Loads a colorspace.
+  RetainPtr<CPDF_ColorSpace> GetColorSpace(const CPDF_Object* pCSObj,
+                                           const CPDF_Dictionary* pResources);
 
-  CPDF_ColorSpace* GetCopiedColorSpace(CPDF_Object* pCSObj);
-  void ReleaseColorSpace(const CPDF_Object* pColorSpace);
+  // Loads a colorspace in a context that might be while loading another
+  // colorspace. |pVisited| is passed recursively to avoid circular calls
+  // involving CPDF_ColorSpace::Load().
+  RetainPtr<CPDF_ColorSpace> GetColorSpaceGuarded(
+      const CPDF_Object* pCSObj,
+      const CPDF_Dictionary* pResources,
+      std::set<const CPDF_Object*>* pVisited);
 
-  CPDF_Pattern* GetPattern(CPDF_Object* pPatternObj,
-                           bool bShading,
-                           const CFX_Matrix& matrix);
-  void ReleasePattern(const CPDF_Object* pPatternObj);
+  RetainPtr<CPDF_Pattern> GetPattern(CPDF_Object* pPatternObj,
+                                     bool bShading,
+                                     const CFX_Matrix& matrix);
 
   RetainPtr<CPDF_Image> GetImage(uint32_t dwStreamObjNum);
   void MaybePurgeImage(uint32_t dwStreamObjNum);
 
-  RetainPtr<CPDF_IccProfile> GetIccProfile(CPDF_Stream* pProfileStream);
-  void MaybePurgeIccProfile(CPDF_Stream* pProfileStream);
-
-  RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(CPDF_Stream* pFontStream);
-  void MaybePurgeFontFileStreamAcc(const CPDF_Stream* pFontStream);
-
-  CPDF_CountedColorSpace* FindColorSpacePtr(CPDF_Object* pCSObj) const;
-  CPDF_CountedPattern* FindPatternPtr(CPDF_Object* pPatternObj) const;
+  RetainPtr<CPDF_IccProfile> GetIccProfile(const CPDF_Stream* pProfileStream);
 
  private:
-  using CPDF_CountedFont = CPDF_CountedObject<CPDF_Font>;
+  // Loads a colorspace in a context that might be while loading another
+  // colorspace, or even in a recursive call from this method itself. |pVisited|
+  // is passed recursively to avoid circular calls involving
+  // CPDF_ColorSpace::Load() and |pVisitedInternal| is also passed recursively
+  // to avoid circular calls with this method calling itself.
+  RetainPtr<CPDF_ColorSpace> GetColorSpaceInternal(
+      const CPDF_Object* pCSObj,
+      const CPDF_Dictionary* pResources,
+      std::set<const CPDF_Object*>* pVisited,
+      std::set<const CPDF_Object*>* pVisitedInternal);
 
-  bool m_bForceClear;
-  UnownedPtr<CPDF_Document> const m_pPDFDoc;
-  std::map<ByteString, CPDF_Stream*> m_HashProfileMap;
-  std::map<const CPDF_Object*, CPDF_CountedColorSpace*> m_ColorSpaceMap;
+  size_t CalculateEncodingDict(int charset, CPDF_Dictionary* pBaseDict);
+  CPDF_Dictionary* ProcessbCJK(
+      CPDF_Dictionary* pBaseDict,
+      int 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_Dictionary*, CPDF_CountedFont*> m_FontMap;
-  std::map<const CPDF_Stream*, RetainPtr<CPDF_IccProfile>> m_IccProfileMap;
+  std::map<const CPDF_Stream*, ObservedPtr<CPDF_IccProfile>> m_IccProfileMap;
+  std::map<const CPDF_Object*, ObservedPtr<CPDF_Pattern>> m_PatternMap;
   std::map<uint32_t, RetainPtr<CPDF_Image>> m_ImageMap;
-  std::map<const CPDF_Object*, CPDF_CountedPattern*> m_PatternMap;
+  std::map<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 5d12131..7194f97 100644
--- a/core/fpdfapi/page/cpdf_expintfunc.cpp
+++ b/core/fpdfapi/page/cpdf_expintfunc.cpp
@@ -8,53 +8,58 @@
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/stl_util.h"
 
 CPDF_ExpIntFunc::CPDF_ExpIntFunc()
-    : CPDF_Function(Type::kType2ExpotentialInterpolation),
-      m_pBeginValues(nullptr),
-      m_pEndValues(nullptr) {}
+    : CPDF_Function(Type::kType2ExpotentialInterpolation) {}
 
-CPDF_ExpIntFunc::~CPDF_ExpIntFunc() {
-  FX_Free(m_pBeginValues);
-  FX_Free(m_pEndValues);
-}
+CPDF_ExpIntFunc::~CPDF_ExpIntFunc() = default;
 
-bool CPDF_ExpIntFunc::v_Init(CPDF_Object* pObj) {
-  CPDF_Dictionary* pDict = pObj->GetDict();
+bool CPDF_ExpIntFunc::v_Init(const CPDF_Object* pObj,
+                             std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Dictionary* pDict = pObj->GetDict();
   if (!pDict)
     return false;
 
-  CPDF_Array* pArray0 = pDict->GetArrayFor("C0");
-  if (m_nOutputs == 0) {
-    m_nOutputs = 1;
-    if (pArray0)
-      m_nOutputs = pArray0->GetCount();
-  }
-
-  CPDF_Array* pArray1 = pDict->GetArrayFor("C1");
-  m_pBeginValues = FX_Alloc2D(float, m_nOutputs, 2);
-  m_pEndValues = FX_Alloc2D(float, m_nOutputs, 2);
-  for (uint32_t i = 0; i < m_nOutputs; i++) {
-    m_pBeginValues[i] = pArray0 ? pArray0->GetFloatAt(i) : 0.0f;
-    m_pEndValues[i] = pArray1 ? pArray1->GetFloatAt(i) : 1.0f;
-  }
-
-  m_Exponent = pDict->GetFloatFor("N");
-  m_nOrigOutputs = m_nOutputs;
-  if (m_nOutputs && m_nInputs > INT_MAX / m_nOutputs)
+  const CPDF_Number* pExponent = ToNumber(pDict->GetObjectFor("N"));
+  if (!pExponent)
     return false;
 
-  m_nOutputs *= m_nInputs;
+  m_Exponent = pExponent->GetNumber();
+
+  const CPDF_Array* pArray0 = pDict->GetArrayFor("C0");
+  if (pArray0 && m_nOutputs == 0)
+    m_nOutputs = pArray0->size();
+  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);
+  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;
+  }
+
+  FX_SAFE_UINT32 nOutputs = m_nOutputs;
+  nOutputs *= m_nInputs;
+  if (!nOutputs.IsValid())
+    return false;
+
+  m_nOrigOutputs = m_nOutputs;
+  m_nOutputs = nOutputs.ValueOrDie();
   return true;
 }
 
-bool CPDF_ExpIntFunc::v_Call(float* inputs, float* results) const {
-  for (uint32_t i = 0; i < m_nInputs; i++)
+bool CPDF_ExpIntFunc::v_Call(const float* inputs, 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_pBeginValues[j] + FXSYS_pow(inputs[i], m_Exponent) *
-                                  (m_pEndValues[j] - m_pBeginValues[j]);
+          m_BeginValues[j] + FXSYS_pow(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 867d2fa..95bdab6 100644
--- a/core/fpdfapi/page/cpdf_expintfunc.h
+++ b/core/fpdfapi/page/cpdf_expintfunc.h
@@ -7,21 +7,25 @@
 #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"
 
-class CPDF_ExpIntFunc : public CPDF_Function {
+class CPDF_ExpIntFunc final : public CPDF_Function {
  public:
   CPDF_ExpIntFunc();
   ~CPDF_ExpIntFunc() override;
 
   // CPDF_Function
-  bool v_Init(CPDF_Object* pObj) override;
-  bool v_Call(float* inputs, float* results) const override;
+  bool v_Init(const CPDF_Object* pObj,
+              std::set<const CPDF_Object*>* pVisited) override;
+  bool v_Call(const float* inputs, float* results) const override;
 
-  uint32_t m_nOrigOutputs;
-  float m_Exponent;
-  float* m_pBeginValues;
-  float* m_pEndValues;
+  uint32_t m_nOrigOutputs = 0;
+  float m_Exponent = 0.0f;
+  std::vector<float> m_BeginValues;
+  std::vector<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 bb06cbf..ab3296e 100644
--- a/core/fpdfapi/page/cpdf_form.cpp
+++ b/core/fpdfapi/page/cpdf_form.cpp
@@ -6,57 +6,118 @@
 
 #include "core/fpdfapi/page/cpdf_form.h"
 
+#include <algorithm>
+
 #include "core/fpdfapi/page/cpdf_contentparser.h"
+#include "core/fpdfapi/page/cpdf_imageobject.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_stream.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "third_party/base/ptr_util.h"
 
+// static
+CPDF_Dictionary* CPDF_Form::ChooseResourcesDict(
+    CPDF_Dictionary* pResources,
+    CPDF_Dictionary* pParentResources,
+    CPDF_Dictionary* pPageResources) {
+  if (pResources)
+    return pResources;
+  return pParentResources ? pParentResources : pPageResources;
+}
+
+CPDF_Form::CPDF_Form(CPDF_Document* pDoc,
+                     CPDF_Dictionary* pPageResources,
+                     CPDF_Stream* pFormStream)
+    : CPDF_Form(pDoc, pPageResources, pFormStream, nullptr) {}
+
 CPDF_Form::CPDF_Form(CPDF_Document* pDoc,
                      CPDF_Dictionary* pPageResources,
                      CPDF_Stream* pFormStream,
                      CPDF_Dictionary* pParentResources)
-    : CPDF_PageObjectHolder(pDoc, pFormStream->GetDict()) {
-  m_pFormStream = pFormStream;
-  m_pResources = m_pFormDict->GetDictFor("Resources");
-  m_pPageResources = pPageResources;
-  if (!m_pResources)
-    m_pResources = pParentResources;
-  if (!m_pResources)
-    m_pResources = pPageResources;
-  m_iTransparency = 0;
-  LoadTransInfo();
+    : CPDF_PageObjectHolder(
+          pDoc,
+          pFormStream->GetDict(),
+          pPageResources,
+          ChooseResourcesDict(pFormStream->GetDict()->GetDictFor("Resources"),
+                              pParentResources,
+                              pPageResources)),
+      m_pFormStream(pFormStream) {
+  LoadTransparencyInfo();
 }
 
-CPDF_Form::~CPDF_Form() {}
-
-void CPDF_Form::StartParse(CPDF_AllStates* pGraphicStates,
-                           const CFX_Matrix* pParentMatrix,
-                           CPDF_Type3Char* pType3Char,
-                           std::set<const uint8_t*>* parsedSet) {
-  if (m_ParseState == CONTENT_PARSED || m_ParseState == CONTENT_PARSING)
-    return;
-
-  if (!parsedSet) {
-    if (!m_ParsedSet)
-      m_ParsedSet = pdfium::MakeUnique<std::set<const uint8_t*>>();
-    parsedSet = m_ParsedSet.get();
-  }
-
-  m_pParser = pdfium::MakeUnique<CPDF_ContentParser>(
-      this, pGraphicStates, pParentMatrix, pType3Char, parsedSet);
-  m_ParseState = CONTENT_PARSING;
-}
+CPDF_Form::~CPDF_Form() = default;
 
 void CPDF_Form::ParseContent() {
-  ParseContentWithParams(nullptr, nullptr, nullptr, nullptr);
+  ParseContentInternal(nullptr, nullptr, nullptr, nullptr);
 }
 
-void CPDF_Form::ParseContentWithParams(CPDF_AllStates* pGraphicStates,
-                                       const CFX_Matrix* pParentMatrix,
-                                       CPDF_Type3Char* pType3Char,
-                                       std::set<const uint8_t*>* parsedSet) {
-  StartParse(pGraphicStates, pParentMatrix, pType3Char, parsedSet);
+void CPDF_Form::ParseContent(const CPDF_AllStates* pGraphicStates,
+                             const CFX_Matrix* pParentMatrix,
+                             std::set<const uint8_t*>* pParsedSet) {
+  ParseContentInternal(pGraphicStates, pParentMatrix, nullptr, pParsedSet);
+}
+
+void CPDF_Form::ParseContentForType3Char(CPDF_Type3Char* pType3Char) {
+  ParseContentInternal(nullptr, nullptr, pType3Char, nullptr);
+}
+
+void CPDF_Form::ParseContentInternal(const CPDF_AllStates* pGraphicStates,
+                                     const CFX_Matrix* pParentMatrix,
+                                     CPDF_Type3Char* pType3Char,
+                                     std::set<const uint8_t*>* pParsedSet) {
+  if (GetParseState() == ParseState::kParsed)
+    return;
+
+  if (GetParseState() == ParseState::kNotParsed) {
+    if (!pParsedSet) {
+      if (!m_ParsedSet)
+        m_ParsedSet = pdfium::MakeUnique<std::set<const uint8_t*>>();
+      pParsedSet = m_ParsedSet.get();
+    }
+    StartParse(pdfium::MakeUnique<CPDF_ContentParser>(
+        this, pGraphicStates, pParentMatrix, pType3Char, pParsedSet));
+  }
+
+  ASSERT(GetParseState() == ParseState::kParsing);
   ContinueParse(nullptr);
 }
+
+bool CPDF_Form::HasPageObjects() const {
+  return GetPageObjectCount() != 0;
+}
+
+CFX_FloatRect CPDF_Form::CalcBoundingBox() const {
+  if (GetPageObjectCount() == 0)
+    return CFX_FloatRect();
+
+  float left = 1000000.0f;
+  float right = -1000000.0f;
+  float bottom = 1000000.0f;
+  float top = -1000000.0f;
+  for (const auto& pObj : *this) {
+    const auto& rect = pObj->GetRect();
+    left = std::min(left, rect.left);
+    right = std::max(right, rect.right);
+    bottom = std::min(bottom, rect.bottom);
+    top = std::max(top, rect.top);
+  }
+  return CFX_FloatRect(left, bottom, right, top);
+}
+
+const CPDF_Stream* CPDF_Form::GetStream() const {
+  return m_pFormStream.Get();
+}
+
+Optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
+CPDF_Form::GetBitmapAndMatrixFromSoleImageOfForm() const {
+  if (GetPageObjectCount() != 1)
+    return {};
+
+  CPDF_ImageObject* pImageObject = (*begin())->AsImage();
+  if (!pImageObject)
+    return {};
+
+  return {{pImageObject->GetIndependentBitmap(), pImageObject->matrix()}};
+}
diff --git a/core/fpdfapi/page/cpdf_form.h b/core/fpdfapi/page/cpdf_form.h
index c5285a1..e8b9d1f 100644
--- a/core/fpdfapi/page/cpdf_form.h
+++ b/core/fpdfapi/page/cpdf_form.h
@@ -9,37 +9,58 @@
 
 #include <memory>
 #include <set>
+#include <utility>
 
+#include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 
-class CPDF_Document;
-class CPDF_Dictionary;
-class CPDF_Stream;
-class CPDF_AllStates;
 class CFX_Matrix;
+class CPDF_AllStates;
+class CPDF_Dictionary;
+class CPDF_Document;
+class CPDF_ImageObject;
+class CPDF_Stream;
 class CPDF_Type3Char;
 
-class CPDF_Form : public CPDF_PageObjectHolder {
+class CPDF_Form final : public CPDF_PageObjectHolder,
+                        public CPDF_Font::FormIface {
  public:
+  // Helper method to choose the first non-null resources dictionary.
+  static CPDF_Dictionary* ChooseResourcesDict(CPDF_Dictionary* pResources,
+                                              CPDF_Dictionary* pParentResources,
+                                              CPDF_Dictionary* pPageResources);
+
+  CPDF_Form(CPDF_Document* pDocument,
+            CPDF_Dictionary* pPageResources,
+            CPDF_Stream* pFormStream);
   CPDF_Form(CPDF_Document* pDocument,
             CPDF_Dictionary* pPageResources,
             CPDF_Stream* pFormStream,
-            CPDF_Dictionary* pParentResources = nullptr);
+            CPDF_Dictionary* pParentResources);
   ~CPDF_Form() override;
 
+  // CPDF_Font::FormIface:
+  void ParseContentForType3Char(CPDF_Type3Char* pType3Char) override;
+  bool HasPageObjects() const override;
+  CFX_FloatRect CalcBoundingBox() const override;
+  Optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
+  GetBitmapAndMatrixFromSoleImageOfForm() const override;
+
   void ParseContent();
-  void ParseContentWithParams(CPDF_AllStates* pGraphicStates,
-                              const CFX_Matrix* pParentMatrix,
-                              CPDF_Type3Char* pType3Char,
-                              std::set<const uint8_t*>* parsedSet);
+  void ParseContent(const CPDF_AllStates* pGraphicStates,
+                    const CFX_Matrix* pParentMatrix,
+                    std::set<const uint8_t*>* pParsedSet);
+
+  const CPDF_Stream* GetStream() const;
 
  private:
-  void StartParse(CPDF_AllStates* pGraphicStates,
-                  const CFX_Matrix* pParentMatrix,
-                  CPDF_Type3Char* pType3Char,
-                  std::set<const uint8_t*>* parsedSet);
+  void ParseContentInternal(const CPDF_AllStates* pGraphicStates,
+                            const CFX_Matrix* pParentMatrix,
+                            CPDF_Type3Char* pType3Char,
+                            std::set<const uint8_t*>* pParsedSet);
 
   std::unique_ptr<std::set<const uint8_t*>> m_ParsedSet;
+  RetainPtr<CPDF_Stream> const m_pFormStream;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_FORM_H_
diff --git a/core/fpdfapi/page/cpdf_formobject.cpp b/core/fpdfapi/page/cpdf_formobject.cpp
index eca92ca..a7c2643 100644
--- a/core/fpdfapi/page/cpdf_formobject.cpp
+++ b/core/fpdfapi/page/cpdf_formobject.cpp
@@ -10,9 +10,12 @@
 
 #include "core/fpdfapi/page/cpdf_form.h"
 
-CPDF_FormObject::CPDF_FormObject(std::unique_ptr<CPDF_Form> pForm,
+CPDF_FormObject::CPDF_FormObject(int32_t content_stream,
+                                 std::unique_ptr<CPDF_Form> pForm,
                                  const CFX_Matrix& matrix)
-    : m_pForm(std::move(pForm)), m_FormMatrix(matrix) {}
+    : CPDF_PageObject(content_stream),
+      m_pForm(std::move(pForm)),
+      m_FormMatrix(matrix) {}
 
 CPDF_FormObject::~CPDF_FormObject() {}
 
@@ -38,10 +41,5 @@
 }
 
 void CPDF_FormObject::CalcBoundingBox() {
-  CFX_FloatRect form_rect =
-      m_FormMatrix.TransformRect(m_pForm->CalcBoundingBox());
-  m_Left = form_rect.left;
-  m_Bottom = form_rect.bottom;
-  m_Right = form_rect.right;
-  m_Top = form_rect.top;
+  SetRect(m_FormMatrix.TransformRect(m_pForm->CalcBoundingBox()));
 }
diff --git a/core/fpdfapi/page/cpdf_formobject.h b/core/fpdfapi/page/cpdf_formobject.h
index c723cc0..c24bfbf 100644
--- a/core/fpdfapi/page/cpdf_formobject.h
+++ b/core/fpdfapi/page/cpdf_formobject.h
@@ -14,9 +14,11 @@
 
 class CPDF_Form;
 
-class CPDF_FormObject : public CPDF_PageObject {
+class CPDF_FormObject final : public CPDF_PageObject {
  public:
-  CPDF_FormObject(std::unique_ptr<CPDF_Form> pForm, const CFX_Matrix& matrix);
+  CPDF_FormObject(int32_t content_stream,
+                  std::unique_ptr<CPDF_Form> pForm,
+                  const CFX_Matrix& matrix);
   ~CPDF_FormObject() override;
 
   // CPDF_PageObject:
@@ -31,7 +33,7 @@
   const CFX_Matrix& form_matrix() const { return m_FormMatrix; }
 
  private:
-  const std::unique_ptr<CPDF_Form> m_pForm;
+  std::unique_ptr<CPDF_Form> const m_pForm;
   CFX_Matrix m_FormMatrix;
 };
 
diff --git a/core/fpdfapi/page/cpdf_function.cpp b/core/fpdfapi/page/cpdf_function.cpp
index 635c53a..688c48e 100644
--- a/core/fpdfapi/page/cpdf_function.cpp
+++ b/core/fpdfapi/page/cpdf_function.cpp
@@ -6,6 +6,8 @@
 
 #include "core/fpdfapi/page/cpdf_function.h"
 
+#include <vector>
+
 #include "core/fpdfapi/page/cpdf_expintfunc.h"
 #include "core/fpdfapi/page/cpdf_psfunc.h"
 #include "core/fpdfapi/page/cpdf_sampledfunc.h"
@@ -13,20 +15,36 @@
 #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/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"
 
 // static
-std::unique_ptr<CPDF_Function> CPDF_Function::Load(CPDF_Object* pFuncObj) {
-  std::unique_ptr<CPDF_Function> pFunc;
+std::unique_ptr<CPDF_Function> CPDF_Function::Load(
+    const CPDF_Object* pFuncObj) {
+  std::set<const CPDF_Object*> visited;
+  return Load(pFuncObj, &visited);
+}
+
+// static
+std::unique_ptr<CPDF_Function> CPDF_Function::Load(
+    const CPDF_Object* pFuncObj,
+    std::set<const CPDF_Object*>* pVisited) {
   if (!pFuncObj)
-    return pFunc;
+    return nullptr;
+
+  if (pdfium::ContainsKey(*pVisited, pFuncObj))
+    return nullptr;
+  pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pFuncObj);
 
   int iType = -1;
-  if (CPDF_Stream* pStream = pFuncObj->AsStream())
+  if (const CPDF_Stream* pStream = pFuncObj->AsStream())
     iType = pStream->GetDict()->GetIntegerFor("FunctionType");
-  else if (CPDF_Dictionary* pDict = pFuncObj->AsDictionary())
+  else if (const CPDF_Dictionary* pDict = pFuncObj->AsDictionary())
     iType = pDict->GetIntegerFor("FunctionType");
 
+  std::unique_ptr<CPDF_Function> pFunc;
   Type type = IntegerToFunctionType(iType);
   if (type == Type::kType0Sampled)
     pFunc = pdfium::MakeUnique<CPDF_SampledFunc>();
@@ -37,7 +55,7 @@
   else if (type == Type::kType4PostScript)
     pFunc = pdfium::MakeUnique<CPDF_PSFunc>();
 
-  if (!pFunc || !pFunc->Init(pFuncObj))
+  if (!pFunc || !pFunc->Init(pFuncObj, pVisited))
     return nullptr;
 
   return pFunc;
@@ -56,52 +74,55 @@
   }
 }
 
-CPDF_Function::CPDF_Function(Type type)
-    : m_pDomains(nullptr), m_pRanges(nullptr), m_Type(type) {}
+CPDF_Function::CPDF_Function(Type type) : m_Type(type) {}
 
-CPDF_Function::~CPDF_Function() {
-  FX_Free(m_pDomains);
-  FX_Free(m_pRanges);
-}
+CPDF_Function::~CPDF_Function() = default;
 
-bool CPDF_Function::Init(CPDF_Object* pObj) {
-  CPDF_Stream* pStream = pObj->AsStream();
-  CPDF_Dictionary* pDict = pStream ? pStream->GetDict() : pObj->AsDictionary();
+bool CPDF_Function::Init(const CPDF_Object* pObj,
+                         std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Stream* pStream = pObj->AsStream();
+  const CPDF_Dictionary* pDict =
+      pStream ? pStream->GetDict() : pObj->AsDictionary();
 
-  CPDF_Array* pDomains = pDict->GetArrayFor("Domain");
+  const CPDF_Array* pDomains = pDict->GetArrayFor("Domain");
   if (!pDomains)
     return false;
 
-  m_nInputs = pDomains->GetCount() / 2;
+  m_nInputs = pDomains->size() / 2;
   if (m_nInputs == 0)
     return false;
 
-  m_pDomains = FX_Alloc2D(float, m_nInputs, 2);
-  for (uint32_t i = 0; i < m_nInputs * 2; i++) {
-    m_pDomains[i] = pDomains->GetFloatAt(i);
-  }
-  CPDF_Array* pRanges = pDict->GetArrayFor("Range");
-  m_nOutputs = 0;
-  if (pRanges) {
-    m_nOutputs = pRanges->GetCount() / 2;
-    m_pRanges = FX_Alloc2D(float, m_nOutputs, 2);
-    for (uint32_t i = 0; i < m_nOutputs * 2; i++)
-      m_pRanges[i] = pRanges->GetFloatAt(i);
-  }
-  uint32_t old_outputs = m_nOutputs;
-  if (!v_Init(pObj))
+  size_t nInputs = m_nInputs * 2;
+  m_Domains = ReadArrayElementsToVector(pDomains, nInputs);
+
+  const CPDF_Array* pRanges = pDict->GetArrayFor("Range");
+  m_nOutputs = pRanges ? pRanges->size() / 2 : 0;
+
+  // Ranges are required for type 0 and type 4 functions. A non-zero
+  // |m_nOutputs| here implied Ranges meets the requirements.
+  bool bRangeRequired =
+      m_Type == Type::kType0Sampled || m_Type == Type::kType4PostScript;
+  if (bRangeRequired && m_nOutputs == 0)
     return false;
-  if (m_pRanges && m_nOutputs > old_outputs) {
-    m_pRanges = FX_Realloc(float, m_pRanges, m_nOutputs * 2);
-    if (m_pRanges) {
-      memset(m_pRanges + (old_outputs * 2), 0,
-             sizeof(float) * (m_nOutputs - old_outputs) * 2);
-    }
+
+  if (m_nOutputs > 0) {
+    size_t nOutputs = m_nOutputs * 2;
+    m_Ranges = ReadArrayElementsToVector(pRanges, nOutputs);
+  }
+
+  uint32_t old_outputs = m_nOutputs;
+  if (!v_Init(pObj, pVisited))
+    return false;
+
+  if (!m_Ranges.empty() && m_nOutputs > old_outputs) {
+    FX_SAFE_SIZE_T nOutputs = m_nOutputs;
+    nOutputs *= 2;
+    m_Ranges.resize(nOutputs.ValueOrDie());
   }
   return true;
 }
 
-bool CPDF_Function::Call(float* inputs,
+bool CPDF_Function::Call(const float* inputs,
                          uint32_t ninputs,
                          float* results,
                          int* nresults) const {
@@ -109,17 +130,20 @@
     return false;
 
   *nresults = m_nOutputs;
+  std::vector<float> clamped_inputs(m_nInputs);
   for (uint32_t i = 0; i < m_nInputs; i++) {
-    inputs[i] =
-        pdfium::clamp(inputs[i], m_pDomains[i * 2], m_pDomains[i * 2 + 1]);
+    clamped_inputs[i] =
+        pdfium::clamp(inputs[i], m_Domains[i * 2], m_Domains[i * 2 + 1]);
   }
-  v_Call(inputs, results);
-  if (!m_pRanges)
+  if (!v_Call(clamped_inputs.data(), results))
+    return false;
+
+  if (m_Ranges.empty())
     return true;
 
   for (uint32_t i = 0; i < m_nOutputs; i++) {
     results[i] =
-        pdfium::clamp(results[i], m_pRanges[i * 2], m_pRanges[i * 2 + 1]);
+        pdfium::clamp(results[i], m_Ranges[i * 2], m_Ranges[i * 2 + 1]);
   }
   return true;
 }
diff --git a/core/fpdfapi/page/cpdf_function.h b/core/fpdfapi/page/cpdf_function.h
index ff7cf84..5f4e125 100644
--- a/core/fpdfapi/page/cpdf_function.h
+++ b/core/fpdfapi/page/cpdf_function.h
@@ -8,6 +8,8 @@
 #define CORE_FPDFAPI_PAGE_CPDF_FUNCTION_H_
 
 #include <memory>
+#include <set>
+#include <vector>
 
 class CPDF_ExpIntFunc;
 class CPDF_Object;
@@ -24,19 +26,19 @@
     kType4PostScript = 4,
   };
 
-  static std::unique_ptr<CPDF_Function> Load(CPDF_Object* pFuncObj);
+  static std::unique_ptr<CPDF_Function> Load(const CPDF_Object* pFuncObj);
   static Type IntegerToFunctionType(int iType);
 
   virtual ~CPDF_Function();
 
-  bool Call(float* inputs,
+  bool Call(const float* inputs,
             uint32_t ninputs,
             float* results,
             int* nresults) const;
   uint32_t CountInputs() const { return m_nInputs; }
   uint32_t CountOutputs() const { return m_nOutputs; }
-  float GetDomain(int i) const { return m_pDomains[i]; }
-  float GetRange(int i) const { return m_pRanges[i]; }
+  float GetDomain(int i) const { return m_Domains[i]; }
+  float GetRange(int i) const { return m_Ranges[i]; }
   float Interpolate(float x,
                     float xmin,
                     float xmax,
@@ -50,15 +52,19 @@
  protected:
   explicit CPDF_Function(Type type);
 
-  bool Init(CPDF_Object* pObj);
-  virtual bool v_Init(CPDF_Object* pObj) = 0;
-  virtual bool v_Call(float* inputs, float* results) const = 0;
+  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;
 
+  const Type m_Type;
   uint32_t m_nInputs;
   uint32_t m_nOutputs;
-  float* m_pDomains;
-  float* m_pRanges;
-  const Type m_Type;
+  std::vector<float> m_Domains;
+  std::vector<float> m_Ranges;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_FUNCTION_H_
diff --git a/core/fpdfapi/page/cpdf_generalstate.cpp b/core/fpdfapi/page/cpdf_generalstate.cpp
index 845cedd..6463b57 100644
--- a/core/fpdfapi/page/cpdf_generalstate.cpp
+++ b/core/fpdfapi/page/cpdf_generalstate.cpp
@@ -6,10 +6,8 @@
 
 #include "core/fpdfapi/page/cpdf_generalstate.h"
 
+#include "core/fpdfapi/page/cpdf_transferfunc.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/render/cpdf_dibsource.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
-#include "core/fpdfapi/render/cpdf_transferfunc.h"
 
 namespace {
 
@@ -27,43 +25,43 @@
   return 0;
 }
 
-int GetBlendTypeInternal(const ByteString& mode) {
+BlendMode GetBlendTypeInternal(const ByteString& mode) {
   switch (mode.GetID()) {
     case FXBSTR_ID('N', 'o', 'r', 'm'):
     case FXBSTR_ID('C', 'o', 'm', 'p'):
-      return FXDIB_BLEND_NORMAL;
+      return BlendMode::kNormal;
     case FXBSTR_ID('M', 'u', 'l', 't'):
-      return FXDIB_BLEND_MULTIPLY;
+      return BlendMode::kMultiply;
     case FXBSTR_ID('S', 'c', 'r', 'e'):
-      return FXDIB_BLEND_SCREEN;
+      return BlendMode::kScreen;
     case FXBSTR_ID('O', 'v', 'e', 'r'):
-      return FXDIB_BLEND_OVERLAY;
+      return BlendMode::kOverlay;
     case FXBSTR_ID('D', 'a', 'r', 'k'):
-      return FXDIB_BLEND_DARKEN;
+      return BlendMode::kDarken;
     case FXBSTR_ID('L', 'i', 'g', 'h'):
-      return FXDIB_BLEND_LIGHTEN;
+      return BlendMode::kLighten;
     case FXBSTR_ID('C', 'o', 'l', 'o'):
       if (mode.GetLength() == 10)
-        return FXDIB_BLEND_COLORDODGE;
+        return BlendMode::kColorDodge;
       if (mode.GetLength() == 9)
-        return FXDIB_BLEND_COLORBURN;
-      return FXDIB_BLEND_COLOR;
+        return BlendMode::kColorBurn;
+      return BlendMode::kColor;
     case FXBSTR_ID('H', 'a', 'r', 'd'):
-      return FXDIB_BLEND_HARDLIGHT;
+      return BlendMode::kHardLight;
     case FXBSTR_ID('S', 'o', 'f', 't'):
-      return FXDIB_BLEND_SOFTLIGHT;
+      return BlendMode::kSoftLight;
     case FXBSTR_ID('D', 'i', 'f', 'f'):
-      return FXDIB_BLEND_DIFFERENCE;
+      return BlendMode::kDifference;
     case FXBSTR_ID('E', 'x', 'c', 'l'):
-      return FXDIB_BLEND_EXCLUSION;
+      return BlendMode::kExclusion;
     case FXBSTR_ID('H', 'u', 'e', 0):
-      return FXDIB_BLEND_HUE;
+      return BlendMode::kHue;
     case FXBSTR_ID('S', 'a', 't', 'u'):
-      return FXDIB_BLEND_SATURATION;
+      return BlendMode::kSaturation;
     case FXBSTR_ID('L', 'u', 'm', 'i'):
-      return FXDIB_BLEND_LUMINOSITY;
+      return BlendMode::kLuminosity;
   }
-  return FXDIB_BLEND_NORMAL;
+  return BlendMode::kNormal;
 }
 
 }  // namespace
@@ -78,50 +76,51 @@
 void CPDF_GeneralState::SetRenderIntent(const ByteString& ri) {
   m_Ref.GetPrivateCopy()->m_RenderIntent = RI_StringToId(ri);
 }
+
 ByteString CPDF_GeneralState::GetBlendMode() const {
   switch (GetBlendType()) {
-    case FXDIB_BLEND_NORMAL:
-      return ByteString("Normal");
-    case FXDIB_BLEND_MULTIPLY:
-      return ByteString("Multiply");
-    case FXDIB_BLEND_SCREEN:
-      return ByteString("Screen");
-    case FXDIB_BLEND_OVERLAY:
-      return ByteString("Overlay");
-    case FXDIB_BLEND_DARKEN:
-      return ByteString("Darken");
-    case FXDIB_BLEND_LIGHTEN:
-      return ByteString("Lighten");
-    case FXDIB_BLEND_COLORDODGE:
-      return ByteString("ColorDodge");
-    case FXDIB_BLEND_COLORBURN:
-      return ByteString("ColorBurn");
-    case FXDIB_BLEND_HARDLIGHT:
-      return ByteString("HardLight");
-    case FXDIB_BLEND_SOFTLIGHT:
-      return ByteString("SoftLight");
-    case FXDIB_BLEND_DIFFERENCE:
-      return ByteString("Difference");
-    case FXDIB_BLEND_EXCLUSION:
-      return ByteString("Exclusion");
-    case FXDIB_BLEND_HUE:
-      return ByteString("Hue");
-    case FXDIB_BLEND_SATURATION:
-      return ByteString("Saturation");
-    case FXDIB_BLEND_COLOR:
-      return ByteString("Color");
-    case FXDIB_BLEND_LUMINOSITY:
-      return ByteString("Luminosity");
+    case BlendMode::kNormal:
+      return ByteString(pdfium::transparency::kNormal);
+    case BlendMode::kMultiply:
+      return ByteString(pdfium::transparency::kMultiply);
+    case BlendMode::kScreen:
+      return ByteString(pdfium::transparency::kScreen);
+    case BlendMode::kOverlay:
+      return ByteString(pdfium::transparency::kOverlay);
+    case BlendMode::kDarken:
+      return ByteString(pdfium::transparency::kDarken);
+    case BlendMode::kLighten:
+      return ByteString(pdfium::transparency::kLighten);
+    case BlendMode::kColorDodge:
+      return ByteString(pdfium::transparency::kColorDodge);
+    case BlendMode::kColorBurn:
+      return ByteString(pdfium::transparency::kColorBurn);
+    case BlendMode::kHardLight:
+      return ByteString(pdfium::transparency::kHardLight);
+    case BlendMode::kSoftLight:
+      return ByteString(pdfium::transparency::kSoftLight);
+    case BlendMode::kDifference:
+      return ByteString(pdfium::transparency::kDifference);
+    case BlendMode::kExclusion:
+      return ByteString(pdfium::transparency::kExclusion);
+    case BlendMode::kHue:
+      return ByteString(pdfium::transparency::kHue);
+    case BlendMode::kSaturation:
+      return ByteString(pdfium::transparency::kSaturation);
+    case BlendMode::kColor:
+      return ByteString(pdfium::transparency::kColor);
+    case BlendMode::kLuminosity:
+      return ByteString(pdfium::transparency::kLuminosity);
   }
-  return ByteString("Normal");
+  return ByteString(pdfium::transparency::kNormal);
 }
 
-int CPDF_GeneralState::GetBlendType() const {
+BlendMode CPDF_GeneralState::GetBlendType() const {
   const StateData* pData = m_Ref.GetObject();
-  return pData ? pData->m_BlendType : FXDIB_BLEND_NORMAL;
+  return pData ? pData->m_BlendType : BlendMode::kNormal;
 }
 
-void CPDF_GeneralState::SetBlendType(int type) {
+void CPDF_GeneralState::SetBlendType(BlendMode type) {
   m_Ref.GetPrivateCopy()->m_BlendType = type;
 }
 
@@ -149,16 +148,16 @@
 }
 
 void CPDF_GeneralState::SetSoftMask(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pSoftMask = pObject;
+  m_Ref.GetPrivateCopy()->m_pSoftMask.Reset(pObject);
 }
 
-CPDF_Object* CPDF_GeneralState::GetTR() const {
+const CPDF_Object* CPDF_GeneralState::GetTR() const {
   const StateData* pData = m_Ref.GetObject();
   return pData ? pData->m_pTR.Get() : nullptr;
 }
 
 void CPDF_GeneralState::SetTR(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pTR = pObject;
+  m_Ref.GetPrivateCopy()->m_pTR.Reset(pObject);
 }
 
 RetainPtr<CPDF_TransferFunc> CPDF_GeneralState::GetTransferFunc() const {
@@ -213,15 +212,15 @@
 }
 
 void CPDF_GeneralState::SetBG(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pBG = pObject;
+  m_Ref.GetPrivateCopy()->m_pBG.Reset(pObject);
 }
 
 void CPDF_GeneralState::SetUCR(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pUCR = pObject;
+  m_Ref.GetPrivateCopy()->m_pUCR.Reset(pObject);
 }
 
 void CPDF_GeneralState::SetHT(CPDF_Object* pObject) {
-  m_Ref.GetPrivateCopy()->m_pHT = pObject;
+  m_Ref.GetPrivateCopy()->m_pHT.Reset(pObject);
 }
 
 void CPDF_GeneralState::SetFlatness(float flatness) {
@@ -257,38 +256,18 @@
   return &m_Ref.GetPrivateCopy()->m_Matrix;
 }
 
-CPDF_GeneralState::StateData::StateData()
-    : m_BlendMode("Normal"),
-      m_BlendType(0),
-      m_pSoftMask(nullptr),
-      m_StrokeAlpha(1.0),
-      m_FillAlpha(1.0f),
-      m_pTR(nullptr),
-      m_pTransferFunc(nullptr),
-      m_RenderIntent(0),
-      m_StrokeAdjust(false),
-      m_AlphaSource(false),
-      m_TextKnockout(false),
-      m_StrokeOP(false),
-      m_FillOP(false),
-      m_OPMode(0),
-      m_pBG(nullptr),
-      m_pUCR(nullptr),
-      m_pHT(nullptr),
-      m_Flatness(1.0f),
-      m_Smoothness(0.0f) {
-  m_SMaskMatrix.SetIdentity();
-  m_Matrix.SetIdentity();
-}
+CPDF_GeneralState::StateData::StateData() = default;
 
 CPDF_GeneralState::StateData::StateData(const StateData& that)
     : m_BlendMode(that.m_BlendMode),
       m_BlendType(that.m_BlendType),
       m_pSoftMask(that.m_pSoftMask),
+      m_SMaskMatrix(that.m_SMaskMatrix),
       m_StrokeAlpha(that.m_StrokeAlpha),
       m_FillAlpha(that.m_FillAlpha),
       m_pTR(that.m_pTR),
       m_pTransferFunc(that.m_pTransferFunc),
+      m_Matrix(that.m_Matrix),
       m_RenderIntent(that.m_RenderIntent),
       m_StrokeAdjust(that.m_StrokeAdjust),
       m_AlphaSource(that.m_AlphaSource),
@@ -300,25 +279,11 @@
       m_pUCR(that.m_pUCR),
       m_pHT(that.m_pHT),
       m_Flatness(that.m_Flatness),
-      m_Smoothness(that.m_Smoothness) {
-  m_Matrix = that.m_Matrix;
-  m_SMaskMatrix = that.m_SMaskMatrix;
+      m_Smoothness(that.m_Smoothness) {}
 
-  if (that.m_pTransferFunc && that.m_pTransferFunc->GetDocument()) {
-    CPDF_DocRenderData* pDocCache =
-        that.m_pTransferFunc->GetDocument()->GetRenderData();
-    if (pDocCache)
-      m_pTransferFunc = pDocCache->GetTransferFunc(m_pTR.Get());
-  }
-}
+CPDF_GeneralState::StateData::~StateData() = default;
 
-CPDF_GeneralState::StateData::~StateData() {
-  if (m_pTransferFunc && m_pTransferFunc->GetDocument()) {
-    CPDF_DocRenderData* pDocCache =
-        m_pTransferFunc->GetDocument()->GetRenderData();
-    if (pDocCache) {
-      m_pTransferFunc.Reset();  // Give up our reference first.
-      pDocCache->MaybePurgeTransferFunc(m_pTR.Get());
-    }
-  }
+RetainPtr<CPDF_GeneralState::StateData> CPDF_GeneralState::StateData::Clone()
+    const {
+  return pdfium::MakeRetain<CPDF_GeneralState::StateData>(*this);
 }
diff --git a/core/fpdfapi/page/cpdf_generalstate.h b/core/fpdfapi/page/cpdf_generalstate.h
index 5d6d5dc..f374380 100644
--- a/core/fpdfapi/page/cpdf_generalstate.h
+++ b/core/fpdfapi/page/cpdf_generalstate.h
@@ -7,10 +7,11 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_GENERALSTATE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_GENERALSTATE_H_
 
+#include "constants/transparency.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/fxcrt/unowned_ptr.h"
 #include "core/fxge/fx_dib.h"
 
 class CPDF_Object;
@@ -28,8 +29,8 @@
   void SetRenderIntent(const ByteString& ri);
 
   ByteString GetBlendMode() const;
-  int GetBlendType() const;
-  void SetBlendType(int type);
+  BlendMode GetBlendType() const;
+  void SetBlendType(BlendMode type);
 
   float GetFillAlpha() const;
   void SetFillAlpha(float alpha);
@@ -40,7 +41,7 @@
   CPDF_Object* GetSoftMask() const;
   void SetSoftMask(CPDF_Object* pObject);
 
-  CPDF_Object* GetTR() const;
+  const CPDF_Object* GetTR() const;
   void SetTR(CPDF_Object* pObject);
 
   RetainPtr<CPDF_TransferFunc> GetTransferFunc() const;
@@ -77,33 +78,39 @@
   CFX_Matrix* GetMutableMatrix();
 
  private:
-  class StateData : public Retainable {
+  class StateData final : public Retainable {
    public:
+    template <typename T, typename... Args>
+    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+    RetainPtr<StateData> Clone() const;
+
+    ByteString m_BlendMode = pdfium::transparency::kNormal;
+    BlendMode m_BlendType = BlendMode::kNormal;
+    RetainPtr<CPDF_Object> m_pSoftMask;
+    CFX_Matrix m_SMaskMatrix;
+    float m_StrokeAlpha = 1.0f;
+    float m_FillAlpha = 1.0f;
+    RetainPtr<const CPDF_Object> m_pTR;
+    RetainPtr<CPDF_TransferFunc> m_pTransferFunc;
+    CFX_Matrix m_Matrix;
+    int m_RenderIntent = 0;
+    bool m_StrokeAdjust = false;
+    bool m_AlphaSource = false;
+    bool m_TextKnockout = false;
+    bool m_StrokeOP = false;
+    bool m_FillOP = false;
+    int m_OPMode = 0;
+    RetainPtr<const CPDF_Object> m_pBG;
+    RetainPtr<const CPDF_Object> m_pUCR;
+    RetainPtr<const CPDF_Object> m_pHT;
+    float m_Flatness = 1.0f;
+    float m_Smoothness = 0.0f;
+
+   private:
     StateData();
     StateData(const StateData& that);
     ~StateData() override;
-
-    ByteString m_BlendMode;
-    int m_BlendType;
-    UnownedPtr<CPDF_Object> m_pSoftMask;
-    CFX_Matrix m_SMaskMatrix;
-    float m_StrokeAlpha;
-    float m_FillAlpha;
-    UnownedPtr<CPDF_Object> m_pTR;
-    RetainPtr<CPDF_TransferFunc> m_pTransferFunc;
-    CFX_Matrix m_Matrix;
-    int m_RenderIntent;
-    bool m_StrokeAdjust;
-    bool m_AlphaSource;
-    bool m_TextKnockout;
-    bool m_StrokeOP;
-    bool m_FillOP;
-    int m_OPMode;
-    UnownedPtr<CPDF_Object> m_pBG;
-    UnownedPtr<CPDF_Object> m_pUCR;
-    UnownedPtr<CPDF_Object> m_pHT;
-    float m_Flatness;
-    float m_Smoothness;
   };
 
   SharedCopyOnWrite<StateData> m_Ref;
diff --git a/core/fpdfapi/page/cpdf_iccprofile.cpp b/core/fpdfapi/page/cpdf_iccprofile.cpp
index 144f530..f8d40e4 100644
--- a/core/fpdfapi/page/cpdf_iccprofile.cpp
+++ b/core/fpdfapi/page/cpdf_iccprofile.cpp
@@ -6,32 +6,29 @@
 
 #include "core/fpdfapi/page/cpdf_iccprofile.h"
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxcodec/codec/ccodec_iccmodule.h"
+#include "core/fxcodec/icc/iccmodule.h"
 
 namespace {
 
-bool DetectSRGB(const uint8_t* pData, uint32_t dwSize) {
-  return dwSize == 3144 && memcmp(pData + 0x190, "sRGB IEC61966-2.1", 17) == 0;
+bool DetectSRGB(pdfium::span<const uint8_t> span) {
+  static const char kSRGB[] = "sRGB IEC61966-2.1";
+  return span.size() == 3144 && memcmp(&span[400], kSRGB, strlen(kSRGB)) == 0;
 }
 
 }  // namespace
 
-CPDF_IccProfile::CPDF_IccProfile(CPDF_Stream* pStream,
-                                 const uint8_t* pData,
-                                 uint32_t dwSize)
-    : m_bsRGB(DetectSRGB(pData, dwSize)), m_pStream(pStream) {
+CPDF_IccProfile::CPDF_IccProfile(const CPDF_Stream* pStream,
+                                 pdfium::span<const uint8_t> span)
+    : m_bsRGB(DetectSRGB(span)), m_pStream(pStream) {
   if (m_bsRGB) {
     m_nSrcComponents = 3;
     return;
   }
 
-  uint32_t nSrcComps = 0;
-  auto* pIccModule = CPDF_ModuleMgr::Get()->GetIccModule();
-  m_Transform = pIccModule->CreateTransform_sRGB(pData, dwSize, &nSrcComps);
+  m_Transform = IccModule::CreateTransformSRGB(span);
   if (m_Transform)
-    m_nSrcComponents = nSrcComps;
+    m_nSrcComponents = m_Transform->components();
 }
 
-CPDF_IccProfile::~CPDF_IccProfile() {}
+CPDF_IccProfile::~CPDF_IccProfile() = default;
diff --git a/core/fpdfapi/page/cpdf_iccprofile.h b/core/fpdfapi/page/cpdf_iccprofile.h
index 1bc498a..070f884 100644
--- a/core/fpdfapi/page/cpdf_iccprofile.h
+++ b/core/fpdfapi/page/cpdf_iccprofile.h
@@ -9,32 +9,36 @@
 
 #include <memory>
 
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/span.h"
 
-class CLcmsCmm;
 class CPDF_Stream;
 
-class CPDF_IccProfile : public Retainable {
+namespace fxcodec {
+class CLcmsCmm;
+}  // namespace fxcodec
+
+class CPDF_IccProfile final : public Retainable, public Observable {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  CPDF_Stream* GetStream() const { return m_pStream.Get(); }
+  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; }
-  CLcmsCmm* transform() { return m_Transform.get(); }
+  fxcodec::CLcmsCmm* transform() { return m_Transform.get(); }
   uint32_t GetComponents() const { return m_nSrcComponents; }
 
  private:
-  CPDF_IccProfile(CPDF_Stream* pStream, const uint8_t* pData, uint32_t dwSize);
+  CPDF_IccProfile(const CPDF_Stream* pStream, pdfium::span<const uint8_t> span);
   ~CPDF_IccProfile() override;
 
   const bool m_bsRGB;
-  UnownedPtr<CPDF_Stream> const m_pStream;
-  std::unique_ptr<CLcmsCmm> m_Transform;
   uint32_t m_nSrcComponents = 0;
+  RetainPtr<const CPDF_Stream> const m_pStream;
+  std::unique_ptr<fxcodec::CLcmsCmm> 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 65ca78e..ce46538 100644
--- a/core/fpdfapi/page/cpdf_image.cpp
+++ b/core/fpdfapi/page/cpdf_image.cpp
@@ -11,7 +11,8 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "constants/stream_dict_common.h"
+#include "core/fpdfapi/page/cpdf_dib.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
@@ -22,84 +23,85 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_dibsource.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
-#include "core/fxcodec/codec/ccodec_jpegmodule.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/jpeg/jpegmodule.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_dib.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "third_party/base/ptr_util.h"
 
-CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {}
+// static
+bool CPDF_Image::IsValidJpegComponent(int32_t comps) {
+  return comps == 1 || comps == 3 || comps == 4;
+}
 
-CPDF_Image::CPDF_Image(CPDF_Document* pDoc,
-                       std::unique_ptr<CPDF_Stream> pStream)
-    : m_bIsInline(true),
-      m_pDocument(pDoc),
-      m_pStream(std::move(pStream)),
-      m_pDict(ToDictionary(m_pStream->GetDict()->Clone())) {
-  ASSERT(m_pStream.IsOwned());
-  ASSERT(m_pDict.IsOwned());
-  FinishInitialization();
+// static
+bool CPDF_Image::IsValidJpegBitsPerComponent(int32_t bpc) {
+  return bpc == 1 || bpc == 2 || bpc == 4 || bpc == 8 || bpc == 16;
+}
+
+CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {
+  ASSERT(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());
 }
 
 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum)
     : m_pDocument(pDoc),
-      m_pStream(ToStream(pDoc->GetIndirectObject(dwStreamObjNum))),
-      m_pDict(m_pStream->GetDict()) {
-  ASSERT(!m_pStream.IsOwned());
-  ASSERT(!m_pDict.IsOwned());
-  FinishInitialization();
+      m_pStream(ToStream(pDoc->GetIndirectObject(dwStreamObjNum))) {
+  ASSERT(m_pDocument);
+  FinishInitialization(m_pStream->GetDict());
 }
 
-CPDF_Image::~CPDF_Image() {}
+CPDF_Image::~CPDF_Image() = default;
 
-void CPDF_Image::FinishInitialization() {
-  m_pOC = m_pDict->GetDictFor("OC");
-  m_bIsMask =
-      !m_pDict->KeyExist("ColorSpace") || m_pDict->GetIntegerFor("ImageMask");
-  m_bInterpolate = !!m_pDict->GetIntegerFor("Interpolate");
-  m_Height = m_pDict->GetIntegerFor("Height");
-  m_Width = m_pDict->GetIntegerFor("Width");
+void CPDF_Image::FinishInitialization(CPDF_Dictionary* pStreamDict) {
+  m_pOC.Reset(pStreamDict->GetDictFor("OC"));
+  m_bIsMask = !pStreamDict->KeyExist("ColorSpace") ||
+              pStreamDict->GetIntegerFor("ImageMask");
+  m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate");
+  m_Height = pStreamDict->GetIntegerFor("Height");
+  m_Width = pStreamDict->GetIntegerFor("Width");
 }
 
 void CPDF_Image::ConvertStreamToIndirectObject() {
   if (!m_pStream->IsInline())
     return;
 
-  ASSERT(m_pStream.IsOwned());
-  m_pDocument->AddIndirectObject(m_pStream.Release());
+  m_pDocument->AddIndirectObject(m_pStream);
 }
 
 CPDF_Dictionary* CPDF_Image::GetDict() const {
   return m_pStream ? m_pStream->GetDict() : nullptr;
 }
 
-std::unique_ptr<CPDF_Dictionary> CPDF_Image::InitJPEG(uint8_t* pData,
-                                                      uint32_t size) {
-  int32_t width;
-  int32_t height;
-  int32_t num_comps;
-  int32_t bits;
-  bool color_trans;
-  if (!CPDF_ModuleMgr::Get()->GetJpegModule()->LoadInfo(
-          pData, size, &width, &height, &num_comps, &bits, &color_trans)) {
+RetainPtr<CPDF_Dictionary> CPDF_Image::InitJPEG(
+    pdfium::span<uint8_t> src_span) {
+  Optional<JpegModule::JpegImageInfo> info_opt =
+      fxcodec::ModuleMgr::GetInstance()->GetJpegModule()->LoadInfo(src_span);
+  if (!info_opt.has_value())
+    return nullptr;
+
+  const JpegModule::JpegImageInfo& info = info_opt.value();
+  if (!IsValidJpegComponent(info.num_components) ||
+      !IsValidJpegBitsPerComponent(info.bits_per_components)) {
     return nullptr;
   }
 
-  auto pDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
-  pDict->SetNewFor<CPDF_Name>("Type", "XObject");
-  pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
-  pDict->SetNewFor<CPDF_Number>("Width", width);
-  pDict->SetNewFor<CPDF_Number>("Height", height);
+  RetainPtr<CPDF_Dictionary> pDict =
+      CreateXObjectImageDict(info.width, info.height);
   const char* csname = nullptr;
-  if (num_comps == 1) {
+  if (info.num_components == 1) {
     csname = "DeviceGray";
-  } else if (num_comps == 3) {
+  } else if (info.num_components == 3) {
     csname = "DeviceRGB";
-  } else if (num_comps == 4) {
+  } else if (info.num_components == 4) {
     csname = "DeviceCMYK";
     CPDF_Array* pDecode = pDict->SetNewFor<CPDF_Array>("Decode");
     for (int n = 0; n < 4; n++) {
@@ -108,17 +110,18 @@
     }
   }
   pDict->SetNewFor<CPDF_Name>("ColorSpace", csname);
-  pDict->SetNewFor<CPDF_Number>("BitsPerComponent", bits);
+  pDict->SetNewFor<CPDF_Number>("BitsPerComponent", info.bits_per_components);
   pDict->SetNewFor<CPDF_Name>("Filter", "DCTDecode");
-  if (!color_trans) {
-    CPDF_Dictionary* pParms = pDict->SetNewFor<CPDF_Dictionary>("DecodeParms");
+  if (!info.color_transform) {
+    CPDF_Dictionary* pParms =
+        pDict->SetNewFor<CPDF_Dictionary>(pdfium::stream::kDecodeParms);
     pParms->SetNewFor<CPDF_Number>("ColorTransform", 0);
   }
   m_bIsMask = false;
-  m_Width = width;
-  m_Height = height;
+  m_Width = info.width;
+  m_Height = info.height;
   if (!m_pStream)
-    m_pStream = pdfium::MakeUnique<CPDF_Stream>();
+    m_pStream = pdfium::MakeRetain<CPDF_Stream>();
   return pDict;
 }
 
@@ -129,15 +132,14 @@
 
   uint32_t dwEstimateSize = std::min(size, 8192U);
   std::vector<uint8_t> data(dwEstimateSize);
-  if (!pFile->ReadBlock(data.data(), 0, dwEstimateSize))
+  if (!pFile->ReadBlockAtOffset(data.data(), 0, dwEstimateSize))
     return;
 
-  std::unique_ptr<CPDF_Dictionary> pDict =
-      InitJPEG(data.data(), dwEstimateSize);
+  RetainPtr<CPDF_Dictionary> pDict = InitJPEG(data);
   if (!pDict && size > dwEstimateSize) {
     data.resize(size);
-    pFile->ReadBlock(data.data(), 0, size);
-    pDict = InitJPEG(data.data(), size);
+    if (pFile->ReadBlockAtOffset(data.data(), 0, size))
+      pDict = InitJPEG(data);
   }
   if (!pDict)
     return;
@@ -152,14 +154,14 @@
     return;
 
   std::vector<uint8_t> data(size);
-  if (!pFile->ReadBlock(data.data(), 0, size))
+  if (!pFile->ReadBlockAtOffset(data.data(), 0, size))
     return;
 
-  std::unique_ptr<CPDF_Dictionary> pDict = InitJPEG(data.data(), size);
+  RetainPtr<CPDF_Dictionary> pDict = InitJPEG(data);
   if (!pDict)
     return;
 
-  m_pStream->InitStream(&(data[0]), size, std::move(pDict));
+  m_pStream->InitStream(data, std::move(pDict));
 }
 
 void CPDF_Image::SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap) {
@@ -168,13 +170,8 @@
   if (BitmapWidth < 1 || BitmapHeight < 1)
     return;
 
-  auto pDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
-  pDict->SetNewFor<CPDF_Name>("Type", "XObject");
-  pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
-  pDict->SetNewFor<CPDF_Number>("Width", BitmapWidth);
-  pDict->SetNewFor<CPDF_Number>("Height", BitmapHeight);
-
+  RetainPtr<CPDF_Dictionary> pDict =
+      CreateXObjectImageDict(BitmapWidth, BitmapHeight);
   const int32_t bpp = pBitmap->GetBPP();
   size_t dest_pitch = 0;
   bool bCopyWithoutAlpha = true;
@@ -206,39 +203,42 @@
       pCS->AddNew<CPDF_Name>("DeviceRGB");
       pCS->AddNew<CPDF_Number>(1);
       ByteString ct;
-      char* pBuf = ct.GetBuffer(6);
-      pBuf[0] = (char)reset_r;
-      pBuf[1] = (char)reset_g;
-      pBuf[2] = (char)reset_b;
-      pBuf[3] = (char)set_r;
-      pBuf[4] = (char)set_g;
-      pBuf[5] = (char)set_b;
+      {
+        // Span's lifetime must end before ReleaseBuffer() below.
+        pdfium::span<char> pBuf = ct.GetBuffer(6);
+        pBuf[0] = static_cast<char>(reset_r);
+        pBuf[1] = static_cast<char>(reset_g);
+        pBuf[2] = static_cast<char>(reset_b);
+        pBuf[3] = static_cast<char>(set_r);
+        pBuf[4] = static_cast<char>(set_g);
+        pBuf[5] = static_cast<char>(set_b);
+      }
       ct.ReleaseBuffer(6);
       pCS->AddNew<CPDF_String>(ct, true);
     }
     pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 1);
     dest_pitch = (BitmapWidth + 7) / 8;
   } else if (bpp == 8) {
-    int32_t iPalette = pBitmap->GetPaletteSize();
-    if (iPalette > 0) {
+    size_t palette_size = pBitmap->GetPaletteSize();
+    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>(iPalette - 1);
+      pCS->AddNew<CPDF_Number>(static_cast<int>(palette_size - 1));
       std::unique_ptr<uint8_t, FxFreeDeleter> pColorTable(
-          FX_Alloc2D(uint8_t, iPalette, 3));
+          FX_Alloc2D(uint8_t, palette_size, 3));
       uint8_t* ptr = pColorTable.get();
-      for (int32_t i = 0; i < iPalette; i++) {
+      for (size_t i = 0; i < palette_size; i++) {
         uint32_t argb = pBitmap->GetPaletteArgb(i);
-        ptr[0] = (uint8_t)(argb >> 16);
-        ptr[1] = (uint8_t)(argb >> 8);
-        ptr[2] = (uint8_t)argb;
+        ptr[0] = FXARGB_R(argb);
+        ptr[1] = FXARGB_G(argb);
+        ptr[2] = FXARGB_B(argb);
         ptr += 3;
       }
-      auto pNewDict =
-          pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
+      auto pNewDict = m_pDocument->New<CPDF_Dictionary>();
       CPDF_Stream* pCTS = m_pDocument->NewIndirect<CPDF_Stream>(
-          std::move(pColorTable), iPalette * 3, std::move(pNewDict));
+          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(),
                                        pCS->GetObjNum());
@@ -263,12 +263,8 @@
     int32_t maskHeight = pMaskBitmap->GetHeight();
     std::unique_ptr<uint8_t, FxFreeDeleter> mask_buf;
     int32_t mask_size = 0;
-    auto pMaskDict =
-        pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
-    pMaskDict->SetNewFor<CPDF_Name>("Type", "XObject");
-    pMaskDict->SetNewFor<CPDF_Name>("Subtype", "Image");
-    pMaskDict->SetNewFor<CPDF_Number>("Width", maskWidth);
-    pMaskDict->SetNewFor<CPDF_Number>("Height", maskHeight);
+    RetainPtr<CPDF_Dictionary> pMaskDict =
+        CreateXObjectImageDict(maskWidth, maskHeight);
     pMaskDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
     pMaskDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
     if (pMaskBitmap->GetFormat() != FXDIB_1bppMask) {
@@ -288,81 +284,89 @@
 
   uint8_t* src_buf = pBitmap->GetBuffer();
   int32_t src_pitch = pBitmap->GetPitch();
-  uint8_t* dest_buf = FX_Alloc2D(uint8_t, dest_pitch, BitmapHeight);
+  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;
-  uint8_t* pDest = dest_buf;
+  auto dest_span = pdfium::make_span(dest_buf.get(), dest_size);
+  size_t dest_span_offset = 0;
   if (bCopyWithoutAlpha) {
     for (int32_t i = 0; i < BitmapHeight; i++) {
-      memcpy(pDest, src_buf, dest_pitch);
-      pDest += dest_pitch;
+      memcpy(&dest_span[dest_span_offset], src_buf, dest_pitch);
+      dest_span_offset += dest_pitch;
       src_buf += src_pitch;
     }
   } else {
     int32_t src_offset = 0;
-    int32_t dest_offset = 0;
     for (int32_t row = 0; row < BitmapHeight; row++) {
+      size_t dest_span_row_offset = dest_span_offset;
       src_offset = row * src_pitch;
       for (int32_t column = 0; column < BitmapWidth; column++) {
         float alpha = 1;
-        pDest[dest_offset] = (uint8_t)(src_buf[src_offset + 2] * alpha);
-        pDest[dest_offset + 1] = (uint8_t)(src_buf[src_offset + 1] * alpha);
-        pDest[dest_offset + 2] = (uint8_t)(src_buf[src_offset] * alpha);
-        dest_offset += 3;
+        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;
       }
 
-      pDest += dest_pitch;
-      dest_offset = 0;
+      dest_span_offset += dest_pitch;
     }
   }
   if (!m_pStream)
-    m_pStream = pdfium::MakeUnique<CPDF_Stream>();
+    m_pStream = pdfium::MakeRetain<CPDF_Stream>();
 
-  m_pStream->InitStream(dest_buf, dest_size, std::move(pDict));
+  m_pStream->InitStream(dest_span, std::move(pDict));
   m_bIsMask = pBitmap->IsAlphaMask();
   m_Width = BitmapWidth;
   m_Height = BitmapHeight;
-  FX_Free(dest_buf);
 }
 
-void CPDF_Image::ResetCache(CPDF_Page* pPage,
-                            const RetainPtr<CFX_DIBitmap>& pBitmap) {
+void CPDF_Image::ResetCache(CPDF_Page* pPage) {
   RetainPtr<CPDF_Image> pHolder(this);
-  pPage->GetRenderCache()->ResetBitmap(pHolder, pBitmap);
+  pPage->GetRenderCache()->ResetBitmapForImage(pHolder);
 }
 
-RetainPtr<CFX_DIBSource> CPDF_Image::LoadDIBSource() const {
-  auto source = pdfium::MakeRetain<CPDF_DIBSource>();
+RetainPtr<CFX_DIBBase> CPDF_Image::LoadDIBBase() const {
+  auto source = pdfium::MakeRetain<CPDF_DIB>();
   if (!source->Load(m_pDocument.Get(), m_pStream.Get()))
     return nullptr;
 
-  return source;
+  if (!source->IsJBigImage())
+    return source;
+
+  CPDF_DIB::LoadState ret = CPDF_DIB::LoadState::kContinue;
+  while (ret == CPDF_DIB::LoadState::kContinue)
+    ret = source->ContinueLoadDIBBase(nullptr);
+  return ret == CPDF_DIB::LoadState::kSuccess ? source : nullptr;
 }
 
-RetainPtr<CFX_DIBSource> CPDF_Image::DetachBitmap() {
-  return std::move(m_pDIBSource);
+RetainPtr<CFX_DIBBase> CPDF_Image::DetachBitmap() {
+  return std::move(m_pDIBBase);
 }
 
-RetainPtr<CFX_DIBSource> CPDF_Image::DetachMask() {
+RetainPtr<CFX_DIBBase> CPDF_Image::DetachMask() {
   return std::move(m_pMask);
 }
 
-bool CPDF_Image::StartLoadDIBSource(CPDF_Dictionary* pFormResource,
-                                    CPDF_Dictionary* pPageResource,
-                                    bool bStdCS,
-                                    uint32_t GroupFamily,
-                                    bool bLoadMask) {
-  auto source = pdfium::MakeRetain<CPDF_DIBSource>();
-  int ret = source->StartLoadDIBSource(m_pDocument.Get(), m_pStream.Get(), true,
-                                       pFormResource, pPageResource, bStdCS,
-                                       GroupFamily, bLoadMask);
-  if (!ret) {
-    m_pDIBSource.Reset();
+bool CPDF_Image::StartLoadDIBBase(const CPDF_Dictionary* pFormResource,
+                                  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);
+  if (ret == CPDF_DIB::LoadState::kFail) {
+    m_pDIBBase.Reset();
     return false;
   }
-  m_pDIBSource = source;
-  if (ret == 2)
+  m_pDIBBase = source;
+  if (ret == CPDF_DIB::LoadState::kContinue)
     return true;
 
   m_pMask = source->DetachMask();
@@ -370,17 +374,27 @@
   return false;
 }
 
-bool CPDF_Image::Continue(IFX_PauseIndicator* pPause) {
-  RetainPtr<CPDF_DIBSource> pSource = m_pDIBSource.As<CPDF_DIBSource>();
-  int ret = pSource->ContinueLoadDIBSource(pPause);
-  if (!ret) {
-    m_pDIBSource.Reset();
-    return false;
-  }
-  if (ret == 2)
+bool CPDF_Image::Continue(PauseIndicatorIface* pPause) {
+  RetainPtr<CPDF_DIB> pSource = m_pDIBBase.As<CPDF_DIB>();
+  CPDF_DIB::LoadState ret = pSource->ContinueLoadDIBBase(pPause);
+  if (ret == CPDF_DIB::LoadState::kContinue)
     return true;
 
-  m_pMask = pSource->DetachMask();
-  m_MatteColor = pSource->GetMatteColor();
+  if (ret == CPDF_DIB::LoadState::kSuccess) {
+    m_pMask = pSource->DetachMask();
+    m_MatteColor = pSource->GetMatteColor();
+  } else {
+    m_pDIBBase.Reset();
+  }
   return false;
 }
+
+RetainPtr<CPDF_Dictionary> CPDF_Image::CreateXObjectImageDict(int width,
+                                                              int height) {
+  auto dict = m_pDocument->New<CPDF_Dictionary>();
+  dict->SetNewFor<CPDF_Name>("Type", "XObject");
+  dict->SetNewFor<CPDF_Name>("Subtype", "Image");
+  dict->SetNewFor<CPDF_Number>("Width", width);
+  dict->SetNewFor<CPDF_Number>("Height", height);
+  return dict;
+}
diff --git a/core/fpdfapi/page/cpdf_image.h b/core/fpdfapi/page/cpdf_image.h
index 23864bb..4194d49 100644
--- a/core/fpdfapi/page/cpdf_image.h
+++ b/core/fpdfapi/page/cpdf_image.h
@@ -9,30 +9,33 @@
 
 #include <memory>
 
-#include "core/fpdfapi/parser/cpdf_stream.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/base/span.h"
 
-class CFX_DIBSource;
+class CFX_DIBBase;
 class CFX_DIBitmap;
+class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_Page;
-class IFX_PauseIndicator;
+class CPDF_Stream;
+class PauseIndicatorIface;
 class IFX_SeekableReadStream;
 
-class CPDF_Image : public Retainable {
+class CPDF_Image final : public Retainable {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
+  static bool IsValidJpegComponent(int32_t comps);
+  static bool IsValidJpegBitsPerComponent(int32_t bpc);
+
   void ConvertStreamToIndirectObject();
 
-  CPDF_Dictionary* GetInlineDict() const { return m_pDict.Get(); }
-  CPDF_Stream* GetStream() const { return m_pStream.Get(); }
   CPDF_Dictionary* GetDict() const;
-  CPDF_Dictionary* GetOC() const { return m_pOC.Get(); }
+  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(); }
 
   int32_t GetPixelHeight() const { return m_Height; }
@@ -42,34 +45,41 @@
   bool IsMask() const { return m_bIsMask; }
   bool IsInterpol() const { return m_bInterpolate; }
 
-  RetainPtr<CFX_DIBSource> LoadDIBSource() const;
+  RetainPtr<CFX_DIBBase> LoadDIBBase() const;
 
-  void SetImage(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+  void SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap);
   void SetJpegImage(const RetainPtr<IFX_SeekableReadStream>& pFile);
   void SetJpegImageInline(const RetainPtr<IFX_SeekableReadStream>& pFile);
 
-  void ResetCache(CPDF_Page* pPage, const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  bool StartLoadDIBSource(CPDF_Dictionary* pFormResource,
-                          CPDF_Dictionary* pPageResource,
-                          bool bStdCS = false,
-                          uint32_t GroupFamily = 0,
-                          bool bLoadMask = false);
-  bool Continue(IFX_PauseIndicator* pPause);
-  RetainPtr<CFX_DIBSource> DetachBitmap();
-  RetainPtr<CFX_DIBSource> DetachMask();
+  void ResetCache(CPDF_Page* pPage);
 
-  RetainPtr<CFX_DIBSource> m_pDIBSource;
-  RetainPtr<CFX_DIBSource> m_pMask;
+  // Returns whether to Continue() or not.
+  bool StartLoadDIBBase(const CPDF_Dictionary* pFormResource,
+                        CPDF_Dictionary* pPageResource,
+                        bool bStdCS,
+                        uint32_t GroupFamily,
+                        bool bLoadMask);
+
+  // Returns whether to Continue() or not.
+  bool Continue(PauseIndicatorIface* pPause);
+
+  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, std::unique_ptr<CPDF_Stream> pStream);
+  CPDF_Image(CPDF_Document* pDoc, RetainPtr<CPDF_Stream> pStream);
   CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum);
   ~CPDF_Image() override;
 
-  void FinishInitialization();
-  std::unique_ptr<CPDF_Dictionary> InitJPEG(uint8_t* pData, uint32_t size);
+  void FinishInitialization(CPDF_Dictionary* pStreamDict);
+  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;
@@ -77,9 +87,8 @@
   bool m_bIsMask = false;
   bool m_bInterpolate = false;
   UnownedPtr<CPDF_Document> const m_pDocument;
-  MaybeOwned<CPDF_Stream> m_pStream;
-  MaybeOwned<CPDF_Dictionary> m_pDict;
-  UnownedPtr<CPDF_Dictionary> m_pOC;
+  RetainPtr<CPDF_Stream> m_pStream;
+  RetainPtr<const CPDF_Dictionary> m_pOC;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_IMAGE_H_
diff --git a/core/fpdfapi/page/cpdf_imageobject.cpp b/core/fpdfapi/page/cpdf_imageobject.cpp
index 3b5a740..0f6a26b 100644
--- a/core/fpdfapi/page/cpdf_imageobject.cpp
+++ b/core/fpdfapi/page/cpdf_imageobject.cpp
@@ -11,8 +11,14 @@
 #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/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 
-CPDF_ImageObject::CPDF_ImageObject() {}
+CPDF_ImageObject::CPDF_ImageObject(int32_t content_stream)
+    : CPDF_PageObject(content_stream) {}
+
+CPDF_ImageObject::CPDF_ImageObject() : CPDF_ImageObject(kNoContentStream) {}
 
 CPDF_ImageObject::~CPDF_ImageObject() {
   MaybePurgeCache();
@@ -41,8 +47,8 @@
 }
 
 void CPDF_ImageObject::CalcBoundingBox() {
-  std::tie(m_Left, m_Right, m_Top, m_Bottom) =
-      m_Matrix.TransformRect(0.f, 1.f, 1.f, 0.f);
+  static constexpr CFX_FloatRect kRect(0.0f, 0.0f, 1.0f, 1.0f);
+  SetRect(m_Matrix.TransformRect(kRect));
 }
 
 void CPDF_ImageObject::SetImage(const RetainPtr<CPDF_Image>& pImage) {
@@ -50,15 +56,26 @@
   m_pImage = pImage;
 }
 
+RetainPtr<CPDF_Image> CPDF_ImageObject::GetImage() const {
+  return m_pImage;
+}
+
+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;
+}
+
 void CPDF_ImageObject::MaybePurgeCache() {
   if (!m_pImage)
     return;
 
-  CPDF_Document* pDocument = m_pImage->GetDocument();
-  if (!pDocument)
-    return;
-
-  CPDF_DocPageData* pPageData = pDocument->GetPageData();
+  auto* pPageData = CPDF_DocPageData::FromDocument(m_pImage->GetDocument());
   if (!pPageData)
     return;
 
diff --git a/core/fpdfapi/page/cpdf_imageobject.h b/core/fpdfapi/page/cpdf_imageobject.h
index 16a506e..f221a98 100644
--- a/core/fpdfapi/page/cpdf_imageobject.h
+++ b/core/fpdfapi/page/cpdf_imageobject.h
@@ -7,15 +7,16 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_IMAGEOBJECT_H_
 #define CORE_FPDFAPI_PAGE_CPDF_IMAGEOBJECT_H_
 
-#include <memory>
-
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
 
+class CFX_DIBitmap;
 class CPDF_Image;
 
-class CPDF_ImageObject : public CPDF_PageObject {
+class CPDF_ImageObject final : public CPDF_PageObject {
  public:
+  explicit CPDF_ImageObject(int32_t content_stream);
   CPDF_ImageObject();
   ~CPDF_ImageObject() override;
 
@@ -27,8 +28,10 @@
   const CPDF_ImageObject* AsImage() const override;
 
   void CalcBoundingBox();
-  RetainPtr<CPDF_Image> GetImage() const { return m_pImage; }
   void SetImage(const RetainPtr<CPDF_Image>& pImage);
+  RetainPtr<CPDF_Image> GetImage() const;
+  RetainPtr<CFX_DIBitmap> GetIndependentBitmap() const;
+
   void set_matrix(const CFX_Matrix& matrix) { m_Matrix = matrix; }
   const CFX_Matrix& matrix() const { return m_Matrix; }
 
diff --git a/core/fpdfapi/page/cpdf_meshstream.cpp b/core/fpdfapi/page/cpdf_meshstream.cpp
index 083acce..06b2329 100644
--- a/core/fpdfapi/page/cpdf_meshstream.cpp
+++ b/core/fpdfapi/page/cpdf_meshstream.cpp
@@ -9,6 +9,11 @@
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_function.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 
 namespace {
 
@@ -92,8 +97,8 @@
 CPDF_MeshStream::CPDF_MeshStream(
     ShadingType type,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    CPDF_Stream* pShadingStream,
-    CPDF_ColorSpace* pCS)
+    const CPDF_Stream* pShadingStream,
+    const RetainPtr<CPDF_ColorSpace>& pCS)
     : m_type(type),
       m_funcs(funcs),
       m_pShadingStream(pShadingStream),
@@ -117,9 +122,8 @@
 
 bool CPDF_MeshStream::Load() {
   m_pStream->LoadAllDataFiltered();
-  m_BitStream = pdfium::MakeUnique<CFX_BitStream>(m_pStream->GetData(),
-                                                  m_pStream->GetSize());
-  CPDF_Dictionary* pDict = m_pShadingStream->GetDict();
+  m_BitStream = pdfium::MakeUnique<CFX_BitStream>(m_pStream->GetSpan());
+  const CPDF_Dictionary* pDict = m_pShadingStream->GetDict();
   m_nCoordBits = pDict->GetIntegerFor("BitsPerCoordinate");
   m_nComponentBits = pDict->GetIntegerFor("BitsPerComponent");
   if (ShouldCheckBPC(m_type)) {
@@ -138,8 +142,8 @@
     return false;
 
   m_nComponents = m_funcs.empty() ? nComponents : 1;
-  CPDF_Array* pDecode = pDict->GetArrayFor("Decode");
-  if (!pDecode || pDecode->GetCount() != 4 + m_nComponents * 2)
+  const CPDF_Array* pDecode = pDict->GetArrayFor("Decode");
+  if (!pDecode || pDecode->size() != 4 + m_nComponents * 2)
     return false;
 
   m_xmin = pDecode->GetNumberAt(0);
diff --git a/core/fpdfapi/page/cpdf_meshstream.h b/core/fpdfapi/page/cpdf_meshstream.h
index 7c34b25..939b192 100644
--- a/core/fpdfapi/page/cpdf_meshstream.h
+++ b/core/fpdfapi/page/cpdf_meshstream.h
@@ -12,9 +12,11 @@
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/cfx_bitstream.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_StreamAcc;
 
 class CPDF_MeshVertex {
  public:
@@ -37,8 +39,8 @@
  public:
   CPDF_MeshStream(ShadingType type,
                   const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                  CPDF_Stream* pShadingStream,
-                  CPDF_ColorSpace* pCS);
+                  const CPDF_Stream* pShadingStream,
+                  const RetainPtr<CPDF_ColorSpace>& pCS);
   ~CPDF_MeshStream();
 
   bool Load();
@@ -66,8 +68,8 @@
 
   const ShadingType m_type;
   const std::vector<std::unique_ptr<CPDF_Function>>& m_funcs;
-  UnownedPtr<CPDF_Stream> const m_pShadingStream;
-  UnownedPtr<CPDF_ColorSpace> const m_pCS;
+  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;
@@ -78,10 +80,10 @@
   float m_xmax;
   float m_ymin;
   float m_ymax;
-  float m_ColorMin[kMaxComponents];
-  float m_ColorMax[kMaxComponents];
   RetainPtr<CPDF_StreamAcc> m_pStream;
   std::unique_ptr<CFX_BitStream> m_BitStream;
+  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
new file mode 100644
index 0000000..96f8a88
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_occontext.cpp
@@ -0,0 +1,287 @@
+// 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/page/cpdf_occontext.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_document.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");
+  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);
+      if (bsIntent == "All" || bsIntent == csElement)
+        return true;
+    }
+    return false;
+  }
+  bsIntent = pIntent->GetString();
+  return bsIntent == "All" || bsIntent == csElement;
+}
+
+CPDF_Dictionary* GetConfig(CPDF_Document* pDoc,
+                           const CPDF_Dictionary* pOCGDict) {
+  ASSERT(pOCGDict);
+  CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDictFor("OCProperties");
+  if (!pOCProperties)
+    return nullptr;
+
+  CPDF_Array* pOCGs = pOCProperties->GetArrayFor("OCGs");
+  if (!pOCGs)
+    return nullptr;
+
+  if (FindGroup(pOCGs, pOCGDict) < 0)
+    return nullptr;
+
+  CPDF_Dictionary* pConfig = pOCProperties->GetDictFor("D");
+  CPDF_Array* pConfigs = pOCProperties->GetArrayFor("Configs");
+  if (!pConfigs)
+    return pConfig;
+
+  for (size_t i = 0; i < pConfigs->size(); i++) {
+    CPDF_Dictionary* pFind = pConfigs->GetDictAt(i);
+    if (pFind && HasIntent(pFind, "View", ""))
+      return pFind;
+  }
+  return pConfig;
+}
+
+ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) {
+  ByteString csState;
+  switch (eType) {
+    case CPDF_OCContext::Design:
+      csState = "Design";
+      break;
+    case CPDF_OCContext::Print:
+      csState = "Print";
+      break;
+    case CPDF_OCContext::Export:
+      csState = "Export";
+      break;
+    default:
+      csState = "View";
+      break;
+  }
+  return csState;
+}
+
+}  // namespace
+
+CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType)
+    : m_pDocument(pDoc), m_eUsageType(eUsageType) {
+  ASSERT(pDoc);
+}
+
+CPDF_OCContext::~CPDF_OCContext() {}
+
+bool CPDF_OCContext::LoadOCGStateFromConfig(
+    const ByteString& csConfig,
+    const CPDF_Dictionary* pOCGDict) const {
+  CPDF_Dictionary* pConfig = GetConfig(m_pDocument.Get(), 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;
+  }
+  pArray = pConfig->GetArrayFor("OFF");
+  if (pArray) {
+    if (FindGroup(pArray, pOCGDict) >= 0)
+      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);
+    if (!pUsage)
+      continue;
+
+    if (pUsage->GetStringFor("Event", "View") != csConfig)
+      continue;
+
+    CPDF_Array* pOCGs = pUsage->GetArrayFor("OCGs");
+    if (!pOCGs)
+      continue;
+
+    if (FindGroup(pOCGs, pOCGDict) < 0)
+      continue;
+
+    CPDF_Dictionary* pState = pUsage->GetDictFor(csConfig);
+    if (!pState)
+      continue;
+
+    bState = pState->GetStringFor(csFind) != "OFF";
+  }
+  return bState;
+}
+
+bool CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
+  if (!HasIntent(pOCGDict, "View", "View"))
+    return true;
+
+  ByteString csState = GetUsageTypeString(m_eUsageType);
+  const CPDF_Dictionary* pUsage = pOCGDict->GetDictFor("Usage");
+  if (pUsage) {
+    const CPDF_Dictionary* pState = pUsage->GetDictFor(csState);
+    if (pState) {
+      ByteString csFind = csState + "State";
+      if (pState->KeyExist(csFind))
+        return pState->GetStringFor(csFind) != "OFF";
+    }
+    if (csState != "View") {
+      pState = pUsage->GetDictFor("View");
+      if (pState && pState->KeyExist("ViewState"))
+        return pState->GetStringFor("ViewState") != "OFF";
+    }
+  }
+  return LoadOCGStateFromConfig(csState, pOCGDict);
+}
+
+bool CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) const {
+  if (!pOCGDict)
+    return false;
+
+  const auto it = m_OGCStateCache.find(pOCGDict);
+  if (it != m_OGCStateCache.end())
+    return it->second;
+
+  bool bState = LoadOCGState(pOCGDict);
+  m_OGCStateCache[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);
+    if (item->GetName() == "OC" &&
+        item->GetParamType() == CPDF_ContentMarkItem::kPropertiesDict &&
+        !CheckOCGVisible(item->GetParam())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool CPDF_OCContext::GetOCGVE(const CPDF_Array* pExpression, int nLevel) const {
+  if (nLevel > 32 || !pExpression)
+    return false;
+
+  ByteString csOperator = pExpression->GetStringAt(0);
+  if (csOperator == "Not") {
+    const CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
+    if (!pOCGObj)
+      return false;
+    if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
+      return !GetOCGVisible(pDict);
+    if (const CPDF_Array* pArray = pOCGObj->AsArray())
+      return !GetOCGVE(pArray, nLevel + 1);
+    return false;
+  }
+
+  if (csOperator != "Or" && csOperator != "And")
+    return false;
+
+  bool bValue = false;
+  for (size_t i = 1; i < pExpression->size(); i++) {
+    const CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
+    if (!pOCGObj)
+      continue;
+
+    bool bItem = false;
+    if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
+      bItem = GetOCGVisible(pDict);
+    else if (const CPDF_Array* pArray = pOCGObj->AsArray())
+      bItem = GetOCGVE(pArray, nLevel + 1);
+
+    if (i == 1) {
+      bValue = bItem;
+    } else {
+      if (csOperator == "Or") {
+        bValue = bValue || bItem;
+      } else {
+        bValue = bValue && bItem;
+      }
+    }
+  }
+  return bValue;
+}
+
+bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) const {
+  const CPDF_Array* pVE = pOCMDDict->GetArrayFor("VE");
+  if (pVE)
+    return GetOCGVE(pVE, 0);
+
+  ByteString csP = pOCMDDict->GetStringFor("P", "AnyOn");
+  const CPDF_Object* pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs");
+  if (!pOCGObj)
+    return true;
+
+  if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
+    return GetOCGVisible(pDict);
+
+  const CPDF_Array* pArray = pOCGObj->AsArray();
+  if (!pArray)
+    return true;
+
+  bool bState = (csP == "AllOn" || csP == "AllOff");
+  // At least one entry of OCGs needs to be a valid dictionary for it to be
+  // considered present. See "OCGs" in table 4.49 in the PDF 1.7 spec.
+  bool bValidEntrySeen = false;
+  for (size_t i = 0; i < pArray->size(); i++) {
+    bool bItem = true;
+    const CPDF_Dictionary* pItemDict = pArray->GetDictAt(i);
+    if (!pItemDict)
+      continue;
+
+    bValidEntrySeen = true;
+    bItem = GetOCGVisible(pItemDict);
+
+    if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
+      return true;
+    if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
+      return false;
+  }
+
+  return !bValidEntrySeen || bState;
+}
+
+bool CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const {
+  if (!pOCGDict)
+    return true;
+
+  ByteString csType = pOCGDict->GetStringFor("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
new file mode 100644
index 0000000..0a68639
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_occontext.h
@@ -0,0 +1,46 @@
+// 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_PAGE_CPDF_OCCONTEXT_H_
+#define CORE_FPDFAPI_PAGE_CPDF_OCCONTEXT_H_
+
+#include <map>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_Array;
+class CPDF_Dictionary;
+class CPDF_Document;
+class CPDF_PageObject;
+
+class CPDF_OCContext final : public Retainable {
+ public:
+  enum UsageType { View = 0, Design, Print, Export };
+
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  bool CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const;
+  bool CheckObjectVisible(const CPDF_PageObject* pObj) const;
+
+ private:
+  CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType);
+  ~CPDF_OCContext() override;
+
+  bool LoadOCGStateFromConfig(const ByteString& csConfig,
+                              const CPDF_Dictionary* pOCGDict) const;
+  bool LoadOCGState(const CPDF_Dictionary* pOCGDict) const;
+  bool GetOCGVisible(const CPDF_Dictionary* pOCGDict) const;
+  bool GetOCGVE(const CPDF_Array* pExpression, int nLevel) const;
+  bool LoadOCMDState(const CPDF_Dictionary* pOCMDDict) const;
+
+  UnownedPtr<CPDF_Document> const m_pDocument;
+  const UsageType m_eUsageType;
+  mutable std::map<const CPDF_Dictionary*, bool> 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 ee7b5d4..210f266 100644
--- a/core/fpdfapi/page/cpdf_page.cpp
+++ b/core/fpdfapi/page/cpdf_page.cpp
@@ -9,102 +9,79 @@
 #include <set>
 #include <utility>
 
-#include "core/fpdfapi/cpdf_pagerendercontext.h"
+#include "constants/page_object.h"
 #include "core/fpdfapi/page/cpdf_contentparser.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 "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
-CPDF_Page::CPDF_Page(CPDF_Document* pDocument,
-                     CPDF_Dictionary* pPageDict,
-                     bool bPageCache)
-    : CPDF_PageObjectHolder(pDocument, pPageDict),
-      m_PageWidth(100),
-      m_PageHeight(100),
-      m_pView(nullptr) {
-  if (bPageCache)
-    m_pPageRender = pdfium::MakeUnique<CPDF_PageRenderCache>(this);
-  if (!pPageDict)
-    return;
+CPDF_Page::CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict)
+    : CPDF_PageObjectHolder(pDocument, pPageDict, nullptr, nullptr),
+      m_PageSize(100, 100),
+      m_pPDFDocument(pDocument) {
+  ASSERT(pPageDict);
 
-  CPDF_Object* pPageAttr = GetPageAttr("Resources");
-  m_pResources = pPageAttr ? pPageAttr->GetDict() : nullptr;
+  // 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);
   m_pPageResources = m_pResources;
 
-  CFX_FloatRect mediabox = GetBox("MediaBox");
-  if (mediabox.IsEmpty())
-    mediabox = CFX_FloatRect(0, 0, 612, 792);
-
-  m_BBox = GetBox("CropBox");
-  if (m_BBox.IsEmpty())
-    m_BBox = mediabox;
-  else
-    m_BBox.Intersect(mediabox);
-
-  m_PageWidth = m_BBox.Width();
-  m_PageHeight = m_BBox.Height();
-
-  int rotate = GetPageRotation();
-  if (rotate % 2)
-    std::swap(m_PageWidth, m_PageHeight);
-
-  switch (rotate) {
-    case 0:
-      m_PageMatrix = CFX_Matrix(1.0f, 0, 0, 1.0f, -m_BBox.left, -m_BBox.bottom);
-      break;
-    case 1:
-      m_PageMatrix =
-          CFX_Matrix(0, -1.0f, 1.0f, 0, -m_BBox.bottom, m_BBox.right);
-      break;
-    case 2:
-      m_PageMatrix = CFX_Matrix(-1.0f, 0, 0, -1.0f, m_BBox.right, m_BBox.top);
-      break;
-    case 3:
-      m_PageMatrix = CFX_Matrix(0, 1.0f, -1.0f, 0, m_BBox.top, -m_BBox.left);
-      break;
-  }
-
-  m_iTransparency = PDFTRANS_ISOLATED;
-  LoadTransInfo();
+  UpdateDimensions();
+  m_Transparency.SetIsolated();
+  LoadTransparencyInfo();
 }
 
 CPDF_Page::~CPDF_Page() {}
 
+CPDF_Page* CPDF_Page::AsPDFPage() {
+  return this;
+}
+
+CPDFXFA_Page* CPDF_Page::AsXFAPage() {
+  return nullptr;
+}
+
+CPDF_Document* CPDF_Page::GetDocument() const {
+  return GetPDFDocument();
+}
+
+float CPDF_Page::GetPageWidth() const {
+  return m_PageSize.width;
+}
+
+float CPDF_Page::GetPageHeight() const {
+  return m_PageSize.height;
+}
+
 bool CPDF_Page::IsPage() const {
   return true;
 }
 
-void CPDF_Page::StartParse() {
-  if (m_ParseState == CONTENT_PARSED || m_ParseState == CONTENT_PARSING)
+void CPDF_Page::ParseContent() {
+  if (GetParseState() == ParseState::kParsed)
     return;
 
-  m_pParser = pdfium::MakeUnique<CPDF_ContentParser>(this);
-  m_ParseState = CONTENT_PARSING;
-}
+  if (GetParseState() == ParseState::kNotParsed)
+    StartParse(pdfium::MakeUnique<CPDF_ContentParser>(this));
 
-void CPDF_Page::ParseContent() {
-  StartParse();
+  ASSERT(GetParseState() == ParseState::kParsing);
   ContinueParse(nullptr);
 }
 
-void CPDF_Page::SetRenderContext(
-    std::unique_ptr<CPDF_PageRenderContext> pContext) {
-  m_pRenderContext = std::move(pContext);
-}
-
 CPDF_Object* CPDF_Page::GetPageAttr(const ByteString& name) const {
-  CPDF_Dictionary* pPageDict = m_pFormDict.Get();
+  CPDF_Dictionary* pPageDict = GetDict();
   std::set<CPDF_Dictionary*> visited;
   while (1) {
     visited.insert(pPageDict);
     if (CPDF_Object* pObj = pPageDict->GetDirectObjectFor(name))
       return pObj;
 
-    pPageDict = pPageDict->GetDictFor("Parent");
+    pPageDict = pPageDict->GetDictFor(pdfium::page_object::kParent);
     if (!pPageDict || pdfium::ContainsKey(visited, pPageDict))
       break;
   }
@@ -121,12 +98,24 @@
   return box;
 }
 
-CFX_Matrix CPDF_Page::GetDisplayMatrix(int xPos,
-                                       int yPos,
-                                       int xSize,
-                                       int ySize,
-                                       int iRotate) const {
-  if (m_PageWidth == 0 || m_PageHeight == 0)
+Optional<CFX_PointF> CPDF_Page::DeviceToPage(
+    const FX_RECT& rect,
+    int rotate,
+    const CFX_PointF& device_point) const {
+  CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
+  return page2device.GetInverse().Transform(device_point);
+}
+
+Optional<CFX_PointF> CPDF_Page::PageToDevice(
+    const FX_RECT& rect,
+    int rotate,
+    const CFX_PointF& page_point) const {
+  CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
+  return page2device.Transform(page_point);
+}
+
+CFX_Matrix CPDF_Page::GetDisplayMatrix(const FX_RECT& rect, int iRotate) const {
+  if (m_PageSize.width == 0 || m_PageSize.height == 0)
     return CFX_Matrix();
 
   float x0 = 0;
@@ -136,63 +125,94 @@
   float x2 = 0;
   float y2 = 0;
   iRotate %= 4;
+  // This code implicitly inverts the y-axis to account for page coordinates
+  // pointing up and bitmap coordinates pointing down. (x0, y0) is the base
+  // point, (x1, y1) is that point translated on y and (x2, y2) is the point
+  // translated on x. On iRotate = 0, y0 is rect.bottom and the translation
+  // to get y1 is performed as negative. This results in the desired
+  // transformation.
   switch (iRotate) {
     case 0:
-      x0 = xPos;
-      y0 = yPos + ySize;
-      x1 = xPos;
-      y1 = yPos;
-      x2 = xPos + xSize;
-      y2 = yPos + ySize;
+      x0 = rect.left;
+      y0 = rect.bottom;
+      x1 = rect.left;
+      y1 = rect.top;
+      x2 = rect.right;
+      y2 = rect.bottom;
       break;
     case 1:
-      x0 = xPos;
-      y0 = yPos;
-      x1 = xPos + xSize;
-      y1 = yPos;
-      x2 = xPos;
-      y2 = yPos + ySize;
+      x0 = rect.left;
+      y0 = rect.top;
+      x1 = rect.right;
+      y1 = rect.top;
+      x2 = rect.left;
+      y2 = rect.bottom;
       break;
     case 2:
-      x0 = xPos + xSize;
-      y0 = yPos;
-      x1 = xPos + xSize;
-      y1 = yPos + ySize;
-      x2 = xPos;
-      y2 = yPos;
+      x0 = rect.right;
+      y0 = rect.top;
+      x1 = rect.right;
+      y1 = rect.bottom;
+      x2 = rect.left;
+      y2 = rect.top;
       break;
     case 3:
-      x0 = xPos + xSize;
-      y0 = yPos + ySize;
-      x1 = xPos;
-      y1 = yPos + ySize;
-      x2 = xPos + xSize;
-      y2 = yPos;
+      x0 = rect.right;
+      y0 = rect.bottom;
+      x1 = rect.left;
+      y1 = rect.bottom;
+      x2 = rect.right;
+      y2 = rect.top;
       break;
   }
-  CFX_Matrix matrix = m_PageMatrix;
-  matrix.Concat(CFX_Matrix((x2 - x0) / m_PageWidth, (y2 - y0) / m_PageWidth,
-                           (x1 - x0) / m_PageHeight, (y1 - y0) / m_PageHeight,
-                           x0, y0));
-  return matrix;
+  CFX_Matrix matrix((x2 - x0) / m_PageSize.width, (y2 - y0) / m_PageSize.width,
+                    (x1 - x0) / m_PageSize.height,
+                    (y1 - y0) / m_PageSize.height, x0, y0);
+  return m_PageMatrix * matrix;
 }
 
 int CPDF_Page::GetPageRotation() const {
-  CPDF_Object* pRotate = GetPageAttr("Rotate");
+  CPDF_Object* pRotate = GetPageAttr(pdfium::page_object::kRotate);
   int rotate = pRotate ? (pRotate->GetInteger() / 90) % 4 : 0;
   return (rotate < 0) ? (rotate + 4) : rotate;
 }
 
-bool GraphicsData::operator<(const GraphicsData& other) const {
-  if (fillAlpha != other.fillAlpha)
-    return fillAlpha < other.fillAlpha;
-  if (strokeAlpha != other.strokeAlpha)
-    return strokeAlpha < other.strokeAlpha;
-  return blendType < other.blendType;
+void CPDF_Page::UpdateDimensions() {
+  CFX_FloatRect mediabox = GetBox(pdfium::page_object::kMediaBox);
+  if (mediabox.IsEmpty())
+    mediabox = CFX_FloatRect(0, 0, 612, 792);
+
+  m_BBox = GetBox(pdfium::page_object::kCropBox);
+  if (m_BBox.IsEmpty())
+    m_BBox = mediabox;
+  else
+    m_BBox.Intersect(mediabox);
+
+  m_PageSize.width = m_BBox.Width();
+  m_PageSize.height = m_BBox.Height();
+
+  switch (GetPageRotation()) {
+    case 0:
+      m_PageMatrix = CFX_Matrix(1.0f, 0, 0, 1.0f, -m_BBox.left, -m_BBox.bottom);
+      break;
+    case 1:
+      std::swap(m_PageSize.width, m_PageSize.height);
+      m_PageMatrix =
+          CFX_Matrix(0, -1.0f, 1.0f, 0, -m_BBox.bottom, m_BBox.right);
+      break;
+    case 2:
+      m_PageMatrix = CFX_Matrix(-1.0f, 0, 0, -1.0f, m_BBox.right, m_BBox.top);
+      break;
+    case 3:
+      std::swap(m_PageSize.width, m_PageSize.height);
+      m_PageMatrix = CFX_Matrix(0, 1.0f, -1.0f, 0, m_BBox.top, -m_BBox.left);
+      break;
+  }
 }
 
-bool FontData::operator<(const FontData& other) const {
-  if (baseFont != other.baseFont)
-    return baseFont < other.baseFont;
-  return type < other.type;
+CPDF_Page::RenderContextClearer::RenderContextClearer(CPDF_Page* pPage)
+    : m_pPage(pPage) {}
+
+CPDF_Page::RenderContextClearer::~RenderContextClearer() {
+  m_pPage->SetRenderContext(nullptr);
 }
diff --git a/core/fpdfapi/page/cpdf_page.h b/core/fpdfapi/page/cpdf_page.h
index e1b5bcf..9bdb75d 100644
--- a/core/fpdfapi/page/cpdf_page.h
+++ b/core/fpdfapi/page/cpdf_page.h
@@ -8,63 +8,104 @@
 #define CORE_FPDFAPI_PAGE_CPDF_PAGE_H_
 
 #include <memory>
+#include <utility>
 
 #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/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
+class CPDF_Image;
 class CPDF_Object;
-class CPDF_PageRenderCache;
-class CPDF_PageRenderContext;
 
-class CPDF_Page : public CPDF_PageObjectHolder {
+class CPDF_Page final : public IPDF_Page, public CPDF_PageObjectHolder {
  public:
-  class View {};  // Caller implements as desired, empty here due to layering.
+  // Caller implements as desired, empty here due to layering.
+  class View : public Observable {};
 
-  CPDF_Page(CPDF_Document* pDocument,
-            CPDF_Dictionary* pPageDict,
-            bool bPageCache);
-  ~CPDF_Page() override;
+  // Data for the render layer to attach to this page.
+  class RenderContextIface {
+   public:
+    virtual ~RenderContextIface() {}
+  };
 
-  // CPDF_PageObjectHolder
+  // Cache for the render layer to attach to this page.
+  class RenderCacheIface {
+   public:
+    virtual ~RenderCacheIface() {}
+    virtual void ResetBitmapForImage(const RetainPtr<CPDF_Image>& pImage) = 0;
+  };
+
+  class RenderContextClearer {
+   public:
+    explicit RenderContextClearer(CPDF_Page* pPage);
+    ~RenderContextClearer();
+
+   private:
+    UnownedPtr<CPDF_Page> const m_pPage;
+  };
+
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  // IPDF_Page:
+  CPDF_Page* AsPDFPage() override;
+  CPDFXFA_Page* AsXFAPage() override;
+  CPDF_Document* GetDocument() const override;
+  float GetPageWidth() const override;
+  float GetPageHeight() const override;
+  CFX_Matrix GetDisplayMatrix(const FX_RECT& rect, int iRotate) const override;
+  Optional<CFX_PointF> DeviceToPage(
+      const FX_RECT& rect,
+      int rotate,
+      const CFX_PointF& device_point) const override;
+  Optional<CFX_PointF> PageToDevice(
+      const FX_RECT& rect,
+      int rotate,
+      const CFX_PointF& page_point) const override;
+
+  // CPDF_PageObjectHolder:
   bool IsPage() const override;
 
   void ParseContent();
-
-  CFX_Matrix GetDisplayMatrix(int xPos,
-                              int yPos,
-                              int xSize,
-                              int ySize,
-                              int iRotate) const;
-
-  float GetPageWidth() const { return m_PageWidth; }
-  float GetPageHeight() const { return m_PageHeight; }
-  CFX_FloatRect GetPageBBox() const { return m_BBox; }
+  const CFX_SizeF& GetPageSize() const { return m_PageSize; }
   int GetPageRotation() const;
-  CPDF_PageRenderCache* GetRenderCache() const { return m_pPageRender.get(); }
+  RenderCacheIface* GetRenderCache() const { return m_pRenderCache.get(); }
+  void SetRenderCache(std::unique_ptr<RenderCacheIface> pCache) {
+    m_pRenderCache = std::move(pCache);
+  }
 
-  CPDF_PageRenderContext* GetRenderContext() const {
+  RenderContextIface* GetRenderContext() const {
     return m_pRenderContext.get();
   }
-  void SetRenderContext(std::unique_ptr<CPDF_PageRenderContext> pContext);
+  void SetRenderContext(std::unique_ptr<RenderContextIface> pContext) {
+    m_pRenderContext = std::move(pContext);
+  }
 
-  View* GetView() const { return m_pView; }
-  void SetView(View* pView) { m_pView = pView; }
+  CPDF_Document* GetPDFDocument() const { return m_pPDFDocument.Get(); }
+  View* GetView() const { return m_pView.Get(); }
+  void SetView(View* pView) { m_pView.Reset(pView); }
+  void UpdateDimensions();
 
  private:
-  void StartParse();
+  CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict);
+  ~CPDF_Page() override;
 
   CPDF_Object* GetPageAttr(const ByteString& name) const;
   CFX_FloatRect GetBox(const ByteString& name) const;
 
-  float m_PageWidth;
-  float m_PageHeight;
+  CFX_SizeF m_PageSize;
   CFX_Matrix m_PageMatrix;
-  View* m_pView;
-  std::unique_ptr<CPDF_PageRenderCache> m_pPageRender;
-  std::unique_ptr<CPDF_PageRenderContext> m_pRenderContext;
+  UnownedPtr<CPDF_Document> m_pPDFDocument;
+  std::unique_ptr<RenderCacheIface> m_pRenderCache;
+  std::unique_ptr<RenderContextIface> m_pRenderContext;
+  ObservedPtr<View> m_pView;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PAGE_H_
diff --git a/core/fpdfapi/page/cpdf_pagemodule.cpp b/core/fpdfapi/page/cpdf_pagemodule.cpp
index 3b18ace..705d313 100644
--- a/core/fpdfapi/page/cpdf_pagemodule.cpp
+++ b/core/fpdfapi/page/cpdf_pagemodule.cpp
@@ -6,32 +6,65 @@
 
 #include "core/fpdfapi/page/cpdf_pagemodule.h"
 
+#include "core/fpdfapi/font/cpdf_fontglobals.h"
+#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"
+
+namespace {
+
+CPDF_PageModule* g_PageModule = nullptr;
+
+}  // namespace
+
+// static
+void CPDF_PageModule::Create() {
+  ASSERT(!g_PageModule);
+  fxcodec::ModuleMgr::Create();
+  g_PageModule = new CPDF_PageModule();
+}
+
+// static
+void CPDF_PageModule::Destroy() {
+  ASSERT(g_PageModule);
+  delete g_PageModule;
+  g_PageModule = nullptr;
+  fxcodec::ModuleMgr::Destroy();
+}
+
+// static
+CPDF_PageModule* CPDF_PageModule::GetInstance() {
+  ASSERT(g_PageModule);
+  return g_PageModule;
+}
+
 CPDF_PageModule::CPDF_PageModule()
-    : m_StockGrayCS(PDFCS_DEVICEGRAY),
-      m_StockRGBCS(PDFCS_DEVICERGB),
-      m_StockCMYKCS(PDFCS_DEVICECMYK),
-      m_StockPatternCS(nullptr) {
-  m_StockPatternCS.InitializeStockPattern();
+    : 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_StockPatternCS->InitializeStockPattern();
+  CPDF_FontGlobals::Create();
+  CPDF_FontGlobals::GetInstance()->LoadEmbeddedMaps();
 }
 
-CPDF_PageModule::~CPDF_PageModule() {}
-
-CPDF_FontGlobals* CPDF_PageModule::GetFontGlobals() {
-  return &m_FontGlobals;
+CPDF_PageModule::~CPDF_PageModule() {
+  CPDF_FontGlobals::Destroy();
 }
 
-CPDF_ColorSpace* CPDF_PageModule::GetStockCS(int family) {
+RetainPtr<CPDF_ColorSpace> CPDF_PageModule::GetStockCS(int family) {
   if (family == PDFCS_DEVICEGRAY)
-    return &m_StockGrayCS;
+    return m_StockGrayCS;
   if (family == PDFCS_DEVICERGB)
-    return &m_StockRGBCS;
+    return m_StockRGBCS;
   if (family == PDFCS_DEVICECMYK)
-    return &m_StockCMYKCS;
+    return m_StockCMYKCS;
   if (family == PDFCS_PATTERN)
-    return &m_StockPatternCS;
+    return m_StockPatternCS;
   return nullptr;
 }
 
 void CPDF_PageModule::ClearStockFont(CPDF_Document* pDoc) {
-  m_FontGlobals.Clear(pDoc);
+  CPDF_FontGlobals::GetInstance()->Clear(pDoc);
 }
diff --git a/core/fpdfapi/page/cpdf_pagemodule.h b/core/fpdfapi/page/cpdf_pagemodule.h
index 720ee85..e7234b9 100644
--- a/core/fpdfapi/page/cpdf_pagemodule.h
+++ b/core/fpdfapi/page/cpdf_pagemodule.h
@@ -7,28 +7,31 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_
 
-#include "core/fpdfapi/font/cpdf_fontglobals.h"
-#include "core/fpdfapi/page/cpdf_colorspace.h"
-#include "core/fpdfapi/page/cpdf_devicecs.h"
-#include "core/fpdfapi/page/cpdf_patterncs.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
+class CPDF_ColorSpace;
+class CPDF_DeviceCS;
+class CPDF_PatternCS;
 
 class CPDF_PageModule {
  public:
-  CPDF_PageModule();
-  ~CPDF_PageModule();
+  // Per-process singleton managed by callers.
+  static void Create();
+  static void Destroy();
+  static CPDF_PageModule* GetInstance();
 
-  CPDF_FontGlobals* GetFontGlobals();
-  CPDF_ColorSpace* GetStockCS(int family);
+  RetainPtr<CPDF_ColorSpace> GetStockCS(int family);
   void ClearStockFont(CPDF_Document* pDoc);
 
  private:
-  CPDF_FontGlobals m_FontGlobals;
-  CPDF_DeviceCS m_StockGrayCS;
-  CPDF_DeviceCS m_StockRGBCS;
-  CPDF_DeviceCS m_StockCMYKCS;
-  CPDF_PatternCS m_StockPatternCS;
+  CPDF_PageModule();
+  ~CPDF_PageModule();
+
+  RetainPtr<CPDF_DeviceCS> m_StockGrayCS;
+  RetainPtr<CPDF_DeviceCS> m_StockRGBCS;
+  RetainPtr<CPDF_DeviceCS> m_StockCMYKCS;
+  RetainPtr<CPDF_PatternCS> m_StockPatternCS;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_
diff --git a/core/fpdfapi/page/cpdf_pageobject.cpp b/core/fpdfapi/page/cpdf_pageobject.cpp
index 8bb5bf5..bde1704 100644
--- a/core/fpdfapi/page/cpdf_pageobject.cpp
+++ b/core/fpdfapi/page/cpdf_pageobject.cpp
@@ -6,9 +6,10 @@
 
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 
-CPDF_PageObject::CPDF_PageObject() : m_bDirty(false) {}
+CPDF_PageObject::CPDF_PageObject(int32_t content_stream)
+    : m_ContentStream(content_stream) {}
 
-CPDF_PageObject::~CPDF_PageObject() {}
+CPDF_PageObject::~CPDF_PageObject() = default;
 
 bool CPDF_PageObject::IsText() const {
   return false;
@@ -72,31 +73,28 @@
 
 void CPDF_PageObject::CopyData(const CPDF_PageObject* pSrc) {
   CopyStates(*pSrc);
-  m_Left = pSrc->m_Left;
-  m_Right = pSrc->m_Right;
-  m_Top = pSrc->m_Top;
-  m_Bottom = pSrc->m_Bottom;
+  m_Rect = pSrc->m_Rect;
   m_bDirty = true;
 }
 
-void CPDF_PageObject::TransformClipPath(CFX_Matrix& matrix) {
+void CPDF_PageObject::TransformClipPath(const CFX_Matrix& matrix) {
   if (!m_ClipPath.HasRef())
     return;
   m_ClipPath.Transform(matrix);
   SetDirty(true);
 }
 
-void CPDF_PageObject::TransformGeneralState(CFX_Matrix& matrix) {
+void CPDF_PageObject::TransformGeneralState(const CFX_Matrix& matrix) {
   if (!m_GeneralState.HasRef())
     return;
   m_GeneralState.GetMutableMatrix()->Concat(matrix);
   SetDirty(true);
 }
 
-FX_RECT CPDF_PageObject::GetBBox(const CFX_Matrix* pMatrix) const {
-  CFX_FloatRect rect(m_Left, m_Bottom, m_Right, m_Top);
-  if (pMatrix)
-    rect = pMatrix->TransformRect(rect);
+FX_RECT CPDF_PageObject::GetBBox() const {
+  return GetRect().GetOuterRect();
+}
 
-  return rect.GetOuterRect();
+FX_RECT CPDF_PageObject::GetTransformedBBox(const CFX_Matrix& matrix) const {
+  return matrix.TransformRect(GetRect()).GetOuterRect();
 }
diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h
index d23cd97..f840214 100644
--- a/core/fpdfapi/page/cpdf_pageobject.h
+++ b/core/fpdfapi/page/cpdf_pageobject.h
@@ -7,16 +7,16 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
 
-#include "core/fpdfapi/page/cpdf_contentmark.h"
+#include "core/fpdfapi/page/cpdf_contentmarks.h"
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
 
-class CPDF_TextObject;
-class CPDF_PathObject;
-class CPDF_ImageObject;
-class CPDF_ShadingObject;
 class CPDF_FormObject;
+class CPDF_ImageObject;
+class CPDF_PathObject;
+class CPDF_ShadingObject;
+class CPDF_TextObject;
 
 class CPDF_PageObject : public CPDF_GraphicStates {
  public:
@@ -28,7 +28,11 @@
     FORM,
   };
 
-  CPDF_PageObject();
+  static constexpr int32_t kNoContentStream = -1;
+
+  explicit CPDF_PageObject(int32_t content_stream);
+  CPDF_PageObject(const CPDF_PageObject& src) = delete;
+  CPDF_PageObject& operator=(const CPDF_PageObject& src) = delete;
   ~CPDF_PageObject() override;
 
   virtual Type GetType() const = 0;
@@ -51,28 +55,36 @@
 
   void SetDirty(bool value) { m_bDirty = value; }
   bool IsDirty() const { return m_bDirty; }
-  void TransformClipPath(CFX_Matrix& matrix);
-  void TransformGeneralState(CFX_Matrix& matrix);
+  void TransformClipPath(const CFX_Matrix& matrix);
+  void TransformGeneralState(const CFX_Matrix& matrix);
 
-  CFX_FloatRect GetRect() const {
-    return CFX_FloatRect(m_Left, m_Bottom, m_Right, m_Top);
+  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;
+
+  // 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,
+  // |kNoContentStream| is returned.
+  //
+  // If the object is spread among more than one content stream, this is the
+  // index of the last stream.
+  int32_t GetContentStream() const { return m_ContentStream; }
+  void SetContentStream(int32_t new_content_stream) {
+    m_ContentStream = new_content_stream;
   }
-  FX_RECT GetBBox(const CFX_Matrix* pMatrix) const;
 
-  float m_Left;
-  float m_Right;
-  float m_Top;
-  float m_Bottom;
-  CPDF_ContentMark m_ContentMark;
+  CPDF_ContentMarks m_ContentMarks;
 
  protected:
   void CopyData(const CPDF_PageObject* pSrcObject);
 
- private:
-  CPDF_PageObject(const CPDF_PageObject& src) = delete;
-  void operator=(const CPDF_PageObject& src) = delete;
+  CFX_FloatRect m_Rect;
 
-  bool m_bDirty;
+ private:
+  bool m_bDirty = false;
+  int32_t m_ContentStream;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
index 51a67e8..d7307ab 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.cpp
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
@@ -7,42 +7,68 @@
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 
 #include <algorithm>
+#include <utility>
 
+#include "constants/transparency.h"
 #include "core/fpdfapi/page/cpdf_allstates.h"
 #include "core/fpdfapi/page/cpdf_contentparser.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #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"
 
-CPDF_PageObjectHolder::CPDF_PageObjectHolder(CPDF_Document* pDoc,
-                                             CPDF_Dictionary* pFormDict)
-    : m_pFormDict(pFormDict),
-      m_pFormStream(nullptr),
-      m_pDocument(pDoc),
-      m_pPageResources(nullptr),
-      m_pResources(nullptr),
-      m_iTransparency(0),
-      m_bBackgroundAlphaNeeded(false),
-      m_ParseState(CONTENT_NOT_PARSED) {
-  // TODO(thestig): Check if |m_pFormDict| is never a nullptr and simplify
-  // callers that checks for that.
+bool GraphicsData::operator<(const GraphicsData& other) const {
+  if (!FXSYS_SafeEQ(fillAlpha, other.fillAlpha))
+    return FXSYS_SafeLT(fillAlpha, other.fillAlpha);
+  if (!FXSYS_SafeEQ(strokeAlpha, other.strokeAlpha))
+    return FXSYS_SafeLT(strokeAlpha, other.strokeAlpha);
+  return blendType < other.blendType;
 }
 
-CPDF_PageObjectHolder::~CPDF_PageObjectHolder() {}
+bool FontData::operator<(const FontData& other) const {
+  if (baseFont != other.baseFont)
+    return baseFont < other.baseFont;
+  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),
+      m_pDocument(pDoc) {
+  ASSERT(m_pDict);
+}
+
+CPDF_PageObjectHolder::~CPDF_PageObjectHolder() = default;
 
 bool CPDF_PageObjectHolder::IsPage() const {
   return false;
 }
 
-void CPDF_PageObjectHolder::ContinueParse(IFX_PauseIndicator* pPause) {
-  if (!m_pParser)
+void CPDF_PageObjectHolder::StartParse(
+    std::unique_ptr<CPDF_ContentParser> pParser) {
+  ASSERT(m_ParseState == ParseState::kNotParsed);
+  m_pParser = std::move(pParser);
+  m_ParseState = ParseState::kParsing;
+}
+
+void CPDF_PageObjectHolder::ContinueParse(PauseIndicatorIface* pPause) {
+  if (m_ParseState == ParseState::kParsed)
     return;
 
+  ASSERT(m_ParseState == ParseState::kParsing);
   if (m_pParser->Continue(pPause))
     return;
 
-  m_ParseState = CONTENT_PARSED;
+  m_ParseState = ParseState::kParsed;
+  m_pDocument->IncrementParsedPageCount();
   if (m_pParser->GetCurStates())
     m_LastCTM = m_pParser->GetCurStates()->m_CTM;
+
   m_pParser.reset();
 }
 
@@ -50,44 +76,60 @@
   m_MaskBoundingBoxes.push_back(box);
 }
 
-void CPDF_PageObjectHolder::Transform(const CFX_Matrix& matrix) {
-  for (auto& pObj : m_PageObjectList)
-    pObj->Transform(matrix);
+std::set<int32_t> CPDF_PageObjectHolder::TakeDirtyStreams() {
+  auto dirty_streams = std::move(m_DirtyStreams);
+  m_DirtyStreams.clear();
+  return dirty_streams;
 }
 
-CFX_FloatRect CPDF_PageObjectHolder::CalcBoundingBox() const {
-  if (m_PageObjectList.empty())
-    return CFX_FloatRect();
+void CPDF_PageObjectHolder::LoadTransparencyInfo() {
+  CPDF_Dictionary* pGroup = m_pDict->GetDictFor("Group");
+  if (!pGroup)
+    return;
 
-  float left = 1000000.0f;
-  float right = -1000000.0f;
-  float bottom = 1000000.0f;
-  float top = -1000000.0f;
-  for (const auto& pObj : m_PageObjectList) {
-    left = std::min(left, pObj->m_Left);
-    right = std::max(right, pObj->m_Right);
-    bottom = std::min(bottom, pObj->m_Bottom);
-    top = std::max(top, pObj->m_Top);
+  if (pGroup->GetStringFor(pdfium::transparency::kGroupSubType) !=
+      pdfium::transparency::kTransparency) {
+    return;
   }
-  return CFX_FloatRect(left, bottom, right, top);
+  m_Transparency.SetGroup();
+  if (pGroup->GetIntegerFor(pdfium::transparency::kI))
+    m_Transparency.SetIsolated();
 }
 
-void CPDF_PageObjectHolder::LoadTransInfo() {
-  if (!m_pFormDict) {
-    return;
-  }
-  CPDF_Dictionary* pGroup = m_pFormDict->GetDictFor("Group");
-  if (!pGroup) {
-    return;
-  }
-  if (pGroup->GetStringFor("S") != "Transparency") {
-    return;
-  }
-  m_iTransparency |= PDFTRANS_GROUP;
-  if (pGroup->GetIntegerFor("I")) {
-    m_iTransparency |= PDFTRANS_ISOLATED;
-  }
-  if (pGroup->GetIntegerFor("K")) {
-    m_iTransparency |= PDFTRANS_KNOCKOUT;
-  }
+CPDF_PageObject* CPDF_PageObjectHolder::GetPageObjectByIndex(
+    size_t index) const {
+  return pdfium::IndexInBounds(m_PageObjectList, index)
+             ? m_PageObjectList[index].get()
+             : nullptr;
+}
+
+void CPDF_PageObjectHolder::AppendPageObject(
+    std::unique_ptr<CPDF_PageObject> pPageObj) {
+  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);
+  if (it == std::end(m_PageObjectList))
+    return false;
+
+  it->release();
+  m_PageObjectList.erase(it);
+
+  int32_t content_stream = pPageObj->GetContentStream();
+  if (content_stream >= 0)
+    m_DirtyStreams.insert(content_stream);
+
+  return true;
+}
+
+bool CPDF_PageObjectHolder::ErasePageObjectAtIndex(size_t index) {
+  if (index >= m_PageObjectList.size())
+    return false;
+
+  m_PageObjectList.erase(m_PageObjectList.begin() + index);
+  return true;
 }
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.h b/core/fpdfapi/page/cpdf_pageobjectholder.h
index f649ef7..b9eff30 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.h
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.h
@@ -7,57 +7,82 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_
 
+#include <deque>
 #include <map>
 #include <memory>
+#include <set>
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_pageobjectlist.h"
+#include "core/fpdfapi/page/cpdf_transparency.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"
+#include "core/fxge/fx_dib.h"
 
-class IFX_PauseIndicator;
-class CPDF_Dictionary;
-class CPDF_Stream;
-class CPDF_Document;
 class CPDF_ContentParser;
-
-#define PDFTRANS_GROUP 0x0100
-#define PDFTRANS_ISOLATED 0x0200
-#define PDFTRANS_KNOCKOUT 0x0400
+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
 // generated in the page object holder.
 struct GraphicsData {
   float fillAlpha;
   float strokeAlpha;
-  int blendType;
+  BlendMode blendType;
+
   bool operator<(const GraphicsData& other) const;
 };
 
 struct FontData {
   ByteString baseFont;
   ByteString type;
+
   bool operator<(const FontData& other) const;
 };
 
 class CPDF_PageObjectHolder {
  public:
-  CPDF_PageObjectHolder(CPDF_Document* pDoc, CPDF_Dictionary* pFormDict);
+  enum class ParseState : uint8_t { kNotParsed, kParsing, kParsed };
+
+  using iterator = std::deque<std::unique_ptr<CPDF_PageObject>>::iterator;
+  using const_iterator =
+      std::deque<std::unique_ptr<CPDF_PageObject>>::const_iterator;
+
+  CPDF_PageObjectHolder(CPDF_Document* pDoc,
+                        CPDF_Dictionary* pDict,
+                        CPDF_Dictionary* pPageResources,
+                        CPDF_Dictionary* pResources);
   virtual ~CPDF_PageObjectHolder();
 
   virtual bool IsPage() const;
 
-  void ContinueParse(IFX_PauseIndicator* pPause);
-  bool IsParsed() const { return m_ParseState == CONTENT_PARSED; }
+  void StartParse(std::unique_ptr<CPDF_ContentParser> pParser);
+  void ContinueParse(PauseIndicatorIface* pPause);
+  ParseState GetParseState() const { return m_ParseState; }
 
-  CPDF_PageObjectList* GetPageObjectList() { return &m_PageObjectList; }
-  const CPDF_PageObjectList* GetPageObjectList() const {
-    return &m_PageObjectList;
-  }
+  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
+
+  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  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);
+  bool ErasePageObjectAtIndex(size_t index);
+
+  iterator begin() { return m_PageObjectList.begin(); }
+  const_iterator begin() const { return m_PageObjectList.begin(); }
+
+  iterator end() { return m_PageObjectList.end(); }
+  const_iterator end() const { return m_PageObjectList.end(); }
+
   const CFX_Matrix& GetLastCTM() const { return m_LastCTM; }
+  const CFX_FloatRect& GetBBox() const { return m_BBox; }
 
+  const CPDF_Transparency& GetTransparency() const { return m_Transparency; }
   bool BackgroundAlphaNeeded() const { return m_bBackgroundAlphaNeeded; }
   void SetBackgroundAlphaNeeded(bool needed) {
     m_bBackgroundAlphaNeeded = needed;
@@ -68,30 +93,32 @@
     return m_MaskBoundingBoxes;
   }
   void AddImageMaskBoundingBox(const CFX_FloatRect& box);
-  void Transform(const CFX_Matrix& matrix);
-  CFX_FloatRect CalcBoundingBox() const;
+  bool HasDirtyStreams() const { return !m_DirtyStreams.empty(); }
+  std::set<int32_t> TakeDirtyStreams();
 
-  const UnownedPtr<CPDF_Dictionary> m_pFormDict;
-  UnownedPtr<CPDF_Stream> m_pFormStream;
-  UnownedPtr<CPDF_Document> m_pDocument;
-  UnownedPtr<CPDF_Dictionary> m_pPageResources;
-  UnownedPtr<CPDF_Dictionary> m_pResources;
+  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;
-  int m_iTransparency;
 
  protected:
-  enum ParseState { CONTENT_NOT_PARSED, CONTENT_PARSING, CONTENT_PARSED };
+  void LoadTransparencyInfo();
 
-  void LoadTransInfo();
+  CFX_FloatRect m_BBox;
+  CPDF_Transparency m_Transparency;
 
-  bool m_bBackgroundAlphaNeeded;
+ private:
+  bool m_bBackgroundAlphaNeeded = false;
+  ParseState m_ParseState = ParseState::kNotParsed;
+  RetainPtr<CPDF_Dictionary> const m_pDict;
+  UnownedPtr<CPDF_Document> m_pDocument;
   std::vector<CFX_FloatRect> m_MaskBoundingBoxes;
-  ParseState m_ParseState;
   std::unique_ptr<CPDF_ContentParser> m_pParser;
-  CPDF_PageObjectList m_PageObjectList;
+  std::deque<std::unique_ptr<CPDF_PageObject>> m_PageObjectList;
   CFX_Matrix m_LastCTM;
+
+  // The indexes of Content streams that are dirty and need to be regenerated.
+  std::set<int32_t> m_DirtyStreams;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp b/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp
new file mode 100644
index 0000000..7760256
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp
@@ -0,0 +1,66 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/page/cpdf_pageobjectholder.h"
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// See https://crbug.com/852273
+TEST(CPDFPageObjectHolder, GraphicsDataAsKey) {
+  const float fMin = std::numeric_limits<float>::min();
+  const float fMax = std::numeric_limits<float>::max();
+  const float fInf = std::numeric_limits<float>::infinity();
+  const float fNan = std::numeric_limits<float>::quiet_NaN();
+
+  // Verify self-comparisions.
+  for (float c1 : {fMin, 1.0f, 2.0f, fMax, fInf, fNan}) {
+    for (float c2 : {fMin, 1.0f, 2.0f, fMax, fInf, fNan}) {
+      for (BlendMode c3 : {BlendMode::kMultiply, BlendMode::kScreen})
+        EXPECT_FALSE(GraphicsData({c1, c2, c3}) < GraphicsData({c1, c2, c3}));
+    }
+  }
+
+  std::map<GraphicsData, int> graphics_map;
+
+  // Insert in reverse index permuted order.
+  size_t x = 0;
+  for (BlendMode c3 : {BlendMode::kScreen, BlendMode::kMultiply}) {
+    for (float c2 : {fNan, fInf, fMax, 2.0f, 1.0f, fMin}) {
+      for (float c1 : {fNan, fInf, fMax, 2.0f, 1.0f, fMin}) {
+        graphics_map[{c1, c2, c3}] = x++;
+      }
+    }
+  }
+  EXPECT_EQ(72u, x);
+  EXPECT_EQ(72u, graphics_map.size());
+
+  // clang-format off
+  const int expected[72] = {
+      71, 35, 65, 29, 59, 23, 53, 17, 47, 11, 41, 5,
+      70, 34, 64, 28, 58, 22, 52, 16, 46, 10, 40, 4,
+      69, 33, 63, 27, 57, 21, 51, 15, 45, 9,  39, 3,
+      68, 32, 62, 26, 56, 20, 50, 14, 44, 8,  38, 2,
+      67, 31, 61, 25, 55, 19, 49, 13, 43, 7,  37, 1,
+      66, 30, 60, 24, 54, 18, 48, 12, 42, 6,  36, 0
+  };
+  // clang-format on
+
+  x = 0;
+  for (const auto& item : graphics_map) {
+    EXPECT_EQ(expected[x], item.second) << " for position " << x;
+    ++x;
+  }
+  EXPECT_EQ(72u, x);
+
+  // Erase in forward index permuted order.
+  for (BlendMode c3 : {BlendMode::kMultiply, BlendMode::kScreen}) {
+    for (float c2 : {fMin, 1.0f, 2.0f, fMax, fInf, fNan}) {
+      for (float c1 : {fMin, 1.0f, 2.0f, fMax, fInf, fNan})
+        graphics_map.erase({c1, c2, c3});
+    }
+  }
+  EXPECT_EQ(0u, graphics_map.size());
+}
diff --git a/core/fpdfapi/page/cpdf_pageobjectlist.cpp b/core/fpdfapi/page/cpdf_pageobjectlist.cpp
deleted file mode 100644
index afd2c98..0000000
--- a/core/fpdfapi/page/cpdf_pageobjectlist.cpp
+++ /dev/null
@@ -1,13 +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/page/cpdf_pageobjectlist.h"
-
-#include "third_party/base/stl_util.h"
-
-CPDF_PageObject* CPDF_PageObjectList::GetPageObjectByIndex(int index) {
-  return pdfium::IndexInBounds(*this, index) ? (*this)[index].get() : nullptr;
-}
diff --git a/core/fpdfapi/page/cpdf_pageobjectlist.h b/core/fpdfapi/page/cpdf_pageobjectlist.h
deleted file mode 100644
index b450537..0000000
--- a/core/fpdfapi/page/cpdf_pageobjectlist.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTLIST_H_
-#define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTLIST_H_
-
-#include <deque>
-#include <memory>
-
-class CPDF_PageObject;
-
-class CPDF_PageObjectList
-    : public std::deque<std::unique_ptr<CPDF_PageObject>> {
- public:
-  CPDF_PageObject* GetPageObjectByIndex(int index);
-};
-
-#endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTLIST_H_
diff --git a/core/fpdfapi/page/cpdf_path.cpp b/core/fpdfapi/page/cpdf_path.cpp
index b56249c..a07bb6a 100644
--- a/core/fpdfapi/page/cpdf_path.cpp
+++ b/core/fpdfapi/page/cpdf_path.cpp
@@ -6,11 +6,11 @@
 
 #include "core/fpdfapi/page/cpdf_path.h"
 
-CPDF_Path::CPDF_Path() {}
+CPDF_Path::CPDF_Path() = default;
 
 CPDF_Path::CPDF_Path(const CPDF_Path& that) : m_Ref(that.m_Ref) {}
 
-CPDF_Path::~CPDF_Path() {}
+CPDF_Path::~CPDF_Path() = default;
 
 const std::vector<FX_PATHPOINT>& CPDF_Path::GetPoints() const {
   return m_Ref.GetObject()->GetPoints();
@@ -37,18 +37,18 @@
   return m_Ref.GetObject()->IsRect();
 }
 
-void CPDF_Path::Transform(const CFX_Matrix* pMatrix) {
-  m_Ref.GetPrivateCopy()->Transform(pMatrix);
-}
-
-void CPDF_Path::Append(const CPDF_Path& other, const CFX_Matrix* pMatrix) {
-  m_Ref.GetPrivateCopy()->Append(other.GetObject(), pMatrix);
+void CPDF_Path::Transform(const CFX_Matrix& matrix) {
+  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::AppendFloatRect(const CFX_FloatRect& rect) {
+  m_Ref.GetPrivateCopy()->AppendFloatRect(rect);
+}
+
 void CPDF_Path::AppendRect(float left, float bottom, float right, float top) {
   m_Ref.GetPrivateCopy()->AppendRect(left, bottom, right, top);
 }
diff --git a/core/fpdfapi/page/cpdf_path.h b/core/fpdfapi/page/cpdf_path.h
index 613d715..48f5634 100644
--- a/core/fpdfapi/page/cpdf_path.h
+++ b/core/fpdfapi/page/cpdf_path.h
@@ -11,9 +11,7 @@
 
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/shared_copy_on_write.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_renderdevice.h"
 
 class CPDF_Path {
  public:
@@ -32,10 +30,10 @@
   CFX_FloatRect GetBoundingBox(float line_width, float miter_limit) const;
 
   bool IsRect() const;
-  void Transform(const CFX_Matrix* pMatrix);
+  void Transform(const CFX_Matrix& matrix);
 
-  void Append(const CPDF_Path& other, const CFX_Matrix* pMatrix);
   void Append(const CFX_PathData* pData, 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);
 
@@ -43,7 +41,7 @@
   const CFX_PathData* GetObject() const { return m_Ref.GetObject(); }
 
  private:
-  SharedCopyOnWrite<CFX_PathData> m_Ref;
+  SharedCopyOnWrite<CFX_RetainablePathData> 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 d8c2cb8..eafbe77 100644
--- a/core/fpdfapi/page/cpdf_pathobject.cpp
+++ b/core/fpdfapi/page/cpdf_pathobject.cpp
@@ -6,9 +6,12 @@
 
 #include "core/fpdfapi/page/cpdf_pathobject.h"
 
-CPDF_PathObject::CPDF_PathObject() : m_FillType(0), m_bStroke(false) {}
+CPDF_PathObject::CPDF_PathObject(int32_t content_stream)
+    : CPDF_PageObject(content_stream) {}
 
-CPDF_PathObject::~CPDF_PathObject() {}
+CPDF_PathObject::CPDF_PathObject() : CPDF_PathObject(kNoContentStream) {}
+
+CPDF_PathObject::~CPDF_PathObject() = default;
 
 CPDF_PageObject::Type CPDF_PathObject::GetType() const {
   return PATH;
@@ -44,14 +47,7 @@
   }
   rect = m_Matrix.TransformRect(rect);
 
-  if (width == 0 && m_bStroke) {
-    rect.left += -0.5f;
-    rect.right += 0.5f;
-    rect.bottom += -0.5f;
-    rect.top += 0.5f;
-  }
-  m_Left = rect.left;
-  m_Right = rect.right;
-  m_Top = rect.top;
-  m_Bottom = rect.bottom;
+  if (width == 0 && m_bStroke)
+    rect.Inflate(0.5f, 0.5f);
+  SetRect(rect);
 }
diff --git a/core/fpdfapi/page/cpdf_pathobject.h b/core/fpdfapi/page/cpdf_pathobject.h
index 58499b5..889d8a6 100644
--- a/core/fpdfapi/page/cpdf_pathobject.h
+++ b/core/fpdfapi/page/cpdf_pathobject.h
@@ -11,24 +11,47 @@
 #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"
 
-class CPDF_PathObject : public CPDF_PageObject {
+class CPDF_PathObject final : public CPDF_PageObject {
  public:
+  explicit CPDF_PathObject(int32_t content_stream);
   CPDF_PathObject();
   ~CPDF_PathObject() override;
 
   // CPDF_PageObject
   Type GetType() const override;
-  void Transform(const CFX_Matrix& maxtrix) override;
+  void Transform(const CFX_Matrix& matrix) override;
   bool IsPath() const override;
   CPDF_PathObject* AsPath() override;
   const CPDF_PathObject* AsPath() const override;
 
   void CalcBoundingBox();
 
+  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; }
+
+  int filltype() const { return m_FillType; }
+  void set_filltype(int filltype) { m_FillType = filltype; }
+
+  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; }
+
+ private:
+  bool m_bStroke = false;
+  int m_FillType = 0;
   CPDF_Path m_Path;
-  int m_FillType;
-  bool m_bStroke;
   CFX_Matrix m_Matrix;
 };
 
diff --git a/core/fpdfapi/page/cpdf_pattern.cpp b/core/fpdfapi/page/cpdf_pattern.cpp
index 88e5dee..36ecc98 100644
--- a/core/fpdfapi/page/cpdf_pattern.cpp
+++ b/core/fpdfapi/page/cpdf_pattern.cpp
@@ -11,12 +11,22 @@
 CPDF_Pattern::CPDF_Pattern(CPDF_Document* pDoc,
                            CPDF_Object* pObj,
                            const CFX_Matrix& parentMatrix)
-    : m_pDocument(pDoc), m_pPatternObj(pObj), m_ParentMatrix(parentMatrix) {}
+    : m_pDocument(pDoc), m_pPatternObj(pObj), m_ParentMatrix(parentMatrix) {
+  ASSERT(m_pDocument);
+  ASSERT(m_pPatternObj);
+}
 
-CPDF_Pattern::~CPDF_Pattern() {}
+CPDF_Pattern::~CPDF_Pattern() = default;
+
+CPDF_TilingPattern* CPDF_Pattern::AsTilingPattern() {
+  return nullptr;
+}
+
+CPDF_ShadingPattern* CPDF_Pattern::AsShadingPattern() {
+  return nullptr;
+}
 
 void CPDF_Pattern::SetPatternToFormMatrix() {
-  CPDF_Dictionary* pDict = pattern_obj()->GetDict();
-  m_Pattern2Form = pDict->GetMatrixFor("Matrix");
-  m_Pattern2Form.Concat(m_ParentMatrix);
+  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 307c677..7e3eb7b 100644
--- a/core/fpdfapi/page/cpdf_pattern.h
+++ b/core/fpdfapi/page/cpdf_pattern.h
@@ -7,9 +7,10 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_
 
-#include "core/fpdfapi/page/cpdf_countedobject.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;
@@ -17,19 +18,20 @@
 class CPDF_ShadingPattern;
 class CPDF_TilingPattern;
 
-class CPDF_Pattern {
+class CPDF_Pattern : public Retainable, public Observable {
  public:
-  enum PatternType { TILING = 1, SHADING };
+  // Values used in PDFs. Do not change.
+  enum PatternType { kTiling = 1, kShading = 2 };
 
-  virtual ~CPDF_Pattern();
+  ~CPDF_Pattern() override;
 
-  virtual CPDF_TilingPattern* AsTilingPattern() = 0;
-  virtual CPDF_ShadingPattern* AsShadingPattern() = 0;
+  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(); }
-  CFX_Matrix* pattern_to_form() { return &m_Pattern2Form; }
+  const CFX_Matrix& pattern_to_form() const { return m_Pattern2Form; }
   const CFX_Matrix& parent_matrix() const { return m_ParentMatrix; }
 
  protected:
@@ -41,10 +43,9 @@
 
  private:
   UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<CPDF_Object> const m_pPatternObj;
+  RetainPtr<CPDF_Object> const m_pPatternObj;
   CFX_Matrix m_Pattern2Form;
   const CFX_Matrix m_ParentMatrix;
 };
-using CPDF_CountedPattern = CPDF_CountedObject<CPDF_Pattern>;
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_
diff --git a/core/fpdfapi/page/cpdf_patterncs.cpp b/core/fpdfapi/page/cpdf_patterncs.cpp
index b4b680f..1c5dc6c 100644
--- a/core/fpdfapi/page/cpdf_patterncs.cpp
+++ b/core/fpdfapi/page/cpdf_patterncs.cpp
@@ -11,31 +11,22 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 
 CPDF_PatternCS::CPDF_PatternCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_PATTERN),
-      m_pBaseCS(nullptr),
-      m_pCountedBaseCS(nullptr) {}
+    : CPDF_ColorSpace(pDoc, PDFCS_PATTERN) {}
 
-CPDF_PatternCS::~CPDF_PatternCS() {
-  CPDF_ColorSpace* pCS = m_pCountedBaseCS ? m_pCountedBaseCS->get() : nullptr;
-  if (pCS && m_pDocument) {
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData)
-      pPageData->ReleaseColorSpace(pCS->GetArray());
-  }
-}
+CPDF_PatternCS::~CPDF_PatternCS() = default;
 
 void CPDF_PatternCS::InitializeStockPattern() {
   SetComponentsForStockCS(1);
 }
 
 uint32_t CPDF_PatternCS::v_Load(CPDF_Document* pDoc,
-                                CPDF_Array* pArray,
-                                std::set<CPDF_Object*>* pVisited) {
-  CPDF_Object* pBaseCS = pArray->GetDirectObjectAt(1);
+                                const CPDF_Array* pArray,
+                                std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Object* pBaseCS = pArray->GetDirectObjectAt(1);
   if (pBaseCS == m_pArray)
     return 0;
 
-  CPDF_DocPageData* pDocPageData = pDoc->GetPageData();
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc);
   m_pBaseCS = pDocPageData->GetColorSpaceGuarded(pBaseCS, nullptr, pVisited);
   if (!m_pBaseCS)
     return 1;
@@ -43,20 +34,35 @@
   if (m_pBaseCS->GetFamily() == PDFCS_PATTERN)
     return 0;
 
-  m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
   if (m_pBaseCS->CountComponents() > kMaxPatternColorComps)
     return 0;
 
   return m_pBaseCS->CountComponents() + 1;
 }
 
-bool CPDF_PatternCS::GetRGB(float* pBuf, float* R, float* G, float* B) const {
-  if (m_pBaseCS) {
-    ASSERT(m_pBaseCS->GetFamily() != PDFCS_PATTERN);
-    PatternValue* pvalue = reinterpret_cast<PatternValue*>(pBuf);
-    if (m_pBaseCS->GetRGB(pvalue->m_Comps, R, G, B))
-      return true;
-  }
+bool CPDF_PatternCS::GetRGB(const float* pBuf,
+                            float* R,
+                            float* G,
+                            float* B) const {
+  NOTREACHED();
+  return false;
+}
+
+CPDF_PatternCS* CPDF_PatternCS::AsPatternCS() {
+  return this;
+}
+
+const CPDF_PatternCS* CPDF_PatternCS::AsPatternCS() const {
+  return this;
+}
+
+bool CPDF_PatternCS::GetPatternRGB(const PatternValue& value,
+                                   float* R,
+                                   float* G,
+                                   float* B) const {
+  if (m_pBaseCS && m_pBaseCS->GetRGB(value.GetComps().data(), R, G, B))
+    return true;
+
   *R = 0.75f;
   *G = 0.75f;
   *B = 0.75f;
diff --git a/core/fpdfapi/page/cpdf_patterncs.h b/core/fpdfapi/page/cpdf_patterncs.h
index d1d0d48..b6b46f6 100644
--- a/core/fpdfapi/page/cpdf_patterncs.h
+++ b/core/fpdfapi/page/cpdf_patterncs.h
@@ -10,26 +10,37 @@
 #include <set>
 
 #include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
 
-class CPDF_PatternCS : public CPDF_ColorSpace {
+class CPDF_PatternCS final : public CPDF_ColorSpace {
  public:
-  explicit CPDF_PatternCS(CPDF_Document* pDoc);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_PatternCS() override;
 
-  // Called for the stock pattern, since it is initialized via Load().
+  // Called for the stock pattern, since it is not initialized via
+  // CPDF_ColorSpace::Load().
   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;
+  const CPDF_PatternCS* AsPatternCS() const override;
   uint32_t v_Load(CPDF_Document* pDoc,
-                  CPDF_Array* pArray,
-                  std::set<CPDF_Object*>* pVisited) override;
-  bool GetRGB(float* pBuf, float* R, float* G, float* B) const override;
+                  const CPDF_Array* pArray,
+                  std::set<const CPDF_Object*>* pVisited) override;
 
  private:
-  CPDF_ColorSpace* m_pBaseCS;
-  CPDF_CountedColorSpace* m_pCountedBaseCS;
+  explicit CPDF_PatternCS(CPDF_Document* pDoc);
+
+  RetainPtr<CPDF_ColorSpace> m_pBaseCS;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PATTERNCS_H_
diff --git a/core/fpdfapi/page/cpdf_psengine.cpp b/core/fpdfapi/page/cpdf_psengine.cpp
index 7ce3a60..98f5c47 100644
--- a/core/fpdfapi/page/cpdf_psengine.cpp
+++ b/core/fpdfapi/page/cpdf_psengine.cpp
@@ -7,6 +7,8 @@
 #include "core/fpdfapi/page/cpdf_psengine.h"
 
 #include <algorithm>
+#include <cmath>
+#include <limits>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
@@ -67,6 +69,16 @@
     {"xor", PSOP_XOR},
 };
 
+// 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))
+    return 0;
+  if (f > std::numeric_limits<float>::max() - 0.5f)
+    return std::numeric_limits<float>::max();
+  return floor(f + 0.5f);
+}
+
 }  // namespace
 
 CPDF_PSOP::CPDF_PSOP()
@@ -157,25 +169,25 @@
   return true;
 }
 
-void CPDF_PSProc::AddOperatorForTesting(const ByteStringView& word) {
+void CPDF_PSProc::AddOperatorForTesting(ByteStringView word) {
   AddOperator(word);
 }
 
-void CPDF_PSProc::AddOperator(const ByteStringView& word) {
-  const auto* pFound = std::lower_bound(
-      std::begin(kPsOpNames), std::end(kPsOpNames), word,
-      [](const PDF_PSOpName& name, const ByteStringView& word) {
-        return name.name < word;
-      });
+void CPDF_PSProc::AddOperator(ByteStringView word) {
+  const auto* pFound =
+      std::lower_bound(std::begin(kPsOpNames), std::end(kPsOpNames), word,
+                       [](const PDF_PSOpName& name, ByteStringView word) {
+                         return name.name < word;
+                       });
   if (pFound != std::end(kPsOpNames) && pFound->name == word)
     m_Operators.push_back(pdfium::MakeUnique<CPDF_PSOP>(pFound->op));
   else
-    m_Operators.push_back(pdfium::MakeUnique<CPDF_PSOP>(FX_atof(word)));
+    m_Operators.push_back(pdfium::MakeUnique<CPDF_PSOP>(StringToFloat(word)));
 }
 
-CPDF_PSEngine::CPDF_PSEngine() : m_StackCount(0) {}
+CPDF_PSEngine::CPDF_PSEngine() = default;
 
-CPDF_PSEngine::~CPDF_PSEngine() {}
+CPDF_PSEngine::~CPDF_PSEngine() = default;
 
 void CPDF_PSEngine::Push(float v) {
   if (m_StackCount < kPSEngineStackSize)
@@ -190,10 +202,9 @@
   return static_cast<int>(Pop());
 }
 
-bool CPDF_PSEngine::Parse(const char* str, int size) {
-  CPDF_SimpleParser parser(reinterpret_cast<const uint8_t*>(str), size);
-  ByteStringView word = parser.GetWord();
-  return word == "{" ? m_MainProc.Parse(&parser, 0) : false;
+bool CPDF_PSEngine::Parse(pdfium::span<const uint8_t> input) {
+  CPDF_SimpleParser parser(input);
+  return parser.GetWord() == "{" && m_MainProc.Parse(&parser, 0);
 }
 
 bool CPDF_PSEngine::DoOperator(PDF_PSOP op) {
@@ -263,7 +274,7 @@
       break;
     case PSOP_ROUND:
       d1 = Pop();
-      Push(FXSYS_round(d1));
+      Push(RoundHalfUp(d1));
       break;
     case PSOP_TRUNCATE:
       i1 = PopInt();
diff --git a/core/fpdfapi/page/cpdf_psengine.h b/core/fpdfapi/page/cpdf_psengine.h
index 63d91d0..3db8db7 100644
--- a/core/fpdfapi/page/cpdf_psengine.h
+++ b/core/fpdfapi/page/cpdf_psengine.h
@@ -12,6 +12,7 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
 
 class CPDF_PSEngine;
 class CPDF_PSProc;
@@ -90,7 +91,7 @@
   bool Execute(CPDF_PSEngine* pEngine);
 
   // These methods are exposed for testing.
-  void AddOperatorForTesting(const ByteStringView& word);
+  void AddOperatorForTesting(ByteStringView word);
   size_t num_operators() const { return m_Operators.size(); }
   const std::unique_ptr<CPDF_PSOP>& last_operator() {
     return m_Operators.back();
@@ -99,7 +100,7 @@
  private:
   static const int kMaxDepth = 128;
 
-  void AddOperator(const ByteStringView& word);
+  void AddOperator(ByteStringView word);
 
   std::vector<std::unique_ptr<CPDF_PSOP>> m_Operators;
 };
@@ -109,7 +110,7 @@
   CPDF_PSEngine();
   ~CPDF_PSEngine();
 
-  bool Parse(const char* str, int size);
+  bool Parse(pdfium::span<const uint8_t> input);
   bool Execute();
   bool DoOperator(PDF_PSOP op);
   void Reset() { m_StackCount = 0; }
@@ -120,9 +121,10 @@
 
  private:
   static constexpr uint32_t kPSEngineStackSize = 100;
-  float m_Stack[kPSEngineStackSize];
-  uint32_t m_StackCount;
+
+  uint32_t m_StackCount = 0;
   CPDF_PSProc m_MainProc;
+  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 f966c3a..a922de1 100644
--- a/core/fpdfapi/page/cpdf_psengine_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_psengine_unittest.cpp
@@ -2,9 +2,34 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <limits>
+
 #include "core/fpdfapi/page/cpdf_psengine.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+
+float DoOperator1(CPDF_PSEngine* engine, float v1, PDF_PSOP op) {
+  EXPECT_EQ(0u, engine->GetStackSize());
+  engine->Push(v1);
+  engine->DoOperator(op);
+  float ret = engine->Pop();
+  EXPECT_EQ(0u, engine->GetStackSize());
+  return ret;
+}
+
+float DoOperator2(CPDF_PSEngine* engine, float v1, float v2, PDF_PSOP op) {
+  EXPECT_EQ(0u, engine->GetStackSize());
+  engine->Push(v1);
+  engine->Push(v2);
+  engine->DoOperator(op);
+  float ret = engine->Pop();
+  EXPECT_EQ(0u, engine->GetStackSize());
+  return ret;
+}
+
+}  // namespace
+
 TEST(CPDF_PSProc, AddOperator) {
   static const struct {
     const char* name;
@@ -54,3 +79,115 @@
     }
   }
 }
+
+TEST(CPDF_PSEngine, Basic) {
+  CPDF_PSEngine engine;
+
+  EXPECT_FLOAT_EQ(300.0f, DoOperator2(&engine, 100, 200, PSOP_ADD));
+  EXPECT_FLOAT_EQ(-50.0f, DoOperator2(&engine, 100, 150, PSOP_SUB));
+  EXPECT_FLOAT_EQ(600.0f, DoOperator2(&engine, 5, 120, PSOP_MUL));
+  EXPECT_FLOAT_EQ(1.5f, DoOperator2(&engine, 15, 10, PSOP_DIV));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 15, 10, PSOP_IDIV));
+  EXPECT_FLOAT_EQ(5.0f, DoOperator2(&engine, 15, 10, PSOP_MOD));
+  EXPECT_FLOAT_EQ(5.0f, DoOperator1(&engine, -5, PSOP_NEG));
+  EXPECT_FLOAT_EQ(5.0f, DoOperator1(&engine, -5, PSOP_ABS));
+}
+
+TEST(CPDF_PSEngine, Ceiling) {
+  CPDF_PSEngine engine;
+
+  // Smallest positive float value.
+  float min_float = std::numeric_limits<float>::min();
+  // Largest positive float value.
+  float max_float = std::numeric_limits<float>::max();
+  EXPECT_FLOAT_EQ(1.0f, DoOperator1(&engine, min_float, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(max_float, DoOperator1(&engine, max_float, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, -min_float, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(-max_float, DoOperator1(&engine, -max_float, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, -0.9f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(1.0f, DoOperator1(&engine, 0.0000000001f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(3.0f, DoOperator1(&engine, 2.3f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(4.0f, DoOperator1(&engine, 3.8f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(6.0f, DoOperator1(&engine, 5.5f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(-2.0f, DoOperator1(&engine, -2.3f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(-3.0f, DoOperator1(&engine, -3.8f, PSOP_CEILING));
+  EXPECT_FLOAT_EQ(-5.0f, DoOperator1(&engine, -5.5f, PSOP_CEILING));
+}
+
+TEST(CPDF_PSEngine, Floor) {
+  CPDF_PSEngine engine;
+
+  // Smallest positive float value.
+  float min_float = std::numeric_limits<float>::min();
+  // Largest positive float value.
+  float max_float = std::numeric_limits<float>::max();
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, min_float, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(max_float, DoOperator1(&engine, max_float, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(-1.0f, DoOperator1(&engine, -min_float, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(-max_float, DoOperator1(&engine, -max_float, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(5.0f, DoOperator1(&engine, 5.9f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(-4.0f, DoOperator1(&engine, -4.0000000001f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(-1.0f, DoOperator1(&engine, -0.9f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0000000001f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(2.0f, DoOperator1(&engine, 2.3f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(3.0f, DoOperator1(&engine, 3.8f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(5.0f, DoOperator1(&engine, 5.5f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(-3.0f, DoOperator1(&engine, -2.3f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(-4.0f, DoOperator1(&engine, -3.8f, PSOP_FLOOR));
+  EXPECT_FLOAT_EQ(-6.0f, DoOperator1(&engine, -5.5f, PSOP_FLOOR));
+}
+
+TEST(CPDF_PSEngine, Round) {
+  CPDF_PSEngine engine;
+
+  EXPECT_FLOAT_EQ(6.0f, DoOperator1(&engine, 5.9f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(-4.0f, DoOperator1(&engine, -4.0000000001f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(-1.0f, DoOperator1(&engine, -0.9f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0000000001f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0f, PSOP_ROUND));
+  // Smallest positive float value.
+  float min_float = std::numeric_limits<float>::min();
+  // Largest positive float value.
+  float max_float = std::numeric_limits<float>::max();
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, min_float, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, -min_float, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(max_float, DoOperator1(&engine, max_float, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(-max_float, DoOperator1(&engine, -max_float, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(2.0f, DoOperator1(&engine, 2.3f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(4.0f, DoOperator1(&engine, 3.8f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(6.0f, DoOperator1(&engine, 5.5f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(-2.0f, DoOperator1(&engine, -2.3f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(-4.0f, DoOperator1(&engine, -3.8f, PSOP_ROUND));
+  EXPECT_FLOAT_EQ(-5.0f, DoOperator1(&engine, -5.5f, PSOP_ROUND));
+}
+
+TEST(CPDF_PSEngine, Truncate) {
+  CPDF_PSEngine engine;
+
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, -0.9f, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0000000001f, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 0.0f, PSOP_TRUNCATE));
+  // Smallest positive float value.
+  float min_float = std::numeric_limits<float>::min();
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, min_float, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, -min_float, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(2.0f, DoOperator1(&engine, 2.3f, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(3.0f, DoOperator1(&engine, 3.8f, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(5.0f, DoOperator1(&engine, 5.5f, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(-2.0f, DoOperator1(&engine, -2.3f, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(-3.0f, DoOperator1(&engine, -3.8f, PSOP_TRUNCATE));
+  EXPECT_FLOAT_EQ(-5.0f, DoOperator1(&engine, -5.5f, PSOP_TRUNCATE));
+
+  // 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));
+  EXPECT_FLOAT_EQ(-max_int,
+                  DoOperator1(&engine, max_int * -1.5f, PSOP_TRUNCATE));
+}
diff --git a/core/fpdfapi/page/cpdf_psfunc.cpp b/core/fpdfapi/page/cpdf_psfunc.cpp
index 9f101eb..ffc5053 100644
--- a/core/fpdfapi/page/cpdf_psfunc.cpp
+++ b/core/fpdfapi/page/cpdf_psfunc.cpp
@@ -6,29 +6,28 @@
 
 #include "core/fpdfapi/page/cpdf_psfunc.h"
 
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "third_party/base/ptr_util.h"
 
 CPDF_PSFunc::CPDF_PSFunc() : CPDF_Function(Type::kType4PostScript) {}
 
 CPDF_PSFunc::~CPDF_PSFunc() {}
 
-bool CPDF_PSFunc::v_Init(CPDF_Object* pObj) {
+bool CPDF_PSFunc::v_Init(const CPDF_Object* pObj,
+                         std::set<const CPDF_Object*>* pVisited) {
   auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pObj->AsStream());
   pAcc->LoadAllDataFiltered();
-  return m_PS.Parse(reinterpret_cast<const char*>(pAcc->GetData()),
-                    pAcc->GetSize());
+  return m_PS.Parse(pAcc->GetSpan());
 }
 
-bool CPDF_PSFunc::v_Call(float* inputs, float* results) const {
-  CPDF_PSEngine& PS = const_cast<CPDF_PSEngine&>(m_PS);
-  PS.Reset();
+bool CPDF_PSFunc::v_Call(const float* inputs, float* results) const {
+  m_PS.Reset();
   for (uint32_t i = 0; i < m_nInputs; i++)
-    PS.Push(inputs[i]);
-  PS.Execute();
-  if (PS.GetStackSize() < m_nOutputs)
+    m_PS.Push(inputs[i]);
+  m_PS.Execute();
+  if (m_PS.GetStackSize() < m_nOutputs)
     return false;
   for (uint32_t i = 0; i < m_nOutputs; i++)
-    results[m_nOutputs - i - 1] = PS.Pop();
+    results[m_nOutputs - i - 1] = m_PS.Pop();
   return true;
 }
diff --git a/core/fpdfapi/page/cpdf_psfunc.h b/core/fpdfapi/page/cpdf_psfunc.h
index b8c18c9..b81c2e7 100644
--- a/core/fpdfapi/page/cpdf_psfunc.h
+++ b/core/fpdfapi/page/cpdf_psfunc.h
@@ -7,22 +7,25 @@
 #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"
 
 class CPDF_Object;
 
-class CPDF_PSFunc : public CPDF_Function {
+class CPDF_PSFunc final : public CPDF_Function {
  public:
   CPDF_PSFunc();
   ~CPDF_PSFunc() override;
 
   // CPDF_Function
-  bool v_Init(CPDF_Object* pObj) override;
-  bool v_Call(float* inputs, float* results) const override;
+  bool v_Init(const CPDF_Object* pObj,
+              std::set<const CPDF_Object*>* pVisited) override;
+  bool v_Call(const float* inputs, float* results) const override;
 
  private:
-  CPDF_PSEngine m_PS;
+  mutable CPDF_PSEngine m_PS;  // Pre-initialized scratch space for v_Call().
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PSFUNC_H_
diff --git a/core/fpdfapi/page/cpdf_sampledfunc.cpp b/core/fpdfapi/page/cpdf_sampledfunc.cpp
index 98e1fd0..c80e16c 100644
--- a/core/fpdfapi/page/cpdf_sampledfunc.cpp
+++ b/core/fpdfapi/page/cpdf_sampledfunc.cpp
@@ -7,9 +7,13 @@
 #include "core/fpdfapi/page/cpdf_sampledfunc.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/cfx_bitstream.h"
 #include "core/fxcrt/cfx_fixedbufgrow.h"
-#include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -36,61 +40,66 @@
 
 CPDF_SampledFunc::~CPDF_SampledFunc() {}
 
-bool CPDF_SampledFunc::v_Init(CPDF_Object* pObj) {
-  CPDF_Stream* pStream = pObj->AsStream();
+bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj,
+                              std::set<const CPDF_Object*>* pVisited) {
+  const CPDF_Stream* pStream = pObj->AsStream();
   if (!pStream)
     return false;
 
-  CPDF_Dictionary* pDict = pStream->GetDict();
-  CPDF_Array* pSize = pDict->GetArrayFor("Size");
-  CPDF_Array* pEncode = pDict->GetArrayFor("Encode");
-  CPDF_Array* pDecode = pDict->GetArrayFor("Decode");
+  const CPDF_Dictionary* pDict = pStream->GetDict();
+  const CPDF_Array* pSize = pDict->GetArrayFor("Size");
+  if (!pSize || pSize->IsEmpty())
+    return false;
+
   m_nBitsPerSample = pDict->GetIntegerFor("BitsPerSample");
   if (!IsValidBitsPerSample(m_nBitsPerSample))
     return false;
 
+  FX_SAFE_UINT32 nTotalSampleBits = m_nBitsPerSample;
+  nTotalSampleBits *= m_nOutputs;
+  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);
+    if (size <= 0)
+      return false;
+
+    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);
+    } else {
+      m_EncodeInfo[i].encode_min = 0;
+      m_EncodeInfo[i].encode_max =
+          m_EncodeInfo[i].sizes == 1 ? 1 : m_EncodeInfo[i].sizes - 1;
+    }
+  }
+  FX_SAFE_UINT32 nTotalSampleBytes = (nTotalSampleBits + 7) / 8;
+  if (!nTotalSampleBytes.IsValid() || nTotalSampleBytes.ValueOrDie() == 0)
+    return false;
+
   m_SampleMax = 0xffffffff >> (32 - m_nBitsPerSample);
   m_pSampleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
   m_pSampleStream->LoadAllDataFiltered();
-  FX_SAFE_UINT32 nTotalSampleBits = 1;
-  m_EncodeInfo.resize(m_nInputs);
-  for (uint32_t i = 0; i < m_nInputs; i++) {
-    m_EncodeInfo[i].sizes = pSize ? pSize->GetIntegerAt(i) : 0;
-    if (!pSize && i == 0)
-      m_EncodeInfo[i].sizes = pDict->GetIntegerFor("Size");
-    nTotalSampleBits *= m_EncodeInfo[i].sizes;
-    if (pEncode) {
-      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 =
-          m_EncodeInfo[i].sizes == 1 ? 1 : (float)m_EncodeInfo[i].sizes - 1;
-    }
-  }
-  nTotalSampleBits *= m_nBitsPerSample;
-  nTotalSampleBits *= m_nOutputs;
-  FX_SAFE_UINT32 nTotalSampleBytes = nTotalSampleBits;
-  nTotalSampleBytes += 7;
-  nTotalSampleBytes /= 8;
-  if (!nTotalSampleBytes.IsValid() || nTotalSampleBytes.ValueOrDie() == 0 ||
-      nTotalSampleBytes.ValueOrDie() > m_pSampleStream->GetSize()) {
+  if (nTotalSampleBytes.ValueOrDie() > m_pSampleStream->GetSize())
     return false;
-  }
+
+  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->GetFloatAt(2 * i);
-      m_DecodeInfo[i].decode_max = pDecode->GetFloatAt(2 * i + 1);
+      m_DecodeInfo[i].decode_min = pDecode->GetNumberAt(2 * i);
+      m_DecodeInfo[i].decode_max = pDecode->GetNumberAt(2 * i + 1);
     } else {
-      m_DecodeInfo[i].decode_min = m_pRanges[i * 2];
-      m_DecodeInfo[i].decode_max = m_pRanges[i * 2 + 1];
+      m_DecodeInfo[i].decode_min = m_Ranges[i * 2];
+      m_DecodeInfo[i].decode_max = m_Ranges[i * 2 + 1];
     }
   }
   return true;
 }
 
-bool CPDF_SampledFunc::v_Call(float* inputs, float* results) const {
+bool CPDF_SampledFunc::v_Call(const float* inputs, float* results) const {
   int pos = 0;
   CFX_FixedBufGrow<float, 16> encoded_input_buf(m_nInputs);
   float* encoded_input = encoded_input_buf;
@@ -103,7 +112,7 @@
     else
       blocksize[i] = blocksize[i - 1] * m_EncodeInfo[i - 1].sizes;
     encoded_input[i] =
-        Interpolate(inputs[i], m_pDomains[i * 2], m_pDomains[i * 2 + 1],
+        Interpolate(inputs[i], m_Domains[i * 2], m_Domains[i * 2 + 1],
                     m_EncodeInfo[i].encode_min, m_EncodeInfo[i].encode_max);
     index[i] = pdfium::clamp(static_cast<uint32_t>(encoded_input[i]), 0U,
                              m_EncodeInfo[i].sizes - 1);
@@ -114,45 +123,59 @@
   if (!bits_to_output.IsValid())
     return false;
 
-  FX_SAFE_INT32 bitpos = pos;
-  bitpos *= bits_to_output.ValueOrDie();
-  if (!bitpos.IsValid())
+  int bits_to_skip;
+  {
+    FX_SAFE_INT32 bitpos = pos;
+    bitpos *= bits_to_output.ValueOrDie();
+    bits_to_skip = bitpos.ValueOrDefault(-1);
+    if (bits_to_skip < 0)
+      return false;
+
+    FX_SAFE_INT32 range_check = bitpos;
+    range_check += bits_to_output.ValueOrDie();
+    if (!range_check.IsValid())
+      return false;
+  }
+
+  pdfium::span<const uint8_t> pSampleData = m_pSampleStream->GetSpan();
+  if (pSampleData.empty())
     return false;
 
-  FX_SAFE_INT32 range_check = bitpos;
-  range_check += bits_to_output.ValueOrDie();
-  if (!range_check.IsValid())
-    return false;
-
-  const uint8_t* pSampleData = m_pSampleStream->GetData();
-  if (!pSampleData)
-    return false;
-
-  for (uint32_t j = 0; j < m_nOutputs; j++, bitpos += m_nBitsPerSample) {
-    uint32_t sample =
-        GetBits32(pSampleData, bitpos.ValueOrDie(), m_nBitsPerSample);
-    float encoded = (float)sample;
-    for (uint32_t i = 0; i < m_nInputs; i++) {
-      if (index[i] == m_EncodeInfo[i].sizes - 1) {
-        if (index[i] == 0)
-          encoded = encoded_input[i] * (float)sample;
+  CFX_BitStream bitstream(pSampleData);
+  bitstream.SkipBits(bits_to_skip);
+  for (uint32_t i = 0; i < m_nOutputs; ++i) {
+    uint32_t sample = bitstream.GetBits(m_nBitsPerSample);
+    float encoded = sample;
+    for (uint32_t j = 0; j < m_nInputs; ++j) {
+      if (index[j] == m_EncodeInfo[j].sizes - 1) {
+        if (index[j] == 0)
+          encoded = encoded_input[j] * sample;
       } else {
-        FX_SAFE_INT32 bitpos2 = blocksize[i];
+        FX_SAFE_INT32 bitpos2 = blocksize[j];
         bitpos2 += pos;
         bitpos2 *= m_nOutputs;
-        bitpos2 += j;
+        bitpos2 += i;
         bitpos2 *= m_nBitsPerSample;
-        if (!bitpos2.IsValid())
+        int bits_to_skip2 = bitpos2.ValueOrDefault(-1);
+        if (bits_to_skip2 < 0)
           return false;
-        uint32_t sample1 =
-            GetBits32(pSampleData, bitpos2.ValueOrDie(), m_nBitsPerSample);
-        encoded +=
-            (encoded_input[i] - index[i]) * ((float)sample1 - (float)sample);
+
+        CFX_BitStream bitstream2(pSampleData);
+        bitstream2.SkipBits(bits_to_skip2);
+        float sample2 =
+            static_cast<float>(bitstream2.GetBits(m_nBitsPerSample));
+        encoded += (encoded_input[j] - index[j]) * (sample2 - sample);
       }
     }
-    results[j] =
-        Interpolate(encoded, 0, (float)m_SampleMax, m_DecodeInfo[j].decode_min,
-                    m_DecodeInfo[j].decode_max);
+    results[i] =
+        Interpolate(encoded, 0, m_SampleMax, m_DecodeInfo[i].decode_min,
+                    m_DecodeInfo[i].decode_max);
   }
   return true;
 }
+
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+RetainPtr<CPDF_StreamAcc> CPDF_SampledFunc::GetSampleStream() const {
+  return m_pSampleStream;
+}
+#endif
diff --git a/core/fpdfapi/page/cpdf_sampledfunc.h b/core/fpdfapi/page/cpdf_sampledfunc.h
index d6dfed5..520b692 100644
--- a/core/fpdfapi/page/cpdf_sampledfunc.h
+++ b/core/fpdfapi/page/cpdf_sampledfunc.h
@@ -7,13 +7,15 @@
 #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"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CPDF_SampledFunc : public CPDF_Function {
+class CPDF_StreamAcc;
+
+class CPDF_SampledFunc final : public CPDF_Function {
  public:
   struct SampleEncodeInfo {
     float encode_max;
@@ -30,14 +32,18 @@
   ~CPDF_SampledFunc() override;
 
   // CPDF_Function
-  bool v_Init(CPDF_Object* pObj) override;
-  bool v_Call(float* inputs, float* results) const override;
+  bool v_Init(const CPDF_Object* pObj,
+              std::set<const CPDF_Object*>* pVisited) override;
+  bool v_Call(const float* inputs, float* results) const override;
 
   const std::vector<SampleEncodeInfo>& GetEncodeInfo() const {
     return m_EncodeInfo;
   }
   uint32_t GetBitsPerSample() const { return m_nBitsPerSample; }
-  RetainPtr<CPDF_StreamAcc> GetSampleStream() const { return m_pSampleStream; }
+
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+  RetainPtr<CPDF_StreamAcc> GetSampleStream() const;
+#endif
 
  private:
   std::vector<SampleEncodeInfo> m_EncodeInfo;
diff --git a/core/fpdfapi/page/cpdf_shadingobject.cpp b/core/fpdfapi/page/cpdf_shadingobject.cpp
index 1b16ac4..bdaceaa 100644
--- a/core/fpdfapi/page/cpdf_shadingobject.cpp
+++ b/core/fpdfapi/page/cpdf_shadingobject.cpp
@@ -9,9 +9,10 @@
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 
-CPDF_ShadingObject::CPDF_ShadingObject(CPDF_ShadingPattern* pattern,
+CPDF_ShadingObject::CPDF_ShadingObject(int32_t content_stream,
+                                       CPDF_ShadingPattern* pattern,
                                        const CFX_Matrix& matrix)
-    : m_pShading(pattern), m_Matrix(matrix) {}
+    : CPDF_PageObject(content_stream), m_pShading(pattern), m_Matrix(matrix) {}
 
 CPDF_ShadingObject::~CPDF_ShadingObject() {}
 
@@ -26,10 +27,10 @@
   m_Matrix.Concat(matrix);
   if (m_ClipPath.HasRef()) {
     CalcBoundingBox();
-  } else {
-    std::tie(m_Left, m_Right, m_Top, m_Bottom) =
-        matrix.TransformRect(m_Left, m_Right, m_Top, m_Bottom);
+    return;
   }
+
+  SetRect(matrix.TransformRect(GetRect()));
 }
 
 bool CPDF_ShadingObject::IsShading() const {
@@ -47,9 +48,5 @@
 void CPDF_ShadingObject::CalcBoundingBox() {
   if (!m_ClipPath.HasRef())
     return;
-  CFX_FloatRect rect = m_ClipPath.GetClipBox();
-  m_Left = rect.left;
-  m_Bottom = rect.bottom;
-  m_Right = rect.right;
-  m_Top = rect.top;
+  SetRect(m_ClipPath.GetClipBox());
 }
diff --git a/core/fpdfapi/page/cpdf_shadingobject.h b/core/fpdfapi/page/cpdf_shadingobject.h
index 80e062c..072a025 100644
--- a/core/fpdfapi/page/cpdf_shadingobject.h
+++ b/core/fpdfapi/page/cpdf_shadingobject.h
@@ -9,13 +9,15 @@
 
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_ShadingPattern;
 
-class CPDF_ShadingObject : public CPDF_PageObject {
+class CPDF_ShadingObject final : public CPDF_PageObject {
  public:
-  CPDF_ShadingObject(CPDF_ShadingPattern* pattern, const CFX_Matrix& matrix);
+  CPDF_ShadingObject(int32_t content_stream,
+                     CPDF_ShadingPattern* pattern,
+                     const CFX_Matrix& matrix);
   ~CPDF_ShadingObject() override;
 
   // CPDF_PageObject:
@@ -31,7 +33,7 @@
   const CFX_Matrix& matrix() const { return m_Matrix; }
 
  private:
-  UnownedPtr<CPDF_ShadingPattern> m_pShading;
+  RetainPtr<CPDF_ShadingPattern> m_pShading;
   CFX_Matrix m_Matrix;
 };
 
diff --git a/core/fpdfapi/page/cpdf_shadingpattern.cpp b/core/fpdfapi/page/cpdf_shadingpattern.cpp
index f1e75b7..69696ac 100644
--- a/core/fpdfapi/page/cpdf_shadingpattern.cpp
+++ b/core/fpdfapi/page/cpdf_shadingpattern.cpp
@@ -15,12 +15,12 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/fx_safe_types.h"
 
 namespace {
 
 ShadingType ToShadingType(int type) {
-  return (type > static_cast<int>(kInvalidShading) &&
-          type < static_cast<int>(kMaxShading))
+  return (type > kInvalidShading && type < kMaxShading)
              ? static_cast<ShadingType>(type)
              : kInvalidShading;
 }
@@ -31,33 +31,13 @@
                                          CPDF_Object* pPatternObj,
                                          bool bShading,
                                          const CFX_Matrix& parentMatrix)
-    : CPDF_Pattern(pDoc, bShading ? nullptr : pPatternObj, parentMatrix),
-      m_ShadingType(kInvalidShading),
-      m_bShadingObj(bShading),
-      m_pShadingObj(pPatternObj),
-      m_pCS(nullptr),
-      m_pCountedCS(nullptr) {
-  assert(document());
-  if (!bShading) {
-    m_pShadingObj = pattern_obj()->GetDict()->GetDirectObjectFor("Shading");
+    : CPDF_Pattern(pDoc, pPatternObj, parentMatrix), m_bShading(bShading) {
+  ASSERT(document());
+  if (!bShading)
     SetPatternToFormMatrix();
-  }
 }
 
-CPDF_ShadingPattern::~CPDF_ShadingPattern() {
-  CPDF_ColorSpace* pCountedCS = m_pCountedCS ? m_pCountedCS->get() : nullptr;
-  if (pCountedCS) {
-    auto* pPageData = document()->GetPageData();
-    if (pPageData) {
-      m_pCS.Release();  // Give up unowned reference first.
-      pPageData->ReleaseColorSpace(pCountedCS->GetArray());
-    }
-  }
-}
-
-CPDF_TilingPattern* CPDF_ShadingPattern::AsTilingPattern() {
-  return nullptr;
-}
+CPDF_ShadingPattern::~CPDF_ShadingPattern() = default;
 
 CPDF_ShadingPattern* CPDF_ShadingPattern::AsShadingPattern() {
   return this;
@@ -67,27 +47,28 @@
   if (m_ShadingType != kInvalidShading)
     return true;
 
-  CPDF_Dictionary* pShadingDict =
-      m_pShadingObj ? m_pShadingObj->GetDict() : nullptr;
+  const CPDF_Object* pShadingObj = GetShadingObject();
+  const CPDF_Dictionary* pShadingDict =
+      pShadingObj ? pShadingObj->GetDict() : nullptr;
   if (!pShadingDict)
     return false;
 
   m_pFunctions.clear();
-  CPDF_Object* pFunc = pShadingDict->GetDirectObjectFor("Function");
+  const CPDF_Object* pFunc = pShadingDict->GetDirectObjectFor("Function");
   if (pFunc) {
-    if (CPDF_Array* pArray = pFunc->AsArray()) {
-      m_pFunctions.resize(std::min<size_t>(pArray->GetCount(), 4));
+    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)
         m_pFunctions[i] = CPDF_Function::Load(pArray->GetDirectObjectAt(i));
     } else {
       m_pFunctions.push_back(CPDF_Function::Load(pFunc));
     }
   }
-  CPDF_Object* pCSObj = pShadingDict->GetDirectObjectFor("ColorSpace");
+  const CPDF_Object* pCSObj = pShadingDict->GetDirectObjectFor("ColorSpace");
   if (!pCSObj)
     return false;
 
-  CPDF_DocPageData* pDocPageData = document()->GetPageData();
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(document());
   m_pCS = pDocPageData->GetColorSpace(pCSObj, nullptr);
 
   // The color space is required and cannot be a Pattern space, according to the
@@ -95,19 +76,21 @@
   if (!m_pCS || m_pCS->GetFamily() == PDFCS_PATTERN)
     return false;
 
-  m_pCountedCS = pDocPageData->FindColorSpacePtr(m_pCS->GetArray());
-
   m_ShadingType = ToShadingType(pShadingDict->GetIntegerFor("ShadingType"));
-
   return Validate();
 }
 
+const CPDF_Object* CPDF_ShadingPattern::GetShadingObject() const {
+  return m_bShading ? pattern_obj()
+                    : pattern_obj()->GetDict()->GetDirectObjectFor("Shading");
+}
+
 bool CPDF_ShadingPattern::Validate() const {
   if (m_ShadingType == kInvalidShading)
     return false;
 
   // We expect to have a stream if our shading type is a mesh.
-  if (IsMeshShading() && !ToStream(m_pShadingObj.Get()))
+  if (IsMeshShading() && !ToStream(GetShadingObject()))
     return false;
 
   // Validate color space
@@ -137,39 +120,29 @@
   switch (m_ShadingType) {
     case kFunctionBasedShading: {
       // Either one 2-to-N function or N 2-to-1 functions.
-      if (!ValidateFunctions(1, 2, nNumColorSpaceComponents) &&
-          !ValidateFunctions(nNumColorSpaceComponents, 2, 1)) {
-        return false;
-      }
-      break;
+      return ValidateFunctions(1, 2, nNumColorSpaceComponents) ||
+             ValidateFunctions(nNumColorSpaceComponents, 2, 1);
     }
     case kAxialShading:
     case kRadialShading: {
       // Either one 1-to-N function or N 1-to-1 functions.
-      if (!ValidateFunctions(1, 1, nNumColorSpaceComponents) &&
-          !ValidateFunctions(nNumColorSpaceComponents, 1, 1)) {
-        return false;
-      }
-      break;
+      return ValidateFunctions(1, 1, nNumColorSpaceComponents) ||
+             ValidateFunctions(nNumColorSpaceComponents, 1, 1);
     }
     case kFreeFormGouraudTriangleMeshShading:
     case kLatticeFormGouraudTriangleMeshShading:
     case kCoonsPatchMeshShading:
     case kTensorProductPatchMeshShading: {
       // Either no function, one 1-to-N function, or N 1-to-1 functions.
-      if (!m_pFunctions.empty() &&
-          !ValidateFunctions(1, 1, nNumColorSpaceComponents) &&
-          !ValidateFunctions(nNumColorSpaceComponents, 1, 1)) {
-        return false;
-      }
+      return m_pFunctions.empty() ||
+             ValidateFunctions(1, 1, nNumColorSpaceComponents) ||
+             ValidateFunctions(nNumColorSpaceComponents, 1, 1);
+    }
+    default:
       break;
-    }
-    default: {
-      NOTREACHED();
-      return false;
-    }
   }
-  return true;
+  NOTREACHED();
+  return false;
 }
 
 bool CPDF_ShadingPattern::ValidateFunctions(
@@ -179,7 +152,7 @@
   if (m_pFunctions.size() != nExpectedNumFunctions)
     return false;
 
-  pdfium::base::CheckedNumeric<uint32_t> nTotalOutputs = 0;
+  FX_SAFE_UINT32 nTotalOutputs = 0;
   for (const auto& function : m_pFunctions) {
     if (!function)
       return false;
diff --git a/core/fpdfapi/page/cpdf_shadingpattern.h b/core/fpdfapi/page/cpdf_shadingpattern.h
index 17b6e4b..392aa27 100644
--- a/core/fpdfapi/page/cpdf_shadingpattern.h
+++ b/core/fpdfapi/page/cpdf_shadingpattern.h
@@ -13,8 +13,11 @@
 #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.
 enum ShadingType {
   kInvalidShading = 0,
   kFunctionBasedShading = 1,
@@ -33,15 +36,14 @@
 class CPDF_Function;
 class CPDF_Object;
 
-class CPDF_ShadingPattern : public CPDF_Pattern {
+class CPDF_ShadingPattern final : public CPDF_Pattern {
  public:
-  CPDF_ShadingPattern(CPDF_Document* pDoc,
-                      CPDF_Object* pPatternObj,
-                      bool bShading,
-                      const CFX_Matrix& parentMatrix);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_ShadingPattern() override;
 
-  CPDF_TilingPattern* AsTilingPattern() override;
+  // CPDF_Pattern:
   CPDF_ShadingPattern* AsShadingPattern() override;
 
   bool IsMeshShading() const {
@@ -53,29 +55,30 @@
   bool Load();
 
   ShadingType GetShadingType() const { return m_ShadingType; }
-  bool IsShadingObject() const { return m_bShadingObj; }
-  CPDF_Object* GetShadingObject() const { return m_pShadingObj.Get(); }
-  CPDF_ColorSpace* GetCS() const { return m_pCS.Get(); }
+  bool IsShadingObject() const { return m_bShading; }
+  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;
   }
 
  private:
+  CPDF_ShadingPattern(CPDF_Document* pDoc,
+                      CPDF_Object* pPatternObj,
+                      bool bShading,
+                      const CFX_Matrix& parentMatrix);
+  CPDF_ShadingPattern(const CPDF_ShadingPattern&) = delete;
+  CPDF_ShadingPattern& operator=(const CPDF_ShadingPattern&) = delete;
+
   // Constraints in PDF 1.7 spec, 4.6.3 Shading Patterns, pages 308-331.
   bool Validate() const;
   bool ValidateFunctions(uint32_t nExpectedNumFunctions,
                          uint32_t nExpectedNumInputs,
                          uint32_t nExpectedNumOutputs) const;
 
-  ShadingType m_ShadingType;
-  bool m_bShadingObj;
-  UnownedPtr<CPDF_Object> m_pShadingObj;
-
-  // Still keep |m_pCS| as some CPDF_ColorSpace (name object) are not managed
-  // as counted objects. Refer to CPDF_DocPageData::GetColorSpace.
-  UnownedPtr<CPDF_ColorSpace> m_pCS;
-
-  UnownedPtr<CPDF_CountedColorSpace> m_pCountedCS;
+  ShadingType m_ShadingType = kInvalidShading;
+  const bool m_bShading;
+  RetainPtr<CPDF_ColorSpace> m_pCS;
   std::vector<std::unique_ptr<CPDF_Function>> m_pFunctions;
 };
 
diff --git a/core/fpdfapi/page/cpdf_stitchfunc.cpp b/core/fpdfapi/page/cpdf_stitchfunc.cpp
index 336c74b..1ec48ac 100644
--- a/core/fpdfapi/page/cpdf_stitchfunc.cpp
+++ b/core/fpdfapi/page/cpdf_stitchfunc.cpp
@@ -10,82 +10,112 @@
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/fx_safe_types.h"
 
-CPDF_StitchFunc::CPDF_StitchFunc()
-    : CPDF_Function(Type::kType3Stitching),
-      m_pBounds(nullptr),
-      m_pEncode(nullptr) {}
+namespace {
 
-CPDF_StitchFunc::~CPDF_StitchFunc() {
-  FX_Free(m_pBounds);
-  FX_Free(m_pEncode);
-}
+constexpr uint32_t kRequiredNumInputs = 1;
 
-bool CPDF_StitchFunc::v_Init(CPDF_Object* pObj) {
-  CPDF_Dictionary* pDict = pObj->GetDict();
-  if (!pDict) {
+}  // namespace
+
+CPDF_StitchFunc::CPDF_StitchFunc() : CPDF_Function(Type::kType3Stitching) {}
+
+CPDF_StitchFunc::~CPDF_StitchFunc() {}
+
+bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj,
+                             std::set<const CPDF_Object*>* pVisited) {
+  if (m_nInputs != kRequiredNumInputs)
     return false;
-  }
-  if (m_nInputs != kRequiredNumInputs) {
+
+  const CPDF_Dictionary* pDict = pObj->GetDict();
+  if (!pDict)
     return false;
-  }
-  CPDF_Array* pArray = pDict->GetArrayFor("Functions");
-  if (!pArray) {
+
+  const CPDF_Array* pFunctionsArray = pDict->GetArrayFor("Functions");
+  if (!pFunctionsArray)
     return false;
-  }
-  uint32_t nSubs = pArray->GetCount();
+
+  const CPDF_Array* pBoundsArray = pDict->GetArrayFor("Bounds");
+  if (!pBoundsArray)
+    return false;
+
+  const CPDF_Array* pEncodeArray = pDict->GetArrayFor("Encode");
+  if (!pEncodeArray)
+    return false;
+
+  const uint32_t nSubs = pFunctionsArray->size();
   if (nSubs == 0)
     return false;
-  m_nOutputs = 0;
-  for (uint32_t i = 0; i < nSubs; i++) {
-    CPDF_Object* pSub = pArray->GetDirectObjectAt(i);
-    if (pSub == pObj)
+
+  // Check array sizes. The checks are slightly relaxed to allow the "Bounds"
+  // and "Encode" arrays to have more than the required number of elements.
+  {
+    if (pBoundsArray->size() < nSubs - 1)
       return false;
-    std::unique_ptr<CPDF_Function> pFunc(CPDF_Function::Load(pSub));
-    if (!pFunc)
+
+    FX_SAFE_UINT32 nExpectedEncodeSize = nSubs;
+    nExpectedEncodeSize *= 2;
+    if (!nExpectedEncodeSize.IsValid())
       return false;
-    // Check that the input dimensionality is 1, and that all output
-    // dimensionalities are the same.
-    if (pFunc->CountInputs() != kRequiredNumInputs)
+
+    if (pEncodeArray->size() < nExpectedEncodeSize.ValueOrDie())
       return false;
-    if (pFunc->CountOutputs() != m_nOutputs) {
-      if (m_nOutputs)
+  }
+
+  // Check sub-functions.
+  {
+    Optional<uint32_t> nOutputs;
+    for (uint32_t i = 0; i < nSubs; ++i) {
+      const CPDF_Object* pSub = pFunctionsArray->GetDirectObjectAt(i);
+      if (pSub == pObj)
         return false;
 
-      m_nOutputs = pFunc->CountOutputs();
+      std::unique_ptr<CPDF_Function> pFunc(CPDF_Function::Load(pSub, pVisited));
+      if (!pFunc)
+        return false;
+
+      // Check that the input dimensionality is 1, and that all output
+      // dimensionalities are the same.
+      if (pFunc->CountInputs() != kRequiredNumInputs)
+        return false;
+
+      uint32_t nFuncOutputs = pFunc->CountOutputs();
+      if (nFuncOutputs == 0)
+        return false;
+
+      if (nOutputs) {
+        if (nFuncOutputs != *nOutputs)
+          return false;
+      } else {
+        nOutputs = nFuncOutputs;
+      }
+
+      m_pSubFunctions.push_back(std::move(pFunc));
     }
-
-    m_pSubFunctions.push_back(std::move(pFunc));
+    m_nOutputs = *nOutputs;
   }
-  m_pBounds = FX_Alloc(float, nSubs + 1);
-  m_pBounds[0] = m_pDomains[0];
-  pArray = pDict->GetArrayFor("Bounds");
-  if (!pArray)
-    return false;
-  for (uint32_t i = 0; i < nSubs - 1; i++)
-    m_pBounds[i + 1] = pArray->GetFloatAt(i);
-  m_pBounds[nSubs] = m_pDomains[1];
-  m_pEncode = FX_Alloc2D(float, nSubs, 2);
-  pArray = pDict->GetArrayFor("Encode");
-  if (!pArray)
-    return false;
 
-  for (uint32_t i = 0; i < nSubs * 2; i++)
-    m_pEncode[i] = pArray->GetFloatAt(i);
+  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(m_Domains[1]);
+
+  m_encode = ReadArrayElementsToVector(pEncodeArray, nSubs * 2);
   return true;
 }
 
-bool CPDF_StitchFunc::v_Call(float* inputs, float* outputs) const {
+bool CPDF_StitchFunc::v_Call(const float* inputs, float* results) const {
   float input = inputs[0];
   size_t i;
   for (i = 0; i < m_pSubFunctions.size() - 1; i++) {
-    if (input < m_pBounds[i + 1])
+    if (input < m_bounds[i + 1])
       break;
   }
-  input = Interpolate(input, m_pBounds[i], m_pBounds[i + 1], m_pEncode[i * 2],
-                      m_pEncode[i * 2 + 1]);
+  input = Interpolate(input, m_bounds[i], m_bounds[i + 1], m_encode[i * 2],
+                      m_encode[i * 2 + 1]);
   int nresults;
-  m_pSubFunctions[i]->Call(&input, kRequiredNumInputs, outputs, &nresults);
-  return true;
+  return m_pSubFunctions[i]->Call(&input, kRequiredNumInputs, results,
+                                  &nresults);
 }
diff --git a/core/fpdfapi/page/cpdf_stitchfunc.h b/core/fpdfapi/page/cpdf_stitchfunc.h
index 69e5e41..761c9ba 100644
--- a/core/fpdfapi/page/cpdf_stitchfunc.h
+++ b/core/fpdfapi/page/cpdf_stitchfunc.h
@@ -8,30 +8,31 @@
 #define CORE_FPDFAPI_PAGE_CPDF_STITCHFUNC_H_
 
 #include <memory>
+#include <set>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_function.h"
 
-class CPDF_StitchFunc : public CPDF_Function {
+class CPDF_StitchFunc final : public CPDF_Function {
  public:
   CPDF_StitchFunc();
   ~CPDF_StitchFunc() override;
 
   // CPDF_Function
-  bool v_Init(CPDF_Object* pObj) override;
-  bool v_Call(float* inputs, float* results) const override;
+  bool v_Init(const CPDF_Object* pObj,
+              std::set<const CPDF_Object*>* pVisited) override;
+  bool v_Call(const float* inputs, float* results) const override;
 
   const std::vector<std::unique_ptr<CPDF_Function>>& GetSubFunctions() const {
     return m_pSubFunctions;
   }
-  float GetBound(size_t i) const { return m_pBounds[i]; }
+  float GetBound(size_t i) const { return m_bounds[i]; }
+  float GetEncode(size_t i) const { return m_encode[i]; }
 
  private:
   std::vector<std::unique_ptr<CPDF_Function>> m_pSubFunctions;
-  float* m_pBounds;
-  float* m_pEncode;
-
-  static const uint32_t kRequiredNumInputs = 1;
+  std::vector<float> m_bounds;
+  std::vector<float> m_encode;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_STITCHFUNC_H_
diff --git a/core/fpdfapi/page/cpdf_stitchfunc_embeddertest.cpp b/core/fpdfapi/page/cpdf_stitchfunc_embeddertest.cpp
deleted file mode 100644
index 6a1b87b..0000000
--- a/core/fpdfapi/page/cpdf_stitchfunc_embeddertest.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class FPDFPageFuncEmbeddertest : public EmbedderTest {};
-
-TEST_F(FPDFPageFuncEmbeddertest, Bug_551460) {
-  // Should not crash under ASan.
-  // Tests that the number of inputs is not simply calculated from the domain
-  // and trusted. The number of inputs has to be 1.
-  EXPECT_TRUE(OpenDocument("bug_551460.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
-  FPDFBitmap_Destroy(bitmap);
-  UnloadPage(page);
-}
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index 4e3857b..a18bead 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_streamcontentparser.h"
 
+#include <algorithm>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -32,11 +33,13 @@
 #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/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/render_defines.h"
 #include "third_party/base/logging.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 #include "third_party/base/stl_util.h"
 
 namespace {
@@ -59,7 +62,7 @@
 
 class CPDF_StreamParserAutoClearer {
  public:
-  CPDF_StreamParserAutoClearer(CPDF_StreamParser** scoped_variable,
+  CPDF_StreamParserAutoClearer(UnownedPtr<CPDF_StreamParser>* scoped_variable,
                                CPDF_StreamParser* new_parser)
       : scoped_variable_(scoped_variable) {
     *scoped_variable_ = new_parser;
@@ -67,14 +70,14 @@
   ~CPDF_StreamParserAutoClearer() { *scoped_variable_ = nullptr; }
 
  private:
-  CPDF_StreamParser** scoped_variable_;
+  UnownedPtr<CPDF_StreamParser>* scoped_variable_;
 };
 
 CFX_FloatRect GetShadingBBox(CPDF_ShadingPattern* pShading,
                              const CFX_Matrix& matrix) {
   ShadingType type = pShading->GetShadingType();
-  CPDF_Stream* pStream = ToStream(pShading->GetShadingObject());
-  CPDF_ColorSpace* pCS = pShading->GetCS();
+  const CPDF_Stream* pStream = ToStream(pShading->GetShadingObject());
+  RetainPtr<CPDF_ColorSpace> pCS = pShading->GetCS();
   if (!pStream || !pCS)
     return CFX_FloatRect();
 
@@ -139,13 +142,13 @@
   const char* full_name;
 };
 
-const AbbrPair InlineKeyAbbr[] = {
+const AbbrPair kInlineKeyAbbr[] = {
     {"BPC", "BitsPerComponent"}, {"CS", "ColorSpace"}, {"D", "Decode"},
     {"DP", "DecodeParms"},       {"F", "Filter"},      {"H", "Height"},
     {"IM", "ImageMask"},         {"I", "Interpolate"}, {"W", "Width"},
 };
 
-const AbbrPair InlineValueAbbr[] = {
+const AbbrPair kInlineValueAbbr[] = {
     {"G", "DeviceGray"},       {"RGB", "DeviceRGB"},
     {"CMYK", "DeviceCMYK"},    {"I", "Indexed"},
     {"AHx", "ASCIIHexDecode"}, {"A85", "ASCII85Decode"},
@@ -162,76 +165,84 @@
 
 ByteStringView FindFullName(const AbbrPair* table,
                             size_t count,
-                            const ByteStringView& abbr) {
+                            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();
 }
 
-void ReplaceAbbr(CPDF_Object* pObj) {
-  switch (pObj->GetType()) {
-    case CPDF_Object::DICTIONARY: {
-      CPDF_Dictionary* pDict = pObj->AsDictionary();
-      std::vector<AbbrReplacementOp> replacements;
-      for (const auto& it : *pDict) {
-        ByteString key = it.first;
-        CPDF_Object* value = it.second.get();
-        ByteStringView fullname = FindFullName(
-            InlineKeyAbbr, FX_ArraySize(InlineKeyAbbr), key.AsStringView());
+void ReplaceAbbr(CPDF_Object* pObj);
+
+void ReplaceAbbrInDictionary(CPDF_Dictionary* pDict) {
+  std::vector<AbbrReplacementOp> replacements;
+  {
+    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());
+      if (!fullname.IsEmpty()) {
+        AbbrReplacementOp op;
+        op.is_replace_key = true;
+        op.key = std::move(key);
+        op.replacement = fullname;
+        replacements.push_back(op);
+        key = fullname;
+      }
+
+      if (value->IsName()) {
+        ByteString name = value->GetString();
+        fullname =
+            FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr),
+                         name.AsStringView());
         if (!fullname.IsEmpty()) {
           AbbrReplacementOp op;
-          op.is_replace_key = true;
+          op.is_replace_key = false;
           op.key = key;
           op.replacement = fullname;
           replacements.push_back(op);
-          key = fullname;
         }
-
-        if (value->IsName()) {
-          ByteString name = value->GetString();
-          fullname =
-              FindFullName(InlineValueAbbr, FX_ArraySize(InlineValueAbbr),
-                           name.AsStringView());
-          if (!fullname.IsEmpty()) {
-            AbbrReplacementOp op;
-            op.is_replace_key = false;
-            op.key = key;
-            op.replacement = fullname;
-            replacements.push_back(op);
-          }
-        } else {
-          ReplaceAbbr(value);
-        }
+      } else {
+        ReplaceAbbr(value);
       }
-      for (const auto& op : replacements) {
-        if (op.is_replace_key)
-          pDict->ReplaceKey(op.key, ByteString(op.replacement));
-        else
-          pDict->SetNewFor<CPDF_Name>(op.key, ByteString(op.replacement));
-      }
-      break;
     }
-    case CPDF_Object::ARRAY: {
-      CPDF_Array* pArray = pObj->AsArray();
-      for (size_t i = 0; i < pArray->GetCount(); i++) {
-        CPDF_Object* pElement = pArray->GetObjectAt(i);
-        if (pElement->IsName()) {
-          ByteString name = pElement->GetString();
-          ByteStringView fullname =
-              FindFullName(InlineValueAbbr, FX_ArraySize(InlineValueAbbr),
-                           name.AsStringView());
-          if (!fullname.IsEmpty())
-            pArray->SetNewAt<CPDF_Name>(i, ByteString(fullname));
-        } else {
-          ReplaceAbbr(pElement);
-        }
-      }
-      break;
-    }
-    default:
-      break;
   }
+  for (const auto& op : replacements) {
+    if (op.is_replace_key)
+      pDict->ReplaceKey(op.key, ByteString(op.replacement));
+    else
+      pDict->SetNewFor<CPDF_Name>(op.key, ByteString(op.replacement));
+  }
+}
+
+void ReplaceAbbrInArray(CPDF_Array* pArray) {
+  for (size_t i = 0; i < pArray->size(); ++i) {
+    CPDF_Object* pElement = pArray->GetObjectAt(i);
+    if (pElement->IsName()) {
+      ByteString name = pElement->GetString();
+      ByteStringView fullname =
+          FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr),
+                       name.AsStringView());
+      if (!fullname.IsEmpty())
+        pArray->SetNewAt<CPDF_Name>(i, ByteString(fullname));
+    } else {
+      ReplaceAbbr(pElement);
+    }
+  }
+}
+
+void ReplaceAbbr(CPDF_Object* pObj) {
+  CPDF_Dictionary* pDict = pObj->AsDictionary();
+  if (pDict) {
+    ReplaceAbbrInDictionary(pDict);
+    return;
+  }
+
+  CPDF_Array* pArray = pObj->AsArray();
+  if (pArray)
+    ReplaceAbbrInArray(pArray);
 }
 
 }  // namespace
@@ -244,32 +255,20 @@
     CPDF_PageObjectHolder* pObjHolder,
     CPDF_Dictionary* pResources,
     const CFX_FloatRect& rcBBox,
-    CPDF_AllStates* pStates,
-    std::set<const uint8_t*>* parsedSet)
+    const CPDF_AllStates* pStates,
+    std::set<const uint8_t*>* pParsedSet)
     : m_pDocument(pDocument),
       m_pPageResources(pPageResources),
       m_pParentResources(pParentResources),
-      m_pResources(pResources),
+      m_pResources(CPDF_Form::ChooseResourcesDict(pResources,
+                                                  pParentResources,
+                                                  pPageResources)),
       m_pObjectHolder(pObjHolder),
-      m_ParsedSet(parsedSet),
+      m_ParsedSet(pParsedSet),
       m_BBox(rcBBox),
-      m_ParamStartPos(0),
-      m_ParamCount(0),
-      m_pCurStates(pdfium::MakeUnique<CPDF_AllStates>()),
-      m_DefFontSize(0),
-      m_PathStartX(0.0f),
-      m_PathStartY(0.0f),
-      m_PathCurrentX(0.0f),
-      m_PathCurrentY(0.0f),
-      m_PathClipType(0),
-      m_bColored(false),
-      m_bResourceMissing(false) {
+      m_pCurStates(pdfium::MakeUnique<CPDF_AllStates>()) {
   if (pmtContentToUser)
     m_mtContentToUser = *pmtContentToUser;
-  if (!m_pResources)
-    m_pResources = m_pParentResources;
-  if (!m_pResources)
-    m_pResources = m_pPageResources;
   if (pStates) {
     m_pCurStates->Copy(*pStates);
   } else {
@@ -278,9 +277,9 @@
     m_pCurStates->m_TextState.Emplace();
     m_pCurStates->m_ColorState.Emplace();
   }
-  for (size_t i = 0; i < FX_ArraySize(m_Type3Data); ++i) {
-    m_Type3Data[i] = 0.0;
-  }
+
+  // Add the sentinel.
+  m_ContentMarksStack.push(pdfium::MakeUnique<CPDF_ContentMarks>());
 }
 
 CPDF_StreamContentParser::~CPDF_StreamContentParser() {
@@ -294,7 +293,7 @@
       m_ParamStartPos = 0;
     }
     if (m_ParamBuf[m_ParamStartPos].m_Type == ContentParam::OBJECT)
-      m_ParamBuf[m_ParamStartPos].m_pObject.reset();
+      m_ParamBuf[m_ParamStartPos].m_pObject.Reset();
 
     return m_ParamStartPos;
   }
@@ -306,33 +305,20 @@
   return index;
 }
 
-void CPDF_StreamContentParser::AddNameParam(const ByteStringView& bsName) {
+void CPDF_StreamContentParser::AddNameParam(ByteStringView bsName) {
   ContentParam& param = m_ParamBuf[GetNextParamPos()];
-  if (bsName.GetLength() > 32) {
-    param.m_Type = ContentParam::OBJECT;
-    param.m_pObject = pdfium::MakeUnique<CPDF_Name>(
-        m_pDocument->GetByteStringPool(), PDF_NameDecode(bsName));
-  } else {
-    param.m_Type = ContentParam::NAME;
-    if (bsName.Contains('#')) {
-      ByteString str = PDF_NameDecode(bsName);
-      memcpy(param.m_Name.m_Buffer, str.c_str(), str.GetLength());
-      param.m_Name.m_Len = str.GetLength();
-    } else {
-      memcpy(param.m_Name.m_Buffer, bsName.raw_str(), bsName.GetLength());
-      param.m_Name.m_Len = bsName.GetLength();
-    }
-  }
+  param.m_Type = ContentParam::NAME;
+  param.m_Name =
+      bsName.Contains('#') ? PDF_NameDecode(bsName) : ByteString(bsName);
 }
 
-void CPDF_StreamContentParser::AddNumberParam(const ByteStringView& str) {
+void CPDF_StreamContentParser::AddNumberParam(ByteStringView str) {
   ContentParam& param = m_ParamBuf[GetNextParamPos()];
   param.m_Type = ContentParam::NUMBER;
-  param.m_Number.m_bInteger = FX_atonum(str, &param.m_Number.m_Integer);
+  param.m_Number = FX_Number(str);
 }
 
-void CPDF_StreamContentParser::AddObjectParam(
-    std::unique_ptr<CPDF_Object> pObj) {
+void CPDF_StreamContentParser::AddObjectParam(RetainPtr<CPDF_Object> pObj) {
   ContentParam& param = m_ParamBuf[GetNextParamPos()];
   param.m_Type = ContentParam::OBJECT;
   param.m_pObject = std::move(pObj);
@@ -342,7 +328,7 @@
   uint32_t index = m_ParamStartPos;
   for (uint32_t i = 0; i < m_ParamCount; i++) {
     if (m_ParamBuf[index].m_Type == ContentParam::OBJECT)
-      m_ParamBuf[index].m_pObject.reset();
+      m_ParamBuf[index].m_pObject.Reset();
     index++;
     if (index == kParamBufSize)
       index = 0;
@@ -363,69 +349,73 @@
   if (param.m_Type == ContentParam::NUMBER) {
     param.m_Type = ContentParam::OBJECT;
     param.m_pObject =
-        param.m_Number.m_bInteger
-            ? pdfium::MakeUnique<CPDF_Number>(param.m_Number.m_Integer)
-            : pdfium::MakeUnique<CPDF_Number>(param.m_Number.m_Float);
-    return param.m_pObject.get();
+        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();
   }
   if (param.m_Type == ContentParam::NAME) {
     param.m_Type = ContentParam::OBJECT;
-    param.m_pObject = pdfium::MakeUnique<CPDF_Name>(
-        m_pDocument->GetByteStringPool(),
-        ByteString(param.m_Name.m_Buffer, param.m_Name.m_Len));
-    return param.m_pObject.get();
+    param.m_pObject = m_pDocument->New<CPDF_Name>(param.m_Name);
+    return param.m_pObject.Get();
   }
   if (param.m_Type == ContentParam::OBJECT)
-    return param.m_pObject.get();
+    return param.m_pObject.Get();
 
   NOTREACHED();
   return nullptr;
 }
 
-ByteString CPDF_StreamContentParser::GetString(uint32_t index) {
-  if (index >= m_ParamCount) {
+ByteString CPDF_StreamContentParser::GetString(uint32_t index) const {
+  if (index >= m_ParamCount)
     return ByteString();
-  }
+
   int real_index = m_ParamStartPos + m_ParamCount - index - 1;
-  if (real_index >= kParamBufSize) {
+  if (real_index >= kParamBufSize)
     real_index -= kParamBufSize;
-  }
-  ContentParam& param = m_ParamBuf[real_index];
-  if (param.m_Type == ContentParam::NAME) {
-    return ByteString(param.m_Name.m_Buffer, param.m_Name.m_Len);
-  }
-  if (param.m_Type == 0 && param.m_pObject) {
+
+  const ContentParam& param = m_ParamBuf[real_index];
+  if (param.m_Type == ContentParam::NAME)
+    return param.m_Name;
+
+  if (param.m_Type == 0 && param.m_pObject)
     return param.m_pObject->GetString();
-  }
+
   return ByteString();
 }
 
-float CPDF_StreamContentParser::GetNumber(uint32_t index) {
-  if (index >= m_ParamCount) {
+float CPDF_StreamContentParser::GetNumber(uint32_t index) const {
+  if (index >= m_ParamCount)
     return 0;
-  }
+
   int real_index = m_ParamStartPos + m_ParamCount - index - 1;
-  if (real_index >= kParamBufSize) {
+  if (real_index >= kParamBufSize)
     real_index -= kParamBufSize;
-  }
-  ContentParam& param = m_ParamBuf[real_index];
-  if (param.m_Type == ContentParam::NUMBER) {
-    return param.m_Number.m_bInteger
-               ? static_cast<float>(param.m_Number.m_Integer)
-               : param.m_Number.m_Float;
-  }
+
+  const ContentParam& param = m_ParamBuf[real_index];
+  if (param.m_Type == ContentParam::NUMBER)
+    return param.m_Number.GetFloat();
+
   if (param.m_Type == 0 && param.m_pObject)
     return param.m_pObject->GetNumber();
+
   return 0;
 }
 
+std::vector<float> CPDF_StreamContentParser::GetNumbers(size_t count) const {
+  std::vector<float> values(count);
+  for (size_t i = 0; i < count; ++i)
+    values[i] = GetNumber(count - i - 1);
+  return values;
+}
+
 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_ContentMark = m_CurContentMark;
+  pObj->m_ContentMarks = *m_ContentMarksStack.top();
   if (bColor) {
     pObj->m_ColorState = m_pCurStates->m_ColorState;
   }
@@ -558,7 +548,7 @@
   });
 }
 
-void CPDF_StreamContentParser::OnOperator(const ByteStringView& op) {
+void CPDF_StreamContentParser::OnOperator(ByteStringView op) {
   static const OpCodes s_OpCodes = InitializeOpCodes();
 
   auto it = s_OpCodes.find(op.GetID());
@@ -585,27 +575,31 @@
 }
 
 void CPDF_StreamContentParser::Handle_BeginMarkedContent_Dictionary() {
-  ByteString tag = GetString(1);
   CPDF_Object* pProperty = GetObject(0);
-  if (!pProperty) {
+  if (!pProperty)
+    return;
+
+  ByteString tag = GetString(1);
+  std::unique_ptr<CPDF_ContentMarks> new_marks =
+      m_ContentMarksStack.top()->Clone();
+
+  if (pProperty->IsName()) {
+    ByteString property_name = pProperty->GetString();
+    CPDF_Dictionary* pHolder = FindResourceHolder("Properties");
+    if (!pHolder || !pHolder->GetDictFor(property_name))
+      return;
+    new_marks->AddMarkWithPropertiesHolder(tag, pHolder, property_name);
+  } else if (pProperty->IsDictionary()) {
+    new_marks->AddMarkWithDirectDict(tag, pProperty->AsDictionary());
+  } else {
     return;
   }
-  bool bDirect = true;
-  if (pProperty->IsName()) {
-    pProperty = FindResourceObj("Properties", pProperty->GetString());
-    if (!pProperty)
-      return;
-    bDirect = false;
-  }
-  if (CPDF_Dictionary* pDict = pProperty->AsDictionary()) {
-    m_CurContentMark.AddMark(tag, pDict, bDirect);
-  }
+  m_ContentMarksStack.push(std::move(new_marks));
 }
 
 void CPDF_StreamContentParser::Handle_BeginImage() {
   FX_FILESIZE savePos = m_pSyntax->GetPos();
-  auto pDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
+  auto pDict = m_pDocument->New<CPDF_Dictionary>();
   while (1) {
     CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
     if (type == CPDF_StreamParser::Keyword) {
@@ -618,17 +612,18 @@
       break;
     }
     auto word = m_pSyntax->GetWord();
-    ByteString key(word.Right(word.GetLength() - 1));
+    ByteString key(word.Last(word.GetLength() - 1));
     auto pObj = m_pSyntax->ReadNextObject(false, false, 0);
     if (!key.IsEmpty()) {
-      uint32_t dwObjNum = pObj ? pObj->GetObjNum() : 0;
-      if (dwObjNum)
-        pDict->SetNewFor<CPDF_Reference>(key, m_pDocument.Get(), dwObjNum);
-      else
+      if (pObj && !pObj->IsInline()) {
+        pDict->SetNewFor<CPDF_Reference>(key, m_pDocument.Get(),
+                                         pObj->GetObjNum());
+      } else {
         pDict->SetFor(key, std::move(pObj));
+      }
     }
   }
-  ReplaceAbbr(pDict.get());
+  ReplaceAbbr(pDict.Get());
   CPDF_Object* pCSObj = nullptr;
   if (pDict->KeyExist("ColorSpace")) {
     pCSObj = pDict->GetDirectObjectFor("ColorSpace");
@@ -642,7 +637,7 @@
     }
   }
   pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
-  std::unique_ptr<CPDF_Stream> pStream =
+  RetainPtr<CPDF_Stream> pStream =
       m_pSyntax->ReadInlineStream(m_pDocument.Get(), std::move(pDict), pCSObj);
   while (1) {
     CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
@@ -664,7 +659,10 @@
 }
 
 void CPDF_StreamContentParser::Handle_BeginMarkedContent() {
-  m_CurContentMark.AddMark(GetString(0), nullptr, false);
+  std::unique_ptr<CPDF_ContentMarks> new_marks =
+      m_ContentMarksStack.top()->Clone();
+  new_marks->AddMark(GetString(0));
+  m_ContentMarksStack.push(std::move(new_marks));
 }
 
 void CPDF_StreamContentParser::Handle_BeginText() {
@@ -683,13 +681,12 @@
 void CPDF_StreamContentParser::Handle_ConcatMatrix() {
   CFX_Matrix new_matrix(GetNumber(5), GetNumber(4), GetNumber(3), GetNumber(2),
                         GetNumber(1), GetNumber(0));
-  new_matrix.Concat(m_pCurStates->m_CTM);
-  m_pCurStates->m_CTM = new_matrix;
+  m_pCurStates->m_CTM = new_matrix * m_pCurStates->m_CTM;
   OnChangeTextMatrix();
 }
 
 void CPDF_StreamContentParser::Handle_SetColorSpace_Fill() {
-  CPDF_ColorSpace* pCS = FindColorSpace(GetString(0));
+  RetainPtr<CPDF_ColorSpace> pCS = FindColorSpace(GetString(0));
   if (!pCS)
     return;
 
@@ -697,7 +694,7 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetColorSpace_Stroke() {
-  CPDF_ColorSpace* pCS = FindColorSpace(GetString(0));
+  RetainPtr<CPDF_ColorSpace> pCS = FindColorSpace(GetString(0));
   if (!pCS)
     return;
 
@@ -747,20 +744,22 @@
   if (pXObject->GetDict())
     type = pXObject->GetDict()->GetStringFor("Subtype");
 
+  if (type == "Form") {
+    AddForm(pXObject);
+    return;
+  }
+
   if (type == "Image") {
     CPDF_ImageObject* pObj = pXObject->IsInline()
-                                 ? AddImage(std::unique_ptr<CPDF_Stream>(
-                                       ToStream(pXObject->Clone())))
+                                 ? AddImage(ToStream(pXObject->Clone()))
                                  : AddImage(pXObject->GetObjNum());
 
-    m_LastImageName = name;
+    m_LastImageName = std::move(name);
     if (pObj) {
       m_pLastImage = pObj->GetImage();
       if (m_pLastImage->IsMask())
         m_pObjectHolder->AddImageMaskBoundingBox(pObj->GetRect());
     }
-  } else if (type == "Form") {
-    AddForm(pXObject);
   }
 }
 
@@ -772,35 +771,40 @@
   status.m_TextState = m_pCurStates->m_TextState;
   auto form = pdfium::MakeUnique<CPDF_Form>(
       m_pDocument.Get(), m_pPageResources.Get(), pStream, m_pResources.Get());
-  form->ParseContentWithParams(&status, nullptr, nullptr, m_ParsedSet.Get());
+  form->ParseContent(&status, nullptr, m_ParsedSet.Get());
 
-  CFX_Matrix matrix = m_pCurStates->m_CTM;
-  matrix.Concat(m_mtContentToUser);
+  CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser;
 
-  auto pFormObj = pdfium::MakeUnique<CPDF_FormObject>(std::move(form), matrix);
+  auto pFormObj = pdfium::MakeUnique<CPDF_FormObject>(GetCurrentStreamIndex(),
+                                                      std::move(form), matrix);
   if (!m_pObjectHolder->BackgroundAlphaNeeded() &&
       pFormObj->form()->BackgroundAlphaNeeded()) {
     m_pObjectHolder->SetBackgroundAlphaNeeded(true);
   }
   pFormObj->CalcBoundingBox();
   SetGraphicStates(pFormObj.get(), true, true, true);
-  m_pObjectHolder->GetPageObjectList()->push_back(std::move(pFormObj));
+  m_pObjectHolder->AppendPageObject(std::move(pFormObj));
 }
 
 CPDF_ImageObject* CPDF_StreamContentParser::AddImage(
-    std::unique_ptr<CPDF_Stream> pStream) {
+    RetainPtr<CPDF_Stream> pStream) {
   if (!pStream)
     return nullptr;
 
-  auto pImageObj = pdfium::MakeUnique<CPDF_ImageObject>();
+  auto pImageObj =
+      pdfium::MakeUnique<CPDF_ImageObject>(GetCurrentStreamIndex());
   pImageObj->SetImage(
       pdfium::MakeRetain<CPDF_Image>(m_pDocument.Get(), std::move(pStream)));
+
   return AddImageObject(std::move(pImageObj));
 }
 
 CPDF_ImageObject* CPDF_StreamContentParser::AddImage(uint32_t streamObjNum) {
-  auto pImageObj = pdfium::MakeUnique<CPDF_ImageObject>();
-  pImageObj->SetImage(m_pDocument->LoadImageFromPageData(streamObjNum));
+  auto pImageObj =
+      pdfium::MakeUnique<CPDF_ImageObject>(GetCurrentStreamIndex());
+  pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument.Get())
+                          ->GetImage(streamObjNum));
+
   return AddImageObject(std::move(pImageObj));
 }
 
@@ -809,9 +813,10 @@
   if (!pImage)
     return nullptr;
 
-  auto pImageObj = pdfium::MakeUnique<CPDF_ImageObject>();
-  pImageObj->SetImage(
-      m_pDocument->GetPageData()->GetImage(pImage->GetStream()->GetObjNum()));
+  auto pImageObj =
+      pdfium::MakeUnique<CPDF_ImageObject>(GetCurrentStreamIndex());
+  pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument.Get())
+                          ->GetImage(pImage->GetStream()->GetObjNum()));
 
   return AddImageObject(std::move(pImageObj));
 }
@@ -821,23 +826,38 @@
   SetGraphicStates(pImageObj.get(), pImageObj->GetImage()->IsMask(), false,
                    false);
 
-  CFX_Matrix ImageMatrix = m_pCurStates->m_CTM;
-  ImageMatrix.Concat(m_mtContentToUser);
+  CFX_Matrix ImageMatrix = m_pCurStates->m_CTM * m_mtContentToUser;
   pImageObj->set_matrix(ImageMatrix);
   pImageObj->CalcBoundingBox();
 
   CPDF_ImageObject* pRet = pImageObj.get();
-  m_pObjectHolder->GetPageObjectList()->push_back(std::move(pImageObj));
+  m_pObjectHolder->AppendPageObject(std::move(pImageObj));
   return pRet;
 }
 
+std::vector<float> CPDF_StreamContentParser::GetColors() const {
+  ASSERT(m_ParamCount > 0);
+  return GetNumbers(m_ParamCount);
+}
+
+std::vector<float> CPDF_StreamContentParser::GetNamedColors() const {
+  ASSERT(m_ParamCount > 0);
+  const uint32_t nvalues = m_ParamCount - 1;
+  std::vector<float> values(nvalues);
+  for (size_t i = 0; i < nvalues; ++i)
+    values[i] = GetNumber(m_ParamCount - i - 1);
+  return values;
+}
+
 void CPDF_StreamContentParser::Handle_MarkPlace_Dictionary() {}
 
 void CPDF_StreamContentParser::Handle_EndImage() {}
 
 void CPDF_StreamContentParser::Handle_EndMarkedContent() {
-  if (m_CurContentMark.HasRef())
-    m_CurContentMark.DeleteLastMark();
+  // First element is a sentinel, so do not pop it, ever. This may come up if
+  // the EMCs are mismatched with the BMC/BDCs.
+  if (m_ContentMarksStack.size() > 1)
+    m_ContentMarksStack.pop();
 }
 
 void CPDF_StreamContentParser::Handle_EndText() {
@@ -863,15 +883,15 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetGray_Fill() {
-  float value = GetNumber(0);
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
-  m_pCurStates->m_ColorState.SetFillColor(pCS, &value, 1);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
+  m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(1));
 }
 
 void CPDF_StreamContentParser::Handle_SetGray_Stroke() {
-  float value = GetNumber(0);
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
-  m_pCurStates->m_ColorState.SetStrokeColor(pCS, &value, 1);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
+  m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(1));
 }
 
 void CPDF_StreamContentParser::Handle_SetExtendGraphState() {
@@ -914,24 +934,18 @@
   if (m_ParamCount != 4)
     return;
 
-  float values[4];
-  for (int i = 0; i < 4; i++) {
-    values[i] = GetNumber(3 - i);
-  }
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
-  m_pCurStates->m_ColorState.SetFillColor(pCS, values, 4);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+  m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(4));
 }
 
 void CPDF_StreamContentParser::Handle_SetCMYKColor_Stroke() {
   if (m_ParamCount != 4)
     return;
 
-  float values[4];
-  for (int i = 0; i < 4; i++) {
-    values[i] = GetNumber(3 - i);
-  }
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
-  m_pCurStates->m_ColorState.SetStrokeColor(pCS, values, 4);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+  m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(4));
 }
 
 void CPDF_StreamContentParser::Handle_LineTo() {
@@ -991,24 +1005,16 @@
   if (m_ParamCount != 3)
     return;
 
-  float values[3];
-  for (int i = 0; i < 3; i++) {
-    values[i] = GetNumber(2 - i);
-  }
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-  m_pCurStates->m_ColorState.SetFillColor(pCS, values, 3);
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(3));
 }
 
 void CPDF_StreamContentParser::Handle_SetRGBColor_Stroke() {
   if (m_ParamCount != 3)
     return;
 
-  float values[3];
-  for (int i = 0; i < 3; i++) {
-    values[i] = GetNumber(2 - i);
-  }
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-  m_pCurStates->m_ColorState.SetStrokeColor(pCS, values, 3);
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(3));
 }
 
 void CPDF_StreamContentParser::Handle_SetRenderIntent() {}
@@ -1023,86 +1029,51 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetColor_Fill() {
-  float values[4];
-  int nargs = m_ParamCount;
-  if (nargs > 4) {
-    nargs = 4;
-  }
-  for (int i = 0; i < nargs; i++) {
-    values[i] = GetNumber(nargs - i - 1);
-  }
-  m_pCurStates->m_ColorState.SetFillColor(nullptr, values, nargs);
+  int nargs = std::min(m_ParamCount, 4U);
+  m_pCurStates->m_ColorState.SetFillColor(nullptr, GetNumbers(nargs));
 }
 
 void CPDF_StreamContentParser::Handle_SetColor_Stroke() {
-  float values[4];
-  int nargs = m_ParamCount;
-  if (nargs > 4) {
-    nargs = 4;
-  }
-  for (int i = 0; i < nargs; i++) {
-    values[i] = GetNumber(nargs - i - 1);
-  }
-  m_pCurStates->m_ColorState.SetStrokeColor(nullptr, values, nargs);
+  int nargs = std::min(m_ParamCount, 4U);
+  m_pCurStates->m_ColorState.SetStrokeColor(nullptr, GetNumbers(nargs));
 }
 
 void CPDF_StreamContentParser::Handle_SetColorPS_Fill() {
   CPDF_Object* pLastParam = GetObject(0);
-  if (!pLastParam) {
+  if (!pLastParam)
+    return;
+
+  if (!pLastParam->IsName()) {
+    m_pCurStates->m_ColorState.SetFillColor(nullptr, GetColors());
     return;
   }
-  uint32_t nargs = m_ParamCount;
-  uint32_t nvalues = nargs;
-  if (pLastParam->IsName())
-    nvalues--;
-  float* values = nullptr;
-  if (nvalues) {
-    values = FX_Alloc(float, nvalues);
-    for (uint32_t i = 0; i < nvalues; i++) {
-      values[i] = GetNumber(nargs - i - 1);
-    }
-  }
-  if (nvalues != nargs) {
-    CPDF_Pattern* pPattern = FindPattern(GetString(0), false);
-    if (pPattern) {
-      m_pCurStates->m_ColorState.SetFillPattern(pPattern, values, nvalues);
-    }
-  } else {
-    m_pCurStates->m_ColorState.SetFillColor(nullptr, values, nvalues);
-  }
-  FX_Free(values);
+
+  // 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());
 }
 
 void CPDF_StreamContentParser::Handle_SetColorPS_Stroke() {
   CPDF_Object* pLastParam = GetObject(0);
-  if (!pLastParam) {
+  if (!pLastParam)
+    return;
+
+  if (!pLastParam->IsName()) {
+    m_pCurStates->m_ColorState.SetStrokeColor(nullptr, GetColors());
     return;
   }
-  int nargs = m_ParamCount;
-  int nvalues = nargs;
-  if (pLastParam->IsName())
-    nvalues--;
 
-  float* values = nullptr;
-  if (nvalues) {
-    values = FX_Alloc(float, nvalues);
-    for (int i = 0; i < nvalues; i++) {
-      values[i] = GetNumber(nargs - i - 1);
-    }
-  }
-  if (nvalues != nargs) {
-    CPDF_Pattern* pPattern = FindPattern(GetString(0), false);
-    if (pPattern) {
-      m_pCurStates->m_ColorState.SetStrokePattern(pPattern, values, nvalues);
-    }
-  } else {
-    m_pCurStates->m_ColorState.SetStrokeColor(nullptr, values, nvalues);
-  }
-  FX_Free(values);
+  // 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() {
-  CPDF_Pattern* pPattern = FindPattern(GetString(0), true);
+  RetainPtr<CPDF_Pattern> pPattern = FindPattern(GetString(0), true);
   if (!pPattern)
     return;
 
@@ -1113,19 +1084,16 @@
   if (!pShading->IsShadingObject() || !pShading->Load())
     return;
 
-  CFX_Matrix matrix = m_pCurStates->m_CTM;
-  matrix.Concat(m_mtContentToUser);
-  auto pObj = pdfium::MakeUnique<CPDF_ShadingObject>(pShading, matrix);
+  CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser;
+  auto pObj = pdfium::MakeUnique<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()));
-  pObj->m_Left = bbox.left;
-  pObj->m_Right = bbox.right;
-  pObj->m_Top = bbox.top;
-  pObj->m_Bottom = bbox.bottom;
-  m_pObjectHolder->GetPageObjectList()->push_back(std::move(pObj));
+  pObj->SetRect(bbox);
+  m_pObjectHolder->AppendPageObject(std::move(pObj));
 }
 
 void CPDF_StreamContentParser::Handle_SetCharSpace() {
@@ -1145,37 +1113,47 @@
 void CPDF_StreamContentParser::Handle_SetFont() {
   float fs = GetNumber(0);
   if (fs == 0) {
-    fs = m_DefFontSize;
+    constexpr float kDefaultFontSize = 0.0f;
+    fs = kDefaultFontSize;
   }
+
   m_pCurStates->m_TextState.SetFontSize(fs);
-  CPDF_Font* pFont = FindFont(GetString(1));
-  if (pFont) {
+  RetainPtr<CPDF_Font> pFont = FindFont(GetString(1));
+  if (pFont)
     m_pCurStates->m_TextState.SetFont(pFont);
-  }
+}
+
+CPDF_Dictionary* CPDF_StreamContentParser::FindResourceHolder(
+    const ByteString& type) {
+  if (!m_pResources)
+    return nullptr;
+
+  CPDF_Dictionary* pDict = m_pResources->GetDictFor(type);
+  if (pDict)
+    return pDict;
+
+  if (m_pResources == m_pPageResources || !m_pPageResources)
+    return nullptr;
+
+  return m_pPageResources->GetDictFor(type);
 }
 
 CPDF_Object* CPDF_StreamContentParser::FindResourceObj(const ByteString& type,
                                                        const ByteString& name) {
-  if (!m_pResources)
-    return nullptr;
-  CPDF_Dictionary* pDict = m_pResources->GetDictFor(type);
-  if (pDict)
-    return pDict->GetDirectObjectFor(name);
-  if (m_pResources == m_pPageResources || !m_pPageResources)
-    return nullptr;
-
-  CPDF_Dictionary* pPageDict = m_pPageResources->GetDictFor(type);
-  return pPageDict ? pPageDict->GetDirectObjectFor(name) : nullptr;
+  CPDF_Dictionary* pHolder = FindResourceHolder(type);
+  return pHolder ? pHolder->GetDirectObjectFor(name) : nullptr;
 }
 
-CPDF_Font* CPDF_StreamContentParser::FindFont(const ByteString& name) {
+RetainPtr<CPDF_Font> CPDF_StreamContentParser::FindFont(
+    const ByteString& name) {
   CPDF_Dictionary* pFontDict = ToDictionary(FindResourceObj("Font", name));
   if (!pFontDict) {
     m_bResourceMissing = true;
-    return CPDF_Font::GetStockFont(m_pDocument.Get(), "Helvetica");
+    return CPDF_Font::GetStockFont(m_pDocument.Get(),
+                                   CFX_Font::kDefaultAnsiFontName);
   }
-
-  CPDF_Font* pFont = m_pDocument->LoadFont(pFontDict);
+  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();
@@ -1183,73 +1161,71 @@
   return pFont;
 }
 
-CPDF_ColorSpace* CPDF_StreamContentParser::FindColorSpace(
+RetainPtr<CPDF_ColorSpace> CPDF_StreamContentParser::FindColorSpace(
     const ByteString& name) {
-  if (name == "Pattern") {
+  if (name == "Pattern")
     return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
-  }
+
   if (name == "DeviceGray" || name == "DeviceCMYK" || name == "DeviceRGB") {
     ByteString defname = "Default";
-    defname += name.Right(name.GetLength() - 7);
-    CPDF_Object* pDefObj = FindResourceObj("ColorSpace", defname);
+    defname += name.Last(name.GetLength() - 7);
+    const CPDF_Object* pDefObj = FindResourceObj("ColorSpace", defname);
     if (!pDefObj) {
-      if (name == "DeviceGray") {
+      if (name == "DeviceGray")
         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
-      }
-      if (name == "DeviceRGB") {
+
+      if (name == "DeviceRGB")
         return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-      }
+
       return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
     }
-    return m_pDocument->LoadColorSpace(pDefObj);
+    return CPDF_DocPageData::FromDocument(m_pDocument.Get())
+        ->GetColorSpace(pDefObj, nullptr);
   }
-  CPDF_Object* pCSObj = FindResourceObj("ColorSpace", name);
+  const CPDF_Object* pCSObj = FindResourceObj("ColorSpace", name);
   if (!pCSObj) {
     m_bResourceMissing = true;
     return nullptr;
   }
-  return m_pDocument->LoadColorSpace(pCSObj);
+  return CPDF_DocPageData::FromDocument(m_pDocument.Get())
+      ->GetColorSpace(pCSObj, nullptr);
 }
 
-CPDF_Pattern* CPDF_StreamContentParser::FindPattern(const ByteString& name,
-                                                    bool bShading) {
+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;
     return nullptr;
   }
-  return m_pDocument->LoadPattern(pPattern, bShading,
-                                  m_pCurStates->m_ParentMatrix);
+  return CPDF_DocPageData::FromDocument(m_pDocument.Get())
+      ->GetPattern(pPattern, bShading, m_pCurStates->m_ParentMatrix);
 }
 
-void CPDF_StreamContentParser::AddTextObject(ByteString* pStrs,
+void CPDF_StreamContentParser::AddTextObject(const ByteString* pStrs,
                                              float fInitKerning,
-                                             float* pKerning,
-                                             int nsegs) {
-  CPDF_Font* pFont = m_pCurStates->m_TextState.GetFont();
-  if (!pFont) {
+                                             const std::vector<float>& kernings,
+                                             size_t nSegs) {
+  RetainPtr<CPDF_Font> pFont = m_pCurStates->m_TextState.GetFont();
+  if (!pFont)
     return;
-  }
+
   if (fInitKerning != 0) {
-    if (!pFont->IsVertWriting()) {
-      m_pCurStates->m_TextPos.x -=
-          (fInitKerning * m_pCurStates->m_TextState.GetFontSize() *
-           m_pCurStates->m_TextHorzScale) /
-          1000;
-    } else {
-      m_pCurStates->m_TextPos.y -=
-          (fInitKerning * m_pCurStates->m_TextState.GetFontSize()) / 1000;
-    }
+    if (pFont->IsVertWriting())
+      m_pCurStates->m_TextPos.y -= GetVerticalTextSize(fInitKerning);
+    else
+      m_pCurStates->m_TextPos.x -= GetHorizontalTextSize(fInitKerning);
   }
-  if (nsegs == 0) {
+  if (nSegs == 0)
     return;
-  }
+
   const TextRenderingMode text_mode =
       pFont->IsType3Font() ? TextRenderingMode::MODE_FILL
                            : m_pCurStates->m_TextState.GetTextMode();
   {
-    auto pText = pdfium::MakeUnique<CPDF_TextObject>();
+    auto pText = pdfium::MakeUnique<CPDF_TextObject>(GetCurrentStreamIndex());
     m_pLastTextObject = pText.get();
     SetGraphicStates(m_pLastTextObject.Get(), true, true, true);
     if (TextRenderingModeIsStrokeMode(text_mode)) {
@@ -1259,7 +1235,7 @@
       pCTM[2] = m_pCurStates->m_CTM.b;
       pCTM[3] = m_pCurStates->m_CTM.d;
     }
-    pText->SetSegments(pStrs, pKerning, nsegs);
+    pText->SetSegments(pStrs, kernings, nSegs);
     pText->SetPosition(
         m_mtContentToUser.Transform(m_pCurStates->m_CTM.Transform(
             m_pCurStates->m_TextMatrix.Transform(CFX_PointF(
@@ -1272,28 +1248,35 @@
       m_ClipTextList.push_back(
           std::unique_ptr<CPDF_TextObject>(pText->Clone()));
     }
-    m_pObjectHolder->GetPageObjectList()->push_back(std::move(pText));
+    m_pObjectHolder->AppendPageObject(std::move(pText));
   }
-  if (pKerning && pKerning[nsegs - 1] != 0) {
-    if (!pFont->IsVertWriting()) {
-      m_pCurStates->m_TextPos.x -=
-          (pKerning[nsegs - 1] * m_pCurStates->m_TextState.GetFontSize() *
-           m_pCurStates->m_TextHorzScale) /
-          1000;
-    } else {
-      m_pCurStates->m_TextPos.y -=
-          (pKerning[nsegs - 1] * m_pCurStates->m_TextState.GetFontSize()) /
-          1000;
-    }
+  if (!kernings.empty() && kernings[nSegs - 1] != 0) {
+    if (pFont->IsVertWriting())
+      m_pCurStates->m_TextPos.y -= GetVerticalTextSize(kernings[nSegs - 1]);
+    else
+      m_pCurStates->m_TextPos.x -= GetHorizontalTextSize(kernings[nSegs - 1]);
   }
 }
 
+float CPDF_StreamContentParser::GetHorizontalTextSize(float fKerning) const {
+  return GetVerticalTextSize(fKerning) * m_pCurStates->m_TextHorzScale;
+}
+
+float CPDF_StreamContentParser::GetVerticalTextSize(float fKerning) const {
+  return fKerning * m_pCurStates->m_TextState.GetFontSize() / 1000;
+}
+
+int32_t CPDF_StreamContentParser::GetCurrentStreamIndex() {
+  auto it =
+      std::upper_bound(m_StreamStartOffsets.begin(), m_StreamStartOffsets.end(),
+                       m_pSyntax->GetPos() + m_StartParseOffset);
+  return (it - m_StreamStartOffsets.begin()) - 1;
+}
+
 void CPDF_StreamContentParser::Handle_ShowText() {
   ByteString str = GetString(0);
-  if (str.IsEmpty()) {
-    return;
-  }
-  AddTextObject(&str, 0, nullptr, 1);
+  if (!str.IsEmpty())
+    AddTextObject(&str, 0, std::vector<float>(), 1);
 }
 
 void CPDF_StreamContentParser::Handle_ShowText_Positioning() {
@@ -1301,18 +1284,18 @@
   if (!pArray)
     return;
 
-  size_t n = pArray->GetCount();
+  size_t n = pArray->size();
   size_t nsegs = 0;
   for (size_t i = 0; i < n; i++) {
-    if (pArray->GetDirectObjectAt(i)->IsString())
+    const CPDF_Object* pDirectObject = pArray->GetDirectObjectAt(i);
+    if (pDirectObject && pDirectObject->IsString())
       nsegs++;
   }
   if (nsegs == 0) {
     for (size_t i = 0; i < n; i++) {
-      m_pCurStates->m_TextPos.x -=
-          (pArray->GetNumberAt(i) * m_pCurStates->m_TextState.GetFontSize() *
-           m_pCurStates->m_TextHorzScale) /
-          1000;
+      float fKerning = pArray->GetNumberAt(i);
+      if (fKerning != 0)
+        m_pCurStates->m_TextPos.x -= GetHorizontalTextSize(fKerning);
     }
     return;
   }
@@ -1322,11 +1305,14 @@
   float fInitKerning = 0;
   for (size_t i = 0; i < n; i++) {
     CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
+    if (!pObj)
+      continue;
+
     if (pObj->IsString()) {
       ByteString str = pObj->GetString();
       if (str.IsEmpty())
         continue;
-      strs[iSegment] = str;
+      strs[iSegment] = std::move(str);
       kernings[iSegment++] = 0;
     } else {
       float num = pObj->GetNumber();
@@ -1336,7 +1322,7 @@
         kernings[iSegment - 1] += num;
     }
   }
-  AddTextObject(strs.data(), fInitKerning, kernings.data(), iSegment);
+  AddTextObject(strs.data(), fInitKerning, kernings, iSegment);
 }
 
 void CPDF_StreamContentParser::Handle_SetTextLeading() {
@@ -1483,42 +1469,55 @@
   for (const auto& point : PathPoints)
     Path.AppendPoint(point.m_Point, point.m_Type, point.m_CloseFigure);
 
-  CFX_Matrix matrix = m_pCurStates->m_CTM;
-  matrix.Concat(m_mtContentToUser);
+  CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser;
   if (bStroke || FillType) {
-    auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-    pPathObj->m_bStroke = bStroke;
-    pPathObj->m_FillType = FillType;
-    pPathObj->m_Path = Path;
-    pPathObj->m_Matrix = matrix;
+    auto pPathObj =
+        pdfium::MakeUnique<CPDF_PathObject>(GetCurrentStreamIndex());
+    pPathObj->set_stroke(bStroke);
+    pPathObj->set_filltype(FillType);
+    pPathObj->path() = Path;
+    pPathObj->set_matrix(matrix);
     SetGraphicStates(pPathObj.get(), true, false, true);
     pPathObj->CalcBoundingBox();
-    m_pObjectHolder->GetPageObjectList()->push_back(std::move(pPathObj));
+    m_pObjectHolder->AppendPageObject(std::move(pPathObj));
   }
   if (PathClipType) {
-    if (!matrix.IsIdentity()) {
-      Path.Transform(&matrix);
-      matrix.SetIdentity();
-    }
+    if (!matrix.IsIdentity())
+      Path.Transform(matrix);
     m_pCurStates->m_ClipPath.AppendPath(Path, PathClipType, true);
   }
 }
 
-uint32_t CPDF_StreamContentParser::Parse(const uint8_t* pData,
-                                         uint32_t dwSize,
-                                         uint32_t max_cost) {
+uint32_t CPDF_StreamContentParser::Parse(
+    const uint8_t* pData,
+    uint32_t dwSize,
+    uint32_t start_offset,
+    uint32_t max_cost,
+    const std::vector<uint32_t>& stream_start_offsets) {
+  ASSERT(start_offset < dwSize);
+
+  // 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;
+
+  m_StartParseOffset = start_offset;
+
   if (m_ParsedSet->size() > kMaxFormLevel ||
-      pdfium::ContainsKey(*m_ParsedSet, pData))
-    return dwSize;
+      pdfium::ContainsKey(*m_ParsedSet, pDataStart)) {
+    return size_left;
+  }
+
+  m_StreamStartOffsets = stream_start_offsets;
 
   pdfium::ScopedSetInsertion<const uint8_t*> scopedInsert(m_ParsedSet.Get(),
-                                                          pData);
+                                                          pDataStart);
 
-  uint32_t InitObjCount = m_pObjectHolder->GetPageObjectList()->size();
-  CPDF_StreamParser syntax(pData, dwSize, m_pDocument->GetByteStringPool());
+  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) {
-    uint32_t cost = m_pObjectHolder->GetPageObjectList()->size() - InitObjCount;
+    uint32_t cost = m_pObjectHolder->GetPageObjectCount() - init_obj_count;
     if (max_cost && cost >= max_cost) {
       break;
     }
@@ -1534,7 +1533,7 @@
         break;
       case CPDF_StreamParser::Name: {
         auto word = syntax.GetWord();
-        AddNameParam(word.Right(word.GetLength() - 1));
+        AddNameParam(word.Last(word.GetLength() - 1));
         break;
       }
       default:
@@ -1614,10 +1613,8 @@
         if (nParams == 6)
           break;
 
-        int value;
-        bool bInteger = FX_atonum(m_pSyntax->GetWord(), &value);
-        params[nParams++] = bInteger ? static_cast<float>(value)
-                                     : *reinterpret_cast<float*>(&value);
+        FX_Number number(m_pSyntax->GetWord());
+        params[nParams++] = number.GetFloat();
         break;
       }
       default:
@@ -1632,14 +1629,14 @@
 
 // static
 ByteStringView CPDF_StreamContentParser::FindKeyAbbreviationForTesting(
-    const ByteStringView& abbr) {
-  return FindFullName(InlineKeyAbbr, FX_ArraySize(InlineKeyAbbr), abbr);
+    ByteStringView abbr) {
+  return FindFullName(kInlineKeyAbbr, FX_ArraySize(kInlineKeyAbbr), abbr);
 }
 
 // static
 ByteStringView CPDF_StreamContentParser::FindValueAbbreviationForTesting(
-    const ByteStringView& abbr) {
-  return FindFullName(InlineValueAbbr, FX_ArraySize(InlineValueAbbr), abbr);
+    ByteStringView abbr) {
+  return FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr), abbr);
 }
 
 CPDF_StreamContentParser::ContentParam::ContentParam() {}
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.h b/core/fpdfapi/page/cpdf_streamcontentparser.h
index de4a92f..a270320 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.h
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.h
@@ -10,25 +10,29 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <stack>
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_contentmark.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/page/cpdf_contentmarks.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"
 
 class CPDF_AllStates;
+class CPDF_ColorSpace;
 class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_Font;
 class CPDF_Image;
 class CPDF_ImageObject;
+class CPDF_Object;
 class CPDF_PageObject;
 class CPDF_PageObjectHolder;
+class CPDF_Pattern;
+class CPDF_Stream;
 class CPDF_StreamParser;
 class CPDF_TextObject;
-class CPDF_ColorSpace;
-class CPDF_Pattern;
 
 class CPDF_StreamContentParser {
  public:
@@ -36,26 +40,28 @@
                            CPDF_Dictionary* pPageResources,
                            CPDF_Dictionary* pParentResources,
                            const CFX_Matrix* pmtContentToUser,
-                           CPDF_PageObjectHolder* pObjectHolder,
+                           CPDF_PageObjectHolder* pObjHolder,
                            CPDF_Dictionary* pResources,
                            const CFX_FloatRect& rcBBox,
-                           CPDF_AllStates* pAllStates,
-                           std::set<const uint8_t*>* parsedSet);
+                           const CPDF_AllStates* pStates,
+                           std::set<const uint8_t*>* pParsedSet);
   ~CPDF_StreamContentParser();
 
-  uint32_t Parse(const uint8_t* pData, uint32_t dwSize, uint32_t max_cost);
+  uint32_t Parse(const uint8_t* pData,
+                 uint32_t dwSize,
+                 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_AllStates* GetCurStates() const { return m_pCurStates.get(); }
   bool IsColored() const { return m_bColored; }
   const float* GetType3Data() const { return m_Type3Data; }
-  CPDF_Font* FindFont(const ByteString& name);
+  RetainPtr<CPDF_Font> FindFont(const ByteString& name);
 
-  static ByteStringView FindKeyAbbreviationForTesting(
-      const ByteStringView& abbr);
-  static ByteStringView FindValueAbbreviationForTesting(
-      const ByteStringView& abbr);
+  static ByteStringView FindKeyAbbreviationForTesting(ByteStringView abbr);
+  static ByteStringView FindValueAbbreviationForTesting(ByteStringView abbr);
 
  private:
   struct ContentParam {
@@ -65,18 +71,9 @@
     ~ContentParam();
 
     Type m_Type;
-    std::unique_ptr<CPDF_Object> m_pObject;
-    struct {
-      bool m_bInteger;
-      union {
-        int m_Integer;
-        float m_Float;
-      };
-    } m_Number;
-    struct {
-      int m_Len;
-      char m_Buffer[32];
-    } m_Name;
+    FX_Number m_Number;
+    ByteString m_Name;
+    RetainPtr<CPDF_Object> m_pObject;
   };
 
   static const int kParamBufSize = 16;
@@ -84,27 +81,34 @@
   using OpCodes = std::map<uint32_t, void (CPDF_StreamContentParser::*)()>;
   static OpCodes InitializeOpCodes();
 
-  void AddNameParam(const ByteStringView& str);
-  void AddNumberParam(const ByteStringView& str);
-  void AddObjectParam(std::unique_ptr<CPDF_Object> pObj);
+  void AddNameParam(ByteStringView bsName);
+  void AddNumberParam(ByteStringView str);
+  void AddObjectParam(RetainPtr<CPDF_Object> pObj);
   int GetNextParamPos();
   void ClearAllParams();
   CPDF_Object* GetObject(uint32_t index);
-  ByteString GetString(uint32_t index);
-  float GetNumber(uint32_t index);
-  int GetInteger(uint32_t index) { return (int32_t)(GetNumber(index)); }
-  void OnOperator(const ByteStringView& op);
-  void AddTextObject(ByteString* pText,
+  ByteString GetString(uint32_t index) const;
+  float GetNumber(uint32_t index) const;
+  // Calls GetNumber() |count| times and returns the values in reverse order.
+  // e.g. for |count| = 3, returns [GetNumber(2), GetNumber(1), GetNumber(0)].
+  std::vector<float> GetNumbers(size_t count) const;
+  int GetInteger(uint32_t index) const {
+    return static_cast<int>(GetNumber(index));
+  }
+  void OnOperator(ByteStringView op);
+  void AddTextObject(const ByteString* pStrs,
                      float fInitKerning,
-                     float* pKerning,
-                     int count);
+                     const std::vector<float>& kernings,
+                     size_t nSegs);
+  float GetHorizontalTextSize(float fKerning) const;
+  float GetVerticalTextSize(float fKerning) const;
 
   void OnChangeTextMatrix();
   void ParsePathObject();
   void AddPathPoint(float x, float y, FXPT_TYPE type, bool close);
   void AddPathRect(float x, float y, float w, float h);
   void AddPathObject(int FillType, bool bStroke);
-  CPDF_ImageObject* AddImage(std::unique_ptr<CPDF_Stream> pStream);
+  CPDF_ImageObject* AddImage(RetainPtr<CPDF_Stream> pStream);
   CPDF_ImageObject* AddImage(uint32_t streamObjNum);
   CPDF_ImageObject* AddImage(const RetainPtr<CPDF_Image>& pImage);
 
@@ -113,13 +117,18 @@
                         bool bColor,
                         bool bText,
                         bool bGraph);
-  CPDF_ColorSpace* FindColorSpace(const ByteString& name);
-  CPDF_Pattern* FindPattern(const ByteString& name, bool bShading);
+  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);
 
   // Takes ownership of |pImageObj|, returns unowned pointer to it.
   CPDF_ImageObject* AddImageObject(std::unique_ptr<CPDF_ImageObject> pImageObj);
 
+  std::vector<float> GetColors() const;
+  std::vector<float> GetNamedColors() const;
+  int32_t GetCurrentStreamIndex();
+
   void Handle_CloseFillStrokePath();
   void Handle_FillStrokePath();
   void Handle_CloseEOFillStrokePath();
@@ -194,34 +203,40 @@
   void Handle_Invalid();
 
   UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<CPDF_Dictionary> const m_pPageResources;
-  UnownedPtr<CPDF_Dictionary> const m_pParentResources;
-  UnownedPtr<CPDF_Dictionary> m_pResources;
+  RetainPtr<CPDF_Dictionary> const m_pPageResources;
+  RetainPtr<CPDF_Dictionary> const m_pParentResources;
+  RetainPtr<CPDF_Dictionary> const m_pResources;
   UnownedPtr<CPDF_PageObjectHolder> const m_pObjectHolder;
   UnownedPtr<std::set<const uint8_t*>> const m_ParsedSet;
   CFX_Matrix m_mtContentToUser;
   const CFX_FloatRect m_BBox;
-  ContentParam m_ParamBuf[kParamBufSize];
-  uint32_t m_ParamStartPos;
-  uint32_t m_ParamCount;
-  CPDF_StreamParser* m_pSyntax;
+  uint32_t m_ParamStartPos = 0;
+  uint32_t m_ParamCount = 0;
+  UnownedPtr<CPDF_StreamParser> m_pSyntax;
   std::unique_ptr<CPDF_AllStates> m_pCurStates;
-  CPDF_ContentMark m_CurContentMark;
+  std::stack<std::unique_ptr<CPDF_ContentMarks>> m_ContentMarksStack;
   std::vector<std::unique_ptr<CPDF_TextObject>> m_ClipTextList;
   UnownedPtr<CPDF_TextObject> m_pLastTextObject;
-  float m_DefFontSize;
   std::vector<FX_PATHPOINT> m_PathPoints;
-  float m_PathStartX;
-  float m_PathStartY;
-  float m_PathCurrentX;
-  float m_PathCurrentY;
-  uint8_t m_PathClipType;
+  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;
   ByteString m_LastImageName;
   RetainPtr<CPDF_Image> m_pLastImage;
-  bool m_bColored;
-  float m_Type3Data[6];
-  bool m_bResourceMissing;
+  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];
+
+  // The merged stream offsets at which a content stream ends and another
+  // begins.
+  std::vector<uint32_t> m_StreamStartOffsets;
+
+  // The merged stream offset at which the last |m_pSyntax| started parsing.
+  uint32_t m_StartParseOffset = 0;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_STREAMCONTENTPARSER_H_
diff --git a/core/fpdfapi/page/cpdf_streamparser.cpp b/core/fpdfapi/page/cpdf_streamparser.cpp
index 2c7e025..25172a6 100644
--- a/core/fpdfapi/page/cpdf_streamparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamparser.cpp
@@ -13,7 +13,7 @@
 #include <sstream>
 #include <utility>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "constants/stream_dict_common.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
@@ -26,171 +26,160 @@
 #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/codec/ccodec_jpegmodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/jpeg/jpegmodule.h"
+#include "core/fxcodec/scanlinedecoder.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"
 
 namespace {
 
 const uint32_t kMaxNestedParsingLevel = 512;
-const uint32_t kMaxWordBuffer = 256;
 const size_t kMaxStringLength = 32767;
 
-uint32_t DecodeAllScanlines(std::unique_ptr<CCodec_ScanlineDecoder> pDecoder,
-                            uint8_t** dest_buf,
-                            uint32_t* dest_size) {
+const char kTrue[] = "true";
+const char kFalse[] = "false";
+const char kNull[] = "null";
+
+uint32_t DecodeAllScanlines(std::unique_ptr<ScanlineDecoder> pDecoder) {
   if (!pDecoder)
     return FX_INVALID_OFFSET;
+
   int ncomps = pDecoder->CountComps();
   int bpc = pDecoder->GetBPC();
   int width = pDecoder->GetWidth();
   int height = pDecoder->GetHeight();
-  int pitch = (width * ncomps * bpc + 7) / 8;
-  if (height == 0 || pitch > (1 << 30) / height)
+  if (width <= 0 || height <= 0)
     return FX_INVALID_OFFSET;
 
-  *dest_buf = FX_Alloc2D(uint8_t, pitch, height);
-  *dest_size = pitch * height;  // Safe since checked alloc returned.
-  for (int row = 0; row < height; ++row) {
-    const uint8_t* pLine = pDecoder->GetScanline(row);
-    if (!pLine)
-      break;
+  FX_SAFE_UINT32 size = fxcodec::CalculatePitch8(bpc, ncomps, width);
+  size *= height;
+  if (size.ValueOrDefault(0) == 0)
+    return FX_INVALID_OFFSET;
 
-    memcpy(*dest_buf + row * pitch, pLine, pitch);
+  for (int row = 0; row < height; ++row) {
+    if (!pDecoder->GetScanline(row))
+      break;
   }
   return pDecoder->GetSrcOffset();
 }
 
-uint32_t DecodeInlineStream(const uint8_t* src_buf,
-                            uint32_t limit,
+uint32_t DecodeInlineStream(pdfium::span<const uint8_t> src_span,
                             int width,
                             int height,
                             const ByteString& decoder,
-                            CPDF_Dictionary* pParam,
-                            uint8_t** dest_buf,
-                            uint32_t* dest_size) {
-  if (decoder == "CCITTFaxDecode" || decoder == "CCF") {
-    std::unique_ptr<CCodec_ScanlineDecoder> pDecoder =
-        FPDFAPI_CreateFaxDecoder(src_buf, limit, width, height, pParam);
-    return DecodeAllScanlines(std::move(pDecoder), dest_buf, dest_size);
+                            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");
+
+  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);
   }
-  if (decoder == "ASCII85Decode" || decoder == "A85")
-    return A85Decode(src_buf, limit, dest_buf, dest_size);
-  if (decoder == "ASCIIHexDecode" || decoder == "AHx")
-    return HexDecode(src_buf, limit, dest_buf, dest_size);
-  if (decoder == "FlateDecode" || decoder == "Fl") {
-    return FPDFAPI_FlateOrLZWDecode(false, src_buf, limit, pParam, *dest_size,
-                                    dest_buf, dest_size);
+  if (decoder == "LZWDecode") {
+    return FlateOrLZWDecode(true, src_span, pParam, 0, &ignored_result,
+                            &ignored_size);
   }
-  if (decoder == "LZWDecode" || decoder == "LZW") {
-    return FPDFAPI_FlateOrLZWDecode(true, src_buf, limit, pParam, 0, dest_buf,
-                                    dest_size);
-  }
-  if (decoder == "DCTDecode" || decoder == "DCT") {
-    std::unique_ptr<CCodec_ScanlineDecoder> pDecoder =
-        CPDF_ModuleMgr::Get()->GetJpegModule()->CreateDecoder(
-            src_buf, limit, width, height, 0,
+  if (decoder == "DCTDecode") {
+    std::unique_ptr<ScanlineDecoder> pDecoder =
+        fxcodec::ModuleMgr::GetInstance()->GetJpegModule()->CreateDecoder(
+            src_span, width, height, 0,
             !pParam || pParam->GetIntegerFor("ColorTransform", 1));
-    return DecodeAllScanlines(std::move(pDecoder), dest_buf, dest_size);
+    return DecodeAllScanlines(std::move(pDecoder));
   }
-  if (decoder == "RunLengthDecode" || decoder == "RL")
-    return RunLengthDecode(src_buf, limit, dest_buf, dest_size);
-  *dest_size = 0;
-  *dest_buf = 0;
-  return 0xFFFFFFFF;
+  if (decoder == "CCITTFaxDecode") {
+    std::unique_ptr<ScanlineDecoder> pDecoder =
+        CreateFaxDecoder(src_span, width, height, pParam);
+    return DecodeAllScanlines(std::move(pDecoder));
+  }
+
+  if (decoder == "ASCII85Decode")
+    return A85Decode(src_span, &ignored_result, &ignored_size);
+  if (decoder == "ASCIIHexDecode")
+    return HexDecode(src_span, &ignored_result, &ignored_size);
+  if (decoder == "RunLengthDecode")
+    return RunLengthDecode(src_span, &ignored_result, &ignored_size);
+
+  return FX_INVALID_OFFSET;
 }
 
 }  // namespace
 
-CPDF_StreamParser::CPDF_StreamParser(const uint8_t* pData, uint32_t dwSize)
-    : m_pBuf(pData), m_Size(dwSize), m_Pos(0), m_pPool(nullptr) {}
+CPDF_StreamParser::CPDF_StreamParser(pdfium::span<const uint8_t> span)
+    : m_pBuf(span) {}
 
-CPDF_StreamParser::CPDF_StreamParser(const uint8_t* pData,
-                                     uint32_t dwSize,
+CPDF_StreamParser::CPDF_StreamParser(pdfium::span<const uint8_t> span,
                                      const WeakPtr<ByteStringPool>& pPool)
-    : m_pBuf(pData), m_Size(dwSize), m_Pos(0), m_pPool(pPool) {}
+    : m_pPool(pPool), m_pBuf(span) {}
 
 CPDF_StreamParser::~CPDF_StreamParser() {}
 
-std::unique_ptr<CPDF_Stream> CPDF_StreamParser::ReadInlineStream(
+RetainPtr<CPDF_Stream> CPDF_StreamParser::ReadInlineStream(
     CPDF_Document* pDoc,
-    std::unique_ptr<CPDF_Dictionary> pDict,
-    CPDF_Object* pCSObj) {
-  if (m_Pos == m_Size)
-    return nullptr;
-
-  if (PDFCharIsWhitespace(m_pBuf[m_Pos]))
+    RetainPtr<CPDF_Dictionary> pDict,
+    const CPDF_Object* pCSObj) {
+  if (m_Pos < m_pBuf.size() && PDFCharIsWhitespace(m_pBuf[m_Pos]))
     m_Pos++;
 
-  ByteString Decoder;
-  CPDF_Dictionary* pParam = nullptr;
+  if (m_Pos == m_pBuf.size())
+    return nullptr;
+
+  ByteString decoder;
+  const CPDF_Dictionary* pParam = nullptr;
   CPDF_Object* pFilter = pDict->GetDirectObjectFor("Filter");
   if (pFilter) {
-    if (CPDF_Array* pArray = pFilter->AsArray()) {
-      Decoder = pArray->GetStringAt(0);
-      CPDF_Array* pParams = pDict->GetArrayFor("DecodeParms");
+    const CPDF_Array* pArray = pFilter->AsArray();
+    if (pArray) {
+      decoder = pArray->GetStringAt(0);
+      const CPDF_Array* pParams =
+          pDict->GetArrayFor(pdfium::stream::kDecodeParms);
       if (pParams)
         pParam = pParams->GetDictAt(0);
     } else {
-      Decoder = pFilter->GetString();
-      pParam = pDict->GetDictFor("DecodeParms");
+      decoder = pFilter->GetString();
+      pParam = pDict->GetDictFor(pdfium::stream::kDecodeParms);
     }
   }
   uint32_t width = pDict->GetIntegerFor("Width");
   uint32_t height = pDict->GetIntegerFor("Height");
-  uint32_t OrigSize = 0;
+  uint32_t bpc = 1;
+  uint32_t nComponents = 1;
   if (pCSObj) {
-    uint32_t bpc = pDict->GetIntegerFor("BitsPerComponent");
-    uint32_t nComponents = 1;
-    CPDF_ColorSpace* pCS = pDoc->LoadColorSpace(pCSObj);
-    if (pCS) {
-      nComponents = pCS->CountComponents();
-      pDoc->GetPageData()->ReleaseColorSpace(pCSObj);
-    } else {
-      nComponents = 3;
-    }
-    uint32_t pitch = width;
-    if (bpc && pitch > INT_MAX / bpc)
-      return nullptr;
-
-    pitch *= bpc;
-    if (nComponents && pitch > INT_MAX / nComponents)
-      return nullptr;
-
-    pitch *= nComponents;
-    if (pitch > INT_MAX - 7)
-      return nullptr;
-
-    pitch += 7;
-    pitch /= 8;
-    OrigSize = pitch;
-  } else {
-    if (width > INT_MAX - 7)
-      return nullptr;
-
-    OrigSize = ((width + 7) / 8);
+    RetainPtr<CPDF_ColorSpace> pCS =
+        CPDF_DocPageData::FromDocument(pDoc)->GetColorSpace(pCSObj, nullptr);
+    nComponents = pCS ? pCS->CountComponents() : 3;
+    bpc = pDict->GetIntegerFor("BitsPerComponent");
   }
-  if (height && OrigSize > INT_MAX / height)
+  FX_SAFE_UINT32 size = fxcodec::CalculatePitch8(bpc, nComponents, width);
+  size *= height;
+  if (!size.IsValid())
     return nullptr;
 
-  OrigSize *= height;
+  uint32_t dwOrigSize = size.ValueOrDie();
   std::unique_ptr<uint8_t, FxFreeDeleter> pData;
   uint32_t dwStreamSize;
-  if (Decoder.IsEmpty()) {
-    if (OrigSize > m_Size - m_Pos)
-      OrigSize = m_Size - m_Pos;
-    pData.reset(FX_Alloc(uint8_t, OrigSize));
-    memcpy(pData.get(), m_pBuf + m_Pos, OrigSize);
-    dwStreamSize = OrigSize;
-    m_Pos += OrigSize;
+  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());
+    dwStreamSize = dwOrigSize;
+    m_Pos += dwOrigSize;
   } else {
-    uint8_t* pIgnore = nullptr;
-    uint32_t dwDestSize = OrigSize;
-    dwStreamSize =
-        DecodeInlineStream(m_pBuf + m_Pos, m_Size - m_Pos, width, height,
-                           Decoder, pParam, &pIgnore, &dwDestSize);
-    FX_Free(pIgnore);
-    if (static_cast<int>(dwStreamSize) < 0)
+    dwStreamSize = DecodeInlineStream(m_pBuf.subspan(m_Pos), width, height,
+                                      decoder, pParam, dwOrigSize);
+    if (!pdfium::base::IsValueInRangeForNumericType<int>(dwStreamSize))
       return nullptr;
 
     uint32_t dwSavePos = m_Pos;
@@ -213,21 +202,22 @@
     }
     m_Pos = dwSavePos;
     pData.reset(FX_Alloc(uint8_t, dwStreamSize));
-    memcpy(pData.get(), m_pBuf + m_Pos, dwStreamSize);
+    auto copy_span = m_pBuf.subspan(m_Pos, dwStreamSize);
+    memcpy(pData.get(), copy_span.data(), copy_span.size());
     m_Pos += dwStreamSize;
   }
   pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(dwStreamSize));
-  return pdfium::MakeUnique<CPDF_Stream>(std::move(pData), dwStreamSize,
+  return pdfium::MakeRetain<CPDF_Stream>(std::move(pData), dwStreamSize,
                                          std::move(pDict));
 }
 
 CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement() {
-  m_pLastObj.reset();
+  m_pLastObj.Reset();
   m_WordSize = 0;
   if (!PositionIsInBounds())
     return EndOfData;
 
-  int ch = m_pBuf[m_Pos++];
+  uint8_t ch = m_pBuf[m_Pos++];
   while (1) {
     while (PDFCharIsWhitespace(ch)) {
       if (!PositionIsInBounds())
@@ -257,7 +247,7 @@
 
   bool bIsNumber = true;
   while (1) {
-    if (m_WordSize < kMaxWordBuffer)
+    if (m_WordSize < kMaxWordLength)
       m_WordBuffer[m_WordSize++] = ch;
 
     if (!PDFCharIsNumeric(ch))
@@ -282,24 +272,24 @@
     return Name;
 
   if (m_WordSize == 4) {
-    if (memcmp(m_WordBuffer, "true", 4) == 0) {
-      m_pLastObj = pdfium::MakeUnique<CPDF_Boolean>(true);
+    if (WordBufferMatches(kTrue)) {
+      m_pLastObj = pdfium::MakeRetain<CPDF_Boolean>(true);
       return Others;
     }
-    if (memcmp(m_WordBuffer, "null", 4) == 0) {
-      m_pLastObj = pdfium::MakeUnique<CPDF_Null>();
+    if (WordBufferMatches(kNull)) {
+      m_pLastObj = pdfium::MakeRetain<CPDF_Null>();
       return Others;
     }
   } else if (m_WordSize == 5) {
-    if (memcmp(m_WordBuffer, "false", 5) == 0) {
-      m_pLastObj = pdfium::MakeUnique<CPDF_Boolean>(false);
+    if (WordBufferMatches(kFalse)) {
+      m_pLastObj = pdfium::MakeRetain<CPDF_Boolean>(false);
       return Others;
     }
   }
   return Keyword;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_StreamParser::ReadNextObject(
+RetainPtr<CPDF_Object> CPDF_StreamParser::ReadNextObject(
     bool bAllowNestedArray,
     bool bInArray,
     uint32_t dwRecursionLevel) {
@@ -311,7 +301,7 @@
 
   if (bIsNumber) {
     m_WordBuffer[m_WordSize] = 0;
-    return pdfium::MakeUnique<CPDF_Number>(
+    return pdfium::MakeRetain<CPDF_Number>(
         ByteStringView(m_WordBuffer, m_WordSize));
   }
 
@@ -319,19 +309,19 @@
   if (first_char == '/') {
     ByteString name =
         PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1));
-    return pdfium::MakeUnique<CPDF_Name>(m_pPool, name);
+    return pdfium::MakeRetain<CPDF_Name>(m_pPool, name);
   }
 
   if (first_char == '(') {
     ByteString str = ReadString();
-    return pdfium::MakeUnique<CPDF_String>(m_pPool, str, false);
+    return pdfium::MakeRetain<CPDF_String>(m_pPool, str, false);
   }
 
   if (first_char == '<') {
     if (m_WordSize == 1)
-      return pdfium::MakeUnique<CPDF_String>(m_pPool, ReadHexString(), true);
+      return pdfium::MakeRetain<CPDF_String>(m_pPool, ReadHexString(), true);
 
-    auto pDict = pdfium::MakeUnique<CPDF_Dictionary>(m_pPool);
+    auto pDict = pdfium::MakeRetain<CPDF_Dictionary>(m_pPool);
     while (1) {
       GetNextWord(bIsNumber);
       if (m_WordSize == 2 && m_WordBuffer[0] == '>')
@@ -342,7 +332,7 @@
 
       ByteString key =
           PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1));
-      std::unique_ptr<CPDF_Object> pObj =
+      RetainPtr<CPDF_Object> pObj =
           ReadNextObject(true, bInArray, dwRecursionLevel + 1);
       if (!pObj)
         return nullptr;
@@ -350,16 +340,16 @@
       if (!key.IsEmpty())
         pDict->SetFor(key, std::move(pObj));
     }
-    return std::move(pDict);
+    return pDict;
   }
 
   if (first_char == '[') {
     if ((!bAllowNestedArray && bInArray))
       return nullptr;
 
-    auto pArray = pdfium::MakeUnique<CPDF_Array>();
+    auto pArray = pdfium::MakeRetain<CPDF_Array>();
     while (1) {
-      std::unique_ptr<CPDF_Object> pObj =
+      RetainPtr<CPDF_Object> pObj =
           ReadNextObject(bAllowNestedArray, true, dwRecursionLevel + 1);
       if (pObj) {
         pArray->Add(std::move(pObj));
@@ -368,19 +358,15 @@
       if (!m_WordSize || m_WordBuffer[0] == ']')
         break;
     }
-    return std::move(pArray);
+    return pArray;
   }
 
-  if (m_WordSize == 5 && !memcmp(m_WordBuffer, "false", 5))
-    return pdfium::MakeUnique<CPDF_Boolean>(false);
-
-  if (m_WordSize == 4) {
-    if (memcmp(m_WordBuffer, "true", 4) == 0)
-      return pdfium::MakeUnique<CPDF_Boolean>(true);
-    if (memcmp(m_WordBuffer, "null", 4) == 0)
-      return pdfium::MakeUnique<CPDF_Null>();
-  }
-
+  if (WordBufferMatches(kFalse))
+    return pdfium::MakeRetain<CPDF_Boolean>(false);
+  if (WordBufferMatches(kTrue))
+    return pdfium::MakeRetain<CPDF_Boolean>(true);
+  if (WordBufferMatches(kNull))
+    return pdfium::MakeRetain<CPDF_Null>();
   return nullptr;
 }
 
@@ -391,7 +377,7 @@
   if (!PositionIsInBounds())
     return;
 
-  int ch = m_pBuf[m_Pos++];
+  uint8_t ch = m_pBuf[m_Pos++];
   while (1) {
     while (PDFCharIsWhitespace(ch)) {
       if (!PositionIsInBounds()) {
@@ -424,8 +410,7 @@
           m_Pos--;
           return;
         }
-
-        if (m_WordSize < kMaxWordBuffer)
+        if (m_WordSize < kMaxWordLength)
           m_WordBuffer[m_WordSize++] = ch;
       }
     } else if (ch == '<') {
@@ -449,13 +434,13 @@
   }
 
   while (1) {
-    if (m_WordSize < kMaxWordBuffer)
+    if (m_WordSize < kMaxWordLength)
       m_WordBuffer[m_WordSize++] = ch;
     if (!PDFCharIsNumeric(ch))
       bIsNumber = false;
-
     if (!PositionIsInBounds())
       return;
+
     ch = m_pBuf[m_Pos++];
     if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
       m_Pos--;
@@ -478,6 +463,9 @@
       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));
@@ -494,7 +482,7 @@
         }
         break;
       case 1:
-        if (ch >= '0' && ch <= '7') {
+        if (FXSYS_IsOctalDigit(ch)) {
           iEscCode = FXSYS_DecimalCharToInt(static_cast<char>(ch));
           status = 2;
           break;
@@ -521,7 +509,7 @@
         status = 0;
         break;
       case 2:
-        if (ch >= '0' && ch <= '7') {
+        if (FXSYS_IsOctalDigit(ch)) {
           iEscCode =
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<char>(ch));
           status = 3;
@@ -532,7 +520,7 @@
         }
         break;
       case 3:
-        if (ch >= '0' && ch <= '7') {
+        if (FXSYS_IsOctalDigit(ch)) {
           iEscCode =
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<char>(ch));
           buf << static_cast<char>(iEscCode);
@@ -557,6 +545,9 @@
   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));
@@ -570,8 +561,7 @@
   bool bFirst = true;
   int code = 0;
   while (PositionIsInBounds()) {
-    int ch = m_pBuf[m_Pos++];
-
+    uint8_t ch = m_pBuf[m_Pos++];
     if (ch == '>')
       break;
 
@@ -590,11 +580,19 @@
   if (!bFirst)
     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));
 }
 
 bool CPDF_StreamParser::PositionIsInBounds() const {
-  return m_Pos < m_Size;
+  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 158726b..6051b8c 100644
--- a/core/fpdfapi/page/cpdf_streamparser.h
+++ b/core/fpdfapi/page/cpdf_streamparser.h
@@ -10,20 +10,21 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_object.h"
-#include "core/fpdfapi/parser/cpdf_stream.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_Object;
+class CPDF_Stream;
 
 class CPDF_StreamParser {
  public:
   enum SyntaxType { EndOfData, Number, Keyword, Name, Others };
 
-  CPDF_StreamParser(const uint8_t* pData, uint32_t dwSize);
-  CPDF_StreamParser(const uint8_t* pData,
-                    uint32_t dwSize,
+  explicit CPDF_StreamParser(pdfium::span<const uint8_t> span);
+  CPDF_StreamParser(pdfium::span<const uint8_t> span,
                     const WeakPtr<ByteStringPool>& pPool);
   ~CPDF_StreamParser();
 
@@ -33,30 +34,30 @@
   }
   uint32_t GetPos() const { return m_Pos; }
   void SetPos(uint32_t pos) { m_Pos = pos; }
-  std::unique_ptr<CPDF_Object> GetObject() { return std::move(m_pLastObj); }
-  std::unique_ptr<CPDF_Object> ReadNextObject(bool bAllowNestedArray,
-                                              bool bInArray,
-                                              uint32_t dwRecursionLevel);
-  std::unique_ptr<CPDF_Stream> ReadInlineStream(
-      CPDF_Document* pDoc,
-      std::unique_ptr<CPDF_Dictionary> pDict,
-      CPDF_Object* pCSObj);
+  const RetainPtr<CPDF_Object>& GetObject() const { return m_pLastObj; }
+  RetainPtr<CPDF_Object> ReadNextObject(bool bAllowNestedArray,
+                                        bool bInArray,
+                                        uint32_t dwRecursionLevel);
+  RetainPtr<CPDF_Stream> ReadInlineStream(CPDF_Document* pDoc,
+                                          RetainPtr<CPDF_Dictionary> pDict,
+                                          const CPDF_Object* pCSObj);
 
  private:
   friend class cpdf_streamparser_ReadHexString_Test;
+  static const uint32_t kMaxWordLength = 255;
 
   void GetNextWord(bool& bIsNumber);
   ByteString ReadString();
   ByteString ReadHexString();
   bool PositionIsInBounds() const;
+  bool WordBufferMatches(const char* pWord) const;
 
-  const uint8_t* m_pBuf;
-  uint32_t m_Size;  // Length in bytes of m_pBuf.
-  uint32_t m_Pos;   // Current byte position within m_pBuf.
-  uint8_t m_WordBuffer[256];
-  uint32_t m_WordSize;
-  std::unique_ptr<CPDF_Object> m_pLastObj;
+  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.
 };
 
 #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 40a41be..64bb57c 100644
--- a/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
@@ -9,7 +9,7 @@
   {
     // Position out of bounds.
     uint8_t data[] = "12ab>";
-    CPDF_StreamParser parser(data, 5);
+    CPDF_StreamParser parser(data);
     parser.SetPos(6);
     EXPECT_EQ("", parser.ReadHexString());
   }
@@ -17,7 +17,7 @@
   {
     // Regular conversion.
     uint8_t data[] = "1A2b>abcd";
-    CPDF_StreamParser parser(data, 5);
+    CPDF_StreamParser parser(data);
     EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
     EXPECT_EQ(5u, parser.GetPos());
   }
@@ -25,7 +25,7 @@
   {
     // Missing ending >
     uint8_t data[] = "1A2b";
-    CPDF_StreamParser parser(data, 5);
+    CPDF_StreamParser parser(data);
     EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
     EXPECT_EQ(5u, parser.GetPos());
   }
@@ -33,14 +33,14 @@
   {
     // Uneven number of bytes.
     uint8_t data[] = "1A2>asdf";
-    CPDF_StreamParser parser(data, 5);
+    CPDF_StreamParser parser(data);
     EXPECT_EQ("\x1a\x20", parser.ReadHexString());
     EXPECT_EQ(4u, parser.GetPos());
   }
 
   {
     uint8_t data[] = ">";
-    CPDF_StreamParser parser(data, 5);
+    CPDF_StreamParser parser(data);
     EXPECT_EQ("", parser.ReadHexString());
     EXPECT_EQ(1u, parser.GetPos());
   }
diff --git a/core/fpdfapi/page/cpdf_textobject.cpp b/core/fpdfapi/page/cpdf_textobject.cpp
index 0d60d50..077e5ed 100644
--- a/core/fpdfapi/page/cpdf_textobject.cpp
+++ b/core/fpdfapi/page/cpdf_textobject.cpp
@@ -12,13 +12,17 @@
 #include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+
+#define ISLATINWORD(u) (u != 0x20 && u <= 0x28FF)
 
 CPDF_TextObjectItem::CPDF_TextObjectItem() : m_CharCode(0) {}
 
 CPDF_TextObjectItem::~CPDF_TextObjectItem() = default;
 
-CPDF_TextObject::CPDF_TextObject() {}
+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,
@@ -32,15 +36,14 @@
 
 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_Font* pFont = m_TextState.GetFont();
-  if (!pFont->IsCIDFont())
-    return;
-  if (!pFont->AsCIDFont()->IsVertWriting())
+  RetainPtr<CPDF_Font> pFont = GetFont();
+  if (!pFont->IsCIDFont() || !pFont->AsCIDFont()->IsVertWriting())
     return;
 
   uint16_t CID = pFont->AsCIDFont()->CIDFromCharCode(pInfo->m_CharCode);
@@ -50,7 +53,7 @@
   short vy;
   pFont->AsCIDFont()->GetVertOrigin(CID, vx, vy);
 
-  float fontsize = m_TextState.GetFontSize();
+  float fontsize = GetFontSize();
   pInfo->m_Origin.x -= fontsize * vx / 1000;
   pInfo->m_Origin.y -= fontsize * vy / 1000;
 }
@@ -98,6 +101,59 @@
   }
 }
 
+int CPDF_TextObject::CountWords() const {
+  RetainPtr<CPDF_Font> pFont = GetFont();
+  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);
+
+    WideString swUnicode = pFont->UnicodeFromCharCode(charcode);
+    uint16_t unicode = 0;
+    if (swUnicode.GetLength() > 0)
+      unicode = swUnicode[0];
+
+    bool bIsLatin = ISLATINWORD(unicode);
+    if (bIsLatin && bInLatinWord)
+      continue;
+
+    bInLatinWord = bIsLatin;
+    if (unicode != 0x20)
+      nWords++;
+  }
+
+  return nWords;
+}
+
+WideString CPDF_TextObject::GetWordString(int nWordIndex) const {
+  RetainPtr<CPDF_Font> pFont = GetFont();
+  WideString swRet;
+  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);
+
+    WideString swUnicode = pFont->UnicodeFromCharCode(charcode);
+    uint16_t unicode = 0;
+    if (swUnicode.GetLength() > 0)
+      unicode = swUnicode[0];
+
+    bool bIsLatin = ISLATINWORD(unicode);
+    if (!bIsLatin || !bInLatinWord) {
+      bInLatinWord = bIsLatin;
+      if (unicode != 0x20)
+        nWords++;
+    }
+    if (nWords - 1 == nWordIndex)
+      swRet += unicode;
+  }
+  return swRet;
+}
+
 std::unique_ptr<CPDF_TextObject> CPDF_TextObject::Clone() const {
   auto obj = pdfium::MakeUnique<CPDF_TextObject>();
   obj->CopyData(this);
@@ -112,8 +168,7 @@
 }
 
 void CPDF_TextObject::Transform(const CFX_Matrix& matrix) {
-  CFX_Matrix text_matrix = GetTextMatrix();
-  text_matrix.Concat(matrix);
+  CFX_Matrix text_matrix = GetTextMatrix() * matrix;
 
   float* pTextMatrix = m_TextState.GetMutableMatrix();
   pTextMatrix[0] = text_matrix.a;
@@ -144,40 +199,41 @@
 }
 
 void CPDF_TextObject::SetSegments(const ByteString* pStrs,
-                                  const float* pKerning,
-                                  int nsegs) {
+                                  const std::vector<float>& kernings,
+                                  size_t nSegs) {
   m_CharCodes.clear();
   m_CharPos.clear();
-  CPDF_Font* pFont = m_TextState.GetFont();
+  RetainPtr<CPDF_Font> pFont = GetFont();
   int nChars = 0;
-  for (int i = 0; i < nsegs; ++i)
-    nChars += pFont->CountChar(pStrs[i].c_str(), pStrs[i].GetLength());
-  nChars += nsegs - 1;
+  for (size_t i = 0; i < nSegs; ++i)
+    nChars += pFont->CountChar(pStrs[i].AsStringView());
+  nChars += nSegs - 1;
   m_CharCodes.resize(nChars);
   m_CharPos.resize(nChars - 1);
-  int index = 0;
-  for (int i = 0; i < nsegs; ++i) {
-    const char* segment = pStrs[i].c_str();
-    int len = pStrs[i].GetLength();
-    int offset = 0;
-    while (offset < len)
-      m_CharCodes[index++] = pFont->GetNextChar(segment, len, offset);
-    if (i != nsegs - 1) {
-      m_CharPos[index - 1] = pKerning[i];
+  size_t index = 0;
+  for (size_t i = 0; i < nSegs; ++i) {
+    ByteStringView segment = pStrs[i].AsStringView();
+    size_t offset = 0;
+    while (offset < segment.GetLength()) {
+      ASSERT(index < m_CharCodes.size());
+      m_CharCodes[index++] = pFont->GetNextChar(segment, &offset);
+    }
+    if (i != nSegs - 1) {
+      m_CharPos[index - 1] = kernings[i];
       m_CharCodes[index++] = CPDF_Font::kInvalidCharCode;
     }
   }
 }
 
 void CPDF_TextObject::SetText(const ByteString& str) {
-  SetSegments(&str, nullptr, 1);
+  SetSegments(&str, std::vector<float>(), 1);
   RecalcPositionData();
   SetDirty(true);
 }
 
 float CPDF_TextObject::GetCharWidth(uint32_t charcode) const {
-  float fontsize = m_TextState.GetFontSize() / 1000;
-  CPDF_Font* pFont = m_TextState.GetFont();
+  float fontsize = GetFontSize() / 1000;
+  RetainPtr<CPDF_Font> pFont = GetFont();
   bool bVertWriting = false;
   CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
   if (pCIDFont)
@@ -189,7 +245,7 @@
   return pCIDFont->GetVertWidth(CID) * fontsize;
 }
 
-CPDF_Font* CPDF_TextObject::GetFont() const {
+RetainPtr<CPDF_Font> CPDF_TextObject::GetFont() const {
   return m_TextState.GetFont();
 }
 
@@ -197,19 +253,23 @@
   return m_TextState.GetFontSize();
 }
 
+TextRenderingMode CPDF_TextObject::GetTextRenderMode() const {
+  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;
-  CPDF_Font* pFont = m_TextState.GetFont();
+  RetainPtr<CPDF_Font> pFont = GetFont();
   bool bVertWriting = false;
   CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
   if (pCIDFont)
     bVertWriting = pCIDFont->IsVertWriting();
 
-  float fontsize = m_TextState.GetFontSize();
+  float fontsize = GetFontSize();
   for (size_t i = 0; i < m_CharCodes.size(); ++i) {
     uint32_t charcode = m_CharCodes[i];
     if (i > 0) {
@@ -268,32 +328,21 @@
     min_y = min_y * fontsize / 1000;
     max_y = max_y * fontsize / 1000;
   }
-  std::tie(m_Left, m_Right, m_Top, m_Bottom) =
-      GetTextMatrix().TransformRect(min_x, max_x, max_y, min_y);
+  SetRect(
+      GetTextMatrix().TransformRect(CFX_FloatRect(min_x, min_y, max_x, max_y)));
 
   if (!TextRenderingModeIsStrokeMode(m_TextState.GetTextMode()))
     return ret;
 
   float half_width = m_GraphState.GetLineWidth() / 2;
-  m_Left -= half_width;
-  m_Right += half_width;
-  m_Top += half_width;
-  m_Bottom -= half_width;
+  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::SetPosition(float x, float y) {
-  float dx = x - m_Pos.x;
-  float dy = y - m_Pos.y;
-  m_Pos.x = x;
-  m_Pos.y = y;
-  m_Left += dx;
-  m_Right += dx;
-  m_Top += dy;
-  m_Bottom += dy;
-}
-
 void CPDF_TextObject::RecalcPositionData() {
   CalcPositionData(1);
 }
diff --git a/core/fpdfapi/page/cpdf_textobject.h b/core/fpdfapi/page/cpdf_textobject.h
index a6fc623..ad9918e 100644
--- a/core/fpdfapi/page/cpdf_textobject.h
+++ b/core/fpdfapi/page/cpdf_textobject.h
@@ -13,6 +13,7 @@
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_TextObjectItem {
  public:
@@ -23,8 +24,9 @@
   CFX_PointF m_Origin;
 };
 
-class CPDF_TextObject : public CPDF_PageObject {
+class CPDF_TextObject final : public CPDF_PageObject {
  public:
+  explicit CPDF_TextObject(int32_t content_stream);
   CPDF_TextObject();
   ~CPDF_TextObject() override;
 
@@ -44,22 +46,28 @@
   void GetCharInfo(size_t index, uint32_t* charcode, float* kerning) const;
   void GetCharInfo(size_t index, CPDF_TextObjectItem* pInfo) const;
   float GetCharWidth(uint32_t charcode) const;
+  int CountWords() const;
+  WideString GetWordString(int nWordIndex) const;
 
   CFX_PointF GetPos() const { return m_Pos; }
   CFX_Matrix GetTextMatrix() const;
-  CPDF_Font* GetFont() const;
+
+  RetainPtr<CPDF_Font> GetFont() const;
   float GetFontSize() const;
 
-  void SetText(const ByteString& text);
-  void SetPosition(CFX_PointF pos) { m_Pos = pos; }
-  void SetPosition(float x, float y);
+  TextRenderingMode GetTextRenderMode() const;
+
+  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; }
 
-  void SetSegments(const ByteString* pStrs, const float* pKerning, int nSegs);
+  void SetSegments(const ByteString* pStrs,
+                   const std::vector<float>& kernings,
+                   size_t nSegs);
   CFX_PointF CalcPositionData(float horz_scale);
 
  private:
diff --git a/core/fpdfapi/page/cpdf_textstate.cpp b/core/fpdfapi/page/cpdf_textstate.cpp
index bd236a2..b8019d5 100644
--- a/core/fpdfapi/page/cpdf_textstate.cpp
+++ b/core/fpdfapi/page/cpdf_textstate.cpp
@@ -10,18 +10,19 @@
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 
-CPDF_TextState::CPDF_TextState() {}
-CPDF_TextState::~CPDF_TextState() {}
+CPDF_TextState::CPDF_TextState() = default;
+
+CPDF_TextState::~CPDF_TextState() = default;
 
 void CPDF_TextState::Emplace() {
   m_Ref.Emplace();
 }
 
-CPDF_Font* CPDF_TextState::GetFont() const {
+RetainPtr<CPDF_Font> CPDF_TextState::GetFont() const {
   return m_Ref.GetObject()->m_pFont;
 }
 
-void CPDF_TextState::SetFont(CPDF_Font* pFont) {
+void CPDF_TextState::SetFont(const RetainPtr<CPDF_Font>& pFont) {
   m_Ref.GetPrivateCopy()->SetFont(pFont);
 }
 
@@ -57,22 +58,10 @@
   m_Ref.GetPrivateCopy()->m_WordSpace = sp;
 }
 
-float CPDF_TextState::GetFontSizeV() const {
-  return m_Ref.GetObject()->GetFontSizeV();
-}
-
 float CPDF_TextState::GetFontSizeH() const {
   return m_Ref.GetObject()->GetFontSizeH();
 }
 
-float CPDF_TextState::GetBaselineAngle() const {
-  return m_Ref.GetObject()->GetBaselineAngle();
-}
-
-float CPDF_TextState::GetShearAngle() const {
-  return m_Ref.GetObject()->GetShearAngle();
-}
-
 TextRenderingMode CPDF_TextState::GetTextMode() const {
   return m_Ref.GetObject()->m_TextMode;
 }
@@ -115,45 +104,27 @@
   for (int i = 0; i < 4; ++i)
     m_CTM[i] = that.m_CTM[i];
 
-  if (m_pDocument && m_pFont)
-    m_pFont = m_pDocument->GetPageData()->GetFont(m_pFont->GetFontDict());
+  if (m_pDocument && m_pFont) {
+    auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get());
+    m_pFont = pPageData->GetFont(m_pFont->GetFontDict());
+  }
 }
 
-CPDF_TextState::TextData::~TextData() {
-  ReleaseFont();
+CPDF_TextState::TextData::~TextData() = default;
+
+RetainPtr<CPDF_TextState::TextData> CPDF_TextState::TextData::Clone() const {
+  return pdfium::MakeRetain<CPDF_TextState::TextData>(*this);
 }
 
-void CPDF_TextState::TextData::SetFont(CPDF_Font* pFont) {
-  ReleaseFont();
+void CPDF_TextState::TextData::SetFont(const RetainPtr<CPDF_Font>& pFont) {
   m_pDocument = pFont ? pFont->GetDocument() : nullptr;
   m_pFont = pFont;
 }
 
-float CPDF_TextState::TextData::GetFontSizeV() const {
-  return fabs(FXSYS_sqrt2(m_Matrix[1], m_Matrix[3]) * m_FontSize);
-}
-
 float CPDF_TextState::TextData::GetFontSizeH() const {
   return fabs(FXSYS_sqrt2(m_Matrix[0], m_Matrix[2]) * m_FontSize);
 }
 
-float CPDF_TextState::TextData::GetBaselineAngle() const {
-  return atan2(m_Matrix[2], m_Matrix[0]);
-}
-
-float CPDF_TextState::TextData::GetShearAngle() const {
-  return GetBaselineAngle() + atan2(m_Matrix[1], m_Matrix[3]);
-}
-
-void CPDF_TextState::TextData::ReleaseFont() {
-  if (!m_pDocument || !m_pFont)
-    return;
-
-  CPDF_DocPageData* pPageData = m_pDocument->GetPageData();
-  if (pPageData && !pPageData->IsForceClear())
-    pPageData->ReleaseFont(m_pFont->GetFontDict());
-}
-
 bool SetTextRenderingModeFromInt(int iMode, TextRenderingMode* mode) {
   if (iMode < 0 || iMode > 7)
     return false;
diff --git a/core/fpdfapi/page/cpdf_textstate.h b/core/fpdfapi/page/cpdf_textstate.h
index aa12807..5cc0c1d 100644
--- a/core/fpdfapi/page/cpdf_textstate.h
+++ b/core/fpdfapi/page/cpdf_textstate.h
@@ -7,6 +7,7 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_TEXTSTATE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_TEXTSTATE_H_
 
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
 #include "core/fxcrt/unowned_ptr.h"
 
@@ -15,6 +16,7 @@
 
 // See PDF Reference 1.7, page 402, table 5.3.
 enum class TextRenderingMode {
+  MODE_UNKNOWN = -1,
   MODE_FILL = 0,
   MODE_STROKE = 1,
   MODE_FILL_STROKE = 2,
@@ -23,6 +25,7 @@
   MODE_STROKE_CLIP = 5,
   MODE_FILL_STROKE_CLIP = 6,
   MODE_CLIP = 7,
+  MODE_LAST = MODE_CLIP,
 };
 
 class CPDF_TextState {
@@ -32,8 +35,8 @@
 
   void Emplace();
 
-  CPDF_Font* GetFont() const;
-  void SetFont(CPDF_Font* pFont);
+  RetainPtr<CPDF_Font> GetFont() const;
+  void SetFont(const RetainPtr<CPDF_Font>& pFont);
 
   float GetFontSize() const;
   void SetFontSize(float size);
@@ -47,10 +50,7 @@
   float GetWordSpace() const;
   void SetWordSpace(float sp);
 
-  float GetFontSizeV() const;
   float GetFontSizeH() const;
-  float GetBaselineAngle() const;
-  float GetShearAngle() const;
 
   TextRenderingMode GetTextMode() const;
   void SetTextMode(TextRenderingMode mode);
@@ -59,19 +59,18 @@
   float* GetMutableCTM();
 
  private:
-  class TextData : public Retainable {
+  class TextData final : public Retainable {
    public:
-    TextData();
-    TextData(const TextData& src);
-    ~TextData() override;
+    template <typename T, typename... Args>
+    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-    void SetFont(CPDF_Font* pFont);
+    RetainPtr<TextData> Clone() const;
+
+    void SetFont(const RetainPtr<CPDF_Font>& pFont);
     float GetFontSizeV() const;
     float GetFontSizeH() const;
-    float GetBaselineAngle() const;
-    float GetShearAngle() const;
 
-    CPDF_Font* m_pFont;
+    RetainPtr<CPDF_Font> m_pFont;
     UnownedPtr<CPDF_Document> m_pDocument;
     float m_FontSize;
     float m_CharSpace;
@@ -81,7 +80,9 @@
     float m_CTM[4];
 
    private:
-    void ReleaseFont();
+    TextData();
+    TextData(const TextData& that);
+    ~TextData() override;
   };
 
   SharedCopyOnWrite<TextData> m_Ref;
diff --git a/core/fpdfapi/page/cpdf_tilingpattern.cpp b/core/fpdfapi/page/cpdf_tilingpattern.cpp
index a525257..46a59db 100644
--- a/core/fpdfapi/page/cpdf_tilingpattern.cpp
+++ b/core/fpdfapi/page/cpdf_tilingpattern.cpp
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
 
+#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"
@@ -16,40 +18,36 @@
                                        CPDF_Object* pPatternObj,
                                        const CFX_Matrix& parentMatrix)
     : CPDF_Pattern(pDoc, pPatternObj, parentMatrix) {
-  assert(document());
+  ASSERT(document());
   m_bColored = pattern_obj()->GetDict()->GetIntegerFor("PaintType") == 1;
   SetPatternToFormMatrix();
 }
 
-CPDF_TilingPattern::~CPDF_TilingPattern() {}
+CPDF_TilingPattern::~CPDF_TilingPattern() = default;
 
 CPDF_TilingPattern* CPDF_TilingPattern::AsTilingPattern() {
   return this;
 }
 
-CPDF_ShadingPattern* CPDF_TilingPattern::AsShadingPattern() {
-  return nullptr;
-}
-
-bool CPDF_TilingPattern::Load() {
-  if (m_pForm)
-    return true;
-
-  CPDF_Dictionary* pDict = pattern_obj()->GetDict();
-  if (!pDict)
-    return false;
-
+std::unique_ptr<CPDF_Form> CPDF_TilingPattern::Load(CPDF_PageObject* pPageObj) {
+  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")));
 
   CPDF_Stream* pStream = pattern_obj()->AsStream();
   if (!pStream)
-    return false;
+    return nullptr;
 
   const CFX_Matrix& matrix = parent_matrix();
-  m_pForm = pdfium::MakeUnique<CPDF_Form>(document(), nullptr, pStream);
-  m_pForm->ParseContentWithParams(nullptr, &matrix, nullptr, 0);
+  auto form = pdfium::MakeUnique<CPDF_Form>(document(), nullptr, pStream);
+
+  CPDF_AllStates allStates;
+  allStates.m_ColorState.Emplace();
+  allStates.m_GraphState.Emplace();
+  allStates.m_TextState.Emplace();
+  allStates.m_GeneralState = pPageObj->m_GeneralState;
+  form->ParseContent(&allStates, &matrix, nullptr);
   m_BBox = pDict->GetRectFor("BBox");
-  return true;
+  return form;
 }
diff --git a/core/fpdfapi/page/cpdf_tilingpattern.h b/core/fpdfapi/page/cpdf_tilingpattern.h
index d9450d7..134da8e 100644
--- a/core/fpdfapi/page/cpdf_tilingpattern.h
+++ b/core/fpdfapi/page/cpdf_tilingpattern.h
@@ -16,31 +16,36 @@
 class CPDF_Document;
 class CPDF_Form;
 class CPDF_Object;
+class CPDF_PageObject;
 
-class CPDF_TilingPattern : public CPDF_Pattern {
+class CPDF_TilingPattern final : public CPDF_Pattern {
  public:
-  CPDF_TilingPattern(CPDF_Document* pDoc,
-                     CPDF_Object* pPatternObj,
-                     const CFX_Matrix& parentMatrix);
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   ~CPDF_TilingPattern() override;
 
+  // CPDF_Pattern:
   CPDF_TilingPattern* AsTilingPattern() override;
-  CPDF_ShadingPattern* AsShadingPattern() override;
 
-  bool Load();
+  std::unique_ptr<CPDF_Form> Load(CPDF_PageObject* pPageObj);
 
   bool colored() const { return m_bColored; }
   const CFX_FloatRect& bbox() const { return m_BBox; }
   float x_step() const { return m_XStep; }
   float y_step() const { return m_YStep; }
-  CPDF_Form* form() const { return m_pForm.get(); }
 
  private:
+  CPDF_TilingPattern(CPDF_Document* pDoc,
+                     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;
-  std::unique_ptr<CPDF_Form> m_pForm;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_TILINGPATTERN_H_
diff --git a/core/fpdfapi/page/cpdf_transferfunc.cpp b/core/fpdfapi/page/cpdf_transferfunc.cpp
new file mode 100644
index 0000000..9e3092b
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_transferfunc.cpp
@@ -0,0 +1,54 @@
+// 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/page/cpdf_transferfunc.h"
+
+#include <utility>
+
+#include "core/fpdfapi/page/cpdf_transferfuncdib.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxge/dib/cfx_dibbase.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),
+      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);
+}
+
+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)]);
+}
+
+RetainPtr<CFX_DIBBase> CPDF_TransferFunc::TranslateImage(
+    const RetainPtr<CFX_DIBBase>& pSrc) {
+  RetainPtr<CPDF_TransferFunc> pHolder(this);
+  return pdfium::MakeRetain<CPDF_TransferFuncDIB>(pSrc, pHolder);
+}
+
+pdfium::span<const uint8_t> CPDF_TransferFunc::GetSamplesR() const {
+  return m_SamplesR;
+}
+
+pdfium::span<const uint8_t> CPDF_TransferFunc::GetSamplesG() const {
+  return m_SamplesG;
+}
+
+pdfium::span<const uint8_t> CPDF_TransferFunc::GetSamplesB() const {
+  return m_SamplesB;
+}
diff --git a/core/fpdfapi/page/cpdf_transferfunc.h b/core/fpdfapi/page/cpdf_transferfunc.h
new file mode 100644
index 0000000..b2d997e
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_transferfunc.h
@@ -0,0 +1,55 @@
+// 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_PAGE_CPDF_TRANSFERFUNC_H_
+#define CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_
+
+#include <vector>
+
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/fx_dib.h"
+#include "third_party/base/span.h"
+
+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);
+
+  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(); }
+
+  // Spans are |kChannelSampleSize| in size.
+  pdfium::span<const uint8_t> GetSamplesR() const;
+  pdfium::span<const uint8_t> GetSamplesG() const;
+  pdfium::span<const uint8_t> GetSamplesB() const;
+
+  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() 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;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_
diff --git a/core/fpdfapi/page/cpdf_transferfuncdib.cpp b/core/fpdfapi/page/cpdf_transferfuncdib.cpp
new file mode 100644
index 0000000..f985616
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_transferfuncdib.cpp
@@ -0,0 +1,205 @@
+// 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/page/cpdf_transferfuncdib.h"
+
+#include <vector>
+
+#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"
+
+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();
+  m_Scanline.resize(m_Pitch);
+}
+
+CPDF_TransferFuncDIB::~CPDF_TransferFuncDIB() = default;
+
+FXDIB_Format CPDF_TransferFuncDIB::GetDestFormat() const {
+  if (m_pSrc->IsAlphaMask())
+    return FXDIB_8bppMask;
+
+#if defined(OS_MACOSX)
+  return m_pSrc->HasAlpha() ? FXDIB_Argb : FXDIB_Rgb32;
+#else
+  return m_pSrc->HasAlpha() ? FXDIB_Argb : FXDIB_Rgb;
+#endif
+}
+
+void CPDF_TransferFuncDIB::TranslateScanline(
+    const uint8_t* src_buf,
+    std::vector<uint8_t>* dest_buf) const {
+  bool bSkip = false;
+  switch (m_pSrc->GetFormat()) {
+    case FXDIB_1bppRgb: {
+      int r0 = m_RampR[0];
+      int g0 = m_RampG[0];
+      int b0 = m_RampB[0];
+      int r1 = m_RampR[255];
+      int g1 = m_RampG[255];
+      int b1 = m_RampB[255];
+      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;
+        } else {
+          (*dest_buf)[index++] = b0;
+          (*dest_buf)[index++] = g0;
+          (*dest_buf)[index++] = r0;
+        }
+#if defined(OS_MACOSX)
+        index++;
+#endif
+      }
+      break;
+    }
+    case FXDIB_1bppMask: {
+      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;
+        else
+          (*dest_buf)[index++] = m0;
+      }
+      break;
+    }
+    case FXDIB_8bppRgb: {
+      FX_ARGB* pPal = m_pSrc->GetPalette();
+      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)];
+        } 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];
+        }
+        src_buf++;
+#if defined(OS_MACOSX)
+        index++;
+#endif
+      }
+      break;
+    }
+    case FXDIB_8bppMask: {
+      int index = 0;
+      for (int i = 0; i < m_Width; i++)
+        (*dest_buf)[index++] = m_RampR[*(src_buf++)];
+      break;
+    }
+    case FXDIB_Rgb: {
+      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)
+        index++;
+#endif
+      }
+      break;
+    }
+    case FXDIB_Rgb32:
+      bSkip = true;
+      FALLTHROUGH;
+    case FXDIB_Argb: {
+      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 (!bSkip) {
+          (*dest_buf)[index++] = *src_buf;
+#if defined(OS_MACOSX)
+        } else {
+          index++;
+#endif
+        }
+        src_buf++;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+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);
+}
diff --git a/core/fpdfapi/page/cpdf_transferfuncdib.h b/core/fpdfapi/page/cpdf_transferfuncdib.h
new file mode 100644
index 0000000..c30718a
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_transferfuncdib.h
@@ -0,0 +1,55 @@
+// 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_PAGE_CPDF_TRANSFERFUNCDIB_H_
+#define CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_
+
+#include <vector>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "third_party/base/span.h"
+
+class CPDF_TransferFunc;
+
+class CPDF_TransferFuncDIB final : public CFX_DIBBase {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  void TranslateScanline(const uint8_t* src_buf,
+                         std::vector<uint8_t>* dest_buf) const;
+  void TranslateDownSamples(uint8_t* dest_buf,
+                            const uint8_t* src_buf,
+                            int pixels,
+                            int Bpp) const;
+
+ private:
+  CPDF_TransferFuncDIB(const RetainPtr<CFX_DIBBase>& pSrc,
+                       const RetainPtr<CPDF_TransferFunc>& pTransferFunc);
+  ~CPDF_TransferFuncDIB() override;
+
+  // 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;
+
+  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;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_
diff --git a/core/fpdfapi/page/cpdf_transparency.cpp b/core/fpdfapi/page/cpdf_transparency.cpp
new file mode 100644
index 0000000..f9be541
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_transparency.cpp
@@ -0,0 +1,9 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/page/cpdf_transparency.h"
+
+CPDF_Transparency::CPDF_Transparency() = default;
+
+CPDF_Transparency::CPDF_Transparency(const CPDF_Transparency& other) = default;
diff --git a/core/fpdfapi/page/cpdf_transparency.h b/core/fpdfapi/page/cpdf_transparency.h
new file mode 100644
index 0000000..6d4972d
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_transparency.h
@@ -0,0 +1,25 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_PAGE_CPDF_TRANSPARENCY_H_
+#define CORE_FPDFAPI_PAGE_CPDF_TRANSPARENCY_H_
+
+class CPDF_Transparency {
+ public:
+  CPDF_Transparency();
+
+  CPDF_Transparency(const CPDF_Transparency& other);
+
+  bool IsGroup() const { return m_bGroup; }
+  bool IsIsolated() const { return m_bIsolated; }
+
+  void SetGroup() { m_bGroup = true; }
+  void SetIsolated() { m_bIsolated = true; }
+
+ private:
+  bool m_bGroup = false;
+  bool m_bIsolated = false;
+};
+
+#endif  // CORE_FPDFAPI_PAGE_CPDF_TRANSPARENCY_H_
diff --git a/core/fpdfapi/page/ipdf_page.h b/core/fpdfapi/page/ipdf_page.h
new file mode 100644
index 0000000..9b28559
--- /dev/null
+++ b/core/fpdfapi/page/ipdf_page.h
@@ -0,0 +1,57 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_PAGE_IPDF_PAGE_H_
+#define CORE_FPDFAPI_PAGE_IPDF_PAGE_H_
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/optional.h"
+
+class CPDF_Document;
+class CPDF_Page;
+
+// Small layering violation, incomplete type and always null if non-XFA.
+class CPDFXFA_Page;
+
+// Interface implemented by both page types (CPDF_Page and CPDFXFA_Page).
+class IPDF_Page : public Retainable {
+ public:
+  // There are actually 3 cases: a PDF page, an XFA page backed by a PDF page,
+  // and an XFA page not backed by a PDF page. AsPDFPage() will return the
+  // PDF page in either of the first two cases. AsXFAPage() is a straight
+  // downcast and is null if not either of the last two cases. Hence, both
+  // of these may return non-null on a given page.
+  virtual CPDF_Page* AsPDFPage() = 0;
+  virtual CPDFXFA_Page* AsXFAPage() = 0;
+
+  virtual CPDF_Document* GetDocument() const = 0;
+
+  virtual float GetPageWidth() const = 0;
+  virtual float GetPageHeight() const = 0;
+  virtual CFX_Matrix GetDisplayMatrix(const FX_RECT& rect,
+                                      int iRotate) const = 0;
+
+  virtual Optional<CFX_PointF> DeviceToPage(
+      const FX_RECT& rect,
+      int rotate,
+      const CFX_PointF& device_point) const = 0;
+
+  virtual Optional<CFX_PointF> PageToDevice(
+      const FX_RECT& rect,
+      int rotate,
+      const CFX_PointF& page_point) const = 0;
+};
+
+inline CPDF_Page* ToPDFPage(IPDF_Page* pBase) {
+  return pBase ? pBase->AsPDFPage() : nullptr;
+}
+
+inline CPDFXFA_Page* ToXFAPage(IPDF_Page* pBase) {
+  return pBase ? pBase->AsXFAPage() : nullptr;
+}
+
+#endif  // CORE_FPDFAPI_PAGE_IPDF_PAGE_H_
diff --git a/core/fpdfapi/parser/Android.bp b/core/fpdfapi/parser/Android.bp
new file mode 100644
index 0000000..6864dfa
--- /dev/null
+++ b/core/fpdfapi/parser/Android.bp
@@ -0,0 +1,25 @@
+cc_library_static {
+    name: "libpdfium-parser",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-fdrm",
+        "libpdfium-fxcodec",
+        "libpdfium-fxcrt",
+    ],
+
+    exclude_srcs: [
+        // pdf_enable_xfa
+        "cpdf_seekablemultistream.cpp",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/core/fpdfapi/parser/BUILD.gn b/core/fpdfapi/parser/BUILD.gn
new file mode 100644
index 0000000..ad8a783
--- /dev/null
+++ b/core/fpdfapi/parser/BUILD.gn
@@ -0,0 +1,143 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+source_set("parser") {
+  sources = [
+    "cfdf_document.cpp",
+    "cfdf_document.h",
+    "cpdf_array.cpp",
+    "cpdf_array.h",
+    "cpdf_boolean.cpp",
+    "cpdf_boolean.h",
+    "cpdf_cross_ref_avail.cpp",
+    "cpdf_cross_ref_avail.h",
+    "cpdf_cross_ref_table.cpp",
+    "cpdf_cross_ref_table.h",
+    "cpdf_crypto_handler.cpp",
+    "cpdf_crypto_handler.h",
+    "cpdf_data_avail.cpp",
+    "cpdf_data_avail.h",
+    "cpdf_dictionary.cpp",
+    "cpdf_dictionary.h",
+    "cpdf_document.cpp",
+    "cpdf_document.h",
+    "cpdf_encryptor.cpp",
+    "cpdf_encryptor.h",
+    "cpdf_flateencoder.cpp",
+    "cpdf_flateencoder.h",
+    "cpdf_hint_tables.cpp",
+    "cpdf_hint_tables.h",
+    "cpdf_indirect_object_holder.cpp",
+    "cpdf_indirect_object_holder.h",
+    "cpdf_linearized_header.cpp",
+    "cpdf_linearized_header.h",
+    "cpdf_name.cpp",
+    "cpdf_name.h",
+    "cpdf_null.cpp",
+    "cpdf_null.h",
+    "cpdf_number.cpp",
+    "cpdf_number.h",
+    "cpdf_object.cpp",
+    "cpdf_object.h",
+    "cpdf_object_avail.cpp",
+    "cpdf_object_avail.h",
+    "cpdf_object_stream.cpp",
+    "cpdf_object_stream.h",
+    "cpdf_object_walker.cpp",
+    "cpdf_object_walker.h",
+    "cpdf_page_object_avail.cpp",
+    "cpdf_page_object_avail.h",
+    "cpdf_parser.cpp",
+    "cpdf_parser.h",
+    "cpdf_read_validator.cpp",
+    "cpdf_read_validator.h",
+    "cpdf_reference.cpp",
+    "cpdf_reference.h",
+    "cpdf_security_handler.cpp",
+    "cpdf_security_handler.h",
+    "cpdf_simple_parser.cpp",
+    "cpdf_simple_parser.h",
+    "cpdf_stream.cpp",
+    "cpdf_stream.h",
+    "cpdf_stream_acc.cpp",
+    "cpdf_stream_acc.h",
+    "cpdf_string.cpp",
+    "cpdf_string.h",
+    "cpdf_syntax_parser.cpp",
+    "cpdf_syntax_parser.h",
+    "fpdf_parser_decode.cpp",
+    "fpdf_parser_decode.h",
+    "fpdf_parser_utility.cpp",
+    "fpdf_parser_utility.h",
+  ]
+  configs += [ "../../../:pdfium_core_config" ]
+  deps = [
+    "../../../constants",
+    "../../fdrm",
+    "../../fxcodec",
+    "../../fxcrt",
+  ]
+  allow_circular_includes_from = []
+  visibility = [ "../../../*" ]
+
+  if (pdf_enable_xfa) {
+    sources += [
+      "cpdf_seekablemultistream.cpp",
+      "cpdf_seekablemultistream.h",
+    ]
+  }
+  if (pdf_use_skia || pdf_use_skia_paths) {
+    deps += [ "../../fxge" ]
+    allow_circular_includes_from += [ "../../fxge" ]
+  }
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cpdf_array_unittest.cpp",
+    "cpdf_cross_ref_avail_unittest.cpp",
+    "cpdf_document_unittest.cpp",
+    "cpdf_hint_tables_unittest.cpp",
+    "cpdf_indirect_object_holder_unittest.cpp",
+    "cpdf_object_avail_unittest.cpp",
+    "cpdf_object_unittest.cpp",
+    "cpdf_object_walker_unittest.cpp",
+    "cpdf_page_object_avail_unittest.cpp",
+    "cpdf_parser_unittest.cpp",
+    "cpdf_read_validator_unittest.cpp",
+    "cpdf_simple_parser_unittest.cpp",
+    "cpdf_stream_acc_unittest.cpp",
+    "cpdf_syntax_parser_unittest.cpp",
+    "fpdf_parser_decode_unittest.cpp",
+    "fpdf_parser_utility_unittest.cpp",
+  ]
+  deps = [
+    ":parser",
+    "../../../constants",
+    "../page",
+    "../render",
+  ]
+  pdfium_root_dir = "../../../"
+
+  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") {
+  sources = [
+    "cpdf_parser_embeddertest.cpp",
+    "cpdf_security_handler_embeddertest.cpp",
+    "fpdf_parser_decode_embeddertest.cpp",
+  ]
+  deps = [ ":parser" ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/core/fpdfapi/parser/cfdf_document.cpp b/core/fpdfapi/parser/cfdf_document.cpp
index f6af454..054f999 100644
--- a/core/fpdfapi/parser/cfdf_document.cpp
+++ b/core/fpdfapi/parser/cfdf_document.cpp
@@ -10,37 +10,34 @@
 #include <sstream>
 #include <utility>
 
-#include "core/fpdfapi/edit/cpdf_creator.h"
 #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_memorystream.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 
-CFDF_Document::CFDF_Document()
-    : CPDF_IndirectObjectHolder(), m_pRootDict(nullptr) {}
+CFDF_Document::CFDF_Document() = default;
 
-CFDF_Document::~CFDF_Document() {}
+CFDF_Document::~CFDF_Document() = default;
 
 std::unique_ptr<CFDF_Document> CFDF_Document::CreateNewDoc() {
   auto pDoc = pdfium::MakeUnique<CFDF_Document>();
-  pDoc->m_pRootDict = pDoc->NewIndirect<CPDF_Dictionary>();
+  pDoc->m_pRootDict.Reset(pDoc->NewIndirect<CPDF_Dictionary>());
   pDoc->m_pRootDict->SetNewFor<CPDF_Dictionary>("FDF");
   return pDoc;
 }
 
-std::unique_ptr<CFDF_Document> CFDF_Document::ParseMemory(uint8_t* pData,
-                                                          uint32_t size) {
+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_MemoryStream>(pData, size, false));
+  pDoc->ParseStream(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(span));
   return pDoc->m_pRootDict ? std::move(pDoc) : nullptr;
 }
 
-void CFDF_Document::ParseStream(
-    const RetainPtr<IFX_SeekableReadStream>& pFile) {
-  m_pFile = pFile;
-  CPDF_SyntaxParser parser;
-  parser.InitParser(m_pFile, 0);
+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);
@@ -57,7 +54,7 @@
       if (word != "obj")
         break;
 
-      std::unique_ptr<CPDF_Object> pObj = parser.GetObjectBody(this);
+      RetainPtr<CPDF_Object> pObj = parser.GetObjectBody(this);
       if (!pObj)
         break;
 
@@ -69,10 +66,10 @@
       if (word != "trailer")
         break;
 
-      std::unique_ptr<CPDF_Dictionary> pMainDict =
+      RetainPtr<CPDF_Dictionary> pMainDict =
           ToDictionary(parser.GetObjectBody(this));
       if (pMainDict)
-        m_pRootDict = pMainDict->GetDictFor("Root");
+        m_pRootDict.Reset(pMainDict->GetDictFor("Root"));
 
       break;
     }
@@ -87,7 +84,7 @@
   buf << "%FDF-1.2\r\n";
   for (const auto& pair : *this)
     buf << pair.first << " 0 obj\r\n"
-        << pair.second.get() << "\r\nendobj\r\n\r\n";
+        << pair.second.Get() << "\r\nendobj\r\n\r\n";
 
   buf << "trailer\r\n<</Root " << m_pRootDict->GetObjNum()
       << " 0 R>>\r\n%%EOF\r\n";
diff --git a/core/fpdfapi/parser/cfdf_document.h b/core/fpdfapi/parser/cfdf_document.h
index b2cbd5b..cad94d0 100644
--- a/core/fpdfapi/parser/cfdf_document.h
+++ b/core/fpdfapi/parser/cfdf_document.h
@@ -10,17 +10,17 @@
 #include <memory>
 
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
-#include "core/fpdfapi/parser/cpdf_object.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/span.h"
 
 class CPDF_Dictionary;
 class IFX_SeekableReadStream;
 
-class CFDF_Document : public CPDF_IndirectObjectHolder {
+class CFDF_Document final : public CPDF_IndirectObjectHolder {
  public:
   static std::unique_ptr<CFDF_Document> CreateNewDoc();
-  static std::unique_ptr<CFDF_Document> ParseMemory(uint8_t* pData,
-                                                    uint32_t size);
+  static std::unique_ptr<CFDF_Document> ParseMemory(
+      pdfium::span<const uint8_t> span);
 
   CFDF_Document();
   ~CFDF_Document() override;
@@ -28,10 +28,10 @@
   ByteString WriteToString() const;
   CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); }
 
- protected:
-  void ParseStream(const RetainPtr<IFX_SeekableReadStream>& pFile);
+ private:
+  void ParseStream(RetainPtr<IFX_SeekableReadStream> pFile);
 
-  UnownedPtr<CPDF_Dictionary> m_pRootDict;
+  RetainPtr<CPDF_Dictionary> m_pRootDict;
   RetainPtr<IFX_SeekableReadStream> m_pFile;
 };
 
diff --git a/core/fpdfapi/parser/cpdf_array.cpp b/core/fpdfapi/parser/cpdf_array.cpp
index 5a5cdc9..0d66cb0 100644
--- a/core/fpdfapi/parser/cpdf_array.cpp
+++ b/core/fpdfapi/parser/cpdf_array.cpp
@@ -9,6 +9,7 @@
 #include <set>
 #include <utility>
 
+#include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
@@ -16,9 +17,10 @@
 #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"
 
-CPDF_Array::CPDF_Array() {}
+CPDF_Array::CPDF_Array() = default;
 
 CPDF_Array::CPDF_Array(const WeakPtr<ByteStringPool>& pPool) : m_pPool(pPool) {}
 
@@ -27,12 +29,12 @@
   m_ObjNum = kInvalidObjNum;
   for (auto& it : m_Objects) {
     if (it && it->GetObjNum() == kInvalidObjNum)
-      it.release();
+      it.Leak();
   }
 }
 
 CPDF_Object::Type CPDF_Array::GetType() const {
-  return ARRAY;
+  return kArray;
 }
 
 bool CPDF_Array::IsArray() const {
@@ -47,28 +49,28 @@
   return this;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Array::Clone() const {
+RetainPtr<CPDF_Object> CPDF_Array::Clone() const {
   return CloneObjectNonCyclic(false);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Array::CloneNonCyclic(
+RetainPtr<CPDF_Object> CPDF_Array::CloneNonCyclic(
     bool bDirect,
     std::set<const CPDF_Object*>* pVisited) const {
   pVisited->insert(this);
-  auto pCopy = pdfium::MakeUnique<CPDF_Array>();
+  auto pCopy = pdfium::MakeRetain<CPDF_Array>();
   for (const auto& pValue : m_Objects) {
-    if (!pdfium::ContainsKey(*pVisited, pValue.get())) {
+    if (!pdfium::ContainsKey(*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));
     }
   }
-  return std::move(pCopy);
+  return pCopy;
 }
 
-CFX_FloatRect CPDF_Array::GetRect() {
+CFX_FloatRect CPDF_Array::GetRect() const {
   CFX_FloatRect rect;
-  if (!IsArray() || m_Objects.size() != 4)
+  if (m_Objects.size() != 4)
     return rect;
 
   rect.left = GetNumberAt(0);
@@ -78,53 +80,71 @@
   return rect;
 }
 
-CFX_Matrix CPDF_Array::GetMatrix() {
-  CFX_Matrix matrix;
-  if (!IsArray() || m_Objects.size() != 6)
+CFX_Matrix CPDF_Array::GetMatrix() const {
+  if (m_Objects.size() != 6)
     return CFX_Matrix();
 
   return CFX_Matrix(GetNumberAt(0), GetNumberAt(1), GetNumberAt(2),
                     GetNumberAt(3), GetNumberAt(4), GetNumberAt(5));
 }
 
-CPDF_Object* CPDF_Array::GetObjectAt(size_t i) const {
-  if (i >= m_Objects.size())
+CPDF_Object* CPDF_Array::GetObjectAt(size_t index) {
+  if (index >= m_Objects.size())
     return nullptr;
-  return m_Objects[i].get();
+  return m_Objects[index].Get();
 }
 
-CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t i) const {
-  if (i >= m_Objects.size())
+const CPDF_Object* CPDF_Array::GetObjectAt(size_t index) const {
+  if (index >= m_Objects.size())
     return nullptr;
-  return m_Objects[i]->GetDirect();
+  return m_Objects[index].Get();
 }
 
-ByteString CPDF_Array::GetStringAt(size_t i) const {
-  if (i >= m_Objects.size())
+CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) {
+  if (index >= m_Objects.size())
+    return nullptr;
+  return m_Objects[index]->GetDirect();
+}
+
+const CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) const {
+  if (index >= m_Objects.size())
+    return nullptr;
+  return m_Objects[index]->GetDirect();
+}
+
+ByteString CPDF_Array::GetStringAt(size_t index) const {
+  if (index >= m_Objects.size())
     return ByteString();
-  return m_Objects[i]->GetString();
+  return m_Objects[index]->GetString();
 }
 
-WideString CPDF_Array::GetUnicodeTextAt(size_t i) const {
-  if (i >= m_Objects.size())
+WideString CPDF_Array::GetUnicodeTextAt(size_t index) const {
+  if (index >= m_Objects.size())
     return WideString();
-  return m_Objects[i]->GetUnicodeText();
+  return m_Objects[index]->GetUnicodeText();
 }
 
-int CPDF_Array::GetIntegerAt(size_t i) const {
-  if (i >= m_Objects.size())
+bool CPDF_Array::GetBooleanAt(size_t index, bool bDefault) const {
+  if (index >= m_Objects.size())
+    return bDefault;
+  const CPDF_Object* p = m_Objects[index].Get();
+  return ToBoolean(p) ? p->GetInteger() != 0 : bDefault;
+}
+
+int CPDF_Array::GetIntegerAt(size_t index) const {
+  if (index >= m_Objects.size())
     return 0;
-  return m_Objects[i]->GetInteger();
+  return m_Objects[index]->GetInteger();
 }
 
-float CPDF_Array::GetNumberAt(size_t i) const {
-  if (i >= m_Objects.size())
+float CPDF_Array::GetNumberAt(size_t index) const {
+  if (index >= m_Objects.size())
     return 0;
-  return m_Objects[i]->GetNumber();
+  return m_Objects[index]->GetNumber();
 }
 
-CPDF_Dictionary* CPDF_Array::GetDictAt(size_t i) const {
-  CPDF_Object* p = GetDirectObjectAt(i);
+CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) {
+  CPDF_Object* p = GetDirectObjectAt(index);
   if (!p)
     return nullptr;
   if (CPDF_Dictionary* pDict = p->AsDictionary())
@@ -134,52 +154,73 @@
   return nullptr;
 }
 
-CPDF_Stream* CPDF_Array::GetStreamAt(size_t i) const {
-  return ToStream(GetDirectObjectAt(i));
+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;
 }
 
-CPDF_Array* CPDF_Array::GetArrayAt(size_t i) const {
-  return ToArray(GetDirectObjectAt(i));
+CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) {
+  return ToStream(GetDirectObjectAt(index));
+}
+
+const CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) const {
+  return ToStream(GetDirectObjectAt(index));
+}
+
+CPDF_Array* CPDF_Array::GetArrayAt(size_t index) {
+  return ToArray(GetDirectObjectAt(index));
+}
+
+const CPDF_Array* CPDF_Array::GetArrayAt(size_t index) const {
+  return ToArray(GetDirectObjectAt(index));
 }
 
 void CPDF_Array::Clear() {
+  CHECK(!IsLocked());
   m_Objects.clear();
 }
 
-void CPDF_Array::RemoveAt(size_t i) {
-  if (i < m_Objects.size())
-    m_Objects.erase(m_Objects.begin() + i);
+void CPDF_Array::RemoveAt(size_t index) {
+  CHECK(!IsLocked());
+  if (index < m_Objects.size())
+    m_Objects.erase(m_Objects.begin() + index);
 }
 
-void CPDF_Array::ConvertToIndirectObjectAt(size_t i,
+void CPDF_Array::ConvertToIndirectObjectAt(size_t index,
                                            CPDF_IndirectObjectHolder* pHolder) {
-  if (i >= m_Objects.size())
+  CHECK(!IsLocked());
+  if (index >= m_Objects.size())
     return;
 
-  if (!m_Objects[i] || m_Objects[i]->IsReference())
+  if (!m_Objects[index] || m_Objects[index]->IsReference())
     return;
 
-  CPDF_Object* pNew = pHolder->AddIndirectObject(std::move(m_Objects[i]));
-  m_Objects[i] = pdfium::MakeUnique<CPDF_Reference>(pHolder, pNew->GetObjNum());
+  CPDF_Object* pNew = pHolder->AddIndirectObject(std::move(m_Objects[index]));
+  m_Objects[index] = pNew->MakeReference(pHolder);
 }
 
-CPDF_Object* CPDF_Array::SetAt(size_t i, std::unique_ptr<CPDF_Object> pObj) {
-  ASSERT(IsArray());
+CPDF_Object* CPDF_Array::SetAt(size_t index, RetainPtr<CPDF_Object> pObj) {
+  CHECK(!IsLocked());
   ASSERT(!pObj || pObj->IsInline());
-  if (i >= m_Objects.size()) {
+  if (index >= m_Objects.size()) {
     NOTREACHED();
     return nullptr;
   }
-  CPDF_Object* pRet = pObj.get();
-  m_Objects[i] = std::move(pObj);
+  CPDF_Object* pRet = pObj.Get();
+  m_Objects[index] = std::move(pObj);
   return pRet;
 }
 
-CPDF_Object* CPDF_Array::InsertAt(size_t index,
-                                  std::unique_ptr<CPDF_Object> pObj) {
-  ASSERT(IsArray());
+CPDF_Object* CPDF_Array::InsertAt(size_t index, RetainPtr<CPDF_Object> pObj) {
+  CHECK(!IsLocked());
   CHECK(!pObj || pObj->IsInline());
-  CPDF_Object* pRet = pObj.get();
+  CPDF_Object* pRet = pObj.Get();
   if (index >= m_Objects.size()) {
     // Allocate space first.
     m_Objects.resize(index + 1);
@@ -191,29 +232,31 @@
   return pRet;
 }
 
-CPDF_Object* CPDF_Array::Add(std::unique_ptr<CPDF_Object> pObj) {
-  ASSERT(IsArray());
+CPDF_Object* CPDF_Array::Add(RetainPtr<CPDF_Object> pObj) {
+  CHECK(!IsLocked());
   CHECK(!pObj || pObj->IsInline());
-  CPDF_Object* pRet = pObj.get();
+  CPDF_Object* pRet = pObj.Get();
   m_Objects.push_back(std::move(pObj));
   return pRet;
 }
 
-bool CPDF_Array::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Array::WriteTo(IFX_ArchiveStream* archive,
+                         const CPDF_Encryptor* encryptor) const {
   if (!archive->WriteString("["))
     return false;
 
-  for (size_t i = 0; i < GetCount(); ++i) {
-    CPDF_Object* pElement = GetObjectAt(i);
-    if (!pElement->IsInline()) {
-      if (!archive->WriteString(" ") ||
-          !archive->WriteDWord(pElement->GetObjNum()) ||
-          !archive->WriteString(" 0 R")) {
-        return false;
-      }
-    } else if (!pElement->WriteTo(archive)) {
+  for (size_t i = 0; i < size(); ++i) {
+    if (!GetObjectAt(i)->WriteTo(archive, encryptor))
       return false;
-    }
   }
   return archive->WriteString("]");
 }
+
+CPDF_ArrayLocker::CPDF_ArrayLocker(const CPDF_Array* pArray)
+    : m_pArray(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 bf8cb42..2a12d99 100644
--- a/core/fpdfapi/parser/cpdf_array.h
+++ b/core/fpdfapi/parser/cpdf_array.h
@@ -16,103 +16,136 @@
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 #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"
 
-class CPDF_Array : public CPDF_Object {
+class CPDF_Array final : public CPDF_Object {
  public:
-  using const_iterator =
-      std::vector<std::unique_ptr<CPDF_Object>>::const_iterator;
+  using const_iterator = std::vector<RetainPtr<CPDF_Object>>::const_iterator;
 
-  CPDF_Array();
-  explicit CPDF_Array(const WeakPtr<ByteStringPool>& pPool);
-  ~CPDF_Array() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
+  RetainPtr<CPDF_Object> Clone() const override;
   bool IsArray() const override;
   CPDF_Array* AsArray() override;
   const CPDF_Array* AsArray() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   bool IsEmpty() const { return m_Objects.empty(); }
-  size_t GetCount() const { return m_Objects.size(); }
-  CPDF_Object* GetObjectAt(size_t index) const;
-  CPDF_Object* GetDirectObjectAt(size_t index) const;
+  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;
   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_Stream* GetStreamAt(size_t index) const;
-  CPDF_Array* GetArrayAt(size_t index) const;
-  float GetFloatAt(size_t index) const { return GetNumberAt(index); }
-  CFX_Matrix GetMatrix();
-  CFX_FloatRect GetRect();
-
-  // Takes ownership of |pObj|, returns unowned pointer to it.
-  CPDF_Object* Add(std::unique_ptr<CPDF_Object> pObj);
-  CPDF_Object* SetAt(size_t index, std::unique_ptr<CPDF_Object> pObj);
-  CPDF_Object* InsertAt(size_t index, std::unique_ptr<CPDF_Object> pObj);
+  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;
 
   // Creates object owned by the array, returns unowned pointer to it.
   // We have special cases for objects that can intern strings from
-  // a ByteStringPool.
+  // a ByteStringPool. Prefer using these templates over direct calls
+  // to Add()/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::MakeUnique<T>(std::forward<Args>(args)...)));
+        Add(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::MakeUnique<T>(m_pPool, std::forward<Args>(args)...)));
+        Add(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::MakeUnique<T>(std::forward<Args>(args)...)));
+        SetAt(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::MakeUnique<T>(m_pPool, std::forward<Args>(args)...)));
+        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::MakeUnique<T>(std::forward<Args>(args)...)));
+        InsertAt(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::MakeUnique<T>(m_pPool, std::forward<Args>(args)...)));
+        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);
+
   void Clear();
   void RemoveAt(size_t index);
-  void ConvertToIndirectObjectAt(size_t index, CPDF_IndirectObjectHolder* pDoc);
+  void ConvertToIndirectObjectAt(size_t index,
+                                 CPDF_IndirectObjectHolder* pHolder);
+  bool IsLocked() const { return !!m_LockCount; }
 
-  const_iterator begin() const { return m_Objects.begin(); }
-  const_iterator end() const { return m_Objects.end(); }
+ private:
+  friend class CPDF_ArrayLocker;
 
- protected:
-  std::unique_ptr<CPDF_Object> CloneNonCyclic(
+  CPDF_Array();
+  explicit CPDF_Array(const WeakPtr<ByteStringPool>& pPool);
+  ~CPDF_Array() override;
+
+  RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* pVisited) const override;
 
-  std::vector<std::unique_ptr<CPDF_Object>> m_Objects;
+  std::vector<RetainPtr<CPDF_Object>> m_Objects;
   WeakPtr<ByteStringPool> m_pPool;
+  mutable uint32_t m_LockCount = 0;
+};
+
+class CPDF_ArrayLocker {
+ public:
+  using const_iterator = CPDF_Array::const_iterator;
+
+  explicit CPDF_ArrayLocker(const CPDF_Array* pArray);
+  ~CPDF_ArrayLocker();
+
+  const_iterator begin() const {
+    CHECK(m_pArray->IsLocked());
+    return m_pArray->m_Objects.begin();
+  }
+  const_iterator end() const {
+    CHECK(m_pArray->IsLocked());
+    return m_pArray->m_Objects.end();
+  }
+
+ private:
+  RetainPtr<const CPDF_Array> const m_pArray;
 };
 
 inline CPDF_Array* ToArray(CPDF_Object* obj) {
@@ -123,12 +156,8 @@
   return obj ? obj->AsArray() : nullptr;
 }
 
-inline std::unique_ptr<CPDF_Array> ToArray(std::unique_ptr<CPDF_Object> obj) {
-  CPDF_Array* pArray = ToArray(obj.get());
-  if (!pArray)
-    return nullptr;
-  obj.release();
-  return std::unique_ptr<CPDF_Array>(pArray);
+inline RetainPtr<CPDF_Array> ToArray(RetainPtr<CPDF_Object> obj) {
+  return RetainPtr<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 4ce66dc..457961f 100644
--- a/core/fpdfapi/parser/cpdf_array_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_array_unittest.cpp
@@ -7,66 +7,87 @@
 #include <memory>
 #include <utility>
 
+#include "core/fpdfapi/parser/cpdf_boolean.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) {
+  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);
+
+  ASSERT_EQ(4u, arr->size());
+  EXPECT_TRUE(arr->GetBooleanAt(0, true));
+  EXPECT_TRUE(arr->GetBooleanAt(0, false));
+  EXPECT_FALSE(arr->GetBooleanAt(1, true));
+  EXPECT_FALSE(arr->GetBooleanAt(1, false));
+  EXPECT_TRUE(arr->GetBooleanAt(2, true));
+  EXPECT_FALSE(arr->GetBooleanAt(2, false));
+  EXPECT_TRUE(arr->GetBooleanAt(3, true));
+  EXPECT_FALSE(arr->GetBooleanAt(3, false));
+  EXPECT_TRUE(arr->GetBooleanAt(99, true));
+  EXPECT_FALSE(arr->GetBooleanAt(99, false));
+}
+
 TEST(cpdf_array, RemoveAt) {
   {
     const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    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 < 3; ++i)
       arr->RemoveAt(3);
     const int expected[] = {1, 2, 3, 7, 8, 9, 10};
-    ASSERT_EQ(FX_ArraySize(expected), arr->GetCount());
+    ASSERT_EQ(FX_ArraySize(expected), arr->size());
     for (size_t i = 0; i < FX_ArraySize(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->GetCount());
+    ASSERT_EQ(FX_ArraySize(expected2), arr->size());
     for (size_t i = 0; i < FX_ArraySize(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::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < FX_ArraySize(elems); ++i)
       arr->AddNew<CPDF_Number>(elems[i]);
     arr->RemoveAt(11);
-    EXPECT_EQ(FX_ArraySize(elems), arr->GetCount());
+    EXPECT_EQ(FX_ArraySize(elems), arr->size());
   }
 }
 
 TEST(cpdf_array, Clear) {
   const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto arr = pdfium::MakeUnique<CPDF_Array>();
-  EXPECT_EQ(0U, arr->GetCount());
+  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->GetCount());
+  EXPECT_EQ(FX_ArraySize(elems), arr->size());
   arr->Clear();
-  EXPECT_EQ(0U, arr->GetCount());
+  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::MakeUnique<CPDF_Array>();
+    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->GetCount());
+    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->GetCount());
+    ASSERT_EQ(FX_ArraySize(expected), arr->size());
     for (size_t i = 0; i < FX_ArraySize(expected); ++i)
       EXPECT_EQ(expected[i], arr->GetIntegerAt(i));
   }
@@ -75,11 +96,11 @@
     // an element is inserted at that position while other unfilled
     // positions have nullptr.
     const int elems[] = {1, 2};
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    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->GetCount());
+    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)
@@ -92,11 +113,11 @@
   {
     // Basic case.
     const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < FX_ArraySize(elems); ++i)
       arr->InsertNewAt<CPDF_Number>(i, elems[i]);
-    std::unique_ptr<CPDF_Array> arr2 = ToArray(arr->Clone());
-    ASSERT_EQ(arr->GetCount(), arr2->GetCount());
+    RetainPtr<CPDF_Array> arr2 = ToArray(arr->Clone());
+    ASSERT_EQ(arr->size(), arr2->size());
     for (size_t i = 0; i < FX_ArraySize(elems); ++i) {
       // Clone() always create new objects.
       EXPECT_NE(arr->GetObjectAt(i), arr2->GetObjectAt(i));
@@ -109,13 +130,13 @@
     static const size_t kNumOfRowElems = 5;
     const int elems[kNumOfRows][kNumOfRowElems] = {
         {1, 2, 3, 4, 5}, {10, 9, 8, 7, 6}, {11, 12, 13, 14, 15}};
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     // Indirect references to indirect objects.
     auto obj_holder = pdfium::MakeUnique<CPDF_IndirectObjectHolder>();
     for (size_t i = 0; i < kNumOfRows; ++i) {
-      auto arr_elem = pdfium::MakeUnique<CPDF_Array>();
+      auto arr_elem = pdfium::MakeRetain<CPDF_Array>();
       for (size_t j = 0; j < kNumOfRowElems; ++j) {
-        auto obj = pdfium::MakeUnique<CPDF_Number>(elems[i][j]);
+        auto obj = pdfium::MakeRetain<CPDF_Number>(elems[i][j]);
         // Starts object number from 1.
         int obj_num = i * kNumOfRowElems + j + 1;
         obj_holder->ReplaceIndirectObjectIfHigherGeneration(obj_num,
@@ -124,14 +145,14 @@
       }
       arr->InsertAt(i, std::move(arr_elem));
     }
-    ASSERT_EQ(kNumOfRows, arr->GetCount());
+    ASSERT_EQ(kNumOfRows, arr->size());
     // Not dereferencing reference objects means just creating new references
     // instead of new copies of direct objects.
-    std::unique_ptr<CPDF_Array> arr1 = ToArray(arr->Clone());
-    ASSERT_EQ(arr->GetCount(), arr1->GetCount());
+    RetainPtr<CPDF_Array> arr1 = ToArray(arr->Clone());
+    ASSERT_EQ(arr->size(), arr1->size());
     // Dereferencing reference objects creates new copies of direct objects.
-    std::unique_ptr<CPDF_Array> arr2 = ToArray(arr->CloneDirectObject());
-    ASSERT_EQ(arr->GetCount(), arr2->GetCount());
+    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();
@@ -155,8 +176,8 @@
         EXPECT_EQ(elem_obj->GetInteger(), elem_obj2->GetInteger());
       }
     }
-    arr.reset();
-    ASSERT_EQ(kNumOfRows, arr1->GetCount());
+    arr.Reset();
+    ASSERT_EQ(kNumOfRows, arr1->size());
     for (size_t i = 0; i < kNumOfRows; ++i) {
       for (size_t j = 0; j < kNumOfRowElems; ++j) {
         // Results from not deferencing reference objects.
@@ -174,11 +195,13 @@
 TEST(cpdf_array, Iterator) {
   const int elems[] = {-23, -11,     3,         455,   2345877,
                        0,   7895330, -12564334, 10000, -100000};
-  auto arr = pdfium::MakeUnique<CPDF_Array>();
+  auto arr = pdfium::MakeRetain<CPDF_Array>();
   for (size_t i = 0; i < FX_ArraySize(elems); ++i)
     arr->InsertNewAt<CPDF_Number>(i, elems[i]);
   size_t index = 0;
-  for (const auto& it : *arr)
+
+  CPDF_ArrayLocker locker(arr.Get());
+  for (const auto& it : locker)
     EXPECT_EQ(elems[index++], it->AsNumber()->GetInteger());
   EXPECT_EQ(FX_ArraySize(elems), index);
 }
diff --git a/core/fpdfapi/parser/cpdf_boolean.cpp b/core/fpdfapi/parser/cpdf_boolean.cpp
index a1dc450..b5e12eb 100644
--- a/core/fpdfapi/parser/cpdf_boolean.cpp
+++ b/core/fpdfapi/parser/cpdf_boolean.cpp
@@ -5,21 +5,22 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/fpdfapi/parser/cpdf_boolean.h"
+
 #include "core/fxcrt/fx_stream.h"
 #include "third_party/base/ptr_util.h"
 
-CPDF_Boolean::CPDF_Boolean() : m_bValue(false) {}
+CPDF_Boolean::CPDF_Boolean() = default;
 
 CPDF_Boolean::CPDF_Boolean(bool value) : m_bValue(value) {}
 
-CPDF_Boolean::~CPDF_Boolean() {}
+CPDF_Boolean::~CPDF_Boolean() = default;
 
 CPDF_Object::Type CPDF_Boolean::GetType() const {
-  return BOOLEAN;
+  return kBoolean;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Boolean::Clone() const {
-  return pdfium::MakeUnique<CPDF_Boolean>(m_bValue);
+RetainPtr<CPDF_Object> CPDF_Boolean::Clone() const {
+  return pdfium::MakeRetain<CPDF_Boolean>(m_bValue);
 }
 
 ByteString CPDF_Boolean::GetString() const {
@@ -46,7 +47,8 @@
   return this;
 }
 
-bool CPDF_Boolean::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Boolean::WriteTo(IFX_ArchiveStream* archive,
+                           const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" ") &&
          archive->WriteString(GetString().AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_boolean.h b/core/fpdfapi/parser/cpdf_boolean.h
index c0a69d8..8ef47ad 100644
--- a/core/fpdfapi/parser/cpdf_boolean.h
+++ b/core/fpdfapi/parser/cpdf_boolean.h
@@ -13,25 +13,29 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 
-class CPDF_Boolean : public CPDF_Object {
+class CPDF_Boolean final : public CPDF_Object {
  public:
-  CPDF_Boolean();
-  explicit CPDF_Boolean(bool value);
-  ~CPDF_Boolean() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
+  RetainPtr<CPDF_Object> Clone() const override;
   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;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
- protected:
-  bool m_bValue;
+ private:
+  CPDF_Boolean();
+  explicit CPDF_Boolean(bool value);
+  ~CPDF_Boolean() override;
+
+  bool m_bValue = false;
 };
 
 inline CPDF_Boolean* ToBoolean(CPDF_Object* obj) {
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
index be9818a..a6de007 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
@@ -5,6 +5,7 @@
 #include "core/fpdfapi/parser/cpdf_cross_ref_avail.h"
 
 #include <algorithm>
+#include <memory>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -39,7 +40,7 @@
   if (current_status_ == CPDF_DataAvail::DataAvailable)
     return CPDF_DataAvail::DataAvailable;
 
-  const CPDF_ReadValidator::Session read_session(GetValidator().Get());
+  const CPDF_ReadValidator::Session read_session(GetValidator());
   while (true) {
     bool check_result = false;
     switch (current_state_) {
@@ -135,7 +136,7 @@
 bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() {
   parser_->SetPos(current_offset_);
 
-  std::unique_ptr<CPDF_Dictionary> trailer =
+  RetainPtr<CPDF_Dictionary> trailer =
       ToDictionary(parser_->GetObjectBody(nullptr));
   if (CheckReadProblems())
     return false;
@@ -151,13 +152,13 @@
   }
 
   const int32_t xrefpos =
-      GetDirectInteger(trailer.get(), kPrevCrossRefFieldKey);
+      GetDirectInteger(trailer.Get(), kPrevCrossRefFieldKey);
   if (xrefpos &&
       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(xrefpos))
     AddCrossRefForCheck(static_cast<FX_FILESIZE>(xrefpos));
 
   const int32_t stream_xref_offset =
-      GetDirectInteger(trailer.get(), kPrevCrossRefStreamOffsetFieldKey);
+      GetDirectInteger(trailer.Get(), kPrevCrossRefStreamOffsetFieldKey);
   if (stream_xref_offset &&
       pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(
           stream_xref_offset))
@@ -186,7 +187,7 @@
     return false;
   }
 
-  CPDF_Name* type_name = ToName(trailer->GetObjectFor(kTypeFieldKey));
+  const CPDF_Name* type_name = ToName(trailer->GetObjectFor(kTypeFieldKey));
   if (type_name && type_name->GetString() == kXRefKeyword) {
     const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey);
     if (xrefpos &&
@@ -206,6 +207,6 @@
   registered_crossrefs_.insert(crossref_offset);
 }
 
-fxcrt::RetainPtr<CPDF_ReadValidator> CPDF_CrossRefAvail::GetValidator() {
+RetainPtr<CPDF_ReadValidator> CPDF_CrossRefAvail::GetValidator() {
   return parser_->GetValidator();
 }
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.h b/core/fpdfapi/parser/cpdf_cross_ref_avail.h
index aad58f3..e550410 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.h
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.h
@@ -5,7 +5,6 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_AVAIL_H_
 #define CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_AVAIL_H_
 
-#include <memory>
 #include <queue>
 #include <set>
 
@@ -41,9 +40,9 @@
 
   void AddCrossRefForCheck(FX_FILESIZE crossref_offset);
 
-  fxcrt::RetainPtr<CPDF_ReadValidator> GetValidator();
+  RetainPtr<CPDF_ReadValidator> GetValidator();
 
-  fxcrt::UnownedPtr<CPDF_SyntaxParser> parser_;
+  UnownedPtr<CPDF_SyntaxParser> parser_;
   const FX_FILESIZE last_crossref_offset_ = 0;
   CPDF_DataAvail::DocAvailStatus current_status_ =
       CPDF_DataAvail::DataNotAvailable;
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp
index b798e17..6cdc3f1 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp
@@ -8,19 +8,16 @@
 #include <string>
 
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
-#include "testing/fx_string_testhelpers.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
 std::unique_ptr<CPDF_SyntaxParser> MakeParserForBuffer(
-    const unsigned char* buffer,
-    size_t buffer_size) {
-  auto parser = pdfium::MakeUnique<CPDF_SyntaxParser>();
-  parser->InitParser(
-      pdfium::MakeRetain<CFX_BufferSeekableReadStream>(buffer, buffer_size), 0);
-  return parser;
+    pdfium::span<const uint8_t> buffer) {
+  return pdfium::MakeUnique<CPDF_SyntaxParser>(
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(buffer));
 }
 
 }  // namespace
@@ -41,7 +38,7 @@
       "/Info 15 0 R/Size 16>>";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser = MakeParserForBuffer(xref_table, FX_ArraySize(xref_table));
+  auto parser = MakeParserForBuffer(xref_table);
   auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
       parser.get(), last_crossref_offset);
 
@@ -58,7 +55,7 @@
       "endobj\n";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser = MakeParserForBuffer(xref_stream, FX_ArraySize(xref_stream));
+  auto parser = MakeParserForBuffer(xref_stream);
   auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
       parser.get(), last_crossref_offset);
 
@@ -76,7 +73,7 @@
 
   const FX_FILESIZE last_crossref_offset = 70000;
 
-  auto parser = MakeParserForBuffer(xref_stream, FX_ArraySize(xref_stream));
+  auto parser = MakeParserForBuffer(xref_stream);
   auto cross_ref_avail = pdfium::MakeUnique<CPDF_CrossRefAvail>(
       parser.get(), last_crossref_offset);
 
@@ -93,7 +90,7 @@
       "endobj\n";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser = MakeParserForBuffer(xref_stream, FX_ArraySize(xref_stream));
+  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());
@@ -115,7 +112,7 @@
       "/Info 15 0 R/Size 16 /XRefStm 70000>>";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser = MakeParserForBuffer(xref_table, FX_ArraySize(xref_table));
+  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());
@@ -127,8 +124,7 @@
       "wfoihoiwfghouiafghwoigahfi";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser =
-      MakeParserForBuffer(incorrect_data, FX_ArraySize(incorrect_data));
+  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());
@@ -136,11 +132,8 @@
 
 TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV4) {
   char int_buffer[100];
-  int prev_offset = 0;
-  int cur_offset = 0;
   std::string table = "pdf blah blah blah\n";
-  prev_offset = cur_offset;
-  cur_offset = static_cast<int>(table.size());
+  size_t cur_offset = table.size();
   table +=
       "xref \n"
       "0 6 \n"
@@ -150,8 +143,8 @@
       "[<afbb0f593c2d2aea5b519cb61da1c17b><4f9bb2e7978401808f8f1f2a75c322c8>]"
       "/Info 15 0 R/Size 16>>\n";
   table += "Dummy Data jgwhughouiwbahng";
-  prev_offset = cur_offset;
-  cur_offset = static_cast<int>(table.size());
+  size_t prev_offset = cur_offset;
+  cur_offset = table.size();
   table += std::string(
                "xref \n"
                "0 6 \n"
@@ -162,10 +155,10 @@
                "4f9bb2e7978401808f8f1f2a75c322c8>]"
                "/Info 15 0 R/Size 16"
                "/Prev ") +
-           FXSYS_itoa(prev_offset, int_buffer, 10) + ">>\n";
+           FXSYS_itoa(static_cast<int>(prev_offset), int_buffer, 10) + ">>\n";
   table += "More Dummy Data jgwhughouiwbahng";
   prev_offset = cur_offset;
-  cur_offset = static_cast<int>(table.size());
+  cur_offset = table.size();
   table += std::string(
                "xref \n"
                "0 6 \n"
@@ -176,11 +169,10 @@
                "4f9bb2e7978401808f8f1f2a75c322c8>]"
                "/Info 15 0 R/Size 16"
                "/Prev ") +
-           FXSYS_itoa(prev_offset, int_buffer, 10) + ">>\n";
-  const FX_FILESIZE last_crossref_offset = cur_offset;
+           FXSYS_itoa(static_cast<int>(prev_offset), int_buffer, 10) + ">>\n";
+  const FX_FILESIZE last_crossref_offset = static_cast<FX_FILESIZE>(cur_offset);
 
-  auto parser = MakeParserForBuffer(
-      reinterpret_cast<const unsigned char*>(table.data()), table.size());
+  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());
@@ -188,11 +180,8 @@
 
 TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV5) {
   char int_buffer[100];
-  int prev_offset = 0;
-  int cur_offset = 0;
   std::string table = "pdf blah blah blah\n";
-  prev_offset = cur_offset;
-  cur_offset = static_cast<int>(table.size());
+  size_t cur_offset = table.size();
   table +=
       "16 0 obj\n"
       "<</Type /XRef>>"
@@ -202,12 +191,12 @@
       "endobj\n";
   table += "Dummy Data jgwhughouiwbahng";
 
-  prev_offset = cur_offset;
-  cur_offset = static_cast<int>(table.size());
+  size_t prev_offset = cur_offset;
+  cur_offset = table.size();
   table += std::string(
                "55 0 obj\n"
                "<</Type /XRef /Prev ") +
-           FXSYS_itoa(prev_offset, int_buffer, 10) +
+           FXSYS_itoa(static_cast<int>(prev_offset), int_buffer, 10) +
            ">>"
            " stream \n"
            "STREAM DATA STREAM DATA STREAM DATA\n"
@@ -215,20 +204,19 @@
            "endobj\n";
   table += "More Dummy Data jgwhughouiwbahng";
   prev_offset = cur_offset;
-  cur_offset = static_cast<int>(table.size());
+  cur_offset = table.size();
   table += std::string(
                "88 0 obj\n"
                "<</Type /XRef /NNNN /Prev ") +
-           FXSYS_itoa(prev_offset, int_buffer, 10) +
+           FXSYS_itoa(static_cast<int>(prev_offset), int_buffer, 10) +
            ">>"
            " stream \n"
            "STREAM DATA STREAM DATA STREAM DATA favav\n"
            "endstream\n"
            "endobj\n";
-  const FX_FILESIZE last_crossref_offset = cur_offset;
+  const FX_FILESIZE last_crossref_offset = static_cast<FX_FILESIZE>(cur_offset);
 
-  auto parser = MakeParserForBuffer(
-      reinterpret_cast<const unsigned char*>(table.data()), table.size());
+  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());
@@ -277,8 +265,7 @@
            FXSYS_itoa(first_v5_table_offset, int_buffer, 10) + ">>\n";
   const FX_FILESIZE last_crossref_offset = last_v4_table_offset;
 
-  auto parser = MakeParserForBuffer(
-      reinterpret_cast<const unsigned char*>(table.data()), table.size());
+  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());
@@ -292,8 +279,7 @@
       "endobj\n";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser = MakeParserForBuffer(invalid_xref_stream,
-                                    FX_ArraySize(invalid_xref_stream));
+  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());
@@ -316,7 +302,7 @@
       "/Info 15 0 R/Size 16>>";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser = MakeParserForBuffer(xref_table, FX_ArraySize(xref_table));
+  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());
@@ -332,7 +318,7 @@
       "endobj\n";
   const FX_FILESIZE last_crossref_offset = 0;
 
-  auto parser = MakeParserForBuffer(xref_stream, FX_ArraySize(xref_stream));
+  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());
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.cpp b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
new file mode 100644
index 0000000..bd25b6c
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
@@ -0,0 +1,158 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/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"
+
+// static
+std::unique_ptr<CPDF_CrossRefTable> CPDF_CrossRefTable::MergeUp(
+    std::unique_ptr<CPDF_CrossRefTable> current,
+    std::unique_ptr<CPDF_CrossRefTable> top) {
+  if (!current)
+    return top;
+
+  if (!top)
+    return current;
+
+  current->Update(std::move(top));
+  return current;
+}
+
+CPDF_CrossRefTable::CPDF_CrossRefTable() = default;
+
+CPDF_CrossRefTable::CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer)
+    : trailer_(std::move(trailer)) {}
+
+CPDF_CrossRefTable::~CPDF_CrossRefTable() = default;
+
+void CPDF_CrossRefTable::AddCompressed(uint32_t obj_num,
+                                       uint32_t archive_obj_num) {
+  if (obj_num >= CPDF_Parser::kMaxObjectNumber ||
+      archive_obj_num >= CPDF_Parser::kMaxObjectNumber) {
+    NOTREACHED();
+    return;
+  }
+
+  auto& info = objects_info_[obj_num];
+  if (info.gennum > 0)
+    return;
+
+  if (info.type == ObjectType::kObjStream)
+    return;
+
+  info.type = ObjectType::kCompressed;
+  info.archive_obj_num = archive_obj_num;
+  info.gennum = 0;
+
+  objects_info_[archive_obj_num].type = ObjectType::kObjStream;
+}
+
+void CPDF_CrossRefTable::AddNormal(uint32_t obj_num,
+                                   uint16_t gen_num,
+                                   FX_FILESIZE pos) {
+  if (obj_num >= CPDF_Parser::kMaxObjectNumber) {
+    NOTREACHED();
+    return;
+  }
+
+  auto& info = objects_info_[obj_num];
+  if (info.gennum > gen_num)
+    return;
+
+  if (info.type == ObjectType::kCompressed && gen_num == 0)
+    return;
+
+  if (info.type != ObjectType::kObjStream)
+    info.type = ObjectType::kNormal;
+
+  info.gennum = gen_num;
+  info.pos = pos;
+}
+
+void CPDF_CrossRefTable::SetFree(uint32_t obj_num) {
+  if (obj_num >= CPDF_Parser::kMaxObjectNumber) {
+    NOTREACHED();
+    return;
+  }
+
+  auto& info = objects_info_[obj_num];
+  info.type = ObjectType::kFree;
+  info.gennum = 0xFFFF;
+  info.pos = 0;
+}
+
+void CPDF_CrossRefTable::SetTrailer(RetainPtr<CPDF_Dictionary> trailer) {
+  trailer_ = std::move(trailer);
+}
+
+const CPDF_CrossRefTable::ObjectInfo* CPDF_CrossRefTable::GetObjectInfo(
+    uint32_t obj_num) const {
+  const auto it = objects_info_.find(obj_num);
+  return it != objects_info_.end() ? &it->second : nullptr;
+}
+
+void CPDF_CrossRefTable::Update(
+    std::unique_ptr<CPDF_CrossRefTable> new_cross_ref) {
+  UpdateInfo(std::move(new_cross_ref->objects_info_));
+  UpdateTrailer(std::move(new_cross_ref->trailer_));
+}
+
+void CPDF_CrossRefTable::ShrinkObjectMap(uint32_t objnum) {
+  if (objnum == 0) {
+    objects_info_.clear();
+    return;
+  }
+
+  objects_info_.erase(objects_info_.lower_bound(objnum), objects_info_.end());
+
+  if (!pdfium::ContainsKey(objects_info_, objnum - 1))
+    objects_info_[objnum - 1].pos = 0;
+}
+
+void CPDF_CrossRefTable::UpdateInfo(
+    std::map<uint32_t, ObjectInfo>&& new_objects_info) {
+  auto cur_it = objects_info_.begin();
+  auto new_it = new_objects_info.begin();
+  while (cur_it != objects_info_.end() && new_it != new_objects_info.end()) {
+    if (cur_it->first == new_it->first) {
+      if (cur_it->second.type == ObjectType::kObjStream &&
+          new_it->second.type == ObjectType::kNormal) {
+        new_it->second.type = ObjectType::kObjStream;
+      }
+      ++cur_it;
+      ++new_it;
+    } else if (cur_it->first < new_it->first) {
+      new_objects_info.insert(new_it, *cur_it);
+      ++cur_it;
+    } else {
+      new_it = new_objects_info.lower_bound(cur_it->first);
+    }
+  }
+  for (; cur_it != objects_info_.end(); ++cur_it) {
+    new_objects_info.insert(new_objects_info.end(), *cur_it);
+  }
+  objects_info_ = std::move(new_objects_info);
+}
+
+void CPDF_CrossRefTable::UpdateTrailer(RetainPtr<CPDF_Dictionary> new_trailer) {
+  if (!new_trailer)
+    return;
+
+  if (!trailer_) {
+    trailer_ = std::move(new_trailer);
+    return;
+  }
+
+  new_trailer->SetFor("XRefStm", trailer_->RemoveFor("XRefStm"));
+  new_trailer->SetFor("Prev", trailer_->RemoveFor("Prev"));
+
+  for (const auto& key : new_trailer->GetKeys())
+    trailer_->SetFor(key, new_trailer->RemoveFor(key));
+}
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.h b/core/fpdfapi/parser/cpdf_cross_ref_table.h
new file mode 100644
index 0000000..66a51de
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_cross_ref_table.h
@@ -0,0 +1,75 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_TABLE_H_
+#define CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_TABLE_H_
+
+#include <map>
+#include <memory>
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_Dictionary;
+
+class CPDF_CrossRefTable {
+ public:
+  enum class ObjectType : uint8_t {
+    kFree = 0x00,
+    kNormal = 0x01,
+    kNotCompressed = kNormal,
+    kCompressed = 0x02,
+    kObjStream = 0xFF,
+    kNull = kObjStream,
+  };
+
+  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.
+    union {
+      FX_FILESIZE pos;
+      uint32_t archive_obj_num;
+    };
+    ObjectType type;
+    uint16_t gennum;
+  };
+
+  // Merge cross reference tables.  Apply top on current.
+  static std::unique_ptr<CPDF_CrossRefTable> MergeUp(
+      std::unique_ptr<CPDF_CrossRefTable> current,
+      std::unique_ptr<CPDF_CrossRefTable> top);
+
+  CPDF_CrossRefTable();
+  explicit CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer);
+  ~CPDF_CrossRefTable();
+
+  void AddCompressed(uint32_t obj_num, uint32_t archive_obj_num);
+  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);
+  const CPDF_Dictionary* trailer() const { return trailer_.Get(); }
+  CPDF_Dictionary* GetMutableTrailerForTesting() { return trailer_.Get(); }
+
+  const ObjectInfo* GetObjectInfo(uint32_t obj_num) const;
+
+  const std::map<uint32_t, ObjectInfo>& objects_info() const {
+    return objects_info_;
+  }
+
+  void Update(std::unique_ptr<CPDF_CrossRefTable> new_cross_ref);
+
+  void ShrinkObjectMap(uint32_t objnum);
+
+ private:
+  void UpdateInfo(std::map<uint32_t, ObjectInfo>&& new_objects_info);
+  void UpdateTrailer(RetainPtr<CPDF_Dictionary> new_trailer);
+
+  RetainPtr<CPDF_Dictionary> trailer_;
+  std::map<uint32_t, ObjectInfo> objects_info_;
+};
+
+#endif  // CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_TABLE_H_
diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.cpp b/core/fpdfapi/parser/cpdf_crypto_handler.cpp
index d963df4..a19cf0a 100644
--- a/core/fpdfapi/parser/cpdf_crypto_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_crypto_handler.cpp
@@ -12,9 +12,8 @@
 #include <stack>
 #include <utility>
 
-#include "core/fdrm/crypto/fx_crypt.h"
-#include "core/fpdfapi/edit/cpdf_encryptor.h"
-#include "core/fpdfapi/edit/cpdf_flateencoder.h"
+#include "constants/form_fields.h"
+#include "core/fdrm/fx_crypt.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_object_walker.h"
@@ -29,8 +28,6 @@
 
 constexpr char kContentsKey[] = "Contents";
 constexpr char kTypeKey[] = "Type";
-constexpr char kFTKey[] = "FT";
-constexpr char kSignTypeValue[] = "Sig";
 
 }  // namespace
 
@@ -41,39 +38,34 @@
     return false;
   const CPDF_Object* type_obj = dictionary->GetDirectObjectFor(kTypeKey);
   if (!type_obj)
-    type_obj = dictionary->GetDirectObjectFor(kFTKey);
-  return type_obj && type_obj->GetString() == kSignTypeValue;
+    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,
-                                    const uint8_t* src_buf,
-                                    uint32_t src_size,
+                                    pdfium::span<const uint8_t> source,
                                     uint8_t* dest_buf,
                                     uint32_t& dest_size) {
   if (m_Cipher == FXCIPHER_NONE) {
-    memcpy(dest_buf, src_buf, src_size);
+    memcpy(dest_buf, source.data(), source.size());
     return;
   }
   uint8_t realkey[16];
-  int realkeylen = 16;
+  size_t realkeylen = sizeof(realkey);
   if (m_Cipher != FXCIPHER_AES || m_KeyLen != 32) {
     uint8_t key1[32];
     PopulateKey(objnum, gennum, key1);
 
-    if (m_Cipher == FXCIPHER_AES) {
+    if (m_Cipher == FXCIPHER_AES)
       memcpy(key1 + m_KeyLen + 5, "sAlT", 4);
-    }
-    CRYPT_MD5Generate(
-        key1, m_Cipher == FXCIPHER_AES ? m_KeyLen + 9 : m_KeyLen + 5, realkey);
-    realkeylen = m_KeyLen + 5;
-    if (realkeylen > 16) {
-      realkeylen = 16;
-    }
+    size_t len = m_Cipher == FXCIPHER_AES ? m_KeyLen + 9 : m_KeyLen + 5;
+    CRYPT_MD5Generate({key1, len}, realkey);
+    realkeylen = std::min(m_KeyLen + 5, sizeof(realkey));
   }
   if (m_Cipher == FXCIPHER_AES) {
-    CRYPT_AESSetKey(m_pAESContext.get(), 16,
+    CRYPT_AESSetKey(m_pAESContext.get(),
                     m_KeyLen == 32 ? m_EncryptKey : realkey, m_KeyLen,
                     bEncrypt);
     if (bEncrypt) {
@@ -83,36 +75,36 @@
       }
       CRYPT_AESSetIV(m_pAESContext.get(), iv);
       memcpy(dest_buf, iv, 16);
-      int nblocks = src_size / 16;
-      CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + 16, src_buf,
+      int nblocks = source.size() / 16;
+      CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + 16, source.data(),
                        nblocks * 16);
       uint8_t padding[16];
-      memcpy(padding, src_buf + nblocks * 16, src_size % 16);
-      memset(padding + src_size % 16, 16 - src_size % 16, 16 - src_size % 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(), src_buf);
-      CRYPT_AESDecrypt(m_pAESContext.get(), dest_buf, src_buf + 16,
-                       src_size - 16);
-      dest_size = src_size - 16;
+      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];
     }
   } else {
-    ASSERT(dest_size == src_size);
-    if (dest_buf != src_buf) {
-      memcpy(dest_buf, src_buf, src_size);
-    }
-    CRYPT_ArcFourCryptBlock(dest_buf, dest_size, realkey, realkeylen);
+    ASSERT(dest_size == source.size());
+    if (dest_buf != source.data())
+      memcpy(dest_buf, source.data(), source.size());
+    CRYPT_ArcFourCryptBlock({dest_buf, dest_size}, {realkey, realkeylen});
   }
 }
 
 struct AESCryptContext {
   bool m_bIV;
-  uint8_t m_Block[16];
   uint32_t m_BlockOffset;
   CRYPT_aes_context m_Context;
+  uint8_t m_Block[16];
 };
 
 void* CPDF_CryptoHandler::CryptStart(uint32_t objnum,
@@ -125,7 +117,7 @@
     AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
     pContext->m_bIV = true;
     pContext->m_BlockOffset = 0;
-    CRYPT_AESSetKey(&pContext->m_Context, 16, m_EncryptKey, 32, bEncrypt);
+    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();
@@ -137,21 +129,19 @@
   uint8_t key1[48];
   PopulateKey(objnum, gennum, key1);
 
-  if (m_Cipher == FXCIPHER_AES) {
+  if (m_Cipher == FXCIPHER_AES)
     memcpy(key1 + m_KeyLen + 5, "sAlT", 4);
-  }
+
   uint8_t realkey[16];
-  CRYPT_MD5Generate(
-      key1, m_Cipher == FXCIPHER_AES ? m_KeyLen + 9 : m_KeyLen + 5, realkey);
-  int realkeylen = m_KeyLen + 5;
-  if (realkeylen > 16) {
-    realkeylen = 16;
-  }
+  size_t len = m_Cipher == FXCIPHER_AES ? 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) {
     AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
     pContext->m_bIV = true;
     pContext->m_BlockOffset = 0;
-    CRYPT_AESSetKey(&pContext->m_Context, 16, realkey, 16, bEncrypt);
+    CRYPT_AESSetKey(&pContext->m_Context, realkey, 16, bEncrypt);
     if (bEncrypt) {
       for (int i = 0; i < 16; i++) {
         pContext->m_Block[i] = (uint8_t)rand();
@@ -161,42 +151,41 @@
     return pContext;
   }
   CRYPT_rc4_context* pContext = FX_Alloc(CRYPT_rc4_context, 1);
-  CRYPT_ArcFourSetup(pContext, realkey, realkeylen);
+  CRYPT_ArcFourSetup(pContext, {realkey, realkeylen});
   return pContext;
 }
 
 bool CPDF_CryptoHandler::CryptStream(void* context,
-                                     const uint8_t* src_buf,
-                                     uint32_t src_size,
+                                     pdfium::span<const uint8_t> source,
                                      CFX_BinaryBuf& dest_buf,
                                      bool bEncrypt) {
-  if (!context) {
+  if (!context)
     return false;
-  }
+
   if (m_Cipher == FXCIPHER_NONE) {
-    dest_buf.AppendBlock(src_buf, src_size);
+    dest_buf.AppendBlock(source.data(), source.size());
     return true;
   }
   if (m_Cipher == FXCIPHER_RC4) {
     int old_size = dest_buf.GetSize();
-    dest_buf.AppendBlock(src_buf, src_size);
-    CRYPT_ArcFourCrypt(reinterpret_cast<CRYPT_rc4_context*>(context),
-                       dest_buf.GetBuffer() + old_size, src_size);
+    dest_buf.AppendBlock(source.data(), source.size());
+    CRYPT_ArcFourCrypt(static_cast<CRYPT_rc4_context*>(context),
+                       dest_buf.GetSpan().subspan(old_size, source.size()));
     return true;
   }
-  AESCryptContext* pContext = reinterpret_cast<AESCryptContext*>(context);
+  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 = src_size;
+  uint32_t src_left = source.size();
   while (1) {
     uint32_t copy_size = 16 - pContext->m_BlockOffset;
     if (copy_size > src_left) {
       copy_size = src_left;
     }
-    memcpy(pContext->m_Block + pContext->m_BlockOffset, src_buf + src_off,
+    memcpy(pContext->m_Block + pContext->m_BlockOffset, source.data() + src_off,
            copy_size);
     src_off += copy_size;
     src_left -= copy_size;
@@ -206,7 +195,7 @@
         CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block);
         pContext->m_bIV = false;
         pContext->m_BlockOffset = 0;
-      } else if (src_off < src_size) {
+      } else if (src_off < source.size()) {
         uint8_t block_buf[16];
         if (bEncrypt) {
           CRYPT_AESEncrypt(&pContext->m_Context, block_buf, pContext->m_Block,
@@ -238,7 +227,7 @@
     FX_Free(context);
     return true;
   }
-  AESCryptContext* pContext = (AESCryptContext*)context;
+  auto* pContext = static_cast<AESCryptContext*>(context);
   if (bEncrypt) {
     uint8_t block_buf[16];
     if (pContext->m_BlockOffset == 16) {
@@ -267,7 +256,7 @@
                                        const ByteString& str) {
   CFX_BinaryBuf dest_buf;
   void* context = DecryptStart(objnum, gennum);
-  DecryptStream(context, str.raw_str(), str.GetLength(), dest_buf);
+  DecryptStream(context, str.raw_span(), dest_buf);
   DecryptFinish(context, dest_buf);
   return ByteString(dest_buf.GetBuffer(), dest_buf.GetSize());
 }
@@ -283,10 +272,9 @@
   return m_Cipher == FXCIPHER_AES;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_CryptoHandler::DecryptObjectTree(
-    std::unique_ptr<CPDF_Object> object) {
+bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr<CPDF_Object> object) {
   if (!object)
-    return nullptr;
+    return false;
 
   struct MayBeSignature {
     const CPDF_Dictionary* parent;
@@ -297,7 +285,7 @@
   const uint32_t obj_num = object->GetObjNum();
   const uint32_t gen_num = object->GetGenNum();
 
-  CPDF_Object* object_to_decrypt = object.get();
+  CPDF_Object* object_to_decrypt = object.Get();
   while (object_to_decrypt) {
     CPDF_NonConstObjectWalker walker(object_to_decrypt);
     object_to_decrypt = nullptr;
@@ -305,7 +293,8 @@
       const CPDF_Dictionary* parent_dict =
           walker.GetParent() ? walker.GetParent()->GetDict() : nullptr;
       if (walker.dictionary_key() == kContentsKey &&
-          (parent_dict->KeyExist(kTypeKey) || parent_dict->KeyExist(kFTKey))) {
+          (parent_dict->KeyExist(kTypeKey) ||
+           parent_dict->KeyExist(pdfium::form_fields::kFT))) {
         // This object may be contents of signature dictionary.
         // But now values of 'Type' and 'FT' of dictionary keys are encrypted,
         // and we can not check this.
@@ -330,7 +319,7 @@
         stream_access->LoadAllDataRaw();
 
         if (IsCipherAES() && stream_access->GetSize() < 16) {
-          stream->SetData(nullptr, 0);
+          stream->SetData({});
           continue;
         }
 
@@ -339,21 +328,20 @@
 
         void* context = DecryptStart(obj_num, gen_num);
         bool decrypt_result =
-            DecryptStream(context, stream_access->GetData(),
-                          stream_access->GetSize(), decrypted_buf);
+            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->SetData(decrypted_buf.DetachBuffer(), decrypted_size);
+          stream->TakeData(decrypted_buf.DetachBuffer(), decrypted_size);
         } else {
           // Decryption failed, set the stream to empty
-          stream->SetData(nullptr, 0);
+          stream->SetData({});
         }
       }
     }
     // Signature dictionaries check.
     while (!may_be_sign_dictionaries.empty()) {
-      auto dict_and_contents = std::move(may_be_sign_dictionaries.top());
+      auto dict_and_contents = may_be_sign_dictionaries.top();
       may_be_sign_dictionaries.pop();
       if (!IsSignatureDictionary(dict_and_contents.parent)) {
         // This is not signature dictionary. Do decrypt its contents.
@@ -362,43 +350,37 @@
       }
     }
   }
-  return object;
+  return true;
 }
 
 bool CPDF_CryptoHandler::DecryptStream(void* context,
-                                       const uint8_t* src_buf,
-                                       uint32_t src_size,
+                                       pdfium::span<const uint8_t> source,
                                        CFX_BinaryBuf& dest_buf) {
-  return CryptStream(context, src_buf, src_size, dest_buf, false);
+  return CryptStream(context, source, dest_buf, false);
 }
 
 bool CPDF_CryptoHandler::DecryptFinish(void* context, CFX_BinaryBuf& dest_buf) {
   return CryptFinish(context, dest_buf, false);
 }
-uint32_t CPDF_CryptoHandler::EncryptGetSize(uint32_t objnum,
-                                            uint32_t version,
-                                            const uint8_t* src_buf,
-                                            uint32_t src_size) {
-  if (m_Cipher == FXCIPHER_AES) {
-    return src_size + 32;
-  }
-  return src_size;
+
+size_t CPDF_CryptoHandler::EncryptGetSize(
+    pdfium::span<const uint8_t> source) const {
+  return m_Cipher == FXCIPHER_AES ? source.size() + 32 : source.size();
 }
 
 bool CPDF_CryptoHandler::EncryptContent(uint32_t objnum,
                                         uint32_t gennum,
-                                        const uint8_t* src_buf,
-                                        uint32_t src_size,
+                                        pdfium::span<const uint8_t> source,
                                         uint8_t* dest_buf,
                                         uint32_t& dest_size) {
-  CryptBlock(true, objnum, gennum, src_buf, src_size, dest_buf, dest_size);
+  CryptBlock(true, objnum, gennum, source, dest_buf, dest_size);
   return true;
 }
 
 CPDF_CryptoHandler::CPDF_CryptoHandler(int cipher,
                                        const uint8_t* key,
-                                       int keylen)
-    : m_KeyLen(std::min(keylen, 32)), m_Cipher(cipher) {
+                                       size_t keylen)
+    : m_KeyLen(std::min<size_t>(keylen, 32)), m_Cipher(cipher) {
   ASSERT(cipher != FXCIPHER_AES || keylen == 16 || keylen == 24 ||
          keylen == 32);
   ASSERT(cipher != FXCIPHER_AES2 || keylen == 32);
@@ -411,7 +393,7 @@
     m_pAESContext.reset(FX_Alloc(CRYPT_aes_context, 1));
 }
 
-CPDF_CryptoHandler::~CPDF_CryptoHandler() {}
+CPDF_CryptoHandler::~CPDF_CryptoHandler() = default;
 
 void CPDF_CryptoHandler::PopulateKey(uint32_t objnum,
                                      uint32_t gennum,
diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.h b/core/fpdfapi/parser/cpdf_crypto_handler.h
index 32eeaef..edfba97 100644
--- a/core/fpdfapi/parser/cpdf_crypto_handler.h
+++ b/core/fpdfapi/parser/cpdf_crypto_handler.h
@@ -9,12 +9,12 @@
 
 #include <memory>
 
-#include "core/fdrm/crypto/fx_crypt.h"
+#include "core/fdrm/fx_crypt.h"
 #include "core/fxcrt/cfx_binarybuf.h"
-#include "core/fxcrt/fx_memory.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;
@@ -22,22 +22,16 @@
 
 class CPDF_CryptoHandler {
  public:
-  CPDF_CryptoHandler(int cipher, const uint8_t* key, int keylen);
+  CPDF_CryptoHandler(int cipher, const uint8_t* key, size_t keylen);
   ~CPDF_CryptoHandler();
 
   static bool IsSignatureDictionary(const CPDF_Dictionary* dictionary);
 
-  std::unique_ptr<CPDF_Object> DecryptObjectTree(
-      std::unique_ptr<CPDF_Object> object);
-
-  uint32_t EncryptGetSize(uint32_t objnum,
-                          uint32_t version,
-                          const uint8_t* src_buf,
-                          uint32_t src_size);
+  bool DecryptObjectTree(RetainPtr<CPDF_Object> object);
+  size_t EncryptGetSize(pdfium::span<const uint8_t> source) const;
   bool EncryptContent(uint32_t objnum,
-                      uint32_t version,
-                      const uint8_t* src_buf,
-                      uint32_t src_size,
+                      uint32_t gennum,
+                      pdfium::span<const uint8_t> source,
                       uint8_t* dest_buf,
                       uint32_t& dest_size);
 
@@ -48,8 +42,7 @@
   void* DecryptStart(uint32_t objnum, uint32_t gennum);
   ByteString Decrypt(uint32_t objnum, uint32_t gennum, const ByteString& str);
   bool DecryptStream(void* context,
-                     const uint8_t* src_buf,
-                     uint32_t src_size,
+                     pdfium::span<const uint8_t> source,
                      CFX_BinaryBuf& dest_buf);
   bool DecryptFinish(void* context, CFX_BinaryBuf& dest_buf);
 
@@ -57,22 +50,20 @@
   void CryptBlock(bool bEncrypt,
                   uint32_t objnum,
                   uint32_t gennum,
-                  const uint8_t* src_buf,
-                  uint32_t src_size,
+                  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,
-                   const uint8_t* src_buf,
-                   uint32_t src_size,
+                   pdfium::span<const uint8_t> source,
                    CFX_BinaryBuf& dest_buf,
                    bool bEncrypt);
   bool CryptFinish(void* context, CFX_BinaryBuf& dest_buf, bool bEncrypt);
 
-  uint8_t m_EncryptKey[32];
-  int m_KeyLen;
-  int m_Cipher;
+  const size_t m_KeyLen;
+  const int m_Cipher;
   std::unique_ptr<CRYPT_aes_context, FxFreeDeleter> m_pAESContext;
+  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 af80fd9..457ae61 100644
--- a/core/fpdfapi/parser/cpdf_data_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_data_avail.cpp
@@ -10,7 +10,6 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_cross_ref_avail.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -23,10 +22,11 @@
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #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/cfx_memorystream.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
@@ -34,16 +34,16 @@
 namespace {
 
 // static
-const CPDF_Object* GetResourceObject(const CPDF_Dictionary* pDict) {
+CPDF_Object* GetResourceObject(CPDF_Dictionary* pDict) {
   constexpr size_t kMaxHierarchyDepth = 64;
   size_t depth = 0;
 
-  const CPDF_Dictionary* dictionary_to_check = pDict;
+  CPDF_Dictionary* dictionary_to_check = pDict;
   while (dictionary_to_check) {
-    const CPDF_Object* result = dictionary_to_check->GetObjectFor("Resources");
+    CPDF_Object* result = dictionary_to_check->GetObjectFor("Resources");
     if (result)
       return result;
-    const CPDF_Object* parent = dictionary_to_check->GetObjectFor("Parent");
+    CPDF_Object* parent = dictionary_to_check->GetObjectFor("Parent");
     dictionary_to_check = parent ? parent->GetDict() : nullptr;
 
     if (++depth > kMaxHierarchyDepth) {
@@ -56,9 +56,9 @@
 
 class HintsScope {
  public:
-  HintsScope(CPDF_ReadValidator* validator,
+  HintsScope(RetainPtr<CPDF_ReadValidator> validator,
              CPDF_DataAvail::DownloadHints* hints)
-      : validator_(validator) {
+      : validator_(std::move(validator)) {
     ASSERT(validator_);
     validator_->SetDownloadHints(hints);
   }
@@ -66,7 +66,7 @@
   ~HintsScope() { validator_->SetDownloadHints(nullptr); }
 
  private:
-  UnownedPtr<CPDF_ReadValidator> validator_;
+  RetainPtr<CPDF_ReadValidator> validator_;
 };
 
 }  // namespace
@@ -79,14 +79,23 @@
     FileAvail* pFileAvail,
     const RetainPtr<IFX_SeekableReadStream>& pFileRead,
     bool bSupportHintTable)
-    : m_pFileAvail(pFileAvail),
-      m_pFileRead(
-          pdfium::MakeRetain<CPDF_ReadValidator>(pFileRead, m_pFileAvail)),
+    : m_pFileRead(
+          pdfium::MakeRetain<CPDF_ReadValidator>(pFileRead, pFileAvail)),
       m_dwFileLen(m_pFileRead->GetSize()),
       m_bSupportHintTable(bSupportHintTable) {}
 
 CPDF_DataAvail::~CPDF_DataAvail() {
   m_pHintTables.reset();
+  if (m_pDocument)
+    m_pDocument->RemoveObserver(this);
+}
+
+void CPDF_DataAvail::OnObservableDestroyed() {
+  m_pDocument = nullptr;
+  m_pFormAvail.reset();
+  m_PagesArray.clear();
+  m_PagesObjAvail.clear();
+  m_PagesResourcesAvail.clear();
 }
 
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::IsDocAvail(
@@ -94,8 +103,7 @@
   if (!m_dwFileLen)
     return DataError;
 
-  const HintsScope hints_scope(m_pFileRead.Get(), pHints);
-
+  const HintsScope hints_scope(GetValidator(), pHints);
   while (!m_bDocAvail) {
     if (!CheckDocStatus())
       return DataNotAvailable;
@@ -133,6 +141,7 @@
       return LoadAllFile();
     case PDF_DATAAVAIL_PAGE_LATERLOAD:
       m_docStatus = PDF_DATAAVAIL_PAGE;
+      FALLTHROUGH;
     default:
       m_bDocAvail = true;
       return true;
@@ -164,7 +173,7 @@
 
 bool CPDF_DataAvail::CheckAndLoadAllXref() {
   if (!m_pCrossRefAvail) {
-    const CPDF_ReadValidator::Session read_session(GetValidator().Get());
+    const CPDF_ReadValidator::Session read_session(GetValidator());
     const FX_FILESIZE last_xref_offset = m_parser.ParseStartXRef();
     if (GetValidator()->has_read_problems())
       return false;
@@ -197,15 +206,12 @@
     return false;
   }
 
-  m_dwRootObjNum = m_parser.GetRootObjNum();
-  m_dwInfoObjNum = m_parser.GetInfoObjNum();
-  m_pCurrentParser = &m_parser;
   m_docStatus = PDF_DATAAVAIL_ROOT;
   return true;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_DataAvail::GetObject(uint32_t objnum,
-                                                       bool* pExistInFile) {
+RetainPtr<CPDF_Object> CPDF_DataAvail::GetObject(uint32_t objnum,
+                                                 bool* pExistInFile) {
   CPDF_Parser* pParser = nullptr;
 
   if (pExistInFile)
@@ -213,10 +219,10 @@
 
   pParser = m_pDocument ? m_pDocument->GetParser() : &m_parser;
 
-  std::unique_ptr<CPDF_Object> pRet;
+  RetainPtr<CPDF_Object> pRet;
   if (pParser) {
-    const CPDF_ReadValidator::Session read_session(GetValidator().Get());
-    pRet = pParser->ParseIndirectObject(nullptr, objnum);
+    const CPDF_ReadValidator::Session read_session(GetValidator());
+    pRet = pParser->ParseIndirectObject(objnum);
     if (GetValidator()->has_read_problems())
       return nullptr;
   }
@@ -228,56 +234,48 @@
 }
 
 bool CPDF_DataAvail::CheckInfo() {
-  bool bExist = false;
-  std::unique_ptr<CPDF_Object> pInfo = GetObject(m_dwInfoObjNum, &bExist);
-  if (bExist && !pInfo) {
-    if (m_docStatus == PDF_DATAAVAIL_ERROR) {
-      m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
-      return true;
-    }
-    return false;
+  const uint32_t dwInfoObjNum = m_parser.GetInfoObjNum();
+  if (dwInfoObjNum == CPDF_Object::kInvalidObjNum) {
+    m_docStatus = PDF_DATAAVAIL_PAGETREE;
+    return true;
   }
+
+  const CPDF_ReadValidator::Session read_session(GetValidator());
+  m_parser.ParseIndirectObject(dwInfoObjNum);
+  if (GetValidator()->has_read_problems())
+    return false;
+
   m_docStatus = PDF_DATAAVAIL_PAGETREE;
   return true;
 }
 
 bool CPDF_DataAvail::CheckRoot() {
-  bool bExist = false;
-  m_pRoot = GetObject(m_dwRootObjNum, &bExist);
-  if (!bExist) {
-    m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
+  const uint32_t dwRootObjNum = m_parser.GetRootObjNum();
+  if (dwRootObjNum == CPDF_Object::kInvalidObjNum) {
+    m_docStatus = PDF_DATAAVAIL_ERROR;
     return true;
   }
 
-  if (!m_pRoot) {
-    if (m_docStatus == PDF_DATAAVAIL_ERROR) {
-      m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
-      return true;
-    }
+  const CPDF_ReadValidator::Session read_session(GetValidator());
+  m_pRoot = ToDictionary(m_parser.ParseIndirectObject(dwRootObjNum));
+  if (GetValidator()->has_read_problems())
     return false;
-  }
 
-  CPDF_Dictionary* pDict = m_pRoot->GetDict();
-  if (!pDict) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
-    return false;
-  }
-
-  CPDF_Reference* pRef = ToReference(pDict->GetObjectFor("Pages"));
+  const CPDF_Reference* pRef =
+      ToReference(m_pRoot ? m_pRoot->GetObjectFor("Pages") : nullptr);
   if (!pRef) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return false;
   }
 
   m_PagesObjNum = pRef->GetRefObjNum();
-
-  m_docStatus = m_dwInfoObjNum ? PDF_DATAAVAIL_INFO : PDF_DATAAVAIL_PAGETREE;
+  m_docStatus = PDF_DATAAVAIL_INFO;
   return true;
 }
 
 bool CPDF_DataAvail::PreparePageItem() {
   const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
-  CPDF_Reference* pRef =
+  const CPDF_Reference* pRef =
       ToReference(pRoot ? pRoot->GetObjectFor("Pages") : nullptr);
   if (!pRef) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
@@ -285,7 +283,6 @@
   }
 
   m_PagesObjNum = pRef->GetRefObjNum();
-  m_pCurrentParser = m_pDocument->GetParser();
   m_docStatus = PDF_DATAAVAIL_PAGETREE;
   return true;
 }
@@ -302,16 +299,17 @@
   std::vector<uint32_t> UnavailObjList;
   for (uint32_t dwPageObjNum : m_PageObjList) {
     bool bExists = false;
-    std::unique_ptr<CPDF_Object> pObj = GetObject(dwPageObjNum, &bExists);
+    RetainPtr<CPDF_Object> pObj = GetObject(dwPageObjNum, &bExists);
     if (!pObj) {
       if (bExists)
         UnavailObjList.push_back(dwPageObjNum);
       continue;
     }
-    CPDF_Array* pArray = ToArray(pObj.get());
+    CPDF_Array* pArray = ToArray(pObj.Get());
     if (pArray) {
-      for (const auto& pArrayObj : *pArray) {
-        if (CPDF_Reference* pRef = ToReference(pArrayObj.get()))
+      CPDF_ArrayLocker locker(pArray);
+      for (const auto& pArrayObj : locker) {
+        if (CPDF_Reference* pRef = ToReference(pArrayObj.Get()))
           UnavailObjList.push_back(pRef->GetRefObjNum());
       }
     }
@@ -331,8 +329,8 @@
   }
   size_t iPages = m_PagesArray.size();
   for (size_t i = 0; i < iPages; ++i) {
-    std::unique_ptr<CPDF_Object> pPages = std::move(m_PagesArray[i]);
-    if (pPages && !GetPageKids(m_pCurrentParser, pPages.get())) {
+    RetainPtr<CPDF_Object> pPages = std::move(m_PagesArray[i]);
+    if (pPages && !GetPageKids(pPages.Get())) {
       m_PagesArray.clear();
       m_docStatus = PDF_DATAAVAIL_ERROR;
       return false;
@@ -345,24 +343,19 @@
   return true;
 }
 
-bool CPDF_DataAvail::GetPageKids(CPDF_Parser* pParser, CPDF_Object* pPages) {
-  if (!pParser) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
-    return false;
-  }
-
+bool CPDF_DataAvail::GetPageKids(CPDF_Object* pPages) {
   CPDF_Dictionary* pDict = pPages->GetDict();
   CPDF_Object* pKids = pDict ? pDict->GetObjectFor("Kids") : nullptr;
   if (!pKids)
     return true;
 
   switch (pKids->GetType()) {
-    case CPDF_Object::REFERENCE:
+    case CPDF_Object::kReference:
       m_PageObjList.push_back(pKids->AsReference()->GetRefObjNum());
       break;
-    case CPDF_Object::ARRAY: {
+    case CPDF_Object::kArray: {
       CPDF_Array* pKidsArray = pKids->AsArray();
-      for (size_t i = 0; i < pKidsArray->GetCount(); ++i) {
+      for (size_t i = 0; i < pKidsArray->size(); ++i) {
         if (CPDF_Reference* pRef = ToReference(pKidsArray->GetObjectAt(i)))
           m_PageObjList.push_back(pRef->GetRefObjNum());
       }
@@ -377,7 +370,7 @@
 
 bool CPDF_DataAvail::CheckPages() {
   bool bExists = false;
-  std::unique_ptr<CPDF_Object> pPages = GetObject(m_PagesObjNum, &bExists);
+  RetainPtr<CPDF_Object> pPages = GetObject(m_PagesObjNum, &bExists);
   if (!bExists) {
     m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
     return true;
@@ -391,7 +384,7 @@
     return false;
   }
 
-  if (!GetPageKids(m_pCurrentParser, pPages.get())) {
+  if (!GetPageKids(pPages.Get())) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return false;
   }
@@ -442,41 +435,28 @@
 }
 
 bool CPDF_DataAvail::CheckHintTables() {
-  if (m_pLinearized->GetPageCount() <= 1) {
-    m_docStatus = PDF_DATAAVAIL_DONE;
+  const CPDF_ReadValidator::Session read_session(GetValidator());
+  m_pHintTables =
+      CPDF_HintTables::Parse(GetSyntaxParser(), m_pLinearized.get());
+
+  if (GetValidator()->read_error()) {
+    m_docStatus = PDF_DATAAVAIL_ERROR;
     return true;
   }
-  if (!m_pLinearized->HasHintTable()) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+  if (GetValidator()->has_unavailable_data())
     return false;
-  }
-
-  const FX_FILESIZE szHintStart = m_pLinearized->GetHintStart();
-  const uint32_t szHintLength = m_pLinearized->GetHintLength();
-
-  if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable(szHintStart,
-                                                             szHintLength))
-    return false;
-
-  auto pHintTables = pdfium::MakeUnique<CPDF_HintTables>(GetValidator().Get(),
-                                                         m_pLinearized.get());
-  std::unique_ptr<CPDF_Object> pHintStream =
-      ParseIndirectObjectAt(szHintStart, 0);
-  CPDF_Stream* pStream = ToStream(pHintStream.get());
-  if (pStream && pHintTables->LoadHintStream(pStream))
-    m_pHintTables = std::move(pHintTables);
 
   m_docStatus = PDF_DATAAVAIL_DONE;
   return true;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_DataAvail::ParseIndirectObjectAt(
+RetainPtr<CPDF_Object> CPDF_DataAvail::ParseIndirectObjectAt(
     FX_FILESIZE pos,
     uint32_t objnum,
-    CPDF_IndirectObjectHolder* pObjList) {
+    CPDF_IndirectObjectHolder* pObjList) const {
   const FX_FILESIZE SavedPos = GetSyntaxParser()->GetPos();
   GetSyntaxParser()->SetPos(pos);
-  std::unique_ptr<CPDF_Object> result = GetSyntaxParser()->GetIndirectObject(
+  RetainPtr<CPDF_Object> result = GetSyntaxParser()->GetIndirectObject(
       pObjList, CPDF_SyntaxParser::ParseType::kLoose);
   GetSyntaxParser()->SetPos(SavedPos);
   return (result && (!objnum || result->GetObjNum() == objnum))
@@ -503,15 +483,16 @@
   if (m_bHeaderAvail)
     return DocAvailStatus::DataAvailable;
 
-  const CPDF_ReadValidator::Session read_session(GetValidator().Get());
-  const int32_t header_offset = GetHeaderOffset(GetValidator());
+  const CPDF_ReadValidator::Session read_session(GetValidator());
+  const Optional<FX_FILESIZE> header_offset = GetHeaderOffset(GetValidator());
   if (GetValidator()->has_read_problems())
     return DocAvailStatus::DataNotAvailable;
 
-  if (header_offset == kInvalidHeaderOffset)
+  if (!header_offset)
     return DocAvailStatus::DataError;
 
-  m_parser.m_pSyntax->InitParserWithValidator(GetValidator(), header_offset);
+  m_parser.m_pSyntax =
+      pdfium::MakeUnique<CPDF_SyntaxParser>(GetValidator(), *header_offset);
   m_pLinearized = m_parser.ParseLinearizedHeader();
   if (GetValidator()->has_read_problems())
     return DocAvailStatus::DataNotAvailable;
@@ -546,7 +527,7 @@
 bool CPDF_DataAvail::CheckArrayPageNode(uint32_t dwPageNo,
                                         PageNode* pPageNode) {
   bool bExists = false;
-  std::unique_ptr<CPDF_Object> pPages = GetObject(dwPageNo, &bExists);
+  RetainPtr<CPDF_Object> pPages = GetObject(dwPageNo, &bExists);
   if (!bExists) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return false;
@@ -562,7 +543,7 @@
   }
 
   pPageNode->m_type = PDF_PAGENODE_PAGES;
-  for (size_t i = 0; i < pArray->GetCount(); ++i) {
+  for (size_t i = 0; i < pArray->size(); ++i) {
     CPDF_Reference* pKid = ToReference(pArray->GetObjectAt(i));
     if (!pKid)
       continue;
@@ -577,7 +558,7 @@
 bool CPDF_DataAvail::CheckUnknownPageNode(uint32_t dwPageNo,
                                           PageNode* pPageNode) {
   bool bExists = false;
-  std::unique_ptr<CPDF_Object> pPage = GetObject(dwPageNo, &bExists);
+  RetainPtr<CPDF_Object> pPage = GetObject(dwPageNo, &bExists);
   if (!bExists) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return false;
@@ -618,16 +599,16 @@
   }
 
   switch (pKids->GetType()) {
-    case CPDF_Object::REFERENCE: {
+    case CPDF_Object::kReference: {
       CPDF_Reference* pKid = pKids->AsReference();
       auto pNode = pdfium::MakeUnique<PageNode>();
       pNode->m_dwPageNo = pKid->GetRefObjNum();
       pPageNode->m_ChildNodes.push_back(std::move(pNode));
       break;
     }
-    case CPDF_Object::ARRAY: {
+    case CPDF_Object::kArray: {
       CPDF_Array* pKidsArray = pKids->AsArray();
-      for (size_t i = 0; i < pKidsArray->GetCount(); ++i) {
+      for (size_t i = 0; i < pKidsArray->size(); ++i) {
         CPDF_Reference* pKid = ToReference(pKidsArray->GetObjectAt(i));
         if (!pKid)
           continue;
@@ -712,7 +693,7 @@
 
 bool CPDF_DataAvail::CheckPageCount() {
   bool bExists = false;
-  std::unique_ptr<CPDF_Object> pPages = GetObject(m_PagesObjNum, &bExists);
+  RetainPtr<CPDF_Object> pPages = GetObject(m_PagesObjNum, &bExists);
   if (!bExists) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return false;
@@ -767,21 +748,22 @@
   }
 
   if (!m_bMainXRefLoadTried) {
-    const FX_SAFE_FILESIZE main_xref_offset =
+    const FX_SAFE_FILESIZE prev =
         m_pDocument->GetParser()->GetTrailer()->GetIntegerFor("Prev");
-    if (!main_xref_offset.IsValid())
+    const FX_FILESIZE main_xref_offset = prev.ValueOrDefault(-1);
+    if (main_xref_offset < 0)
       return DataError;
 
-    if (main_xref_offset.ValueOrDie() == 0)
+    if (main_xref_offset == 0)
       return DataAvailable;
 
     FX_SAFE_SIZE_T data_size = m_dwFileLen;
-    data_size -= main_xref_offset.ValueOrDie();
+    data_size -= main_xref_offset;
     if (!data_size.IsValid())
       return DataError;
 
     if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable(
-            main_xref_offset.ValueOrDie(), data_size.ValueOrDie()))
+            main_xref_offset, data_size.ValueOrDie()))
       return DataNotAvailable;
 
     CPDF_Parser::Error eRet =
@@ -822,17 +804,16 @@
   if (pdfium::ContainsKey(m_pagesLoadState, dwPage))
     return DataAvailable;
 
-  const HintsScope hints_scope(GetValidator().Get(), pHints);
-
+  const HintsScope hints_scope(GetValidator(), pHints);
   if (m_pLinearized) {
     if (dwPage == m_pLinearized->GetFirstPageNo()) {
-      CPDF_Dictionary* pPageDict = m_pDocument->GetPage(safePage.ValueOrDie());
+      auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie());
       if (!pPageDict)
         return DataError;
 
       auto page_num_obj = std::make_pair(
           dwPage, pdfium::MakeUnique<CPDF_PageObjectAvail>(
-                      GetValidator().Get(), m_pDocument, pPageDict));
+                      GetValidator(), m_pDocument.Get(), pPageDict));
 
       CPDF_PageObjectAvail* page_obj_avail =
           m_PagesObjAvail.insert(std::move(page_num_obj)).first->second.get();
@@ -848,7 +829,7 @@
       nResult = m_pHintTables->CheckPage(dwPage);
       if (nResult != DataAvailable)
         return nResult;
-      if (GetPage(dwPage)) {
+      if (GetPageDictionary(dwPage)) {
         m_pagesLoadState.insert(dwPage);
         return DataAvailable;
       }
@@ -877,14 +858,14 @@
   if (CheckAcroForm() == DocFormStatus::FormNotAvailable)
     return DataNotAvailable;
 
-  CPDF_Dictionary* pPageDict = m_pDocument->GetPage(safePage.ValueOrDie());
+  auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie());
   if (!pPageDict)
     return DataError;
 
   {
     auto page_num_obj = std::make_pair(
         dwPage, pdfium::MakeUnique<CPDF_PageObjectAvail>(
-                    GetValidator().Get(), m_pDocument, pPageDict));
+                    GetValidator(), m_pDocument.Get(), pPageDict));
     CPDF_PageObjectAvail* page_obj_avail =
         m_PagesObjAvail.insert(std::move(page_num_obj)).first->second.get();
     const DocAvailStatus status = page_obj_avail->CheckAvail();
@@ -903,10 +884,10 @@
 }
 
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckResources(
-    const CPDF_Dictionary* page) {
+    CPDF_Dictionary* page) {
   ASSERT(page);
-  const CPDF_ReadValidator::Session read_session(GetValidator().Get());
-  const CPDF_Object* resources = GetResourceObject(page);
+  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_Object* resources = GetResourceObject(page);
   if (GetValidator()->has_read_problems())
     return DocAvailStatus::DataNotAvailable;
 
@@ -917,15 +898,11 @@
       m_PagesResourcesAvail
           .insert(std::make_pair(
               resources, pdfium::MakeUnique<CPDF_PageObjectAvail>(
-                             GetValidator().Get(), m_pDocument, resources)))
+                             GetValidator(), m_pDocument.Get(), resources)))
           .first->second.get();
   return resource_avail->CheckAvail();
 }
 
-RetainPtr<IFX_SeekableReadStream> CPDF_DataAvail::GetFileRead() const {
-  return m_pFileRead;
-}
-
 RetainPtr<CPDF_ReadValidator> CPDF_DataAvail::GetValidator() const {
   return m_pFileRead;
 }
@@ -941,10 +918,10 @@
   return m_pDocument ? m_pDocument->GetPageCount() : 0;
 }
 
-CPDF_Dictionary* CPDF_DataAvail::GetPage(int index) {
+CPDF_Dictionary* CPDF_DataAvail::GetPageDictionary(int index) const {
   if (!m_pDocument || index < 0 || index >= GetPageCount())
     return nullptr;
-  CPDF_Dictionary* page = m_pDocument->GetPage(index);
+  CPDF_Dictionary* page = m_pDocument->GetPageDictionary(index);
   if (page)
     return page;
   if (!m_pLinearized || !m_pHintTables)
@@ -964,16 +941,17 @@
   // Page object already can be parsed in document.
   if (!m_pDocument->GetIndirectObject(dwObjNum)) {
     m_pDocument->ReplaceIndirectObjectIfHigherGeneration(
-        dwObjNum, ParseIndirectObjectAt(szPageStartPos, dwObjNum, m_pDocument));
+        dwObjNum,
+        ParseIndirectObjectAt(szPageStartPos, dwObjNum, m_pDocument.Get()));
   }
   if (!ValidatePage(index))
     return nullptr;
-  return m_pDocument->GetPage(index);
+  return m_pDocument->GetPageDictionary(index);
 }
 
 CPDF_DataAvail::DocFormStatus CPDF_DataAvail::IsFormAvail(
     DownloadHints* pHints) {
-  const HintsScope hints_scope(GetValidator().Get(), pHints);
+  const HintsScope hints_scope(GetValidator(), pHints);
   return CheckAcroForm();
 }
 
@@ -990,7 +968,7 @@
   }
 
   if (!m_pFormAvail) {
-    const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
+    CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
     if (!pRoot)
       return FormAvailable;
 
@@ -999,7 +977,7 @@
       return FormNotExist;
 
     m_pFormAvail = pdfium::MakeUnique<CPDF_PageObjectAvail>(
-        GetValidator().Get(), m_pDocument, pAcroForm);
+        GetValidator(), m_pDocument.Get(), pAcroForm);
   }
   switch (m_pFormAvail->CheckAvail()) {
     case DocAvailStatus::DataError:
@@ -1014,28 +992,31 @@
   return DocFormStatus::FormError;
 }
 
-bool CPDF_DataAvail::ValidatePage(uint32_t dwPage) {
+bool CPDF_DataAvail::ValidatePage(uint32_t dwPage) const {
   FX_SAFE_INT32 safePage = pdfium::base::checked_cast<int32_t>(dwPage);
-  CPDF_Dictionary* pPageDict = m_pDocument->GetPage(safePage.ValueOrDie());
+  auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie());
   if (!pPageDict)
     return false;
-  CPDF_PageObjectAvail obj_avail(GetValidator().Get(), m_pDocument, pPageDict);
+  CPDF_PageObjectAvail obj_avail(GetValidator(), m_pDocument.Get(), pPageDict);
   return obj_avail.CheckAvail() == DocAvailStatus::DataAvailable;
 }
 
 std::pair<CPDF_Parser::Error, std::unique_ptr<CPDF_Document>>
-CPDF_DataAvail::ParseDocument(const char* password) {
+CPDF_DataAvail::ParseDocument(
+    std::unique_ptr<CPDF_Document::RenderDataIface> pRenderData,
+    std::unique_ptr<CPDF_Document::PageDataIface> pPageData,
+    const char* password) {
   if (m_pDocument) {
     // We already returned parsed document.
     return std::make_pair(CPDF_Parser::HANDLER_ERROR, nullptr);
   }
-  auto parser = pdfium::MakeUnique<CPDF_Parser>();
-  parser->SetPassword(password);
-  auto document = pdfium::MakeUnique<CPDF_Document>(std::move(parser));
+  auto document = pdfium::MakeUnique<CPDF_Document>(std::move(pRenderData),
+                                                    std::move(pPageData));
+  document->AddObserver(this);
 
-  CPDF_ReadValidator::Session read_session(GetValidator().Get());
-  CPDF_Parser::Error error = document->GetParser()->StartLinearizedParse(
-      GetFileRead(), document.get());
+  CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_Parser::Error error =
+      document->LoadLinearizedDoc(GetValidator(), password);
 
   // Additional check, that all ok.
   if (GetValidator()->has_read_problems()) {
diff --git a/core/fpdfapi/parser/cpdf_data_avail.h b/core/fpdfapi/parser/cpdf_data_avail.h
index dfb1f0c..6bcea07 100644
--- a/core/fpdfapi/parser/cpdf_data_avail.h
+++ b/core/fpdfapi/parser/cpdf_data_avail.h
@@ -13,8 +13,8 @@
 #include <utility>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_CrossRefAvail;
@@ -23,8 +23,8 @@
 class CPDF_IndirectObjectHolder;
 class CPDF_LinearizedHeader;
 class CPDF_PageObjectAvail;
-class CPDF_Parser;
 class CPDF_ReadValidator;
+class CPDF_SyntaxParser;
 
 enum PDF_DATAAVAIL_STATUS {
   PDF_DATAAVAIL_HEADER = 0,
@@ -49,7 +49,7 @@
   PDF_PAGENODE_ARRAY,
 };
 
-class CPDF_DataAvail final {
+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
@@ -94,23 +94,27 @@
   CPDF_DataAvail(FileAvail* pFileAvail,
                  const RetainPtr<IFX_SeekableReadStream>& pFileRead,
                  bool bSupportHintTable);
-  ~CPDF_DataAvail();
+  ~CPDF_DataAvail() override;
+
+  // CPDF_Document::Observer:
+  void OnObservableDestroyed() override;
 
   DocAvailStatus IsDocAvail(DownloadHints* pHints);
   DocAvailStatus IsPageAvail(uint32_t dwPage, DownloadHints* pHints);
   DocFormStatus IsFormAvail(DownloadHints* pHints);
   DocLinearizationStatus IsLinearizedPDF();
-  RetainPtr<IFX_SeekableReadStream> GetFileRead() const;
   int GetPageCount() const;
-  CPDF_Dictionary* GetPage(int index);
+  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 CPDF_HintTables* GetHintTables() const { return m_pHintTables.get(); }
 
- protected:
+ private:
   class PageNode {
    public:
     PageNode();
@@ -131,18 +135,17 @@
   bool CheckInfo();
   bool CheckPages();
   bool CheckPage();
-  DocAvailStatus CheckResources(const CPDF_Dictionary* page);
+  DocAvailStatus CheckResources(CPDF_Dictionary* page);
   DocFormStatus CheckAcroForm();
   bool CheckPageStatus();
 
   DocAvailStatus CheckHeaderAndLinearized();
-  std::unique_ptr<CPDF_Object> ParseIndirectObjectAt(
+  RetainPtr<CPDF_Object> ParseIndirectObjectAt(
       FX_FILESIZE pos,
       uint32_t objnum,
-      CPDF_IndirectObjectHolder* pObjList = nullptr);
-  std::unique_ptr<CPDF_Object> GetObject(uint32_t objnum,
-                                         bool* pExistInFile);
-  bool GetPageKids(CPDF_Parser* pParser, CPDF_Object* pPages);
+      CPDF_IndirectObjectHolder* pObjList) const;
+  RetainPtr<CPDF_Object> GetObject(uint32_t objnum, bool* pExistInFile);
+  bool GetPageKids(CPDF_Object* pPages);
   bool PreparePageItem();
   bool LoadPages();
   bool CheckAndLoadAllXref();
@@ -161,21 +164,18 @@
   bool CheckPageCount();
   bool IsFirstCheck(uint32_t dwPage);
   void ResetFirstCheck(uint32_t dwPage);
-  bool ValidatePage(uint32_t dwPage);
+  bool ValidatePage(uint32_t dwPage) const;
   CPDF_SyntaxParser* GetSyntaxParser() const;
 
-  FileAvail* const m_pFileAvail;
   RetainPtr<CPDF_ReadValidator> m_pFileRead;
   CPDF_Parser m_parser;
-  std::unique_ptr<CPDF_Object> m_pRoot;
-  uint32_t m_dwRootObjNum = 0;
-  uint32_t m_dwInfoObjNum = 0;
+  RetainPtr<CPDF_Dictionary> m_pRoot;
   std::unique_ptr<CPDF_LinearizedHeader> m_pLinearized;
   bool m_bDocAvail = false;
   std::unique_ptr<CPDF_CrossRefAvail> m_pCrossRefAvail;
   PDF_DATAAVAIL_STATUS m_docStatus = PDF_DATAAVAIL_HEADER;
   const FX_FILESIZE m_dwFileLen;
-  CPDF_Document* m_pDocument = nullptr;
+  UnownedPtr<CPDF_Document> m_pDocument;
   std::vector<uint32_t> m_PageObjList;
   uint32_t m_PagesObjNum = 0;
   bool m_bLinearedDataOK = false;
@@ -183,16 +183,13 @@
   bool m_bMainXRefLoadedOK = false;
   bool m_bPagesTreeLoad = false;
   bool m_bPagesLoad = false;
-  CPDF_Parser* m_pCurrentParser = nullptr;
   std::unique_ptr<CPDF_PageObjectAvail> m_pFormAvail;
-  std::vector<std::unique_ptr<CPDF_Object>> m_PagesArray;
-  uint32_t m_dwEncryptObjNum = 0;
+  std::vector<RetainPtr<CPDF_Object>> m_PagesArray;
   bool m_bTotalLoadPageTree = false;
   bool m_bCurPageDictLoadOK = false;
   PageNode m_PageNode;
   std::set<uint32_t> m_pageMapCheckState;
   std::set<uint32_t> m_pagesLoadState;
-  std::set<uint32_t> m_SeenPrevPositions;
   std::unique_ptr<CPDF_HintTables> m_pHintTables;
   const bool m_bSupportHintTable;
   std::map<uint32_t, std::unique_ptr<CPDF_PageObjectAvail>> m_PagesObjAvail;
diff --git a/core/fpdfapi/parser/cpdf_dictionary.cpp b/core/fpdfapi/parser/cpdf_dictionary.cpp
index 1e7f6a9..6c4c6e6 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.cpp
+++ b/core/fpdfapi/parser/cpdf_dictionary.cpp
@@ -17,9 +17,10 @@
 #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/fxcrt/fx_stream.h"
 #include "third_party/base/logging.h"
+#include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
 CPDF_Dictionary::CPDF_Dictionary()
@@ -34,18 +35,20 @@
   m_ObjNum = kInvalidObjNum;
   for (auto& it : m_Map) {
     if (it.second && it.second->GetObjNum() == kInvalidObjNum)
-      it.second.release();
+      it.second.Leak();
   }
 }
 
 CPDF_Object::Type CPDF_Dictionary::GetType() const {
-  return DICTIONARY;
+  return kDictionary;
 }
 
-CPDF_Dictionary* CPDF_Dictionary::GetDict() const {
-  // The method should be made non-const if we want to not be const.
-  // See bug #234.
-  return const_cast<CPDF_Dictionary*>(this);
+CPDF_Dictionary* CPDF_Dictionary::GetDict() {
+  return this;
+}
+
+const CPDF_Dictionary* CPDF_Dictionary::GetDict() const {
+  return this;
 }
 
 bool CPDF_Dictionary::IsDictionary() const {
@@ -60,96 +63,122 @@
   return this;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Dictionary::Clone() const {
+RetainPtr<CPDF_Object> CPDF_Dictionary::Clone() const {
   return CloneObjectNonCyclic(false);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Dictionary::CloneNonCyclic(
+RetainPtr<CPDF_Object> CPDF_Dictionary::CloneNonCyclic(
     bool bDirect,
     std::set<const CPDF_Object*>* pVisited) const {
   pVisited->insert(this);
-  auto pCopy = pdfium::MakeUnique<CPDF_Dictionary>(m_pPool);
-  for (const auto& it : *this) {
-    if (!pdfium::ContainsKey(*pVisited, it.second.get())) {
+  auto pCopy = pdfium::MakeRetain<CPDF_Dictionary>(m_pPool);
+  CPDF_DictionaryLocker locker(this);
+  for (const auto& it : locker) {
+    if (!pdfium::ContainsKey(*pVisited, it.second.Get())) {
       std::set<const CPDF_Object*> visited(*pVisited);
       if (auto obj = it.second->CloneNonCyclic(bDirect, &visited))
         pCopy->m_Map.insert(std::make_pair(it.first, std::move(obj)));
     }
   }
-  return std::move(pCopy);
+  return pCopy;
 }
 
-CPDF_Object* CPDF_Dictionary::GetObjectFor(const ByteString& key) const {
+const CPDF_Object* CPDF_Dictionary::GetObjectFor(const ByteString& key) const {
   auto it = m_Map.find(key);
-  return it != m_Map.end() ? it->second.get() : nullptr;
+  return it != m_Map.end() ? it->second.Get() : nullptr;
 }
 
-CPDF_Object* CPDF_Dictionary::GetDirectObjectFor(const ByteString& key) const {
-  CPDF_Object* p = GetObjectFor(key);
+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(
+    const ByteString& key) const {
+  const CPDF_Object* p = GetObjectFor(key);
   return p ? p->GetDirect() : nullptr;
 }
 
+CPDF_Object* CPDF_Dictionary::GetDirectObjectFor(const ByteString& key) {
+  return const_cast<CPDF_Object*>(
+      static_cast<const CPDF_Dictionary*>(this)->GetDirectObjectFor(key));
+}
+
 ByteString CPDF_Dictionary::GetStringFor(const ByteString& key) const {
-  CPDF_Object* p = GetObjectFor(key);
+  const CPDF_Object* p = GetObjectFor(key);
   return p ? p->GetString() : ByteString();
 }
 
 WideString CPDF_Dictionary::GetUnicodeTextFor(const ByteString& key) const {
-  CPDF_Object* p = GetObjectFor(key);
-  if (CPDF_Reference* pRef = ToReference(p))
+  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 {
-  CPDF_Object* p = GetObjectFor(key);
+  const CPDF_Object* p = GetObjectFor(key);
   return p ? p->GetString() : ByteString(def);
 }
 
 int CPDF_Dictionary::GetIntegerFor(const ByteString& key) const {
-  CPDF_Object* p = GetObjectFor(key);
+  const CPDF_Object* p = GetObjectFor(key);
   return p ? p->GetInteger() : 0;
 }
 
 int CPDF_Dictionary::GetIntegerFor(const ByteString& key, int def) const {
-  CPDF_Object* p = GetObjectFor(key);
+  const CPDF_Object* p = GetObjectFor(key);
   return p ? p->GetInteger() : def;
 }
 
 float CPDF_Dictionary::GetNumberFor(const ByteString& key) const {
-  CPDF_Object* p = GetObjectFor(key);
+  const CPDF_Object* p = GetObjectFor(key);
   return p ? p->GetNumber() : 0;
 }
 
 bool CPDF_Dictionary::GetBooleanFor(const ByteString& key,
                                     bool bDefault) const {
-  CPDF_Object* p = GetObjectFor(key);
+  const CPDF_Object* p = GetObjectFor(key);
   return ToBoolean(p) ? p->GetInteger() != 0 : bDefault;
 }
 
-CPDF_Dictionary* CPDF_Dictionary::GetDictFor(const ByteString& key) const {
-  CPDF_Object* p = GetDirectObjectFor(key);
+const CPDF_Dictionary* CPDF_Dictionary::GetDictFor(
+    const ByteString& key) const {
+  const CPDF_Object* p = GetDirectObjectFor(key);
   if (!p)
     return nullptr;
-  if (CPDF_Dictionary* pDict = p->AsDictionary())
+  if (const CPDF_Dictionary* pDict = p->AsDictionary())
     return pDict;
-  if (CPDF_Stream* pStream = p->AsStream())
+  if (const CPDF_Stream* pStream = p->AsStream())
     return pStream->GetDict();
   return nullptr;
 }
 
-CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) const {
+CPDF_Dictionary* CPDF_Dictionary::GetDictFor(const ByteString& key) {
+  return const_cast<CPDF_Dictionary*>(
+      static_cast<const CPDF_Dictionary*>(this)->GetDictFor(key));
+}
+
+const CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) const {
   return ToArray(GetDirectObjectFor(key));
 }
 
-CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) const {
+CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) {
+  return ToArray(GetDirectObjectFor(key));
+}
+
+const CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) const {
+  return ToStream(GetDirectObjectFor(key));
+}
+
+CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) {
   return ToStream(GetDirectObjectFor(key));
 }
 
 CFX_FloatRect CPDF_Dictionary::GetRectFor(const ByteString& key) const {
   CFX_FloatRect rect;
-  CPDF_Array* pArray = GetArrayFor(key);
+  const CPDF_Array* pArray = GetArrayFor(key);
   if (pArray)
     rect = pArray->GetRect();
   return rect;
@@ -157,7 +186,7 @@
 
 CFX_Matrix CPDF_Dictionary::GetMatrixFor(const ByteString& key) const {
   CFX_Matrix matrix;
-  CPDF_Array* pArray = GetArrayFor(key);
+  const CPDF_Array* pArray = GetArrayFor(key);
   if (pArray)
     matrix = pArray->GetMatrix();
   return matrix;
@@ -167,18 +196,23 @@
   return pdfium::ContainsKey(m_Map, key);
 }
 
-bool CPDF_Dictionary::IsSignatureDict() const {
-  return CPDF_CryptoHandler::IsSignatureDictionary(this);
+std::vector<ByteString> CPDF_Dictionary::GetKeys() const {
+  std::vector<ByteString> result;
+  CPDF_DictionaryLocker locker(this);
+  for (const auto& item : locker)
+    result.push_back(item.first);
+  return result;
 }
 
 CPDF_Object* CPDF_Dictionary::SetFor(const ByteString& key,
-                                     std::unique_ptr<CPDF_Object> pObj) {
+                                     RetainPtr<CPDF_Object> pObj) {
+  CHECK(!IsLocked());
   if (!pObj) {
     m_Map.erase(key);
     return nullptr;
   }
   ASSERT(pObj->IsInline());
-  CPDF_Object* pRet = pObj.get();
+  CPDF_Object* pRet = pObj.Get();
   m_Map[MaybeIntern(key)] = std::move(pObj);
   return pRet;
 }
@@ -186,16 +220,18 @@
 void CPDF_Dictionary::ConvertToIndirectObjectFor(
     const ByteString& key,
     CPDF_IndirectObjectHolder* pHolder) {
+  CHECK(!IsLocked());
   auto it = m_Map.find(key);
   if (it == m_Map.end() || it->second->IsReference())
     return;
 
   CPDF_Object* pObj = pHolder->AddIndirectObject(std::move(it->second));
-  it->second = pdfium::MakeUnique<CPDF_Reference>(pHolder, pObj->GetObjNum());
+  it->second = pObj->MakeReference(pHolder);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Dictionary::RemoveFor(const ByteString& key) {
-  std::unique_ptr<CPDF_Object> result;
+RetainPtr<CPDF_Object> CPDF_Dictionary::RemoveFor(const ByteString& key) {
+  CHECK(!IsLocked());
+  RetainPtr<CPDF_Object> result;
   auto it = m_Map.find(key);
   if (it != m_Map.end()) {
     result = std::move(it->second);
@@ -206,6 +242,7 @@
 
 void CPDF_Dictionary::ReplaceKey(const ByteString& oldkey,
                                  const ByteString& newkey) {
+  CHECK(!IsLocked());
   auto old_it = m_Map.find(oldkey);
   if (old_it == m_Map.end())
     return;
@@ -242,27 +279,35 @@
   return m_pPool ? m_pPool->Intern(str) : str;
 }
 
-bool CPDF_Dictionary::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Dictionary::WriteTo(IFX_ArchiveStream* archive,
+                              const CPDF_Encryptor* encryptor) const {
   if (!archive->WriteString("<<"))
     return false;
 
-  for (const auto& it : *this) {
+  const bool is_signature = CPDF_CryptoHandler::IsSignatureDictionary(this);
+
+  CPDF_DictionaryLocker locker(this);
+  for (const auto& it : locker) {
     const ByteString& key = it.first;
-    CPDF_Object* pValue = it.second.get();
+    CPDF_Object* pValue = it.second.Get();
     if (!archive->WriteString("/") ||
         !archive->WriteString(PDF_NameEncode(key).AsStringView())) {
       return false;
     }
-
-    if (!pValue->IsInline()) {
-      if (!archive->WriteString(" ") ||
-          !archive->WriteDWord(pValue->GetObjNum()) ||
-          !archive->WriteString(" 0 R")) {
-        return false;
-      }
-    } else if (!pValue->WriteTo(archive)) {
+    if (!pValue->WriteTo(archive, !is_signature || key != "Contents"
+                                      ? encryptor
+                                      : nullptr)) {
       return false;
     }
   }
   return archive->WriteString(">>");
 }
+
+CPDF_DictionaryLocker::CPDF_DictionaryLocker(const CPDF_Dictionary* pDictionary)
+    : m_pDictionary(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 688e88e..ac1b226 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.h
+++ b/core/fpdfapi/parser/cpdf_dictionary.h
@@ -11,102 +11,141 @@
 #include <memory>
 #include <set>
 #include <utility>
+#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcrt/fx_coordinates.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 "third_party/base/logging.h"
 #include "third_party/base/ptr_util.h"
 
 class CPDF_IndirectObjectHolder;
 
-class CPDF_Dictionary : public CPDF_Object {
+class CPDF_Dictionary final : public CPDF_Object {
  public:
   using const_iterator =
-      std::map<ByteString, std::unique_ptr<CPDF_Object>>::const_iterator;
+      std::map<ByteString, RetainPtr<CPDF_Object>>::const_iterator;
 
-  CPDF_Dictionary();
-  explicit CPDF_Dictionary(const WeakPtr<ByteStringPool>& pPool);
-  ~CPDF_Dictionary() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
-  CPDF_Dictionary* GetDict() 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;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
-  size_t GetCount() const { return m_Map.size(); }
-  CPDF_Object* GetObjectFor(const ByteString& key) const;
-  CPDF_Object* GetDirectObjectFor(const ByteString& key) const;
+  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;
   WideString GetUnicodeTextFor(const ByteString& key) const;
   int GetIntegerFor(const ByteString& key) const;
   int GetIntegerFor(const ByteString& key, int default_int) const;
-  bool GetBooleanFor(const ByteString& key, bool bDefault = false) const;
+  bool GetBooleanFor(const ByteString& key, bool bDefault) const;
   float GetNumberFor(const ByteString& key) const;
-  CPDF_Dictionary* GetDictFor(const ByteString& key) const;
-  CPDF_Stream* GetStreamFor(const ByteString& key) const;
-  CPDF_Array* GetArrayFor(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);
   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;
-  bool IsSignatureDict() const;
-
-  // 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, std::unique_ptr<CPDF_Object> pObj);
+  std::vector<ByteString> GetKeys() const;
 
   // Creates a new object owned by the dictionary and returns an unowned
-  // pointer to it.
+  // 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.
   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::MakeUnique<T>(std::forward<Args>(args)...)));
+        SetFor(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::MakeUnique<T>(m_pPool, std::forward<Args>(args)...)));
+        key, pdfium::MakeRetain<T>(m_pPool, std::forward<Args>(args)...)));
   }
 
   // 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|.
-  std::unique_ptr<CPDF_Object> RemoveFor(const ByteString& key);
+  RetainPtr<CPDF_Object> RemoveFor(const ByteString& key);
 
   // Invalidates iterators for the element with the key |oldkey|.
   void ReplaceKey(const ByteString& oldkey, const ByteString& newkey);
 
-  const_iterator begin() const { return m_Map.begin(); }
-  const_iterator end() const { return m_Map.end(); }
-
   WeakPtr<ByteStringPool> GetByteStringPool() const { return m_pPool; }
 
- protected:
+ private:
+  friend class CPDF_DictionaryLocker;
+
+  CPDF_Dictionary();
+  explicit CPDF_Dictionary(const WeakPtr<ByteStringPool>& pPool);
+  ~CPDF_Dictionary() override;
+
   ByteString MaybeIntern(const ByteString& str);
-  std::unique_ptr<CPDF_Object> CloneNonCyclic(
+  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, std::unique_ptr<CPDF_Object>> m_Map;
+  std::map<ByteString, RetainPtr<CPDF_Object>> m_Map;
+};
+
+class CPDF_DictionaryLocker {
+ public:
+  using const_iterator = CPDF_Dictionary::const_iterator;
+
+  explicit CPDF_DictionaryLocker(const CPDF_Dictionary* pDictionary);
+  ~CPDF_DictionaryLocker();
+
+  const_iterator begin() const {
+    CHECK(m_pDictionary->IsLocked());
+    return m_pDictionary->m_Map.begin();
+  }
+  const_iterator end() const {
+    CHECK(m_pDictionary->IsLocked());
+    return m_pDictionary->m_Map.end();
+  }
+
+ private:
+  RetainPtr<const CPDF_Dictionary> const m_pDictionary;
 };
 
 inline CPDF_Dictionary* ToDictionary(CPDF_Object* obj) {
@@ -117,13 +156,8 @@
   return obj ? obj->AsDictionary() : nullptr;
 }
 
-inline std::unique_ptr<CPDF_Dictionary> ToDictionary(
-    std::unique_ptr<CPDF_Object> obj) {
-  CPDF_Dictionary* pDict = ToDictionary(obj.get());
-  if (!pDict)
-    return nullptr;
-  obj.release();
-  return std::unique_ptr<CPDF_Dictionary>(pDict);
+inline RetainPtr<CPDF_Dictionary> ToDictionary(RetainPtr<CPDF_Object> obj) {
+  return RetainPtr<CPDF_Dictionary>(ToDictionary(obj.Get()));
 }
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_DICTIONARY_H_
diff --git a/core/fpdfapi/parser/cpdf_document.cpp b/core/fpdfapi/parser/cpdf_document.cpp
index 565886a..d26d5c4 100644
--- a/core/fpdfapi/parser/cpdf_document.cpp
+++ b/core/fpdfapi/parser/cpdf_document.cpp
@@ -6,255 +6,41 @@
 
 #include "core/fpdfapi/parser/cpdf_document.h"
 
-#include <memory>
 #include <set>
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_fontencoding.h"
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_iccprofile.h"
-#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#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"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fpdfapi/parser/cpdf_read_validator.h"
 #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/render/cpdf_dibsource.h"
-#include "core/fpdfapi/render/cpdf_docrenderdata.h"
-#include "core/fxcodec/JBig2_DocumentContext.h"
+#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
 #include "core/fxcrt/fx_codepage.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"
 
 namespace {
 
-const int FX_MAX_PAGE_LEVEL = 1024;
-
-const uint16_t g_FX_MSDOSThaiUnicodes[128] = {
-    0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, 0x0000,
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x0000, 0x0000,
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00A0, 0x0E01, 0x0E02, 0x0E03,
-    0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C,
-    0x0E0D, 0x0E0E, 0x0E0F, 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15,
-    0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E,
-    0x0E1F, 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
-    0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, 0x0E30,
-    0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39,
-    0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F, 0x0E40, 0x0E41, 0x0E42,
-    0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B,
-    0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54,
-    0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000,
-    0x0000, 0x0000,
-};
-const uint16_t g_FX_MSWinEasternEuropeanUnicodes[128] = {
-    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
-    0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, 0x0000, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0161,
-    0x203A, 0x015B, 0x0165, 0x017E, 0x017A, 0x00A0, 0x02C7, 0x02D8, 0x0141,
-    0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC,
-    0x00AD, 0x00AE, 0x017B, 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5,
-    0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E,
-    0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
-    0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, 0x0110,
-    0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E,
-    0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155, 0x00E1, 0x00E2,
-    0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB,
-    0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4,
-    0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD,
-    0x0163, 0x02D9,
-};
-const uint16_t g_FX_MSWinCyrillicUnicodes[128] = {
-    0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC,
-    0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, 0x0452, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0459,
-    0x203A, 0x045A, 0x045C, 0x045B, 0x045F, 0x00A0, 0x040E, 0x045E, 0x0408,
-    0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC,
-    0x00AD, 0x00AE, 0x0407, 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5,
-    0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455,
-    0x0457, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
-    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420,
-    0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429,
-    0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432,
-    0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B,
-    0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444,
-    0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D,
-    0x044E, 0x044F,
-};
-const uint16_t g_FX_MSWinGreekUnicodes[128] = {
-    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
-    0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000,
-    0x203A, 0x0000, 0x0000, 0x0000, 0x0000, 0x00A0, 0x0385, 0x0386, 0x00A3,
-    0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC,
-    0x00AD, 0x00AE, 0x2015, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5,
-    0x00B6, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E,
-    0x038F, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
-    0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
-    0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,
-    0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, 0x03B1, 0x03B2,
-    0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB,
-    0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4,
-    0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD,
-    0x03CE, 0x0000,
-};
-const uint16_t g_FX_MSWinTurkishUnicodes[128] = {
-    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
-    0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161,
-    0x203A, 0x0153, 0x0000, 0x0000, 0x0178, 0x00A0, 0x00A1, 0x00A2, 0x00A3,
-    0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC,
-    0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
-    0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
-    0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
-    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x011E,
-    0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9,
-    0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, 0x00E0, 0x00E1, 0x00E2,
-    0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB,
-    0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4,
-    0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131,
-    0x015F, 0x00FF,
-};
-const uint16_t g_FX_MSWinHebrewUnicodes[128] = {
-    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
-    0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000,
-    0x203A, 0x0000, 0x0000, 0x0000, 0x0000, 0x00A0, 0x00A1, 0x00A2, 0x00A3,
-    0x20AA, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC,
-    0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
-    0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
-    0x00BF, 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
-    0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, 0x05C0,
-    0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x0000,
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x05D0, 0x05D1, 0x05D2,
-    0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB,
-    0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4,
-    0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E,
-    0x200F, 0x0000,
-};
-const uint16_t g_FX_MSWinArabicUnicodes[128] = {
-    0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
-    0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, 0x06AF, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691,
-    0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, 0x00A0, 0x060C, 0x00A2, 0x00A3,
-    0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC,
-    0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
-    0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
-    0x061F, 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,
-    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630,
-    0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, 0x0637, 0x0638,
-    0x0639, 0x063A, 0x0640, 0x0641, 0x0642, 0x0643, 0x00E0, 0x0644, 0x00E2,
-    0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB,
-    0x0649, 0x064A, 0x00EE, 0x00EF, 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4,
-    0x064F, 0x0650, 0x00F7, 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E,
-    0x200F, 0x06D2,
-};
-const uint16_t g_FX_MSWinBalticUnicodes[128] = {
-    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
-    0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, 0x0000, 0x2018,
-    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000,
-    0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000, 0x00A0, 0x0000, 0x00A2, 0x00A3,
-    0x00A4, 0x0000, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC,
-    0x00AD, 0x00AE, 0x00C6, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
-    0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
-    0x00E6, 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
-    0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, 0x0160,
-    0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141,
-    0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, 0x0105, 0x012F, 0x0101,
-    0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117,
-    0x0123, 0x0137, 0x012B, 0x013C, 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D,
-    0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C,
-    0x017E, 0x02D9,
-};
-
-struct FX_CharsetUnicodes {
-  uint8_t m_Charset;
-  const uint16_t* m_pUnicodes;
-};
-
-const FX_CharsetUnicodes g_FX_CharsetUnicodes[] = {
-    {FX_CHARSET_Thai, g_FX_MSDOSThaiUnicodes},
-    {FX_CHARSET_MSWin_EasternEuropean, g_FX_MSWinEasternEuropeanUnicodes},
-    {FX_CHARSET_MSWin_Cyrillic, g_FX_MSWinCyrillicUnicodes},
-    {FX_CHARSET_MSWin_Greek, g_FX_MSWinGreekUnicodes},
-    {FX_CHARSET_MSWin_Turkish, g_FX_MSWinTurkishUnicodes},
-    {FX_CHARSET_MSWin_Hebrew, g_FX_MSWinHebrewUnicodes},
-    {FX_CHARSET_MSWin_Arabic, g_FX_MSWinArabicUnicodes},
-    {FX_CHARSET_MSWin_Baltic, g_FX_MSWinBalticUnicodes},
-};
-
-void InsertWidthArrayImpl(int* widths, int size, CPDF_Array* pWidthArray) {
-  int i;
-  for (i = 1; i < size; i++) {
-    if (widths[i] != *widths)
-      break;
-  }
-  if (i == size) {
-    int first = pWidthArray->GetIntegerAt(pWidthArray->GetCount() - 1);
-    pWidthArray->AddNew<CPDF_Number>(first + size - 1);
-    pWidthArray->AddNew<CPDF_Number>(*widths);
-  } else {
-    CPDF_Array* pWidthArray1 = pWidthArray->AddNew<CPDF_Array>();
-    for (i = 0; i < size; i++)
-      pWidthArray1->AddNew<CPDF_Number>(widths[i]);
-  }
-  FX_Free(widths);
-}
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-void InsertWidthArray(HDC hDC, int start, int end, CPDF_Array* pWidthArray) {
-  int size = end - start + 1;
-  int* widths = FX_Alloc(int, size);
-  GetCharWidth(hDC, start, end, widths);
-  InsertWidthArrayImpl(widths, size, pWidthArray);
-}
-
-ByteString FPDF_GetPSNameFromTT(HDC hDC) {
-  ByteString result;
-  DWORD size = ::GetFontData(hDC, 'eman', 0, nullptr, 0);
-  if (size != GDI_ERROR) {
-    LPBYTE buffer = FX_Alloc(BYTE, size);
-    ::GetFontData(hDC, 'eman', 0, buffer, size);
-    result = GetNameFromTT(buffer, size, 6);
-    FX_Free(buffer);
-  }
-  return result;
-}
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-void InsertWidthArray1(CFX_Font* pFont,
-                       CFX_UnicodeEncoding* pEncoding,
-                       wchar_t start,
-                       wchar_t end,
-                       CPDF_Array* pWidthArray) {
-  int size = end - start + 1;
-  int* widths = FX_Alloc(int, size);
-  int i;
-  for (i = 0; i < size; i++) {
-    int glyph_index = pEncoding->GlyphFromCharCode(start + i);
-    widths[i] = pFont->GetGlyphWidth(glyph_index);
-  }
-  InsertWidthArrayImpl(widths, size, pWidthArray);
-}
+const int kMaxPageLevel = 1024;
 
 int CountPages(CPDF_Dictionary* pPages,
                std::set<CPDF_Dictionary*>* visited_pages) {
   int count = pPages->GetIntegerFor("Count");
-  if (count > 0 && count < FPDF_PAGE_MAX_NUM)
+  if (count > 0 && count < CPDF_Document::kPageMaxNum)
     return count;
   CPDF_Array* pKidList = pPages->GetArrayFor("Kids");
   if (!pKidList)
     return 0;
   count = 0;
-  for (size_t i = 0; i < pKidList->GetCount(); i++) {
+  for (size_t i = 0; i < pKidList->size(); i++) {
     CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
     if (!pKid || pdfium::ContainsKey(*visited_pages, pKid))
       continue;
@@ -272,135 +58,64 @@
   return count;
 }
 
-int CalculateFlags(bool bold,
-                   bool italic,
-                   bool fixedPitch,
-                   bool serif,
-                   bool script,
-                   bool symbolic) {
-  int flags = 0;
-  if (bold)
-    flags |= FXFONT_BOLD;
-  if (italic)
-    flags |= FXFONT_ITALIC;
-  if (fixedPitch)
-    flags |= FXFONT_FIXED_PITCH;
-  if (serif)
-    flags |= FXFONT_SERIF;
-  if (script)
-    flags |= FXFONT_SCRIPT;
-  if (symbolic)
-    flags |= FXFONT_SYMBOLIC;
-  else
-    flags |= FXFONT_NONSYMBOLIC;
-  return flags;
-}
-
-void ProcessNonbCJK(CPDF_Dictionary* pBaseDict,
-                    bool bold,
-                    bool italic,
-                    ByteString basefont,
-                    std::unique_ptr<CPDF_Array> pWidths) {
-  if (bold && italic)
-    basefont += ",BoldItalic";
-  else if (bold)
-    basefont += ",Bold";
-  else if (italic)
-    basefont += ",Italic";
-  pBaseDict->SetNewFor<CPDF_Name>("Subtype", "TrueType");
-  pBaseDict->SetNewFor<CPDF_Name>("BaseFont", basefont);
-  pBaseDict->SetNewFor<CPDF_Number>("FirstChar", 32);
-  pBaseDict->SetNewFor<CPDF_Number>("LastChar", 255);
-  pBaseDict->SetFor("Widths", std::move(pWidths));
-}
-
-std::unique_ptr<CPDF_Dictionary> CalculateFontDesc(
-    CPDF_Document* pDoc,
-    ByteString basefont,
-    int flags,
-    int italicangle,
-    int ascend,
-    int descend,
-    std::unique_ptr<CPDF_Array> bbox,
-    int32_t stemV) {
-  auto pFontDesc =
-      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
-  pFontDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
-  pFontDesc->SetNewFor<CPDF_Name>("FontName", basefont);
-  pFontDesc->SetNewFor<CPDF_Number>("Flags", flags);
-  pFontDesc->SetFor("FontBBox", std::move(bbox));
-  pFontDesc->SetNewFor<CPDF_Number>("ItalicAngle", italicangle);
-  pFontDesc->SetNewFor<CPDF_Number>("Ascent", ascend);
-  pFontDesc->SetNewFor<CPDF_Number>("Descent", descend);
-  pFontDesc->SetNewFor<CPDF_Number>("StemV", stemV);
-  return pFontDesc;
-}
-
 }  // namespace
 
-CPDF_Document::CPDF_Document(std::unique_ptr<CPDF_Parser> pParser)
-    : CPDF_IndirectObjectHolder(),
-      m_pParser(std::move(pParser)),
-      m_pRootDict(nullptr),
-      m_iNextPageToTraverse(0),
-      m_bReachedMaxPageLevel(false),
-      m_bLinearized(false),
-      m_iFirstPageNo(0),
-      m_dwFirstPageObjNum(0),
-      m_pDocPage(pdfium::MakeUnique<CPDF_DocPageData>(this)),
-      m_pDocRender(pdfium::MakeUnique<CPDF_DocRenderData>(this)) {
-  if (pParser)
-    SetLastObjNum(m_pParser->GetLastObjNum());
+CPDF_Document::CPDF_Document(std::unique_ptr<RenderDataIface> pRenderData,
+                             std::unique_ptr<PageDataIface> pPageData)
+    : m_pDocRender(std::move(pRenderData)),
+      m_pDocPage(std::move(pPageData)),
+      m_StockFontClearer(m_pDocPage.get()) {
+  m_pDocRender->SetDocument(this);
+  m_pDocPage->SetDocument(this);
 }
 
-CPDF_Document::~CPDF_Document() {
-  CPDF_ModuleMgr::Get()->GetPageModule()->ClearStockFont(this);
+CPDF_Document::~CPDF_Document() = default;
+
+RetainPtr<CPDF_Object> CPDF_Document::ParseIndirectObject(uint32_t objnum) {
+  return m_pParser ? m_pParser->ParseIndirectObject(objnum) : nullptr;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Document::ParseIndirectObject(
-    uint32_t objnum) {
-  return m_pParser ? m_pParser->ParseIndirectObject(this, objnum) : nullptr;
-}
-
-void CPDF_Document::LoadDocInternal() {
+bool CPDF_Document::TryInit() {
   SetLastObjNum(m_pParser->GetLastObjNum());
 
   CPDF_Object* pRootObj = GetOrParseIndirectObject(m_pParser->GetRootObjNum());
-  if (!pRootObj)
-    return;
+  if (pRootObj)
+    m_pRootDict.Reset(pRootObj->GetDict());
 
-  m_pRootDict = pRootObj->GetDict();
-  if (!m_pRootDict)
-    return;
-
-  LoadDocumentInfo();
-}
-
-void CPDF_Document::LoadDocumentInfo() {
-  if (!m_pParser)
-    return;
-
-  CPDF_Object* pInfoObj = GetOrParseIndirectObject(m_pParser->GetInfoObjNum());
-  if (pInfoObj)
-    m_pInfoDict = pInfoObj->GetDict();
-}
-
-void CPDF_Document::LoadDoc() {
-  LoadDocInternal();
   LoadPages();
+  return GetRoot() && GetPageCount() > 0;
 }
 
-void CPDF_Document::LoadLinearizedDoc(
-    const CPDF_LinearizedHeader* pLinearizationParams) {
-  m_bLinearized = true;
-  LoadDocInternal();
-  m_PageList.resize(pLinearizationParams->GetPageCount());
-  m_iFirstPageNo = pLinearizationParams->GetFirstPageNo();
-  m_dwFirstPageObjNum = pLinearizationParams->GetFirstPageObjNum();
+CPDF_Parser::Error CPDF_Document::LoadDoc(
+    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
+    const char* password) {
+  if (!m_pParser)
+    SetParser(pdfium::MakeUnique<CPDF_Parser>(this));
+
+  return HandleLoadResult(m_pParser->StartParse(pFileAccess, password));
+}
+
+CPDF_Parser::Error CPDF_Document::LoadLinearizedDoc(
+    const RetainPtr<CPDF_ReadValidator>& validator,
+    const char* password) {
+  if (!m_pParser)
+    SetParser(pdfium::MakeUnique<CPDF_Parser>(this));
+
+  return HandleLoadResult(m_pParser->StartLinearizedParse(validator, password));
 }
 
 void CPDF_Document::LoadPages() {
-  m_PageList.resize(RetrievePageCount());
+  const CPDF_LinearizedHeader* linearized_header =
+      m_pParser->GetLinearizedHeader();
+  if (!linearized_header) {
+    m_PageList.resize(RetrievePageCount());
+    return;
+  }
+
+  m_PageList.resize(linearized_header->GetPageCount());
+  ASSERT(linearized_header->GetFirstPageNo() < m_PageList.size());
+  m_PageList[linearized_header->GetFirstPageNo()] =
+      linearized_header->GetFirstPageObjNum();
 }
 
 CPDF_Dictionary* CPDF_Document::TraversePDFPages(int iPage,
@@ -418,14 +133,13 @@
     m_PageList[iPage] = pPages->GetObjNum();
     return pPages;
   }
-  if (level >= FX_MAX_PAGE_LEVEL) {
+  if (level >= kMaxPageLevel) {
     m_pTreeTraversal.pop_back();
     m_bReachedMaxPageLevel = true;
     return nullptr;
   }
   CPDF_Dictionary* page = nullptr;
-  for (size_t i = m_pTreeTraversal[level].second; i < pKidList->GetCount();
-       i++) {
+  for (size_t i = m_pTreeTraversal[level].second; i < pKidList->size(); i++) {
     if (*nPagesToGo == 0)
       break;
     pKidList->ConvertToIndirectObjectAt(i, this);
@@ -464,7 +178,7 @@
       }
     }
   }
-  if (m_pTreeTraversal[level].second == pKidList->GetCount())
+  if (m_pTreeTraversal[level].second == pKidList->size())
     m_pTreeTraversal.pop_back();
   return page;
 }
@@ -475,28 +189,41 @@
   m_pTreeTraversal.clear();
 }
 
-CPDF_Dictionary* CPDF_Document::GetPagesDict() const {
+void CPDF_Document::SetParser(std::unique_ptr<CPDF_Parser> pParser) {
+  ASSERT(!m_pParser);
+  m_pParser = std::move(pParser);
+}
+
+CPDF_Parser::Error CPDF_Document::HandleLoadResult(CPDF_Parser::Error error) {
+  if (error == CPDF_Parser::SUCCESS)
+    m_bHasValidCrossReferenceTable = !m_pParser->xref_table_rebuilt();
+  return error;
+}
+
+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());
+}
+
 bool CPDF_Document::IsPageLoaded(int iPage) const {
   return !!m_PageList[iPage];
 }
 
-CPDF_Dictionary* CPDF_Document::GetPage(int iPage) {
+CPDF_Dictionary* CPDF_Document::GetPageDictionary(int iPage) {
   if (!pdfium::IndexInBounds(m_PageList, iPage))
     return nullptr;
 
-  if (m_bLinearized && iPage == m_iFirstPageNo) {
-    if (CPDF_Dictionary* pDict =
-            ToDictionary(GetOrParseIndirectObject(m_dwFirstPageObjNum))) {
-      return pDict;
-    }
+  const uint32_t objnum = m_PageList[iPage];
+  if (objnum) {
+    CPDF_Dictionary* result = ToDictionary(GetOrParseIndirectObject(objnum));
+    if (result)
+      return result;
   }
-  uint32_t objnum = m_PageList[iPage];
-  if (objnum)
-    return ToDictionary(GetOrParseIndirectObject(objnum));
 
   CPDF_Dictionary* pPages = GetPagesDict();
   if (!pPages)
@@ -516,7 +243,7 @@
   m_PageList[iPage] = objNum;
 }
 
-int CPDF_Document::FindPageIndex(CPDF_Dictionary* pNode,
+int CPDF_Document::FindPageIndex(const CPDF_Dictionary* pNode,
                                  uint32_t* skip_count,
                                  uint32_t objnum,
                                  int* index,
@@ -532,11 +259,11 @@
     return -1;
   }
 
-  CPDF_Array* pKidList = pNode->GetArrayFor("Kids");
+  const CPDF_Array* pKidList = pNode->GetArrayFor("Kids");
   if (!pKidList)
     return -1;
 
-  if (level >= FX_MAX_PAGE_LEVEL)
+  if (level >= kMaxPageLevel)
     return -1;
 
   size_t count = pNode->GetIntegerFor("Count");
@@ -546,16 +273,16 @@
     return -1;
   }
 
-  if (count && count == pKidList->GetCount()) {
+  if (count && count == pKidList->size()) {
     for (size_t i = 0; i < count; i++) {
-      CPDF_Reference* pKid = ToReference(pKidList->GetObjectAt(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->GetCount(); i++) {
-    CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
+  for (size_t i = 0; i < pKidList->size(); i++) {
+    const CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
     if (!pKid || pKid == pNode)
       continue;
 
@@ -579,12 +306,12 @@
       bSkipped = true;
     }
   }
-  CPDF_Dictionary* pPages = GetPagesDict();
+  const CPDF_Dictionary* pPages = GetPagesDict();
   if (!pPages)
     return -1;
 
   int start_index = 0;
-  int found_index = FindPageIndex(pPages, &skip_count, objnum, &start_index);
+  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))
@@ -598,7 +325,7 @@
   return pdfium::CollectionSize<int>(m_PageList);
 }
 
-int CPDF_Document::RetrievePageCount() const {
+int CPDF_Document::RetrievePageCount() {
   CPDF_Dictionary* pPages = GetPagesDict();
   if (!pPages)
     return 0;
@@ -612,51 +339,16 @@
 }
 
 uint32_t CPDF_Document::GetUserPermissions() const {
-  // https://bugs.chromium.org/p/pdfium/issues/detail?id=499
-  if (!m_pParser) {
-#ifndef PDF_ENABLE_XFA
-    return 0;
-#else  // PDF_ENABLE_XFA
-    return 0xFFFFFFFF;
-#endif
-  }
-  return m_pParser->GetPermissions();
-}
+  if (m_pParser)
+    return m_pParser->GetPermissions();
 
-CPDF_Font* CPDF_Document::LoadFont(CPDF_Dictionary* pFontDict) {
-  ASSERT(pFontDict);
-  return m_pDocPage->GetFont(pFontDict);
-}
-
-RetainPtr<CPDF_StreamAcc> CPDF_Document::LoadFontFile(CPDF_Stream* pStream) {
-  return m_pDocPage->GetFontFileStreamAcc(pStream);
-}
-
-CPDF_ColorSpace* CPDF_Document::LoadColorSpace(CPDF_Object* pCSObj,
-                                               CPDF_Dictionary* pResources) {
-  return m_pDocPage->GetColorSpace(pCSObj, pResources);
-}
-
-CPDF_Pattern* CPDF_Document::LoadPattern(CPDF_Object* pPatternObj,
-                                         bool bShading,
-                                         const CFX_Matrix& matrix) {
-  return m_pDocPage->GetPattern(pPatternObj, bShading, matrix);
-}
-
-RetainPtr<CPDF_IccProfile> CPDF_Document::LoadIccProfile(CPDF_Stream* pStream) {
-  return m_pDocPage->GetIccProfile(pStream);
-}
-
-RetainPtr<CPDF_Image> CPDF_Document::LoadImageFromPageData(
-    uint32_t dwStreamObjNum) {
-  ASSERT(dwStreamObjNum);
-  return m_pDocPage->GetImage(dwStreamObjNum);
+  return m_pExtension ? m_pExtension->GetUserPermissions() : 0;
 }
 
 void CPDF_Document::CreateNewDoc() {
   ASSERT(!m_pRootDict);
   ASSERT(!m_pInfoDict);
-  m_pRootDict = NewIndirect<CPDF_Dictionary>();
+  m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
   m_pRootDict->SetNewFor<CPDF_Name>("Type", "Catalog");
 
   CPDF_Dictionary* pPages = NewIndirect<CPDF_Dictionary>();
@@ -664,7 +356,7 @@
   pPages->SetNewFor<CPDF_Number>("Count", 0);
   pPages->SetNewFor<CPDF_Array>("Kids");
   m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this, pPages->GetObjNum());
-  m_pInfoDict = NewIndirect<CPDF_Dictionary>();
+  m_pInfoDict.Reset(NewIndirect<CPDF_Dictionary>());
 }
 
 CPDF_Dictionary* CPDF_Document::CreateNewPage(int iPage) {
@@ -687,7 +379,7 @@
   if (!pKidList)
     return false;
 
-  for (size_t i = 0; i < pKidList->GetCount(); i++) {
+  for (size_t i = 0; i < pKidList->size(); i++) {
     CPDF_Dictionary* pKid = pKidList->GetDictAt(i);
     if (pKid->GetStringFor("Type") == "Page") {
       if (nPagesToGo != 0) {
@@ -726,7 +418,7 @@
 }
 
 bool CPDF_Document::InsertNewPage(int iPage, CPDF_Dictionary* pPageDict) {
-  const CPDF_Dictionary* pRoot = GetRoot();
+  CPDF_Dictionary* pRoot = GetRoot();
   CPDF_Dictionary* pPages = pRoot ? pRoot->GetDictFor("Pages") : nullptr;
   if (!pPages)
     return false;
@@ -752,6 +444,19 @@
   return true;
 }
 
+CPDF_Dictionary* CPDF_Document::GetInfo() {
+  if (m_pInfoDict)
+    return m_pInfoDict.Get();
+
+  if (!m_pParser || !m_pParser->GetInfoObjNum())
+    return nullptr;
+
+  auto ref =
+      pdfium::MakeRetain<CPDF_Reference>(this, m_pParser->GetInfoObjNum());
+  m_pInfoDict.Reset(ToDictionary(ref->GetDirect()));
+  return m_pInfoDict.Get();
+}
+
 void CPDF_Document::DeletePage(int iPage) {
   CPDF_Dictionary* pPages = GetPagesDict();
   if (!pPages)
@@ -768,288 +473,18 @@
   m_PageList.erase(m_PageList.begin() + iPage);
 }
 
-CPDF_Font* CPDF_Document::AddStandardFont(const char* font,
-                                          CPDF_FontEncoding* pEncoding) {
-  ByteString name(font);
-  if (PDF_GetStandardFontName(&name) < 0)
-    return nullptr;
-  return GetPageData()->GetStandardFont(name, pEncoding);
+CPDF_Document::StockFontClearer::StockFontClearer(
+    CPDF_Document::PageDataIface* pPageData)
+    : m_pPageData(pPageData) {}
+
+CPDF_Document::StockFontClearer::~StockFontClearer() {
+  m_pPageData->ClearStockFont();
 }
 
-size_t CPDF_Document::CalculateEncodingDict(int 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)
-      break;
-  }
-  if (i == FX_ArraySize(g_FX_CharsetUnicodes))
-    return i;
+CPDF_Document::PageDataIface::PageDataIface() = default;
 
-  CPDF_Dictionary* pEncodingDict = NewIndirect<CPDF_Dictionary>();
-  pEncodingDict->SetNewFor<CPDF_Name>("BaseEncoding", "WinAnsiEncoding");
+CPDF_Document::PageDataIface::~PageDataIface() = default;
 
-  CPDF_Array* pArray = pEncodingDict->SetNewFor<CPDF_Array>("Differences");
-  pArray->AddNew<CPDF_Number>(128);
+CPDF_Document::RenderDataIface::RenderDataIface() = default;
 
-  const uint16_t* pUnicodes = g_FX_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);
-  }
-  pBaseDict->SetNewFor<CPDF_Reference>("Encoding", this,
-                                       pEncodingDict->GetObjNum());
-  return i;
-}
-
-CPDF_Dictionary* CPDF_Document::ProcessbCJK(
-    CPDF_Dictionary* pBaseDict,
-    int charset,
-    bool bVert,
-    ByteString basefont,
-    std::function<void(wchar_t, wchar_t, CPDF_Array*)> Insert) {
-  CPDF_Dictionary* pFontDict = NewIndirect<CPDF_Dictionary>();
-  ByteString cmap;
-  ByteString ordering;
-  int supplement = 0;
-  CPDF_Array* pWidthArray = pFontDict->SetNewFor<CPDF_Array>("W");
-  switch (charset) {
-    case FX_CHARSET_ChineseTraditional:
-      cmap = bVert ? "ETenms-B5-V" : "ETenms-B5-H";
-      ordering = "CNS1";
-      supplement = 4;
-      pWidthArray->AddNew<CPDF_Number>(1);
-      Insert(0x20, 0x7e, pWidthArray);
-      break;
-    case FX_CHARSET_ChineseSimplified:
-      cmap = bVert ? "GBK-EUC-V" : "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);
-      break;
-    case FX_CHARSET_Hangul:
-      cmap = bVert ? "KSCms-UHC-V" : "KSCms-UHC-H";
-      ordering = "Korea1";
-      supplement = 2;
-      pWidthArray->AddNew<CPDF_Number>(1);
-      Insert(0x20, 0x7e, pWidthArray);
-      break;
-    case FX_CHARSET_ShiftJIS:
-      cmap = bVert ? "90ms-RKSJ-V" : "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);
-      break;
-  }
-  pBaseDict->SetNewFor<CPDF_Name>("Subtype", "Type0");
-  pBaseDict->SetNewFor<CPDF_Name>("BaseFont", basefont);
-  pBaseDict->SetNewFor<CPDF_Name>("Encoding", cmap);
-  pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
-  pFontDict->SetNewFor<CPDF_Name>("Subtype", "CIDFontType2");
-  pFontDict->SetNewFor<CPDF_Name>("BaseFont", basefont);
-
-  CPDF_Dictionary* 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>(this, pFontDict->GetObjNum());
-  return pFontDict;
-}
-
-CPDF_Font* CPDF_Document::AddFont(CFX_Font* pFont, int charset, bool bVert) {
-  if (!pFont)
-    return nullptr;
-
-  bool bCJK = charset == FX_CHARSET_ChineseTraditional ||
-              charset == FX_CHARSET_ChineseSimplified ||
-              charset == FX_CHARSET_Hangul || charset == FX_CHARSET_ShiftJIS;
-  ByteString basefont = pFont->GetFamilyName();
-  basefont.Replace(" ", "");
-  int flags =
-      CalculateFlags(pFont->IsBold(), pFont->IsItalic(), pFont->IsFixedWidth(),
-                     false, false, charset == FX_CHARSET_Symbol);
-
-  CPDF_Dictionary* pBaseDict = NewIndirect<CPDF_Dictionary>();
-  pBaseDict->SetNewFor<CPDF_Name>("Type", "Font");
-  auto pEncoding = pdfium::MakeUnique<CFX_UnicodeEncoding>(pFont);
-  CPDF_Dictionary* pFontDict = pBaseDict;
-  if (!bCJK) {
-    auto pWidths = pdfium::MakeUnique<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);
-    }
-    if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Default ||
-        charset == FX_CHARSET_Symbol) {
-      pBaseDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
-      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);
-      }
-    } else {
-      size_t i = CalculateEncodingDict(charset, pBaseDict);
-      if (i < FX_ArraySize(g_FX_CharsetUnicodes)) {
-        const uint16_t* pUnicodes = g_FX_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);
-        }
-      }
-    }
-    ProcessNonbCJK(pBaseDict, pFont->IsBold(), pFont->IsItalic(), basefont,
-                   std::move(pWidths));
-  } else {
-    pFontDict = ProcessbCJK(
-        pBaseDict, charset, bVert, basefont,
-        [pFont, &pEncoding](wchar_t start, wchar_t end, CPDF_Array* widthArr) {
-          InsertWidthArray1(pFont, pEncoding.get(), start, end, widthArr);
-        });
-  }
-  int italicangle =
-      pFont->GetSubstFont() ? pFont->GetSubstFont()->m_ItalicAngle : 0;
-  FX_RECT bbox;
-  pFont->GetBBox(bbox);
-  auto pBBox = pdfium::MakeUnique<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);
-  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);
-    uint32_t glyph = pEncoding->GlyphFromCharCode(stem_chars[0]);
-    nStemV = pFont->GetGlyphWidth(glyph);
-    for (size_t i = 1; i < count; i++) {
-      glyph = pEncoding->GlyphFromCharCode(stem_chars[i]);
-      int width = pFont->GetGlyphWidth(glyph);
-      if (width > 0 && width < nStemV)
-        nStemV = width;
-    }
-  }
-  CPDF_Dictionary* pFontDesc = ToDictionary(AddIndirectObject(
-      CalculateFontDesc(this, basefont, flags, italicangle, pFont->GetAscent(),
-                        pFont->GetDescent(), std::move(pBBox), nStemV)));
-  pFontDict->SetNewFor<CPDF_Reference>("FontDescriptor", this,
-                                       pFontDesc->GetObjNum());
-  return LoadFont(pBaseDict);
-}
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-CPDF_Font* CPDF_Document::AddWindowsFont(LOGFONTW* pLogFont,
-                                         bool bVert,
-                                         bool bTranslateName) {
-  LOGFONTA lfa;
-  memcpy(&lfa, pLogFont, (char*)lfa.lfFaceName - (char*)&lfa);
-  ByteString face = ByteString::FromUnicode(pLogFont->lfFaceName);
-  if (face.GetLength() >= LF_FACESIZE)
-    return nullptr;
-
-  strncpy(lfa.lfFaceName, face.c_str(),
-          (face.GetLength() + 1) * sizeof(ByteString::CharType));
-  return AddWindowsFont(&lfa, bVert, bTranslateName);
-}
-
-CPDF_Font* CPDF_Document::AddWindowsFont(LOGFONTA* pLogFont,
-                                         bool bVert,
-                                         bool bTranslateName) {
-  pLogFont->lfHeight = -1000;
-  pLogFont->lfWidth = 0;
-  HGDIOBJ hFont = CreateFontIndirectA(pLogFont);
-  HDC hDC = CreateCompatibleDC(nullptr);
-  hFont = SelectObject(hDC, hFont);
-  int tm_size = GetOutlineTextMetrics(hDC, 0, nullptr);
-  if (tm_size == 0) {
-    hFont = SelectObject(hDC, hFont);
-    DeleteObject(hFont);
-    DeleteDC(hDC);
-    return nullptr;
-  }
-
-  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);
-
-  bool bCJK = pLogFont->lfCharSet == FX_CHARSET_ChineseTraditional ||
-              pLogFont->lfCharSet == FX_CHARSET_ChineseSimplified ||
-              pLogFont->lfCharSet == FX_CHARSET_Hangul ||
-              pLogFont->lfCharSet == FX_CHARSET_ShiftJIS;
-  ByteString basefont;
-  if (bTranslateName && bCJK)
-    basefont = FPDF_GetPSNameFromTT(hDC);
-
-  if (basefont.IsEmpty())
-    basefont = pLogFont->lfFaceName;
-
-  int italicangle = ptm->otmItalicAngle / 10;
-  int ascend = ptm->otmrcFontBox.top;
-  int descend = ptm->otmrcFontBox.bottom;
-  int capheight = ptm->otmsCapEmHeight;
-  int bbox[4] = {ptm->otmrcFontBox.left, ptm->otmrcFontBox.bottom,
-                 ptm->otmrcFontBox.right, ptm->otmrcFontBox.top};
-  FX_Free(tm_buf);
-  basefont.Replace(" ", "");
-  CPDF_Dictionary* pBaseDict = NewIndirect<CPDF_Dictionary>();
-  pBaseDict->SetNewFor<CPDF_Name>("Type", "Font");
-  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");
-    } else {
-      CalculateEncodingDict(pLogFont->lfCharSet, pBaseDict);
-    }
-    int char_widths[224];
-    GetCharWidth(hDC, 32, 255, char_widths);
-    auto pWidths = pdfium::MakeUnique<CPDF_Array>();
-    for (size_t i = 0; i < 224; i++)
-      pWidths->AddNew<CPDF_Number>(char_widths[i]);
-    ProcessNonbCJK(pBaseDict, pLogFont->lfWeight > FW_MEDIUM,
-                   pLogFont->lfItalic != 0, basefont, std::move(pWidths));
-  } else {
-    pFontDict =
-        ProcessbCJK(pBaseDict, pLogFont->lfCharSet, bVert, basefont,
-                    [&hDC](wchar_t start, wchar_t end, CPDF_Array* widthArr) {
-                      InsertWidthArray(hDC, start, end, widthArr);
-                    });
-  }
-  auto pBBox = pdfium::MakeUnique<CPDF_Array>();
-  for (int i = 0; i < 4; i++)
-    pBBox->AddNew<CPDF_Number>(bbox[i]);
-  std::unique_ptr<CPDF_Dictionary> pFontDesc =
-      CalculateFontDesc(this, basefont, flags, italicangle, ascend, descend,
-                        std::move(pBBox), pLogFont->lfWeight / 5);
-  pFontDesc->SetNewFor<CPDF_Number>("CapHeight", capheight);
-  pFontDict->SetNewFor<CPDF_Reference>(
-      "FontDescriptor", this,
-      AddIndirectObject(std::move(pFontDesc))->GetObjNum());
-  hFont = SelectObject(hDC, hFont);
-  DeleteObject(hFont);
-  DeleteDC(hDC);
-  return LoadFont(pBaseDict);
-}
-#endif  //  _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+CPDF_Document::RenderDataIface::~RenderDataIface() = default;
diff --git a/core/fpdfapi/parser/cpdf_document.h b/core/fpdfapi/parser/cpdf_document.h
index d73dbc1..d0d1729 100644
--- a/core/fpdfapi/parser/cpdf_document.h
+++ b/core/fpdfapi/parser/cpdf_document.h
@@ -13,23 +13,19 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_image.h"
-#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
+#include "build/build_config.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
-#include "core/fpdfdoc/cpdf_linklist.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 
-class CFX_Font;
 class CFX_Matrix;
-class CPDF_ColorSpace;
-class CPDF_DocPageData;
-class CPDF_DocRenderData;
-class CPDF_Font;
-class CPDF_FontEncoding;
-class CPDF_IccProfile;
 class CPDF_LinearizedHeader;
-class CPDF_Parser;
-class CPDF_Pattern;
+class CPDF_Object;
+class CPDF_ReadValidator;
 class CPDF_StreamAcc;
+class IFX_SeekableReadStream;
 class JBig2_DocumentContext;
 
 #define FPDFPERM_MODIFY 0x0008
@@ -37,90 +33,136 @@
 #define FPDFPERM_FILL_FORM 0x0100
 #define FPDFPERM_EXTRACT_ACCESS 0x0200
 
-#define FPDF_PAGE_MAX_NUM 0xFFFFF
-
-class CPDF_Document : public CPDF_IndirectObjectHolder {
+class CPDF_Document : public Observable,
+                      public CPDF_Parser::ParsedObjectsHolder {
  public:
-  explicit CPDF_Document(std::unique_ptr<CPDF_Parser> pParser);
+  // Type from which the XFA extension can subclass itself.
+  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;
+    virtual bool ContainsExtensionForm() const = 0;
+    virtual bool ContainsExtensionFullForm() const = 0;
+    virtual bool ContainsExtensionForegroundForm() const = 0;
+  };
+
+  class LinkListIface {
+   public:
+    // CPDF_Document merely helps manage the lifetime.
+    virtual ~LinkListIface() = default;
+  };
+
+  class PageDataIface {
+   public:
+    PageDataIface();
+    virtual ~PageDataIface();
+
+    virtual void ClearStockFont() = 0;
+    virtual RetainPtr<CPDF_StreamAcc> GetFontFileStreamAcc(
+        const CPDF_Stream* pFontStream) = 0;
+    virtual void MaybePurgeFontFileStreamAcc(
+        const CPDF_Stream* pFontStream) = 0;
+
+    void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; }
+    CPDF_Document* GetDocument() const { return m_pDoc.Get(); }
+
+   private:
+    UnownedPtr<CPDF_Document> m_pDoc;
+  };
+
+  class RenderDataIface {
+   public:
+    RenderDataIface();
+    virtual ~RenderDataIface();
+
+    void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; }
+    CPDF_Document* GetDocument() const { return m_pDoc.Get(); }
+
+   private:
+    UnownedPtr<CPDF_Document> m_pDoc;
+  };
+
+  static const int kPageMaxNum = 0xFFFFF;
+
+  CPDF_Document(std::unique_ptr<RenderDataIface> pRenderData,
+                std::unique_ptr<PageDataIface> pPageData);
   ~CPDF_Document() override;
 
+  Extension* GetExtension() const { return m_pExtension.get(); }
+  void SetExtension(std::unique_ptr<Extension> pExt) {
+    m_pExtension = std::move(pExt);
+  }
+
   CPDF_Parser* GetParser() const { return m_pParser.get(); }
-  const CPDF_Dictionary* GetRoot() const { return m_pRootDict; }
-  CPDF_Dictionary* GetRoot() { return m_pRootDict; }
-  const CPDF_Dictionary* GetInfo() const { return m_pInfoDict.Get(); }
-  CPDF_Dictionary* GetInfo() { return m_pInfoDict.Get(); }
+  CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); }
+  CPDF_Dictionary* GetInfo();
 
   void DeletePage(int iPage);
   int GetPageCount() const;
   bool IsPageLoaded(int iPage) const;
-  CPDF_Dictionary* GetPage(int iPage);
+  CPDF_Dictionary* GetPageDictionary(int iPage);
   int GetPageIndex(uint32_t objnum);
   uint32_t GetUserPermissions() const;
 
   // Returns a valid pointer, unless it is called during destruction.
-  CPDF_DocPageData* GetPageData() const { return m_pDocPage.get(); }
+  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;
   }
-  std::unique_ptr<CPDF_LinkList>* LinksContext() { return &m_pLinksContext; }
+  LinkListIface* GetLinksContext() const { return m_pLinksContext.get(); }
+  void SetLinksContext(std::unique_ptr<LinkListIface> pContext) {
+    m_pLinksContext = std::move(pContext);
+  }
 
-  CPDF_DocRenderData* GetRenderData() const { return m_pDocRender.get(); }
+  //  CPDF_Parser::ParsedObjectsHolder overrides:
+  bool TryInit() override;
 
-  // |pFontDict| must not be null.
-  CPDF_Font* LoadFont(CPDF_Dictionary* pFontDict);
-  CPDF_ColorSpace* LoadColorSpace(CPDF_Object* pCSObj,
-                                  CPDF_Dictionary* pResources = nullptr);
+  CPDF_Parser::Error LoadDoc(
+      const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
+      const char* password);
+  CPDF_Parser::Error LoadLinearizedDoc(
+      const RetainPtr<CPDF_ReadValidator>& validator,
+      const char* password);
+  bool has_valid_cross_reference_table() const {
+    return m_bHasValidCrossReferenceTable;
+  }
 
-  CPDF_Pattern* LoadPattern(CPDF_Object* pObj,
-                            bool bShading,
-                            const CFX_Matrix& matrix);
-
-  RetainPtr<CPDF_Image> LoadImageFromPageData(uint32_t dwStreamObjNum);
-  RetainPtr<CPDF_StreamAcc> LoadFontFile(CPDF_Stream* pStream);
-  RetainPtr<CPDF_IccProfile> LoadIccProfile(CPDF_Stream* pStream);
-
-  void LoadDoc();
-  void LoadLinearizedDoc(const CPDF_LinearizedHeader* pLinearizationParams);
   void LoadPages();
-  void LoadDocumentInfo();
-
   void CreateNewDoc();
   CPDF_Dictionary* CreateNewPage(int iPage);
 
-  CPDF_Font* AddStandardFont(const char* font, CPDF_FontEncoding* pEncoding);
-  CPDF_Font* AddFont(CFX_Font* pFont, int charset, bool bVert);
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  CPDF_Font* AddWindowsFont(LOGFONTA* pLogFont,
-                            bool bVert,
-                            bool bTranslateName = false);
-  CPDF_Font* AddWindowsFont(LOGFONTW* pLogFont,
-                            bool bVert,
-                            bool bTranslateName = false);
-#endif
+  void IncrementParsedPageCount() { ++m_ParsedPageCount; }
+  uint32_t GetParsedPageCountForTesting() { return m_ParsedPageCount; }
 
  protected:
+  class StockFontClearer {
+   public:
+    explicit StockFontClearer(CPDF_Document::PageDataIface* pPageData);
+    ~StockFontClearer();
+
+   private:
+    UnownedPtr<CPDF_Document::PageDataIface> const m_pPageData;
+  };
+
   // Retrieve page count information by getting count value from the tree nodes
-  int RetrievePageCount() const;
+  int RetrievePageCount();
   // When this method is called, m_pTreeTraversal[level] exists.
   CPDF_Dictionary* TraversePDFPages(int iPage, int* nPagesToGo, size_t level);
-  int FindPageIndex(CPDF_Dictionary* pNode,
+  int FindPageIndex(const CPDF_Dictionary* pNode,
                     uint32_t* skip_count,
                     uint32_t objnum,
                     int* index,
-                    int level = 0) const;
-  std::unique_ptr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override;
-  void LoadDocInternal();
-  size_t CalculateEncodingDict(int charset, CPDF_Dictionary* pBaseDict);
-  CPDF_Dictionary* GetPagesDict() const;
-  CPDF_Dictionary* ProcessbCJK(
-      CPDF_Dictionary* pBaseDict,
-      int charset,
-      bool bVert,
-      ByteString basefont,
-      std::function<void(wchar_t, wchar_t, CPDF_Array*)> Insert);
+                    int level) const;
+  RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum) override;
+  const CPDF_Dictionary* GetPagesDict() const;
+  CPDF_Dictionary* GetPagesDict();
   bool InsertDeletePDFPage(CPDF_Dictionary* pPages,
                            int nPagesToGo,
                            CPDF_Dictionary* pPageDict,
@@ -128,13 +170,12 @@
                            std::set<CPDF_Dictionary*>* pVisited);
   bool InsertNewPage(int iPage, 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;
-
-  // TODO(tsepez): figure out why tests break if this is an UnownedPtr.
-  CPDF_Dictionary* m_pRootDict;  // Not owned.
-
-  UnownedPtr<CPDF_Dictionary> m_pInfoDict;
+  RetainPtr<CPDF_Dictionary> m_pRootDict;
+  RetainPtr<CPDF_Dictionary> m_pInfoDict;
 
   // Vector of pairs to know current position in the page tree. The index in the
   // vector corresponds to the level being described. The pair contains a
@@ -142,17 +183,26 @@
   // of the child being processed within the dictionary's /Kids array.
   std::vector<std::pair<CPDF_Dictionary*, size_t>> m_pTreeTraversal;
 
+  // True if the CPDF_Parser succeeded without having to rebuild the cross
+  // reference table.
+  bool m_bHasValidCrossReferenceTable = false;
+
   // Index of the next page that will be traversed from the page tree.
-  int m_iNextPageToTraverse;
-  bool m_bReachedMaxPageLevel;
-  bool m_bLinearized;
-  int m_iFirstPageNo;
-  uint32_t m_dwFirstPageObjNum;
-  std::unique_ptr<CPDF_DocPageData> m_pDocPage;
-  std::unique_ptr<CPDF_DocRenderData> m_pDocRender;
+  bool m_bReachedMaxPageLevel = false;
+  int m_iNextPageToTraverse = 0;
+  uint32_t m_ParsedPageCount = 0;
+
+  std::unique_ptr<RenderDataIface> m_pDocRender;
+  std::unique_ptr<PageDataIface> m_pDocPage;  // Must be after |m_pDocRender|.
   std::unique_ptr<JBig2_DocumentContext> m_pCodecContext;
-  std::unique_ptr<CPDF_LinkList> m_pLinksContext;
-  std::vector<uint32_t> m_PageList;
+  std::unique_ptr<LinkListIface> m_pLinksContext;
+  std::vector<uint32_t> m_PageList;  // Page number to page's dict objnum.
+
+  // Must be second to last.
+  StockFontClearer m_StockFontClearer;
+
+  // Must be last. Destroy the extension before any non-extension teardown.
+  std::unique_ptr<Extension> m_pExtension;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_
diff --git a/core/fpdfapi/parser/cpdf_document_unittest.cpp b/core/fpdfapi/parser/cpdf_document_unittest.cpp
index d1b8dce..1c52e51 100644
--- a/core/fpdfapi/parser/cpdf_document_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_document_unittest.cpp
@@ -7,7 +7,8 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -17,7 +18,7 @@
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/ptr_util.h"
 
@@ -25,7 +26,7 @@
 
 const int kNumTestPages = 7;
 
-CPDF_Dictionary* CreatePageTreeNode(std::unique_ptr<CPDF_Array> kids,
+CPDF_Dictionary* CreatePageTreeNode(RetainPtr<CPDF_Array> kids,
                                     CPDF_Document* pDoc,
                                     int count) {
   CPDF_Array* pUnowned = pDoc->AddIndirectObject(std::move(kids))->AsArray();
@@ -33,25 +34,27 @@
   pageNode->SetNewFor<CPDF_String>("Type", "Pages", false);
   pageNode->SetNewFor<CPDF_Reference>("Kids", pDoc, pUnowned->GetObjNum());
   pageNode->SetNewFor<CPDF_Number>("Count", count);
-  for (size_t i = 0; i < pUnowned->GetCount(); i++) {
+  for (size_t i = 0; i < pUnowned->size(); i++) {
     pUnowned->GetDictAt(i)->SetNewFor<CPDF_Reference>("Parent", pDoc,
                                                       pageNode->GetObjNum());
   }
   return pageNode;
 }
 
-std::unique_ptr<CPDF_Dictionary> CreateNumberedPage(size_t number) {
-  auto page = pdfium::MakeUnique<CPDF_Dictionary>();
+RetainPtr<CPDF_Dictionary> CreateNumberedPage(size_t number) {
+  auto page = pdfium::MakeRetain<CPDF_Dictionary>();
   page->SetNewFor<CPDF_String>("Type", "Page", false);
   page->SetNewFor<CPDF_Number>("PageNumbering", static_cast<int>(number));
   return page;
 }
 
-class CPDF_TestDocumentForPages : public CPDF_Document {
+class CPDF_TestDocumentForPages final : public CPDF_Document {
  public:
-  CPDF_TestDocumentForPages() : CPDF_Document(nullptr) {
+  CPDF_TestDocumentForPages()
+      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
+                      pdfium::MakeUnique<CPDF_DocPageData>()) {
     // Set up test
-    auto zeroToTwo = pdfium::MakeUnique<CPDF_Array>();
+    auto zeroToTwo = pdfium::MakeRetain<CPDF_Array>();
     zeroToTwo->AddNew<CPDF_Reference>(
         this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum());
     zeroToTwo->AddNew<CPDF_Reference>(
@@ -61,53 +64,51 @@
     CPDF_Dictionary* branch1 =
         CreatePageTreeNode(std::move(zeroToTwo), this, 3);
 
-    auto zeroToThree = pdfium::MakeUnique<CPDF_Array>();
+    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 =
         CreatePageTreeNode(std::move(zeroToThree), this, 4);
 
-    auto fourFive = pdfium::MakeUnique<CPDF_Array>();
+    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);
 
-    auto justSix = pdfium::MakeUnique<CPDF_Array>();
+    auto justSix = pdfium::MakeRetain<CPDF_Array>();
     justSix->AddNew<CPDF_Reference>(
         this, AddIndirectObject(CreateNumberedPage(6))->GetObjNum());
     CPDF_Dictionary* branch4 = CreatePageTreeNode(std::move(justSix), this, 1);
 
-    auto allPages = pdfium::MakeUnique<CPDF_Array>();
+    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 =
         CreatePageTreeNode(std::move(allPages), this, kNumTestPages);
 
-    m_pOwnedRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
-    m_pOwnedRootDict->SetNewFor<CPDF_Reference>("Pages", this,
-                                                pagesDict->GetObjNum());
-    m_pRootDict = m_pOwnedRootDict.get();
+    m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
+    m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this,
+                                           pagesDict->GetObjNum());
     m_PageList.resize(kNumTestPages);
   }
 
   void SetTreeSize(int size) {
-    m_pOwnedRootDict->SetNewFor<CPDF_Number>("Count", size);
+    m_pRootDict->SetNewFor<CPDF_Number>("Count", size);
     m_PageList.resize(size);
   }
-
- private:
-  std::unique_ptr<CPDF_Dictionary> m_pOwnedRootDict;
 };
 
-class CPDF_TestDocumentWithPageWithoutPageNum : public CPDF_Document {
+class CPDF_TestDocumentWithPageWithoutPageNum final : public CPDF_Document {
  public:
-  CPDF_TestDocumentWithPageWithoutPageNum() : CPDF_Document(nullptr) {
+  CPDF_TestDocumentWithPageWithoutPageNum()
+      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
+                      pdfium::MakeUnique<CPDF_DocPageData>()) {
     // Set up test
-    auto allPages = pdfium::MakeUnique<CPDF_Array>();
+    auto allPages = pdfium::MakeRetain<CPDF_Array>();
     allPages->AddNew<CPDF_Reference>(
         this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum());
     allPages->AddNew<CPDF_Reference>(
@@ -116,70 +117,76 @@
     inlined_page_ = allPages->Add(CreateNumberedPage(2));
     CPDF_Dictionary* pagesDict =
         CreatePageTreeNode(std::move(allPages), this, 3);
-    m_pOwnedRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
-    m_pOwnedRootDict->SetNewFor<CPDF_Reference>("Pages", this,
-                                                pagesDict->GetObjNum());
-    m_pRootDict = m_pOwnedRootDict.get();
+    m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
+    m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this,
+                                           pagesDict->GetObjNum());
     m_PageList.resize(3);
   }
 
   const CPDF_Object* inlined_page() const { return inlined_page_; }
 
  private:
-  std::unique_ptr<CPDF_Dictionary> m_pOwnedRootDict;
   const CPDF_Object* inlined_page_;
 };
 
-class TestLinearized : public CPDF_LinearizedHeader {
+class TestLinearized final : public CPDF_LinearizedHeader {
  public:
   explicit TestLinearized(CPDF_Dictionary* dict)
       : CPDF_LinearizedHeader(dict, 0) {}
 };
 
-class CPDF_TestDocPagesWithoutKids : public CPDF_Document {
+class CPDF_TestDocPagesWithoutKids final : public CPDF_Document {
  public:
-  CPDF_TestDocPagesWithoutKids() : CPDF_Document(nullptr) {
+  CPDF_TestDocPagesWithoutKids()
+      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
+                      pdfium::MakeUnique<CPDF_DocPageData>()) {
     CPDF_Dictionary* pagesDict = NewIndirect<CPDF_Dictionary>();
     pagesDict->SetNewFor<CPDF_Name>("Type", "Pages");
     pagesDict->SetNewFor<CPDF_Number>("Count", 3);
     m_PageList.resize(10);
-    m_pOwnedRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
-    m_pOwnedRootDict->SetNewFor<CPDF_Reference>("Pages", this,
-                                                pagesDict->GetObjNum());
-    m_pRootDict = m_pOwnedRootDict.get();
+    m_pRootDict.Reset(NewIndirect<CPDF_Dictionary>());
+    m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this,
+                                           pagesDict->GetObjNum());
   }
-
- private:
-  std::unique_ptr<CPDF_Dictionary> m_pOwnedRootDict;
 };
+
+class CPDF_TestDocumentAllowSetParser final : public CPDF_Document {
+ public:
+  CPDF_TestDocumentAllowSetParser()
+      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
+                      pdfium::MakeUnique<CPDF_DocPageData>()) {}
+
+  using CPDF_Document::SetParser;
+};
+
 }  // namespace
 
 class cpdf_document_test : public testing::Test {
  public:
-  void SetUp() override { CPDF_ModuleMgr::Get()->Init(); }
-  void TearDown() override { CPDF_ModuleMgr::Destroy(); }
+  void SetUp() override { CPDF_PageModule::Create(); }
+  void TearDown() override { CPDF_PageModule::Destroy(); }
 };
 
 TEST_F(cpdf_document_test, GetPages) {
   std::unique_ptr<CPDF_TestDocumentForPages> document =
       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
   for (int i = 0; i < kNumTestPages; i++) {
-    CPDF_Dictionary* page = document->GetPage(i);
+    CPDF_Dictionary* page = document->GetPageDictionary(i);
     ASSERT_TRUE(page);
     ASSERT_TRUE(page->KeyExist("PageNumbering"));
     EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
   }
-  CPDF_Dictionary* page = document->GetPage(kNumTestPages);
+  CPDF_Dictionary* page = document->GetPageDictionary(kNumTestPages);
   EXPECT_FALSE(page);
 }
 
 TEST_F(cpdf_document_test, GetPageWithoutObjNumTwice) {
   auto document = pdfium::MakeUnique<CPDF_TestDocumentWithPageWithoutPageNum>();
-  const CPDF_Dictionary* page = document->GetPage(2);
+  CPDF_Dictionary* page = document->GetPageDictionary(2);
   ASSERT_TRUE(page);
   ASSERT_EQ(document->inlined_page(), page);
 
-  const CPDF_Dictionary* second_call_page = document->GetPage(2);
+  CPDF_Dictionary* second_call_page = document->GetPageDictionary(2);
   EXPECT_TRUE(second_call_page);
   EXPECT_EQ(page, second_call_page);
 }
@@ -188,12 +195,12 @@
   std::unique_ptr<CPDF_TestDocumentForPages> document =
       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
   for (int i = 6; i >= 0; i--) {
-    CPDF_Dictionary* page = document->GetPage(i);
+    CPDF_Dictionary* page = document->GetPageDictionary(i);
     ASSERT_TRUE(page);
     ASSERT_TRUE(page->KeyExist("PageNumbering"));
     EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
   }
-  CPDF_Dictionary* page = document->GetPage(kNumTestPages);
+  CPDF_Dictionary* page = document->GetPageDictionary(kNumTestPages);
   EXPECT_FALSE(page);
 }
 
@@ -201,20 +208,20 @@
   std::unique_ptr<CPDF_TestDocumentForPages> document =
       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
 
-  CPDF_Dictionary* page = document->GetPage(1);
+  CPDF_Dictionary* page = document->GetPageDictionary(1);
   ASSERT_TRUE(page);
   ASSERT_TRUE(page->KeyExist("PageNumbering"));
   EXPECT_EQ(1, page->GetIntegerFor("PageNumbering"));
 
-  page = document->GetPage(3);
+  page = document->GetPageDictionary(3);
   ASSERT_TRUE(page);
   ASSERT_TRUE(page->KeyExist("PageNumbering"));
   EXPECT_EQ(3, page->GetIntegerFor("PageNumbering"));
 
-  page = document->GetPage(kNumTestPages);
+  page = document->GetPageDictionary(kNumTestPages);
   EXPECT_FALSE(page);
 
-  page = document->GetPage(6);
+  page = document->GetPageDictionary(6);
   ASSERT_TRUE(page);
   ASSERT_TRUE(page->KeyExist("PageNumbering"));
   EXPECT_EQ(6, page->GetIntegerFor("PageNumbering"));
@@ -224,24 +231,27 @@
   // 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).
-  CPDF_Document document(pdfium::MakeUnique<CPDF_Parser>());
-  auto dict = pdfium::MakeUnique<CPDF_Dictionary>();
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
   dict->SetNewFor<CPDF_Boolean>("Linearized", true);
   const int page_count = 100;
   dict->SetNewFor<CPDF_Number>("N", page_count);
-  TestLinearized linearized(dict.get());
-  document.LoadLinearizedDoc(&linearized);
+  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;
 
   EXPECT_FALSE(document.IsPageLoaded(test_page_num));
-  EXPECT_EQ(nullptr, document.GetPage(test_page_num));
+  EXPECT_EQ(nullptr, document.GetPageDictionary(test_page_num));
 
   document.SetPageObjNum(test_page_num, obj_num);
   EXPECT_TRUE(document.IsPageLoaded(test_page_num));
-  EXPECT_EQ(page_stub, document.GetPage(test_page_num));
+  EXPECT_EQ(page_stub, document.GetPageDictionary(test_page_num));
 }
 
 TEST_F(cpdf_document_test, CountGreaterThanPageTree) {
@@ -249,19 +259,19 @@
       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
   document->SetTreeSize(kNumTestPages + 3);
   for (int i = 0; i < kNumTestPages; i++)
-    EXPECT_TRUE(document->GetPage(i));
+    EXPECT_TRUE(document->GetPageDictionary(i));
   for (int i = kNumTestPages; i < kNumTestPages + 4; i++)
-    EXPECT_FALSE(document->GetPage(i));
-  EXPECT_TRUE(document->GetPage(kNumTestPages - 1));
+    EXPECT_FALSE(document->GetPageDictionary(i));
+  EXPECT_TRUE(document->GetPageDictionary(kNumTestPages - 1));
 }
 
 TEST_F(cpdf_document_test, PagesWithoutKids) {
   // Set up a document with Pages dict without kids, and Count = 3
   auto pDoc = pdfium::MakeUnique<CPDF_TestDocPagesWithoutKids>();
-  EXPECT_TRUE(pDoc->GetPage(0));
+  EXPECT_TRUE(pDoc->GetPageDictionary(0));
   // Test GetPage does not fetch pages out of range
   for (int i = 1; i < 5; i++)
-    EXPECT_FALSE(pDoc->GetPage(i));
+    EXPECT_FALSE(pDoc->GetPageDictionary(i));
 
-  EXPECT_TRUE(pDoc->GetPage(0));
+  EXPECT_TRUE(pDoc->GetPageDictionary(0));
 }
diff --git a/core/fpdfapi/parser/cpdf_encryptor.cpp b/core/fpdfapi/parser/cpdf_encryptor.cpp
new file mode 100644
index 0000000..706d668
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_encryptor.cpp
@@ -0,0 +1,30 @@
+// 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/parser/cpdf_encryptor.h"
+
+#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
+
+CPDF_Encryptor::CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum)
+    : m_pHandler(pHandler), m_ObjNum(objnum) {
+  ASSERT(m_pHandler);
+}
+
+std::vector<uint8_t> CPDF_Encryptor::Encrypt(
+    pdfium::span<const uint8_t> src_data) const {
+  if (src_data.empty())
+    return std::vector<uint8_t>();
+
+  std::vector<uint8_t> result;
+  uint32_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.
+  result.resize(buf_size);
+  return result;
+}
+
+CPDF_Encryptor::~CPDF_Encryptor() {}
diff --git a/core/fpdfapi/parser/cpdf_encryptor.h b/core/fpdfapi/parser/cpdf_encryptor.h
new file mode 100644
index 0000000..cea737a
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_encryptor.h
@@ -0,0 +1,31 @@
+// 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_PARSER_CPDF_ENCRYPTOR_H_
+#define CORE_FPDFAPI_PARSER_CPDF_ENCRYPTOR_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/span.h"
+
+class CPDF_CryptoHandler;
+
+class CPDF_Encryptor {
+ public:
+  CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum);
+  ~CPDF_Encryptor();
+
+  std::vector<uint8_t> Encrypt(pdfium::span<const uint8_t> src_data) const;
+
+ private:
+  UnownedPtr<CPDF_CryptoHandler> const m_pHandler;
+  const int m_ObjNum;
+};
+
+#endif  // CORE_FPDFAPI_PARSER_CPDF_ENCRYPTOR_H_
diff --git a/core/fpdfapi/parser/cpdf_flateencoder.cpp b/core/fpdfapi/parser/cpdf_flateencoder.cpp
new file mode 100644
index 0000000..b685822
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_flateencoder.cpp
@@ -0,0 +1,82 @@
+// 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/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"
+#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"
+
+CPDF_FlateEncoder::CPDF_FlateEncoder(const CPDF_Stream* pStream,
+                                     bool bFlateEncode)
+    : m_pAcc(pdfium::MakeRetain<CPDF_StreamAcc>(pStream)), m_dwSize(0) {
+  m_pAcc->LoadAllDataRaw();
+
+  bool bHasFilter = pStream->HasFilter();
+  if (bHasFilter && !bFlateEncode) {
+    auto pDestAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+    pDestAcc->LoadAllDataFiltered();
+
+    m_dwSize = pDestAcc->GetSize();
+    m_pData = pDestAcc->DetachData();
+    m_pClonedDict = ToDictionary(pStream->GetDict()->Clone());
+    m_pClonedDict->RemoveFor("Filter");
+    ASSERT(!m_pDict);
+    return;
+  }
+  if (bHasFilter || !bFlateEncode) {
+    m_pData = m_pAcc->GetData();
+    m_dwSize = m_pAcc->GetSize();
+    m_pDict.Reset(pStream->GetDict());
+    ASSERT(!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);
+  m_pClonedDict = ToDictionary(pStream->GetDict()->Clone());
+  m_pClonedDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(m_dwSize));
+  m_pClonedDict->SetNewFor<CPDF_Name>("Filter", "FlateDecode");
+  m_pClonedDict->RemoveFor(pdfium::stream::kDecodeParms);
+  ASSERT(!m_pDict);
+}
+
+CPDF_FlateEncoder::~CPDF_FlateEncoder() {}
+
+void CPDF_FlateEncoder::CloneDict() {
+  if (m_pClonedDict) {
+    ASSERT(!m_pDict);
+    return;
+  }
+
+  m_pClonedDict = ToDictionary(m_pDict->Clone());
+  ASSERT(m_pClonedDict);
+  m_pDict.Reset();
+}
+
+CPDF_Dictionary* CPDF_FlateEncoder::GetClonedDict() {
+  ASSERT(!m_pDict);
+  return m_pClonedDict.Get();
+}
+
+const CPDF_Dictionary* CPDF_FlateEncoder::GetDict() const {
+  if (m_pClonedDict) {
+    ASSERT(!m_pDict);
+    return m_pClonedDict.Get();
+  }
+
+  return m_pDict.Get();
+}
diff --git a/core/fpdfapi/parser/cpdf_flateencoder.h b/core/fpdfapi/parser/cpdf_flateencoder.h
new file mode 100644
index 0000000..df0db82
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_flateencoder.h
@@ -0,0 +1,47 @@
+// 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_PARSER_CPDF_FLATEENCODER_H_
+#define CORE_FPDFAPI_PARSER_CPDF_FLATEENCODER_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/span.h"
+
+class CPDF_Dictionary;
+class CPDF_Stream;
+class CPDF_StreamAcc;
+
+class CPDF_FlateEncoder {
+ public:
+  CPDF_FlateEncoder(const CPDF_Stream* pStream, bool bFlateEncode);
+  ~CPDF_FlateEncoder();
+
+  void CloneDict();
+  CPDF_Dictionary* GetClonedDict();
+
+  // 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);
+  }
+
+ private:
+  RetainPtr<CPDF_StreamAcc> m_pAcc;
+
+  uint32_t m_dwSize;
+  MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
+
+  // Only one of these two pointers is valid at any time.
+  RetainPtr<const CPDF_Dictionary> m_pDict;
+  RetainPtr<CPDF_Dictionary> m_pClonedDict;
+};
+
+#endif  // CORE_FPDFAPI_PARSER_CPDF_FLATEENCODER_H_
diff --git a/core/fpdfapi/parser/cpdf_hint_tables.cpp b/core/fpdfapi/parser/cpdf_hint_tables.cpp
index c9123d4..e051254 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables.cpp
+++ b/core/fpdfapi/parser/cpdf_hint_tables.cpp
@@ -16,9 +16,12 @@
 #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/numerics/safe_conversions.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 
 namespace {
 
@@ -36,6 +39,42 @@
 
 }  // namespace
 
+CPDF_HintTables::PageInfo::PageInfo() = default;
+CPDF_HintTables::PageInfo::~PageInfo() = default;
+
+//  static
+std::unique_ptr<CPDF_HintTables> CPDF_HintTables::Parse(
+    CPDF_SyntaxParser* parser,
+    CPDF_LinearizedHeader* pLinearized) {
+  ASSERT(parser);
+  if (!pLinearized || pLinearized->GetPageCount() <= 1 ||
+      !pLinearized->HasHintTable()) {
+    return nullptr;
+  }
+
+  const FX_FILESIZE szHintStart = pLinearized->GetHintStart();
+  const uint32_t szHintLength = pLinearized->GetHintLength();
+
+  if (!parser->GetValidator()->CheckDataRangeAndRequestIfUnavailable(
+          szHintStart, szHintLength)) {
+    return nullptr;
+  }
+
+  parser->SetPos(szHintStart);
+  RetainPtr<CPDF_Stream> hints_stream = ToStream(
+      parser->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose));
+
+  if (!hints_stream)
+    return nullptr;
+
+  auto pHintTables = pdfium::MakeUnique<CPDF_HintTables>(
+      parser->GetValidator().Get(), pLinearized);
+  if (!pHintTables->LoadHintStream(hints_stream.Get()))
+    return nullptr;
+
+  return pHintTables;
+}
+
 CPDF_HintTables::CPDF_HintTables(CPDF_ReadValidator* pValidator,
                                  CPDF_LinearizedHeader* pLinearized)
     : m_pValidator(pValidator),
@@ -47,30 +86,18 @@
 
 CPDF_HintTables::~CPDF_HintTables() {}
 
-uint32_t CPDF_HintTables::GetItemLength(
-    uint32_t index,
-    const std::vector<FX_FILESIZE>& szArray) const {
-  if (szArray.size() < 2 || index > szArray.size() - 2 ||
-      szArray[index] > szArray[index + 1]) {
-    return 0;
-  }
-  return szArray[index + 1] - szArray[index];
-}
-
 bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) {
+  const uint32_t nPages = m_pLinearized->GetPageCount();
+  if (nPages < 1 || nPages >= CPDF_Document::kPageMaxNum)
+    return false;
+
+  const uint32_t nFirstPageNum = m_pLinearized->GetFirstPageNo();
+  if (nFirstPageNum >= nPages)
+    return false;
+
   if (!hStream || hStream->IsEOF())
     return false;
 
-  int nStreamOffset = ReadPrimaryHintStreamOffset();
-  if (nStreamOffset < 0)
-    return false;
-
-  int nStreamLen = ReadPrimaryHintStreamLength();
-  if (nStreamLen < 1 ||
-      !pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(nStreamLen)) {
-    return false;
-  }
-
   const uint32_t kHeaderSize = 288;
   if (hStream->BitsRemaining() < kHeaderSize)
     return false;
@@ -81,18 +108,12 @@
     return false;
 
   // Item 2: The location of the first page's page object.
-  const uint32_t dwFirstObjLoc = hStream->GetBits(32);
-  if (dwFirstObjLoc > static_cast<uint32_t>(nStreamOffset)) {
-    FX_SAFE_FILESIZE safeLoc = nStreamLen;
-    safeLoc += dwFirstObjLoc;
-    if (!safeLoc.IsValid())
-      return false;
-    m_szFirstPageObjOffset = safeLoc.ValueOrDie();
-  } else {
-    if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(dwFirstObjLoc))
-      return false;
-    m_szFirstPageObjOffset = dwFirstObjLoc;
-  }
+  const FX_FILESIZE szFirstObjLoc =
+      HintsOffsetToFileOffset(hStream->GetBits(32));
+  if (!szFirstObjLoc)
+    return false;
+
+  m_szFirstPageObjOffset = szFirstObjLoc;
 
   // Item 3: The number of bits needed to represent the difference
   // between the greatest and least number of objects in a page.
@@ -131,111 +152,95 @@
   // shared object referenced from a page, there is an indication of
   // where in the page's content stream the object is first referenced.
   const uint32_t dwSharedNumeratorBits = hStream->GetBits(16);
-  if (!IsValidPageOffsetHintTableBitCount(dwSharedNumeratorBits))
+  if (dwSharedNumeratorBits > 32)
     return false;
 
   // Item 13: Skip Item 13 which has 16 bits.
   hStream->SkipBits(16);
 
-  const int nPages = GetNumberOfPages();
-  if (nPages < 1 || nPages >= FPDF_PAGE_MAX_NUM)
-    return false;
-
-  const uint32_t dwPages = pdfium::base::checked_cast<uint32_t>(nPages);
   FX_SAFE_UINT32 required_bits = dwDeltaObjectsBits;
-  required_bits *= dwPages;
+  required_bits *= nPages;
   if (!CanReadFromBitStream(hStream, required_bits))
     return false;
 
-  for (int i = 0; i < nPages; ++i) {
+  m_PageInfos = std::vector<PageInfo>(nPages);
+  m_PageInfos[nFirstPageNum].set_start_obj_num(
+      m_pLinearized->GetFirstPageObjNum());
+  // The object number of remaining pages starts from 1.
+  uint32_t dwStartObjNum = 1;
+  for (uint32_t i = 0; i < nPages; ++i) {
     FX_SAFE_UINT32 safeDeltaObj = hStream->GetBits(dwDeltaObjectsBits);
     safeDeltaObj += dwObjLeastNum;
     if (!safeDeltaObj.IsValid())
       return false;
-    m_dwDeltaNObjsArray.push_back(safeDeltaObj.ValueOrDie());
+    m_PageInfos[i].set_objects_count(safeDeltaObj.ValueOrDie());
+    if (i == nFirstPageNum)
+      continue;
+    m_PageInfos[i].set_start_obj_num(dwStartObjNum);
+    dwStartObjNum += m_PageInfos[i].objects_count();
   }
   hStream->ByteAlign();
 
   required_bits = dwDeltaPageLenBits;
-  required_bits *= dwPages;
+  required_bits *= nPages;
   if (!CanReadFromBitStream(hStream, required_bits))
     return false;
 
-  std::vector<uint32_t> dwPageLenArray;
-  for (int i = 0; i < nPages; ++i) {
+  for (uint32_t i = 0; i < nPages; ++i) {
     FX_SAFE_UINT32 safePageLen = hStream->GetBits(dwDeltaPageLenBits);
     safePageLen += dwPageLeastLen;
     if (!safePageLen.IsValid())
       return false;
-
-    dwPageLenArray.push_back(safePageLen.ValueOrDie());
+    m_PageInfos[i].set_page_length(safePageLen.ValueOrDie());
   }
 
-  int nOffsetE = GetEndOfFirstPageOffset();
-  if (nOffsetE < 0)
-    return false;
-
-  int nFirstPageNum = GetFirstPageNumber();
-  if (nFirstPageNum < 0 || nFirstPageNum > std::numeric_limits<int>::max() - 1)
-    return false;
-
-  for (int i = 0; i < nPages; ++i) {
-    if (i == nFirstPageNum) {
-      m_szPageOffsetArray.push_back(m_szFirstPageObjOffset);
-    } else if (i == nFirstPageNum + 1) {
-      if (i == 1) {
-        m_szPageOffsetArray.push_back(nOffsetE);
-      } else {
-        m_szPageOffsetArray.push_back(m_szPageOffsetArray[i - 2] +
-                                      dwPageLenArray[i - 2]);
-      }
-    } else {
-      if (i == 0) {
-        m_szPageOffsetArray.push_back(nOffsetE);
-      } else {
-        m_szPageOffsetArray.push_back(m_szPageOffsetArray[i - 1] +
-                                      dwPageLenArray[i - 1]);
-      }
-    }
+  ASSERT(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) {
+    if (i == nFirstPageNum)
+      continue;
+    m_PageInfos[i].set_page_offset(prev_page_end);
+    prev_page_end += m_PageInfos[i].page_length();
   }
-
-  m_szPageOffsetArray.push_back(m_szPageOffsetArray[nPages - 1] +
-                                dwPageLenArray[nPages - 1]);
   hStream->ByteAlign();
 
   // Number of shared objects.
   required_bits = dwSharedObjBits;
-  required_bits *= dwPages;
+  required_bits *= nPages;
   if (!CanReadFromBitStream(hStream, required_bits))
     return false;
 
-  for (int i = 0; i < nPages; i++)
-    m_dwNSharedObjsArray.push_back(hStream->GetBits(dwSharedObjBits));
+  std::vector<uint32_t> dwNSharedObjsArray(nPages);
+  for (uint32_t i = 0; i < nPages; i++)
+    dwNSharedObjsArray[i] = hStream->GetBits(dwSharedObjBits);
   hStream->ByteAlign();
 
   // Array of identifiers, size = nshared_objects.
-  for (int i = 0; i < nPages; i++) {
+  for (uint32_t i = 0; i < nPages; i++) {
     required_bits = dwSharedIdBits;
-    required_bits *= m_dwNSharedObjsArray[i];
+    required_bits *= dwNSharedObjsArray[i];
     if (!CanReadFromBitStream(hStream, required_bits))
       return false;
 
-    for (uint32_t j = 0; j < m_dwNSharedObjsArray[i]; j++)
-      m_dwIdentifierArray.push_back(hStream->GetBits(dwSharedIdBits));
+    for (uint32_t j = 0; j < dwNSharedObjsArray[i]; j++)
+      m_PageInfos[i].AddIdentifier(hStream->GetBits(dwSharedIdBits));
   }
   hStream->ByteAlign();
 
-  for (int i = 0; i < nPages; i++) {
-    FX_SAFE_UINT32 safeSize = m_dwNSharedObjsArray[i];
-    safeSize *= dwSharedNumeratorBits;
-    if (!CanReadFromBitStream(hStream, safeSize))
-      return false;
+  if (dwSharedNumeratorBits) {
+    for (uint32_t i = 0; i < nPages; i++) {
+      FX_SAFE_UINT32 safeSize = dwNSharedObjsArray[i];
+      safeSize *= dwSharedNumeratorBits;
+      if (!CanReadFromBitStream(hStream, safeSize))
+        return false;
 
-    hStream->SkipBits(safeSize.ValueOrDie());
+      hStream->SkipBits(safeSize.ValueOrDie());
+    }
+    hStream->ByteAlign();
   }
-  hStream->ByteAlign();
 
-  FX_SAFE_UINT32 safeTotalPageLen = dwPages;
+  FX_SAFE_UINT32 safeTotalPageLen = nPages;
   safeTotalPageLen *= dwDeltaPageLenBits;
   if (!CanReadFromBitStream(hStream, safeTotalPageLen))
     return false;
@@ -250,11 +255,6 @@
   if (!hStream || hStream->IsEOF())
     return false;
 
-  int nStreamOffset = ReadPrimaryHintStreamOffset();
-  int nStreamLen = ReadPrimaryHintStreamLength();
-  if (nStreamOffset < 0 || nStreamLen < 1)
-    return false;
-
   FX_SAFE_UINT32 bit_offset = offset;
   bit_offset *= 8;
   if (!bit_offset.IsValid() || hStream->GetPos() > bit_offset.ValueOrDie())
@@ -268,11 +268,14 @@
   // Item 1: The object number of the first object in the shared objects
   // section.
   uint32_t dwFirstSharedObjNum = hStream->GetBits(32);
+  if (!dwFirstSharedObjNum)
+    return false;
 
   // Item 2: The location of the first object in the shared objects section.
-  uint32_t dwFirstSharedObjLoc = hStream->GetBits(32);
-  if (dwFirstSharedObjLoc > static_cast<uint32_t>(nStreamOffset))
-    dwFirstSharedObjLoc += nStreamLen;
+  const FX_FILESIZE szFirstSharedObjLoc =
+      HintsOffsetToFileOffset(hStream->GetBits(32));
+  if (!szFirstSharedObjLoc)
+    return false;
 
   // Item 3: The number of shared object entries for the first page.
   m_nFirstPageSharedObjs = hStream->GetBits(32);
@@ -282,8 +285,10 @@
   uint32_t dwSharedObjTotal = hStream->GetBits(32);
 
   // Item 5: The number of bits needed to represent the greatest number of
-  // objects in a shared object group. Skipped.
-  hStream->SkipBits(16);
+  // objects in a shared object group.
+  uint32_t dwSharedObjNumBits = hStream->GetBits(16);
+  if (dwSharedObjNumBits > 32)
+    return false;
 
   // Item 6: The least length of a shared object group in bytes.
   uint32_t dwGroupLeastLen = hStream->GetBits(32);
@@ -293,8 +298,8 @@
   uint32_t dwDeltaGroupLen = hStream->GetBits(16);
 
   // Trying to decode more than 32 bits isn't going to work when we write into
-  // a uint32_t.
-  if (dwDeltaGroupLen > 31)
+  // a uint32_t. Decoding 0 bits also makes no sense.
+  if (!IsValidPageOffsetHintTableBitCount(dwDeltaGroupLen))
     return false;
 
   if (dwFirstSharedObjNum >= CPDF_Parser::kMaxObjectNumber ||
@@ -303,69 +308,86 @@
     return false;
   }
 
-  int nFirstPageObjNum = GetFirstPageObjectNumber();
-  if (nFirstPageObjNum < 0)
-    return false;
-
-  uint32_t dwPrevObjLen = 0;
-  uint32_t dwCurObjLen = 0;
   FX_SAFE_UINT32 required_bits = dwSharedObjTotal;
   required_bits *= dwDeltaGroupLen;
   if (!CanReadFromBitStream(hStream, required_bits))
     return false;
 
+  if (dwSharedObjTotal > 0) {
+    uint32_t dwLastSharedObj = dwSharedObjTotal - 1;
+    if (dwLastSharedObj > m_nFirstPageSharedObjs) {
+      FX_SAFE_UINT32 safeObjNum = dwFirstSharedObjNum;
+      safeObjNum += dwLastSharedObj - m_nFirstPageSharedObjs;
+      if (!safeObjNum.IsValid())
+        return false;
+    }
+  }
+
+  m_SharedObjGroupInfos.resize(dwSharedObjTotal);
+  // Table F.6 –  Shared object hint table, shared object group entries:
+  // Item 1: A number that, when added to the least shared object
+  // group length.
+  FX_SAFE_FILESIZE prev_shared_group_end_offset = m_szFirstPageObjOffset;
   for (uint32_t i = 0; i < dwSharedObjTotal; ++i) {
-    dwPrevObjLen = dwCurObjLen;
+    if (i == m_nFirstPageSharedObjs)
+      prev_shared_group_end_offset = szFirstSharedObjLoc;
+
     FX_SAFE_UINT32 safeObjLen = hStream->GetBits(dwDeltaGroupLen);
     safeObjLen += dwGroupLeastLen;
     if (!safeObjLen.IsValid())
       return false;
 
-    dwCurObjLen = safeObjLen.ValueOrDie();
-    if (i < m_nFirstPageSharedObjs) {
-      m_dwSharedObjNumArray.push_back(nFirstPageObjNum + i);
-      if (i == 0)
-        m_szSharedObjOffsetArray.push_back(m_szFirstPageObjOffset);
-    } else {
-      FX_SAFE_UINT32 safeObjNum = dwFirstSharedObjNum;
-      safeObjNum += i - m_nFirstPageSharedObjs;
-      if (!safeObjNum.IsValid())
-        return false;
-
-      m_dwSharedObjNumArray.push_back(safeObjNum.ValueOrDie());
-      if (i == m_nFirstPageSharedObjs) {
-        FX_SAFE_FILESIZE safeLoc = dwFirstSharedObjLoc;
-        if (!safeLoc.IsValid())
-          return false;
-
-        m_szSharedObjOffsetArray.push_back(safeLoc.ValueOrDie());
-      }
-    }
-
-    if (i != 0 && i != m_nFirstPageSharedObjs) {
-      FX_SAFE_FILESIZE safeLoc = dwPrevObjLen;
-      safeLoc += m_szSharedObjOffsetArray[i - 1];
-      if (!safeLoc.IsValid())
-        return false;
-
-      m_szSharedObjOffsetArray.push_back(safeLoc.ValueOrDie());
-    }
-  }
-
-  if (dwSharedObjTotal > 0) {
-    FX_SAFE_FILESIZE safeLoc = dwCurObjLen;
-    safeLoc += m_szSharedObjOffsetArray[dwSharedObjTotal - 1];
-    if (!safeLoc.IsValid())
+    m_SharedObjGroupInfos[i].m_dwLength = safeObjLen.ValueOrDie();
+    m_SharedObjGroupInfos[i].m_szOffset =
+        prev_shared_group_end_offset.ValueOrDie();
+    prev_shared_group_end_offset += m_SharedObjGroupInfos[i].m_dwLength;
+    if (!prev_shared_group_end_offset.IsValid())
       return false;
-
-    m_szSharedObjOffsetArray.push_back(safeLoc.ValueOrDie());
   }
 
   hStream->ByteAlign();
-  if (hStream->BitsRemaining() < dwSharedObjTotal)
-    return false;
+  {
+    // Item 2: A flag indicating whether the shared object signature (item 3) is
+    // present.
+    uint32_t signature_count = 0;
+    for (uint32_t i = 0; i < dwSharedObjTotal; ++i) {
+      signature_count += hStream->GetBits(1);
+    }
+    hStream->ByteAlign();
+    // Item 3: (Only if item 2 is 1) The shared object signature, a 16-byte MD5
+    // hash that uniquely identifies the resource that the group of objects
+    // represents.
+    if (signature_count) {
+      required_bits = signature_count;
+      required_bits *= 128;
+      if (!CanReadFromBitStream(hStream, required_bits))
+        return false;
 
-  hStream->SkipBits(dwSharedObjTotal);
+      hStream->SkipBits(required_bits.ValueOrDie());
+      hStream->ByteAlign();
+    }
+  }
+  // Item 4: A number equal to 1 less than the number of objects in the group.
+  FX_SAFE_UINT32 cur_obj_num = m_pLinearized->GetFirstPageObjNum();
+  for (uint32_t i = 0; i < dwSharedObjTotal; ++i) {
+    if (i == m_nFirstPageSharedObjs)
+      cur_obj_num = dwFirstSharedObjNum;
+
+    FX_SAFE_UINT32 obj_count =
+        dwSharedObjNumBits ? hStream->GetBits(dwSharedObjNumBits) : 0;
+    obj_count += 1;
+    if (!obj_count.IsValid())
+      return false;
+
+    uint32_t obj_num = cur_obj_num.ValueOrDie();
+    cur_obj_num += obj_count.ValueOrDie();
+    if (!cur_obj_num.IsValid())
+      return false;
+
+    m_SharedObjGroupInfos[i].m_dwStartObjNum = obj_num;
+    m_SharedObjGroupInfos[i].m_dwObjectsCount = obj_count.ValueOrDie();
+  }
+
   hStream->ByteAlign();
   return true;
 }
@@ -377,80 +399,40 @@
   if (index >= m_pLinearized->GetPageCount())
     return false;
 
-  *szPageStartPos = m_szPageOffsetArray[index];
-  *szPageLength = GetItemLength(index, m_szPageOffsetArray);
-
-  int nFirstPageObjNum = GetFirstPageObjectNumber();
-  if (nFirstPageObjNum < 0)
-    return false;
-
-  int nFirstPageNum = GetFirstPageNumber();
-  if (!pdfium::base::IsValueInRangeForNumericType<uint32_t>(nFirstPageNum))
-    return false;
-
-  uint32_t dwFirstPageNum = static_cast<uint32_t>(nFirstPageNum);
-  if (index == dwFirstPageNum) {
-    *dwObjNum = nFirstPageObjNum;
-    return true;
-  }
-
-  // The object number of remaining pages starts from 1.
-  *dwObjNum = 1;
-  for (uint32_t i = 0; i < index; ++i) {
-    if (i == dwFirstPageNum)
-      continue;
-    *dwObjNum += m_dwDeltaNObjsArray[i];
-  }
+  *szPageStartPos = m_PageInfos[index].page_offset();
+  *szPageLength = m_PageInfos[index].page_length();
+  *dwObjNum = m_PageInfos[index].start_obj_num();
   return true;
 }
 
 CPDF_DataAvail::DocAvailStatus CPDF_HintTables::CheckPage(uint32_t index) {
-  int nFirstPageNum = GetFirstPageNumber();
-  if (!pdfium::base::IsValueInRangeForNumericType<uint32_t>(nFirstPageNum))
-    return CPDF_DataAvail::DataError;
-
-  if (index == static_cast<uint32_t>(nFirstPageNum))
+  if (index == m_pLinearized->GetFirstPageNo())
     return CPDF_DataAvail::DataAvailable;
 
-  uint32_t dwLength = GetItemLength(index, m_szPageOffsetArray);
-  // If two pages have the same offset, it should be treated as an error.
+  if (index >= m_pLinearized->GetPageCount())
+    return CPDF_DataAvail::DataError;
+
+  const uint32_t dwLength = m_PageInfos[index].page_length();
   if (!dwLength)
     return CPDF_DataAvail::DataError;
 
   if (!m_pValidator->CheckDataRangeAndRequestIfUnavailable(
-          m_szPageOffsetArray[index], dwLength))
+          m_PageInfos[index].page_offset(), dwLength)) {
     return CPDF_DataAvail::DataNotAvailable;
+  }
 
   // Download data of shared objects in the page.
-  uint32_t offset = 0;
-  for (uint32_t i = 0; i < index; ++i)
-    offset += m_dwNSharedObjsArray[i];
-
-  int nFirstPageObjNum = GetFirstPageObjectNumber();
-  if (nFirstPageObjNum < 0)
-    return CPDF_DataAvail::DataError;
-
-  uint32_t dwIndex = 0;
-  uint32_t dwObjNum = 0;
-  for (uint32_t j = 0; j < m_dwNSharedObjsArray[index]; ++j) {
-    dwIndex = m_dwIdentifierArray[offset + j];
-    if (dwIndex >= m_dwSharedObjNumArray.size())
-      return CPDF_DataAvail::DataNotAvailable;
-
-    dwObjNum = m_dwSharedObjNumArray[dwIndex];
-    if (dwObjNum >= static_cast<uint32_t>(nFirstPageObjNum) &&
-        dwObjNum <
-            static_cast<uint32_t>(nFirstPageObjNum) + m_nFirstPageSharedObjs) {
+  for (const uint32_t dwIndex : m_PageInfos[index].Identifiers()) {
+    if (dwIndex >= m_SharedObjGroupInfos.size())
       continue;
-    }
+    const SharedObjGroupInfo& shared_group_info =
+        m_SharedObjGroupInfos[dwIndex];
 
-    dwLength = GetItemLength(dwIndex, m_szSharedObjOffsetArray);
-    // If two objects have the same offset, it should be treated as an error.
-    if (!dwLength)
+    if (!shared_group_info.m_szOffset || !shared_group_info.m_dwLength)
       return CPDF_DataAvail::DataError;
 
     if (!m_pValidator->CheckDataRangeAndRequestIfUnavailable(
-            m_szSharedObjOffsetArray[dwIndex], dwLength)) {
+            shared_group_info.m_szOffset, shared_group_info.m_dwLength)) {
       return CPDF_DataAvail::DataNotAvailable;
     }
   }
@@ -458,7 +440,7 @@
 }
 
 bool CPDF_HintTables::LoadHintStream(CPDF_Stream* pHintStream) {
-  if (!pHintStream)
+  if (!pHintStream || !m_pLinearized->HasHintTable())
     return false;
 
   CPDF_Dictionary* pDict = pHintStream->GetDict();
@@ -487,31 +469,28 @@
     return false;
   }
 
-  CFX_BitStream bs(pAcc->GetData(), size);
+  CFX_BitStream bs(pAcc->GetSpan().subspan(0, size));
   return ReadPageHintTable(&bs) &&
          ReadSharedObjHintTable(&bs, shared_hint_table_offset);
 }
 
-int CPDF_HintTables::GetEndOfFirstPageOffset() const {
-  return static_cast<int>(m_pLinearized->GetFirstPageEndOffset());
-}
+FX_FILESIZE CPDF_HintTables::HintsOffsetToFileOffset(
+    uint32_t hints_offset) const {
+  FX_SAFE_FILESIZE file_offset = hints_offset;
+  if (!file_offset.IsValid())
+    return 0;
 
-int CPDF_HintTables::GetNumberOfPages() const {
-  return static_cast<int>(m_pLinearized->GetPageCount());
-}
+  // The resulting positions shall be interpreted as if the primary hint stream
+  // 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).
+  // 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
+  // property.
+  if (file_offset.ValueOrDie() >= m_pLinearized->GetHintStart())
+    file_offset += m_pLinearized->GetHintLength();
 
-int CPDF_HintTables::GetFirstPageObjectNumber() const {
-  return static_cast<int>(m_pLinearized->GetFirstPageObjNum());
-}
-
-int CPDF_HintTables::GetFirstPageNumber() const {
-  return static_cast<int>(m_pLinearized->GetFirstPageNo());
-}
-
-int CPDF_HintTables::ReadPrimaryHintStreamOffset() const {
-  return static_cast<int>(m_pLinearized->GetHintStart());
-}
-
-int CPDF_HintTables::ReadPrimaryHintStreamLength() const {
-  return static_cast<int>(m_pLinearized->GetHintLength());
+  return file_offset.ValueOrDefault(0);
 }
diff --git a/core/fpdfapi/parser/cpdf_hint_tables.h b/core/fpdfapi/parser/cpdf_hint_tables.h
index d5feb46..e3f280f 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables.h
+++ b/core/fpdfapi/parser/cpdf_hint_tables.h
@@ -7,19 +7,71 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_HINT_TABLES_H_
 #define CORE_FPDFAPI_PARSER_CPDF_HINT_TABLES_H_
 
+#include <memory>
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_data_avail.h"
-#include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CFX_BitStream;
 class CPDF_LinearizedHeader;
-class CPDF_Stream;
 class CPDF_ReadValidator;
+class CPDF_Stream;
+class CPDF_SyntaxParser;
 
 class CPDF_HintTables {
  public:
+  struct SharedObjGroupInfo {
+    FX_FILESIZE m_szOffset = 0;
+    uint32_t m_dwLength = 0;
+    uint32_t m_dwObjectsCount = 0;
+    uint32_t m_dwStartObjNum = 0;
+  };
+
+  class PageInfo {
+   public:
+    PageInfo();
+    ~PageInfo();
+
+    void set_objects_count(uint32_t objects_count) {
+      m_dwObjectsCount = objects_count;
+    }
+    uint32_t objects_count() const { return m_dwObjectsCount; }
+
+    void set_page_offset(FX_FILESIZE offset) { m_szOffset = offset; }
+    FX_FILESIZE page_offset() const { return m_szOffset; }
+
+    void set_page_length(uint32_t length) { m_dwLength = length; }
+    uint32_t page_length() const { return m_dwLength; }
+
+    void set_start_obj_num(uint32_t start_obj_num) {
+      m_dwStartObjNum = start_obj_num;
+    }
+    uint32_t start_obj_num() const { return m_dwStartObjNum; }
+
+    void AddIdentifier(uint32_t Identifier) {
+      m_dwIdentifierArray.push_back(Identifier);
+    }
+
+    const std::vector<uint32_t>& Identifiers() const {
+      return m_dwIdentifierArray;
+    }
+
+   private:
+    uint32_t m_dwObjectsCount = 0;
+    FX_FILESIZE m_szOffset = 0;
+    uint32_t m_dwLength = 0;
+    uint32_t m_dwStartObjNum = 0;
+    std::vector<uint32_t> m_dwIdentifierArray;
+
+    PageInfo(const PageInfo& other) = delete;
+    PageInfo& operator=(const PageInfo&) = delete;
+  };
+
+  static std::unique_ptr<CPDF_HintTables> Parse(
+      CPDF_SyntaxParser* parser,
+      CPDF_LinearizedHeader* pLinearized);
+
   CPDF_HintTables(CPDF_ReadValidator* pValidator,
                   CPDF_LinearizedHeader* pLinearized);
   virtual ~CPDF_HintTables();
@@ -33,21 +85,19 @@
 
   bool LoadHintStream(CPDF_Stream* pHintStream);
 
+  const std::vector<PageInfo>& PageInfos() const { return m_PageInfos; }
+  const std::vector<SharedObjGroupInfo>& SharedGroupInfos() const {
+    return m_SharedObjGroupInfos;
+  }
+
+  FX_FILESIZE GetFirstPageObjOffset() const { return m_szFirstPageObjOffset; }
+
  protected:
   bool ReadPageHintTable(CFX_BitStream* hStream);
   bool ReadSharedObjHintTable(CFX_BitStream* hStream, uint32_t offset);
 
  private:
-  // Tests can override.
-  virtual int GetEndOfFirstPageOffset() const;
-  virtual int GetNumberOfPages() const;
-  virtual int GetFirstPageObjectNumber() const;
-  virtual int GetFirstPageNumber() const;
-  virtual int ReadPrimaryHintStreamOffset() const;
-  virtual int ReadPrimaryHintStreamLength() const;
-
-  uint32_t GetItemLength(uint32_t index,
-                         const std::vector<FX_FILESIZE>& szArray) const;
+  FX_FILESIZE HintsOffsetToFileOffset(uint32_t hints_offset) const;
 
   // Owned by |m_pDataAvail|.
   UnownedPtr<CPDF_ReadValidator> m_pValidator;
@@ -57,12 +107,9 @@
 
   uint32_t m_nFirstPageSharedObjs;
   FX_FILESIZE m_szFirstPageObjOffset;
-  std::vector<uint32_t> m_dwDeltaNObjsArray;
-  std::vector<uint32_t> m_dwNSharedObjsArray;
-  std::vector<uint32_t> m_dwSharedObjNumArray;
-  std::vector<uint32_t> m_dwIdentifierArray;
-  std::vector<FX_FILESIZE> m_szPageOffsetArray;
-  std::vector<FX_FILESIZE> m_szSharedObjOffsetArray;
+
+  std::vector<PageInfo> m_PageInfos;
+  std::vector<SharedObjGroupInfo> m_SharedObjGroupInfos;
 };
 
 #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 e457229..27592ba 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp
@@ -8,8 +8,15 @@
 #include <string>
 #include <utility>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
 #include "core/fpdfapi/parser/cpdf_data_avail.h"
+#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_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/fx_stream.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,26 +25,48 @@
 
 namespace {
 
-std::unique_ptr<CPDF_DataAvail> MakeDataAvailFromFile(
+RetainPtr<CPDF_ReadValidator> MakeValidatorFromFile(
     const std::string& file_name) {
   std::string file_path;
-  if (!PathService::GetTestFilePath(file_name, &file_path))
-    return nullptr;
-  return pdfium::MakeUnique<CPDF_DataAvail>(
-      nullptr, IFX_SeekableReadStream::CreateFromFilename(file_path.c_str()),
-      true);
+  PathService::GetTestFilePath(file_name, &file_path);
+  ASSERT(!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);
+}
+
+class TestLinearizedHeader final : public CPDF_LinearizedHeader {
+ public:
+  TestLinearizedHeader(const CPDF_Dictionary* pDict,
+                       FX_FILESIZE szLastXRefOffset)
+      : 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))));
+    RetainPtr<CPDF_Dictionary> dict =
+        ToDictionary(parser.GetObjectBody(nullptr));
+    ASSERT(dict);
+    return pdfium::MakeUnique<TestLinearizedHeader>(dict.Get(), 0);
+  }
+};
+
 }  // namespace
 
 class CPDF_HintTablesTest : public testing::Test {
  public:
   CPDF_HintTablesTest() {
     // Needs for encoding Hint table stream.
-    CPDF_ModuleMgr::Get()->Init();
+    CPDF_PageModule::Create();
   }
 
-  ~CPDF_HintTablesTest() override { CPDF_ModuleMgr::Destroy(); }
+  ~CPDF_HintTablesTest() override { CPDF_PageModule::Destroy(); }
 };
 
 TEST_F(CPDF_HintTablesTest, Load) {
@@ -67,3 +96,87 @@
   ASSERT_FALSE(
       hint_tables->GetPagePos(2, &page_start, &page_length, &page_obj_num));
 }
+
+TEST_F(CPDF_HintTablesTest, PageAndGroupInfos) {
+  auto data_avail = MakeDataAvailFromFile("feature_linearized_loading.pdf");
+  ASSERT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable,
+            data_avail->IsDocAvail(nullptr));
+
+  const CPDF_HintTables* hint_tables = data_avail->GetHintTables();
+  ASSERT_TRUE(hint_tables);
+  ASSERT_EQ(2u, hint_tables->PageInfos().size());
+
+  EXPECT_EQ(5u, hint_tables->PageInfos()[0].objects_count());
+  EXPECT_EQ(777, hint_tables->PageInfos()[0].page_offset());
+  EXPECT_EQ(4328u, hint_tables->PageInfos()[0].page_length());
+  EXPECT_EQ(39u, hint_tables->PageInfos()[0].start_obj_num());
+  ASSERT_EQ(2u, hint_tables->PageInfos()[0].Identifiers().size());
+
+  EXPECT_EQ(0u, hint_tables->PageInfos()[0].Identifiers()[0]);
+  EXPECT_EQ(0u, hint_tables->PageInfos()[0].Identifiers()[1]);
+
+  EXPECT_EQ(3u, hint_tables->PageInfos()[1].objects_count());
+  EXPECT_EQ(5105, hint_tables->PageInfos()[1].page_offset());
+  EXPECT_EQ(767u, hint_tables->PageInfos()[1].page_length());
+  EXPECT_EQ(1u, hint_tables->PageInfos()[1].start_obj_num());
+  ASSERT_EQ(3u, hint_tables->PageInfos()[1].Identifiers().size());
+
+  EXPECT_EQ(2u, hint_tables->PageInfos()[1].Identifiers()[0]);
+  EXPECT_EQ(5u, hint_tables->PageInfos()[1].Identifiers()[1]);
+  EXPECT_EQ(3u, hint_tables->PageInfos()[1].Identifiers()[2]);
+
+  // SharedGroupInfo
+  ASSERT_EQ(6u, hint_tables->SharedGroupInfos().size());
+
+  EXPECT_EQ(777, hint_tables->SharedGroupInfos()[0].m_szOffset);
+  EXPECT_EQ(254u, hint_tables->SharedGroupInfos()[0].m_dwLength);
+  EXPECT_EQ(39u, hint_tables->SharedGroupInfos()[0].m_dwStartObjNum);
+  EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[0].m_dwObjectsCount);
+
+  EXPECT_EQ(1031, hint_tables->SharedGroupInfos()[1].m_szOffset);
+  EXPECT_EQ(389u, hint_tables->SharedGroupInfos()[1].m_dwLength);
+  EXPECT_EQ(40u, hint_tables->SharedGroupInfos()[1].m_dwStartObjNum);
+  EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[1].m_dwObjectsCount);
+
+  EXPECT_EQ(1420, hint_tables->SharedGroupInfos()[2].m_szOffset);
+  EXPECT_EQ(726u, hint_tables->SharedGroupInfos()[2].m_dwLength);
+  EXPECT_EQ(41u, hint_tables->SharedGroupInfos()[2].m_dwStartObjNum);
+  EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[2].m_dwObjectsCount);
+
+  EXPECT_EQ(2146, hint_tables->SharedGroupInfos()[3].m_szOffset);
+  EXPECT_EQ(290u, hint_tables->SharedGroupInfos()[3].m_dwLength);
+  EXPECT_EQ(42u, hint_tables->SharedGroupInfos()[3].m_dwStartObjNum);
+  EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[3].m_dwObjectsCount);
+
+  EXPECT_EQ(2436, hint_tables->SharedGroupInfos()[4].m_szOffset);
+  EXPECT_EQ(2669u, hint_tables->SharedGroupInfos()[4].m_dwLength);
+  EXPECT_EQ(43u, hint_tables->SharedGroupInfos()[4].m_dwStartObjNum);
+  EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[4].m_dwObjectsCount);
+
+  EXPECT_EQ(10939, hint_tables->SharedGroupInfos()[5].m_szOffset);
+  EXPECT_EQ(544u, hint_tables->SharedGroupInfos()[5].m_dwLength);
+  EXPECT_EQ(4u, hint_tables->SharedGroupInfos()[5].m_dwStartObjNum);
+  EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[5].m_dwObjectsCount);
+}
+
+TEST_F(CPDF_HintTablesTest, FirstPageOffset) {
+  // Test that valid hint table is loaded, and have correct offset of first page
+  // object.
+  const auto linearized_header = TestLinearizedHeader::MakeHeader(
+      "<< /Linearized 1 /L 19326762 /H [ 123730 3816 ] /O 5932 /E 639518 /N "
+      "102 /T 19220281 >>");
+  ASSERT_TRUE(linearized_header);
+  // This hint table is extracted from linearized file, generated by qpdf tool.
+  RetainPtr<CPDF_ReadValidator> validator =
+      MakeValidatorFromFile("hint_table_102p.bin");
+  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());
+  // Check that hint table will load.
+  ASSERT_TRUE(hint_tables->LoadHintStream(stream.Get()));
+  // Check that hint table have correct first page offset.
+  // 127546 is predefined real value from original file.
+  EXPECT_EQ(127546, hint_tables->GetFirstPageObjOffset());
+}
diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp b/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp
index 42e312c..b2e1b54 100644
--- a/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp
+++ b/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp
@@ -12,6 +12,7 @@
 #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"
 
 namespace {
 
@@ -32,7 +33,7 @@
 CPDF_Object* CPDF_IndirectObjectHolder::GetIndirectObject(
     uint32_t objnum) const {
   auto it = m_IndirectObjs.find(objnum);
-  return (it != m_IndirectObjs.end()) ? FilterInvalidObjNum(it->second.get())
+  return (it != m_IndirectObjs.end()) ? FilterInvalidObjNum(it->second.Get())
                                       : nullptr;
 }
 
@@ -44,9 +45,9 @@
   // 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());
+    return FilterInvalidObjNum(insert_result.first->second.Get());
 
-  std::unique_ptr<CPDF_Object> pNewObj = ParseIndirectObject(objnum);
+  RetainPtr<CPDF_Object> pNewObj = ParseIndirectObject(objnum);
   if (!pNewObj) {
     m_IndirectObjs.erase(insert_result.first);
     return nullptr;
@@ -55,43 +56,37 @@
   pNewObj->SetObjNum(objnum);
   m_LastObjNum = std::max(m_LastObjNum, objnum);
   insert_result.first->second = std::move(pNewObj);
-  return insert_result.first->second.get();
+  return insert_result.first->second.Get();
 }
 
-std::unique_ptr<CPDF_Object> CPDF_IndirectObjectHolder::ParseIndirectObject(
+RetainPtr<CPDF_Object> CPDF_IndirectObjectHolder::ParseIndirectObject(
     uint32_t objnum) {
   return nullptr;
 }
 
 CPDF_Object* CPDF_IndirectObjectHolder::AddIndirectObject(
-    std::unique_ptr<CPDF_Object> pObj) {
+    RetainPtr<CPDF_Object> pObj) {
   CHECK(!pObj->GetObjNum());
   pObj->SetObjNum(++m_LastObjNum);
 
   auto& obj_holder = m_IndirectObjs[m_LastObjNum];
-  if (obj_holder)
-    m_OrphanObjs.push_back(std::move(obj_holder));
-
   obj_holder = std::move(pObj);
-  return obj_holder.get();
+  return obj_holder.Get();
 }
 
 bool CPDF_IndirectObjectHolder::ReplaceIndirectObjectIfHigherGeneration(
     uint32_t objnum,
-    std::unique_ptr<CPDF_Object> pObj) {
+    RetainPtr<CPDF_Object> pObj) {
   ASSERT(objnum);
   if (!pObj || objnum == CPDF_Object::kInvalidObjNum)
     return false;
 
   auto& obj_holder = m_IndirectObjs[objnum];
-  const CPDF_Object* old_object = FilterInvalidObjNum(obj_holder.get());
+  const CPDF_Object* old_object = FilterInvalidObjNum(obj_holder.Get());
   if (old_object && pObj->GetGenNum() <= old_object->GetGenNum())
     return false;
 
   pObj->SetObjNum(objnum);
-  if (obj_holder)
-    m_OrphanObjs.push_back(std::move(obj_holder));
-
   obj_holder = std::move(pObj);
   m_LastObjNum = std::max(m_LastObjNum, objnum);
   return true;
@@ -99,7 +94,7 @@
 
 void CPDF_IndirectObjectHolder::DeleteIndirectObject(uint32_t objnum) {
   auto it = m_IndirectObjs.find(objnum);
-  if (it == m_IndirectObjs.end() || !FilterInvalidObjNum(it->second.get()))
+  if (it == m_IndirectObjs.end() || !FilterInvalidObjNum(it->second.Get()))
     return;
 
   m_IndirectObjs.erase(it);
diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder.h b/core/fpdfapi/parser/cpdf_indirect_object_holder.h
index 57374b9..1887cc8 100644
--- a/core/fpdfapi/parser/cpdf_indirect_object_holder.h
+++ b/core/fpdfapi/parser/cpdf_indirect_object_holder.h
@@ -22,7 +22,7 @@
 class CPDF_IndirectObjectHolder {
  public:
   using const_iterator =
-      std::map<uint32_t, std::unique_ptr<CPDF_Object>>::const_iterator;
+      std::map<uint32_t, RetainPtr<CPDF_Object>>::const_iterator;
 
   CPDF_IndirectObjectHolder();
   virtual ~CPDF_IndirectObjectHolder();
@@ -38,22 +38,30 @@
   typename std::enable_if<!CanInternStrings<T>::value, T*>::type NewIndirect(
       Args&&... args) {
     return static_cast<T*>(
-        AddIndirectObject(pdfium::MakeUnique<T>(std::forward<Args>(args)...)));
+        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::MakeUnique<T>(m_pByteStringPool, std::forward<Args>(args)...)));
+        pdfium::MakeRetain<T>(m_pByteStringPool, std::forward<Args>(args)...)));
+  }
+
+  // Creates and adds a new object not owned by the indirect object holder,
+  // but which can intern strings from it.
+  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)...);
   }
 
   // Takes ownership of |pObj|, returns unowned pointer to it.
-  CPDF_Object* AddIndirectObject(std::unique_ptr<CPDF_Object> pObj);
+  CPDF_Object* AddIndirectObject(RetainPtr<CPDF_Object> pObj);
 
   // Always takes ownership of |pObj|, return true if higher generation number.
-  bool ReplaceIndirectObjectIfHigherGeneration(
-      uint32_t objnum,
-      std::unique_ptr<CPDF_Object> pObj);
+  bool ReplaceIndirectObjectIfHigherGeneration(uint32_t objnum,
+                                               RetainPtr<CPDF_Object> pObj);
 
   uint32_t GetLastObjNum() const { return m_LastObjNum; }
   void SetLastObjNum(uint32_t objnum) { m_LastObjNum = objnum; }
@@ -66,12 +74,11 @@
   const_iterator end() const { return m_IndirectObjs.end(); }
 
  protected:
-  virtual std::unique_ptr<CPDF_Object> ParseIndirectObject(uint32_t objnum);
+  virtual RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum);
 
  private:
   uint32_t m_LastObjNum;
-  std::map<uint32_t, std::unique_ptr<CPDF_Object>> m_IndirectObjs;
-  std::vector<std::unique_ptr<CPDF_Object>> m_OrphanObjs;
+  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 666264f..5494855 100644
--- a/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp
@@ -14,13 +14,12 @@
 
 namespace {
 
-class MockIndirectObjectHolder : public CPDF_IndirectObjectHolder {
+class MockIndirectObjectHolder final : public CPDF_IndirectObjectHolder {
  public:
   MockIndirectObjectHolder() {}
   ~MockIndirectObjectHolder() override {}
 
-  MOCK_METHOD1(ParseIndirectObject,
-               std::unique_ptr<CPDF_Object>(uint32_t objnum));
+  MOCK_METHOD1(ParseIndirectObject, RetainPtr<CPDF_Object>(uint32_t objnum));
 };
 
 }  // namespace
@@ -31,11 +30,11 @@
   // parse request.
   EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_))
       .WillOnce(::testing::WithArg<0>(::testing::Invoke(
-          [&mock_holder](uint32_t objnum) -> std::unique_ptr<CPDF_Object> {
+          [&mock_holder](uint32_t objnum) -> RetainPtr<CPDF_Object> {
             const CPDF_Object* same_parse =
                 mock_holder.GetOrParseIndirectObject(objnum);
             CHECK(!same_parse);
-            return pdfium::MakeUnique<CPDF_Null>();
+            return pdfium::MakeRetain<CPDF_Null>();
           })));
 
   EXPECT_TRUE(mock_holder.GetOrParseIndirectObject(1000));
@@ -50,9 +49,9 @@
   ::testing::Mock::VerifyAndClearExpectations(&mock_holder);
 
   EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_))
-      .WillOnce(::testing::WithArg<0>(::testing::Invoke(
-          [](uint32_t objnum) -> std::unique_ptr<CPDF_Object> {
-            return pdfium::MakeUnique<CPDF_Null>();
+      .WillOnce(::testing::WithArg<0>(
+          ::testing::Invoke([](uint32_t objnum) -> RetainPtr<CPDF_Object> {
+            return pdfium::MakeRetain<CPDF_Null>();
           })));
   EXPECT_TRUE(mock_holder.GetOrParseIndirectObject(kObjNum));
   ::testing::Mock::VerifyAndClearExpectations(&mock_holder);
@@ -77,5 +76,5 @@
 
   EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0);
   EXPECT_FALSE(mock_holder.ReplaceIndirectObjectIfHigherGeneration(
-      CPDF_Object::kInvalidObjNum, pdfium::MakeUnique<CPDF_Null>()));
+      CPDF_Object::kInvalidObjNum, pdfium::MakeRetain<CPDF_Null>()));
 }
diff --git a/core/fpdfapi/parser/cpdf_linearized_header.cpp b/core/fpdfapi/parser/cpdf_linearized_header.cpp
index 994d69f..c7dc54a 100644
--- a/core/fpdfapi/parser/cpdf_linearized_header.cpp
+++ b/core/fpdfapi/parser/cpdf_linearized_header.cpp
@@ -14,6 +14,7 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
@@ -38,16 +39,15 @@
 }
 
 bool IsLinearizedHeaderValid(const CPDF_LinearizedHeader* header,
-                             FX_FILESIZE file_size) {
+                             FX_FILESIZE document_size) {
   ASSERT(header);
-  return header->GetFileSize() == file_size &&
-         static_cast<int>(header->GetFirstPageNo()) >= 0 &&
+  return header->GetFileSize() == document_size &&
          header->GetFirstPageNo() < kMaxInt &&
-         header->GetMainXRefTableFirstEntryOffset() < file_size &&
-         header->GetPageCount() > 0 &&
-         header->GetFirstPageEndOffset() < file_size &&
-         header->GetLastXRefOffset() < file_size &&
-         header->GetHintStart() < file_size;
+         header->GetFirstPageNo() < header->GetPageCount() &&
+         header->GetMainXRefTableFirstEntryOffset() < document_size &&
+         header->GetFirstPageEndOffset() < document_size &&
+         header->GetLastXRefOffset() < document_size &&
+         header->GetHintStart() < document_size;
 }
 
 }  // namespace
@@ -61,12 +61,12 @@
       parser->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose));
 
   if (!pDict || !pDict->KeyExist("Linearized") ||
-      !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.get(), "L", 1) ||
-      !IsValidNumericDictionaryValue<uint32_t>(pDict.get(), "P", 0, false) ||
-      !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.get(), "T", 1) ||
-      !IsValidNumericDictionaryValue<uint32_t>(pDict.get(), "N", 0) ||
-      !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.get(), "E", 1) ||
-      !IsValidNumericDictionaryValue<uint32_t>(pDict.get(), "O", 1)) {
+      !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.Get(), "L", 1) ||
+      !IsValidNumericDictionaryValue<uint32_t>(pDict.Get(), "P", 0, false) ||
+      !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.Get(), "T", 1) ||
+      !IsValidNumericDictionaryValue<uint32_t>(pDict.Get(), "N", 1) ||
+      !IsValidNumericDictionaryValue<FX_FILESIZE>(pDict.Get(), "E", 1) ||
+      !IsValidNumericDictionaryValue<uint32_t>(pDict.Get(), "O", 1)) {
     return nullptr;
   }
   // Move parser to the start of the xref table for the documents first page.
@@ -75,12 +75,11 @@
     return nullptr;
 
   auto result = pdfium::WrapUnique(
-      new CPDF_LinearizedHeader(pDict.get(), parser->GetPos()));
+      new CPDF_LinearizedHeader(pDict.Get(), parser->GetPos()));
 
-  if (!IsLinearizedHeaderValid(result.get(),
-                               parser->GetFileAccess()->GetSize())) {
+  if (!IsLinearizedHeaderValid(result.get(), parser->GetDocumentSize()))
     return nullptr;
-  }
+
   return result;
 }
 
@@ -95,7 +94,7 @@
       m_szLastXRefOffset(szLastXRefOffset) {
   const CPDF_Array* pHintStreamRange = pDict->GetArrayFor("H");
   const size_t nHintStreamSize =
-      pHintStreamRange ? pHintStreamRange->GetCount() : 0;
+      pHintStreamRange ? pHintStreamRange->size() : 0;
   if (nHintStreamSize == 2 || nHintStreamSize == 4) {
     m_szHintStart = std::max(pHintStreamRange->GetIntegerAt(0), 0);
     const FX_SAFE_UINT32 safe_hint_length = pHintStreamRange->GetIntegerAt(1);
diff --git a/core/fpdfapi/parser/cpdf_linearized_header.h b/core/fpdfapi/parser/cpdf_linearized_header.h
index 964ae26..44e0422 100644
--- a/core/fpdfapi/parser/cpdf_linearized_header.h
+++ b/core/fpdfapi/parser/cpdf_linearized_header.h
@@ -9,8 +9,7 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_system.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
diff --git a/core/fpdfapi/parser/cpdf_name.cpp b/core/fpdfapi/parser/cpdf_name.cpp
index 0607a30..9f3f49f 100644
--- a/core/fpdfapi/parser/cpdf_name.cpp
+++ b/core/fpdfapi/parser/cpdf_name.cpp
@@ -7,6 +7,7 @@
 #include "core/fpdfapi/parser/cpdf_name.h"
 
 #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"
 
@@ -19,11 +20,11 @@
 CPDF_Name::~CPDF_Name() {}
 
 CPDF_Object::Type CPDF_Name::GetType() const {
-  return NAME;
+  return kName;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Name::Clone() const {
-  return pdfium::MakeUnique<CPDF_Name>(nullptr, m_Name);
+RetainPtr<CPDF_Object> CPDF_Name::Clone() const {
+  return pdfium::MakeRetain<CPDF_Name>(nullptr, m_Name);
 }
 
 ByteString CPDF_Name::GetString() const {
@@ -47,10 +48,11 @@
 }
 
 WideString CPDF_Name::GetUnicodeText() const {
-  return PDF_DecodeText(m_Name);
+  return PDF_DecodeText(m_Name.raw_span());
 }
 
-bool CPDF_Name::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Name::WriteTo(IFX_ArchiveStream* archive,
+                        const CPDF_Encryptor* encryptor) const {
   return archive->WriteString("/") &&
          archive->WriteString(PDF_NameEncode(GetString()).AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_name.h b/core/fpdfapi/parser/cpdf_name.h
index 880c10a..cfd90bb 100644
--- a/core/fpdfapi/parser/cpdf_name.h
+++ b/core/fpdfapi/parser/cpdf_name.h
@@ -13,23 +13,27 @@
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
 
-class CPDF_Name : public CPDF_Object {
+class CPDF_Name final : public CPDF_Object {
  public:
-  CPDF_Name(WeakPtr<ByteStringPool> pPool, const ByteString& str);
-  ~CPDF_Name() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
+  RetainPtr<CPDF_Object> Clone() const override;
   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;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
- protected:
+ private:
+  CPDF_Name(WeakPtr<ByteStringPool> pPool, const ByteString& str);
+  ~CPDF_Name() override;
+
   ByteString m_Name;
 };
 
diff --git a/core/fpdfapi/parser/cpdf_null.cpp b/core/fpdfapi/parser/cpdf_null.cpp
index 1074efd..71299c1 100644
--- a/core/fpdfapi/parser/cpdf_null.cpp
+++ b/core/fpdfapi/parser/cpdf_null.cpp
@@ -5,20 +5,22 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #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_Object::Type CPDF_Null::GetType() const {
-  return NULLOBJ;
+  return kNullobj;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Null::Clone() const {
-  return pdfium::MakeUnique<CPDF_Null>();
+RetainPtr<CPDF_Object> CPDF_Null::Clone() const {
+  return pdfium::MakeRetain<CPDF_Null>();
 }
 
-bool CPDF_Null::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Null::WriteTo(IFX_ArchiveStream* archive,
+                        const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" null");
 }
 
diff --git a/core/fpdfapi/parser/cpdf_null.h b/core/fpdfapi/parser/cpdf_null.h
index 2ec05ec..767583b 100644
--- a/core/fpdfapi/parser/cpdf_null.h
+++ b/core/fpdfapi/parser/cpdf_null.h
@@ -11,15 +11,20 @@
 
 #include "core/fpdfapi/parser/cpdf_object.h"
 
-class CPDF_Null : public CPDF_Object {
+class CPDF_Null final : public CPDF_Object {
  public:
-  CPDF_Null();
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object.
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  RetainPtr<CPDF_Object> Clone() const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
   bool IsNull() const override;
+
+ private:
+  CPDF_Null();
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_NULL_H_
diff --git a/core/fpdfapi/parser/cpdf_number.cpp b/core/fpdfapi/parser/cpdf_number.cpp
index dac1f40..24abf20 100644
--- a/core/fpdfapi/parser/cpdf_number.cpp
+++ b/core/fpdfapi/parser/cpdf_number.cpp
@@ -5,35 +5,36 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/fpdfapi/parser/cpdf_number.h"
+
 #include "core/fxcrt/fx_stream.h"
 #include "third_party/base/ptr_util.h"
 
-CPDF_Number::CPDF_Number() : m_bInteger(true), m_Integer(0) {}
+CPDF_Number::CPDF_Number() {}
 
-CPDF_Number::CPDF_Number(int value) : m_bInteger(true), m_Integer(value) {}
+CPDF_Number::CPDF_Number(int value) : m_Number(value) {}
 
-CPDF_Number::CPDF_Number(float value) : m_bInteger(false), m_Float(value) {}
+CPDF_Number::CPDF_Number(float value) : m_Number(value) {}
 
-CPDF_Number::CPDF_Number(const ByteStringView& str)
-    : m_bInteger(FX_atonum(str, &m_Integer)) {}
+CPDF_Number::CPDF_Number(ByteStringView str) : m_Number(str) {}
 
 CPDF_Number::~CPDF_Number() {}
 
 CPDF_Object::Type CPDF_Number::GetType() const {
-  return NUMBER;
+  return kNumber;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Number::Clone() const {
-  return m_bInteger ? pdfium::MakeUnique<CPDF_Number>(m_Integer)
-                    : pdfium::MakeUnique<CPDF_Number>(m_Float);
+RetainPtr<CPDF_Object> CPDF_Number::Clone() const {
+  return m_Number.IsInteger()
+             ? pdfium::MakeRetain<CPDF_Number>(m_Number.GetSigned())
+             : pdfium::MakeRetain<CPDF_Number>(m_Number.GetFloat());
 }
 
 float CPDF_Number::GetNumber() const {
-  return m_bInteger ? static_cast<float>(m_Integer) : m_Float;
+  return m_Number.GetFloat();
 }
 
 int CPDF_Number::GetInteger() const {
-  return m_bInteger ? m_Integer : static_cast<int>(m_Float);
+  return m_Number.GetSigned();
 }
 
 bool CPDF_Number::IsNumber() const {
@@ -49,15 +50,16 @@
 }
 
 void CPDF_Number::SetString(const ByteString& str) {
-  m_bInteger = FX_atonum(str.AsStringView(), &m_Integer);
+  m_Number = FX_Number(str.AsStringView());
 }
 
 ByteString CPDF_Number::GetString() const {
-  return m_bInteger ? ByteString::FormatInteger(m_Integer)
-                    : ByteString::FormatFloat(m_Float);
+  return m_Number.IsInteger() ? ByteString::FormatInteger(m_Number.GetSigned())
+                              : ByteString::FormatFloat(m_Number.GetFloat());
 }
 
-bool CPDF_Number::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Number::WriteTo(IFX_ArchiveStream* archive,
+                          const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" ") &&
          archive->WriteString(GetString().AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_number.h b/core/fpdfapi/parser/cpdf_number.h
index 6611773..dc75340 100644
--- a/core/fpdfapi/parser/cpdf_number.h
+++ b/core/fpdfapi/parser/cpdf_number.h
@@ -10,20 +10,18 @@
 #include <memory>
 
 #include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/fx_number.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 
-class CPDF_Number : public CPDF_Object {
+class CPDF_Number final : public CPDF_Object {
  public:
-  CPDF_Number();
-  explicit CPDF_Number(int value);
-  explicit CPDF_Number(float value);
-  explicit CPDF_Number(const ByteStringView& str);
-  ~CPDF_Number() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
+  RetainPtr<CPDF_Object> Clone() const override;
   ByteString GetString() const override;
   float GetNumber() const override;
   int GetInteger() const override;
@@ -31,16 +29,19 @@
   bool IsNumber() const override;
   CPDF_Number* AsNumber() override;
   const CPDF_Number* AsNumber() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
-  bool IsInteger() const { return m_bInteger; }
+  bool IsInteger() const { return m_Number.IsInteger(); }
 
- protected:
-  bool m_bInteger;
-  union {
-    int m_Integer;
-    float m_Float;
-  };
+ private:
+  CPDF_Number();
+  explicit CPDF_Number(int value);
+  explicit CPDF_Number(float value);
+  explicit CPDF_Number(ByteStringView str);
+  ~CPDF_Number() override;
+
+  FX_Number m_Number;
 };
 
 inline CPDF_Number* ToNumber(CPDF_Object* obj) {
diff --git a/core/fpdfapi/parser/cpdf_object.cpp b/core/fpdfapi/parser/cpdf_object.cpp
index 67632a0..b61ee7e 100644
--- a/core/fpdfapi/parser/cpdf_object.cpp
+++ b/core/fpdfapi/parser/cpdf_object.cpp
@@ -12,28 +12,31 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 #include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fxcrt/fx_string.h"
 #include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/ptr_util.h"
 
 CPDF_Object::~CPDF_Object() {}
 
-CPDF_Object* CPDF_Object::GetDirect() const {
-  return const_cast<CPDF_Object*>(this);
+CPDF_Object* CPDF_Object::GetDirect() {
+  return this;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Object::CloneObjectNonCyclic(
-    bool bDirect) const {
+const CPDF_Object* CPDF_Object::GetDirect() const {
+  return this;
+}
+
+RetainPtr<CPDF_Object> CPDF_Object::CloneObjectNonCyclic(bool bDirect) const {
   std::set<const CPDF_Object*> visited_objs;
   return CloneNonCyclic(bDirect, &visited_objs);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Object::CloneDirectObject() const {
+RetainPtr<CPDF_Object> CPDF_Object::CloneDirectObject() const {
   return CloneObjectNonCyclic(true);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Object::CloneNonCyclic(
+RetainPtr<CPDF_Object> CPDF_Object::CloneNonCyclic(
     bool bDirect,
     std::set<const CPDF_Object*>* pVisited) const {
   return Clone();
@@ -55,7 +58,11 @@
   return 0;
 }
 
-CPDF_Dictionary* CPDF_Object::GetDict() const {
+CPDF_Dictionary* CPDF_Object::GetDict() {
+  return nullptr;
+}
+
+const CPDF_Dictionary* CPDF_Object::GetDict() const {
   return nullptr;
 }
 
@@ -162,3 +169,12 @@
 const CPDF_String* CPDF_Object::AsString() const {
   return nullptr;
 }
+
+RetainPtr<CPDF_Object> CPDF_Object::MakeReference(
+    CPDF_IndirectObjectHolder* holder) const {
+  if (IsInline()) {
+    NOTREACHED();
+    return nullptr;
+  }
+  return pdfium::MakeRetain<CPDF_Reference>(holder, GetObjNum());
+}
diff --git a/core/fpdfapi/parser/cpdf_object.h b/core/fpdfapi/parser/cpdf_object.h
index 7a14492..77810ca 100644
--- a/core/fpdfapi/parser/cpdf_object.h
+++ b/core/fpdfapi/parser/cpdf_object.h
@@ -17,6 +17,8 @@
 class CPDF_Array;
 class CPDF_Boolean;
 class CPDF_Dictionary;
+class CPDF_Encryptor;
+class CPDF_IndirectObjectHolder;
 class CPDF_Name;
 class CPDF_Null;
 class CPDF_Number;
@@ -25,23 +27,21 @@
 class CPDF_String;
 class IFX_ArchiveStream;
 
-class CPDF_Object {
+class CPDF_Object : public Retainable {
  public:
   static const uint32_t kInvalidObjNum = static_cast<uint32_t>(-1);
   enum Type {
-    BOOLEAN = 1,
-    NUMBER,
-    STRING,
-    NAME,
-    ARRAY,
-    DICTIONARY,
-    STREAM,
-    NULLOBJ,
-    REFERENCE
+    kBoolean = 1,
+    kNumber,
+    kString,
+    kName,
+    kArray,
+    kDictionary,
+    kStream,
+    kNullobj,
+    kReference
   };
 
-  virtual ~CPDF_Object();
-
   virtual Type GetType() const = 0;
   uint32_t GetObjNum() const { return m_ObjNum; }
   void SetObjNum(uint32_t objnum) { m_ObjNum = objnum; }
@@ -50,18 +50,20 @@
   bool IsInline() const { return m_ObjNum == 0; }
 
   // Create a deep copy of the object.
-  virtual std::unique_ptr<CPDF_Object> Clone() const = 0;
+  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 std::unique_ptr<CPDF_Object> CloneDirectObject() const;
+  virtual RetainPtr<CPDF_Object> CloneDirectObject() const;
 
-  virtual CPDF_Object* GetDirect() 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() const;
+  virtual CPDF_Dictionary* GetDict();
+  virtual const CPDF_Dictionary* GetDict() const;
 
   virtual void SetString(const ByteString& str);
 
@@ -92,7 +94,8 @@
   virtual CPDF_String* AsString();
   virtual const CPDF_String* AsString() const;
 
-  virtual bool WriteTo(IFX_ArchiveStream* archive) const = 0;
+  virtual bool WriteTo(IFX_ArchiveStream* archive,
+                       const CPDF_Encryptor* encryptor) const = 0;
 
   // Create a deep copy of the object with the option to either
   // copy a reference object or directly copy the object it refers to
@@ -100,21 +103,24 @@
   // Also check cyclic reference against |pVisited|, no copy if it is found.
   // Complex objects should implement their own CloneNonCyclic()
   // function to properly check for possible loop.
-  virtual std::unique_ptr<CPDF_Object> CloneNonCyclic(
+  virtual RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* pVisited) const;
 
+  // Return a reference to itself.
+  // The object must be direct (!IsInlined).
+  virtual RetainPtr<CPDF_Object> MakeReference(
+      CPDF_IndirectObjectHolder* holder) const;
+
  protected:
-  CPDF_Object() : m_ObjNum(0), m_GenNum(0) {}
+  CPDF_Object() = default;
+  CPDF_Object(const CPDF_Object& src) = delete;
+  ~CPDF_Object() override;
 
-  std::unique_ptr<CPDF_Object> CloneObjectNonCyclic(bool bDirect) const;
+  RetainPtr<CPDF_Object> CloneObjectNonCyclic(bool bDirect) const;
 
-  uint32_t m_ObjNum;
-
- private:
-  CPDF_Object(const CPDF_Object& src) {}
-
-  uint32_t m_GenNum;
+  uint32_t m_ObjNum = 0;
+  uint32_t m_GenNum = 0;
 };
 
 template <typename T>
diff --git a/core/fpdfapi/parser/cpdf_object_avail.cpp b/core/fpdfapi/parser/cpdf_object_avail.cpp
index 35e7f45..1dc5125 100644
--- a/core/fpdfapi/parser/cpdf_object_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_object_avail.cpp
@@ -11,10 +11,12 @@
 #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"
 
-CPDF_ObjectAvail::CPDF_ObjectAvail(CPDF_ReadValidator* validator,
-                                   CPDF_IndirectObjectHolder* holder,
-                                   const CPDF_Object* root)
+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);
@@ -23,12 +25,13 @@
     parsed_objnums_.insert(root_->GetObjNum());
 }
 
-CPDF_ObjectAvail::CPDF_ObjectAvail(CPDF_ReadValidator* validator,
-                                   CPDF_IndirectObjectHolder* holder,
-                                   uint32_t obj_num)
+CPDF_ObjectAvail::CPDF_ObjectAvail(
+    const RetainPtr<CPDF_ReadValidator>& validator,
+    CPDF_IndirectObjectHolder* holder,
+    uint32_t obj_num)
     : validator_(validator),
       holder_(holder),
-      root_(pdfium::MakeUnique<CPDF_Reference>(holder, obj_num)) {
+      root_(pdfium::MakeRetain<CPDF_Reference>(holder, obj_num)) {
   ASSERT(validator_);
   ASSERT(holder);
 }
@@ -57,13 +60,13 @@
       return true;
     }
 
-    const CPDF_ReadValidator::Session parse_session(validator_.Get());
-    const CPDF_Object* direct = holder_->GetOrParseIndirectObject(ref_obj_num);
+    const CPDF_ReadValidator::Session parse_session(validator_);
+    CPDF_Object* direct = holder_->GetOrParseIndirectObject(ref_obj_num);
     if (validator_->has_read_problems())
       return false;
 
     parsed_objnums_.insert(ref_obj_num);
-    root_ = direct;
+    root_.Reset(direct);
   }
   std::stack<uint32_t> non_parsed_objects_in_root;
   if (AppendObjectSubRefs(root_.Get(), &non_parsed_objects_in_root)) {
@@ -74,8 +77,9 @@
 }
 
 bool CPDF_ObjectAvail::CheckObjects() {
-  std::stack<uint32_t> objects_to_check = std::move(non_parsed_objects_);
   std::set<uint32_t> checked_objects;
+  std::stack<uint32_t> objects_to_check = std::move(non_parsed_objects_);
+  non_parsed_objects_ = std::stack<uint32_t>();
   while (!objects_to_check.empty()) {
     const uint32_t obj_num = objects_to_check.top();
     objects_to_check.pop();
@@ -86,9 +90,9 @@
     if (!checked_objects.insert(obj_num).second)
       continue;
 
-    const CPDF_ReadValidator::Session parse_session(validator_.Get());
+    const CPDF_ReadValidator::Session parse_session(validator_);
     const CPDF_Object* direct = holder_->GetOrParseIndirectObject(obj_num);
-    if (direct == root_.Get())
+    if (direct == root_)
       continue;
 
     if (validator_->has_read_problems() ||
@@ -109,13 +113,13 @@
 
   CPDF_ObjectWalker walker(object);
   while (const CPDF_Object* obj = walker.GetNext()) {
-    const CPDF_ReadValidator::Session parse_session(validator_.Get());
+    const CPDF_ReadValidator::Session 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_.Get()) ||
+    const bool skip = (walker.GetParent() && obj == root_) ||
                       walker.dictionary_key() == "Parent" ||
-                      (obj != root_.Get() && ExcludeObject(obj));
+                      (obj != root_ && ExcludeObject(obj));
 
     // We need to parse the object before we can do the exclusion check.
     // This is because the exclusion check may check against a referenced
diff --git a/core/fpdfapi/parser/cpdf_object_avail.h b/core/fpdfapi/parser/cpdf_object_avail.h
index f593ea2..901fb17 100644
--- a/core/fpdfapi/parser/cpdf_object_avail.h
+++ b/core/fpdfapi/parser/cpdf_object_avail.h
@@ -5,12 +5,11 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_OBJECT_AVAIL_H_
 #define CORE_FPDFAPI_PARSER_CPDF_OBJECT_AVAIL_H_
 
-#include <memory>
 #include <set>
 #include <stack>
 
 #include "core/fpdfapi/parser/cpdf_data_avail.h"
-#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Object;
@@ -21,10 +20,10 @@
 // Helper for check availability of object tree.
 class CPDF_ObjectAvail {
  public:
-  CPDF_ObjectAvail(CPDF_ReadValidator* validator,
+  CPDF_ObjectAvail(const RetainPtr<CPDF_ReadValidator>& validator,
                    CPDF_IndirectObjectHolder* holder,
-                   const CPDF_Object* root);
-  CPDF_ObjectAvail(CPDF_ReadValidator* validator,
+                   CPDF_Object* root);
+  CPDF_ObjectAvail(const RetainPtr<CPDF_ReadValidator>& validator,
                    CPDF_IndirectObjectHolder* holder,
                    uint32_t obj_num);
   virtual ~CPDF_ObjectAvail();
@@ -42,10 +41,9 @@
   void CleanMemory();
   bool HasObjectParsed(uint32_t obj_num) const;
 
-  UnownedPtr<CPDF_ReadValidator> validator_;
-  // TODO(art-snake): Make it UnownedPtr<, after fix document owning issue.
-  CPDF_IndirectObjectHolder* holder_;
-  MaybeOwned<const CPDF_Object> root_;
+  RetainPtr<CPDF_ReadValidator> validator_;
+  UnownedPtr<CPDF_IndirectObjectHolder> holder_;
+  RetainPtr<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 5c3da70..f27ae63 100644
--- a/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
@@ -5,6 +5,7 @@
 #include "core/fpdfapi/parser/cpdf_object_avail.h"
 
 #include <map>
+#include <memory>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -14,28 +15,27 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/fx_stream.h"
-#include "testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/invalid_seekable_read_stream.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-class TestReadValidator : public CPDF_ReadValidator {
+class TestReadValidator final : public CPDF_ReadValidator {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  void SimulateReadError() { ReadBlock(nullptr, 0, 1); }
+  void SimulateReadError() { ReadBlockAtOffset(nullptr, 0, 1); }
 
- protected:
+ private:
   TestReadValidator()
-      : CPDF_ReadValidator(
-            pdfium::MakeRetain<CFX_InvalidSeekableReadStream>(100),
-            nullptr) {}
+      : CPDF_ReadValidator(pdfium::MakeRetain<InvalidSeekableReadStream>(100),
+                           nullptr) {}
   ~TestReadValidator() override {}
 };
 
-class TestHolder : public CPDF_IndirectObjectHolder {
+class TestHolder final : public CPDF_IndirectObjectHolder {
  public:
   enum class ObjectState {
     Unavailable,
@@ -55,13 +55,13 @@
       validator_->SimulateReadError();
       return nullptr;
     }
-    return obj_data.object.get();
+    return obj_data.object.Get();
   }
 
   RetainPtr<CPDF_ReadValidator> GetValidator() { return validator_; }
 
   void AddObject(uint32_t objnum,
-                 std::unique_ptr<CPDF_Object> object,
+                 RetainPtr<CPDF_Object> object,
                  ObjectState state) {
     ObjectData object_data;
     object_data.object = std::move(object);
@@ -81,19 +81,19 @@
     auto it = objects_data_.find(objnum);
     if (it == objects_data_.end())
       return nullptr;
-    return it->second.object.get();
+    return it->second.object.Get();
   }
 
  private:
   struct ObjectData {
-    std::unique_ptr<CPDF_Object> object;
+    RetainPtr<CPDF_Object> object;
     ObjectState state = ObjectState::Unavailable;
   };
   std::map<uint32_t, ObjectData> objects_data_;
   RetainPtr<TestReadValidator> validator_;
 };
 
-class CPDF_ObjectAvailFailOnExclude : public CPDF_ObjectAvail {
+class CPDF_ObjectAvailFailOnExclude final : public CPDF_ObjectAvail {
  public:
   using CPDF_ObjectAvail::CPDF_ObjectAvail;
   ~CPDF_ObjectAvailFailOnExclude() override {}
@@ -103,7 +103,7 @@
   }
 };
 
-class CPDF_ObjectAvailExcludeArray : public CPDF_ObjectAvail {
+class CPDF_ObjectAvailExcludeArray final : public CPDF_ObjectAvail {
  public:
   using CPDF_ObjectAvail::CPDF_ObjectAvail;
   ~CPDF_ObjectAvailExcludeArray() override {}
@@ -112,7 +112,7 @@
   }
 };
 
-class CPDF_ObjectAvailExcludeTypeKey : public CPDF_ObjectAvail {
+class CPDF_ObjectAvailExcludeTypeKey final : public CPDF_ObjectAvail {
  public:
   using CPDF_ObjectAvail::CPDF_ObjectAvail;
   ~CPDF_ObjectAvailExcludeTypeKey() override {}
@@ -130,9 +130,9 @@
 
 TEST(CPDF_ObjectAvailTest, OneObject) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_String>(nullptr, "string", false),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
                    TestHolder::ObjectState::Unavailable);
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
             avail.CheckAvail());
   holder.SetObjectState(1, TestHolder::ObjectState::Available);
@@ -141,11 +141,11 @@
 
 TEST(CPDF_ObjectAvailTest, OneReferencedObject) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Reference>(&holder, 2),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
                    TestHolder::ObjectState::Unavailable);
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_String>(nullptr, "string", false),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
                    TestHolder::ObjectState::Unavailable);
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
             avail.CheckAvail());
 
@@ -159,14 +159,14 @@
 
 TEST(CPDF_ObjectAvailTest, CycledReferences) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Reference>(&holder, 2),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
                    TestHolder::ObjectState::Unavailable);
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_Reference>(&holder, 3),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_Reference>(&holder, 3),
                    TestHolder::ObjectState::Unavailable);
-  holder.AddObject(3, pdfium::MakeUnique<CPDF_Reference>(&holder, 1),
+  holder.AddObject(3, pdfium::MakeRetain<CPDF_Reference>(&holder, 1),
                    TestHolder::ObjectState::Unavailable);
 
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
             avail.CheckAvail());
 
@@ -184,15 +184,15 @@
 
 TEST(CPDF_ObjectAvailTest, DoNotCheckParent) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Unavailable);
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Unavailable);
 
   holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("Parent",
                                                                 &holder, 1);
 
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 2);
+  CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 2);
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
             avail.CheckAvail());
 
@@ -205,18 +205,17 @@
   TestHolder holder;
   const uint32_t kDepth = 100;
   for (uint32_t i = 1; i < kDepth; ++i) {
-    holder.AddObject(i, pdfium::MakeUnique<CPDF_Dictionary>(),
+    holder.AddObject(i, pdfium::MakeRetain<CPDF_Dictionary>(),
                      TestHolder::ObjectState::Unavailable);
     // Add ref to next dictionary.
     holder.GetTestObject(i)->GetDict()->SetNewFor<CPDF_Reference>(
         "Child", &holder, i + 1);
   }
   // Add final object
-  holder.AddObject(kDepth, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(kDepth, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Unavailable);
 
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
-
+  CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
   for (uint32_t i = 1; i <= kDepth; ++i) {
     EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
               avail.CheckAvail());
@@ -227,55 +226,55 @@
 
 TEST(CPDF_ObjectAvailTest, NotExcludeRoot) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
-  CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1);
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
 }
 
 TEST(CPDF_ObjectAvailTest, NotExcludeReferedRoot) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Reference>(&holder, 2),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Reference>(&holder, 2),
                    TestHolder::ObjectState::Available);
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
-  CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1);
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
 }
 
 TEST(CPDF_ObjectAvailTest, Exclude) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
   holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("ArrayRef",
                                                                 &holder, 2);
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_Array>(),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_Array>(),
                    TestHolder::ObjectState::Available);
   holder.GetTestObject(2)->AsArray()->AddNew<CPDF_Reference>(&holder, 2);
 
   // Add string, which is refered by array item. It is should not be checked.
   holder.AddObject(
       3,
-      pdfium::MakeUnique<CPDF_String>(nullptr, "Not available string", false),
+      pdfium::MakeRetain<CPDF_String>(nullptr, "Not available string", false),
       TestHolder::ObjectState::Unavailable);
-  CPDF_ObjectAvailExcludeArray avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvailExcludeArray avail(holder.GetValidator(), &holder, 1);
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
 }
 
 TEST(CPDF_ObjectAvailTest, ReadErrorOnExclude) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
   holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("DictRef",
                                                                 &holder, 2);
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
 
   holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("Type", &holder,
                                                                 3);
   // The value of "Type" key is not available at start
   holder.AddObject(
-      3, pdfium::MakeUnique<CPDF_String>(nullptr, "Exclude me", false),
+      3, pdfium::MakeRetain<CPDF_String>(nullptr, "Exclude me", false),
       TestHolder::ObjectState::Unavailable);
 
   holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("OtherData",
@@ -284,10 +283,10 @@
   // checked, because the dictionary with it, should be skipped.
   holder.AddObject(
       4,
-      pdfium::MakeUnique<CPDF_String>(nullptr, "Not available string", false),
+      pdfium::MakeRetain<CPDF_String>(nullptr, "Not available string", false),
       TestHolder::ObjectState::Unavailable);
 
-  CPDF_ObjectAvailExcludeTypeKey avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvailExcludeTypeKey avail(holder.GetValidator(), &holder, 1);
 
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
             avail.CheckAvail());
@@ -302,11 +301,11 @@
 
 TEST(CPDF_ObjectAvailTest, IgnoreNotExistsObject) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
   holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>(
       "NotExistsObjRef", &holder, 2);
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+  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());
@@ -314,10 +313,10 @@
 
 TEST(CPDF_ObjectAvailTest, CheckTwice) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_String>(nullptr, "string", false),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_String>(nullptr, "string", false),
                    TestHolder::ObjectState::Unavailable);
 
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+  CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1);
   EXPECT_EQ(avail.CheckAvail(), avail.CheckAvail());
 
   holder.SetObjectState(1, TestHolder::ObjectState::Available);
@@ -326,7 +325,7 @@
 
 TEST(CPDF_ObjectAvailTest, SelfReferedInlinedObject) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
 
   holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("Data", &holder,
@@ -336,10 +335,10 @@
 
   root->SetNewFor<CPDF_Reference>("Self", &holder, 1);
 
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_String>(nullptr, "Data", false),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_String>(nullptr, "Data", false),
                    TestHolder::ObjectState::Unavailable);
 
-  CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, root);
+  CPDF_ObjectAvail avail(holder.GetValidator(), &holder, root);
 
   EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
             avail.CheckAvail());
diff --git a/core/fpdfapi/parser/cpdf_object_stream.cpp b/core/fpdfapi/parser/cpdf_object_stream.cpp
new file mode 100644
index 0000000..a515dbb
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_object_stream.cpp
@@ -0,0 +1,134 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/cpdf_object_stream.h"
+
+#include <utility>
+
+#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_reference.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_readonlymemorystream.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+// static
+bool CPDF_ObjectStream::IsObjectsStreamObject(const CPDF_Object* object) {
+  const CPDF_Stream* stream = ToStream(object);
+  if (!stream)
+    return false;
+
+  const CPDF_Dictionary* stream_dict = stream->GetDict();
+  if (!stream_dict)
+    return false;
+
+  if (stream_dict->GetStringFor("Type") != "ObjStm")
+    return false;
+
+  const CPDF_Number* number_of_objects =
+      ToNumber(stream_dict->GetObjectFor("N"));
+  if (!number_of_objects || !number_of_objects->IsInteger() ||
+      number_of_objects->GetInteger() < 0 ||
+      number_of_objects->GetInteger() >=
+          static_cast<int>(CPDF_Parser::kMaxObjectNumber)) {
+    return false;
+  }
+
+  const CPDF_Number* first_object_offset =
+      ToNumber(stream_dict->GetObjectFor("First"));
+  if (!first_object_offset || !first_object_offset->IsInteger() ||
+      first_object_offset->GetInteger() < 0) {
+    return false;
+  }
+
+  return true;
+}
+
+//  static
+std::unique_ptr<CPDF_ObjectStream> CPDF_ObjectStream::Create(
+    const CPDF_Stream* stream) {
+  if (!IsObjectsStreamObject(stream))
+    return nullptr;
+  // The ctor of CPDF_ObjectStream is protected. Use WrapUnique instead
+  // MakeUnique.
+  return pdfium::WrapUnique(new CPDF_ObjectStream(stream));
+}
+
+CPDF_ObjectStream::CPDF_ObjectStream(const CPDF_Stream* obj_stream)
+    : obj_num_(obj_stream->GetObjNum()),
+      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);
+}
+
+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())
+    return nullptr;
+
+  RetainPtr<CPDF_Object> result = ParseObjectAtOffset(pObjList, it->second);
+  if (!result)
+    return nullptr;
+
+  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);
+  }
+
+  CPDF_SyntaxParser syntax(data_stream_);
+  const int object_count = stream->GetDict()->GetIntegerFor("N");
+  for (int32_t i = object_count; i > 0; --i) {
+    if (syntax.GetPos() >= data_stream_->GetSize())
+      break;
+
+    const uint32_t obj_num = syntax.GetDirectNum();
+    const uint32_t obj_offset = syntax.GetDirectNum();
+    if (!obj_num)
+      continue;
+
+    objects_offsets_[obj_num] = obj_offset;
+  }
+}
+
+RetainPtr<CPDF_Object> CPDF_ObjectStream::ParseObjectAtOffset(
+    CPDF_IndirectObjectHolder* pObjList,
+    uint32_t object_offset) const {
+  FX_SAFE_FILESIZE offset_in_stream = first_object_offset_;
+  offset_in_stream += object_offset;
+
+  if (!offset_in_stream.IsValid())
+    return nullptr;
+
+  if (offset_in_stream.ValueOrDie() >= data_stream_->GetSize())
+    return nullptr;
+
+  CPDF_SyntaxParser syntax(data_stream_);
+  syntax.SetPos(offset_in_stream.ValueOrDie());
+  return syntax.GetObjectBody(pObjList);
+}
diff --git a/core/fpdfapi/parser/cpdf_object_stream.h b/core/fpdfapi/parser/cpdf_object_stream.h
new file mode 100644
index 0000000..2fa1634
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_object_stream.h
@@ -0,0 +1,54 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_
+#define CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_
+
+#include <map>
+#include <memory>
+
+#include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_IndirectObjectHolder;
+class CPDF_Stream;
+class IFX_SeekableReadStream;
+
+// Implementation of logic of PDF "Object Streams".
+// See "PDF 32000-1:2008" Spec. section 7.5.7.
+class CPDF_ObjectStream {
+ public:
+  static bool IsObjectsStreamObject(const CPDF_Object* object);
+
+  static std::unique_ptr<CPDF_ObjectStream> Create(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_;
+  }
+
+ protected:
+  explicit CPDF_ObjectStream(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;
+
+  RetainPtr<IFX_SeekableReadStream> data_stream_;
+  int first_object_offset_ = 0;
+  std::map<uint32_t, uint32_t> objects_offsets_;
+};
+
+#endif  // CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_
diff --git a/core/fpdfapi/parser/cpdf_object_unittest.cpp b/core/fpdfapi/parser/cpdf_object_unittest.cpp
index 4b16021..ca02618 100644
--- a/core/fpdfapi/parser/cpdf_object_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_object_unittest.cpp
@@ -7,6 +7,7 @@
 #include <utility>
 #include <vector>
 
+#include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -16,8 +17,11 @@
 #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_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -33,7 +37,6 @@
   EXPECT_STREQ(str_val, arr->GetStringAt(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));
@@ -46,22 +49,23 @@
   void SetUp() override {
     // Initialize different kinds of objects.
     // Boolean objects.
-    CPDF_Boolean* boolean_false_obj = new CPDF_Boolean(false);
-    CPDF_Boolean* boolean_true_obj = new CPDF_Boolean(true);
+    auto boolean_false_obj = pdfium::MakeRetain<CPDF_Boolean>(false);
+    auto boolean_true_obj = pdfium::MakeRetain<CPDF_Boolean>(true);
     // Number objects.
-    CPDF_Number* number_int_obj = new CPDF_Number(1245);
-    CPDF_Number* number_float_obj = new CPDF_Number(9.00345f);
+    auto number_int_obj = pdfium::MakeRetain<CPDF_Number>(1245);
+    auto number_float_obj = pdfium::MakeRetain<CPDF_Number>(9.00345f);
     // String objects.
-    CPDF_String* str_reg_obj = new CPDF_String(nullptr, L"A simple test");
-    CPDF_String* str_spec_obj = new CPDF_String(nullptr, L"\t\n");
+    auto str_reg_obj =
+        pdfium::MakeRetain<CPDF_String>(nullptr, L"A simple test");
+    auto str_spec_obj = pdfium::MakeRetain<CPDF_String>(nullptr, L"\t\n");
     // Name object.
-    CPDF_Name* name_obj = new CPDF_Name(nullptr, "space");
+    auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "space");
     // Array object.
-    m_ArrayObj = new CPDF_Array;
+    m_ArrayObj = pdfium::MakeRetain<CPDF_Array>();
     m_ArrayObj->InsertNewAt<CPDF_Number>(0, 8902);
     m_ArrayObj->InsertNewAt<CPDF_Name>(1, "address");
     // Dictionary object.
-    m_DictObj = new CPDF_Dictionary();
+    m_DictObj = pdfium::MakeRetain<CPDF_Dictionary>();
     m_DictObj->SetNewFor<CPDF_Boolean>("bool", false);
     m_DictObj->SetNewFor<CPDF_Number>("num", 0.23f);
     // Stream object.
@@ -69,24 +73,25 @@
     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 pNewDict = pdfium::MakeUnique<CPDF_Dictionary>();
-    m_StreamDictObj = pNewDict.get();
+    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);
-    CPDF_Stream* stream_obj =
-        new CPDF_Stream(std::move(buf), buf_len, std::move(pNewDict));
+    auto stream_obj = pdfium::MakeRetain<CPDF_Stream>(std::move(buf), buf_len,
+                                                      std::move(pNewDict));
     // Null Object.
-    CPDF_Null* null_obj = new CPDF_Null;
+    auto null_obj = pdfium::MakeRetain<CPDF_Null>();
     // All direct objects.
-    CPDF_Object* objs[] = {boolean_false_obj, boolean_true_obj, number_int_obj,
-                           number_float_obj,  str_reg_obj,      str_spec_obj,
-                           name_obj,          m_ArrayObj.Get(), m_DictObj.Get(),
-                           stream_obj,        null_obj};
+    CPDF_Object* objs[] = {
+        boolean_false_obj.Get(), boolean_true_obj.Get(), number_int_obj.Get(),
+        number_float_obj.Get(),  str_reg_obj.Get(),      str_spec_obj.Get(),
+        name_obj.Get(),          m_ArrayObj.Get(),       m_DictObj.Get(),
+        stream_obj.Get(),        null_obj.Get()};
     m_DirectObjTypes = {
-        CPDF_Object::BOOLEAN, CPDF_Object::BOOLEAN, CPDF_Object::NUMBER,
-        CPDF_Object::NUMBER,  CPDF_Object::STRING,  CPDF_Object::STRING,
-        CPDF_Object::NAME,    CPDF_Object::ARRAY,   CPDF_Object::DICTIONARY,
-        CPDF_Object::STREAM,  CPDF_Object::NULLOBJ};
+        CPDF_Object::kBoolean, CPDF_Object::kBoolean, CPDF_Object::kNumber,
+        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)
       m_DirectObjs.emplace_back(objs[i]);
 
@@ -100,8 +105,8 @@
                       m_ObjHolder->AddIndirectObject(m_DictObj->Clone()),
                       m_ObjHolder->AddIndirectObject(stream_obj->Clone())};
     for (CPDF_Object* pObj : m_IndirectObjs) {
-      m_RefObjs.emplace_back(
-          new CPDF_Reference(m_ObjHolder.get(), pObj->GetObjNum()));
+      m_RefObjs.emplace_back(pdfium::MakeRetain<CPDF_Reference>(
+          m_ObjHolder.get(), pObj->GetObjNum()));
     }
   }
 
@@ -111,40 +116,40 @@
     if (!obj1 || !obj2 || obj1->GetType() != obj2->GetType())
       return false;
     switch (obj1->GetType()) {
-      case CPDF_Object::BOOLEAN:
+      case CPDF_Object::kBoolean:
         return obj1->GetInteger() == obj2->GetInteger();
-      case CPDF_Object::NUMBER:
+      case CPDF_Object::kNumber:
         return obj1->AsNumber()->IsInteger() == obj2->AsNumber()->IsInteger() &&
                obj1->GetInteger() == obj2->GetInteger();
-      case CPDF_Object::STRING:
-      case CPDF_Object::NAME:
+      case CPDF_Object::kString:
+      case CPDF_Object::kName:
         return obj1->GetString() == obj2->GetString();
-      case CPDF_Object::ARRAY: {
+      case CPDF_Object::kArray: {
         const CPDF_Array* array1 = obj1->AsArray();
         const CPDF_Array* array2 = obj2->AsArray();
-        if (array1->GetCount() != array2->GetCount())
+        if (array1->size() != array2->size())
           return false;
-        for (size_t i = 0; i < array1->GetCount(); ++i) {
+        for (size_t i = 0; i < array1->size(); ++i) {
           if (!Equal(array1->GetObjectAt(i), array2->GetObjectAt(i)))
             return false;
         }
         return true;
       }
-      case CPDF_Object::DICTIONARY: {
+      case CPDF_Object::kDictionary: {
         const CPDF_Dictionary* dict1 = obj1->AsDictionary();
         const CPDF_Dictionary* dict2 = obj2->AsDictionary();
-        if (dict1->GetCount() != dict2->GetCount())
+        if (dict1->size() != dict2->size())
           return false;
-        for (CPDF_Dictionary::const_iterator it = dict1->begin();
-             it != dict1->end(); ++it) {
-          if (!Equal(it->second.get(), dict2->GetObjectFor(it->first)))
+        CPDF_DictionaryLocker locker1(dict1);
+        for (const auto& item : locker1) {
+          if (!Equal(item.second.Get(), dict2->GetObjectFor(item.first)))
             return false;
         }
         return true;
       }
-      case CPDF_Object::NULLOBJ:
+      case CPDF_Object::kNullobj:
         return true;
-      case CPDF_Object::STREAM: {
+      case CPDF_Object::kStream: {
         const CPDF_Stream* stream1 = obj1->AsStream();
         const CPDF_Stream* stream2 = obj2->AsStream();
         if (!stream1->GetDict() && !stream2->GetDict())
@@ -152,18 +157,20 @@
         // Compare dictionaries.
         if (!Equal(stream1->GetDict(), stream2->GetDict()))
           return false;
+
+        auto streamAcc1 = pdfium::MakeRetain<CPDF_StreamAcc>(stream1);
+        streamAcc1->LoadAllDataRaw();
+        auto streamAcc2 = pdfium::MakeRetain<CPDF_StreamAcc>(stream2);
+        streamAcc2->LoadAllDataRaw();
+
         // Compare sizes.
-        if (stream1->GetRawSize() != stream2->GetRawSize())
+        if (streamAcc1->GetSize() != streamAcc2->GetSize())
           return false;
-        // Compare contents.
-        // Since this function is used for testing Clone(), only memory based
-        // streams need to be handled.
-        if (!stream1->IsMemoryBased() || !stream2->IsMemoryBased())
-          return false;
-        return memcmp(stream1->GetRawData(), stream2->GetRawData(),
-                      stream1->GetRawSize()) == 0;
+
+        return memcmp(streamAcc1->GetData(), streamAcc2->GetData(),
+                      streamAcc2->GetSize()) == 0;
       }
-      case CPDF_Object::REFERENCE:
+      case CPDF_Object::kReference:
         return obj1->AsReference()->GetRefObjNum() ==
                obj2->AsReference()->GetRefObjNum();
     }
@@ -174,12 +181,12 @@
   // m_ObjHolder needs to be declared first and destructed last since it also
   // refers to some objects in m_DirectObjs.
   std::unique_ptr<CPDF_IndirectObjectHolder> m_ObjHolder;
-  std::vector<std::unique_ptr<CPDF_Object>> m_DirectObjs;
+  std::vector<RetainPtr<CPDF_Object>> m_DirectObjs;
   std::vector<int> m_DirectObjTypes;
-  std::vector<std::unique_ptr<CPDF_Object>> m_RefObjs;
-  UnownedPtr<CPDF_Dictionary> m_DictObj;
-  UnownedPtr<CPDF_Dictionary> m_StreamDictObj;
-  UnownedPtr<CPDF_Array> m_ArrayObj;
+  std::vector<RetainPtr<CPDF_Object>> m_RefObjs;
+  RetainPtr<CPDF_Dictionary> m_DictObj;
+  RetainPtr<CPDF_Dictionary> m_StreamDictObj;
+  RetainPtr<CPDF_Array> m_ArrayObj;
   std::vector<CPDF_Object*> m_IndirectObjs;
 };
 
@@ -277,14 +284,14 @@
 TEST_F(PDFObjectsTest, Clone) {
   // Check for direct objects.
   for (size_t i = 0; i < m_DirectObjs.size(); ++i) {
-    std::unique_ptr<CPDF_Object> obj = m_DirectObjs[i]->Clone();
-    EXPECT_TRUE(Equal(m_DirectObjs[i].get(), obj.get()));
+    RetainPtr<CPDF_Object> obj = m_DirectObjs[i]->Clone();
+    EXPECT_TRUE(Equal(m_DirectObjs[i].Get(), obj.Get()));
   }
 
   // Check indirect references.
   for (const auto& it : m_RefObjs) {
-    std::unique_ptr<CPDF_Object> obj = it->Clone();
-    EXPECT_TRUE(Equal(it.get(), obj.get()));
+    RetainPtr<CPDF_Object> obj = it->Clone();
+    EXPECT_TRUE(Equal(it.Get(), obj.Get()));
   }
 }
 
@@ -295,13 +302,13 @@
 
   // Check indirect references.
   for (const auto& it : m_RefObjs)
-    EXPECT_EQ(CPDF_Object::REFERENCE, it->GetType());
+    EXPECT_EQ(CPDF_Object::kReference, it->GetType());
 }
 
 TEST_F(PDFObjectsTest, GetDirect) {
   // Check for direct objects.
   for (size_t i = 0; i < m_DirectObjs.size(); ++i)
-    EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->GetDirect());
+    EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->GetDirect());
 
   // Check indirect references.
   for (size_t i = 0; i < m_RefObjs.size(); ++i)
@@ -323,57 +330,57 @@
 TEST_F(PDFObjectsTest, IsTypeAndAsType) {
   // Check for direct objects.
   for (size_t i = 0; i < m_DirectObjs.size(); ++i) {
-    if (m_DirectObjTypes[i] == CPDF_Object::ARRAY) {
+    if (m_DirectObjTypes[i] == CPDF_Object::kArray) {
       EXPECT_TRUE(m_DirectObjs[i]->IsArray());
-      EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->AsArray());
+      EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsArray());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsArray());
       EXPECT_EQ(nullptr, m_DirectObjs[i]->AsArray());
     }
 
-    if (m_DirectObjTypes[i] == CPDF_Object::BOOLEAN) {
+    if (m_DirectObjTypes[i] == CPDF_Object::kBoolean) {
       EXPECT_TRUE(m_DirectObjs[i]->IsBoolean());
-      EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->AsBoolean());
+      EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsBoolean());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsBoolean());
       EXPECT_EQ(nullptr, m_DirectObjs[i]->AsBoolean());
     }
 
-    if (m_DirectObjTypes[i] == CPDF_Object::NAME) {
+    if (m_DirectObjTypes[i] == CPDF_Object::kName) {
       EXPECT_TRUE(m_DirectObjs[i]->IsName());
-      EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->AsName());
+      EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsName());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsName());
       EXPECT_EQ(nullptr, m_DirectObjs[i]->AsName());
     }
 
-    if (m_DirectObjTypes[i] == CPDF_Object::NUMBER) {
+    if (m_DirectObjTypes[i] == CPDF_Object::kNumber) {
       EXPECT_TRUE(m_DirectObjs[i]->IsNumber());
-      EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->AsNumber());
+      EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsNumber());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsNumber());
       EXPECT_EQ(nullptr, m_DirectObjs[i]->AsNumber());
     }
 
-    if (m_DirectObjTypes[i] == CPDF_Object::STRING) {
+    if (m_DirectObjTypes[i] == CPDF_Object::kString) {
       EXPECT_TRUE(m_DirectObjs[i]->IsString());
-      EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->AsString());
+      EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsString());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsString());
       EXPECT_EQ(nullptr, m_DirectObjs[i]->AsString());
     }
 
-    if (m_DirectObjTypes[i] == CPDF_Object::DICTIONARY) {
+    if (m_DirectObjTypes[i] == CPDF_Object::kDictionary) {
       EXPECT_TRUE(m_DirectObjs[i]->IsDictionary());
-      EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->AsDictionary());
+      EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsDictionary());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsDictionary());
       EXPECT_EQ(nullptr, m_DirectObjs[i]->AsDictionary());
     }
 
-    if (m_DirectObjTypes[i] == CPDF_Object::STREAM) {
+    if (m_DirectObjTypes[i] == CPDF_Object::kStream) {
       EXPECT_TRUE(m_DirectObjs[i]->IsStream());
-      EXPECT_EQ(m_DirectObjs[i].get(), m_DirectObjs[i]->AsStream());
+      EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsStream());
     } else {
       EXPECT_FALSE(m_DirectObjs[i]->IsStream());
       EXPECT_EQ(nullptr, m_DirectObjs[i]->AsStream());
@@ -385,17 +392,29 @@
   // Check indirect references.
   for (size_t i = 0; i < m_RefObjs.size(); ++i) {
     EXPECT_TRUE(m_RefObjs[i]->IsReference());
-    EXPECT_EQ(m_RefObjs[i].get(), m_RefObjs[i]->AsReference());
+    EXPECT_EQ(m_RefObjs[i].Get(), m_RefObjs[i]->AsReference());
   }
 }
 
+TEST_F(PDFObjectsTest, MakeReferenceGeneric) {
+  auto original_obj = pdfium::MakeRetain<CPDF_Null>();
+  original_obj->SetObjNum(42);
+  ASSERT_FALSE(original_obj->IsInline());
+
+  auto ref_obj = original_obj->MakeReference(m_ObjHolder.get());
+
+  ASSERT_TRUE(ref_obj->IsReference());
+  EXPECT_EQ(original_obj->GetObjNum(),
+            ToReference(ref_obj.Get())->GetRefObjNum());
+}
+
 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) {
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    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)
@@ -416,7 +435,7 @@
                       {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) {
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     CFX_FloatRect rect(elems[i]);
     for (size_t j = 0; j < 4; ++j)
       arr->AddNew<CPDF_Number>(elems[i][j]);
@@ -432,11 +451,11 @@
   {
     // Boolean array.
     const bool vals[] = {true, false, false, true, true};
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < FX_ArraySize(vals); ++i)
       arr->InsertNewAt<CPDF_Boolean>(i, vals[i]);
     for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
-      TestArrayAccessors(arr.get(), i,                // Array and index.
+      TestArrayAccessors(arr.Get(), i,                // Array and index.
                          vals[i] ? "true" : "false",  // String value.
                          nullptr,                     // Const string value.
                          vals[i] ? 1 : 0,             // Integer value.
@@ -449,12 +468,12 @@
   {
     // Integer array.
     const int vals[] = {10, 0, -345, 2089345456, -1000000000, 567, 93658767};
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < FX_ArraySize(vals); ++i)
       arr->InsertNewAt<CPDF_Number>(i, vals[i]);
     for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
       char buf[33];
-      TestArrayAccessors(arr.get(), i,                  // Array and index.
+      TestArrayAccessors(arr.Get(), i,                  // Array and index.
                          FXSYS_itoa(vals[i], buf, 10),  // String value.
                          nullptr,                       // Const string value.
                          vals[i],                       // Integer value.
@@ -470,11 +489,11 @@
                           897.34f, -2.5f, -1.0f, -345.0f, -0.0f};
     const char* const expected_str[] = {
         "0", "0", "10", "10", "0.0345", "897.34", "-2.5", "-1", "-345", "0"};
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < FX_ArraySize(vals); ++i)
       arr->InsertNewAt<CPDF_Number>(i, vals[i]);
     for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
-      TestArrayAccessors(arr.get(), i,     // Array and index.
+      TestArrayAccessors(arr.Get(), i,     // Array and index.
                          expected_str[i],  // String value.
                          nullptr,          // Const string value.
                          vals[i],          // Integer value.
@@ -488,14 +507,14 @@
     // String and name array
     const char* const vals[] = {"this", "adsde$%^", "\r\t",           "\"012",
                                 ".",    "EYREW",    "It is a joke :)"};
-    auto string_array = pdfium::MakeUnique<CPDF_Array>();
-    auto name_array = pdfium::MakeUnique<CPDF_Array>();
+    auto string_array = pdfium::MakeRetain<CPDF_Array>();
+    auto name_array = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < FX_ArraySize(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) {
-      TestArrayAccessors(string_array.get(), i,  // Array and index.
+      TestArrayAccessors(string_array.Get(), i,  // Array and index.
                          vals[i],                // String value.
                          vals[i],                // Const string value.
                          0,                      // Integer value.
@@ -503,7 +522,7 @@
                          nullptr,                // Array value.
                          nullptr,                // Dictionary value.
                          nullptr);               // Stream value.
-      TestArrayAccessors(name_array.get(), i,    // Array and index.
+      TestArrayAccessors(name_array.Get(), i,    // Array and index.
                          vals[i],                // String value.
                          vals[i],                // Const string value.
                          0,                      // Integer value.
@@ -515,11 +534,11 @@
   }
   {
     // Null element array.
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 3; ++i)
       arr->InsertNewAt<CPDF_Null>(i);
     for (size_t i = 0; i < 3; ++i) {
-      TestArrayAccessors(arr.get(), i,  // Array and index.
+      TestArrayAccessors(arr.Get(), i,  // Array and index.
                          "",            // String value.
                          nullptr,       // Const string value.
                          0,             // Integer value.
@@ -532,7 +551,7 @@
   {
     // Array of array.
     CPDF_Array* vals[3];
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 3; ++i) {
       vals[i] = arr->AddNew<CPDF_Array>();
       for (size_t j = 0; j < 3; ++j) {
@@ -541,7 +560,7 @@
       }
     }
     for (size_t i = 0; i < 3; ++i) {
-      TestArrayAccessors(arr.get(), i,  // Array and index.
+      TestArrayAccessors(arr.Get(), i,  // Array and index.
                          "",            // String value.
                          nullptr,       // Const string value.
                          0,             // Integer value.
@@ -554,7 +573,7 @@
   {
     // Dictionary array.
     CPDF_Dictionary* vals[3];
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 3; ++i) {
       vals[i] = arr->AddNew<CPDF_Dictionary>();
       for (size_t j = 0; j < 3; ++j) {
@@ -566,7 +585,7 @@
       }
     }
     for (size_t i = 0; i < 3; ++i) {
-      TestArrayAccessors(arr.get(), i,  // Array and index.
+      TestArrayAccessors(arr.Get(), i,  // Array and index.
                          "",            // String value.
                          nullptr,       // Const string value.
                          0,             // Integer value.
@@ -578,11 +597,11 @@
   }
   {
     // Stream array.
-    CPDF_Dictionary* vals[3];
+    RetainPtr<CPDF_Dictionary> vals[3];
     CPDF_Stream* stream_vals[3];
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     for (size_t i = 0; i < 3; ++i) {
-      vals[i] = new CPDF_Dictionary();
+      vals[i] = pdfium::MakeRetain<CPDF_Dictionary>();
       for (size_t j = 0; j < 3; ++j) {
         std::string key("key");
         char buf[33];
@@ -595,23 +614,23 @@
       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,
-                                                pdfium::WrapUnique(vals[i]));
+      stream_vals[i] =
+          arr->AddNew<CPDF_Stream>(std::move(data), data_size, vals[i]);
     }
     for (size_t i = 0; i < 3; ++i) {
-      TestArrayAccessors(arr.get(), i,     // Array and index.
+      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.
+                         vals[i].Get(),    // Dictionary value.
                          stream_vals[i]);  // Stream value.
     }
   }
   {
     // Mixed array.
-    auto arr = pdfium::MakeUnique<CPDF_Array>();
+    auto arr = pdfium::MakeRetain<CPDF_Array>();
     arr->InsertNewAt<CPDF_Boolean>(0, true);
     arr->InsertNewAt<CPDF_Boolean>(1, false);
     arr->InsertNewAt<CPDF_Number>(2, 0);
@@ -632,7 +651,7 @@
     dict_val->SetNewFor<CPDF_String>("key1", "Linda", false);
     dict_val->SetNewFor<CPDF_String>("key2", "Zoe", false);
 
-    CPDF_Dictionary* stream_dict = new CPDF_Dictionary();
+    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";
@@ -642,7 +661,7 @@
     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, pdfium::WrapUnique(stream_dict));
+        13, std::move(buf), buf_size, stream_dict);
     const char* const expected_str[] = {
         "true",          "false", "0",    "-1234", "2345", "0.05", "",
         "It is a test!", "NAME",  "test", "",      "",     "",     ""};
@@ -650,11 +669,10 @@
                                 0, 0, 0, 0,     0,    0, 0};
     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->GetCount(); ++i) {
+    for (size_t i = 0; i < arr->size(); ++i) {
       EXPECT_STREQ(expected_str[i], arr->GetStringAt(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
@@ -676,62 +694,62 @@
 TEST(PDFArrayTest, AddNumber) {
   float vals[] = {1.0f,         -1.0f, 0,    0.456734f,
                   12345.54321f, 0.5f,  1000, 0.000045f};
-  auto arr = pdfium::MakeUnique<CPDF_Array>();
+  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) {
-    EXPECT_EQ(CPDF_Object::NUMBER, arr->GetObjectAt(i)->GetType());
+    EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType());
     EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber());
   }
 }
 
 TEST(PDFArrayTest, AddInteger) {
   int vals[] = {0, 1, 934435456, 876, 10000, -1, -24354656, -100};
-  auto arr = pdfium::MakeUnique<CPDF_Array>();
+  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) {
-    EXPECT_EQ(CPDF_Object::NUMBER, arr->GetObjectAt(i)->GetType());
+    EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType());
     EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber());
   }
 }
 
 TEST(PDFArrayTest, AddStringAndName) {
-  static constexpr const char* vals[] = {
+  static constexpr const char* kVals[] = {
       "",        "a", "ehjhRIOYTTFdfcdnv",  "122323",
       "$#%^&**", " ", "This is a test.\r\n"};
-  auto string_array = pdfium::MakeUnique<CPDF_Array>();
-  auto name_array = pdfium::MakeUnique<CPDF_Array>();
-  for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
-    string_array->AddNew<CPDF_String>(vals[i], false);
-    name_array->AddNew<CPDF_Name>(vals[i]);
+  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);
   }
-  for (size_t i = 0; i < FX_ArraySize(vals); ++i) {
-    EXPECT_EQ(CPDF_Object::STRING, string_array->GetObjectAt(i)->GetType());
-    EXPECT_STREQ(vals[i], string_array->GetObjectAt(i)->GetString().c_str());
-    EXPECT_EQ(CPDF_Object::NAME, name_array->GetObjectAt(i)->GetType());
-    EXPECT_STREQ(vals[i], name_array->GetObjectAt(i)->GetString().c_str());
+  for (size_t i = 0; i < FX_ArraySize(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());
+    EXPECT_STREQ(kVals[i], name_array->GetObjectAt(i)->GetString().c_str());
   }
 }
 
 TEST(PDFArrayTest, AddReferenceAndGetObjectAt) {
   auto holder = pdfium::MakeUnique<CPDF_IndirectObjectHolder>();
-  CPDF_Boolean* boolean_obj = new CPDF_Boolean(true);
-  CPDF_Number* int_obj = new CPDF_Number(-1234);
-  CPDF_Number* float_obj = new CPDF_Number(2345.089f);
-  CPDF_String* str_obj =
-      new CPDF_String(nullptr, "Adsfdsf 343434 %&&*\n", false);
-  CPDF_Name* name_obj = new CPDF_Name(nullptr, "Title:");
-  CPDF_Null* null_obj = new CPDF_Null();
-  CPDF_Object* indirect_objs[] = {boolean_obj, int_obj,  float_obj,
-                                  str_obj,     name_obj, null_obj};
+  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);
+  auto str_obj =
+      pdfium::MakeRetain<CPDF_String>(nullptr, "Adsfdsf 343434 %&&*\n", false);
+  auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "Title:");
+  auto null_obj = pdfium::MakeRetain<CPDF_Null>();
+  RetainPtr<CPDF_Object> indirect_objs[] = {boolean_obj, int_obj,  float_obj,
+                                            str_obj,     name_obj, null_obj};
   unsigned int obj_nums[] = {2, 4, 7, 2345, 799887, 1};
-  auto arr = pdfium::MakeUnique<CPDF_Array>();
-  auto arr1 = pdfium::MakeUnique<CPDF_Array>();
+  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) {
-    holder->ReplaceIndirectObjectIfHigherGeneration(
-        obj_nums[i], pdfium::WrapUnique<CPDF_Object>(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());
   }
@@ -739,12 +757,12 @@
   for (size_t i = 0; i < FX_ArraySize(obj_nums); ++i)
     EXPECT_EQ(indirect_objs[i], holder->GetOrParseIndirectObject(obj_nums[i]));
   // Check arrays.
-  EXPECT_EQ(arr->GetCount(), arr1->GetCount());
-  for (size_t i = 0; i < arr->GetCount(); ++i) {
-    EXPECT_EQ(CPDF_Object::REFERENCE, arr->GetObjectAt(i)->GetType());
+  EXPECT_EQ(arr->size(), arr1->size());
+  for (size_t i = 0; i < arr->size(); ++i) {
+    EXPECT_EQ(CPDF_Object::kReference, arr->GetObjectAt(i)->GetType());
     EXPECT_EQ(indirect_objs[i], arr->GetObjectAt(i)->GetDirect());
     EXPECT_EQ(indirect_objs[i], arr->GetDirectObjectAt(i));
-    EXPECT_EQ(CPDF_Object::REFERENCE, arr1->GetObjectAt(i)->GetType());
+    EXPECT_EQ(CPDF_Object::kReference, arr1->GetObjectAt(i)->GetType());
     EXPECT_EQ(indirect_objs[i], arr1->GetObjectAt(i)->GetDirect());
     EXPECT_EQ(indirect_objs[i], arr1->GetDirectObjectAt(i));
   }
@@ -752,27 +770,26 @@
 
 TEST(PDFArrayTest, CloneDirectObject) {
   CPDF_IndirectObjectHolder objects_holder;
-  auto array = pdfium::MakeUnique<CPDF_Array>();
+  auto array = pdfium::MakeRetain<CPDF_Array>();
   array->AddNew<CPDF_Reference>(&objects_holder, 1234);
-  ASSERT_EQ(1U, array->GetCount());
+  ASSERT_EQ(1U, array->size());
   CPDF_Object* obj = array->GetObjectAt(0);
   ASSERT_TRUE(obj);
   EXPECT_TRUE(obj->IsReference());
 
-  std::unique_ptr<CPDF_Object> cloned_array_object = array->CloneDirectObject();
+  RetainPtr<CPDF_Object> cloned_array_object = array->CloneDirectObject();
   ASSERT_TRUE(cloned_array_object);
   ASSERT_TRUE(cloned_array_object->IsArray());
 
-  std::unique_ptr<CPDF_Array> cloned_array =
-      ToArray(std::move(cloned_array_object));
-  ASSERT_EQ(0U, cloned_array->GetCount());
+  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);
   EXPECT_FALSE(cloned_obj);
 }
 
 TEST(PDFArrayTest, ConvertIndirect) {
   CPDF_IndirectObjectHolder objects_holder;
-  auto array = pdfium::MakeUnique<CPDF_Array>();
+  auto array = pdfium::MakeRetain<CPDF_Array>();
   CPDF_Object* pObj = array->AddNew<CPDF_Number>(42);
   array->ConvertToIndirectObjectAt(0, &objects_holder);
   CPDF_Object* pRef = array->GetObjectAt(0);
@@ -786,47 +803,51 @@
 
 TEST(PDFStreamTest, SetData) {
   std::vector<uint8_t> data(100);
-  auto stream = pdfium::MakeUnique<CPDF_Stream>();
-  stream->InitStream(data.data(), data.size(),
-                     pdfium::MakeUnique<CPDF_Dictionary>());
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->InitStream(data, pdfium::MakeRetain<CPDF_Dictionary>());
   EXPECT_EQ(static_cast<int>(data.size()),
-            stream->GetDict()->GetIntegerFor("Length"));
+            stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
 
-  stream->GetDict()->SetNewFor<CPDF_String>("Filter", L"SomeFilter");
-  stream->GetDict()->SetNewFor<CPDF_String>("DecodeParms", L"SomeParams");
+  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
+                                            L"SomeFilter");
+  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
+                                            L"SomeParams");
 
   std::vector<uint8_t> new_data(data.size() * 2);
-  stream->SetData(new_data.data(), new_data.size());
+  stream->SetData(new_data);
 
   // The "Length" field should be updated for new data size.
   EXPECT_EQ(static_cast<int>(new_data.size()),
-            stream->GetDict()->GetIntegerFor("Length"));
+            stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
 
   // The "Filter" and "DecodeParms" fields should not be changed.
-  EXPECT_EQ(stream->GetDict()->GetUnicodeTextFor("Filter"), L"SomeFilter");
-  EXPECT_EQ(stream->GetDict()->GetUnicodeTextFor("DecodeParms"), L"SomeParams");
+  EXPECT_EQ(stream->GetDict()->GetUnicodeTextFor(pdfium::stream::kFilter),
+            L"SomeFilter");
+  EXPECT_EQ(stream->GetDict()->GetUnicodeTextFor(pdfium::stream::kDecodeParms),
+            L"SomeParams");
 }
 
 TEST(PDFStreamTest, SetDataAndRemoveFilter) {
   std::vector<uint8_t> data(100);
-  auto stream = pdfium::MakeUnique<CPDF_Stream>();
-  stream->InitStream(data.data(), data.size(),
-                     pdfium::MakeUnique<CPDF_Dictionary>());
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->InitStream(data, pdfium::MakeRetain<CPDF_Dictionary>());
   EXPECT_EQ(static_cast<int>(data.size()),
-            stream->GetDict()->GetIntegerFor("Length"));
+            stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
 
-  stream->GetDict()->SetNewFor<CPDF_String>("Filter", L"SomeFilter");
-  stream->GetDict()->SetNewFor<CPDF_String>("DecodeParms", L"SomeParams");
+  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
+                                            L"SomeFilter");
+  stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
+                                            L"SomeParams");
 
   std::vector<uint8_t> new_data(data.size() * 2);
-  stream->SetDataAndRemoveFilter(new_data.data(), new_data.size());
+  stream->SetDataAndRemoveFilter(new_data);
   // The "Length" field should be updated for new data size.
   EXPECT_EQ(static_cast<int>(new_data.size()),
-            stream->GetDict()->GetIntegerFor("Length"));
+            stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
 
   // The "Filter" and "DecodeParms" should be removed.
-  EXPECT_FALSE(stream->GetDict()->KeyExist("Filter"));
-  EXPECT_FALSE(stream->GetDict()->KeyExist("DecodeParms"));
+  EXPECT_FALSE(stream->GetDict()->KeyExist(pdfium::stream::kFilter));
+  EXPECT_FALSE(stream->GetDict()->KeyExist(pdfium::stream::kDecodeParms));
 }
 
 TEST(PDFStreamTest, LengthInDictionaryOnCreate) {
@@ -835,72 +856,69 @@
   {
     std::unique_ptr<uint8_t, FxFreeDeleter> data;
     data.reset(FX_Alloc(uint8_t, kBufSize));
-    auto stream = pdfium::MakeUnique<CPDF_Stream>(
-        std::move(data), kBufSize, pdfium::MakeUnique<CPDF_Dictionary>());
+    auto stream = pdfium::MakeRetain<CPDF_Stream>(
+        std::move(data), kBufSize, pdfium::MakeRetain<CPDF_Dictionary>());
     EXPECT_EQ(static_cast<int>(kBufSize),
-              stream->GetDict()->GetIntegerFor("Length"));
+              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::MakeUnique<CPDF_Dictionary>();
-    dict->SetNewFor<CPDF_Number>("Length", 30000);
-    auto stream = pdfium::MakeUnique<CPDF_Stream>(std::move(data), 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,
                                                   std::move(dict));
     EXPECT_EQ(static_cast<int>(kBufSize),
-              stream->GetDict()->GetIntegerFor("Length"));
+              stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
   }
 }
 
 TEST(PDFDictionaryTest, CloneDirectObject) {
   CPDF_IndirectObjectHolder objects_holder;
-  auto dict = pdfium::MakeUnique<CPDF_Dictionary>();
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
   dict->SetNewFor<CPDF_Reference>("foo", &objects_holder, 1234);
-  ASSERT_EQ(1U, dict->GetCount());
+  ASSERT_EQ(1U, dict->size());
   CPDF_Object* obj = dict->GetObjectFor("foo");
   ASSERT_TRUE(obj);
   EXPECT_TRUE(obj->IsReference());
 
-  std::unique_ptr<CPDF_Object> cloned_dict_object = dict->CloneDirectObject();
+  RetainPtr<CPDF_Object> cloned_dict_object = dict->CloneDirectObject();
   ASSERT_TRUE(cloned_dict_object);
   ASSERT_TRUE(cloned_dict_object->IsDictionary());
 
-  std::unique_ptr<CPDF_Dictionary> cloned_dict =
+  RetainPtr<CPDF_Dictionary> cloned_dict =
       ToDictionary(std::move(cloned_dict_object));
-  ASSERT_EQ(0U, cloned_dict->GetCount());
+  ASSERT_EQ(0U, cloned_dict->size());
   CPDF_Object* cloned_obj = cloned_dict->GetObjectFor("foo");
   EXPECT_FALSE(cloned_obj);
 }
 
 TEST(PDFObjectTest, CloneCheckLoop) {
   {
-    // Create a dictionary/array pair with a reference loop. It takes
-    // some work to do this nowadays, in particular we need the
-    // anti-pattern pdfium::WrapUnique(arr.get()).
-    auto arr_obj = pdfium::MakeUnique<CPDF_Array>();
+    // 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);
-    dict_obj->SetFor("arr", pdfium::WrapUnique(arr_obj.get()));
+    dict_obj->SetFor("arr", arr_obj);
     // Clone this object to see whether stack overflow will be triggered.
-    std::unique_ptr<CPDF_Array> cloned_array = ToArray(arr_obj->Clone());
+    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->GetCount());
+    EXPECT_EQ(1u, cloned_array->size());
     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"));
+    dict_obj->RemoveFor("arr");  // Break deliberate cycle for cleanup.
   }
   {
-    // Create a dictionary/stream pair with a reference loop. It takes
-    // some work to do this nowadays, in particular we need the
-    // anti-pattern pdfium::WrapUnique(dict.get()).
-    auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
-    CPDF_Stream* stream_obj = dict_obj->SetNewFor<CPDF_Stream>(
-        "stream", nullptr, 0, pdfium::WrapUnique(dict_obj.get()));
+    // 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);
     // Clone this object to see whether stack overflow will be triggered.
-    std::unique_ptr<CPDF_Stream> cloned_stream = ToStream(stream_obj->Clone());
+    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();
@@ -908,12 +926,13 @@
     ASSERT_TRUE(cloned_dict->IsDictionary());
     // Recursively referenced object is not cloned.
     EXPECT_EQ(nullptr, 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>();
-    std::unique_ptr<CPDF_Array> arr_obj = pdfium::MakeUnique<CPDF_Array>();
+    RetainPtr<CPDF_Array> arr_obj = pdfium::MakeRetain<CPDF_Array>();
     arr_obj->InsertNewAt<CPDF_Reference>(0, &objects_holder,
                                          dict_obj->GetObjNum());
     CPDF_Object* elem0 = arr_obj->GetObjectAt(0);
@@ -925,22 +944,23 @@
     EXPECT_EQ(dict_obj, elem0->AsReference()->GetDirect());
 
     // Clone this object to see whether stack overflow will be triggered.
-    std::unique_ptr<CPDF_Dictionary> cloned_dict =
+    RetainPtr<CPDF_Dictionary> cloned_dict =
         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");
     ASSERT_TRUE(cloned_arr);
     ASSERT_TRUE(cloned_arr->IsArray());
-    EXPECT_EQ(0U, cloned_arr->AsArray()->GetCount());
+    EXPECT_EQ(0U, cloned_arr->AsArray()->size());
     // Recursively referenced object is not cloned.
     EXPECT_EQ(nullptr, cloned_arr->AsArray()->GetObjectAt(0));
+    dict_obj->RemoveFor("arr");  // Break deliberate cycle for cleanup.
   }
 }
 
 TEST(PDFDictionaryTest, ConvertIndirect) {
   CPDF_IndirectObjectHolder objects_holder;
-  auto dict = pdfium::MakeUnique<CPDF_Dictionary>();
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
   CPDF_Object* pObj = dict->SetNewFor<CPDF_Number>("clams", 42);
   dict->ConvertToIndirectObjectFor("clams", &objects_holder);
   CPDF_Object* pRef = dict->GetObjectFor("clams");
@@ -953,11 +973,26 @@
 }
 
 TEST(PDFDictionaryTest, ExtractObjectOnRemove) {
-  auto dict = pdfium::MakeUnique<CPDF_Dictionary>();
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
   CPDF_Object* pObj = dict->SetNewFor<CPDF_Number>("child", 42);
   auto extracted_object = dict->RemoveFor("child");
-  EXPECT_EQ(pObj, extracted_object.get());
+  EXPECT_EQ(pObj, extracted_object.Get());
 
   extracted_object = dict->RemoveFor("non_exists_object");
   EXPECT_FALSE(extracted_object);
 }
+
+TEST(PDFRefernceTest, MakeReferenceToReference) {
+  auto obj_holder = pdfium::MakeUnique<CPDF_IndirectObjectHolder>();
+  auto original_ref = pdfium::MakeRetain<CPDF_Reference>(obj_holder.get(), 42);
+  original_ref->SetObjNum(1952);
+  ASSERT_FALSE(original_ref->IsInline());
+
+  auto ref_obj = original_ref->MakeReference(obj_holder.get());
+
+  ASSERT_TRUE(ref_obj->IsReference());
+  // We do not allow reference to reference.
+  // New reference should have same RefObjNum.
+  EXPECT_EQ(original_ref->GetRefObjNum(),
+            ToReference(ref_obj.Get())->GetRefObjNum());
+}
diff --git a/core/fpdfapi/parser/cpdf_object_walker.cpp b/core/fpdfapi/parser/cpdf_object_walker.cpp
index cb59a05..efd4a5c 100644
--- a/core/fpdfapi/parser/cpdf_object_walker.cpp
+++ b/core/fpdfapi/parser/cpdf_object_walker.cpp
@@ -9,10 +9,11 @@
 #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"
 
 namespace {
 
-class StreamIterator : public CPDF_ObjectWalker::SubobjectIterator {
+class StreamIterator final : public CPDF_ObjectWalker::SubobjectIterator {
  public:
   explicit StreamIterator(const CPDF_Stream* stream)
       : SubobjectIterator(stream) {}
@@ -33,20 +34,20 @@
   bool is_finished_ = false;
 };
 
-class DictionaryIterator : public CPDF_ObjectWalker::SubobjectIterator {
+class DictionaryIterator final : public CPDF_ObjectWalker::SubobjectIterator {
  public:
   explicit DictionaryIterator(const CPDF_Dictionary* dictionary)
-      : SubobjectIterator(dictionary) {}
+      : SubobjectIterator(dictionary), locker_(dictionary) {}
   ~DictionaryIterator() override {}
 
   bool IsFinished() const override {
-    return IsStarted() && dict_iterator_ == object()->GetDict()->end();
+    return IsStarted() && dict_iterator_ == locker_.end();
   }
 
   const CPDF_Object* IncrementImpl() override {
     ASSERT(IsStarted());
     ASSERT(!IsFinished());
-    const CPDF_Object* result = dict_iterator_->second.get();
+    const CPDF_Object* result = dict_iterator_->second.Get();
     dict_key_ = dict_iterator_->first;
     ++dict_iterator_;
     return result;
@@ -54,38 +55,41 @@
 
   void Start() override {
     ASSERT(!IsStarted());
-    dict_iterator_ = object()->GetDict()->begin();
+    dict_iterator_ = locker_.begin();
   }
 
-  const ByteString& dict_key() const { return dict_key_; }
+  ByteString dict_key() const { return dict_key_; }
 
  private:
   CPDF_Dictionary::const_iterator dict_iterator_;
+  CPDF_DictionaryLocker locker_;
   ByteString dict_key_;
 };
 
-class ArrayIterator : public CPDF_ObjectWalker::SubobjectIterator {
+class ArrayIterator final : public CPDF_ObjectWalker::SubobjectIterator {
  public:
-  explicit ArrayIterator(const CPDF_Array* array) : SubobjectIterator(array) {}
+  explicit ArrayIterator(const CPDF_Array* array)
+      : SubobjectIterator(array), locker_(array) {}
 
   ~ArrayIterator() override {}
 
   bool IsFinished() const override {
-    return IsStarted() && arr_iterator_ == object()->AsArray()->end();
+    return IsStarted() && arr_iterator_ == locker_.end();
   }
 
   const CPDF_Object* IncrementImpl() override {
     ASSERT(IsStarted());
     ASSERT(!IsFinished());
-    const CPDF_Object* result = arr_iterator_->get();
+    const CPDF_Object* result = arr_iterator_->Get();
     ++arr_iterator_;
     return result;
   }
 
-  void Start() override { arr_iterator_ = object()->AsArray()->begin(); }
+  void Start() override { arr_iterator_ = locker_.begin(); }
 
  public:
   CPDF_Array::const_iterator arr_iterator_;
+  CPDF_ArrayLocker locker_;
 };
 
 }  // namespace
@@ -124,19 +128,19 @@
 }
 
 CPDF_ObjectWalker::CPDF_ObjectWalker(const CPDF_Object* root)
-    : next_object_(root), parent_object_(nullptr), current_depth_(0) {}
+    : next_object_(root) {}
 
-CPDF_ObjectWalker::~CPDF_ObjectWalker() {}
+CPDF_ObjectWalker::~CPDF_ObjectWalker() = default;
 
 const CPDF_Object* CPDF_ObjectWalker::GetNext() {
   while (!stack_.empty() || next_object_) {
     if (next_object_) {
-      auto new_iterator = MakeIterator(next_object_);
+      auto new_iterator = MakeIterator(next_object_.Get());
       if (new_iterator) {
         // Schedule walk within composite objects.
         stack_.push(std::move(new_iterator));
       }
-      auto* result = next_object_;
+      auto* result = next_object_.Get();
       next_object_ = nullptr;
       return result;
     }
@@ -145,8 +149,8 @@
     if (it->IsFinished()) {
       stack_.pop();
     } else {
-      next_object_ = it->Increment();
-      parent_object_ = it->object();
+      next_object_.Reset(it->Increment());
+      parent_object_.Reset(it->object());
       dict_key_ = parent_object_->IsDictionary()
                       ? static_cast<DictionaryIterator*>(it)->dict_key()
                       : ByteString();
diff --git a/core/fpdfapi/parser/cpdf_object_walker.h b/core/fpdfapi/parser/cpdf_object_walker.h
index 8cad3c3..d019286 100644
--- a/core/fpdfapi/parser/cpdf_object_walker.h
+++ b/core/fpdfapi/parser/cpdf_object_walker.h
@@ -8,7 +8,10 @@
 #include <memory>
 #include <stack>
 
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_Object;
 
 // Walk on all non-null sub-objects in an object in depth, include itself,
 // like in flat list.
@@ -17,10 +20,10 @@
   class SubobjectIterator {
    public:
     virtual ~SubobjectIterator();
+    virtual bool IsFinished() const = 0;
     bool IsStarted() const { return is_started_; }
-    bool virtual IsFinished() const = 0;
     const CPDF_Object* Increment();
-    const CPDF_Object* object() const { return object_; }
+    const CPDF_Object* object() const { return object_.Get(); }
 
    protected:
     explicit SubobjectIterator(const CPDF_Object* object);
@@ -29,7 +32,7 @@
     virtual void Start() = 0;
 
    private:
-    const CPDF_Object* object_;
+    RetainPtr<const CPDF_Object> object_;
     bool is_started_ = false;
   };
 
@@ -40,23 +43,21 @@
   void SkipWalkIntoCurrentObject();
 
   size_t current_depth() const { return current_depth_; }
-  const CPDF_Object* GetParent() const { return parent_object_; }
+  const CPDF_Object* GetParent() const { return parent_object_.Get(); }
   const ByteString& dictionary_key() const { return dict_key_; }
 
  private:
   static std::unique_ptr<SubobjectIterator> MakeIterator(
       const CPDF_Object* object);
 
-  const CPDF_Object* next_object_;
-  const CPDF_Object* parent_object_;
-
+  RetainPtr<const CPDF_Object> next_object_;
+  RetainPtr<const CPDF_Object> parent_object_;
   ByteString dict_key_;
-  size_t current_depth_;
-
+  size_t current_depth_ = 0;
   std::stack<std::unique_ptr<SubobjectIterator>> stack_;
 };
 
-class CPDF_NonConstObjectWalker : public CPDF_ObjectWalker {
+class CPDF_NonConstObjectWalker final : public CPDF_ObjectWalker {
  public:
   explicit CPDF_NonConstObjectWalker(CPDF_Object* root)
       : CPDF_ObjectWalker(root) {}
diff --git a/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp b/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp
index c5ffe84..4dde72f 100644
--- a/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp
@@ -55,45 +55,45 @@
 }  // namespace
 
 TEST(CPDF_ObjectWalkerTest, Simple) {
-  EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Null>().get()), "Null");
-  EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Dictionary>().get()), "Dict");
-  EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Array>().get()), "Arr");
-  EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_String>().get()), "Str");
-  EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Boolean>().get()), "Bool");
-  EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Stream>().get()), "Stream");
-  EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Reference>(nullptr, 0).get()), "Ref");
+  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(CPDF_ObjectWalkerTest, CombinedObject) {
-  auto dict = pdfium::MakeUnique<CPDF_Dictionary>();
-  dict->SetFor("1", pdfium::MakeUnique<CPDF_String>());
-  dict->SetFor("2", pdfium::MakeUnique<CPDF_Boolean>());
-  auto array = pdfium::MakeUnique<CPDF_Array>();
-  array->Add(pdfium::MakeUnique<CPDF_Reference>(nullptr, 0));
-  array->Add(pdfium::MakeUnique<CPDF_Null>());
-  array->Add(pdfium::MakeUnique<CPDF_Stream>(
-      nullptr, 0, pdfium::MakeUnique<CPDF_Dictionary>()));
+  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>()));
   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.Get()), "Dict Str Bool Arr Ref Null Stream Dict Num");
 }
 
 TEST(CPDF_ObjectWalkerTest, GetParent) {
-  auto level_4 = pdfium::MakeUnique<CPDF_Number>(0);
-  auto level_3 = pdfium::MakeUnique<CPDF_Dictionary>();
+  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::MakeUnique<CPDF_Stream>(nullptr, 0, std::move(level_3));
-  auto level_1 = pdfium::MakeUnique<CPDF_Array>();
+      pdfium::MakeRetain<CPDF_Stream>(nullptr, 0, std::move(level_3));
+  auto level_1 = pdfium::MakeRetain<CPDF_Array>();
   level_1->Add(std::move(level_2));
-  auto level_0 = pdfium::MakeUnique<CPDF_Dictionary>();
+  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());
+  CPDF_ObjectWalker walker(level_0.Get());
   while (const CPDF_Object* obj = walker.GetNext()) {
     EXPECT_EQ(cur_parent, walker.GetParent());
     cur_parent = obj;
@@ -101,7 +101,7 @@
 }
 
 TEST(CPDF_ObjectWalkerTest, SkipWalkIntoCurrentObject) {
-  auto root_array = pdfium::MakeUnique<CPDF_Array>();
+  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>();
@@ -110,9 +110,9 @@
   root_array->Add(root_array->Clone());
 
   int non_array_objects = 0;
-  CPDF_ObjectWalker walker(root_array.get());
+  CPDF_ObjectWalker walker(root_array.Get());
   while (const CPDF_Object* obj = walker.GetNext()) {
-    if (obj != root_array.get() && obj->IsArray()) {
+    if (obj != root_array && obj->IsArray()) {
       // skip other array except root.
       walker.SkipWalkIntoCurrentObject();
     }
@@ -124,16 +124,16 @@
 }
 
 TEST(CPDF_ObjectWalkerTest, DictionaryKey) {
-  auto dict = pdfium::MakeUnique<CPDF_Dictionary>();
-  dict->SetFor("1", pdfium::MakeUnique<CPDF_Null>());
-  dict->SetFor("2", pdfium::MakeUnique<CPDF_Null>());
-  dict->SetFor("3", pdfium::MakeUnique<CPDF_Null>());
-  dict->SetFor("4", pdfium::MakeUnique<CPDF_Null>());
-  dict->SetFor("5", pdfium::MakeUnique<CPDF_Null>());
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  dict->SetFor("1", pdfium::MakeRetain<CPDF_Null>());
+  dict->SetFor("2", pdfium::MakeRetain<CPDF_Null>());
+  dict->SetFor("3", pdfium::MakeRetain<CPDF_Null>());
+  dict->SetFor("4", pdfium::MakeRetain<CPDF_Null>());
+  dict->SetFor("5", pdfium::MakeRetain<CPDF_Null>());
 
-  CPDF_ObjectWalker walker(dict.get());
+  CPDF_ObjectWalker walker(dict.Get());
   while (const CPDF_Object* obj = walker.GetNext()) {
-    if (obj == dict.get()) {
+    if (dict == obj) {
       // Ignore root dictinary object
       continue;
     }
diff --git a/core/fpdfapi/parser/cpdf_page_object_avail.h b/core/fpdfapi/parser/cpdf_page_object_avail.h
index 8c740ed..bb9e80a 100644
--- a/core/fpdfapi/parser/cpdf_page_object_avail.h
+++ b/core/fpdfapi/parser/cpdf_page_object_avail.h
@@ -9,12 +9,12 @@
 
 // Helper for check availability of page's object tree.
 // Exclude references to pages.
-class CPDF_PageObjectAvail : public CPDF_ObjectAvail {
+class CPDF_PageObjectAvail final : public CPDF_ObjectAvail {
  public:
   using CPDF_ObjectAvail::CPDF_ObjectAvail;
   ~CPDF_PageObjectAvail() override;
 
- protected:
+ private:
   bool ExcludeObject(const CPDF_Object* object) const override;
 };
 
diff --git a/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp
index 14ba0e3..6a76118 100644
--- a/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp
@@ -15,29 +15,27 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/fx_stream.h"
-#include "testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/invalid_seekable_read_stream.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-
-class TestReadValidator : public CPDF_ReadValidator {
+class TestReadValidator final : public CPDF_ReadValidator {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  void SimulateReadError() { ReadBlock(nullptr, 0, 1); }
+  void SimulateReadError() { ReadBlockAtOffset(nullptr, 0, 1); }
 
- protected:
+ private:
   TestReadValidator()
-      : CPDF_ReadValidator(
-            pdfium::MakeRetain<CFX_InvalidSeekableReadStream>(100),
-            nullptr) {}
+      : CPDF_ReadValidator(pdfium::MakeRetain<InvalidSeekableReadStream>(100),
+                           nullptr) {}
   ~TestReadValidator() override {}
 };
 
-class TestHolder : public CPDF_IndirectObjectHolder {
+class TestHolder final : public CPDF_IndirectObjectHolder {
  public:
   enum class ObjectState {
     Unavailable,
@@ -57,13 +55,13 @@
       validator_->SimulateReadError();
       return nullptr;
     }
-    return obj_data.object.get();
+    return obj_data.object.Get();
   }
 
   RetainPtr<CPDF_ReadValidator> GetValidator() { return validator_; }
 
   void AddObject(uint32_t objnum,
-                 std::unique_ptr<CPDF_Object> object,
+                 RetainPtr<CPDF_Object> object,
                  ObjectState state) {
     ObjectData object_data;
     object_data.object = std::move(object);
@@ -83,12 +81,12 @@
     auto it = objects_data_.find(objnum);
     if (it == objects_data_.end())
       return nullptr;
-    return it->second.object.get();
+    return it->second.object.Get();
   }
 
  private:
   struct ObjectData {
-    std::unique_ptr<CPDF_Object> object;
+    RetainPtr<CPDF_Object> object;
     ObjectState state = ObjectState::Unavailable;
   };
   std::map<uint32_t, ObjectData> objects_data_;
@@ -99,26 +97,26 @@
 
 TEST(CPDF_PageObjectAvailTest, ExcludePages) {
   TestHolder holder;
-  holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(1, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
   holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("Kids", &holder,
                                                                 2);
-  holder.AddObject(2, pdfium::MakeUnique<CPDF_Array>(),
+  holder.AddObject(2, pdfium::MakeRetain<CPDF_Array>(),
                    TestHolder::ObjectState::Available);
   holder.GetTestObject(2)->AsArray()->AddNew<CPDF_Reference>(&holder, 3);
 
-  holder.AddObject(3, pdfium::MakeUnique<CPDF_Dictionary>(),
+  holder.AddObject(3, pdfium::MakeRetain<CPDF_Dictionary>(),
                    TestHolder::ObjectState::Available);
   holder.GetTestObject(3)->GetDict()->SetFor(
-      "Type", pdfium::MakeUnique<CPDF_String>(nullptr, "Page", false));
+      "Type", pdfium::MakeRetain<CPDF_String>(nullptr, "Page", false));
   holder.GetTestObject(3)->GetDict()->SetNewFor<CPDF_Reference>("OtherPageData",
                                                                 &holder, 4);
   // Add unavailable object related to other page.
   holder.AddObject(
-      4, pdfium::MakeUnique<CPDF_String>(nullptr, "Other page data", false),
+      4, pdfium::MakeRetain<CPDF_String>(nullptr, "Other page data", false),
       TestHolder::ObjectState::Unavailable);
 
-  CPDF_PageObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+  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());
diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp
index 0d1b02a..3e2819b 100644
--- a/core/fpdfapi/parser/cpdf_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_parser.cpp
@@ -16,6 +16,8 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_linearized_header.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_object_stream.h"
+#include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_security_handler.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
@@ -23,7 +25,6 @@
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_memorystream.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/ptr_util.h"
@@ -35,6 +36,7 @@
 // this may be large enough in practice.
 const int32_t kMaxXRefSize = 1048576;
 
+// "%PDF-1.7\n"
 constexpr FX_FILESIZE kPDFHeaderSize = 9;
 
 uint32_t GetVarInt(const uint8_t* p, int32_t n) {
@@ -44,118 +46,49 @@
   return result;
 }
 
-int32_t GetStreamNCount(const RetainPtr<CPDF_StreamAcc>& pObjStream) {
-  return pObjStream->GetDict()->GetIntegerFor("N");
-}
-
-int32_t GetStreamFirst(const RetainPtr<CPDF_StreamAcc>& pObjStream) {
-  return pObjStream->GetDict()->GetIntegerFor("First");
-}
+class ObjectsHolderStub final : public CPDF_Parser::ParsedObjectsHolder {
+ public:
+  ObjectsHolderStub() = default;
+  ~ObjectsHolderStub() override = default;
+  bool TryInit() override { return true; }
+};
 
 }  // namespace
 
-class CPDF_Parser::TrailerData {
- public:
-  TrailerData() {}
-  ~TrailerData() {}
-
-  CPDF_Dictionary* GetMainTrailer() const { return main_trailer_.get(); }
-
-  std::unique_ptr<CPDF_Dictionary> GetCombinedTrailer() const {
-    std::unique_ptr<CPDF_Dictionary> result =
-        ToDictionary(main_trailer_->Clone());
-
-    // Info is optional.
-    uint32_t info_obj_num = GetInfoObjNum();
-    if (info_obj_num > 0)
-      result->SetNewFor<CPDF_Reference>("Info", nullptr, GetInfoObjNum());
-
-    // Root is required.
-    result->SetNewFor<CPDF_Reference>("Root", nullptr, GetRootObjNum());
-    return result;
+CPDF_Parser::CPDF_Parser(ParsedObjectsHolder* holder)
+    : m_pObjectsHolder(holder),
+      m_CrossRefTable(pdfium::MakeUnique<CPDF_CrossRefTable>()) {
+  if (!holder) {
+    m_pOwnedObjectsHolder = pdfium::MakeUnique<ObjectsHolderStub>();
+    m_pObjectsHolder = m_pOwnedObjectsHolder.get();
   }
+}
 
-  void SetMainTrailer(std::unique_ptr<CPDF_Dictionary> trailer) {
-    ASSERT(trailer);
-    main_trailer_ = std::move(trailer);
-    ApplyTrailer(main_trailer_.get());
-  }
-
-  void AppendTrailer(std::unique_ptr<CPDF_Dictionary> trailer) {
-    ASSERT(trailer);
-    ApplyTrailer(trailer.get());
-  }
-
-  void Clear() {
-    main_trailer_.reset();
-    last_info_obj_num_ = 0;
-    last_root_obj_num_ = 0;
-  }
-
-  uint32_t GetInfoObjNum() const {
-    const CPDF_Reference* pRef = ToReference(
-        GetMainTrailer() ? GetMainTrailer()->GetObjectFor("Info") : nullptr);
-    return pRef ? pRef->GetRefObjNum() : last_info_obj_num_;
-  }
-
-  uint32_t GetRootObjNum() const {
-    const CPDF_Reference* pRef = ToReference(
-        GetMainTrailer() ? GetMainTrailer()->GetObjectFor("Root") : nullptr);
-    return pRef ? pRef->GetRefObjNum() : last_root_obj_num_;
-  }
-
- private:
-  void ApplyTrailer(const CPDF_Dictionary* dict) {
-    // The most recent Info object number contained in last added trailer.
-    // See PDF 1.7 spec, section 3.4.5 - Incremental Updates.
-    const auto* pRef = ToReference(dict->GetObjectFor("Info"));
-    if (pRef)
-      last_info_obj_num_ = pRef->GetRefObjNum();
-
-    const auto* pRoot = ToReference(dict->GetObjectFor("Root"));
-    if (pRoot)
-      last_root_obj_num_ = pRoot->GetRefObjNum();
-  }
-
-  std::unique_ptr<CPDF_Dictionary> main_trailer_;
-  uint32_t last_info_obj_num_ = 0;
-  uint32_t last_root_obj_num_ = 0;
-};
-
-CPDF_Parser::CPDF_Parser()
-    : m_pSyntax(pdfium::MakeUnique<CPDF_SyntaxParser>()),
-      m_bHasParsed(false),
-      m_bXRefStream(false),
-      m_FileVersion(0),
-      m_TrailerData(pdfium::MakeUnique<TrailerData>()) {}
+CPDF_Parser::CPDF_Parser() : CPDF_Parser(nullptr) {}
 
 CPDF_Parser::~CPDF_Parser() {
   ReleaseEncryptHandler();
 }
 
 uint32_t CPDF_Parser::GetLastObjNum() const {
-  return m_ObjectInfo.empty() ? 0 : m_ObjectInfo.rbegin()->first;
+  return m_CrossRefTable->objects_info().empty()
+             ? 0
+             : m_CrossRefTable->objects_info().rbegin()->first;
 }
 
 bool CPDF_Parser::IsValidObjectNumber(uint32_t objnum) const {
-  return !m_ObjectInfo.empty() && objnum <= m_ObjectInfo.rbegin()->first;
+  return objnum <= GetLastObjNum();
 }
 
 FX_FILESIZE CPDF_Parser::GetObjectPositionOrZero(uint32_t objnum) const {
-  auto it = m_ObjectInfo.find(objnum);
-  return it != m_ObjectInfo.end() ? it->second.pos : 0;
+  const auto* info = m_CrossRefTable->GetObjectInfo(objnum);
+  return (info && info->type == ObjectType::kNormal) ? info->pos : 0;
 }
 
 CPDF_Parser::ObjectType CPDF_Parser::GetObjectType(uint32_t objnum) const {
   ASSERT(IsValidObjectNumber(objnum));
-  auto it = m_ObjectInfo.find(objnum);
-  return it != m_ObjectInfo.end() ? it->second.type : ObjectType::kFree;
-}
-
-uint16_t CPDF_Parser::GetObjectGenNum(uint32_t objnum) const {
-  ASSERT(IsValidObjectNumber(objnum));
-  auto it = m_ObjectInfo.find(objnum);
-  return it != m_ObjectInfo.end() ? it->second.gennum : 0;
+  const auto* info = m_CrossRefTable->GetObjectInfo(objnum);
+  return info ? info->type : ObjectType::kFree;
 }
 
 bool CPDF_Parser::IsObjectFreeOrNull(uint32_t objnum) const {
@@ -167,7 +100,7 @@
     case ObjectType::kCompressed:
       return false;
   }
-  ASSERT(false);  // NOTREACHED();
+  NOTREACHED();
   return false;
 }
 
@@ -175,39 +108,19 @@
   return GetObjectType(objnum) == ObjectType::kFree;
 }
 
-void CPDF_Parser::SetEncryptDictionary(CPDF_Dictionary* pDict) {
-  m_pEncryptDict = pDict;
-}
-
-RetainPtr<IFX_SeekableReadStream> CPDF_Parser::GetFileAccess() const {
-  return m_pSyntax->GetFileAccess();
-}
-
-void CPDF_Parser::ShrinkObjectMap(uint32_t objnum) {
-  if (objnum == 0) {
-    m_ObjectInfo.clear();
-    return;
-  }
-
-  auto it = m_ObjectInfo.lower_bound(objnum);
-  while (it != m_ObjectInfo.end()) {
-    auto saved_it = it++;
-    m_ObjectInfo.erase(saved_it);
-  }
-
-  if (!pdfium::ContainsKey(m_ObjectInfo, objnum - 1))
-    m_ObjectInfo[objnum - 1].pos = 0;
+void CPDF_Parser::ShrinkObjectMap(uint32_t size) {
+  m_CrossRefTable->ShrinkObjectMap(size);
 }
 
 bool CPDF_Parser::InitSyntaxParser(
-    const RetainPtr<IFX_SeekableReadStream>& file_access) {
-  const int32_t header_offset = GetHeaderOffset(file_access);
-  if (header_offset == kInvalidHeaderOffset)
+    const RetainPtr<CPDF_ReadValidator>& validator) {
+  const Optional<FX_FILESIZE> header_offset = GetHeaderOffset(validator);
+  if (!header_offset)
     return false;
-  if (file_access->GetSize() < header_offset + kPDFHeaderSize)
+  if (validator->GetSize() < *header_offset + kPDFHeaderSize)
     return false;
 
-  m_pSyntax->InitParser(file_access, header_offset);
+  m_pSyntax = pdfium::MakeUnique<CPDF_SyntaxParser>(validator, *header_offset);
   return ParseFileVersion();
 }
 
@@ -230,45 +143,42 @@
 
 CPDF_Parser::Error CPDF_Parser::StartParse(
     const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-    CPDF_Document* pDocument) {
-  if (!InitSyntaxParser(pFileAccess))
+    const char* password) {
+  if (!InitSyntaxParser(
+          pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr)))
     return FORMAT_ERROR;
-  return StartParseInternal(pDocument);
+  SetPassword(password);
+  return StartParseInternal();
 }
 
-CPDF_Parser::Error CPDF_Parser::StartParseInternal(CPDF_Document* pDocument) {
+CPDF_Parser::Error CPDF_Parser::StartParseInternal() {
   ASSERT(!m_bHasParsed);
+  ASSERT(!m_bXRefTableRebuilt);
   m_bHasParsed = true;
   m_bXRefStream = false;
 
-  m_pDocument = pDocument;
-
-  bool bXRefRebuilt = false;
-
   m_LastXRefOffset = ParseStartXRef();
-
-  if (m_LastXRefOffset > 0) {
+  if (m_LastXRefOffset >= kPDFHeaderSize) {
     if (!LoadAllCrossRefV4(m_LastXRefOffset) &&
         !LoadAllCrossRefV5(m_LastXRefOffset)) {
       if (!RebuildCrossRef())
         return FORMAT_ERROR;
 
-      bXRefRebuilt = true;
+      m_bXRefTableRebuilt = true;
       m_LastXRefOffset = 0;
     }
   } else {
     if (!RebuildCrossRef())
       return FORMAT_ERROR;
 
-    bXRefRebuilt = true;
+    m_bXRefTableRebuilt = true;
   }
   Error eRet = SetEncryptHandler();
   if (eRet != SUCCESS)
     return eRet;
 
-  m_pDocument->LoadDoc();
-  if (!m_pDocument->GetRoot() || m_pDocument->GetPageCount() == 0) {
-    if (bXRefRebuilt)
+  if (!GetRoot() || !m_pObjectsHolder->TryInit()) {
+    if (m_bXRefTableRebuilt)
       return FORMAT_ERROR;
 
     ReleaseEncryptHandler();
@@ -279,13 +189,13 @@
     if (eRet != SUCCESS)
       return eRet;
 
-    m_pDocument->LoadDoc();
-    if (!m_pDocument->GetRoot())
+    m_pObjectsHolder->TryInit();
+    if (!GetRoot())
       return FORMAT_ERROR;
   }
-  if (GetRootObjNum() == 0) {
+  if (GetRootObjNum() == CPDF_Object::kInvalidObjNum) {
     ReleaseEncryptHandler();
-    if (!RebuildCrossRef() || GetRootObjNum() == 0)
+    if (!RebuildCrossRef() || GetRootObjNum() == CPDF_Object::kInvalidObjNum)
       return FORMAT_ERROR;
 
     eRet = SetEncryptHandler();
@@ -294,7 +204,7 @@
   }
   if (m_pSecurityHandler && !m_pSecurityHandler->IsMetadataEncrypted()) {
     CPDF_Reference* pMetadata =
-        ToReference(m_pDocument->GetRoot()->GetObjectFor("Metadata"));
+        ToReference(GetRoot()->GetObjectFor("Metadata"));
     if (pMetadata)
       m_MetadataObjnum = pMetadata->GetRefObjNum();
   }
@@ -303,8 +213,7 @@
 
 FX_FILESIZE CPDF_Parser::ParseStartXRef() {
   static constexpr char kStartXRefKeyword[] = "startxref";
-  m_pSyntax->SetPos(m_pSyntax->m_FileLen - m_pSyntax->m_HeaderOffset -
-                    strlen(kStartXRefKeyword));
+  m_pSyntax->SetPos(m_pSyntax->GetDocumentSize() - strlen(kStartXRefKeyword));
   if (!m_pSyntax->BackwardsSearchToWord(kStartXRefKeyword, 4096))
     return 0;
 
@@ -313,12 +222,12 @@
 
   // Read XRef offset.
   bool bNumber;
-  const ByteString xrefpos_str = m_pSyntax->GetNextWord(&bNumber);
-  if (!bNumber || xrefpos_str.IsEmpty())
+  const ByteString xref_offset_str = m_pSyntax->GetNextWord(&bNumber);
+  if (!bNumber || xref_offset_str.IsEmpty())
     return 0;
 
-  const FX_SAFE_FILESIZE result = FXSYS_atoi64(xrefpos_str.c_str());
-  if (!result.IsValid() || result.ValueOrDie() >= GetFileAccess()->GetSize())
+  const FX_SAFE_FILESIZE result = FXSYS_atoi64(xref_offset_str.c_str());
+  if (!result.IsValid() || result.ValueOrDie() >= m_pSyntax->GetDocumentSize())
     return 0;
 
   return result.ValueOrDie();
@@ -329,50 +238,23 @@
   if (!GetTrailer())
     return FORMAT_ERROR;
 
-  CPDF_Object* pEncryptObj = GetTrailer()->GetObjectFor("Encrypt");
-  if (pEncryptObj) {
-    if (CPDF_Dictionary* pEncryptDict = pEncryptObj->AsDictionary()) {
-      SetEncryptDictionary(pEncryptDict);
-    } else if (CPDF_Reference* pRef = pEncryptObj->AsReference()) {
-      pEncryptObj = m_pDocument->GetOrParseIndirectObject(pRef->GetRefObjNum());
-      if (pEncryptObj)
-        SetEncryptDictionary(pEncryptObj->GetDict());
-    }
-  }
+  const CPDF_Dictionary* pEncryptDict = GetEncryptDict();
+  if (!pEncryptDict)
+    return SUCCESS;
 
-  if (m_pEncryptDict) {
-    ByteString filter = m_pEncryptDict->GetStringFor("Filter");
-    if (filter != "Standard")
-      return HANDLER_ERROR;
+  if (pEncryptDict->GetStringFor("Filter") != "Standard")
+    return HANDLER_ERROR;
 
-    std::unique_ptr<CPDF_SecurityHandler> pSecurityHandler =
-        pdfium::MakeUnique<CPDF_SecurityHandler>();
-    if (!pSecurityHandler->OnInit(m_pEncryptDict.Get(), GetIDArray(),
-                                  m_Password))
-      return PASSWORD_ERROR;
+  auto pSecurityHandler = pdfium::MakeRetain<CPDF_SecurityHandler>();
+  if (!pSecurityHandler->OnInit(pEncryptDict, GetIDArray(), GetPassword()))
+    return PASSWORD_ERROR;
 
-    m_pSecurityHandler = std::move(pSecurityHandler);
-  }
+  m_pSecurityHandler = std::move(pSecurityHandler);
   return SUCCESS;
 }
 
 void CPDF_Parser::ReleaseEncryptHandler() {
-  m_pSecurityHandler.reset();
-  SetEncryptDictionary(nullptr);
-}
-
-FX_FILESIZE CPDF_Parser::GetObjectOffset(uint32_t objnum) const {
-  if (!IsValidObjectNumber(objnum))
-    return 0;
-
-  if (GetObjectType(objnum) == ObjectType::kNotCompressed)
-    return GetObjectPositionOrZero(objnum);
-
-  if (GetObjectType(objnum) == ObjectType::kCompressed) {
-    FX_FILESIZE pos = GetObjectPositionOrZero(objnum);
-    return GetObjectPositionOrZero(pos);
-  }
-  return 0;
+  m_pSecurityHandler.Reset();
 }
 
 // Ideally, all the cross reference entries should be verified.
@@ -380,7 +262,7 @@
 // with the objects. crbug/602650 showed a case where object numbers
 // in the cross reference table are all off by one.
 bool CPDF_Parser::VerifyCrossRefV4() {
-  for (const auto& it : m_ObjectInfo) {
+  for (const auto& it : m_CrossRefTable->objects_info()) {
     if (it.second.pos == 0)
       continue;
     // Find the first non-zero position.
@@ -395,63 +277,63 @@
       // something is wrong with the cross reference table.
       return false;
     }
-    return true;
+    break;
   }
   return true;
 }
 
-bool CPDF_Parser::LoadAllCrossRefV4(FX_FILESIZE xrefpos) {
-  if (!LoadCrossRefV4(xrefpos, true))
+bool CPDF_Parser::LoadAllCrossRefV4(FX_FILESIZE xref_offset) {
+  if (!LoadCrossRefV4(xref_offset, true))
     return false;
 
-  std::unique_ptr<CPDF_Dictionary> trailer = LoadTrailerV4();
+  RetainPtr<CPDF_Dictionary> trailer = LoadTrailerV4();
   if (!trailer)
     return false;
 
-  m_TrailerData->SetMainTrailer(std::move(trailer));
+  m_CrossRefTable->SetTrailer(std::move(trailer));
   int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
   if (xrefsize > 0 && xrefsize <= kMaxXRefSize)
     ShrinkObjectMap(xrefsize);
 
-  std::vector<FX_FILESIZE> CrossRefList;
-  std::vector<FX_FILESIZE> XRefStreamList;
-  std::set<FX_FILESIZE> seen_xrefpos;
-
-  CrossRefList.push_back(xrefpos);
-  XRefStreamList.push_back(GetDirectInteger(GetTrailer(), "XRefStm"));
-  seen_xrefpos.insert(xrefpos);
+  std::vector<FX_FILESIZE> xref_stream_list{
+      GetDirectInteger(GetTrailer(), "XRefStm")};
+  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.
-  xrefpos = GetDirectInteger(GetTrailer(), "Prev");
-  while (xrefpos) {
+  xref_offset = GetDirectInteger(GetTrailer(), "Prev");
+  while (xref_offset) {
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xrefpos, xrefpos))
+    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
       return false;
 
-    seen_xrefpos.insert(xrefpos);
+    seen_xref_offset.insert(xref_offset);
 
     // SLOW ...
-    CrossRefList.insert(CrossRefList.begin(), xrefpos);
-    LoadCrossRefV4(xrefpos, true);
+    xref_list.insert(xref_list.begin(), xref_offset);
+    LoadCrossRefV4(xref_offset, true);
 
-    std::unique_ptr<CPDF_Dictionary> pDict(LoadTrailerV4());
+    RetainPtr<CPDF_Dictionary> pDict(LoadTrailerV4());
     if (!pDict)
       return false;
 
-    xrefpos = GetDirectInteger(pDict.get(), "Prev");
+    xref_offset = GetDirectInteger(pDict.Get(), "Prev");
 
     // SLOW ...
-    XRefStreamList.insert(XRefStreamList.begin(),
-                          pDict->GetIntegerFor("XRefStm"));
-    m_TrailerData->AppendTrailer(std::move(pDict));
+    xref_stream_list.insert(xref_stream_list.begin(),
+                            pDict->GetIntegerFor("XRefStm"));
+
+    m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
+        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pDict)),
+        std::move(m_CrossRefTable));
   }
 
-  for (size_t i = 0; i < CrossRefList.size(); ++i) {
-    if (!LoadCrossRefV4(CrossRefList[i], false))
+  for (size_t i = 0; i < xref_list.size(); ++i) {
+    if (!LoadCrossRefV4(xref_list[i], false))
       return false;
 
-    if (XRefStreamList[i] && !LoadCrossRefV5(&XRefStreamList[i], false))
+    if (xref_stream_list[i] && !LoadCrossRefV5(&xref_stream_list[i], false))
       return false;
 
     if (i == 0 && !VerifyCrossRefV4())
@@ -460,56 +342,68 @@
   return true;
 }
 
-bool CPDF_Parser::LoadLinearizedAllCrossRefV4(FX_FILESIZE xrefpos) {
-  if (!LoadCrossRefV4(xrefpos, false))
+bool CPDF_Parser::LoadLinearizedAllCrossRefV4(FX_FILESIZE main_xref_offset) {
+  if (!LoadCrossRefV4(main_xref_offset, false))
     return false;
 
-  std::unique_ptr<CPDF_Dictionary> trailer = LoadTrailerV4();
-  if (!trailer)
+  RetainPtr<CPDF_Dictionary> main_trailer = LoadTrailerV4();
+  if (!main_trailer)
     return false;
 
-  m_TrailerData->SetMainTrailer(std::move(trailer));
-  int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
-  if (xrefsize == 0)
+  // GetTrailer() currently returns the first-page trailer.
+  if (GetDirectInteger(GetTrailer(), "Size") == 0)
     return false;
 
-  std::vector<FX_FILESIZE> CrossRefList;
-  std::vector<FX_FILESIZE> XRefStreamList;
-  std::set<FX_FILESIZE> seen_xrefpos;
+  // 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")};
+  std::vector<FX_FILESIZE> xref_list{main_xref_offset};
+  std::set<FX_FILESIZE> seen_xref_offset{main_xref_offset};
 
-  CrossRefList.push_back(xrefpos);
-  XRefStreamList.push_back(GetDirectInteger(GetTrailer(), "XRefStm"));
-  seen_xrefpos.insert(xrefpos);
+  // Merge the trailers.
+  m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
+      pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(main_trailer)),
+      std::move(m_CrossRefTable));
 
-  xrefpos = GetDirectInteger(GetTrailer(), "Prev");
-  while (xrefpos) {
+  // Now GetTrailer() returns the merged trailer, where /Prev is from the
+  // main-trailer.
+  FX_FILESIZE xref_offset = GetDirectInteger(GetTrailer(), "Prev");
+  while (xref_offset) {
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xrefpos, xrefpos))
+    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
       return false;
 
-    seen_xrefpos.insert(xrefpos);
+    seen_xref_offset.insert(xref_offset);
 
     // SLOW ...
-    CrossRefList.insert(CrossRefList.begin(), xrefpos);
-    LoadCrossRefV4(xrefpos, true);
+    xref_list.insert(xref_list.begin(), xref_offset);
+    LoadCrossRefV4(xref_offset, true);
 
-    std::unique_ptr<CPDF_Dictionary> pDict(LoadTrailerV4());
+    RetainPtr<CPDF_Dictionary> pDict(LoadTrailerV4());
     if (!pDict)
       return false;
 
-    xrefpos = GetDirectInteger(pDict.get(), "Prev");
+    xref_offset = GetDirectInteger(pDict.Get(), "Prev");
 
     // SLOW ...
-    XRefStreamList.insert(XRefStreamList.begin(),
-                          pDict->GetIntegerFor("XRefStm"));
-    m_TrailerData->AppendTrailer(std::move(pDict));
+    xref_stream_list.insert(xref_stream_list.begin(),
+                            pDict->GetIntegerFor("XRefStm"));
+
+    m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
+        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pDict)),
+        std::move(m_CrossRefTable));
   }
 
-  for (size_t i = 1; i < CrossRefList.size(); ++i) {
-    if (!LoadCrossRefV4(CrossRefList[i], false))
+  if (xref_stream_list[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))
       return false;
 
-    if (XRefStreamList[i] && !LoadCrossRefV5(&XRefStreamList[i], false))
+    if (xref_stream_list[i] && !LoadCrossRefV5(&xref_stream_list[i], false))
       return false;
   }
   return true;
@@ -519,6 +413,9 @@
     uint32_t start_objnum,
     uint32_t count,
     std::vector<CrossRefObjData>* out_objects) {
+  if (!count)
+    return true;
+
   // Each entry shall be exactly 20 byte.
   // A sample entry looks like:
   // "0000000000 00007 f\r\n"
@@ -543,7 +440,7 @@
     return false;
 
   const size_t max_entries_in_file =
-      m_pSyntax->GetFileAccess()->GetSize() / kEntryConstSize;
+      m_pSyntax->GetDocumentSize() / kEntryConstSize;
   if (new_size.ValueOrDie() > max_entries_in_file)
     return false;
 
@@ -552,22 +449,20 @@
   std::vector<char> buf(1024 * kEntryConstSize + 1);
   buf.back() = '\0';
 
-  int32_t nBlocks = count / 1024 + 1;
-  for (int32_t block = 0; block < nBlocks; block++) {
-    int32_t block_size = block == nBlocks - 1 ? count % 1024 : 1024;
+  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)) {
       return false;
     }
 
-    for (int32_t i = 0; i < block_size; i++) {
+    for (uint32_t i = 0; i < block_size; i++) {
+      uint32_t iObjectIndex = count - nBytesToRead + i;
       CrossRefObjData& obj_data =
-          (*out_objects)[start_obj_index + block * 1024 + i];
-
-      const uint32_t objnum = start_objnum + block * 1024 + i;
-
+          (*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];
@@ -595,6 +490,7 @@
         info.type = ObjectType::kNotCompressed;
       }
     }
+    nBytesToRead -= block_size;
   }
   return true;
 }
@@ -607,15 +503,14 @@
     return false;
   std::vector<CrossRefObjData> result_objects;
   while (1) {
-    FX_FILESIZE SavedPos = m_pSyntax->GetPos();
+    FX_FILESIZE saved_pos = m_pSyntax->GetPos();
     bool bIsNumber;
     ByteString word = m_pSyntax->GetNextWord(&bIsNumber);
-    if (word.IsEmpty()) {
+    if (word.IsEmpty())
       return false;
-    }
 
     if (!bIsNumber) {
-      m_pSyntax->SetPos(SavedPos);
+      m_pSyntax->SetPos(saved_pos);
       break;
     }
 
@@ -625,7 +520,6 @@
 
     uint32_t count = m_pSyntax->GetDirectNum();
     m_pSyntax->ToNextWord();
-    SavedPos = m_pSyntax->GetPos();
 
     if (!ParseAndAppendCrossRefSubsectionData(
             start_objnum, count, out_objects ? &result_objects : nullptr)) {
@@ -637,37 +531,49 @@
   return true;
 }
 
-bool CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos,
-                                 bool bSkip) {
+bool CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos, bool bSkip) {
   m_pSyntax->SetPos(pos);
   std::vector<CrossRefObjData> objects;
   if (!ParseCrossRefV4(bSkip ? nullptr : &objects))
     return false;
 
   MergeCrossRefObjectsData(objects);
-
   return true;
 }
 
 void CPDF_Parser::MergeCrossRefObjectsData(
     const std::vector<CrossRefObjData>& objects) {
   for (const auto& obj : objects) {
-    m_ObjectInfo[obj.obj_num] = obj.info;
+    switch (obj.info.type) {
+      case ObjectType::kFree:
+        if (obj.info.gennum > 0)
+          m_CrossRefTable->SetFree(obj.obj_num);
+        break;
+      case ObjectType::kNormal:
+      case ObjectType::kObjStream:
+        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);
+        break;
+      default:
+        NOTREACHED();
+    }
   }
 }
 
-bool CPDF_Parser::LoadAllCrossRefV5(FX_FILESIZE xrefpos) {
-  if (!LoadCrossRefV5(&xrefpos, true))
+bool CPDF_Parser::LoadAllCrossRefV5(FX_FILESIZE xref_offset) {
+  if (!LoadCrossRefV5(&xref_offset, true))
     return false;
 
-  std::set<FX_FILESIZE> seen_xrefpos;
-  while (xrefpos) {
-    seen_xrefpos.insert(xrefpos);
-    if (!LoadCrossRefV5(&xrefpos, false))
+  std::set<FX_FILESIZE> seen_xref_offset;
+  while (xref_offset) {
+    seen_xref_offset.insert(xref_offset);
+    if (!LoadCrossRefV5(&xref_offset, false))
       return false;
 
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xrefpos, xrefpos))
+    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
       return false;
   }
   m_ObjectStreamMap.clear();
@@ -676,372 +582,82 @@
 }
 
 bool CPDF_Parser::RebuildCrossRef() {
-  m_ObjectInfo.clear();
-  m_TrailerData->Clear();
+  auto cross_ref_table = pdfium::MakeUnique<CPDF_CrossRefTable>();
 
-  ParserState state = ParserState::kDefault;
-  int32_t inside_index = 0;
-  uint32_t objnum = 0;
-  uint32_t gennum = 0;
-  int32_t depth = 0;
   const uint32_t kBufferSize = 4096;
-  std::vector<uint8_t> buffer(kBufferSize);
+  m_pSyntax->SetReadBufferSize(kBufferSize);
+  m_pSyntax->SetPos(0);
 
-  FX_FILESIZE pos = m_pSyntax->m_HeaderOffset;
-  FX_FILESIZE start_pos = 0;
-  FX_FILESIZE start_pos1 = 0;
-  FX_FILESIZE last_obj = -1;
-  FX_FILESIZE last_xref = -1;
-  FX_FILESIZE last_trailer = -1;
+  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) {
+      numbers.emplace_back(FXSYS_atoui(word.c_str()),
+                           m_pSyntax->GetPos() - word.GetLength());
+      if (numbers.size() > 2u)
+        numbers.erase(numbers.begin());
+      continue;
+    }
 
-  while (pos < m_pSyntax->m_FileLen) {
-    const FX_FILESIZE saved_pos = pos;
-    bool bOverFlow = false;
-    uint32_t size =
-        std::min((uint32_t)(m_pSyntax->m_FileLen - pos), kBufferSize);
-    if (!m_pSyntax->GetFileAccess()->ReadBlock(buffer.data(), pos, size))
-      break;
+    if (word == "(") {
+      m_pSyntax->ReadString();
+    } else if (word == "<") {
+      m_pSyntax->ReadHexString();
+    } else if (word == "trailer") {
+      RetainPtr<CPDF_Object> pTrailer = m_pSyntax->GetObjectBody(nullptr);
+      if (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))));
+      }
+    } else if (word == "obj" && numbers.size() == 2u) {
+      const FX_FILESIZE obj_pos = numbers[0].second;
+      const uint32_t obj_num = numbers[0].first;
+      const uint32_t gen_num = numbers[1].first;
 
-    for (uint32_t i = 0; i < size; i++) {
-      uint8_t byte = buffer[i];
-      switch (state) {
-        case ParserState::kDefault:
-          if (PDFCharIsWhitespace(byte)) {
-            state = ParserState::kWhitespace;
-          } else if (std::isdigit(byte)) {
-            --i;
-            state = ParserState::kWhitespace;
-          } else if (byte == '%') {
-            inside_index = 0;
-            state = ParserState::kComment;
-          } else if (byte == '(') {
-            state = ParserState::kString;
-            depth = 1;
-          } else if (byte == '<') {
-            inside_index = 1;
-            state = ParserState::kHexString;
-          } else if (byte == '\\') {
-            state = ParserState::kEscapedString;
-          } else if (byte == 't') {
-            state = ParserState::kTrailer;
-            inside_index = 1;
-          }
-          break;
+      m_pSyntax->SetPos(obj_pos);
+      const RetainPtr<CPDF_Stream> pStream =
+          ToStream(m_pSyntax->GetIndirectObject(
+              nullptr, CPDF_SyntaxParser::ParseType::kStrict));
 
-        case ParserState::kWhitespace:
-          if (std::isdigit(byte)) {
-            start_pos = pos + i;
-            state = ParserState::kObjNum;
-            objnum = FXSYS_DecimalCharToInt(static_cast<wchar_t>(byte));
-          } else if (byte == 't') {
-            state = ParserState::kTrailer;
-            inside_index = 1;
-          } else if (byte == 'x') {
-            state = ParserState::kXref;
-            inside_index = 1;
-          } else if (!PDFCharIsWhitespace(byte)) {
-            --i;
-            state = ParserState::kDefault;
-          }
-          break;
-
-        case ParserState::kObjNum:
-          if (std::isdigit(byte)) {
-            objnum = objnum * 10 +
-                     FXSYS_DecimalCharToInt(static_cast<wchar_t>(byte));
-          } else if (PDFCharIsWhitespace(byte)) {
-            state = ParserState::kPostObjNum;
-          } else {
-            --i;
-            state = ParserState::kEndObj;
-            inside_index = 0;
-          }
-          break;
-
-        case ParserState::kPostObjNum:
-          if (std::isdigit(byte)) {
-            start_pos1 = pos + i;
-            state = ParserState::kGenNum;
-            gennum = FXSYS_DecimalCharToInt(static_cast<wchar_t>(byte));
-          } else if (byte == 't') {
-            state = ParserState::kTrailer;
-            inside_index = 1;
-          } else if (!PDFCharIsWhitespace(byte)) {
-            --i;
-            state = ParserState::kDefault;
-          }
-          break;
-
-        case ParserState::kGenNum:
-          if (std::isdigit(byte)) {
-            gennum = gennum * 10 +
-                     FXSYS_DecimalCharToInt(static_cast<wchar_t>(byte));
-          } else if (PDFCharIsWhitespace(byte)) {
-            state = ParserState::kPostGenNum;
-          } else {
-            --i;
-            state = ParserState::kDefault;
-          }
-          break;
-
-        case ParserState::kPostGenNum:
-          if (byte == 'o') {
-            state = ParserState::kBeginObj;
-            inside_index = 1;
-          } else if (std::isdigit(byte)) {
-            objnum = gennum;
-            gennum = FXSYS_DecimalCharToInt(static_cast<wchar_t>(byte));
-            start_pos = start_pos1;
-            start_pos1 = pos + i;
-            state = ParserState::kGenNum;
-          } else if (byte == 't') {
-            state = ParserState::kTrailer;
-            inside_index = 1;
-          } else if (!PDFCharIsWhitespace(byte)) {
-            --i;
-            state = ParserState::kDefault;
-          }
-          break;
-
-        case ParserState::kBeginObj:
-          switch (inside_index) {
-            case 1:
-              if (byte != 'b') {
-                --i;
-                state = ParserState::kDefault;
-              } else {
-                inside_index++;
-              }
-              break;
-            case 2:
-              if (byte != 'j') {
-                --i;
-                state = ParserState::kDefault;
-              } else {
-                inside_index++;
-              }
-              break;
-            case 3:
-              if (PDFCharIsWhitespace(byte) || PDFCharIsDelimiter(byte)) {
-                FX_FILESIZE obj_pos = start_pos - m_pSyntax->m_HeaderOffset;
-                last_obj = start_pos;
-                FX_FILESIZE obj_end = 0;
-                std::unique_ptr<CPDF_Object> pObject =
-                    ParseIndirectObjectAtByStrict(m_pDocument.Get(), obj_pos,
-                                                  objnum, &obj_end);
-                if (CPDF_Stream* pStream = ToStream(pObject.get())) {
-                  if (CPDF_Dictionary* pDict = pStream->GetDict()) {
-                    if ((pDict->KeyExist("Type")) &&
-                        (pDict->GetStringFor("Type") == "XRef" &&
-                         pDict->KeyExist("Size"))) {
-                      CPDF_Object* pRoot = pDict->GetObjectFor("Root");
-                      if (pRoot && pRoot->GetDict() &&
-                          pRoot->GetDict()->GetObjectFor("Pages")) {
-                        m_TrailerData->SetMainTrailer(
-                            ToDictionary(pDict->Clone()));
-                      }
-                    }
-                  }
-                }
-
-                FX_FILESIZE offset = 0;
-                m_pSyntax->SetPos(obj_pos);
-                offset = m_pSyntax->FindTag("obj", 0);
-                if (offset == -1)
-                  offset = 0;
-                else
-                  offset += 3;
-
-                FX_FILESIZE nLen = obj_end - obj_pos - offset;
-                if ((uint32_t)nLen > size - i) {
-                  pos = obj_end + m_pSyntax->m_HeaderOffset;
-                  bOverFlow = true;
-                } else {
-                  i += (uint32_t)nLen;
-                }
-
-                if (!m_ObjectInfo.empty() && IsValidObjectNumber(objnum) &&
-                    m_ObjectInfo[objnum].pos) {
-                  if (pObject) {
-                    m_ObjectInfo[objnum].pos = obj_pos;
-                    m_ObjectInfo[objnum].gennum = gennum;
-                  }
-                } else {
-                  m_ObjectInfo[objnum].pos = obj_pos;
-                  m_ObjectInfo[objnum].type = ObjectType::kNotCompressed;
-                  m_ObjectInfo[objnum].gennum = gennum;
-                }
-              }
-              --i;
-              state = ParserState::kDefault;
-              break;
-          }
-          break;
-
-        case ParserState::kTrailer:
-          if (inside_index == 7) {
-            if (PDFCharIsWhitespace(byte) || PDFCharIsDelimiter(byte)) {
-              last_trailer = pos + i - 7;
-              m_pSyntax->SetPos(pos + i - m_pSyntax->m_HeaderOffset);
-
-              std::unique_ptr<CPDF_Object> pObj =
-                  m_pSyntax->GetObjectBody(m_pDocument.Get());
-              if (pObj) {
-                if (pObj->IsDictionary() || pObj->AsStream()) {
-                  CPDF_Stream* pStream = pObj->AsStream();
-                  if (CPDF_Dictionary* pTrailer =
-                          pStream ? pStream->GetDict() : pObj->AsDictionary()) {
-                    if (GetTrailer()) {
-                      CPDF_Object* pRoot = pTrailer->GetObjectFor("Root");
-                      CPDF_Reference* pRef = ToReference(pRoot);
-                      if (!pRoot ||
-                          (pRef && IsValidObjectNumber(pRef->GetRefObjNum()) &&
-                           m_ObjectInfo[pRef->GetRefObjNum()].pos != 0)) {
-                        auto it = pTrailer->begin();
-                        while (it != pTrailer->end()) {
-                          const ByteString& key = it->first;
-                          CPDF_Object* pElement = it->second.get();
-                          ++it;
-                          uint32_t dwObjNum =
-                              pElement ? pElement->GetObjNum() : 0;
-                          if (dwObjNum) {
-                            GetTrailer()->SetNewFor<CPDF_Reference>(
-                                key, m_pDocument.Get(), dwObjNum);
-                          } else {
-                            GetTrailer()->SetFor(key, pElement->Clone());
-                          }
-                        }
-                      }
-                    } else {
-                      m_TrailerData->SetMainTrailer(
-                          ToDictionary(pObj->IsStream() ? pTrailer->Clone()
-                                                        : std::move(pObj)));
-
-                      FX_FILESIZE dwSavePos = m_pSyntax->GetPos();
-                      ByteString strWord = m_pSyntax->GetKeyword();
-                      if (!strWord.Compare("startxref")) {
-                        bool bNumber;
-                        ByteString bsOffset = m_pSyntax->GetNextWord(&bNumber);
-                        if (bNumber)
-                          m_LastXRefOffset = FXSYS_atoi(bsOffset.c_str());
-                      }
-                      m_pSyntax->SetPos(dwSavePos);
-                    }
-                  }
-                }
-              }
-            }
-            --i;
-            state = ParserState::kDefault;
-          } else if (byte == "trailer"[inside_index]) {
-            inside_index++;
-          } else {
-            --i;
-            state = ParserState::kDefault;
-          }
-          break;
-
-        case ParserState::kXref:
-          if (inside_index == 4) {
-            last_xref = pos + i - 4;
-            state = ParserState::kWhitespace;
-          } else if (byte == "xref"[inside_index]) {
-            inside_index++;
-          } else {
-            --i;
-            state = ParserState::kDefault;
-          }
-          break;
-
-        case ParserState::kComment:
-          if (PDFCharIsLineEnding(byte))
-            state = ParserState::kDefault;
-          break;
-
-        case ParserState::kString:
-          if (byte == ')') {
-            if (depth > 0)
-              depth--;
-          } else if (byte == '(') {
-            depth++;
-          }
-
-          if (!depth)
-            state = ParserState::kDefault;
-          break;
-
-        case ParserState::kHexString:
-          if (byte == '>' || (byte == '<' && inside_index == 1))
-            state = ParserState::kDefault;
-          inside_index = 0;
-          break;
-
-        case ParserState::kEscapedString:
-          if (PDFCharIsDelimiter(byte) || PDFCharIsWhitespace(byte)) {
-            --i;
-            state = ParserState::kDefault;
-          }
-          break;
-
-        case ParserState::kEndObj:
-          if (PDFCharIsWhitespace(byte)) {
-            state = ParserState::kDefault;
-          } else if (byte == '%' || byte == '(' || byte == '<' ||
-                     byte == '\\') {
-            state = ParserState::kDefault;
-            --i;
-          } else if (inside_index == 6) {
-            state = ParserState::kDefault;
-            --i;
-          } else if (byte == "endobj"[inside_index]) {
-            inside_index++;
-          }
-          break;
+      if (pStream && pStream->GetDict()->GetStringFor("Type") == "XRef") {
+        cross_ref_table = CPDF_CrossRefTable::MergeUp(
+            std::move(cross_ref_table),
+            pdfium::MakeUnique<CPDF_CrossRefTable>(
+                ToDictionary(pStream->GetDict()->Clone())));
       }
 
-      if (bOverFlow) {
-        size = 0;
-        break;
+      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);
+          }
+        }
       }
     }
-    pos += size;
-
-    // If the position has not changed at all or went backwards in a loop
-    // iteration, then break out to prevent infinite looping.
-    if (pos <= saved_pos)
-      break;
+    numbers.clear();
   }
 
-  if (last_xref != -1 && last_xref > last_obj)
-    last_trailer = last_xref;
-  else if (last_trailer == -1 || last_xref < last_obj)
-    last_trailer = m_pSyntax->m_FileLen;
+  m_CrossRefTable = CPDF_CrossRefTable::MergeUp(std::move(m_CrossRefTable),
+                                                std::move(cross_ref_table));
+  // Resore default buffer size.
+  m_pSyntax->SetReadBufferSize(CPDF_Stream::kFileBufSize);
 
-  return GetTrailer() && !m_ObjectInfo.empty();
+  return GetTrailer() && !m_CrossRefTable->objects_info().empty();
 }
 
 bool CPDF_Parser::LoadCrossRefV5(FX_FILESIZE* pos, bool bMainXRef) {
-  std::unique_ptr<CPDF_Object> pObject(
-      ParseIndirectObjectAt(m_pDocument.Get(), *pos, 0));
-  if (!pObject)
+  RetainPtr<CPDF_Object> pObject(ParseIndirectObjectAt(*pos, 0));
+  if (!pObject || !pObject->GetObjNum())
     return false;
 
-  uint32_t objnum = pObject->GetObjNum();
-  if (!objnum)
-    return false;
-
-  CPDF_Object* pUnownedObject = pObject.get();
-  if (m_pDocument) {
-    const CPDF_Dictionary* pRootDict = m_pDocument->GetRoot();
-    if (pRootDict && pRootDict->GetObjNum() == objnum)
-      return false;
-    if (!m_pDocument->ReplaceIndirectObjectIfHigherGeneration(
-            objnum, std::move(pObject))) {
-      return false;
-    }
-  }
-
-  CPDF_Stream* pStream = pUnownedObject->AsStream();
+  CPDF_Stream* pStream = pObject->AsStream();
   if (!pStream)
     return false;
 
@@ -1051,20 +667,21 @@
   if (size < 0)
     return false;
 
-  std::unique_ptr<CPDF_Dictionary> pNewTrailer = ToDictionary(pDict->Clone());
+  RetainPtr<CPDF_Dictionary> pNewTrailer = ToDictionary(pDict->Clone());
   if (bMainXRef) {
-    m_TrailerData->SetMainTrailer(std::move(pNewTrailer));
-    ShrinkObjectMap(size);
-    for (auto& it : m_ObjectInfo)
-      it.second.type = ObjectType::kFree;
+    m_CrossRefTable =
+        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pNewTrailer));
+    m_CrossRefTable->ShrinkObjectMap(size);
   } else {
-    m_TrailerData->AppendTrailer(std::move(pNewTrailer));
+    m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
+        pdfium::MakeUnique<CPDF_CrossRefTable>(std::move(pNewTrailer)),
+        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->GetCount() / 2; i++) {
+    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);
 
@@ -1077,7 +694,7 @@
     }
   }
 
-  if (arrIndex.size() == 0)
+  if (arrIndex.empty())
     arrIndex.push_back(std::make_pair(0, size));
 
   pArray = pDict->GetArrayFor("W");
@@ -1086,7 +703,7 @@
 
   std::vector<uint32_t> WidthArray;
   FX_SAFE_UINT32 dwAccWidth = 0;
-  for (size_t i = 0; i < pArray->GetCount(); ++i) {
+  for (size_t i = 0; i < pArray->size(); ++i) {
     WidthArray.push_back(pArray->GetIntegerAt(i));
     dwAccWidth += WidthArray[i];
   }
@@ -1101,12 +718,12 @@
   const uint8_t* pData = pAcc->GetData();
   uint32_t dwTotalSize = pAcc->GetSize();
   uint32_t segindex = 0;
-  for (uint32_t i = 0; i < arrIndex.size(); i++) {
-    int32_t startnum = arrIndex[i].first;
+  for (const auto& index : arrIndex) {
+    const int32_t startnum = index.first;
     if (startnum < 0)
       continue;
 
-    uint32_t count = pdfium::base::checked_cast<uint32_t>(arrIndex[i].second);
+    uint32_t count = pdfium::base::checked_cast<uint32_t>(index.second);
     FX_SAFE_UINT32 dwCaculatedSize = segindex;
     dwCaculatedSize += count;
     dwCaculatedSize *= totalWidth;
@@ -1118,48 +735,57 @@
     const uint8_t* segstart = pData + segindex * totalWidth;
     FX_SAFE_UINT32 dwMaxObjNum = startnum;
     dwMaxObjNum += count;
-    uint32_t dwV5Size = m_ObjectInfo.empty() ? 0 : GetLastObjNum() + 1;
+    uint32_t dwV5Size =
+        m_CrossRefTable->objects_info().empty() ? 0 : GetLastObjNum() + 1;
     if (!dwMaxObjNum.IsValid() || dwMaxObjNum.ValueOrDie() > dwV5Size)
       continue;
 
-    for (uint32_t j = 0; j < count; j++) {
+    for (uint32_t i = 0; i < count; i++) {
       ObjectType type = ObjectType::kNotCompressed;
-      const uint8_t* entrystart = segstart + j * totalWidth;
+      const uint8_t* entrystart = segstart + i * totalWidth;
       if (WidthArray[0]) {
-        const int cross_ref_stream_obj_type =
+        const uint32_t cross_ref_stream_obj_type =
             GetVarInt(entrystart, WidthArray[0]);
         type = GetObjectTypeFromCrossRefStreamType(cross_ref_stream_obj_type);
+        if (type == ObjectType::kNull)
+          continue;
       }
 
-      if (GetObjectType(startnum + j) == ObjectType::kNull) {
-        FX_FILESIZE offset =
-            GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
-        m_ObjectInfo[startnum + j].pos = offset;
+      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 (GetObjectType(startnum + j) != ObjectType::kFree)
+      if (existing_type != ObjectType::kFree)
         continue;
 
-      ObjectInfo& info = m_ObjectInfo[startnum + j];
-
-      info.type = type;
       if (type == ObjectType::kFree) {
-        info.pos = 0;
-      } else {
-        const FX_FILESIZE entry_value =
-            GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
-        if (type == ObjectType::kNotCompressed) {
-          const auto object_offset = entry_value;
-          info.pos = object_offset;
-        } else {
-          const auto archive_obj_num = entry_value;
-          info.archive_obj_num = archive_obj_num;
-          if (archive_obj_num < 0 || !IsValidObjectNumber(archive_obj_num))
-            return false;
-          m_ObjectInfo[archive_obj_num].type = ObjectType::kNull;
-        }
+        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);
     }
     segindex += count;
   }
@@ -1170,25 +796,65 @@
   return GetTrailer() ? GetTrailer()->GetArrayFor("ID") : nullptr;
 }
 
-CPDF_Dictionary* CPDF_Parser::GetTrailer() const {
-  return m_TrailerData->GetMainTrailer();
+CPDF_Dictionary* CPDF_Parser::GetRoot() const {
+  CPDF_Object* obj =
+      m_pObjectsHolder->GetOrParseIndirectObject(GetRootObjNum());
+  return obj ? obj->GetDict() : nullptr;
 }
 
-std::unique_ptr<CPDF_Dictionary> CPDF_Parser::GetCombinedTrailer() const {
-  return m_TrailerData->GetCombinedTrailer();
+const CPDF_Dictionary* CPDF_Parser::GetEncryptDict() const {
+  if (!GetTrailer())
+    return nullptr;
+
+  const CPDF_Object* pEncryptObj = GetTrailer()->GetObjectFor("Encrypt");
+  if (!pEncryptObj)
+    return nullptr;
+
+  if (pEncryptObj->IsDictionary())
+    return ToDictionary(pEncryptObj);
+
+  if (pEncryptObj->IsReference()) {
+    return ToDictionary(m_pObjectsHolder->GetOrParseIndirectObject(
+        pEncryptObj->AsReference()->GetRefObjNum()));
+  }
+  return nullptr;
 }
 
-uint32_t CPDF_Parser::GetInfoObjNum() {
-  return m_TrailerData->GetInfoObjNum();
+ByteString CPDF_Parser::GetEncodedPassword() const {
+  return GetSecurityHandler()->GetEncodedPassword(GetPassword().AsStringView());
 }
 
-uint32_t CPDF_Parser::GetRootObjNum() {
-  return m_TrailerData->GetRootObjNum();
+const CPDF_Dictionary* CPDF_Parser::GetTrailer() const {
+  return m_CrossRefTable->trailer();
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Parser::ParseIndirectObject(
-    CPDF_IndirectObjectHolder* pObjList,
-    uint32_t objnum) {
+CPDF_Dictionary* CPDF_Parser::GetMutableTrailerForTesting() {
+  return m_CrossRefTable->GetMutableTrailerForTesting();
+}
+
+RetainPtr<CPDF_Dictionary> CPDF_Parser::GetCombinedTrailer() const {
+  return m_CrossRefTable->trailer()
+             ? ToDictionary(m_CrossRefTable->trailer()->Clone())
+             : RetainPtr<CPDF_Dictionary>();
+}
+
+uint32_t CPDF_Parser::GetInfoObjNum() const {
+  const CPDF_Reference* pRef =
+      ToReference(m_CrossRefTable->trailer()
+                      ? m_CrossRefTable->trailer()->GetObjectFor("Info")
+                      : nullptr);
+  return pRef ? pRef->GetRefObjNum() : CPDF_Object::kInvalidObjNum;
+}
+
+uint32_t CPDF_Parser::GetRootObjNum() const {
+  const CPDF_Reference* pRef =
+      ToReference(m_CrossRefTable->trailer()
+                      ? m_CrossRefTable->trailer()->GetObjectFor("Root")
+                      : nullptr);
+  return pRef ? pRef->GetRefObjNum() : CPDF_Object::kInvalidObjNum;
+}
+
+RetainPtr<CPDF_Object> CPDF_Parser::ParseIndirectObject(uint32_t objnum) {
   if (!IsValidObjectNumber(objnum))
     return nullptr;
 
@@ -1197,130 +863,95 @@
     return nullptr;
 
   pdfium::ScopedSetInsertion<uint32_t> local_insert(&m_ParsingObjNums, objnum);
-  if (GetObjectType(objnum) == ObjectType::kNotCompressed ||
-      GetObjectType(objnum) == ObjectType::kNull) {
-    FX_FILESIZE pos = m_ObjectInfo[objnum].pos;
+  if (GetObjectType(objnum) == ObjectType::kNotCompressed) {
+    FX_FILESIZE pos = GetObjectPositionOrZero(objnum);
     if (pos <= 0)
       return nullptr;
-    return ParseIndirectObjectAt(pObjList, pos, objnum);
+    return ParseIndirectObjectAt(pos, objnum);
   }
   if (GetObjectType(objnum) != ObjectType::kCompressed)
     return nullptr;
 
-  RetainPtr<CPDF_StreamAcc> pObjStream =
-      GetObjectStream(m_ObjectInfo[objnum].pos);
+  const CPDF_ObjectStream* pObjStream =
+      GetObjectStream(m_CrossRefTable->GetObjectInfo(objnum)->archive_obj_num);
   if (!pObjStream)
     return nullptr;
 
-  auto file = pdfium::MakeRetain<CFX_MemoryStream>(
-      const_cast<uint8_t*>(pObjStream->GetData()),
-      static_cast<size_t>(pObjStream->GetSize()), false);
-  CPDF_SyntaxParser syntax;
-  syntax.InitParser(file, 0);
-  const int32_t offset = GetStreamFirst(pObjStream);
-
-  // Read object numbers from |pObjStream| into a cache.
-  if (!pdfium::ContainsKey(m_ObjCache, pObjStream)) {
-    for (int32_t i = GetStreamNCount(pObjStream); i > 0; --i) {
-      uint32_t thisnum = syntax.GetDirectNum();
-      uint32_t thisoff = syntax.GetDirectNum();
-      m_ObjCache[pObjStream][thisnum] = thisoff;
-    }
-  }
-
-  const auto it = m_ObjCache[pObjStream].find(objnum);
-  if (it == m_ObjCache[pObjStream].end())
-    return nullptr;
-
-  syntax.SetPos(offset + it->second);
-  return syntax.GetObjectBody(pObjList);
+  return pObjStream->ParseObject(m_pObjectsHolder.Get(), objnum);
 }
 
-RetainPtr<CPDF_StreamAcc> CPDF_Parser::GetObjectStream(uint32_t objnum) {
-  auto it = m_ObjectStreamMap.find(objnum);
+const CPDF_ObjectStream* CPDF_Parser::GetObjectStream(uint32_t object_number) {
+  // Prevent circular parsing the same object.
+  if (pdfium::ContainsKey(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;
+    return it->second.get();
 
-  if (!m_pDocument)
+  const auto* info = m_CrossRefTable->GetObjectInfo(object_number);
+  if (!info || info->type != ObjectType::kObjStream)
     return nullptr;
 
-  const CPDF_Stream* pStream =
-      ToStream(m_pDocument->GetOrParseIndirectObject(objnum));
-  if (!pStream)
+  const FX_FILESIZE object_pos = info->pos;
+  if (object_pos <= 0)
     return nullptr;
 
-  auto pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-  pStreamAcc->LoadAllDataFiltered();
-  m_ObjectStreamMap[objnum] = pStreamAcc;
-  return pStreamAcc;
+  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()));
+  const CPDF_ObjectStream* result = objs_stream.get();
+  m_ObjectStreamMap[object_number] = std::move(objs_stream);
+
+  return result;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Parser::ParseIndirectObjectAt(
-    CPDF_IndirectObjectHolder* pObjList,
-    FX_FILESIZE pos,
-    uint32_t objnum) {
-  return ParseIndirectObjectAtInternal(
-      pObjList, pos, objnum, CPDF_SyntaxParser::ParseType::kLoose, nullptr);
-}
-
-std::unique_ptr<CPDF_Object> CPDF_Parser::ParseIndirectObjectAtInternal(
-    CPDF_IndirectObjectHolder* pObjList,
-    FX_FILESIZE pos,
-    uint32_t objnum,
-    CPDF_SyntaxParser::ParseType parse_type,
-    FX_FILESIZE* pResultPos) {
+RetainPtr<CPDF_Object> CPDF_Parser::ParseIndirectObjectAt(FX_FILESIZE pos,
+                                                          uint32_t objnum) {
   const FX_FILESIZE saved_pos = m_pSyntax->GetPos();
   m_pSyntax->SetPos(pos);
-  auto result = m_pSyntax->GetIndirectObject(pObjList, parse_type);
 
-  if (pResultPos)
-    *pResultPos = m_pSyntax->GetPos();
+  auto result = m_pSyntax->GetIndirectObject(
+      m_pObjectsHolder.Get(), CPDF_SyntaxParser::ParseType::kLoose);
   m_pSyntax->SetPos(saved_pos);
-
   if (result && objnum && result->GetObjNum() != objnum)
     return nullptr;
 
   const bool should_decrypt = m_pSecurityHandler &&
                               m_pSecurityHandler->GetCryptoHandler() &&
                               objnum != m_MetadataObjnum;
-  if (should_decrypt)
-    result = m_pSecurityHandler->GetCryptoHandler()->DecryptObjectTree(
-        std::move(result));
-
+  if (should_decrypt &&
+      !m_pSecurityHandler->GetCryptoHandler()->DecryptObjectTree(result)) {
+    return nullptr;
+  }
   return result;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Parser::ParseIndirectObjectAtByStrict(
-    CPDF_IndirectObjectHolder* pObjList,
-    FX_FILESIZE pos,
-    uint32_t objnum,
-    FX_FILESIZE* pResultPos) {
-  return ParseIndirectObjectAtInternal(
-      pObjList, pos, objnum, CPDF_SyntaxParser::ParseType::kStrict, pResultPos);
-}
-
 uint32_t CPDF_Parser::GetFirstPageNo() const {
   return m_pLinearized ? m_pLinearized->GetFirstPageNo() : 0;
 }
 
-std::unique_ptr<CPDF_Dictionary> CPDF_Parser::LoadTrailerV4() {
+void CPDF_Parser::SetLinearizedHeader(
+    std::unique_ptr<CPDF_LinearizedHeader> pLinearized) {
+  m_pLinearized = std::move(pLinearized);
+}
+
+RetainPtr<CPDF_Dictionary> CPDF_Parser::LoadTrailerV4() {
   if (m_pSyntax->GetKeyword() != "trailer")
     return nullptr;
 
-  return ToDictionary(m_pSyntax->GetObjectBody(m_pDocument.Get()));
+  return ToDictionary(m_pSyntax->GetObjectBody(m_pObjectsHolder.Get()));
 }
 
 uint32_t CPDF_Parser::GetPermissions() const {
-  if (!m_pSecurityHandler)
-    return 0xFFFFFFFF;
-
-  uint32_t dwPermission = m_pSecurityHandler->GetPermissions();
-  if (m_pEncryptDict && m_pEncryptDict->GetStringFor("Filter") == "Standard") {
-    // See PDF Reference 1.7, page 123, table 3.20.
-    dwPermission &= 0xFFFFFFFC;
-    dwPermission |= 0xFFFFF0C0;
-  }
-  return dwPermission;
+  return m_pSecurityHandler ? m_pSecurityHandler->GetPermissions() : 0xFFFFFFFF;
 }
 
 std::unique_ptr<CPDF_LinearizedHeader> CPDF_Parser::ParseLinearizedHeader() {
@@ -1328,39 +959,39 @@
 }
 
 CPDF_Parser::Error CPDF_Parser::StartLinearizedParse(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-    CPDF_Document* pDocument) {
+    const RetainPtr<CPDF_ReadValidator>& validator,
+    const char* password) {
   ASSERT(!m_bHasParsed);
+  ASSERT(!m_bXRefTableRebuilt);
+  SetPassword(password);
   m_bXRefStream = false;
   m_LastXRefOffset = 0;
 
-  if (!InitSyntaxParser(pFileAccess))
+  if (!InitSyntaxParser(validator))
     return FORMAT_ERROR;
 
   m_pLinearized = ParseLinearizedHeader();
   if (!m_pLinearized)
-    return StartParseInternal(std::move(pDocument));
+    return StartParseInternal();
 
   m_bHasParsed = true;
-  m_pDocument = pDocument;
 
   m_LastXRefOffset = m_pLinearized->GetLastXRefOffset();
   FX_FILESIZE dwFirstXRefOffset = m_LastXRefOffset;
-  bool bXRefRebuilt = false;
   bool bLoadV4 = LoadCrossRefV4(dwFirstXRefOffset, false);
   if (!bLoadV4 && !LoadCrossRefV5(&dwFirstXRefOffset, true)) {
     if (!RebuildCrossRef())
       return FORMAT_ERROR;
 
-    bXRefRebuilt = true;
+    m_bXRefTableRebuilt = true;
     m_LastXRefOffset = 0;
   }
   if (bLoadV4) {
-    std::unique_ptr<CPDF_Dictionary> trailer = LoadTrailerV4();
+    RetainPtr<CPDF_Dictionary> trailer = LoadTrailerV4();
     if (!trailer)
       return SUCCESS;
 
-    m_TrailerData->SetMainTrailer(std::move(trailer));
+    m_CrossRefTable->SetTrailer(std::move(trailer));
     int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size");
     if (xrefsize > 0)
       ShrinkObjectMap(xrefsize);
@@ -1370,9 +1001,8 @@
   if (eRet != SUCCESS)
     return eRet;
 
-  m_pDocument->LoadLinearizedDoc(m_pLinearized.get());
-  if (!m_pDocument->GetRoot() || m_pDocument->GetPageCount() == 0) {
-    if (bXRefRebuilt)
+  if (!GetRoot() || !m_pObjectsHolder->TryInit()) {
+    if (m_bXRefTableRebuilt)
       return FORMAT_ERROR;
 
     ReleaseEncryptHandler();
@@ -1383,14 +1013,14 @@
     if (eRet != SUCCESS)
       return eRet;
 
-    m_pDocument->LoadLinearizedDoc(m_pLinearized.get());
-    if (!m_pDocument->GetRoot())
+    m_pObjectsHolder->TryInit();
+    if (!GetRoot())
       return FORMAT_ERROR;
   }
 
-  if (GetRootObjNum() == 0) {
+  if (GetRootObjNum() == CPDF_Object::kInvalidObjNum) {
     ReleaseEncryptHandler();
-    if (!RebuildCrossRef() || GetRootObjNum() == 0)
+    if (!RebuildCrossRef() || GetRootObjNum() == CPDF_Object::kInvalidObjNum)
       return FORMAT_ERROR;
 
     eRet = SetEncryptHandler();
@@ -1400,24 +1030,25 @@
 
   if (m_pSecurityHandler && m_pSecurityHandler->IsMetadataEncrypted()) {
     if (CPDF_Reference* pMetadata =
-            ToReference(m_pDocument->GetRoot()->GetObjectFor("Metadata")))
+            ToReference(GetRoot()->GetObjectFor("Metadata")))
       m_MetadataObjnum = pMetadata->GetRefObjNum();
   }
   return SUCCESS;
 }
 
-bool CPDF_Parser::LoadLinearizedAllCrossRefV5(FX_FILESIZE xrefpos) {
-  if (!LoadCrossRefV5(&xrefpos, false))
+bool CPDF_Parser::LoadLinearizedAllCrossRefV5(FX_FILESIZE main_xref_offset) {
+  FX_FILESIZE xref_offset = main_xref_offset;
+  if (!LoadCrossRefV5(&xref_offset, false))
     return false;
 
-  std::set<FX_FILESIZE> seen_xrefpos;
-  while (xrefpos) {
-    seen_xrefpos.insert(xrefpos);
-    if (!LoadCrossRefV5(&xrefpos, false))
+  std::set<FX_FILESIZE> seen_xref_offset;
+  while (xref_offset) {
+    seen_xref_offset.insert(xref_offset);
+    if (!LoadCrossRefV5(&xref_offset, false))
       return false;
 
     // Check for circular references.
-    if (pdfium::ContainsKey(seen_xrefpos, xrefpos))
+    if (pdfium::ContainsKey(seen_xref_offset, xref_offset))
       return false;
   }
   m_ObjectStreamMap.clear();
@@ -1426,20 +1057,20 @@
 }
 
 CPDF_Parser::Error CPDF_Parser::LoadLinearizedMainXRefTable() {
-  const FX_SAFE_FILESIZE main_xref_offset = GetTrailer()->GetIntegerFor("Prev");
-  if (!main_xref_offset.IsValid())
+  const FX_SAFE_FILESIZE prev = GetTrailer()->GetIntegerFor("Prev");
+  const FX_FILESIZE main_xref_offset = prev.ValueOrDefault(-1);
+  if (main_xref_offset < 0)
     return FORMAT_ERROR;
 
-  if (main_xref_offset.ValueOrDie() == 0)
+  if (main_xref_offset == 0)
     return SUCCESS;
 
   const AutoRestorer<uint32_t> save_metadata_objnum(&m_MetadataObjnum);
   m_MetadataObjnum = 0;
   m_ObjectStreamMap.clear();
-  m_ObjCache.clear();
 
-  if (!LoadLinearizedAllCrossRefV4(main_xref_offset.ValueOrDie()) &&
-      !LoadLinearizedAllCrossRefV5(main_xref_offset.ValueOrDie())) {
+  if (!LoadLinearizedAllCrossRefV4(main_xref_offset) &&
+      !LoadLinearizedAllCrossRefV5(main_xref_offset)) {
     m_LastXRefOffset = 0;
     return FORMAT_ERROR;
   }
@@ -1448,7 +1079,7 @@
 }
 
 CPDF_Parser::ObjectType CPDF_Parser::GetObjectTypeFromCrossRefStreamType(
-    int cross_ref_stream_type) const {
+    uint32_t cross_ref_stream_type) const {
   switch (cross_ref_stream_type) {
     case 0:
       return CPDF_Parser::ObjectType::kFree;
diff --git a/core/fpdfapi/parser/cpdf_parser.h b/core/fpdfapi/parser/cpdf_parser.h
index 45b8cb9..d44244a 100644
--- a/core/fpdfapi/parser/cpdf_parser.h
+++ b/core/fpdfapi/parser/cpdf_parser.h
@@ -13,7 +13,8 @@
 #include <set>
 #include <vector>
 
-#include "core/fpdfapi/parser/cpdf_syntax_parser.h"
+#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/retain_ptr.h"
@@ -22,17 +23,21 @@
 class CPDF_Array;
 class CPDF_CryptoHandler;
 class CPDF_Dictionary;
-class CPDF_Document;
-class CPDF_IndirectObjectHolder;
 class CPDF_LinearizedHeader;
 class CPDF_Object;
+class CPDF_ObjectStream;
+class CPDF_ReadValidator;
 class CPDF_SecurityHandler;
-class CPDF_StreamAcc;
 class CPDF_SyntaxParser;
 class IFX_SeekableReadStream;
 
 class CPDF_Parser {
  public:
+  class ParsedObjectsHolder : public CPDF_IndirectObjectHolder {
+   public:
+    virtual bool TryInit() = 0;
+  };
+
   enum Error {
     SUCCESS = 0,
     FILE_ERROR,
@@ -43,137 +48,112 @@
 
   // A limit on the maximum object number in the xref table. Theoretical limits
   // are higher, but this may be large enough in practice.
-  static const uint32_t kMaxObjectNumber = 1048576;
+  // Note: This was 1M, but https://crbug.com/910009 encountered a PDF with
+  // object numbers in the 1.7M range. The PDF only has 10K objects, but they
+  // are non-consecutive.
+  static constexpr uint32_t kMaxObjectNumber = 4 * 1024 * 1024;
 
   static const 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,
-                   CPDF_Document* pDocument);
-  Error StartLinearizedParse(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                             CPDF_Document* pDocument);
+                   const char* password);
+  Error StartLinearizedParse(const RetainPtr<CPDF_ReadValidator>& validator,
+                             const char* password);
 
   void SetPassword(const char* password) { m_Password = password; }
-  ByteString GetPassword() { return m_Password; }
+  ByteString GetPassword() const { return m_Password; }
 
-  CPDF_Dictionary* GetTrailer() const;
+  // Take the GetPassword() value and encode it, if necessary, based on the
+  // password encoding conversion.
+  ByteString GetEncodedPassword() const;
+
+  const CPDF_Dictionary* GetTrailer() const;
+  CPDF_Dictionary* GetMutableTrailerForTesting();
 
   // Returns a new trailer which combines the last read trailer with the /Root
   // and /Info from previous ones.
-  std::unique_ptr<CPDF_Dictionary> GetCombinedTrailer() const;
+  RetainPtr<CPDF_Dictionary> GetCombinedTrailer() const;
 
   FX_FILESIZE GetLastXRefOffset() const { return m_LastXRefOffset; }
 
   uint32_t GetPermissions() const;
-  uint32_t GetRootObjNum();
-  uint32_t GetInfoObjNum();
+  uint32_t GetRootObjNum() const;
+  uint32_t GetInfoObjNum() const;
   const CPDF_Array* GetIDArray() const;
+  CPDF_Dictionary* GetRoot() const;
 
-  CPDF_Dictionary* GetEncryptDict() const { return m_pEncryptDict.Get(); }
+  const CPDF_Dictionary* GetEncryptDict() const;
 
-  std::unique_ptr<CPDF_Object> ParseIndirectObject(
-      CPDF_IndirectObjectHolder* pObjList,
-      uint32_t objnum);
+  RetainPtr<CPDF_Object> ParseIndirectObject(uint32_t objnum);
 
   uint32_t GetLastObjNum() const;
   bool IsValidObjectNumber(uint32_t objnum) const;
   FX_FILESIZE GetObjectPositionOrZero(uint32_t objnum) const;
-  uint16_t GetObjectGenNum(uint32_t objnum) const;
   bool IsObjectFreeOrNull(uint32_t objnum) const;
-  CPDF_SecurityHandler* GetSecurityHandler() const {
-    return m_pSecurityHandler.get();
+  const RetainPtr<CPDF_SecurityHandler>& GetSecurityHandler() const {
+    return m_pSecurityHandler;
   }
-  RetainPtr<IFX_SeekableReadStream> GetFileAccess() const;
   bool IsObjectFree(uint32_t objnum) const;
 
-  FX_FILESIZE GetObjectOffset(uint32_t objnum) const;
-
   int GetFileVersion() const { return m_FileVersion; }
   bool IsXRefStream() const { return m_bXRefStream; }
 
-  std::unique_ptr<CPDF_Object> ParseIndirectObjectAt(
-      CPDF_IndirectObjectHolder* pObjList,
-      FX_FILESIZE pos,
-      uint32_t objnum);
-
-  std::unique_ptr<CPDF_Object> ParseIndirectObjectAtByStrict(
-      CPDF_IndirectObjectHolder* pObjList,
-      FX_FILESIZE pos,
-      uint32_t objnum,
-      FX_FILESIZE* pResultPos);
+  RetainPtr<CPDF_Object> ParseIndirectObjectAt(FX_FILESIZE pos,
+                                               uint32_t objnum);
 
   uint32_t GetFirstPageNo() const;
+  const CPDF_LinearizedHeader* GetLinearizedHeader() const {
+    return m_pLinearized.get();
+  }
+
+  const CPDF_CrossRefTable* GetCrossRefTable() const {
+    return m_CrossRefTable.get();
+  }
+
+  bool xref_table_rebuilt() const { return m_bXRefTableRebuilt; }
+
+  CPDF_SyntaxParser* GetSyntax() const { return m_pSyntax.get(); }
+
+  void SetLinearizedHeader(std::unique_ptr<CPDF_LinearizedHeader> pLinearized);
 
  protected:
-  enum class ObjectType : uint8_t {
-    kFree = 0x00,
-    kNotCompressed = 0x01,
-    kCompressed = 0x02,
-    kNull = 0xFF,
-  };
-
-  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.
-    union {
-      FX_FILESIZE pos;
-      FX_FILESIZE archive_obj_num;
-    };
-    ObjectType type;
-    uint16_t gennum;
-  };
-
-  std::unique_ptr<CPDF_SyntaxParser> m_pSyntax;
-  std::map<uint32_t, ObjectInfo> m_ObjectInfo;
+  using ObjectType = CPDF_CrossRefTable::ObjectType;
+  using ObjectInfo = CPDF_CrossRefTable::ObjectInfo;
 
   bool LoadCrossRefV4(FX_FILESIZE pos, bool bSkip);
   bool RebuildCrossRef();
 
+  std::unique_ptr<CPDF_SyntaxParser> m_pSyntax;
+
  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;
 
-  class TrailerData;
-
-  enum class ParserState {
-    kDefault,
-    kComment,
-    kWhitespace,
-    kString,
-    kHexString,
-    kEscapedString,
-    kXref,
-    kObjNum,
-    kPostObjNum,
-    kGenNum,
-    kPostGenNum,
-    kTrailer,
-    kBeginObj,
-    kEndObj
-  };
-
   struct CrossRefObjData {
     uint32_t obj_num = 0;
     ObjectInfo info;
   };
 
-  Error StartParseInternal(CPDF_Document* pDocument);
+  Error StartParseInternal();
   FX_FILESIZE ParseStartXRef();
-  bool LoadAllCrossRefV4(FX_FILESIZE pos);
-  bool LoadAllCrossRefV5(FX_FILESIZE pos);
+  bool LoadAllCrossRefV4(FX_FILESIZE xref_offset);
+  bool LoadAllCrossRefV5(FX_FILESIZE xref_offset);
   bool LoadCrossRefV5(FX_FILESIZE* pos, bool bMainXRef);
-  std::unique_ptr<CPDF_Dictionary> LoadTrailerV4();
+  RetainPtr<CPDF_Dictionary> LoadTrailerV4();
   Error SetEncryptHandler();
   void ReleaseEncryptHandler();
-  bool LoadLinearizedAllCrossRefV4(FX_FILESIZE pos);
-  bool LoadLinearizedAllCrossRefV5(FX_FILESIZE pos);
+  bool LoadLinearizedAllCrossRefV4(FX_FILESIZE main_xref_offset);
+  bool LoadLinearizedAllCrossRefV5(FX_FILESIZE main_xref_offset);
   Error LoadLinearizedMainXRefTable();
-  RetainPtr<CPDF_StreamAcc> GetObjectStream(uint32_t number);
+  const CPDF_ObjectStream* GetObjectStream(uint32_t object_number);
   std::unique_ptr<CPDF_LinearizedHeader> ParseLinearizedHeader();
-  void SetEncryptDictionary(CPDF_Dictionary* pDict);
   void ShrinkObjectMap(uint32_t size);
   // A simple check whether the cross reference table matches with
   // the objects.
@@ -188,43 +168,30 @@
   bool ParseCrossRefV4(std::vector<CrossRefObjData>* out_objects);
   void MergeCrossRefObjectsData(const std::vector<CrossRefObjData>& objects);
 
-  std::unique_ptr<CPDF_Object> ParseIndirectObjectAtInternal(
-      CPDF_IndirectObjectHolder* pObjList,
-      FX_FILESIZE pos,
-      uint32_t objnum,
-      CPDF_SyntaxParser::ParseType parse_type,
-      FX_FILESIZE* pResultPos);
-
-  bool InitSyntaxParser(const RetainPtr<IFX_SeekableReadStream>& file_access);
+  bool InitSyntaxParser(const RetainPtr<CPDF_ReadValidator>& validator);
   bool ParseFileVersion();
 
-  UnownedPtr<CPDF_Document> m_pDocument;
   ObjectType GetObjectType(uint32_t objnum) const;
   ObjectType GetObjectTypeFromCrossRefStreamType(
-      int cross_ref_stream_type) const;
+      uint32_t cross_ref_stream_type) const;
 
-  bool m_bHasParsed;
-  bool m_bXRefStream;
-  int m_FileVersion;
-  // m_TrailerData must be destroyed after m_pSecurityHandler due to the
+  std::unique_ptr<ParsedObjectsHolder> m_pOwnedObjectsHolder;
+  UnownedPtr<ParsedObjectsHolder> m_pObjectsHolder;
+
+  bool m_bHasParsed = false;
+  bool m_bXRefStream = false;
+  bool m_bXRefTableRebuilt = false;
+  int m_FileVersion = 0;
+  // m_CrossRefTable must be destroyed after m_pSecurityHandler due to the
   // ownership of the ID array data.
-  std::unique_ptr<TrailerData> m_TrailerData;
-  UnownedPtr<CPDF_Dictionary> m_pEncryptDict;
+  std::unique_ptr<CPDF_CrossRefTable> m_CrossRefTable;
   FX_FILESIZE m_LastXRefOffset;
-  std::unique_ptr<CPDF_SecurityHandler> m_pSecurityHandler;
+  RetainPtr<CPDF_SecurityHandler> m_pSecurityHandler;
   ByteString m_Password;
   std::unique_ptr<CPDF_LinearizedHeader> m_pLinearized;
 
   // A map of object numbers to indirect streams.
-  std::map<uint32_t, RetainPtr<CPDF_StreamAcc>> m_ObjectStreamMap;
-
-  // Mapping of object numbers to offsets. The offsets are relative to the first
-  // object in the stream.
-  using StreamObjectCache = std::map<uint32_t, uint32_t>;
-
-  // Mapping of streams to their object caches. This is valid as long as the
-  // streams in |m_ObjectStreamMap| are valid.
-  std::map<RetainPtr<CPDF_StreamAcc>, StreamObjectCache> m_ObjCache;
+  std::map<uint32_t, std::unique_ptr<CPDF_ObjectStream>> m_ObjectStreamMap;
 
   // All indirect object numbers that are being parsed.
   std::set<uint32_t> m_ParsingObjNums;
diff --git a/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp b/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp
index 4109715..9294534 100644
--- a/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp
@@ -6,15 +6,15 @@
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CPDFParserEmbeddertest : public EmbedderTest {};
+class CPDFParserEmbedderTest : public EmbedderTest {};
 
-TEST_F(CPDFParserEmbeddertest, LoadError_454695) {
+TEST_F(CPDFParserEmbedderTest, LoadError_454695) {
   // 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, Bug_481363) {
   // Test colorspace object with malformed dictionary.
   EXPECT_TRUE(OpenDocument("bug_481363.pdf"));
   FPDF_PAGE page = LoadPage(0);
@@ -22,7 +22,7 @@
   UnloadPage(page);
 }
 
-TEST_F(CPDFParserEmbeddertest, Bug_544880) {
+TEST_F(CPDFParserEmbedderTest, Bug_544880) {
   // Test self referencing /Pages object.
   EXPECT_TRUE(OpenDocument("bug_544880.pdf"));
   // Shouldn't crash. We don't check the return value here because we get the
@@ -31,15 +31,15 @@
   (void)GetPageCount();
 }
 
-TEST_F(CPDFParserEmbeddertest, Bug_325a) {
+TEST_F(CPDFParserEmbedderTest, Bug_325a) {
   EXPECT_FALSE(OpenDocument("bug_325_a.pdf"));
 }
 
-TEST_F(CPDFParserEmbeddertest, Bug_325b) {
+TEST_F(CPDFParserEmbedderTest, Bug_325b) {
   EXPECT_FALSE(OpenDocument("bug_325_b.pdf"));
 }
 
-TEST_F(CPDFParserEmbeddertest, Bug_602650) {
+TEST_F(CPDFParserEmbedderTest, Bug_602650) {
   // Test the case that cross reference entries, which are well formed,
   // but do not match with the objects.
   EXPECT_TRUE(OpenDocument("bug_602650.pdf"));
@@ -54,11 +54,11 @@
   UnloadPage(page);
 }
 
-TEST_F(CPDFParserEmbeddertest, Bug_757705) {
+TEST_F(CPDFParserEmbedderTest, Bug_757705) {
   EXPECT_TRUE(OpenDocument("bug_757705.pdf"));
 }
 
-TEST_F(CPDFParserEmbeddertest, LoadMainCrossRefTable) {
+TEST_F(CPDFParserEmbedderTest, LoadMainCrossRefTable) {
   EXPECT_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
@@ -73,3 +73,10 @@
   FPDFText_ClosePage(text_page);
   UnloadPage(page);
 }
+
+TEST_F(CPDFParserEmbedderTest, Bug_828049) {
+  EXPECT_TRUE(OpenDocument("bug_828049.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+  UnloadPage(page);
+}
diff --git a/core/fpdfapi/parser/cpdf_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_parser_unittest.cpp
index 555c35c..06328f0 100644
--- a/core/fpdfapi/parser/cpdf_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_parser_unittest.cpp
@@ -3,19 +3,34 @@
 // found in the LICENSE file.
 
 #include <limits>
+#include <memory>
 #include <string>
+#include <vector>
 
+#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/fx_extension.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+CPDF_CrossRefTable::ObjectInfo GetObjInfo(const CPDF_Parser& parser,
+                                          uint32_t obj_num) {
+  const auto* info = parser.GetCrossRefTable()->GetObjectInfo(obj_num);
+  return info ? *info : CPDF_CrossRefTable::ObjectInfo();
+}
+
+}  // namespace
 
 // A wrapper class to help test member functions of CPDF_Parser.
-class CPDF_TestParser : public CPDF_Parser {
+class CPDF_TestParser final : public CPDF_Parser {
  public:
   CPDF_TestParser() {}
   ~CPDF_TestParser() {}
@@ -28,18 +43,22 @@
       return false;
 
     // For the test file, the header is set at the beginning.
-    m_pSyntax->InitParser(pFileAccess, 0);
+    m_pSyntax = pdfium::MakeUnique<CPDF_SyntaxParser>(pFileAccess);
     return true;
   }
 
   // Setup reading from a buffer and initial states.
-  bool InitTestFromBuffer(const unsigned char* buffer, size_t len) {
-    // For the test file, the header is set at the beginning.
-    m_pSyntax->InitParser(
-        pdfium::MakeRetain<CFX_BufferSeekableReadStream>(buffer, len), 0);
+  bool InitTestFromBufferWithOffset(pdfium::span<const uint8_t> buffer,
+                                    FX_FILESIZE header_offset) {
+    m_pSyntax = CPDF_SyntaxParser::CreateForTesting(
+        pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(buffer), header_offset);
     return true;
   }
 
+  bool InitTestFromBuffer(pdfium::span<const uint8_t> buffer) {
+    return InitTestFromBufferWithOffset(buffer, 0 /*header_offset*/);
+  }
+
  private:
   // Add test cases here as private friend so that protected members in
   // CPDF_Parser can be accessed by test cases.
@@ -61,9 +80,9 @@
   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)
-    EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
+    EXPECT_EQ(offsets[i], GetObjInfo(parser, i).pos);
   for (size_t i = 0; i < FX_ArraySize(versions); ++i)
-    EXPECT_EQ(versions[i], parser.m_ObjectInfo[i].gennum);
+    EXPECT_EQ(versions[i], GetObjInfo(parser, i).gennum);
 }
 
 TEST(cpdf_parser, RebuildCrossRefFailed) {
@@ -78,7 +97,7 @@
 
 TEST(cpdf_parser, LoadCrossRefV4) {
   {
-    const unsigned char xref_table[] =
+    static const unsigned char kXrefTable[] =
         "xref \n"
         "0 6 \n"
         "0000000003 65535 f \n"
@@ -89,25 +108,26 @@
         "0000000409 00000 n \n"
         "trail";  // Needed to end cross ref table reading.
     CPDF_TestParser parser;
-    ASSERT_TRUE(
-        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
+    ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
 
     ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
-    const FX_FILESIZE offsets[] = {0, 17, 81, 0, 331, 409};
-    const CPDF_TestParser::ObjectType types[] = {
+    static const FX_FILESIZE kOffsets[] = {0, 17, 81, 0, 331, 409};
+    static const CPDF_TestParser::ObjectType kTypes[] = {
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed,
         CPDF_TestParser::ObjectType::kNotCompressed,
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
-      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
-      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
+    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+                  "kOffsets / kTypes size mismatch");
+    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+      EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
+      EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
   }
   {
-    const unsigned char xref_table[] =
+    static const unsigned char kXrefTable[] =
         "xref \n"
         "0 1 \n"
         "0000000000 65535 f \n"
@@ -120,13 +140,12 @@
         "0000025777 00000 n \n"
         "trail";  // Needed to end cross ref table reading.
     CPDF_TestParser parser;
-    ASSERT_TRUE(
-        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
+    ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
 
     ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
-    const FX_FILESIZE offsets[] = {0, 0,     0,     25325, 0, 0,    0,
-                                   0, 25518, 25635, 0,     0, 25777};
-    const CPDF_TestParser::ObjectType types[] = {
+    static const FX_FILESIZE kOffsets[] = {0, 0,     0,     25325, 0, 0,    0,
+                                           0, 25518, 25635, 0,     0, 25777};
+    static const CPDF_TestParser::ObjectType kTypes[] = {
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
@@ -140,13 +159,15 @@
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
-      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
-      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
+    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+                  "kOffsets / kTypes size mismatch");
+    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+      EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
+      EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
   }
   {
-    const unsigned char xref_table[] =
+    static const unsigned char kXrefTable[] =
         "xref \n"
         "0 1 \n"
         "0000000000 65535 f \n"
@@ -159,13 +180,12 @@
         "0000025777 00000 n \n"
         "trail";  // Needed to end cross ref table reading.
     CPDF_TestParser parser;
-    ASSERT_TRUE(
-        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
+    ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
 
     ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
-    const FX_FILESIZE offsets[] = {0, 0, 0,     25325, 0, 0,    0,
-                                   0, 0, 25635, 0,     0, 25777};
-    const CPDF_TestParser::ObjectType types[] = {
+    static const FX_FILESIZE kOffsets[] = {0, 0, 0,     25325, 0, 0,    0,
+                                           0, 0, 25635, 0,     0, 25777};
+    static const CPDF_TestParser::ObjectType kTypes[] = {
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
@@ -179,13 +199,15 @@
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
-      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
-      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
+    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+                  "kOffsets / kTypes size mismatch");
+    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+      EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
+      EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
   }
   {
-    const unsigned char xref_table[] =
+    static const unsigned char kXrefTable[] =
         "xref \n"
         "0 7 \n"
         "0000000002 65535 f \n"
@@ -197,12 +219,11 @@
         "0000000179 00000 n \n"
         "trail";  // Needed to end cross ref table reading.
     CPDF_TestParser parser;
-    ASSERT_TRUE(
-        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
+    ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
 
     ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
-    const FX_FILESIZE offsets[] = {0, 23, 0, 0, 0, 45, 179};
-    const CPDF_TestParser::ObjectType types[] = {
+    static const FX_FILESIZE kOffsets[] = {0, 23, 0, 0, 0, 45, 179};
+    static const CPDF_TestParser::ObjectType kTypes[] = {
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed,
         CPDF_TestParser::ObjectType::kFree,
@@ -210,9 +231,105 @@
         CPDF_TestParser::ObjectType::kFree,
         CPDF_TestParser::ObjectType::kNotCompressed,
         CPDF_TestParser::ObjectType::kNotCompressed};
-    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
-      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
-      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
+    static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes),
+                  "kOffsets / kTypes size mismatch");
+    for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) {
+      EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
+      EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
     }
   }
+  {
+    // Regression test for https://crbug.com/945624 - Make sure the parser
+    // can correctly handle table sizes that are multiples of the read size,
+    // which is 1024.
+    std::string xref_table = "xref \n 0 2048 \n";
+    xref_table.reserve(41000);
+    for (int i = 0; i < 2048; ++i) {
+      char buffer[21];
+      snprintf(buffer, sizeof(buffer), "%010d 00000 n \n", i + 1);
+      xref_table += buffer;
+    }
+    xref_table += "trail";  // Needed to end cross ref table reading.
+    CPDF_TestParser parser;
+    ASSERT_TRUE(parser.InitTestFromBuffer(
+        pdfium::make_span(reinterpret_cast<const uint8_t*>(xref_table.c_str()),
+                          xref_table.size())));
+
+    ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
+    for (size_t i = 0; i < 2048; ++i) {
+      EXPECT_EQ(static_cast<int>(i) + 1, GetObjInfo(parser, i).pos);
+      EXPECT_EQ(CPDF_TestParser::ObjectType::kNotCompressed,
+                GetObjInfo(parser, i).type);
+    }
+  }
+}
+
+TEST(cpdf_parser, ParseStartXRef) {
+  CPDF_TestParser parser;
+  std::string test_file;
+  ASSERT_TRUE(
+      PathService::GetTestFilePath("annotation_stamp_with_ap.pdf", &test_file));
+  ASSERT_TRUE(parser.InitTestFromFile(test_file.c_str())) << test_file;
+
+  EXPECT_EQ(100940, parser.ParseStartXRef());
+  RetainPtr<CPDF_Object> cross_ref_v5_obj =
+      parser.ParseIndirectObjectAt(100940, 0);
+  ASSERT_TRUE(cross_ref_v5_obj);
+  EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum());
+}
+
+TEST(cpdf_parser, ParseStartXRefWithHeaderOffset) {
+  static constexpr FX_FILESIZE kTestHeaderOffset = 765;
+  std::string test_file;
+  ASSERT_TRUE(
+      PathService::GetTestFilePath("annotation_stamp_with_ap.pdf", &test_file));
+  RetainPtr<IFX_SeekableReadStream> pFileAccess =
+      IFX_SeekableReadStream::CreateFromFilename(test_file.c_str());
+  ASSERT_TRUE(pFileAccess);
+
+  std::vector<unsigned char> data(pFileAccess->GetSize() + kTestHeaderOffset);
+  ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(&data.front() + kTestHeaderOffset,
+                                             0, pFileAccess->GetSize()));
+  CPDF_TestParser parser;
+  parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset);
+
+  EXPECT_EQ(100940, parser.ParseStartXRef());
+  RetainPtr<CPDF_Object> cross_ref_v5_obj =
+      parser.ParseIndirectObjectAt(100940, 0);
+  ASSERT_TRUE(cross_ref_v5_obj);
+  EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum());
+}
+
+TEST(cpdf_parser, ParseLinearizedWithHeaderOffset) {
+  static constexpr FX_FILESIZE kTestHeaderOffset = 765;
+  std::string test_file;
+  ASSERT_TRUE(PathService::GetTestFilePath("linearized.pdf", &test_file));
+  RetainPtr<IFX_SeekableReadStream> pFileAccess =
+      IFX_SeekableReadStream::CreateFromFilename(test_file.c_str());
+  ASSERT_TRUE(pFileAccess);
+
+  std::vector<unsigned char> data(pFileAccess->GetSize() + kTestHeaderOffset);
+  ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(&data.front() + kTestHeaderOffset,
+                                             0, pFileAccess->GetSize()));
+  CPDF_TestParser parser;
+  parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset);
+
+  EXPECT_TRUE(parser.ParseLinearizedHeader());
+}
+
+TEST(cpdf_parser, BadStartXrefShouldNotBuildCrossRefTable) {
+  const unsigned char kData[] =
+      "%PDF1-7 0 obj <</Size 2 /W [0 0 0]\n>>\n"
+      "stream\n"
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "6\n"
+      "%%EOF\n";
+  CPDF_TestParser parser;
+  ASSERT_TRUE(parser.InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::FORMAT_ERROR, parser.StartParseInternal());
+  ASSERT_TRUE(parser.GetCrossRefTable());
+  EXPECT_EQ(0u, parser.GetCrossRefTable()->objects_info().size());
 }
diff --git a/core/fpdfapi/parser/cpdf_read_validator.cpp b/core/fpdfapi/parser/cpdf_read_validator.cpp
index d850561..80e6517 100644
--- a/core/fpdfapi/parser/cpdf_read_validator.cpp
+++ b/core/fpdfapi/parser/cpdf_read_validator.cpp
@@ -6,12 +6,13 @@
 
 #include <algorithm>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/logging.h"
 
 namespace {
 
-constexpr FX_FILESIZE kAlignBlockValue = CPDF_ModuleMgr::kFileBufSize;
+constexpr FX_FILESIZE kAlignBlockValue = CPDF_Stream::kFileBufSize;
 
 FX_FILESIZE AlignDown(FX_FILESIZE offset) {
   return offset > 0 ? (offset - offset % kAlignBlockValue) : 0;
@@ -20,15 +21,14 @@
 FX_FILESIZE AlignUp(FX_FILESIZE offset) {
   FX_SAFE_FILESIZE safe_result = AlignDown(offset);
   safe_result += kAlignBlockValue;
-  if (safe_result.IsValid())
-    return safe_result.ValueOrDie();
-  return offset;
+  return safe_result.ValueOrDefault(offset);
 }
 
 }  // namespace
 
-CPDF_ReadValidator::Session::Session(CPDF_ReadValidator* validator)
-    : validator_(validator) {
+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_;
@@ -57,9 +57,9 @@
   has_unavailable_data_ = false;
 }
 
-bool CPDF_ReadValidator::ReadBlock(void* buffer,
-                                   FX_FILESIZE offset,
-                                   size_t size) {
+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_)
@@ -70,7 +70,7 @@
     return false;
   }
 
-  if (file_read_->ReadBlock(buffer, offset, size))
+  if (file_read_->ReadBlockAtOffset(buffer, offset, size))
     return true;
 
   read_error_ = true;
@@ -131,7 +131,7 @@
   FX_SAFE_FILESIZE end_segment_offset = offset;
   end_segment_offset += size;
   // Increase checked range to allow CPDF_SyntaxParser read whole buffer.
-  end_segment_offset += CPDF_ModuleMgr::kFileBufSize;
+  end_segment_offset += CPDF_Stream::kFileBufSize;
   if (!end_segment_offset.IsValid()) {
     NOTREACHED();
     return false;
diff --git a/core/fpdfapi/parser/cpdf_read_validator.h b/core/fpdfapi/parser/cpdf_read_validator.h
index 31c33fa..6adde02 100644
--- a/core/fpdfapi/parser/cpdf_read_validator.h
+++ b/core/fpdfapi/parser/cpdf_read_validator.h
@@ -10,12 +10,9 @@
 
 class CPDF_ReadValidator : public IFX_SeekableReadStream {
  public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
   class Session {
    public:
-    explicit Session(CPDF_ReadValidator* validator);
+    explicit Session(const RetainPtr<CPDF_ReadValidator>& validator);
     ~Session();
 
    private:
@@ -24,13 +21,14 @@
     bool saved_has_unavailable_data_;
   };
 
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   void SetDownloadHints(CPDF_DataAvail::DownloadHints* hints) {
     hints_ = hints;
   }
-
   bool read_error() const { return read_error_; }
   bool has_unavailable_data() const { return has_unavailable_data_; }
-
   bool has_read_problems() const {
     return read_error() || has_unavailable_data();
   }
@@ -43,7 +41,9 @@
   bool CheckWholeFileAndRequestIfUnavailable();
 
   // IFX_SeekableReadStream overrides:
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override;
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override;
   FX_FILESIZE GetSize() override;
 
  protected:
diff --git a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
index 247abaa..38b4bf9 100644
--- a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
@@ -8,10 +8,9 @@
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/cfx_memorystream.h"
-#include "core/fxcrt/fx_stream.h"
-#include "testing/fx_string_testhelpers.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/invalid_seekable_read_stream.h"
 
 namespace {
 
@@ -21,7 +20,7 @@
   return std::pair<FX_FILESIZE, FX_FILESIZE>(start, end);
 }
 
-class MockFileAvail : public CPDF_DataAvail::FileAvail {
+class MockFileAvail final : public CPDF_DataAvail::FileAvail {
  public:
   MockFileAvail() : available_range_(0, 0) {}
   ~MockFileAvail() override {}
@@ -43,7 +42,7 @@
   std::pair<FX_FILESIZE, FX_FILESIZE> available_range_;
 };
 
-class MockDownloadHints : public CPDF_DataAvail::DownloadHints {
+class MockDownloadHints final : public CPDF_DataAvail::DownloadHints {
  public:
   MockDownloadHints() : last_requested_range_(0, 0) {}
   ~MockDownloadHints() override {}
@@ -67,14 +66,13 @@
 
 TEST(CPDF_ReadValidatorTest, UnavailableData) {
   std::vector<uint8_t> test_data(kTestDataSize);
-  auto file = pdfium::MakeRetain<CFX_MemoryStream>(test_data.data(),
-                                                   test_data.size(), false);
+  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
   MockFileAvail file_avail;
   auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
 
   std::vector<uint8_t> read_buffer(100);
-  EXPECT_FALSE(
-      validator->ReadBlock(read_buffer.data(), 5000, read_buffer.size()));
+  EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
+                                            read_buffer.size()));
 
   EXPECT_FALSE(validator->read_error());
   EXPECT_TRUE(validator->has_unavailable_data());
@@ -83,16 +81,15 @@
 
   file_avail.SetAvailableRange(5000, 5000 + read_buffer.size());
 
-  EXPECT_TRUE(
-      validator->ReadBlock(read_buffer.data(), 5000, read_buffer.size()));
+  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
+                                           read_buffer.size()));
   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_MemoryStream>(test_data.data(),
-                                                   test_data.size(), false);
+  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
   MockFileAvail file_avail;
   auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
 
@@ -101,8 +98,8 @@
 
   std::vector<uint8_t> read_buffer(100);
 
-  EXPECT_FALSE(
-      validator->ReadBlock(read_buffer.data(), 5000, read_buffer.size()));
+  EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
+                                            read_buffer.size()));
   EXPECT_FALSE(validator->read_error());
   EXPECT_TRUE(validator->has_unavailable_data());
 
@@ -113,8 +110,8 @@
   hints.Reset();
 
   validator->ResetErrors();
-  EXPECT_TRUE(
-      validator->ReadBlock(read_buffer.data(), 5000, read_buffer.size()));
+  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
+                                           read_buffer.size()));
   // No new request on already available data.
   EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange());
   EXPECT_FALSE(validator->read_error());
@@ -122,9 +119,9 @@
 
   validator->ResetErrors();
   // Try read unavailable data at file end.
-  EXPECT_FALSE(validator->ReadBlock(read_buffer.data(),
-                                    validator->GetSize() - read_buffer.size(),
-                                    read_buffer.size()));
+  EXPECT_FALSE(validator->ReadBlockAtOffset(
+      read_buffer.data(), validator->GetSize() - read_buffer.size(),
+      read_buffer.size()));
   // Should not enlarge request at file end.
   EXPECT_EQ(validator->GetSize(), hints.GetLastRequstedRange().second);
   EXPECT_FALSE(validator->read_error());
@@ -134,21 +131,20 @@
 }
 
 TEST(CPDF_ReadValidatorTest, ReadError) {
-  auto file = pdfium::MakeRetain<CFX_InvalidSeekableReadStream>(kTestDataSize);
+  auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
   auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, nullptr);
 
   static const uint32_t kBufferSize = 3 * 1000;
   std::vector<uint8_t> buffer(kBufferSize);
 
-  EXPECT_FALSE(validator->ReadBlock(buffer.data(), 5000, 100));
+  EXPECT_FALSE(validator->ReadBlockAtOffset(buffer.data(), 5000, 100));
   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_MemoryStream>(test_data.data(),
-                                                   test_data.size(), false);
+  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
   MockFileAvail file_avail;
   auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
 
@@ -157,9 +153,9 @@
   // 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->ReadBlock(read_buffer.data(),
-                                    std::numeric_limits<FX_FILESIZE>::max() - 1,
-                                    read_buffer.size()));
+  EXPECT_FALSE(validator->ReadBlockAtOffset(
+      read_buffer.data(), std::numeric_limits<FX_FILESIZE>::max() - 1,
+      read_buffer.size()));
   EXPECT_FALSE(validator->read_error());
   EXPECT_FALSE(validator->has_unavailable_data());
 }
@@ -167,30 +163,30 @@
 TEST(CPDF_ReadValidatorTest, Session) {
   std::vector<uint8_t> test_data(kTestDataSize);
 
-  auto file = pdfium::MakeRetain<CFX_InvalidSeekableReadStream>(kTestDataSize);
+  auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
   MockFileAvail file_avail;
   MockDownloadHints hints;
   auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
   validator->SetDownloadHints(&hints);
 
-  const CPDF_ReadValidator::Session read_session(validator.Get());
+  const CPDF_ReadValidator::Session read_session(validator);
   ASSERT_FALSE(validator->has_read_problems());
 
   // Data is unavailable
-  validator->ReadBlock(test_data.data(), 0, 100);
+  validator->ReadBlockAtOffset(test_data.data(), 0, 100);
 
   EXPECT_TRUE(validator->has_read_problems());
   EXPECT_TRUE(validator->has_unavailable_data());
   EXPECT_FALSE(validator->read_error());
 
   {
-    const CPDF_ReadValidator::Session read_subsession(validator.Get());
+    const CPDF_ReadValidator::Session read_subsession(validator);
     // The read problems should be hidden.
     EXPECT_FALSE(validator->has_read_problems());
 
     file_avail.SetAvailableRange(0, 100);
     // Read fail.
-    validator->ReadBlock(test_data.data(), 0, 100);
+    validator->ReadBlockAtOffset(test_data.data(), 0, 100);
     EXPECT_TRUE(validator->has_read_problems());
     EXPECT_TRUE(validator->has_unavailable_data());
     EXPECT_TRUE(validator->read_error());
@@ -205,30 +201,30 @@
 TEST(CPDF_ReadValidatorTest, SessionReset) {
   std::vector<uint8_t> test_data(kTestDataSize);
 
-  auto file = pdfium::MakeRetain<CFX_InvalidSeekableReadStream>(kTestDataSize);
+  auto file = pdfium::MakeRetain<InvalidSeekableReadStream>(kTestDataSize);
   MockFileAvail file_avail;
   MockDownloadHints hints;
   auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
   validator->SetDownloadHints(&hints);
 
-  const CPDF_ReadValidator::Session read_session(validator.Get());
+  const CPDF_ReadValidator::Session read_session(validator);
   ASSERT_FALSE(validator->has_read_problems());
 
   // Data is unavailable
-  validator->ReadBlock(test_data.data(), 0, 100);
+  validator->ReadBlockAtOffset(test_data.data(), 0, 100);
 
   EXPECT_TRUE(validator->has_read_problems());
   EXPECT_TRUE(validator->has_unavailable_data());
   EXPECT_FALSE(validator->read_error());
 
   {
-    const CPDF_ReadValidator::Session read_subsession(validator.Get());
+    const CPDF_ReadValidator::Session read_subsession(validator);
     // The read problems should be hidden.
     EXPECT_FALSE(validator->has_read_problems());
 
     file_avail.SetAvailableRange(0, 100);
     // Read fail.
-    validator->ReadBlock(test_data.data(), 0, 100);
+    validator->ReadBlockAtOffset(test_data.data(), 0, 100);
     EXPECT_TRUE(validator->has_read_problems());
     EXPECT_TRUE(validator->has_unavailable_data());
     EXPECT_TRUE(validator->read_error());
@@ -246,8 +242,7 @@
 
 TEST(CPDF_ReadValidatorTest, CheckDataRangeAndRequestIfUnavailable) {
   std::vector<uint8_t> test_data(kTestDataSize);
-  auto file = pdfium::MakeRetain<CFX_MemoryStream>(test_data.data(),
-                                                   test_data.size(), false);
+  auto file = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(test_data);
   MockFileAvail file_avail;
   auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
 
@@ -272,8 +267,8 @@
   EXPECT_FALSE(validator->has_unavailable_data());
 
   std::vector<uint8_t> read_buffer(100);
-  EXPECT_TRUE(
-      validator->ReadBlock(read_buffer.data(), 5000, read_buffer.size()));
+  EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000,
+                                           read_buffer.size()));
   // 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 2ca0bdd..d50db34 100644
--- a/core/fpdfapi/parser/cpdf_reference.cpp
+++ b/core/fpdfapi/parser/cpdf_reference.cpp
@@ -17,29 +17,34 @@
 CPDF_Reference::~CPDF_Reference() {}
 
 CPDF_Object::Type CPDF_Reference::GetType() const {
-  return REFERENCE;
+  return kReference;
 }
 
 ByteString CPDF_Reference::GetString() const {
-  CPDF_Object* obj = SafeGetDirect();
+  const CPDF_Object* obj = SafeGetDirect();
   return obj ? obj->GetString() : ByteString();
 }
 
 float CPDF_Reference::GetNumber() const {
-  CPDF_Object* obj = SafeGetDirect();
+  const CPDF_Object* obj = SafeGetDirect();
   return obj ? obj->GetNumber() : 0;
 }
 
 int CPDF_Reference::GetInteger() const {
-  CPDF_Object* obj = SafeGetDirect();
+  const CPDF_Object* obj = SafeGetDirect();
   return obj ? obj->GetInteger() : 0;
 }
 
-CPDF_Dictionary* CPDF_Reference::GetDict() const {
+CPDF_Dictionary* CPDF_Reference::GetDict() {
   CPDF_Object* obj = SafeGetDirect();
   return obj ? obj->GetDict() : 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;
 }
@@ -52,11 +57,11 @@
   return this;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Reference::Clone() const {
+RetainPtr<CPDF_Object> CPDF_Reference::Clone() const {
   return CloneObjectNonCyclic(false);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Reference::CloneNonCyclic(
+RetainPtr<CPDF_Object> CPDF_Reference::CloneNonCyclic(
     bool bDirect,
     std::set<const CPDF_Object*>* pVisited) const {
   pVisited->insert(this);
@@ -66,25 +71,44 @@
                ? pDirect->CloneNonCyclic(true, pVisited)
                : nullptr;
   }
-  return pdfium::MakeUnique<CPDF_Reference>(m_pObjList.Get(), m_RefObjNum);
+  return pdfium::MakeRetain<CPDF_Reference>(m_pObjList.Get(), m_RefObjNum);
 }
 
-CPDF_Object* CPDF_Reference::SafeGetDirect() const {
+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();
+  return (obj && !obj->IsReference()) ? obj : nullptr;
+}
+
 void CPDF_Reference::SetRef(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum) {
   m_pObjList = pDoc;
   m_RefObjNum = objnum;
 }
 
-CPDF_Object* CPDF_Reference::GetDirect() const {
+CPDF_Object* CPDF_Reference::GetDirect() {
   return m_pObjList ? m_pObjList->GetOrParseIndirectObject(m_RefObjNum)
                     : nullptr;
 }
 
-bool CPDF_Reference::WriteTo(IFX_ArchiveStream* archive) const {
+const CPDF_Object* CPDF_Reference::GetDirect() const {
+  return m_pObjList ? m_pObjList->GetOrParseIndirectObject(m_RefObjNum)
+                    : nullptr;
+}
+
+bool CPDF_Reference::WriteTo(IFX_ArchiveStream* archive,
+                             const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" ") && archive->WriteDWord(GetRefObjNum()) &&
          archive->WriteString(" 0 R ");
 }
+
+RetainPtr<CPDF_Object> CPDF_Reference::MakeReference(
+    CPDF_IndirectObjectHolder* holder) const {
+  ASSERT(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 b472b4a..1ec7282 100644
--- a/core/fpdfapi/parser/cpdf_reference.h
+++ b/core/fpdfapi/parser/cpdf_reference.h
@@ -15,33 +15,42 @@
 
 class CPDF_IndirectObjectHolder;
 
-class CPDF_Reference : public CPDF_Object {
+class CPDF_Reference final : public CPDF_Object {
  public:
-  CPDF_Reference(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum);
-  ~CPDF_Reference() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
-  CPDF_Object* GetDirect() 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() 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;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
+  RetainPtr<CPDF_Object> MakeReference(
+      CPDF_IndirectObjectHolder* holder) const override;
 
   CPDF_IndirectObjectHolder* GetObjList() const { return m_pObjList.Get(); }
   uint32_t GetRefObjNum() const { return m_RefObjNum; }
   void SetRef(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum);
 
- protected:
-  std::unique_ptr<CPDF_Object> CloneNonCyclic(
+ private:
+  CPDF_Reference(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum);
+  ~CPDF_Reference() override;
+
+  RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* pVisited) const override;
-  CPDF_Object* SafeGetDirect() const;
+  CPDF_Object* SafeGetDirect();
+  const CPDF_Object* SafeGetDirect() const;
 
   UnownedPtr<CPDF_IndirectObjectHolder> m_pObjList;
   uint32_t m_RefObjNum;
diff --git a/core/fpdfapi/parser/cpdf_security_handler.cpp b/core/fpdfapi/parser/cpdf_security_handler.cpp
index 7eb5c42..126fc6b 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler.cpp
@@ -12,61 +12,65 @@
 #include <utility>
 #include <vector>
 
-#include "core/fdrm/crypto/fx_crypt.h"
+#include "core/fdrm/fx_crypt.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_crypto_handler.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/fx_random.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
-const uint8_t defpasscode[32] = {
+const uint8_t kDefaultPasscode[32] = {
     0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e,
     0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68,
     0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a};
 
+void GetPassCode(const ByteString& password, pdfium::span<uint8_t> output) {
+  DCHECK_EQ(sizeof(kDefaultPasscode), output.size());
+  size_t len = std::min(password.GetLength(), output.size());
+  size_t remaining = output.size() - len;
+  memcpy(output.data(), password.raw_str(), len);
+  if (remaining)
+    memcpy(&output[len], kDefaultPasscode, remaining);
+}
+
 void CalcEncryptKey(const CPDF_Dictionary* pEncrypt,
                     const ByteString& password,
                     uint8_t* key,
-                    int keylen,
-                    bool bIgnoreMeta,
-                    const ByteString& fileId) {
-  int revision = pEncrypt->GetIntegerFor("R");
+                    size_t keylen,
+                    bool ignore_metadata,
+                    const ByteString& file_id) {
   uint8_t passcode[32];
-  for (uint32_t i = 0; i < 32; i++) {
-    passcode[i] = i < password.GetLength()
-                      ? password[i]
-                      : defpasscode[i - password.GetLength()];
-  }
-  CRYPT_md5_context md5;
-  CRYPT_MD5Start(&md5);
-  CRYPT_MD5Update(&md5, passcode, 32);
+  GetPassCode(password, passcode);
+  CRYPT_md5_context md5 = CRYPT_MD5Start();
+  CRYPT_MD5Update(&md5, passcode);
   ByteString okey = pEncrypt->GetStringFor("O");
-  CRYPT_MD5Update(&md5, (uint8_t*)okey.c_str(), okey.GetLength());
+  CRYPT_MD5Update(&md5, okey.raw_span());
   uint32_t perm = pEncrypt->GetIntegerFor("P");
-  CRYPT_MD5Update(&md5, (uint8_t*)&perm, 4);
-  if (!fileId.IsEmpty())
-    CRYPT_MD5Update(&md5, (uint8_t*)fileId.c_str(), fileId.GetLength());
-  if (!bIgnoreMeta && revision >= 3 &&
-      !pEncrypt->GetIntegerFor("EncryptMetadata", 1)) {
-    uint32_t tag = 0xFFFFFFFF;
-    CRYPT_MD5Update(&md5, (uint8_t*)&tag, 4);
+  CRYPT_MD5Update(&md5, pdfium::as_bytes(pdfium::make_span(&perm, 1)));
+  if (!file_id.IsEmpty())
+    CRYPT_MD5Update(&md5, file_id.raw_span());
+  const bool is_revision_3_or_greater = pEncrypt->GetIntegerFor("R") >= 3;
+  if (!ignore_metadata && is_revision_3_or_greater &&
+      !pEncrypt->GetBooleanFor("EncryptMetadata", true)) {
+    constexpr uint32_t tag = 0xFFFFFFFF;
+    CRYPT_MD5Update(&md5, pdfium::as_bytes(pdfium::make_span(&tag, 1)));
   }
   uint8_t digest[16];
   CRYPT_MD5Finish(&md5, digest);
-  uint32_t copy_len = keylen;
-  if (copy_len > sizeof(digest))
-    copy_len = sizeof(digest);
-  if (revision >= 3) {
+  size_t copy_len = std::min(keylen, sizeof(digest));
+  if (is_revision_3_or_greater) {
     for (int i = 0; i < 50; i++)
-      CRYPT_MD5Generate(digest, copy_len, digest);
+      CRYPT_MD5Generate({digest, copy_len}, digest);
   }
   memset(key, 0, keylen);
   memcpy(key, digest, copy_len);
 }
 
-bool IsValidKeyLengthForCipher(int cipher, int keylen) {
+bool IsValidKeyLengthForCipher(int cipher, size_t keylen) {
   switch (cipher) {
     case FXCIPHER_AES:
       return keylen == 16 || keylen == 24 || keylen == 32;
@@ -82,132 +86,6 @@
   return false;
 }
 
-}  // namespace
-
-CPDF_SecurityHandler::CPDF_SecurityHandler()
-    : m_Version(0),
-      m_Revision(0),
-      m_Permissions(0),
-      m_Cipher(FXCIPHER_NONE),
-      m_KeyLen(0),
-      m_bOwnerUnlocked(false) {}
-
-CPDF_SecurityHandler::~CPDF_SecurityHandler() {}
-
-bool CPDF_SecurityHandler::OnInit(const CPDF_Dictionary* pEncryptDict,
-                                  const CPDF_Array* pIdArray,
-                                  const ByteString& password) {
-  m_FileId = pIdArray ? pIdArray->GetStringAt(0) : "";
-  if (!LoadDict(pEncryptDict))
-    return false;
-  if (m_Cipher == FXCIPHER_NONE)
-    return true;
-  if (!CheckSecurity(password))
-    return false;
-
-  InitCryptoHandler();
-  return true;
-}
-
-bool CPDF_SecurityHandler::CheckSecurity(const ByteString& password) {
-  if (!password.IsEmpty() &&
-      CheckPassword(password, true, m_EncryptKey, m_KeyLen)) {
-    m_bOwnerUnlocked = true;
-    return true;
-  }
-  return CheckPassword(password, false, m_EncryptKey, m_KeyLen);
-}
-
-uint32_t CPDF_SecurityHandler::GetPermissions() const {
-  return m_bOwnerUnlocked ? 0xFFFFFFFF : m_Permissions;
-}
-
-static bool LoadCryptInfo(const CPDF_Dictionary* pEncryptDict,
-                          const ByteString& name,
-                          int& cipher,
-                          int& keylen) {
-  int Version = pEncryptDict->GetIntegerFor("V");
-  cipher = FXCIPHER_RC4;
-  keylen = 0;
-  if (Version >= 4) {
-    CPDF_Dictionary* pCryptFilters = pEncryptDict->GetDictFor("CF");
-    if (!pCryptFilters) {
-      return false;
-    }
-    if (name == "Identity") {
-      cipher = FXCIPHER_NONE;
-    } else {
-      CPDF_Dictionary* pDefFilter = pCryptFilters->GetDictFor(name);
-      if (!pDefFilter) {
-        return false;
-      }
-      int nKeyBits = 0;
-      if (Version == 4) {
-        nKeyBits = pDefFilter->GetIntegerFor("Length", 0);
-        if (nKeyBits == 0) {
-          nKeyBits = pEncryptDict->GetIntegerFor("Length", 128);
-        }
-      } else {
-        nKeyBits = pEncryptDict->GetIntegerFor("Length", 256);
-      }
-      if (nKeyBits < 40) {
-        nKeyBits *= 8;
-      }
-      keylen = nKeyBits / 8;
-      ByteString cipher_name = pDefFilter->GetStringFor("CFM");
-      if (cipher_name == "AESV2" || cipher_name == "AESV3") {
-        cipher = FXCIPHER_AES;
-      }
-    }
-  } else {
-    keylen = Version > 1 ? pEncryptDict->GetIntegerFor("Length", 40) / 8 : 5;
-  }
-  if (keylen > 32 || keylen < 0) {
-    return false;
-  }
-  return IsValidKeyLengthForCipher(cipher, keylen);
-}
-
-bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict) {
-  m_pEncryptDict = pEncryptDict;
-  m_Version = pEncryptDict->GetIntegerFor("V");
-  m_Revision = pEncryptDict->GetIntegerFor("R");
-  m_Permissions = pEncryptDict->GetIntegerFor("P", -1);
-  if (m_Version < 4)
-    return LoadCryptInfo(pEncryptDict, ByteString(), m_Cipher, m_KeyLen);
-
-  ByteString stmf_name = pEncryptDict->GetStringFor("StmF");
-  ByteString strf_name = pEncryptDict->GetStringFor("StrF");
-  if (stmf_name != strf_name)
-    return false;
-
-  return LoadCryptInfo(pEncryptDict, strf_name, m_Cipher, m_KeyLen);
-}
-
-bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict,
-                                    int& cipher,
-                                    int& key_len) {
-  m_pEncryptDict = pEncryptDict;
-  m_Version = pEncryptDict->GetIntegerFor("V");
-  m_Revision = pEncryptDict->GetIntegerFor("R");
-  m_Permissions = pEncryptDict->GetIntegerFor("P", -1);
-
-  ByteString strf_name;
-  ByteString stmf_name;
-  if (m_Version >= 4) {
-    stmf_name = pEncryptDict->GetStringFor("StmF");
-    strf_name = pEncryptDict->GetStringFor("StrF");
-    if (stmf_name != strf_name)
-      return false;
-  }
-  if (!LoadCryptInfo(pEncryptDict, strf_name, cipher, key_len))
-    return false;
-
-  m_Cipher = cipher;
-  m_KeyLen = key_len;
-  return true;
-}
-
 #define FX_GET_32WORD(n, b, i)                                        \
   {                                                                   \
     (n) = (uint32_t)(                                                 \
@@ -249,8 +127,7 @@
   std::vector<uint8_t> interDigest;
   int i = 0;
   int iBlockSize = 32;
-  CRYPT_aes_context aes;
-  memset(&aes, 0, sizeof(aes));
+  CRYPT_aes_context aes = {};
   while (i < 64 || i < E[iBufLen - 1] + 32) {
     int iRoundSize = password.GetLength() + iBlockSize;
     if (vector) {
@@ -268,7 +145,7 @@
         content.insert(std::end(content), vector, vector + 48);
       }
     }
-    CRYPT_AESSetKey(&aes, 16, key, 16, true);
+    CRYPT_AESSetKey(&aes, key, 16, true);
     CRYPT_AESSetIV(&aes, iv);
     CRYPT_AESEncrypt(&aes, E, content.data(), iBufLen);
     int iHash = 0;
@@ -304,11 +181,145 @@
   }
 }
 
-bool CPDF_SecurityHandler::AES256_CheckPassword(const ByteString& password,
-                                                bool bOwner,
-                                                uint8_t* key) {
-  if (!m_pEncryptDict)
+}  // namespace
+
+CPDF_SecurityHandler::CPDF_SecurityHandler() = default;
+
+CPDF_SecurityHandler::~CPDF_SecurityHandler() = default;
+
+bool CPDF_SecurityHandler::OnInit(const CPDF_Dictionary* pEncryptDict,
+                                  const CPDF_Array* pIdArray,
+                                  const ByteString& password) {
+  if (pIdArray)
+    m_FileId = pIdArray->GetStringAt(0);
+  else
+    m_FileId.clear();
+  if (!LoadDict(pEncryptDict))
     return false;
+  if (m_Cipher == FXCIPHER_NONE)
+    return true;
+  if (!CheckSecurity(password))
+    return false;
+
+  InitCryptoHandler();
+  return true;
+}
+
+bool CPDF_SecurityHandler::CheckSecurity(const ByteString& password) {
+  if (!password.IsEmpty() && CheckPassword(password, true)) {
+    m_bOwnerUnlocked = true;
+    return true;
+  }
+  return CheckPassword(password, false);
+}
+
+uint32_t CPDF_SecurityHandler::GetPermissions() const {
+  uint32_t dwPermission = m_bOwnerUnlocked ? 0xFFFFFFFF : m_Permissions;
+  if (m_pEncryptDict && m_pEncryptDict->GetStringFor("Filter") == "Standard") {
+    // See PDF Reference 1.7, page 123, table 3.20.
+    dwPermission &= 0xFFFFFFFC;
+    dwPermission |= 0xFFFFF0C0;
+  }
+  return dwPermission;
+}
+
+static bool LoadCryptInfo(const CPDF_Dictionary* pEncryptDict,
+                          const ByteString& name,
+                          int* cipher,
+                          size_t* keylen_out) {
+  int Version = pEncryptDict->GetIntegerFor("V");
+  *cipher = FXCIPHER_RC4;
+  *keylen_out = 0;
+  int keylen = 0;
+  if (Version >= 4) {
+    const CPDF_Dictionary* pCryptFilters = pEncryptDict->GetDictFor("CF");
+    if (!pCryptFilters)
+      return false;
+
+    if (name == "Identity") {
+      *cipher = FXCIPHER_NONE;
+    } else {
+      const CPDF_Dictionary* pDefFilter = pCryptFilters->GetDictFor(name);
+      if (!pDefFilter)
+        return false;
+
+      int nKeyBits = 0;
+      if (Version == 4) {
+        nKeyBits = pDefFilter->GetIntegerFor("Length", 0);
+        if (nKeyBits == 0) {
+          nKeyBits = pEncryptDict->GetIntegerFor("Length", 128);
+        }
+      } else {
+        nKeyBits = pEncryptDict->GetIntegerFor("Length", 256);
+      }
+      if (nKeyBits < 0)
+        return false;
+
+      if (nKeyBits < 40) {
+        nKeyBits *= 8;
+      }
+      keylen = nKeyBits / 8;
+      ByteString cipher_name = pDefFilter->GetStringFor("CFM");
+      if (cipher_name == "AESV2" || cipher_name == "AESV3")
+        *cipher = FXCIPHER_AES;
+    }
+  } else {
+    keylen = Version > 1 ? pEncryptDict->GetIntegerFor("Length", 40) / 8 : 5;
+  }
+
+  if (keylen < 0 || keylen > 32)
+    return false;
+  if (!IsValidKeyLengthForCipher(*cipher, keylen))
+    return false;
+
+  *keylen_out = keylen;
+  return true;
+}
+
+bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict) {
+  m_pEncryptDict.Reset(pEncryptDict);
+  m_Version = pEncryptDict->GetIntegerFor("V");
+  m_Revision = pEncryptDict->GetIntegerFor("R");
+  m_Permissions = pEncryptDict->GetIntegerFor("P", -1);
+  if (m_Version < 4)
+    return LoadCryptInfo(pEncryptDict, ByteString(), &m_Cipher, &m_KeyLen);
+
+  ByteString stmf_name = pEncryptDict->GetStringFor("StmF");
+  ByteString strf_name = pEncryptDict->GetStringFor("StrF");
+  if (stmf_name != strf_name)
+    return false;
+
+  return LoadCryptInfo(pEncryptDict, strf_name, &m_Cipher, &m_KeyLen);
+}
+
+bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict,
+                                    int* cipher,
+                                    size_t* key_len) {
+  m_pEncryptDict.Reset(pEncryptDict);
+  m_Version = pEncryptDict->GetIntegerFor("V");
+  m_Revision = pEncryptDict->GetIntegerFor("R");
+  m_Permissions = pEncryptDict->GetIntegerFor("P", -1);
+
+  ByteString strf_name;
+  ByteString stmf_name;
+  if (m_Version >= 4) {
+    stmf_name = pEncryptDict->GetStringFor("StmF");
+    strf_name = pEncryptDict->GetStringFor("StrF");
+    if (stmf_name != strf_name)
+      return false;
+  }
+  if (!LoadCryptInfo(pEncryptDict, strf_name, cipher, key_len))
+    return false;
+
+  m_Cipher = *cipher;
+  m_KeyLen = *key_len;
+  return true;
+}
+
+bool CPDF_SecurityHandler::AES256_CheckPassword(const ByteString& password,
+                                                bool bOwner) {
+  ASSERT(m_pEncryptDict);
+  ASSERT(m_Revision >= 5);
 
   ByteString okey = m_pEncryptDict->GetStringFor("O");
   if (okey.GetLength() < 48)
@@ -335,9 +346,6 @@
   if (memcmp(digest, pkey, 32) != 0)
     return false;
 
-  if (!key)
-    return true;
-
   if (m_Revision >= 6) {
     Revision6_Hash(password, (const uint8_t*)pkey + 40,
                    bOwner ? ukey.raw_str() : nullptr, digest);
@@ -353,21 +361,18 @@
   if (ekey.GetLength() < 32)
     return false;
 
-  CRYPT_aes_context aes;
-  memset(&aes, 0, sizeof(aes));
-  CRYPT_AESSetKey(&aes, 16, digest, 32, false);
-  uint8_t iv[16];
-  memset(iv, 0, 16);
+  CRYPT_aes_context aes = {};
+  CRYPT_AESSetKey(&aes, digest, sizeof(digest), false);
+  uint8_t iv[16] = {};
   CRYPT_AESSetIV(&aes, iv);
-  CRYPT_AESDecrypt(&aes, key, ekey.raw_str(), 32);
-  CRYPT_AESSetKey(&aes, 16, key, 32, false);
+  CRYPT_AESDecrypt(&aes, m_EncryptKey, ekey.raw_str(), 32);
+  CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey), false);
   CRYPT_AESSetIV(&aes, iv);
   ByteString perms = m_pEncryptDict->GetStringFor("Perms");
   if (perms.IsEmpty())
     return false;
 
-  uint8_t perms_buf[16];
-  memset(perms_buf, 0, sizeof(perms_buf));
+  uint8_t perms_buf[16] = {};
   size_t copy_len =
       std::min(sizeof(perms_buf), static_cast<size_t>(perms.GetLength()));
   memcpy(perms_buf, perms.raw_str(), copy_len);
@@ -387,28 +392,49 @@
 }
 
 bool CPDF_SecurityHandler::CheckPassword(const ByteString& password,
-                                         bool bOwner,
-                                         uint8_t* key,
-                                         int32_t key_len) {
-  if (m_Revision >= 5)
-    return AES256_CheckPassword(password, bOwner, key);
+                                         bool bOwner) {
+  DCHECK_EQ(kUnknown, m_PasswordEncodingConversion);
+  if (CheckPasswordImpl(password, bOwner)) {
+    m_PasswordEncodingConversion = kNone;
+    return true;
+  }
 
-  uint8_t keybuf[32];
-  if (!key)
-    key = keybuf;
+  ByteStringView password_view = password.AsStringView();
+  if (password_view.IsASCII())
+    return false;
+
+  if (m_Revision >= 5) {
+    ByteString utf8_password = WideString::FromLatin1(password_view).ToUTF8();
+    if (!CheckPasswordImpl(utf8_password, bOwner))
+      return false;
+
+    m_PasswordEncodingConversion = kLatin1ToUtf8;
+    return true;
+  }
+
+  ByteString latin1_password = WideString::FromUTF8(password_view).ToLatin1();
+  if (!CheckPasswordImpl(latin1_password, bOwner))
+    return false;
+
+  m_PasswordEncodingConversion = kUtf8toLatin1;
+  return true;
+}
+
+bool CPDF_SecurityHandler::CheckPasswordImpl(const ByteString& password,
+                                             bool bOwner) {
+  if (m_Revision >= 5)
+    return AES256_CheckPassword(password, bOwner);
 
   if (bOwner)
-    return CheckOwnerPassword(password, key, key_len);
+    return CheckOwnerPassword(password);
 
-  return CheckUserPassword(password, false, key, key_len) ||
-         CheckUserPassword(password, true, key, key_len);
+  return CheckUserPassword(password, false) ||
+         CheckUserPassword(password, true);
 }
 
 bool CPDF_SecurityHandler::CheckUserPassword(const ByteString& password,
-                                             bool bIgnoreEncryptMeta,
-                                             uint8_t* key,
-                                             int32_t key_len) {
-  CalcEncryptKey(m_pEncryptDict.Get(), password, key, key_len,
+                                             bool bIgnoreEncryptMeta) {
+  CalcEncryptKey(m_pEncryptDict.Get(), password, m_EncryptKey, m_KeyLen,
                  bIgnoreEncryptMeta, m_FileId);
   ByteString ukey =
       m_pEncryptDict ? m_pEncryptDict->GetStringFor("U") : ByteString();
@@ -418,96 +444,96 @@
 
   uint8_t ukeybuf[32];
   if (m_Revision == 2) {
-    memcpy(ukeybuf, defpasscode, 32);
-    CRYPT_ArcFourCryptBlock(ukeybuf, 32, key, key_len);
-  } else {
-    uint8_t test[32], tmpkey[32];
-    uint32_t copy_len = sizeof(test);
-    if (copy_len > (uint32_t)ukey.GetLength()) {
-      copy_len = ukey.GetLength();
-    }
-    memset(test, 0, sizeof(test));
-    memset(tmpkey, 0, sizeof(tmpkey));
-    memcpy(test, ukey.c_str(), copy_len);
-    for (int32_t i = 19; i >= 0; i--) {
-      for (int j = 0; j < key_len; j++)
-        tmpkey[j] = key[j] ^ static_cast<uint8_t>(i);
-      CRYPT_ArcFourCryptBlock(test, 32, tmpkey, key_len);
-    }
-    CRYPT_md5_context md5;
-    CRYPT_MD5Start(&md5);
-    CRYPT_MD5Update(&md5, defpasscode, 32);
-    if (!m_FileId.IsEmpty()) {
-      CRYPT_MD5Update(&md5, (uint8_t*)m_FileId.c_str(), m_FileId.GetLength());
-    }
-    CRYPT_MD5Finish(&md5, ukeybuf);
-    return memcmp(test, ukeybuf, 16) == 0;
+    memcpy(ukeybuf, kDefaultPasscode, sizeof(kDefaultPasscode));
+    CRYPT_ArcFourCryptBlock(ukeybuf, {m_EncryptKey, m_KeyLen});
+    return memcmp(ukey.c_str(), ukeybuf, 16) == 0;
   }
-  return memcmp(ukey.c_str(), ukeybuf, 16) == 0;
+
+  uint8_t test[32] = {};
+  uint8_t tmpkey[32] = {};
+  uint32_t copy_len = std::min(sizeof(test), ukey.GetLength());
+
+  memcpy(test, ukey.c_str(), copy_len);
+  for (int32_t i = 19; i >= 0; i--) {
+    for (size_t j = 0; j < m_KeyLen; j++)
+      tmpkey[j] = m_EncryptKey[j] ^ static_cast<uint8_t>(i);
+    CRYPT_ArcFourCryptBlock(test, {tmpkey, m_KeyLen});
+  }
+  CRYPT_md5_context md5 = CRYPT_MD5Start();
+  CRYPT_MD5Update(&md5, kDefaultPasscode);
+  if (!m_FileId.IsEmpty())
+    CRYPT_MD5Update(&md5, m_FileId.raw_span());
+  CRYPT_MD5Finish(&md5, ukeybuf);
+  return memcmp(test, ukeybuf, 16) == 0;
 }
 
 ByteString CPDF_SecurityHandler::GetUserPassword(
-    const ByteString& owner_password,
-    int32_t key_len) const {
+    const ByteString& owner_password) const {
+  constexpr size_t kRequiredOkeyLength = 32;
   ByteString okey = m_pEncryptDict->GetStringFor("O");
+  size_t okeylen = std::min<size_t>(okey.GetLength(), kRequiredOkeyLength);
+  if (okeylen < kRequiredOkeyLength)
+    return ByteString();
+
+  DCHECK_EQ(kRequiredOkeyLength, okeylen);
   uint8_t passcode[32];
-  for (uint32_t i = 0; i < 32; i++) {
-    passcode[i] = i < owner_password.GetLength()
-                      ? owner_password[i]
-                      : defpasscode[i - owner_password.GetLength()];
-  }
+  GetPassCode(owner_password, passcode);
   uint8_t digest[16];
-  CRYPT_MD5Generate(passcode, 32, digest);
+  CRYPT_MD5Generate(passcode, digest);
   if (m_Revision >= 3) {
-    for (uint32_t i = 0; i < 50; i++) {
-      CRYPT_MD5Generate(digest, 16, digest);
-    }
+    for (uint32_t i = 0; i < 50; i++)
+      CRYPT_MD5Generate(digest, digest);
   }
-  uint8_t enckey[32];
-  memset(enckey, 0, sizeof(enckey));
-  uint32_t copy_len = key_len;
-  if (copy_len > sizeof(digest)) {
-    copy_len = sizeof(digest);
-  }
+  uint8_t enckey[32] = {};
+  size_t copy_len = std::min(m_KeyLen, sizeof(digest));
+
   memcpy(enckey, digest, copy_len);
-  int okeylen = okey.GetLength();
-  if (okeylen > 32) {
-    okeylen = 32;
-  }
-  uint8_t okeybuf[64];
-  memset(okeybuf, 0, sizeof(okeybuf));
+  uint8_t okeybuf[32] = {};
   memcpy(okeybuf, okey.c_str(), okeylen);
+  pdfium::span<uint8_t> okey_span(okeybuf, okeylen);
   if (m_Revision == 2) {
-    CRYPT_ArcFourCryptBlock(okeybuf, okeylen, enckey, key_len);
+    CRYPT_ArcFourCryptBlock(okey_span, {enckey, m_KeyLen});
   } else {
     for (int32_t i = 19; i >= 0; i--) {
-      uint8_t tempkey[32];
-      memset(tempkey, 0, sizeof(tempkey));
-      for (int j = 0; j < m_KeyLen; j++)
+      uint8_t tempkey[32] = {};
+      for (size_t j = 0; j < m_KeyLen; j++)
         tempkey[j] = enckey[j] ^ static_cast<uint8_t>(i);
-      CRYPT_ArcFourCryptBlock(okeybuf, okeylen, tempkey, key_len);
+      CRYPT_ArcFourCryptBlock(okey_span, {tempkey, m_KeyLen});
     }
   }
-  int len = 32;
-  while (len && defpasscode[len - 1] == okeybuf[len - 1]) {
+  size_t len = kRequiredOkeyLength;
+  while (len && kDefaultPasscode[len - 1] == okey_span[len - 1])
     len--;
-  }
+
   return ByteString(okeybuf, len);
 }
 
-bool CPDF_SecurityHandler::CheckOwnerPassword(const ByteString& password,
-                                              uint8_t* key,
-                                              int32_t key_len) {
-  ByteString user_pass = GetUserPassword(password, key_len);
-  if (CheckUserPassword(user_pass, false, key, key_len))
-    return true;
-  return CheckUserPassword(user_pass, true, key, key_len);
+bool CPDF_SecurityHandler::CheckOwnerPassword(const ByteString& password) {
+  ByteString user_pass = GetUserPassword(password);
+  return CheckUserPassword(user_pass, false) ||
+         CheckUserPassword(user_pass, true);
 }
 
 bool CPDF_SecurityHandler::IsMetadataEncrypted() const {
   return m_pEncryptDict->GetBooleanFor("EncryptMetadata", true);
 }
 
+ByteString CPDF_SecurityHandler::GetEncodedPassword(
+    ByteStringView password) const {
+  switch (m_PasswordEncodingConversion) {
+    case kNone:
+      // Do nothing.
+      return ByteString(password);
+    case kLatin1ToUtf8:
+      return WideString::FromLatin1(password).ToUTF8();
+    case kUtf8toLatin1:
+      return WideString::FromUTF8(password).ToLatin1();
+    default:
+      NOTREACHED();
+      return ByteString(password);
+  }
+}
+
 void CPDF_SecurityHandler::OnCreateInternal(CPDF_Dictionary* pEncryptDict,
                                             const CPDF_Array* pIdArray,
                                             const ByteString& user_password,
@@ -515,9 +541,9 @@
                                             bool bDefault) {
   ASSERT(pEncryptDict);
 
-  int cipher = 0;
-  int key_len = 0;
-  if (!LoadDict(pEncryptDict, cipher, key_len)) {
+  int cipher = FXCIPHER_NONE;
+  size_t key_len = 0;
+  if (!LoadDict(pEncryptDict, &cipher, &key_len)) {
     return;
   }
   ByteString owner_password_copy = owner_password;
@@ -525,84 +551,71 @@
     owner_password_copy = user_password;
 
   if (m_Revision >= 5) {
-    int t = (int)time(nullptr);
+    uint32_t random[4];
+    FX_Random_GenerateMT(random, FX_ArraySize(random));
     CRYPT_sha2_context sha;
     CRYPT_SHA256Start(&sha);
-    CRYPT_SHA256Update(&sha, (uint8_t*)&t, sizeof t);
-    CRYPT_SHA256Update(&sha, m_EncryptKey, 32);
-    CRYPT_SHA256Update(&sha, (uint8_t*)"there", 5);
+    CRYPT_SHA256Update(&sha, reinterpret_cast<uint8_t*>(random),
+                       sizeof(random));
     CRYPT_SHA256Finish(&sha, m_EncryptKey);
-    AES256_SetPassword(pEncryptDict, user_password, false, m_EncryptKey);
-    if (bDefault) {
-      AES256_SetPassword(pEncryptDict, owner_password_copy, true, m_EncryptKey);
-      AES256_SetPerms(pEncryptDict, m_Permissions,
-                      pEncryptDict->GetBooleanFor("EncryptMetadata", true),
-                      m_EncryptKey);
-    }
+    AES256_SetPassword(pEncryptDict, user_password, false);
+    if (bDefault)
+      AES256_SetPassword(pEncryptDict, owner_password_copy, true);
+    AES256_SetPerms(pEncryptDict);
     return;
   }
   if (bDefault) {
     uint8_t passcode[32];
-    for (uint32_t i = 0; i < 32; i++) {
-      passcode[i] = i < owner_password_copy.GetLength()
-                        ? owner_password_copy[i]
-                        : defpasscode[i - owner_password_copy.GetLength()];
-    }
+    GetPassCode(owner_password_copy, passcode);
     uint8_t digest[16];
-    CRYPT_MD5Generate(passcode, 32, digest);
+    CRYPT_MD5Generate(passcode, digest);
     if (m_Revision >= 3) {
       for (uint32_t i = 0; i < 50; i++)
-        CRYPT_MD5Generate(digest, 16, digest);
+        CRYPT_MD5Generate(digest, digest);
     }
     uint8_t enckey[32];
     memcpy(enckey, digest, key_len);
-    for (uint32_t i = 0; i < 32; i++) {
-      passcode[i] = i < user_password.GetLength()
-                        ? user_password[i]
-                        : defpasscode[i - user_password.GetLength()];
-    }
-    CRYPT_ArcFourCryptBlock(passcode, 32, enckey, key_len);
+    GetPassCode(user_password, passcode);
+    CRYPT_ArcFourCryptBlock(passcode, {enckey, key_len});
     uint8_t tempkey[32];
     if (m_Revision >= 3) {
       for (uint8_t i = 1; i <= 19; i++) {
-        for (int j = 0; j < key_len; j++)
+        for (size_t j = 0; j < key_len; j++)
           tempkey[j] = enckey[j] ^ i;
-        CRYPT_ArcFourCryptBlock(passcode, 32, tempkey, key_len);
+        CRYPT_ArcFourCryptBlock(passcode, {tempkey, key_len});
       }
     }
     pEncryptDict->SetNewFor<CPDF_String>("O", ByteString(passcode, 32), false);
   }
 
-  ByteString fileId;
-  if (pIdArray) {
-    fileId = pIdArray->GetStringAt(0);
-  }
+  ByteString file_id;
+  if (pIdArray)
+    file_id = pIdArray->GetStringAt(0);
 
   CalcEncryptKey(m_pEncryptDict.Get(), user_password, m_EncryptKey, key_len,
-                 false, fileId);
+                 false, file_id);
   if (m_Revision < 3) {
     uint8_t tempbuf[32];
-    memcpy(tempbuf, defpasscode, 32);
-    CRYPT_ArcFourCryptBlock(tempbuf, 32, m_EncryptKey, key_len);
+    memcpy(tempbuf, kDefaultPasscode, sizeof(kDefaultPasscode));
+    CRYPT_ArcFourCryptBlock(tempbuf, {m_EncryptKey, key_len});
     pEncryptDict->SetNewFor<CPDF_String>("U", ByteString(tempbuf, 32), false);
   } else {
-    CRYPT_md5_context md5;
-    CRYPT_MD5Start(&md5);
-    CRYPT_MD5Update(&md5, defpasscode, 32);
-    if (!fileId.IsEmpty()) {
-      CRYPT_MD5Update(&md5, (uint8_t*)fileId.c_str(), fileId.GetLength());
-    }
+    CRYPT_md5_context md5 = CRYPT_MD5Start();
+    CRYPT_MD5Update(&md5, kDefaultPasscode);
+    if (!file_id.IsEmpty())
+      CRYPT_MD5Update(&md5, file_id.raw_span());
+
     uint8_t digest[32];
     CRYPT_MD5Finish(&md5, digest);
-    CRYPT_ArcFourCryptBlock(digest, 16, m_EncryptKey, key_len);
+    pdfium::span<uint8_t> partial_digest_span(digest, 16);
+    CRYPT_ArcFourCryptBlock(partial_digest_span, {m_EncryptKey, key_len});
     uint8_t tempkey[32];
     for (uint8_t i = 1; i <= 19; i++) {
-      for (int j = 0; j < key_len; j++) {
+      for (size_t j = 0; j < key_len; j++)
         tempkey[j] = m_EncryptKey[j] ^ i;
-      }
-      CRYPT_ArcFourCryptBlock(digest, 16, tempkey, key_len);
+      CRYPT_ArcFourCryptBlock(partial_digest_span, {tempkey, key_len});
     }
-    CRYPT_MD5Generate(digest, 16, digest + 16);
+    CRYPT_MD5Generate({digest, 16}, digest + 16);
     pEncryptDict->SetNewFor<CPDF_String>("U", ByteString(digest, 32), false);
   }
 }
@@ -624,11 +637,10 @@
 
 void CPDF_SecurityHandler::AES256_SetPassword(CPDF_Dictionary* pEncryptDict,
                                               const ByteString& password,
-                                              bool bOwner,
-                                              const uint8_t* key) {
+                                              bool bOwner) {
   CRYPT_sha1_context sha;
   CRYPT_SHA1Start(&sha);
-  CRYPT_SHA1Update(&sha, key, 32);
+  CRYPT_SHA1Update(&sha, m_EncryptKey, sizeof(m_EncryptKey));
   CRYPT_SHA1Update(&sha, (uint8_t*)"hello", 5);
 
   uint8_t digest[20];
@@ -664,41 +676,39 @@
     }
     CRYPT_SHA256Finish(&sha2, digest1);
   }
-  CRYPT_aes_context aes;
-  memset(&aes, 0, sizeof(aes));
-  CRYPT_AESSetKey(&aes, 16, digest1, 32, true);
-  uint8_t iv[16];
-  memset(iv, 0, 16);
+  CRYPT_aes_context aes = {};
+  CRYPT_AESSetKey(&aes, digest1, 32, true);
+  uint8_t iv[16] = {};
   CRYPT_AESSetIV(&aes, iv);
-  CRYPT_AESEncrypt(&aes, digest1, key, 32);
+  CRYPT_AESEncrypt(&aes, digest1, m_EncryptKey, sizeof(m_EncryptKey));
   pEncryptDict->SetNewFor<CPDF_String>(bOwner ? "OE" : "UE",
                                        ByteString(digest1, 32), false);
 }
 
-void CPDF_SecurityHandler::AES256_SetPerms(CPDF_Dictionary* pEncryptDict,
-                                           uint32_t permissions,
-                                           bool bEncryptMetadata,
-                                           const uint8_t* key) {
+void CPDF_SecurityHandler::AES256_SetPerms(CPDF_Dictionary* pEncryptDict) {
   uint8_t buf[16];
-  buf[0] = (uint8_t)permissions;
-  buf[1] = (uint8_t)(permissions >> 8);
-  buf[2] = (uint8_t)(permissions >> 16);
-  buf[3] = (uint8_t)(permissions >> 24);
+  buf[0] = static_cast<uint8_t>(m_Permissions);
+  buf[1] = static_cast<uint8_t>(m_Permissions >> 8);
+  buf[2] = static_cast<uint8_t>(m_Permissions >> 16);
+  buf[3] = static_cast<uint8_t>(m_Permissions >> 24);
   buf[4] = 0xff;
   buf[5] = 0xff;
   buf[6] = 0xff;
   buf[7] = 0xff;
-  buf[8] = bEncryptMetadata ? 'T' : 'F';
+  buf[8] = pEncryptDict->GetBooleanFor("EncryptMetadata", true) ? 'T' : 'F';
   buf[9] = 'a';
   buf[10] = 'd';
   buf[11] = 'b';
 
-  CRYPT_aes_context aes;
-  memset(&aes, 0, sizeof(aes));
-  CRYPT_AESSetKey(&aes, 16, key, 32, true);
+  // In ISO 32000 Supplement for ExtensionLevel 3, Algorithm 3.10 says bytes 12
+  // to 15 should be random data.
+  uint32_t* buf_random = reinterpret_cast<uint32_t*>(&buf[12]);
+  FX_Random_GenerateMT(buf_random, 1);
 
-  uint8_t iv[16];
-  memset(iv, 0, 16);
+  CRYPT_aes_context aes = {};
+  CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey), true);
+
+  uint8_t iv[16] = {};
   CRYPT_AESSetIV(&aes, iv);
 
   uint8_t buf1[16];
diff --git a/core/fpdfapi/parser/cpdf_security_handler.h b/core/fpdfapi/parser/cpdf_security_handler.h
index eb95743..05eb689 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.h
+++ b/core/fpdfapi/parser/cpdf_security_handler.h
@@ -11,6 +11,7 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 #define FXCIPHER_NONE 0
 #define FXCIPHER_RC4 1
@@ -22,10 +23,10 @@
 class CPDF_Dictionary;
 class CPDF_Parser;
 
-class CPDF_SecurityHandler {
+class CPDF_SecurityHandler : public Retainable {
  public:
-  CPDF_SecurityHandler();
-  ~CPDF_SecurityHandler();
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   bool OnInit(const CPDF_Dictionary* pEncryptDict,
               const CPDF_Array* pIdArray,
@@ -41,42 +42,40 @@
   uint32_t GetPermissions() const;
   bool IsMetadataEncrypted() const;
 
-  ByteString GetUserPassword(const ByteString& owner_password,
-                             int32_t key_len) const;
-  bool CheckPassword(const ByteString& user_password,
-                     bool bOwner,
-                     uint8_t* key,
-                     int key_len);
-
   CPDF_CryptoHandler* GetCryptoHandler() const {
     return m_pCryptoHandler.get();
   }
 
+  // Take |password| and encode it, if necessary, based on the password encoding
+  // conversion.
+  ByteString GetEncodedPassword(ByteStringView password) const;
+
  private:
+  enum PasswordEncodingConversion {
+    kUnknown,
+    kNone,
+    kLatin1ToUtf8,
+    kUtf8toLatin1,
+  };
+
+  CPDF_SecurityHandler();
+  ~CPDF_SecurityHandler() override;
+
   bool LoadDict(const CPDF_Dictionary* pEncryptDict);
   bool LoadDict(const CPDF_Dictionary* pEncryptDict,
-                int& cipher,
-                int& key_len);
+                int* cipher,
+                size_t* key_len);
 
-  bool CheckUserPassword(const ByteString& password,
-                         bool bIgnoreEncryptMeta,
-                         uint8_t* key,
-                         int32_t key_len);
-
-  bool CheckOwnerPassword(const ByteString& password,
-                          uint8_t* key,
-                          int32_t key_len);
-  bool AES256_CheckPassword(const ByteString& password,
-                            bool bOwner,
-                            uint8_t* key);
+  ByteString GetUserPassword(const ByteString& owner_password) const;
+  bool CheckPassword(const ByteString& user_password, bool bOwner);
+  bool CheckPasswordImpl(const ByteString& password, bool bOwner);
+  bool CheckUserPassword(const ByteString& password, bool bIgnoreEncryptMeta);
+  bool CheckOwnerPassword(const ByteString& password);
+  bool AES256_CheckPassword(const ByteString& password, bool bOwner);
   void AES256_SetPassword(CPDF_Dictionary* pEncryptDict,
                           const ByteString& password,
-                          bool bOwner,
-                          const uint8_t* key);
-  void AES256_SetPerms(CPDF_Dictionary* pEncryptDict,
-                       uint32_t permission,
-                       bool bEncryptMetadata,
-                       const uint8_t* key);
+                          bool bOwner);
+  void AES256_SetPerms(CPDF_Dictionary* pEncryptDict);
   void OnCreateInternal(CPDF_Dictionary* pEncryptDict,
                         const CPDF_Array* pIdArray,
                         const ByteString& user_password,
@@ -86,16 +85,17 @@
 
   void InitCryptoHandler();
 
-  int m_Version;
-  int m_Revision;
-  UnownedPtr<const CPDF_Dictionary> m_pEncryptDict;
+  bool m_bOwnerUnlocked = false;
+  int m_Version = 0;
+  int m_Revision = 0;
+  uint32_t m_Permissions = 0;
+  int m_Cipher = FXCIPHER_NONE;
+  size_t m_KeyLen = 0;
+  PasswordEncodingConversion m_PasswordEncodingConversion = kUnknown;
   ByteString m_FileId;
-  uint32_t m_Permissions;
-  int m_Cipher;
-  uint8_t m_EncryptKey[32];
-  int m_KeyLen;
-  bool m_bOwnerUnlocked;
+  RetainPtr<const CPDF_Dictionary> m_pEncryptDict;
   std::unique_ptr<CPDF_CryptoHandler> m_pCryptoHandler;
+  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 a1d5fda..ee2a621 100644
--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
@@ -2,65 +2,179 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <string>
 
+#include "build/build_config.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fxcrt/fx_system.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/gtest/include/gtest/gtest.h"
 
-class CPDFSecurityHandlerEmbeddertest : public EmbedderTest {};
+namespace {
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, Unencrypted) {
+const char kAgeUTF8[] =
+    "\xc3\xa2"
+    "ge";
+const char kAgeLatin1[] =
+    "\xe2"
+    "ge";
+
+const char kHotelUTF8[] =
+    "h"
+    "\xc3\xb4"
+    "tel";
+const char kHotelLatin1[] =
+    "h"
+    "\xf4"
+    "tel";
+
+}  // namespace
+
+class CPDFSecurityHandlerEmbedderTest : public EmbedderTest {
+ protected:
+  void OpenAndVerifyHelloWorldDocumentWithPassword(const char* filename,
+                                                   const char* password) {
+    ASSERT_TRUE(OpenDocumentWithPassword(filename, password));
+    FPDF_PAGE page = LoadPage(0);
+    VerifyHelloWorldPage(page);
+    UnloadPage(page);
+  }
+
+  void VerifySavedHelloWorldDocumentWithPassword(const char* password) {
+    ASSERT_TRUE(OpenSavedDocumentWithPassword(password));
+    FPDF_PAGE page = LoadSavedPage(0);
+    VerifyHelloWorldPage(page);
+    CloseSavedPage(page);
+    CloseSavedDocument();
+  }
+
+  void VerifySavedModifiedHelloWorldDocumentWithPassword(const char* password) {
+    ASSERT_TRUE(OpenSavedDocumentWithPassword(password));
+    FPDF_PAGE page = LoadSavedPage(0);
+    VerifyModifiedHelloWorldPage(page);
+    CloseSavedPage(page);
+    CloseSavedDocument();
+  }
+
+  void RemoveTrailerIdFromDocument() {
+    // This is cheating slightly to avoid a layering violation, since this file
+    // cannot include fpdfsdk/cpdfsdk_helpers.h to get access to
+    // CPDFDocumentFromFPDFDocument().
+    CPDF_Document* doc = reinterpret_cast<CPDF_Document*>((document()));
+    ASSERT_TRUE(doc);
+    CPDF_Parser* parser = doc->GetParser();
+    ASSERT_TRUE(parser);
+    CPDF_Dictionary* trailer = parser->GetMutableTrailerForTesting();
+    ASSERT_TRUE(trailer);
+    ASSERT_TRUE(trailer->RemoveFor("ID"));
+  }
+
+  void RemoveGoodbyeObject() {
+    FPDF_PAGE page = LoadPage(0);
+    {
+      ScopedFPDFPageObject goodbye_object(FPDFPage_GetObject(page, 1));
+      ASSERT_TRUE(goodbye_object);
+      ASSERT_TRUE(FPDFPage_RemoveObject(page, goodbye_object.get()));
+    }
+    ASSERT_TRUE(FPDFPage_GenerateContent(page));
+    VerifyModifiedHelloWorldPage(page);
+    UnloadPage(page);
+  }
+
+ private:
+  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);
+  }
+
+  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);
+  }
+};
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, Unencrypted) {
   ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, UnencryptedWithPassword) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, UnencryptedWithPassword) {
   ASSERT_TRUE(OpenDocumentWithPassword("about_blank.pdf", "foobar"));
   EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, NoPassword) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, NoPassword) {
   EXPECT_FALSE(OpenDocument("encrypted.pdf"));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, BadPassword) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, BadPassword) {
   EXPECT_FALSE(OpenDocumentWithPassword("encrypted.pdf", "tiger"));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, UserPassword) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPassword) {
   ASSERT_TRUE(OpenDocumentWithPassword("encrypted.pdf", "1234"));
   EXPECT_EQ(0xFFFFF2C0, FPDF_GetDocPermissions(document()));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, OwnerPassword) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPassword) {
   ASSERT_TRUE(OpenDocumentWithPassword("encrypted.pdf", "5678"));
   EXPECT_EQ(0xFFFFFFFC, FPDF_GetDocPermissions(document()));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, PasswordAfterGenerateSave) {
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_PasswordAfterGenerateSave DISABLED_PasswordAfterGenerateSave
+#else
+#define MAYBE_PasswordAfterGenerateSave PasswordAfterGenerateSave
+#endif
+TEST_F(CPDFSecurityHandlerEmbedderTest, MAYBE_PasswordAfterGenerateSave) {
 #if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
   const char md5[] = "7048dca58e2ed8f93339008b91e4eb4e";
-#elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#elif defined(OS_MACOSX)
   const char md5[] = "6951b6c9891dfe0332a5b1983e484400";
 #else
-  const char md5[] = "a5dde3c6c37b8716b9b369a03752a728";
+  const char md5[] = "041c2fb541c8907cc22ce101b686c79e";
 #endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
   {
-    ASSERT_TRUE(OpenDocumentWithOptions("encrypted.pdf", "5678", true));
+    ASSERT_TRUE(OpenDocumentWithOptions("encrypted.pdf", "5678",
+                                        LinearizeOption::kMustLinearize,
+                                        JavaScriptOption::kEnableJavaScript));
     FPDF_PAGE page = LoadPage(0);
     ASSERT_TRUE(page);
     FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
     ASSERT_TRUE(red_rect);
-    EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
+    EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
     EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
     FPDFPage_InsertObject(page, red_rect);
-    FPDF_BITMAP page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap, 612, 792, md5);
-    FPDFBitmap_Destroy(page_bitmap);
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), 612, 792, md5);
     EXPECT_TRUE(FPDFPage_GenerateContent(page));
     SetWholeFileAvailable();
     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
@@ -80,30 +194,488 @@
   } tests[] = {{"1234", 0xFFFFF2C0}, {"5678", 0xFFFFFFFC}};
 
   for (const auto& test : tests) {
-    OpenSavedDocument(test.password);
+    ASSERT_TRUE(OpenSavedDocumentWithPassword(test.password));
     FPDF_PAGE page = LoadSavedPage(0);
     VerifySavedRendering(page, 612, 792, md5);
-    EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(m_SavedDocument));
+    EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(saved_document_));
 
     CloseSavedPage(page);
     CloseSavedDocument();
   }
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, NoPasswordVersion5) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, NoPasswordVersion5) {
   ASSERT_FALSE(OpenDocument("bug_644.pdf"));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, BadPasswordVersion5) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, BadPasswordVersion5) {
   ASSERT_FALSE(OpenDocumentWithPassword("bug_644.pdf", "tiger"));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, OwnerPasswordVersion5) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion5) {
   ASSERT_TRUE(OpenDocumentWithPassword("bug_644.pdf", "a"));
   EXPECT_EQ(0xFFFFFFFC, FPDF_GetDocPermissions(document()));
 }
 
-TEST_F(CPDFSecurityHandlerEmbeddertest, UserPasswordVersion5) {
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion5) {
   ASSERT_TRUE(OpenDocumentWithPassword("bug_644.pdf", "b"));
   EXPECT_EQ(0xFFFFFFFC, FPDF_GetDocPermissions(document()));
 }
+
+// Should not crash. https://crbug.com/pdfium/1436
+TEST_F(CPDFSecurityHandlerEmbedderTest, BadOkeyVersion2) {
+  EXPECT_FALSE(
+      OpenDocumentWithPassword("encrypted_hello_world_r2_bad_okey.pdf", "a"));
+}
+
+// Should not crash. https://crbug.com/pdfium/1436
+TEST_F(CPDFSecurityHandlerEmbedderTest, BadOkeyVersion3) {
+  EXPECT_FALSE(
+      OpenDocumentWithPassword("encrypted_hello_world_r3_bad_okey.pdf", "a"));
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion2UTF8) {
+  // The password is "age", where the 'a' has a circumflex. Encoding the
+  // password as UTF-8 works.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r2.pdf",
+                                              kAgeUTF8);
+  EXPECT_EQ(2, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  // With revision 2 and 3, the owner password is not tied to the document ID in
+  // the trailer, so the owner password entry remains in the copy and is still
+  // valid, even though the document ID has changed.
+  // The user password is tied to the document ID, so without an existing ID,
+  // the user password entry has to be regenerated with the owner password.
+  // Since the user password was not used to decrypt the document, it cannot be
+  // recovered. Thus only verify the owner password, which is now also the user
+  // password.
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion2Latin1) {
+  // The same password encoded as Latin-1 also works at revision 2.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r2.pdf",
+                                              kAgeLatin1);
+  EXPECT_EQ(2, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion3UTF8) {
+  // Same as OwnerPasswordVersion2UTF8 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r3.pdf",
+                                              kAgeUTF8);
+  EXPECT_EQ(3, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion3Latin1) {
+  // Same as OwnerPasswordVersion2Latin1 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r3.pdf",
+                                              kAgeLatin1);
+  EXPECT_EQ(3, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion5UTF8) {
+  // Same as OwnerPasswordVersion2UTF8 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r5.pdf",
+                                              kAgeUTF8);
+  EXPECT_EQ(5, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion5Latin1) {
+  // Same as OwnerPasswordVersion2Latin1 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r5.pdf",
+                                              kAgeLatin1);
+  EXPECT_EQ(5, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion6UTF8) {
+  // Same as OwnerPasswordVersion2UTF8 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r6.pdf",
+                                              kAgeUTF8);
+  EXPECT_EQ(6, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion6Latin1) {
+  // Same as OwnerPasswordVersion2Latin1 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r6.pdf",
+                                              kAgeLatin1);
+  EXPECT_EQ(6, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion2UTF8) {
+  // The password is "hotel", where the 'o' has a circumflex. Encoding the
+  // password as UTF-8 works.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r2.pdf",
+                                              kHotelUTF8);
+  EXPECT_EQ(2, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  // Unlike the OwnerPasswordVersion2UTF8 test case, the user password was used
+  // to decrypt the document, so it is available to regenerated the user
+  // password entry. Thus it is possible to verify with both the unmodified
+  // owner password, and the updated user password.
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion2Latin1) {
+  // The same password encoded as Latin-1 also works at revision 2.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r2.pdf",
+                                              kHotelLatin1);
+  EXPECT_EQ(2, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion3UTF8) {
+  // Same as UserPasswordVersion2UTF8 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r3.pdf",
+                                              kHotelUTF8);
+  EXPECT_EQ(3, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion3Latin1) {
+  // Same as UserPasswordVersion2Latin1 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r3.pdf",
+                                              kHotelLatin1);
+  EXPECT_EQ(3, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion5UTF8) {
+  // Same as UserPasswordVersion2UTF8 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r5.pdf",
+                                              kHotelUTF8);
+  EXPECT_EQ(5, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion5Latin1) {
+  // Same as UserPasswordVersion2Latin1 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r5.pdf",
+                                              kHotelLatin1);
+  EXPECT_EQ(5, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion6UTF8) {
+  // Same as UserPasswordVersion2UTF8 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r6.pdf",
+                                              kHotelUTF8);
+  EXPECT_EQ(6, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
+
+TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion6Latin1) {
+  // Same as UserPasswordVersion2Latin1 test above.
+  OpenAndVerifyHelloWorldDocumentWithPassword("encrypted_hello_world_r6.pdf",
+                                              kHotelLatin1);
+  EXPECT_EQ(6, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kHotelUTF8);
+
+  ClearString();
+  RemoveGoodbyeObject();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kAgeUTF8);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1);
+  VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8);
+}
diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream.cpp b/core/fpdfapi/parser/cpdf_seekablemultistream.cpp
new file mode 100644
index 0000000..d2a0417
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_seekablemultistream.cpp
@@ -0,0 +1,85 @@
+// 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/parser/cpdf_seekablemultistream.h"
+
+#include <algorithm>
+
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/stl_util.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));
+    m_Data.back()->LoadAllDataFiltered();
+  }
+}
+
+CPDF_SeekableMultiStream::~CPDF_SeekableMultiStream() {}
+
+FX_FILESIZE CPDF_SeekableMultiStream::GetSize() {
+  uint32_t dwSize = 0;
+  for (const auto& acc : m_Data)
+    dwSize += acc->GetSize();
+  return dwSize;
+}
+
+bool CPDF_SeekableMultiStream::ReadBlockAtOffset(void* buffer,
+                                                 FX_FILESIZE offset,
+                                                 size_t size) {
+  int32_t iCount = pdfium::CollectionSize<int32_t>(m_Data);
+  int32_t index = 0;
+  while (index < iCount) {
+    const auto& acc = m_Data[index];
+    FX_FILESIZE dwSize = acc->GetSize();
+    if (offset < dwSize)
+      break;
+
+    offset -= dwSize;
+    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)
+      return true;
+
+    buffer = static_cast<uint8_t*>(buffer) + dwRead;
+    offset = 0;
+    index++;
+  }
+  return false;
+}
+
+size_t CPDF_SeekableMultiStream::ReadBlock(void* buffer, size_t size) {
+  NOTREACHED();
+  return 0;
+}
+
+FX_FILESIZE CPDF_SeekableMultiStream::GetPosition() {
+  return 0;
+}
+
+bool CPDF_SeekableMultiStream::IsEOF() {
+  return false;
+}
+
+bool CPDF_SeekableMultiStream::Flush() {
+  NOTREACHED();
+  return false;
+}
+
+bool CPDF_SeekableMultiStream::WriteBlockAtOffset(const void* pData,
+                                                  FX_FILESIZE offset,
+                                                  size_t size) {
+  NOTREACHED();
+  return false;
+}
diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream.h b/core/fpdfapi/parser/cpdf_seekablemultistream.h
new file mode 100644
index 0000000..30a8479
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_seekablemultistream.h
@@ -0,0 +1,41 @@
+// 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_PARSER_CPDF_SEEKABLEMULTISTREAM_H_
+#define CORE_FPDFAPI_PARSER_CPDF_SEEKABLEMULTISTREAM_H_
+
+#include <vector>
+
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_Stream;
+class CPDF_StreamAcc;
+
+class CPDF_SeekableMultiStream final : public IFX_SeekableStream {
+ public:
+  explicit CPDF_SeekableMultiStream(
+      const std::vector<const CPDF_Stream*>& streams);
+  ~CPDF_SeekableMultiStream() override;
+
+  // 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;
+  bool Flush() override;
+  bool WriteBlockAtOffset(const void* pData,
+                          FX_FILESIZE offset,
+                          size_t size) override;
+
+ private:
+  std::vector<RetainPtr<CPDF_StreamAcc>> m_Data;
+};
+
+#endif  // CORE_FPDFAPI_PARSER_CPDF_SEEKABLEMULTISTREAM_H_
diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
new file mode 100644
index 0000000..4f806fa
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
@@ -0,0 +1,85 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/cpdf_seekablemultistream.h"
+
+#include <memory>
+#include <vector>
+
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_stream.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);
+
+  uint8_t output_buffer[16];
+  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  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);
+
+  uint8_t output_buffer[16];
+  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  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>());
+
+  streams.push_back(stream1.Get());
+  streams.push_back(stream2.Get());
+  streams.push_back(stream3.Get());
+  auto fileread = pdfium::MakeRetain<CPDF_SeekableMultiStream>(streams);
+
+  uint8_t output_buffer[16];
+  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  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_EQ(0xbd, output_buffer[0]);
+
+  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0, 1));
+  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_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_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_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 b49f842..ff6e2cf 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_simple_parser.cpp
@@ -8,164 +8,123 @@
 
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 
-CPDF_SimpleParser::CPDF_SimpleParser(const uint8_t* pData, uint32_t dwSize)
-    : m_pData(pData), m_dwSize(dwSize), m_dwCurPos(0) {}
+CPDF_SimpleParser::CPDF_SimpleParser(pdfium::span<const uint8_t> input)
+    : data_(input) {}
 
-CPDF_SimpleParser::CPDF_SimpleParser(const ByteStringView& str)
-    : m_pData(str.raw_str()), m_dwSize(str.GetLength()), m_dwCurPos(0) {}
+CPDF_SimpleParser::~CPDF_SimpleParser() = default;
 
-std::pair<const uint8_t*, uint32_t> CPDF_SimpleParser::ParseWord() {
-  const uint8_t* pStart = nullptr;
-  uint8_t dwSize = 0;
+ByteStringView CPDF_SimpleParser::GetWord() {
   uint8_t ch;
+
+  // Skip whitespace and comment lines.
   while (1) {
-    if (m_dwSize <= m_dwCurPos)
-      return std::make_pair(pStart, dwSize);
-    ch = m_pData[m_dwCurPos++];
+    if (data_.size() <= cur_pos_)
+      return ByteStringView();
+
+    ch = data_[cur_pos_++];
     while (PDFCharIsWhitespace(ch)) {
-      if (m_dwSize <= m_dwCurPos)
-        return std::make_pair(pStart, dwSize);
-      ch = m_pData[m_dwCurPos++];
+      if (data_.size() <= cur_pos_)
+        return ByteStringView();
+      ch = data_[cur_pos_++];
     }
 
     if (ch != '%')
       break;
 
     while (1) {
-      if (m_dwSize <= m_dwCurPos)
-        return std::make_pair(pStart, dwSize);
-      ch = m_pData[m_dwCurPos++];
+      if (data_.size() <= cur_pos_)
+        return ByteStringView();
+
+      ch = data_[cur_pos_++];
       if (PDFCharIsLineEnding(ch))
         break;
     }
   }
 
-  uint32_t start_pos = m_dwCurPos - 1;
-  pStart = m_pData + start_pos;
+  uint8_t dwSize = 0;
+  uint32_t start_pos = cur_pos_ - 1;
   if (PDFCharIsDelimiter(ch)) {
+    // Find names
     if (ch == '/') {
       while (1) {
-        if (m_dwSize <= m_dwCurPos)
-          return std::make_pair(pStart, dwSize);
-        ch = m_pData[m_dwCurPos++];
+        if (data_.size() <= cur_pos_)
+          break;
+
+        ch = data_[cur_pos_++];
         if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) {
-          m_dwCurPos--;
-          dwSize = m_dwCurPos - start_pos;
-          return std::make_pair(pStart, dwSize);
+          cur_pos_--;
+          dwSize = cur_pos_ - start_pos;
+          break;
         }
       }
-    } else {
-      dwSize = 1;
-      if (ch == '<') {
-        if (m_dwSize <= m_dwCurPos)
-          return std::make_pair(pStart, dwSize);
-        ch = m_pData[m_dwCurPos++];
-        if (ch == '<')
-          dwSize = 2;
-        else
-          m_dwCurPos--;
-      } else if (ch == '>') {
-        if (m_dwSize <= m_dwCurPos)
-          return std::make_pair(pStart, dwSize);
-        ch = m_pData[m_dwCurPos++];
-        if (ch == '>')
-          dwSize = 2;
-        else
-          m_dwCurPos--;
-      }
+      return data_.subspan(start_pos, dwSize);
     }
-    return std::make_pair(pStart, dwSize);
+
+    dwSize = 1;
+    if (ch == '<') {
+      if (data_.size() <= cur_pos_)
+        return data_.subspan(start_pos, dwSize);
+
+      ch = data_[cur_pos_++];
+      if (ch == '<') {
+        dwSize = 2;
+      } else {
+        while (cur_pos_ < data_.size() && data_[cur_pos_] != '>')
+          cur_pos_++;
+
+        if (cur_pos_ < data_.size())
+          cur_pos_++;
+
+        dwSize = cur_pos_ - start_pos;
+      }
+    } else if (ch == '>') {
+      if (data_.size() <= cur_pos_)
+        return data_.subspan(start_pos, dwSize);
+
+      ch = data_[cur_pos_++];
+      if (ch == '>')
+        dwSize = 2;
+      else
+        cur_pos_--;
+    } else if (ch == '(') {
+      int level = 1;
+      while (cur_pos_ < data_.size()) {
+        if (data_[cur_pos_] == ')') {
+          level--;
+          if (level == 0)
+            break;
+        }
+
+        if (data_[cur_pos_] == '\\') {
+          if (data_.size() <= cur_pos_)
+            break;
+
+          cur_pos_++;
+        } else if (data_[cur_pos_] == '(') {
+          level++;
+        }
+        if (data_.size() <= cur_pos_)
+          break;
+
+        cur_pos_++;
+      }
+      if (cur_pos_ < data_.size())
+        cur_pos_++;
+
+      dwSize = cur_pos_ - start_pos;
+    }
+    return data_.subspan(start_pos, dwSize);
   }
 
   dwSize = 1;
-  while (1) {
-    if (m_dwSize <= m_dwCurPos)
-      return std::make_pair(pStart, dwSize);
-    ch = m_pData[m_dwCurPos++];
+  while (cur_pos_ < data_.size()) {
+    ch = data_[cur_pos_++];
 
     if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
-      m_dwCurPos--;
+      cur_pos_--;
       break;
     }
     dwSize++;
   }
-  return std::make_pair(pStart, dwSize);
-}
-
-ByteStringView CPDF_SimpleParser::GetWord() {
-  const uint8_t* pStart;
-  uint32_t dwSize;
-  std::tie(pStart, dwSize) = ParseWord();
-  if (dwSize == 1 && pStart[0] == '<') {
-    while (m_dwCurPos < m_dwSize && m_pData[m_dwCurPos] != '>') {
-      m_dwCurPos++;
-    }
-    if (m_dwCurPos < m_dwSize) {
-      m_dwCurPos++;
-    }
-    return ByteStringView(pStart,
-                          static_cast<size_t>(m_dwCurPos - (pStart - m_pData)));
-  }
-  if (dwSize == 1 && pStart[0] == '(') {
-    int level = 1;
-    while (m_dwCurPos < m_dwSize) {
-      if (m_pData[m_dwCurPos] == ')') {
-        level--;
-        if (level == 0) {
-          break;
-        }
-      }
-      if (m_pData[m_dwCurPos] == '\\') {
-        if (m_dwSize <= m_dwCurPos) {
-          break;
-        }
-        m_dwCurPos++;
-      } else if (m_pData[m_dwCurPos] == '(') {
-        level++;
-      }
-      if (m_dwSize <= m_dwCurPos) {
-        break;
-      }
-      m_dwCurPos++;
-    }
-    if (m_dwCurPos < m_dwSize) {
-      m_dwCurPos++;
-    }
-    return ByteStringView(pStart,
-                          static_cast<size_t>(m_dwCurPos - (pStart - m_pData)));
-  }
-  return ByteStringView(pStart, dwSize);
-}
-
-bool CPDF_SimpleParser::FindTagParamFromStart(const ByteStringView& token,
-                                              int nParams) {
-  nParams++;
-  uint32_t* pBuf = FX_Alloc(uint32_t, nParams);
-  int buf_index = 0;
-  int buf_count = 0;
-  m_dwCurPos = 0;
-  while (1) {
-    pBuf[buf_index++] = m_dwCurPos;
-    if (buf_index == nParams) {
-      buf_index = 0;
-    }
-    buf_count++;
-    if (buf_count > nParams) {
-      buf_count = nParams;
-    }
-    ByteStringView word = GetWord();
-    if (word.IsEmpty()) {
-      FX_Free(pBuf);
-      return false;
-    }
-    if (word == token) {
-      if (buf_count < nParams) {
-        continue;
-      }
-      m_dwCurPos = pBuf[buf_index];
-      FX_Free(pBuf);
-      return true;
-    }
-  }
-  return false;
+  return data_.subspan(start_pos, dwSize);
 }
diff --git a/core/fpdfapi/parser/cpdf_simple_parser.h b/core/fpdfapi/parser/cpdf_simple_parser.h
index 659039e..f9461b6 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser.h
+++ b/core/fpdfapi/parser/cpdf_simple_parser.h
@@ -7,31 +7,23 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_
 
-#include <utility>
-
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
 
 class CPDF_SimpleParser {
  public:
-  CPDF_SimpleParser(const uint8_t* pData, uint32_t dwSize);
-  explicit CPDF_SimpleParser(const ByteStringView& str);
+  explicit CPDF_SimpleParser(pdfium::span<const uint8_t> input);
+  ~CPDF_SimpleParser();
 
   ByteStringView GetWord();
 
-  // Find the token and its |nParams| parameters from the start of data,
-  // and move the current position to the start of those parameters.
-  bool FindTagParamFromStart(const ByteStringView& token, int nParams);
-
-  // For testing only.
-  uint32_t GetCurPos() const { return m_dwCurPos; }
+  void SetCurPos(uint32_t pos) { cur_pos_ = pos; }
+  uint32_t GetCurPos() const { return cur_pos_; }
 
  private:
-  std::pair<const uint8_t*, uint32_t> ParseWord();
-
-  const uint8_t* m_pData;
-  uint32_t m_dwSize;
-  uint32_t m_dwCurPos;
+  const pdfium::span<const uint8_t> data_;
+  uint32_t cur_pos_ = 0;
 };
 
 #endif  // CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_
diff --git a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
index e8d3b71..5834d77 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
@@ -9,6 +9,7 @@
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
+#include "third_party/base/span.h"
 
 TEST(SimpleParserTest, GetWord) {
   static const pdfium::StrFuncTestData test_data[] = {
@@ -37,19 +38,21 @@
       STR_IN_OUT_CASE("\t\t<< /abc>>", "<<"),
       // Handling ending delimiters.
       STR_IN_OUT_CASE("> little bear", ">"),
-      STR_IN_OUT_CASE(") another bear", ")"), STR_IN_OUT_CASE(">> end ", ">>"),
+      STR_IN_OUT_CASE(") another bear", ")"),
+      STR_IN_OUT_CASE(">> end ", ">>"),
       // No ending delimiters.
       STR_IN_OUT_CASE("(sdfgfgbcv", "(sdfgfgbcv"),
       // Regular cases.
       STR_IN_OUT_CASE("apple pear", "apple"),
       STR_IN_OUT_CASE(" pi=3.1415 ", "pi=3.1415"),
-      STR_IN_OUT_CASE(" p t x c ", "p"), STR_IN_OUT_CASE(" pt\0xc ", "pt"),
+      STR_IN_OUT_CASE(" p t x c ", "p"),
+      STR_IN_OUT_CASE(" pt\0xc ", "pt"),
       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) {
     const pdfium::StrFuncTestData& data = test_data[i];
-    CPDF_SimpleParser parser(data.input, data.input_size);
+    CPDF_SimpleParser parser(pdfium::make_span(data.input, data.input_size));
     ByteStringView word = parser.GetWord();
     EXPECT_EQ(data.expected_size, word.GetLength()) << " for case " << i;
     if (data.expected_size != word.GetLength())
@@ -59,39 +62,3 @@
         << " for case " << i;
   }
 }
-
-TEST(SimpleParserTest, FindTagParamFromStart) {
-  static const struct FindTagTestStruct {
-    const unsigned char* input;
-    unsigned int input_size;
-    const char* token;
-    int num_params;
-    bool result;
-    unsigned int result_pos;
-  } test_data[] = {
-      // Empty strings.
-      STR_IN_TEST_CASE("", "Tj", 1, false, 0),
-      STR_IN_TEST_CASE("", "", 1, false, 0),
-      // Empty token.
-      STR_IN_TEST_CASE("  T j", "", 1, false, 5),
-      // No parameter.
-      STR_IN_TEST_CASE("Tj", "Tj", 1, false, 2),
-      STR_IN_TEST_CASE("(Tj", "Tj", 1, false, 3),
-      // Partial token match.
-      STR_IN_TEST_CASE("\r12\t34  56 78Tj", "Tj", 1, false, 15),
-      // Regular cases with various parameters.
-      STR_IN_TEST_CASE("\r\0abd Tj", "Tj", 1, true, 0),
-      STR_IN_TEST_CASE("12 4 Tj 3 46 Tj", "Tj", 1, true, 2),
-      STR_IN_TEST_CASE("er^ 2 (34) (5667) Tj", "Tj", 2, true, 5),
-      STR_IN_TEST_CASE("<344> (232)\t343.4\n12 45 Tj", "Tj", 3, true, 11),
-      STR_IN_TEST_CASE("1 2 3 4 5 6 7 8 cm", "cm", 6, true, 3),
-  };
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
-    const FindTagTestStruct& data = test_data[i];
-    CPDF_SimpleParser parser(data.input, data.input_size);
-    EXPECT_EQ(data.result,
-              parser.FindTagParamFromStart(data.token, data.num_params))
-        << " for case " << i;
-    EXPECT_EQ(data.result_pos, parser.GetCurPos()) << " for case " << i;
-  }
-}
diff --git a/core/fpdfapi/parser/cpdf_stream.cpp b/core/fpdfapi/parser/cpdf_stream.cpp
index 074e747..4b5ef5c 100644
--- a/core/fpdfapi/parser/cpdf_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_stream.cpp
@@ -7,8 +7,12 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 
 #include <utility>
+#include <vector>
 
+#include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_encryptor.h"
+#include "core/fpdfapi/parser/cpdf_flateencoder.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
@@ -17,27 +21,40 @@
 #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";
+}
+
+}  // namespace
+
 CPDF_Stream::CPDF_Stream() {}
 
 CPDF_Stream::CPDF_Stream(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
                          uint32_t size,
-                         std::unique_ptr<CPDF_Dictionary> pDict)
+                         RetainPtr<CPDF_Dictionary> pDict)
     : m_pDict(std::move(pDict)) {
-  SetData(std::move(pData), size);
+  TakeData(std::move(pData), size);
 }
 
 CPDF_Stream::~CPDF_Stream() {
   m_ObjNum = kInvalidObjNum;
   if (m_pDict && m_pDict->GetObjNum() == kInvalidObjNum)
-    m_pDict.release();  // lowercase release, release ownership.
+    m_pDict.Leak();  // lowercase release, release ownership.
 }
 
 CPDF_Object::Type CPDF_Stream::GetType() const {
-  return STREAM;
+  return kStream;
 }
 
-CPDF_Dictionary* CPDF_Stream::GetDict() const {
-  return m_pDict.get();
+CPDF_Dictionary* CPDF_Stream::GetDict() {
+  return m_pDict.Get();
+}
+
+const CPDF_Dictionary* CPDF_Stream::GetDict() const {
+  return m_pDict.Get();
 }
 
 bool CPDF_Stream::IsStream() const {
@@ -52,30 +69,28 @@
   return this;
 }
 
-void CPDF_Stream::InitStream(const uint8_t* pData,
-                             uint32_t size,
-                             std::unique_ptr<CPDF_Dictionary> pDict) {
+void CPDF_Stream::InitStream(pdfium::span<const uint8_t> pData,
+                             RetainPtr<CPDF_Dictionary> pDict) {
   m_pDict = std::move(pDict);
-  SetData(pData, size);
+  SetData(pData);
 }
 
 void CPDF_Stream::InitStreamFromFile(
     const RetainPtr<IFX_SeekableReadStream>& pFile,
-    std::unique_ptr<CPDF_Dictionary> pDict) {
-  m_pDict = std::move(pDict);
+    RetainPtr<CPDF_Dictionary> pDict) {
   m_bMemoryBased = false;
   m_pDataBuf.reset();
   m_pFile = pFile;
   m_dwSize = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
-  if (m_pDict)
-    m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(m_dwSize));
+  m_pDict = std::move(pDict);
+  m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(m_dwSize));
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Stream::Clone() const {
+RetainPtr<CPDF_Object> CPDF_Stream::Clone() const {
   return CloneObjectNonCyclic(false);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_Stream::CloneNonCyclic(
+RetainPtr<CPDF_Object> CPDF_Stream::CloneNonCyclic(
     bool bDirect,
     std::set<const CPDF_Object*>* pVisited) const {
   pVisited->insert(this);
@@ -83,57 +98,69 @@
   pAcc->LoadAllDataRaw();
 
   uint32_t streamSize = pAcc->GetSize();
-  CPDF_Dictionary* pDict = GetDict();
-  std::unique_ptr<CPDF_Dictionary> pNewDict;
+  const CPDF_Dictionary* pDict = GetDict();
+  RetainPtr<CPDF_Dictionary> pNewDict;
   if (pDict && !pdfium::ContainsKey(*pVisited, pDict)) {
-    pNewDict = ToDictionary(
-        static_cast<CPDF_Object*>(pDict)->CloneNonCyclic(bDirect, pVisited));
+    pNewDict =
+        ToDictionary(static_cast<const CPDF_Object*>(pDict)->CloneNonCyclic(
+            bDirect, pVisited));
   }
-  return pdfium::MakeUnique<CPDF_Stream>(pAcc->DetachData(), streamSize,
+  return pdfium::MakeRetain<CPDF_Stream>(pAcc->DetachData(), streamSize,
                                          std::move(pNewDict));
 }
 
-void CPDF_Stream::SetDataAndRemoveFilter(const uint8_t* pData, uint32_t size) {
-  SetData(pData, size);
+void CPDF_Stream::SetDataAndRemoveFilter(pdfium::span<const uint8_t> pData) {
+  SetData(pData);
   m_pDict->RemoveFor("Filter");
-  m_pDict->RemoveFor("DecodeParms");
+  m_pDict->RemoveFor(pdfium::stream::kDecodeParms);
 }
 
-void CPDF_Stream::SetDataAndRemoveFilter(std::ostringstream* stream) {
-  SetDataAndRemoveFilter(
-      reinterpret_cast<const uint8_t*>(stream->str().c_str()), stream->tellp());
-}
-
-void CPDF_Stream::SetData(const uint8_t* pData, uint32_t size) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> data_copy;
-  if (pData) {
-    data_copy.reset(FX_Alloc(uint8_t, size));
-    memcpy(data_copy.get(), pData, size);
+void CPDF_Stream::SetDataFromStringstreamAndRemoveFilter(
+    std::ostringstream* stream) {
+  if (stream->tellp() <= 0) {
+    SetDataAndRemoveFilter({});
+    return;
   }
-  SetData(std::move(data_copy), size);
+
+  SetDataAndRemoveFilter(
+      {reinterpret_cast<const uint8_t*>(stream->str().c_str()),
+       static_cast<size_t>(stream->tellp())});
 }
 
-void CPDF_Stream::SetData(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-                          uint32_t size) {
+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());
+}
+
+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::MakeUnique<CPDF_Dictionary>();
+    m_pDict = pdfium::MakeRetain<CPDF_Dictionary>();
   m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(size));
 }
 
-void CPDF_Stream::SetData(std::ostringstream* stream) {
-  SetData(reinterpret_cast<const uint8_t*>(stream->str().c_str()),
-          stream->tellp());
+void CPDF_Stream::SetDataFromStringstream(std::ostringstream* stream) {
+  if (stream->tellp() <= 0) {
+    SetData({});
+    return;
+  }
+  SetData({reinterpret_cast<const uint8_t*>(stream->str().c_str()),
+           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->ReadBlock(buf, offset, size);
+    return m_pFile->ReadBlockAtOffset(buf, offset, size);
 
   if (m_pDataBuf)
     memcpy(buf, m_pDataBuf.get() + offset, size);
@@ -148,15 +175,40 @@
 WideString CPDF_Stream::GetUnicodeText() const {
   auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(this);
   pAcc->LoadAllDataFiltered();
-  return PDF_DecodeText(pAcc->GetData(), pAcc->GetSize());
+  return PDF_DecodeText(pAcc->GetSpan());
 }
 
-bool CPDF_Stream::WriteTo(IFX_ArchiveStream* archive) const {
-  if (!GetDict()->WriteTo(archive) || !archive->WriteString("stream\r\n"))
+bool CPDF_Stream::WriteTo(IFX_ArchiveStream* archive,
+                          const CPDF_Encryptor* encryptor) const {
+  const bool is_metadata = IsMetaDataStreamDictionary(GetDict());
+  CPDF_FlateEncoder encoder(this, !is_metadata);
+
+  std::vector<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))
     return false;
 
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(this);
-  pAcc->LoadAllDataRaw();
-  return archive->WriteBlock(pAcc->GetData(), pAcc->GetSize()) &&
-         archive->WriteString("\r\nendstream");
+  if (!archive->WriteString("stream\r\n"))
+    return false;
+
+  if (size && !archive->WriteBlock(data.data(), size))
+    return false;
+
+  if (!archive->WriteString("\r\nendstream"))
+    return false;
+
+  return true;
 }
diff --git a/core/fpdfapi/parser/cpdf_stream.h b/core/fpdfapi/parser/cpdf_stream.h
index f8b09af..d29f655 100644
--- a/core/fpdfapi/parser/cpdf_stream.h
+++ b/core/fpdfapi/parser/cpdf_stream.h
@@ -11,64 +11,70 @@
 #include <set>
 #include <sstream>
 
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_stream.h"
 
-class CPDF_Stream : public CPDF_Object {
+class CPDF_Stream final : public CPDF_Object {
  public:
-  CPDF_Stream();
+  static const int kFileBufSize = 512;
 
-  // Takes ownership of |pData|.
-  CPDF_Stream(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
-              uint32_t size,
-              std::unique_ptr<CPDF_Dictionary> pDict);
-
-  ~CPDF_Stream() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
-  CPDF_Dictionary* GetDict() 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;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   uint32_t GetRawSize() const { return m_dwSize; }
-  uint8_t* GetRawData() const { return m_pDataBuf.get(); }
+  // 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(); }
 
-  // Does not takes ownership of |pData|, copies into internally-owned buffer.
-  void SetData(const uint8_t* pData, uint32_t size);
-  void SetData(std::unique_ptr<uint8_t, FxFreeDeleter> pData, uint32_t size);
-  void SetData(std::ostringstream* stream);
+  // Copies span into internally-owned buffer.
+  void SetData(pdfium::span<const uint8_t> pData);
+
+  void TakeData(std::unique_ptr<uint8_t, FxFreeDeleter> pData, uint32_t size);
+
+  void SetDataFromStringstream(std::ostringstream* stream);
+
   // Set data and remove "Filter" and "DecodeParms" fields from stream
   // dictionary.
-  void SetDataAndRemoveFilter(const uint8_t* pData, uint32_t size);
-  void SetDataAndRemoveFilter(std::ostringstream* stream);
+  void SetDataAndRemoveFilter(pdfium::span<const uint8_t> pData);
+  void SetDataFromStringstreamAndRemoveFilter(std::ostringstream* stream);
 
-  void InitStream(const uint8_t* pData,
-                  uint32_t size,
-                  std::unique_ptr<CPDF_Dictionary> pDict);
+  void InitStream(pdfium::span<const uint8_t> pData,
+                  RetainPtr<CPDF_Dictionary> pDict);
   void InitStreamFromFile(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                          std::unique_ptr<CPDF_Dictionary> pDict);
+                          RetainPtr<CPDF_Dictionary> pDict);
 
-  bool ReadRawData(FX_FILESIZE start_pos,
-                   uint8_t* pBuf,
-                   uint32_t buf_size) const;
+  bool ReadRawData(FX_FILESIZE offset, uint8_t* pBuf, uint32_t buf_size) const;
 
   bool IsMemoryBased() const { return m_bMemoryBased; }
   bool HasFilter() const;
 
- protected:
-  std::unique_ptr<CPDF_Object> CloneNonCyclic(
+ private:
+  CPDF_Stream();
+  CPDF_Stream(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
+              uint32_t size,
+              RetainPtr<CPDF_Dictionary> pDict);
+  ~CPDF_Stream() override;
+
+  RetainPtr<CPDF_Object> CloneNonCyclic(
       bool bDirect,
       std::set<const CPDF_Object*>* pVisited) const override;
 
   bool m_bMemoryBased = true;
   uint32_t m_dwSize = 0;
-  std::unique_ptr<CPDF_Dictionary> m_pDict;
+  RetainPtr<CPDF_Dictionary> m_pDict;
   std::unique_ptr<uint8_t, FxFreeDeleter> m_pDataBuf;
   RetainPtr<IFX_SeekableReadStream> m_pFile;
 };
@@ -81,12 +87,8 @@
   return obj ? obj->AsStream() : nullptr;
 }
 
-inline std::unique_ptr<CPDF_Stream> ToStream(std::unique_ptr<CPDF_Object> obj) {
-  CPDF_Stream* pStream = ToStream(obj.get());
-  if (!pStream)
-    return nullptr;
-  obj.release();
-  return std::unique_ptr<CPDF_Stream>(pStream);
+inline RetainPtr<CPDF_Stream> ToStream(RetainPtr<CPDF_Object> obj) {
+  return RetainPtr<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 d115b48..cd99751 100644
--- a/core/fpdfapi/parser/cpdf_stream_acc.cpp
+++ b/core/fpdfapi/parser/cpdf_stream_acc.cpp
@@ -6,96 +6,165 @@
 
 #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"
 
 CPDF_StreamAcc::CPDF_StreamAcc(const CPDF_Stream* pStream)
     : m_pStream(pStream) {}
 
-CPDF_StreamAcc::~CPDF_StreamAcc() {
-  if (m_bNewBuf)
-    FX_Free(m_pData);
-  FX_Free(m_pSrcData);
-}
+CPDF_StreamAcc::~CPDF_StreamAcc() = default;
 
 void CPDF_StreamAcc::LoadAllData(bool bRawAccess,
                                  uint32_t estimated_size,
                                  bool bImageAcc) {
+  if (bRawAccess) {
+    ASSERT(!estimated_size);
+    ASSERT(!bImageAcc);
+  }
+
   if (!m_pStream)
     return;
 
   bool bProcessRawData = bRawAccess || !m_pStream->HasFilter();
-  if (bProcessRawData && m_pStream->IsMemoryBased()) {
-    m_dwSize = m_pStream->GetRawSize();
-    m_pData = m_pStream->GetRawData();
-    return;
-  }
-  uint32_t dwSrcSize = m_pStream->GetRawSize();
-  if (dwSrcSize == 0)
-    return;
-
-  uint8_t* pSrcData;
-  if (m_pStream->IsMemoryBased()) {
-    pSrcData = m_pStream->GetRawData();
-  } else {
-    pSrcData = m_pSrcData = FX_Alloc(uint8_t, dwSrcSize);
-    if (!m_pStream->ReadRawData(0, pSrcData, dwSrcSize))
-      return;
-  }
-  if (bProcessRawData) {
-    m_pData = pSrcData;
-    m_dwSize = dwSrcSize;
-  } else if (!PDF_DataDecode(pSrcData, dwSrcSize, m_pStream->GetDict(),
-                             estimated_size, bImageAcc, &m_pData, &m_dwSize,
-                             &m_ImageDecoder, &m_pImageParam)) {
-    m_pData = pSrcData;
-    m_dwSize = dwSrcSize;
-  }
-  if (pSrcData != m_pStream->GetRawData() && pSrcData != m_pData)
-    FX_Free(pSrcData);
-  m_pSrcData = nullptr;
-  m_bNewBuf = m_pData != m_pStream->GetRawData();
+  if (bProcessRawData)
+    ProcessRawData();
+  else
+    ProcessFilteredData(estimated_size, bImageAcc);
 }
 
 void CPDF_StreamAcc::LoadAllDataFiltered() {
   LoadAllData(false, 0, false);
 }
 
+void CPDF_StreamAcc::LoadAllDataFilteredWithEstimatedSize(
+    uint32_t estimated_size) {
+  LoadAllData(false, estimated_size, false);
+}
+
+void CPDF_StreamAcc::LoadAllDataImageAcc(uint32_t estimated_size) {
+  LoadAllData(false, estimated_size, true);
+}
+
 void CPDF_StreamAcc::LoadAllDataRaw() {
   LoadAllData(true, 0, false);
 }
 
-CPDF_Dictionary* CPDF_StreamAcc::GetDict() const {
+const CPDF_Dictionary* CPDF_StreamAcc::GetDict() const {
   return m_pStream ? m_pStream->GetDict() : nullptr;
 }
 
-const uint8_t* CPDF_StreamAcc::GetData() const {
-  return GetDataHelper();
-}
-
-uint8_t* CPDF_StreamAcc::GetData() {
-  return GetDataHelper();
+uint8_t* CPDF_StreamAcc::GetData() const {
+  if (m_pData.IsOwned())
+    return m_pData.Get();
+  return m_pStream ? m_pStream->GetInMemoryRawData() : nullptr;
 }
 
 uint32_t CPDF_StreamAcc::GetSize() const {
-  if (m_bNewBuf)
+  if (m_pData.IsOwned())
     return m_dwSize;
-  return m_pStream ? m_pStream->GetRawSize() : 0;
+  return (m_pStream && m_pStream->IsMemoryBased()) ? m_pStream->GetRawSize()
+                                                   : 0;
+}
+
+pdfium::span<uint8_t> CPDF_StreamAcc::GetSpan() {
+  return {GetData(), GetSize()};
+}
+
+pdfium::span<const uint8_t> CPDF_StreamAcc::GetSpan() const {
+  return {GetData(), GetSize()};
+}
+
+ByteString CPDF_StreamAcc::ComputeDigest() const {
+  uint8_t digest[20];
+  CRYPT_SHA1Generate(GetData(), GetSize(), digest);
+  return ByteString(digest, 20);
 }
 
 std::unique_ptr<uint8_t, FxFreeDeleter> CPDF_StreamAcc::DetachData() {
-  if (m_bNewBuf) {
-    std::unique_ptr<uint8_t, FxFreeDeleter> p(m_pData);
-    m_pData = nullptr;
+  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, m_dwSize);
+  memcpy(p.get(), m_pData.Get(), m_dwSize);
   return p;
 }
 
-uint8_t* CPDF_StreamAcc::GetDataHelper() const {
-  if (m_bNewBuf)
-    return m_pData;
-  return m_pStream ? m_pStream->GetRawData() : nullptr;
+void CPDF_StreamAcc::ProcessRawData() {
+  uint32_t dwSrcSize = m_pStream->GetRawSize();
+  if (dwSrcSize == 0)
+    return;
+
+  if (m_pStream->IsMemoryBased()) {
+    m_pData = m_pStream->GetInMemoryRawData();
+    m_dwSize = dwSrcSize;
+    return;
+  }
+
+  std::unique_ptr<uint8_t, FxFreeDeleter> pData = ReadRawStream();
+  if (!pData)
+    return;
+
+  m_pData = std::move(pData);
+  m_dwSize = dwSrcSize;
+}
+
+void CPDF_StreamAcc::ProcessFilteredData(uint32_t estimated_size,
+                                         bool bImageAcc) {
+  uint32_t dwSrcSize = m_pStream->GetRawSize();
+  if (dwSrcSize == 0)
+    return;
+
+  MaybeOwned<uint8_t, FxFreeDeleter> pSrcData;
+  if (m_pStream->IsMemoryBased()) {
+    pSrcData = m_pStream->GetInMemoryRawData();
+  } else {
+    std::unique_ptr<uint8_t, FxFreeDeleter> pTempSrcData = ReadRawStream();
+    if (!pTempSrcData)
+      return;
+
+    pSrcData = std::move(pTempSrcData);
+  }
+
+  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,
+                      decoder_array.value(), &pDecodedData, &dwDecodedSize,
+                      &m_ImageDecoder, &m_pImageParam)) {
+    m_pData = std::move(pSrcData);
+    m_dwSize = dwSrcSize;
+    return;
+  }
+
+  if (pDecodedData) {
+    ASSERT(pDecodedData.get() != pSrcData.Get());
+    m_pData = std::move(pDecodedData);
+    m_dwSize = dwDecodedSize;
+  } else {
+    m_pData = std::move(pSrcData);
+    m_dwSize = dwSrcSize;
+  }
+}
+
+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;
 }
diff --git a/core/fpdfapi/parser/cpdf_stream_acc.h b/core/fpdfapi/parser/cpdf_stream_acc.h
index d54e000..046fe5d 100644
--- a/core/fpdfapi/parser/cpdf_stream_acc.h
+++ b/core/fpdfapi/parser/cpdf_stream_acc.h
@@ -9,13 +9,17 @@
 
 #include <memory>
 
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
+#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/retain_ptr.h"
+#include "third_party/base/span.h"
 
-class CPDF_StreamAcc : public Retainable {
+class CPDF_Dictionary;
+class CPDF_Stream;
+
+class CPDF_StreamAcc final : public Retainable {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
@@ -23,34 +27,39 @@
   CPDF_StreamAcc(const CPDF_StreamAcc&) = delete;
   CPDF_StreamAcc& operator=(const CPDF_StreamAcc&) = delete;
 
-  void LoadAllData(bool bRawAccess, uint32_t estimated_size, bool bImageAcc);
   void LoadAllDataFiltered();
+  void LoadAllDataFilteredWithEstimatedSize(uint32_t estimated_size);
+  void LoadAllDataImageAcc(uint32_t estimated_size);
   void LoadAllDataRaw();
 
   const CPDF_Stream* GetStream() const { return m_pStream.Get(); }
-  CPDF_Dictionary* GetDict() const;
+  const CPDF_Dictionary* GetDict() const;
 
-  const uint8_t* GetData() const;
-  uint8_t* GetData();
+  uint8_t* GetData() const;
   uint32_t GetSize() const;
-  const ByteString& GetImageDecoder() const { return m_ImageDecoder; }
-  const CPDF_Dictionary* GetImageParam() const { return m_pImageParam; }
+  pdfium::span<uint8_t> GetSpan();
+  pdfium::span<const uint8_t> GetSpan() 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();
 
- protected:
+ private:
   explicit CPDF_StreamAcc(const CPDF_Stream* pStream);
   ~CPDF_StreamAcc() override;
 
- private:
-  uint8_t* GetDataHelper() const;
+  void LoadAllData(bool bRawAccess, uint32_t estimated_size, bool bImageAcc);
+  void ProcessRawData();
+  void ProcessFilteredData(uint32_t estimated_size, bool bImageAcc);
 
-  uint8_t* m_pData = nullptr;
+  // Reads the raw data from |m_pStream|, or return nullptr on failure.
+  std::unique_ptr<uint8_t, FxFreeDeleter> ReadRawStream() const;
+
+  MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
   uint32_t m_dwSize = 0;
-  bool m_bNewBuf = false;
   ByteString m_ImageDecoder;
-  CPDF_Dictionary* m_pImageParam = nullptr;
-  UnownedPtr<const CPDF_Stream> const m_pStream;
-  uint8_t* m_pSrcData = nullptr;
+  RetainPtr<const CPDF_Dictionary> m_pImageParam;
+  RetainPtr<const CPDF_Stream> const m_pStream;
 };
 
 #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
new file mode 100644
index 0000000..72ba93d
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp
@@ -0,0 +1,22 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+
+#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) {
+  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());
+  stream_acc->LoadAllDataRaw();
+  EXPECT_EQ(0u, stream_acc->GetSize());
+  EXPECT_FALSE(stream_acc->GetData());
+}
diff --git a/core/fpdfapi/parser/cpdf_string.cpp b/core/fpdfapi/parser/cpdf_string.cpp
index 851442e..7058169 100644
--- a/core/fpdfapi/parser/cpdf_string.cpp
+++ b/core/fpdfapi/parser/cpdf_string.cpp
@@ -7,12 +7,14 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 
 #include <utility>
+#include <vector>
 
+#include "core/fpdfapi/parser/cpdf_encryptor.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fxcrt/fx_stream.h"
 #include "third_party/base/ptr_util.h"
 
-CPDF_String::CPDF_String() : m_bHex(false) {}
+CPDF_String::CPDF_String() = default;
 
 CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool,
                          const ByteString& str,
@@ -23,22 +25,22 @@
 }
 
 CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool, const WideString& str)
-    : m_String(PDF_EncodeText(str)), m_bHex(false) {
+    : m_String(PDF_EncodeText(str)) {
   if (pPool)
     m_String = pPool->Intern(m_String);
 }
 
-CPDF_String::~CPDF_String() {}
+CPDF_String::~CPDF_String() = default;
 
 CPDF_Object::Type CPDF_String::GetType() const {
-  return STRING;
+  return kString;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_String::Clone() const {
-  auto pRet = pdfium::MakeUnique<CPDF_String>();
+RetainPtr<CPDF_Object> CPDF_String::Clone() const {
+  auto pRet = pdfium::MakeRetain<CPDF_String>();
   pRet->m_String = m_String;
   pRet->m_bHex = m_bHex;
-  return std::move(pRet);
+  return pRet;
 }
 
 ByteString CPDF_String::GetString() const {
@@ -62,10 +64,18 @@
 }
 
 WideString CPDF_String::GetUnicodeText() const {
-  return PDF_DecodeText(m_String);
+  return PDF_DecodeText(m_String.raw_span());
 }
 
-bool CPDF_String::WriteTo(IFX_ArchiveStream* archive) const {
-  return archive->WriteString(
-      PDF_EncodeString(GetString(), IsHex()).AsStringView());
+bool CPDF_String::WriteTo(IFX_ArchiveStream* archive,
+                          const CPDF_Encryptor* encryptor) const {
+  std::vector<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());
+  return archive->WriteString(content.AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_string.h b/core/fpdfapi/parser/cpdf_string.h
index b6aaacb..8efc71b 100644
--- a/core/fpdfapi/parser/cpdf_string.h
+++ b/core/fpdfapi/parser/cpdf_string.h
@@ -15,29 +15,33 @@
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
 
-class CPDF_String : public CPDF_Object {
+class CPDF_String final : public CPDF_Object {
  public:
-  CPDF_String();
-  CPDF_String(WeakPtr<ByteStringPool> pPool, const ByteString& str, bool bHex);
-  CPDF_String(WeakPtr<ByteStringPool> pPool, const WideString& str);
-  ~CPDF_String() override;
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
   // CPDF_Object:
   Type GetType() const override;
-  std::unique_ptr<CPDF_Object> Clone() const override;
+  RetainPtr<CPDF_Object> Clone() const override;
   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;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   bool IsHex() const { return m_bHex; }
 
- protected:
+ private:
+  CPDF_String();
+  CPDF_String(WeakPtr<ByteStringPool> pPool, const ByteString& str, bool bHex);
+  CPDF_String(WeakPtr<ByteStringPool> pPool, const WideString& str);
+  ~CPDF_String() override;
+
   ByteString m_String;
-  bool m_bHex;
+  bool m_bHex = false;
 };
 
 inline CPDF_String* ToString(CPDF_Object* obj) {
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.cpp b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
index 66ce183..0a55c27 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
@@ -11,7 +11,6 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_boolean.h"
 #include "core/fpdfapi/parser/cpdf_crypto_handler.h"
@@ -23,11 +22,11 @@
 #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/fxcrt/autorestorer.h"
 #include "core/fxcrt/cfx_binarybuf.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/ptr_util.h"
 
@@ -35,20 +34,70 @@
 
 enum class ReadStatus { Normal, Backslash, Octal, FinishOctal, CarriageReturn };
 
+class ReadableSubStream final : public IFX_SeekableReadStream {
+ public:
+  ReadableSubStream(const RetainPtr<IFX_SeekableReadStream>& pFileRead,
+                    FX_FILESIZE part_offset,
+                    FX_FILESIZE part_size)
+      : m_pFileRead(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 {
+    FX_SAFE_FILESIZE safe_end = offset;
+    safe_end += 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);
+  }
+
+  FX_FILESIZE GetSize() override { return m_PartSize; }
+
+ private:
+  RetainPtr<IFX_SeekableReadStream> m_pFileRead;
+  FX_FILESIZE m_PartOffset;
+  FX_FILESIZE m_PartSize;
+};
+
 }  // namespace
 
 // static
 int CPDF_SyntaxParser::s_CurrentRecursionDepth = 0;
 
-CPDF_SyntaxParser::CPDF_SyntaxParser()
-    : CPDF_SyntaxParser(WeakPtr<ByteStringPool>()) {}
-
-CPDF_SyntaxParser::CPDF_SyntaxParser(const WeakPtr<ByteStringPool>& pPool)
-    : m_pFileAccess(nullptr), m_pPool(pPool) {}
-
-CPDF_SyntaxParser::~CPDF_SyntaxParser() {
+// static
+std::unique_ptr<CPDF_SyntaxParser> CPDF_SyntaxParser::CreateForTesting(
+    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
+    FX_FILESIZE HeaderOffset) {
+  return pdfium::MakeUnique<CPDF_SyntaxParser>(
+      pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr),
+      HeaderOffset);
 }
 
+CPDF_SyntaxParser::CPDF_SyntaxParser(
+    const RetainPtr<IFX_SeekableReadStream>& pFileAccess)
+    : CPDF_SyntaxParser(
+          pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr),
+          0) {}
+
+CPDF_SyntaxParser::CPDF_SyntaxParser(
+    const RetainPtr<CPDF_ReadValidator>& validator,
+    FX_FILESIZE HeaderOffset)
+    : m_pFileAccess(validator),
+      m_HeaderOffset(HeaderOffset),
+      m_FileLen(m_pFileAccess->GetSize()) {
+  ASSERT(m_HeaderOffset <= m_FileLen);
+}
+
+CPDF_SyntaxParser::~CPDF_SyntaxParser() = default;
+
 bool CPDF_SyntaxParser::GetCharAt(FX_FILESIZE pos, uint8_t& ch) {
   AutoRestorer<FX_FILESIZE> save_pos(&m_Pos);
   m_Pos = pos;
@@ -58,14 +107,15 @@
 bool CPDF_SyntaxParser::ReadBlockAt(FX_FILESIZE read_pos) {
   if (read_pos >= m_FileLen)
     return false;
-  size_t read_size = CPDF_ModuleMgr::kFileBufSize;
+  size_t read_size = m_ReadBufferSize;
   FX_SAFE_FILESIZE safe_end = read_pos;
   safe_end += read_size;
   if (!safe_end.IsValid() || safe_end.ValueOrDie() > m_FileLen)
     read_size = m_FileLen - read_pos;
 
   m_pFileBuf.resize(read_size);
-  if (!m_pFileAccess->ReadBlock(m_pFileBuf.data(), read_pos, read_size)) {
+  if (!m_pFileAccess->ReadBlockAtOffset(m_pFileBuf.data(), read_pos,
+                                        read_size)) {
     m_pFileBuf.clear();
     return false;
   }
@@ -87,6 +137,10 @@
   return true;
 }
 
+FX_FILESIZE CPDF_SyntaxParser::GetDocumentSize() const {
+  return m_FileLen - m_HeaderOffset;
+}
+
 bool CPDF_SyntaxParser::GetCharAtBackward(FX_FILESIZE pos, uint8_t* ch) {
   pos += m_HeaderOffset;
   if (pos >= m_FileLen)
@@ -94,8 +148,8 @@
 
   if (!IsPositionRead(pos)) {
     FX_FILESIZE block_start = 0;
-    if (pos >= CPDF_ModuleMgr::kFileBufSize)
-      block_start = pos - CPDF_ModuleMgr::kFileBufSize + 1;
+    if (pos >= CPDF_Stream::kFileBufSize)
+      block_start = pos - CPDF_Stream::kFileBufSize + 1;
     if (!ReadBlockAt(block_start) || !IsPositionRead(pos))
       return false;
   }
@@ -104,7 +158,7 @@
 }
 
 bool CPDF_SyntaxParser::ReadBlock(uint8_t* pBuf, uint32_t size) {
-  if (!m_pFileAccess->ReadBlock(pBuf, m_Pos + m_HeaderOffset, size))
+  if (!m_pFileAccess->ReadBlockAtOffset(pBuf, m_Pos + m_HeaderOffset, size))
     return false;
   m_Pos += size;
   return true;
@@ -202,7 +256,7 @@
           buf << static_cast<char>(ch);
         break;
       case ReadStatus::Backslash:
-        if (ch >= '0' && ch <= '7') {
+        if (FXSYS_IsOctalDigit(ch)) {
           iEscCode = FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
           status = ReadStatus::Octal;
           break;
@@ -228,7 +282,7 @@
         status = ReadStatus::Normal;
         break;
       case ReadStatus::Octal:
-        if (ch >= '0' && ch <= '7') {
+        if (FXSYS_IsOctalDigit(ch)) {
           iEscCode =
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
           status = ReadStatus::FinishOctal;
@@ -240,7 +294,7 @@
         break;
       case ReadStatus::FinishOctal:
         status = ReadStatus::Normal;
-        if (ch >= '0' && ch <= '7') {
+        if (FXSYS_IsOctalDigit(ch)) {
           iEscCode =
               iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
           buf << static_cast<char>(iEscCode);
@@ -336,7 +390,7 @@
 }
 
 ByteString CPDF_SyntaxParser::GetNextWord(bool* bIsNumber) {
-  const CPDF_ReadValidator::Session read_session(GetValidator().Get());
+  const CPDF_ReadValidator::Session read_session(GetValidator());
   GetNextWordInternal(bIsNumber);
   ByteString ret;
   if (!GetValidator()->has_read_problems())
@@ -353,61 +407,67 @@
   return GetNextWord(nullptr);
 }
 
-std::unique_ptr<CPDF_Object> CPDF_SyntaxParser::GetObjectBody(
+void CPDF_SyntaxParser::SetPos(FX_FILESIZE pos) {
+  m_Pos = std::min(pos, m_FileLen);
+}
+
+RetainPtr<CPDF_Object> CPDF_SyntaxParser::GetObjectBody(
     CPDF_IndirectObjectHolder* pObjList) {
-  const CPDF_ReadValidator::Session read_session(GetValidator().Get());
+  const CPDF_ReadValidator::Session read_session(GetValidator());
   auto result = GetObjectBodyInternal(pObjList, ParseType::kLoose);
   if (GetValidator()->has_read_problems())
     return nullptr;
   return result;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_SyntaxParser::GetObjectBodyInternal(
+RetainPtr<CPDF_Object> CPDF_SyntaxParser::GetObjectBodyInternal(
     CPDF_IndirectObjectHolder* pObjList,
     ParseType parse_type) {
-  AutoRestorer<int> restorer(&s_CurrentRecursionDepth);
+  AutoRestorer<int> depth_restorer(&s_CurrentRecursionDepth);
   if (++s_CurrentRecursionDepth > kParserMaxRecursionDepth)
     return nullptr;
 
   FX_FILESIZE SavedObjPos = m_Pos;
   bool bIsNumber;
   ByteString word = GetNextWord(&bIsNumber);
-  if (word.GetLength() == 0)
+  if (word.IsEmpty())
     return nullptr;
 
   if (bIsNumber) {
-    FX_FILESIZE SavedPos = m_Pos;
+    AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
     ByteString nextword = GetNextWord(&bIsNumber);
-    if (bIsNumber) {
-      ByteString nextword2 = GetNextWord(nullptr);
-      if (nextword2 == "R") {
-        uint32_t refnum = FXSYS_atoui(word.c_str());
-        if (refnum == CPDF_Object::kInvalidObjNum)
-          return nullptr;
-        return pdfium::MakeUnique<CPDF_Reference>(pObjList, refnum);
-      }
-    }
-    m_Pos = SavedPos;
-    return pdfium::MakeUnique<CPDF_Number>(word.AsStringView());
+    if (!bIsNumber)
+      return pdfium::MakeRetain<CPDF_Number>(word.AsStringView());
+
+    ByteString nextword2 = GetNextWord(nullptr);
+    if (nextword2 != "R")
+      return pdfium::MakeRetain<CPDF_Number>(word.AsStringView());
+
+    pos_restorer.AbandonRestoration();
+    uint32_t refnum = FXSYS_atoui(word.c_str());
+    if (refnum == CPDF_Object::kInvalidObjNum)
+      return nullptr;
+
+    return pdfium::MakeRetain<CPDF_Reference>(pObjList, refnum);
   }
 
   if (word == "true" || word == "false")
-    return pdfium::MakeUnique<CPDF_Boolean>(word == "true");
+    return pdfium::MakeRetain<CPDF_Boolean>(word == "true");
 
   if (word == "null")
-    return pdfium::MakeUnique<CPDF_Null>();
+    return pdfium::MakeRetain<CPDF_Null>();
 
   if (word == "(") {
     ByteString str = ReadString();
-    return pdfium::MakeUnique<CPDF_String>(m_pPool, str, false);
+    return pdfium::MakeRetain<CPDF_String>(m_pPool, str, false);
   }
   if (word == "<") {
     ByteString str = ReadHexString();
-    return pdfium::MakeUnique<CPDF_String>(m_pPool, str, true);
+    return pdfium::MakeRetain<CPDF_String>(m_pPool, str, true);
   }
   if (word == "[") {
-    auto pArray = pdfium::MakeUnique<CPDF_Array>();
-    while (std::unique_ptr<CPDF_Object> pObj =
+    auto pArray = pdfium::MakeRetain<CPDF_Array>();
+    while (RetainPtr<CPDF_Object> pObj =
                GetObjectBodyInternal(pObjList, ParseType::kLoose)) {
       pArray->Add(std::move(pObj));
     }
@@ -416,35 +476,34 @@
                : nullptr;
   }
   if (word[0] == '/') {
-    return pdfium::MakeUnique<CPDF_Name>(
+    return pdfium::MakeRetain<CPDF_Name>(
         m_pPool,
         PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1)));
   }
   if (word == "<<") {
-    std::unique_ptr<CPDF_Dictionary> pDict =
-        pdfium::MakeUnique<CPDF_Dictionary>(m_pPool);
+    RetainPtr<CPDF_Dictionary> pDict =
+        pdfium::MakeRetain<CPDF_Dictionary>(m_pPool);
     while (1) {
-      ByteString key = GetNextWord(nullptr);
-      if (key.IsEmpty())
+      ByteString inner_word = GetNextWord(nullptr);
+      if (inner_word.IsEmpty())
         return nullptr;
 
-      FX_FILESIZE SavedPos = m_Pos - key.GetLength();
-      if (key == ">>")
+      FX_FILESIZE SavedPos = m_Pos - inner_word.GetLength();
+      if (inner_word == ">>")
         break;
 
-      if (key == "endobj") {
+      if (inner_word == "endobj") {
         m_Pos = SavedPos;
         break;
       }
-      if (key[0] != '/')
+      if (inner_word[0] != '/')
         continue;
 
-      key = PDF_NameDecode(key);
-
+      ByteString key = PDF_NameDecode(inner_word.AsStringView());
       if (key.IsEmpty() && parse_type == ParseType::kLoose)
         continue;
 
-      std::unique_ptr<CPDF_Object> pObj =
+      RetainPtr<CPDF_Object> pObj =
           GetObjectBodyInternal(pObjList, ParseType::kLoose);
       if (!pObj) {
         if (parse_type == ParseType::kLoose)
@@ -460,12 +519,10 @@
       }
     }
 
-    FX_FILESIZE SavedPos = m_Pos;
-    ByteString nextword = GetNextWord(nullptr);
-    if (nextword != "stream") {
-      m_Pos = SavedPos;
-      return std::move(pDict);
-    }
+    AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
+    if (GetNextWord(nullptr) != "stream")
+      return pDict;
+    pos_restorer.AbandonRestoration();
     return ReadStream(std::move(pDict));
   }
   if (word == ">>")
@@ -474,10 +531,10 @@
   return nullptr;
 }
 
-std::unique_ptr<CPDF_Object> CPDF_SyntaxParser::GetIndirectObject(
+RetainPtr<CPDF_Object> CPDF_SyntaxParser::GetIndirectObject(
     CPDF_IndirectObjectHolder* pObjList,
     ParseType parse_type) {
-  const CPDF_ReadValidator::Session read_session(GetValidator().Get());
+  const CPDF_ReadValidator::Session read_session(GetValidator());
   const FX_FILESIZE saved_pos = GetPos();
   bool is_number = false;
   ByteString word = GetNextWord(&is_number);
@@ -499,8 +556,7 @@
     return nullptr;
   }
 
-  std::unique_ptr<CPDF_Object> pObj =
-      GetObjectBodyInternal(pObjList, parse_type);
+  RetainPtr<CPDF_Object> pObj = GetObjectBodyInternal(pObjList, parse_type);
   if (pObj) {
     pObj->SetObjNum(parser_objnum);
     pObj->SetGenNum(parser_gennum);
@@ -525,153 +581,154 @@
   return 0;
 }
 
-std::unique_ptr<CPDF_Stream> CPDF_SyntaxParser::ReadStream(
-    std::unique_ptr<CPDF_Dictionary> pDict) {
+FX_FILESIZE CPDF_SyntaxParser::FindWordPos(ByteStringView word) {
+  AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
+  FX_FILESIZE end_offset = FindTag(word);
+  while (end_offset >= 0) {
+    // Stop searching when word is found.
+    if (IsWholeWord(GetPos() - word.GetLength(), m_FileLen, word, true))
+      return GetPos() - word.GetLength();
+
+    end_offset = FindTag(word);
+  }
+  return -1;
+}
+
+FX_FILESIZE CPDF_SyntaxParser::FindStreamEndPos() {
+  const ByteStringView kEndStreamStr("endstream");
+  const ByteStringView kEndObjStr("endobj");
+
+  FX_FILESIZE endStreamWordOffset = FindWordPos(kEndStreamStr);
+  FX_FILESIZE endObjWordOffset = FindWordPos(kEndObjStr);
+
+  // Can't find "endstream" or "endobj".
+  if (endStreamWordOffset < 0 && endObjWordOffset < 0) {
+    return -1;
+  }
+
+  if (endStreamWordOffset < 0 && endObjWordOffset >= 0) {
+    // Correct the position of end stream.
+    endStreamWordOffset = endObjWordOffset;
+  } else if (endStreamWordOffset >= 0 && endObjWordOffset < 0) {
+    // Correct the position of end obj.
+    endObjWordOffset = endStreamWordOffset;
+  } else if (endStreamWordOffset > endObjWordOffset) {
+    endStreamWordOffset = endObjWordOffset;
+  }
+
+  int numMarkers = ReadEOLMarkers(endStreamWordOffset - 2);
+  if (numMarkers == 2) {
+    endStreamWordOffset -= 2;
+  } else {
+    numMarkers = ReadEOLMarkers(endStreamWordOffset - 1);
+    if (numMarkers == 1) {
+      endStreamWordOffset -= 1;
+    }
+  }
+  if (endStreamWordOffset < GetPos()) {
+    return -1;
+  }
+  return endStreamWordOffset;
+}
+
+RetainPtr<CPDF_Stream> CPDF_SyntaxParser::ReadStream(
+    RetainPtr<CPDF_Dictionary> pDict) {
   const CPDF_Number* pLenObj = ToNumber(pDict->GetDirectObjectFor("Length"));
   FX_FILESIZE len = pLenObj ? pLenObj->GetInteger() : -1;
 
   // Locate the start of stream.
   ToNextLine();
-  FX_FILESIZE streamStartPos = m_Pos;
+  const FX_FILESIZE streamStartPos = GetPos();
+
+  if (len > 0) {
+    FX_SAFE_FILESIZE pos = GetPos();
+    pos += len;
+    if (!pos.IsValid() || pos.ValueOrDie() >= m_FileLen)
+      len = -1;
+  }
+
+  RetainPtr<IFX_SeekableReadStream> data;
+  if (len > 0) {
+    // Check data availability first to allow the Validator to request data
+    // smoothly, without jumps.
+    if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable(
+            m_HeaderOffset + GetPos(), len)) {
+      return nullptr;
+    }
+
+    data = pdfium::MakeRetain<ReadableSubStream>(
+        GetValidator(), m_HeaderOffset + GetPos(), len);
+    SetPos(GetPos() + len);
+  }
 
   const ByteStringView kEndStreamStr("endstream");
   const ByteStringView kEndObjStr("endobj");
 
-    bool bSearchForKeyword = true;
-    if (len >= 0) {
-      pdfium::base::CheckedNumeric<FX_FILESIZE> pos = m_Pos;
-      pos += len;
-      if (pos.IsValid() && pos.ValueOrDie() < m_FileLen)
-        m_Pos = pos.ValueOrDie();
+  // 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());
+    m_Pos += ReadEOLMarkers(GetPos());
+    memset(m_WordBuffer, 0, kEndStreamStr.GetLength() + 1);
+    GetNextWordInternal(nullptr);
+    if (GetValidator()->has_read_problems())
+      return nullptr;
 
-      m_Pos += ReadEOLMarkers(m_Pos);
-      memset(m_WordBuffer, 0, kEndStreamStr.GetLength() + 1);
-      GetNextWordInternal(nullptr);
-      // Earlier version of PDF specification doesn't require EOL marker before
-      // 'endstream' keyword. If keyword 'endstream' follows the bytes in
-      // specified length, it signals the end of stream.
-      if (memcmp(m_WordBuffer, kEndStreamStr.raw_str(),
-                 kEndStreamStr.GetLength()) == 0) {
-        bSearchForKeyword = false;
-      }
+    // Earlier version of PDF specification doesn't require EOL marker before
+    // 'endstream' keyword. If keyword 'endstream' follows the bytes in
+    // specified length, it signals the end of stream.
+    if (memcmp(m_WordBuffer, kEndStreamStr.raw_str(),
+               kEndStreamStr.GetLength()) != 0) {
+      data.Reset();
+      len = -1;
+      SetPos(streamStartPos);
     }
-
-    if (bSearchForKeyword) {
-      // If len is not available, len needs to be calculated
-      // by searching the keywords "endstream" or "endobj".
-      m_Pos = streamStartPos;
-      FX_FILESIZE endStreamOffset = 0;
-      while (endStreamOffset >= 0) {
-        endStreamOffset = FindTag(kEndStreamStr, 0);
-
-        // Can't find "endstream".
-        if (endStreamOffset < 0)
-          break;
-
-        // Stop searching when "endstream" is found.
-        if (IsWholeWord(m_Pos - kEndStreamStr.GetLength(), m_FileLen,
-                        kEndStreamStr, true)) {
-          endStreamOffset = m_Pos - streamStartPos - kEndStreamStr.GetLength();
-          break;
-        }
-      }
-
-      m_Pos = streamStartPos;
-      FX_FILESIZE endObjOffset = 0;
-      while (endObjOffset >= 0) {
-        endObjOffset = FindTag(kEndObjStr, 0);
-
-        // Can't find "endobj".
-        if (endObjOffset < 0)
-          break;
-
-        // Stop searching when "endobj" is found.
-        if (IsWholeWord(m_Pos - kEndObjStr.GetLength(), m_FileLen, kEndObjStr,
-                        true)) {
-          endObjOffset = m_Pos - streamStartPos - kEndObjStr.GetLength();
-          break;
-        }
-      }
-
-      // Can't find "endstream" or "endobj".
-      if (endStreamOffset < 0 && endObjOffset < 0)
-        return nullptr;
-
-      if (endStreamOffset < 0 && endObjOffset >= 0) {
-        // Correct the position of end stream.
-        endStreamOffset = endObjOffset;
-      } else if (endStreamOffset >= 0 && endObjOffset < 0) {
-        // Correct the position of end obj.
-        endObjOffset = endStreamOffset;
-      } else if (endStreamOffset > endObjOffset) {
-        endStreamOffset = endObjOffset;
-      }
-      len = endStreamOffset;
-
-      int numMarkers = ReadEOLMarkers(streamStartPos + endStreamOffset - 2);
-      if (numMarkers == 2) {
-        len -= 2;
-      } else {
-        numMarkers = ReadEOLMarkers(streamStartPos + endStreamOffset - 1);
-        if (numMarkers == 1) {
-          len -= 1;
-        }
-      }
-      if (len < 0)
-        return nullptr;
-
-      pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(len));
-    }
-    m_Pos = streamStartPos;
-
-  // Read up to the end of the buffer. Note, we allow zero length streams as
-  // we need to pass them through when we are importing pages into a new
-  // document.
-  len = std::min(len, m_FileLen - m_Pos - m_HeaderOffset);
-  if (len < 0)
-    return nullptr;
-
-  std::unique_ptr<uint8_t, FxFreeDeleter> pData;
-  if (len > 0) {
-    pData.reset(FX_Alloc(uint8_t, len));
-    ReadBlock(pData.get(), len);
   }
-  auto pStream =
-      pdfium::MakeUnique<CPDF_Stream>(std::move(pData), len, std::move(pDict));
-  streamStartPos = m_Pos;
+
+  if (len < 0) {
+    // If len is not available or incorrect, len needs to be calculated
+    // by searching the keywords "endstream" or "endobj".
+    const FX_FILESIZE streamEndPos = FindStreamEndPos();
+    if (streamEndPos < 0)
+      return nullptr;
+
+    len = streamEndPos - streamStartPos;
+    ASSERT(len >= 0);
+    if (len > 0) {
+      SetPos(streamStartPos);
+      // Check data availability first to allow the Validator to request data
+      // smoothly, without jumps.
+      if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable(
+              m_HeaderOffset + GetPos(), len)) {
+        return nullptr;
+      }
+
+      data = 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));
+  } else {
+    DCHECK(!len);
+    pStream->InitStream({}, std::move(pDict));  // Empty stream
+  }
+  const FX_FILESIZE end_stream_offset = GetPos();
   memset(m_WordBuffer, 0, kEndObjStr.GetLength() + 1);
   GetNextWordInternal(nullptr);
 
-  int numMarkers = ReadEOLMarkers(m_Pos);
+  int numMarkers = ReadEOLMarkers(GetPos());
   if (m_WordSize == static_cast<unsigned int>(kEndObjStr.GetLength()) &&
       numMarkers != 0 &&
       memcmp(m_WordBuffer, kEndObjStr.raw_str(), kEndObjStr.GetLength()) == 0) {
-    m_Pos = streamStartPos;
+    SetPos(end_stream_offset);
   }
   return pStream;
 }
 
-void CPDF_SyntaxParser::InitParser(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-    uint32_t HeaderOffset) {
-  ASSERT(pFileAccess);
-  return InitParserWithValidator(
-      pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr),
-      HeaderOffset);
-}
-
-void CPDF_SyntaxParser::InitParserWithValidator(
-    const RetainPtr<CPDF_ReadValidator>& validator,
-    uint32_t HeaderOffset) {
-  ASSERT(validator);
-  m_pFileBuf.clear();
-  m_HeaderOffset = HeaderOffset;
-  m_FileLen = validator->GetSize();
-  m_Pos = 0;
-  m_pFileAccess = validator;
-  m_BufOffset = 0;
-}
-
 uint32_t CPDF_SyntaxParser::GetDirectNum() {
   bool bIsNumber;
   GetNextWordInternal(&bIsNumber);
@@ -684,7 +741,7 @@
 
 bool CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos,
                                     FX_FILESIZE limit,
-                                    const ByteStringView& tag,
+                                    ByteStringView tag,
                                     bool checkKeyword) {
   const uint32_t taglen = tag.GetLength();
 
@@ -710,9 +767,9 @@
   return true;
 }
 
-bool CPDF_SyntaxParser::BackwardsSearchToWord(const ByteStringView& tag,
+bool CPDF_SyntaxParser::BackwardsSearchToWord(ByteStringView word,
                                               FX_FILESIZE limit) {
-  int32_t taglen = tag.GetLength();
+  int32_t taglen = word.GetLength();
   if (taglen == 0)
     return false;
 
@@ -726,31 +783,30 @@
     if (!GetCharAtBackward(pos, &byte))
       return false;
 
-    if (byte == tag[offset]) {
+    if (byte == word[offset]) {
       offset--;
       if (offset >= 0) {
         pos--;
         continue;
       }
-      if (IsWholeWord(pos, limit, tag, false)) {
+      if (IsWholeWord(pos, limit, word, false)) {
         m_Pos = pos;
         return true;
       }
     }
-    offset = byte == tag[taglen - 1] ? taglen - 2 : taglen - 1;
+    offset = byte == word[taglen - 1] ? taglen - 2 : taglen - 1;
     pos--;
     if (pos < 0)
       return false;
   }
 }
 
-FX_FILESIZE CPDF_SyntaxParser::FindTag(const ByteStringView& tag,
-                                       FX_FILESIZE limit) {
-  int32_t taglen = tag.GetLength();
-  int32_t match = 0;
-  limit += m_Pos;
-  FX_FILESIZE startpos = m_Pos;
+FX_FILESIZE CPDF_SyntaxParser::FindTag(ByteStringView tag) {
+  const FX_FILESIZE startpos = GetPos();
+  const int32_t taglen = tag.GetLength();
+  ASSERT(taglen > 0);
 
+  int32_t match = 0;
   while (1) {
     uint8_t ch;
     if (!GetNextChar(ch))
@@ -759,21 +815,14 @@
     if (ch == tag[match]) {
       match++;
       if (match == taglen)
-        return m_Pos - startpos - taglen;
+        return GetPos() - startpos - taglen;
     } else {
       match = ch == tag[0] ? 1 : 0;
     }
-
-    if (limit && m_Pos == limit)
-      return -1;
   }
   return -1;
 }
 
-RetainPtr<IFX_SeekableReadStream> CPDF_SyntaxParser::GetFileAccess() const {
-  return m_pFileAccess;
-}
-
 bool CPDF_SyntaxParser::IsPositionRead(FX_FILESIZE pos) const {
   return m_BufOffset <= pos &&
          pos < static_cast<FX_FILESIZE>(m_BufOffset + m_pFileBuf.size());
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.h b/core/fpdfapi/parser/cpdf_syntax_parser.h
index d344288..d36aedf 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.h
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.h
@@ -7,10 +7,10 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_
 
-#include <algorithm>
 #include <memory>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
 
@@ -26,81 +26,94 @@
  public:
   enum class ParseType { kStrict, kLoose };
 
-  CPDF_SyntaxParser();
-  explicit CPDF_SyntaxParser(const WeakPtr<ByteStringPool>& pPool);
+  static std::unique_ptr<CPDF_SyntaxParser> CreateForTesting(
+      const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
+      FX_FILESIZE HeaderOffset);
+
+  explicit CPDF_SyntaxParser(
+      const RetainPtr<IFX_SeekableReadStream>& pFileAccess);
+  CPDF_SyntaxParser(const RetainPtr<CPDF_ReadValidator>& pValidator,
+                    FX_FILESIZE HeaderOffset);
   ~CPDF_SyntaxParser();
 
-  void InitParser(const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-                  uint32_t HeaderOffset);
-
-  void InitParserWithValidator(const RetainPtr<CPDF_ReadValidator>& pValidator,
-                               uint32_t HeaderOffset);
+  void SetReadBufferSize(uint32_t read_buffer_size) {
+    m_ReadBufferSize = read_buffer_size;
+  }
 
   FX_FILESIZE GetPos() const { return m_Pos; }
-  void SetPos(FX_FILESIZE pos) { m_Pos = std::min(pos, m_FileLen); }
+  void SetPos(FX_FILESIZE pos);
 
-  std::unique_ptr<CPDF_Object> GetObjectBody(
-      CPDF_IndirectObjectHolder* pObjList);
+  RetainPtr<CPDF_Object> GetObjectBody(CPDF_IndirectObjectHolder* pObjList);
 
-  std::unique_ptr<CPDF_Object> GetIndirectObject(
-      CPDF_IndirectObjectHolder* pObjList,
-      ParseType parse_type);
+  RetainPtr<CPDF_Object> GetIndirectObject(CPDF_IndirectObjectHolder* pObjList,
+                                           ParseType parse_type);
 
   ByteString GetKeyword();
   void ToNextLine();
   void ToNextWord();
-  bool BackwardsSearchToWord(const ByteStringView& word, FX_FILESIZE limit);
-  FX_FILESIZE FindTag(const ByteStringView& tag, FX_FILESIZE limit);
+  bool BackwardsSearchToWord(ByteStringView word, FX_FILESIZE limit);
+  FX_FILESIZE FindTag(ByteStringView tag);
   bool ReadBlock(uint8_t* pBuf, uint32_t size);
   bool GetCharAt(FX_FILESIZE pos, uint8_t& ch);
   ByteString GetNextWord(bool* bIsNumber);
   ByteString PeekNextWord(bool* bIsNumber);
 
-  RetainPtr<IFX_SeekableReadStream> GetFileAccess() const;
-
   const RetainPtr<CPDF_ReadValidator>& GetValidator() const {
     return m_pFileAccess;
   }
+  uint32_t GetDirectNum();
+  bool GetNextChar(uint8_t& ch);
+
+  // The document size may be smaller than the file size.
+  // The syntax parser use position relative to document
+  // offset (|m_HeaderOffset|).
+  // The document size will be FileSize - "Header offset".
+  // All offsets was readed from document, should not be great than document
+  // size. Use it for checks instead of real file size.
+  FX_FILESIZE GetDocumentSize() const;
+
+  ByteString ReadString();
+  ByteString ReadHexString();
 
  private:
-  friend class CPDF_Parser;
   friend class CPDF_DataAvail;
   friend class cpdf_syntax_parser_ReadHexString_Test;
 
   static const int kParserMaxRecursionDepth = 64;
   static int s_CurrentRecursionDepth;
 
-  uint32_t GetDirectNum();
   bool ReadBlockAt(FX_FILESIZE read_pos);
-  bool GetNextChar(uint8_t& ch);
   bool GetCharAtBackward(FX_FILESIZE pos, uint8_t* ch);
   void GetNextWordInternal(bool* bIsNumber);
   bool IsWholeWord(FX_FILESIZE startpos,
                    FX_FILESIZE limit,
-                   const ByteStringView& tag,
+                   ByteStringView tag,
                    bool checkKeyword);
 
-  ByteString ReadString();
-  ByteString ReadHexString();
   unsigned int ReadEOLMarkers(FX_FILESIZE pos);
-  std::unique_ptr<CPDF_Stream> ReadStream(
-      std::unique_ptr<CPDF_Dictionary> pDict);
+  FX_FILESIZE FindWordPos(ByteStringView word);
+  FX_FILESIZE FindStreamEndPos();
+  RetainPtr<CPDF_Stream> ReadStream(RetainPtr<CPDF_Dictionary> pDict);
 
   bool IsPositionRead(FX_FILESIZE pos) const;
 
-  std::unique_ptr<CPDF_Object> GetObjectBodyInternal(
+  RetainPtr<CPDF_Object> GetObjectBodyInternal(
       CPDF_IndirectObjectHolder* pObjList,
       ParseType parse_type);
 
-  FX_FILESIZE m_Pos;
   RetainPtr<CPDF_ReadValidator> m_pFileAccess;
-  FX_FILESIZE m_HeaderOffset;
-  FX_FILESIZE m_FileLen;
-  std::vector<uint8_t> m_pFileBuf;
-  FX_FILESIZE m_BufOffset;
-  uint8_t m_WordBuffer[257];
-  uint32_t m_WordSize;
+  // The syntax parser use position relative to header offset.
+  // The header contains at file start, and can follow after some stuff. We
+  // ignore this stuff.
+  const FX_FILESIZE m_HeaderOffset;
+  const FX_FILESIZE m_FileLen;
+  FX_FILESIZE m_Pos = 0;
   WeakPtr<ByteStringPool> m_pPool;
+  std::vector<uint8_t> m_pFileBuf;
+  FX_FILESIZE m_BufOffset = 0;
+  uint32_t m_WordSize = 0;
+  uint8_t m_WordBuffer[257];
+  uint32_t m_ReadBufferSize = CPDF_Stream::kFileBufSize;
 };
 
 #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 cb065eb..53f0a17 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
@@ -8,81 +8,80 @@
 #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_memorystream.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
 
 TEST(cpdf_syntax_parser, ReadHexString) {
   {
     // Empty string.
-    uint8_t data[] = "";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 0, false), 0);
+    static const uint8_t data[] = "";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 0)));
     EXPECT_EQ("", parser.ReadHexString());
     EXPECT_EQ(0, parser.GetPos());
   }
 
   {
     // Blank string.
-    uint8_t data[] = "  ";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 2, false), 0);
+    static const uint8_t data[] = "  ";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 2)));
     EXPECT_EQ("", parser.ReadHexString());
     EXPECT_EQ(2, parser.GetPos());
   }
 
   {
     // Skips unknown characters.
-    uint8_t data[] = "z12b";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0);
+    static const uint8_t data[] = "z12b";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 4)));
     EXPECT_EQ("\x12\xb0", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
 
   {
     // Skips unknown characters.
-    uint8_t data[] = "*<&*#$^&@1";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 10, false), 0);
+    static const uint8_t data[] = "*<&*#$^&@1";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 10)));
     EXPECT_EQ("\x10", parser.ReadHexString());
     EXPECT_EQ(10, parser.GetPos());
   }
 
   {
     // Skips unknown characters.
-    uint8_t data[] = "\x80zab";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0);
+    static const uint8_t data[] = "\x80zab";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 4)));
     EXPECT_EQ("\xab", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
 
   {
     // Skips unknown characters.
-    uint8_t data[] = "\xffzab";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0);
+    static const uint8_t data[] = "\xffzab";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 4)));
     EXPECT_EQ("\xab", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
 
   {
     // Regular conversion.
-    uint8_t data[] = "1A2b>abcd";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 9, false), 0);
+    static const uint8_t data[] = "1A2b>abcd";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 9)));
     EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
     EXPECT_EQ(5, parser.GetPos());
   }
 
   {
     // Position out of bounds.
-    uint8_t data[] = "12ab>";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 5, false), 0);
+    static const uint8_t data[] = "12ab>";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 5)));
     parser.SetPos(5);
     EXPECT_EQ("", parser.ReadHexString());
 
@@ -102,64 +101,62 @@
 
   {
     // Missing ending >.
-    uint8_t data[] = "1A2b";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0);
+    static const uint8_t data[] = "1A2b";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 4)));
     EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
 
   {
     // Missing ending >.
-    uint8_t data[] = "12abz";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 5, false), 0);
+    static const uint8_t data[] = "12abz";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 5)));
     EXPECT_EQ("\x12\xab", parser.ReadHexString());
     EXPECT_EQ(5, parser.GetPos());
   }
 
   {
     // Uneven number of bytes.
-    uint8_t data[] = "1A2>asdf";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 8, false), 0);
+    static const uint8_t data[] = "1A2>asdf";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 8)));
     EXPECT_EQ("\x1a\x20", parser.ReadHexString());
     EXPECT_EQ(4, parser.GetPos());
   }
 
   {
     // Uneven number of bytes.
-    uint8_t data[] = "1A2zasdf";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 8, false), 0);
+    static const uint8_t data[] = "1A2zasdf";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 8)));
     EXPECT_EQ("\x1a\x2a\xdf", parser.ReadHexString());
     EXPECT_EQ(8, parser.GetPos());
   }
 
   {
     // Just ending character.
-    uint8_t data[] = ">";
-    CPDF_SyntaxParser parser;
-    parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 1, false), 0);
+    static const uint8_t data[] = ">";
+    CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, 1)));
     EXPECT_EQ("", parser.ReadHexString());
     EXPECT_EQ(1, parser.GetPos());
   }
 }
 
 TEST(cpdf_syntax_parser, GetInvalidReference) {
-  CPDF_SyntaxParser parser;
   // Data with a reference with number CPDF_Object::kInvalidObjNum
-  uint8_t data[] = "4294967295 0 R";
-  parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 14, false), 0);
-  std::unique_ptr<CPDF_Object> ref = parser.GetObjectBody(nullptr);
+  static const uint8_t data[] = "4294967295 0 R";
+  CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::make_span(data, 14)));
+  RetainPtr<CPDF_Object> ref = parser.GetObjectBody(nullptr);
   EXPECT_FALSE(ref);
 }
 
 TEST(cpdf_syntax_parser, PeekNextWord) {
-  CPDF_SyntaxParser parser;
-  uint8_t data[] = "    WORD ";
-  parser.InitParser(
-      pdfium::MakeRetain<CFX_MemoryStream>(data, sizeof(data), false), 0);
+  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));
 }
diff --git a/core/fpdfapi/parser/fpdf_parser_decode.cpp b/core/fpdfapi/parser/fpdf_parser_decode.cpp
index d7114b6..7074061 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode.cpp
@@ -13,23 +13,29 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
+#include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fxcodec/codec/ccodec_faxmodule.h"
-#include "core/fxcodec/codec/ccodec_flatemodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.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"
 
 namespace {
 
 const uint32_t kMaxStreamSize = 20 * 1024 * 1024;
 
-uint16_t GetUnicodeFromBytes(const uint8_t* bytes, bool bBE) {
-  return bBE ? (bytes[0] << 8 | bytes[1]) : (bytes[1] << 8 | bytes[0]);
+uint16_t GetUnicodeFromBigEndianBytes(const uint8_t* bytes) {
+  return bytes[0] << 8 | bytes[1];
+}
+
+uint16_t GetUnicodeFromLittleEndianBytes(const uint8_t* bytes) {
+  return bytes[1] << 8 | bytes[0];
 }
 
 bool CheckFlateDecodeParams(int Colors, int BitsPerComponent, int Columns) {
@@ -45,6 +51,10 @@
   return check.ValueOrDie() <= INT_MAX - 7;
 }
 
+uint8_t GetA85Result(uint32_t res, size_t i) {
+  return static_cast<uint8_t>(res >> (3 - i) * 8);
+}
+
 }  // namespace
 
 const uint16_t PDFDocEncoding[256] = {
@@ -78,20 +88,44 @@
     0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb,
     0x00fc, 0x00fd, 0x00fe, 0x00ff};
 
-uint32_t A85Decode(const uint8_t* src_buf,
-                   uint32_t src_size,
-                   uint8_t** dest_buf,
+bool ValidateDecoderPipeline(const CPDF_Array* pDecoders) {
+  size_t count = pDecoders->size();
+  if (count == 0)
+    return true;
+
+  for (size_t i = 0; i < count; ++i) {
+    if (!pDecoders->GetObjectAt(i)->IsName())
+      return false;
+  }
+
+  if (count == 1)
+    return true;
+
+  // TODO(thestig): Consolidate all the places that use these filter names.
+  static const char kValidDecoders[][16] = {
+      "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)))
+      return false;
+  }
+  return true;
+}
+
+uint32_t A85Decode(pdfium::span<const uint8_t> src_span,
+                   std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                    uint32_t* dest_size) {
   *dest_size = 0;
-  *dest_buf = nullptr;
-  if (src_size == 0)
+  if (src_span.empty()) {
+    dest_buf->reset();
     return 0;
+  }
 
   // Count legal characters and zeros.
   uint32_t zcount = 0;
   uint32_t pos = 0;
-  while (pos < src_size) {
-    uint8_t ch = src_buf[pos];
+  while (pos < src_span.size()) {
+    uint8_t ch = src_span[pos];
     if (ch == 'z') {
       zcount++;
     } else if ((ch < '!' || ch > 'u') && !PDFCharIsLineEnding(ch) &&
@@ -107,20 +141,24 @@
   // Count the space needed to contain non-zero characters. The encoding ratio
   // of Ascii85 is 4:5.
   uint32_t space_for_non_zeroes = (pos - zcount) / 5 * 4 + 4;
-  if (zcount > (UINT_MAX - space_for_non_zeroes) / 4)
+  FX_SAFE_UINT32 size = zcount;
+  size *= 4;
+  size += space_for_non_zeroes;
+  if (!size.IsValid())
     return FX_INVALID_OFFSET;
 
-  *dest_buf = FX_Alloc(uint8_t, zcount * 4 + space_for_non_zeroes);
+  dest_buf->reset(FX_Alloc(uint8_t, size.ValueOrDie()));
+  uint8_t* dest_buf_ptr = dest_buf->get();
   size_t state = 0;
   uint32_t res = 0;
   pos = 0;
-  while (pos < src_size) {
-    uint8_t ch = src_buf[pos++];
+  while (pos < src_span.size()) {
+    uint8_t ch = src_span[pos++];
     if (PDFCharIsLineEnding(ch) || ch == ' ' || ch == '\t')
       continue;
 
     if (ch == 'z') {
-      memset(*dest_buf + *dest_size, 0, 4);
+      memset(dest_buf_ptr + *dest_size, 0, 4);
       state = 0;
       res = 0;
       *dest_size += 4;
@@ -138,7 +176,7 @@
     }
 
     for (size_t i = 0; i < 4; ++i) {
-      (*dest_buf)[(*dest_size)++] = static_cast<uint8_t>(res >> (3 - i) * 8);
+      dest_buf_ptr[(*dest_size)++] = GetA85Result(res, i);
     }
     state = 0;
     res = 0;
@@ -148,32 +186,32 @@
     for (size_t i = state; i < 5; ++i)
       res = res * 85 + 84;
     for (size_t i = 0; i < state - 1; ++i)
-      (*dest_buf)[(*dest_size)++] = static_cast<uint8_t>(res >> (3 - i) * 8);
+      dest_buf_ptr[(*dest_size)++] = GetA85Result(res, i);
   }
-  if (pos < src_size && src_buf[pos] == '>')
+  if (pos < src_span.size() && src_span[pos] == '>')
     ++pos;
   return pos;
 }
 
-uint32_t HexDecode(const uint8_t* src_buf,
-                   uint32_t src_size,
-                   uint8_t** dest_buf,
+uint32_t HexDecode(pdfium::span<const uint8_t> src_span,
+                   std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                    uint32_t* dest_size) {
   *dest_size = 0;
-  if (src_size == 0) {
-    *dest_buf = nullptr;
+  if (src_span.empty()) {
+    dest_buf->reset();
     return 0;
   }
 
   uint32_t i = 0;
   // Find the end of data.
-  while (i < src_size && src_buf[i] != '>')
+  while (i < src_span.size() && src_span[i] != '>')
     ++i;
 
-  *dest_buf = FX_Alloc(uint8_t, i / 2 + 1);
+  dest_buf->reset(FX_Alloc(uint8_t, i / 2 + 1));
+  uint8_t* dest_buf_ptr = dest_buf->get();
   bool bFirst = true;
-  for (i = 0; i < src_size; ++i) {
-    uint8_t ch = src_buf[i];
+  for (i = 0; i < src_span.size(); ++i) {
+    uint8_t ch = src_span[i];
     if (PDFCharIsLineEnding(ch) || ch == ' ' || ch == '\t')
       continue;
 
@@ -186,9 +224,9 @@
 
     int digit = FXSYS_HexCharToInt(ch);
     if (bFirst)
-      (*dest_buf)[*dest_size] = digit * 16;
+      dest_buf_ptr[*dest_size] = digit * 16;
     else
-      (*dest_buf)[(*dest_size)++] += digit;
+      dest_buf_ptr[(*dest_size)++] += digit;
     bFirst = !bFirst;
   }
   if (!bFirst)
@@ -196,24 +234,23 @@
   return i;
 }
 
-uint32_t RunLengthDecode(const uint8_t* src_buf,
-                         uint32_t src_size,
-                         uint8_t** dest_buf,
+uint32_t RunLengthDecode(pdfium::span<const uint8_t> src_span,
+                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                          uint32_t* dest_size) {
-  uint32_t i = 0;
+  size_t i = 0;
   *dest_size = 0;
-  while (i < src_size) {
-    if (src_buf[i] == 128)
+  while (i < src_span.size()) {
+    if (src_span[i] == 128)
       break;
 
     uint32_t old = *dest_size;
-    if (src_buf[i] < 128) {
-      *dest_size += src_buf[i] + 1;
+    if (src_span[i] < 128) {
+      *dest_size += src_span[i] + 1;
       if (*dest_size < old)
         return FX_INVALID_OFFSET;
-      i += src_buf[i] + 2;
+      i += src_span[i] + 2;
     } else {
-      *dest_size += 257 - src_buf[i];
+      *dest_size += 257 - src_span[i];
       if (*dest_size < old)
         return FX_INVALID_OFFSET;
       i += 2;
@@ -222,39 +259,40 @@
   if (*dest_size >= kMaxStreamSize)
     return FX_INVALID_OFFSET;
 
-  *dest_buf = FX_Alloc(uint8_t, *dest_size);
+  dest_buf->reset(FX_Alloc(uint8_t, *dest_size));
+  pdfium::span<uint8_t> dest_span(dest_buf->get(), *dest_size);
   i = 0;
   int dest_count = 0;
-  while (i < src_size) {
-    if (src_buf[i] == 128)
+  while (i < src_span.size()) {
+    if (src_span[i] == 128)
       break;
 
-    if (src_buf[i] < 128) {
-      uint32_t copy_len = src_buf[i] + 1;
-      uint32_t buf_left = src_size - i - 1;
+    if (src_span[i] < 128) {
+      uint32_t copy_len = src_span[i] + 1;
+      uint32_t buf_left = src_span.size() - i - 1;
       if (buf_left < copy_len) {
         uint32_t delta = copy_len - buf_left;
         copy_len = buf_left;
-        memset(*dest_buf + dest_count + copy_len, '\0', delta);
+        memset(&dest_span[dest_count + copy_len], '\0', delta);
       }
-      memcpy(*dest_buf + dest_count, src_buf + i + 1, copy_len);
-      dest_count += src_buf[i] + 1;
-      i += src_buf[i] + 2;
+      auto copy_span = src_span.subspan(i + 1, copy_len);
+      memcpy(&dest_span[dest_count], copy_span.data(), copy_span.size());
+      dest_count += src_span[i] + 1;
+      i += src_span[i] + 2;
     } else {
       int fill = 0;
-      if (i < src_size - 1)
-        fill = src_buf[i + 1];
-      memset(*dest_buf + dest_count, fill, 257 - src_buf[i]);
-      dest_count += 257 - src_buf[i];
+      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];
       i += 2;
     }
   }
-  return std::min(i + 1, src_size);
+  return std::min(i + 1, src_span.size());
 }
 
-std::unique_ptr<CCodec_ScanlineDecoder> FPDFAPI_CreateFaxDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
+std::unique_ptr<ScanlineDecoder> CreateFaxDecoder(
+    pdfium::span<const uint8_t> src_span,
     int width,
     int height,
     const CPDF_Dictionary* pParams) {
@@ -274,14 +312,12 @@
     if (Rows > USHRT_MAX)
       Rows = 0;
   }
-  return CPDF_ModuleMgr::Get()->GetFaxModule()->CreateDecoder(
-      src_buf, src_size, width, height, K, EndOfLine, ByteAlign, BlackIs1,
-      Columns, Rows);
+  return FaxModule::CreateDecoder(src_span, width, height, K, EndOfLine,
+                                  ByteAlign, BlackIs1, Columns, Rows);
 }
 
-std::unique_ptr<CCodec_ScanlineDecoder> FPDFAPI_CreateFlateDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
+std::unique_ptr<ScanlineDecoder> CreateFlateDecoder(
+    pdfium::span<const uint8_t> src_span,
     int width,
     int height,
     int nComps,
@@ -299,18 +335,17 @@
     if (!CheckFlateDecodeParams(Colors, BitsPerComponent, Columns))
       return nullptr;
   }
-  return CPDF_ModuleMgr::Get()->GetFlateModule()->CreateDecoder(
-      src_buf, src_size, width, height, nComps, bpc, predictor, Colors,
-      BitsPerComponent, Columns);
+  return FlateModule::CreateDecoder(src_span, width, height, nComps, bpc,
+                                    predictor, Colors, BitsPerComponent,
+                                    Columns);
 }
 
-uint32_t FPDFAPI_FlateOrLZWDecode(bool bLZW,
-                                  const uint8_t* src_buf,
-                                  uint32_t src_size,
-                                  CPDF_Dictionary* pParams,
-                                  uint32_t estimated_size,
-                                  uint8_t** dest_buf,
-                                  uint32_t* dest_size) {
+uint32_t FlateOrLZWDecode(bool bLZW,
+                          pdfium::span<const uint8_t> src_span,
+                          const CPDF_Dictionary* pParams,
+                          uint32_t estimated_size,
+                          std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                          uint32_t* dest_size) {
   int predictor = 0;
   int Colors = 0;
   int BitsPerComponent = 0;
@@ -325,47 +360,58 @@
     if (!CheckFlateDecodeParams(Colors, BitsPerComponent, Columns))
       return FX_INVALID_OFFSET;
   }
-  return CPDF_ModuleMgr::Get()->GetFlateModule()->FlateOrLZWDecode(
-      bLZW, src_buf, src_size, bEarlyChange, predictor, Colors,
-      BitsPerComponent, Columns, estimated_size, dest_buf, dest_size);
+  return FlateModule::FlateOrLZWDecode(bLZW, src_span, bEarlyChange, predictor,
+                                       Colors, BitsPerComponent, Columns,
+                                       estimated_size, dest_buf, dest_size);
 }
 
-bool PDF_DataDecode(const uint8_t* src_buf,
-                    uint32_t src_size,
-                    const CPDF_Dictionary* pDict,
-                    uint32_t last_estimated_size,
-                    bool bImageAcc,
-                    uint8_t** dest_buf,
-                    uint32_t* dest_size,
-                    ByteString* ImageEncoding,
-                    CPDF_Dictionary** pImageParms) {
-  CPDF_Object* pDecoder = pDict ? pDict->GetDirectObjectFor("Filter") : nullptr;
+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 false;
+    return {};
 
-  CPDF_Object* pParams =
-      pDict ? pDict->GetDirectObjectFor("DecodeParms") : nullptr;
+  const CPDF_Object* pParams =
+      pDict->GetDirectObjectFor(pdfium::stream::kDecodeParms);
 
-  std::vector<std::pair<ByteString, CPDF_Object*>> DecoderArray;
-  if (CPDF_Array* pDecoders = pDecoder->AsArray()) {
-    CPDF_Array* pParamsArray = ToArray(pParams);
-    for (size_t i = 0; i < pDecoders->GetCount(); ++i) {
-      DecoderArray.push_back(
+  std::vector<std::pair<ByteString, const CPDF_Object*>> decoder_array;
+  if (const CPDF_Array* pDecoders = pDecoder->AsArray()) {
+    if (!ValidateDecoderPipeline(pDecoders))
+      return {};
+
+    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});
     }
   } else {
-    DecoderArray.push_back(
+    decoder_array.push_back(
         {pDecoder->GetString(), pParams ? pParams->GetDict() : nullptr});
   }
-  uint8_t* last_buf = const_cast<uint8_t*>(src_buf);
-  uint32_t last_size = src_size;
-  size_t nSize = DecoderArray.size();
+
+  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) {
+  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.
+  pdfium::span<const uint8_t> last_span = src_span;
+  size_t nSize = decoder_array.size();
   for (size_t i = 0; i < nSize; ++i) {
     int estimated_size = i == nSize - 1 ? last_estimated_size : 0;
-    ByteString decoder = DecoderArray[i].first;
-    CPDF_Dictionary* pParam = ToDictionary(DecoderArray[i].second);
-    uint8_t* new_buf = nullptr;
+    ByteString decoder = decoder_array[i].first;
+    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;
     if (decoder == "Crypt")
@@ -373,117 +419,117 @@
     if (decoder == "FlateDecode" || decoder == "Fl") {
       if (bImageAcc && i == nSize - 1) {
         *ImageEncoding = "FlateDecode";
-        *dest_buf = last_buf;
-        *dest_size = last_size;
-        *pImageParms = pParam;
+        *dest_buf = std::move(result);
+        *dest_size = last_span.size();
+        pImageParams->Reset(pParam);
         return true;
       }
-      offset = FPDFAPI_FlateOrLZWDecode(false, last_buf, last_size, pParam,
-                                        estimated_size, &new_buf, &new_size);
+      offset = FlateOrLZWDecode(false, last_span, pParam, estimated_size,
+                                &new_buf, &new_size);
     } else if (decoder == "LZWDecode" || decoder == "LZW") {
-      offset = FPDFAPI_FlateOrLZWDecode(true, last_buf, last_size, pParam,
-                                        estimated_size, &new_buf, &new_size);
+      offset = FlateOrLZWDecode(true, last_span, pParam, estimated_size,
+                                &new_buf, &new_size);
     } else if (decoder == "ASCII85Decode" || decoder == "A85") {
-      offset = A85Decode(last_buf, last_size, &new_buf, &new_size);
+      offset = A85Decode(last_span, &new_buf, &new_size);
     } else if (decoder == "ASCIIHexDecode" || decoder == "AHx") {
-      offset = HexDecode(last_buf, last_size, &new_buf, &new_size);
+      offset = HexDecode(last_span, &new_buf, &new_size);
     } else if (decoder == "RunLengthDecode" || decoder == "RL") {
       if (bImageAcc && i == nSize - 1) {
         *ImageEncoding = "RunLengthDecode";
-        *dest_buf = last_buf;
-        *dest_size = last_size;
-        *pImageParms = pParam;
+        *dest_buf = std::move(result);
+        *dest_size = last_span.size();
+        pImageParams->Reset(pParam);
         return true;
       }
-      offset = RunLengthDecode(last_buf, last_size, &new_buf, &new_size);
+      offset = RunLengthDecode(last_span, &new_buf, &new_size);
     } else {
       // If we get here, assume it's an image decoder.
       if (decoder == "DCT")
         decoder = "DCTDecode";
       else if (decoder == "CCF")
         decoder = "CCITTFaxDecode";
-      *ImageEncoding = decoder;
-      *pImageParms = pParam;
-      *dest_buf = last_buf;
-      *dest_size = last_size;
+      *ImageEncoding = std::move(decoder);
+      pImageParams->Reset(pParam);
+      *dest_buf = std::move(result);
+      *dest_size = last_span.size();
       return true;
     }
-    if (last_buf != src_buf)
-      FX_Free(last_buf);
-    if (offset == FX_INVALID_OFFSET) {
-      FX_Free(new_buf);
+    if (offset == FX_INVALID_OFFSET)
       return false;
-    }
-    last_buf = new_buf;
-    last_size = new_size;
+
+    last_span = {new_buf.get(), new_size};
+    result = std::move(new_buf);
   }
   ImageEncoding->clear();
-  *pImageParms = nullptr;
-  *dest_buf = last_buf;
-  *dest_size = last_size;
+  *pImageParams = nullptr;
+  *dest_buf = std::move(result);
+  *dest_size = last_span.size();
   return true;
 }
 
-WideString PDF_DecodeText(const uint8_t* src_data, uint32_t src_len) {
+WideString PDF_DecodeText(pdfium::span<const uint8_t> span) {
+  int dest_pos = 0;
   WideString result;
-  if (src_len >= 2 && ((src_data[0] == 0xfe && src_data[1] == 0xff) ||
-                       (src_data[0] == 0xff && src_data[1] == 0xfe))) {
-    uint32_t max_chars = (src_len - 2) / 2;
+  if (span.size() >= 2 && ((span[0] == 0xfe && span[1] == 0xff) ||
+                           (span[0] == 0xff && span[1] == 0xfe))) {
+    size_t max_chars = (span.size() - 2) / 2;
     if (!max_chars)
       return result;
 
-    bool bBE = src_data[0] == 0xfe || (src_data[0] == 0xff && !src_data[2]);
-    wchar_t* dest_buf = result.GetBuffer(max_chars);
-    const uint8_t* uni_str = src_data + 2;
-    int dest_pos = 0;
-    for (uint32_t i = 0; i < max_chars * 2; i += 2) {
-      uint16_t unicode = GetUnicodeFromBytes(uni_str + i, bBE);
-      if (unicode != 0x1b) {
-        dest_buf[dest_pos++] = unicode;
-        continue;
-      }
+    pdfium::span<wchar_t> dest_buf = result.GetBuffer(max_chars);
+    uint16_t (*GetUnicodeFromBytes)(const uint8_t*) =
+        span[0] == 0xfe ? GetUnicodeFromBigEndianBytes
+                        : GetUnicodeFromLittleEndianBytes;
+    const uint8_t* unicode_str = &span[2];
+    for (size_t i = 0; i < max_chars * 2; i += 2) {
+      uint16_t unicode = GetUnicodeFromBytes(unicode_str + i);
 
-      i += 2;
-      while (i < max_chars * 2) {
-        uint16_t unicode2 = GetUnicodeFromBytes(uni_str + i, bBE);
+      // 0x001B is a begin/end marker for language metadata region that
+      // should not be in the decoded text.
+      if (unicode == 0x001B) {
         i += 2;
-        if (unicode2 == 0x1b)
+        for (; i < max_chars * 2; i += 2) {
+          unicode = GetUnicodeFromBytes(unicode_str + i);
+          if (unicode == 0x001B) {
+            i += 2;
+            if (i < max_chars * 2)
+              unicode = GetUnicodeFromBytes(unicode_str + i);
+            break;
+          }
+        }
+        if (i >= max_chars * 2)
           break;
       }
+
+      dest_buf[dest_pos++] = unicode;
     }
-    result.ReleaseBuffer(dest_pos);
   } else {
-    wchar_t* dest_buf = result.GetBuffer(src_len);
-    for (uint32_t i = 0; i < src_len; ++i)
-      dest_buf[i] = PDFDocEncoding[src_data[i]];
-    result.ReleaseBuffer(src_len);
+    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_pos = span.size();
   }
+  result.ReleaseBuffer(dest_pos);
   return result;
 }
 
-WideString PDF_DecodeText(const ByteString& bstr) {
-  return PDF_DecodeText(reinterpret_cast<const uint8_t*>(bstr.c_str()),
-                        bstr.GetLength());
-}
-
-ByteString PDF_EncodeText(const wchar_t* pString, int len) {
-  if (len == -1)
-    len = wcslen(pString);
-
+ByteString PDF_EncodeText(const WideString& str) {
+  size_t i = 0;
+  size_t len = str.GetLength();
   ByteString result;
-  char* dest_buf1 = result.GetBuffer(len);
-  int i;
-  for (i = 0; i < len; ++i) {
-    int code;
-    for (code = 0; code < 256; ++code) {
-      if (PDFDocEncoding[code] == pString[i])
+  {
+    pdfium::span<char> dest_buf = result.GetBuffer(len);
+    for (i = 0; i < len; ++i) {
+      int code;
+      for (code = 0; code < 256; ++code) {
+        if (PDFDocEncoding[code] == str[i])
+          break;
+      }
+      if (code == 256)
         break;
+
+      dest_buf[i] = code;
     }
-
-    if (code == 256)
-      break;
-
-    dest_buf1[i] = code;
   }
   result.ReleaseBuffer(i);
   if (i == len)
@@ -494,24 +540,22 @@
     return result;
   }
 
-  int encLen = len * 2 + 2;
-
-  uint8_t* dest_buf2 = reinterpret_cast<uint8_t*>(result.GetBuffer(encLen));
-  dest_buf2[0] = 0xfe;
-  dest_buf2[1] = 0xff;
-  dest_buf2 += 2;
-  for (int j = 0; j < len; ++j) {
-    *dest_buf2++ = pString[j] >> 8;
-    *dest_buf2++ = static_cast<uint8_t>(pString[j]);
+  size_t dest_index = 0;
+  size_t encLen = len * 2 + 2;
+  {
+    pdfium::span<uint8_t> dest_buf =
+        pdfium::as_writable_bytes(result.GetBuffer(encLen));
+    dest_buf[dest_index++] = 0xfe;
+    dest_buf[dest_index++] = 0xff;
+    for (size_t j = 0; j < len; ++j) {
+      dest_buf[dest_index++] = str[j] >> 8;
+      dest_buf[dest_index++] = static_cast<uint8_t>(str[j]);
+    }
   }
   result.ReleaseBuffer(encLen);
   return result;
 }
 
-ByteString PDF_EncodeText(const WideString& str) {
-  return PDF_EncodeText(str.c_str(), str.GetLength());
-}
-
 ByteString PDF_EncodeString(const ByteString& src, bool bHex) {
   std::ostringstream result;
   int srclen = src.GetLength();
@@ -545,29 +589,16 @@
   return ByteString(result);
 }
 
-bool FlateEncode(const uint8_t* src_buf,
-                 uint32_t src_size,
-                 uint8_t** dest_buf,
+bool FlateEncode(pdfium::span<const uint8_t> src_span,
+                 std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                  uint32_t* dest_size) {
-  CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
-  return pEncoders->GetFlateModule()->Encode(src_buf, src_size, dest_buf,
-                                             dest_size);
+  return FlateModule::Encode(src_span.data(), src_span.size(), dest_buf,
+                             dest_size);
 }
 
-bool PngEncode(const uint8_t* src_buf,
-               uint32_t src_size,
-               uint8_t** dest_buf,
-               uint32_t* dest_size) {
-  CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
-  return pEncoders->GetFlateModule()->PngEncode(src_buf, src_size, dest_buf,
-                                                dest_size);
-}
-
-uint32_t FlateDecode(const uint8_t* src_buf,
-                     uint32_t src_size,
-                     uint8_t** dest_buf,
+uint32_t FlateDecode(pdfium::span<const uint8_t> src_span,
+                     std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                      uint32_t* dest_size) {
-  CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
-  return pEncoders->GetFlateModule()->FlateOrLZWDecode(
-      false, src_buf, src_size, false, 0, 0, 0, 0, 0, dest_buf, dest_size);
+  return FlateModule::FlateOrLZWDecode(false, src_span, false, 0, 0, 0, 0, 0,
+                                       dest_buf, dest_size);
 }
diff --git a/core/fpdfapi/parser/fpdf_parser_decode.h b/core/fpdfapi/parser/fpdf_parser_decode.h
index 6650b68..ebb64e6 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.h
+++ b/core/fpdfapi/parser/fpdf_parser_decode.h
@@ -8,89 +8,84 @@
 #define CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_
 
 #include <memory>
+#include <utility>
+#include <vector>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
+#include "third_party/base/span.h"
 
-class CCodec_ScanlineDecoder;
+class CPDF_Array;
 class CPDF_Dictionary;
+class CPDF_Object;
+
+namespace fxcodec {
+class ScanlineDecoder;
+}
 
 // Indexed by 8-bit char code, contains unicode code points.
 extern const uint16_t PDFDocEncoding[256];
 
-ByteString PDF_NameDecode(const ByteStringView& orig);
-ByteString PDF_NameDecode(const ByteString& orig);
-ByteString PDF_NameEncode(const ByteString& orig);
+bool ValidateDecoderPipeline(const CPDF_Array* pDecoders);
+
 ByteString PDF_EncodeString(const ByteString& src, bool bHex);
-WideString PDF_DecodeText(const uint8_t* pData, uint32_t size);
-WideString PDF_DecodeText(const ByteString& bstr);
-ByteString PDF_EncodeText(const wchar_t* pString, int len);
+WideString PDF_DecodeText(pdfium::span<const uint8_t> span);
 ByteString PDF_EncodeText(const WideString& str);
 
-bool FlateEncode(const uint8_t* src_buf,
-                 uint32_t src_size,
-                 uint8_t** dest_buf,
-                 uint32_t* dest_size);
-
-// This used to have more parameters like the predictor and bpc, but there was
-// only one caller, so the interface has been simplified, the values are hard
-// coded, and dead code has been removed.
-bool PngEncode(const uint8_t* src_buf,
-               uint32_t src_size,
-               uint8_t** dest_buf,
-               uint32_t* dest_size);
-
-uint32_t FlateDecode(const uint8_t* src_buf,
-                     uint32_t src_size,
-                     uint8_t** dest_buf,
-                     uint32_t* dest_size);
-
-uint32_t RunLengthDecode(const uint8_t* src_buf,
-                         uint32_t src_size,
-                         uint8_t** dest_buf,
-                         uint32_t* dest_size);
-
-std::unique_ptr<CCodec_ScanlineDecoder> FPDFAPI_CreateFaxDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
+std::unique_ptr<fxcodec::ScanlineDecoder> CreateFaxDecoder(
+    pdfium::span<const uint8_t> src_span,
     int width,
     int height,
     const CPDF_Dictionary* pParams);
 
-std::unique_ptr<CCodec_ScanlineDecoder> FPDFAPI_CreateFlateDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
+std::unique_ptr<fxcodec::ScanlineDecoder> CreateFlateDecoder(
+    pdfium::span<const uint8_t> src_span,
     int width,
     int height,
     int nComps,
     int bpc,
     const CPDF_Dictionary* pParams);
 
-uint32_t A85Decode(const uint8_t* src_buf,
-                   uint32_t src_size,
-                   uint8_t** dest_buf,
+bool FlateEncode(pdfium::span<const uint8_t> src_span,
+                 std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                 uint32_t* dest_size);
+
+uint32_t FlateDecode(pdfium::span<const uint8_t> src_span,
+                     std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                     uint32_t* dest_size);
+
+uint32_t RunLengthDecode(pdfium::span<const uint8_t> src_span,
+                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                         uint32_t* dest_size);
+
+uint32_t A85Decode(pdfium::span<const uint8_t> src_span,
+                   std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                    uint32_t* dest_size);
 
-uint32_t HexDecode(const uint8_t* src_buf,
-                   uint32_t src_size,
-                   uint8_t** dest_buf,
+uint32_t HexDecode(pdfium::span<const uint8_t> src_span,
+                   std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
                    uint32_t* dest_size);
 
-uint32_t FPDFAPI_FlateOrLZWDecode(bool bLZW,
-                                  const uint8_t* src_buf,
-                                  uint32_t src_size,
-                                  CPDF_Dictionary* pParams,
-                                  uint32_t estimated_size,
-                                  uint8_t** dest_buf,
-                                  uint32_t* dest_size);
+uint32_t FlateOrLZWDecode(bool bLZW,
+                          pdfium::span<const uint8_t> src_span,
+                          const CPDF_Dictionary* pParams,
+                          uint32_t estimated_size,
+                          std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                          uint32_t* dest_size);
 
-bool PDF_DataDecode(const uint8_t* src_buf,
-                    uint32_t src_size,
-                    const CPDF_Dictionary* pDict,
-                    uint32_t estimated_size,
-                    bool bImageAcc,
-                    uint8_t** dest_buf,
-                    uint32_t* dest_size,
-                    ByteString* ImageEncoding,
-                    CPDF_Dictionary** pImageParms);
+Optional<std::vector<std::pair<ByteString, const CPDF_Object*>>>
+GetDecoderArray(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);
 
 #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 e95a4cc..32a34db 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp
@@ -2,21 +2,24 @@
 // 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 <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/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
 
-using FPDFParserDecodeEmbeddertest = EmbedderTest;
+using FPDFParserDecodeEmbedderTest = EmbedderTest;
 
 // NOTE: python's zlib.compress() and zlib.decompress() may be useful for
 // external validation of the FlateEncode/FlateDecode test cases.
 
-TEST_F(FPDFParserDecodeEmbeddertest, FlateEncode) {
+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"),
@@ -36,20 +39,19 @@
 
   for (size_t i = 0; i < FX_ArraySize(flate_encode_cases); ++i) {
     const pdfium::StrFuncTestData& data = flate_encode_cases[i];
-    unsigned char* buf = nullptr;
+    std::unique_ptr<uint8_t, FxFreeDeleter> buf;
     uint32_t buf_size;
-    EXPECT_TRUE(FlateEncode(data.input, data.input_size, &buf, &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, data.expected_size))
+    EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size))
         << " for case " << i;
-    FX_Free(buf);
   }
 }
 
-TEST_F(FPDFParserDecodeEmbeddertest, FlateDecode) {
+TEST_F(FPDFParserDecodeEmbedderTest, FlateDecode) {
   static const pdfium::DecodeTestData flate_decode_cases[] = {
       STR_IN_OUT_CASE("", "", 0),
       STR_IN_OUT_CASE("preposterous nonsense", "", 2),
@@ -73,57 +75,62 @@
 
   for (size_t i = 0; i < FX_ArraySize(flate_decode_cases); ++i) {
     const pdfium::DecodeTestData& data = flate_decode_cases[i];
-    unsigned char* buf = nullptr;
+    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))
+              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, data.expected_size))
+    EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size))
         << " for case " << i;
-    FX_Free(buf);
   }
 }
 
-TEST_F(FPDFParserDecodeEmbeddertest, Bug_552046) {
+TEST_F(FPDFParserDecodeEmbedderTest, Bug_552046) {
   // Tests specifying multiple image filters for a stream. Should not cause a
   // crash when rendered.
   EXPECT_TRUE(OpenDocument("bug_552046.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
-  FPDFBitmap_Destroy(bitmap);
+  ASSERT_TRUE(page);
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
   UnloadPage(page);
 }
 
-TEST_F(FPDFParserDecodeEmbeddertest, Bug_555784) {
+TEST_F(FPDFParserDecodeEmbedderTest, Bug_555784) {
   // 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"));
   FPDF_PAGE page = LoadPage(0);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
-  FPDFBitmap_Destroy(bitmap);
+  ASSERT_TRUE(page);
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
   UnloadPage(page);
 }
 
-TEST_F(FPDFParserDecodeEmbeddertest, Bug_455199) {
+// 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) {
   // Tests object numbers with a value > 01000000.
   // Should open successfully.
   EXPECT_TRUE(OpenDocument("bug_455199.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  FPDF_BITMAP bitmap = RenderPage(page);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+  ASSERT_TRUE(page);
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+#if defined(OS_MACOSX)
   const char kExpectedMd5sum[] = "b90475ca64d1348c3bf5e2b77ad9187a";
-#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  const char kExpectedMd5sum[] = "e5a6fa28298db07484cd922f3e210c88";
+#elif defined(OS_WIN)
+  const char kExpectedMd5sum[] = "795b7ce1626931aa06af0fa23b7d80bb";
 #else
   const char kExpectedMd5sum[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
 #endif
-  CompareBitmap(bitmap, 200, 200, kExpectedMd5sum);
-  FPDFBitmap_Destroy(bitmap);
+  CompareBitmap(bitmap.get(), 200, 200, kExpectedMd5sum);
   UnloadPage(page);
 }
diff --git a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
index 9ab4958..311226f 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
@@ -4,11 +4,116 @@
 
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_name.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) {
+  {
+    // Empty decoder list is always valid.
+    auto decoders = pdfium::MakeRetain<CPDF_Array>();
+    EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get()));
+  }
+  {
+    // 1 decoder is almost always valid.
+    auto decoders = pdfium::MakeRetain<CPDF_Array>();
+    decoders->AddNew<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");
+    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");
+    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");
+    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");
+    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");
+    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);
+    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");
+    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");
+    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");
+    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");
+    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);
+    EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get()));
+  }
+}
+
 TEST(fpdf_parser_decode, A85Decode) {
-  pdfium::DecodeTestData test_data[] = {
+  const pdfium::DecodeTestData kTestData[] = {
       // Empty src string.
       STR_IN_OUT_CASE("", "", 0),
       // Empty content in src string.
@@ -26,24 +131,24 @@
       // Stop at unknown characters.
       STR_IN_OUT_CASE("FCfN8FCfN8vw", "testtest", 11),
   };
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
-    pdfium::DecodeTestData* ptr = &test_data[i];
-    uint8_t* result = nullptr;
+  for (const auto& test_case : kTestData) {
+    std::unique_ptr<uint8_t, FxFreeDeleter> result;
     uint32_t result_size = 0;
-    EXPECT_EQ(ptr->processed_size,
-              A85Decode(ptr->input, ptr->input_size, &result, &result_size))
-        << "for case " << i;
-    ASSERT_EQ(ptr->expected_size, result_size);
+    EXPECT_EQ(test_case.processed_size,
+              A85Decode({test_case.input, test_case.input_size}, &result,
+                        &result_size))
+        << "for case " << test_case.input;
+    ASSERT_EQ(test_case.expected_size, result_size);
+    const uint8_t* result_ptr = result.get();
     for (size_t j = 0; j < result_size; ++j) {
-      EXPECT_EQ(ptr->expected[j], result[j]) << "for case " << i << " char "
-                                             << j;
+      EXPECT_EQ(test_case.expected[j], result_ptr[j])
+          << "for case " << test_case.input << " char " << j;
     }
-    FX_Free(result);
   }
 }
 
 TEST(fpdf_parser_decode, HexDecode) {
-  pdfium::DecodeTestData test_data[] = {
+  const pdfium::DecodeTestData kTestData[] = {
       // Empty src string.
       STR_IN_OUT_CASE("", "", 0),
       // Empty content in src string.
@@ -61,28 +166,77 @@
       // No ending mark.
       STR_IN_OUT_CASE("12AcED3c3456", "\x12\xac\xed\x3c\x34\x56", 12),
   };
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
-    pdfium::DecodeTestData* ptr = &test_data[i];
-    uint8_t* result = nullptr;
+  for (const auto& test_case : kTestData) {
+    std::unique_ptr<uint8_t, FxFreeDeleter> result;
     uint32_t result_size = 0;
-    EXPECT_EQ(ptr->processed_size,
-              HexDecode(ptr->input, ptr->input_size, &result, &result_size))
-        << "for case " << i;
-    ASSERT_EQ(ptr->expected_size, result_size);
+    EXPECT_EQ(test_case.processed_size,
+              HexDecode({test_case.input, test_case.input_size}, &result,
+                        &result_size))
+        << "for case " << test_case.input;
+    ASSERT_EQ(test_case.expected_size, result_size);
+    const uint8_t* result_ptr = result.get();
     for (size_t j = 0; j < result_size; ++j) {
-      EXPECT_EQ(ptr->expected[j], result[j]) << "for case " << i << " char "
-                                             << j;
+      EXPECT_EQ(test_case.expected[j], result_ptr[j])
+          << "for case " << test_case.input << " char " << j;
     }
-    FX_Free(result);
+  }
+}
+
+TEST(fpdf_parser_decode, DecodeText) {
+  const struct DecodeTestData {
+    const char* input;
+    size_t input_length;
+    const wchar_t* expected_output;
+    size_t expected_length;
+  } kTestData[] = {
+      // Empty src string.
+      {"", 0, L"", 0},
+      // ASCII text.
+      {"the quick\tfox", 13, L"the quick\tfox", 13},
+      // Unicode text.
+      {"\xFE\xFF\x03\x30\x03\x31", 6, L"\x0330\x0331", 2},
+      // More Unicode text.
+      {"\xFE\xFF\x7F\x51\x98\x75\x00\x20\x56\xFE\x72\x47\x00"
+       "\x20\x8D\x44\x8B\xAF\x66\xF4\x59\x1A\x00\x20\x00\xBB",
+       26,
+       L"\x7F51\x9875\x0020\x56FE\x7247\x0020"
+       L"\x8D44\x8BAF\x66F4\x591A\x0020\x00BB",
+       12},
+      // Unicode escape sequence. https://crbug.com/pdfium/182
+      {"\xFE\xFF\x00\x1B\x6A\x61\x00\x1B\x00\x20\x53\x70\x52\x37", 14,
+       L"\x0020\x5370\x5237", 3},
+      {"\xFE\xFF\x00\x1B\x6A\x61\x00\x1B\x00\x20\x53\x70\x52\x37\x29", 15,
+       L"\x0020\x5370\x5237", 3},
+      {"\xFE\xFF\x00\x1B\x6A\x61\x4A\x50\x00\x1B\x00\x20\x53\x70\x52\x37", 16,
+       L"\x0020\x5370\x5237", 3},
+      {"\xFE\xFF\x00\x20\x00\x1B\x6A\x61\x4A\x50\x00\x1B\x52\x37", 14,
+       L"\x0020\x5237", 2},
+      // https://crbug.com/1001159
+      {"\xFE\xFF\x00\x1B\x00\x1B", 6, L"", 0},
+      {"\xFE\xFF\x00\x1B\x00\x1B\x20", 7, L"", 0},
+      {"\xFE\xFF\x00\x1B\x00\x1B\x00\x20", 8, L"\x0020", 1},
+  };
+
+  for (const auto& test_case : kTestData) {
+    WideString output = PDF_DecodeText(
+        pdfium::make_span(reinterpret_cast<const uint8_t*>(test_case.input),
+                          test_case.input_length));
+    ASSERT_EQ(test_case.expected_length, output.GetLength())
+        << "for case " << test_case.input;
+    const wchar_t* str_ptr = output.c_str();
+    for (size_t i = 0; i < test_case.expected_length; ++i) {
+      EXPECT_EQ(test_case.expected_output[i], str_ptr[i])
+          << "for case " << test_case.input << " char " << i;
+    }
   }
 }
 
 TEST(fpdf_parser_decode, EncodeText) {
-  struct EncodeTestData {
+  const struct EncodeTestData {
     const wchar_t* input;
     const char* expected_output;
     size_t expected_length;
-  } test_data[] = {
+  } kTestData[] = {
       // Empty src string.
       {L"", "", 0},
       // ASCII text.
@@ -97,15 +251,14 @@
        26},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
-    const auto& test_case = test_data[i];
+  for (const auto& test_case : kTestData) {
     ByteString output = PDF_EncodeText(test_case.input);
-    ASSERT_EQ(test_case.expected_length, output.GetLength()) << "for case "
-                                                             << i;
+    ASSERT_EQ(test_case.expected_length, output.GetLength())
+        << "for case " << test_case.input;
     const char* str_ptr = output.c_str();
     for (size_t j = 0; j < test_case.expected_length; ++j) {
-      EXPECT_EQ(test_case.expected_output[j], str_ptr[j]) << "for case " << i
-                                                          << " char " << j;
+      EXPECT_EQ(test_case.expected_output[j], str_ptr[j])
+          << "for case " << test_case.input << " char " << j;
     }
   }
 }
diff --git a/core/fpdfapi/parser/fpdf_parser_utility.cpp b/core/fpdfapi/parser/fpdf_parser_utility.cpp
index 45284a4..af58e8d 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_utility.cpp
@@ -9,6 +9,7 @@
 #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"
@@ -71,51 +72,51 @@
     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W'};
 
-int32_t GetHeaderOffset(const RetainPtr<IFX_SeekableReadStream>& pFile) {
-  const size_t kBufSize = 4;
+Optional<FX_FILESIZE> GetHeaderOffset(
+    const RetainPtr<IFX_SeekableReadStream>& pFile) {
+  static constexpr size_t kBufSize = 4;
   uint8_t buf[kBufSize];
-  for (int32_t offset = 0; offset <= 1024; ++offset) {
-    if (!pFile->ReadBlock(buf, offset, kBufSize))
-      return kInvalidHeaderOffset;
+  for (FX_FILESIZE offset = 0; offset <= 1024; ++offset) {
+    if (!pFile->ReadBlockAtOffset(buf, offset, kBufSize))
+      return {};
 
     if (memcmp(buf, "%PDF", 4) == 0)
       return offset;
   }
-  return kInvalidHeaderOffset;
+  return {};
 }
 
-int32_t GetDirectInteger(CPDF_Dictionary* pDict, const ByteString& key) {
-  CPDF_Number* pObj = ToNumber(pDict->GetObjectFor(key));
+int32_t GetDirectInteger(const CPDF_Dictionary* pDict, const ByteString& key) {
+  const CPDF_Number* pObj = ToNumber(pDict->GetObjectFor(key));
   return pObj ? pObj->GetInteger() : 0;
 }
 
-ByteString PDF_NameDecode(const ByteStringView& bstr) {
-  if (!bstr.Contains('#'))
-    return ByteString(bstr);
+ByteString PDF_NameDecode(ByteStringView orig) {
+  if (!orig.Contains('#'))
+    return ByteString(orig);
 
-  int size = bstr.GetLength();
+  size_t src_size = orig.GetLength();
+  size_t out_index = 0;
   ByteString result;
-  char* pDestStart = result.GetBuffer(size);
-  char* pDest = pDestStart;
-  for (int i = 0; i < size; i++) {
-    if (bstr[i] == '#' && i < size - 2) {
-      *pDest++ = FXSYS_HexCharToInt(bstr[i + 1]) * 16 +
-                 FXSYS_HexCharToInt(bstr[i + 2]);
-      i += 2;
-    } else {
-      *pDest++ = bstr[i];
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> pDest = result.GetBuffer(src_size);
+    for (size_t i = 0; i < src_size; i++) {
+      if (orig[i] == '#' && i + 2 < src_size) {
+        pDest[out_index++] = FXSYS_HexCharToInt(orig[i + 1]) * 16 +
+                             FXSYS_HexCharToInt(orig[i + 2]);
+        i += 2;
+      } else {
+        pDest[out_index++] = orig[i];
+      }
     }
   }
-  result.ReleaseBuffer(static_cast<size_t>(pDest - pDestStart));
+  result.ReleaseBuffer(out_index);
   return result;
 }
 
-ByteString PDF_NameDecode(const ByteString& orig) {
-  return orig.Contains("#") ? PDF_NameDecode(orig.AsStringView()) : orig;
-}
-
 ByteString PDF_NameEncode(const ByteString& orig) {
-  uint8_t* src_buf = (uint8_t*)orig.c_str();
+  const uint8_t* src_buf = reinterpret_cast<const uint8_t*>(orig.c_str());
   int src_len = orig.GetLength();
   int dest_len = 0;
   int i;
@@ -132,54 +133,91 @@
     return orig;
 
   ByteString res;
-  char* dest_buf = res.GetBuffer(dest_len);
-  dest_len = 0;
-  for (i = 0; i < src_len; i++) {
-    uint8_t ch = src_buf[i];
-    if (ch >= 0x80 || PDFCharIsWhitespace(ch) || ch == '#' ||
-        PDFCharIsDelimiter(ch)) {
-      dest_buf[dest_len++] = '#';
-      FXSYS_IntToTwoHexChars(ch, dest_buf + dest_len);
-      dest_len += 2;
-    } else {
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> dest_buf = res.GetBuffer(dest_len);
+    dest_len = 0;
+    for (i = 0; i < src_len; i++) {
+      uint8_t ch = src_buf[i];
+      if (ch >= 0x80 || PDFCharIsWhitespace(ch) || ch == '#' ||
+          PDFCharIsDelimiter(ch)) {
+        dest_buf[dest_len++] = '#';
+        FXSYS_IntToTwoHexChars(ch, &dest_buf[dest_len]);
+        dest_len += 2;
+        continue;
+      }
       dest_buf[dest_len++] = ch;
     }
   }
-  dest_buf[dest_len] = 0;
-  res.ReleaseBuffer(res.GetStringLength());
+  res.ReleaseBuffer(dest_len);
   return res;
 }
 
+std::vector<float> ReadArrayElementsToVector(const CPDF_Array* pArray,
+                                             size_t nCount) {
+  ASSERT(pArray);
+  ASSERT(pArray->size() >= nCount);
+  std::vector<float> ret(nCount);
+  for (size_t i = 0; i < nCount; ++i)
+    ret[i] = pArray->GetNumberAt(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 ValidateDictAllResourcesOfType(const CPDF_Dictionary* dict,
+                                    const ByteString& 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))
+      return false;
+  }
+  return true;
+}
+
+bool ValidateFontResourceDict(const CPDF_Dictionary* dict) {
+  return ValidateDictAllResourcesOfType(dict, "Font");
+}
+
 std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj) {
   if (!pObj) {
     buf << " null";
     return buf;
   }
   switch (pObj->GetType()) {
-    case CPDF_Object::NULLOBJ:
+    case CPDF_Object::kNullobj:
       buf << " null";
       break;
-    case CPDF_Object::BOOLEAN:
-    case CPDF_Object::NUMBER:
+    case CPDF_Object::kBoolean:
+    case CPDF_Object::kNumber:
       buf << " " << pObj->GetString();
       break;
-    case CPDF_Object::STRING:
+    case CPDF_Object::kString:
       buf << PDF_EncodeString(pObj->GetString(), pObj->AsString()->IsHex());
       break;
-    case CPDF_Object::NAME: {
+    case CPDF_Object::kName: {
       ByteString str = pObj->GetString();
       buf << "/" << PDF_NameEncode(str);
       break;
     }
-    case CPDF_Object::REFERENCE: {
+    case CPDF_Object::kReference: {
       buf << " " << pObj->AsReference()->GetRefObjNum() << " 0 R ";
       break;
     }
-    case CPDF_Object::ARRAY: {
+    case CPDF_Object::kArray: {
       const CPDF_Array* p = pObj->AsArray();
       buf << "[";
-      for (size_t i = 0; i < p->GetCount(); i++) {
-        CPDF_Object* pElement = p->GetObjectAt(i);
+      for (size_t i = 0; i < p->size(); i++) {
+        const CPDF_Object* pElement = p->GetObjectAt(i);
         if (pElement && !pElement->IsInline()) {
           buf << " " << pElement->GetObjNum() << " 0 R";
         } else {
@@ -189,12 +227,12 @@
       buf << "]";
       break;
     }
-    case CPDF_Object::DICTIONARY: {
-      const CPDF_Dictionary* p = pObj->AsDictionary();
+    case CPDF_Object::kDictionary: {
+      CPDF_DictionaryLocker locker(pObj->AsDictionary());
       buf << "<<";
-      for (const auto& it : *p) {
+      for (const auto& it : locker) {
         const ByteString& key = it.first;
-        CPDF_Object* pValue = it.second.get();
+        CPDF_Object* pValue = it.second.Get();
         buf << "/" << PDF_NameEncode(key);
         if (pValue && !pValue->IsInline()) {
           buf << " " << pValue->GetObjNum() << " 0 R ";
@@ -205,7 +243,7 @@
       buf << ">>";
       break;
     }
-    case CPDF_Object::STREAM: {
+    case CPDF_Object::kStream: {
       const CPDF_Stream* p = pObj->AsStream();
       buf << p->GetDict() << "stream\r\n";
       auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(p);
diff --git a/core/fpdfapi/parser/fpdf_parser_utility.h b/core/fpdfapi/parser/fpdf_parser_utility.h
index e4b922c..75f4076 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility.h
+++ b/core/fpdfapi/parser/fpdf_parser_utility.h
@@ -7,12 +7,17 @@
 #ifndef CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_
 #define CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_
 
+#include <ostream>
+#include <vector>
+
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/optional.h"
 
-class IFX_SeekableReadStream;
+class CPDF_Array;
 class CPDF_Dictionary;
 class CPDF_Object;
+class IFX_SeekableReadStream;
 
 // Use the accessors below instead of directly accessing PDF_CharType.
 extern const char PDF_CharType[256];
@@ -34,14 +39,32 @@
   return c == '\r' || c == '\n';
 }
 
-constexpr int32_t kInvalidHeaderOffset = -1;
-
-// On success, return a positive offset value to the PDF header.. If the header
+// 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
-// |kInvalidHeaderOffset|.
-int32_t GetHeaderOffset(const RetainPtr<IFX_SeekableReadStream>& pFile);
+// nullopt.
+Optional<FX_FILESIZE> GetHeaderOffset(
+    const RetainPtr<IFX_SeekableReadStream>& pFile);
 
-int32_t GetDirectInteger(CPDF_Dictionary* pDict, const ByteString& key);
+int32_t GetDirectInteger(const CPDF_Dictionary* pDict, const ByteString& key);
+
+ByteString PDF_NameDecode(ByteStringView orig);
+ByteString PDF_NameEncode(const ByteString& orig);
+
+// Return |nCount| elements from |pArray| as a vector of floats. |pArray| must
+// have at least |nCount| elements.
+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 all entries in |dict| are dictionaries
+// of |type|.
+bool ValidateDictAllResourcesOfType(const CPDF_Dictionary* dict,
+                                    const ByteString& type);
+
+// Shorthand for ValidateDictAllResourcesOfType(dict, "Font").
+bool ValidateFontResourceDict(const CPDF_Dictionary* dict);
 
 std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj);
 
diff --git a/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp b/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp
new file mode 100644
index 0000000..098c3bd
--- /dev/null
+++ b/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp
@@ -0,0 +1,108 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#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 "testing/gtest/include/gtest/gtest.h"
+
+TEST(fpdf_parser_utility, PDF_NameDecode) {
+  EXPECT_EQ("", PDF_NameDecode(""));
+  EXPECT_EQ("A", PDF_NameDecode("A"));
+  EXPECT_EQ("#", PDF_NameDecode("#"));
+  EXPECT_EQ("#4", PDF_NameDecode("#4"));
+  EXPECT_EQ("A", PDF_NameDecode("#41"));
+  EXPECT_EQ("A1", PDF_NameDecode("#411"));
+}
+
+TEST(fpdf_parser_utility, PDF_NameEncode) {
+  EXPECT_EQ("", PDF_NameEncode(""));
+  EXPECT_EQ("A", PDF_NameEncode("A"));
+  EXPECT_EQ("#23", PDF_NameEncode("#"));
+  EXPECT_EQ("#20", PDF_NameEncode(" "));
+  EXPECT_EQ("!@#23$#25^&*#28#29#3C#3E#5B#5D", PDF_NameEncode("!@#$%^&*()<>[]"));
+  EXPECT_EQ("#C2", PDF_NameEncode("\xc2"));
+  EXPECT_EQ("f#C2#A5", PDF_NameEncode("f\xc2\xa5"));
+}
+
+TEST(fpdf_parser_utility, ValidateDictType) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+
+  // No type.
+  EXPECT_FALSE(ValidateDictType(dict.Get(), "foo"));
+  EXPECT_FALSE(ValidateDictType(dict.Get(), "bar"));
+
+  // Add the wrong object type.
+  dict->SetNewFor<CPDF_String>("Type", L"foo");
+  EXPECT_FALSE(ValidateDictType(dict.Get(), "foo"));
+  EXPECT_FALSE(ValidateDictType(dict.Get(), "bar"));
+
+  // Add the correct object type.
+  dict->SetNewFor<CPDF_Name>("Type", "foo");
+  EXPECT_TRUE(ValidateDictType(dict.Get(), "foo"));
+  EXPECT_FALSE(ValidateDictType(dict.Get(), "bar"));
+}
+
+TEST(fpdf_parser_utility, ValidateDictAllResourcesOfType) {
+  CPDF_PageModule::Create();
+
+  {
+    // Direct dictionary.
+    auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+
+    // Empty dict is ok.
+    EXPECT_TRUE(ValidateDictAllResourcesOfType(dict.Get(), "foo"));
+    EXPECT_TRUE(ValidateDictAllResourcesOfType(dict.Get(), "bar"));
+
+    // nullptr is not.
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(nullptr, "foo"));
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(nullptr, "bar"));
+
+    // Add two correct dictionary entries and one string entry.
+    CPDF_Dictionary* 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");
+    dict->SetNewFor<CPDF_String>("f3", L"foo");
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(dict.Get(), "foo"));
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(dict.Get(), "bar"));
+
+    // Change the string entry to a dictionary, but with the wrong /Type.
+    new_dict = dict->SetNewFor<CPDF_Dictionary>("f3");
+    new_dict->SetNewFor<CPDF_Name>("Type", "bar");
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(dict.Get(), "foo"));
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(dict.Get(), "bar"));
+
+    // Change the /Type to match.
+    new_dict->SetNewFor<CPDF_Name>("Type", "foo");
+    EXPECT_TRUE(ValidateDictAllResourcesOfType(dict.Get(), "foo"));
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(dict.Get(), "bar"));
+  }
+
+  {
+    // Indirect dictionary.
+    auto doc = pdfium::MakeUnique<CPDF_Document>(
+        pdfium::MakeUnique<CPDF_DocRenderData>(),
+        pdfium::MakeUnique<CPDF_DocPageData>());
+
+    auto dict = doc->New<CPDF_Dictionary>();
+
+    // Add a correct dictionary entry.
+    CPDF_Dictionary* new_dict = doc->NewIndirect<CPDF_Dictionary>();
+    new_dict->SetNewFor<CPDF_Name>("Type", "foo");
+    dict->SetNewFor<CPDF_Reference>("f1", doc.get(), new_dict->GetObjNum());
+
+    EXPECT_TRUE(ValidateDictAllResourcesOfType(dict.Get(), "foo"));
+    EXPECT_FALSE(ValidateDictAllResourcesOfType(dict.Get(), "bar"));
+  }
+
+  CPDF_PageModule::Destroy();
+}
diff --git a/core/fpdfapi/render/Android.bp b/core/fpdfapi/render/Android.bp
new file mode 100644
index 0000000..60818b5
--- /dev/null
+++ b/core/fpdfapi/render/Android.bp
@@ -0,0 +1,28 @@
+cc_library_static {
+    name: "libpdfium-render",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-fxcodec",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+        "libpdfium-font",
+        "libpdfium-page",
+        "libpdfium-parser",
+    ],
+
+    exclude_srcs: [
+        // is_win
+        "cpdf_windowsrenderdevice.cpp",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/core/fpdfapi/render/BUILD.gn b/core/fpdfapi/render/BUILD.gn
new file mode 100644
index 0000000..ddce77a
--- /dev/null
+++ b/core/fpdfapi/render/BUILD.gn
@@ -0,0 +1,80 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+source_set("render") {
+  sources = [
+    "cpdf_charposlist.cpp",
+    "cpdf_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",
+    "cpdf_progressiverenderer.h",
+    "cpdf_rendercontext.cpp",
+    "cpdf_rendercontext.h",
+    "cpdf_renderoptions.cpp",
+    "cpdf_renderoptions.h",
+    "cpdf_rendershading.cpp",
+    "cpdf_rendershading.h",
+    "cpdf_renderstatus.cpp",
+    "cpdf_renderstatus.h",
+    "cpdf_scaledrenderbuffer.cpp",
+    "cpdf_scaledrenderbuffer.h",
+    "cpdf_textrenderer.cpp",
+    "cpdf_textrenderer.h",
+    "cpdf_type3cache.cpp",
+    "cpdf_type3cache.h",
+    "cpdf_type3glyphmap.cpp",
+    "cpdf_type3glyphmap.h",
+  ]
+  configs += [ "../../../:pdfium_core_config" ]
+  deps = [
+    "../../../constants",
+    "../../fxcodec",
+    "../../fxcrt",
+    "../../fxge",
+    "../font",
+    "../page",
+    "../parser",
+  ]
+  if (is_win) {
+    sources += [
+      "cpdf_windowsrenderdevice.cpp",
+      "cpdf_windowsrenderdevice.h",
+    ]
+  }
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [ "cpdf_docrenderdata_unittest.cpp" ]
+  deps = [
+    ":render",
+    "../page",
+    "../parser",
+  ]
+  pdfium_root_dir = "../../../"
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [
+    "fpdf_progressive_render_embeddertest.cpp",
+    "fpdf_render_pattern_embeddertest.cpp",
+  ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/core/fpdfapi/render/cpdf_charposlist.cpp b/core/fpdfapi/render/cpdf_charposlist.cpp
index c0c700c..b8d0cf2 100644
--- a/core/fpdfapi/render/cpdf_charposlist.cpp
+++ b/core/fpdfapi/render/cpdf_charposlist.cpp
@@ -6,56 +6,75 @@
 
 #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() {
-  m_pCharPos = nullptr;
-  m_nChars = 0;
-}
-
-CPDF_CharPosList::~CPDF_CharPosList() {
-  FX_Free(m_pCharPos);
-}
-
-void CPDF_CharPosList::Load(const std::vector<uint32_t>& charCodes,
-                            const std::vector<float>& charPos,
-                            CPDF_Font* pFont,
-                            float FontSize) {
-  m_pCharPos = FX_Alloc(FXTEXT_CHARPOS, charCodes.size());
-  m_nChars = 0;
+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();
-  for (size_t iChar = 0; iChar < charCodes.size(); ++iChar) {
-    uint32_t CharCode = charCodes[iChar];
+  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;
-    FXTEXT_CHARPOS& charpos = m_pCharPos[m_nChars++];
+    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 _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
     charpos.m_ExtGID = pFont->GlyphFromCharCodeExt(CharCode);
-    GlyphID = charpos.m_ExtGID;
+    GlyphID = charpos.m_ExtGID != static_cast<uint32_t>(-1)
+                  ? charpos.m_ExtGID
+                  : charpos.m_GlyphIndex;
 #endif
-    CFX_Font* pCurrentFont;
-    if (GlyphID != static_cast<uint32_t>(-1)) {
-      charpos.m_FallbackFontPosition = -1;
-      pCurrentFont = pFont->GetFont();
-    } else {
+    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 _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
       charpos.m_ExtGID = charpos.m_GlyphIndex;
 #endif
+    } else {
+      pCurrentFont = pFont->GetFont();
+      charpos.m_FallbackFontPosition = -1;
     }
 
     if (!pFont->IsEmbedded() && !pFont->IsCIDFont())
@@ -63,24 +82,23 @@
     else
       charpos.m_FontCharWidth = 0;
 
-    charpos.m_Origin = CFX_PointF(iChar > 0 ? charPos[iChar - 1] : 0, 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) {
-      int pdfGlyphWidth = pFont->GetCharWidthF(CharCode);
-      int ftGlyphWidth =
+      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) * FontSize / 2000.0f;
+            (pdfGlyphWidth - ftGlyphWidth) * font_size / 2000.0f;
       } else if (pdfGlyphWidth && ftGlyphWidth &&
                  pdfGlyphWidth < ftGlyphWidth) {
         scalingFactor = static_cast<float>(pdfGlyphWidth) / ftGlyphWidth;
-        ASSERT(scalingFactor >= 0.0f);
         charpos.m_AdjustMatrix[0] = scalingFactor;
         charpos.m_AdjustMatrix[1] = 0.0f;
         charpos.m_AdjustMatrix[2] = 0.0f;
@@ -98,8 +116,8 @@
       short vx;
       short vy;
       pCIDFont->GetVertOrigin(CID, vx, vy);
-      charpos.m_Origin.x -= FontSize * vx / 1000;
-      charpos.m_Origin.y -= FontSize * vy / 1000;
+      charpos.m_Origin.x -= font_size * vx / 1000;
+      charpos.m_Origin.y -= font_size * vy / 1000;
     }
 
     const uint8_t* pTransform = pCIDFont->GetCIDTransform(CID);
@@ -111,10 +129,12 @@
       charpos.m_AdjustMatrix[2] = pCIDFont->CIDTransformToFloat(pTransform[2]);
       charpos.m_AdjustMatrix[3] = pCIDFont->CIDTransformToFloat(pTransform[3]);
       charpos.m_Origin.x +=
-          pCIDFont->CIDTransformToFloat(pTransform[4]) * FontSize;
+          pCIDFont->CIDTransformToFloat(pTransform[4]) * font_size;
       charpos.m_Origin.y +=
-          pCIDFont->CIDTransformToFloat(pTransform[5]) * FontSize;
+          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
index c4636bc..b06fcfc 100644
--- a/core/fpdfapi/render/cpdf_charposlist.h
+++ b/core/fpdfapi/render/cpdf_charposlist.h
@@ -10,20 +10,22 @@
 #include <vector>
 
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_renderdevice.h"
 
 class CPDF_Font;
+class TextCharPos;
 
 class CPDF_CharPosList {
  public:
-  CPDF_CharPosList();
+  CPDF_CharPosList(const std::vector<uint32_t>& charCodes,
+                   const std::vector<float>& charPos,
+                   CPDF_Font* pFont,
+                   float font_size);
   ~CPDF_CharPosList();
-  void Load(const std::vector<uint32_t>& charCodes,
-            const std::vector<float>& charPos,
-            CPDF_Font* pFont,
-            float font_size);
-  FXTEXT_CHARPOS* m_pCharPos;
-  uint32_t m_nChars;
+
+  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 b96d002..3426f01 100644
--- a/core/fpdfapi/render/cpdf_devicebuffer.cpp
+++ b/core/fpdfapi/render/cpdf_devicebuffer.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/render/cpdf_devicebuffer.h"
 
+#include "build/build_config.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
@@ -13,44 +14,60 @@
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
 
-CPDF_DeviceBuffer::CPDF_DeviceBuffer() {}
+namespace {
 
-CPDF_DeviceBuffer::~CPDF_DeviceBuffer() {}
-
-bool CPDF_DeviceBuffer::Initialize(CPDF_RenderContext* pContext,
-                                   CFX_RenderDevice* pDevice,
-                                   FX_RECT* pRect,
-                                   const CPDF_PageObject* pObj,
-                                   int max_dpi) {
-  m_pDevice = pDevice;
-  m_pContext = pContext;
-  m_Rect = *pRect;
-  m_pObject = pObj;
-  m_Matrix.Translate(-pRect->left, -pRect->top);
-#if _FX_PLATFORM_ != _FX_PLATFORM_APPLE_
-  int horz_size = pDevice->GetDeviceCaps(FXDC_HORZ_SIZE);
-  int vert_size = pDevice->GetDeviceCaps(FXDC_VERT_SIZE);
-  if (horz_size && vert_size && max_dpi) {
-    int dpih =
-        pDevice->GetDeviceCaps(FXDC_PIXEL_WIDTH) * 254 / (horz_size * 10);
-    int dpiv =
-        pDevice->GetDeviceCaps(FXDC_PIXEL_HEIGHT) * 254 / (vert_size * 10);
-    if (dpih > max_dpi)
-      m_Matrix.Scale((float)(max_dpi) / dpih, 1.0f);
-    if (dpiv > max_dpi)
-      m_Matrix.Scale(1.0f, (float)(max_dpi) / (float)dpiv);
-  }
+#if defined(OS_MACOSX)
+constexpr bool kScaleDeviceBuffer = false;
+#else
+constexpr bool kScaleDeviceBuffer = true;
 #endif
-  CFX_Matrix ctm = m_pDevice->GetCTM();
-  m_Matrix.Concat(CFX_Matrix(fabs(ctm.a), 0, 0, fabs(ctm.d), 0, 0));
 
+}  // namespace
+
+// static
+CFX_Matrix CPDF_DeviceBuffer::CalculateMatrix(CFX_RenderDevice* pDevice,
+                                              const FX_RECT& rect,
+                                              int max_dpi,
+                                              bool scale) {
+  CFX_Matrix matrix;
+  matrix.Translate(-rect.left, -rect.top);
+  if (scale) {
+    int horz_size = pDevice->GetDeviceCaps(FXDC_HORZ_SIZE);
+    int vert_size = pDevice->GetDeviceCaps(FXDC_VERT_SIZE);
+    if (horz_size && vert_size && max_dpi) {
+      int dpih =
+          pDevice->GetDeviceCaps(FXDC_PIXEL_WIDTH) * 254 / (horz_size * 10);
+      int dpiv =
+          pDevice->GetDeviceCaps(FXDC_PIXEL_HEIGHT) * 254 / (vert_size * 10);
+      if (dpih > max_dpi)
+        matrix.Scale(static_cast<float>(max_dpi) / dpih, 1.0f);
+      if (dpiv > max_dpi)
+        matrix.Scale(1.0f, static_cast<float>(max_dpi) / dpiv);
+    }
+  }
+  return matrix;
+}
+
+CPDF_DeviceBuffer::CPDF_DeviceBuffer(CPDF_RenderContext* pContext,
+                                     CFX_RenderDevice* pDevice,
+                                     const FX_RECT& rect,
+                                     const CPDF_PageObject* pObj,
+                                     int max_dpi)
+    : m_pDevice(pDevice),
+      m_pContext(pContext),
+      m_pObject(pObj),
+      m_pBitmap(pdfium::MakeRetain<CFX_DIBitmap>()),
+      m_Rect(rect),
+      m_Matrix(CalculateMatrix(pDevice, rect, max_dpi, kScaleDeviceBuffer)) {}
+
+CPDF_DeviceBuffer::~CPDF_DeviceBuffer() = default;
+
+bool CPDF_DeviceBuffer::Initialize() {
   FX_RECT bitmap_rect =
-      m_Matrix.TransformRect(CFX_FloatRect(*pRect)).GetOuterRect();
-  m_pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  m_pBitmap->Create(bitmap_rect.Width(), bitmap_rect.Height(), FXDIB_Argb);
-  return true;
+      m_Matrix.TransformRect(CFX_FloatRect(m_Rect)).GetOuterRect();
+  return m_pBitmap->Create(bitmap_rect.Width(), bitmap_rect.Height(),
+                           FXDIB_Argb);
 }
 
 void CPDF_DeviceBuffer::OutputToDevice() {
@@ -66,9 +83,9 @@
   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.Get(), nullptr, m_Matrix);
   pBuffer->CompositeBitmap(0, 0, pBuffer->GetWidth(), pBuffer->GetHeight(),
-                           m_pBitmap, 0, 0);
+                           m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false);
   m_pDevice->StretchDIBits(pBuffer, m_Rect.left, m_Rect.top, m_Rect.Width(),
                            m_Rect.Height());
 }
diff --git a/core/fpdfapi/render/cpdf_devicebuffer.h b/core/fpdfapi/render/cpdf_devicebuffer.h
index 854906e..1f16dbe 100644
--- a/core/fpdfapi/render/cpdf_devicebuffer.h
+++ b/core/fpdfapi/render/cpdf_devicebuffer.h
@@ -7,8 +7,6 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_DEVICEBUFFER_H_
 #define CORE_FPDFAPI_RENDER_CPDF_DEVICEBUFFER_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
@@ -20,25 +18,30 @@
 
 class CPDF_DeviceBuffer {
  public:
-  CPDF_DeviceBuffer();
+  static CFX_Matrix CalculateMatrix(CFX_RenderDevice* pDevice,
+                                    const FX_RECT& rect,
+                                    int max_dpi,
+                                    bool scale);
+
+  CPDF_DeviceBuffer(CPDF_RenderContext* pContext,
+                    CFX_RenderDevice* pDevice,
+                    const FX_RECT& rect,
+                    const CPDF_PageObject* pObj,
+                    int max_dpi);
   ~CPDF_DeviceBuffer();
 
-  bool Initialize(CPDF_RenderContext* pContext,
-                  CFX_RenderDevice* pDevice,
-                  FX_RECT* pRect,
-                  const CPDF_PageObject* pObj,
-                  int max_dpi);
+  bool Initialize();
   void OutputToDevice();
   RetainPtr<CFX_DIBitmap> GetBitmap() const { return m_pBitmap; }
-  const CFX_Matrix* GetMatrix() const { return &m_Matrix; }
+  const CFX_Matrix& GetMatrix() const { return m_Matrix; }
 
  private:
-  UnownedPtr<CFX_RenderDevice> m_pDevice;
-  UnownedPtr<CPDF_RenderContext> m_pContext;
-  UnownedPtr<const CPDF_PageObject> m_pObject;
-  RetainPtr<CFX_DIBitmap> m_pBitmap;
-  FX_RECT m_Rect;
-  CFX_Matrix m_Matrix;
+  UnownedPtr<CFX_RenderDevice> const m_pDevice;
+  UnownedPtr<CPDF_RenderContext> const m_pContext;
+  UnownedPtr<const CPDF_PageObject> const m_pObject;
+  RetainPtr<CFX_DIBitmap> const m_pBitmap;
+  const FX_RECT m_Rect;
+  const CFX_Matrix m_Matrix;
 };
 
 #endif  // CORE_FPDFAPI_RENDER_CPDF_DEVICEBUFFER_H_
diff --git a/core/fpdfapi/render/cpdf_dibsource.cpp b/core/fpdfapi/render/cpdf_dibsource.cpp
deleted file mode 100644
index c1b9607..0000000
--- a/core/fpdfapi/render/cpdf_dibsource.cpp
+++ /dev/null
@@ -1,1412 +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/render/cpdf_dibsource.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_image.h"
-#include "core/fpdfapi/page/cpdf_imageobject.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/fpdf_parser_decode.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
-#include "core/fpdfapi/render/cpdf_renderstatus.h"
-#include "core/fxcodec/codec/ccodec_basicmodule.h"
-#include "core/fxcodec/codec/ccodec_jbig2module.h"
-#include "core/fxcodec/codec/ccodec_jpegmodule.h"
-#include "core/fxcodec/codec/ccodec_jpxmodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
-#include "core/fxcodec/codec/cjpx_decoder.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/cfx_fixedbufgrow.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-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);
-  unsigned int byte = pData[bitpos / 8];
-  if (nbits == 8)
-    return byte;
-
-  if (nbits == 16)
-    return byte * 256 + pData[bitpos / 8 + 1];
-
-  return (byte >> (8 - nbits - (bitpos % 8))) & ((1 << nbits) - 1);
-}
-
-FX_SAFE_UINT32 CalculatePitch8(uint32_t bpc, uint32_t components, int width) {
-  FX_SAFE_UINT32 pitch = bpc;
-  pitch *= components;
-  pitch *= width;
-  pitch += 7;
-  pitch /= 8;
-  return pitch;
-}
-
-FX_SAFE_UINT32 CalculatePitch32(int bpp, int width) {
-  FX_SAFE_UINT32 pitch = bpp;
-  pitch *= width;
-  pitch += 31;
-  pitch /= 32;  // quantized to number of 32-bit words.
-  pitch *= 4;   // and then back to bytes, (not just /8 in one step).
-  return pitch;
-}
-
-bool IsAllowedBPCValue(int bpc) {
-  return bpc == 1 || bpc == 2 || bpc == 4 || bpc == 8 || bpc == 16;
-}
-
-bool IsAllowedICCComponents(int nComp) {
-  return nComp == 1 || nComp == 3 || nComp == 4;
-}
-
-template <typename T>
-T ClampValue(T value, T max_value) {
-  value = std::min(value, max_value);
-  value = std::max<T>(0, value);
-  return value;
-}
-
-// Wrapper class to use with std::unique_ptr for CJPX_Decoder.
-class JpxBitMapContext {
- public:
-  explicit JpxBitMapContext(CCodec_JpxModule* jpx_module)
-      : jpx_module_(jpx_module), decoder_(nullptr) {}
-
-  ~JpxBitMapContext() {}
-
-  void set_decoder(std::unique_ptr<CJPX_Decoder> decoder) {
-    decoder_ = std::move(decoder);
-  }
-
-  CJPX_Decoder* decoder() { return decoder_.get(); }
-
- private:
-  CCodec_JpxModule* const jpx_module_;  // Weak pointer.
-  std::unique_ptr<CJPX_Decoder> decoder_;
-
-  // Disallow evil constructors
-  JpxBitMapContext(const JpxBitMapContext&);
-  void operator=(const JpxBitMapContext&);
-};
-
-const int kMaxImageDimension = 0x01FFFF;
-
-}  // namespace
-
-CPDF_DIBSource::CPDF_DIBSource()
-    : m_pDocument(nullptr),
-      m_pStream(nullptr),
-      m_pDict(nullptr),
-      m_pColorSpace(nullptr),
-      m_Family(0),
-      m_bpc(0),
-      m_bpc_orig(0),
-      m_nComponents(0),
-      m_GroupFamily(0),
-      m_MatteColor(0),
-      m_bLoadMask(false),
-      m_bDefaultDecode(true),
-      m_bImageMask(false),
-      m_bDoBpcCheck(true),
-      m_bColorKey(false),
-      m_bHasMask(false),
-      m_bStdCS(false),
-      m_pCompData(nullptr),
-      m_pLineBuf(nullptr),
-      m_pMaskedLine(nullptr),
-      m_pMask(nullptr),
-      m_pMaskStream(nullptr),
-      m_Status(0) {}
-
-CPDF_DIBSource::~CPDF_DIBSource() {
-  FX_Free(m_pMaskedLine);
-  FX_Free(m_pLineBuf);
-  m_pCachedBitmap.Reset();  // TODO(tsepez): determine if required early here.
-  FX_Free(m_pCompData);
-  if (m_pColorSpace && m_pDocument) {
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData)
-      pPageData->ReleaseColorSpace(m_pColorSpace->GetArray());
-  }
-}
-
-bool CPDF_DIBSource::Load(CPDF_Document* pDoc, const CPDF_Stream* pStream) {
-  if (!pStream)
-    return false;
-
-  m_pDocument = pDoc;
-  m_pDict = pStream->GetDict();
-  if (!m_pDict)
-    return false;
-
-  m_pStream = 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 =
-      CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height;
-  if (!src_size.IsValid())
-    return false;
-
-  m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-  m_pStreamAcc->LoadAllData(false, src_size.ValueOrDie(), true);
-  if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData())
-    return false;
-
-  if (!CreateDecoder())
-    return false;
-
-  if (m_bImageMask) {
-    m_bpp = 1;
-    m_bpc = 1;
-    m_nComponents = 1;
-    m_AlphaFlag = 1;
-  } else if (m_bpc * m_nComponents == 1) {
-    m_bpp = 1;
-  } else if (m_bpc * m_nComponents <= 8) {
-    m_bpp = 8;
-  } else {
-    m_bpp = 24;
-  }
-  FX_SAFE_UINT32 pitch = CalculatePitch32(m_bpp, m_Width);
-  if (!pitch.IsValid())
-    return false;
-
-  m_pLineBuf = FX_Alloc(uint8_t, pitch.ValueOrDie());
-  LoadPalette();
-  if (m_bColorKey) {
-    m_bpp = 32;
-    m_AlphaFlag = 2;
-    pitch = CalculatePitch32(m_bpp, m_Width);
-    if (!pitch.IsValid())
-      return false;
-
-    m_pMaskedLine = FX_Alloc(uint8_t, pitch.ValueOrDie());
-  }
-  m_Pitch = pitch.ValueOrDie();
-  return true;
-}
-
-bool CPDF_DIBSource::ContinueToLoadMask() {
-  if (m_bImageMask) {
-    m_bpp = 1;
-    m_bpc = 1;
-    m_nComponents = 1;
-    m_AlphaFlag = 1;
-  } else if (m_bpc * m_nComponents == 1) {
-    m_bpp = 1;
-  } else if (m_bpc * m_nComponents <= 8) {
-    m_bpp = 8;
-  } else {
-    m_bpp = 24;
-  }
-  if (!m_bpc || !m_nComponents) {
-    return false;
-  }
-  FX_SAFE_UINT32 pitch = CalculatePitch32(m_bpp, m_Width);
-  if (!pitch.IsValid())
-    return false;
-
-  m_pLineBuf = FX_Alloc(uint8_t, pitch.ValueOrDie());
-  if (m_pColorSpace && m_bStdCS) {
-    m_pColorSpace->EnableStdConversion(true);
-  }
-  LoadPalette();
-  if (m_bColorKey) {
-    m_bpp = 32;
-    m_AlphaFlag = 2;
-    pitch = CalculatePitch32(m_bpp, m_Width);
-    if (!pitch.IsValid())
-      return false;
-    m_pMaskedLine = FX_Alloc(uint8_t, pitch.ValueOrDie());
-  }
-  m_Pitch = pitch.ValueOrDie();
-  return true;
-}
-
-int CPDF_DIBSource::StartLoadDIBSource(CPDF_Document* pDoc,
-                                       const CPDF_Stream* pStream,
-                                       bool bHasMask,
-                                       CPDF_Dictionary* pFormResources,
-                                       CPDF_Dictionary* pPageResources,
-                                       bool bStdCS,
-                                       uint32_t GroupFamily,
-                                       bool bLoadMask) {
-  if (!pStream)
-    return 0;
-
-  m_pDocument = pDoc;
-  m_pDict = pStream->GetDict();
-  m_pStream = pStream;
-  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 0;
-  }
-  m_GroupFamily = GroupFamily;
-  m_bLoadMask = bLoadMask;
-  if (!LoadColorInfo(m_pStream->IsInline() ? pFormResources : nullptr,
-                     pPageResources)) {
-    return 0;
-  }
-  if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0))
-    return 0;
-
-  FX_SAFE_UINT32 src_size =
-      CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height;
-  if (!src_size.IsValid())
-    return 0;
-
-  m_pStreamAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-  m_pStreamAcc->LoadAllData(false, src_size.ValueOrDie(), true);
-  if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData())
-    return 0;
-
-  int iCreatedDecoder = CreateDecoder();
-  if (!iCreatedDecoder)
-    return 0;
-
-  if (!ContinueToLoadMask())
-    return 0;
-
-  int iLoadedMask = m_bHasMask ? StartLoadMask() : 1;
-  if (iCreatedDecoder == 2 || iLoadedMask == 2)
-    return 2;
-
-  ASSERT(iCreatedDecoder == 1);
-  ASSERT(iLoadedMask == 1);
-  if (m_pColorSpace && m_bStdCS)
-    m_pColorSpace->EnableStdConversion(false);
-  return 1;
-}
-
-int CPDF_DIBSource::ContinueLoadDIBSource(IFX_PauseIndicator* pPause) {
-  if (m_Status == 2)
-    return ContinueLoadMaskDIB(pPause);
-
-  if (m_Status != 1)
-    return 0;
-
-  const ByteString& decoder = m_pStreamAcc->GetImageDecoder();
-  if (decoder == "JPXDecode")
-    return 0;
-
-  FXCODEC_STATUS iDecodeStatus;
-  CCodec_Jbig2Module* pJbig2Module = CPDF_ModuleMgr::Get()->GetJbig2Module();
-  if (!m_pJbig2Context) {
-    m_pJbig2Context = pdfium::MakeUnique<CCodec_Jbig2Context>();
-    if (m_pStreamAcc->GetImageParam()) {
-      CPDF_Stream* pGlobals =
-          m_pStreamAcc->GetImageParam()->GetStreamFor("JBIG2Globals");
-      if (pGlobals) {
-        m_pGlobalStream = pdfium::MakeRetain<CPDF_StreamAcc>(pGlobals);
-        m_pGlobalStream->LoadAllDataFiltered();
-      }
-    }
-    iDecodeStatus = pJbig2Module->StartDecode(
-        m_pJbig2Context.get(), m_pDocument->CodecContext(), m_Width, m_Height,
-        m_pStreamAcc, m_pGlobalStream, m_pCachedBitmap->GetBuffer(),
-        m_pCachedBitmap->GetPitch(), pPause);
-  } else {
-    iDecodeStatus = pJbig2Module->ContinueDecode(m_pJbig2Context.get(), pPause);
-  }
-
-  if (iDecodeStatus < 0) {
-    m_pCachedBitmap.Reset();
-    m_pGlobalStream.Reset();
-    m_pJbig2Context.reset();
-    return 0;
-  }
-  if (iDecodeStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    return 2;
-
-  int iContinueStatus = 1;
-  if (m_bHasMask) {
-    iContinueStatus = ContinueLoadMaskDIB(pPause);
-    m_Status = 2;
-  }
-  if (iContinueStatus == 2)
-    return 2;
-
-  if (m_pColorSpace && m_bStdCS)
-    m_pColorSpace->EnableStdConversion(false);
-  return iContinueStatus;
-}
-
-bool CPDF_DIBSource::LoadColorInfo(const CPDF_Dictionary* pFormResources,
-                                   const CPDF_Dictionary* pPageResources) {
-  m_bpc_orig = m_pDict->GetIntegerFor("BitsPerComponent");
-  if (m_pDict->GetIntegerFor("ImageMask"))
-    m_bImageMask = true;
-
-  if (m_bImageMask || !m_pDict->KeyExist("ColorSpace")) {
-    if (!m_bImageMask) {
-      CPDF_Object* pFilter = m_pDict->GetDirectObjectFor("Filter");
-      if (pFilter) {
-        ByteString filter;
-        if (pFilter->IsName()) {
-          filter = pFilter->GetString();
-        } else if (CPDF_Array* pArray = pFilter->AsArray()) {
-          filter = pArray->GetStringAt(pArray->GetCount() - 1);
-        }
-
-        if (filter == "JPXDecode") {
-          m_bDoBpcCheck = false;
-          return true;
-        }
-      }
-    }
-    m_bImageMask = true;
-    m_bpc = m_nComponents = 1;
-    CPDF_Array* pDecode = m_pDict->GetArrayFor("Decode");
-    m_bDefaultDecode = !pDecode || !pDecode->GetIntegerAt(0);
-    return true;
-  }
-
-  CPDF_Object* pCSObj = m_pDict->GetDirectObjectFor("ColorSpace");
-  if (!pCSObj)
-    return false;
-
-  CPDF_DocPageData* pDocPageData = m_pDocument->GetPageData();
-  if (pFormResources)
-    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pFormResources);
-  if (!m_pColorSpace)
-    m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pPageResources);
-  if (!m_pColorSpace)
-    return false;
-
-  m_Family = m_pColorSpace->GetFamily();
-  m_nComponents = m_pColorSpace->CountComponents();
-  if (m_Family == PDFCS_ICCBASED && pCSObj->IsName()) {
-    ByteString cs = pCSObj->GetString();
-    if (cs == "DeviceGray")
-      m_nComponents = 1;
-    else if (cs == "DeviceRGB")
-      m_nComponents = 3;
-    else if (cs == "DeviceCMYK")
-      m_nComponents = 4;
-  }
-  ValidateDictParam();
-  m_pCompData = GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey);
-  return !!m_pCompData;
-}
-
-DIB_COMP_DATA* CPDF_DIBSource::GetDecodeAndMaskArray(bool* bDefaultDecode,
-                                                     bool* bColorKey) {
-  if (!m_pColorSpace)
-    return nullptr;
-
-  DIB_COMP_DATA* const pCompData = FX_Alloc(DIB_COMP_DATA, m_nComponents);
-  int max_data = (1 << m_bpc) - 1;
-  CPDF_Array* pDecode = m_pDict->GetArrayFor("Decode");
-  if (pDecode) {
-    for (uint32_t i = 0; i < m_nComponents; i++) {
-      pCompData[i].m_DecodeMin = pDecode->GetNumberAt(i * 2);
-      float max = pDecode->GetNumberAt(i * 2 + 1);
-      pCompData[i].m_DecodeStep = (max - pCompData[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)
-        def_max = max_data;
-      if (def_min != pCompData[i].m_DecodeMin || def_max != max)
-        *bDefaultDecode = false;
-    }
-  } else {
-    for (uint32_t i = 0; i < m_nComponents; i++) {
-      float def_value;
-      m_pColorSpace->GetDefaultValue(i, &def_value, &pCompData[i].m_DecodeMin,
-                                     &pCompData[i].m_DecodeStep);
-      if (m_Family == PDFCS_INDEXED)
-        pCompData[i].m_DecodeStep = max_data;
-      pCompData[i].m_DecodeStep =
-          (pCompData[i].m_DecodeStep - pCompData[i].m_DecodeMin) / max_data;
-    }
-  }
-  if (m_pDict->KeyExist("SMask"))
-    return pCompData;
-
-  CPDF_Object* pMask = m_pDict->GetDirectObjectFor("Mask");
-  if (!pMask)
-    return pCompData;
-
-  if (CPDF_Array* pArray = pMask->AsArray()) {
-    if (pArray->GetCount() >= m_nComponents * 2) {
-      for (uint32_t i = 0; i < m_nComponents; i++) {
-        int min_num = pArray->GetIntegerAt(i * 2);
-        int max_num = pArray->GetIntegerAt(i * 2 + 1);
-        pCompData[i].m_ColorKeyMin = std::max(min_num, 0);
-        pCompData[i].m_ColorKeyMax = std::min(max_num, max_data);
-      }
-    }
-    *bColorKey = true;
-  }
-  return pCompData;
-}
-
-int CPDF_DIBSource::CreateDecoder() {
-  const ByteString& decoder = m_pStreamAcc->GetImageDecoder();
-  if (decoder.IsEmpty())
-    return 1;
-
-  if (m_bDoBpcCheck && m_bpc == 0)
-    return 0;
-
-  if (decoder == "JPXDecode") {
-    LoadJpxBitmap();
-    return m_pCachedBitmap ? 1 : 0;
-  }
-  if (decoder == "JBIG2Decode") {
-    m_pCachedBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (!m_pCachedBitmap->Create(
-            m_Width, m_Height, m_bImageMask ? FXDIB_1bppMask : FXDIB_1bppRgb)) {
-      m_pCachedBitmap.Reset();
-      return 0;
-    }
-    m_Status = 1;
-    return 2;
-  }
-
-  const uint8_t* src_data = m_pStreamAcc->GetData();
-  uint32_t src_size = m_pStreamAcc->GetSize();
-  const CPDF_Dictionary* pParams = m_pStreamAcc->GetImageParam();
-  if (decoder == "CCITTFaxDecode") {
-    m_pDecoder = FPDFAPI_CreateFaxDecoder(src_data, src_size, m_Width, m_Height,
-                                          pParams);
-  } else if (decoder == "FlateDecode") {
-    m_pDecoder = FPDFAPI_CreateFlateDecoder(
-        src_data, src_size, m_Width, m_Height, m_nComponents, m_bpc, pParams);
-  } else if (decoder == "RunLengthDecode") {
-    CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
-    m_pDecoder = pEncoders->GetBasicModule()->CreateRunLengthDecoder(
-        src_data, src_size, m_Width, m_Height, m_nComponents, m_bpc);
-  } else if (decoder == "DCTDecode") {
-    if (!CreateDCTDecoder(src_data, src_size, pParams))
-      return 0;
-  }
-  if (!m_pDecoder)
-    return 0;
-
-  FX_SAFE_UINT32 requested_pitch =
-      CalculatePitch8(m_bpc, m_nComponents, m_Width);
-  if (!requested_pitch.IsValid())
-    return 0;
-  FX_SAFE_UINT32 provided_pitch = CalculatePitch8(
-      m_pDecoder->GetBPC(), m_pDecoder->CountComps(), m_pDecoder->GetWidth());
-  if (!provided_pitch.IsValid())
-    return 0;
-  if (provided_pitch.ValueOrDie() < requested_pitch.ValueOrDie())
-    return 0;
-  return 1;
-}
-
-bool CPDF_DIBSource::CreateDCTDecoder(const uint8_t* src_data,
-                                      uint32_t src_size,
-                                      const CPDF_Dictionary* pParams) {
-  CCodec_JpegModule* pJpegModule = CPDF_ModuleMgr::Get()->GetJpegModule();
-  m_pDecoder = pJpegModule->CreateDecoder(
-      src_data, src_size, m_Width, m_Height, m_nComponents,
-      !pParams || pParams->GetIntegerFor("ColorTransform", 1));
-  if (m_pDecoder)
-    return true;
-
-  bool bTransform = false;
-  int comps;
-  int bpc;
-  if (!pJpegModule->LoadInfo(src_data, src_size, &m_Width, &m_Height, &comps,
-                             &bpc, &bTransform)) {
-    return false;
-  }
-
-  if (m_nComponents == static_cast<uint32_t>(comps)) {
-    m_bpc = bpc;
-    m_pDecoder = pJpegModule->CreateDecoder(
-        src_data, src_size, m_Width, m_Height, m_nComponents, bTransform);
-    return true;
-  }
-
-  m_nComponents = static_cast<uint32_t>(comps);
-  FX_Free(m_pCompData);
-  m_pCompData = nullptr;
-  if (m_pColorSpace) {
-    switch (m_Family) {
-      case PDFCS_DEVICEGRAY:
-      case PDFCS_DEVICERGB:
-      case PDFCS_DEVICECMYK: {
-        uint32_t dwMinComps = ComponentsForFamily(m_Family);
-        if (m_pColorSpace->CountComponents() < dwMinComps ||
-            m_nComponents < dwMinComps) {
-          return false;
-        }
-        break;
-      }
-      case PDFCS_LAB: {
-        if (m_nComponents != 3 || m_pColorSpace->CountComponents() < 3)
-          return false;
-        break;
-      }
-      case PDFCS_ICCBASED: {
-        if (!IsAllowedICCComponents(m_nComponents) ||
-            !IsAllowedICCComponents(m_pColorSpace->CountComponents()) ||
-            m_pColorSpace->CountComponents() < m_nComponents) {
-          return false;
-        }
-        break;
-      }
-      default: {
-        if (m_pColorSpace->CountComponents() != m_nComponents)
-          return false;
-        break;
-      }
-    }
-  } else {
-    if (m_Family == PDFCS_LAB && m_nComponents != 3)
-      return false;
-  }
-  m_pCompData = GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey);
-  if (!m_pCompData)
-    return false;
-
-  m_bpc = bpc;
-  m_pDecoder = pJpegModule->CreateDecoder(src_data, src_size, m_Width, m_Height,
-                                          m_nComponents, bTransform);
-  return true;
-}
-
-void CPDF_DIBSource::LoadJpxBitmap() {
-  CCodec_JpxModule* pJpxModule = CPDF_ModuleMgr::Get()->GetJpxModule();
-  auto context = pdfium::MakeUnique<JpxBitMapContext>(pJpxModule);
-  context->set_decoder(pJpxModule->CreateDecoder(
-      m_pStreamAcc->GetData(), m_pStreamAcc->GetSize(), m_pColorSpace));
-  if (!context->decoder())
-    return;
-
-  uint32_t width = 0;
-  uint32_t height = 0;
-  uint32_t components = 0;
-  pJpxModule->GetImageInfo(context->decoder(), &width, &height, &components);
-  if (static_cast<int>(width) < m_Width || static_cast<int>(height) < m_Height)
-    return;
-
-  bool bSwapRGB = false;
-  if (m_pColorSpace) {
-    if (components != m_pColorSpace->CountComponents())
-      return;
-
-    if (m_pColorSpace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB)) {
-      bSwapRGB = true;
-      m_pColorSpace = nullptr;
-    }
-  } else {
-    if (components == 3) {
-      bSwapRGB = true;
-    } else if (components == 4) {
-      m_pColorSpace = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
-    }
-    m_nComponents = components;
-  }
-
-  FXDIB_Format format;
-  if (components == 1) {
-    format = FXDIB_8bppRgb;
-  } else if (components <= 3) {
-    format = FXDIB_Rgb;
-  } else if (components == 4) {
-    format = FXDIB_Rgb32;
-  } else {
-    width = (width * components + 2) / 3;
-    format = FXDIB_Rgb;
-  }
-
-  m_pCachedBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!m_pCachedBitmap->Create(width, height, format)) {
-    m_pCachedBitmap.Reset();
-    return;
-  }
-  m_pCachedBitmap->Clear(0xFFFFFFFF);
-  std::vector<uint8_t> output_offsets(components);
-  for (uint32_t i = 0; i < components; ++i)
-    output_offsets[i] = i;
-  if (bSwapRGB) {
-    output_offsets[0] = 2;
-    output_offsets[2] = 0;
-  }
-  if (!pJpxModule->Decode(context->decoder(), m_pCachedBitmap->GetBuffer(),
-                          m_pCachedBitmap->GetPitch(), output_offsets)) {
-    m_pCachedBitmap.Reset();
-    return;
-  }
-  if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED &&
-      m_bpc < 8) {
-    int scale = 8 - m_bpc;
-    for (uint32_t row = 0; row < height; ++row) {
-      uint8_t* scanline =
-          const_cast<uint8_t*>(m_pCachedBitmap->GetScanline(row));
-      for (uint32_t col = 0; col < width; ++col) {
-        *scanline = (*scanline) >> scale;
-        ++scanline;
-      }
-    }
-  }
-  m_bpc = 8;
-}
-
-int CPDF_DIBSource::StartLoadMask() {
-  m_MatteColor = 0XFFFFFFFF;
-  m_pMaskStream = m_pDict->GetStreamFor("SMask");
-  if (m_pMaskStream) {
-    CPDF_Array* pMatte = m_pMaskStream->GetDict()->GetArrayFor("Matte");
-    if (pMatte && m_pColorSpace &&
-        m_pColorSpace->CountComponents() <= m_nComponents) {
-      float R, G, B;
-      std::vector<float> colors(m_nComponents);
-      for (uint32_t i = 0; i < m_nComponents; i++)
-        colors[i] = pMatte->GetFloatAt(i);
-
-      m_pColorSpace->GetRGB(colors.data(), &R, &G, &B);
-      m_MatteColor = FXARGB_MAKE(0, FXSYS_round(R * 255), FXSYS_round(G * 255),
-                                 FXSYS_round(B * 255));
-    }
-    return StartLoadMaskDIB();
-  }
-
-  m_pMaskStream = ToStream(m_pDict->GetDirectObjectFor("Mask"));
-  return m_pMaskStream ? StartLoadMaskDIB() : 1;
-}
-
-int CPDF_DIBSource::ContinueLoadMaskDIB(IFX_PauseIndicator* pPause) {
-  if (!m_pMask)
-    return 1;
-
-  int ret = m_pMask->ContinueLoadDIBSource(pPause);
-  if (ret == 2)
-    return 2;
-
-  if (m_pColorSpace && m_bStdCS)
-    m_pColorSpace->EnableStdConversion(false);
-
-  if (!ret) {
-    m_pMask.Reset();
-    return 0;
-  }
-  return 1;
-}
-
-RetainPtr<CPDF_DIBSource> CPDF_DIBSource::DetachMask() {
-  return std::move(m_pMask);
-}
-
-int CPDF_DIBSource::StartLoadMaskDIB() {
-  m_pMask = pdfium::MakeRetain<CPDF_DIBSource>();
-  int ret = m_pMask->StartLoadDIBSource(m_pDocument.Get(), m_pMaskStream.Get(),
-                                        false, nullptr, nullptr, true);
-  if (ret == 2) {
-    if (m_Status == 0)
-      m_Status = 2;
-    return 2;
-  }
-  if (!ret) {
-    m_pMask.Reset();
-    return 1;
-  }
-  return 1;
-}
-
-void CPDF_DIBSource::LoadPalette() {
-  if (m_bpc == 0) {
-    return;
-  }
-  if (m_bpc * m_nComponents > 8) {
-    return;
-  }
-  if (!m_pColorSpace) {
-    return;
-  }
-  if (m_bpc * m_nComponents == 1) {
-    if (m_bDefaultDecode &&
-        (m_Family == PDFCS_DEVICEGRAY || m_Family == PDFCS_DEVICERGB)) {
-      return;
-    }
-    if (m_pColorSpace->CountComponents() > 3) {
-      return;
-    }
-    float color_values[3];
-    color_values[0] = m_pCompData[0].m_DecodeMin;
-    color_values[1] = color_values[2] = color_values[0];
-    float R = 0.0f, G = 0.0f, B = 0.0f;
-    m_pColorSpace->GetRGB(color_values, &R, &G, &B);
-    FX_ARGB argb0 = ArgbEncode(255, FXSYS_round(R * 255), FXSYS_round(G * 255),
-                               FXSYS_round(B * 255));
-    color_values[0] += m_pCompData[0].m_DecodeStep;
-    color_values[1] += m_pCompData[0].m_DecodeStep;
-    color_values[2] += m_pCompData[0].m_DecodeStep;
-    m_pColorSpace->GetRGB(color_values, &R, &G, &B);
-    FX_ARGB argb1 = ArgbEncode(255, FXSYS_round(R * 255), FXSYS_round(G * 255),
-                               FXSYS_round(B * 255));
-    if (argb0 != 0xFF000000 || argb1 != 0xFFFFFFFF) {
-      SetPaletteArgb(0, argb0);
-      SetPaletteArgb(1, argb1);
-    }
-    return;
-  }
-  if (m_pColorSpace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY) &&
-      m_bpc == 8 && m_bDefaultDecode) {
-  } else {
-    int palette_count = 1 << (m_bpc * m_nComponents);
-    CFX_FixedBufGrow<float, 16> color_values(m_nComponents);
-    float* color_value = color_values;
-    for (int i = 0; i < palette_count; i++) {
-      int color_data = i;
-      for (uint32_t j = 0; j < m_nComponents; j++) {
-        int encoded_component = color_data % (1 << m_bpc);
-        color_data /= 1 << m_bpc;
-        color_value[j] = m_pCompData[j].m_DecodeMin +
-                         m_pCompData[j].m_DecodeStep * encoded_component;
-      }
-      float R = 0, G = 0, B = 0;
-      if (m_nComponents == 1 && m_Family == PDFCS_ICCBASED &&
-          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_value;
-        }
-        m_pColorSpace->GetRGB(temp_buf.data(), &R, &G, &B);
-      } else {
-        m_pColorSpace->GetRGB(color_value, &R, &G, &B);
-      }
-      SetPaletteArgb(i, ArgbEncode(255, FXSYS_round(R * 255),
-                                   FXSYS_round(G * 255), FXSYS_round(B * 255)));
-    }
-  }
-}
-
-void CPDF_DIBSource::ValidateDictParam() {
-  m_bpc = m_bpc_orig;
-  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 (CPDF_Array* pArray = pFilter->AsArray()) {
-      ByteString filter = pArray->GetStringAt(pArray->GetCount() - 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;
-      }
-    }
-  }
-
-  if (!IsAllowedBPCValue(m_bpc))
-    m_bpc = 0;
-}
-
-void CPDF_DIBSource::TranslateScanline24bpp(uint8_t* dest_scan,
-                                            const uint8_t* src_scan) const {
-  if (m_bpc == 0)
-    return;
-
-  unsigned int max_data = (1 << m_bpc) - 1;
-  if (m_bDefaultDecode) {
-    if (m_Family == PDFCS_DEVICERGB || m_Family == PDFCS_CALRGB) {
-      if (m_nComponents != 3)
-        return;
-
-      const uint8_t* src_pos = src_scan;
-      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;
-            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;
-            src_pos += 6;
-          }
-          break;
-        default:
-          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);
-            src_bit_pos += m_bpc;
-            unsigned int G = GetBits8(src_scan, src_bit_pos, m_bpc);
-            src_bit_pos += m_bpc;
-            unsigned int B = GetBits8(src_scan, 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_byte_pos += 3;
-          }
-          break;
-      }
-      return;
-    }
-    if (m_bpc == 8) {
-      if (m_nComponents == m_pColorSpace->CountComponents())
-        m_pColorSpace->TranslateImageLine(dest_scan, src_scan, m_Width, m_Width,
-                                          m_Height, TransMask());
-      return;
-    }
-  }
-
-  CFX_FixedBufGrow<float, 16> color_values1(m_nComponents);
-  float* color_values = color_values1;
-  float R = 0.0f;
-  float G = 0.0f;
-  float B = 0.0f;
-  if (m_bpc == 8) {
-    uint64_t src_byte_pos = 0;
-    size_t dest_byte_pos = 0;
-    for (int column = 0; column < m_Width; column++) {
-      for (uint32_t color = 0; color < m_nComponents; color++) {
-        uint8_t data = src_scan[src_byte_pos++];
-        color_values[color] = m_pCompData[color].m_DecodeMin +
-                              m_pCompData[color].m_DecodeStep * data;
-      }
-      if (TransMask()) {
-        float k = 1.0f - color_values[3];
-        R = (1.0f - color_values[0]) * k;
-        G = (1.0f - color_values[1]) * k;
-        B = (1.0f - color_values[2]) * k;
-      } else {
-        m_pColorSpace->GetRGB(color_values, &R, &G, &B);
-      }
-      R = ClampValue(R, 1.0f);
-      G = ClampValue(G, 1.0f);
-      B = ClampValue(B, 1.0f);
-      dest_scan[dest_byte_pos] = static_cast<uint8_t>(B * 255);
-      dest_scan[dest_byte_pos + 1] = static_cast<uint8_t>(G * 255);
-      dest_scan[dest_byte_pos + 2] = static_cast<uint8_t>(R * 255);
-      dest_byte_pos += 3;
-    }
-  } else {
-    uint64_t src_bit_pos = 0;
-    size_t dest_byte_pos = 0;
-    for (int column = 0; column < m_Width; column++) {
-      for (uint32_t color = 0; color < m_nComponents; color++) {
-        unsigned int data = GetBits8(src_scan, src_bit_pos, m_bpc);
-        color_values[color] = m_pCompData[color].m_DecodeMin +
-                              m_pCompData[color].m_DecodeStep * data;
-        src_bit_pos += m_bpc;
-      }
-      if (TransMask()) {
-        float k = 1.0f - color_values[3];
-        R = (1.0f - color_values[0]) * k;
-        G = (1.0f - color_values[1]) * k;
-        B = (1.0f - color_values[2]) * k;
-      } else {
-        m_pColorSpace->GetRGB(color_values, &R, &G, &B);
-      }
-      R = ClampValue(R, 1.0f);
-      G = ClampValue(G, 1.0f);
-      B = ClampValue(B, 1.0f);
-      dest_scan[dest_byte_pos] = static_cast<uint8_t>(B * 255);
-      dest_scan[dest_byte_pos + 1] = static_cast<uint8_t>(G * 255);
-      dest_scan[dest_byte_pos + 2] = static_cast<uint8_t>(R * 255);
-      dest_byte_pos += 3;
-    }
-  }
-}
-
-uint8_t* CPDF_DIBSource::GetBuffer() const {
-  return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() : nullptr;
-}
-
-const uint8_t* CPDF_DIBSource::GetScanline(int line) const {
-  if (m_bpc == 0) {
-    return nullptr;
-  }
-  FX_SAFE_UINT32 src_pitch = CalculatePitch8(m_bpc, m_nComponents, m_Width);
-  if (!src_pitch.IsValid())
-    return nullptr;
-  uint32_t src_pitch_value = src_pitch.ValueOrDie();
-  const uint8_t* pSrcLine = nullptr;
-  if (m_pCachedBitmap && src_pitch_value <= m_pCachedBitmap->GetPitch()) {
-    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 : m_pLineBuf;
-    memset(pLineBuf, 0xFF, m_Pitch);
-    return pLineBuf;
-  }
-  if (m_bpc * m_nComponents == 1) {
-    if (m_bImageMask && m_bDefaultDecode) {
-      for (uint32_t i = 0; i < src_pitch_value; i++) {
-        m_pLineBuf[i] = ~pSrcLine[i];
-      }
-    } else if (m_bColorKey) {
-      uint32_t reset_argb, set_argb;
-      reset_argb = m_pPalette ? m_pPalette.get()[0] : 0xFF000000;
-      set_argb = m_pPalette ? m_pPalette.get()[1] : 0xFFFFFFFF;
-      if (m_pCompData[0].m_ColorKeyMin == 0) {
-        reset_argb = 0;
-      }
-      if (m_pCompData[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);
-      for (int col = 0; col < m_Width; col++) {
-        if (pSrcLine[col / 8] & (1 << (7 - col % 8))) {
-          *dest_scan = set_argb;
-        } else {
-          *dest_scan = reset_argb;
-        }
-        dest_scan++;
-      }
-      return m_pMaskedLine;
-    } else {
-      memcpy(m_pLineBuf, pSrcLine, src_pitch_value);
-    }
-    return m_pLineBuf;
-  }
-  if (m_bpc * m_nComponents <= 8) {
-    if (m_bpc == 8) {
-      memcpy(m_pLineBuf, pSrcLine, 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);
-          color_index |= data << (color * m_bpc);
-          src_bit_pos += m_bpc;
-        }
-        m_pLineBuf[col] = color_index;
-      }
-    }
-    if (!m_bColorKey)
-      return m_pLineBuf;
-
-    uint8_t* pDestPixel = m_pMaskedLine;
-    const uint8_t* pSrcPixel = m_pLineBuf;
-    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;
-      }
-      *pDestPixel = (index < m_pCompData[0].m_ColorKeyMin ||
-                     index > m_pCompData[0].m_ColorKeyMax)
-                        ? 0xFF
-                        : 0;
-      pDestPixel++;
-    }
-    return m_pMaskedLine;
-  }
-  if (m_bColorKey) {
-    if (m_nComponents == 3 && m_bpc == 8) {
-      uint8_t* alpha_channel = m_pMaskedLine + 3;
-      for (int col = 0; col < m_Width; col++) {
-        const uint8_t* pPixel = pSrcLine + col * 3;
-        alpha_channel[col * 4] = (pPixel[0] < m_pCompData[0].m_ColorKeyMin ||
-                                  pPixel[0] > m_pCompData[0].m_ColorKeyMax ||
-                                  pPixel[1] < m_pCompData[1].m_ColorKeyMin ||
-                                  pPixel[1] > m_pCompData[1].m_ColorKeyMax ||
-                                  pPixel[2] < m_pCompData[2].m_ColorKeyMin ||
-                                  pPixel[2] > m_pCompData[2].m_ColorKeyMax)
-                                     ? 0xFF
-                                     : 0;
-      }
-    } else {
-      memset(m_pMaskedLine, 0xFF, m_Pitch);
-    }
-  }
-  if (m_pColorSpace) {
-    TranslateScanline24bpp(m_pLineBuf, pSrcLine);
-    pSrcLine = m_pLineBuf;
-  }
-  if (!m_bColorKey)
-    return pSrcLine;
-
-  const uint8_t* pSrcPixel = pSrcLine;
-  uint8_t* pDestPixel = m_pMaskedLine;
-  for (int col = 0; col < m_Width; col++) {
-    *pDestPixel++ = *pSrcPixel++;
-    *pDestPixel++ = *pSrcPixel++;
-    *pDestPixel++ = *pSrcPixel++;
-    pDestPixel++;
-  }
-  return m_pMaskedLine;
-}
-
-bool CPDF_DIBSource::SkipToScanline(int line,
-                                    IFX_PauseIndicator* pPause) const {
-  return m_pDecoder && m_pDecoder->SkipToScanline(line, pPause);
-}
-
-void CPDF_DIBSource::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 = 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_DIBSource::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 {
-  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_bColorKey) {
-    reset_argb = m_pPalette ? m_pPalette.get()[0] : 0xFF000000;
-    set_argb = m_pPalette ? m_pPalette.get()[1] : 0xFFFFFFFF;
-    if (m_pCompData[0].m_ColorKeyMin == 0) {
-      reset_argb = 0;
-    }
-    if (m_pCompData[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;
-      if (pSrcLine[src_x / 8] & (1 << (7 - src_x % 8))) {
-        dest_scan_dword[i] = set_argb;
-      } else {
-        dest_scan_dword[i] = reset_argb;
-      }
-    }
-    return;
-  } else {
-    if (dest_Bpp == 1) {
-    } else if (m_pPalette) {
-      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;
-    if (pSrcLine[src_x / 8] & (1 << (7 - src_x % 8))) {
-      if (dest_Bpp == 1) {
-        dest_scan[dest_pos] = static_cast<uint8_t>(set_argb);
-      } else if (dest_Bpp == 3) {
-        dest_scan[dest_pos] = FXARGB_B(set_argb);
-        dest_scan[dest_pos + 1] = FXARGB_G(set_argb);
-        dest_scan[dest_pos + 2] = FXARGB_R(set_argb);
-      } else {
-        *reinterpret_cast<uint32_t*>(dest_scan + dest_pos) = set_argb;
-      }
-    } else {
-      if (dest_Bpp == 1) {
-        dest_scan[dest_pos] = static_cast<uint8_t>(reset_argb);
-      } else if (dest_Bpp == 3) {
-        dest_scan[dest_pos] = FXARGB_B(reset_argb);
-        dest_scan[dest_pos + 1] = FXARGB_G(reset_argb);
-        dest_scan[dest_pos + 2] = FXARGB_R(reset_argb);
-      } else {
-        *reinterpret_cast<uint32_t*>(dest_scan + dest_pos) = reset_argb;
-      }
-    }
-  }
-}
-
-void CPDF_DIBSource::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[col] = color_index;
-    }
-    pSrcLine = m_pLineBuf;
-  }
-  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_pCompData[0].m_ColorKeyMin ||
-                     index > m_pCompData[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_DIBSource::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 = FXARGB_MAKE(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_pCompData[j].m_DecodeMin +
-                 m_pCompData[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 = FXARGB_MAKE(0xFF, color[2], color[1], color[0]);
-      } else {
-        argb = FXARGB_MAKE(0xFF, pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]);
-      }
-      if (m_bColorKey) {
-        int alpha = 0xFF;
-        if (m_nComponents == 3 && m_bpc == 8) {
-          alpha = (pSrcPixel[0] < m_pCompData[0].m_ColorKeyMin ||
-                   pSrcPixel[0] > m_pCompData[0].m_ColorKeyMax ||
-                   pSrcPixel[1] < m_pCompData[1].m_ColorKeyMin ||
-                   pSrcPixel[1] > m_pCompData[1].m_ColorKeyMax ||
-                   pSrcPixel[2] < m_pCompData[2].m_ColorKeyMin ||
-                   pSrcPixel[2] > m_pCompData[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);
-    }
-  }
-}
-
-bool CPDF_DIBSource::TransMask() const {
-  return m_bLoadMask && m_GroupFamily == PDFCS_DEVICECMYK &&
-         m_Family == PDFCS_DEVICECMYK;
-}
diff --git a/core/fpdfapi/render/cpdf_dibsource.h b/core/fpdfapi/render/cpdf_dibsource.h
deleted file mode 100644
index d3f6515..0000000
--- a/core/fpdfapi/render/cpdf_dibsource.h
+++ /dev/null
@@ -1,155 +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_RENDER_CPDF_DIBSOURCE_H_
-#define CORE_FPDFAPI_RENDER_CPDF_DIBSOURCE_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_clippath.h"
-#include "core/fpdfapi/page/cpdf_countedobject.h"
-#include "core/fpdfapi/page/cpdf_graphicstates.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fpdfapi/render/cpdf_imageloader.h"
-#include "core/fpdfapi/render/cpdf_rendercontext.h"
-#include "core/fpdfapi/render/cpdf_renderoptions.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/dib/cfx_dibsource.h"
-
-class CCodec_Jbig2Context;
-class CCodec_ScanlineDecoder;
-class CPDF_Color;
-class CPDF_Dictionary;
-class CPDF_Document;
-class CPDF_Stream;
-
-typedef struct {
-  float m_DecodeMin;
-  float m_DecodeStep;
-  int m_ColorKeyMin;
-  int m_ColorKeyMax;
-} DIB_COMP_DATA;
-
-#define FPDF_HUGE_IMAGE_SIZE 60000000
-
-class CPDF_DIBSource : public CFX_DIBSource {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  ~CPDF_DIBSource() override;
-
-  bool Load(CPDF_Document* pDoc, const CPDF_Stream* pStream);
-
-  // CFX_DIBSource
-  bool SkipToScanline(int line, IFX_PauseIndicator* 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;
-
-  const CPDF_ColorSpace* GetColorSpace() const { return m_pColorSpace; }
-  uint32_t GetMatteColor() const { return m_MatteColor; }
-
-  int StartLoadDIBSource(CPDF_Document* pDoc,
-                         const CPDF_Stream* pStream,
-                         bool bHasMask,
-                         CPDF_Dictionary* pFormResources,
-                         CPDF_Dictionary* pPageResources,
-                         bool bStdCS = false,
-                         uint32_t GroupFamily = 0,
-                         bool bLoadMask = false);
-  int ContinueLoadDIBSource(IFX_PauseIndicator* pPause);
-  int StartLoadMask();
-  int StartLoadMaskDIB();
-  int ContinueLoadMaskDIB(IFX_PauseIndicator* pPause);
-  bool ContinueToLoadMask();
-  RetainPtr<CPDF_DIBSource> DetachMask();
-
- private:
-  CPDF_DIBSource();
-
-  bool LoadColorInfo(const CPDF_Dictionary* pFormResources,
-                     const CPDF_Dictionary* pPageResources);
-  DIB_COMP_DATA* GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey);
-  void LoadJpxBitmap();
-  void LoadPalette();
-  int CreateDecoder();
-  bool CreateDCTDecoder(const uint8_t* src_data,
-                        uint32_t src_size,
-                        const CPDF_Dictionary* pParams);
-  void TranslateScanline24bpp(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;
-  bool TransMask() const;
-
-  UnownedPtr<CPDF_Document> m_pDocument;
-  UnownedPtr<const CPDF_Stream> m_pStream;
-  UnownedPtr<const CPDF_Dictionary> m_pDict;
-  RetainPtr<CPDF_StreamAcc> m_pStreamAcc;
-  CPDF_ColorSpace* m_pColorSpace;
-  uint32_t m_Family;
-  uint32_t m_bpc;
-  uint32_t m_bpc_orig;
-  uint32_t m_nComponents;
-  uint32_t m_GroupFamily;
-  uint32_t m_MatteColor;
-  bool m_bLoadMask;
-  bool m_bDefaultDecode;
-  bool m_bImageMask;
-  bool m_bDoBpcCheck;
-  bool m_bColorKey;
-  bool m_bHasMask;
-  bool m_bStdCS;
-  DIB_COMP_DATA* m_pCompData;
-  uint8_t* m_pLineBuf;
-  uint8_t* m_pMaskedLine;
-  RetainPtr<CFX_DIBitmap> m_pCachedBitmap;
-  RetainPtr<CPDF_DIBSource> m_pMask;
-  RetainPtr<CPDF_StreamAcc> m_pGlobalStream;
-  std::unique_ptr<CCodec_ScanlineDecoder> m_pDecoder;
-  std::unique_ptr<CCodec_Jbig2Context> m_pJbig2Context;
-  UnownedPtr<CPDF_Stream> m_pMaskStream;
-  int m_Status;
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_DIBSOURCE_H_
diff --git a/core/fpdfapi/render/cpdf_dibtransferfunc.cpp b/core/fpdfapi/render/cpdf_dibtransferfunc.cpp
deleted file mode 100644
index 3b0e8b2..0000000
--- a/core/fpdfapi/render/cpdf_dibtransferfunc.cpp
+++ /dev/null
@@ -1,179 +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_dibtransferfunc.h"
-
-#include <vector>
-
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/render/cpdf_transferfunc.h"
-
-CPDF_DIBTransferFunc::CPDF_DIBTransferFunc(
-    const RetainPtr<CPDF_TransferFunc>& pTransferFunc)
-    : m_pTransferFunc(pTransferFunc) {
-  m_RampR = pTransferFunc->GetSamples();
-  m_RampG = &pTransferFunc->GetSamples()[256];
-  m_RampB = &pTransferFunc->GetSamples()[512];
-}
-
-CPDF_DIBTransferFunc::~CPDF_DIBTransferFunc() {}
-
-FXDIB_Format CPDF_DIBTransferFunc::GetDestFormat() {
-  if (m_pSrc->IsAlphaMask())
-    return FXDIB_8bppMask;
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  return (m_pSrc->HasAlpha()) ? FXDIB_Argb : FXDIB_Rgb32;
-#else
-  return (m_pSrc->HasAlpha()) ? FXDIB_Argb : FXDIB_Rgb;
-#endif
-}
-
-FX_ARGB* CPDF_DIBTransferFunc::GetDestPalette() {
-  return nullptr;
-}
-
-void CPDF_DIBTransferFunc::TranslateScanline(
-    const uint8_t* src_buf,
-    std::vector<uint8_t>* dest_buf) const {
-  bool bSkip = false;
-  switch (m_pSrc->GetFormat()) {
-    case FXDIB_1bppRgb: {
-      int r0 = m_RampR[0];
-      int g0 = m_RampG[0];
-      int b0 = m_RampB[0];
-      int r1 = m_RampR[255];
-      int g1 = m_RampG[255];
-      int b1 = m_RampB[255];
-      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;
-        } else {
-          (*dest_buf)[index++] = b0;
-          (*dest_buf)[index++] = g0;
-          (*dest_buf)[index++] = r0;
-        }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-        index++;
-#endif
-      }
-      break;
-    }
-    case FXDIB_1bppMask: {
-      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;
-        else
-          (*dest_buf)[index++] = m0;
-      }
-      break;
-    }
-    case FXDIB_8bppRgb: {
-      FX_ARGB* pPal = m_pSrc->GetPalette();
-      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)];
-        } 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];
-        }
-        src_buf++;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-        index++;
-#endif
-      }
-      break;
-    }
-    case FXDIB_8bppMask: {
-      int index = 0;
-      for (int i = 0; i < m_Width; i++)
-        (*dest_buf)[index++] = m_RampR[*(src_buf++)];
-      break;
-    }
-    case FXDIB_Rgb: {
-      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 _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-        index++;
-#endif
-      }
-      break;
-    }
-    case FXDIB_Rgb32:
-      bSkip = true;
-    case FXDIB_Argb: {
-      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 (!bSkip) {
-          (*dest_buf)[index++] = *src_buf;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-        } else {
-          index++;
-#endif
-        }
-        src_buf++;
-      }
-      break;
-    }
-    default:
-      break;
-  }
-}
-
-void CPDF_DIBTransferFunc::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 _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-    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 _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-    }
-#endif
-  }
-}
diff --git a/core/fpdfapi/render/cpdf_dibtransferfunc.h b/core/fpdfapi/render/cpdf_dibtransferfunc.h
deleted file mode 100644
index 34f5bb1..0000000
--- a/core/fpdfapi/render/cpdf_dibtransferfunc.h
+++ /dev/null
@@ -1,45 +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_DIBTRANSFERFUNC_H_
-#define CORE_FPDFAPI_RENDER_CPDF_DIBTRANSFERFUNC_H_
-
-#include <vector>
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/dib/cfx_filtereddib.h"
-#include "core/fxge/fx_dib.h"
-
-class CPDF_TransferFunc;
-
-class CPDF_DIBTransferFunc : public CFX_FilteredDIB {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  ~CPDF_DIBTransferFunc() override;
-
-  // CFX_FilteredDIB
-  FXDIB_Format GetDestFormat() override;
-  FX_ARGB* GetDestPalette() override;
-  void TranslateScanline(const uint8_t* src_buf,
-                         std::vector<uint8_t>* dest_buf) const override;
-  void TranslateDownSamples(uint8_t* dest_buf,
-                            const uint8_t* src_buf,
-                            int pixels,
-                            int Bpp) const override;
-
- private:
-  explicit CPDF_DIBTransferFunc(
-      const RetainPtr<CPDF_TransferFunc>& pTransferFunc);
-
-  RetainPtr<CPDF_TransferFunc> m_pTransferFunc;
-  const uint8_t* m_RampR;
-  const uint8_t* m_RampG;
-  const uint8_t* m_RampB;
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_DIBTRANSFERFUNC_H_
diff --git a/core/fpdfapi/render/cpdf_docrenderdata.cpp b/core/fpdfapi/render/cpdf_docrenderdata.cpp
index 6480688..ba702b7 100644
--- a/core/fpdfapi/render/cpdf_docrenderdata.cpp
+++ b/core/fpdfapi/render/cpdf_docrenderdata.cpp
@@ -6,14 +6,17 @@
 
 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
 
+#include <array>
 #include <memory>
+#include <utility>
+#include <vector>
 
 #include "core/fpdfapi/font/cpdf_type3font.h"
+#include "core/fpdfapi/page/cpdf_dib.h"
 #include "core/fpdfapi/page/cpdf_function.h"
+#include "core/fpdfapi/page/cpdf_transferfunc.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/render/cpdf_dibsource.h"
-#include "core/fpdfapi/render/cpdf_transferfunc.h"
 #include "core/fpdfapi/render/cpdf_type3cache.h"
 
 namespace {
@@ -22,60 +25,47 @@
 
 }  // namespace
 
-CPDF_DocRenderData::CPDF_DocRenderData(CPDF_Document* pPDFDoc)
-    : m_pPDFDoc(pPDFDoc) {}
-
-CPDF_DocRenderData::~CPDF_DocRenderData() {
-  Clear(true);
+// static
+CPDF_DocRenderData* CPDF_DocRenderData::FromDocument(
+    const CPDF_Document* pDoc) {
+  return static_cast<CPDF_DocRenderData*>(pDoc->GetRenderData());
 }
 
-void CPDF_DocRenderData::Clear(bool bRelease) {
-  for (auto it = m_Type3FaceMap.begin(); it != m_Type3FaceMap.end();) {
-    auto curr_it = it++;
-    if (bRelease || curr_it->second->HasOneRef()) {
-      m_Type3FaceMap.erase(curr_it);
-    }
-  }
+CPDF_DocRenderData::CPDF_DocRenderData() = default;
 
-  for (auto it = m_TransferFuncMap.begin(); it != m_TransferFuncMap.end();) {
-    auto curr_it = it++;
-    if (bRelease || curr_it->second->HasOneRef())
-      m_TransferFuncMap.erase(curr_it);
-  }
-}
+CPDF_DocRenderData::~CPDF_DocRenderData() = default;
 
 RetainPtr<CPDF_Type3Cache> CPDF_DocRenderData::GetCachedType3(
     CPDF_Type3Font* pFont) {
   auto it = m_Type3FaceMap.find(pFont);
-  if (it != m_Type3FaceMap.end())
-    return it->second;
+  if (it != m_Type3FaceMap.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
 
   auto pCache = pdfium::MakeRetain<CPDF_Type3Cache>(pFont);
-  m_Type3FaceMap[pFont] = pCache;
+  m_Type3FaceMap[pFont].Reset(pCache.Get());
   return pCache;
 }
 
-void CPDF_DocRenderData::MaybePurgeCachedType3(CPDF_Type3Font* pFont) {
-  auto it = m_Type3FaceMap.find(pFont);
-  if (it != m_Type3FaceMap.end() && it->second->HasOneRef())
-    m_Type3FaceMap.erase(it);
-}
-
 RetainPtr<CPDF_TransferFunc> CPDF_DocRenderData::GetTransferFunc(
-    CPDF_Object* pObj) {
+    const CPDF_Object* pObj) {
   if (!pObj)
     return nullptr;
 
   auto it = m_TransferFuncMap.find(pObj);
-  if (it != m_TransferFuncMap.end())
-    return it->second;
+  if (it != m_TransferFuncMap.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
 
+  auto pFunc = CreateTransferFunc(pObj);
+  m_TransferFuncMap[pObj].Reset(pFunc.Get());
+  return pFunc;
+}
+
+RetainPtr<CPDF_TransferFunc> CPDF_DocRenderData::CreateTransferFunc(
+    const CPDF_Object* pObj) const {
   std::unique_ptr<CPDF_Function> pFuncs[3];
-  bool bUniTransfer = true;
-  bool bIdentity = true;
-  if (CPDF_Array* pArray = pObj->AsArray()) {
-    bUniTransfer = false;
-    if (pArray->GetCount() < 3)
+  const CPDF_Array* pArray = pObj->AsArray();
+  if (pArray) {
+    if (pArray->size() < 3)
       return nullptr;
 
     for (uint32_t i = 0; i < 3; ++i) {
@@ -88,44 +78,43 @@
     if (!pFuncs[0])
       return nullptr;
   }
-  auto pTransfer = pdfium::MakeRetain<CPDF_TransferFunc>(m_pPDFDoc.Get());
-  m_TransferFuncMap[pObj] = pTransfer;
 
-  float input;
   int noutput;
   float output[kMaxOutputs];
   memset(output, 0, sizeof(output));
-  for (int v = 0; v < 256; ++v) {
-    input = (float)v / 255.0f;
-    if (bUniTransfer) {
-      if (pFuncs[0] && pFuncs[0]->CountOutputs() <= kMaxOutputs)
-        pFuncs[0]->Call(&input, 1, output, &noutput);
-      int o = FXSYS_round(output[0] * 255);
-      if (o != v)
-        bIdentity = false;
-      for (int i = 0; i < 3; ++i)
-        pTransfer->GetSamples()[i * 256 + v] = o;
+
+  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) {
+      for (int i = 0; i < 3; ++i) {
+        if (pFuncs[i]->CountOutputs() > kMaxOutputs) {
+          samples[i][v] = v;
+          continue;
+        }
+        pFuncs[i]->Call(&input, 1, output, &noutput);
+        size_t o = FXSYS_roundf(output[0] * 255);
+        if (o != v)
+          bIdentity = false;
+        samples[i][v] = o;
+      }
       continue;
     }
-    for (int i = 0; i < 3; ++i) {
-      if (!pFuncs[i] || pFuncs[i]->CountOutputs() > kMaxOutputs) {
-        pTransfer->GetSamples()[i * 256 + v] = v;
-        continue;
-      }
-      pFuncs[i]->Call(&input, 1, output, &noutput);
-      int o = FXSYS_round(output[0] * 255);
-      if (o != v)
-        bIdentity = false;
-      pTransfer->GetSamples()[i * 256 + v] = o;
-    }
+    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;
   }
 
-  pTransfer->SetIdentity(bIdentity);
-  return pTransfer;
-}
-
-void CPDF_DocRenderData::MaybePurgeTransferFunc(CPDF_Object* pObj) {
-  auto it = m_TransferFuncMap.find(pObj);
-  if (it != m_TransferFuncMap.end() && it->second->HasOneRef())
-    m_TransferFuncMap.erase(it);
+  return pdfium::MakeRetain<CPDF_TransferFunc>(
+      GetDocument(), 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 7b3e0ed..32b90a3 100644
--- a/core/fpdfapi/render/cpdf_docrenderdata.h
+++ b/core/fpdfapi/render/cpdf_docrenderdata.h
@@ -9,34 +9,38 @@
 
 #include <map>
 
-#include "core/fpdfapi/page/cpdf_countedobject.h"
-#include "core/fpdfapi/render/cpdf_transferfunc.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
-class CPDF_Document;
 class CPDF_Font;
 class CPDF_Object;
+class CPDF_TransferFunc;
 class CPDF_Type3Cache;
 class CPDF_Type3Font;
 
-class CPDF_DocRenderData {
+class CPDF_DocRenderData : public CPDF_Document::RenderDataIface {
  public:
-  explicit CPDF_DocRenderData(CPDF_Document* pPDFDoc);
-  ~CPDF_DocRenderData();
+  static CPDF_DocRenderData* FromDocument(const CPDF_Document* pDoc);
+
+  CPDF_DocRenderData();
+  ~CPDF_DocRenderData() override;
+
+  CPDF_DocRenderData(const CPDF_DocRenderData&) = delete;
+  CPDF_DocRenderData& operator=(const CPDF_DocRenderData&) = delete;
 
   RetainPtr<CPDF_Type3Cache> GetCachedType3(CPDF_Type3Font* pFont);
-  void MaybePurgeCachedType3(CPDF_Type3Font* pFont);
+  RetainPtr<CPDF_TransferFunc> GetTransferFunc(const CPDF_Object* pObj);
 
-  RetainPtr<CPDF_TransferFunc> GetTransferFunc(CPDF_Object* pObj);
-  void MaybePurgeTransferFunc(CPDF_Object* pOb);
-
-  void Clear(bool bRelease);
+ protected:
+  // protected for use by test subclasses.
+  RetainPtr<CPDF_TransferFunc> CreateTransferFunc(
+      const CPDF_Object* pObj) const;
 
  private:
-  UnownedPtr<CPDF_Document> m_pPDFDoc;
-  std::map<CPDF_Font*, RetainPtr<CPDF_Type3Cache>> m_Type3FaceMap;
-  std::map<CPDF_Object*, RetainPtr<CPDF_TransferFunc>> m_TransferFuncMap;
+  std::map<CPDF_Font*, ObservedPtr<CPDF_Type3Cache>> m_Type3FaceMap;
+  std::map<const CPDF_Object*, ObservedPtr<CPDF_TransferFunc>>
+      m_TransferFuncMap;
 };
 
 #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
new file mode 100644
index 0000000..2fb25b6
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp
@@ -0,0 +1,268 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+
+#include <memory>
+#include <utility>
+
+#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_number.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+constexpr uint8_t kExpectedType0FunctionSamples[] = {
+    0,   3,   6,   9,   13,  16,  19,  22,  25,  28,  31,  34,  37,  40,  43,
+    46,  49,  52,  55,  58,  60,  63,  66,  68,  71,  74,  76,  79,  81,  84,
+    86,  88,  90,  93,  95,  97,  99,  101, 103, 105, 106, 108, 110, 111, 113,
+    114, 115, 117, 118, 119, 120, 121, 122, 123, 124, 125, 125, 126, 126, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 125, 125, 124, 123,
+    123, 122, 121, 120, 119, 117, 116, 115, 113, 112, 110, 109, 107, 105, 104,
+    102, 100, 98,  96,  94,  92,  89,  87,  85,  82,  80,  77,  75,  72,  70,
+    67,  64,  62,  59,  56,  53,  50,  48,  45,  42,  39,  36,  33,  30,  27,
+    23,  20,  17,  14,  11,  8,   5,   2,   254, 251, 248, 245, 242, 239, 236,
+    233, 229, 226, 223, 220, 217, 214, 211, 208, 206, 203, 200, 197, 194, 192,
+    189, 186, 184, 181, 179, 176, 174, 171, 169, 167, 164, 162, 160, 158, 156,
+    154, 152, 151, 149, 147, 146, 144, 143, 141, 140, 139, 137, 136, 135, 134,
+    133, 133, 132, 131, 131, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129,
+    129, 129, 130, 130, 131, 131, 132, 133, 134, 135, 136, 137, 138, 139, 141,
+    142, 143, 145, 146, 148, 150, 151, 153, 155, 157, 159, 161, 163, 166, 168,
+    170, 172, 175, 177, 180, 182, 185, 188, 190, 193, 196, 198, 201, 204, 207,
+    210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, 243, 247, 250, 253,
+    0};
+
+constexpr uint8_t kExpectedType2FunctionSamples[] = {
+    26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+    24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+    23, 23, 23, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+    22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+    21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+    20, 20, 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+    19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+    18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+    17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14,
+    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13,
+    13, 13, 13, 13, 13, 13, 13, 13, 13};
+
+constexpr uint8_t kExpectedType4FunctionSamples[] = {
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26,
+    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+    26, 26, 26, 26, 26, 26, 26, 26, 26};
+
+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));
+}
+
+RetainPtr<CPDF_Dictionary> CreateType2FunctionDict() {
+  auto func_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  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);
+
+  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);
+
+  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);
+
+  return func_dict;
+}
+
+RetainPtr<CPDF_Stream> CreateType4FunctionStream() {
+  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);
+
+  CPDF_Array* range_array = func_dict->SetNewFor<CPDF_Array>("Range");
+  range_array->AddNew<CPDF_Number>(-1);
+  range_array->AddNew<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));
+}
+
+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);
+
+  CPDF_Array* range_array = func_dict->SetNewFor<CPDF_Array>("Range");
+  range_array->AddNew<CPDF_Number>(-1);
+  range_array->AddNew<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));
+}
+
+class TestDocRenderData : public CPDF_DocRenderData {
+ public:
+  TestDocRenderData() : CPDF_DocRenderData() {}
+
+  RetainPtr<CPDF_TransferFunc> CreateTransferFuncForTesting(
+      const CPDF_Object* pObj) const {
+    return CreateTransferFunc(pObj);
+  }
+};
+
+TEST(CPDF_DocRenderDataTest, TransferFunctionOne) {
+  RetainPtr<CPDF_Dictionary> func_dict = CreateType2FunctionDict();
+
+  TestDocRenderData render_data;
+  auto func = render_data.CreateTransferFuncForTesting(func_dict.Get());
+  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());
+
+  for (size_t i = 0; i < FX_ArraySize(kExpectedType2FunctionSamples); ++i) {
+    EXPECT_EQ(kExpectedType2FunctionSamples[i], r_samples[i]);
+    EXPECT_EQ(kExpectedType2FunctionSamples[i], g_samples[i]);
+    EXPECT_EQ(kExpectedType2FunctionSamples[i], b_samples[i]);
+  }
+
+  EXPECT_EQ(0x000d0d0du, func->TranslateColor(0x00ffffff));
+  EXPECT_EQ(0x000d1a1au, func->TranslateColor(0x00ff0000));
+  EXPECT_EQ(0x001a0d1au, func->TranslateColor(0x0000ff00));
+  EXPECT_EQ(0x001a1a0du, func->TranslateColor(0x000000ff));
+  EXPECT_EQ(0x000f0f0fu, func->TranslateColor(0x00cccccc));
+  EXPECT_EQ(0x00191715u, func->TranslateColor(0x00123456));
+  EXPECT_EQ(0x000d0d0du, func->TranslateColor(0xffffffff));
+  EXPECT_EQ(0x001a1a1au, func->TranslateColor(0xff000000));
+  EXPECT_EQ(0x000d0d0du, func->TranslateColor(0xccffffff));
+  EXPECT_EQ(0x001a1a1au, func->TranslateColor(0x99000000));
+}
+
+TEST(CPDF_DocRenderDataTest, TransferFunctionArray) {
+  auto func_array = pdfium::MakeRetain<CPDF_Array>();
+  func_array->Add(CreateType0FunctionStream());
+  func_array->Add(CreateType2FunctionDict());
+  func_array->Add(CreateType4FunctionStream());
+
+  TestDocRenderData render_data;
+  auto func = render_data.CreateTransferFuncForTesting(func_array.Get());
+  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());
+
+  for (size_t i = 0; i < FX_ArraySize(kExpectedType2FunctionSamples); ++i) {
+    EXPECT_EQ(kExpectedType0FunctionSamples[i], r_samples[i]);
+    EXPECT_EQ(kExpectedType2FunctionSamples[i], g_samples[i]);
+    EXPECT_EQ(kExpectedType4FunctionSamples[i], b_samples[i]);
+  }
+
+  EXPECT_EQ(0x001a0d00u, func->TranslateColor(0x00ffffff));
+  EXPECT_EQ(0x001a1a00u, func->TranslateColor(0x00ff0000));
+  EXPECT_EQ(0x00190d00u, func->TranslateColor(0x0000ff00));
+  EXPECT_EQ(0x00191a00u, func->TranslateColor(0x000000ff));
+  EXPECT_EQ(0x001a0f87u, func->TranslateColor(0x00cccccc));
+  EXPECT_EQ(0x0019176du, func->TranslateColor(0x00123456));
+  EXPECT_EQ(0x001a0d00u, func->TranslateColor(0xffffffff));
+  EXPECT_EQ(0x00191a00u, func->TranslateColor(0xff000000));
+  EXPECT_EQ(0x001a0d00u, func->TranslateColor(0xccffffff));
+  EXPECT_EQ(0x00191a00u, func->TranslateColor(0x99000000));
+}
+
+TEST(CPDF_DocRenderDataTest, BadTransferFunctions) {
+  {
+    auto func_stream = CreateBadType4FunctionStream();
+
+    TestDocRenderData render_data;
+    auto func = render_data.CreateTransferFuncForTesting(func_stream.Get());
+    EXPECT_FALSE(func);
+  }
+
+  {
+    auto func_array = pdfium::MakeRetain<CPDF_Array>();
+
+    TestDocRenderData render_data;
+    auto func = render_data.CreateTransferFuncForTesting(func_array.Get());
+    EXPECT_FALSE(func);
+  }
+
+  {
+    auto func_array = pdfium::MakeRetain<CPDF_Array>();
+    func_array->Add(CreateType0FunctionStream());
+    func_array->Add(CreateType2FunctionDict());
+    func_array->Add(CreateBadType4FunctionStream());
+
+    TestDocRenderData render_data;
+    auto func = render_data.CreateTransferFuncForTesting(func_array.Get());
+    EXPECT_FALSE(func);
+  }
+}
+
+}  // namespace
diff --git a/core/fpdfapi/render/cpdf_imagecacheentry.cpp b/core/fpdfapi/render/cpdf_imagecacheentry.cpp
index f69bfda..3805b2d 100644
--- a/core/fpdfapi/render/cpdf_imagecacheentry.cpp
+++ b/core/fpdfapi/render/cpdf_imagecacheentry.cpp
@@ -9,51 +9,52 @@
 #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_dibsource.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_dwTimeCount(0),
-      m_MatteColor(0),
-      m_pDocument(pDoc),
-      m_pImage(pImage),
-      m_dwCacheSize(0) {}
+    : m_pDocument(pDoc), m_pImage(pImage) {}
 
-CPDF_ImageCacheEntry::~CPDF_ImageCacheEntry() {}
+CPDF_ImageCacheEntry::~CPDF_ImageCacheEntry() = default;
 
-void CPDF_ImageCacheEntry::Reset(const RetainPtr<CFX_DIBitmap>& pBitmap) {
+void CPDF_ImageCacheEntry::Reset() {
   m_pCachedBitmap.Reset();
-  if (pBitmap)
-    m_pCachedBitmap = pBitmap->Clone(nullptr);
   CalcSize();
 }
 
-static uint32_t FPDF_ImageCache_EstimateImageSize(
-    const RetainPtr<CFX_DIBSource>& pDIB) {
-  return pDIB && pDIB->GetBuffer()
-             ? (uint32_t)pDIB->GetHeight() * pDIB->GetPitch() +
-                   (uint32_t)pDIB->GetPaletteSize() * 4
-             : 0;
-}
-
-RetainPtr<CFX_DIBSource> CPDF_ImageCacheEntry::DetachBitmap() {
+RetainPtr<CFX_DIBBase> CPDF_ImageCacheEntry::DetachBitmap() {
   return std::move(m_pCurBitmap);
 }
 
-RetainPtr<CFX_DIBSource> CPDF_ImageCacheEntry::DetachMask() {
+RetainPtr<CFX_DIBBase> CPDF_ImageCacheEntry::DetachMask() {
   return std::move(m_pCurMask);
 }
 
-int CPDF_ImageCacheEntry::StartGetCachedBitmap(
-    CPDF_Dictionary* pFormResources,
+CPDF_DIB::LoadState CPDF_ImageCacheEntry::StartGetCachedBitmap(
+    const CPDF_Dictionary* pFormResources,
     CPDF_Dictionary* pPageResources,
     bool bStdCS,
     uint32_t GroupFamily,
@@ -64,33 +65,45 @@
   if (m_pCachedBitmap) {
     m_pCurBitmap = m_pCachedBitmap;
     m_pCurMask = m_pCachedMask;
-    return 1;
+    return CPDF_DIB::LoadState::kSuccess;
   }
 
-  m_pCurBitmap = pdfium::MakeRetain<CPDF_DIBSource>();
-  int ret = m_pCurBitmap.As<CPDF_DIBSource>()->StartLoadDIBSource(
+  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 == 2)
-    return ret;
+  if (ret == CPDF_DIB::LoadState::kContinue)
+    return CPDF_DIB::LoadState::kContinue;
 
-  if (!ret) {
+  if (ret == CPDF_DIB::LoadState::kSuccess)
+    ContinueGetCachedBitmap(pRenderStatus);
+  else
     m_pCurBitmap.Reset();
-    return 0;
-  }
-  ContinueGetCachedBitmap(pRenderStatus);
-  return 0;
+  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_DIBSource>()->GetMatteColor();
-  m_pCurMask = m_pCurBitmap.As<CPDF_DIBSource>()->DetachMask();
+  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() <
-      FPDF_HUGE_IMAGE_SIZE) {
+  if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) {
     m_pCachedBitmap = m_pCurBitmap->Clone(nullptr);
     m_pCurBitmap.Reset();
   } else {
@@ -105,21 +118,7 @@
   CalcSize();
 }
 
-int CPDF_ImageCacheEntry::Continue(IFX_PauseIndicator* pPause,
-                                   CPDF_RenderStatus* pRenderStatus) {
-  int ret = m_pCurBitmap.As<CPDF_DIBSource>()->ContinueLoadDIBSource(pPause);
-  if (!ret) {
-    m_pCurBitmap.Reset();
-    return 0;
-  }
-  if (ret == 2)
-    return ret;
-
-  ContinueGetCachedBitmap(pRenderStatus);
-  return 0;
-}
-
 void CPDF_ImageCacheEntry::CalcSize() {
-  m_dwCacheSize = FPDF_ImageCache_EstimateImageSize(m_pCachedBitmap) +
-                  FPDF_ImageCache_EstimateImageSize(m_pCachedMask);
+  m_dwCacheSize = GetEstimatedImageSize(m_pCachedBitmap) +
+                  GetEstimatedImageSize(m_pCachedMask);
 }
diff --git a/core/fpdfapi/render/cpdf_imagecacheentry.h b/core/fpdfapi/render/cpdf_imagecacheentry.h
index 2bede23..c3f373e 100644
--- a/core/fpdfapi/render/cpdf_imagecacheentry.h
+++ b/core/fpdfapi/render/cpdf_imagecacheentry.h
@@ -7,19 +7,16 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_
 #define CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_
 
-#include <memory>
-
+#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 CFX_DIBSource;
-class CFX_DIBitmap;
 class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_Image;
 class CPDF_RenderStatus;
-class IFX_PauseIndicator;
+class PauseIndicatorIface;
 
 class CPDF_ImageCacheEntry {
  public:
@@ -27,22 +24,27 @@
                        const RetainPtr<CPDF_Image>& pImage);
   ~CPDF_ImageCacheEntry();
 
-  void Reset(const RetainPtr<CFX_DIBitmap>& pBitmap);
+  void Reset();
   uint32_t EstimateSize() const { return m_dwCacheSize; }
   uint32_t GetTimeCount() const { return m_dwTimeCount; }
   CPDF_Image* GetImage() const { return m_pImage.Get(); }
-  int StartGetCachedBitmap(CPDF_Dictionary* pFormResources,
-                           CPDF_Dictionary* pPageResources,
-                           bool bStdCS,
-                           uint32_t GroupFamily,
-                           bool bLoadMask,
-                           CPDF_RenderStatus* pRenderStatus);
-  int Continue(IFX_PauseIndicator* pPause, CPDF_RenderStatus* pRenderStatus);
-  RetainPtr<CFX_DIBSource> DetachBitmap();
-  RetainPtr<CFX_DIBSource> DetachMask();
 
-  int m_dwTimeCount;
-  uint32_t m_MatteColor;
+  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);
@@ -50,11 +52,11 @@
 
   UnownedPtr<CPDF_Document> const m_pDocument;
   RetainPtr<CPDF_Image> const m_pImage;
-  RetainPtr<CFX_DIBSource> m_pCurBitmap;
-  RetainPtr<CFX_DIBSource> m_pCurMask;
-  RetainPtr<CFX_DIBSource> m_pCachedBitmap;
-  RetainPtr<CFX_DIBSource> m_pCachedMask;
-  uint32_t m_dwCacheSize;
+  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
index 08aa5f1..fac4799 100644
--- a/core/fpdfapi/render/cpdf_imageloader.cpp
+++ b/core/fpdfapi/render/cpdf_imageloader.cpp
@@ -6,35 +6,33 @@
 
 #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/render/cpdf_dibsource.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()
-    : m_MatteColor(0),
-      m_bCached(false),
-      m_pCache(nullptr),
-      m_pImageObject(nullptr) {}
+CPDF_ImageLoader::CPDF_ImageLoader() = default;
 
-CPDF_ImageLoader::~CPDF_ImageLoader() {}
+CPDF_ImageLoader::~CPDF_ImageLoader() = default;
 
-bool CPDF_ImageLoader::Start(const CPDF_ImageObject* pImage,
+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 = const_cast<CPDF_ImageObject*>(pImage);
+  m_pImageObject = pImage;
   bool ret;
   if (pCache) {
     ret = pCache->StartGetCachedBitmap(m_pImageObject->GetImage(), bStdCS,
                                        GroupFamily, bLoadMask, pRenderStatus);
   } else {
-    ret = m_pImageObject->GetImage()->StartLoadDIBSource(
+    ret = m_pImageObject->GetImage()->StartLoadDIBBase(
         pRenderStatus->GetFormResource(), pRenderStatus->GetPageResource(),
         bStdCS, GroupFamily, bLoadMask);
   }
@@ -43,7 +41,7 @@
   return ret;
 }
 
-bool CPDF_ImageLoader::Continue(IFX_PauseIndicator* pPause,
+bool CPDF_ImageLoader::Continue(PauseIndicatorIface* pPause,
                                 CPDF_RenderStatus* pRenderStatus) {
   bool ret = m_pCache ? m_pCache->Continue(pPause, pRenderStatus)
                       : m_pImageObject->GetImage()->Continue(pPause);
@@ -52,6 +50,18 @@
   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();
diff --git a/core/fpdfapi/render/cpdf_imageloader.h b/core/fpdfapi/render/cpdf_imageloader.h
index 497ef15..7e12c01 100644
--- a/core/fpdfapi/render/cpdf_imageloader.h
+++ b/core/fpdfapi/render/cpdf_imageloader.h
@@ -7,38 +7,43 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_
 #define CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_
 
-#include <memory>
-
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
 
+class CFX_DIBBase;
 class CPDF_ImageObject;
 class CPDF_PageRenderCache;
 class CPDF_RenderStatus;
-class IFX_PauseIndicator;
+class CPDF_TransferFunc;
+class PauseIndicatorIface;
 
 class CPDF_ImageLoader {
  public:
   CPDF_ImageLoader();
   ~CPDF_ImageLoader();
 
-  bool Start(const CPDF_ImageObject* pImage,
+  bool Start(CPDF_ImageObject* pImage,
              CPDF_PageRenderCache* pCache,
              bool bStdCS,
              uint32_t GroupFamily,
              bool bLoadMask,
              CPDF_RenderStatus* pRenderStatus);
-  bool Continue(IFX_PauseIndicator* pPause, CPDF_RenderStatus* pRenderStatus);
+  bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus);
 
-  RetainPtr<CFX_DIBSource> m_pBitmap;
-  RetainPtr<CFX_DIBSource> m_pMask;
-  uint32_t m_MatteColor;
-  bool m_bCached;
+  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;
 };
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.cpp b/core/fpdfapi/render/cpdf_imagerenderer.cpp
index 1351597..2d3ec4c 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.cpp
+++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp
@@ -9,28 +9,29 @@
 #include <algorithm>
 #include <memory>
 
+#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_imageobject.h"
+#include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.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/render/cpdf_dibsource.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/fpdfapi/render/cpdf_transferfunc.h"
-#include "core/fpdfdoc/cpdf_occontext.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/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/cfx_dibsource.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
 #include "third_party/base/ptr_util.h"
@@ -40,109 +41,104 @@
 #include "core/fxge/skia/fx_skia_device.h"
 #endif
 
-CPDF_ImageRenderer::CPDF_ImageRenderer()
-    : m_pRenderStatus(nullptr),
-      m_pImageObject(nullptr),
-      m_Status(0),
-      m_pObj2Device(nullptr),
-      m_bPatternColor(false),
-      m_pPattern(nullptr),
-      m_bStdCS(false),
-      m_BlendType(FXDIB_BLEND_NORMAL),
-      m_Result(true) {}
+namespace {
 
-CPDF_ImageRenderer::~CPDF_ImageRenderer() {}
+bool IsImageValueTooBig(int val) {
+  // Likely large enough for any real rendering need, but sufficiently small
+  // that operations like val1 + val2 or -val will not overflow.
+  constexpr int kLimit = 256 * 1024 * 1024;
+  FX_SAFE_INT32 safe_val = val;
+  safe_val = safe_val.Abs();
+  return safe_val.ValueOrDefault(kLimit) >= kLimit;
+}
 
-bool CPDF_ImageRenderer::StartLoadDIBSource() {
-  CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
-  FX_RECT image_rect = image_rect_f.GetOuterRect();
-  if (!image_rect.Valid())
+}  // namespace
+
+CPDF_ImageRenderer::CPDF_ImageRenderer() = default;
+
+CPDF_ImageRenderer::~CPDF_ImageRenderer() = default;
+
+bool CPDF_ImageRenderer::StartLoadDIBBase() {
+  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_Status = 4;
+    m_Mode = Mode::kDefault;
     return true;
   }
   return false;
 }
 
-bool CPDF_ImageRenderer::StartRenderDIBSource() {
-  if (!m_Loader.m_pBitmap)
+bool CPDF_ImageRenderer::StartRenderDIBBase() {
+  if (!m_Loader.GetBitmap())
     return false;
 
   CPDF_GeneralState& state = m_pImageObject->m_GeneralState;
-  m_BitmapAlpha = FXSYS_round(255 * state.GetFillAlpha());
-  m_pDIBSource = m_Loader.m_pBitmap;
-  if (m_pRenderStatus->GetRenderOptions()->ColorModeIs(
-          CPDF_RenderOptions::kAlpha) &&
-      !m_Loader.m_pMask) {
+  m_BitmapAlpha = FXSYS_roundf(255 * state.GetFillAlpha());
+  m_pDIBBase = m_Loader.GetBitmap();
+  if (GetRenderOptions().ColorModeIs(CPDF_RenderOptions::kAlpha) &&
+      !m_Loader.GetMask()) {
     return StartBitmapAlpha();
   }
   if (state.GetTR()) {
     if (!state.GetTransferFunc())
       state.SetTransferFunc(m_pRenderStatus->GetTransferFunc(state.GetTR()));
 
-    if (state.GetTransferFunc() && !state.GetTransferFunc()->GetIdentity()) {
-      m_pDIBSource = m_Loader.m_pBitmap =
-          state.GetTransferFunc()->TranslateImage(m_Loader.m_pBitmap);
-      if (m_Loader.m_bCached && m_Loader.m_pMask)
-        m_Loader.m_pMask = m_Loader.m_pMask->Clone(nullptr);
-      m_Loader.m_bCached = false;
-    }
+    if (state.GetTransferFunc() && !state.GetTransferFunc()->GetIdentity())
+      m_pDIBBase = m_Loader.TranslateImage(state.GetTransferFunc());
   }
   m_FillArgb = 0;
   m_bPatternColor = false;
   m_pPattern = nullptr;
-  if (m_pDIBSource->IsAlphaMask()) {
+  if (m_pDIBBase->IsAlphaMask()) {
     const CPDF_Color* pColor = m_pImageObject->m_ColorState.GetFillColor();
     if (pColor && pColor->IsPattern()) {
-      m_pPattern = pColor->GetPattern();
+      m_pPattern.Reset(pColor->GetPattern());
       if (m_pPattern)
         m_bPatternColor = true;
     }
     m_FillArgb = m_pRenderStatus->GetFillArgb(m_pImageObject.Get());
-  } else if (m_pRenderStatus->GetRenderOptions()->ColorModeIs(
-                 CPDF_RenderOptions::kGray)) {
-    RetainPtr<CFX_DIBitmap> pClone = m_pDIBSource->Clone(nullptr);
+  } else if (GetRenderOptions().ColorModeIs(CPDF_RenderOptions::kGray)) {
+    RetainPtr<CFX_DIBitmap> pClone = m_pDIBBase->Clone(nullptr);
     if (!pClone)
       return false;
 
     pClone->ConvertColorScale(0xffffff, 0);
-    m_pDIBSource = pClone;
+    m_pDIBBase = pClone;
   }
-  m_Flags = 0;
-  if (m_pRenderStatus->GetRenderOptions()->HasFlag(RENDER_FORCE_DOWNSAMPLE))
-    m_Flags |= RENDER_FORCE_DOWNSAMPLE;
-  else if (m_pRenderStatus->GetRenderOptions()->HasFlag(RENDER_FORCE_HALFTONE))
-    m_Flags |= RENDER_FORCE_HALFTONE;
+  m_ResampleOptions = FXDIB_ResampleOptions();
+  if (GetRenderOptions().GetOptions().bForceHalftone)
+    m_ResampleOptions.bHalftone = true;
 
-  if (m_pRenderStatus->GetRenderDevice()->GetDeviceClass() != FXDC_DISPLAY)
+  if (m_pRenderStatus->GetRenderDevice()->GetDeviceType() !=
+      DeviceType::kDisplay) {
     HandleFilters();
+  }
 
-  if (m_pRenderStatus->GetRenderOptions()->HasFlag(RENDER_NOIMAGESMOOTH))
-    m_Flags |= FXDIB_NOSMOOTH;
+  if (GetRenderOptions().GetOptions().bNoImageSmooth)
+    m_ResampleOptions.bNoSmoothing = true;
   else if (m_pImageObject->GetImage()->IsInterpol())
-    m_Flags |= FXDIB_INTERPOL;
+    m_ResampleOptions.bInterpolateBilinear = true;
 
-  if (m_Loader.m_pMask)
+  if (m_Loader.GetMask())
     return DrawMaskedImage();
 
   if (m_bPatternColor)
-    return DrawPatternImage(m_pObj2Device.Get());
+    return DrawPatternImage();
 
   if (m_BitmapAlpha != 255 || !state.HasRef() || !state.GetFillOP() ||
-      state.GetOPMode() != 0 || state.GetBlendType() != FXDIB_BLEND_NORMAL ||
+      state.GetOPMode() != 0 || state.GetBlendType() != BlendMode::kNormal ||
       state.GetStrokeAlpha() != 1.0f || state.GetFillAlpha() != 1.0f) {
-    return StartDIBSource();
+    return StartDIBBase();
   }
   CPDF_Document* pDocument = nullptr;
   CPDF_Page* pPage = nullptr;
   if (auto* pPageCache = m_pRenderStatus->GetContext()->GetPageCache()) {
     pPage = pPageCache->GetPage();
-    pDocument = pPage->m_pDocument.Get();
+    pDocument = pPage->GetDocument();
   } else {
     pDocument = m_pImageObject->GetImage()->GetDocument();
   }
@@ -151,60 +147,58 @@
   CPDF_Object* pCSObj =
       m_pImageObject->GetImage()->GetStream()->GetDict()->GetDirectObjectFor(
           "ColorSpace");
-  CPDF_ColorSpace* pColorSpace =
-      pDocument->LoadColorSpace(pCSObj, pPageResources);
-  if (!pColorSpace)
-    return StartDIBSource();
-  int format = pColorSpace->GetFamily();
-  if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
-      format == PDFCS_DEVICEN) {
-    m_BlendType = FXDIB_BLEND_DARKEN;
+  auto* pData = CPDF_DocPageData::FromDocument(pDocument);
+  RetainPtr<CPDF_ColorSpace> pColorSpace =
+      pData->GetColorSpace(pCSObj, pPageResources);
+  if (pColorSpace) {
+    int format = pColorSpace->GetFamily();
+    if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
+        format == PDFCS_DEVICEN) {
+      m_BlendType = BlendMode::kDarken;
+    }
   }
-  pDocument->GetPageData()->ReleaseColorSpace(pCSObj);
-  return StartDIBSource();
+  return StartDIBBase();
 }
 
 bool CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus,
                                CPDF_ImageObject* pImageObject,
-                               const CFX_Matrix* pObj2Device,
+                               const CFX_Matrix& mtObj2Device,
                                bool bStdCS,
-                               int blendType) {
+                               BlendMode blendType) {
   ASSERT(pImageObject);
   m_pRenderStatus = pStatus;
   m_bStdCS = bStdCS;
   m_pImageObject = pImageObject;
   m_BlendType = blendType;
-  m_pObj2Device = pObj2Device;
-  CPDF_Dictionary* pOC = m_pImageObject->GetImage()->GetOC();
-  if (pOC && m_pRenderStatus->GetRenderOptions()->GetOCContext() &&
-      !m_pRenderStatus->GetRenderOptions()->GetOCContext()->CheckOCGVisible(
-          pOC)) {
+  m_mtObj2Device = mtObj2Device;
+  const CPDF_Dictionary* pOC = m_pImageObject->GetImage()->GetOC();
+  if (pOC && GetRenderOptions().GetOCContext() &&
+      !GetRenderOptions().GetOCContext()->CheckOCGVisible(pOC)) {
     return false;
   }
-  m_ImageMatrix = m_pImageObject->matrix();
-  m_ImageMatrix.Concat(*pObj2Device);
-  if (StartLoadDIBSource())
+  m_ImageMatrix = m_pImageObject->matrix() * mtObj2Device;
+  if (StartLoadDIBBase())
     return true;
-  return StartRenderDIBSource();
+  return StartRenderDIBBase();
 }
 
 bool CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus,
-                               const RetainPtr<CFX_DIBSource>& pDIBSource,
+                               const RetainPtr<CFX_DIBBase>& pDIBBase,
                                FX_ARGB bitmap_argb,
                                int bitmap_alpha,
-                               const CFX_Matrix* pImage2Device,
-                               uint32_t flags,
+                               const CFX_Matrix& mtImage2Device,
+                               const FXDIB_ResampleOptions& options,
                                bool bStdCS,
-                               int blendType) {
+                               BlendMode blendType) {
   m_pRenderStatus = pStatus;
-  m_pDIBSource = pDIBSource;
+  m_pDIBBase = pDIBBase;
   m_FillArgb = bitmap_argb;
   m_BitmapAlpha = bitmap_alpha;
-  m_ImageMatrix = *pImage2Device;
-  m_Flags = flags;
+  m_ImageMatrix = mtImage2Device;
+  m_ResampleOptions = options;
   m_bStdCS = bStdCS;
   m_BlendType = blendType;
-  return StartDIBSource();
+  return StartDIBBase();
 }
 
 bool CPDF_ImageRenderer::NotDrawing() const {
@@ -228,26 +222,27 @@
 void CPDF_ImageRenderer::CalculateDrawImage(
     CFX_DefaultRenderDevice* pBitmapDevice1,
     CFX_DefaultRenderDevice* pBitmapDevice2,
-    const RetainPtr<CFX_DIBSource>& pDIBSource,
-    CFX_Matrix* pNewMatrix,
+    const RetainPtr<CFX_DIBBase>& pDIBBase,
+    const CFX_Matrix& mtNewMatrix,
     const FX_RECT& rect) const {
-  CPDF_RenderStatus bitmap_render;
-  bitmap_render.Initialize(m_pRenderStatus->GetContext(), pBitmapDevice2,
-                           nullptr, nullptr, nullptr, nullptr, nullptr, 0,
-                           m_pRenderStatus->GetDropObjects(), nullptr, true);
+  CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(),
+                                  pBitmapDevice2);
+  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, pDIBSource, 0xffffffff, 255,
-                         pNewMatrix, m_Flags, true, FXDIB_BLEND_NORMAL)) {
+  if (image_render.Start(&bitmap_render, pDIBBase, 0xffffffff, 255, mtNewMatrix,
+                         m_ResampleOptions, true, BlendMode::kNormal)) {
     image_render.Continue(nullptr);
   }
-  if (m_Loader.m_MatteColor == 0xffffffff)
+  if (m_Loader.MatteColor() == 0xffffffff)
     return;
-  int matte_r = FXARGB_R(m_Loader.m_MatteColor);
-  int matte_g = FXARGB_G(m_Loader.m_MatteColor);
-  int matte_b = FXARGB_B(m_Loader.m_MatteColor);
+  int matte_r = FXARGB_R(m_Loader.MatteColor());
+  int matte_g = FXARGB_G(m_Loader.MatteColor());
+  int matte_b = FXARGB_B(m_Loader.MatteColor());
   for (int row = 0; row < rect.Height(); row++) {
-    uint8_t* dest_scan =
-        const_cast<uint8_t*>(pBitmapDevice1->GetBitmap()->GetScanline(row));
+    uint8_t* dest_scan = pBitmapDevice1->GetBitmap()->GetWritableScanline(row);
     const uint8_t* mask_scan = pBitmapDevice2->GetBitmap()->GetScanline(row);
     for (int col = 0; col < rect.Width(); col++) {
       int alpha = *mask_scan++;
@@ -266,7 +261,11 @@
   }
 }
 
-bool CPDF_ImageRenderer::DrawPatternImage(const CFX_Matrix* pObj2Device) {
+const CPDF_RenderOptions& CPDF_ImageRenderer::GetRenderOptions() const {
+  return m_pRenderStatus->GetRenderOptions();
+}
+
+bool CPDF_ImageRenderer::DrawPatternImage() {
   if (NotDrawing()) {
     m_Result = false;
     return false;
@@ -282,21 +281,24 @@
     return true;
 
   bitmap_device1.GetBitmap()->Clear(0xffffff);
-  CPDF_RenderStatus bitmap_render;
-  bitmap_render.Initialize(m_pRenderStatus->GetContext(), &bitmap_device1,
-                           nullptr, nullptr, nullptr, nullptr,
-                           m_pRenderStatus->GetRenderOptions(), 0,
-                           m_pRenderStatus->GetDropObjects(), nullptr, true);
-  CFX_Matrix patternDevice = *pObj2Device;
+
+  CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(),
+                                  &bitmap_device1);
+  bitmap_render.SetOptions(GetRenderOptions());
+  bitmap_render.SetDropObjects(m_pRenderStatus->GetDropObjects());
+  bitmap_render.SetStdCS(true);
+  bitmap_render.Initialize(nullptr, nullptr);
+
+  CFX_Matrix patternDevice = m_mtObj2Device;
   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(),
-                                    &patternDevice, false);
+                                    patternDevice, false);
   } else if (CPDF_ShadingPattern* pShadingPattern =
                  m_pPattern->AsShadingPattern()) {
     bitmap_render.DrawShadingPattern(pShadingPattern, m_pImageObject.Get(),
-                                     &patternDevice, false);
+                                     patternDevice, false);
   }
 
   CFX_DefaultRenderDevice bitmap_device2;
@@ -305,8 +307,8 @@
     return true;
   }
   bitmap_device2.GetBitmap()->Clear(0);
-  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pDIBSource,
-                     &new_matrix, rect);
+  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pDIBBase, new_matrix,
+                     rect);
   bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask);
   bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
   bitmap_device1.GetBitmap()->MultiplyAlpha(255);
@@ -335,13 +337,14 @@
 #else
   bitmap_device1.GetBitmap()->Clear(0xffffff);
 #endif
-  CPDF_RenderStatus bitmap_render;
-  bitmap_render.Initialize(m_pRenderStatus->GetContext(), &bitmap_device1,
-                           nullptr, nullptr, nullptr, nullptr, nullptr, 0,
-                           m_pRenderStatus->GetDropObjects(), nullptr, true);
+  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_pDIBSource, 0, 255, &new_matrix,
-                         m_Flags, true, FXDIB_BLEND_NORMAL)) {
+  if (image_render.Start(&bitmap_render, m_pDIBBase, 0, 255, new_matrix,
+                         m_ResampleOptions, true, BlendMode::kNormal)) {
     image_render.Continue(nullptr);
   }
   CFX_DefaultRenderDevice bitmap_device2;
@@ -354,8 +357,8 @@
 #else
   bitmap_device2.GetBitmap()->Clear(0);
 #endif
-  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_Loader.m_pMask,
-                     &new_matrix, rect);
+  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_Loader.GetMask(),
+                     new_matrix, rect);
 #ifdef _SKIA_SUPPORT_
   m_pRenderStatus->GetRenderDevice()->SetBitsWithMask(
       bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left,
@@ -371,48 +374,45 @@
   return false;
 }
 
-bool CPDF_ImageRenderer::StartDIBSource() {
-  if (!(m_Flags & RENDER_FORCE_DOWNSAMPLE) && m_pDIBSource->GetBPP() > 1) {
-    FX_SAFE_SIZE_T image_size = m_pDIBSource->GetBPP();
+bool CPDF_ImageRenderer::StartDIBBase() {
+  if (m_pDIBBase->GetBPP() > 1) {
+    FX_SAFE_SIZE_T image_size = m_pDIBBase->GetBPP();
     image_size /= 8;
-    image_size *= m_pDIBSource->GetWidth();
-    image_size *= m_pDIBSource->GetHeight();
+    image_size *= m_pDIBBase->GetWidth();
+    image_size *= m_pDIBBase->GetHeight();
     if (!image_size.IsValid())
       return false;
 
-    if (image_size.ValueOrDie() > FPDF_HUGE_IMAGE_SIZE &&
-        !(m_Flags & RENDER_FORCE_HALFTONE)) {
-      m_Flags |= RENDER_FORCE_DOWNSAMPLE;
+    if (image_size.ValueOrDie() > kHugeImageSize &&
+        !m_ResampleOptions.bHalftone) {
+      m_ResampleOptions.bInterpolateBilinear = true;
     }
   }
 #ifdef _SKIA_SUPPORT_
-  RetainPtr<CFX_DIBitmap> premultiplied = m_pDIBSource->Clone(nullptr);
-  if (m_pDIBSource->HasAlpha())
+  RetainPtr<CFX_DIBitmap> premultiplied = m_pDIBBase->Clone(nullptr);
+  if (m_pDIBBase->HasAlpha())
     CFX_SkiaDeviceDriver::PreMultiply(premultiplied);
   if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend(
-          premultiplied, m_BitmapAlpha, m_FillArgb, &m_ImageMatrix, m_Flags,
-          &m_DeviceHandle, m_BlendType)) {
+          premultiplied, m_BitmapAlpha, m_FillArgb, m_ImageMatrix,
+          m_ResampleOptions, &m_DeviceHandle, m_BlendType)) {
     if (m_DeviceHandle) {
-      m_Status = 3;
+      m_Mode = Mode::kBlend;
       return true;
     }
     return false;
   }
 #else
   if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend(
-          m_pDIBSource, m_BitmapAlpha, m_FillArgb, &m_ImageMatrix, m_Flags,
-          &m_DeviceHandle, m_BlendType)) {
+          m_pDIBBase, m_BitmapAlpha, m_FillArgb, m_ImageMatrix,
+          m_ResampleOptions, &m_DeviceHandle, m_BlendType)) {
     if (m_DeviceHandle) {
-      m_Status = 3;
+      m_Mode = Mode::kBlend;
       return true;
     }
     return false;
   }
 #endif
-  CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
-  FX_RECT image_rect = image_rect_f.GetOuterRect();
-  int dest_width = image_rect.Width();
-  int dest_height = image_rect.Height();
+
   if ((fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) ||
       (fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) {
     if (NotDrawing()) {
@@ -420,34 +420,44 @@
       return false;
     }
 
+    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);
-    m_Status = 2;
+    clip_box.Intersect(image_rect.value());
+    m_Mode = Mode::kTransform;
     m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
-        m_pDIBSource, &m_ImageMatrix, m_Flags, &clip_box);
+        m_pDIBBase, m_ImageMatrix, m_ResampleOptions, &clip_box);
     return true;
   }
-  if (m_ImageMatrix.a < 0)
-    dest_width = -dest_width;
 
-  if (m_ImageMatrix.d > 0)
-    dest_height = -dest_height;
+  Optional<FX_RECT> image_rect = GetUnitRect();
+  if (!image_rect.has_value())
+    return false;
 
-  int dest_left = dest_width > 0 ? image_rect.left : image_rect.right;
-  int dest_top = dest_height > 0 ? image_rect.top : image_rect.bottom;
-  if (m_pDIBSource->IsOpaqueImage() && m_BitmapAlpha == 255) {
+  int dest_left;
+  int dest_top;
+  int dest_width;
+  int dest_height;
+  if (!GetDimensionsFromUnitRect(image_rect.value(), &dest_left, &dest_top,
+                                 &dest_width, &dest_height)) {
+    return false;
+  }
+
+  if (m_pDIBBase->IsOpaqueImage() && m_BitmapAlpha == 255) {
     if (m_pRenderStatus->GetRenderDevice()->StretchDIBitsWithFlagsAndBlend(
-            m_pDIBSource, dest_left, dest_top, dest_width, dest_height, m_Flags,
-            m_BlendType)) {
+            m_pDIBBase, dest_left, dest_top, dest_width, dest_height,
+            m_ResampleOptions, m_BlendType)) {
       return false;
     }
   }
-  if (m_pDIBSource->IsAlphaMask()) {
+  if (m_pDIBBase->IsAlphaMask()) {
     if (m_BitmapAlpha != 255)
       m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha);
     if (m_pRenderStatus->GetRenderDevice()->StretchBitMaskWithFlags(
-            m_pDIBSource, dest_left, dest_top, dest_width, dest_height,
-            m_FillArgb, m_Flags)) {
+            m_pDIBBase, dest_left, dest_top, dest_width, dest_height,
+            m_FillArgb, m_ResampleOptions)) {
       return false;
     }
   }
@@ -458,42 +468,42 @@
 
   FX_RECT clip_box = m_pRenderStatus->GetRenderDevice()->GetClipBox();
   FX_RECT dest_rect = clip_box;
-  dest_rect.Intersect(image_rect);
+  dest_rect.Intersect(image_rect.value());
   FX_RECT dest_clip(
-      dest_rect.left - image_rect.left, dest_rect.top - image_rect.top,
-      dest_rect.right - image_rect.left, dest_rect.bottom - image_rect.top);
-  RetainPtr<CFX_DIBitmap> pStretched =
-      m_pDIBSource->StretchTo(dest_width, dest_height, m_Flags, &dest_clip);
+      dest_rect.left - image_rect->left, dest_rect.top - image_rect->top,
+      dest_rect.right - image_rect->left, dest_rect.bottom - image_rect->top);
+  RetainPtr<CFX_DIBitmap> pStretched = m_pDIBBase->StretchTo(
+      dest_width, dest_height, m_ResampleOptions, &dest_clip);
   if (pStretched) {
     m_pRenderStatus->CompositeDIBitmap(pStretched, dest_rect.left,
                                        dest_rect.top, m_FillArgb, m_BitmapAlpha,
-                                       m_BlendType, false);
+                                       m_BlendType, CPDF_Transparency());
   }
   return false;
 }
 
 bool CPDF_ImageRenderer::StartBitmapAlpha() {
-  if (m_pDIBSource->IsOpaqueImage()) {
+  if (m_pDIBBase->IsOpaqueImage()) {
     CFX_PathData path;
     path.AppendRect(0, 0, 1, 1);
-    path.Transform(&m_ImageMatrix);
+    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);
     return false;
   }
-  RetainPtr<CFX_DIBSource> pAlphaMask;
-  if (m_pDIBSource->IsAlphaMask())
-    pAlphaMask = m_pDIBSource;
+  RetainPtr<CFX_DIBBase> pAlphaMask;
+  if (m_pDIBBase->IsAlphaMask())
+    pAlphaMask = m_pDIBBase;
   else
-    pAlphaMask = m_pDIBSource->CloneAlphaMask();
+    pAlphaMask = m_pDIBBase->CloneAlphaMask();
 
   if (fabs(m_ImageMatrix.b) >= 0.5f || fabs(m_ImageMatrix.c) >= 0.5f) {
     int left;
     int top;
     RetainPtr<CFX_DIBitmap> pTransformed =
-        pAlphaMask->TransformTo(&m_ImageMatrix, &left, &top);
+        pAlphaMask->TransformTo(m_ImageMatrix, &left, &top);
     if (!pTransformed)
       return true;
 
@@ -502,55 +512,79 @@
         ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha));
     return false;
   }
-  CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
-  FX_RECT image_rect = image_rect_f.GetOuterRect();
-  int dest_width =
-      m_ImageMatrix.a > 0 ? image_rect.Width() : -image_rect.Width();
-  int dest_height =
-      m_ImageMatrix.d > 0 ? -image_rect.Height() : image_rect.Height();
-  int left = dest_width > 0 ? image_rect.left : image_rect.right;
-  int top = dest_height > 0 ? image_rect.top : image_rect.bottom;
+
+  Optional<FX_RECT> image_rect = GetUnitRect();
+  if (!image_rect.has_value())
+    return false;
+
+  int left;
+  int top;
+  int dest_width;
+  int dest_height;
+  if (!GetDimensionsFromUnitRect(image_rect.value(), &left, &top, &dest_width,
+                                 &dest_height)) {
+    return false;
+  }
+
   m_pRenderStatus->GetRenderDevice()->StretchBitMask(
       pAlphaMask, left, top, dest_width, dest_height,
       ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha));
   return false;
 }
 
-bool CPDF_ImageRenderer::Continue(IFX_PauseIndicator* pPause) {
-  if (m_Status == 2) {
-    if (m_pTransformer->Continue(pPause))
-      return true;
-
-    RetainPtr<CFX_DIBitmap> pBitmap = m_pTransformer->DetachBitmap();
-    if (!pBitmap)
+bool CPDF_ImageRenderer::Continue(PauseIndicatorIface* pPause) {
+  switch (m_Mode) {
+    case Mode::kNone:
       return false;
+    case Mode::kDefault:
+      return ContinueDefault(pPause);
+    case Mode::kBlend:
+      return ContinueBlend(pPause);
+    case Mode::kTransform:
+      return ContinueTransform(pPause);
+  }
+  NOTREACHED();
+  return false;
+}
 
-    if (pBitmap->IsAlphaMask()) {
-      if (m_BitmapAlpha != 255)
-        m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha);
-      m_Result = m_pRenderStatus->GetRenderDevice()->SetBitMask(
-          pBitmap, m_pTransformer->result().left, m_pTransformer->result().top,
-          m_FillArgb);
-    } else {
-      if (m_BitmapAlpha != 255)
-        pBitmap->MultiplyAlpha(m_BitmapAlpha);
-      m_Result = m_pRenderStatus->GetRenderDevice()->SetDIBitsWithBlend(
-          pBitmap, m_pTransformer->result().left, m_pTransformer->result().top,
-          m_BlendType);
-    }
+bool CPDF_ImageRenderer::ContinueDefault(PauseIndicatorIface* pPause) {
+  if (m_Loader.Continue(pPause, m_pRenderStatus.Get()))
+    return true;
+
+  if (!StartRenderDIBBase())
     return false;
-  }
-  if (m_Status == 3) {
-    return m_pRenderStatus->GetRenderDevice()->ContinueDIBits(
-        m_DeviceHandle.get(), pPause);
-  }
 
-  if (m_Status == 4) {
-    if (m_Loader.Continue(pPause, m_pRenderStatus.Get()))
-      return true;
+  if (m_Mode == Mode::kDefault)
+    return false;
 
-    if (StartRenderDIBSource())
-      return Continue(pPause);
+  return Continue(pPause);
+}
+
+bool CPDF_ImageRenderer::ContinueBlend(PauseIndicatorIface* pPause) {
+  return m_pRenderStatus->GetRenderDevice()->ContinueDIBits(
+      m_DeviceHandle.get(), pPause);
+}
+
+bool CPDF_ImageRenderer::ContinueTransform(PauseIndicatorIface* pPause) {
+  if (m_pTransformer->Continue(pPause))
+    return true;
+
+  RetainPtr<CFX_DIBitmap> pBitmap = m_pTransformer->DetachBitmap();
+  if (!pBitmap)
+    return false;
+
+  if (pBitmap->IsAlphaMask()) {
+    if (m_BitmapAlpha != 255)
+      m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha);
+    m_Result = m_pRenderStatus->GetRenderDevice()->SetBitMask(
+        pBitmap, m_pTransformer->result().left, m_pTransformer->result().top,
+        m_FillArgb);
+  } else {
+    if (m_BitmapAlpha != 255)
+      pBitmap->MultiplyAlpha(m_BitmapAlpha);
+    m_Result = m_pRenderStatus->GetRenderDevice()->SetDIBitsWithBlend(
+        pBitmap, m_pTransformer->result().left, m_pTransformer->result().top,
+        m_BlendType);
   }
   return false;
 }
@@ -565,7 +599,7 @@
   if (pFilters->IsName()) {
     ByteString bsDecodeType = pFilters->GetString();
     if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode")
-      m_Flags |= FXRENDER_IMAGE_LOSSY;
+      m_ResampleOptions.bLossy = true;
     return;
   }
 
@@ -573,11 +607,49 @@
   if (!pArray)
     return;
 
-  for (size_t i = 0; i < pArray->GetCount(); i++) {
+  for (size_t i = 0; i < pArray->size(); i++) {
     ByteString bsDecodeType = pArray->GetStringAt(i);
     if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode") {
-      m_Flags |= FXRENDER_IMAGE_LOSSY;
+      m_ResampleOptions.bLossy = true;
       break;
     }
   }
 }
+
+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 image_rect;
+}
+
+bool CPDF_ImageRenderer::GetDimensionsFromUnitRect(const FX_RECT& rect,
+                                                   int* left,
+                                                   int* top,
+                                                   int* width,
+                                                   int* height) const {
+  ASSERT(rect.Valid());
+
+  int dest_width = rect.Width();
+  int dest_height = rect.Height();
+  if (IsImageValueTooBig(dest_width) || IsImageValueTooBig(dest_height))
+    return false;
+
+  if (m_ImageMatrix.a < 0)
+    dest_width = -dest_width;
+
+  if (m_ImageMatrix.d > 0)
+    dest_height = -dest_height;
+
+  int dest_left = dest_width > 0 ? rect.left : rect.right;
+  int dest_top = dest_height > 0 ? rect.top : rect.bottom;
+  if (IsImageValueTooBig(dest_left) || IsImageValueTooBig(dest_top))
+    return false;
+
+  *left = dest_left;
+  *top = dest_top;
+  *width = dest_width;
+  *height = dest_height;
+  return true;
+}
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.h b/core/fpdfapi/render/cpdf_imagerenderer.h
index a5d9ba7..d7c7bf0 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.h
+++ b/core/fpdfapi/render/cpdf_imagerenderer.h
@@ -13,14 +13,17 @@
 #include "core/fxcrt/fx_coordinates.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"
 
 class CFX_DIBitmap;
-class CFX_DIBSource;
+class CFX_DIBBase;
 class CFX_DefaultRenderDevice;
 class CFX_ImageTransformer;
 class CPDF_ImageObject;
 class CPDF_PageObject;
 class CPDF_Pattern;
+class CPDF_RenderOptions;
 class CPDF_RenderStatus;
 
 class CPDF_ImageRenderer {
@@ -30,56 +33,73 @@
 
   bool Start(CPDF_RenderStatus* pStatus,
              CPDF_ImageObject* pImageObject,
-             const CFX_Matrix* pObj2Device,
+             const CFX_Matrix& mtObj2Device,
              bool bStdCS,
-             int blendType);
+             BlendMode blendType);
 
   bool Start(CPDF_RenderStatus* pStatus,
-             const RetainPtr<CFX_DIBSource>& pDIBSource,
+             const RetainPtr<CFX_DIBBase>& pDIBBase,
              FX_ARGB bitmap_argb,
              int bitmap_alpha,
-             const CFX_Matrix* pImage2Device,
-             uint32_t flags,
+             const CFX_Matrix& mtImage2Device,
+             const FXDIB_ResampleOptions& options,
              bool bStdCS,
-             int blendType);
+             BlendMode blendType);
 
-  bool Continue(IFX_PauseIndicator* pPause);
+  bool Continue(PauseIndicatorIface* pPause);
   bool GetResult() const { return m_Result; }
 
  private:
+  enum class Mode {
+    kNone = 0,
+    kDefault,
+    kBlend,
+    kTransform,
+  };
+
   bool StartBitmapAlpha();
-  bool StartDIBSource();
-  bool StartRenderDIBSource();
-  bool StartLoadDIBSource();
+  bool StartDIBBase();
+  bool StartRenderDIBBase();
+  bool StartLoadDIBBase();
+  bool ContinueDefault(PauseIndicatorIface* pPause);
+  bool ContinueBlend(PauseIndicatorIface* pPause);
+  bool ContinueTransform(PauseIndicatorIface* pPause);
   bool DrawMaskedImage();
-  bool DrawPatternImage(const CFX_Matrix* pObj2Device);
+  bool DrawPatternImage();
   bool NotDrawing() const;
   FX_RECT GetDrawRect() const;
   CFX_Matrix GetDrawMatrix(const FX_RECT& rect) const;
-  void CalculateDrawImage(CFX_DefaultRenderDevice* bitmap_device1,
-                          CFX_DefaultRenderDevice* bitmap_device2,
-                          const RetainPtr<CFX_DIBSource>& pDIBSource,
-                          CFX_Matrix* pNewMatrix,
+  void CalculateDrawImage(CFX_DefaultRenderDevice* pBitmapDevice1,
+                          CFX_DefaultRenderDevice* pBitmapDevice2,
+                          const 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;
+  bool GetDimensionsFromUnitRect(const FX_RECT& rect,
+                                 int* left,
+                                 int* top,
+                                 int* width,
+                                 int* height) const;
 
   UnownedPtr<CPDF_RenderStatus> m_pRenderStatus;
   UnownedPtr<CPDF_ImageObject> m_pImageObject;
-  int m_Status;
-  UnownedPtr<const CFX_Matrix> m_pObj2Device;
+  RetainPtr<CPDF_Pattern> m_pPattern;
+  RetainPtr<CFX_DIBBase> m_pDIBBase;
+  CFX_Matrix m_mtObj2Device;
   CFX_Matrix m_ImageMatrix;
   CPDF_ImageLoader m_Loader;
-  RetainPtr<CFX_DIBSource> m_pDIBSource;
-  int m_BitmapAlpha;
-  bool m_bPatternColor;
-  UnownedPtr<CPDF_Pattern> m_pPattern;
-  FX_ARGB m_FillArgb;
-  uint32_t m_Flags;
   std::unique_ptr<CFX_ImageTransformer> m_pTransformer;
   std::unique_ptr<CFX_ImageRenderer> m_DeviceHandle;
-  bool m_bStdCS;
-  int m_BlendType;
-  bool m_Result;
+  Mode m_Mode = Mode::kNone;
+  int m_BitmapAlpha = 0;
+  BlendMode m_BlendType = BlendMode::kNormal;
+  FX_ARGB m_FillArgb = 0;
+  FXDIB_ResampleOptions m_ResampleOptions;
+  bool m_bPatternColor = false;
+  bool m_bStdCS = false;
+  bool m_Result = true;
 };
 
 #endif  // CORE_FPDFAPI_RENDER_CPDF_IMAGERENDERER_H_
diff --git a/core/fpdfapi/render/cpdf_pagerendercache.cpp b/core/fpdfapi/render/cpdf_pagerendercache.cpp
index 0d01589..ca4f8ee 100644
--- a/core/fpdfapi/render/cpdf_pagerendercache.cpp
+++ b/core/fpdfapi/render/cpdf_pagerendercache.cpp
@@ -13,6 +13,8 @@
 #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 {
 
@@ -27,17 +29,9 @@
 
 }  // namespace
 
-CPDF_PageRenderCache::CPDF_PageRenderCache(CPDF_Page* pPage)
-    : m_pPage(pPage),
-      m_pCurImageCacheEntry(nullptr),
-      m_nTimeCount(0),
-      m_nCacheSize(0),
-      m_bCurFindCache(false) {}
+CPDF_PageRenderCache::CPDF_PageRenderCache(CPDF_Page* pPage) : m_pPage(pPage) {}
 
-CPDF_PageRenderCache::~CPDF_PageRenderCache() {
-  for (const auto& it : m_ImageCache)
-    delete it.second;
-}
+CPDF_PageRenderCache::~CPDF_PageRenderCache() = default;
 
 void CPDF_PageRenderCache::CacheOptimization(int32_t dwLimitCacheSize) {
   if (m_nCacheSize <= (uint32_t)dwLimitCacheSize)
@@ -75,7 +69,6 @@
     return;
 
   m_nCacheSize -= it->second->EstimateSize();
-  delete it->second;
   m_ImageCache.erase(it);
 }
 
@@ -89,58 +82,52 @@
   const auto it = m_ImageCache.find(pStream);
   m_bCurFindCache = it != m_ImageCache.end();
   if (m_bCurFindCache) {
-    m_pCurImageCacheEntry = it->second;
+    m_pCurImageCacheEntry = it->second.get();
   } else {
-    m_pCurImageCacheEntry =
-        new CPDF_ImageCacheEntry(m_pPage->m_pDocument.Get(), pImage);
+    m_pCurImageCacheEntry = pdfium::MakeUnique<CPDF_ImageCacheEntry>(
+        m_pPage->GetDocument(), pImage);
   }
-  int ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
+  CPDF_DIB::LoadState ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
       pRenderStatus->GetFormResource(), m_pPage->m_pPageResources.Get(), bStdCS,
       GroupFamily, bLoadMask, pRenderStatus);
-  if (ret == 2)
+  if (ret == CPDF_DIB::LoadState::kContinue)
     return true;
 
   m_nTimeCount++;
   if (!m_bCurFindCache)
-    m_ImageCache[pStream] = m_pCurImageCacheEntry;
+    m_ImageCache[pStream] = m_pCurImageCacheEntry.Release();
 
-  if (!ret)
+  if (ret == CPDF_DIB::LoadState::kFail)
     m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
 
   return false;
 }
 
-bool CPDF_PageRenderCache::Continue(IFX_PauseIndicator* pPause,
+bool CPDF_PageRenderCache::Continue(PauseIndicatorIface* pPause,
                                     CPDF_RenderStatus* pRenderStatus) {
-  int ret = m_pCurImageCacheEntry->Continue(pPause, pRenderStatus);
-  if (ret == 2)
+  bool ret = m_pCurImageCacheEntry->Continue(pPause, pRenderStatus);
+  if (ret)
     return true;
 
   m_nTimeCount++;
   if (!m_bCurFindCache) {
     m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] =
-        m_pCurImageCacheEntry;
+        m_pCurImageCacheEntry.Release();
   }
-  if (!ret)
-    m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
+  m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
   return false;
 }
 
-void CPDF_PageRenderCache::ResetBitmap(const RetainPtr<CPDF_Image>& pImage,
-                                       const RetainPtr<CFX_DIBitmap>& pBitmap) {
+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()) {
-    if (!pBitmap)
-      return;
+  if (it == m_ImageCache.end())
+    return;
 
-    pEntry = new CPDF_ImageCacheEntry(m_pPage->m_pDocument.Get(), pImage);
-    m_ImageCache[pStream] = pEntry;
-  } else {
-    pEntry = it->second;
-  }
+  pEntry = it->second.get();
   m_nCacheSize -= pEntry->EstimateSize();
-  pEntry->Reset(pBitmap);
+  pEntry->Reset();
   m_nCacheSize += pEntry->EstimateSize();
 }
diff --git a/core/fpdfapi/render/cpdf_pagerendercache.h b/core/fpdfapi/render/cpdf_pagerendercache.h
index 58f0bb7..1fb2a61 100644
--- a/core/fpdfapi/render/cpdf_pagerendercache.h
+++ b/core/fpdfapi/render/cpdf_pagerendercache.h
@@ -8,31 +8,34 @@
 #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 CFX_DIBitmap;
 class CPDF_Image;
 class CPDF_ImageCacheEntry;
 class CPDF_Page;
 class CPDF_RenderStatus;
 class CPDF_Stream;
-class IFX_PauseIndicator;
+class PauseIndicatorIface;
 
-class CPDF_PageRenderCache {
+class CPDF_PageRenderCache : public CPDF_Page::RenderCacheIface {
  public:
   explicit CPDF_PageRenderCache(CPDF_Page* pPage);
-  ~CPDF_PageRenderCache();
+  ~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; }
-  void ResetBitmap(const RetainPtr<CPDF_Image>& pImage,
-                   const RetainPtr<CFX_DIBitmap>& pBitmap);
   CPDF_Page* GetPage() const { return m_pPage.Get(); }
   CPDF_ImageCacheEntry* GetCurImageCacheEntry() const {
-    return m_pCurImageCacheEntry;
+    return m_pCurImageCacheEntry.Get();
   }
 
   bool StartGetCachedBitmap(const RetainPtr<CPDF_Image>& pImage,
@@ -41,17 +44,17 @@
                             bool bLoadMask,
                             CPDF_RenderStatus* pRenderStatus);
 
-  bool Continue(IFX_PauseIndicator* pPause, CPDF_RenderStatus* pRenderStatus);
+  bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus);
 
  private:
   void ClearImageCacheEntry(CPDF_Stream* pStream);
 
   UnownedPtr<CPDF_Page> const m_pPage;
-  CPDF_ImageCacheEntry* m_pCurImageCacheEntry;
-  std::map<CPDF_Stream*, CPDF_ImageCacheEntry*> m_ImageCache;
-  uint32_t m_nTimeCount;
-  uint32_t m_nCacheSize;
-  bool m_bCurFindCache;
+  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
new file mode 100644
index 0000000..8dd66c7
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_pagerendercontext.cpp
@@ -0,0 +1,17 @@
+// 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_pagerendercontext.h"
+
+#include "core/fpdfapi/page/cpdf_occontext.h"
+#include "core/fpdfapi/render/cpdf_progressiverenderer.h"
+#include "core/fpdfapi/render/cpdf_rendercontext.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fxge/cfx_renderdevice.h"
+
+CPDF_PageRenderContext::CPDF_PageRenderContext() {}
+
+CPDF_PageRenderContext::~CPDF_PageRenderContext() {}
diff --git a/core/fpdfapi/render/cpdf_pagerendercontext.h b/core/fpdfapi/render/cpdf_pagerendercontext.h
new file mode 100644
index 0000000..8a5a5eb
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_pagerendercontext.h
@@ -0,0 +1,39 @@
+// 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_PAGERENDERCONTEXT_H_
+#define CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCONTEXT_H_
+
+#include <memory>
+
+#include "core/fpdfapi/page/cpdf_page.h"
+
+class CFX_RenderDevice;
+class CPDF_ProgressiveRenderer;
+class CPDF_RenderContext;
+class CPDF_RenderOptions;
+
+// Everything about rendering is put here: for OOM recovery
+class CPDF_PageRenderContext final : public CPDF_Page::RenderContextIface {
+ public:
+  // Context merely manages the lifetime for callers.
+  class AnnotListIface {
+   public:
+    virtual ~AnnotListIface() {}
+  };
+
+  CPDF_PageRenderContext();
+  ~CPDF_PageRenderContext() override;
+
+  // Specific destruction order required.
+  std::unique_ptr<AnnotListIface> m_pAnnots;
+  std::unique_ptr<CPDF_RenderOptions> m_pOptions;
+  std::unique_ptr<CFX_RenderDevice> m_pDevice;
+  std::unique_ptr<CPDF_RenderContext> m_pContext;
+  std::unique_ptr<CPDF_ProgressiveRenderer> m_pRenderer;
+};
+
+#endif  // CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCONTEXT_H_
diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.cpp b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
index e509a25..6e5fc36 100644
--- a/core/fpdfapi/render/cpdf_progressiverenderer.cpp
+++ b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
@@ -13,7 +13,7 @@
 #include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
-#include "core/fxcrt/ifx_pauseindicator.h"
+#include "core/fxcrt/pauseindicator_iface.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "third_party/base/ptr_util.h"
 
@@ -21,12 +21,7 @@
     CPDF_RenderContext* pContext,
     CFX_RenderDevice* pDevice,
     const CPDF_RenderOptions* pOptions)
-    : m_Status(Ready),
-      m_pContext(pContext),
-      m_pDevice(pDevice),
-      m_pOptions(pOptions),
-      m_LayerIndex(0),
-      m_pCurrentLayer(nullptr) {}
+    : m_pContext(pContext), m_pDevice(pDevice), m_pOptions(pOptions) {}
 
 CPDF_ProgressiveRenderer::~CPDF_ProgressiveRenderer() {
   if (m_pRenderStatus) {
@@ -35,69 +30,71 @@
   }
 }
 
-void CPDF_ProgressiveRenderer::Start(IFX_PauseIndicator* pPause) {
-  if (!m_pContext || !m_pDevice || m_Status != Ready) {
-    m_Status = Failed;
+void CPDF_ProgressiveRenderer::Start(PauseIndicatorIface* pPause) {
+  if (!m_pContext || !m_pDevice || m_Status != kReady) {
+    m_Status = kFailed;
     return;
   }
-  m_Status = ToBeContinued;
+  m_Status = kToBeContinued;
   Continue(pPause);
 }
 
-void CPDF_ProgressiveRenderer::Continue(IFX_PauseIndicator* pPause) {
-  while (m_Status == ToBeContinued) {
+void CPDF_ProgressiveRenderer::Continue(PauseIndicatorIface* pPause) {
+  while (m_Status == kToBeContinued) {
     if (!m_pCurrentLayer) {
       if (m_LayerIndex >= m_pContext->CountLayers()) {
-        m_Status = Done;
+        m_Status = kDone;
         return;
       }
       m_pCurrentLayer = m_pContext->GetLayer(m_LayerIndex);
-      m_LastObjectRendered =
-          m_pCurrentLayer->m_pObjectHolder->GetPageObjectList()->end();
-      m_pRenderStatus = pdfium::MakeUnique<CPDF_RenderStatus>();
-      m_pRenderStatus->Initialize(
-          m_pContext.Get(), m_pDevice.Get(), nullptr, nullptr, nullptr, nullptr,
-          m_pOptions, m_pCurrentLayer->m_pObjectHolder->m_iTransparency, false,
-          nullptr);
+      m_LastObjectRendered = m_pCurrentLayer->m_pObjectHolder->end();
+      m_pRenderStatus = pdfium::MakeUnique<CPDF_RenderStatus>(m_pContext.Get(),
+                                                              m_pDevice.Get());
+      if (m_pOptions)
+        m_pRenderStatus->SetOptions(*m_pOptions);
+      m_pRenderStatus->SetTransparency(
+          m_pCurrentLayer->m_pObjectHolder->GetTransparency());
+      m_pRenderStatus->Initialize(nullptr, nullptr);
       m_pDevice->SaveState();
       m_ClipRect = m_pCurrentLayer->m_Matrix.GetInverse().TransformRect(
           CFX_FloatRect(m_pDevice->GetClipBox()));
     }
-    CPDF_PageObjectList::iterator iter;
-    CPDF_PageObjectList::iterator iterEnd =
-        m_pCurrentLayer->m_pObjectHolder->GetPageObjectList()->end();
+    CPDF_PageObjectHolder::const_iterator iter;
+    CPDF_PageObjectHolder::const_iterator iterEnd =
+        m_pCurrentLayer->m_pObjectHolder->end();
     if (m_LastObjectRendered != iterEnd) {
       iter = m_LastObjectRendered;
       ++iter;
     } else {
-      iter = m_pCurrentLayer->m_pObjectHolder->GetPageObjectList()->begin();
+      iter = m_pCurrentLayer->m_pObjectHolder->begin();
     }
     int nObjsToGo = kStepLimit;
     bool is_mask = false;
     while (iter != iterEnd) {
       CPDF_PageObject* pCurObj = iter->get();
-      if (pCurObj && pCurObj->m_Left <= m_ClipRect.right &&
-          pCurObj->m_Right >= m_ClipRect.left &&
-          pCurObj->m_Bottom <= m_ClipRect.top &&
-          pCurObj->m_Top >= m_ClipRect.bottom) {
-        if (m_pOptions->HasFlag(RENDER_BREAKFORMASKS) && pCurObj->IsImage() &&
+      if (pCurObj && pCurObj->GetRect().left <= m_ClipRect.right &&
+          pCurObj->GetRect().right >= m_ClipRect.left &&
+          pCurObj->GetRect().bottom <= m_ClipRect.top &&
+          pCurObj->GetRect().top >= m_ClipRect.bottom) {
+        if (m_pOptions->GetOptions().bBreakForMasks && pCurObj->IsImage() &&
             pCurObj->AsImage()->GetImage()->IsMask()) {
-          if (m_pDevice->GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
+          if (m_pDevice->GetDeviceType() == DeviceType::kPrinter) {
             m_LastObjectRendered = iter;
             m_pRenderStatus->ProcessClipPath(pCurObj->m_ClipPath,
-                                             &m_pCurrentLayer->m_Matrix);
+                                             m_pCurrentLayer->m_Matrix);
             return;
           }
           is_mask = true;
         }
         if (m_pRenderStatus->ContinueSingleObject(
-                pCurObj, &m_pCurrentLayer->m_Matrix, pPause)) {
+                pCurObj, m_pCurrentLayer->m_Matrix, pPause)) {
           return;
         }
-        if (pCurObj->IsImage() && m_pRenderStatus->GetRenderOptions()->HasFlag(
-                                      RENDER_LIMITEDIMAGECACHE)) {
+        if (pCurObj->IsImage() && m_pRenderStatus->GetRenderOptions()
+                                      .GetOptions()
+                                      .bLimitedImageCache) {
           m_pContext->GetPageCache()->CacheOptimization(
-              m_pRenderStatus->GetRenderOptions()->GetCacheSizeLimit());
+              m_pRenderStatus->GetRenderOptions().GetCacheSizeLimit());
         }
         if (pCurObj->IsForm() || pCurObj->IsShading())
           nObjsToGo = 0;
@@ -114,20 +111,22 @@
       if (is_mask && iter != iterEnd)
         return;
     }
-    if (m_pCurrentLayer->m_pObjectHolder->IsParsed()) {
+    if (m_pCurrentLayer->m_pObjectHolder->GetParseState() ==
+        CPDF_PageObjectHolder::ParseState::kParsed) {
       m_pRenderStatus.reset();
       m_pDevice->RestoreState(false);
       m_pCurrentLayer = nullptr;
       m_LayerIndex++;
-      if (is_mask || (pPause && pPause->NeedToPauseNow())) {
+      if (is_mask || (pPause && pPause->NeedToPauseNow()))
         return;
-      }
     } else if (is_mask) {
       return;
     } else {
       m_pCurrentLayer->m_pObjectHolder->ContinueParse(pPause);
-      if (!m_pCurrentLayer->m_pObjectHolder->IsParsed())
+      if (m_pCurrentLayer->m_pObjectHolder->GetParseState() !=
+          CPDF_PageObjectHolder::ParseState::kParsed) {
         return;
+      }
     }
   }
 }
diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.h b/core/fpdfapi/render/cpdf_progressiverenderer.h
index ffd63a9..fd399f9 100644
--- a/core/fpdfapi/render/cpdf_progressiverenderer.h
+++ b/core/fpdfapi/render/cpdf_progressiverenderer.h
@@ -9,7 +9,7 @@
 
 #include <memory>
 
-#include "core/fpdfapi/page/cpdf_pageobjectlist.h"
+#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"
@@ -17,7 +17,7 @@
 class CPDF_RenderOptions;
 class CPDF_RenderStatus;
 class CFX_RenderDevice;
-class IFX_PauseIndicator;
+class PauseIndicatorIface;
 
 class CPDF_ProgressiveRenderer {
  public:
@@ -25,36 +25,34 @@
   // cannot #include that header. fpdfsdk/fpdf_progressive.cpp has
   // static_asserts to make sure the two sets of values match.
   enum Status {
-    Ready,          // FPDF_RENDER_READY
-    ToBeContinued,  // FPDF_RENDER_TOBECONTINUED
-    Done,           // FPDF_RENDER_DONE
-    Failed          // FPDF_RENDER_FAILED
+    kReady,          // FPDF_RENDER_READY
+    kToBeContinued,  // FPDF_RENDER_TOBECONTINUED
+    kDone,           // FPDF_RENDER_DONE
+    kFailed          // FPDF_RENDER_FAILED
   };
 
-  static int ToFPDFStatus(Status status) { return static_cast<int>(status); }
-
   CPDF_ProgressiveRenderer(CPDF_RenderContext* pContext,
                            CFX_RenderDevice* pDevice,
                            const CPDF_RenderOptions* pOptions);
   ~CPDF_ProgressiveRenderer();
 
   Status GetStatus() const { return m_Status; }
-  void Start(IFX_PauseIndicator* pPause);
-  void Continue(IFX_PauseIndicator* pPause);
+  void Start(PauseIndicatorIface* pPause);
+  void Continue(PauseIndicatorIface* pPause);
 
  private:
   // Maximum page objects to render before checking for pause.
   static const int kStepLimit = 100;
 
-  Status m_Status;
+  Status m_Status = kReady;
   UnownedPtr<CPDF_RenderContext> const m_pContext;
   UnownedPtr<CFX_RenderDevice> const m_pDevice;
   const CPDF_RenderOptions* const m_pOptions;
   std::unique_ptr<CPDF_RenderStatus> m_pRenderStatus;
   CFX_FloatRect m_ClipRect;
-  uint32_t m_LayerIndex;
-  CPDF_RenderContext::Layer* m_pCurrentLayer;
-  CPDF_PageObjectList::iterator m_LastObjectRendered;
+  uint32_t m_LayerIndex = 0;
+  CPDF_RenderContext::Layer* m_pCurrentLayer = nullptr;
+  CPDF_PageObjectHolder::const_iterator m_LastObjectRendered;
 };
 
 #endif  // CORE_FPDFAPI_RENDER_CPDF_PROGRESSIVERENDERER_H_
diff --git a/core/fpdfapi/render/cpdf_rendercontext.cpp b/core/fpdfapi/render/cpdf_rendercontext.cpp
index 0656edd..1280cee 100644
--- a/core/fpdfapi/render/cpdf_rendercontext.cpp
+++ b/core/fpdfapi/render/cpdf_rendercontext.cpp
@@ -6,7 +6,6 @@
 
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 
-#include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -16,31 +15,30 @@
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
 #include "core/fpdfapi/render/cpdf_textrenderer.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"
 
-CPDF_RenderContext::CPDF_RenderContext(CPDF_Page* pPage)
-    : m_pDocument(pPage->m_pDocument.Get()),
-      m_pPageResources(pPage->m_pPageResources.Get()),
-      m_pPageCache(pPage->GetRenderCache()) {}
-
 CPDF_RenderContext::CPDF_RenderContext(CPDF_Document* pDoc,
+                                       CPDF_Dictionary* pPageResources,
                                        CPDF_PageRenderCache* pPageCache)
-    : m_pDocument(pDoc), m_pPageResources(nullptr), m_pPageCache(pPageCache) {}
+    : m_pDocument(pDoc),
+      m_pPageResources(pPageResources),
+      m_pPageCache(pPageCache) {}
 
-CPDF_RenderContext::~CPDF_RenderContext() {}
+CPDF_RenderContext::~CPDF_RenderContext() = default;
 
 void CPDF_RenderContext::GetBackground(const RetainPtr<CFX_DIBitmap>& pBuffer,
                                        const CPDF_PageObject* pObj,
                                        const CPDF_RenderOptions* pOptions,
-                                       CFX_Matrix* pFinalMatrix) {
+                                       const CFX_Matrix& mtFinal) {
   CFX_DefaultRenderDevice device;
   device.Attach(pBuffer, false, nullptr, false);
 
-  FX_RECT rect(0, 0, device.GetWidth(), device.GetHeight());
-  device.FillRect(&rect, 0xffffffff);
-  Render(&device, pObj, pOptions, pFinalMatrix);
+  device.FillRect(FX_RECT(0, 0, device.GetWidth(), device.GetHeight()),
+                  0xffffffff);
+  Render(&device, pObj, pOptions, &mtFinal);
 }
 
 void CPDF_RenderContext::AppendLayer(CPDF_PageObjectHolder* pObjectHolder,
@@ -49,8 +47,6 @@
   m_Layers.back().m_pObjectHolder = pObjectHolder;
   if (pObject2Device)
     m_Layers.back().m_Matrix = *pObject2Device;
-  else
-    m_Layers.back().m_Matrix.SetIdentity();
 }
 
 void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice,
@@ -65,31 +61,29 @@
                                 const CFX_Matrix* pLastMatrix) {
   for (auto& layer : m_Layers) {
     CFX_RenderDevice::StateRestorer restorer(pDevice);
-    CPDF_RenderStatus status;
+    CPDF_RenderStatus status(this, pDevice);
+    if (pOptions)
+      status.SetOptions(*pOptions);
+    status.SetStopObject(pStopObj);
+    status.SetTransparency(layer.m_pObjectHolder->GetTransparency());
+    CFX_Matrix final_matrix = layer.m_Matrix;
     if (pLastMatrix) {
-      CFX_Matrix FinalMatrix = layer.m_Matrix;
-      FinalMatrix.Concat(*pLastMatrix);
-      status.Initialize(this, pDevice, pLastMatrix, pStopObj, nullptr, nullptr,
-                        pOptions, layer.m_pObjectHolder->m_iTransparency, false,
-                        nullptr);
-      status.RenderObjectList(layer.m_pObjectHolder.Get(), &FinalMatrix);
-    } else {
-      status.Initialize(this, pDevice, nullptr, pStopObj, nullptr, nullptr,
-                        pOptions, layer.m_pObjectHolder->m_iTransparency, false,
-                        nullptr);
-      status.RenderObjectList(layer.m_pObjectHolder.Get(), &layer.m_Matrix);
+      final_matrix *= *pLastMatrix;
+      status.SetDeviceMatrix(*pLastMatrix);
     }
-    if (status.GetRenderOptions()->HasFlag(RENDER_LIMITEDIMAGECACHE)) {
+    status.Initialize(nullptr, nullptr);
+    status.RenderObjectList(layer.m_pObjectHolder.Get(), final_matrix);
+    if (status.GetRenderOptions().GetOptions().bLimitedImageCache) {
       m_pPageCache->CacheOptimization(
-          status.GetRenderOptions()->GetCacheSizeLimit());
+          status.GetRenderOptions().GetCacheSizeLimit());
     }
     if (status.IsStopped())
       break;
   }
 }
 
-CPDF_RenderContext::Layer::Layer() {}
+CPDF_RenderContext::Layer::Layer() = default;
 
 CPDF_RenderContext::Layer::Layer(const Layer& that) = default;
 
-CPDF_RenderContext::Layer::~Layer() {}
+CPDF_RenderContext::Layer::~Layer() = default;
diff --git a/core/fpdfapi/render/cpdf_rendercontext.h b/core/fpdfapi/render/cpdf_rendercontext.h
index 606e5b9..d9efbc4 100644
--- a/core/fpdfapi/render/cpdf_rendercontext.h
+++ b/core/fpdfapi/render/cpdf_rendercontext.h
@@ -15,7 +15,6 @@
 
 class CPDF_Dictionary;
 class CPDF_Document;
-class CPDF_Page;
 class CPDF_PageObject;
 class CPDF_PageObjectHolder;
 class CPDF_PageRenderCache;
@@ -36,8 +35,9 @@
     CFX_Matrix m_Matrix;
   };
 
-  explicit CPDF_RenderContext(CPDF_Page* pPage);
-  CPDF_RenderContext(CPDF_Document* pDoc, CPDF_PageRenderCache* pPageCache);
+  CPDF_RenderContext(CPDF_Document* pDoc,
+                     CPDF_Dictionary* pPageResources,
+                     CPDF_PageRenderCache* pPageCache);
   ~CPDF_RenderContext();
 
   void AppendLayer(CPDF_PageObjectHolder* pObjectHolder,
@@ -45,17 +45,17 @@
 
   void Render(CFX_RenderDevice* pDevice,
               const CPDF_RenderOptions* pOptions,
-              const CFX_Matrix* pFinalMatrix);
+              const CFX_Matrix* pLastMatrix);
 
   void Render(CFX_RenderDevice* pDevice,
               const CPDF_PageObject* pStopObj,
               const CPDF_RenderOptions* pOptions,
-              const CFX_Matrix* pFinalMatrix);
+              const CFX_Matrix* pLastMatrix);
 
   void GetBackground(const RetainPtr<CFX_DIBitmap>& pBuffer,
                      const CPDF_PageObject* pObj,
                      const CPDF_RenderOptions* pOptions,
-                     CFX_Matrix* pFinalMatrix);
+                     const CFX_Matrix& mtFinal);
 
   size_t CountLayers() const { return m_Layers.size(); }
   Layer* GetLayer(uint32_t index) { return &m_Layers[index]; }
@@ -66,8 +66,8 @@
 
  protected:
   UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<CPDF_Dictionary> m_pPageResources;
-  UnownedPtr<CPDF_PageRenderCache> m_pPageCache;
+  RetainPtr<CPDF_Dictionary> const m_pPageResources;
+  UnownedPtr<CPDF_PageRenderCache> 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 9af9511..11efe9e 100644
--- a/core/fpdfapi/render/cpdf_renderoptions.cpp
+++ b/core/fpdfapi/render/cpdf_renderoptions.cpp
@@ -6,20 +6,26 @@
 
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 
-CPDF_RenderOptions::CPDF_RenderOptions()
-    : m_ColorMode(kNormal),
-      m_Flags(RENDER_CLEARTYPE),
-      m_dwLimitCacheSize(1024 * 1024 * 100),
-      m_bDrawAnnots(false) {}
+namespace {
 
-CPDF_RenderOptions::CPDF_RenderOptions(const CPDF_RenderOptions& rhs)
-    : m_ColorMode(rhs.m_ColorMode),
-      m_Flags(rhs.m_Flags),
-      m_dwLimitCacheSize(rhs.m_dwLimitCacheSize),
-      m_bDrawAnnots(rhs.m_bDrawAnnots),
-      m_pOCContext(rhs.m_pOCContext) {}
+constexpr uint32_t kCacheSizeLimitBytes = 100 * 1024 * 1024;
 
-CPDF_RenderOptions::~CPDF_RenderOptions() {}
+}  // namespace
+
+CPDF_RenderOptions::Options::Options() = default;
+
+CPDF_RenderOptions::Options::Options(const CPDF_RenderOptions::Options& rhs) =
+    default;
+
+CPDF_RenderOptions::CPDF_RenderOptions() {
+  // TODO(thestig): Make constexpr to initialize |m_Options| once C++14 is
+  // available.
+  m_Options.bClearType = true;
+}
+
+CPDF_RenderOptions::CPDF_RenderOptions(const CPDF_RenderOptions& rhs) = default;
+
+CPDF_RenderOptions::~CPDF_RenderOptions() = default;
 
 FX_ARGB CPDF_RenderOptions::TranslateColor(FX_ARGB argb) const {
   if (ColorModeIs(kNormal))
@@ -35,3 +41,7 @@
   int gray = FXRGB2GRAY(r, g, b);
   return ArgbEncode(a, gray, gray, gray);
 }
+
+uint32_t CPDF_RenderOptions::GetCacheSizeLimit() const {
+  return kCacheSizeLimitBytes;
+}
diff --git a/core/fpdfapi/render/cpdf_renderoptions.h b/core/fpdfapi/render/cpdf_renderoptions.h
index 9dca633..84f7e4c 100644
--- a/core/fpdfapi/render/cpdf_renderoptions.h
+++ b/core/fpdfapi/render/cpdf_renderoptions.h
@@ -7,32 +7,36 @@
 #ifndef CORE_FPDFAPI_RENDER_CPDF_RENDEROPTIONS_H_
 #define CORE_FPDFAPI_RENDER_CPDF_RENDEROPTIONS_H_
 
-#include "core/fpdfdoc/cpdf_occontext.h"
+#include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/fx_dib.h"
 
-#define RENDER_CLEARTYPE 0x00000001
-#define RENDER_PRINTGRAPHICTEXT 0x00000002
-#define RENDER_FORCE_DOWNSAMPLE 0x00000004
-#define RENDER_PRINTPREVIEW 0x00000008
-#define RENDER_BGR_STRIPE 0x00000010
-#define RENDER_NO_NATIVETEXT 0x00000020
-#define RENDER_FORCE_HALFTONE 0x00000040
-#define RENDER_RECT_AA 0x00000080
-#define RENDER_FILL_FULLCOVER 0x00000100
-#define RENDER_PRINTIMAGETEXT 0x00000200
-#define RENDER_OVERPRINT 0x00000400
-#define RENDER_THINLINE 0x00000800
-#define RENDER_BREAKFORMASKS 0x00001000
-#define RENDER_NOTEXTSMOOTH 0x10000000
-#define RENDER_NOPATHSMOOTH 0x20000000
-#define RENDER_NOIMAGESMOOTH 0x40000000
-#define RENDER_LIMITEDIMAGECACHE 0x80000000
-
 class CPDF_RenderOptions {
  public:
-  enum Type { kNormal = 0, kGray, kAlpha };
+  enum Type : uint8_t { kNormal = 0, kGray, kAlpha };
+
+  struct Options {
+    Options();
+    Options(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;
+  };
 
   CPDF_RenderOptions();
   CPDF_RenderOptions(const CPDF_RenderOptions& rhs);
@@ -43,11 +47,10 @@
   void SetColorMode(Type mode) { m_ColorMode = mode; }
   bool ColorModeIs(Type mode) const { return m_ColorMode == mode; }
 
-  bool HasFlag(uint32_t flag) const { return !!(m_Flags & flag); }
-  uint32_t GetFlags() const { return m_Flags; }
-  void SetFlags(uint32_t flags) { m_Flags = flags; }
+  const Options& GetOptions() const { return m_Options; }
+  Options& GetOptions() { return m_Options; }
 
-  uint32_t GetCacheSizeLimit() const { return m_dwLimitCacheSize; }
+  uint32_t GetCacheSizeLimit() const;
 
   void SetDrawAnnots(bool draw) { m_bDrawAnnots = draw; }
   bool GetDrawAnnots() const { return m_bDrawAnnots; }
@@ -55,13 +58,12 @@
   void SetOCContext(RetainPtr<CPDF_OCContext> context) {
     m_pOCContext = context;
   }
-  CPDF_OCContext* GetOCContext() const { return m_pOCContext.Get(); }
+  const CPDF_OCContext* GetOCContext() const { return m_pOCContext.Get(); }
 
  private:
-  Type m_ColorMode;
-  uint32_t m_Flags;
-  uint32_t m_dwLimitCacheSize;
-  bool m_bDrawAnnots;
+  Type m_ColorMode = kNormal;
+  bool m_bDrawAnnots = false;
+  Options m_Options;
   RetainPtr<CPDF_OCContext> m_pOCContext;
 };
 
diff --git a/core/fpdfapi/render/cpdf_rendershading.cpp b/core/fpdfapi/render/cpdf_rendershading.cpp
new file mode 100644
index 0000000..53fb79a
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendershading.cpp
@@ -0,0 +1,954 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/render/cpdf_rendershading.h"
+
+#include <algorithm>
+#include <array>
+#include <cmath>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fpdfapi/page/cpdf_dib.h"
+#include "core/fpdfapi/page/cpdf_function.h"
+#include "core/fpdfapi/page/cpdf_meshstream.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/fpdf_parser_utility.h"
+#include "core/fpdfapi/render/cpdf_devicebuffer.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_dib.h"
+
+namespace {
+
+constexpr int kShadingSteps = 256;
+
+uint32_t CountOutputsFromFunctions(
+    const std::vector<std::unique_ptr<CPDF_Function>>& funcs) {
+  FX_SAFE_UINT32 total = 0;
+  for (const auto& func : funcs) {
+    if (func)
+      total += func->CountOutputs();
+  }
+  return total.ValueOrDefault(0);
+}
+
+uint32_t GetValidatedOutputsCount(
+    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+    const RetainPtr<CPDF_ColorSpace>& pCS) {
+  uint32_t funcs_outputs = CountOutputsFromFunctions(funcs);
+  return funcs_outputs ? std::max(funcs_outputs, pCS->CountComponents()) : 0;
+}
+
+std::array<FX_ARGB, kShadingSteps> GetShadingSteps(
+    float t_min,
+    float t_max,
+    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+    const RetainPtr<CPDF_ColorSpace>& pCS,
+    int alpha,
+    size_t results_count) {
+  ASSERT(results_count >= CountOutputsFromFunctions(funcs));
+  ASSERT(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;
+    for (const auto& func : funcs) {
+      if (func) {
+        int nresults = 0;
+        if (func->Call(&input, 1, &result_array[offset], &nresults))
+          offset += nresults;
+      }
+    }
+    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)));
+  }
+  return shading_steps;
+}
+
+void DrawAxialShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                      const CFX_Matrix& mtObject2Bitmap,
+                      const CPDF_Dictionary* pDict,
+                      const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+                      const RetainPtr<CPDF_ColorSpace>& pCS,
+                      int alpha) {
+  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+  const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
+  if (total_results == 0)
+    return;
+
+  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 t_min = 0;
+  float t_max = 1.0f;
+  const CPDF_Array* pArray = pDict->GetArrayFor("Domain");
+  if (pArray) {
+    t_min = pArray->GetNumberAt(0);
+    t_max = pArray->GetNumberAt(1);
+  }
+  pArray = pDict->GetArrayFor("Extend");
+  const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false);
+  const bool bEndExtend = pArray && pArray->GetBooleanAt(1, false);
+
+  int width = pBitmap->GetWidth();
+  int height = pBitmap->GetHeight();
+  float x_span = end_x - start_x;
+  float y_span = end_y - start_y;
+  float axis_len_square = (x_span * x_span) + (y_span * y_span);
+
+  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);
+    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));
+      if (index < 0) {
+        if (!bStartExtend)
+          continue;
+
+        index = 0;
+      } else if (index >= kShadingSteps) {
+        if (!bEndExtend)
+          continue;
+
+        index = kShadingSteps - 1;
+      }
+      dib_buf[column] = shading_steps[index];
+    }
+  }
+}
+
+void DrawRadialShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                       const CFX_Matrix& mtObject2Bitmap,
+                       const CPDF_Dictionary* pDict,
+                       const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+                       const RetainPtr<CPDF_ColorSpace>& pCS,
+                       int alpha) {
+  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+  const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
+  if (total_results == 0)
+    return;
+
+  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 t_min = 0;
+  float t_max = 1.0f;
+  const CPDF_Array* pArray = pDict->GetArrayFor("Domain");
+  if (pArray) {
+    t_min = pArray->GetNumberAt(0);
+    t_max = pArray->GetNumberAt(1);
+  }
+  pArray = pDict->GetArrayFor("Extend");
+  const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false);
+  const bool bEndExtend = pArray && pArray->GetBooleanAt(1, false);
+
+  std::array<FX_ARGB, kShadingSteps> shading_steps =
+      GetShadingSteps(t_min, t_max, funcs, pCS, alpha, total_results);
+
+  const float dx = end_x - start_x;
+  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);
+
+  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);
+
+  CFX_Matrix matrix = mtObject2Bitmap.GetInverse();
+  for (int row = 0; row < height; row++) {
+    uint32_t* dib_buf =
+        reinterpret_cast<uint32_t*>(pBitmap->GetBuffer() + row * pitch);
+    for (int column = 0; column < width; column++) {
+      CFX_PointF pos = matrix.Transform(
+          CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
+      float pos_dx = pos.x - start_x;
+      float pos_dy = pos.y - start_y;
+      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)) {
+        s = sqrt(-c / a);
+      } else if (a_is_float_zero) {
+        s = -c / b;
+      } else {
+        float b2_4ac = (b * b) - 4 * (a * c);
+        if (b2_4ac < 0)
+          continue;
+
+        float root = sqrt(b2_4ac);
+        float s1 = (-b - root) / (2 * a);
+        float s2 = (-b + root) / (2 * a);
+        if (a <= 0)
+          std::swap(s1, s2);
+        if (bDecreasing)
+          s = (s1 >= 0 || bStartExtend) ? s1 : s2;
+        else
+          s = (s2 <= 1.0f || bEndExtend) ? s2 : s1;
+
+        if (start_r + s * dr < 0)
+          continue;
+      }
+
+      int index = static_cast<int32_t>(s * (kShadingSteps - 1));
+      if (index < 0) {
+        if (!bStartExtend)
+          continue;
+        index = 0;
+      } else if (index >= kShadingSteps) {
+        if (!bEndExtend)
+          continue;
+        index = kShadingSteps - 1;
+      }
+      dib_buf[column] = shading_steps[index];
+    }
+  }
+}
+
+void DrawFuncShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                     const CFX_Matrix& mtObject2Bitmap,
+                     const CPDF_Dictionary* pDict,
+                     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+                     const RetainPtr<CPDF_ColorSpace>& pCS,
+                     int alpha) {
+  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+  const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
+  if (total_results == 0)
+    return;
+
+  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);
+  }
+  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());
+  std::vector<float> result_array(total_results);
+  for (int row = 0; row < height; ++row) {
+    uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch);
+    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;
+      for (const auto& func : funcs) {
+        if (func) {
+          int nresults;
+          if (func->Call(input, 2, &result_array[offset], &nresults))
+            offset += nresults;
+        }
+      }
+
+      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)));
+    }
+  }
+}
+
+bool GetScanlineIntersect(int y,
+                          const CFX_PointF& first,
+                          const CFX_PointF& second,
+                          float* x) {
+  if (first.y == second.y)
+    return false;
+
+  if (first.y < second.y) {
+    if (y < first.y || y > second.y)
+      return false;
+  } else if (y < second.y || y > first.y) {
+    return false;
+  }
+  *x = first.x + ((second.x - first.x) * (y - first.y) / (second.y - first.y));
+  return true;
+}
+
+void DrawGouraud(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                 int alpha,
+                 CPDF_MeshVertex triangle[3]) {
+  float min_y = triangle[0].position.y;
+  float max_y = triangle[0].position.y;
+  for (int i = 1; i < 3; i++) {
+    min_y = std::min(min_y, triangle[i].position.y);
+    max_y = std::max(max_y, triangle[i].position.y);
+  }
+  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));
+
+  if (max_yi >= pBitmap->GetHeight())
+    max_yi = pBitmap->GetHeight() - 1;
+
+  for (int y = min_yi; y <= max_yi; y++) {
+    int nIntersects = 0;
+    float inter_x[3];
+    float r[3];
+    float g[3];
+    float b[3];
+    for (int i = 0; i < 3; i++) {
+      CPDF_MeshVertex& vertex1 = triangle[i];
+      CPDF_MeshVertex& vertex2 = triangle[(i + 1) % 3];
+      CFX_PointF& position1 = vertex1.position;
+      CFX_PointF& position2 = vertex2.position;
+      bool bIntersect =
+          GetScanlineIntersect(y, position1, position2, &inter_x[nIntersects]);
+      if (!bIntersect)
+        continue;
+
+      float y_dist = (y - position1.y) / (position2.y - position1.y);
+      r[nIntersects] = vertex1.r + ((vertex2.r - vertex1.r) * y_dist);
+      g[nIntersects] = vertex1.g + ((vertex2.g - vertex1.g) * y_dist);
+      b[nIntersects] = vertex1.b + ((vertex2.b - vertex1.b) * y_dist);
+      nIntersects++;
+    }
+    if (nIntersects != 2)
+      continue;
+
+    int min_x, max_x, start_index, end_index;
+    if (inter_x[0] < inter_x[1]) {
+      min_x = (int)floor(inter_x[0]);
+      max_x = (int)ceil(inter_x[1]);
+      start_index = 0;
+      end_index = 1;
+    } else {
+      min_x = (int)floor(inter_x[1]);
+      max_x = (int)ceil(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;
+    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;
+    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;
+    }
+  }
+}
+
+void DrawFreeGouraudShading(
+    const RetainPtr<CFX_DIBitmap>& pBitmap,
+    const CFX_Matrix& mtObject2Bitmap,
+    const CPDF_Stream* pShadingStream,
+    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+    const RetainPtr<CPDF_ColorSpace>& pCS,
+    int alpha) {
+  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+  CPDF_MeshStream stream(kFreeFormGouraudTriangleMeshShading, funcs,
+                         pShadingStream, pCS);
+  if (!stream.Load())
+    return;
+
+  CPDF_MeshVertex triangle[3];
+  memset(triangle, 0, sizeof(triangle));
+
+  while (!stream.BitStream()->IsEOF()) {
+    CPDF_MeshVertex vertex;
+    uint32_t flag;
+    if (!stream.ReadVertex(mtObject2Bitmap, &vertex, &flag))
+      return;
+
+    if (flag == 0) {
+      triangle[0] = vertex;
+      for (int j = 1; j < 3; j++) {
+        uint32_t tflag;
+        if (!stream.ReadVertex(mtObject2Bitmap, &triangle[j], &tflag))
+          return;
+      }
+    } else {
+      if (flag == 1)
+        triangle[0] = triangle[1];
+
+      triangle[1] = triangle[2];
+      triangle[2] = vertex;
+    }
+    DrawGouraud(pBitmap, alpha, triangle);
+  }
+}
+
+void DrawLatticeGouraudShading(
+    const RetainPtr<CFX_DIBitmap>& pBitmap,
+    const CFX_Matrix& mtObject2Bitmap,
+    const CPDF_Stream* pShadingStream,
+    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+    const RetainPtr<CPDF_ColorSpace>& pCS,
+    int alpha) {
+  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+  int row_verts = pShadingStream->GetDict()->GetIntegerFor("VerticesPerRow");
+  if (row_verts < 2)
+    return;
+
+  CPDF_MeshStream stream(kLatticeFormGouraudTriangleMeshShading, funcs,
+                         pShadingStream, pCS);
+  if (!stream.Load())
+    return;
+
+  std::vector<CPDF_MeshVertex> vertices[2];
+  vertices[0] = stream.ReadVertexRow(mtObject2Bitmap, row_verts);
+  if (vertices[0].empty())
+    return;
+
+  int last_index = 0;
+  while (1) {
+    vertices[1 - last_index] = stream.ReadVertexRow(mtObject2Bitmap, row_verts);
+    if (vertices[1 - last_index].empty())
+      return;
+
+    CPDF_MeshVertex triangle[3];
+    for (int i = 1; i < row_verts; ++i) {
+      triangle[0] = vertices[last_index][i];
+      triangle[1] = vertices[1 - last_index][i - 1];
+      triangle[2] = vertices[last_index][i - 1];
+      DrawGouraud(pBitmap, alpha, triangle);
+      triangle[2] = vertices[1 - last_index][i];
+      DrawGouraud(pBitmap, alpha, triangle);
+    }
+    last_index = 1 - last_index;
+  }
+}
+
+struct Coon_BezierCoeff {
+  float a, b, c, d;
+  void FromPoints(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) {
+    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() {
+    float dis = a + b + c;
+    return dis < 0 ? -dis : dis;
+  }
+};
+
+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);
+  }
+
+  Coon_Bezier first_half() {
+    Coon_Bezier result;
+    result.x = x.first_half();
+    result.y = y.first_half();
+    return result;
+  }
+
+  Coon_Bezier second_half() {
+    Coon_Bezier 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(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(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];
+
+    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(); }
+};
+
+int Interpolate(int p1, int p2, int delta1, int delta2, bool* overflow) {
+  pdfium::base::CheckedNumeric<int> p = p2;
+  p -= p1;
+  p *= delta1;
+  p /= delta2;
+  p += p1;
+  if (!p.IsValid())
+    *overflow = true;
+  return p.ValueOrDefault(0);
+}
+
+int BiInterpolImpl(int c0,
+                   int c1,
+                   int c2,
+                   int c3,
+                   int x,
+                   int y,
+                   int x_scale,
+                   int y_scale,
+                   bool* overflow) {
+  int x1 = Interpolate(c0, c3, x, x_scale, overflow);
+  int x2 = Interpolate(c1, c2, x, x_scale, overflow);
+  return Interpolate(x1, x2, y, y_scale, overflow);
+}
+
+struct Coon_Color {
+  Coon_Color() { memset(comp, 0, sizeof(int) * 3); }
+
+  // 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 overflow = false;
+    for (int i = 0; i < 3; i++) {
+      comp[i] = BiInterpolImpl(colors[0].comp[i], colors[1].comp[i],
+                               colors[2].comp[i], colors[3].comp[i], x, y,
+                               x_scale, y_scale, &overflow);
+    }
+    return !overflow;
+  }
+
+  int Distance(Coon_Color& o) {
+    return std::max({abs(comp[0] - o.comp[0]), abs(comp[1] - o.comp[1]),
+                     abs(comp[2] - o.comp[2])});
+  }
+
+  int comp[3];
+};
+
+#define COONCOLOR_THRESHOLD 4
+struct CPDF_PatchDrawer {
+  void Draw(int x_scale,
+            int y_scale,
+            int left,
+            int bottom,
+            Coon_Bezier C1,
+            Coon_Bezier C2,
+            Coon_Bezier D1,
+            Coon_Bezier D2) {
+    bool bSmall = C1.Distance() < 2 && C2.Distance() < 2 && D1.Distance() < 2 &&
+                  D2.Distance() < 2;
+    Coon_Color div_colors[4];
+    int d_bottom = 0;
+    int d_left = 0;
+    int d_top = 0;
+    int d_right = 0;
+    if (!div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale,
+                                  y_scale)) {
+      return;
+    }
+    if (!bSmall) {
+      if (!div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale,
+                                    y_scale)) {
+        return;
+      }
+      if (!div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale,
+                                    y_scale)) {
+        return;
+      }
+      if (!div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale,
+                                    y_scale)) {
+        return;
+      }
+      d_bottom = div_colors[3].Distance(div_colors[0]);
+      d_left = div_colors[1].Distance(div_colors[0]);
+      d_top = div_colors[1].Distance(div_colors[2]);
+      d_right = div_colors[2].Distance(div_colors[3]);
+    }
+
+    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;
+      if (bNoPathSmooth)
+        fillFlags |= FXFILL_NOPATHSMOOTH;
+      pDevice->DrawPath(
+          &path, nullptr, nullptr,
+          ArgbEncode(alpha, div_colors[0].comp[0], div_colors[0].comp[1],
+                     div_colors[0].comp[2]),
+          0, fillFlags);
+    } else {
+      if (d_bottom < COONCOLOR_THRESHOLD && d_top < COONCOLOR_THRESHOLD) {
+        Coon_Bezier m1;
+        m1.BezierInterpol(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);
+        x_scale *= 2;
+        left *= 2;
+        Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(),
+             D1, m2);
+        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();
+        x_scale *= 2;
+        y_scale *= 2;
+        left *= 2;
+        bottom *= 2;
+        Draw(x_scale, y_scale, left, bottom, C1.first_half(), m1f,
+             D1.first_half(), m2f);
+        Draw(x_scale, y_scale, left, bottom + 1, m1f, C2.first_half(),
+             D1.second_half(), m2s);
+        Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), m1s, m2f,
+             D2.first_half());
+        Draw(x_scale, y_scale, left + 1, bottom + 1, m1s, C2.second_half(), m2s,
+             D2.second_half());
+      }
+    }
+  }
+
+  int max_delta;
+  CFX_PathData path;
+  CFX_RenderDevice* pDevice;
+  int bNoPathSmooth;
+  int alpha;
+  Coon_Color patch_colors[4];
+};
+
+void DrawCoonPatchMeshes(
+    ShadingType type,
+    const RetainPtr<CFX_DIBitmap>& pBitmap,
+    const CFX_Matrix& mtObject2Bitmap,
+    const CPDF_Stream* pShadingStream,
+    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
+    const RetainPtr<CPDF_ColorSpace>& pCS,
+    bool bNoPathSmooth,
+    int alpha) {
+  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+  ASSERT(type == kCoonsPatchMeshShading ||
+         type == kTensorProductPatchMeshShading);
+
+  CFX_DefaultRenderDevice device;
+  device.Attach(pBitmap, false, nullptr, false);
+  CPDF_MeshStream stream(type, funcs, pShadingStream, pCS);
+  if (!stream.Load())
+    return;
+
+  CPDF_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);
+  }
+
+  CFX_PointF coords[16];
+  int point_count = type == kTensorProductPatchMeshShading ? 16 : 12;
+  while (!stream.BitStream()->IsEOF()) {
+    if (!stream.CanReadFlag())
+      break;
+    uint32_t flag = stream.ReadFlag();
+    int iStartPoint = 0, iStartColor = 0, i = 0;
+    if (flag) {
+      iStartPoint = 4;
+      iStartColor = 2;
+      CFX_PointF tempCoords[4];
+      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);
+    }
+    for (i = iStartPoint; i < point_count; i++) {
+      if (!stream.CanReadCoords())
+        break;
+      coords[i] = mtObject2Bitmap.Transform(stream.ReadCoords());
+    }
+
+    for (i = iStartColor; i < 4; i++) {
+      if (!stream.CanReadColor())
+        break;
+
+      float r;
+      float g;
+      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);
+    }
+    CFX_FloatRect bbox = CFX_FloatRect::GetBBox(coords, 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);
+    patch.Draw(1, 1, 0, 0, C1, C2, D1, D2);
+  }
+}
+
+}  // namespace
+
+// static
+void CPDF_RenderShading::Draw(CFX_RenderDevice* pDevice,
+                              CPDF_RenderContext* pContext,
+                              const CPDF_PageObject* pCurObj,
+                              const CPDF_ShadingPattern* pPattern,
+                              const CFX_Matrix& mtMatrix,
+                              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;
+  if (!pPattern->IsShadingObject() && pDict->KeyExist("Background")) {
+    const CPDF_Array* pBackColor = pDict->GetArrayFor("Background");
+    if (pBackColor && pBackColor->size() >= pColorSpace->CountComponents()) {
+      std::vector<float> comps =
+          ReadArrayElementsToVector(pBackColor, 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));
+    }
+  }
+  FX_RECT clip_rect_bbox = clip_rect;
+  if (pDict->KeyExist("BBox")) {
+    clip_rect_bbox.Intersect(
+        mtMatrix.TransformRect(pDict->GetRectFor("BBox")).GetOuterRect());
+  }
+  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)) {
+    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())
+    return;
+
+  pBitmap->Clear(background);
+  switch (pPattern->GetShadingType()) {
+    case kInvalidShading:
+    case kMaxShading:
+      return;
+    case kFunctionBasedShading:
+      DrawFuncShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha);
+      break;
+    case kAxialShading:
+      DrawAxialShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha);
+      break;
+    case kRadialShading:
+      DrawRadialShading(pBitmap, FinalMatrix, pDict, 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,
+                               pColorSpace, alpha);
+      }
+    } 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);
+      }
+    } 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,
+                            options.GetOptions().bNoPathSmooth, alpha);
+      }
+    } break;
+  }
+  if (bAlphaMode)
+    pBitmap->LoadChannelFromAlpha(FXDIB_Red, 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
new file mode 100644
index 0000000..8c0d8a4
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendershading.h
@@ -0,0 +1,34 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_RENDER_CPDF_RENDERSHADING_H_
+#define CORE_FPDFAPI_RENDER_CPDF_RENDERSHADING_H_
+
+class CFX_Matrix;
+class CFX_RenderDevice;
+class CPDF_PageObject;
+class CPDF_RenderContext;
+class CPDF_RenderOptions;
+class CPDF_ShadingPattern;
+struct FX_RECT;
+
+class CPDF_RenderShading {
+ public:
+  static void Draw(CFX_RenderDevice* pDevice,
+                   CPDF_RenderContext* pContext,
+                   const CPDF_PageObject* pCurObj,
+                   const CPDF_ShadingPattern* pPattern,
+                   const CFX_Matrix& mtMatrix,
+                   const FX_RECT& clip_rect,
+                   int alpha,
+                   const CPDF_RenderOptions& options);
+
+  CPDF_RenderShading() = delete;
+  CPDF_RenderShading(const CPDF_RenderShading&) = delete;
+  CPDF_RenderShading& operator=(const CPDF_RenderShading&) = delete;
+};
+
+#endif  // CORE_FPDFAPI_RENDER_CPDF_RENDERSHADING_H_
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 8899867..79d31b9 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -10,9 +10,13 @@
 #include <cmath>
 #include <limits>
 #include <memory>
+#include <numeric>
+#include <set>
 #include <utility>
 #include <vector>
 
+#include "build/build_config.h"
+#include "constants/transparency.h"
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/font/cpdf_type3char.h"
 #include "core/fpdfapi/font/cpdf_type3font.h"
@@ -23,7 +27,7 @@
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
 #include "core/fpdfapi/page/cpdf_image.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
-#include "core/fpdfapi/page/cpdf_meshstream.h"
+#include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_pathobject.h"
@@ -31,894 +35,56 @@
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
 #include "core/fpdfapi/page/cpdf_textobject.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/parser/fpdf_parser_utility.h"
 #include "core/fpdfapi/render/cpdf_charposlist.h"
-#include "core/fpdfapi/render/cpdf_devicebuffer.h"
-#include "core/fpdfapi/render/cpdf_dibsource.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_scaledrenderbuffer.h"
 #include "core/fpdfapi/render/cpdf_textrenderer.h"
-#include "core/fpdfapi/render/cpdf_transferfunc.h"
 #include "core/fpdfapi/render/cpdf_type3cache.h"
-#include "core/fpdfdoc/cpdf_occontext.h"
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_fixedbufgrow.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/cfx_glyphbitmap.h"
 #include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/ifx_renderdevicedriver.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/renderdevicedriver_iface.h"
+#include "core/fxge/text_char_pos.h"
+#include "core/fxge/text_glyph_pos.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/logging.h"
 #include "third_party/base/numerics/safe_math.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 #ifdef _SKIA_SUPPORT_
 #include "core/fxge/skia/fx_skia_device.h"
 #endif
 
-#define SHADING_STEPS 256
-
 namespace {
 
-void ReleaseCachedType3(CPDF_Type3Font* pFont) {
-  CPDF_Document* pDoc = pFont->GetDocument();
-  if (!pDoc)
-    return;
+constexpr int kRenderMaxRecursionDepth = 64;
+int g_CurrentRecursionDepth = 0;
 
-  pDoc->GetRenderData()->MaybePurgeCachedType3(pFont);
-  pDoc->GetPageData()->ReleaseFont(pFont->GetFontDict());
-}
-
-class CPDF_RefType3Cache {
- public:
-  explicit CPDF_RefType3Cache(CPDF_Type3Font* pType3Font)
-      : m_dwCount(0), m_pType3Font(pType3Font) {}
-
-  ~CPDF_RefType3Cache() {
-    while (m_dwCount--)
-      ReleaseCachedType3(m_pType3Font.Get());
-  }
-
-  uint32_t m_dwCount;
-  UnownedPtr<CPDF_Type3Font> const m_pType3Font;
-};
-
-uint32_t CountOutputs(
-    const std::vector<std::unique_ptr<CPDF_Function>>& funcs) {
-  uint32_t total = 0;
-  for (const auto& func : funcs) {
-    if (func)
-      total += func->CountOutputs();
-  }
-  return total;
-}
-
-void DrawAxialShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                      CFX_Matrix* pObject2Bitmap,
-                      CPDF_Dictionary* pDict,
-                      const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                      CPDF_ColorSpace* pCS,
-                      int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
-  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 t_min = 0;
-  float t_max = 1.0f;
-  CPDF_Array* pArray = pDict->GetArrayFor("Domain");
-  if (pArray) {
-    t_min = pArray->GetNumberAt(0);
-    t_max = pArray->GetNumberAt(1);
-  }
-  bool bStartExtend = false;
-  bool bEndExtend = false;
-  pArray = pDict->GetArrayFor("Extend");
-  if (pArray) {
-    bStartExtend = !!pArray->GetIntegerAt(0);
-    bEndExtend = !!pArray->GetIntegerAt(1);
-  }
-  int width = pBitmap->GetWidth();
-  int height = pBitmap->GetHeight();
-  float x_span = end_x - start_x;
-  float y_span = end_y - start_y;
-  float axis_len_square = (x_span * x_span) + (y_span * y_span);
-  uint32_t total_results =
-      std::max(CountOutputs(funcs), pCS->CountComponents());
-  CFX_FixedBufGrow<float, 16> result_array(total_results);
-  float* pResults = result_array;
-  memset(pResults, 0, total_results * sizeof(float));
-  uint32_t rgb_array[SHADING_STEPS];
-  for (int i = 0; i < SHADING_STEPS; i++) {
-    float input = (t_max - t_min) * i / SHADING_STEPS + t_min;
-    int offset = 0;
-    for (const auto& func : funcs) {
-      if (func) {
-        int nresults = 0;
-        if (func->Call(&input, 1, pResults + offset, &nresults))
-          offset += nresults;
-      }
-    }
-    float R = 0.0f;
-    float G = 0.0f;
-    float B = 0.0f;
-    pCS->GetRGB(pResults, &R, &G, &B);
-    rgb_array[i] =
-        FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255),
-                                 FXSYS_round(G * 255), FXSYS_round(B * 255)));
-  }
-  int pitch = pBitmap->GetPitch();
-  CFX_Matrix matrix = pObject2Bitmap->GetInverse();
-  for (int row = 0; row < height; row++) {
-    uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch);
-    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 * (SHADING_STEPS - 1));
-      if (index < 0) {
-        if (!bStartExtend)
-          continue;
-
-        index = 0;
-      } else if (index >= SHADING_STEPS) {
-        if (!bEndExtend)
-          continue;
-
-        index = SHADING_STEPS - 1;
-      }
-      dib_buf[column] = rgb_array[index];
-    }
-  }
-}
-
-void DrawRadialShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                       CFX_Matrix* pObject2Bitmap,
-                       CPDF_Dictionary* pDict,
-                       const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                       CPDF_ColorSpace* pCS,
-                       int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
-  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 t_min = 0;
-  float t_max = 1.0f;
-  CPDF_Array* pArray = pDict->GetArrayFor("Domain");
-  if (pArray) {
-    t_min = pArray->GetNumberAt(0);
-    t_max = pArray->GetNumberAt(1);
-  }
-  bool bStartExtend = false;
-  bool bEndExtend = false;
-  pArray = pDict->GetArrayFor("Extend");
-  if (pArray) {
-    bStartExtend = !!pArray->GetIntegerAt(0);
-    bEndExtend = !!pArray->GetIntegerAt(1);
-  }
-  uint32_t total_results =
-      std::max(CountOutputs(funcs), pCS->CountComponents());
-  CFX_FixedBufGrow<float, 16> result_array(total_results);
-  float* pResults = result_array;
-  memset(pResults, 0, total_results * sizeof(float));
-  uint32_t rgb_array[SHADING_STEPS];
-  for (int i = 0; i < SHADING_STEPS; i++) {
-    float input = (t_max - t_min) * i / SHADING_STEPS + t_min;
-    int offset = 0;
-    for (const auto& func : funcs) {
-      if (func) {
-        int nresults;
-        if (func->Call(&input, 1, pResults + offset, &nresults))
-          offset += nresults;
-      }
-    }
-    float R = 0.0f;
-    float G = 0.0f;
-    float B = 0.0f;
-    pCS->GetRGB(pResults, &R, &G, &B);
-    rgb_array[i] =
-        FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255),
-                                 FXSYS_round(G * 255), FXSYS_round(B * 255)));
-  }
-  float a = ((start_x - end_x) * (start_x - end_x)) +
-            ((start_y - end_y) * (start_y - end_y)) -
-            ((start_r - end_r) * (start_r - end_r));
-  int width = pBitmap->GetWidth();
-  int height = pBitmap->GetHeight();
-  int pitch = pBitmap->GetPitch();
-  bool bDecreasing = false;
-  if (start_r > end_r) {
-    int length = (int)sqrt((((start_x - end_x) * (start_x - end_x)) +
-                            ((start_y - end_y) * (start_y - end_y))));
-    if (length < start_r - end_r) {
-      bDecreasing = true;
-    }
-  }
-  CFX_Matrix matrix = pObject2Bitmap->GetInverse();
-  for (int row = 0; row < height; row++) {
-    uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch);
-    for (int column = 0; column < width; column++) {
-      CFX_PointF pos = matrix.Transform(
-          CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
-      float b = -2 * (((pos.x - start_x) * (end_x - start_x)) +
-                      ((pos.y - start_y) * (end_y - start_y)) +
-                      (start_r * (end_r - start_r)));
-      float c = ((pos.x - start_x) * (pos.x - start_x)) +
-                ((pos.y - start_y) * (pos.y - start_y)) - (start_r * start_r);
-      float s;
-      if (a == 0) {
-        s = -c / b;
-      } else {
-        float b2_4ac = (b * b) - 4 * (a * c);
-        if (b2_4ac < 0) {
-          continue;
-        }
-        float root = sqrt(b2_4ac);
-        float s1, s2;
-        if (a > 0) {
-          s1 = (-b - root) / (2 * a);
-          s2 = (-b + root) / (2 * a);
-        } else {
-          s2 = (-b - root) / (2 * a);
-          s1 = (-b + root) / (2 * a);
-        }
-        if (bDecreasing) {
-          if (s1 >= 0 || bStartExtend) {
-            s = s1;
-          } else {
-            s = s2;
-          }
-        } else {
-          if (s2 <= 1.0f || bEndExtend) {
-            s = s2;
-          } else {
-            s = s1;
-          }
-        }
-        if ((start_r + s * (end_r - start_r)) < 0) {
-          continue;
-        }
-      }
-      int index = (int32_t)(s * (SHADING_STEPS - 1));
-      if (index < 0) {
-        if (!bStartExtend) {
-          continue;
-        }
-        index = 0;
-      }
-      if (index >= SHADING_STEPS) {
-        if (!bEndExtend) {
-          continue;
-        }
-        index = SHADING_STEPS - 1;
-      }
-      dib_buf[column] = rgb_array[index];
-    }
-  }
-}
-
-void DrawFuncShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                     CFX_Matrix* pObject2Bitmap,
-                     CPDF_Dictionary* pDict,
-                     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                     CPDF_ColorSpace* pCS,
-                     int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
-  CPDF_Array* pDomain = pDict->GetArrayFor("Domain");
-  float xmin = 0, ymin = 0, xmax = 1.0f, ymax = 1.0f;
-  if (pDomain) {
-    xmin = pDomain->GetNumberAt(0);
-    xmax = pDomain->GetNumberAt(1);
-    ymin = pDomain->GetNumberAt(2);
-    ymax = pDomain->GetNumberAt(3);
-  }
-  CFX_Matrix mtDomain2Target = pDict->GetMatrixFor("Matrix");
-  CFX_Matrix matrix = pObject2Bitmap->GetInverse();
-  matrix.Concat(mtDomain2Target.GetInverse());
-  int width = pBitmap->GetWidth();
-  int height = pBitmap->GetHeight();
-  int pitch = pBitmap->GetPitch();
-  uint32_t total_results =
-      std::max(CountOutputs(funcs), pCS->CountComponents());
-  CFX_FixedBufGrow<float, 16> result_array(total_results);
-  float* pResults = result_array;
-  memset(pResults, 0, total_results * sizeof(float));
-  for (int row = 0; row < height; row++) {
-    uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch);
-    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;
-      for (const auto& func : funcs) {
-        if (func) {
-          int nresults;
-          if (func->Call(input, 2, pResults + offset, &nresults))
-            offset += nresults;
-        }
-      }
-
-      float R = 0.0f;
-      float G = 0.0f;
-      float B = 0.0f;
-      pCS->GetRGB(pResults, &R, &G, &B);
-      dib_buf[column] = FXARGB_TODIB(FXARGB_MAKE(
-          alpha, (int32_t)(R * 255), (int32_t)(G * 255), (int32_t)(B * 255)));
-    }
-  }
-}
-
-bool GetScanlineIntersect(int y,
-                          const CFX_PointF& first,
-                          const CFX_PointF& second,
-                          float* x) {
-  if (first.y == second.y)
-    return false;
-
-  if (first.y < second.y) {
-    if (y < first.y || y > second.y)
-      return false;
-  } else if (y < second.y || y > first.y) {
-    return false;
-  }
-  *x = first.x + ((second.x - first.x) * (y - first.y) / (second.y - first.y));
-  return true;
-}
-
-void DrawGouraud(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                 int alpha,
-                 CPDF_MeshVertex triangle[3]) {
-  float min_y = triangle[0].position.y;
-  float max_y = triangle[0].position.y;
-  for (int i = 1; i < 3; i++) {
-    min_y = std::min(min_y, triangle[i].position.y);
-    max_y = std::max(max_y, triangle[i].position.y);
-  }
-  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));
-
-  if (max_yi >= pBitmap->GetHeight())
-    max_yi = pBitmap->GetHeight() - 1;
-
-  for (int y = min_yi; y <= max_yi; y++) {
-    int nIntersects = 0;
-    float inter_x[3];
-    float r[3];
-    float g[3];
-    float b[3];
-    for (int i = 0; i < 3; i++) {
-      CPDF_MeshVertex& vertex1 = triangle[i];
-      CPDF_MeshVertex& vertex2 = triangle[(i + 1) % 3];
-      CFX_PointF& position1 = vertex1.position;
-      CFX_PointF& position2 = vertex2.position;
-      bool bIntersect =
-          GetScanlineIntersect(y, position1, position2, &inter_x[nIntersects]);
-      if (!bIntersect)
-        continue;
-
-      float y_dist = (y - position1.y) / (position2.y - position1.y);
-      r[nIntersects] = vertex1.r + ((vertex2.r - vertex1.r) * y_dist);
-      g[nIntersects] = vertex1.g + ((vertex2.g - vertex1.g) * y_dist);
-      b[nIntersects] = vertex1.b + ((vertex2.b - vertex1.b) * y_dist);
-      nIntersects++;
-    }
-    if (nIntersects != 2)
-      continue;
-
-    int min_x, max_x, start_index, end_index;
-    if (inter_x[0] < inter_x[1]) {
-      min_x = (int)floor(inter_x[0]);
-      max_x = (int)ceil(inter_x[1]);
-      start_index = 0;
-      end_index = 1;
-    } else {
-      min_x = (int)floor(inter_x[1]);
-      max_x = (int)ceil(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;
-    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;
-    for (int x = start_x; x < end_x; x++) {
-      R += r_unit;
-      G += g_unit;
-      B += b_unit;
-      FXARGB_SETDIB(dib_buf,
-                    FXARGB_MAKE(alpha, (int32_t)(R * 255), (int32_t)(G * 255),
-                                (int32_t)(B * 255)));
-      dib_buf += 4;
-    }
-  }
-}
-
-void DrawFreeGouraudShading(
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
-    CFX_Matrix* pObject2Bitmap,
-    CPDF_Stream* pShadingStream,
-    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    CPDF_ColorSpace* pCS,
-    int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
-
-  CPDF_MeshStream stream(kFreeFormGouraudTriangleMeshShading, funcs,
-                         pShadingStream, pCS);
-  if (!stream.Load())
-    return;
-
-  CPDF_MeshVertex triangle[3];
-  memset(triangle, 0, sizeof(triangle));
-
-  while (!stream.BitStream()->IsEOF()) {
-    CPDF_MeshVertex vertex;
-    uint32_t flag;
-    if (!stream.ReadVertex(*pObject2Bitmap, &vertex, &flag))
-      return;
-
-    if (flag == 0) {
-      triangle[0] = vertex;
-      for (int j = 1; j < 3; j++) {
-        uint32_t tflag;
-        if (!stream.ReadVertex(*pObject2Bitmap, &triangle[j], &tflag))
-          return;
-      }
-    } else {
-      if (flag == 1)
-        triangle[0] = triangle[1];
-
-      triangle[1] = triangle[2];
-      triangle[2] = vertex;
-    }
-    DrawGouraud(pBitmap, alpha, triangle);
-  }
-}
-
-void DrawLatticeGouraudShading(
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
-    CFX_Matrix* pObject2Bitmap,
-    CPDF_Stream* pShadingStream,
-    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    CPDF_ColorSpace* pCS,
-    int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
-
-  int row_verts = pShadingStream->GetDict()->GetIntegerFor("VerticesPerRow");
-  if (row_verts < 2)
-    return;
-
-  CPDF_MeshStream stream(kLatticeFormGouraudTriangleMeshShading, funcs,
-                         pShadingStream, pCS);
-  if (!stream.Load())
-    return;
-
-  std::vector<CPDF_MeshVertex> vertices[2];
-  vertices[0] = stream.ReadVertexRow(*pObject2Bitmap, row_verts);
-  if (vertices[0].empty())
-    return;
-
-  int last_index = 0;
-  while (1) {
-    vertices[1 - last_index] = stream.ReadVertexRow(*pObject2Bitmap, row_verts);
-    if (vertices[1 - last_index].empty())
-      return;
-
-    CPDF_MeshVertex triangle[3];
-    for (int i = 1; i < row_verts; ++i) {
-      triangle[0] = vertices[last_index][i];
-      triangle[1] = vertices[1 - last_index][i - 1];
-      triangle[2] = vertices[last_index][i - 1];
-      DrawGouraud(pBitmap, alpha, triangle);
-      triangle[2] = vertices[1 - last_index][i];
-      DrawGouraud(pBitmap, alpha, triangle);
-    }
-    last_index = 1 - last_index;
-  }
-}
-
-struct Coon_BezierCoeff {
-  float a, b, c, d;
-  void FromPoints(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) {
-    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() {
-    float dis = a + b + c;
-    return dis < 0 ? -dis : dis;
-  }
-};
-
-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);
-  }
-
-  Coon_Bezier first_half() {
-    Coon_Bezier result;
-    result.x = x.first_half();
-    result.y = y.first_half();
-    return result;
-  }
-
-  Coon_Bezier second_half() {
-    Coon_Bezier 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(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(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];
-
-    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(); }
-};
-
-int Interpolate(int p1, int p2, int delta1, int delta2, bool* overflow) {
-  pdfium::base::CheckedNumeric<int> p = p2;
-  p -= p1;
-  p *= delta1;
-  p /= delta2;
-  p += p1;
-  if (!p.IsValid())
-    *overflow = true;
-  return p.ValueOrDefault(0);
-}
-
-int BiInterpolImpl(int c0,
-                   int c1,
-                   int c2,
-                   int c3,
-                   int x,
-                   int y,
-                   int x_scale,
-                   int y_scale,
-                   bool* overflow) {
-  int x1 = Interpolate(c0, c3, x, x_scale, overflow);
-  int x2 = Interpolate(c1, c2, x, x_scale, overflow);
-  return Interpolate(x1, x2, y, y_scale, overflow);
-}
-
-struct Coon_Color {
-  Coon_Color() { memset(comp, 0, sizeof(int) * 3); }
-  int comp[3];
-
-  // 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 overflow = false;
-    for (int i = 0; i < 3; i++) {
-      comp[i] = BiInterpolImpl(colors[0].comp[i], colors[1].comp[i],
-                               colors[2].comp[i], colors[3].comp[i], x, y,
-                               x_scale, y_scale, &overflow);
-    }
-    return !overflow;
-  }
-
-  int Distance(Coon_Color& o) {
-    return std::max({abs(comp[0] - o.comp[0]), abs(comp[1] - o.comp[1]),
-                     abs(comp[2] - o.comp[2])});
-  }
-};
-
-#define COONCOLOR_THRESHOLD 4
-struct CPDF_PatchDrawer {
-  Coon_Color patch_colors[4];
-  int max_delta;
-  CFX_PathData path;
-  CFX_RenderDevice* pDevice;
-  int fill_mode;
-  int alpha;
-  void Draw(int x_scale,
-            int y_scale,
-            int left,
-            int bottom,
-            Coon_Bezier C1,
-            Coon_Bezier C2,
-            Coon_Bezier D1,
-            Coon_Bezier D2) {
-    bool bSmall = C1.Distance() < 2 && C2.Distance() < 2 && D1.Distance() < 2 &&
-                  D2.Distance() < 2;
-    Coon_Color div_colors[4];
-    int d_bottom = 0;
-    int d_left = 0;
-    int d_top = 0;
-    int d_right = 0;
-    if (!div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale,
-                                  y_scale)) {
-      return;
-    }
-    if (!bSmall) {
-      if (!div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale,
-                                    y_scale)) {
-        return;
-      }
-      if (!div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale,
-                                    y_scale)) {
-        return;
-      }
-      if (!div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale,
-                                    y_scale)) {
-        return;
-      }
-      d_bottom = div_colors[3].Distance(div_colors[0]);
-      d_left = div_colors[1].Distance(div_colors[0]);
-      d_top = div_colors[1].Distance(div_colors[2]);
-      d_right = div_colors[2].Distance(div_colors[3]);
-    }
-
-    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;
-      if (fill_mode & RENDER_NOPATHSMOOTH) {
-        fillFlags |= FXFILL_NOPATHSMOOTH;
-      }
-      pDevice->DrawPath(
-          &path, nullptr, nullptr,
-          FXARGB_MAKE(alpha, div_colors[0].comp[0], div_colors[0].comp[1],
-                      div_colors[0].comp[2]),
-          0, fillFlags);
-    } else {
-      if (d_bottom < COONCOLOR_THRESHOLD && d_top < COONCOLOR_THRESHOLD) {
-        Coon_Bezier m1;
-        m1.BezierInterpol(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);
-        x_scale *= 2;
-        left *= 2;
-        Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(),
-             D1, m2);
-        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();
-        x_scale *= 2;
-        y_scale *= 2;
-        left *= 2;
-        bottom *= 2;
-        Draw(x_scale, y_scale, left, bottom, C1.first_half(), m1f,
-             D1.first_half(), m2f);
-        Draw(x_scale, y_scale, left, bottom + 1, m1f, C2.first_half(),
-             D1.second_half(), m2s);
-        Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), m1s, m2f,
-             D2.first_half());
-        Draw(x_scale, y_scale, left + 1, bottom + 1, m1s, C2.second_half(), m2s,
-             D2.second_half());
-      }
-    }
-  }
-};
-
-void DrawCoonPatchMeshes(
-    ShadingType type,
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
-    CFX_Matrix* pObject2Bitmap,
-    CPDF_Stream* pShadingStream,
-    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    CPDF_ColorSpace* pCS,
-    int fill_mode,
-    int alpha) {
-  ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
-  ASSERT(type == kCoonsPatchMeshShading ||
-         type == kTensorProductPatchMeshShading);
-
-  CFX_DefaultRenderDevice device;
-  device.Attach(pBitmap, false, nullptr, false);
-  CPDF_MeshStream stream(type, funcs, pShadingStream, pCS);
-  if (!stream.Load())
-    return;
-
-  CPDF_PatchDrawer patch;
-  patch.alpha = alpha;
-  patch.pDevice = &device;
-  patch.fill_mode = fill_mode;
-
-  for (int i = 0; i < 13; i++) {
-    patch.path.AppendPoint(
-        CFX_PointF(), i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::BezierTo, false);
-  }
-
-  CFX_PointF coords[16];
-  int point_count = type == kTensorProductPatchMeshShading ? 16 : 12;
-  while (!stream.BitStream()->IsEOF()) {
-    if (!stream.CanReadFlag())
-      break;
-    uint32_t flag = stream.ReadFlag();
-    int iStartPoint = 0, iStartColor = 0, i = 0;
-    if (flag) {
-      iStartPoint = 4;
-      iStartColor = 2;
-      CFX_PointF tempCoords[4];
-      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);
-    }
-    for (i = iStartPoint; i < point_count; i++) {
-      if (!stream.CanReadCoords())
-        break;
-      coords[i] = pObject2Bitmap->Transform(stream.ReadCoords());
-    }
-
-    for (i = iStartColor; i < 4; i++) {
-      if (!stream.CanReadColor())
-        break;
-
-      float r;
-      float g;
-      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);
-    }
-    CFX_FloatRect bbox = CFX_FloatRect::GetBBox(coords, 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);
-    patch.Draw(1, 1, 0, 0, C1, C2, D1, D2);
-  }
-}
-
-RetainPtr<CFX_DIBitmap> DrawPatternBitmap(CPDF_Document* pDoc,
-                                          CPDF_PageRenderCache* pCache,
-                                          CPDF_TilingPattern* pPattern,
-                                          const CFX_Matrix* pObject2Device,
-                                          int width,
-                                          int height,
-                                          int flags) {
+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)) {
@@ -928,23 +94,22 @@
   bitmap_device.Attach(pBitmap, false, nullptr, false);
   pBitmap->Clear(0);
   CFX_FloatRect cell_bbox =
-      pPattern->pattern_to_form()->TransformRect(pPattern->bbox());
-  cell_bbox = pObject2Device->TransformRect(cell_bbox);
-  CFX_FloatRect bitmap_rect(0.0f, 0.0f, (float)width, (float)height);
+      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 = *pObject2Device;
-  mtPattern2Bitmap.Concat(mtAdjust);
+  CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
   CPDF_RenderOptions options;
   if (!pPattern->colored())
     options.SetColorMode(CPDF_RenderOptions::kAlpha);
 
-  flags |= RENDER_FORCE_HALFTONE;
-  options.SetFlags(flags);
+  options.GetOptions() = draw_options;
+  options.GetOptions().bForceHalftone = true;
 
-  CPDF_RenderContext context(pDoc, pCache);
-  context.AppendLayer(pPattern->form(), &mtPattern2Bitmap);
+  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);
@@ -973,107 +138,72 @@
 
 bool Type3CharMissingFillColor(const CPDF_Type3Char* pChar,
                                const CPDF_ColorState* pColorState) {
-  return pChar && (!pChar->colored() ||
-                   (pChar->colored() && MissingFillColor(pColorState)));
+  return pChar && (!pChar->colored() || MissingFillColor(pColorState));
 }
 
 bool Type3CharMissingStrokeColor(const CPDF_Type3Char* pChar,
                                  const CPDF_ColorState* pColorState) {
-  return pChar && (!pChar->colored() ||
-                   (pChar->colored() && MissingStrokeColor(pColorState)));
+  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
 
-// static
-int CPDF_RenderStatus::s_CurrentRecursionDepth = 0;
-
-CPDF_RenderStatus::CPDF_RenderStatus()
-    : m_pFormResource(nullptr),
-      m_pPageResource(nullptr),
-      m_pContext(nullptr),
-      m_bStopped(false),
-      m_pDevice(nullptr),
-      m_pCurObj(nullptr),
-      m_pStopObj(nullptr),
-      m_bPrint(false),
-      m_iTransparency(0),
-      m_bDropObjects(false),
-      m_bStdCS(false),
-      m_GroupFamily(0),
-      m_bLoadMask(false),
-      m_pType3Char(nullptr),
-      m_T3FillColor(0),
-      m_curBlend(FXDIB_BLEND_NORMAL) {}
+CPDF_RenderStatus::CPDF_RenderStatus(CPDF_RenderContext* pContext,
+                                     CFX_RenderDevice* pDevice)
+    : m_pContext(pContext), m_pDevice(pDevice) {}
 
 CPDF_RenderStatus::~CPDF_RenderStatus() {}
 
-bool CPDF_RenderStatus::Initialize(CPDF_RenderContext* pContext,
-                                   CFX_RenderDevice* pDevice,
-                                   const CFX_Matrix* pDeviceMatrix,
-                                   const CPDF_PageObject* pStopObj,
-                                   const CPDF_RenderStatus* pParentState,
-                                   const CPDF_GraphicStates* pInitialStates,
-                                   const CPDF_RenderOptions* pOptions,
-                                   int transparency,
-                                   bool bDropObjects,
-                                   CPDF_Dictionary* pFormResource,
-                                   bool bStdCS,
-                                   CPDF_Type3Char* pType3Char,
-                                   FX_ARGB fill_color,
-                                   uint32_t GroupFamily,
-                                   bool bLoadMask) {
-  m_pContext = pContext;
-  m_pDevice = pDevice;
-  m_bPrint = m_pDevice->GetDeviceClass() != FXDC_DISPLAY;
-  if (pDeviceMatrix) {
-    m_DeviceMatrix = *pDeviceMatrix;
-  }
-  m_pStopObj = pStopObj;
-  if (pOptions) {
-    m_Options = *pOptions;
-  }
-  m_bDropObjects = bDropObjects;
-  m_bStdCS = bStdCS;
-  m_T3FillColor = fill_color;
-  m_pType3Char = pType3Char;
-  m_GroupFamily = GroupFamily;
-  m_bLoadMask = bLoadMask;
-  m_pFormResource = pFormResource;
-  m_pPageResource = m_pContext->GetPageResources();
+void CPDF_RenderStatus::Initialize(const CPDF_RenderStatus* pParentStatus,
+                                   const CPDF_GraphicStates* pInitialStates) {
+  m_bPrint = m_pDevice->GetDeviceType() != DeviceType::kDisplay;
+  m_pPageResource.Reset(m_pContext->GetPageResources());
   if (pInitialStates && !m_pType3Char) {
     m_InitialStates.CopyStates(*pInitialStates);
-    if (pParentState) {
+    if (pParentStatus) {
       if (!m_InitialStates.m_ColorState.HasFillColor()) {
-        m_InitialStates.m_ColorState.SetFillRGB(
-            pParentState->m_InitialStates.m_ColorState.GetFillRGB());
-        m_InitialStates.m_ColorState.GetMutableFillColor()->Copy(
-            pParentState->m_InitialStates.m_ColorState.GetFillColor());
+        m_InitialStates.m_ColorState.SetFillColorRef(
+            pParentStatus->m_InitialStates.m_ColorState.GetFillColorRef());
+        *m_InitialStates.m_ColorState.GetMutableFillColor() =
+            *pParentStatus->m_InitialStates.m_ColorState.GetFillColor();
       }
       if (!m_InitialStates.m_ColorState.HasStrokeColor()) {
-        m_InitialStates.m_ColorState.SetStrokeRGB(
-            pParentState->m_InitialStates.m_ColorState.GetFillRGB());
-        m_InitialStates.m_ColorState.GetMutableStrokeColor()->Copy(
-            pParentState->m_InitialStates.m_ColorState.GetStrokeColor());
+        m_InitialStates.m_ColorState.SetStrokeColorRef(
+            pParentStatus->m_InitialStates.m_ColorState.GetFillColorRef());
+        *m_InitialStates.m_ColorState.GetMutableStrokeColor() =
+            *pParentStatus->m_InitialStates.m_ColorState.GetStrokeColor();
       }
     }
   } else {
     m_InitialStates.DefaultStates();
   }
-  m_pImageRenderer.reset();
-  m_iTransparency = transparency;
-  return true;
 }
 
 void CPDF_RenderStatus::RenderObjectList(
     const CPDF_PageObjectHolder* pObjectHolder,
-    const CFX_Matrix* pObj2Device) {
+    const CFX_Matrix& mtObj2Device) {
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
-  CFX_FloatRect clip_rect = pObj2Device->GetInverse().TransformRect(
+  CFX_FloatRect clip_rect = mtObj2Device.GetInverse().TransformRect(
       CFX_FloatRect(m_pDevice->GetClipBox()));
-  for (const auto& pCurObj : *pObjectHolder->GetPageObjectList()) {
+  for (const auto& pCurObj : *pObjectHolder) {
     if (pCurObj.get() == m_pStopObj) {
       m_bStopped = true;
       return;
@@ -1081,13 +211,13 @@
     if (!pCurObj)
       continue;
 
-    if (pCurObj->m_Left > clip_rect.right ||
-        pCurObj->m_Right < clip_rect.left ||
-        pCurObj->m_Bottom > clip_rect.top ||
-        pCurObj->m_Top < clip_rect.bottom) {
+    if (pCurObj->GetRect().left > clip_rect.right ||
+        pCurObj->GetRect().right < clip_rect.left ||
+        pCurObj->GetRect().bottom > clip_rect.top ||
+        pCurObj->GetRect().top < clip_rect.bottom) {
       continue;
     }
-    RenderSingleObject(pCurObj.get(), pObj2Device);
+    RenderSingleObject(pCurObj.get(), mtObj2Device);
     if (m_bStopped)
       return;
   }
@@ -1097,202 +227,175 @@
 }
 
 void CPDF_RenderStatus::RenderSingleObject(CPDF_PageObject* pObj,
-                                           const CFX_Matrix* pObj2Device) {
+                                           const CFX_Matrix& mtObj2Device) {
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
-  AutoRestorer<int> restorer(&s_CurrentRecursionDepth);
-  if (++s_CurrentRecursionDepth > kRenderMaxRecursionDepth) {
+  AutoRestorer<int> restorer(&g_CurrentRecursionDepth);
+  if (++g_CurrentRecursionDepth > kRenderMaxRecursionDepth) {
     return;
   }
   m_pCurObj = pObj;
-  if (m_Options.GetOCContext() && pObj->m_ContentMark.HasRef()) {
-    if (!m_Options.GetOCContext()->CheckObjectVisible(pObj)) {
-      return;
-    }
-  }
-  ProcessClipPath(pObj->m_ClipPath, pObj2Device);
-  if (ProcessTransparency(pObj, pObj2Device)) {
+  if (m_Options.GetOCContext() &&
+      !m_Options.GetOCContext()->CheckObjectVisible(pObj)) {
     return;
   }
-  ProcessObjectNoClip(pObj, pObj2Device);
+  ProcessClipPath(pObj->m_ClipPath, mtObj2Device);
+  if (ProcessTransparency(pObj, mtObj2Device)) {
+    return;
+  }
+  ProcessObjectNoClip(pObj, mtObj2Device);
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
 }
 
 bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj,
-                                             const CFX_Matrix* pObj2Device,
-                                             IFX_PauseIndicator* pPause) {
+                                             const CFX_Matrix& mtObj2Device,
+                                             PauseIndicatorIface* pPause) {
   if (m_pImageRenderer) {
     if (m_pImageRenderer->Continue(pPause))
       return true;
 
     if (!m_pImageRenderer->GetResult())
-      DrawObjWithBackground(pObj, pObj2Device);
+      DrawObjWithBackground(pObj, mtObj2Device);
     m_pImageRenderer.reset();
     return false;
   }
 
   m_pCurObj = pObj;
-  if (m_Options.GetOCContext() && pObj->m_ContentMark.HasRef() &&
+  if (m_Options.GetOCContext() &&
       !m_Options.GetOCContext()->CheckObjectVisible(pObj)) {
     return false;
   }
 
-  ProcessClipPath(pObj->m_ClipPath, pObj2Device);
-  if (ProcessTransparency(pObj, pObj2Device))
+  ProcessClipPath(pObj->m_ClipPath, mtObj2Device);
+  if (ProcessTransparency(pObj, mtObj2Device))
     return false;
 
   if (!pObj->IsImage()) {
-    ProcessObjectNoClip(pObj, pObj2Device);
+    ProcessObjectNoClip(pObj, mtObj2Device);
     return false;
   }
 
   m_pImageRenderer = pdfium::MakeUnique<CPDF_ImageRenderer>();
-  if (!m_pImageRenderer->Start(this, pObj->AsImage(), pObj2Device, false,
-                               FXDIB_BLEND_NORMAL)) {
+  if (!m_pImageRenderer->Start(this, pObj->AsImage(), mtObj2Device, false,
+                               BlendMode::kNormal)) {
     if (!m_pImageRenderer->GetResult())
-      DrawObjWithBackground(pObj, pObj2Device);
+      DrawObjWithBackground(pObj, mtObj2Device);
     m_pImageRenderer.reset();
     return false;
   }
-  return ContinueSingleObject(pObj, pObj2Device, pPause);
+  return ContinueSingleObject(pObj, mtObj2Device, pPause);
 }
 
-bool CPDF_RenderStatus::GetObjectClippedRect(const CPDF_PageObject* pObj,
-                                             const CFX_Matrix* pObj2Device,
-                                             bool bLogical,
-                                             FX_RECT& rect) const {
-  rect = pObj->GetBBox(pObj2Device);
-  FX_RECT rtClip = m_pDevice->GetClipBox();
-  if (!bLogical) {
-    CFX_Matrix dCTM = m_pDevice->GetCTM();
-    float a = fabs(dCTM.a);
-    float d = fabs(dCTM.d);
-    if (a != 1.0f || d != 1.0f) {
-      rect.right = rect.left + (int32_t)ceil((float)rect.Width() * a);
-      rect.bottom = rect.top + (int32_t)ceil((float)rect.Height() * d);
-      rtClip.right = rtClip.left + (int32_t)ceil((float)rtClip.Width() * a);
-      rtClip.bottom = rtClip.top + (int32_t)ceil((float)rtClip.Height() * d);
-    }
-  }
-  rect.Intersect(rtClip);
-  return rect.IsEmpty();
+FX_RECT CPDF_RenderStatus::GetObjectClippedRect(
+    const CPDF_PageObject* pObj,
+    const CFX_Matrix& mtObj2Device) const {
+  FX_RECT rect = pObj->GetTransformedBBox(mtObj2Device);
+  rect.Intersect(m_pDevice->GetClipBox());
+  return rect;
 }
 
 void CPDF_RenderStatus::ProcessObjectNoClip(CPDF_PageObject* pObj,
-                                            const CFX_Matrix* pObj2Device) {
+                                            const CFX_Matrix& mtObj2Device) {
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
   bool bRet = false;
   switch (pObj->GetType()) {
     case CPDF_PageObject::TEXT:
-      bRet = ProcessText(pObj->AsText(), pObj2Device, nullptr);
+      bRet = ProcessText(pObj->AsText(), mtObj2Device, nullptr);
       break;
     case CPDF_PageObject::PATH:
-      bRet = ProcessPath(pObj->AsPath(), pObj2Device);
+      bRet = ProcessPath(pObj->AsPath(), mtObj2Device);
       break;
     case CPDF_PageObject::IMAGE:
-      bRet = ProcessImage(pObj->AsImage(), pObj2Device);
+      bRet = ProcessImage(pObj->AsImage(), mtObj2Device);
       break;
     case CPDF_PageObject::SHADING:
-      ProcessShading(pObj->AsShading(), pObj2Device);
+      ProcessShading(pObj->AsShading(), mtObj2Device);
       return;
     case CPDF_PageObject::FORM:
-      bRet = ProcessForm(pObj->AsForm(), pObj2Device);
+      bRet = ProcessForm(pObj->AsForm(), mtObj2Device);
       break;
   }
   if (!bRet)
-    DrawObjWithBackground(pObj, pObj2Device);
+    DrawObjWithBackground(pObj, mtObj2Device);
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
 }
 
 bool CPDF_RenderStatus::DrawObjWithBlend(CPDF_PageObject* pObj,
-                                         const CFX_Matrix* pObj2Device) {
-  bool bRet = false;
+                                         const CFX_Matrix& mtObj2Device) {
   switch (pObj->GetType()) {
     case CPDF_PageObject::PATH:
-      bRet = ProcessPath(pObj->AsPath(), pObj2Device);
-      break;
+      return ProcessPath(pObj->AsPath(), mtObj2Device);
     case CPDF_PageObject::IMAGE:
-      bRet = ProcessImage(pObj->AsImage(), pObj2Device);
-      break;
+      return ProcessImage(pObj->AsImage(), mtObj2Device);
     case CPDF_PageObject::FORM:
-      bRet = ProcessForm(pObj->AsForm(), pObj2Device);
-      break;
+      return ProcessForm(pObj->AsForm(), mtObj2Device);
     default:
-      break;
+      return false;
   }
-  return bRet;
-}
-
-void CPDF_RenderStatus::GetScaledMatrix(CFX_Matrix& matrix) const {
-  CFX_Matrix dCTM = m_pDevice->GetCTM();
-  matrix.a *= fabs(dCTM.a);
-  matrix.d *= fabs(dCTM.d);
 }
 
 void CPDF_RenderStatus::DrawObjWithBackground(CPDF_PageObject* pObj,
-                                              const CFX_Matrix* pObj2Device) {
-  FX_RECT rect;
-  if (GetObjectClippedRect(pObj, pObj2Device, false, rect)) {
+                                              const CFX_Matrix& mtObj2Device) {
+  FX_RECT rect = GetObjectClippedRect(pObj, mtObj2Device);
+  if (rect.IsEmpty())
     return;
-  }
+
   int res = 300;
-  if (pObj->IsImage() &&
-      m_pDevice->GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
+  if (pObj->IsImage() && m_pDevice->GetDeviceType() == DeviceType::kPrinter)
     res = 0;
-  }
+
   CPDF_ScaledRenderBuffer buffer;
   if (!buffer.Initialize(m_pContext.Get(), m_pDevice, rect, pObj, &m_Options,
                          res)) {
     return;
   }
-  CFX_Matrix matrix = *pObj2Device;
-  matrix.Concat(*buffer.GetMatrix());
-  GetScaledMatrix(matrix);
-  CPDF_Dictionary* pFormResource = nullptr;
+  CFX_Matrix matrix = mtObj2Device * buffer.GetMatrix();
+  const CPDF_Dictionary* pFormResource = nullptr;
   const CPDF_FormObject* pFormObj = pObj->AsForm();
-  if (pFormObj) {
-    const auto& pFormDict = pFormObj->form()->m_pFormDict;
-    if (pFormDict)
-      pFormResource = pFormDict->GetDictFor("Resources");
-  }
-  CPDF_RenderStatus status;
-  status.Initialize(m_pContext.Get(), buffer.GetDevice(), buffer.GetMatrix(),
-                    nullptr, nullptr, nullptr, &m_Options, m_iTransparency,
-                    m_bDropObjects, pFormResource);
-  status.RenderSingleObject(pObj, &matrix);
+  if (pFormObj)
+    pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
+  CPDF_RenderStatus status(m_pContext.Get(), buffer.GetDevice());
+  status.SetOptions(m_Options);
+  status.SetDeviceMatrix(buffer.GetMatrix());
+  status.SetTransparency(m_Transparency);
+  status.SetDropObjects(m_bDropObjects);
+  status.SetFormResource(pFormResource);
+  status.Initialize(nullptr, nullptr);
+  status.RenderSingleObject(pObj, matrix);
   buffer.OutputToDevice();
 }
 
 bool CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj,
-                                    const CFX_Matrix* pObj2Device) {
+                                    const CFX_Matrix& mtObj2Device) {
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
-  CPDF_Dictionary* pOC = pFormObj->form()->m_pFormDict->GetDictFor("OC");
+  const CPDF_Dictionary* pOC = pFormObj->form()->GetDict()->GetDictFor("OC");
   if (pOC && m_Options.GetOCContext() &&
       !m_Options.GetOCContext()->CheckOCGVisible(pOC)) {
     return true;
   }
-  CFX_Matrix matrix = pFormObj->form_matrix();
-  matrix.Concat(*pObj2Device);
-  const auto& pFormDict = pFormObj->form()->m_pFormDict;
-  CPDF_Dictionary* pResources =
-      pFormDict ? pFormDict->GetDictFor("Resources") : nullptr;
-  CPDF_RenderStatus status;
-  status.Initialize(m_pContext.Get(), m_pDevice, nullptr, m_pStopObj, this,
-                    pFormObj, &m_Options, m_iTransparency, m_bDropObjects,
-                    pResources, false);
+  CFX_Matrix matrix = pFormObj->form_matrix() * mtObj2Device;
+  const CPDF_Dictionary* pResources =
+      pFormObj->form()->GetDict()->GetDictFor("Resources");
+  CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
+  status.SetOptions(m_Options);
+  status.SetStopObject(m_pStopObj.Get());
+  status.SetTransparency(m_Transparency);
+  status.SetDropObjects(m_bDropObjects);
+  status.SetFormResource(pResources);
+  status.Initialize(this, pFormObj);
   status.m_curBlend = m_curBlend;
   {
     CFX_RenderDevice::StateRestorer restorer(m_pDevice);
-    status.RenderObjectList(pFormObj->form(), &matrix);
+    status.RenderObjectList(pFormObj->form(), matrix);
     m_bStopped = status.m_bStopped;
   }
 #if defined _SKIA_SUPPORT_
@@ -1302,25 +405,24 @@
 }
 
 bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* pPathObj,
-                                    const CFX_Matrix* pObj2Device) {
-  int FillType = pPathObj->m_FillType;
-  bool bStroke = pPathObj->m_bStroke;
-  ProcessPathPattern(pPathObj, pObj2Device, FillType, bStroke);
+                                    const CFX_Matrix& mtObj2Device) {
+  int FillType = pPathObj->filltype();
+  bool bStroke = pPathObj->stroke();
+  ProcessPathPattern(pPathObj, mtObj2Device, &FillType, &bStroke);
   if (FillType == 0 && !bStroke)
     return true;
 
   uint32_t fill_argb = FillType ? GetFillArgb(pPathObj) : 0;
   uint32_t stroke_argb = bStroke ? GetStrokeArgb(pPathObj) : 0;
-  CFX_Matrix path_matrix = pPathObj->m_Matrix;
-  path_matrix.Concat(*pObj2Device);
+  CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device;
   if (!IsAvailableMatrix(path_matrix))
     return true;
 
-  if (FillType && (m_Options.HasFlag(RENDER_RECT_AA)))
+  if (FillType && m_Options.GetOptions().bRectAA)
     FillType |= FXFILL_RECT_AA;
-  if (m_Options.HasFlag(RENDER_FILL_FULLCOVER))
+  if (m_Options.GetOptions().bFillFullcover)
     FillType |= FXFILL_FULLCOVER;
-  if (m_Options.HasFlag(RENDER_NOPATHSMOOTH))
+  if (m_Options.GetOptions().bNoPathSmooth)
     FillType |= FXFILL_NOPATHSMOOTH;
   if (bStroke)
     FillType |= FX_FILL_STROKE;
@@ -1333,22 +435,22 @@
     FillType |= FX_FILL_TEXT_MODE;
 
   CFX_GraphState graphState = pPathObj->m_GraphState;
-  if (m_Options.HasFlag(RENDER_THINLINE))
+  if (m_Options.GetOptions().bThinLine)
     graphState.SetLineWidth(0);
   return m_pDevice->DrawPathWithBlend(
-      pPathObj->m_Path.GetObject(), &path_matrix, graphState.GetObject(),
+      pPathObj->path().GetObject(), &path_matrix, graphState.GetObject(),
       fill_argb, stroke_argb, FillType, m_curBlend);
 }
 
 RetainPtr<CPDF_TransferFunc> CPDF_RenderStatus::GetTransferFunc(
-    CPDF_Object* pObj) const {
+    const CPDF_Object* pObj) const {
   ASSERT(pObj);
-  CPDF_DocRenderData* pDocCache = m_pContext->GetDocument()->GetRenderData();
+  auto* pDocCache = CPDF_DocRenderData::FromDocument(m_pContext->GetDocument());
   return pDocCache ? pDocCache->GetTransferFunc(pObj) : nullptr;
 }
 
-FX_ARGB CPDF_RenderStatus::GetFillArgb(CPDF_PageObject* pObj,
-                                       bool bType3) const {
+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))
     return m_T3FillColor;
@@ -1356,8 +458,8 @@
   if (MissingFillColor(pColorState))
     pColorState = &m_InitialStates.m_ColorState;
 
-  FX_COLORREF rgb = pColorState->GetFillRGB();
-  if (rgb == (uint32_t)-1)
+  FX_COLORREF colorref = pColorState->GetFillColorRef();
+  if (colorref == 0xFFFFFFFF)
     return 0;
 
   int32_t alpha =
@@ -1367,10 +469,12 @@
       pObj->m_GeneralState.SetTransferFunc(
           GetTransferFunc(pObj->m_GeneralState.GetTR()));
     }
-    if (pObj->m_GeneralState.GetTransferFunc())
-      rgb = pObj->m_GeneralState.GetTransferFunc()->TranslateColor(rgb);
+    if (pObj->m_GeneralState.GetTransferFunc()) {
+      colorref =
+          pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref);
+    }
   }
-  return m_Options.TranslateColor(ArgbEncode(alpha, rgb));
+  return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref));
 }
 
 FX_ARGB CPDF_RenderStatus::GetStrokeArgb(CPDF_PageObject* pObj) const {
@@ -1381,8 +485,8 @@
   if (MissingStrokeColor(pColorState))
     pColorState = &m_InitialStates.m_ColorState;
 
-  FX_COLORREF rgb = pColorState->GetStrokeRGB();
-  if (rgb == (uint32_t)-1)
+  FX_COLORREF colorref = pColorState->GetStrokeColorRef();
+  if (colorref == 0xFFFFFFFF)
     return 0;
 
   int32_t alpha = static_cast<int32_t>(pObj->m_GeneralState.GetStrokeAlpha() *
@@ -1392,14 +496,16 @@
       pObj->m_GeneralState.SetTransferFunc(
           GetTransferFunc(pObj->m_GeneralState.GetTR()));
     }
-    if (pObj->m_GeneralState.GetTransferFunc())
-      rgb = pObj->m_GeneralState.GetTransferFunc()->TranslateColor(rgb);
+    if (pObj->m_GeneralState.GetTransferFunc()) {
+      colorref =
+          pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref);
+    }
   }
-  return m_Options.TranslateColor(ArgbEncode(alpha, rgb));
+  return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref));
 }
 
 void CPDF_RenderStatus::ProcessClipPath(const CPDF_ClipPath& ClipPath,
-                                        const CFX_Matrix* pObj2Device) {
+                                        const CFX_Matrix& mtObj2Device) {
   if (!ClipPath.HasRef()) {
     if (m_LastClipPath.HasRef()) {
       m_pDevice->RestoreState(true);
@@ -1422,7 +528,7 @@
       EmptyPath.AppendRect(-1, -1, 0, 0);
       m_pDevice->SetClip_PathFill(&EmptyPath, nullptr, FXFILL_WINDING);
     } else {
-      m_pDevice->SetClip_PathFill(pPathData, pObj2Device,
+      m_pDevice->SetClip_PathFill(pPathData, &mtObj2Device,
                                   ClipPath.GetClipType(i));
     }
   }
@@ -1430,7 +536,7 @@
   if (ClipPath.GetTextCount() == 0)
     return;
 
-  if (m_pDevice->GetDeviceClass() == FXDC_DISPLAY &&
+  if (m_pDevice->GetDeviceType() == DeviceType::kDisplay &&
       !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) {
     return;
   }
@@ -1441,7 +547,7 @@
     if (pText) {
       if (!pTextClippingPath)
         pTextClippingPath = pdfium::MakeUnique<CFX_PathData>();
-      ProcessText(pText, pObj2Device, pTextClippingPath.get());
+      ProcessText(pText, mtObj2Device, pTextClippingPath.get());
       continue;
     }
 
@@ -1449,42 +555,50 @@
       continue;
 
     int fill_mode = FXFILL_WINDING;
-    if (m_Options.HasFlag(RENDER_NOTEXTSMOOTH))
+    if (m_Options.GetOptions().bNoTextSmooth)
       fill_mode |= FXFILL_NOPATHSMOOTH;
     m_pDevice->SetClip_PathFill(pTextClippingPath.get(), nullptr, fill_mode);
     pTextClippingPath.reset();
   }
 }
 
+bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* pPageObj,
+                                    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));
+    return true;
+  }
+  return false;
+}
+
 bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* pPathObj,
-                                       const CFX_Matrix* pObj2Device,
+                                       const CFX_Matrix& mtObj2Device,
                                        bool bStroke) {
-  CFX_Matrix path_matrix = pPathObj->m_Matrix;
-  path_matrix.Concat(*pObj2Device);
+  CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device;
   if (bStroke) {
     CFX_GraphState graphState = pPathObj->m_GraphState;
-    if (m_Options.HasFlag(RENDER_THINLINE))
+    if (m_Options.GetOptions().bThinLine)
       graphState.SetLineWidth(0);
-    return m_pDevice->SetClip_PathStroke(pPathObj->m_Path.GetObject(),
+    return m_pDevice->SetClip_PathStroke(pPathObj->path().GetObject(),
                                          &path_matrix, graphState.GetObject());
   }
-  int fill_mode = pPathObj->m_FillType;
-  if (m_Options.HasFlag(RENDER_NOPATHSMOOTH)) {
+  int fill_mode = pPathObj->filltype();
+  if (m_Options.GetOptions().bNoPathSmooth) {
     fill_mode |= FXFILL_NOPATHSMOOTH;
   }
-  return m_pDevice->SetClip_PathFill(pPathObj->m_Path.GetObject(), &path_matrix,
+  return m_pDevice->SetClip_PathFill(pPathObj->path().GetObject(), &path_matrix,
                                      fill_mode);
 }
 
 bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj,
-                                            const CFX_Matrix* pObj2Device) {
+                                            const CFX_Matrix& mtObj2Device) {
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
-  int blend_type = pPageObj->m_GeneralState.GetBlendType();
-  if (blend_type == FXDIB_BLEND_UNSUPPORTED)
-    return true;
-
+  BlendMode blend_type = pPageObj->m_GeneralState.GetBlendType();
   CPDF_Dictionary* pSMaskDict =
       ToDictionary(pPageObj->m_GeneralState.GetSoftMask());
   if (pSMaskDict) {
@@ -1493,100 +607,92 @@
       pSMaskDict = nullptr;
     }
   }
-  CPDF_Dictionary* pFormResource = nullptr;
+  const CPDF_Dictionary* pFormResource = nullptr;
   float group_alpha = 1.0f;
-  int iTransparency = m_iTransparency;
+  CPDF_Transparency transparency = m_Transparency;
   bool bGroupTransparent = false;
   const CPDF_FormObject* pFormObj = pPageObj->AsForm();
   if (pFormObj) {
     group_alpha = pFormObj->m_GeneralState.GetFillAlpha();
-    iTransparency = pFormObj->form()->m_iTransparency;
-    bGroupTransparent = !!(iTransparency & PDFTRANS_ISOLATED);
-    const auto& pFormDict = pFormObj->form()->m_pFormDict;
-    if (pFormDict)
-      pFormResource = pFormDict->GetDictFor("Resources");
+    transparency = pFormObj->form()->GetTransparency();
+    bGroupTransparent = transparency.IsIsolated();
+    pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
   }
   bool bTextClip =
       (pPageObj->m_ClipPath.HasRef() &&
        pPageObj->m_ClipPath.GetTextCount() > 0 &&
-       m_pDevice->GetDeviceClass() == FXDC_DISPLAY &&
+       m_pDevice->GetDeviceType() == DeviceType::kDisplay &&
        !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP));
-  if ((m_Options.HasFlag(RENDER_OVERPRINT)) && pPageObj->IsImage() &&
+  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->m_pDocument.Get();
+      pDocument = pPage->GetDocument();
     } else {
       pDocument = pPageObj->AsImage()->GetImage()->GetDocument();
     }
-    CPDF_Dictionary* pPageResources =
+    const CPDF_Dictionary* pPageResources =
         pPage ? pPage->m_pPageResources.Get() : nullptr;
-    CPDF_Object* pCSObj = pPageObj->AsImage()
-                              ->GetImage()
-                              ->GetStream()
-                              ->GetDict()
-                              ->GetDirectObjectFor("ColorSpace");
-    CPDF_ColorSpace* pColorSpace =
-        pDocument->LoadColorSpace(pCSObj, pPageResources);
+    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 = FXDIB_BLEND_DARKEN;
+        blend_type = BlendMode::kDarken;
       }
-      pDocument->GetPageData()->ReleaseColorSpace(pCSObj);
     }
   }
-  if (!pSMaskDict && group_alpha == 1.0f && blend_type == FXDIB_BLEND_NORMAL &&
+  if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal &&
       !bTextClip && !bGroupTransparent) {
     return false;
   }
-  bool isolated = !!(iTransparency & PDFTRANS_ISOLATED);
   if (m_bPrint) {
     bool bRet = false;
     int rendCaps = m_pDevice->GetRenderCaps();
-    if (!((iTransparency & PDFTRANS_ISOLATED) || pSMaskDict || bTextClip) &&
+    if (!(transparency.IsIsolated() || pSMaskDict || bTextClip) &&
         (rendCaps & FXRC_BLEND_MODE)) {
-      int oldBlend = m_curBlend;
+      BlendMode oldBlend = m_curBlend;
       m_curBlend = blend_type;
-      bRet = DrawObjWithBlend(pPageObj, pObj2Device);
+      bRet = DrawObjWithBlend(pPageObj, mtObj2Device);
       m_curBlend = oldBlend;
     }
     if (!bRet) {
-      DrawObjWithBackground(pPageObj, pObj2Device);
+      DrawObjWithBackground(pPageObj, mtObj2Device);
     }
     return true;
   }
-  FX_RECT rect = pPageObj->GetBBox(pObj2Device);
+  FX_RECT rect = pPageObj->GetTransformedBBox(mtObj2Device);
   rect.Intersect(m_pDevice->GetClipBox());
   if (rect.IsEmpty())
     return true;
 
-  CFX_Matrix deviceCTM = m_pDevice->GetCTM();
-  float scaleX = fabs(deviceCTM.a);
-  float scaleY = fabs(deviceCTM.d);
-  int width = FXSYS_round((float)rect.Width() * scaleX);
-  int height = FXSYS_round((float)rect.Height() * scaleY);
+  int width = rect.Width();
+  int height = rect.Height();
   CFX_DefaultRenderDevice bitmap_device;
-  RetainPtr<CFX_DIBitmap> oriDevice;
-  if (!isolated && (m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
-    oriDevice = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (!m_pDevice->CreateCompatibleBitmap(oriDevice, width, height))
+  RetainPtr<CFX_DIBitmap> backdrop;
+  if (!transparency.IsIsolated() &&
+      (m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
+    backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
+    if (!m_pDevice->CreateCompatibleBitmap(backdrop, width, height))
       return true;
-    m_pDevice->GetDIBits(oriDevice, rect.left, rect.top);
+    m_pDevice->GetDIBits(backdrop, rect.left, rect.top);
   }
-  if (!bitmap_device.Create(width, height, FXDIB_Argb, oriDevice))
+  if (!bitmap_device.Create(width, height, FXDIB_Argb, backdrop))
     return true;
 
   RetainPtr<CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
   bitmap->Clear(0);
 
-  CFX_Matrix new_matrix = *pObj2Device;
+  CFX_Matrix new_matrix = mtObj2Device;
   new_matrix.Translate(-rect.left, -rect.top);
-  new_matrix.Scale(scaleX, scaleY);
 
   RetainPtr<CFX_DIBitmap> pTextMask;
   if (bTextClip) {
@@ -1602,28 +708,32 @@
       if (!textobj)
         break;
 
-      CFX_Matrix text_matrix = textobj->GetTextMatrix();
+      // TODO(thestig): Should we check the return value here?
       CPDF_TextRenderer::DrawTextPath(
           &text_device, textobj->GetCharCodes(), textobj->GetCharPositions(),
-          textobj->m_TextState.GetFont(), textobj->m_TextState.GetFontSize(),
-          &text_matrix, &new_matrix, textobj->m_GraphState.GetObject(),
-          (FX_ARGB)-1, 0, nullptr, 0);
+          textobj->m_TextState.GetFont().Get(),
+          textobj->m_TextState.GetFontSize(), textobj->GetTextMatrix(),
+          &new_matrix, textobj->m_GraphState.GetObject(), 0xffffffff, 0,
+          nullptr, 0);
     }
   }
-  CPDF_RenderStatus bitmap_render;
-  bitmap_render.Initialize(m_pContext.Get(), &bitmap_device, nullptr,
-                           m_pStopObj, nullptr, nullptr, &m_Options, 0,
-                           m_bDropObjects, pFormResource, true);
-  bitmap_render.ProcessObjectNoClip(pPageObj, &new_matrix);
+  CPDF_RenderStatus bitmap_render(m_pContext.Get(), &bitmap_device);
+  bitmap_render.SetOptions(m_Options);
+  bitmap_render.SetStopObject(m_pStopObj.Get());
+  bitmap_render.SetStdCS(true);
+  bitmap_render.SetDropObjects(m_bDropObjects);
+  bitmap_render.SetFormResource(pFormResource);
+  bitmap_render.Initialize(nullptr, nullptr);
+  bitmap_render.ProcessObjectNoClip(pPageObj, new_matrix);
 #if defined _SKIA_SUPPORT_PATHS_
   bitmap_device.Flush(true);
   bitmap->UnPreMultiply();
 #endif
   m_bStopped = bitmap_render.m_bStopped;
   if (pSMaskDict) {
-    CFX_Matrix smask_matrix = *pPageObj->m_GeneralState.GetSMaskMatrix();
-    smask_matrix.Concat(*pObj2Device);
-    RetainPtr<CFX_DIBSource> pSMaskSource =
+    CFX_Matrix smask_matrix =
+        *pPageObj->m_GeneralState.GetSMaskMatrix() * mtObj2Device;
+    RetainPtr<CFX_DIBBase> pSMaskSource =
         LoadSMask(pSMaskDict, &rect, &smask_matrix);
     if (pSMaskSource)
       bitmap->MultiplyAlpha(pSMaskSource);
@@ -1633,19 +743,19 @@
     pTextMask.Reset();
   }
   int32_t blitAlpha = 255;
-  if (iTransparency & PDFTRANS_GROUP && group_alpha != 1.0f) {
+  if (group_alpha != 1.0f && transparency.IsGroup()) {
     blitAlpha = (int32_t)(group_alpha * 255);
 #ifndef _SKIA_SUPPORT_
     bitmap->MultiplyAlpha(blitAlpha);
     blitAlpha = 255;
 #endif
   }
-  iTransparency = m_iTransparency;
+  transparency = m_Transparency;
   if (pPageObj->IsForm()) {
-    iTransparency |= PDFTRANS_GROUP;
+    transparency.SetGroup();
   }
   CompositeDIBitmap(bitmap, rect.left, rect.top, 0, blitAlpha, blend_type,
-                    iTransparency);
+                    transparency);
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
@@ -1662,11 +772,8 @@
   bbox.Intersect(m_pDevice->GetClipBox());
   *left = bbox.left;
   *top = bbox.top;
-  CFX_Matrix deviceCTM = m_pDevice->GetCTM();
-  float scaleX = fabs(deviceCTM.a);
-  float scaleY = fabs(deviceCTM.d);
-  int width = FXSYS_round(bbox.Width() * scaleX);
-  int height = FXSYS_round(bbox.Height() * scaleY);
+  int width = bbox.Width();
+  int height = bbox.Height();
   auto pBackdrop = pdfium::MakeRetain<CFX_DIBitmap>();
   if (bBackAlphaRequired && !m_bDropObjects)
     pBackdrop->Create(width, height, FXDIB_Argb);
@@ -1688,7 +795,6 @@
   }
   CFX_Matrix FinalMatrix = m_DeviceMatrix;
   FinalMatrix.Translate(-*left, -*top);
-  FinalMatrix.Scale(scaleX, scaleY);
   pBackdrop->Clear(pBackdrop->HasAlpha() ? 0 : 0xffffffff);
 
   CFX_DefaultRenderDevice device;
@@ -1709,10 +815,11 @@
                                     ? pSrcStates->m_ColorState.GetStrokeColor()
                                     : pSrcStates->m_ColorState.GetFillColor();
   if (!pObjColor->IsNull()) {
-    pStates->m_ColorState.SetFillRGB(
-        bStroke ? pSrcStates->m_ColorState.GetStrokeRGB()
-                : pSrcStates->m_ColorState.GetFillRGB());
-    pStates->m_ColorState.SetStrokeRGB(pStates->m_ColorState.GetFillRGB());
+    pStates->m_ColorState.SetFillColorRef(
+        bStroke ? pSrcStates->m_ColorState.GetStrokeColorRef()
+                : pSrcStates->m_ColorState.GetFillColorRef());
+    pStates->m_ColorState.SetStrokeColorRef(
+        pStates->m_ColorState.GetFillColorRef());
   }
   return pStates;
 }
@@ -1724,7 +831,7 @@
 #endif
 
 bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj,
-                                    const CFX_Matrix* pObj2Device,
+                                    const CFX_Matrix& mtObj2Device,
                                     CFX_PathData* pClippingPath) {
   if (textobj->GetCharCodes().empty())
     return true;
@@ -1733,9 +840,9 @@
   if (text_render_mode == TextRenderingMode::MODE_INVISIBLE)
     return true;
 
-  CPDF_Font* pFont = textobj->m_TextState.GetFont();
+  RetainPtr<CPDF_Font> pFont = textobj->m_TextState.GetFont();
   if (pFont->IsType3Font())
-    return ProcessType3Text(textobj, pObj2Device);
+    return ProcessType3Text(textobj, mtObj2Device);
 
   bool bFill = false;
   bool bStroke = false;
@@ -1750,7 +857,7 @@
         break;
       case TextRenderingMode::MODE_STROKE:
       case TextRenderingMode::MODE_STROKE_CLIP:
-        if (pFont->GetFace())
+        if (pFont->HasFace())
           bStroke = true;
         else
           bFill = true;
@@ -1758,15 +865,19 @@
       case TextRenderingMode::MODE_FILL_STROKE:
       case TextRenderingMode::MODE_FILL_STROKE_CLIP:
         bFill = true;
-        if (pFont->GetFace())
+        if (pFont->HasFace())
           bStroke = true;
         break;
       case TextRenderingMode::MODE_INVISIBLE:
         // Already handled above, but the compiler is not smart enough to
-        // realize it. Fall through.
+        // realize it.
         NOTREACHED();
+        return true;
       case TextRenderingMode::MODE_CLIP:
         return true;
+      case TextRenderingMode::MODE_UNKNOWN:
+        NOTREACHED();
+        return false;
     }
   }
   FX_ARGB stroke_argb = 0;
@@ -1792,20 +903,19 @@
 
   float font_size = textobj->m_TextState.GetFontSize();
   if (bPattern) {
-    DrawTextPathWithPattern(textobj, pObj2Device, pFont, font_size,
+    DrawTextPathWithPattern(textobj, mtObj2Device, pFont.Get(), font_size,
                             &text_matrix, bFill, bStroke);
     return true;
   }
   if (bClip || bStroke) {
-    const CFX_Matrix* pDeviceMatrix = pObj2Device;
+    const CFX_Matrix* pDeviceMatrix = &mtObj2Device;
     CFX_Matrix device_matrix;
     if (bStroke) {
       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.ConcatInverse(ctm);
-        device_matrix = ctm;
-        device_matrix.Concat(*pObj2Device);
+        text_matrix *= ctm.GetInverse();
+        device_matrix = ctm * mtObj2Device;
         pDeviceMatrix = &device_matrix;
       }
     }
@@ -1816,54 +926,44 @@
     }
     if (textobj->m_GeneralState.GetStrokeAdjust())
       flag |= FX_STROKE_ADJUST;
-    if (m_Options.HasFlag(RENDER_NOTEXTSMOOTH))
+    if (m_Options.GetOptions().bNoTextSmooth)
       flag |= FXFILL_NOPATHSMOOTH;
     return CPDF_TextRenderer::DrawTextPath(
-        m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(), pFont,
-        font_size, &text_matrix, pDeviceMatrix,
+        m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
+        pFont.Get(), font_size, text_matrix, pDeviceMatrix,
         textobj->m_GraphState.GetObject(), fill_argb, stroke_argb,
         pClippingPath, flag);
   }
-  text_matrix.Concat(*pObj2Device);
+  text_matrix.Concat(mtObj2Device);
   return CPDF_TextRenderer::DrawNormalText(
-      m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(), pFont,
-      font_size, &text_matrix, fill_argb, &m_Options);
-}
-
-RetainPtr<CPDF_Type3Cache> CPDF_RenderStatus::GetCachedType3(
-    CPDF_Type3Font* pFont) {
-  CPDF_Document* pDoc = pFont->GetDocument();
-  if (!pDoc)
-    return nullptr;
-
-  pDoc->GetPageData()->GetFont(pFont->GetFontDict());
-  return pDoc->GetRenderData()->GetCachedType3(pFont);
+      m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
+      pFont.Get(), font_size, text_matrix, fill_argb, m_Options);
 }
 
 // TODO(npm): Font fallback for type 3 fonts? (Completely separate code!!)
 bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj,
-                                         const CFX_Matrix* pObj2Device) {
+                                         const CFX_Matrix& mtObj2Device) {
   CPDF_Type3Font* pType3Font = textobj->m_TextState.GetFont()->AsType3Font();
   if (pdfium::ContainsValue(m_Type3FontCache, pType3Font))
     return true;
 
-  CFX_Matrix dCTM = m_pDevice->GetCTM();
-  float sa = fabs(dCTM.a);
-  float sd = fabs(dCTM.d);
+  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)
+    return false;
+
   CFX_Matrix text_matrix = textobj->GetTextMatrix();
   CFX_Matrix char_matrix = pType3Font->GetFontMatrix();
   float font_size = textobj->m_TextState.GetFontSize();
   char_matrix.Scale(font_size, font_size);
-  FX_ARGB fill_argb = GetFillArgb(textobj, true);
-  int fill_alpha = FXARGB_A(fill_argb);
-  int device_class = m_pDevice->GetDeviceClass();
-  std::vector<FXTEXT_GLYPHPOS> glyphs;
-  if (device_class == FXDC_DISPLAY)
-    glyphs.resize(textobj->GetCharCodes().size());
-  else if (fill_alpha < 255)
-    return false;
 
-  CPDF_RefType3Cache refTypeCache(pType3Font);
+  // Must come before |glyphs|, because |glyphs| points into |refTypeCache|.
+  std::set<RetainPtr<CPDF_Type3Cache>> refTypeCache;
+  std::vector<TextGlyphPos> glyphs;
+  if (device_type == DeviceType::kDisplay)
+    glyphs.resize(textobj->GetCharCodes().size());
+
   for (size_t iChar = 0; iChar < textobj->GetCharCodes().size(); ++iChar) {
     uint32_t charcode = textobj->GetCharCodes()[iChar];
     if (charcode == static_cast<uint32_t>(-1))
@@ -1876,17 +976,19 @@
     CFX_Matrix matrix = char_matrix;
     matrix.e += iChar > 0 ? textobj->GetCharPositions()[iChar - 1] : 0;
     matrix.Concat(text_matrix);
-    matrix.Concat(*pObj2Device);
-    if (!pType3Char->LoadBitmap(m_pContext.Get())) {
+    matrix.Concat(mtObj2Device);
+    if (!pType3Char->LoadBitmapFromSoleImageOfForm()) {
       if (!glyphs.empty()) {
         for (size_t i = 0; i < iChar; ++i) {
-          const FXTEXT_GLYPHPOS& glyph = glyphs[i];
+          const TextGlyphPos& glyph = glyphs[i];
           if (!glyph.m_pGlyph)
             continue;
 
-          m_pDevice->SetBitMask(glyph.m_pGlyph->m_pBitmap,
-                                glyph.m_Origin.x + glyph.m_pGlyph->m_Left,
-                                glyph.m_Origin.y - glyph.m_pGlyph->m_Top,
+          Optional<CFX_Point> point = glyph.GetOrigin({0, 0});
+          if (!point.has_value())
+            continue;
+
+          m_pDevice->SetBitMask(glyph.m_pGlyph->GetBitmap(), point->x, point->y,
                                 fill_argb);
         }
         glyphs.clear();
@@ -1895,72 +997,89 @@
       std::unique_ptr<CPDF_GraphicStates> pStates =
           CloneObjStates(textobj, false);
       CPDF_RenderOptions options = m_Options;
-      uint32_t option_flags = options.GetFlags();
-      option_flags |= RENDER_FORCE_HALFTONE | RENDER_RECT_AA;
-      option_flags &= ~RENDER_FORCE_DOWNSAMPLE;
-      options.SetFlags(option_flags);
+      options.GetOptions().bForceHalftone = true;
+      options.GetOptions().bRectAA = true;
 
-      CPDF_Dictionary* pFormResource = nullptr;
-      if (pType3Char->form() && pType3Char->form()->m_pFormDict) {
-        pFormResource =
-            pType3Char->form()->m_pFormDict->GetDictFor("Resources");
-      }
+      const auto* pForm = static_cast<const CPDF_Form*>(pType3Char->form());
+      const CPDF_Dictionary* pFormResource =
+          pForm->GetDict()->GetDictFor("Resources");
+
       if (fill_alpha == 255) {
-        CPDF_RenderStatus status;
-        status.Initialize(m_pContext.Get(), m_pDevice, nullptr, nullptr, this,
-                          pStates.get(), &options,
-                          pType3Char->form()->m_iTransparency, m_bDropObjects,
-                          pFormResource, false, pType3Char, fill_argb);
+        CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
+        status.SetOptions(options);
+        status.SetTransparency(pForm->GetTransparency());
+        status.SetType3Char(pType3Char);
+        status.SetFillColor(fill_argb);
+        status.SetDropObjects(m_bDropObjects);
+        status.SetFormResource(pFormResource);
+        status.Initialize(this, pStates.get());
         status.m_Type3FontCache = m_Type3FontCache;
         status.m_Type3FontCache.push_back(pType3Font);
 
         CFX_RenderDevice::StateRestorer restorer(m_pDevice);
-        status.RenderObjectList(pType3Char->form(), &matrix);
+        status.RenderObjectList(pForm, matrix);
       } else {
         FX_RECT rect =
-            matrix.TransformRect(pType3Char->form()->CalcBoundingBox())
-                .GetOuterRect();
+            matrix.TransformRect(pForm->CalcBoundingBox()).GetOuterRect();
+        if (!rect.Valid())
+          continue;
+
         CFX_DefaultRenderDevice bitmap_device;
-        if (!bitmap_device.Create((int)(rect.Width() * sa),
-                                  (int)(rect.Height() * sd), FXDIB_Argb,
+        if (!bitmap_device.Create(rect.Width(), rect.Height(), FXDIB_Argb,
                                   nullptr)) {
           return true;
         }
         bitmap_device.GetBitmap()->Clear(0);
-        CPDF_RenderStatus status;
-        status.Initialize(m_pContext.Get(), &bitmap_device, nullptr, nullptr,
-                          this, pStates.get(), &options,
-                          pType3Char->form()->m_iTransparency, m_bDropObjects,
-                          pFormResource, false, pType3Char, fill_argb);
+        CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device);
+        status.SetOptions(options);
+        status.SetTransparency(pForm->GetTransparency());
+        status.SetType3Char(pType3Char);
+        status.SetFillColor(fill_argb);
+        status.SetDropObjects(m_bDropObjects);
+        status.SetFormResource(pFormResource);
+        status.Initialize(this, pStates.get());
         status.m_Type3FontCache = m_Type3FontCache;
         status.m_Type3FontCache.push_back(pType3Font);
         matrix.Translate(-rect.left, -rect.top);
-        matrix.Scale(sa, sd);
-        status.RenderObjectList(pType3Char->form(), &matrix);
+        status.RenderObjectList(pForm, matrix);
         m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top);
       }
     } else if (pType3Char->GetBitmap()) {
-      if (device_class == FXDC_DISPLAY) {
-        RetainPtr<CPDF_Type3Cache> pCache = GetCachedType3(pType3Font);
-        refTypeCache.m_dwCount++;
-        CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, &matrix, sa, sd);
+      if (device_type == DeviceType::kDisplay) {
+        CPDF_Document* pDoc = pType3Font->GetDocument();
+        RetainPtr<CPDF_Type3Cache> pCache =
+            CPDF_DocRenderData::FromDocument(pDoc)->GetCachedType3(pType3Font);
+
+        const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, &matrix);
         if (!pBitmap)
           continue;
 
-        CFX_Point origin(FXSYS_round(matrix.e), FXSYS_round(matrix.f));
+        refTypeCache.insert(std::move(pCache));
+
+        CFX_Point origin(FXSYS_roundf(matrix.e), FXSYS_roundf(matrix.f));
         if (glyphs.empty()) {
-          m_pDevice->SetBitMask(pBitmap->m_pBitmap, origin.x + pBitmap->m_Left,
-                                origin.y - pBitmap->m_Top, fill_argb);
+          FX_SAFE_INT32 left = origin.x;
+          left += pBitmap->left();
+          if (!left.IsValid())
+            continue;
+
+          FX_SAFE_INT32 top = origin.y;
+          top -= pBitmap->top();
+          if (!top.IsValid())
+            continue;
+
+          m_pDevice->SetBitMask(pBitmap->GetBitmap(), left.ValueOrDie(),
+                                top.ValueOrDie(), fill_argb);
         } else {
           glyphs[iChar].m_pGlyph = pBitmap;
           glyphs[iChar].m_Origin = origin;
         }
       } else {
-        CFX_Matrix image_matrix = pType3Char->matrix();
-        image_matrix.Concat(matrix);
+        CFX_Matrix image_matrix = pType3Char->matrix() * matrix;
         CPDF_ImageRenderer renderer;
         if (renderer.Start(this, pType3Char->GetBitmap(), fill_argb, 255,
-                           &image_matrix, 0, false, FXDIB_BLEND_NORMAL)) {
+                           image_matrix, FXDIB_ResampleOptions(), false,
+                           BlendMode::kNormal)) {
           renderer.Continue(nullptr);
         }
         if (!renderer.GetResult())
@@ -1972,70 +1091,56 @@
   if (glyphs.empty())
     return true;
 
-  FX_RECT rect = FXGE_GetGlyphsBBox(glyphs, 0, sa, sd);
+  FX_RECT rect = GetGlyphsBBox(glyphs, 0);
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(static_cast<int>(rect.Width() * sa),
-                       static_cast<int>(rect.Height() * sd), FXDIB_8bppMask)) {
+  if (!pBitmap->Create(rect.Width(), rect.Height(), FXDIB_8bppMask))
     return true;
-  }
+
   pBitmap->Clear(0);
-  for (const FXTEXT_GLYPHPOS& glyph : glyphs) {
+  for (const TextGlyphPos& glyph : glyphs) {
     if (!glyph.m_pGlyph)
       continue;
 
-    pdfium::base::CheckedNumeric<int> left = glyph.m_Origin.x;
-    left += glyph.m_pGlyph->m_Left;
-    left -= rect.left;
-    left *= sa;
-    if (!left.IsValid())
+    Optional<CFX_Point> point = glyph.GetOrigin({rect.left, rect.top});
+    if (!point.has_value())
       continue;
 
-    pdfium::base::CheckedNumeric<int> top = glyph.m_Origin.y;
-    top -= glyph.m_pGlyph->m_Top;
-    top -= rect.top;
-    top *= sd;
-    if (!top.IsValid())
-      continue;
-
-    pBitmap->CompositeMask(left.ValueOrDie(), top.ValueOrDie(),
-                           glyph.m_pGlyph->m_pBitmap->GetWidth(),
-                           glyph.m_pGlyph->m_pBitmap->GetHeight(),
-                           glyph.m_pGlyph->m_pBitmap, fill_argb, 0, 0,
-                           FXDIB_BLEND_NORMAL, nullptr, false, 0);
+    pBitmap->CompositeMask(
+        point->x, point->y, glyph.m_pGlyph->GetBitmap()->GetWidth(),
+        glyph.m_pGlyph->GetBitmap()->GetHeight(), glyph.m_pGlyph->GetBitmap(),
+        fill_argb, 0, 0, BlendMode::kNormal, nullptr, false);
   }
   m_pDevice->SetBitMask(pBitmap, rect.left, rect.top, fill_argb);
   return true;
 }
 
 void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj,
-                                                const CFX_Matrix* pObj2Device,
+                                                const CFX_Matrix& mtObj2Device,
                                                 CPDF_Font* pFont,
                                                 float font_size,
                                                 const CFX_Matrix* pTextMatrix,
                                                 bool bFill,
                                                 bool bStroke) {
   if (!bStroke) {
-    CPDF_PathObject path;
     std::vector<std::unique_ptr<CPDF_TextObject>> pCopy;
     pCopy.push_back(std::unique_ptr<CPDF_TextObject>(textobj->Clone()));
-    path.m_bStroke = false;
-    path.m_FillType = FXFILL_WINDING;
+
+    CPDF_PathObject path;
+    path.set_filltype(FXFILL_WINDING);
+    path.m_ClipPath.CopyClipPath(m_LastClipPath);
     path.m_ClipPath.AppendTexts(&pCopy);
     path.m_ColorState = textobj->m_ColorState;
-    path.m_Path.AppendRect(textobj->m_Left, textobj->m_Bottom, textobj->m_Right,
-                           textobj->m_Top);
-    path.m_Left = textobj->m_Left;
-    path.m_Bottom = textobj->m_Bottom;
-    path.m_Right = textobj->m_Right;
-    path.m_Top = textobj->m_Top;
-    RenderSingleObject(&path, pObj2Device);
+    path.m_GeneralState = textobj->m_GeneralState;
+    path.path().AppendFloatRect(textobj->GetRect());
+    path.SetRect(textobj->GetRect());
+
+    AutoRestorer<UnownedPtr<const CPDF_PageObject>> restorer2(&m_pCurObj);
+    RenderSingleObject(&path, mtObj2Device);
     return;
   }
-  CPDF_CharPosList CharPosList;
-  CharPosList.Load(textobj->GetCharCodes(), textobj->GetCharPositions(), pFont,
-                   font_size);
-  for (uint32_t i = 0; i < CharPosList.m_nChars; i++) {
-    FXTEXT_CHARPOS& charpos = CharPosList.m_pCharPos[i];
+  const CPDF_CharPosList CharPosList(
+      textobj->GetCharCodes(), textobj->GetCharPositions(), pFont, font_size);
+  for (const TextCharPos& charpos : CharPosList.Get()) {
     auto* font = charpos.m_FallbackFontPosition == -1
                      ? pFont->GetFont()
                      : pFont->GetFontFallback(charpos.m_FallbackFontPosition);
@@ -2056,190 +1161,75 @@
     }
     matrix.Concat(CFX_Matrix(font_size, 0, 0, font_size, charpos.m_Origin.x,
                              charpos.m_Origin.y));
-    path.m_Path.Append(pPath, &matrix);
-    path.m_Matrix = *pTextMatrix;
-    path.m_bStroke = bStroke;
-    path.m_FillType = bFill ? FXFILL_WINDING : 0;
+    path.set_stroke(bStroke);
+    path.set_filltype(bFill ? FXFILL_WINDING : 0);
+    path.path().Append(pPath, &matrix);
+    path.set_matrix(*pTextMatrix);
     path.CalcBoundingBox();
-    ProcessPath(&path, pObj2Device);
+    ProcessPath(&path, mtObj2Device);
   }
 }
 
-void CPDF_RenderStatus::DrawShading(const CPDF_ShadingPattern* pPattern,
-                                    CFX_Matrix* pMatrix,
-                                    FX_RECT& clip_rect,
-                                    int alpha,
-                                    bool bAlphaMode) {
-  const auto& funcs = pPattern->GetFuncs();
-  CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict();
-  CPDF_ColorSpace* pColorSpace = pPattern->GetCS();
-  if (!pColorSpace)
-    return;
-
-  FX_ARGB background = 0;
-  if (!pPattern->IsShadingObject() && pDict->KeyExist("Background")) {
-    CPDF_Array* pBackColor = pDict->GetArrayFor("Background");
-    if (pBackColor &&
-        pBackColor->GetCount() >= pColorSpace->CountComponents()) {
-      CFX_FixedBufGrow<float, 16> comps(pColorSpace->CountComponents());
-      for (uint32_t i = 0; i < pColorSpace->CountComponents(); i++)
-        comps[i] = pBackColor->GetNumberAt(i);
-      float R = 0.0f;
-      float G = 0.0f;
-      float B = 0.0f;
-      pColorSpace->GetRGB(comps, &R, &G, &B);
-      background = ArgbEncode(255, (int32_t)(R * 255), (int32_t)(G * 255),
-                              (int32_t)(B * 255));
-    }
-  }
-  if (pDict->KeyExist("BBox")) {
-    clip_rect.Intersect(
-        pMatrix->TransformRect(pDict->GetRectFor("BBox")).GetOuterRect());
-  }
-  if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SHADING &&
-      m_pDevice->GetDeviceDriver()->DrawShading(pPattern, pMatrix, clip_rect,
-                                                alpha, bAlphaMode)) {
-    return;
-  }
-  CPDF_DeviceBuffer buffer;
-  buffer.Initialize(m_pContext.Get(), m_pDevice, &clip_rect, m_pCurObj, 150);
-  CFX_Matrix FinalMatrix = *pMatrix;
-  FinalMatrix.Concat(*buffer.GetMatrix());
-  RetainPtr<CFX_DIBitmap> pBitmap = buffer.GetBitmap();
-  if (!pBitmap->GetBuffer())
-    return;
-
-  pBitmap->Clear(background);
-  switch (pPattern->GetShadingType()) {
-    case kInvalidShading:
-    case kMaxShading:
-      return;
-    case kFunctionBasedShading:
-      DrawFuncShading(pBitmap, &FinalMatrix, pDict, funcs, pColorSpace, alpha);
-      break;
-    case kAxialShading:
-      DrawAxialShading(pBitmap, &FinalMatrix, pDict, funcs, pColorSpace, alpha);
-      break;
-    case kRadialShading:
-      DrawRadialShading(pBitmap, &FinalMatrix, pDict, 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 (CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) {
-        DrawFreeGouraudShading(pBitmap, &FinalMatrix, pStream, funcs,
-                               pColorSpace, alpha);
-      }
-    } 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 (CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) {
-        DrawLatticeGouraudShading(pBitmap, &FinalMatrix, pStream, funcs,
-                                  pColorSpace, alpha);
-      }
-    } 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 (CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) {
-        DrawCoonPatchMeshes(pPattern->GetShadingType(), pBitmap, &FinalMatrix,
-                            pStream, funcs, pColorSpace, m_Options.GetFlags(),
-                            alpha);
-      }
-    } break;
-  }
-  if (bAlphaMode)
-    pBitmap->LoadChannel(FXDIB_Red, pBitmap, FXDIB_Alpha);
-
-  if (m_Options.ColorModeIs(CPDF_RenderOptions::kGray))
-    pBitmap->ConvertColorScale(0, 0xffffff);
-  buffer.OutputToDevice();
-}
-
 void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern,
                                            const CPDF_PageObject* pPageObj,
-                                           const CFX_Matrix* pObj2Device,
+                                           const CFX_Matrix& mtObj2Device,
                                            bool bStroke) {
   if (!pattern->Load())
     return;
 
   CFX_RenderDevice::StateRestorer restorer(m_pDevice);
-  if (pPageObj->IsPath()) {
-    if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke))
-      return;
-  } else if (pPageObj->IsImage()) {
-    m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device));
-  } else {
-    return;
-  }
-  FX_RECT rect;
-  if (GetObjectClippedRect(pPageObj, pObj2Device, false, rect))
+  if (!ClipPattern(pPageObj, mtObj2Device, bStroke))
     return;
 
-  CFX_Matrix matrix = *pattern->pattern_to_form();
-  matrix.Concat(*pObj2Device);
-  GetScaledMatrix(matrix);
+  FX_RECT rect = GetObjectClippedRect(pPageObj, mtObj2Device);
+  if (rect.IsEmpty())
+    return;
+
+  CFX_Matrix matrix = pattern->pattern_to_form() * mtObj2Device;
   int alpha =
-      FXSYS_round(255 * (bStroke ? pPageObj->m_GeneralState.GetStrokeAlpha()
-                                 : pPageObj->m_GeneralState.GetFillAlpha()));
-  DrawShading(pattern, &matrix, rect, alpha,
-              m_Options.ColorModeIs(CPDF_RenderOptions::kAlpha));
+      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);
 }
 
 void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj,
-                                       const CFX_Matrix* pObj2Device) {
-  FX_RECT rect = pShadingObj->GetBBox(pObj2Device);
+                                       const CFX_Matrix& mtObj2Device) {
+  FX_RECT rect = pShadingObj->GetTransformedBBox(mtObj2Device);
   FX_RECT clip_box = m_pDevice->GetClipBox();
   rect.Intersect(clip_box);
   if (rect.IsEmpty())
     return;
 
-  CFX_Matrix matrix = pShadingObj->matrix();
-  matrix.Concat(*pObj2Device);
-  DrawShading(pShadingObj->pattern(), &matrix, rect,
-              FXSYS_round(255 * pShadingObj->m_GeneralState.GetFillAlpha()),
-              m_Options.ColorModeIs(CPDF_RenderOptions::kAlpha));
+  CFX_Matrix matrix = pShadingObj->matrix() * mtObj2Device;
+  CPDF_RenderShading::Draw(
+      m_pDevice, m_pContext.Get(), m_pCurObj.Get(), pShadingObj->pattern(),
+      matrix, rect,
+      FXSYS_roundf(255 * pShadingObj->m_GeneralState.GetFillAlpha()),
+      m_Options);
 }
 
 void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pPattern,
                                           CPDF_PageObject* pPageObj,
-                                          const CFX_Matrix* pObj2Device,
+                                          const CFX_Matrix& mtObj2Device,
                                           bool bStroke) {
-  if (!pPattern->Load())
+  const std::unique_ptr<CPDF_Form> pPatternForm = pPattern->Load(pPageObj);
+  if (!pPatternForm)
     return;
 
   CFX_RenderDevice::StateRestorer restorer(m_pDevice);
-  if (pPageObj->IsPath()) {
-    if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke))
-      return;
-  } else if (pPageObj->IsImage()) {
-    m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device));
-  } else {
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+  ScopedSkiaDeviceFlush scoped_skia_device_flush(m_pDevice);
+#endif
+  if (!ClipPattern(pPageObj, mtObj2Device, bStroke))
     return;
-  }
 
   FX_RECT clip_box = m_pDevice->GetClipBox();
   if (clip_box.IsEmpty())
     return;
 
-  CFX_Matrix dCTM = m_pDevice->GetCTM();
-  float sa = fabs(dCTM.a);
-  float sd = fabs(dCTM.d);
-  clip_box.right = clip_box.left + (int32_t)ceil(clip_box.Width() * sa);
-  clip_box.bottom = clip_box.top + (int32_t)ceil(clip_box.Height() * sd);
-
-  CFX_Matrix mtPattern2Device = *pPattern->pattern_to_form();
-  mtPattern2Device.Concat(*pObj2Device);
-  GetScaledMatrix(mtPattern2Device);
-
-  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());
+  const CFX_Matrix mtPattern2Device =
+      pPattern->pattern_to_form() * mtObj2Device;
 
   CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
 
@@ -2261,14 +1251,14 @@
 
   CFX_FloatRect clip_box_p =
       mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
-  int min_col = (int)ceil((clip_box_p.left - pPattern->bbox().right) /
-                          pPattern->x_step());
-  int max_col = (int)floor((clip_box_p.right - pPattern->bbox().left) /
-                           pPattern->x_step());
-  int min_row = (int)ceil((clip_box_p.bottom - pPattern->bbox().top) /
-                          pPattern->y_step());
-  int max_row = (int)floor((clip_box_p.top - pPattern->bbox().bottom) /
-                           pPattern->y_step());
+  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)
@@ -2280,30 +1270,37 @@
     if (!pPattern->colored())
       pStates = CloneObjStates(pPageObj, bStroke);
 
-    auto& pFormDict = pPattern->form()->m_pFormDict;
-    CPDF_Dictionary* pFormResource =
+    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 = *pObj2Device;
+        CFX_Matrix matrix = mtObj2Device;
         matrix.Translate(original.x - mtPattern2Device.e,
                          original.y - mtPattern2Device.f);
         CFX_RenderDevice::StateRestorer restorer2(m_pDevice);
-        CPDF_RenderStatus status;
-        status.Initialize(m_pContext.Get(), m_pDevice, nullptr, nullptr, this,
-                          pStates.get(), &m_Options,
-                          pPattern->form()->m_iTransparency, m_bDropObjects,
-                          pFormResource);
-        status.RenderObjectList(pPattern->form(), &matrix);
+        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_round(mtPattern2Device.e);
-    int orig_y = FXSYS_round(mtPattern2Device.f);
+    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--;
@@ -2324,14 +1321,16 @@
   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, pObj2Device, 8, 8, m_Options.GetFlags());
-    pPatternBitmap = pEnlargedBitmap->StretchTo(width, height, 0, nullptr);
-  } else {
-    pPatternBitmap = DrawPatternBitmap(
+    RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
         m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
-        pObj2Device, width, height, m_Options.GetFlags());
+        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;
@@ -2347,21 +1346,24 @@
     return;
 
   pScreen->Clear(0);
-  uint32_t* src_buf = (uint32_t*)pPatternBitmap->GetBuffer();
+  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, start_y;
+      int start_x;
+      int start_y;
       if (bAligned) {
-        start_x = FXSYS_round(mtPattern2Device.e) + col * width - clip_box.left;
-        start_y = FXSYS_round(mtPattern2Device.f) + row * height - clip_box.top;
+        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_round(original.x + left_offset);
+            FXSYS_roundf(original.x + left_offset);
         pdfium::base::CheckedNumeric<int> safeStartY =
-            FXSYS_round(original.y + top_offset);
+            FXSYS_roundf(original.y + top_offset);
 
         safeStartX -= clip_box.left;
         safeStartY -= clip_box.top;
@@ -2376,30 +1378,33 @@
             start_y >= clip_box.Height()) {
           continue;
         }
-        uint32_t* dest_buf =
-            (uint32_t*)(pScreen->GetBuffer() + pScreen->GetPitch() * start_y +
-                        start_x * 4);
-        if (pPattern->colored())
-          *dest_buf = *src_buf;
-        else
-          *dest_buf = (*(uint8_t*)src_buf << 24) | (fill_argb & 0xffffff);
+        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);
+                                   pPatternBitmap, 0, 0, BlendMode::kNormal,
+                                   nullptr, false);
         } else {
           pScreen->CompositeMask(start_x, start_y, width, height,
-                                 pPatternBitmap, fill_argb, 0, 0);
+                                 pPatternBitmap, fill_argb, 0, 0,
+                                 BlendMode::kNormal, nullptr, false);
         }
       }
     }
   }
   CompositeDIBitmap(pScreen, clip_box.left, clip_box.top, 0, 255,
-                    FXDIB_BLEND_NORMAL, false);
+                    BlendMode::kNormal, CPDF_Transparency());
 }
 
 void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* pPathObj,
-                                            const CFX_Matrix* pObj2Device,
+                                            const CFX_Matrix& mtObj2Device,
                                             const CPDF_Color* pColor,
                                             bool bStroke) {
   CPDF_Pattern* pattern = pColor->GetPattern();
@@ -2407,35 +1412,38 @@
     return;
 
   if (CPDF_TilingPattern* pTilingPattern = pattern->AsTilingPattern())
-    DrawTilingPattern(pTilingPattern, pPathObj, pObj2Device, bStroke);
+    DrawTilingPattern(pTilingPattern, pPathObj, mtObj2Device, bStroke);
   else if (CPDF_ShadingPattern* pShadingPattern = pattern->AsShadingPattern())
-    DrawShadingPattern(pShadingPattern, pPathObj, pObj2Device, bStroke);
+    DrawShadingPattern(pShadingPattern, pPathObj, mtObj2Device, bStroke);
 }
 
 void CPDF_RenderStatus::ProcessPathPattern(CPDF_PathObject* pPathObj,
-                                           const CFX_Matrix* pObj2Device,
-                                           int& filltype,
-                                           bool& bStroke) {
-  if (filltype) {
+                                           const CFX_Matrix& mtObj2Device,
+                                           int* filltype,
+                                           bool* bStroke) {
+  ASSERT(filltype);
+  ASSERT(bStroke);
+
+  if (*filltype) {
     const CPDF_Color& FillColor = *pPathObj->m_ColorState.GetFillColor();
     if (FillColor.IsPattern()) {
-      DrawPathWithPattern(pPathObj, pObj2Device, &FillColor, false);
-      filltype = 0;
+      DrawPathWithPattern(pPathObj, mtObj2Device, &FillColor, false);
+      *filltype = 0;
     }
   }
-  if (bStroke) {
+  if (*bStroke) {
     const CPDF_Color& StrokeColor = *pPathObj->m_ColorState.GetStrokeColor();
     if (StrokeColor.IsPattern()) {
-      DrawPathWithPattern(pPathObj, pObj2Device, &StrokeColor, true);
-      bStroke = false;
+      DrawPathWithPattern(pPathObj, mtObj2Device, &StrokeColor, true);
+      *bStroke = false;
     }
   }
 }
 
 bool CPDF_RenderStatus::ProcessImage(CPDF_ImageObject* pImageObj,
-                                     const CFX_Matrix* pObj2Device) {
+                                     const CFX_Matrix& mtObj2Device) {
   CPDF_ImageRenderer render;
-  if (render.Start(this, pImageObj, pObj2Device, m_bStdCS, m_curBlend))
+  if (render.Start(this, pImageObj, mtObj2Device, m_bStdCS, m_curBlend))
     render.Continue(nullptr);
   return render.GetResult();
 }
@@ -2446,19 +1454,20 @@
     int top,
     FX_ARGB mask_argb,
     int bitmap_alpha,
-    int blend_mode,
-    int iTransparency) {
+    BlendMode blend_mode,
+    const CPDF_Transparency& transparency) {
   if (!pDIBitmap)
     return;
 
-  if (blend_mode == FXDIB_BLEND_NORMAL) {
+  if (blend_mode == BlendMode::kNormal) {
     if (!pDIBitmap->IsAlphaMask()) {
       if (bitmap_alpha < 255) {
 #ifdef _SKIA_SUPPORT_
         std::unique_ptr<CFX_ImageRenderer> dummy;
-        CFX_Matrix m(pDIBitmap->GetWidth(), 0, 0, -pDIBitmap->GetHeight(), left,
-                     top + pDIBitmap->GetHeight());
-        m_pDevice->StartDIBits(pDIBitmap, bitmap_alpha, 0, &m, 0, &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
         pDIBitmap->MultiplyAlpha(bitmap_alpha);
@@ -2481,15 +1490,15 @@
       }
     }
   }
-  bool bIsolated = !!(iTransparency & PDFTRANS_ISOLATED);
-  bool bGroup = !!(iTransparency & PDFTRANS_GROUP);
-  bool bBackAlphaRequired = blend_mode && bIsolated && !m_bDropObjects;
+  bool bIsolated = transparency.IsIsolated();
+  bool bBackAlphaRequired =
+      blend_mode != BlendMode::kNormal && bIsolated && !m_bDropObjects;
   bool bGetBackGround =
       ((m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT)) ||
       (!(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT) &&
        (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired);
   if (bGetBackGround) {
-    if (bIsolated || !bGroup) {
+    if (bIsolated || !transparency.IsGroup()) {
       if (!pDIBitmap->IsAlphaMask())
         m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, blend_mode);
       return;
@@ -2506,15 +1515,18 @@
 
       RetainPtr<CFX_DIBitmap> pForeBitmap = m_pDevice->GetBitmap();
       pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
-                              pForeBitmap, rect.left, rect.top);
+                              pForeBitmap, rect.left, rect.top,
+                              BlendMode::kNormal, nullptr, false);
       left = std::min(left, 0);
       top = std::min(top, 0);
       if (pDIBitmap->IsAlphaMask()) {
         pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(),
-                              pDIBitmap, mask_argb, left, top, blend_mode);
+                              pDIBitmap, mask_argb, left, top, blend_mode,
+                              nullptr, false);
       } else {
         pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
-                                pDIBitmap, left, top, blend_mode);
+                                pDIBitmap, left, top, blend_mode, nullptr,
+                                false);
       }
     } else {
       pClone = pDIBitmap;
@@ -2533,20 +1545,21 @@
   int back_top;
   FX_RECT rect(left, top, left + pDIBitmap->GetWidth(),
                top + pDIBitmap->GetHeight());
-  RetainPtr<CFX_DIBitmap> pBackdrop =
-      GetBackdrop(m_pCurObj, rect, blend_mode > FXDIB_BLEND_NORMAL && bIsolated,
-                  &back_left, &back_top);
+  RetainPtr<CFX_DIBitmap> pBackdrop = GetBackdrop(
+      m_pCurObj.Get(), rect, blend_mode != BlendMode::kNormal && bIsolated,
+      &back_left, &back_top);
   if (!pBackdrop)
     return;
 
   if (pDIBitmap->IsAlphaMask()) {
     pBackdrop->CompositeMask(left - back_left, top - back_top,
                              pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
-                             pDIBitmap, mask_argb, 0, 0, blend_mode);
+                             pDIBitmap, mask_argb, 0, 0, blend_mode, nullptr,
+                             false);
   } else {
     pBackdrop->CompositeBitmap(left - back_left, top - back_top,
                                pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
-                               pDIBitmap, 0, 0, blend_mode);
+                               pDIBitmap, 0, 0, blend_mode, nullptr, false);
   }
 
   auto pBackdrop1 = pdfium::MakeRetain<CFX_DIBitmap>();
@@ -2554,7 +1567,8 @@
                      FXDIB_Rgb32);
   pBackdrop1->Clear((uint32_t)-1);
   pBackdrop1->CompositeBitmap(0, 0, pBackdrop->GetWidth(),
-                              pBackdrop->GetHeight(), pBackdrop, 0, 0);
+                              pBackdrop->GetHeight(), pBackdrop, 0, 0,
+                              BlendMode::kNormal, nullptr, false);
   pBackdrop = std::move(pBackdrop1);
   m_pDevice->SetDIBits(pBackdrop, back_left, back_top);
 }
@@ -2566,12 +1580,13 @@
   if (!pSMaskDict)
     return nullptr;
 
-  CPDF_Stream* pGroup = pSMaskDict->GetStreamFor("G");
+  CPDF_Stream* pGroup = pSMaskDict->GetStreamFor(pdfium::transparency::kG);
   if (!pGroup)
     return nullptr;
 
   std::unique_ptr<CPDF_Function> pFunc;
-  CPDF_Object* pFuncObj = pSMaskDict->GetDirectObjectFor("TR");
+  const CPDF_Object* pFuncObj =
+      pSMaskDict->GetDirectObjectFor(pdfium::transparency::kTR);
   if (pFuncObj && (pFuncObj->IsDictionary() || pFuncObj->IsStream()))
     pFunc = CPDF_Function::Load(pFuncObj);
 
@@ -2583,12 +1598,13 @@
   form.ParseContent();
 
   CFX_DefaultRenderDevice bitmap_device;
-  bool bLuminosity = pSMaskDict->GetStringFor("S") != "Alpha";
+  bool bLuminosity =
+      pSMaskDict->GetStringFor(pdfium::transparency::kSoftMaskSubType) !=
+      pdfium::transparency::kAlpha;
   int width = pClipRect->right - pClipRect->left;
   int height = pClipRect->bottom - pClipRect->top;
   FXDIB_Format format;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ || defined _SKIA_SUPPORT_ || \
-    defined _SKIA_SUPPORT_PATHS_
+#if defined(OS_MACOSX) || defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
   format = bLuminosity ? FXDIB_Rgb32 : FXDIB_8bppMask;
 #else
   format = bLuminosity ? FXDIB_Rgb : FXDIB_8bppMask;
@@ -2596,62 +1612,30 @@
   if (!bitmap_device.Create(width, height, format, nullptr))
     return nullptr;
 
-  CFX_DIBitmap& bitmap = *bitmap_device.GetBitmap();
-  int color_space_family = 0;
+  RetainPtr<CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
+  int nCSFamily = 0;
   if (bLuminosity) {
-    CPDF_Array* pBC = pSMaskDict->GetArrayFor("BC");
-    FX_ARGB back_color = 0xff000000;
-    if (pBC) {
-      CPDF_Object* pCSObj = nullptr;
-      CPDF_Dictionary* pDict = pGroup->GetDict();
-      if (pDict && pDict->GetDictFor("Group")) {
-        pCSObj = pDict->GetDictFor("Group")->GetDirectObjectFor("CS");
-      }
-      const CPDF_ColorSpace* pCS =
-          m_pContext->GetDocument()->LoadColorSpace(pCSObj);
-      if (pCS) {
-        // Store Color Space Family to use in CPDF_RenderStatus::Initialize.
-        color_space_family = pCS->GetFamily();
-
-        float R, G, B;
-        uint32_t comps = 8;
-        if (pCS->CountComponents() > comps) {
-          comps = pCS->CountComponents();
-        }
-        CFX_FixedBufGrow<float, 8> float_array(comps);
-        float* pFloats = float_array;
-        FX_SAFE_UINT32 num_floats = comps;
-        num_floats *= sizeof(float);
-        if (!num_floats.IsValid()) {
-          return nullptr;
-        }
-        memset(pFloats, 0, num_floats.ValueOrDie());
-        size_t count = pBC->GetCount() > 8 ? 8 : pBC->GetCount();
-        for (size_t i = 0; i < count; i++) {
-          pFloats[i] = pBC->GetNumberAt(i);
-        }
-        pCS->GetRGB(pFloats, &R, &G, &B);
-        back_color = 0xff000000 | ((int32_t)(R * 255) << 16) |
-                     ((int32_t)(G * 255) << 8) | (int32_t)(B * 255);
-        m_pContext->GetDocument()->GetPageData()->ReleaseColorSpace(pCSObj);
-      }
-    }
-    bitmap.Clear(back_color);
+    FX_ARGB back_color =
+        GetBackColor(pSMaskDict, pGroup->GetDict(), &nCSFamily);
+    bitmap->Clear(back_color);
   } else {
-    bitmap.Clear(0);
+    bitmap->Clear(0);
   }
-  CPDF_Dictionary* pFormResource = nullptr;
-  if (form.m_pFormDict) {
-    pFormResource = form.m_pFormDict->GetDictFor("Resources");
-  }
+
+  const CPDF_Dictionary* pFormResource =
+      form.GetDict()->GetDictFor("Resources");
   CPDF_RenderOptions options;
   options.SetColorMode(bLuminosity ? CPDF_RenderOptions::kNormal
                                    : CPDF_RenderOptions::kAlpha);
-  CPDF_RenderStatus status;
-  status.Initialize(m_pContext.Get(), &bitmap_device, nullptr, nullptr, nullptr,
-                    nullptr, &options, 0, m_bDropObjects, pFormResource, true,
-                    nullptr, 0, color_space_family, bLuminosity);
-  status.RenderObjectList(&form, &matrix);
+  CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device);
+  status.SetOptions(options);
+  status.SetGroupFamily(nCSFamily);
+  status.SetLoadMask(bLuminosity);
+  status.SetStdCS(true);
+  status.SetFormResource(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))
@@ -2659,24 +1643,23 @@
 
   uint8_t* dest_buf = pMask->GetBuffer();
   int dest_pitch = pMask->GetPitch();
-  uint8_t* src_buf = bitmap.GetBuffer();
-  int src_pitch = bitmap.GetPitch();
+  uint8_t* src_buf = bitmap->GetBuffer();
+  int src_pitch = bitmap->GetPitch();
   std::vector<uint8_t> transfers(256);
   if (pFunc) {
-    CFX_FixedBufGrow<float, 16> results(pFunc->CountOutputs());
-    for (int i = 0; i < 256; i++) {
-      float input = (float)i / 255.0f;
+    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, &nresult);
-      transfers[i] = FXSYS_round(results[0] * 255);
+      pFunc->Call(&input, 1, results.data(), &nresult);
+      transfers[i] = FXSYS_roundf(results[0] * 255);
     }
   } else {
-    for (int i = 0; i < 256; i++) {
-      transfers[i] = i;
-    }
+    // Fill |transfers| with 0, 1, ... N.
+    std::iota(transfers.begin(), transfers.end(), 0);
   }
   if (bLuminosity) {
-    int Bpp = bitmap.GetBPP() / 8;
+    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;
@@ -2695,3 +1678,44 @@
   }
   return pMask;
 }
+
+FX_ARGB CPDF_RenderStatus::GetBackColor(const CPDF_Dictionary* pSMaskDict,
+                                        const CPDF_Dictionary* pGroupDict,
+                                        int* pCSFamily) {
+  static constexpr FX_ARGB kDefaultColor = ArgbEncode(255, 0, 0, 0);
+  const CPDF_Array* pBC = pSMaskDict->GetArrayFor(pdfium::transparency::kBC);
+  if (!pBC)
+    return kDefaultColor;
+
+  const CPDF_Object* pCSObj = nullptr;
+  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);
+  if (!pCS)
+    return kDefaultColor;
+
+  int family = pCS->GetFamily();
+  if (family == PDFCS_LAB || pCS->IsSpecial() ||
+      (family == PDFCS_ICCBASED && !pCS->IsNormal())) {
+    return kDefaultColor;
+  }
+
+  // Store Color Space Family to use in CPDF_RenderStatus::Initialize().
+  *pCSFamily = family;
+
+  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);
+  floats.resize(comps);
+
+  float R;
+  float G;
+  float B;
+  pCS->GetRGB(floats.data(), &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 c8b7b09..76eb71c 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.h
+++ b/core/fpdfapi/render/cpdf_renderstatus.h
@@ -12,13 +12,17 @@
 
 #include "core/fpdfapi/page/cpdf_clippath.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/cfx_renderdevice.h"
+#include "core/fxge/fx_dib.h"
 
+class CFX_DIBitmap;
 class CFX_PathData;
+class CFX_RenderDevice;
 class CPDF_Color;
-class CPDF_Dictionary;
 class CPDF_Font;
 class CPDF_FormObject;
 class CPDF_ImageCacheEntry;
@@ -28,6 +32,7 @@
 class CPDF_PageObject;
 class CPDF_PageObjectHolder;
 class CPDF_PathObject;
+class CPDF_RenderContext;
 class CPDF_ShadingObject;
 class CPDF_ShadingPattern;
 class CPDF_TilingPattern;
@@ -35,35 +40,42 @@
 class CPDF_Type3Cache;
 class CPDF_Type3Char;
 class CPDF_Type3Font;
+class PauseIndicatorIface;
 
 class CPDF_RenderStatus {
  public:
-  CPDF_RenderStatus();
+  CPDF_RenderStatus(CPDF_RenderContext* pContext, CFX_RenderDevice* pDevice);
   ~CPDF_RenderStatus();
 
-  bool Initialize(class CPDF_RenderContext* pContext,
-                  CFX_RenderDevice* pDevice,
-                  const CFX_Matrix* pDeviceMatrix,
-                  const CPDF_PageObject* pStopObj,
-                  const CPDF_RenderStatus* pParentStatus,
-                  const CPDF_GraphicStates* pInitialStates,
-                  const CPDF_RenderOptions* pOptions,
-                  int transparency,
-                  bool bDropObjects,
-                  CPDF_Dictionary* pFormResource = nullptr,
-                  bool bStdCS = false,
-                  CPDF_Type3Char* pType3Char = nullptr,
-                  FX_ARGB fill_color = 0,
-                  uint32_t GroupFamily = 0,
-                  bool bLoadMask = false);
+  // Called prior to Initialize().
+  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 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 SetTransparency(const CPDF_Transparency& transparency) {
+    m_Transparency = transparency;
+  }
+
+  void Initialize(const CPDF_RenderStatus* pParentStatus,
+                  const CPDF_GraphicStates* pInitialStates);
+
   void RenderObjectList(const CPDF_PageObjectHolder* pObjectHolder,
-                        const CFX_Matrix* pObj2Device);
-  void RenderSingleObject(CPDF_PageObject* pObj, const CFX_Matrix* pObj2Device);
+                        const CFX_Matrix& mtObj2Device);
+  void RenderSingleObject(CPDF_PageObject* pObj,
+                          const CFX_Matrix& mtObj2Device);
   bool ContinueSingleObject(CPDF_PageObject* pObj,
-                            const CFX_Matrix* pObj2Device,
-                            IFX_PauseIndicator* pPause);
+                            const CFX_Matrix& mtObj2Device,
+                            PauseIndicatorIface* pPause);
   void ProcessClipPath(const CPDF_ClipPath& ClipPath,
-                       const CFX_Matrix* pObj2Device);
+                       const CFX_Matrix& mtObj2Device);
 
   uint32_t GetGroupFamily() const { return m_GroupFamily; }
   bool GetLoadMask() const { return m_bLoadMask; }
@@ -71,75 +83,89 @@
   bool IsPrint() const { return m_bPrint; }
   bool IsStopped() const { return m_bStopped; }
   CPDF_RenderContext* GetContext() const { return m_pContext.Get(); }
-  CPDF_Dictionary* GetFormResource() const { return m_pFormResource.Get(); }
+  const CPDF_Dictionary* GetFormResource() const {
+    return m_pFormResource.Get();
+  }
   CPDF_Dictionary* GetPageResource() const { return m_pPageResource.Get(); }
   CFX_RenderDevice* GetRenderDevice() const { return m_pDevice; }
-  const CPDF_RenderOptions* GetRenderOptions() const { return &m_Options; }
+  const CPDF_RenderOptions& GetRenderOptions() const { return m_Options; }
 
 #if defined _SKIA_SUPPORT_
   void DebugVerifyDeviceIsPreMultiplied() const;
 #endif
 
-  RetainPtr<CPDF_TransferFunc> GetTransferFunc(CPDF_Object* pObject) const;
-  FX_ARGB GetFillArgb(CPDF_PageObject* pObj, bool bType3 = false) const;
+  RetainPtr<CPDF_TransferFunc> GetTransferFunc(
+      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);
+  }
+
   void DrawTilingPattern(CPDF_TilingPattern* pPattern,
                          CPDF_PageObject* pPageObj,
-                         const CFX_Matrix* pObj2Device,
+                         const CFX_Matrix& mtObj2Device,
                          bool bStroke);
   void DrawShadingPattern(CPDF_ShadingPattern* pPattern,
                           const CPDF_PageObject* pPageObj,
-                          const CFX_Matrix* pObj2Device,
+                          const CFX_Matrix& mtObj2Device,
                           bool bStroke);
   void CompositeDIBitmap(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
                          int left,
                          int top,
                          FX_ARGB mask_argb,
                          int bitmap_alpha,
-                         int blend_mode,
-                         int iTransparency);
+                         BlendMode blend_mode,
+                         const CPDF_Transparency& transparency);
 
  private:
+  static std::unique_ptr<CPDF_GraphicStates> CloneObjStates(
+      const CPDF_GraphicStates* pSrcStates,
+      bool bStroke);
+
+  FX_ARGB GetFillArgbInternal(CPDF_PageObject* pObj, bool bType3) const;
   bool ProcessTransparency(CPDF_PageObject* PageObj,
-                           const CFX_Matrix* pObj2Device);
-  void ProcessObjectNoClip(CPDF_PageObject* PageObj,
-                           const CFX_Matrix* pObj2Device);
+                           const CFX_Matrix& mtObj2Device);
+  void ProcessObjectNoClip(CPDF_PageObject* pObj,
+                           const CFX_Matrix& mtObj2Device);
   void DrawObjWithBackground(CPDF_PageObject* pObj,
-                             const CFX_Matrix* pObj2Device);
-  bool DrawObjWithBlend(CPDF_PageObject* pObj, const CFX_Matrix* pObj2Device);
-  bool ProcessPath(CPDF_PathObject* pPathObj, const CFX_Matrix* pObj2Device);
+                             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,
-                          const CFX_Matrix* pObj2Device,
-                          int& filltype,
-                          bool& bStroke);
+                          const CFX_Matrix& mtObj2Device,
+                          int* filltype,
+                          bool* bStroke);
   void DrawPathWithPattern(CPDF_PathObject* pPathObj,
-                           const CFX_Matrix* pObj2Device,
+                           const CFX_Matrix& mtObj2Device,
                            const CPDF_Color* pColor,
                            bool bStroke);
+  bool ClipPattern(const CPDF_PageObject* pPageObj,
+                   const CFX_Matrix& mtObj2Device,
+                   bool bStroke);
   bool SelectClipPath(const CPDF_PathObject* pPathObj,
-                      const CFX_Matrix* pObj2Device,
+                      const CFX_Matrix& mtObj2Device,
                       bool bStroke);
-  bool ProcessImage(CPDF_ImageObject* pImageObj, const CFX_Matrix* pObj2Device);
+  bool ProcessImage(CPDF_ImageObject* pImageObj,
+                    const CFX_Matrix& mtObj2Device);
   void ProcessShading(const CPDF_ShadingObject* pShadingObj,
-                      const CFX_Matrix* pObj2Device);
-  void DrawShading(const CPDF_ShadingPattern* pPattern,
-                   CFX_Matrix* pMatrix,
-                   FX_RECT& clip_rect,
-                   int alpha,
-                   bool bAlphaMode);
+                      const CFX_Matrix& mtObj2Device);
   bool ProcessType3Text(CPDF_TextObject* textobj,
-                        const CFX_Matrix* pObj2Device);
+                        const CFX_Matrix& mtObj2Device);
   bool ProcessText(CPDF_TextObject* textobj,
-                   const CFX_Matrix* pObj2Device,
+                   const CFX_Matrix& mtObj2Device,
                    CFX_PathData* pClippingPath);
   void DrawTextPathWithPattern(const CPDF_TextObject* textobj,
-                               const CFX_Matrix* pObj2Device,
+                               const CFX_Matrix& mtObj2Device,
                                CPDF_Font* pFont,
                                float font_size,
                                const CFX_Matrix* pTextMatrix,
                                bool bFill,
                                bool bStroke);
   bool ProcessForm(const CPDF_FormObject* pFormObj,
-                   const CFX_Matrix* pObj2Device);
+                   const CFX_Matrix& mtObj2Device);
   RetainPtr<CFX_DIBitmap> GetBackdrop(const CPDF_PageObject* pObj,
                                       const FX_RECT& rect,
                                       bool bBackAlphaRequired,
@@ -148,42 +174,36 @@
   RetainPtr<CFX_DIBitmap> LoadSMask(CPDF_Dictionary* pSMaskDict,
                                     FX_RECT* pClipRect,
                                     const CFX_Matrix* pMatrix);
-  static RetainPtr<CPDF_Type3Cache> GetCachedType3(CPDF_Type3Font* pFont);
-  static std::unique_ptr<CPDF_GraphicStates> CloneObjStates(
-      const CPDF_GraphicStates* pPathObj,
-      bool bStroke);
+  // Optionally write the colorspace family value into |pCSFamily|.
+  FX_ARGB GetBackColor(const CPDF_Dictionary* pSMaskDict,
+                       const CPDF_Dictionary* pGroupDict,
+                       int* pCSFamily);
   FX_ARGB GetStrokeArgb(CPDF_PageObject* pObj) const;
-  bool GetObjectClippedRect(const CPDF_PageObject* pObj,
-                            const CFX_Matrix* pObj2Device,
-                            bool bLogical,
-                            FX_RECT& rect) const;
-  void GetScaledMatrix(CFX_Matrix& matrix) const;
-
-  static const int kRenderMaxRecursionDepth = 64;
-  static int s_CurrentRecursionDepth;
+  FX_RECT GetObjectClippedRect(const CPDF_PageObject* pObj,
+                               const CFX_Matrix& mtObj2Device) const;
 
   CPDF_RenderOptions m_Options;
-  UnownedPtr<CPDF_Dictionary> m_pFormResource;
-  UnownedPtr<CPDF_Dictionary> m_pPageResource;
+  RetainPtr<const CPDF_Dictionary> m_pFormResource;
+  RetainPtr<CPDF_Dictionary> m_pPageResource;
   std::vector<CPDF_Type3Font*> m_Type3FontCache;
-  UnownedPtr<CPDF_RenderContext> m_pContext;
-  bool m_bStopped;
-  CFX_RenderDevice* m_pDevice;
+  UnownedPtr<CPDF_RenderContext> const m_pContext;
+  bool m_bStopped = false;
+  CFX_RenderDevice* const m_pDevice;
   CFX_Matrix m_DeviceMatrix;
   CPDF_ClipPath m_LastClipPath;
-  const CPDF_PageObject* m_pCurObj;
-  const CPDF_PageObject* m_pStopObj;
+  UnownedPtr<const CPDF_PageObject> m_pCurObj;
+  UnownedPtr<const CPDF_PageObject> m_pStopObj;
   CPDF_GraphicStates m_InitialStates;
   std::unique_ptr<CPDF_ImageRenderer> m_pImageRenderer;
-  bool m_bPrint;
-  int m_iTransparency;
-  bool m_bDropObjects;
-  bool m_bStdCS;
-  uint32_t m_GroupFamily;
-  bool m_bLoadMask;
+  CPDF_Transparency m_Transparency;
+  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;
-  FX_ARGB m_T3FillColor;
-  int m_curBlend;
+  FX_ARGB m_T3FillColor = 0;
+  BlendMode m_curBlend = BlendMode::kNormal;
 };
 
 #endif  // CORE_FPDFAPI_RENDER_CPDF_RENDERSTATUS_H_
diff --git a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp
index 2d86024..8536b3b 100644
--- a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp
+++ b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp
@@ -6,13 +6,17 @@
 
 #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
 
+#include "core/fpdfapi/render/cpdf_devicebuffer.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
-#include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "third_party/base/ptr_util.h"
 
-#define _FPDFAPI_IMAGESIZE_LIMIT_ (30 * 1024 * 1024)
+namespace {
+
+constexpr size_t kImageSizeLimitBytes = 30 * 1024 * 1024;
+
+}  // namespace
 
 CPDF_ScaledRenderBuffer::CPDF_ScaledRenderBuffer() {}
 
@@ -20,7 +24,7 @@
 
 bool CPDF_ScaledRenderBuffer::Initialize(CPDF_RenderContext* pContext,
                                          CFX_RenderDevice* pDevice,
-                                         const FX_RECT& pRect,
+                                         const FX_RECT& rect,
                                          const CPDF_PageObject* pObj,
                                          const CPDF_RenderOptions* pOptions,
                                          int max_dpi) {
@@ -29,48 +33,42 @@
     return true;
 
   m_pContext = pContext;
-  m_Rect = pRect;
+  m_Rect = rect;
   m_pObject = pObj;
-  m_Matrix.Translate(-pRect.left, -pRect.top);
-  int horz_size = pDevice->GetDeviceCaps(FXDC_HORZ_SIZE);
-  int vert_size = pDevice->GetDeviceCaps(FXDC_VERT_SIZE);
-  if (horz_size && vert_size && max_dpi) {
-    int dpih =
-        pDevice->GetDeviceCaps(FXDC_PIXEL_WIDTH) * 254 / (horz_size * 10);
-    int dpiv =
-        pDevice->GetDeviceCaps(FXDC_PIXEL_HEIGHT) * 254 / (vert_size * 10);
-    if (dpih > max_dpi)
-      m_Matrix.Scale((float)(max_dpi) / dpih, 1.0f);
-    if (dpiv > max_dpi)
-      m_Matrix.Scale(1.0f, (float)(max_dpi) / (float)dpiv);
-  }
+  m_Matrix = CPDF_DeviceBuffer::CalculateMatrix(pDevice, rect, max_dpi,
+                                                /*scale=*/true);
   m_pBitmapDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
-  FXDIB_Format dibFormat = FXDIB_Rgb;
-  int32_t bpp = 24;
-  if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_ALPHA_OUTPUT) {
-    dibFormat = FXDIB_Argb;
-    bpp = 32;
-  }
+  bool bIsAlpha =
+      !!(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_ALPHA_OUTPUT);
+  FXDIB_Format dibFormat = bIsAlpha ? FXDIB_Argb : FXDIB_Rgb;
   while (1) {
     FX_RECT bitmap_rect =
-        m_Matrix.TransformRect(CFX_FloatRect(pRect)).GetOuterRect();
-    int32_t iWidth = bitmap_rect.Width();
-    int32_t iHeight = bitmap_rect.Height();
-    int32_t iPitch = (iWidth * bpp + 31) / 32 * 4;
-    if (iWidth * iHeight < 1)
+        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)) {
       return false;
+    }
 
-    if (iPitch * iHeight <= _FPDFAPI_IMAGESIZE_LIMIT_ &&
-        m_pBitmapDevice->Create(iWidth, iHeight, dibFormat, nullptr)) {
+    if (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);
+                            pOptions, m_Matrix);
   return true;
 }
 
+CFX_RenderDevice* CPDF_ScaledRenderBuffer::GetDevice() const {
+  return m_pBitmapDevice ? m_pBitmapDevice.get() : m_pDevice.Get();
+}
+
 void CPDF_ScaledRenderBuffer::OutputToDevice() {
   if (m_pBitmapDevice) {
     m_pDevice->StretchDIBits(m_pBitmapDevice->GetBitmap(), m_Rect.left,
diff --git a/core/fpdfapi/render/cpdf_scaledrenderbuffer.h b/core/fpdfapi/render/cpdf_scaledrenderbuffer.h
index b77e4b6..0e7ac07 100644
--- a/core/fpdfapi/render/cpdf_scaledrenderbuffer.h
+++ b/core/fpdfapi/render/cpdf_scaledrenderbuffer.h
@@ -11,8 +11,8 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
 
+class CFX_DefaultRenderDevice;
 class CFX_RenderDevice;
 class CPDF_PageObject;
 class CPDF_RenderContext;
@@ -25,15 +25,13 @@
 
   bool Initialize(CPDF_RenderContext* pContext,
                   CFX_RenderDevice* pDevice,
-                  const FX_RECT& pRect,
+                  const FX_RECT& rect,
                   const CPDF_PageObject* pObj,
                   const CPDF_RenderOptions* pOptions,
                   int max_dpi);
 
-  CFX_RenderDevice* GetDevice() const {
-    return m_pBitmapDevice ? m_pBitmapDevice.get() : m_pDevice.Get();
-  }
-  CFX_Matrix* GetMatrix() { return &m_Matrix; }
+  CFX_RenderDevice* GetDevice() const;
+  const CFX_Matrix& GetMatrix() const { return m_Matrix; }
   void OutputToDevice();
 
  private:
diff --git a/core/fpdfapi/render/cpdf_textrenderer.cpp b/core/fpdfapi/render/cpdf_textrenderer.cpp
index 711dbfa..fe1d258 100644
--- a/core/fpdfapi/render/cpdf_textrenderer.cpp
+++ b/core/fpdfapi/render/cpdf_textrenderer.cpp
@@ -14,6 +14,8 @@
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/text_char_pos.h"
 
 namespace {
 
@@ -29,40 +31,39 @@
                                      const std::vector<float>& charPos,
                                      CPDF_Font* pFont,
                                      float font_size,
-                                     const CFX_Matrix* pText2User,
+                                     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) {
-  CPDF_CharPosList CharPosList;
-  CharPosList.Load(charCodes, charPos, pFont, font_size);
-  if (CharPosList.m_nChars == 0)
+  const CPDF_CharPosList CharPosList(charCodes, charPos, pFont, font_size);
+  const std::vector<TextCharPos>& pos = CharPosList.Get();
+  if (pos.empty())
     return true;
 
   bool bDraw = true;
-  int32_t fontPosition = CharPosList.m_pCharPos[0].m_FallbackFontPosition;
-  uint32_t startIndex = 0;
-  for (uint32_t i = 0; i < CharPosList.m_nChars; i++) {
-    int32_t curFontPosition = CharPosList.m_pCharPos[i].m_FallbackFontPosition;
+  int32_t fontPosition = pos[0].m_FallbackFontPosition;
+  size_t startIndex = 0;
+  for (size_t i = 0; i < pos.size(); ++i) {
+    int32_t curFontPosition = pos[i].m_FallbackFontPosition;
     if (fontPosition == curFontPosition)
       continue;
 
     CFX_Font* font = GetFont(pFont, fontPosition);
-    if (!pDevice->DrawTextPath(i - startIndex,
-                               CharPosList.m_pCharPos + startIndex, font,
-                               font_size, pText2User, pUser2Device, pGraphState,
-                               fill_argb, stroke_argb, pClippingPath, nFlag)) {
+    if (!pDevice->DrawTextPath(i - startIndex, &pos[startIndex], font,
+                               font_size, mtText2User, pUser2Device,
+                               pGraphState, fill_argb, stroke_argb,
+                               pClippingPath, nFlag)) {
       bDraw = false;
     }
     fontPosition = curFontPosition;
     startIndex = i;
   }
   CFX_Font* font = GetFont(pFont, fontPosition);
-  if (!pDevice->DrawTextPath(CharPosList.m_nChars - startIndex,
-                             CharPosList.m_pCharPos + startIndex, font,
-                             font_size, pText2User, pUser2Device, pGraphState,
+  if (!pDevice->DrawTextPath(pos.size() - startIndex, &pos[startIndex], font,
+                             font_size, mtText2User, pUser2Device, pGraphState,
                              fill_argb, stroke_argb, pClippingPath, nFlag)) {
     bDraw = false;
   }
@@ -75,39 +76,34 @@
                                        float origin_y,
                                        CPDF_Font* pFont,
                                        float font_size,
-                                       const CFX_Matrix* pMatrix,
+                                       const CFX_Matrix& matrix,
                                        const ByteString& str,
                                        FX_ARGB fill_argb,
-                                       const CFX_GraphStateData* pGraphState,
-                                       const CPDF_RenderOptions* pOptions) {
+                                       const CPDF_RenderOptions& options) {
   if (pFont->IsType3Font())
     return;
 
-  int nChars = pFont->CountChar(str.c_str(), str.GetLength());
+  int nChars = pFont->CountChar(str.AsStringView());
   if (nChars <= 0)
     return;
 
-  int offset = 0;
+  size_t offset = 0;
   std::vector<uint32_t> codes;
   std::vector<float> positions;
   codes.resize(nChars);
   positions.resize(nChars - 1);
   float cur_pos = 0;
   for (int i = 0; i < nChars; i++) {
-    codes[i] = pFont->GetNextChar(str.c_str(), str.GetLength(), offset);
+    codes[i] = pFont->GetNextChar(str.AsStringView(), &offset);
     if (i)
       positions[i - 1] = cur_pos;
     cur_pos += pFont->GetCharWidthF(codes[i]) * font_size / 1000;
   }
-  CFX_Matrix matrix;
-  if (pMatrix)
-    matrix = *pMatrix;
-
-  matrix.e = origin_x;
-  matrix.f = origin_y;
-
-  DrawNormalText(pDevice, codes, positions, pFont, font_size, &matrix,
-                 fill_argb, pOptions);
+  CFX_Matrix new_matrix = matrix;
+  new_matrix.e = origin_x;
+  new_matrix.f = origin_y;
+  DrawNormalText(pDevice, codes, positions, pFont, font_size, new_matrix,
+                 fill_argb, options);
 }
 
 // static
@@ -116,55 +112,53 @@
                                        const std::vector<float>& charPos,
                                        CPDF_Font* pFont,
                                        float font_size,
-                                       const CFX_Matrix* pText2Device,
+                                       const CFX_Matrix& mtText2Device,
                                        FX_ARGB fill_argb,
-                                       const CPDF_RenderOptions* pOptions) {
-  CPDF_CharPosList CharPosList;
-  CharPosList.Load(charCodes, charPos, pFont, font_size);
-  if (CharPosList.m_nChars == 0)
+                                       const CPDF_RenderOptions& options) {
+  const CPDF_CharPosList CharPosList(charCodes, charPos, pFont, font_size);
+  const std::vector<TextCharPos>& pos = CharPosList.Get();
+  if (pos.empty())
     return true;
-  int FXGE_flags = 0;
-  if (pOptions) {
-    if (pOptions->HasFlag(RENDER_CLEARTYPE)) {
-      FXGE_flags |= FXTEXT_CLEARTYPE;
-      if (pOptions->HasFlag(RENDER_BGR_STRIPE))
-        FXGE_flags |= FXTEXT_BGR_STRIPE;
-    }
-    if (pOptions->HasFlag(RENDER_NOTEXTSMOOTH))
-      FXGE_flags |= FXTEXT_NOSMOOTH;
-    if (pOptions->HasFlag(RENDER_PRINTGRAPHICTEXT))
-      FXGE_flags |= FXTEXT_PRINTGRAPHICTEXT;
-    if (pOptions->HasFlag(RENDER_NO_NATIVETEXT))
-      FXGE_flags |= FXTEXT_NO_NATIVETEXT;
-    if (pOptions->HasFlag(RENDER_PRINTIMAGETEXT))
-      FXGE_flags |= FXTEXT_PRINTIMAGETEXT;
-  } else {
-    FXGE_flags = FXTEXT_CLEARTYPE;
+
+  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;
+    fxge_flags |= FXFONT_CIDFONT;
+
   bool bDraw = true;
-  int32_t fontPosition = CharPosList.m_pCharPos[0].m_FallbackFontPosition;
-  uint32_t startIndex = 0;
-  for (uint32_t i = 0; i < CharPosList.m_nChars; i++) {
-    int32_t curFontPosition = CharPosList.m_pCharPos[i].m_FallbackFontPosition;
+  int32_t fontPosition = pos[0].m_FallbackFontPosition;
+  size_t startIndex = 0;
+  for (size_t i = 0; i < pos.size(); ++i) {
+    int32_t curFontPosition = pos[i].m_FallbackFontPosition;
     if (fontPosition == curFontPosition)
       continue;
 
     CFX_Font* font = GetFont(pFont, fontPosition);
-    if (!pDevice->DrawNormalText(
-            i - startIndex, CharPosList.m_pCharPos + startIndex, font,
-            font_size, pText2Device, fill_argb, FXGE_flags)) {
+    if (!pDevice->DrawNormalText(i - startIndex, &pos[startIndex], font,
+                                 font_size, mtText2Device, fill_argb,
+                                 fxge_flags)) {
       bDraw = false;
     }
     fontPosition = curFontPosition;
     startIndex = i;
   }
   CFX_Font* font = GetFont(pFont, fontPosition);
-  if (!pDevice->DrawNormalText(CharPosList.m_nChars - startIndex,
-                               CharPosList.m_pCharPos + startIndex, font,
-                               font_size, pText2Device, fill_argb,
-                               FXGE_flags)) {
+  if (!pDevice->DrawNormalText(pos.size() - startIndex, &pos[startIndex], font,
+                               font_size, mtText2Device, fill_argb,
+                               fxge_flags)) {
     bDraw = false;
   }
   return bDraw;
diff --git a/core/fpdfapi/render/cpdf_textrenderer.h b/core/fpdfapi/render/cpdf_textrenderer.h
index ac8c8c5..91ab4cc 100644
--- a/core/fpdfapi/render/cpdf_textrenderer.h
+++ b/core/fpdfapi/render/cpdf_textrenderer.h
@@ -27,18 +27,17 @@
                              float origin_y,
                              CPDF_Font* pFont,
                              float font_size,
-                             const CFX_Matrix* matrix,
+                             const CFX_Matrix& matrix,
                              const ByteString& str,
                              FX_ARGB fill_argb,
-                             const CFX_GraphStateData* pGraphState,
-                             const CPDF_RenderOptions* pOptions);
+                             const CPDF_RenderOptions& options);
 
   static bool DrawTextPath(CFX_RenderDevice* pDevice,
                            const std::vector<uint32_t>& charCodes,
                            const std::vector<float>& charPos,
                            CPDF_Font* pFont,
                            float font_size,
-                           const CFX_Matrix* pText2User,
+                           const CFX_Matrix& mtText2User,
                            const CFX_Matrix* pUser2Device,
                            const CFX_GraphStateData* pGraphState,
                            FX_ARGB fill_argb,
@@ -51,9 +50,13 @@
                              const std::vector<float>& charPos,
                              CPDF_Font* pFont,
                              float font_size,
-                             const CFX_Matrix* pText2Device,
+                             const CFX_Matrix& mtText2Device,
                              FX_ARGB fill_argb,
-                             const CPDF_RenderOptions* pOptions);
+                             const CPDF_RenderOptions& options);
+
+  CPDF_TextRenderer() = delete;
+  CPDF_TextRenderer(const CPDF_TextRenderer&) = delete;
+  CPDF_TextRenderer& operator=(const CPDF_TextRenderer&) = delete;
 };
 
 #endif  // CORE_FPDFAPI_RENDER_CPDF_TEXTRENDERER_H_
diff --git a/core/fpdfapi/render/cpdf_transferfunc.cpp b/core/fpdfapi/render/cpdf_transferfunc.cpp
deleted file mode 100644
index 98528c3..0000000
--- a/core/fpdfapi/render/cpdf_transferfunc.cpp
+++ /dev/null
@@ -1,29 +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_transferfunc.h"
-
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/render/cpdf_dibtransferfunc.h"
-#include "core/fxge/dib/cfx_dibsource.h"
-
-CPDF_TransferFunc::CPDF_TransferFunc(CPDF_Document* pDoc) : m_pPDFDoc(pDoc) {}
-
-CPDF_TransferFunc::~CPDF_TransferFunc() {}
-
-FX_COLORREF CPDF_TransferFunc::TranslateColor(FX_COLORREF rgb) const {
-  return FXSYS_RGB(m_Samples[FXSYS_GetRValue(rgb)],
-                   m_Samples[256 + FXSYS_GetGValue(rgb)],
-                   m_Samples[512 + FXSYS_GetBValue(rgb)]);
-}
-
-RetainPtr<CFX_DIBSource> CPDF_TransferFunc::TranslateImage(
-    const RetainPtr<CFX_DIBSource>& pSrc) {
-  RetainPtr<CPDF_TransferFunc> pHolder(this);
-  auto pDest = pdfium::MakeRetain<CPDF_DIBTransferFunc>(pHolder);
-  pDest->LoadSrc(pSrc);
-  return pDest;
-}
diff --git a/core/fpdfapi/render/cpdf_transferfunc.h b/core/fpdfapi/render/cpdf_transferfunc.h
deleted file mode 100644
index d7e21fc..0000000
--- a/core/fpdfapi/render/cpdf_transferfunc.h
+++ /dev/null
@@ -1,42 +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_TRANSFERFUNC_H_
-#define CORE_FPDFAPI_RENDER_CPDF_TRANSFERFUNC_H_
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-
-class CPDF_Document;
-class CFX_DIBSource;
-
-class CPDF_TransferFunc : public Retainable {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  FX_COLORREF TranslateColor(FX_COLORREF src) const;
-  RetainPtr<CFX_DIBSource> TranslateImage(const RetainPtr<CFX_DIBSource>& pSrc);
-
-  const CPDF_Document* GetDocument() const { return m_pPDFDoc.Get(); }
-
-  const uint8_t* GetSamples() const { return m_Samples; }
-  uint8_t* GetSamples() { return m_Samples; }
-
-  bool GetIdentity() const { return m_bIdentity; }
-  void SetIdentity(bool identity) { m_bIdentity = identity; }
-
- private:
-  explicit CPDF_TransferFunc(CPDF_Document* pDoc);
-  ~CPDF_TransferFunc() override;
-
-  UnownedPtr<CPDF_Document> const m_pPDFDoc;
-  bool m_bIdentity;
-  uint8_t m_Samples[256 * 3];
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_TRANSFERFUNC_H_
diff --git a/core/fpdfapi/render/cpdf_type3cache.cpp b/core/fpdfapi/render/cpdf_type3cache.cpp
index fe9dfbf..41e630b 100644
--- a/core/fpdfapi/render/cpdf_type3cache.cpp
+++ b/core/fpdfapi/render/cpdf_type3cache.cpp
@@ -12,17 +12,23 @@
 
 #include "core/fpdfapi/font/cpdf_type3char.h"
 #include "core/fpdfapi/font/cpdf_type3font.h"
-#include "core/fpdfapi/render/cpdf_type3glyphs.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 "core/fxge/fx_font.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
 struct CPDF_UniqueKeyGen {
   void Generate(int count, ...);
-  char m_Key[128];
+
   int m_KeyLen;
+  char m_Key[128];
 };
 
 void CPDF_UniqueKeyGen::Generate(int count, ...) {
@@ -81,50 +87,45 @@
 
 CPDF_Type3Cache::CPDF_Type3Cache(CPDF_Type3Font* pFont) : m_pFont(pFont) {}
 
-CPDF_Type3Cache::~CPDF_Type3Cache() {}
+CPDF_Type3Cache::~CPDF_Type3Cache() = default;
 
-CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(uint32_t charcode,
-                                            const CFX_Matrix* pMatrix,
-                                            float retinaScaleX,
-                                            float retinaScaleY) {
+const CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(uint32_t charcode,
+                                                  const CFX_Matrix* pMatrix) {
   CPDF_UniqueKeyGen keygen;
   keygen.Generate(
-      4, FXSYS_round(pMatrix->a * 10000), FXSYS_round(pMatrix->b * 10000),
-      FXSYS_round(pMatrix->c * 10000), FXSYS_round(pMatrix->d * 10000));
+      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);
-  CPDF_Type3Glyphs* pSizeCache;
+  CPDF_Type3GlyphMap* pSizeCache;
   auto it = m_SizeMap.find(FaceGlyphsKey);
   if (it == m_SizeMap.end()) {
-    auto pNew = pdfium::MakeUnique<CPDF_Type3Glyphs>();
+    auto pNew = pdfium::MakeUnique<CPDF_Type3GlyphMap>();
     pSizeCache = pNew.get();
     m_SizeMap[FaceGlyphsKey] = std::move(pNew);
   } else {
     pSizeCache = it->second.get();
   }
-  auto it2 = pSizeCache->m_GlyphMap.find(charcode);
-  if (it2 != pSizeCache->m_GlyphMap.end())
-    return it2->second.get();
+  const CFX_GlyphBitmap* pExisting = pSizeCache->GetBitmap(charcode);
+  if (pExisting)
+    return pExisting;
 
   std::unique_ptr<CFX_GlyphBitmap> pNewBitmap =
-      RenderGlyph(pSizeCache, charcode, pMatrix, retinaScaleX, retinaScaleY);
+      RenderGlyph(pSizeCache, charcode, pMatrix);
   CFX_GlyphBitmap* pGlyphBitmap = pNewBitmap.get();
-  pSizeCache->m_GlyphMap[charcode] = std::move(pNewBitmap);
+  pSizeCache->SetBitmap(charcode, std::move(pNewBitmap));
   return pGlyphBitmap;
 }
 
 std::unique_ptr<CFX_GlyphBitmap> CPDF_Type3Cache::RenderGlyph(
-    CPDF_Type3Glyphs* pSize,
+    CPDF_Type3GlyphMap* pSize,
     uint32_t charcode,
-    const CFX_Matrix* pMatrix,
-    float retinaScaleX,
-    float retinaScaleY) {
+    const CFX_Matrix* pMatrix) {
   const CPDF_Type3Char* pChar = m_pFont->LoadChar(charcode);
   if (!pChar || !pChar->GetBitmap())
     return nullptr;
 
   CFX_Matrix text_matrix(pMatrix->a, pMatrix->b, pMatrix->c, pMatrix->d, 0, 0);
-  CFX_Matrix image_matrix = pChar->matrix();
-  image_matrix.Concat(text_matrix);
+  CFX_Matrix image_matrix = pChar->matrix() * text_matrix;
 
   RetainPtr<CFX_DIBitmap> pBitmap = pChar->GetBitmap();
   RetainPtr<CFX_DIBitmap> pResBitmap;
@@ -138,37 +139,30 @@
       float top_y = image_matrix.d + image_matrix.f;
       float bottom_y = image_matrix.f;
       bool bFlipped = top_y > bottom_y;
-      if (bFlipped) {
-        float temp = top_y;
-        top_y = bottom_y;
-        bottom_y = temp;
-      }
-      pSize->AdjustBlue(top_y, bottom_y, top_line, bottom_line);
-      pResBitmap = pBitmap->StretchTo(
-          static_cast<int>(FXSYS_round(image_matrix.a) * retinaScaleX),
-          static_cast<int>(
-              (bFlipped ? top_line - bottom_line : bottom_line - top_line) *
-              retinaScaleY),
-          0, nullptr);
+      if (bFlipped)
+        std::swap(top_y, bottom_y);
+      std::tie(top_line, bottom_line) = pSize->AdjustBlue(top_y, bottom_y);
+      FX_SAFE_INT32 safe_height = bFlipped ? top_line : bottom_line;
+      safe_height -= bFlipped ? bottom_line : top_line;
+      if (!safe_height.IsValid())
+        return nullptr;
+
+      pResBitmap = pBitmap->StretchTo(static_cast<int>(image_matrix.a),
+                                      safe_height.ValueOrDie(),
+                                      FXDIB_ResampleOptions(), nullptr);
       top = top_line;
-      if (image_matrix.a < 0) {
-        image_matrix.Scale(retinaScaleX, retinaScaleY);
-        left = FXSYS_round(image_matrix.e + image_matrix.a);
-      } else {
-        left = FXSYS_round(image_matrix.e);
-      }
+      if (image_matrix.a < 0)
+        left = FXSYS_roundf(image_matrix.e + image_matrix.a);
+      else
+        left = FXSYS_roundf(image_matrix.e);
     }
   }
-  if (!pResBitmap) {
-    image_matrix.Scale(retinaScaleX, retinaScaleY);
-    pResBitmap = pBitmap->TransformTo(&image_matrix, &left, &top);
-  }
+  if (!pResBitmap)
+    pResBitmap = pBitmap->TransformTo(image_matrix, &left, &top);
   if (!pResBitmap)
     return nullptr;
 
-  auto pGlyph = pdfium::MakeUnique<CFX_GlyphBitmap>();
-  pGlyph->m_Left = left;
-  pGlyph->m_Top = -top;
-  pGlyph->m_pBitmap->TakeOver(std::move(pResBitmap));
+  auto pGlyph = pdfium::MakeUnique<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 7911785..4371a01 100644
--- a/core/fpdfapi/render/cpdf_type3cache.h
+++ b/core/fpdfapi/render/cpdf_type3cache.h
@@ -10,36 +10,34 @@
 #include <map>
 #include <memory>
 
-#include "core/fpdfapi/font/cpdf_type3font.h"
-#include "core/fxcrt/fx_coordinates.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"
 
-class CPDF_Type3Glyphs;
+class CFX_GlyphBitmap;
+class CFX_Matrix;
+class CPDF_Type3Font;
+class CPDF_Type3GlyphMap;
 
-class CPDF_Type3Cache : public Retainable {
+class CPDF_Type3Cache final : public Retainable, public Observable {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  CFX_GlyphBitmap* LoadGlyph(uint32_t charcode,
-                             const CFX_Matrix* pMatrix,
-                             float retinaScaleX,
-                             float retinaScaleY);
+  const CFX_GlyphBitmap* LoadGlyph(uint32_t charcode,
+                                   const CFX_Matrix* pMatrix);
 
  private:
   explicit CPDF_Type3Cache(CPDF_Type3Font* pFont);
   ~CPDF_Type3Cache() override;
 
-  std::unique_ptr<CFX_GlyphBitmap> RenderGlyph(CPDF_Type3Glyphs* pSize,
+  std::unique_ptr<CFX_GlyphBitmap> RenderGlyph(CPDF_Type3GlyphMap* pSize,
                                                uint32_t charcode,
-                                               const CFX_Matrix* pMatrix,
-                                               float retinaScaleX,
-                                               float retinaScaleY);
+                                               const CFX_Matrix* pMatrix);
 
-  UnownedPtr<CPDF_Type3Font> const m_pFont;
-  std::map<ByteString, std::unique_ptr<CPDF_Type3Glyphs>> m_SizeMap;
+  RetainPtr<CPDF_Type3Font> const m_pFont;
+  std::map<ByteString, 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
new file mode 100644
index 0000000..b144991
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_type3glyphmap.cpp
@@ -0,0 +1,57 @@
+// 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_type3glyphmap.h"
+
+#include <algorithm>
+#include <map>
+#include <utility>
+
+#include "core/fxge/cfx_glyphbitmap.h"
+#include "core/fxge/fx_font.h"
+
+namespace {
+
+constexpr int kType3MaxBlues = 16;
+
+int AdjustBlueHelper(float pos, std::vector<int>* blues) {
+  float min_distance = 1000000.0f;
+  int closest_pos = -1;
+  for (int i = 0; i < static_cast<int>(blues->size()); ++i) {
+    float distance = fabs(pos - static_cast<float>(blues->at(i)));
+    if (distance < std::min(0.8f, min_distance)) {
+      min_distance = distance;
+      closest_pos = i;
+    }
+  }
+  if (closest_pos >= 0)
+    return blues->at(closest_pos);
+  int new_pos = FXSYS_roundf(pos);
+  if (blues->size() < kType3MaxBlues)
+    blues->push_back(new_pos);
+  return new_pos;
+}
+
+}  // namespace
+
+CPDF_Type3GlyphMap::CPDF_Type3GlyphMap() {}
+
+CPDF_Type3GlyphMap::~CPDF_Type3GlyphMap() {}
+
+std::pair<int, int> CPDF_Type3GlyphMap::AdjustBlue(float top, float bottom) {
+  return std::make_pair(AdjustBlueHelper(top, &m_TopBlue),
+                        AdjustBlueHelper(bottom, &m_BottomBlue));
+}
+
+const CFX_GlyphBitmap* CPDF_Type3GlyphMap::GetBitmap(uint32_t charcode) const {
+  auto it = m_GlyphMap.find(charcode);
+  return it != m_GlyphMap.end() ? it->second.get() : nullptr;
+}
+
+void CPDF_Type3GlyphMap::SetBitmap(uint32_t charcode,
+                                   std::unique_ptr<CFX_GlyphBitmap> pMap) {
+  m_GlyphMap[charcode] = std::move(pMap);
+}
diff --git a/core/fpdfapi/render/cpdf_type3glyphmap.h b/core/fpdfapi/render/cpdf_type3glyphmap.h
new file mode 100644
index 0000000..fced0ee
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_type3glyphmap.h
@@ -0,0 +1,36 @@
+// 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_TYPE3GLYPHMAP_H_
+#define CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHMAP_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/fx_system.h"
+
+class CFX_GlyphBitmap;
+
+class CPDF_Type3GlyphMap {
+ public:
+  CPDF_Type3GlyphMap();
+  ~CPDF_Type3GlyphMap();
+
+  // Returns a pair of integers (top_line, bottom_line).
+  std::pair<int, int> AdjustBlue(float top, float bottom);
+
+  const CFX_GlyphBitmap* GetBitmap(uint32_t charcode) const;
+  void SetBitmap(uint32_t charcode, std::unique_ptr<CFX_GlyphBitmap> pMap);
+
+ private:
+  std::vector<int> m_TopBlue;
+  std::vector<int> m_BottomBlue;
+  std::map<uint32_t, std::unique_ptr<CFX_GlyphBitmap>> m_GlyphMap;
+};
+
+#endif  // CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHMAP_H_
diff --git a/core/fpdfapi/render/cpdf_type3glyphs.cpp b/core/fpdfapi/render/cpdf_type3glyphs.cpp
deleted file mode 100644
index 01b689f..0000000
--- a/core/fpdfapi/render/cpdf_type3glyphs.cpp
+++ /dev/null
@@ -1,43 +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_type3glyphs.h"
-
-#include <map>
-
-#include "core/fxge/fx_font.h"
-
-CPDF_Type3Glyphs::CPDF_Type3Glyphs()
-    : m_TopBlueCount(0), m_BottomBlueCount(0) {}
-
-CPDF_Type3Glyphs::~CPDF_Type3Glyphs() {}
-
-static int _AdjustBlue(float pos, int& count, int blues[]) {
-  float min_distance = 1000000.0f;
-  int closest_pos = -1;
-  for (int i = 0; i < count; i++) {
-    float distance = fabs(pos - static_cast<float>(blues[i]));
-    if (distance < 1.0f * 80.0f / 100.0f && distance < min_distance) {
-      min_distance = distance;
-      closest_pos = i;
-    }
-  }
-  if (closest_pos >= 0)
-    return blues[closest_pos];
-  int new_pos = FXSYS_round(pos);
-  if (count == TYPE3_MAX_BLUES)
-    return new_pos;
-  blues[count++] = new_pos;
-  return new_pos;
-}
-
-void CPDF_Type3Glyphs::AdjustBlue(float top,
-                                  float bottom,
-                                  int& top_line,
-                                  int& bottom_line) {
-  top_line = _AdjustBlue(top, m_TopBlueCount, m_TopBlue);
-  bottom_line = _AdjustBlue(bottom, m_BottomBlueCount, m_BottomBlue);
-}
diff --git a/core/fpdfapi/render/cpdf_type3glyphs.h b/core/fpdfapi/render/cpdf_type3glyphs.h
deleted file mode 100644
index 778b639..0000000
--- a/core/fpdfapi/render/cpdf_type3glyphs.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHS_H_
-#define CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHS_H_
-
-#include <map>
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-
-class CFX_GlyphBitmap;
-
-#define TYPE3_MAX_BLUES 16
-
-class CPDF_Type3Glyphs {
- public:
-  CPDF_Type3Glyphs();
-  ~CPDF_Type3Glyphs();
-
-  void AdjustBlue(float top, float bottom, int& top_line, int& bottom_line);
-
-  std::map<uint32_t, std::unique_ptr<CFX_GlyphBitmap>> m_GlyphMap;
-  int m_TopBlue[TYPE3_MAX_BLUES];
-  int m_BottomBlue[TYPE3_MAX_BLUES];
-  int m_TopBlueCount;
-  int m_BottomBlueCount;
-};
-
-#endif  // CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHS_H_
diff --git a/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp b/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp
new file mode 100644
index 0000000..8e1f212
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp
@@ -0,0 +1,26 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
+
+#include "core/fxcodec/basic/basicmodule.h"
+#include "core/fxcodec/fax/faxmodule.h"
+#include "core/fxcodec/flate/flatemodule.h"
+#include "core/fxcodec/jpeg/jpegmodule.h"
+#include "core/fxge/win32/cfx_psrenderer.h"
+
+namespace {
+
+constexpr EncoderIface kEncoderIface = {
+    BasicModule::A85Encode, FaxModule::FaxEncode, FlateModule::Encode,
+    JpegModule::JpegEncode, BasicModule::RunLengthEncode};
+
+}  // namespace
+
+CPDF_WindowsRenderDevice::CPDF_WindowsRenderDevice(HDC hDC)
+    : CFX_WindowsRenderDevice(hDC, &kEncoderIface) {}
+
+CPDF_WindowsRenderDevice::~CPDF_WindowsRenderDevice() = default;
diff --git a/core/fpdfapi/render/cpdf_windowsrenderdevice.h b/core/fpdfapi/render/cpdf_windowsrenderdevice.h
new file mode 100644
index 0000000..51d150e
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_windowsrenderdevice.h
@@ -0,0 +1,18 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_RENDER_CPDF_WINDOWSRENDERDEVICE_H_
+#define CORE_FPDFAPI_RENDER_CPDF_WINDOWSRENDERDEVICE_H_
+
+#include "core/fxge/cfx_windowsrenderdevice.h"
+
+class CPDF_WindowsRenderDevice final : public CFX_WindowsRenderDevice {
+ public:
+  explicit CPDF_WindowsRenderDevice(HDC hDC);
+  ~CPDF_WindowsRenderDevice() override;
+};
+
+#endif  // CORE_FPDFAPI_RENDER_CPDF_WINDOWSRENDERDEVICE_H_
diff --git a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
new file mode 100644
index 0000000..271a251
--- /dev/null
+++ b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
@@ -0,0 +1,245 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_system.h"
+#include "public/fpdf_progressive.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFProgressiveRenderEmbedderTest : public EmbedderTest {
+ public:
+  // 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
+  // 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);
+
+  // Continue rendering of |page| into the bitmap created in
+  // StartRenderPageWithFlags().
+  // The call returns true if the rendering is complete.
+  bool ContinueRenderPage(FPDF_PAGE page, IFSDK_PAUSE* pause);
+
+  // Simplified form of FinishRenderPageWithForms() with no form handle.
+  ScopedFPDFBitmap FinishRenderPage(FPDF_PAGE page);
+
+  // Finish rendering of |page| into the bitmap created in
+  // StartRenderPageWithFlags(). This also renders the forms associated with
+  // the page. 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.
+  ScopedFPDFBitmap FinishRenderPageWithForms(FPDF_PAGE page,
+                                             FPDF_FORMHANDLE handle);
+
+ private:
+  // Keeps the bitmap used for progressive rendering alive until
+  // FPDF_RenderPage_Close() is called after which the bitmap is returned
+  // to the caller.
+  ScopedFPDFBitmap progressive_render_bitmap_;
+  int progressive_render_flags_ = 0;
+};
+
+bool FPDFProgressiveRenderEmbedderTest::StartRenderPage(FPDF_PAGE page,
+                                                        IFSDK_PAUSE* pause) {
+  return StartRenderPageWithFlags(page, pause, 0);
+}
+
+bool FPDFProgressiveRenderEmbedderTest::StartRenderPageWithFlags(
+    FPDF_PAGE page,
+    IFSDK_PAUSE* pause,
+    int flags) {
+  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));
+  FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
+  FPDFBitmap_FillRect(progressive_render_bitmap_.get(), 0, 0, width, height,
+                      fill_color);
+  int rv = FPDF_RenderPageBitmap_Start(progressive_render_bitmap_.get(), page,
+                                       0, 0, width, height, 0,
+                                       progressive_render_flags_, pause);
+  return rv != FPDF_RENDER_TOBECONTINUED;
+}
+
+bool FPDFProgressiveRenderEmbedderTest::ContinueRenderPage(FPDF_PAGE page,
+                                                           IFSDK_PAUSE* pause) {
+  ASSERT(progressive_render_bitmap_);
+
+  int rv = FPDF_RenderPage_Continue(page, pause);
+  return rv != FPDF_RENDER_TOBECONTINUED;
+}
+
+ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPage(
+    FPDF_PAGE page) {
+  return FinishRenderPageWithForms(page, /*handle=*/nullptr);
+}
+
+ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPageWithForms(
+    FPDF_PAGE page,
+    FPDF_FORMHANDLE handle) {
+  ASSERT(progressive_render_bitmap_);
+
+  int width = static_cast<int>(FPDF_GetPageWidth(page));
+  int height = static_cast<int>(FPDF_GetPageHeight(page));
+  FPDF_FFLDraw(handle, progressive_render_bitmap_.get(), page, 0, 0, width,
+               height, 0, progressive_render_flags_);
+  FPDF_RenderPage_Close(page);
+  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;
+  }
+  ~FakePause() = default;
+
+  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 rendering of page content using progressive render APIs
+  // without pausing the rendering.
+  EXPECT_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);
+  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 rendering of page content using progressive render APIs
+  // with pause in rendering.
+  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FakePause pause(true);
+  bool render_done = StartRenderPage(page, &pause);
+  EXPECT_FALSE(render_done);
+
+  while (!render_done) {
+    render_done = ContinueRenderPage(page, &pause);
+  }
+  ScopedFPDFBitmap bitmap = FinishRenderPage(page);
+  CompareBitmap(bitmap.get(), 595, 842, kMd5BaseContent);
+  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 rendering of the page with annotations using progressive render APIs
+  // with pause in rendering.
+  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FakePause pause(true);
+  bool render_done = StartRenderPageWithFlags(page, &pause, FPDF_ANNOT);
+  EXPECT_FALSE(render_done);
+
+  while (!render_done) {
+    render_done = ContinueRenderPage(page, &pause);
+  }
+  ScopedFPDFBitmap bitmap = FinishRenderPage(page);
+  CompareBitmap(bitmap.get(), 595, 842, kMd5ContentWithAnnot);
+  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 rendering of the page with forms using progressive render APIs
+  // with pause in rendering.
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FakePause pause(true);
+  bool render_done = StartRenderPage(page, &pause);
+  EXPECT_FALSE(render_done);
+
+  while (!render_done) {
+    render_done = ContinueRenderPage(page, &pause);
+  }
+  ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle_);
+  CompareBitmap(bitmap.get(), 300, 300, kMd5ContentWithForms);
+  UnloadPage(page);
+}
diff --git a/core/fpdfapi/render/fpdf_render_loadimage_embeddertest.cpp b/core/fpdfapi/render/fpdf_render_loadimage_embeddertest.cpp
deleted file mode 100644
index 5b7cbc6..0000000
--- a/core/fpdfapi/render/fpdf_render_loadimage_embeddertest.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class FPDFRenderLoadImageEmbeddertest : public EmbedderTest {};
-
-TEST_F(FPDFRenderLoadImageEmbeddertest, Bug_554151) {
-  // Test scanline downsampling with a BitsPerComponent of 4.
-  // Should not crash.
-  EXPECT_TRUE(OpenDocument("bug_554151.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 612, 792, "a14d7ee573c1b2456d7bf6b7762823cf");
-  FPDFBitmap_Destroy(bitmap);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFRenderLoadImageEmbeddertest, Bug_557223) {
-  // Should not crash
-  EXPECT_TRUE(OpenDocument("bug_557223.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 24, 24, "dc0ea1b743c2edb22c597cadc8537f7b");
-  FPDFBitmap_Destroy(bitmap);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFRenderLoadImageEmbeddertest, Bug_603518) {
-  // Should not crash
-  EXPECT_TRUE(OpenDocument("bug_603518.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 749, 749, "b9e75190cdc5edf0069a408744ca07dc");
-  FPDFBitmap_Destroy(bitmap);
-  UnloadPage(page);
-}
diff --git a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
index ed6da46..6de721c 100644
--- a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
+++ b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
@@ -2,17 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+
+#include "public/cpp/fpdf_scopers.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class FPDFRenderPatternEmbeddertest : public EmbedderTest {};
+class FPDFRenderPatternEmbedderTest : public EmbedderTest {};
 
-TEST_F(FPDFRenderPatternEmbeddertest, LoadError_547706) {
+TEST_F(FPDFRenderPatternEmbedderTest, LoadError_547706) {
   // Test shading where object is a dictionary instead of a stream.
   EXPECT_TRUE(OpenDocument("bug_547706.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
-  FPDFBitmap_Destroy(bitmap);
+  ASSERT_TRUE(page);
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
   UnloadPage(page);
 }
diff --git a/core/fpdfdoc/Android.bp b/core/fpdfdoc/Android.bp
new file mode 100644
index 0000000..5bb33ea
--- /dev/null
+++ b/core/fpdfdoc/Android.bp
@@ -0,0 +1,23 @@
+cc_library_static {
+    name: "libpdfium-fpdfdoc",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-font",
+        "libpdfium-page",
+        "libpdfium-parser",
+        "libpdfium-render",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/core/fpdfdoc/BUILD.gn b/core/fpdfdoc/BUILD.gn
new file mode 100644
index 0000000..f25ace9
--- /dev/null
+++ b/core/fpdfdoc/BUILD.gn
@@ -0,0 +1,112 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+source_set("fpdfdoc") {
+  sources = [
+    "cba_fontmap.cpp",
+    "cba_fontmap.h",
+    "cline.cpp",
+    "cline.h",
+    "cpdf_aaction.cpp",
+    "cpdf_aaction.h",
+    "cpdf_action.cpp",
+    "cpdf_action.h",
+    "cpdf_annot.cpp",
+    "cpdf_annot.h",
+    "cpdf_annotlist.cpp",
+    "cpdf_annotlist.h",
+    "cpdf_apsettings.cpp",
+    "cpdf_apsettings.h",
+    "cpdf_bookmark.cpp",
+    "cpdf_bookmark.h",
+    "cpdf_bookmarktree.cpp",
+    "cpdf_bookmarktree.h",
+    "cpdf_color_utils.cpp",
+    "cpdf_color_utils.h",
+    "cpdf_defaultappearance.cpp",
+    "cpdf_defaultappearance.h",
+    "cpdf_dest.cpp",
+    "cpdf_dest.h",
+    "cpdf_filespec.cpp",
+    "cpdf_filespec.h",
+    "cpdf_formcontrol.cpp",
+    "cpdf_formcontrol.h",
+    "cpdf_formfield.cpp",
+    "cpdf_formfield.h",
+    "cpdf_icon.cpp",
+    "cpdf_icon.h",
+    "cpdf_iconfit.cpp",
+    "cpdf_iconfit.h",
+    "cpdf_interactiveform.cpp",
+    "cpdf_interactiveform.h",
+    "cpdf_link.cpp",
+    "cpdf_link.h",
+    "cpdf_linklist.cpp",
+    "cpdf_linklist.h",
+    "cpdf_metadata.cpp",
+    "cpdf_metadata.h",
+    "cpdf_nametree.cpp",
+    "cpdf_nametree.h",
+    "cpdf_numbertree.cpp",
+    "cpdf_numbertree.h",
+    "cpdf_pagelabel.cpp",
+    "cpdf_pagelabel.h",
+    "cpdf_structelement.cpp",
+    "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_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" ]
+  deps = [
+    "../../constants",
+    "../fpdfapi/font",
+    "../fpdfapi/page",
+    "../fpdfapi/parser",
+    "../fpdfapi/render",
+    "../fxcrt",
+    "../fxge",
+  ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cpdf_annot_unittest.cpp",
+    "cpdf_defaultappearance_unittest.cpp",
+    "cpdf_dest_unittest.cpp",
+    "cpdf_filespec_unittest.cpp",
+    "cpdf_formfield_unittest.cpp",
+    "cpdf_metadata_unittest.cpp",
+    "cpdf_nametree_unittest.cpp",
+  ]
+  deps = [
+    ":fpdfdoc",
+    "../fpdfapi/parser",
+  ]
+  pdfium_root_dir = "../../"
+}
diff --git a/core/fpdfdoc/cba_fontmap.cpp b/core/fpdfdoc/cba_fontmap.cpp
new file mode 100644
index 0000000..c71c724
--- /dev/null
+++ b/core/fpdfdoc/cba_fontmap.cpp
@@ -0,0 +1,493 @@
+// 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
new file mode 100644
index 0000000..7130064
--- /dev/null
+++ b/core/fpdfdoc/cba_fontmap.h
@@ -0,0 +1,96 @@
+// 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
index a24b819..7940dc8 100644
--- a/core/fpdfdoc/cline.cpp
+++ b/core/fpdfdoc/cline.cpp
@@ -6,11 +6,9 @@
 
 #include "core/fpdfdoc/cline.h"
 
-CLine::CLine() {}
-
 CLine::CLine(const CPVT_LineInfo& lineinfo) : m_LineInfo(lineinfo) {}
 
-CLine::~CLine() {}
+CLine::~CLine() = default;
 
 CPVT_WordPlace CLine::GetBeginWordPlace() const {
   return CPVT_WordPlace(LinePlace.nSecIndex, LinePlace.nLineIndex, -1);
diff --git a/core/fpdfdoc/cline.h b/core/fpdfdoc/cline.h
index 67bf06b..b7e32d4 100644
--- a/core/fpdfdoc/cline.h
+++ b/core/fpdfdoc/cline.h
@@ -12,7 +12,6 @@
 
 class CLine {
  public:
-  CLine();
   explicit CLine(const CPVT_LineInfo& lineinfo);
   ~CLine();
 
diff --git a/core/fpdfdoc/cpdf_aaction.cpp b/core/fpdfdoc/cpdf_aaction.cpp
index 2628ca0..8284913 100644
--- a/core/fpdfdoc/cpdf_aaction.cpp
+++ b/core/fpdfdoc/cpdf_aaction.cpp
@@ -6,49 +6,62 @@
 
 #include "core/fpdfdoc/cpdf_aaction.h"
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+
 namespace {
 
-constexpr const char* g_sAATypes[] = {
-    "E",   // CursorEnter
-    "X",   // CursorExit
-    "D",   // ButtonDown
-    "U",   // ButtonUp
-    "Fo",  // GetFocus
-    "Bl",  // LoseFocus
-    "PO",  // PageOpen
-    "PC",  // PageClose
-    "PV",  // PageVisible
-    "PI",  // PageInvisible
-    "O",   // OpenPage
-    "C",   // ClosePage
-    "K",   // KeyStroke
-    "F",   // Format
-    "V",   // Validate
-    "C",   // Calculate
-    "WC",  // CloseDocument
-    "WS",  // SaveDocument
-    "DS",  // DocumentSaved
-    "WP",  // PrintDocument
-    "DP",  // DocumentPrinted
+constexpr const char* kAATypes[] = {
+    "E",   // kCursorEnter
+    "X",   // kCursorExit
+    "D",   // kButtonDown
+    "U",   // kButtonUp
+    "Fo",  // kGetFocus
+    "Bl",  // kLoseFocus
+    "PO",  // kPageOpen
+    "PC",  // kPageClose
+    "PV",  // kPageVisible
+    "PI",  // kPageInvisible
+    "O",   // kOpenPage
+    "C",   // kClosePage
+    "K",   // kKeyStroke
+    "F",   // kFormat
+    "V",   // kValidate
+    "C",   // kCalculate
+    "WC",  // kCloseDocument
+    "WS",  // kSaveDocument
+    "DS",  // kDocumentSaved
+    "WP",  // kPrintDocument
+    "DP",  // kDocumentPrinted
 };
 
-// |g_sAATypes| should have as many elements as enum AActionType.
-static_assert(FX_ArraySize(g_sAATypes) == CPDF_AAction::NumberOfActions,
-              "g_sAATypes count mismatch");
+// |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,
+              "kAATypes count mismatch");
 
 }  // namespace
 
-CPDF_AAction::CPDF_AAction(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_AAction::CPDF_AAction(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
 
 CPDF_AAction::CPDF_AAction(const CPDF_AAction& that) = default;
 
-CPDF_AAction::~CPDF_AAction() {}
+CPDF_AAction::~CPDF_AAction() = default;
 
 bool CPDF_AAction::ActionExist(AActionType eType) const {
-  return m_pDict && m_pDict->KeyExist(g_sAATypes[eType]);
+  return m_pDict && m_pDict->KeyExist(kAATypes[eType]);
 }
 
 CPDF_Action CPDF_AAction::GetAction(AActionType eType) const {
-  return CPDF_Action(m_pDict ? m_pDict->GetDictFor(g_sAATypes[eType])
-                             : nullptr);
+  return CPDF_Action(m_pDict ? m_pDict->GetDictFor(kAATypes[eType]) : nullptr);
+}
+
+// static
+bool CPDF_AAction::IsUserClick(AActionType eType) {
+  switch (eType) {
+    case kButtonUp:
+    case kButtonDown:
+      return true;
+    default:
+      return false;
+  }
 }
diff --git a/core/fpdfdoc/cpdf_aaction.h b/core/fpdfdoc/cpdf_aaction.h
index a5b0d25..c3c65e8 100644
--- a/core/fpdfdoc/cpdf_aaction.h
+++ b/core/fpdfdoc/cpdf_aaction.h
@@ -8,46 +8,50 @@
 #define CORE_FPDFDOC_CPDF_AACTION_H_
 
 #include "core/fpdfdoc/cpdf_action.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
 
 class CPDF_AAction {
  public:
   enum AActionType {
-    CursorEnter = 0,
-    CursorExit,
-    ButtonDown,
-    ButtonUp,
-    GetFocus,
-    LoseFocus,
-    PageOpen,
-    PageClose,
-    PageVisible,
-    PageInvisible,
-    OpenPage,
-    ClosePage,
-    KeyStroke,
-    Format,
-    Validate,
-    Calculate,
-    CloseDocument,
-    SaveDocument,
-    DocumentSaved,
-    PrintDocument,
-    DocumentPrinted,
-    NumberOfActions  // Must be last.
+    kCursorEnter = 0,
+    kCursorExit,
+    kButtonDown,
+    kButtonUp,
+    kGetFocus,
+    kLoseFocus,
+    kPageOpen,
+    kPageClose,
+    kPageVisible,
+    kPageInvisible,
+    kOpenPage,
+    kClosePage,
+    kKeyStroke,
+    kFormat,
+    kValidate,
+    kCalculate,
+    kCloseDocument,
+    kSaveDocument,
+    kDocumentSaved,
+    kPrintDocument,
+    kDocumentPrinted,
+    kDocumentOpen,
+    kNumberOfActions  // Must be last.
   };
 
-  explicit CPDF_AAction(CPDF_Dictionary* pDict);
+  explicit CPDF_AAction(const CPDF_Dictionary* pDict);
   CPDF_AAction(const CPDF_AAction& that);
   ~CPDF_AAction();
 
   bool ActionExist(AActionType eType) const;
   CPDF_Action GetAction(AActionType eType) const;
-  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+
+  static bool IsUserClick(AActionType eType);
 
  private:
-  UnownedPtr<CPDF_Dictionary> const m_pDict;
+  RetainPtr<const CPDF_Dictionary> const m_pDict;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_AACTION_H_
diff --git a/core/fpdfdoc/cpdf_action.cpp b/core/fpdfdoc/cpdf_action.cpp
index 383f328..5133c1b 100644
--- a/core/fpdfdoc/cpdf_action.cpp
+++ b/core/fpdfdoc/cpdf_action.cpp
@@ -6,8 +6,11 @@
 
 #include "core/fpdfdoc/cpdf_action.h"
 
+#include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfdoc/cpdf_filespec.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 
@@ -21,37 +24,24 @@
 
 }  // namespace
 
-CPDF_Action::CPDF_Action(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_Action::CPDF_Action(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
 
 CPDF_Action::CPDF_Action(const CPDF_Action& that) = default;
 
-CPDF_Action::~CPDF_Action() {}
-
-CPDF_Dest CPDF_Action::GetDest(CPDF_Document* pDoc) const {
-  if (!m_pDict)
-    return CPDF_Dest();
-
-  ByteString type = m_pDict->GetStringFor("S");
-  if (type != "GoTo" && type != "GoToR")
-    return CPDF_Dest();
-
-  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()));
-  }
-  if (CPDF_Array* pArray = pDest->AsArray())
-    return CPDF_Dest(pArray);
-
-  return CPDF_Dest();
-}
+CPDF_Action::~CPDF_Action() = default;
 
 CPDF_Action::ActionType CPDF_Action::GetType() const {
   if (!m_pDict)
     return Unknown;
 
+  // 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");
   if (csType.IsEmpty())
     return Unknown;
@@ -63,77 +53,144 @@
   return Unknown;
 }
 
+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()));
+  }
+  if (const CPDF_Array* pArray = pDest->AsArray())
+    return CPDF_Dest(pArray);
+
+  return CPDF_Dest();
+}
+
 WideString CPDF_Action::GetFilePath() const {
-  ByteString type = m_pDict->GetStringFor("S");
-  if (type != "GoToR" && type != "Launch" && type != "SubmitForm" &&
-      type != "ImportData") {
+  ActionType type = GetType();
+  if (type != GoToR && type != Launch && type != SubmitForm &&
+      type != ImportData) {
     return WideString();
   }
 
-  CPDF_Object* pFile = m_pDict->GetDirectObjectFor("F");
+  const CPDF_Object* pFile = m_pDict->GetDirectObjectFor(pdfium::stream::kF);
   if (pFile)
     return CPDF_FileSpec(pFile).GetFileName();
 
-  if (type == "Launch") {
-    CPDF_Dictionary* pWinDict = m_pDict->GetDictFor("Win");
-    if (pWinDict) {
-      return WideString::FromLocal(pWinDict->GetStringFor("F").AsStringView());
-    }
-  }
-  return WideString();
+  if (type != Launch)
+    return WideString();
+
+  const CPDF_Dictionary* pWinDict = m_pDict->GetDictFor("Win");
+  if (!pWinDict)
+    return WideString();
+
+  return WideString::FromDefANSI(
+      pWinDict->GetStringFor(pdfium::stream::kF).AsStringView());
 }
 
 ByteString CPDF_Action::GetURI(const CPDF_Document* pDoc) const {
-  ByteString csURI;
-  if (!m_pDict)
-    return csURI;
-  if (m_pDict->GetStringFor("S") != "URI")
-    return csURI;
+  ActionType type = GetType();
+  if (type != URI)
+    return ByteString();
 
-  csURI = m_pDict->GetStringFor("URI");
+  ByteString csURI = m_pDict->GetStringFor("URI");
   const CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  CPDF_Dictionary* pURI = pRoot->GetDictFor("URI");
+  const CPDF_Dictionary* pURI = pRoot->GetDictFor("URI");
   if (pURI) {
     auto result = csURI.Find(":");
-    if (!result.has_value() || result.value() == 0)
-      csURI = pURI->GetStringFor("Base") + csURI;
+    if (!result.has_value() || result.value() == 0) {
+      auto* pBase = pURI->GetDirectObjectFor("Base");
+      if (pBase && (pBase->IsString() || pBase->IsStream()))
+        csURI = pBase->GetString() + csURI;
+    }
   }
   return csURI;
 }
 
-WideString CPDF_Action::GetJavaScript() const {
-  WideString csJS;
-  if (!m_pDict)
-    return csJS;
+bool CPDF_Action::GetHideStatus() const {
+  return m_pDict->GetBooleanFor("H", true);
+}
 
-  CPDF_Object* pJS = m_pDict->GetDirectObjectFor("JS");
-  return pJS ? pJS->GetUnicodeText() : csJS;
+ByteString CPDF_Action::GetNamedAction() const {
+  return m_pDict->GetStringFor("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;
+  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");
+  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);
+    }
+  }
+  return result;
+}
+
+Optional<WideString> CPDF_Action::MaybeGetJavaScript() const {
+  const CPDF_Object* pObject = GetJavaScriptObject();
+  if (!pObject)
+    return pdfium::nullopt;
+  return pObject->GetUnicodeText();
+}
+
+WideString CPDF_Action::GetJavaScript() const {
+  const CPDF_Object* pObject = GetJavaScriptObject();
+  return pObject ? pObject->GetUnicodeText() : WideString();
 }
 
 size_t CPDF_Action::GetSubActionsCount() const {
   if (!m_pDict || !m_pDict->KeyExist("Next"))
     return 0;
 
-  CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
+  const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
   if (!pNext)
     return 0;
   if (pNext->IsDictionary())
     return 1;
-  if (CPDF_Array* pArray = pNext->AsArray())
-    return pArray->GetCount();
-  return 0;
+  const CPDF_Array* pArray = pNext->AsArray();
+  return pArray ? pArray->size() : 0;
 }
 
 CPDF_Action CPDF_Action::GetSubAction(size_t iIndex) const {
   if (!m_pDict || !m_pDict->KeyExist("Next"))
     return CPDF_Action(nullptr);
 
-  CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
-  if (CPDF_Array* pArray = ToArray(pNext))
+  const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
+  if (const CPDF_Array* pArray = ToArray(pNext))
     return CPDF_Action(pArray->GetDictAt(iIndex));
-  if (CPDF_Dictionary* pDict = ToDictionary(pNext)) {
+  if (const CPDF_Dictionary* pDict = ToDictionary(pNext)) {
     if (iIndex == 0)
       return CPDF_Action(pDict);
   }
   return CPDF_Action(nullptr);
 }
+
+const CPDF_Object* CPDF_Action::GetJavaScriptObject() const {
+  if (!m_pDict)
+    return nullptr;
+
+  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 1d9b722..c7cc2d8 100644
--- a/core/fpdfdoc/cpdf_action.h
+++ b/core/fpdfdoc/cpdf_action.h
@@ -7,11 +7,16 @@
 #ifndef CORE_FPDFDOC_CPDF_ACTION_H_
 #define CORE_FPDFDOC_CPDF_ACTION_H_
 
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include <vector>
+
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/optional.h"
 
+class CPDF_Dictionary;
 class CPDF_Document;
+class CPDF_Object;
 
 class CPDF_Action {
  public:
@@ -37,24 +42,35 @@
     GoTo3DView
   };
 
-  explicit CPDF_Action(CPDF_Dictionary* pDict);
+  explicit CPDF_Action(const CPDF_Dictionary* pDict);
   CPDF_Action(const CPDF_Action& that);
   ~CPDF_Action();
 
-  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+
   ActionType GetType() const;
   CPDF_Dest GetDest(CPDF_Document* pDoc) const;
   WideString GetFilePath() const;
   ByteString GetURI(const CPDF_Document* pDoc) const;
-  bool GetHideStatus() const { return m_pDict->GetBooleanFor("H", true); }
-  ByteString GetNamedAction() const { return m_pDict->GetStringFor("N"); }
-  uint32_t GetFlags() const { return m_pDict->GetIntegerFor("Flags"); }
+  bool GetHideStatus() const;
+  ByteString GetNamedAction() const;
+  uint32_t GetFlags() const;
+
+  std::vector<const CPDF_Object*> GetAllFields() const;
+
+  // Differentiates between empty JS entry and no JS entry.
+  Optional<WideString> MaybeGetJavaScript() const;
+
+  // Returns empty string for empty JS entry and no JS entry.
   WideString GetJavaScript() const;
+
   size_t GetSubActionsCount() const;
   CPDF_Action GetSubAction(size_t iIndex) const;
 
  private:
-  UnownedPtr<CPDF_Dictionary> const m_pDict;
+  const CPDF_Object* GetJavaScriptObject() const;
+
+  RetainPtr<const CPDF_Dictionary> const m_pDict;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_ACTION_H_
diff --git a/core/fpdfdoc/cpdf_actionfields.cpp b/core/fpdfdoc/cpdf_actionfields.cpp
deleted file mode 100644
index cee256d..0000000
--- a/core/fpdfdoc/cpdf_actionfields.cpp
+++ /dev/null
@@ -1,101 +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_actionfields.h"
-
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfdoc/cpdf_action.h"
-
-CPDF_ActionFields::CPDF_ActionFields(const CPDF_Action* pAction)
-    : m_pAction(pAction) {}
-
-CPDF_ActionFields::~CPDF_ActionFields() {}
-
-size_t CPDF_ActionFields::GetFieldsCount() const {
-  if (!m_pAction)
-    return 0;
-
-  CPDF_Dictionary* pDict = m_pAction->GetDict();
-  if (!pDict)
-    return 0;
-
-  ByteString csType = pDict->GetStringFor("S");
-  CPDF_Object* pFields = nullptr;
-  if (csType == "Hide")
-    pFields = pDict->GetDirectObjectFor("T");
-  else
-    pFields = pDict->GetArrayFor("Fields");
-
-  if (!pFields)
-    return 0;
-  if (pFields->IsDictionary())
-    return 1;
-  if (pFields->IsString())
-    return 1;
-  if (CPDF_Array* pArray = pFields->AsArray())
-    return pArray->GetCount();
-  return 0;
-}
-
-std::vector<CPDF_Object*> CPDF_ActionFields::GetAllFields() const {
-  std::vector<CPDF_Object*> fields;
-  if (!m_pAction)
-    return fields;
-
-  CPDF_Dictionary* pDict = m_pAction->GetDict();
-  if (!pDict)
-    return fields;
-
-  ByteString csType = pDict->GetStringFor("S");
-  CPDF_Object* pFields;
-  if (csType == "Hide")
-    pFields = pDict->GetDirectObjectFor("T");
-  else
-    pFields = pDict->GetArrayFor("Fields");
-
-  if (!pFields)
-    return fields;
-
-  if (pFields->IsDictionary() || pFields->IsString()) {
-    fields.push_back(pFields);
-  } else if (CPDF_Array* pArray = pFields->AsArray()) {
-    for (size_t i = 0; i < pArray->GetCount(); ++i) {
-      CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
-      if (pObj)
-        fields.push_back(pObj);
-    }
-  }
-  return fields;
-}
-
-CPDF_Object* CPDF_ActionFields::GetField(size_t iIndex) const {
-  if (!m_pAction)
-    return nullptr;
-
-  CPDF_Dictionary* pDict = m_pAction->GetDict();
-  if (!pDict)
-    return nullptr;
-
-  ByteString csType = pDict->GetStringFor("S");
-  CPDF_Object* pFields = nullptr;
-  if (csType == "Hide")
-    pFields = pDict->GetDirectObjectFor("T");
-  else
-    pFields = pDict->GetArrayFor("Fields");
-
-  if (!pFields)
-    return nullptr;
-
-  CPDF_Object* pFindObj = nullptr;
-  if (pFields->IsDictionary() || pFields->IsString()) {
-    if (iIndex == 0)
-      pFindObj = pFields;
-  } else if (CPDF_Array* pArray = pFields->AsArray()) {
-    pFindObj = pArray->GetDirectObjectAt(iIndex);
-  }
-  return pFindObj;
-}
diff --git a/core/fpdfdoc/cpdf_actionfields.h b/core/fpdfdoc/cpdf_actionfields.h
deleted file mode 100644
index 83c70f6..0000000
--- a/core/fpdfdoc/cpdf_actionfields.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CPDF_ACTIONFIELDS_H_
-#define CORE_FPDFDOC_CPDF_ACTIONFIELDS_H_
-
-#include <stddef.h>
-
-#include <vector>
-
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDF_Action;
-class CPDF_Object;
-
-class CPDF_ActionFields {
- public:
-  explicit CPDF_ActionFields(const CPDF_Action* pAction);
-  ~CPDF_ActionFields();
-
-  size_t GetFieldsCount() const;
-  std::vector<CPDF_Object*> GetAllFields() const;
-  CPDF_Object* GetField(size_t iIndex) const;
-
- private:
-  UnownedPtr<const CPDF_Action> const m_pAction;
-};
-
-#endif  // CORE_FPDFDOC_CPDF_ACTIONFIELDS_H_
diff --git a/core/fpdfdoc/cpdf_annot.cpp b/core/fpdfdoc/cpdf_annot.cpp
index 02df3cf..f474074 100644
--- a/core/fpdfdoc/cpdf_annot.cpp
+++ b/core/fpdfdoc/cpdf_annot.cpp
@@ -6,17 +6,22 @@
 
 #include "core/fpdfdoc/cpdf_annot.h"
 
+#include <algorithm>
 #include <utility>
 
+#include "constants/annotation_common.h"
+#include "constants/annotation_flags.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_page.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/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/fxcrt/fx_memory.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
@@ -24,7 +29,7 @@
 
 namespace {
 
-char kPDFiumKey_HasGeneratedAP[] = "PDFIUM_HasGeneratedAP";
+const char kPDFiumKey_HasGeneratedAP[] = "PDFIUM_HasGeneratedAP";
 
 bool IsTextMarkupAnnotation(CPDF_Annot::Subtype type) {
   return type == CPDF_Annot::Subtype::HIGHLIGHT ||
@@ -33,16 +38,6 @@
          type == CPDF_Annot::Subtype::UNDERLINE;
 }
 
-bool ShouldGenerateAPForAnnotation(CPDF_Dictionary* pAnnotDict) {
-  // If AP dictionary exists and defines an appearance for normal mode, we use
-  // the appearance defined in the existing AP dictionary.
-  CPDF_Dictionary* pAP = pAnnotDict->GetDictFor("AP");
-  if (pAP && pAP->GetDictFor("N"))
-    return false;
-
-  return !CPDF_Annot::IsAnnotationHidden(pAnnotDict);
-}
-
 CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage,
                           CPDF_Annot* pAnnot,
                           CPDF_Annot::AppearanceMode mode,
@@ -52,18 +47,18 @@
   if (!pForm)
     return nullptr;
 
-  CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrixFor("Matrix");
+  CFX_Matrix form_matrix = pForm->GetDict()->GetMatrixFor("Matrix");
   CFX_FloatRect form_bbox =
-      form_matrix.TransformRect(pForm->m_pFormDict->GetRectFor("BBox"));
+      form_matrix.TransformRect(pForm->GetDict()->GetRectFor("BBox"));
   matrix->MatchRect(pAnnot->GetRect(), form_bbox);
   matrix->Concat(*pUser2Device);
   return pForm;
 }
 
-CPDF_Stream* FPDFDOC_GetAnnotAPInternal(const CPDF_Dictionary* pAnnotDict,
-                                        CPDF_Annot::AppearanceMode eMode,
-                                        bool bFallbackToNormal) {
-  CPDF_Dictionary* pAP = pAnnotDict->GetDictFor("AP");
+CPDF_Stream* GetAnnotAPInternal(CPDF_Dictionary* pAnnotDict,
+                                CPDF_Annot::AppearanceMode eMode,
+                                bool bFallbackToNormal) {
+  CPDF_Dictionary* pAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
   if (!pAP)
     return nullptr;
 
@@ -85,11 +80,11 @@
   if (!pDict)
     return nullptr;
 
-  ByteString as = pAnnotDict->GetStringFor("AS");
+  ByteString as = pAnnotDict->GetStringFor(pdfium::annotation::kAS);
   if (as.IsEmpty()) {
     ByteString value = pAnnotDict->GetStringFor("V");
     if (value.IsEmpty()) {
-      CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
+      const CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
       value = pParentDict ? pParentDict->GetStringFor("V") : ByteString();
     }
     as = (!value.IsEmpty() && pDict->KeyExist(value)) ? value : "Off";
@@ -99,7 +94,7 @@
 
 }  // namespace
 
-CPDF_Annot::CPDF_Annot(std::unique_ptr<CPDF_Dictionary> pDict,
+CPDF_Annot::CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict,
                        CPDF_Document* pDocument)
     : m_pAnnotDict(std::move(pDict)), m_pDocument(pDocument) {
   Init();
@@ -115,17 +110,19 @@
 }
 
 void CPDF_Annot::Init() {
-  m_nSubtype = StringToAnnotSubtype(m_pAnnotDict->GetStringFor("Subtype"));
+  m_nSubtype = StringToAnnotSubtype(
+      m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype));
   m_bIsTextMarkupAnnotation = IsTextMarkupAnnotation(m_nSubtype);
-  m_bHasGeneratedAP = m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP);
+  m_bHasGeneratedAP =
+      m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false);
   GenerateAPIfNeeded();
 }
 
 void CPDF_Annot::GenerateAPIfNeeded() {
-  if (!ShouldGenerateAPForAnnotation(m_pAnnotDict.Get()))
+  if (!ShouldGenerateAP())
     return;
-  if (!CPVT_GenerateAP::GenerateAnnotAP(m_nSubtype, m_pDocument.Get(),
-                                        m_pAnnotDict.Get())) {
+  if (!CPVT_GenerateAP::GenerateAnnotAP(m_pDocument.Get(), m_pAnnotDict.Get(),
+                                        m_nSubtype)) {
     return;
   }
 
@@ -133,14 +130,21 @@
   m_bHasGeneratedAP = true;
 }
 
-bool CPDF_Annot::ShouldDrawAnnotation() {
-  if (IsAnnotationHidden(m_pAnnotDict.Get()))
+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 =
+      m_pAnnotDict->GetDictFor(pdfium::annotation::kAP);
+  if (pAP && pAP->GetDictFor("N"))
     return false;
 
-  if (m_nSubtype == CPDF_Annot::Subtype::POPUP && !m_bOpenState)
-    return false;
+  return !IsHidden();
+}
 
-  return true;
+bool CPDF_Annot::ShouldDrawAnnotation() const {
+  if (IsHidden())
+    return false;
+  return m_bOpenState || m_nSubtype != CPDF_Annot::Subtype::POPUP;
 }
 
 void CPDF_Annot::ClearCachedAP() {
@@ -152,42 +156,41 @@
 }
 
 CFX_FloatRect CPDF_Annot::RectForDrawing() const {
-  if (!m_pAnnotDict)
-    return CFX_FloatRect();
-
   bool bShouldUseQuadPointsCoords =
       m_bIsTextMarkupAnnotation && m_bHasGeneratedAP;
   if (bShouldUseQuadPointsCoords)
-    return RectFromQuadPoints(m_pAnnotDict.Get());
-
-  return m_pAnnotDict->GetRectFor("Rect");
+    return BoundingRectFromQuadPoints(m_pAnnotDict.Get());
+  return m_pAnnotDict->GetRectFor(pdfium::annotation::kRect);
 }
 
 CFX_FloatRect CPDF_Annot::GetRect() const {
-  if (!m_pAnnotDict)
-    return CFX_FloatRect();
-
   CFX_FloatRect rect = RectForDrawing();
   rect.Normalize();
   return rect;
 }
 
 uint32_t CPDF_Annot::GetFlags() const {
-  return m_pAnnotDict->GetIntegerFor("F");
+  return m_pAnnotDict->GetIntegerFor(pdfium::annotation::kF);
 }
 
-CPDF_Stream* FPDFDOC_GetAnnotAP(const CPDF_Dictionary* pAnnotDict,
-                                CPDF_Annot::AppearanceMode eMode) {
-  return FPDFDOC_GetAnnotAPInternal(pAnnotDict, eMode, true);
+bool CPDF_Annot::IsHidden() const {
+  return !!(GetFlags() & pdfium::annotation_flags::kHidden);
 }
 
-CPDF_Stream* FPDFDOC_GetAnnotAPNoFallback(const CPDF_Dictionary* pAnnotDict,
-                                          CPDF_Annot::AppearanceMode eMode) {
-  return FPDFDOC_GetAnnotAPInternal(pAnnotDict, eMode, false);
+CPDF_Stream* GetAnnotAP(CPDF_Dictionary* pAnnotDict,
+                        CPDF_Annot::AppearanceMode eMode) {
+  ASSERT(pAnnotDict);
+  return GetAnnotAPInternal(pAnnotDict, eMode, true);
+}
+
+CPDF_Stream* GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict,
+                                  CPDF_Annot::AppearanceMode eMode) {
+  ASSERT(pAnnotDict);
+  return GetAnnotAPInternal(pAnnotDict, eMode, false);
 }
 
 CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) {
-  CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pAnnotDict.Get(), mode);
+  CPDF_Stream* pStream = GetAnnotAP(m_pAnnotDict.Get(), mode);
   if (!pStream)
     return nullptr;
 
@@ -204,11 +207,11 @@
   return pResult;
 }
 
-// Static.
-CFX_FloatRect CPDF_Annot::RectFromQuadPoints(CPDF_Dictionary* pAnnotDict) {
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (!pArray)
-    return CFX_FloatRect();
+// static
+CFX_FloatRect CPDF_Annot::RectFromQuadPointsArray(const CPDF_Array* pArray,
+                                                  size_t nIndex) {
+  ASSERT(pArray);
+  ASSERT(nIndex < pArray->size() / 8);
 
   // QuadPoints are defined with 4 pairs of numbers
   // ([ pair0, pair1, pair2, pair3 ]), where
@@ -217,19 +220,43 @@
   // pair2 = bottom_left
   // pair3 = bottom_right
   //
-  // On the other hand, /Rect is define as 2 pairs [pair0, pair1] where:
+  // On the other hand, /Rect is defined as 2 pairs [pair0, pair1] where:
   // pair0 = bottom_left
   // pair1 = top_right.
-  return CFX_FloatRect(pArray->GetNumberAt(4), pArray->GetNumberAt(5),
-                       pArray->GetNumberAt(2), pArray->GetNumberAt(3));
+
+  return CFX_FloatRect(
+      pArray->GetNumberAt(4 + nIndex * 8), pArray->GetNumberAt(5 + nIndex * 8),
+      pArray->GetNumberAt(2 + nIndex * 8), pArray->GetNumberAt(3 + nIndex * 8));
 }
 
-// Static.
-bool CPDF_Annot::IsAnnotationHidden(CPDF_Dictionary* pAnnotDict) {
-  return !!(pAnnotDict->GetIntegerFor("F") & ANNOTFLAG_HIDDEN);
+// 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;
+  if (nQuadPointCount == 0)
+    return ret;
+
+  ret = RectFromQuadPointsArray(pArray, 0);
+  for (size_t i = 1; i < nQuadPointCount; ++i) {
+    CFX_FloatRect rect = RectFromQuadPointsArray(pArray, i);
+    ret.Union(rect);
+  }
+  return ret;
 }
 
-// Static.
+// 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;
+  if (nIndex >= nQuadPointCount)
+    return CFX_FloatRect();
+  return RectFromQuadPointsArray(pArray, nIndex);
+}
+
+// static
 CPDF_Annot::Subtype CPDF_Annot::StringToAnnotSubtype(
     const ByteString& sSubtype) {
   if (sSubtype == "Text")
@@ -289,7 +316,7 @@
   return CPDF_Annot::Subtype::UNKNOWN;
 }
 
-// Static.
+// static
 ByteString CPDF_Annot::AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype) {
   if (nSubtype == CPDF_Annot::Subtype::TEXT)
     return "Text";
@@ -345,7 +372,12 @@
     return "RichMedia";
   if (nSubtype == CPDF_Annot::Subtype::XFAWIDGET)
     return "XFAWidget";
-  return "";
+  return ByteString();
+}
+
+// static
+size_t CPDF_Annot::QuadPointCount(const CPDF_Array* pArray) {
+  return pArray->size() / 8;
 }
 
 bool CPDF_Annot::DrawAppearance(CPDF_Page* pPage,
@@ -368,7 +400,9 @@
   if (!pForm)
     return false;
 
-  CPDF_RenderContext context(pPage);
+  CPDF_RenderContext context(
+      pPage->GetDocument(), pPage->m_pPageResources.Get(),
+      static_cast<CPDF_PageRenderCache*>(pPage->GetRenderCache()));
   context.AppendLayer(pForm, &matrix);
   context.Render(pDevice, pOptions, nullptr);
   return true;
@@ -404,15 +438,15 @@
     return;
 
   uint32_t annot_flags = GetFlags();
-  if (annot_flags & ANNOTFLAG_HIDDEN) {
+  if (annot_flags & pdfium::annotation_flags::kHidden)
+    return;
+
+  bool bPrinting = pDevice->GetDeviceType() == DeviceType::kPrinter ||
+                   (pOptions && pOptions->GetOptions().bPrintPreview);
+  if (bPrinting && (annot_flags & pdfium::annotation_flags::kPrint) == 0) {
     return;
   }
-  bool bPrinting = pDevice->GetDeviceClass() == FXDC_PRINTER ||
-                   (pOptions && (pOptions->HasFlag(RENDER_PRINTPREVIEW)));
-  if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) {
-    return;
-  }
-  if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) {
+  if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView)) {
     return;
   }
   CPDF_Dictionary* pBS = m_pAnnotDict->GetDictFor("BS");
@@ -420,16 +454,17 @@
   float width;
   CPDF_Array* pDashArray = nullptr;
   if (!pBS) {
-    CPDF_Array* pBorderArray = m_pAnnotDict->GetArrayFor("Border");
+    CPDF_Array* pBorderArray =
+        m_pAnnotDict->GetArrayFor(pdfium::annotation::kBorder);
     style_char = 'S';
     if (pBorderArray) {
       width = pBorderArray->GetNumberAt(2);
-      if (pBorderArray->GetCount() == 4) {
+      if (pBorderArray->size() == 4) {
         pDashArray = pBorderArray->GetArrayAt(3);
         if (!pDashArray) {
           return;
         }
-        size_t nLen = pDashArray->GetCount();
+        size_t nLen = pDashArray->size();
         size_t i = 0;
         for (; i < nLen; ++i) {
           CPDF_Object* pObj = pDashArray->GetDirectObjectAt(i);
@@ -454,7 +489,7 @@
   if (width <= 0) {
     return;
   }
-  CPDF_Array* pColor = m_pAnnotDict->GetArrayFor("C");
+  CPDF_Array* pColor = m_pAnnotDict->GetArrayFor(pdfium::annotation::kC);
   uint32_t argb = 0xff000000;
   if (pColor) {
     int R = (int32_t)(pColor->GetNumberAt(0) * 255);
@@ -466,32 +501,22 @@
   graph_state.m_LineWidth = width;
   if (style_char == 'D') {
     if (pDashArray) {
-      size_t dash_count = pDashArray->GetCount();
-      if (dash_count % 2) {
-        dash_count++;
-      }
-      graph_state.m_DashArray = FX_Alloc(float, dash_count);
-      graph_state.m_DashCount = dash_count;
-      size_t i;
-      for (i = 0; i < pDashArray->GetCount(); ++i) {
-        graph_state.m_DashArray[i] = pDashArray->GetNumberAt(i);
-      }
-      if (i < dash_count) {
-        graph_state.m_DashArray[i] = graph_state.m_DashArray[i - 1];
-      }
+      graph_state.m_DashArray =
+          ReadArrayElementsToVector(pDashArray, pDashArray->size());
+      if (graph_state.m_DashArray.size() % 2)
+        graph_state.m_DashArray.push_back(graph_state.m_DashArray.back());
     } else {
-      graph_state.m_DashArray = FX_Alloc(float, 2);
-      graph_state.m_DashCount = 2;
-      graph_state.m_DashArray[0] = graph_state.m_DashArray[1] = 3 * 1.0f;
+      graph_state.m_DashArray = {3.0f, 3.0f};
     }
   }
+
   CFX_FloatRect rect = GetRect();
+  rect.Deflate(width / 2, width / 2);
   CFX_PathData path;
-  width /= 2;
-  path.AppendRect(rect.left + width, rect.bottom + width, rect.right - width,
-                  rect.top - width);
+  path.AppendFloatRect(rect);
+
   int fill_type = 0;
-  if (pOptions && (pOptions->HasFlag(RENDER_NOPATHSMOOTH)))
+  if (pOptions && pOptions->GetOptions().bNoPathSmooth)
     fill_type |= FXFILL_NOPATHSMOOTH;
 
   pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
diff --git a/core/fpdfdoc/cpdf_annot.h b/core/fpdfdoc/cpdf_annot.h
index 499c62d..64ab69d 100644
--- a/core/fpdfdoc/cpdf_annot.h
+++ b/core/fpdfdoc/cpdf_annot.h
@@ -16,6 +16,7 @@
 #include "core/fxcrt/maybe_owned.h"
 
 class CFX_RenderDevice;
+class CPDF_Array;
 class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_Form;
@@ -24,11 +25,6 @@
 class CPDF_RenderOptions;
 class CPDF_Stream;
 
-#define ANNOTFLAG_INVISIBLE 0x0001
-#define ANNOTFLAG_HIDDEN 0x0002
-#define ANNOTFLAG_PRINT 0x0004
-#define ANNOTFLAG_NOVIEW 0x0020
-
 class CPDF_Annot {
  public:
   enum AppearanceMode { Normal, Rollover, Down };
@@ -63,13 +59,18 @@
     XFAWIDGET
   };
 
-  static bool IsAnnotationHidden(CPDF_Dictionary* pAnnotDict);
   static CPDF_Annot::Subtype StringToAnnotSubtype(const ByteString& sSubtype);
   static ByteString AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype);
-  static CFX_FloatRect RectFromQuadPoints(CPDF_Dictionary* pAnnotDict);
+  static CFX_FloatRect RectFromQuadPointsArray(const CPDF_Array* pArray,
+                                               size_t nIndex);
+  static CFX_FloatRect BoundingRectFromQuadPoints(
+      const CPDF_Dictionary* pAnnotDict);
+  static CFX_FloatRect RectFromQuadPoints(const CPDF_Dictionary* pAnnotDict,
+                                          size_t nIndex);
+  static size_t QuadPointCount(const CPDF_Array* pArray);
 
   // The second constructor does not take ownership of the dictionary.
-  CPDF_Annot(std::unique_ptr<CPDF_Dictionary> pDict, CPDF_Document* pDocument);
+  CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict, CPDF_Document* pDocument);
   CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument);
   ~CPDF_Annot();
 
@@ -77,7 +78,10 @@
   uint32_t GetFlags() const;
   CFX_FloatRect GetRect() const;
   CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
-  CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
+  const CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
+  CPDF_Dictionary* GetAnnotDict() { return m_pAnnotDict.Get(); }
+
+  bool IsHidden() const;
 
   bool DrawAppearance(CPDF_Page* pPage,
                       CFX_RenderDevice* pDevice,
@@ -95,38 +99,38 @@
                   const CPDF_RenderOptions* pOptions);
   CPDF_Form* GetAPForm(const CPDF_Page* pPage, AppearanceMode mode);
   void SetOpenState(bool bOpenState) { m_bOpenState = bOpenState; }
-  CPDF_Annot* GetPopupAnnot() const { return m_pPopupAnnot; }
+  CPDF_Annot* GetPopupAnnot() const { return m_pPopupAnnot.Get(); }
   void SetPopupAnnot(CPDF_Annot* pAnnot) { m_pPopupAnnot = pAnnot; }
 
  private:
   void Init();
   void GenerateAPIfNeeded();
-  bool ShouldDrawAnnotation();
+  bool ShouldGenerateAP() const;
+  bool ShouldDrawAnnotation() const;
 
   CFX_FloatRect RectForDrawing() const;
 
-  MaybeOwned<CPDF_Dictionary> m_pAnnotDict;
+  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;
+  // If non-null, then this is not a popup annotation.
+  UnownedPtr<CPDF_Annot> m_pPopupAnnot;
   // |m_bOpenState| is only set for popup annotations.
   bool m_bOpenState = false;
   bool m_bHasGeneratedAP;
   bool m_bIsTextMarkupAnnotation;
-  // Not owned. If there is a valid pointer in |m_pPopupAnnot|,
-  // then this annot is never a popup.
-  CPDF_Annot* m_pPopupAnnot = nullptr;
 };
 
 // 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* FPDFDOC_GetAnnotAP(const CPDF_Dictionary* pAnnotDict,
-                                CPDF_Annot::AppearanceMode eMode);
+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 FPDFDOC_GetAnnotAP.
-CPDF_Stream* FPDFDOC_GetAnnotAPNoFallback(const CPDF_Dictionary* pAnnotDict,
-                                          CPDF_Annot::AppearanceMode eMode);
+// No fallbacks to Normal like in GetAnnotAP.
+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
new file mode 100644
index 0000000..a7625e9
--- /dev/null
+++ b/core/fpdfdoc/cpdf_annot_unittest.cpp
@@ -0,0 +1,137 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfdoc/cpdf_annot.h"
+
+#include <memory>
+#include <vector>
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+RetainPtr<CPDF_Array> CreateQuadPointArrayFromVector(
+    const std::vector<int>& points) {
+  auto array = pdfium::MakeRetain<CPDF_Array>();
+  for (float point : points)
+    array->AddNew<CPDF_Number>(point);
+  return array;
+}
+
+}  // namespace
+
+TEST(CPDFAnnotTest, RectFromQuadPointsArray) {
+  RetainPtr<CPDF_Array> array = CreateQuadPointArrayFromVector(
+      {0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1});
+  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPointsArray(array.Get(), 0);
+  EXPECT_EQ(4.0f, rect.left);
+  EXPECT_EQ(5.0f, rect.bottom);
+  EXPECT_EQ(2.0f, rect.right);
+  EXPECT_EQ(3.0f, rect.top);
+
+  rect = CPDF_Annot::RectFromQuadPointsArray(array.Get(), 1);
+  EXPECT_EQ(4.0f, rect.left);
+  EXPECT_EQ(3.0f, rect.bottom);
+  EXPECT_EQ(6.0f, rect.right);
+  EXPECT_EQ(5.0f, rect.top);
+}
+
+TEST(CPDFAnnotTest, BoundingRectFromQuadPoints) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  CFX_FloatRect rect = CPDF_Annot::BoundingRectFromQuadPoints(dict.Get());
+  EXPECT_EQ(0.0f, rect.left);
+  EXPECT_EQ(0.0f, rect.bottom);
+  EXPECT_EQ(0.0f, rect.right);
+  EXPECT_EQ(0.0f, rect.top);
+
+  dict->SetFor("QuadPoints", CreateQuadPointArrayFromVector({0, 1, 2}));
+  rect = CPDF_Annot::BoundingRectFromQuadPoints(dict.Get());
+  EXPECT_EQ(0.0f, rect.left);
+  EXPECT_EQ(0.0f, rect.bottom);
+  EXPECT_EQ(0.0f, rect.right);
+  EXPECT_EQ(0.0f, rect.top);
+
+  dict->SetFor("QuadPoints",
+               CreateQuadPointArrayFromVector({0, 1, 2, 3, 4, 5, 6, 7}));
+  rect = CPDF_Annot::BoundingRectFromQuadPoints(dict.Get());
+  EXPECT_EQ(4.0f, rect.left);
+  EXPECT_EQ(5.0f, rect.bottom);
+  EXPECT_EQ(2.0f, rect.right);
+  EXPECT_EQ(3.0f, rect.top);
+
+  dict->SetFor("QuadPoints", CreateQuadPointArrayFromVector(
+                                 {0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5,
+                                  4, 3, 2, 1, 9, 2, 5, 7, 3, 6, 4, 1}));
+  rect = CPDF_Annot::BoundingRectFromQuadPoints(dict.Get());
+  EXPECT_EQ(2.0f, rect.left);
+  EXPECT_EQ(3.0f, rect.bottom);
+  EXPECT_EQ(6.0f, rect.right);
+  EXPECT_EQ(7.0f, rect.top);
+}
+
+TEST(CPDFAnnotTest, RectFromQuadPoints) {
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(dict.Get(), 0);
+  EXPECT_EQ(0.0f, rect.left);
+  EXPECT_EQ(0.0f, rect.bottom);
+  EXPECT_EQ(0.0f, rect.right);
+  EXPECT_EQ(0.0f, rect.top);
+  rect = CPDF_Annot::RectFromQuadPoints(dict.Get(), 5);
+  EXPECT_EQ(0.0f, rect.left);
+  EXPECT_EQ(0.0f, rect.bottom);
+  EXPECT_EQ(0.0f, rect.right);
+  EXPECT_EQ(0.0f, rect.top);
+
+  dict->SetFor("QuadPoints",
+               CreateQuadPointArrayFromVector({0, 1, 2, 3, 4, 5, 6, 7}));
+  rect = CPDF_Annot::RectFromQuadPoints(dict.Get(), 0);
+  EXPECT_EQ(4.0f, rect.left);
+  EXPECT_EQ(5.0f, rect.bottom);
+  EXPECT_EQ(2.0f, rect.right);
+  EXPECT_EQ(3.0f, rect.top);
+  rect = CPDF_Annot::RectFromQuadPoints(dict.Get(), 5);
+  EXPECT_EQ(0.0f, rect.left);
+  EXPECT_EQ(0.0f, rect.bottom);
+  EXPECT_EQ(0.0f, rect.right);
+  EXPECT_EQ(0.0f, rect.top);
+
+  dict->SetFor("QuadPoints", CreateQuadPointArrayFromVector(
+                                 {0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5,
+                                  4, 3, 2, 1, 9, 2, 5, 7, 3, 6, 4, 1}));
+  rect = CPDF_Annot::RectFromQuadPoints(dict.Get(), 0);
+  EXPECT_EQ(4.0f, rect.left);
+  EXPECT_EQ(5.0f, rect.bottom);
+  EXPECT_EQ(2.0f, rect.right);
+  EXPECT_EQ(3.0f, rect.top);
+  rect = CPDF_Annot::RectFromQuadPoints(dict.Get(), 1);
+  EXPECT_EQ(4.0f, rect.left);
+  EXPECT_EQ(3.0f, rect.bottom);
+  EXPECT_EQ(6.0f, rect.right);
+  EXPECT_EQ(5.0f, rect.top);
+  rect = CPDF_Annot::RectFromQuadPoints(dict.Get(), 2);
+  EXPECT_EQ(3.0f, rect.left);
+  EXPECT_EQ(6.0f, rect.bottom);
+  EXPECT_EQ(5.0f, rect.right);
+  EXPECT_EQ(7.0f, rect.top);
+}
+
+TEST(CPDFAnnotTest, QuadPointCount) {
+  RetainPtr<CPDF_Array> array = CreateQuadPointArrayFromVector({});
+  EXPECT_EQ(0u, CPDF_Annot::QuadPointCount(array.Get()));
+
+  for (int i = 0; i < 7; ++i) {
+    array->AddNew<CPDF_Number>(0);
+    EXPECT_EQ(0u, CPDF_Annot::QuadPointCount(array.Get()));
+  }
+  for (int i = 0; i < 8; ++i) {
+    array->AddNew<CPDF_Number>(0);
+    EXPECT_EQ(1u, CPDF_Annot::QuadPointCount(array.Get()));
+  }
+  for (int i = 0; i < 50; ++i)
+    array->AddNew<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 192649d..37208e3 100644
--- a/core/fpdfdoc/cpdf_annotlist.cpp
+++ b/core/fpdfdoc/cpdf_annotlist.cpp
@@ -10,8 +10,14 @@
 #include <memory>
 #include <utility>
 
+#include "constants/annotation_common.h"
+#include "constants/annotation_flags.h"
+#include "constants/form_fields.h"
+#include "constants/form_flags.h"
+#include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
@@ -19,36 +25,77 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "core/fpdfdoc/cpdf_occontext.h"
+#include "core/fpdfdoc/cpdf_formfield.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 {
 
-std::unique_ptr<CPDF_Annot> CreatePopupAnnot(CPDF_Annot* pAnnot,
-                                             CPDF_Document* pDocument,
-                                             CPDF_Page* pPage) {
-  CPDF_Dictionary* pParentDict = pAnnot->GetAnnotDict();
+bool PopupAppearsForAnnotType(CPDF_Annot::Subtype subtype) {
+  switch (subtype) {
+    case CPDF_Annot::Subtype::TEXT:
+    case CPDF_Annot::Subtype::LINE:
+    case CPDF_Annot::Subtype::SQUARE:
+    case CPDF_Annot::Subtype::CIRCLE:
+    case CPDF_Annot::Subtype::POLYGON:
+    case CPDF_Annot::Subtype::POLYLINE:
+    case CPDF_Annot::Subtype::HIGHLIGHT:
+    case CPDF_Annot::Subtype::UNDERLINE:
+    case CPDF_Annot::Subtype::SQUIGGLY:
+    case CPDF_Annot::Subtype::STRIKEOUT:
+    case CPDF_Annot::Subtype::STAMP:
+    case CPDF_Annot::Subtype::CARET:
+    case CPDF_Annot::Subtype::INK:
+    case CPDF_Annot::Subtype::FILEATTACHMENT:
+      return true;
+    case CPDF_Annot::Subtype::UNKNOWN:
+    case CPDF_Annot::Subtype::LINK:
+    case CPDF_Annot::Subtype::FREETEXT:
+    case CPDF_Annot::Subtype::POPUP:
+    case CPDF_Annot::Subtype::SOUND:
+    case CPDF_Annot::Subtype::MOVIE:
+    case CPDF_Annot::Subtype::WIDGET:
+    case CPDF_Annot::Subtype::SCREEN:
+    case CPDF_Annot::Subtype::PRINTERMARK:
+    case CPDF_Annot::Subtype::TRAPNET:
+    case CPDF_Annot::Subtype::WATERMARK:
+    case CPDF_Annot::Subtype::THREED:
+    case CPDF_Annot::Subtype::RICHMEDIA:
+    case CPDF_Annot::Subtype::XFAWIDGET:
+    default:
+      return false;
+  }
+}
+
+std::unique_ptr<CPDF_Annot> CreatePopupAnnot(CPDF_Document* pDocument,
+                                             CPDF_Page* pPage,
+                                             CPDF_Annot* pAnnot) {
+  if (!PopupAppearsForAnnotType(pAnnot->GetSubtype()))
+    return nullptr;
+
+  const CPDF_Dictionary* pParentDict = pAnnot->GetAnnotDict();
   if (!pParentDict)
     return nullptr;
 
   // TODO(jaepark): We shouldn't strip BOM for some strings and not for others.
   // See pdfium:593.
-  WideString sContents = pParentDict->GetUnicodeTextFor("Contents");
+  WideString sContents =
+      pParentDict->GetUnicodeTextFor(pdfium::annotation::kContents);
   if (sContents.IsEmpty())
     return nullptr;
 
-  auto pAnnotDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(pDocument->GetByteStringPool());
-  pAnnotDict->SetNewFor<CPDF_Name>("Type", "Annot");
-  pAnnotDict->SetNewFor<CPDF_Name>("Subtype", "Popup");
-  pAnnotDict->SetNewFor<CPDF_String>("T", pParentDict->GetStringFor("T"),
-                                     false);
-  pAnnotDict->SetNewFor<CPDF_String>("Contents", sContents.UTF8Encode(), false);
+  auto pAnnotDict = pDocument->New<CPDF_Dictionary>();
+  pAnnotDict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "Annot");
+  pAnnotDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Popup");
+  pAnnotDict->SetNewFor<CPDF_String>(
+      pdfium::form_fields::kT,
+      pParentDict->GetStringFor(pdfium::form_fields::kT), false);
+  pAnnotDict->SetNewFor<CPDF_String>(pdfium::annotation::kContents,
+                                     sContents.ToUTF8(), false);
 
-  CFX_FloatRect rect = pParentDict->GetRectFor("Rect");
+  CFX_FloatRect rect = pParentDict->GetRectFor(pdfium::annotation::kRect);
   rect.Normalize();
   CFX_FloatRect popupRect(0, 0, 200, 200);
   // Note that if the popup can set its own dimensions, then we will need to
@@ -66,8 +113,8 @@
         std::max(rect.bottom - popupRect.Height(), 0.f));
   }
 
-  pAnnotDict->SetRectFor("Rect", popupRect);
-  pAnnotDict->SetNewFor<CPDF_Number>("F", 0);
+  pAnnotDict->SetRectFor(pdfium::annotation::kRect, popupRect);
+  pAnnotDict->SetNewFor<CPDF_Number>(pdfium::annotation::kF, 0);
 
   auto pPopupAnnot =
       pdfium::MakeUnique<CPDF_Annot>(std::move(pAnnotDict), pDocument);
@@ -76,88 +123,103 @@
 }
 
 void GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
-  if (!pAnnotDict || pAnnotDict->GetStringFor("Subtype") != "Widget")
+  if (!pAnnotDict ||
+      pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) != "Widget") {
     return;
+  }
 
-  CPDF_Object* pFieldTypeObj = FPDF_GetFieldAttr(pAnnotDict, "FT");
+  CPDF_Object* pFieldTypeObj =
+      CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFT);
   if (!pFieldTypeObj)
     return;
 
   ByteString field_type = pFieldTypeObj->GetString();
-  if (field_type == "Tx") {
-    CPVT_GenerateAP::GenerateFormAP(CPVT_GenerateAP::kTextField, pDoc,
-                                    pAnnotDict);
+  if (field_type == pdfium::form_fields::kTx) {
+    CPVT_GenerateAP::GenerateFormAP(pDoc, pAnnotDict,
+                                    CPVT_GenerateAP::kTextField);
     return;
   }
 
-  CPDF_Object* pFieldFlagsObj = FPDF_GetFieldAttr(pAnnotDict, "Ff");
+  CPDF_Object* pFieldFlagsObj =
+      CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFf);
   uint32_t flags = pFieldFlagsObj ? pFieldFlagsObj->GetInteger() : 0;
-  if (field_type == "Ch") {
-    CPVT_GenerateAP::GenerateFormAP((flags & (1 << 17))
-                                        ? CPVT_GenerateAP::kComboBox
-                                        : CPVT_GenerateAP::kListBox,
-                                    pDoc, pAnnotDict);
+  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);
     return;
   }
 
-  if (field_type != "Btn")
+  if (field_type != pdfium::form_fields::kBtn)
     return;
-  if (flags & (1 << 16))
+  if (flags & pdfium::form_flags::kButtonPushbutton)
     return;
-  if (pAnnotDict->KeyExist("AS"))
+  if (pAnnotDict->KeyExist(pdfium::annotation::kAS))
     return;
 
-  CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
-  if (!pParentDict || !pParentDict->KeyExist("AS"))
+  CPDF_Dictionary* pParentDict =
+      pAnnotDict->GetDictFor(pdfium::form_fields::kParent);
+  if (!pParentDict || !pParentDict->KeyExist(pdfium::annotation::kAS))
     return;
 
-  pAnnotDict->SetNewFor<CPDF_String>("AS", pParentDict->GetStringFor("AS"),
-                                     false);
-  return;
+  pAnnotDict->SetNewFor<CPDF_String>(
+      pdfium::annotation::kAS,
+      pParentDict->GetStringFor(pdfium::annotation::kAS), false);
 }
 
 }  // namespace
 
 CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage)
-    : m_pDocument(pPage->m_pDocument.Get()) {
-  if (!pPage->m_pFormDict)
-    return;
-
-  CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
+    : m_pDocument(pPage->GetDocument()) {
+  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
   if (!pAnnots)
     return;
 
   const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
-  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
-  bool bRegenerateAP = pAcroForm && pAcroForm->GetBooleanFor("NeedAppearances");
-  for (size_t i = 0; i < pAnnots->GetCount(); ++i) {
+  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));
     if (!pDict)
       continue;
-    const ByteString subtype = pDict->GetStringFor("Subtype");
+    const ByteString subtype =
+        pDict->GetStringFor(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);
-    m_AnnotList.push_back(pdfium::MakeUnique<CPDF_Annot>(pDict, m_pDocument));
+    pAnnots->ConvertToIndirectObjectAt(i, m_pDocument.Get());
+    m_AnnotList.push_back(
+        pdfium::MakeUnique<CPDF_Annot>(pDict, m_pDocument.Get()));
     if (bRegenerateAP && subtype == "Widget" &&
-        CPDF_InterForm::IsUpdateAPEnabled() && !pDict->GetDictFor("AP")) {
-      GenerateAP(m_pDocument, pDict);
+        CPDF_InteractiveForm::IsUpdateAPEnabled() &&
+        !pDict->GetDictFor(pdfium::annotation::kAP)) {
+      GenerateAP(m_pDocument.Get(), pDict);
     }
   }
 
-  size_t nAnnotListSize = m_AnnotList.size();
-  for (size_t i = 0; i < nAnnotListSize; ++i) {
-    std::unique_ptr<CPDF_Annot> pPopupAnnot(
-        CreatePopupAnnot(m_AnnotList[i].get(), m_pDocument, pPage));
+  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());
     if (pPopupAnnot)
       m_AnnotList.push_back(std::move(pPopupAnnot));
   }
 }
 
-CPDF_AnnotList::~CPDF_AnnotList() {}
+CPDF_AnnotList::~CPDF_AnnotList() {
+  // Move the pop-up annotations out of |m_AnnotList| into |popups|. Then
+  // destroy |m_AnnotList| first. This prevents dangling pointers to the pop-up
+  // annotations.
+  size_t nPopupCount = m_AnnotList.size() - m_nAnnotCount;
+  std::vector<std::unique_ptr<CPDF_Annot>> popups(nPopupCount);
+  for (size_t i = 0; i < nPopupCount; ++i)
+    popups[i] = std::move(m_AnnotList[m_nAnnotCount + i]);
+  m_AnnotList.clear();
+}
 
 void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage,
                                  CFX_RenderDevice* pDevice,
@@ -173,20 +235,21 @@
       continue;
 
     uint32_t annot_flags = pAnnot->GetFlags();
-    if (annot_flags & ANNOTFLAG_HIDDEN)
+    if (annot_flags & pdfium::annotation_flags::kHidden)
       continue;
 
-    if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0)
+    if (bPrinting && (annot_flags & pdfium::annotation_flags::kPrint) == 0)
       continue;
 
-    if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW))
+    if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView))
       continue;
 
     if (pOptions) {
-      CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-      if (pOptions->GetOCContext() && pAnnotDict &&
-          !pOptions->GetOCContext()->CheckOCGVisible(
-              pAnnotDict->GetDictFor("OC"))) {
+      const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
+      const CPDF_OCContext* pOCContext = pOptions->GetOCContext();
+      if (pAnnotDict && pOCContext &&
+          !pOCContext->CheckOCGVisible(
+              pAnnotDict->GetDictFor(pdfium::annotation::kOC))) {
         continue;
       }
     }
@@ -216,11 +279,11 @@
                                    uint32_t dwAnnotFlags,
                                    CPDF_RenderOptions* pOptions,
                                    FX_RECT* pClipRect) {
-  if (dwAnnotFlags & ANNOTFLAG_INVISIBLE) {
+  if (dwAnnotFlags & pdfium::annotation_flags::kInvisible) {
     DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, false,
                 pOptions, pClipRect);
   }
-  if (dwAnnotFlags & ANNOTFLAG_HIDDEN) {
+  if (dwAnnotFlags & pdfium::annotation_flags::kHidden) {
     DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, true,
                 pOptions, pClipRect);
   }
@@ -232,8 +295,9 @@
                                    const CFX_Matrix* pMatrix,
                                    bool bShowWidget,
                                    CPDF_RenderOptions* pOptions) {
-  uint32_t dwAnnotFlags = bShowWidget ? ANNOTFLAG_INVISIBLE | ANNOTFLAG_HIDDEN
-                                      : ANNOTFLAG_INVISIBLE;
+  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);
 }
diff --git a/core/fpdfdoc/cpdf_annotlist.h b/core/fpdfdoc/cpdf_annotlist.h
index 3443277..85075cf 100644
--- a/core/fpdfdoc/cpdf_annotlist.h
+++ b/core/fpdfdoc/cpdf_annotlist.h
@@ -10,8 +10,10 @@
 #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;
 class CPDF_Annot;
@@ -20,10 +22,10 @@
 class CPDF_RenderContext;
 class CPDF_RenderOptions;
 
-class CPDF_AnnotList {
+class CPDF_AnnotList : public CPDF_PageRenderContext::AnnotListIface {
  public:
   explicit CPDF_AnnotList(CPDF_Page* pPage);
-  ~CPDF_AnnotList();
+  ~CPDF_AnnotList() override;
 
   void DisplayAnnots(CPDF_Page* pPage,
                      CPDF_RenderContext* pContext,
@@ -36,7 +38,7 @@
                      CFX_RenderDevice* pDevice,
                      CPDF_RenderContext* pContext,
                      bool bPrinting,
-                     const CFX_Matrix* pMatrix,
+                     const CFX_Matrix* pUser2Device,
                      uint32_t dwAnnotFlags,
                      CPDF_RenderOptions* pOptions,
                      FX_RECT* pClipRect);
@@ -57,8 +59,12 @@
                    CPDF_RenderOptions* pOptions,
                    FX_RECT* clip_rect);
 
-  CPDF_Document* const m_pDocument;
+  UnownedPtr<CPDF_Document> const m_pDocument;
+
+  // The first |m_nAnnotCount| elements are from the PDF itself. The rest are
+  // generated pop-up annotations.
   std::vector<std::unique_ptr<CPDF_Annot>> m_AnnotList;
+  size_t m_nAnnotCount = 0;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_ANNOTLIST_H_
diff --git a/core/fpdfdoc/cpdf_apsettings.cpp b/core/fpdfdoc/cpdf_apsettings.cpp
index 076cdd9..341764f 100644
--- a/core/fpdfdoc/cpdf_apsettings.cpp
+++ b/core/fpdfdoc/cpdf_apsettings.cpp
@@ -10,6 +10,7 @@
 
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fxge/cfx_color.h"
 
 CPDF_ApSettings::CPDF_ApSettings(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
@@ -37,7 +38,7 @@
     return 0;
 
   FX_ARGB color = 0;
-  size_t dwCount = pEntry->GetCount();
+  size_t dwCount = pEntry->size();
   if (dwCount == 1) {
     iColorType = CFX_Color::kGray;
     float g = pEntry->GetNumberAt(0) * 255;
@@ -87,7 +88,7 @@
   if (!pEntry)
     return;
 
-  size_t dwCount = pEntry->GetCount();
+  size_t dwCount = pEntry->size();
   if (dwCount == 1) {
     iColorType = CFX_Color::kGray;
     fc[0] = pEntry->GetNumberAt(0);
diff --git a/core/fpdfdoc/cpdf_apsettings.h b/core/fpdfdoc/cpdf_apsettings.h
index a807c2b..2a16336 100644
--- a/core/fpdfdoc/cpdf_apsettings.h
+++ b/core/fpdfdoc/cpdf_apsettings.h
@@ -10,13 +10,22 @@
 #include "core/fpdfdoc/cpdf_iconfit.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/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).
+#define TEXTPOS_CAPTION 0
+#define TEXTPOS_ICON 1
+#define TEXTPOS_BELOW 2
+#define TEXTPOS_ABOVE 3
+#define TEXTPOS_RIGHT 4
+#define TEXTPOS_LEFT 5
+#define TEXTPOS_OVERLAID 6
+
 class CPDF_ApSettings {
  public:
   explicit CPDF_ApSettings(CPDF_Dictionary* pDict);
@@ -57,6 +66,8 @@
   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;
@@ -69,7 +80,7 @@
   CPDF_Stream* GetIcon(const ByteString& csEntry) const;
 
  private:
-  UnownedPtr<CPDF_Dictionary> const m_pDict;
+  RetainPtr<CPDF_Dictionary> const m_pDict;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_APSETTINGS_H_
diff --git a/core/fpdfdoc/cpdf_bookmark.cpp b/core/fpdfdoc/cpdf_bookmark.cpp
index cc7e127..b08fba5 100644
--- a/core/fpdfdoc/cpdf_bookmark.cpp
+++ b/core/fpdfdoc/cpdf_bookmark.cpp
@@ -10,41 +10,25 @@
 #include <vector>
 
 #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"
 
-CPDF_Bookmark::CPDF_Bookmark() {}
+CPDF_Bookmark::CPDF_Bookmark() = default;
 
 CPDF_Bookmark::CPDF_Bookmark(const CPDF_Bookmark& that) = default;
 
-CPDF_Bookmark::CPDF_Bookmark(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+CPDF_Bookmark::CPDF_Bookmark(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
 
-CPDF_Bookmark::~CPDF_Bookmark() {}
-
-uint32_t CPDF_Bookmark::GetColorRef() const {
-  if (!m_pDict)
-    return FXSYS_RGB(0, 0, 0);
-
-  CPDF_Array* pColor = m_pDict->GetArrayFor("C");
-  if (!pColor)
-    return FXSYS_RGB(0, 0, 0);
-
-  int r = FXSYS_round(pColor->GetNumberAt(0) * 255);
-  int g = FXSYS_round(pColor->GetNumberAt(1) * 255);
-  int b = FXSYS_round(pColor->GetNumberAt(2) * 255);
-  return FXSYS_RGB(r, g, b);
-}
-
-uint32_t CPDF_Bookmark::GetFontStyle() const {
-  return m_pDict ? m_pDict->GetIntegerFor("F") : 0;
-}
+CPDF_Bookmark::~CPDF_Bookmark() = default;
 
 WideString CPDF_Bookmark::GetTitle() const {
   if (!m_pDict)
     return WideString();
 
-  CPDF_String* pString = ToString(m_pDict->GetDirectObjectFor("Title"));
+  const CPDF_String* pString = ToString(m_pDict->GetDirectObjectFor("Title"));
   if (!pString)
     return WideString();
 
@@ -53,7 +37,7 @@
   if (!len)
     return WideString();
 
-  std::vector<wchar_t> buf(len);
+  std::vector<wchar_t, FxAllocAllocator<wchar_t>> buf(len);
   for (int i = 0; i < len; i++) {
     wchar_t w = title[i];
     buf[i] = w > 0x20 ? w : 0x20;
@@ -65,7 +49,7 @@
   if (!m_pDict)
     return CPDF_Dest();
 
-  CPDF_Object* pDest = m_pDict->GetDirectObjectFor("Dest");
+  const CPDF_Object* pDest = m_pDict->GetDirectObjectFor("Dest");
   if (!pDest)
     return CPDF_Dest();
   if (pDest->IsString() || pDest->IsName()) {
@@ -73,7 +57,7 @@
     return CPDF_Dest(
         name_tree.LookupNamedDest(pDocument, pDest->GetUnicodeText()));
   }
-  if (CPDF_Array* pArray = pDest->AsArray())
+  if (const CPDF_Array* pArray = pDest->AsArray())
     return CPDF_Dest(pArray);
   return CPDF_Dest();
 }
diff --git a/core/fpdfdoc/cpdf_bookmark.h b/core/fpdfdoc/cpdf_bookmark.h
index 60c86dd..b185c03 100644
--- a/core/fpdfdoc/cpdf_bookmark.h
+++ b/core/fpdfdoc/cpdf_bookmark.h
@@ -10,7 +10,7 @@
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
@@ -19,18 +19,17 @@
  public:
   CPDF_Bookmark();
   CPDF_Bookmark(const CPDF_Bookmark& that);
-  explicit CPDF_Bookmark(CPDF_Dictionary* pDict);
+  explicit CPDF_Bookmark(const CPDF_Dictionary* pDict);
   ~CPDF_Bookmark();
 
-  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
-  uint32_t GetColorRef() const;
-  uint32_t GetFontStyle() const;
+  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+
   WideString GetTitle() const;
   CPDF_Dest GetDest(CPDF_Document* pDocument) const;
   CPDF_Action GetAction() const;
 
  private:
-  UnownedPtr<CPDF_Dictionary> m_pDict;
+  RetainPtr<const CPDF_Dictionary> m_pDict;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_BOOKMARK_H_
diff --git a/core/fpdfdoc/cpdf_bookmarktree.cpp b/core/fpdfdoc/cpdf_bookmarktree.cpp
index 33c9f3d..5c4fffe 100644
--- a/core/fpdfdoc/cpdf_bookmarktree.cpp
+++ b/core/fpdfdoc/cpdf_bookmarktree.cpp
@@ -6,19 +6,19 @@
 
 #include "core/fpdfdoc/cpdf_bookmarktree.h"
 
+#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() {}
+CPDF_BookmarkTree::~CPDF_BookmarkTree() = default;
 
-CPDF_Bookmark CPDF_BookmarkTree::GetFirstChild(
-    const CPDF_Bookmark& parent) const {
-  CPDF_Dictionary* pParentDict = parent.GetDict();
+CPDF_Bookmark CPDF_BookmarkTree::GetFirstChild(CPDF_Bookmark* parent) const {
+  const CPDF_Dictionary* pParentDict = parent->GetDict();
   if (pParentDict)
     return CPDF_Bookmark(pParentDict->GetDictFor("First"));
 
-  const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
+  CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
   if (!pRoot)
     return CPDF_Bookmark();
 
@@ -27,12 +27,11 @@
                    : CPDF_Bookmark();
 }
 
-CPDF_Bookmark CPDF_BookmarkTree::GetNextSibling(
-    const CPDF_Bookmark& bookmark) const {
-  CPDF_Dictionary* pDict = bookmark.GetDict();
+CPDF_Bookmark CPDF_BookmarkTree::GetNextSibling(CPDF_Bookmark* bookmark) const {
+  const CPDF_Dictionary* pDict = bookmark->GetDict();
   if (!pDict)
     return CPDF_Bookmark();
 
-  CPDF_Dictionary* pNext = pDict->GetDictFor("Next");
+  const CPDF_Dictionary* pNext = pDict->GetDictFor("Next");
   return pNext == pDict ? CPDF_Bookmark() : CPDF_Bookmark(pNext);
 }
diff --git a/core/fpdfdoc/cpdf_bookmarktree.h b/core/fpdfdoc/cpdf_bookmarktree.h
index 3729dfc..b374bfa 100644
--- a/core/fpdfdoc/cpdf_bookmarktree.h
+++ b/core/fpdfdoc/cpdf_bookmarktree.h
@@ -17,8 +17,8 @@
   explicit CPDF_BookmarkTree(CPDF_Document* pDoc);
   ~CPDF_BookmarkTree();
 
-  CPDF_Bookmark GetFirstChild(const CPDF_Bookmark& parent) const;
-  CPDF_Bookmark GetNextSibling(const CPDF_Bookmark& bookmark) const;
+  CPDF_Bookmark GetFirstChild(CPDF_Bookmark* parent) const;
+  CPDF_Bookmark GetNextSibling(CPDF_Bookmark* bookmark) const;
   CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
 
  private:
diff --git a/core/fpdfdoc/cpdf_color_utils.cpp b/core/fpdfdoc/cpdf_color_utils.cpp
new file mode 100644
index 0000000..198b1ca
--- /dev/null
+++ b/core/fpdfdoc/cpdf_color_utils.cpp
@@ -0,0 +1,53 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfdoc/cpdf_color_utils.h"
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfdoc/cpdf_defaultappearance.h"
+#include "core/fxcrt/bytestring.h"
+
+namespace fpdfdoc {
+
+CFX_Color CFXColorFromArray(const CPDF_Array& array) {
+  CFX_Color rt;
+  switch (array.size()) {
+    case 1:
+      rt = CFX_Color(CFX_Color::kGray, array.GetNumberAt(0));
+      break;
+    case 3:
+      rt = CFX_Color(CFX_Color::kRGB, array.GetNumberAt(0),
+                     array.GetNumberAt(1), array.GetNumberAt(2));
+      break;
+    case 4:
+      rt = CFX_Color(CFX_Color::kCMYK, array.GetNumberAt(0),
+                     array.GetNumberAt(1), array.GetNumberAt(2),
+                     array.GetNumberAt(3));
+      break;
+  }
+  return rt;
+}
+
+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);
+}
+
+}  // namespace fpdfdoc
diff --git a/core/fpdfdoc/cpdf_color_utils.h b/core/fpdfdoc/cpdf_color_utils.h
new file mode 100644
index 0000000..993a7d7
--- /dev/null
+++ b/core/fpdfdoc/cpdf_color_utils.h
@@ -0,0 +1,25 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFDOC_CPDF_COLOR_UTILS_H_
+#define CORE_FPDFDOC_CPDF_COLOR_UTILS_H_
+
+#include "core/fxge/cfx_color.h"
+
+class CPDF_Array;
+
+namespace fxcrt {
+class ByteString;
+}
+
+namespace fpdfdoc {
+
+CFX_Color CFXColorFromArray(const CPDF_Array& array);
+CFX_Color CFXColorFromString(const fxcrt::ByteString& str);
+
+}  // namespace fpdfdoc
+
+#endif  // CORE_FPDFDOC_CPDF_COLOR_UTILS_H_
diff --git a/core/fpdfdoc/cpdf_defaultappearance.cpp b/core/fpdfdoc/cpdf_defaultappearance.cpp
index 02d6eea..e6ef4f7 100644
--- a/core/fpdfdoc/cpdf_defaultappearance.cpp
+++ b/core/fpdfdoc/cpdf_defaultappearance.cpp
@@ -7,215 +7,128 @@
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 
 #include <algorithm>
+#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxge/cfx_color.h"
 
-bool CPDF_DefaultAppearance::HasFont() {
-  if (m_csDA.IsEmpty())
-    return false;
+namespace {
 
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  return syntax.FindTagParamFromStart("Tf", 2);
-}
+// Find the token and its |nParams| parameters from the start of data,
+// and move the current position to the start of those parameters.
+bool FindTagParamFromStart(CPDF_SimpleParser* parser,
+                           ByteStringView token,
+                           int nParams) {
+  nParams++;
 
-ByteString CPDF_DefaultAppearance::GetFontString() {
-  ByteString csFont;
-  if (m_csDA.IsEmpty())
-    return csFont;
+  std::vector<uint32_t> pBuf(nParams);
+  int buf_index = 0;
+  int buf_count = 0;
 
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (syntax.FindTagParamFromStart("Tf", 2)) {
-    csFont += syntax.GetWord();
-    csFont += " ";
-    csFont += syntax.GetWord();
-    csFont += " ";
-    csFont += syntax.GetWord();
+  parser->SetCurPos(0);
+  while (1) {
+    pBuf[buf_index++] = parser->GetCurPos();
+    if (buf_index == nParams)
+      buf_index = 0;
+
+    buf_count++;
+    if (buf_count > nParams)
+      buf_count = nParams;
+
+    ByteStringView word = parser->GetWord();
+    if (word.IsEmpty())
+      return false;
+
+    if (word == token) {
+      if (buf_count < nParams)
+        continue;
+
+      parser->SetCurPos(pBuf[buf_index]);
+      return true;
+    }
   }
-  return csFont;
+  return false;
 }
 
-ByteString CPDF_DefaultAppearance::GetFont(float* fFontSize) {
+}  // namespace
+
+Optional<ByteString> CPDF_DefaultAppearance::GetFont(float* fFontSize) {
   *fFontSize = 0.0f;
   if (m_csDA.IsEmpty())
-    return ByteString();
+    return {};
 
   ByteString csFontNameTag;
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (syntax.FindTagParamFromStart("Tf", 2)) {
+  CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span());
+  if (FindTagParamFromStart(&syntax, "Tf", 2)) {
     csFontNameTag = ByteString(syntax.GetWord());
     csFontNameTag.Delete(0, 1);
-    *fFontSize = FX_atof(syntax.GetWord());
+    *fFontSize = StringToFloat(syntax.GetWord());
   }
-  return PDF_NameDecode(csFontNameTag);
+  return {PDF_NameDecode(csFontNameTag.AsStringView())};
 }
 
-bool CPDF_DefaultAppearance::HasColor(PaintOperation nOperation) {
-  if (m_csDA.IsEmpty())
-    return false;
-
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "G" : "g"), 1)) {
-    return true;
-  }
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "RG" : "rg"), 3)) {
-    return true;
-  }
-  return syntax.FindTagParamFromStart(
-      (nOperation == PaintOperation::STROKE ? "K" : "k"), 4);
-}
-
-ByteString CPDF_DefaultAppearance::GetColorString(PaintOperation nOperation) {
-  ByteString csColor;
-  if (m_csDA.IsEmpty())
-    return csColor;
-
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "G" : "g"), 1)) {
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-    return csColor;
-  }
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "RG" : "rg"), 3)) {
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-    return csColor;
-  }
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "K" : "k"), 4)) {
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-    csColor += " ";
-    csColor += syntax.GetWord();
-  }
-  return csColor;
-}
-
-void CPDF_DefaultAppearance::GetColor(int& iColorType,
-                                      float fc[4],
-                                      PaintOperation nOperation) {
-  iColorType = CFX_Color::kTransparent;
+Optional<CFX_Color::Type> CPDF_DefaultAppearance::GetColor(float fc[4]) {
   for (int c = 0; c < 4; c++)
     fc[c] = 0;
 
   if (m_csDA.IsEmpty())
-    return;
+    return {};
 
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "G" : "g"), 1)) {
-    iColorType = CFX_Color::kGray;
-    fc[0] = FX_atof(syntax.GetWord());
-    return;
+  CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span());
+  if (FindTagParamFromStart(&syntax, "g", 1)) {
+    fc[0] = StringToFloat(syntax.GetWord());
+    return {CFX_Color::kGray};
   }
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "RG" : "rg"), 3)) {
-    iColorType = CFX_Color::kRGB;
-    fc[0] = FX_atof(syntax.GetWord());
-    fc[1] = FX_atof(syntax.GetWord());
-    fc[2] = FX_atof(syntax.GetWord());
-    return;
+  if (FindTagParamFromStart(&syntax, "rg", 3)) {
+    fc[0] = StringToFloat(syntax.GetWord());
+    fc[1] = StringToFloat(syntax.GetWord());
+    fc[2] = StringToFloat(syntax.GetWord());
+    return {CFX_Color::kRGB};
   }
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "K" : "k"), 4)) {
-    iColorType = CFX_Color::kCMYK;
-    fc[0] = FX_atof(syntax.GetWord());
-    fc[1] = FX_atof(syntax.GetWord());
-    fc[2] = FX_atof(syntax.GetWord());
-    fc[3] = FX_atof(syntax.GetWord());
+  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 {};
 }
 
-void CPDF_DefaultAppearance::GetColor(FX_ARGB& color,
-                                      int& iColorType,
-                                      PaintOperation nOperation) {
-  color = 0;
-  iColorType = CFX_Color::kTransparent;
-  if (m_csDA.IsEmpty())
-    return;
+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};
 
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "G" : "g"), 1)) {
-    iColorType = CFX_Color::kGray;
-    float g = FX_atof(syntax.GetWord()) * 255 + 0.5f;
-    color = ArgbEncode(255, (int)g, (int)g, (int)g);
-    return;
+  if (*type == CFX_Color::kGray) {
+    int g = static_cast<int>(values[0] * 255 + 0.5f);
+    return {type, ArgbEncode(255, g, g, g)};
   }
-  if (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "RG" : "rg"), 3)) {
-    iColorType = CFX_Color::kRGB;
-    float r = FX_atof(syntax.GetWord()) * 255 + 0.5f;
-    float g = FX_atof(syntax.GetWord()) * 255 + 0.5f;
-    float b = FX_atof(syntax.GetWord()) * 255 + 0.5f;
-    color = ArgbEncode(255, (int)r, (int)g, (int)b);
-    return;
+  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 (syntax.FindTagParamFromStart(
-          (nOperation == PaintOperation::STROKE ? "K" : "k"), 4)) {
-    iColorType = CFX_Color::kCMYK;
-    float c = FX_atof(syntax.GetWord());
-    float m = FX_atof(syntax.GetWord());
-    float y = FX_atof(syntax.GetWord());
-    float k = FX_atof(syntax.GetWord());
-    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);
-    color = ArgbEncode(255, (int)(r * 255 + 0.5f), (int)(g * 255 + 0.5f),
-                       (int)(b * 255 + 0.5f));
+  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))};
   }
+  NOTREACHED();
+  return {{}, 0};
 }
 
-bool CPDF_DefaultAppearance::HasTextMatrix() {
-  if (m_csDA.IsEmpty())
-    return false;
-
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  return syntax.FindTagParamFromStart("Tm", 6);
-}
-
-ByteString CPDF_DefaultAppearance::GetTextMatrixString() {
-  ByteString csTM;
-  if (m_csDA.IsEmpty())
-    return csTM;
-
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (syntax.FindTagParamFromStart("Tm", 6)) {
-    for (int i = 0; i < 6; i++) {
-      csTM += syntax.GetWord();
-      csTM += " ";
-    }
-    csTM += syntax.GetWord();
-  }
-  return csTM;
-}
-
-CFX_Matrix CPDF_DefaultAppearance::GetTextMatrix() {
-  if (m_csDA.IsEmpty())
-    return CFX_Matrix();
-
-  CPDF_SimpleParser syntax(m_csDA.AsStringView());
-  if (!syntax.FindTagParamFromStart("Tm", 6))
-    return CFX_Matrix();
-
-  float f[6];
-  for (int i = 0; i < 6; i++)
-    f[i] = FX_atof(syntax.GetWord());
-  return CFX_Matrix(f);
+bool CPDF_DefaultAppearance::FindTagParamFromStartForTesting(
+    CPDF_SimpleParser* parser,
+    ByteStringView token,
+    int nParams) {
+  return FindTagParamFromStart(parser, token, nParams);
 }
diff --git a/core/fpdfdoc/cpdf_defaultappearance.h b/core/fpdfdoc/cpdf_defaultappearance.h
index 33d4bfd..97762f8 100644
--- a/core/fpdfdoc/cpdf_defaultappearance.h
+++ b/core/fpdfdoc/cpdf_defaultappearance.h
@@ -7,42 +7,29 @@
 #ifndef CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_
 #define CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_
 
-#include "core/fpdfdoc/cpdf_defaultappearance.h"
-#include "core/fxcrt/fx_coordinates.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/fxge/cfx_color.h"
 #include "core/fxge/fx_dib.h"
 
-enum class BorderStyle { SOLID, DASH, BEVELED, INSET, UNDERLINE };
-enum class PaintOperation { STROKE, FILL };
-
 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(const CPDF_DefaultAppearance& cDA) {
-    m_csDA = cDA.GetStr();
-  }
+  Optional<ByteString> GetFont(float* fFontSize);
 
-  ByteString GetStr() const { return m_csDA; }
+  Optional<CFX_Color::Type> GetColor(float fc[4]);
+  std::pair<Optional<CFX_Color::Type>, FX_ARGB> GetColor();
 
-  bool HasFont();
-  ByteString GetFontString();
-  ByteString GetFont(float* fFontSize);
-
-  bool HasColor(PaintOperation nOperation = PaintOperation::FILL);
-  ByteString GetColorString(PaintOperation nOperation = PaintOperation::FILL);
-  void GetColor(int& iColorType,
-                float fc[4],
-                PaintOperation nOperation = PaintOperation::FILL);
-  void GetColor(FX_ARGB& color,
-                int& iColorType,
-                PaintOperation nOperation = PaintOperation::FILL);
-
-  bool HasTextMatrix();
-  ByteString GetTextMatrixString();
-  CFX_Matrix GetTextMatrix();
+  bool FindTagParamFromStartForTesting(CPDF_SimpleParser* parser,
+                                       ByteStringView token,
+                                       int nParams);
 
  private:
   ByteString m_csDA;
diff --git a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
new file mode 100644
index 0000000..6f6c525
--- /dev/null
+++ b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
@@ -0,0 +1,48 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfdoc/cpdf_defaultappearance.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/test_support.h"
+#include "third_party/base/span.h"
+
+TEST(CPDFDefaultAppearanceTest, FindTagParamFromStart) {
+  static const struct FindTagTestStruct {
+    const unsigned char* input;
+    unsigned int input_size;
+    const char* token;
+    int num_params;
+    bool result;
+    unsigned int result_pos;
+  } test_data[] = {
+      // Empty strings.
+      STR_IN_TEST_CASE("", "Tj", 1, false, 0),
+      STR_IN_TEST_CASE("", "", 1, false, 0),
+      // Empty token.
+      STR_IN_TEST_CASE("  T j", "", 1, false, 5),
+      // No parameter.
+      STR_IN_TEST_CASE("Tj", "Tj", 1, false, 2),
+      STR_IN_TEST_CASE("(Tj", "Tj", 1, false, 3),
+      // Partial token match.
+      STR_IN_TEST_CASE("\r12\t34  56 78Tj", "Tj", 1, false, 15),
+      // Regular cases with various parameters.
+      STR_IN_TEST_CASE("\r\0abd Tj", "Tj", 1, true, 0),
+      STR_IN_TEST_CASE("12 4 Tj 3 46 Tj", "Tj", 1, true, 2),
+      STR_IN_TEST_CASE("er^ 2 (34) (5667) Tj", "Tj", 2, true, 5),
+      STR_IN_TEST_CASE("<344> (232)\t343.4\n12 45 Tj", "Tj", 3, true, 11),
+      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) {
+    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))
+        << " 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 0098f73..1a07acf 100644
--- a/core/fpdfdoc/cpdf_dest.cpp
+++ b/core/fpdfdoc/cpdf_dest.cpp
@@ -32,52 +32,38 @@
 
 CPDF_Dest::CPDF_Dest() {}
 
-CPDF_Dest::CPDF_Dest(const CPDF_Dest& pObj) = default;
+CPDF_Dest::CPDF_Dest(const CPDF_Array* pArray) : m_pArray(pArray) {}
 
-CPDF_Dest::CPDF_Dest(CPDF_Object* pObj) : m_pObj(pObj) {}
+CPDF_Dest::CPDF_Dest(const CPDF_Dest& that) = default;
 
 CPDF_Dest::~CPDF_Dest() {}
 
-int CPDF_Dest::GetPageIndex(CPDF_Document* pDoc) const {
-  CPDF_Array* pArray = ToArray(m_pObj.Get());
-  if (!pArray)
-    return 0;
+int CPDF_Dest::GetDestPageIndex(CPDF_Document* pDoc) const {
+  if (!m_pArray)
+    return -1;
 
-  CPDF_Object* pPage = pArray->GetDirectObjectAt(0);
+  const CPDF_Object* pPage = m_pArray->GetDirectObjectAt(0);
   if (!pPage)
-    return 0;
+    return -1;
+
   if (pPage->IsNumber())
     return pPage->GetInteger();
+
   if (!pPage->IsDictionary())
-    return 0;
+    return -1;
+
   return pDoc->GetPageIndex(pPage->GetObjNum());
 }
 
-uint32_t CPDF_Dest::GetPageObjNum() const {
-  CPDF_Array* pArray = ToArray(m_pObj.Get());
-  if (!pArray)
-    return 0;
-
-  CPDF_Object* pPage = pArray->GetDirectObjectAt(0);
-  if (!pPage)
-    return 0;
-  if (pPage->IsNumber())
-    return pPage->GetInteger();
-  if (pPage->IsDictionary())
-    return pPage->GetObjNum();
-  return 0;
-}
-
 int CPDF_Dest::GetZoomMode() const {
-  CPDF_Array* pArray = ToArray(m_pObj.Get());
+  if (!m_pArray)
+    return 0;
+
+  const CPDF_Object* pArray = m_pArray->GetDirectObjectAt(1);
   if (!pArray)
     return 0;
 
-  CPDF_Object* pObj = pArray->GetDirectObjectAt(1);
-  if (!pObj)
-    return 0;
-
-  ByteString mode = pObj->GetString();
+  ByteString mode = pArray->GetString();
   for (int i = 1; g_sZoomModes[i]; ++i) {
     if (mode == g_sZoomModes[i])
       return i;
@@ -96,20 +82,19 @@
   *pHasY = false;
   *pHasZoom = false;
 
-  CPDF_Array* pArray = ToArray(m_pObj.Get());
-  if (!pArray)
+  if (!m_pArray)
     return false;
 
-  if (pArray->GetCount() < 5)
+  if (m_pArray->size() < 5)
     return false;
 
-  const CPDF_Name* xyz = ToName(pArray->GetDirectObjectAt(1));
+  const CPDF_Name* xyz = ToName(m_pArray->GetDirectObjectAt(1));
   if (!xyz || xyz->GetString() != "XYZ")
     return false;
 
-  const CPDF_Number* numX = ToNumber(pArray->GetDirectObjectAt(2));
-  const CPDF_Number* numY = ToNumber(pArray->GetDirectObjectAt(3));
-  const CPDF_Number* numZoom = ToNumber(pArray->GetDirectObjectAt(4));
+  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));
 
   // If the value is a CPDF_Null then ToNumber will return nullptr.
   *pHasX = !!numX;
@@ -134,20 +119,14 @@
 }
 
 unsigned long CPDF_Dest::GetNumParams() const {
-  CPDF_Array* pArray = ToArray(m_pObj.Get());
-  if (!pArray || pArray->GetCount() < 2)
+  if (!m_pArray || m_pArray->size() < 2)
     return 0;
 
   unsigned long maxParamsForFitType = g_sZoomModeMaxParamCount[GetZoomMode()];
-  unsigned long numParamsInArray = pArray->GetCount() - 2;
+  unsigned long numParamsInArray = m_pArray->size() - 2;
   return std::min(maxParamsForFitType, numParamsInArray);
 }
 
 float CPDF_Dest::GetParam(int index) const {
-  CPDF_Array* pArray = ToArray(m_pObj.Get());
-  return pArray ? pArray->GetNumberAt(2 + index) : 0;
-}
-
-ByteString CPDF_Dest::GetRemoteName() const {
-  return m_pObj ? m_pObj->GetString() : ByteString();
+  return m_pArray ? m_pArray->GetNumberAt(2 + index) : 0;
 }
diff --git a/core/fpdfdoc/cpdf_dest.h b/core/fpdfdoc/cpdf_dest.h
index 584669a..c215634 100644
--- a/core/fpdfdoc/cpdf_dest.h
+++ b/core/fpdfdoc/cpdf_dest.h
@@ -9,22 +9,20 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
-class CPDF_Object;
+class CPDF_Array;
 
 class CPDF_Dest {
  public:
   CPDF_Dest();
+  explicit CPDF_Dest(const CPDF_Array* pArray);
   CPDF_Dest(const CPDF_Dest& that);
-  explicit CPDF_Dest(CPDF_Object* pObj);
   ~CPDF_Dest();
 
-  CPDF_Object* GetObject() const { return m_pObj.Get(); }
-  ByteString GetRemoteName() const;
-  int GetPageIndex(CPDF_Document* pDoc) const;
-  uint32_t GetPageObjNum() const;
+  const CPDF_Array* GetArray() const { return m_pArray.Get(); }
+  int GetDestPageIndex(CPDF_Document* pDoc) const;
 
   // Returns the zoom mode, as one of the PDFDEST_VIEW_* values in fpdf_doc.h.
   int GetZoomMode() const;
@@ -40,7 +38,7 @@
               float* pZoom) const;
 
  private:
-  UnownedPtr<CPDF_Object> m_pObj;
+  RetainPtr<const CPDF_Array> const m_pArray;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_DEST_H_
diff --git a/core/fpdfdoc/cpdf_dest_unittest.cpp b/core/fpdfdoc/cpdf_dest_unittest.cpp
index 2b3c86a..07e46b3 100644
--- a/core/fpdfdoc/cpdf_dest_unittest.cpp
+++ b/core/fpdfdoc/cpdf_dest_unittest.cpp
@@ -2,13 +2,13 @@
 // 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 "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 "core/fpdfdoc/cpdf_dest.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
 
 TEST(cpdf_dest, GetXYZ) {
@@ -20,7 +20,7 @@
   float zoom;
 
   // |array| must outlive |dest|.
-  auto array = pdfium::MakeUnique<CPDF_Array>();
+  auto array = pdfium::MakeRetain<CPDF_Array>();
   array->AddNew<CPDF_Number>(0);  // Page Index.
   array->AddNew<CPDF_Name>("XYZ");
   array->AddNew<CPDF_Number>(4);  // X
@@ -30,13 +30,13 @@
   }
   {
     // Not enough entries.
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.get());
+    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
     EXPECT_FALSE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
   }
   array->AddNew<CPDF_Number>(5);  // Y
   array->AddNew<CPDF_Number>(6);  // Zoom.
   {
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.get());
+    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
     EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
     EXPECT_TRUE(hasX);
     EXPECT_TRUE(hasY);
@@ -48,7 +48,7 @@
   // Set zoom to 0.
   array->SetNewAt<CPDF_Number>(4, 0);
   {
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.get());
+    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
     EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
     EXPECT_FALSE(hasZoom);
   }
@@ -57,7 +57,7 @@
   array->SetNewAt<CPDF_Null>(3);
   array->SetNewAt<CPDF_Null>(4);
   {
-    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.get());
+    auto dest = pdfium::MakeUnique<CPDF_Dest>(array.Get());
     EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom));
     EXPECT_FALSE(hasX);
     EXPECT_FALSE(hasY);
diff --git a/core/fpdfdoc/cpdf_docjsactions.cpp b/core/fpdfdoc/cpdf_docjsactions.cpp
deleted file mode 100644
index 91db2b0..0000000
--- a/core/fpdfdoc/cpdf_docjsactions.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/cpdf_docjsactions.h"
-
-#include "core/fpdfdoc/cpdf_nametree.h"
-
-CPDF_DocJSActions::CPDF_DocJSActions(CPDF_Document* pDoc) : m_pDocument(pDoc) {}
-
-CPDF_DocJSActions::~CPDF_DocJSActions() {}
-
-int CPDF_DocJSActions::CountJSActions() const {
-  ASSERT(m_pDocument);
-  CPDF_NameTree name_tree(m_pDocument.Get(), "JavaScript");
-  return name_tree.GetCount();
-}
-
-CPDF_Action CPDF_DocJSActions::GetJSActionAndName(int index,
-                                                  WideString* csName) const {
-  ASSERT(m_pDocument);
-  CPDF_NameTree name_tree(m_pDocument.Get(), "JavaScript");
-  return CPDF_Action(ToDictionary(name_tree.LookupValueAndName(index, csName)));
-}
-
-CPDF_Action CPDF_DocJSActions::GetJSAction(const WideString& csName) const {
-  ASSERT(m_pDocument);
-  CPDF_NameTree name_tree(m_pDocument.Get(), "JavaScript");
-  return CPDF_Action(ToDictionary(name_tree.LookupValue(csName)));
-}
-
-int CPDF_DocJSActions::FindJSAction(const WideString& csName) const {
-  ASSERT(m_pDocument);
-  CPDF_NameTree name_tree(m_pDocument.Get(), "JavaScript");
-  return name_tree.GetIndex(csName);
-}
diff --git a/core/fpdfdoc/cpdf_docjsactions.h b/core/fpdfdoc/cpdf_docjsactions.h
deleted file mode 100644
index 39aafb8..0000000
--- a/core/fpdfdoc/cpdf_docjsactions.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_FPDFDOC_CPDF_DOCJSACTIONS_H_
-#define CORE_FPDFDOC_CPDF_DOCJSACTIONS_H_
-
-#include "core/fpdfdoc/cpdf_action.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDF_Document;
-
-class CPDF_DocJSActions {
- public:
-  explicit CPDF_DocJSActions(CPDF_Document* pDoc);
-  ~CPDF_DocJSActions();
-
-  int CountJSActions() const;
-  CPDF_Action GetJSActionAndName(int index, WideString* csName) const;
-  CPDF_Action GetJSAction(const WideString& csName) const;
-  int FindJSAction(const WideString& csName) const;
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
-
- private:
-  UnownedPtr<CPDF_Document> const m_pDocument;
-};
-
-#endif  // CORE_FPDFDOC_CPDF_DOCJSACTIONS_H_
diff --git a/core/fpdfdoc/cpdf_filespec.cpp b/core/fpdfdoc/cpdf_filespec.cpp
index 2f999bc..506e176 100644
--- a/core/fpdfdoc/cpdf_filespec.cpp
+++ b/core/fpdfdoc/cpdf_filespec.cpp
@@ -8,6 +8,8 @@
 
 #include <vector>
 
+#include "build/build_config.h"
+#include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
@@ -18,13 +20,12 @@
 
 namespace {
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ || \
-    _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_MACOSX) || defined(OS_WIN)
 WideString ChangeSlashToPlatform(const wchar_t* str) {
   WideString result;
   while (*str) {
     if (*str == '/') {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
       result += L':';
 #else
       result += L'\\';
@@ -49,11 +50,16 @@
   }
   return result;
 }
-#endif  // _FX_PLATFORM_APPLE_ || _FX_PLATFORM_WINDOWS_
+#endif  // defined(OS_MACOSX) || defined(OS_WIN)
 
 }  // namespace
 
-CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj) : m_pObj(pObj) {
+CPDF_FileSpec::CPDF_FileSpec(const CPDF_Object* pObj) : m_pObj(pObj) {
+  ASSERT(m_pObj);
+}
+
+CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj)
+    : m_pObj(pObj), m_pWritableObj(pObj) {
   ASSERT(m_pObj);
 }
 
@@ -63,11 +69,11 @@
   if (filepath.GetLength() <= 1)
     return WideString();
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  if (filepath.Left(sizeof("/Mac") - 1) == WideStringView(L"/Mac"))
+#if defined(OS_MACOSX)
+  if (filepath.First(sizeof("/Mac") - 1) == WideStringView(L"/Mac"))
     return ChangeSlashToPlatform(filepath.c_str() + 1);
   return ChangeSlashToPlatform(filepath.c_str());
-#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#elif defined(OS_WIN)
 
   if (filepath[0] != L'/')
     return ChangeSlashToPlatform(filepath.c_str());
@@ -91,49 +97,53 @@
 
 WideString CPDF_FileSpec::GetFileName() const {
   WideString csFileName;
-  if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
-    csFileName = pDict->GetUnicodeTextFor("UF");
+  if (const CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
+    const CPDF_String* pUF = ToString(pDict->GetDirectObjectFor("UF"));
+    if (pUF)
+      csFileName = pUF->GetUnicodeText();
     if (csFileName.IsEmpty()) {
-      csFileName =
-          WideString::FromLocal(pDict->GetStringFor("F").AsStringView());
+      const CPDF_String* pK =
+          ToString(pDict->GetDirectObjectFor(pdfium::stream::kF));
+      if (pK)
+        csFileName = WideString::FromDefANSI(pK->GetString().AsStringView());
     }
     if (pDict->GetStringFor("FS") == "URL")
       return csFileName;
 
     if (csFileName.IsEmpty()) {
-      constexpr const char* keys[] = {"DOS", "Mac", "Unix"};
-      for (const auto* key : keys) {
-        if (pDict->KeyExist(key)) {
+      for (const auto* key : {"DOS", "Mac", "Unix"}) {
+        const CPDF_String* pValue = ToString(pDict->GetDirectObjectFor(key));
+        if (pValue) {
           csFileName =
-              WideString::FromLocal(pDict->GetStringFor(key).AsStringView());
+              WideString::FromDefANSI(pValue->GetString().AsStringView());
           break;
         }
       }
     }
-  } else if (m_pObj->IsString()) {
-    csFileName = WideString::FromLocal(m_pObj->GetString().AsStringView());
+  } else if (const CPDF_String* pString = m_pObj->AsString()) {
+    csFileName = WideString::FromDefANSI(pString->GetString().AsStringView());
   }
   return DecodeFileName(csFileName);
 }
 
-CPDF_Stream* CPDF_FileSpec::GetFileStream() const {
-  CPDF_Dictionary* pDict = m_pObj->AsDictionary();
+const CPDF_Stream* CPDF_FileSpec::GetFileStream() const {
+  const CPDF_Dictionary* pDict = m_pObj->AsDictionary();
   if (!pDict)
     return nullptr;
 
   // Get the embedded files dictionary.
-  CPDF_Dictionary* pFiles = pDict->GetDictFor("EF");
+  const CPDF_Dictionary* pFiles = pDict->GetDictFor("EF");
   if (!pFiles)
     return nullptr;
 
-  // Get the file stream of the highest precedence with its file specification
-  // string available. Follows the same precedence order as GetFileName().
-  constexpr const char* keys[] = {"UF", "F", "DOS", "Mac", "Unix"};
-  size_t end = pDict->GetStringFor("FS") == "URL" ? 2 : FX_ArraySize(keys);
+  // 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);
   for (size_t i = 0; i < end; ++i) {
-    const ByteString& key = keys[i];
+    ByteString key = kKeys[i];
     if (!pDict->GetUnicodeTextFor(key).IsEmpty()) {
-      CPDF_Stream* pStream = pFiles->GetStreamFor(key);
+      const CPDF_Stream* pStream = pFiles->GetStreamFor(key);
       if (pStream)
         return pStream;
     }
@@ -141,23 +151,30 @@
   return nullptr;
 }
 
-CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() const {
-  CPDF_Stream* pStream = GetFileStream();
+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();
   if (!pStream)
     return nullptr;
 
-  CPDF_Dictionary* pDict = pStream->GetDict();
-  if (!pDict)
-    return nullptr;
+  const CPDF_Dictionary* pDict = pStream->GetDict();
+  return pDict ? pDict->GetDictFor("Params") : nullptr;
+}
 
-  return pDict->GetDictFor("Params");
+CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() {
+  return const_cast<CPDF_Dictionary*>(
+      static_cast<const CPDF_FileSpec*>(this)->GetParamsDict());
 }
 
 WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) {
   if (filepath.GetLength() <= 1)
     return WideString();
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   if (filepath[1] == L':') {
     WideString result(L'/');
     result += filepath[0];
@@ -173,8 +190,8 @@
   if (filepath[0] == L'\\')
     return L'/' + ChangeSlashToPDF(filepath.c_str());
   return ChangeSlashToPDF(filepath.c_str());
-#elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  if (filepath.Left(sizeof("Mac") - 1) == L"Mac")
+#elif defined(OS_MACOSX)
+  if (filepath.First(sizeof("Mac") - 1).EqualsASCII("Mac"))
     return L'/' + ChangeSlashToPDF(filepath.c_str());
   return ChangeSlashToPDF(filepath.c_str());
 #else
@@ -183,11 +200,16 @@
 }
 
 void CPDF_FileSpec::SetFileName(const WideString& wsFileName) {
+  if (!m_pWritableObj) {
+    NOTREACHED();
+    return;
+  }
+
   WideString wsStr = EncodeFileName(wsFileName);
   if (m_pObj->IsString()) {
-    m_pObj->SetString(ByteString::FromUnicode(wsStr));
-  } else if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
-    pDict->SetNewFor<CPDF_String>("F", ByteString::FromUnicode(wsStr), false);
-    pDict->SetNewFor<CPDF_String>("UF", PDF_EncodeText(wsStr), false);
+    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 deeccb9..820b948 100644
--- a/core/fpdfdoc/cpdf_filespec.h
+++ b/core/fpdfdoc/cpdf_filespec.h
@@ -8,8 +8,8 @@
 #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/unowned_ptr.h"
 #include "core/fxcrt/weak_ptr.h"
 
 class CPDF_Dictionary;
@@ -18,6 +18,7 @@
 
 class CPDF_FileSpec {
  public:
+  explicit CPDF_FileSpec(const CPDF_Object* pObj);
   explicit CPDF_FileSpec(CPDF_Object* pObj);
   ~CPDF_FileSpec();
 
@@ -27,16 +28,20 @@
   // Convert a pdf file name into platform dependent format.
   static WideString DecodeFileName(const WideString& filepath);
 
-  CPDF_Object* GetObj() const { return m_pObj.Get(); }
+  const CPDF_Object* GetObj() const { return m_pObj.Get(); }
+  CPDF_Object* GetObj() { return m_pWritableObj.Get(); }
   WideString GetFileName() const;
-  CPDF_Stream* GetFileStream() const;
-  CPDF_Dictionary* GetParamsDict() 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);
 
  private:
-  UnownedPtr<CPDF_Object> const m_pObj;
+  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 73fc8a4..a075c5e 100644
--- a/core/fpdfdoc/cpdf_filespec_unittest.cpp
+++ b/core/fpdfdoc/cpdf_filespec_unittest.cpp
@@ -6,12 +6,14 @@
 #include <utility>
 #include <vector>
 
+#include "build/build_config.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_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfdoc/cpdf_filespec.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
@@ -22,7 +24,7 @@
     {L"", L""},
     // only file name.
     {L"test.pdf", L"test.pdf"},
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
     // With drive identifier.
     {L"r:\\pdfdocs\\spec.pdf", L"/r/pdfdocs/spec.pdf"},
     // Relative path.
@@ -33,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 _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#elif defined(OS_MACOSX)
     // Absolute path with colon separator.
     {L"Mac HD:PDFDocs:spec.pdf", L"/Mac HD/PDFDocs/spec.pdf"},
     // Relative path with colon separator.
@@ -60,10 +62,10 @@
   {
     // String object.
     static const pdfium::NullTermWstrFuncTestData test_data = {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
       L"/C/docs/test.pdf",
       L"C:\\docs\\test.pdf"
-#elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#elif defined(OS_MACOSX)
       L"/Mac HD/docs/test.pdf",
       L"Mac HD:docs:test.pdf"
 #else
@@ -71,20 +73,20 @@
       L"/docs/test.pdf"
 #endif
     };
-    auto str_obj = pdfium::MakeUnique<CPDF_String>(nullptr, test_data.input);
-    CPDF_FileSpec file_spec(str_obj.get());
+    auto str_obj = pdfium::MakeRetain<CPDF_String>(nullptr, test_data.input);
+    CPDF_FileSpec file_spec(str_obj.Get());
     EXPECT_STREQ(test_data.expected, file_spec.GetFileName().c_str());
   }
   {
     // Dictionary object.
     static const pdfium::NullTermWstrFuncTestData test_data[] = {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_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 _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#elif defined(OS_MACOSX)
       {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,8 +104,8 @@
     const char* const keywords[] = {"Unix", "Mac", "DOS", "F", "UF"};
     static_assert(FX_ArraySize(test_data) == FX_ArraySize(keywords),
                   "size mismatch");
-    auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
-    CPDF_FileSpec file_spec(dict_obj.get());
+    auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
+    CPDF_FileSpec file_spec(dict_obj.Get());
     EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
     for (size_t i = 0; i < FX_ArraySize(keywords); ++i) {
       dict_obj->SetNewFor<CPDF_String>(keywords[i], test_data[i].input);
@@ -117,18 +119,29 @@
   }
   {
     // Invalid object.
-    auto name_obj = pdfium::MakeUnique<CPDF_Name>(nullptr, "test.pdf");
-    CPDF_FileSpec file_spec(name_obj.get());
+    auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "test.pdf");
+    CPDF_FileSpec file_spec(name_obj.Get());
+    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());
+    for (const char* key : {"Unix", "Mac", "DOS", "F", "UF"}) {
+      dict_obj->SetNewFor<CPDF_Name>(key, "http://evil.org");
+      EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
+    }
+    dict_obj->SetNewFor<CPDF_String>("FS", "URL", false);
     EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
   }
 }
 
 TEST(cpdf_filespec, SetFileName) {
   static const pdfium::NullTermWstrFuncTestData test_data = {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
     L"C:\\docs\\test.pdf",
     L"/C/docs/test.pdf"
-#elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#elif defined(OS_MACOSX)
     L"Mac HD:docs:test.pdf",
     L"/Mac HD/docs/test.pdf"
 #else
@@ -137,8 +150,8 @@
 #endif
   };
   // String object.
-  auto str_obj = pdfium::MakeUnique<CPDF_String>(nullptr, L"babababa");
-  CPDF_FileSpec file_spec1(str_obj.get());
+  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());
@@ -146,8 +159,8 @@
   EXPECT_STREQ(test_data.input, file_spec1.GetFileName().c_str());
 
   // Dictionary object.
-  auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
-  CPDF_FileSpec file_spec2(dict_obj.get());
+  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());
@@ -159,28 +172,28 @@
 TEST(cpdf_filespec, GetFileStream) {
   {
     // Invalid object.
-    auto name_obj = pdfium::MakeUnique<CPDF_Name>(nullptr, "test.pdf");
-    CPDF_FileSpec file_spec(name_obj.get());
+    auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "test.pdf");
+    CPDF_FileSpec file_spec(name_obj.Get());
     EXPECT_FALSE(file_spec.GetFileStream());
   }
   {
     // Dictionary object missing its embedded files dictionary.
-    auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
-    CPDF_FileSpec file_spec(dict_obj.get());
+    auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
+    CPDF_FileSpec file_spec(dict_obj.Get());
     EXPECT_FALSE(file_spec.GetFileStream());
   }
   {
     // Dictionary object with an empty embedded files dictionary.
-    auto dict_obj = pdfium::MakeUnique<CPDF_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.Get());
     EXPECT_FALSE(file_spec.GetFileStream());
   }
   {
     // Dictionary object with a non-empty embedded files dictionary.
-    auto dict_obj = pdfium::MakeUnique<CPDF_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.Get());
 
     const wchar_t file_name[] = L"test.pdf";
     const char* const keys[] = {"Unix", "Mac", "DOS", "F", "UF"};
@@ -195,7 +208,7 @@
       dict_obj->SetNewFor<CPDF_String>(keys[i], file_name);
 
       // Set the file stream.
-      auto pDict = pdfium::MakeUnique<CPDF_Dictionary>();
+      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);
@@ -205,7 +218,7 @@
       // Check that the file content stream is as expected.
       EXPECT_STREQ(
           streams[i],
-          file_spec.GetFileStream()->GetUnicodeText().UTF8Encode().c_str());
+          file_spec.GetFileStream()->GetUnicodeText().ToUTF8().c_str());
 
       if (i == 2) {
         dict_obj->SetNewFor<CPDF_String>("FS", "URL", false);
@@ -218,22 +231,22 @@
 TEST(cpdf_filespec, GetParamsDict) {
   {
     // Invalid object.
-    auto name_obj = pdfium::MakeUnique<CPDF_Name>(nullptr, "test.pdf");
-    CPDF_FileSpec file_spec(name_obj.get());
+    auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "test.pdf");
+    CPDF_FileSpec file_spec(name_obj.Get());
     EXPECT_FALSE(file_spec.GetParamsDict());
   }
   {
     // Dictionary object.
-    auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
+    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.Get());
     EXPECT_FALSE(file_spec.GetParamsDict());
 
     // Add a file stream to the embedded files dictionary.
     CPDF_Dictionary* file_dict =
         file_spec.GetObj()->AsDictionary()->GetDictFor("EF");
-    auto pDict = pdfium::MakeUnique<CPDF_Dictionary>();
+    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,
diff --git a/core/fpdfdoc/cpdf_formcontrol.cpp b/core/fpdfdoc/cpdf_formcontrol.cpp
index 384c35e..b98afba 100644
--- a/core/fpdfdoc/cpdf_formcontrol.cpp
+++ b/core/fpdfdoc/cpdf_formcontrol.cpp
@@ -8,15 +8,16 @@
 
 #include <algorithm>
 
-#include "core/fpdfapi/page/cpdf_form.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/render/cpdf_rendercontext.h"
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "core/fxge/cfx_renderdevice.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 
 namespace {
 
@@ -32,11 +33,15 @@
       m_pWidgetDict(pWidgetDict),
       m_pForm(m_pField->GetForm()) {}
 
-CPDF_FormControl::~CPDF_FormControl() {}
+CPDF_FormControl::~CPDF_FormControl() = default;
+
+CFX_FloatRect CPDF_FormControl::GetRect() const {
+  return m_pWidgetDict->GetRectFor("Rect");
+}
 
 ByteString CPDF_FormControl::GetOnStateName() const {
-  ASSERT(GetType() == CPDF_FormField::CheckBox ||
-         GetType() == CPDF_FormField::RadioButton);
+  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+         GetType() == CPDF_FormField::kRadioButton);
   ByteString csOn;
   CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
   if (!pAP)
@@ -46,98 +51,50 @@
   if (!pN)
     return csOn;
 
-  for (const auto& it : *pN) {
+  CPDF_DictionaryLocker locker(pN);
+  for (const auto& it : locker) {
     if (it.first != "Off")
       return it.first;
   }
   return ByteString();
 }
 
-void CPDF_FormControl::SetOnStateName(const ByteString& csOn) {
-  ASSERT(GetType() == CPDF_FormField::CheckBox ||
-         GetType() == CPDF_FormField::RadioButton);
-  ByteString csValue = csOn;
-  if (csValue.IsEmpty())
-    csValue = "Yes";
-  else if (csValue == "Off")
-    csValue = "Yes";
-
-  ByteString csAS = m_pWidgetDict->GetStringFor("AS", "Off");
-  if (csAS != "Off")
-    m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csValue);
-
-  CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
-  if (!pAP)
-    return;
-
-  for (const auto& it : *pAP) {
-    CPDF_Object* pObj1 = it.second.get();
-    if (!pObj1)
-      continue;
-
-    CPDF_Object* pObjDirect1 = pObj1->GetDirect();
-    CPDF_Dictionary* pSubDict = pObjDirect1->AsDictionary();
-    if (!pSubDict)
-      continue;
-
-    auto subdict_it = pSubDict->begin();
-    while (subdict_it != pSubDict->end()) {
-      const ByteString& csKey2 = subdict_it->first;
-      CPDF_Object* pObj2 = subdict_it->second.get();
-      ++subdict_it;
-      if (!pObj2)
-        continue;
-      if (csKey2 != "Off") {
-        pSubDict->ReplaceKey(csKey2, csValue);
-        break;
-      }
-    }
-  }
-}
-
-ByteString CPDF_FormControl::GetCheckedAPState() {
-  ASSERT(GetType() == CPDF_FormField::CheckBox ||
-         GetType() == CPDF_FormField::RadioButton);
+ByteString CPDF_FormControl::GetCheckedAPState() const {
+  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+         GetType() == CPDF_FormField::kRadioButton);
   ByteString csOn = GetOnStateName();
-  if (GetType() == CPDF_FormField::RadioButton ||
-      GetType() == CPDF_FormField::CheckBox) {
-    if (ToArray(FPDF_GetFieldAttr(m_pField->GetDict(), "Opt")))
-      csOn = ByteString::Format("%d", m_pField->GetControlIndex(this));
-  }
+  if (ToArray(CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Opt")))
+    csOn = ByteString::Format("%d", m_pField->GetControlIndex(this));
   if (csOn.IsEmpty())
     csOn = "Yes";
   return csOn;
 }
 
 WideString CPDF_FormControl::GetExportValue() const {
-  ASSERT(GetType() == CPDF_FormField::CheckBox ||
-         GetType() == CPDF_FormField::RadioButton);
+  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+         GetType() == CPDF_FormField::kRadioButton);
   ByteString csOn = GetOnStateName();
-  if (GetType() == CPDF_FormField::RadioButton ||
-      GetType() == CPDF_FormField::CheckBox) {
-    if (CPDF_Array* pArray =
-            ToArray(FPDF_GetFieldAttr(m_pField->GetDict(), "Opt"))) {
-      int iIndex = m_pField->GetControlIndex(this);
-      csOn = pArray->GetStringAt(iIndex);
-    }
-  }
+  CPDF_Array* pArray =
+      ToArray(CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Opt"));
+  if (pArray)
+    csOn = pArray->GetStringAt(m_pField->GetControlIndex(this));
   if (csOn.IsEmpty())
     csOn = "Yes";
-  return PDF_DecodeText(csOn);
+  return PDF_DecodeText(csOn.raw_span());
 }
 
 bool CPDF_FormControl::IsChecked() const {
-  ASSERT(GetType() == CPDF_FormField::CheckBox ||
-         GetType() == CPDF_FormField::RadioButton);
+  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+         GetType() == CPDF_FormField::kRadioButton);
   ByteString csOn = GetOnStateName();
   ByteString csAS = m_pWidgetDict->GetStringFor("AS");
   return csAS == csOn;
 }
 
 bool CPDF_FormControl::IsDefaultChecked() const {
-  ASSERT(GetType() == CPDF_FormField::CheckBox ||
-         GetType() == CPDF_FormField::RadioButton);
-  CPDF_Object* pDV = FPDF_GetFieldAttr(m_pField->GetDict(), "DV");
+  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+         GetType() == CPDF_FormField::kRadioButton);
+  CPDF_Object* pDV = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "DV");
   if (!pDV)
     return false;
 
@@ -147,46 +104,19 @@
 }
 
 void CPDF_FormControl::CheckControl(bool bChecked) {
-  ASSERT(GetType() == CPDF_FormField::CheckBox ||
-         GetType() == CPDF_FormField::RadioButton);
-  ByteString csOn = GetOnStateName();
+  ASSERT(GetType() == CPDF_FormField::kCheckBox ||
+         GetType() == CPDF_FormField::kRadioButton);
   ByteString csOldAS = m_pWidgetDict->GetStringFor("AS", "Off");
   ByteString csAS = "Off";
   if (bChecked)
-    csAS = csOn;
+    csAS = GetOnStateName();
   if (csOldAS == csAS)
     return;
   m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csAS);
 }
 
-void CPDF_FormControl::DrawControl(CFX_RenderDevice* pDevice,
-                                   CFX_Matrix* pMatrix,
-                                   CPDF_Page* pPage,
-                                   CPDF_Annot::AppearanceMode mode,
-                                   const CPDF_RenderOptions* pOptions) {
-  if (m_pWidgetDict->GetIntegerFor("F") & ANNOTFLAG_HIDDEN)
-    return;
-
-  CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pWidgetDict.Get(), mode);
-  if (!pStream)
-    return;
-
-  CFX_Matrix form_matrix = pStream->GetDict()->GetMatrixFor("Matrix");
-  CFX_FloatRect form_bbox =
-      form_matrix.TransformRect(pStream->GetDict()->GetRectFor("BBox"));
-  CFX_FloatRect arect = m_pWidgetDict->GetRectFor("Rect");
-  CFX_Matrix matrix;
-  matrix.MatchRect(arect, form_bbox);
-  matrix.Concat(*pMatrix);
-  CPDF_Form form(m_pField->GetForm()->GetDocument(),
-                 m_pField->GetForm()->GetFormDict()->GetDictFor("DR"), pStream);
-  form.ParseContent();
-  CPDF_RenderContext context(pPage);
-  context.AppendLayer(&form, &matrix);
-  context.Render(pDevice, pOptions, nullptr);
-}
-
-CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode() {
+CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode()
+    const {
   if (!m_pWidgetDict)
     return Invert;
 
@@ -207,7 +137,7 @@
   return GetMK().HasMKEntry(csEntry);
 }
 
-int CPDF_FormControl::GetRotation() {
+int CPDF_FormControl::GetRotation() const {
   return GetMK().GetRotation();
 }
 
@@ -225,7 +155,7 @@
   GetMK().GetOriginalColor(iColorType, fc, csEntry);
 }
 
-WideString CPDF_FormControl::GetCaption(const ByteString& csEntry) {
+WideString CPDF_FormControl::GetCaption(const ByteString& csEntry) const {
   return GetMK().GetCaption(csEntry);
 }
 
@@ -233,26 +163,26 @@
   return GetMK().GetIcon(csEntry);
 }
 
-CPDF_IconFit CPDF_FormControl::GetIconFit() {
+CPDF_IconFit CPDF_FormControl::GetIconFit() const {
   return GetMK().GetIconFit();
 }
 
-int CPDF_FormControl::GetTextPosition() {
+int CPDF_FormControl::GetTextPosition() const {
   return GetMK().GetTextPosition();
 }
 
-CPDF_Action CPDF_FormControl::GetAction() {
+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 = FPDF_GetFieldAttr(m_pField->GetDict(), "A");
+  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "A");
   return CPDF_Action(pObj ? pObj->GetDict() : nullptr);
 }
 
-CPDF_AAction CPDF_FormControl::GetAdditionalAction() {
+CPDF_AAction CPDF_FormControl::GetAdditionalAction() const {
   if (!m_pWidgetDict)
     return CPDF_AAction(nullptr);
 
@@ -261,67 +191,77 @@
   return m_pField->GetAdditionalAction();
 }
 
-CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() {
+CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() const {
   if (!m_pWidgetDict)
     return CPDF_DefaultAppearance();
 
   if (m_pWidgetDict->KeyExist("DA"))
     return CPDF_DefaultAppearance(m_pWidgetDict->GetStringFor("DA"));
 
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->GetDict(), "DA");
-  if (pObj)
-    return CPDF_DefaultAppearance(pObj->GetString());
-  return m_pField->GetForm()->GetDefaultAppearance();
+  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "DA");
+  if (!pObj)
+    return m_pForm->GetDefaultAppearance();
+  return CPDF_DefaultAppearance(pObj->GetString());
 }
 
-CPDF_Font* CPDF_FormControl::GetDefaultControlFont() {
+Optional<WideString> CPDF_FormControl::GetDefaultControlFontName() const {
+  RetainPtr<CPDF_Font> pFont = GetDefaultControlFont();
+  if (!pFont)
+    return {};
+
+  return WideString::FromDefANSI(pFont->GetBaseFontName().AsStringView());
+}
+
+RetainPtr<CPDF_Font> CPDF_FormControl::GetDefaultControlFont() const {
   float fFontSize;
   CPDF_DefaultAppearance cDA = GetDefaultAppearance();
-  ByteString csFontNameTag = cDA.GetFont(&fFontSize);
-  if (csFontNameTag.IsEmpty())
+  Optional<ByteString> csFontNameTag = cDA.GetFont(&fFontSize);
+  if (!csFontNameTag || csFontNameTag->IsEmpty())
     return nullptr;
 
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pWidgetDict.Get(), "DR");
+  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pWidgetDict.Get(), "DR");
   if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
     CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
-    if (pFonts) {
-      CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
+    if (ValidateFontResourceDict(pFonts)) {
+      CPDF_Dictionary* pElement = pFonts->GetDictFor(*csFontNameTag);
       if (pElement) {
-        CPDF_Font* pFont =
-            m_pField->GetForm()->GetDocument()->LoadFont(pElement);
+        auto* pData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument());
+        RetainPtr<CPDF_Font> pFont = pData->GetFont(pElement);
         if (pFont)
           return pFont;
       }
     }
   }
-  if (CPDF_Font* pFormFont = m_pField->GetForm()->GetFormFont(csFontNameTag))
+  RetainPtr<CPDF_Font> pFormFont = m_pForm->GetFormFont(*csFontNameTag);
+  if (pFormFont)
     return pFormFont;
 
   CPDF_Dictionary* pPageDict = m_pWidgetDict->GetDictFor("P");
-  pObj = FPDF_GetFieldAttr(pPageDict, "Resources");
-  if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
-    CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
-    if (pFonts) {
-      CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
-      if (pElement) {
-        CPDF_Font* pFont =
-            m_pField->GetForm()->GetDocument()->LoadFont(pElement);
-        if (pFont)
-          return pFont;
-      }
-    }
-  }
-  return nullptr;
+  CPDF_Dictionary* pDict =
+      ToDictionary(CPDF_FormField::GetFieldAttr(pPageDict, "Resources"));
+  if (!pDict)
+    return nullptr;
+
+  CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
+  if (!ValidateFontResourceDict(pFonts))
+    return nullptr;
+
+  CPDF_Dictionary* pElement = pFonts->GetDictFor(*csFontNameTag);
+  if (!pElement)
+    return nullptr;
+
+  auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument());
+  return pDocPageData->GetFont(pElement);
 }
 
-int CPDF_FormControl::GetControlAlignment() {
+int CPDF_FormControl::GetControlAlignment() const {
   if (!m_pWidgetDict)
     return 0;
   if (m_pWidgetDict->KeyExist("Q"))
     return m_pWidgetDict->GetIntegerFor("Q", 0);
 
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->GetDict(), "Q");
+  CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Q");
   if (pObj)
     return pObj->GetInteger();
-  return m_pField->GetForm()->GetFormAlignment();
+  return m_pForm->GetFormAlignment();
 }
diff --git a/core/fpdfdoc/cpdf_formcontrol.h b/core/fpdfdoc/cpdf_formcontrol.h
index 6485af0..d825634 100644
--- a/core/fpdfdoc/cpdf_formcontrol.h
+++ b/core/fpdfdoc/cpdf_formcontrol.h
@@ -15,24 +15,17 @@
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fpdfdoc/cpdf_iconfit.h"
-#include "core/fpdfdoc/ipdf_formnotify.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/fx_dib.h"
-
-#define TEXTPOS_CAPTION 0
-#define TEXTPOS_ICON 1
-#define TEXTPOS_BELOW 2
-#define TEXTPOS_ABOVE 3
-#define TEXTPOS_RIGHT 4
-#define TEXTPOS_LEFT 5
-#define TEXTPOS_OVERLAID 6
+#include "third_party/base/optional.h"
 
 class CFX_RenderDevice;
 class CPDF_Dictionary;
 class CPDF_Font;
 class CPDF_FormField;
-class CPDF_InterForm;
+class CPDF_InteractiveForm;
 class CPDF_OCContext;
 class CPDF_RenderOptions;
 class CPDF_Stream;
@@ -45,26 +38,22 @@
   ~CPDF_FormControl();
 
   CPDF_FormField::Type GetType() const { return m_pField->GetType(); }
-  const CPDF_InterForm* GetInterForm() const { return m_pForm.Get(); }
-  CPDF_FormField* GetField() const { return m_pField; }
+  const CPDF_InteractiveForm* GetInteractiveForm() const {
+    return m_pForm.Get();
+  }
+  CPDF_FormField* GetField() const { return m_pField.Get(); }
   CPDF_Dictionary* GetWidget() const { return m_pWidgetDict.Get(); }
-  CFX_FloatRect GetRect() const { return m_pWidgetDict->GetRectFor("Rect"); }
+  CFX_FloatRect GetRect() const;
 
-  void DrawControl(CFX_RenderDevice* pDevice,
-                   CFX_Matrix* pMatrix,
-                   CPDF_Page* pPage,
-                   CPDF_Annot::AppearanceMode mode,
-                   const CPDF_RenderOptions* pOptions = nullptr);
-
-  ByteString GetCheckedAPState();
+  ByteString GetCheckedAPState() const;
   WideString GetExportValue() const;
 
   bool IsChecked() const;
   bool IsDefaultChecked() const;
 
-  HighlightingMode GetHighlightingMode();
+  HighlightingMode GetHighlightingMode() const;
   bool HasMKEntry(const ByteString& csEntry) const;
-  int GetRotation();
+  int GetRotation() const;
 
   FX_ARGB GetBorderColor(int& iColorType) { return GetColor(iColorType, "BC"); }
 
@@ -88,41 +77,41 @@
     GetOriginalColor(iColorType, fc, "BG");
   }
 
-  WideString GetNormalCaption() { return GetCaption("CA"); }
-  WideString GetRolloverCaption() { return GetCaption("RC"); }
-  WideString GetDownCaption() { return GetCaption("AC"); }
+  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"); }
-  CPDF_IconFit GetIconFit();
+  CPDF_IconFit GetIconFit() const;
 
-  int GetTextPosition();
-  CPDF_Action GetAction();
-  CPDF_AAction GetAdditionalAction();
-  CPDF_DefaultAppearance GetDefaultAppearance();
+  int GetTextPosition() const;
+  CPDF_Action GetAction() const;
+  CPDF_AAction GetAdditionalAction() const;
+  CPDF_DefaultAppearance GetDefaultAppearance() const;
 
-  CPDF_Font* GetDefaultControlFont();
-  int GetControlAlignment();
+  Optional<WideString> GetDefaultControlFontName() const;
+  int GetControlAlignment() const;
 
   ByteString GetOnStateName() const;
   void CheckControl(bool bChecked);
 
  private:
-  void SetOnStateName(const ByteString& csOn);
+  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);
 
-  WideString GetCaption(const ByteString& csEntry);
+  WideString GetCaption(const ByteString& csEntry) const;
   CPDF_Stream* GetIcon(const ByteString& csEntry);
   CPDF_ApSettings GetMK() const;
 
-  CPDF_FormField* const m_pField;
-  UnownedPtr<CPDF_Dictionary> const m_pWidgetDict;
-  UnownedPtr<const CPDF_InterForm> const m_pForm;
+  UnownedPtr<CPDF_FormField> const m_pField;
+  RetainPtr<CPDF_Dictionary> const m_pWidgetDict;
+  UnownedPtr<const CPDF_InteractiveForm> const m_pForm;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_FORMCONTROL_H_
diff --git a/core/fpdfdoc/cpdf_formfield.cpp b/core/fpdfdoc/cpdf_formfield.cpp
index df59369..1eae005 100644
--- a/core/fpdfdoc/cpdf_formfield.cpp
+++ b/core/fpdfdoc/cpdf_formfield.cpp
@@ -10,42 +10,47 @@
 #include <set>
 #include <utility>
 
+#include "constants/form_fields.h"
+#include "constants/form_flags.h"
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
 #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_simple_parser.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_interform.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"
 
 namespace {
 
-const int kFormListMultiSelect = 0x100;
+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 int kFormComboEdit = 0x100;
+  const CPDF_Object* pAttr = pFieldDict->GetDirectObjectFor(name);
+  if (pAttr)
+    return pAttr;
 
-const int kFormRadioNoToggleOff = 0x100;
-const int kFormRadioUnison = 0x200;
-
-const int kFormTextMultiLine = 0x100;
-const int kFormTextPassword = 0x200;
-const int kFormTextNoScroll = 0x400;
-const int kFormTextComb = 0x800;
-
-bool IsUnison(CPDF_FormField* pField) {
-  if (pField->GetType() == CPDF_FormField::CheckBox)
-    return true;
-  return (pField->GetFieldFlags() & 0x2000000) != 0;
+  return GetFieldAttrRecursive(
+      pFieldDict->GetDictFor(pdfium::form_fields::kParent), name, nLevel + 1);
 }
 
 }  // namespace
 
-Optional<FormFieldType> IntToFormFieldType(int value) {
+// static
+Optional<FormFieldType> CPDF_FormField::IntToFormFieldType(int value) {
   if (value >= static_cast<int>(FormFieldType::kUnknown) &&
       value < static_cast<int>(kFormFieldTypeCount)) {
     return {static_cast<FormFieldType>(value)};
@@ -53,184 +58,162 @@
   return {};
 }
 
-CPDF_Object* FPDF_GetFieldAttr(const CPDF_Dictionary* pFieldDict,
-                               const char* name,
-                               int nLevel) {
-  static constexpr int kGetFieldMaxRecursion = 32;
-  if (!pFieldDict || nLevel > kGetFieldMaxRecursion)
-    return nullptr;
-
-  CPDF_Object* pAttr = pFieldDict->GetDirectObjectFor(name);
-  if (pAttr)
-    return pAttr;
-
-  CPDF_Dictionary* pParent = pFieldDict->GetDictFor("Parent");
-  return pParent ? FPDF_GetFieldAttr(pParent, name, nLevel + 1) : nullptr;
+// static
+const CPDF_Object* CPDF_FormField::GetFieldAttr(
+    const CPDF_Dictionary* pFieldDict,
+    const ByteString& name) {
+  return GetFieldAttrRecursive(pFieldDict, name, 0);
 }
 
-WideString FPDF_GetFullName(CPDF_Dictionary* pFieldDict) {
+// 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));
+}
+
+// static
+WideString CPDF_FormField::GetFullNameForDict(CPDF_Dictionary* pFieldDict) {
   WideString full_name;
   std::set<CPDF_Dictionary*> visited;
   CPDF_Dictionary* pLevel = pFieldDict;
   while (pLevel) {
     visited.insert(pLevel);
-    WideString short_name = pLevel->GetUnicodeTextFor("T");
+    WideString short_name = pLevel->GetUnicodeTextFor(pdfium::form_fields::kT);
     if (!short_name.IsEmpty()) {
       if (full_name.IsEmpty())
-        full_name = short_name;
+        full_name = std::move(short_name);
       else
-        full_name = short_name + L"." + full_name;
+        full_name = short_name + L'.' + full_name;
     }
-    pLevel = pLevel->GetDictFor("Parent");
+    pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent);
     if (pdfium::ContainsKey(visited, pLevel))
       break;
   }
   return full_name;
 }
 
-CPDF_FormField::CPDF_FormField(CPDF_InterForm* pForm, CPDF_Dictionary* pDict)
-    : m_Type(Unknown),
-      m_pForm(pForm),
-      m_pDict(pDict),
-      m_FontSize(0),
-      m_pFont(nullptr) {
-  SyncFieldFlags();
+CPDF_FormField::CPDF_FormField(CPDF_InteractiveForm* pForm,
+                               CPDF_Dictionary* pDict)
+    : m_pForm(pForm), m_pDict(pDict) {
+  InitFieldFlags();
 }
 
-CPDF_FormField::~CPDF_FormField() {}
+CPDF_FormField::~CPDF_FormField() = default;
 
-void CPDF_FormField::SyncFieldFlags() {
-  CPDF_Object* ft_attr = FPDF_GetFieldAttr(m_pDict.Get(), "FT");
+void CPDF_FormField::InitFieldFlags() {
+  const CPDF_Object* ft_attr =
+      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFT);
   ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString();
-  CPDF_Object* ff_attr = FPDF_GetFieldAttr(m_pDict.Get(), "Ff");
-  uint32_t flags = ff_attr ? ff_attr->GetInteger() : 0;
-  m_Flags = 0;
-  if (flags & FORMFLAG_READONLY)
-    m_Flags |= FORMFLAG_READONLY;
-  if (flags & FORMFLAG_REQUIRED)
-    m_Flags |= FORMFLAG_REQUIRED;
-  if (flags & FORMFLAG_NOEXPORT)
-    m_Flags |= FORMFLAG_NOEXPORT;
+  uint32_t flags = GetFieldFlags();
+  m_bRequired = flags & pdfium::form_flags::kRequired;
+  m_bNoExport = flags & pdfium::form_flags::kNoExport;
 
-  if (type_name == "Btn") {
-    if (flags & 0x8000) {
-      m_Type = RadioButton;
-      if (flags & 0x4000)
-        m_Flags |= kFormRadioNoToggleOff;
-      if (flags & 0x2000000)
-        m_Flags |= kFormRadioUnison;
-    } else if (flags & 0x10000) {
-      m_Type = PushButton;
+  if (type_name == pdfium::form_fields::kBtn) {
+    if (flags & pdfium::form_flags::kButtonRadio) {
+      m_Type = kRadioButton;
+      m_bIsUnison = flags & pdfium::form_flags::kButtonRadiosInUnison;
+    } else if (flags & pdfium::form_flags::kButtonPushbutton) {
+      m_Type = kPushButton;
     } else {
-      m_Type = CheckBox;
+      m_Type = kCheckBox;
+      m_bIsUnison = true;
     }
-  } else if (type_name == "Tx") {
-    if (flags & 0x100000) {
-      m_Type = File;
-    } else if (flags & 0x2000000) {
-      m_Type = RichText;
+  } else if (type_name == pdfium::form_fields::kTx) {
+    if (flags & pdfium::form_flags::kTextFileSelect)
+      m_Type = kFile;
+    else if (flags & pdfium::form_flags::kTextRichText)
+      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;
     } else {
-      m_Type = Text;
-      if (flags & 0x1000)
-        m_Flags |= kFormTextMultiLine;
-      if (flags & 0x2000)
-        m_Flags |= kFormTextPassword;
-      if (flags & 0x800000)
-        m_Flags |= kFormTextNoScroll;
-      if (flags & 0x100000)
-        m_Flags |= kFormTextComb;
+      m_Type = kListBox;
+      m_bIsMultiSelectListBox = flags & pdfium::form_flags::kChoiceMultiSelect;
     }
     LoadDA();
-  } else if (type_name == "Ch") {
-    if (flags & 0x20000) {
-      m_Type = ComboBox;
-      if (flags & 0x40000)
-        m_Flags |= kFormComboEdit;
-    } else {
-      m_Type = ListBox;
-      if (flags & 0x200000)
-        m_Flags |= kFormListMultiSelect;
-    }
-    LoadDA();
-  } else if (type_name == "Sig") {
-    m_Type = Sign;
+  } else if (type_name == pdfium::form_fields::kSig) {
+    m_Type = kSign;
   }
 }
 
 WideString CPDF_FormField::GetFullName() const {
-  return FPDF_GetFullName(m_pDict.Get());
+  return GetFullNameForDict(m_pDict.Get());
 }
 
-bool CPDF_FormField::ResetField(bool bNotify) {
+bool CPDF_FormField::ResetField(NotificationOption notify) {
   switch (m_Type) {
-    case CPDF_FormField::CheckBox:
-    case CPDF_FormField::RadioButton: {
+    case kCheckBox:
+    case kRadioButton: {
       int iCount = CountControls();
-      if (iCount) {
-        // TODO(weili): Check whether anything special needs to be done for
-        // unison field. Otherwise, merge these branches.
-        if (IsUnison(this)) {
-          for (int i = 0; i < iCount; i++)
-            CheckControl(i, GetControl(i)->IsDefaultChecked(), false);
-        } else {
-          for (int i = 0; i < iCount; i++)
-            CheckControl(i, GetControl(i)->IsDefaultChecked(), false);
-        }
+      // TODO(weili): Check whether anything special needs to be done for
+      // |m_bIsUnison|.
+      for (int i = 0; i < iCount; i++) {
+        CheckControl(i, GetControl(i)->IsDefaultChecked(),
+                     NotificationOption::kDoNotNotify);
       }
-      if (bNotify && m_pForm->GetFormNotify())
+      if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
         m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
       break;
     }
-    case CPDF_FormField::ComboBox:
-    case CPDF_FormField::ListBox: {
+    case kComboBox:
+    case kListBox: {
+      ClearSelection(NotificationOption::kDoNotNotify);
       WideString csValue;
-      ClearSelection();
       int iIndex = GetDefaultSelectedItem();
       if (iIndex >= 0)
         csValue = GetOptionLabel(iIndex);
-
-      if (bNotify && !NotifyListOrComboBoxBeforeChange(csValue))
+      if (notify == NotificationOption::kNotify &&
+          !NotifyListOrComboBoxBeforeChange(csValue)) {
         return false;
-
-      SetItemSelection(iIndex, true);
-      if (bNotify)
+      }
+      SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
+      if (notify == NotificationOption::kNotify)
         NotifyListOrComboBoxAfterChange();
       break;
     }
-    case CPDF_FormField::Text:
-    case CPDF_FormField::RichText:
-    case CPDF_FormField::File:
+    case kText:
+    case kRichText:
+    case kFile:
     default: {
-      CPDF_Object* pDV = FPDF_GetFieldAttr(m_pDict.Get(), "DV");
+      const CPDF_Object* pDV = GetDefaultValueObject();
       WideString csDValue;
       if (pDV)
         csDValue = pDV->GetUnicodeText();
 
-      CPDF_Object* pV = FPDF_GetFieldAttr(m_pDict.Get(), "V");
       WideString csValue;
-      if (pV)
-        csValue = pV->GetUnicodeText();
+      {
+        // Limit the scope of |pV| because it may get invalidated below.
+        const CPDF_Object* pV = GetValueObject();
+        if (pV)
+          csValue = pV->GetUnicodeText();
+      }
 
-      CPDF_Object* pRV = FPDF_GetFieldAttr(m_pDict.Get(), "RV");
-      if (!pRV && (csDValue == csValue))
+      bool bHasRV = !!GetFieldAttr(m_pDict.Get(), "RV");
+      if (!bHasRV && (csDValue == csValue))
         return false;
 
-      if (bNotify && !NotifyBeforeValueChange(csDValue))
+      if (notify == NotificationOption::kNotify &&
+          !NotifyBeforeValueChange(csDValue)) {
         return false;
-
+      }
       if (pDV) {
-        std::unique_ptr<CPDF_Object> pClone = pDV->Clone();
+        RetainPtr<CPDF_Object> pClone = pDV->Clone();
         if (!pClone)
           return false;
 
-        m_pDict->SetFor("V", std::move(pClone));
-        if (pRV)
+        m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone));
+        if (bHasRV) {
           m_pDict->SetFor("RV", pDV->Clone());
+        }
       } else {
-        m_pDict->RemoveFor("V");
+        m_pDict->RemoveFor(pdfium::form_fields::kV);
         m_pDict->RemoveFor("RV");
       }
-      if (bNotify)
+      if (notify == NotificationOption::kNotify)
         NotifyAfterValueChange();
       break;
     }
@@ -238,31 +221,40 @@
   return true;
 }
 
+int CPDF_FormField::CountControls() const {
+  return pdfium::CollectionSize<int>(GetControls());
+}
+
+CPDF_FormControl* CPDF_FormField::GetControl(int index) const {
+  return GetControls()[index].Get();
+}
+
 int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const {
   if (!pControl)
     return -1;
 
-  auto it = std::find(m_ControlList.begin(), m_ControlList.end(), pControl);
-  return it != m_ControlList.end() ? it - m_ControlList.begin() : -1;
+  const auto& controls = GetControls();
+  auto it = std::find(controls.begin(), controls.end(), pControl);
+  return it != controls.end() ? it - controls.begin() : -1;
 }
 
 FormFieldType CPDF_FormField::GetFieldType() const {
   switch (m_Type) {
-    case PushButton:
+    case kPushButton:
       return FormFieldType::kPushButton;
-    case CheckBox:
+    case kCheckBox:
       return FormFieldType::kCheckBox;
-    case RadioButton:
+    case kRadioButton:
       return FormFieldType::kRadioButton;
-    case ComboBox:
+    case kComboBox:
       return FormFieldType::kComboBox;
-    case ListBox:
+    case kListBox:
       return FormFieldType::kListBox;
-    case Text:
-    case RichText:
-    case File:
+    case kText:
+    case kRichText:
+    case kFile:
       return FormFieldType::kTextField;
-    case Sign:
+    case kSign:
       return FormFieldType::kSignature;
     default:
       return FormFieldType::kUnknown;
@@ -270,56 +262,55 @@
 }
 
 CPDF_AAction CPDF_FormField::GetAdditionalAction() const {
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "AA");
+  CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kAA);
   return CPDF_AAction(pObj ? pObj->GetDict() : nullptr);
 }
 
 WideString CPDF_FormField::GetAlternateName() const {
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TU");
-  return pObj ? pObj->GetUnicodeText() : L"";
+  const CPDF_Object* pObj =
+      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTU);
+  return pObj ? pObj->GetUnicodeText() : WideString();
 }
 
 WideString CPDF_FormField::GetMappingName() const {
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TM");
-  return pObj ? pObj->GetUnicodeText() : L"";
+  const CPDF_Object* pObj =
+      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTM);
+  return pObj ? pObj->GetUnicodeText() : WideString();
 }
 
 uint32_t CPDF_FormField::GetFieldFlags() const {
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "Ff");
+  const CPDF_Object* pObj =
+      GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFf);
   return pObj ? pObj->GetInteger() : 0;
 }
 
 ByteString CPDF_FormField::GetDefaultStyle() const {
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "DS");
-  return pObj ? pObj->GetString() : "";
+  const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DS");
+  return pObj ? pObj->GetString() : ByteString();
 }
 
-WideString CPDF_FormField::GetRichTextString() const {
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "RV");
-  return pObj ? pObj->GetUnicodeText() : L"";
+void CPDF_FormField::SetOpt(RetainPtr<CPDF_Object> pOpt) {
+  m_pDict->SetFor("Opt", std::move(pOpt));
 }
 
 WideString CPDF_FormField::GetValue(bool bDefault) const {
-  if (GetType() == CheckBox || GetType() == RadioButton)
+  if (GetType() == kCheckBox || GetType() == kRadioButton)
     return GetCheckValue(bDefault);
 
-  CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), bDefault ? "DV" : "V");
+  const CPDF_Object* pValue =
+      bDefault ? GetDefaultValueObject() : GetValueObject();
   if (!pValue) {
-    if (!bDefault) {
-      if (m_Type == RichText)
-        pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
-      if (!pValue && m_Type != Text)
-        pValue = FPDF_GetFieldAttr(m_pDict.Get(), "DV");
-    }
+    if (!bDefault && m_Type != kText)
+      pValue = GetDefaultValueObject();
     if (!pValue)
       return WideString();
   }
 
   switch (pValue->GetType()) {
-    case CPDF_Object::STRING:
-    case CPDF_Object::STREAM:
+    case CPDF_Object::kString:
+    case CPDF_Object::kStream:
       return pValue->GetUnicodeText();
-    case CPDF_Object::ARRAY:
+    case CPDF_Object::kArray:
       pValue = pValue->AsArray()->GetDirectObjectAt(0);
       if (pValue)
         return pValue->GetUnicodeText();
@@ -340,56 +331,58 @@
 
 bool CPDF_FormField::SetValue(const WideString& value,
                               bool bDefault,
-                              bool bNotify) {
+                              NotificationOption notify) {
   switch (m_Type) {
-    case CheckBox:
-    case RadioButton: {
-      SetCheckValue(value, bDefault, bNotify);
+    case kCheckBox:
+    case kRadioButton: {
+      SetCheckValue(value, bDefault, notify);
       return true;
     }
-    case File:
-    case RichText:
-    case Text:
-    case ComboBox: {
+    case kFile:
+    case kRichText:
+    case kText:
+    case kComboBox: {
       WideString csValue = value;
-      if (bNotify && !NotifyBeforeValueChange(csValue))
+      if (notify == NotificationOption::kNotify &&
+          !NotifyBeforeValueChange(csValue)) {
         return false;
-
-      ByteString key(bDefault ? "DV" : "V");
-      int iIndex = FindOptionValue(csValue);
+      }
+      ByteString key(bDefault ? pdfium::form_fields::kDV
+                              : pdfium::form_fields::kV);
+      m_pDict->SetNewFor<CPDF_String>(key, csValue);
+      int iIndex = FindOption(csValue);
       if (iIndex < 0) {
-        ByteString bsEncodeText = PDF_EncodeText(csValue);
-        m_pDict->SetNewFor<CPDF_String>(key, bsEncodeText, false);
-        if (m_Type == RichText && !bDefault)
-          m_pDict->SetNewFor<CPDF_String>("RV", bsEncodeText, false);
+        if (m_Type == kRichText && !bDefault) {
+          m_pDict->SetFor("RV", m_pDict->GetObjectFor(key)->Clone());
+        }
         m_pDict->RemoveFor("I");
       } else {
-        m_pDict->SetNewFor<CPDF_String>(key, PDF_EncodeText(csValue), false);
         if (!bDefault) {
-          ClearSelection();
-          SetItemSelection(iIndex, true);
+          ClearSelection(NotificationOption::kDoNotNotify);
+          SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
         }
       }
-      if (bNotify)
+      if (notify == NotificationOption::kNotify)
         NotifyAfterValueChange();
       break;
     }
-    case ListBox: {
-      int iIndex = FindOptionValue(value);
+    case kListBox: {
+      int iIndex = FindOption(value);
       if (iIndex < 0)
         return false;
 
       if (bDefault && iIndex == GetDefaultSelectedItem())
         return false;
 
-      if (bNotify && !NotifyBeforeSelectionChange(value))
+      if (notify == NotificationOption::kNotify &&
+          !NotifyBeforeSelectionChange(value)) {
         return false;
-
-      if (!bDefault) {
-        ClearSelection();
-        SetItemSelection(iIndex, true);
       }
-      if (bNotify)
+      if (!bDefault) {
+        ClearSelection(NotificationOption::kDoNotNotify);
+        SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
+      }
+      if (notify == NotificationOption::kNotify)
         NotifyAfterSelectionChange();
       break;
     }
@@ -399,18 +392,20 @@
   return true;
 }
 
-bool CPDF_FormField::SetValue(const WideString& value, bool bNotify) {
-  return SetValue(value, false, bNotify);
+bool CPDF_FormField::SetValue(const WideString& value,
+                              NotificationOption notify) {
+  return SetValue(value, false, notify);
 }
 
 int CPDF_FormField::GetMaxLen() const {
-  if (CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "MaxLen"))
+  if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "MaxLen"))
     return pObj->GetInteger();
 
-  for (auto& pControl : m_ControlList) {
+  for (auto& pControl : GetControls()) {
     if (!pControl)
       continue;
-    CPDF_Dictionary* pWidgetDict = pControl->GetWidget();
+
+    const CPDF_Dictionary* pWidgetDict = pControl->GetWidget();
     if (pWidgetDict->KeyExist("MaxLen"))
       return pWidgetDict->GetIntegerFor("MaxLen");
   }
@@ -418,27 +413,21 @@
 }
 
 int CPDF_FormField::CountSelectedItems() const {
-  CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
-  if (!pValue) {
-    pValue = FPDF_GetFieldAttr(m_pDict.Get(), "I");
-    if (!pValue)
-      return 0;
-  }
+  const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
+  if (!pValue)
+    return 0;
 
   if (pValue->IsString() || pValue->IsNumber())
     return pValue->GetString().IsEmpty() ? 0 : 1;
-  if (CPDF_Array* pArray = pValue->AsArray())
-    return pArray->GetCount();
-  return 0;
+  const CPDF_Array* pArray = pValue->AsArray();
+  return pArray ? pArray->size() : 0;
 }
 
 int CPDF_FormField::GetSelectedIndex(int index) const {
-  CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
-  if (!pValue) {
-    pValue = FPDF_GetFieldAttr(m_pDict.Get(), "I");
-    if (!pValue)
-      return -1;
-  }
+  const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
+  if (!pValue)
+    return -1;
+
   if (pValue->IsNumber())
     return pValue->GetInteger();
 
@@ -448,11 +437,11 @@
       return -1;
     sel_value = pValue->GetUnicodeText();
   } else {
-    CPDF_Array* pArray = pValue->AsArray();
+    const CPDF_Array* pArray = pValue->AsArray();
     if (!pArray || index < 0)
       return -1;
 
-    CPDF_Object* elementValue = pArray->GetDirectObjectAt(index);
+    const CPDF_Object* elementValue = pArray->GetDirectObjectAt(index);
     sel_value = elementValue ? elementValue->GetUnicodeText() : WideString();
   }
   if (index < CountSelectedOptions()) {
@@ -468,37 +457,33 @@
   return -1;
 }
 
-bool CPDF_FormField::ClearSelection(bool bNotify) {
-  if (bNotify && m_pForm->GetFormNotify()) {
+bool CPDF_FormField::ClearSelection(NotificationOption notify) {
+  if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
     WideString csValue;
     int iIndex = GetSelectedIndex(0);
     if (iIndex >= 0)
       csValue = GetOptionLabel(iIndex);
-
     if (!NotifyListOrComboBoxBeforeChange(csValue))
       return false;
   }
-  m_pDict->RemoveFor("V");
+  m_pDict->RemoveFor(pdfium::form_fields::kV);
   m_pDict->RemoveFor("I");
-  if (bNotify)
+  if (notify == NotificationOption::kNotify)
     NotifyListOrComboBoxAfterChange();
   return true;
 }
 
 bool CPDF_FormField::IsItemSelected(int index) const {
-  ASSERT(GetType() == ComboBox || GetType() == ListBox);
+  ASSERT(GetType() == kComboBox || GetType() == kListBox);
   if (index < 0 || index >= CountOptions())
     return false;
   if (IsOptionSelected(index))
     return true;
 
   WideString opt_value = GetOptionValue(index);
-  CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
-  if (!pValue) {
-    pValue = FPDF_GetFieldAttr(m_pDict.Get(), "I");
-    if (!pValue)
-      return false;
-  }
+  const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
+  if (!pValue)
+    return false;
 
   if (pValue->IsString())
     return pValue->GetUnicodeText() == opt_value;
@@ -509,85 +494,99 @@
     return (pValue->GetInteger() == index);
   }
 
-  CPDF_Array* pArray = pValue->AsArray();
+  const CPDF_Array* pArray = pValue->AsArray();
   if (!pArray)
     return false;
 
-  int iPos = -1;
-  for (int j = 0; j < CountSelectedOptions(); j++) {
-    if (GetSelectedOptionIndex(j) == index) {
-      iPos = j;
-      break;
+  for (int i = 0; i < CountSelectedOptions(); ++i) {
+    if (GetSelectedOptionIndex(i) == index) {
+      const CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(i);
+      return pDirectObj && pDirectObj->GetUnicodeText() == opt_value;
     }
   }
-  for (int i = 0; i < static_cast<int>(pArray->GetCount()); i++)
-    if (pArray->GetDirectObjectAt(i)->GetUnicodeText() == opt_value &&
-        i == iPos) {
-      return true;
-    }
   return false;
 }
 
-bool CPDF_FormField::SetItemSelection(int index, bool bSelected, bool bNotify) {
-  ASSERT(GetType() == ComboBox || GetType() == ListBox);
+bool CPDF_FormField::SetItemSelection(int index,
+                                      bool bSelected,
+                                      NotificationOption notify) {
+  ASSERT(GetType() == kComboBox || GetType() == kListBox);
   if (index < 0 || index >= CountOptions())
     return false;
 
   WideString opt_value = GetOptionValue(index);
-  if (bNotify && !NotifyListOrComboBoxBeforeChange(opt_value))
+  if (notify == NotificationOption::kNotify &&
+      !NotifyListOrComboBoxBeforeChange(opt_value)) {
     return false;
-
-  if (bSelected) {
-    if (GetType() == ListBox) {
-      SelectOption(index, true);
-      if (!(m_Flags & kFormListMultiSelect)) {
-        m_pDict->SetNewFor<CPDF_String>("V", PDF_EncodeText(opt_value), false);
-      } else {
-        CPDF_Array* pArray = m_pDict->SetNewFor<CPDF_Array>("V");
-        for (int i = 0; i < CountOptions(); i++) {
-          if (i == index || IsItemSelected(i)) {
-            opt_value = GetOptionValue(i);
-            pArray->AddNew<CPDF_String>(PDF_EncodeText(opt_value), false);
-          }
-        }
-      }
-    } else {
-      m_pDict->SetNewFor<CPDF_String>("V", PDF_EncodeText(opt_value), false);
-      CPDF_Array* pI = m_pDict->SetNewFor<CPDF_Array>("I");
-      pI->AddNew<CPDF_Number>(index);
-    }
-  } else {
-    CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
-    if (pValue) {
-      if (GetType() == ListBox) {
-        SelectOption(index, false);
-        if (pValue->IsString()) {
-          if (pValue->GetUnicodeText() == opt_value)
-            m_pDict->RemoveFor("V");
-        } else if (pValue->IsArray()) {
-          auto pArray = pdfium::MakeUnique<CPDF_Array>();
-          for (int i = 0; i < CountOptions(); i++) {
-            if (i != index && IsItemSelected(i)) {
-              opt_value = GetOptionValue(i);
-              pArray->AddNew<CPDF_String>(PDF_EncodeText(opt_value), false);
-            }
-          }
-          if (pArray->GetCount() > 0)
-            m_pDict->SetFor("V", std::move(pArray));
-        }
-      } else {
-        m_pDict->RemoveFor("V");
-        m_pDict->RemoveFor("I");
-      }
-    }
   }
-  if (bNotify)
+
+  if (bSelected)
+    SetItemSelectionSelected(index, opt_value);
+  else
+    SetItemSelectionUnselected(index, opt_value);
+
+  if (notify == NotificationOption::kNotify)
     NotifyListOrComboBoxAfterChange();
   return true;
 }
 
+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);
+    return;
+  }
+
+  SelectOption(index, true, NotificationOption::kDoNotNotify);
+  if (!m_bIsMultiSelectListBox) {
+    m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, opt_value);
+    return;
+  }
+
+  CPDF_Array* 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));
+  }
+}
+
+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() == ComboBox || GetType() == ListBox);
+  ASSERT(GetType() == kComboBox || GetType() == kListBox);
   if (index < 0 || index >= CountOptions())
     return false;
   int iDVIndex = GetDefaultSelectedItem();
@@ -595,8 +594,8 @@
 }
 
 int CPDF_FormField::GetDefaultSelectedItem() const {
-  ASSERT(GetType() == ComboBox || GetType() == ListBox);
-  CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "DV");
+  ASSERT(GetType() == kComboBox || GetType() == kListBox);
+  const CPDF_Object* pValue = GetDefaultValueObject();
   if (!pValue)
     return -1;
   WideString csDV = pValue->GetUnicodeText();
@@ -610,22 +609,22 @@
 }
 
 int CPDF_FormField::CountOptions() const {
-  CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "Opt"));
-  return pArray ? pArray->GetCount() : 0;
+  const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt"));
+  return pArray ? pArray->size() : 0;
 }
 
 WideString CPDF_FormField::GetOptionText(int index, int sub_index) const {
-  CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "Opt"));
+  const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt"));
   if (!pArray)
     return WideString();
 
-  CPDF_Object* pOption = pArray->GetDirectObjectAt(index);
+  const CPDF_Object* pOption = pArray->GetDirectObjectAt(index);
   if (!pOption)
     return WideString();
-  if (CPDF_Array* pOptionArray = pOption->AsArray())
+  if (const CPDF_Array* pOptionArray = pOption->AsArray())
     pOption = pOptionArray->GetDirectObjectAt(sub_index);
 
-  CPDF_String* pString = ToString(pOption);
+  const CPDF_String* pString = ToString(pOption);
   return pString ? pString->GetUnicodeText() : WideString();
 }
 
@@ -637,15 +636,7 @@
   return GetOptionText(index, 0);
 }
 
-int CPDF_FormField::FindOption(WideString csOptLabel) const {
-  for (int i = 0; i < CountOptions(); i++) {
-    if (GetOptionValue(i) == csOptLabel)
-      return i;
-  }
-  return -1;
-}
-
-int CPDF_FormField::FindOptionValue(const WideString& csOptValue) const {
+int CPDF_FormField::FindOption(const WideString& csOptValue) const {
   for (int i = 0; i < CountOptions(); i++) {
     if (GetOptionValue(i) == csOptValue)
       return i;
@@ -653,74 +644,21 @@
   return -1;
 }
 
-#ifdef PDF_ENABLE_XFA
-int CPDF_FormField::InsertOption(WideString csOptLabel,
-                                 int index,
-                                 bool bNotify) {
-  if (csOptLabel.IsEmpty())
-    return -1;
-
-  if (bNotify && !NotifyListOrComboBoxBeforeChange(csOptLabel))
-    return -1;
-
-  ByteString csStr = PDF_EncodeText(csOptLabel);
-  CPDF_Array* pOpt = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "Opt"));
-  if (!pOpt)
-    pOpt = m_pDict->SetNewFor<CPDF_Array>("Opt");
-
-  int iCount = pdfium::base::checked_cast<int>(pOpt->GetCount());
-  if (index >= iCount) {
-    pOpt->AddNew<CPDF_String>(csStr, false);
-    index = iCount;
-  } else {
-    pOpt->InsertNewAt<CPDF_String>(index, csStr, false);
-  }
-
-  if (bNotify)
-    NotifyListOrComboBoxAfterChange();
-  return index;
-}
-
-bool CPDF_FormField::ClearOptions(bool bNotify) {
-  if (bNotify && m_pForm->GetFormNotify()) {
-    WideString csValue;
-    int iIndex = GetSelectedIndex(0);
-    if (iIndex >= 0)
-      csValue = GetOptionLabel(iIndex);
-    if (!NotifyListOrComboBoxBeforeChange(csValue))
-      return false;
-  }
-
-  m_pDict->RemoveFor("Opt");
-  m_pDict->RemoveFor("V");
-  m_pDict->RemoveFor("DV");
-  m_pDict->RemoveFor("I");
-  m_pDict->RemoveFor("TI");
-
-  if (bNotify)
-    NotifyListOrComboBoxAfterChange();
-
-  return true;
-}
-#endif  // PDF_ENABLE_XFA
-
 bool CPDF_FormField::CheckControl(int iControlIndex,
                                   bool bChecked,
-                                  bool bNotify) {
-  ASSERT(GetType() == CheckBox || GetType() == RadioButton);
+                                  NotificationOption notify) {
+  ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
   CPDF_FormControl* pControl = GetControl(iControlIndex);
   if (!pControl)
     return false;
   if (!bChecked && pControl->IsChecked() == bChecked)
     return false;
 
-  WideString csWExport = pControl->GetExportValue();
-  ByteString csBExport = PDF_EncodeText(csWExport);
+  const WideString csWExport = pControl->GetExportValue();
   int iCount = CountControls();
-  bool bUnison = IsUnison(this);
   for (int i = 0; i < iCount; i++) {
     CPDF_FormControl* pCtrl = GetControl(i);
-    if (bUnison) {
+    if (m_bIsUnison) {
       WideString csEValue = pCtrl->GetExportValue();
       if (csEValue == csWExport) {
         if (pCtrl->GetOnStateName() == pControl->GetOnStateName())
@@ -738,28 +676,30 @@
     }
   }
 
-  CPDF_Object* pOpt = FPDF_GetFieldAttr(m_pDict.Get(), "Opt");
+  const CPDF_Object* pOpt = GetFieldAttr(m_pDict.Get(), "Opt");
   if (!ToArray(pOpt)) {
+    ByteString csBExport = PDF_EncodeText(csWExport);
     if (bChecked) {
-      m_pDict->SetNewFor<CPDF_Name>("V", csBExport);
+      m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
     } else {
       ByteString csV;
-      CPDF_Object* pV = FPDF_GetFieldAttr(m_pDict.Get(), "V");
+      const CPDF_Object* pV = GetValueObject();
       if (pV)
         csV = pV->GetString();
       if (csV == csBExport)
-        m_pDict->SetNewFor<CPDF_Name>("V", "Off");
+        m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, "Off");
     }
   } else if (bChecked) {
-    m_pDict->SetNewFor<CPDF_Name>("V", ByteString::Format("%d", iControlIndex));
+    m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV,
+                                  ByteString::Format("%d", iControlIndex));
   }
-  if (bNotify && m_pForm->GetFormNotify())
+  if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
     m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
   return true;
 }
 
 WideString CPDF_FormField::GetCheckValue(bool bDefault) const {
-  ASSERT(GetType() == CheckBox || GetType() == RadioButton);
+  ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
   WideString csExport = L"Off";
   int iCount = CountControls();
   for (int i = 0; i < iCount; i++) {
@@ -776,57 +716,62 @@
 
 bool CPDF_FormField::SetCheckValue(const WideString& value,
                                    bool bDefault,
-                                   bool bNotify) {
-  ASSERT(GetType() == CheckBox || GetType() == RadioButton);
+                                   NotificationOption notify) {
+  ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
   int iCount = CountControls();
   for (int i = 0; i < iCount; i++) {
     CPDF_FormControl* pControl = GetControl(i);
     WideString csExport = pControl->GetExportValue();
     bool val = csExport == value;
-    if (!bDefault)
-      CheckControl(GetControlIndex(pControl), val);
+    if (!bDefault) {
+      CheckControl(GetControlIndex(pControl), val,
+                   NotificationOption::kDoNotNotify);
+    }
     if (val)
       break;
   }
-  if (bNotify && m_pForm->GetFormNotify())
+  if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
     m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
   return true;
 }
 
 int CPDF_FormField::GetTopVisibleIndex() const {
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TI");
+  const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "TI");
   return pObj ? pObj->GetInteger() : 0;
 }
 
 int CPDF_FormField::CountSelectedOptions() const {
-  CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "I"));
-  return pArray ? pArray->GetCount() : 0;
+  const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
+  return pArray ? pArray->size() : 0;
 }
 
 int CPDF_FormField::GetSelectedOptionIndex(int index) const {
-  CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "I"));
+  const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
   if (!pArray)
     return -1;
 
-  int iCount = pArray->GetCount();
+  int iCount = pArray->size();
   if (iCount < 0 || index >= iCount)
     return -1;
   return pArray->GetIntegerAt(index);
 }
 
 bool CPDF_FormField::IsOptionSelected(int iOptIndex) const {
-  CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "I"));
+  const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
   if (!pArray)
     return false;
 
-  for (const auto& pObj : *pArray) {
+  CPDF_ArrayLocker locker(pArray);
+  for (const auto& pObj : locker) {
     if (pObj->GetInteger() == iOptIndex)
       return true;
   }
   return false;
 }
 
-bool CPDF_FormField::SelectOption(int iOptIndex, bool bSelected, bool bNotify) {
+bool CPDF_FormField::SelectOption(int iOptIndex,
+                                  bool bSelected,
+                                  NotificationOption notify) {
   CPDF_Array* pArray = m_pDict->GetArrayFor("I");
   if (!pArray) {
     if (!bSelected)
@@ -836,13 +781,13 @@
   }
 
   bool bReturn = false;
-  for (size_t i = 0; i < pArray->GetCount(); i++) {
+  for (size_t i = 0; i < pArray->size(); i++) {
     int iFind = pArray->GetIntegerAt(i);
     if (iFind == iOptIndex) {
       if (bSelected)
         return true;
 
-      if (bNotify && m_pForm->GetFormNotify()) {
+      if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
         WideString csValue = GetOptionLabel(iOptIndex);
         if (!NotifyListOrComboBoxBeforeChange(csValue))
           return false;
@@ -856,7 +801,7 @@
       if (!bSelected)
         continue;
 
-      if (bNotify && m_pForm->GetFormNotify()) {
+      if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
         WideString csValue = GetOptionLabel(iOptIndex);
         if (!NotifyListOrComboBoxBeforeChange(csValue))
           return false;
@@ -869,28 +814,10 @@
   if (!bReturn) {
     if (bSelected)
       pArray->AddNew<CPDF_Number>(iOptIndex);
-
     if (pArray->IsEmpty())
       m_pDict->RemoveFor("I");
   }
-  if (bNotify)
-    NotifyListOrComboBoxAfterChange();
-
-  return true;
-}
-
-bool CPDF_FormField::ClearSelectedOptions(bool bNotify) {
-  if (bNotify && m_pForm->GetFormNotify()) {
-    WideString csValue;
-    int iIndex = GetSelectedIndex(0);
-    if (iIndex >= 0)
-      csValue = GetOptionLabel(iIndex);
-
-    if (!NotifyListOrComboBoxBeforeChange(csValue))
-      return false;
-  }
-  m_pDict->RemoveFor("I");
-  if (bNotify)
+  if (notify == NotificationOption::kNotify)
     NotifyListOrComboBoxAfterChange();
 
   return true;
@@ -902,7 +829,7 @@
     return;
 
   ByteString DA;
-  if (CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "DA"))
+  if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DA"))
     DA = pObj->GetString();
 
   if (DA.IsEmpty())
@@ -916,49 +843,49 @@
     return;
 
   CPDF_Dictionary* pFont = pDR->GetDictFor("Font");
-  if (!pFont)
+  if (!ValidateFontResourceDict(pFont))
     return;
 
-  CPDF_SimpleParser syntax(DA.AsStringView());
-  syntax.FindTagParamFromStart("Tf", 2);
-  ByteString font_name(syntax.GetWord());
-  CPDF_Dictionary* pFontDict = pFont->GetDictFor(font_name);
+  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;
 
-  m_pFont = m_pForm->GetDocument()->LoadFont(pFontDict);
-  m_FontSize = FX_atof(syntax.GetWord());
+  auto* pData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument());
+  m_pFont = pData->GetFont(pFontDict);
 }
 
 bool CPDF_FormField::NotifyBeforeSelectionChange(const WideString& value) {
-  if (!m_pForm->GetFormNotify())
-    return true;
-  return m_pForm->GetFormNotify()->BeforeSelectionChange(this, value) >= 0;
+  auto* pNotify = m_pForm->GetFormNotify();
+  return !pNotify || pNotify->BeforeSelectionChange(this, value);
 }
 
 void CPDF_FormField::NotifyAfterSelectionChange() {
-  if (!m_pForm->GetFormNotify())
-    return;
-  m_pForm->GetFormNotify()->AfterSelectionChange(this);
+  auto* pNotify = m_pForm->GetFormNotify();
+  if (pNotify)
+    pNotify->AfterSelectionChange(this);
 }
 
 bool CPDF_FormField::NotifyBeforeValueChange(const WideString& value) {
-  if (!m_pForm->GetFormNotify())
-    return true;
-  return m_pForm->GetFormNotify()->BeforeValueChange(this, value) >= 0;
+  auto* pNotify = m_pForm->GetFormNotify();
+  return !pNotify || pNotify->BeforeValueChange(this, value);
 }
 
 void CPDF_FormField::NotifyAfterValueChange() {
-  if (!m_pForm->GetFormNotify())
-    return;
-  m_pForm->GetFormNotify()->AfterValueChange(this);
+  auto* pNotify = m_pForm->GetFormNotify();
+  if (pNotify)
+    pNotify->AfterValueChange(this);
 }
 
 bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) {
   switch (GetType()) {
-    case ListBox:
+    case kListBox:
       return NotifyBeforeSelectionChange(value);
-    case ComboBox:
+    case kComboBox:
       return NotifyBeforeValueChange(value);
     default:
       return true;
@@ -967,13 +894,37 @@
 
 void CPDF_FormField::NotifyListOrComboBoxAfterChange() {
   switch (GetType()) {
-    case ListBox:
+    case kListBox:
       NotifyAfterSelectionChange();
       break;
-    case ComboBox:
+    case kComboBox:
       NotifyAfterValueChange();
       break;
     default:
       break;
   }
 }
+
+const CPDF_Object* CPDF_FormField::GetDefaultValueObject() const {
+  return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kDV);
+}
+
+const CPDF_Object* CPDF_FormField::GetValueObject() const {
+  return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kV);
+}
+
+const CPDF_Object* CPDF_FormField::GetSelectedIndicesObject() const {
+  ASSERT(GetType() == kComboBox || GetType() == kListBox);
+  return GetFieldAttr(m_pDict.Get(), "I");
+}
+
+const CPDF_Object* CPDF_FormField::GetValueOrSelectedIndicesObject() const {
+  ASSERT(GetType() == kComboBox || GetType() == kListBox);
+  const CPDF_Object* pValue = GetValueObject();
+  return pValue ? pValue : GetSelectedIndicesObject();
+}
+
+const std::vector<UnownedPtr<CPDF_FormControl>>& CPDF_FormField::GetControls()
+    const {
+  return m_pForm->GetControlsForField(this);
+}
diff --git a/core/fpdfdoc/cpdf_formfield.h b/core/fpdfdoc/cpdf_formfield.h
index 4f25465..aa51ddc 100644
--- a/core/fpdfdoc/cpdf_formfield.h
+++ b/core/fpdfdoc/cpdf_formfield.h
@@ -12,11 +12,19 @@
 #include <vector>
 
 #include "core/fpdfdoc/cpdf_aaction.h"
-#include "core/fpdfdoc/cpdf_formfield.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/stl_util.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 FormFieldType : uint8_t {
   kUnknown = 0,
@@ -39,88 +47,50 @@
 #endif  // PDF_ENABLE_XFA
 };
 
-Optional<FormFieldType> IntToFormFieldType(int value);
-
 // If values are added to FormFieldType, these will need to be updated.
 #ifdef PDF_ENABLE_XFA
 constexpr size_t kFormFieldTypeCount = 16;
-#else
+#else   // PDF_ENABLE_XFA
 constexpr size_t kFormFieldTypeCount = 8;
 #endif  // PDF_ENABLE_XFA
 
-constexpr FormFieldType kFormFieldTypes[kFormFieldTypeCount] = {
-    FormFieldType::kUnknown,
-    FormFieldType::kPushButton,
-    FormFieldType::kCheckBox,
-    FormFieldType::kRadioButton,
-    FormFieldType::kComboBox,
-    FormFieldType::kListBox,
-    FormFieldType::kTextField,
-    FormFieldType::kSignature,
-#ifdef PDF_ENABLE_XFA
-    FormFieldType::kXFA,
-    FormFieldType::kXFA_CheckBox,
-    FormFieldType::kXFA_ComboBox,
-    FormFieldType::kXFA_ImageField,
-    FormFieldType::kXFA_ListBox,
-    FormFieldType::kXFA_PushButton,
-    FormFieldType::kXFA_Signature,
-    FormFieldType::kXFA_TextField
-#endif  // PDF_ENABLE_XFA
-};
-
-#define FORMFLAG_READONLY 0x01
-#define FORMFLAG_REQUIRED 0x02
-#define FORMFLAG_NOEXPORT 0x04
-
-class CPDF_Dictionary;
-class CPDF_Font;
-class CPDF_FormControl;
-class CPDF_InterForm;
-class CPDF_String;
-
-CPDF_Object* FPDF_GetFieldAttr(const CPDF_Dictionary* pFieldDict,
-                               const char* name,
-                               int nLevel = 0);
-WideString FPDF_GetFullName(CPDF_Dictionary* pFieldDict);
-
 class CPDF_FormField {
  public:
   enum Type {
-    Unknown,
-    PushButton,
-    RadioButton,
-    CheckBox,
-    Text,
-    RichText,
-    File,
-    ListBox,
-    ComboBox,
-    Sign
+    kUnknown,
+    kPushButton,
+    kRadioButton,
+    kCheckBox,
+    kText,
+    kRichText,
+    kFile,
+    kListBox,
+    kComboBox,
+    kSign
   };
 
-  CPDF_FormField(CPDF_InterForm* pForm, CPDF_Dictionary* pDict);
+  CPDF_FormField(CPDF_InteractiveForm* pForm, CPDF_Dictionary* pDict);
   ~CPDF_FormField();
 
-  WideString GetFullName() const;
+  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);
+
+  WideString GetFullName() const;
   Type GetType() const { return m_Type; }
-  uint32_t GetFlags() const { return m_Flags; }
 
   CPDF_Dictionary* GetFieldDict() const { return m_pDict.Get(); }
-  void SetFieldDict(CPDF_Dictionary* pDict) { m_pDict = pDict; }
+  bool ResetField(NotificationOption notify);
 
-  bool ResetField(bool bNotify = false);
-
-  int CountControls() const {
-    return pdfium::CollectionSize<int>(m_ControlList);
-  }
-
-  CPDF_FormControl* GetControl(int index) const {
-    return m_ControlList[index].Get();
-  }
-
+  int CountControls() const;
+  CPDF_FormControl* GetControl(int index) const;
   int GetControlIndex(const CPDF_FormControl* pControl) const;
+
   FormFieldType GetFieldType() const;
 
   CPDF_AAction GetAdditionalAction() const;
@@ -129,93 +99,92 @@
 
   uint32_t GetFieldFlags() const;
   ByteString GetDefaultStyle() const;
-  WideString GetRichTextString() const;
+
+  bool IsRequired() const { return m_bRequired; }
+  bool IsNoExport() const { return m_bNoExport; }
 
   WideString GetValue() const;
   WideString GetDefaultValue() const;
-  bool SetValue(const WideString& value, bool bNotify = false);
+  bool SetValue(const WideString& value, NotificationOption notify);
 
   int GetMaxLen() const;
   int CountSelectedItems() const;
   int GetSelectedIndex(int index) const;
 
-  bool ClearSelection(bool bNotify = false);
+  bool ClearSelection(NotificationOption notify);
   bool IsItemSelected(int index) const;
-  bool SetItemSelection(int index, bool bSelected, bool bNotify = false);
+  bool SetItemSelection(int index, bool bSelected, NotificationOption notify);
 
   bool IsItemDefaultSelected(int index) const;
 
   int GetDefaultSelectedItem() const;
   int CountOptions() const;
-
   WideString GetOptionLabel(int index) const;
   WideString GetOptionValue(int index) const;
+  int FindOption(const WideString& csOptValue) const;
 
-  int FindOption(WideString csOptLabel) const;
-  int FindOptionValue(const WideString& csOptValue) const;
-
-  bool CheckControl(int iControlIndex, bool bChecked, bool bNotify = false);
+  bool CheckControl(int iControlIndex,
+                    bool bChecked,
+                    NotificationOption notify);
 
   int GetTopVisibleIndex() const;
   int CountSelectedOptions() const;
-
   int GetSelectedOptionIndex(int index) const;
   bool IsOptionSelected(int iOptIndex) const;
-
-  bool SelectOption(int iOptIndex, bool bSelected, bool bNotify = false);
-
-  bool ClearSelectedOptions(bool bNotify = false);
-
-#ifdef PDF_ENABLE_XFA
-  bool ClearOptions(bool bNotify = false);
-
-  int InsertOption(WideString csOptLabel, int index = -1, bool bNotify = false);
-#endif  // PDF_ENABLE_XFA
+  bool SelectOption(int iOptIndex, bool bSelected, NotificationOption notify);
 
   float GetFontSize() const { return m_FontSize; }
   CPDF_Font* GetFont() const { return m_pFont.Get(); }
 
-  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
-  const CPDF_InterForm* GetForm() const { return m_pForm.Get(); }
+  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  CPDF_InteractiveForm* GetForm() const { return m_pForm.Get(); }
 
   WideString GetCheckValue(bool bDefault) const;
 
-  void AddFormControl(CPDF_FormControl* pFormControl) {
-    m_ControlList.emplace_back(pFormControl);
-  }
-
-  void SetOpt(std::unique_ptr<CPDF_Object> pOpt) {
-    m_pDict->SetFor("Opt", std::move(pOpt));
-  }
+  void SetOpt(RetainPtr<CPDF_Object> pOpt);
 
  private:
   WideString GetValue(bool bDefault) const;
-  bool SetValue(const WideString& value, bool bDefault, bool bNotify);
-
-  void SyncFieldFlags();
+  bool SetValue(const WideString& value,
+                bool bDefault,
+                NotificationOption notify);
+  void InitFieldFlags();
   int FindListSel(CPDF_String* str);
   WideString GetOptionText(int index, int sub_index) const;
-
   void LoadDA();
-  bool SetCheckValue(const WideString& value, bool bDefault, bool bNotify);
-
+  bool SetCheckValue(const WideString& value,
+                     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();
 
-  CPDF_FormField::Type m_Type;
-  uint32_t m_Flags;
-  UnownedPtr<CPDF_InterForm> const m_pForm;
-  UnownedPtr<CPDF_Dictionary> m_pDict;
-  // Owned by InterForm parent.
-  std::vector<UnownedPtr<CPDF_FormControl>> m_ControlList;
-  float m_FontSize;
-  UnownedPtr<CPDF_Font> m_pFont;
+  const CPDF_Object* GetDefaultValueObject() const;
+  const CPDF_Object* GetValueObject() const;
+
+  // For choice fields.
+  const CPDF_Object* GetSelectedIndicesObject() const;
+
+  // For choice fields.
+  // Value object takes precedence over selected indices object.
+  const CPDF_Object* GetValueOrSelectedIndicesObject() const;
+
+  const std::vector<UnownedPtr<CPDF_FormControl>>& GetControls() const;
+
+  CPDF_FormField::Type m_Type = kUnknown;
+  bool m_bRequired = false;
+  bool m_bNoExport = false;
+  bool m_bIsMultiSelectListBox = false;
+  bool m_bIsUnison = false;
+  float m_FontSize = 0;
+  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 af5d15d..ca75891 100644
--- a/core/fpdfdoc/cpdf_formfield_unittest.cpp
+++ b/core/fpdfdoc/cpdf_formfield_unittest.cpp
@@ -2,47 +2,48 @@
 // 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 "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/fpdfdoc/cpdf_formfield.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(cpdf_formfield, FPDF_GetFullName) {
-  WideString name = FPDF_GetFullName(nullptr);
+TEST(cpdf_formfield, GetFullNameForDict) {
+  WideString name = CPDF_FormField::GetFullNameForDict(nullptr);
   EXPECT_TRUE(name.IsEmpty());
 
   CPDF_IndirectObjectHolder obj_holder;
   CPDF_Dictionary* root = obj_holder.NewIndirect<CPDF_Dictionary>();
   root->SetNewFor<CPDF_Name>("T", "foo");
-  name = FPDF_GetFullName(root);
-  EXPECT_STREQ("foo", name.UTF8Encode().c_str());
+  name = CPDF_FormField::GetFullNameForDict(root);
+  EXPECT_STREQ("foo", name.ToUTF8().c_str());
 
   CPDF_Dictionary* dict1 = obj_holder.NewIndirect<CPDF_Dictionary>();
   root->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict1->GetObjNum());
   dict1->SetNewFor<CPDF_Name>("T", "bar");
-  name = FPDF_GetFullName(root);
-  EXPECT_STREQ("bar.foo", name.UTF8Encode().c_str());
+  name = CPDF_FormField::GetFullNameForDict(root);
+  EXPECT_STREQ("bar.foo", name.ToUTF8().c_str());
 
   CPDF_Dictionary* dict2 = dict1->SetNewFor<CPDF_Dictionary>("Parent");
-  name = FPDF_GetFullName(root);
-  EXPECT_STREQ("bar.foo", name.UTF8Encode().c_str());
+  name = CPDF_FormField::GetFullNameForDict(root);
+  EXPECT_STREQ("bar.foo", name.ToUTF8().c_str());
 
   CPDF_Dictionary* dict3 = obj_holder.NewIndirect<CPDF_Dictionary>();
   dict2->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict3->GetObjNum());
 
   dict3->SetNewFor<CPDF_Name>("T", "qux");
-  name = FPDF_GetFullName(root);
-  EXPECT_STREQ("qux.bar.foo", name.UTF8Encode().c_str());
+  name = CPDF_FormField::GetFullNameForDict(root);
+  EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str());
 
   dict3->SetNewFor<CPDF_Reference>("Parent", &obj_holder, root->GetObjNum());
-  name = FPDF_GetFullName(root);
-  EXPECT_STREQ("qux.bar.foo", name.UTF8Encode().c_str());
-  name = FPDF_GetFullName(dict1);
-  EXPECT_STREQ("foo.qux.bar", name.UTF8Encode().c_str());
-  name = FPDF_GetFullName(dict2);
-  EXPECT_STREQ("bar.foo.qux", name.UTF8Encode().c_str());
-  name = FPDF_GetFullName(dict3);
-  EXPECT_STREQ("bar.foo.qux", name.UTF8Encode().c_str());
+  name = CPDF_FormField::GetFullNameForDict(root);
+  EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str());
+  name = CPDF_FormField::GetFullNameForDict(dict1);
+  EXPECT_STREQ("foo.qux.bar", name.ToUTF8().c_str());
+  name = CPDF_FormField::GetFullNameForDict(dict2);
+  EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str());
+  name = CPDF_FormField::GetFullNameForDict(dict3);
+  EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str());
 }
diff --git a/core/fpdfdoc/cpdf_icon.cpp b/core/fpdfdoc/cpdf_icon.cpp
new file mode 100644
index 0000000..a450e1e
--- /dev/null
+++ b/core/fpdfdoc/cpdf_icon.cpp
@@ -0,0 +1,39 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfdoc/cpdf_icon.h"
+
+#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() = default;
+
+CFX_SizeF CPDF_Icon::GetImageSize() const {
+  CPDF_Dictionary* pDict = m_pStream->GetDict();
+  if (!pDict)
+    return CFX_SizeF();
+
+  CFX_FloatRect rect = pDict->GetRectFor("BBox");
+  return {rect.right - rect.left, rect.top - rect.bottom};
+}
+
+CFX_Matrix CPDF_Icon::GetImageMatrix() const {
+  CPDF_Dictionary* pDict = m_pStream->GetDict();
+  if (!pDict)
+    return CFX_Matrix();
+
+  return pDict->GetMatrixFor("Matrix");
+}
+
+ByteString CPDF_Icon::GetImageAlias() const {
+  CPDF_Dictionary* pDict = m_pStream->GetDict();
+  if (!pDict)
+    return ByteString();
+
+  return pDict->GetStringFor("Name");
+}
diff --git a/core/fpdfdoc/cpdf_icon.h b/core/fpdfdoc/cpdf_icon.h
new file mode 100644
index 0000000..519fcda
--- /dev/null
+++ b/core/fpdfdoc/cpdf_icon.h
@@ -0,0 +1,29 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFDOC_CPDF_ICON_H_
+#define CORE_FPDFDOC_CPDF_ICON_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);
+  ~CPDF_Icon();
+
+  CFX_SizeF GetImageSize() const;
+  CFX_Matrix GetImageMatrix() const;
+  ByteString GetImageAlias() const;
+
+ private:
+  RetainPtr<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 20ed963..55702a3 100644
--- a/core/fpdfdoc/cpdf_iconfit.cpp
+++ b/core/fpdfdoc/cpdf_iconfit.cpp
@@ -10,13 +10,19 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fxcrt/fx_string.h"
 
+namespace {
+
+constexpr float kDefaultPosition = 0.5f;
+
+}  // namespace
+
 CPDF_IconFit::CPDF_IconFit(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
 
 CPDF_IconFit::CPDF_IconFit(const CPDF_IconFit& that) = default;
 
-CPDF_IconFit::~CPDF_IconFit() {}
+CPDF_IconFit::~CPDF_IconFit() = default;
 
-CPDF_IconFit::ScaleMethod CPDF_IconFit::GetScaleMethod() {
+CPDF_IconFit::ScaleMethod CPDF_IconFit::GetScaleMethod() const {
   if (!m_pDict)
     return Always;
 
@@ -30,25 +36,41 @@
   return Always;
 }
 
-bool CPDF_IconFit::IsProportionalScale() {
-  return m_pDict ? m_pDict->GetStringFor("S", "P") != "A" : true;
+bool CPDF_IconFit::IsProportionalScale() const {
+  return !m_pDict || m_pDict->GetStringFor("S", "P") != "A";
 }
 
-void CPDF_IconFit::GetIconPosition(float& fLeft, float& fBottom) {
-  fLeft = fBottom = 0.5;
+CFX_PointF CPDF_IconFit::GetIconBottomLeftPosition() const {
+  float fLeft = kDefaultPosition;
+  float fBottom = kDefaultPosition;
   if (!m_pDict)
-    return;
+    return {fLeft, fBottom};
 
-  CPDF_Array* pA = m_pDict->GetArrayFor("A");
-  if (pA) {
-    uint32_t dwCount = pA->GetCount();
-    if (dwCount > 0)
-      fLeft = pA->GetNumberAt(0);
-    if (dwCount > 1)
-      fBottom = pA->GetNumberAt(1);
-  }
+  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);
+  if (dwCount > 1)
+    fBottom = pA->GetNumberAt(1);
+  return {fLeft, fBottom};
 }
 
-bool CPDF_IconFit::GetFittingBounds() {
-  return m_pDict ? m_pDict->GetBooleanFor("FB") : false;
+bool CPDF_IconFit::GetFittingBounds() const {
+  return m_pDict && m_pDict->GetBooleanFor("FB", false);
+}
+
+CFX_PointF CPDF_IconFit::GetIconPosition() const {
+  if (!m_pDict)
+    return CFX_PointF();
+
+  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};
 }
diff --git a/core/fpdfdoc/cpdf_iconfit.h b/core/fpdfdoc/cpdf_iconfit.h
index e6aafb6..86a4918 100644
--- a/core/fpdfdoc/cpdf_iconfit.h
+++ b/core/fpdfdoc/cpdf_iconfit.h
@@ -7,8 +7,9 @@
 #ifndef CORE_FPDFDOC_CPDF_ICONFIT_H_
 #define CORE_FPDFDOC_CPDF_ICONFIT_H_
 
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
 
@@ -20,14 +21,14 @@
   CPDF_IconFit(const CPDF_IconFit& that);
   ~CPDF_IconFit();
 
-  ScaleMethod GetScaleMethod();
-  bool IsProportionalScale();
-  void GetIconPosition(float& fLeft, float& fBottom);
-  bool GetFittingBounds();
-  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  ScaleMethod GetScaleMethod() const;
+  bool IsProportionalScale() const;
+  bool GetFittingBounds() const;
+  CFX_PointF GetIconBottomLeftPosition() const;
+  CFX_PointF GetIconPosition() const;
 
  private:
-  UnownedPtr<const CPDF_Dictionary> const m_pDict;
+  RetainPtr<const CPDF_Dictionary> const m_pDict;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_ICONFIT_H_
diff --git a/core/fpdfdoc/cpdf_interactiveform.cpp b/core/fpdfdoc/cpdf_interactiveform.cpp
new file mode 100644
index 0000000..a4a1025
--- /dev/null
+++ b/core/fpdfdoc/cpdf_interactiveform.cpp
@@ -0,0 +1,1095 @@
+// 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_interactiveform.h"
+
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "constants/form_fields.h"
+#include "constants/stream_dict_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/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_reference.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/fxge/fx_font.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+const int nMaxRecursion = 32;
+
+void AddFont(CPDF_Dictionary*& pFormDict,
+             CPDF_Document* pDocument,
+             const RetainPtr<CPDF_Font>& pFont,
+             ByteString* csNameTag);
+
+ByteString GenerateNewFontResourceName(const CPDF_Dictionary* pResDict,
+                                       const ByteString& csPrefix) {
+  static const char kDummyFontName[] = "ZiTi";
+  ByteString csStr = csPrefix;
+  if (csStr.IsEmpty())
+    csStr = kDummyFontName;
+
+  const size_t szCount = csStr.GetLength();
+  size_t m = 0;
+  ByteString csTmp;
+  while (m < strlen(kDummyFontName) && m < szCount)
+    csTmp += csStr[m++];
+  while (m < strlen(kDummyFontName)) {
+    csTmp += '0' + m % 10;
+    m++;
+  }
+
+  const CPDF_Dictionary* pDict = pResDict->GetDictFor("Font");
+  ASSERT(pDict);
+
+  int num = 0;
+  ByteString bsNum;
+  while (true) {
+    ByteString csKey = csTmp + bsNum;
+    if (!pDict->KeyExist(csKey))
+      return csKey;
+    if (m < szCount)
+      csTmp += csStr[m++];
+    else
+      bsNum = ByteString::Format("%d", 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> GetFont(CPDF_Dictionary* pFormDict,
+                             CPDF_Document* pDocument,
+                             const ByteString& csNameTag) {
+  ByteString csAlias = PDF_NameDecode(csNameTag.AsStringView());
+  if (!pFormDict || csAlias.IsEmpty())
+    return nullptr;
+
+  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;
+    }
+  }
+  return nullptr;
+}
+
+bool FindFont(CPDF_Dictionary* pFormDict,
+              const CPDF_Font* pFont,
+              ByteString* csNameTag) {
+  if (!pFormDict || !pFont)
+    return false;
+
+  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
+  if (!pDR)
+    return false;
+
+  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
+  if (!ValidateFontResourceDict(pFonts))
+    return false;
+
+  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)
+      continue;
+    if (pElement->GetStringFor("Type") != "Font")
+      continue;
+    if (pFont->GetFontDict() == pElement) {
+      *csNameTag = csKey;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindFont(CPDF_Dictionary* pFormDict,
+              CPDF_Document* pDocument,
+              ByteString csFontName,
+              RetainPtr<CPDF_Font>& pFont,
+              ByteString* csNameTag) {
+  if (!pFormDict)
+    return false;
+
+  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
+  if (!pDR)
+    return false;
+
+  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
+  if (!ValidateFontResourceDict(pFonts))
+    return false;
+
+  if (csFontName.GetLength() > 0)
+    csFontName.Remove(' ');
+
+  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;
+
+    pFont = CPDF_DocPageData::FromDocument(pDocument)->GetFont(pElement);
+    if (!pFont)
+      continue;
+
+    ByteString csBaseFont = pFont->GetBaseFontName();
+    csBaseFont.Remove(' ');
+    if (csBaseFont == csFontName) {
+      *csNameTag = csKey;
+      return true;
+    }
+  }
+  return false;
+}
+
+void AddFont(CPDF_Dictionary*& pFormDict,
+             CPDF_Document* pDocument,
+             const RetainPtr<CPDF_Font>& pFont,
+             ByteString* csNameTag) {
+  if (!pFont)
+    return;
+  if (!pFormDict)
+    InitDict(pFormDict, pDocument);
+
+  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");
+
+  if (csNameTag->IsEmpty())
+    *csNameTag = pFont->GetBaseFontName();
+
+  csNameTag->Remove(' ');
+  *csNameTag = GenerateNewFontResourceName(pDR, *csNameTag);
+  pFonts->SetNewFor<CPDF_Reference>(*csNameTag, pDocument,
+                                    pFont->GetFontDict()->GetObjNum());
+}
+
+RetainPtr<CPDF_Font> AddNativeFont(CPDF_Dictionary*& pFormDict,
+                                   CPDF_Document* pDocument,
+                                   uint8_t charSet,
+                                   ByteString* csNameTag) {
+  if (!pFormDict)
+    InitDict(pFormDict, pDocument);
+
+  ByteString csTemp;
+  RetainPtr<CPDF_Font> pFont =
+      GetNativeFont(pFormDict, pDocument, charSet, &csTemp);
+  if (pFont) {
+    *csNameTag = std::move(csTemp);
+    return pFont;
+  }
+  ByteString csFontName =
+      CPDF_InteractiveForm::GetNativeFontName(charSet, nullptr);
+  if (!csFontName.IsEmpty() &&
+      FindFont(pFormDict, pDocument, csFontName, pFont, csNameTag)) {
+    return pFont;
+  }
+  pFont = CPDF_InteractiveForm::AddNativeFont(charSet, pDocument);
+  if (!pFont)
+    return nullptr;
+
+  AddFont(pFormDict, pDocument, pFont, csNameTag);
+  return pFont;
+}
+
+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();
+  }
+
+  void GetNext(const wchar_t*& pSubName, size_t& size) {
+    pSubName = m_pCur;
+    while (m_pCur < m_pEnd && m_pCur[0] != L'.')
+      m_pCur++;
+
+    size = static_cast<size_t>(m_pCur - pSubName);
+    if (m_pCur < m_pEnd && m_pCur[0] == L'.')
+      m_pCur++;
+  }
+
+ protected:
+  WideString m_FullName;
+  const wchar_t* m_pCur;
+  const wchar_t* m_pEnd;
+};
+
+#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 {
+ public:
+  class Node {
+   public:
+    Node() : m_level(0) {}
+    Node(const WideString& short_name, int level)
+        : m_ShortName(short_name), m_level(level) {}
+    ~Node() = default;
+
+    void AddChildNode(std::unique_ptr<Node> pNode) {
+      m_Children.push_back(std::move(pNode));
+    }
+
+    size_t GetChildrenCount() const { return m_Children.size(); }
+
+    Node* GetChildAt(size_t i) { return m_Children[i].get(); }
+    const Node* GetChildAt(size_t i) const { return m_Children[i].get(); }
+
+    CPDF_FormField* GetFieldAtIndex(size_t index) {
+      size_t nFieldsToGo = index;
+      return GetFieldInternal(&nFieldsToGo);
+    }
+
+    size_t CountFields() const { return CountFieldsInternal(); }
+
+    void SetField(std::unique_ptr<CPDF_FormField> pField) {
+      m_pField = std::move(pField);
+    }
+
+    CPDF_FormField* GetField() const { return m_pField.get(); }
+    WideString GetShortName() const { return m_ShortName; }
+    int GetLevel() const { return m_level; }
+
+   private:
+    CPDF_FormField* GetFieldInternal(size_t* pFieldsToGo) {
+      if (m_pField) {
+        if (*pFieldsToGo == 0)
+          return m_pField.get();
+
+        --*pFieldsToGo;
+      }
+      for (size_t i = 0; i < GetChildrenCount(); ++i) {
+        CPDF_FormField* pField = GetChildAt(i)->GetFieldInternal(pFieldsToGo);
+        if (pField)
+          return pField;
+      }
+      return nullptr;
+    }
+
+    size_t CountFieldsInternal() const {
+      size_t count = 0;
+      if (m_pField)
+        ++count;
+
+      for (size_t i = 0; i < GetChildrenCount(); ++i)
+        count += GetChildAt(i)->CountFieldsInternal();
+      return count;
+    }
+
+    std::vector<std::unique_ptr<Node>> m_Children;
+    WideString m_ShortName;
+    std::unique_ptr<CPDF_FormField> m_pField;
+    const int m_level;
+  };
+
+  CFieldTree();
+  ~CFieldTree();
+
+  bool SetField(const WideString& full_name,
+                std::unique_ptr<CPDF_FormField> pField);
+  CPDF_FormField* GetField(const WideString& full_name);
+
+  Node* FindNode(const WideString& full_name);
+  Node* AddChild(Node* pParent, const WideString& short_name);
+
+  Node* Lookup(Node* pParent, const WideString& short_name);
+
+  Node m_Root;
+};
+
+CFieldTree::CFieldTree() = default;
+
+CFieldTree::~CFieldTree() = default;
+
+CFieldTree::Node* CFieldTree::AddChild(Node* pParent,
+                                       const WideString& short_name) {
+  if (!pParent)
+    return nullptr;
+
+  int level = pParent->GetLevel() + 1;
+  if (level > nMaxRecursion)
+    return nullptr;
+
+  auto pNew = pdfium::MakeUnique<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) {
+  if (!pParent)
+    return nullptr;
+
+  for (size_t i = 0; i < pParent->GetChildrenCount(); ++i) {
+    Node* pNode = pParent->GetChildAt(i);
+    if (pNode->GetShortName() == short_name)
+      return pNode;
+  }
+  return nullptr;
+}
+
+bool CFieldTree::SetField(const WideString& full_name,
+                          std::unique_ptr<CPDF_FormField> pField) {
+  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* pLast = nullptr;
+  while (nLength > 0) {
+    pLast = pNode;
+    WideString name = WideString(pName, nLength);
+    pNode = Lookup(pLast, name);
+    if (!pNode)
+      pNode = AddChild(pLast, name);
+    if (!pNode)
+      return false;
+
+    name_extractor.GetNext(pName, nLength);
+  }
+  if (pNode == &m_Root)
+    return false;
+
+  pNode->SetField(std::move(pField));
+  return true;
+}
+
+CPDF_FormField* CFieldTree::GetField(const WideString& full_name) {
+  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* pLast = nullptr;
+  while (nLength > 0 && pNode) {
+    pLast = pNode;
+    WideString name = WideString(pName, nLength);
+    pNode = Lookup(pLast, name);
+    name_extractor.GetNext(pName, nLength);
+  }
+  return pNode ? pNode->GetField() : nullptr;
+}
+
+CFieldTree::Node* CFieldTree::FindNode(const WideString& full_name) {
+  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* pLast = nullptr;
+  while (nLength > 0 && pNode) {
+    pLast = pNode;
+    WideString name = WideString(pName, nLength);
+    pNode = Lookup(pLast, name);
+    name_extractor.GetNext(pName, nLength);
+  }
+  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();
+  if (!pRoot)
+    return;
+
+  m_pFormDict.Reset(pRoot->GetDictFor("AcroForm"));
+  if (!m_pFormDict)
+    return;
+
+  CPDF_Array* pFields = m_pFormDict->GetArrayFor("Fields");
+  if (!pFields)
+    return;
+
+  for (size_t i = 0; i < pFields->size(); ++i)
+    LoadField(pFields->GetDictAt(i), 0);
+}
+
+CPDF_InteractiveForm::~CPDF_InteractiveForm() = default;
+
+bool CPDF_InteractiveForm::s_bUpdateAP = true;
+
+bool CPDF_InteractiveForm::IsUpdateAPEnabled() {
+  return s_bUpdateAP;
+}
+
+void CPDF_InteractiveForm::SetUpdateAP(bool bUpdateAP) {
+  s_bUpdateAP = bUpdateAP;
+}
+
+RetainPtr<CPDF_Font> CPDF_InteractiveForm::AddStandardFont(
+    CPDF_Document* pDocument,
+    ByteString csFontName) {
+  if (!pDocument || csFontName.IsEmpty())
+    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;
+}
+
+size_t CPDF_InteractiveForm::CountFields(const WideString& csFieldName) const {
+  if (csFieldName.IsEmpty())
+    return m_pFieldTree->m_Root.CountFields();
+
+  CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
+  return pNode ? pNode->CountFields() : 0;
+}
+
+CPDF_FormField* CPDF_InteractiveForm::GetField(
+    uint32_t index,
+    const WideString& csFieldName) const {
+  if (csFieldName.IsEmpty())
+    return m_pFieldTree->m_Root.GetFieldAtIndex(index);
+
+  CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
+  return pNode ? pNode->GetFieldAtIndex(index) : nullptr;
+}
+
+CPDF_FormField* CPDF_InteractiveForm::GetFieldByDict(
+    CPDF_Dictionary* pFieldDict) const {
+  if (!pFieldDict)
+    return nullptr;
+
+  WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict);
+  return m_pFieldTree->GetField(csWName);
+}
+
+CPDF_FormControl* CPDF_InteractiveForm::GetControlAtPoint(
+    CPDF_Page* pPage,
+    const CFX_PointF& point,
+
+    int* z_order) const {
+  CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots");
+  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);
+    if (!pAnnot)
+      continue;
+
+    const auto it = m_ControlMap.find(pAnnot);
+    if (it == m_ControlMap.end())
+      continue;
+
+    CPDF_FormControl* pControl = it->second.get();
+    if (!pControl->GetRect().Contains(point))
+      continue;
+
+    if (z_order)
+      *z_order = static_cast<int>(annot_index);
+    return pControl;
+  }
+  return nullptr;
+}
+
+CPDF_FormControl* CPDF_InteractiveForm::GetControlByDict(
+    const CPDF_Dictionary* pWidgetDict) const {
+  const auto it = m_ControlMap.find(pWidgetDict);
+  return it != m_ControlMap.end() ? it->second.get() : nullptr;
+}
+
+bool CPDF_InteractiveForm::NeedConstructAP() const {
+  return m_pFormDict && m_pFormDict->GetBooleanFor("NeedAppearances", false);
+}
+
+int CPDF_InteractiveForm::CountFieldsInCalculationOrder() {
+  if (!m_pFormDict)
+    return 0;
+
+  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
+  return pArray ? pArray->size() : 0;
+}
+
+CPDF_FormField* CPDF_InteractiveForm::GetFieldInCalculationOrder(int index) {
+  if (!m_pFormDict || index < 0)
+    return nullptr;
+
+  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
+  if (!pArray)
+    return nullptr;
+
+  CPDF_Dictionary* pElement = ToDictionary(pArray->GetDirectObjectAt(index));
+  return pElement ? GetFieldByDict(pElement) : nullptr;
+}
+
+int CPDF_InteractiveForm::FindFieldInCalculationOrder(
+    const CPDF_FormField* pField) {
+  if (!m_pFormDict || !pField)
+    return -1;
+
+  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;
+}
+
+RetainPtr<CPDF_Font> CPDF_InteractiveForm::GetFormFont(
+    ByteString csNameTag) const {
+  return GetFont(m_pFormDict.Get(), m_pDocument.Get(), csNameTag);
+}
+
+CPDF_DefaultAppearance CPDF_InteractiveForm::GetDefaultAppearance() const {
+  if (!m_pFormDict)
+    return CPDF_DefaultAppearance();
+  return CPDF_DefaultAppearance(m_pFormDict->GetStringFor("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();
+  for (size_t i = 0; i < nCount; ++i) {
+    CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i);
+    if (!pField)
+      continue;
+
+    if (bIncludeOrExclude == pdfium::ContainsValue(fields, pField))
+      pField->ResetField(notify);
+  }
+  if (notify == NotificationOption::kNotify && 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);
+}
+
+const std::vector<UnownedPtr<CPDF_FormControl>>&
+CPDF_InteractiveForm::GetControlsForField(const CPDF_FormField* pField) {
+  return m_ControlLists[pField];
+}
+
+void CPDF_InteractiveForm::LoadField(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);
+  if (!pKids) {
+    AddTerminalField(pFieldDict);
+    return;
+  }
+
+  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);
+  }
+}
+
+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");
+  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);
+  }
+}
+
+void CPDF_InteractiveForm::AddTerminalField(CPDF_Dictionary* pFieldDict) {
+  if (!pFieldDict->KeyExist(pdfium::form_fields::kFT)) {
+    // Key "FT" is required for terminal fields, it is also inheritable.
+    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);
+  if (csWName.IsEmpty())
+    return;
+
+  CPDF_FormField* pField = nullptr;
+  pField = m_pFieldTree->GetField(csWName);
+  if (!pField) {
+    CPDF_Dictionary* pParent = pFieldDict;
+    if (!pFieldDict->KeyExist(pdfium::form_fields::kT) &&
+        pFieldDict->GetStringFor("Subtype") == "Widget") {
+      pParent = pFieldDict->GetDictFor(pdfium::form_fields::kParent);
+      if (!pParent)
+        pParent = pFieldDict;
+    }
+
+    if (pParent && pParent != pFieldDict &&
+        !pParent->KeyExist(pdfium::form_fields::kFT)) {
+      if (pFieldDict->KeyExist(pdfium::form_fields::kFT)) {
+        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 =
+            pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFf);
+        if (pFfValue)
+          pParent->SetFor(pdfium::form_fields::kFf, pFfValue->Clone());
+      }
+    }
+
+    auto newField = pdfium::MakeUnique<CPDF_FormField>(this, pParent);
+    pField = newField.get();
+    CPDF_Object* pTObj = pDict->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));
+      else
+        pDict->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);
+  }
+}
+
+CPDF_FormControl* CPDF_InteractiveForm::AddControl(
+    CPDF_FormField* pField,
+    CPDF_Dictionary* pWidgetDict) {
+  const auto it = m_ControlMap.find(pWidgetDict);
+  if (it != m_ControlMap.end())
+    return it->second.get();
+
+  auto pNew = pdfium::MakeUnique<CPDF_FormControl>(pField, pWidgetDict);
+  CPDF_FormControl* pControl = pNew.get();
+  m_ControlMap[pWidgetDict] = std::move(pNew);
+  m_ControlLists[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();
+  for (size_t i = 0; i < nCount; ++i) {
+    CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i);
+    if (!pField)
+      continue;
+
+    int32_t iType = pField->GetType();
+    if (iType == CPDF_FormField::kPushButton ||
+        iType == CPDF_FormField::kCheckBox ||
+        iType == CPDF_FormField::kListBox) {
+      continue;
+    }
+    if (pField->IsNoExport())
+      continue;
+
+    bool bFind = true;
+    if (fields)
+      bFind = pdfium::ContainsValue(*fields, pField);
+    if (bIncludeOrExclude == bFind) {
+      const CPDF_Dictionary* pFieldDict = pField->GetDict();
+      if (pField->IsRequired() &&
+          pFieldDict->GetStringFor(pdfium::form_fields::kV).IsEmpty()) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+std::unique_ptr<CFDF_Document> CPDF_InteractiveForm::ExportToFDF(
+    const WideString& pdf_path,
+    bool bSimpleFileSpec) const {
+  std::vector<CPDF_FormField*> fields;
+  size_t nCount = m_pFieldTree->m_Root.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);
+}
+
+std::unique_ptr<CFDF_Document> CPDF_InteractiveForm::ExportToFDF(
+    const WideString& pdf_path,
+    const std::vector<CPDF_FormField*>& fields,
+    bool bIncludeOrExclude,
+    bool bSimpleFileSpec) const {
+  std::unique_ptr<CFDF_Document> pDoc = CFDF_Document::CreateNewDoc();
+  if (!pDoc)
+    return nullptr;
+
+  CPDF_Dictionary* pMainDict = pDoc->GetRoot()->GetDictFor("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);
+    }
+  }
+
+  CPDF_Array* pFields = pMainDict->SetNewFor<CPDF_Array>("Fields");
+  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 || pField->GetType() == CPDF_FormField::kPushButton)
+      continue;
+
+    uint32_t dwFlags = pField->GetFieldFlags();
+    if (dwFlags & 0x04)
+      continue;
+
+    if (bIncludeOrExclude != pdfium::ContainsValue(fields, pField))
+      continue;
+
+    if ((dwFlags & 0x02) != 0 &&
+        pField->GetDict()->GetStringFor(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);
+    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");
+      if (pOpt) {
+        pFieldDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, csBExport,
+                                           false);
+      } else {
+        pFieldDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
+      }
+    } else {
+      CPDF_Object* pV = CPDF_FormField::GetFieldAttr(pField->GetDict(),
+                                                     pdfium::form_fields::kV);
+      if (pV)
+        pFieldDict->SetFor(pdfium::form_fields::kV, pV->CloneDirectObject());
+    }
+    pFields->Add(pFieldDict);
+  }
+  return pDoc;
+}
+
+void CPDF_InteractiveForm::SetNotifierIface(NotifierIface* pNotify) {
+  m_pFormNotify = pNotify;
+}
diff --git a/core/fpdfdoc/cpdf_interactiveform.h b/core/fpdfdoc/cpdf_interactiveform.h
new file mode 100644
index 0000000..7d1f6c2
--- /dev/null
+++ b/core/fpdfdoc/cpdf_interactiveform.h
@@ -0,0 +1,132 @@
+// 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_INTERACTIVEFORM_H_
+#define CORE_FPDFDOC_CPDF_INTERACTIVEFORM_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfdoc/cpdf_defaultappearance.h"
+#include "core/fpdfdoc/cpdf_formfield.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 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 {
+   public:
+    virtual ~NotifierIface() = default;
+
+    virtual bool BeforeValueChange(CPDF_FormField* pField,
+                                   const WideString& csValue) = 0;
+    virtual void AfterValueChange(CPDF_FormField* pField) = 0;
+    virtual bool BeforeSelectionChange(CPDF_FormField* pField,
+                                       const WideString& csValue) = 0;
+    virtual void AfterSelectionChange(CPDF_FormField* pField) = 0;
+    virtual void AfterCheckedStatusChange(CPDF_FormField* pField) = 0;
+    virtual void AfterFormReset(CPDF_InteractiveForm* pForm) = 0;
+  };
+
+  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);
+
+  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_FormControl* GetControlAtPoint(CPDF_Page* pPage,
+                                      const CFX_PointF& point,
+                                      int* z_order) const;
+  CPDF_FormControl* GetControlByDict(const CPDF_Dictionary* pWidgetDict) const;
+
+  bool NeedConstructAP() const;
+  int CountFieldsInCalculationOrder();
+  CPDF_FormField* GetFieldInCalculationOrder(int index);
+  int FindFieldInCalculationOrder(const CPDF_FormField* pField);
+
+  RetainPtr<CPDF_Font> GetFormFont(ByteString csNameTag) 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::vector<CPDF_FormField*>& fields,
+      bool bIncludeOrExclude,
+      bool bSimpleFileSpec) const;
+
+  void ResetForm(NotificationOption notify);
+
+  // TODO(tsepez): Use a span.
+  void ResetForm(const std::vector<CPDF_FormField*>& fields,
+                 bool bIncludeOrExclude,
+                 NotificationOption notify);
+
+  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(); }
+
+  const std::vector<UnownedPtr<CPDF_FormControl>>& GetControlsForField(
+      const CPDF_FormField* pField);
+
+ private:
+  void LoadField(CPDF_Dictionary* pFieldDict, int nLevel);
+  void AddTerminalField(CPDF_Dictionary* pFieldDict);
+  CPDF_FormControl* AddControl(CPDF_FormField* pField,
+                               CPDF_Dictionary* pWidgetDict);
+
+  static bool s_bUpdateAP;
+
+  ByteString m_bsEncoding;
+  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>>
+      m_ControlMap;
+  // Points into |m_ControlMap|.
+  std::map<const CPDF_FormField*, std::vector<UnownedPtr<CPDF_FormControl>>>
+      m_ControlLists;
+  UnownedPtr<NotifierIface> m_pFormNotify;
+};
+
+#endif  // CORE_FPDFDOC_CPDF_INTERACTIVEFORM_H_
diff --git a/core/fpdfdoc/cpdf_interform.cpp b/core/fpdfdoc/cpdf_interform.cpp
deleted file mode 100644
index 1596b17..0000000
--- a/core/fpdfdoc/cpdf_interform.cpp
+++ /dev/null
@@ -1,1242 +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_interform.h"
-
-#include <utility>
-#include <vector>
-
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/font/cpdf_fontencoding.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cfdf_document.h"
-#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_reference.h"
-#include "core/fpdfapi/parser/cpdf_string.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/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-const int nMaxRecursion = 32;
-
-const struct SupportFieldEncoding {
-  const char* m_name;
-  uint16_t m_codePage;
-} g_fieldEncoding[] = {
-    {"BigFive", 950},
-    {"GBK", 936},
-    {"Shift-JIS", 932},
-    {"UHC", 949},
-};
-
-WideString GetFieldValue(const CPDF_Dictionary& pFieldDict,
-                         const ByteString& bsEncoding) {
-  const ByteString csBValue = pFieldDict.GetStringFor("V");
-  for (const auto& encoding : g_fieldEncoding) {
-    if (bsEncoding == encoding.m_name)
-      return WideString::FromCodePage(csBValue.AsStringView(),
-                                      encoding.m_codePage);
-  }
-  ByteString csTemp = csBValue.Left(2);
-  if (csTemp == "\xFF\xFE" || csTemp == "\xFE\xFF")
-    return PDF_DecodeText(csBValue);
-  return WideString::FromLocal(csBValue.AsStringView());
-}
-
-void AddFont(CPDF_Dictionary*& pFormDict,
-             CPDF_Document* pDocument,
-             const CPDF_Font* pFont,
-             ByteString* csNameTag);
-
-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_InterForm::GetNativeCharSet();
-    CPDF_Font* pFont = CPDF_InterForm::AddStandardFont(pDocument, "Helvetica");
-    if (pFont)
-      AddFont(pFormDict, pDocument, pFont, &csBaseName);
-
-    if (charSet != FX_CHARSET_ANSI) {
-      ByteString csFontName = CPDF_InterForm::GetNativeFont(charSet, nullptr);
-      if (!pFont || csFontName != "Helvetica") {
-        pFont = CPDF_InterForm::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);
-}
-
-CPDF_Font* GetFont(CPDF_Dictionary* pFormDict,
-                   CPDF_Document* pDocument,
-                   const ByteString& csNameTag) {
-  ByteString csAlias = PDF_NameDecode(csNameTag);
-  if (!pFormDict || csAlias.IsEmpty())
-    return nullptr;
-
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
-  if (!pDR)
-    return nullptr;
-
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!pFonts)
-    return nullptr;
-
-  CPDF_Dictionary* pElement = pFonts->GetDictFor(csAlias);
-  if (!pElement)
-    return nullptr;
-
-  if (pElement->GetStringFor("Type") == "Font")
-    return pDocument->LoadFont(pElement);
-  return nullptr;
-}
-
-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 (!pFonts)
-    return nullptr;
-
-  for (const auto& it : *pFonts) {
-    const ByteString& csKey = it.first;
-    if (!it.second)
-      continue;
-
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement)
-      continue;
-    if (pElement->GetStringFor("Type") != "Font")
-      continue;
-    CPDF_Font* pFind = pDocument->LoadFont(pElement);
-    if (!pFind)
-      continue;
-
-    CFX_SubstFont* pSubst = pFind->GetSubstFont();
-    if (!pSubst)
-      continue;
-
-    if (pSubst->m_Charset == static_cast<int>(charSet)) {
-      *csNameTag = csKey;
-      return pFind;
-    }
-  }
-  return nullptr;
-}
-
-bool FindFont(CPDF_Dictionary* pFormDict,
-              const CPDF_Font* pFont,
-              ByteString* csNameTag) {
-  if (!pFormDict || !pFont)
-    return false;
-
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
-  if (!pDR)
-    return false;
-
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!pFonts)
-    return false;
-
-  for (const auto& it : *pFonts) {
-    const ByteString& csKey = it.first;
-    if (!it.second)
-      continue;
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement)
-      continue;
-    if (pElement->GetStringFor("Type") != "Font")
-      continue;
-    if (pFont->GetFontDict() == pElement) {
-      *csNameTag = csKey;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool FindFont(CPDF_Dictionary* pFormDict,
-              CPDF_Document* pDocument,
-              ByteString csFontName,
-              CPDF_Font*& pFont,
-              ByteString* csNameTag) {
-  if (!pFormDict)
-    return false;
-
-  CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
-  if (!pDR)
-    return false;
-
-  CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
-  if (!pFonts)
-    return false;
-
-  if (csFontName.GetLength() > 0)
-    csFontName.Remove(' ');
-
-  for (const auto& it : *pFonts) {
-    const ByteString& csKey = it.first;
-    if (!it.second)
-      continue;
-
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement)
-      continue;
-    if (pElement->GetStringFor("Type") != "Font")
-      continue;
-    pFont = pDocument->LoadFont(pElement);
-    if (!pFont)
-      continue;
-
-    ByteString csBaseFont;
-    csBaseFont = pFont->GetBaseFont();
-    csBaseFont.Remove(' ');
-    if (csBaseFont == csFontName) {
-      *csNameTag = csKey;
-      return true;
-    }
-  }
-  return false;
-}
-
-void AddFont(CPDF_Dictionary*& pFormDict,
-             CPDF_Document* pDocument,
-             const CPDF_Font* pFont,
-             ByteString* csNameTag) {
-  if (!pFont)
-    return;
-  if (!pFormDict)
-    InitDict(pFormDict, pDocument);
-
-  ByteString csTag;
-  if (FindFont(pFormDict, pFont, &csTag)) {
-    *csNameTag = 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");
-
-  if (csNameTag->IsEmpty())
-    *csNameTag = pFont->GetBaseFont();
-
-  csNameTag->Remove(' ');
-  *csNameTag = CPDF_InterForm::GenerateNewResourceName(pDR, "Font", 4,
-                                                       csNameTag->c_str());
-  pFonts->SetNewFor<CPDF_Reference>(*csNameTag, pDocument,
-                                    pFont->GetFontDict()->GetObjNum());
-}
-
-CPDF_Font* AddNativeFont(CPDF_Dictionary*& pFormDict,
-                         CPDF_Document* pDocument,
-                         uint8_t charSet,
-                         ByteString* csNameTag) {
-  if (!pFormDict)
-    InitDict(pFormDict, pDocument);
-
-  ByteString csTemp;
-  CPDF_Font* pFont = GetNativeFont(pFormDict, pDocument, charSet, &csTemp);
-  if (pFont) {
-    *csNameTag = csTemp;
-    return pFont;
-  }
-  ByteString csFontName = CPDF_InterForm::GetNativeFont(charSet, nullptr);
-  if (!csFontName.IsEmpty() &&
-      FindFont(pFormDict, pDocument, csFontName, pFont, csNameTag)) {
-    return pFont;
-  }
-  pFont = CPDF_InterForm::AddNativeFont(charSet, pDocument);
-  if (pFont)
-    AddFont(pFormDict, pDocument, pFont, csNameTag);
-
-  return pFont;
-}
-
-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();
-  }
-
-  void GetNext(const wchar_t*& pSubName, size_t& size) {
-    pSubName = m_pCur;
-    while (m_pCur < m_pEnd && m_pCur[0] != L'.')
-      m_pCur++;
-
-    size = static_cast<size_t>(m_pCur - pSubName);
-    if (m_pCur < m_pEnd && m_pCur[0] == L'.')
-      m_pCur++;
-  }
-
- protected:
-  WideString m_FullName;
-  const wchar_t* m_pCur;
-  const wchar_t* m_pEnd;
-};
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-typedef struct {
-  bool bFind;
-  LOGFONTA lf;
-} PDF_FONTDATA;
-
-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  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-}  // namespace
-
-class CFieldTree {
- public:
-  class Node {
-   public:
-    Node() : m_pField(nullptr), m_level(0) {}
-    Node(const WideString& short_name, int level)
-        : m_ShortName(short_name), m_level(level) {}
-    ~Node() {}
-
-    void AddChildNode(std::unique_ptr<Node> pNode) {
-      m_Children.push_back(std::move(pNode));
-    }
-
-    size_t GetChildrenCount() const { return m_Children.size(); }
-
-    Node* GetChildAt(size_t i) { return m_Children[i].get(); }
-    const Node* GetChildAt(size_t i) const { return m_Children[i].get(); }
-
-    CPDF_FormField* GetFieldAtIndex(size_t index) {
-      size_t nFieldsToGo = index;
-      return GetFieldInternal(&nFieldsToGo);
-    }
-
-    size_t CountFields() const { return CountFieldsInternal(); }
-
-    void SetField(std::unique_ptr<CPDF_FormField> pField) {
-      m_pField = std::move(pField);
-    }
-
-    CPDF_FormField* GetField() const { return m_pField.get(); }
-
-    const WideString& GetShortName() const { return m_ShortName; }
-
-    int GetLevel() const { return m_level; }
-
-   private:
-    CPDF_FormField* GetFieldInternal(size_t* pFieldsToGo) {
-      if (m_pField) {
-        if (*pFieldsToGo == 0)
-          return m_pField.get();
-
-        --*pFieldsToGo;
-      }
-      for (size_t i = 0; i < GetChildrenCount(); ++i) {
-        CPDF_FormField* pField = GetChildAt(i)->GetFieldInternal(pFieldsToGo);
-        if (pField)
-          return pField;
-      }
-      return nullptr;
-    }
-
-    size_t CountFieldsInternal() const {
-      size_t count = 0;
-      if (m_pField)
-        ++count;
-
-      for (size_t i = 0; i < GetChildrenCount(); ++i)
-        count += GetChildAt(i)->CountFieldsInternal();
-      return count;
-    }
-
-    std::vector<std::unique_ptr<Node>> m_Children;
-    WideString m_ShortName;
-    std::unique_ptr<CPDF_FormField> m_pField;
-    const int m_level;
-  };
-
-  CFieldTree();
-  ~CFieldTree();
-
-  bool SetField(const WideString& full_name,
-                std::unique_ptr<CPDF_FormField> pField);
-  CPDF_FormField* GetField(const WideString& full_name);
-
-  Node* FindNode(const WideString& full_name);
-  Node* AddChild(Node* pParent, const WideString& short_name);
-
-  Node* Lookup(Node* pParent, const WideString& short_name);
-
-  Node m_Root;
-};
-
-CFieldTree::CFieldTree() {}
-
-CFieldTree::~CFieldTree() {}
-
-CFieldTree::Node* CFieldTree::AddChild(Node* pParent,
-                                       const WideString& short_name) {
-  if (!pParent)
-    return nullptr;
-
-  int level = pParent->GetLevel() + 1;
-  if (level > nMaxRecursion)
-    return nullptr;
-
-  auto pNew = pdfium::MakeUnique<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) {
-  if (!pParent)
-    return nullptr;
-
-  for (size_t i = 0; i < pParent->GetChildrenCount(); ++i) {
-    Node* pNode = pParent->GetChildAt(i);
-    if (pNode->GetShortName() == short_name)
-      return pNode;
-  }
-  return nullptr;
-}
-
-bool CFieldTree::SetField(const WideString& full_name,
-                          std::unique_ptr<CPDF_FormField> pField) {
-  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* pLast = nullptr;
-  while (nLength > 0) {
-    pLast = pNode;
-    WideString name = WideString(pName, nLength);
-    pNode = Lookup(pLast, name);
-    if (!pNode)
-      pNode = AddChild(pLast, name);
-    if (!pNode)
-      return false;
-
-    name_extractor.GetNext(pName, nLength);
-  }
-  if (pNode == &m_Root)
-    return false;
-
-  pNode->SetField(std::move(pField));
-  return true;
-}
-
-CPDF_FormField* CFieldTree::GetField(const WideString& full_name) {
-  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* pLast = nullptr;
-  while (nLength > 0 && pNode) {
-    pLast = pNode;
-    WideString name = WideString(pName, nLength);
-    pNode = Lookup(pLast, name);
-    name_extractor.GetNext(pName, nLength);
-  }
-  return pNode ? pNode->GetField() : nullptr;
-}
-
-CFieldTree::Node* CFieldTree::FindNode(const WideString& full_name) {
-  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* pLast = nullptr;
-  while (nLength > 0 && pNode) {
-    pLast = pNode;
-    WideString name = WideString(pName, nLength);
-    pNode = Lookup(pLast, name);
-    name_extractor.GetNext(pName, nLength);
-  }
-  return pNode;
-}
-
-CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
-                                  CPDF_Document* pDocument,
-                                  ByteString* csNameTag) {
-  uint8_t charSet = CPDF_InterForm::GetNativeCharSet();
-  return AddNativeFont(pFormDict, pDocument, charSet, csNameTag);
-}
-
-// static
-uint8_t CPDF_InterForm::GetNativeCharSet() {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  uint8_t charSet = FX_CHARSET_ANSI;
-  UINT iCodePage = ::GetACP();
-  switch (iCodePage) {
-    case FX_CODEPAGE_ShiftJIS:
-      charSet = FX_CHARSET_ShiftJIS;
-      break;
-    case FX_CODEPAGE_ChineseSimplified:
-      charSet = FX_CHARSET_ChineseSimplified;
-      break;
-    case FX_CODEPAGE_ChineseTraditional:
-      charSet = FX_CHARSET_ChineseTraditional;
-      break;
-    case FX_CODEPAGE_MSWin_WesternEuropean:
-      charSet = FX_CHARSET_ANSI;
-      break;
-    case FX_CODEPAGE_MSDOS_Thai:
-      charSet = FX_CHARSET_Thai;
-      break;
-    case FX_CODEPAGE_Hangul:
-      charSet = FX_CHARSET_Hangul;
-      break;
-    case FX_CODEPAGE_UTF16LE:
-      charSet = FX_CHARSET_ANSI;
-      break;
-    case FX_CODEPAGE_MSWin_EasternEuropean:
-      charSet = FX_CHARSET_MSWin_EasternEuropean;
-      break;
-    case FX_CODEPAGE_MSWin_Cyrillic:
-      charSet = FX_CHARSET_MSWin_Cyrillic;
-      break;
-    case FX_CODEPAGE_MSWin_Greek:
-      charSet = FX_CHARSET_MSWin_Greek;
-      break;
-    case FX_CODEPAGE_MSWin_Turkish:
-      charSet = FX_CHARSET_MSWin_Turkish;
-      break;
-    case FX_CODEPAGE_MSWin_Hebrew:
-      charSet = FX_CHARSET_MSWin_Hebrew;
-      break;
-    case FX_CODEPAGE_MSWin_Arabic:
-      charSet = FX_CHARSET_MSWin_Arabic;
-      break;
-    case FX_CODEPAGE_MSWin_Baltic:
-      charSet = FX_CHARSET_MSWin_Baltic;
-      break;
-    case FX_CODEPAGE_MSWin_Vietnamese:
-      charSet = FX_CHARSET_MSWin_Vietnamese;
-      break;
-    case FX_CODEPAGE_Johab:
-      charSet = FX_CHARSET_Johab;
-      break;
-  }
-  return charSet;
-#else
-  return 0;
-#endif
-}
-
-CPDF_InterForm::CPDF_InterForm(CPDF_Document* pDocument)
-    : m_pDocument(pDocument),
-      m_pFormDict(nullptr),
-      m_pFieldTree(pdfium::MakeUnique<CFieldTree>()),
-      m_pFormNotify(nullptr) {
-  CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
-  if (!pRoot)
-    return;
-
-  m_pFormDict = pRoot->GetDictFor("AcroForm");
-  if (!m_pFormDict)
-    return;
-
-  CPDF_Array* pFields = m_pFormDict->GetArrayFor("Fields");
-  if (!pFields)
-    return;
-
-  for (size_t i = 0; i < pFields->GetCount(); ++i)
-    LoadField(pFields->GetDictAt(i), 0);
-}
-
-CPDF_InterForm::~CPDF_InterForm() {}
-
-bool CPDF_InterForm::s_bUpdateAP = true;
-
-bool CPDF_InterForm::IsUpdateAPEnabled() {
-  return s_bUpdateAP;
-}
-
-void CPDF_InterForm::SetUpdateAP(bool bUpdateAP) {
-  s_bUpdateAP = bUpdateAP;
-}
-
-ByteString CPDF_InterForm::GenerateNewResourceName(
-    const CPDF_Dictionary* pResDict,
-    const char* csType,
-    int iMinLen,
-    const char* csPrefix) {
-  ByteString csStr = csPrefix;
-  ByteString csBType = csType;
-  if (csStr.IsEmpty()) {
-    if (csBType == "ExtGState")
-      csStr = "GS";
-    else if (csBType == "ColorSpace")
-      csStr = "CS";
-    else if (csBType == "Font")
-      csStr = "ZiTi";
-    else
-      csStr = "Res";
-  }
-  ByteString csTmp = csStr;
-  int iCount = csStr.GetLength();
-  int m = 0;
-  if (iMinLen > 0) {
-    csTmp.clear();
-    while (m < iMinLen && m < iCount)
-      csTmp += csStr[m++];
-    while (m < iMinLen) {
-      csTmp += '0' + m % 10;
-      m++;
-    }
-  } else {
-    m = iCount;
-  }
-  if (!pResDict)
-    return csTmp;
-
-  CPDF_Dictionary* pDict = pResDict->GetDictFor(csType);
-  if (!pDict)
-    return csTmp;
-
-  int num = 0;
-  ByteString bsNum;
-  while (true) {
-    ByteString csKey = csTmp + bsNum;
-    if (!pDict->KeyExist(csKey))
-      return csKey;
-    if (m < iCount)
-      csTmp += csStr[m++];
-    else
-      bsNum = ByteString::Format("%d", num++);
-
-    m++;
-  }
-  return csTmp;
-}
-
-CPDF_Font* CPDF_InterForm::AddStandardFont(CPDF_Document* pDocument,
-                                           ByteString csFontName) {
-  if (!pDocument || csFontName.IsEmpty())
-    return nullptr;
-
-  if (csFontName == "ZapfDingbats")
-    return pDocument->AddStandardFont(csFontName.c_str(), nullptr);
-
-  CPDF_FontEncoding encoding(PDFFONT_ENCODING_WINANSI);
-  return pDocument->AddStandardFont(csFontName.c_str(), &encoding);
-}
-
-ByteString CPDF_InterForm::GetNativeFont(uint8_t charSet, void* pLogFont) {
-  ByteString csFontName;
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  LOGFONTA lf = {};
-  if (charSet == FX_CHARSET_ANSI) {
-    csFontName = "Helvetica";
-    return csFontName;
-  }
-  bool bRet = false;
-  if (charSet == FX_CHARSET_ShiftJIS) {
-    bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
-                                "MS Mincho", lf);
-  } else if (charSet == FX_CHARSET_ChineseSimplified) {
-    bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, "SimSun",
-                                lf);
-  } else if (charSet == FX_CHARSET_ChineseTraditional) {
-    bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, "MingLiU",
-                                lf);
-  }
-  if (!bRet) {
-    bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
-                                "Arial Unicode MS", 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;
-}
-
-CPDF_Font* CPDF_InterForm::AddNativeFont(uint8_t charSet,
-                                         CPDF_Document* pDocument) {
-  if (!pDocument)
-    return nullptr;
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  LOGFONTA lf;
-  ByteString csFontName = GetNativeFont(charSet, &lf);
-  if (!csFontName.IsEmpty()) {
-    if (csFontName == "Helvetica")
-      return AddStandardFont(pDocument, csFontName);
-    return pDocument->AddWindowsFont(&lf, false, true);
-  }
-#endif
-  return nullptr;
-}
-
-CPDF_Font* CPDF_InterForm::AddNativeFont(CPDF_Document* pDocument) {
-  return pDocument ? AddNativeFont(GetNativeCharSet(), pDocument) : nullptr;
-}
-
-size_t CPDF_InterForm::CountFields(const WideString& csFieldName) const {
-  if (csFieldName.IsEmpty())
-    return m_pFieldTree->m_Root.CountFields();
-
-  CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
-  return pNode ? pNode->CountFields() : 0;
-}
-
-CPDF_FormField* CPDF_InterForm::GetField(uint32_t index,
-                                         const WideString& csFieldName) const {
-  if (csFieldName.IsEmpty())
-    return m_pFieldTree->m_Root.GetFieldAtIndex(index);
-
-  CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
-  return pNode ? pNode->GetFieldAtIndex(index) : nullptr;
-}
-
-CPDF_FormField* CPDF_InterForm::GetFieldByDict(
-    CPDF_Dictionary* pFieldDict) const {
-  if (!pFieldDict)
-    return nullptr;
-
-  WideString csWName = FPDF_GetFullName(pFieldDict);
-  return m_pFieldTree->GetField(csWName);
-}
-
-CPDF_FormControl* CPDF_InterForm::GetControlAtPoint(CPDF_Page* pPage,
-                                                    const CFX_PointF& point,
-
-                                                    int* z_order) const {
-  CPDF_Array* pAnnotList = pPage->m_pFormDict->GetArrayFor("Annots");
-  if (!pAnnotList)
-    return nullptr;
-
-  for (size_t i = pAnnotList->GetCount(); i > 0; --i) {
-    size_t annot_index = i - 1;
-    CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(annot_index);
-    if (!pAnnot)
-      continue;
-
-    const auto it = m_ControlMap.find(pAnnot);
-    if (it == m_ControlMap.end())
-      continue;
-
-    CPDF_FormControl* pControl = it->second.get();
-    if (!pControl->GetRect().Contains(point))
-      continue;
-
-    if (z_order)
-      *z_order = static_cast<int>(annot_index);
-    return pControl;
-  }
-  return nullptr;
-}
-
-CPDF_FormControl* CPDF_InterForm::GetControlByDict(
-    const CPDF_Dictionary* pWidgetDict) const {
-  const auto it = m_ControlMap.find(pWidgetDict);
-  return it != m_ControlMap.end() ? it->second.get() : nullptr;
-}
-
-bool CPDF_InterForm::NeedConstructAP() const {
-  return m_pFormDict && m_pFormDict->GetBooleanFor("NeedAppearances");
-}
-
-int CPDF_InterForm::CountFieldsInCalculationOrder() {
-  if (!m_pFormDict)
-    return 0;
-
-  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
-  return pArray ? pArray->GetCount() : 0;
-}
-
-CPDF_FormField* CPDF_InterForm::GetFieldInCalculationOrder(int index) {
-  if (!m_pFormDict || index < 0)
-    return nullptr;
-
-  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
-  if (!pArray)
-    return nullptr;
-
-  CPDF_Dictionary* pElement = ToDictionary(pArray->GetDirectObjectAt(index));
-  return pElement ? GetFieldByDict(pElement) : nullptr;
-}
-
-int CPDF_InterForm::FindFieldInCalculationOrder(const CPDF_FormField* pField) {
-  if (!m_pFormDict || !pField)
-    return -1;
-
-  CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
-  if (!pArray)
-    return -1;
-
-  for (size_t i = 0; i < pArray->GetCount(); i++) {
-    CPDF_Object* pElement = pArray->GetDirectObjectAt(i);
-    if (pElement == pField->GetDict())
-      return i;
-  }
-  return -1;
-}
-
-CPDF_Font* CPDF_InterForm::GetFormFont(ByteString csNameTag) const {
-  return GetFont(m_pFormDict.Get(), m_pDocument.Get(), csNameTag);
-}
-
-CPDF_DefaultAppearance CPDF_InterForm::GetDefaultAppearance() const {
-  if (!m_pFormDict)
-    return CPDF_DefaultAppearance();
-  return CPDF_DefaultAppearance(m_pFormDict->GetStringFor("DA"));
-}
-
-int CPDF_InterForm::GetFormAlignment() const {
-  return m_pFormDict ? m_pFormDict->GetIntegerFor("Q", 0) : 0;
-}
-
-bool CPDF_InterForm::ResetForm(const std::vector<CPDF_FormField*>& fields,
-                               bool bIncludeOrExclude,
-                               bool bNotify) {
-  if (bNotify && m_pFormNotify && m_pFormNotify->BeforeFormReset(this) < 0)
-    return false;
-
-  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;
-
-    if (bIncludeOrExclude == pdfium::ContainsValue(fields, pField))
-      pField->ResetField(bNotify);
-  }
-  if (bNotify && m_pFormNotify)
-    m_pFormNotify->AfterFormReset(this);
-  return true;
-}
-
-bool CPDF_InterForm::ResetForm(bool bNotify) {
-  if (bNotify && m_pFormNotify && m_pFormNotify->BeforeFormReset(this) < 0)
-    return false;
-
-  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(bNotify);
-  }
-  if (bNotify && m_pFormNotify)
-    m_pFormNotify->AfterFormReset(this);
-  return true;
-}
-
-void CPDF_InterForm::LoadField(CPDF_Dictionary* pFieldDict, int nLevel) {
-  if (nLevel > nMaxRecursion)
-    return;
-  if (!pFieldDict)
-    return;
-
-  uint32_t dwParentObjNum = pFieldDict->GetObjNum();
-  CPDF_Array* pKids = pFieldDict->GetArrayFor("Kids");
-  if (!pKids) {
-    AddTerminalField(pFieldDict);
-    return;
-  }
-
-  CPDF_Dictionary* pFirstKid = pKids->GetDictAt(0);
-  if (!pFirstKid)
-    return;
-
-  if (pFirstKid->KeyExist("T") || pFirstKid->KeyExist("Kids")) {
-    for (size_t i = 0; i < pKids->GetCount(); i++) {
-      CPDF_Dictionary* pChildDict = pKids->GetDictAt(i);
-      if (pChildDict) {
-        if (pChildDict->GetObjNum() != dwParentObjNum)
-          LoadField(pChildDict, nLevel + 1);
-      }
-    }
-  } else {
-    AddTerminalField(pFieldDict);
-  }
-}
-
-bool CPDF_InterForm::HasXFAForm() const {
-  return m_pFormDict && m_pFormDict->GetArrayFor("XFA");
-}
-
-void CPDF_InterForm::FixPageFields(const CPDF_Page* pPage) {
-  CPDF_Dictionary* pPageDict = pPage->m_pFormDict.Get();
-  if (!pPageDict)
-    return;
-
-  CPDF_Array* pAnnots = pPageDict->GetArrayFor("Annots");
-  if (!pAnnots)
-    return;
-
-  for (size_t i = 0; i < pAnnots->GetCount(); i++) {
-    CPDF_Dictionary* pAnnot = pAnnots->GetDictAt(i);
-    if (pAnnot && pAnnot->GetStringFor("Subtype") == "Widget")
-      LoadField(pAnnot, 0);
-  }
-}
-
-void CPDF_InterForm::AddTerminalField(CPDF_Dictionary* pFieldDict) {
-  if (!pFieldDict->KeyExist("FT")) {
-    // Key "FT" is required for terminal fields, it is also inheritable.
-    CPDF_Dictionary* pParentDict = pFieldDict->GetDictFor("Parent");
-    if (!pParentDict || !pParentDict->KeyExist("FT"))
-      return;
-  }
-
-  CPDF_Dictionary* pDict = pFieldDict;
-  WideString csWName = FPDF_GetFullName(pFieldDict);
-  if (csWName.IsEmpty())
-    return;
-
-  CPDF_FormField* pField = nullptr;
-  pField = m_pFieldTree->GetField(csWName);
-  if (!pField) {
-    CPDF_Dictionary* pParent = pFieldDict;
-    if (!pFieldDict->KeyExist("T") &&
-        pFieldDict->GetStringFor("Subtype") == "Widget") {
-      pParent = pFieldDict->GetDictFor("Parent");
-      if (!pParent)
-        pParent = pFieldDict;
-    }
-
-    if (pParent && pParent != pFieldDict && !pParent->KeyExist("FT")) {
-      if (pFieldDict->KeyExist("FT")) {
-        CPDF_Object* pFTValue = pFieldDict->GetDirectObjectFor("FT");
-        if (pFTValue)
-          pParent->SetFor("FT", pFTValue->Clone());
-      }
-
-      if (pFieldDict->KeyExist("Ff")) {
-        CPDF_Object* pFfValue = pFieldDict->GetDirectObjectFor("Ff");
-        if (pFfValue)
-          pParent->SetFor("Ff", pFfValue->Clone());
-      }
-    }
-
-    auto newField = pdfium::MakeUnique<CPDF_FormField>(this, pParent);
-    pField = newField.get();
-    CPDF_Object* pTObj = pDict->GetObjectFor("T");
-    if (ToReference(pTObj)) {
-      std::unique_ptr<CPDF_Object> pClone = pTObj->CloneDirectObject();
-      if (pClone)
-        pDict->SetFor("T", std::move(pClone));
-      else
-        pDict->SetNewFor<CPDF_Name>("T", "");
-    }
-    if (!m_pFieldTree->SetField(csWName, std::move(newField)))
-      return;
-  }
-
-  CPDF_Array* pKids = pFieldDict->GetArrayFor("Kids");
-  if (pKids) {
-    for (size_t i = 0; i < pKids->GetCount(); 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);
-  }
-}
-
-CPDF_FormControl* CPDF_InterForm::AddControl(CPDF_FormField* pField,
-                                             CPDF_Dictionary* pWidgetDict) {
-  const auto it = m_ControlMap.find(pWidgetDict);
-  if (it != m_ControlMap.end())
-    return it->second.get();
-
-  auto pNew = pdfium::MakeUnique<CPDF_FormControl>(pField, pWidgetDict);
-  CPDF_FormControl* pControl = pNew.get();
-  m_ControlMap[pWidgetDict] = std::move(pNew);
-  pField->AddFormControl(pControl);
-  return pControl;
-}
-
-bool CPDF_InterForm::CheckRequiredFields(
-    const std::vector<CPDF_FormField*>* fields,
-    bool bIncludeOrExclude) const {
-  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;
-
-    int32_t iType = pField->GetType();
-    if (iType == CPDF_FormField::PushButton ||
-        iType == CPDF_FormField::CheckBox || iType == CPDF_FormField::ListBox) {
-      continue;
-    }
-    uint32_t dwFlags = pField->GetFieldFlags();
-    // TODO(thestig): Look up these magic numbers and add constants for them.
-    if (dwFlags & FORMFLAG_NOEXPORT)
-      continue;
-
-    bool bFind = true;
-    if (fields)
-      bFind = pdfium::ContainsValue(*fields, pField);
-    if (bIncludeOrExclude == bFind) {
-      const CPDF_Dictionary* pFieldDict = pField->GetDict();
-      if ((dwFlags & FORMFLAG_REQUIRED) != 0 &&
-          pFieldDict->GetStringFor("V").IsEmpty()) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-std::unique_ptr<CFDF_Document> CPDF_InterForm::ExportToFDF(
-    const WideString& pdf_path,
-    bool bSimpleFileSpec) const {
-  std::vector<CPDF_FormField*> fields;
-  size_t nCount = m_pFieldTree->m_Root.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);
-}
-
-std::unique_ptr<CFDF_Document> CPDF_InterForm::ExportToFDF(
-    const WideString& pdf_path,
-    const std::vector<CPDF_FormField*>& fields,
-    bool bIncludeOrExclude,
-    bool bSimpleFileSpec) const {
-  std::unique_ptr<CFDF_Document> pDoc = CFDF_Document::CreateNewDoc();
-  if (!pDoc)
-    return nullptr;
-
-  CPDF_Dictionary* pMainDict = pDoc->GetRoot()->GetDictFor("FDF");
-  if (!pdf_path.IsEmpty()) {
-    if (bSimpleFileSpec) {
-      WideString wsFilePath = CPDF_FileSpec::EncodeFileName(pdf_path);
-      pMainDict->SetNewFor<CPDF_String>(
-          "F", ByteString::FromUnicode(wsFilePath), false);
-      pMainDict->SetNewFor<CPDF_String>("UF", PDF_EncodeText(wsFilePath),
-                                        false);
-    } else {
-      auto pNewDict =
-          pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
-      pNewDict->SetNewFor<CPDF_Name>("Type", "Filespec");
-      CPDF_FileSpec filespec(pNewDict.get());
-      filespec.SetFileName(pdf_path);
-      pMainDict->SetFor("F", std::move(pNewDict));
-    }
-  }
-
-  CPDF_Array* pFields = pMainDict->SetNewFor<CPDF_Array>("Fields");
-  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 || pField->GetType() == CPDF_FormField::PushButton)
-      continue;
-
-    uint32_t dwFlags = pField->GetFieldFlags();
-    if (dwFlags & 0x04)
-      continue;
-
-    if (bIncludeOrExclude == pdfium::ContainsValue(fields, pField)) {
-      if ((dwFlags & 0x02) != 0 &&
-          pField->GetDict()->GetStringFor("V").IsEmpty()) {
-        continue;
-      }
-
-      WideString fullname = FPDF_GetFullName(pField->GetFieldDict());
-      auto pFieldDict =
-          pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
-      pFieldDict->SetNewFor<CPDF_String>("T", fullname);
-      if (pField->GetType() == CPDF_FormField::CheckBox ||
-          pField->GetType() == CPDF_FormField::RadioButton) {
-        WideString csExport = pField->GetCheckValue(false);
-        ByteString csBExport = PDF_EncodeText(csExport);
-        CPDF_Object* pOpt = FPDF_GetFieldAttr(pField->GetDict(), "Opt");
-        if (pOpt)
-          pFieldDict->SetNewFor<CPDF_String>("V", csBExport, false);
-        else
-          pFieldDict->SetNewFor<CPDF_Name>("V", csBExport);
-      } else {
-        CPDF_Object* pV = FPDF_GetFieldAttr(pField->GetDict(), "V");
-        if (pV)
-          pFieldDict->SetFor("V", pV->CloneDirectObject());
-      }
-      pFields->Add(std::move(pFieldDict));
-    }
-  }
-  return pDoc;
-}
-
-void CPDF_InterForm::FDF_ImportField(CPDF_Dictionary* pFieldDict,
-                                     const WideString& parent_name,
-                                     bool bNotify,
-                                     int nLevel) {
-  WideString name;
-  if (!parent_name.IsEmpty())
-    name = parent_name + L".";
-
-  name += pFieldDict->GetUnicodeTextFor("T");
-  CPDF_Array* pKids = pFieldDict->GetArrayFor("Kids");
-  if (pKids) {
-    for (size_t i = 0; i < pKids->GetCount(); i++) {
-      CPDF_Dictionary* pKid = pKids->GetDictAt(i);
-      if (!pKid)
-        continue;
-      if (nLevel <= nMaxRecursion)
-        FDF_ImportField(pKid, name, bNotify, nLevel + 1);
-    }
-    return;
-  }
-  if (!pFieldDict->KeyExist("V"))
-    return;
-
-  CPDF_FormField* pField = m_pFieldTree->GetField(name);
-  if (!pField)
-    return;
-
-  WideString csWValue = GetFieldValue(*pFieldDict, m_bsEncoding);
-  FormFieldType fieldType = pField->GetFieldType();
-  if (bNotify && m_pFormNotify) {
-    if (fieldType == FormFieldType::kListBox) {
-      if (m_pFormNotify->BeforeSelectionChange(pField, csWValue) < 0)
-        return;
-    } else if (fieldType == FormFieldType::kComboBox ||
-               fieldType == FormFieldType::kTextField) {
-      if (m_pFormNotify->BeforeValueChange(pField, csWValue) < 0)
-        return;
-    }
-  }
-  pField->SetValue(csWValue);
-  CPDF_FormField::Type eType = pField->GetType();
-  if ((eType == CPDF_FormField::ListBox || eType == CPDF_FormField::ComboBox) &&
-      pFieldDict->KeyExist("Opt")) {
-    pField->SetOpt(pFieldDict->GetDirectObjectFor("Opt")->CloneDirectObject());
-  }
-
-  if (bNotify && m_pFormNotify) {
-    if (fieldType == FormFieldType::kCheckBox ||
-        fieldType == FormFieldType::kRadioButton) {
-      m_pFormNotify->AfterCheckedStatusChange(pField);
-    } else if (fieldType == FormFieldType::kListBox) {
-      m_pFormNotify->AfterSelectionChange(pField);
-    } else if (fieldType == FormFieldType::kComboBox ||
-               fieldType == FormFieldType::kTextField) {
-      m_pFormNotify->AfterValueChange(pField);
-    }
-  }
-}
-
-void CPDF_InterForm::SetFormNotify(IPDF_FormNotify* pNotify) {
-  m_pFormNotify = pNotify;
-}
diff --git a/core/fpdfdoc/cpdf_interform.h b/core/fpdfdoc/cpdf_interform.h
deleted file mode 100644
index e267384..0000000
--- a/core/fpdfdoc/cpdf_interform.h
+++ /dev/null
@@ -1,117 +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_INTERFORM_H_
-#define CORE_FPDFDOC_CPDF_INTERFORM_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "core/fpdfdoc/cpdf_defaultappearance.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CFieldTree;
-class CFDF_Document;
-class CPDF_Document;
-class CPDF_Dictionary;
-class CPDF_Font;
-class CPDF_FormControl;
-class CPDF_FormField;
-class CPDF_Object;
-class CPDF_Page;
-class IPDF_FormNotify;
-
-CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
-                                  CPDF_Document* pDocument,
-                                  ByteString* csNameTag);
-
-class CPDF_InterForm {
- public:
-  explicit CPDF_InterForm(CPDF_Document* pDocument);
-  ~CPDF_InterForm();
-
-  static void SetUpdateAP(bool bUpdateAP);
-  static bool IsUpdateAPEnabled();
-  static ByteString GenerateNewResourceName(const CPDF_Dictionary* pResDict,
-                                            const char* csType,
-                                            int iMinLen,
-                                            const char* csPrefix);
-  static CPDF_Font* AddStandardFont(CPDF_Document* pDocument,
-                                    ByteString csFontName);
-  static ByteString GetNativeFont(uint8_t iCharSet, void* pLogFont);
-  static uint8_t GetNativeCharSet();
-  static CPDF_Font* AddNativeFont(uint8_t iCharSet, CPDF_Document* pDocument);
-  static CPDF_Font* AddNativeFont(CPDF_Document* pDocument);
-
-  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_FormControl* GetControlAtPoint(CPDF_Page* pPage,
-                                      const CFX_PointF& point,
-                                      int* z_order) const;
-  CPDF_FormControl* GetControlByDict(const CPDF_Dictionary* pWidgetDict) const;
-
-  bool NeedConstructAP() const;
-  int CountFieldsInCalculationOrder();
-  CPDF_FormField* GetFieldInCalculationOrder(int index);
-  int FindFieldInCalculationOrder(const CPDF_FormField* pField);
-
-  CPDF_Font* GetFormFont(ByteString csNameTag) 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::vector<CPDF_FormField*>& fields,
-      bool bIncludeOrExclude,
-      bool bSimpleFileSpec) const;
-
-  bool ResetForm(const std::vector<CPDF_FormField*>& fields,
-                 bool bIncludeOrExclude,
-                 bool bNotify);
-  bool ResetForm(bool bNotify);
-
-  void SetFormNotify(IPDF_FormNotify* pNotify);
-  bool HasXFAForm() const;
-  void FixPageFields(const CPDF_Page* pPage);
-
-  IPDF_FormNotify* GetFormNotify() const { return m_pFormNotify.Get(); }
-  CPDF_Document* GetDocument() const { return m_pDocument.Get(); }
-  CPDF_Dictionary* GetFormDict() const { return m_pFormDict.Get(); }
-
- private:
-  void LoadField(CPDF_Dictionary* pFieldDict, int nLevel);
-  void AddTerminalField(CPDF_Dictionary* pFieldDict);
-  CPDF_FormControl* AddControl(CPDF_FormField* pField,
-                               CPDF_Dictionary* pWidgetDict);
-  void FDF_ImportField(CPDF_Dictionary* pField,
-                       const WideString& parent_name,
-                       bool bNotify = false,
-                       int nLevel = 0);
-
-  static bool s_bUpdateAP;
-
-  UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<CPDF_Dictionary> m_pFormDict;
-  std::map<const CPDF_Dictionary*, std::unique_ptr<CPDF_FormControl>>
-      m_ControlMap;
-  std::unique_ptr<CFieldTree> m_pFieldTree;
-  ByteString m_bsEncoding;
-  UnownedPtr<IPDF_FormNotify> m_pFormNotify;
-};
-
-#endif  // CORE_FPDFDOC_CPDF_INTERFORM_H_
diff --git a/core/fpdfdoc/cpdf_link.cpp b/core/fpdfdoc/cpdf_link.cpp
index f7aec40..d5f24b1 100644
--- a/core/fpdfdoc/cpdf_link.cpp
+++ b/core/fpdfdoc/cpdf_link.cpp
@@ -7,15 +7,16 @@
 #include "core/fpdfdoc/cpdf_link.h"
 
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 
-CPDF_Link::CPDF_Link() {}
+CPDF_Link::CPDF_Link() = default;
 
 CPDF_Link::CPDF_Link(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
 
 CPDF_Link::CPDF_Link(const CPDF_Link& that) = default;
 
-CPDF_Link::~CPDF_Link() {}
+CPDF_Link::~CPDF_Link() = default;
 
 CFX_FloatRect CPDF_Link::GetRect() {
   return m_pDict->GetRectFor("Rect");
diff --git a/core/fpdfdoc/cpdf_link.h b/core/fpdfdoc/cpdf_link.h
index 18200cc..aaa6e8e 100644
--- a/core/fpdfdoc/cpdf_link.h
+++ b/core/fpdfdoc/cpdf_link.h
@@ -10,7 +10,7 @@
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
 
@@ -27,7 +27,7 @@
   CPDF_Action GetAction();
 
  private:
-  UnownedPtr<CPDF_Dictionary> m_pDict;
+  RetainPtr<CPDF_Dictionary> m_pDict;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_LINK_H_
diff --git a/core/fpdfdoc/cpdf_linklist.cpp b/core/fpdfdoc/cpdf_linklist.cpp
index 0620dbf..746bbab 100644
--- a/core/fpdfdoc/cpdf_linklist.cpp
+++ b/core/fpdfdoc/cpdf_linklist.cpp
@@ -8,14 +8,15 @@
 
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 
-CPDF_LinkList::CPDF_LinkList() {}
+CPDF_LinkList::CPDF_LinkList() = default;
 
-CPDF_LinkList::~CPDF_LinkList() {}
+CPDF_LinkList::~CPDF_LinkList() = default;
 
 const std::vector<CPDF_Dictionary*>* CPDF_LinkList::GetPageLinks(
     CPDF_Page* pPage) {
-  uint32_t objnum = pPage->m_pFormDict->GetObjNum();
+  uint32_t objnum = pPage->GetDict()->GetObjNum();
   if (objnum == 0)
     return nullptr;
 
@@ -55,11 +56,11 @@
 
 void CPDF_LinkList::LoadPageLinks(CPDF_Page* pPage,
                                   std::vector<CPDF_Dictionary*>* pList) {
-  CPDF_Array* pAnnotList = pPage->m_pFormDict->GetArrayFor("Annots");
+  CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots");
   if (!pAnnotList)
     return;
 
-  for (size_t i = 0; i < pAnnotList->GetCount(); ++i) {
+  for (size_t i = 0; i < pAnnotList->size(); ++i) {
     CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(i);
     bool add_link = (pAnnot && pAnnot->GetStringFor("Subtype") == "Link");
     // Add non-links as nullptrs to preserve z-order.
diff --git a/core/fpdfdoc/cpdf_linklist.h b/core/fpdfdoc/cpdf_linklist.h
index 129790f..442f66c 100644
--- a/core/fpdfdoc/cpdf_linklist.h
+++ b/core/fpdfdoc/cpdf_linklist.h
@@ -10,16 +10,17 @@
 #include <map>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfdoc/cpdf_link.h"
 #include "core/fxcrt/fx_system.h"
 
 class CPDF_Page;
 class CPDF_Dictionary;
 
-class CPDF_LinkList {
+class CPDF_LinkList : public CPDF_Document::LinkListIface {
  public:
   CPDF_LinkList();
-  ~CPDF_LinkList();
+  ~CPDF_LinkList() override;
 
   CPDF_Link GetLinkAtPoint(CPDF_Page* pPage,
                            const CFX_PointF& point,
diff --git a/core/fpdfdoc/cpdf_metadata.cpp b/core/fpdfdoc/cpdf_metadata.cpp
index efe3c29..da8dcb9 100644
--- a/core/fpdfdoc/cpdf_metadata.cpp
+++ b/core/fpdfdoc/cpdf_metadata.cpp
@@ -6,27 +6,77 @@
 
 #include "core/fpdfdoc/cpdf_metadata.h"
 
-#include "core/fpdfapi/parser/cpdf_document.h"
+#include <memory>
+
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fxcrt/xml/cxml_element.h"
+#include "core/fxcrt/cfx_readonlymemorystream.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"
 
-CPDF_Metadata::CPDF_Metadata(const CPDF_Document* pDoc) {
-  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return;
+namespace {
 
-  CPDF_Stream* pStream = pRoot->GetStreamFor("Metadata");
-  if (!pStream)
-    return;
+void CheckForSharedFormInternal(CFX_XMLElement* element,
+                                std::vector<UnsupportedFeature>* unsupported) {
+  WideString attr =
+      element->GetAttribute(WideString::FromASCII("xmlns:adhocwf"));
+  if (attr.EqualsASCII("http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/")) {
+    for (const auto* child = element->GetFirstChild(); child;
+         child = child->GetNextSibling()) {
+      if (child->GetType() != CFX_XMLNode::Type::kElement)
+        continue;
 
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
-  pAcc->LoadAllDataFiltered();
-  m_pXmlElement = CXML_Element::Parse(pAcc->GetData(), pAcc->GetSize());
+      const auto* child_elem = static_cast<const CFX_XMLElement*>(child);
+      if (!child_elem->GetName().EqualsASCII("adhocwf:workflowType"))
+        continue;
+
+      switch (child_elem->GetTextData().GetInteger()) {
+        case 0:
+          unsupported->push_back(UnsupportedFeature::kDocumentSharedFormEmail);
+          break;
+        case 1:
+          unsupported->push_back(
+              UnsupportedFeature::kDocumentSharedFormAcrobat);
+          break;
+        case 2:
+          unsupported->push_back(
+              UnsupportedFeature::kDocumentSharedFormFilesystem);
+          break;
+      }
+      // We only care about the first one we find.
+      break;
+    }
+  }
+
+  for (auto* child = element->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    CFX_XMLElement* pElement = ToXMLElement(child);
+    if (pElement)
+      CheckForSharedFormInternal(pElement, unsupported);
+  }
 }
 
-CPDF_Metadata::~CPDF_Metadata() {}
+}  // namespace
 
-const CXML_Element* CPDF_Metadata::GetRoot() const {
-  return m_pXmlElement.get();
+CPDF_Metadata::CPDF_Metadata(const CPDF_Stream* pStream) : stream_(pStream) {
+  ASSERT(pStream);
+}
+
+CPDF_Metadata::~CPDF_Metadata() = default;
+
+std::vector<UnsupportedFeature> CPDF_Metadata::CheckForSharedForm() const {
+  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(stream_.Get());
+  pAcc->LoadAllDataFiltered();
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pAcc->GetSpan());
+  CFX_XMLParser parser(stream);
+  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
+  if (!doc)
+    return {};
+
+  std::vector<UnsupportedFeature> unsupported;
+  CheckForSharedFormInternal(doc->GetRoot(), &unsupported);
+  return unsupported;
 }
diff --git a/core/fpdfdoc/cpdf_metadata.h b/core/fpdfdoc/cpdf_metadata.h
index fa95815..554492a 100644
--- a/core/fpdfdoc/cpdf_metadata.h
+++ b/core/fpdfdoc/cpdf_metadata.h
@@ -7,20 +7,40 @@
 #ifndef CORE_FPDFDOC_CPDF_METADATA_H_
 #define CORE_FPDFDOC_CPDF_METADATA_H_
 
-#include <memory>
+#include <vector>
 
-class CPDF_Document;
-class CXML_Element;
+#include "core/fxcrt/retain_ptr.h"
+
+class CPDF_Stream;
+
+enum class UnsupportedFeature : uint8_t {
+  kDocumentXFAForm = 1,
+  kDocumentPortableCollection = 2,
+  kDocumentAttachment = 3,
+  kDocumentSecurity = 4,
+  kDocumentSharedReview = 5,
+  kDocumentSharedFormAcrobat = 6,
+  kDocumentSharedFormFilesystem = 7,
+  kDocumentSharedFormEmail = 8,
+
+  kAnnotation3d = 11,
+  kAnnotationMovie = 12,
+  kAnnotationSound = 13,
+  kAnnotationScreenMedia = 14,
+  kAnnotationScreenRichMedia = 15,
+  kAnnotationAttachment = 16,
+  kAnnotationSignature = 17
+};
 
 class CPDF_Metadata {
  public:
-  explicit CPDF_Metadata(const CPDF_Document* pDoc);
+  explicit CPDF_Metadata(const CPDF_Stream* pStream);
   ~CPDF_Metadata();
 
-  const CXML_Element* GetRoot() const;
+  std::vector<UnsupportedFeature> CheckForSharedForm() const;
 
  private:
-  std::unique_ptr<CXML_Element> m_pXmlElement;
+  RetainPtr<const CPDF_Stream> stream_;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_METADATA_H_
diff --git a/core/fpdfdoc/cpdf_metadata_unittest.cpp b/core/fpdfdoc/cpdf_metadata_unittest.cpp
new file mode 100644
index 0000000..41f4b5d
--- /dev/null
+++ b/core/fpdfdoc/cpdf_metadata_unittest.cpp
@@ -0,0 +1,156 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfdoc/cpdf_metadata.h"
+
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CPDF_MetadataTest, CheckSharedFormEmailAtTopLevel) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>0</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  ASSERT_EQ(1U, results.size());
+  EXPECT_EQ(UnsupportedFeature::kDocumentSharedFormEmail, results[0]);
+}
+
+TEST(CPDF_MetadataTest, CheckSharedFormAcrobatAtTopLevel) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>1</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  ASSERT_EQ(1U, results.size());
+  EXPECT_EQ(UnsupportedFeature::kDocumentSharedFormAcrobat, results[0]);
+}
+
+TEST(CPDF_MetadataTest, CheckSharedFormFilesystemAtTopLevel) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>2</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  ASSERT_EQ(1U, results.size());
+  EXPECT_EQ(UnsupportedFeature::kDocumentSharedFormFilesystem, results[0]);
+}
+
+TEST(CPDF_MetadataTest, CheckSharedFormWithoutWorkflow) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:state>2</adhocwf:state>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  EXPECT_EQ(0U, results.size());
+}
+
+TEST(CPDF_MetadataTest, CheckSharedFormAsChild) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<grandparent><parent>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>0</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>"
+      "</parent></grandparent>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  ASSERT_EQ(1U, results.size());
+  EXPECT_EQ(UnsupportedFeature::kDocumentSharedFormEmail, results[0]);
+}
+
+TEST(CPDF_MetadataTest, CheckSharedFormAsNoAdhoc) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node></node>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  EXPECT_EQ(0U, results.size());
+}
+
+TEST(CPDF_MetadataTest, CheckSharedFormWrongNamespace) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/2.0/\">\n"
+      "<adhocwf:workflowType>1</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  EXPECT_EQ(0U, results.size());
+}
+
+TEST(CPDF_MetadataTest, CheckSharedFormMultipleErrors) {
+  static const char data[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<grandparent>"
+      "<parent>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>0</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>"
+      "</parent>"
+      "<node2 "
+      "xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>2</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node2>"
+      "<node3 "
+      "xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>1</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node3>"
+      "</grandparent>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(data).raw_span());
+  CPDF_Metadata metadata(stream.Get());
+
+  auto results = metadata.CheckForSharedForm();
+  ASSERT_EQ(3U, results.size());
+  EXPECT_EQ(UnsupportedFeature::kDocumentSharedFormEmail, results[0]);
+  EXPECT_EQ(UnsupportedFeature::kDocumentSharedFormFilesystem, results[1]);
+  EXPECT_EQ(UnsupportedFeature::kDocumentSharedFormAcrobat, results[2]);
+}
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
index 3c5b08d..b232540 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -36,7 +36,7 @@
 // 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(const CPDF_Dictionary* pNode,
+bool GetNodeAncestorsLimits(CPDF_Dictionary* pNode,
                             const CPDF_Array* pFind,
                             int nLevel,
                             std::vector<CPDF_Array*>* pLimits) {
@@ -52,7 +52,7 @@
   if (!pKids)
     return false;
 
-  for (size_t i = 0; i < pKids->GetCount(); ++i) {
+  for (size_t i = 0; i < pKids->size(); ++i) {
     CPDF_Dictionary* pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
@@ -94,7 +94,7 @@
     // names to find the new lower and upper limits.
     WideString csNewLeft = csRight;
     WideString csNewRight = csLeft;
-    for (size_t i = 0; i < pNames->GetCount() / 2; ++i) {
+    for (size_t i = 0; i < pNames->size() / 2; ++i) {
       WideString wsName = pNames->GetUnicodeTextAt(i * 2);
       if (wsName.Compare(csNewLeft) < 0)
         csNewLeft = wsName;
@@ -111,7 +111,7 @@
     return false;
 
   // Loop through the kids to find the leaf array |pFind|.
-  for (size_t i = 0; i < pKids->GetCount(); ++i) {
+  for (size_t i = 0; i < pKids->size(); ++i) {
     CPDF_Dictionary* pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
@@ -132,7 +132,7 @@
     // kids to find the new lower and upper limits.
     WideString csNewLeft = csRight;
     WideString csNewRight = csLeft;
-    for (size_t j = 0; j < pKids->GetCount(); ++j) {
+    for (size_t j = 0; j < pKids->size(); ++j) {
       CPDF_Array* pKidLimits = pKids->GetDictAt(j)->GetArrayFor("Limits");
       ASSERT(pKidLimits);
       if (pKidLimits->GetUnicodeTextAt(0).Compare(csNewLeft) < 0)
@@ -153,7 +153,7 @@
 // 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(const CPDF_Dictionary* pNode,
+CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode,
                                   const WideString& csName,
                                   int nLevel,
                                   size_t* nIndex,
@@ -178,7 +178,7 @@
       if (ppFind)
         *ppFind = pNames;
       if (pFindIndex)
-        *pFindIndex = pNames->GetCount() / 2 - 1;
+        *pFindIndex = pNames->size() / 2 - 1;
 
       return nullptr;
     }
@@ -186,7 +186,7 @@
 
   // If the node is a leaf node, look for the name in its names array.
   if (pNames) {
-    size_t dwCount = pNames->GetCount() / 2;
+    size_t dwCount = pNames->size() / 2;
     for (size_t i = 0; i < dwCount; i++) {
       WideString csValue = pNames->GetUnicodeTextAt(i * 2);
       int32_t iCompare = csValue.Compare(csName);
@@ -211,7 +211,7 @@
   if (!pKids)
     return nullptr;
 
-  for (size_t i = 0; i < pKids->GetCount(); i++) {
+  for (size_t i = 0; i < pKids->size(); i++) {
     CPDF_Dictionary* pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
@@ -228,7 +228,7 @@
 // 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(const CPDF_Dictionary* pNode,
+CPDF_Object* SearchNameNodeByIndex(CPDF_Dictionary* pNode,
                                    size_t nIndex,
                                    int nLevel,
                                    size_t* nCurIndex,
@@ -240,7 +240,7 @@
 
   CPDF_Array* pNames = pNode->GetArrayFor("Names");
   if (pNames) {
-    size_t nCount = pNames->GetCount() / 2;
+    size_t nCount = pNames->size() / 2;
     if (nIndex >= *nCurIndex + nCount) {
       *nCurIndex += nCount;
       return nullptr;
@@ -258,7 +258,7 @@
   if (!pKids)
     return nullptr;
 
-  for (size_t i = 0; i < pKids->GetCount(); i++) {
+  for (size_t i = 0; i < pKids->size(); i++) {
     CPDF_Dictionary* pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
@@ -277,14 +277,14 @@
 
   CPDF_Array* pNames = pNode->GetArrayFor("Names");
   if (pNames)
-    return pNames->GetCount() / 2;
+    return pNames->size() / 2;
 
   CPDF_Array* pKids = pNode->GetArrayFor("Kids");
   if (!pKids)
     return 0;
 
   size_t nCount = 0;
-  for (size_t i = 0; i < pKids->GetCount(); i++) {
+  for (size_t i = 0; i < pKids->size(); i++) {
     CPDF_Dictionary* pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
@@ -298,10 +298,8 @@
 
 CPDF_NameTree::CPDF_NameTree(CPDF_Dictionary* pRoot) : m_pRoot(pRoot) {}
 
-CPDF_NameTree::CPDF_NameTree(const CPDF_Document* pDoc,
-                             const ByteString& category)
-    : m_pRoot(nullptr) {
-  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
+CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, const ByteString& category) {
+  CPDF_Dictionary* pRoot = pDoc->GetRoot();
   if (!pRoot)
     return;
 
@@ -309,7 +307,7 @@
   if (!pNames)
     return;
 
-  m_pRoot = pNames->GetDictFor(category);
+  m_pRoot.Reset(pNames->GetDictFor(category));
 }
 
 CPDF_NameTree::~CPDF_NameTree() {}
@@ -318,19 +316,7 @@
   return m_pRoot ? CountNamesInternal(m_pRoot.Get(), 0) : 0;
 }
 
-int CPDF_NameTree::GetIndex(const WideString& csName) const {
-  if (!m_pRoot)
-    return -1;
-
-  size_t nIndex = 0;
-  if (!SearchNameNodeByName(m_pRoot.Get(), csName, 0, &nIndex, nullptr,
-                            nullptr)) {
-    return -1;
-  }
-  return nIndex;
-}
-
-bool CPDF_NameTree::AddValueAndName(std::unique_ptr<CPDF_Object> pObj,
+bool CPDF_NameTree::AddValueAndName(RetainPtr<CPDF_Object> pObj,
                                     const WideString& name) {
   if (!m_pRoot)
     return false;
@@ -354,7 +340,9 @@
     SearchNameNodeByIndex(m_pRoot.Get(), 0, 0, &nCurIndex, &csName, &pFind,
                           nullptr);
   }
-  ASSERT(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|.
diff --git a/core/fpdfdoc/cpdf_nametree.h b/core/fpdfdoc/cpdf_nametree.h
index 72d0d66..b018ae7 100644
--- a/core/fpdfdoc/cpdf_nametree.h
+++ b/core/fpdfdoc/cpdf_nametree.h
@@ -10,7 +10,7 @@
 #include <memory>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
@@ -20,23 +20,21 @@
 class CPDF_NameTree {
  public:
   explicit CPDF_NameTree(CPDF_Dictionary* pRoot);
-  CPDF_NameTree(const CPDF_Document* pDoc, const ByteString& category);
+  CPDF_NameTree(CPDF_Document* pDoc, const ByteString& category);
   ~CPDF_NameTree();
 
-  bool AddValueAndName(std::unique_ptr<CPDF_Object> pObj,
-                       const WideString& name);
+  bool AddValueAndName(RetainPtr<CPDF_Object> pObj, const WideString& name);
   bool DeleteValueAndName(int nIndex);
 
   CPDF_Object* LookupValueAndName(int nIndex, WideString* csName) const;
   CPDF_Object* LookupValue(const WideString& csName) const;
   CPDF_Array* LookupNamedDest(CPDF_Document* pDoc, const WideString& sName);
 
-  int GetIndex(const WideString& csName) const;
   size_t GetCount() const;
-  CPDF_Dictionary* GetRoot() const { return m_pRoot.Get(); }
+  CPDF_Dictionary* GetRootForTest() const { return m_pRoot.Get(); }
 
  private:
-  UnownedPtr<CPDF_Dictionary> m_pRoot;
+  RetainPtr<CPDF_Dictionary> 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 38c3140..13b6b5c 100644
--- a/core/fpdfdoc/cpdf_nametree_unittest.cpp
+++ b/core/fpdfdoc/cpdf_nametree_unittest.cpp
@@ -8,6 +8,7 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -76,7 +77,7 @@
 
 TEST(cpdf_nametree, GetUnicodeNameWithBOM) {
   // Set up the root dictionary with a Names array.
-  auto pRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
+  auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
   CPDF_Array* pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
 
   // Add the key "1" (with BOM) and value 100 into the array.
@@ -88,7 +89,7 @@
   pNames->AddNew<CPDF_Number>(100);
 
   // Check that the key is as expected.
-  CPDF_NameTree nameTree(pRootDict.get());
+  CPDF_NameTree nameTree(pRootDict.Get());
   WideString storedName;
   nameTree.LookupValueAndName(0, &storedName);
   EXPECT_STREQ(L"1", storedName.c_str());
@@ -102,29 +103,29 @@
 
 TEST(cpdf_nametree, AddIntoNames) {
   // Set up a name tree with a single Names array.
-  auto pRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
+  auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
   CPDF_Array* pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
   AddNameKeyValue(pNames, "2.txt", 222);
   AddNameKeyValue(pNames, "7.txt", 777);
 
-  CPDF_NameTree nameTree(pRootDict.get());
-  pNames = nameTree.GetRoot()->GetArrayFor("Names");
+  CPDF_NameTree nameTree(pRootDict.Get());
+  pNames = nameTree.GetRootForTest()->GetArrayFor("Names");
 
   // Insert a name that already exists in the names array.
   EXPECT_FALSE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(111), L"2.txt"));
+      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111), L"2.txt"));
 
   // Insert in the beginning of the names array.
   EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(111), L"1.txt"));
+      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111), L"1.txt"));
 
   // Insert in the middle of the names array.
   EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(555), L"5.txt"));
+      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555), L"5.txt"));
 
   // Insert at the end of the names array.
   EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(999), L"9.txt"));
+      nameTree.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);
@@ -136,41 +137,41 @@
 
 TEST(cpdf_nametree, AddIntoKids) {
   // Set up a name tree with five nodes of three levels.
-  auto pRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
-  FillNameTreeDict(pRootDict.get());
-  CPDF_NameTree nameTree(pRootDict.get());
+  auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  FillNameTreeDict(pRootDict.Get());
+  CPDF_NameTree nameTree(pRootDict.Get());
 
   // Check that adding an existing name would fail.
   EXPECT_FALSE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(444), L"9.txt"));
+      nameTree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444), L"9.txt"));
 
   // Add a name within the limits of a leaf node.
   EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(444), L"4.txt"));
+      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());
 
   // Add a name that requires changing the limits of two bottom levels.
   EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(666), L"6.txt"));
+      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());
 
   // Add a name that requires changing the limits of two top levels.
   EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(99), L"99.txt"));
+      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());
 
   // Add a name that requires changing the lower limit of all levels.
   EXPECT_TRUE(
-      nameTree.AddValueAndName(pdfium::MakeUnique<CPDF_Number>(-5), L"0.txt"));
+      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());
 
   // Check that the node on the first level has the expected limits.
   CPDF_Dictionary* pKid1 =
-      nameTree.GetRoot()->GetArrayFor("Kids")->GetDictAt(0);
+      nameTree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid1);
   CheckLimitsArray(pKid1, "0.txt", "99.txt");
 
@@ -210,13 +211,13 @@
 
 TEST(cpdf_nametree, DeleteFromKids) {
   // Set up a name tree with five nodes of three levels.
-  auto pRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
-  FillNameTreeDict(pRootDict.get());
-  CPDF_NameTree nameTree(pRootDict.get());
+  auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  FillNameTreeDict(pRootDict.Get());
+  CPDF_NameTree nameTree(pRootDict.Get());
 
   // Retrieve the kid dictionaries.
   CPDF_Dictionary* pKid1 =
-      nameTree.GetRoot()->GetArrayFor("Kids")->GetDictAt(0);
+      nameTree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid1);
   CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid2);
@@ -237,9 +238,9 @@
   EXPECT_EQ(999, nameTree.LookupValue(L"9.txt")->GetInteger());
   EXPECT_TRUE(nameTree.LookupValueAndName(4, &csName));
   EXPECT_STREQ(L"9.txt", csName.c_str());
-  EXPECT_EQ(2u, pKid1->GetArrayFor("Kids")->GetCount());
+  EXPECT_EQ(2u, pKid1->GetArrayFor("Kids")->size());
   EXPECT_TRUE(nameTree.DeleteValueAndName(4));
-  EXPECT_EQ(1u, pKid1->GetArrayFor("Kids")->GetCount());
+  EXPECT_EQ(1u, pKid1->GetArrayFor("Kids")->size());
   CheckLimitsArray(pKid1, "1.txt", "5.txt");
 
   // Delete the name "2.txt", and check that its node does not get deleted, its
@@ -248,9 +249,9 @@
   EXPECT_EQ(222, nameTree.LookupValue(L"2.txt")->GetInteger());
   EXPECT_TRUE(nameTree.LookupValueAndName(1, &csName));
   EXPECT_STREQ(L"2.txt", csName.c_str());
-  EXPECT_EQ(4u, pKid4->GetArrayFor("Names")->GetCount());
+  EXPECT_EQ(4u, pKid4->GetArrayFor("Names")->size());
   EXPECT_TRUE(nameTree.DeleteValueAndName(1));
-  EXPECT_EQ(2u, pKid4->GetArrayFor("Names")->GetCount());
+  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");
@@ -261,9 +262,9 @@
   EXPECT_EQ(111, nameTree.LookupValue(L"1.txt")->GetInteger());
   EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"1.txt", csName.c_str());
-  EXPECT_EQ(2u, pKid2->GetArrayFor("Kids")->GetCount());
+  EXPECT_EQ(2u, pKid2->GetArrayFor("Kids")->size());
   EXPECT_TRUE(nameTree.DeleteValueAndName(0));
-  EXPECT_EQ(1u, pKid2->GetArrayFor("Kids")->GetCount());
+  EXPECT_EQ(1u, pKid2->GetArrayFor("Kids")->size());
   CheckLimitsArray(pKid2, "3.txt", "5.txt");
   CheckLimitsArray(pKid1, "3.txt", "5.txt");
 
@@ -273,9 +274,9 @@
   EXPECT_EQ(333, nameTree.LookupValue(L"3.txt")->GetInteger());
   EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"3.txt", csName.c_str());
-  EXPECT_EQ(4u, pKid5->GetArrayFor("Names")->GetCount());
+  EXPECT_EQ(4u, pKid5->GetArrayFor("Names")->size());
   EXPECT_TRUE(nameTree.DeleteValueAndName(0));
-  EXPECT_EQ(2u, pKid5->GetArrayFor("Names")->GetCount());
+  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");
@@ -286,9 +287,9 @@
   EXPECT_EQ(555, nameTree.LookupValue(L"5.txt")->GetInteger());
   EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"5.txt", csName.c_str());
-  EXPECT_EQ(1u, nameTree.GetRoot()->GetArrayFor("Kids")->GetCount());
+  EXPECT_EQ(1u, nameTree.GetRootForTest()->GetArrayFor("Kids")->size());
   EXPECT_TRUE(nameTree.DeleteValueAndName(0));
-  EXPECT_EQ(0u, nameTree.GetRoot()->GetArrayFor("Kids")->GetCount());
+  EXPECT_EQ(0u, nameTree.GetRootForTest()->GetArrayFor("Kids")->size());
 
   // Check that the tree is now empty.
   EXPECT_EQ(0u, nameTree.GetCount());
diff --git a/core/fpdfdoc/cpdf_numbertree.cpp b/core/fpdfdoc/cpdf_numbertree.cpp
index 952fb4e..46257c8 100644
--- a/core/fpdfdoc/cpdf_numbertree.cpp
+++ b/core/fpdfdoc/cpdf_numbertree.cpp
@@ -11,15 +11,15 @@
 
 namespace {
 
-CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) {
-  CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
+const CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) {
+  const CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
   if (pLimits &&
       (num < pLimits->GetIntegerAt(0) || num > pLimits->GetIntegerAt(1))) {
     return nullptr;
   }
-  CPDF_Array* pNumbers = pNode->GetArrayFor("Nums");
+  const CPDF_Array* pNumbers = pNode->GetArrayFor("Nums");
   if (pNumbers) {
-    for (size_t i = 0; i < pNumbers->GetCount() / 2; i++) {
+    for (size_t i = 0; i < pNumbers->size() / 2; i++) {
       int index = pNumbers->GetIntegerAt(i * 2);
       if (num == index)
         return pNumbers->GetDirectObjectAt(i * 2 + 1);
@@ -29,16 +29,16 @@
     return nullptr;
   }
 
-  CPDF_Array* pKids = pNode->GetArrayFor("Kids");
+  const CPDF_Array* pKids = pNode->GetArrayFor("Kids");
   if (!pKids)
     return nullptr;
 
-  for (size_t i = 0; i < pKids->GetCount(); i++) {
-    CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+  for (size_t i = 0; i < pKids->size(); i++) {
+    const CPDF_Dictionary* pKid = pKids->GetDictAt(i);
     if (!pKid)
       continue;
 
-    CPDF_Object* pFound = SearchNumberNode(pKid, num);
+    const CPDF_Object* pFound = SearchNumberNode(pKid, num);
     if (pFound)
       return pFound;
   }
@@ -47,10 +47,11 @@
 
 }  // namespace
 
-CPDF_NumberTree::CPDF_NumberTree(CPDF_Dictionary* pRoot) : m_pRoot(pRoot) {}
+CPDF_NumberTree::CPDF_NumberTree(const CPDF_Dictionary* pRoot)
+    : m_pRoot(pRoot) {}
 
 CPDF_NumberTree::~CPDF_NumberTree() {}
 
-CPDF_Object* CPDF_NumberTree::LookupValue(int num) const {
+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 47c40b9..2a3fa6e 100644
--- a/core/fpdfdoc/cpdf_numbertree.h
+++ b/core/fpdfdoc/cpdf_numbertree.h
@@ -7,20 +7,20 @@
 #ifndef CORE_FPDFDOC_CPDF_NUMBERTREE_H_
 #define CORE_FPDFDOC_CPDF_NUMBERTREE_H_
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
 
 class CPDF_NumberTree {
  public:
-  explicit CPDF_NumberTree(CPDF_Dictionary* pRoot);
+  explicit CPDF_NumberTree(const CPDF_Dictionary* pRoot);
   ~CPDF_NumberTree();
 
-  CPDF_Object* LookupValue(int num) const;
+  const CPDF_Object* LookupValue(int num) const;
 
  protected:
-  UnownedPtr<CPDF_Dictionary> const m_pRoot;
+  RetainPtr<const CPDF_Dictionary> const m_pRoot;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_NUMBERTREE_H_
diff --git a/core/fpdfdoc/cpdf_occontext.cpp b/core/fpdfdoc/cpdf_occontext.cpp
deleted file mode 100644
index e596909..0000000
--- a/core/fpdfdoc/cpdf_occontext.cpp
+++ /dev/null
@@ -1,286 +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_occontext.h"
-
-#include "core/fpdfapi/page/cpdf_pageobject.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_document.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->GetCount(); i++) {
-    if (pArray->GetDictAt(i) == pGroupDict)
-      return i;
-  }
-  return -1;
-}
-
-bool HasIntent(const CPDF_Dictionary* pDict,
-               const ByteStringView& csElement,
-               const ByteStringView& csDef) {
-  CPDF_Object* pIntent = pDict->GetDirectObjectFor("Intent");
-  if (!pIntent)
-    return csElement == csDef;
-
-  ByteString bsIntent;
-  if (CPDF_Array* pArray = pIntent->AsArray()) {
-    for (size_t i = 0; i < pArray->GetCount(); i++) {
-      bsIntent = pArray->GetStringAt(i);
-      if (bsIntent == "All" || bsIntent == csElement)
-        return true;
-    }
-    return false;
-  }
-  bsIntent = pIntent->GetString();
-  return bsIntent == "All" || bsIntent == csElement;
-}
-
-CPDF_Dictionary* GetConfig(CPDF_Document* pDoc,
-                           const CPDF_Dictionary* pOCGDict) {
-  ASSERT(pOCGDict);
-  CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDictFor("OCProperties");
-  if (!pOCProperties)
-    return nullptr;
-
-  CPDF_Array* pOCGs = pOCProperties->GetArrayFor("OCGs");
-  if (!pOCGs)
-    return nullptr;
-
-  if (FindGroup(pOCGs, pOCGDict) < 0)
-    return nullptr;
-
-  CPDF_Dictionary* pConfig = pOCProperties->GetDictFor("D");
-  CPDF_Array* pConfigs = pOCProperties->GetArrayFor("Configs");
-  if (!pConfigs)
-    return pConfig;
-
-  for (size_t i = 0; i < pConfigs->GetCount(); i++) {
-    CPDF_Dictionary* pFind = pConfigs->GetDictAt(i);
-    if (pFind && HasIntent(pFind, "View", "View"))
-      return pFind;
-  }
-  return pConfig;
-}
-
-ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) {
-  ByteString csState;
-  switch (eType) {
-    case CPDF_OCContext::Design:
-      csState = "Design";
-      break;
-    case CPDF_OCContext::Print:
-      csState = "Print";
-      break;
-    case CPDF_OCContext::Export:
-      csState = "Export";
-      break;
-    default:
-      csState = "View";
-      break;
-  }
-  return csState;
-}
-
-}  // namespace
-
-CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType)
-    : m_pDocument(pDoc), m_eUsageType(eUsageType) {
-  ASSERT(pDoc);
-}
-
-CPDF_OCContext::~CPDF_OCContext() {}
-
-bool CPDF_OCContext::LoadOCGStateFromConfig(
-    const ByteString& csConfig,
-    const CPDF_Dictionary* pOCGDict) const {
-  CPDF_Dictionary* pConfig = GetConfig(m_pDocument.Get(), 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;
-  }
-  pArray = pConfig->GetArrayFor("OFF");
-  if (pArray) {
-    if (FindGroup(pArray, pOCGDict) >= 0)
-      bState = false;
-  }
-  pArray = pConfig->GetArrayFor("AS");
-  if (!pArray)
-    return bState;
-
-  ByteString csFind = csConfig + "State";
-  for (size_t i = 0; i < pArray->GetCount(); i++) {
-    CPDF_Dictionary* pUsage = pArray->GetDictAt(i);
-    if (!pUsage)
-      continue;
-
-    if (pUsage->GetStringFor("Event", "View") != csConfig)
-      continue;
-
-    CPDF_Array* pOCGs = pUsage->GetArrayFor("OCGs");
-    if (!pOCGs)
-      continue;
-
-    if (FindGroup(pOCGs, pOCGDict) < 0)
-      continue;
-
-    CPDF_Dictionary* pState = pUsage->GetDictFor(csConfig);
-    if (!pState)
-      continue;
-
-    bState = pState->GetStringFor(csFind) != "OFF";
-  }
-  return bState;
-}
-
-bool CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
-  if (!HasIntent(pOCGDict, "View", "View"))
-    return true;
-
-  ByteString csState = GetUsageTypeString(m_eUsageType);
-  CPDF_Dictionary* pUsage = pOCGDict->GetDictFor("Usage");
-  if (pUsage) {
-    CPDF_Dictionary* pState = pUsage->GetDictFor(csState);
-    if (pState) {
-      ByteString csFind = csState + "State";
-      if (pState->KeyExist(csFind))
-        return pState->GetStringFor(csFind) != "OFF";
-    }
-    if (csState != "View") {
-      pState = pUsage->GetDictFor("View");
-      if (pState && pState->KeyExist("ViewState"))
-        return pState->GetStringFor("ViewState") != "OFF";
-    }
-  }
-  return LoadOCGStateFromConfig(csState, pOCGDict);
-}
-
-bool CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) {
-  if (!pOCGDict)
-    return false;
-
-  const auto it = m_OCGStates.find(pOCGDict);
-  if (it != m_OCGStates.end())
-    return it->second;
-
-  bool bState = LoadOCGState(pOCGDict);
-  m_OCGStates[pOCGDict] = bState;
-  return bState;
-}
-
-bool CPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) {
-  for (size_t i = 0; i < pObj->m_ContentMark.CountItems(); ++i) {
-    const CPDF_ContentMarkItem& item = pObj->m_ContentMark.GetItem(i);
-    if (item.GetName() == "OC" &&
-        item.GetParamType() == CPDF_ContentMarkItem::PropertiesDict &&
-        !CheckOCGVisible(item.GetParam())) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool CPDF_OCContext::GetOCGVE(CPDF_Array* pExpression, int nLevel) {
-  if (nLevel > 32 || !pExpression)
-    return false;
-
-  ByteString csOperator = pExpression->GetStringAt(0);
-  if (csOperator == "Not") {
-    CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
-    if (!pOCGObj)
-      return false;
-    if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
-      return !GetOCGVisible(pDict);
-    if (CPDF_Array* pArray = pOCGObj->AsArray())
-      return !GetOCGVE(pArray, nLevel + 1);
-    return false;
-  }
-
-  if (csOperator != "Or" && csOperator != "And")
-    return false;
-
-  bool bValue = false;
-  for (size_t i = 1; i < pExpression->GetCount(); i++) {
-    CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
-    if (!pOCGObj)
-      continue;
-
-    bool bItem = false;
-    if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
-      bItem = GetOCGVisible(pDict);
-    else if (CPDF_Array* pArray = pOCGObj->AsArray())
-      bItem = GetOCGVE(pArray, nLevel + 1);
-
-    if (i == 1) {
-      bValue = bItem;
-    } else {
-      if (csOperator == "Or") {
-        bValue = bValue || bItem;
-      } else {
-        bValue = bValue && bItem;
-      }
-    }
-  }
-  return bValue;
-}
-
-bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) {
-  CPDF_Array* pVE = pOCMDDict->GetArrayFor("VE");
-  if (pVE)
-    return GetOCGVE(pVE, 0);
-
-  ByteString csP = pOCMDDict->GetStringFor("P", "AnyOn");
-  CPDF_Object* pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs");
-  if (!pOCGObj)
-    return true;
-
-  if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
-    return GetOCGVisible(pDict);
-
-  CPDF_Array* pArray = pOCGObj->AsArray();
-  if (!pArray)
-    return true;
-
-  bool bState = (csP == "AllOn" || csP == "AllOff");
-  // At least one entry of OCGs needs to be a valid dictionary for it to be
-  // considered present. See "OCGs" in table 4.49 in the PDF 1.7 spec.
-  bool bValidEntrySeen = false;
-  for (size_t i = 0; i < pArray->GetCount(); i++) {
-    bool bItem = true;
-    CPDF_Dictionary* pItemDict = pArray->GetDictAt(i);
-    if (!pItemDict)
-      continue;
-
-    bValidEntrySeen = true;
-    bItem = GetOCGVisible(pItemDict);
-
-    if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
-      return true;
-    if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
-      return false;
-  }
-
-  return !bValidEntrySeen || bState;
-}
-
-bool CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) {
-  if (!pOCGDict)
-    return true;
-
-  ByteString csType = pOCGDict->GetStringFor("Type", "OCG");
-  if (csType == "OCG")
-    return GetOCGVisible(pOCGDict);
-  return LoadOCMDState(pOCGDict);
-}
diff --git a/core/fpdfdoc/cpdf_occontext.h b/core/fpdfdoc/cpdf_occontext.h
deleted file mode 100644
index 07b41cc..0000000
--- a/core/fpdfdoc/cpdf_occontext.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_CPDF_OCCONTEXT_H_
-#define CORE_FPDFDOC_CPDF_OCCONTEXT_H_
-
-#include <map>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CPDF_Array;
-class CPDF_Dictionary;
-class CPDF_Document;
-class CPDF_PageObject;
-
-class CPDF_OCContext : public Retainable {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  enum UsageType { View = 0, Design, Print, Export };
-
-  bool CheckOCGVisible(const CPDF_Dictionary* pOCGDict);
-  bool CheckObjectVisible(const CPDF_PageObject* pObj);
-
- private:
-  CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType);
-  ~CPDF_OCContext() override;
-
-  bool LoadOCGStateFromConfig(const ByteString& csConfig,
-                              const CPDF_Dictionary* pOCGDict) const;
-  bool LoadOCGState(const CPDF_Dictionary* pOCGDict) const;
-  bool GetOCGVisible(const CPDF_Dictionary* pOCGDict);
-  bool GetOCGVE(CPDF_Array* pExpression, int nLevel);
-  bool LoadOCMDState(const CPDF_Dictionary* pOCMDDict);
-
-  UnownedPtr<CPDF_Document> const m_pDocument;
-  const UsageType m_eUsageType;
-  std::map<const CPDF_Dictionary*, bool> m_OCGStates;
-};
-
-#endif  // CORE_FPDFDOC_CPDF_OCCONTEXT_H_
diff --git a/core/fpdfdoc/cpdf_pagelabel.cpp b/core/fpdfdoc/cpdf_pagelabel.cpp
index f06e401..8985def 100644
--- a/core/fpdfdoc/cpdf_pagelabel.cpp
+++ b/core/fpdfdoc/cpdf_pagelabel.cpp
@@ -51,7 +51,7 @@
 
 WideString GetLabelNumPortion(int num, const ByteString& bsStyle) {
   if (bsStyle.IsEmpty())
-    return L"";
+    return WideString();
   if (bsStyle == "D")
     return WideString::Format(L"%d", num);
   if (bsStyle == "R") {
@@ -68,7 +68,7 @@
   }
   if (bsStyle == "a")
     return MakeLetters(num);
-  return L"";
+  return WideString();
 }
 
 }  // namespace
@@ -89,12 +89,12 @@
   if (!pPDFRoot)
     return {};
 
-  CPDF_Dictionary* pLabels = pPDFRoot->GetDictFor("PageLabels");
+  const CPDF_Dictionary* pLabels = pPDFRoot->GetDictFor("PageLabels");
   if (!pLabels)
     return {};
 
   CPDF_NumberTree numberTree(pLabels);
-  CPDF_Object* pValue = nullptr;
+  const CPDF_Object* pValue = nullptr;
   int n = nPage;
   while (n >= 0) {
     pValue = numberTree.LookupValue(n);
@@ -106,11 +106,11 @@
   WideString label;
   if (pValue) {
     pValue = pValue->GetDirect();
-    if (CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
+    if (const CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
       if (pLabel->KeyExist("P"))
         label += pLabel->GetUnicodeTextFor("P");
 
-      ByteString bsNumberingStyle = pLabel->GetStringFor("S", "");
+      ByteString bsNumberingStyle = pLabel->GetStringFor("S", ByteString());
       int nLabelNum = nPage - n + pLabel->GetIntegerFor("St", 1);
       WideString wsNumPortion = GetLabelNumPortion(nLabelNum, bsNumberingStyle);
       label += wsNumPortion;
@@ -120,30 +120,3 @@
   label = WideString::Format(L"%d", nPage + 1);
   return {label};
 }
-
-int32_t CPDF_PageLabel::GetPageByLabel(const ByteStringView& bsLabel) const {
-  if (!m_pDocument)
-    return -1;
-
-  const CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
-  if (!pPDFRoot)
-    return -1;
-
-  int nPages = m_pDocument->GetPageCount();
-  for (int i = 0; i < nPages; i++) {
-    Optional<WideString> str = GetLabel(i);
-    if (!str.has_value())
-      continue;
-    if (PDF_EncodeText(str.value()).Compare(bsLabel))
-      return i;
-  }
-
-  int nPage = FXSYS_atoi(ByteString(bsLabel).c_str());  // NUL terminate.
-  return nPage > 0 && nPage <= nPages ? nPage : -1;
-}
-
-int32_t CPDF_PageLabel::GetPageByLabel(const WideStringView& wsLabel) const {
-  // TODO(tsepez): check usage of c_str() below.
-  return GetPageByLabel(
-      PDF_EncodeText(wsLabel.unterminated_c_str()).AsStringView());
-}
diff --git a/core/fpdfdoc/cpdf_pagelabel.h b/core/fpdfdoc/cpdf_pagelabel.h
index 4570e97..473485d 100644
--- a/core/fpdfdoc/cpdf_pagelabel.h
+++ b/core/fpdfdoc/cpdf_pagelabel.h
@@ -18,8 +18,6 @@
   ~CPDF_PageLabel();
 
   Optional<WideString> GetLabel(int nPage) const;
-  int32_t GetPageByLabel(const ByteStringView& bsLabel) const;
-  int32_t GetPageByLabel(const WideStringView& wsLabel) const;
 
  private:
   UnownedPtr<CPDF_Document> const m_pDocument;
diff --git a/core/fpdfdoc/cpdf_structelement.cpp b/core/fpdfdoc/cpdf_structelement.cpp
index 13985b8..40477b4 100644
--- a/core/fpdfdoc/cpdf_structelement.cpp
+++ b/core/fpdfdoc/cpdf_structelement.cpp
@@ -6,6 +6,8 @@
 
 #include "core/fpdfdoc/cpdf_structelement.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
@@ -15,12 +17,22 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfdoc/cpdf_structtree.h"
 
-CPDF_StructKid::CPDF_StructKid()
-    : m_Type(Invalid),
-      m_pDict(nullptr),
-      m_PageObjNum(0),
-      m_RefObjNum(0),
-      m_ContentId(0) {}
+namespace {
+
+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;
+}
+
+}  // namespace
+
+CPDF_StructKid::CPDF_StructKid() = default;
 
 CPDF_StructKid::CPDF_StructKid(const CPDF_StructKid& that) = default;
 
@@ -28,47 +40,49 @@
 
 CPDF_StructElement::CPDF_StructElement(CPDF_StructTree* pTree,
                                        CPDF_StructElement* pParent,
-                                       CPDF_Dictionary* pDict)
+                                       const CPDF_Dictionary* pDict)
     : m_pTree(pTree),
       m_pParent(pParent),
       m_pDict(pDict),
-      m_Type(pDict->GetStringFor("S")),
-      m_Title(pDict->GetStringFor("T")) {
-  if (pTree->GetRoleMap()) {
-    ByteString mapped = pTree->GetRoleMap()->GetStringFor(m_Type);
-    if (!mapped.IsEmpty())
-      m_Type = mapped;
-  }
-  LoadKids(pDict);
+      m_Type(GetStructElementType(m_pTree.Get(), m_pDict.Get())) {
+  LoadKids(m_pDict.Get());
 }
 
 CPDF_StructElement::~CPDF_StructElement() = default;
 
+WideString CPDF_StructElement::GetAltText() const {
+  return GetDict()->GetUnicodeTextFor("Alt");
+}
+
+WideString CPDF_StructElement::GetTitle() const {
+  return GetDict()->GetUnicodeTextFor("T");
+}
+
 size_t CPDF_StructElement::CountKids() const {
   return m_Kids.size();
 }
 
 CPDF_StructElement* CPDF_StructElement::GetKidIfElement(size_t index) const {
-  return m_Kids[index].m_Type == CPDF_StructKid::Element
+  return m_Kids[index].m_Type == CPDF_StructKid::kElement
              ? m_Kids[index].m_pElement.Get()
              : nullptr;
 }
 
-void CPDF_StructElement::LoadKids(CPDF_Dictionary* pDict) {
-  CPDF_Object* pObj = pDict->GetObjectFor("Pg");
+void CPDF_StructElement::LoadKids(const CPDF_Dictionary* pDict) {
+  const CPDF_Object* pObj = pDict->GetObjectFor("Pg");
   uint32_t PageObjNum = 0;
-  if (CPDF_Reference* pRef = ToReference(pObj))
+  if (const CPDF_Reference* pRef = ToReference(pObj))
     PageObjNum = pRef->GetRefObjNum();
 
-  CPDF_Object* pKids = pDict->GetDirectObjectFor("K");
+  const CPDF_Object* pKids = pDict->GetDirectObjectFor("K");
   if (!pKids)
     return;
 
   m_Kids.clear();
-  if (CPDF_Array* pArray = pKids->AsArray()) {
-    m_Kids.resize(pArray->GetCount());
-    for (uint32_t i = 0; i < pArray->GetCount(); i++) {
-      CPDF_Object* pKid = pArray->GetDirectObjectAt(i);
+  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]);
     }
     return;
@@ -79,37 +93,37 @@
 }
 
 void CPDF_StructElement::LoadKid(uint32_t PageObjNum,
-                                 CPDF_Object* pKidObj,
+                                 const CPDF_Object* pKidObj,
                                  CPDF_StructKid* pKid) {
-  pKid->m_Type = CPDF_StructKid::Invalid;
+  pKid->m_Type = CPDF_StructKid::kInvalid;
   if (!pKidObj)
     return;
 
   if (pKidObj->IsNumber()) {
-    if (m_pTree->GetPage() && m_pTree->GetPage()->GetObjNum() != PageObjNum)
+    if (m_pTree->GetPage()->GetObjNum() != PageObjNum)
       return;
 
-    pKid->m_Type = CPDF_StructKid::PageContent;
+    pKid->m_Type = CPDF_StructKid::kPageContent;
     pKid->m_ContentId = pKidObj->GetInteger();
     pKid->m_PageObjNum = PageObjNum;
     return;
   }
 
-  CPDF_Dictionary* pKidDict = pKidObj->AsDictionary();
+  const CPDF_Dictionary* pKidDict = pKidObj->AsDictionary();
   if (!pKidDict)
     return;
-  if (CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Pg")))
+  if (const CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Pg")))
     PageObjNum = pRef->GetRefObjNum();
 
   ByteString type = pKidDict->GetStringFor("Type");
-  if ((type == "MCR" || type == "OBJR") && m_pTree->GetPage() &&
+  if ((type == "MCR" || type == "OBJR") &&
       m_pTree->GetPage()->GetObjNum() != PageObjNum) {
     return;
   }
 
   if (type == "MCR") {
-    pKid->m_Type = CPDF_StructKid::StreamContent;
-    CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Stm"));
+    pKid->m_Type = CPDF_StructKid::kStreamContent;
+    const CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Stm"));
     pKid->m_RefObjNum = pRef ? pRef->GetRefObjNum() : 0;
     pKid->m_PageObjNum = PageObjNum;
     pKid->m_ContentId = pKidDict->GetIntegerFor("MCID");
@@ -117,20 +131,14 @@
   }
 
   if (type == "OBJR") {
-    pKid->m_Type = CPDF_StructKid::Object;
-    CPDF_Reference* pObj = ToReference(pKidDict->GetObjectFor("Obj"));
+    pKid->m_Type = CPDF_StructKid::kObject;
+    const CPDF_Reference* pObj = ToReference(pKidDict->GetObjectFor("Obj"));
     pKid->m_RefObjNum = pObj ? pObj->GetRefObjNum() : 0;
     pKid->m_PageObjNum = PageObjNum;
     return;
   }
 
-  pKid->m_Type = CPDF_StructKid::Element;
-  pKid->m_pDict = pKidDict;
-  if (m_pTree->GetPage()) {
-    pKid->m_pElement = nullptr;
-    return;
-  }
-
-  pKid->m_pElement =
-      pdfium::MakeRetain<CPDF_StructElement>(m_pTree.Get(), this, pKidDict);
+  pKid->m_Type = CPDF_StructKid::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 51ee93b..54042a9 100644
--- a/core/fpdfdoc/cpdf_structelement.h
+++ b/core/fpdfdoc/cpdf_structelement.h
@@ -12,7 +12,6 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
@@ -21,27 +20,31 @@
 
 class CPDF_StructKid {
  public:
+  enum Type { kInvalid, kElement, kPageContent, kStreamContent, kObject };
+
   CPDF_StructKid();
   CPDF_StructKid(const CPDF_StructKid& that);
   ~CPDF_StructKid();
 
-  enum { Invalid, Element, PageContent, StreamContent, Object } m_Type;
-
-  RetainPtr<CPDF_StructElement> m_pElement;      // For Element.
-  UnownedPtr<CPDF_Dictionary> m_pDict;           // For Element.
-  uint32_t m_PageObjNum;  // For PageContent, StreamContent, Object.
-  uint32_t m_RefObjNum;   // For StreamContent, Object.
-  uint32_t m_ContentId;   // For PageContent, StreamContent.
+  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 : public Retainable {
+class CPDF_StructElement final : public Retainable {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  const ByteString& GetType() const { return m_Type; }
-  const ByteString& GetTitle() const { return m_Title; }
-  CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
+  ByteString GetType() const { return m_Type; }
+  WideString GetAltText() const;
+  WideString GetTitle() const;
+
+  // Never returns nullptr.
+  const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); }
 
   size_t CountKids() const;
   CPDF_StructElement* GetKidIfElement(size_t index) const;
@@ -50,17 +53,18 @@
  private:
   CPDF_StructElement(CPDF_StructTree* pTree,
                      CPDF_StructElement* pParent,
-                     CPDF_Dictionary* pDict);
+                     const CPDF_Dictionary* pDict);
   ~CPDF_StructElement() override;
 
-  void LoadKids(CPDF_Dictionary* pDict);
-  void LoadKid(uint32_t PageObjNum, CPDF_Object* pObj, CPDF_StructKid* pKid);
+  void LoadKids(const CPDF_Dictionary* pDict);
+  void LoadKid(uint32_t PageObjNum,
+               const CPDF_Object* pKidObj,
+               CPDF_StructKid* pKid);
 
   UnownedPtr<CPDF_StructTree> const m_pTree;
   UnownedPtr<CPDF_StructElement> const m_pParent;
-  UnownedPtr<CPDF_Dictionary> const m_pDict;
-  ByteString m_Type;
-  ByteString m_Title;
+  RetainPtr<const CPDF_Dictionary> const m_pDict;
+  const ByteString m_Type;
   std::vector<CPDF_StructKid> m_Kids;
 };
 
diff --git a/core/fpdfdoc/cpdf_structtree.cpp b/core/fpdfdoc/cpdf_structtree.cpp
index 97db691..8fd43e2 100644
--- a/core/fpdfdoc/cpdf_structtree.cpp
+++ b/core/fpdfdoc/cpdf_structtree.cpp
@@ -7,11 +7,13 @@
 #include "core/fpdfdoc/cpdf_structtree.h"
 
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfdoc/cpdf_numbertree.h"
 #include "core/fpdfdoc/cpdf_structelement.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -37,31 +39,30 @@
 
 CPDF_StructTree::CPDF_StructTree(const CPDF_Document* pDoc)
     : m_pTreeRoot(pDoc->GetRoot()->GetDictFor("StructTreeRoot")),
-      m_pRoleMap(m_pTreeRoot ? m_pTreeRoot->GetDictFor("RoleMap") : nullptr),
-      m_pPage(nullptr) {}
+      m_pRoleMap(m_pTreeRoot ? m_pTreeRoot->GetDictFor("RoleMap") : nullptr) {}
 
-CPDF_StructTree::~CPDF_StructTree() {}
+CPDF_StructTree::~CPDF_StructTree() = default;
 
 void CPDF_StructTree::LoadPageTree(const CPDF_Dictionary* pPageDict) {
-  m_pPage = pPageDict;
+  m_pPage.Reset(pPageDict);
   if (!m_pTreeRoot)
     return;
 
-  CPDF_Object* pKids = m_pTreeRoot->GetDirectObjectFor("K");
+  const CPDF_Object* pKids = m_pTreeRoot->GetDirectObjectFor("K");
   if (!pKids)
     return;
 
   uint32_t dwKids = 0;
   if (pKids->IsDictionary())
     dwKids = 1;
-  else if (CPDF_Array* pArray = pKids->AsArray())
-    dwKids = pArray->GetCount();
+  else if (const CPDF_Array* pArray = pKids->AsArray())
+    dwKids = pArray->size();
   else
     return;
 
   m_Kids.clear();
   m_Kids.resize(dwKids);
-  CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDictFor("ParentTree");
+  const CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDictFor("ParentTree");
   if (!pParentTree)
     return;
 
@@ -70,20 +71,20 @@
   if (parents_id < 0)
     return;
 
-  CPDF_Array* pParentArray = ToArray(parent_tree.LookupValue(parents_id));
+  const CPDF_Array* pParentArray = ToArray(parent_tree.LookupValue(parents_id));
   if (!pParentArray)
     return;
 
-  std::map<CPDF_Dictionary*, RetainPtr<CPDF_StructElement>> element_map;
-  for (size_t i = 0; i < pParentArray->GetCount(); i++) {
-    if (CPDF_Dictionary* pParent = pParentArray->GetDictAt(i))
+  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<CPDF_StructElement> CPDF_StructTree::AddPageNode(
-    CPDF_Dictionary* pDict,
-    std::map<CPDF_Dictionary*, RetainPtr<CPDF_StructElement>>* map,
+    const CPDF_Dictionary* pDict,
+    StructElementMap* map,
     int nLevel) {
   static constexpr int kStructTreeMaxRecursion = 32;
   if (nLevel > kStructTreeMaxRecursion)
@@ -95,7 +96,7 @@
 
   auto pElement = pdfium::MakeRetain<CPDF_StructElement>(this, nullptr, pDict);
   (*map)[pDict] = pElement;
-  CPDF_Dictionary* pParent = pDict->GetDictFor("P");
+  const CPDF_Dictionary* pParent = pDict->GetDictFor("P");
   if (!pParent || pParent->GetStringFor("Type") == "StructTreeRoot") {
     if (!AddTopLevelNode(pDict, pElement))
       map->erase(pDict);
@@ -106,7 +107,7 @@
       AddPageNode(pParent, map, nLevel + 1);
   bool bSave = false;
   for (CPDF_StructKid& kid : *pParentElement->GetKids()) {
-    if (kid.m_Type == CPDF_StructKid::Element && kid.m_pDict == pDict) {
+    if (kid.m_Type == CPDF_StructKid::kElement && kid.m_pDict == pDict) {
       kid.m_pElement = pElement;
       bSave = true;
     }
@@ -117,9 +118,9 @@
 }
 
 bool CPDF_StructTree::AddTopLevelNode(
-    CPDF_Dictionary* pDict,
+    const CPDF_Dictionary* pDict,
     const RetainPtr<CPDF_StructElement>& pElement) {
-  CPDF_Object* pObj = m_pTreeRoot->GetDirectObjectFor("K");
+  const CPDF_Object* pObj = m_pTreeRoot->GetDirectObjectFor("K");
   if (!pObj)
     return false;
 
@@ -129,13 +130,13 @@
     m_Kids[0] = pElement;
   }
 
-  CPDF_Array* pTopKids = pObj->AsArray();
+  const CPDF_Array* pTopKids = pObj->AsArray();
   if (!pTopKids)
     return true;
 
   bool bSave = false;
-  for (size_t i = 0; i < pTopKids->GetCount(); i++) {
-    CPDF_Reference* pKidRef = ToReference(pTopKids->GetObjectAt(i));
+  for (size_t i = 0; i < pTopKids->size(); i++) {
+    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 99342fb..b0eafba 100644
--- a/core/fpdfdoc/cpdf_structtree.h
+++ b/core/fpdfdoc/cpdf_structtree.h
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
@@ -34,17 +33,19 @@
   const CPDF_Dictionary* GetTreeRoot() const { return m_pTreeRoot.Get(); }
 
  private:
+  using StructElementMap =
+      std::map<const CPDF_Dictionary*, RetainPtr<CPDF_StructElement>>;
+
   void LoadPageTree(const CPDF_Dictionary* pPageDict);
-  RetainPtr<CPDF_StructElement> AddPageNode(
-      CPDF_Dictionary* pElement,
-      std::map<CPDF_Dictionary*, RetainPtr<CPDF_StructElement>>* map,
-      int nLevel);
-  bool AddTopLevelNode(CPDF_Dictionary* pDict,
+  RetainPtr<CPDF_StructElement> AddPageNode(const CPDF_Dictionary* pDict,
+                                            StructElementMap* map,
+                                            int nLevel);
+  bool AddTopLevelNode(const CPDF_Dictionary* pDict,
                        const RetainPtr<CPDF_StructElement>& pElement);
 
-  UnownedPtr<const CPDF_Dictionary> const m_pTreeRoot;
-  UnownedPtr<const CPDF_Dictionary> const m_pRoleMap;
-  UnownedPtr<const CPDF_Dictionary> m_pPage;
+  RetainPtr<const CPDF_Dictionary> const m_pTreeRoot;
+  RetainPtr<const CPDF_Dictionary> const m_pRoleMap;
+  RetainPtr<const CPDF_Dictionary> m_pPage;
   std::vector<RetainPtr<CPDF_StructElement>> m_Kids;
 };
 
diff --git a/core/fpdfdoc/cpdf_variabletext.cpp b/core/fpdfdoc/cpdf_variabletext.cpp
index 74ea239..d7d2265 100644
--- a/core/fpdfdoc/cpdf_variabletext.cpp
+++ b/core/fpdfdoc/cpdf_variabletext.cpp
@@ -16,6 +16,7 @@
 #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"
 
@@ -23,7 +24,6 @@
 
 const float kFontScale = 0.001f;
 const uint8_t kReturnLength = 1;
-const float kScalePercent = 0.01f;
 
 const uint8_t gFontSizeSteps[] = {4,  6,  8,   9,   10,  12,  14, 18, 20,
                                   25, 30, 35,  40,  45,  50,  55, 60, 70,
@@ -38,36 +38,37 @@
 
 CPDF_VariableText::Provider::~Provider() {}
 
-int32_t CPDF_VariableText::Provider::GetCharWidth(int32_t nFontIndex,
-                                                  uint16_t word) {
-  if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) {
-    uint32_t charcode = pPDFFont->CharCodeFromUnicode(word);
-    if (charcode != CPDF_Font::kInvalidCharCode)
-      return pPDFFont->GetCharWidthF(charcode);
-  }
-  return 0;
+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) {
-  if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex))
-    return pPDFFont->GetTypeAscent();
-  return 0;
+  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+  return pPDFFont ? pPDFFont->GetTypeAscent() : 0;
 }
 
 int32_t CPDF_VariableText::Provider::GetTypeDescent(int32_t nFontIndex) {
-  if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex))
-    return pPDFFont->GetTypeDescent();
-  return 0;
+  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 (CPDF_Font* pDefFont = m_pFontMap->GetPDFFont(0)) {
+  if (RetainPtr<CPDF_Font> pDefFont = m_pFontMap->GetPDFFont(0)) {
     if (pDefFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
       return 0;
   }
-  if (CPDF_Font* pSysFont = m_pFontMap->GetPDFFont(1)) {
+  if (RetainPtr<CPDF_Font> pSysFont = m_pFontMap->GetPDFFont(1)) {
     if (pSysFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode)
       return 1;
   }
@@ -177,22 +178,9 @@
   return true;
 }
 
-CPDF_VariableText::CPDF_VariableText()
-    : m_nLimitChar(0),
-      m_nCharArray(0),
-      m_bMultiLine(false),
-      m_bLimitWidth(false),
-      m_bAutoFontSize(false),
-      m_nAlignment(0),
-      m_fLineLeading(0.0f),
-      m_fCharSpace(0.0f),
-      m_nHorzScale(100),
-      m_wSubWord(0),
-      m_fFontSize(0.0f),
-      m_bInitialized(false),
-      m_pVTProvider(nullptr) {}
+CPDF_VariableText::CPDF_VariableText() = default;
 
-CPDF_VariableText::~CPDF_VariableText() {}
+CPDF_VariableText::~CPDF_VariableText() = default;
 
 void CPDF_VariableText::Initialize() {
   if (m_bInitialized)
@@ -317,6 +305,7 @@
         break;
       case 0x09:
         word = 0x20;
+        FALLTHROUGH;
       default:
         wp = InsertWord(wp, word, FX_CHARSET_Default);
         break;
@@ -583,45 +572,6 @@
   return m_SectionArray[newplace.nSecIndex]->AddWord(newplace, wordinfo);
 }
 
-bool CPDF_VariableText::GetWordInfo(const CPVT_WordPlace& place,
-                                    CPVT_WordInfo& wordinfo) {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return false;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  if (!pdfium::IndexInBounds(pSection->m_WordArray, place.nWordIndex))
-    return false;
-
-  wordinfo = *pSection->m_WordArray[place.nWordIndex];
-  return true;
-}
-
-bool CPDF_VariableText::SetWordInfo(const CPVT_WordPlace& place,
-                                    const CPVT_WordInfo& wordinfo) {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return false;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  if (!pdfium::IndexInBounds(pSection->m_WordArray, place.nWordIndex))
-    return false;
-
-  *pSection->m_WordArray[place.nWordIndex] = wordinfo;
-  return true;
-}
-
-bool CPDF_VariableText::GetLineInfo(const CPVT_WordPlace& place,
-                                    CPVT_LineInfo& lineinfo) {
-  if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex))
-    return false;
-
-  CSection* pSection = m_SectionArray[place.nSecIndex].get();
-  if (!pdfium::IndexInBounds(pSection->m_LineArray, place.nLineIndex))
-    return false;
-
-  lineinfo = pSection->m_LineArray[place.nLineIndex]->m_LineInfo;
-  return true;
-}
-
 void CPDF_VariableText::SetPlateRect(const CFX_FloatRect& rect) {
   m_rcPlate = rect;
 }
@@ -650,19 +600,15 @@
                                       uint16_t Word,
                                       uint16_t SubWord,
                                       float fCharSpace,
-                                      int32_t nHorzScale,
                                       float fFontSize,
                                       float fWordTail) {
-  return (GetCharWidth(nFontIndex, Word, SubWord) * fFontSize * kFontScale +
-          fCharSpace) *
-             nHorzScale * kScalePercent +
-         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(), GetHorzScale(), GetWordFontSize(),
-                      WordInfo.fWordTail);
+                      GetCharSpace(), GetWordFontSize(), WordInfo.fWordTail);
 }
 
 float CPDF_VariableText::GetLineAscent() {
@@ -917,9 +863,9 @@
   return rcRet;
 }
 
-int32_t CPDF_VariableText::GetCharWidth(int32_t nFontIndex,
-                                        uint16_t Word,
-                                        uint16_t SubWord) {
+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;
@@ -947,7 +893,7 @@
 }
 
 bool CPDF_VariableText::IsLatinWord(uint16_t word) {
-  return m_pVTProvider ? m_pVTProvider->IsLatinWord(word) : false;
+  return m_pVTProvider && m_pVTProvider->IsLatinWord(word);
 }
 
 CPDF_VariableText::Iterator* CPDF_VariableText::GetIterator() {
diff --git a/core/fpdfdoc/cpdf_variabletext.h b/core/fpdfdoc/cpdf_variabletext.h
index a939dcb..aa47015 100644
--- a/core/fpdfdoc/cpdf_variabletext.h
+++ b/core/fpdfdoc/cpdf_variabletext.h
@@ -18,6 +18,7 @@
 #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;
@@ -28,7 +29,6 @@
 
 class CPDF_VariableText {
  public:
-
   class Iterator {
    public:
     explicit Iterator(CPDF_VariableText* pVT);
@@ -53,7 +53,7 @@
     explicit Provider(IPVT_FontMap* pFontMap);
     virtual ~Provider();
 
-    virtual int32_t GetCharWidth(int32_t nFontIndex, uint16_t word);
+    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,
@@ -63,7 +63,7 @@
     virtual int32_t GetDefaultFontIndex();
 
    private:
-    IPVT_FontMap* const m_pFontMap;
+    UnownedPtr<IPVT_FontMap> const m_pFontMap;
   };
 
   CPDF_VariableText();
@@ -108,7 +108,6 @@
   int32_t GetCharArray() const { return m_nCharArray; }
   int32_t GetLimitChar() const { return m_nLimitChar; }
   bool IsMultiLine() const { return m_bMultiLine; }
-  int32_t GetHorzScale() const { return m_nHorzScale; }
   float GetCharSpace() const { return m_fCharSpace; }
   bool IsAutoReturn() const { return m_bLimitWidth; }
 
@@ -153,7 +152,6 @@
                      uint16_t Word,
                      uint16_t SubWord,
                      float fCharSpace,
-                     int32_t nHorzScale,
                      float fFontSize,
                      float fWordTail);
   float GetWordAscent(const CPVT_WordInfo& WordInfo);
@@ -165,7 +163,7 @@
   float GetLineIndent();
 
  private:
-  int32_t GetCharWidth(int32_t nFontIndex, uint16_t Word, uint16_t SubWord);
+  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);
@@ -176,9 +174,6 @@
                          const CPVT_LineInfo& lineinfo);
   CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
                          const CPVT_WordInfo& wordinfo);
-  bool GetWordInfo(const CPVT_WordPlace& place, CPVT_WordInfo& wordinfo);
-  bool SetWordInfo(const CPVT_WordPlace& place, const CPVT_WordInfo& wordinfo);
-  bool GetLineInfo(const CPVT_WordPlace& place, CPVT_LineInfo& lineinfo);
   float GetWordFontSize();
   int32_t GetWordFontIndex(const CPVT_WordInfo& WordInfo);
 
@@ -196,20 +191,19 @@
   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;
-  int32_t m_nLimitChar;
-  int32_t m_nCharArray;
-  bool m_bMultiLine;
-  bool m_bLimitWidth;
-  bool m_bAutoFontSize;
-  int32_t m_nAlignment;
-  float m_fLineLeading;
-  float m_fCharSpace;
-  int32_t m_nHorzScale;
-  uint16_t m_wSubWord;
-  float m_fFontSize;
-  bool m_bInitialized;
-  CPDF_VariableText::Provider* m_pVTProvider;
+  UnownedPtr<CPDF_VariableText::Provider> m_pVTProvider;
   std::unique_ptr<CPDF_VariableText::Iterator> m_pVTIterator;
   CFX_FloatRect m_rcPlate;
   CPVT_FloatRect m_rcContent;
diff --git a/core/fpdfdoc/cpdf_viewerpreferences.cpp b/core/fpdfdoc/cpdf_viewerpreferences.cpp
index e7fb141..be07fdc 100644
--- a/core/fpdfdoc/cpdf_viewerpreferences.cpp
+++ b/core/fpdfdoc/cpdf_viewerpreferences.cpp
@@ -6,26 +6,27 @@
 
 #include "core/fpdfdoc/cpdf_viewerpreferences.h"
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 
-CPDF_ViewerPreferences::CPDF_ViewerPreferences(CPDF_Document* pDoc)
+CPDF_ViewerPreferences::CPDF_ViewerPreferences(const CPDF_Document* pDoc)
     : m_pDoc(pDoc) {}
 
-CPDF_ViewerPreferences::~CPDF_ViewerPreferences() {}
+CPDF_ViewerPreferences::~CPDF_ViewerPreferences() = default;
 
 bool CPDF_ViewerPreferences::IsDirectionR2L() const {
-  CPDF_Dictionary* pDict = GetViewerPreferences();
-  return pDict ? pDict->GetStringFor("Direction") == "R2L" : false;
+  const CPDF_Dictionary* pDict = GetViewerPreferences();
+  return pDict && pDict->GetStringFor("Direction") == "R2L";
 }
 
 bool CPDF_ViewerPreferences::PrintScaling() const {
-  CPDF_Dictionary* pDict = GetViewerPreferences();
-  return pDict ? pDict->GetStringFor("PrintScaling") != "None" : true;
+  const CPDF_Dictionary* pDict = GetViewerPreferences();
+  return !pDict || pDict->GetStringFor("PrintScaling") != "None";
 }
 
 int32_t CPDF_ViewerPreferences::NumCopies() const {
-  CPDF_Dictionary* pDict = GetViewerPreferences();
+  const CPDF_Dictionary* pDict = GetViewerPreferences();
   return pDict ? pDict->GetIntegerFor("NumCopies") : 1;
 }
 
@@ -35,26 +36,24 @@
 }
 
 ByteString CPDF_ViewerPreferences::Duplex() const {
-  CPDF_Dictionary* pDict = GetViewerPreferences();
+  const CPDF_Dictionary* pDict = GetViewerPreferences();
   return pDict ? pDict->GetStringFor("Duplex") : ByteString("None");
 }
 
-bool CPDF_ViewerPreferences::GenericName(const ByteString& bsKey,
-                                         ByteString* bsVal) const {
-  ASSERT(bsVal);
-  CPDF_Dictionary* pDict = GetViewerPreferences();
+Optional<ByteString> CPDF_ViewerPreferences::GenericName(
+    const ByteString& bsKey) const {
+  const CPDF_Dictionary* pDict = GetViewerPreferences();
   if (!pDict)
-    return false;
+    return {};
 
   const CPDF_Name* pName = ToName(pDict->GetObjectFor(bsKey));
   if (!pName)
-    return false;
+    return {};
 
-  *bsVal = pName->GetString();
-  return true;
+  return pName->GetString();
 }
 
 CPDF_Dictionary* CPDF_ViewerPreferences::GetViewerPreferences() const {
-  const CPDF_Dictionary* pDict = m_pDoc->GetRoot();
+  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 a188474..ff2b1c8 100644
--- a/core/fpdfdoc/cpdf_viewerpreferences.h
+++ b/core/fpdfdoc/cpdf_viewerpreferences.h
@@ -10,6 +10,7 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
@@ -17,7 +18,7 @@
 
 class CPDF_ViewerPreferences {
  public:
-  explicit CPDF_ViewerPreferences(CPDF_Document* pDoc);
+  explicit CPDF_ViewerPreferences(const CPDF_Document* pDoc);
   ~CPDF_ViewerPreferences();
 
   bool IsDirectionR2L() const;
@@ -26,15 +27,13 @@
   CPDF_Array* PrintPageRange() const;
   ByteString Duplex() const;
 
-  // Gets the entry for |bsKey|. If the entry exists and it is of type name,
-  // then this method writes the value into |bsVal| and returns true. Otherwise
-  // returns false and |bsVal| is untouched. |bsVal| must not be NULL.
-  bool GenericName(const ByteString& bsKey, ByteString* bsVal) const;
+  // Gets the entry for |bsKey|.
+  Optional<ByteString> GenericName(const ByteString& bsKey) const;
 
  private:
   CPDF_Dictionary* GetViewerPreferences() const;
 
-  UnownedPtr<CPDF_Document> const m_pDoc;
+  UnownedPtr<const CPDF_Document> const m_pDoc;
 };
 
 #endif  // CORE_FPDFDOC_CPDF_VIEWERPREFERENCES_H_
diff --git a/core/fpdfdoc/cpvt_floatrect.h b/core/fpdfdoc/cpvt_floatrect.h
index c5eb220..d302ed0 100644
--- a/core/fpdfdoc/cpvt_floatrect.h
+++ b/core/fpdfdoc/cpvt_floatrect.h
@@ -9,26 +9,18 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 
-class CPVT_FloatRect : public CFX_FloatRect {
+class CPVT_FloatRect final : public CFX_FloatRect {
  public:
-  CPVT_FloatRect() { left = top = right = bottom = 0.0f; }
+  CPVT_FloatRect() = default;
 
   CPVT_FloatRect(float other_left,
                  float other_top,
                  float other_right,
-                 float other_bottom) {
-    left = other_left;
-    top = other_top;
-    right = other_right;
-    bottom = other_bottom;
-  }
+                 float other_bottom)
+      : CFX_FloatRect(other_left, other_bottom, other_right, other_top) {}
 
-  explicit CPVT_FloatRect(const CFX_FloatRect& rect) {
-    left = rect.left;
-    top = rect.top;
-    right = rect.right;
-    bottom = rect.bottom;
-  }
+  explicit CPVT_FloatRect(const CFX_FloatRect& rect)
+      : CFX_FloatRect(rect.left, rect.bottom, rect.right, rect.top) {}
 
   float Height() const {
     if (top > bottom)
diff --git a/core/fpdfdoc/cpvt_fontmap.cpp b/core/fpdfdoc/cpvt_fontmap.cpp
index aaf8661..b21bd0d 100644
--- a/core/fpdfdoc/cpvt_fontmap.cpp
+++ b/core/fpdfdoc/cpvt_fontmap.cpp
@@ -10,13 +10,14 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfdoc/cpdf_interform.h"
+#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"
 
 CPVT_FontMap::CPVT_FontMap(CPDF_Document* pDoc,
                            CPDF_Dictionary* pResDict,
-                           CPDF_Font* pDefFont,
+                           const RetainPtr<CPDF_Font>& pDefFont,
                            const ByteString& sDefFontAlias)
     : m_pDocument(pDoc),
       m_pResDict(pResDict),
@@ -25,35 +26,39 @@
 
 CPVT_FontMap::~CPVT_FontMap() {}
 
-CPDF_Font* CPVT_FontMap::GetAnnotSysPDFFont(CPDF_Document* pDoc,
-                                            const CPDF_Dictionary* pResDict,
-                                            ByteString* sSysFontAlias) {
+// static
+RetainPtr<CPDF_Font> CPVT_FontMap::GetAnnotSysPDFFont(
+    CPDF_Document* pDoc,
+    CPDF_Dictionary* pResDict,
+    ByteString* sSysFontAlias) {
   if (!pDoc || !pResDict)
     return nullptr;
 
   CPDF_Dictionary* pFormDict = pDoc->GetRoot()->GetDictFor("AcroForm");
-  CPDF_Font* pPDFFont = AddNativeInterFormFont(pFormDict, pDoc, sSysFontAlias);
+  RetainPtr<CPDF_Font> pPDFFont =
+      AddNativeInteractiveFormFont(pFormDict, pDoc, sSysFontAlias);
   if (!pPDFFont)
     return nullptr;
 
   CPDF_Dictionary* pFontList = pResDict->GetDictFor("Font");
-  if (pFontList && !pFontList->KeyExist(*sSysFontAlias)) {
+  if (ValidateFontResourceDict(pFontList) &&
+      !pFontList->KeyExist(*sSysFontAlias)) {
     pFontList->SetNewFor<CPDF_Reference>(*sSysFontAlias, pDoc,
                                          pPDFFont->GetFontDict()->GetObjNum());
   }
   return pPDFFont;
 }
 
-CPDF_Font* CPVT_FontMap::GetPDFFont(int32_t nFontIndex) {
+RetainPtr<CPDF_Font> CPVT_FontMap::GetPDFFont(int32_t nFontIndex) {
   switch (nFontIndex) {
     case 0:
-      return m_pDefFont.Get();
+      return m_pDefFont;
     case 1:
       if (!m_pSysFont) {
         m_pSysFont = GetAnnotSysPDFFont(m_pDocument.Get(), m_pResDict.Get(),
                                         &m_sSysFontAlias);
       }
-      return m_pSysFont.Get();
+      return m_pSysFont;
     default:
       return nullptr;
   }
diff --git a/core/fpdfdoc/cpvt_fontmap.h b/core/fpdfdoc/cpvt_fontmap.h
index a0b94cf..a85afcf 100644
--- a/core/fpdfdoc/cpvt_fontmap.h
+++ b/core/fpdfdoc/cpvt_fontmap.h
@@ -11,22 +11,23 @@
 
 #include "core/fpdfdoc/ipvt_fontmap.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Document;
 class CPDF_Dictionary;
 class CPDF_Font;
 
-class CPVT_FontMap : public IPVT_FontMap {
+class CPVT_FontMap final : public IPVT_FontMap {
  public:
   CPVT_FontMap(CPDF_Document* pDoc,
                CPDF_Dictionary* pResDict,
-               CPDF_Font* pDefFont,
+               const RetainPtr<CPDF_Font>& pDefFont,
                const ByteString& sDefFontAlias);
   ~CPVT_FontMap() override;
 
   // IPVT_FontMap:
-  CPDF_Font* GetPDFFont(int32_t nFontIndex) override;
+  RetainPtr<CPDF_Font> GetPDFFont(int32_t nFontIndex) override;
   ByteString GetPDFFontAlias(int32_t nFontIndex) override;
   int32_t GetWordFontIndex(uint16_t word,
                            int32_t charset,
@@ -34,15 +35,15 @@
   int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) override;
   int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) override;
 
-  static CPDF_Font* GetAnnotSysPDFFont(CPDF_Document* pDoc,
-                                       const CPDF_Dictionary* pResDict,
-                                       ByteString* sSysFontAlias);
+  static RetainPtr<CPDF_Font> GetAnnotSysPDFFont(CPDF_Document* pDoc,
+                                                 CPDF_Dictionary* pResDict,
+                                                 ByteString* sSysFontAlias);
 
  private:
   UnownedPtr<CPDF_Document> const m_pDocument;
-  UnownedPtr<CPDF_Dictionary> const m_pResDict;
-  UnownedPtr<CPDF_Font> const m_pDefFont;
-  UnownedPtr<CPDF_Font> m_pSysFont;
+  RetainPtr<CPDF_Dictionary> const m_pResDict;
+  RetainPtr<CPDF_Font> const m_pDefFont;
+  RetainPtr<CPDF_Font> m_pSysFont;
   const ByteString m_sDefFontAlias;
   ByteString m_sSysFontAlias;
 };
diff --git a/core/fpdfdoc/cpvt_generateap.cpp b/core/fpdfdoc/cpvt_generateap.cpp
index 1b94f7e..2b90365 100644
--- a/core/fpdfdoc/cpvt_generateap.cpp
+++ b/core/fpdfdoc/cpvt_generateap.cpp
@@ -11,7 +11,10 @@
 #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"
@@ -19,16 +22,22 @@
 #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_simple_parser.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfapi/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) {}
@@ -38,7 +47,7 @@
   int32_t nPhase;
 };
 
-namespace {
+enum class PaintOperation { STROKE, FILL };
 
 ByteString GetPDFWordString(IPVT_FontMap* pFontMap,
                             int32_t nFontIndex,
@@ -48,14 +57,14 @@
     return ByteString::Format("%c", SubWord);
 
   if (!pFontMap)
-    return "";
+    return ByteString();
 
-  CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex);
+  RetainPtr<CPDF_Font> pPDFFont = pFontMap->GetPDFFont(nFontIndex);
   if (!pPDFFont)
-    return "";
+    return ByteString();
 
-  if (pPDFFont->GetBaseFont().Compare("Symbol") == 0 ||
-      pPDFFont->GetBaseFont().Compare("ZapfDingbats") == 0) {
+  if (pPDFFont->GetBaseFontName().Compare("Symbol") == 0 ||
+      pPDFFont->GetBaseFontName().Compare("ZapfDingbats") == 0) {
     return ByteString::Format("%c", Word);
   }
 
@@ -63,13 +72,14 @@
   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 "";
+  return ByteString();
 }
 
 ByteString GetFontSetString(IPVT_FontMap* pFontMap,
@@ -302,7 +312,7 @@
                                      const CFX_Color& crDefaultColor,
                                      PaintOperation nOperation) {
   if (pColor) {
-    CFX_Color color = CFX_Color::ParseColor(*pColor);
+    CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor);
     return GenerateColorAP(color, nOperation);
   }
 
@@ -310,27 +320,29 @@
 }
 
 float GetBorderWidth(const CPDF_Dictionary& pAnnotDict) {
-  if (CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
+  if (const CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
     if (pBorderStyleDict->KeyExist("W"))
       return pBorderStyleDict->GetNumberFor("W");
   }
 
-  if (CPDF_Array* pBorderArray = pAnnotDict.GetArrayFor("Border")) {
-    if (pBorderArray->GetCount() > 2)
+  if (const CPDF_Array* pBorderArray =
+          pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) {
+    if (pBorderArray->size() > 2)
       return pBorderArray->GetNumberAt(2);
   }
 
   return 1;
 }
 
-CPDF_Array* GetDashArray(const CPDF_Dictionary& pAnnotDict) {
-  if (CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
+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 (CPDF_Array* pBorderArray = pAnnotDict.GetArrayFor("Border")) {
-    if (pBorderArray->GetCount() == 4)
+  if (const CPDF_Array* pBorderArray =
+          pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) {
+    if (pBorderArray->size() == 4)
       return pBorderArray->GetArrayAt(3);
   }
 
@@ -338,12 +350,12 @@
 }
 
 ByteString GetDashPatternString(const CPDF_Dictionary& pAnnotDict) {
-  CPDF_Array* pDashArray = GetDashArray(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->GetCount(), 10);
+  size_t pDashArrayCount = std::min<size_t>(pDashArray->size(), 10);
   std::ostringstream sDashStream;
 
   sDashStream << "[";
@@ -356,17 +368,17 @@
 
 ByteString GetPopupContentsString(CPDF_Document* pDoc,
                                   const CPDF_Dictionary& pAnnotDict,
-                                  CPDF_Font* pDefFont,
+                                  const RetainPtr<CPDF_Font>& pDefFont,
                                   const ByteString& sFontName) {
-  WideString swValue(pAnnotDict.GetUnicodeTextFor("T"));
+  WideString swValue(pAnnotDict.GetUnicodeTextFor(pdfium::form_fields::kT));
   swValue += L'\n';
-  swValue += pAnnotDict.GetUnicodeTextFor("Contents");
+  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("Rect"));
+  vt.SetPlateRect(pAnnotDict.GetRectFor(pdfium::annotation::kRect));
   vt.SetFontSize(12);
   vt.SetAutoReturn(true);
   vt.SetMultiLine(true);
@@ -390,17 +402,16 @@
   return ByteString(sAppStream);
 }
 
-std::unique_ptr<CPDF_Dictionary> GenerateResourceFontDict(
+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", "Helvetica");
+  pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
   pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
 
-  auto pResourceFontDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
+  auto pResourceFontDict = pDoc->New<CPDF_Dictionary>();
   pResourceFontDict->SetNewFor<CPDF_Reference>(sFontDictName, pDoc,
                                                pFontDict->GetObjNum());
   return pResourceFontDict;
@@ -462,64 +473,64 @@
   return ByteString(sAppStream);
 }
 
-std::unique_ptr<CPDF_Dictionary> GenerateExtGStateDict(
+RetainPtr<CPDF_Dictionary> GenerateExtGStateDict(
     const CPDF_Dictionary& pAnnotDict,
     const ByteString& sExtGSDictName,
     const ByteString& sBlendMode) {
   auto pGSDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
-  pGSDict->SetNewFor<CPDF_String>("Type", "ExtGState", false);
+      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_String>("BM", sBlendMode, false);
+  pGSDict->SetNewFor<CPDF_Name>("BM", sBlendMode);
 
   auto pExtGStateDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
-  pExtGStateDict->SetFor(sExtGSDictName, std::move(pGSDict));
+      pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
+  pExtGStateDict->SetFor(sExtGSDictName, pGSDict);
   return pExtGStateDict;
 }
 
-std::unique_ptr<CPDF_Dictionary> GenerateResourceDict(
+RetainPtr<CPDF_Dictionary> GenerateResourceDict(
     CPDF_Document* pDoc,
-    std::unique_ptr<CPDF_Dictionary> pExtGStateDict,
-    std::unique_ptr<CPDF_Dictionary> pResourceFontDict) {
-  auto pResourceDict =
-      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
+    RetainPtr<CPDF_Dictionary> pExtGStateDict,
+    RetainPtr<CPDF_Dictionary> pResourceFontDict) {
+  auto pResourceDict = pDoc->New<CPDF_Dictionary>();
   if (pExtGStateDict)
-    pResourceDict->SetFor("ExtGState", std::move(pExtGStateDict));
+    pResourceDict->SetFor("ExtGState", pExtGStateDict);
   if (pResourceFontDict)
-    pResourceDict->SetFor("Font", std::move(pResourceFontDict));
+    pResourceDict->SetFor("Font", pResourceFontDict);
   return pResourceDict;
 }
 
 void GenerateAndSetAPDict(CPDF_Document* pDoc,
                           CPDF_Dictionary* pAnnotDict,
                           std::ostringstream* psAppStream,
-                          std::unique_ptr<CPDF_Dictionary> pResourceDict,
+                          RetainPtr<CPDF_Dictionary> pResourceDict,
                           bool bIsTextMarkupAnnotation) {
   CPDF_Stream* pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
-  pNormalStream->SetData(psAppStream);
+  pNormalStream->SetDataFromStringstream(psAppStream);
 
-  CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor("AP");
+  CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
   if (!pAPDict)
-    pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
+    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_String>("Subtype", "Form", false);
+  pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
+  pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
   pStreamDict->SetMatrixFor("Matrix", CFX_Matrix());
 
   CFX_FloatRect rect = bIsTextMarkupAnnotation
-                           ? CPDF_Annot::RectFromQuadPoints(pAnnotDict)
-                           : pAnnotDict->GetRectFor("Rect");
+                           ? CPDF_Annot::BoundingRectFromQuadPoints(pAnnotDict)
+                           : pAnnotDict->GetRectFor(pdfium::annotation::kRect);
   pStreamDict->SetRectFor("BBox", rect);
-  pStreamDict->SetFor("Resources", std::move(pResourceDict));
+  pStreamDict->SetFor("Resources", pResourceDict);
 }
 
 bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
@@ -531,9 +542,9 @@
   sAppStream << GetColorStringWithDefault(
       pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL);
 
-  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
-                                          CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                          PaintOperation::STROKE);
+  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;
@@ -543,7 +554,7 @@
     sAppStream << GetDashPatternString(*pAnnotDict);
   }
 
-  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
   rect.Normalize();
 
   if (bIsStrokeRect) {
@@ -599,17 +610,22 @@
   ByteString sExtGSDictName = "GS";
   sAppStream << "/" << sExtGSDictName << " gs ";
 
-  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
-                                          CFX_Color(CFX_Color::kRGB, 1, 1, 0),
-                                          PaintOperation::FILL);
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
+      CFX_Color(CFX_Color::kRGB, 1, 1, 0), PaintOperation::FILL);
 
-  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
-  rect.Normalize();
+  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
 
-  sAppStream << rect.left << " " << rect.top << " m " << rect.right << " "
-             << rect.top << " l " << rect.right << " " << rect.bottom << " l "
-             << rect.left << " " << rect.bottom << " l "
-             << "h f\n";
+      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");
@@ -636,28 +652,28 @@
   ByteString sExtGSDictName = "GS";
   sAppStream << "/" << sExtGSDictName << " gs ";
 
-  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
-                                          CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                          PaintOperation::STROKE);
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
+      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
 
   sAppStream << fBorderWidth << " w ";
   sAppStream << GetDashPatternString(*pAnnotDict);
 
   // Set inflated rect as a new rect because paths near the border with large
   // width should not be clipped to the original rect.
-  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
   rect.Inflate(fBorderWidth / 2, fBorderWidth / 2);
-  pAnnotDict->SetRectFor("Rect", rect);
+  pAnnotDict->SetRectFor(pdfium::annotation::kRect, rect);
 
-  for (size_t i = 0; i < pInkList->GetCount(); i++) {
+  for (size_t i = 0; i < pInkList->size(); i++) {
     CPDF_Array* pInkCoordList = pInkList->GetArrayAt(i);
-    if (!pInkCoordList || pInkCoordList->GetCount() < 2)
+    if (!pInkCoordList || pInkCoordList->size() < 2)
       continue;
 
     sAppStream << pInkCoordList->GetNumberAt(0) << " "
                << pInkCoordList->GetNumberAt(1) << " m ";
 
-    for (size_t j = 0; j < pInkCoordList->GetCount() - 1; j += 2) {
+    for (size_t j = 0; j < pInkCoordList->size() - 1; j += 2) {
       sAppStream << pInkCoordList->GetNumberAt(j) << " "
                  << pInkCoordList->GetNumberAt(j + 1) << " l ";
     }
@@ -679,11 +695,11 @@
   ByteString sExtGSDictName = "GS";
   sAppStream << "/" << sExtGSDictName << " gs ";
 
-  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
+  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("Rect", noteRect);
+  pAnnotDict->SetRectFor(pdfium::annotation::kRect, noteRect);
 
   sAppStream << GenerateTextSymbolAP(noteRect);
 
@@ -701,17 +717,22 @@
   ByteString sExtGSDictName = "GS";
   sAppStream << "/" << sExtGSDictName << " gs ";
 
-  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
-                                          CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                          PaintOperation::STROKE);
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
+      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
 
-  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
-  rect.Normalize();
-
-  float fLineWidth = 1.0;
-  sAppStream << fLineWidth << " w " << rect.left << " "
-             << rect.bottom + fLineWidth << " m " << rect.right << " "
-             << rect.bottom + fLineWidth << " l S\n";
+  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    static constexpr float kLineWidth = 1.0f;
+    sAppStream << kLineWidth << " w ";
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
+      sAppStream << rect.left << " " << rect.bottom + kLineWidth << " m "
+                 << rect.right << " " << rect.bottom + kLineWidth << " l S\n";
+    }
+  }
 
   auto pExtGStateDict =
       GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
@@ -735,7 +756,7 @@
   const float fBorderWidth = 1;
   sAppStream << fBorderWidth << " w\n";
 
-  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
   rect.Normalize();
   rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
 
@@ -743,15 +764,18 @@
              << rect.Height() << " re b\n";
 
   ByteString sFontName = "FONT";
-  auto pResourceFontDict = GenerateResourceFontDict(pDoc, sFontName);
-  CPDF_Font* pDefFont = pDoc->LoadFont(pResourceFontDict.get());
+  RetainPtr<CPDF_Dictionary> pResourceFontDict =
+      GenerateResourceFontDict(pDoc, sFontName);
+
+  auto* pData = CPDF_DocPageData::FromDocument(pDoc);
+  RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pResourceFontDict.Get());
   if (!pDefFont)
     return false;
 
-  auto pExtGStateDict =
+  RetainPtr<CPDF_Dictionary> pExtGStateDict =
       GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
-  auto pResourceDict = GenerateResourceDict(pDoc, std::move(pResourceFontDict),
-                                            std::move(pExtGStateDict));
+  RetainPtr<CPDF_Dictionary> pResourceDict = GenerateResourceDict(
+      pDoc, std::move(pExtGStateDict), std::move(pResourceFontDict));
 
   sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, pDefFont, sFontName);
   GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
@@ -768,9 +792,9 @@
   sAppStream << GetColorStringWithDefault(
       pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL);
 
-  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
-                                          CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                          PaintOperation::STROKE);
+  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;
@@ -780,7 +804,7 @@
     sAppStream << GetDashPatternString(*pAnnotDict);
   }
 
-  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
+  CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
   rect.Normalize();
 
   if (bIsStrokeRect) {
@@ -790,7 +814,7 @@
     rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
   }
 
-  bool bIsFillRect = pInteriorColor && (pInteriorColor->GetCount() > 0);
+  bool bIsFillRect = pInteriorColor && (pInteriorColor->size() > 0);
 
   sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
              << rect.Height() << " re "
@@ -810,40 +834,42 @@
   ByteString sExtGSDictName = "GS";
   sAppStream << "/" << sExtGSDictName << " gs ";
 
-  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
-                                          CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                          PaintOperation::STROKE);
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
+      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
 
-  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
-  rect.Normalize();
+  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    static constexpr float kLineWidth = 1.0f;
+    static constexpr float kDelta = 2.0f;
+    sAppStream << kLineWidth << " w ";
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
 
-  float fLineWidth = 1.0;
-  sAppStream << fLineWidth << " w ";
+      const float fTop = rect.bottom + kDelta;
+      const float fBottom = rect.bottom;
+      sAppStream << rect.left << " " << fTop << " m ";
 
-  const float fDelta = 2.0;
-  const float fTop = rect.bottom + fDelta;
-  const float fBottom = rect.bottom;
+      float fX = rect.left + kDelta;
+      bool isUpwards = false;
+      while (fX < rect.right) {
+        sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
+        fX += kDelta;
+        isUpwards = !isUpwards;
+      }
 
-  sAppStream << rect.left << " " << fTop << " m ";
+      float fRemainder = rect.right - (fX - kDelta);
+      if (isUpwards)
+        sAppStream << rect.right << " " << fBottom + fRemainder << " l ";
+      else
+        sAppStream << rect.right << " " << fTop - fRemainder << " l ";
 
-  float fX = rect.left + fDelta;
-  bool isUpwards = false;
-
-  while (fX < rect.right) {
-    sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
-
-    fX += fDelta;
-    isUpwards = !isUpwards;
+      sAppStream << "S\n";
+    }
   }
 
-  float fRemainder = rect.right - (fX - fDelta);
-  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 =
@@ -858,17 +884,23 @@
   ByteString sExtGSDictName = "GS";
   sAppStream << "/" << sExtGSDictName << " gs ";
 
-  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
-                                          CFX_Color(CFX_Color::kRGB, 0, 0, 0),
-                                          PaintOperation::STROKE);
+  sAppStream << GetColorStringWithDefault(
+      pAnnotDict->GetArrayFor(pdfium::annotation::kC),
+      CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
 
-  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
-  rect.Normalize();
+  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
+  if (pArray) {
+    static constexpr float kLineWidth = 1.0f;
+    size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
+    for (size_t i = 0; i < nQuadPointCount; ++i) {
+      CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
+      rect.Normalize();
 
-  float fLineWidth = 1.0;
-  float fY = (rect.top + rect.bottom) / 2;
-  sAppStream << fLineWidth << " w " << rect.left << " " << fY << " m "
-             << rect.right << " " << fY << " l S\n";
+      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");
@@ -882,62 +914,60 @@
 }  // namespace
 
 // static
-void CPVT_GenerateAP::GenerateFormAP(Type type,
-                                     CPDF_Document* pDoc,
-                                     CPDF_Dictionary* pAnnotDict) {
-  const CPDF_Dictionary* pRootDict = pDoc->GetRoot();
+void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc,
+                                     CPDF_Dictionary* pAnnotDict,
+                                     FormType type) {
+  CPDF_Dictionary* pRootDict = pDoc->GetRoot();
   if (!pRootDict)
     return;
 
-  const CPDF_Dictionary* pFormDict = pRootDict->GetDictFor("AcroForm");
+  CPDF_Dictionary* pFormDict = pRootDict->GetDictFor("AcroForm");
   if (!pFormDict)
     return;
 
   ByteString DA;
-  if (CPDF_Object* pDAObj = FPDF_GetFieldAttr(pAnnotDict, "DA"))
+  if (CPDF_Object* pDAObj = CPDF_FormField::GetFieldAttr(pAnnotDict, "DA"))
     DA = pDAObj->GetString();
   if (DA.IsEmpty())
     DA = pFormDict->GetStringFor("DA");
   if (DA.IsEmpty())
     return;
 
-  CPDF_SimpleParser syntax(DA.AsStringView());
-  syntax.FindTagParamFromStart("Tf", 2);
-  ByteString sFontName(syntax.GetWord());
-  sFontName = PDF_NameDecode(sFontName);
-  if (sFontName.IsEmpty())
+  CPDF_DefaultAppearance appearance(DA);
+
+  float fFontSize = 0;
+  Optional<ByteString> font = appearance.GetFont(&fFontSize);
+  if (!font)
     return;
 
-  float fFontSize = FX_atof(syntax.GetWord());
-  CFX_Color crText = CFX_Color::ParseColor(DA);
+  ByteString font_name = *font;
+  CFX_Color crText = fpdfdoc::CFXColorFromString(DA);
   CPDF_Dictionary* pDRDict = pFormDict->GetDictFor("DR");
   if (!pDRDict)
     return;
 
   CPDF_Dictionary* pDRFontDict = pDRDict->GetDictFor("Font");
-  if (!pDRFontDict)
+  if (!ValidateFontResourceDict(pDRFontDict))
     return;
 
-  CPDF_Dictionary* pFontDict =
-      pDRFontDict->GetDictFor(sFontName.Right(sFontName.GetLength() - 1));
+  CPDF_Dictionary* pFontDict = pDRFontDict->GetDictFor(font_name);
   if (!pFontDict) {
     pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
     pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
     pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
-    pFontDict->SetNewFor<CPDF_Name>("BaseFont", "Helvetica");
+    pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
     pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
-    pDRFontDict->SetNewFor<CPDF_Reference>(
-        sFontName.Right(sFontName.GetLength() - 1), pDoc,
-        pFontDict->GetObjNum());
+    pDRFontDict->SetNewFor<CPDF_Reference>(font_name, pDoc,
+                                           pFontDict->GetObjNum());
   }
-  CPDF_Font* pDefFont = pDoc->LoadFont(pFontDict);
+  auto* pData = CPDF_DocPageData::FromDocument(pDoc);
+  RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pFontDict);
   if (!pDefFont)
     return;
 
-  CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor("Rect");
-  int32_t nRotate = 0;
-  if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK"))
-    nRotate = pMKDict->GetIntegerFor("R");
+  CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+  CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK");
+  int32_t nRotate = pMKDict ? pMKDict->GetIntegerFor("R") : 0;
 
   CFX_FloatRect rcBBox;
   CFX_Matrix matrix;
@@ -1005,11 +1035,11 @@
   }
   CFX_Color crBorder;
   CFX_Color crBG;
-  if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK")) {
+  if (pMKDict) {
     if (CPDF_Array* pArray = pMKDict->GetArrayFor("BC"))
-      crBorder = CFX_Color::ParseColor(*pArray);
+      crBorder = fpdfdoc::CFXColorFromArray(*pArray);
     if (CPDF_Array* pArray = pMKDict->GetArrayFor("BG"))
-      crBG = CFX_Color::ParseColor(*pArray);
+      crBG = fpdfdoc::CFXColorFromArray(*pArray);
   }
   std::ostringstream sAppStream;
   ByteString sBG = GenerateColorAP(crBG, PaintOperation::FILL);
@@ -1030,9 +1060,9 @@
                     rcBBox.right - fBorderWidth, rcBBox.top - fBorderWidth);
   rcBody.Normalize();
 
-  CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor("AP");
+  CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
   if (!pAPDict)
-    pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
+    pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
 
   CPDF_Stream* pNormalStream = pAPDict->GetStreamFor("N");
   if (!pNormalStream) {
@@ -1041,41 +1071,41 @@
   }
   CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
   if (pStreamDict) {
-    pStreamDict->SetMatrixFor("Matrix", matrix);
-    pStreamDict->SetRectFor("BBox", rcBBox);
     CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
     if (pStreamResList) {
       CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font");
-      if (!pStreamResFontList)
+      if (pStreamResFontList) {
+        if (!ValidateFontResourceDict(pStreamResFontList))
+          return;
+      } else {
         pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
-      if (!pStreamResFontList->KeyExist(sFontName)) {
-        pStreamResFontList->SetNewFor<CPDF_Reference>(sFontName, pDoc,
+      }
+      if (!pStreamResFontList->KeyExist(font_name)) {
+        pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
                                                       pFontDict->GetObjNum());
       }
     } else {
       pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
-      pStreamResList = pStreamDict->GetDictFor("Resources");
     }
+    pStreamDict->SetMatrixFor("Matrix", matrix);
+    pStreamDict->SetRectFor("BBox", rcBBox);
   }
   switch (type) {
     case CPVT_GenerateAP::kTextField: {
-      WideString swValue =
-          FPDF_GetFieldAttr(pAnnotDict, "V")
-              ? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
-              : WideString();
-      int32_t nAlign = FPDF_GetFieldAttr(pAnnotDict, "Q")
-                           ? FPDF_GetFieldAttr(pAnnotDict, "Q")->GetInteger()
-                           : 0;
-      uint32_t dwFlags = FPDF_GetFieldAttr(pAnnotDict, "Ff")
-                             ? FPDF_GetFieldAttr(pAnnotDict, "Ff")->GetInteger()
-                             : 0;
-      uint32_t dwMaxLen =
-          FPDF_GetFieldAttr(pAnnotDict, "MaxLen")
-              ? FPDF_GetFieldAttr(pAnnotDict, "MaxLen")->GetInteger()
-              : 0;
+      const CPDF_Object* pV =
+          CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV);
+      WideString swValue = pV ? pV->GetUnicodeText() : WideString();
+      const CPDF_Object* pQ = CPDF_FormField::GetFieldAttr(pAnnotDict, "Q");
+      int32_t nAlign = pQ ? pQ->GetInteger() : 0;
+      const CPDF_Object* pFf =
+          CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFf);
+      uint32_t dwFlags = pFf ? pFf->GetInteger() : 0;
+      const CPDF_Object* pMaxLen =
+          CPDF_FormField::GetFieldAttr(pAnnotDict, "MaxLen");
+      uint32_t dwMaxLen = pMaxLen ? pMaxLen->GetInteger() : 0;
       CPVT_FontMap map(
           pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
-          pDefFont, sFontName.Right(sFontName.GetLength() - 1));
+          pDefFont, font_name);
       CPDF_VariableText::Provider prd(&map);
       CPDF_VariableText vt;
       vt.SetProvider(&prd);
@@ -1130,13 +1160,12 @@
       break;
     }
     case CPVT_GenerateAP::kComboBox: {
-      WideString swValue =
-          FPDF_GetFieldAttr(pAnnotDict, "V")
-              ? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
-              : WideString();
+      const CPDF_Object* pV =
+          CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV);
+      WideString swValue = pV ? pV->GetUnicodeText() : WideString();
       CPVT_FontMap map(
           pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
-          pDefFont, sFontName.Right(sFontName.GetLength() - 1));
+          pDefFont, font_name);
       CPDF_VariableText::Provider prd(&map);
       CPDF_VariableText vt;
       vt.SetProvider(&prd);
@@ -1204,29 +1233,33 @@
     case CPVT_GenerateAP::kListBox: {
       CPVT_FontMap map(
           pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
-          pDefFont, sFontName.Right(sFontName.GetLength() - 1));
+          pDefFont, font_name);
       CPDF_VariableText::Provider prd(&map);
-      CPDF_Array* pOpts = ToArray(FPDF_GetFieldAttr(pAnnotDict, "Opt"));
-      CPDF_Array* pSels = ToArray(FPDF_GetFieldAttr(pAnnotDict, "I"));
-      CPDF_Object* pTi = FPDF_GetFieldAttr(pAnnotDict, "TI");
+      CPDF_Array* pOpts =
+          ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "Opt"));
+      CPDF_Array* pSels =
+          ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "I"));
+      CPDF_Object* pTi = CPDF_FormField::GetFieldAttr(pAnnotDict, "TI");
       int32_t nTop = pTi ? pTi->GetInteger() : 0;
       std::ostringstream sBody;
       if (pOpts) {
         float fy = rcBody.top;
-        for (size_t i = nTop, sz = pOpts->GetCount(); i < sz; i++) {
+        for (size_t i = nTop, sz = pOpts->size(); i < sz; i++) {
           if (IsFloatSmaller(fy, rcBody.bottom))
             break;
 
           if (CPDF_Object* pOpt = pOpts->GetDirectObjectAt(i)) {
             WideString swItem;
-            if (pOpt->IsString())
+            if (pOpt->IsString()) {
               swItem = pOpt->GetUnicodeText();
-            else if (CPDF_Array* pArray = pOpt->AsArray())
-              swItem = pArray->GetDirectObjectAt(1)->GetUnicodeText();
-
+            } else if (CPDF_Array* pArray = pOpt->AsArray()) {
+              CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(1);
+              if (pDirectObj)
+                swItem = pDirectObj->GetUnicodeText();
+            }
             bool bSelected = false;
             if (pSels) {
-              for (size_t s = 0, ssz = pSels->GetCount(); s < ssz; s++) {
+              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;
@@ -1282,31 +1315,34 @@
     }
   }
 
-  if (pNormalStream) {
-    pNormalStream->SetDataAndRemoveFilter(&sAppStream);
-    pStreamDict = pNormalStream->GetDict();
-    if (pStreamDict) {
-      pStreamDict->SetMatrixFor("Matrix", matrix);
-      pStreamDict->SetRectFor("BBox", rcBBox);
-      CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
-      if (pStreamResList) {
-        CPDF_Dictionary* pStreamResFontList =
-            pStreamResList->GetDictFor("Font");
-        if (!pStreamResFontList) {
-          pStreamResFontList =
-              pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
-        }
-        if (!pStreamResFontList->KeyExist(sFontName)) {
-          pStreamResFontList->SetNewFor<CPDF_Reference>(sFontName, pDoc,
-                                                        pFontDict->GetObjNum());
-        }
-      } else {
-        pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
-        pStreamResList = pStreamDict->GetDictFor("Resources");
-      }
-    }
+  if (!pNormalStream)
+    return;
+
+  pNormalStream->SetDataFromStringstreamAndRemoveFilter(&sAppStream);
+  pStreamDict = pNormalStream->GetDict();
+  if (!pStreamDict)
+    return;
+
+  pStreamDict->SetMatrixFor("Matrix", matrix);
+  pStreamDict->SetRectFor("BBox", rcBBox);
+  CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
+  if (!pStreamResList) {
+    pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
+    return;
   }
-  return;
+
+  CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font");
+  if (pStreamResFontList) {
+    if (!ValidateFontResourceDict(pStreamResFontList))
+      return;
+  } else {
+    pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
+  }
+
+  if (!pStreamResFontList->KeyExist(font_name)) {
+    pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
+                                                  pFontDict->GetObjNum());
+  }
 }
 
 // static
@@ -1322,9 +1358,9 @@
 }
 
 // static
-bool CPVT_GenerateAP::GenerateAnnotAP(CPDF_Annot::Subtype subtype,
-                                      CPDF_Document* pDoc,
-                                      CPDF_Dictionary* pAnnotDict) {
+bool CPVT_GenerateAP::GenerateAnnotAP(CPDF_Document* pDoc,
+                                      CPDF_Dictionary* pAnnotDict,
+                                      CPDF_Annot::Subtype subtype) {
   switch (subtype) {
     case CPDF_Annot::Subtype::CIRCLE:
       return GenerateCircleAP(pDoc, pAnnotDict);
diff --git a/core/fpdfdoc/cpvt_generateap.h b/core/fpdfdoc/cpvt_generateap.h
index 6f56993..78eebba 100644
--- a/core/fpdfdoc/cpvt_generateap.h
+++ b/core/fpdfdoc/cpvt_generateap.h
@@ -7,31 +7,29 @@
 #ifndef CORE_FPDFDOC_CPVT_GENERATEAP_H_
 #define CORE_FPDFDOC_CPVT_GENERATEAP_H_
 
-#include <memory>
-
-#include "core/fpdfdoc/cpdf_defaultappearance.h"
-#include "core/fpdfdoc/cpdf_variabletext.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
+#include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_color.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
-struct CPVT_Dash;
 
 class CPVT_GenerateAP {
  public:
-  enum Type { kTextField, kComboBox, kListBox };
+  enum FormType { kTextField, kComboBox, kListBox };
 
-  static void GenerateFormAP(Type type,
-                             CPDF_Document* pDoc,
-                             CPDF_Dictionary* pAnnotDict);
+  static void GenerateFormAP(CPDF_Document* pDoc,
+                             CPDF_Dictionary* pAnnotDict,
+                             FormType type);
+
   static void GenerateEmptyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict);
 
-  static bool GenerateAnnotAP(CPDF_Annot::Subtype subtype,
-                              CPDF_Document* pDoc,
-                              CPDF_Dictionary* pAnnotDict);
+  static bool GenerateAnnotAP(CPDF_Document* pDoc,
+                              CPDF_Dictionary* pAnnotDict,
+                              CPDF_Annot::Subtype subtype);
+
+  CPVT_GenerateAP() = delete;
+  CPVT_GenerateAP(const CPVT_GenerateAP&) = delete;
+  CPVT_GenerateAP& operator=(const CPVT_GenerateAP&) = delete;
 };
 
 #endif  // CORE_FPDFDOC_CPVT_GENERATEAP_H_
diff --git a/core/fpdfdoc/cpvt_wordinfo.cpp b/core/fpdfdoc/cpvt_wordinfo.cpp
index 24ad4b8..1e1556b 100644
--- a/core/fpdfdoc/cpvt_wordinfo.cpp
+++ b/core/fpdfdoc/cpvt_wordinfo.cpp
@@ -5,8 +5,8 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/fpdfdoc/cpvt_wordinfo.h"
+
 #include "core/fxcrt/fx_codepage.h"
-#include "third_party/base/ptr_util.h"
 
 CPVT_WordInfo::CPVT_WordInfo()
     : Word(0),
@@ -36,9 +36,9 @@
 
 CPVT_WordInfo::~CPVT_WordInfo() {}
 
-void CPVT_WordInfo::operator=(const CPVT_WordInfo& word) {
+CPVT_WordInfo& CPVT_WordInfo::operator=(const CPVT_WordInfo& word) {
   if (this == &word)
-    return;
+    return *this;
 
   Word = word.Word;
   nCharset = word.nCharset;
@@ -46,4 +46,5 @@
   fWordX = word.fWordX;
   fWordY = word.fWordY;
   fWordTail = word.fWordTail;
+  return *this;
 }
diff --git a/core/fpdfdoc/cpvt_wordinfo.h b/core/fpdfdoc/cpvt_wordinfo.h
index 9b2ec8a..e23ab2c 100644
--- a/core/fpdfdoc/cpvt_wordinfo.h
+++ b/core/fpdfdoc/cpvt_wordinfo.h
@@ -7,8 +7,6 @@
 #ifndef CORE_FPDFDOC_CPVT_WORDINFO_H_
 #define CORE_FPDFDOC_CPVT_WORDINFO_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_system.h"
 
 struct CPVT_WordInfo {
@@ -17,7 +15,7 @@
   CPVT_WordInfo(const CPVT_WordInfo& word);
   ~CPVT_WordInfo();
 
-  void operator=(const CPVT_WordInfo& word);
+  CPVT_WordInfo& operator=(const CPVT_WordInfo& word);
 
   uint16_t Word;
   int32_t nCharset;
diff --git a/core/fpdfdoc/cpvt_wordrange.h b/core/fpdfdoc/cpvt_wordrange.h
index 638f6c1..fbc691f 100644
--- a/core/fpdfdoc/cpvt_wordrange.h
+++ b/core/fpdfdoc/cpvt_wordrange.h
@@ -14,44 +14,13 @@
 #include "core/fxcrt/fx_system.h"
 
 struct CPVT_WordRange {
-  CPVT_WordRange() {}
+  CPVT_WordRange() = default;
 
   CPVT_WordRange(const CPVT_WordPlace& begin, const CPVT_WordPlace& end)
       : BeginPos(begin), EndPos(end) {
     Normalize();
   }
 
-  void Reset() {
-    BeginPos.Reset();
-    EndPos.Reset();
-  }
-
-  void Set(const CPVT_WordPlace& begin, const CPVT_WordPlace& end) {
-    BeginPos = begin;
-    EndPos = end;
-    Normalize();
-  }
-
-  void SetBeginPos(const CPVT_WordPlace& begin) {
-    BeginPos = begin;
-    Normalize();
-  }
-
-  void SetEndPos(const CPVT_WordPlace& end) {
-    EndPos = end;
-    Normalize();
-  }
-
-  CPVT_WordRange Intersect(const CPVT_WordRange& that) const {
-    if (that.EndPos < BeginPos || that.BeginPos > EndPos ||
-        EndPos < that.BeginPos || BeginPos > that.EndPos) {
-      return CPVT_WordRange();
-    }
-
-    return CPVT_WordRange(std::max(BeginPos, that.BeginPos),
-                          std::min(EndPos, that.EndPos));
-  }
-
   inline bool IsEmpty() const { return BeginPos == EndPos; }
   inline bool operator==(const CPVT_WordRange& wr) const {
     return wr.BeginPos == BeginPos && wr.EndPos == EndPos;
diff --git a/core/fpdfdoc/csection.cpp b/core/fpdfdoc/csection.cpp
index 45cbf6b..ad962be 100644
--- a/core/fpdfdoc/csection.cpp
+++ b/core/fpdfdoc/csection.cpp
@@ -14,15 +14,12 @@
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
-CSection::CSection(CPDF_VariableText* pVT) : m_pVT(pVT) {}
-
-CSection::~CSection() {}
-
-void CSection::ResetAll() {
-  m_WordArray.clear();
-  m_LineArray.clear();
+CSection::CSection(CPDF_VariableText* pVT) : m_pVT(pVT) {
+  ASSERT(m_pVT);
 }
 
+CSection::~CSection() = default;
+
 void CSection::ResetLinePlace() {
   int32_t i = 0;
   for (auto& pLine : m_LineArray) {
@@ -124,7 +121,6 @@
 }
 
 CPVT_WordPlace CSection::SearchWordPlace(const CFX_PointF& point) const {
-  ASSERT(m_pVT);
   CPVT_WordPlace place = GetBeginWordPlace();
   bool bUp = true;
   bool bDown = true;
@@ -181,9 +177,7 @@
                                          const CPVT_WordRange& range) const {
   CPVT_WordPlace wordplace = range.BeginPos;
   wordplace.nWordIndex = -1;
-  if (!m_pVT) {
-    return wordplace;
-  }
+
   int32_t nLeft = range.BeginPos.nWordIndex;
   int32_t nRight = range.EndPos.nWordIndex + 1;
   int32_t nMid = (nLeft + nRight) / 2;
diff --git a/core/fpdfdoc/csection.h b/core/fpdfdoc/csection.h
index a8ade86..7352c71 100644
--- a/core/fpdfdoc/csection.h
+++ b/core/fpdfdoc/csection.h
@@ -27,7 +27,6 @@
   explicit CSection(CPDF_VariableText* pVT);
   ~CSection();
 
-  void ResetAll();
   void ResetLinePlace();
   CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
                          const CPVT_WordInfo& wordinfo);
diff --git a/core/fpdfdoc/ctypeset.cpp b/core/fpdfdoc/ctypeset.cpp
index 290b95e..8c61f9e 100644
--- a/core/fpdfdoc/ctypeset.cpp
+++ b/core/fpdfdoc/ctypeset.cpp
@@ -174,14 +174,12 @@
 }  // namespace
 
 CTypeset::CTypeset(CSection* pSection)
-    : m_rcRet(0.0f, 0.0f, 0.0f, 0.0f),
-      m_pVT(pSection->m_pVT),
-      m_pSection(pSection) {}
+    : m_pVT(pSection->m_pVT), m_pSection(pSection) {}
 
-CTypeset::~CTypeset() {}
+CTypeset::~CTypeset() = default;
 
 CPVT_FloatRect CTypeset::CharArray() {
-  m_rcRet = CPVT_FloatRect(0, 0, 0, 0);
+  m_rcRet = CPVT_FloatRect();
   if (m_pSection->m_LineArray.empty())
     return m_rcRet;
 
@@ -278,125 +276,11 @@
 void CTypeset::SplitLines(bool bTypeset, float fFontSize) {
   ASSERT(m_pVT);
   ASSERT(m_pSection);
-  int32_t nLineHead = 0;
-  int32_t nLineTail = 0;
-  float fMaxX = 0.0f, fMaxY = 0.0f;
-  float fLineWidth = 0.0f, fBackupLineWidth = 0.0f;
-  float fLineAscent = 0.0f, fBackupLineAscent = 0.0f;
-  float fLineDescent = 0.0f, fBackupLineDescent = 0.0f;
-  int32_t nWordStartPos = 0;
-  bool bFullWord = false;
-  int32_t nLineFullWordIndex = 0;
-  int32_t nCharIndex = 0;
+
   CPVT_LineInfo line;
-  float fWordWidth = 0;
-  float fTypesetWidth =
-      std::max(m_pVT->GetPlateWidth() - m_pVT->GetLineIndent(), 0.0f);
-  int32_t nTotalWords =
-      pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray);
-  bool bOpened = false;
-  if (nTotalWords > 0) {
-    int32_t i = 0;
-    while (i < nTotalWords) {
-      CPVT_WordInfo* pWord = m_pSection->m_WordArray[i].get();
-      CPVT_WordInfo* pOldWord = pWord;
-      if (i > 0) {
-        pOldWord = m_pSection->m_WordArray[i - 1].get();
-      }
-      if (pWord) {
-        if (bTypeset) {
-          fLineAscent = std::max(fLineAscent, m_pVT->GetWordAscent(*pWord));
-          fLineDescent = std::min(fLineDescent, m_pVT->GetWordDescent(*pWord));
-          fWordWidth = m_pVT->GetWordWidth(*pWord);
-        } else {
-          fLineAscent =
-              std::max(fLineAscent, m_pVT->GetWordAscent(*pWord, fFontSize));
-          fLineDescent =
-              std::min(fLineDescent, m_pVT->GetWordDescent(*pWord, fFontSize));
-          fWordWidth = m_pVT->GetWordWidth(
-              pWord->nFontIndex, pWord->Word, m_pVT->GetSubWord(),
-              m_pVT->GetCharSpace(), m_pVT->GetHorzScale(), fFontSize,
-              pWord->fWordTail);
-        }
-        if (!bOpened) {
-          if (IsOpenStylePunctuation(pWord->Word)) {
-            bOpened = true;
-            bFullWord = true;
-          } else if (pOldWord) {
-            if (NeedDivision(pOldWord->Word, pWord->Word)) {
-              bFullWord = true;
-            }
-          }
-        } else {
-          if (!IsSpace(pWord->Word) && !IsOpenStylePunctuation(pWord->Word)) {
-            bOpened = false;
-          }
-        }
-        if (bFullWord) {
-          bFullWord = false;
-          if (nCharIndex > 0) {
-            nLineFullWordIndex++;
-          }
-          nWordStartPos = i;
-          fBackupLineWidth = fLineWidth;
-          fBackupLineAscent = fLineAscent;
-          fBackupLineDescent = fLineDescent;
-        }
-        nCharIndex++;
-      }
-      if (m_pVT->IsAutoReturn() && fTypesetWidth > 0 &&
-          fLineWidth + fWordWidth > fTypesetWidth) {
-        if (nLineFullWordIndex > 0) {
-          i = nWordStartPos;
-          fLineWidth = fBackupLineWidth;
-          fLineAscent = fBackupLineAscent;
-          fLineDescent = fBackupLineDescent;
-        }
-        if (nCharIndex == 1) {
-          fLineWidth = fWordWidth;
-          i++;
-        }
-        nLineTail = i - 1;
-        if (bTypeset) {
-          line.nBeginWordIndex = nLineHead;
-          line.nEndWordIndex = nLineTail;
-          line.nTotalWord = nLineTail - nLineHead + 1;
-          line.fLineWidth = fLineWidth;
-          line.fLineAscent = fLineAscent;
-          line.fLineDescent = fLineDescent;
-          m_pSection->AddLine(line);
-        }
-        fMaxY += (fLineAscent + m_pVT->GetLineLeading());
-        fMaxY -= fLineDescent;
-        fMaxX = std::max(fLineWidth, fMaxX);
-        nLineHead = i;
-        fLineWidth = 0.0f;
-        fLineAscent = 0.0f;
-        fLineDescent = 0.0f;
-        nCharIndex = 0;
-        nLineFullWordIndex = 0;
-        bFullWord = false;
-      } else {
-        fLineWidth += fWordWidth;
-        i++;
-      }
-    }
-    if (nLineHead <= nTotalWords - 1) {
-      nLineTail = nTotalWords - 1;
-      if (bTypeset) {
-        line.nBeginWordIndex = nLineHead;
-        line.nEndWordIndex = nLineTail;
-        line.nTotalWord = nLineTail - nLineHead + 1;
-        line.fLineWidth = fLineWidth;
-        line.fLineAscent = fLineAscent;
-        line.fLineDescent = fLineDescent;
-        m_pSection->AddLine(line);
-      }
-      fMaxY += (fLineAscent + m_pVT->GetLineLeading());
-      fMaxY -= fLineDescent;
-      fMaxX = std::max(fLineWidth, fMaxX);
-    }
-  } else {
+  if (m_pSection->m_WordArray.empty()) {
+    float fLineAscent;
+    float fLineDescent;
     if (bTypeset) {
       fLineAscent = m_pVT->GetLineAscent();
       fLineDescent = m_pVT->GetLineDescent();
@@ -415,7 +299,129 @@
       line.fLineDescent = fLineDescent;
       m_pSection->AddLine(line);
     }
-    fMaxY += m_pVT->GetLineLeading() + fLineAscent - fLineDescent;
+    float fMaxY = m_pVT->GetLineLeading() + fLineAscent - fLineDescent;
+    m_rcRet = CPVT_FloatRect(0, 0, 0, fMaxY);
+    return;
+  }
+
+  int32_t nLineHead = 0;
+  int32_t nLineTail = 0;
+  float fMaxX = 0.0f;
+  float fMaxY = 0.0f;
+  float fLineWidth = 0.0f;
+  float fBackupLineWidth = 0.0f;
+  float fLineAscent = 0.0f;
+  float fBackupLineAscent = 0.0f;
+  float fLineDescent = 0.0f;
+  float fBackupLineDescent = 0.0f;
+  int32_t nWordStartPos = 0;
+  bool bFullWord = false;
+  int32_t nLineFullWordIndex = 0;
+  int32_t nCharIndex = 0;
+  float fWordWidth = 0;
+  float fTypesetWidth =
+      std::max(m_pVT->GetPlateWidth() - m_pVT->GetLineIndent(), 0.0f);
+  int32_t nTotalWords =
+      pdfium::CollectionSize<int32_t>(m_pSection->m_WordArray);
+  bool bOpened = false;
+  int32_t i = 0;
+  while (i < nTotalWords) {
+    CPVT_WordInfo* pWord = m_pSection->m_WordArray[i].get();
+    CPVT_WordInfo* pOldWord = pWord;
+    if (i > 0) {
+      pOldWord = m_pSection->m_WordArray[i - 1].get();
+    }
+    if (pWord) {
+      if (bTypeset) {
+        fLineAscent = std::max(fLineAscent, m_pVT->GetWordAscent(*pWord));
+        fLineDescent = std::min(fLineDescent, m_pVT->GetWordDescent(*pWord));
+        fWordWidth = m_pVT->GetWordWidth(*pWord);
+      } else {
+        fLineAscent =
+            std::max(fLineAscent, m_pVT->GetWordAscent(*pWord, fFontSize));
+        fLineDescent =
+            std::min(fLineDescent, m_pVT->GetWordDescent(*pWord, fFontSize));
+        fWordWidth = m_pVT->GetWordWidth(
+            pWord->nFontIndex, pWord->Word, m_pVT->GetSubWord(),
+            m_pVT->GetCharSpace(), fFontSize, pWord->fWordTail);
+      }
+      if (!bOpened) {
+        if (IsOpenStylePunctuation(pWord->Word)) {
+          bOpened = true;
+          bFullWord = true;
+        } else if (pOldWord) {
+          if (NeedDivision(pOldWord->Word, pWord->Word)) {
+            bFullWord = true;
+          }
+        }
+      } else {
+        if (!IsSpace(pWord->Word) && !IsOpenStylePunctuation(pWord->Word)) {
+          bOpened = false;
+        }
+      }
+      if (bFullWord) {
+        bFullWord = false;
+        if (nCharIndex > 0) {
+          nLineFullWordIndex++;
+        }
+        nWordStartPos = i;
+        fBackupLineWidth = fLineWidth;
+        fBackupLineAscent = fLineAscent;
+        fBackupLineDescent = fLineDescent;
+      }
+      nCharIndex++;
+    }
+    if (m_pVT->IsAutoReturn() && fTypesetWidth > 0 &&
+        fLineWidth + fWordWidth > fTypesetWidth) {
+      if (nLineFullWordIndex > 0) {
+        i = nWordStartPos;
+        fLineWidth = fBackupLineWidth;
+        fLineAscent = fBackupLineAscent;
+        fLineDescent = fBackupLineDescent;
+      }
+      if (nCharIndex == 1) {
+        fLineWidth = fWordWidth;
+        i++;
+      }
+      nLineTail = i - 1;
+      if (bTypeset) {
+        line.nBeginWordIndex = nLineHead;
+        line.nEndWordIndex = nLineTail;
+        line.nTotalWord = nLineTail - nLineHead + 1;
+        line.fLineWidth = fLineWidth;
+        line.fLineAscent = fLineAscent;
+        line.fLineDescent = fLineDescent;
+        m_pSection->AddLine(line);
+      }
+      fMaxY += (fLineAscent + m_pVT->GetLineLeading());
+      fMaxY -= fLineDescent;
+      fMaxX = std::max(fLineWidth, fMaxX);
+      nLineHead = i;
+      fLineWidth = 0.0f;
+      fLineAscent = 0.0f;
+      fLineDescent = 0.0f;
+      nCharIndex = 0;
+      nLineFullWordIndex = 0;
+      bFullWord = false;
+    } else {
+      fLineWidth += fWordWidth;
+      i++;
+    }
+  }
+  if (nLineHead <= nTotalWords - 1) {
+    nLineTail = nTotalWords - 1;
+    if (bTypeset) {
+      line.nBeginWordIndex = nLineHead;
+      line.nEndWordIndex = nLineTail;
+      line.nTotalWord = nLineTail - nLineHead + 1;
+      line.fLineWidth = fLineWidth;
+      line.fLineAscent = fLineAscent;
+      line.fLineDescent = fLineDescent;
+      m_pSection->AddLine(line);
+    }
+    fMaxY += (fLineAscent + m_pVT->GetLineLeading());
+    fMaxY -= fLineDescent;
+    fMaxX = std::max(fLineWidth, fMaxX);
   }
   m_rcRet = CPVT_FloatRect(0, 0, fMaxX, fMaxY);
 }
@@ -423,8 +429,7 @@
 void CTypeset::OutputLines() {
   ASSERT(m_pVT);
   ASSERT(m_pSection);
-  float fMinX = 0.0f, fMinY = 0.0f, fMaxX = 0.0f, fMaxY = 0.0f;
-  float fPosX = 0.0f, fPosY = 0.0f;
+  float fMinX;
   float fLineIndent = m_pVT->GetLineIndent();
   float fTypesetWidth = std::max(m_pVT->GetPlateWidth() - fLineIndent, 0.0f);
   switch (m_pVT->GetAlignment()) {
@@ -439,12 +444,14 @@
       fMinX = fTypesetWidth - m_rcRet.Width();
       break;
   }
-  fMaxX = fMinX + m_rcRet.Width();
-  fMinY = 0.0f;
-  fMaxY = m_rcRet.Height();
+  float fMaxX = fMinX + m_rcRet.Width();
+  float fMinY = 0.0f;
+  float fMaxY = m_rcRet.Height();
   int32_t nTotalLines =
       pdfium::CollectionSize<int32_t>(m_pSection->m_LineArray);
   if (nTotalLines > 0) {
+    float fPosX = 0.0f;
+    float fPosY = 0.0f;
     for (int32_t l = 0; l < nTotalLines; l++) {
       CLine* pLine = m_pSection->m_LineArray[l].get();
       switch (m_pVT->GetAlignment()) {
diff --git a/core/fpdfdoc/ipdf_formnotify.h b/core/fpdfdoc/ipdf_formnotify.h
deleted file mode 100644
index 52e5cc9..0000000
--- a/core/fpdfdoc/ipdf_formnotify.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FPDFDOC_IPDF_FORMNOTIFY_H_
-#define CORE_FPDFDOC_IPDF_FORMNOTIFY_H_
-
-#include "core/fxcrt/fx_string.h"
-
-class CPDF_FormField;
-class CPDF_InterForm;
-
-class IPDF_FormNotify {
- public:
-  virtual ~IPDF_FormNotify() {}
-
-  virtual int BeforeValueChange(CPDF_FormField* pField,
-                                const WideString& csValue) = 0;
-  virtual void AfterValueChange(CPDF_FormField* pField) = 0;
-  virtual int BeforeSelectionChange(CPDF_FormField* pField,
-                                    const WideString& csValue) = 0;
-  virtual void AfterSelectionChange(CPDF_FormField* pField) = 0;
-  virtual void AfterCheckedStatusChange(CPDF_FormField* pField) = 0;
-  virtual int BeforeFormReset(CPDF_InterForm* pForm) = 0;
-  virtual void AfterFormReset(CPDF_InterForm* pForm) = 0;
-  virtual int BeforeFormImportData(CPDF_InterForm* pForm) = 0;
-  virtual void AfterFormImportData(CPDF_InterForm* pForm) = 0;
-};
-
-#endif  // CORE_FPDFDOC_IPDF_FORMNOTIFY_H_
diff --git a/core/fpdfdoc/ipvt_fontmap.h b/core/fpdfdoc/ipvt_fontmap.h
index 31aa73b..e818356 100644
--- a/core/fpdfdoc/ipvt_fontmap.h
+++ b/core/fpdfdoc/ipvt_fontmap.h
@@ -10,14 +10,15 @@
 #include <stdint.h>
 
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Font;
 
 class IPVT_FontMap {
  public:
-  virtual ~IPVT_FontMap() {}
+  virtual ~IPVT_FontMap() = default;
 
-  virtual CPDF_Font* GetPDFFont(int32_t nFontIndex) = 0;
+  virtual RetainPtr<CPDF_Font> GetPDFFont(int32_t nFontIndex) = 0;
   virtual ByteString GetPDFFontAlias(int32_t nFontIndex) = 0;
   virtual int32_t GetWordFontIndex(uint16_t word,
                                    int32_t charset,
diff --git a/core/fpdftext/Android.bp b/core/fpdftext/Android.bp
new file mode 100644
index 0000000..711f35d
--- /dev/null
+++ b/core/fpdftext/Android.bp
@@ -0,0 +1,17 @@
+cc_library_static {
+    name: "libpdfium-fpdftext",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    static_libs: [
+        "libpdfium-font",
+        "libpdfium-page",
+        "libpdfium-parser",
+        "libpdfium-fxcrt",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/core/fpdftext/BUILD.gn b/core/fpdftext/BUILD.gn
new file mode 100644
index 0000000..f48a96d
--- /dev/null
+++ b/core/fpdftext/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+source_set("fpdftext") {
+  sources = [
+    "cpdf_linkextract.cpp",
+    "cpdf_linkextract.h",
+    "cpdf_textpage.cpp",
+    "cpdf_textpage.h",
+    "cpdf_textpagefind.cpp",
+    "cpdf_textpagefind.h",
+    "unicodenormalizationdata.cpp",
+    "unicodenormalizationdata.h",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  deps = [
+    "../fpdfapi/font",
+    "../fpdfapi/page",
+    "../fpdfapi/parser",
+    "../fxcrt",
+  ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [ "cpdf_linkextract_unittest.cpp" ]
+  deps = [ ":fpdftext" ]
+  pdfium_root_dir = "../../"
+}
diff --git a/core/fpdftext/cpdf_linkextract.cpp b/core/fpdftext/cpdf_linkextract.cpp
index 05cbdfb..f822f3b 100644
--- a/core/fpdftext/cpdf_linkextract.cpp
+++ b/core/fpdftext/cpdf_linkextract.cpp
@@ -39,7 +39,7 @@
         size_t off = end + 1;
         if (off < len && str[off] == L':') {
           off++;
-          while (off < len && str[off] >= L'0' && str[off] <= L'9')
+          while (off < len && FXSYS_IsDecimalDigit(str[off]))
             off++;
           if (off > end + 2 &&
               off <= len)   // At least one digit in port number.
@@ -54,9 +54,10 @@
   // and periods. Hyphen should not at the end though.
   // Non-ASCII chars are ignored during checking.
   while (end > start && str[end] < 0x80) {
-    if ((str[end] >= L'0' && str[end] <= L'9') ||
-        (str[end] >= L'a' && str[end] <= L'z') || str[end] == L'.')
+    if (FXSYS_IsDecimalDigit(str[end]) ||
+        (str[end] >= L'a' && str[end] <= L'z') || str[end] == L'.') {
       break;
+    }
     end--;
   }
   return end;
@@ -107,81 +108,72 @@
 CPDF_LinkExtract::CPDF_LinkExtract(const CPDF_TextPage* pTextPage)
     : m_pTextPage(pTextPage) {}
 
-CPDF_LinkExtract::~CPDF_LinkExtract() {}
+CPDF_LinkExtract::~CPDF_LinkExtract() = default;
 
 void CPDF_LinkExtract::ExtractLinks() {
   m_LinkArray.clear();
-  if (!m_pTextPage->IsParsed())
-    return;
-
-  m_strPageText = m_pTextPage->GetAllPageText();
-  if (m_strPageText.IsEmpty())
-    return;
-
-  ParseLink();
-}
-
-void CPDF_LinkExtract::ParseLink() {
   int start = 0;
   int pos = 0;
-  int nTotalChar = m_pTextPage->CountChars();
   bool bAfterHyphen = false;
   bool bLineBreak = false;
+  const int nTotalChar = m_pTextPage->CountChars();
+  const WideString page_text = m_pTextPage->GetAllPageText();
   while (pos < nTotalChar) {
-    FPDF_CHAR_INFO pageChar;
-    m_pTextPage->GetCharInfo(pos, &pageChar);
-    if (pageChar.m_Flag == FPDFTEXT_CHAR_GENERATED ||
-        pageChar.m_Unicode == TEXT_SPACE_CHAR || pos == nTotalChar - 1) {
-      int nCount = pos - start;
-      if (pos == nTotalChar - 1) {
-        nCount++;
-      } else if (bAfterHyphen && (pageChar.m_Unicode == TEXT_LINEFEED_CHAR ||
-                                  pageChar.m_Unicode == TEXT_RETURN_CHAR)) {
-        // Handle text breaks with a hyphen to the next line.
-        bLineBreak = true;
-        pos++;
-        continue;
-      }
-      WideString strBeCheck;
-      strBeCheck = m_pTextPage->GetPageText(start, nCount);
-      if (bLineBreak) {
-        strBeCheck.Remove(TEXT_LINEFEED_CHAR);
-        strBeCheck.Remove(TEXT_RETURN_CHAR);
-        bLineBreak = false;
-      }
-      // Replace the generated code with the hyphen char.
-      strBeCheck.Replace(L"\xfffe", TEXT_HYPHEN);
-
-      if (strBeCheck.GetLength() > 5) {
-        while (strBeCheck.GetLength() > 0) {
-          wchar_t ch = strBeCheck[strBeCheck.GetLength() - 1];
-          if (ch == L')' || ch == L',' || ch == L'>' || ch == L'.') {
-            strBeCheck = strBeCheck.Left(strBeCheck.GetLength() - 1);
-            nCount--;
-          } else {
-            break;
-          }
-        }
-        // Check for potential web URLs and email addresses.
-        // Ftp address, file system links, data, blob etc. are not checked.
-        if (nCount > 5) {
-          int32_t nStartOffset;
-          int32_t nCountOverload;
-          if (CheckWebLink(&strBeCheck, &nStartOffset, &nCountOverload)) {
-            m_LinkArray.push_back(
-                {start + nStartOffset, nCountOverload, strBeCheck});
-          } else if (CheckMailLink(&strBeCheck)) {
-            m_LinkArray.push_back({start, nCount, strBeCheck});
-          }
-        }
-      }
-      start = ++pos;
-    } else {
-      bAfterHyphen = (pageChar.m_Flag == FPDFTEXT_CHAR_HYPHEN ||
-                      (pageChar.m_Flag == FPDFTEXT_CHAR_NORMAL &&
-                       pageChar.m_Unicode == TEXT_HYPHEN_CHAR));
-      pos++;
+    const CPDF_TextPage::CharInfo& char_info = m_pTextPage->GetCharInfo(pos);
+    if (char_info.m_CharType != CPDF_TextPage::CharType::kGenerated &&
+        char_info.m_Unicode != L' ' && pos != nTotalChar - 1) {
+      bAfterHyphen =
+          (char_info.m_CharType == CPDF_TextPage::CharType::kHyphen ||
+           (char_info.m_CharType == CPDF_TextPage::CharType::kNormal &&
+            char_info.m_Unicode == L'-'));
+      ++pos;
+      continue;
     }
+
+    int nCount = pos - start;
+    if (pos == nTotalChar - 1) {
+      ++nCount;
+    } else if (bAfterHyphen &&
+               (char_info.m_Unicode == L'\n' || char_info.m_Unicode == L'\r')) {
+      // Handle text breaks with a hyphen to the next line.
+      bLineBreak = true;
+      ++pos;
+      continue;
+    }
+
+    WideString strBeCheck = page_text.Substr(start, nCount);
+    if (bLineBreak) {
+      strBeCheck.Remove(L'\n');
+      strBeCheck.Remove(L'\r');
+      bLineBreak = false;
+    }
+    // Replace the generated code with the hyphen char.
+    strBeCheck.Replace(L"\xfffe", L"-");
+
+    if (strBeCheck.GetLength() > 5) {
+      while (strBeCheck.GetLength() > 0) {
+        wchar_t ch = strBeCheck.Back();
+        if (ch != L')' && ch != L',' && ch != L'>' && ch != L'.')
+          break;
+
+        strBeCheck = strBeCheck.First(strBeCheck.GetLength() - 1);
+        nCount--;
+      }
+
+      // Check for potential web URLs and email addresses.
+      // Ftp address, file system links, data, blob etc. are not checked.
+      if (nCount > 5) {
+        int32_t nStartOffset;
+        int32_t nCountOverload;
+        if (CheckWebLink(&strBeCheck, &nStartOffset, &nCountOverload)) {
+          m_LinkArray.push_back(
+              {start + nStartOffset, nCountOverload, strBeCheck});
+        } else if (CheckMailLink(&strBeCheck)) {
+          m_LinkArray.push_back({start, nCount, strBeCheck});
+        }
+      }
+    }
+    start = ++pos;
   }
 }
 
@@ -189,9 +181,10 @@
                                     int32_t* nStart,
                                     int32_t* nCount) {
   static const wchar_t kHttpScheme[] = L"http";
-  static const size_t kHttpSchemeLen = FXSYS_len(kHttpScheme);
   static const wchar_t kWWWAddrStart[] = L"www.";
-  static const size_t kWWWAddrStartLen = FXSYS_len(kWWWAddrStart);
+
+  const size_t kHttpSchemeLen = FXSYS_len(kHttpScheme);
+  const size_t kWWWAddrStartLen = FXSYS_len(kWWWAddrStart);
 
   WideString str = *strBeCheck;
   str.MakeLower();
@@ -201,8 +194,8 @@
   auto start = str.Find(kHttpScheme);
   if (start.has_value()) {
     size_t off = start.value() + kHttpSchemeLen;  // move after "http".
-    if (len > off + 4) {                      // At least "://<char>" follows.
-      if (str[off] == L's')                   // "https" scheme is accepted.
+    if (len > off + 4) {     // At least "://<char>" follows.
+      if (str[off] == L's')  // "https" scheme is accepted.
         off++;
       if (str[off] == L':' && str[off + 1] == L'/' && str[off + 2] == L'/') {
         off += 3;
@@ -212,7 +205,7 @@
         if (end > off) {  // Non-empty host name.
           *nStart = start.value();
           *nCount = end - start.value() + 1;
-          *strBeCheck = strBeCheck->Mid(*nStart, *nCount);
+          *strBeCheck = strBeCheck->Substr(*nStart, *nCount);
           return true;
         }
       }
@@ -228,7 +221,7 @@
     if (end > start.value() + kWWWAddrStartLen) {
       *nStart = start.value();
       *nCount = end - start.value() + 1;
-      *strBeCheck = L"http://" + strBeCheck->Mid(*nStart, *nCount);
+      *strBeCheck = L"http://" + strBeCheck->Substr(*nStart, *nCount);
       return true;
     }
   }
@@ -256,7 +249,7 @@
       // End extracting for other invalid chars, '.' at the beginning, or
       // consecutive '.'.
       size_t removed_len = i == pPos ? i + 1 : i;
-      *str = str->Right(str->GetLength() - removed_len);
+      *str = str->Last(str->GetLength() - removed_len);
       break;
     }
     // Found a valid '.'.
@@ -289,7 +282,7 @@
       size_t host_end = i == pPos + 1 ? i - 2 : i - 1;
       if (pPos > 0 && host_end - aPos.value() >= 3) {
         // Trim the ending invalid chars if there is at least one '.' and name.
-        *str = str->Left(host_end + 1);
+        *str = str->First(host_end + 1);
         break;
       }
       return false;
@@ -304,7 +297,8 @@
 }
 
 WideString CPDF_LinkExtract::GetURL(size_t index) const {
-  return index < m_LinkArray.size() ? m_LinkArray[index].m_strUrl : L"";
+  return index < m_LinkArray.size() ? m_LinkArray[index].m_strUrl
+                                    : WideString();
 }
 
 std::vector<CFX_FloatRect> CPDF_LinkExtract::GetRects(size_t index) const {
@@ -314,3 +308,13 @@
   return m_pTextPage->GetRectArray(m_LinkArray[index].m_Start,
                                    m_LinkArray[index].m_Count);
 }
+
+bool CPDF_LinkExtract::GetTextRange(size_t index,
+                                    int* start_char_index,
+                                    int* char_count) const {
+  if (index >= m_LinkArray.size())
+    return false;
+  *start_char_index = m_LinkArray[index].m_Start;
+  *char_count = m_LinkArray[index].m_Count;
+  return true;
+}
diff --git a/core/fpdftext/cpdf_linkextract.h b/core/fpdftext/cpdf_linkextract.h
index 713f658..56d70a3 100644
--- a/core/fpdftext/cpdf_linkextract.h
+++ b/core/fpdftext/cpdf_linkextract.h
@@ -24,9 +24,9 @@
   size_t CountLinks() const { return m_LinkArray.size(); }
   WideString GetURL(size_t index) const;
   std::vector<CFX_FloatRect> GetRects(size_t index) const;
+  bool GetTextRange(size_t index, int* start_char_index, int* char_count) const;
 
  protected:
-  void ParseLink();
   bool CheckWebLink(WideString* str, int32_t* nStart, int32_t* nCount);
   bool CheckMailLink(WideString* str);
 
@@ -38,7 +38,6 @@
   };
 
   UnownedPtr<const CPDF_TextPage> const m_pTextPage;
-  WideString m_strPageText;
   std::vector<Link> m_LinkArray;
 };
 
diff --git a/core/fpdftext/cpdf_linkextract_unittest.cpp b/core/fpdftext/cpdf_linkextract_unittest.cpp
index 30438e6..f4bd197 100644
--- a/core/fpdftext/cpdf_linkextract_unittest.cpp
+++ b/core/fpdftext/cpdf_linkextract_unittest.cpp
@@ -7,7 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 // Class to help test functions in CPDF_LinkExtract class.
-class CPDF_TestLinkExtract : public CPDF_LinkExtract {
+class CPDF_TestLinkExtract final : public CPDF_LinkExtract {
  public:
   CPDF_TestLinkExtract() : CPDF_LinkExtract(nullptr) {}
 
diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp
index b448a59..58a2959 100644
--- a/core/fpdftext/cpdf_textpage.cpp
+++ b/core/fpdftext/cpdf_textpage.cpp
@@ -7,6 +7,7 @@
 #include "core/fpdftext/cpdf_textpage.h"
 
 #include <algorithm>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -21,48 +22,54 @@
 #include "core/fpdftext/unicodenormalizationdata.h"
 #include "core/fxcrt/fx_bidi.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_unicode.h"
 #include "third_party/base/stl_util.h"
 
 namespace {
 
-const float kDefaultFontSize = 1.0f;
-const uint16_t* const g_UnicodeData_Normalization_Maps[5] = {
-    nullptr, g_UnicodeData_Normalization_Map1, g_UnicodeData_Normalization_Map2,
-    g_UnicodeData_Normalization_Map3, g_UnicodeData_Normalization_Map4};
+constexpr float kDefaultFontSize = 1.0f;
+constexpr float kSizeEpsilon = 0.01f;
 
-float NormalizeThreshold(float threshold) {
-  if (threshold < 300)
+const uint16_t* const g_UnicodeData_Normalization_Maps[] = {
+    g_UnicodeData_Normalization_Map2, g_UnicodeData_Normalization_Map3,
+    g_UnicodeData_Normalization_Map4};
+
+float NormalizeThreshold(float threshold, int t1, int t2, int t3) {
+  ASSERT(t1 < t2);
+  ASSERT(t2 < t3);
+  if (threshold < t1)
     return threshold / 2.0f;
-  if (threshold < 500)
+  if (threshold < t2)
     return threshold / 4.0f;
-  if (threshold < 700)
+  if (threshold < t3)
     return threshold / 5.0f;
   return threshold / 6.0f;
 }
 
 float CalculateBaseSpace(const CPDF_TextObject* pTextObj,
                          const CFX_Matrix& matrix) {
-  float baseSpace = 0.0;
   const size_t nItems = pTextObj->CountItems();
-  if (pTextObj->m_TextState.GetCharSpace() && nItems >= 3) {
-    bool bAllChar = true;
-    float spacing =
-        matrix.TransformDistance(pTextObj->m_TextState.GetCharSpace());
-    baseSpace = spacing;
-    for (size_t i = 0; i < nItems; ++i) {
-      CPDF_TextObjectItem item;
-      pTextObj->GetItemInfo(i, &item);
-      if (item.m_CharCode == static_cast<uint32_t>(-1)) {
-        float fontsize_h = pTextObj->m_TextState.GetFontSizeH();
-        float kerning = -fontsize_h * item.m_Origin.x / 1000;
-        baseSpace = std::min(baseSpace, kerning + spacing);
-        bAllChar = false;
-      }
+  if (!pTextObj->m_TextState.GetCharSpace() || nItems < 3)
+    return 0.0f;
+
+  bool bAllChar = true;
+  float spacing =
+      matrix.TransformDistance(pTextObj->m_TextState.GetCharSpace());
+  float baseSpace = spacing;
+  for (size_t i = 0; i < nItems; ++i) {
+    CPDF_TextObjectItem item;
+    pTextObj->GetItemInfo(i, &item);
+    if (item.m_CharCode == 0xffffffff) {
+      float fontsize_h = pTextObj->m_TextState.GetFontSizeH();
+      float kerning = -fontsize_h * item.m_Origin.x / 1000;
+      baseSpace = std::min(baseSpace, kerning + spacing);
+      bAllChar = false;
     }
-    if (baseSpace < 0.0 || (nItems == 3 && !bAllChar))
-      baseSpace = 0.0;
   }
+  if (baseSpace < 0.0 || (nItems == 3 && !bAllChar))
+    return 0.0f;
+
   return baseSpace;
 }
 
@@ -75,19 +82,18 @@
     return 1;
   }
   if (wFind >= 0x8000) {
-    wch = wFind - 0x8000;
-    wFind = 1;
-  } else {
-    wch = wFind & 0x0FFF;
-    wFind >>= 12;
+    const uint16_t* pMap = g_UnicodeData_Normalization_Map1 + (wFind - 0x8000);
+    if (pDst)
+      *pDst = *pMap;
+    return 1;
   }
-  const uint16_t* pMap = g_UnicodeData_Normalization_Maps[wFind];
-  if (pMap == g_UnicodeData_Normalization_Map4) {
-    pMap = g_UnicodeData_Normalization_Map4 + wch;
-    wFind = (wchar_t)(*pMap++);
-  } else {
-    pMap += wch;
-  }
+
+  wch = wFind & 0x0FFF;
+  wFind >>= 12;
+  const uint16_t* pMap = g_UnicodeData_Normalization_Maps[wFind - 2] + wch;
+  if (wFind == 4)
+    wFind = static_cast<wchar_t>(*pMap++);
+
   if (pDst) {
     wchar_t n = wFind;
     while (n--)
@@ -106,10 +112,120 @@
   return count / (end - start);
 }
 
+bool IsControlChar(const CPDF_TextPage::CharInfo& char_info) {
+  switch (char_info.m_Unicode) {
+    case 0x2:
+    case 0x3:
+    case 0x93:
+    case 0x94:
+    case 0x96:
+    case 0x97:
+    case 0x98:
+    case 0xfffe:
+      return char_info.m_CharType != CPDF_TextPage::CharType::kHyphen;
+    default:
+      return false;
+  }
+}
+
 bool IsHyphenCode(wchar_t c) {
   return c == 0x2D || c == 0xAD;
 }
 
+bool IsRectIntersect(const CFX_FloatRect& rect1, const CFX_FloatRect& rect2) {
+  CFX_FloatRect rect = rect1;
+  rect.Intersect(rect2);
+  return !rect.IsEmpty();
+}
+
+bool IsRightToLeft(const CPDF_TextObject& text_obj, const CPDF_Font& font) {
+  const size_t nItems = text_obj.CountItems();
+  WideString str;
+  str.Reserve(nItems);
+  for (size_t i = 0; i < nItems; ++i) {
+    CPDF_TextObjectItem item;
+    text_obj.GetItemInfo(i, &item);
+    if (item.m_CharCode == 0xffffffff)
+      continue;
+    WideString wstrItem = font.UnicodeFromCharCode(item.m_CharCode);
+    wchar_t wChar = !wstrItem.IsEmpty() ? wstrItem[0] : 0;
+    if (wChar == 0)
+      wChar = item.m_CharCode;
+    if (wChar)
+      str += wChar;
+  }
+  return CFX_BidiString(str).OverallDirection() == CFX_BidiChar::RIGHT;
+}
+
+uint32_t GetCharWidth(uint32_t charCode, CPDF_Font* pFont) {
+  if (charCode == CPDF_Font::kInvalidCharCode)
+    return 0;
+
+  uint32_t w = pFont->GetCharWidthF(charCode);
+  if (w > 0)
+    return w;
+
+  ByteString str;
+  pFont->AppendChar(&str, charCode);
+  w = pFont->GetStringWidth(str.AsStringView());
+  if (w > 0)
+    return w;
+
+  FX_RECT rect = pFont->GetCharBBox(charCode);
+  if (!rect.Valid())
+    return 0;
+
+  return std::max(rect.Width(), 0);
+}
+
+bool GenerateSpace(const CFX_PointF& pos,
+                   float last_pos,
+                   float this_width,
+                   float last_width,
+                   float threshold) {
+  if (fabs(last_pos + last_width - pos.x) <= threshold)
+    return false;
+
+  float threshold_pos = threshold + last_width;
+  float pos_difference = pos.x - last_pos;
+  if (fabs(pos_difference) > threshold_pos)
+    return true;
+  if (pos.x < 0 && -threshold_pos > pos_difference)
+    return true;
+  return pos_difference > this_width + last_width;
+}
+
+bool EndHorizontalLine(const CFX_FloatRect& this_rect,
+                       const CFX_FloatRect& prev_rect) {
+  if (this_rect.Height() <= 4.5 || prev_rect.Height() <= 4.5)
+    return false;
+
+  float top = std::min(this_rect.top, prev_rect.top);
+  float bottom = std::max(this_rect.bottom, prev_rect.bottom);
+  return bottom >= top;
+}
+
+bool EndVerticalLine(const CFX_FloatRect& this_rect,
+                     const CFX_FloatRect& prev_rect,
+                     const CFX_FloatRect& curline_rect,
+                     float this_fontsize,
+                     float prev_fontsize) {
+  if (this_rect.Width() <= this_fontsize * 0.1f ||
+      prev_rect.Width() <= prev_fontsize * 0.1f) {
+    return false;
+  }
+
+  float left = std::max(this_rect.left, curline_rect.left);
+  float right = std::min(this_rect.right, curline_rect.right);
+  return right <= left;
+}
+
+CFX_Matrix GetPageMatrix(const CPDF_Page* pPage) {
+  const FX_RECT rect(0, 0, static_cast<int>(pPage->GetPageWidth()),
+                     static_cast<int>(pPage->GetPageHeight()));
+  return pPage->GetDisplayMatrix(rect, 0);
+}
+
 }  // namespace
 
 PDFTEXT_Obj::PDFTEXT_Obj() {}
@@ -118,246 +234,169 @@
 
 PDFTEXT_Obj::~PDFTEXT_Obj() {}
 
-FPDF_CHAR_INFO::FPDF_CHAR_INFO()
-    : m_Unicode(0),
-      m_Charcode(0),
-      m_Flag(0),
-      m_FontSize(0),
-      m_pTextObj(nullptr) {}
+CPDF_TextPage::CharInfo::CharInfo() = default;
 
-FPDF_CHAR_INFO::~FPDF_CHAR_INFO() {}
+CPDF_TextPage::CharInfo::CharInfo(const CharInfo&) = default;
 
-PAGECHAR_INFO::PAGECHAR_INFO()
-    : m_Index(0), m_CharCode(0), m_Unicode(0), m_Flag(0), m_pTextObj(nullptr) {}
+CPDF_TextPage::CharInfo::~CharInfo() = default;
 
-PAGECHAR_INFO::PAGECHAR_INFO(const PAGECHAR_INFO&) = default;
-
-PAGECHAR_INFO::~PAGECHAR_INFO() {}
-
-CPDF_TextPage::CPDF_TextPage(const CPDF_Page* pPage, FPDFText_Direction flags)
-    : m_pPage(pPage),
-      m_parserflag(flags),
-      m_pPreTextObj(nullptr),
-      m_bIsParsed(false),
-      m_TextlineDir(TextOrientation::Unknown) {
-  m_TextBuf.EstimateSize(0, 10240);
-  m_DisplayMatrix =
-      pPage->GetDisplayMatrix(0, 0, static_cast<int>(pPage->GetPageWidth()),
-                              static_cast<int>(pPage->GetPageHeight()), 0);
+CPDF_TextPage::CPDF_TextPage(const CPDF_Page* pPage, bool rtl)
+    : m_pPage(pPage), m_rtl(rtl), m_DisplayMatrix(GetPageMatrix(pPage)) {
+  Init();
 }
 
-CPDF_TextPage::~CPDF_TextPage() {}
+CPDF_TextPage::~CPDF_TextPage() = default;
 
-bool CPDF_TextPage::IsControlChar(const PAGECHAR_INFO& charInfo) {
-  switch (charInfo.m_Unicode) {
-    case 0x2:
-    case 0x3:
-    case 0x93:
-    case 0x94:
-    case 0x96:
-    case 0x97:
-    case 0x98:
-    case 0xfffe:
-      return charInfo.m_Flag != FPDFTEXT_CHAR_HYPHEN;
-    default:
-      return false;
-  }
-}
-
-void CPDF_TextPage::ParseTextPage() {
-  m_bIsParsed = false;
-  m_TextBuf.Clear();
-  m_CharList.clear();
-  m_pPreTextObj = nullptr;
+void CPDF_TextPage::Init() {
+  m_TextBuf.SetAllocStep(10240);
   ProcessObject();
 
-  m_bIsParsed = true;
-  m_CharIndex.clear();
-  int nCount = pdfium::CollectionSize<int>(m_CharList);
+  const int nCount = CountChars();
   if (nCount)
-    m_CharIndex.push_back(0);
+    m_CharIndices.push_back(0);
 
-  for (int i = 0; i < nCount; i++) {
-    int indexSize = pdfium::CollectionSize<int>(m_CharIndex);
-    const PAGECHAR_INFO& charinfo = m_CharList[i];
-    if (charinfo.m_Flag == FPDFTEXT_CHAR_GENERATED ||
-        (charinfo.m_Unicode != 0 && !IsControlChar(charinfo))) {
-      if (indexSize % 2) {
-        m_CharIndex.push_back(1);
+  for (int i = 0; i < nCount; ++i) {
+    const CharInfo& charinfo = m_CharList[i];
+    if (charinfo.m_CharType == CPDF_TextPage::CharType::kGenerated ||
+        (charinfo.m_Unicode != 0 && !IsControlChar(charinfo)) ||
+        (charinfo.m_Unicode == 0 && charinfo.m_CharCode != 0)) {
+      if (m_CharIndices.size() % 2) {
+        m_CharIndices.push_back(1);
       } else {
-        if (indexSize <= 0)
+        if (m_CharIndices.empty())
           continue;
-        m_CharIndex[indexSize - 1] += 1;
+        m_CharIndices.back() += 1;
       }
     } else {
-      if (indexSize % 2) {
-        if (indexSize <= 0)
+      if (m_CharIndices.size() % 2) {
+        if (m_CharIndices.empty())
           continue;
-        m_CharIndex[indexSize - 1] = i + 1;
+        m_CharIndices.back() = i + 1;
       } else {
-        m_CharIndex.push_back(i + 1);
+        m_CharIndices.push_back(i + 1);
       }
     }
   }
-  int indexSize = pdfium::CollectionSize<int>(m_CharIndex);
-  if (indexSize % 2)
-    m_CharIndex.erase(m_CharIndex.begin() + indexSize - 1);
+  if (m_CharIndices.size() % 2)
+    m_CharIndices.pop_back();
 }
 
 int CPDF_TextPage::CountChars() const {
   return pdfium::CollectionSize<int>(m_CharList);
 }
 
-int CPDF_TextPage::CharIndexFromTextIndex(int TextIndex) const {
-  int indexSize = pdfium::CollectionSize<int>(m_CharIndex);
+int CPDF_TextPage::CharIndexFromTextIndex(int text_index) const {
   int count = 0;
-  for (int i = 0; i < indexSize; i += 2) {
-    count += m_CharIndex[i + 1];
-    if (count > TextIndex)
-      return TextIndex - count + m_CharIndex[i + 1] + m_CharIndex[i];
+  for (size_t i = 0; i < m_CharIndices.size(); i += 2) {
+    count += m_CharIndices[i + 1];
+    if (count > text_index)
+      return text_index - count + m_CharIndices[i + 1] + m_CharIndices[i];
   }
   return -1;
 }
 
-int CPDF_TextPage::TextIndexFromCharIndex(int CharIndex) const {
-  int indexSize = pdfium::CollectionSize<int>(m_CharIndex);
+int CPDF_TextPage::TextIndexFromCharIndex(int char_index) const {
   int count = 0;
-  for (int i = 0; i < indexSize; i += 2) {
-    count += m_CharIndex[i + 1];
-    if (m_CharIndex[i + 1] + m_CharIndex[i] > CharIndex) {
-      if (CharIndex - m_CharIndex[i] < 0)
-        return -1;
+  for (size_t i = 0; i < m_CharIndices.size(); i += 2) {
+    int text_index = char_index - m_CharIndices[i];
+    if (text_index < m_CharIndices[i + 1])
+      return text_index >= 0 ? text_index + count : -1;
 
-      return CharIndex - m_CharIndex[i] + count - m_CharIndex[i + 1];
-    }
+    count += m_CharIndices[i + 1];
   }
   return -1;
 }
 
 std::vector<CFX_FloatRect> CPDF_TextPage::GetRectArray(int start,
                                                        int nCount) const {
-  if (start < 0 || nCount == 0 || !m_bIsParsed)
-    return std::vector<CFX_FloatRect>();
+  std::vector<CFX_FloatRect> rects;
+  if (start < 0 || nCount == 0)
+    return rects;
 
-  if (nCount + start > pdfium::CollectionSize<int>(m_CharList) ||
-      nCount == -1) {
-    nCount = pdfium::CollectionSize<int>(m_CharList) - start;
-  }
+  const int nCharListSize = CountChars();
+  if (start >= nCharListSize)
+    return rects;
 
-  std::vector<CFX_FloatRect> rectArray;
+  if (nCount < 0 || start + nCount > nCharListSize)
+    nCount = nCharListSize - start;
+  ASSERT(nCount > 0);
+
   CPDF_TextObject* pCurObj = nullptr;
   CFX_FloatRect rect;
   int curPos = start;
   bool bFlagNewRect = true;
   while (nCount--) {
-    PAGECHAR_INFO info_curchar = m_CharList[curPos++];
-    if (info_curchar.m_Flag == FPDFTEXT_CHAR_GENERATED)
+    const CharInfo& info_curchar = m_CharList[curPos++];
+    if (info_curchar.m_CharType == CPDF_TextPage::CharType::kGenerated)
       continue;
-    if (info_curchar.m_CharBox.Width() < 0.01 ||
-        info_curchar.m_CharBox.Height() < 0.01) {
+    if (info_curchar.m_CharBox.Width() < kSizeEpsilon ||
+        info_curchar.m_CharBox.Height() < kSizeEpsilon) {
       continue;
     }
     if (!pCurObj)
       pCurObj = info_curchar.m_pTextObj.Get();
     if (pCurObj != info_curchar.m_pTextObj) {
-      rectArray.push_back(rect);
+      rects.push_back(rect);
       pCurObj = info_curchar.m_pTextObj.Get();
       bFlagNewRect = true;
     }
     if (bFlagNewRect) {
-      CFX_Matrix matrix = info_curchar.m_pTextObj->GetTextMatrix();
-      matrix.Concat(info_curchar.m_Matrix);
-
-      CFX_PointF origin = matrix.GetInverse().Transform(info_curchar.m_Origin);
-      rect.left = info_curchar.m_CharBox.left;
-      rect.right = info_curchar.m_CharBox.right;
-      if (pCurObj->GetFont()->GetTypeDescent()) {
-        rect.bottom = origin.y +
-                      pCurObj->GetFont()->GetTypeDescent() *
-                          pCurObj->GetFontSize() / 1000;
-
-        rect.bottom = matrix.Transform(CFX_PointF(origin.x, rect.bottom)).y;
-      } else {
-        rect.bottom = info_curchar.m_CharBox.bottom;
-      }
-      if (pCurObj->GetFont()->GetTypeAscent()) {
-        rect.top =
-            origin.y +
-            pCurObj->GetFont()->GetTypeAscent() * pCurObj->GetFontSize() / 1000;
-        float xPosTemp =
-            origin.x +
-            GetCharWidth(info_curchar.m_CharCode, pCurObj->GetFont()) *
-                pCurObj->GetFontSize() / 1000;
-        rect.top = matrix.Transform(CFX_PointF(xPosTemp, rect.top)).y;
-      } else {
-        rect.top = info_curchar.m_CharBox.top;
-      }
       bFlagNewRect = false;
       rect = info_curchar.m_CharBox;
       rect.Normalize();
-    } else {
-      info_curchar.m_CharBox.Normalize();
-      rect.left = std::min(rect.left, info_curchar.m_CharBox.left);
-      rect.right = std::max(rect.right, info_curchar.m_CharBox.right);
-      rect.top = std::max(rect.top, info_curchar.m_CharBox.top);
-      rect.bottom = std::min(rect.bottom, info_curchar.m_CharBox.bottom);
+      continue;
     }
+    rect.Union(info_curchar.m_CharBox);
   }
-  rectArray.push_back(rect);
-  return rectArray;
+  rects.push_back(rect);
+  return rects;
 }
 
 int CPDF_TextPage::GetIndexAtPos(const CFX_PointF& point,
                                  const CFX_SizeF& tolerance) const {
-  if (!m_bIsParsed)
-    return -3;
-
-  int pos = 0;
+  int pos;
   int NearPos = -1;
   double xdif = 5000;
   double ydif = 5000;
-  while (pos < pdfium::CollectionSize<int>(m_CharList)) {
-    PAGECHAR_INFO charinfo = m_CharList[pos];
-    CFX_FloatRect charrect = charinfo.m_CharBox;
-    if (charrect.Contains(point))
+  const int nCount = CountChars();
+  for (pos = 0; pos < nCount; ++pos) {
+    const CFX_FloatRect& orig_charrect = m_CharList[pos].m_CharBox;
+    if (orig_charrect.Contains(point))
       break;
-    if (tolerance.width > 0 || tolerance.height > 0) {
-      CFX_FloatRect charRectExt;
-      charrect.Normalize();
-      charRectExt.left = charrect.left - tolerance.width / 2;
-      charRectExt.right = charrect.right + tolerance.width / 2;
-      charRectExt.top = charrect.top + tolerance.height / 2;
-      charRectExt.bottom = charrect.bottom - tolerance.height / 2;
-      if (charRectExt.Contains(point)) {
-        double curXdif, curYdif;
-        curXdif = fabs(point.x - charrect.left) < fabs(point.x - charrect.right)
-                      ? fabs(point.x - charrect.left)
-                      : fabs(point.x - charrect.right);
-        curYdif = fabs(point.y - charrect.bottom) < fabs(point.y - charrect.top)
-                      ? fabs(point.y - charrect.bottom)
-                      : fabs(point.y - charrect.top);
-        if (curYdif + curXdif < xdif + ydif) {
-          ydif = curYdif;
-          xdif = curXdif;
-          NearPos = pos;
-        }
-      }
+
+    if (tolerance.width <= 0 && tolerance.height <= 0)
+      continue;
+
+    CFX_FloatRect charrect = orig_charrect;
+    charrect.Normalize();
+    CFX_FloatRect char_rect_ext(charrect.left - tolerance.width / 2,
+                                charrect.bottom - tolerance.height / 2,
+                                charrect.right + tolerance.width / 2,
+                                charrect.top + tolerance.height / 2);
+    if (!char_rect_ext.Contains(point))
+      continue;
+
+    double curXdif =
+        std::min(fabs(point.x - charrect.left), fabs(point.x - charrect.right));
+    double curYdif =
+        std::min(fabs(point.y - charrect.bottom), fabs(point.y - charrect.top));
+    if (curYdif + curXdif < xdif + ydif) {
+      ydif = curYdif;
+      xdif = curXdif;
+      NearPos = pos;
     }
-    ++pos;
   }
-  return pos < pdfium::CollectionSize<int>(m_CharList) ? pos : NearPos;
+  return pos < nCount ? pos : NearPos;
 }
 
-WideString CPDF_TextPage::GetTextByRect(const CFX_FloatRect& rect) const {
-  if (!m_bIsParsed)
-    return WideString();
-
+WideString CPDF_TextPage::GetTextByPredicate(
+    const std::function<bool(const CharInfo&)>& predicate) const {
   float posy = 0;
   bool IsContainPreChar = false;
   bool IsAddLineFeed = false;
   WideString strText;
   for (const auto& charinfo : m_CharList) {
-    if (IsRectIntersect(rect, charinfo.m_CharBox)) {
+    if (predicate(charinfo)) {
       if (fabs(posy - charinfo.m_Origin.y) > 0 && !IsContainPreChar &&
           IsAddLineFeed) {
         posy = charinfo.m_Origin.y;
@@ -368,9 +407,9 @@
       IsAddLineFeed = false;
       if (charinfo.m_Unicode)
         strText += charinfo.m_Unicode;
-    } else if (charinfo.m_Unicode == 32) {
-      if (IsContainPreChar && charinfo.m_Unicode) {
-        strText += charinfo.m_Unicode;
+    } else if (charinfo.m_Unicode == L' ') {
+      if (IsContainPreChar) {
+        strText += L' ';
         IsContainPreChar = false;
         IsAddLineFeed = false;
       }
@@ -382,97 +421,84 @@
   return strText;
 }
 
-void CPDF_TextPage::GetCharInfo(int index, FPDF_CHAR_INFO* info) const {
-  if (!m_bIsParsed || !pdfium::IndexInBounds(m_CharList, index))
-    return;
-
-  const PAGECHAR_INFO& charinfo = m_CharList[index];
-  info->m_Charcode = charinfo.m_CharCode;
-  info->m_Origin = charinfo.m_Origin;
-  info->m_Unicode = charinfo.m_Unicode;
-  info->m_Flag = charinfo.m_Flag;
-  info->m_CharBox = charinfo.m_CharBox;
-  info->m_pTextObj = charinfo.m_pTextObj;
-  if (charinfo.m_pTextObj && charinfo.m_pTextObj->GetFont())
-    info->m_FontSize = charinfo.m_pTextObj->GetFontSize();
-  else
-    info->m_FontSize = kDefaultFontSize;
-  info->m_Matrix = charinfo.m_Matrix;
+WideString CPDF_TextPage::GetTextByRect(const CFX_FloatRect& rect) const {
+  return GetTextByPredicate([&rect](const CharInfo& charinfo) {
+    return IsRectIntersect(rect, charinfo.m_CharBox);
+  });
 }
 
-void CPDF_TextPage::CheckMarkedContentObject(int32_t& start,
-                                             int32_t& nCount) const {
-  PAGECHAR_INFO charinfo = m_CharList[start];
-  PAGECHAR_INFO charinfo2 = m_CharList[start + nCount - 1];
-  if (FPDFTEXT_CHAR_PIECE != charinfo.m_Flag &&
-      FPDFTEXT_CHAR_PIECE != charinfo2.m_Flag) {
-    return;
-  }
-  if (FPDFTEXT_CHAR_PIECE == charinfo.m_Flag) {
-    PAGECHAR_INFO charinfo1 = charinfo;
-    int startIndex = start;
-    while (FPDFTEXT_CHAR_PIECE == charinfo1.m_Flag &&
-           charinfo1.m_Index == charinfo.m_Index) {
-      startIndex--;
-      if (startIndex < 0)
-        break;
-      charinfo1 = m_CharList[startIndex];
-    }
-    startIndex++;
-    start = startIndex;
-  }
-  if (FPDFTEXT_CHAR_PIECE == charinfo2.m_Flag) {
-    PAGECHAR_INFO charinfo3 = charinfo2;
-    int endIndex = start + nCount - 1;
-    while (FPDFTEXT_CHAR_PIECE == charinfo3.m_Flag &&
-           charinfo3.m_Index == charinfo2.m_Index) {
-      endIndex++;
-      if (endIndex >= pdfium::CollectionSize<int>(m_CharList))
-        break;
-      charinfo3 = m_CharList[endIndex];
-    }
-    endIndex--;
-    nCount = endIndex - start + 1;
-  }
+WideString CPDF_TextPage::GetTextByObject(
+    const CPDF_TextObject* pTextObj) const {
+  return GetTextByPredicate([pTextObj](const CharInfo& charinfo) {
+    return charinfo.m_pTextObj == pTextObj;
+  });
+}
+
+const CPDF_TextPage::CharInfo& CPDF_TextPage::GetCharInfo(size_t index) const {
+  CHECK(index < m_CharList.size());
+  return m_CharList[index];
+}
+
+float CPDF_TextPage::GetCharFontSize(size_t index) const {
+  CHECK(index < m_CharList.size());
+  const CPDF_TextObject* text_object = m_CharList[index].m_pTextObj.Get();
+  bool has_font = text_object && text_object->GetFont();
+  return has_font ? text_object->GetFontSize() : kDefaultFontSize;
 }
 
 WideString CPDF_TextPage::GetPageText(int start, int count) const {
-  if (start < 0 || start >= CountChars() || count <= 0 || !m_bIsParsed ||
-      m_CharList.empty() || m_TextBuf.GetLength() == 0) {
-    return L"";
+  if (start < 0 || start >= CountChars() || count <= 0 || m_CharList.empty() ||
+      m_TextBuf.IsEmpty()) {
+    return WideString();
   }
 
+  const int count_chars = CountChars();
   int text_start = TextIndexFromCharIndex(start);
-  if (text_start < 0)
-    return L"";
 
-  count = std::min(count, CountChars() - start);
+  // If the character at |start| is a non-printing character, then
+  // TextIndexFromCharIndex will return -1, so scan ahead to the first printing
+  // character.
+  while (text_start < 0) {
+    if (start >= count_chars)
+      return WideString();
+    start++;
+    text_start = TextIndexFromCharIndex(start);
+  }
+
+  count = std::min(count, count_chars - start);
 
   int last = start + count - 1;
   int text_last = TextIndexFromCharIndex(last);
-  if (text_last < 0 || text_last < text_start)
-    return L"";
+
+  // If the character at |last| is a non-printing character, then
+  // TextIndexFromCharIndex will return -1, so scan back to the last printing
+  // character.
+  while (text_last < 0) {
+    if (last < text_start)
+      return WideString();
+
+    last--;
+    text_last = TextIndexFromCharIndex(last);
+  }
+
+  if (text_last < text_start)
+    return WideString();
 
   int text_count = text_last - text_start + 1;
 
-  return WideString(m_TextBuf.AsStringView().Mid(
-      static_cast<size_t>(text_start), static_cast<size_t>(text_count)));
+  return WideString(m_TextBuf.AsStringView().Substr(text_start, text_count));
 }
 
 int CPDF_TextPage::CountRects(int start, int nCount) {
-  if (!m_bIsParsed || start < 0)
+  if (start < 0)
     return -1;
 
-  if (nCount == -1 ||
-      nCount + start > pdfium::CollectionSize<int>(m_CharList)) {
-    nCount = pdfium::CollectionSize<int>(m_CharList) - start;
-  }
   m_SelRects = GetRectArray(start, nCount);
   return pdfium::CollectionSize<int>(m_SelRects);
 }
 
 bool CPDF_TextPage::GetRect(int rectIndex, CFX_FloatRect* pRect) const {
-  if (!m_bIsParsed || !pdfium::IndexInBounds(m_SelRects, rectIndex))
+  if (!pdfium::IndexInBounds(m_SelRects, rectIndex))
     return false;
 
   *pRect = m_SelRects[rectIndex];
@@ -481,13 +507,12 @@
 
 CPDF_TextPage::TextOrientation CPDF_TextPage::FindTextlineFlowOrientation()
     const {
-  if (m_pPage->GetPageObjectList()->empty())
-    return TextOrientation::Unknown;
+  DCHECK_NE(m_pPage->GetPageObjectCount(), 0);
 
   const int32_t nPageWidth = static_cast<int32_t>(m_pPage->GetPageWidth());
   const int32_t nPageHeight = static_cast<int32_t>(m_pPage->GetPageHeight());
   if (nPageWidth <= 0 || nPageHeight <= 0)
-    return TextOrientation::Unknown;
+    return TextOrientation::kUnknown;
 
   std::vector<bool> nHorizontalMask(nPageWidth);
   std::vector<bool> nVerticalMask(nPageHeight);
@@ -496,15 +521,17 @@
   int32_t nEndH = 0;
   int32_t nStartV = nPageHeight;
   int32_t nEndV = 0;
-  for (const auto& pPageObj : *m_pPage->GetPageObjectList()) {
+  for (const auto& pPageObj : *m_pPage) {
     if (!pPageObj->IsText())
       continue;
 
-    int32_t minH = std::max(static_cast<int32_t>(pPageObj->m_Left), 0);
+    int32_t minH = std::max(static_cast<int32_t>(pPageObj->GetRect().left), 0);
     int32_t maxH =
-        std::min(static_cast<int32_t>(pPageObj->m_Right), nPageWidth);
-    int32_t minV = std::max(static_cast<int32_t>(pPageObj->m_Bottom), 0);
-    int32_t maxV = std::min(static_cast<int32_t>(pPageObj->m_Top), nPageHeight);
+        std::min(static_cast<int32_t>(pPageObj->GetRect().right), nPageWidth);
+    int32_t minV =
+        std::max(static_cast<int32_t>(pPageObj->GetRect().bottom), 0);
+    int32_t maxV =
+        std::min(static_cast<int32_t>(pPageObj->GetRect().top), nPageHeight);
     if (minH >= maxH || minV >= maxV)
       continue;
 
@@ -519,54 +546,53 @@
     nEndV = std::max(nEndV, maxV);
 
     if (fLineHeight <= 0.0f)
-      fLineHeight = pPageObj->m_Top - pPageObj->m_Bottom;
+      fLineHeight = pPageObj->GetRect().Height();
   }
   const int32_t nDoubleLineHeight = 2 * fLineHeight;
   if ((nEndV - nStartV) < nDoubleLineHeight)
-    return TextOrientation::Horizontal;
+    return TextOrientation::kHorizontal;
   if ((nEndH - nStartH) < nDoubleLineHeight)
-    return TextOrientation::Vertical;
+    return TextOrientation::kVertical;
 
   const float nSumH = MaskPercentFilled(nHorizontalMask, nStartH, nEndH);
   if (nSumH > 0.8f)
-    return TextOrientation::Horizontal;
+    return TextOrientation::kHorizontal;
 
   const float nSumV = MaskPercentFilled(nVerticalMask, nStartV, nEndV);
   if (nSumH > nSumV)
-    return TextOrientation::Horizontal;
+    return TextOrientation::kHorizontal;
   if (nSumH < nSumV)
-    return TextOrientation::Vertical;
-  return TextOrientation::Unknown;
+    return TextOrientation::kVertical;
+  return TextOrientation::kUnknown;
 }
 
 void CPDF_TextPage::AppendGeneratedCharacter(wchar_t unicode,
                                              const CFX_Matrix& formMatrix) {
-  PAGECHAR_INFO generateChar;
-  if (!GenerateCharInfo(unicode, generateChar))
+  Optional<CharInfo> pGenerateChar = GenerateCharInfo(unicode);
+  if (!pGenerateChar)
     return;
 
   m_TextBuf.AppendChar(unicode);
   if (!formMatrix.IsIdentity())
-    generateChar.m_Matrix = formMatrix;
-  m_CharList.push_back(generateChar);
+    pGenerateChar->m_Matrix = formMatrix;
+  m_CharList.push_back(*pGenerateChar);
 }
 
 void CPDF_TextPage::ProcessObject() {
-  if (m_pPage->GetPageObjectList()->empty())
+  if (m_pPage->GetPageObjectCount() == 0)
     return;
 
   m_TextlineDir = FindTextlineFlowOrientation();
-  const CPDF_PageObjectList* pObjList = m_pPage->GetPageObjectList();
-  for (auto it = pObjList->begin(); it != pObjList->end(); ++it) {
-    if (CPDF_PageObject* pObj = it->get()) {
-      if (pObj->IsText()) {
-        CFX_Matrix matrix;
-        ProcessTextObject(pObj->AsText(), matrix, pObjList, it);
-      } else if (pObj->IsForm()) {
-        CFX_Matrix formMatrix;
-        ProcessFormObject(pObj->AsForm(), formMatrix);
-      }
-    }
+  for (auto it = m_pPage->begin(); it != m_pPage->end(); ++it) {
+    CPDF_PageObject* pObj = it->get();
+    if (!pObj)
+      continue;
+
+    CFX_Matrix matrix;
+    if (pObj->IsText())
+      ProcessTextObject(pObj->AsText(), matrix, m_pPage.Get(), it);
+    else if (pObj->IsForm())
+      ProcessFormObject(pObj->AsForm(), matrix);
   }
   for (const auto& obj : m_LineObj)
     ProcessTextObject(obj);
@@ -577,97 +603,76 @@
 
 void CPDF_TextPage::ProcessFormObject(CPDF_FormObject* pFormObj,
                                       const CFX_Matrix& formMatrix) {
-  const CPDF_PageObjectList* pObjectList =
-      pFormObj->form()->GetPageObjectList();
-  if (pObjectList->empty())
-    return;
+  CFX_Matrix curFormMatrix = pFormObj->form_matrix() * formMatrix;
+  const CPDF_PageObjectHolder* pHolder = pFormObj->form();
+  for (auto it = pHolder->begin(); it != pHolder->end(); ++it) {
+    CPDF_PageObject* pPageObj = it->get();
+    if (!pPageObj)
+      continue;
 
-  CFX_Matrix curFormMatrix = pFormObj->form_matrix();
-  curFormMatrix.Concat(formMatrix);
-
-  for (auto it = pObjectList->begin(); it != pObjectList->end(); ++it) {
-    if (CPDF_PageObject* pPageObj = it->get()) {
-      if (pPageObj->IsText())
-        ProcessTextObject(pPageObj->AsText(), curFormMatrix, pObjectList, it);
-      else if (pPageObj->IsForm())
-        ProcessFormObject(pPageObj->AsForm(), curFormMatrix);
-    }
+    if (pPageObj->IsText())
+      ProcessTextObject(pPageObj->AsText(), curFormMatrix, pHolder, it);
+    else if (pPageObj->IsForm())
+      ProcessFormObject(pPageObj->AsForm(), curFormMatrix);
   }
 }
 
-int CPDF_TextPage::GetCharWidth(uint32_t charCode, CPDF_Font* pFont) const {
-  if (charCode == CPDF_Font::kInvalidCharCode)
-    return 0;
-
-  if (int w = pFont->GetCharWidthF(charCode))
-    return w;
-
-  ByteString str;
-  pFont->AppendChar(&str, charCode);
-  if (int w = pFont->GetStringWidth(str.c_str(), 1))
-    return w;
-
-  return pFont->GetCharBBox(charCode).Width();
-}
-
 void CPDF_TextPage::AddCharInfoByLRDirection(wchar_t wChar,
-                                             PAGECHAR_INFO info) {
-  if (IsControlChar(info)) {
-    info.m_Index = -1;
-    m_CharList.push_back(info);
+                                             const CharInfo& info) {
+  CharInfo info2 = info;
+  if (IsControlChar(info2)) {
+    info2.m_Index = -1;
+    m_CharList.push_back(info2);
     return;
   }
 
-  info.m_Index = m_TextBuf.GetLength();
-  if (wChar >= 0xFB00 && wChar <= 0xFB06) {
-    wchar_t* pDst = nullptr;
-    size_t nCount = Unicode_GetNormalization(wChar, pDst);
-    if (nCount >= 1) {
-      pDst = FX_Alloc(wchar_t, nCount);
-      Unicode_GetNormalization(wChar, pDst);
-      for (size_t nIndex = 0; nIndex < nCount; nIndex++) {
-        PAGECHAR_INFO info2 = info;
-        info2.m_Unicode = pDst[nIndex];
-        info2.m_Flag = FPDFTEXT_CHAR_PIECE;
-        m_TextBuf.AppendChar(info2.m_Unicode);
-        m_CharList.push_back(info2);
-      }
-      FX_Free(pDst);
-      return;
-    }
+  info2.m_Index = m_TextBuf.GetLength();
+  size_t nCount = 0;
+  if (wChar >= 0xFB00 && wChar <= 0xFB06)
+    nCount = Unicode_GetNormalization(wChar, nullptr);
+  if (nCount == 0) {
+    m_TextBuf.AppendChar(wChar);
+    m_CharList.push_back(info2);
+    return;
   }
-  m_TextBuf.AppendChar(wChar);
-  m_CharList.push_back(info);
+
+  std::unique_ptr<wchar_t, FxFreeDeleter> pDst(FX_Alloc(wchar_t, nCount));
+  Unicode_GetNormalization(wChar, pDst.get());
+  for (size_t nIndex = 0; nIndex < nCount; ++nIndex) {
+    info2.m_Unicode = pDst.get()[nIndex];
+    info2.m_CharType = CPDF_TextPage::CharType::kPiece;
+    m_TextBuf.AppendChar(info2.m_Unicode);
+    m_CharList.push_back(info2);
+  }
 }
 
 void CPDF_TextPage::AddCharInfoByRLDirection(wchar_t wChar,
-                                             PAGECHAR_INFO info) {
-  if (IsControlChar(info)) {
-    info.m_Index = -1;
-    m_CharList.push_back(info);
+                                             const CharInfo& info) {
+  CharInfo info2 = info;
+  if (IsControlChar(info2)) {
+    info2.m_Index = -1;
+    m_CharList.push_back(info2);
     return;
   }
 
-  info.m_Index = m_TextBuf.GetLength();
+  info2.m_Index = m_TextBuf.GetLength();
   wChar = FX_GetMirrorChar(wChar);
-  wchar_t* pDst = nullptr;
-  size_t nCount = Unicode_GetNormalization(wChar, pDst);
-  if (nCount >= 1) {
-    pDst = FX_Alloc(wchar_t, nCount);
-    Unicode_GetNormalization(wChar, pDst);
-    for (size_t nIndex = 0; nIndex < nCount; nIndex++) {
-      PAGECHAR_INFO info2 = info;
-      info2.m_Unicode = pDst[nIndex];
-      info2.m_Flag = FPDFTEXT_CHAR_PIECE;
-      m_TextBuf.AppendChar(info2.m_Unicode);
-      m_CharList.push_back(info2);
-    }
-    FX_Free(pDst);
+  size_t nCount = Unicode_GetNormalization(wChar, nullptr);
+  if (nCount == 0) {
+    info2.m_Unicode = wChar;
+    m_TextBuf.AppendChar(info2.m_Unicode);
+    m_CharList.push_back(info2);
     return;
   }
-  info.m_Unicode = wChar;
-  m_TextBuf.AppendChar(info.m_Unicode);
-  m_CharList.push_back(info);
+
+  std::unique_ptr<wchar_t, FxFreeDeleter> pDst(FX_Alloc(wchar_t, nCount));
+  Unicode_GetNormalization(wChar, pDst.get());
+  for (size_t nIndex = 0; nIndex < nCount; ++nIndex) {
+    info2.m_Unicode = pDst.get()[nIndex];
+    info2.m_CharType = CPDF_TextPage::CharType::kPiece;
+    m_TextBuf.AppendChar(info2.m_Unicode);
+    m_CharList.push_back(info2);
+  }
 }
 
 void CPDF_TextPage::CloseTempLine() {
@@ -676,7 +681,7 @@
 
   WideString str = m_TempTextBuf.MakeString();
   bool bPrevSpace = false;
-  for (size_t i = 0; i < str.GetLength(); i++) {
+  for (size_t i = 0; i < str.GetLength(); ++i) {
     if (str[i] != ' ') {
       bPrevSpace = false;
       continue;
@@ -685,12 +690,12 @@
       m_TempTextBuf.Delete(i, 1);
       m_TempCharList.erase(m_TempCharList.begin() + i);
       str.Delete(i);
-      i--;
+      --i;
     }
     bPrevSpace = true;
   }
   CFX_BidiString bidi(str);
-  if (m_parserflag == FPDFText_Direction::Right)
+  if (m_rtl)
     bidi.SetOverallDirectionRight();
   CFX_BidiChar::Direction eCurrentDirection = bidi.OverallDirection();
   for (const auto& segment : bidi) {
@@ -699,11 +704,11 @@
          eCurrentDirection == CFX_BidiChar::RIGHT)) {
       eCurrentDirection = CFX_BidiChar::RIGHT;
       for (int m = segment.start + segment.count; m > segment.start; --m)
-        AddCharInfoByRLDirection(bidi.CharAt(m - 1), m_TempCharList[m - 1]);
+        AddCharInfoByRLDirection(str[m - 1], m_TempCharList[m - 1]);
     } else {
       eCurrentDirection = CFX_BidiChar::LEFT;
-      for (int m = segment.start; m < segment.start + segment.count; m++)
-        AddCharInfoByLRDirection(bidi.CharAt(m), m_TempCharList[m]);
+      for (int m = segment.start; m < segment.start + segment.count; ++m)
+        AddCharInfoByLRDirection(str[m], m_TempCharList[m]);
     }
   }
   m_TempCharList.clear();
@@ -713,9 +718,9 @@
 void CPDF_TextPage::ProcessTextObject(
     CPDF_TextObject* pTextObj,
     const CFX_Matrix& formMatrix,
-    const CPDF_PageObjectList* pObjList,
-    CPDF_PageObjectList::const_iterator ObjPos) {
-  if (fabs(pTextObj->m_Right - pTextObj->m_Left) < 0.01f)
+    const CPDF_PageObjectHolder* pObjList,
+    CPDF_PageObjectHolder::const_iterator ObjPos) {
+  if (fabs(pTextObj->GetRect().Width()) < kSizeEpsilon)
     return;
 
   size_t count = m_LineObj.size();
@@ -737,30 +742,27 @@
   CPDF_TextObjectItem item;
   prev_Obj.m_pTextObj->GetItemInfo(nItem - 1, &item);
   float prev_width =
-      GetCharWidth(item.m_CharCode, prev_Obj.m_pTextObj->GetFont()) *
+      GetCharWidth(item.m_CharCode, prev_Obj.m_pTextObj->GetFont().Get()) *
       prev_Obj.m_pTextObj->GetFontSize() / 1000;
 
-  CFX_Matrix prev_matrix = prev_Obj.m_pTextObj->GetTextMatrix();
-  prev_width = fabs(prev_width);
-  prev_matrix.Concat(prev_Obj.m_formMatrix);
-  prev_width = prev_matrix.TransformDistance(prev_width);
+  CFX_Matrix prev_matrix =
+      prev_Obj.m_pTextObj->GetTextMatrix() * prev_Obj.m_formMatrix;
+  prev_width = prev_matrix.TransformDistance(fabs(prev_width));
   pTextObj->GetItemInfo(0, &item);
-  float this_width = GetCharWidth(item.m_CharCode, pTextObj->GetFont()) *
+  float this_width = GetCharWidth(item.m_CharCode, pTextObj->GetFont().Get()) *
                      pTextObj->GetFontSize() / 1000;
   this_width = fabs(this_width);
 
-  CFX_Matrix this_matrix = pTextObj->GetTextMatrix();
-  this_width = fabs(this_width);
-  this_matrix.Concat(formMatrix);
-  this_width = this_matrix.TransformDistance(this_width);
+  CFX_Matrix this_matrix = pTextObj->GetTextMatrix() * formMatrix;
+  this_width = this_matrix.TransformDistance(fabs(this_width));
 
-  float threshold = prev_width > this_width ? prev_width / 4 : this_width / 4;
+  float threshold = std::max(prev_width, this_width) / 4;
   CFX_PointF prev_pos = m_DisplayMatrix.Transform(
       prev_Obj.m_formMatrix.Transform(prev_Obj.m_pTextObj->GetPos()));
   CFX_PointF this_pos =
       m_DisplayMatrix.Transform(formMatrix.Transform(pTextObj->GetPos()));
   if (fabs(this_pos.y - prev_pos.y) > threshold * 2) {
-    for (size_t i = 0; i < count; i++)
+    for (size_t i = 0; i < count; ++i)
       ProcessTextObject(m_LineObj[i]);
     m_LineObj.clear();
     m_LineObj.push_back(Obj);
@@ -780,56 +782,54 @@
   m_LineObj.insert(m_LineObj.begin(), Obj);
 }
 
-FPDFText_MarkedContent CPDF_TextPage::PreMarkedContent(PDFTEXT_Obj Obj) {
+CPDF_TextPage::MarkedContentState CPDF_TextPage::PreMarkedContent(
+    PDFTEXT_Obj Obj) {
   CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get();
-  if (!pTextObj->m_ContentMark.HasRef())
-    return FPDFText_MarkedContent::Pass;
-
-  size_t nContentMark = pTextObj->m_ContentMark.CountItems();
-  if (nContentMark == 0)
-    return FPDFText_MarkedContent::Pass;
+  size_t nContentMarks = pTextObj->m_ContentMarks.CountItems();
+  if (nContentMarks == 0)
+    return MarkedContentState::kPass;
 
   WideString actText;
   bool bExist = false;
-  CPDF_Dictionary* pDict = nullptr;
-  for (size_t i = 0; i < nContentMark; ++i) {
-    const CPDF_ContentMarkItem& item = pTextObj->m_ContentMark.GetItem(i);
-    pDict = item.GetParam();
+  const CPDF_Dictionary* pDict = nullptr;
+  for (size_t i = 0; i < nContentMarks; ++i) {
+    const CPDF_ContentMarkItem* item = pTextObj->m_ContentMarks.GetItem(i);
+    pDict = item->GetParam();
     if (!pDict)
       continue;
-    CPDF_String* temp = ToString(pDict->GetObjectFor("ActualText"));
+    const CPDF_String* temp = ToString(pDict->GetObjectFor("ActualText"));
     if (temp) {
       bExist = true;
       actText = temp->GetUnicodeText();
     }
   }
   if (!bExist)
-    return FPDFText_MarkedContent::Pass;
+    return MarkedContentState::kPass;
 
-  if (m_pPreTextObj) {
-    const CPDF_ContentMark& mark = m_pPreTextObj->m_ContentMark;
-    if (mark.HasRef() && mark.CountItems() == nContentMark &&
-        mark.GetItem(nContentMark - 1).GetParam() == pDict) {
-      return FPDFText_MarkedContent::Done;
+  if (m_pPrevTextObj) {
+    const CPDF_ContentMarks& marks = m_pPrevTextObj->m_ContentMarks;
+    if (marks.CountItems() == nContentMarks &&
+        marks.GetItem(nContentMarks - 1)->GetParam() == pDict) {
+      return MarkedContentState::kDone;
     }
   }
 
   if (actText.IsEmpty())
-    return FPDFText_MarkedContent::Pass;
+    return MarkedContentState::kPass;
 
-  CPDF_Font* pFont = pTextObj->GetFont();
+  RetainPtr<CPDF_Font> pFont = pTextObj->GetFont();
   bExist = false;
-  for (size_t i = 0; i < actText.GetLength(); i++) {
+  for (size_t i = 0; i < actText.GetLength(); ++i) {
     if (pFont->CharCodeFromUnicode(actText[i]) != CPDF_Font::kInvalidCharCode) {
       bExist = true;
       break;
     }
   }
   if (!bExist)
-    return FPDFText_MarkedContent::Pass;
+    return MarkedContentState::kPass;
 
   bExist = false;
-  for (size_t i = 0; i < actText.GetLength(); i++) {
+  for (size_t i = 0; i < actText.GetLength(); ++i) {
     wchar_t wChar = actText[i];
     if ((wChar > 0x80 && wChar < 0xFFFD) || (wChar <= 0x80 && isprint(wChar))) {
       bExist = true;
@@ -837,47 +837,44 @@
     }
   }
   if (!bExist)
-    return FPDFText_MarkedContent::Done;
+    return MarkedContentState::kDone;
 
-  return FPDFText_MarkedContent::Delay;
+  return MarkedContentState::kDelay;
 }
 
 void CPDF_TextPage::ProcessMarkedContent(PDFTEXT_Obj Obj) {
   CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get();
-  if (!pTextObj->m_ContentMark.HasRef())
-    return;
 
-  int nContentMark = pTextObj->m_ContentMark.CountItems();
-  if (nContentMark < 1)
+  size_t nContentMarks = pTextObj->m_ContentMarks.CountItems();
+  if (nContentMarks == 0)
     return;
 
   WideString actText;
-  for (int n = 0; n < nContentMark; n++) {
-    const CPDF_ContentMarkItem& item = pTextObj->m_ContentMark.GetItem(n);
-    CPDF_Dictionary* pDict = item.GetParam();
+  for (size_t n = 0; n < nContentMarks; ++n) {
+    const CPDF_ContentMarkItem* item = pTextObj->m_ContentMarks.GetItem(n);
+    const CPDF_Dictionary* pDict = item->GetParam();
     if (pDict)
       actText = pDict->GetUnicodeTextFor("ActualText");
   }
   if (actText.IsEmpty())
     return;
 
-  CPDF_Font* pFont = pTextObj->GetFont();
-  CFX_Matrix matrix = pTextObj->GetTextMatrix();
-  matrix.Concat(Obj.m_formMatrix);
+  RetainPtr<CPDF_Font> pFont = pTextObj->GetFont();
+  CFX_Matrix matrix = pTextObj->GetTextMatrix() * Obj.m_formMatrix;
 
-  for (size_t k = 0; k < actText.GetLength(); k++) {
+  for (size_t k = 0; k < actText.GetLength(); ++k) {
     wchar_t wChar = actText[k];
     if (wChar <= 0x80 && !isprint(wChar))
       wChar = 0x20;
     if (wChar >= 0xFFFD)
       continue;
 
-    PAGECHAR_INFO charinfo;
+    CharInfo charinfo;
     charinfo.m_Origin = pTextObj->GetPos();
     charinfo.m_Index = m_TextBuf.GetLength();
     charinfo.m_Unicode = wChar;
     charinfo.m_CharCode = pFont->CharCodeFromUnicode(wChar);
-    charinfo.m_Flag = FPDFTEXT_CHAR_PIECE;
+    charinfo.m_CharType = CPDF_TextPage::CharType::kPiece;
     charinfo.m_pTextObj = pTextObj;
     charinfo.m_CharBox = pTextObj->GetRect();
     charinfo.m_Matrix = matrix;
@@ -887,94 +884,72 @@
 }
 
 void CPDF_TextPage::FindPreviousTextObject() {
-  if (m_TempCharList.empty() && m_CharList.empty())
+  const CharInfo* pPrevCharInfo = GetPrevCharInfo();
+  if (!pPrevCharInfo)
     return;
 
-  PAGECHAR_INFO preChar =
-      m_TempCharList.empty() ? m_CharList.back() : m_TempCharList.back();
-
-  if (preChar.m_pTextObj)
-    m_pPreTextObj = preChar.m_pTextObj;
+  if (pPrevCharInfo->m_pTextObj)
+    m_pPrevTextObj = pPrevCharInfo->m_pTextObj;
 }
 
 void CPDF_TextPage::SwapTempTextBuf(int32_t iCharListStartAppend,
                                     int32_t iBufStartAppend) {
   int32_t i = iCharListStartAppend;
   int32_t j = pdfium::CollectionSize<int32_t>(m_TempCharList) - 1;
-  for (; i < j; i++, j--) {
+  for (; i < j; ++i, --j) {
     std::swap(m_TempCharList[i], m_TempCharList[j]);
     std::swap(m_TempCharList[i].m_Index, m_TempCharList[j].m_Index);
   }
   wchar_t* pTempBuffer = m_TempTextBuf.GetBuffer();
   i = iBufStartAppend;
   j = m_TempTextBuf.GetLength() - 1;
-  for (; i < j; i++, j--)
+  for (; i < j; ++i, --j)
     std::swap(pTempBuffer[i], pTempBuffer[j]);
 }
 
-bool CPDF_TextPage::IsRightToLeft(const CPDF_TextObject* pTextObj,
-                                  const CPDF_Font* pFont,
-                                  size_t nItems) const {
-  WideString str;
-  for (size_t i = 0; i < nItems; ++i) {
-    CPDF_TextObjectItem item;
-    pTextObj->GetItemInfo(i, &item);
-    if (item.m_CharCode == static_cast<uint32_t>(-1))
-      continue;
-    WideString wstrItem = pFont->UnicodeFromCharCode(item.m_CharCode);
-    wchar_t wChar = !wstrItem.IsEmpty() ? wstrItem[0] : 0;
-    if (wChar == 0)
-      wChar = item.m_CharCode;
-    if (wChar)
-      str += wChar;
-  }
-  return CFX_BidiString(str).OverallDirection() == CFX_BidiChar::RIGHT;
-}
-
 void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) {
   CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get();
-  if (fabs(pTextObj->m_Right - pTextObj->m_Left) < 0.01f)
+  if (fabs(pTextObj->GetRect().Width()) < kSizeEpsilon)
     return;
-  CFX_Matrix formMatrix = Obj.m_formMatrix;
-  CPDF_Font* pFont = pTextObj->GetFont();
-  CFX_Matrix matrix = pTextObj->GetTextMatrix();
-  matrix.Concat(formMatrix);
 
-  FPDFText_MarkedContent ePreMKC = PreMarkedContent(Obj);
-  if (ePreMKC == FPDFText_MarkedContent::Done) {
-    m_pPreTextObj = pTextObj;
-    m_perMatrix = formMatrix;
+  CFX_Matrix formMatrix = Obj.m_formMatrix;
+  RetainPtr<CPDF_Font> pFont = pTextObj->GetFont();
+  CFX_Matrix matrix = pTextObj->GetTextMatrix() * formMatrix;
+  MarkedContentState ePreMKC = PreMarkedContent(Obj);
+  if (ePreMKC == MarkedContentState::kDone) {
+    m_pPrevTextObj = pTextObj;
+    m_PrevMatrix = formMatrix;
     return;
   }
-  GenerateCharacter result = GenerateCharacter::None;
-  if (m_pPreTextObj) {
+  GenerateCharacter result = GenerateCharacter::kNone;
+  if (m_pPrevTextObj) {
     result = ProcessInsertObject(pTextObj, formMatrix);
-    if (result == GenerateCharacter::LineBreak)
+    if (result == GenerateCharacter::kLineBreak)
       m_CurlineRect = Obj.m_pTextObj->GetRect();
     else
       m_CurlineRect.Union(Obj.m_pTextObj->GetRect());
 
     switch (result) {
-      case GenerateCharacter::None:
+      case GenerateCharacter::kNone:
         break;
-      case GenerateCharacter::Space: {
-        PAGECHAR_INFO generateChar;
-        if (GenerateCharInfo(TEXT_SPACE_CHAR, generateChar)) {
+      case GenerateCharacter::kSpace: {
+        Optional<CharInfo> pGenerateChar = GenerateCharInfo(L' ');
+        if (pGenerateChar) {
           if (!formMatrix.IsIdentity())
-            generateChar.m_Matrix = formMatrix;
-          m_TempTextBuf.AppendChar(TEXT_SPACE_CHAR);
-          m_TempCharList.push_back(generateChar);
+            pGenerateChar->m_Matrix = formMatrix;
+          m_TempTextBuf.AppendChar(L' ');
+          m_TempCharList.push_back(*pGenerateChar);
         }
         break;
       }
-      case GenerateCharacter::LineBreak:
+      case GenerateCharacter::kLineBreak:
         CloseTempLine();
         if (m_TextBuf.GetSize()) {
-          AppendGeneratedCharacter(TEXT_RETURN_CHAR, formMatrix);
-          AppendGeneratedCharacter(TEXT_LINEFEED_CHAR, formMatrix);
+          AppendGeneratedCharacter(L'\r', formMatrix);
+          AppendGeneratedCharacter(L'\n', formMatrix);
         }
         break;
-      case GenerateCharacter::Hyphen:
+      case GenerateCharacter::kHyphen:
         if (pTextObj->CountChars() == 1) {
           CPDF_TextObjectItem item;
           pTextObj->GetCharInfo(0, &item);
@@ -983,19 +958,18 @@
           if (wstrItem.IsEmpty())
             wstrItem += (wchar_t)item.m_CharCode;
           wchar_t curChar = wstrItem[0];
-          if (curChar == 0x2D || curChar == 0xAD)
+          if (IsHyphenCode(curChar))
             return;
         }
         while (m_TempTextBuf.GetSize() > 0 &&
-               m_TempTextBuf.AsStringView()[m_TempTextBuf.GetLength() - 1] ==
-                   0x20) {
+               m_TempTextBuf.AsStringView().Back() == 0x20) {
           m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
           m_TempCharList.pop_back();
         }
-        PAGECHAR_INFO* charinfo = &m_TempCharList.back();
+        CharInfo* charinfo = &m_TempCharList.back();
         m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
         charinfo->m_Unicode = 0x2;
-        charinfo->m_Flag = FPDFTEXT_CHAR_HYPHEN;
+        charinfo->m_CharType = CPDF_TextPage::CharType::kHyphen;
         m_TempTextBuf.AppendChar(0xfffe);
         break;
     }
@@ -1003,18 +977,17 @@
     m_CurlineRect = Obj.m_pTextObj->GetRect();
   }
 
-  if (ePreMKC == FPDFText_MarkedContent::Delay) {
+  if (ePreMKC == MarkedContentState::kDelay) {
     ProcessMarkedContent(Obj);
-    m_pPreTextObj = pTextObj;
-    m_perMatrix = formMatrix;
+    m_pPrevTextObj = pTextObj;
+    m_PrevMatrix = formMatrix;
     return;
   }
-  m_pPreTextObj = pTextObj;
-  m_perMatrix = formMatrix;
-  size_t nItems = pTextObj->CountItems();
+  m_pPrevTextObj = pTextObj;
+  m_PrevMatrix = formMatrix;
   float baseSpace = CalculateBaseSpace(pTextObj, matrix);
 
-  const bool bR2L = IsRightToLeft(pTextObj, pFont, nItems);
+  const bool bR2L = IsRightToLeft(*pTextObj, *pFont);
   const bool bIsBidiAndMirrorInverse =
       bR2L && (matrix.a * matrix.d - matrix.b * matrix.c) < 0;
   int32_t iBufStartAppend = m_TempTextBuf.GetLength();
@@ -1022,15 +995,16 @@
       pdfium::CollectionSize<int32_t>(m_TempCharList);
 
   float spacing = 0;
+  const size_t nItems = pTextObj->CountItems();
   for (size_t i = 0; i < nItems; ++i) {
     CPDF_TextObjectItem item;
-    PAGECHAR_INFO charinfo;
+    CharInfo charinfo;
     pTextObj->GetItemInfo(i, &item);
-    if (item.m_CharCode == static_cast<uint32_t>(-1)) {
+    if (item.m_CharCode == 0xffffffff) {
       WideString str = m_TempTextBuf.MakeString();
       if (str.IsEmpty())
         str = m_TextBuf.AsStringView();
-      if (str.IsEmpty() || str[str.GetLength() - 1] == TEXT_SPACE_CHAR)
+      if (str.IsEmpty() || str.Back() == L' ')
         continue;
 
       float fontsize_h = pTextObj->m_TextState.GetFontSizeH();
@@ -1044,7 +1018,6 @@
       spacing -= matrix.TransformDistance(fabs(charSpace));
     spacing -= baseSpace;
     if (spacing && i > 0) {
-      int last_width = 0;
       float fontsize_h = pTextObj->m_TextState.GetFontSizeH();
       uint32_t space_charcode = pFont->CharCodeFromUnicode(' ');
       float threshold = 0;
@@ -1055,19 +1028,16 @@
       else
         threshold /= 2;
       if (threshold == 0) {
-        threshold = fontsize_h;
-        int this_width = abs(GetCharWidth(item.m_CharCode, pFont));
-        threshold =
-            this_width > last_width ? (float)this_width : (float)last_width;
-        threshold = NormalizeThreshold(threshold);
+        threshold = GetCharWidth(item.m_CharCode, pFont.Get());
+        threshold = NormalizeThreshold(threshold, 300, 500, 700);
         threshold = fontsize_h * threshold / 1000;
       }
       if (threshold && (spacing && spacing >= threshold)) {
-        charinfo.m_Unicode = TEXT_SPACE_CHAR;
-        charinfo.m_Flag = FPDFTEXT_CHAR_GENERATED;
+        charinfo.m_Unicode = L' ';
+        charinfo.m_CharType = CPDF_TextPage::CharType::kGenerated;
         charinfo.m_pTextObj = pTextObj;
         charinfo.m_Index = m_TextBuf.GetLength();
-        m_TempTextBuf.AppendChar(TEXT_SPACE_CHAR);
+        m_TempTextBuf.AppendChar(L' ');
         charinfo.m_CharCode = CPDF_Font::kInvalidCharCode;
         charinfo.m_Matrix = formMatrix;
         charinfo.m_Origin = matrix.Transform(item.m_Origin);
@@ -1088,29 +1058,25 @@
     }
     charinfo.m_Index = -1;
     charinfo.m_CharCode = item.m_CharCode;
-    if (bNoUnicode)
-      charinfo.m_Flag = FPDFTEXT_CHAR_UNUNICODE;
-    else
-      charinfo.m_Flag = FPDFTEXT_CHAR_NORMAL;
-
+    charinfo.m_CharType = bNoUnicode ? CPDF_TextPage::CharType::kNotUnicode
+                                     : CPDF_TextPage::CharType::kNormal;
     charinfo.m_pTextObj = pTextObj;
     charinfo.m_Origin = matrix.Transform(item.m_Origin);
 
-    FX_RECT rect =
+    const FX_RECT rect =
         charinfo.m_pTextObj->GetFont()->GetCharBBox(charinfo.m_CharCode);
-    charinfo.m_CharBox.top =
-        rect.top * pTextObj->GetFontSize() / 1000 + item.m_Origin.y;
-    charinfo.m_CharBox.left =
-        rect.left * pTextObj->GetFontSize() / 1000 + item.m_Origin.x;
-    charinfo.m_CharBox.right =
-        rect.right * pTextObj->GetFontSize() / 1000 + item.m_Origin.x;
-    charinfo.m_CharBox.bottom =
-        rect.bottom * pTextObj->GetFontSize() / 1000 + item.m_Origin.y;
-    if (fabsf(charinfo.m_CharBox.top - charinfo.m_CharBox.bottom) < 0.01f) {
+    const float fFontSize = pTextObj->GetFontSize() / 1000;
+    charinfo.m_CharBox.top = rect.top * fFontSize + item.m_Origin.y;
+    charinfo.m_CharBox.left = rect.left * fFontSize + item.m_Origin.x;
+    charinfo.m_CharBox.right = rect.right * fFontSize + item.m_Origin.x;
+    charinfo.m_CharBox.bottom = rect.bottom * fFontSize + item.m_Origin.y;
+    if (fabsf(charinfo.m_CharBox.top - charinfo.m_CharBox.bottom) <
+        kSizeEpsilon) {
       charinfo.m_CharBox.top =
           charinfo.m_CharBox.bottom + pTextObj->GetFontSize();
     }
-    if (fabsf(charinfo.m_CharBox.right - charinfo.m_CharBox.left) < 0.01f) {
+    if (fabsf(charinfo.m_CharBox.right - charinfo.m_CharBox.left) <
+        kSizeEpsilon) {
       charinfo.m_CharBox.right =
           charinfo.m_CharBox.left + pTextObj->GetCharWidth(charinfo.m_CharCode);
     }
@@ -1125,11 +1091,12 @@
     int nTotal = wstrItem.GetLength();
     bool bDel = false;
     const int count = std::min(pdfium::CollectionSize<int>(m_TempCharList), 7);
+    constexpr float kTextCharRatioGapDelta = 0.07f;
     float threshold = charinfo.m_Matrix.TransformXDistance(
-        (float)TEXT_CHARRATIO_GAPDELTA * pTextObj->GetFontSize());
+        kTextCharRatioGapDelta * pTextObj->GetFontSize());
     for (int n = pdfium::CollectionSize<int>(m_TempCharList);
-         n > pdfium::CollectionSize<int>(m_TempCharList) - count; n--) {
-      const PAGECHAR_INFO& charinfo1 = m_TempCharList[n - 1];
+         n > pdfium::CollectionSize<int>(m_TempCharList) - count; --n) {
+      const CharInfo& charinfo1 = m_TempCharList[n - 1];
       CFX_PointF diff = charinfo1.m_Origin - charinfo.m_Origin;
       if (charinfo1.m_CharCode == charinfo.m_CharCode &&
           charinfo1.m_pTextObj->GetFont() == charinfo.m_pTextObj->GetFont() &&
@@ -1139,7 +1106,7 @@
       }
     }
     if (!bDel) {
-      for (int nIndex = 0; nIndex < nTotal; nIndex++) {
+      for (int nIndex = 0; nIndex < nTotal; ++nIndex) {
         charinfo.m_Unicode = wstrItem[nIndex];
         if (charinfo.m_Unicode) {
           charinfo.m_Index = m_TextBuf.GetLength();
@@ -1151,7 +1118,7 @@
       }
     } else if (i == 0) {
       WideString str = m_TempTextBuf.MakeString();
-      if (!str.IsEmpty() && str[str.GetLength() - 1] == TEXT_SPACE_CHAR) {
+      if (!str.IsEmpty() && str.Back() == L' ') {
         m_TempTextBuf.Delete(m_TempTextBuf.GetLength() - 1, 1);
         m_TempCharList.pop_back();
       }
@@ -1175,20 +1142,19 @@
   first.m_Origin = textMatrix.Transform(first.m_Origin);
   last.m_Origin = textMatrix.Transform(last.m_Origin);
 
+  static constexpr float kEpsilon = 0.0001f;
   float dX = fabs(last.m_Origin.x - first.m_Origin.x);
   float dY = fabs(last.m_Origin.y - first.m_Origin.y);
-  if (dX <= 0.0001f && dY <= 0.0001f)
-    return TextOrientation::Unknown;
+  if (dX <= kEpsilon && dY <= kEpsilon)
+    return TextOrientation::kUnknown;
 
+  static constexpr float kThreshold = 0.0872f;
   CFX_VectorF v(dX, dY);
   v.Normalize();
-  if (v.y <= 0.0872f)
-    return v.x <= 0.0872f ? m_TextlineDir : TextOrientation::Horizontal;
-
-  if (v.x <= 0.0872f)
-    return TextOrientation::Vertical;
-
-  return m_TextlineDir;
+  bool bXUnderThreshold = v.x <= kThreshold;
+  if (v.y <= kThreshold)
+    return bXUnderThreshold ? m_TextlineDir : TextOrientation::kHorizontal;
+  return bXUnderThreshold ? TextOrientation::kVertical : m_TextlineDir;
 }
 
 bool CPDF_TextPage::IsHyphen(wchar_t curChar) const {
@@ -1200,7 +1166,7 @@
     return false;
 
   auto iter = curText.rbegin();
-  for (; (iter + 1) != curText.rend() && *iter == 0x20; iter++) {
+  for (; (iter + 1) != curText.rend() && *iter == 0x20; ++iter) {
     // Do nothing
   }
 
@@ -1209,20 +1175,20 @@
 
   if ((iter + 1) != curText.rend()) {
     iter++;
-    if (FXSYS_iswalpha(*iter) && FXSYS_iswalpha(*iter))
+    if (FXSYS_iswalpha(*iter) && FXSYS_iswalnum(curChar))
       return true;
   }
 
-  const PAGECHAR_INFO* preInfo;
-  if (!m_TempCharList.empty())
-    preInfo = &m_TempCharList.back();
-  else if (!m_CharList.empty())
-    preInfo = &m_CharList.back();
-  else
-    return false;
+  const CharInfo* pPrevCharInfo = GetPrevCharInfo();
+  return pPrevCharInfo &&
+         pPrevCharInfo->m_CharType == CPDF_TextPage::CharType::kPiece &&
+         IsHyphenCode(pPrevCharInfo->m_Unicode);
+}
 
-  return FPDFTEXT_CHAR_PIECE == preInfo->m_Flag &&
-         IsHyphenCode(preInfo->m_Unicode);
+const CPDF_TextPage::CharInfo* CPDF_TextPage::GetPrevCharInfo() const {
+  if (!m_TempCharList.empty())
+    return &m_TempCharList.back();
+  return !m_CharList.empty() ? &m_CharList.back() : nullptr;
 }
 
 CPDF_TextPage::GenerateCharacter CPDF_TextPage::ProcessInsertObject(
@@ -1230,62 +1196,49 @@
     const CFX_Matrix& formMatrix) {
   FindPreviousTextObject();
   TextOrientation WritingMode = GetTextObjectWritingMode(pObj);
-  if (WritingMode == TextOrientation::Unknown)
-    WritingMode = GetTextObjectWritingMode(m_pPreTextObj.Get());
+  if (WritingMode == TextOrientation::kUnknown)
+    WritingMode = GetTextObjectWritingMode(m_pPrevTextObj.Get());
 
-  size_t nItem = m_pPreTextObj->CountItems();
+  size_t nItem = m_pPrevTextObj->CountItems();
   if (nItem == 0)
-    return GenerateCharacter::None;
+    return GenerateCharacter::kNone;
 
   CPDF_TextObjectItem PrevItem;
-  m_pPreTextObj->GetItemInfo(nItem - 1, &PrevItem);
+  m_pPrevTextObj->GetItemInfo(nItem - 1, &PrevItem);
 
   CPDF_TextObjectItem item;
   pObj->GetItemInfo(0, &item);
 
-  CFX_FloatRect this_rect = pObj->GetRect();
-  CFX_FloatRect prev_rect = m_pPreTextObj->GetRect();
+  const CFX_FloatRect& this_rect = pObj->GetRect();
+  const CFX_FloatRect& prev_rect = m_pPrevTextObj->GetRect();
 
   WideString wstrItem = pObj->GetFont()->UnicodeFromCharCode(item.m_CharCode);
   if (wstrItem.IsEmpty())
     wstrItem += static_cast<wchar_t>(item.m_CharCode);
   wchar_t curChar = wstrItem[0];
-  if (WritingMode == TextOrientation::Horizontal) {
-    if (this_rect.Height() > 4.5 && prev_rect.Height() > 4.5) {
-      float top = this_rect.top < prev_rect.top ? this_rect.top : prev_rect.top;
-      float bottom = this_rect.bottom > prev_rect.bottom ? this_rect.bottom
-                                                         : prev_rect.bottom;
-      if (bottom >= top) {
-        return IsHyphen(curChar) ? GenerateCharacter::Hyphen
-                                 : GenerateCharacter::LineBreak;
-      }
+  if (WritingMode == TextOrientation::kHorizontal) {
+    if (EndHorizontalLine(this_rect, prev_rect)) {
+      return IsHyphen(curChar) ? GenerateCharacter::kHyphen
+                               : GenerateCharacter::kLineBreak;
     }
-  } else if (WritingMode == TextOrientation::Vertical) {
-    if (this_rect.Width() > pObj->GetFontSize() * 0.1f &&
-        prev_rect.Width() > m_pPreTextObj->GetFontSize() * 0.1f) {
-      float left = this_rect.left > m_CurlineRect.left ? this_rect.left
-                                                       : m_CurlineRect.left;
-      float right = this_rect.right < m_CurlineRect.right ? this_rect.right
-                                                          : m_CurlineRect.right;
-      if (right <= left) {
-        return IsHyphen(curChar) ? GenerateCharacter::Hyphen
-                                 : GenerateCharacter::LineBreak;
-      }
+  } else if (WritingMode == TextOrientation::kVertical) {
+    if (EndVerticalLine(this_rect, prev_rect, m_CurlineRect,
+                        pObj->GetFontSize(), m_pPrevTextObj->GetFontSize())) {
+      return IsHyphen(curChar) ? GenerateCharacter::kHyphen
+                               : GenerateCharacter::kLineBreak;
     }
   }
 
   float last_pos = PrevItem.m_Origin.x;
-  int nLastWidth = GetCharWidth(PrevItem.m_CharCode, m_pPreTextObj->GetFont());
-  float last_width = nLastWidth * m_pPreTextObj->GetFontSize() / 1000;
+  uint32_t nLastWidth =
+      GetCharWidth(PrevItem.m_CharCode, m_pPrevTextObj->GetFont().Get());
+  float last_width = nLastWidth * m_pPrevTextObj->GetFontSize() / 1000;
   last_width = fabs(last_width);
-  int nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont());
-  float this_width = nThisWidth * pObj->GetFontSize() / 1000;
-  this_width = fabs(this_width);
-  float threshold = last_width > this_width ? last_width / 4 : this_width / 4;
+  uint32_t nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont().Get());
+  float this_width = fabs(nThisWidth * pObj->GetFontSize() / 1000);
+  float threshold = std::max(last_width, this_width) / 4;
 
-  CFX_Matrix prev_matrix = m_pPreTextObj->GetTextMatrix();
-  prev_matrix.Concat(m_perMatrix);
-
+  CFX_Matrix prev_matrix = m_pPrevTextObj->GetTextMatrix() * m_PrevMatrix;
   CFX_Matrix prev_reverse = prev_matrix.GetInverse();
 
   CFX_PointF pos = prev_reverse.Transform(formMatrix.Transform(pObj->GetPos()));
@@ -1293,101 +1246,86 @@
     threshold = prev_reverse.TransformDistance(threshold);
 
   bool bNewline = false;
-  if (WritingMode == TextOrientation::Horizontal) {
-    CFX_FloatRect rect1(m_pPreTextObj->m_Left, pObj->m_Bottom,
-                        m_pPreTextObj->m_Right, pObj->m_Top);
-    CFX_FloatRect rect2 = m_pPreTextObj->GetRect();
-    CFX_FloatRect rect3 = rect1;
-    rect1.Intersect(rect2);
-    if ((rect1.IsEmpty() && rect2.Height() > 5 && rect3.Height() > 5) ||
+  if (WritingMode == TextOrientation::kHorizontal) {
+    CFX_FloatRect rect = m_pPrevTextObj->GetRect();
+    float rect_height = rect.Height();
+    rect.Normalize();
+    if ((rect.IsEmpty() && rect_height > 5) ||
         ((pos.y > threshold * 2 || pos.y < threshold * -3) &&
-         (fabs(pos.y) < 1 ? fabs(pos.x) < fabs(pos.y) : true))) {
+         (fabs(pos.y) >= 1 || fabs(pos.y) > fabs(pos.x)))) {
       bNewline = true;
       if (nItem > 1) {
         CPDF_TextObjectItem tempItem;
-        m_pPreTextObj->GetItemInfo(0, &tempItem);
-        CFX_Matrix m = m_pPreTextObj->GetTextMatrix();
+        m_pPrevTextObj->GetItemInfo(0, &tempItem);
+        CFX_Matrix m = m_pPrevTextObj->GetTextMatrix();
         if (PrevItem.m_Origin.x > tempItem.m_Origin.x &&
             m_DisplayMatrix.a > 0.9 && m_DisplayMatrix.b < 0.1 &&
             m_DisplayMatrix.c < 0.1 && m_DisplayMatrix.d < -0.9 && m.b < 0.1 &&
             m.c < 0.1) {
-          CFX_FloatRect re(0, m_pPreTextObj->m_Bottom, 1000,
-                           m_pPreTextObj->m_Top);
+          CFX_FloatRect re(0, m_pPrevTextObj->GetRect().bottom, 1000,
+                           m_pPrevTextObj->GetRect().top);
           if (re.Contains(pObj->GetPos())) {
             bNewline = false;
           } else {
-            CFX_FloatRect rect(0, pObj->m_Bottom, 1000, pObj->m_Top);
-            if (rect.Contains(m_pPreTextObj->GetPos()))
+            if (CFX_FloatRect(0, pObj->GetRect().bottom, 1000,
+                              pObj->GetRect().top)
+                    .Contains(m_pPrevTextObj->GetPos())) {
               bNewline = false;
+            }
           }
         }
       }
     }
   }
   if (bNewline) {
-    return IsHyphen(curChar) ? GenerateCharacter::Hyphen
-                             : GenerateCharacter::LineBreak;
+    return IsHyphen(curChar) ? GenerateCharacter::kHyphen
+                             : GenerateCharacter::kLineBreak;
   }
 
-  if (pObj->CountChars() == 1 && (0x2D == curChar || 0xAD == curChar) &&
-      IsHyphen(curChar)) {
-    return GenerateCharacter::Hyphen;
-  }
+  if (pObj->CountChars() == 1 && IsHyphenCode(curChar) && IsHyphen(curChar))
+    return GenerateCharacter::kHyphen;
+
+  if (curChar == L' ')
+    return GenerateCharacter::kNone;
+
   WideString PrevStr =
-      m_pPreTextObj->GetFont()->UnicodeFromCharCode(PrevItem.m_CharCode);
-  wchar_t preChar = PrevStr.Last();
-  CFX_Matrix matrix = pObj->GetTextMatrix();
-  matrix.Concat(formMatrix);
+      m_pPrevTextObj->GetFont()->UnicodeFromCharCode(PrevItem.m_CharCode);
+  wchar_t preChar = PrevStr.Back();
+  if (preChar == L' ')
+    return GenerateCharacter::kNone;
 
-  threshold = (float)(nLastWidth > nThisWidth ? nLastWidth : nThisWidth);
-  threshold = threshold > 400
-                  ? (threshold < 700
-                         ? threshold / 4
-                         : (threshold > 800 ? threshold / 6 : threshold / 5))
-                  : (threshold / 2);
+  CFX_Matrix matrix = pObj->GetTextMatrix() * formMatrix;
+  float threshold2 = std::max(nLastWidth, nThisWidth);
+  threshold2 = NormalizeThreshold(threshold2, 400, 700, 800);
   if (nLastWidth >= nThisWidth) {
-    threshold *= fabs(m_pPreTextObj->GetFontSize());
+    threshold2 *= fabs(m_pPrevTextObj->GetFontSize());
   } else {
-    threshold *= fabs(pObj->GetFontSize());
-    threshold = matrix.TransformDistance(threshold);
-    threshold = prev_reverse.TransformDistance(threshold);
+    threshold2 *= fabs(pObj->GetFontSize());
+    threshold2 = matrix.TransformDistance(threshold2);
+    threshold2 = prev_reverse.TransformDistance(threshold2);
   }
-  threshold /= 1000;
-  if ((threshold < 1.4881 && threshold > 1.4879) ||
-      (threshold < 1.39001 && threshold > 1.38999)) {
-    threshold *= 1.5;
+  threshold2 /= 1000;
+  if ((threshold2 < 1.4881 && threshold2 > 1.4879) ||
+      (threshold2 < 1.39001 && threshold2 > 1.38999)) {
+    threshold2 *= 1.5;
   }
-  if (fabs(last_pos + last_width - pos.x) > threshold && curChar != L' ' &&
-      preChar != L' ') {
-    if (curChar != L' ' && preChar != L' ') {
-      if ((pos.x - last_pos - last_width) > threshold ||
-          (last_pos - pos.x - last_width) > threshold) {
-        return GenerateCharacter::Space;
-      }
-      if (pos.x < 0 && (last_pos - pos.x - last_width) > threshold)
-        return GenerateCharacter::Space;
-      if ((pos.x - last_pos - last_width) > this_width ||
-          (pos.x - last_pos - this_width) > last_width) {
-        return GenerateCharacter::Space;
-      }
-    }
-  }
-  return GenerateCharacter::None;
+  return GenerateSpace(pos, last_pos, this_width, last_width, threshold2)
+             ? GenerateCharacter::kSpace
+             : GenerateCharacter::kNone;
 }
 
 bool CPDF_TextPage::IsSameTextObject(CPDF_TextObject* pTextObj1,
-                                     CPDF_TextObject* pTextObj2) {
+                                     CPDF_TextObject* pTextObj2) const {
   if (!pTextObj1 || !pTextObj2)
     return false;
 
   CFX_FloatRect rcPreObj = pTextObj2->GetRect();
-  CFX_FloatRect rcCurObj = pTextObj1->GetRect();
+  const CFX_FloatRect& rcCurObj = pTextObj1->GetRect();
   if (rcPreObj.IsEmpty() && rcCurObj.IsEmpty()) {
     float dbXdif = fabs(rcPreObj.left - rcCurObj.left);
     size_t nCount = m_CharList.size();
     if (nCount >= 2) {
-      PAGECHAR_INFO perCharTemp = m_CharList[nCount - 2];
-      float dbSpace = perCharTemp.m_CharBox.Width();
+      float dbSpace = m_CharList[nCount - 2].m_CharBox.Width();
       if (dbXdif > dbSpace)
         return false;
     }
@@ -1422,20 +1360,18 @@
 
   CFX_PointF diff = pTextObj1->GetPos() - pTextObj2->GetPos();
   float font_size = pTextObj2->GetFontSize();
-  float char_size = GetCharWidth(itemPer.m_CharCode, pTextObj2->GetFont());
+  float char_size =
+      GetCharWidth(itemPer.m_CharCode, pTextObj2->GetFont().Get());
   float max_pre_size =
       std::max(std::max(rcPreObj.Height(), rcPreObj.Width()), font_size);
-  if (fabs(diff.x) > char_size * font_size / 1000 * 0.9 ||
-      fabs(diff.y) > max_pre_size / 8) {
-    return false;
-  }
-  return true;
+  return fabs(diff.x) <= 0.9 * char_size * font_size / 1000 &&
+         fabs(diff.y) <= max_pre_size / 8;
 }
 
 bool CPDF_TextPage::IsSameAsPreTextObject(
     CPDF_TextObject* pTextObj,
-    const CPDF_PageObjectList* pObjList,
-    CPDF_PageObjectList::const_iterator iter) {
+    const CPDF_PageObjectHolder* pObjList,
+    CPDF_PageObjectHolder::const_iterator iter) const {
   int i = 0;
   while (i < 5 && iter != pObjList->begin()) {
     --iter;
@@ -1449,42 +1385,35 @@
   return false;
 }
 
-bool CPDF_TextPage::GenerateCharInfo(wchar_t unicode, PAGECHAR_INFO& info) {
-  const PAGECHAR_INFO* preChar;
-  if (!m_TempCharList.empty())
-    preChar = &m_TempCharList.back();
-  else if (!m_CharList.empty())
-    preChar = &m_CharList.back();
-  else
-    return false;
+Optional<CPDF_TextPage::CharInfo> CPDF_TextPage::GenerateCharInfo(
+    wchar_t unicode) {
+  const CharInfo* pPrevCharInfo = GetPrevCharInfo();
+  if (!pPrevCharInfo)
+    return {};
 
+  CharInfo info;
   info.m_Index = m_TextBuf.GetLength();
-  info.m_Unicode = unicode;
-  info.m_pTextObj = nullptr;
   info.m_CharCode = CPDF_Font::kInvalidCharCode;
-  info.m_Flag = FPDFTEXT_CHAR_GENERATED;
+  info.m_Unicode = unicode;
+  info.m_CharType = CPDF_TextPage::CharType::kGenerated;
 
   int preWidth = 0;
-  if (preChar->m_pTextObj && preChar->m_CharCode != -1) {
-    preWidth =
-        GetCharWidth(preChar->m_CharCode, preChar->m_pTextObj->GetFont());
+  if (pPrevCharInfo->m_pTextObj &&
+      pPrevCharInfo->m_CharCode != CPDF_Font::kInvalidCharCode) {
+    preWidth = GetCharWidth(pPrevCharInfo->m_CharCode,
+                            pPrevCharInfo->m_pTextObj->GetFont().Get());
   }
 
-  float fFontSize = preChar->m_pTextObj ? preChar->m_pTextObj->GetFontSize()
-                                        : preChar->m_CharBox.Height();
+  float fFontSize = pPrevCharInfo->m_pTextObj
+                        ? pPrevCharInfo->m_pTextObj->GetFontSize()
+                        : pPrevCharInfo->m_CharBox.Height();
   if (!fFontSize)
     fFontSize = kDefaultFontSize;
 
-  info.m_Origin = CFX_PointF(
-      preChar->m_Origin.x + preWidth * (fFontSize) / 1000, preChar->m_Origin.y);
+  info.m_Origin =
+      CFX_PointF(pPrevCharInfo->m_Origin.x + preWidth * (fFontSize) / 1000,
+                 pPrevCharInfo->m_Origin.y);
   info.m_CharBox = CFX_FloatRect(info.m_Origin.x, info.m_Origin.y,
                                  info.m_Origin.x, info.m_Origin.y);
-  return true;
-}
-
-bool CPDF_TextPage::IsRectIntersect(const CFX_FloatRect& rect1,
-                                    const CFX_FloatRect& rect2) {
-  CFX_FloatRect rect = rect1;
-  rect.Intersect(rect2);
-  return !rect.IsEmpty();
+  return info;
 }
diff --git a/core/fpdftext/cpdf_textpage.h b/core/fpdftext/cpdf_textpage.h
index 51d0660..bac7181 100644
--- a/core/fpdftext/cpdf_textpage.h
+++ b/core/fpdftext/cpdf_textpage.h
@@ -8,77 +8,21 @@
 #define CORE_FPDFTEXT_CPDF_TEXTPAGE_H_
 
 #include <deque>
+#include <functional>
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_pageobjectlist.h"
+#include "core/fpdfapi/page/cpdf_pageobjectholder.h"
 #include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
 
 class CPDF_Font;
 class CPDF_FormObject;
 class CPDF_Page;
 class CPDF_TextObject;
 
-#define FPDFTEXT_MATCHCASE 0x00000001
-#define FPDFTEXT_MATCHWHOLEWORD 0x00000002
-#define FPDFTEXT_CONSECUTIVE 0x00000004
-
-#define FPDFTEXT_CHAR_NORMAL 0
-#define FPDFTEXT_CHAR_GENERATED 1
-#define FPDFTEXT_CHAR_UNUNICODE 2
-#define FPDFTEXT_CHAR_HYPHEN 3
-#define FPDFTEXT_CHAR_PIECE 4
-
-#define TEXT_SPACE_CHAR L' '
-#define TEXT_LINEFEED_CHAR L'\n'
-#define TEXT_RETURN_CHAR L'\r'
-#define TEXT_HYPHEN_CHAR L'-'
-#define TEXT_EMPTY L""
-#define TEXT_HYPHEN L"-"
-#define TEXT_CHARRATIO_GAPDELTA 0.070
-
-enum class FPDFText_MarkedContent { Pass = 0, Done, Delay };
-
-enum class FPDFText_Direction { Left = -1, Right = 1 };
-
-class FPDF_CHAR_INFO {
- public:
-  FPDF_CHAR_INFO();
-  ~FPDF_CHAR_INFO();
-
-  wchar_t m_Unicode;
-  wchar_t m_Charcode;
-  int32_t m_Flag;
-  float m_FontSize;
-  CFX_PointF m_Origin;
-  CFX_FloatRect m_CharBox;
-  UnownedPtr<CPDF_TextObject> m_pTextObj;
-  CFX_Matrix m_Matrix;
-};
-
-struct FPDF_SEGMENT {
-  int m_Start;
-  int m_nCount;
-};
-
-class PAGECHAR_INFO {
- public:
-  PAGECHAR_INFO();
-  PAGECHAR_INFO(const PAGECHAR_INFO&);
-  ~PAGECHAR_INFO();
-
-  int m_Index;
-  int m_CharCode;
-  wchar_t m_Unicode;
-  int32_t m_Flag;
-  CFX_PointF m_Origin;
-  CFX_FloatRect m_CharBox;
-  UnownedPtr<CPDF_TextObject> m_pTextObj;
-  CFX_Matrix m_Matrix;
-};
-
 struct PDFTEXT_Obj {
   PDFTEXT_Obj();
   PDFTEXT_Obj(const PDFTEXT_Obj& that);
@@ -90,95 +34,118 @@
 
 class CPDF_TextPage {
  public:
-  CPDF_TextPage(const CPDF_Page* pPage, FPDFText_Direction flags);
+  enum class CharType : uint8_t {
+    kNormal,
+    kGenerated,
+    kNotUnicode,
+    kHyphen,
+    kPiece,
+  };
+
+  class CharInfo {
+   public:
+    CharInfo();
+    CharInfo(const CharInfo&);
+    ~CharInfo();
+
+    int m_Index = 0;
+    uint32_t m_CharCode = 0;
+    wchar_t m_Unicode = 0;
+    CharType m_CharType = CharType::kNormal;
+    CFX_PointF m_Origin;
+    CFX_FloatRect m_CharBox;
+    UnownedPtr<CPDF_TextObject> m_pTextObj;
+    CFX_Matrix m_Matrix;
+  };
+
+  CPDF_TextPage(const CPDF_Page* pPage, bool rtl);
   ~CPDF_TextPage();
 
-  // IPDF_TextPage:
-  void ParseTextPage();
-  bool IsParsed() const { return m_bIsParsed; }
-  int CharIndexFromTextIndex(int TextIndex) const;
-  int TextIndexFromCharIndex(int CharIndex) const;
+  int CharIndexFromTextIndex(int text_index) const;
+  int TextIndexFromCharIndex(int char_index) const;
+  size_t size() const { return m_CharList.size(); }
   int CountChars() const;
-  void GetCharInfo(int index, FPDF_CHAR_INFO* info) const;
+
+  // These methods CHECK() to make sure |index| is within bounds.
+  const CharInfo& GetCharInfo(size_t index) const;
+  float GetCharFontSize(size_t index) const;
+
   std::vector<CFX_FloatRect> GetRectArray(int start, int nCount) const;
   int GetIndexAtPos(const CFX_PointF& point, const CFX_SizeF& tolerance) const;
   WideString GetTextByRect(const CFX_FloatRect& rect) const;
+  WideString GetTextByObject(const CPDF_TextObject* pTextObj) const;
 
   // Returns string with the text from |m_TextBuf| that are covered by the input
-  // range. |start| and |count| are in terms of the m_CharIndex, so the range
-  // will be converted into appropriate indices.
+  // range. |start| and |count| are in terms of the |m_CharIndices|, so the
+  // range will be converted into appropriate indices.
   WideString GetPageText(int start, int count) const;
   WideString GetAllPageText() const { return GetPageText(0, CountChars()); }
 
   int CountRects(int start, int nCount);
   bool GetRect(int rectIndex, CFX_FloatRect* pRect) const;
 
-  static bool IsRectIntersect(const CFX_FloatRect& rect1,
-                              const CFX_FloatRect& rect2);
-
  private:
   enum class TextOrientation {
-    Unknown,
-    Horizontal,
-    Vertical,
+    kUnknown,
+    kHorizontal,
+    kVertical,
   };
 
   enum class GenerateCharacter {
-    None,
-    Space,
-    LineBreak,
-    Hyphen,
+    kNone,
+    kSpace,
+    kLineBreak,
+    kHyphen,
   };
 
+  enum class MarkedContentState { kPass = 0, kDone, kDelay };
+
+  void Init();
   bool IsHyphen(wchar_t curChar) const;
-  bool IsControlChar(const PAGECHAR_INFO& charInfo);
   void ProcessObject();
   void ProcessFormObject(CPDF_FormObject* pFormObj,
                          const CFX_Matrix& formMatrix);
   void ProcessTextObject(PDFTEXT_Obj pObj);
   void ProcessTextObject(CPDF_TextObject* pTextObj,
                          const CFX_Matrix& formMatrix,
-                         const CPDF_PageObjectList* pObjList,
-                         CPDF_PageObjectList::const_iterator ObjPos);
+                         const CPDF_PageObjectHolder* pObjList,
+                         CPDF_PageObjectHolder::const_iterator ObjPos);
   GenerateCharacter ProcessInsertObject(const CPDF_TextObject* pObj,
                                         const CFX_Matrix& formMatrix);
-  bool GenerateCharInfo(wchar_t unicode, PAGECHAR_INFO& info);
+  const CharInfo* GetPrevCharInfo() const;
+  Optional<CharInfo> GenerateCharInfo(wchar_t unicode);
   bool IsSameAsPreTextObject(CPDF_TextObject* pTextObj,
-                             const CPDF_PageObjectList* pObjList,
-                             CPDF_PageObjectList::const_iterator ObjPos);
-  bool IsSameTextObject(CPDF_TextObject* pTextObj1, CPDF_TextObject* pTextObj2);
-  int GetCharWidth(uint32_t charCode, CPDF_Font* pFont) const;
+                             const CPDF_PageObjectHolder* pObjList,
+                             CPDF_PageObjectHolder::const_iterator iter) const;
+  bool IsSameTextObject(CPDF_TextObject* pTextObj1,
+                        CPDF_TextObject* pTextObj2) const;
   void CloseTempLine();
-  FPDFText_MarkedContent PreMarkedContent(PDFTEXT_Obj pObj);
+  MarkedContentState PreMarkedContent(PDFTEXT_Obj pObj);
   void ProcessMarkedContent(PDFTEXT_Obj pObj);
-  void CheckMarkedContentObject(int32_t& start, int32_t& nCount) const;
   void FindPreviousTextObject();
-  void AddCharInfoByLRDirection(wchar_t wChar, PAGECHAR_INFO info);
-  void AddCharInfoByRLDirection(wchar_t wChar, PAGECHAR_INFO info);
+  void AddCharInfoByLRDirection(wchar_t wChar, const CharInfo& info);
+  void AddCharInfoByRLDirection(wchar_t wChar, const CharInfo& info);
   TextOrientation GetTextObjectWritingMode(
       const CPDF_TextObject* pTextObj) const;
   TextOrientation FindTextlineFlowOrientation() const;
   void AppendGeneratedCharacter(wchar_t unicode, const CFX_Matrix& formMatrix);
-
   void SwapTempTextBuf(int32_t iCharListStartAppend, int32_t iBufStartAppend);
-  bool IsRightToLeft(const CPDF_TextObject* pTextObj,
-                     const CPDF_Font* pFont,
-                     size_t nItems) const;
+  WideString GetTextByPredicate(
+      const std::function<bool(const CharInfo&)>& predicate) const;
 
   UnownedPtr<const CPDF_Page> const m_pPage;
-  std::vector<uint16_t> m_CharIndex;
-  std::deque<PAGECHAR_INFO> m_CharList;
-  std::deque<PAGECHAR_INFO> m_TempCharList;
+  std::vector<uint16_t> m_CharIndices;
+  std::deque<CharInfo> m_CharList;
+  std::deque<CharInfo> m_TempCharList;
   CFX_WideTextBuf m_TextBuf;
   CFX_WideTextBuf m_TempTextBuf;
-  const FPDFText_Direction m_parserflag;
-  UnownedPtr<CPDF_TextObject> m_pPreTextObj;
-  CFX_Matrix m_perMatrix;
-  bool m_bIsParsed;
-  CFX_Matrix m_DisplayMatrix;
+  UnownedPtr<CPDF_TextObject> m_pPrevTextObj;
+  CFX_Matrix m_PrevMatrix;
+  const bool m_rtl;
+  const CFX_Matrix m_DisplayMatrix;
   std::vector<CFX_FloatRect> m_SelRects;
   std::vector<PDFTEXT_Obj> m_LineObj;
-  TextOrientation m_TextlineDir;
+  TextOrientation m_TextlineDir = TextOrientation::kUnknown;
   CFX_FloatRect m_CurlineRect;
 };
 
diff --git a/core/fpdftext/cpdf_textpagefind.cpp b/core/fpdftext/cpdf_textpagefind.cpp
index 52fdb4a..3df499f 100644
--- a/core/fpdftext/cpdf_textpagefind.cpp
+++ b/core/fpdftext/cpdf_textpagefind.cpp
@@ -11,12 +11,16 @@
 #include <vector>
 
 #include "core/fpdftext/cpdf_textpage.h"
+#include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
 namespace {
 
+constexpr wchar_t kNonBreakingSpace = 160;
+
 bool IsIgnoreSpaceCharacter(wchar_t curChar) {
   if (curChar < 255 || (curChar >= 0x0600 && curChar <= 0x06FF) ||
       (curChar >= 0xFE70 && curChar <= 0xFEFF) ||
@@ -31,296 +35,9 @@
   return true;
 }
 
-}  // namespace
-
-CPDF_TextPageFind::CPDF_TextPageFind(const CPDF_TextPage* pTextPage)
-    : m_pTextPage(pTextPage),
-      m_flags(0),
-      m_bMatchCase(false),
-      m_bMatchWholeWord(false),
-      m_resStart(0),
-      m_resEnd(-1),
-      m_IsFind(false) {
-  m_strText = m_pTextPage->GetAllPageText();
-  int nCount = pTextPage->CountChars();
-  if (nCount)
-    m_CharIndex.push_back(0);
-  for (int i = 0; i < nCount; i++) {
-    FPDF_CHAR_INFO info;
-    pTextPage->GetCharInfo(i, &info);
-    int indexSize = pdfium::CollectionSize<int>(m_CharIndex);
-    if (info.m_Flag == FPDFTEXT_CHAR_NORMAL ||
-        info.m_Flag == FPDFTEXT_CHAR_GENERATED) {
-      if (indexSize % 2) {
-        m_CharIndex.push_back(1);
-      } else {
-        if (indexSize <= 0)
-          continue;
-        m_CharIndex[indexSize - 1] += 1;
-      }
-    } else {
-      if (indexSize % 2) {
-        if (indexSize <= 0)
-          continue;
-        m_CharIndex[indexSize - 1] = i + 1;
-      } else {
-        m_CharIndex.push_back(i + 1);
-      }
-    }
-  }
-  int indexSize = pdfium::CollectionSize<int>(m_CharIndex);
-  if (indexSize % 2)
-    m_CharIndex.erase(m_CharIndex.begin() + indexSize - 1);
-}
-
-CPDF_TextPageFind::~CPDF_TextPageFind() {}
-
-int CPDF_TextPageFind::GetCharIndex(int index) const {
-  return m_pTextPage->CharIndexFromTextIndex(index);
-}
-
-bool CPDF_TextPageFind::FindFirst(const WideString& findwhat,
-                                  int flags,
-                                  Optional<size_t> startPos) {
-  if (!m_pTextPage)
-    return false;
-  if (m_strText.IsEmpty() || m_bMatchCase != (flags & FPDFTEXT_MATCHCASE))
-    m_strText = m_pTextPage->GetAllPageText();
-  WideString findwhatStr = findwhat;
-  m_findWhat = findwhatStr;
-  m_flags = flags;
-  m_bMatchCase = flags & FPDFTEXT_MATCHCASE;
-  if (m_strText.IsEmpty()) {
-    m_IsFind = false;
-    return true;
-  }
-  size_t len = findwhatStr.GetLength();
-  if (!m_bMatchCase) {
-    findwhatStr.MakeLower();
-    m_strText.MakeLower();
-  }
-  m_bMatchWholeWord = !!(flags & FPDFTEXT_MATCHWHOLEWORD);
-  m_findNextStart = startPos;
-  if (!startPos.has_value()) {
-    if (!m_strText.IsEmpty())
-      m_findPreStart = m_strText.GetLength() - 1;
-  } else {
-    m_findPreStart = startPos;
-  }
-
-  m_csFindWhatArray.clear();
-  size_t i = 0;
-  for (i = 0; i < len; ++i)
-    if (findwhatStr[i] != ' ')
-      break;
-  if (i < len)
-    ExtractFindWhat(findwhatStr);
-  else
-    m_csFindWhatArray.push_back(findwhatStr);
-  if (m_csFindWhatArray.empty())
-    return false;
-
-  m_IsFind = true;
-  m_resStart = 0;
-  m_resEnd = -1;
-  return true;
-}
-
-bool CPDF_TextPageFind::FindNext() {
-  if (!m_pTextPage)
-    return false;
-  m_resArray.clear();
-  if (!m_findNextStart.has_value())
-    return false;
-  if (m_strText.IsEmpty()) {
-    m_IsFind = false;
-    return m_IsFind;
-  }
-  size_t strLen = m_strText.GetLength();
-  if (m_findNextStart.value() > strLen - 1) {
-    m_IsFind = false;
-    return m_IsFind;
-  }
-  int nCount = pdfium::CollectionSize<int>(m_csFindWhatArray);
-  Optional<size_t> nResultPos = 0;
-  size_t nStartPos = m_findNextStart.value();
-  bool bSpaceStart = false;
-  for (int iWord = 0; iWord < nCount; iWord++) {
-    WideString csWord = m_csFindWhatArray[iWord];
-    if (csWord.IsEmpty()) {
-      if (iWord == nCount - 1) {
-        wchar_t strInsert = m_strText[nStartPos];
-        if (strInsert == TEXT_LINEFEED_CHAR || strInsert == TEXT_SPACE_CHAR ||
-            strInsert == TEXT_RETURN_CHAR || strInsert == 160) {
-          nResultPos = nStartPos + 1;
-          break;
-        }
-        iWord = -1;
-      } else if (iWord == 0) {
-        bSpaceStart = true;
-      }
-      continue;
-    }
-    nResultPos = m_strText.Find(csWord.c_str(), nStartPos);
-    if (!nResultPos.has_value()) {
-      m_IsFind = false;
-      return m_IsFind;
-    }
-    size_t endIndex = nResultPos.value() + csWord.GetLength() - 1;
-    if (iWord == 0)
-      m_resStart = nResultPos.value();
-    bool bMatch = true;
-    if (iWord != 0 && !bSpaceStart) {
-      size_t PreResEndPos = nStartPos;
-      int curChar = csWord[0];
-      WideString lastWord = m_csFindWhatArray[iWord - 1];
-      int lastChar = lastWord[lastWord.GetLength() - 1];
-      if (nStartPos == nResultPos.value() &&
-          !(IsIgnoreSpaceCharacter(lastChar) ||
-            IsIgnoreSpaceCharacter(curChar))) {
-        bMatch = false;
-      }
-      for (size_t d = PreResEndPos; d < nResultPos.value(); d++) {
-        wchar_t strInsert = m_strText[d];
-        if (strInsert != TEXT_LINEFEED_CHAR && strInsert != TEXT_SPACE_CHAR &&
-            strInsert != TEXT_RETURN_CHAR && strInsert != 160) {
-          bMatch = false;
-          break;
-        }
-      }
-    } else if (bSpaceStart) {
-      if (nResultPos.value() > 0) {
-        wchar_t strInsert = m_strText[nResultPos.value() - 1];
-        if (strInsert != TEXT_LINEFEED_CHAR && strInsert != TEXT_SPACE_CHAR &&
-            strInsert != TEXT_RETURN_CHAR && strInsert != 160) {
-          bMatch = false;
-          m_resStart = nResultPos.value();
-        } else {
-          m_resStart = nResultPos.value() - 1;
-        }
-      }
-    }
-    if (m_bMatchWholeWord && bMatch) {
-      bMatch = IsMatchWholeWord(m_strText, nResultPos.value(), endIndex);
-    }
-    nStartPos = endIndex + 1;
-    if (!bMatch) {
-      iWord = -1;
-      if (bSpaceStart)
-        nStartPos = m_resStart + m_csFindWhatArray[1].GetLength();
-      else
-        nStartPos = m_resStart + m_csFindWhatArray[0].GetLength();
-    }
-  }
-  m_resEnd = nResultPos.value() + m_csFindWhatArray.back().GetLength() - 1;
-  m_IsFind = true;
-  int resStart = GetCharIndex(m_resStart);
-  int resEnd = GetCharIndex(m_resEnd);
-  m_resArray = m_pTextPage->GetRectArray(resStart, resEnd - resStart + 1);
-  if (m_flags & FPDFTEXT_CONSECUTIVE) {
-    m_findNextStart = m_resStart + 1;
-    m_findPreStart = m_resEnd - 1;
-  } else {
-    m_findNextStart = m_resEnd + 1;
-    m_findPreStart = m_resStart - 1;
-  }
-  return m_IsFind;
-}
-
-bool CPDF_TextPageFind::FindPrev() {
-  if (!m_pTextPage)
-    return false;
-  m_resArray.clear();
-  if (m_strText.IsEmpty() || !m_findPreStart.has_value()) {
-    m_IsFind = false;
-    return m_IsFind;
-  }
-  CPDF_TextPageFind findEngine(m_pTextPage.Get());
-  bool ret = findEngine.FindFirst(m_findWhat, m_flags, Optional<size_t>(0));
-  if (!ret) {
-    m_IsFind = false;
-    return m_IsFind;
-  }
-  int order = -1;
-  int MatchedCount = 0;
-  while (ret) {
-    ret = findEngine.FindNext();
-    if (ret) {
-      int order1 = findEngine.GetCurOrder();
-      int MatchedCount1 = findEngine.GetMatchedCount();
-      int temp = order1 + MatchedCount1;
-      if (temp < 0 || static_cast<size_t>(temp) > m_findPreStart.value() + 1)
-        break;
-      order = order1;
-      MatchedCount = MatchedCount1;
-    }
-  }
-  if (order == -1) {
-    m_IsFind = false;
-    return m_IsFind;
-  }
-  m_resStart = m_pTextPage->TextIndexFromCharIndex(order);
-  m_resEnd = m_pTextPage->TextIndexFromCharIndex(order + MatchedCount - 1);
-  m_IsFind = true;
-  m_resArray = m_pTextPage->GetRectArray(order, MatchedCount);
-  if (m_flags & FPDFTEXT_CONSECUTIVE) {
-    m_findNextStart = m_resStart + 1;
-    m_findPreStart = m_resEnd - 1;
-  } else {
-    m_findNextStart = m_resEnd + 1;
-    m_findPreStart = m_resStart - 1;
-  }
-  return m_IsFind;
-}
-
-void CPDF_TextPageFind::ExtractFindWhat(const WideString& findwhat) {
-  if (findwhat.IsEmpty())
-    return;
-  int index = 0;
-  while (1) {
-    Optional<WideString> word =
-        ExtractSubString(findwhat.c_str(), index, TEXT_SPACE_CHAR);
-    if (!word)
-      break;
-
-    if (word->IsEmpty()) {
-      m_csFindWhatArray.push_back(L"");
-      index++;
-      continue;
-    }
-
-    size_t pos = 0;
-    while (pos < word->GetLength()) {
-      WideString curStr = word->Mid(pos, 1);
-      wchar_t curChar = word->operator[](pos);
-      if (IsIgnoreSpaceCharacter(curChar)) {
-        if (pos > 0 && curChar == 0x2019) {
-          pos++;
-          continue;
-        }
-        if (pos > 0)
-          m_csFindWhatArray.push_back(word->Left(pos));
-        m_csFindWhatArray.push_back(curStr);
-        if (pos == word->GetLength() - 1) {
-          word->clear();
-          break;
-        }
-        word.emplace(word->Right(word->GetLength() - pos - 1));
-        pos = 0;
-        continue;
-      }
-      pos++;
-    }
-
-    if (!word->IsEmpty())
-      m_csFindWhatArray.push_back(word.value());
-    index++;
-  }
-}
-
-bool CPDF_TextPageFind::IsMatchWholeWord(const WideString& csPageText,
-                                         size_t startPos,
-                                         size_t endPos) {
+bool IsMatchWholeWord(const WideString& csPageText,
+                      size_t startPos,
+                      size_t endPos) {
   if (startPos > endPos)
     return false;
   wchar_t char_left = 0;
@@ -336,11 +53,12 @@
     char_right = csPageText[startPos + char_count];
   if ((char_left > 'A' && char_left < 'a') ||
       (char_left > 'a' && char_left < 'z') ||
-      (char_left > 0xfb00 && char_left < 0xfb06) || std::iswdigit(char_left) ||
+      (char_left > 0xfb00 && char_left < 0xfb06) ||
+      FXSYS_IsDecimalDigit(char_left) ||
       (char_right > 'A' && char_right < 'a') ||
       (char_right > 'a' && char_right < 'z') ||
       (char_right > 0xfb00 && char_right < 0xfb06) ||
-      std::iswdigit(char_right)) {
+      FXSYS_IsDecimalDigit(char_right)) {
     return false;
   }
   if (!(('A' > char_left || char_left > 'Z') &&
@@ -350,38 +68,267 @@
     return false;
   }
   if (char_count > 0) {
-    if (std::iswdigit(char_left) && std::iswdigit(csPageText[startPos]))
+    if (FXSYS_IsDecimalDigit(char_left) &&
+        FXSYS_IsDecimalDigit(csPageText[startPos])) {
       return false;
-    if (std::iswdigit(char_right) && std::iswdigit(csPageText[endPos]))
+    }
+    if (FXSYS_IsDecimalDigit(char_right) &&
+        FXSYS_IsDecimalDigit(csPageText[endPos])) {
       return false;
+    }
   }
   return true;
 }
 
-Optional<WideString> CPDF_TextPageFind::ExtractSubString(
-    const wchar_t* lpszFullString,
-    int iSubString,
-    wchar_t chSep) {
-  if (!lpszFullString)
-    return {};
+WideString GetStringCase(const WideString& wsOriginal, bool bMatchCase) {
+  if (bMatchCase)
+    return wsOriginal;
+
+  WideString wsLower = wsOriginal;
+  wsLower.MakeLower();
+  return wsLower;
+}
+
+Optional<WideString> ExtractSubString(const wchar_t* lpszFullString,
+                                      int iSubString) {
+  ASSERT(lpszFullString);
 
   while (iSubString--) {
-    lpszFullString = std::wcschr(lpszFullString, chSep);
+    lpszFullString = std::wcschr(lpszFullString, L' ');
     if (!lpszFullString)
       return {};
 
     lpszFullString++;
-    while (*lpszFullString == chSep)
+    while (*lpszFullString == L' ')
       lpszFullString++;
   }
 
-  const wchar_t* lpchEnd = std::wcschr(lpszFullString, chSep);
+  const wchar_t* lpchEnd = std::wcschr(lpszFullString, L' ');
   int nLen = lpchEnd ? static_cast<int>(lpchEnd - lpszFullString)
                      : static_cast<int>(wcslen(lpszFullString));
   if (nLen < 0)
     return {};
 
-  return {WideString(lpszFullString, static_cast<size_t>(nLen))};
+  return WideString(lpszFullString, static_cast<size_t>(nLen));
+}
+
+std::vector<WideString> ExtractFindWhat(const WideString& findwhat) {
+  std::vector<WideString> findwhat_array;
+
+  size_t len = findwhat.GetLength();
+  size_t i = 0;
+  for (i = 0; i < len; ++i)
+    if (findwhat[i] != ' ')
+      break;
+  if (i == len) {
+    findwhat_array.push_back(findwhat);
+    return findwhat_array;
+  }
+
+  int index = 0;
+  while (1) {
+    Optional<WideString> word = ExtractSubString(findwhat.c_str(), index);
+    if (!word)
+      break;
+
+    if (word->IsEmpty()) {
+      findwhat_array.push_back(L"");
+      index++;
+      continue;
+    }
+
+    size_t pos = 0;
+    while (pos < word->GetLength()) {
+      WideString curStr = word->Substr(pos, 1);
+      wchar_t curChar = (*word)[pos];
+      if (IsIgnoreSpaceCharacter(curChar)) {
+        if (pos > 0 && curChar == 0x2019) {
+          pos++;
+          continue;
+        }
+        if (pos > 0)
+          findwhat_array.push_back(word->First(pos));
+        findwhat_array.push_back(curStr);
+        if (pos == word->GetLength() - 1) {
+          word->clear();
+          break;
+        }
+        word.emplace(word->Last(word->GetLength() - pos - 1));
+        pos = 0;
+        continue;
+      }
+      pos++;
+    }
+
+    if (!word->IsEmpty())
+      findwhat_array.push_back(word.value());
+    index++;
+  }
+  return findwhat_array;
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<CPDF_TextPageFind> CPDF_TextPageFind::Create(
+    const CPDF_TextPage* pTextPage,
+    const WideString& findwhat,
+    const Options& options,
+    Optional<size_t> startPos) {
+  std::vector<WideString> findwhat_array =
+      ExtractFindWhat(GetStringCase(findwhat, options.bMatchCase));
+  auto find = pdfium::WrapUnique(
+      new CPDF_TextPageFind(pTextPage, findwhat_array, options, startPos));
+  find->FindFirst();
+  return find;
+}
+
+CPDF_TextPageFind::CPDF_TextPageFind(
+    const CPDF_TextPage* pTextPage,
+    const std::vector<WideString>& findwhat_array,
+    const Options& options,
+    Optional<size_t> startPos)
+    : m_pTextPage(pTextPage),
+      m_strText(GetStringCase(pTextPage->GetAllPageText(), options.bMatchCase)),
+      m_csFindWhatArray(findwhat_array),
+      m_options(options) {
+  if (!m_strText.IsEmpty()) {
+    m_findNextStart = startPos;
+    m_findPreStart = startPos.value_or(m_strText.GetLength() - 1);
+  }
+}
+
+CPDF_TextPageFind::~CPDF_TextPageFind() = default;
+
+int CPDF_TextPageFind::GetCharIndex(int index) const {
+  return m_pTextPage->CharIndexFromTextIndex(index);
+}
+
+bool CPDF_TextPageFind::FindFirst() {
+  return m_strText.IsEmpty() || !m_csFindWhatArray.empty();
+}
+
+bool CPDF_TextPageFind::FindNext() {
+  if (m_strText.IsEmpty() || !m_findNextStart.has_value())
+    return false;
+
+  size_t strLen = m_strText.GetLength();
+  if (m_findNextStart.value() > strLen - 1)
+    return false;
+
+  int nCount = pdfium::CollectionSize<int>(m_csFindWhatArray);
+  Optional<size_t> nResultPos = 0;
+  size_t nStartPos = m_findNextStart.value();
+  bool bSpaceStart = false;
+  for (int iWord = 0; iWord < nCount; iWord++) {
+    WideString csWord = m_csFindWhatArray[iWord];
+    if (csWord.IsEmpty()) {
+      if (iWord == nCount - 1) {
+        wchar_t strInsert = m_strText[nStartPos];
+        if (strInsert == L'\n' || strInsert == L' ' || strInsert == L'\r' ||
+            strInsert == kNonBreakingSpace) {
+          nResultPos = nStartPos + 1;
+          break;
+        }
+        iWord = -1;
+      } else if (iWord == 0) {
+        bSpaceStart = true;
+      }
+      continue;
+    }
+    nResultPos = m_strText.Find(csWord.AsStringView(), nStartPos);
+    if (!nResultPos.has_value())
+      return false;
+
+    size_t endIndex = nResultPos.value() + csWord.GetLength() - 1;
+    if (iWord == 0)
+      m_resStart = nResultPos.value();
+    bool bMatch = true;
+    if (iWord != 0 && !bSpaceStart) {
+      size_t PreResEndPos = nStartPos;
+      int curChar = csWord[0];
+      WideString lastWord = m_csFindWhatArray[iWord - 1];
+      int lastChar = lastWord.Back();
+      if (nStartPos == nResultPos.value() &&
+          !(IsIgnoreSpaceCharacter(lastChar) ||
+            IsIgnoreSpaceCharacter(curChar))) {
+        bMatch = false;
+      }
+      for (size_t d = PreResEndPos; d < nResultPos.value(); d++) {
+        wchar_t strInsert = m_strText[d];
+        if (strInsert != L'\n' && strInsert != L' ' && strInsert != L'\r' &&
+            strInsert != kNonBreakingSpace) {
+          bMatch = false;
+          break;
+        }
+      }
+    } else if (bSpaceStart) {
+      if (nResultPos.value() > 0) {
+        wchar_t strInsert = m_strText[nResultPos.value() - 1];
+        if (strInsert != L'\n' && strInsert != L' ' && strInsert != L'\r' &&
+            strInsert != kNonBreakingSpace) {
+          bMatch = false;
+          m_resStart = nResultPos.value();
+        } else {
+          m_resStart = nResultPos.value() - 1;
+        }
+      }
+    }
+    if (m_options.bMatchWholeWord && bMatch)
+      bMatch = IsMatchWholeWord(m_strText, nResultPos.value(), endIndex);
+
+    nStartPos = endIndex + 1;
+    if (!bMatch) {
+      iWord = -1;
+      size_t index = bSpaceStart ? 1 : 0;
+      nStartPos = m_resStart + m_csFindWhatArray[index].GetLength();
+    }
+  }
+  m_resEnd = nResultPos.value() + m_csFindWhatArray.back().GetLength() - 1;
+  if (m_options.bConsecutive) {
+    m_findNextStart = m_resStart + 1;
+    m_findPreStart = m_resEnd - 1;
+  } else {
+    m_findNextStart = m_resEnd + 1;
+    m_findPreStart = m_resStart - 1;
+  }
+  return true;
+}
+
+bool CPDF_TextPageFind::FindPrev() {
+  if (m_strText.IsEmpty() || !m_findPreStart.has_value())
+    return false;
+
+  CPDF_TextPageFind find_engine(m_pTextPage.Get(), m_csFindWhatArray, m_options,
+                                0);
+  if (!find_engine.FindFirst())
+    return false;
+
+  int order = -1;
+  int matches = 0;
+  while (find_engine.FindNext()) {
+    int cur_order = find_engine.GetCurOrder();
+    int cur_match = find_engine.GetMatchedCount();
+    int temp = cur_order + cur_match;
+    if (temp < 0 || static_cast<size_t>(temp) > m_findPreStart.value() + 1)
+      break;
+
+    order = cur_order;
+    matches = cur_match;
+  }
+  if (order == -1)
+    return false;
+
+  m_resStart = m_pTextPage->TextIndexFromCharIndex(order);
+  m_resEnd = m_pTextPage->TextIndexFromCharIndex(order + matches - 1);
+  if (m_options.bConsecutive) {
+    m_findNextStart = m_resStart + 1;
+    m_findPreStart = m_resEnd - 1;
+  } else {
+    m_findNextStart = m_resEnd + 1;
+    m_findPreStart = m_resStart - 1;
+  }
+  return true;
 }
 
 int CPDF_TextPageFind::GetCurOrder() const {
diff --git a/core/fpdftext/cpdf_textpagefind.h b/core/fpdftext/cpdf_textpagefind.h
index f7419cf..c3b2956 100644
--- a/core/fpdftext/cpdf_textpagefind.h
+++ b/core/fpdftext/cpdf_textpagefind.h
@@ -7,6 +7,7 @@
 #ifndef CORE_FPDFTEXT_CPDF_TEXTPAGEFIND_H_
 #define CORE_FPDFTEXT_CPDF_TEXTPAGEFIND_H_
 
+#include <memory>
 #include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
@@ -19,42 +20,44 @@
 
 class CPDF_TextPageFind {
  public:
-  explicit CPDF_TextPageFind(const CPDF_TextPage* pTextPage);
+  struct Options {
+    bool bMatchCase = false;
+    bool bMatchWholeWord = false;
+    bool bConsecutive = false;
+  };
+
+  static std::unique_ptr<CPDF_TextPageFind> Create(
+      const CPDF_TextPage* pTextPage,
+      const WideString& findwhat,
+      const Options& options,
+      Optional<size_t> startPos);
+
   ~CPDF_TextPageFind();
 
-  bool FindFirst(const WideString& findwhat,
-                 int flags,
-                 Optional<size_t> startPos);
   bool FindNext();
   bool FindPrev();
   int GetCurOrder() const;
   int GetMatchedCount() const;
 
- protected:
-  void ExtractFindWhat(const WideString& findwhat);
-  bool IsMatchWholeWord(const WideString& csPageText,
-                        size_t startPos,
-                        size_t endPos);
-  Optional<WideString> ExtractSubString(const wchar_t* lpszFullString,
-                                        int iSubString,
-                                        wchar_t chSep);
+ private:
+  CPDF_TextPageFind(const CPDF_TextPage* pTextPage,
+                    const std::vector<WideString>& findwhat_array,
+                    const Options& options,
+                    Optional<size_t> startPos);
+
+  // Should be called immediately after construction.
+  bool FindFirst();
+
   int GetCharIndex(int index) const;
 
- private:
-  std::vector<uint16_t> m_CharIndex;
-  UnownedPtr<const CPDF_TextPage> m_pTextPage;
-  WideString m_strText;
-  WideString m_findWhat;
-  int m_flags;
-  std::vector<WideString> m_csFindWhatArray;
+  UnownedPtr<const CPDF_TextPage> const m_pTextPage;
+  const WideString m_strText;
+  const std::vector<WideString> m_csFindWhatArray;
   Optional<size_t> m_findNextStart;
   Optional<size_t> m_findPreStart;
-  bool m_bMatchCase;
-  bool m_bMatchWholeWord;
-  int m_resStart;
-  int m_resEnd;
-  std::vector<CFX_FloatRect> m_resArray;
-  bool m_IsFind;
+  int m_resStart = 0;
+  int m_resEnd = -1;
+  const Options m_options;
 };
 
 #endif  // CORE_FPDFTEXT_CPDF_TEXTPAGEFIND_H_
diff --git a/core/fxcodec/Android.bp b/core/fxcodec/Android.bp
new file mode 100644
index 0000000..b5d78d1
--- /dev/null
+++ b/core/fxcodec/Android.bp
@@ -0,0 +1,35 @@
+cc_library_static {
+    name: "libpdfium-fxcodec",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    static_libs: [
+        "libpdfium-lcms2",
+        "libpdfium-libopenjpeg2",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+    ],
+
+    shared_libs: [
+        "libz",
+        "libjpeg",
+    ],
+
+    exclude_srcs: [
+        // pdf_enable_xfa
+        "progressivedecoder.cpp",
+        // pdf_enable_xfa_bmp
+        "bmp/*",
+        // pdf_enable_xfa_gif
+        "gif/*",
+        // pdf_enable_xfa_png
+        "png/*",
+        // pdf_enable_xfa_tiff
+        "tiff/*",
+    ],
+
+    srcs: [
+        "**/*.cpp",
+    ],
+}
diff --git a/core/fxcodec/BUILD.gn b/core/fxcodec/BUILD.gn
new file mode 100644
index 0000000..e0bc3a7
--- /dev/null
+++ b/core/fxcodec/BUILD.gn
@@ -0,0 +1,160 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+source_set("fxcodec") {
+  sources = [
+    "basic/basicmodule.cpp",
+    "basic/basicmodule.h",
+    "cfx_codec_memory.cpp",
+    "cfx_codec_memory.h",
+    "codec_module_iface.h",
+    "fax/faxmodule.cpp",
+    "fax/faxmodule.h",
+    "flate/flatemodule.cpp",
+    "flate/flatemodule.h",
+    "fx_codec.cpp",
+    "fx_codec.h",
+    "fx_codec_def.h",
+    "icc/iccmodule.cpp",
+    "icc/iccmodule.h",
+    "jbig2/JBig2_ArithDecoder.cpp",
+    "jbig2/JBig2_ArithDecoder.h",
+    "jbig2/JBig2_ArithIntDecoder.cpp",
+    "jbig2/JBig2_ArithIntDecoder.h",
+    "jbig2/JBig2_BitStream.cpp",
+    "jbig2/JBig2_BitStream.h",
+    "jbig2/JBig2_Context.cpp",
+    "jbig2/JBig2_Context.h",
+    "jbig2/JBig2_Define.h",
+    "jbig2/JBig2_DocumentContext.cpp",
+    "jbig2/JBig2_DocumentContext.h",
+    "jbig2/JBig2_GrdProc.cpp",
+    "jbig2/JBig2_GrdProc.h",
+    "jbig2/JBig2_GrrdProc.cpp",
+    "jbig2/JBig2_GrrdProc.h",
+    "jbig2/JBig2_HtrdProc.cpp",
+    "jbig2/JBig2_HtrdProc.h",
+    "jbig2/JBig2_HuffmanDecoder.cpp",
+    "jbig2/JBig2_HuffmanDecoder.h",
+    "jbig2/JBig2_HuffmanTable.cpp",
+    "jbig2/JBig2_HuffmanTable.h",
+    "jbig2/JBig2_Image.cpp",
+    "jbig2/JBig2_Image.h",
+    "jbig2/JBig2_Page.h",
+    "jbig2/JBig2_PatternDict.cpp",
+    "jbig2/JBig2_PatternDict.h",
+    "jbig2/JBig2_PddProc.cpp",
+    "jbig2/JBig2_PddProc.h",
+    "jbig2/JBig2_SddProc.cpp",
+    "jbig2/JBig2_SddProc.h",
+    "jbig2/JBig2_Segment.cpp",
+    "jbig2/JBig2_Segment.h",
+    "jbig2/JBig2_SymbolDict.cpp",
+    "jbig2/JBig2_SymbolDict.h",
+    "jbig2/JBig2_TrdProc.cpp",
+    "jbig2/JBig2_TrdProc.h",
+    "jbig2/jbig2module.cpp",
+    "jbig2/jbig2module.h",
+    "jpeg/jpegmodule.cpp",
+    "jpeg/jpegmodule.h",
+    "jpx/cjpx_decoder.cpp",
+    "jpx/cjpx_decoder.h",
+    "jpx/jpx_decode_utils.cpp",
+    "jpx/jpx_decode_utils.h",
+    "jpx/jpxmodule.cpp",
+    "jpx/jpxmodule.h",
+    "scanlinedecoder.cpp",
+    "scanlinedecoder.h",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  include_dirs = []
+  deps = [
+    "../../third_party:lcms2",
+    "../../third_party:libopenjpeg2",
+    "../../third_party:zlib",
+    "../fxcrt",
+    "../fxge",
+    "//third_party:jpeg",
+  ]
+
+  if (pdf_enable_xfa) {
+    sources += [
+      "progressivedecoder.cpp",
+      "progressivedecoder.h",
+    ]
+    if (pdf_enable_xfa_bmp) {
+      sources += [
+        "bmp/bmpmodule.cpp",
+        "bmp/bmpmodule.h",
+        "bmp/cfx_bmpcontext.cpp",
+        "bmp/cfx_bmpcontext.h",
+        "bmp/cfx_bmpdecompressor.cpp",
+        "bmp/cfx_bmpdecompressor.h",
+        "bmp/fx_bmp.h",
+      ]
+    }
+    if (pdf_enable_xfa_gif) {
+      sources += [
+        "gif/cfx_gif.cpp",
+        "gif/cfx_gif.h",
+        "gif/cfx_gifcontext.cpp",
+        "gif/cfx_gifcontext.h",
+        "gif/cfx_lzwdecompressor.cpp",
+        "gif/cfx_lzwdecompressor.h",
+        "gif/gifmodule.cpp",
+        "gif/gifmodule.h",
+      ]
+    }
+    if (pdf_enable_xfa_png) {
+      sources += [
+        "png/pngmodule.cpp",
+        "png/pngmodule.h",
+      ]
+      deps += [ "../../third_party:png" ]
+    }
+    if (pdf_enable_xfa_tiff) {
+      sources += [
+        "tiff/tiffmodule.cpp",
+        "tiff/tiffmodule.h",
+      ]
+      deps += [ "../../third_party:fx_tiff" ]
+    }
+  }
+
+  visibility = [ "../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "basic/a85_unittest.cpp",
+    "basic/rle_unittest.cpp",
+    "jbig2/JBig2_BitStream_unittest.cpp",
+    "jbig2/JBig2_Image_unittest.cpp",
+    "jpx/jpx_unittest.cpp",
+  ]
+  deps = [
+    ":fxcodec",
+    "../fpdfapi/parser",
+  ]
+  pdfium_root_dir = "../../"
+
+  if (pdf_enable_xfa) {
+    sources += [ "progressivedecoder_unittest.cpp" ]
+    deps += [ "../fxge" ]
+    if (pdf_enable_xfa_gif) {
+      sources += [
+        "gif/cfx_gifcontext_unittest.cpp",
+        "gif/cfx_lzwdecompressor_unittest.cpp",
+      ]
+    }
+  }
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "jbig2/jbig2_embeddertest.cpp" ]
+  pdfium_root_dir = "../../"
+}
diff --git a/core/fxcodec/DEPS b/core/fxcodec/DEPS
deleted file mode 100644
index 9919478..0000000
--- a/core/fxcodec/DEPS
+++ /dev/null
@@ -1,8 +0,0 @@
-include_rules = [
-  '+third_party/lcms',
-  '+third_party/libjpeg/jpeglib.h',
-  # For non-standalone builds that may use libjpeg_turbo.
-  '+third_party/libjpeg_turbo/jpeglib.h',
-  '+third_party/libopenjpeg20',
-  '+third_party/zlib',
-]
diff --git a/core/fxcodec/JBig2_DocumentContext.h b/core/fxcodec/JBig2_DocumentContext.h
deleted file mode 100644
index 9e5d3b3..0000000
--- a/core/fxcodec/JBig2_DocumentContext.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_JBIG2_DOCUMENTCONTEXT_H_
-#define CORE_FXCODEC_JBIG2_DOCUMENTCONTEXT_H_
-
-#include <list>
-#include <memory>
-#include <utility>
-
-class CJBig2_SymbolDict;
-
-using CJBig2_CacheKey = std::pair<uint32_t, uint32_t>;
-using CJBig2_CachePair =
-    std::pair<CJBig2_CacheKey, std::unique_ptr<CJBig2_SymbolDict>>;
-
-// Holds per-document JBig2 related data.
-class JBig2_DocumentContext {
- public:
-  JBig2_DocumentContext();
-  ~JBig2_DocumentContext();
-
-  std::list<CJBig2_CachePair>* GetSymbolDictCache() {
-    return &m_SymbolDictCache;
-  }
-
- private:
-  std::list<CJBig2_CachePair> m_SymbolDictCache;
-};
-
-#endif  // CORE_FXCODEC_JBIG2_DOCUMENTCONTEXT_H_
diff --git a/core/fxcodec/basic/a85_unittest.cpp b/core/fxcodec/basic/a85_unittest.cpp
new file mode 100644
index 0000000..e01d3d2
--- /dev/null
+++ b/core/fxcodec/basic/a85_unittest.cpp
@@ -0,0 +1,193 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+
+#include "core/fxcodec/basic/basicmodule.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcodec, A85TestBadInputs) {
+  const uint8_t src_buf[] = {1, 2, 3, 4};
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+
+  // Error codes, not segvs, should callers pass us a nullptr pointer.
+  EXPECT_FALSE(BasicModule::A85Encode(src_buf, &dest_buf, nullptr));
+  EXPECT_FALSE(BasicModule::A85Encode(src_buf, nullptr, &dest_size));
+  EXPECT_FALSE(BasicModule::A85Encode({}, &dest_buf, &dest_size));
+}
+
+// No leftover bytes, just translate 2 sets of symbols.
+TEST(fxcodec, A85TestBasic) {
+  // Make sure really big values don't break.
+  const uint8_t src_buf[] = {1, 2, 3, 4, 255, 255, 255, 255};
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+  EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size));
+
+  // Should have 5 chars for each set of 4 and 2 terminators.
+  const uint8_t expected_out[] = {33, 60, 78, 63, 43,  115,
+                                  56, 87, 45, 33, 126, 62};
+  ASSERT_EQ(FX_ArraySize(expected_out), dest_size);
+
+  // Check the output
+  auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+  for (uint32_t i = 0; i < dest_size; i++)
+    EXPECT_EQ(expected_out[i], dest_buf_span[i]) << " at " << i;
+}
+
+// Leftover bytes.
+TEST(fxcodec, A85TestLeftoverBytes) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+
+  {
+    // 1 Leftover Byte:
+    const uint8_t src_buf_1leftover[] = {1, 2, 3, 4, 255};
+    EXPECT_TRUE(
+        BasicModule::A85Encode(src_buf_1leftover, &dest_buf, &dest_size));
+
+    // 5 chars for first symbol + 2 + 2 terminators.
+    uint8_t expected_out_1leftover[] = {33, 60, 78, 63, 43, 114, 114, 126, 62};
+    ASSERT_EQ(FX_ArraySize(expected_out_1leftover), dest_size);
+
+    // Check the output
+    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+    for (uint32_t i = 0; i < dest_size; i++)
+      EXPECT_EQ(expected_out_1leftover[i], dest_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // 2 Leftover bytes:
+    const uint8_t src_buf_2leftover[] = {1, 2, 3, 4, 255, 254};
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(
+        BasicModule::A85Encode(src_buf_2leftover, &dest_buf, &dest_size));
+    // 5 chars for first symbol + 3 + 2 terminators.
+    const uint8_t expected_out_2leftover[] = {33,  60, 78, 63,  43,
+                                              115, 56, 68, 126, 62};
+    ASSERT_EQ(FX_ArraySize(expected_out_2leftover), dest_size);
+
+    // Check the output
+    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+    for (uint32_t i = 0; i < dest_size; i++)
+      EXPECT_EQ(expected_out_2leftover[i], dest_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // 3 Leftover bytes:
+    const uint8_t src_buf_3leftover[] = {1, 2, 3, 4, 255, 254, 253};
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(
+        BasicModule::A85Encode(src_buf_3leftover, &dest_buf, &dest_size));
+    // 5 chars for first symbol + 4 + 2 terminators.
+    const uint8_t expected_out_3leftover[] = {33, 60, 78,  63,  43, 115,
+                                              56, 77, 114, 126, 62};
+    ASSERT_EQ(FX_ArraySize(expected_out_3leftover), dest_size);
+
+    // Check the output
+    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+    for (uint32_t i = 0; i < dest_size; i++)
+      EXPECT_EQ(expected_out_3leftover[i], dest_buf_span[i]) << " at " << i;
+  }
+}
+
+// Test all zeros comes through as "z".
+TEST(fxcodec, A85TestZeros) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+
+  {
+    // Make sure really big values don't break.
+    const uint8_t src_buf[] = {1, 2, 3, 4, 0, 0, 0, 0};
+    EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size));
+
+    // Should have 5 chars for first set of 4 + 1 for z + 2 terminators.
+    const uint8_t expected_out[] = {33, 60, 78, 63, 43, 122, 126, 62};
+    ASSERT_EQ(FX_ArraySize(expected_out), dest_size);
+
+    // Check the output
+    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+    for (uint32_t i = 0; i < dest_size; i++)
+      EXPECT_EQ(expected_out[i], dest_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // Should also work if it is at the start:
+    const uint8_t src_buf_2[] = {0, 0, 0, 0, 1, 2, 3, 4};
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(BasicModule::A85Encode(src_buf_2, &dest_buf, &dest_size));
+
+    // Should have 5 chars for set of 4 + 1 for z + 2 terminators.
+    const uint8_t expected_out_2[] = {122, 33, 60, 78, 63, 43, 126, 62};
+    ASSERT_EQ(FX_ArraySize(expected_out_2), dest_size);
+
+    // Check the output
+    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+    for (uint32_t i = 0; i < dest_size; i++)
+      EXPECT_EQ(expected_out_2[i], dest_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // Try with 2 leftover zero bytes. Make sure we don't get a "z".
+    const uint8_t src_buf_3[] = {1, 2, 3, 4, 0, 0};
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(BasicModule::A85Encode(src_buf_3, &dest_buf, &dest_size));
+
+    // Should have 5 chars for set of 4 + 3 for last 2 + 2 terminators.
+    const uint8_t expected_out_leftover[] = {33, 60, 78, 63,  43,
+                                             33, 33, 33, 126, 62};
+    ASSERT_EQ(FX_ArraySize(expected_out_leftover), dest_size);
+
+    // Check the output
+    auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+    for (uint32_t i = 0; i < dest_size; i++)
+      EXPECT_EQ(expected_out_leftover[i], dest_buf_span[i]) << " at " << i;
+  }
+}
+
+// Make sure we get returns in the expected locations.
+TEST(fxcodec, A85TestLineBreaks) {
+  // Make sure really big values don't break.
+  uint8_t src_buf[131] = {0};
+  // 1 full line + most of a line of normal symbols.
+  for (int k = 0; k < 116; k += 4) {
+    src_buf[k] = 1;
+    src_buf[k + 1] = 2;
+    src_buf[k + 2] = 3;
+    src_buf[k + 3] = 4;
+  }
+  // Fill in the end, leaving an all zero gap + 3 extra zeros at the end.
+  for (int k = 120; k < 128; k++) {
+    src_buf[k] = 1;
+    src_buf[k + 1] = 2;
+    src_buf[k + 2] = 3;
+    src_buf[k + 3] = 4;
+  }
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+
+  // Should succeed.
+  EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size));
+
+  // Should have 75 chars in the first row plus 2 char return,
+  // 76 chars in the second row plus 2 char return,
+  // and 9 chars in the last row with 2 terminators.
+  ASSERT_EQ(166u, dest_size);
+
+  // Check for the returns.
+  auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+  EXPECT_EQ(13, dest_buf_span[75]);
+  EXPECT_EQ(10, dest_buf_span[76]);
+  EXPECT_EQ(13, dest_buf_span[153]);
+  EXPECT_EQ(10, dest_buf_span[154]);
+}
diff --git a/core/fxcodec/basic/basicmodule.cpp b/core/fxcodec/basic/basicmodule.cpp
new file mode 100644
index 0000000..d495454
--- /dev/null
+++ b/core/fxcodec/basic/basicmodule.cpp
@@ -0,0 +1,376 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcodec/basic/basicmodule.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcodec {
+
+namespace {
+
+class RLScanlineDecoder final : public ScanlineDecoder {
+ public:
+  RLScanlineDecoder();
+  ~RLScanlineDecoder() override;
+
+  bool Create(pdfium::span<const uint8_t> src_buf,
+              int width,
+              int height,
+              int nComps,
+              int bpc);
+
+  // ScanlineDecoder:
+  bool v_Rewind() override;
+  uint8_t* v_GetNextLine() override;
+  uint32_t GetSrcOffset() override { return m_SrcOffset; }
+
+ private:
+  bool CheckDestSize();
+  void GetNextOperator();
+  void UpdateOperator(uint8_t used_bytes);
+
+  std::unique_ptr<uint8_t, FxFreeDeleter> m_pScanline;
+  pdfium::span<const uint8_t> m_SrcBuf;
+  size_t m_dwLineBytes = 0;
+  size_t m_SrcOffset = 0;
+  bool m_bEOD = false;
+  uint8_t m_Operator = 0;
+};
+
+RLScanlineDecoder::RLScanlineDecoder() = default;
+
+RLScanlineDecoder::~RLScanlineDecoder() = default;
+
+bool RLScanlineDecoder::CheckDestSize() {
+  size_t i = 0;
+  uint32_t old_size = 0;
+  uint32_t dest_size = 0;
+  while (i < m_SrcBuf.size()) {
+    if (m_SrcBuf[i] < 128) {
+      old_size = dest_size;
+      dest_size += m_SrcBuf[i] + 1;
+      if (dest_size < old_size) {
+        return false;
+      }
+      i += m_SrcBuf[i] + 2;
+    } else if (m_SrcBuf[i] > 128) {
+      old_size = dest_size;
+      dest_size += 257 - m_SrcBuf[i];
+      if (dest_size < old_size) {
+        return false;
+      }
+      i += 2;
+    } else {
+      break;
+    }
+  }
+  if (((uint32_t)m_OrigWidth * m_nComps * m_bpc * m_OrigHeight + 7) / 8 >
+      dest_size) {
+    return false;
+  }
+  return true;
+}
+
+bool RLScanlineDecoder::Create(pdfium::span<const uint8_t> src_buf,
+                               int width,
+                               int height,
+                               int nComps,
+                               int bpc) {
+  m_SrcBuf = src_buf;
+  m_OutputWidth = m_OrigWidth = width;
+  m_OutputHeight = m_OrigHeight = height;
+  m_nComps = nComps;
+  m_bpc = bpc;
+  // Aligning the pitch to 4 bytes requires an integer overflow check.
+  FX_SAFE_UINT32 pitch = width;
+  pitch *= nComps;
+  pitch *= bpc;
+  pitch += 31;
+  pitch /= 32;
+  pitch *= 4;
+  if (!pitch.IsValid()) {
+    return false;
+  }
+  m_Pitch = pitch.ValueOrDie();
+  // Overflow should already have been checked before this is called.
+  m_dwLineBytes = (static_cast<uint32_t>(width) * nComps * bpc + 7) / 8;
+  m_pScanline.reset(FX_Alloc(uint8_t, m_Pitch));
+  return CheckDestSize();
+}
+
+bool RLScanlineDecoder::v_Rewind() {
+  memset(m_pScanline.get(), 0, m_Pitch);
+  m_SrcOffset = 0;
+  m_bEOD = false;
+  m_Operator = 0;
+  return true;
+}
+
+uint8_t* RLScanlineDecoder::v_GetNextLine() {
+  if (m_SrcOffset == 0) {
+    GetNextOperator();
+  } else if (m_bEOD) {
+    return nullptr;
+  }
+  memset(m_pScanline.get(), 0, m_Pitch);
+  uint32_t col_pos = 0;
+  bool eol = false;
+  while (m_SrcOffset < m_SrcBuf.size() && !eol) {
+    if (m_Operator < 128) {
+      uint32_t copy_len = m_Operator + 1;
+      if (col_pos + copy_len >= m_dwLineBytes) {
+        copy_len = m_dwLineBytes - col_pos;
+        eol = true;
+      }
+      if (copy_len >= m_SrcBuf.size() - m_SrcOffset) {
+        copy_len = m_SrcBuf.size() - m_SrcOffset;
+        m_bEOD = true;
+      }
+      auto copy_span = m_SrcBuf.subspan(m_SrcOffset, copy_len);
+      memcpy(m_pScanline.get() + col_pos, copy_span.data(), copy_span.size());
+      col_pos += copy_len;
+      UpdateOperator((uint8_t)copy_len);
+    } else if (m_Operator > 128) {
+      int fill = 0;
+      if (m_SrcOffset - 1 < m_SrcBuf.size() - 1) {
+        fill = m_SrcBuf[m_SrcOffset];
+      }
+      uint32_t duplicate_len = 257 - m_Operator;
+      if (col_pos + duplicate_len >= m_dwLineBytes) {
+        duplicate_len = m_dwLineBytes - col_pos;
+        eol = true;
+      }
+      memset(m_pScanline.get() + col_pos, fill, duplicate_len);
+      col_pos += duplicate_len;
+      UpdateOperator((uint8_t)duplicate_len);
+    } else {
+      m_bEOD = true;
+      break;
+    }
+  }
+  return m_pScanline.get();
+}
+
+void RLScanlineDecoder::GetNextOperator() {
+  if (m_SrcOffset >= m_SrcBuf.size()) {
+    m_Operator = 128;
+    return;
+  }
+  m_Operator = m_SrcBuf[m_SrcOffset];
+  m_SrcOffset++;
+}
+void RLScanlineDecoder::UpdateOperator(uint8_t used_bytes) {
+  if (used_bytes == 0) {
+    return;
+  }
+  if (m_Operator < 128) {
+    ASSERT((uint32_t)m_Operator + 1 >= used_bytes);
+    if (used_bytes == m_Operator + 1) {
+      m_SrcOffset += used_bytes;
+      GetNextOperator();
+      return;
+    }
+    m_Operator -= used_bytes;
+    m_SrcOffset += used_bytes;
+    if (m_SrcOffset >= m_SrcBuf.size()) {
+      m_Operator = 128;
+    }
+    return;
+  }
+  uint8_t count = 257 - m_Operator;
+  ASSERT((uint32_t)count >= used_bytes);
+  if (used_bytes == count) {
+    m_SrcOffset++;
+    GetNextOperator();
+    return;
+  }
+  count -= used_bytes;
+  m_Operator = 257 - count;
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<ScanlineDecoder> BasicModule::CreateRunLengthDecoder(
+    pdfium::span<const uint8_t> src_buf,
+    int width,
+    int height,
+    int nComps,
+    int bpc) {
+  auto pDecoder = pdfium::MakeUnique<RLScanlineDecoder>();
+  if (!pDecoder->Create(src_buf, width, height, nComps, bpc))
+    return nullptr;
+
+  return std::move(pDecoder);
+}
+
+// static
+bool BasicModule::RunLengthEncode(
+    pdfium::span<const uint8_t> src_span,
+    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+    uint32_t* dest_size) {
+  // Check inputs
+  if (src_span.empty() || !dest_buf || !dest_size)
+    return false;
+
+  // Edge case
+  if (src_span.size() == 1) {
+    *dest_size = 3;
+    dest_buf->reset(FX_Alloc(uint8_t, *dest_size));
+    auto dest_buf_span = pdfium::make_span(dest_buf->get(), *dest_size);
+    dest_buf_span[0] = 0;
+    dest_buf_span[1] = src_span[0];
+    dest_buf_span[2] = 128;
+    return true;
+  }
+
+  // Worst case: 1 nonmatch, 2 match, 1 nonmatch, 2 match, etc. This becomes
+  // 4 output chars for every 3 input, plus up to 4 more for the 1-2 chars
+  // rounded off plus the terminating character.
+  FX_SAFE_SIZE_T estimated_size = src_span.size();
+  estimated_size += 2;
+  estimated_size /= 3;
+  estimated_size *= 4;
+  estimated_size += 1;
+  dest_buf->reset(FX_Alloc(uint8_t, estimated_size.ValueOrDie()));
+
+  // Set up pointers.
+  uint8_t* out = dest_buf->get();
+  uint32_t run_start = 0;
+  uint32_t run_end = 1;
+  uint8_t x = src_span[run_start];
+  uint8_t y = src_span[run_end];
+  while (run_end < src_span.size()) {
+    size_t max_len = std::min<size_t>(128, src_span.size() - run_start);
+    while (x == y && (run_end - run_start < max_len - 1))
+      y = src_span[++run_end];
+
+    // Reached end with matched run. Update variables to expected values.
+    if (x == y) {
+      run_end++;
+      if (run_end < src_span.size())
+        y = src_span[run_end];
+    }
+    if (run_end - run_start > 1) {  // Matched run but not at end of input.
+      out[0] = 257 - (run_end - run_start);
+      out[1] = x;
+      x = y;
+      run_start = run_end;
+      run_end++;
+      if (run_end < src_span.size())
+        y = src_span[run_end];
+      out += 2;
+      continue;
+    }
+    // Mismatched run
+    while (x != y && run_end <= run_start + max_len) {
+      out[run_end - run_start] = x;
+      x = y;
+      run_end++;
+      if (run_end == src_span.size()) {
+        if (run_end <= run_start + max_len) {
+          out[run_end - run_start] = x;
+          run_end++;
+        }
+        break;
+      }
+      y = src_span[run_end];
+    }
+    out[0] = run_end - run_start - 2;
+    out += run_end - run_start;
+    run_start = run_end - 1;
+  }
+  if (run_start < src_span.size()) {  // 1 leftover character
+    out[0] = 0;
+    out[1] = x;
+    out += 2;
+  }
+  *out = 128;
+  *dest_size = out + 1 - dest_buf->get();
+  return true;
+}
+
+// static
+bool BasicModule::A85Encode(pdfium::span<const uint8_t> src_span,
+                            std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                            uint32_t* dest_size) {
+  // Check inputs.
+  if (!dest_buf || !dest_size)
+    return false;
+
+  if (src_span.empty()) {
+    *dest_size = 0;
+    return false;
+  }
+
+  // Worst case: 5 output for each 4 input (plus up to 4 from leftover), plus
+  // 2 character new lines each 75 output chars plus 2 termination chars. May
+  // have fewer if there are special "z" chars.
+  FX_SAFE_SIZE_T estimated_size = src_span.size();
+  estimated_size /= 4;
+  estimated_size *= 5;
+  estimated_size += 4;
+  estimated_size += src_span.size() / 30;
+  estimated_size += 2;
+  dest_buf->reset(FX_Alloc(uint8_t, estimated_size.ValueOrDie()));
+
+  // Set up pointers.
+  uint8_t* out = dest_buf->get();
+  uint32_t pos = 0;
+  uint32_t line_length = 0;
+  while (src_span.size() >= 4 && pos < src_span.size() - 3) {
+    uint32_t val = ((uint32_t)(src_span[pos]) << 24) +
+                   ((uint32_t)(src_span[pos + 1]) << 16) +
+                   ((uint32_t)(src_span[pos + 2]) << 8) +
+                   (uint32_t)(src_span[pos + 3]);
+    pos += 4;
+    if (val == 0) {  // All zero special case
+      *out = 'z';
+      out++;
+      line_length++;
+    } else {  // Compute base 85 characters and add 33.
+      for (int i = 4; i >= 0; i--) {
+        out[i] = (uint8_t)(val % 85) + 33;
+        val = val / 85;
+      }
+      out += 5;
+      line_length += 5;
+    }
+    if (line_length >= 75) {  // Add a return.
+      *out++ = '\r';
+      *out++ = '\n';
+      line_length = 0;
+    }
+  }
+  if (pos < src_span.size()) {  // Leftover bytes
+    uint32_t val = 0;
+    int count = 0;
+    while (pos < src_span.size()) {
+      val += (uint32_t)(src_span[pos]) << (8 * (3 - count));
+      count++;
+      pos++;
+    }
+    for (int i = 4; i >= 0; i--) {
+      if (i <= count)
+        out[i] = (uint8_t)(val % 85) + 33;
+      val = val / 85;
+    }
+    out += count + 1;
+  }
+
+  // Terminating characters.
+  out[0] = '~';
+  out[1] = '>';
+  out += 2;
+  *dest_size = out - dest_buf->get();
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/basic/basicmodule.h b/core/fxcodec/basic/basicmodule.h
new file mode 100644
index 0000000..5de4da9
--- /dev/null
+++ b/core/fxcodec/basic/basicmodule.h
@@ -0,0 +1,46 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_BASIC_BASICMODULE_H_
+#define CORE_FXCODEC_BASIC_BASICMODULE_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
+
+namespace fxcodec {
+
+class ScanlineDecoder;
+
+class BasicModule {
+ public:
+  static std::unique_ptr<ScanlineDecoder> CreateRunLengthDecoder(
+      pdfium::span<const uint8_t> src_buf,
+      int width,
+      int height,
+      int nComps,
+      int bpc);
+
+  static bool RunLengthEncode(pdfium::span<const uint8_t> src_span,
+                              std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                              uint32_t* dest_size);
+
+  static bool A85Encode(pdfium::span<const uint8_t> src_span,
+                        std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                        uint32_t* dest_size);
+
+  BasicModule() = delete;
+  BasicModule(const BasicModule&) = delete;
+  BasicModule& operator=(const BasicModule&) = delete;
+};
+
+}  // namespace fxcodec
+
+using BasicModule = fxcodec::BasicModule;
+
+#endif  // CORE_FXCODEC_BASIC_BASICMODULE_H_
diff --git a/core/fxcodec/basic/rle_unittest.cpp b/core/fxcodec/basic/rle_unittest.cpp
new file mode 100644
index 0000000..047b447
--- /dev/null
+++ b/core/fxcodec/basic/rle_unittest.cpp
@@ -0,0 +1,159 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fxcodec/basic/basicmodule.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcodec, RLETestBadInputs) {
+  const uint8_t src_buf[] = {1};
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+
+  // Error codes, not segvs, should callers pass us a nullptr pointer.
+  EXPECT_FALSE(BasicModule::RunLengthEncode(src_buf, &dest_buf, nullptr));
+  EXPECT_FALSE(BasicModule::RunLengthEncode(src_buf, nullptr, &dest_size));
+  EXPECT_FALSE(BasicModule::RunLengthEncode({}, &dest_buf, &dest_size));
+}
+
+// Check length 1 input works. Check terminating character is applied.
+TEST(fxcodec, RLETestShortInput) {
+  const uint8_t src_buf[] = {1};
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+
+  EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf, &dest_buf, &dest_size));
+  ASSERT_EQ(3u, dest_size);
+  auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size);
+  EXPECT_EQ(0, dest_buf_span[0]);
+  EXPECT_EQ(1, dest_buf_span[1]);
+  EXPECT_EQ(128, dest_buf_span[2]);
+}
+
+// Check a few basic cases (2 matching runs in a row, matching run followed
+// by a non-matching run, and non-matching run followed by a matching run).
+TEST(fxcodec, RLETestNormalInputs) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+  std::unique_ptr<uint8_t, FxFreeDeleter> decoded_buf;
+  uint32_t decoded_size = 0;
+
+  {
+    // Case 1: Match, match
+    const uint8_t src_buf_1[] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4};
+    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_1, &dest_buf, &dest_size));
+    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    ASSERT_EQ(sizeof(src_buf_1), decoded_size);
+    auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
+    for (uint32_t i = 0; i < decoded_size; i++)
+      EXPECT_EQ(src_buf_1[i], decoded_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // Case 2: Match, non-match
+    const uint8_t src_buf_2[] = {2, 2, 2, 2, 1, 2, 3, 4, 5, 6};
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_2, &dest_buf, &dest_size));
+    decoded_buf.reset();
+    decoded_size = 0;
+    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    ASSERT_EQ(sizeof(src_buf_2), decoded_size);
+    auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
+    for (uint32_t i = 0; i < decoded_size; i++)
+      EXPECT_EQ(src_buf_2[i], decoded_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // Case 3: Non-match, match
+    const uint8_t src_buf_3[] = {1, 2, 3, 4, 5, 3, 3, 3, 3, 3};
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_3, &dest_buf, &dest_size));
+    decoded_buf.reset();
+    decoded_size = 0;
+    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    ASSERT_EQ(sizeof(src_buf_3), decoded_size);
+    auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
+    for (uint32_t i = 0; i < decoded_size; i++)
+      EXPECT_EQ(src_buf_3[i], decoded_buf_span[i]) << " at " << i;
+  }
+}
+
+// Check that runs longer than 128 are broken up properly, both matched and
+// non-matched.
+TEST(fxcodec, RLETestFullLengthInputs) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+  std::unique_ptr<uint8_t, FxFreeDeleter> decoded_buf;
+  uint32_t decoded_size = 0;
+
+  {
+    // Case 1: Match, match
+    const uint8_t src_buf_1[260] = {1};
+    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_1, &dest_buf, &dest_size));
+    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    ASSERT_EQ(sizeof(src_buf_1), decoded_size);
+    auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
+    for (uint32_t i = 0; i < decoded_size; i++)
+      EXPECT_EQ(src_buf_1[i], decoded_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // Case 2: Match, non-match
+    uint8_t src_buf_2[260] = {2};
+    for (uint16_t i = 128; i < 260; i++)
+      src_buf_2[i] = static_cast<uint8_t>(i - 125);
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_2, &dest_buf, &dest_size));
+    decoded_buf.reset();
+    decoded_size = 0;
+    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    ASSERT_EQ(sizeof(src_buf_2), decoded_size);
+    auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
+    for (uint32_t i = 0; i < decoded_size; i++)
+      EXPECT_EQ(src_buf_2[i], decoded_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // Case 3: Non-match, match
+    uint8_t src_buf_3[260] = {3};
+    for (uint8_t i = 0; i < 128; i++)
+      src_buf_3[i] = i;
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_3, &dest_buf, &dest_size));
+    decoded_buf.reset();
+    decoded_size = 0;
+    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    ASSERT_EQ(sizeof(src_buf_3), decoded_size);
+    auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
+    for (uint32_t i = 0; i < decoded_size; i++)
+      EXPECT_EQ(src_buf_3[i], decoded_buf_span[i]) << " at " << i;
+  }
+
+  {
+    // Case 4: Non-match, non-match
+    uint8_t src_buf_4[260];
+    for (uint16_t i = 0; i < 260; i++)
+      src_buf_4[i] = static_cast<uint8_t>(i);
+    dest_buf.reset();
+    dest_size = 0;
+    EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_4, &dest_buf, &dest_size));
+    decoded_buf.reset();
+    decoded_size = 0;
+    RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size);
+    ASSERT_EQ(sizeof(src_buf_4), decoded_size);
+    auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size);
+    for (uint32_t i = 0; i < decoded_size; i++)
+      EXPECT_EQ(src_buf_4[i], decoded_buf_span[i]) << " at " << i;
+  }
+}
diff --git a/core/fxcodec/bmp/bmpmodule.cpp b/core/fxcodec/bmp/bmpmodule.cpp
new file mode 100644
index 0000000..78854c4
--- /dev/null
+++ b/core/fxcodec/bmp/bmpmodule.cpp
@@ -0,0 +1,70 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/bmp/bmpmodule.h"
+
+#include <utility>
+
+#include "core/fxcodec/bmp/cfx_bmpcontext.h"
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxge/fx_dib.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcodec {
+
+BmpModule::BmpModule() = default;
+
+BmpModule::~BmpModule() = default;
+
+std::unique_ptr<ModuleIface::Context> BmpModule::Start(Delegate* pDelegate) {
+  return pdfium::MakeUnique<CFX_BmpContext>(this, pDelegate);
+}
+
+BmpModule::Status BmpModule::ReadHeader(Context* pContext,
+                                        int32_t* width,
+                                        int32_t* height,
+                                        bool* tb_flag,
+                                        int32_t* components,
+                                        int32_t* pal_num,
+                                        const std::vector<uint32_t>** palette,
+                                        CFX_DIBAttribute* pAttribute) {
+  ASSERT(pAttribute);
+
+  auto* ctx = static_cast<CFX_BmpContext*>(pContext);
+  Status status = ctx->m_Bmp.ReadHeader();
+  if (status != Status::kSuccess)
+    return status;
+
+  *width = ctx->m_Bmp.width();
+  *height = ctx->m_Bmp.height();
+  *tb_flag = ctx->m_Bmp.img_tb_flag();
+  *components = ctx->m_Bmp.components();
+  *pal_num = ctx->m_Bmp.pal_num();
+  *palette = ctx->m_Bmp.palette();
+  pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;
+  pAttribute->m_nXDPI = ctx->m_Bmp.dpi_x();
+  pAttribute->m_nYDPI = ctx->m_Bmp.dpi_y();
+  return Status::kSuccess;
+}
+
+BmpModule::Status BmpModule::LoadImage(Context* pContext) {
+  return static_cast<CFX_BmpContext*>(pContext)->m_Bmp.DecodeImage();
+}
+
+FX_FILESIZE BmpModule::GetAvailInput(Context* pContext) const {
+  return static_cast<CFX_BmpContext*>(pContext)->m_Bmp.GetAvailInput();
+}
+
+bool BmpModule::Input(Context* pContext,
+                      RetainPtr<CFX_CodecMemory> codec_memory,
+                      CFX_DIBAttribute*) {
+  auto* ctx = static_cast<CFX_BmpContext*>(pContext);
+  ctx->m_Bmp.SetInputBuffer(std::move(codec_memory));
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/bmp/bmpmodule.h b/core/fxcodec/bmp/bmpmodule.h
new file mode 100644
index 0000000..5630128
--- /dev/null
+++ b/core/fxcodec/bmp/bmpmodule.h
@@ -0,0 +1,56 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_BMP_BMPMODULE_H_
+#define CORE_FXCODEC_BMP_BMPMODULE_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcodec/codec_module_iface.h"
+#include "third_party/base/span.h"
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class BmpModule final : public ModuleIface {
+ public:
+  class Delegate {
+   public:
+    virtual bool BmpInputImagePositionBuf(uint32_t rcd_pos) = 0;
+    virtual void BmpReadScanline(uint32_t row_num,
+                                 pdfium::span<const uint8_t> row_buf) = 0;
+  };
+
+  enum class Status : uint8_t { kFail, kSuccess, kContinue };
+
+  BmpModule();
+  ~BmpModule() override;
+
+  // ModuleIface:
+  FX_FILESIZE GetAvailInput(Context* pContext) const override;
+  bool Input(Context* pContext,
+             RetainPtr<CFX_CodecMemory> codec_memory,
+             CFX_DIBAttribute* pAttribute) override;
+
+  std::unique_ptr<Context> Start(Delegate* pDelegate);
+  Status ReadHeader(Context* pContext,
+                    int32_t* width,
+                    int32_t* height,
+                    bool* tb_flag,
+                    int32_t* components,
+                    int32_t* pal_num,
+                    const std::vector<uint32_t>** palette,
+                    CFX_DIBAttribute* pAttribute);
+  Status LoadImage(Context* pContext);
+};
+
+}  // namespace fxcodec
+
+using BmpModule = fxcodec::BmpModule;
+
+#endif  // CORE_FXCODEC_BMP_BMPMODULE_H_
diff --git a/core/fxcodec/bmp/cfx_bmpcontext.cpp b/core/fxcodec/bmp/cfx_bmpcontext.cpp
new file mode 100644
index 0000000..a35a585
--- /dev/null
+++ b/core/fxcodec/bmp/cfx_bmpcontext.cpp
@@ -0,0 +1,17 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/bmp/cfx_bmpcontext.h"
+
+namespace fxcodec {
+
+CFX_BmpContext::CFX_BmpContext(BmpModule* pModule,
+                               BmpModule::Delegate* pDelegate)
+    : m_Bmp(this), m_pModule(pModule), m_pDelegate(pDelegate) {}
+
+CFX_BmpContext::~CFX_BmpContext() = default;
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/bmp/cfx_bmpcontext.h b/core/fxcodec/bmp/cfx_bmpcontext.h
new file mode 100644
index 0000000..e45caf9
--- /dev/null
+++ b/core/fxcodec/bmp/cfx_bmpcontext.h
@@ -0,0 +1,29 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_BMP_CFX_BMPCONTEXT_H_
+#define CORE_FXCODEC_BMP_CFX_BMPCONTEXT_H_
+
+#include "core/fxcodec/bmp/bmpmodule.h"
+#include "core/fxcodec/bmp/cfx_bmpdecompressor.h"
+#include "core/fxcodec/bmp/fx_bmp.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+namespace fxcodec {
+
+class CFX_BmpContext final : public ModuleIface::Context {
+ public:
+  CFX_BmpContext(BmpModule* pModule, BmpModule::Delegate* pDelegate);
+  ~CFX_BmpContext() override;
+
+  CFX_BmpDecompressor m_Bmp;
+  UnownedPtr<BmpModule> const m_pModule;
+  UnownedPtr<BmpModule::Delegate> const m_pDelegate;
+};
+
+}  // namespace fxcodec
+
+#endif  // CORE_FXCODEC_BMP_CFX_BMPCONTEXT_H_
diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.cpp b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
new file mode 100644
index 0000000..7414647
--- /dev/null
+++ b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
@@ -0,0 +1,676 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/bmp/cfx_bmpdecompressor.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "core/fxcodec/bmp/cfx_bmpcontext.h"
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/numerics/safe_math.h"
+
+namespace fxcodec {
+
+namespace {
+
+#define BMP_PAL_ENCODE(a, r, g, b) \
+  (((uint32_t)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+
+constexpr size_t kBmpCoreHeaderSize = 12;
+constexpr size_t kBmpInfoHeaderSize = 40;
+
+static_assert(sizeof(BmpCoreHeader) == kBmpCoreHeaderSize,
+              "BmpCoreHeader has wrong size");
+static_assert(sizeof(BmpInfoHeader) == kBmpInfoHeaderSize,
+              "BmpInfoHeader has wrong size");
+
+constexpr uint16_t kBmpSignature = 0x4D42;
+constexpr int32_t kBmpPalOld = 1;
+constexpr uint8_t kRleMarker = 0;
+constexpr uint8_t kRleEol = 0;
+constexpr uint8_t kRleEoi = 1;
+constexpr uint8_t kRleDelta = 2;
+constexpr uint32_t kBmpRgb = 0L;
+constexpr uint32_t kBmpRle8 = 1L;
+constexpr uint32_t kBmpRle4 = 2L;
+constexpr uint32_t kBmpBitfields = 3L;
+
+// Limit of image dimension. Use the same limit as the JBIG2 codecs.
+constexpr uint32_t kBmpMaxImageDimension = 65535;
+
+uint8_t HalfRoundUp(uint8_t value) {
+  uint16_t value16 = value;
+  return static_cast<uint8_t>((value16 + 1) / 2);
+}
+
+}  // namespace
+
+CFX_BmpDecompressor::CFX_BmpDecompressor(CFX_BmpContext* context)
+    : context_(context) {}
+
+CFX_BmpDecompressor::~CFX_BmpDecompressor() = default;
+
+void CFX_BmpDecompressor::ReadNextScanline() {
+  uint32_t row = img_tb_flag_ ? row_num_ : (height_ - 1 - row_num_);
+  context_->m_pDelegate->BmpReadScanline(row, out_row_buffer_);
+  ++row_num_;
+}
+
+bool CFX_BmpDecompressor::GetDataPosition(uint32_t rcd_pos) {
+  return context_->m_pDelegate->BmpInputImagePositionBuf(rcd_pos);
+}
+
+BmpModule::Status CFX_BmpDecompressor::ReadHeader() {
+  if (decode_status_ == DecodeStatus::kHeader) {
+    BmpModule::Status status = ReadBmpHeader();
+    if (status != BmpModule::Status::kSuccess)
+      return status;
+  }
+
+  if (decode_status_ != DecodeStatus::kPal)
+    return BmpModule::Status::kSuccess;
+
+  if (compress_flag_ == kBmpBitfields)
+    return ReadBmpBitfields();
+
+  return ReadBmpPalette();
+}
+
+BmpModule::Status CFX_BmpDecompressor::ReadBmpHeader() {
+  BmpFileHeader bmp_header;
+  if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_header),
+                sizeof(BmpFileHeader))) {
+    return BmpModule::Status::kContinue;
+  }
+
+  bmp_header.bfType =
+      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfType));
+  bmp_header.bfOffBits =
+      FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfOffBits));
+  data_size_ =
+      FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&bmp_header.bfSize));
+  if (bmp_header.bfType != kBmpSignature)
+    return BmpModule::Status::kFail;
+
+  size_t pos = input_buffer_->GetPosition();
+  if (!ReadData(reinterpret_cast<uint8_t*>(&img_ifh_size_),
+                sizeof(img_ifh_size_))) {
+    return BmpModule::Status::kContinue;
+  }
+  if (!input_buffer_->Seek(pos))
+    return BmpModule::Status::kFail;
+
+  img_ifh_size_ =
+      FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_ifh_size_));
+  pal_type_ = 0;
+  BmpModule::Status status = ReadBmpHeaderIfh();
+  if (status != BmpModule::Status::kSuccess)
+    return status;
+
+  return ReadBmpHeaderDimensions();
+}
+
+BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderIfh() {
+  if (img_ifh_size_ == kBmpCoreHeaderSize) {
+    pal_type_ = 1;
+    BmpCoreHeader bmp_core_header;
+    if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_core_header),
+                  sizeof(BmpCoreHeader))) {
+      return BmpModule::Status::kContinue;
+    }
+
+    width_ = FXWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_core_header.bcWidth));
+    height_ = FXWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_core_header.bcHeight));
+    bit_counts_ = FXWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_core_header.bcBitCount));
+    compress_flag_ = kBmpRgb;
+    img_tb_flag_ = false;
+    return BmpModule::Status::kSuccess;
+  }
+
+  if (img_ifh_size_ == kBmpInfoHeaderSize) {
+    BmpInfoHeader bmp_info_header;
+    if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_info_header),
+                  sizeof(BmpInfoHeader))) {
+      return BmpModule::Status::kContinue;
+    }
+
+    width_ = FXDWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_info_header.biWidth));
+    int32_t signed_height = FXDWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_info_header.biHeight));
+    bit_counts_ = FXWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_info_header.biBitCount));
+    compress_flag_ = FXDWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_info_header.biCompression));
+    color_used_ = FXDWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_info_header.biClrUsed));
+    dpi_x_ = static_cast<int32_t>(FXDWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_info_header.biXPelsPerMeter)));
+    dpi_y_ = static_cast<int32_t>(FXDWORD_GET_LSBFIRST(
+        reinterpret_cast<uint8_t*>(&bmp_info_header.biYPelsPerMeter)));
+    if (!SetHeight(signed_height))
+      return BmpModule::Status::kFail;
+    return BmpModule::Status::kSuccess;
+  }
+
+  if (img_ifh_size_ <= sizeof(BmpInfoHeader))
+    return BmpModule::Status::kFail;
+
+  FX_SAFE_SIZE_T new_pos = input_buffer_->GetPosition();
+  BmpInfoHeader bmp_info_header;
+  if (!ReadData(reinterpret_cast<uint8_t*>(&bmp_info_header),
+                sizeof(bmp_info_header))) {
+    return BmpModule::Status::kContinue;
+  }
+
+  new_pos += img_ifh_size_;
+  if (!new_pos.IsValid())
+    return BmpModule::Status::kFail;
+
+  if (!input_buffer_->Seek(new_pos.ValueOrDie()))
+    return BmpModule::Status::kContinue;
+
+  uint16_t bi_planes;
+  width_ = FXDWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biWidth));
+  int32_t signed_height = FXDWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biHeight));
+  bit_counts_ = FXWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biBitCount));
+  compress_flag_ = FXDWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biCompression));
+  color_used_ = FXDWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biClrUsed));
+  bi_planes = FXWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biPlanes));
+  dpi_x_ = FXDWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biXPelsPerMeter));
+  dpi_y_ = FXDWORD_GET_LSBFIRST(
+      reinterpret_cast<uint8_t*>(&bmp_info_header.biYPelsPerMeter));
+  if (!SetHeight(signed_height))
+    return BmpModule::Status::kFail;
+  if (compress_flag_ != kBmpRgb || bi_planes != 1 || color_used_ != 0)
+    return BmpModule::Status::kFail;
+  return BmpModule::Status::kSuccess;
+}
+
+BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderDimensions() {
+  if (width_ > kBmpMaxImageDimension || height_ > kBmpMaxImageDimension ||
+      compress_flag_ > kBmpBitfields) {
+    return BmpModule::Status::kFail;
+  }
+
+  switch (bit_counts_) {
+    case 1:
+    case 4:
+    case 8:
+    case 16:
+    case 24: {
+      if (color_used_ > 1U << bit_counts_)
+        return BmpModule::Status::kFail;
+      break;
+    }
+    case 32:
+      break;
+    default:
+      return BmpModule::Status::kFail;
+  }
+  FX_SAFE_UINT32 stride = CalculatePitch32(bit_counts_, width_);
+  if (!stride.IsValid())
+    return BmpModule::Status::kFail;
+
+  src_row_bytes_ = stride.ValueOrDie();
+  switch (bit_counts_) {
+    case 1:
+    case 4:
+    case 8:
+      stride = CalculatePitch32(8, width_);
+      if (!stride.IsValid())
+        return BmpModule::Status::kFail;
+      out_row_bytes_ = stride.ValueOrDie();
+      components_ = 1;
+      break;
+    case 16:
+    case 24:
+      stride = CalculatePitch32(24, width_);
+      if (!stride.IsValid())
+        return BmpModule::Status::kFail;
+      out_row_bytes_ = stride.ValueOrDie();
+      components_ = 3;
+      break;
+    case 32:
+      out_row_bytes_ = src_row_bytes_;
+      components_ = 4;
+      break;
+  }
+  out_row_buffer_.clear();
+
+  if (out_row_bytes_ <= 0)
+    return BmpModule::Status::kFail;
+
+  out_row_buffer_.resize(out_row_bytes_);
+  SaveDecodingStatus(DecodeStatus::kPal);
+  return BmpModule::Status::kSuccess;
+}
+
+BmpModule::Status CFX_BmpDecompressor::ReadBmpBitfields() {
+  if (bit_counts_ != 16 && bit_counts_ != 32)
+    return BmpModule::Status::kFail;
+
+  uint32_t masks[3];
+  if (!ReadData(reinterpret_cast<uint8_t*>(masks), sizeof(masks)))
+    return BmpModule::Status::kContinue;
+
+  mask_red_ = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[0]));
+  mask_green_ = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[1]));
+  mask_blue_ = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&masks[2]));
+  if (mask_red_ & mask_green_ || mask_red_ & mask_blue_ ||
+      mask_green_ & mask_blue_) {
+    return BmpModule::Status::kFail;
+  }
+  header_offset_ = std::max(header_offset_, 26 + img_ifh_size_);
+  SaveDecodingStatus(DecodeStatus::kDataPre);
+  return BmpModule::Status::kSuccess;
+}
+
+BmpModule::Status CFX_BmpDecompressor::ReadBmpPalette() {
+  if (bit_counts_ == 16) {
+    mask_red_ = 0x7C00;
+    mask_green_ = 0x03E0;
+    mask_blue_ = 0x001F;
+  }
+  pal_num_ = 0;
+  if (bit_counts_ < 16) {
+    pal_num_ = 1 << bit_counts_;
+    if (color_used_ != 0)
+      pal_num_ = color_used_;
+    uint32_t src_pal_size = pal_num_ * (pal_type_ ? 3 : 4);
+    std::vector<uint8_t, FxAllocAllocator<uint8_t>> src_pal(src_pal_size);
+    uint8_t* src_pal_data = src_pal.data();
+    if (!ReadData(src_pal_data, src_pal_size))
+      return BmpModule::Status::kContinue;
+
+    palette_.resize(pal_num_);
+    int32_t src_pal_index = 0;
+    if (pal_type_ == kBmpPalOld) {
+      while (src_pal_index < pal_num_) {
+        palette_[src_pal_index++] = BMP_PAL_ENCODE(
+            0x00, src_pal_data[2], src_pal_data[1], src_pal_data[0]);
+        src_pal_data += 3;
+      }
+    } else {
+      while (src_pal_index < pal_num_) {
+        palette_[src_pal_index++] = BMP_PAL_ENCODE(
+            src_pal_data[3], src_pal_data[2], src_pal_data[1], src_pal_data[0]);
+        src_pal_data += 4;
+      }
+    }
+  }
+  header_offset_ = std::max(
+      header_offset_, 14 + img_ifh_size_ + pal_num_ * (pal_type_ ? 3 : 4));
+  SaveDecodingStatus(DecodeStatus::kDataPre);
+  return BmpModule::Status::kSuccess;
+}
+
+bool CFX_BmpDecompressor::ValidateFlag() const {
+  switch (compress_flag_) {
+    case kBmpRgb:
+    case kBmpBitfields:
+    case kBmpRle8:
+    case kBmpRle4:
+      return true;
+    default:
+      return false;
+  }
+}
+
+BmpModule::Status CFX_BmpDecompressor::DecodeImage() {
+  if (decode_status_ == DecodeStatus::kDataPre) {
+    input_buffer_->Seek(0);
+    if (!GetDataPosition(header_offset_)) {
+      decode_status_ = DecodeStatus::kTail;
+      return BmpModule::Status::kFail;
+    }
+
+    row_num_ = 0;
+    SaveDecodingStatus(DecodeStatus::kData);
+  }
+  if (decode_status_ != DecodeStatus::kData || !ValidateFlag())
+    return BmpModule::Status::kFail;
+
+  switch (compress_flag_) {
+    case kBmpRgb:
+    case kBmpBitfields:
+      return DecodeRGB();
+    case kBmpRle8:
+      return DecodeRLE8();
+    case kBmpRle4:
+      return DecodeRLE4();
+    default:
+      return BmpModule::Status::kFail;
+  }
+}
+
+bool CFX_BmpDecompressor::ValidateColorIndex(uint8_t val) const {
+  return val < pal_num_;
+}
+
+BmpModule::Status CFX_BmpDecompressor::DecodeRGB() {
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> dest_buf(src_row_bytes_);
+  while (row_num_ < height_) {
+    size_t idx = 0;
+    if (!ReadData(dest_buf.data(), src_row_bytes_))
+      return BmpModule::Status::kContinue;
+
+    SaveDecodingStatus(DecodeStatus::kData);
+    switch (bit_counts_) {
+      case 1: {
+        for (uint32_t col = 0; col < width_; ++col)
+          out_row_buffer_[idx++] =
+              dest_buf[col >> 3] & (0x80 >> (col % 8)) ? 0x01 : 0x00;
+        break;
+      }
+      case 4: {
+        for (uint32_t col = 0; col < width_; ++col) {
+          out_row_buffer_[idx++] = (col & 0x01)
+                                       ? (dest_buf[col >> 1] & 0x0F)
+                                       : ((dest_buf[col >> 1] & 0xF0) >> 4);
+        }
+        break;
+      }
+      case 16: {
+        uint16_t* buf = reinterpret_cast<uint16_t*>(dest_buf.data());
+        uint8_t blue_bits = 0;
+        uint8_t green_bits = 0;
+        uint8_t red_bits = 0;
+        for (int32_t i = 0; i < 16; i++) {
+          if ((mask_blue_ >> i) & 0x01)
+            blue_bits++;
+          if ((mask_green_ >> i) & 0x01)
+            green_bits++;
+          if ((mask_red_ >> i) & 0x01)
+            red_bits++;
+        }
+        green_bits += blue_bits;
+        red_bits += green_bits;
+        if (blue_bits > 8 || green_bits < 8 || red_bits < 8)
+          return BmpModule::Status::kContinue;
+        blue_bits = 8 - blue_bits;
+        green_bits -= 8;
+        red_bits -= 8;
+        for (uint32_t col = 0; col < width_; ++col) {
+          *buf = FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(buf));
+          out_row_buffer_[idx++] =
+              static_cast<uint8_t>((*buf & mask_blue_) << blue_bits);
+          out_row_buffer_[idx++] =
+              static_cast<uint8_t>((*buf & mask_green_) >> green_bits);
+          out_row_buffer_[idx++] =
+              static_cast<uint8_t>((*buf++ & mask_red_) >> red_bits);
+        }
+        break;
+      }
+      case 8:
+      case 24:
+      case 32:
+        uint8_t* dest_buf_data = dest_buf.data();
+        std::copy(dest_buf_data, dest_buf_data + src_row_bytes_,
+                  out_row_buffer_.begin());
+        idx += src_row_bytes_;
+        break;
+    }
+    for (uint8_t byte : out_row_buffer_) {
+      if (!ValidateColorIndex(byte))
+        return BmpModule::Status::kFail;
+    }
+    ReadNextScanline();
+  }
+  SaveDecodingStatus(DecodeStatus::kTail);
+  return BmpModule::Status::kSuccess;
+}
+
+BmpModule::Status CFX_BmpDecompressor::DecodeRLE8() {
+  uint8_t first_part;
+  col_num_ = 0;
+  while (true) {
+    if (!ReadData(&first_part, sizeof(first_part)))
+      return BmpModule::Status::kContinue;
+
+    switch (first_part) {
+      case kRleMarker: {
+        if (!ReadData(&first_part, sizeof(first_part)))
+          return BmpModule::Status::kContinue;
+
+        switch (first_part) {
+          case kRleEol: {
+            if (row_num_ >= height_) {
+              SaveDecodingStatus(DecodeStatus::kTail);
+              return BmpModule::Status::kFail;
+            }
+
+            ReadNextScanline();
+            col_num_ = 0;
+            std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+            SaveDecodingStatus(DecodeStatus::kData);
+            continue;
+          }
+          case kRleEoi: {
+            if (row_num_ < height_)
+              ReadNextScanline();
+            SaveDecodingStatus(DecodeStatus::kTail);
+            return BmpModule::Status::kSuccess;
+          }
+          case kRleDelta: {
+            uint8_t delta[2];
+            if (!ReadData(delta, sizeof(delta)))
+              return BmpModule::Status::kContinue;
+
+            col_num_ += delta[0];
+            size_t bmp_row_num__next = row_num_ + delta[1];
+            if (col_num_ >= out_row_bytes_ || bmp_row_num__next >= height_)
+              return BmpModule::Status::kFail;
+
+            while (row_num_ < bmp_row_num__next) {
+              std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+              ReadNextScanline();
+            }
+            break;
+          }
+          default: {
+            int32_t avail_size = out_row_bytes_ - col_num_;
+            if (!avail_size || static_cast<int32_t>(first_part) > avail_size)
+              return BmpModule::Status::kFail;
+
+            size_t second_part_size =
+                first_part & 1 ? first_part + 1 : first_part;
+            std::vector<uint8_t, FxAllocAllocator<uint8_t>> second_part(
+                second_part_size);
+            uint8_t* second_part_data = second_part.data();
+            if (!ReadData(second_part_data, second_part_size))
+              return BmpModule::Status::kContinue;
+
+            std::copy(second_part_data, second_part_data + first_part,
+                      out_row_buffer_.begin() + col_num_);
+            for (size_t i = col_num_; i < col_num_ + first_part; ++i) {
+              if (!ValidateColorIndex(out_row_buffer_[i]))
+                return BmpModule::Status::kFail;
+            }
+            col_num_ += first_part;
+          }
+        }
+        break;
+      }
+      default: {
+        int32_t avail_size = out_row_bytes_ - col_num_;
+        if (!avail_size || static_cast<int32_t>(first_part) > avail_size)
+          return BmpModule::Status::kFail;
+
+        uint8_t second_part;
+        if (!ReadData(&second_part, sizeof(second_part)))
+          return BmpModule::Status::kContinue;
+
+        std::fill(out_row_buffer_.begin() + col_num_,
+                  out_row_buffer_.begin() + col_num_ + first_part, second_part);
+        if (!ValidateColorIndex(out_row_buffer_[col_num_]))
+          return BmpModule::Status::kFail;
+        col_num_ += first_part;
+      }
+    }
+  }
+  return BmpModule::Status::kFail;
+}
+
+BmpModule::Status CFX_BmpDecompressor::DecodeRLE4() {
+  uint8_t first_part;
+  col_num_ = 0;
+  while (true) {
+    if (!ReadData(&first_part, sizeof(first_part)))
+      return BmpModule::Status::kContinue;
+
+    switch (first_part) {
+      case kRleMarker: {
+        if (!ReadData(&first_part, sizeof(first_part)))
+          return BmpModule::Status::kContinue;
+
+        switch (first_part) {
+          case kRleEol: {
+            if (row_num_ >= height_) {
+              SaveDecodingStatus(DecodeStatus::kTail);
+              return BmpModule::Status::kFail;
+            }
+
+            ReadNextScanline();
+            col_num_ = 0;
+            std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+            SaveDecodingStatus(DecodeStatus::kData);
+            continue;
+          }
+          case kRleEoi: {
+            if (row_num_ < height_)
+              ReadNextScanline();
+            SaveDecodingStatus(DecodeStatus::kTail);
+            return BmpModule::Status::kSuccess;
+          }
+          case kRleDelta: {
+            uint8_t delta[2];
+            if (!ReadData(delta, sizeof(delta)))
+              return BmpModule::Status::kContinue;
+
+            col_num_ += delta[0];
+            size_t bmp_row_num__next = row_num_ + delta[1];
+            if (col_num_ >= out_row_bytes_ || bmp_row_num__next >= height_)
+              return BmpModule::Status::kFail;
+
+            while (row_num_ < bmp_row_num__next) {
+              std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0);
+              ReadNextScanline();
+            }
+            break;
+          }
+          default: {
+            int32_t avail_size = out_row_bytes_ - col_num_;
+            if (!avail_size)
+              return BmpModule::Status::kFail;
+            uint8_t size = HalfRoundUp(first_part);
+            if (static_cast<int32_t>(first_part) > avail_size) {
+              if (size + (col_num_ >> 1) > src_row_bytes_)
+                return BmpModule::Status::kFail;
+
+              first_part = avail_size - 1;
+            }
+            size_t second_part_size = size & 1 ? size + 1 : size;
+            std::vector<uint8_t, FxAllocAllocator<uint8_t>> second_part(
+                second_part_size);
+            uint8_t* second_part_data = second_part.data();
+            if (!ReadData(second_part_data, second_part_size))
+              return BmpModule::Status::kContinue;
+
+            for (uint8_t i = 0; i < first_part; i++) {
+              uint8_t color = (i & 0x01) ? (*second_part_data++ & 0x0F)
+                                         : (*second_part_data & 0xF0) >> 4;
+              if (!ValidateColorIndex(color))
+                return BmpModule::Status::kFail;
+
+              out_row_buffer_[col_num_++] = color;
+            }
+          }
+        }
+        break;
+      }
+      default: {
+        int32_t avail_size = out_row_bytes_ - col_num_;
+        if (!avail_size)
+          return BmpModule::Status::kFail;
+
+        if (static_cast<int32_t>(first_part) > avail_size) {
+          uint8_t size = HalfRoundUp(first_part);
+          if (size + (col_num_ >> 1) > src_row_bytes_)
+            return BmpModule::Status::kFail;
+
+          first_part = avail_size - 1;
+        }
+        uint8_t second_part;
+        if (!ReadData(&second_part, sizeof(second_part)))
+          return BmpModule::Status::kContinue;
+
+        for (uint8_t i = 0; i < first_part; i++) {
+          uint8_t second_byte = second_part;
+          second_byte =
+              i & 0x01 ? (second_byte & 0x0F) : (second_byte & 0xF0) >> 4;
+          if (!ValidateColorIndex(second_byte))
+            return BmpModule::Status::kFail;
+
+          out_row_buffer_[col_num_++] = second_byte;
+        }
+      }
+    }
+  }
+  return BmpModule::Status::kFail;
+}
+
+bool CFX_BmpDecompressor::ReadData(uint8_t* destination, uint32_t size) {
+  return input_buffer_ && input_buffer_->ReadBlock(destination, size) == size;
+}
+
+void CFX_BmpDecompressor::SaveDecodingStatus(DecodeStatus status) {
+  decode_status_ = status;
+}
+
+void CFX_BmpDecompressor::SetInputBuffer(
+    RetainPtr<CFX_CodecMemory> codec_memory) {
+  input_buffer_ = std::move(codec_memory);
+}
+
+FX_FILESIZE CFX_BmpDecompressor::GetAvailInput() const {
+  if (!input_buffer_)
+    return 0;
+
+  return input_buffer_->GetSize() - input_buffer_->GetPosition();
+}
+
+bool CFX_BmpDecompressor::SetHeight(int32_t signed_height) {
+  if (signed_height >= 0) {
+    height_ = signed_height;
+    return true;
+  }
+  if (signed_height != std::numeric_limits<int>::min()) {
+    height_ = -signed_height;
+    img_tb_flag_ = true;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.h b/core/fxcodec/bmp/cfx_bmpdecompressor.h
new file mode 100644
index 0000000..3f4e2d8
--- /dev/null
+++ b/core/fxcodec/bmp/cfx_bmpdecompressor.h
@@ -0,0 +1,97 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_BMP_CFX_BMPDECOMPRESSOR_H_
+#define CORE_FXCODEC_BMP_CFX_BMPDECOMPRESSOR_H_
+
+#include <vector>
+
+#include "core/fxcodec/bmp/bmpmodule.h"
+#include "core/fxcodec/bmp/fx_bmp.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CFX_CodecMemory;
+
+namespace fxcodec {
+
+class CFX_BmpContext;
+
+class CFX_BmpDecompressor {
+ public:
+  explicit CFX_BmpDecompressor(CFX_BmpContext* context);
+  ~CFX_BmpDecompressor();
+
+  BmpModule::Status DecodeImage();
+  BmpModule::Status ReadHeader();
+  void SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory);
+  FX_FILESIZE GetAvailInput() const;
+
+  const std::vector<uint32_t>* palette() const { return &palette_; }
+  uint32_t width() const { return width_; }
+  uint32_t height() const { return height_; }
+  int32_t components() const { return components_; }
+  bool img_tb_flag() const { return img_tb_flag_; }
+  int32_t pal_num() const { return pal_num_; }
+  int32_t dpi_x() const { return dpi_x_; }
+  int32_t dpi_y() const { return dpi_y_; }
+
+ private:
+  enum class DecodeStatus : uint8_t {
+    kHeader,
+    kPal,
+    kDataPre,
+    kData,
+    kTail,
+  };
+
+  BmpModule::Status ReadBmpHeader();
+  BmpModule::Status ReadBmpHeaderIfh();
+  BmpModule::Status ReadBmpHeaderDimensions();
+  BmpModule::Status ReadBmpBitfields();
+  BmpModule::Status ReadBmpPalette();
+  bool GetDataPosition(uint32_t cur_pos);
+  void ReadNextScanline();
+  BmpModule::Status DecodeRGB();
+  BmpModule::Status DecodeRLE8();
+  BmpModule::Status DecodeRLE4();
+  bool ReadData(uint8_t* destination, uint32_t size);
+  void SaveDecodingStatus(DecodeStatus status);
+  bool ValidateColorIndex(uint8_t val) const;
+  bool ValidateFlag() const;
+  bool SetHeight(int32_t signed_height);
+
+  UnownedPtr<CFX_BmpContext> const context_;
+  std::vector<uint8_t> out_row_buffer_;
+  std::vector<uint32_t> palette_;
+  uint32_t header_offset_ = 0;
+  uint32_t width_ = 0;
+  uint32_t height_ = 0;
+  uint32_t compress_flag_ = 0;
+  int32_t components_ = 0;
+  size_t src_row_bytes_ = 0;
+  size_t out_row_bytes_ = 0;
+  bool img_tb_flag_ = false;
+  uint16_t bit_counts_ = 0;
+  uint32_t color_used_ = 0;
+  int32_t pal_num_ = 0;
+  int32_t pal_type_ = 0;
+  uint32_t data_size_ = 0;
+  uint32_t img_ifh_size_ = 0;
+  uint32_t row_num_ = 0;
+  uint32_t col_num_ = 0;
+  int32_t dpi_x_ = 0;
+  int32_t dpi_y_ = 0;
+  uint32_t mask_red_ = 0;
+  uint32_t mask_green_ = 0;
+  uint32_t mask_blue_ = 0;
+  DecodeStatus decode_status_ = DecodeStatus::kHeader;
+  RetainPtr<CFX_CodecMemory> input_buffer_;
+};
+
+}  // namespace fxcodec
+
+#endif  // CORE_FXCODEC_BMP_CFX_BMPDECOMPRESSOR_H_
diff --git a/core/fxcodec/bmp/fx_bmp.h b/core/fxcodec/bmp/fx_bmp.h
new file mode 100644
index 0000000..79c61bb
--- /dev/null
+++ b/core/fxcodec/bmp/fx_bmp.h
@@ -0,0 +1,48 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_BMP_FX_BMP_H_
+#define CORE_FXCODEC_BMP_FX_BMP_H_
+
+#include <stdint.h>
+
+#pragma pack(1)
+struct BmpFileHeader {
+  uint16_t bfType;
+  uint32_t bfSize;
+  uint16_t bfReserved1;
+  uint16_t bfReserved2;
+  uint32_t bfOffBits;
+};
+
+struct BmpCoreHeader {
+  uint32_t bcSize;
+  uint16_t bcWidth;
+  uint16_t bcHeight;
+  uint16_t bcPlanes;
+  uint16_t bcBitCount;
+};
+
+struct BmpInfoHeader {
+  uint32_t biSize;
+  int32_t biWidth;
+  int32_t biHeight;
+  uint16_t biPlanes;
+  uint16_t biBitCount;
+  uint32_t biCompression;
+  uint32_t biSizeImage;
+  int32_t biXPelsPerMeter;
+  int32_t biYPelsPerMeter;
+  uint32_t biClrUsed;
+  uint32_t biClrImportant;
+};
+#pragma pack()
+
+static_assert(sizeof(BmpFileHeader) == 14, "BmpFileHeader has wrong size");
+static_assert(sizeof(BmpCoreHeader) == 12, "BmpCoreHeader has wrong size");
+static_assert(sizeof(BmpInfoHeader) == 40, "BmpInfoHeader has wrong size");
+
+#endif  // CORE_FXCODEC_BMP_FX_BMP_H_
diff --git a/core/fxcodec/cfx_codec_memory.cpp b/core/fxcodec/cfx_codec_memory.cpp
new file mode 100644
index 0000000..256e15c
--- /dev/null
+++ b/core/fxcodec/cfx_codec_memory.cpp
@@ -0,0 +1,47 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcodec/cfx_codec_memory.h"
+
+#include <algorithm>
+
+CFX_CodecMemory::CFX_CodecMemory(size_t buffer_size)
+    : buffer_(FX_Alloc(uint8_t, buffer_size)), size_(buffer_size) {}
+
+CFX_CodecMemory::~CFX_CodecMemory() = default;
+
+bool CFX_CodecMemory::Seek(size_t pos) {
+  if (pos > size_)
+    return false;
+
+  pos_ = pos;
+  return true;
+}
+
+size_t CFX_CodecMemory::ReadBlock(void* buffer, size_t size) {
+  if (!buffer || !size || IsEOF())
+    return 0;
+
+  size_t bytes_to_read = std::min(size, size_ - pos_);
+  memcpy(buffer, buffer_.get() + pos_, bytes_to_read);
+  pos_ += bytes_to_read;
+  return bytes_to_read;
+}
+
+bool CFX_CodecMemory::TryResize(size_t new_buffer_size) {
+  uint8_t* pOldBuf = buffer_.release();
+  uint8_t* pNewBuf = FX_TryRealloc(uint8_t, pOldBuf, new_buffer_size);
+  if (new_buffer_size && !pNewBuf) {
+    buffer_.reset(pOldBuf);
+    return false;
+  }
+  buffer_.reset(pNewBuf);
+  size_ = new_buffer_size;
+  return true;
+}
+
+void CFX_CodecMemory::Consume(size_t consumed) {
+  size_t unconsumed = size_ - consumed;
+  memmove(buffer_.get(), buffer_.get() + consumed, unconsumed);
+}
diff --git a/core/fxcodec/cfx_codec_memory.h b/core/fxcodec/cfx_codec_memory.h
new file mode 100644
index 0000000..57c1ae6
--- /dev/null
+++ b/core/fxcodec/cfx_codec_memory.h
@@ -0,0 +1,44 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCODEC_CFX_CODEC_MEMORY_H_
+#define CORE_FXCODEC_CFX_CODEC_MEMORY_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/span.h"
+
+class CFX_CodecMemory final : public Retainable {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  pdfium::span<uint8_t> GetSpan() { return {buffer_.get(), size_}; }
+  uint8_t* GetBuffer() { return buffer_.get(); }
+  size_t GetSize() const { return size_; }
+  size_t GetPosition() const { return pos_; }
+  bool IsEOF() const { return pos_ >= size_; }
+  size_t ReadBlock(void* buffer, size_t size);
+
+  // Sets the cursor position to |pos| if possible.
+  bool Seek(size_t pos);
+
+  // Try to change the size of the buffer, keep the old one on failure.
+  bool TryResize(size_t new_buffer_size);
+
+  // Schlep the bytes down the buffer.
+  void Consume(size_t consumed);
+
+ private:
+  explicit CFX_CodecMemory(size_t buffer_size);
+  ~CFX_CodecMemory() override;
+
+  std::unique_ptr<uint8_t, FxFreeDeleter> buffer_;
+  size_t size_ = 0;
+  size_t pos_ = 0;
+};
+
+#endif  // CORE_FXCODEC_CFX_CODEC_MEMORY_H_
diff --git a/core/fxcodec/codec/DEPS b/core/fxcodec/codec/DEPS
deleted file mode 100644
index 495d288..0000000
--- a/core/fxcodec/codec/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
-  '+third_party/libpng16/png.h',
-  '+third_party/libtiff/tiffiop.h',
-]
diff --git a/core/fxcodec/codec/ccodec_basicmodule.h b/core/fxcodec/codec/ccodec_basicmodule.h
deleted file mode 100644
index 015f41a..0000000
--- a/core/fxcodec/codec/ccodec_basicmodule.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_BASICMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_BASICMODULE_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-
-class CCodec_ScanlineDecoder;
-
-class CCodec_BasicModule {
- public:
-  std::unique_ptr<CCodec_ScanlineDecoder> CreateRunLengthDecoder(
-      const uint8_t* src_buf,
-      uint32_t src_size,
-      int width,
-      int height,
-      int nComps,
-      int bpc);
-  bool RunLengthEncode(const uint8_t* src_buf,
-                       uint32_t src_size,
-                       uint8_t** dest_buf,
-                       uint32_t* dest_size);
-  bool A85Encode(const uint8_t* src_buf,
-                 uint32_t src_size,
-                 uint8_t** dest_buf,
-                 uint32_t* dest_size);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_BASICMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_bmpmodule.cpp b/core/fxcodec/codec/ccodec_bmpmodule.cpp
deleted file mode 100644
index b2fcca8..0000000
--- a/core/fxcodec/codec/ccodec_bmpmodule.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/ccodec_bmpmodule.h"
-
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/lbmp/fx_bmp.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
-
-CBmpContext::CBmpContext(CCodec_BmpModule* pModule,
-                         CCodec_BmpModule::Delegate* pDelegate)
-    : m_pModule(pModule), m_pDelegate(pDelegate) {
-}
-
-CBmpContext::~CBmpContext() {}
-
-CCodec_BmpModule::CCodec_BmpModule() {}
-
-CCodec_BmpModule::~CCodec_BmpModule() {}
-
-std::unique_ptr<CCodec_BmpModule::Context> CCodec_BmpModule::Start(
-    Delegate* pDelegate) {
-  auto p = pdfium::MakeUnique<CBmpContext>(this, pDelegate);
-  p->m_Bmp.context_ptr = p.get();
-  return p;
-}
-
-int32_t CCodec_BmpModule::ReadHeader(Context* pContext,
-                                     int32_t* width,
-                                     int32_t* height,
-                                     bool* tb_flag,
-                                     int32_t* components,
-                                     int32_t* pal_num,
-                                     std::vector<uint32_t>* palette,
-                                     CFX_DIBAttribute* pAttribute) {
-  auto* ctx = static_cast<CBmpContext*>(pContext);
-  if (setjmp(ctx->m_Bmp.jmpbuf))
-    return 0;
-
-  int32_t ret = ctx->m_Bmp.ReadHeader();
-  if (ret != 1)
-    return ret;
-
-  *width = ctx->m_Bmp.width;
-  *height = ctx->m_Bmp.height;
-  *tb_flag = ctx->m_Bmp.imgTB_flag;
-  *components = ctx->m_Bmp.components;
-  *pal_num = ctx->m_Bmp.pal_num;
-  *palette = ctx->m_Bmp.palette;
-  if (pAttribute) {
-    pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;
-    pAttribute->m_nXDPI = ctx->m_Bmp.dpi_x;
-    pAttribute->m_nYDPI = ctx->m_Bmp.dpi_y;
-    pAttribute->m_nBmpCompressType = ctx->m_Bmp.compress_flag;
-  }
-  return 1;
-}
-
-int32_t CCodec_BmpModule::LoadImage(Context* pContext) {
-  auto* ctx = static_cast<CBmpContext*>(pContext);
-  if (setjmp(ctx->m_Bmp.jmpbuf))
-    return 0;
-
-  return ctx->m_Bmp.DecodeImage();
-}
-
-uint32_t CCodec_BmpModule::GetAvailInput(Context* pContext,
-                                         uint8_t** avail_buf_ptr) {
-  auto* ctx = static_cast<CBmpContext*>(pContext);
-  return ctx->m_Bmp.GetAvailInput(avail_buf_ptr);
-}
-
-void CCodec_BmpModule::Input(Context* pContext,
-                             const uint8_t* src_buf,
-                             uint32_t src_size) {
-  auto* ctx = static_cast<CBmpContext*>(pContext);
-  ctx->m_Bmp.SetInputBuffer(const_cast<uint8_t*>(src_buf), src_size);
-}
diff --git a/core/fxcodec/codec/ccodec_bmpmodule.h b/core/fxcodec/codec/ccodec_bmpmodule.h
deleted file mode 100644
index 8a33f66..0000000
--- a/core/fxcodec/codec/ccodec_bmpmodule.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_BMPMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_BMPMODULE_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CFX_DIBAttribute;
-
-class CCodec_BmpModule {
- public:
-  class Context {
-   public:
-    virtual ~Context() {}
-  };
-
-  class Delegate {
-   public:
-    virtual bool BmpInputImagePositionBuf(uint32_t rcd_pos) = 0;
-    virtual void BmpReadScanline(uint32_t row_num,
-                                 const std::vector<uint8_t>& row_buf) = 0;
-  };
-
-  CCodec_BmpModule();
-  ~CCodec_BmpModule();
-
-  std::unique_ptr<Context> Start(Delegate* pDelegate);
-  uint32_t GetAvailInput(Context* pContext, uint8_t** avail_buf_ptr);
-  void Input(Context* pContext, const uint8_t* src_buf, uint32_t src_size);
-  int32_t ReadHeader(Context* pContext,
-                     int32_t* width,
-                     int32_t* height,
-                     bool* tb_flag,
-                     int32_t* components,
-                     int32_t* pal_num,
-                     std::vector<uint32_t>* palette,
-                     CFX_DIBAttribute* pAttribute);
-  int32_t LoadImage(Context* pContext);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_BMPMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_faxmodule.h b/core/fxcodec/codec/ccodec_faxmodule.h
deleted file mode 100644
index 4f8ab78..0000000
--- a/core/fxcodec/codec/ccodec_faxmodule.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_FAXMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_FAXMODULE_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_system.h"
-
-class CCodec_ScanlineDecoder;
-
-class CCodec_FaxModule {
- public:
-  std::unique_ptr<CCodec_ScanlineDecoder> CreateDecoder(const uint8_t* src_buf,
-                                                        uint32_t src_size,
-                                                        int width,
-                                                        int height,
-                                                        int K,
-                                                        bool EndOfLine,
-                                                        bool EncodedByteAlign,
-                                                        bool BlackIs1,
-                                                        int Columns,
-                                                        int Rows);
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  static void FaxEncode(const uint8_t* src_buf,
-                        int width,
-                        int height,
-                        int pitch,
-                        std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                        uint32_t* dest_size);
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_FAXMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_flatemodule.h b/core/fxcodec/codec/ccodec_flatemodule.h
deleted file mode 100644
index ae34103..0000000
--- a/core/fxcodec/codec/ccodec_flatemodule.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_FXCODEC_CODEC_CCODEC_FLATEMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_FLATEMODULE_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-
-class CCodec_ScanlineDecoder;
-
-class CCodec_FlateModule {
- public:
-  std::unique_ptr<CCodec_ScanlineDecoder> CreateDecoder(const uint8_t* src_buf,
-                                                        uint32_t src_size,
-                                                        int width,
-                                                        int height,
-                                                        int nComps,
-                                                        int bpc,
-                                                        int predictor,
-                                                        int Colors,
-                                                        int BitsPerComponent,
-                                                        int Columns);
-  uint32_t FlateOrLZWDecode(bool bLZW,
-                            const uint8_t* src_buf,
-                            uint32_t src_size,
-                            bool bEarlyChange,
-                            int predictor,
-                            int Colors,
-                            int BitsPerComponent,
-                            int Columns,
-                            uint32_t estimated_size,
-                            uint8_t** dest_buf,
-                            uint32_t* dest_size);
-
-  bool Encode(const uint8_t* src_buf,
-              uint32_t src_size,
-              uint8_t** dest_buf,
-              uint32_t* dest_size);
-
-  bool PngEncode(const uint8_t* src_buf,
-                 uint32_t src_size,
-                 uint8_t** dest_buf,
-                 uint32_t* dest_size);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_FLATEMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_gifmodule.cpp b/core/fxcodec/codec/ccodec_gifmodule.cpp
deleted file mode 100644
index d6ab10d..0000000
--- a/core/fxcodec/codec/ccodec_gifmodule.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/ccodec_gifmodule.h"
-
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/gif/cfx_gif.h"
-#include "core/fxcodec/gif/cfx_gifcontext.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
-
-CCodec_GifModule::CCodec_GifModule() {}
-
-CCodec_GifModule::~CCodec_GifModule() {}
-
-std::unique_ptr<CCodec_GifModule::Context> CCodec_GifModule::Start(
-    Delegate* pDelegate) {
-  return pdfium::MakeUnique<CFX_GifContext>(this, pDelegate);
-}
-
-CFX_GifDecodeStatus CCodec_GifModule::ReadHeader(Context* pContext,
-                                                 int* width,
-                                                 int* height,
-                                                 int* pal_num,
-                                                 void** pal_pp,
-                                                 int* bg_index,
-                                                 CFX_DIBAttribute* pAttribute) {
-  auto* context = static_cast<CFX_GifContext*>(pContext);
-  CFX_GifDecodeStatus ret = context->ReadHeader();
-  if (ret != CFX_GifDecodeStatus::Success)
-    return ret;
-
-  *width = context->width_;
-  *height = context->height_;
-  *pal_num = (2 << context->global_pal_exp_);
-  *pal_pp = context->global_palette_.empty() ? nullptr
-                                             : context->global_palette_.data();
-  *bg_index = context->bc_index_;
-  return CFX_GifDecodeStatus::Success;
-}
-
-std::pair<CFX_GifDecodeStatus, size_t> CCodec_GifModule::LoadFrameInfo(
-    Context* pContext) {
-  auto* context = static_cast<CFX_GifContext*>(pContext);
-  CFX_GifDecodeStatus ret = context->GetFrame();
-  if (ret != CFX_GifDecodeStatus::Success)
-    return {ret, 0};
-  return {CFX_GifDecodeStatus::Success, context->GetFrameNum()};
-}
-
-CFX_GifDecodeStatus CCodec_GifModule::LoadFrame(Context* pContext,
-                                                size_t frame_num,
-                                                CFX_DIBAttribute* pAttribute) {
-  auto* context = static_cast<CFX_GifContext*>(pContext);
-  CFX_GifDecodeStatus ret = context->LoadFrame(frame_num);
-  if (ret != CFX_GifDecodeStatus::Success || !pAttribute)
-    return ret;
-
-  pAttribute->m_nGifLeft = context->images_[frame_num]->image_info.left;
-  pAttribute->m_nGifTop = context->images_[frame_num]->image_info.top;
-  pAttribute->m_fAspectRatio = context->pixel_aspect_;
-  const uint8_t* buf =
-      reinterpret_cast<const uint8_t*>(context->cmt_data_.GetBuffer(0));
-  uint32_t len = context->cmt_data_.GetLength();
-  if (len > 21) {
-    uint8_t size = *buf++;
-    if (size != 0)
-      pAttribute->m_strAuthor = ByteString(buf, size);
-    else
-      pAttribute->m_strAuthor.clear();
-  }
-  return CFX_GifDecodeStatus::Success;
-}
-
-uint32_t CCodec_GifModule::GetAvailInput(Context* pContext,
-                                         uint8_t** avail_buf_ptr) {
-  auto* context = static_cast<CFX_GifContext*>(pContext);
-  return context->GetAvailInput(avail_buf_ptr);
-}
-
-void CCodec_GifModule::Input(Context* pContext,
-                             const uint8_t* src_buf,
-                             uint32_t src_size) {
-  auto* context = static_cast<CFX_GifContext*>(pContext);
-  context->SetInputBuffer((uint8_t*)src_buf, src_size);
-}
diff --git a/core/fxcodec/codec/ccodec_gifmodule.h b/core/fxcodec/codec/ccodec_gifmodule.h
deleted file mode 100644
index 5ceff7b..0000000
--- a/core/fxcodec/codec/ccodec_gifmodule.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_FXCODEC_CODEC_CCODEC_GIFMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_GIFMODULE_H_
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcodec/gif/cfx_gif.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-
-class CFX_DIBAttribute;
-
-class CCodec_GifModule {
- public:
-  class Context {
-   public:
-    virtual ~Context() {}
-  };
-
-  class Delegate {
-   public:
-    virtual void GifRecordCurrentPosition(uint32_t& cur_pos) = 0;
-    virtual bool GifInputRecordPositionBuf(uint32_t rcd_pos,
-                                           const FX_RECT& img_rc,
-                                           int32_t pal_num,
-                                           void* pal_ptr,
-                                           int32_t delay_time,
-                                           bool user_input,
-                                           int32_t trans_index,
-                                           int32_t disposal_method,
-                                           bool interlace) = 0;
-    virtual void GifReadScanline(int32_t row_num, uint8_t* row_buf) = 0;
-  };
-
-  CCodec_GifModule();
-  ~CCodec_GifModule();
-
-  std::unique_ptr<Context> Start(Delegate* pDelegate);
-  uint32_t GetAvailInput(Context* context, uint8_t** avail_buf_ptr = nullptr);
-  void Input(Context* context, const uint8_t* src_buf, uint32_t src_size);
-  CFX_GifDecodeStatus ReadHeader(Context* context,
-                                 int* width,
-                                 int* height,
-                                 int* pal_num,
-                                 void** pal_pp,
-                                 int* bg_index,
-                                 CFX_DIBAttribute* pAttribute);
-  std::pair<CFX_GifDecodeStatus, size_t> LoadFrameInfo(Context* context);
-  CFX_GifDecodeStatus LoadFrame(Context* context,
-                                size_t frame_num,
-                                CFX_DIBAttribute* pAttribute);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_GIFMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_iccmodule.h b/core/fxcodec/codec/ccodec_iccmodule.h
deleted file mode 100644
index c304b4b..0000000
--- a/core/fxcodec/codec/ccodec_iccmodule.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_ICCMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_ICCMODULE_H_
-
-#include <memory>
-
-#include "core/fxcodec/fx_codec_def.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/ptr_util.h"
-
-#if defined(USE_SYSTEM_LCMS2)
-#include <lcms2.h>
-#else
-#include "third_party/lcms/include/lcms2.h"
-#endif
-
-class CLcmsCmm {
- public:
-  CLcmsCmm(int srcComponents, cmsHTRANSFORM transform, bool isLab);
-  ~CLcmsCmm();
-
-  cmsHTRANSFORM m_hTransform;
-  int m_nSrcComponents;
-  bool m_bLab;
-};
-
-class CCodec_IccModule {
- public:
-  CCodec_IccModule();
-  ~CCodec_IccModule();
-
-  std::unique_ptr<CLcmsCmm> CreateTransform_sRGB(const uint8_t* pProfileData,
-                                                 uint32_t dwProfileSize,
-                                                 uint32_t* nComponents);
-  void Translate(CLcmsCmm* pTransform,
-                 const float* pSrcValues,
-                 float* pDestValues);
-  void TranslateScanline(CLcmsCmm* pTransform,
-                         uint8_t* pDest,
-                         const uint8_t* pSrc,
-                         int pixels);
-  void SetComponents(uint32_t nComponents) { m_nComponents = nComponents; }
-
- protected:
-  uint32_t m_nComponents;
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_ICCMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_jbig2module.h b/core/fxcodec/codec/ccodec_jbig2module.h
deleted file mode 100644
index 4fa4a81..0000000
--- a/core/fxcodec/codec/ccodec_jbig2module.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_JBIG2MODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_JBIG2MODULE_H_
-
-#include <memory>
-
-#include "core/fxcodec/fx_codec_def.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CJBig2_Context;
-class CJBig2_Image;
-class CPDF_StreamAcc;
-class IFX_PauseIndicator;
-class JBig2_DocumentContext;
-
-class CCodec_Jbig2Context {
- public:
-  CCodec_Jbig2Context();
-  ~CCodec_Jbig2Context();
-
-  uint32_t m_width;
-  uint32_t m_height;
-  RetainPtr<CPDF_StreamAcc> m_pGlobalStream;
-  RetainPtr<CPDF_StreamAcc> m_pSrcStream;
-  uint8_t* m_dest_buf;
-  uint32_t m_dest_pitch;
-  std::unique_ptr<CJBig2_Context> m_pContext;
-};
-
-class CCodec_Jbig2Module {
- public:
-  CCodec_Jbig2Module() {}
-  ~CCodec_Jbig2Module();
-
-  FXCODEC_STATUS StartDecode(
-      CCodec_Jbig2Context* pJbig2Context,
-      std::unique_ptr<JBig2_DocumentContext>* pContextHolder,
-      uint32_t width,
-      uint32_t height,
-      const RetainPtr<CPDF_StreamAcc>& src_stream,
-      const RetainPtr<CPDF_StreamAcc>& global_stream,
-      uint8_t* dest_buf,
-      uint32_t dest_pitch,
-      IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS ContinueDecode(CCodec_Jbig2Context* pJbig2Context,
-                                IFX_PauseIndicator* pPause);
-
- private:
-  FXCODEC_STATUS Decode(CCodec_Jbig2Context* pJbig2Context, int result);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_JBIG2MODULE_H_
diff --git a/core/fxcodec/codec/ccodec_jpegmodule.h b/core/fxcodec/codec/ccodec_jpegmodule.h
deleted file mode 100644
index aed01bd..0000000
--- a/core/fxcodec/codec/ccodec_jpegmodule.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_JPEGMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_JPEGMODULE_H_
-
-#include <csetjmp>
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CCodec_ScanlineDecoder;
-class CFX_DIBSource;
-
-#ifdef PDF_ENABLE_XFA
-class CFX_DIBAttribute;
-#endif  // PDF_ENABLE_XFA
-
-class CCodec_JpegModule {
- public:
-  class Context {
-   public:
-    virtual ~Context() {}
-    virtual jmp_buf* GetJumpMark() = 0;
-  };
-
-  std::unique_ptr<CCodec_ScanlineDecoder> CreateDecoder(const uint8_t* src_buf,
-                                                        uint32_t src_size,
-                                                        int width,
-                                                        int height,
-                                                        int nComps,
-                                                        bool ColorTransform);
-  bool LoadInfo(const uint8_t* src_buf,
-                uint32_t src_size,
-                int* width,
-                int* height,
-                int* num_components,
-                int* bits_per_components,
-                bool* color_transform);
-
-  std::unique_ptr<Context> Start();
-  void Input(Context* pContext, const uint8_t* src_buf, uint32_t src_size);
-
-#ifndef PDF_ENABLE_XFA
-  int ReadHeader(Context* pContext, int* width, int* height, int* nComps);
-#else   // PDF_ENABLE_XFA
-  int ReadHeader(Context* pContext,
-                 int* width,
-                 int* height,
-                 int* nComps,
-                 CFX_DIBAttribute* pAttribute);
-#endif  // PDF_ENABLE_XFA
-
-  bool StartScanline(Context* pContext, int down_scale);
-  bool ReadScanline(Context* pContext, uint8_t* dest_buf);
-  uint32_t GetAvailInput(Context* pContext, uint8_t** avail_buf_ptr);
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  static bool JpegEncode(const RetainPtr<CFX_DIBSource>& pSource,
-                         uint8_t** dest_buf,
-                         size_t* dest_size);
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_JPEGMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_jpxmodule.h b/core/fxcodec/codec/ccodec_jpxmodule.h
deleted file mode 100644
index c570027..0000000
--- a/core/fxcodec/codec/ccodec_jpxmodule.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_JPXMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_JPXMODULE_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-
-class CJPX_Decoder;
-class CPDF_ColorSpace;
-
-class CCodec_JpxModule {
- public:
-  CCodec_JpxModule();
-  ~CCodec_JpxModule();
-
-  std::unique_ptr<CJPX_Decoder> CreateDecoder(const uint8_t* src_buf,
-                                              uint32_t src_size,
-                                              CPDF_ColorSpace* cs);
-  void GetImageInfo(CJPX_Decoder* pDecoder,
-                    uint32_t* width,
-                    uint32_t* height,
-                    uint32_t* components);
-  bool Decode(CJPX_Decoder* pDecoder,
-              uint8_t* dest_data,
-              int pitch,
-              const std::vector<uint8_t>& offsets);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_JPXMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_pngmodule.cpp b/core/fxcodec/codec/ccodec_pngmodule.cpp
deleted file mode 100644
index 37e83f7..0000000
--- a/core/fxcodec/codec/ccodec_pngmodule.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/ccodec_pngmodule.h"
-
-#include <algorithm>
-
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef USE_SYSTEM_LIBPNG
-#include <png.h>
-#else
-#include "third_party/libpng16/png.h"
-#endif
-
-#define PNG_ERROR_SIZE 256
-
-class CPngContext : public CCodec_PngModule::Context {
- public:
-  CPngContext(CCodec_PngModule* pModule, CCodec_PngModule::Delegate* pDelegate);
-  ~CPngContext() override;
-
-  png_structp m_pPng;
-  png_infop m_pInfo;
-  UnownedPtr<CCodec_PngModule> m_pModule;
-  UnownedPtr<CCodec_PngModule::Delegate> m_pDelegate;
-  void* (*m_AllocFunc)(unsigned int);
-  void (*m_FreeFunc)(void*);
-  char m_szLastError[PNG_ERROR_SIZE];
-};
-
-extern "C" {
-
-static void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {
-  if (png_get_error_ptr(png_ptr))
-    strncpy((char*)png_get_error_ptr(png_ptr), error_msg, PNG_ERROR_SIZE - 1);
-
-  longjmp(png_jmpbuf(png_ptr), 1);
-}
-
-static void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {}
-
-static void _png_load_bmp_attribute(png_structp png_ptr,
-                                    png_infop info_ptr,
-                                    CFX_DIBAttribute* pAttribute) {
-  if (pAttribute) {
-#if defined(PNG_pHYs_SUPPORTED)
-    pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr);
-    pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr);
-    png_uint_32 res_x, res_y;
-    int unit_type;
-    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
-    switch (unit_type) {
-      case PNG_RESOLUTION_METER:
-        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;
-        break;
-      default:
-        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE;
-    }
-#endif
-#if defined(PNG_iCCP_SUPPORTED)
-    png_charp icc_name;
-    png_bytep icc_profile;
-    png_uint_32 icc_proflen;
-    int compress_type;
-    png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile,
-                 &icc_proflen);
-#endif
-#if defined(PNG_TEXT_SUPPORTED)
-    int i;
-    size_t len;
-    const char* buf;
-    int num_text;
-    png_textp text = nullptr;
-    png_get_text(png_ptr, info_ptr, &text, &num_text);
-    for (i = 0; i < num_text; i++) {
-      len = strlen(text[i].key);
-      buf = "Time";
-      if (memcmp(buf, text[i].key, std::min(len, strlen(buf)))) {
-        buf = "Author";
-        if (!memcmp(buf, text[i].key, std::min(len, strlen(buf)))) {
-          pAttribute->m_strAuthor =
-              text[i].text_length > 0
-                  ? ByteString(reinterpret_cast<uint8_t*>(text[i].text),
-                               static_cast<size_t>(text[i].text_length))
-                  : ByteString();
-        }
-      }
-    }
-#endif
-  }
-}
-
-static void* _png_alloc_func(unsigned int size) {
-  return FX_Alloc(char, size);
-}
-
-static void _png_free_func(void* p) {
-  FX_Free(p);
-}
-
-static void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) {
-  auto* pContext =
-      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
-  if (!pContext)
-    return;
-
-  png_uint_32 width = 0;
-  png_uint_32 height = 0;
-  int bpc = 0;
-  int color_type = 0;
-  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr,
-               nullptr, nullptr);
-  int color_type1 = color_type;
-  if (bpc > 8)
-    png_set_strip_16(png_ptr);
-  else if (bpc < 8)
-    png_set_expand_gray_1_2_4_to_8(png_ptr);
-
-  bpc = 8;
-  if (color_type == PNG_COLOR_TYPE_PALETTE)
-    png_set_palette_to_rgb(png_ptr);
-
-  int pass = png_set_interlace_handling(png_ptr);
-  double gamma = 1.0;
-  if (!pContext->m_pDelegate->PngReadHeader(width, height, bpc, pass,
-                                            &color_type, &gamma)) {
-    png_error(pContext->m_pPng, "Read Header Callback Error");
-  }
-  int intent;
-  if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
-    png_set_gamma(png_ptr, gamma, 0.45455);
-  } else {
-    double image_gamma;
-    if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
-      png_set_gamma(png_ptr, gamma, image_gamma);
-    else
-      png_set_gamma(png_ptr, gamma, 0.45455);
-  }
-  switch (color_type) {
-    case PNG_COLOR_TYPE_GRAY:
-    case PNG_COLOR_TYPE_GRAY_ALPHA: {
-      if (color_type1 & PNG_COLOR_MASK_COLOR) {
-        png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);
-      }
-    } break;
-    case PNG_COLOR_TYPE_PALETTE:
-      if (color_type1 != PNG_COLOR_TYPE_PALETTE) {
-        png_error(pContext->m_pPng, "Not Support Output Palette Now");
-      }
-    case PNG_COLOR_TYPE_RGB:
-    case PNG_COLOR_TYPE_RGB_ALPHA:
-      if (!(color_type1 & PNG_COLOR_MASK_COLOR)) {
-        png_set_gray_to_rgb(png_ptr);
-      }
-      png_set_bgr(png_ptr);
-      break;
-  }
-  if (!(color_type & PNG_COLOR_MASK_ALPHA))
-    png_set_strip_alpha(png_ptr);
-
-  if (color_type & PNG_COLOR_MASK_ALPHA &&
-      !(color_type1 & PNG_COLOR_MASK_ALPHA)) {
-    png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
-  }
-  png_read_update_info(png_ptr, info_ptr);
-}
-
-static void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
-
-static void _png_get_row_func(png_structp png_ptr,
-                              png_bytep new_row,
-                              png_uint_32 row_num,
-                              int pass) {
-  auto* pContext =
-      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
-  if (!pContext)
-    return;
-
-  uint8_t* src_buf;
-  if (!pContext->m_pDelegate->PngAskScanlineBuf(row_num, &src_buf))
-    png_error(png_ptr, "Ask Scanline buffer Callback Error");
-
-  if (src_buf)
-    png_progressive_combine_row(png_ptr, src_buf, new_row);
-
-  pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num);
-}
-
-}  // extern "C"
-
-CPngContext::CPngContext(CCodec_PngModule* pModule,
-                         CCodec_PngModule::Delegate* pDelegate)
-    : m_pPng(nullptr),
-      m_pInfo(nullptr),
-      m_pModule(pModule),
-      m_pDelegate(pDelegate),
-      m_AllocFunc(_png_alloc_func),
-      m_FreeFunc(_png_free_func) {
-  memset(m_szLastError, 0, sizeof(m_szLastError));
-}
-
-CPngContext::~CPngContext() {
-  png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
-                          m_pInfo ? &m_pInfo : nullptr, nullptr);
-}
-
-std::unique_ptr<CCodec_PngModule::Context> CCodec_PngModule::Start(
-    Delegate* pDelegate) {
-  auto p = pdfium::MakeUnique<CPngContext>(this, pDelegate);
-  p->m_pPng =
-      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
-  if (!p->m_pPng)
-    return nullptr;
-
-  p->m_pInfo = png_create_info_struct(p->m_pPng);
-  if (!p->m_pInfo)
-    return nullptr;
-
-  if (setjmp(png_jmpbuf(p->m_pPng)))
-    return nullptr;
-
-  png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func,
-                              _png_get_row_func, _png_get_end_func);
-  png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data,
-                   _png_warning_data);
-  return p;
-}
-
-bool CCodec_PngModule::Input(Context* pContext,
-                             const uint8_t* src_buf,
-                             uint32_t src_size,
-                             CFX_DIBAttribute* pAttribute) {
-  auto* ctx = static_cast<CPngContext*>(pContext);
-  if (setjmp(png_jmpbuf(ctx->m_pPng))) {
-    if (pAttribute &&
-        strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) {
-      _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute);
-    }
-    return false;
-  }
-  png_process_data(ctx->m_pPng, ctx->m_pInfo, (uint8_t*)src_buf, src_size);
-  return true;
-}
diff --git a/core/fxcodec/codec/ccodec_pngmodule.h b/core/fxcodec/codec/ccodec_pngmodule.h
deleted file mode 100644
index 847f67e..0000000
--- a/core/fxcodec/codec/ccodec_pngmodule.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_PNGMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_PNGMODULE_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-
-class CFX_DIBAttribute;
-
-class CCodec_PngModule {
- public:
-  class Context {
-   public:
-    virtual ~Context() {}
-  };
-
-  class Delegate {
-   public:
-    virtual bool PngReadHeader(int width,
-                               int height,
-                               int bpc,
-                               int pass,
-                               int* color_type,
-                               double* gamma) = 0;
-
-    // Returns true on success. |pSrcBuf| will be set if this succeeds.
-    // |pSrcBuf| does not take ownership of the buffer.
-    virtual bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) = 0;
-
-    virtual void PngFillScanlineBufCompleted(int pass, int line) = 0;
-  };
-
-  std::unique_ptr<Context> Start(Delegate* pDelegate);
-  bool Input(Context* pContext,
-             const uint8_t* src_buf,
-             uint32_t src_size,
-             CFX_DIBAttribute* pAttribute);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_PNGMODULE_H_
diff --git a/core/fxcodec/codec/ccodec_progressivedecoder.h b/core/fxcodec/codec/ccodec_progressivedecoder.h
deleted file mode 100644
index 41e10e6..0000000
--- a/core/fxcodec/codec/ccodec_progressivedecoder.h
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_PROGRESSIVEDECODER_H_
-#define CORE_FXCODEC_CODEC_CCODEC_PROGRESSIVEDECODER_H_
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcodec/codec/ccodec_bmpmodule.h"
-#include "core/fxcodec/codec/ccodec_gifmodule.h"
-#include "core/fxcodec/codec/ccodec_jpegmodule.h"
-#include "core/fxcodec/codec/ccodec_pngmodule.h"
-#include "core/fxcodec/codec/ccodec_tiffmodule.h"
-#include "core/fxcodec/fx_codec_def.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"
-
-class CCodec_ModuleMgr;
-class CFX_DIBAttribute;
-class IFX_SeekableReadStream;
-
-class CCodec_ProgressiveDecoder : public CCodec_BmpModule::Delegate,
-                                  public CCodec_GifModule::Delegate,
-                                  public CCodec_PngModule::Delegate {
- public:
-  enum FXCodec_Format {
-    FXCodec_Invalid = 0,
-    FXCodec_1bppGray = 0x101,
-    FXCodec_1bppRgb = 0x001,
-    FXCodec_8bppGray = 0x108,
-    FXCodec_8bppRgb = 0x008,
-    FXCodec_Rgb = 0x018,
-    FXCodec_Rgb32 = 0x020,
-    FXCodec_Argb = 0x220,
-    FXCodec_Cmyk = 0x120
-  };
-
-  explicit CCodec_ProgressiveDecoder(CCodec_ModuleMgr* pCodecMgr);
-  virtual ~CCodec_ProgressiveDecoder();
-
-  FXCODEC_STATUS LoadImageInfo(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                               FXCODEC_IMAGE_TYPE imageType,
-                               CFX_DIBAttribute* pAttribute,
-                               bool bSkipImageTypeCheck);
-
-  FXCODEC_IMAGE_TYPE GetType() const { return m_imagType; }
-  int32_t GetWidth() const { return m_SrcWidth; }
-  int32_t GetHeight() const { return m_SrcHeight; }
-  int32_t GetNumComponents() const { return m_SrcComponents; }
-  int32_t GetBPC() const { return m_SrcBPC; }
-  void SetClipBox(FX_RECT* clip);
-
-  std::pair<FXCODEC_STATUS, size_t> GetFrames();
-  FXCODEC_STATUS StartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                             int start_x,
-                             int start_y,
-                             int size_x,
-                             int size_y);
-
-  FXCODEC_STATUS ContinueDecode();
-
-  struct PixelWeight {
-    int m_SrcStart;
-    int m_SrcEnd;
-    int m_Weights[1];
-  };
-
-  class CFXCODEC_WeightTable {
-   public:
-    CFXCODEC_WeightTable();
-    ~CFXCODEC_WeightTable();
-
-    void Calc(int dest_len,
-              int dest_min,
-              int dest_max,
-              int src_len,
-              int src_min,
-              int src_max);
-    PixelWeight* GetPixelWeight(int pixel) {
-      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
-                                            (pixel - m_DestMin) * m_ItemSize);
-    }
-
-    int m_DestMin;
-    int m_ItemSize;
-    std::vector<uint8_t> m_pWeightTables;
-  };
-
-  class CFXCODEC_HorzTable {
-   public:
-    CFXCODEC_HorzTable();
-    ~CFXCODEC_HorzTable();
-
-    void Calc(int dest_len, int src_len);
-    PixelWeight* GetPixelWeight(int pixel) {
-      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
-                                            pixel * m_ItemSize);
-    }
-
-    int m_ItemSize;
-    std::vector<uint8_t> m_pWeightTables;
-  };
-
-  class CFXCODEC_VertTable {
-   public:
-    CFXCODEC_VertTable();
-    ~CFXCODEC_VertTable();
-
-    void Calc(int dest_len, int src_len);
-    PixelWeight* GetPixelWeight(int pixel) {
-      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
-                                            pixel * m_ItemSize);
-    }
-    int m_ItemSize;
-    std::vector<uint8_t> m_pWeightTables;
-  };
-
-  // CCodec_PngModule::Delegate
-  bool PngReadHeader(int width,
-                     int height,
-                     int bpc,
-                     int pass,
-                     int* color_type,
-                     double* gamma) override;
-  bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) override;
-  void PngFillScanlineBufCompleted(int pass, int line) override;
-
-  // CCodec_GifModule::Delegate
-  void GifRecordCurrentPosition(uint32_t& cur_pos) override;
-  bool GifInputRecordPositionBuf(uint32_t rcd_pos,
-                                 const FX_RECT& img_rc,
-                                 int32_t pal_num,
-                                 void* pal_ptr,
-                                 int32_t delay_time,
-                                 bool user_input,
-                                 int32_t trans_index,
-                                 int32_t disposal_method,
-                                 bool interlace) override;
-  void GifReadScanline(int32_t row_num, uint8_t* row_buf) override;
-
-  // CCodec_BmpModule::Delegate
-  bool BmpInputImagePositionBuf(uint32_t rcd_pos) override;
-  void BmpReadScanline(uint32_t row_num,
-                       const std::vector<uint8_t>& row_buf) override;
-
- private:
-  bool BmpReadMoreData(CCodec_BmpModule* pBmpModule,
-                       FXCODEC_STATUS& err_status);
-  bool GifReadMoreData(CCodec_GifModule* pGifModule,
-                       FXCODEC_STATUS& err_status);
-  bool JpegReadMoreData(CCodec_JpegModule* pJpegModule,
-                        FXCODEC_STATUS& err_status);
-  void PngOneOneMapResampleHorz(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                                int32_t des_line,
-                                uint8_t* src_scan,
-                                FXCodec_Format src_format);
-  bool DetectImageType(FXCODEC_IMAGE_TYPE imageType,
-                       CFX_DIBAttribute* pAttribute);
-  bool BmpDetectImageType(CFX_DIBAttribute* pAttribute, uint32_t size);
-  bool JpegDetectImageType(CFX_DIBAttribute* pAttribute, uint32_t size);
-  bool PngDetectImageType(CFX_DIBAttribute* pAttribute, uint32_t size);
-  bool GifDetectImageType(CFX_DIBAttribute* pAttribute, uint32_t size);
-  bool TifDetectImageType(CFX_DIBAttribute* pAttribute, uint32_t size);
-
-  void GetDownScale(int& down_scale);
-  void GetTransMethod(FXDIB_Format des_format, FXCodec_Format src_format);
-
-  void ReSampleScanline(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                        int32_t des_line,
-                        uint8_t* src_scan,
-                        FXCodec_Format src_format);
-  void Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                int32_t src_line,
-                uint8_t* src_scan,
-                FXCodec_Format src_format);
-  void ResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                    double scale_y,
-                    int des_row);
-  void ResampleVertBT(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                      double scale_y,
-                      int des_row);
-  void GifDoubleLineResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-                                 double scale_y,
-                                 int des_row);
-
-  FXCODEC_STATUS JpegStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  FXCODEC_STATUS PngStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  FXCODEC_STATUS GifStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-  FXCODEC_STATUS BmpStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-
-  FXCODEC_STATUS JpegContinueDecode();
-  FXCODEC_STATUS PngContinueDecode();
-  FXCODEC_STATUS GifContinueDecode();
-  FXCODEC_STATUS BmpContinueDecode();
-  FXCODEC_STATUS TifContinueDecode();
-
-  RetainPtr<IFX_SeekableReadStream> m_pFile;
-  RetainPtr<CFX_DIBitmap> m_pDeviceBitmap;
-  UnownedPtr<CCodec_ModuleMgr> m_pCodecMgr;
-  std::unique_ptr<CCodec_JpegModule::Context> m_pJpegContext;
-  std::unique_ptr<CCodec_PngModule::Context> m_pPngContext;
-  std::unique_ptr<CCodec_GifModule::Context> m_pGifContext;
-  std::unique_ptr<CCodec_BmpModule::Context> m_pBmpContext;
-  std::unique_ptr<CCodec_TiffModule::Context> m_pTiffContext;
-  FXCODEC_IMAGE_TYPE m_imagType;
-  uint32_t m_offSet;
-  uint8_t* m_pSrcBuf;
-  uint32_t m_SrcSize;
-  uint8_t* m_pDecodeBuf;
-  int m_ScanlineSize;
-  CFXCODEC_WeightTable m_WeightHorz;
-  CFXCODEC_VertTable m_WeightVert;
-  CFXCODEC_HorzTable m_WeightHorzOO;
-  int m_SrcWidth;
-  int m_SrcHeight;
-  int m_SrcComponents;
-  int m_SrcBPC;
-  FX_RECT m_clipBox;
-  int m_startX;
-  int m_startY;
-  int m_sizeX;
-  int m_sizeY;
-  int m_TransMethod;
-  FX_ARGB* m_pSrcPalette;
-  int m_SrcPaletteNumber;
-  int m_SrcRow;
-  FXCodec_Format m_SrcFormat;
-  int m_SrcPassNumber;
-  size_t m_FrameNumber;
-  size_t m_FrameCur;
-  int m_GifBgIndex;
-  uint8_t* m_pGifPalette;
-  int32_t m_GifPltNumber;
-  int m_GifTransIndex;
-  FX_RECT m_GifFrameRect;
-  bool m_BmpIsTopBottom;
-  FXCODEC_STATUS m_status;
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_PROGRESSIVEDECODER_H_
diff --git a/core/fxcodec/codec/ccodec_scanlinedecoder.cpp b/core/fxcodec/codec/ccodec_scanlinedecoder.cpp
deleted file mode 100644
index 55b9a2a..0000000
--- a/core/fxcodec/codec/ccodec_scanlinedecoder.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
-
-#include "core/fxcrt/ifx_pauseindicator.h"
-
-CCodec_ScanlineDecoder::CCodec_ScanlineDecoder()
-    : CCodec_ScanlineDecoder(0, 0, 0, 0, 0, 0, 0) {}
-
-CCodec_ScanlineDecoder::CCodec_ScanlineDecoder(int nOrigWidth,
-                                               int nOrigHeight,
-                                               int nOutputWidth,
-                                               int nOutputHeight,
-                                               int nComps,
-                                               int nBpc,
-                                               uint32_t nPitch)
-    : m_OrigWidth(nOrigWidth),
-      m_OrigHeight(nOrigHeight),
-      m_OutputWidth(nOutputWidth),
-      m_OutputHeight(nOutputHeight),
-      m_nComps(nComps),
-      m_bpc(nBpc),
-      m_Pitch(nPitch),
-      m_NextLine(-1),
-      m_pLastScanline(nullptr) {}
-
-CCodec_ScanlineDecoder::~CCodec_ScanlineDecoder() {}
-
-const uint8_t* CCodec_ScanlineDecoder::GetScanline(int line) {
-  if (m_NextLine == line + 1)
-    return m_pLastScanline;
-
-  if (m_NextLine < 0 || m_NextLine > line) {
-    if (!v_Rewind())
-      return nullptr;
-    m_NextLine = 0;
-  }
-  while (m_NextLine < line) {
-    ReadNextLine();
-    m_NextLine++;
-  }
-  m_pLastScanline = ReadNextLine();
-  m_NextLine++;
-  return m_pLastScanline;
-}
-
-bool CCodec_ScanlineDecoder::SkipToScanline(int line,
-                                            IFX_PauseIndicator* pPause) {
-  if (m_NextLine == line || m_NextLine == line + 1)
-    return false;
-
-  if (m_NextLine < 0 || m_NextLine > line) {
-    v_Rewind();
-    m_NextLine = 0;
-  }
-  m_pLastScanline = nullptr;
-  while (m_NextLine < line) {
-    m_pLastScanline = ReadNextLine();
-    m_NextLine++;
-    if (pPause && pPause->NeedToPauseNow()) {
-      return true;
-    }
-  }
-  return false;
-}
-
-uint8_t* CCodec_ScanlineDecoder::ReadNextLine() {
-  return v_GetNextLine();
-}
diff --git a/core/fxcodec/codec/ccodec_scanlinedecoder.h b/core/fxcodec/codec/ccodec_scanlinedecoder.h
deleted file mode 100644
index 05d5eab..0000000
--- a/core/fxcodec/codec/ccodec_scanlinedecoder.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_SCANLINEDECODER_H_
-#define CORE_FXCODEC_CODEC_CCODEC_SCANLINEDECODER_H_
-
-#include "core/fxcrt/fx_system.h"
-
-class IFX_PauseIndicator;
-
-class CCodec_ScanlineDecoder {
- public:
-  CCodec_ScanlineDecoder();
-  CCodec_ScanlineDecoder(int nOrigWidth,
-                         int nOrigHeight,
-                         int nOutputWidth,
-                         int nOutputHeight,
-                         int nComps,
-                         int nBpc,
-                         uint32_t nPitch);
-  virtual ~CCodec_ScanlineDecoder();
-
-  const uint8_t* GetScanline(int line);
-  bool SkipToScanline(int line, IFX_PauseIndicator* pPause);
-
-  int GetWidth() { return m_OutputWidth; }
-  int GetHeight() { return m_OutputHeight; }
-  int CountComps() { return m_nComps; }
-  int GetBPC() { return m_bpc; }
-
-  virtual uint32_t GetSrcOffset() = 0;
-
- protected:
-  virtual bool v_Rewind() = 0;
-  virtual uint8_t* v_GetNextLine() = 0;
-
-  uint8_t* ReadNextLine();
-
-  int m_OrigWidth;
-  int m_OrigHeight;
-  int m_OutputWidth;
-  int m_OutputHeight;
-  int m_nComps;
-  int m_bpc;
-  uint32_t m_Pitch;
-  int m_NextLine;
-  uint8_t* m_pLastScanline;
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_SCANLINEDECODER_H_
diff --git a/core/fxcodec/codec/ccodec_tiffmodule.cpp b/core/fxcodec/codec/ccodec_tiffmodule.cpp
deleted file mode 100644
index 0bf0045..0000000
--- a/core/fxcodec/codec/ccodec_tiffmodule.cpp
+++ /dev/null
@@ -1,506 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/ccodec_tiffmodule.h"
-
-#include <limits>
-
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-
-extern "C" {
-#include "third_party/libtiff/tiffiop.h"
-}
-
-class CTiffContext : public CCodec_TiffModule::Context {
- public:
-  CTiffContext();
-  ~CTiffContext() override;
-
-  bool InitDecoder(const RetainPtr<IFX_SeekableReadStream>& file_ptr);
-  bool LoadFrameInfo(int32_t frame,
-                     int32_t* width,
-                     int32_t* height,
-                     int32_t* comps,
-                     int32_t* bpc,
-                     CFX_DIBAttribute* pAttribute);
-  bool Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-
-  RetainPtr<IFX_SeekableReadStream> io_in() const { return m_io_in; }
-  uint32_t offset() const { return m_offset; }
-  void set_offset(uint32_t offset) { m_offset = offset; }
-
- private:
-  bool IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const;
-  void SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap, uint16_t bps);
-  bool Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                     int32_t height,
-                     int32_t width,
-                     uint16_t bps,
-                     uint16_t spp);
-  bool Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                     int32_t height,
-                     int32_t width,
-                     uint16_t bps,
-                     uint16_t spp);
-  bool Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                      int32_t height,
-                      int32_t width,
-                      uint16_t bps,
-                      uint16_t spp);
-
-  RetainPtr<IFX_SeekableReadStream> m_io_in;
-  uint32_t m_offset;
-  TIFF* m_tif_ctx;
-};
-
-void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz) {
-  return FXMEM_DefaultCalloc(nmemb, siz);
-}
-
-void* _TIFFmalloc(tmsize_t size) {
-  return FXMEM_DefaultAlloc(size);
-}
-
-void _TIFFfree(void* ptr) {
-  if (ptr)
-    FXMEM_DefaultFree(ptr);
-}
-
-void* _TIFFrealloc(void* ptr, tmsize_t size) {
-  return FXMEM_DefaultRealloc(ptr, size);
-}
-
-void _TIFFmemset(void* ptr, int val, tmsize_t size) {
-  memset(ptr, val, static_cast<size_t>(size));
-}
-
-void _TIFFmemcpy(void* des, const void* src, tmsize_t size) {
-  memcpy(des, src, static_cast<size_t>(size));
-}
-
-int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) {
-  return memcmp(ptr1, ptr2, static_cast<size_t>(size));
-}
-
-int _TIFFIfMultiplicationOverflow(tmsize_t op1, tmsize_t op2) {
-  return op1 > std::numeric_limits<tmsize_t>::max() / op2;
-}
-
-TIFFErrorHandler _TIFFwarningHandler = nullptr;
-TIFFErrorHandler _TIFFerrorHandler = nullptr;
-
-namespace {
-
-tsize_t tiff_read(thandle_t context, tdata_t buf, tsize_t length) {
-  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
-  FX_SAFE_UINT32 increment = pTiffContext->offset();
-  increment += length;
-  if (!increment.IsValid())
-    return 0;
-
-  FX_FILESIZE offset = pTiffContext->offset();
-  if (!pTiffContext->io_in()->ReadBlock(buf, offset, length))
-    return 0;
-
-  pTiffContext->set_offset(increment.ValueOrDie());
-  if (offset + length > pTiffContext->io_in()->GetSize())
-    return pTiffContext->io_in()->GetSize() - offset;
-
-  return length;
-}
-
-tsize_t tiff_write(thandle_t context, tdata_t buf, tsize_t length) {
-  NOTREACHED();
-  return 0;
-}
-
-toff_t tiff_seek(thandle_t context, toff_t offset, int whence) {
-  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
-  FX_SAFE_FILESIZE safe_offset = offset;
-  if (!safe_offset.IsValid())
-    return static_cast<toff_t>(-1);
-  FX_FILESIZE file_offset = safe_offset.ValueOrDie();
-
-  switch (whence) {
-    case 0: {
-      if (file_offset > pTiffContext->io_in()->GetSize())
-        return static_cast<toff_t>(-1);
-      pTiffContext->set_offset(file_offset);
-      return pTiffContext->offset();
-    }
-    case 1: {
-      FX_SAFE_UINT32 new_increment = pTiffContext->offset();
-      new_increment += file_offset;
-      if (!new_increment.IsValid())
-        return static_cast<toff_t>(-1);
-      pTiffContext->set_offset(new_increment.ValueOrDie());
-      return pTiffContext->offset();
-    }
-    case 2: {
-      if (pTiffContext->io_in()->GetSize() < file_offset)
-        return static_cast<toff_t>(-1);
-      pTiffContext->set_offset(pTiffContext->io_in()->GetSize() - file_offset);
-      return pTiffContext->offset();
-    }
-    default:
-      return static_cast<toff_t>(-1);
-  }
-}
-
-int tiff_close(thandle_t context) {
-  return 0;
-}
-
-toff_t tiff_get_size(thandle_t context) {
-  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
-  return static_cast<toff_t>(pTiffContext->io_in()->GetSize());
-}
-
-int tiff_map(thandle_t context, tdata_t*, toff_t*) {
-  return 0;
-}
-
-void tiff_unmap(thandle_t context, tdata_t, toff_t) {}
-
-TIFF* tiff_open(void* context, const char* mode) {
-  TIFF* tif = TIFFClientOpen("Tiff Image", mode, (thandle_t)context, tiff_read,
-                             tiff_write, tiff_seek, tiff_close, tiff_get_size,
-                             tiff_map, tiff_unmap);
-  if (tif) {
-    tif->tif_fd = (int)(intptr_t)context;
-  }
-  return tif;
-}
-
-template <class T>
-bool Tiff_Exif_GetInfo(TIFF* tif_ctx, ttag_t tag, CFX_DIBAttribute* pAttr) {
-  T val = 0;
-  TIFFGetField(tif_ctx, tag, &val);
-  if (!val)
-    return false;
-  T* ptr = FX_Alloc(T, 1);
-  *ptr = val;
-  pAttr->m_Exif[tag] = ptr;
-  return true;
-}
-
-void Tiff_Exif_GetStringInfo(TIFF* tif_ctx,
-                             ttag_t tag,
-                             CFX_DIBAttribute* pAttr) {
-  char* buf = nullptr;
-  TIFFGetField(tif_ctx, tag, &buf);
-  if (!buf)
-    return;
-  size_t size = strlen(buf);
-  uint8_t* ptr = FX_Alloc(uint8_t, size + 1);
-  memcpy(ptr, buf, size);
-  ptr[size] = 0;
-  pAttr->m_Exif[tag] = ptr;
-}
-
-void TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) {
-  for (int32_t n = 0; n < pixel; n++) {
-    uint8_t tmp = pBuf[0];
-    pBuf[0] = pBuf[2];
-    pBuf[2] = tmp;
-    pBuf += spp;
-  }
-}
-
-}  // namespace
-
-CTiffContext::CTiffContext()
-    : m_io_in(nullptr), m_offset(0), m_tif_ctx(nullptr) {}
-
-CTiffContext::~CTiffContext() {
-  if (m_tif_ctx)
-    TIFFClose(m_tif_ctx);
-}
-
-bool CTiffContext::InitDecoder(
-    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
-  m_io_in = file_ptr;
-  m_tif_ctx = tiff_open(this, "r");
-  return !!m_tif_ctx;
-}
-
-bool CTiffContext::LoadFrameInfo(int32_t frame,
-                                 int32_t* width,
-                                 int32_t* height,
-                                 int32_t* comps,
-                                 int32_t* bpc,
-                                 CFX_DIBAttribute* pAttribute) {
-  if (!TIFFSetDirectory(m_tif_ctx, (uint16)frame))
-    return false;
-
-  uint32_t tif_width = 0;
-  uint32_t tif_height = 0;
-  uint16_t tif_comps = 0;
-  uint16_t tif_bpc = 0;
-  uint32_t tif_rps = 0;
-  TIFFGetField(m_tif_ctx, TIFFTAG_IMAGEWIDTH, &tif_width);
-  TIFFGetField(m_tif_ctx, TIFFTAG_IMAGELENGTH, &tif_height);
-  TIFFGetField(m_tif_ctx, TIFFTAG_SAMPLESPERPIXEL, &tif_comps);
-  TIFFGetField(m_tif_ctx, TIFFTAG_BITSPERSAMPLE, &tif_bpc);
-  TIFFGetField(m_tif_ctx, TIFFTAG_ROWSPERSTRIP, &tif_rps);
-
-  if (pAttribute) {
-    pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_INCH;
-    if (TIFFGetField(m_tif_ctx, TIFFTAG_RESOLUTIONUNIT,
-                     &pAttribute->m_wDPIUnit)) {
-      pAttribute->m_wDPIUnit--;
-    }
-    Tiff_Exif_GetInfo<uint16_t>(m_tif_ctx, TIFFTAG_ORIENTATION, pAttribute);
-    if (Tiff_Exif_GetInfo<float>(m_tif_ctx, TIFFTAG_XRESOLUTION, pAttribute)) {
-      void* val = pAttribute->m_Exif[TIFFTAG_XRESOLUTION];
-      float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
-      pAttribute->m_nXDPI = (int32_t)(fDpi + 0.5f);
-    }
-    if (Tiff_Exif_GetInfo<float>(m_tif_ctx, TIFFTAG_YRESOLUTION, pAttribute)) {
-      void* val = pAttribute->m_Exif[TIFFTAG_YRESOLUTION];
-      float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
-      pAttribute->m_nYDPI = (int32_t)(fDpi + 0.5f);
-    }
-    Tiff_Exif_GetStringInfo(m_tif_ctx, TIFFTAG_IMAGEDESCRIPTION, pAttribute);
-    Tiff_Exif_GetStringInfo(m_tif_ctx, TIFFTAG_MAKE, pAttribute);
-    Tiff_Exif_GetStringInfo(m_tif_ctx, TIFFTAG_MODEL, pAttribute);
-  }
-  pdfium::base::CheckedNumeric<int32_t> checked_width = tif_width;
-  pdfium::base::CheckedNumeric<int32_t> checked_height = tif_height;
-  if (!checked_width.IsValid() || !checked_height.IsValid())
-    return false;
-
-  *width = checked_width.ValueOrDie();
-  *height = checked_height.ValueOrDie();
-  *comps = tif_comps;
-  *bpc = tif_bpc;
-  if (tif_rps > tif_height) {
-    tif_rps = tif_height;
-    TIFFSetField(m_tif_ctx, TIFFTAG_ROWSPERSTRIP, tif_rps);
-  }
-  return true;
-}
-
-bool CTiffContext::IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const {
-  if (TIFFIsTiled(m_tif_ctx))
-    return false;
-
-  uint16_t photometric = 0;
-  if (!TIFFGetField(m_tif_ctx, TIFFTAG_PHOTOMETRIC, &photometric))
-    return false;
-
-  switch (pDIBitmap->GetBPP()) {
-    case 1:
-    case 8:
-      if (photometric != PHOTOMETRIC_PALETTE) {
-        return false;
-      }
-      break;
-    case 24:
-      if (photometric != PHOTOMETRIC_RGB) {
-        return false;
-      }
-      break;
-    default:
-      return false;
-  }
-  uint16_t planarconfig = 0;
-  if (!TIFFGetFieldDefaulted(m_tif_ctx, TIFFTAG_PLANARCONFIG, &planarconfig))
-    return false;
-
-  return planarconfig != PLANARCONFIG_SEPARATE;
-}
-
-void CTiffContext::SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                              uint16_t bps) {
-  uint16_t* red_orig = nullptr;
-  uint16_t* green_orig = nullptr;
-  uint16_t* blue_orig = nullptr;
-  TIFFGetField(m_tif_ctx, TIFFTAG_COLORMAP, &red_orig, &green_orig, &blue_orig);
-  for (int32_t i = (1L << bps) - 1; i >= 0; i--) {
-#define CVT(x) ((uint16_t)((x) >> 8))
-    red_orig[i] = CVT(red_orig[i]);
-    green_orig[i] = CVT(green_orig[i]);
-    blue_orig[i] = CVT(blue_orig[i]);
-#undef CVT
-  }
-  int32_t len = 1 << bps;
-  for (int32_t index = 0; index < len; index++) {
-    uint32_t r = red_orig[index] & 0xFF;
-    uint32_t g = green_orig[index] & 0xFF;
-    uint32_t b = blue_orig[index] & 0xFF;
-    uint32_t color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) |
-                     (((uint32)0xffL) << 24);
-    pDIBitmap->SetPaletteArgb(index, color);
-  }
-}
-
-bool CTiffContext::Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                 int32_t height,
-                                 int32_t width,
-                                 uint16_t bps,
-                                 uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 1 || spp != 1 || bps != 1 ||
-      !IsSupport(pDIBitmap)) {
-    return false;
-  }
-  SetPalette(pDIBitmap, bps);
-  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx);
-  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx), "No space for scanline buffer");
-    return false;
-  }
-  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
-  uint32_t pitch = pDIBitmap->GetPitch();
-  for (int32_t row = 0; row < height; row++) {
-    TIFFReadScanline(m_tif_ctx, buf, row, 0);
-    for (int32_t j = 0; j < size; j++) {
-      bitMapbuffer[row * pitch + j] = buf[j];
-    }
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                 int32_t height,
-                                 int32_t width,
-                                 uint16_t bps,
-                                 uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 8 || spp != 1 || (bps != 4 && bps != 8) ||
-      !IsSupport(pDIBitmap)) {
-    return false;
-  }
-  SetPalette(pDIBitmap, bps);
-  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx);
-  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx), "No space for scanline buffer");
-    return false;
-  }
-  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
-  uint32_t pitch = pDIBitmap->GetPitch();
-  for (int32_t row = 0; row < height; row++) {
-    TIFFReadScanline(m_tif_ctx, buf, row, 0);
-    for (int32_t j = 0; j < size; j++) {
-      switch (bps) {
-        case 4:
-          bitMapbuffer[row * pitch + 2 * j + 0] = (buf[j] & 0xF0) >> 4;
-          bitMapbuffer[row * pitch + 2 * j + 1] = (buf[j] & 0x0F) >> 0;
-          break;
-        case 8:
-          bitMapbuffer[row * pitch + j] = buf[j];
-          break;
-      }
-    }
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                  int32_t height,
-                                  int32_t width,
-                                  uint16_t bps,
-                                  uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 24 || !IsSupport(pDIBitmap))
-    return false;
-
-  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx);
-  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx), "No space for scanline buffer");
-    return false;
-  }
-  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
-  uint32_t pitch = pDIBitmap->GetPitch();
-  for (int32_t row = 0; row < height; row++) {
-    TIFFReadScanline(m_tif_ctx, buf, row, 0);
-    for (int32_t j = 0; j < size - 2; j += 3) {
-      bitMapbuffer[row * pitch + j + 0] = buf[j + 2];
-      bitMapbuffer[row * pitch + j + 1] = buf[j + 1];
-      bitMapbuffer[row * pitch + j + 2] = buf[j + 0];
-    }
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  uint32_t img_wid = pDIBitmap->GetWidth();
-  uint32_t img_hei = pDIBitmap->GetHeight();
-  uint32_t width = 0;
-  uint32_t height = 0;
-  TIFFGetField(m_tif_ctx, TIFFTAG_IMAGEWIDTH, &width);
-  TIFFGetField(m_tif_ctx, TIFFTAG_IMAGELENGTH, &height);
-  if (img_wid != width || img_hei != height)
-    return false;
-
-  if (pDIBitmap->GetBPP() == 32) {
-    uint16_t rotation = ORIENTATION_TOPLEFT;
-    TIFFGetField(m_tif_ctx, TIFFTAG_ORIENTATION, &rotation);
-    if (TIFFReadRGBAImageOriented(m_tif_ctx, img_wid, img_hei,
-                                  (uint32*)pDIBitmap->GetBuffer(), rotation,
-                                  1)) {
-      for (uint32_t row = 0; row < img_hei; row++) {
-        uint8_t* row_buf = (uint8_t*)pDIBitmap->GetScanline(row);
-        TiffBGRA2RGBA(row_buf, img_wid, 4);
-      }
-      return true;
-    }
-  }
-  uint16_t spp = 0;
-  uint16_t bps = 0;
-  TIFFGetField(m_tif_ctx, TIFFTAG_SAMPLESPERPIXEL, &spp);
-  TIFFGetField(m_tif_ctx, TIFFTAG_BITSPERSAMPLE, &bps);
-  FX_SAFE_UINT32 safe_bpp = bps;
-  safe_bpp *= spp;
-  if (!safe_bpp.IsValid())
-    return false;
-  uint32_t bpp = safe_bpp.ValueOrDie();
-  if (bpp == 1)
-    return Decode1bppRGB(pDIBitmap, height, width, bps, spp);
-  if (bpp <= 8)
-    return Decode8bppRGB(pDIBitmap, height, width, bps, spp);
-  if (bpp <= 24)
-    return Decode24bppRGB(pDIBitmap, height, width, bps, spp);
-  return false;
-}
-
-std::unique_ptr<CCodec_TiffModule::Context> CCodec_TiffModule::CreateDecoder(
-    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
-  auto pDecoder = pdfium::MakeUnique<CTiffContext>();
-  if (!pDecoder->InitDecoder(file_ptr))
-    return nullptr;
-
-  return pDecoder;
-}
-
-bool CCodec_TiffModule::LoadFrameInfo(Context* pContext,
-                                      int32_t frame,
-                                      int32_t* width,
-                                      int32_t* height,
-                                      int32_t* comps,
-                                      int32_t* bpc,
-                                      CFX_DIBAttribute* pAttribute) {
-  auto* ctx = static_cast<CTiffContext*>(pContext);
-  return ctx->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute);
-}
-
-bool CCodec_TiffModule::Decode(Context* pContext,
-                               const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  auto* ctx = static_cast<CTiffContext*>(pContext);
-  return ctx->Decode(pDIBitmap);
-}
diff --git a/core/fxcodec/codec/ccodec_tiffmodule.h b/core/fxcodec/codec/ccodec_tiffmodule.h
deleted file mode 100644
index 8d3bed7..0000000
--- a/core/fxcodec/codec/ccodec_tiffmodule.h
+++ /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
-
-#ifndef CORE_FXCODEC_CODEC_CCODEC_TIFFMODULE_H_
-#define CORE_FXCODEC_CODEC_CCODEC_TIFFMODULE_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-
-class CFX_DIBAttribute;
-class IFX_SeekableReadStream;
-
-class CCodec_TiffModule {
- public:
-  class Context {
-   public:
-    virtual ~Context() {}
-  };
-
-  std::unique_ptr<Context> CreateDecoder(
-      const RetainPtr<IFX_SeekableReadStream>& file_ptr);
-  bool LoadFrameInfo(Context* ctx,
-                     int32_t frame,
-                     int32_t* width,
-                     int32_t* height,
-                     int32_t* comps,
-                     int32_t* bpc,
-                     CFX_DIBAttribute* pAttribute);
-  bool Decode(Context* ctx, const RetainPtr<CFX_DIBitmap>& pDIBitmap);
-};
-
-#endif  // CORE_FXCODEC_CODEC_CCODEC_TIFFMODULE_H_
diff --git a/core/fxcodec/codec/cjpx_decoder.h b/core/fxcodec/codec/cjpx_decoder.h
deleted file mode 100644
index c689693..0000000
--- a/core/fxcodec/codec/cjpx_decoder.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CJPX_DECODER_H_
-#define CORE_FXCODEC_CODEC_CJPX_DECODER_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "third_party/libopenjpeg20/openjpeg.h"
-
-class CPDF_ColorSpace;
-
-class CJPX_Decoder {
- public:
-  explicit CJPX_Decoder(CPDF_ColorSpace* cs);
-  ~CJPX_Decoder();
-
-  bool Init(const unsigned char* src_data, uint32_t src_size);
-  void GetInfo(uint32_t* width, uint32_t* height, uint32_t* components);
-  bool Decode(uint8_t* dest_buf,
-              int pitch,
-              const std::vector<uint8_t>& offsets);
-
- private:
-  const uint8_t* m_SrcData;
-  uint32_t m_SrcSize;
-  // TODO(rharrison): Convert these to unowned ptrs, if possible.
-  opj_image_t* m_Image;
-  opj_codec_t* m_Codec;
-  std::unique_ptr<DecodeData> m_DecodeData;
-  opj_stream_t* m_Stream;
-  opj_dparameters_t m_Parameters;
-  UnownedPtr<const CPDF_ColorSpace> const m_ColorSpace;
-};
-
-#endif  // CORE_FXCODEC_CODEC_CJPX_DECODER_H_
diff --git a/core/fxcodec/codec/codec_int.h b/core/fxcodec/codec/codec_int.h
deleted file mode 100644
index df2e758..0000000
--- a/core/fxcodec/codec/codec_int.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_CODEC_CODEC_INT_H_
-#define CORE_FXCODEC_CODEC_CODEC_INT_H_
-
-#include <limits.h>
-
-#include <list>
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/jbig2/JBig2_Context.h"
-#include "third_party/libopenjpeg20/openjpeg.h"
-
-class CPDF_ColorSpace;
-
-struct DecodeData {
-  DecodeData(const uint8_t* data, OPJ_SIZE_T size)
-      : src_data(data), src_size(size), offset(0) {}
-
-  const uint8_t* src_data;
-  OPJ_SIZE_T src_size;
-  OPJ_SIZE_T offset;
-};
-
-void sycc420_to_rgb(opj_image_t* img);
-
-/* Wrappers for C-style callbacks. */
-OPJ_SIZE_T opj_read_from_memory(void* p_buffer,
-                                OPJ_SIZE_T nb_bytes,
-                                void* p_user_data);
-OPJ_OFF_T opj_skip_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data);
-OPJ_BOOL opj_seek_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data);
-
-#endif  // CORE_FXCODEC_CODEC_CODEC_INT_H_
diff --git a/core/fxcodec/codec/fx_codec.cpp b/core/fxcodec/codec/fx_codec.cpp
deleted file mode 100644
index 4d094e5..0000000
--- a/core/fxcodec/codec/fx_codec.cpp
+++ /dev/null
@@ -1,1841 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/fx_codec.h"
-
-#include <algorithm>
-#include <cmath>
-#include <memory>
-#include <tuple>
-#include <utility>
-
-#include "core/fxcodec/codec/ccodec_basicmodule.h"
-#include "core/fxcodec/codec/ccodec_faxmodule.h"
-#include "core/fxcodec/codec/ccodec_flatemodule.h"
-#include "core/fxcodec/codec/ccodec_iccmodule.h"
-#include "core/fxcodec/codec/ccodec_jbig2module.h"
-#include "core/fxcodec/codec/ccodec_jpegmodule.h"
-#include "core/fxcodec/codec/ccodec_jpxmodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "core/fxcodec/codec/ccodec_bmpmodule.h"
-#include "core/fxcodec/codec/ccodec_gifmodule.h"
-#include "core/fxcodec/codec/ccodec_pngmodule.h"
-#include "core/fxcodec/codec/ccodec_tiffmodule.h"
-#endif  // PDF_ENABLE_XFA
-
-namespace {
-
-const uint8_t g_CMYK[81 * 81 * 3] = {
-    255, 255, 255, 225, 226, 228, 199, 200, 202, 173, 174, 178, 147, 149, 152,
-    123, 125, 128, 99,  99,  102, 69,  70,  71,  34,  30,  31,  255, 253, 229,
-    226, 224, 203, 200, 199, 182, 173, 173, 158, 149, 148, 135, 125, 124, 113,
-    99,  99,  90,  70,  69,  63,  33,  29,  24,  255, 251, 204, 228, 223, 182,
-    201, 198, 163, 174, 172, 142, 150, 147, 122, 125, 123, 101, 99,  98,  80,
-    70,  68,  54,  32,  28,  16,  255, 249, 179, 230, 222, 160, 203, 197, 144,
-    174, 170, 124, 150, 145, 105, 125, 122, 88,  99,  97,  69,  70,  68,  46,
-    31,  28,  6,   255, 247, 154, 229, 220, 138, 203, 195, 122, 176, 169, 107,
-    150, 145, 91,  125, 121, 74,  100, 96,  57,  70,  67,  35,  29,  26,  0,
-    255, 246, 128, 231, 217, 114, 205, 194, 101, 176, 167, 88,  150, 144, 75,
-    125, 120, 60,  100, 96,  44,  70,  66,  24,  28,  26,  0,   255, 244, 96,
-    231, 217, 87,  203, 192, 78,  175, 167, 66,  150, 143, 56,  125, 119, 43,
-    100, 95,  29,  69,  66,  7,   26,  26,  0,   255, 243, 51,  232, 215, 51,
-    204, 191, 43,  176, 165, 38,  150, 142, 28,  125, 118, 17,  99,  94,  0,
-    68,  65,  0,   24,  25,  0,   255, 241, 0,   231, 215, 0,   203, 190, 0,
-    176, 164, 0,   150, 141, 0,   126, 117, 0,   99,  93,  0,   68,  65,  0,
-    24,  25,  0,   252, 228, 238, 222, 201, 211, 197, 180, 190, 171, 156, 166,
-    147, 133, 143, 123, 111, 119, 99,  88,  94,  71,  61,  66,  34,  22,  26,
-    254, 226, 213, 224, 201, 191, 199, 179, 171, 172, 155, 148, 147, 133, 128,
-    123, 110, 106, 98,  87,  83,  70,  59,  57,  33,  21,  18,  254, 224, 191,
-    224, 199, 172, 200, 177, 153, 173, 154, 133, 147, 132, 115, 123, 109, 94,
-    98,  86,  74,  70,  59,  49,  32,  21,  9,   255, 222, 168, 227, 198, 150,
-    200, 175, 135, 173, 153, 118, 148, 130, 99,  123, 109, 82,  98,  86,  64,
-    69,  58,  40,  31,  19,  0,   255, 221, 145, 227, 196, 129, 201, 174, 115,
-    173, 151, 99,  148, 129, 85,  124, 108, 69,  98,  85,  52,  69,  58,  30,
-    30,  19,  0,   255, 219, 121, 227, 195, 109, 201, 174, 97,  174, 150, 83,
-    148, 129, 70,  124, 107, 55,  98,  84,  40,  69,  58,  19,  28,  18,  0,
-    255, 218, 92,  229, 194, 82,  202, 173, 75,  174, 150, 63,  149, 128, 51,
-    124, 106, 39,  98,  84,  24,  68,  57,  3,   26,  18,  0,   255, 217, 54,
-    228, 193, 52,  201, 172, 46,  174, 148, 36,  148, 127, 27,  123, 105, 14,
-    98,  83,  0,   68,  56,  0,   25,  18,  0,   255, 216, 0,   229, 192, 2,
-    202, 171, 4,   173, 148, 0,   148, 126, 0,   124, 105, 0,   98,  83,  0,
-    68,  56,  0,   24,  17,  0,   249, 204, 223, 219, 181, 199, 195, 160, 178,
-    170, 140, 156, 146, 119, 134, 123, 99,  112, 98,  77,  88,  70,  52,  61,
-    34,  11,  20,  250, 201, 200, 221, 180, 178, 197, 159, 161, 171, 139, 139,
-    147, 119, 120, 123, 98,  99,  98,  77,  78,  69,  51,  52,  34,  11,  10,
-    252, 201, 180, 223, 179, 162, 197, 159, 144, 170, 138, 125, 146, 117, 107,
-    122, 97,  89,  98,  76,  69,  69,  50,  44,  32,  11,  2,   252, 199, 158,
-    222, 177, 143, 199, 158, 127, 171, 137, 110, 147, 117, 93,  122, 96,  76,
-    97,  75,  58,  69,  50,  36,  32,  10,  0,   253, 198, 137, 223, 177, 123,
-    198, 156, 110, 171, 136, 95,  146, 116, 80,  122, 96,  65,  97,  75,  47,
-    69,  50,  25,  30,  10,  0,   254, 197, 115, 225, 175, 104, 198, 156, 92,
-    172, 135, 79,  147, 115, 66,  123, 95,  52,  98,  74,  37,  69,  49,  15,
-    29,  10,  0,   254, 196, 89,  224, 175, 80,  199, 154, 70,  172, 134, 59,
-    146, 114, 48,  122, 95,  36,  97,  74,  21,  68,  49,  0,   27,  9,   0,
-    255, 195, 57,  225, 173, 52,  198, 154, 44,  172, 133, 36,  147, 113, 26,
-    123, 94,  14,  98,  74,  0,   68,  49,  0,   26,  10,  0,   254, 194, 15,
-    225, 172, 12,  198, 153, 7,   172, 132, 3,   146, 113, 0,   123, 93,  0,
-    98,  73,  0,   68,  49,  0,   26,  9,   0,   246, 178, 209, 218, 159, 186,
-    194, 140, 166, 168, 122, 145, 144, 104, 125, 121, 85,  103, 97,  65,  81,
-    69,  41,  55,  34,  0,   12,  248, 176, 186, 219, 157, 166, 195, 139, 149,
-    168, 121, 130, 144, 103, 111, 121, 85,  91,  97,  65,  71,  69,  41,  46,
-    34,  0,   4,   249, 175, 168, 220, 156, 150, 196, 139, 135, 169, 121, 116,
-    144, 103, 100, 122, 84,  83,  98,  65,  63,  70,  41,  39,  33,  0,   0,
-    249, 175, 148, 220, 155, 133, 196, 138, 119, 169, 120, 103, 145, 101, 87,
-    121, 83,  71,  97,  65,  54,  69,  41,  31,  32,  0,   0,   249, 173, 128,
-    222, 154, 115, 195, 137, 102, 170, 119, 88,  145, 101, 74,  122, 83,  59,
-    97,  64,  43,  68,  40,  20,  30,  0,   0,   250, 172, 108, 221, 154, 98,
-    195, 136, 86,  170, 118, 73,  145, 100, 61,  122, 82,  48,  97,  63,  32,
-    69,  40,  11,  28,  0,   0,   250, 171, 85,  221, 153, 76,  196, 136, 67,
-    170, 117, 56,  145, 99,  44,  121, 82,  33,  97,  63,  17,  68,  40,  0,
-    28,  0,   0,   251, 171, 58,  222, 152, 50,  197, 135, 43,  169, 117, 34,
-    146, 99,  25,  121, 81,  10,  96,  63,  0,   68,  40,  0,   27,  0,   0,
-    250, 170, 26,  222, 151, 19,  196, 134, 13,  169, 116, 4,   145, 99,  0,
-    122, 81,  0,   97,  63,  0,   67,  40,  0,   26,  0,   0,   244, 153, 194,
-    215, 136, 173, 192, 121, 155, 167, 104, 135, 143, 89,  115, 121, 72,  96,
-    97,  54,  75,  70,  31,  49,  34,  0,   6,   245, 153, 173, 216, 136, 155,
-    192, 120, 138, 167, 104, 121, 144, 88,  103, 121, 71,  85,  97,  54,  66,
-    69,  31,  42,  34,  0,   0,   246, 152, 157, 217, 135, 140, 193, 120, 126,
-    167, 103, 109, 143, 88,  92,  121, 72,  76,  97,  54,  58,  69,  31,  35,
-    33,  0,   0,   245, 150, 139, 218, 134, 125, 193, 119, 111, 167, 103, 96,
-    144, 87,  80,  121, 71,  66,  96,  53,  49,  68,  31,  26,  32,  0,   0,
-    246, 151, 122, 218, 133, 108, 194, 118, 96,  168, 102, 81,  144, 86,  69,
-    120, 71,  55,  95,  53,  39,  68,  30,  17,  31,  0,   0,   248, 150, 103,
-    218, 133, 91,  193, 118, 81,  168, 102, 69,  143, 86,  56,  120, 70,  43,
-    96,  53,  28,  68,  31,  6,   29,  0,   0,   247, 149, 81,  218, 132, 72,
-    194, 117, 62,  168, 101, 52,  144, 86,  42,  121, 70,  29,  96,  52,  13,
-    68,  30,  0,   28,  0,   0,   247, 148, 55,  219, 131, 50,  194, 117, 43,
-    167, 101, 32,  144, 85,  22,  120, 69,  8,   96,  52,  0,   67,  30,  0,
-    27,  0,   0,   247, 147, 29,  218, 131, 24,  194, 116, 20,  168, 100, 11,
-    144, 85,  0,   120, 69,  0,   96,  52,  0,   67,  30,  0,   26,  0,   0,
-    242, 130, 179, 214, 114, 160, 190, 101, 143, 166, 87,  125, 143, 72,  107,
-    120, 58,  88,  96,  42,  68,  69,  17,  44,  35,  0,   0,   243, 129, 161,
-    215, 114, 143, 191, 101, 128, 166, 87,  113, 143, 73,  96,  120, 58,  79,
-    96,  41,  60,  69,  18,  37,  33,  0,   0,   243, 129, 146, 216, 114, 130,
-    192, 101, 117, 166, 87,  101, 143, 72,  86,  121, 58,  69,  96,  42,  52,
-    69,  18,  29,  31,  0,   0,   243, 128, 130, 216, 114, 115, 191, 101, 102,
-    165, 86,  88,  142, 72,  75,  120, 58,  60,  95,  42,  43,  68,  19,  21,
-    30,  0,   0,   244, 127, 112, 217, 113, 101, 192, 99,  89,  166, 85,  75,
-    142, 72,  63,  119, 57,  50,  96,  41,  35,  68,  19,  13,  30,  0,   0,
-    244, 127, 96,  216, 112, 86,  191, 99,  75,  166, 86,  64,  143, 72,  52,
-    120, 57,  40,  95,  41,  24,  67,  20,  1,   29,  0,   0,   245, 126, 77,
-    216, 113, 68,  191, 100, 59,  166, 85,  49,  142, 71,  38,  119, 57,  26,
-    95,  41,  10,  67,  20,  0,   28,  0,   0,   244, 126, 55,  216, 112, 48,
-    191, 99,  40,  166, 85,  31,  143, 71,  20,  119, 57,  6,   95,  42,  0,
-    67,  20,  0,   28,  0,   0,   245, 126, 33,  217, 112, 26,  192, 99,  22,
-    166, 84,  11,  142, 70,  0,   119, 57,  0,   95,  41,  0,   66,  20,  0,
-    27,  0,   0,   241, 102, 167, 213, 90,  149, 189, 79,  133, 165, 66,  115,
-    141, 54,  98,  119, 41,  81,  96,  25,  63,  69,  0,   38,  30,  0,   0,
-    241, 102, 149, 213, 90,  133, 189, 79,  119, 165, 66,  103, 142, 55,  88,
-    119, 41,  71,  96,  25,  53,  69,  0,   31,  28,  0,   0,   241, 102, 135,
-    214, 90,  121, 190, 79,  108, 165, 66,  92,  141, 55,  78,  119, 42,  63,
-    96,  26,  46,  69,  0,   24,  28,  0,   0,   241, 101, 120, 214, 90,  107,
-    189, 79,  95,  165, 67,  83,  141, 54,  68,  118, 41,  54,  95,  27,  39,
-    68,  0,   16,  27,  0,   0,   241, 102, 106, 213, 90,  93,  189, 78,  82,
-    164, 67,  70,  141, 55,  58,  118, 42,  45,  94,  27,  29,  67,  2,   6,
-    27,  0,   0,   242, 101, 90,  214, 89,  79,  190, 79,  69,  166, 67,  59,
-    141, 55,  47,  118, 41,  35,  95,  27,  19,  67,  3,   0,   26,  0,   0,
-    242, 102, 72,  213, 89,  63,  191, 79,  56,  164, 67,  45,  141, 55,  34,
-    118, 42,  22,  94,  28,  6,   67,  3,   0,   26,  0,   0,   242, 100, 51,
-    214, 89,  45,  190, 78,  38,  164, 67,  30,  141, 55,  18,  118, 42,  3,
-    95,  28,  0,   66,  4,   0,   26,  0,   0,   243, 100, 33,  214, 90,  27,
-    190, 78,  22,  165, 67,  13,  141, 55,  0,   118, 43,  0,   94,  29,  0,
-    66,  5,   0,   26,  0,   0,   237, 69,  153, 211, 58,  135, 187, 51,  121,
-    163, 41,  105, 141, 28,  90,  118, 15,  73,  96,  0,   56,  68,  0,   33,
-    25,  0,   0,   239, 67,  137, 212, 60,  123, 189, 50,  110, 163, 41,  94,
-    141, 29,  79,  118, 17,  65,  95,  0,   48,  69,  0,   26,  25,  0,   0,
-    240, 69,  124, 211, 60,  111, 188, 50,  98,  163, 42,  85,  141, 31,  72,
-    118, 18,  57,  94,  0,   41,  68,  0,   19,  25,  0,   0,   240, 70,  112,
-    212, 61,  99,  188, 52,  87,  163, 41,  74,  140, 31,  62,  118, 20,  48,
-    94,  2,   32,  68,  0,   11,  24,  0,   0,   239, 70,  98,  212, 62,  86,
-    188, 53,  77,  164, 42,  64,  140, 32,  52,  118, 20,  40,  94,  3,   24,
-    67,  0,   3,   23,  0,   0,   239, 71,  85,  212, 61,  74,  187, 53,  65,
-    163, 44,  54,  140, 34,  43,  118, 22,  30,  95,  3,   14,  67,  0,   0,
-    23,  0,   0,   239, 70,  67,  212, 62,  59,  188, 53,  51,  163, 45,  42,
-    141, 34,  31,  117, 22,  17,  94,  5,   2,   66,  0,   0,   23,  0,   0,
-    239, 71,  50,  213, 62,  43,  188, 54,  37,  164, 45,  28,  139, 34,  16,
-    117, 22,  2,   94,  7,   0,   65,  0,   0,   23,  0,   0,   240, 71,  34,
-    212, 63,  29,  189, 54,  24,  163, 46,  15,  139, 36,  2,   117, 25,  0,
-    94,  8,   0,   66,  0,   0,   23,  0,   0,   237, 0,   140, 209, 0,   124,
-    186, 0,   112, 162, 0,   97,  141, 0,   82,  118, 0,   67,  95,  0,   49,
-    68,  0,   27,  20,  0,   0,   237, 0,   126, 210, 0,   113, 187, 0,   99,
-    163, 0,   86,  139, 0,   72,  118, 0,   58,  95,  0,   42,  67,  0,   20,
-    20,  0,   0,   237, 1,   114, 209, 1,   102, 187, 0,   90,  163, 0,   78,
-    139, 0,   64,  118, 0,   50,  95,  0,   35,  67,  0,   13,  20,  0,   0,
-    236, 16,  102, 209, 7,   91,  186, 0,   80,  162, 0,   68,  139, 0,   56,
-    117, 0,   43,  94,  0,   27,  67,  0,   6,   20,  0,   0,   238, 15,  89,
-    209, 13,  79,  186, 6,   69,  162, 0,   58,  139, 0,   47,  117, 0,   34,
-    93,  0,   20,  66,  0,   2,   20,  0,   0,   237, 20,  78,  210, 12,  68,
-    187, 4,   59,  163, 0,   49,  139, 0,   38,  116, 0,   26,  94,  0,   11,
-    66,  0,   0,   20,  0,   0,   237, 25,  64,  210, 18,  56,  186, 11,  48,
-    162, 4,   39,  138, 0,   27,  117, 0,   14,  93,  0,   0,   66,  0,   0,
-    20,  0,   0,   238, 25,  48,  210, 22,  43,  186, 15,  35,  162, 8,   26,
-    140, 0,   14,  117, 0,   0,   93,  0,   0,   65,  0,   0,   20,  0,   0,
-    238, 28,  35,  210, 21,  30,  187, 15,  24,  162, 8,   16,  139, 1,   2,
-    117, 0,   0,   93,  0,   0,   65,  0,   0,   22,  0,   0,   219, 242, 252,
-    195, 214, 225, 172, 191, 201, 148, 165, 175, 127, 142, 150, 106, 119, 126,
-    84,  95,  101, 58,  66,  72,  24,  27,  32,  222, 239, 226, 196, 213, 202,
-    173, 189, 180, 150, 165, 158, 129, 141, 135, 107, 118, 113, 85,  94,  90,
-    58,  66,  63,  21,  26,  24,  223, 237, 203, 198, 211, 182, 175, 188, 163,
-    152, 164, 141, 129, 140, 121, 107, 117, 101, 85,  93,  80,  58,  64,  54,
-    21,  26,  18,  226, 236, 179, 201, 210, 160, 177, 187, 143, 153, 162, 125,
-    130, 139, 106, 108, 116, 89,  85,  92,  69,  58,  64,  45,  20,  25,  8,
-    227, 234, 153, 201, 208, 139, 178, 185, 124, 154, 161, 107, 131, 138, 91,
-    108, 115, 75,  85,  91,  58,  58,  63,  35,  17,  25,  0,   229, 233, 130,
-    203, 207, 116, 178, 184, 104, 154, 160, 90,  131, 137, 76,  109, 114, 62,
-    85,  90,  46,  58,  63,  25,  16,  24,  0,   230, 231, 100, 202, 205, 90,
-    179, 183, 80,  154, 159, 69,  131, 136, 57,  109, 113, 46,  86,  90,  32,
-    58,  63,  10,  14,  24,  0,   230, 230, 65,  204, 204, 58,  180, 182, 52,
-    155, 157, 44,  132, 135, 35,  110, 113, 24,  86,  89,  9,   57,  62,  0,
-    11,  24,  0,   232, 230, 19,  204, 204, 19,  180, 181, 17,  155, 157, 10,
-    131, 134, 2,   109, 112, 0,   85,  89,  0,   57,  62,  0,   10,  23,  0,
-    218, 216, 236, 194, 192, 211, 172, 171, 188, 149, 149, 164, 128, 127, 141,
-    106, 106, 119, 84,  84,  94,  59,  57,  66,  25,  18,  26,  221, 214, 211,
-    196, 191, 190, 174, 170, 170, 150, 148, 148, 128, 126, 127, 107, 105, 106,
-    85,  83,  84,  59,  56,  58,  23,  17,  18,  222, 213, 190, 197, 189, 170,
-    175, 169, 153, 151, 147, 133, 129, 126, 113, 108, 105, 94,  85,  82,  74,
-    59,  56,  49,  22,  17,  11,  224, 211, 168, 199, 188, 151, 175, 168, 135,
-    152, 146, 117, 129, 124, 99,  107, 103, 82,  84,  82,  64,  59,  55,  41,
-    21,  17,  1,   224, 210, 145, 199, 187, 130, 176, 166, 117, 152, 145, 101,
-    129, 123, 86,  107, 103, 70,  85,  81,  53,  58,  55,  31,  19,  17,  0,
-    227, 208, 123, 200, 186, 110, 177, 165, 98,  153, 143, 84,  130, 122, 70,
-    108, 102, 57,  85,  80,  41,  58,  54,  20,  18,  16,  0,   227, 208, 97,
-    202, 185, 86,  177, 164, 77,  153, 142, 65,  130, 122, 54,  108, 101, 42,
-    85,  80,  27,  58,  54,  7,   16,  16,  0,   228, 206, 66,  202, 184, 58,
-    178, 163, 50,  154, 141, 42,  131, 121, 33,  109, 101, 21,  86,  79,  5,
-    58,  54,  0,   13,  16,  0,   228, 206, 29,  202, 183, 25,  178, 163, 20,
-    154, 141, 15,  131, 121, 5,   108, 100, 0,   85,  79,  0,   58,  53,  0,
-    13,  16,  0,   217, 193, 221, 193, 172, 198, 172, 153, 178, 149, 133, 154,
-    128, 114, 132, 107, 94,  111, 85,  74,  89,  59,  49,  61,  25,  8,   22,
-    219, 191, 198, 195, 171, 178, 173, 153, 159, 149, 132, 139, 128, 113, 119,
-    107, 94,  100, 85,  73,  79,  59,  48,  52,  25,  7,   14,  221, 191, 180,
-    196, 170, 160, 174, 152, 144, 150, 132, 125, 129, 113, 107, 107, 93,  89,
-    85,  73,  69,  59,  48,  45,  23,  7,   4,   222, 189, 159, 197, 169, 142,
-    174, 151, 127, 151, 131, 110, 129, 112, 94,  108, 93,  78,  85,  72,  60,
-    58,  47,  37,  22,  7,   0,   223, 188, 138, 197, 168, 123, 175, 150, 109,
-    151, 130, 95,  130, 111, 81,  108, 92,  65,  85,  72,  49,  59,  47,  27,
-    21,  7,   0,   224, 187, 118, 198, 167, 105, 176, 149, 93,  152, 129, 79,
-    130, 110, 68,  108, 91,  54,  85,  71,  38,  59,  47,  17,  18,  7,   0,
-    224, 187, 93,  199, 166, 83,  176, 148, 73,  152, 128, 62,  129, 109, 51,
-    108, 90,  39,  85,  71,  25,  58,  46,  3,   16,  8,   0,   226, 186, 64,
-    200, 165, 57,  177, 147, 50,  153, 127, 40,  130, 108, 31,  108, 90,  19,
-    85,  70,  3,   58,  46,  0,   16,  8,   0,   227, 185, 35,  200, 165, 30,
-    176, 146, 25,  152, 127, 18,  130, 108, 7,   108, 89,  0,   85,  70,  0,
-    57,  46,  0,   14,  8,   0,   216, 169, 205, 192, 150, 184, 171, 134, 164,
-    149, 116, 144, 128, 99,  124, 107, 81,  103, 85,  63,  81,  60,  39,  55,
-    26,  0,   15,  217, 168, 186, 193, 150, 165, 172, 134, 149, 150, 116, 130,
-    128, 99,  111, 107, 81,  92,  85,  62,  72,  59,  39,  47,  25,  0,   6,
-    219, 168, 168, 194, 149, 150, 173, 133, 135, 150, 116, 117, 128, 98,  99,
-    107, 80,  82,  86,  62,  63,  59,  38,  39,  24,  0,   0,   219, 166, 148,
-    195, 149, 133, 173, 133, 119, 150, 115, 103, 128, 98,  88,  107, 80,  72,
-    85,  61,  54,  59,  38,  32,  23,  0,   0,   220, 166, 129, 196, 148, 116,
-    174, 132, 103, 151, 114, 89,  129, 97,  75,  107, 79,  60,  85,  61,  44,
-    59,  38,  22,  21,  0,   0,   222, 164, 110, 197, 147, 99,  175, 131, 87,
-    151, 113, 75,  129, 96,  63,  107, 79,  49,  85,  61,  33,  58,  38,  12,
-    19,  0,   0,   222, 164, 88,  197, 146, 79,  174, 130, 69,  151, 113, 58,
-    129, 95,  47,  107, 78,  35,  85,  60,  20,  58,  38,  0,   18,  0,   0,
-    223, 164, 63,  198, 145, 55,  175, 129, 48,  151, 112, 39,  129, 95,  29,
-    107, 78,  16,  85,  60,  1,   58,  38,  0,   17,  0,   0,   223, 163, 36,
-    198, 145, 32,  174, 129, 26,  151, 111, 17,  129, 95,  7,   107, 78,  0,
-    84,  60,  0,   57,  37,  0,   15,  0,   0,   215, 147, 192, 191, 130, 172,
-    170, 116, 153, 148, 100, 133, 127, 85,  115, 107, 69,  96,  85,  51,  75,
-    60,  28,  50,  25,  0,   8,   217, 146, 173, 192, 130, 154, 171, 115, 138,
-    149, 100, 121, 128, 84,  103, 107, 68,  85,  85,  51,  66,  60,  28,  42,
-    25,  0,   0,   217, 145, 157, 193, 129, 140, 173, 115, 125, 149, 100, 109,
-    128, 84,  92,  107, 68,  76,  85,  51,  58,  59,  28,  35,  23,  0,   0,
-    218, 145, 140, 193, 129, 125, 172, 114, 110, 149, 99,  96,  128, 83,  81,
-    107, 67,  65,  84,  51,  49,  59,  29,  27,  22,  0,   0,   219, 144, 121,
-    194, 128, 108, 172, 113, 96,  149, 98,  83,  128, 83,  69,  107, 68,  55,
-    85,  50,  40,  59,  28,  18,  20,  0,   0,   220, 143, 104, 195, 128, 93,
-    173, 114, 82,  150, 98,  69,  127, 82,  58,  107, 67,  45,  85,  50,  30,
-    59,  28,  7,   19,  0,   0,   220, 143, 84,  195, 127, 74,  173, 113, 65,
-    149, 97,  55,  128, 82,  44,  106, 67,  32,  84,  50,  16,  58,  28,  0,
-    18,  0,   0,   221, 142, 62,  196, 126, 53,  173, 112, 46,  150, 97,  37,
-    128, 82,  26,  107, 66,  14,  84,  50,  0,   58,  28,  0,   16,  0,   0,
-    222, 142, 38,  196, 126, 34,  174, 112, 27,  150, 96,  17,  128, 82,  6,
-    106, 66,  0,   84,  50,  0,   57,  29,  0,   16,  0,   0,   214, 123, 179,
-    191, 110, 159, 169, 98,  143, 147, 84,  124, 126, 70,  106, 107, 55,  88,
-    85,  39,  69,  60,  15,  45,  23,  0,   2,   216, 123, 161, 192, 110, 144,
-    170, 98,  129, 148, 84,  112, 127, 70,  95,  107, 55,  79,  85,  39,  61,
-    60,  15,  37,  20,  0,   0,   217, 122, 145, 192, 110, 130, 170, 97,  116,
-    149, 84,  101, 127, 70,  85,  106, 55,  70,  85,  39,  53,  59,  16,  30,
-    19,  0,   0,   217, 123, 131, 192, 109, 116, 171, 96,  103, 149, 83,  89,
-    127, 70,  75,  106, 55,  60,  85,  40,  45,  59,  16,  23,  17,  0,   0,
-    217, 122, 114, 193, 109, 101, 172, 96,  91,  149, 82,  77,  128, 69,  64,
-    106, 55,  50,  84,  39,  35,  59,  17,  14,  17,  0,   0,   218, 122, 98,
-    194, 108, 87,  171, 96,  77,  149, 82,  65,  127, 69,  52,  106, 55,  40,
-    84,  40,  25,  59,  18,  3,   15,  0,   0,   219, 122, 80,  193, 108, 70,
-    172, 95,  61,  149, 82,  51,  127, 69,  40,  106, 55,  28,  84,  39,  12,
-    58,  17,  0,   13,  0,   0,   219, 121, 59,  194, 108, 52,  172, 96,  44,
-    149, 82,  35,  127, 68,  24,  106, 55,  11,  84,  40,  0,   57,  18,  0,
-    13,  0,   0,   219, 121, 40,  193, 108, 33,  172, 95,  26,  149, 81,  19,
-    128, 68,  6,   106, 54,  0,   84,  39,  0,   57,  18,  0,   13,  0,   0,
-    213, 99,  165, 189, 87,  148, 169, 76,  132, 147, 64,  115, 126, 52,  98,
-    106, 39,  81,  85,  23,  63,  60,  0,   39,  16,  0,   0,   214, 98,  149,
-    191, 87,  133, 170, 76,  119, 148, 65,  103, 127, 53,  88,  106, 39,  72,
-    85,  24,  55,  60,  0,   32,  15,  0,   0,   215, 99,  136, 191, 87,  121,
-    170, 77,  108, 148, 65,  93,  126, 53,  79,  106, 40,  64,  85,  24,  47,
-    59,  0,   25,  14,  0,   0,   215, 99,  121, 192, 87,  108, 170, 77,  96,
-    148, 65,  82,  126, 53,  69,  106, 40,  55,  85,  25,  39,  59,  0,   18,
-    13,  0,   0,   216, 99,  106, 191, 87,  95,  170, 76,  83,  148, 65,  71,
-    126, 53,  58,  106, 41,  45,  85,  26,  30,  59,  0,   8,   11,  0,   0,
-    216, 98,  91,  192, 88,  82,  170, 77,  71,  148, 65,  60,  127, 53,  48,
-    105, 41,  36,  83,  26,  21,  58,  1,   2,   11,  0,   0,   217, 99,  75,
-    192, 87,  66,  170, 76,  57,  148, 65,  47,  126, 53,  36,  105, 41,  24,
-    83,  26,  8,   57,  2,   0,   9,   0,   0,   217, 98,  57,  192, 87,  49,
-    171, 77,  41,  147, 65,  32,  126, 53,  21,  105, 41,  8,   84,  27,  0,
-    57,  3,   0,   9,   0,   0,   217, 98,  40,  193, 87,  34,  171, 76,  27,
-    148, 65,  19,  126, 53,  6,   105, 41,  0,   83,  27,  0,   57,  4,   0,
-    9,   0,   0,   211, 67,  152, 189, 58,  136, 168, 50,  122, 147, 39,  105,
-    127, 28,  89,  106, 14,  74,  85,  0,   56,  59,  0,   33,  9,   0,   0,
-    213, 68,  138, 190, 59,  123, 169, 51,  109, 148, 40,  95,  126, 30,  80,
-    106, 16,  65,  85,  0,   48,  59,  0,   27,  9,   0,   0,   214, 69,  125,
-    190, 59,  111, 168, 51,  99,  148, 41,  86,  126, 31,  72,  106, 18,  58,
-    85,  0,   41,  59,  0,   20,  7,   0,   0,   215, 70,  112, 190, 61,  100,
-    169, 52,  88,  147, 42,  76,  126, 32,  63,  106, 19,  49,  84,  1,   34,
-    58,  0,   13,  7,   0,   0,   214, 70,  99,  190, 62,  88,  169, 53,  77,
-    147, 43,  65,  125, 32,  53,  106, 20,  40,  84,  3,   26,  58,  0,   4,
-    7,   0,   0,   214, 71,  86,  190, 61,  75,  169, 53,  65,  146, 43,  54,
-    126, 33,  44,  105, 21,  31,  83,  4,   17,  57,  0,   0,   7,   0,   0,
-    215, 71,  71,  191, 62,  62,  169, 53,  53,  147, 44,  44,  126, 34,  33,
-    105, 22,  20,  83,  5,   4,   57,  0,   0,   7,   0,   0,   215, 71,  54,
-    191, 62,  47,  169, 54,  39,  147, 44,  30,  126, 35,  20,  105, 23,  6,
-    83,  6,   0,   56,  0,   0,   5,   0,   0,   215, 71,  41,  191, 63,  34,
-    170, 54,  27,  147, 45,  17,  126, 35,  6,   105, 23,  0,   83,  8,   0,
-    56,  0,   0,   5,   0,   0,   210, 13,  140, 189, 1,   125, 167, 0,   110,
-    146, 0,   96,  126, 0,   81,  106, 0,   67,  85,  0,   51,  59,  0,   28,
-    4,   0,   0,   212, 18,  126, 190, 7,   113, 168, 0,   100, 146, 0,   86,
-    126, 0,   73,  106, 0,   59,  84,  0,   43,  59,  0,   22,  4,   0,   0,
-    212, 21,  115, 190, 13,  103, 168, 3,   91,  146, 0,   78,  125, 0,   65,
-    105, 0,   52,  84,  0,   36,  58,  0,   16,  4,   0,   0,   213, 24,  103,
-    189, 19,  91,  168, 9,   82,  146, 0,   69,  125, 0,   57,  105, 0,   44,
-    84,  0,   29,  58,  0,   7,   4,   0,   0,   213, 27,  92,  188, 21,  81,
-    168, 14,  71,  146, 1,   59,  125, 0,   48,  105, 0,   36,  84,  0,   21,
-    58,  0,   4,   4,   0,   0,   213, 30,  80,  189, 22,  69,  168, 17,  61,
-    146, 5,   50,  125, 0,   39,  104, 0,   27,  83,  0,   12,  57,  0,   0,
-    4,   0,   0,   214, 30,  67,  189, 25,  57,  168, 20,  50,  146, 9,   40,
-    125, 0,   29,  104, 0,   17,  83,  0,   2,   56,  0,   0,   4,   0,   0,
-    214, 32,  53,  189, 27,  44,  169, 20,  38,  146, 13,  28,  124, 2,   17,
-    104, 0,   4,   83,  0,   0,   56,  0,   0,   4,   0,   0,   214, 33,  41,
-    190, 27,  33,  168, 23,  27,  146, 13,  18,  125, 3,   5,   105, 0,   0,
-    83,  0,   0,   56,  0,   0,   4,   0,   0,   185, 229, 250, 164, 204, 223,
-    146, 182, 199, 127, 158, 174, 108, 136, 149, 89,  113, 125, 70,  90,  100,
-    46,  62,  71,  10,  25,  33,  189, 227, 225, 168, 202, 201, 148, 181, 179,
-    129, 157, 156, 109, 135, 134, 90,  113, 113, 70,  89,  90,  46,  62,  62,
-    8,   24,  25,  192, 226, 202, 170, 202, 182, 151, 179, 162, 130, 156, 141,
-    110, 133, 121, 91,  112, 101, 71,  89,  80,  46,  61,  54,  7,   24,  19,
-    194, 224, 179, 173, 200, 160, 153, 178, 144, 132, 155, 125, 112, 133, 107,
-    92,  111, 89,  71,  88,  69,  46,  61,  45,  6,   23,  10,  196, 223, 155,
-    174, 198, 139, 154, 176, 124, 132, 153, 107, 113, 131, 91,  92,  110, 75,
-    72,  87,  58,  47,  60,  37,  4,   23,  0,   198, 221, 131, 175, 197, 117,
-    155, 175, 105, 133, 152, 91,  113, 130, 76,  92,  109, 63,  72,  86,  47,
-    46,  60,  26,  3,   23,  0,   200, 220, 104, 176, 196, 94,  156, 175, 84,
-    134, 151, 72,  113, 129, 59,  93,  108, 47,  72,  85,  33,  46,  59,  13,
-    0,   23,  0,   201, 219, 73,  179, 195, 65,  157, 173, 57,  135, 150, 48,
-    114, 129, 39,  94,  108, 28,  72,  85,  15,  47,  59,  0,   0,   22,  0,
-    203, 219, 42,  178, 195, 37,  157, 173, 32,  135, 150, 26,  114, 128, 16,
-    94,  107, 6,   73,  85,  0,   46,  58,  0,   0,   22,  0,   186, 205, 233,
-    165, 183, 209, 148, 163, 187, 128, 142, 163, 109, 121, 140, 91,  101, 118,
-    71,  80,  94,  48,  54,  66,  12,  15,  27,  189, 204, 211, 169, 182, 189,
-    151, 163, 169, 131, 141, 147, 111, 121, 126, 92,  101, 105, 72,  79,  84,
-    48,  54,  58,  11,  15,  19,  192, 202, 190, 171, 181, 170, 152, 161, 152,
-    131, 141, 133, 112, 120, 113, 93,  100, 94,  72,  79,  74,  48,  53,  50,
-    10,  15,  11,  195, 201, 169, 172, 179, 151, 153, 160, 135, 132, 139, 117,
-    113, 119, 100, 93,  99,  82,  72,  78,  64,  48,  53,  41,  9,   14,  3,
-    195, 200, 146, 174, 179, 131, 154, 159, 117, 133, 138, 101, 113, 118, 86,
-    93,  98,  70,  73,  77,  53,  48,  52,  32,  8,   15,  0,   198, 199, 125,
-    175, 177, 111, 155, 158, 100, 133, 137, 85,  113, 117, 71,  93,  97,  57,
-    72,  77,  42,  47,  52,  22,  5,   14,  0,   199, 198, 101, 176, 177, 89,
-    155, 157, 79,  134, 136, 68,  113, 116, 56,  94,  97,  44,  73,  76,  30,
-    47,  52,  10,  2,   15,  0,   200, 197, 72,  178, 176, 63,  157, 156, 56,
-    135, 136, 46,  114, 116, 37,  94,  96,  26,  73,  76,  11,  47,  51,  0,
-    0,   14,  0,   201, 197, 45,  177, 175, 38,  156, 155, 31,  135, 135, 25,
-    114, 115, 17,  94,  96,  5,   73,  75,  0,   46,  51,  0,   0,   14,  0,
-    187, 183, 218, 167, 165, 197, 149, 147, 176, 129, 127, 153, 111, 109, 132,
-    92,  90,  111, 73,  70,  89,  49,  46,  62,  15,  4,   22,  190, 183, 197,
-    170, 164, 177, 151, 146, 159, 130, 127, 139, 112, 109, 119, 93,  90,  99,
-    72,  70,  78,  49,  45,  53,  14,  4,   15,  192, 182, 179, 171, 163, 161,
-    153, 145, 144, 132, 126, 125, 113, 108, 107, 93,  89,  88,  73,  70,  69,
-    49,  45,  45,  13,  5,   6,   195, 181, 159, 172, 162, 142, 152, 145, 127,
-    132, 125, 111, 113, 107, 94,  93,  88,  77,  73,  69,  59,  48,  45,  37,
-    11,  5,   0,   195, 180, 139, 173, 161, 124, 153, 143, 110, 133, 125, 96,
-    113, 106, 81,  94,  88,  66,  73,  68,  49,  49,  44,  28,  9,   6,   0,
-    196, 179, 118, 174, 160, 106, 154, 142, 94,  133, 124, 81,  113, 105, 68,
-    94,  87,  54,  73,  68,  39,  48,  44,  18,  5,   5,   0,   197, 178, 96,
-    176, 159, 86,  155, 141, 75,  134, 123, 64,  114, 105, 53,  94,  87,  40,
-    73,  68,  26,  48,  44,  5,   2,   6,   0,   199, 178, 70,  176, 158, 62,
-    156, 141, 54,  134, 122, 44,  114, 104, 35,  94,  86,  23,  73,  67,  8,
-    47,  44,  0,   2,   6,   0,   199, 177, 45,  178, 158, 40,  156, 140, 32,
-    135, 122, 26,  114, 104, 16,  94,  86,  4,   73,  67,  0,   47,  44,  0,
-    0,   7,   0,   188, 161, 204, 168, 144, 183, 149, 129, 164, 130, 112, 144,
-    112, 95,  123, 93,  78,  103, 74,  60,  81,  50,  36,  56,  16,  0,   16,
-    190, 160, 185, 170, 144, 165, 151, 128, 148, 132, 111, 130, 112, 95,  110,
-    93,  78,  92,  74,  59,  72,  50,  36,  48,  16,  0,   8,   192, 160, 167,
-    171, 143, 150, 153, 128, 134, 132, 111, 117, 112, 94,  100, 94,  77,  82,
-    74,  59,  63,  50,  36,  40,  14,  0,   0,   193, 159, 149, 172, 143, 134,
-    153, 127, 119, 133, 110, 103, 113, 94,  87,  93,  77,  72,  73,  59,  54,
-    50,  36,  32,  12,  0,   0,   195, 159, 131, 173, 142, 117, 153, 127, 104,
-    132, 110, 90,  113, 93,  76,  93,  76,  61,  74,  59,  45,  49,  36,  23,
-    9,   0,   0,   196, 158, 113, 174, 141, 101, 155, 126, 89,  133, 109, 76,
-    113, 93,  64,  94,  76,  51,  74,  58,  35,  49,  36,  14,  6,   0,   0,
-    197, 157, 92,  174, 141, 80,  154, 125, 71,  134, 108, 60,  114, 92,  50,
-    94,  75,  37,  73,  58,  22,  48,  36,  1,   5,   0,   0,   197, 157, 68,
-    175, 140, 59,  155, 124, 51,  134, 108, 41,  113, 91,  32,  94,  75,  21,
-    73,  57,  5,   48,  35,  0,   5,   0,   0,   198, 156, 46,  176, 140, 40,
-    155, 124, 32,  134, 107, 24,  114, 91,  14,  94,  75,  2,   73,  57,  0,
-    48,  36,  0,   3,   0,   0,   189, 140, 191, 168, 126, 172, 150, 112, 154,
-    131, 97,  134, 112, 82,  115, 94,  66,  96,  74,  49,  75,  51,  25,  50,
-    12,  0,   10,  191, 139, 173, 170, 125, 154, 152, 111, 138, 132, 96,  121,
-    113, 81,  103, 94,  66,  85,  74,  48,  66,  50,  26,  42,  12,  0,   1,
-    192, 139, 157, 171, 125, 140, 152, 111, 125, 132, 96,  109, 113, 81,  92,
-    94,  65,  76,  74,  48,  58,  50,  26,  35,  9,   0,   0,   193, 139, 140,
-    172, 124, 125, 153, 110, 112, 133, 95,  96,  113, 80,  82,  94,  65,  66,
-    74,  49,  50,  50,  26,  28,  7,   0,   0,   194, 138, 123, 172, 123, 109,
-    153, 110, 97,  133, 95,  84,  113, 80,  70,  94,  65,  56,  74,  48,  40,
-    50,  26,  20,  6,   0,   0,   194, 138, 105, 173, 123, 94,  153, 109, 83,
-    133, 94,  70,  112, 79,  59,  94,  64,  46,  74,  48,  31,  50,  26,  9,
-    4,   0,   0,   196, 138, 87,  174, 122, 77,  153, 109, 67,  133, 93,  56,
-    113, 79,  46,  94,  64,  34,  73,  48,  18,  49,  27,  0,   4,   0,   0,
-    196, 137, 65,  174, 122, 57,  154, 108, 49,  133, 93,  39,  113, 79,  29,
-    94,  64,  18,  74,  48,  3,   49,  27,  0,   2,   0,   0,   197, 137, 47,
-    175, 122, 40,  155, 108, 32,  133, 93,  23,  114, 79,  14,  94,  64,  1,
-    73,  48,  0,   48,  27,  0,   2,   0,   0,   189, 119, 177, 168, 106, 159,
-    150, 94,  142, 131, 81,  124, 113, 67,  107, 94,  53,  89,  74,  37,  69,
-    51,  11,  45,  6,   0,   3,   191, 119, 161, 170, 106, 144, 152, 94,  129,
-    132, 81,  112, 113, 67,  96,  94,  53,  79,  74,  37,  61,  51,  13,  38,
-    6,   0,   0,   192, 119, 146, 170, 106, 131, 152, 94,  117, 132, 80,  101,
-    112, 67,  85,  94,  53,  70,  74,  37,  53,  50,  14,  31,  4,   0,   0,
-    192, 119, 131, 171, 106, 117, 153, 94,  105, 132, 80,  89,  113, 67,  75,
-    94,  54,  61,  74,  38,  45,  51,  14,  23,  3,   0,   0,   193, 118, 114,
-    171, 106, 102, 153, 93,  90,  132, 80,  78,  113, 67,  65,  94,  53,  52,
-    74,  37,  36,  50,  15,  16,  1,   0,   0,   194, 118, 99,  172, 105, 89,
-    153, 93,  78,  132, 80,  66,  113, 67,  54,  94,  53,  42,  74,  38,  27,
-    50,  16,  5,   1,   0,   0,   194, 118, 82,  173, 105, 72,  153, 93,  63,
-    132, 79,  53,  113, 67,  42,  94,  53,  30,  74,  38,  15,  49,  16,  0,
-    0,   0,   0,   195, 117, 63,  173, 105, 55,  154, 93,  47,  133, 79,  37,
-    113, 66,  27,  94,  53,  15,  73,  38,  0,   48,  16,  0,   0,   0,   0,
-    195, 117, 46,  173, 104, 39,  154, 92,  32,  133, 79,  22,  113, 66,  13,
-    94,  53,  0,   73,  38,  0,   48,  17,  0,   0,   0,   0,   189, 96,  166,
-    168, 85,  147, 150, 74,  132, 131, 62,  115, 113, 51,  99,  94,  38,  82,
-    74,  21,  63,  51,  0,   40,  1,   0,   0,   190, 96,  150, 170, 85,  133,
-    152, 75,  119, 132, 63,  104, 113, 51,  88,  94,  38,  72,  75,  22,  55,
-    51,  0,   33,  1,   0,   0,   192, 96,  137, 170, 85,  121, 152, 74,  108,
-    132, 64,  94,  113, 52,  79,  94,  39,  64,  74,  23,  48,  50,  0,   26,
-    0,   0,   0,   192, 96,  122, 171, 86,  109, 152, 75,  96,  132, 63,  83,
-    113, 52,  69,  94,  39,  56,  74,  24,  41,  50,  0,   19,  0,   0,   0,
-    193, 96,  107, 171, 85,  96,  152, 75,  84,  132, 64,  72,  113, 52,  60,
-    94,  39,  47,  74,  24,  32,  50,  1,   10,  0,   0,   0,   193, 96,  93,
-    172, 85,  82,  152, 75,  72,  133, 63,  61,  113, 51,  49,  94,  39,  37,
-    73,  25,  23,  49,  2,   2,   0,   0,   0,   194, 96,  78,  172, 85,  68,
-    152, 75,  59,  132, 63,  49,  113, 52,  39,  94,  40,  26,  73,  25,  11,
-    48,  3,   0,   0,   0,   0,   194, 96,  60,  173, 85,  52,  153, 75,  44,
-    132, 64,  35,  112, 52,  25,  94,  40,  12,  73,  26,  0,   48,  4,   0,
-    0,   0,   0,   195, 96,  46,  173, 85,  38,  154, 74,  31,  133, 63,  22,
-    113, 52,  11,  93,  40,  0,   73,  26,  0,   47,  5,   0,   0,   0,   0,
-    188, 67,  153, 168, 58,  137, 151, 49,  122, 131, 39,  106, 113, 28,  90,
-    94,  13,  75,  75,  0,   57,  51,  0,   35,  0,   0,   0,   190, 68,  138,
-    170, 59,  123, 152, 50,  110, 132, 41,  96,  113, 29,  80,  94,  16,  66,
-    75,  0,   49,  50,  0,   27,  0,   0,   0,   191, 69,  126, 170, 59,  112,
-    151, 52,  100, 132, 42,  86,  113, 30,  73,  95,  17,  58,  75,  0,   42,
-    50,  0,   21,  0,   0,   0,   192, 70,  113, 170, 61,  100, 151, 52,  89,
-    132, 42,  77,  113, 31,  64,  94,  19,  50,  74,  1,   35,  50,  0,   14,
-    0,   0,   0,   192, 70,  100, 170, 62,  89,  151, 53,  77,  131, 43,  66,
-    112, 32,  54,  94,  20,  42,  74,  2,   27,  49,  0,   5,   0,   0,   0,
-    192, 71,  87,  171, 61,  77,  152, 53,  67,  131, 44,  57,  112, 33,  45,
-    94,  21,  33,  74,  4,   19,  49,  0,   1,   0,   0,   0,   193, 71,  74,
-    171, 62,  64,  152, 53,  55,  132, 44,  45,  113, 34,  34,  94,  22,  23,
-    73,  5,   7,   48,  0,   0,   0,   0,   0,   193, 70,  58,  172, 62,  50,
-    152, 54,  42,  132, 44,  32,  112, 35,  22,  93,  23,  10,  73,  6,   0,
-    47,  0,   0,   0,   0,   0,   193, 70,  45,  172, 62,  38,  153, 54,  31,
-    132, 44,  21,  112, 35,  9,   94,  23,  0,   73,  7,   0,   47,  0,   0,
-    0,   0,   0,   189, 26,  141, 169, 15,  126, 150, 2,   112, 131, 0,   97,
-    113, 0,   82,  94,  0,   67,  75,  0,   51,  50,  0,   29,  0,   0,   0,
-    190, 28,  128, 170, 18,  114, 151, 8,   101, 132, 0,   88,  113, 0,   74,
-    94,  0,   60,  75,  0,   44,  50,  0,   23,  0,   0,   0,   191, 30,  117,
-    170, 23,  104, 152, 11,  92,  132, 1,   79,  113, 0,   67,  95,  0,   53,
-    75,  0,   37,  50,  0,   17,  0,   0,   0,   191, 33,  105, 170, 26,  93,
-    151, 18,  83,  132, 6,   70,  112, 0,   58,  94,  0,   45,  75,  0,   30,
-    49,  0,   8,   0,   0,   0,   191, 34,  93,  170, 27,  82,  151, 20,  72,
-    131, 8,   61,  112, 0,   49,  94,  0,   38,  74,  0,   23,  49,  0,   4,
-    0,   0,   0,   191, 36,  82,  170, 29,  71,  151, 22,  63,  131, 11,  52,
-    112, 0,   41,  93,  0,   29,  74,  0,   14,  48,  0,   1,   0,   0,   0,
-    191, 38,  69,  170, 31,  60,  151, 24,  51,  131, 14,  41,  112, 1,   31,
-    93,  0,   19,  73,  0,   3,   48,  0,   0,   0,   0,   0,   192, 37,  56,
-    171, 31,  47,  152, 25,  40,  131, 17,  30,  112, 4,   19,  93,  0,   7,
-    73,  0,   0,   47,  0,   0,   0,   0,   0,   192, 38,  45,  171, 33,  36,
-    152, 26,  30,  131, 18,  21,  111, 7,   9,   93,  0,   0,   73,  0,   0,
-    47,  0,   0,   0,   0,   0,   149, 218, 248, 133, 194, 222, 119, 173, 198,
-    102, 151, 173, 86,  130, 148, 70,  108, 125, 53,  85,  100, 32,  59,  71,
-    0,   22,  33,  154, 216, 223, 137, 193, 200, 122, 172, 178, 106, 150, 156,
-    89,  128, 133, 73,  107, 112, 54,  85,  89,  31,  59,  63,  0,   22,  26,
-    159, 215, 202, 141, 192, 181, 126, 171, 161, 108, 149, 141, 90,  128, 121,
-    74,  107, 100, 55,  85,  80,  32,  58,  55,  0,   22,  19,  161, 213, 179,
-    144, 190, 160, 126, 170, 143, 109, 148, 125, 92,  127, 107, 74,  106, 89,
-    56,  84,  69,  32,  58,  46,  0,   21,  11,  163, 211, 156, 144, 189, 139,
-    129, 168, 125, 110, 147, 108, 93,  126, 92,  75,  105, 76,  57,  83,  58,
-    33,  58,  37,  0,   21,  1,   167, 211, 133, 147, 188, 120, 130, 167, 105,
-    110, 145, 92,  93,  125, 78,  76,  104, 64,  58,  83,  48,  33,  57,  27,
-    0,   21,  0,   169, 210, 108, 149, 187, 96,  131, 166, 86,  112, 144, 74,
-    94,  124, 62,  77,  103, 49,  58,  82,  35,  33,  57,  15,  0,   21,  0,
-    170, 209, 80,  151, 186, 71,  133, 165, 62,  114, 143, 52,  95,  123, 42,
-    77,  103, 32,  58,  81,  18,  33,  56,  0,   0,   21,  0,   173, 208, 55,
-    152, 186, 49,  134, 165, 41,  114, 143, 34,  95,  122, 25,  77,  102, 14,
-    58,  81,  0,   33,  56,  0,   0,   21,  0,   154, 195, 232, 137, 174, 207,
-    122, 156, 185, 105, 136, 163, 89,  116, 140, 73,  97,  117, 56,  76,  94,
-    35,  51,  66,  0,   13,  28,  158, 194, 209, 141, 174, 187, 125, 155, 167,
-    109, 135, 146, 91,  116, 125, 75,  96,  105, 57,  75,  83,  35,  50,  57,
-    0,   12,  21,  161, 193, 189, 144, 173, 169, 128, 154, 151, 110, 134, 132,
-    93,  115, 113, 77,  95,  94,  58,  75,  74,  35,  50,  50,  0,   12,  13,
-    164, 192, 168, 145, 171, 151, 129, 153, 134, 111, 133, 117, 94,  114, 100,
-    76,  95,  82,  58,  75,  64,  36,  50,  42,  0,   12,  5,   165, 191, 147,
-    147, 170, 131, 130, 152, 117, 113, 132, 102, 95,  113, 86,  77,  94,  71,
-    58,  74,  54,  35,  50,  33,  0,   13,  0,   167, 189, 126, 148, 169, 113,
-    132, 151, 100, 113, 131, 86,  96,  112, 73,  77,  93,  59,  59,  73,  43,
-    35,  49,  23,  0,   12,  0,   170, 189, 104, 150, 168, 91,  133, 150, 81,
-    114, 130, 69,  96,  111, 57,  78,  92,  46,  59,  73,  31,  35,  49,  11,
-    0,   13,  0,   171, 188, 78,  152, 168, 68,  134, 149, 60,  115, 130, 50,
-    96,  111, 41,  78,  92,  29,  60,  73,  15,  35,  49,  0,   0,   12,  0,
-    173, 187, 55,  153, 167, 47,  134, 149, 39,  115, 129, 33,  97,  110, 24,
-    79,  92,  13,  60,  72,  0,   35,  48,  0,   0,   12,  0,   157, 175, 217,
-    139, 157, 196, 125, 141, 175, 109, 122, 153, 92,  104, 132, 76,  86,  110,
-    59,  67,  88,  37,  43,  61,  1,   1,   23,  161, 174, 196, 144, 156, 176,
-    127, 140, 158, 110, 121, 137, 94,  104, 118, 77,  85,  98,  59,  67,  78,
-    37,  43,  53,  0,   2,   16,  163, 174, 178, 146, 156, 160, 130, 139, 143,
-    112, 121, 124, 95,  103, 106, 78,  85,  88,  60,  66,  69,  37,  42,  46,
-    0,   2,   7,   166, 173, 159, 147, 154, 142, 130, 138, 127, 113, 120, 111,
-    96,  103, 95,  78,  84,  77,  60,  66,  59,  37,  43,  37,  0,   2,   0,
-    166, 172, 139, 148, 154, 125, 131, 137, 112, 113, 120, 96,  96,  102, 81,
-    78,  84,  66,  60,  65,  50,  37,  42,  29,  0,   3,   0,   167, 171, 120,
-    149, 153, 107, 133, 137, 95,  114, 118, 81,  97,  101, 69,  79,  84,  56,
-    60,  65,  40,  37,  42,  19,  0,   3,   0,   170, 170, 99,  151, 152, 87,
-    134, 136, 77,  115, 118, 66,  97,  101, 55,  79,  83,  42,  61,  65,  28,
-    37,  42,  7,   0,   3,   0,   172, 170, 75,  152, 151, 65,  134, 135, 57,
-    115, 117, 48,  97,  100, 38,  79,  83,  27,  61,  64,  12,  36,  42,  0,
-    0,   3,   0,   172, 169, 55,  154, 151, 46,  135, 134, 40,  116, 116, 32,
-    97,  99,  21,  80,  82,  10,  61,  64,  0,   36,  41,  0,   0,   3,   0,
-    160, 154, 203, 143, 139, 182, 127, 124, 164, 111, 107, 143, 95,  91,  122,
-    78,  75,  103, 60,  57,  81,  39,  33,  56,  1,   0,   18,  163, 154, 184,
-    146, 138, 165, 130, 123, 148, 113, 107, 129, 96,  90,  110, 79,  74,  92,
-    61,  56,  72,  39,  34,  48,  2,   0,   9,   165, 154, 167, 147, 137, 149,
-    131, 122, 134, 114, 106, 117, 96,  90,  100, 79,  74,  82,  61,  56,  64,
-    39,  33,  40,  2,   0,   1,   166, 153, 150, 149, 137, 133, 132, 122, 119,
-    114, 106, 104, 97,  90,  88,  79,  74,  72,  61,  56,  55,  39,  34,  33,
-    0,   0,   0,   168, 152, 132, 149, 136, 117, 132, 121, 104, 114, 105, 90,
-    97,  89,  76,  79,  73,  62,  61,  56,  46,  38,  34,  25,  0,   0,   0,
-    169, 151, 114, 150, 135, 101, 133, 121, 90,  114, 104, 77,  97,  89,  65,
-    80,  73,  51,  61,  56,  36,  38,  34,  16,  0,   0,   0,   170, 150, 94,
-    151, 135, 83,  134, 120, 73,  115, 104, 62,  98,  88,  51,  80,  72,  39,
-    61,  56,  24,  38,  34,  3,   0,   0,   0,   172, 150, 72,  153, 134, 63,
-    135, 119, 55,  115, 103, 45,  98,  88,  36,  80,  72,  24,  61,  55,  9,
-    38,  34,  0,   0,   0,   0,   172, 150, 54,  153, 134, 47,  135, 119, 38,
-    116, 103, 30,  98,  87,  21,  80,  72,  8,   62,  55,  0,   37,  34,  0,
-    0,   0,   0,   162, 134, 190, 145, 120, 171, 129, 108, 153, 113, 93,  134,
-    97,  78,  115, 80,  63,  96,  62,  46,  75,  41,  23,  51,  0,   0,   11,
-    165, 134, 173, 147, 120, 154, 131, 107, 138, 114, 92,  120, 97,  78,  103,
-    80,  63,  85,  62,  46,  66,  40,  23,  43,  0,   0,   2,   166, 134, 157,
-    148, 120, 140, 132, 106, 125, 114, 92,  109, 97,  77,  93,  81,  63,  77,
-    62,  46,  58,  40,  24,  36,  0,   0,   0,   168, 133, 140, 149, 119, 125,
-    132, 106, 112, 115, 92,  97,  98,  77,  82,  81,  62,  67,  62,  46,  50,
-    40,  24,  29,  0,   0,   0,   168, 133, 123, 150, 119, 110, 133, 106, 97,
-    115, 91,  84,  98,  77,  70,  81,  62,  57,  62,  46,  41,  40,  24,  20,
-    0,   0,   0,   169, 132, 107, 150, 118, 94,  133, 105, 84,  115, 91,  72,
-    98,  76,  60,  80,  62,  47,  62,  46,  32,  39,  25,  11,  0,   0,   0,
-    171, 132, 89,  152, 118, 79,  135, 105, 69,  115, 90,  58,  98,  76,  47,
-    80,  62,  36,  62,  46,  21,  39,  25,  0,   0,   0,   0,   171, 132, 69,
-    153, 117, 60,  135, 104, 52,  116, 90,  42,  98,  76,  33,  81,  61,  21,
-    62,  46,  6,   38,  25,  0,   0,   0,   0,   172, 132, 54,  153, 118, 45,
-    135, 104, 38,  116, 90,  28,  98,  76,  18,  81,  61,  6,   62,  46,  0,
-    38,  25,  0,   0,   0,   0,   164, 115, 177, 146, 103, 159, 130, 91,  143,
-    114, 78,  125, 97,  65,  107, 81,  51,  89,  63,  34,  69,  41,  9,   46,
-    0,   0,   4,   166, 115, 161, 148, 103, 144, 132, 91,  129, 115, 78,  112,
-    98,  65,  96,  81,  51,  79,  63,  35,  61,  41,  11,  38,  0,   0,   0,
-    167, 115, 146, 150, 102, 131, 132, 91,  117, 115, 78,  101, 98,  65,  86,
-    81,  51,  71,  63,  35,  54,  41,  12,  32,  0,   0,   0,   168, 114, 132,
-    150, 103, 118, 133, 91,  105, 116, 78,  91,  98,  64,  76,  82,  51,  61,
-    63,  36,  46,  41,  13,  24,  0,   0,   0,   169, 114, 116, 150, 102, 103,
-    134, 90,  91,  116, 78,  79,  98,  65,  66,  81,  51,  53,  63,  36,  37,
-    40,  14,  17,  0,   0,   0,   169, 114, 101, 151, 101, 89,  134, 90,  79,
-    116, 77,  67,  98,  64,  56,  81,  51,  44,  63,  36,  29,  40,  15,  7,
-    0,   0,   0,   170, 114, 85,  152, 101, 75,  135, 90,  65,  116, 77,  54,
-    98,  64,  44,  81,  51,  32,  63,  36,  17,  39,  15,  0,   0,   0,   0,
-    172, 113, 66,  152, 101, 58,  135, 89,  49,  116, 77,  40,  99,  64,  30,
-    81,  51,  18,  62,  36,  3,   38,  16,  0,   0,   0,   0,   171, 113, 51,
-    153, 101, 44,  136, 89,  36,  116, 77,  28,  99,  64,  18,  81,  51,  5,
-    62,  36,  0,   38,  16,  0,   0,   0,   0,   165, 94,  166, 147, 82,  147,
-    132, 72,  132, 115, 61,  115, 98,  49,  99,  82,  36,  82,  64,  19,  64,
-    42,  0,   41,  0,   0,   0,   167, 93,  150, 150, 83,  134, 133, 73,  120,
-    116, 62,  104, 99,  49,  88,  82,  36,  72,  64,  20,  55,  41,  0,   33,
-    0,   0,   0,   169, 93,  137, 150, 83,  122, 134, 73,  109, 116, 61,  94,
-    99,  50,  80,  82,  37,  65,  64,  21,  49,  41,  0,   27,  0,   0,   0,
-    169, 94,  123, 150, 83,  110, 133, 73,  97,  116, 61,  83,  99,  50,  70,
-    82,  38,  57,  63,  23,  42,  41,  0,   20,  0,   0,   0,   169, 94,  109,
-    150, 84,  97,  134, 73,  85,  116, 62,  73,  99,  51,  61,  81,  38,  48,
-    63,  23,  33,  41,  1,   11,  0,   0,   0,   170, 94,  96,  150, 83,  84,
-    134, 73,  74,  116, 61,  62,  99,  50,  51,  82,  38,  39,  64,  23,  24,
-    40,  3,   4,   0,   0,   0,   171, 93,  79,  152, 82,  70,  135, 73,  61,
-    116, 62,  51,  98,  51,  40,  81,  38,  28,  63,  24,  14,  39,  4,   0,
-    0,   0,   0,   171, 94,  64,  152, 83,  55,  135, 73,  47,  116, 62,  37,
-    98,  50,  27,  81,  38,  15,  63,  24,  1,   39,  4,   0,   0,   0,   0,
-    172, 93,  51,  153, 82,  42,  135, 73,  35,  117, 62,  26,  99,  51,  16,
-    81,  39,  3,   63,  25,  0,   38,  5,   0,   0,   0,   0,   166, 68,  153,
-    148, 59,  137, 133, 49,  121, 115, 39,  106, 99,  28,  91,  82,  13,  75,
-    65,  0,   58,  42,  0,   36,  0,   0,   0,   168, 68,  139, 150, 59,  124,
-    134, 50,  110, 116, 40,  96,  99,  30,  81,  82,  16,  66,  64,  0,   50,
-    41,  0,   29,  0,   0,   0,   169, 69,  126, 150, 59,  113, 134, 51,  101,
-    117, 42,  87,  100, 30,  73,  82,  17,  59,  65,  0,   43,  41,  0,   23,
-    0,   0,   0,   169, 70,  115, 150, 61,  102, 134, 52,  89,  116, 42,  77,
-    99,  32,  65,  82,  19,  52,  64,  0,   36,  41,  0,   15,  0,   0,   0,
-    169, 70,  101, 150, 61,  90,  134, 52,  79,  116, 43,  68,  99,  32,  55,
-    82,  21,  43,  64,  2,   28,  41,  0,   6,   0,   0,   0,   170, 70,  89,
-    151, 62,  79,  134, 53,  69,  116, 44,  58,  99,  33,  46,  81,  21,  34,
-    64,  3,   20,  41,  0,   2,   0,   0,   0,   170, 71,  76,  152, 62,  66,
-    134, 53,  57,  116, 43,  46,  99,  33,  36,  82,  22,  24,  64,  5,   10,
-    40,  0,   0,   0,   0,   0,   171, 70,  61,  152, 62,  52,  135, 53,  44,
-    116, 44,  35,  99,  34,  24,  82,  22,  12,  63,  6,   0,   39,  0,   0,
-    0,   0,   0,   171, 71,  49,  153, 62,  41,  135, 54,  33,  117, 45,  25,
-    98,  34,  13,  81,  23,  0,   63,  7,   0,   39,  0,   0,   0,   0,   0,
-    167, 33,  142, 149, 24,  127, 134, 10,  113, 116, 0,   97,  100, 0,   83,
-    83,  0,   68,  65,  0,   52,  40,  0,   30,  0,   0,   0,   169, 33,  129,
-    150, 26,  115, 134, 17,  102, 116, 3,   89,  100, 0,   75,  83,  0,   60,
-    65,  0,   45,  40,  0,   24,  0,   0,   0,   169, 36,  118, 151, 27,  104,
-    134, 19,  93,  116, 7,   80,  100, 0,   67,  83,  0,   54,  65,  0,   38,
-    41,  0,   17,  0,   0,   0,   169, 39,  107, 150, 30,  94,  134, 22,  84,
-    116, 11,  71,  99,  0,   59,  83,  0,   46,  64,  0,   31,  40,  0,   9,
-    0,   0,   0,   169, 39,  95,  151, 31,  83,  134, 24,  73,  116, 15,  62,
-    100, 1,   51,  83,  0,   38,  64,  0,   24,  40,  0,   5,   0,   0,   0,
-    169, 41,  83,  151, 33,  73,  134, 26,  64,  117, 17,  54,  99,  4,   42,
-    82,  0,   30,  64,  0,   16,  40,  0,   1,   0,   0,   0,   170, 42,  71,
-    152, 34,  62,  134, 28,  53,  117, 19,  44,  99,  6,   33,  82,  0,   21,
-    63,  0,   4,   39,  0,   0,   0,   0,   0,   171, 42,  59,  152, 35,  50,
-    134, 29,  42,  117, 21,  32,  99,  9,   22,  82,  0,   9,   63,  0,   0,
-    38,  0,   0,   0,   0,   0,   172, 42,  48,  152, 36,  40,  135, 29,  32,
-    117, 21,  23,  99,  10,  12,  82,  0,   0,   63,  0,   0,   38,  0,   0,
-    0,   0,   0,   107, 207, 246, 96,  185, 220, 86,  165, 196, 73,  144, 171,
-    60,  123, 147, 46,  103, 125, 32,  82,  100, 9,   56,  71,  0,   20,  33,
-    115, 206, 221, 104, 184, 198, 92,  164, 178, 78,  143, 154, 64,  123, 133,
-    51,  102, 111, 34,  81,  89,  10,  56,  63,  0,   20,  27,  122, 204, 200,
-    108, 183, 180, 95,  163, 161, 82,  142, 140, 68,  122, 120, 54,  102, 101,
-    36,  81,  79,  11,  56,  55,  0,   20,  20,  125, 203, 179, 111, 181, 160,
-    97,  162, 143, 85,  141, 124, 70,  121, 107, 55,  101, 89,  38,  80,  69,
-    14,  55,  46,  0,   19,  10,  128, 202, 156, 113, 180, 140, 102, 161, 125,
-    87,  140, 108, 71,  120, 92,  56,  100, 76,  39,  79,  59,  14,  55,  38,
-    0,   20,  3,   132, 200, 135, 117, 179, 121, 103, 159, 106, 88,  139, 93,
-    73,  119, 79,  57,  100, 65,  41,  79,  49,  15,  54,  28,  0,   19,  0,
-    134, 200, 111, 119, 178, 98,  105, 158, 87,  89,  138, 76,  74,  118, 64,
-    58,  99,  51,  41,  78,  37,  16,  54,  17,  0,   19,  0,   137, 199, 85,
-    122, 177, 75,  108, 158, 66,  91,  137, 56,  75,  118, 46,  59,  98,  35,
-    42,  78,  22,  16,  54,  3,   0,   19,  0,   140, 198, 62,  125, 177, 55,
-    109, 158, 47,  92,  137, 40,  76,  117, 32,  59,  98,  21,  42,  78,  6,
-    16,  54,  0,   0,   18,  0,   118, 186, 231, 106, 167, 206, 93,  149, 184,
-    81,  130, 161, 67,  111, 139, 54,  92,  117, 39,  72,  93,  17,  48,  66,
-    0,   10,  29,  123, 185, 207, 110, 166, 186, 98,  148, 167, 85,  129, 145,
-    71,  111, 125, 56,  92,  104, 40,  72,  83,  18,  48,  57,  0,   10,  22,
-    128, 184, 188, 113, 165, 168, 102, 147, 151, 88,  128, 131, 73,  110, 113,
-    58,  91,  94,  42,  71,  74,  19,  48,  50,  0,   9,   15,  131, 183, 168,
-    116, 164, 151, 104, 146, 134, 89,  127, 117, 73,  109, 100, 58,  90,  83,
-    42,  71,  65,  20,  48,  42,  0,   9,   5,   134, 182, 148, 120, 163, 131,
-    105, 145, 118, 90,  126, 102, 75,  108, 86,  59,  90,  72,  43,  71,  55,
-    19,  47,  34,  0,   9,   0,   136, 181, 128, 122, 162, 115, 107, 144, 102,
-    92,  125, 87,  76,  107, 74,  61,  89,  60,  44,  70,  45,  20,  47,  24,
-    0,   8,   0,   139, 180, 106, 124, 161, 95,  109, 144, 83,  93,  124, 71,
-    77,  107, 60,  61,  89,  47,  44,  70,  33,  20,  47,  13,  0,   8,   0,
-    142, 179, 82,  125, 160, 72,  111, 143, 63,  94,  124, 54,  77,  106, 44,
-    61,  88,  32,  44,  69,  18,  20,  46,  0,   0,   8,   0,   143, 179, 62,
-    127, 160, 54,  111, 142, 47,  94,  124, 39,  78,  106, 29,  62,  88,  18,
-    45,  69,  3,   20,  46,  0,   0,   8,   0,   124, 167, 216, 112, 150, 194,
-    99,  134, 174, 87,  117, 153, 73,  100, 131, 58,  82,  110, 43,  64,  88,
-    23,  40,  61,  0,   0,   24,  129, 166, 195, 116, 150, 175, 103, 134, 158,
-    89,  116, 137, 75,  99,  118, 60,  82,  98,  44,  63,  78,  23,  40,  53,
-    0,   0,   17,  132, 166, 177, 119, 149, 160, 106, 133, 143, 90,  115, 124,
-    76,  99,  107, 61,  81,  88,  45,  63,  69,  24,  40,  46,  0,   0,   9,
-    136, 166, 159, 121, 148, 143, 107, 132, 126, 92,  115, 111, 77,  98,  94,
-    62,  81,  78,  46,  63,  60,  23,  40,  38,  0,   0,   0,   138, 164, 140,
-    122, 147, 125, 108, 131, 111, 93,  114, 97,  79,  98,  82,  63,  80,  67,
-    46,  62,  50,  24,  40,  29,  0,   0,   0,   139, 163, 122, 124, 146, 109,
-    110, 131, 96,  94,  114, 83,  79,  97,  70,  63,  81,  57,  46,  62,  41,
-    24,  40,  21,  0,   0,   0,   141, 163, 101, 126, 145, 90,  111, 130, 79,
-    95,  113, 68,  79,  96,  56,  63,  80,  44,  47,  62,  30,  23,  40,  10,
-    0,   0,   0,   144, 162, 79,  127, 145, 70,  112, 129, 60,  95,  112, 51,
-    79,  96,  41,  64,  79,  30,  47,  61,  15,  23,  40,  0,   0,   0,   0,
-    145, 162, 60,  129, 145, 52,  113, 129, 46,  96,  112, 37,  79,  95,  27,
-    64,  79,  16,  47,  61,  1,   23,  39,  0,   0,   0,   0,   131, 147, 202,
-    117, 133, 181, 105, 119, 162, 91,  103, 142, 77,  87,  122, 62,  71,  102,
-    47,  54,  81,  26,  31,  56,  0,   0,   18,  135, 147, 183, 120, 132, 164,
-    107, 118, 147, 93,  102, 128, 78,  87,  110, 63,  71,  92,  47,  54,  72,
-    26,  31,  48,  0,   0,   10,  138, 147, 166, 123, 131, 149, 108, 118, 133,
-    94,  102, 116, 79,  86,  100, 64,  71,  82,  48,  54,  64,  27,  31,  41,
-    0,   0,   2,   139, 146, 149, 124, 131, 134, 111, 117, 119, 94,  101, 103,
-    79,  86,  88,  64,  70,  72,  48,  53,  55,  27,  31,  33,  0,   0,   0,
-    141, 146, 132, 125, 131, 117, 111, 117, 104, 95,  101, 91,  80,  86,  77,
-    65,  70,  62,  48,  53,  46,  26,  31,  25,  0,   0,   0,   143, 145, 115,
-    126, 130, 101, 112, 116, 90,  96,  100, 78,  80,  85,  65,  65,  70,  52,
-    49,  53,  37,  27,  32,  17,  0,   0,   0,   144, 144, 96,  128, 129, 85,
-    112, 115, 75,  97,  100, 64,  81,  85,  52,  65,  69,  40,  49,  53,  26,
-    26,  31,  5,   0,   0,   0,   146, 144, 76,  129, 129, 67,  114, 115, 58,
-    97,  99,  48,  82,  84,  38,  66,  69,  27,  49,  53,  12,  26,  32,  0,
-    0,   0,   0,   146, 144, 59,  130, 128, 51,  114, 114, 43,  98,  99,  35,
-    82,  84,  25,  66,  69,  13,  49,  53,  0,   26,  32,  0,   0,   0,   0,
-    135, 129, 189, 122, 115, 170, 107, 103, 152, 94,  89,  133, 79,  74,  114,
-    64,  60,  95,  49,  43,  75,  29,  20,  51,  0,   0,   12,  138, 129, 171,
-    124, 115, 153, 110, 103, 138, 95,  89,  120, 81,  74,  103, 66,  60,  86,
-    50,  44,  67,  28,  21,  43,  0,   0,   3,   140, 129, 156, 125, 115, 140,
-    111, 103, 125, 96,  89,  109, 81,  74,  93,  67,  60,  76,  50,  44,  59,
-    29,  22,  36,  0,   0,   0,   142, 128, 140, 127, 115, 125, 112, 102, 112,
-    97,  88,  97,  82,  74,  83,  67,  60,  67,  50,  44,  51,  29,  22,  29,
-    0,   0,   0,   142, 128, 124, 127, 114, 111, 113, 102, 98,  98,  88,  85,
-    82,  74,  71,  66,  60,  58,  50,  44,  42,  29,  22,  21,  0,   0,   0,
-    144, 127, 108, 128, 114, 96,  113, 101, 85,  98,  87,  73,  82,  74,  61,
-    67,  60,  48,  50,  44,  33,  28,  23,  12,  0,   0,   0,   145, 127, 91,
-    129, 114, 81,  115, 101, 71,  98,  87,  60,  82,  73,  48,  67,  59,  37,
-    50,  44,  22,  29,  23,  1,   0,   0,   0,   147, 127, 73,  130, 113, 63,
-    115, 101, 55,  98,  87,  45,  83,  73,  35,  67,  59,  24,  50,  44,  10,
-    28,  24,  0,   0,   0,   0,   147, 127, 58,  131, 113, 49,  115, 100, 42,
-    99,  86,  33,  83,  73,  23,  67,  59,  10,  50,  44,  0,   27,  24,  0,
-    0,   0,   0,   138, 110, 177, 124, 99,  159, 110, 88,  142, 96,  75,  125,
-    82,  62,  107, 66,  48,  89,  51,  33,  70,  30,  8,   46,  0,   0,   5,
-    142, 111, 160, 127, 99,  144, 113, 88,  130, 98,  75,  112, 82,  62,  96,
-    68,  49,  80,  51,  33,  61,  30,  10,  39,  0,   0,   0,   143, 111, 146,
-    128, 99,  131, 114, 88,  118, 98,  75,  101, 83,  62,  86,  68,  49,  71,
-    52,  33,  54,  30,  11,  32,  0,   0,   0,   144, 111, 132, 128, 99,  118,
-    113, 88,  106, 99,  75,  91,  83,  62,  77,  68,  49,  62,  52,  34,  46,
-    30,  12,  25,  0,   0,   0,   144, 111, 117, 129, 98,  104, 114, 87,  92,
-    99,  75,  80,  83,  62,  67,  68,  49,  53,  51,  34,  38,  30,  13,  18,
-    0,   0,   0,   145, 111, 103, 130, 98,  91,  114, 87,  80,  99,  75,  68,
-    83,  63,  57,  68,  50,  45,  51,  34,  30,  30,  14,  8,   0,   0,   0,
-    146, 110, 87,  131, 98,  76,  115, 87,  67,  99,  75,  56,  83,  62,  45,
-    68,  49,  33,  52,  35,  19,  30,  15,  2,   0,   0,   0,   148, 110, 70,
-    131, 98,  60,  116, 86,  52,  99,  74,  43,  84,  62,  33,  69,  49,  21,
-    52,  35,  6,   29,  15,  0,   0,   0,   0,   148, 110, 56,  132, 97,  48,
-    117, 87,  40,  100, 75,  31,  84,  62,  22,  68,  49,  9,   51,  35,  0,
-    28,  15,  0,   0,   0,   0,   142, 91,  166, 126, 80,  148, 113, 71,  132,
-    98,  59,  115, 83,  47,  99,  69,  34,  82,  53,  17,  64,  32,  0,   41,
-    0,   0,   0,   143, 91,  150, 128, 81,  135, 114, 71,  120, 99,  60,  104,
-    85,  48,  89,  69,  35,  73,  53,  19,  56,  32,  0,   34,  0,   0,   0,
-    145, 91,  137, 129, 81,  122, 115, 71,  109, 100, 60,  94,  85,  48,  81,
-    69,  35,  65,  53,  19,  49,  32,  0,   28,  0,   0,   0,   146, 92,  124,
-    130, 81,  110, 115, 71,  98,  100, 60,  84,  85,  49,  71,  69,  36,  57,
-    53,  21,  42,  32,  0,   21,  0,   0,   0,   147, 91,  110, 130, 81,  97,
-    115, 71,  86,  100, 60,  74,  84,  49,  62,  69,  36,  48,  53,  22,  34,
-    32,  0,   13,  0,   0,   0,   147, 92,  97,  130, 81,  85,  116, 72,  76,
-    100, 60,  63,  85,  49,  52,  69,  37,  40,  53,  22,  26,  31,  1,   5,
-    0,   0,   0,   148, 92,  82,  131, 81,  71,  116, 71,  62,  100, 60,  53,
-    84,  49,  42,  69,  37,  30,  52,  23,  16,  31,  2,   0,   0,   0,   0,
-    148, 91,  67,  132, 81,  57,  117, 71,  49,  100, 60,  39,  84,  49,  30,
-    69,  37,  18,  52,  23,  2,   30,  2,   0,   0,   0,   0,   149, 91,  54,
-    132, 81,  46,  118, 71,  39,  101, 60,  29,  85,  49,  19,  69,  37,  6,
-    52,  23,  0,   29,  3,   0,   0,   0,   0,   143, 68,  153, 128, 59,  137,
-    115, 49,  122, 99,  39,  107, 85,  28,  91,  70,  13,  75,  54,  0,   58,
-    32,  0,   36,  0,   0,   0,   146, 68,  140, 131, 59,  125, 116, 51,  111,
-    100, 40,  97,  85,  29,  82,  70,  15,  67,  54,  0,   50,  32,  0,   29,
-    0,   0,   0,   147, 68,  127, 131, 59,  114, 117, 51,  102, 101, 41,  88,
-    86,  30,  74,  70,  17,  60,  54,  0,   44,  32,  0,   23,  0,   0,   0,
-    147, 70,  115, 131, 60,  103, 116, 52,  91,  100, 42,  78,  85,  32,  65,
-    70,  19,  53,  54,  1,   38,  32,  0,   17,  0,   0,   0,   147, 70,  103,
-    131, 61,  91,  117, 53,  81,  101, 43,  69,  86,  32,  57,  70,  20,  44,
-    54,  2,   30,  32,  0,   7,   0,   0,   0,   148, 70,  91,  132, 61,  80,
-    117, 52,  70,  101, 43,  59,  85,  33,  48,  70,  21,  36,  53,  4,   22,
-    32,  0,   3,   0,   0,   0,   148, 70,  78,  132, 62,  68,  117, 53,  58,
-    101, 43,  48,  85,  34,  38,  70,  22,  26,  53,  6,   12,  31,  0,   0,
-    0,   0,   0,   149, 71,  64,  132, 62,  54,  118, 54,  46,  101, 44,  37,
-    85,  34,  27,  69,  23,  15,  53,  7,   1,   30,  0,   0,   0,   0,   0,
-    150, 70,  53,  134, 61,  44,  118, 54,  36,  101, 44,  28,  85,  35,  17,
-    69,  23,  4,   52,  8,   0,   30,  0,   0,   0,   0,   0,   145, 38,  143,
-    130, 29,  128, 117, 18,  114, 101, 3,   98,  87,  0,   84,  72,  0,   69,
-    54,  0,   53,  30,  0,   31,  0,   0,   0,   147, 38,  130, 132, 30,  116,
-    117, 22,  103, 101, 8,   89,  87,  0,   76,  72,  0,   62,  54,  0,   46,
-    30,  0,   24,  0,   0,   0,   148, 40,  119, 132, 31,  105, 117, 23,  94,
-    101, 13,  81,  87,  0,   68,  71,  0,   55,  54,  0,   39,  30,  0,   18,
-    0,   0,   0,   148, 42,  108, 132, 34,  96,  117, 25,  85,  102, 15,  73,
-    86,  2,   60,  71,  0,   47,  54,  0,   33,  30,  0,   11,  0,   0,   0,
-    148, 43,  96,  133, 35,  85,  117, 28,  75,  102, 18,  64,  87,  5,   52,
-    71,  0,   40,  54,  0,   25,  30,  0,   5,   0,   0,   0,   149, 44,  85,
-    132, 36,  75,  118, 29,  66,  101, 20,  55,  86,  8,   44,  70,  0,   32,
-    53,  0,   18,  29,  0,   2,   0,   0,   0,   149, 45,  74,  133, 37,  64,
-    118, 31,  55,  102, 21,  45,  85,  10,  34,  70,  0,   22,  53,  0,   6,
-    28,  0,   0,   0,   0,   0,   150, 46,  61,  133, 39,  52,  118, 31,  44,
-    102, 23,  34,  85,  12,  24,  70,  0,   12,  52,  0,   0,   28,  0,   0,
-    0,   0,   0,   150, 46,  51,  133, 40,  42,  119, 32,  35,  102, 24,  25,
-    85,  13,  14,  70,  0,   1,   52,  0,   0,   27,  0,   0,   0,   0,   0,
-    53,  198, 244, 49,  177, 218, 41,  158, 195, 32,  138, 171, 22,  118, 147,
-    11,  98,  124, 0,   78,  100, 0,   54,  71,  0,   18,  34,  69,  196, 220,
-    64,  175, 196, 54,  157, 176, 45,  137, 154, 32,  117, 133, 19,  98,  111,
-    0,   78,  89,  0,   53,  63,  0,   17,  27,  80,  195, 198, 69,  175, 179,
-    60,  156, 159, 50,  136, 139, 38,  116, 120, 25,  98,  101, 4,   77,  80,
-    0,   53,  55,  0,   17,  21,  84,  193, 177, 75,  173, 159, 64,  155, 142,
-    55,  135, 124, 41,  116, 107, 27,  97,  89,  9,   76,  70,  0,   53,  47,
-    0,   17,  11,  89,  193, 157, 79,  172, 140, 70,  154, 125, 57,  134, 109,
-    44,  115, 92,  32,  96,  76,  13,  76,  59,  0,   52,  39,  0,   16,  4,
-    94,  191, 135, 85,  171, 121, 72,  152, 108, 60,  133, 94,  47,  114, 80,
-    32,  95,  65,  15,  76,  49,  0,   52,  29,  0,   16,  0,   98,  190, 113,
-    87,  170, 100, 76,  152, 89,  62,  132, 77,  49,  113, 65,  35,  95,  52,
-    18,  75,  37,  0,   52,  18,  0,   15,  0,   103, 190, 89,  90,  169, 80,
-    78,  151, 70,  64,  132, 60,  51,  113, 49,  37,  94,  38,  20,  75,  25,
-    0,   52,  5,   0,   15,  0,   106, 189, 69,  93,  169, 61,  80,  151, 53,
-    66,  131, 45,  52,  113, 36,  37,  94,  25,  19,  74,  11,  0,   51,  0,
-    0,   15,  0,   76,  178, 229, 68,  159, 205, 61,  142, 183, 50,  124, 160,
-    40,  106, 138, 28,  88,  116, 12,  69,  93,  0,   45,  66,  0,   5,   29,
-    86,  177, 207, 78,  158, 184, 67,  142, 166, 56,  123, 145, 45,  106, 125,
-    31,  88,  105, 16,  69,  83,  0,   45,  58,  0,   6,   22,  93,  176, 187,
-    81,  158, 168, 71,  141, 150, 61,  123, 131, 47,  105, 113, 35,  87,  94,
-    20,  68,  74,  0,   45,  51,  0,   5,   16,  98,  175, 168, 84,  157, 150,
-    75,  140, 134, 63,  122, 117, 50,  104, 100, 37,  87,  83,  21,  68,  65,
-    0,   45,  42,  0,   4,   7,   100, 174, 149, 89,  155, 132, 76,  139, 117,
-    65,  121, 102, 53,  104, 87,  39,  86,  72,  23,  67,  55,  0,   45,  34,
-    0,   3,   0,   103, 173, 130, 92,  155, 115, 80,  138, 102, 68,  120, 88,
-    53,  103, 75,  40,  86,  61,  24,  67,  45,  0,   45,  25,  0,   3,   0,
-    107, 172, 108, 95,  154, 96,  82,  137, 85,  70,  119, 73,  55,  102, 61,
-    42,  85,  49,  25,  67,  34,  0,   45,  14,  0,   3,   0,   110, 172, 86,
-    97,  153, 76,  85,  137, 67,  70,  119, 57,  56,  102, 46,  42,  84,  35,
-    26,  66,  21,  0,   44,  1,   0,   3,   0,   112, 171, 67,  98,  153, 59,
-    86,  137, 52,  71,  119, 44,  58,  102, 34,  44,  85,  22,  27,  66,  7,
-    0,   44,  0,   0,   3,   0,   90,  160, 215, 81,  144, 193, 70,  129, 173,
-    61,  112, 151, 49,  95,  131, 37,  79,  109, 22,  61,  87,  0,   38,  61,
-    0,   0,   25,  96,  160, 194, 86,  143, 174, 75,  128, 157, 65,  112, 137,
-    53,  95,  117, 40,  78,  98,  25,  60,  78,  0,   38,  53,  0,   0,   17,
-    100, 159, 177, 89,  143, 159, 79,  128, 143, 67,  111, 124, 55,  95,  107,
-    42,  78,  89,  27,  60,  70,  2,   38,  46,  0,   0,   9,   104, 158, 159,
-    92,  142, 143, 81,  127, 127, 69,  110, 110, 56,  94,  94,  43,  78,  78,
-    28,  60,  60,  2,   38,  38,  0,   0,   1,   107, 157, 140, 94,  141, 125,
-    82,  126, 112, 71,  110, 97,  59,  94,  82,  45,  77,  67,  29,  59,  51,
-    4,   37,  30,  0,   0,   0,   110, 156, 122, 97,  140, 109, 85,  125, 97,
-    72,  109, 83,  58,  93,  71,  45,  77,  57,  29,  60,  42,  5,   38,  22,
-    0,   0,   0,   111, 156, 103, 99,  139, 91,  87,  125, 81,  73,  108, 69,
-    60,  92,  58,  46,  77,  45,  30,  59,  31,  5,   38,  12,  0,   0,   0,
-    115, 156, 82,  101, 140, 73,  88,  124, 63,  74,  108, 53,  60,  92,  44,
-    46,  76,  32,  31,  59,  18,  6,   37,  0,   0,   0,   0,   116, 155, 65,
-    102, 139, 58,  89,  124, 49,  75,  108, 41,  61,  92,  32,  48,  76,  21,
-    31,  59,  6,   5,   37,  0,   0,   0,   0,   100, 141, 201, 88,  127, 181,
-    79,  114, 162, 69,  99,  142, 57,  83,  122, 44,  68,  102, 30,  51,  81,
-    7,   28,  56,  0,   0,   19,  105, 141, 182, 94,  127, 163, 83,  114, 146,
-    71,  98,  128, 59,  83,  110, 46,  68,  91,  31,  51,  72,  10,  28,  48,
-    0,   0,   11,  108, 141, 166, 96,  127, 149, 85,  113, 133, 73,  98,  116,
-    60,  83,  99,  46,  68,  82,  32,  51,  64,  11,  29,  41,  0,   0,   2,
-    111, 141, 149, 98,  126, 134, 88,  112, 119, 74,  97,  103, 61,  83,  88,
-    48,  67,  72,  33,  51,  56,  11,  29,  34,  0,   0,   0,   112, 140, 132,
-    100, 125, 118, 89,  112, 105, 75,  97,  91,  62,  82,  77,  49,  68,  62,
-    33,  51,  47,  12,  29,  26,  0,   0,   0,   115, 140, 116, 102, 125, 103,
-    90,  111, 91,  76,  96,  78,  62,  82,  65,  49,  67,  52,  34,  51,  38,
-    13,  29,  18,  0,   0,   0,   117, 139, 97,  103, 124, 87,  91,  111, 77,
-    78,  96,  65,  63,  81,  54,  49,  67,  41,  34,  51,  27,  12,  29,  7,
-    0,   0,   0,   119, 138, 78,  105, 124, 69,  92,  110, 60,  78,  95,  50,
-    65,  81,  40,  50,  67,  29,  34,  51,  15,  13,  30,  0,   0,   0,   0,
-    120, 138, 64,  106, 124, 54,  93,  110, 47,  78,  95,  38,  65,  81,  29,
-    50,  66,  17,  34,  50,  2,   13,  29,  0,   0,   0,   0,   107, 124, 189,
-    96,  111, 169, 85,  99,  152, 73,  85,  132, 61,  71,  114, 48,  57,  95,
-    34,  41,  75,  14,  18,  51,  0,   0,   13,  111, 124, 171, 100, 111, 153,
-    88,  99,  137, 75,  85,  120, 63,  72,  103, 50,  58,  85,  36,  41,  66,
-    15,  19,  43,  0,   0,   4,   113, 124, 156, 101, 111, 139, 90,  99,  125,
-    77,  85,  109, 64,  71,  93,  51,  57,  77,  36,  42,  59,  17,  20,  37,
-    0,   0,   0,   115, 124, 140, 103, 111, 125, 90,  99,  112, 78,  85,  97,
-    64,  71,  82,  52,  57,  67,  36,  42,  50,  16,  20,  30,  0,   0,   0,
-    117, 123, 125, 104, 110, 111, 92,  98,  99,  79,  85,  86,  65,  71,  72,
-    51,  58,  59,  37,  42,  43,  17,  21,  22,  0,   0,   0,   118, 123, 110,
-    105, 110, 97,  93,  98,  86,  78,  84,  74,  66,  71,  62,  52,  57,  49,
-    37,  42,  34,  17,  22,  14,  0,   0,   0,   120, 123, 93,  106, 109, 82,
-    94,  97,  72,  80,  84,  61,  66,  71,  50,  52,  57,  38,  37,  42,  24,
-    17,  22,  2,   0,   0,   0,   121, 122, 75,  108, 109, 66,  95,  97,  58,
-    80,  84,  48,  66,  71,  37,  52,  57,  26,  37,  42,  12,  16,  22,  0,
-    0,   0,   0,   122, 123, 62,  108, 109, 52,  95,  97,  45,  81,  84,  36,
-    67,  70,  26,  52,  57,  14,  37,  42,  0,   15,  22,  0,   0,   0,   0,
-    113, 107, 177, 102, 96,  159, 89,  85,  141, 78,  72,  124, 65,  60,  107,
-    52,  46,  89,  37,  30,  70,  18,  5,   46,  0,   0,   6,   116, 107, 160,
-    104, 96,  144, 92,  85,  129, 80,  72,  112, 67,  60,  96,  53,  47,  80,
-    38,  31,  62,  19,  7,   39,  0,   0,   0,   118, 107, 147, 105, 96,  131,
-    93,  85,  118, 80,  72,  101, 67,  60,  87,  54,  47,  71,  39,  31,  54,
-    19,  8,   32,  0,   0,   0,   119, 107, 132, 106, 96,  118, 94,  85,  106,
-    81,  73,  91,  67,  60,  77,  54,  47,  63,  39,  32,  47,  20,  9,   25,
-    0,   0,   0,   119, 107, 118, 106, 95,  105, 94,  85,  93,  81,  72,  80,
-    68,  60,  68,  54,  47,  54,  39,  32,  39,  20,  11,  18,  0,   0,   0,
-    121, 107, 104, 107, 96,  92,  95,  84,  80,  81,  72,  69,  68,  61,  58,
-    54,  48,  46,  39,  33,  31,  20,  12,  9,   0,   0,   0,   123, 107, 88,
-    108, 95,  77,  96,  84,  68,  82,  72,  57,  68,  60,  46,  54,  47,  35,
-    39,  33,  20,  19,  13,  2,   0,   0,   0,   123, 106, 72,  110, 95,  63,
-    96,  84,  54,  82,  72,  45,  69,  60,  35,  55,  48,  23,  39,  33,  9,
-    18,  14,  0,   0,   0,   0,   125, 106, 60,  110, 94,  50,  98,  84,  42,
-    83,  72,  34,  69,  60,  25,  55,  48,  12,  39,  33,  0,   17,  13,  0,
-    0,   0,   0,   118, 89,  165, 105, 79,  148, 93,  69,  132, 81,  57,  115,
-    68,  45,  99,  55,  32,  82,  41,  15,  64,  21,  0,   41,  0,   0,   0,
-    120, 89,  150, 107, 79,  135, 96,  69,  121, 82,  58,  105, 70,  46,  89,
-    56,  34,  73,  41,  17,  56,  21,  0,   34,  0,   0,   0,   121, 89,  137,
-    108, 79,  123, 96,  69,  109, 82,  58,  95,  70,  47,  81,  56,  34,  66,
-    41,  18,  49,  21,  0,   28,  0,   0,   0,   122, 90,  124, 109, 79,  110,
-    96,  69,  99,  83,  58,  85,  70,  47,  72,  56,  35,  58,  41,  19,  42,
-    21,  0,   22,  0,   0,   0,   123, 90,  111, 110, 79,  98,  97,  69,  87,
-    83,  59,  75,  70,  47,  63,  56,  35,  50,  41,  20,  35,  21,  0,   14,
-    0,   0,   0,   123, 90,  98,  110, 79,  87,  97,  70,  76,  84,  58,  64,
-    70,  48,  53,  56,  36,  41,  40,  21,  26,  21,  0,   5,   0,   0,   0,
-    125, 89,  84,  111, 79,  73,  97,  69,  64,  84,  59,  54,  70,  48,  43,
-    56,  36,  31,  40,  22,  17,  20,  1,   1,   0,   0,   0,   125, 89,  69,
-    112, 79,  60,  98,  70,  51,  84,  59,  42,  70,  48,  32,  56,  36,  20,
-    41,  22,  5,   19,  2,   0,   0,   0,   0,   126, 89,  57,  112, 79,  49,
-    99,  70,  41,  84,  59,  32,  70,  48,  22,  56,  36,  10,  40,  22,  0,
-    18,  2,   0,   0,   0,   0,   121, 67,  154, 108, 58,  138, 97,  50,  124,
-    84,  39,  107, 71,  28,  92,  58,  12,  76,  43,  0,   59,  20,  0,   37,
-    0,   0,   0,   124, 68,  140, 111, 59,  126, 98,  50,  112, 84,  40,  98,
-    71,  29,  83,  58,  15,  67,  42,  0,   51,  20,  0,   30,  0,   0,   0,
-    124, 68,  129, 111, 59,  114, 99,  51,  102, 86,  41,  88,  71,  30,  75,
-    58,  17,  60,  42,  0,   45,  20,  0,   24,  0,   0,   0,   125, 70,  116,
-    111, 60,  103, 99,  51,  92,  85,  41,  79,  71,  31,  66,  58,  19,  53,
-    42,  3,   38,  20,  0,   17,  0,   0,   0,   125, 70,  104, 111, 61,  93,
-    99,  52,  81,  85,  43,  69,  72,  32,  58,  58,  20,  45,  42,  4,   31,
-    20,  0,   8,   0,   0,   0,   126, 70,  92,  111, 61,  81,  99,  52,  71,
-    85,  42,  60,  71,  33,  49,  57,  21,  37,  42,  6,   23,  20,  0,   3,
-    0,   0,   0,   126, 70,  79,  112, 61,  70,  99,  53,  60,  85,  43,  50,
-    71,  33,  39,  57,  22,  28,  41,  7,   13,  19,  0,   0,   0,   0,   0,
-    127, 71,  66,  113, 62,  56,  100, 53,  48,  86,  44,  39,  71,  34,  29,
-    57,  23,  18,  41,  8,   2,   18,  0,   0,   0,   0,   0,   128, 70,  55,
-    114, 62,  46,  100, 54,  39,  86,  44,  30,  71,  34,  20,  57,  23,  7,
-    41,  9,   0,   18,  0,   0,   0,   0,   0,   124, 41,  145, 111, 32,  128,
-    99,  23,  114, 86,  10,  100, 73,  0,   85,  60,  0,   71,  43,  0,   54,
-    17,  0,   32,  0,   0,   0,   126, 42,  131, 113, 33,  117, 100, 25,  104,
-    86,  14,  90,  73,  0,   77,  60,  0,   63,  44,  0,   47,  18,  0,   25,
-    0,   0,   0,   127, 43,  120, 113, 34,  106, 101, 26,  95,  86,  17,  82,
-    73,  2,   69,  59,  0,   56,  43,  0,   41,  18,  0,   19,  0,   0,   0,
-    127, 45,  109, 113, 37,  97,  101, 28,  85,  86,  19,  74,  73,  5,   61,
-    59,  0,   48,  43,  0,   34,  19,  0,   11,  0,   0,   0,   127, 46,  98,
-    114, 38,  86,  100, 30,  76,  87,  21,  65,  73,  9,   54,  59,  0,   41,
-    43,  0,   26,  18,  0,   5,   0,   0,   0,   127, 47,  87,  113, 39,  76,
-    101, 31,  67,  86,  22,  56,  72,  11,  45,  59,  0,   33,  43,  0,   19,
-    18,  0,   2,   0,   0,   0,   128, 48,  75,  114, 39,  65,  101, 33,  56,
-    86,  23,  46,  72,  12,  36,  58,  0,   24,  42,  0,   9,   17,  0,   0,
-    0,   0,   0,   129, 48,  63,  114, 41,  54,  102, 33,  46,  87,  24,  36,
-    72,  14,  26,  58,  1,   14,  42,  0,   2,   16,  0,   0,   0,   0,   0,
-    128, 48,  53,  114, 41,  44,  102, 34,  37,  87,  25,  27,  72,  15,  17,
-    58,  1,   3,   41,  0,   0,   15,  0,   0,   0,   0,   0,   0,   189, 242,
-    0,   169, 217, 0,   151, 194, 0,   132, 170, 0,   113, 147, 0,   94,  123,
-    0,   74,  99,  0,   51,  71,  0,   15,  34,  1,   187, 219, 1,   167, 195,
-    0,   150, 175, 0,   131, 153, 0,   113, 132, 0,   94,  111, 0,   74,  89,
-    0,   50,  63,  0,   13,  28,  1,   186, 198, 1,   167, 178, 0,   149, 158,
-    0,   130, 139, 0,   111, 119, 0,   93,  100, 0,   74,  80,  0,   50,  55,
-    0,   13,  22,  1,   185, 176, 1,   165, 159, 1,   148, 142, 0,   129, 123,
-    0,   111, 106, 0,   93,  89,  0,   73,  70,  0,   50,  47,  0,   13,  13,
-    1,   184, 157, 1,   164, 141, 1,   147, 125, 0,   128, 110, 0,   110, 93,
-    0,   92,  77,  0,   73,  60,  0,   50,  39,  0,   12,  5,   25,  182, 137,
-    25,  163, 122, 17,  146, 109, 0,   128, 96,  0,   110, 81,  0,   92,  66,
-    0,   73,  51,  0,   50,  30,  0,   10,  0,   42,  181, 114, 35,  163, 102,
-    30,  145, 91,  14,  127, 80,  0,   109, 67,  0,   91,  53,  0,   72,  39,
-    0,   50,  19,  0,   10,  0,   52,  181, 92,  43,  162, 83,  32,  145, 73,
-    19,  126, 63,  0,   108, 52,  0,   90,  40,  0,   72,  27,  0,   50,  7,
-    0,   10,  0,   57,  181, 74,  48,  162, 66,  37,  144, 57,  24,  126, 49,
-    7,   108, 40,  0,   90,  29,  0,   72,  15,  0,   49,  0,   0,   10,  0,
-    1,   170, 227, 1,   152, 203, 0,   136, 182, 0,   119, 159, 0,   101, 137,
-    0,   84,  115, 0,   65,  92,  0,   43,  66,  0,   1,   29,  1,   169, 206,
-    1,   151, 184, 1,   136, 165, 0,   118, 144, 0,   102, 125, 0,   84,  105,
-    0,   65,  83,  0,   43,  58,  0,   0,   22,  29,  168, 186, 21,  151, 167,
-    14,  135, 150, 4,   118, 131, 0,   101, 112, 0,   83,  94,  0,   65,  75,
-    0,   43,  51,  0,   0,   16,  41,  167, 167, 33,  150, 150, 31,  134, 134,
-    19,  117, 117, 4,   100, 100, 0,   83,  83,  0,   65,  65,  0,   42,  43,
-    0,   0,   8,   48,  167, 149, 41,  149, 133, 33,  133, 118, 25,  116, 103,
-    13,  99,  88,  0,   83,  73,  0,   65,  56,  0,   42,  35,  0,   0,   0,
-    58,  165, 130, 49,  148, 115, 42,  132, 103, 31,  115, 89,  18,  99,  75,
-    0,   82,  61,  0,   64,  46,  0,   42,  26,  0,   0,   0,   62,  164, 110,
-    55,  147, 97,  45,  132, 87,  35,  115, 75,  22,  98,  63,  5,   82,  50,
-    0,   64,  36,  0,   42,  16,  0,   0,   0,   69,  164, 89,  60,  147, 78,
-    50,  131, 70,  37,  114, 59,  26,  98,  49,  10,  81,  37,  0,   64,  24,
-    0,   42,  4,   0,   0,   0,   71,  164, 71,  63,  147, 63,  53,  131, 55,
-    40,  114, 47,  28,  98,  38,  13,  81,  26,  0,   64,  12,  0,   42,  0,
-    0,   0,   0,   28,  153, 214, 24,  138, 193, 23,  123, 171, 16,  107, 150,
-    0,   91,  130, 0,   75,  109, 0,   58,  87,  0,   35,  61,  0,   0,   25,
-    48,  153, 194, 41,  138, 174, 34,  123, 156, 27,  107, 136, 16,  91,  117,
-    1,   75,  98,  0,   57,  78,  0,   35,  53,  0,   0,   17,  55,  153, 177,
-    47,  137, 158, 42,  122, 142, 33,  107, 124, 22,  91,  106, 6,   75,  88,
-    0,   57,  70,  0,   35,  46,  0,   0,   9,   61,  152, 158, 53,  136, 143,
-    45,  122, 127, 36,  106, 111, 24,  90,  94,  10,  74,  78,  0,   57,  61,
-    0,   35,  39,  0,   0,   2,   67,  151, 141, 59,  135, 126, 49,  121, 112,
-    39,  105, 98,  29,  90,  83,  14,  74,  68,  0,   57,  52,  0,   35,  31,
-    0,   0,   0,   71,  150, 123, 62,  135, 110, 54,  120, 98,  42,  105, 84,
-    31,  89,  71,  16,  74,  58,  0,   57,  43,  0,   35,  22,  0,   0,   0,
-    74,  150, 105, 64,  134, 92,  55,  120, 83,  45,  104, 71,  34,  89,  59,
-    20,  73,  47,  0,   57,  32,  0,   35,  13,  0,   0,   0,   78,  149, 84,
-    69,  134, 75,  59,  120, 66,  47,  103, 56,  34,  88,  46,  22,  73,  34,
-    1,   57,  20,  0,   35,  1,   0,   0,   0,   80,  149, 69,  70,  133, 61,
-    60,  119, 53,  49,  103, 44,  36,  88,  35,  23,  73,  24,  2,   56,  10,
-    0,   35,  0,   0,   0,   0,   58,  136, 200, 50,  122, 180, 45,  109, 162,
-    38,  94,  141, 27,  80,  121, 15,  65,  102, 0,   48,  81,  0,   26,  56,
-    0,   0,   19,  66,  136, 182, 59,  122, 163, 52,  109, 146, 42,  94,  128,
-    32,  80,  109, 20,  65,  91,  2,   48,  72,  0,   26,  49,  0,   0,   11,
-    70,  136, 165, 62,  122, 149, 55,  108, 133, 46,  94,  116, 35,  80,  99,
-    21,  65,  82,  4,   49,  64,  0,   26,  41,  0,   0,   3,   76,  135, 149,
-    66,  121, 133, 58,  108, 119, 48,  94,  103, 36,  79,  88,  23,  65,  73,
-    7,   49,  56,  0,   27,  34,  0,   0,   0,   78,  135, 133, 69,  120, 118,
-    60,  107, 106, 50,  93,  92,  39,  79,  77,  26,  65,  63,  8,   49,  47,
-    0,   27,  26,  0,   0,   0,   82,  134, 117, 71,  120, 104, 62,  107, 92,
-    51,  93,  79,  39,  78,  66,  27,  64,  53,  10,  48,  39,  0,   27,  18,
-    0,   0,   0,   84,  134, 99,  73,  119, 87,  64,  106, 77,  53,  92,  66,
-    42,  78,  55,  28,  64,  42,  11,  48,  29,  0,   28,  9,   0,   0,   0,
-    87,  133, 81,  76,  119, 72,  66,  106, 62,  55,  92,  52,  43,  78,  42,
-    29,  64,  31,  12,  48,  17,  0,   28,  0,   0,   0,   0,   88,  134, 67,
-    77,  119, 58,  68,  106, 51,  56,  92,  42,  44,  78,  32,  30,  64,  20,
-    12,  48,  6,   0,   28,  0,   0,   0,   0,   73,  120, 189, 64,  107, 168,
-    57,  96,  151, 47,  82,  133, 38,  69,  114, 26,  55,  95,  11,  39,  75,
-    0,   16,  51,  0,   0,   14,  78,  120, 171, 69,  107, 153, 62,  95,  137,
-    51,  82,  119, 40,  69,  102, 29,  55,  85,  15,  39,  66,  0,   17,  44,
-    0,   0,   4,   81,  120, 156, 71,  107, 140, 64,  95,  125, 53,  82,  109,
-    42,  69,  93,  31,  55,  77,  16,  39,  59,  0,   18,  37,  0,   0,   0,
-    85,  120, 141, 74,  107, 126, 65,  95,  112, 54,  82,  97,  43,  69,  82,
-    32,  55,  67,  17,  39,  51,  0,   19,  30,  0,   0,   0,   86,  119, 126,
-    76,  106, 112, 66,  95,  100, 56,  81,  85,  45,  69,  72,  33,  55,  59,
-    18,  40,  43,  0,   19,  22,  0,   0,   0,   89,  119, 110, 78,  106, 98,
-    69,  94,  87,  56,  81,  75,  46,  68,  62,  33,  55,  49,  18,  40,  35,
-    0,   20,  15,  0,   0,   0,   89,  119, 95,  80,  106, 83,  70,  94,  73,
-    58,  81,  63,  46,  68,  51,  34,  55,  39,  19,  40,  25,  0,   20,  4,
-    0,   0,   0,   92,  118, 78,  82,  106, 68,  70,  93,  59,  59,  81,  49,
-    47,  68,  39,  34,  55,  28,  19,  40,  14,  0,   20,  0,   0,   0,   0,
-    93,  118, 65,  82,  105, 55,  72,  93,  48,  60,  81,  39,  47,  68,  29,
-    34,  55,  18,  20,  40,  2,   0,   20,  0,   0,   0,   0,   83,  104, 177,
-    74,  93,  159, 65,  82,  142, 56,  70,  124, 45,  57,  106, 33,  44,  89,
-    20,  28,  70,  1,   4,   46,  0,   0,   8,   86,  104, 161, 78,  93,  145,
-    68,  82,  128, 58,  70,  112, 48,  58,  96,  35,  45,  80,  21,  29,  62,
-    1,   6,   40,  0,   0,   0,   89,  104, 147, 79,  93,  131, 69,  82,  118,
-    59,  70,  102, 47,  58,  87,  36,  45,  72,  23,  29,  55,  3,   7,   33,
-    0,   0,   0,   90,  104, 132, 80,  93,  119, 71,  82,  106, 60,  70,  91,
-    48,  58,  77,  37,  45,  62,  23,  30,  47,  4,   7,   26,  0,   0,   0,
-    92,  104, 118, 82,  93,  105, 72,  82,  93,  61,  69,  80,  50,  58,  68,
-    37,  45,  55,  23,  30,  39,  4,   8,   18,  0,   0,   0,   94,  104, 105,
-    82,  92,  93,  72,  82,  82,  61,  70,  70,  50,  58,  58,  38,  46,  46,
-    23,  31,  31,  6,   10,  11,  0,   0,   0,   95,  104, 90,  84,  92,  79,
-    74,  82,  70,  62,  70,  58,  50,  58,  48,  37,  46,  36,  23,  31,  22,
-    4,   11,  3,   0,   0,   0,   96,  103, 74,  85,  92,  65,  75,  81,  56,
-    63,  70,  47,  50,  58,  37,  38,  46,  25,  24,  31,  11,  3,   11,  0,
-    0,   0,   0,   97,  103, 62,  86,  92,  53,  76,  81,  45,  63,  69,  36,
-    51,  58,  27,  38,  46,  15,  23,  31,  0,   3,   11,  0,   0,   0,   0,
-    90,  87,  165, 81,  77,  148, 72,  67,  132, 62,  55,  116, 50,  44,  99,
-    39,  31,  82,  25,  14,  64,  1,   0,   42,  0,   0,   1,   93,  87,  150,
-    83,  77,  135, 74,  67,  121, 63,  56,  105, 52,  45,  90,  40,  32,  74,
-    25,  16,  57,  3,   0,   35,  0,   0,   0,   95,  87,  138, 85,  77,  123,
-    75,  67,  109, 63,  57,  95,  53,  45,  81,  41,  33,  66,  26,  17,  50,
-    4,   0,   28,  0,   0,   0,   95,  88,  124, 85,  77,  111, 75,  67,  99,
-    63,  57,  86,  53,  45,  72,  41,  33,  58,  26,  18,  43,  5,   1,   22,
-    0,   0,   0,   97,  88,  112, 87,  77,  100, 76,  68,  88,  64,  57,  76,
-    53,  46,  63,  41,  34,  50,  26,  19,  35,  5,   2,   14,  0,   0,   0,
-    99,  87,  99,  87,  78,  88,  76,  68,  77,  65,  57,  65,  53,  46,  54,
-    41,  35,  42,  27,  20,  27,  6,   2,   5,   0,   0,   0,   100, 87,  85,
-    88,  77,  75,  77,  68,  65,  65,  57,  54,  53,  46,  44,  41,  35,  32,
-    27,  21,  19,  5,   3,   2,   0,   0,   0,   100, 88,  71,  89,  77,  61,
-    78,  68,  53,  66,  57,  44,  53,  47,  33,  41,  35,  22,  27,  21,  7,
-    5,   3,   0,   0,   0,   0,   101, 87,  60,  90,  77,  52,  79,  68,  44,
-    66,  58,  34,  53,  47,  25,  41,  35,  13,  26,  22,  0,   5,   3,   0,
-    0,   0,   0,   97,  67,  155, 86,  58,  138, 77,  50,  125, 66,  39,  108,
-    55,  28,  92,  43,  12,  76,  29,  0,   59,  2,   0,   37,  0,   0,   0,
-    99,  67,  141, 88,  59,  127, 78,  50,  113, 68,  40,  98,  56,  29,  83,
-    44,  15,  68,  29,  0,   52,  3,   0,   30,  0,   0,   0,   100, 68,  129,
-    89,  59,  115, 80,  51,  103, 68,  41,  89,  56,  30,  75,  44,  16,  61,
-    30,  0,   45,  4,   0,   24,  0,   0,   0,   100, 69,  118, 90,  60,  104,
-    80,  51,  92,  67,  41,  79,  56,  31,  66,  44,  18,  53,  29,  2,   38,
-    4,   0,   18,  0,   0,   0,   101, 69,  104, 90,  61,  93,  79,  51,  82,
-    67,  42,  70,  56,  32,  59,  44,  20,  46,  29,  4,   31,  6,   0,   9,
-    0,   0,   0,   102, 69,  93,  90,  61,  83,  80,  52,  72,  68,  42,  61,
-    56,  33,  50,  43,  20,  38,  29,  5,   23,  7,   0,   4,   0,   0,   0,
-    102, 70,  80,  91,  61,  71,  80,  52,  61,  68,  43,  51,  56,  32,  40,
-    44,  21,  29,  30,  6,   14,  7,   0,   0,   0,   0,   0,   103, 70,  68,
-    92,  61,  58,  81,  53,  50,  69,  43,  41,  56,  34,  31,  43,  22,  19,
-    29,  7,   3,   7,   0,   0,   0,   0,   0,   104, 70,  57,  92,  61,  48,
-    82,  53,  40,  69,  43,  32,  56,  34,  22,  43,  23,  10,  29,  8,   0,
-    6,   0,   0,   0,   0,   0,   101, 45,  145, 91,  35,  129, 80,  26,  116,
-    69,  15,  101, 59,  0,   86,  46,  0,   71,  31,  0,   55,  0,   0,   33,
-    0,   0,   0,   104, 44,  132, 92,  36,  118, 82,  28,  105, 71,  17,  91,
-    58,  3,   77,  46,  0,   63,  31,  0,   48,  2,   0,   26,  0,   0,   0,
-    104, 46,  121, 93,  37,  107, 82,  30,  96,  70,  20,  83,  58,  6,   70,
-    46,  0,   57,  32,  0,   41,  4,   0,   20,  0,   0,   0,   104, 48,  110,
-    93,  40,  98,  82,  31,  87,  70,  22,  74,  59,  9,   62,  45,  0,   49,
-    31,  0,   35,  6,   0,   13,  0,   0,   0,   104, 48,  99,  92,  41,  88,
-    82,  32,  77,  70,  23,  65,  58,  11,  54,  46,  0,   42,  32,  0,   27,
-    7,   0,   5,   0,   0,   0,   105, 50,  88,  93,  41,  77,  82,  34,  68,
-    71,  24,  57,  58,  13,  46,  45,  1,   35,  31,  0,   21,  7,   0,   2,
-    0,   0,   0,   105, 50,  76,  94,  41,  66,  83,  34,  57,  71,  25,  47,
-    58,  15,  37,  45,  2,   25,  32,  0,   11,  7,   0,   0,   0,   0,   0,
-    106, 50,  64,  94,  42,  55,  83,  35,  47,  71,  26,  38,  58,  16,  27,
-    45,  4,   17,  31,  0,   4,   7,   0,   0,   0,   0,   0,   106, 51,  54,
-    95,  42,  45,  83,  35,  38,  71,  27,  30,  58,  16,  19,  45,  5,   7,
-    30,  0,   0,   6,   0,   0,   0,   0,   0,   0,   181, 240, 0,   162, 216,
-    0,   144, 193, 0,   126, 168, 0,   109, 146, 0,   91,  123, 0,   71,  98,
-    0,   48,  71,  0,   9,   34,  0,   179, 218, 0,   161, 195, 0,   144, 174,
-    0,   126, 153, 0,   108, 132, 0,   90,  110, 0,   71,  88,  0,   48,  63,
-    0,   8,   29,  0,   178, 197, 0,   159, 177, 0,   143, 159, 0,   125, 139,
-    0,   107, 119, 0,   90,  99,  0,   71,  79,  0,   48,  55,  0,   8,   22,
-    0,   177, 177, 0,   158, 158, 0,   142, 141, 0,   124, 123, 0,   107, 106,
-    0,   89,  88,  0,   71,  70,  0,   48,  47,  0,   8,   14,  0,   176, 157,
-    0,   158, 141, 0,   141, 126, 0,   123, 109, 0,   106, 93,  0,   89,  78,
-    0,   70,  60,  0,   47,  39,  0,   7,   5,   0,   175, 138, 0,   157, 123,
-    0,   141, 110, 0,   123, 96,  0,   105, 81,  0,   88,  67,  0,   70,  51,
-    0,   48,  30,  0,   6,   0,   0,   173, 115, 0,   155, 104, 0,   140, 92,
-    0,   122, 80,  0,   105, 67,  0,   88,  55,  0,   69,  40,  0,   47,  20,
-    0,   6,   0,   0,   173, 94,  0,   155, 85,  0,   139, 75,  0,   121, 64,
-    0,   104, 53,  0,   88,  42,  0,   70,  28,  0,   47,  9,   0,   6,   0,
-    0,   173, 76,  0,   155, 70,  0,   138, 61,  0,   122, 53,  0,   104, 44,
-    0,   87,  32,  0,   69,  18,  0,   47,  0,   0,   6,   0,   0,   164, 226,
-    0,   147, 203, 0,   131, 181, 0,   114, 158, 0,   97,  136, 0,   80,  115,
-    0,   63,  92,  0,   40,  65,  0,   0,   30,  0,   162, 205, 0,   145, 184,
-    0,   130, 164, 0,   114, 143, 0,   97,  124, 0,   81,  104, 0,   63,  83,
-    0,   40,  58,  0,   0,   23,  0,   162, 187, 0,   145, 167, 0,   130, 150,
-    0,   113, 131, 0,   96,  112, 0,   80,  93,  0,   62,  74,  0,   40,  50,
-    0,   0,   16,  0,   160, 167, 0,   144, 150, 0,   129, 134, 0,   112, 116,
-    0,   96,  100, 0,   80,  82,  0,   62,  65,  0,   40,  43,  0,   0,   7,
-    0,   160, 148, 0,   143, 133, 0,   128, 118, 0,   111, 103, 0,   96,  88,
-    0,   80,  73,  0,   62,  56,  0,   40,  35,  0,   0,   0,   0,   158, 130,
-    0,   142, 117, 0,   127, 104, 0,   111, 89,  0,   95,  76,  0,   79,  62,
-    0,   62,  46,  0,   40,  26,  0,   0,   0,   0,   158, 111, 0,   141, 99,
-    0,   127, 88,  0,   111, 76,  0,   95,  63,  0,   79,  51,  0,   62,  37,
-    0,   40,  18,  0,   0,   0,   0,   158, 91,  0,   141, 81,  0,   126, 72,
-    0,   110, 62,  0,   94,  50,  0,   79,  39,  0,   62,  25,  0,   40,  5,
-    0,   0,   0,   0,   157, 74,  0,   141, 66,  0,   126, 59,  0,   110, 49,
-    0,   94,  40,  0,   78,  29,  0,   61,  15,  0,   40,  0,   0,   0,   0,
-    0,   148, 214, 0,   133, 192, 0,   119, 171, 0,   103, 150, 0,   87,  129,
-    0,   72,  108, 0,   55,  86,  0,   32,  61,  0,   0,   25,  0,   147, 193,
-    0,   132, 173, 0,   118, 155, 0,   103, 136, 0,   87,  116, 0,   72,  98,
-    0,   55,  78,  0,   32,  53,  0,   0,   17,  0,   147, 176, 0,   132, 158,
-    0,   118, 142, 0,   102, 124, 0,   87,  106, 0,   72,  88,  0,   55,  69,
-    0,   33,  46,  0,   0,   9,   0,   146, 159, 0,   131, 142, 0,   117, 127,
-    0,   102, 111, 0,   87,  95,  0,   71,  79,  0,   55,  61,  0,   33,  39,
-    0,   0,   2,   0,   145, 140, 0,   130, 126, 0,   117, 112, 0,   101, 98,
-    0,   86,  83,  0,   71,  68,  0,   55,  52,  0,   33,  31,  0,   0,   0,
-    0,   144, 124, 0,   130, 111, 0,   116, 99,  0,   101, 84,  0,   86,  72,
-    0,   71,  59,  0,   55,  43,  0,   33,  23,  0,   0,   0,   0,   144, 106,
-    0,   129, 94,  0,   115, 83,  0,   101, 72,  0,   85,  60,  0,   71,  48,
-    0,   55,  34,  0,   33,  14,  0,   0,   0,   3,   143, 86,  0,   129, 77,
-    0,   115, 68,  0,   100, 58,  0,   85,  48,  0,   70,  36,  0,   54,  22,
-    0,   33,  3,   0,   0,   0,   18,  143, 72,  13,  128, 63,  0,   115, 57,
-    0,   100, 47,  0,   85,  37,  0,   70,  26,  0,   54,  13,  0,   33,  0,
-    0,   0,   0,   0,   132, 200, 0,   118, 179, 0,   105, 161, 0,   91,  140,
-    0,   76,  121, 0,   62,  101, 0,   46,  81,  0,   24,  56,  0,   0,   19,
-    0,   131, 182, 0,   118, 163, 0,   105, 146, 0,   91,  128, 0,   77,  110,
-    0,   62,  91,  0,   46,  72,  0,   25,  48,  0,   0,   11,  0,   131, 165,
-    0,   117, 149, 0,   104, 133, 0,   91,  116, 0,   77,  99,  0,   62,  82,
-    0,   46,  64,  0,   25,  41,  0,   0,   4,   0,   131, 149, 0,   116, 134,
-    0,   104, 119, 0,   91,  104, 0,   77,  89,  0,   62,  73,  0,   46,  56,
-    0,   25,  34,  0,   0,   0,   10,  130, 133, 2,   116, 119, 0,   104, 106,
-    0,   90,  91,  0,   76,  78,  0,   62,  64,  0,   46,  48,  0,   26,  27,
-    0,   0,   0,   23,  130, 118, 20,  116, 104, 13,  103, 93,  3,   89,  79,
-    0,   76,  67,  0,   62,  54,  0,   46,  39,  0,   26,  19,  0,   0,   0,
-    33,  129, 101, 27,  115, 89,  19,  103, 79,  9,   89,  67,  0,   75,  56,
-    0,   61,  43,  0,   46,  29,  0,   26,  10,  0,   0,   0,   41,  128, 83,
-    35,  115, 73,  27,  102, 64,  15,  89,  55,  0,   76,  45,  0,   62,  33,
-    0,   46,  18,  0,   26,  0,   0,   0,   0,   43,  129, 69,  38,  115, 61,
-    30,  102, 54,  17,  89,  45,  2,   75,  34,  0,   61,  23,  0,   46,  9,
-    0,   26,  0,   0,   0,   0,   1,   116, 188, 1,   104, 168, 0,   92,  151,
-    0,   79,  132, 0,   66,  113, 0,   52,  94,  0,   36,  75,  0,   14,  52,
-    0,   0,   14,  17,  116, 171, 16,  104, 153, 14,  92,  137, 8,   79,  119,
-    0,   67,  102, 0,   53,  85,  0,   37,  67,  0,   16,  44,  0,   0,   4,
-    31,  116, 155, 27,  104, 140, 21,  92,  125, 13,  79,  109, 3,   66,  93,
-    0,   53,  77,  0,   37,  59,  0,   16,  38,  0,   0,   0,   37,  115, 141,
-    30,  103, 126, 26,  92,  112, 16,  79,  98,  5,   66,  83,  0,   53,  67,
-    0,   38,  51,  0,   17,  31,  0,   0,   0,   41,  115, 126, 37,  103, 112,
-    31,  92,  100, 22,  79,  86,  10,  66,  72,  0,   53,  59,  0,   38,  44,
-    0,   17,  23,  0,   0,   0,   48,  115, 111, 41,  102, 99,  34,  91,  88,
-    24,  78,  76,  14,  66,  63,  0,   53,  50,  0,   38,  36,  0,   18,  15,
-    0,   0,   0,   51,  115, 95,  46,  102, 85,  37,  91,  74,  26,  78,  63,
-    16,  66,  52,  0,   53,  40,  0,   38,  26,  0,   18,  5,   0,   0,   0,
-    55,  114, 80,  47,  102, 69,  40,  90,  60,  30,  78,  51,  19,  66,  41,
-    3,   53,  29,  0,   38,  15,  0,   17,  0,   0,   0,   0,   56,  114, 66,
-    50,  102, 58,  40,  91,  50,  32,  78,  41,  18,  66,  32,  4,   53,  21,
-    0,   38,  5,   0,   17,  0,   0,   0,   0,   39,  102, 178, 37,  90,  159,
-    30,  79,  142, 21,  68,  124, 14,  55,  106, 0,   42,  89,  0,   26,  70,
-    0,   4,   46,  0,   0,   8,   48,  102, 161, 42,  90,  145, 35,  79,  128,
-    26,  68,  112, 19,  55,  96,  3,   43,  79,  0,   27,  62,  0,   6,   40,
-    0,   0,   0,   50,  102, 147, 44,  90,  132, 37,  79,  118, 30,  68,  102,
-    20,  56,  87,  7,   43,  72,  0,   28,  55,  0,   6,   34,  0,   0,   0,
-    53,  101, 133, 47,  90,  118, 41,  79,  106, 32,  68,  91,  21,  56,  78,
-    9,   43,  63,  0,   28,  47,  0,   6,   26,  0,   0,   0,   57,  101, 119,
-    50,  89,  106, 42,  79,  94,  34,  67,  81,  24,  56,  68,  9,   44,  55,
-    0,   29,  40,  0,   6,   19,  0,   0,   0,   60,  100, 105, 50,  90,  94,
-    45,  80,  83,  36,  68,  71,  24,  56,  59,  11,  44,  46,  0,   29,  32,
-    0,   7,   12,  0,   0,   0,   63,  101, 91,  55,  90,  80,  46,  79,  70,
-    37,  68,  59,  26,  56,  49,  12,  44,  37,  1,   29,  23,  0,   7,   3,
-    0,   0,   0,   64,  101, 75,  56,  89,  67,  48,  79,  57,  37,  68,  48,
-    27,  56,  37,  15,  44,  26,  0,   29,  12,  0,   7,   0,   0,   0,   0,
-    66,  101, 64,  58,  89,  55,  49,  79,  47,  39,  68,  38,  27,  56,  29,
-    14,  44,  18,  1,   30,  2,   0,   7,   0,   0,   0,   0,   57,  86,  165,
-    51,  75,  148, 45,  65,  133, 38,  54,  116, 28,  43,  100, 16,  29,  83,
-    0,   13,  64,  0,   0,   42,  0,   0,   3,   60,  86,  151, 55,  75,  135,
-    47,  66,  121, 39,  55,  105, 30,  44,  90,  18,  31,  74,  3,   16,  57,
-    0,   1,   35,  0,   0,   0,   62,  86,  139, 56,  75,  123, 49,  66,  110,
-    40,  55,  95,  30,  44,  81,  19,  31,  66,  4,   17,  51,  0,   1,   29,
-    0,   0,   0,   65,  86,  125, 56,  76,  112, 49,  66,  99,  39,  55,  86,
-    31,  44,  72,  19,  32,  59,  5,   18,  44,  0,   1,   23,  0,   0,   0,
-    67,  86,  113, 58,  75,  100, 51,  66,  88,  41,  56,  77,  31,  45,  64,
-    20,  32,  51,  6,   18,  35,  0,   1,   14,  0,   0,   0,   69,  86,  99,
-    61,  76,  88,  52,  66,  78,  43,  56,  66,  32,  45,  55,  20,  33,  42,
-    7,   19,  27,  0,   1,   6,   0,   0,   0,   69,  86,  86,  61,  76,  75,
-    53,  67,  66,  43,  56,  55,  33,  45,  45,  21,  34,  34,  8,   20,  20,
-    0,   2,   2,   0,   0,   0,   71,  86,  72,  63,  75,  62,  54,  66,  55,
-    45,  56,  45,  33,  45,  35,  22,  34,  23,  7,   20,  8,   0,   2,   0,
-    0,   0,   0,   71,  86,  62,  64,  75,  53,  55,  66,  46,  45,  56,  36,
-    33,  46,  27,  22,  34,  15,  8,   20,  0,   0,   2,   0,   0,   0,   0,
-    69,  67,  156, 61,  58,  140, 53,  50,  125, 45,  39,  108, 35,  28,  93,
-    25,  12,  77,  12,  0,   59,  0,   0,   37,  0,   0,   0,   71,  68,  142,
-    63,  59,  126, 56,  50,  114, 47,  40,  98,  37,  28,  84,  26,  15,  68,
-    12,  0,   53,  0,   0,   30,  0,   0,   0,   72,  68,  130, 63,  59,  116,
-    56,  50,  104, 47,  40,  90,  38,  30,  75,  27,  16,  61,  13,  0,   46,
-    0,   0,   24,  0,   0,   0,   73,  69,  118, 65,  59,  105, 57,  51,  92,
-    47,  41,  80,  37,  30,  67,  26,  18,  53,  14,  1,   39,  0,   0,   18,
-    0,   0,   0,   74,  69,  106, 65,  60,  93,  57,  51,  82,  48,  41,  70,
-    38,  31,  59,  26,  19,  46,  13,  2,   32,  0,   0,   10,  0,   0,   0,
-    76,  69,  95,  66,  61,  84,  58,  52,  73,  48,  42,  61,  37,  32,  50,
-    26,  20,  38,  14,  4,   24,  0,   0,   4,   0,   0,   0,   76,  69,  81,
-    68,  60,  72,  58,  52,  62,  48,  42,  51,  38,  32,  41,  27,  21,  30,
-    14,  4,   16,  0,   0,   1,   0,   0,   0,   76,  69,  68,  68,  61,  60,
-    60,  52,  51,  49,  43,  41,  38,  33,  32,  27,  21,  20,  14,  5,   5,
-    0,   0,   0,   0,   0,   0,   78,  70,  59,  69,  61,  50,  60,  52,  42,
-    49,  43,  34,  39,  33,  24,  27,  22,  13,  14,  7,   1,   0,   0,   0,
-    0,   0,   0,   75,  46,  146, 68,  38,  131, 60,  30,  117, 50,  19,  102,
-    41,  4,   87,  29,  0,   72,  13,  0,   55,  0,   0,   33,  0,   0,   0,
-    78,  47,  132, 70,  39,  119, 61,  30,  105, 53,  20,  92,  42,  5,   78,
-    30,  0,   64,  13,  0,   49,  0,   0,   27,  0,   0,   0,   79,  48,  122,
-    70,  40,  108, 62,  32,  96,  52,  22,  84,  42,  9,   71,  30,  0,   58,
-    14,  0,   42,  0,   0,   20,  0,   0,   0,   79,  50,  111, 70,  42,  99,
-    62,  33,  88,  52,  23,  74,  41,  11,  63,  29,  0,   50,  14,  0,   36,
-    0,   0,   14,  0,   0,   0,   80,  50,  99,  70,  42,  89,  61,  34,  78,
-    52,  25,  67,  41,  14,  55,  30,  0,   42,  15,  0,   28,  0,   0,   6,
-    0,   0,   0,   81,  51,  89,  71,  43,  78,  62,  35,  69,  52,  25,  58,
-    42,  15,  47,  30,  3,   36,  15,  0,   22,  0,   0,   3,   0,   0,   0,
-    81,  51,  77,  71,  44,  68,  63,  36,  59,  53,  26,  49,  41,  16,  38,
-    31,  4,   27,  16,  0,   12,  0,   0,   0,   0,   0,   0,   81,  52,  65,
-    72,  43,  56,  63,  36,  48,  53,  27,  39,  41,  17,  29,  30,  4,   18,
-    14,  0,   3,   0,   0,   0,   0,   0,   0,   81,  52,  55,  73,  44,  47,
-    64,  36,  39,  53,  28,  32,  42,  18,  21,  31,  6,   9,   14,  0,   0,
-    0,   0,   0,   0,   0,   0,   0,   174, 239, 0,   156, 214, 0,   139, 192,
-    0,   121, 168, 0,   105, 145, 0,   87,  123, 0,   68,  98,  0,   46,  70,
-    0,   3,   35,  0,   172, 217, 0,   155, 194, 0,   139, 173, 0,   121, 152,
-    0,   104, 130, 0,   87,  110, 0,   69,  88,  0,   46,  63,  0,   4,   28,
-    0,   171, 197, 0,   153, 175, 0,   138, 158, 0,   121, 139, 0,   103, 118,
-    0,   86,  100, 0,   68,  79,  0,   46,  55,  0,   4,   22,  0,   170, 177,
-    0,   152, 158, 0,   136, 141, 0,   119, 124, 0,   103, 106, 0,   86,  88,
-    0,   68,  70,  0,   45,  47,  0,   3,   14,  0,   169, 157, 0,   152, 141,
-    0,   136, 126, 0,   119, 109, 0,   102, 94,  0,   86,  78,  0,   68,  60,
-    0,   46,  39,  0,   3,   5,   0,   167, 138, 0,   150, 124, 0,   135, 111,
-    0,   118, 97,  0,   102, 82,  0,   85,  68,  0,   68,  52,  0,   46,  31,
-    0,   3,   0,   0,   167, 118, 0,   150, 104, 0,   135, 94,  0,   118, 81,
-    0,   101, 69,  0,   84,  56,  0,   67,  41,  0,   45,  21,  0,   3,   0,
-    0,   166, 97,  0,   149, 87,  0,   134, 77,  0,   117, 67,  0,   101, 56,
-    0,   85,  44,  0,   67,  30,  0,   45,  10,  0,   3,   0,   0,   165, 79,
-    0,   149, 73,  0,   133, 64,  0,   117, 56,  0,   101, 46,  0,   85,  34,
-    0,   68,  21,  0,   46,  1,   0,   3,   0,   0,   158, 225, 0,   141, 201,
-    0,   126, 180, 0,   109, 158, 0,   94,  136, 0,   78,  114, 0,   60,  91,
-    0,   38,  66,  0,   0,   30,  0,   156, 203, 0,   140, 183, 0,   125, 164,
-    0,   109, 143, 0,   94,  124, 0,   78,  104, 0,   61,  83,  0,   38,  57,
-    0,   0,   23,  0,   156, 186, 0,   140, 166, 0,   125, 150, 0,   109, 130,
-    0,   93,  111, 0,   77,  93,  0,   60,  74,  0,   38,  50,  0,   0,   17,
-    0,   155, 167, 0,   138, 149, 0,   124, 134, 0,   109, 117, 0,   93,  100,
-    0,   76,  83,  0,   60,  65,  0,   38,  43,  0,   0,   9,   0,   153, 147,
-    0,   138, 134, 0,   124, 120, 0,   107, 103, 0,   92,  88,  0,   77,  73,
-    0,   60,  56,  0,   38,  35,  0,   0,   0,   0,   153, 131, 0,   137, 118,
-    0,   122, 105, 0,   107, 90,  0,   91,  76,  0,   76,  63,  0,   60,  47,
-    0,   39,  28,  0,   0,   0,   0,   153, 111, 0,   136, 100, 0,   123, 90,
-    0,   107, 77,  0,   92,  65,  0,   76,  52,  0,   60,  37,  0,   38,  18,
-    0,   0,   0,   0,   152, 93,  0,   136, 82,  0,   122, 74,  0,   106, 63,
-    0,   91,  52,  0,   76,  40,  0,   59,  26,  0,   38,  6,   0,   0,   0,
-    0,   151, 78,  0,   136, 69,  0,   121, 61,  0,   106, 52,  0,   91,  43,
-    0,   76,  32,  0,   59,  17,  0,   38,  0,   0,   0,   0,   0,   143, 213,
-    0,   128, 191, 0,   115, 171, 0,   100, 149, 0,   84,  128, 0,   69,  108,
-    0,   52,  86,  0,   30,  61,  0,   0,   25,  0,   142, 193, 0,   127, 173,
-    0,   114, 154, 0,   99,  134, 0,   84,  116, 0,   69,  98,  0,   52,  77,
-    0,   31,  53,  0,   0,   18,  0,   141, 176, 0,   127, 158, 0,   114, 141,
-    0,   98,  122, 0,   84,  105, 0,   69,  88,  0,   53,  69,  0,   31,  46,
-    0,   0,   9,   0,   141, 159, 0,   126, 142, 0,   113, 127, 0,   98,  110,
-    0,   83,  95,  0,   69,  78,  0,   53,  60,  0,   32,  39,  0,   0,   2,
-    0,   140, 140, 0,   126, 126, 0,   112, 112, 0,   98,  98,  0,   83,  83,
-    0,   68,  69,  0,   52,  52,  0,   31,  31,  0,   0,   0,   0,   140, 124,
-    0,   125, 112, 0,   112, 100, 0,   97,  86,  0,   83,  72,  0,   68,  59,
-    0,   52,  44,  0,   31,  23,  0,   0,   0,   0,   139, 106, 0,   125, 96,
-    0,   111, 85,  0,   97,  72,  0,   83,  62,  0,   68,  49,  0,   52,  35,
-    0,   31,  15,  0,   0,   0,   0,   138, 88,  0,   124, 79,  0,   111, 70,
-    0,   96,  59,  0,   82,  48,  0,   68,  38,  0,   52,  24,  0,   31,  4,
-    0,   0,   0,   0,   139, 76,  0,   124, 66,  0,   111, 58,  0,   96,  50,
-    0,   82,  40,  0,   68,  29,  0,   52,  15,  0,   31,  0,   0,   0,   0,
-    0,   129, 200, 0,   114, 179, 0,   102, 160, 0,   87,  139, 0,   74,  120,
-    0,   60,  101, 0,   44,  81,  0,   22,  56,  0,   0,   19,  0,   127, 181,
-    0,   114, 163, 0,   102, 146, 0,   88,  127, 0,   74,  109, 0,   60,  91,
-    0,   44,  72,  0,   23,  48,  0,   0,   11,  0,   127, 166, 0,   113, 148,
-    0,   101, 133, 0,   87,  115, 0,   74,  99,  0,   60,  82,  0,   44,  64,
-    0,   23,  42,  0,   0,   4,   0,   127, 150, 0,   113, 134, 0,   101, 119,
-    0,   87,  104, 0,   74,  89,  0,   60,  73,  0,   44,  56,  0,   23,  35,
-    0,   0,   0,   0,   125, 134, 0,   112, 118, 0,   100, 106, 0,   87,  92,
-    0,   73,  78,  0,   60,  64,  0,   44,  48,  0,   23,  27,  0,   0,   0,
-    0,   125, 118, 0,   112, 105, 0,   100, 94,  0,   86,  80,  0,   73,  68,
-    0,   60,  54,  0,   44,  39,  0,   23,  20,  0,   0,   0,   0,   125, 101,
-    0,   111, 90,  0,   99,  80,  0,   86,  69,  0,   73,  58,  0,   59,  45,
-    0,   44,  30,  0,   23,  11,  0,   0,   0,   0,   124, 85,  0,   111, 75,
-    0,   99,  66,  0,   86,  56,  0,   73,  45,  0,   59,  34,  0,   44,  20,
-    0,   23,  1,   0,   0,   0,   0,   125, 72,  0,   111, 62,  0,   99,  56,
-    0,   86,  46,  0,   73,  36,  0,   60,  26,  0,   44,  12,  0,   23,  0,
-    0,   0,   0,   0,   114, 188, 0,   101, 167, 0,   89,  150, 0,   77,  131,
-    0,   64,  113, 0,   50,  95,  0,   34,  75,  0,   12,  52,  0,   0,   14,
-    0,   113, 170, 0,   101, 153, 0,   89,  137, 0,   77,  120, 0,   64,  102,
-    0,   50,  85,  0,   35,  67,  0,   12,  44,  0,   0,   4,   0,   113, 156,
-    0,   100, 139, 0,   89,  125, 0,   77,  109, 0,   64,  92,  0,   51,  77,
-    0,   35,  60,  0,   12,  38,  0,   0,   0,   0,   112, 141, 0,   100, 126,
-    0,   89,  113, 0,   77,  98,  0,   64,  83,  0,   51,  68,  0,   35,  51,
-    0,   12,  30,  0,   0,   0,   0,   112, 127, 0,   100, 112, 0,   89,  100,
-    0,   76,  87,  0,   64,  74,  0,   51,  59,  0,   35,  44,  0,   13,  24,
-    0,   0,   0,   0,   112, 111, 0,   100, 100, 0,   88,  88,  0,   76,  76,
-    0,   64,  64,  0,   51,  52,  0,   36,  37,  0,   13,  17,  0,   0,   0,
-    0,   111, 96,  0,   99,  85,  0,   88,  76,  0,   76,  64,  0,   64,  53,
-    0,   51,  41,  0,   36,  27,  0,   13,  6,   0,   0,   0,   0,   111, 81,
-    0,   99,  71,  0,   88,  62,  0,   76,  52,  0,   64,  43,  0,   51,  31,
-    0,   36,  17,  0,   13,  0,   0,   0,   0,   0,   111, 69,  0,   99,  60,
-    0,   88,  52,  0,   75,  43,  0,   63,  34,  0,   51,  21,  0,   36,  7,
-    0,   13,  0,   0,   0,   0,   0,   99,  177, 0,   88,  158, 0,   77,  141,
-    0,   66,  123, 0,   53,  106, 0,   40,  89,  0,   25,  71,  0,   5,   47,
-    0,   0,   8,   0,   99,  160, 0,   88,  144, 0,   77,  129, 0,   66,  112,
-    0,   54,  97,  0,   41,  80,  0,   26,  62,  0,   5,   40,  0,   0,   0,
-    0,   99,  147, 0,   87,  132, 0,   78,  117, 0,   66,  102, 0,   54,  87,
-    0,   42,  72,  0,   26,  55,  0,   5,   34,  0,   0,   0,   0,   99,  134,
-    0,   88,  119, 0,   77,  107, 0,   66,  92,  0,   54,  78,  0,   42,  64,
-    0,   27,  48,  0,   5,   27,  0,   0,   0,   0,   99,  120, 0,   87,  107,
-    0,   78,  94,  0,   66,  81,  0,   54,  68,  0,   42,  55,  0,   27,  40,
-    0,   6,   20,  0,   0,   0,   0,   98,  105, 0,   87,  94,  0,   77,  84,
-    0,   65,  71,  0,   55,  59,  0,   42,  47,  0,   28,  33,  0,   6,   12,
-    0,   0,   0,   0,   98,  93,  0,   87,  81,  0,   77,  72,  0,   66,  61,
-    0,   54,  49,  0,   42,  37,  0,   28,  24,  0,   6,   4,   0,   0,   0,
-    0,   98,  77,  0,   87,  68,  0,   77,  59,  0,   65,  49,  0,   54,  39,
-    0,   42,  27,  0,   29,  14,  0,   6,   0,   0,   0,   0,   1,   98,  65,
-    7,   87,  56,  0,   77,  49,  0,   66,  41,  0,   54,  30,  0,   42,  19,
-    0,   29,  3,   0,   6,   0,   0,   0,   0,   0,   84,  166, 0,   74,  149,
-    0,   64,  134, 0,   53,  117, 0,   41,  100, 0,   28,  83,  0,   11,  64,
-    0,   0,   42,  0,   0,   3,   0,   84,  151, 0,   74,  135, 0,   64,  121,
-    0,   53,  105, 0,   42,  90,  0,   30,  75,  0,   14,  58,  0,   0,   36,
-    0,   0,   0,   0,   84,  138, 0,   74,  124, 1,   64,  110, 0,   54,  95,
-    0,   43,  81,  0,   30,  67,  0,   15,  51,  0,   1,   29,  0,   0,   0,
-    14,  84,  126, 12,  74,  112, 2,   65,  99,  0,   54,  85,  0,   44,  73,
-    0,   31,  59,  0,   16,  44,  0,   1,   23,  0,   0,   0,   16,  84,  113,
-    13,  74,  100, 6,   65,  89,  0,   54,  77,  0,   44,  65,  0,   31,  51,
-    0,   17,  36,  0,   1,   16,  0,   0,   0,   24,  84,  100, 18,  74,  88,
-    13,  65,  78,  2,   55,  68,  0,   44,  55,  0,   32,  43,  0,   18,  28,
-    0,   1,   6,   0,   0,   0,   26,  84,  87,  24,  74,  76,  17,  65,  67,
-    7,   54,  57,  0,   44,  46,  0,   32,  35,  0,   19,  21,  0,   2,   3,
-    0,   0,   0,   30,  84,  74,  28,  74,  64,  20,  65,  55,  12,  55,  46,
-    0,   44,  35,  0,   32,  24,  0,   18,  9,   0,   1,   0,   0,   0,   0,
-    32,  84,  63,  28,  74,  54,  21,  65,  47,  13,  54,  38,  0,   44,  28,
-    0,   32,  16,  0,   18,  1,   0,   1,   0,   0,   0,   0,   30,  67,  155,
-    20,  58,  139, 20,  49,  126, 12,  39,  110, 0,   27,  94,  0,   13,  77,
-    0,   0,   60,  0,   0,   37,  0,   0,   0,   35,  67,  142, 30,  58,  126,
-    23,  50,  114, 16,  40,  99,  7,   29,  85,  0,   15,  69,  0,   0,   52,
-    0,   0,   30,  0,   0,   0,   35,  68,  131, 30,  59,  116, 27,  50,  104,
-    18,  40,  90,  9,   29,  76,  0,   17,  62,  0,   2,   46,  0,   0,   24,
-    0,   0,   0,   37,  69,  119, 33,  59,  106, 27,  51,  94,  21,  41,  80,
-    9,   30,  67,  0,   18,  54,  0,   3,   39,  0,   0,   18,  0,   0,   0,
-    40,  69,  107, 36,  59,  94,  28,  51,  84,  18,  41,  72,  10,  31,  60,
-    0,   19,  47,  0,   4,   32,  0,   0,   10,  0,   0,   0,   42,  69,  95,
-    36,  59,  84,  29,  51,  74,  19,  41,  63,  10,  31,  52,  0,   20,  39,
-    0,   4,   25,  0,   0,   4,   0,   0,   0,   43,  69,  83,  38,  60,  73,
-    32,  51,  62,  23,  42,  53,  11,  31,  42,  0,   20,  31,  0,   5,   17,
-    0,   0,   1,   0,   0,   0,   45,  69,  70,  39,  60,  60,  33,  51,  52,
-    24,  42,  43,  13,  32,  33,  0,   21,  21,  0,   5,   6,   0,   0,   0,
-    0,   0,   0,   47,  69,  59,  41,  60,  51,  34,  51,  43,  24,  42,  35,
-    12,  33,  26,  1,   22,  14,  0,   5,   1,   0,   0,   0,   0,   0,   0,
-    46,  48,  146, 42,  40,  131, 36,  32,  118, 27,  22,  103, 17,  6,   88,
-    5,   0,   73,  0,   0,   55,  0,   0,   33,  0,   0,   0,   48,  48,  133,
-    44,  40,  119, 37,  32,  107, 28,  22,  93,  20,  8,   79,  7,   0,   65,
-    0,   0,   49,  0,   0,   27,  0,   0,   0,   48,  50,  123, 44,  41,  109,
-    37,  33,  97,  30,  23,  83,  21,  11,  71,  8,   0,   58,  0,   0,   42,
-    0,   0,   21,  0,   0,   0,   49,  51,  111, 45,  42,  99,  38,  34,  87,
-    29,  25,  75,  20,  13,  63,  8,   0,   51,  0,   0,   36,  0,   0,   14,
-    0,   0,   0,   52,  52,  100, 44,  43,  89,  38,  35,  79,  29,  26,  68,
-    19,  15,  56,  10,  1,   43,  0,   0,   28,  0,   0,   6,   0,   0,   0,
-    52,  52,  90,  47,  44,  79,  39,  36,  70,  30,  27,  59,  20,  16,  47,
-    9,   2,   36,  0,   0,   22,  0,   0,   2,   0,   0,   0,   52,  53,  78,
-    46,  44,  68,  39,  37,  60,  32,  27,  49,  22,  17,  39,  10,  3,   28,
-    0,   0,   12,  0,   0,   0,   0,   0,   0,   53,  53,  66,  47,  44,  57,
-    40,  36,  48,  32,  27,  39,  22,  18,  30,  9,   4,   18,  0,   0,   3,
-    0,   0,   0,   0,   0,   0,   54,  53,  57,  48,  45,  49,  41,  37,  41,
-    33,  28,  32,  22,  19,  23,  11,  6,   10,  1,   0,   0,   0,   0,   0,
-    0,   0,   0,
-};
-
-}  // namespace
-
-CCodec_ModuleMgr::CCodec_ModuleMgr()
-    : m_pBasicModule(pdfium::MakeUnique<CCodec_BasicModule>()),
-      m_pFaxModule(pdfium::MakeUnique<CCodec_FaxModule>()),
-      m_pJpegModule(pdfium::MakeUnique<CCodec_JpegModule>()),
-      m_pJpxModule(pdfium::MakeUnique<CCodec_JpxModule>()),
-      m_pJbig2Module(pdfium::MakeUnique<CCodec_Jbig2Module>()),
-      m_pIccModule(pdfium::MakeUnique<CCodec_IccModule>()),
-      m_pFlateModule(pdfium::MakeUnique<CCodec_FlateModule>()) {}
-
-CCodec_ModuleMgr::~CCodec_ModuleMgr() {}
-
-#ifdef PDF_ENABLE_XFA
-void CCodec_ModuleMgr::SetBmpModule(std::unique_ptr<CCodec_BmpModule> module) {
-  m_pBmpModule = std::move(module);
-}
-
-void CCodec_ModuleMgr::SetGifModule(std::unique_ptr<CCodec_GifModule> module) {
-  m_pGifModule = std::move(module);
-}
-
-void CCodec_ModuleMgr::SetPngModule(std::unique_ptr<CCodec_PngModule> module) {
-  m_pPngModule = std::move(module);
-}
-
-void CCodec_ModuleMgr::SetTiffModule(
-    std::unique_ptr<CCodec_TiffModule> module) {
-  m_pTiffModule = std::move(module);
-}
-#endif  // PDF_ENABLE_XFA
-
-bool CCodec_BasicModule::RunLengthEncode(const uint8_t* src_buf,
-                                         uint32_t src_size,
-                                         uint8_t** dest_buf,
-                                         uint32_t* dest_size) {
-  // Check inputs
-  if (!src_buf || !dest_buf || !dest_size || src_size == 0)
-    return false;
-
-  // Edge case
-  if (src_size == 1) {
-    *dest_buf = FX_Alloc(uint8_t, 3);
-    (*dest_buf)[0] = 0;
-    (*dest_buf)[1] = src_buf[0];
-    (*dest_buf)[2] = 128;
-    *dest_size = 3;
-    return true;
-  }
-
-  // Worst case: 1 nonmatch, 2 match, 1 nonmatch, 2 match, etc. This becomes
-  // 4 output chars for every 3 input, plus up to 4 more for the 1-2 chars
-  // rounded off plus the terminating character.
-  uint32_t est_size = 4 * ((src_size + 2) / 3) + 1;
-  *dest_buf = FX_Alloc(uint8_t, est_size);
-
-  // Set up pointers.
-  uint8_t* out = *dest_buf;
-  uint32_t run_start = 0;
-  uint32_t run_end = 1;
-  uint8_t x = src_buf[run_start];
-  uint8_t y = src_buf[run_end];
-  while (run_end < src_size) {
-    uint32_t max_len = std::min((uint32_t)128, src_size - run_start);
-    while (x == y && (run_end - run_start < max_len - 1))
-      y = src_buf[++run_end];
-
-    // Reached end with matched run. Update variables to expected values.
-    if (x == y) {
-      run_end++;
-      if (run_end < src_size)
-        y = src_buf[run_end];
-    }
-    if (run_end - run_start > 1) {  // Matched run but not at end of input.
-      out[0] = 257 - (run_end - run_start);
-      out[1] = x;
-      x = y;
-      run_start = run_end;
-      run_end++;
-      if (run_end < src_size)
-        y = src_buf[run_end];
-      out += 2;
-      continue;
-    }
-    // Mismatched run
-    while (x != y && run_end <= run_start + max_len) {
-      out[run_end - run_start] = x;
-      x = y;
-      run_end++;
-      if (run_end == src_size) {
-        if (run_end <= run_start + max_len) {
-          out[run_end - run_start] = x;
-          run_end++;
-        }
-        break;
-      }
-      y = src_buf[run_end];
-    }
-    out[0] = run_end - run_start - 2;
-    out += run_end - run_start;
-    run_start = run_end - 1;
-  }
-  if (run_start < src_size) {  // 1 leftover character
-    out[0] = 0;
-    out[1] = x;
-    out += 2;
-  }
-  *out = 128;
-  *dest_size = out + 1 - *dest_buf;
-  return true;
-}
-
-bool CCodec_BasicModule::A85Encode(const uint8_t* src_buf,
-                                   uint32_t src_size,
-                                   uint8_t** dest_buf,
-                                   uint32_t* dest_size) {
-  // Check inputs.
-  if (!src_buf || !dest_buf || !dest_size)
-    return false;
-
-  if (src_size == 0) {
-    *dest_size = 0;
-    return false;
-  }
-
-  // Worst case: 5 output for each 4 input (plus up to 4 from leftover), plus
-  // 2 character new lines each 75 output chars plus 2 termination chars. May
-  // have fewer if there are special "z" chars.
-  uint32_t est_size = 5 * (src_size / 4) + 4 + src_size / 30 + 2;
-  *dest_buf = FX_Alloc(uint8_t, est_size);
-
-  // Set up pointers.
-  uint8_t* out = *dest_buf;
-  uint32_t pos = 0;
-  uint32_t line_length = 0;
-  while (src_size >= 4 && pos < src_size - 3) {
-    uint32_t val = ((uint32_t)(src_buf[pos]) << 24) +
-                   ((uint32_t)(src_buf[pos + 1]) << 16) +
-                   ((uint32_t)(src_buf[pos + 2]) << 8) +
-                   (uint32_t)(src_buf[pos + 3]);
-    pos += 4;
-    if (val == 0) {  // All zero special case
-      *out = 'z';
-      out++;
-      line_length++;
-    } else {  // Compute base 85 characters and add 33.
-      for (int i = 4; i >= 0; i--) {
-        out[i] = (uint8_t)(val % 85) + 33;
-        val = val / 85;
-      }
-      out += 5;
-      line_length += 5;
-    }
-    if (line_length >= 75) {  // Add a return.
-      *out++ = '\r';
-      *out++ = '\n';
-      line_length = 0;
-    }
-  }
-  if (pos < src_size) {  // Leftover bytes
-    uint32_t val = 0;
-    int count = 0;
-    while (pos < src_size) {
-      val += (uint32_t)(src_buf[pos]) << (8 * (3 - count));
-      count++;
-      pos++;
-    }
-    for (int i = 4; i >= 0; i--) {
-      if (i <= count)
-        out[i] = (uint8_t)(val % 85) + 33;
-      val = val / 85;
-    }
-    out += count + 1;
-  }
-
-  // Terminating characters.
-  out[0] = '~';
-  out[1] = '>';
-  out += 2;
-  *dest_size = out - *dest_buf;
-  return true;
-}
-
-#ifdef PDF_ENABLE_XFA
-CFX_DIBAttribute::CFX_DIBAttribute()
-    : m_nXDPI(-1),
-      m_nYDPI(-1),
-      m_fAspectRatio(-1.0f),
-      m_wDPIUnit(0),
-      m_nGifLeft(0),
-      m_nGifTop(0),
-      m_pGifLocalPalette(nullptr),
-      m_nGifLocalPalNum(0),
-      m_nBmpCompressType(0) {}
-
-CFX_DIBAttribute::~CFX_DIBAttribute() {
-  for (const auto& pair : m_Exif)
-    FX_Free(pair.second);
-}
-#endif  // PDF_ENABLE_XFA
-
-class CCodec_RLScanlineDecoder : public CCodec_ScanlineDecoder {
- public:
-  CCodec_RLScanlineDecoder();
-  ~CCodec_RLScanlineDecoder() override;
-
-  bool Create(const uint8_t* src_buf,
-              uint32_t src_size,
-              int width,
-              int height,
-              int nComps,
-              int bpc);
-
-  // CCodec_ScanlineDecoder
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
-  uint32_t GetSrcOffset() override { return m_SrcOffset; }
-
- protected:
-  bool CheckDestSize();
-  void GetNextOperator();
-  void UpdateOperator(uint8_t used_bytes);
-
-  uint8_t* m_pScanline;
-  const uint8_t* m_pSrcBuf;
-  uint32_t m_SrcSize;
-  uint32_t m_dwLineBytes;
-  uint32_t m_SrcOffset;
-  bool m_bEOD;
-  uint8_t m_Operator;
-};
-CCodec_RLScanlineDecoder::CCodec_RLScanlineDecoder()
-    : m_pScanline(nullptr),
-      m_pSrcBuf(nullptr),
-      m_SrcSize(0),
-      m_dwLineBytes(0),
-      m_SrcOffset(0),
-      m_bEOD(false),
-      m_Operator(0) {}
-CCodec_RLScanlineDecoder::~CCodec_RLScanlineDecoder() {
-  FX_Free(m_pScanline);
-}
-bool CCodec_RLScanlineDecoder::CheckDestSize() {
-  uint32_t i = 0;
-  uint32_t old_size = 0;
-  uint32_t dest_size = 0;
-  while (i < m_SrcSize) {
-    if (m_pSrcBuf[i] < 128) {
-      old_size = dest_size;
-      dest_size += m_pSrcBuf[i] + 1;
-      if (dest_size < old_size) {
-        return false;
-      }
-      i += m_pSrcBuf[i] + 2;
-    } else if (m_pSrcBuf[i] > 128) {
-      old_size = dest_size;
-      dest_size += 257 - m_pSrcBuf[i];
-      if (dest_size < old_size) {
-        return false;
-      }
-      i += 2;
-    } else {
-      break;
-    }
-  }
-  if (((uint32_t)m_OrigWidth * m_nComps * m_bpc * m_OrigHeight + 7) / 8 >
-      dest_size) {
-    return false;
-  }
-  return true;
-}
-bool CCodec_RLScanlineDecoder::Create(const uint8_t* src_buf,
-                                      uint32_t src_size,
-                                      int width,
-                                      int height,
-                                      int nComps,
-                                      int bpc) {
-  m_pSrcBuf = src_buf;
-  m_SrcSize = src_size;
-  m_OutputWidth = m_OrigWidth = width;
-  m_OutputHeight = m_OrigHeight = height;
-  m_nComps = nComps;
-  m_bpc = bpc;
-  // Aligning the pitch to 4 bytes requires an integer overflow check.
-  FX_SAFE_UINT32 pitch = width;
-  pitch *= nComps;
-  pitch *= bpc;
-  pitch += 31;
-  pitch /= 32;
-  pitch *= 4;
-  if (!pitch.IsValid()) {
-    return false;
-  }
-  m_Pitch = pitch.ValueOrDie();
-  // Overflow should already have been checked before this is called.
-  m_dwLineBytes = (static_cast<uint32_t>(width) * nComps * bpc + 7) / 8;
-  m_pScanline = FX_Alloc(uint8_t, m_Pitch);
-  return CheckDestSize();
-}
-bool CCodec_RLScanlineDecoder::v_Rewind() {
-  memset(m_pScanline, 0, m_Pitch);
-  m_SrcOffset = 0;
-  m_bEOD = false;
-  m_Operator = 0;
-  return true;
-}
-uint8_t* CCodec_RLScanlineDecoder::v_GetNextLine() {
-  if (m_SrcOffset == 0) {
-    GetNextOperator();
-  } else {
-    if (m_bEOD) {
-      return nullptr;
-    }
-  }
-  memset(m_pScanline, 0, m_Pitch);
-  uint32_t col_pos = 0;
-  bool eol = false;
-  while (m_SrcOffset < m_SrcSize && !eol) {
-    if (m_Operator < 128) {
-      uint32_t copy_len = m_Operator + 1;
-      if (col_pos + copy_len >= m_dwLineBytes) {
-        copy_len = m_dwLineBytes - col_pos;
-        eol = true;
-      }
-      if (copy_len >= m_SrcSize - m_SrcOffset) {
-        copy_len = m_SrcSize - m_SrcOffset;
-        m_bEOD = true;
-      }
-      memcpy(m_pScanline + col_pos, m_pSrcBuf + m_SrcOffset, copy_len);
-      col_pos += copy_len;
-      UpdateOperator((uint8_t)copy_len);
-    } else if (m_Operator > 128) {
-      int fill = 0;
-      if (m_SrcOffset - 1 < m_SrcSize - 1) {
-        fill = m_pSrcBuf[m_SrcOffset];
-      }
-      uint32_t duplicate_len = 257 - m_Operator;
-      if (col_pos + duplicate_len >= m_dwLineBytes) {
-        duplicate_len = m_dwLineBytes - col_pos;
-        eol = true;
-      }
-      memset(m_pScanline + col_pos, fill, duplicate_len);
-      col_pos += duplicate_len;
-      UpdateOperator((uint8_t)duplicate_len);
-    } else {
-      m_bEOD = true;
-      break;
-    }
-  }
-  return m_pScanline;
-}
-void CCodec_RLScanlineDecoder::GetNextOperator() {
-  if (m_SrcOffset >= m_SrcSize) {
-    m_Operator = 128;
-    return;
-  }
-  m_Operator = m_pSrcBuf[m_SrcOffset];
-  m_SrcOffset++;
-}
-void CCodec_RLScanlineDecoder::UpdateOperator(uint8_t used_bytes) {
-  if (used_bytes == 0) {
-    return;
-  }
-  if (m_Operator < 128) {
-    ASSERT((uint32_t)m_Operator + 1 >= used_bytes);
-    if (used_bytes == m_Operator + 1) {
-      m_SrcOffset += used_bytes;
-      GetNextOperator();
-      return;
-    }
-    m_Operator -= used_bytes;
-    m_SrcOffset += used_bytes;
-    if (m_SrcOffset >= m_SrcSize) {
-      m_Operator = 128;
-    }
-    return;
-  }
-  uint8_t count = 257 - m_Operator;
-  ASSERT((uint32_t)count >= used_bytes);
-  if (used_bytes == count) {
-    m_SrcOffset++;
-    GetNextOperator();
-    return;
-  }
-  count -= used_bytes;
-  m_Operator = 257 - count;
-}
-
-std::unique_ptr<CCodec_ScanlineDecoder>
-CCodec_BasicModule::CreateRunLengthDecoder(const uint8_t* src_buf,
-                                           uint32_t src_size,
-                                           int width,
-                                           int height,
-                                           int nComps,
-                                           int bpc) {
-  auto pDecoder = pdfium::MakeUnique<CCodec_RLScanlineDecoder>();
-  if (!pDecoder->Create(src_buf, src_size, width, height, nComps, bpc))
-    return nullptr;
-
-  return std::move(pDecoder);
-}
-
-std::tuple<uint8_t, uint8_t, uint8_t> AdobeCMYK_to_sRGB1(uint8_t c,
-                                                         uint8_t m,
-                                                         uint8_t y,
-                                                         uint8_t k) {
-  int fix_c = c << 8;
-  int fix_m = m << 8;
-  int fix_y = y << 8;
-  int fix_k = k << 8;
-  int c_index = (fix_c + 4096) >> 13;
-  int m_index = (fix_m + 4096) >> 13;
-  int y_index = (fix_y + 4096) >> 13;
-  int k_index = (fix_k + 4096) >> 13;
-  int pos = (c_index * 9 * 9 * 9 + m_index * 9 * 9 + y_index * 9 + k_index) * 3;
-  int fix_r = g_CMYK[pos] << 8;
-  int fix_g = g_CMYK[pos + 1] << 8;
-  int fix_b = g_CMYK[pos + 2] << 8;
-  int c1_index = fix_c >> 13;
-  if (c1_index == c_index)
-    c1_index = c1_index == 8 ? c1_index - 1 : c1_index + 1;
-  int m1_index = fix_m >> 13;
-  if (m1_index == m_index)
-    m1_index = m1_index == 8 ? m1_index - 1 : m1_index + 1;
-  int y1_index = fix_y >> 13;
-  if (y1_index == y_index)
-    y1_index = y1_index == 8 ? y1_index - 1 : y1_index + 1;
-  int k1_index = fix_k >> 13;
-  if (k1_index == k_index)
-    k1_index = k1_index == 8 ? k1_index - 1 : k1_index + 1;
-  int c1_pos = pos + (c1_index - c_index) * 9 * 9 * 9 * 3;
-  int m1_pos = pos + (m1_index - m_index) * 9 * 9 * 3;
-  int y1_pos = pos + (y1_index - y_index) * 9 * 3;
-  int k1_pos = pos + (k1_index - k_index) * 3;
-  int c_rate = (fix_c - (c_index << 13)) * (c_index - c1_index);
-  fix_r += (g_CMYK[pos] - g_CMYK[c1_pos]) * c_rate / 32;
-  fix_g += (g_CMYK[pos + 1] - g_CMYK[c1_pos + 1]) * c_rate / 32;
-  fix_b += (g_CMYK[pos + 2] - g_CMYK[c1_pos + 2]) * c_rate / 32;
-  int m_rate = (fix_m - (m_index << 13)) * (m_index - m1_index);
-  fix_r += (g_CMYK[pos] - g_CMYK[m1_pos]) * m_rate / 32;
-  fix_g += (g_CMYK[pos + 1] - g_CMYK[m1_pos + 1]) * m_rate / 32;
-  fix_b += (g_CMYK[pos + 2] - g_CMYK[m1_pos + 2]) * m_rate / 32;
-  int y_rate = (fix_y - (y_index << 13)) * (y_index - y1_index);
-  fix_r += (g_CMYK[pos] - g_CMYK[y1_pos]) * y_rate / 32;
-  fix_g += (g_CMYK[pos + 1] - g_CMYK[y1_pos + 1]) * y_rate / 32;
-  fix_b += (g_CMYK[pos + 2] - g_CMYK[y1_pos + 2]) * y_rate / 32;
-  int k_rate = (fix_k - (k_index << 13)) * (k_index - k1_index);
-  fix_r += (g_CMYK[pos] - g_CMYK[k1_pos]) * k_rate / 32;
-  fix_g += (g_CMYK[pos + 1] - g_CMYK[k1_pos + 1]) * k_rate / 32;
-  fix_b += (g_CMYK[pos + 2] - g_CMYK[k1_pos + 2]) * k_rate / 32;
-  fix_r = std::max(fix_r, 0);
-  fix_g = std::max(fix_g, 0);
-  fix_b = std::max(fix_b, 0);
-  return std::make_tuple(fix_r >> 8, fix_g >> 8, fix_b >> 8);
-}
-
-std::tuple<float, float, float> AdobeCMYK_to_sRGB(float c,
-                                                  float m,
-                                                  float y,
-                                                  float k) {
-  // Convert to uint8_t with round-to-nearest. Avoid using FXSYS_round because
-  // it is incredibly expensive with VC++ (tested on VC++ 2015) because round()
-  // is very expensive.
-  // The 'magic' value of 0.49999997f, the float that precedes 0.5f, was chosen
-  // because it gives identical results to FXSYS_round(). Using the constant
-  // 0.5f gives different results (1 instead of 0) for one value, 0.0019607842.
-  // That value is close to the cusp but zero is the correct answer, and
-  // getting the same answer as before is desirable.
-  // All floats from 0.0 to 1.0 were tested and now give the same results.
-  const float rounding_offset = 0.49999997f;
-  uint8_t c1 = int(c * 255.f + rounding_offset);
-  uint8_t m1 = int(m * 255.f + rounding_offset);
-  uint8_t y1 = int(y * 255.f + rounding_offset);
-  uint8_t k1 = int(k * 255.f + rounding_offset);
-
-  ASSERT(c1 == FXSYS_round(c * 255));
-  ASSERT(m1 == FXSYS_round(m * 255));
-  ASSERT(y1 == FXSYS_round(y * 255));
-  ASSERT(k1 == FXSYS_round(k * 255));
-
-  uint8_t r;
-  uint8_t g;
-  uint8_t b;
-  std::tie(r, g, b) = AdobeCMYK_to_sRGB1(c1, m1, y1, k1);
-  // Multiply by a constant rather than dividing because division is much
-  // more expensive.
-  return std::make_tuple(r * (1.0f / 255), g * (1.0f / 255), b * (1.0f / 255));
-}
diff --git a/core/fxcodec/codec/fx_codec_a85_unittest.cpp b/core/fxcodec/codec/fx_codec_a85_unittest.cpp
deleted file mode 100644
index 5e40548..0000000
--- a/core/fxcodec/codec/fx_codec_a85_unittest.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdint.h>
-
-#include <limits>
-
-#include "core/fxcodec/codec/ccodec_basicmodule.h"
-#include "core/fxcodec/fx_codec.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TEST(fxcodec, A85TestBadInputs) {
-  const uint8_t src_buf[] = {1, 2, 3, 4};
-  uint8_t* dest_buf = nullptr;
-  uint32_t src_size = 4;
-  uint32_t dest_size = 0;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Error codes, not segvs, should callers pass us a nullptr pointer.
-  EXPECT_FALSE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, nullptr));
-  EXPECT_FALSE(pEncoders->A85Encode(src_buf, src_size, nullptr, &dest_size));
-  EXPECT_FALSE(pEncoders->A85Encode(src_buf, 0, &dest_buf, &dest_size));
-  EXPECT_FALSE(pEncoders->A85Encode(nullptr, src_size, &dest_buf, &dest_size));
-}
-
-// No leftover bytes, just translate 2 sets of symbols.
-TEST(fxcodec, A85TestBasic) {
-  // Make sure really big values don't break.
-  const uint8_t src_buf[] = {1, 2, 3, 4, 255, 255, 255, 255};
-  uint8_t* dest_buf = nullptr;
-  uint32_t src_size = 8;
-  uint32_t dest_size = 0;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Should succeed.
-  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
-
-  // Should have 5 chars for each set of 4 and 2 terminators.
-  ASSERT_EQ(12u, dest_size);
-  const uint8_t expected_out[] = {33, 60, 78, 63, 43,  115,
-                                  56, 87, 45, 33, 126, 62};
-
-  // Check the output
-  for (uint32_t i = 0; i < 12; i++)
-    EXPECT_EQ(expected_out[i], dest_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-}
-
-// Leftover bytes.
-TEST(fxcodec, A85TestLeftoverBytes) {
-  // 1 Leftover Byte:
-  const uint8_t src_buf_1leftover[] = {1, 2, 3, 4, 255};
-  uint8_t* dest_buf = nullptr;
-  uint32_t src_size = 5;
-  uint32_t dest_size = 0;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Should succeed
-  EXPECT_TRUE(
-      pEncoders->A85Encode(src_buf_1leftover, src_size, &dest_buf, &dest_size));
-  ASSERT_EQ(9u, dest_size);  // 5 chars for first symbol + 2 + 2 terminators.
-  uint8_t expected_out_1leftover[] = {33, 60, 78, 63, 43, 114, 114, 126, 62};
-
-  // Check the output
-  for (uint32_t i = 0; i < 9; i++)
-    EXPECT_EQ(expected_out_1leftover[i], dest_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-
-  // 2 Leftover bytes:
-  src_size++;
-  dest_buf = nullptr;
-  dest_size = 0;
-  const uint8_t src_buf_2leftover[] = {1, 2, 3, 4, 255, 254};
-  // Should succeed
-  EXPECT_TRUE(
-      pEncoders->A85Encode(src_buf_2leftover, src_size, &dest_buf, &dest_size));
-  ASSERT_EQ(10u, dest_size);  // 5 chars for first symbol + 3 + 2 terminators.
-  const uint8_t expected_out_2leftover[] = {33,  60, 78, 63,  43,
-                                            115, 56, 68, 126, 62};
-
-  // Check the output
-  for (uint32_t i = 0; i < 10; i++)
-    EXPECT_EQ(expected_out_2leftover[i], dest_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-
-  // 3 Leftover bytes:
-  src_size++;
-  dest_buf = nullptr;
-  dest_size = 0;
-  const uint8_t src_buf_3leftover[] = {1, 2, 3, 4, 255, 254, 253};
-  // Should succeed
-  EXPECT_TRUE(
-      pEncoders->A85Encode(src_buf_3leftover, src_size, &dest_buf, &dest_size));
-  ASSERT_EQ(11u, dest_size);  // 5 chars for first symbol + 4 + 2 terminators.
-  const uint8_t expected_out_3leftover[] = {33, 60, 78,  63,  43, 115,
-                                            56, 77, 114, 126, 62};
-
-  // Check the output
-  for (uint32_t i = 0; i < 11; i++)
-    EXPECT_EQ(expected_out_3leftover[i], dest_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-}
-
-// Test all zeros comes through as "z".
-TEST(fxcodec, A85TestZeros) {
-  // Make sure really big values don't break.
-  const uint8_t src_buf[] = {1, 2, 3, 4, 0, 0, 0, 0};
-  uint8_t* dest_buf = nullptr;
-  uint32_t src_size = 8;
-  uint32_t dest_size = 0;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Should succeed.
-  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
-
-  // Should have 5 chars for first set of 4 + 1 for z + 2 terminators.
-  ASSERT_EQ(8u, dest_size);
-  const uint8_t expected_out[] = {33, 60, 78, 63, 43, 122, 126, 62};
-
-  // Check the output
-  for (uint32_t i = 0; i < 8; i++)
-    EXPECT_EQ(expected_out[i], dest_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-
-  // Should also work if it is at the start:
-  dest_buf = nullptr;
-  dest_size = 0;
-  const uint8_t src_buf_2[] = {0, 0, 0, 0, 1, 2, 3, 4};
-
-  // Should succeed.
-  EXPECT_TRUE(pEncoders->A85Encode(src_buf_2, src_size, &dest_buf, &dest_size));
-
-  // Should have 5 chars for set of 4 + 1 for z + 2 terminators.
-  ASSERT_EQ(8u, dest_size);
-  const uint8_t expected_out_2[] = {122, 33, 60, 78, 63, 43, 126, 62};
-
-  // Check the output
-  for (uint32_t i = 0; i < 8; i++)
-    EXPECT_EQ(expected_out_2[i], dest_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-
-  // Try with 2 leftover zero bytes. Make sure we don't get a "z".
-  src_size = 6;  // Cut off the last 2 zeros.
-  dest_buf = nullptr;
-  dest_size = 0;
-
-  // Should succeed.
-  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
-
-  // Should have 5 chars for set of 4 + 3 for last 2 + 2 terminators.
-  ASSERT_EQ(10u, dest_size);
-  const uint8_t expected_out_leftover[] = {33, 60, 78, 63,  43,
-                                           33, 33, 33, 126, 62};
-
-  // Check the output
-  for (uint32_t i = 0; i < 10; i++)
-    EXPECT_EQ(expected_out_leftover[i], dest_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-}
-
-// Make sure we get returns in the expected locations.
-TEST(fxcodec, A85TestLineBreaks) {
-  // Make sure really big values don't break.
-  uint8_t src_buf[131] = {0};
-  // 1 full line + most of a line of normal symbols.
-  for (int k = 0; k < 116; k += 4) {
-    src_buf[k] = 1;
-    src_buf[k + 1] = 2;
-    src_buf[k + 2] = 3;
-    src_buf[k + 3] = 4;
-  }
-  // Fill in the end, leaving an all zero gap + 3 extra zeros at the end.
-  for (int k = 120; k < 128; k++) {
-    src_buf[k] = 1;
-    src_buf[k + 1] = 2;
-    src_buf[k + 2] = 3;
-    src_buf[k + 3] = 4;
-  }
-  uint8_t* dest_buf = nullptr;
-  uint32_t src_size = 131;
-  uint32_t dest_size = 0;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Should succeed.
-  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
-
-  // Should have 75 chars in the first row plus 2 char return,
-  // 76 chars in the second row plus 2 char return,
-  // and 9 chars in the last row with 2 terminators.
-  ASSERT_EQ(166u, dest_size);
-
-  // Check for the returns.
-  EXPECT_EQ(13, dest_buf[75]);
-  EXPECT_EQ(10, dest_buf[76]);
-  EXPECT_EQ(13, dest_buf[153]);
-  EXPECT_EQ(10, dest_buf[154]);
-
-  FX_Free(dest_buf);
-}
diff --git a/core/fxcodec/codec/fx_codec_embeddertest.cpp b/core/fxcodec/codec/fx_codec_embeddertest.cpp
deleted file mode 100644
index fd02f97..0000000
--- a/core/fxcodec/codec/fx_codec_embeddertest.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class FXCodecEmbeddertest : public EmbedderTest {};
-
-TEST_F(FXCodecEmbeddertest, Bug_631912) {
-  // Test jbig2 image in PDF file can be loaded successfully.
-  // Should not crash.
-  EXPECT_TRUE(OpenDocument("bug_631912.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 691, 432, "24d75af646f8772c5ee7ced260452ae4");
-  FPDFBitmap_Destroy(bitmap);
-  UnloadPage(page);
-}
diff --git a/core/fxcodec/codec/fx_codec_fax.cpp b/core/fxcodec/codec/fx_codec_fax.cpp
deleted file mode 100644
index c800fda..0000000
--- a/core/fxcodec/codec/fx_codec_fax.cpp
+++ /dev/null
@@ -1,804 +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 <algorithm>
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/codec/ccodec_faxmodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcrt/cfx_binarybuf.h"
-#include "core/fxcrt/fx_memory.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-const uint8_t OneLeadPos[256] = {
-    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3,
-    3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-const uint8_t ZeroLeadPos[256] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-    2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,
-};
-
-// Limit of image dimension, an arbitrary large number.
-const int kMaxImageDimension = 0x01FFFF;
-
-int FindBit(const uint8_t* data_buf, int max_pos, int start_pos, int bit) {
-  ASSERT(start_pos >= 0);
-  if (start_pos >= max_pos)
-    return max_pos;
-
-  const uint8_t* leading_pos = bit ? OneLeadPos : ZeroLeadPos;
-  if (start_pos % 8) {
-    uint8_t data = data_buf[start_pos / 8];
-    if (bit)
-      data &= 0xff >> (start_pos % 8);
-    else
-      data |= 0xff << (8 - start_pos % 8);
-
-    if (leading_pos[data] < 8)
-      return start_pos / 8 * 8 + leading_pos[data];
-
-    start_pos += 7;
-  }
-  uint8_t skip = bit ? 0x00 : 0xff;
-  int byte_pos = start_pos / 8;
-  int max_byte = (max_pos + 7) / 8;
-  while (byte_pos < max_byte) {
-    if (data_buf[byte_pos] != skip)
-      break;
-
-    ++byte_pos;
-  }
-  if (byte_pos == max_byte)
-    return max_pos;
-
-  return std::min(leading_pos[data_buf[byte_pos]] + byte_pos * 8, max_pos);
-}
-
-void FaxG4FindB1B2(const std::vector<uint8_t>& ref_buf,
-                   int columns,
-                   int a0,
-                   bool a0color,
-                   int* b1,
-                   int* b2) {
-  uint8_t first_bit =
-      (a0 < 0) ? 1 : ((ref_buf[a0 / 8] & (1 << (7 - a0 % 8))) != 0);
-  *b1 = FindBit(ref_buf.data(), columns, a0 + 1, !first_bit);
-  if (*b1 >= columns) {
-    *b1 = *b2 = columns;
-    return;
-  }
-  if (first_bit == !a0color) {
-    *b1 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit);
-    first_bit = !first_bit;
-  }
-  if (*b1 >= columns) {
-    *b1 = *b2 = columns;
-    return;
-  }
-  *b2 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit);
-}
-
-void FaxFillBits(uint8_t* dest_buf, int columns, int startpos, int endpos) {
-  startpos = std::max(startpos, 0);
-  endpos = pdfium::clamp(endpos, 0, columns);
-  if (startpos >= endpos)
-    return;
-
-  int first_byte = startpos / 8;
-  int last_byte = (endpos - 1) / 8;
-  if (first_byte == last_byte) {
-    for (int i = startpos % 8; i <= (endpos - 1) % 8; ++i)
-      dest_buf[first_byte] -= 1 << (7 - i);
-    return;
-  }
-
-  for (int i = startpos % 8; i < 8; ++i)
-    dest_buf[first_byte] -= 1 << (7 - i);
-  for (int i = 0; i <= (endpos - 1) % 8; ++i)
-    dest_buf[last_byte] -= 1 << (7 - i);
-
-  if (last_byte > first_byte + 1)
-    memset(dest_buf + first_byte + 1, 0, last_byte - first_byte - 1);
-}
-
-inline bool NextBit(const uint8_t* src_buf, int* bitpos) {
-  int pos = (*bitpos)++;
-  return !!(src_buf[pos / 8] & (1 << (7 - pos % 8)));
-}
-
-const uint8_t FaxBlackRunIns[] = {
-    0,          2,          0x02,       3,          0,          0x03,
-    2,          0,          2,          0x02,       1,          0,
-    0x03,       4,          0,          2,          0x02,       6,
-    0,          0x03,       5,          0,          1,          0x03,
-    7,          0,          2,          0x04,       9,          0,
-    0x05,       8,          0,          3,          0x04,       10,
-    0,          0x05,       11,         0,          0x07,       12,
-    0,          2,          0x04,       13,         0,          0x07,
-    14,         0,          1,          0x18,       15,         0,
-    5,          0x08,       18,         0,          0x0f,       64,
-    0,          0x17,       16,         0,          0x18,       17,
-    0,          0x37,       0,          0,          10,         0x08,
-    0x00,       0x07,       0x0c,       0x40,       0x07,       0x0d,
-    0x80,       0x07,       0x17,       24,         0,          0x18,
-    25,         0,          0x28,       23,         0,          0x37,
-    22,         0,          0x67,       19,         0,          0x68,
-    20,         0,          0x6c,       21,         0,          54,
-    0x12,       1984 % 256, 1984 / 256, 0x13,       2048 % 256, 2048 / 256,
-    0x14,       2112 % 256, 2112 / 256, 0x15,       2176 % 256, 2176 / 256,
-    0x16,       2240 % 256, 2240 / 256, 0x17,       2304 % 256, 2304 / 256,
-    0x1c,       2368 % 256, 2368 / 256, 0x1d,       2432 % 256, 2432 / 256,
-    0x1e,       2496 % 256, 2496 / 256, 0x1f,       2560 % 256, 2560 / 256,
-    0x24,       52,         0,          0x27,       55,         0,
-    0x28,       56,         0,          0x2b,       59,         0,
-    0x2c,       60,         0,          0x33,       320 % 256,  320 / 256,
-    0x34,       384 % 256,  384 / 256,  0x35,       448 % 256,  448 / 256,
-    0x37,       53,         0,          0x38,       54,         0,
-    0x52,       50,         0,          0x53,       51,         0,
-    0x54,       44,         0,          0x55,       45,         0,
-    0x56,       46,         0,          0x57,       47,         0,
-    0x58,       57,         0,          0x59,       58,         0,
-    0x5a,       61,         0,          0x5b,       256 % 256,  256 / 256,
-    0x64,       48,         0,          0x65,       49,         0,
-    0x66,       62,         0,          0x67,       63,         0,
-    0x68,       30,         0,          0x69,       31,         0,
-    0x6a,       32,         0,          0x6b,       33,         0,
-    0x6c,       40,         0,          0x6d,       41,         0,
-    0xc8,       128,        0,          0xc9,       192,        0,
-    0xca,       26,         0,          0xcb,       27,         0,
-    0xcc,       28,         0,          0xcd,       29,         0,
-    0xd2,       34,         0,          0xd3,       35,         0,
-    0xd4,       36,         0,          0xd5,       37,         0,
-    0xd6,       38,         0,          0xd7,       39,         0,
-    0xda,       42,         0,          0xdb,       43,         0,
-    20,         0x4a,       640 % 256,  640 / 256,  0x4b,       704 % 256,
-    704 / 256,  0x4c,       768 % 256,  768 / 256,  0x4d,       832 % 256,
-    832 / 256,  0x52,       1280 % 256, 1280 / 256, 0x53,       1344 % 256,
-    1344 / 256, 0x54,       1408 % 256, 1408 / 256, 0x55,       1472 % 256,
-    1472 / 256, 0x5a,       1536 % 256, 1536 / 256, 0x5b,       1600 % 256,
-    1600 / 256, 0x64,       1664 % 256, 1664 / 256, 0x65,       1728 % 256,
-    1728 / 256, 0x6c,       512 % 256,  512 / 256,  0x6d,       576 % 256,
-    576 / 256,  0x72,       896 % 256,  896 / 256,  0x73,       960 % 256,
-    960 / 256,  0x74,       1024 % 256, 1024 / 256, 0x75,       1088 % 256,
-    1088 / 256, 0x76,       1152 % 256, 1152 / 256, 0x77,       1216 % 256,
-    1216 / 256, 0xff};
-
-const uint8_t FaxWhiteRunIns[] = {
-    0,          0,          0,          6,          0x07,       2,
-    0,          0x08,       3,          0,          0x0B,       4,
-    0,          0x0C,       5,          0,          0x0E,       6,
-    0,          0x0F,       7,          0,          6,          0x07,
-    10,         0,          0x08,       11,         0,          0x12,
-    128,        0,          0x13,       8,          0,          0x14,
-    9,          0,          0x1b,       64,         0,          9,
-    0x03,       13,         0,          0x07,       1,          0,
-    0x08,       12,         0,          0x17,       192,        0,
-    0x18,       1664 % 256, 1664 / 256, 0x2a,       16,         0,
-    0x2B,       17,         0,          0x34,       14,         0,
-    0x35,       15,         0,          12,         0x03,       22,
-    0,          0x04,       23,         0,          0x08,       20,
-    0,          0x0c,       19,         0,          0x13,       26,
-    0,          0x17,       21,         0,          0x18,       28,
-    0,          0x24,       27,         0,          0x27,       18,
-    0,          0x28,       24,         0,          0x2B,       25,
-    0,          0x37,       256 % 256,  256 / 256,  42,         0x02,
-    29,         0,          0x03,       30,         0,          0x04,
-    45,         0,          0x05,       46,         0,          0x0a,
-    47,         0,          0x0b,       48,         0,          0x12,
-    33,         0,          0x13,       34,         0,          0x14,
-    35,         0,          0x15,       36,         0,          0x16,
-    37,         0,          0x17,       38,         0,          0x1a,
-    31,         0,          0x1b,       32,         0,          0x24,
-    53,         0,          0x25,       54,         0,          0x28,
-    39,         0,          0x29,       40,         0,          0x2a,
-    41,         0,          0x2b,       42,         0,          0x2c,
-    43,         0,          0x2d,       44,         0,          0x32,
-    61,         0,          0x33,       62,         0,          0x34,
-    63,         0,          0x35,       0,          0,          0x36,
-    320 % 256,  320 / 256,  0x37,       384 % 256,  384 / 256,  0x4a,
-    59,         0,          0x4b,       60,         0,          0x52,
-    49,         0,          0x53,       50,         0,          0x54,
-    51,         0,          0x55,       52,         0,          0x58,
-    55,         0,          0x59,       56,         0,          0x5a,
-    57,         0,          0x5b,       58,         0,          0x64,
-    448 % 256,  448 / 256,  0x65,       512 % 256,  512 / 256,  0x67,
-    640 % 256,  640 / 256,  0x68,       576 % 256,  576 / 256,  16,
-    0x98,       1472 % 256, 1472 / 256, 0x99,       1536 % 256, 1536 / 256,
-    0x9a,       1600 % 256, 1600 / 256, 0x9b,       1728 % 256, 1728 / 256,
-    0xcc,       704 % 256,  704 / 256,  0xcd,       768 % 256,  768 / 256,
-    0xd2,       832 % 256,  832 / 256,  0xd3,       896 % 256,  896 / 256,
-    0xd4,       960 % 256,  960 / 256,  0xd5,       1024 % 256, 1024 / 256,
-    0xd6,       1088 % 256, 1088 / 256, 0xd7,       1152 % 256, 1152 / 256,
-    0xd8,       1216 % 256, 1216 / 256, 0xd9,       1280 % 256, 1280 / 256,
-    0xda,       1344 % 256, 1344 / 256, 0xdb,       1408 % 256, 1408 / 256,
-    0,          3,          0x08,       1792 % 256, 1792 / 256, 0x0c,
-    1856 % 256, 1856 / 256, 0x0d,       1920 % 256, 1920 / 256, 10,
-    0x12,       1984 % 256, 1984 / 256, 0x13,       2048 % 256, 2048 / 256,
-    0x14,       2112 % 256, 2112 / 256, 0x15,       2176 % 256, 2176 / 256,
-    0x16,       2240 % 256, 2240 / 256, 0x17,       2304 % 256, 2304 / 256,
-    0x1c,       2368 % 256, 2368 / 256, 0x1d,       2432 % 256, 2432 / 256,
-    0x1e,       2496 % 256, 2496 / 256, 0x1f,       2560 % 256, 2560 / 256,
-    0xff,
-};
-
-int FaxGetRun(const uint8_t* ins_array,
-              const uint8_t* src_buf,
-              int* bitpos,
-              int bitsize) {
-  uint32_t code = 0;
-  int ins_off = 0;
-  while (1) {
-    uint8_t ins = ins_array[ins_off++];
-    if (ins == 0xff)
-      return -1;
-
-    if (*bitpos >= bitsize)
-      return -1;
-
-    code <<= 1;
-    if (src_buf[*bitpos / 8] & (1 << (7 - *bitpos % 8)))
-      ++code;
-
-    ++(*bitpos);
-    int next_off = ins_off + ins * 3;
-    for (; ins_off < next_off; ins_off += 3) {
-      if (ins_array[ins_off] == code)
-        return ins_array[ins_off + 1] + ins_array[ins_off + 2] * 256;
-    }
-  }
-}
-
-void FaxG4GetRow(const uint8_t* src_buf,
-                 int bitsize,
-                 int* bitpos,
-                 uint8_t* dest_buf,
-                 const std::vector<uint8_t>& ref_buf,
-                 int columns) {
-  int a0 = -1;
-  bool a0color = true;
-  while (1) {
-    if (*bitpos >= bitsize)
-      return;
-
-    int a1;
-    int a2;
-    int b1;
-    int b2;
-    FaxG4FindB1B2(ref_buf, columns, a0, a0color, &b1, &b2);
-
-    int v_delta = 0;
-    if (!NextBit(src_buf, bitpos)) {
-      if (*bitpos >= bitsize)
-        return;
-
-      bool bit1 = NextBit(src_buf, bitpos);
-      if (*bitpos >= bitsize)
-        return;
-
-      bool bit2 = NextBit(src_buf, bitpos);
-      if (bit1) {
-        v_delta = bit2 ? 1 : -1;
-      } else if (bit2) {
-        int run_len1 = 0;
-        while (1) {
-          int run = FaxGetRun(a0color ? FaxWhiteRunIns : FaxBlackRunIns,
-                              src_buf, bitpos, bitsize);
-          run_len1 += run;
-          if (run < 64)
-            break;
-        }
-        if (a0 < 0)
-          ++run_len1;
-        if (run_len1 < 0)
-          return;
-
-        a1 = a0 + run_len1;
-        if (!a0color)
-          FaxFillBits(dest_buf, columns, a0, a1);
-
-        int run_len2 = 0;
-        while (1) {
-          int run = FaxGetRun(a0color ? FaxBlackRunIns : FaxWhiteRunIns,
-                              src_buf, bitpos, bitsize);
-          run_len2 += run;
-          if (run < 64)
-            break;
-        }
-        if (run_len2 < 0)
-          return;
-        a2 = a1 + run_len2;
-        if (a0color)
-          FaxFillBits(dest_buf, columns, a1, a2);
-
-        a0 = a2;
-        if (a0 < columns)
-          continue;
-
-        return;
-      } else {
-        if (*bitpos >= bitsize)
-          return;
-
-        if (NextBit(src_buf, bitpos)) {
-          if (!a0color)
-            FaxFillBits(dest_buf, columns, a0, b2);
-
-          if (b2 >= columns)
-            return;
-
-          a0 = b2;
-          continue;
-        }
-
-        if (*bitpos >= bitsize)
-          return;
-
-        bool next_bit1 = NextBit(src_buf, bitpos);
-        if (*bitpos >= bitsize)
-          return;
-
-        bool next_bit2 = NextBit(src_buf, bitpos);
-        if (next_bit1) {
-          v_delta = next_bit2 ? 2 : -2;
-        } else if (next_bit2) {
-          if (*bitpos >= bitsize)
-            return;
-
-          v_delta = NextBit(src_buf, bitpos) ? 3 : -3;
-        } else {
-          if (*bitpos >= bitsize)
-            return;
-
-          if (NextBit(src_buf, bitpos)) {
-            *bitpos += 3;
-            continue;
-          }
-          *bitpos += 5;
-          return;
-        }
-      }
-    }
-    a1 = b1 + v_delta;
-    if (!a0color)
-      FaxFillBits(dest_buf, columns, a0, a1);
-
-    if (a1 >= columns)
-      return;
-
-    // The position of picture element must be monotonic increasing.
-    if (a0 >= a1)
-      return;
-
-    a0 = a1;
-    a0color = !a0color;
-  }
-}
-
-void FaxSkipEOL(const uint8_t* src_buf, int bitsize, int* bitpos) {
-  int startbit = *bitpos;
-  while (*bitpos < bitsize) {
-    if (!NextBit(src_buf, bitpos))
-      continue;
-    if (*bitpos - startbit <= 11)
-      *bitpos = startbit;
-    return;
-  }
-}
-
-void FaxGet1DLine(const uint8_t* src_buf,
-                  int bitsize,
-                  int* bitpos,
-                  std::vector<uint8_t>* dest_buf,
-                  int columns) {
-  bool color = true;
-  int startpos = 0;
-  while (1) {
-    if (*bitpos >= bitsize)
-      return;
-
-    int run_len = 0;
-    while (1) {
-      int run = FaxGetRun(color ? FaxWhiteRunIns : FaxBlackRunIns, src_buf,
-                          bitpos, bitsize);
-      if (run < 0) {
-        while (*bitpos < bitsize) {
-          if (NextBit(src_buf, bitpos))
-            return;
-        }
-        return;
-      }
-      run_len += run;
-      if (run < 64)
-        break;
-    }
-    if (!color)
-      FaxFillBits(dest_buf->data(), columns, startpos, startpos + run_len);
-
-    startpos += run_len;
-    if (startpos >= columns)
-      break;
-
-    color = !color;
-  }
-}
-
-}  // namespace
-
-class CCodec_FaxDecoder : public CCodec_ScanlineDecoder {
- public:
-  CCodec_FaxDecoder(const uint8_t* src_buf,
-                    uint32_t src_size,
-                    int width,
-                    int height,
-                    uint32_t pitch,
-                    int K,
-                    bool EndOfLine,
-                    bool EncodedByteAlign,
-                    bool BlackIs1);
-  ~CCodec_FaxDecoder() override;
-
-  // CCodec_ScanlineDecoder
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
-  uint32_t GetSrcOffset() override;
-
- private:
-  const int m_Encoding;
-  int m_bitpos;
-  bool m_bByteAlign;
-  const bool m_bEndOfLine;
-  const bool m_bBlack;
-  const uint32_t m_SrcSize;
-  const uint8_t* const m_pSrcBuf;
-  std::vector<uint8_t> m_ScanlineBuf;
-  std::vector<uint8_t> m_RefBuf;
-};
-
-CCodec_FaxDecoder::CCodec_FaxDecoder(const uint8_t* src_buf,
-                                     uint32_t src_size,
-                                     int width,
-                                     int height,
-                                     uint32_t pitch,
-                                     int K,
-                                     bool EndOfLine,
-                                     bool EncodedByteAlign,
-                                     bool BlackIs1)
-    : CCodec_ScanlineDecoder(width, height, width, height, 1, 1, pitch),
-      m_Encoding(K),
-      m_bitpos(0),
-      m_bByteAlign(EncodedByteAlign),
-      m_bEndOfLine(EndOfLine),
-      m_bBlack(BlackIs1),
-      m_SrcSize(src_size),
-      m_pSrcBuf(src_buf),
-      m_ScanlineBuf(pitch),
-      m_RefBuf(pitch) {}
-
-CCodec_FaxDecoder::~CCodec_FaxDecoder() {}
-
-bool CCodec_FaxDecoder::v_Rewind() {
-  memset(m_RefBuf.data(), 0xff, m_RefBuf.size());
-  m_bitpos = 0;
-  return true;
-}
-
-uint8_t* CCodec_FaxDecoder::v_GetNextLine() {
-  int bitsize = m_SrcSize * 8;
-  FaxSkipEOL(m_pSrcBuf, bitsize, &m_bitpos);
-  if (m_bitpos >= bitsize)
-    return nullptr;
-
-  memset(m_ScanlineBuf.data(), 0xff, m_ScanlineBuf.size());
-  if (m_Encoding < 0) {
-    FaxG4GetRow(m_pSrcBuf, bitsize, &m_bitpos, m_ScanlineBuf.data(), m_RefBuf,
-                m_OrigWidth);
-    m_RefBuf = m_ScanlineBuf;
-  } else if (m_Encoding == 0) {
-    FaxGet1DLine(m_pSrcBuf, bitsize, &m_bitpos, &m_ScanlineBuf, m_OrigWidth);
-  } else {
-    if (NextBit(m_pSrcBuf, &m_bitpos)) {
-      FaxGet1DLine(m_pSrcBuf, bitsize, &m_bitpos, &m_ScanlineBuf, m_OrigWidth);
-    } else {
-      FaxG4GetRow(m_pSrcBuf, bitsize, &m_bitpos, m_ScanlineBuf.data(), m_RefBuf,
-                  m_OrigWidth);
-    }
-    m_RefBuf = m_ScanlineBuf;
-  }
-  if (m_bEndOfLine)
-    FaxSkipEOL(m_pSrcBuf, bitsize, &m_bitpos);
-
-  if (m_bByteAlign && m_bitpos < bitsize) {
-    int bitpos0 = m_bitpos;
-    int bitpos1 = (m_bitpos + 7) / 8 * 8;
-    while (m_bByteAlign && bitpos0 < bitpos1) {
-      int bit = m_pSrcBuf[bitpos0 / 8] & (1 << (7 - bitpos0 % 8));
-      if (bit != 0)
-        m_bByteAlign = false;
-      else
-        ++bitpos0;
-    }
-    if (m_bByteAlign)
-      m_bitpos = bitpos1;
-  }
-  if (m_bBlack) {
-    for (uint32_t i = 0; i < m_Pitch; ++i)
-      m_ScanlineBuf[i] = ~m_ScanlineBuf[i];
-  }
-  return m_ScanlineBuf.data();
-}
-
-uint32_t CCodec_FaxDecoder::GetSrcOffset() {
-  return std::min(static_cast<uint32_t>((m_bitpos + 7) / 8), m_SrcSize);
-}
-
-void FaxG4Decode(const uint8_t* src_buf,
-                 uint32_t src_size,
-                 int* pbitpos,
-                 uint8_t* dest_buf,
-                 int width,
-                 int height,
-                 int pitch) {
-  if (pitch == 0)
-    pitch = (width + 7) / 8;
-
-  std::vector<uint8_t> ref_buf(pitch, 0xff);
-  int bitpos = *pbitpos;
-  for (int iRow = 0; iRow < height; iRow++) {
-    uint8_t* line_buf = dest_buf + iRow * pitch;
-    memset(line_buf, 0xff, pitch);
-    FaxG4GetRow(src_buf, src_size << 3, &bitpos, line_buf, ref_buf, width);
-    memcpy(ref_buf.data(), line_buf, pitch);
-  }
-  *pbitpos = bitpos;
-}
-
-std::unique_ptr<CCodec_ScanlineDecoder> CCodec_FaxModule::CreateDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
-    int width,
-    int height,
-    int K,
-    bool EndOfLine,
-    bool EncodedByteAlign,
-    bool BlackIs1,
-    int Columns,
-    int Rows) {
-  int actual_width = Columns ? Columns : width;
-  int actual_height = Rows ? Rows : height;
-
-  // Reject invalid values.
-  if (actual_width <= 0 || actual_height <= 0)
-    return nullptr;
-
-  // Reject unreasonable large input.
-  if (actual_width > kMaxImageDimension || actual_height > kMaxImageDimension)
-    return nullptr;
-
-  uint32_t pitch = (static_cast<uint32_t>(actual_width) + 31) / 32 * 4;
-  return pdfium::MakeUnique<CCodec_FaxDecoder>(
-      src_buf, src_size, actual_width, actual_height, pitch, K, EndOfLine,
-      EncodedByteAlign, BlackIs1);
-}
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-namespace {
-const uint8_t BlackRunTerminator[128] = {
-    0x37, 10, 0x02, 3,  0x03, 2,  0x02, 2,  0x03, 3,  0x03, 4,  0x02, 4,
-    0x03, 5,  0x05, 6,  0x04, 6,  0x04, 7,  0x05, 7,  0x07, 7,  0x04, 8,
-    0x07, 8,  0x18, 9,  0x17, 10, 0x18, 10, 0x08, 10, 0x67, 11, 0x68, 11,
-    0x6c, 11, 0x37, 11, 0x28, 11, 0x17, 11, 0x18, 11, 0xca, 12, 0xcb, 12,
-    0xcc, 12, 0xcd, 12, 0x68, 12, 0x69, 12, 0x6a, 12, 0x6b, 12, 0xd2, 12,
-    0xd3, 12, 0xd4, 12, 0xd5, 12, 0xd6, 12, 0xd7, 12, 0x6c, 12, 0x6d, 12,
-    0xda, 12, 0xdb, 12, 0x54, 12, 0x55, 12, 0x56, 12, 0x57, 12, 0x64, 12,
-    0x65, 12, 0x52, 12, 0x53, 12, 0x24, 12, 0x37, 12, 0x38, 12, 0x27, 12,
-    0x28, 12, 0x58, 12, 0x59, 12, 0x2b, 12, 0x2c, 12, 0x5a, 12, 0x66, 12,
-    0x67, 12,
-};
-
-const uint8_t BlackRunMarkup[80] = {
-    0x0f, 10, 0xc8, 12, 0xc9, 12, 0x5b, 12, 0x33, 12, 0x34, 12, 0x35, 12,
-    0x6c, 13, 0x6d, 13, 0x4a, 13, 0x4b, 13, 0x4c, 13, 0x4d, 13, 0x72, 13,
-    0x73, 13, 0x74, 13, 0x75, 13, 0x76, 13, 0x77, 13, 0x52, 13, 0x53, 13,
-    0x54, 13, 0x55, 13, 0x5a, 13, 0x5b, 13, 0x64, 13, 0x65, 13, 0x08, 11,
-    0x0c, 11, 0x0d, 11, 0x12, 12, 0x13, 12, 0x14, 12, 0x15, 12, 0x16, 12,
-    0x17, 12, 0x1c, 12, 0x1d, 12, 0x1e, 12, 0x1f, 12,
-};
-
-const uint8_t WhiteRunTerminator[128] = {
-    0x35, 8, 0x07, 6, 0x07, 4, 0x08, 4, 0x0B, 4, 0x0C, 4, 0x0E, 4, 0x0F, 4,
-    0x13, 5, 0x14, 5, 0x07, 5, 0x08, 5, 0x08, 6, 0x03, 6, 0x34, 6, 0x35, 6,
-    0x2a, 6, 0x2B, 6, 0x27, 7, 0x0c, 7, 0x08, 7, 0x17, 7, 0x03, 7, 0x04, 7,
-    0x28, 7, 0x2B, 7, 0x13, 7, 0x24, 7, 0x18, 7, 0x02, 8, 0x03, 8, 0x1a, 8,
-    0x1b, 8, 0x12, 8, 0x13, 8, 0x14, 8, 0x15, 8, 0x16, 8, 0x17, 8, 0x28, 8,
-    0x29, 8, 0x2a, 8, 0x2b, 8, 0x2c, 8, 0x2d, 8, 0x04, 8, 0x05, 8, 0x0a, 8,
-    0x0b, 8, 0x52, 8, 0x53, 8, 0x54, 8, 0x55, 8, 0x24, 8, 0x25, 8, 0x58, 8,
-    0x59, 8, 0x5a, 8, 0x5b, 8, 0x4a, 8, 0x4b, 8, 0x32, 8, 0x33, 8, 0x34, 8,
-};
-
-const uint8_t WhiteRunMarkup[80] = {
-    0x1b, 5,  0x12, 5,  0x17, 6,  0x37, 7,  0x36, 8,  0x37, 8,  0x64, 8,
-    0x65, 8,  0x68, 8,  0x67, 8,  0xcc, 9,  0xcd, 9,  0xd2, 9,  0xd3, 9,
-    0xd4, 9,  0xd5, 9,  0xd6, 9,  0xd7, 9,  0xd8, 9,  0xd9, 9,  0xda, 9,
-    0xdb, 9,  0x98, 9,  0x99, 9,  0x9a, 9,  0x18, 6,  0x9b, 9,  0x08, 11,
-    0x0c, 11, 0x0d, 11, 0x12, 12, 0x13, 12, 0x14, 12, 0x15, 12, 0x16, 12,
-    0x17, 12, 0x1c, 12, 0x1d, 12, 0x1e, 12, 0x1f, 12,
-};
-
-void AddBitStream(uint8_t* dest_buf, int* dest_bitpos, int data, int bitlen) {
-  for (int i = bitlen - 1; i >= 0; i--) {
-    if (data & (1 << i))
-      dest_buf[*dest_bitpos / 8] |= 1 << (7 - *dest_bitpos % 8);
-    (*dest_bitpos)++;
-  }
-}
-
-void FaxEncodeRun(uint8_t* dest_buf, int* dest_bitpos, int run, bool bWhite) {
-  while (run >= 2560) {
-    AddBitStream(dest_buf, dest_bitpos, 0x1f, 12);
-    run -= 2560;
-  }
-  if (run >= 64) {
-    int markup = run - run % 64;
-    const uint8_t* p = bWhite ? WhiteRunMarkup : BlackRunMarkup;
-    p += (markup / 64 - 1) * 2;
-    AddBitStream(dest_buf, dest_bitpos, *p, p[1]);
-  }
-  run %= 64;
-  const uint8_t* p = bWhite ? WhiteRunTerminator : BlackRunTerminator;
-  p += run * 2;
-  AddBitStream(dest_buf, dest_bitpos, *p, p[1]);
-}
-
-void FaxEncode2DLine(uint8_t* dest_buf,
-                     int* dest_bitpos,
-                     const uint8_t* src_buf,
-                     const std::vector<uint8_t>& ref_buf,
-                     int cols) {
-  int a0 = -1;
-  bool a0color = true;
-  while (1) {
-    int a1 = FindBit(src_buf, cols, a0 + 1, !a0color);
-    int b1;
-    int b2;
-    FaxG4FindB1B2(ref_buf, cols, a0, a0color, &b1, &b2);
-    if (b2 < a1) {
-      *dest_bitpos += 3;
-      dest_buf[*dest_bitpos / 8] |= 1 << (7 - *dest_bitpos % 8);
-      (*dest_bitpos)++;
-      a0 = b2;
-    } else if (a1 - b1 <= 3 && b1 - a1 <= 3) {
-      int delta = a1 - b1;
-      switch (delta) {
-        case 0:
-          dest_buf[*dest_bitpos / 8] |= 1 << (7 - *dest_bitpos % 8);
-          break;
-        case 1:
-        case 2:
-        case 3:
-          *dest_bitpos += delta == 1 ? 1 : delta + 2;
-          dest_buf[*dest_bitpos / 8] |= 1 << (7 - *dest_bitpos % 8);
-          (*dest_bitpos)++;
-          dest_buf[*dest_bitpos / 8] |= 1 << (7 - *dest_bitpos % 8);
-          break;
-        case -1:
-        case -2:
-        case -3:
-          *dest_bitpos += delta == -1 ? 1 : -delta + 2;
-          dest_buf[*dest_bitpos / 8] |= 1 << (7 - *dest_bitpos % 8);
-          (*dest_bitpos)++;
-          break;
-      }
-      (*dest_bitpos)++;
-      a0 = a1;
-      a0color = !a0color;
-    } else {
-      int a2 = FindBit(src_buf, cols, a1 + 1, a0color);
-      (*dest_bitpos)++;
-      (*dest_bitpos)++;
-      dest_buf[*dest_bitpos / 8] |= 1 << (7 - *dest_bitpos % 8);
-      (*dest_bitpos)++;
-      if (a0 < 0)
-        a0 = 0;
-      FaxEncodeRun(dest_buf, dest_bitpos, a1 - a0, a0color);
-      FaxEncodeRun(dest_buf, dest_bitpos, a2 - a1, !a0color);
-      a0 = a2;
-    }
-    if (a0 >= cols)
-      return;
-  }
-}
-
-class CCodec_FaxEncoder {
- public:
-  CCodec_FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch);
-  ~CCodec_FaxEncoder();
-  void Encode(std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-              uint32_t* dest_size);
-
- private:
-  CFX_BinaryBuf m_DestBuf;
-  std::vector<uint8_t> m_RefLine;
-  uint8_t* m_pLineBuf;
-  const int m_Cols;
-  const int m_Rows;
-  const int m_Pitch;
-  const uint8_t* m_pSrcBuf;
-};
-
-CCodec_FaxEncoder::CCodec_FaxEncoder(const uint8_t* src_buf,
-                                     int width,
-                                     int height,
-                                     int pitch)
-    : m_Cols(width), m_Rows(height), m_Pitch(pitch), m_pSrcBuf(src_buf) {
-  m_RefLine.resize(m_Pitch);
-  memset(m_RefLine.data(), 0xff, m_Pitch);
-  m_pLineBuf = FX_Alloc2D(uint8_t, m_Pitch, 8);
-  m_DestBuf.EstimateSize(0, 10240);
-}
-
-CCodec_FaxEncoder::~CCodec_FaxEncoder() {
-  FX_Free(m_pLineBuf);
-}
-
-void CCodec_FaxEncoder::Encode(
-    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-    uint32_t* dest_size) {
-  int dest_bitpos = 0;
-  uint8_t last_byte = 0;
-  for (int i = 0; i < m_Rows; i++) {
-    const uint8_t* scan_line = m_pSrcBuf + i * m_Pitch;
-    memset(m_pLineBuf, 0, m_Pitch * 8);
-    m_pLineBuf[0] = last_byte;
-    FaxEncode2DLine(m_pLineBuf, &dest_bitpos, scan_line, m_RefLine, m_Cols);
-    m_DestBuf.AppendBlock(m_pLineBuf, dest_bitpos / 8);
-    last_byte = m_pLineBuf[dest_bitpos / 8];
-    dest_bitpos %= 8;
-    memcpy(m_RefLine.data(), scan_line, m_Pitch);
-  }
-  if (dest_bitpos)
-    m_DestBuf.AppendByte(last_byte);
-  *dest_size = m_DestBuf.GetSize();
-  *dest_buf = m_DestBuf.DetachBuffer();
-}
-
-}  // namespace
-
-void CCodec_FaxModule::FaxEncode(
-    const uint8_t* src_buf,
-    int width,
-    int height,
-    int pitch,
-    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-    uint32_t* dest_size) {
-  CCodec_FaxEncoder encoder(src_buf, width, height, pitch);
-  encoder.Encode(dest_buf, dest_size);
-}
-
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
diff --git a/core/fxcodec/codec/fx_codec_flate.cpp b/core/fxcodec/codec/fx_codec_flate.cpp
deleted file mode 100644
index 95902bb..0000000
--- a/core/fxcodec/codec/fx_codec_flate.cpp
+++ /dev/null
@@ -1,837 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/codec_int.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcodec/codec/ccodec_flatemodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
-#include "core/fxcrt/fx_extension.h"
-#include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-
-#if defined(USE_SYSTEM_ZLIB)
-#include <zlib.h>
-#else
-#include "third_party/zlib/zlib.h"
-#endif
-
-extern "C" {
-
-static void* my_alloc_func(void* opaque,
-                           unsigned int items,
-                           unsigned int size) {
-  return FX_Alloc2D(uint8_t, items, size);
-}
-
-static void my_free_func(void* opaque, void* address) {
-  FX_Free(address);
-}
-
-}  // extern "C"
-
-namespace {
-
-uint32_t FlateGetPossiblyTruncatedTotalOut(void* context) {
-  return pdfium::base::saturated_cast<uint32_t>(
-      static_cast<z_stream*>(context)->total_out);
-}
-
-uint32_t FlateGetPossiblyTruncatedTotalIn(void* context) {
-  return pdfium::base::saturated_cast<uint32_t>(
-      static_cast<z_stream*>(context)->total_in);
-}
-
-bool FlateCompress(unsigned char* dest_buf,
-                   unsigned long* dest_size,
-                   const unsigned char* src_buf,
-                   uint32_t src_size) {
-  return compress(dest_buf, dest_size, src_buf, src_size) == Z_OK;
-}
-
-void* FlateInit() {
-  z_stream* p = FX_Alloc(z_stream, 1);
-  memset(p, 0, sizeof(z_stream));
-  p->zalloc = my_alloc_func;
-  p->zfree = my_free_func;
-  inflateInit(p);
-  return p;
-}
-
-void FlateInput(void* context,
-                const unsigned char* src_buf,
-                uint32_t src_size) {
-  static_cast<z_stream*>(context)->next_in =
-      const_cast<unsigned char*>(src_buf);
-  static_cast<z_stream*>(context)->avail_in = src_size;
-}
-
-uint32_t FlateOutput(void* context,
-                     unsigned char* dest_buf,
-                     uint32_t dest_size) {
-  static_cast<z_stream*>(context)->next_out = dest_buf;
-  static_cast<z_stream*>(context)->avail_out = dest_size;
-  uint32_t pre_pos = FlateGetPossiblyTruncatedTotalOut(context);
-  int ret = inflate(static_cast<z_stream*>(context), Z_SYNC_FLUSH);
-
-  uint32_t post_pos = FlateGetPossiblyTruncatedTotalOut(context);
-  ASSERT(post_pos >= pre_pos);
-
-  uint32_t written = post_pos - pre_pos;
-  if (written < dest_size)
-    memset(dest_buf + written, '\0', dest_size - written);
-
-  return ret;
-}
-
-uint32_t FlateGetAvailOut(void* context) {
-  return static_cast<z_stream*>(context)->avail_out;
-}
-
-void FlateEnd(void* context) {
-  inflateEnd(static_cast<z_stream*>(context));
-  static_cast<z_stream*>(context)->zfree(0, context);
-}
-
-class CLZWDecoder {
- public:
-  int Decode(uint8_t* output,
-             uint32_t& outlen,
-             const uint8_t* input,
-             uint32_t& size,
-             bool bEarlyChange);
-
- private:
-  void AddCode(uint32_t prefix_code, uint8_t append_char);
-  void DecodeString(uint32_t code);
-
-  uint32_t m_InPos;
-  uint32_t m_OutPos;
-  uint8_t* m_pOutput;
-  const uint8_t* m_pInput;
-  bool m_Early;
-  uint32_t m_CodeArray[5021];
-  uint32_t m_nCodes;
-  uint8_t m_DecodeStack[4000];
-  uint32_t m_StackLen;
-  int m_CodeLen;
-};
-
-void CLZWDecoder::AddCode(uint32_t prefix_code, uint8_t append_char) {
-  if (m_nCodes + m_Early == 4094) {
-    return;
-  }
-  m_CodeArray[m_nCodes++] = (prefix_code << 16) | append_char;
-  if (m_nCodes + m_Early == 512 - 258) {
-    m_CodeLen = 10;
-  } else if (m_nCodes + m_Early == 1024 - 258) {
-    m_CodeLen = 11;
-  } else if (m_nCodes + m_Early == 2048 - 258) {
-    m_CodeLen = 12;
-  }
-}
-void CLZWDecoder::DecodeString(uint32_t code) {
-  while (1) {
-    int index = code - 258;
-    if (index < 0 || index >= (int)m_nCodes) {
-      break;
-    }
-    uint32_t data = m_CodeArray[index];
-    if (m_StackLen >= sizeof(m_DecodeStack)) {
-      return;
-    }
-    m_DecodeStack[m_StackLen++] = (uint8_t)data;
-    code = data >> 16;
-  }
-  if (m_StackLen >= sizeof(m_DecodeStack)) {
-    return;
-  }
-  m_DecodeStack[m_StackLen++] = (uint8_t)code;
-}
-int CLZWDecoder::Decode(uint8_t* dest_buf,
-                        uint32_t& dest_size,
-                        const uint8_t* src_buf,
-                        uint32_t& src_size,
-                        bool bEarlyChange) {
-  m_CodeLen = 9;
-  m_InPos = 0;
-  m_OutPos = 0;
-  m_pInput = src_buf;
-  m_pOutput = dest_buf;
-  m_Early = bEarlyChange ? 1 : 0;
-  m_nCodes = 0;
-  uint32_t old_code = 0xFFFFFFFF;
-  uint8_t last_char = 0;
-  while (1) {
-    if (m_InPos + m_CodeLen > src_size * 8) {
-      break;
-    }
-    int byte_pos = m_InPos / 8;
-    int bit_pos = m_InPos % 8, bit_left = m_CodeLen;
-    uint32_t code = 0;
-    if (bit_pos) {
-      bit_left -= 8 - bit_pos;
-      code = (m_pInput[byte_pos++] & ((1 << (8 - bit_pos)) - 1)) << bit_left;
-    }
-    if (bit_left < 8) {
-      code |= m_pInput[byte_pos] >> (8 - bit_left);
-    } else {
-      bit_left -= 8;
-      code |= m_pInput[byte_pos++] << bit_left;
-      if (bit_left) {
-        code |= m_pInput[byte_pos] >> (8 - bit_left);
-      }
-    }
-    m_InPos += m_CodeLen;
-    if (code == 257)
-      break;
-    if (code < 256) {
-      if (m_OutPos == dest_size) {
-        return -5;
-      }
-      if (m_pOutput) {
-        m_pOutput[m_OutPos] = (uint8_t)code;
-      }
-      m_OutPos++;
-      last_char = (uint8_t)code;
-      if (old_code != 0xFFFFFFFF)
-        AddCode(old_code, last_char);
-      old_code = code;
-    } else if (code == 256) {
-      m_CodeLen = 9;
-      m_nCodes = 0;
-      old_code = 0xFFFFFFFF;
-    } else {
-      // Else 257 or greater.
-      if (old_code == 0xFFFFFFFF)
-        return 2;
-
-      m_StackLen = 0;
-      if (code >= m_nCodes + 258) {
-        if (m_StackLen < sizeof(m_DecodeStack)) {
-          m_DecodeStack[m_StackLen++] = last_char;
-        }
-        DecodeString(old_code);
-      } else {
-        DecodeString(code);
-      }
-      if (m_OutPos + m_StackLen > dest_size) {
-        return -5;
-      }
-      if (m_pOutput) {
-        for (uint32_t i = 0; i < m_StackLen; i++) {
-          m_pOutput[m_OutPos + i] = m_DecodeStack[m_StackLen - i - 1];
-        }
-      }
-      m_OutPos += m_StackLen;
-      last_char = m_DecodeStack[m_StackLen - 1];
-      if (old_code < 256) {
-        AddCode(old_code, last_char);
-      } else if (old_code - 258 >= m_nCodes) {
-        dest_size = m_OutPos;
-        src_size = (m_InPos + 7) / 8;
-        return 0;
-      } else {
-        AddCode(old_code, last_char);
-      }
-      old_code = code;
-    }
-  }
-  dest_size = m_OutPos;
-  src_size = (m_InPos + 7) / 8;
-  return 0;
-}
-
-uint8_t PathPredictor(int a, int b, int c) {
-  int p = a + b - c;
-  int pa = abs(p - a);
-  int pb = abs(p - b);
-  int pc = abs(p - c);
-  if (pa <= pb && pa <= pc)
-    return (uint8_t)a;
-  if (pb <= pc)
-    return (uint8_t)b;
-  return (uint8_t)c;
-}
-
-void PNG_PredictorEncode(uint8_t** data_buf, uint32_t* data_size) {
-  const int row_size = 7;
-  const int row_count = (*data_size + row_size - 1) / row_size;
-  const int last_row_size = *data_size % row_size;
-  uint8_t* dest_buf = FX_Alloc2D(uint8_t, row_size + 1, row_count);
-  int byte_cnt = 0;
-  uint8_t* pSrcData = *data_buf;
-  uint8_t* pDestData = dest_buf;
-  for (int row = 0; row < row_count; row++) {
-    for (int byte = 0; byte < row_size && byte_cnt < (int)*data_size; byte++) {
-      pDestData[0] = 2;
-      uint8_t up = 0;
-      if (row)
-        up = pSrcData[byte - row_size];
-      pDestData[byte + 1] = pSrcData[byte] - up;
-      ++byte_cnt;
-    }
-    pDestData += (row_size + 1);
-    pSrcData += row_size;
-  }
-  FX_Free(*data_buf);
-  *data_buf = dest_buf;
-  *data_size = (row_size + 1) * row_count -
-               (last_row_size > 0 ? (row_size - last_row_size) : 0);
-}
-
-void PNG_PredictLine(uint8_t* pDestData,
-                     const uint8_t* pSrcData,
-                     const uint8_t* pLastLine,
-                     int bpc,
-                     int nColors,
-                     int nPixels) {
-  int row_size = (nPixels * bpc * nColors + 7) / 8;
-  int BytesPerPixel = (bpc * nColors + 7) / 8;
-  uint8_t tag = pSrcData[0];
-  if (tag == 0) {
-    memmove(pDestData, pSrcData + 1, row_size);
-    return;
-  }
-  for (int byte = 0; byte < row_size; byte++) {
-    uint8_t raw_byte = pSrcData[byte + 1];
-    switch (tag) {
-      case 1: {
-        uint8_t left = 0;
-        if (byte >= BytesPerPixel) {
-          left = pDestData[byte - BytesPerPixel];
-        }
-        pDestData[byte] = raw_byte + left;
-        break;
-      }
-      case 2: {
-        uint8_t up = 0;
-        if (pLastLine) {
-          up = pLastLine[byte];
-        }
-        pDestData[byte] = raw_byte + up;
-        break;
-      }
-      case 3: {
-        uint8_t left = 0;
-        if (byte >= BytesPerPixel) {
-          left = pDestData[byte - BytesPerPixel];
-        }
-        uint8_t up = 0;
-        if (pLastLine) {
-          up = pLastLine[byte];
-        }
-        pDestData[byte] = raw_byte + (up + left) / 2;
-        break;
-      }
-      case 4: {
-        uint8_t left = 0;
-        if (byte >= BytesPerPixel) {
-          left = pDestData[byte - BytesPerPixel];
-        }
-        uint8_t up = 0;
-        if (pLastLine) {
-          up = pLastLine[byte];
-        }
-        uint8_t upper_left = 0;
-        if (byte >= BytesPerPixel && pLastLine) {
-          upper_left = pLastLine[byte - BytesPerPixel];
-        }
-        pDestData[byte] = raw_byte + PathPredictor(left, up, upper_left);
-        break;
-      }
-      default:
-        pDestData[byte] = raw_byte;
-        break;
-    }
-  }
-}
-
-bool PNG_Predictor(uint8_t*& data_buf,
-                   uint32_t& data_size,
-                   int Colors,
-                   int BitsPerComponent,
-                   int Columns) {
-  const int BytesPerPixel = (Colors * BitsPerComponent + 7) / 8;
-  const int row_size = (Colors * BitsPerComponent * Columns + 7) / 8;
-  if (row_size <= 0)
-    return false;
-  const int row_count = (data_size + row_size) / (row_size + 1);
-  if (row_count <= 0)
-    return false;
-  const int last_row_size = data_size % (row_size + 1);
-  uint8_t* dest_buf = FX_Alloc2D(uint8_t, row_size, row_count);
-  int byte_cnt = 0;
-  uint8_t* pSrcData = data_buf;
-  uint8_t* pDestData = dest_buf;
-  for (int row = 0; row < row_count; row++) {
-    uint8_t tag = pSrcData[0];
-    byte_cnt++;
-    if (tag == 0) {
-      int move_size = row_size;
-      if ((row + 1) * (move_size + 1) > (int)data_size) {
-        move_size = last_row_size - 1;
-      }
-      memmove(pDestData, pSrcData + 1, move_size);
-      pSrcData += move_size + 1;
-      pDestData += move_size;
-      byte_cnt += move_size;
-      continue;
-    }
-    for (int byte = 0; byte < row_size && byte_cnt < (int)data_size; byte++) {
-      uint8_t raw_byte = pSrcData[byte + 1];
-      switch (tag) {
-        case 1: {
-          uint8_t left = 0;
-          if (byte >= BytesPerPixel) {
-            left = pDestData[byte - BytesPerPixel];
-          }
-          pDestData[byte] = raw_byte + left;
-          break;
-        }
-        case 2: {
-          uint8_t up = 0;
-          if (row) {
-            up = pDestData[byte - row_size];
-          }
-          pDestData[byte] = raw_byte + up;
-          break;
-        }
-        case 3: {
-          uint8_t left = 0;
-          if (byte >= BytesPerPixel) {
-            left = pDestData[byte - BytesPerPixel];
-          }
-          uint8_t up = 0;
-          if (row) {
-            up = pDestData[byte - row_size];
-          }
-          pDestData[byte] = raw_byte + (up + left) / 2;
-          break;
-        }
-        case 4: {
-          uint8_t left = 0;
-          if (byte >= BytesPerPixel) {
-            left = pDestData[byte - BytesPerPixel];
-          }
-          uint8_t up = 0;
-          if (row) {
-            up = pDestData[byte - row_size];
-          }
-          uint8_t upper_left = 0;
-          if (byte >= BytesPerPixel && row) {
-            upper_left = pDestData[byte - row_size - BytesPerPixel];
-          }
-          pDestData[byte] = raw_byte + PathPredictor(left, up, upper_left);
-          break;
-        }
-        default:
-          pDestData[byte] = raw_byte;
-          break;
-      }
-      byte_cnt++;
-    }
-    pSrcData += row_size + 1;
-    pDestData += row_size;
-  }
-  FX_Free(data_buf);
-  data_buf = dest_buf;
-  data_size = row_size * row_count -
-              (last_row_size > 0 ? (row_size + 1 - last_row_size) : 0);
-  return true;
-}
-
-void TIFF_PredictLine(uint8_t* dest_buf,
-                      uint32_t row_size,
-                      int BitsPerComponent,
-                      int Colors,
-                      int Columns) {
-  if (BitsPerComponent == 1) {
-    int row_bits = std::min(BitsPerComponent * Colors * Columns,
-                            pdfium::base::checked_cast<int>(row_size * 8));
-    int index_pre = 0;
-    int col_pre = 0;
-    for (int i = 1; i < row_bits; i++) {
-      int col = i % 8;
-      int index = i / 8;
-      if (((dest_buf[index] >> (7 - col)) & 1) ^
-          ((dest_buf[index_pre] >> (7 - col_pre)) & 1)) {
-        dest_buf[index] |= 1 << (7 - col);
-      } else {
-        dest_buf[index] &= ~(1 << (7 - col));
-      }
-      index_pre = index;
-      col_pre = col;
-    }
-    return;
-  }
-  int BytesPerPixel = BitsPerComponent * Colors / 8;
-  if (BitsPerComponent == 16) {
-    for (uint32_t i = BytesPerPixel; i + 1 < row_size; i += 2) {
-      uint16_t pixel =
-          (dest_buf[i - BytesPerPixel] << 8) | dest_buf[i - BytesPerPixel + 1];
-      pixel += (dest_buf[i] << 8) | dest_buf[i + 1];
-      dest_buf[i] = pixel >> 8;
-      dest_buf[i + 1] = (uint8_t)pixel;
-    }
-  } else {
-    for (uint32_t i = BytesPerPixel; i < row_size; i++) {
-      dest_buf[i] += dest_buf[i - BytesPerPixel];
-    }
-  }
-}
-
-bool TIFF_Predictor(uint8_t*& data_buf,
-                    uint32_t& data_size,
-                    int Colors,
-                    int BitsPerComponent,
-                    int Columns) {
-  int row_size = (Colors * BitsPerComponent * Columns + 7) / 8;
-  if (row_size == 0)
-    return false;
-  const int row_count = (data_size + row_size - 1) / row_size;
-  const int last_row_size = data_size % row_size;
-  for (int row = 0; row < row_count; row++) {
-    uint8_t* scan_line = data_buf + row * row_size;
-    if ((row + 1) * row_size > (int)data_size) {
-      row_size = last_row_size;
-    }
-    TIFF_PredictLine(scan_line, row_size, BitsPerComponent, Colors, Columns);
-  }
-  return true;
-}
-
-void FlateUncompress(const uint8_t* src_buf,
-                     uint32_t src_size,
-                     uint32_t orig_size,
-                     uint8_t*& dest_buf,
-                     uint32_t& dest_size,
-                     uint32_t& offset) {
-  dest_buf = nullptr;
-  dest_size = 0;
-  void* context = FlateInit();
-  if (!context)
-    return;
-
-  FlateInput(context, src_buf, src_size);
-
-  const uint32_t kMaxInitialAllocSize = 10000000;
-  uint32_t guess_size = orig_size ? orig_size : src_size * 2;
-  guess_size = std::min(guess_size, kMaxInitialAllocSize);
-
-  uint32_t buf_size = guess_size;
-  uint32_t last_buf_size = buf_size;
-  std::unique_ptr<uint8_t, FxFreeDeleter> guess_buf(
-      FX_Alloc(uint8_t, guess_size + 1));
-  guess_buf.get()[guess_size] = '\0';
-
-  std::vector<uint8_t*> result_tmp_bufs;
-  uint8_t* cur_buf = guess_buf.release();
-  while (1) {
-    uint32_t ret = FlateOutput(context, cur_buf, buf_size);
-    uint32_t avail_buf_size = FlateGetAvailOut(context);
-    if (ret != Z_OK || avail_buf_size != 0) {
-      last_buf_size = buf_size - avail_buf_size;
-      result_tmp_bufs.push_back(cur_buf);
-      break;
-    }
-    result_tmp_bufs.push_back(cur_buf);
-    cur_buf = FX_Alloc(uint8_t, buf_size + 1);
-    cur_buf[buf_size] = '\0';
-  }
-
-  // The TotalOut size returned from the library may not be big enough to
-  // handle the content the library returns. We can only handle items
-  // up to 4GB in size.
-  dest_size = FlateGetPossiblyTruncatedTotalOut(context);
-  offset = FlateGetPossiblyTruncatedTotalIn(context);
-  if (result_tmp_bufs.size() == 1) {
-    dest_buf = result_tmp_bufs[0];
-  } else {
-    uint8_t* result_buf = FX_Alloc(uint8_t, dest_size);
-    uint32_t result_pos = 0;
-    uint32_t remaining = dest_size;
-    for (size_t i = 0; i < result_tmp_bufs.size(); i++) {
-      uint8_t* tmp_buf = result_tmp_bufs[i];
-      uint32_t tmp_buf_size = buf_size;
-      if (i == result_tmp_bufs.size() - 1)
-        tmp_buf_size = last_buf_size;
-
-      uint32_t cp_size = std::min(tmp_buf_size, remaining);
-      memcpy(result_buf + result_pos, tmp_buf, cp_size);
-      result_pos += cp_size;
-      remaining -= cp_size;
-
-      FX_Free(result_tmp_bufs[i]);
-    }
-    dest_buf = result_buf;
-  }
-  FlateEnd(context);
-}
-
-}  // namespace
-
-class CCodec_FlateScanlineDecoder : public CCodec_ScanlineDecoder {
- public:
-  CCodec_FlateScanlineDecoder();
-  ~CCodec_FlateScanlineDecoder() override;
-
-  void Create(const uint8_t* src_buf,
-              uint32_t src_size,
-              int width,
-              int height,
-              int nComps,
-              int bpc,
-              int predictor,
-              int Colors,
-              int BitsPerComponent,
-              int Columns);
-
-  // CCodec_ScanlineDecoder
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
-  uint32_t GetSrcOffset() override;
-
-  void* m_pFlate;
-  const uint8_t* m_SrcBuf;
-  uint32_t m_SrcSize;
-  uint8_t* m_pScanline;
-  uint8_t* m_pLastLine;
-  uint8_t* m_pPredictBuffer;
-  uint8_t* m_pPredictRaw;
-  int m_Predictor;
-  int m_Colors;
-  int m_BitsPerComponent;
-  int m_Columns;
-  uint32_t m_PredictPitch;
-  size_t m_LeftOver;
-};
-
-CCodec_FlateScanlineDecoder::CCodec_FlateScanlineDecoder() {
-  m_pFlate = nullptr;
-  m_pScanline = nullptr;
-  m_pLastLine = nullptr;
-  m_pPredictBuffer = nullptr;
-  m_pPredictRaw = nullptr;
-  m_LeftOver = 0;
-}
-
-CCodec_FlateScanlineDecoder::~CCodec_FlateScanlineDecoder() {
-  FX_Free(m_pScanline);
-  FX_Free(m_pLastLine);
-  FX_Free(m_pPredictBuffer);
-  FX_Free(m_pPredictRaw);
-  if (m_pFlate)
-    FlateEnd(m_pFlate);
-}
-
-void CCodec_FlateScanlineDecoder::Create(const uint8_t* src_buf,
-                                         uint32_t src_size,
-                                         int width,
-                                         int height,
-                                         int nComps,
-                                         int bpc,
-                                         int predictor,
-                                         int Colors,
-                                         int BitsPerComponent,
-                                         int Columns) {
-  m_SrcBuf = src_buf;
-  m_SrcSize = src_size;
-  m_OutputWidth = m_OrigWidth = width;
-  m_OutputHeight = m_OrigHeight = height;
-  m_nComps = nComps;
-  m_bpc = bpc;
-  m_Pitch = (static_cast<uint32_t>(width) * nComps * bpc + 7) / 8;
-  m_pScanline = FX_Alloc(uint8_t, m_Pitch);
-  m_Predictor = 0;
-  if (predictor) {
-    if (predictor >= 10) {
-      m_Predictor = 2;
-    } else if (predictor == 2) {
-      m_Predictor = 1;
-    }
-    if (m_Predictor) {
-      if (BitsPerComponent * Colors * Columns == 0) {
-        BitsPerComponent = m_bpc;
-        Colors = m_nComps;
-        Columns = m_OrigWidth;
-      }
-      m_Colors = Colors;
-      m_BitsPerComponent = BitsPerComponent;
-      m_Columns = Columns;
-      m_PredictPitch =
-          (static_cast<uint32_t>(m_BitsPerComponent) * m_Colors * m_Columns +
-           7) /
-          8;
-      m_pLastLine = FX_Alloc(uint8_t, m_PredictPitch);
-      m_pPredictRaw = FX_Alloc(uint8_t, m_PredictPitch + 1);
-      m_pPredictBuffer = FX_Alloc(uint8_t, m_PredictPitch);
-    }
-  }
-}
-
-bool CCodec_FlateScanlineDecoder::v_Rewind() {
-  if (m_pFlate)
-    FlateEnd(m_pFlate);
-
-  m_pFlate = FlateInit();
-  if (!m_pFlate)
-    return false;
-
-  FlateInput(m_pFlate, m_SrcBuf, m_SrcSize);
-  m_LeftOver = 0;
-  return true;
-}
-
-uint8_t* CCodec_FlateScanlineDecoder::v_GetNextLine() {
-  if (m_Predictor) {
-    if (m_Pitch == m_PredictPitch) {
-      if (m_Predictor == 2) {
-        FlateOutput(m_pFlate, m_pPredictRaw, m_PredictPitch + 1);
-        PNG_PredictLine(m_pScanline, m_pPredictRaw, m_pLastLine,
-                        m_BitsPerComponent, m_Colors, m_Columns);
-        memcpy(m_pLastLine, m_pScanline, m_PredictPitch);
-      } else {
-        FlateOutput(m_pFlate, m_pScanline, m_Pitch);
-        TIFF_PredictLine(m_pScanline, m_PredictPitch, m_bpc, m_nComps,
-                         m_OutputWidth);
-      }
-    } else {
-      size_t bytes_to_go = m_Pitch;
-      size_t read_leftover =
-          m_LeftOver > bytes_to_go ? bytes_to_go : m_LeftOver;
-      if (read_leftover) {
-        memcpy(m_pScanline, m_pPredictBuffer + m_PredictPitch - m_LeftOver,
-               read_leftover);
-        m_LeftOver -= read_leftover;
-        bytes_to_go -= read_leftover;
-      }
-      while (bytes_to_go) {
-        if (m_Predictor == 2) {
-          FlateOutput(m_pFlate, m_pPredictRaw, m_PredictPitch + 1);
-          PNG_PredictLine(m_pPredictBuffer, m_pPredictRaw, m_pLastLine,
-                          m_BitsPerComponent, m_Colors, m_Columns);
-          memcpy(m_pLastLine, m_pPredictBuffer, m_PredictPitch);
-        } else {
-          FlateOutput(m_pFlate, m_pPredictBuffer, m_PredictPitch);
-          TIFF_PredictLine(m_pPredictBuffer, m_PredictPitch, m_BitsPerComponent,
-                           m_Colors, m_Columns);
-        }
-        size_t read_bytes =
-            m_PredictPitch > bytes_to_go ? bytes_to_go : m_PredictPitch;
-        memcpy(m_pScanline + m_Pitch - bytes_to_go, m_pPredictBuffer,
-               read_bytes);
-        m_LeftOver += m_PredictPitch - read_bytes;
-        bytes_to_go -= read_bytes;
-      }
-    }
-  } else {
-    FlateOutput(m_pFlate, m_pScanline, m_Pitch);
-  }
-  return m_pScanline;
-}
-
-uint32_t CCodec_FlateScanlineDecoder::GetSrcOffset() {
-  return FlateGetPossiblyTruncatedTotalIn(m_pFlate);
-}
-
-std::unique_ptr<CCodec_ScanlineDecoder> CCodec_FlateModule::CreateDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
-    int width,
-    int height,
-    int nComps,
-    int bpc,
-    int predictor,
-    int Colors,
-    int BitsPerComponent,
-    int Columns) {
-  auto pDecoder = pdfium::MakeUnique<CCodec_FlateScanlineDecoder>();
-  pDecoder->Create(src_buf, src_size, width, height, nComps, bpc, predictor,
-                   Colors, BitsPerComponent, Columns);
-  return std::move(pDecoder);
-}
-
-uint32_t CCodec_FlateModule::FlateOrLZWDecode(bool bLZW,
-                                              const uint8_t* src_buf,
-                                              uint32_t src_size,
-                                              bool bEarlyChange,
-                                              int predictor,
-                                              int Colors,
-                                              int BitsPerComponent,
-                                              int Columns,
-                                              uint32_t estimated_size,
-                                              uint8_t** dest_buf,
-                                              uint32_t* dest_size) {
-  *dest_buf = nullptr;
-  uint32_t offset = 0;
-  int predictor_type = 0;
-  if (predictor) {
-    if (predictor >= 10)
-      predictor_type = 2;
-    else if (predictor == 2)
-      predictor_type = 1;
-  }
-  if (bLZW) {
-    auto decoder = pdfium::MakeUnique<CLZWDecoder>();
-    *dest_size = 0xFFFFFFFF;
-    offset = src_size;
-    int err =
-        decoder->Decode(nullptr, *dest_size, src_buf, offset, bEarlyChange);
-    if (err || *dest_size == 0 || *dest_size + 1 < *dest_size)
-      return FX_INVALID_OFFSET;
-
-    decoder = pdfium::MakeUnique<CLZWDecoder>();
-    *dest_buf = FX_Alloc(uint8_t, *dest_size + 1);
-    (*dest_buf)[*dest_size] = '\0';
-    decoder->Decode(*dest_buf, *dest_size, src_buf, offset, bEarlyChange);
-  } else {
-    FlateUncompress(src_buf, src_size, estimated_size, *dest_buf, *dest_size,
-                    offset);
-  }
-  if (predictor_type == 0)
-    return offset;
-
-  bool ret = true;
-  if (predictor_type == 2) {
-    ret =
-        PNG_Predictor(*dest_buf, *dest_size, Colors, BitsPerComponent, Columns);
-  } else if (predictor_type == 1) {
-    ret = TIFF_Predictor(*dest_buf, *dest_size, Colors, BitsPerComponent,
-                         Columns);
-  }
-  return ret ? offset : FX_INVALID_OFFSET;
-}
-
-bool CCodec_FlateModule::Encode(const uint8_t* src_buf,
-                                uint32_t src_size,
-                                uint8_t** dest_buf,
-                                uint32_t* dest_size) {
-  *dest_size = src_size + src_size / 1000 + 12;
-  *dest_buf = FX_Alloc(uint8_t, *dest_size);
-  unsigned long temp_size = *dest_size;
-  if (!FlateCompress(*dest_buf, &temp_size, src_buf, src_size))
-    return false;
-
-  *dest_size = (uint32_t)temp_size;
-  return true;
-}
-
-bool CCodec_FlateModule::PngEncode(const uint8_t* src_buf,
-                                   uint32_t src_size,
-                                   uint8_t** dest_buf,
-                                   uint32_t* dest_size) {
-  uint8_t* pSrcBuf = FX_Alloc(uint8_t, src_size);
-  memcpy(pSrcBuf, src_buf, src_size);
-  PNG_PredictorEncode(&pSrcBuf, &src_size);
-  bool ret = Encode(pSrcBuf, src_size, dest_buf, dest_size);
-  FX_Free(pSrcBuf);
-  return ret;
-}
diff --git a/core/fxcodec/codec/fx_codec_icc.cpp b/core/fxcodec/codec/fx_codec_icc.cpp
deleted file mode 100644
index 29b37d1..0000000
--- a/core/fxcodec/codec/fx_codec_icc.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <memory>
-
-#include "core/fxcodec/codec/ccodec_iccmodule.h"
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcrt/cfx_fixedbufgrow.h"
-
-namespace {
-
-bool Check3Components(cmsColorSpaceSignature cs, bool bDst) {
-  switch (cs) {
-    case cmsSigGrayData:
-      return false;
-    case cmsSigCmykData:
-      if (bDst)
-        return false;
-      break;
-    case cmsSigLabData:
-    case cmsSigRgbData:
-    default:
-      break;
-  }
-  return true;
-}
-
-}  // namespace
-
-CLcmsCmm::CLcmsCmm(int srcComponents, cmsHTRANSFORM hTransform, bool isLab)
-    : m_hTransform(hTransform),
-      m_nSrcComponents(srcComponents),
-      m_bLab(isLab) {}
-
-CLcmsCmm::~CLcmsCmm() {
-  cmsDeleteTransform(m_hTransform);
-}
-
-CCodec_IccModule::CCodec_IccModule() : m_nComponents(0) {}
-
-CCodec_IccModule::~CCodec_IccModule() {}
-
-std::unique_ptr<CLcmsCmm> CCodec_IccModule::CreateTransform_sRGB(
-    const unsigned char* pSrcProfileData,
-    uint32_t dwSrcProfileSize,
-    uint32_t* nSrcComponents) {
-  *nSrcComponents = 0;
-  cmsHPROFILE srcProfile =
-      cmsOpenProfileFromMem(pSrcProfileData, dwSrcProfileSize);
-  if (!srcProfile)
-    return nullptr;
-
-  cmsHPROFILE dstProfile;
-  dstProfile = cmsCreate_sRGBProfile();
-  if (!dstProfile) {
-    cmsCloseProfile(srcProfile);
-    return nullptr;
-  }
-  int srcFormat;
-  bool bLab = false;
-  cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile);
-
-  *nSrcComponents = cmsChannelsOf(srcCS);
-  // According to PDF spec, number of components must be 1, 3, or 4.
-  if (*nSrcComponents != 1 && *nSrcComponents != 3 && *nSrcComponents != 4) {
-    cmsCloseProfile(srcProfile);
-    cmsCloseProfile(dstProfile);
-    return nullptr;
-  }
-
-  if (srcCS == cmsSigLabData) {
-    srcFormat =
-        COLORSPACE_SH(PT_Lab) | CHANNELS_SH(*nSrcComponents) | BYTES_SH(0);
-    bLab = true;
-  } else {
-    srcFormat =
-        COLORSPACE_SH(PT_ANY) | CHANNELS_SH(*nSrcComponents) | BYTES_SH(1);
-  }
-  cmsColorSpaceSignature dstCS = cmsGetColorSpace(dstProfile);
-  if (!Check3Components(dstCS, true)) {
-    cmsCloseProfile(srcProfile);
-    cmsCloseProfile(dstProfile);
-    return nullptr;
-  }
-
-  cmsHTRANSFORM hTransform = nullptr;
-  int intent = 0;
-  switch (dstCS) {
-    case cmsSigGrayData:
-      hTransform = cmsCreateTransform(srcProfile, srcFormat, dstProfile,
-                                      TYPE_GRAY_8, intent, 0);
-      break;
-    case cmsSigRgbData:
-      hTransform = cmsCreateTransform(srcProfile, srcFormat, dstProfile,
-                                      TYPE_BGR_8, intent, 0);
-      break;
-    case cmsSigCmykData:
-      hTransform = cmsCreateTransform(srcProfile, srcFormat, dstProfile,
-                                      TYPE_CMYK_8, intent, 0);
-      break;
-    default:
-      break;
-  }
-  if (!hTransform) {
-    cmsCloseProfile(srcProfile);
-    cmsCloseProfile(dstProfile);
-    return nullptr;
-  }
-  auto pCmm = pdfium::MakeUnique<CLcmsCmm>(*nSrcComponents, hTransform, bLab);
-  cmsCloseProfile(srcProfile);
-  cmsCloseProfile(dstProfile);
-  return pCmm;
-}
-
-void CCodec_IccModule::Translate(CLcmsCmm* pTransform,
-                                 const float* pSrcValues,
-                                 float* pDestValues) {
-  if (!pTransform)
-    return;
-
-  uint32_t nSrcComponents = m_nComponents;
-  uint8_t output[4];
-  if (pTransform->m_bLab) {
-    CFX_FixedBufGrow<double, 16> inputs(nSrcComponents);
-    double* input = inputs;
-    for (uint32_t i = 0; i < nSrcComponents; ++i)
-      input[i] = pSrcValues[i];
-    cmsDoTransform(pTransform->m_hTransform, input, output, 1);
-  } else {
-    CFX_FixedBufGrow<uint8_t, 16> inputs(nSrcComponents);
-    uint8_t* input = inputs;
-    for (uint32_t i = 0; i < nSrcComponents; ++i) {
-      input[i] =
-          pdfium::clamp(static_cast<int>(pSrcValues[i] * 255.0f), 0, 255);
-    }
-    cmsDoTransform(pTransform->m_hTransform, input, output, 1);
-  }
-  pDestValues[0] = output[2] / 255.0f;
-  pDestValues[1] = output[1] / 255.0f;
-  pDestValues[2] = output[0] / 255.0f;
-}
-
-void CCodec_IccModule::TranslateScanline(CLcmsCmm* pTransform,
-                                         unsigned char* pDest,
-                                         const unsigned char* pSrc,
-                                         int32_t pixels) {
-  if (pTransform)
-    cmsDoTransform(pTransform->m_hTransform, pSrc, pDest, pixels);
-}
diff --git a/core/fxcodec/codec/fx_codec_jbig.cpp b/core/fxcodec/codec/fx_codec_jbig.cpp
deleted file mode 100644
index 543aeeb..0000000
--- a/core/fxcodec/codec/fx_codec_jbig.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/ccodec_jbig2module.h"
-
-#include <list>
-#include <memory>
-
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fxcodec/JBig2_DocumentContext.h"
-#include "core/fxcodec/jbig2/JBig2_Context.h"
-#include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "core/fxcrt/fx_memory.h"
-#include "third_party/base/ptr_util.h"
-
-JBig2_DocumentContext::JBig2_DocumentContext() {}
-
-JBig2_DocumentContext::~JBig2_DocumentContext() {}
-
-JBig2_DocumentContext* GetJBig2DocumentContext(
-    std::unique_ptr<JBig2_DocumentContext>* pContextHolder) {
-  if (!pContextHolder->get())
-    *pContextHolder = pdfium::MakeUnique<JBig2_DocumentContext>();
-  return pContextHolder->get();
-}
-
-CCodec_Jbig2Context::CCodec_Jbig2Context()
-    : m_width(0),
-      m_height(0),
-      m_pGlobalStream(nullptr),
-      m_pSrcStream(nullptr),
-      m_dest_buf(0),
-      m_dest_pitch(0) {}
-
-CCodec_Jbig2Context::~CCodec_Jbig2Context() {}
-
-CCodec_Jbig2Module::~CCodec_Jbig2Module() {}
-
-FXCODEC_STATUS CCodec_Jbig2Module::StartDecode(
-    CCodec_Jbig2Context* pJbig2Context,
-    std::unique_ptr<JBig2_DocumentContext>* pContextHolder,
-    uint32_t width,
-    uint32_t height,
-    const RetainPtr<CPDF_StreamAcc>& src_stream,
-    const RetainPtr<CPDF_StreamAcc>& global_stream,
-    uint8_t* dest_buf,
-    uint32_t dest_pitch,
-    IFX_PauseIndicator* pPause) {
-  if (!pJbig2Context)
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  JBig2_DocumentContext* pJBig2DocumentContext =
-      GetJBig2DocumentContext(pContextHolder);
-  pJbig2Context->m_width = width;
-  pJbig2Context->m_height = height;
-  pJbig2Context->m_pSrcStream = src_stream;
-  pJbig2Context->m_pGlobalStream = global_stream;
-  pJbig2Context->m_dest_buf = dest_buf;
-  pJbig2Context->m_dest_pitch = dest_pitch;
-  memset(dest_buf, 0, height * dest_pitch);
-  pJbig2Context->m_pContext = pdfium::MakeUnique<CJBig2_Context>(
-      global_stream, src_stream, pJBig2DocumentContext->GetSymbolDictCache(),
-      false);
-  int ret = pJbig2Context->m_pContext->getFirstPage(dest_buf, width, height,
-                                                    dest_pitch, pPause);
-  return Decode(pJbig2Context, ret);
-}
-
-FXCODEC_STATUS CCodec_Jbig2Module::ContinueDecode(
-    CCodec_Jbig2Context* pJbig2Context,
-    IFX_PauseIndicator* pPause) {
-  int ret = pJbig2Context->m_pContext->Continue(pPause);
-  return Decode(pJbig2Context, ret);
-}
-
-FXCODEC_STATUS CCodec_Jbig2Module::Decode(CCodec_Jbig2Context* pJbig2Context,
-                                          int result) {
-  FXCODEC_STATUS status = pJbig2Context->m_pContext->GetProcessingStatus();
-  if (status != FXCODEC_STATUS_DECODE_FINISH)
-    return status;
-
-  pJbig2Context->m_pContext.reset();
-  if (result != JBIG2_SUCCESS)
-    return FXCODEC_STATUS_ERROR;
-
-  int dword_size = pJbig2Context->m_height * pJbig2Context->m_dest_pitch / 4;
-  uint32_t* dword_buf = reinterpret_cast<uint32_t*>(pJbig2Context->m_dest_buf);
-  for (int i = 0; i < dword_size; i++)
-    dword_buf[i] = ~dword_buf[i];
-  return FXCODEC_STATUS_DECODE_FINISH;
-}
diff --git a/core/fxcodec/codec/fx_codec_jpeg.cpp b/core/fxcodec/codec/fx_codec_jpeg.cpp
deleted file mode 100644
index 6e227ca..0000000
--- a/core/fxcodec/codec/fx_codec_jpeg.cpp
+++ /dev/null
@@ -1,603 +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 <setjmp.h>
-
-#include <memory>
-#include <utility>
-
-#include "core/fxcodec/codec/ccodec_jpegmodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/dib/cfx_dibsource.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-
-extern "C" {
-#undef FAR
-#if defined(USE_SYSTEM_LIBJPEG)
-#include <jpeglib.h>
-#elif defined(USE_LIBJPEG_TURBO)
-#include "third_party/libjpeg_turbo/jpeglib.h"
-#else
-#include "third_party/libjpeg/jpeglib.h"
-#endif
-}  // extern "C"
-
-class CJpegContext : public CCodec_JpegModule::Context {
- public:
-  CJpegContext();
-  ~CJpegContext() override;
-
-  jmp_buf* GetJumpMark() override { return &m_JumpMark; }
-
-  jmp_buf m_JumpMark;
-  jpeg_decompress_struct m_Info;
-  jpeg_error_mgr m_ErrMgr;
-  jpeg_source_mgr m_SrcMgr;
-  unsigned int m_SkipSize;
-  void* (*m_AllocFunc)(unsigned int);
-  void (*m_FreeFunc)(void*);
-};
-
-extern "C" {
-
-static void JpegScanSOI(const uint8_t** src_buf, uint32_t* src_size) {
-  if (*src_size == 0)
-    return;
-
-  uint32_t offset = 0;
-  while (offset < *src_size - 1) {
-    if ((*src_buf)[offset] == 0xff && (*src_buf)[offset + 1] == 0xd8) {
-      *src_buf += offset;
-      *src_size -= offset;
-      return;
-    }
-    offset++;
-  }
-}
-
-static void _src_do_nothing(struct jpeg_decompress_struct* cinfo) {}
-
-static void _error_fatal(j_common_ptr cinfo) {
-  longjmp(*(jmp_buf*)cinfo->client_data, -1);
-}
-
-static void _src_skip_data(struct jpeg_decompress_struct* cinfo, long num) {
-  if (num > (long)cinfo->src->bytes_in_buffer) {
-    _error_fatal((j_common_ptr)cinfo);
-  }
-  cinfo->src->next_input_byte += num;
-  cinfo->src->bytes_in_buffer -= num;
-}
-
-static boolean _src_fill_buffer(j_decompress_ptr cinfo) {
-  return 0;
-}
-
-static boolean _src_resync(j_decompress_ptr cinfo, int desired) {
-  return 0;
-}
-
-static void _error_do_nothing(j_common_ptr cinfo) {}
-
-static void _error_do_nothing1(j_common_ptr cinfo, int) {}
-
-static void _error_do_nothing2(j_common_ptr cinfo, char*) {}
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-static void _dest_do_nothing(j_compress_ptr cinfo) {}
-
-static boolean _dest_empty(j_compress_ptr cinfo) {
-  return false;
-}
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-}  // extern "C"
-
-#define JPEG_MARKER_ICC (JPEG_APP0 + 2)
-#define JPEG_MARKER_MAXSIZE 0xFFFF
-
-#ifdef PDF_ENABLE_XFA
-static void JpegLoadAttribute(struct jpeg_decompress_struct* pInfo,
-                              CFX_DIBAttribute* pAttribute) {
-  if (!pAttribute)
-    return;
-
-  pAttribute->m_nXDPI = pInfo->X_density;
-  pAttribute->m_nYDPI = pInfo->Y_density;
-  pAttribute->m_wDPIUnit = pInfo->density_unit;
-}
-#endif  // PDF_ENABLE_XFA
-
-static bool JpegLoadInfo(const uint8_t* src_buf,
-                         uint32_t src_size,
-                         int* width,
-                         int* height,
-                         int* num_components,
-                         int* bits_per_components,
-                         bool* color_transform) {
-  JpegScanSOI(&src_buf, &src_size);
-  struct jpeg_decompress_struct cinfo;
-  struct jpeg_error_mgr jerr;
-  jerr.error_exit = _error_fatal;
-  jerr.emit_message = _error_do_nothing1;
-  jerr.output_message = _error_do_nothing;
-  jerr.format_message = _error_do_nothing2;
-  jerr.reset_error_mgr = _error_do_nothing;
-  jerr.trace_level = 0;
-  cinfo.err = &jerr;
-  jmp_buf mark;
-  cinfo.client_data = &mark;
-  if (setjmp(mark) == -1)
-    return false;
-
-  jpeg_create_decompress(&cinfo);
-  struct jpeg_source_mgr src;
-  src.init_source = _src_do_nothing;
-  src.term_source = _src_do_nothing;
-  src.skip_input_data = _src_skip_data;
-  src.fill_input_buffer = _src_fill_buffer;
-  src.resync_to_restart = _src_resync;
-  src.bytes_in_buffer = src_size;
-  src.next_input_byte = src_buf;
-  cinfo.src = &src;
-  if (setjmp(mark) == -1) {
-    jpeg_destroy_decompress(&cinfo);
-    return false;
-  }
-  int ret = jpeg_read_header(&cinfo, true);
-  if (ret != JPEG_HEADER_OK) {
-    jpeg_destroy_decompress(&cinfo);
-    return false;
-  }
-  *width = cinfo.image_width;
-  *height = cinfo.image_height;
-  *num_components = cinfo.num_components;
-  *color_transform =
-      cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK;
-  *bits_per_components = cinfo.data_precision;
-  jpeg_destroy_decompress(&cinfo);
-  return true;
-}
-
-class CCodec_JpegDecoder : public CCodec_ScanlineDecoder {
- public:
-  CCodec_JpegDecoder();
-  ~CCodec_JpegDecoder() override;
-
-  bool Create(const uint8_t* src_buf,
-              uint32_t src_size,
-              int width,
-              int height,
-              int nComps,
-              bool ColorTransform);
-
-  // CCodec_ScanlineDecoder
-  bool v_Rewind() override;
-  uint8_t* v_GetNextLine() override;
-  uint32_t GetSrcOffset() override;
-
-  bool InitDecode();
-
-  jmp_buf m_JmpBuf;
-  struct jpeg_decompress_struct cinfo;
-  struct jpeg_error_mgr jerr;
-  struct jpeg_source_mgr src;
-  const uint8_t* m_SrcBuf;
-  uint32_t m_SrcSize;
-  uint8_t* m_pScanlineBuf;
-
-  bool m_bInited;
-  bool m_bStarted;
-  bool m_bJpegTransform;
-
- protected:
-  uint32_t m_nDefaultScaleDenom;
-};
-
-CCodec_JpegDecoder::CCodec_JpegDecoder() {
-  m_pScanlineBuf = nullptr;
-  m_bStarted = false;
-  m_bInited = false;
-  memset(&cinfo, 0, sizeof(cinfo));
-  memset(&jerr, 0, sizeof(jerr));
-  memset(&src, 0, sizeof(src));
-  m_nDefaultScaleDenom = 1;
-}
-
-CCodec_JpegDecoder::~CCodec_JpegDecoder() {
-  FX_Free(m_pScanlineBuf);
-  if (m_bInited)
-    jpeg_destroy_decompress(&cinfo);
-}
-
-bool CCodec_JpegDecoder::InitDecode() {
-  cinfo.err = &jerr;
-  cinfo.client_data = &m_JmpBuf;
-  if (setjmp(m_JmpBuf) == -1)
-    return false;
-
-  jpeg_create_decompress(&cinfo);
-  m_bInited = true;
-  cinfo.src = &src;
-  src.bytes_in_buffer = m_SrcSize;
-  src.next_input_byte = m_SrcBuf;
-  if (setjmp(m_JmpBuf) == -1) {
-    jpeg_destroy_decompress(&cinfo);
-    m_bInited = false;
-    return false;
-  }
-  cinfo.image_width = m_OrigWidth;
-  cinfo.image_height = m_OrigHeight;
-  int ret = jpeg_read_header(&cinfo, true);
-  if (ret != JPEG_HEADER_OK)
-    return false;
-
-  if (cinfo.saw_Adobe_marker)
-    m_bJpegTransform = true;
-
-  if (cinfo.num_components == 3 && !m_bJpegTransform)
-    cinfo.out_color_space = cinfo.jpeg_color_space;
-
-  m_OrigWidth = cinfo.image_width;
-  m_OrigHeight = cinfo.image_height;
-  m_OutputWidth = m_OrigWidth;
-  m_OutputHeight = m_OrigHeight;
-  m_nDefaultScaleDenom = cinfo.scale_denom;
-  return true;
-}
-
-bool CCodec_JpegDecoder::Create(const uint8_t* src_buf,
-                                uint32_t src_size,
-                                int width,
-                                int height,
-                                int nComps,
-                                bool ColorTransform) {
-  JpegScanSOI(&src_buf, &src_size);
-  m_SrcBuf = src_buf;
-  m_SrcSize = src_size;
-  jerr.error_exit = _error_fatal;
-  jerr.emit_message = _error_do_nothing1;
-  jerr.output_message = _error_do_nothing;
-  jerr.format_message = _error_do_nothing2;
-  jerr.reset_error_mgr = _error_do_nothing;
-  src.init_source = _src_do_nothing;
-  src.term_source = _src_do_nothing;
-  src.skip_input_data = _src_skip_data;
-  src.fill_input_buffer = _src_fill_buffer;
-  src.resync_to_restart = _src_resync;
-  m_bJpegTransform = ColorTransform;
-  if (src_size > 1 && memcmp(src_buf + src_size - 2, "\xFF\xD9", 2) != 0) {
-    ((uint8_t*)src_buf)[src_size - 2] = 0xFF;
-    ((uint8_t*)src_buf)[src_size - 1] = 0xD9;
-  }
-  m_OutputWidth = m_OrigWidth = width;
-  m_OutputHeight = m_OrigHeight = height;
-  if (!InitDecode())
-    return false;
-
-  if (cinfo.num_components < nComps)
-    return false;
-
-  if ((int)cinfo.image_width < width)
-    return false;
-
-  m_Pitch =
-      (static_cast<uint32_t>(cinfo.image_width) * cinfo.num_components + 3) /
-      4 * 4;
-  m_pScanlineBuf = FX_Alloc(uint8_t, m_Pitch);
-  m_nComps = cinfo.num_components;
-  m_bpc = 8;
-  m_bStarted = false;
-  return true;
-}
-
-bool CCodec_JpegDecoder::v_Rewind() {
-  if (m_bStarted) {
-    jpeg_destroy_decompress(&cinfo);
-    if (!InitDecode()) {
-      return false;
-    }
-  }
-  if (setjmp(m_JmpBuf) == -1) {
-    return false;
-  }
-  cinfo.scale_denom = m_nDefaultScaleDenom;
-  m_OutputWidth = m_OrigWidth;
-  m_OutputHeight = m_OrigHeight;
-  if (!jpeg_start_decompress(&cinfo)) {
-    jpeg_destroy_decompress(&cinfo);
-    return false;
-  }
-  if ((int)cinfo.output_width > m_OrigWidth) {
-    NOTREACHED();
-    return false;
-  }
-  m_bStarted = true;
-  return true;
-}
-
-uint8_t* CCodec_JpegDecoder::v_GetNextLine() {
-  if (setjmp(m_JmpBuf) == -1)
-    return nullptr;
-
-  int nlines = jpeg_read_scanlines(&cinfo, &m_pScanlineBuf, 1);
-  return nlines > 0 ? m_pScanlineBuf : nullptr;
-}
-
-uint32_t CCodec_JpegDecoder::GetSrcOffset() {
-  return (uint32_t)(m_SrcSize - src.bytes_in_buffer);
-}
-
-std::unique_ptr<CCodec_ScanlineDecoder> CCodec_JpegModule::CreateDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
-    int width,
-    int height,
-    int nComps,
-    bool ColorTransform) {
-  if (!src_buf || src_size == 0)
-    return nullptr;
-
-  auto pDecoder = pdfium::MakeUnique<CCodec_JpegDecoder>();
-  if (!pDecoder->Create(src_buf, src_size, width, height, nComps,
-                        ColorTransform)) {
-    return nullptr;
-  }
-  return std::move(pDecoder);
-}
-
-bool CCodec_JpegModule::LoadInfo(const uint8_t* src_buf,
-                                 uint32_t src_size,
-                                 int* width,
-                                 int* height,
-                                 int* num_components,
-                                 int* bits_per_components,
-                                 bool* color_transform) {
-  return JpegLoadInfo(src_buf, src_size, width, height, num_components,
-                      bits_per_components, color_transform);
-}
-
-extern "C" {
-
-static void _error_fatal1(j_common_ptr cinfo) {
-  auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
-  longjmp(pContext->m_JumpMark, -1);
-}
-
-static void _src_skip_data1(struct jpeg_decompress_struct* cinfo, long num) {
-  if (cinfo->src->bytes_in_buffer < static_cast<size_t>(num)) {
-    auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
-    pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer);
-    cinfo->src->bytes_in_buffer = 0;
-  } else {
-    cinfo->src->next_input_byte += num;
-    cinfo->src->bytes_in_buffer -= num;
-  }
-}
-
-static void* jpeg_alloc_func(unsigned int size) {
-  return FX_Alloc(char, size);
-}
-
-static void jpeg_free_func(void* p) {
-  FX_Free(p);
-}
-
-}  // extern "C"
-
-CJpegContext::CJpegContext()
-    : m_SkipSize(0), m_AllocFunc(jpeg_alloc_func), m_FreeFunc(jpeg_free_func) {
-  memset(&m_Info, 0, sizeof(m_Info));
-  m_Info.client_data = this;
-  m_Info.err = &m_ErrMgr;
-
-  memset(&m_ErrMgr, 0, sizeof(m_ErrMgr));
-  m_ErrMgr.error_exit = _error_fatal1;
-  m_ErrMgr.emit_message = _error_do_nothing1;
-  m_ErrMgr.output_message = _error_do_nothing;
-  m_ErrMgr.format_message = _error_do_nothing2;
-  m_ErrMgr.reset_error_mgr = _error_do_nothing;
-
-  memset(&m_SrcMgr, 0, sizeof(m_SrcMgr));
-  m_SrcMgr.init_source = _src_do_nothing;
-  m_SrcMgr.term_source = _src_do_nothing;
-  m_SrcMgr.skip_input_data = _src_skip_data1;
-  m_SrcMgr.fill_input_buffer = _src_fill_buffer;
-  m_SrcMgr.resync_to_restart = _src_resync;
-}
-
-CJpegContext::~CJpegContext() {
-  jpeg_destroy_decompress(&m_Info);
-}
-
-std::unique_ptr<CCodec_JpegModule::Context> CCodec_JpegModule::Start() {
-  // Use ordinary pointer until past the possibility of a longjump.
-  auto* pContext = new CJpegContext();
-  if (setjmp(pContext->m_JumpMark) == -1)
-    return nullptr;
-
-  jpeg_create_decompress(&pContext->m_Info);
-  pContext->m_Info.src = &pContext->m_SrcMgr;
-  pContext->m_SkipSize = 0;
-  return pdfium::WrapUnique(pContext);
-}
-
-void CCodec_JpegModule::Input(Context* pContext,
-                              const unsigned char* src_buf,
-                              uint32_t src_size) {
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  if (ctx->m_SkipSize) {
-    if (ctx->m_SkipSize > src_size) {
-      ctx->m_SrcMgr.bytes_in_buffer = 0;
-      ctx->m_SkipSize -= src_size;
-      return;
-    }
-    src_size -= ctx->m_SkipSize;
-    src_buf += ctx->m_SkipSize;
-    ctx->m_SkipSize = 0;
-  }
-  ctx->m_SrcMgr.next_input_byte = src_buf;
-  ctx->m_SrcMgr.bytes_in_buffer = src_size;
-}
-
-#ifdef PDF_ENABLE_XFA
-int CCodec_JpegModule::ReadHeader(Context* pContext,
-                                  int* width,
-                                  int* height,
-                                  int* nComps,
-                                  CFX_DIBAttribute* pAttribute) {
-#else   // PDF_ENABLE_XFA
-int CCodec_JpegModule::ReadHeader(Context* pContext,
-                                  int* width,
-                                  int* height,
-                                  int* nComps) {
-#endif  // PDF_ENABLE_XFA
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  int ret = jpeg_read_header(&ctx->m_Info, true);
-  if (ret == JPEG_SUSPENDED)
-    return 2;
-  if (ret != JPEG_HEADER_OK)
-    return 1;
-
-  *width = ctx->m_Info.image_width;
-  *height = ctx->m_Info.image_height;
-  *nComps = ctx->m_Info.num_components;
-#ifdef PDF_ENABLE_XFA
-  JpegLoadAttribute(&ctx->m_Info, pAttribute);
-#endif
-  return 0;
-}
-
-bool CCodec_JpegModule::StartScanline(Context* pContext, int down_scale) {
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  ctx->m_Info.scale_denom = static_cast<unsigned int>(down_scale);
-  return !!jpeg_start_decompress(&ctx->m_Info);
-}
-
-bool CCodec_JpegModule::ReadScanline(Context* pContext,
-                                     unsigned char* dest_buf) {
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1);
-  return nlines == 1;
-}
-
-uint32_t CCodec_JpegModule::GetAvailInput(Context* pContext,
-                                          uint8_t** avail_buf_ptr) {
-  auto* ctx = static_cast<CJpegContext*>(pContext);
-  if (avail_buf_ptr) {
-    *avail_buf_ptr = nullptr;
-    if (ctx->m_SrcMgr.bytes_in_buffer > 0) {
-      *avail_buf_ptr = (uint8_t*)ctx->m_SrcMgr.next_input_byte;
-    }
-  }
-  return (uint32_t)ctx->m_SrcMgr.bytes_in_buffer;
-}
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#define JPEG_BLOCK_SIZE 1048576
-bool CCodec_JpegModule::JpegEncode(const RetainPtr<CFX_DIBSource>& pSource,
-                                   uint8_t** dest_buf,
-                                   size_t* dest_size) {
-  struct jpeg_error_mgr jerr;
-  jerr.error_exit = _error_do_nothing;
-  jerr.emit_message = _error_do_nothing1;
-  jerr.output_message = _error_do_nothing;
-  jerr.format_message = _error_do_nothing2;
-  jerr.reset_error_mgr = _error_do_nothing;
-
-  struct jpeg_compress_struct cinfo;
-  memset(&cinfo, 0, sizeof(cinfo));
-  cinfo.err = &jerr;
-  jpeg_create_compress(&cinfo);
-  int Bpp = pSource->GetBPP() / 8;
-  uint32_t nComponents = Bpp >= 3 ? (pSource->IsCmykImage() ? 4 : 3) : 1;
-  uint32_t pitch = pSource->GetPitch();
-  uint32_t width = pdfium::base::checked_cast<uint32_t>(pSource->GetWidth());
-  uint32_t height = pdfium::base::checked_cast<uint32_t>(pSource->GetHeight());
-  FX_SAFE_UINT32 safe_buf_len = width;
-  safe_buf_len *= height;
-  safe_buf_len *= nComponents;
-  safe_buf_len += 1024;
-  if (!safe_buf_len.IsValid())
-    return false;
-
-  uint32_t dest_buf_length = safe_buf_len.ValueOrDie();
-  *dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
-  const int MIN_TRY_BUF_LEN = 1024;
-  while (!(*dest_buf) && dest_buf_length > MIN_TRY_BUF_LEN) {
-    dest_buf_length >>= 1;
-    *dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
-  }
-  if (!(*dest_buf))
-    return false;
-
-  struct jpeg_destination_mgr dest;
-  dest.init_destination = _dest_do_nothing;
-  dest.term_destination = _dest_do_nothing;
-  dest.empty_output_buffer = _dest_empty;
-  dest.next_output_byte = *dest_buf;
-  dest.free_in_buffer = dest_buf_length;
-  cinfo.dest = &dest;
-  cinfo.image_width = width;
-  cinfo.image_height = height;
-  cinfo.input_components = nComponents;
-  if (nComponents == 1) {
-    cinfo.in_color_space = JCS_GRAYSCALE;
-  } else if (nComponents == 3) {
-    cinfo.in_color_space = JCS_RGB;
-  } else {
-    cinfo.in_color_space = JCS_CMYK;
-  }
-  uint8_t* line_buf = nullptr;
-  if (nComponents > 1)
-    line_buf = FX_Alloc2D(uint8_t, width, nComponents);
-
-  jpeg_set_defaults(&cinfo);
-  jpeg_start_compress(&cinfo, TRUE);
-  JSAMPROW row_pointer[1];
-  JDIMENSION row;
-  while (cinfo.next_scanline < cinfo.image_height) {
-    const uint8_t* src_scan = pSource->GetScanline(cinfo.next_scanline);
-    if (nComponents > 1) {
-      uint8_t* dest_scan = line_buf;
-      if (nComponents == 3) {
-        for (uint32_t i = 0; i < width; i++) {
-          dest_scan[0] = src_scan[2];
-          dest_scan[1] = src_scan[1];
-          dest_scan[2] = src_scan[0];
-          dest_scan += 3;
-          src_scan += Bpp;
-        }
-      } else {
-        for (uint32_t i = 0; i < pitch; i++) {
-          *dest_scan++ = ~*src_scan++;
-        }
-      }
-      row_pointer[0] = line_buf;
-    } else {
-      row_pointer[0] = (uint8_t*)src_scan;
-    }
-    row = cinfo.next_scanline;
-    jpeg_write_scanlines(&cinfo, row_pointer, 1);
-    if (cinfo.next_scanline == row) {
-      *dest_buf =
-          FX_Realloc(uint8_t, *dest_buf, dest_buf_length + JPEG_BLOCK_SIZE);
-      dest.next_output_byte = *dest_buf + dest_buf_length - dest.free_in_buffer;
-      dest_buf_length += JPEG_BLOCK_SIZE;
-      dest.free_in_buffer += JPEG_BLOCK_SIZE;
-    }
-  }
-  jpeg_finish_compress(&cinfo);
-  jpeg_destroy_compress(&cinfo);
-  FX_Free(line_buf);
-  *dest_size = dest_buf_length - static_cast<size_t>(dest.free_in_buffer);
-
-  return true;
-}
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
diff --git a/core/fxcodec/codec/fx_codec_jpx_opj.cpp b/core/fxcodec/codec/fx_codec_jpx_opj.cpp
deleted file mode 100644
index 89c5123..0000000
--- a/core/fxcodec/codec/fx_codec_jpx_opj.cpp
+++ /dev/null
@@ -1,639 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_colorspace.h"
-#include "core/fxcodec/codec/ccodec_jpxmodule.h"
-#include "core/fxcodec/codec/cjpx_decoder.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/libopenjpeg20/openjpeg.h"
-#include "third_party/libopenjpeg20/opj_malloc.h"
-
-namespace {
-
-void fx_ignore_callback(const char* msg, void* client_data) {}
-
-opj_stream_t* fx_opj_stream_create_memory_stream(DecodeData* data,
-                                                 OPJ_SIZE_T p_size,
-                                                 OPJ_BOOL p_is_read_stream) {
-  if (!data || !data->src_data || data->src_size <= 0)
-    return nullptr;
-
-  opj_stream_t* stream = opj_stream_create(p_size, p_is_read_stream);
-  if (!stream)
-    return nullptr;
-
-  opj_stream_set_user_data(stream, data, nullptr);
-  opj_stream_set_user_data_length(stream, data->src_size);
-  opj_stream_set_read_function(stream, opj_read_from_memory);
-  opj_stream_set_skip_function(stream, opj_skip_from_memory);
-  opj_stream_set_seek_function(stream, opj_seek_from_memory);
-  return stream;
-}
-
-void sycc_to_rgb(int offset,
-                 int upb,
-                 int y,
-                 int cb,
-                 int cr,
-                 int* out_r,
-                 int* out_g,
-                 int* out_b) {
-  cb -= offset;
-  cr -= offset;
-  *out_r = pdfium::clamp(y + static_cast<int>(1.402 * cr), 0, upb);
-  *out_g = pdfium::clamp(y - static_cast<int>(0.344 * cb + 0.714 * cr), 0, upb);
-  *out_b = pdfium::clamp(y + static_cast<int>(1.772 * cb), 0, upb);
-}
-
-void sycc444_to_rgb(opj_image_t* img) {
-  int prec = img->comps[0].prec;
-  // If we shift 31 we're going to go negative, then things go bad.
-  if (prec > 30)
-    return;
-  int offset = 1 << (prec - 1);
-  int upb = (1 << prec) - 1;
-  OPJ_UINT32 maxw =
-      std::min({img->comps[0].w, img->comps[1].w, img->comps[2].w});
-  OPJ_UINT32 maxh =
-      std::min({img->comps[0].h, img->comps[1].h, img->comps[2].h});
-  FX_SAFE_SIZE_T max_size = maxw;
-  max_size *= maxh;
-  max_size *= sizeof(int);
-  if (!max_size.IsValid())
-    return;
-
-  const int* y = img->comps[0].data;
-  const int* cb = img->comps[1].data;
-  const int* cr = img->comps[2].data;
-  if (!y || !cb || !cr)
-    return;
-
-  int* r = static_cast<int*>(opj_image_data_alloc(max_size.ValueOrDie()));
-  int* g = static_cast<int*>(opj_image_data_alloc(max_size.ValueOrDie()));
-  int* b = static_cast<int*>(opj_image_data_alloc(max_size.ValueOrDie()));
-  int* d0 = r;
-  int* d1 = g;
-  int* d2 = b;
-  max_size /= sizeof(int);
-  for (size_t i = 0; i < max_size.ValueOrDie(); ++i) {
-    sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
-  }
-  opj_image_data_free(img->comps[0].data);
-  opj_image_data_free(img->comps[1].data);
-  opj_image_data_free(img->comps[2].data);
-  img->comps[0].data = d0;
-  img->comps[1].data = d1;
-  img->comps[2].data = d2;
-}
-
-bool sycc420_422_size_is_valid(opj_image_t* img) {
-  return img && img->comps[0].w != std::numeric_limits<OPJ_UINT32>::max() &&
-         (img->comps[0].w + 1) / 2 == img->comps[1].w &&
-         img->comps[1].w == img->comps[2].w &&
-         img->comps[1].h == img->comps[2].h;
-}
-
-bool sycc420_size_is_valid(opj_image_t* img) {
-  return sycc420_422_size_is_valid(img) &&
-         img->comps[0].h != std::numeric_limits<OPJ_UINT32>::max() &&
-         (img->comps[0].h + 1) / 2 == img->comps[1].h;
-}
-
-bool sycc422_size_is_valid(opj_image_t* img) {
-  return sycc420_422_size_is_valid(img) && img->comps[0].h == img->comps[1].h;
-}
-
-void sycc422_to_rgb(opj_image_t* img) {
-  if (!sycc422_size_is_valid(img))
-    return;
-
-  int prec = img->comps[0].prec;
-  if (prec <= 0 || prec >= 32)
-    return;
-
-  int offset = 1 << (prec - 1);
-  int upb = (1 << prec) - 1;
-  OPJ_UINT32 maxw = img->comps[0].w;
-  OPJ_UINT32 maxh = img->comps[0].h;
-  FX_SAFE_SIZE_T max_size = maxw;
-  max_size *= maxh;
-  max_size *= sizeof(int);
-  if (!max_size.IsValid())
-    return;
-
-  const int* y = img->comps[0].data;
-  const int* cb = img->comps[1].data;
-  const int* cr = img->comps[2].data;
-  if (!y || !cb || !cr)
-    return;
-
-  int* r = static_cast<int*>(opj_image_data_alloc(max_size.ValueOrDie()));
-  int* g = static_cast<int*>(opj_image_data_alloc(max_size.ValueOrDie()));
-  int* b = static_cast<int*>(opj_image_data_alloc(max_size.ValueOrDie()));
-  int* d0 = r;
-  int* d1 = g;
-  int* d2 = b;
-  for (uint32_t i = 0; i < maxh; ++i) {
-    OPJ_UINT32 j;
-    for (j = 0; j < (maxw & ~static_cast<OPJ_UINT32>(1)); j += 2) {
-      sycc_to_rgb(offset, upb, *y++, *cb, *cr, r++, g++, b++);
-      sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
-    }
-    if (j < maxw) {
-      sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
-    }
-  }
-  opj_image_data_free(img->comps[0].data);
-  opj_image_data_free(img->comps[1].data);
-  opj_image_data_free(img->comps[2].data);
-  img->comps[0].data = d0;
-  img->comps[1].data = d1;
-  img->comps[2].data = d2;
-  img->comps[1].w = maxw;
-  img->comps[1].h = maxh;
-  img->comps[2].w = maxw;
-  img->comps[2].h = maxh;
-  img->comps[1].dx = img->comps[0].dx;
-  img->comps[2].dx = img->comps[0].dx;
-  img->comps[1].dy = img->comps[0].dy;
-  img->comps[2].dy = img->comps[0].dy;
-}
-
-bool sycc420_must_extend_cbcr(OPJ_UINT32 y, OPJ_UINT32 cbcr) {
-  return (y & 1) && (cbcr == y / 2);
-}
-
-bool is_sycc420(const opj_image_t* img) {
-  return img->comps[0].dx == 1 && img->comps[0].dy == 1 &&
-         img->comps[1].dx == 2 && img->comps[1].dy == 2 &&
-         img->comps[2].dx == 2 && img->comps[2].dy == 2;
-}
-
-bool is_sycc422(const opj_image_t* img) {
-  return img->comps[0].dx == 1 && img->comps[0].dy == 1 &&
-         img->comps[1].dx == 2 && img->comps[1].dy == 1 &&
-         img->comps[2].dx == 2 && img->comps[2].dy == 1;
-}
-
-bool is_sycc444(const opj_image_t* img) {
-  return img->comps[0].dx == 1 && img->comps[0].dy == 1 &&
-         img->comps[1].dx == 1 && img->comps[1].dy == 1 &&
-         img->comps[2].dx == 1 && img->comps[2].dy == 1;
-}
-
-void color_sycc_to_rgb(opj_image_t* img) {
-  if (img->numcomps < 3) {
-    img->color_space = OPJ_CLRSPC_GRAY;
-    return;
-  }
-  if (is_sycc420(img))
-    sycc420_to_rgb(img);
-  else if (is_sycc422(img))
-    sycc422_to_rgb(img);
-  else if (is_sycc444(img))
-    sycc444_to_rgb(img);
-  else
-    return;
-
-  img->color_space = OPJ_CLRSPC_SRGB;
-}
-
-}  // namespace
-
-OPJ_SIZE_T opj_read_from_memory(void* p_buffer,
-                                OPJ_SIZE_T nb_bytes,
-                                void* p_user_data) {
-  DecodeData* srcData = static_cast<DecodeData*>(p_user_data);
-  if (!srcData || !srcData->src_data || srcData->src_size == 0)
-    return static_cast<OPJ_SIZE_T>(-1);
-
-  // Reads at EOF return an error code.
-  if (srcData->offset >= srcData->src_size)
-    return static_cast<OPJ_SIZE_T>(-1);
-
-  OPJ_SIZE_T bufferLength = srcData->src_size - srcData->offset;
-  OPJ_SIZE_T readlength = nb_bytes < bufferLength ? nb_bytes : bufferLength;
-  memcpy(p_buffer, &srcData->src_data[srcData->offset], readlength);
-  srcData->offset += readlength;
-  return readlength;
-}
-
-OPJ_OFF_T opj_skip_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data) {
-  DecodeData* srcData = static_cast<DecodeData*>(p_user_data);
-  if (!srcData || !srcData->src_data || srcData->src_size == 0)
-    return static_cast<OPJ_OFF_T>(-1);
-
-  // Offsets are signed and may indicate a negative skip. Do not support this
-  // because of the strange return convention where either bytes skipped or
-  // -1 is returned. Following that convention, a successful relative seek of
-  // -1 bytes would be required to to give the same result as the error case.
-  if (nb_bytes < 0)
-    return static_cast<OPJ_OFF_T>(-1);
-
-  // FIXME: use std::make_unsigned<OPJ_OFF_T>::type once c++11 lib is OK'd.
-  uint64_t unsignedNbBytes = static_cast<uint64_t>(nb_bytes);
-  // Additionally, the offset may take us beyond the range of a size_t (e.g.
-  // 32-bit platforms). If so, just clamp at EOF.
-  if (unsignedNbBytes >
-      std::numeric_limits<OPJ_SIZE_T>::max() - srcData->offset) {
-    srcData->offset = srcData->src_size;
-  } else {
-    OPJ_SIZE_T checkedNbBytes = static_cast<OPJ_SIZE_T>(unsignedNbBytes);
-    // Otherwise, mimic fseek() semantics to always succeed, even past EOF,
-    // clamping at EOF.  We can get away with this since we don't actually
-    // provide negative relative skips from beyond EOF back to inside the
-    // data, which would be the only reason to need to know exactly how far
-    // beyond EOF we are.
-    srcData->offset =
-        std::min(srcData->offset + checkedNbBytes, srcData->src_size);
-  }
-  return nb_bytes;
-}
-
-OPJ_BOOL opj_seek_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data) {
-  DecodeData* srcData = static_cast<DecodeData*>(p_user_data);
-  if (!srcData || !srcData->src_data || srcData->src_size == 0)
-    return OPJ_FALSE;
-
-  // Offsets are signed and may indicate a negative position, which would
-  // be before the start of the file. Do not support this.
-  if (nb_bytes < 0)
-    return OPJ_FALSE;
-
-  // FIXME: use std::make_unsigned<OPJ_OFF_T>::type once c++11 lib is OK'd.
-  uint64_t unsignedNbBytes = static_cast<uint64_t>(nb_bytes);
-  // Additionally, the offset may take us beyond the range of a size_t (e.g.
-  // 32-bit platforms). If so, just clamp at EOF.
-  if (unsignedNbBytes > std::numeric_limits<OPJ_SIZE_T>::max()) {
-    srcData->offset = srcData->src_size;
-  } else {
-    OPJ_SIZE_T checkedNbBytes = static_cast<OPJ_SIZE_T>(nb_bytes);
-    // Otherwise, mimic fseek() semantics to always succeed, even past EOF,
-    // again clamping at EOF.
-    srcData->offset = std::min(checkedNbBytes, srcData->src_size);
-  }
-  return OPJ_TRUE;
-}
-
-void sycc420_to_rgb(opj_image_t* img) {
-  if (!sycc420_size_is_valid(img))
-    return;
-
-  OPJ_UINT32 prec = img->comps[0].prec;
-  if (!prec)
-    return;
-
-  OPJ_UINT32 offset = 1 << (prec - 1);
-  OPJ_UINT32 upb = (1 << prec) - 1;
-  OPJ_UINT32 yw = img->comps[0].w;
-  OPJ_UINT32 yh = img->comps[0].h;
-  OPJ_UINT32 cbw = img->comps[1].w;
-  OPJ_UINT32 cbh = img->comps[1].h;
-  OPJ_UINT32 crw = img->comps[2].w;
-  bool extw = sycc420_must_extend_cbcr(yw, cbw);
-  bool exth = sycc420_must_extend_cbcr(yh, cbh);
-  FX_SAFE_UINT32 safeSize = yw;
-  safeSize *= yh;
-  safeSize *= sizeof(int);
-  if (!safeSize.IsValid())
-    return;
-
-  int* r = static_cast<int*>(opj_image_data_alloc(safeSize.ValueOrDie()));
-  int* g = static_cast<int*>(opj_image_data_alloc(safeSize.ValueOrDie()));
-  int* b = static_cast<int*>(opj_image_data_alloc(safeSize.ValueOrDie()));
-  int* d0 = r;
-  int* d1 = g;
-  int* d2 = b;
-  const int* y = img->comps[0].data;
-  const int* cb = img->comps[1].data;
-  const int* cr = img->comps[2].data;
-  if (!y || !cb || !cr)
-    return;
-
-  const int* ny = nullptr;
-  int* nr = nullptr;
-  int* ng = nullptr;
-  int* nb = nullptr;
-  OPJ_UINT32 i = 0;
-  OPJ_UINT32 j = 0;
-  for (i = 0; i < (yh & ~(OPJ_UINT32)1); i += 2) {
-    ny = y + yw;
-    nr = r + yw;
-    ng = g + yw;
-    nb = b + yw;
-    for (j = 0; j < (yw & ~(OPJ_UINT32)1); j += 2) {
-      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
-      ++y;
-      ++r;
-      ++g;
-      ++b;
-      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
-      ++y;
-      ++r;
-      ++g;
-      ++b;
-      sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
-      ++ny;
-      ++nr;
-      ++ng;
-      ++nb;
-      sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
-      ++ny;
-      ++nr;
-      ++ng;
-      ++nb;
-      ++cb;
-      ++cr;
-    }
-    if (j < yw) {
-      if (extw) {
-        --cb;
-        --cr;
-      }
-      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
-      ++y;
-      ++r;
-      ++g;
-      ++b;
-      sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
-      ++ny;
-      ++nr;
-      ++ng;
-      ++nb;
-      ++cb;
-      ++cr;
-    }
-    y += yw;
-    r += yw;
-    g += yw;
-    b += yw;
-  }
-  if (i < yh) {
-    if (exth) {
-      cb -= cbw;
-      cr -= crw;
-    }
-    for (j = 0; j < (yw & ~(OPJ_UINT32)1); j += 2) {
-      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
-      ++y;
-      ++r;
-      ++g;
-      ++b;
-      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
-      ++y;
-      ++r;
-      ++g;
-      ++b;
-      ++cb;
-      ++cr;
-    }
-    if (j < yw) {
-      if (extw) {
-        --cb;
-        --cr;
-      }
-      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
-    }
-  }
-
-  opj_image_data_free(img->comps[0].data);
-  opj_image_data_free(img->comps[1].data);
-  opj_image_data_free(img->comps[2].data);
-  img->comps[0].data = d0;
-  img->comps[1].data = d1;
-  img->comps[2].data = d2;
-  img->comps[1].w = yw;
-  img->comps[1].h = yh;
-  img->comps[2].w = yw;
-  img->comps[2].h = yh;
-  img->comps[1].w = yw;
-  img->comps[1].h = yh;
-  img->comps[2].w = yw;
-  img->comps[2].h = yh;
-  img->comps[1].dx = img->comps[0].dx;
-  img->comps[2].dx = img->comps[0].dx;
-  img->comps[1].dy = img->comps[0].dy;
-  img->comps[2].dy = img->comps[0].dy;
-}
-
-CJPX_Decoder::CJPX_Decoder(CPDF_ColorSpace* cs)
-    : m_Image(nullptr),
-      m_Codec(nullptr),
-      m_DecodeData(nullptr),
-      m_Stream(nullptr),
-      m_ColorSpace(cs) {}
-
-CJPX_Decoder::~CJPX_Decoder() {
-  if (m_Codec)
-    opj_destroy_codec(m_Codec);
-  if (m_Stream)
-    opj_stream_destroy(m_Stream);
-  if (m_Image)
-    opj_image_destroy(m_Image);
-}
-
-bool CJPX_Decoder::Init(const unsigned char* src_data, uint32_t src_size) {
-  static const unsigned char szJP2Header[] = {
-      0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a};
-  if (!src_data || src_size < sizeof(szJP2Header))
-    return false;
-
-  m_Image = nullptr;
-  m_SrcData = src_data;
-  m_SrcSize = src_size;
-  m_DecodeData = pdfium::MakeUnique<DecodeData>(
-      const_cast<unsigned char*>(src_data), src_size);
-  m_Stream = fx_opj_stream_create_memory_stream(
-      m_DecodeData.get(), static_cast<unsigned int>(OPJ_J2K_STREAM_CHUNK_SIZE),
-      1);
-  if (!m_Stream)
-    return false;
-
-  opj_set_default_decoder_parameters(&m_Parameters);
-  m_Parameters.decod_format = 0;
-  m_Parameters.cod_format = 3;
-  if (memcmp(m_SrcData, szJP2Header, sizeof(szJP2Header)) == 0) {
-    m_Codec = opj_create_decompress(OPJ_CODEC_JP2);
-    m_Parameters.decod_format = 1;
-  } else {
-    m_Codec = opj_create_decompress(OPJ_CODEC_J2K);
-  }
-  if (!m_Codec)
-    return false;
-
-  if (m_ColorSpace && m_ColorSpace->GetFamily() == PDFCS_INDEXED)
-    m_Parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
-  opj_set_info_handler(m_Codec, fx_ignore_callback, nullptr);
-  opj_set_warning_handler(m_Codec, fx_ignore_callback, nullptr);
-  opj_set_error_handler(m_Codec, fx_ignore_callback, nullptr);
-  if (!opj_setup_decoder(m_Codec, &m_Parameters))
-    return false;
-
-  if (!opj_read_header(m_Stream, m_Codec, &m_Image)) {
-    m_Image = nullptr;
-    return false;
-  }
-  m_Image->pdfium_use_colorspace = !!m_ColorSpace;
-
-  if (!m_Parameters.nb_tile_to_decode) {
-    if (!opj_set_decode_area(m_Codec, m_Image, m_Parameters.DA_x0,
-                             m_Parameters.DA_y0, m_Parameters.DA_x1,
-                             m_Parameters.DA_y1)) {
-      opj_image_destroy(m_Image);
-      m_Image = nullptr;
-      return false;
-    }
-    if (!(opj_decode(m_Codec, m_Stream, m_Image) &&
-          opj_end_decompress(m_Codec, m_Stream))) {
-      opj_image_destroy(m_Image);
-      m_Image = nullptr;
-      return false;
-    }
-  } else if (!opj_get_decoded_tile(m_Codec, m_Stream, m_Image,
-                                   m_Parameters.tile_index)) {
-    return false;
-  }
-
-  opj_stream_destroy(m_Stream);
-  m_Stream = nullptr;
-  if (m_Image->color_space != OPJ_CLRSPC_SYCC && m_Image->numcomps == 3 &&
-      m_Image->comps[0].dx == m_Image->comps[0].dy &&
-      m_Image->comps[1].dx != 1) {
-    m_Image->color_space = OPJ_CLRSPC_SYCC;
-  } else if (m_Image->numcomps <= 2) {
-    m_Image->color_space = OPJ_CLRSPC_GRAY;
-  }
-  if (m_Image->color_space == OPJ_CLRSPC_SYCC)
-    color_sycc_to_rgb(m_Image);
-
-  if (m_Image->icc_profile_buf) {
-    // TODO(palmer): Using |opj_free| here resolves the crash described in
-    // https://crbug.com/737033, but ultimately we need to harmonize the
-    // memory allocation strategy across OpenJPEG and its PDFium callers.
-    opj_free(m_Image->icc_profile_buf);
-    m_Image->icc_profile_buf = nullptr;
-    m_Image->icc_profile_len = 0;
-  }
-
-  return true;
-}
-
-void CJPX_Decoder::GetInfo(uint32_t* width,
-                           uint32_t* height,
-                           uint32_t* components) {
-  *width = m_Image->x1;
-  *height = m_Image->y1;
-  *components = m_Image->numcomps;
-}
-
-bool CJPX_Decoder::Decode(uint8_t* dest_buf,
-                          int pitch,
-                          const std::vector<uint8_t>& offsets) {
-  if (m_Image->comps[0].w != m_Image->x1 || m_Image->comps[0].h != m_Image->y1)
-    return false;
-
-  if (pitch<static_cast<int>(m_Image->comps[0].w * 8 * m_Image->numcomps + 31)>>
-      5 << 2) {
-    return false;
-  }
-
-  memset(dest_buf, 0xff, m_Image->y1 * pitch);
-  std::vector<uint8_t*> channel_bufs(m_Image->numcomps);
-  std::vector<int> adjust_comps(m_Image->numcomps);
-  for (uint32_t i = 0; i < m_Image->numcomps; i++) {
-    channel_bufs[i] = dest_buf + offsets[i];
-    adjust_comps[i] = m_Image->comps[i].prec - 8;
-    if (i > 0) {
-      if (m_Image->comps[i].dx != m_Image->comps[i - 1].dx ||
-          m_Image->comps[i].dy != m_Image->comps[i - 1].dy ||
-          m_Image->comps[i].prec != m_Image->comps[i - 1].prec) {
-        return false;
-      }
-    }
-  }
-  int width = m_Image->comps[0].w;
-  int height = m_Image->comps[0].h;
-  for (uint32_t channel = 0; channel < m_Image->numcomps; ++channel) {
-    uint8_t* pChannel = channel_bufs[channel];
-    if (adjust_comps[channel] < 0) {
-      for (int row = 0; row < height; ++row) {
-        uint8_t* pScanline = pChannel + row * pitch;
-        for (int col = 0; col < width; ++col) {
-          uint8_t* pPixel = pScanline + col * m_Image->numcomps;
-          if (!m_Image->comps[channel].data)
-            continue;
-
-          int src = m_Image->comps[channel].data[row * width + col];
-          src += m_Image->comps[channel].sgnd
-                     ? 1 << (m_Image->comps[channel].prec - 1)
-                     : 0;
-          if (adjust_comps[channel] > 0) {
-            *pPixel = 0;
-          } else {
-            *pPixel = static_cast<uint8_t>(src << -adjust_comps[channel]);
-          }
-        }
-      }
-    } else {
-      for (int row = 0; row < height; ++row) {
-        uint8_t* pScanline = pChannel + row * pitch;
-        for (int col = 0; col < width; ++col) {
-          uint8_t* pPixel = pScanline + col * m_Image->numcomps;
-          if (!m_Image->comps[channel].data)
-            continue;
-
-          int src = m_Image->comps[channel].data[row * width + col];
-          src += m_Image->comps[channel].sgnd
-                     ? 1 << (m_Image->comps[channel].prec - 1)
-                     : 0;
-          if (adjust_comps[channel] - 1 < 0) {
-            *pPixel = static_cast<uint8_t>((src >> adjust_comps[channel]));
-          } else {
-            int tmpPixel = (src >> adjust_comps[channel]) +
-                           ((src >> (adjust_comps[channel] - 1)) % 2);
-            tmpPixel = pdfium::clamp(tmpPixel, 0, 255);
-            *pPixel = static_cast<uint8_t>(tmpPixel);
-          }
-        }
-      }
-    }
-  }
-  return true;
-}
-
-CCodec_JpxModule::CCodec_JpxModule() {}
-
-CCodec_JpxModule::~CCodec_JpxModule() {}
-
-std::unique_ptr<CJPX_Decoder> CCodec_JpxModule::CreateDecoder(
-    const uint8_t* src_buf,
-    uint32_t src_size,
-    CPDF_ColorSpace* cs) {
-  auto decoder = pdfium::MakeUnique<CJPX_Decoder>(cs);
-  return decoder->Init(src_buf, src_size) ? std::move(decoder) : nullptr;
-}
-
-void CCodec_JpxModule::GetImageInfo(CJPX_Decoder* pDecoder,
-                                    uint32_t* width,
-                                    uint32_t* height,
-                                    uint32_t* components) {
-  pDecoder->GetInfo(width, height, components);
-}
-
-bool CCodec_JpxModule::Decode(CJPX_Decoder* pDecoder,
-                              uint8_t* dest_data,
-                              int pitch,
-                              const std::vector<uint8_t>& offsets) {
-  return pDecoder->Decode(dest_data, pitch, offsets);
-}
diff --git a/core/fxcodec/codec/fx_codec_jpx_unittest.cpp b/core/fxcodec/codec/fx_codec_jpx_unittest.cpp
deleted file mode 100644
index 1bd368e..0000000
--- a/core/fxcodec/codec/fx_codec_jpx_unittest.cpp
+++ /dev/null
@@ -1,469 +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.
-
-#include <stdint.h>
-
-#include <limits>
-
-#include "core/fxcodec/codec/codec_int.h"
-#include "core/fxcodec/fx_codec.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/libopenjpeg20/opj_malloc.h"
-
-static const OPJ_OFF_T kSkipError = static_cast<OPJ_OFF_T>(-1);
-static const OPJ_SIZE_T kReadError = static_cast<OPJ_SIZE_T>(-1);
-
-static const uint8_t stream_data[] = {
-    0x00, 0x01, 0x02, 0x03,
-    0x84, 0x85, 0x86, 0x87,  // Include some hi-bytes, too.
-};
-
-union Float_t {
-  Float_t(float num = 0.0f) : f(num) {}
-
-  int32_t i;
-  float f;
-};
-
-TEST(fxcodec, CMYK_Rounding) {
-  // Testing all floats from 0.0 to 1.0 takes about 35 seconds in release
-  // builds and much longer in debug builds, so just test the known-dangerous
-  // range.
-  const float startValue = 0.001f;
-  const float endValue = 0.003f;
-  float R = 0.0f, G = 0.0f, B = 0.0f;
-  // Iterate through floats by incrementing the representation, as discussed in
-  // https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/
-  for (Float_t f = startValue; f.f < endValue; f.i++) {
-    std::tie(R, G, B) = AdobeCMYK_to_sRGB(f.f, f.f, f.f, f.f);
-  }
-  // Check various other 'special' numbers.
-  std::tie(R, G, B) = AdobeCMYK_to_sRGB(0.0f, 0.25f, 0.5f, 1.0f);
-}
-
-TEST(fxcodec, DecodeDataNullDecodeData) {
-  uint8_t buffer[16];
-  DecodeData* ptr = nullptr;
-
-  // Error codes, not segvs, should callers pass us a nullptr pointer.
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), ptr));
-  EXPECT_EQ(kSkipError, opj_skip_from_memory(1, ptr));
-  EXPECT_FALSE(opj_seek_from_memory(1, ptr));
-}
-
-TEST(fxcodec, DecodeDataNullStream) {
-  DecodeData dd(nullptr, 0);
-  uint8_t buffer[16];
-
-  // Reads of size 0 do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
-  EXPECT_EQ(0xbd, buffer[0]);
-
-  // Reads of nonzero size do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
-  EXPECT_EQ(0xbd, buffer[0]);
-
-  // Skips of size 0 always return an error code.
-  EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd));
-
-  // Skips of nonzero size always return an error code.
-  EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd));
-
-  // Seeks to 0 offset return in error.
-  EXPECT_FALSE(opj_seek_from_memory(0, &dd));
-
-  // Seeks to non-zero offsets return in error.
-  EXPECT_FALSE(opj_seek_from_memory(1, &dd));
-}
-
-TEST(fxcodec, DecodeDataZeroSize) {
-  DecodeData dd(stream_data, 0);
-  uint8_t buffer[16];
-
-  // Reads of size 0 do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
-  EXPECT_EQ(0xbd, buffer[0]);
-
-  // Reads of nonzero size do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
-  EXPECT_EQ(0xbd, buffer[0]);
-
-  // Skips of size 0 always return an error code.
-  EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd));
-
-  // Skips of nonzero size always return an error code.
-  EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd));
-
-  // Seeks to 0 offset return in error.
-  EXPECT_FALSE(opj_seek_from_memory(0, &dd));
-
-  // Seeks to non-zero offsets return in error.
-  EXPECT_FALSE(opj_seek_from_memory(1, &dd));
-}
-
-TEST(fxcodec, DecodeDataReadInBounds) {
-  uint8_t buffer[16];
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Exact sized read in a single call.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer), &dd));
-    EXPECT_EQ(0x00, buffer[0]);
-    EXPECT_EQ(0x01, buffer[1]);
-    EXPECT_EQ(0x02, buffer[2]);
-    EXPECT_EQ(0x03, buffer[3]);
-    EXPECT_EQ(0x84, buffer[4]);
-    EXPECT_EQ(0x85, buffer[5]);
-    EXPECT_EQ(0x86, buffer[6]);
-    EXPECT_EQ(0x87, buffer[7]);
-    EXPECT_EQ(0xbd, buffer[8]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Simple read.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(2u, opj_read_from_memory(buffer, 2, &dd));
-    EXPECT_EQ(0x00, buffer[0]);
-    EXPECT_EQ(0x01, buffer[1]);
-    EXPECT_EQ(0xbd, buffer[2]);
-
-    // Read of size 0 doesn't affect things.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(0u, opj_read_from_memory(buffer, 0, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-
-    // Read exactly up to end of data.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
-    EXPECT_EQ(0x02, buffer[0]);
-    EXPECT_EQ(0x03, buffer[1]);
-    EXPECT_EQ(0x84, buffer[2]);
-    EXPECT_EQ(0x85, buffer[3]);
-    EXPECT_EQ(0x86, buffer[4]);
-    EXPECT_EQ(0x87, buffer[5]);
-    EXPECT_EQ(0xbd, buffer[6]);
-
-    // Read of size 0 at EOF is still an error.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-  }
-}
-
-TEST(fxcodec, DecodeDataReadBeyondBounds) {
-  uint8_t buffer[16];
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Read beyond bounds in a single step.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer) + 1, &dd));
-    EXPECT_EQ(0x00, buffer[0]);
-    EXPECT_EQ(0x01, buffer[1]);
-    EXPECT_EQ(0x02, buffer[2]);
-    EXPECT_EQ(0x03, buffer[3]);
-    EXPECT_EQ(0x84, buffer[4]);
-    EXPECT_EQ(0x85, buffer[5]);
-    EXPECT_EQ(0x86, buffer[6]);
-    EXPECT_EQ(0x87, buffer[7]);
-    EXPECT_EQ(0xbd, buffer[8]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Read well beyond bounds in a single step.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(8u, opj_read_from_memory(
-                      buffer, std::numeric_limits<OPJ_SIZE_T>::max(), &dd));
-    EXPECT_EQ(0x00, buffer[0]);
-    EXPECT_EQ(0x01, buffer[1]);
-    EXPECT_EQ(0x02, buffer[2]);
-    EXPECT_EQ(0x03, buffer[3]);
-    EXPECT_EQ(0x84, buffer[4]);
-    EXPECT_EQ(0x85, buffer[5]);
-    EXPECT_EQ(0x86, buffer[6]);
-    EXPECT_EQ(0x87, buffer[7]);
-    EXPECT_EQ(0xbd, buffer[8]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Read of size 6 gets first 6 bytes.
-    // rest of buffer intact.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
-    EXPECT_EQ(0x00, buffer[0]);
-    EXPECT_EQ(0x01, buffer[1]);
-    EXPECT_EQ(0x02, buffer[2]);
-    EXPECT_EQ(0x03, buffer[3]);
-    EXPECT_EQ(0x84, buffer[4]);
-    EXPECT_EQ(0x85, buffer[5]);
-    EXPECT_EQ(0xbd, buffer[6]);
-
-    // Read of size 6 gets remaining two bytes.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(2u, opj_read_from_memory(buffer, 6, &dd));
-    EXPECT_EQ(0x86, buffer[0]);
-    EXPECT_EQ(0x87, buffer[1]);
-    EXPECT_EQ(0xbd, buffer[2]);
-
-    // Read of 6 more gets nothing and leaves rest of buffer intact.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 6, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-  }
-}
-
-// Note: Some care needs to be taken here because the skip/seek functions
-// take OPJ_OFF_T's as arguments, which are typically a signed type.
-TEST(fxcodec, DecodeDataSkip) {
-  uint8_t buffer[16];
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Skiping within buffer is allowed.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(1u, opj_skip_from_memory(1, &dd));
-    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0x01, buffer[0]);
-    EXPECT_EQ(0xbd, buffer[1]);
-
-    // Skiping 0 bytes changes nothing.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(0, opj_skip_from_memory(0, &dd));
-    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0x02, buffer[0]);
-    EXPECT_EQ(0xbd, buffer[1]);
-
-    // Skiping to EOS-1 is possible.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
-    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0x87, buffer[0]);
-    EXPECT_EQ(0xbd, buffer[1]);
-
-    // Next read fails.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Skiping directly to EOS is allowed.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
-
-    // Next read fails.
-    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Skipping beyond end of stream is allowed and returns full distance.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(9u, opj_skip_from_memory(9, &dd));
-
-    // Next read fails.
-    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Skipping way beyond EOS is allowd, doesn't wrap, and returns
-    // full distance.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
-    EXPECT_EQ(std::numeric_limits<OPJ_OFF_T>::max(),
-              opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
-
-    // Next read fails. If it succeeds, it may mean we wrapped.
-    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Negative skip within buffer not is allowed, position unchanged.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
-    EXPECT_EQ(kSkipError, opj_skip_from_memory(-2, &dd));
-
-    // Next read succeeds as if nothing has happenned.
-    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0x84, buffer[0]);
-    EXPECT_EQ(0xbd, buffer[1]);
-
-    // Negative skip before buffer is not allowed, position unchanged.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
-
-    // Next read succeeds as if nothing has happenned.
-    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0x85, buffer[0]);
-    EXPECT_EQ(0xbd, buffer[1]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Negative skip way before buffer is not allowed, doesn't wrap
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
-    EXPECT_EQ(kSkipError,
-              opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
-
-    // Next read succeeds. If it fails, it may mean we wrapped.
-    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0x84, buffer[0]);
-    EXPECT_EQ(0xbd, buffer[1]);
-  }
-  {
-    DecodeData dd(stream_data, sizeof(stream_data));
-
-    // Negative skip after EOS isn't alowed, still EOS.
-    memset(buffer, 0xbd, sizeof(buffer));
-    EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
-    EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
-
-    // Next read fails.
-    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-    EXPECT_EQ(0xbd, buffer[0]);
-  }
-}
-
-TEST(fxcodec, DecodeDataSeek) {
-  uint8_t buffer[16];
-  DecodeData dd(stream_data, sizeof(stream_data));
-
-  // Seeking within buffer is allowed and read succeeds
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_TRUE(opj_seek_from_memory(1, &dd));
-  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0x01, buffer[0]);
-  EXPECT_EQ(0xbd, buffer[1]);
-
-  // Seeking before start returns error leaving position unchanged.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_FALSE(opj_seek_from_memory(-1, &dd));
-  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0x02, buffer[0]);
-  EXPECT_EQ(0xbd, buffer[1]);
-
-  // Seeking way before start returns error leaving position unchanged.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_FALSE(
-      opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
-  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0x03, buffer[0]);
-  EXPECT_EQ(0xbd, buffer[1]);
-
-  // Seeking exactly to EOS is allowed but read fails.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_TRUE(opj_seek_from_memory(8, &dd));
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0xbd, buffer[0]);
-
-  // Seeking back to zero offset is allowed and read succeeds.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_TRUE(opj_seek_from_memory(0, &dd));
-  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0x00, buffer[0]);
-  EXPECT_EQ(0xbd, buffer[1]);
-
-  // Seeking beyond end of stream is allowed but read fails.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_TRUE(opj_seek_from_memory(16, &dd));
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0xbd, buffer[0]);
-
-  // Seeking within buffer after seek past EOF restores good state.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_TRUE(opj_seek_from_memory(4, &dd));
-  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0x84, buffer[0]);
-  EXPECT_EQ(0xbd, buffer[1]);
-
-  // Seeking way beyond EOS is allowed, doesn't wrap, and read fails.
-  memset(buffer, 0xbd, sizeof(buffer));
-  EXPECT_TRUE(opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
-  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
-  EXPECT_EQ(0xbd, buffer[0]);
-}
-
-TEST(fxcodec, YUV420ToRGB) {
-  opj_image_comp_t u;
-  memset(&u, 0, sizeof(u));
-  u.dx = 1;
-  u.dy = 1;
-  u.w = 16;
-  u.h = 16;
-  u.prec = 8;
-  u.bpp = 8;
-  opj_image_comp_t v;
-  memset(&v, 0, sizeof(v));
-  v.dx = 1;
-  v.dy = 1;
-  v.w = 16;
-  v.h = 16;
-  v.prec = 8;
-  v.bpp = 8;
-  opj_image_comp_t y;
-  memset(&y, 0, sizeof(y));
-  y.dx = 1;
-  y.dy = 1;
-  y.prec = 8;
-  y.bpp = 8;
-  opj_image_t img;
-  memset(&img, 0, sizeof(img));
-  img.numcomps = 3;
-  img.color_space = OPJ_CLRSPC_SYCC;
-  img.comps = FX_Alloc(opj_image_comp_t, 3);
-  const struct {
-    OPJ_UINT32 w;
-    bool expected;
-  } cases[] = {{0, false}, {1, false},  {30, false}, {31, true},
-               {32, true}, {33, false}, {34, false}, {UINT_MAX, false}};
-  for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); ++i) {
-    y.w = cases[i].w;
-    y.h = y.w;
-    img.x1 = y.w;
-    img.y1 = y.h;
-    y.data = static_cast<OPJ_INT32*>(
-        opj_image_data_alloc(y.w * y.h * sizeof(OPJ_INT32)));
-    v.data = static_cast<OPJ_INT32*>(
-        opj_image_data_alloc(v.w * v.h * sizeof(OPJ_INT32)));
-    u.data = static_cast<OPJ_INT32*>(
-        opj_image_data_alloc(u.w * u.h * sizeof(OPJ_INT32)));
-    memset(y.data, 1, y.w * y.h * sizeof(OPJ_INT32));
-    memset(u.data, 0, u.w * u.h * sizeof(OPJ_INT32));
-    memset(v.data, 0, v.w * v.h * sizeof(OPJ_INT32));
-    img.comps[0] = y;
-    img.comps[1] = u;
-    img.comps[2] = v;
-    sycc420_to_rgb(&img);
-    if (cases[i].expected) {
-      EXPECT_EQ(img.comps[0].w, img.comps[1].w);
-      EXPECT_EQ(img.comps[0].h, img.comps[1].h);
-      EXPECT_EQ(img.comps[0].w, img.comps[2].w);
-      EXPECT_EQ(img.comps[0].h, img.comps[2].h);
-    } else {
-      EXPECT_NE(img.comps[0].w, img.comps[1].w);
-      EXPECT_NE(img.comps[0].h, img.comps[1].h);
-      EXPECT_NE(img.comps[0].w, img.comps[2].w);
-      EXPECT_NE(img.comps[0].h, img.comps[2].h);
-    }
-    opj_image_data_free(img.comps[0].data);
-    opj_image_data_free(img.comps[1].data);
-    opj_image_data_free(img.comps[2].data);
-  }
-  FX_Free(img.comps);
-}
diff --git a/core/fxcodec/codec/fx_codec_progress.cpp b/core/fxcodec/codec/fx_codec_progress.cpp
deleted file mode 100644
index eb0ecc9..0000000
--- a/core/fxcodec/codec/fx_codec_progress.cpp
+++ /dev/null
@@ -1,2370 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/codec/ccodec_progressivedecoder.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
-
-#define FXCODEC_BLOCK_SIZE 4096
-
-namespace {
-
-#if _FX_OS_ == _FX_OS_MACOSX_
-const double kPngGamma = 1.7;
-#else   // _FX_OS_ == _FX_OS_MACOSX_
-const double kPngGamma = 2.2;
-#endif  // _FX_OS_ == _FX_OS_MACOSX_
-
-void RGB2BGR(uint8_t* buffer, int width = 1) {
-  if (buffer && width > 0) {
-    uint8_t temp;
-    int i = 0;
-    int j = 0;
-    for (; i < width; i++, j += 3) {
-      temp = buffer[j];
-      buffer[j] = buffer[j + 2];
-      buffer[j + 2] = temp;
-    }
-  }
-}
-
-}  // namespace
-
-CCodec_ProgressiveDecoder::CFXCODEC_WeightTable::CFXCODEC_WeightTable() {}
-
-CCodec_ProgressiveDecoder::CFXCODEC_WeightTable::~CFXCODEC_WeightTable() {}
-
-void CCodec_ProgressiveDecoder::CFXCODEC_WeightTable::Calc(int dest_len,
-                                                           int dest_min,
-                                                           int dest_max,
-                                                           int src_len,
-                                                           int src_min,
-                                                           int src_max) {
-  double scale, base;
-  scale = (float)src_len / (float)dest_len;
-  if (dest_len < 0) {
-    base = (float)(src_len);
-  } else {
-    base = 0.0f;
-  }
-  m_ItemSize =
-      (int)(sizeof(int) * 2 + sizeof(int) * (ceil(fabs((float)scale)) + 1));
-  m_DestMin = dest_min;
-  m_pWeightTables.resize((dest_max - dest_min) * m_ItemSize + 4);
-  if (fabs((float)scale) < 1.0f) {
-    for (int dest_pixel = dest_min; dest_pixel < dest_max; dest_pixel++) {
-      PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
-      double src_pos = dest_pixel * scale + scale / 2 + base;
-      pixel_weights.m_SrcStart = (int)floor((float)src_pos - 1.0f / 2);
-      pixel_weights.m_SrcEnd = (int)floor((float)src_pos + 1.0f / 2);
-      if (pixel_weights.m_SrcStart < src_min) {
-        pixel_weights.m_SrcStart = src_min;
-      }
-      if (pixel_weights.m_SrcEnd >= src_max) {
-        pixel_weights.m_SrcEnd = src_max - 1;
-      }
-      if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
-        pixel_weights.m_Weights[0] = 65536;
-      } else {
-        pixel_weights.m_Weights[1] = FXSYS_round(
-            (float)(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * 65536);
-        pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1];
-      }
-    }
-    return;
-  }
-  for (int dest_pixel = dest_min; dest_pixel < dest_max; dest_pixel++) {
-    PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
-    double src_start = dest_pixel * scale + base;
-    double src_end = src_start + scale;
-    int start_i, end_i;
-    if (src_start < src_end) {
-      start_i = (int)floor((float)src_start);
-      end_i = (int)ceil((float)src_end);
-    } else {
-      start_i = (int)floor((float)src_end);
-      end_i = (int)ceil((float)src_start);
-    }
-    if (start_i < src_min) {
-      start_i = src_min;
-    }
-    if (end_i >= src_max) {
-      end_i = src_max - 1;
-    }
-    if (start_i > end_i) {
-      pixel_weights.m_SrcStart = start_i;
-      pixel_weights.m_SrcEnd = start_i;
-      continue;
-    }
-    pixel_weights.m_SrcStart = start_i;
-    pixel_weights.m_SrcEnd = end_i;
-    for (int j = start_i; j <= end_i; j++) {
-      double dest_start = ((float)j - base) / scale;
-      double dest_end = ((float)(j + 1) - base) / scale;
-      if (dest_start > dest_end) {
-        double temp = dest_start;
-        dest_start = dest_end;
-        dest_end = temp;
-      }
-      double area_start =
-          dest_start > (float)(dest_pixel) ? dest_start : (float)(dest_pixel);
-      double area_end = dest_end > (float)(dest_pixel + 1)
-                            ? (float)(dest_pixel + 1)
-                            : dest_end;
-      double weight = area_start >= area_end ? 0.0f : area_end - area_start;
-      if (weight == 0 && j == end_i) {
-        pixel_weights.m_SrcEnd--;
-        break;
-      }
-      pixel_weights.m_Weights[j - start_i] =
-          FXSYS_round((float)(weight * 65536));
-    }
-  }
-}
-
-CCodec_ProgressiveDecoder::CFXCODEC_HorzTable::CFXCODEC_HorzTable() {}
-
-CCodec_ProgressiveDecoder::CFXCODEC_HorzTable::~CFXCODEC_HorzTable() {}
-
-void CCodec_ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len,
-                                                         int src_len) {
-  double scale = (double)dest_len / (double)src_len;
-  m_ItemSize = sizeof(int) * 4;
-  int size = dest_len * m_ItemSize + 4;
-  m_pWeightTables.resize(size, 0);
-  if (scale > 1) {
-    int pre_des_col = 0;
-    for (int src_col = 0; src_col < src_len; src_col++) {
-      double des_col_f = src_col * scale;
-      int des_col = FXSYS_round((float)des_col_f);
-      PixelWeight* pWeight = GetPixelWeight(des_col);
-      pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
-      pWeight->m_Weights[0] = 65536;
-      pWeight->m_Weights[1] = 0;
-      if (src_col == src_len - 1 && des_col < dest_len - 1) {
-        for (int des_col_index = pre_des_col + 1; des_col_index < dest_len;
-             des_col_index++) {
-          pWeight = GetPixelWeight(des_col_index);
-          pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
-          pWeight->m_Weights[0] = 65536;
-          pWeight->m_Weights[1] = 0;
-        }
-        return;
-      }
-      int des_col_len = des_col - pre_des_col;
-      for (int des_col_index = pre_des_col + 1; des_col_index < des_col;
-           des_col_index++) {
-        pWeight = GetPixelWeight(des_col_index);
-        pWeight->m_SrcStart = src_col - 1;
-        pWeight->m_SrcEnd = src_col;
-        pWeight->m_Weights[0] =
-            FXSYS_round((float)(((float)des_col - (float)des_col_index) /
-                                (float)des_col_len * 65536));
-        pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
-      }
-      pre_des_col = des_col;
-    }
-    return;
-  }
-  for (int des_col = 0; des_col < dest_len; des_col++) {
-    double src_col_f = des_col / scale;
-    int src_col = FXSYS_round((float)src_col_f);
-    PixelWeight* pWeight = GetPixelWeight(des_col);
-    pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
-    pWeight->m_Weights[0] = 65536;
-    pWeight->m_Weights[1] = 0;
-  }
-}
-
-CCodec_ProgressiveDecoder::CFXCODEC_VertTable::CFXCODEC_VertTable() {}
-
-CCodec_ProgressiveDecoder::CFXCODEC_VertTable::~CFXCODEC_VertTable() {}
-
-void CCodec_ProgressiveDecoder::CFXCODEC_VertTable::Calc(int dest_len,
-                                                         int src_len) {
-  double scale = (double)dest_len / (double)src_len;
-  m_ItemSize = sizeof(int) * 4;
-  int size = dest_len * m_ItemSize + 4;
-  m_pWeightTables.resize(size, 0);
-  if (scale <= 1) {
-    for (int des_row = 0; des_row < dest_len; des_row++) {
-      PixelWeight* pWeight = GetPixelWeight(des_row);
-      pWeight->m_SrcStart = des_row;
-      pWeight->m_SrcEnd = des_row;
-      pWeight->m_Weights[0] = 65536;
-      pWeight->m_Weights[1] = 0;
-    }
-    return;
-  }
-
-  double step = 0.0;
-  int src_row = 0;
-  while (step < (double)dest_len) {
-    int start_step = (int)step;
-    step = scale * (++src_row);
-    int end_step = (int)step;
-    if (end_step >= dest_len) {
-      end_step = dest_len;
-      for (int des_row = start_step; des_row < end_step; des_row++) {
-        PixelWeight* pWeight = GetPixelWeight(des_row);
-        pWeight->m_SrcStart = start_step;
-        pWeight->m_SrcEnd = start_step;
-        pWeight->m_Weights[0] = 65536;
-        pWeight->m_Weights[1] = 0;
-      }
-      return;
-    }
-    int length = end_step - start_step;
-    {
-      PixelWeight* pWeight = GetPixelWeight(start_step);
-      pWeight->m_SrcStart = start_step;
-      pWeight->m_SrcEnd = start_step;
-      pWeight->m_Weights[0] = 65536;
-      pWeight->m_Weights[1] = 0;
-    }
-    for (int des_row = start_step + 1; des_row < end_step; des_row++) {
-      PixelWeight* pWeight = GetPixelWeight(des_row);
-      pWeight->m_SrcStart = start_step;
-      pWeight->m_SrcEnd = end_step;
-      pWeight->m_Weights[0] =
-          FXSYS_round((float)(end_step - des_row) / (float)length * 65536);
-      pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
-    }
-  }
-}
-
-CCodec_ProgressiveDecoder::CCodec_ProgressiveDecoder(
-    CCodec_ModuleMgr* pCodecMgr) {
-  m_pFile = nullptr;
-  m_pJpegContext = nullptr;
-  m_pPngContext = nullptr;
-  m_pBmpContext = nullptr;
-  m_pTiffContext = nullptr;
-  m_pCodecMgr = nullptr;
-  m_pSrcBuf = nullptr;
-  m_pDecodeBuf = nullptr;
-  m_pDeviceBitmap = nullptr;
-  m_pSrcPalette = nullptr;
-  m_pCodecMgr = pCodecMgr;
-  m_offSet = 0;
-  m_SrcSize = 0;
-  m_ScanlineSize = 0;
-  m_SrcWidth = 0;
-  m_SrcHeight = 0;
-  m_SrcComponents = 0;
-  m_SrcBPC = 0;
-  m_SrcPassNumber = 0;
-  m_clipBox = FX_RECT(0, 0, 0, 0);
-  m_imagType = FXCODEC_IMAGE_UNKNOWN;
-  m_status = FXCODEC_STATUS_DECODE_FINISH;
-  m_TransMethod = -1;
-  m_SrcRow = 0;
-  m_SrcFormat = FXCodec_Invalid;
-  m_FrameNumber = 0;
-  m_FrameCur = 0;
-  m_SrcPaletteNumber = 0;
-  m_GifPltNumber = 0;
-  m_GifBgIndex = 0;
-  m_pGifPalette = nullptr;
-  m_GifTransIndex = -1;
-  m_GifFrameRect = FX_RECT(0, 0, 0, 0);
-  m_BmpIsTopBottom = false;
-}
-
-CCodec_ProgressiveDecoder::~CCodec_ProgressiveDecoder() {
-  FX_Free(m_pSrcBuf);
-  FX_Free(m_pDecodeBuf);
-  FX_Free(m_pSrcPalette);
-}
-
-bool CCodec_ProgressiveDecoder::JpegReadMoreData(CCodec_JpegModule* pJpegModule,
-                                                 FXCODEC_STATUS& err_status) {
-  uint32_t dwSize = (uint32_t)m_pFile->GetSize();
-  if (dwSize <= m_offSet) {
-    return false;
-  }
-  dwSize = dwSize - m_offSet;
-  uint32_t dwAvail = pJpegModule->GetAvailInput(m_pJpegContext.get(), nullptr);
-  if (dwAvail == m_SrcSize) {
-    if (dwSize > FXCODEC_BLOCK_SIZE) {
-      dwSize = FXCODEC_BLOCK_SIZE;
-    }
-    m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) /
-                FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE;
-    m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize);
-    if (!m_pSrcBuf) {
-      err_status = FXCODEC_STATUS_ERR_MEMORY;
-      return false;
-    }
-  } else {
-    uint32_t dwConsume = m_SrcSize - dwAvail;
-    if (dwAvail) {
-      memmove(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail);
-    }
-    if (dwSize > dwConsume) {
-      dwSize = dwConsume;
-    }
-  }
-  if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) {
-    err_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += dwSize;
-  pJpegModule->Input(m_pJpegContext.get(), m_pSrcBuf, dwSize + dwAvail);
-  return true;
-}
-
-bool CCodec_ProgressiveDecoder::PngReadHeader(int width,
-                                              int height,
-                                              int bpc,
-                                              int pass,
-                                              int* color_type,
-                                              double* gamma) {
-  if (!m_pDeviceBitmap) {
-    m_SrcWidth = width;
-    m_SrcHeight = height;
-    m_SrcBPC = bpc;
-    m_SrcPassNumber = pass;
-    switch (*color_type) {
-      case 0:
-        m_SrcComponents = 1;
-        break;
-      case 4:
-        m_SrcComponents = 2;
-        break;
-      case 2:
-        m_SrcComponents = 3;
-        break;
-      case 3:
-      case 6:
-        m_SrcComponents = 4;
-        break;
-      default:
-        m_SrcComponents = 0;
-        break;
-    }
-    m_clipBox = FX_RECT(0, 0, width, height);
-    return false;
-  }
-  FXDIB_Format format = m_pDeviceBitmap->GetFormat();
-  switch (format) {
-    case FXDIB_1bppMask:
-    case FXDIB_1bppRgb:
-      NOTREACHED();
-      return false;
-    case FXDIB_8bppMask:
-    case FXDIB_8bppRgb:
-      *color_type = 0;
-      break;
-    case FXDIB_Rgb:
-      *color_type = 2;
-      break;
-    case FXDIB_Rgb32:
-    case FXDIB_Argb:
-      *color_type = 6;
-      break;
-    default:
-      NOTREACHED();
-      return false;
-  }
-  *gamma = kPngGamma;
-  return true;
-}
-
-bool CCodec_ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t** pSrcBuf) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  if (!pDIBitmap) {
-    NOTREACHED();
-    return false;
-  }
-  if (line >= m_clipBox.top && line < m_clipBox.bottom) {
-    double scale_y = (double)m_sizeY / (double)m_clipBox.Height();
-    int32_t row = (int32_t)((line - m_clipBox.top) * scale_y) + m_startY;
-    uint8_t* src_scan = (uint8_t*)pDIBitmap->GetScanline(row);
-    uint8_t* des_scan = m_pDecodeBuf;
-    *pSrcBuf = m_pDecodeBuf;
-    int32_t src_Bpp = pDIBitmap->GetBPP() >> 3;
-    int32_t des_Bpp = (m_SrcFormat & 0xff) >> 3;
-    int32_t src_left = m_startX;
-    int32_t des_left = m_clipBox.left;
-    src_scan += src_left * src_Bpp;
-    des_scan += des_left * des_Bpp;
-    for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
-      PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
-      if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) {
-        continue;
-      }
-      switch (pDIBitmap->GetFormat()) {
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          NOTREACHED();
-          return false;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDIBitmap->GetPalette()) {
-            return false;
-          }
-          uint32_t des_g = 0;
-          des_g += pPixelWeights->m_Weights[0] * src_scan[src_col];
-          des_scan[pPixelWeights->m_SrcStart] = (uint8_t)(des_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t des_b = 0, des_g = 0, des_r = 0;
-          const uint8_t* p = src_scan + src_col * src_Bpp;
-          des_b += pPixelWeights->m_Weights[0] * (*p++);
-          des_g += pPixelWeights->m_Weights[0] * (*p++);
-          des_r += pPixelWeights->m_Weights[0] * (*p);
-          uint8_t* pDes = &des_scan[pPixelWeights->m_SrcStart * des_Bpp];
-          *pDes++ = (uint8_t)((des_b) >> 16);
-          *pDes++ = (uint8_t)((des_g) >> 16);
-          *pDes = (uint8_t)((des_r) >> 16);
-        } break;
-        case FXDIB_Argb: {
-          uint32_t des_r = 0, des_g = 0, des_b = 0;
-          const uint8_t* p = src_scan + src_col * src_Bpp;
-          des_b += pPixelWeights->m_Weights[0] * (*p++);
-          des_g += pPixelWeights->m_Weights[0] * (*p++);
-          des_r += pPixelWeights->m_Weights[0] * (*p++);
-          uint8_t* pDes = &des_scan[pPixelWeights->m_SrcStart * des_Bpp];
-          *pDes++ = (uint8_t)((des_b) >> 16);
-          *pDes++ = (uint8_t)((des_g) >> 16);
-          *pDes++ = (uint8_t)((des_r) >> 16);
-          *pDes = *p;
-        } break;
-        default:
-          return false;
-      }
-    }
-  }
-  return true;
-}
-
-void CCodec_ProgressiveDecoder::PngOneOneMapResampleHorz(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    int32_t des_line,
-    uint8_t* src_scan,
-    FXCodec_Format src_format) {
-  uint8_t* des_scan = (uint8_t*)pDeviceBitmap->GetScanline(des_line);
-  int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3;
-  int32_t des_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  int32_t src_left = m_clipBox.left;
-  int32_t des_left = m_startX;
-  src_scan += src_left * src_Bpp;
-  des_scan += des_left * des_Bpp;
-  for (int32_t des_col = 0; des_col < m_sizeX; des_col++) {
-    PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(des_col);
-    switch (pDeviceBitmap->GetFormat()) {
-      case FXDIB_1bppMask:
-      case FXDIB_1bppRgb:
-        NOTREACHED();
-        return;
-      case FXDIB_8bppMask:
-      case FXDIB_8bppRgb: {
-        if (pDeviceBitmap->GetPalette()) {
-          return;
-        }
-        uint32_t des_g = 0;
-        des_g +=
-            pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart];
-        des_g +=
-            pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd];
-        *des_scan++ = (uint8_t)(des_g >> 16);
-      } break;
-      case FXDIB_Rgb:
-      case FXDIB_Rgb32: {
-        uint32_t des_b = 0, des_g = 0, des_r = 0;
-        const uint8_t* p = src_scan;
-        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
-        des_b += pPixelWeights->m_Weights[0] * (*p++);
-        des_g += pPixelWeights->m_Weights[0] * (*p++);
-        des_r += pPixelWeights->m_Weights[0] * (*p);
-        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
-        des_b += pPixelWeights->m_Weights[1] * (*p++);
-        des_g += pPixelWeights->m_Weights[1] * (*p++);
-        des_r += pPixelWeights->m_Weights[1] * (*p);
-        *des_scan++ = (uint8_t)((des_b) >> 16);
-        *des_scan++ = (uint8_t)((des_g) >> 16);
-        *des_scan++ = (uint8_t)((des_r) >> 16);
-        des_scan += des_Bpp - 3;
-      } break;
-      case FXDIB_Argb: {
-        uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
-        const uint8_t* p = src_scan;
-        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
-        des_b += pPixelWeights->m_Weights[0] * (*p++);
-        des_g += pPixelWeights->m_Weights[0] * (*p++);
-        des_r += pPixelWeights->m_Weights[0] * (*p++);
-        des_a += pPixelWeights->m_Weights[0] * (*p);
-        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
-        des_b += pPixelWeights->m_Weights[1] * (*p++);
-        des_g += pPixelWeights->m_Weights[1] * (*p++);
-        des_r += pPixelWeights->m_Weights[1] * (*p++);
-        des_a += pPixelWeights->m_Weights[1] * (*p);
-        *des_scan++ = (uint8_t)((des_b) >> 16);
-        *des_scan++ = (uint8_t)((des_g) >> 16);
-        *des_scan++ = (uint8_t)((des_r) >> 16);
-        *des_scan++ = (uint8_t)((des_a) >> 16);
-      } break;
-      default:
-        return;
-    }
-  }
-}
-
-void CCodec_ProgressiveDecoder::PngFillScanlineBufCompleted(int pass,
-                                                            int line) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  ASSERT(pDIBitmap);
-  int src_top = m_clipBox.top;
-  int src_bottom = m_clipBox.bottom;
-  int des_top = m_startY;
-  int src_hei = m_clipBox.Height();
-  int des_hei = m_sizeY;
-  if (line >= src_top && line < src_bottom) {
-    double scale_y = (double)des_hei / (double)src_hei;
-    int src_row = line - src_top;
-    int des_row = (int)(src_row * scale_y) + des_top;
-    if (des_row >= des_top + des_hei) {
-      return;
-    }
-    PngOneOneMapResampleHorz(pDIBitmap, des_row, m_pDecodeBuf, m_SrcFormat);
-    if (m_SrcPassNumber == 1 && scale_y > 1.0) {
-      ResampleVert(pDIBitmap, scale_y, des_row);
-      return;
-    }
-    if (pass == 6 && scale_y > 1.0) {
-      ResampleVert(pDIBitmap, scale_y, des_row);
-    }
-  }
-}
-
-bool CCodec_ProgressiveDecoder::GifReadMoreData(CCodec_GifModule* pGifModule,
-                                                FXCODEC_STATUS& err_status) {
-  uint32_t dwSize = (uint32_t)m_pFile->GetSize();
-  if (dwSize <= m_offSet) {
-    return false;
-  }
-  dwSize = dwSize - m_offSet;
-  uint32_t dwAvail = pGifModule->GetAvailInput(m_pGifContext.get(), nullptr);
-  if (dwAvail == m_SrcSize) {
-    if (dwSize > FXCODEC_BLOCK_SIZE) {
-      dwSize = FXCODEC_BLOCK_SIZE;
-    }
-    m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) /
-                FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE;
-    m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize);
-    if (!m_pSrcBuf) {
-      err_status = FXCODEC_STATUS_ERR_MEMORY;
-      return false;
-    }
-  } else {
-    uint32_t dwConsume = m_SrcSize - dwAvail;
-    if (dwAvail) {
-      memmove(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail);
-    }
-    if (dwSize > dwConsume) {
-      dwSize = dwConsume;
-    }
-  }
-  if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) {
-    err_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += dwSize;
-  pGifModule->Input(m_pGifContext.get(), m_pSrcBuf, dwSize + dwAvail);
-  return true;
-}
-
-void CCodec_ProgressiveDecoder::GifRecordCurrentPosition(uint32_t& cur_pos) {
-  uint32_t remain_size =
-      m_pCodecMgr->GetGifModule()->GetAvailInput(m_pGifContext.get());
-  cur_pos = m_offSet - remain_size;
-}
-
-bool CCodec_ProgressiveDecoder::GifInputRecordPositionBuf(
-    uint32_t rcd_pos,
-    const FX_RECT& img_rc,
-    int32_t pal_num,
-    void* pal_ptr,
-    int32_t delay_time,
-    bool user_input,
-    int32_t trans_index,
-    int32_t disposal_method,
-    bool interlace) {
-  m_offSet = rcd_pos;
-  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
-  if (!GifReadMoreData(m_pCodecMgr->GetGifModule(), error_status)) {
-    return false;
-  }
-  uint8_t* pPalette = nullptr;
-  if (pal_num != 0 && pal_ptr) {
-    pPalette = (uint8_t*)pal_ptr;
-  } else {
-    if (!m_pGifPalette)
-      return false;
-    pal_num = m_GifPltNumber;
-    pPalette = m_pGifPalette;
-  }
-  if (!m_pSrcPalette)
-    m_pSrcPalette = FX_Alloc(FX_ARGB, pal_num);
-  else if (pal_num > m_SrcPaletteNumber)
-    m_pSrcPalette = FX_Realloc(FX_ARGB, m_pSrcPalette, pal_num);
-  if (!m_pSrcPalette)
-    return false;
-
-  m_SrcPaletteNumber = pal_num;
-  for (int i = 0; i < pal_num; i++) {
-    uint32_t j = i * 3;
-    m_pSrcPalette[i] =
-        ArgbEncode(0xff, pPalette[j], pPalette[j + 1], pPalette[j + 2]);
-  }
-  m_GifTransIndex = trans_index;
-  m_GifFrameRect = img_rc;
-  m_SrcPassNumber = interlace ? 4 : 1;
-  int32_t pal_index = m_GifBgIndex;
-  RetainPtr<CFX_DIBitmap> pDevice = m_pDeviceBitmap;
-  if (trans_index >= pal_num)
-    trans_index = -1;
-  if (trans_index != -1) {
-    m_pSrcPalette[trans_index] &= 0x00ffffff;
-    if (pDevice->HasAlpha())
-      pal_index = trans_index;
-  }
-  if (pal_index >= pal_num)
-    return false;
-
-  int startX = m_startX;
-  int startY = m_startY;
-  int sizeX = m_sizeX;
-  int sizeY = m_sizeY;
-  int Bpp = pDevice->GetBPP() / 8;
-  FX_ARGB argb = m_pSrcPalette[pal_index];
-  for (int row = 0; row < sizeY; row++) {
-    uint8_t* pScanline =
-        (uint8_t*)pDevice->GetScanline(row + startY) + startX * Bpp;
-    switch (m_TransMethod) {
-      case 3: {
-        uint8_t gray =
-            FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb));
-        memset(pScanline, gray, sizeX);
-        break;
-      }
-      case 8: {
-        for (int col = 0; col < sizeX; col++) {
-          *pScanline++ = FXARGB_B(argb);
-          *pScanline++ = FXARGB_G(argb);
-          *pScanline++ = FXARGB_R(argb);
-          pScanline += Bpp - 3;
-        }
-        break;
-      }
-      case 12: {
-        for (int col = 0; col < sizeX; col++) {
-          FXARGB_SETDIB(pScanline, argb);
-          pScanline += 4;
-        }
-        break;
-      }
-    }
-  }
-  return true;
-}
-
-void CCodec_ProgressiveDecoder::GifReadScanline(int32_t row_num,
-                                                uint8_t* row_buf) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  ASSERT(pDIBitmap);
-  int32_t img_width = m_GifFrameRect.Width();
-  if (!pDIBitmap->HasAlpha()) {
-    uint8_t* byte_ptr = row_buf;
-    for (int i = 0; i < img_width; i++) {
-      if (*byte_ptr == m_GifTransIndex) {
-        *byte_ptr = m_GifBgIndex;
-      }
-      byte_ptr++;
-    }
-  }
-  int32_t pal_index = m_GifBgIndex;
-  if (m_GifTransIndex != -1 && m_pDeviceBitmap->HasAlpha()) {
-    pal_index = m_GifTransIndex;
-  }
-  memset(m_pDecodeBuf, pal_index, m_SrcWidth);
-  bool bLastPass = (row_num % 2) == 1;
-  int32_t line = row_num + m_GifFrameRect.top;
-  int32_t left = m_GifFrameRect.left;
-  memcpy(m_pDecodeBuf + left, row_buf, img_width);
-  int src_top = m_clipBox.top;
-  int src_bottom = m_clipBox.bottom;
-  int des_top = m_startY;
-  int src_hei = m_clipBox.Height();
-  int des_hei = m_sizeY;
-  if (line < src_top || line >= src_bottom)
-    return;
-
-  double scale_y = (double)des_hei / (double)src_hei;
-  int src_row = line - src_top;
-  int des_row = (int)(src_row * scale_y) + des_top;
-  if (des_row >= des_top + des_hei)
-    return;
-
-  ReSampleScanline(pDIBitmap, des_row, m_pDecodeBuf, m_SrcFormat);
-  if (scale_y > 1.0 && m_SrcPassNumber == 1) {
-    ResampleVert(pDIBitmap, scale_y, des_row);
-    return;
-  }
-  if (scale_y <= 1.0)
-    return;
-
-  int des_bottom = des_top + m_sizeY;
-  int des_Bpp = pDIBitmap->GetBPP() >> 3;
-  uint32_t des_ScanOffet = m_startX * des_Bpp;
-  if (des_row + (int)scale_y >= des_bottom - 1) {
-    uint8_t* scan_src =
-        (uint8_t*)pDIBitmap->GetScanline(des_row) + des_ScanOffet;
-    int cur_row = des_row;
-    while (++cur_row < des_bottom) {
-      uint8_t* scan_des =
-          (uint8_t*)pDIBitmap->GetScanline(cur_row) + des_ScanOffet;
-      uint32_t size = m_sizeX * des_Bpp;
-      memmove(scan_des, scan_src, size);
-    }
-  }
-  if (bLastPass)
-    GifDoubleLineResampleVert(pDIBitmap, scale_y, des_row);
-}
-
-void CCodec_ProgressiveDecoder::GifDoubleLineResampleVert(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    double scale_y,
-    int des_row) {
-  int des_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  uint32_t des_ScanOffet = m_startX * des_Bpp;
-  int des_top = m_startY;
-  pdfium::base::CheckedNumeric<double> scale_y2 = scale_y;
-  scale_y2 *= 2;
-  pdfium::base::CheckedNumeric<int> check_des_row_1 = des_row;
-  check_des_row_1 -= scale_y2.ValueOrDie();
-  int des_row_1 = check_des_row_1.ValueOrDie();
-  des_row_1 = std::max(des_row_1, des_top);
-  for (; des_row_1 < des_row; des_row_1++) {
-    uint8_t* scan_des =
-        (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet;
-    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top);
-    const uint8_t* scan_src1 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) +
-        des_ScanOffet;
-    const uint8_t* scan_src2 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + des_ScanOffet;
-    for (int des_col = 0; des_col < m_sizeX; des_col++) {
-      switch (pDeviceBitmap->GetFormat()) {
-        case FXDIB_Invalid:
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          return;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDeviceBitmap->GetPalette()) {
-            return;
-          }
-          int des_g = 0;
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)(des_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t des_b = 0, des_g = 0, des_r = 0;
-          des_b += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_r += pWeight->m_Weights[0] * (*scan_src1++);
-          scan_src1 += des_Bpp - 3;
-          des_b += pWeight->m_Weights[1] * (*scan_src2++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          des_r += pWeight->m_Weights[1] * (*scan_src2++);
-          scan_src2 += des_Bpp - 3;
-          *scan_des++ = (uint8_t)((des_b) >> 16);
-          *scan_des++ = (uint8_t)((des_g) >> 16);
-          *scan_des++ = (uint8_t)((des_r) >> 16);
-          scan_des += des_Bpp - 3;
-        } break;
-        case FXDIB_Argb: {
-          uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
-          des_b += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_r += pWeight->m_Weights[0] * (*scan_src1++);
-          des_a += pWeight->m_Weights[0] * (*scan_src1++);
-          des_b += pWeight->m_Weights[1] * (*scan_src2++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          des_r += pWeight->m_Weights[1] * (*scan_src2++);
-          des_a += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)((des_b) >> 16);
-          *scan_des++ = (uint8_t)((des_g) >> 16);
-          *scan_des++ = (uint8_t)((des_r) >> 16);
-          *scan_des++ = (uint8_t)((des_a) >> 16);
-        } break;
-        default:
-          return;
-      }
-    }
-  }
-  int des_bottom = des_top + m_sizeY - 1;
-  if (des_row + (int)(2 * scale_y) >= des_bottom &&
-      des_row + (int)scale_y < des_bottom) {
-    GifDoubleLineResampleVert(pDeviceBitmap, scale_y, des_row + (int)scale_y);
-  }
-}
-
-bool CCodec_ProgressiveDecoder::BmpReadMoreData(CCodec_BmpModule* pBmpModule,
-                                                FXCODEC_STATUS& err_status) {
-  uint32_t dwSize = (uint32_t)m_pFile->GetSize();
-  if (dwSize <= m_offSet)
-    return false;
-
-  dwSize = dwSize - m_offSet;
-  uint32_t dwAvail = pBmpModule->GetAvailInput(m_pBmpContext.get(), nullptr);
-  if (dwAvail == m_SrcSize) {
-    if (dwSize > FXCODEC_BLOCK_SIZE) {
-      dwSize = FXCODEC_BLOCK_SIZE;
-    }
-    m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) /
-                FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE;
-    m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize);
-    if (!m_pSrcBuf) {
-      err_status = FXCODEC_STATUS_ERR_MEMORY;
-      return false;
-    }
-  } else {
-    uint32_t dwConsume = m_SrcSize - dwAvail;
-    if (dwAvail) {
-      memmove(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail);
-    }
-    if (dwSize > dwConsume) {
-      dwSize = dwConsume;
-    }
-  }
-  if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) {
-    err_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += dwSize;
-  pBmpModule->Input(m_pBmpContext.get(), m_pSrcBuf, dwSize + dwAvail);
-  return true;
-}
-
-bool CCodec_ProgressiveDecoder::BmpInputImagePositionBuf(uint32_t rcd_pos) {
-  m_offSet = rcd_pos;
-  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
-  return BmpReadMoreData(m_pCodecMgr->GetBmpModule(), error_status);
-}
-
-void CCodec_ProgressiveDecoder::BmpReadScanline(
-    uint32_t row_num,
-    const std::vector<uint8_t>& row_buf) {
-  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
-  ASSERT(pDIBitmap);
-  std::copy(row_buf.begin(), row_buf.begin() + m_ScanlineSize, m_pDecodeBuf);
-  int src_top = m_clipBox.top;
-  int src_bottom = m_clipBox.bottom;
-  int des_top = m_startY;
-  int src_hei = m_clipBox.Height();
-  int des_hei = m_sizeY;
-  if ((src_top >= 0 && row_num < static_cast<uint32_t>(src_top)) ||
-      src_bottom < 0 || row_num >= static_cast<uint32_t>(src_bottom)) {
-    return;
-  }
-
-  double scale_y = (double)des_hei / (double)src_hei;
-  int src_row = row_num - src_top;
-  int des_row = (int)(src_row * scale_y) + des_top;
-  if (des_row >= des_top + des_hei)
-    return;
-
-  ReSampleScanline(pDIBitmap, des_row, m_pDecodeBuf, m_SrcFormat);
-  if (scale_y <= 1.0)
-    return;
-
-  if (m_BmpIsTopBottom) {
-    ResampleVert(pDIBitmap, scale_y, des_row);
-    return;
-  }
-  ResampleVertBT(pDIBitmap, scale_y, des_row);
-}
-
-void CCodec_ProgressiveDecoder::ResampleVertBT(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    double scale_y,
-    int des_row) {
-  int des_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  uint32_t des_ScanOffet = m_startX * des_Bpp;
-  int des_top = m_startY;
-  int des_bottom = m_startY + m_sizeY;
-  pdfium::base::CheckedNumeric<int> check_des_row_1 = des_row;
-  check_des_row_1 += pdfium::base::checked_cast<int>(scale_y);
-  int des_row_1 = check_des_row_1.ValueOrDie();
-  if (des_row_1 >= des_bottom - 1) {
-    uint8_t* scan_src =
-        (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
-    while (++des_row < des_bottom) {
-      uint8_t* scan_des =
-          (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
-      uint32_t size = m_sizeX * des_Bpp;
-      memmove(scan_des, scan_src, size);
-    }
-    return;
-  }
-  for (; des_row_1 > des_row; des_row_1--) {
-    uint8_t* scan_des =
-        (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet;
-    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top);
-    const uint8_t* scan_src1 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) +
-        des_ScanOffet;
-    const uint8_t* scan_src2 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + des_ScanOffet;
-    for (int des_col = 0; des_col < m_sizeX; des_col++) {
-      switch (pDeviceBitmap->GetFormat()) {
-        case FXDIB_Invalid:
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          return;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDeviceBitmap->GetPalette()) {
-            return;
-          }
-          int des_g = 0;
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)(des_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t des_b = 0, des_g = 0, des_r = 0;
-          des_b += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_r += pWeight->m_Weights[0] * (*scan_src1++);
-          scan_src1 += des_Bpp - 3;
-          des_b += pWeight->m_Weights[1] * (*scan_src2++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          des_r += pWeight->m_Weights[1] * (*scan_src2++);
-          scan_src2 += des_Bpp - 3;
-          *scan_des++ = (uint8_t)((des_b) >> 16);
-          *scan_des++ = (uint8_t)((des_g) >> 16);
-          *scan_des++ = (uint8_t)((des_r) >> 16);
-          scan_des += des_Bpp - 3;
-        } break;
-        case FXDIB_Argb: {
-          uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
-          des_b += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_r += pWeight->m_Weights[0] * (*scan_src1++);
-          des_a += pWeight->m_Weights[0] * (*scan_src1++);
-          des_b += pWeight->m_Weights[1] * (*scan_src2++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          des_r += pWeight->m_Weights[1] * (*scan_src2++);
-          des_a += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)((des_b) >> 16);
-          *scan_des++ = (uint8_t)((des_g) >> 16);
-          *scan_des++ = (uint8_t)((des_r) >> 16);
-          *scan_des++ = (uint8_t)((des_a) >> 16);
-        } break;
-        default:
-          return;
-      }
-    }
-  }
-}
-
-bool CCodec_ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType,
-                                                CFX_DIBAttribute* pAttribute) {
-  m_offSet = 0;
-  uint32_t size = (uint32_t)m_pFile->GetSize();
-  if (size > FXCODEC_BLOCK_SIZE) {
-    size = FXCODEC_BLOCK_SIZE;
-  }
-  FX_Free(m_pSrcBuf);
-  m_pSrcBuf = FX_Alloc(uint8_t, size);
-  memset(m_pSrcBuf, 0, size);
-  m_SrcSize = size;
-  switch (imageType) {
-    case FXCODEC_IMAGE_BMP:
-      return BmpDetectImageType(pAttribute, size);
-    case FXCODEC_IMAGE_JPG:
-      return JpegDetectImageType(pAttribute, size);
-    case FXCODEC_IMAGE_PNG:
-      return PngDetectImageType(pAttribute, size);
-    case FXCODEC_IMAGE_GIF:
-      return GifDetectImageType(pAttribute, size);
-    case FXCODEC_IMAGE_TIF:
-      return TifDetectImageType(pAttribute, size);
-    default:
-      m_status = FXCODEC_STATUS_ERR_FORMAT;
-      return false;
-  }
-}
-
-bool CCodec_ProgressiveDecoder::BmpDetectImageType(CFX_DIBAttribute* pAttribute,
-                                                   uint32_t size) {
-  CCodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
-  if (!pBmpModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-
-  m_pBmpContext = pBmpModule->Start(this);
-  if (!m_pBmpContext) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-
-  if (!m_pFile->ReadBlock(m_pSrcBuf, 0, size)) {
-    m_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-
-  m_offSet += size;
-  pBmpModule->Input(m_pBmpContext.get(), m_pSrcBuf, size);
-  std::vector<uint32_t> palette;
-  int32_t readResult = pBmpModule->ReadHeader(
-      m_pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
-      &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
-  while (readResult == 2) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
-    if (!BmpReadMoreData(pBmpModule, error_status)) {
-      m_status = error_status;
-      return false;
-    }
-    readResult = pBmpModule->ReadHeader(
-        m_pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
-        &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
-  }
-
-  if (readResult != 1) {
-    m_pBmpContext.reset();
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  FXDIB_Format format = FXDIB_Invalid;
-  switch (m_SrcComponents) {
-    case 1:
-      m_SrcFormat = FXCodec_8bppRgb;
-      format = FXDIB_8bppRgb;
-      break;
-    case 3:
-      m_SrcFormat = FXCodec_Rgb;
-      format = FXDIB_Rgb;
-      break;
-    case 4:
-      m_SrcFormat = FXCodec_Rgb32;
-      format = FXDIB_Rgb32;
-      break;
-    default:
-      m_pBmpContext.reset();
-      m_status = FXCODEC_STATUS_ERR_FORMAT;
-      return false;
-  }
-
-  uint32_t pitch = 0;
-  uint32_t neededData = 0;
-  if (!CFX_DIBitmap::CalculatePitchAndSize(m_SrcWidth, m_SrcHeight, format,
-                                           &pitch, &neededData)) {
-    m_pBmpContext.reset();
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  uint32_t availableData = m_SrcSize > m_offSet ? m_SrcSize - m_offSet : 0;
-  if (neededData > availableData) {
-    m_pBmpContext.reset();
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  m_SrcBPC = 8;
-  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-  FX_Free(m_pSrcPalette);
-  if (m_SrcPaletteNumber) {
-    m_pSrcPalette = FX_Alloc(FX_ARGB, m_SrcPaletteNumber);
-    memcpy(m_pSrcPalette, palette.data(), m_SrcPaletteNumber * sizeof(FX_ARGB));
-  } else {
-    m_pSrcPalette = nullptr;
-  }
-  return true;
-}
-
-bool CCodec_ProgressiveDecoder::JpegDetectImageType(
-    CFX_DIBAttribute* pAttribute,
-    uint32_t size) {
-  CCodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
-  // Setting jump marker before calling Start or ReadHeader, since a longjmp
-  // to the marker indicates a fatal error in these functions.
-  if (setjmp(*m_pJpegContext->GetJumpMark()) == -1) {
-    m_status = FXCODEC_STATUS_ERROR;
-    return false;
-  }
-
-  m_pJpegContext = pJpegModule->Start();
-  if (!m_pJpegContext) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  if (!m_pFile->ReadBlock(m_pSrcBuf, 0, size)) {
-    m_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += size;
-  pJpegModule->Input(m_pJpegContext.get(), m_pSrcBuf, size);
-  // Setting jump marker before calling ReadHeader, since a longjmp to
-  // the marker indicates a fatal error.
-  if (setjmp(*m_pJpegContext->GetJumpMark()) == -1) {
-    m_pJpegContext.reset();
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-
-  int32_t readResult =
-      pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight,
-                              &m_SrcComponents, pAttribute);
-  while (readResult == 2) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
-    if (!JpegReadMoreData(pJpegModule, error_status)) {
-      m_status = error_status;
-      return false;
-    }
-    readResult =
-        pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight,
-                                &m_SrcComponents, pAttribute);
-  }
-  if (!readResult) {
-    m_SrcBPC = 8;
-    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-    return true;
-  }
-  m_pJpegContext.reset();
-  m_status = FXCODEC_STATUS_ERR_FORMAT;
-  return false;
-}
-
-bool CCodec_ProgressiveDecoder::PngDetectImageType(CFX_DIBAttribute* pAttribute,
-                                                   uint32_t size) {
-  CCodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule();
-  if (!pPngModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  m_pPngContext = pPngModule->Start(this);
-  if (!m_pPngContext) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  bool bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size);
-  if (!bResult) {
-    m_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += size;
-  bResult = pPngModule->Input(m_pPngContext.get(), m_pSrcBuf, size, pAttribute);
-  while (bResult) {
-    uint32_t remain_size = static_cast<uint32_t>(m_pFile->GetSize()) - m_offSet;
-    uint32_t input_size =
-        remain_size > FXCODEC_BLOCK_SIZE ? FXCODEC_BLOCK_SIZE : remain_size;
-    if (input_size == 0) {
-      m_pPngContext.reset();
-      m_status = FXCODEC_STATUS_ERR_FORMAT;
-      return false;
-    }
-    if (m_pSrcBuf && input_size > m_SrcSize) {
-      FX_Free(m_pSrcBuf);
-      m_pSrcBuf = FX_Alloc(uint8_t, input_size);
-      memset(m_pSrcBuf, 0, input_size);
-      m_SrcSize = input_size;
-    }
-    bResult = m_pFile->ReadBlock(m_pSrcBuf, m_offSet, input_size);
-    if (!bResult) {
-      m_status = FXCODEC_STATUS_ERR_READ;
-      return false;
-    }
-    m_offSet += input_size;
-    bResult = pPngModule->Input(m_pPngContext.get(), m_pSrcBuf, input_size,
-                                pAttribute);
-  }
-  ASSERT(!bResult);
-  m_pPngContext.reset();
-  if (m_SrcPassNumber == 0) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  return true;
-}
-
-bool CCodec_ProgressiveDecoder::GifDetectImageType(CFX_DIBAttribute* pAttribute,
-                                                   uint32_t size) {
-  CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-  if (!pGifModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return false;
-  }
-  m_pGifContext = pGifModule->Start(this);
-  bool bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size);
-  if (!bResult) {
-    m_status = FXCODEC_STATUS_ERR_READ;
-    return false;
-  }
-  m_offSet += size;
-  pGifModule->Input(m_pGifContext.get(), m_pSrcBuf, size);
-  m_SrcComponents = 1;
-  CFX_GifDecodeStatus readResult = pGifModule->ReadHeader(
-      m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, &m_GifPltNumber,
-      (void**)&m_pGifPalette, &m_GifBgIndex, nullptr);
-  while (readResult == CFX_GifDecodeStatus::Unfinished) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
-    if (!GifReadMoreData(pGifModule, error_status)) {
-      m_pGifContext = nullptr;
-      m_status = error_status;
-      return false;
-    }
-    readResult = pGifModule->ReadHeader(
-        m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, &m_GifPltNumber,
-        (void**)&m_pGifPalette, &m_GifBgIndex, nullptr);
-  }
-  if (readResult == CFX_GifDecodeStatus::Success) {
-    m_SrcBPC = 8;
-    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-    return true;
-  }
-  m_pGifContext = nullptr;
-  m_status = FXCODEC_STATUS_ERR_FORMAT;
-  return false;
-}
-
-bool CCodec_ProgressiveDecoder::TifDetectImageType(CFX_DIBAttribute* pAttribute,
-                                                   uint32_t size) {
-  CCodec_TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
-  if (!pTiffModule) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  m_pTiffContext = pTiffModule->CreateDecoder(m_pFile);
-  if (!m_pTiffContext) {
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  int32_t dummy_bpc;
-  bool ret = pTiffModule->LoadFrameInfo(m_pTiffContext.get(), 0, &m_SrcWidth,
-                                        &m_SrcHeight, &m_SrcComponents,
-                                        &dummy_bpc, pAttribute);
-  m_SrcComponents = 4;
-  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
-  if (!ret) {
-    m_pTiffContext.reset();
-    m_status = FXCODEC_STATUS_ERR_FORMAT;
-    return false;
-  }
-  return true;
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::LoadImageInfo(
-    const RetainPtr<IFX_SeekableReadStream>& pFile,
-    FXCODEC_IMAGE_TYPE imageType,
-    CFX_DIBAttribute* pAttribute,
-    bool bSkipImageTypeCheck) {
-  switch (m_status) {
-    case FXCODEC_STATUS_FRAME_READY:
-    case FXCODEC_STATUS_FRAME_TOBECONTINUE:
-    case FXCODEC_STATUS_DECODE_READY:
-    case FXCODEC_STATUS_DECODE_TOBECONTINUE:
-      return FXCODEC_STATUS_ERROR;
-    default:
-      break;
-  }
-  if (!pFile) {
-    m_status = FXCODEC_STATUS_ERR_PARAMS;
-    m_pFile = nullptr;
-    return m_status;
-  }
-  m_pFile = pFile;
-  m_offSet = 0;
-  m_SrcWidth = m_SrcHeight = 0;
-  m_SrcComponents = m_SrcBPC = 0;
-  m_clipBox = FX_RECT(0, 0, 0, 0);
-  m_startX = m_startY = 0;
-  m_sizeX = m_sizeY = 0;
-  m_SrcPassNumber = 0;
-  if (imageType != FXCODEC_IMAGE_UNKNOWN &&
-      DetectImageType(imageType, pAttribute)) {
-    m_imagType = imageType;
-    m_status = FXCODEC_STATUS_FRAME_READY;
-    return m_status;
-  }
-  // If we got here then the image data does not match the requested decoder.
-  // If we're skipping the type check then bail out at this point and return
-  // the failed status.
-  if (bSkipImageTypeCheck)
-    return m_status;
-
-  for (int type = FXCODEC_IMAGE_BMP; type < FXCODEC_IMAGE_MAX; type++) {
-    if (DetectImageType((FXCODEC_IMAGE_TYPE)type, pAttribute)) {
-      m_imagType = (FXCODEC_IMAGE_TYPE)type;
-      m_status = FXCODEC_STATUS_FRAME_READY;
-      return m_status;
-    }
-  }
-  m_status = FXCODEC_STATUS_ERR_FORMAT;
-  m_pFile = nullptr;
-  return m_status;
-}
-
-void CCodec_ProgressiveDecoder::SetClipBox(FX_RECT* clip) {
-  if (m_status != FXCODEC_STATUS_FRAME_READY)
-    return;
-
-  if (clip->IsEmpty()) {
-    m_clipBox = FX_RECT(0, 0, 0, 0);
-    return;
-  }
-  clip->left = std::max(clip->left, 0);
-  clip->right = std::min(clip->right, m_SrcWidth);
-  clip->top = std::max(clip->top, 0);
-  clip->bottom = std::min(clip->bottom, m_SrcHeight);
-  if (clip->IsEmpty()) {
-    m_clipBox = FX_RECT(0, 0, 0, 0);
-    return;
-  }
-  m_clipBox = *clip;
-}
-
-void CCodec_ProgressiveDecoder::GetDownScale(int& down_scale) {
-  down_scale = 1;
-  int ratio_w = m_clipBox.Width() / m_sizeX;
-  int ratio_h = m_clipBox.Height() / m_sizeY;
-  int ratio = (ratio_w > ratio_h) ? ratio_h : ratio_w;
-  if (ratio >= 8) {
-    down_scale = 8;
-  } else if (ratio >= 4) {
-    down_scale = 4;
-  } else if (ratio >= 2) {
-    down_scale = 2;
-  }
-  m_clipBox.left /= down_scale;
-  m_clipBox.right /= down_scale;
-  m_clipBox.top /= down_scale;
-  m_clipBox.bottom /= down_scale;
-  if (m_clipBox.right == m_clipBox.left) {
-    m_clipBox.right = m_clipBox.left + 1;
-  }
-  if (m_clipBox.bottom == m_clipBox.top) {
-    m_clipBox.bottom = m_clipBox.top + 1;
-  }
-}
-
-void CCodec_ProgressiveDecoder::GetTransMethod(FXDIB_Format des_format,
-                                               FXCodec_Format src_format) {
-  switch (des_format) {
-    case FXDIB_1bppMask:
-    case FXDIB_1bppRgb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 0;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    case FXDIB_8bppMask:
-    case FXDIB_8bppRgb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 1;
-          break;
-        case FXCodec_8bppGray:
-          m_TransMethod = 2;
-          break;
-        case FXCodec_1bppRgb:
-        case FXCodec_8bppRgb:
-          m_TransMethod = 3;
-          break;
-        case FXCodec_Rgb:
-        case FXCodec_Rgb32:
-        case FXCodec_Argb:
-          m_TransMethod = 4;
-          break;
-        case FXCodec_Cmyk:
-          m_TransMethod = 5;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    case FXDIB_Rgb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 6;
-          break;
-        case FXCodec_8bppGray:
-          m_TransMethod = 7;
-          break;
-        case FXCodec_1bppRgb:
-        case FXCodec_8bppRgb:
-          m_TransMethod = 8;
-          break;
-        case FXCodec_Rgb:
-        case FXCodec_Rgb32:
-        case FXCodec_Argb:
-          m_TransMethod = 9;
-          break;
-        case FXCodec_Cmyk:
-          m_TransMethod = 10;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    case FXDIB_Rgb32:
-    case FXDIB_Argb: {
-      switch (src_format) {
-        case FXCodec_1bppGray:
-          m_TransMethod = 6;
-          break;
-        case FXCodec_8bppGray:
-          m_TransMethod = 7;
-          break;
-        case FXCodec_1bppRgb:
-        case FXCodec_8bppRgb:
-          if (des_format == FXDIB_Argb) {
-            m_TransMethod = 12;
-          } else {
-            m_TransMethod = 8;
-          }
-          break;
-        case FXCodec_Rgb:
-        case FXCodec_Rgb32:
-          m_TransMethod = 9;
-          break;
-        case FXCodec_Cmyk:
-          m_TransMethod = 10;
-          break;
-        case FXCodec_Argb:
-          m_TransMethod = 11;
-          break;
-        default:
-          m_TransMethod = -1;
-      }
-    } break;
-    default:
-      m_TransMethod = -1;
-  }
-}
-
-void CCodec_ProgressiveDecoder::ReSampleScanline(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    int des_line,
-    uint8_t* src_scan,
-    FXCodec_Format src_format) {
-  int src_left = m_clipBox.left;
-  int des_left = m_startX;
-  uint8_t* des_scan =
-      pDeviceBitmap->GetBuffer() + des_line * pDeviceBitmap->GetPitch();
-  int src_bpp = src_format & 0xff;
-  int des_bpp = pDeviceBitmap->GetBPP();
-  int src_Bpp = src_bpp >> 3;
-  int des_Bpp = des_bpp >> 3;
-  src_scan += src_left * src_Bpp;
-  des_scan += des_left * des_Bpp;
-  for (int des_col = 0; des_col < m_sizeX; des_col++) {
-    PixelWeight* pPixelWeights = m_WeightHorz.GetPixelWeight(des_col);
-    switch (m_TransMethod) {
-      case -1:
-        return;
-      case 0:
-        return;
-      case 1:
-        return;
-      case 2: {
-        uint32_t des_g = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          des_g += pixel_weight * src_scan[j];
-        }
-        *des_scan++ = (uint8_t)(des_g >> 16);
-      } break;
-      case 3: {
-        int des_r = 0, des_g = 0, des_b = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          unsigned long argb = m_pSrcPalette[src_scan[j]];
-          des_r += pixel_weight * (uint8_t)(argb >> 16);
-          des_g += pixel_weight * (uint8_t)(argb >> 8);
-          des_b += pixel_weight * (uint8_t)argb;
-        }
-        *des_scan++ =
-            (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16));
-      } break;
-      case 4: {
-        uint32_t des_b = 0, des_g = 0, des_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_Bpp;
-          des_b += pixel_weight * (*src_pixel++);
-          des_g += pixel_weight * (*src_pixel++);
-          des_r += pixel_weight * (*src_pixel);
-        }
-        *des_scan++ =
-            (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16));
-      } break;
-      case 5: {
-        uint32_t des_b = 0, des_g = 0, des_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_Bpp;
-          uint8_t src_b = 0;
-          uint8_t src_g = 0;
-          uint8_t src_r = 0;
-          std::tie(src_r, src_g, src_b) =
-              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
-                                 255 - src_pixel[2], 255 - src_pixel[3]);
-          des_b += pixel_weight * src_b;
-          des_g += pixel_weight * src_g;
-          des_r += pixel_weight * src_r;
-        }
-        *des_scan++ =
-            (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16));
-      } break;
-      case 6:
-        return;
-      case 7: {
-        uint32_t des_g = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          des_g += pixel_weight * src_scan[j];
-        }
-        memset(des_scan, (uint8_t)(des_g >> 16), 3);
-        des_scan += des_Bpp;
-      } break;
-      case 8: {
-        int des_r = 0, des_g = 0, des_b = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          unsigned long argb = m_pSrcPalette[src_scan[j]];
-          des_r += pixel_weight * (uint8_t)(argb >> 16);
-          des_g += pixel_weight * (uint8_t)(argb >> 8);
-          des_b += pixel_weight * (uint8_t)argb;
-        }
-        *des_scan++ = (uint8_t)((des_b) >> 16);
-        *des_scan++ = (uint8_t)((des_g) >> 16);
-        *des_scan++ = (uint8_t)((des_r) >> 16);
-        des_scan += des_Bpp - 3;
-      } break;
-      case 12: {
-        if (m_pBmpContext) {
-          int des_r = 0, des_g = 0, des_b = 0;
-          for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-               j++) {
-            int pixel_weight =
-                pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-            unsigned long argb = m_pSrcPalette[src_scan[j]];
-            des_r += pixel_weight * (uint8_t)(argb >> 16);
-            des_g += pixel_weight * (uint8_t)(argb >> 8);
-            des_b += pixel_weight * (uint8_t)argb;
-          }
-          *des_scan++ = (uint8_t)((des_b) >> 16);
-          *des_scan++ = (uint8_t)((des_g) >> 16);
-          *des_scan++ = (uint8_t)((des_r) >> 16);
-          *des_scan++ = 0xFF;
-        } else {
-          int des_a = 0, des_r = 0, des_g = 0, des_b = 0;
-          for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-               j++) {
-            int pixel_weight =
-                pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-            unsigned long argb = m_pSrcPalette[src_scan[j]];
-            des_a += pixel_weight * (uint8_t)(argb >> 24);
-            des_r += pixel_weight * (uint8_t)(argb >> 16);
-            des_g += pixel_weight * (uint8_t)(argb >> 8);
-            des_b += pixel_weight * (uint8_t)argb;
-          }
-          *des_scan++ = (uint8_t)((des_b) >> 16);
-          *des_scan++ = (uint8_t)((des_g) >> 16);
-          *des_scan++ = (uint8_t)((des_r) >> 16);
-          *des_scan++ = (uint8_t)((des_a) >> 16);
-        }
-      } break;
-      case 9: {
-        uint32_t des_b = 0, des_g = 0, des_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_Bpp;
-          des_b += pixel_weight * (*src_pixel++);
-          des_g += pixel_weight * (*src_pixel++);
-          des_r += pixel_weight * (*src_pixel);
-        }
-        *des_scan++ = (uint8_t)((des_b) >> 16);
-        *des_scan++ = (uint8_t)((des_g) >> 16);
-        *des_scan++ = (uint8_t)((des_r) >> 16);
-        des_scan += des_Bpp - 3;
-      } break;
-      case 10: {
-        uint32_t des_b = 0, des_g = 0, des_r = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_Bpp;
-          uint8_t src_b = 0;
-          uint8_t src_g = 0;
-          uint8_t src_r = 0;
-          std::tie(src_r, src_g, src_b) =
-              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
-                                 255 - src_pixel[2], 255 - src_pixel[3]);
-          des_b += pixel_weight * src_b;
-          des_g += pixel_weight * src_g;
-          des_r += pixel_weight * src_r;
-        }
-        *des_scan++ = (uint8_t)((des_b) >> 16);
-        *des_scan++ = (uint8_t)((des_g) >> 16);
-        *des_scan++ = (uint8_t)((des_r) >> 16);
-        des_scan += des_Bpp - 3;
-      } break;
-      case 11: {
-        uint32_t des_alpha = 0, des_r = 0, des_g = 0, des_b = 0;
-        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
-             j++) {
-          int pixel_weight =
-              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
-          const uint8_t* src_pixel = src_scan + j * src_Bpp;
-          pixel_weight = pixel_weight * src_pixel[3] / 255;
-          des_b += pixel_weight * (*src_pixel++);
-          des_g += pixel_weight * (*src_pixel++);
-          des_r += pixel_weight * (*src_pixel);
-          des_alpha += pixel_weight;
-        }
-        *des_scan++ = (uint8_t)((des_b) >> 16);
-        *des_scan++ = (uint8_t)((des_g) >> 16);
-        *des_scan++ = (uint8_t)((des_r) >> 16);
-        *des_scan++ = (uint8_t)((des_alpha * 255) >> 16);
-      } break;
-      default:
-        return;
-    }
-  }
-}
-
-void CCodec_ProgressiveDecoder::ResampleVert(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    double scale_y,
-    int des_row) {
-  int des_Bpp = pDeviceBitmap->GetBPP() >> 3;
-  uint32_t des_ScanOffet = m_startX * des_Bpp;
-  int des_top = m_startY;
-  pdfium::base::CheckedNumeric<int> check_des_row_1 = des_row;
-  check_des_row_1 -= pdfium::base::checked_cast<int>(scale_y);
-  int des_row_1 = check_des_row_1.ValueOrDie();
-  if (des_row_1 < des_top) {
-    int des_bottom = des_top + m_sizeY;
-    if (des_row + (int)scale_y >= des_bottom - 1) {
-      uint8_t* scan_src =
-          (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
-      while (++des_row < des_bottom) {
-        uint8_t* scan_des =
-            (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
-        uint32_t size = m_sizeX * des_Bpp;
-        memmove(scan_des, scan_src, size);
-      }
-    }
-    return;
-  }
-  for (; des_row_1 < des_row; des_row_1++) {
-    uint8_t* scan_des =
-        (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet;
-    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top);
-    const uint8_t* scan_src1 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) +
-        des_ScanOffet;
-    const uint8_t* scan_src2 =
-        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + des_ScanOffet;
-    for (int des_col = 0; des_col < m_sizeX; des_col++) {
-      switch (pDeviceBitmap->GetFormat()) {
-        case FXDIB_Invalid:
-        case FXDIB_1bppMask:
-        case FXDIB_1bppRgb:
-          return;
-        case FXDIB_8bppMask:
-        case FXDIB_8bppRgb: {
-          if (pDeviceBitmap->GetPalette()) {
-            return;
-          }
-          int des_g = 0;
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)(des_g >> 16);
-        } break;
-        case FXDIB_Rgb:
-        case FXDIB_Rgb32: {
-          uint32_t des_b = 0, des_g = 0, des_r = 0;
-          des_b += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_r += pWeight->m_Weights[0] * (*scan_src1++);
-          scan_src1 += des_Bpp - 3;
-          des_b += pWeight->m_Weights[1] * (*scan_src2++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          des_r += pWeight->m_Weights[1] * (*scan_src2++);
-          scan_src2 += des_Bpp - 3;
-          *scan_des++ = (uint8_t)((des_b) >> 16);
-          *scan_des++ = (uint8_t)((des_g) >> 16);
-          *scan_des++ = (uint8_t)((des_r) >> 16);
-          scan_des += des_Bpp - 3;
-        } break;
-        case FXDIB_Argb: {
-          uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
-          des_b += pWeight->m_Weights[0] * (*scan_src1++);
-          des_g += pWeight->m_Weights[0] * (*scan_src1++);
-          des_r += pWeight->m_Weights[0] * (*scan_src1++);
-          des_a += pWeight->m_Weights[0] * (*scan_src1++);
-          des_b += pWeight->m_Weights[1] * (*scan_src2++);
-          des_g += pWeight->m_Weights[1] * (*scan_src2++);
-          des_r += pWeight->m_Weights[1] * (*scan_src2++);
-          des_a += pWeight->m_Weights[1] * (*scan_src2++);
-          *scan_des++ = (uint8_t)((des_b) >> 16);
-          *scan_des++ = (uint8_t)((des_g) >> 16);
-          *scan_des++ = (uint8_t)((des_r) >> 16);
-          *scan_des++ = (uint8_t)((des_a) >> 16);
-        } break;
-        default:
-          return;
-      }
-    }
-  }
-  int des_bottom = des_top + m_sizeY;
-  if (des_row + (int)scale_y >= des_bottom - 1) {
-    uint8_t* scan_src =
-        (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
-    while (++des_row < des_bottom) {
-      uint8_t* scan_des =
-          (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
-      uint32_t size = m_sizeX * des_Bpp;
-      memmove(scan_des, scan_src, size);
-    }
-  }
-}
-
-void CCodec_ProgressiveDecoder::Resample(
-    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
-    int32_t src_line,
-    uint8_t* src_scan,
-    FXCodec_Format src_format) {
-  int src_top = m_clipBox.top;
-  int des_top = m_startY;
-  int src_hei = m_clipBox.Height();
-  int des_hei = m_sizeY;
-  if (src_line >= src_top) {
-    double scale_y = (double)des_hei / (double)src_hei;
-    int src_row = src_line - src_top;
-    int des_row = (int)(src_row * scale_y) + des_top;
-    if (des_row >= des_top + des_hei) {
-      return;
-    }
-    ReSampleScanline(pDeviceBitmap, des_row, m_pDecodeBuf, src_format);
-    if (scale_y > 1.0) {
-      ResampleVert(pDeviceBitmap, scale_y, des_row);
-    }
-  }
-}
-
-std::pair<FXCODEC_STATUS, size_t> CCodec_ProgressiveDecoder::GetFrames() {
-  if (!(m_status == FXCODEC_STATUS_FRAME_READY ||
-        m_status == FXCODEC_STATUS_FRAME_TOBECONTINUE)) {
-    return {FXCODEC_STATUS_ERROR, 0};
-  }
-
-  switch (m_imagType) {
-    case FXCODEC_IMAGE_JPG:
-    case FXCODEC_IMAGE_BMP:
-    case FXCODEC_IMAGE_PNG:
-    case FXCODEC_IMAGE_TIF:
-      m_FrameNumber = 1;
-      m_status = FXCODEC_STATUS_DECODE_READY;
-      return {m_status, 1};
-    case FXCODEC_IMAGE_GIF: {
-      CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-      if (!pGifModule) {
-        m_status = FXCODEC_STATUS_ERR_MEMORY;
-        return {m_status, 0};
-      }
-      while (true) {
-        CFX_GifDecodeStatus readResult;
-        std::tie(readResult, m_FrameNumber) =
-            pGifModule->LoadFrameInfo(m_pGifContext.get());
-        while (readResult == CFX_GifDecodeStatus::Unfinished) {
-          FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_READ;
-          if (!GifReadMoreData(pGifModule, error_status))
-            return {error_status, 0};
-
-          std::tie(readResult, m_FrameNumber) =
-              pGifModule->LoadFrameInfo(m_pGifContext.get());
-        }
-        if (readResult == CFX_GifDecodeStatus::Success) {
-          m_status = FXCODEC_STATUS_DECODE_READY;
-          return {m_status, m_FrameNumber};
-        }
-        m_pGifContext = nullptr;
-        m_status = FXCODEC_STATUS_ERROR;
-        return {m_status, 0};
-      }
-    }
-    default:
-      return {FXCODEC_STATUS_ERROR, 0};
-  }
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::StartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-    int start_x,
-    int start_y,
-    int size_x,
-    int size_y) {
-  if (m_status != FXCODEC_STATUS_DECODE_READY)
-    return FXCODEC_STATUS_ERROR;
-
-  if (!pDIBitmap || pDIBitmap->GetBPP() < 8 || m_FrameNumber == 0)
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  m_pDeviceBitmap = pDIBitmap;
-  if (m_clipBox.IsEmpty())
-    return FXCODEC_STATUS_ERR_PARAMS;
-  if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535)
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  FX_RECT device_rc =
-      FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y);
-  int32_t out_range_x = device_rc.right - pDIBitmap->GetWidth();
-  int32_t out_range_y = device_rc.bottom - pDIBitmap->GetHeight();
-  device_rc.Intersect(
-      FX_RECT(0, 0, pDIBitmap->GetWidth(), pDIBitmap->GetHeight()));
-  if (device_rc.IsEmpty())
-    return FXCODEC_STATUS_ERR_PARAMS;
-
-  m_startX = device_rc.left;
-  m_startY = device_rc.top;
-  m_sizeX = device_rc.Width();
-  m_sizeY = device_rc.Height();
-  m_FrameCur = 0;
-  if (start_x < 0 || out_range_x > 0) {
-    float scaleX = (float)m_clipBox.Width() / (float)size_x;
-    if (start_x < 0) {
-      m_clipBox.left -= (int32_t)ceil((float)start_x * scaleX);
-    }
-    if (out_range_x > 0) {
-      m_clipBox.right -= (int32_t)floor((float)out_range_x * scaleX);
-    }
-  }
-  if (start_y < 0 || out_range_y > 0) {
-    float scaleY = (float)m_clipBox.Height() / (float)size_y;
-    if (start_y < 0) {
-      m_clipBox.top -= (int32_t)ceil((float)start_y * scaleY);
-    }
-    if (out_range_y > 0) {
-      m_clipBox.bottom -= (int32_t)floor((float)out_range_y * scaleY);
-    }
-  }
-  if (m_clipBox.IsEmpty()) {
-    return FXCODEC_STATUS_ERR_PARAMS;
-  }
-  switch (m_imagType) {
-    case FXCODEC_IMAGE_JPG:
-      return JpegStartDecode(pDIBitmap);
-    case FXCODEC_IMAGE_PNG:
-      return PngStartDecode(pDIBitmap);
-    case FXCODEC_IMAGE_GIF:
-      return GifStartDecode(pDIBitmap);
-    case FXCODEC_IMAGE_BMP:
-      return BmpStartDecode(pDIBitmap);
-    case FXCODEC_IMAGE_TIF:
-      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return m_status;
-    default:
-      return FXCODEC_STATUS_ERROR;
-  }
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::JpegStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  int down_scale = 1;
-  GetDownScale(down_scale);
-  // Setting jump marker before calling StartScanLine, since a longjmp to
-  // the marker indicates a fatal error.
-  if (setjmp(*m_pJpegContext->GetJumpMark()) == -1) {
-    m_pJpegContext.reset();
-    m_status = FXCODEC_STATUS_ERROR;
-    return FXCODEC_STATUS_ERROR;
-  }
-
-  CCodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
-  bool startStatus =
-      pJpegModule->StartScanline(m_pJpegContext.get(), down_scale);
-  while (!startStatus) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
-    if (!JpegReadMoreData(pJpegModule, error_status)) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = error_status;
-      return m_status;
-    }
-
-    startStatus = pJpegModule->StartScanline(m_pJpegContext.get(), down_scale);
-  }
-  int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale;
-  scanline_size = (scanline_size * m_SrcComponents + 3) / 4 * 4;
-  FX_Free(m_pDecodeBuf);
-  m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size);
-  memset(m_pDecodeBuf, 0, scanline_size);
-  m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
-                    m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  switch (m_SrcComponents) {
-    case 1:
-      m_SrcFormat = FXCodec_8bppGray;
-      break;
-    case 3:
-      m_SrcFormat = FXCodec_Rgb;
-      break;
-    case 4:
-      m_SrcFormat = FXCodec_Cmyk;
-      break;
-  }
-  GetTransMethod(pDIBitmap->GetFormat(), m_SrcFormat);
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::PngStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  CCodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule();
-  if (!pPngModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_pPngContext = pPngModule->Start(this);
-  if (!m_pPngContext) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_offSet = 0;
-  switch (m_pDeviceBitmap->GetFormat()) {
-    case FXDIB_8bppMask:
-    case FXDIB_8bppRgb:
-      m_SrcComponents = 1;
-      m_SrcFormat = FXCodec_8bppGray;
-      break;
-    case FXDIB_Rgb:
-      m_SrcComponents = 3;
-      m_SrcFormat = FXCodec_Rgb;
-      break;
-    case FXDIB_Rgb32:
-    case FXDIB_Argb:
-      m_SrcComponents = 4;
-      m_SrcFormat = FXCodec_Argb;
-      break;
-    default: {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_ERR_PARAMS;
-      return m_status;
-    }
-  }
-  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  int scanline_size = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4;
-  FX_Free(m_pDecodeBuf);
-  m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size);
-  memset(m_pDecodeBuf, 0, scanline_size);
-  m_WeightHorzOO.Calc(m_sizeX, m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::GifStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-  if (!pGifModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_SrcFormat = FXCodec_8bppRgb;
-  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  int scanline_size = (m_SrcWidth + 3) / 4 * 4;
-  FX_Free(m_pDecodeBuf);
-  m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size);
-  memset(m_pDecodeBuf, 0, scanline_size);
-  m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
-                    m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  m_FrameCur = 0;
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::BmpStartDecode(
-    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  CCodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
-  if (!pBmpModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
-  m_ScanlineSize = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4;
-  FX_Free(m_pDecodeBuf);
-  m_pDecodeBuf = FX_Alloc(uint8_t, m_ScanlineSize);
-  memset(m_pDecodeBuf, 0, m_ScanlineSize);
-  m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
-                    m_clipBox.Width());
-  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
-  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-  return m_status;
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::ContinueDecode() {
-  if (m_status != FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    return FXCODEC_STATUS_ERROR;
-
-  switch (m_imagType) {
-    case FXCODEC_IMAGE_JPG:
-      return JpegContinueDecode();
-    case FXCODEC_IMAGE_PNG:
-      return PngContinueDecode();
-    case FXCODEC_IMAGE_GIF:
-      return GifContinueDecode();
-    case FXCODEC_IMAGE_BMP:
-      return BmpContinueDecode();
-    case FXCODEC_IMAGE_TIF:
-      return TifContinueDecode();
-    default:
-      return FXCODEC_STATUS_ERROR;
-  }
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::JpegContinueDecode() {
-  CCodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
-  // Setting jump marker before calling ReadScanLine, since a longjmp to
-  // the marker indicates a fatal error.
-  if (setjmp(*m_pJpegContext->GetJumpMark()) == -1) {
-    m_pJpegContext.reset();
-    m_status = FXCODEC_STATUS_ERROR;
-    return FXCODEC_STATUS_ERROR;
-  }
-
-  while (true) {
-    bool readRes =
-        pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf);
-    while (!readRes) {
-      FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
-      if (!JpegReadMoreData(pJpegModule, error_status)) {
-        m_pDeviceBitmap = nullptr;
-        m_pFile = nullptr;
-        m_status = error_status;
-        return m_status;
-      }
-      readRes = pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf);
-    }
-    if (m_SrcFormat == FXCodec_Rgb) {
-      int src_Bpp = (m_SrcFormat & 0xff) >> 3;
-      RGB2BGR(m_pDecodeBuf + m_clipBox.left * src_Bpp, m_clipBox.Width());
-    }
-    if (m_SrcRow >= m_clipBox.bottom) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_DECODE_FINISH;
-      return m_status;
-    }
-    Resample(m_pDeviceBitmap, m_SrcRow, m_pDecodeBuf, m_SrcFormat);
-    m_SrcRow++;
-  }
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::PngContinueDecode() {
-  CCodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule();
-  if (!pPngModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  while (true) {
-    uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet;
-    uint32_t input_size =
-        remain_size > FXCODEC_BLOCK_SIZE ? FXCODEC_BLOCK_SIZE : remain_size;
-    if (input_size == 0) {
-      m_pPngContext.reset();
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_DECODE_FINISH;
-      return m_status;
-    }
-    if (m_pSrcBuf && input_size > m_SrcSize) {
-      FX_Free(m_pSrcBuf);
-      m_pSrcBuf = FX_Alloc(uint8_t, input_size);
-      memset(m_pSrcBuf, 0, input_size);
-      m_SrcSize = input_size;
-    }
-    bool bResult = m_pFile->ReadBlock(m_pSrcBuf, m_offSet, input_size);
-    if (!bResult) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_ERR_READ;
-      return m_status;
-    }
-    m_offSet += input_size;
-    bResult =
-        pPngModule->Input(m_pPngContext.get(), m_pSrcBuf, input_size, nullptr);
-    if (!bResult) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_ERROR;
-      return m_status;
-    }
-  }
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::GifContinueDecode() {
-  CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
-  if (!pGifModule) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-
-  CFX_GifDecodeStatus readRes =
-      pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur, nullptr);
-  while (readRes == CFX_GifDecodeStatus::Unfinished) {
-    FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
-    if (!GifReadMoreData(pGifModule, error_status)) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = error_status;
-      return m_status;
-    }
-    readRes = pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur, nullptr);
-  }
-
-  if (readRes == CFX_GifDecodeStatus::Success) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_DECODE_FINISH;
-    return m_status;
-  }
-
-  m_pDeviceBitmap = nullptr;
-  m_pFile = nullptr;
-  m_status = FXCODEC_STATUS_ERROR;
-  return m_status;
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::BmpContinueDecode() {
-  CCodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
-  if (!pBmpModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  while (true) {
-    int32_t readRes = pBmpModule->LoadImage(m_pBmpContext.get());
-    while (readRes == 2) {
-      FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
-      if (!BmpReadMoreData(pBmpModule, error_status)) {
-        m_pDeviceBitmap = nullptr;
-        m_pFile = nullptr;
-        m_status = error_status;
-        return m_status;
-      }
-      readRes = pBmpModule->LoadImage(m_pBmpContext.get());
-    }
-    if (readRes == 1) {
-      m_pDeviceBitmap = nullptr;
-      m_pFile = nullptr;
-      m_status = FXCODEC_STATUS_DECODE_FINISH;
-      return m_status;
-    }
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERROR;
-    return m_status;
-  }
-}
-
-FXCODEC_STATUS CCodec_ProgressiveDecoder::TifContinueDecode() {
-  CCodec_TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
-  if (!pTiffModule) {
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  bool ret = false;
-  if (m_pDeviceBitmap->GetBPP() == 32 &&
-      m_pDeviceBitmap->GetWidth() == m_SrcWidth && m_SrcWidth == m_sizeX &&
-      m_pDeviceBitmap->GetHeight() == m_SrcHeight && m_SrcHeight == m_sizeY &&
-      m_startX == 0 && m_startY == 0 && m_clipBox.left == 0 &&
-      m_clipBox.top == 0 && m_clipBox.right == m_SrcWidth &&
-      m_clipBox.bottom == m_SrcHeight) {
-    ret = pTiffModule->Decode(m_pTiffContext.get(), m_pDeviceBitmap);
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    if (!ret) {
-      m_status = FXCODEC_STATUS_ERROR;
-      return m_status;
-    }
-    m_status = FXCODEC_STATUS_DECODE_FINISH;
-    return m_status;
-  }
-
-  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Argb);
-  if (!pDIBitmap->GetBuffer()) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  ret = pTiffModule->Decode(m_pTiffContext.get(), pDIBitmap);
-  if (!ret) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERROR;
-    return m_status;
-  }
-  RetainPtr<CFX_DIBitmap> pClipBitmap =
-      (m_clipBox.left == 0 && m_clipBox.top == 0 &&
-       m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight)
-          ? pDIBitmap
-          : pDIBitmap->Clone(&m_clipBox);
-  if (!pClipBitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  RetainPtr<CFX_DIBitmap> pFormatBitmap;
-  switch (m_pDeviceBitmap->GetFormat()) {
-    case FXDIB_8bppRgb:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_8bppRgb);
-      break;
-    case FXDIB_8bppMask:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_8bppMask);
-      break;
-    case FXDIB_Rgb:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_Rgb);
-      break;
-    case FXDIB_Rgb32:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
-                            FXDIB_Rgb32);
-      break;
-    case FXDIB_Argb:
-      pFormatBitmap = pClipBitmap;
-      break;
-    default:
-      break;
-  }
-  switch (m_pDeviceBitmap->GetFormat()) {
-    case FXDIB_8bppRgb:
-    case FXDIB_8bppMask: {
-      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
-        uint8_t* src_line = (uint8_t*)pClipBitmap->GetScanline(row);
-        uint8_t* des_line = (uint8_t*)pFormatBitmap->GetScanline(row);
-        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
-          uint8_t _a = 255 - src_line[3];
-          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
-          *des_line++ = FXRGB2GRAY(r, g, b);
-          src_line += 4;
-        }
-      }
-    } break;
-    case FXDIB_Rgb:
-    case FXDIB_Rgb32: {
-      int32_t desBpp = (m_pDeviceBitmap->GetFormat() == FXDIB_Rgb) ? 3 : 4;
-      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
-        uint8_t* src_line = (uint8_t*)pClipBitmap->GetScanline(row);
-        uint8_t* des_line = (uint8_t*)pFormatBitmap->GetScanline(row);
-        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
-          uint8_t _a = 255 - src_line[3];
-          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
-          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
-          *des_line++ = b;
-          *des_line++ = g;
-          *des_line++ = r;
-          des_line += desBpp - 3;
-          src_line += 4;
-        }
-      }
-    } break;
-    default:
-      break;
-  }
-  if (!pFormatBitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  RetainPtr<CFX_DIBitmap> pStrechBitmap =
-      pFormatBitmap->StretchTo(m_sizeX, m_sizeY, FXDIB_INTERPOL, nullptr);
-  pFormatBitmap = nullptr;
-  if (!pStrechBitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS_ERR_MEMORY;
-    return m_status;
-  }
-  m_pDeviceBitmap->TransferBitmap(m_startX, m_startY, m_sizeX, m_sizeY,
-                                  pStrechBitmap, 0, 0);
-  m_pDeviceBitmap = nullptr;
-  m_pFile = nullptr;
-  m_status = FXCODEC_STATUS_DECODE_FINISH;
-  return m_status;
-}
-
-std::unique_ptr<CCodec_ProgressiveDecoder>
-CCodec_ModuleMgr::CreateProgressiveDecoder() {
-  return pdfium::MakeUnique<CCodec_ProgressiveDecoder>(this);
-}
diff --git a/core/fxcodec/codec/fx_codec_rle_unittest.cpp b/core/fxcodec/codec/fx_codec_rle_unittest.cpp
deleted file mode 100644
index 7b0af90..0000000
--- a/core/fxcodec/codec/fx_codec_rle_unittest.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdint.h>
-
-#include <limits>
-
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "core/fxcodec/codec/ccodec_basicmodule.h"
-#include "core/fxcodec/fx_codec.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TEST(fxcodec, RLETestBadInputs) {
-  const uint8_t src_buf[] = {1};
-  uint8_t* dest_buf = nullptr;
-  uint32_t src_size = 4;
-  uint32_t dest_size = 0;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Error codes, not segvs, should callers pass us a nullptr pointer.
-  EXPECT_FALSE(
-      pEncoders->RunLengthEncode(src_buf, src_size, &dest_buf, nullptr));
-  EXPECT_FALSE(
-      pEncoders->RunLengthEncode(src_buf, src_size, nullptr, &dest_size));
-  EXPECT_FALSE(pEncoders->RunLengthEncode(src_buf, 0, &dest_buf, &dest_size));
-  EXPECT_FALSE(
-      pEncoders->RunLengthEncode(nullptr, src_size, &dest_buf, &dest_size));
-}
-
-// Check length 1 input works. Check terminating character is applied.
-TEST(fxcodec, RLETestShortInput) {
-  const uint8_t src_buf[] = {1};
-  uint8_t* dest_buf = nullptr;
-  uint32_t src_size = 1;
-  uint32_t dest_size = 0;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf, src_size, &dest_buf, &dest_size));
-  ASSERT_EQ(3u, dest_size);
-  EXPECT_EQ(0, dest_buf[0]);
-  EXPECT_EQ(1, dest_buf[1]);
-  EXPECT_EQ(128, dest_buf[2]);
-
-  FX_Free(dest_buf);
-}
-
-// Check a few basic cases (2 matching runs in a row, matching run followed
-// by a non-matching run, and non-matching run followed by a matching run).
-TEST(fxcodec, RLETestNormalInputs) {
-  // Match, match
-  const uint8_t src_buf_1[] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4};
-
-  // Match, non-match
-  const uint8_t src_buf_2[] = {2, 2, 2, 2, 1, 2, 3, 4, 5, 6};
-
-  // Non-match, match
-  const uint8_t src_buf_3[] = {1, 2, 3, 4, 5, 3, 3, 3, 3, 3};
-
-  uint32_t src_size = 10;
-  uint32_t dest_size = 0;
-  uint8_t* dest_buf = nullptr;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Case 1:
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf_1, src_size, &dest_buf, &dest_size));
-  uint8_t* decoded_buf = nullptr;
-  uint32_t decoded_size = 0;
-  RunLengthDecode(dest_buf, dest_size, &decoded_buf, &decoded_size);
-  ASSERT_EQ(src_size, decoded_size);
-  for (uint32_t i = 0; i < src_size; i++)
-    EXPECT_EQ(src_buf_1[i], decoded_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-  FX_Free(decoded_buf);
-
-  // Case 2:
-  dest_buf = nullptr;
-  dest_size = 0;
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf_2, src_size, &dest_buf, &dest_size));
-  decoded_buf = nullptr;
-  decoded_size = 0;
-  RunLengthDecode(dest_buf, dest_size, &decoded_buf, &decoded_size);
-  ASSERT_EQ(src_size, decoded_size);
-  for (uint32_t i = 0; i < src_size; i++)
-    EXPECT_EQ(src_buf_2[i], decoded_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-  FX_Free(decoded_buf);
-
-  // Case 3:
-  dest_buf = nullptr;
-  dest_size = 0;
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf_3, src_size, &dest_buf, &dest_size));
-  decoded_buf = nullptr;
-  decoded_size = 0;
-  RunLengthDecode(dest_buf, dest_size, &decoded_buf, &decoded_size);
-  ASSERT_EQ(src_size, decoded_size);
-  for (uint32_t i = 0; i < src_size; i++)
-    EXPECT_EQ(src_buf_3[i], decoded_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-  FX_Free(decoded_buf);
-}
-
-// Check that runs longer than 128 are broken up properly, both matched and
-// non-matched.
-TEST(fxcodec, RLETestFullLengthInputs) {
-  // Match, match
-  const uint8_t src_buf_1[260] = {1};
-
-  // Match, non-match
-  uint8_t src_buf_2[260] = {2};
-  for (uint16_t i = 128; i < 260; i++)
-    src_buf_2[i] = (uint8_t)(i - 125);
-
-  // Non-match, match
-  uint8_t src_buf_3[260] = {3};
-  for (uint8_t i = 0; i < 128; i++)
-    src_buf_3[i] = i;
-
-  // Non-match, non-match
-  uint8_t src_buf_4[260];
-  for (uint16_t i = 0; i < 260; i++)
-    src_buf_4[i] = (uint8_t)(i);
-
-  uint32_t src_size = 260;
-  uint32_t dest_size = 0;
-  uint8_t* dest_buf = nullptr;
-
-  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
-  EXPECT_TRUE(pEncoders);
-
-  // Case 1:
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf_1, src_size, &dest_buf, &dest_size));
-  uint8_t* decoded_buf = nullptr;
-  uint32_t decoded_size = 0;
-  RunLengthDecode(dest_buf, dest_size, &decoded_buf, &decoded_size);
-  ASSERT_EQ(src_size, decoded_size);
-  for (uint32_t i = 0; i < src_size; i++)
-    EXPECT_EQ(src_buf_1[i], decoded_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-  FX_Free(decoded_buf);
-
-  // Case 2:
-  dest_buf = nullptr;
-  dest_size = 0;
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf_2, src_size, &dest_buf, &dest_size));
-  decoded_buf = nullptr;
-  decoded_size = 0;
-  RunLengthDecode(dest_buf, dest_size, &decoded_buf, &decoded_size);
-  ASSERT_EQ(src_size, decoded_size);
-  for (uint32_t i = 0; i < src_size; i++)
-    EXPECT_EQ(src_buf_2[i], decoded_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-  FX_Free(decoded_buf);
-
-  // Case 3:
-  dest_buf = nullptr;
-  dest_size = 0;
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf_3, src_size, &dest_buf, &dest_size));
-  decoded_buf = nullptr;
-  decoded_size = 0;
-  RunLengthDecode(dest_buf, dest_size, &decoded_buf, &decoded_size);
-  ASSERT_EQ(src_size, decoded_size);
-  for (uint32_t i = 0; i < src_size; i++)
-    EXPECT_EQ(src_buf_3[i], decoded_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-  FX_Free(decoded_buf);
-
-  // Case 4:
-  dest_buf = nullptr;
-  dest_size = 0;
-  EXPECT_TRUE(
-      pEncoders->RunLengthEncode(src_buf_4, src_size, &dest_buf, &dest_size));
-  decoded_buf = nullptr;
-  decoded_size = 0;
-  RunLengthDecode(dest_buf, dest_size, &decoded_buf, &decoded_size);
-  ASSERT_EQ(src_size, decoded_size);
-  for (uint32_t i = 0; i < src_size; i++)
-    EXPECT_EQ(src_buf_4[i], decoded_buf[i]) << " at " << i;
-  FX_Free(dest_buf);
-  FX_Free(decoded_buf);
-}
diff --git a/core/fxcodec/codec_module_iface.h b/core/fxcodec/codec_module_iface.h
new file mode 100644
index 0000000..992d2ad
--- /dev/null
+++ b/core/fxcodec/codec_module_iface.h
@@ -0,0 +1,43 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_CODEC_MODULE_IFACE_H_
+#define CORE_FXCODEC_CODEC_MODULE_IFACE_H_
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_CodecMemory;
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class ModuleIface {
+ public:
+  class Context {
+   public:
+    virtual ~Context() = default;
+  };
+
+  virtual ~ModuleIface() = default;
+
+  // Returns the number of unprocessed bytes remaining in the input buffer.
+  virtual FX_FILESIZE GetAvailInput(Context* pContext) const = 0;
+
+  // Provides a new input buffer to the codec. Returns true on success,
+  // setting details about the image extracted from the buffer into |pAttribute|
+  // (if provided and the codec is capable providing that information).
+  virtual bool Input(Context* pContext,
+                     RetainPtr<CFX_CodecMemory> codec_memory,
+                     CFX_DIBAttribute* pAttribute) = 0;
+};
+
+}  // namespace fxcodec
+
+using fxcodec::ModuleIface;
+
+#endif  // CORE_FXCODEC_CODEC_MODULE_IFACE_H_
diff --git a/core/fxcodec/fax/faxmodule.cpp b/core/fxcodec/fax/faxmodule.cpp
new file mode 100644
index 0000000..22ba764
--- /dev/null
+++ b/core/fxcodec/fax/faxmodule.cpp
@@ -0,0 +1,818 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/fax/faxmodule.h"
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/cfx_binarybuf.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+namespace fxcodec {
+
+namespace {
+
+const uint8_t OneLeadPos[256] = {
+    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3,
+    3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+// Limit of image dimension. Use the same limit as the JBIG2 codecs.
+constexpr int kFaxMaxImageDimension = 65535;
+
+constexpr int kFaxBpc = 1;
+constexpr int kFaxComps = 1;
+
+int FindBit(const uint8_t* data_buf, int max_pos, int start_pos, bool bit) {
+  ASSERT(start_pos >= 0);
+  if (start_pos >= max_pos)
+    return max_pos;
+
+  const uint8_t bit_xor = bit ? 0x00 : 0xff;
+  int bit_offset = start_pos % 8;
+  if (bit_offset) {
+    const int byte_pos = start_pos / 8;
+    uint8_t data = (data_buf[byte_pos] ^ bit_xor) & (0xff >> bit_offset);
+    if (data)
+      return byte_pos * 8 + OneLeadPos[data];
+
+    start_pos += 7;
+  }
+
+  const int max_byte = (max_pos + 7) / 8;
+  int byte_pos = start_pos / 8;
+
+  // Try reading in bigger chunks in case there are long runs to be skipped.
+  static constexpr int kBulkReadSize = 8;
+  if (max_byte >= kBulkReadSize && byte_pos < max_byte - kBulkReadSize) {
+    static constexpr uint8_t skip_block_0[kBulkReadSize] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    static constexpr uint8_t skip_block_1[kBulkReadSize] = {
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    const uint8_t* skip_block = bit ? skip_block_0 : skip_block_1;
+    while (byte_pos < max_byte - kBulkReadSize &&
+           memcmp(data_buf + byte_pos, skip_block, kBulkReadSize) == 0) {
+      byte_pos += kBulkReadSize;
+    }
+  }
+
+  while (byte_pos < max_byte) {
+    uint8_t data = data_buf[byte_pos] ^ bit_xor;
+    if (data)
+      return std::min(byte_pos * 8 + OneLeadPos[data], max_pos);
+
+    ++byte_pos;
+  }
+  return max_pos;
+}
+
+void FaxG4FindB1B2(pdfium::span<const uint8_t> ref_buf,
+                   int columns,
+                   int a0,
+                   bool a0color,
+                   int* b1,
+                   int* b2) {
+  bool first_bit = a0 < 0 || (ref_buf[a0 / 8] & (1 << (7 - a0 % 8))) != 0;
+  *b1 = FindBit(ref_buf.data(), columns, a0 + 1, !first_bit);
+  if (*b1 >= columns) {
+    *b1 = *b2 = columns;
+    return;
+  }
+  if (first_bit == !a0color) {
+    *b1 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit);
+    first_bit = !first_bit;
+  }
+  if (*b1 >= columns) {
+    *b1 = *b2 = columns;
+    return;
+  }
+  *b2 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit);
+}
+
+void FaxFillBits(uint8_t* dest_buf, int columns, int startpos, int endpos) {
+  startpos = std::max(startpos, 0);
+  endpos = pdfium::clamp(endpos, 0, columns);
+  if (startpos >= endpos)
+    return;
+
+  int first_byte = startpos / 8;
+  int last_byte = (endpos - 1) / 8;
+  if (first_byte == last_byte) {
+    for (int i = startpos % 8; i <= (endpos - 1) % 8; ++i)
+      dest_buf[first_byte] -= 1 << (7 - i);
+    return;
+  }
+
+  for (int i = startpos % 8; i < 8; ++i)
+    dest_buf[first_byte] -= 1 << (7 - i);
+  for (int i = 0; i <= (endpos - 1) % 8; ++i)
+    dest_buf[last_byte] -= 1 << (7 - i);
+
+  if (last_byte > first_byte + 1)
+    memset(dest_buf + first_byte + 1, 0, last_byte - first_byte - 1);
+}
+
+inline bool NextBit(const uint8_t* src_buf, int* bitpos) {
+  int pos = (*bitpos)++;
+  return !!(src_buf[pos / 8] & (1 << (7 - pos % 8)));
+}
+
+const uint8_t FaxBlackRunIns[] = {
+    0,          2,          0x02,       3,          0,          0x03,
+    2,          0,          2,          0x02,       1,          0,
+    0x03,       4,          0,          2,          0x02,       6,
+    0,          0x03,       5,          0,          1,          0x03,
+    7,          0,          2,          0x04,       9,          0,
+    0x05,       8,          0,          3,          0x04,       10,
+    0,          0x05,       11,         0,          0x07,       12,
+    0,          2,          0x04,       13,         0,          0x07,
+    14,         0,          1,          0x18,       15,         0,
+    5,          0x08,       18,         0,          0x0f,       64,
+    0,          0x17,       16,         0,          0x18,       17,
+    0,          0x37,       0,          0,          10,         0x08,
+    0x00,       0x07,       0x0c,       0x40,       0x07,       0x0d,
+    0x80,       0x07,       0x17,       24,         0,          0x18,
+    25,         0,          0x28,       23,         0,          0x37,
+    22,         0,          0x67,       19,         0,          0x68,
+    20,         0,          0x6c,       21,         0,          54,
+    0x12,       1984 % 256, 1984 / 256, 0x13,       2048 % 256, 2048 / 256,
+    0x14,       2112 % 256, 2112 / 256, 0x15,       2176 % 256, 2176 / 256,
+    0x16,       2240 % 256, 2240 / 256, 0x17,       2304 % 256, 2304 / 256,
+    0x1c,       2368 % 256, 2368 / 256, 0x1d,       2432 % 256, 2432 / 256,
+    0x1e,       2496 % 256, 2496 / 256, 0x1f,       2560 % 256, 2560 / 256,
+    0x24,       52,         0,          0x27,       55,         0,
+    0x28,       56,         0,          0x2b,       59,         0,
+    0x2c,       60,         0,          0x33,       320 % 256,  320 / 256,
+    0x34,       384 % 256,  384 / 256,  0x35,       448 % 256,  448 / 256,
+    0x37,       53,         0,          0x38,       54,         0,
+    0x52,       50,         0,          0x53,       51,         0,
+    0x54,       44,         0,          0x55,       45,         0,
+    0x56,       46,         0,          0x57,       47,         0,
+    0x58,       57,         0,          0x59,       58,         0,
+    0x5a,       61,         0,          0x5b,       256 % 256,  256 / 256,
+    0x64,       48,         0,          0x65,       49,         0,
+    0x66,       62,         0,          0x67,       63,         0,
+    0x68,       30,         0,          0x69,       31,         0,
+    0x6a,       32,         0,          0x6b,       33,         0,
+    0x6c,       40,         0,          0x6d,       41,         0,
+    0xc8,       128,        0,          0xc9,       192,        0,
+    0xca,       26,         0,          0xcb,       27,         0,
+    0xcc,       28,         0,          0xcd,       29,         0,
+    0xd2,       34,         0,          0xd3,       35,         0,
+    0xd4,       36,         0,          0xd5,       37,         0,
+    0xd6,       38,         0,          0xd7,       39,         0,
+    0xda,       42,         0,          0xdb,       43,         0,
+    20,         0x4a,       640 % 256,  640 / 256,  0x4b,       704 % 256,
+    704 / 256,  0x4c,       768 % 256,  768 / 256,  0x4d,       832 % 256,
+    832 / 256,  0x52,       1280 % 256, 1280 / 256, 0x53,       1344 % 256,
+    1344 / 256, 0x54,       1408 % 256, 1408 / 256, 0x55,       1472 % 256,
+    1472 / 256, 0x5a,       1536 % 256, 1536 / 256, 0x5b,       1600 % 256,
+    1600 / 256, 0x64,       1664 % 256, 1664 / 256, 0x65,       1728 % 256,
+    1728 / 256, 0x6c,       512 % 256,  512 / 256,  0x6d,       576 % 256,
+    576 / 256,  0x72,       896 % 256,  896 / 256,  0x73,       960 % 256,
+    960 / 256,  0x74,       1024 % 256, 1024 / 256, 0x75,       1088 % 256,
+    1088 / 256, 0x76,       1152 % 256, 1152 / 256, 0x77,       1216 % 256,
+    1216 / 256, 0xff};
+
+const uint8_t FaxWhiteRunIns[] = {
+    0,          0,          0,          6,          0x07,       2,
+    0,          0x08,       3,          0,          0x0B,       4,
+    0,          0x0C,       5,          0,          0x0E,       6,
+    0,          0x0F,       7,          0,          6,          0x07,
+    10,         0,          0x08,       11,         0,          0x12,
+    128,        0,          0x13,       8,          0,          0x14,
+    9,          0,          0x1b,       64,         0,          9,
+    0x03,       13,         0,          0x07,       1,          0,
+    0x08,       12,         0,          0x17,       192,        0,
+    0x18,       1664 % 256, 1664 / 256, 0x2a,       16,         0,
+    0x2B,       17,         0,          0x34,       14,         0,
+    0x35,       15,         0,          12,         0x03,       22,
+    0,          0x04,       23,         0,          0x08,       20,
+    0,          0x0c,       19,         0,          0x13,       26,
+    0,          0x17,       21,         0,          0x18,       28,
+    0,          0x24,       27,         0,          0x27,       18,
+    0,          0x28,       24,         0,          0x2B,       25,
+    0,          0x37,       256 % 256,  256 / 256,  42,         0x02,
+    29,         0,          0x03,       30,         0,          0x04,
+    45,         0,          0x05,       46,         0,          0x0a,
+    47,         0,          0x0b,       48,         0,          0x12,
+    33,         0,          0x13,       34,         0,          0x14,
+    35,         0,          0x15,       36,         0,          0x16,
+    37,         0,          0x17,       38,         0,          0x1a,
+    31,         0,          0x1b,       32,         0,          0x24,
+    53,         0,          0x25,       54,         0,          0x28,
+    39,         0,          0x29,       40,         0,          0x2a,
+    41,         0,          0x2b,       42,         0,          0x2c,
+    43,         0,          0x2d,       44,         0,          0x32,
+    61,         0,          0x33,       62,         0,          0x34,
+    63,         0,          0x35,       0,          0,          0x36,
+    320 % 256,  320 / 256,  0x37,       384 % 256,  384 / 256,  0x4a,
+    59,         0,          0x4b,       60,         0,          0x52,
+    49,         0,          0x53,       50,         0,          0x54,
+    51,         0,          0x55,       52,         0,          0x58,
+    55,         0,          0x59,       56,         0,          0x5a,
+    57,         0,          0x5b,       58,         0,          0x64,
+    448 % 256,  448 / 256,  0x65,       512 % 256,  512 / 256,  0x67,
+    640 % 256,  640 / 256,  0x68,       576 % 256,  576 / 256,  16,
+    0x98,       1472 % 256, 1472 / 256, 0x99,       1536 % 256, 1536 / 256,
+    0x9a,       1600 % 256, 1600 / 256, 0x9b,       1728 % 256, 1728 / 256,
+    0xcc,       704 % 256,  704 / 256,  0xcd,       768 % 256,  768 / 256,
+    0xd2,       832 % 256,  832 / 256,  0xd3,       896 % 256,  896 / 256,
+    0xd4,       960 % 256,  960 / 256,  0xd5,       1024 % 256, 1024 / 256,
+    0xd6,       1088 % 256, 1088 / 256, 0xd7,       1152 % 256, 1152 / 256,
+    0xd8,       1216 % 256, 1216 / 256, 0xd9,       1280 % 256, 1280 / 256,
+    0xda,       1344 % 256, 1344 / 256, 0xdb,       1408 % 256, 1408 / 256,
+    0,          3,          0x08,       1792 % 256, 1792 / 256, 0x0c,
+    1856 % 256, 1856 / 256, 0x0d,       1920 % 256, 1920 / 256, 10,
+    0x12,       1984 % 256, 1984 / 256, 0x13,       2048 % 256, 2048 / 256,
+    0x14,       2112 % 256, 2112 / 256, 0x15,       2176 % 256, 2176 / 256,
+    0x16,       2240 % 256, 2240 / 256, 0x17,       2304 % 256, 2304 / 256,
+    0x1c,       2368 % 256, 2368 / 256, 0x1d,       2432 % 256, 2432 / 256,
+    0x1e,       2496 % 256, 2496 / 256, 0x1f,       2560 % 256, 2560 / 256,
+    0xff,
+};
+
+int FaxGetRun(const uint8_t* ins_array,
+              const uint8_t* src_buf,
+              int* bitpos,
+              int bitsize) {
+  uint32_t code = 0;
+  int ins_off = 0;
+  while (1) {
+    uint8_t ins = ins_array[ins_off++];
+    if (ins == 0xff)
+      return -1;
+
+    if (*bitpos >= bitsize)
+      return -1;
+
+    code <<= 1;
+    if (src_buf[*bitpos / 8] & (1 << (7 - *bitpos % 8)))
+      ++code;
+
+    ++(*bitpos);
+    int next_off = ins_off + ins * 3;
+    for (; ins_off < next_off; ins_off += 3) {
+      if (ins_array[ins_off] == code)
+        return ins_array[ins_off + 1] + ins_array[ins_off + 2] * 256;
+    }
+  }
+}
+
+void FaxG4GetRow(const uint8_t* src_buf,
+                 int bitsize,
+                 int* bitpos,
+                 uint8_t* dest_buf,
+                 pdfium::span<const uint8_t> ref_buf,
+                 int columns) {
+  int a0 = -1;
+  bool a0color = true;
+  while (1) {
+    if (*bitpos >= bitsize)
+      return;
+
+    int a1;
+    int a2;
+    int b1;
+    int b2;
+    FaxG4FindB1B2(ref_buf, columns, a0, a0color, &b1, &b2);
+
+    int v_delta = 0;
+    if (!NextBit(src_buf, bitpos)) {
+      if (*bitpos >= bitsize)
+        return;
+
+      bool bit1 = NextBit(src_buf, bitpos);
+      if (*bitpos >= bitsize)
+        return;
+
+      bool bit2 = NextBit(src_buf, bitpos);
+      if (bit1) {
+        v_delta = bit2 ? 1 : -1;
+      } else if (bit2) {
+        int run_len1 = 0;
+        while (1) {
+          int run = FaxGetRun(a0color ? FaxWhiteRunIns : FaxBlackRunIns,
+                              src_buf, bitpos, bitsize);
+          run_len1 += run;
+          if (run < 64)
+            break;
+        }
+        if (a0 < 0)
+          ++run_len1;
+        if (run_len1 < 0)
+          return;
+
+        a1 = a0 + run_len1;
+        if (!a0color)
+          FaxFillBits(dest_buf, columns, a0, a1);
+
+        int run_len2 = 0;
+        while (1) {
+          int run = FaxGetRun(a0color ? FaxBlackRunIns : FaxWhiteRunIns,
+                              src_buf, bitpos, bitsize);
+          run_len2 += run;
+          if (run < 64)
+            break;
+        }
+        if (run_len2 < 0)
+          return;
+        a2 = a1 + run_len2;
+        if (a0color)
+          FaxFillBits(dest_buf, columns, a1, a2);
+
+        a0 = a2;
+        if (a0 < columns)
+          continue;
+
+        return;
+      } else {
+        if (*bitpos >= bitsize)
+          return;
+
+        if (NextBit(src_buf, bitpos)) {
+          if (!a0color)
+            FaxFillBits(dest_buf, columns, a0, b2);
+
+          if (b2 >= columns)
+            return;
+
+          a0 = b2;
+          continue;
+        }
+
+        if (*bitpos >= bitsize)
+          return;
+
+        bool next_bit1 = NextBit(src_buf, bitpos);
+        if (*bitpos >= bitsize)
+          return;
+
+        bool next_bit2 = NextBit(src_buf, bitpos);
+        if (next_bit1) {
+          v_delta = next_bit2 ? 2 : -2;
+        } else if (next_bit2) {
+          if (*bitpos >= bitsize)
+            return;
+
+          v_delta = NextBit(src_buf, bitpos) ? 3 : -3;
+        } else {
+          if (*bitpos >= bitsize)
+            return;
+
+          if (NextBit(src_buf, bitpos)) {
+            *bitpos += 3;
+            continue;
+          }
+          *bitpos += 5;
+          return;
+        }
+      }
+    }
+    a1 = b1 + v_delta;
+    if (!a0color)
+      FaxFillBits(dest_buf, columns, a0, a1);
+
+    if (a1 >= columns)
+      return;
+
+    // The position of picture element must be monotonic increasing.
+    if (a0 >= a1)
+      return;
+
+    a0 = a1;
+    a0color = !a0color;
+  }
+}
+
+void FaxSkipEOL(const uint8_t* src_buf, int bitsize, int* bitpos) {
+  int startbit = *bitpos;
+  while (*bitpos < bitsize) {
+    if (!NextBit(src_buf, bitpos))
+      continue;
+    if (*bitpos - startbit <= 11)
+      *bitpos = startbit;
+    return;
+  }
+}
+
+void FaxGet1DLine(const uint8_t* src_buf,
+                  int bitsize,
+                  int* bitpos,
+                  uint8_t* dest_buf,
+                  int columns) {
+  bool color = true;
+  int startpos = 0;
+  while (1) {
+    if (*bitpos >= bitsize)
+      return;
+
+    int run_len = 0;
+    while (1) {
+      int run = FaxGetRun(color ? FaxWhiteRunIns : FaxBlackRunIns, src_buf,
+                          bitpos, bitsize);
+      if (run < 0) {
+        while (*bitpos < bitsize) {
+          if (NextBit(src_buf, bitpos))
+            return;
+        }
+        return;
+      }
+      run_len += run;
+      if (run < 64)
+        break;
+    }
+    if (!color)
+      FaxFillBits(dest_buf, columns, startpos, startpos + run_len);
+
+    startpos += run_len;
+    if (startpos >= columns)
+      break;
+
+    color = !color;
+  }
+}
+
+class FaxDecoder final : public ScanlineDecoder {
+ public:
+  FaxDecoder(pdfium::span<const uint8_t> src_span,
+             int width,
+             int height,
+             int K,
+             bool EndOfLine,
+             bool EncodedByteAlign,
+             bool BlackIs1);
+  ~FaxDecoder() override;
+
+  // ScanlineDecoder:
+  bool v_Rewind() override;
+  uint8_t* v_GetNextLine() override;
+  uint32_t GetSrcOffset() override;
+
+ private:
+  void InvertBuffer();
+
+  const int m_Encoding;
+  int m_bitpos = 0;
+  bool m_bByteAlign = false;
+  const bool m_bEndOfLine;
+  const bool m_bBlack;
+  const pdfium::span<const uint8_t> m_SrcSpan;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_ScanlineBuf;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_RefBuf;
+};
+
+FaxDecoder::FaxDecoder(pdfium::span<const uint8_t> src_span,
+                       int width,
+                       int height,
+                       int K,
+                       bool EndOfLine,
+                       bool EncodedByteAlign,
+                       bool BlackIs1)
+    : ScanlineDecoder(width,
+                      height,
+                      width,
+                      height,
+                      kFaxComps,
+                      kFaxBpc,
+                      CalculatePitch32(kFaxBpc, width).ValueOrDie()),
+      m_Encoding(K),
+      m_bByteAlign(EncodedByteAlign),
+      m_bEndOfLine(EndOfLine),
+      m_bBlack(BlackIs1),
+      m_SrcSpan(src_span),
+      m_ScanlineBuf(m_Pitch),
+      m_RefBuf(m_Pitch) {}
+
+FaxDecoder::~FaxDecoder() = default;
+
+bool FaxDecoder::v_Rewind() {
+  memset(m_RefBuf.data(), 0xff, m_RefBuf.size());
+  m_bitpos = 0;
+  return true;
+}
+
+uint8_t* FaxDecoder::v_GetNextLine() {
+  int bitsize = m_SrcSpan.size() * 8;
+  FaxSkipEOL(m_SrcSpan.data(), bitsize, &m_bitpos);
+  if (m_bitpos >= bitsize)
+    return nullptr;
+
+  memset(m_ScanlineBuf.data(), 0xff, m_ScanlineBuf.size());
+  if (m_Encoding < 0) {
+    FaxG4GetRow(m_SrcSpan.data(), bitsize, &m_bitpos, m_ScanlineBuf.data(),
+                m_RefBuf, m_OrigWidth);
+    m_RefBuf = m_ScanlineBuf;
+  } else if (m_Encoding == 0) {
+    FaxGet1DLine(m_SrcSpan.data(), bitsize, &m_bitpos, m_ScanlineBuf.data(),
+                 m_OrigWidth);
+  } else {
+    if (NextBit(m_SrcSpan.data(), &m_bitpos)) {
+      FaxGet1DLine(m_SrcSpan.data(), bitsize, &m_bitpos, m_ScanlineBuf.data(),
+                   m_OrigWidth);
+    } else {
+      FaxG4GetRow(m_SrcSpan.data(), bitsize, &m_bitpos, m_ScanlineBuf.data(),
+                  m_RefBuf, m_OrigWidth);
+    }
+    m_RefBuf = m_ScanlineBuf;
+  }
+  if (m_bEndOfLine)
+    FaxSkipEOL(m_SrcSpan.data(), bitsize, &m_bitpos);
+
+  if (m_bByteAlign && m_bitpos < bitsize) {
+    int bitpos0 = m_bitpos;
+    int bitpos1 = FxAlignToBoundary<8>(m_bitpos);
+    while (m_bByteAlign && bitpos0 < bitpos1) {
+      int bit = m_SrcSpan[bitpos0 / 8] & (1 << (7 - bitpos0 % 8));
+      if (bit != 0)
+        m_bByteAlign = false;
+      else
+        ++bitpos0;
+    }
+    if (m_bByteAlign)
+      m_bitpos = bitpos1;
+  }
+  if (m_bBlack)
+    InvertBuffer();
+  return m_ScanlineBuf.data();
+}
+
+uint32_t FaxDecoder::GetSrcOffset() {
+  return std::min(static_cast<size_t>((m_bitpos + 7) / 8), m_SrcSpan.size());
+}
+
+void FaxDecoder::InvertBuffer() {
+  ASSERT(m_Pitch == m_ScanlineBuf.size());
+  ASSERT(m_Pitch % 4 == 0);
+  uint32_t* data = reinterpret_cast<uint32_t*>(m_ScanlineBuf.data());
+  for (size_t i = 0; i < m_ScanlineBuf.size() / 4; ++i)
+    data[i] = ~data[i];
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<ScanlineDecoder> FaxModule::CreateDecoder(
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int height,
+    int K,
+    bool EndOfLine,
+    bool EncodedByteAlign,
+    bool BlackIs1,
+    int Columns,
+    int Rows) {
+  int actual_width = Columns ? Columns : width;
+  int actual_height = Rows ? Rows : height;
+
+  // Reject invalid values.
+  if (actual_width <= 0 || actual_height <= 0)
+    return nullptr;
+
+  // Reject unreasonable large input.
+  if (actual_width > kFaxMaxImageDimension ||
+      actual_height > kFaxMaxImageDimension) {
+    return nullptr;
+  }
+
+  return pdfium::MakeUnique<FaxDecoder>(src_span, actual_width, actual_height,
+                                        K, EndOfLine, EncodedByteAlign,
+                                        BlackIs1);
+}
+
+// static
+int FaxModule::FaxG4Decode(const uint8_t* src_buf,
+                           uint32_t src_size,
+                           int starting_bitpos,
+                           int width,
+                           int height,
+                           int pitch,
+                           uint8_t* dest_buf) {
+  ASSERT(pitch != 0);
+
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> ref_buf(pitch, 0xff);
+  int bitpos = starting_bitpos;
+  for (int iRow = 0; iRow < height; ++iRow) {
+    uint8_t* line_buf = dest_buf + iRow * pitch;
+    memset(line_buf, 0xff, pitch);
+    FaxG4GetRow(src_buf, src_size << 3, &bitpos, line_buf, ref_buf, width);
+    memcpy(ref_buf.data(), line_buf, pitch);
+  }
+  return bitpos;
+}
+
+#if defined(OS_WIN)
+namespace {
+const uint8_t BlackRunTerminator[128] = {
+    0x37, 10, 0x02, 3,  0x03, 2,  0x02, 2,  0x03, 3,  0x03, 4,  0x02, 4,
+    0x03, 5,  0x05, 6,  0x04, 6,  0x04, 7,  0x05, 7,  0x07, 7,  0x04, 8,
+    0x07, 8,  0x18, 9,  0x17, 10, 0x18, 10, 0x08, 10, 0x67, 11, 0x68, 11,
+    0x6c, 11, 0x37, 11, 0x28, 11, 0x17, 11, 0x18, 11, 0xca, 12, 0xcb, 12,
+    0xcc, 12, 0xcd, 12, 0x68, 12, 0x69, 12, 0x6a, 12, 0x6b, 12, 0xd2, 12,
+    0xd3, 12, 0xd4, 12, 0xd5, 12, 0xd6, 12, 0xd7, 12, 0x6c, 12, 0x6d, 12,
+    0xda, 12, 0xdb, 12, 0x54, 12, 0x55, 12, 0x56, 12, 0x57, 12, 0x64, 12,
+    0x65, 12, 0x52, 12, 0x53, 12, 0x24, 12, 0x37, 12, 0x38, 12, 0x27, 12,
+    0x28, 12, 0x58, 12, 0x59, 12, 0x2b, 12, 0x2c, 12, 0x5a, 12, 0x66, 12,
+    0x67, 12,
+};
+
+const uint8_t BlackRunMarkup[80] = {
+    0x0f, 10, 0xc8, 12, 0xc9, 12, 0x5b, 12, 0x33, 12, 0x34, 12, 0x35, 12,
+    0x6c, 13, 0x6d, 13, 0x4a, 13, 0x4b, 13, 0x4c, 13, 0x4d, 13, 0x72, 13,
+    0x73, 13, 0x74, 13, 0x75, 13, 0x76, 13, 0x77, 13, 0x52, 13, 0x53, 13,
+    0x54, 13, 0x55, 13, 0x5a, 13, 0x5b, 13, 0x64, 13, 0x65, 13, 0x08, 11,
+    0x0c, 11, 0x0d, 11, 0x12, 12, 0x13, 12, 0x14, 12, 0x15, 12, 0x16, 12,
+    0x17, 12, 0x1c, 12, 0x1d, 12, 0x1e, 12, 0x1f, 12,
+};
+
+const uint8_t WhiteRunTerminator[128] = {
+    0x35, 8, 0x07, 6, 0x07, 4, 0x08, 4, 0x0B, 4, 0x0C, 4, 0x0E, 4, 0x0F, 4,
+    0x13, 5, 0x14, 5, 0x07, 5, 0x08, 5, 0x08, 6, 0x03, 6, 0x34, 6, 0x35, 6,
+    0x2a, 6, 0x2B, 6, 0x27, 7, 0x0c, 7, 0x08, 7, 0x17, 7, 0x03, 7, 0x04, 7,
+    0x28, 7, 0x2B, 7, 0x13, 7, 0x24, 7, 0x18, 7, 0x02, 8, 0x03, 8, 0x1a, 8,
+    0x1b, 8, 0x12, 8, 0x13, 8, 0x14, 8, 0x15, 8, 0x16, 8, 0x17, 8, 0x28, 8,
+    0x29, 8, 0x2a, 8, 0x2b, 8, 0x2c, 8, 0x2d, 8, 0x04, 8, 0x05, 8, 0x0a, 8,
+    0x0b, 8, 0x52, 8, 0x53, 8, 0x54, 8, 0x55, 8, 0x24, 8, 0x25, 8, 0x58, 8,
+    0x59, 8, 0x5a, 8, 0x5b, 8, 0x4a, 8, 0x4b, 8, 0x32, 8, 0x33, 8, 0x34, 8,
+};
+
+const uint8_t WhiteRunMarkup[80] = {
+    0x1b, 5,  0x12, 5,  0x17, 6,  0x37, 7,  0x36, 8,  0x37, 8,  0x64, 8,
+    0x65, 8,  0x68, 8,  0x67, 8,  0xcc, 9,  0xcd, 9,  0xd2, 9,  0xd3, 9,
+    0xd4, 9,  0xd5, 9,  0xd6, 9,  0xd7, 9,  0xd8, 9,  0xd9, 9,  0xda, 9,
+    0xdb, 9,  0x98, 9,  0x99, 9,  0x9a, 9,  0x18, 6,  0x9b, 9,  0x08, 11,
+    0x0c, 11, 0x0d, 11, 0x12, 12, 0x13, 12, 0x14, 12, 0x15, 12, 0x16, 12,
+    0x17, 12, 0x1c, 12, 0x1d, 12, 0x1e, 12, 0x1f, 12,
+};
+
+class FaxEncoder {
+ public:
+  FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch);
+  ~FaxEncoder();
+  void Encode(std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+              uint32_t* dest_size);
+
+ private:
+  void FaxEncode2DLine(const uint8_t* src_buf);
+  void FaxEncodeRun(int run, bool bWhite);
+  void AddBitStream(int data, int bitlen);
+
+  int m_DestBitpos = 0;
+  const int m_Cols;
+  const int m_Rows;
+  const int m_Pitch;
+  const uint8_t* m_pSrcBuf;
+  CFX_BinaryBuf m_DestBuf;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_RefLine;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_LineBuf;
+};
+
+FaxEncoder::FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch)
+    : m_Cols(width),
+      m_Rows(height),
+      m_Pitch(pitch),
+      m_pSrcBuf(src_buf),
+      m_RefLine(pitch, 0xff),
+      m_LineBuf(
+          pdfium::Vector2D<uint8_t, FxAllocAllocator<uint8_t>>(8, pitch)) {
+  m_DestBuf.SetAllocStep(10240);
+}
+
+FaxEncoder::~FaxEncoder() = default;
+
+void FaxEncoder::AddBitStream(int data, int bitlen) {
+  for (int i = bitlen - 1; i >= 0; --i, ++m_DestBitpos) {
+    if (data & (1 << i))
+      m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
+  }
+}
+
+void FaxEncoder::FaxEncodeRun(int run, bool bWhite) {
+  while (run >= 2560) {
+    AddBitStream(0x1f, 12);
+    run -= 2560;
+  }
+  if (run >= 64) {
+    int markup = run - run % 64;
+    const uint8_t* p = bWhite ? WhiteRunMarkup : BlackRunMarkup;
+    p += (markup / 64 - 1) * 2;
+    AddBitStream(*p, p[1]);
+  }
+  run %= 64;
+  const uint8_t* p = bWhite ? WhiteRunTerminator : BlackRunTerminator;
+  p += run * 2;
+  AddBitStream(*p, p[1]);
+}
+
+void FaxEncoder::FaxEncode2DLine(const uint8_t* src_buf) {
+  int a0 = -1;
+  bool a0color = true;
+  while (1) {
+    int a1 = FindBit(src_buf, m_Cols, a0 + 1, !a0color);
+    int b1;
+    int b2;
+    FaxG4FindB1B2(m_RefLine, m_Cols, a0, a0color, &b1, &b2);
+    if (b2 < a1) {
+      m_DestBitpos += 3;
+      m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
+      ++m_DestBitpos;
+      a0 = b2;
+    } else if (a1 - b1 <= 3 && b1 - a1 <= 3) {
+      int delta = a1 - b1;
+      switch (delta) {
+        case 0:
+          m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
+          break;
+        case 1:
+        case 2:
+        case 3:
+          m_DestBitpos += delta == 1 ? 1 : delta + 2;
+          m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
+          ++m_DestBitpos;
+          m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
+          break;
+        case -1:
+        case -2:
+        case -3:
+          m_DestBitpos += delta == -1 ? 1 : -delta + 2;
+          m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
+          ++m_DestBitpos;
+          break;
+      }
+      ++m_DestBitpos;
+      a0 = a1;
+      a0color = !a0color;
+    } else {
+      int a2 = FindBit(src_buf, m_Cols, a1 + 1, a0color);
+      ++m_DestBitpos;
+      ++m_DestBitpos;
+      m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8);
+      ++m_DestBitpos;
+      if (a0 < 0)
+        a0 = 0;
+      FaxEncodeRun(a1 - a0, a0color);
+      FaxEncodeRun(a2 - a1, !a0color);
+      a0 = a2;
+    }
+    if (a0 >= m_Cols)
+      return;
+  }
+}
+
+void FaxEncoder::Encode(std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                        uint32_t* dest_size) {
+  m_DestBitpos = 0;
+  uint8_t last_byte = 0;
+  for (int i = 0; i < m_Rows; ++i) {
+    const uint8_t* scan_line = m_pSrcBuf + i * m_Pitch;
+    std::fill(std::begin(m_LineBuf), std::end(m_LineBuf), 0);
+    m_LineBuf[0] = last_byte;
+    FaxEncode2DLine(scan_line);
+    m_DestBuf.AppendBlock(m_LineBuf.data(), m_DestBitpos / 8);
+    last_byte = m_LineBuf[m_DestBitpos / 8];
+    m_DestBitpos %= 8;
+    memcpy(m_RefLine.data(), scan_line, m_Pitch);
+  }
+  if (m_DestBitpos)
+    m_DestBuf.AppendByte(last_byte);
+  *dest_size = m_DestBuf.GetSize();
+  *dest_buf = m_DestBuf.DetachBuffer();
+}
+
+}  // namespace
+
+// static
+void FaxModule::FaxEncode(const uint8_t* src_buf,
+                          int width,
+                          int height,
+                          int pitch,
+                          std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                          uint32_t* dest_size) {
+  FaxEncoder encoder(src_buf, width, height, pitch);
+  encoder.Encode(dest_buf, dest_size);
+}
+
+#endif  // defined(OS_WIN)
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/fax/faxmodule.h b/core/fxcodec/fax/faxmodule.h
new file mode 100644
index 0000000..0e1d7b2
--- /dev/null
+++ b/core/fxcodec/fax/faxmodule.h
@@ -0,0 +1,61 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_FAX_FAXMODULE_H_
+#define CORE_FXCODEC_FAX_FAXMODULE_H_
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
+
+namespace fxcodec {
+
+class ScanlineDecoder;
+
+class FaxModule {
+ public:
+  static std::unique_ptr<ScanlineDecoder> CreateDecoder(
+      pdfium::span<const uint8_t> src_span,
+      int width,
+      int height,
+      int K,
+      bool EndOfLine,
+      bool EncodedByteAlign,
+      bool BlackIs1,
+      int Columns,
+      int Rows);
+
+  // Return the ending bit position.
+  static int FaxG4Decode(const uint8_t* src_buf,
+                         uint32_t src_size,
+                         int starting_bitpos,
+                         int width,
+                         int height,
+                         int pitch,
+                         uint8_t* dest_buf);
+
+#if defined(OS_WIN)
+  static void FaxEncode(const uint8_t* src_buf,
+                        int width,
+                        int height,
+                        int pitch,
+                        std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                        uint32_t* dest_size);
+#endif  // defined(OS_WIN)
+
+  FaxModule() = delete;
+  FaxModule(const FaxModule&) = delete;
+  FaxModule& operator=(const FaxModule&) = delete;
+};
+
+}  // namespace fxcodec
+
+using FaxModule = fxcodec::FaxModule;
+
+#endif  // CORE_FXCODEC_FAX_FAXMODULE_H_
diff --git a/core/fxcodec/flate/DEPS b/core/fxcodec/flate/DEPS
new file mode 100644
index 0000000..784b7fb
--- /dev/null
+++ b/core/fxcodec/flate/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/zlib',
+]
diff --git a/core/fxcodec/flate/flatemodule.cpp b/core/fxcodec/flate/flatemodule.cpp
new file mode 100644
index 0000000..9663df3
--- /dev/null
+++ b/core/fxcodec/flate/flatemodule.cpp
@@ -0,0 +1,866 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/flate/flatemodule.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
+
+#if defined(USE_SYSTEM_ZLIB)
+#include <zlib.h>
+#else
+#include "third_party/zlib/zlib.h"
+#endif
+
+extern "C" {
+
+static void* my_alloc_func(void* opaque,
+                           unsigned int items,
+                           unsigned int size) {
+  return FX_Alloc2D(uint8_t, items, size);
+}
+
+static void my_free_func(void* opaque, void* address) {
+  FX_Free(address);
+}
+
+}  // extern "C"
+
+namespace fxcodec {
+
+namespace {
+
+static constexpr uint32_t kMaxTotalOutSize = 1024 * 1024 * 1024;  // 1 GiB
+
+uint32_t FlateGetPossiblyTruncatedTotalOut(z_stream* context) {
+  return std::min(pdfium::base::saturated_cast<uint32_t>(context->total_out),
+                  kMaxTotalOutSize);
+}
+
+uint32_t FlateGetPossiblyTruncatedTotalIn(z_stream* context) {
+  return pdfium::base::saturated_cast<uint32_t>(context->total_in);
+}
+
+bool FlateCompress(unsigned char* dest_buf,
+                   unsigned long* dest_size,
+                   const unsigned char* src_buf,
+                   uint32_t src_size) {
+  return compress(dest_buf, dest_size, src_buf, src_size) == Z_OK;
+}
+
+z_stream* FlateInit() {
+  z_stream* p = FX_Alloc(z_stream, 1);
+  p->zalloc = my_alloc_func;
+  p->zfree = my_free_func;
+  inflateInit(p);
+  return p;
+}
+
+void FlateInput(z_stream* context, pdfium::span<const uint8_t> src_buf) {
+  context->next_in = const_cast<unsigned char*>(src_buf.data());
+  context->avail_in = static_cast<uint32_t>(src_buf.size());
+}
+
+uint32_t FlateOutput(z_stream* context,
+                     unsigned char* dest_buf,
+                     uint32_t dest_size) {
+  context->next_out = dest_buf;
+  context->avail_out = dest_size;
+  uint32_t pre_pos = FlateGetPossiblyTruncatedTotalOut(context);
+  int ret = inflate(static_cast<z_stream*>(context), Z_SYNC_FLUSH);
+
+  uint32_t post_pos = FlateGetPossiblyTruncatedTotalOut(context);
+  ASSERT(post_pos >= pre_pos);
+
+  uint32_t written = post_pos - pre_pos;
+  if (written < dest_size)
+    memset(dest_buf + written, '\0', dest_size - written);
+
+  return ret;
+}
+
+uint32_t FlateGetAvailOut(z_stream* context) {
+  return context->avail_out;
+}
+
+void FlateEnd(z_stream* context) {
+  inflateEnd(context);
+  FX_Free(context);
+}
+
+// For use with std::unique_ptr<z_stream>.
+struct FlateDeleter {
+  inline void operator()(z_stream* context) { FlateEnd(context); }
+};
+
+class CLZWDecoder {
+ public:
+  CLZWDecoder(pdfium::span<const uint8_t> src_span, bool early_change);
+
+  bool Decode();
+  uint32_t GetSrcSize() const { return (src_bit_pos_ + 7) / 8; }
+  uint32_t GetDestSize() const { return dest_byte_pos_; }
+  std::unique_ptr<uint8_t, FxFreeDeleter> TakeDestBuf() {
+    return std::move(dest_buf_);
+  }
+
+ private:
+  void AddCode(uint32_t prefix_code, uint8_t append_char);
+  void DecodeString(uint32_t code);
+  void ExpandDestBuf(uint32_t additional_size);
+
+  pdfium::span<const uint8_t> const src_span_;
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf_;
+  uint32_t src_bit_pos_ = 0;
+  uint32_t dest_buf_size_ = 0;  // Actual allocated size.
+  uint32_t dest_byte_pos_ = 0;  // Size used.
+  uint32_t stack_len_ = 0;
+  uint8_t decode_stack_[4000];
+  const uint8_t early_change_;
+  uint8_t code_len_ = 9;
+  uint32_t current_code_ = 0;
+  uint32_t codes_[5021];
+};
+
+CLZWDecoder::CLZWDecoder(pdfium::span<const uint8_t> src_span,
+                         bool early_change)
+    : src_span_(src_span), early_change_(early_change ? 1 : 0) {}
+
+void CLZWDecoder::AddCode(uint32_t prefix_code, uint8_t append_char) {
+  if (current_code_ + early_change_ == 4094)
+    return;
+
+  codes_[current_code_++] = (prefix_code << 16) | append_char;
+  if (current_code_ + early_change_ == 512 - 258)
+    code_len_ = 10;
+  else if (current_code_ + early_change_ == 1024 - 258)
+    code_len_ = 11;
+  else if (current_code_ + early_change_ == 2048 - 258)
+    code_len_ = 12;
+}
+
+void CLZWDecoder::DecodeString(uint32_t code) {
+  while (1) {
+    int index = code - 258;
+    if (index < 0 || static_cast<uint32_t>(index) >= current_code_)
+      break;
+
+    uint32_t data = codes_[index];
+    if (stack_len_ >= sizeof(decode_stack_))
+      return;
+
+    decode_stack_[stack_len_++] = static_cast<uint8_t>(data);
+    code = data >> 16;
+  }
+  if (stack_len_ >= sizeof(decode_stack_))
+    return;
+
+  decode_stack_[stack_len_++] = static_cast<uint8_t>(code);
+}
+
+void CLZWDecoder::ExpandDestBuf(uint32_t additional_size) {
+  FX_SAFE_UINT32 new_size = std::max(dest_buf_size_ / 2, additional_size);
+  new_size += dest_buf_size_;
+  if (!new_size.IsValid()) {
+    dest_buf_.reset();
+    return;
+  }
+
+  dest_buf_size_ = new_size.ValueOrDie();
+  dest_buf_.reset(FX_Realloc(uint8_t, dest_buf_.release(), dest_buf_size_));
+}
+
+bool CLZWDecoder::Decode() {
+  uint32_t old_code = 0xFFFFFFFF;
+  uint8_t last_char = 0;
+
+  // In one PDF test set, 40% of Decode() calls did not need to realloc with
+  // this size.
+  dest_buf_size_ = 512;
+  dest_buf_.reset(FX_Alloc(uint8_t, dest_buf_size_));
+  while (1) {
+    if (src_bit_pos_ + code_len_ > src_span_.size() * 8)
+      break;
+
+    int byte_pos = src_bit_pos_ / 8;
+    int bit_pos = src_bit_pos_ % 8;
+    uint8_t bit_left = code_len_;
+    uint32_t code = 0;
+    if (bit_pos) {
+      bit_left -= 8 - bit_pos;
+      code = (src_span_[byte_pos++] & ((1 << (8 - bit_pos)) - 1)) << bit_left;
+    }
+    if (bit_left < 8) {
+      code |= src_span_[byte_pos] >> (8 - bit_left);
+    } else {
+      bit_left -= 8;
+      code |= src_span_[byte_pos++] << bit_left;
+      if (bit_left)
+        code |= src_span_[byte_pos] >> (8 - bit_left);
+    }
+    src_bit_pos_ += code_len_;
+
+    if (code < 256) {
+      if (dest_byte_pos_ >= dest_buf_size_) {
+        ExpandDestBuf(dest_byte_pos_ - dest_buf_size_ + 1);
+        if (!dest_buf_)
+          return false;
+      }
+
+      dest_buf_.get()[dest_byte_pos_] = (uint8_t)code;
+      dest_byte_pos_++;
+      last_char = (uint8_t)code;
+      if (old_code != 0xFFFFFFFF)
+        AddCode(old_code, last_char);
+      old_code = code;
+      continue;
+    }
+    if (code == 256) {
+      code_len_ = 9;
+      current_code_ = 0;
+      old_code = 0xFFFFFFFF;
+      continue;
+    }
+    if (code == 257)
+      break;
+
+    // Case where |code| is 258 or greater.
+    if (old_code == 0xFFFFFFFF)
+      return false;
+
+    ASSERT(old_code < 256 || old_code >= 258);
+    stack_len_ = 0;
+    if (code - 258 >= current_code_) {
+      if (stack_len_ < sizeof(decode_stack_))
+        decode_stack_[stack_len_++] = last_char;
+      DecodeString(old_code);
+    } else {
+      DecodeString(code);
+    }
+
+    FX_SAFE_UINT32 safe_required_size = dest_byte_pos_;
+    safe_required_size += stack_len_;
+    if (!safe_required_size.IsValid())
+      return false;
+
+    uint32_t required_size = safe_required_size.ValueOrDie();
+    if (required_size > dest_buf_size_) {
+      ExpandDestBuf(required_size - dest_buf_size_);
+      if (!dest_buf_)
+        return false;
+    }
+
+    for (uint32_t i = 0; i < stack_len_; i++)
+      dest_buf_.get()[dest_byte_pos_ + i] = decode_stack_[stack_len_ - i - 1];
+    dest_byte_pos_ += stack_len_;
+    last_char = decode_stack_[stack_len_ - 1];
+    if (old_code >= 258 && old_code - 258 >= current_code_)
+      break;
+
+    AddCode(old_code, last_char);
+    old_code = code;
+  }
+  return dest_byte_pos_ != 0;
+}
+
+uint8_t PathPredictor(int a, int b, int c) {
+  int p = a + b - c;
+  int pa = abs(p - a);
+  int pb = abs(p - b);
+  int pc = abs(p - c);
+  if (pa <= pb && pa <= pc)
+    return (uint8_t)a;
+  if (pb <= pc)
+    return (uint8_t)b;
+  return (uint8_t)c;
+}
+
+void PNG_PredictLine(uint8_t* pDestData,
+                     const uint8_t* pSrcData,
+                     const uint8_t* pLastLine,
+                     int bpc,
+                     int nColors,
+                     int nPixels) {
+  const uint32_t row_size = CalculatePitch8(bpc, nColors, nPixels).ValueOrDie();
+  const uint32_t BytesPerPixel = (bpc * nColors + 7) / 8;
+  uint8_t tag = pSrcData[0];
+  if (tag == 0) {
+    memmove(pDestData, pSrcData + 1, row_size);
+    return;
+  }
+  for (uint32_t byte = 0; byte < row_size; ++byte) {
+    uint8_t raw_byte = pSrcData[byte + 1];
+    switch (tag) {
+      case 1: {
+        uint8_t left = 0;
+        if (byte >= BytesPerPixel) {
+          left = pDestData[byte - BytesPerPixel];
+        }
+        pDestData[byte] = raw_byte + left;
+        break;
+      }
+      case 2: {
+        uint8_t up = 0;
+        if (pLastLine) {
+          up = pLastLine[byte];
+        }
+        pDestData[byte] = raw_byte + up;
+        break;
+      }
+      case 3: {
+        uint8_t left = 0;
+        if (byte >= BytesPerPixel) {
+          left = pDestData[byte - BytesPerPixel];
+        }
+        uint8_t up = 0;
+        if (pLastLine) {
+          up = pLastLine[byte];
+        }
+        pDestData[byte] = raw_byte + (up + left) / 2;
+        break;
+      }
+      case 4: {
+        uint8_t left = 0;
+        if (byte >= BytesPerPixel) {
+          left = pDestData[byte - BytesPerPixel];
+        }
+        uint8_t up = 0;
+        if (pLastLine) {
+          up = pLastLine[byte];
+        }
+        uint8_t upper_left = 0;
+        if (byte >= BytesPerPixel && pLastLine) {
+          upper_left = pLastLine[byte - BytesPerPixel];
+        }
+        pDestData[byte] = raw_byte + PathPredictor(left, up, upper_left);
+        break;
+      }
+      default:
+        pDestData[byte] = raw_byte;
+        break;
+    }
+  }
+}
+
+bool PNG_Predictor(int Colors,
+                   int BitsPerComponent,
+                   int Columns,
+                   std::unique_ptr<uint8_t, FxFreeDeleter>* data_buf,
+                   uint32_t* data_size) {
+  // TODO(thestig): Look into using CalculatePitch8() here.
+  const int BytesPerPixel = (Colors * BitsPerComponent + 7) / 8;
+  const int row_size = (Colors * BitsPerComponent * Columns + 7) / 8;
+  if (row_size <= 0)
+    return false;
+  const int row_count = (*data_size + row_size) / (row_size + 1);
+  if (row_count <= 0)
+    return false;
+  const int last_row_size = *data_size % (row_size + 1);
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf(
+      FX_Alloc2D(uint8_t, row_size, row_count));
+  uint32_t byte_cnt = 0;
+  uint8_t* pSrcData = data_buf->get();
+  uint8_t* pDestData = dest_buf.get();
+  for (int row = 0; row < row_count; row++) {
+    uint8_t tag = pSrcData[0];
+    byte_cnt++;
+    if (tag == 0) {
+      int move_size = row_size;
+      if ((row + 1) * (move_size + 1) > static_cast<int>(*data_size)) {
+        move_size = last_row_size - 1;
+      }
+      memcpy(pDestData, pSrcData + 1, move_size);
+      pSrcData += move_size + 1;
+      pDestData += move_size;
+      byte_cnt += move_size;
+      continue;
+    }
+    for (int byte = 0; byte < row_size && byte_cnt < *data_size;
+         ++byte, ++byte_cnt) {
+      uint8_t raw_byte = pSrcData[byte + 1];
+      switch (tag) {
+        case 1: {
+          uint8_t left = 0;
+          if (byte >= BytesPerPixel) {
+            left = pDestData[byte - BytesPerPixel];
+          }
+          pDestData[byte] = raw_byte + left;
+          break;
+        }
+        case 2: {
+          uint8_t up = 0;
+          if (row) {
+            up = pDestData[byte - row_size];
+          }
+          pDestData[byte] = raw_byte + up;
+          break;
+        }
+        case 3: {
+          uint8_t left = 0;
+          if (byte >= BytesPerPixel) {
+            left = pDestData[byte - BytesPerPixel];
+          }
+          uint8_t up = 0;
+          if (row) {
+            up = pDestData[byte - row_size];
+          }
+          pDestData[byte] = raw_byte + (up + left) / 2;
+          break;
+        }
+        case 4: {
+          uint8_t left = 0;
+          if (byte >= BytesPerPixel) {
+            left = pDestData[byte - BytesPerPixel];
+          }
+          uint8_t up = 0;
+          if (row) {
+            up = pDestData[byte - row_size];
+          }
+          uint8_t upper_left = 0;
+          if (byte >= BytesPerPixel && row) {
+            upper_left = pDestData[byte - row_size - BytesPerPixel];
+          }
+          pDestData[byte] = raw_byte + PathPredictor(left, up, upper_left);
+          break;
+        }
+        default:
+          pDestData[byte] = raw_byte;
+          break;
+      }
+    }
+    pSrcData += row_size + 1;
+    pDestData += row_size;
+  }
+  *data_buf = std::move(dest_buf);
+  *data_size = row_size * row_count -
+               (last_row_size > 0 ? (row_size + 1 - last_row_size) : 0);
+  return true;
+}
+
+void TIFF_PredictLine(uint8_t* dest_buf,
+                      uint32_t row_size,
+                      int BitsPerComponent,
+                      int Colors,
+                      int Columns) {
+  if (BitsPerComponent == 1) {
+    int row_bits = std::min(BitsPerComponent * Colors * Columns,
+                            pdfium::base::checked_cast<int>(row_size * 8));
+    int index_pre = 0;
+    int col_pre = 0;
+    for (int i = 1; i < row_bits; i++) {
+      int col = i % 8;
+      int index = i / 8;
+      if (((dest_buf[index] >> (7 - col)) & 1) ^
+          ((dest_buf[index_pre] >> (7 - col_pre)) & 1)) {
+        dest_buf[index] |= 1 << (7 - col);
+      } else {
+        dest_buf[index] &= ~(1 << (7 - col));
+      }
+      index_pre = index;
+      col_pre = col;
+    }
+    return;
+  }
+  int BytesPerPixel = BitsPerComponent * Colors / 8;
+  if (BitsPerComponent == 16) {
+    for (uint32_t i = BytesPerPixel; i + 1 < row_size; i += 2) {
+      uint16_t pixel =
+          (dest_buf[i - BytesPerPixel] << 8) | dest_buf[i - BytesPerPixel + 1];
+      pixel += (dest_buf[i] << 8) | dest_buf[i + 1];
+      dest_buf[i] = pixel >> 8;
+      dest_buf[i + 1] = (uint8_t)pixel;
+    }
+  } else {
+    for (uint32_t i = BytesPerPixel; i < row_size; i++) {
+      dest_buf[i] += dest_buf[i - BytesPerPixel];
+    }
+  }
+}
+
+bool TIFF_Predictor(int Colors,
+                    int BitsPerComponent,
+                    int Columns,
+                    std::unique_ptr<uint8_t, FxFreeDeleter>* data_buf,
+                    uint32_t* data_size) {
+  int row_size = (Colors * BitsPerComponent * Columns + 7) / 8;
+  if (row_size == 0)
+    return false;
+  const int row_count = (*data_size + row_size - 1) / row_size;
+  const int last_row_size = *data_size % row_size;
+  for (int row = 0; row < row_count; row++) {
+    uint8_t* scan_line = data_buf->get() + row * row_size;
+    if ((row + 1) * row_size > static_cast<int>(*data_size)) {
+      row_size = last_row_size;
+    }
+    TIFF_PredictLine(scan_line, row_size, BitsPerComponent, Colors, Columns);
+  }
+  return true;
+}
+
+void FlateUncompress(pdfium::span<const uint8_t> src_buf,
+                     uint32_t orig_size,
+                     std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                     uint32_t* dest_size,
+                     uint32_t* offset) {
+  dest_buf->reset();
+  *dest_size = 0;
+
+  std::unique_ptr<z_stream, FlateDeleter> context(FlateInit());
+  if (!context)
+    return;
+
+  FlateInput(context.get(), src_buf);
+
+  const uint32_t kMaxInitialAllocSize = 10000000;
+  uint32_t guess_size = orig_size ? orig_size : src_buf.size() * 2;
+  guess_size = std::min(guess_size, kMaxInitialAllocSize);
+
+  uint32_t buf_size = guess_size;
+  uint32_t last_buf_size = buf_size;
+  std::unique_ptr<uint8_t, FxFreeDeleter> guess_buf(
+      FX_Alloc(uint8_t, guess_size + 1));
+  guess_buf.get()[guess_size] = '\0';
+
+  std::vector<std::unique_ptr<uint8_t, FxFreeDeleter>> result_tmp_bufs;
+  {
+    std::unique_ptr<uint8_t, FxFreeDeleter> cur_buf = std::move(guess_buf);
+    while (1) {
+      uint32_t ret = FlateOutput(context.get(), cur_buf.get(), buf_size);
+      uint32_t avail_buf_size = FlateGetAvailOut(context.get());
+      if (ret != Z_OK || avail_buf_size != 0) {
+        last_buf_size = buf_size - avail_buf_size;
+        result_tmp_bufs.push_back(std::move(cur_buf));
+        break;
+      }
+      result_tmp_bufs.push_back(std::move(cur_buf));
+      cur_buf.reset(FX_Alloc(uint8_t, buf_size + 1));
+      cur_buf.get()[buf_size] = '\0';
+    }
+  }
+
+  // The TotalOut size returned from the library may not be big enough to
+  // handle the content the library returns. We can only handle items
+  // up to 4GB in size.
+  *dest_size = FlateGetPossiblyTruncatedTotalOut(context.get());
+  *offset = FlateGetPossiblyTruncatedTotalIn(context.get());
+  if (result_tmp_bufs.size() == 1) {
+    *dest_buf = std::move(result_tmp_bufs[0]);
+    return;
+  }
+
+  std::unique_ptr<uint8_t, FxFreeDeleter> result_buf(
+      FX_Alloc(uint8_t, *dest_size));
+  uint32_t result_pos = 0;
+  uint32_t remaining = *dest_size;
+  for (size_t i = 0; i < result_tmp_bufs.size(); i++) {
+    std::unique_ptr<uint8_t, FxFreeDeleter> tmp_buf =
+        std::move(result_tmp_bufs[i]);
+    uint32_t tmp_buf_size = buf_size;
+    if (i == result_tmp_bufs.size() - 1)
+      tmp_buf_size = last_buf_size;
+
+    uint32_t cp_size = std::min(tmp_buf_size, remaining);
+    memcpy(result_buf.get() + result_pos, tmp_buf.get(), cp_size);
+    result_pos += cp_size;
+    remaining -= cp_size;
+  }
+  *dest_buf = std::move(result_buf);
+}
+
+enum class PredictorType : uint8_t { kNone, kFlate, kPng };
+static PredictorType GetPredictor(int predictor) {
+  if (predictor >= 10)
+    return PredictorType::kPng;
+  if (predictor == 2)
+    return PredictorType::kFlate;
+  return PredictorType::kNone;
+}
+
+class FlateScanlineDecoder : public ScanlineDecoder {
+ public:
+  FlateScanlineDecoder(pdfium::span<const uint8_t> src_span,
+                       int width,
+                       int height,
+                       int nComps,
+                       int bpc);
+  ~FlateScanlineDecoder() override;
+
+  // ScanlineDecoder:
+  bool v_Rewind() override;
+  uint8_t* v_GetNextLine() override;
+  uint32_t GetSrcOffset() override;
+
+ protected:
+  std::unique_ptr<z_stream, FlateDeleter> m_pFlate;
+  const pdfium::span<const uint8_t> m_SrcBuf;
+  std::unique_ptr<uint8_t, FxFreeDeleter> const m_pScanline;
+};
+
+FlateScanlineDecoder::FlateScanlineDecoder(pdfium::span<const uint8_t> src_span,
+                                           int width,
+                                           int height,
+                                           int nComps,
+                                           int bpc)
+    : ScanlineDecoder(width,
+                      height,
+                      width,
+                      height,
+                      nComps,
+                      bpc,
+                      CalculatePitch8(bpc, nComps, width).ValueOrDie()),
+      m_SrcBuf(src_span),
+      m_pScanline(FX_Alloc(uint8_t, m_Pitch)) {}
+
+FlateScanlineDecoder::~FlateScanlineDecoder() = default;
+
+bool FlateScanlineDecoder::v_Rewind() {
+  m_pFlate.reset(FlateInit());
+  if (!m_pFlate)
+    return false;
+
+  FlateInput(m_pFlate.get(), m_SrcBuf);
+  return true;
+}
+
+uint8_t* FlateScanlineDecoder::v_GetNextLine() {
+  FlateOutput(m_pFlate.get(), m_pScanline.get(), m_Pitch);
+  return m_pScanline.get();
+}
+
+uint32_t FlateScanlineDecoder::GetSrcOffset() {
+  return FlateGetPossiblyTruncatedTotalIn(m_pFlate.get());
+}
+
+class FlatePredictorScanlineDecoder final : public FlateScanlineDecoder {
+ public:
+  FlatePredictorScanlineDecoder(pdfium::span<const uint8_t> src_span,
+                                int width,
+                                int height,
+                                int comps,
+                                int bpc,
+                                PredictorType predictor,
+                                int Colors,
+                                int BitsPerComponent,
+                                int Columns);
+  ~FlatePredictorScanlineDecoder() override;
+
+  // ScanlineDecoder:
+  bool v_Rewind() override;
+  uint8_t* v_GetNextLine() override;
+
+ private:
+  void GetNextLineWithPredictedPitch();
+  void GetNextLineWithoutPredictedPitch();
+
+  const PredictorType m_Predictor;
+  int m_Colors = 0;
+  int m_BitsPerComponent = 0;
+  int m_Columns = 0;
+  uint32_t m_PredictPitch = 0;
+  size_t m_LeftOver = 0;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_LastLine;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_PredictBuffer;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_PredictRaw;
+};
+
+FlatePredictorScanlineDecoder::FlatePredictorScanlineDecoder(
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int height,
+    int comps,
+    int bpc,
+    PredictorType predictor,
+    int Colors,
+    int BitsPerComponent,
+    int Columns)
+    : FlateScanlineDecoder(src_span, width, height, comps, bpc),
+      m_Predictor(predictor) {
+  ASSERT(m_Predictor != PredictorType::kNone);
+  if (BitsPerComponent * Colors * Columns == 0) {
+    BitsPerComponent = m_bpc;
+    Colors = m_nComps;
+    Columns = m_OrigWidth;
+  }
+  m_Colors = Colors;
+  m_BitsPerComponent = BitsPerComponent;
+  m_Columns = Columns;
+  m_PredictPitch =
+      CalculatePitch8(m_BitsPerComponent, m_Colors, m_Columns).ValueOrDie();
+  m_LastLine.resize(m_PredictPitch);
+  m_PredictBuffer.resize(m_PredictPitch);
+  m_PredictRaw.resize(m_PredictPitch + 1);
+}
+
+FlatePredictorScanlineDecoder::~FlatePredictorScanlineDecoder() = default;
+
+bool FlatePredictorScanlineDecoder::v_Rewind() {
+  if (!FlateScanlineDecoder::v_Rewind())
+    return false;
+
+  m_LeftOver = 0;
+  return true;
+}
+
+uint8_t* FlatePredictorScanlineDecoder::v_GetNextLine() {
+  if (m_Pitch == m_PredictPitch)
+    GetNextLineWithPredictedPitch();
+  else
+    GetNextLineWithoutPredictedPitch();
+  return m_pScanline.get();
+}
+
+void FlatePredictorScanlineDecoder::GetNextLineWithPredictedPitch() {
+  switch (m_Predictor) {
+    case PredictorType::kPng:
+      FlateOutput(m_pFlate.get(), m_PredictRaw.data(), m_PredictPitch + 1);
+      PNG_PredictLine(m_pScanline.get(), m_PredictRaw.data(), m_LastLine.data(),
+                      m_BitsPerComponent, m_Colors, m_Columns);
+      memcpy(m_LastLine.data(), m_pScanline.get(), m_PredictPitch);
+      break;
+    case PredictorType::kFlate:
+      FlateOutput(m_pFlate.get(), m_pScanline.get(), m_Pitch);
+      TIFF_PredictLine(m_pScanline.get(), m_PredictPitch, m_bpc, m_nComps,
+                       m_OutputWidth);
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+void FlatePredictorScanlineDecoder::GetNextLineWithoutPredictedPitch() {
+  size_t bytes_to_go = m_Pitch;
+  size_t read_leftover = m_LeftOver > bytes_to_go ? bytes_to_go : m_LeftOver;
+  if (read_leftover) {
+    memcpy(m_pScanline.get(), &m_PredictBuffer[m_PredictPitch - m_LeftOver],
+           read_leftover);
+    m_LeftOver -= read_leftover;
+    bytes_to_go -= read_leftover;
+  }
+  while (bytes_to_go) {
+    switch (m_Predictor) {
+      case PredictorType::kPng:
+        FlateOutput(m_pFlate.get(), m_PredictRaw.data(), m_PredictPitch + 1);
+        PNG_PredictLine(m_PredictBuffer.data(), m_PredictRaw.data(),
+                        m_LastLine.data(), m_BitsPerComponent, m_Colors,
+                        m_Columns);
+        memcpy(m_LastLine.data(), m_PredictBuffer.data(), m_PredictPitch);
+        break;
+      case PredictorType::kFlate:
+        FlateOutput(m_pFlate.get(), m_PredictBuffer.data(), m_PredictPitch);
+        TIFF_PredictLine(m_PredictBuffer.data(), m_PredictPitch,
+                         m_BitsPerComponent, m_Colors, m_Columns);
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+    size_t read_bytes =
+        m_PredictPitch > bytes_to_go ? bytes_to_go : m_PredictPitch;
+    memcpy(m_pScanline.get() + m_Pitch - bytes_to_go, m_PredictBuffer.data(),
+           read_bytes);
+    m_LeftOver += m_PredictPitch - read_bytes;
+    bytes_to_go -= read_bytes;
+  }
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<ScanlineDecoder> FlateModule::CreateDecoder(
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int height,
+    int nComps,
+    int bpc,
+    int predictor,
+    int Colors,
+    int BitsPerComponent,
+    int Columns) {
+  PredictorType predictor_type = GetPredictor(predictor);
+  if (predictor_type == PredictorType::kNone) {
+    return pdfium::MakeUnique<FlateScanlineDecoder>(src_span, width, height,
+                                                    nComps, bpc);
+  }
+  return pdfium::MakeUnique<FlatePredictorScanlineDecoder>(
+      src_span, width, height, nComps, bpc, predictor_type, Colors,
+      BitsPerComponent, Columns);
+}
+
+// static
+uint32_t FlateModule::FlateOrLZWDecode(
+    bool bLZW,
+    pdfium::span<const uint8_t> src_span,
+    bool bEarlyChange,
+    int predictor,
+    int Colors,
+    int BitsPerComponent,
+    int Columns,
+    uint32_t estimated_size,
+    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+    uint32_t* dest_size) {
+  dest_buf->reset();
+  uint32_t offset = 0;
+  PredictorType predictor_type = GetPredictor(predictor);
+
+  if (bLZW) {
+    auto decoder = pdfium::MakeUnique<CLZWDecoder>(src_span, bEarlyChange);
+    if (!decoder->Decode())
+      return FX_INVALID_OFFSET;
+
+    offset = decoder->GetSrcSize();
+    *dest_size = decoder->GetDestSize();
+    *dest_buf = decoder->TakeDestBuf();
+  } else {
+    FlateUncompress(src_span, estimated_size, dest_buf, dest_size, &offset);
+  }
+
+  bool ret = false;
+  switch (predictor_type) {
+    case PredictorType::kNone:
+      return offset;
+    case PredictorType::kPng:
+      ret =
+          PNG_Predictor(Colors, BitsPerComponent, Columns, dest_buf, dest_size);
+      break;
+    case PredictorType::kFlate:
+      ret = TIFF_Predictor(Colors, BitsPerComponent, Columns, dest_buf,
+                           dest_size);
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+  return ret ? offset : FX_INVALID_OFFSET;
+}
+
+// static
+bool FlateModule::Encode(const uint8_t* src_buf,
+                         uint32_t src_size,
+                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                         uint32_t* dest_size) {
+  *dest_size = src_size + src_size / 1000 + 12;
+  dest_buf->reset(FX_Alloc(uint8_t, *dest_size));
+  unsigned long temp_size = *dest_size;
+  if (!FlateCompress(dest_buf->get(), &temp_size, src_buf, src_size))
+    return false;
+
+  *dest_size = (uint32_t)temp_size;
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/flate/flatemodule.h b/core/fxcodec/flate/flatemodule.h
new file mode 100644
index 0000000..8ce5aa5
--- /dev/null
+++ b/core/fxcodec/flate/flatemodule.h
@@ -0,0 +1,59 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_FLATE_FLATEMODULE_H_
+#define CORE_FXCODEC_FLATE_FLATEMODULE_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
+
+namespace fxcodec {
+
+class ScanlineDecoder;
+
+class FlateModule {
+ public:
+  static std::unique_ptr<ScanlineDecoder> CreateDecoder(
+      pdfium::span<const uint8_t> src_span,
+      int width,
+      int height,
+      int nComps,
+      int bpc,
+      int predictor,
+      int Colors,
+      int BitsPerComponent,
+      int Columns);
+
+  static uint32_t FlateOrLZWDecode(
+      bool bLZW,
+      pdfium::span<const uint8_t> src_span,
+      bool bEarlyChange,
+      int predictor,
+      int Colors,
+      int BitsPerComponent,
+      int Columns,
+      uint32_t estimated_size,
+      std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+      uint32_t* dest_size);
+
+  static bool Encode(const uint8_t* src_buf,
+                     uint32_t src_size,
+                     std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                     uint32_t* dest_size);
+
+  FlateModule() = delete;
+  FlateModule(const FlateModule&) = delete;
+  FlateModule& operator=(const FlateModule&) = delete;
+};
+
+}  // namespace fxcodec
+
+using FlateModule = fxcodec::FlateModule;
+
+#endif  // CORE_FXCODEC_FLATE_FLATEMODULE_H_
diff --git a/core/fxcodec/fx_codec.cpp b/core/fxcodec/fx_codec.cpp
new file mode 100644
index 0000000..5625534
--- /dev/null
+++ b/core/fxcodec/fx_codec.cpp
@@ -0,0 +1,115 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/fx_codec.h"
+
+#include <algorithm>
+#include <cmath>
+#include <memory>
+#include <utility>
+
+#include "core/fxcodec/jbig2/jbig2module.h"
+#include "core/fxcodec/jpeg/jpegmodule.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcodec {
+
+namespace {
+
+ModuleMgr* g_ModuleMgr = nullptr;
+
+}  // namespace
+
+// static
+void ModuleMgr::Create() {
+  ASSERT(!g_ModuleMgr);
+  g_ModuleMgr = new ModuleMgr();
+}
+
+// static
+void ModuleMgr::Destroy() {
+  ASSERT(g_ModuleMgr);
+  delete g_ModuleMgr;
+  g_ModuleMgr = nullptr;
+}
+
+// static
+ModuleMgr* ModuleMgr::GetInstance() {
+  ASSERT(g_ModuleMgr);
+  return g_ModuleMgr;
+}
+
+ModuleMgr::ModuleMgr()
+    : m_pJpegModule(pdfium::MakeUnique<JpegModule>()),
+      m_pJbig2Module(pdfium::MakeUnique<Jbig2Module>()) {
+#ifdef PDF_ENABLE_XFA_BMP
+  SetBmpModule(pdfium::MakeUnique<BmpModule>());
+#endif
+
+#ifdef PDF_ENABLE_XFA_GIF
+  SetGifModule(pdfium::MakeUnique<GifModule>());
+#endif
+
+#ifdef PDF_ENABLE_XFA_PNG
+  SetPngModule(pdfium::MakeUnique<PngModule>());
+#endif
+
+#ifdef PDF_ENABLE_XFA_TIFF
+  SetTiffModule(pdfium::MakeUnique<TiffModule>());
+#endif
+}
+
+ModuleMgr::~ModuleMgr() = default;
+
+#ifdef PDF_ENABLE_XFA
+CFX_DIBAttribute::CFX_DIBAttribute() = default;
+
+CFX_DIBAttribute::~CFX_DIBAttribute() {
+  for (const auto& pair : m_Exif)
+    FX_Free(pair.second);
+}
+#endif  // PDF_ENABLE_XFA
+
+void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels) {
+  if (pDestBuf == pSrcBuf) {
+    for (int i = 0; i < pixels; i++) {
+      uint8_t temp = pDestBuf[2];
+      pDestBuf[2] = pDestBuf[0];
+      pDestBuf[0] = temp;
+      pDestBuf += 3;
+    }
+  } else {
+    for (int i = 0; i < pixels; i++) {
+      *pDestBuf++ = pSrcBuf[2];
+      *pDestBuf++ = pSrcBuf[1];
+      *pDestBuf++ = pSrcBuf[0];
+      pSrcBuf += 3;
+    }
+  }
+}
+
+FX_SAFE_UINT32 CalculatePitch8(uint32_t bpc, uint32_t components, int width) {
+  FX_SAFE_UINT32 pitch = bpc;
+  pitch *= components;
+  pitch *= width;
+  pitch += 7;
+  pitch /= 8;
+  return pitch;
+}
+
+FX_SAFE_UINT32 CalculatePitch32(int bpp, int width) {
+  FX_SAFE_UINT32 pitch = bpp;
+  pitch *= width;
+  pitch += 31;
+  pitch /= 32;  // quantized to number of 32-bit words.
+  pitch *= 4;   // and then back to bytes, (not just /8 in one step).
+  return pitch;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/fx_codec.h b/core/fxcodec/fx_codec.h
index 923eb7c..c7224ff 100644
--- a/core/fxcodec/fx_codec.h
+++ b/core/fxcodec/fx_codec.h
@@ -9,111 +9,127 @@
 
 #include <map>
 #include <memory>
-#include <tuple>
 #include <utility>
-#include <vector>
 
 #include "core/fxcodec/fx_codec_def.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_string.h"
 
-class CCodec_BasicModule;
-class CCodec_FaxModule;
-class CCodec_FlateModule;
-class CCodec_IccModule;
-class CCodec_Jbig2Module;
-class CCodec_JpegModule;
-class CCodec_JpxModule;
-class CFX_DIBSource;
-class CJPX_Decoder;
-class CPDF_ColorSpace;
-class CPDF_StreamAcc;
+#ifdef PDF_ENABLE_XFA
+#ifdef PDF_ENABLE_XFA_BMP
+#include "core/fxcodec/bmp/bmpmodule.h"
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+#include "core/fxcodec/gif/gifmodule.h"
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+#include "core/fxcodec/png/pngmodule.h"
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+#include "core/fxcodec/tiff/tiffmodule.h"
+#endif  // PDF_ENABLE_XFA_TIFF
+#endif  // PDF_ENABLE_XFA
+
+namespace fxcodec {
 
 #ifdef PDF_ENABLE_XFA
-class CCodec_BmpModule;
-class CCodec_GifModule;
-class CCodec_PngModule;
-class CCodec_ProgressiveDecoder;
-class CCodec_TiffModule;
-
 class CFX_DIBAttribute {
  public:
   CFX_DIBAttribute();
   ~CFX_DIBAttribute();
 
-  int32_t m_nXDPI;
-  int32_t m_nYDPI;
-  float m_fAspectRatio;
-  uint16_t m_wDPIUnit;
-  ByteString m_strAuthor;
-  int32_t m_nGifLeft;
-  int32_t m_nGifTop;
-  uint32_t* m_pGifLocalPalette;
-  uint32_t m_nGifLocalPalNum;
-  int32_t m_nBmpCompressType;
+  int32_t m_nXDPI = -1;
+  int32_t m_nYDPI = -1;
+  uint16_t m_wDPIUnit = 0;
   std::map<uint32_t, void*> m_Exif;
 };
 #endif  // PDF_ENABLE_XFA
 
-class CCodec_ModuleMgr {
+class Jbig2Module;
+class JpegModule;
+class ProgressiveDecoder;
+
+class ModuleMgr {
  public:
-  CCodec_ModuleMgr();
-  ~CCodec_ModuleMgr();
+  // Per-process singleton managed by callers.
+  static void Create();
+  static void Destroy();
+  static ModuleMgr* GetInstance();
 
-  CCodec_BasicModule* GetBasicModule() const { return m_pBasicModule.get(); }
-  CCodec_FaxModule* GetFaxModule() const { return m_pFaxModule.get(); }
-  CCodec_JpegModule* GetJpegModule() const { return m_pJpegModule.get(); }
-  CCodec_JpxModule* GetJpxModule() const { return m_pJpxModule.get(); }
-  CCodec_Jbig2Module* GetJbig2Module() const { return m_pJbig2Module.get(); }
-  CCodec_IccModule* GetIccModule() const { return m_pIccModule.get(); }
-  CCodec_FlateModule* GetFlateModule() const { return m_pFlateModule.get(); }
+  JpegModule* GetJpegModule() const { return m_pJpegModule.get(); }
+  Jbig2Module* GetJbig2Module() const { return m_pJbig2Module.get(); }
 
 #ifdef PDF_ENABLE_XFA
-  std::unique_ptr<CCodec_ProgressiveDecoder> CreateProgressiveDecoder();
-  void SetBmpModule(std::unique_ptr<CCodec_BmpModule> module);
-  void SetGifModule(std::unique_ptr<CCodec_GifModule> module);
-  void SetPngModule(std::unique_ptr<CCodec_PngModule> module);
-  void SetTiffModule(std::unique_ptr<CCodec_TiffModule> module);
-  CCodec_BmpModule* GetBmpModule() const { return m_pBmpModule.get(); }
-  CCodec_GifModule* GetGifModule() const { return m_pGifModule.get(); }
-  CCodec_PngModule* GetPngModule() const { return m_pPngModule.get(); }
-  CCodec_TiffModule* GetTiffModule() const { return m_pTiffModule.get(); }
+  std::unique_ptr<ProgressiveDecoder> CreateProgressiveDecoder();
+
+#ifdef PDF_ENABLE_XFA_BMP
+  BmpModule* GetBmpModule() const { return m_pBmpModule.get(); }
+  void SetBmpModule(std::unique_ptr<BmpModule> module) {
+    m_pBmpModule = std::move(module);
+  }
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+  GifModule* GetGifModule() const { return m_pGifModule.get(); }
+  void SetGifModule(std::unique_ptr<GifModule> module) {
+    m_pGifModule = std::move(module);
+  }
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+  PngModule* GetPngModule() const { return m_pPngModule.get(); }
+  void SetPngModule(std::unique_ptr<PngModule> module) {
+    m_pPngModule = std::move(module);
+  }
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+  TiffModule* GetTiffModule() const { return m_pTiffModule.get(); }
+  void SetTiffModule(std::unique_ptr<TiffModule> module) {
+    m_pTiffModule = std::move(module);
+  }
+#endif  // PDF_ENABLE_XFA_TIFF
 #endif  // PDF_ENABLE_XFA
 
- protected:
-  std::unique_ptr<CCodec_BasicModule> m_pBasicModule;
-  std::unique_ptr<CCodec_FaxModule> m_pFaxModule;
-  std::unique_ptr<CCodec_JpegModule> m_pJpegModule;
-  std::unique_ptr<CCodec_JpxModule> m_pJpxModule;
-  std::unique_ptr<CCodec_Jbig2Module> m_pJbig2Module;
-  std::unique_ptr<CCodec_IccModule> m_pIccModule;
+ private:
+  ModuleMgr();
+  ~ModuleMgr();
+
+  std::unique_ptr<JpegModule> m_pJpegModule;
+  std::unique_ptr<Jbig2Module> m_pJbig2Module;
 
 #ifdef PDF_ENABLE_XFA
-  std::unique_ptr<CCodec_BmpModule> m_pBmpModule;
-  std::unique_ptr<CCodec_GifModule> m_pGifModule;
-  std::unique_ptr<CCodec_PngModule> m_pPngModule;
-  std::unique_ptr<CCodec_TiffModule> m_pTiffModule;
-#endif  // PDF_ENABLE_XFA
+#ifdef PDF_ENABLE_XFA_BMP
+  std::unique_ptr<BmpModule> m_pBmpModule;
+#endif  // PDF_ENABLE_XFA_BMP
 
-  std::unique_ptr<CCodec_FlateModule> m_pFlateModule;
+#ifdef PDF_ENABLE_XFA_GIF
+  std::unique_ptr<GifModule> m_pGifModule;
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+  std::unique_ptr<PngModule> m_pPngModule;
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+  std::unique_ptr<TiffModule> m_pTiffModule;
+#endif  // PDF_ENABLE_XFA_TIFF
+#endif  // PDF_ENABLE_XFA
 };
 
 void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels);
-uint32_t ComponentsForFamily(int family);
-std::tuple<float, float, float> AdobeCMYK_to_sRGB(float c,
-                                                  float m,
-                                                  float y,
-                                                  float k);
-std::tuple<uint8_t, uint8_t, uint8_t> AdobeCMYK_to_sRGB1(uint8_t c,
-                                                         uint8_t m,
-                                                         uint8_t y,
-                                                         uint8_t k);
-void FaxG4Decode(const uint8_t* src_buf,
-                 uint32_t src_size,
-                 int* pbitpos,
-                 uint8_t* dest_buf,
-                 int width,
-                 int height,
-                 int pitch);
+
+FX_SAFE_UINT32 CalculatePitch8(uint32_t bpc, uint32_t components, int width);
+FX_SAFE_UINT32 CalculatePitch32(int bpp, int width);
+
+}  // namespace fxcodec
+
+#ifdef PDF_ENABLE_XFA
+using CFX_DIBAttribute = fxcodec::CFX_DIBAttribute;
+#endif
 
 #endif  // CORE_FXCODEC_FX_CODEC_H_
diff --git a/core/fxcodec/fx_codec_def.h b/core/fxcodec/fx_codec_def.h
index 3983285..2d6b5ed 100644
--- a/core/fxcodec/fx_codec_def.h
+++ b/core/fxcodec/fx_codec_def.h
@@ -26,13 +26,22 @@
 #ifdef PDF_ENABLE_XFA
 enum FXCODEC_IMAGE_TYPE {
   FXCODEC_IMAGE_UNKNOWN = 0,
-  FXCODEC_IMAGE_BMP,
   FXCODEC_IMAGE_JPG,
+#ifdef PDF_ENABLE_XFA_BMP
+  FXCODEC_IMAGE_BMP,
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_PNG
   FXCODEC_IMAGE_PNG,
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_GIF
   FXCODEC_IMAGE_GIF,
-  FXCODEC_IMAGE_TIF,
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_TIFF
+  FXCODEC_IMAGE_TIFF,
+#endif  // PDF_ENABLE_XFA_TIFF
   FXCODEC_IMAGE_MAX
 };
+
 enum FXCODEC_RESUNIT {
   FXCODEC_RESUNIT_NONE = 0,
   FXCODEC_RESUNIT_INCH,
diff --git a/core/fxcodec/gif/cfx_gif.cpp b/core/fxcodec/gif/cfx_gif.cpp
index 985a687..efe4f4b 100644
--- a/core/fxcodec/gif/cfx_gif.cpp
+++ b/core/fxcodec/gif/cfx_gif.cpp
@@ -6,18 +6,30 @@
 
 #include "core/fxcodec/gif/cfx_gif.h"
 
-const char* kGifSignature87 = "GIF87a";
-const char* kGifSignature89 = "GIF89a";
+const char kGifSignature87[] = "GIF87a";
+const char kGifSignature89[] = "GIF89a";
 
-static_assert(sizeof(CFX_CFX_GifImageInfo) == 9,
-              "CFX_CFX_GifImageInfo should have a size of 9");
-static_assert(sizeof(CFX_GifPalette) == 3,
-              "CFX_GifPalette should have a size of 3");
-static_assert(sizeof(CFX_GifPlainTextExtension) == 13,
-              "CFX_GifPlainTextExtension should have a size of 13");
-static_assert(sizeof(CFX_GifGraphicControlExtension) == 5,
-              "CFX_GifGraphicControlExtension should have a size of 5");
+static_assert(sizeof(CFX_GifGlobalFlags) == 1,
+              "CFX_GifGlobalFlags should have a size of 1");
+static_assert(sizeof(CFX_GifLocalFlags) == 1,
+              "CFX_GifLocalFlags should have a size of 1");
 static_assert(sizeof(CFX_GifHeader) == 6,
               "CFX_GifHeader should have a size of 6");
 static_assert(sizeof(CFX_GifLocalScreenDescriptor) == 7,
               "CFX_GifLocalScreenDescriptor should have a size of 7");
+static_assert(sizeof(CFX_CFX_GifImageInfo) == 9,
+              "CFX_CFX_GifImageInfo should have a size of 9");
+static_assert(sizeof(CFX_GifControlExtensionFlags) == 1,
+              "CFX_GifControlExtensionFlags should have a size of 1");
+static_assert(sizeof(CFX_GifPlainTextExtension) == 13,
+              "CFX_GifPlainTextExtension should have a size of 13");
+static_assert(sizeof(CFX_GifGraphicControlExtension) == 5,
+              "CFX_GifGraphicControlExtension should have a size of 5");
+static_assert(sizeof(GifApplicationExtension) == 12,
+              "GifApplicationExtension should have a size of 12");
+static_assert(sizeof(CFX_GifPalette) == 3,
+              "CFX_GifPalette should have a size of 3");
+
+CFX_GifImage::CFX_GifImage() = default;
+
+CFX_GifImage::~CFX_GifImage() = default;
diff --git a/core/fxcodec/gif/cfx_gif.h b/core/fxcodec/gif/cfx_gif.h
index ab8278e..c0c9d00 100644
--- a/core/fxcodec/gif/cfx_gif.h
+++ b/core/fxcodec/gif/cfx_gif.h
@@ -10,10 +10,12 @@
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
+
 class CFX_GifContext;
 
-extern const char* kGifSignature87;
-extern const char* kGifSignature89;
+extern const char kGifSignature87[];
+extern const char kGifSignature89[];
 
 #define GIF_SIG_EXTENSION 0x21
 #define GIF_SIG_IMAGE 0x2C
@@ -24,7 +26,6 @@
 #define GIF_BLOCK_TERMINAL 0x00
 #define GIF_MAX_LZW_EXP 12
 #define GIF_MAX_LZW_CODE 4096
-#define GIF_DATA_BLOCK 255
 #define GIF_D_STATUS_SIG 0x01
 #define GIF_D_STATUS_TAIL 0x02
 #define GIF_D_STATUS_EXT 0x03
@@ -36,74 +37,78 @@
 #define GIF_D_STATUS_IMG_DATA 0x0A
 
 #pragma pack(1)
-typedef struct {
+struct CFX_GifGlobalFlags {
   uint8_t pal_bits : 3;
   uint8_t sort_flag : 1;
   uint8_t color_resolution : 3;
   uint8_t global_pal : 1;
-} CFX_GifGlobalFlags;
+};
 
-typedef struct {
+struct CFX_GifLocalFlags {
   uint8_t pal_bits : 3;
   uint8_t reserved : 2;
   uint8_t sort_flag : 1;
   uint8_t interlace : 1;
   uint8_t local_pal : 1;
-} CFX_GifLocalFlags;
+};
 
-typedef struct { char signature[6]; } CFX_GifHeader;
+struct CFX_GifHeader {
+  char signature[6];
+};
 
-typedef struct {
+struct CFX_GifLocalScreenDescriptor {
   uint16_t width;
   uint16_t height;
   CFX_GifGlobalFlags global_flags;
   uint8_t bc_index;
   uint8_t pixel_aspect;
-} CFX_GifLocalScreenDescriptor;
+};
 
-typedef struct {
+struct CFX_CFX_GifImageInfo {
   uint16_t left;
   uint16_t top;
   uint16_t width;
   uint16_t height;
   CFX_GifLocalFlags local_flags;
-} CFX_CFX_GifImageInfo;
+};
 
-typedef struct {
+struct CFX_GifControlExtensionFlags {
   uint8_t transparency : 1;
   uint8_t user_input : 1;
   uint8_t disposal_method : 3;
   uint8_t reserved : 3;
-} CFX_GifControlExtensionFlags;
+};
 
-typedef struct {
+struct CFX_GifGraphicControlExtension {
   uint8_t block_size;
   CFX_GifControlExtensionFlags gce_flags;
   uint16_t delay_time;
   uint8_t trans_index;
-} CFX_GifGraphicControlExtension;
+};
 
-typedef struct {
+struct CFX_GifPlainTextExtension {
   uint8_t block_size;
   uint16_t grid_left;
   uint16_t grid_top;
   uint16_t grid_width;
   uint16_t grid_height;
-
   uint8_t char_width;
   uint8_t char_height;
-
   uint8_t fc_index;
   uint8_t bc_index;
-} CFX_GifPlainTextExtension;
+};
 
-typedef struct {
+struct GifApplicationExtension {
   uint8_t block_size;
   uint8_t app_identify[8];
   uint8_t app_authentication[3];
-} GifApplicationExtension;
+};
 
-typedef struct { uint8_t r, g, b; } CFX_GifPalette;
+struct CFX_GifPalette {
+  uint8_t r;
+  uint8_t g;
+  uint8_t b;
+};
 #pragma pack()
 
 enum class CFX_GifDecodeStatus {
@@ -113,15 +118,18 @@
   InsufficientDestSize,  // Only used internally by CGifLZWDecoder::Decode()
 };
 
-typedef struct {
+struct CFX_GifImage {
+  CFX_GifImage();
+  ~CFX_GifImage();
+
   std::unique_ptr<CFX_GifGraphicControlExtension> image_GCE;
   std::vector<CFX_GifPalette> local_palettes;
-  std::vector<uint8_t> row_buffer;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> row_buffer;
   CFX_CFX_GifImageInfo image_info;
   uint8_t local_pallette_exp;
   uint8_t code_exp;
   uint32_t data_pos;
   int32_t row_num;
-} CFX_GifImage;
+};
 
 #endif  // CORE_FXCODEC_GIF_CFX_GIF_H_
diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp
index 6159e56..267098a 100644
--- a/core/fxcodec/gif/cfx_gifcontext.cpp
+++ b/core/fxcodec/gif/cfx_gifcontext.cpp
@@ -9,37 +9,25 @@
 #include <algorithm>
 #include <utility>
 
-#include "core/fxcodec/codec/ccodec_gifmodule.h"
+#include "core/fxcodec/cfx_codec_memory.h"
 #include "core/fxcodec/gif/cfx_gif.h"
+#include "core/fxcodec/gif/gifmodule.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
+namespace fxcodec {
+
 namespace {
 
-const int32_t s_gif_interlace_step[4] = {8, 8, 4, 2};
+constexpr int32_t kGifInterlaceStep[4] = {8, 8, 4, 2};
 
 }  // namespace
 
-CFX_GifContext::CFX_GifContext(CCodec_GifModule* gif_module,
-                               CCodec_GifModule::Delegate* delegate)
-    : gif_module_(gif_module),
-      delegate_(delegate),
-      global_pal_exp_(0),
-      img_row_offset_(0),
-      img_row_avail_size_(0),
-      avail_in_(0),
-      decode_status_(GIF_D_STATUS_SIG),
-      skip_size_(0),
-      next_in_(nullptr),
-      width_(0),
-      height_(0),
-      bc_index_(0),
-      pixel_aspect_(0),
-      global_sort_flag_(0),
-      global_color_resolution_(0),
-      img_pass_num_(0) {}
+CFX_GifContext::CFX_GifContext(GifModule* gif_module,
+                               GifModule::Delegate* delegate)
+    : gif_module_(gif_module), delegate_(delegate) {}
 
-CFX_GifContext::~CFX_GifContext() {}
+CFX_GifContext::~CFX_GifContext() = default;
 
 void CFX_GifContext::RecordCurrentPosition(uint32_t* cur_pos) {
   delegate_->GifRecordCurrentPosition(*cur_pos);
@@ -80,11 +68,11 @@
       case GIF_D_STATUS_TAIL:
         return CFX_GifDecodeStatus::Success;
       case GIF_D_STATUS_SIG: {
-        uint8_t* signature = nullptr;
-        if (!ReadData(&signature, 1))
+        uint8_t signature;
+        if (!ReadAllOrNone(&signature, sizeof(signature)))
           return CFX_GifDecodeStatus::Unfinished;
 
-        switch (*signature) {
+        switch (signature) {
           case GIF_SIG_EXTENSION:
             SaveDecodingStatus(GIF_D_STATUS_EXT);
             continue;
@@ -95,7 +83,7 @@
             SaveDecodingStatus(GIF_D_STATUS_TAIL);
             return CFX_GifDecodeStatus::Success;
           default:
-            if (avail_in_) {
+            if (!input_buffer_->IsEOF()) {
               // The Gif File has non_standard Tag!
               SaveDecodingStatus(GIF_D_STATUS_SIG);
               continue;
@@ -105,11 +93,11 @@
         }
       }
       case GIF_D_STATUS_EXT: {
-        uint8_t* extension = nullptr;
-        if (!ReadData(&extension, 1))
+        uint8_t extension;
+        if (!ReadAllOrNone(&extension, sizeof(extension)))
           return CFX_GifDecodeStatus::Unfinished;
 
-        switch (*extension) {
+        switch (extension) {
           case GIF_BLOCK_CE:
             SaveDecodingStatus(GIF_D_STATUS_EXT_CE);
             continue;
@@ -121,7 +109,7 @@
             continue;
           default: {
             int32_t status = GIF_D_STATUS_EXT_UNE;
-            if (*extension == GIF_BLOCK_PTE) {
+            if (extension == GIF_BLOCK_PTE) {
               status = GIF_D_STATUS_EXT_PTE;
             }
             SaveDecodingStatus(status);
@@ -137,23 +125,27 @@
         continue;
       }
       case GIF_D_STATUS_IMG_DATA: {
-        uint8_t* img_data_size = nullptr;
-        uint8_t* img_data = nullptr;
-        uint32_t skip_size_org = skip_size_;
-        if (!ReadData(&img_data_size, 1))
+        uint8_t img_data_size;
+        size_t read_marker = input_buffer_->GetPosition();
+
+        if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
           return CFX_GifDecodeStatus::Unfinished;
 
-        while (*img_data_size != GIF_BLOCK_TERMINAL) {
-          if (!ReadData(&img_data, *img_data_size)) {
-            skip_size_ = skip_size_org;
+        while (img_data_size != GIF_BLOCK_TERMINAL) {
+          if (!input_buffer_->Seek(input_buffer_->GetPosition() +
+                                   img_data_size)) {
+            input_buffer_->Seek(read_marker);
             return CFX_GifDecodeStatus::Unfinished;
           }
 
+          // This saving of the scan state on partial reads is why
+          // ScanForTerminalMarker() cannot be used here.
           SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
-          skip_size_org = skip_size_;
-          if (!ReadData(&img_data_size, 1))
+          read_marker = input_buffer_->GetPosition();
+          if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
             return CFX_GifDecodeStatus::Unfinished;
         }
+
         SaveDecodingStatus(GIF_D_STATUS_SIG);
         continue;
       }
@@ -172,9 +164,6 @@
   if (!pdfium::IndexInBounds(images_, frame_num))
     return CFX_GifDecodeStatus::Error;
 
-  uint8_t* img_data_size = nullptr;
-  uint8_t* img_data = nullptr;
-  uint32_t skip_size_org = skip_size_;
   CFX_GifImage* gif_image = images_[static_cast<size_t>(frame_num)].get();
   if (gif_image->image_info.height == 0)
     return CFX_GifDecodeStatus::Error;
@@ -190,7 +179,6 @@
         gif_image->image_info.local_flags.local_pal
             ? (2 << gif_image->image_info.local_flags.pal_bits)
             : 0;
-    avail_in_ = 0;
     CFX_GifPalette* pLocalPalette = gif_image->local_palettes.empty()
                                         ? nullptr
                                         : gif_image->local_palettes.data();
@@ -234,13 +222,18 @@
     SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
   }
 
+  uint8_t img_data_size;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> img_data;
+  size_t read_marker = input_buffer_->GetPosition();
+
   if (decode_status_ == GIF_D_STATUS_IMG_DATA) {
-    if (!ReadData(&img_data_size, 1))
+    if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
       return CFX_GifDecodeStatus::Unfinished;
 
-    if (*img_data_size != GIF_BLOCK_TERMINAL) {
-      if (!ReadData(&img_data, *img_data_size)) {
-        skip_size_ = skip_size_org;
+    if (img_data_size != GIF_BLOCK_TERMINAL) {
+      img_data.resize(img_data_size);
+      if (!ReadAllOrNone(img_data.data(), img_data_size)) {
+        input_buffer_->Seek(read_marker);
         return CFX_GifDecodeStatus::Unfinished;
       }
 
@@ -255,7 +248,7 @@
       CFX_GifDecodeStatus ret =
           lzw_decompressor_.get()
               ? lzw_decompressor_->Decode(
-                    img_data, *img_data_size,
+                    img_data.data(), img_data_size,
                     gif_image->row_buffer.data() + img_row_offset_,
                     &img_row_avail_size_)
               : CFX_GifDecodeStatus::Error;
@@ -263,6 +256,7 @@
         DecodingFailureAtTailCleanup(gif_image);
         return CFX_GifDecodeStatus::Error;
       }
+
       while (ret != CFX_GifDecodeStatus::Error) {
         if (ret == CFX_GifDecodeStatus::Success) {
           ReadScanline(gif_image->row_num, gif_image->row_buffer.data());
@@ -270,16 +264,19 @@
           SaveDecodingStatus(GIF_D_STATUS_TAIL);
           return CFX_GifDecodeStatus::Success;
         }
+
         if (ret == CFX_GifDecodeStatus::Unfinished) {
-          skip_size_org = skip_size_;
-          if (!ReadData(&img_data_size, 1))
+          read_marker = input_buffer_->GetPosition();
+          if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
             return CFX_GifDecodeStatus::Unfinished;
 
-          if (*img_data_size != GIF_BLOCK_TERMINAL) {
-            if (!ReadData(&img_data, *img_data_size)) {
-              skip_size_ = skip_size_org;
+          if (img_data_size != GIF_BLOCK_TERMINAL) {
+            img_data.resize(img_data_size);
+            if (!ReadAllOrNone(img_data.data(), img_data_size)) {
+              input_buffer_->Seek(read_marker);
               return CFX_GifDecodeStatus::Unfinished;
             }
+
             if (!lzw_decompressor_.get())
               lzw_decompressor_ = CFX_LZWDecompressor::Create(
                   !gif_image->local_palettes.empty()
@@ -291,24 +288,25 @@
             img_row_avail_size_ = gif_img_row_bytes - img_row_offset_;
             ret = lzw_decompressor_.get()
                       ? lzw_decompressor_->Decode(
-                            img_data, *img_data_size,
+                            img_data.data(), img_data_size,
                             gif_image->row_buffer.data() + img_row_offset_,
                             &img_row_avail_size_)
                       : CFX_GifDecodeStatus::Error;
           }
         }
+
         if (ret == CFX_GifDecodeStatus::InsufficientDestSize) {
           if (gif_image->image_info.local_flags.interlace) {
             ReadScanline(gif_image->row_num, gif_image->row_buffer.data());
-            gif_image->row_num += s_gif_interlace_step[img_pass_num_];
+            gif_image->row_num += kGifInterlaceStep[img_pass_num_];
             if (gif_image->row_num >=
                 static_cast<int32_t>(gif_image->image_info.height)) {
               img_pass_num_++;
-              if (img_pass_num_ == FX_ArraySize(s_gif_interlace_step)) {
+              if (img_pass_num_ == FX_ArraySize(kGifInterlaceStep)) {
                 DecodingFailureAtTailCleanup(gif_image);
                 return CFX_GifDecodeStatus::Error;
               }
-              gif_image->row_num = s_gif_interlace_step[img_pass_num_] / 2;
+              gif_image->row_num = kGifInterlaceStep[img_pass_num_] / 2;
             }
           } else {
             ReadScanline(gif_image->row_num++, gif_image->row_buffer.data());
@@ -317,12 +315,14 @@
           img_row_avail_size_ = gif_img_row_bytes;
           ret = lzw_decompressor_.get()
                     ? lzw_decompressor_->Decode(
-                          img_data, *img_data_size,
+                          img_data.data(), img_data_size,
                           gif_image->row_buffer.data() + img_row_offset_,
                           &img_row_avail_size_)
                     : CFX_GifDecodeStatus::Error;
         }
-        if (ret == CFX_GifDecodeStatus::Error) {
+
+        if (ret == CFX_GifDecodeStatus::InsufficientDestSize ||
+            ret == CFX_GifDecodeStatus::Error) {
           DecodingFailureAtTailCleanup(gif_image);
           return CFX_GifDecodeStatus::Error;
         }
@@ -333,168 +333,132 @@
   return CFX_GifDecodeStatus::Error;
 }
 
-void CFX_GifContext::SetInputBuffer(uint8_t* src_buf, uint32_t src_size) {
-  next_in_ = src_buf;
-  avail_in_ = src_size;
-  skip_size_ = 0;
+void CFX_GifContext::SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory) {
+  input_buffer_ = std::move(codec_memory);
 }
 
-uint32_t CFX_GifContext::GetAvailInput(uint8_t** avail_buf) const {
-  if (avail_buf) {
-    *avail_buf = nullptr;
-    if (avail_in_ > 0)
-      *avail_buf = next_in_;
+uint32_t CFX_GifContext::GetAvailInput() const {
+  if (!input_buffer_)
+    return 0;
+
+  return input_buffer_->GetSize() - input_buffer_->GetPosition();
+}
+
+bool CFX_GifContext::ReadAllOrNone(uint8_t* dest, uint32_t size) {
+  if (!input_buffer_ || !dest)
+    return false;
+
+  size_t read_marker = input_buffer_->GetPosition();
+  size_t read = input_buffer_->ReadBlock(dest, size);
+  if (read < size) {
+    input_buffer_->Seek(read_marker);
+    return false;
   }
-  return avail_in_;
-}
 
-uint8_t* CFX_GifContext::ReadData(uint8_t** des_buf_pp, uint32_t data_size) {
-  if (!next_in_)
-    return nullptr;
-  if (avail_in_ <= skip_size_)
-    return nullptr;
-  if (!des_buf_pp)
-    return nullptr;
-  if (data_size == 0)
-    return nullptr;
-  if (avail_in_ - skip_size_ < data_size)
-    return nullptr;
-
-  *des_buf_pp = next_in_ + skip_size_;
-  skip_size_ += data_size;
-  return *des_buf_pp;
+  return true;
 }
 
 CFX_GifDecodeStatus CFX_GifContext::ReadGifSignature() {
-  CFX_GifHeader* header = nullptr;
-  uint32_t skip_size_org = skip_size_;
-  if (!ReadData(reinterpret_cast<uint8_t**>(&header), 6)) {
-    skip_size_ = skip_size_org;
+  CFX_GifHeader header;
+  if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&header), 6))
     return CFX_GifDecodeStatus::Unfinished;
-  }
 
-  if (strncmp(header->signature, kGifSignature87, 6) != 0 &&
-      strncmp(header->signature, kGifSignature89, 6) != 0)
+  if (strncmp(header.signature, kGifSignature87, 6) != 0 &&
+      strncmp(header.signature, kGifSignature89, 6) != 0) {
     return CFX_GifDecodeStatus::Error;
+  }
 
   return CFX_GifDecodeStatus::Success;
 }
 
 CFX_GifDecodeStatus CFX_GifContext::ReadLogicalScreenDescriptor() {
-  CFX_GifLocalScreenDescriptor* lsd = nullptr;
-  uint32_t skip_size_org = skip_size_;
-  if (!ReadData(reinterpret_cast<uint8_t**>(&lsd), 7)) {
-    skip_size_ = skip_size_org;
+  CFX_GifLocalScreenDescriptor lsd;
+  size_t read_marker = input_buffer_->GetPosition();
+
+  if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&lsd), sizeof(lsd)))
     return CFX_GifDecodeStatus::Unfinished;
-  }
 
-  if (lsd->global_flags.global_pal) {
-    uint32_t palette_count = unsigned(2 << lsd->global_flags.pal_bits);
-    if (lsd->bc_index >= palette_count)
+  if (lsd.global_flags.global_pal) {
+    uint32_t palette_count = unsigned(2 << lsd.global_flags.pal_bits);
+    if (lsd.bc_index >= palette_count)
       return CFX_GifDecodeStatus::Error;
-    bc_index_ = lsd->bc_index;
+    bc_index_ = lsd.bc_index;
 
-    uint32_t palette_size = palette_count * 3u;
-    uint8_t* palette = nullptr;
-    if (!ReadData(&palette, palette_size)) {
-      skip_size_ = skip_size_org;
+    uint32_t palette_size = palette_count * sizeof(CFX_GifPalette);
+    std::vector<CFX_GifPalette> palette(palette_count);
+    if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(palette.data()),
+                       palette_size)) {
+      // Roll back the read for the LSD
+      input_buffer_->Seek(read_marker);
       return CFX_GifDecodeStatus::Unfinished;
     }
 
-    global_pal_exp_ = lsd->global_flags.pal_bits;
-    global_sort_flag_ = lsd->global_flags.sort_flag;
-    global_color_resolution_ = lsd->global_flags.color_resolution;
-    global_palette_.resize(palette_count);
-    memcpy(global_palette_.data(), palette, palette_size);
+    global_pal_exp_ = lsd.global_flags.pal_bits;
+    global_sort_flag_ = lsd.global_flags.sort_flag;
+    global_color_resolution_ = lsd.global_flags.color_resolution;
+    std::swap(global_palette_, palette);
   }
 
   width_ = static_cast<int>(
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd->width)));
+      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.width)));
   height_ = static_cast<int>(
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd->height)));
+      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.height)));
 
-  pixel_aspect_ = lsd->pixel_aspect;
   return CFX_GifDecodeStatus::Success;
 }
 
 void CFX_GifContext::SaveDecodingStatus(int32_t status) {
   decode_status_ = status;
-  next_in_ += skip_size_;
-  avail_in_ -= skip_size_;
-  skip_size_ = 0;
 }
 
 CFX_GifDecodeStatus CFX_GifContext::DecodeExtension() {
-  uint8_t* data_size = nullptr;
-  uint8_t* data_buf = nullptr;
-  uint32_t skip_size_org = skip_size_;
+  size_t read_marker = input_buffer_->GetPosition();
+
   switch (decode_status_) {
     case GIF_D_STATUS_EXT_CE: {
-      if (!ReadData(&data_size, 1)) {
-        skip_size_ = skip_size_org;
+      if (!ScanForTerminalMarker()) {
+        input_buffer_->Seek(read_marker);
         return CFX_GifDecodeStatus::Unfinished;
       }
-
-      cmt_data_.clear();
-      while (*data_size != GIF_BLOCK_TERMINAL) {
-        uint8_t block_size = *data_size;
-        if (!ReadData(&data_buf, *data_size) || !ReadData(&data_size, 1)) {
-          skip_size_ = skip_size_org;
-          return CFX_GifDecodeStatus::Unfinished;
-        }
-
-        cmt_data_ += ByteString(data_buf, block_size);
-      }
       break;
     }
     case GIF_D_STATUS_EXT_PTE: {
-      CFX_GifPlainTextExtension* gif_pte = nullptr;
-      if (!ReadData(reinterpret_cast<uint8_t**>(&gif_pte), 13))
+      CFX_GifPlainTextExtension gif_pte;
+      if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_pte), sizeof(gif_pte)))
         return CFX_GifDecodeStatus::Unfinished;
 
       graphic_control_extension_ = nullptr;
-      if (!ReadData(&data_size, 1)) {
-        skip_size_ = skip_size_org;
+      if (!ScanForTerminalMarker()) {
+        input_buffer_->Seek(read_marker);
         return CFX_GifDecodeStatus::Unfinished;
       }
-
-      while (*data_size != GIF_BLOCK_TERMINAL) {
-        if (!ReadData(&data_buf, *data_size) || !ReadData(&data_size, 1)) {
-          skip_size_ = skip_size_org;
-          return CFX_GifDecodeStatus::Unfinished;
-        }
-      }
       break;
     }
     case GIF_D_STATUS_EXT_GCE: {
-      CFX_GifGraphicControlExtension* gif_gce = nullptr;
-      if (!ReadData(reinterpret_cast<uint8_t**>(&gif_gce), 6))
+      CFX_GifGraphicControlExtension gif_gce;
+      if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_gce), sizeof(gif_gce)))
         return CFX_GifDecodeStatus::Unfinished;
 
       if (!graphic_control_extension_.get())
         graphic_control_extension_ =
             pdfium::MakeUnique<CFX_GifGraphicControlExtension>();
-      graphic_control_extension_->block_size = gif_gce->block_size;
-      graphic_control_extension_->gce_flags = gif_gce->gce_flags;
+      graphic_control_extension_->block_size = gif_gce.block_size;
+      graphic_control_extension_->gce_flags = gif_gce.gce_flags;
       graphic_control_extension_->delay_time =
-          FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&gif_gce->delay_time));
-      graphic_control_extension_->trans_index = gif_gce->trans_index;
+          FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&gif_gce.delay_time));
+      graphic_control_extension_->trans_index = gif_gce.trans_index;
       break;
     }
     default: {
       if (decode_status_ == GIF_D_STATUS_EXT_PTE)
         graphic_control_extension_ = nullptr;
-      if (!ReadData(&data_size, 1))
+      if (!ScanForTerminalMarker()) {
+        input_buffer_->Seek(read_marker);
         return CFX_GifDecodeStatus::Unfinished;
-
-      while (*data_size != GIF_BLOCK_TERMINAL) {
-        if (!ReadData(&data_buf, *data_size) || !ReadData(&data_size, 1)) {
-          skip_size_ = skip_size_org;
-          return CFX_GifDecodeStatus::Unfinished;
-        }
       }
     }
   }
+
   SaveDecodingStatus(GIF_D_STATUS_SIG);
   return CFX_GifDecodeStatus::Success;
 }
@@ -503,49 +467,48 @@
   if (width_ <= 0 || height_ <= 0)
     return CFX_GifDecodeStatus::Error;
 
-  uint32_t skip_size_org = skip_size_;
-  CFX_CFX_GifImageInfo* img_info = nullptr;
-  if (!ReadData(reinterpret_cast<uint8_t**>(&img_info), 9))
+  size_t read_marker = input_buffer_->GetPosition();
+  CFX_CFX_GifImageInfo img_info;
+  if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&img_info), sizeof(img_info)))
     return CFX_GifDecodeStatus::Unfinished;
 
   auto gif_image = pdfium::MakeUnique<CFX_GifImage>();
   gif_image->image_info.left =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->left));
+      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.left));
   gif_image->image_info.top =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->top));
+      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.top));
   gif_image->image_info.width =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->width));
+      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.width));
   gif_image->image_info.height =
-      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->height));
-  gif_image->image_info.local_flags = img_info->local_flags;
+      FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.height));
+  gif_image->image_info.local_flags = img_info.local_flags;
   if (gif_image->image_info.left + gif_image->image_info.width > width_ ||
       gif_image->image_info.top + gif_image->image_info.height > height_)
     return CFX_GifDecodeStatus::Error;
 
-  CFX_GifLocalFlags* gif_img_info_lf = &img_info->local_flags;
+  CFX_GifLocalFlags* gif_img_info_lf = &img_info.local_flags;
   if (gif_img_info_lf->local_pal) {
     gif_image->local_pallette_exp = gif_img_info_lf->pal_bits;
-    uint32_t loc_pal_size = unsigned(2 << gif_img_info_lf->pal_bits) * 3u;
-    uint8_t* loc_pal = nullptr;
-    if (!ReadData(&loc_pal, loc_pal_size)) {
-      skip_size_ = skip_size_org;
+    uint32_t loc_pal_count = unsigned(2 << gif_img_info_lf->pal_bits);
+    std::vector<CFX_GifPalette> loc_pal(loc_pal_count);
+    if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(loc_pal.data()),
+                       loc_pal_count * sizeof(CFX_GifPalette))) {
+      input_buffer_->Seek(read_marker);
       return CFX_GifDecodeStatus::Unfinished;
     }
 
-    gif_image->local_palettes = std::vector<CFX_GifPalette>(loc_pal_size / 3);
-    std::copy(loc_pal, loc_pal + loc_pal_size,
-              reinterpret_cast<uint8_t*>(gif_image->local_palettes.data()));
+    gif_image->local_palettes = std::move(loc_pal);
   }
 
-  uint8_t* code_size = nullptr;
-  if (!ReadData(&code_size, 1)) {
-    skip_size_ = skip_size_org;
+  uint8_t code_size;
+  if (!ReadAllOrNone(&code_size, sizeof(code_size))) {
+    input_buffer_->Seek(read_marker);
     return CFX_GifDecodeStatus::Unfinished;
   }
 
-  gif_image->code_exp = *code_size;
+  gif_image->code_exp = code_size;
   RecordCurrentPosition(&gif_image->data_pos);
-  gif_image->data_pos += skip_size_;
+  gif_image->data_pos += input_buffer_->GetPosition();
   gif_image->image_GCE = nullptr;
   if (graphic_control_extension_.get()) {
     if (graphic_control_extension_->gce_flags.transparency) {
@@ -570,3 +533,21 @@
   gif_image->row_buffer.clear();
   SaveDecodingStatus(GIF_D_STATUS_TAIL);
 }
+
+bool CFX_GifContext::ScanForTerminalMarker() {
+  uint8_t data_size;
+
+  if (!ReadAllOrNone(&data_size, sizeof(data_size)))
+    return false;
+
+  while (data_size != GIF_BLOCK_TERMINAL) {
+    if (!input_buffer_->Seek(input_buffer_->GetPosition() + data_size) ||
+        !ReadAllOrNone(&data_size, sizeof(data_size))) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h
index 3a367bd..d2ccd9d 100644
--- a/core/fxcodec/gif/cfx_gifcontext.h
+++ b/core/fxcodec/gif/cfx_gifcontext.h
@@ -10,16 +10,19 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcodec/codec/ccodec_gifmodule.h"
 #include "core/fxcodec/gif/cfx_gif.h"
 #include "core/fxcodec/gif/cfx_lzwdecompressor.h"
+#include "core/fxcodec/gif/gifmodule.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
 
-class CFX_GifContext : public CCodec_GifModule::Context {
+class CFX_CodecMemory;
+
+namespace fxcodec {
+
+class CFX_GifContext : public ModuleIface::Context {
  public:
-  CFX_GifContext(CCodec_GifModule* gif_module,
-                 CCodec_GifModule::Delegate* delegate);
+  CFX_GifContext(GifModule* gif_module, GifModule::Delegate* delegate);
   ~CFX_GifContext() override;
 
   void RecordCurrentPosition(uint32_t* cur_pos);
@@ -39,42 +42,42 @@
   CFX_GifDecodeStatus ReadHeader();
   CFX_GifDecodeStatus GetFrame();
   CFX_GifDecodeStatus LoadFrame(int32_t frame_num);
-  void SetInputBuffer(uint8_t* src_buf, uint32_t src_size);
-  uint32_t GetAvailInput(uint8_t** avail_buf) const;
+  void SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory);
+  uint32_t GetAvailInput() const;
   size_t GetFrameNum() const { return images_.size(); }
 
-  UnownedPtr<CCodec_GifModule> gif_module_;
-  UnownedPtr<CCodec_GifModule::Delegate> delegate_;
+  UnownedPtr<GifModule> const gif_module_;
+  UnownedPtr<GifModule::Delegate> const delegate_;
   std::vector<CFX_GifPalette> global_palette_;
-  uint8_t global_pal_exp_;
-  uint32_t img_row_offset_;
-  uint32_t img_row_avail_size_;
-  uint32_t avail_in_;
-  int32_t decode_status_;
-  uint32_t skip_size_;
-  ByteString cmt_data_;
+  uint8_t global_pal_exp_ = 0;
+  uint32_t img_row_offset_ = 0;
+  uint32_t img_row_avail_size_ = 0;
+  int32_t decode_status_ = GIF_D_STATUS_SIG;
   std::unique_ptr<CFX_GifGraphicControlExtension> graphic_control_extension_;
-  uint8_t* next_in_;
   std::vector<std::unique_ptr<CFX_GifImage>> images_;
   std::unique_ptr<CFX_LZWDecompressor> lzw_decompressor_;
-  int width_;
-  int height_;
-  uint8_t bc_index_;
-  uint8_t pixel_aspect_;
-  uint8_t global_sort_flag_;
-  uint8_t global_color_resolution_;
-  uint8_t img_pass_num_;
+  int width_ = 0;
+  int height_ = 0;
+  uint8_t bc_index_ = 0;
+  uint8_t global_sort_flag_ = 0;
+  uint8_t global_color_resolution_ = 0;
+  uint8_t img_pass_num_ = 0;
 
  protected:
-  uint8_t* ReadData(uint8_t** des_buf_pp, uint32_t data_size);
+  bool ReadAllOrNone(uint8_t* dest, uint32_t size);
   CFX_GifDecodeStatus ReadGifSignature();
   CFX_GifDecodeStatus ReadLogicalScreenDescriptor();
 
+  RetainPtr<CFX_CodecMemory> input_buffer_;
+
  private:
   void SaveDecodingStatus(int32_t status);
   CFX_GifDecodeStatus DecodeExtension();
   CFX_GifDecodeStatus DecodeImageInfo();
   void DecodingFailureAtTailCleanup(CFX_GifImage* gif_image);
+  bool ScanForTerminalMarker();
 };
 
+}  // namespace fxcodec
+
 #endif  // CORE_FXCODEC_GIF_CFX_GIFCONTEXT_H_
diff --git a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
index c5d3ac9..20d34ea 100644
--- a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
+++ b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
@@ -4,157 +4,141 @@
 
 #include "core/fxcodec/gif/cfx_gifcontext.h"
 
-#include "core/fxcrt/unowned_ptr.h"
+#include <utility>
+
+#include "core/fxcodec/cfx_codec_memory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CFX_GifContextForTest : public CFX_GifContext {
+namespace fxcodec {
+
+class CFX_GifContextForTest final : public CFX_GifContext {
  public:
-  CFX_GifContextForTest(CCodec_GifModule* gif_module,
-                        CCodec_GifModule::Delegate* delegate)
+  CFX_GifContextForTest(GifModule* gif_module, GifModule::Delegate* delegate)
       : CFX_GifContext(gif_module, delegate) {}
   ~CFX_GifContextForTest() override {}
 
-  using CFX_GifContext::ReadData;
+  using CFX_GifContext::ReadAllOrNone;
   using CFX_GifContext::ReadGifSignature;
   using CFX_GifContext::ReadLogicalScreenDescriptor;
+
+  CFX_CodecMemory* InputBuffer() const { return input_buffer_.Get(); }
+  void SetTestInputBuffer(pdfium::span<uint8_t> input) {
+    auto pMemory = pdfium::MakeRetain<CFX_CodecMemory>(input.size());
+    memcpy(pMemory->GetBuffer(), input.data(), input.size());
+    SetInputBuffer(std::move(pMemory));
+  }
 };
 
 TEST(CFX_GifContext, SetInputBuffer) {
+  uint8_t buffer[] = {0x00, 0x01, 0x02};
   CFX_GifContextForTest context(nullptr, nullptr);
 
-  context.SetInputBuffer(nullptr, 0);
-  EXPECT_EQ(nullptr, context.next_in_);
-  EXPECT_EQ(0u, context.avail_in_);
-  EXPECT_EQ(0u, context.skip_size_);
+  context.SetTestInputBuffer({nullptr, 0});
+  EXPECT_EQ(0u, context.InputBuffer()->GetSize());
+  EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
 
-  context.SetInputBuffer(nullptr, 100);
-  EXPECT_EQ(nullptr, context.next_in_);
-  EXPECT_EQ(100u, context.avail_in_);
-  EXPECT_EQ(0u, context.skip_size_);
+  context.SetTestInputBuffer({buffer, 0});
+  EXPECT_EQ(0u, context.InputBuffer()->GetSize());
+  EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
 
-  uint8_t buffer[] = {0x00, 0x01, 0x02};
-  context.SetInputBuffer(buffer, 0);
-  EXPECT_EQ(buffer, context.next_in_);
-  EXPECT_EQ(0u, context.avail_in_);
-  EXPECT_EQ(0u, context.skip_size_);
-
-  context.SetInputBuffer(buffer, 3);
-  EXPECT_EQ(buffer, context.next_in_);
-  EXPECT_EQ(3u, context.avail_in_);
-  EXPECT_EQ(0u, context.skip_size_);
-
-  context.SetInputBuffer(buffer, 100);
-  EXPECT_EQ(buffer, context.next_in_);
-  EXPECT_EQ(100u, context.avail_in_);
-  EXPECT_EQ(0u, context.skip_size_);
+  context.SetTestInputBuffer({buffer, 3});
+  EXPECT_EQ(3u, context.InputBuffer()->GetSize());
+  EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
 }
 
-TEST(CFX_GifContext, ReadData) {
-  CFX_GifContextForTest context(nullptr, nullptr);
-
-  context.SetInputBuffer(nullptr, 0);
-  EXPECT_EQ(nullptr, context.ReadData(nullptr, 0));
-  EXPECT_EQ(nullptr, context.ReadData(nullptr, 10));
-
-  uint8_t* dest_buffer = nullptr;
-  EXPECT_EQ(nullptr,
-            context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 0));
-  EXPECT_EQ(nullptr,
-            context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10));
-
+TEST(CFX_GifContext, ReadAllOrNone) {
+  std::vector<uint8_t> dest_buffer;
   uint8_t src_buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04,
                           0x05, 0x06, 0x07, 0x08, 0x09};
-  context.SetInputBuffer(src_buffer, 0);
-  EXPECT_EQ(nullptr,
-            context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10));
-  EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), context.next_in_);
-  EXPECT_EQ(0u, context.avail_in_);
-  EXPECT_EQ(0u, context.skip_size_);
+  CFX_GifContextForTest context(nullptr, nullptr);
 
-  dest_buffer = nullptr;
-  context.SetInputBuffer(src_buffer, 10);
-  EXPECT_EQ(src_buffer,
-            context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10));
-  EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), dest_buffer);
-  EXPECT_EQ(src_buffer, context.next_in_);
-  EXPECT_EQ(10u, context.avail_in_);
-  EXPECT_EQ(10u, context.skip_size_);
+  context.SetTestInputBuffer({nullptr, 0});
+  EXPECT_FALSE(context.ReadAllOrNone(nullptr, 0));
+  EXPECT_FALSE(context.ReadAllOrNone(nullptr, 10));
 
-  dest_buffer = nullptr;
-  context.SetInputBuffer(src_buffer, 10);
-  EXPECT_EQ(src_buffer,
-            context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5));
-  EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), dest_buffer);
-  EXPECT_EQ(src_buffer, context.next_in_);
-  EXPECT_EQ(10u, context.avail_in_);
-  EXPECT_EQ(5u, context.skip_size_);
+  EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), 0));
+  EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), 10));
 
-  dest_buffer = nullptr;
-  EXPECT_EQ(src_buffer + 5,
-            context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5));
-  EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer + 5), dest_buffer);
-  EXPECT_EQ(src_buffer, context.next_in_);
-  EXPECT_EQ(10u, context.avail_in_);
-  EXPECT_EQ(10u, context.skip_size_);
+  context.SetTestInputBuffer({src_buffer, 0});
+  dest_buffer.resize(sizeof(src_buffer));
+  EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), sizeof(src_buffer)));
 
-  dest_buffer = nullptr;
-  EXPECT_EQ(nullptr,
-            context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5));
-  EXPECT_EQ(nullptr, dest_buffer);
-  EXPECT_EQ(src_buffer, context.next_in_);
-  EXPECT_EQ(10u, context.avail_in_);
-  EXPECT_EQ(10u, context.skip_size_);
+  context.SetTestInputBuffer({src_buffer, 1});
+  EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), sizeof(src_buffer)));
+  EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
+  EXPECT_FALSE(context.ReadAllOrNone(nullptr, sizeof(src_buffer)));
+  EXPECT_FALSE(context.ReadAllOrNone(nullptr, 1));
+  EXPECT_TRUE(context.ReadAllOrNone(dest_buffer.data(), 1));
+  EXPECT_EQ(src_buffer[0], dest_buffer[0]);
+
+  context.SetTestInputBuffer(src_buffer);
+  EXPECT_FALSE(context.ReadAllOrNone(nullptr, sizeof(src_buffer)));
+  EXPECT_TRUE(context.ReadAllOrNone(dest_buffer.data(), sizeof(src_buffer)));
+  for (size_t i = 0; i < sizeof(src_buffer); i++)
+    EXPECT_EQ(src_buffer[i], dest_buffer[i]);
+
+  context.SetTestInputBuffer(src_buffer);
+  for (size_t i = 0; i < sizeof(src_buffer); i++) {
+    EXPECT_TRUE(context.ReadAllOrNone(dest_buffer.data(), 1));
+    EXPECT_EQ(src_buffer[i], dest_buffer[0]);
+  }
 }
 
 TEST(CFX_GifContext, ReadGifSignature) {
   CFX_GifContextForTest context(nullptr, nullptr);
-
   {
     uint8_t data[1];
-    context.SetInputBuffer(data, 0);
+    context.SetTestInputBuffer({data, 0});
     EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
-    EXPECT_EQ(0u, context.skip_size_);
+    EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // Make sure testing the entire signature
   {
     uint8_t data[] = {'G', 'I', 'F'};
-    context.SetInputBuffer(data, sizeof(data));
+    context.SetTestInputBuffer(data);
     EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
-    EXPECT_EQ(0u, context.skip_size_);
+    EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   {
     uint8_t data[] = {'N', 'O', 'T', 'G', 'I', 'F'};
-    context.SetInputBuffer(data, sizeof(data));
+    context.SetTestInputBuffer(data);
     EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
-    EXPECT_EQ(6u, context.skip_size_);
+    EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // Make sure not matching GIF8*a
   {
     uint8_t data[] = {'G', 'I', 'F', '8', '0', 'a'};
-    context.SetInputBuffer(data, sizeof(data));
+    context.SetTestInputBuffer(data);
     EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
-    EXPECT_EQ(6u, context.skip_size_);
+    EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // Make sure not matching GIF**a
   {
     uint8_t data[] = {'G', 'I', 'F', '9', '2', 'a'};
-    context.SetInputBuffer(data, sizeof(data));
+    context.SetTestInputBuffer(data);
     EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
-    EXPECT_EQ(6u, context.skip_size_);
+    EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // One valid signature
   {
     uint8_t data[] = {'G', 'I', 'F', '8', '7', 'a'};
-    context.SetInputBuffer(data, sizeof(data));
+    context.SetTestInputBuffer(data);
     EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
-    EXPECT_EQ(6u, context.skip_size_);
+    EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // The other valid signature
   {
     uint8_t data[] = {'G', 'I', 'F', '8', '9', 'a'};
-    context.SetInputBuffer(data, sizeof(data));
+    context.SetTestInputBuffer(data);
     EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
-    EXPECT_EQ(6u, context.skip_size_);
+    EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
 }
 
@@ -162,50 +146,54 @@
   CFX_GifContextForTest context(nullptr, nullptr);
   {
     uint8_t data[1];
-    context.SetInputBuffer(data, 0);
+    context.SetTestInputBuffer({data, 0});
     EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
               context.ReadLogicalScreenDescriptor());
+    context.SetTestInputBuffer({});
   }
   // LSD with all the values zero'd
   {
     uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
     memset(&lsd, 0, sizeof(CFX_GifLocalScreenDescriptor));
-    context.SetInputBuffer(lsd, sizeof(CFX_GifLocalScreenDescriptor));
+    context.SetTestInputBuffer(lsd);
 
     EXPECT_EQ(CFX_GifDecodeStatus::Success,
               context.ReadLogicalScreenDescriptor());
 
-    EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), context.skip_size_);
+    EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor),
+              static_cast<size_t>(context.InputBuffer()->GetPosition()));
     EXPECT_EQ(0, context.width_);
     EXPECT_EQ(0, context.height_);
     EXPECT_EQ(0u, context.bc_index_);
-    EXPECT_EQ(0u, context.pixel_aspect_);
+    context.SetTestInputBuffer({});
   }
   // LSD with no global palette
   {
     uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)] = {0x0A, 0x00, 0x00, 0x0F,
                                                          0x00, 0x01, 0x02};
-    context.SetInputBuffer(lsd, sizeof(CFX_GifLocalScreenDescriptor));
+    context.SetTestInputBuffer(lsd);
 
     EXPECT_EQ(CFX_GifDecodeStatus::Success,
               context.ReadLogicalScreenDescriptor());
 
-    EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), context.skip_size_);
+    EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor),
+              static_cast<size_t>(context.InputBuffer()->GetPosition()));
     EXPECT_EQ(0x000A, context.width_);
     EXPECT_EQ(0x0F00, context.height_);
     EXPECT_EQ(0u, context.bc_index_);  // bc_index_ is 0 if no global palette
-    EXPECT_EQ(2u, context.pixel_aspect_);
+    context.SetTestInputBuffer({});
   }
   // LSD with global palette bit set, but no global palette
   {
     uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)] = {0x0A, 0x00, 0x00, 0x0F,
                                                          0x80, 0x01, 0x02};
-    context.SetInputBuffer(lsd, sizeof(CFX_GifLocalScreenDescriptor));
+    context.SetTestInputBuffer(lsd);
 
     EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
               context.ReadLogicalScreenDescriptor());
 
-    EXPECT_EQ(0u, context.skip_size_);
+    EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // LSD with global palette
   {
@@ -214,22 +202,22 @@
       uint8_t palette[4 * sizeof(CFX_GifPalette)];
     } data = {{0x0A, 0x00, 0x00, 0x0F, 0xA9, 0x01, 0x02},
               {0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1}};
-    context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+    context.SetTestInputBuffer(
+        {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
     EXPECT_EQ(CFX_GifDecodeStatus::Success,
               context.ReadLogicalScreenDescriptor());
 
-    EXPECT_EQ(sizeof(data), context.skip_size_);
+    EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
     EXPECT_EQ(0x000A, context.width_);
     EXPECT_EQ(0x0F00, context.height_);
     EXPECT_EQ(1u, context.bc_index_);
-    EXPECT_EQ(2u, context.pixel_aspect_);
-
     EXPECT_EQ(1u, context.global_pal_exp_);
     EXPECT_EQ(1, context.global_sort_flag_);
     EXPECT_EQ(2, context.global_color_resolution_);
-    EXPECT_TRUE(0 == memcmp(data.palette, context.global_palette_.data(),
-                            sizeof(data.palette)));
+    EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(),
+                        sizeof(data.palette)));
+    context.SetTestInputBuffer({});
   }
 }
 
@@ -242,21 +230,22 @@
       uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
     } data = {{'N', 'O', 'T', 'G', 'I', 'F'},
               {0x0A, 0x00, 0x00, 0x0F, 0x00, 0x01, 0x02}};
-    context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+    context.SetTestInputBuffer(
+        {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
     EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadHeader());
-
-    EXPECT_EQ(sizeof(data.signature), context.skip_size_);
+    EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // Short after signature
   {
     uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'};
-    context.SetInputBuffer(reinterpret_cast<uint8_t*>(&signature),
-                           sizeof(signature));
+    context.SetTestInputBuffer(
+        {reinterpret_cast<uint8_t*>(&signature), sizeof(signature)});
 
     EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
-
-    EXPECT_EQ(sizeof(signature), context.skip_size_);
+    EXPECT_EQ(sizeof(signature), context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // Success without global palette
   {
@@ -265,15 +254,15 @@
       uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
     } data = {{'G', 'I', 'F', '8', '7', 'a'},
               {0x0A, 0x00, 0x00, 0x0F, 0x00, 0x01, 0x02}};
-    context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+    context.SetTestInputBuffer(
+        {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
     EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
-
-    EXPECT_EQ(sizeof(data), context.skip_size_);
+    EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
     EXPECT_EQ(0x000A, context.width_);
     EXPECT_EQ(0x0F00, context.height_);
     EXPECT_EQ(0u, context.bc_index_);  // bc_index_ is 0 if no global palette
-    EXPECT_EQ(2u, context.pixel_aspect_);
+    context.SetTestInputBuffer({});
   }
   // Missing Global Palette
   {
@@ -282,11 +271,12 @@
       uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
     } data = {{'G', 'I', 'F', '8', '7', 'a'},
               {0x0A, 0x00, 0x00, 0x0F, 0x80, 0x01, 0x02}};
-    context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+    context.SetTestInputBuffer(
+        {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
     EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
-
-    EXPECT_EQ(sizeof(data.signature), context.skip_size_);
+    EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition());
+    context.SetTestInputBuffer({});
   }
   // Success with global palette
   {
@@ -297,19 +287,21 @@
     } data = {{'G', 'I', 'F', '8', '7', 'a'},
               {0x0A, 0x00, 0x00, 0x0F, 0xA9, 0x01, 0x02},
               {0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1}};
-    context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+    context.SetTestInputBuffer(
+        {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
 
     EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
-
-    EXPECT_EQ(sizeof(data), context.skip_size_);
+    EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
     EXPECT_EQ(0x000A, context.width_);
     EXPECT_EQ(0x0F00, context.height_);
     EXPECT_EQ(1u, context.bc_index_);
-    EXPECT_EQ(2u, context.pixel_aspect_);
     EXPECT_EQ(1u, context.global_pal_exp_);
     EXPECT_EQ(1, context.global_sort_flag_);
     EXPECT_EQ(2, context.global_color_resolution_);
-    EXPECT_TRUE(0 == memcmp(data.palette, context.global_palette_.data(),
-                            sizeof(data.palette)));
+    EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(),
+                        sizeof(data.palette)));
+    context.SetTestInputBuffer({});
   }
 }
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.cpp b/core/fxcodec/gif/cfx_lzwdecompressor.cpp
index 1ee7b5a..12e0d89 100644
--- a/core/fxcodec/gif/cfx_lzwdecompressor.cpp
+++ b/core/fxcodec/gif/cfx_lzwdecompressor.cpp
@@ -7,13 +7,12 @@
 #include "core/fxcodec/gif/cfx_lzwdecompressor.h"
 
 #include <algorithm>
+#include <cstring>
 #include <memory>
 #include <utility>
 
-#include "core/fxcodec/lbmp/fx_bmp.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
 std::unique_ptr<CFX_LZWDecompressor> CFX_LZWDecompressor::Create(
     uint8_t color_exp,
@@ -42,14 +41,14 @@
 
 CFX_LZWDecompressor::~CFX_LZWDecompressor() {}
 
-CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(uint8_t* src_buf,
+CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf,
                                                 uint32_t src_size,
-                                                uint8_t* des_buf,
-                                                uint32_t* des_size) {
-  if (!src_buf || src_size == 0 || !des_buf || !des_size)
+                                                uint8_t* dest_buf,
+                                                uint32_t* dest_size) {
+  if (!src_buf || src_size == 0 || !dest_buf || !dest_size)
     return CFX_GifDecodeStatus::Error;
 
-  if (*des_size == 0)
+  if (*dest_size == 0)
     return CFX_GifDecodeStatus::InsufficientDestSize;
 
   next_in_ = src_buf;
@@ -59,15 +58,15 @@
 
   uint32_t i = 0;
   if (decompressed_next_ != 0) {
-    uint32_t extracted_size = ExtractData(des_buf, *des_size);
+    uint32_t extracted_size = ExtractData(dest_buf, *dest_size);
     if (decompressed_next_ != 0)
       return CFX_GifDecodeStatus::InsufficientDestSize;
 
-    des_buf += extracted_size;
+    dest_buf += extracted_size;
     i += extracted_size;
   }
 
-  while (i <= *des_size && (avail_in_ > 0 || bits_left_ >= code_size_cur_)) {
+  while (i <= *dest_size && (avail_in_ > 0 || bits_left_ >= code_size_cur_)) {
     if (code_size_cur_ > GIF_MAX_LZW_EXP)
       return CFX_GifDecodeStatus::Error;
 
@@ -75,7 +74,7 @@
       if (bits_left_ > 31)
         return CFX_GifDecodeStatus::Error;
 
-      pdfium::base::CheckedNumeric<uint32_t> safe_code = *next_in_++;
+      FX_SAFE_UINT32 safe_code = *next_in_++;
       safe_code <<= bits_left_;
       safe_code |= code_store_;
       if (!safe_code.IsValid())
@@ -96,7 +95,7 @@
         continue;
       }
       if (code == code_end_) {
-        *des_size = i;
+        *dest_size = i;
         return CFX_GifDecodeStatus::Success;
       }
 
@@ -122,11 +121,11 @@
       }
 
       code_old_ = code;
-      uint32_t extracted_size = ExtractData(des_buf, *des_size - i);
+      uint32_t extracted_size = ExtractData(dest_buf, *dest_size - i);
       if (decompressed_next_ != 0)
         return CFX_GifDecodeStatus::InsufficientDestSize;
 
-      des_buf += extracted_size;
+      dest_buf += extracted_size;
       i += extracted_size;
     }
   }
@@ -134,7 +133,7 @@
   if (avail_in_ != 0)
     return CFX_GifDecodeStatus::Error;
 
-  *des_size = i;
+  *dest_size = i;
   return CFX_GifDecodeStatus::Unfinished;
 }
 
@@ -182,15 +181,16 @@
   return true;
 }
 
-uint32_t CFX_LZWDecompressor::ExtractData(uint8_t* des_buf, uint32_t des_size) {
-  if (des_size == 0)
+uint32_t CFX_LZWDecompressor::ExtractData(uint8_t* dest_buf,
+                                          uint32_t dest_size) {
+  if (dest_size == 0)
     return 0;
 
-  uint32_t copy_size = des_size <= decompressed_next_
-                           ? des_size
+  uint32_t copy_size = dest_size <= decompressed_next_
+                           ? dest_size
                            : static_cast<uint32_t>(decompressed_next_);
   std::reverse_copy(decompressed_.data() + decompressed_next_ - copy_size,
-                    decompressed_.data() + decompressed_next_, des_buf);
+                    decompressed_.data() + decompressed_next_, dest_buf);
   decompressed_next_ -= copy_size;
   return copy_size;
 }
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.h b/core/fxcodec/gif/cfx_lzwdecompressor.h
index 717c055..d3ec588 100644
--- a/core/fxcodec/gif/cfx_lzwdecompressor.h
+++ b/core/fxcodec/gif/cfx_lzwdecompressor.h
@@ -14,28 +14,27 @@
 
 class CFX_LZWDecompressor {
  public:
-  typedef struct {
+  struct CodeEntry {
     uint16_t prefix;
     uint8_t suffix;
-  } CodeEntry;
+  };
 
   // Returns nullptr on error
   static std::unique_ptr<CFX_LZWDecompressor> Create(uint8_t color_exp,
                                                      uint8_t code_exp);
   ~CFX_LZWDecompressor();
 
-  CFX_GifDecodeStatus Decode(uint8_t* src_buf,
+  CFX_GifDecodeStatus Decode(const uint8_t* src_buf,
                              uint32_t src_size,
-                             uint8_t* des_buf,
-                             uint32_t* des_size);
+                             uint8_t* dest_buf,
+                             uint32_t* dest_size);
 
   // Used by unittests, should not be called in production code.
-  uint32_t ExtractDataForTest(uint8_t* des_buf, uint32_t des_size) {
-    return ExtractData(des_buf, des_size);
+  uint32_t ExtractDataForTest(uint8_t* dest_buf, uint32_t dest_size) {
+    return ExtractData(dest_buf, dest_size);
   }
 
   std::vector<uint8_t>* DecompressedForTest() { return &decompressed_; }
-
   size_t* DecompressedNextForTest() { return &decompressed_next_; }
 
  private:
@@ -43,7 +42,7 @@
   void ClearTable();
   void AddCode(uint16_t prefix_code, uint8_t append_char);
   bool DecodeString(uint16_t code);
-  uint32_t ExtractData(uint8_t* des_buf, uint32_t des_size);
+  uint32_t ExtractData(uint8_t* dest_buf, uint32_t dest_size);
 
   uint8_t code_size_;
   uint8_t code_size_cur_;
@@ -54,12 +53,12 @@
   uint8_t code_first_;
   std::vector<uint8_t> decompressed_;
   size_t decompressed_next_;
-  CodeEntry code_table_[GIF_MAX_LZW_CODE];
   uint16_t code_old_;
-  uint8_t* next_in_;
+  const uint8_t* next_in_;
   uint32_t avail_in_;
   uint8_t bits_left_;
   uint32_t code_store_;
+  CodeEntry code_table_[GIF_MAX_LZW_CODE];
 };
 
 #endif  // CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp b/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp
index 0d0906e..e31fb78 100644
--- a/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp
+++ b/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp
@@ -24,16 +24,16 @@
     std::vector<uint8_t>* decompressed = decompressor->DecompressedForTest();
     *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
     *(decompressor->DecompressedNextForTest()) = decompressed->size();
-    uint8_t des_buf[20];
-    memset(des_buf, static_cast<uint8_t>(-1), sizeof(des_buf));
+    uint8_t dest_buf[20];
+    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
 
-    EXPECT_EQ(0u, decompressor->ExtractDataForTest(des_buf, 0));
-    for (size_t i = 0; i < FX_ArraySize(des_buf); ++i)
-      EXPECT_EQ(static_cast<uint8_t>(-1), des_buf[i]);
+    EXPECT_EQ(0u, decompressor->ExtractDataForTest(dest_buf, 0));
+    for (size_t i = 0; i < FX_ArraySize(dest_buf); ++i)
+      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
 
     EXPECT_EQ(10u, *(decompressor->DecompressedNextForTest()));
     for (size_t i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
-      EXPECT_EQ(i, decompressed->operator[](i));
+      EXPECT_EQ(i, (*decompressed)[i]);
   }
 
   // Check that less than decompressed size only gets the expected number
@@ -41,19 +41,19 @@
     std::vector<uint8_t>* decompressed = decompressor->DecompressedForTest();
     *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
     *(decompressor->DecompressedNextForTest()) = decompressed->size();
-    uint8_t des_buf[20];
-    memset(des_buf, static_cast<uint8_t>(-1), sizeof(des_buf));
+    uint8_t dest_buf[20];
+    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
 
-    EXPECT_EQ(5u, decompressor->ExtractDataForTest(des_buf, 5));
+    EXPECT_EQ(5u, decompressor->ExtractDataForTest(dest_buf, 5));
     size_t i = 0;
     for (; i < 5; ++i)
-      EXPECT_EQ(9 - i, des_buf[i]);
-    for (; i < FX_ArraySize(des_buf); ++i)
-      EXPECT_EQ(static_cast<uint8_t>(-1), des_buf[i]);
+      EXPECT_EQ(9 - i, dest_buf[i]);
+    for (; i < FX_ArraySize(dest_buf); ++i)
+      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
 
     EXPECT_EQ(5u, *(decompressor->DecompressedNextForTest()));
     for (i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
-      EXPECT_EQ(i, decompressed->operator[](i));
+      EXPECT_EQ(i, (*decompressed)[i]);
   }
 
   // Check that greater than decompressed size depletes the decompressor
@@ -61,16 +61,16 @@
     std::vector<uint8_t>* decompressed = decompressor->DecompressedForTest();
     *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
     *(decompressor->DecompressedNextForTest()) = decompressed->size();
-    uint8_t des_buf[20];
-    memset(des_buf, static_cast<uint8_t>(-1), sizeof(des_buf));
+    uint8_t dest_buf[20];
+    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
 
-    EXPECT_EQ(10u,
-              decompressor->ExtractDataForTest(des_buf, FX_ArraySize(des_buf)));
+    EXPECT_EQ(10u, decompressor->ExtractDataForTest(dest_buf,
+                                                    FX_ArraySize(dest_buf)));
     size_t i = 0;
     for (; i < 10; ++i)
-      EXPECT_EQ(9 - i, des_buf[i]);
-    for (; i < FX_ArraySize(des_buf); ++i)
-      EXPECT_EQ(static_cast<uint8_t>(-1), des_buf[i]);
+      EXPECT_EQ(9 - i, dest_buf[i]);
+    for (; i < FX_ArraySize(dest_buf); ++i)
+      EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
 
     EXPECT_EQ(0u, *(decompressor->DecompressedNextForTest()));
   }
@@ -133,30 +133,30 @@
   auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
   ASSERT_NE(nullptr, decompressor);
 
-  uint8_t image_data[] = {0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x0F, 0x63, 0x2B};
-  uint32_t image_size = FX_ArraySize(image_data);
+  static constexpr uint8_t kImageData[] = {0x84, 0x8F, 0xA9, 0xCB,
+                                           0xED, 0x0F, 0x63, 0x2B};
+  uint32_t image_size = FX_ArraySize(kImageData);
 
-  uint8_t expected_data[] = {
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-  uint8_t output_data[FX_ArraySize(expected_data)];
+  static constexpr uint8_t kExpectedData[] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00};
+  uint8_t output_data[FX_ArraySize(kExpectedData)];
   memset(output_data, 0, sizeof(output_data));
   uint32_t output_size = FX_ArraySize(output_data);
 
   EXPECT_EQ(
       CFX_GifDecodeStatus::Success,
-      decompressor->Decode(image_data, image_size, output_data, &output_size));
+      decompressor->Decode(kImageData, image_size, output_data, &output_size));
 
   EXPECT_EQ(FX_ArraySize(output_data), output_size);
-  EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data)));
+  EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData)));
 }
 
 TEST(CFX_LZWDecompressor, Decode10x10MultipleColour) {
@@ -165,33 +165,32 @@
   auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
   ASSERT_NE(nullptr, decompressor);
 
-  uint8_t image_data[] = {0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33,
-                          0xA0, 0x02, 0x75, 0xEC, 0x95, 0xFA, 0xA8, 0xDE,
-                          0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01};
-  uint32_t image_size = FX_ArraySize(image_data);
+  static constexpr uint8_t kImageData[] = {
+      0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
+      0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01};
+  uint32_t image_size = FX_ArraySize(kImageData);
 
-  uint8_t expected_data[] = {
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
-    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02,
-    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02,
-    0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
-    0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
-    0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01};
+  static constexpr uint8_t kExpectedData[] = {
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02,
+      0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+      0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02,
+      0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+      0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,
+      0x01, 0x01, 0x01, 0x01};
 
-  uint8_t output_data[FX_ArraySize(expected_data)];
+  uint8_t output_data[FX_ArraySize(kExpectedData)];
   memset(output_data, 0, sizeof(output_data));
   uint32_t output_size = FX_ArraySize(output_data);
 
   EXPECT_EQ(
       CFX_GifDecodeStatus::Success,
-      decompressor->Decode(image_data, image_size, output_data, &output_size));
+      decompressor->Decode(kImageData, image_size, output_data, &output_size));
 
   EXPECT_EQ(FX_ArraySize(output_data), output_size);
-  EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data)));
+  EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData)));
 }
 
 TEST(CFX_LZWDecompressor, HandleColourCodeOutOfPalette) {
@@ -201,11 +200,11 @@
   auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp);
   ASSERT_NE(nullptr, decompressor);
 
-  uint8_t image_data[] = {0x30, 0xC9, 0x49, 0x81, 0xBD, 0x78, 0xE8, 0xCD,
-                          0x89, 0xFF, 0x60, 0x20, 0x8E, 0xE4, 0x61, 0x9E,
-                          0xA8, 0xA1, 0xAE, 0x2C, 0xE2, 0xBE, 0xB0, 0x20,
-                          0xCF, 0x74, 0x61, 0xDF, 0x78, 0x04};
-  uint32_t image_size = FX_ArraySize(image_data);
+  static constexpr uint8_t kImageData[] = {
+      0x30, 0xC9, 0x49, 0x81, 0xBD, 0x78, 0xE8, 0xCD, 0x89, 0xFF,
+      0x60, 0x20, 0x8E, 0xE4, 0x61, 0x9E, 0xA8, 0xA1, 0xAE, 0x2C,
+      0xE2, 0xBE, 0xB0, 0x20, 0xCF, 0x74, 0x61, 0xDF, 0x78, 0x04};
+  uint32_t image_size = FX_ArraySize(kImageData);
 
   uint8_t output_data[100];  // The uncompressed data is for a 10x10 image
   memset(output_data, 0, sizeof(output_data));
@@ -213,5 +212,5 @@
 
   EXPECT_EQ(
       CFX_GifDecodeStatus::Error,
-      decompressor->Decode(image_data, image_size, output_data, &output_size));
+      decompressor->Decode(kImageData, image_size, output_data, &output_size));
 }
diff --git a/core/fxcodec/gif/gifmodule.cpp b/core/fxcodec/gif/gifmodule.cpp
new file mode 100644
index 0000000..041aa36
--- /dev/null
+++ b/core/fxcodec/gif/gifmodule.cpp
@@ -0,0 +1,71 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/gif/gifmodule.h"
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/gif/cfx_gif.h"
+#include "core/fxcodec/gif/cfx_gifcontext.h"
+#include "core/fxge/fx_dib.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcodec {
+
+GifModule::GifModule() = default;
+
+GifModule::~GifModule() = default;
+
+std::unique_ptr<ModuleIface::Context> GifModule::Start(Delegate* pDelegate) {
+  return pdfium::MakeUnique<CFX_GifContext>(this, pDelegate);
+}
+
+CFX_GifDecodeStatus GifModule::ReadHeader(Context* pContext,
+                                          int* width,
+                                          int* height,
+                                          int* pal_num,
+                                          CFX_GifPalette** pal_pp,
+                                          int* bg_index) {
+  auto* context = static_cast<CFX_GifContext*>(pContext);
+  CFX_GifDecodeStatus ret = context->ReadHeader();
+  if (ret != CFX_GifDecodeStatus::Success)
+    return ret;
+
+  *width = context->width_;
+  *height = context->height_;
+  *pal_num = (2 << context->global_pal_exp_);
+  *pal_pp = context->global_palette_.empty() ? nullptr
+                                             : context->global_palette_.data();
+  *bg_index = context->bc_index_;
+  return CFX_GifDecodeStatus::Success;
+}
+
+std::pair<CFX_GifDecodeStatus, size_t> GifModule::LoadFrameInfo(
+    Context* pContext) {
+  auto* context = static_cast<CFX_GifContext*>(pContext);
+  CFX_GifDecodeStatus ret = context->GetFrame();
+  if (ret != CFX_GifDecodeStatus::Success)
+    return {ret, 0};
+  return {CFX_GifDecodeStatus::Success, context->GetFrameNum()};
+}
+
+CFX_GifDecodeStatus GifModule::LoadFrame(Context* pContext, size_t frame_num) {
+  return static_cast<CFX_GifContext*>(pContext)->LoadFrame(frame_num);
+}
+
+FX_FILESIZE GifModule::GetAvailInput(Context* pContext) const {
+  return static_cast<CFX_GifContext*>(pContext)->GetAvailInput();
+}
+
+bool GifModule::Input(Context* pContext,
+                      RetainPtr<CFX_CodecMemory> codec_memory,
+                      CFX_DIBAttribute*) {
+  auto* ctx = static_cast<CFX_GifContext*>(pContext);
+  ctx->SetInputBuffer(std::move(codec_memory));
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/gif/gifmodule.h b/core/fxcodec/gif/gifmodule.h
new file mode 100644
index 0000000..2944e34
--- /dev/null
+++ b/core/fxcodec/gif/gifmodule.h
@@ -0,0 +1,62 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_GIF_GIFMODULE_H_
+#define CORE_FXCODEC_GIF_GIFMODULE_H_
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcodec/codec_module_iface.h"
+#include "core/fxcodec/gif/cfx_gif.h"
+#include "core/fxcrt/fx_coordinates.h"
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class GifModule final : public ModuleIface {
+ public:
+  class Delegate {
+   public:
+    virtual void GifRecordCurrentPosition(uint32_t& cur_pos) = 0;
+    virtual bool GifInputRecordPositionBuf(uint32_t rcd_pos,
+                                           const FX_RECT& img_rc,
+                                           int32_t pal_num,
+                                           CFX_GifPalette* pal_ptr,
+                                           int32_t delay_time,
+                                           bool user_input,
+                                           int32_t trans_index,
+                                           int32_t disposal_method,
+                                           bool interlace) = 0;
+    virtual void GifReadScanline(int32_t row_num, uint8_t* row_buf) = 0;
+  };
+
+  GifModule();
+  ~GifModule() override;
+
+  // ModuleIface:
+  FX_FILESIZE GetAvailInput(Context* context) const override;
+  bool Input(Context* context,
+             RetainPtr<CFX_CodecMemory> codec_memory,
+             CFX_DIBAttribute* pAttribute) override;
+
+  std::unique_ptr<Context> Start(Delegate* pDelegate);
+  CFX_GifDecodeStatus ReadHeader(Context* context,
+                                 int* width,
+                                 int* height,
+                                 int* pal_num,
+                                 CFX_GifPalette** pal_pp,
+                                 int* bg_index);
+  std::pair<CFX_GifDecodeStatus, size_t> LoadFrameInfo(Context* context);
+  CFX_GifDecodeStatus LoadFrame(Context* context, size_t frame_num);
+};
+
+}  // namespace fxcodec
+
+using GifModule = fxcodec::GifModule;
+
+#endif  // CORE_FXCODEC_GIF_GIFMODULE_H_
diff --git a/core/fxcodec/icc/DEPS b/core/fxcodec/icc/DEPS
new file mode 100644
index 0000000..b1d9522
--- /dev/null
+++ b/core/fxcodec/icc/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/lcms',
+]
diff --git a/core/fxcodec/icc/iccmodule.cpp b/core/fxcodec/icc/iccmodule.cpp
new file mode 100644
index 0000000..e9a8cd6
--- /dev/null
+++ b/core/fxcodec/icc/iccmodule.cpp
@@ -0,0 +1,150 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/icc/iccmodule.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+namespace fxcodec {
+
+namespace {
+
+// For use with std::unique_ptr<cmsHPROFILE>.
+struct CmsProfileDeleter {
+  inline void operator()(cmsHPROFILE p) { cmsCloseProfile(p); }
+};
+
+using ScopedCmsProfile = std::unique_ptr<void, CmsProfileDeleter>;
+
+bool Check3Components(cmsColorSpaceSignature cs) {
+  switch (cs) {
+    case cmsSigGrayData:
+    case cmsSigCmykData:
+      return false;
+    default:
+      return true;
+  }
+}
+
+}  // namespace
+
+CLcmsCmm::CLcmsCmm(cmsHTRANSFORM hTransform,
+                   int srcComponents,
+                   bool bIsLab,
+                   bool bNormal)
+    : m_hTransform(hTransform),
+      m_nSrcComponents(srcComponents),
+      m_bLab(bIsLab),
+      m_bNormal(bNormal) {}
+
+CLcmsCmm::~CLcmsCmm() {
+  cmsDeleteTransform(m_hTransform);
+}
+
+// static
+std::unique_ptr<CLcmsCmm> IccModule::CreateTransformSRGB(
+    pdfium::span<const uint8_t> span) {
+  ScopedCmsProfile srcProfile(cmsOpenProfileFromMem(span.data(), span.size()));
+  if (!srcProfile)
+    return nullptr;
+
+  ScopedCmsProfile dstProfile(cmsCreate_sRGBProfile());
+  if (!dstProfile)
+    return nullptr;
+
+  cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile.get());
+
+  uint32_t nSrcComponents = cmsChannelsOf(srcCS);
+  // According to PDF spec, number of components must be 1, 3, or 4.
+  if (nSrcComponents != 1 && nSrcComponents != 3 && nSrcComponents != 4)
+    return nullptr;
+
+  int srcFormat;
+  bool bLab = false;
+  bool bNormal = false;
+  if (srcCS == cmsSigLabData) {
+    srcFormat =
+        COLORSPACE_SH(PT_Lab) | CHANNELS_SH(nSrcComponents) | BYTES_SH(0);
+    bLab = true;
+  } else {
+    srcFormat =
+        COLORSPACE_SH(PT_ANY) | CHANNELS_SH(nSrcComponents) | BYTES_SH(1);
+    // TODO(thestig): Check to see if lcms2 supports more colorspaces that can
+    // be considered normal.
+    bNormal = srcCS == cmsSigGrayData || srcCS == cmsSigRgbData ||
+              srcCS == cmsSigCmykData;
+  }
+  cmsColorSpaceSignature dstCS = cmsGetColorSpace(dstProfile.get());
+  if (!Check3Components(dstCS))
+    return nullptr;
+
+  cmsHTRANSFORM hTransform = nullptr;
+  const int intent = 0;
+  switch (dstCS) {
+    case cmsSigRgbData:
+      hTransform = cmsCreateTransform(srcProfile.get(), srcFormat,
+                                      dstProfile.get(), TYPE_BGR_8, intent, 0);
+      break;
+    case cmsSigGrayData:
+    case cmsSigCmykData:
+      // Check3Components() already filtered these types.
+      NOTREACHED();
+      break;
+    default:
+      break;
+  }
+  if (!hTransform)
+    return nullptr;
+
+  return pdfium::MakeUnique<CLcmsCmm>(hTransform, nSrcComponents, bLab,
+                                      bNormal);
+}
+
+// static
+void IccModule::Translate(CLcmsCmm* pTransform,
+                          uint32_t nSrcComponents,
+                          const float* pSrcValues,
+                          float* pDestValues) {
+  if (!pTransform)
+    return;
+
+  uint8_t output[4];
+  // TODO(npm): Currently the CmsDoTransform method is part of LCMS and it will
+  // apply some member of m_hTransform to the input. We need to go over all the
+  // places which set transform to verify that only |nSrcComponents| are used.
+  if (pTransform->IsLab()) {
+    std::vector<double> inputs(std::max(nSrcComponents, 16u));
+    for (uint32_t i = 0; i < nSrcComponents; ++i)
+      inputs[i] = pSrcValues[i];
+    cmsDoTransform(pTransform->transform(), inputs.data(), output, 1);
+  } else {
+    std::vector<uint8_t> inputs(std::max(nSrcComponents, 16u));
+    for (uint32_t i = 0; i < nSrcComponents; ++i) {
+      inputs[i] =
+          pdfium::clamp(static_cast<int>(pSrcValues[i] * 255.0f), 0, 255);
+    }
+    cmsDoTransform(pTransform->transform(), inputs.data(), output, 1);
+  }
+  pDestValues[0] = output[2] / 255.0f;
+  pDestValues[1] = output[1] / 255.0f;
+  pDestValues[2] = output[0] / 255.0f;
+}
+
+// static
+void IccModule::TranslateScanline(CLcmsCmm* pTransform,
+                                  unsigned char* pDest,
+                                  const unsigned char* pSrc,
+                                  int32_t pixels) {
+  if (pTransform)
+    cmsDoTransform(pTransform->transform(), pSrc, pDest, pixels);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/icc/iccmodule.h b/core/fxcodec/icc/iccmodule.h
new file mode 100644
index 0000000..a031172
--- /dev/null
+++ b/core/fxcodec/icc/iccmodule.h
@@ -0,0 +1,68 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_ICC_ICCMODULE_H_
+#define CORE_FXCODEC_ICC_ICCMODULE_H_
+
+#include <memory>
+
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
+
+#if defined(USE_SYSTEM_LCMS2)
+#include <lcms2.h>
+#else
+#include "third_party/lcms/include/lcms2.h"
+#endif
+
+namespace fxcodec {
+
+class CLcmsCmm {
+ public:
+  CLcmsCmm(cmsHTRANSFORM transform,
+           int srcComponents,
+           bool bIsLab,
+           bool bNormal);
+  ~CLcmsCmm();
+
+  cmsHTRANSFORM transform() const { return m_hTransform; }
+  int components() const { return m_nSrcComponents; }
+  bool IsLab() const { return m_bLab; }
+  bool IsNormal() const { return m_bNormal; }
+
+ private:
+  const cmsHTRANSFORM m_hTransform;
+  const int m_nSrcComponents;
+  const bool m_bLab;
+  const bool m_bNormal;
+};
+
+class IccModule {
+ public:
+  static std::unique_ptr<CLcmsCmm> CreateTransformSRGB(
+      pdfium::span<const uint8_t> span);
+  static void Translate(CLcmsCmm* pTransform,
+                        uint32_t nSrcComponents,
+                        const float* pSrcValues,
+                        float* pDestValues);
+  static void TranslateScanline(CLcmsCmm* pTransform,
+                                uint8_t* pDest,
+                                const uint8_t* pSrc,
+                                int pixels);
+
+  IccModule() = delete;
+  IccModule(const IccModule&) = delete;
+  IccModule& operator=(const IccModule&) = delete;
+};
+
+}  // namespace fxcodec
+
+using CLcmsCmm = fxcodec::CLcmsCmm;
+using IccModule = fxcodec::IccModule;
+
+#endif  // CORE_FXCODEC_ICC_ICCMODULE_H_
diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
index deba72b..8d3fc5a 100644
--- a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
+++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
@@ -11,52 +11,48 @@
 
 namespace {
 
-struct JBig2ArithQe {
-  unsigned int Qe;
-  unsigned int NMPS;
-  unsigned int NLPS;
-  unsigned int nSwitch;
-};
-
-const JBig2ArithQe kQeTable[] = {
+const JBig2ArithCtx::JBig2ArithQe kQeTable[] = {
     // Stupid hack to keep clang-format from reformatting this badly.
-    {0x5601, 1, 1, 1},   {0x3401, 2, 6, 0},   {0x1801, 3, 9, 0},
-    {0x0AC1, 4, 12, 0},  {0x0521, 5, 29, 0},  {0x0221, 38, 33, 0},
-    {0x5601, 7, 6, 1},   {0x5401, 8, 14, 0},  {0x4801, 9, 14, 0},
-    {0x3801, 10, 14, 0}, {0x3001, 11, 17, 0}, {0x2401, 12, 18, 0},
-    {0x1C01, 13, 20, 0}, {0x1601, 29, 21, 0}, {0x5601, 15, 14, 1},
-    {0x5401, 16, 14, 0}, {0x5101, 17, 15, 0}, {0x4801, 18, 16, 0},
-    {0x3801, 19, 17, 0}, {0x3401, 20, 18, 0}, {0x3001, 21, 19, 0},
-    {0x2801, 22, 19, 0}, {0x2401, 23, 20, 0}, {0x2201, 24, 21, 0},
-    {0x1C01, 25, 22, 0}, {0x1801, 26, 23, 0}, {0x1601, 27, 24, 0},
-    {0x1401, 28, 25, 0}, {0x1201, 29, 26, 0}, {0x1101, 30, 27, 0},
-    {0x0AC1, 31, 28, 0}, {0x09C1, 32, 29, 0}, {0x08A1, 33, 30, 0},
-    {0x0521, 34, 31, 0}, {0x0441, 35, 32, 0}, {0x02A1, 36, 33, 0},
-    {0x0221, 37, 34, 0}, {0x0141, 38, 35, 0}, {0x0111, 39, 36, 0},
-    {0x0085, 40, 37, 0}, {0x0049, 41, 38, 0}, {0x0025, 42, 39, 0},
-    {0x0015, 43, 40, 0}, {0x0009, 44, 41, 0}, {0x0005, 45, 42, 0},
-    {0x0001, 45, 43, 0}, {0x5601, 46, 46, 0}};
+    {0x5601, 1, 1, true},    {0x3401, 2, 6, false},   {0x1801, 3, 9, false},
+    {0x0AC1, 4, 12, false},  {0x0521, 5, 29, false},  {0x0221, 38, 33, false},
+    {0x5601, 7, 6, true},    {0x5401, 8, 14, false},  {0x4801, 9, 14, false},
+    {0x3801, 10, 14, false}, {0x3001, 11, 17, false}, {0x2401, 12, 18, false},
+    {0x1C01, 13, 20, false}, {0x1601, 29, 21, false}, {0x5601, 15, 14, true},
+    {0x5401, 16, 14, false}, {0x5101, 17, 15, false}, {0x4801, 18, 16, false},
+    {0x3801, 19, 17, false}, {0x3401, 20, 18, false}, {0x3001, 21, 19, false},
+    {0x2801, 22, 19, false}, {0x2401, 23, 20, false}, {0x2201, 24, 21, false},
+    {0x1C01, 25, 22, false}, {0x1801, 26, 23, false}, {0x1601, 27, 24, false},
+    {0x1401, 28, 25, false}, {0x1201, 29, 26, false}, {0x1101, 30, 27, false},
+    {0x0AC1, 31, 28, false}, {0x09C1, 32, 29, false}, {0x08A1, 33, 30, false},
+    {0x0521, 34, 31, false}, {0x0441, 35, 32, false}, {0x02A1, 36, 33, false},
+    {0x0221, 37, 34, false}, {0x0141, 38, 35, false}, {0x0111, 39, 36, false},
+    {0x0085, 40, 37, false}, {0x0049, 41, 38, false}, {0x0025, 42, 39, false},
+    {0x0015, 43, 40, false}, {0x0009, 44, 41, false}, {0x0005, 45, 42, false},
+    {0x0001, 45, 43, false}, {0x5601, 46, 46, false}};
 
 const unsigned int kDefaultAValue = 0x8000;
 
-int DecodeNMPS(JBig2ArithCtx* pCX, const JBig2ArithQe& qe) {
-  pCX->I = qe.NMPS;
-  return pCX->MPS;
-}
+}  // namespace
 
-int DecodeNLPS(JBig2ArithCtx* pCX, const JBig2ArithQe& qe) {
-  // TODO(thestig): |D|, |MPS| and friends probably should be booleans.
-  int D = 1 - pCX->MPS;
-  if (qe.nSwitch == 1)
-    pCX->MPS = 1 - pCX->MPS;
-  pCX->I = qe.NLPS;
+JBig2ArithCtx::JBig2ArithCtx() = default;
+
+int JBig2ArithCtx::DecodeNLPS(const JBig2ArithQe& qe) {
+  bool D = !m_MPS;
+  if (qe.bSwitch)
+    m_MPS = !m_MPS;
+  m_I = qe.NLPS;
+  ASSERT(m_I < FX_ArraySize(kQeTable));
   return D;
 }
 
-}  // namespace
+int JBig2ArithCtx::DecodeNMPS(const JBig2ArithQe& qe) {
+  m_I = qe.NMPS;
+  ASSERT(m_I < FX_ArraySize(kQeTable));
+  return MPS();
+}
 
 CJBig2_ArithDecoder::CJBig2_ArithDecoder(CJBig2_BitStream* pStream)
-    : m_Complete(false), m_FinishedStream(false), m_pStream(pStream) {
+    : m_pStream(pStream) {
   m_B = m_pStream->getCurByte_arith();
   m_C = (m_B ^ 0xff) << 16;
   BYTEIN();
@@ -67,40 +63,49 @@
 
 CJBig2_ArithDecoder::~CJBig2_ArithDecoder() {}
 
-int CJBig2_ArithDecoder::DECODE(JBig2ArithCtx* pCX) {
-  if (!pCX || pCX->I >= FX_ArraySize(kQeTable))
-    return 0;
+int CJBig2_ArithDecoder::Decode(JBig2ArithCtx* pCX) {
+  ASSERT(pCX);
+  ASSERT(pCX->I() < FX_ArraySize(kQeTable));
 
-  const JBig2ArithQe& qe = kQeTable[pCX->I];
+  const JBig2ArithCtx::JBig2ArithQe& qe = kQeTable[pCX->I()];
   m_A -= qe.Qe;
   if ((m_C >> 16) < m_A) {
     if (m_A & kDefaultAValue)
-      return pCX->MPS;
+      return pCX->MPS();
 
-    const int D = m_A < qe.Qe ? DecodeNLPS(pCX, qe) : DecodeNMPS(pCX, qe);
+    const int D = m_A < qe.Qe ? pCX->DecodeNLPS(qe) : pCX->DecodeNMPS(qe);
     ReadValueA();
     return D;
   }
 
   m_C -= m_A << 16;
-  const int D = m_A < qe.Qe ? DecodeNMPS(pCX, qe) : DecodeNLPS(pCX, qe);
+  const int D = m_A < qe.Qe ? pCX->DecodeNMPS(qe) : pCX->DecodeNLPS(qe);
   m_A = qe.Qe;
   ReadValueA();
   return D;
 }
 
 void CJBig2_ArithDecoder::BYTEIN() {
-  unsigned char B1;
   if (m_B == 0xff) {
-    B1 = m_pStream->getNextByte_arith();
+    unsigned char B1 = m_pStream->getNextByte_arith();
     if (B1 > 0x8f) {
       m_CT = 8;
-      // If we are here, it means that we have finished decoding data (see JBIG2
-      // spec, Section E.3.4). If we arrive here a second time, we're looping,
-      // so complete decoding.
-      if (m_FinishedStream)
-        m_Complete = true;
-      m_FinishedStream = true;
+
+      switch (m_State) {
+        case StreamState::kDataAvailable:
+          // Finished decoding data (see JBIG2 spec, Section E.3.4).
+          m_State = StreamState::kDecodingFinished;
+          break;
+        case StreamState::kDecodingFinished:
+          // Allow one more call in the finished state. https://crbug.com/947622
+          m_State = StreamState::kLooping;
+          break;
+        case StreamState::kLooping:
+          // Looping state detected. Mark decoding as complete to bail out.
+          // https://crbug.com/767156
+          m_Complete = true;
+          break;
+      }
     } else {
       m_pStream->incByteIdx();
       m_B = B1;
diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.h b/core/fxcodec/jbig2/JBig2_ArithDecoder.h
index c9e29a5..9731ac6 100644
--- a/core/fxcodec/jbig2/JBig2_ArithDecoder.h
+++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.h
@@ -12,30 +12,51 @@
 #include "core/fxcrt/unowned_ptr.h"
 
 class CJBig2_BitStream;
+struct JBig2ArithQe;
 
-struct JBig2ArithCtx {
-  JBig2ArithCtx() : MPS(0), I(0) {}
+class JBig2ArithCtx {
+ public:
+  struct JBig2ArithQe {
+    uint16_t Qe;
+    uint8_t NMPS;
+    uint8_t NLPS;
+    bool bSwitch;
+  };
 
-  unsigned int MPS;
-  unsigned int I;
+  JBig2ArithCtx();
+
+  int DecodeNLPS(const JBig2ArithQe& qe);
+  int DecodeNMPS(const JBig2ArithQe& qe);
+
+  unsigned int MPS() const { return m_MPS ? 1 : 0; }
+  unsigned int I() const { return m_I; }
+
+ private:
+  bool m_MPS = 0;
+  unsigned int m_I = 0;
 };
 
 class CJBig2_ArithDecoder {
  public:
   explicit CJBig2_ArithDecoder(CJBig2_BitStream* pStream);
-
   ~CJBig2_ArithDecoder();
 
-  int DECODE(JBig2ArithCtx* pCX);
+  int Decode(JBig2ArithCtx* pCX);
 
   bool IsComplete() const { return m_Complete; }
 
  private:
+  enum class StreamState : uint8_t {
+    kDataAvailable,
+    kDecodingFinished,
+    kLooping,
+  };
+
   void BYTEIN();
   void ReadValueA();
 
-  bool m_Complete;
-  bool m_FinishedStream;
+  bool m_Complete = false;
+  StreamState m_State = StreamState::kDataAvailable;
   uint8_t m_B;
   unsigned int m_C;
   unsigned int m_A;
diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp
index 7ed7702..0d09b31 100644
--- a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp
+++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp
@@ -32,7 +32,7 @@
     return kDepthEnd;
 
   JBig2ArithCtx* pCX = &(*context)[*prev];
-  int D = decoder->DECODE(pCX);
+  int D = decoder->Decode(pCX);
   *prev = ShiftOr(*prev, D);
   if (!D)
     return depth;
@@ -47,13 +47,13 @@
 
 CJBig2_ArithIntDecoder::~CJBig2_ArithIntDecoder() {}
 
-bool CJBig2_ArithIntDecoder::decode(CJBig2_ArithDecoder* pArithDecoder,
+bool CJBig2_ArithIntDecoder::Decode(CJBig2_ArithDecoder* pArithDecoder,
                                     int* nResult) {
   // This decoding algorithm is explained in "Annex A - Arithmetic Integer
   // Decoding Procedure" on page 113 of the JBIG2 specification (ISO/IEC FCD
   // 14492).
   int PREV = 1;
-  const int S = pArithDecoder->DECODE(&m_IAx[PREV]);
+  const int S = pArithDecoder->Decode(&m_IAx[PREV]);
   PREV = ShiftOr(PREV, S);
 
   const size_t nDecodeDataIndex =
@@ -61,7 +61,7 @@
 
   int nTemp = 0;
   for (int i = 0; i < g_ArithIntDecodeData[nDecodeDataIndex].nNeedBits; ++i) {
-    int D = pArithDecoder->DECODE(&m_IAx[PREV]);
+    int D = pArithDecoder->Decode(&m_IAx[PREV]);
     PREV = ShiftOr(PREV, D);
     if (PREV >= 256)
       PREV = (PREV & 511) | 256;
@@ -92,12 +92,12 @@
 
 CJBig2_ArithIaidDecoder::~CJBig2_ArithIaidDecoder() {}
 
-void CJBig2_ArithIaidDecoder::decode(CJBig2_ArithDecoder* pArithDecoder,
+void CJBig2_ArithIaidDecoder::Decode(CJBig2_ArithDecoder* pArithDecoder,
                                      uint32_t* nResult) {
   int PREV = 1;
   for (unsigned char i = 0; i < SBSYMCODELEN; ++i) {
     JBig2ArithCtx* pCX = &m_IAID[PREV];
-    int D = pArithDecoder->DECODE(pCX);
+    int D = pArithDecoder->Decode(pCX);
     PREV = ShiftOr(PREV, D);
   }
   *nResult = PREV - (1 << SBSYMCODELEN);
diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h
index fd9fa89..2221767 100644
--- a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h
+++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h
@@ -19,7 +19,7 @@
 
   // Returns true on success, and false when an OOB condition occurs. Many
   // callers can tolerate OOB and do not check the return value.
-  bool decode(CJBig2_ArithDecoder* pArithDecoder, int* nResult);
+  bool Decode(CJBig2_ArithDecoder* pArithDecoder, int* nResult);
 
  private:
   std::vector<JBig2ArithCtx> m_IAx;
@@ -30,7 +30,7 @@
   explicit CJBig2_ArithIaidDecoder(unsigned char SBSYMCODELENA);
   ~CJBig2_ArithIaidDecoder();
 
-  void decode(CJBig2_ArithDecoder* pArithDecoder, uint32_t* nResult);
+  void Decode(CJBig2_ArithDecoder* pArithDecoder, uint32_t* nResult);
 
  private:
   std::vector<JBig2ArithCtx> m_IAID;
diff --git a/core/fxcodec/jbig2/JBig2_BitStream.cpp b/core/fxcodec/jbig2/JBig2_BitStream.cpp
index a72099d..fb2a888 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream.cpp
+++ b/core/fxcodec/jbig2/JBig2_BitStream.cpp
@@ -8,23 +8,21 @@
 
 #include <algorithm>
 
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+namespace {
 
-CJBig2_BitStream::CJBig2_BitStream(const RetainPtr<CPDF_StreamAcc>& pSrcStream)
-    : m_pBuf(pSrcStream->GetData()),
-      m_dwLength(pSrcStream->GetSize()),
-      m_dwByteIdx(0),
-      m_dwBitIdx(0),
-      m_dwObjNum(pSrcStream->GetStream() ? pSrcStream->GetStream()->GetObjNum()
-                                         : 0) {
-  if (m_dwLength > 256 * 1024 * 1024) {
-    m_dwLength = 0;
-    m_pBuf = nullptr;
-  }
+pdfium::span<const uint8_t> ValidatedSpan(pdfium::span<const uint8_t> sp) {
+  if (sp.size() > 256 * 1024 * 1024)
+    return {};
+  return sp;
 }
 
-CJBig2_BitStream::~CJBig2_BitStream() {}
+}  // namespace
+
+CJBig2_BitStream::CJBig2_BitStream(pdfium::span<const uint8_t> pSrcStream,
+                                   uint32_t dwObjNum)
+    : m_Span(ValidatedSpan(pSrcStream)), m_dwObjNum(dwObjNum) {}
+
+CJBig2_BitStream::~CJBig2_BitStream() = default;
 
 int32_t CJBig2_BitStream::readNBits(uint32_t dwBits, uint32_t* dwResult) {
   if (!IsInBounds())
@@ -42,7 +40,7 @@
 
   for (; dwBitPos > 0; --dwBitPos) {
     *dwResult =
-        (*dwResult << 1) | ((m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01);
+        (*dwResult << 1) | ((m_Span[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01);
     AdvanceBit();
   }
   return 0;
@@ -64,7 +62,7 @@
 
   for (; dwBitPos > 0; --dwBitPos) {
     *nResult =
-        (*nResult << 1) | ((m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01);
+        (*nResult << 1) | ((m_Span[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01);
     AdvanceBit();
   }
   return 0;
@@ -74,7 +72,7 @@
   if (!IsInBounds())
     return -1;
 
-  *dwResult = (m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01;
+  *dwResult = (m_Span[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01;
   AdvanceBit();
   return 0;
 }
@@ -83,7 +81,7 @@
   if (!IsInBounds())
     return -1;
 
-  *bResult = (m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01;
+  *bResult = (m_Span[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01;
   AdvanceBit();
   return 0;
 }
@@ -92,26 +90,26 @@
   if (!IsInBounds())
     return -1;
 
-  *cResult = m_pBuf[m_dwByteIdx];
+  *cResult = m_Span[m_dwByteIdx];
   ++m_dwByteIdx;
   return 0;
 }
 
 int32_t CJBig2_BitStream::readInteger(uint32_t* dwResult) {
-  if (m_dwByteIdx + 3 >= m_dwLength)
+  if (m_dwByteIdx + 3 >= m_Span.size())
     return -1;
 
-  *dwResult = (m_pBuf[m_dwByteIdx] << 24) | (m_pBuf[m_dwByteIdx + 1] << 16) |
-              (m_pBuf[m_dwByteIdx + 2] << 8) | m_pBuf[m_dwByteIdx + 3];
+  *dwResult = (m_Span[m_dwByteIdx] << 24) | (m_Span[m_dwByteIdx + 1] << 16) |
+              (m_Span[m_dwByteIdx + 2] << 8) | m_Span[m_dwByteIdx + 3];
   m_dwByteIdx += 4;
   return 0;
 }
 
 int32_t CJBig2_BitStream::readShortInteger(uint16_t* dwResult) {
-  if (m_dwByteIdx + 1 >= m_dwLength)
+  if (m_dwByteIdx + 1 >= m_Span.size())
     return -1;
 
-  *dwResult = (m_pBuf[m_dwByteIdx] << 8) | m_pBuf[m_dwByteIdx + 1];
+  *dwResult = (m_Span[m_dwByteIdx] << 8) | m_Span[m_dwByteIdx + 1];
   m_dwByteIdx += 2;
   return 0;
 }
@@ -124,7 +122,7 @@
 }
 
 uint8_t CJBig2_BitStream::getCurByte() const {
-  return IsInBounds() ? m_pBuf[m_dwByteIdx] : 0;
+  return IsInBounds() ? m_Span[m_dwByteIdx] : 0;
 }
 
 void CJBig2_BitStream::incByteIdx() {
@@ -133,11 +131,11 @@
 }
 
 uint8_t CJBig2_BitStream::getCurByte_arith() const {
-  return IsInBounds() ? m_pBuf[m_dwByteIdx] : 0xFF;
+  return IsInBounds() ? m_Span[m_dwByteIdx] : 0xFF;
 }
 
 uint8_t CJBig2_BitStream::getNextByte_arith() const {
-  return m_dwByteIdx + 1 < m_dwLength ? m_pBuf[m_dwByteIdx + 1] : 0xFF;
+  return m_dwByteIdx + 1 < m_Span.size() ? m_Span[m_dwByteIdx + 1] : 0xFF;
 }
 
 uint32_t CJBig2_BitStream::getOffset() const {
@@ -145,7 +143,7 @@
 }
 
 void CJBig2_BitStream::setOffset(uint32_t dwOffset) {
-  m_dwByteIdx = std::min(dwOffset, m_dwLength);
+  m_dwByteIdx = std::min<size_t>(dwOffset, m_Span.size());
 }
 
 uint32_t CJBig2_BitStream::getBitPos() const {
@@ -158,11 +156,11 @@
 }
 
 const uint8_t* CJBig2_BitStream::getBuf() const {
-  return m_pBuf;
+  return m_Span.data();
 }
 
 const uint8_t* CJBig2_BitStream::getPointer() const {
-  return m_pBuf + m_dwByteIdx;
+  return getBuf() + m_dwByteIdx;
 }
 
 void CJBig2_BitStream::offset(uint32_t dwOffset) {
@@ -170,7 +168,7 @@
 }
 
 uint32_t CJBig2_BitStream::getByteLeft() const {
-  return m_dwLength - m_dwByteIdx;
+  return m_Span.size() - m_dwByteIdx;
 }
 
 void CJBig2_BitStream::AdvanceBit() {
@@ -183,11 +181,11 @@
 }
 
 bool CJBig2_BitStream::IsInBounds() const {
-  return m_dwByteIdx < m_dwLength;
+  return m_dwByteIdx < m_Span.size();
 }
 
 uint32_t CJBig2_BitStream::LengthInBits() const {
-  return m_dwLength << 3;
+  return m_Span.size() << 3;
 }
 
 uint32_t CJBig2_BitStream::getObjNum() const {
diff --git a/core/fxcodec/jbig2/JBig2_BitStream.h b/core/fxcodec/jbig2/JBig2_BitStream.h
index 0edb432..8032090 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream.h
+++ b/core/fxcodec/jbig2/JBig2_BitStream.h
@@ -8,17 +8,18 @@
 #define CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_
 
 #include "core/fxcrt/retain_ptr.h"
-
-class CPDF_StreamAcc;
+#include "third_party/base/span.h"
 
 class CJBig2_BitStream {
  public:
-  explicit CJBig2_BitStream(const RetainPtr<CPDF_StreamAcc>& pSrcStream);
+  CJBig2_BitStream(pdfium::span<const uint8_t> pSrcStream, uint32_t dwObjNum);
+  CJBig2_BitStream(const CJBig2_BitStream&) = delete;
+  CJBig2_BitStream& operator=(const CJBig2_BitStream&) = delete;
   ~CJBig2_BitStream();
 
   // TODO(thestig): readFoo() should return bool.
-  int32_t readNBits(uint32_t nBits, uint32_t* dwResult);
-  int32_t readNBits(uint32_t nBits, int32_t* nResult);
+  int32_t readNBits(uint32_t dwBits, uint32_t* dwResult);
+  int32_t readNBits(uint32_t dwBits, int32_t* nResult);
   int32_t read1Bit(uint32_t* dwResult);
   int32_t read1Bit(bool* bResult);
   int32_t read1Byte(uint8_t* cResult);
@@ -34,26 +35,21 @@
   uint32_t getBitPos() const;
   void setBitPos(uint32_t dwBitPos);
   const uint8_t* getBuf() const;
-  uint32_t getLength() const { return m_dwLength; }
+  uint32_t getLength() const { return m_Span.size(); }
   const uint8_t* getPointer() const;
   void offset(uint32_t dwOffset);
   uint32_t getByteLeft() const;
   uint32_t getObjNum() const;
-
   bool IsInBounds() const;
 
  private:
   void AdvanceBit();
   uint32_t LengthInBits() const;
 
-  const uint8_t* m_pBuf;
-  uint32_t m_dwLength;
-  uint32_t m_dwByteIdx;
-  uint32_t m_dwBitIdx;
+  const pdfium::span<const uint8_t> m_Span;
+  uint32_t m_dwByteIdx = 0;
+  uint32_t m_dwBitIdx = 0;
   const uint32_t m_dwObjNum;
-
-  CJBig2_BitStream(const CJBig2_BitStream&) = delete;
-  void operator=(const CJBig2_BitStream&) = delete;
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_
diff --git a/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp b/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp
index b66d8b0..8669489 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp
+++ b/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp
@@ -7,19 +7,12 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/ptr_util.h"
 
 TEST(JBig2_BitStream, ReadNBits) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(FX_Alloc(uint8_t, 1));
-  data.get()[0] = 0xb1;  // 10110001
-
-  auto in_stream = pdfium::MakeUnique<CPDF_Stream>(std::move(data), 1, nullptr);
-  auto acc = pdfium::MakeRetain<CPDF_StreamAcc>(in_stream.get());
-  acc->LoadAllDataFiltered();
-
-  CJBig2_BitStream stream(acc);
+  const uint8_t kData[] = {0xb1};  // 10110001
+  CJBig2_BitStream stream(kData, 0);
 
   uint32_t val1;
   EXPECT_EQ(0, stream.readNBits(1, &val1));
@@ -37,14 +30,8 @@
 }
 
 TEST(JBig2_BitStream, ReadNBitsLargerThenData) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(FX_Alloc(uint8_t, 1));
-  data.get()[0] = 0xb1;
-
-  auto in_stream = pdfium::MakeUnique<CPDF_Stream>(std::move(data), 1, nullptr);
-  auto acc = pdfium::MakeRetain<CPDF_StreamAcc>(in_stream.get());
-  acc->LoadAllDataFiltered();
-
-  CJBig2_BitStream stream(acc);
+  const uint8_t kData[] = {0xb1};  // 10110001
+  CJBig2_BitStream stream(kData, 42);
 
   uint32_t val1;
   EXPECT_EQ(0, stream.readNBits(10, &val1));
@@ -52,27 +39,7 @@
 }
 
 TEST(JBig2_BitStream, ReadNBitsNullStream) {
-  auto in_stream = pdfium::MakeUnique<CPDF_Stream>(nullptr, 0, nullptr);
-  auto acc = pdfium::MakeRetain<CPDF_StreamAcc>(in_stream.get());
-  acc->LoadAllDataFiltered();
-
-  CJBig2_BitStream stream(acc);
-
-  uint32_t val1;
-  EXPECT_EQ(-1, stream.readNBits(1, &val1));
-
-  int32_t val2;
-  EXPECT_EQ(-1, stream.readNBits(2, &val2));
-}
-
-TEST(JBig2_BitStream, ReadNBitsEmptyStream) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(FX_Alloc(uint8_t, 1));
-
-  auto in_stream = pdfium::MakeUnique<CPDF_Stream>(std::move(data), 0, nullptr);
-  auto acc = pdfium::MakeRetain<CPDF_StreamAcc>(in_stream.get());
-  acc->LoadAllDataFiltered();
-
-  CJBig2_BitStream stream(acc);
+  CJBig2_BitStream stream({}, 0);
 
   uint32_t val1;
   EXPECT_EQ(-1, stream.readNBits(1, &val1));
@@ -82,14 +49,8 @@
 }
 
 TEST(JBig2_BitStream, ReadNBitsOutOfBounds) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(FX_Alloc(uint8_t, 1));
-  data.get()[0] = 0xb1;  // 10110001
-
-  auto in_stream = pdfium::MakeUnique<CPDF_Stream>(std::move(data), 1, nullptr);
-  auto acc = pdfium::MakeRetain<CPDF_StreamAcc>(in_stream.get());
-  acc->LoadAllDataFiltered();
-
-  CJBig2_BitStream stream(acc);
+  const uint8_t kData[] = {0xb1};  // 10110001
+  CJBig2_BitStream stream(kData, 42);
 
   uint32_t val1;
   EXPECT_EQ(0, stream.readNBits(8, &val1));
@@ -99,18 +60,8 @@
 }
 
 TEST(JBig2_BitStream, ReadNBitsWhereNIs36) {
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(FX_Alloc(uint8_t, 5));
-  data.get()[0] = 0xb0;
-  data.get()[1] = 0x01;
-  data.get()[2] = 0x00;
-  data.get()[3] = 0x00;
-  data.get()[4] = 0x40;
-
-  auto in_stream = pdfium::MakeUnique<CPDF_Stream>(std::move(data), 5, nullptr);
-  auto acc = pdfium::MakeRetain<CPDF_StreamAcc>(in_stream.get());
-  acc->LoadAllDataFiltered();
-
-  CJBig2_BitStream stream(acc);
+  const uint8_t kData[] = {0xb0, 0x01, 0x00, 0x00, 0x40};
+  CJBig2_BitStream stream(kData, 42);
 
   // This will shift off the top two bits and they end up lost.
   uint32_t val1;
diff --git a/core/fxcodec/jbig2/JBig2_Context.cpp b/core/fxcodec/jbig2/JBig2_Context.cpp
index 30e4aab..d9c9ee8 100644
--- a/core/fxcodec/jbig2/JBig2_Context.cpp
+++ b/core/fxcodec/jbig2/JBig2_Context.cpp
@@ -6,24 +6,25 @@
 
 #include "core/fxcodec/jbig2/JBig2_Context.h"
 
+#include <string.h>
+
 #include <algorithm>
 #include <limits>
 #include <list>
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 #include "core/fxcodec/jbig2/JBig2_GrdProc.h"
 #include "core/fxcodec/jbig2/JBig2_GrrdProc.h"
 #include "core/fxcodec/jbig2/JBig2_HtrdProc.h"
-#include "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h"
 #include "core/fxcodec/jbig2/JBig2_PddProc.h"
 #include "core/fxcodec/jbig2/JBig2_SddProc.h"
 #include "core/fxcodec/jbig2/JBig2_TrdProc.h"
-#include "core/fxcrt/ifx_pauseindicator.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/pauseindicator_iface.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
@@ -49,60 +50,66 @@
 static_assert(kSymbolDictCacheMaxSize > 0,
               "Symbol Dictionary Cache must have non-zero size");
 
-CJBig2_Context::CJBig2_Context(const RetainPtr<CPDF_StreamAcc>& pGlobalStream,
-                               const RetainPtr<CPDF_StreamAcc>& pSrcStream,
+// static
+std::unique_ptr<CJBig2_Context> CJBig2_Context::Create(
+    pdfium::span<const uint8_t> pGlobalSpan,
+    uint32_t dwGlobalObjNum,
+    pdfium::span<const uint8_t> pSrcSpan,
+    uint32_t dwSrcObjNum,
+    std::list<CJBig2_CachePair>* pSymbolDictCache) {
+  auto result = pdfium::WrapUnique(
+      new CJBig2_Context(pSrcSpan, dwSrcObjNum, pSymbolDictCache, false));
+  if (!pGlobalSpan.empty()) {
+    result->m_pGlobalContext = pdfium::WrapUnique(new CJBig2_Context(
+        pGlobalSpan, dwGlobalObjNum, pSymbolDictCache, true));
+  }
+  return result;
+}
+
+CJBig2_Context::CJBig2_Context(pdfium::span<const uint8_t> pSrcSpan,
+                               uint32_t dwObjNum,
                                std::list<CJBig2_CachePair>* pSymbolDictCache,
                                bool bIsGlobal)
-    : m_nSegmentDecoded(0),
-      m_bInPage(false),
-      m_bBufSpecified(false),
-      m_PauseStep(10),
-      m_ProcessingStatus(FXCODEC_STATUS_FRAME_READY),
-      m_dwOffset(0),
-      m_pSymbolDictCache(pSymbolDictCache),
-      m_bIsGlobal(bIsGlobal) {
-  if (pGlobalStream && pGlobalStream->GetSize() > 0) {
-    m_pGlobalContext = pdfium::MakeUnique<CJBig2_Context>(
-        nullptr, pGlobalStream, pSymbolDictCache, true);
-  }
-  m_pStream = pdfium::MakeUnique<CJBig2_BitStream>(pSrcStream);
-}
+    : m_pStream(pdfium::MakeUnique<CJBig2_BitStream>(pSrcSpan, dwObjNum)),
+      m_HuffmanTables(CJBig2_HuffmanTable::kNumHuffmanTables),
+      m_bIsGlobal(bIsGlobal),
+      m_pSymbolDictCache(pSymbolDictCache) {}
 
 CJBig2_Context::~CJBig2_Context() {}
 
-int32_t CJBig2_Context::decodeSequential(IFX_PauseIndicator* pPause) {
-  int32_t nRet;
+JBig2_Result CJBig2_Context::DecodeSequential(PauseIndicatorIface* pPause) {
   if (m_pStream->getByteLeft() <= 0)
-    return JBIG2_END_OF_FILE;
+    return JBig2_Result::kEndReached;
 
   while (m_pStream->getByteLeft() >= JBIG2_MIN_SEGMENT_SIZE) {
+    JBig2_Result nRet;
     if (!m_pSegment) {
       m_pSegment = pdfium::MakeUnique<CJBig2_Segment>();
-      nRet = parseSegmentHeader(m_pSegment.get());
-      if (nRet != JBIG2_SUCCESS) {
+      nRet = ParseSegmentHeader(m_pSegment.get());
+      if (nRet != JBig2_Result::kSuccess) {
         m_pSegment.reset();
         return nRet;
       }
       m_dwOffset = m_pStream->getOffset();
     }
-    nRet = parseSegmentData(m_pSegment.get(), pPause);
+    nRet = ParseSegmentData(m_pSegment.get(), pPause);
     if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
       m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       m_PauseStep = 2;
-      return JBIG2_SUCCESS;
+      return JBig2_Result::kSuccess;
     }
-    if (nRet == JBIG2_END_OF_PAGE || nRet == JBIG2_END_OF_FILE) {
+    if (nRet == JBig2_Result::kEndReached) {
       m_pSegment.reset();
-      return JBIG2_SUCCESS;
+      return JBig2_Result::kSuccess;
     }
-    if (nRet != JBIG2_SUCCESS) {
+    if (nRet != JBig2_Result::kSuccess) {
       m_pSegment.reset();
       return nRet;
     }
     if (m_pSegment->m_dwData_length != 0xffffffff) {
       m_dwOffset += m_pSegment->m_dwData_length;
       if (!m_dwOffset.IsValid())
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
 
       m_pStream->setOffset(m_dwOffset.ValueOrDie());
     } else {
@@ -113,64 +120,22 @@
         pPause->NeedToPauseNow()) {
       m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       m_PauseStep = 2;
-      return JBIG2_SUCCESS;
+      return JBig2_Result::kSuccess;
     }
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::decodeRandomFirstPage(IFX_PauseIndicator* pPause) {
-  int32_t nRet;
-  while (m_pStream->getByteLeft() > JBIG2_MIN_SEGMENT_SIZE) {
-    auto pSegment = pdfium::MakeUnique<CJBig2_Segment>();
-    nRet = parseSegmentHeader(pSegment.get());
-    if (nRet != JBIG2_SUCCESS)
-      return nRet;
-
-    if (pSegment->m_cFlags.s.type == 51)
-      break;
-
-    m_SegmentList.push_back(std::move(pSegment));
-    if (pPause && pPause->NeedToPauseNow()) {
-      m_PauseStep = 3;
-      m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return JBIG2_SUCCESS;
-    }
-  }
-  m_nSegmentDecoded = 0;
-  return decodeRandom(pPause);
-}
-
-int32_t CJBig2_Context::decodeRandom(IFX_PauseIndicator* pPause) {
-  for (; m_nSegmentDecoded < m_SegmentList.size(); ++m_nSegmentDecoded) {
-    int32_t nRet =
-        parseSegmentData(m_SegmentList[m_nSegmentDecoded].get(), pPause);
-    if (nRet == JBIG2_END_OF_PAGE || nRet == JBIG2_END_OF_FILE)
-      return JBIG2_SUCCESS;
-
-    if (nRet != JBIG2_SUCCESS)
-      return nRet;
-
-    if (m_pPage && pPause && pPause->NeedToPauseNow()) {
-      m_PauseStep = 4;
-      m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-      return JBIG2_SUCCESS;
-    }
-  }
-  return JBIG2_SUCCESS;
-}
-
-int32_t CJBig2_Context::getFirstPage(uint8_t* pBuf,
-                                     int32_t width,
-                                     int32_t height,
-                                     int32_t stride,
-                                     IFX_PauseIndicator* pPause) {
-  int32_t nRet = 0;
+bool CJBig2_Context::GetFirstPage(uint8_t* pBuf,
+                                  int32_t width,
+                                  int32_t height,
+                                  int32_t stride,
+                                  PauseIndicatorIface* pPause) {
   if (m_pGlobalContext) {
-    nRet = m_pGlobalContext->decodeSequential(pPause);
-    if (nRet != JBIG2_SUCCESS) {
+    JBig2_Result nRet = m_pGlobalContext->DecodeSequential(pPause);
+    if (nRet != JBig2_Result::kSuccess) {
       m_ProcessingStatus = FXCODEC_STATUS_ERROR;
-      return nRet;
+      return nRet == JBig2_Result::kSuccess;
     }
   }
   m_PauseStep = 0;
@@ -179,40 +144,38 @@
   if (pPause && pPause->NeedToPauseNow()) {
     m_PauseStep = 1;
     m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
-    return nRet;
+    return true;
   }
   return Continue(pPause);
 }
 
-int32_t CJBig2_Context::Continue(IFX_PauseIndicator* pPause) {
+bool CJBig2_Context::Continue(PauseIndicatorIface* pPause) {
   m_ProcessingStatus = FXCODEC_STATUS_DECODE_READY;
-  int32_t nRet = 0;
-  if (m_PauseStep <= 2) {
-    nRet = decodeSequential(pPause);
-  } else if (m_PauseStep == 3) {
-    nRet = decodeRandomFirstPage(pPause);
-  } else if (m_PauseStep == 4) {
-    nRet = decodeRandom(pPause);
-  } else if (m_PauseStep == 5) {
+  JBig2_Result nRet = JBig2_Result::kSuccess;
+  if (m_PauseStep == 5) {
     m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH;
-    return JBIG2_SUCCESS;
+    return true;
   }
+
+  if (m_PauseStep <= 2)
+    nRet = DecodeSequential(pPause);
   if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    return nRet;
+    return nRet == JBig2_Result::kSuccess;
 
   m_PauseStep = 5;
-  if (!m_bBufSpecified && nRet == JBIG2_SUCCESS) {
+  if (!m_bBufSpecified && nRet == JBig2_Result::kSuccess) {
     m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH;
-    return JBIG2_SUCCESS;
+    return true;
   }
-  m_ProcessingStatus = nRet == JBIG2_SUCCESS ? FXCODEC_STATUS_DECODE_FINISH
-                                             : FXCODEC_STATUS_ERROR;
-  return nRet;
+  m_ProcessingStatus = nRet == JBig2_Result::kSuccess
+                           ? FXCODEC_STATUS_DECODE_FINISH
+                           : FXCODEC_STATUS_ERROR;
+  return nRet == JBig2_Result::kSuccess;
 }
 
-CJBig2_Segment* CJBig2_Context::findSegmentByNumber(uint32_t dwNumber) {
+CJBig2_Segment* CJBig2_Context::FindSegmentByNumber(uint32_t dwNumber) {
   if (m_pGlobalContext) {
-    CJBig2_Segment* pSeg = m_pGlobalContext->findSegmentByNumber(dwNumber);
+    CJBig2_Segment* pSeg = m_pGlobalContext->FindSegmentByNumber(dwNumber);
     if (pSeg)
       return pSeg;
   }
@@ -223,15 +186,15 @@
   return nullptr;
 }
 
-CJBig2_Segment* CJBig2_Context::findReferredSegmentByTypeAndIndex(
+CJBig2_Segment* CJBig2_Context::FindReferredTableSegmentByIndex(
     CJBig2_Segment* pSegment,
-    uint8_t cType,
     int32_t nIndex) {
+  static const uint8_t kTableType = 53;
   int32_t count = 0;
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
     CJBig2_Segment* pSeg =
-        findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
-    if (pSeg && pSeg->m_cFlags.s.type == cType) {
+        FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
+    if (pSeg && pSeg->m_cFlags.s.type == kTableType) {
       if (count == nIndex)
         return pSeg;
       ++count;
@@ -240,31 +203,28 @@
   return nullptr;
 }
 
-int32_t CJBig2_Context::parseSegmentHeader(CJBig2_Segment* pSegment) {
+JBig2_Result CJBig2_Context::ParseSegmentHeader(CJBig2_Segment* pSegment) {
   if (m_pStream->readInteger(&pSegment->m_dwNumber) != 0 ||
       m_pStream->read1Byte(&pSegment->m_cFlags.c) != 0) {
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
   }
 
-  uint32_t dwTemp;
   uint8_t cTemp = m_pStream->getCurByte();
   if ((cTemp >> 5) == 7) {
     if (m_pStream->readInteger(
             (uint32_t*)&pSegment->m_nReferred_to_segment_count) != 0) {
-      return JBIG2_ERROR_TOO_SHORT;
+      return JBig2_Result::kFailure;
     }
     pSegment->m_nReferred_to_segment_count &= 0x1fffffff;
     if (pSegment->m_nReferred_to_segment_count >
         JBIG2_MAX_REFERRED_SEGMENT_COUNT) {
-      return JBIG2_ERROR_LIMIT;
+      return JBig2_Result::kFailure;
     }
-    dwTemp = 5 + 4 + (pSegment->m_nReferred_to_segment_count + 1) / 8;
   } else {
     if (m_pStream->read1Byte(&cTemp) != 0)
-      return JBIG2_ERROR_TOO_SHORT;
+      return JBig2_Result::kFailure;
 
     pSegment->m_nReferred_to_segment_count = cTemp >> 5;
-    dwTemp = 5 + 1;
   }
   uint8_t cSSize =
       pSegment->m_dwNumber > 65536 ? 4 : pSegment->m_dwNumber > 256 ? 2 : 1;
@@ -276,49 +236,48 @@
       switch (cSSize) {
         case 1:
           if (m_pStream->read1Byte(&cTemp) != 0)
-            return JBIG2_ERROR_TOO_SHORT;
+            return JBig2_Result::kFailure;
 
           pSegment->m_Referred_to_segment_numbers[i] = cTemp;
           break;
         case 2:
           uint16_t wTemp;
           if (m_pStream->readShortInteger(&wTemp) != 0)
-            return JBIG2_ERROR_TOO_SHORT;
+            return JBig2_Result::kFailure;
 
           pSegment->m_Referred_to_segment_numbers[i] = wTemp;
           break;
         case 4:
+          uint32_t dwTemp;
           if (m_pStream->readInteger(&dwTemp) != 0)
-            return JBIG2_ERROR_TOO_SHORT;
+            return JBig2_Result::kFailure;
 
           pSegment->m_Referred_to_segment_numbers[i] = dwTemp;
           break;
       }
       if (pSegment->m_Referred_to_segment_numbers[i] >= pSegment->m_dwNumber)
-        return JBIG2_ERROR_TOO_SHORT;
+        return JBig2_Result::kFailure;
     }
   }
   if (cPSize == 1) {
     if (m_pStream->read1Byte(&cTemp) != 0)
-      return JBIG2_ERROR_TOO_SHORT;
+      return JBig2_Result::kFailure;
     pSegment->m_dwPage_association = cTemp;
-  } else {
-    if (m_pStream->readInteger(&pSegment->m_dwPage_association) != 0) {
-      return JBIG2_ERROR_TOO_SHORT;
-    }
+  } else if (m_pStream->readInteger(&pSegment->m_dwPage_association) != 0) {
+    return JBig2_Result::kFailure;
   }
   if (m_pStream->readInteger(&pSegment->m_dwData_length) != 0)
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
 
   pSegment->m_dwObjNum = m_pStream->getObjNum();
   pSegment->m_dwDataOffset = m_pStream->getOffset();
   pSegment->m_State = JBIG2_SEGMENT_DATA_UNPARSED;
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseSegmentData(CJBig2_Segment* pSegment,
-                                         IFX_PauseIndicator* pPause) {
-  int32_t ret = ProcessingParseSegmentData(pSegment, pPause);
+JBig2_Result CJBig2_Context::ParseSegmentData(CJBig2_Segment* pSegment,
+                                              PauseIndicatorIface* pPause) {
+  JBig2_Result ret = ProcessingParseSegmentData(pSegment, pPause);
   while (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE &&
          m_pStream->getByteLeft() > 0) {
     ret = ProcessingParseSegmentData(pSegment, pPause);
@@ -326,37 +285,38 @@
   return ret;
 }
 
-int32_t CJBig2_Context::ProcessingParseSegmentData(CJBig2_Segment* pSegment,
-                                                   IFX_PauseIndicator* pPause) {
+JBig2_Result CJBig2_Context::ProcessingParseSegmentData(
+    CJBig2_Segment* pSegment,
+    PauseIndicatorIface* pPause) {
   switch (pSegment->m_cFlags.s.type) {
     case 0:
-      return parseSymbolDict(pSegment);
+      return ParseSymbolDict(pSegment);
     case 4:
     case 6:
     case 7:
       if (!m_bInPage)
-        return JBIG2_ERROR_FATAL;
-      return parseTextRegion(pSegment);
+        return JBig2_Result::kFailure;
+      return ParseTextRegion(pSegment);
     case 16:
-      return parsePatternDict(pSegment, pPause);
+      return ParsePatternDict(pSegment, pPause);
     case 20:
     case 22:
     case 23:
       if (!m_bInPage)
-        return JBIG2_ERROR_FATAL;
-      return parseHalftoneRegion(pSegment, pPause);
+        return JBig2_Result::kFailure;
+      return ParseHalftoneRegion(pSegment, pPause);
     case 36:
     case 38:
     case 39:
       if (!m_bInPage)
-        return JBIG2_ERROR_FATAL;
-      return parseGenericRegion(pSegment, pPause);
+        return JBig2_Result::kFailure;
+      return ParseGenericRegion(pSegment, pPause);
     case 40:
     case 42:
     case 43:
       if (!m_bInPage)
-        return JBIG2_ERROR_FATAL;
-      return parseGenericRefinementRegion(pSegment);
+        return JBig2_Result::kFailure;
+      return ParseGenericRefinementRegion(pSegment);
     case 48: {
       uint16_t wTemp;
       auto pPageInfo = pdfium::MakeUnique<JBig2PageInfo>();
@@ -366,12 +326,12 @@
           m_pStream->readInteger(&pPageInfo->m_dwResolutionY) != 0 ||
           m_pStream->read1Byte(&pPageInfo->m_cFlags) != 0 ||
           m_pStream->readShortInteger(&wTemp) != 0) {
-        return JBIG2_ERROR_TOO_SHORT;
+        return JBig2_Result::kFailure;
       }
       pPageInfo->m_bIsStriped = !!(wTemp & 0x8000);
       pPageInfo->m_wMaxStripeSize = wTemp & 0x7fff;
       bool bMaxHeight = (pPageInfo->m_dwHeight == 0xffffffff);
-      if (bMaxHeight && pPageInfo->m_bIsStriped != true)
+      if (bMaxHeight && !pPageInfo->m_bIsStriped)
         pPageInfo->m_bIsStriped = true;
 
       if (!m_bBufSpecified) {
@@ -383,80 +343,76 @@
 
       if (!m_pPage->data()) {
         m_ProcessingStatus = FXCODEC_STATUS_ERROR;
-        return JBIG2_ERROR_TOO_SHORT;
+        return JBig2_Result::kFailure;
       }
 
-      m_pPage->fill((pPageInfo->m_cFlags & 4) ? 1 : 0);
+      m_pPage->Fill((pPageInfo->m_cFlags & 4) ? 1 : 0);
       m_PageInfoList.push_back(std::move(pPageInfo));
       m_bInPage = true;
     } break;
     case 49:
       m_bInPage = false;
-      return JBIG2_END_OF_PAGE;
+      return JBig2_Result::kEndReached;
       break;
     case 50:
       m_pStream->offset(pSegment->m_dwData_length);
       break;
     case 51:
-      return JBIG2_END_OF_FILE;
+      return JBig2_Result::kEndReached;
     case 52:
       m_pStream->offset(pSegment->m_dwData_length);
       break;
     case 53:
-      return parseTable(pSegment);
+      return ParseTable(pSegment);
     case 62:
       m_pStream->offset(pSegment->m_dwData_length);
       break;
     default:
       break;
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseSymbolDict(CJBig2_Segment* pSegment) {
+JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) {
   uint16_t wFlags;
   if (m_pStream->readShortInteger(&wFlags) != 0)
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
 
   auto pSymbolDictDecoder = pdfium::MakeUnique<CJBig2_SDDProc>();
   pSymbolDictDecoder->SDHUFF = wFlags & 0x0001;
   pSymbolDictDecoder->SDREFAGG = (wFlags >> 1) & 0x0001;
   pSymbolDictDecoder->SDTEMPLATE = (wFlags >> 10) & 0x0003;
   pSymbolDictDecoder->SDRTEMPLATE = !!((wFlags >> 12) & 0x0003);
-  uint8_t cSDHUFFDH = (wFlags >> 2) & 0x0003;
-  uint8_t cSDHUFFDW = (wFlags >> 4) & 0x0003;
-  uint8_t cSDHUFFBMSIZE = (wFlags >> 6) & 0x0001;
-  uint8_t cSDHUFFAGGINST = (wFlags >> 7) & 0x0001;
   if (pSymbolDictDecoder->SDHUFF == 0) {
     const uint32_t dwTemp = (pSymbolDictDecoder->SDTEMPLATE == 0) ? 8 : 2;
     for (uint32_t i = 0; i < dwTemp; ++i) {
       if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDAT[i]) != 0)
-        return JBIG2_ERROR_TOO_SHORT;
+        return JBig2_Result::kFailure;
     }
   }
   if (pSymbolDictDecoder->SDREFAGG == 1 && !pSymbolDictDecoder->SDRTEMPLATE) {
     for (int32_t i = 0; i < 4; ++i) {
       if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDRAT[i]) != 0)
-        return JBIG2_ERROR_TOO_SHORT;
+        return JBig2_Result::kFailure;
     }
   }
   if (m_pStream->readInteger(&pSymbolDictDecoder->SDNUMEXSYMS) != 0 ||
       m_pStream->readInteger(&pSymbolDictDecoder->SDNUMNEWSYMS) != 0) {
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
   }
   if (pSymbolDictDecoder->SDNUMEXSYMS > JBIG2_MAX_EXPORT_SYSMBOLS ||
       pSymbolDictDecoder->SDNUMNEWSYMS > JBIG2_MAX_NEW_SYSMBOLS) {
-    return JBIG2_ERROR_LIMIT;
+    return JBig2_Result::kFailure;
   }
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
-    if (!findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]))
-      return JBIG2_ERROR_FATAL;
+    if (!FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]))
+      return JBig2_Result::kFailure;
   }
   CJBig2_Segment* pLRSeg = nullptr;
   pSymbolDictDecoder->SDNUMINSYMS = 0;
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
     CJBig2_Segment* pSeg =
-        findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
+        FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
     if (pSeg->m_cFlags.s.type == 0) {
       pSymbolDictDecoder->SDNUMINSYMS += pSeg->m_SymbolDict->NumImages();
       pLRSeg = pSeg;
@@ -469,9 +425,9 @@
     uint32_t dwTemp = 0;
     for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
       CJBig2_Segment* pSeg =
-          findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
+          FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
       if (pSeg->m_cFlags.s.type == 0) {
-        const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict.get();
+        const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict;
         for (size_t j = 0; j < dict.NumImages(); ++j)
           SDINSYMS.get()[dwTemp + j] = dict.GetImage(j);
         dwTemp += dict.NumImages();
@@ -480,69 +436,54 @@
   }
   pSymbolDictDecoder->SDINSYMS = SDINSYMS.get();
 
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B1;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B2;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B3;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B4;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B5;
+  uint8_t cSDHUFFDH = (wFlags >> 2) & 0x0003;
+  uint8_t cSDHUFFDW = (wFlags >> 4) & 0x0003;
   if (pSymbolDictDecoder->SDHUFF == 1) {
     if (cSDHUFFDH == 2 || cSDHUFFDW == 2)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
 
     int32_t nIndex = 0;
     if (cSDHUFFDH == 0) {
-      Table_B4 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B4, HuffmanTable_B4_Size, HuffmanTable_HTOOB_B4);
-      pSymbolDictDecoder->SDHUFFDH = Table_B4.get();
+      pSymbolDictDecoder->SDHUFFDH = GetHuffmanTable(4);
     } else if (cSDHUFFDH == 1) {
-      Table_B5 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B5, HuffmanTable_B5_Size, HuffmanTable_HTOOB_B5);
-      pSymbolDictDecoder->SDHUFFDH = Table_B5.get();
+      pSymbolDictDecoder->SDHUFFDH = GetHuffmanTable(5);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pSymbolDictDecoder->SDHUFFDH = pSeg->m_HuffmanTable.get();
     }
     if (cSDHUFFDW == 0) {
-      Table_B2 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B2, HuffmanTable_B2_Size, HuffmanTable_HTOOB_B2);
-      pSymbolDictDecoder->SDHUFFDW = Table_B2.get();
+      pSymbolDictDecoder->SDHUFFDW = GetHuffmanTable(2);
     } else if (cSDHUFFDW == 1) {
-      Table_B3 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B3, HuffmanTable_B3_Size, HuffmanTable_HTOOB_B3);
-      pSymbolDictDecoder->SDHUFFDW = Table_B3.get();
+      pSymbolDictDecoder->SDHUFFDW = GetHuffmanTable(3);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pSymbolDictDecoder->SDHUFFDW = pSeg->m_HuffmanTable.get();
     }
+    uint8_t cSDHUFFBMSIZE = (wFlags >> 6) & 0x0001;
     if (cSDHUFFBMSIZE == 0) {
-      Table_B1 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B1, HuffmanTable_B1_Size, HuffmanTable_HTOOB_B1);
-      pSymbolDictDecoder->SDHUFFBMSIZE = Table_B1.get();
+      pSymbolDictDecoder->SDHUFFBMSIZE = GetHuffmanTable(1);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pSymbolDictDecoder->SDHUFFBMSIZE = pSeg->m_HuffmanTable.get();
     }
     if (pSymbolDictDecoder->SDREFAGG == 1) {
+      uint8_t cSDHUFFAGGINST = (wFlags >> 7) & 0x0001;
       if (cSDHUFFAGGINST == 0) {
-        if (!Table_B1) {
-          Table_B1 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B1, HuffmanTable_B1_Size, HuffmanTable_HTOOB_B1);
-        }
-        pSymbolDictDecoder->SDHUFFAGGINST = Table_B1.get();
+        pSymbolDictDecoder->SDHUFFAGGINST = GetHuffmanTable(1);
       } else {
         CJBig2_Segment* pSeg =
-            findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+            FindReferredTableSegmentByIndex(pSegment, nIndex++);
         if (!pSeg)
-          return JBIG2_ERROR_FATAL;
+          return JBig2_Result::kFailure;
         pSymbolDictDecoder->SDHUFFAGGINST = pSeg->m_HuffmanTable.get();
       }
     }
@@ -560,12 +501,12 @@
     if (bUseGbContext) {
       gbContext = pLRSeg->m_SymbolDict->GbContext();
       if (gbContext.size() != gbContextSize)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
     }
     if (bUseGrContext) {
       grContext = pLRSeg->m_SymbolDict->GrContext();
       if (grContext.size() != grContextSize)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
     }
   } else {
     if (bUseGbContext)
@@ -595,18 +536,18 @@
     if (bUseGbContext) {
       auto pArithDecoder =
           pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
-      pSegment->m_SymbolDict = pSymbolDictDecoder->decode_Arith(
+      pSegment->m_SymbolDict = pSymbolDictDecoder->DecodeArith(
           pArithDecoder.get(), &gbContext, &grContext);
       if (!pSegment->m_SymbolDict)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
 
       m_pStream->alignByte();
       m_pStream->offset(2);
     } else {
-      pSegment->m_SymbolDict = pSymbolDictDecoder->decode_Huffman(
+      pSegment->m_SymbolDict = pSymbolDictDecoder->DecodeHuffman(
           m_pStream.get(), &gbContext, &grContext);
       if (!pSegment->m_SymbolDict)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       m_pStream->alignByte();
     }
     if (m_bIsGlobal) {
@@ -622,20 +563,22 @@
   }
   if (wFlags & 0x0200) {
     if (bUseGbContext)
-      pSegment->m_SymbolDict->SetGbContext(gbContext);
+      pSegment->m_SymbolDict->SetGbContext(std::move(gbContext));
     if (bUseGrContext)
-      pSegment->m_SymbolDict->SetGrContext(grContext);
+      pSegment->m_SymbolDict->SetGrContext(std::move(grContext));
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseTextRegion(CJBig2_Segment* pSegment) {
+JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) {
   uint16_t wFlags;
   JBig2RegionInfo ri;
-  if (parseRegionInfo(&ri) != JBIG2_SUCCESS ||
+  if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess ||
       m_pStream->readShortInteger(&wFlags) != 0) {
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
   }
+  if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height))
+    return JBig2_Result::kFailure;
 
   auto pTRD = pdfium::MakeUnique<CJBig2_TRDProc>();
   pTRD->SBW = ri.width;
@@ -654,45 +597,37 @@
   }
   pTRD->SBRTEMPLATE = !!((wFlags >> 15) & 0x0001);
 
-  uint8_t cSBHUFFFS = 0;
-  uint8_t cSBHUFFDS = 0;
-  uint8_t cSBHUFFDT = 0;
-  uint8_t cSBHUFFRDW = 0;
-  uint8_t cSBHUFFRDH = 0;
-  uint8_t cSBHUFFRDX = 0;
-  uint8_t cSBHUFFRDY = 0;
-  uint8_t cSBHUFFRSIZE = 0;
-  if (pTRD->SBHUFF == 1) {
-    if (m_pStream->readShortInteger(&wFlags) != 0)
-      return JBIG2_ERROR_TOO_SHORT;
-
-    cSBHUFFFS = wFlags & 0x0003;
-    cSBHUFFDS = (wFlags >> 2) & 0x0003;
-    cSBHUFFDT = (wFlags >> 4) & 0x0003;
-    cSBHUFFRDW = (wFlags >> 6) & 0x0003;
-    cSBHUFFRDH = (wFlags >> 8) & 0x0003;
-    cSBHUFFRDX = (wFlags >> 10) & 0x0003;
-    cSBHUFFRDY = (wFlags >> 12) & 0x0003;
-    cSBHUFFRSIZE = (wFlags >> 14) & 0x0001;
+  if (pTRD->SBHUFF == 1 && m_pStream->readShortInteger(&wFlags) != 0) {
+    return JBig2_Result::kFailure;
   }
   if (pTRD->SBREFINE == 1 && !pTRD->SBRTEMPLATE) {
     for (int32_t i = 0; i < 4; ++i) {
       if (m_pStream->read1Byte((uint8_t*)&pTRD->SBRAT[i]) != 0)
-        return JBIG2_ERROR_TOO_SHORT;
+        return JBig2_Result::kFailure;
     }
   }
   if (m_pStream->readInteger(&pTRD->SBNUMINSTANCES) != 0)
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
+
+  // Assume each instance takes at least 0.25 bits when encoded. That means for
+  // a stream of length N bytes, there can be at most 32N instances. This is a
+  // conservative estimate just to sanitize the |SBNUMINSTANCES| value.
+  // Use FX_SAFE_INT32 to be safe, though it should never overflow because PDFs
+  // have a maximum size of roughly 11 GB.
+  FX_SAFE_INT32 nMaxStripInstances = m_pStream->getLength();
+  nMaxStripInstances *= 32;
+  if (pTRD->SBNUMINSTANCES > nMaxStripInstances.ValueOrDie())
+    return JBig2_Result::kFailure;
 
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
-    if (!findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]))
-      return JBIG2_ERROR_FATAL;
+    if (!FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]))
+      return JBig2_Result::kFailure;
   }
 
   pTRD->SBNUMSYMS = 0;
   for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
     CJBig2_Segment* pSeg =
-        findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
+        FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
     if (pSeg->m_cFlags.s.type == 0) {
       pTRD->SBNUMSYMS += pSeg->m_SymbolDict->NumImages();
     }
@@ -704,9 +639,9 @@
     dwTemp = 0;
     for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
       CJBig2_Segment* pSeg =
-          findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
+          FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]);
       if (pSeg->m_cFlags.s.type == 0) {
-        const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict.get();
+        const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict;
         for (size_t j = 0; j < dict.NumImages(); ++j)
           SBSYMS.get()[dwTemp + j] = dict.GetImage(j);
         dwTemp += dict.NumImages();
@@ -719,9 +654,9 @@
 
   if (pTRD->SBHUFF == 1) {
     std::vector<JBig2HuffmanCode> SBSYMCODES =
-        decodeSymbolIDHuffmanTable(m_pStream.get(), pTRD->SBNUMSYMS);
+        DecodeSymbolIDHuffmanTable(pTRD->SBNUMSYMS);
     if (SBSYMCODES.empty())
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
 
     m_pStream->alignByte();
     pTRD->SBSYMCODES = std::move(SBSYMCODES);
@@ -733,157 +668,108 @@
     pTRD->SBSYMCODELEN = (uint8_t)dwTemp;
   }
 
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B1;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B6;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B7;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B8;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B9;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B10;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B11;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B12;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B13;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B14;
-  std::unique_ptr<CJBig2_HuffmanTable> Table_B15;
   if (pTRD->SBHUFF == 1) {
+    uint8_t cSBHUFFFS = wFlags & 0x0003;
+    uint8_t cSBHUFFDS = (wFlags >> 2) & 0x0003;
+    uint8_t cSBHUFFDT = (wFlags >> 4) & 0x0003;
+    uint8_t cSBHUFFRDW = (wFlags >> 6) & 0x0003;
+    uint8_t cSBHUFFRDH = (wFlags >> 8) & 0x0003;
+    uint8_t cSBHUFFRDX = (wFlags >> 10) & 0x0003;
+    uint8_t cSBHUFFRDY = (wFlags >> 12) & 0x0003;
+    uint8_t cSBHUFFRSIZE = (wFlags >> 14) & 0x0001;
     if (cSBHUFFFS == 2 || cSBHUFFRDW == 2 || cSBHUFFRDH == 2 ||
         cSBHUFFRDX == 2 || cSBHUFFRDY == 2) {
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
     }
     int32_t nIndex = 0;
     if (cSBHUFFFS == 0) {
-      Table_B6 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B6, HuffmanTable_B6_Size, HuffmanTable_HTOOB_B6);
-      pTRD->SBHUFFFS = Table_B6.get();
+      pTRD->SBHUFFFS = GetHuffmanTable(6);
     } else if (cSBHUFFFS == 1) {
-      Table_B7 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B7, HuffmanTable_B7_Size, HuffmanTable_HTOOB_B7);
-      pTRD->SBHUFFFS = Table_B7.get();
+      pTRD->SBHUFFFS = GetHuffmanTable(7);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFFS = pSeg->m_HuffmanTable.get();
     }
     if (cSBHUFFDS == 0) {
-      Table_B8 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B8, HuffmanTable_B8_Size, HuffmanTable_HTOOB_B8);
-      pTRD->SBHUFFDS = Table_B8.get();
+      pTRD->SBHUFFDS = GetHuffmanTable(8);
     } else if (cSBHUFFDS == 1) {
-      Table_B9 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B9, HuffmanTable_B9_Size, HuffmanTable_HTOOB_B9);
-      pTRD->SBHUFFDS = Table_B9.get();
+      pTRD->SBHUFFDS = GetHuffmanTable(9);
     } else if (cSBHUFFDS == 2) {
-      Table_B10 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B10, HuffmanTable_B10_Size, HuffmanTable_HTOOB_B10);
-      pTRD->SBHUFFDS = Table_B10.get();
+      pTRD->SBHUFFDS = GetHuffmanTable(10);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFDS = pSeg->m_HuffmanTable.get();
     }
     if (cSBHUFFDT == 0) {
-      Table_B11 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B11, HuffmanTable_B11_Size, HuffmanTable_HTOOB_B11);
-      pTRD->SBHUFFDT = Table_B11.get();
+      pTRD->SBHUFFDT = GetHuffmanTable(11);
     } else if (cSBHUFFDT == 1) {
-      Table_B12 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B12, HuffmanTable_B12_Size, HuffmanTable_HTOOB_B12);
-      pTRD->SBHUFFDT = Table_B12.get();
+      pTRD->SBHUFFDT = GetHuffmanTable(12);
     } else if (cSBHUFFDT == 2) {
-      Table_B13 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B13, HuffmanTable_B13_Size, HuffmanTable_HTOOB_B13);
-      pTRD->SBHUFFDT = Table_B13.get();
+      pTRD->SBHUFFDT = GetHuffmanTable(13);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFDT = pSeg->m_HuffmanTable.get();
     }
     if (cSBHUFFRDW == 0) {
-      Table_B14 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B14, HuffmanTable_B14_Size, HuffmanTable_HTOOB_B14);
-      pTRD->SBHUFFRDW = Table_B14.get();
+      pTRD->SBHUFFRDW = GetHuffmanTable(14);
     } else if (cSBHUFFRDW == 1) {
-      Table_B15 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-      pTRD->SBHUFFRDW = Table_B15.get();
+      pTRD->SBHUFFRDW = GetHuffmanTable(15);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFRDW = pSeg->m_HuffmanTable.get();
     }
     if (cSBHUFFRDH == 0) {
-      if (!Table_B14) {
-        Table_B14 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-            HuffmanTable_B14, HuffmanTable_B14_Size, HuffmanTable_HTOOB_B14);
-      }
-      pTRD->SBHUFFRDH = Table_B14.get();
+      pTRD->SBHUFFRDH = GetHuffmanTable(14);
     } else if (cSBHUFFRDH == 1) {
-      if (!Table_B15) {
-        Table_B15 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-            HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-      }
-      pTRD->SBHUFFRDH = Table_B15.get();
+      pTRD->SBHUFFRDH = GetHuffmanTable(15);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFRDH = pSeg->m_HuffmanTable.get();
     }
     if (cSBHUFFRDX == 0) {
-      if (!Table_B14) {
-        Table_B14 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-            HuffmanTable_B14, HuffmanTable_B14_Size, HuffmanTable_HTOOB_B14);
-      }
-      pTRD->SBHUFFRDX = Table_B14.get();
+      pTRD->SBHUFFRDX = GetHuffmanTable(14);
     } else if (cSBHUFFRDX == 1) {
-      if (!Table_B15) {
-        Table_B15 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-            HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-      }
-      pTRD->SBHUFFRDX = Table_B15.get();
+      pTRD->SBHUFFRDX = GetHuffmanTable(15);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFRDX = pSeg->m_HuffmanTable.get();
     }
     if (cSBHUFFRDY == 0) {
-      if (!Table_B14) {
-        Table_B14 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-            HuffmanTable_B14, HuffmanTable_B14_Size, HuffmanTable_HTOOB_B14);
-      }
-      pTRD->SBHUFFRDY = Table_B14.get();
+      pTRD->SBHUFFRDY = GetHuffmanTable(14);
     } else if (cSBHUFFRDY == 1) {
-      if (!Table_B15) {
-        Table_B15 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-            HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-      }
-      pTRD->SBHUFFRDY = Table_B15.get();
+      pTRD->SBHUFFRDY = GetHuffmanTable(15);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFRDY = pSeg->m_HuffmanTable.get();
     }
     if (cSBHUFFRSIZE == 0) {
-      Table_B1 = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-          HuffmanTable_B1, HuffmanTable_B1_Size, HuffmanTable_HTOOB_B1);
-      pTRD->SBHUFFRSIZE = Table_B1.get();
+      pTRD->SBHUFFRSIZE = GetHuffmanTable(1);
     } else {
       CJBig2_Segment* pSeg =
-          findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++);
+          FindReferredTableSegmentByIndex(pSegment, nIndex++);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
       pTRD->SBHUFFRSIZE = pSeg->m_HuffmanTable.get();
     }
   }
@@ -891,23 +777,22 @@
   if (pTRD->SBREFINE == 1) {
     const size_t size = GetRefAggContextSize(pTRD->SBRTEMPLATE);
     grContext.reset(FX_Alloc(JBig2ArithCtx, size));
-    JBIG2_memset(grContext.get(), 0, sizeof(JBig2ArithCtx) * size);
   }
   if (pTRD->SBHUFF == 0) {
     auto pArithDecoder =
         pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
     pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
     pSegment->m_Image =
-        pTRD->decode_Arith(pArithDecoder.get(), grContext.get(), nullptr);
+        pTRD->DecodeArith(pArithDecoder.get(), grContext.get(), nullptr);
     if (!pSegment->m_Image)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
     m_pStream->alignByte();
     m_pStream->offset(2);
   } else {
     pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
-    pSegment->m_Image = pTRD->decode_Huffman(m_pStream.get(), grContext.get());
+    pSegment->m_Image = pTRD->DecodeHuffman(m_pStream.get(), grContext.get());
     if (!pSegment->m_Image)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
     m_pStream->alignByte();
   }
   if (pSegment->m_cFlags.s.type != 4) {
@@ -915,28 +800,28 @@
       const auto& pPageInfo = m_PageInfoList.back();
       if ((pPageInfo->m_bIsStriped == 1) &&
           (ri.y + ri.height > m_pPage->height())) {
-        m_pPage->expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
+        m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
       }
     }
-    m_pPage->composeFrom(ri.x, ri.y, pSegment->m_Image.get(),
+    m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(),
                          (JBig2ComposeOp)(ri.flags & 0x03));
     pSegment->m_Image.reset();
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parsePatternDict(CJBig2_Segment* pSegment,
-                                         IFX_PauseIndicator* pPause) {
+JBig2_Result CJBig2_Context::ParsePatternDict(CJBig2_Segment* pSegment,
+                                              PauseIndicatorIface* pPause) {
   uint8_t cFlags;
   auto pPDD = pdfium::MakeUnique<CJBig2_PDDProc>();
   if (m_pStream->read1Byte(&cFlags) != 0 ||
       m_pStream->read1Byte(&pPDD->HDPW) != 0 ||
       m_pStream->read1Byte(&pPDD->HDPH) != 0 ||
       m_pStream->readInteger(&pPDD->GRAYMAX) != 0) {
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
   }
   if (pPDD->GRAYMAX > JBIG2_MAX_PATTERN_INDEX)
-    return JBIG2_ERROR_LIMIT;
+    return JBig2_Result::kFailure;
 
   pPDD->HDMMR = cFlags & 0x01;
   pPDD->HDTEMPLATE = (cFlags >> 1) & 0x03;
@@ -945,31 +830,30 @@
     const size_t size = GetHuffContextSize(pPDD->HDTEMPLATE);
     std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext(
         FX_Alloc(JBig2ArithCtx, size));
-    JBIG2_memset(gbContext.get(), 0, sizeof(JBig2ArithCtx) * size);
     auto pArithDecoder =
         pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
     pSegment->m_PatternDict =
-        pPDD->decode_Arith(pArithDecoder.get(), gbContext.get(), pPause);
+        pPDD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause);
     if (!pSegment->m_PatternDict)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
 
     m_pStream->alignByte();
     m_pStream->offset(2);
   } else {
-    pSegment->m_PatternDict = pPDD->decode_MMR(m_pStream.get());
+    pSegment->m_PatternDict = pPDD->DecodeMMR(m_pStream.get());
     if (!pSegment->m_PatternDict)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
     m_pStream->alignByte();
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseHalftoneRegion(CJBig2_Segment* pSegment,
-                                            IFX_PauseIndicator* pPause) {
+JBig2_Result CJBig2_Context::ParseHalftoneRegion(CJBig2_Segment* pSegment,
+                                                 PauseIndicatorIface* pPause) {
   uint8_t cFlags;
   JBig2RegionInfo ri;
   auto pHRD = pdfium::MakeUnique<CJBig2_HTRDProc>();
-  if (parseRegionInfo(&ri) != JBIG2_SUCCESS ||
+  if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess ||
       m_pStream->read1Byte(&cFlags) != 0 ||
       m_pStream->readInteger(&pHRD->HGW) != 0 ||
       m_pStream->readInteger(&pHRD->HGH) != 0 ||
@@ -977,11 +861,14 @@
       m_pStream->readInteger((uint32_t*)&pHRD->HGY) != 0 ||
       m_pStream->readShortInteger(&pHRD->HRX) != 0 ||
       m_pStream->readShortInteger(&pHRD->HRY) != 0) {
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
   }
 
-  if (pHRD->HGW == 0 || pHRD->HGH == 0)
-    return JBIG2_ERROR_FATAL;
+  if (!CJBig2_Image::IsValidImageSize(pHRD->HGW, pHRD->HGH))
+    return JBig2_Result::kFailure;
+
+  if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height))
+    return JBig2_Result::kFailure;
 
   pHRD->HBW = ri.width;
   pHRD->HBH = ri.height;
@@ -991,16 +878,16 @@
   pHRD->HCOMBOP = (JBig2ComposeOp)((cFlags >> 4) & 0x07);
   pHRD->HDEFPIXEL = (cFlags >> 7) & 0x01;
   if (pSegment->m_nReferred_to_segment_count != 1)
-    return JBIG2_ERROR_FATAL;
+    return JBig2_Result::kFailure;
 
   CJBig2_Segment* pSeg =
-      findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[0]);
+      FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[0]);
   if (!pSeg || (pSeg->m_cFlags.s.type != 16))
-    return JBIG2_ERROR_FATAL;
+    return JBig2_Result::kFailure;
 
   const CJBig2_PatternDict* pPatternDict = pSeg->m_PatternDict.get();
   if (!pPatternDict || (pPatternDict->NUMPATS == 0))
-    return JBIG2_ERROR_FATAL;
+    return JBig2_Result::kFailure;
 
   pHRD->HNUMPATS = pPatternDict->NUMPATS;
   pHRD->HPATS = &pPatternDict->HDPATS;
@@ -1011,20 +898,19 @@
     const size_t size = GetHuffContextSize(pHRD->HTEMPLATE);
     std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext(
         FX_Alloc(JBig2ArithCtx, size));
-    JBIG2_memset(gbContext.get(), 0, sizeof(JBig2ArithCtx) * size);
     auto pArithDecoder =
         pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
     pSegment->m_Image =
-        pHRD->decode_Arith(pArithDecoder.get(), gbContext.get(), pPause);
+        pHRD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause);
     if (!pSegment->m_Image)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
 
     m_pStream->alignByte();
     m_pStream->offset(2);
   } else {
-    pSegment->m_Image = pHRD->decode_MMR(m_pStream.get());
+    pSegment->m_Image = pHRD->DecodeMMR(m_pStream.get());
     if (!pSegment->m_Image)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
     m_pStream->alignByte();
   }
   if (pSegment->m_cFlags.s.type != 20) {
@@ -1032,27 +918,27 @@
       const auto& pPageInfo = m_PageInfoList.back();
       if (pPageInfo->m_bIsStriped == 1 &&
           ri.y + ri.height > m_pPage->height()) {
-        m_pPage->expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
+        m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
       }
     }
-    m_pPage->composeFrom(ri.x, ri.y, pSegment->m_Image.get(),
+    m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(),
                          (JBig2ComposeOp)(ri.flags & 0x03));
     pSegment->m_Image.reset();
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseGenericRegion(CJBig2_Segment* pSegment,
-                                           IFX_PauseIndicator* pPause) {
+JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment,
+                                                PauseIndicatorIface* pPause) {
   if (!m_pGRD) {
     auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
     uint8_t cFlags;
-    if (parseRegionInfo(&m_ri) != JBIG2_SUCCESS ||
+    if (ParseRegionInfo(&m_ri) != JBig2_Result::kSuccess ||
         m_pStream->read1Byte(&cFlags) != 0) {
-      return JBIG2_ERROR_TOO_SHORT;
+      return JBig2_Result::kFailure;
     }
     if (m_ri.height < 0 || m_ri.width < 0)
-      return JBIG2_FAILED;
+      return JBig2_Result::kFailure;
     pGRD->GBW = m_ri.width;
     pGRD->GBH = m_ri.height;
     pGRD->MMR = cFlags & 0x01;
@@ -1062,12 +948,12 @@
       if (pGRD->GBTEMPLATE == 0) {
         for (int32_t i = 0; i < 8; ++i) {
           if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0)
-            return JBIG2_ERROR_TOO_SHORT;
+            return JBig2_Result::kFailure;
         }
       } else {
         for (int32_t i = 0; i < 2; ++i) {
           if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0)
-            return JBIG2_ERROR_TOO_SHORT;
+            return JBig2_Result::kFailure;
         }
       }
     }
@@ -1078,46 +964,53 @@
   if (m_pGRD->MMR == 0) {
     if (m_gbContext.empty())
       m_gbContext.resize(GetHuffContextSize(m_pGRD->GBTEMPLATE));
-    if (!m_pArithDecoder) {
+
+    bool bStart = !m_pArithDecoder;
+    if (bStart) {
       m_pArithDecoder =
           pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
-      m_ProcessingStatus = m_pGRD->Start_decode_Arith(
-          &pSegment->m_Image, m_pArithDecoder.get(), &m_gbContext[0], pPause);
-    } else {
-      m_ProcessingStatus =
-          m_pGRD->Continue_decode(pPause, m_pArithDecoder.get());
     }
-    if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
-      if (pSegment->m_cFlags.s.type != 36) {
-        if (!m_bBufSpecified) {
-          const auto& pPageInfo = m_PageInfoList.back();
-          if ((pPageInfo->m_bIsStriped == 1) &&
-              (m_ri.y + m_ri.height > m_pPage->height())) {
-            m_pPage->expand(m_ri.y + m_ri.height,
-                            (pPageInfo->m_cFlags & 4) ? 1 : 0);
+    {
+      // |state.gbContext| can't exist when m_gbContext.clear() called below.
+      CJBig2_GRDProc::ProgressiveArithDecodeState state;
+      state.pImage = &pSegment->m_Image;
+      state.pArithDecoder = m_pArithDecoder.get();
+      state.gbContext = m_gbContext.data();
+      state.pPause = pPause;
+      m_ProcessingStatus = bStart ? m_pGRD->StartDecodeArith(&state)
+                                  : m_pGRD->ContinueDecode(&state);
+      if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
+        if (pSegment->m_cFlags.s.type != 36) {
+          if (!m_bBufSpecified) {
+            const auto& pPageInfo = m_PageInfoList.back();
+            if ((pPageInfo->m_bIsStriped == 1) &&
+                (m_ri.y + m_ri.height > m_pPage->height())) {
+              m_pPage->Expand(m_ri.y + m_ri.height,
+                              (pPageInfo->m_cFlags & 4) ? 1 : 0);
+            }
           }
+          const FX_RECT& rect = m_pGRD->GetReplaceRect();
+          m_pPage->ComposeFromWithRect(m_ri.x + rect.left, m_ri.y + rect.top,
+                                       pSegment->m_Image.get(), rect,
+                                       (JBig2ComposeOp)(m_ri.flags & 0x03));
         }
-        FX_RECT Rect = m_pGRD->GetReplaceRect();
-        m_pPage->composeFrom(m_ri.x + Rect.left, m_ri.y + Rect.top,
-                             pSegment->m_Image.get(),
-                             (JBig2ComposeOp)(m_ri.flags & 0x03), &Rect);
+        return JBig2_Result::kSuccess;
       }
-      return JBIG2_SUCCESS;
     }
     m_pArithDecoder.reset();
     m_gbContext.clear();
     if (!pSegment->m_Image) {
       m_ProcessingStatus = FXCODEC_STATUS_ERROR;
       m_pGRD.reset();
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
     }
     m_pStream->alignByte();
     m_pStream->offset(2);
   } else {
-    m_pGRD->Start_decode_MMR(&pSegment->m_Image, m_pStream.get());
+    m_pGRD->StartDecodeMMR(&pSegment->m_Image, m_pStream.get());
     if (!pSegment->m_Image) {
       m_pGRD.reset();
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
     }
     m_pStream->alignByte();
   }
@@ -1126,27 +1019,31 @@
       JBig2PageInfo* pPageInfo = m_PageInfoList.back().get();
       if ((pPageInfo->m_bIsStriped == 1) &&
           (m_ri.y + m_ri.height > m_pPage->height())) {
-        m_pPage->expand(m_ri.y + m_ri.height,
+        m_pPage->Expand(m_ri.y + m_ri.height,
                         (pPageInfo->m_cFlags & 4) ? 1 : 0);
       }
     }
-    FX_RECT Rect = m_pGRD->GetReplaceRect();
-    m_pPage->composeFrom(m_ri.x + Rect.left, m_ri.y + Rect.top,
-                         pSegment->m_Image.get(),
-                         (JBig2ComposeOp)(m_ri.flags & 0x03), &Rect);
+    const FX_RECT& rect = m_pGRD->GetReplaceRect();
+    m_pPage->ComposeFromWithRect(m_ri.x + rect.left, m_ri.y + rect.top,
+                                 pSegment->m_Image.get(), rect,
+                                 (JBig2ComposeOp)(m_ri.flags & 0x03));
     pSegment->m_Image.reset();
   }
   m_pGRD.reset();
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseGenericRefinementRegion(CJBig2_Segment* pSegment) {
+JBig2_Result CJBig2_Context::ParseGenericRefinementRegion(
+    CJBig2_Segment* pSegment) {
   JBig2RegionInfo ri;
   uint8_t cFlags;
-  if (parseRegionInfo(&ri) != JBIG2_SUCCESS ||
+  if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess ||
       m_pStream->read1Byte(&cFlags) != 0) {
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
   }
+  if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height))
+    return JBig2_Result::kFailure;
+
   auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
   pGRRD->GRW = ri.width;
   pGRRD->GRH = ri.height;
@@ -1155,16 +1052,16 @@
   if (!pGRRD->GRTEMPLATE) {
     for (int32_t i = 0; i < 4; ++i) {
       if (m_pStream->read1Byte((uint8_t*)&pGRRD->GRAT[i]) != 0)
-        return JBIG2_ERROR_TOO_SHORT;
+        return JBig2_Result::kFailure;
     }
   }
   CJBig2_Segment* pSeg = nullptr;
   if (pSegment->m_nReferred_to_segment_count > 0) {
     int32_t i;
     for (i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) {
-      pSeg = findSegmentByNumber(pSegment->m_Referred_to_segment_numbers[0]);
+      pSeg = FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[0]);
       if (!pSeg)
-        return JBIG2_ERROR_FATAL;
+        return JBig2_Result::kFailure;
 
       if (pSeg->m_cFlags.s.type == 4 || pSeg->m_cFlags.s.type == 20 ||
           pSeg->m_cFlags.s.type == 36 || pSeg->m_cFlags.s.type == 40) {
@@ -1172,7 +1069,7 @@
       }
     }
     if (i >= pSegment->m_nReferred_to_segment_count)
-      return JBIG2_ERROR_FATAL;
+      return JBig2_Result::kFailure;
 
     pGRRD->GRREFERENCE = pSeg->m_Image.get();
   } else {
@@ -1183,12 +1080,11 @@
   const size_t size = GetRefAggContextSize(pGRRD->GRTEMPLATE);
   std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> grContext(
       FX_Alloc(JBig2ArithCtx, size));
-  JBIG2_memset(grContext.get(), 0, sizeof(JBig2ArithCtx) * size);
   auto pArithDecoder = pdfium::MakeUnique<CJBig2_ArithDecoder>(m_pStream.get());
   pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
-  pSegment->m_Image = pGRRD->decode(pArithDecoder.get(), grContext.get());
+  pSegment->m_Image = pGRRD->Decode(pArithDecoder.get(), grContext.get());
   if (!pSegment->m_Image)
-    return JBIG2_ERROR_FATAL;
+    return JBig2_Result::kFailure;
 
   m_pStream->alignByte();
   m_pStream->offset(2);
@@ -1197,66 +1093,69 @@
       JBig2PageInfo* pPageInfo = m_PageInfoList.back().get();
       if ((pPageInfo->m_bIsStriped == 1) &&
           (ri.y + ri.height > m_pPage->height())) {
-        m_pPage->expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
+        m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0);
       }
     }
-    m_pPage->composeFrom(ri.x, ri.y, pSegment->m_Image.get(),
+    m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(),
                          (JBig2ComposeOp)(ri.flags & 0x03));
     pSegment->m_Image.reset();
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseTable(CJBig2_Segment* pSegment) {
+JBig2_Result CJBig2_Context::ParseTable(CJBig2_Segment* pSegment) {
   pSegment->m_nResultType = JBIG2_HUFFMAN_TABLE_POINTER;
   pSegment->m_HuffmanTable.reset();
   auto pHuff = pdfium::MakeUnique<CJBig2_HuffmanTable>(m_pStream.get());
   if (!pHuff->IsOK())
-    return JBIG2_ERROR_FATAL;
+    return JBig2_Result::kFailure;
 
   pSegment->m_HuffmanTable = std::move(pHuff);
   m_pStream->alignByte();
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-int32_t CJBig2_Context::parseRegionInfo(JBig2RegionInfo* pRI) {
+JBig2_Result CJBig2_Context::ParseRegionInfo(JBig2RegionInfo* pRI) {
   if (m_pStream->readInteger((uint32_t*)&pRI->width) != 0 ||
       m_pStream->readInteger((uint32_t*)&pRI->height) != 0 ||
       m_pStream->readInteger((uint32_t*)&pRI->x) != 0 ||
       m_pStream->readInteger((uint32_t*)&pRI->y) != 0 ||
       m_pStream->read1Byte(&pRI->flags) != 0) {
-    return JBIG2_ERROR_TOO_SHORT;
+    return JBig2_Result::kFailure;
   }
-  return JBIG2_SUCCESS;
+  return JBig2_Result::kSuccess;
 }
 
-std::vector<JBig2HuffmanCode> CJBig2_Context::decodeSymbolIDHuffmanTable(
-    CJBig2_BitStream* pStream,
+std::vector<JBig2HuffmanCode> CJBig2_Context::DecodeSymbolIDHuffmanTable(
     uint32_t SBNUMSYMS) {
   const size_t kRunCodesSize = 35;
   JBig2HuffmanCode huffman_codes[kRunCodesSize];
   for (size_t i = 0; i < kRunCodesSize; ++i) {
-    if (pStream->readNBits(4, &huffman_codes[i].codelen) != 0)
+    if (m_pStream->readNBits(4, &huffman_codes[i].codelen) != 0)
       return std::vector<JBig2HuffmanCode>();
   }
-  huffman_assign_code(huffman_codes, kRunCodesSize);
+  if (!HuffmanAssignCode(huffman_codes, kRunCodesSize))
+    return std::vector<JBig2HuffmanCode>();
 
   std::vector<JBig2HuffmanCode> SBSYMCODES(SBNUMSYMS);
   int32_t run = 0;
   int32_t i = 0;
   while (i < static_cast<int>(SBNUMSYMS)) {
     size_t j;
-    int32_t nVal = 0;
+    FX_SAFE_INT32 nSafeVal = 0;
     int32_t nBits = 0;
     uint32_t nTemp;
     while (true) {
-      if (nVal > std::numeric_limits<int32_t>::max() / 2 ||
-          pStream->read1Bit(&nTemp) != 0) {
+      if (m_pStream->read1Bit(&nTemp) != 0)
         return std::vector<JBig2HuffmanCode>();
-      }
 
-      nVal = (nVal << 1) | nTemp;
+      nSafeVal <<= 1;
+      if (!nSafeVal.IsValid())
+        return std::vector<JBig2HuffmanCode>();
+
+      nSafeVal |= nTemp;
       ++nBits;
+      const int32_t nVal = nSafeVal.ValueOrDie();
       for (j = 0; j < kRunCodesSize; ++j) {
         if (nBits == huffman_codes[j].codelen && nVal == huffman_codes[j].code)
           break;
@@ -1269,15 +1168,15 @@
       SBSYMCODES[i].codelen = runcode;
       run = 0;
     } else if (runcode == 32) {
-      if (pStream->readNBits(2, &nTemp) != 0)
+      if (m_pStream->readNBits(2, &nTemp) != 0)
         return std::vector<JBig2HuffmanCode>();
       run = nTemp + 3;
     } else if (runcode == 33) {
-      if (pStream->readNBits(3, &nTemp) != 0)
+      if (m_pStream->readNBits(3, &nTemp) != 0)
         return std::vector<JBig2HuffmanCode>();
       run = nTemp + 3;
     } else if (runcode == 34) {
-      if (pStream->readNBits(7, &nTemp) != 0)
+      if (m_pStream->readNBits(7, &nTemp) != 0)
         return std::vector<JBig2HuffmanCode>();
       run = nTemp + 11;
     }
@@ -1295,29 +1194,45 @@
       ++i;
     }
   }
-  huffman_assign_code(SBSYMCODES.data(), SBNUMSYMS);
+  if (!HuffmanAssignCode(SBSYMCODES.data(), SBNUMSYMS))
+    return std::vector<JBig2HuffmanCode>();
   return SBSYMCODES;
 }
 
-void CJBig2_Context::huffman_assign_code(JBig2HuffmanCode* SBSYMCODES,
-                                         int NTEMP) {
-  // TODO(thestig) CJBig2_HuffmanTable::parseFromCodedBuffer() has similar code.
+const CJBig2_HuffmanTable* CJBig2_Context::GetHuffmanTable(size_t idx) {
+  ASSERT(idx > 0);
+  ASSERT(idx < CJBig2_HuffmanTable::kNumHuffmanTables);
+  if (!m_HuffmanTables[idx].get())
+    m_HuffmanTables[idx] = pdfium::MakeUnique<CJBig2_HuffmanTable>(idx);
+  return m_HuffmanTables[idx].get();
+}
+
+// static
+bool CJBig2_Context::HuffmanAssignCode(JBig2HuffmanCode* SBSYMCODES,
+                                       uint32_t NTEMP) {
   int LENMAX = 0;
-  for (int i = 0; i < NTEMP; ++i)
-    LENMAX = std::max(LENMAX, SBSYMCODES[i].codelen);
+  for (uint32_t i = 0; i < NTEMP; ++i)
+    LENMAX = std::max(SBSYMCODES[i].codelen, LENMAX);
+
   std::vector<int> LENCOUNT(LENMAX + 1);
   std::vector<int> FIRSTCODE(LENMAX + 1);
-  for (int i = 0; i < NTEMP; ++i)
+  for (uint32_t i = 0; i < NTEMP; ++i)
     ++LENCOUNT[SBSYMCODES[i].codelen];
   LENCOUNT[0] = 0;
-  for (int CURLEN = 1; CURLEN <= LENMAX; ++CURLEN) {
-    FIRSTCODE[CURLEN] = (FIRSTCODE[CURLEN - 1] + LENCOUNT[CURLEN - 1]) << 1;
-    int CURCODE = FIRSTCODE[CURLEN];
-    for (int CURTEMP = 0; CURTEMP < NTEMP; ++CURTEMP) {
-      if (SBSYMCODES[CURTEMP].codelen == CURLEN) {
-        SBSYMCODES[CURTEMP].code = CURCODE;
-        CURCODE = CURCODE + 1;
-      }
+
+  for (int i = 1; i <= LENMAX; ++i) {
+    pdfium::base::CheckedNumeric<int> shifted = FIRSTCODE[i - 1];
+    shifted += LENCOUNT[i - 1];
+    shifted <<= 1;
+    if (!shifted.IsValid())
+      return false;
+
+    FIRSTCODE[i] = shifted.ValueOrDie();
+    int CURCODE = FIRSTCODE[i];
+    for (uint32_t j = 0; j < NTEMP; ++j) {
+      if (SBSYMCODES[j].codelen == i)
+        SBSYMCODES[j].code = CURCODE++;
     }
   }
+  return true;
 }
diff --git a/core/fxcodec/jbig2/JBig2_Context.h b/core/fxcodec/jbig2/JBig2_Context.h
index f86aa2e..49669ed 100644
--- a/core/fxcodec/jbig2/JBig2_Context.h
+++ b/core/fxcodec/jbig2/JBig2_Context.h
@@ -12,99 +12,98 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcodec/fx_codec_def.h"
 #include "core/fxcodec/jbig2/JBig2_Page.h"
 #include "core/fxcodec/jbig2/JBig2_Segment.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/span.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_GRDProc;
 class CPDF_StreamAcc;
-class IFX_PauseIndicator;
+class PauseIndicatorIface;
 
 // Cache is keyed by the ObjNum of a stream and an index within the stream.
 using CJBig2_CacheKey = std::pair<uint32_t, uint32_t>;
 using CJBig2_CachePair =
     std::pair<CJBig2_CacheKey, std::unique_ptr<CJBig2_SymbolDict>>;
 
-#define JBIG2_SUCCESS 0
-#define JBIG2_FAILED -1
-#define JBIG2_ERROR_TOO_SHORT -2
-#define JBIG2_ERROR_FATAL -3
-#define JBIG2_END_OF_PAGE 2
-#define JBIG2_END_OF_FILE 3
-#define JBIG2_ERROR_LIMIT -6
 #define JBIG2_MIN_SEGMENT_SIZE 11
 
+enum class JBig2_Result { kSuccess, kFailure, kEndReached };
+
 class CJBig2_Context {
  public:
-  CJBig2_Context(const RetainPtr<CPDF_StreamAcc>& pGlobalStream,
-                 const RetainPtr<CPDF_StreamAcc>& pSrcStream,
-                 std::list<CJBig2_CachePair>* pSymbolDictCache,
-                 bool bIsGlobal);
+  static std::unique_ptr<CJBig2_Context> Create(
+      pdfium::span<const uint8_t> pGlobalSpan,
+      uint32_t dwGlobalObjNum,
+      pdfium::span<const uint8_t> pSrcSpan,
+      uint32_t dwSrcObjNum,
+      std::list<CJBig2_CachePair>* pSymbolDictCache);
+
   ~CJBig2_Context();
 
-  int32_t getFirstPage(uint8_t* pBuf,
-                       int32_t width,
-                       int32_t height,
-                       int32_t stride,
-                       IFX_PauseIndicator* pPause);
+  static bool HuffmanAssignCode(JBig2HuffmanCode* SBSYMCODES, uint32_t NTEMP);
 
-  int32_t Continue(IFX_PauseIndicator* pPause);
+  bool GetFirstPage(uint8_t* pBuf,
+                    int32_t width,
+                    int32_t height,
+                    int32_t stride,
+                    PauseIndicatorIface* pPause);
+
+  bool Continue(PauseIndicatorIface* pPause);
   FXCODEC_STATUS GetProcessingStatus() const { return m_ProcessingStatus; }
 
  private:
-  int32_t decodeSequential(IFX_PauseIndicator* pPause);
-  int32_t decodeRandomFirstPage(IFX_PauseIndicator* pPause);
-  int32_t decodeRandom(IFX_PauseIndicator* pPause);
+  CJBig2_Context(pdfium::span<const uint8_t> pSrcSpan,
+                 uint32_t dwObjNum,
+                 std::list<CJBig2_CachePair>* pSymbolDictCache,
+                 bool bIsGlobal);
 
-  CJBig2_Segment* findSegmentByNumber(uint32_t dwNumber);
-  CJBig2_Segment* findReferredSegmentByTypeAndIndex(CJBig2_Segment* pSegment,
-                                                    uint8_t cType,
-                                                    int32_t nIndex);
+  JBig2_Result DecodeSequential(PauseIndicatorIface* pPause);
 
-  int32_t parseSegmentHeader(CJBig2_Segment* pSegment);
-  int32_t parseSegmentData(CJBig2_Segment* pSegment,
-                           IFX_PauseIndicator* pPause);
-  int32_t ProcessingParseSegmentData(CJBig2_Segment* pSegment,
-                                     IFX_PauseIndicator* pPause);
-  int32_t parseSymbolDict(CJBig2_Segment* pSegment);
-  int32_t parseTextRegion(CJBig2_Segment* pSegment);
-  int32_t parsePatternDict(CJBig2_Segment* pSegment,
-                           IFX_PauseIndicator* pPause);
-  int32_t parseHalftoneRegion(CJBig2_Segment* pSegment,
-                              IFX_PauseIndicator* pPause);
-  int32_t parseGenericRegion(CJBig2_Segment* pSegment,
-                             IFX_PauseIndicator* pPause);
-  int32_t parseGenericRefinementRegion(CJBig2_Segment* pSegment);
-  int32_t parseTable(CJBig2_Segment* pSegment);
-  int32_t parseRegionInfo(JBig2RegionInfo* pRI);
+  CJBig2_Segment* FindSegmentByNumber(uint32_t dwNumber);
+  CJBig2_Segment* FindReferredTableSegmentByIndex(CJBig2_Segment* pSegment,
+                                                  int32_t nIndex);
 
-  std::vector<JBig2HuffmanCode> decodeSymbolIDHuffmanTable(
-      CJBig2_BitStream* pStream,
-      uint32_t SBNUMSYMS);
+  JBig2_Result ParseSegmentHeader(CJBig2_Segment* pSegment);
+  JBig2_Result ParseSegmentData(CJBig2_Segment* pSegment,
+                                PauseIndicatorIface* pPause);
+  JBig2_Result ProcessingParseSegmentData(CJBig2_Segment* pSegment,
+                                          PauseIndicatorIface* pPause);
+  JBig2_Result ParseSymbolDict(CJBig2_Segment* pSegment);
+  JBig2_Result ParseTextRegion(CJBig2_Segment* pSegment);
+  JBig2_Result ParsePatternDict(CJBig2_Segment* pSegment,
+                                PauseIndicatorIface* pPause);
+  JBig2_Result ParseHalftoneRegion(CJBig2_Segment* pSegment,
+                                   PauseIndicatorIface* pPause);
+  JBig2_Result ParseGenericRegion(CJBig2_Segment* pSegment,
+                                  PauseIndicatorIface* pPause);
+  JBig2_Result ParseGenericRefinementRegion(CJBig2_Segment* pSegment);
+  JBig2_Result ParseTable(CJBig2_Segment* pSegment);
+  JBig2_Result ParseRegionInfo(JBig2RegionInfo* pRI);
 
-  void huffman_assign_code(JBig2HuffmanCode* SBSYMCODES, int NTEMP);
+  std::vector<JBig2HuffmanCode> DecodeSymbolIDHuffmanTable(uint32_t SBNUMSYMS);
+  const CJBig2_HuffmanTable* GetHuffmanTable(size_t idx);
 
   std::unique_ptr<CJBig2_Context> m_pGlobalContext;
   std::unique_ptr<CJBig2_BitStream> m_pStream;
   std::vector<std::unique_ptr<CJBig2_Segment>> m_SegmentList;
   std::vector<std::unique_ptr<JBig2PageInfo>> m_PageInfoList;
   std::unique_ptr<CJBig2_Image> m_pPage;
-  size_t m_nSegmentDecoded;
-  bool m_bInPage;
-  bool m_bBufSpecified;
-  int32_t m_PauseStep;
-  FXCODEC_STATUS m_ProcessingStatus;
+  std::vector<std::unique_ptr<CJBig2_HuffmanTable>> m_HuffmanTables;
+  const bool m_bIsGlobal;
+  bool m_bInPage = false;
+  bool m_bBufSpecified = false;
+  int32_t m_PauseStep = 10;
+  FXCODEC_STATUS m_ProcessingStatus = FXCODEC_STATUS_FRAME_READY;
   std::vector<JBig2ArithCtx> m_gbContext;
   std::unique_ptr<CJBig2_ArithDecoder> m_pArithDecoder;
   std::unique_ptr<CJBig2_GRDProc> m_pGRD;
   std::unique_ptr<CJBig2_Segment> m_pSegment;
-  FX_SAFE_UINT32 m_dwOffset;
+  FX_SAFE_UINT32 m_dwOffset = 0;
   JBig2RegionInfo m_ri;
   std::list<CJBig2_CachePair>* const m_pSymbolDictCache;
-  bool m_bIsGlobal;
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_CONTEXT_H_
diff --git a/core/fxcodec/jbig2/JBig2_Define.h b/core/fxcodec/jbig2/JBig2_Define.h
index 6fc3434..17eb007 100644
--- a/core/fxcodec/jbig2/JBig2_Define.h
+++ b/core/fxcodec/jbig2/JBig2_Define.h
@@ -7,10 +7,8 @@
 #ifndef CORE_FXCODEC_JBIG2_JBIG2_DEFINE_H_
 #define CORE_FXCODEC_JBIG2_JBIG2_DEFINE_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
 
-#define JBIG2_memset memset
-#define JBIG2_memcpy memcpy
 #define JBIG2_OOB 1
 
 struct JBig2RegionInfo {
diff --git a/core/fxcodec/jbig2/JBig2_DocumentContext.cpp b/core/fxcodec/jbig2/JBig2_DocumentContext.cpp
new file mode 100644
index 0000000..bca92fe
--- /dev/null
+++ b/core/fxcodec/jbig2/JBig2_DocumentContext.cpp
@@ -0,0 +1,14 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
+
+#include "core/fxcodec/jbig2/JBig2_Image.h"
+#include "core/fxcodec/jbig2/JBig2_SymbolDict.h"
+
+JBig2_DocumentContext::JBig2_DocumentContext() = default;
+
+JBig2_DocumentContext::~JBig2_DocumentContext() = default;
diff --git a/core/fxcodec/jbig2/JBig2_DocumentContext.h b/core/fxcodec/jbig2/JBig2_DocumentContext.h
new file mode 100644
index 0000000..9bcdd37
--- /dev/null
+++ b/core/fxcodec/jbig2/JBig2_DocumentContext.h
@@ -0,0 +1,34 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JBIG2_JBIG2_DOCUMENTCONTEXT_H_
+#define CORE_FXCODEC_JBIG2_JBIG2_DOCUMENTCONTEXT_H_
+
+#include <list>
+#include <memory>
+#include <utility>
+
+class CJBig2_SymbolDict;
+
+using CJBig2_CacheKey = std::pair<uint32_t, uint32_t>;
+using CJBig2_CachePair =
+    std::pair<CJBig2_CacheKey, std::unique_ptr<CJBig2_SymbolDict>>;
+
+// Holds per-document JBig2 related data.
+class JBig2_DocumentContext {
+ public:
+  JBig2_DocumentContext();
+  ~JBig2_DocumentContext();
+
+  std::list<CJBig2_CachePair>* GetSymbolDictCache() {
+    return &m_SymbolDictCache;
+  }
+
+ private:
+  std::list<CJBig2_CachePair> m_SymbolDictCache;
+};
+
+#endif  // CORE_FXCODEC_JBIG2_JBIG2_DOCUMENTCONTEXT_H_
diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrdProc.cpp
index e308ae3..df6e207 100644
--- a/core/fxcodec/jbig2/JBig2_GrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_GrdProc.cpp
@@ -6,28 +6,44 @@
 
 #include "core/fxcodec/jbig2/JBig2_GrdProc.h"
 
+#include <functional>
 #include <memory>
 #include <utility>
 
-#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/fax/faxmodule.h"
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 #include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "core/fxcrt/ifx_pauseindicator.h"
+#include "core/fxcrt/pauseindicator_iface.h"
 #include "third_party/base/ptr_util.h"
 
-CJBig2_GRDProc::CJBig2_GRDProc()
-    : m_loopIndex(0),
-      m_pLine(nullptr),
-      m_DecodeType(0),
-      m_LTP(0) {
-  m_ReplaceRect.left = 0;
-  m_ReplaceRect.bottom = 0;
-  m_ReplaceRect.top = 0;
-  m_ReplaceRect.right = 0;
-}
+namespace {
 
-CJBig2_GRDProc::~CJBig2_GRDProc() {}
+// TODO(npm): Name this constants better or merge some together.
+constexpr uint16_t kOptConstant1[] = {0x9b25, 0x0795, 0x00e5};
+constexpr uint16_t kOptConstant2[] = {0x0006, 0x0004, 0x0001};
+constexpr uint16_t kOptConstant3[] = {0xf800, 0x1e00, 0x0380};
+constexpr uint16_t kOptConstant4[] = {0x0000, 0x0001, 0x0003};
+constexpr uint16_t kOptConstant5[] = {0x07f0, 0x01f8, 0x007c};
+constexpr uint16_t kOptConstant6[] = {0x7bf7, 0x0efb, 0x01bd};
+constexpr uint16_t kOptConstant7[] = {0x0800, 0x0200, 0x0080};
+constexpr uint16_t kOptConstant8[] = {0x0010, 0x0008, 0x0004};
+constexpr uint16_t kOptConstant9[] = {0x000c, 0x0009, 0x0007};
+constexpr uint16_t kOptConstant10[] = {0x0007, 0x000f, 0x0007};
+constexpr uint16_t kOptConstant11[] = {0x001f, 0x001f, 0x000f};
+constexpr uint16_t kOptConstant12[] = {0x000f, 0x0007, 0x0003};
+
+}  // namespace
+
+CJBig2_GRDProc::ProgressiveArithDecodeState::ProgressiveArithDecodeState() =
+    default;
+
+CJBig2_GRDProc::ProgressiveArithDecodeState::~ProgressiveArithDecodeState() =
+    default;
+
+CJBig2_GRDProc::CJBig2_GRDProc() = default;
+
+CJBig2_GRDProc::~CJBig2_GRDProc() = default;
 
 bool CJBig2_GRDProc::UseTemplate0Opt3() const {
   return (GBAT[0] == 3) && (GBAT[1] == -1) && (GBAT[2] == -3) &&
@@ -43,36 +59,36 @@
   return (GBAT[0] == 2) && (GBAT[1] == -1);
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith(
+std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext) {
-  if (GBW == 0 || GBW > JBIG2_MAX_IMAGE_SIZE || GBH == 0 ||
-      GBH > JBIG2_MAX_IMAGE_SIZE) {
+  if (!CJBig2_Image::IsValidImageSize(GBW, GBH))
     return pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
-  }
 
-  if (GBTEMPLATE == 0) {
-    if (UseTemplate0Opt3())
-      return decode_Arith_Template0_opt3(pArithDecoder, gbContext);
-    return decode_Arith_Template0_unopt(pArithDecoder, gbContext);
-  } else if (GBTEMPLATE == 1) {
-    if (UseTemplate1Opt3())
-      return decode_Arith_Template1_opt3(pArithDecoder, gbContext);
-    return decode_Arith_Template1_unopt(pArithDecoder, gbContext);
-  } else if (GBTEMPLATE == 2) {
-    if (UseTemplate23Opt3())
-      return decode_Arith_Template2_opt3(pArithDecoder, gbContext);
-    return decode_Arith_Template2_unopt(pArithDecoder, gbContext);
-  } else {
-    if (UseTemplate23Opt3())
-      return decode_Arith_Template3_opt3(pArithDecoder, gbContext);
-    return decode_Arith_Template3_unopt(pArithDecoder, gbContext);
+  switch (GBTEMPLATE) {
+    case 0:
+      return UseTemplate0Opt3()
+                 ? DecodeArithOpt3(pArithDecoder, gbContext, 0)
+                 : DecodeArithTemplateUnopt(pArithDecoder, gbContext, 0);
+    case 1:
+      return UseTemplate1Opt3()
+                 ? DecodeArithOpt3(pArithDecoder, gbContext, 1)
+                 : DecodeArithTemplateUnopt(pArithDecoder, gbContext, 1);
+    case 2:
+      return UseTemplate23Opt3()
+                 ? DecodeArithOpt3(pArithDecoder, gbContext, 2)
+                 : DecodeArithTemplateUnopt(pArithDecoder, gbContext, 2);
+    default:
+      return UseTemplate23Opt3()
+                 ? DecodeArithTemplate3Opt3(pArithDecoder, gbContext)
+                 : DecodeArithTemplate3Unopt(pArithDecoder, gbContext);
   }
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template0_opt3(
+std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithOpt3(
     CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext) {
+    JBig2ArithCtx* gbContext,
+    int OPT) {
   auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
   if (!GBREG->data())
     return nullptr;
@@ -83,376 +99,89 @@
   int32_t nStride2 = nStride << 1;
   int32_t nLineBytes = ((GBW + 7) >> 3) - 1;
   int32_t nBitsLeft = GBW - (nLineBytes << 3);
-  uint32_t height = GBH & 0x7fffffff;
-  for (uint32_t h = 0; h < height; h++) {
+  // TODO(npm): Why is the height only trimmed when OPT is 0?
+  uint32_t height = OPT == 0 ? GBH & 0x7fffffff : GBH;
+  for (uint32_t h = 0; h < height; ++h) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x9b25]);
+      LTP = LTP ^ pArithDecoder->Decode(&gbContext[kOptConstant1[OPT]]);
     }
     if (LTP) {
-      GBREG->copyLine(h, h - 1);
+      GBREG->CopyLine(h, h - 1);
     } else {
       if (h > 1) {
         uint8_t* pLine1 = pLine - nStride2;
         uint8_t* pLine2 = pLine - nStride;
-        uint32_t line1 = (*pLine1++) << 6;
+        uint32_t line1 = (*pLine1++) << kOptConstant2[OPT];
         uint32_t line2 = *pLine2++;
-        uint32_t CONTEXT = ((line1 & 0xf800) | (line2 & 0x07f0));
-        for (int32_t cc = 0; cc < nLineBytes; cc++) {
-          line1 = (line1 << 8) | ((*pLine1++) << 6);
+        uint32_t CONTEXT = (line1 & kOptConstant3[OPT]) |
+                           ((line2 >> kOptConstant4[OPT]) & kOptConstant5[OPT]);
+        for (int32_t cc = 0; cc < nLineBytes; ++cc) {
+          line1 = (line1 << 8) | ((*pLine1++) << kOptConstant2[OPT]);
           line2 = (line2 << 8) | (*pLine2++);
           uint8_t cVal = 0;
-          for (int32_t k = 7; k >= 0; k--) {
+          for (int32_t k = 7; k >= 0; --k) {
             if (pArithDecoder->IsComplete())
               return nullptr;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-            cVal |= bVal << k;
-            CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal |
-                       ((line1 >> k) & 0x0800) | ((line2 >> k) & 0x0010));
-          }
-          pLine[cc] = cVal;
-        }
-        line1 <<= 8;
-        line2 <<= 8;
-        uint8_t cVal1 = 0;
-        for (int32_t k = 0; k < nBitsLeft; k++) {
-          if (pArithDecoder->IsComplete())
-            return nullptr;
-
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-          cVal1 |= bVal << (7 - k);
-          CONTEXT =
-              (((CONTEXT & 0x7bf7) << 1) | bVal |
-               ((line1 >> (7 - k)) & 0x0800) | ((line2 >> (7 - k)) & 0x0010));
-        }
-        pLine[nLineBytes] = cVal1;
-      } else {
-        uint8_t* pLine2 = pLine - nStride;
-        uint32_t line2 = (h & 1) ? (*pLine2++) : 0;
-        uint32_t CONTEXT = (line2 & 0x07f0);
-        for (int32_t cc = 0; cc < nLineBytes; cc++) {
-          if (h & 1) {
-            line2 = (line2 << 8) | (*pLine2++);
-          }
-          uint8_t cVal = 0;
-          for (int32_t k = 7; k >= 0; k--) {
-            if (pArithDecoder->IsComplete())
-              return nullptr;
-
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT =
-                (((CONTEXT & 0x7bf7) << 1) | bVal | ((line2 >> k) & 0x0010));
-          }
-          pLine[cc] = cVal;
-        }
-        line2 <<= 8;
-        uint8_t cVal1 = 0;
-        for (int32_t k = 0; k < nBitsLeft; k++) {
-          if (pArithDecoder->IsComplete())
-            return nullptr;
-
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-          cVal1 |= bVal << (7 - k);
-          CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal |
-                     (((line2 >> (7 - k))) & 0x0010));
-        }
-        pLine[nLineBytes] = cVal1;
-      }
-    }
-    pLine += nStride;
-  }
-  return GBREG;
-}
-
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template0_unopt(
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext) {
-  int LTP = 0;
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
-  GBREG->fill(0);
-  for (uint32_t h = 0; h < GBH; h++) {
-    if (TPGDON) {
-      if (pArithDecoder->IsComplete())
-        return nullptr;
-
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x9b25]);
-    }
-    if (LTP) {
-      GBREG->copyLine(h, h - 1);
-    } else {
-      uint32_t line1 = GBREG->getPixel(1, h - 2);
-      line1 |= GBREG->getPixel(0, h - 2) << 1;
-      uint32_t line2 = GBREG->getPixel(2, h - 1);
-      line2 |= GBREG->getPixel(1, h - 1) << 1;
-      line2 |= GBREG->getPixel(0, h - 1) << 2;
-      uint32_t line3 = 0;
-      for (uint32_t w = 0; w < GBW; w++) {
-        int bVal;
-        if (USESKIP && SKIP->getPixel(w, h)) {
-          bVal = 0;
-        } else {
-          uint32_t CONTEXT = line3;
-          CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 4;
-          CONTEXT |= line2 << 5;
-          CONTEXT |= GBREG->getPixel(w + GBAT[2], h + GBAT[3]) << 10;
-          CONTEXT |= GBREG->getPixel(w + GBAT[4], h + GBAT[5]) << 11;
-          CONTEXT |= line1 << 12;
-          CONTEXT |= GBREG->getPixel(w + GBAT[6], h + GBAT[7]) << 15;
-          if (pArithDecoder->IsComplete())
-            return nullptr;
-
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-        }
-        if (bVal) {
-          GBREG->setPixel(w, h, bVal);
-        }
-        line1 = ((line1 << 1) | GBREG->getPixel(w + 2, h - 2)) & 0x07;
-        line2 = ((line2 << 1) | GBREG->getPixel(w + 3, h - 1)) & 0x1f;
-        line3 = ((line3 << 1) | bVal) & 0x0f;
-      }
-    }
-  }
-  return GBREG;
-}
-
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template1_opt3(
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext) {
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
-  if (!GBREG->data())
-    return nullptr;
-
-  int LTP = 0;
-  uint8_t* pLine = GBREG->data();
-  int32_t nStride = GBREG->stride();
-  int32_t nStride2 = nStride << 1;
-  int32_t nLineBytes = ((GBW + 7) >> 3) - 1;
-  int32_t nBitsLeft = GBW - (nLineBytes << 3);
-  for (uint32_t h = 0; h < GBH; h++) {
-    if (TPGDON) {
-      if (pArithDecoder->IsComplete())
-        return nullptr;
-
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x0795]);
-    }
-    if (LTP) {
-      GBREG->copyLine(h, h - 1);
-    } else {
-      if (h > 1) {
-        uint8_t* pLine1 = pLine - nStride2;
-        uint8_t* pLine2 = pLine - nStride;
-        uint32_t line1 = (*pLine1++) << 4;
-        uint32_t line2 = *pLine2++;
-        uint32_t CONTEXT = (line1 & 0x1e00) | ((line2 >> 1) & 0x01f8);
-        for (int32_t cc = 0; cc < nLineBytes; cc++) {
-          line1 = (line1 << 8) | ((*pLine1++) << 4);
-          line2 = (line2 << 8) | (*pLine2++);
-          uint8_t cVal = 0;
-          for (int32_t k = 7; k >= 0; k--) {
-            if (pArithDecoder->IsComplete())
-              return nullptr;
-
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-            cVal |= bVal << k;
-            CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal |
-                      ((line1 >> k) & 0x0200) | ((line2 >> (k + 1)) & 0x0008);
+                (((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
+                 ((line1 >> k) & kOptConstant7[OPT]) |
+                 ((line2 >> (k + kOptConstant4[OPT])) & kOptConstant8[OPT]));
           }
           pLine[cc] = cVal;
         }
         line1 <<= 8;
         line2 <<= 8;
         uint8_t cVal1 = 0;
-        for (int32_t k = 0; k < nBitsLeft; k++) {
+        for (int32_t k = 0; k < nBitsLeft; ++k) {
           if (pArithDecoder->IsComplete())
             return nullptr;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-          cVal1 |= bVal << (7 - k);
-          CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal |
-                    ((line1 >> (7 - k)) & 0x0200) |
-                    ((line2 >> (8 - k)) & 0x0008);
-        }
-        pLine[nLineBytes] = cVal1;
-      } else {
-        uint8_t* pLine2 = pLine - nStride;
-        uint32_t line2 = (h & 1) ? (*pLine2++) : 0;
-        uint32_t CONTEXT = (line2 >> 1) & 0x01f8;
-        for (int32_t cc = 0; cc < nLineBytes; cc++) {
-          if (h & 1) {
-            line2 = (line2 << 8) | (*pLine2++);
-          }
-          uint8_t cVal = 0;
-          for (int32_t k = 7; k >= 0; k--) {
-            if (pArithDecoder->IsComplete())
-              return nullptr;
-
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-            cVal |= bVal << k;
-            CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal |
-                      ((line2 >> (k + 1)) & 0x0008);
-          }
-          pLine[cc] = cVal;
-        }
-        line2 <<= 8;
-        uint8_t cVal1 = 0;
-        for (int32_t k = 0; k < nBitsLeft; k++) {
-          if (pArithDecoder->IsComplete())
-            return nullptr;
-
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT =
-              ((CONTEXT & 0x0efb) << 1) | bVal | ((line2 >> (8 - k)) & 0x0008);
-        }
-        pLine[nLineBytes] = cVal1;
-      }
-    }
-    pLine += nStride;
-  }
-  return GBREG;
-}
-
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template1_unopt(
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext) {
-  int LTP = 0;
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
-  GBREG->fill(0);
-  for (uint32_t h = 0; h < GBH; h++) {
-    if (TPGDON) {
-      if (pArithDecoder->IsComplete())
-        return nullptr;
-
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x0795]);
-    }
-    if (LTP) {
-      GBREG->copyLine(h, h - 1);
-    } else {
-      uint32_t line1 = GBREG->getPixel(2, h - 2);
-      line1 |= GBREG->getPixel(1, h - 2) << 1;
-      line1 |= GBREG->getPixel(0, h - 2) << 2;
-      uint32_t line2 = GBREG->getPixel(2, h - 1);
-      line2 |= GBREG->getPixel(1, h - 1) << 1;
-      line2 |= GBREG->getPixel(0, h - 1) << 2;
-      uint32_t line3 = 0;
-      for (uint32_t w = 0; w < GBW; w++) {
-        int bVal;
-        if (USESKIP && SKIP->getPixel(w, h)) {
-          bVal = 0;
-        } else {
-          uint32_t CONTEXT = line3;
-          CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 3;
-          CONTEXT |= line2 << 4;
-          CONTEXT |= line1 << 9;
-          if (pArithDecoder->IsComplete())
-            return nullptr;
-
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-        }
-        if (bVal) {
-          GBREG->setPixel(w, h, bVal);
-        }
-        line1 = ((line1 << 1) | GBREG->getPixel(w + 3, h - 2)) & 0x0f;
-        line2 = ((line2 << 1) | GBREG->getPixel(w + 3, h - 1)) & 0x1f;
-        line3 = ((line3 << 1) | bVal) & 0x07;
-      }
-    }
-  }
-  return GBREG;
-}
-
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template2_opt3(
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext) {
-  auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
-  if (!GBREG->data())
-    return nullptr;
-
-  int LTP = 0;
-  uint8_t* pLine = GBREG->data();
-  int32_t nStride = GBREG->stride();
-  int32_t nStride2 = nStride << 1;
-  int32_t nLineBytes = ((GBW + 7) >> 3) - 1;
-  int32_t nBitsLeft = GBW - (nLineBytes << 3);
-  for (uint32_t h = 0; h < GBH; h++) {
-    if (TPGDON) {
-      if (pArithDecoder->IsComplete())
-        return nullptr;
-
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x00e5]);
-    }
-    if (LTP) {
-      GBREG->copyLine(h, h - 1);
-    } else {
-      if (h > 1) {
-        uint8_t* pLine1 = pLine - nStride2;
-        uint8_t* pLine2 = pLine - nStride;
-        uint32_t line1 = (*pLine1++) << 1;
-        uint32_t line2 = *pLine2++;
-        uint32_t CONTEXT = (line1 & 0x0380) | ((line2 >> 3) & 0x007c);
-        for (int32_t cc = 0; cc < nLineBytes; cc++) {
-          line1 = (line1 << 8) | ((*pLine1++) << 1);
-          line2 = (line2 << 8) | (*pLine2++);
-          uint8_t cVal = 0;
-          for (int32_t k = 7; k >= 0; k--) {
-            if (pArithDecoder->IsComplete())
-              return nullptr;
-
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-            cVal |= bVal << k;
-            CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
-                      ((line1 >> k) & 0x0080) | ((line2 >> (k + 3)) & 0x0004);
-          }
-          pLine[cc] = cVal;
-        }
-        line1 <<= 8;
-        line2 <<= 8;
-        uint8_t cVal1 = 0;
-        for (int32_t k = 0; k < nBitsLeft; k++) {
-          if (pArithDecoder->IsComplete())
-            return nullptr;
-
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
-          cVal1 |= bVal << (7 - k);
-          CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
-                    ((line1 >> (7 - k)) & 0x0080) |
-                    ((line2 >> (10 - k)) & 0x0004);
+              (((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
+               ((line1 >> (7 - k)) & kOptConstant7[OPT]) |
+               ((line2 >> (7 + kOptConstant4[OPT] - k)) & kOptConstant8[OPT]));
         }
         pLine[nLineBytes] = cVal1;
       } else {
         uint8_t* pLine2 = pLine - nStride;
         uint32_t line2 = (h & 1) ? (*pLine2++) : 0;
-        uint32_t CONTEXT = (line2 >> 3) & 0x007c;
-        for (int32_t cc = 0; cc < nLineBytes; cc++) {
+        uint32_t CONTEXT = ((line2 >> kOptConstant4[OPT]) & kOptConstant5[OPT]);
+        for (int32_t cc = 0; cc < nLineBytes; ++cc) {
           if (h & 1) {
             line2 = (line2 << 8) | (*pLine2++);
           }
           uint8_t cVal = 0;
-          for (int32_t k = 7; k >= 0; k--) {
+          for (int32_t k = 7; k >= 0; --k) {
             if (pArithDecoder->IsComplete())
               return nullptr;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
-            CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
-                      ((line2 >> (k + 3)) & 0x0004);
+            CONTEXT =
+                (((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
+                 ((line2 >> (k + kOptConstant4[OPT])) & kOptConstant8[OPT]));
           }
           pLine[cc] = cVal;
         }
         line2 <<= 8;
         uint8_t cVal1 = 0;
-        for (int32_t k = 0; k < nBitsLeft; k++) {
+        for (int32_t k = 0; k < nBitsLeft; ++k) {
           if (pArithDecoder->IsComplete())
             return nullptr;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
-          CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
-                    (((line2 >> (10 - k))) & 0x0004);
+          CONTEXT = (((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
+                     (((line2 >> (7 + kOptConstant4[OPT] - k))) &
+                      kOptConstant8[OPT]));
         }
         pLine[nLineBytes] = cVal1;
       }
@@ -462,54 +191,69 @@
   return GBREG;
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template2_unopt(
+std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplateUnopt(
     CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext) {
-  int LTP = 0;
+    JBig2ArithCtx* gbContext,
+    int UNOPT) {
   auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
-  GBREG->fill(0);
+  if (!GBREG->data())
+    return nullptr;
+
+  GBREG->Fill(0);
+  int LTP = 0;
+  uint8_t MOD2 = UNOPT % 2;
+  uint8_t DIV2 = UNOPT / 2;
+  uint8_t SHIFT = 4 - UNOPT;
   for (uint32_t h = 0; h < GBH; h++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x00e5]);
+      LTP = LTP ^ pArithDecoder->Decode(&gbContext[kOptConstant1[UNOPT]]);
     }
     if (LTP) {
-      GBREG->copyLine(h, h - 1);
-    } else {
-      uint32_t line1 = GBREG->getPixel(1, h - 2);
-      line1 |= GBREG->getPixel(0, h - 2) << 1;
-      uint32_t line2 = GBREG->getPixel(1, h - 1);
-      line2 |= GBREG->getPixel(0, h - 1) << 1;
-      uint32_t line3 = 0;
-      for (uint32_t w = 0; w < GBW; w++) {
-        int bVal;
-        if (USESKIP && SKIP->getPixel(w, h)) {
-          bVal = 0;
-        } else {
-          uint32_t CONTEXT = line3;
-          CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 2;
-          CONTEXT |= line2 << 3;
-          CONTEXT |= line1 << 7;
-          if (pArithDecoder->IsComplete())
-            return nullptr;
+      GBREG->CopyLine(h, h - 1);
+      continue;
+    }
+    uint32_t line1 = GBREG->GetPixel(1 + MOD2, h - 2);
+    line1 |= GBREG->GetPixel(MOD2, h - 2) << 1;
+    if (UNOPT == 1)
+      line1 |= GBREG->GetPixel(0, h - 2) << 2;
+    uint32_t line2 = GBREG->GetPixel(2 - DIV2, h - 1);
+    line2 |= GBREG->GetPixel(1 - DIV2, h - 1) << 1;
+    if (UNOPT < 2)
+      line2 |= GBREG->GetPixel(0, h - 1) << 2;
+    uint32_t line3 = 0;
+    for (uint32_t w = 0; w < GBW; w++) {
+      int bVal = 0;
+      if (!USESKIP || !SKIP->GetPixel(w, h)) {
+        if (pArithDecoder->IsComplete())
+          return nullptr;
 
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+        uint32_t CONTEXT = line3;
+        CONTEXT |= GBREG->GetPixel(w + GBAT[0], h + GBAT[1]) << SHIFT;
+        CONTEXT |= line2 << (SHIFT + 1);
+        CONTEXT |= line1 << kOptConstant9[UNOPT];
+        if (UNOPT == 0) {
+          CONTEXT |= GBREG->GetPixel(w + GBAT[2], h + GBAT[3]) << 10;
+          CONTEXT |= GBREG->GetPixel(w + GBAT[4], h + GBAT[5]) << 11;
+          CONTEXT |= GBREG->GetPixel(w + GBAT[6], h + GBAT[7]) << 15;
         }
-        if (bVal) {
-          GBREG->setPixel(w, h, bVal);
-        }
-        line1 = ((line1 << 1) | GBREG->getPixel(w + 2, h - 2)) & 0x07;
-        line2 = ((line2 << 1) | GBREG->getPixel(w + 2, h - 1)) & 0x0f;
-        line3 = ((line3 << 1) | bVal) & 0x03;
+        bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
+        if (bVal)
+          GBREG->SetPixel(w, h, bVal);
       }
+      line1 = ((line1 << 1) | GBREG->GetPixel(w + 2 + MOD2, h - 2)) &
+              kOptConstant10[UNOPT];
+      line2 = ((line2 << 1) | GBREG->GetPixel(w + 3 - DIV2, h - 1)) &
+              kOptConstant11[UNOPT];
+      line3 = ((line3 << 1) | bVal) & kOptConstant12[UNOPT];
     }
   }
   return GBREG;
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template3_opt3(
+std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplate3Opt3(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext) {
   auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
@@ -527,11 +271,11 @@
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x0195]);
+      LTP = LTP ^ pArithDecoder->Decode(&gbContext[0x0195]);
     }
 
     if (LTP) {
-      GBREG->copyLine(h, h - 1);
+      GBREG->CopyLine(h, h - 1);
     } else {
       if (h > 0) {
         uint8_t* pLine1 = pLine - nStride;
@@ -544,7 +288,7 @@
             if (pArithDecoder->IsComplete())
               return nullptr;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal |
                       ((line1 >> (k + 1)) & 0x0010);
@@ -557,7 +301,7 @@
           if (pArithDecoder->IsComplete())
             return nullptr;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT =
               ((CONTEXT & 0x01f7) << 1) | bVal | ((line1 >> (8 - k)) & 0x0010);
@@ -571,7 +315,7 @@
             if (pArithDecoder->IsComplete())
               return nullptr;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
           }
@@ -582,7 +326,7 @@
           if (pArithDecoder->IsComplete())
             return nullptr;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
         }
@@ -594,42 +338,45 @@
   return GBREG;
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::decode_Arith_Template3_unopt(
+std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplate3Unopt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext) {
-  int LTP = 0;
   auto GBREG = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
-  GBREG->fill(0);
+  if (!GBREG->data())
+    return nullptr;
+
+  GBREG->Fill(0);
+  int LTP = 0;
   for (uint32_t h = 0; h < GBH; h++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&gbContext[0x0195]);
+      LTP = LTP ^ pArithDecoder->Decode(&gbContext[0x0195]);
     }
     if (LTP == 1) {
-      GBREG->copyLine(h, h - 1);
+      GBREG->CopyLine(h, h - 1);
     } else {
-      uint32_t line1 = GBREG->getPixel(1, h - 1);
-      line1 |= GBREG->getPixel(0, h - 1) << 1;
+      uint32_t line1 = GBREG->GetPixel(1, h - 1);
+      line1 |= GBREG->GetPixel(0, h - 1) << 1;
       uint32_t line2 = 0;
       for (uint32_t w = 0; w < GBW; w++) {
         int bVal;
-        if (USESKIP && SKIP->getPixel(w, h)) {
+        if (USESKIP && SKIP->GetPixel(w, h)) {
           bVal = 0;
         } else {
           uint32_t CONTEXT = line2;
-          CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 4;
+          CONTEXT |= GBREG->GetPixel(w + GBAT[0], h + GBAT[1]) << 4;
           CONTEXT |= line1 << 5;
           if (pArithDecoder->IsComplete())
             return nullptr;
 
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
         if (bVal) {
-          GBREG->setPixel(w, h, bVal);
+          GBREG->SetPixel(w, h, bVal);
         }
-        line1 = ((line1 << 1) | GBREG->getPixel(w + 2, h - 1)) & 0x1f;
+        line1 = ((line1 << 1) | GBREG->GetPixel(w + 2, h - 1)) & 0x1f;
         line2 = ((line2 << 1) | bVal) & 0x0f;
       }
     }
@@ -637,17 +384,14 @@
   return GBREG;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::Start_decode_Arith(
-    std::unique_ptr<CJBig2_Image>* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
-  if (GBW == 0 || GBW > JBIG2_MAX_IMAGE_SIZE || GBH == 0 ||
-      GBH > JBIG2_MAX_IMAGE_SIZE) {
+FXCODEC_STATUS CJBig2_GRDProc::StartDecodeArith(
+    ProgressiveArithDecodeState* pState) {
+  if (!CJBig2_Image::IsValidImageSize(GBW, GBH)) {
     m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
     return FXCODEC_STATUS_DECODE_FINISH;
   }
   m_ProssiveStatus = FXCODEC_STATUS_DECODE_READY;
+  std::unique_ptr<CJBig2_Image>* pImage = pState->pImage;
   if (!*pImage)
     *pImage = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
   if (!(*pImage)->data()) {
@@ -655,55 +399,47 @@
     m_ProssiveStatus = FXCODEC_STATUS_ERROR;
     return FXCODEC_STATUS_ERROR;
   }
+  pImage->get()->Fill(0);
   m_DecodeType = 1;
-  m_pImage = pImage->get();
-  m_pImage->fill(0);
-  m_gbContext = gbContext;
   m_LTP = 0;
   m_pLine = nullptr;
   m_loopIndex = 0;
-  return decode_Arith(pPause, pArithDecoder);
+  return ProgressiveDecodeArith(pState);
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith(
-    IFX_PauseIndicator* pPause,
-    CJBig2_ArithDecoder* pArithDecoder) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArith(
+    ProgressiveArithDecodeState* pState) {
   int iline = m_loopIndex;
-  if (GBTEMPLATE == 0) {
-    if (UseTemplate0Opt3()) {
-      m_ProssiveStatus = decode_Arith_Template0_opt3(m_pImage, pArithDecoder,
-                                                     m_gbContext, pPause);
-    } else {
-      m_ProssiveStatus = decode_Arith_Template0_unopt(m_pImage, pArithDecoder,
-                                                      m_gbContext, pPause);
-    }
-  } else if (GBTEMPLATE == 1) {
-    if (UseTemplate1Opt3()) {
-      m_ProssiveStatus = decode_Arith_Template1_opt3(m_pImage, pArithDecoder,
-                                                     m_gbContext, pPause);
-    } else {
-      m_ProssiveStatus = decode_Arith_Template1_unopt(m_pImage, pArithDecoder,
-                                                      m_gbContext, pPause);
-    }
-  } else if (GBTEMPLATE == 2) {
-    if (UseTemplate23Opt3()) {
-      m_ProssiveStatus = decode_Arith_Template2_opt3(m_pImage, pArithDecoder,
-                                                     m_gbContext, pPause);
-    } else {
-      m_ProssiveStatus = decode_Arith_Template2_unopt(m_pImage, pArithDecoder,
-                                                      m_gbContext, pPause);
-    }
-  } else {
-    if (UseTemplate23Opt3()) {
-      m_ProssiveStatus = decode_Arith_Template3_opt3(m_pImage, pArithDecoder,
-                                                     m_gbContext, pPause);
-    } else {
-      m_ProssiveStatus = decode_Arith_Template3_unopt(m_pImage, pArithDecoder,
-                                                      m_gbContext, pPause);
-    }
+
+  using DecodeFunction = std::function<FXCODEC_STATUS(
+      CJBig2_GRDProc&, ProgressiveArithDecodeState*)>;
+  DecodeFunction func;
+  switch (GBTEMPLATE) {
+    case 0:
+      func = UseTemplate0Opt3()
+                 ? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3
+                 : &CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt;
+      break;
+    case 1:
+      func = UseTemplate1Opt3()
+                 ? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3
+                 : &CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt;
+      break;
+    case 2:
+      func = UseTemplate23Opt3()
+                 ? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3
+                 : &CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt;
+      break;
+    default:
+      func = UseTemplate23Opt3()
+                 ? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3
+                 : &CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt;
+      break;
   }
+  CJBig2_Image* pImage = pState->pImage->get();
+  m_ProssiveStatus = func(*this, pState);
   m_ReplaceRect.left = 0;
-  m_ReplaceRect.right = m_pImage->width();
+  m_ReplaceRect.right = pImage->width();
   m_ReplaceRect.top = iline;
   m_ReplaceRect.bottom = m_loopIndex;
   if (m_ProssiveStatus == FXCODEC_STATUS_DECODE_FINISH)
@@ -712,30 +448,29 @@
   return m_ProssiveStatus;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::Start_decode_MMR(
+FXCODEC_STATUS CJBig2_GRDProc::StartDecodeMMR(
     std::unique_ptr<CJBig2_Image>* pImage,
     CJBig2_BitStream* pStream) {
-  int bitpos, i;
   auto image = pdfium::MakeUnique<CJBig2_Image>(GBW, GBH);
   if (!image->data()) {
     *pImage = nullptr;
     m_ProssiveStatus = FXCODEC_STATUS_ERROR;
     return m_ProssiveStatus;
   }
-  bitpos = static_cast<int>(pStream->getBitPos());
-  FaxG4Decode(pStream->getBuf(), pStream->getLength(), &bitpos, image->data(),
-              GBW, GBH, image->stride());
+  int bitpos = static_cast<int>(pStream->getBitPos());
+  bitpos =
+      FaxModule::FaxG4Decode(pStream->getBuf(), pStream->getLength(), bitpos,
+                             GBW, GBH, image->stride(), image->data());
   pStream->setBitPos(bitpos);
-  for (i = 0; (uint32_t)i < image->stride() * GBH; ++i)
+  for (uint32_t i = 0; i < image->stride() * GBH; ++i)
     image->data()[i] = ~image->data()[i];
   m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH;
   *pImage = std::move(image);
   return m_ProssiveStatus;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::Continue_decode(
-    IFX_PauseIndicator* pPause,
-    CJBig2_ArithDecoder* pArithDecoder) {
+FXCODEC_STATUS CJBig2_GRDProc::ContinueDecode(
+    ProgressiveArithDecodeState* pState) {
   if (m_ProssiveStatus != FXCODEC_STATUS_DECODE_TOBECONTINUE)
     return m_ProssiveStatus;
 
@@ -743,14 +478,14 @@
     m_ProssiveStatus = FXCODEC_STATUS_ERROR;
     return m_ProssiveStatus;
   }
-  return decode_Arith(pPause, pArithDecoder);
+  return ProgressiveDecodeArith(pState);
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template0_opt3(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -764,10 +499,10 @@
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x9b25]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x9b25]);
     }
     if (m_LTP) {
-      pImage->copyLine(m_loopIndex, m_loopIndex - 1);
+      pImage->CopyLine(m_loopIndex, m_loopIndex - 1);
     } else {
       if (m_loopIndex > 1) {
         uint8_t* pLine1 = m_pLine - nStride2;
@@ -783,7 +518,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal |
                        ((line1 >> k) & 0x0800) | ((line2 >> k) & 0x0010));
@@ -797,7 +532,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT =
               (((CONTEXT & 0x7bf7) << 1) | bVal |
@@ -817,7 +552,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT =
                 (((CONTEXT & 0x7bf7) << 1) | bVal | ((line2 >> k) & 0x0010));
@@ -830,7 +565,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal |
                      ((line2 >> (7 - k)) & 0x0010));
@@ -839,7 +574,7 @@
       }
     }
     m_pLine += nStride;
-    if (pPause && pPause->NeedToPauseNow()) {
+    if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
@@ -849,55 +584,55 @@
   return FXCODEC_STATUS_DECODE_FINISH;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template0_unopt(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x9b25]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x9b25]);
     }
     if (m_LTP) {
-      pImage->copyLine(m_loopIndex, m_loopIndex - 1);
+      pImage->CopyLine(m_loopIndex, m_loopIndex - 1);
     } else {
-      uint32_t line1 = pImage->getPixel(1, m_loopIndex - 2);
-      line1 |= pImage->getPixel(0, m_loopIndex - 2) << 1;
-      uint32_t line2 = pImage->getPixel(2, m_loopIndex - 1);
-      line2 |= pImage->getPixel(1, m_loopIndex - 1) << 1;
-      line2 |= pImage->getPixel(0, m_loopIndex - 1) << 2;
+      uint32_t line1 = pImage->GetPixel(1, m_loopIndex - 2);
+      line1 |= pImage->GetPixel(0, m_loopIndex - 2) << 1;
+      uint32_t line2 = pImage->GetPixel(2, m_loopIndex - 1);
+      line2 |= pImage->GetPixel(1, m_loopIndex - 1) << 1;
+      line2 |= pImage->GetPixel(0, m_loopIndex - 1) << 2;
       uint32_t line3 = 0;
       for (uint32_t w = 0; w < GBW; w++) {
         int bVal;
-        if (USESKIP && SKIP->getPixel(w, m_loopIndex)) {
+        if (USESKIP && SKIP->GetPixel(w, m_loopIndex)) {
           bVal = 0;
         } else {
           uint32_t CONTEXT = line3;
-          CONTEXT |= pImage->getPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4;
+          CONTEXT |= pImage->GetPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4;
           CONTEXT |= line2 << 5;
-          CONTEXT |= pImage->getPixel(w + GBAT[2], m_loopIndex + GBAT[3]) << 10;
-          CONTEXT |= pImage->getPixel(w + GBAT[4], m_loopIndex + GBAT[5]) << 11;
+          CONTEXT |= pImage->GetPixel(w + GBAT[2], m_loopIndex + GBAT[3]) << 10;
+          CONTEXT |= pImage->GetPixel(w + GBAT[4], m_loopIndex + GBAT[5]) << 11;
           CONTEXT |= line1 << 12;
-          CONTEXT |= pImage->getPixel(w + GBAT[6], m_loopIndex + GBAT[7]) << 15;
+          CONTEXT |= pImage->GetPixel(w + GBAT[6], m_loopIndex + GBAT[7]) << 15;
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
         if (bVal) {
-          pImage->setPixel(w, m_loopIndex, bVal);
+          pImage->SetPixel(w, m_loopIndex, bVal);
         }
         line1 =
-            ((line1 << 1) | pImage->getPixel(w + 2, m_loopIndex - 2)) & 0x07;
+            ((line1 << 1) | pImage->GetPixel(w + 2, m_loopIndex - 2)) & 0x07;
         line2 =
-            ((line2 << 1) | pImage->getPixel(w + 3, m_loopIndex - 1)) & 0x1f;
+            ((line2 << 1) | pImage->GetPixel(w + 3, m_loopIndex - 1)) & 0x1f;
         line3 = ((line3 << 1) | bVal) & 0x0f;
       }
     }
-    if (pPause && pPause->NeedToPauseNow()) {
+    if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
@@ -907,11 +642,11 @@
   return FXCODEC_STATUS_DECODE_FINISH;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template1_opt3(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -923,10 +658,10 @@
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x0795]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0795]);
     }
     if (m_LTP) {
-      pImage->copyLine(m_loopIndex, m_loopIndex - 1);
+      pImage->CopyLine(m_loopIndex, m_loopIndex - 1);
     } else {
       if (m_loopIndex > 1) {
         uint8_t* pLine1 = m_pLine - nStride2;
@@ -942,7 +677,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal |
                       ((line1 >> k) & 0x0200) | ((line2 >> (k + 1)) & 0x0008);
@@ -956,7 +691,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal |
                     ((line1 >> (7 - k)) & 0x0200) |
@@ -976,7 +711,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal |
                       ((line2 >> (k + 1)) & 0x0008);
@@ -989,7 +724,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT =
               ((CONTEXT & 0x0efb) << 1) | bVal | ((line2 >> (8 - k)) & 0x0008);
@@ -998,7 +733,7 @@
       }
     }
     m_pLine += nStride;
-    if (pPause && pPause->NeedToPauseNow()) {
+    if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
@@ -1008,51 +743,51 @@
   return FXCODEC_STATUS_DECODE_FINISH;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template1_unopt(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   for (uint32_t h = 0; h < GBH; h++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x0795]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0795]);
     }
     if (m_LTP) {
-      pImage->copyLine(h, h - 1);
+      pImage->CopyLine(h, h - 1);
     } else {
-      uint32_t line1 = pImage->getPixel(2, h - 2);
-      line1 |= pImage->getPixel(1, h - 2) << 1;
-      line1 |= pImage->getPixel(0, h - 2) << 2;
-      uint32_t line2 = pImage->getPixel(2, h - 1);
-      line2 |= pImage->getPixel(1, h - 1) << 1;
-      line2 |= pImage->getPixel(0, h - 1) << 2;
+      uint32_t line1 = pImage->GetPixel(2, h - 2);
+      line1 |= pImage->GetPixel(1, h - 2) << 1;
+      line1 |= pImage->GetPixel(0, h - 2) << 2;
+      uint32_t line2 = pImage->GetPixel(2, h - 1);
+      line2 |= pImage->GetPixel(1, h - 1) << 1;
+      line2 |= pImage->GetPixel(0, h - 1) << 2;
       uint32_t line3 = 0;
       for (uint32_t w = 0; w < GBW; w++) {
         int bVal;
-        if (USESKIP && SKIP->getPixel(w, h)) {
+        if (USESKIP && SKIP->GetPixel(w, h)) {
           bVal = 0;
         } else {
           uint32_t CONTEXT = line3;
-          CONTEXT |= pImage->getPixel(w + GBAT[0], h + GBAT[1]) << 3;
+          CONTEXT |= pImage->GetPixel(w + GBAT[0], h + GBAT[1]) << 3;
           CONTEXT |= line2 << 4;
           CONTEXT |= line1 << 9;
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
         if (bVal) {
-          pImage->setPixel(w, h, bVal);
+          pImage->SetPixel(w, h, bVal);
         }
-        line1 = ((line1 << 1) | pImage->getPixel(w + 3, h - 2)) & 0x0f;
-        line2 = ((line2 << 1) | pImage->getPixel(w + 3, h - 1)) & 0x1f;
+        line1 = ((line1 << 1) | pImage->GetPixel(w + 3, h - 2)) & 0x0f;
+        line2 = ((line2 << 1) | pImage->GetPixel(w + 3, h - 1)) & 0x1f;
         line3 = ((line3 << 1) | bVal) & 0x07;
       }
     }
-    if (pPause && pPause->NeedToPauseNow()) {
+    if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
@@ -1062,11 +797,11 @@
   return FXCODEC_STATUS_DECODE_FINISH;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template2_opt3(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -1078,10 +813,10 @@
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x00e5]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x00e5]);
     }
     if (m_LTP) {
-      pImage->copyLine(m_loopIndex, m_loopIndex - 1);
+      pImage->CopyLine(m_loopIndex, m_loopIndex - 1);
     } else {
       if (m_loopIndex > 1) {
         uint8_t* pLine1 = m_pLine - nStride2;
@@ -1097,7 +832,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
                       ((line1 >> k) & 0x0080) | ((line2 >> (k + 3)) & 0x0004);
@@ -1111,7 +846,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
                     ((line1 >> (7 - k)) & 0x0080) |
@@ -1131,7 +866,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
                       ((line2 >> (k + 3)) & 0x0004);
@@ -1144,7 +879,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal |
                     (((line2 >> (10 - k))) & 0x0004);
@@ -1153,7 +888,8 @@
       }
     }
     m_pLine += nStride;
-    if (pPause && m_loopIndex % 50 == 0 && pPause->NeedToPauseNow()) {
+    if (pState->pPause && m_loopIndex % 50 == 0 &&
+        pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
@@ -1163,51 +899,51 @@
   return FXCODEC_STATUS_DECODE_FINISH;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template2_unopt(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x00e5]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x00e5]);
     }
     if (m_LTP) {
-      pImage->copyLine(m_loopIndex, m_loopIndex - 1);
+      pImage->CopyLine(m_loopIndex, m_loopIndex - 1);
     } else {
-      uint32_t line1 = pImage->getPixel(1, m_loopIndex - 2);
-      line1 |= pImage->getPixel(0, m_loopIndex - 2) << 1;
-      uint32_t line2 = pImage->getPixel(1, m_loopIndex - 1);
-      line2 |= pImage->getPixel(0, m_loopIndex - 1) << 1;
+      uint32_t line1 = pImage->GetPixel(1, m_loopIndex - 2);
+      line1 |= pImage->GetPixel(0, m_loopIndex - 2) << 1;
+      uint32_t line2 = pImage->GetPixel(1, m_loopIndex - 1);
+      line2 |= pImage->GetPixel(0, m_loopIndex - 1) << 1;
       uint32_t line3 = 0;
       for (uint32_t w = 0; w < GBW; w++) {
         int bVal;
-        if (USESKIP && SKIP->getPixel(w, m_loopIndex)) {
+        if (USESKIP && SKIP->GetPixel(w, m_loopIndex)) {
           bVal = 0;
         } else {
           uint32_t CONTEXT = line3;
-          CONTEXT |= pImage->getPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 2;
+          CONTEXT |= pImage->GetPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 2;
           CONTEXT |= line2 << 3;
           CONTEXT |= line1 << 7;
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
         if (bVal) {
-          pImage->setPixel(w, m_loopIndex, bVal);
+          pImage->SetPixel(w, m_loopIndex, bVal);
         }
         line1 =
-            ((line1 << 1) | pImage->getPixel(w + 2, m_loopIndex - 2)) & 0x07;
+            ((line1 << 1) | pImage->GetPixel(w + 2, m_loopIndex - 2)) & 0x07;
         line2 =
-            ((line2 << 1) | pImage->getPixel(w + 2, m_loopIndex - 1)) & 0x0f;
+            ((line2 << 1) | pImage->GetPixel(w + 2, m_loopIndex - 1)) & 0x0f;
         line3 = ((line3 << 1) | bVal) & 0x03;
       }
     }
-    if (pPause && pPause->NeedToPauseNow()) {
+    if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
@@ -1217,11 +953,11 @@
   return FXCODEC_STATUS_DECODE_FINISH;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template3_opt3(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   if (!m_pLine)
     m_pLine = pImage->data();
   int32_t nStride = pImage->stride();
@@ -1232,10 +968,10 @@
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x0195]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0195]);
     }
     if (m_LTP) {
-      pImage->copyLine(m_loopIndex, m_loopIndex - 1);
+      pImage->CopyLine(m_loopIndex, m_loopIndex - 1);
     } else {
       if (m_loopIndex > 0) {
         uint8_t* pLine1 = m_pLine - nStride;
@@ -1248,7 +984,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal |
                       ((line1 >> (k + 1)) & 0x0010);
@@ -1261,7 +997,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT =
               ((CONTEXT & 0x01f7) << 1) | bVal | ((line1 >> (8 - k)) & 0x0010);
@@ -1275,7 +1011,7 @@
             if (pArithDecoder->IsComplete())
               return FXCODEC_STATUS_ERROR;
 
-            int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+            int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
             cVal |= bVal << k;
             CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
           }
@@ -1286,7 +1022,7 @@
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          int bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
           cVal1 |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
         }
@@ -1294,7 +1030,7 @@
       }
     }
     m_pLine += nStride;
-    if (pPause && pPause->NeedToPauseNow()) {
+    if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
@@ -1304,46 +1040,46 @@
   return FXCODEC_STATUS_DECODE_FINISH;
 }
 
-FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template3_unopt(
-    CJBig2_Image* pImage,
-    CJBig2_ArithDecoder* pArithDecoder,
-    JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt(
+    ProgressiveArithDecodeState* pState) {
+  CJBig2_Image* pImage = pState->pImage->get();
+  JBig2ArithCtx* gbContext = pState->gbContext.Get();
+  CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get();
   for (; m_loopIndex < GBH; m_loopIndex++) {
     if (TPGDON) {
       if (pArithDecoder->IsComplete())
         return FXCODEC_STATUS_ERROR;
 
-      m_LTP = m_LTP ^ pArithDecoder->DECODE(&gbContext[0x0195]);
+      m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0195]);
     }
     if (m_LTP) {
-      pImage->copyLine(m_loopIndex, m_loopIndex - 1);
+      pImage->CopyLine(m_loopIndex, m_loopIndex - 1);
     } else {
-      uint32_t line1 = pImage->getPixel(1, m_loopIndex - 1);
-      line1 |= pImage->getPixel(0, m_loopIndex - 1) << 1;
+      uint32_t line1 = pImage->GetPixel(1, m_loopIndex - 1);
+      line1 |= pImage->GetPixel(0, m_loopIndex - 1) << 1;
       uint32_t line2 = 0;
       for (uint32_t w = 0; w < GBW; w++) {
         int bVal;
-        if (USESKIP && SKIP->getPixel(w, m_loopIndex)) {
+        if (USESKIP && SKIP->GetPixel(w, m_loopIndex)) {
           bVal = 0;
         } else {
           uint32_t CONTEXT = line2;
-          CONTEXT |= pImage->getPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4;
+          CONTEXT |= pImage->GetPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4;
           CONTEXT |= line1 << 5;
           if (pArithDecoder->IsComplete())
             return FXCODEC_STATUS_ERROR;
 
-          bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]);
+          bVal = pArithDecoder->Decode(&gbContext[CONTEXT]);
         }
         if (bVal) {
-          pImage->setPixel(w, m_loopIndex, bVal);
+          pImage->SetPixel(w, m_loopIndex, bVal);
         }
         line1 =
-            ((line1 << 1) | pImage->getPixel(w + 2, m_loopIndex - 1)) & 0x1f;
+            ((line1 << 1) | pImage->GetPixel(w + 2, m_loopIndex - 1)) & 0x1f;
         line2 = ((line2 << 1) | bVal) & 0x0f;
       }
     }
-    if (pPause && pPause->NeedToPauseNow()) {
+    if (pState->pPause && pState->pPause->NeedToPauseNow()) {
       m_loopIndex++;
       m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE;
       return FXCODEC_STATUS_DECODE_TOBECONTINUE;
diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.h b/core/fxcodec/jbig2/JBig2_GrdProc.h
index 79992ad..af6f223 100644
--- a/core/fxcodec/jbig2/JBig2_GrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_GrdProc.h
@@ -17,34 +17,41 @@
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
 class CJBig2_Image;
-class IFX_PauseIndicator;
-struct JBig2ArithCtx;
+class JBig2ArithCtx;
+class PauseIndicatorIface;
 
 class CJBig2_GRDProc {
  public:
+  class ProgressiveArithDecodeState {
+   public:
+    ProgressiveArithDecodeState();
+    ~ProgressiveArithDecodeState();
+
+    std::unique_ptr<CJBig2_Image>* pImage;
+    UnownedPtr<CJBig2_ArithDecoder> pArithDecoder;
+    UnownedPtr<JBig2ArithCtx> gbContext;
+    UnownedPtr<PauseIndicatorIface> pPause;
+  };
+
   CJBig2_GRDProc();
   ~CJBig2_GRDProc();
 
-  std::unique_ptr<CJBig2_Image> decode_Arith(CJBig2_ArithDecoder* pArithDecoder,
-                                             JBig2ArithCtx* gbContext);
+  std::unique_ptr<CJBig2_Image> DecodeArith(CJBig2_ArithDecoder* pArithDecoder,
+                                            JBig2ArithCtx* gbContext);
 
-  FXCODEC_STATUS Start_decode_Arith(std::unique_ptr<CJBig2_Image>* pImage,
-                                    CJBig2_ArithDecoder* pArithDecoder,
-                                    JBig2ArithCtx* gbContext,
-                                    IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS Start_decode_MMR(std::unique_ptr<CJBig2_Image>* pImage,
-                                  CJBig2_BitStream* pStream);
-  FXCODEC_STATUS Continue_decode(IFX_PauseIndicator* pPause,
-                                 CJBig2_ArithDecoder* pArithDecoder);
-  FX_RECT GetReplaceRect() const { return m_ReplaceRect; }
+  FXCODEC_STATUS StartDecodeArith(ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS StartDecodeMMR(std::unique_ptr<CJBig2_Image>* pImage,
+                                CJBig2_BitStream* pStream);
+  FXCODEC_STATUS ContinueDecode(ProgressiveArithDecodeState* pState);
+  const FX_RECT& GetReplaceRect() const { return m_ReplaceRect; }
 
   bool MMR;
-  uint32_t GBW;
-  uint32_t GBH;
-  uint8_t GBTEMPLATE;
   bool TPGDON;
   bool USESKIP;
-  CJBig2_Image* SKIP;
+  uint8_t GBTEMPLATE;
+  uint32_t GBW;
+  uint32_t GBH;
+  UnownedPtr<CJBig2_Image> SKIP;
   int8_t GBAT[8];
 
  private:
@@ -52,83 +59,44 @@
   bool UseTemplate1Opt3() const;
   bool UseTemplate23Opt3() const;
 
-  FXCODEC_STATUS decode_Arith(IFX_PauseIndicator* pPause,
-                              CJBig2_ArithDecoder* pArithDecoder);
-  FXCODEC_STATUS decode_Arith_Template0_opt3(CJBig2_Image* pImage,
-                                             CJBig2_ArithDecoder* pArithDecoder,
-                                             JBig2ArithCtx* gbContext,
-                                             IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS decode_Arith_Template0_unopt(
-      CJBig2_Image* pImage,
+  FXCODEC_STATUS ProgressiveDecodeArith(ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate0Opt3(
+      ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate0Unopt(
+      ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate1Opt3(
+      ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate1Unopt(
+      ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate2Opt3(
+      ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate2Unopt(
+      ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate3Opt3(
+      ProgressiveArithDecodeState* pState);
+  FXCODEC_STATUS ProgressiveDecodeArithTemplate3Unopt(
+      ProgressiveArithDecodeState* pState);
+
+  std::unique_ptr<CJBig2_Image> DecodeArithOpt3(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* gbContext,
-      IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS decode_Arith_Template1_opt3(CJBig2_Image* pImage,
-                                             CJBig2_ArithDecoder* pArithDecoder,
-                                             JBig2ArithCtx* gbContext,
-                                             IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS decode_Arith_Template1_unopt(
-      CJBig2_Image* pImage,
+      int OPT);
+  std::unique_ptr<CJBig2_Image> DecodeArithTemplateUnopt(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* gbContext,
-      IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS decode_Arith_Template2_opt3(CJBig2_Image* pImage,
-                                             CJBig2_ArithDecoder* pArithDecoder,
-                                             JBig2ArithCtx* gbContext,
-                                             IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS decode_Arith_Template2_unopt(
-      CJBig2_Image* pImage,
+      int UNOPT);
+  std::unique_ptr<CJBig2_Image> DecodeArithTemplate3Opt3(
       CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext,
-      IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS decode_Arith_Template3_opt3(CJBig2_Image* pImage,
-                                             CJBig2_ArithDecoder* pArithDecoder,
-                                             JBig2ArithCtx* gbContext,
-                                             IFX_PauseIndicator* pPause);
-  FXCODEC_STATUS decode_Arith_Template3_unopt(
-      CJBig2_Image* pImage,
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext,
-      IFX_PauseIndicator* pPause);
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template0_opt3(
+      JBig2ArithCtx* gbContext);
+  std::unique_ptr<CJBig2_Image> DecodeArithTemplate3Unopt(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* gbContext);
 
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template0_unopt(
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext);
-
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template1_opt3(
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext);
-
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template1_unopt(
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext);
-
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template2_opt3(
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext);
-
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template2_unopt(
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext);
-
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template3_opt3(
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext);
-
-  std::unique_ptr<CJBig2_Image> decode_Arith_Template3_unopt(
-      CJBig2_ArithDecoder* pArithDecoder,
-      JBig2ArithCtx* gbContext);
-
-  uint32_t m_loopIndex;
-  uint8_t* m_pLine;
+  uint32_t m_loopIndex = 0;
+  uint8_t* m_pLine = nullptr;
   FXCODEC_STATUS m_ProssiveStatus;
-  CJBig2_Image* m_pImage;
-  JBig2ArithCtx* m_gbContext;
-  uint16_t m_DecodeType;
-  int m_LTP;
+  uint16_t m_DecodeType = 0;
+  int m_LTP = 0;
   FX_RECT m_ReplaceRect;
 };
 
diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp
index f5f569c..e673776 100644
--- a/core/fxcodec/jbig2/JBig2_GrrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp
@@ -13,94 +13,99 @@
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "third_party/base/ptr_util.h"
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::decode(
+CJBig2_GRRDProc::CJBig2_GRRDProc() = default;
+
+CJBig2_GRRDProc::~CJBig2_GRRDProc() = default;
+
+std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::Decode(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
-  if (GRW == 0 || GRW > JBIG2_MAX_IMAGE_SIZE || GRH == 0 ||
-      GRH > JBIG2_MAX_IMAGE_SIZE) {
+  if (!CJBig2_Image::IsValidImageSize(GRW, GRH))
     return pdfium::MakeUnique<CJBig2_Image>(GRW, GRH);
-  }
 
   if (!GRTEMPLATE) {
     if ((GRAT[0] == -1) && (GRAT[1] == -1) && (GRAT[2] == -1) &&
         (GRAT[3] == -1) && (GRREFERENCEDX == 0) &&
         (GRW == (uint32_t)GRREFERENCE->width())) {
-      return decode_Template0_opt(pArithDecoder, grContext);
+      return DecodeTemplate0Opt(pArithDecoder, grContext);
     }
-    return decode_Template0_unopt(pArithDecoder, grContext);
+    return DecodeTemplate0Unopt(pArithDecoder, grContext);
   }
 
   if ((GRREFERENCEDX == 0) && (GRW == (uint32_t)GRREFERENCE->width()))
-    return decode_Template1_opt(pArithDecoder, grContext);
+    return DecodeTemplate1Opt(pArithDecoder, grContext);
 
-  return decode_Template1_unopt(pArithDecoder, grContext);
+  return DecodeTemplate1Unopt(pArithDecoder, grContext);
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::decode_Template0_unopt(
+std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate0Unopt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
-  int LTP = 0;
   auto GRREG = pdfium::MakeUnique<CJBig2_Image>(GRW, GRH);
-  GRREG->fill(0);
+  if (!GRREG->data())
+    return nullptr;
+
+  GRREG->Fill(0);
+  int LTP = 0;
   for (uint32_t h = 0; h < GRH; h++) {
     if (TPGRON) {
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&grContext[0x0010]);
+      LTP = LTP ^ pArithDecoder->Decode(&grContext[0x0010]);
     }
     uint32_t lines[5];
-    lines[0] = GRREG->getPixel(1, h - 1);
-    lines[0] |= GRREG->getPixel(0, h - 1) << 1;
+    lines[0] = GRREG->GetPixel(1, h - 1);
+    lines[0] |= GRREG->GetPixel(0, h - 1) << 1;
     lines[1] = 0;
-    lines[2] = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY - 1);
-    lines[2] |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1)
+    lines[2] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY - 1);
+    lines[2] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1)
                 << 1;
-    lines[3] = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY);
-    lines[3] |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1;
-    lines[3] |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY)
+    lines[3] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY);
+    lines[3] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1;
+    lines[3] |= GRREFERENCE->GetPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY)
                 << 2;
-    lines[4] = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1);
-    lines[4] |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1)
+    lines[4] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1);
+    lines[4] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1)
                 << 1;
-    lines[4] |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY + 1)
+    lines[4] |= GRREFERENCE->GetPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY + 1)
                 << 2;
     if (!LTP) {
       for (uint32_t w = 0; w < GRW; w++) {
         uint32_t CONTEXT =
-            decode_Template0_unopt_CalculateContext(*GRREG, lines, w, h);
+            DecodeTemplate0UnoptCalculateContext(*GRREG, lines, w, h);
         if (pArithDecoder->IsComplete())
           return nullptr;
 
-        int bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
-        decode_Template0_unopt_SetPixel(GRREG.get(), lines, w, h, bVal);
+        int bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
+        DecodeTemplate0UnoptSetPixel(GRREG.get(), lines, w, h, bVal);
       }
     } else {
       for (uint32_t w = 0; w < GRW; w++) {
-        int bVal = GRREFERENCE->getPixel(w, h);
-        if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w - 1, h - 1)) &&
-              (bVal == GRREFERENCE->getPixel(w, h - 1)) &&
-              (bVal == GRREFERENCE->getPixel(w + 1, h - 1)) &&
-              (bVal == GRREFERENCE->getPixel(w - 1, h)) &&
-              (bVal == GRREFERENCE->getPixel(w + 1, h)) &&
-              (bVal == GRREFERENCE->getPixel(w - 1, h + 1)) &&
-              (bVal == GRREFERENCE->getPixel(w, h + 1)) &&
-              (bVal == GRREFERENCE->getPixel(w + 1, h + 1)))) {
+        int bVal = GRREFERENCE->GetPixel(w, h);
+        if (!(TPGRON && (bVal == GRREFERENCE->GetPixel(w - 1, h - 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w, h - 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w + 1, h - 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w - 1, h)) &&
+              (bVal == GRREFERENCE->GetPixel(w + 1, h)) &&
+              (bVal == GRREFERENCE->GetPixel(w - 1, h + 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w, h + 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w + 1, h + 1)))) {
           uint32_t CONTEXT =
-              decode_Template0_unopt_CalculateContext(*GRREG, lines, w, h);
+              DecodeTemplate0UnoptCalculateContext(*GRREG, lines, w, h);
           if (pArithDecoder->IsComplete())
             return nullptr;
 
-          bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
+          bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
         }
-        decode_Template0_unopt_SetPixel(GRREG.get(), lines, w, h, bVal);
+        DecodeTemplate0UnoptSetPixel(GRREG.get(), lines, w, h, bVal);
       }
     }
   }
   return GRREG;
 }
 
-uint32_t CJBig2_GRRDProc::decode_Template0_unopt_CalculateContext(
+uint32_t CJBig2_GRRDProc::DecodeTemplate0UnoptCalculateContext(
     const CJBig2_Image& GRREG,
     const uint32_t* lines,
     uint32_t w,
@@ -108,35 +113,35 @@
   uint32_t CONTEXT = lines[4];
   CONTEXT |= lines[3] << 3;
   CONTEXT |= lines[2] << 6;
-  CONTEXT |= GRREFERENCE->getPixel(w - GRREFERENCEDX + GRAT[2],
+  CONTEXT |= GRREFERENCE->GetPixel(w - GRREFERENCEDX + GRAT[2],
                                    h - GRREFERENCEDY + GRAT[3])
              << 8;
   CONTEXT |= lines[1] << 9;
   CONTEXT |= lines[0] << 10;
-  CONTEXT |= GRREG.getPixel(w + GRAT[0], h + GRAT[1]) << 12;
+  CONTEXT |= GRREG.GetPixel(w + GRAT[0], h + GRAT[1]) << 12;
   return CONTEXT;
 }
 
-void CJBig2_GRRDProc::decode_Template0_unopt_SetPixel(CJBig2_Image* GRREG,
-                                                      uint32_t* lines,
-                                                      uint32_t w,
-                                                      uint32_t h,
-                                                      int bVal) {
-  GRREG->setPixel(w, h, bVal);
-  lines[0] = ((lines[0] << 1) | GRREG->getPixel(w + 2, h - 1)) & 0x03;
+void CJBig2_GRRDProc::DecodeTemplate0UnoptSetPixel(CJBig2_Image* GRREG,
+                                                   uint32_t* lines,
+                                                   uint32_t w,
+                                                   uint32_t h,
+                                                   int bVal) {
+  GRREG->SetPixel(w, h, bVal);
+  lines[0] = ((lines[0] << 1) | GRREG->GetPixel(w + 2, h - 1)) & 0x03;
   lines[1] = ((lines[1] << 1) | bVal) & 0x01;
-  lines[2] = ((lines[2] << 1) | GRREFERENCE->getPixel(w - GRREFERENCEDX + 2,
+  lines[2] = ((lines[2] << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 2,
                                                       h - GRREFERENCEDY - 1)) &
              0x03;
   lines[3] = ((lines[3] << 1) |
-              GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) &
+              GRREFERENCE->GetPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) &
              0x07;
-  lines[4] = ((lines[4] << 1) | GRREFERENCE->getPixel(w - GRREFERENCEDX + 2,
+  lines[4] = ((lines[4] << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 2,
                                                       h - GRREFERENCEDY + 1)) &
              0x07;
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::decode_Template0_opt(
+std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate0Opt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
   if (!GRREFERENCE->data())
@@ -163,7 +168,7 @@
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&grContext[0x0010]);
+      LTP = LTP ^ pArithDecoder->Decode(&grContext[0x0010]);
     }
     uint32_t line1 = (h > 0) ? pLine[-nStride] << 4 : 0;
     int32_t reference_h = h - GRREFERENCEDY;
@@ -206,10 +211,7 @@
         }
         uint8_t cVal = 0;
         for (int32_t k = 0; k < nBits; k++) {
-          if (pArithDecoder->IsComplete())
-            return nullptr;
-
-          int bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
           cVal |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x0cdb) << 1) | (bVal << 9) |
                     ((line1 >> (7 - k)) & 0x0400) |
@@ -246,19 +248,19 @@
         }
         uint8_t cVal = 0;
         for (int32_t k = 0; k < nBits; k++) {
-          int bVal = GRREFERENCE->getPixel(w + k, h);
-          if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w + k - 1, h - 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k, h - 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k + 1, h - 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k - 1, h)) &&
-                (bVal == GRREFERENCE->getPixel(w + k + 1, h)) &&
-                (bVal == GRREFERENCE->getPixel(w + k - 1, h + 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k, h + 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k + 1, h + 1)))) {
+          int bVal = GRREFERENCE->GetPixel(w + k, h);
+          if (!(TPGRON && (bVal == GRREFERENCE->GetPixel(w + k - 1, h - 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k, h - 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k + 1, h - 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k - 1, h)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k + 1, h)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k - 1, h + 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k, h + 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k + 1, h + 1)))) {
             if (pArithDecoder->IsComplete())
               return nullptr;
 
-            bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
+            bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
           }
           cVal |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x0cdb) << 1) | (bVal << 9) |
@@ -277,34 +279,37 @@
   return GRREG;
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::decode_Template1_unopt(
+std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate1Unopt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
-  int LTP = 0;
   auto GRREG = pdfium::MakeUnique<CJBig2_Image>(GRW, GRH);
-  GRREG->fill(0);
+  if (!GRREG->data())
+    return nullptr;
+
+  GRREG->Fill(0);
+  int LTP = 0;
   for (uint32_t h = 0; h < GRH; h++) {
     if (TPGRON) {
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&grContext[0x0008]);
+      LTP = LTP ^ pArithDecoder->Decode(&grContext[0x0008]);
     }
     if (!LTP) {
-      uint32_t line1 = GRREG->getPixel(1, h - 1);
-      line1 |= GRREG->getPixel(0, h - 1) << 1;
-      line1 |= GRREG->getPixel(-1, h - 1) << 2;
+      uint32_t line1 = GRREG->GetPixel(1, h - 1);
+      line1 |= GRREG->GetPixel(0, h - 1) << 1;
+      line1 |= GRREG->GetPixel(-1, h - 1) << 2;
       uint32_t line2 = 0;
       uint32_t line3 =
-          GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1);
+          GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1);
       uint32_t line4 =
-          GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY);
-      line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1;
-      line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY)
+          GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY);
+      line4 |= GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1;
+      line4 |= GRREFERENCE->GetPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY)
                << 2;
       uint32_t line5 =
-          GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1);
-      line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1)
+          GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1);
+      line5 |= GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1)
                << 1;
       for (uint32_t w = 0; w < GRW; w++) {
         uint32_t CONTEXT = line5;
@@ -315,49 +320,46 @@
         if (pArithDecoder->IsComplete())
           return nullptr;
 
-        int bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
-        GRREG->setPixel(w, h, bVal);
-        line1 = ((line1 << 1) | GRREG->getPixel(w + 2, h - 1)) & 0x07;
+        int bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
+        GRREG->SetPixel(w, h, bVal);
+        line1 = ((line1 << 1) | GRREG->GetPixel(w + 2, h - 1)) & 0x07;
         line2 = ((line2 << 1) | bVal) & 0x01;
-        line3 = ((line3 << 1) |
-                 GRREFERENCE->getPixel(w - GRREFERENCEDX + 1,
-                                       h - GRREFERENCEDY - 1)) &
+        line3 = ((line3 << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 1,
+                                                      h - GRREFERENCEDY - 1)) &
                 0x01;
-        line4 =
-            ((line4 << 1) |
-             GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) &
-            0x07;
-        line5 = ((line5 << 1) |
-                 GRREFERENCE->getPixel(w - GRREFERENCEDX + 2,
-                                       h - GRREFERENCEDY + 1)) &
+        line4 = ((line4 << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 2,
+                                                      h - GRREFERENCEDY)) &
+                0x07;
+        line5 = ((line5 << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 2,
+                                                      h - GRREFERENCEDY + 1)) &
                 0x03;
       }
     } else {
-      uint32_t line1 = GRREG->getPixel(1, h - 1);
-      line1 |= GRREG->getPixel(0, h - 1) << 1;
-      line1 |= GRREG->getPixel(-1, h - 1) << 2;
+      uint32_t line1 = GRREG->GetPixel(1, h - 1);
+      line1 |= GRREG->GetPixel(0, h - 1) << 1;
+      line1 |= GRREG->GetPixel(-1, h - 1) << 2;
       uint32_t line2 = 0;
       uint32_t line3 =
-          GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1);
+          GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1);
       uint32_t line4 =
-          GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY);
-      line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1;
-      line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY)
+          GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY);
+      line4 |= GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1;
+      line4 |= GRREFERENCE->GetPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY)
                << 2;
       uint32_t line5 =
-          GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1);
-      line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1)
+          GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1);
+      line5 |= GRREFERENCE->GetPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1)
                << 1;
       for (uint32_t w = 0; w < GRW; w++) {
-        int bVal = GRREFERENCE->getPixel(w, h);
-        if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w - 1, h - 1)) &&
-              (bVal == GRREFERENCE->getPixel(w, h - 1)) &&
-              (bVal == GRREFERENCE->getPixel(w + 1, h - 1)) &&
-              (bVal == GRREFERENCE->getPixel(w - 1, h)) &&
-              (bVal == GRREFERENCE->getPixel(w + 1, h)) &&
-              (bVal == GRREFERENCE->getPixel(w - 1, h + 1)) &&
-              (bVal == GRREFERENCE->getPixel(w, h + 1)) &&
-              (bVal == GRREFERENCE->getPixel(w + 1, h + 1)))) {
+        int bVal = GRREFERENCE->GetPixel(w, h);
+        if (!(TPGRON && (bVal == GRREFERENCE->GetPixel(w - 1, h - 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w, h - 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w + 1, h - 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w - 1, h)) &&
+              (bVal == GRREFERENCE->GetPixel(w + 1, h)) &&
+              (bVal == GRREFERENCE->GetPixel(w - 1, h + 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w, h + 1)) &&
+              (bVal == GRREFERENCE->GetPixel(w + 1, h + 1)))) {
           uint32_t CONTEXT = line5;
           CONTEXT |= line4 << 2;
           CONTEXT |= line3 << 5;
@@ -366,22 +368,19 @@
           if (pArithDecoder->IsComplete())
             return nullptr;
 
-          bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
+          bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
         }
-        GRREG->setPixel(w, h, bVal);
-        line1 = ((line1 << 1) | GRREG->getPixel(w + 2, h - 1)) & 0x07;
+        GRREG->SetPixel(w, h, bVal);
+        line1 = ((line1 << 1) | GRREG->GetPixel(w + 2, h - 1)) & 0x07;
         line2 = ((line2 << 1) | bVal) & 0x01;
-        line3 = ((line3 << 1) |
-                 GRREFERENCE->getPixel(w - GRREFERENCEDX + 1,
-                                       h - GRREFERENCEDY - 1)) &
+        line3 = ((line3 << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 1,
+                                                      h - GRREFERENCEDY - 1)) &
                 0x01;
-        line4 =
-            ((line4 << 1) |
-             GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) &
-            0x07;
-        line5 = ((line5 << 1) |
-                 GRREFERENCE->getPixel(w - GRREFERENCEDX + 2,
-                                       h - GRREFERENCEDY + 1)) &
+        line4 = ((line4 << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 2,
+                                                      h - GRREFERENCEDY)) &
+                0x07;
+        line5 = ((line5 << 1) | GRREFERENCE->GetPixel(w - GRREFERENCEDX + 2,
+                                                      h - GRREFERENCEDY + 1)) &
                 0x03;
       }
     }
@@ -389,7 +388,7 @@
   return GRREG;
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::decode_Template1_opt(
+std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate1Opt(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext) {
   if (!GRREFERENCE->data())
@@ -417,7 +416,7 @@
       if (pArithDecoder->IsComplete())
         return nullptr;
 
-      LTP = LTP ^ pArithDecoder->DECODE(&grContext[0x0008]);
+      LTP = LTP ^ pArithDecoder->Decode(&grContext[0x0008]);
     }
     uint32_t line1 = (h > 0) ? pLine[-nStride] << 1 : 0;
     int32_t reference_h = h - GRREFERENCEDY;
@@ -451,7 +450,7 @@
         }
         uint8_t cVal = 0;
         for (int32_t k = 0; k < nBits; k++) {
-          int bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
+          int bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
           cVal |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x018d) << 1) | (bVal << 6) |
                     ((line1 >> (7 - k)) & 0x0080) |
@@ -485,19 +484,19 @@
         }
         uint8_t cVal = 0;
         for (int32_t k = 0; k < nBits; k++) {
-          int bVal = GRREFERENCE->getPixel(w + k, h);
-          if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w + k - 1, h - 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k, h - 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k + 1, h - 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k - 1, h)) &&
-                (bVal == GRREFERENCE->getPixel(w + k + 1, h)) &&
-                (bVal == GRREFERENCE->getPixel(w + k - 1, h + 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k, h + 1)) &&
-                (bVal == GRREFERENCE->getPixel(w + k + 1, h + 1)))) {
+          int bVal = GRREFERENCE->GetPixel(w + k, h);
+          if (!(TPGRON && (bVal == GRREFERENCE->GetPixel(w + k - 1, h - 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k, h - 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k + 1, h - 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k - 1, h)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k + 1, h)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k - 1, h + 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k, h + 1)) &&
+                (bVal == GRREFERENCE->GetPixel(w + k + 1, h + 1)))) {
             if (pArithDecoder->IsComplete())
               return nullptr;
 
-            bVal = pArithDecoder->DECODE(&grContext[CONTEXT]);
+            bVal = pArithDecoder->Decode(&grContext[CONTEXT]);
           }
           cVal |= bVal << (7 - k);
           CONTEXT = ((CONTEXT & 0x018d) << 1) | (bVal << 6) |
diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.h b/core/fxcodec/jbig2/JBig2_GrrdProc.h
index a9a1439..c7e3bf9 100644
--- a/core/fxcodec/jbig2/JBig2_GrrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_GrrdProc.h
@@ -10,48 +10,52 @@
 #include <memory>
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_Image;
-struct JBig2ArithCtx;
+class JBig2ArithCtx;
 
 class CJBig2_GRRDProc {
  public:
-  std::unique_ptr<CJBig2_Image> decode(CJBig2_ArithDecoder* pArithDecoder,
+  CJBig2_GRRDProc();
+  ~CJBig2_GRRDProc();
+
+  std::unique_ptr<CJBig2_Image> Decode(CJBig2_ArithDecoder* pArithDecoder,
                                        JBig2ArithCtx* grContext);
 
+  bool GRTEMPLATE;
+  bool TPGRON;
   uint32_t GRW;
   uint32_t GRH;
-  bool GRTEMPLATE;
-  CJBig2_Image* GRREFERENCE;
   int32_t GRREFERENCEDX;
   int32_t GRREFERENCEDY;
-  bool TPGRON;
+  UnownedPtr<CJBig2_Image> GRREFERENCE;
   int8_t GRAT[4];
 
  private:
-  std::unique_ptr<CJBig2_Image> decode_Template0_unopt(
+  std::unique_ptr<CJBig2_Image> DecodeTemplate0Unopt(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* grContext);
-  uint32_t decode_Template0_unopt_CalculateContext(const CJBig2_Image& GRREG,
-                                                   const uint32_t* lines,
-                                                   uint32_t w,
-                                                   uint32_t h) const;
-  void decode_Template0_unopt_SetPixel(CJBig2_Image* GRREG,
-                                       uint32_t* lines,
-                                       uint32_t w,
-                                       uint32_t h,
-                                       int bVal);
+  uint32_t DecodeTemplate0UnoptCalculateContext(const CJBig2_Image& GRREG,
+                                                const uint32_t* lines,
+                                                uint32_t w,
+                                                uint32_t h) const;
+  void DecodeTemplate0UnoptSetPixel(CJBig2_Image* GRREG,
+                                    uint32_t* lines,
+                                    uint32_t w,
+                                    uint32_t h,
+                                    int bVal);
 
-  std::unique_ptr<CJBig2_Image> decode_Template0_opt(
+  std::unique_ptr<CJBig2_Image> DecodeTemplate0Opt(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* grContext);
 
-  std::unique_ptr<CJBig2_Image> decode_Template1_unopt(
+  std::unique_ptr<CJBig2_Image> DecodeTemplate1Unopt(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* grContext);
 
-  std::unique_ptr<CJBig2_Image> decode_Template1_opt(
+  std::unique_ptr<CJBig2_Image> DecodeTemplate1Opt(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* grContext);
 };
diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
index bd1d92f..59fbd89 100644
--- a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
@@ -14,10 +14,10 @@
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "third_party/base/ptr_util.h"
 
-std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::decode_Arith(
+std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
+    PauseIndicatorIface* pPause) {
   std::unique_ptr<CJBig2_Image> HSKIP;
   if (HENABLESKIP == 1) {
     HSKIP = pdfium::MakeUnique<CJBig2_Image>(HGW, HGH);
@@ -27,9 +27,9 @@
         int32_t y = (HGY + mg * HRX - ng * HRY) >> 8;
         if ((x + HPW <= 0) | (x >= static_cast<int32_t>(HBW)) | (y + HPH <= 0) |
             (y >= static_cast<int32_t>(HPH))) {
-          HSKIP->setPixel(ng, mg, 1);
+          HSKIP->SetPixel(ng, mg, 1);
         } else {
-          HSKIP->setPixel(ng, mg, 0);
+          HSKIP->SetPixel(ng, mg, 0);
         }
       }
     }
@@ -64,22 +64,26 @@
   std::vector<std::unique_ptr<CJBig2_Image>> GSPLANES(GSBPP);
   for (int32_t i = GSBPP - 1; i >= 0; --i) {
     std::unique_ptr<CJBig2_Image> pImage;
-    FXCODEC_STATUS status =
-        GRD.Start_decode_Arith(&pImage, pArithDecoder, gbContext, nullptr);
+    CJBig2_GRDProc::ProgressiveArithDecodeState state;
+    state.pImage = &pImage;
+    state.pArithDecoder = pArithDecoder;
+    state.gbContext = gbContext;
+    state.pPause = nullptr;
+    FXCODEC_STATUS status = GRD.StartDecodeArith(&state);
+    state.pPause = pPause;
     while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-      status = GRD.Continue_decode(pPause, pArithDecoder);
-
+      status = GRD.ContinueDecode(&state);
     if (!pImage)
       return nullptr;
 
     GSPLANES[i] = std::move(pImage);
     if (i < GSBPP - 1)
-      GSPLANES[i]->composeFrom(0, 0, GSPLANES[i + 1].get(), JBIG2_COMPOSE_XOR);
+      GSPLANES[i]->ComposeFrom(0, 0, GSPLANES[i + 1].get(), JBIG2_COMPOSE_XOR);
   }
-  return decode_image(GSPLANES);
+  return DecodeImage(GSPLANES);
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::decode_MMR(
+std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::DecodeMMR(
     CJBig2_BitStream* pStream) {
   uint32_t HBPP = 1;
   while (static_cast<uint32_t>(1 << HBPP) < HNUMPATS)
@@ -92,41 +96,40 @@
 
   uint8_t GSBPP = static_cast<uint8_t>(HBPP);
   std::vector<std::unique_ptr<CJBig2_Image>> GSPLANES(GSBPP);
-  GRD.Start_decode_MMR(&GSPLANES[GSBPP - 1], pStream);
+  GRD.StartDecodeMMR(&GSPLANES[GSBPP - 1], pStream);
   if (!GSPLANES[GSBPP - 1])
     return nullptr;
 
   pStream->alignByte();
   pStream->offset(3);
   for (int32_t J = GSBPP - 2; J >= 0; --J) {
-    GRD.Start_decode_MMR(&GSPLANES[J], pStream);
+    GRD.StartDecodeMMR(&GSPLANES[J], pStream);
     if (!GSPLANES[J])
       return nullptr;
 
     pStream->alignByte();
     pStream->offset(3);
-    GSPLANES[J]->composeFrom(0, 0, GSPLANES[J + 1].get(), JBIG2_COMPOSE_XOR);
+    GSPLANES[J]->ComposeFrom(0, 0, GSPLANES[J + 1].get(), JBIG2_COMPOSE_XOR);
   }
-  return decode_image(GSPLANES);
+  return DecodeImage(GSPLANES);
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::decode_image(
+std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::DecodeImage(
     const std::vector<std::unique_ptr<CJBig2_Image>>& GSPLANES) {
   auto HTREG = pdfium::MakeUnique<CJBig2_Image>(HBW, HBH);
-  HTREG->fill(HDEFPIXEL);
-  std::vector<uint32_t> GSVALS(HGW * HGH);
+  if (!HTREG->data())
+    return nullptr;
+
+  HTREG->Fill(HDEFPIXEL);
   for (uint32_t y = 0; y < HGH; ++y) {
     for (uint32_t x = 0; x < HGW; ++x) {
-      for (uint8_t J = 0; J < GSPLANES.size(); ++J)
-        GSVALS[y * HGW + x] |= GSPLANES[J]->getPixel(x, y) << J;
-    }
-  }
-  for (uint32_t mg = 0; mg < HGH; ++mg) {
-    for (uint32_t ng = 0; ng < HGW; ++ng) {
-      int32_t x = (HGX + mg * HRY + ng * HRX) >> 8;
-      int32_t y = (HGY + mg * HRX - ng * HRY) >> 8;
-      uint32_t pat_index = std::min(GSVALS[mg * HGW + ng], HNUMPATS - 1);
-      HTREG->composeFrom(x, y, (*HPATS)[pat_index].get(), HCOMBOP);
+      uint32_t gsval = 0;
+      for (uint8_t i = 0; i < GSPLANES.size(); ++i)
+        gsval |= GSPLANES[i]->GetPixel(x, y) << i;
+      uint32_t pat_index = std::min(gsval, HNUMPATS - 1);
+      int32_t out_x = (HGX + y * HRY + x * HRX) >> 8;
+      int32_t out_y = (HGY + y * HRX - x * HRY) >> 8;
+      (*HPATS)[pat_index]->ComposeTo(HTREG.get(), out_x, out_y, HCOMBOP);
     }
   }
   return HTREG;
diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.h b/core/fxcodec/jbig2/JBig2_HtrdProc.h
index ed7cdbf..cd90aae 100644
--- a/core/fxcodec/jbig2/JBig2_HtrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_HtrdProc.h
@@ -15,16 +15,16 @@
 
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
-class IFX_PauseIndicator;
-struct JBig2ArithCtx;
+class JBig2ArithCtx;
+class PauseIndicatorIface;
 
 class CJBig2_HTRDProc {
  public:
-  std::unique_ptr<CJBig2_Image> decode_Arith(CJBig2_ArithDecoder* pArithDecoder,
-                                             JBig2ArithCtx* gbContext,
-                                             IFX_PauseIndicator* pPause);
+  std::unique_ptr<CJBig2_Image> DecodeArith(CJBig2_ArithDecoder* pArithDecoder,
+                                            JBig2ArithCtx* gbContext,
+                                            PauseIndicatorIface* pPause);
 
-  std::unique_ptr<CJBig2_Image> decode_MMR(CJBig2_BitStream* pStream);
+  std::unique_ptr<CJBig2_Image> DecodeMMR(CJBig2_BitStream* pStream);
 
  public:
   uint32_t HBW;
@@ -46,7 +46,7 @@
   uint8_t HPH;
 
  private:
-  std::unique_ptr<CJBig2_Image> decode_image(
+  std::unique_ptr<CJBig2_Image> DecodeImage(
       const std::vector<std::unique_ptr<CJBig2_Image>>& GSPLANES);
 };
 
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp
index 050b5e6..bea2b09 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp
+++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp
@@ -7,38 +7,46 @@
 #include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h"
 
 #include "core/fxcodec/jbig2/JBig2_Define.h"
+#include "core/fxcrt/fx_safe_types.h"
 
 CJBig2_HuffmanDecoder::CJBig2_HuffmanDecoder(CJBig2_BitStream* pStream)
     : m_pStream(pStream) {}
 
 CJBig2_HuffmanDecoder::~CJBig2_HuffmanDecoder() {}
 
-int CJBig2_HuffmanDecoder::decodeAValue(CJBig2_HuffmanTable* pTable,
+int CJBig2_HuffmanDecoder::DecodeAValue(const CJBig2_HuffmanTable* pTable,
                                         int* nResult) {
-  int nVal = 0;
+  FX_SAFE_INT32 nSafeVal = 0;
   int nBits = 0;
   while (1) {
     uint32_t nTmp;
     if (m_pStream->read1Bit(&nTmp) == -1)
       break;
 
-    nVal = (nVal << 1) | nTmp;
+    nSafeVal <<= 1;
+    if (!nSafeVal.IsValid())
+      break;
+
+    nSafeVal |= nTmp;
     ++nBits;
+    const int32_t nVal = nSafeVal.ValueOrDie();
     for (uint32_t i = 0; i < pTable->Size(); ++i) {
-      if (pTable->GetPREFLEN()[i] == nBits && pTable->GetCODES()[i] == nVal) {
-        if (pTable->IsHTOOB() && i == pTable->Size() - 1)
-          return JBIG2_OOB;
+      const JBig2HuffmanCode& code = pTable->GetCODES()[i];
+      if (code.codelen != nBits || code.code != nVal)
+        continue;
 
-        if (m_pStream->readNBits(pTable->GetRANGELEN()[i], &nTmp) == -1)
-          return -1;
+      if (pTable->IsHTOOB() && i == pTable->Size() - 1)
+        return JBIG2_OOB;
 
-        uint32_t offset = pTable->IsHTOOB() ? 3 : 2;
-        if (i == pTable->Size() - offset)
-          *nResult = pTable->GetRANGELOW()[i] - nTmp;
-        else
-          *nResult = pTable->GetRANGELOW()[i] + nTmp;
-        return 0;
-      }
+      if (m_pStream->readNBits(pTable->GetRANGELEN()[i], &nTmp) == -1)
+        return -1;
+
+      uint32_t offset = pTable->IsHTOOB() ? 3 : 2;
+      if (i == pTable->Size() - offset)
+        *nResult = pTable->GetRANGELOW()[i] - nTmp;
+      else
+        *nResult = pTable->GetRANGELOW()[i] + nTmp;
+      return 0;
     }
   }
   return -1;
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h
index ac53b4d..48f8d0f 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h
+++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h
@@ -14,10 +14,9 @@
 class CJBig2_HuffmanDecoder {
  public:
   explicit CJBig2_HuffmanDecoder(CJBig2_BitStream* pStream);
-
   ~CJBig2_HuffmanDecoder();
 
-  int decodeAValue(CJBig2_HuffmanTable* pTable, int* nResult);
+  int DecodeAValue(const CJBig2_HuffmanTable* pTable, int* nResult);
 
  private:
   UnownedPtr<CJBig2_BitStream> const m_pStream;
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
index 1127f52..392b718 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
+++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
@@ -6,21 +6,137 @@
 
 #include "core/fxcodec/jbig2/JBig2_HuffmanTable.h"
 
-#include <algorithm>
 #include <limits>
 #include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
-#include "core/fxcodec/jbig2/JBig2_Define.h"
-#include "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h"
+#include "core/fxcodec/jbig2/JBig2_Context.h"
 #include "core/fxcrt/fx_memory.h"
 #include "third_party/base/numerics/safe_math.h"
 
-CJBig2_HuffmanTable::CJBig2_HuffmanTable(const JBig2TableLine* pTable,
-                                         uint32_t nLines,
-                                         bool bHTOOB)
-    : m_bOK(true), HTOOB(bHTOOB), NTEMP(nLines) {
-  ParseFromStandardTable(pTable);
+namespace {
+
+struct JBig2TableLine {
+  uint8_t PREFLEN;
+  uint8_t RANDELEN;
+  int32_t RANGELOW;
+};
+
+struct HuffmanTable {
+  bool HTOOB;
+  const JBig2TableLine* lines;
+  size_t size;
+};
+
+constexpr JBig2TableLine kTableLine1[] = {{1, 4, 0},
+                                          {2, 8, 16},
+                                          {3, 16, 272},
+                                          {0, 32, -1},
+                                          {3, 32, 65808}};
+
+constexpr JBig2TableLine kTableLine2[] = {{1, 0, 0},   {2, 0, 1},  {3, 0, 2},
+                                          {4, 3, 3},   {5, 6, 11}, {0, 32, -1},
+                                          {6, 32, 75}, {6, 0, 0}};
+
+constexpr JBig2TableLine kTableLine3[] = {
+    {8, 8, -256}, {1, 0, 0},     {2, 0, 1},   {3, 0, 2}, {4, 3, 3},
+    {5, 6, 11},   {8, 32, -257}, {7, 32, 75}, {6, 0, 0}};
+
+constexpr JBig2TableLine kTableLine4[] = {{1, 0, 1},  {2, 0, 2},  {3, 0, 3},
+                                          {4, 3, 4},  {5, 6, 12}, {0, 32, -1},
+                                          {5, 32, 76}};
+
+constexpr JBig2TableLine kTableLine5[] = {{7, 8, -255},  {1, 0, 1},  {2, 0, 2},
+                                          {3, 0, 3},     {4, 3, 4},  {5, 6, 12},
+                                          {7, 32, -256}, {6, 32, 76}};
+
+constexpr JBig2TableLine kTableLine6[] = {
+    {5, 10, -2048}, {4, 9, -1024}, {4, 8, -512},   {4, 7, -256}, {5, 6, -128},
+    {5, 5, -64},    {4, 5, -32},   {2, 7, 0},      {3, 7, 128},  {3, 8, 256},
+    {4, 9, 512},    {4, 10, 1024}, {6, 32, -2049}, {6, 32, 2048}};
+
+constexpr JBig2TableLine kTableLine7[] = {
+    {4, 9, -1024}, {3, 8, -512}, {4, 7, -256},  {5, 6, -128},   {5, 5, -64},
+    {4, 5, -32},   {4, 5, 0},    {5, 5, 32},    {5, 6, 64},     {4, 7, 128},
+    {3, 8, 256},   {3, 9, 512},  {3, 10, 1024}, {5, 32, -1025}, {5, 32, 2048}};
+
+constexpr JBig2TableLine kTableLine8[] = {
+    {8, 3, -15}, {9, 1, -7},  {8, 1, -5},   {9, 0, -3},   {7, 0, -2},
+    {4, 0, -1},  {2, 1, 0},   {5, 0, 2},    {6, 0, 3},    {3, 4, 4},
+    {6, 1, 20},  {4, 4, 22},  {4, 5, 38},   {5, 6, 70},   {5, 7, 134},
+    {6, 7, 262}, {7, 8, 390}, {6, 10, 646}, {9, 32, -16}, {9, 32, 1670},
+    {2, 0, 0}};
+
+constexpr JBig2TableLine kTableLine9[] = {
+    {8, 4, -31},   {9, 2, -15}, {8, 2, -11}, {9, 1, -7},    {7, 1, -5},
+    {4, 1, -3},    {3, 1, -1},  {3, 1, 1},   {5, 1, 3},     {6, 1, 5},
+    {3, 5, 7},     {6, 2, 39},  {4, 5, 43},  {4, 6, 75},    {5, 7, 139},
+    {5, 8, 267},   {6, 8, 523}, {7, 9, 779}, {6, 11, 1291}, {9, 32, -32},
+    {9, 32, 3339}, {2, 0, 0}};
+
+constexpr JBig2TableLine kTableLine10[] = {
+    {7, 4, -21}, {8, 0, -5},    {7, 0, -4},    {5, 0, -3},   {2, 2, -2},
+    {5, 0, 2},   {6, 0, 3},     {7, 0, 4},     {8, 0, 5},    {2, 6, 6},
+    {5, 5, 70},  {6, 5, 102},   {6, 6, 134},   {6, 7, 198},  {6, 8, 326},
+    {6, 9, 582}, {6, 10, 1094}, {7, 11, 2118}, {8, 32, -22}, {8, 32, 4166},
+    {2, 0, 0}};
+
+constexpr JBig2TableLine kTableLine11[] = {
+    {1, 0, 1},  {2, 1, 2},  {4, 0, 4},  {4, 1, 5},   {5, 1, 7},
+    {5, 2, 9},  {6, 2, 13}, {7, 2, 17}, {7, 3, 21},  {7, 4, 29},
+    {7, 5, 45}, {7, 6, 77}, {0, 32, 0}, {7, 32, 141}};
+
+constexpr JBig2TableLine kTableLine12[] = {
+    {1, 0, 1},  {2, 0, 2},  {3, 1, 3},  {5, 0, 5},  {5, 1, 6},
+    {6, 1, 8},  {7, 0, 10}, {7, 1, 11}, {7, 2, 13}, {7, 3, 17},
+    {7, 4, 25}, {8, 5, 41}, {0, 32, 0}, {8, 32, 73}};
+
+constexpr JBig2TableLine kTableLine13[] = {
+    {1, 0, 1},  {3, 0, 2},  {4, 0, 3},  {5, 0, 4},   {4, 1, 5},
+    {3, 3, 7},  {6, 1, 15}, {6, 2, 17}, {6, 3, 21},  {6, 4, 29},
+    {6, 5, 45}, {7, 6, 77}, {0, 32, 0}, {7, 32, 141}};
+
+constexpr JBig2TableLine kTableLine14[] = {{3, 0, -2}, {3, 0, -1}, {1, 0, 0},
+                                           {3, 0, 1},  {3, 0, 2},  {0, 32, -3},
+                                           {0, 32, 3}};
+
+constexpr JBig2TableLine kTableLine15[] = {
+    {7, 4, -24}, {6, 2, -8},   {5, 1, -4}, {4, 0, -2}, {3, 0, -1},
+    {1, 0, 0},   {3, 0, 1},    {4, 0, 2},  {5, 1, 3},  {6, 2, 5},
+    {7, 4, 9},   {7, 32, -25}, {7, 32, 25}};
+
+constexpr HuffmanTable kHuffmanTables[16] = {
+    {false, nullptr, 0},  // Zero dummy to preserve indexing.
+    {false, kTableLine1, FX_ArraySize(kTableLine1)},
+    {true, kTableLine2, FX_ArraySize(kTableLine2)},
+    {true, kTableLine3, FX_ArraySize(kTableLine3)},
+    {false, kTableLine4, FX_ArraySize(kTableLine4)},
+    {false, kTableLine5, FX_ArraySize(kTableLine5)},
+    {false, kTableLine6, FX_ArraySize(kTableLine6)},
+    {false, kTableLine7, FX_ArraySize(kTableLine7)},
+    {true, kTableLine8, FX_ArraySize(kTableLine8)},
+    {true, kTableLine9, FX_ArraySize(kTableLine9)},
+    {true, kTableLine10, FX_ArraySize(kTableLine10)},
+    {false, kTableLine11, FX_ArraySize(kTableLine11)},
+    {false, kTableLine12, FX_ArraySize(kTableLine12)},
+    {false, kTableLine13, FX_ArraySize(kTableLine13)},
+    {false, kTableLine14, FX_ArraySize(kTableLine14)},
+    {false, kTableLine15, FX_ArraySize(kTableLine15)}};
+
+static_assert(CJBig2_HuffmanTable::kNumHuffmanTables ==
+                  FX_ArraySize(kHuffmanTables),
+              "kNumHuffmanTables must be equal to the size of kHuffmanTables");
+
+}  // namespace
+
+CJBig2_HuffmanTable::CJBig2_HuffmanTable(size_t idx) {
+  ASSERT(idx > 0);
+  ASSERT(idx < kNumHuffmanTables);
+  const HuffmanTable& table = kHuffmanTables[idx];
+  HTOOB = table.HTOOB;
+  NTEMP = table.size;
+  m_bOK = ParseFromStandardTable(idx);
+  ASSERT(m_bOK);
 }
 
 CJBig2_HuffmanTable::CJBig2_HuffmanTable(CJBig2_BitStream* pStream)
@@ -30,16 +146,17 @@
 
 CJBig2_HuffmanTable::~CJBig2_HuffmanTable() {}
 
-void CJBig2_HuffmanTable::ParseFromStandardTable(const JBig2TableLine* pTable) {
-  PREFLEN.resize(NTEMP);
+bool CJBig2_HuffmanTable::ParseFromStandardTable(size_t idx) {
+  const JBig2TableLine* pTable = kHuffmanTables[idx].lines;
+  CODES.resize(NTEMP);
   RANGELEN.resize(NTEMP);
   RANGELOW.resize(NTEMP);
   for (uint32_t i = 0; i < NTEMP; ++i) {
-    PREFLEN[i] = pTable[i].PREFLEN;
+    CODES[i].codelen = pTable[i].PREFLEN;
     RANGELEN[i] = pTable[i].RANDELEN;
     RANGELOW[i] = pTable[i].RANGELOW;
   }
-  InitCodes();
+  return CJBig2_Context::HuffmanAssignCode(CODES.data(), NTEMP);
 }
 
 bool CJBig2_HuffmanTable::ParseFromCodedBuffer(CJBig2_BitStream* pStream) {
@@ -53,9 +170,7 @@
   uint32_t HTLOW;
   uint32_t HTHIGH;
   if (pStream->readInteger(&HTLOW) == -1 ||
-      pStream->readInteger(&HTHIGH) == -1 ||
-      HTLOW > static_cast<uint32_t>(std::numeric_limits<int>::max()) ||
-      HTHIGH > static_cast<uint32_t>(std::numeric_limits<int>::max())) {
+      pStream->readInteger(&HTHIGH) == -1) {
     return false;
   }
 
@@ -67,7 +182,7 @@
   ExtendBuffers(false);
   pdfium::base::CheckedNumeric<int> cur_low = low;
   do {
-    if ((pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) ||
+    if ((pStream->readNBits(HTPS, &CODES[NTEMP].codelen) == -1) ||
         (pStream->readNBits(HTRS, &RANGELEN[NTEMP]) == -1) ||
         (static_cast<size_t>(RANGELEN[NTEMP]) >= 8 * sizeof(cur_low))) {
       return false;
@@ -83,14 +198,17 @@
     ExtendBuffers(true);
   } while (cur_low.ValueOrDie() < high);
 
-  if (pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1)
+  if (pStream->readNBits(HTPS, &CODES[NTEMP].codelen) == -1)
     return false;
 
   RANGELEN[NTEMP] = 32;
+  if (low == std::numeric_limits<int>::min())
+    return false;
+
   RANGELOW[NTEMP] = low - 1;
   ExtendBuffers(true);
 
-  if (pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1)
+  if (pStream->readNBits(HTPS, &CODES[NTEMP].codelen) == -1)
     return false;
 
   RANGELEN[NTEMP] = 32;
@@ -98,57 +216,26 @@
   ExtendBuffers(true);
 
   if (HTOOB) {
-    if (pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1)
+    if (pStream->readNBits(HTPS, &CODES[NTEMP].codelen) == -1)
       return false;
 
     ++NTEMP;
   }
 
-  return InitCodes();
-}
-
-bool CJBig2_HuffmanTable::InitCodes() {
-  int lenmax = 0;
-  for (uint32_t i = 0; i < NTEMP; ++i)
-    lenmax = std::max(PREFLEN[i], lenmax);
-
-  CODES.resize(NTEMP);
-  std::vector<int> LENCOUNT(lenmax + 1);
-  std::vector<int> FIRSTCODE(lenmax + 1);
-  for (int len : PREFLEN)
-    ++LENCOUNT[len];
-
-  FIRSTCODE[0] = 0;
-  LENCOUNT[0] = 0;
-  for (int i = 1; i <= lenmax; ++i) {
-    pdfium::base::CheckedNumeric<int> shifted;
-    shifted = FIRSTCODE[i - 1] + LENCOUNT[i - 1];
-    shifted <<= 1;
-    if (!shifted.IsValid())
-      return false;
-
-    FIRSTCODE[i] = shifted.ValueOrDie();
-    int CURCODE = FIRSTCODE[i];
-    for (uint32_t j = 0; j < NTEMP; ++j) {
-      if (PREFLEN[j] == i)
-        CODES[j] = CURCODE++;
-    }
-  }
-
-  return true;
+  return CJBig2_Context::HuffmanAssignCode(CODES.data(), NTEMP);
 }
 
 void CJBig2_HuffmanTable::ExtendBuffers(bool increment) {
   if (increment)
     ++NTEMP;
 
-  size_t size = PREFLEN.size();
+  size_t size = CODES.size();
   if (NTEMP < size)
     return;
 
   size += 16;
   ASSERT(NTEMP < size);
-  PREFLEN.resize(size);
+  CODES.resize(size);
   RANGELEN.resize(size);
   RANGELOW.resize(size);
 }
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.h b/core/fxcodec/jbig2/JBig2_HuffmanTable.h
index b49fceb..351eb9d 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanTable.h
+++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.h
@@ -9,40 +9,35 @@
 
 #include <vector>
 
+#include "core/fxcodec/jbig2/JBig2_Define.h"
 #include "core/fxcrt/fx_system.h"
 
 class CJBig2_BitStream;
-struct JBig2TableLine;
 
 class CJBig2_HuffmanTable {
  public:
-  CJBig2_HuffmanTable(const JBig2TableLine* pTable,
-                      uint32_t nLines,
-                      bool bHTOOB);
-
+  explicit CJBig2_HuffmanTable(size_t idx);
   explicit CJBig2_HuffmanTable(CJBig2_BitStream* pStream);
-
   ~CJBig2_HuffmanTable();
 
   bool IsHTOOB() const { return HTOOB; }
   uint32_t Size() const { return NTEMP; }
-  const std::vector<int>& GetCODES() const { return CODES; }
-  const std::vector<int>& GetPREFLEN() const { return PREFLEN; }
+  const std::vector<JBig2HuffmanCode>& GetCODES() const { return CODES; }
   const std::vector<int>& GetRANGELEN() const { return RANGELEN; }
   const std::vector<int>& GetRANGELOW() const { return RANGELOW; }
   bool IsOK() const { return m_bOK; }
 
+  constexpr static size_t kNumHuffmanTables = 16;
+
  private:
-  void ParseFromStandardTable(const JBig2TableLine* pTable);
+  bool ParseFromStandardTable(size_t table_idx);
   bool ParseFromCodedBuffer(CJBig2_BitStream* pStream);
-  bool InitCodes();
   void ExtendBuffers(bool increment);
 
   bool m_bOK;
   bool HTOOB;
   uint32_t NTEMP;
-  std::vector<int> CODES;
-  std::vector<int> PREFLEN;
+  std::vector<JBig2HuffmanCode> CODES;
   std::vector<int> RANGELEN;
   std::vector<int> RANGELOW;
 };
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.cpp b/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.cpp
deleted file mode 100644
index abff786..0000000
--- a/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.cpp
+++ /dev/null
@@ -1,118 +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/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h"
-
-#include "core/fxcrt/fx_memory.h"
-
-const bool HuffmanTable_HTOOB_B1 = false;
-const JBig2TableLine HuffmanTable_B1[] = {{1, 4, 0},
-                                          {2, 8, 16},
-                                          {3, 16, 272},
-                                          {0, 32, -1},
-                                          {3, 32, 65808}};
-const size_t HuffmanTable_B1_Size = FX_ArraySize(HuffmanTable_B1);
-
-const bool HuffmanTable_HTOOB_B2 = true;
-const JBig2TableLine HuffmanTable_B2[] = {{1, 0, 0},   {2, 0, 1},  {3, 0, 2},
-                                          {4, 3, 3},   {5, 6, 11}, {0, 32, -1},
-                                          {6, 32, 75}, {6, 0, 0}};
-const size_t HuffmanTable_B2_Size = FX_ArraySize(HuffmanTable_B2);
-
-const bool HuffmanTable_HTOOB_B3 = true;
-const JBig2TableLine HuffmanTable_B3[] = {
-    {8, 8, -256}, {1, 0, 0},     {2, 0, 1},   {3, 0, 2}, {4, 3, 3},
-    {5, 6, 11},   {8, 32, -257}, {7, 32, 75}, {6, 0, 0}};
-const size_t HuffmanTable_B3_Size = FX_ArraySize(HuffmanTable_B3);
-
-const bool HuffmanTable_HTOOB_B4 = false;
-const JBig2TableLine HuffmanTable_B4[] = {
-    {1, 0, 1},  {2, 0, 2},   {3, 0, 3},   {4, 3, 4},
-    {5, 6, 12}, {0, 32, -1}, {5, 32, 76},
-};
-const size_t HuffmanTable_B4_Size = FX_ArraySize(HuffmanTable_B4);
-
-const bool HuffmanTable_HTOOB_B5 = false;
-const JBig2TableLine HuffmanTable_B5[] = {{7, 8, -255},  {1, 0, 1},  {2, 0, 2},
-                                          {3, 0, 3},     {4, 3, 4},  {5, 6, 12},
-                                          {7, 32, -256}, {6, 32, 76}};
-const size_t HuffmanTable_B5_Size = FX_ArraySize(HuffmanTable_B5);
-
-const bool HuffmanTable_HTOOB_B6 = false;
-const JBig2TableLine HuffmanTable_B6[] = {
-    {5, 10, -2048}, {4, 9, -1024}, {4, 8, -512},   {4, 7, -256}, {5, 6, -128},
-    {5, 5, -64},    {4, 5, -32},   {2, 7, 0},      {3, 7, 128},  {3, 8, 256},
-    {4, 9, 512},    {4, 10, 1024}, {6, 32, -2049}, {6, 32, 2048}};
-const size_t HuffmanTable_B6_Size = FX_ArraySize(HuffmanTable_B6);
-
-const bool HuffmanTable_HTOOB_B7 = false;
-const JBig2TableLine HuffmanTable_B7[] = {
-    {4, 9, -1024}, {3, 8, -512}, {4, 7, -256},  {5, 6, -128},   {5, 5, -64},
-    {4, 5, -32},   {4, 5, 0},    {5, 5, 32},    {5, 6, 64},     {4, 7, 128},
-    {3, 8, 256},   {3, 9, 512},  {3, 10, 1024}, {5, 32, -1025}, {5, 32, 2048},
-};
-const size_t HuffmanTable_B7_Size = FX_ArraySize(HuffmanTable_B7);
-
-const bool HuffmanTable_HTOOB_B8 = true;
-const JBig2TableLine HuffmanTable_B8[] = {
-    {8, 3, -15}, {9, 1, -7},  {8, 1, -5},   {9, 0, -3},   {7, 0, -2},
-    {4, 0, -1},  {2, 1, 0},   {5, 0, 2},    {6, 0, 3},    {3, 4, 4},
-    {6, 1, 20},  {4, 4, 22},  {4, 5, 38},   {5, 6, 70},   {5, 7, 134},
-    {6, 7, 262}, {7, 8, 390}, {6, 10, 646}, {9, 32, -16}, {9, 32, 1670},
-    {2, 0, 0}};
-const size_t HuffmanTable_B8_Size = FX_ArraySize(HuffmanTable_B8);
-
-const bool HuffmanTable_HTOOB_B9 = true;
-const JBig2TableLine HuffmanTable_B9[] = {
-    {8, 4, -31},   {9, 2, -15}, {8, 2, -11}, {9, 1, -7},    {7, 1, -5},
-    {4, 1, -3},    {3, 1, -1},  {3, 1, 1},   {5, 1, 3},     {6, 1, 5},
-    {3, 5, 7},     {6, 2, 39},  {4, 5, 43},  {4, 6, 75},    {5, 7, 139},
-    {5, 8, 267},   {6, 8, 523}, {7, 9, 779}, {6, 11, 1291}, {9, 32, -32},
-    {9, 32, 3339}, {2, 0, 0}};
-const size_t HuffmanTable_B9_Size = FX_ArraySize(HuffmanTable_B9);
-
-const bool HuffmanTable_HTOOB_B10 = true;
-const JBig2TableLine HuffmanTable_B10[] = {
-    {7, 4, -21}, {8, 0, -5},    {7, 0, -4},    {5, 0, -3},   {2, 2, -2},
-    {5, 0, 2},   {6, 0, 3},     {7, 0, 4},     {8, 0, 5},    {2, 6, 6},
-    {5, 5, 70},  {6, 5, 102},   {6, 6, 134},   {6, 7, 198},  {6, 8, 326},
-    {6, 9, 582}, {6, 10, 1094}, {7, 11, 2118}, {8, 32, -22}, {8, 32, 4166},
-    {2, 0, 0}};
-const size_t HuffmanTable_B10_Size = FX_ArraySize(HuffmanTable_B10);
-
-const bool HuffmanTable_HTOOB_B11 = false;
-const JBig2TableLine HuffmanTable_B11[] = {
-    {1, 0, 1},  {2, 1, 2},  {4, 0, 4},  {4, 1, 5},   {5, 1, 7},
-    {5, 2, 9},  {6, 2, 13}, {7, 2, 17}, {7, 3, 21},  {7, 4, 29},
-    {7, 5, 45}, {7, 6, 77}, {0, 32, 0}, {7, 32, 141}};
-const size_t HuffmanTable_B11_Size = FX_ArraySize(HuffmanTable_B11);
-
-const bool HuffmanTable_HTOOB_B12 = false;
-const JBig2TableLine HuffmanTable_B12[] = {
-    {1, 0, 1},  {2, 0, 2},  {3, 1, 3},  {5, 0, 5},  {5, 1, 6},
-    {6, 1, 8},  {7, 0, 10}, {7, 1, 11}, {7, 2, 13}, {7, 3, 17},
-    {7, 4, 25}, {8, 5, 41}, {0, 32, 0}, {8, 32, 73}};
-const size_t HuffmanTable_B12_Size = FX_ArraySize(HuffmanTable_B12);
-
-const bool HuffmanTable_HTOOB_B13 = false;
-const JBig2TableLine HuffmanTable_B13[] = {
-    {1, 0, 1},  {3, 0, 2},  {4, 0, 3},  {5, 0, 4},   {4, 1, 5},
-    {3, 3, 7},  {6, 1, 15}, {6, 2, 17}, {6, 3, 21},  {6, 4, 29},
-    {6, 5, 45}, {7, 6, 77}, {0, 32, 0}, {7, 32, 141}};
-const size_t HuffmanTable_B13_Size = FX_ArraySize(HuffmanTable_B13);
-
-const bool HuffmanTable_HTOOB_B14 = false;
-const JBig2TableLine HuffmanTable_B14[] = {{3, 0, -2}, {3, 0, -1}, {1, 0, 0},
-                                           {3, 0, 1},  {3, 0, 2},  {0, 32, -3},
-                                           {0, 32, 3}};
-const size_t HuffmanTable_B14_Size = FX_ArraySize(HuffmanTable_B14);
-
-const bool HuffmanTable_HTOOB_B15 = false;
-const JBig2TableLine HuffmanTable_B15[] = {
-    {7, 4, -24}, {6, 2, -8},   {5, 1, -4}, {4, 0, -2}, {3, 0, -1},
-    {1, 0, 0},   {3, 0, 1},    {4, 0, 2},  {5, 1, 3},  {6, 2, 5},
-    {7, 4, 9},   {7, 32, -25}, {7, 32, 25}};
-const size_t HuffmanTable_B15_Size = FX_ArraySize(HuffmanTable_B15);
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h b/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h
deleted file mode 100644
index c064f57..0000000
--- a/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_STANDARD_H_
-#define CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_STANDARD_H_
-
-#include "core/fxcrt/fx_system.h"
-
-struct JBig2TableLine {
-  uint8_t PREFLEN;
-  uint8_t RANDELEN;
-  int32_t RANGELOW;
-};
-
-extern const bool HuffmanTable_HTOOB_B1;
-extern const JBig2TableLine HuffmanTable_B1[];
-extern const size_t HuffmanTable_B1_Size;
-
-extern const bool HuffmanTable_HTOOB_B2;
-extern const JBig2TableLine HuffmanTable_B2[];
-extern const size_t HuffmanTable_B2_Size;
-
-extern const bool HuffmanTable_HTOOB_B3;
-extern const JBig2TableLine HuffmanTable_B3[];
-extern const size_t HuffmanTable_B3_Size;
-
-extern const bool HuffmanTable_HTOOB_B4;
-extern const JBig2TableLine HuffmanTable_B4[];
-extern const size_t HuffmanTable_B4_Size;
-
-extern const bool HuffmanTable_HTOOB_B5;
-extern const JBig2TableLine HuffmanTable_B5[];
-extern const size_t HuffmanTable_B5_Size;
-
-extern const bool HuffmanTable_HTOOB_B6;
-extern const JBig2TableLine HuffmanTable_B6[];
-extern const size_t HuffmanTable_B6_Size;
-
-extern const bool HuffmanTable_HTOOB_B7;
-extern const JBig2TableLine HuffmanTable_B7[];
-extern const size_t HuffmanTable_B7_Size;
-
-extern const bool HuffmanTable_HTOOB_B8;
-extern const JBig2TableLine HuffmanTable_B8[];
-extern const size_t HuffmanTable_B8_Size;
-
-extern const bool HuffmanTable_HTOOB_B9;
-extern const JBig2TableLine HuffmanTable_B9[];
-extern const size_t HuffmanTable_B9_Size;
-
-extern const bool HuffmanTable_HTOOB_B10;
-extern const JBig2TableLine HuffmanTable_B10[];
-extern const size_t HuffmanTable_B10_Size;
-
-extern const bool HuffmanTable_HTOOB_B11;
-extern const JBig2TableLine HuffmanTable_B11[];
-extern const size_t HuffmanTable_B11_Size;
-
-extern const bool HuffmanTable_HTOOB_B12;
-extern const JBig2TableLine HuffmanTable_B12[];
-extern const size_t HuffmanTable_B12_Size;
-
-extern const bool HuffmanTable_HTOOB_B13;
-extern const JBig2TableLine HuffmanTable_B13[];
-extern const size_t HuffmanTable_B13_Size;
-
-extern const bool HuffmanTable_HTOOB_B14;
-extern const JBig2TableLine HuffmanTable_B14[];
-extern const size_t HuffmanTable_B14_Size;
-
-extern const bool HuffmanTable_HTOOB_B15;
-extern const JBig2TableLine HuffmanTable_B15[];
-extern const size_t HuffmanTable_B15_Size;
-
-#endif  // CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_STANDARD_H_
diff --git a/core/fxcodec/jbig2/JBig2_Image.cpp b/core/fxcodec/jbig2/JBig2_Image.cpp
index b0d75d4..3cfb4f8 100644
--- a/core/fxcodec/jbig2/JBig2_Image.cpp
+++ b/core/fxcodec/jbig2/JBig2_Image.cpp
@@ -7,6 +7,7 @@
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 
 #include <limits.h>
+#include <string.h>
 
 #include <algorithm>
 #include <memory>
@@ -16,6 +17,21 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/ptr_util.h"
 
+#define JBIG2_GETDWORD(buf)                  \
+  ((static_cast<uint32_t>((buf)[0]) << 24) | \
+   (static_cast<uint32_t>((buf)[1]) << 16) | \
+   (static_cast<uint32_t>((buf)[2]) << 8) |  \
+   (static_cast<uint32_t>((buf)[3]) << 0))
+
+#define JBIG2_PUTDWORD(buf, val)                 \
+  ((buf)[0] = static_cast<uint8_t>((val) >> 24), \
+   (buf)[1] = static_cast<uint8_t>((val) >> 16), \
+   (buf)[2] = static_cast<uint8_t>((val) >> 8),  \
+   (buf)[3] = static_cast<uint8_t>((val) >> 0))
+
+#define BIT_INDEX_TO_BYTE(x) ((x) >> 3)
+#define BIT_INDEX_TO_ALIGNED_BYTE(x) (((x) >> 5) << 2)
+
 namespace {
 
 const int kMaxImagePixels = INT_MAX - 31;
@@ -23,12 +39,11 @@
 
 }  // namespace
 
-CJBig2_Image::CJBig2_Image(int32_t w, int32_t h)
-    : m_pData(nullptr), m_nWidth(0), m_nHeight(0), m_nStride(0) {
+CJBig2_Image::CJBig2_Image(int32_t w, int32_t h) {
   if (w <= 0 || h <= 0 || w > kMaxImagePixels)
     return;
 
-  int32_t stride_pixels = (w + 31) & ~31;
+  int32_t stride_pixels = FxAlignToBoundary<32>(w);
   if (h > kMaxImagePixels / stride_pixels)
     return;
 
@@ -39,9 +54,15 @@
       FX_Alloc2D(uint8_t, m_nStride, m_nHeight)));
 }
 
-CJBig2_Image::CJBig2_Image(int32_t w, int32_t h, int32_t stride, uint8_t* pBuf)
-    : m_pData(nullptr), m_nWidth(0), m_nHeight(0), m_nStride(0) {
-  if (w < 0 || h < 0 || stride < 0 || stride > kMaxImageBytes)
+CJBig2_Image::CJBig2_Image(int32_t w,
+                           int32_t h,
+                           int32_t stride,
+                           uint8_t* pBuf) {
+  if (w < 0 || h < 0)
+    return;
+
+  // Stride must be word-aligned.
+  if (stride < 0 || stride > kMaxImageBytes || stride % 4 != 0)
     return;
 
   int32_t stride_pixels = 8 * stride;
@@ -55,225 +76,218 @@
 }
 
 CJBig2_Image::CJBig2_Image(const CJBig2_Image& other)
-    : m_pData(nullptr),
-      m_nWidth(other.m_nWidth),
+    : m_nWidth(other.m_nWidth),
       m_nHeight(other.m_nHeight),
       m_nStride(other.m_nStride) {
   if (other.m_pData) {
     m_pData.Reset(std::unique_ptr<uint8_t, FxFreeDeleter>(
         FX_Alloc2D(uint8_t, m_nStride, m_nHeight)));
-    JBIG2_memcpy(data(), other.data(), m_nStride * m_nHeight);
+    memcpy(data(), other.data(), m_nStride * m_nHeight);
   }
 }
 
 CJBig2_Image::~CJBig2_Image() {}
 
-int CJBig2_Image::getPixel(int32_t x, int32_t y) const {
-  if (!m_pData)
-    return 0;
-
-  if (x < 0 || x >= m_nWidth)
-    return 0;
-
-  if (y < 0 || y >= m_nHeight)
-    return 0;
-
-  int32_t m = y * m_nStride + (x >> 3);
-  int32_t n = x & 7;
-  return ((data()[m] >> (7 - n)) & 1);
+// static
+bool CJBig2_Image::IsValidImageSize(int32_t w, int32_t h) {
+  return w > 0 && w <= JBIG2_MAX_IMAGE_SIZE && h > 0 &&
+         h <= JBIG2_MAX_IMAGE_SIZE;
 }
 
-int32_t CJBig2_Image::setPixel(int32_t x, int32_t y, int v) {
+int CJBig2_Image::GetPixel(int32_t x, int32_t y) const {
   if (!m_pData)
     return 0;
 
   if (x < 0 || x >= m_nWidth)
     return 0;
 
-  if (y < 0 || y >= m_nHeight)
+  const uint8_t* pLine = GetLine(y);
+  if (!pLine)
     return 0;
 
-  int32_t m = y * m_nStride + (x >> 3);
+  int32_t m = BIT_INDEX_TO_BYTE(x);
   int32_t n = x & 7;
+  return ((pLine[m] >> (7 - n)) & 1);
+}
+
+void CJBig2_Image::SetPixel(int32_t x, int32_t y, int v) {
+  if (!m_pData)
+    return;
+
+  if (x < 0 || x >= m_nWidth)
+    return;
+
+  uint8_t* pLine = GetLine(y);
+  if (!pLine)
+    return;
+
+  int32_t m = BIT_INDEX_TO_BYTE(x);
+  int32_t n = 1 << (7 - (x & 7));
   if (v)
-    data()[m] |= 1 << (7 - n);
+    pLine[m] |= n;
   else
-    data()[m] &= ~(1 << (7 - n));
-
-  return 1;
+    pLine[m] &= ~n;
 }
 
-void CJBig2_Image::copyLine(int32_t hTo, int32_t hFrom) {
+void CJBig2_Image::CopyLine(int32_t hTo, int32_t hFrom) {
   if (!m_pData)
     return;
 
-  if (hFrom < 0 || hFrom >= m_nHeight) {
-    JBIG2_memset(data() + hTo * m_nStride, 0, m_nStride);
-  } else {
-    JBIG2_memcpy(data() + hTo * m_nStride, data() + hFrom * m_nStride,
-                 m_nStride);
+  uint8_t* pDst = GetLine(hTo);
+  if (!pDst)
+    return;
+
+  const uint8_t* pSrc = GetLine(hFrom);
+  if (!pSrc) {
+    memset(pDst, 0, m_nStride);
+    return;
   }
+  memcpy(pDst, pSrc, m_nStride);
 }
-void CJBig2_Image::fill(bool v) {
+
+void CJBig2_Image::Fill(bool v) {
   if (!m_pData)
     return;
 
-  JBIG2_memset(data(), v ? 0xff : 0, m_nStride * m_nHeight);
+  memset(data(), v ? 0xff : 0, m_nStride * m_nHeight);
 }
-bool CJBig2_Image::composeTo(CJBig2_Image* pDst,
+
+bool CJBig2_Image::ComposeTo(CJBig2_Image* pDst,
                              int32_t x,
                              int32_t y,
                              JBig2ComposeOp op) {
-  if (!m_pData)
-    return false;
-
-  return composeTo_opt2(pDst, x, y, op);
-}
-bool CJBig2_Image::composeTo(CJBig2_Image* pDst,
-                             int32_t x,
-                             int32_t y,
-                             JBig2ComposeOp op,
-                             const FX_RECT* pSrcRect) {
-  if (!m_pData)
-    return false;
-
-  if (!pSrcRect || *pSrcRect == FX_RECT(0, 0, m_nWidth, m_nHeight))
-    return composeTo_opt2(pDst, x, y, op);
-
-  return composeTo_opt2(pDst, x, y, op, pSrcRect);
+  return m_pData &&
+         ComposeToInternal(pDst, x, y, op, FX_RECT(0, 0, m_nWidth, m_nHeight));
 }
 
-bool CJBig2_Image::composeFrom(int32_t x,
+bool CJBig2_Image::ComposeToWithRect(CJBig2_Image* pDst,
+                                     int32_t x,
+                                     int32_t y,
+                                     const FX_RECT& rtSrc,
+                                     JBig2ComposeOp op) {
+  return m_pData && ComposeToInternal(pDst, x, y, op, rtSrc);
+}
+
+bool CJBig2_Image::ComposeFrom(int32_t x,
                                int32_t y,
                                CJBig2_Image* pSrc,
                                JBig2ComposeOp op) {
-  if (!m_pData)
-    return false;
-
-  return pSrc->composeTo(this, x, y, op);
-}
-bool CJBig2_Image::composeFrom(int32_t x,
-                               int32_t y,
-                               CJBig2_Image* pSrc,
-                               JBig2ComposeOp op,
-                               const FX_RECT* pSrcRect) {
-  return m_pData ? pSrc->composeTo(this, x, y, op, pSrcRect) : false;
+  return m_pData && pSrc->ComposeTo(this, x, y, op);
 }
 
-#define JBIG2_GETDWORD(buf) \
-  ((uint32_t)(((buf)[0] << 24) | ((buf)[1] << 16) | ((buf)[2] << 8) | (buf)[3]))
+bool CJBig2_Image::ComposeFromWithRect(int32_t x,
+                                       int32_t y,
+                                       CJBig2_Image* pSrc,
+                                       const FX_RECT& rtSrc,
+                                       JBig2ComposeOp op) {
+  return m_pData && pSrc->ComposeToWithRect(this, x, y, rtSrc, op);
+}
 
-std::unique_ptr<CJBig2_Image> CJBig2_Image::subImage(int32_t x,
+std::unique_ptr<CJBig2_Image> CJBig2_Image::SubImage(int32_t x,
                                                      int32_t y,
                                                      int32_t w,
                                                      int32_t h) {
-  int32_t m;
-  int32_t n;
-  int32_t j;
-  uint8_t* pLineSrc;
-  uint8_t* pLineDst;
-  uint32_t wTmp;
-  uint8_t* pSrc;
-  uint8_t* pSrcEnd;
-  uint8_t* pDst;
-  uint8_t* pDstEnd;
-  if (w == 0 || h == 0)
-    return nullptr;
-
   auto pImage = pdfium::MakeUnique<CJBig2_Image>(w, h);
-  if (!m_pData) {
-    pImage->fill(0);
-    return pImage;
-  }
-  if (!pImage->m_pData)
+  if (!pImage->data() || !m_pData)
     return pImage;
 
-  pLineSrc = data() + m_nStride * y;
-  pLineDst = pImage->data();
-  m = (x >> 5) << 2;
-  n = x & 31;
-  if (n == 0) {
-    for (j = 0; j < h; j++) {
-      pSrc = pLineSrc + m;
-      pSrcEnd = pLineSrc + m_nStride;
-      pDst = pLineDst;
-      pDstEnd = pLineDst + pImage->m_nStride;
-      for (; pDst < pDstEnd; pSrc += 4, pDst += 4) {
-        *((uint32_t*)pDst) = *((uint32_t*)pSrc);
-      }
-      pLineSrc += m_nStride;
-      pLineDst += pImage->m_nStride;
-    }
-  } else {
-    for (j = 0; j < h; j++) {
-      pSrc = pLineSrc + m;
-      pSrcEnd = pLineSrc + m_nStride;
-      pDst = pLineDst;
-      pDstEnd = pLineDst + pImage->m_nStride;
-      for (; pDst < pDstEnd; pSrc += 4, pDst += 4) {
-        if (pSrc + 4 < pSrcEnd) {
-          wTmp = (JBIG2_GETDWORD(pSrc) << n) |
-                 (JBIG2_GETDWORD(pSrc + 4) >> (32 - n));
-        } else {
-          wTmp = JBIG2_GETDWORD(pSrc) << n;
-        }
-        pDst[0] = (uint8_t)(wTmp >> 24);
-        pDst[1] = (uint8_t)(wTmp >> 16);
-        pDst[2] = (uint8_t)(wTmp >> 8);
-        pDst[3] = (uint8_t)wTmp;
-      }
-      pLineSrc += m_nStride;
-      pLineDst += pImage->m_nStride;
-    }
-  }
+  if (x < 0 || x >= m_nWidth || y < 0 || y >= m_nHeight)
+    return pImage;
+
+  // Fast case when byte-aligned, normal slow case otherwise.
+  if ((x & 7) == 0)
+    SubImageFast(x, y, w, h, pImage.get());
+  else
+    SubImageSlow(x, y, w, h, pImage.get());
+
   return pImage;
 }
 
-void CJBig2_Image::expand(int32_t h, bool v) {
+void CJBig2_Image::SubImageFast(int32_t x,
+                                int32_t y,
+                                int32_t w,
+                                int32_t h,
+                                CJBig2_Image* pImage) {
+  int32_t m = BIT_INDEX_TO_BYTE(x);
+  int32_t bytes_to_copy = std::min(pImage->m_nStride, m_nStride - m);
+  int32_t lines_to_copy = std::min(pImage->m_nHeight, m_nHeight - y);
+  for (int32_t j = 0; j < lines_to_copy; j++)
+    memcpy(pImage->GetLineUnsafe(j), GetLineUnsafe(y + j) + m, bytes_to_copy);
+}
+
+void CJBig2_Image::SubImageSlow(int32_t x,
+                                int32_t y,
+                                int32_t w,
+                                int32_t h,
+                                CJBig2_Image* pImage) {
+  int32_t m = BIT_INDEX_TO_ALIGNED_BYTE(x);
+  int32_t n = x & 31;
+  int32_t bytes_to_copy = std::min(pImage->m_nStride, m_nStride - m);
+  int32_t lines_to_copy = std::min(pImage->m_nHeight, m_nHeight - y);
+  for (int32_t j = 0; j < lines_to_copy; j++) {
+    const uint8_t* pLineSrc = GetLineUnsafe(y + j);
+    uint8_t* pLineDst = pImage->GetLineUnsafe(j);
+    const uint8_t* pSrc = pLineSrc + m;
+    const uint8_t* pSrcEnd = pLineSrc + m_nStride;
+    uint8_t* pDstEnd = pLineDst + bytes_to_copy;
+    for (uint8_t* pDst = pLineDst; pDst < pDstEnd; pSrc += 4, pDst += 4) {
+      uint32_t wTmp = JBIG2_GETDWORD(pSrc) << n;
+      if (pSrc + 4 < pSrcEnd)
+        wTmp |= (JBIG2_GETDWORD(pSrc + 4) >> (32 - n));
+      JBIG2_PUTDWORD(pDst, wTmp);
+    }
+  }
+}
+
+void CJBig2_Image::Expand(int32_t h, bool v) {
   if (!m_pData || h <= m_nHeight || h > kMaxImageBytes / m_nStride)
     return;
 
   if (m_pData.IsOwned()) {
-    m_pData.Reset(std::unique_ptr<uint8_t, FxFreeDeleter>(
-        FX_Realloc(uint8_t, m_pData.Release().release(), h * m_nStride)));
+    m_pData.Reset(std::unique_ptr<uint8_t, FxFreeDeleter>(FX_Realloc(
+        uint8_t, m_pData.ReleaseAndClear().release(), h * m_nStride)));
   } else {
     uint8_t* pExternalBuffer = data();
     m_pData.Reset(std::unique_ptr<uint8_t, FxFreeDeleter>(
         FX_Alloc(uint8_t, h * m_nStride)));
-    JBIG2_memcpy(data(), pExternalBuffer, m_nHeight * m_nStride);
+    memcpy(data(), pExternalBuffer, m_nHeight * m_nStride);
   }
-  JBIG2_memset(data() + m_nHeight * m_nStride, v ? 0xff : 0,
-               (h - m_nHeight) * m_nStride);
+  memset(data() + m_nHeight * m_nStride, v ? 0xff : 0,
+         (h - m_nHeight) * m_nStride);
   m_nHeight = h;
 }
 
-bool CJBig2_Image::composeTo_opt2(CJBig2_Image* pDst,
-                                  int32_t x,
-                                  int32_t y,
-                                  JBig2ComposeOp op) {
-  if (!m_pData)
-    return false;
+bool CJBig2_Image::ComposeToInternal(CJBig2_Image* pDst,
+                                     int32_t x,
+                                     int32_t y,
+                                     JBig2ComposeOp op,
+                                     const FX_RECT& rtSrc) {
+  ASSERT(m_pData);
 
+  // TODO(weili): Check whether the range check is correct. Should x>=1048576?
   if (x < -1048576 || x > 1048576 || y < -1048576 || y > 1048576)
     return false;
 
+  int32_t sw = rtSrc.Width();
+  int32_t sh = rtSrc.Height();
+
   int32_t xs0 = x < 0 ? -x : 0;
   int32_t xs1;
   FX_SAFE_INT32 iChecked = pDst->m_nWidth;
   iChecked -= x;
-  if (iChecked.IsValid() && m_nWidth > iChecked.ValueOrDie())
+  if (iChecked.IsValid() && sw > iChecked.ValueOrDie())
     xs1 = iChecked.ValueOrDie();
   else
-    xs1 = m_nWidth;
+    xs1 = sw;
 
   int32_t ys0 = y < 0 ? -y : 0;
   int32_t ys1;
   iChecked = pDst->m_nHeight;
   iChecked -= y;
-  if (iChecked.IsValid() && m_nHeight > iChecked.ValueOrDie())
-    ys1 = pDst->m_nHeight - y;
+  if (iChecked.IsValid() && sh > iChecked.ValueOrDie())
+    ys1 = iChecked.ValueOrDie();
   else
-    ys1 = m_nHeight;
+    ys1 = sh;
 
   if (ys0 >= ys1 || xs0 >= xs1)
     return false;
@@ -290,14 +304,18 @@
   uint32_t maskL = 0xffffffff >> d1;
   uint32_t maskR = 0xffffffff << ((32 - (xd1 & 31)) % 32);
   uint32_t maskM = maskL & maskR;
-  uint8_t* lineSrc = data() + ys0 * m_nStride + ((xs0 >> 5) << 2);
-  int32_t lineLeft = m_nStride - ((xs0 >> 5) << 2);
-  uint8_t* lineDst = pDst->data() + yd0 * pDst->m_nStride + ((xd0 >> 5) << 2);
+  const uint8_t* lineSrc = GetLineUnsafe(rtSrc.top + ys0) +
+                           BIT_INDEX_TO_ALIGNED_BYTE(xs0 + rtSrc.left);
+  const uint8_t* lineSrcEnd = data() + m_nHeight * m_nStride;
+  int32_t lineLeft = m_nStride - BIT_INDEX_TO_ALIGNED_BYTE(xs0);
+  uint8_t* lineDst = pDst->GetLineUnsafe(yd0) + BIT_INDEX_TO_ALIGNED_BYTE(xd0);
   if ((xd0 & ~31) == ((xd1 - 1) & ~31)) {
     if ((xs0 & ~31) == ((xs1 - 1) & ~31)) {
       if (s1 > d1) {
         uint32_t shift = s1 - d1;
         for (int32_t yy = yd0; yy < yd1; yy++) {
+          if (lineSrc >= lineSrcEnd)
+            return false;
           uint32_t tmp1 = JBIG2_GETDWORD(lineSrc) << shift;
           uint32_t tmp2 = JBIG2_GETDWORD(lineDst);
           uint32_t tmp = 0;
@@ -318,16 +336,15 @@
               tmp = (tmp2 & ~maskM) | (tmp1 & maskM);
               break;
           }
-          lineDst[0] = (uint8_t)(tmp >> 24);
-          lineDst[1] = (uint8_t)(tmp >> 16);
-          lineDst[2] = (uint8_t)(tmp >> 8);
-          lineDst[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(lineDst, tmp);
           lineSrc += m_nStride;
           lineDst += pDst->m_nStride;
         }
       } else {
         uint32_t shift = d1 - s1;
         for (int32_t yy = yd0; yy < yd1; yy++) {
+          if (lineSrc >= lineSrcEnd)
+            return false;
           uint32_t tmp1 = JBIG2_GETDWORD(lineSrc) >> shift;
           uint32_t tmp2 = JBIG2_GETDWORD(lineDst);
           uint32_t tmp = 0;
@@ -348,10 +365,7 @@
               tmp = (tmp2 & ~maskM) | (tmp1 & maskM);
               break;
           }
-          lineDst[0] = (uint8_t)(tmp >> 24);
-          lineDst[1] = (uint8_t)(tmp >> 16);
-          lineDst[2] = (uint8_t)(tmp >> 8);
-          lineDst[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(lineDst, tmp);
           lineSrc += m_nStride;
           lineDst += pDst->m_nStride;
         }
@@ -360,6 +374,8 @@
       uint32_t shift1 = s1 - d1;
       uint32_t shift2 = 32 - shift1;
       for (int32_t yy = yd0; yy < yd1; yy++) {
+        if (lineSrc >= lineSrcEnd)
+          return false;
         uint32_t tmp1 = (JBIG2_GETDWORD(lineSrc) << shift1) |
                         (JBIG2_GETDWORD(lineSrc + 4) >> shift2);
         uint32_t tmp2 = JBIG2_GETDWORD(lineDst);
@@ -381,25 +397,21 @@
             tmp = (tmp2 & ~maskM) | (tmp1 & maskM);
             break;
         }
-        lineDst[0] = (uint8_t)(tmp >> 24);
-        lineDst[1] = (uint8_t)(tmp >> 16);
-        lineDst[2] = (uint8_t)(tmp >> 8);
-        lineDst[3] = (uint8_t)tmp;
+        JBIG2_PUTDWORD(lineDst, tmp);
         lineSrc += m_nStride;
         lineDst += pDst->m_nStride;
       }
     }
   } else {
-    uint8_t* sp = nullptr;
-    uint8_t* dp = nullptr;
-
     if (s1 > d1) {
       uint32_t shift1 = s1 - d1;
       uint32_t shift2 = 32 - shift1;
       int32_t middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5);
       for (int32_t yy = yd0; yy < yd1; yy++) {
-        sp = lineSrc;
-        dp = lineDst;
+        if (lineSrc >= lineSrcEnd)
+          return false;
+        const uint8_t* sp = lineSrc;
+        uint8_t* dp = lineDst;
         if (d1 != 0) {
           uint32_t tmp1 = (JBIG2_GETDWORD(sp) << shift1) |
                           (JBIG2_GETDWORD(sp + 4) >> shift2);
@@ -422,10 +434,7 @@
               tmp = (tmp2 & ~maskL) | (tmp1 & maskL);
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
           sp += 4;
           dp += 4;
         }
@@ -451,10 +460,7 @@
               tmp = tmp1;
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
           sp += 4;
           dp += 4;
         }
@@ -482,10 +488,7 @@
               tmp = (tmp2 & ~maskR) | (tmp1 & maskR);
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
         }
         lineSrc += m_nStride;
         lineDst += pDst->m_nStride;
@@ -493,8 +496,10 @@
     } else if (s1 == d1) {
       int32_t middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5);
       for (int32_t yy = yd0; yy < yd1; yy++) {
-        sp = lineSrc;
-        dp = lineDst;
+        if (lineSrc >= lineSrcEnd)
+          return false;
+        const uint8_t* sp = lineSrc;
+        uint8_t* dp = lineDst;
         if (d1 != 0) {
           uint32_t tmp1 = JBIG2_GETDWORD(sp);
           uint32_t tmp2 = JBIG2_GETDWORD(dp);
@@ -516,10 +521,7 @@
               tmp = (tmp2 & ~maskL) | (tmp1 & maskL);
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
           sp += 4;
           dp += 4;
         }
@@ -544,10 +546,7 @@
               tmp = tmp1;
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
           sp += 4;
           dp += 4;
         }
@@ -572,10 +571,7 @@
               tmp = (tmp2 & ~maskR) | (tmp1 & maskR);
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
         }
         lineSrc += m_nStride;
         lineDst += pDst->m_nStride;
@@ -585,8 +581,10 @@
       uint32_t shift2 = 32 - shift1;
       int32_t middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5);
       for (int32_t yy = yd0; yy < yd1; yy++) {
-        sp = lineSrc;
-        dp = lineDst;
+        if (lineSrc >= lineSrcEnd)
+          return false;
+        const uint8_t* sp = lineSrc;
+        uint8_t* dp = lineDst;
         if (d1 != 0) {
           uint32_t tmp1 = JBIG2_GETDWORD(sp) >> shift1;
           uint32_t tmp2 = JBIG2_GETDWORD(dp);
@@ -608,10 +606,7 @@
               tmp = (tmp2 & ~maskL) | (tmp1 & maskL);
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
           dp += 4;
         }
         for (int32_t xx = 0; xx < middleDwords; xx++) {
@@ -636,10 +631,7 @@
               tmp = tmp1;
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
           sp += 4;
           dp += 4;
         }
@@ -667,431 +659,7 @@
               tmp = (tmp2 & ~maskR) | (tmp1 & maskR);
               break;
           }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-        }
-        lineSrc += m_nStride;
-        lineDst += pDst->m_nStride;
-      }
-    }
-  }
-  return true;
-}
-
-bool CJBig2_Image::composeTo_opt2(CJBig2_Image* pDst,
-                                  int32_t x,
-                                  int32_t y,
-                                  JBig2ComposeOp op,
-                                  const FX_RECT* pSrcRect) {
-  if (!m_pData)
-    return false;
-
-  // TODO(weili): Check whether the range check is correct. Should x>=1048576?
-  if (x < -1048576 || x > 1048576 || y < -1048576 || y > 1048576) {
-    return false;
-  }
-  int32_t sw = pSrcRect->Width();
-  int32_t sh = pSrcRect->Height();
-  int32_t ys0 = y < 0 ? -y : 0;
-  int32_t ys1 = y + sh > pDst->m_nHeight ? pDst->m_nHeight - y : sh;
-  int32_t xs0 = x < 0 ? -x : 0;
-  int32_t xs1 = x + sw > pDst->m_nWidth ? pDst->m_nWidth - x : sw;
-  if ((ys0 >= ys1) || (xs0 >= xs1)) {
-    return 0;
-  }
-  int32_t w = xs1 - xs0;
-  int32_t h = ys1 - ys0;
-  int32_t yd0 = y < 0 ? 0 : y;
-  int32_t xd0 = x < 0 ? 0 : x;
-  int32_t xd1 = xd0 + w;
-  int32_t yd1 = yd0 + h;
-  int32_t d1 = xd0 & 31;
-  int32_t d2 = xd1 & 31;
-  int32_t s1 = xs0 & 31;
-  int32_t maskL = 0xffffffff >> d1;
-  int32_t maskR = 0xffffffff << ((32 - (xd1 & 31)) % 32);
-  int32_t maskM = maskL & maskR;
-  uint8_t* lineSrc = data() + (pSrcRect->top + ys0) * m_nStride +
-                     (((xs0 + pSrcRect->left) >> 5) << 2);
-  int32_t lineLeft = m_nStride - ((xs0 >> 5) << 2);
-  uint8_t* lineDst = pDst->data() + yd0 * pDst->m_nStride + ((xd0 >> 5) << 2);
-  if ((xd0 & ~31) == ((xd1 - 1) & ~31)) {
-    if ((xs0 & ~31) == ((xs1 - 1) & ~31)) {
-      if (s1 > d1) {
-        uint32_t shift = s1 - d1;
-        for (int32_t yy = yd0; yy < yd1; yy++) {
-          uint32_t tmp1 = JBIG2_GETDWORD(lineSrc) << shift;
-          uint32_t tmp2 = JBIG2_GETDWORD(lineDst);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskM) | (tmp1 & maskM);
-              break;
-          }
-          lineDst[0] = (uint8_t)(tmp >> 24);
-          lineDst[1] = (uint8_t)(tmp >> 16);
-          lineDst[2] = (uint8_t)(tmp >> 8);
-          lineDst[3] = (uint8_t)tmp;
-          lineSrc += m_nStride;
-          lineDst += pDst->m_nStride;
-        }
-      } else {
-        uint32_t shift = d1 - s1;
-        for (int32_t yy = yd0; yy < yd1; yy++) {
-          uint32_t tmp1 = JBIG2_GETDWORD(lineSrc) >> shift;
-          uint32_t tmp2 = JBIG2_GETDWORD(lineDst);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskM) | (tmp1 & maskM);
-              break;
-          }
-          lineDst[0] = (uint8_t)(tmp >> 24);
-          lineDst[1] = (uint8_t)(tmp >> 16);
-          lineDst[2] = (uint8_t)(tmp >> 8);
-          lineDst[3] = (uint8_t)tmp;
-          lineSrc += m_nStride;
-          lineDst += pDst->m_nStride;
-        }
-      }
-    } else {
-      uint32_t shift1 = s1 - d1;
-      uint32_t shift2 = 32 - shift1;
-      for (int32_t yy = yd0; yy < yd1; yy++) {
-        uint32_t tmp1 = (JBIG2_GETDWORD(lineSrc) << shift1) |
-                        (JBIG2_GETDWORD(lineSrc + 4) >> shift2);
-        uint32_t tmp2 = JBIG2_GETDWORD(lineDst);
-        uint32_t tmp = 0;
-        switch (op) {
-          case JBIG2_COMPOSE_OR:
-            tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM);
-            break;
-          case JBIG2_COMPOSE_AND:
-            tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM);
-            break;
-          case JBIG2_COMPOSE_XOR:
-            tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM);
-            break;
-          case JBIG2_COMPOSE_XNOR:
-            tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM);
-            break;
-          case JBIG2_COMPOSE_REPLACE:
-            tmp = (tmp2 & ~maskM) | (tmp1 & maskM);
-            break;
-        }
-        lineDst[0] = (uint8_t)(tmp >> 24);
-        lineDst[1] = (uint8_t)(tmp >> 16);
-        lineDst[2] = (uint8_t)(tmp >> 8);
-        lineDst[3] = (uint8_t)tmp;
-        lineSrc += m_nStride;
-        lineDst += pDst->m_nStride;
-      }
-    }
-  } else {
-    if (s1 > d1) {
-      uint32_t shift1 = s1 - d1;
-      uint32_t shift2 = 32 - shift1;
-      int32_t middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5);
-      for (int32_t yy = yd0; yy < yd1; yy++) {
-        uint8_t* sp = lineSrc;
-        uint8_t* dp = lineDst;
-        if (d1 != 0) {
-          uint32_t tmp1 = (JBIG2_GETDWORD(sp) << shift1) |
-                          (JBIG2_GETDWORD(sp + 4) >> shift2);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskL) | (tmp1 & maskL);
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-          sp += 4;
-          dp += 4;
-        }
-        for (int32_t xx = 0; xx < middleDwords; xx++) {
-          uint32_t tmp1 = (JBIG2_GETDWORD(sp) << shift1) |
-                          (JBIG2_GETDWORD(sp + 4) >> shift2);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = tmp1 | tmp2;
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = tmp1 & tmp2;
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = tmp1 ^ tmp2;
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = ~(tmp1 ^ tmp2);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = tmp1;
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-          sp += 4;
-          dp += 4;
-        }
-        if (d2 != 0) {
-          uint32_t tmp1 =
-              (JBIG2_GETDWORD(sp) << shift1) |
-              (((sp + 4) < lineSrc + lineLeft ? JBIG2_GETDWORD(sp + 4) : 0) >>
-               shift2);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskR) | (tmp1 & maskR);
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-        }
-        lineSrc += m_nStride;
-        lineDst += pDst->m_nStride;
-      }
-    } else if (s1 == d1) {
-      int32_t middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5);
-      for (int32_t yy = yd0; yy < yd1; yy++) {
-        uint8_t* sp = lineSrc;
-        uint8_t* dp = lineDst;
-        if (d1 != 0) {
-          uint32_t tmp1 = JBIG2_GETDWORD(sp);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskL) | (tmp1 & maskL);
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-          sp += 4;
-          dp += 4;
-        }
-        for (int32_t xx = 0; xx < middleDwords; xx++) {
-          uint32_t tmp1 = JBIG2_GETDWORD(sp);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = tmp1 | tmp2;
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = tmp1 & tmp2;
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = tmp1 ^ tmp2;
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = ~(tmp1 ^ tmp2);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = tmp1;
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-          sp += 4;
-          dp += 4;
-        }
-        if (d2 != 0) {
-          uint32_t tmp1 = JBIG2_GETDWORD(sp);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskR) | (tmp1 & maskR);
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-        }
-        lineSrc += m_nStride;
-        lineDst += pDst->m_nStride;
-      }
-    } else {
-      uint32_t shift1 = d1 - s1;
-      uint32_t shift2 = 32 - shift1;
-      int32_t middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5);
-      for (int32_t yy = yd0; yy < yd1; yy++) {
-        uint8_t* sp = lineSrc;
-        uint8_t* dp = lineDst;
-        if (d1 != 0) {
-          uint32_t tmp1 = JBIG2_GETDWORD(sp) >> shift1;
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskL) | (tmp1 & maskL);
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-          dp += 4;
-        }
-        for (int32_t xx = 0; xx < middleDwords; xx++) {
-          uint32_t tmp1 = (JBIG2_GETDWORD(sp) << shift2) |
-                          ((JBIG2_GETDWORD(sp + 4)) >> shift1);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = tmp1 | tmp2;
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = tmp1 & tmp2;
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = tmp1 ^ tmp2;
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = ~(tmp1 ^ tmp2);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = tmp1;
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
-          sp += 4;
-          dp += 4;
-        }
-        if (d2 != 0) {
-          uint32_t tmp1 =
-              (JBIG2_GETDWORD(sp) << shift2) |
-              (((sp + 4) < lineSrc + lineLeft ? JBIG2_GETDWORD(sp + 4) : 0) >>
-               shift1);
-          uint32_t tmp2 = JBIG2_GETDWORD(dp);
-          uint32_t tmp = 0;
-          switch (op) {
-            case JBIG2_COMPOSE_OR:
-              tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_AND:
-              tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_XOR:
-              tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR);
-              break;
-            case JBIG2_COMPOSE_XNOR:
-              tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR);
-              break;
-            case JBIG2_COMPOSE_REPLACE:
-              tmp = (tmp2 & ~maskR) | (tmp1 & maskR);
-              break;
-          }
-          dp[0] = (uint8_t)(tmp >> 24);
-          dp[1] = (uint8_t)(tmp >> 16);
-          dp[2] = (uint8_t)(tmp >> 8);
-          dp[3] = (uint8_t)tmp;
+          JBIG2_PUTDWORD(dp, tmp);
         }
         lineSrc += m_nStride;
         lineDst += pDst->m_nStride;
diff --git a/core/fxcodec/jbig2/JBig2_Image.h b/core/fxcodec/jbig2/JBig2_Image.h
index c098040..2be4ad3 100644
--- a/core/fxcodec/jbig2/JBig2_Image.h
+++ b/core/fxcodec/jbig2/JBig2_Image.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "core/fxcodec/jbig2/JBig2_Define.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/maybe_owned.h"
 
 struct FX_RECT;
@@ -26,56 +27,69 @@
  public:
   CJBig2_Image(int32_t w, int32_t h);
   CJBig2_Image(int32_t w, int32_t h, int32_t stride, uint8_t* pBuf);
-  CJBig2_Image(const CJBig2_Image& im);
+  CJBig2_Image(const CJBig2_Image& other);
   ~CJBig2_Image();
 
+  static bool IsValidImageSize(int32_t w, int32_t h);
+
   int32_t width() const { return m_nWidth; }
   int32_t height() const { return m_nHeight; }
   int32_t stride() const { return m_nStride; }
+
   uint8_t* data() const { return m_pData.Get(); }
 
-  int getPixel(int32_t x, int32_t y) const;
-  int32_t setPixel(int32_t x, int32_t y, int bVal);
+  int GetPixel(int32_t x, int32_t y) const;
+  void SetPixel(int32_t x, int32_t y, int v);
 
-  void copyLine(int32_t hTo, int32_t hFrom);
-  void fill(bool v);
+  uint8_t* GetLineUnsafe(int32_t y) const { return data() + y * m_nStride; }
+  uint8_t* GetLine(int32_t y) const {
+    return (y >= 0 && y < m_nHeight) ? GetLineUnsafe(y) : nullptr;
+  }
 
-  bool composeTo(CJBig2_Image* pDst, int32_t x, int32_t y, JBig2ComposeOp op);
-  bool composeTo(CJBig2_Image* pDst,
-                 int32_t x,
-                 int32_t y,
-                 JBig2ComposeOp op,
-                 const FX_RECT* pSrcRect);
+  void CopyLine(int32_t hTo, int32_t hFrom);
+  void Fill(bool v);
 
-  bool composeTo_opt2(CJBig2_Image* pDst,
-                      int32_t x,
-                      int32_t y,
-                      JBig2ComposeOp op);
-  bool composeTo_opt2(CJBig2_Image* pDst,
-                      int32_t x,
-                      int32_t y,
-                      JBig2ComposeOp op,
-                      const FX_RECT* pSrcRect);
+  bool ComposeFrom(int32_t x, int32_t y, CJBig2_Image* pSrc, JBig2ComposeOp op);
+  bool ComposeFromWithRect(int32_t x,
+                           int32_t y,
+                           CJBig2_Image* pSrc,
+                           const FX_RECT& rtSrc,
+                           JBig2ComposeOp op);
 
-  bool composeFrom(int32_t x, int32_t y, CJBig2_Image* pSrc, JBig2ComposeOp op);
-  bool composeFrom(int32_t x,
-                   int32_t y,
-                   CJBig2_Image* pSrc,
-                   JBig2ComposeOp op,
-                   const FX_RECT* pSrcRect);
-
-  std::unique_ptr<CJBig2_Image> subImage(int32_t x,
+  std::unique_ptr<CJBig2_Image> SubImage(int32_t x,
                                          int32_t y,
                                          int32_t w,
                                          int32_t h);
-  void expand(int32_t h, bool v);
+  void Expand(int32_t h, bool v);
 
+  bool ComposeTo(CJBig2_Image* pDst, int32_t x, int32_t y, JBig2ComposeOp op);
+  bool ComposeToWithRect(CJBig2_Image* pDst,
+                         int32_t x,
+                         int32_t y,
+                         const FX_RECT& rtSrc,
+                         JBig2ComposeOp op);
 
  private:
+  void SubImageFast(int32_t x,
+                    int32_t y,
+                    int32_t w,
+                    int32_t h,
+                    CJBig2_Image* pImage);
+  void SubImageSlow(int32_t x,
+                    int32_t y,
+                    int32_t w,
+                    int32_t h,
+                    CJBig2_Image* pImage);
+  bool ComposeToInternal(CJBig2_Image* pDst,
+                         int32_t x,
+                         int32_t y,
+                         JBig2ComposeOp op,
+                         const FX_RECT& rtSrc);
+
   MaybeOwned<uint8_t, FxFreeDeleter> m_pData;
-  int32_t m_nWidth;   // 1-bit pixels
-  int32_t m_nHeight;  // lines
-  int32_t m_nStride;  // bytes
+  int32_t m_nWidth = 0;   // 1-bit pixels
+  int32_t m_nHeight = 0;  // lines
+  int32_t m_nStride = 0;  // bytes, must be multiple of 4.
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_IMAGE_H_
diff --git a/core/fxcodec/jbig2/JBig2_Image_unittest.cpp b/core/fxcodec/jbig2/JBig2_Image_unittest.cpp
index 2e53e7b..13e6eb5 100644
--- a/core/fxcodec/jbig2/JBig2_Image_unittest.cpp
+++ b/core/fxcodec/jbig2/JBig2_Image_unittest.cpp
@@ -8,26 +8,75 @@
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
 const int32_t kWidthPixels = 80;
 const int32_t kWidthBytes = 10;
-const int32_t kStrideBytes = kWidthBytes + 1;  // For testing stride != width.
+const int32_t kStrideBytes = kWidthBytes + 2;  // For testing stride != width.
 const int32_t kHeightLines = 20;
 const int32_t kLargerHeightLines = 100;
 const int32_t kTooLargeHeightLines = 40000000;
 
+void CheckImageEq(CJBig2_Image* img1, CJBig2_Image* img2, int line) {
+  EXPECT_EQ(img1->width(), img2->width());
+  EXPECT_EQ(img1->height(), img2->height());
+  for (int32_t y = 0; y < img1->height(); ++y) {
+    for (int32_t x = 0; x < img1->width(); ++x) {
+      EXPECT_EQ(img1->GetPixel(x, y), img2->GetPixel(x, y))
+          << " at " << x << " " << y << " actual line " << line;
+    }
+  }
+}
+
 }  // namespace
 
+TEST(fxcodec, EmptyImage) {
+  CJBig2_Image empty(0, 0);
+  EXPECT_EQ(empty.width(), 0);
+  EXPECT_EQ(empty.height(), 0);
+
+  // Out-of-bounds SetPixel() is silent no-op.
+  empty.SetPixel(0, 0, true);
+  empty.SetPixel(1, 1, true);
+
+  // Out-of-bounds GetPixel returns 0.
+  EXPECT_EQ(empty.GetPixel(0, 0), 0);
+  EXPECT_EQ(empty.GetPixel(1, 1), 0);
+
+  // Out-of-bounds GetLine() returns null.
+  EXPECT_EQ(empty.GetLine(0), nullptr);
+  EXPECT_EQ(empty.GetLine(1), nullptr);
+}
+
 TEST(fxcodec, JBig2ImageCreate) {
   CJBig2_Image img(kWidthPixels, kHeightLines);
-  img.setPixel(0, 0, true);
-  img.setPixel(kWidthPixels - 1, kHeightLines - 1, false);
   EXPECT_EQ(kWidthPixels, img.width());
   EXPECT_EQ(kHeightLines, img.height());
-  EXPECT_TRUE(img.getPixel(0, 0));
-  EXPECT_FALSE(img.getPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_EQ(0, img.GetPixel(0, 0));
+  EXPECT_EQ(0, img.GetLine(0)[0]);
+  EXPECT_EQ(0, img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_EQ(0, img.GetLine(kHeightLines - 1)[kWidthBytes - 1]);
+
+  img.SetPixel(0, 0, true);
+  img.SetPixel(kWidthPixels - 1, kHeightLines - 1, true);
+  EXPECT_EQ(1, img.GetPixel(0, 0));
+  EXPECT_EQ(1, img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_EQ(0x80, img.GetLine(0)[0]);
+  EXPECT_EQ(0x01, img.GetLine(kHeightLines - 1)[kWidthBytes - 1]);
+
+  // Out-of-bounds SetPixel() is silent no-op.
+  img.SetPixel(-1, 1, true);
+  img.SetPixel(kWidthPixels, kHeightLines, true);
+
+  // Out-of-bounds GetPixel returns 0.
+  EXPECT_EQ(0, img.GetPixel(-1, -1));
+  EXPECT_EQ(0, img.GetPixel(kWidthPixels, kHeightLines));
+
+  // Out-of-bounds GetLine() returns null.
+  EXPECT_EQ(nullptr, img.GetLine(-1));
+  EXPECT_EQ(nullptr, img.GetLine(kHeightLines));
 }
 
 TEST(fxcodec, JBig2ImageCreateTooBig) {
@@ -40,12 +89,12 @@
 TEST(fxcodec, JBig2ImageCreateExternal) {
   uint8_t buf[kHeightLines * kStrideBytes];
   CJBig2_Image img(kWidthPixels, kHeightLines, kStrideBytes, buf);
-  img.setPixel(0, 0, true);
-  img.setPixel(kWidthPixels - 1, kHeightLines - 1, false);
+  img.SetPixel(0, 0, true);
+  img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
   EXPECT_EQ(kWidthPixels, img.width());
   EXPECT_EQ(kHeightLines, img.height());
-  EXPECT_TRUE(img.getPixel(0, 0));
-  EXPECT_FALSE(img.getPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_TRUE(img.GetPixel(0, 0));
+  EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
 }
 
 TEST(fxcodec, JBig2ImageCreateExternalTooBig) {
@@ -56,50 +105,248 @@
   EXPECT_EQ(nullptr, img.data());
 }
 
+TEST(fxcodec, JBig2ImageCreateExternalBadStride) {
+  uint8_t buf[kHeightLines * kStrideBytes];
+  CJBig2_Image img(kWidthPixels, kTooLargeHeightLines, kStrideBytes - 1, buf);
+  EXPECT_EQ(0, img.width());
+  EXPECT_EQ(0, img.height());
+  EXPECT_EQ(nullptr, img.data());
+}
+
 TEST(fxcodec, JBig2ImageExpand) {
   CJBig2_Image img(kWidthPixels, kHeightLines);
-  img.setPixel(0, 0, true);
-  img.setPixel(kWidthPixels - 1, kHeightLines - 1, false);
-  img.expand(kLargerHeightLines, true);
+  img.SetPixel(0, 0, true);
+  img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
+  img.Expand(kLargerHeightLines, true);
   EXPECT_EQ(kWidthPixels, img.width());
   EXPECT_EQ(kLargerHeightLines, img.height());
-  EXPECT_TRUE(img.getPixel(0, 0));
-  EXPECT_FALSE(img.getPixel(kWidthPixels - 1, kHeightLines - 1));
-  EXPECT_TRUE(img.getPixel(kWidthPixels - 1, kLargerHeightLines - 1));
+  EXPECT_TRUE(img.GetPixel(0, 0));
+  EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_TRUE(img.GetPixel(kWidthPixels - 1, kLargerHeightLines - 1));
 }
 
 TEST(fxcodec, JBig2ImageExpandTooBig) {
   CJBig2_Image img(kWidthPixels, kHeightLines);
-  img.setPixel(0, 0, true);
-  img.setPixel(kWidthPixels - 1, kHeightLines - 1, false);
-  img.expand(kTooLargeHeightLines, true);
+  img.SetPixel(0, 0, true);
+  img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
+  img.Expand(kTooLargeHeightLines, true);
   EXPECT_EQ(kWidthPixels, img.width());
   EXPECT_EQ(kHeightLines, img.height());
-  EXPECT_TRUE(img.getPixel(0, 0));
-  EXPECT_FALSE(img.getPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_TRUE(img.GetPixel(0, 0));
+  EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
 }
 
 TEST(fxcodec, JBig2ImageExpandExternal) {
   uint8_t buf[kHeightLines * kStrideBytes];
   CJBig2_Image img(kWidthPixels, kHeightLines, kStrideBytes, buf);
-  img.setPixel(0, 0, true);
-  img.setPixel(kWidthPixels - 1, kHeightLines - 1, false);
-  img.expand(kLargerHeightLines, true);
+  img.SetPixel(0, 0, true);
+  img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
+  img.Expand(kLargerHeightLines, true);
   EXPECT_EQ(kWidthPixels, img.width());
   EXPECT_EQ(kLargerHeightLines, img.height());
-  EXPECT_TRUE(img.getPixel(0, 0));
-  EXPECT_FALSE(img.getPixel(kWidthPixels - 1, kHeightLines - 1));
-  EXPECT_TRUE(img.getPixel(kWidthPixels - 1, kLargerHeightLines - 1));
+  EXPECT_TRUE(img.GetPixel(0, 0));
+  EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_TRUE(img.GetPixel(kWidthPixels - 1, kLargerHeightLines - 1));
 }
 
 TEST(fxcodec, JBig2ImageExpandExternalTooBig) {
   uint8_t buf[kHeightLines * kStrideBytes];
   CJBig2_Image img(kWidthPixels, kHeightLines, kStrideBytes, buf);
-  img.setPixel(0, 0, true);
-  img.setPixel(kWidthPixels - 1, kHeightLines - 1, false);
-  img.expand(kTooLargeHeightLines, true);
+  img.SetPixel(0, 0, true);
+  img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
+  img.Expand(kTooLargeHeightLines, true);
   EXPECT_EQ(kWidthPixels, img.width());
   EXPECT_EQ(kHeightLines, img.height());
-  EXPECT_TRUE(img.getPixel(0, 0));
-  EXPECT_FALSE(img.getPixel(kWidthPixels - 1, kHeightLines - 1));
+  EXPECT_TRUE(img.GetPixel(0, 0));
+  EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
+}
+
+TEST(fxcodec, JBig2EmptyImage) {
+  auto empty = pdfium::MakeUnique<CJBig2_Image>(0, 0);
+
+  // Empty subimage.
+  auto sub1 = empty->SubImage(0, 0, 0, 0);
+  EXPECT_EQ(sub1->width(), 0);
+  EXPECT_EQ(sub1->height(), 0);
+
+  // Larger dimensions are zero-padded.
+  auto sub2 = empty->SubImage(0, 0, 1, 1);
+  EXPECT_EQ(1, sub2->width());
+  EXPECT_EQ(1, sub2->height());
+  EXPECT_EQ(0, sub2->GetPixel(0, 0));
+
+  // Bad dimensions give an empty image.
+  sub2 = empty->SubImage(0, 0, -1, -1);
+  EXPECT_EQ(sub2->width(), 0);
+  EXPECT_EQ(sub2->height(), 0);
+
+  // Bad offsets zero pad the image.
+  auto sub3 = empty->SubImage(-1, -1, 2, 2);
+  EXPECT_EQ(sub3->width(), 2);
+  EXPECT_EQ(sub3->height(), 2);
+
+  // Bad dimensions and bad offsets give an empty image.
+  sub3 = empty->SubImage(-1, -1, -100, -100);
+  EXPECT_EQ(sub3->width(), 0);
+  EXPECT_EQ(sub3->height(), 0);
+}
+
+TEST(fxcodec, JBig2SubImage) {
+  // 1-px wide rectangle in image.
+  uint8_t pattern[5][8] = {
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  };
+
+  // 1-px wide rectangle in image, offset 2 in x.
+  uint8_t pattern20[5][8] = {
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  };
+
+  // 1-px wide rectangle in image, offset 2 in x and y, padded.
+  uint8_t pattern22[5][8] = {
+      {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  };
+
+  // 1-px wide rectangle in image, offset 16 in x, 1 in y, padded.
+  uint8_t pattern161[5][8] = {
+      {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  };
+
+  // Image size a nice clean power of two.
+  auto img32 = pdfium::MakeUnique<CJBig2_Image>(
+      32, 5, 8, reinterpret_cast<uint8_t*>(pattern));
+
+  // Image size not a nice clean value.
+  auto img37 = pdfium::MakeUnique<CJBig2_Image>(
+      37, 5, 8, reinterpret_cast<uint8_t*>(pattern));
+
+  // Expected results to check against.
+  auto expected20 = pdfium::MakeUnique<CJBig2_Image>(
+      30, 5, 8, reinterpret_cast<uint8_t*>(pattern20));
+
+  auto expected22 = pdfium::MakeUnique<CJBig2_Image>(
+      30, 5, 8, reinterpret_cast<uint8_t*>(pattern22));
+
+  auto expected161 = pdfium::MakeUnique<CJBig2_Image>(
+      25, 5, 8, reinterpret_cast<uint8_t*>(pattern161));
+
+  auto expected_zeros = pdfium::MakeUnique<CJBig2_Image>(32, 5);
+
+  // Empty subimage.
+  auto sub = img32->SubImage(0, 0, 0, 0);
+  EXPECT_EQ(sub->width(), 0);
+  EXPECT_EQ(sub->height(), 0);
+
+  // Full sub-image.
+  sub = img32->SubImage(0, 0, 32, 5);
+  EXPECT_EQ(sub->width(), 32);
+  EXPECT_EQ(sub->height(), 5);
+  CheckImageEq(img32.get(), sub.get(), __LINE__);
+
+  sub = img37->SubImage(0, 0, 32, 5);
+  EXPECT_EQ(sub->width(), 32);
+  EXPECT_EQ(sub->height(), 5);
+  CheckImageEq(img32.get(), sub.get(), __LINE__);
+
+  // Actual bit manipulations.
+  sub = img32->SubImage(2, 0, 30, 5);
+  CheckImageEq(expected20.get(), sub.get(), __LINE__);
+
+  sub = img37->SubImage(2, 2, 30, 5);
+  CheckImageEq(expected22.get(), sub.get(), __LINE__);
+
+  // Fast path.
+  sub = img37->SubImage(16, 1, 25, 5);
+  CheckImageEq(expected161.get(), sub.get(), __LINE__);
+
+  // Aligned Sub-image including cruft in stride beyond width.
+  sub = img37->SubImage(32, 0, 32, 5);
+  CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
+
+  // Sub-image waaaaay beyond width.
+  sub = img37->SubImage(2000, 0, 32, 5);
+  CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
+
+  // Sub-image waaaaay beyond height.
+  sub = img37->SubImage(0, 2000, 32, 5);
+  CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
+
+  // Sub-image with negative x offset.
+  sub = img37->SubImage(-1, 0, 32, 5);
+  CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
+
+  // Sub-image with negative y offset.
+  sub = img37->SubImage(0, -1, 32, 5);
+  CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
+
+  // Sub-image with negative width.
+  sub = img37->SubImage(-1, 0, 32, 5);
+  CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
+
+  // Sub-image with negative height.
+  sub = img37->SubImage(0, -1, 32, 5);
+  CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
+
+  // Sub-image wider than original.
+  sub = img37->SubImage(0, 0, 128, 5);
+  EXPECT_EQ(128, sub->width());
+  EXPECT_EQ(5, sub->height());
+
+  // Sub-image higher than original.
+  sub = img37->SubImage(0, 0, 32, 40);
+  EXPECT_EQ(32, sub->width());
+  EXPECT_EQ(40, sub->height());
+}
+
+TEST(fxcodec, JBig2CopyLine) {
+  // Horizontal line in image.
+  uint8_t pattern[3][8] = {
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  };
+
+  uint8_t expected_pattern[3][8] = {
+      {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+  };
+
+  auto img = pdfium::MakeUnique<CJBig2_Image>(
+      37, 3, 8, reinterpret_cast<uint8_t*>(pattern));
+
+  auto expected = pdfium::MakeUnique<CJBig2_Image>(
+      37, 3, 8, reinterpret_cast<uint8_t*>(expected_pattern));
+
+  // Shuffle.
+  img->CopyLine(2, 1);
+  img->CopyLine(1, 0);
+  img->CopyLine(0, 2);
+
+  // Clear top line via invalid |from| offset.
+  img->CopyLine(2, 3);
+
+  // Copies with invalid |to|s don't mess with things.
+  img->CopyLine(-1, 0);
+  img->CopyLine(4, 0);
+  img->CopyLine(-1, -1);
+  img->CopyLine(4, 4);
+
+  CheckImageEq(expected.get(), img.get(), __LINE__);
 }
diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.cpp b/core/fxcodec/jbig2/JBig2_PatternDict.cpp
index 450e78a..4e7959f 100644
--- a/core/fxcodec/jbig2/JBig2_PatternDict.cpp
+++ b/core/fxcodec/jbig2/JBig2_PatternDict.cpp
@@ -6,8 +6,6 @@
 
 #include "core/fxcodec/jbig2/JBig2_PatternDict.h"
 
-#include "core/fxcrt/fx_memory.h"
-
 CJBig2_PatternDict::CJBig2_PatternDict(uint32_t dict_size)
     : NUMPATS(dict_size), HDPATS(dict_size) {}
 
diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.h b/core/fxcodec/jbig2/JBig2_PatternDict.h
index ad55917..ca566d0 100644
--- a/core/fxcodec/jbig2/JBig2_PatternDict.h
+++ b/core/fxcodec/jbig2/JBig2_PatternDict.h
@@ -16,7 +16,6 @@
 class CJBig2_PatternDict {
  public:
   explicit CJBig2_PatternDict(uint32_t dict_size);
-
   ~CJBig2_PatternDict();
 
   uint32_t NUMPATS;
diff --git a/core/fxcodec/jbig2/JBig2_PddProc.cpp b/core/fxcodec/jbig2/JBig2_PddProc.cpp
index ee891cc..9d274f9 100644
--- a/core/fxcodec/jbig2/JBig2_PddProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_PddProc.cpp
@@ -13,22 +13,18 @@
 #include "core/fxcodec/jbig2/JBig2_PatternDict.h"
 #include "third_party/base/ptr_util.h"
 
-std::unique_ptr<CJBig2_PatternDict> CJBig2_PDDProc::decode_Arith(
+std::unique_ptr<CJBig2_PatternDict> CJBig2_PDDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext,
-    IFX_PauseIndicator* pPause) {
-  uint32_t GRAY;
-  std::unique_ptr<CJBig2_Image> BHDC;
-  auto pDict = pdfium::MakeUnique<CJBig2_PatternDict>(GRAYMAX + 1);
+    PauseIndicatorIface* pPause) {
+  std::unique_ptr<CJBig2_GRDProc> pGRD = CreateGRDProc();
+  if (!pGRD)
+    return nullptr;
 
-  auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
-  pGRD->MMR = HDMMR;
-  pGRD->GBW = (GRAYMAX + 1) * HDPW;
-  pGRD->GBH = HDPH;
   pGRD->GBTEMPLATE = HDTEMPLATE;
   pGRD->TPGDON = 0;
   pGRD->USESKIP = 0;
-  pGRD->GBAT[0] = -(int32_t)HDPW;
+  pGRD->GBAT[0] = -1 * static_cast<int32_t>(HDPW);
   pGRD->GBAT[1] = 0;
   if (pGRD->GBTEMPLATE == 0) {
     pGRD->GBAT[2] = -3;
@@ -38,39 +34,53 @@
     pGRD->GBAT[6] = -2;
     pGRD->GBAT[7] = -2;
   }
-  FXCODEC_STATUS status =
-      pGRD->Start_decode_Arith(&BHDC, pArithDecoder, gbContext, nullptr);
+
+  std::unique_ptr<CJBig2_Image> BHDC;
+  CJBig2_GRDProc::ProgressiveArithDecodeState state;
+  state.pImage = &BHDC;
+  state.pArithDecoder = pArithDecoder;
+  state.gbContext = gbContext;
+  state.pPause = nullptr;
+
+  FXCODEC_STATUS status = pGRD->StartDecodeArith(&state);
+  state.pPause = pPause;
   while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    status = pGRD->Continue_decode(pPause, pArithDecoder);
+    status = pGRD->ContinueDecode(&state);
   if (!BHDC)
     return nullptr;
 
-  GRAY = 0;
-  while (GRAY <= GRAYMAX) {
-    pDict->HDPATS[GRAY] = BHDC->subImage(HDPW * GRAY, 0, HDPW, HDPH);
-    GRAY = GRAY + 1;
-  }
+  auto pDict = pdfium::MakeUnique<CJBig2_PatternDict>(GRAYMAX + 1);
+  for (uint32_t GRAY = 0; GRAY <= GRAYMAX; ++GRAY)
+    pDict->HDPATS[GRAY] = BHDC->SubImage(HDPW * GRAY, 0, HDPW, HDPH);
   return pDict;
 }
 
-std::unique_ptr<CJBig2_PatternDict> CJBig2_PDDProc::decode_MMR(
+std::unique_ptr<CJBig2_PatternDict> CJBig2_PDDProc::DecodeMMR(
     CJBig2_BitStream* pStream) {
-  uint32_t GRAY;
-  std::unique_ptr<CJBig2_Image> BHDC;
-  auto pDict = pdfium::MakeUnique<CJBig2_PatternDict>(GRAYMAX + 1);
+  std::unique_ptr<CJBig2_GRDProc> pGRD = CreateGRDProc();
+  if (!pGRD)
+    return nullptr;
 
-  auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
-  pGRD->MMR = HDMMR;
-  pGRD->GBW = (GRAYMAX + 1) * HDPW;
-  pGRD->GBH = HDPH;
-  pGRD->Start_decode_MMR(&BHDC, pStream);
+  std::unique_ptr<CJBig2_Image> BHDC;
+  pGRD->StartDecodeMMR(&BHDC, pStream);
   if (!BHDC)
     return nullptr;
 
-  GRAY = 0;
-  while (GRAY <= GRAYMAX) {
-    pDict->HDPATS[GRAY] = BHDC->subImage(HDPW * GRAY, 0, HDPW, HDPH);
-    GRAY = GRAY + 1;
-  }
+  auto pDict = pdfium::MakeUnique<CJBig2_PatternDict>(GRAYMAX + 1);
+  for (uint32_t GRAY = 0; GRAY <= GRAYMAX; ++GRAY)
+    pDict->HDPATS[GRAY] = BHDC->SubImage(HDPW * GRAY, 0, HDPW, HDPH);
   return pDict;
 }
+
+std::unique_ptr<CJBig2_GRDProc> CJBig2_PDDProc::CreateGRDProc() {
+  uint32_t width = (GRAYMAX + 1) * HDPW;
+  uint32_t height = HDPH;
+  if (width > JBIG2_MAX_IMAGE_SIZE || height > JBIG2_MAX_IMAGE_SIZE)
+    return nullptr;
+
+  auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
+  pGRD->MMR = HDMMR;
+  pGRD->GBW = width;
+  pGRD->GBH = height;
+  return pGRD;
+}
diff --git a/core/fxcodec/jbig2/JBig2_PddProc.h b/core/fxcodec/jbig2/JBig2_PddProc.h
index 938da1b..be5fadf 100644
--- a/core/fxcodec/jbig2/JBig2_PddProc.h
+++ b/core/fxcodec/jbig2/JBig2_PddProc.h
@@ -13,25 +13,28 @@
 
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
+class CJBig2_GRDProc;
 class CJBig2_PatternDict;
-class IFX_PauseIndicator;
-struct JBig2ArithCtx;
+class JBig2ArithCtx;
+class PauseIndicatorIface;
 
 class CJBig2_PDDProc {
  public:
-  std::unique_ptr<CJBig2_PatternDict> decode_Arith(
+  std::unique_ptr<CJBig2_PatternDict> DecodeArith(
       CJBig2_ArithDecoder* pArithDecoder,
       JBig2ArithCtx* gbContext,
-      IFX_PauseIndicator* pPause);
+      PauseIndicatorIface* pPause);
 
-  std::unique_ptr<CJBig2_PatternDict> decode_MMR(CJBig2_BitStream* pStream);
+  std::unique_ptr<CJBig2_PatternDict> DecodeMMR(CJBig2_BitStream* pStream);
 
- public:
   bool HDMMR;
   uint8_t HDPW;
   uint8_t HDPH;
   uint32_t GRAYMAX;
   uint8_t HDTEMPLATE;
+
+ private:
+  std::unique_ptr<CJBig2_GRDProc> CreateGRDProc();
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_
diff --git a/core/fxcodec/jbig2/JBig2_SddProc.cpp b/core/fxcodec/jbig2/JBig2_SddProc.cpp
index a446dbc..6fd1226 100644
--- a/core/fxcodec/jbig2/JBig2_SddProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_SddProc.cpp
@@ -6,6 +6,8 @@
 
 #include "core/fxcodec/jbig2/JBig2_SddProc.h"
 
+#include <string.h>
+
 #include <algorithm>
 #include <memory>
 #include <utility>
@@ -16,12 +18,16 @@
 #include "core/fxcodec/jbig2/JBig2_GrrdProc.h"
 #include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h"
 #include "core/fxcodec/jbig2/JBig2_HuffmanTable.h"
-#include "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h"
 #include "core/fxcodec/jbig2/JBig2_SymbolDict.h"
 #include "core/fxcodec/jbig2/JBig2_TrdProc.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/ptr_util.h"
 
-std::unique_ptr<CJBig2_SymbolDict> CJBig2_SDDProc::decode_Arith(
+CJBig2_SDDProc::CJBig2_SDDProc() = default;
+
+CJBig2_SDDProc::~CJBig2_SDDProc() = default;
+
+std::unique_ptr<CJBig2_SymbolDict> CJBig2_SDDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
     std::vector<JBig2ArithCtx>* gbContext,
     std::vector<JBig2ArithCtx>* grContext) {
@@ -68,7 +74,7 @@
   NSYMSDECODED = 0;
   while (NSYMSDECODED < SDNUMNEWSYMS) {
     std::unique_ptr<CJBig2_Image> BS;
-    IADH->decode(pArithDecoder, &HCDH);
+    IADH->Decode(pArithDecoder, &HCDH);
     HCHEIGHT = HCHEIGHT + HCDH;
     if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > JBIG2_MAX_IMAGE_SIZE)
       return nullptr;
@@ -76,7 +82,7 @@
     SYMWIDTH = 0;
     TOTWIDTH = 0;
     for (;;) {
-      if (!IADW->decode(pArithDecoder, &DW))
+      if (!IADW->Decode(pArithDecoder, &DW))
         break;
 
       if (NSYMSDECODED >= SDNUMNEWSYMS)
@@ -109,12 +115,21 @@
         pGRD->GBAT[5] = SDAT[5];
         pGRD->GBAT[6] = SDAT[6];
         pGRD->GBAT[7] = SDAT[7];
-        BS = pGRD->decode_Arith(pArithDecoder, gbContext->data());
+        BS = pGRD->DecodeArith(pArithDecoder, gbContext->data());
         if (!BS)
           return nullptr;
       } else {
-        IAAI->decode(pArithDecoder, (int*)&REFAGGNINST);
+        IAAI->Decode(pArithDecoder, (int*)&REFAGGNINST);
         if (REFAGGNINST > 1) {
+          // Huffman tables must not outlive |pDecoder|.
+          auto SBHUFFFS = pdfium::MakeUnique<CJBig2_HuffmanTable>(6);
+          auto SBHUFFDS = pdfium::MakeUnique<CJBig2_HuffmanTable>(8);
+          auto SBHUFFDT = pdfium::MakeUnique<CJBig2_HuffmanTable>(11);
+          auto SBHUFFRDW = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDH = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDY = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
           auto pDecoder = pdfium::MakeUnique<CJBig2_TRDProc>();
           pDecoder->SBHUFF = SDHUFF;
           pDecoder->SBREFINE = 1;
@@ -140,22 +155,6 @@
           pDecoder->TRANSPOSED = 0;
           pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT;
           pDecoder->SBDSOFFSET = 0;
-          auto SBHUFFFS = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B6, HuffmanTable_B6_Size, HuffmanTable_HTOOB_B6);
-          auto SBHUFFDS = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B8, HuffmanTable_B8_Size, HuffmanTable_HTOOB_B8);
-          auto SBHUFFDT = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B11, HuffmanTable_B11_Size, HuffmanTable_HTOOB_B11);
-          auto SBHUFFRDW = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRDH = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRDY = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B1, HuffmanTable_B1_Size, HuffmanTable_HTOOB_B1);
           pDecoder->SBHUFFFS = SBHUFFFS.get();
           pDecoder->SBHUFFDS = SBHUFFDS.get();
           pDecoder->SBHUFFDT = SBHUFFDT.get();
@@ -180,15 +179,15 @@
           ids.IARDX = IARDX.get();
           ids.IARDY = IARDY.get();
           ids.IAID = IAID.get();
-          BS = pDecoder->decode_Arith(pArithDecoder, grContext->data(), &ids);
+          BS = pDecoder->DecodeArith(pArithDecoder, grContext->data(), &ids);
           if (!BS)
             return nullptr;
         } else if (REFAGGNINST == 1) {
           SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED;
           uint32_t IDI;
-          IAID->decode(pArithDecoder, &IDI);
-          IARDX->decode(pArithDecoder, &RDXI);
-          IARDY->decode(pArithDecoder, &RDYI);
+          IAID->Decode(pArithDecoder, &IDI);
+          IARDX->Decode(pArithDecoder, &RDXI);
+          IARDY->Decode(pArithDecoder, &RDYI);
           if (IDI >= SBNUMSYMS)
             return nullptr;
 
@@ -211,7 +210,7 @@
           pGRRD->GRAT[1] = SDRAT[1];
           pGRRD->GRAT[2] = SDRAT[2];
           pGRRD->GRAT[3] = SDRAT[3];
-          BS = pGRRD->decode(pArithDecoder, grContext->data());
+          BS = pGRRD->Decode(pArithDecoder, grContext->data());
           if (!BS)
             return nullptr;
         }
@@ -225,7 +224,7 @@
   EXFLAGS.resize(SDNUMINSYMS + SDNUMNEWSYMS);
   num_ex_syms = 0;
   while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) {
-    IAEX->decode(pArithDecoder, (int*)&EXRUNLENGTH);
+    IAEX->Decode(pArithDecoder, (int*)&EXRUNLENGTH);
     if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS)
       return nullptr;
 
@@ -243,7 +242,7 @@
     return nullptr;
 
   pDict = pdfium::MakeUnique<CJBig2_SymbolDict>();
-  I = J = 0;
+  J = 0;
   for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; I++) {
     if (!EXFLAGS[I] || J >= SDNUMEXSYMS)
       continue;
@@ -259,7 +258,7 @@
   return pDict;
 }
 
-std::unique_ptr<CJBig2_SymbolDict> CJBig2_SDDProc::decode_Huffman(
+std::unique_ptr<CJBig2_SymbolDict> CJBig2_SDDProc::DecodeHuffman(
     CJBig2_BitStream* pStream,
     std::vector<JBig2ArithCtx>* gbContext,
     std::vector<JBig2ArithCtx>* grContext) {
@@ -281,7 +280,6 @@
   uint32_t IDI;
   int32_t RDXI, RDYI;
   uint32_t BMSIZE;
-  uint32_t stride;
   uint32_t num_ex_syms;
   // Pointers are not owned
   std::vector<CJBig2_Image*> SBSYMS;
@@ -296,7 +294,7 @@
   NSYMSDECODED = 0;
   std::unique_ptr<CJBig2_Image> BS;
   while (NSYMSDECODED < SDNUMNEWSYMS) {
-    if (pHuffmanDecoder->decodeAValue(SDHUFFDH, &HCDH) != 0)
+    if (pHuffmanDecoder->DecodeAValue(SDHUFFDH.Get(), &HCDH) != 0)
       return nullptr;
 
     HCHEIGHT = HCHEIGHT + HCDH;
@@ -307,7 +305,7 @@
     TOTWIDTH = 0;
     HCFIRSTSYM = NSYMSDECODED;
     for (;;) {
-      nVal = pHuffmanDecoder->decodeAValue(SDHUFFDW, &DW);
+      nVal = pHuffmanDecoder->DecodeAValue(SDHUFFDW.Get(), &DW);
       if (nVal == JBIG2_OOB)
         break;
       if (nVal != 0)
@@ -326,12 +324,21 @@
       }
       TOTWIDTH = TOTWIDTH + SYMWIDTH;
       if (SDREFAGG == 1) {
-        if (pHuffmanDecoder->decodeAValue(SDHUFFAGGINST, (int*)&REFAGGNINST) !=
-            0) {
+        if (pHuffmanDecoder->DecodeAValue(SDHUFFAGGINST.Get(),
+                                          (int*)&REFAGGNINST) != 0) {
           return nullptr;
         }
         BS = nullptr;
         if (REFAGGNINST > 1) {
+          // Huffman tables must outlive |pDecoder|.
+          auto SBHUFFFS = pdfium::MakeUnique<CJBig2_HuffmanTable>(6);
+          auto SBHUFFDS = pdfium::MakeUnique<CJBig2_HuffmanTable>(8);
+          auto SBHUFFDT = pdfium::MakeUnique<CJBig2_HuffmanTable>(11);
+          auto SBHUFFRDW = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDH = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRDY = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
           auto pDecoder = pdfium::MakeUnique<CJBig2_TRDProc>();
           pDecoder->SBHUFF = SDHUFF;
           pDecoder->SBREFINE = 1;
@@ -360,22 +367,6 @@
           pDecoder->TRANSPOSED = 0;
           pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT;
           pDecoder->SBDSOFFSET = 0;
-          auto SBHUFFFS = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B6, HuffmanTable_B6_Size, HuffmanTable_HTOOB_B6);
-          auto SBHUFFDS = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B8, HuffmanTable_B8_Size, HuffmanTable_HTOOB_B8);
-          auto SBHUFFDT = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B11, HuffmanTable_B11_Size, HuffmanTable_HTOOB_B11);
-          auto SBHUFFRDW = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRDH = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRDY = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B1, HuffmanTable_B1_Size, HuffmanTable_HTOOB_B1);
           pDecoder->SBHUFFFS = SBHUFFFS.get();
           pDecoder->SBHUFFDS = SBHUFFDS.get();
           pDecoder->SBHUFFDT = SBHUFFDT.get();
@@ -389,7 +380,7 @@
           pDecoder->SBRAT[1] = SDRAT[1];
           pDecoder->SBRAT[2] = SDRAT[2];
           pDecoder->SBRAT[3] = SDRAT[3];
-          BS = pDecoder->decode_Huffman(pStream, grContext->data());
+          BS = pDecoder->DecodeHuffman(pStream, grContext->data());
           if (!BS)
             return nullptr;
 
@@ -413,13 +404,11 @@
             if (IDI < SBNUMSYMS)
               break;
           }
-          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B15, HuffmanTable_B15_Size, HuffmanTable_HTOOB_B15);
-          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-              HuffmanTable_B1, HuffmanTable_B1_Size, HuffmanTable_HTOOB_B1);
-          if ((pHuffmanDecoder->decodeAValue(SBHUFFRDX.get(), &RDXI) != 0) ||
-              (pHuffmanDecoder->decodeAValue(SBHUFFRDX.get(), &RDYI) != 0) ||
-              (pHuffmanDecoder->decodeAValue(SBHUFFRSIZE.get(), &nVal) != 0)) {
+          auto SBHUFFRDX = pdfium::MakeUnique<CJBig2_HuffmanTable>(15);
+          auto SBHUFFRSIZE = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
+          if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDX.get(), &RDXI) != 0) ||
+              (pHuffmanDecoder->DecodeAValue(SBHUFFRDX.get(), &RDYI) != 0) ||
+              (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE.get(), &nVal) != 0)) {
             return nullptr;
           }
           pStream->alignByte();
@@ -441,7 +430,7 @@
           pGRRD->GRAT[2] = SDRAT[2];
           pGRRD->GRAT[3] = SDRAT[3];
           auto pArithDecoder = pdfium::MakeUnique<CJBig2_ArithDecoder>(pStream);
-          BS = pGRRD->decode(pArithDecoder.get(), grContext->data());
+          BS = pGRRD->Decode(pArithDecoder.get(), grContext->data());
           if (!BS)
             return nullptr;
 
@@ -457,29 +446,36 @@
       NSYMSDECODED = NSYMSDECODED + 1;
     }
     if (SDREFAGG == 0) {
-      if (pHuffmanDecoder->decodeAValue(SDHUFFBMSIZE, (int32_t*)&BMSIZE) != 0)
+      if (pHuffmanDecoder->DecodeAValue(SDHUFFBMSIZE.Get(),
+                                        (int32_t*)&BMSIZE) != 0) {
         return nullptr;
-
+      }
       pStream->alignByte();
       std::unique_ptr<CJBig2_Image> BHC;
       if (BMSIZE == 0) {
-        stride = (TOTWIDTH + 7) >> 3;
-        if (pStream->getByteLeft() >= stride * HCHEIGHT) {
-          BHC = pdfium::MakeUnique<CJBig2_Image>(TOTWIDTH, HCHEIGHT);
-          for (I = 0; I < HCHEIGHT; I++) {
-            JBIG2_memcpy(BHC->data() + I * BHC->stride(), pStream->getPointer(),
-                         stride);
-            pStream->offset(stride);
-          }
-        } else {
+        FX_SAFE_UINT32 safe_stride = TOTWIDTH;
+        safe_stride += 7;
+        safe_stride /= 8;
+        FX_SAFE_UINT32 safe_image_size = safe_stride;
+        safe_image_size *= HCHEIGHT;
+        if (!safe_image_size.IsValid() ||
+            pStream->getByteLeft() < safe_image_size.ValueOrDie()) {
           return nullptr;
         }
+
+        const uint32_t stride = safe_stride.ValueOrDie();
+        BHC = pdfium::MakeUnique<CJBig2_Image>(TOTWIDTH, HCHEIGHT);
+        for (I = 0; I < HCHEIGHT; I++) {
+          memcpy(BHC->data() + I * BHC->stride(), pStream->getPointer(),
+                 stride);
+          pStream->offset(stride);
+        }
       } else {
         auto pGRD = pdfium::MakeUnique<CJBig2_GRDProc>();
         pGRD->MMR = 1;
         pGRD->GBW = TOTWIDTH;
         pGRD->GBH = HCHEIGHT;
-        pGRD->Start_decode_MMR(&BHC, pStream);
+        pGRD->StartDecodeMMR(&BHC, pStream);
         pStream->alignByte();
       }
       nTmp = 0;
@@ -487,19 +483,18 @@
         continue;
 
       for (I = HCFIRSTSYM; I < NSYMSDECODED; ++I) {
-        SDNEWSYMS[I] = BHC->subImage(nTmp, 0, SDNEWSYMWIDTHS[I], HCHEIGHT);
+        SDNEWSYMS[I] = BHC->SubImage(nTmp, 0, SDNEWSYMWIDTHS[I], HCHEIGHT);
         nTmp += SDNEWSYMWIDTHS[I];
       }
     }
   }
   EXINDEX = 0;
   CUREXFLAG = 0;
-  pTable = pdfium::MakeUnique<CJBig2_HuffmanTable>(
-      HuffmanTable_B1, HuffmanTable_B1_Size, HuffmanTable_HTOOB_B1);
+  pTable = pdfium::MakeUnique<CJBig2_HuffmanTable>(1);
   EXFLAGS.resize(SDNUMINSYMS + SDNUMNEWSYMS);
   num_ex_syms = 0;
   while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) {
-    if (pHuffmanDecoder->decodeAValue(pTable.get(), (int*)&EXRUNLENGTH) != 0)
+    if (pHuffmanDecoder->DecodeAValue(pTable.get(), (int*)&EXRUNLENGTH) != 0)
       return nullptr;
 
     if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS)
@@ -519,7 +514,7 @@
   if (num_ex_syms > SDNUMEXSYMS)
     return nullptr;
 
-  I = J = 0;
+  J = 0;
   for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; ++I) {
     if (!EXFLAGS[I] || J >= SDNUMEXSYMS)
       continue;
diff --git a/core/fxcodec/jbig2/JBig2_SddProc.h b/core/fxcodec/jbig2/JBig2_SddProc.h
index 80b988e..abef010 100644
--- a/core/fxcodec/jbig2/JBig2_SddProc.h
+++ b/core/fxcodec/jbig2/JBig2_SddProc.h
@@ -12,6 +12,7 @@
 
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CJBig2_BitStream;
 class CJBig2_HuffmanTable;
@@ -20,29 +21,32 @@
 
 class CJBig2_SDDProc {
  public:
-  std::unique_ptr<CJBig2_SymbolDict> decode_Arith(
+  CJBig2_SDDProc();
+  ~CJBig2_SDDProc();
+
+  std::unique_ptr<CJBig2_SymbolDict> DecodeArith(
       CJBig2_ArithDecoder* pArithDecoder,
       std::vector<JBig2ArithCtx>* gbContext,
       std::vector<JBig2ArithCtx>* grContext);
 
-  std::unique_ptr<CJBig2_SymbolDict> decode_Huffman(
+  std::unique_ptr<CJBig2_SymbolDict> DecodeHuffman(
       CJBig2_BitStream* pStream,
       std::vector<JBig2ArithCtx>* gbContext,
       std::vector<JBig2ArithCtx>* grContext);
 
   bool SDHUFF;
   bool SDREFAGG;
+  bool SDRTEMPLATE;
+  uint8_t SDTEMPLATE;
   uint32_t SDNUMINSYMS;
-  CJBig2_Image** SDINSYMS;
   uint32_t SDNUMNEWSYMS;
   uint32_t SDNUMEXSYMS;
-  CJBig2_HuffmanTable* SDHUFFDH;
-  CJBig2_HuffmanTable* SDHUFFDW;
-  CJBig2_HuffmanTable* SDHUFFBMSIZE;
-  CJBig2_HuffmanTable* SDHUFFAGGINST;
-  uint8_t SDTEMPLATE;
+  CJBig2_Image** SDINSYMS;
+  UnownedPtr<const CJBig2_HuffmanTable> SDHUFFDH;
+  UnownedPtr<const CJBig2_HuffmanTable> SDHUFFDW;
+  UnownedPtr<const CJBig2_HuffmanTable> SDHUFFBMSIZE;
+  UnownedPtr<const CJBig2_HuffmanTable> SDHUFFAGGINST;
   int8_t SDAT[8];
-  bool SDRTEMPLATE;
   int8_t SDRAT[4];
 };
 
diff --git a/core/fxcodec/jbig2/JBig2_Segment.cpp b/core/fxcodec/jbig2/JBig2_Segment.cpp
index 58be810..91fa2d9 100644
--- a/core/fxcodec/jbig2/JBig2_Segment.cpp
+++ b/core/fxcodec/jbig2/JBig2_Segment.cpp
@@ -6,8 +6,6 @@
 
 #include "core/fxcodec/jbig2/JBig2_Segment.h"
 
-#include "core/fxcrt/fx_memory.h"
-
 CJBig2_Segment::CJBig2_Segment()
     : m_dwNumber(0),
       m_nReferred_to_segment_count(0),
diff --git a/core/fxcodec/jbig2/JBig2_Segment.h b/core/fxcodec/jbig2/JBig2_Segment.h
index 914115b..45e2a3d 100644
--- a/core/fxcodec/jbig2/JBig2_Segment.h
+++ b/core/fxcodec/jbig2/JBig2_Segment.h
@@ -15,24 +15,25 @@
 #include "core/fxcodec/jbig2/JBig2_PatternDict.h"
 #include "core/fxcodec/jbig2/JBig2_SymbolDict.h"
 
-typedef enum {
+enum JBig2_SegmentState {
   JBIG2_SEGMENT_HEADER_UNPARSED,
   JBIG2_SEGMENT_DATA_UNPARSED,
   JBIG2_SEGMENT_PARSE_COMPLETE,
   JBIG2_SEGMENT_PAUSED,
   JBIG2_SEGMENT_ERROR
-} JBig2_SegmentState;
-typedef enum {
+};
+
+enum JBig2_ResultType {
   JBIG2_VOID_POINTER = 0,
   JBIG2_IMAGE_POINTER,
   JBIG2_SYMBOL_DICT_POINTER,
   JBIG2_PATTERN_DICT_POINTER,
   JBIG2_HUFFMAN_TABLE_POINTER
-} JBig2_ResultType;
+};
+
 class CJBig2_Segment {
  public:
   CJBig2_Segment();
-
   ~CJBig2_Segment();
 
   uint32_t m_dwNumber;
diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.cpp b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp
index c896095..a6fafb4 100644
--- a/core/fxcodec/jbig2/JBig2_SymbolDict.cpp
+++ b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp
@@ -7,7 +7,6 @@
 #include "core/fxcodec/jbig2/JBig2_SymbolDict.h"
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
-#include "core/fxcrt/fx_memory.h"
 #include "third_party/base/ptr_util.h"
 
 CJBig2_SymbolDict::CJBig2_SymbolDict() {}
diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.h b/core/fxcodec/jbig2/JBig2_SymbolDict.h
index 49289fb..956c83b 100644
--- a/core/fxcodec/jbig2/JBig2_SymbolDict.h
+++ b/core/fxcodec/jbig2/JBig2_SymbolDict.h
@@ -22,7 +22,6 @@
 
   std::unique_ptr<CJBig2_SymbolDict> DeepCopy() const;
 
-  // Takes ownership of |image|.
   void AddImage(std::unique_ptr<CJBig2_Image> image) {
     m_SDEXSYMS.push_back(std::move(image));
   }
@@ -33,11 +32,11 @@
   const std::vector<JBig2ArithCtx>& GbContext() const { return m_gbContext; }
   const std::vector<JBig2ArithCtx>& GrContext() const { return m_grContext; }
 
-  void SetGbContext(const std::vector<JBig2ArithCtx>& gbContext) {
-    m_gbContext = gbContext;
+  void SetGbContext(std::vector<JBig2ArithCtx> gbContext) {
+    m_gbContext = std::move(gbContext);
   }
-  void SetGrContext(const std::vector<JBig2ArithCtx>& grContext) {
-    m_grContext = grContext;
+  void SetGrContext(std::vector<JBig2ArithCtx> grContext) {
+    m_grContext = std::move(grContext);
   }
 
  private:
diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.cpp b/core/fxcodec/jbig2/JBig2_TrdProc.cpp
index 332e586..7aa5989 100644
--- a/core/fxcodec/jbig2/JBig2_TrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_TrdProc.cpp
@@ -12,21 +12,52 @@
 #include "core/fxcodec/jbig2/JBig2_ArithIntDecoder.h"
 #include "core/fxcodec/jbig2/JBig2_GrrdProc.h"
 #include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/maybe_owned.h"
+#include "third_party/base/optional.h"
 #include "third_party/base/ptr_util.h"
 
-CJBig2_TRDProc::CJBig2_TRDProc() {}
+namespace {
 
-CJBig2_TRDProc::~CJBig2_TRDProc() {}
+Optional<uint32_t> CheckTRDDimension(uint32_t dimension, int32_t delta) {
+  FX_SAFE_UINT32 result = dimension;
+  result += delta;
+  if (!result.IsValid())
+    return {};
+  return {result.ValueOrDie()};
+}
 
-std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::decode_Huffman(
+Optional<int32_t> CheckTRDReferenceDimension(int32_t dimension,
+                                             uint32_t shift,
+                                             int32_t offset) {
+  FX_SAFE_INT32 result = offset;
+  result += dimension >> shift;
+  if (!result.IsValid())
+    return {};
+  return {result.ValueOrDie()};
+}
+
+}  // namespace
+
+JBig2IntDecoderState::JBig2IntDecoderState() = default;
+
+JBig2IntDecoderState::~JBig2IntDecoderState() = default;
+
+CJBig2_TRDProc::CJBig2_TRDProc() = default;
+
+CJBig2_TRDProc::~CJBig2_TRDProc() = default;
+
+std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::DecodeHuffman(
     CJBig2_BitStream* pStream,
     JBig2ArithCtx* grContext) {
-  auto pHuffmanDecoder = pdfium::MakeUnique<CJBig2_HuffmanDecoder>(pStream);
   auto SBREG = pdfium::MakeUnique<CJBig2_Image>(SBW, SBH);
-  SBREG->fill(SBDEFPIXEL);
+  if (!SBREG->data())
+    return nullptr;
+
+  SBREG->Fill(SBDEFPIXEL);
   int32_t INITIAL_STRIPT;
-  if (pHuffmanDecoder->decodeAValue(SBHUFFDT, &INITIAL_STRIPT) != 0)
+  auto pHuffmanDecoder = pdfium::MakeUnique<CJBig2_HuffmanDecoder>(pStream);
+  if (pHuffmanDecoder->DecodeAValue(SBHUFFDT.Get(), &INITIAL_STRIPT) != 0)
     return nullptr;
 
   FX_SAFE_INT32 STRIPT = INITIAL_STRIPT;
@@ -36,7 +67,7 @@
   uint32_t NINSTANCES = 0;
   while (NINSTANCES < SBNUMINSTANCES) {
     int32_t INITIAL_DT;
-    if (pHuffmanDecoder->decodeAValue(SBHUFFDT, &INITIAL_DT) != 0)
+    if (pHuffmanDecoder->DecodeAValue(SBHUFFDT.Get(), &INITIAL_DT) != 0)
       return nullptr;
 
     FX_SAFE_INT32 DT = INITIAL_DT;
@@ -47,7 +78,7 @@
     for (;;) {
       if (bFirst) {
         int32_t DFS;
-        if (pHuffmanDecoder->decodeAValue(SBHUFFFS, &DFS) != 0)
+        if (pHuffmanDecoder->DecodeAValue(SBHUFFFS.Get(), &DFS) != 0)
           return nullptr;
 
         FIRSTS += DFS;
@@ -55,7 +86,7 @@
         bFirst = false;
       } else {
         int32_t IDS;
-        int32_t nVal = pHuffmanDecoder->decodeAValue(SBHUFFDS, &IDS);
+        int32_t nVal = pHuffmanDecoder->DecodeAValue(SBHUFFDS.Get(), &IDS);
         if (nVal == JBIG2_OOB)
           break;
 
@@ -81,7 +112,7 @@
         return nullptr;
 
       int32_t TI = SAFE_TI.ValueOrDie();
-      pdfium::base::CheckedNumeric<int32_t> nVal = 0;
+      FX_SAFE_INT32 nSafeVal = 0;
       int32_t nBits = 0;
       uint32_t IDI;
       for (;;) {
@@ -89,17 +120,16 @@
         if (pStream->read1Bit(&nTmp) != 0)
           return nullptr;
 
-        nVal <<= 1;
-        if (!nVal.IsValid())
+        nSafeVal <<= 1;
+        if (!nSafeVal.IsValid())
           return nullptr;
 
-        nVal |= nTmp;
+        nSafeVal |= nTmp;
         ++nBits;
+        const int32_t nVal = nSafeVal.ValueOrDie();
         for (IDI = 0; IDI < SBNUMSYMS; ++IDI) {
-          if ((nBits == SBSYMCODES[IDI].codelen) &&
-              (nVal.ValueOrDie() == SBSYMCODES[IDI].code)) {
+          if (nBits == SBSYMCODES[IDI].codelen && nVal == SBSYMCODES[IDI].code)
             break;
-          }
         }
         if (IDI < SBNUMSYMS)
           break;
@@ -117,11 +147,12 @@
         int32_t RDXI;
         int32_t RDYI;
         int32_t HUFFRSIZE;
-        if ((pHuffmanDecoder->decodeAValue(SBHUFFRDW, &RDWI) != 0) ||
-            (pHuffmanDecoder->decodeAValue(SBHUFFRDH, &RDHI) != 0) ||
-            (pHuffmanDecoder->decodeAValue(SBHUFFRDX, &RDXI) != 0) ||
-            (pHuffmanDecoder->decodeAValue(SBHUFFRDY, &RDYI) != 0) ||
-            (pHuffmanDecoder->decodeAValue(SBHUFFRSIZE, &HUFFRSIZE) != 0)) {
+        if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDW.Get(), &RDWI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRDH.Get(), &RDHI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRDX.Get(), &RDXI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRDY.Get(), &RDYI) != 0) ||
+            (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE.Get(), &HUFFRSIZE) !=
+             0)) {
           return nullptr;
         }
         pStream->alignByte();
@@ -130,20 +161,25 @@
         if (!IBOI)
           return nullptr;
 
-        uint32_t WOI = IBOI->width();
-        uint32_t HOI = IBOI->height();
-        if (static_cast<int>(WOI + RDWI) < 0 ||
-            static_cast<int>(HOI + RDHI) < 0) {
+        Optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
+        Optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
+        if (!WOI || !HOI)
           return nullptr;
-        }
+
+        Optional<int32_t> GRREFERENCEDX =
+            CheckTRDReferenceDimension(RDWI, 2, RDXI);
+        Optional<int32_t> GRREFERENCEDY =
+            CheckTRDReferenceDimension(RDHI, 2, RDYI);
+        if (!GRREFERENCEDX || !GRREFERENCEDY)
+          return nullptr;
 
         auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
-        pGRRD->GRW = WOI + RDWI;
-        pGRRD->GRH = HOI + RDHI;
+        pGRRD->GRW = WOI.value();
+        pGRRD->GRH = HOI.value();
         pGRRD->GRTEMPLATE = SBRTEMPLATE;
         pGRRD->GRREFERENCE = IBOI;
-        pGRRD->GRREFERENCEDX = (RDWI >> 2) + RDXI;
-        pGRRD->GRREFERENCEDY = (RDHI >> 2) + RDYI;
+        pGRRD->GRREFERENCEDX = GRREFERENCEDX.value();
+        pGRRD->GRREFERENCEDY = GRREFERENCEDY.value();
         pGRRD->TPGRON = 0;
         pGRRD->GRAT[0] = SBRAT[0];
         pGRRD->GRAT[1] = SBRAT[1];
@@ -151,7 +187,7 @@
         pGRRD->GRAT[3] = SBRAT[3];
 
         auto pArithDecoder = pdfium::MakeUnique<CJBig2_ArithDecoder>(pStream);
-        IBI = pGRRD->decode(pArithDecoder.get(), grContext);
+        IBI = pGRRD->Decode(pArithDecoder.get(), grContext);
         if (!IBI)
           return nullptr;
 
@@ -176,55 +212,33 @@
         return nullptr;
 
       int32_t SI = CURS.ValueOrDie();
-      if (TRANSPOSED == 0) {
-        switch (REFCORNER) {
-          case JBIG2_CORNER_TOPLEFT:
-            SBREG->composeFrom(SI, TI, IBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_TOPRIGHT:
-            SBREG->composeFrom(SI - WI + 1, TI, IBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMLEFT:
-            SBREG->composeFrom(SI, TI - HI + 1, IBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMRIGHT:
-            SBREG->composeFrom(SI - WI + 1, TI - HI + 1, IBI.Get(), SBCOMBOP);
-            break;
-        }
-      } else {
-        switch (REFCORNER) {
-          case JBIG2_CORNER_TOPLEFT:
-            SBREG->composeFrom(TI, SI, IBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_TOPRIGHT:
-            SBREG->composeFrom(TI - WI + 1, SI, IBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMLEFT:
-            SBREG->composeFrom(TI, SI - HI + 1, IBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMRIGHT:
-            SBREG->composeFrom(TI - WI + 1, SI - HI + 1, IBI.Get(), SBCOMBOP);
-            break;
-        }
-      }
-      if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) ||
-                              (REFCORNER == JBIG2_CORNER_BOTTOMLEFT))) {
-        CURS += WI - 1;
-      } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) ||
-                                     (REFCORNER == JBIG2_CORNER_TOPRIGHT))) {
-        CURS += HI - 1;
-      }
-      NINSTANCES = NINSTANCES + 1;
+      ComposeData compose = GetComposeData(SI, TI, WI, HI);
+      IBI.Get()->ComposeTo(SBREG.get(), compose.x, compose.y, SBCOMBOP);
+      if (compose.increment)
+        CURS += compose.increment;
+      ++NINSTANCES;
     }
   }
   return SBREG;
 }
 
-std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::decode_Arith(
+std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* grContext,
     JBig2IntDecoderState* pIDS) {
+  auto SBREG = pdfium::MakeUnique<CJBig2_Image>(SBW, SBH);
+  if (!SBREG->data())
+    return nullptr;
+
   MaybeOwned<CJBig2_ArithIntDecoder> pIADT;
+  if (pIDS)
+    pIADT = pIDS->IADT;
+  else
+    pIADT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
+  int32_t INITIAL_STRIPT;
+  if (!pIADT->Decode(pArithDecoder, &INITIAL_STRIPT))
+    return nullptr;
+
   MaybeOwned<CJBig2_ArithIntDecoder> pIAFS;
   MaybeOwned<CJBig2_ArithIntDecoder> pIADS;
   MaybeOwned<CJBig2_ArithIntDecoder> pIAIT;
@@ -235,7 +249,6 @@
   MaybeOwned<CJBig2_ArithIntDecoder> pIARDY;
   MaybeOwned<CJBig2_ArithIaidDecoder> pIAID;
   if (pIDS) {
-    pIADT = pIDS->IADT;
     pIAFS = pIDS->IAFS;
     pIADS = pIDS->IADS;
     pIAIT = pIDS->IAIT;
@@ -246,7 +259,6 @@
     pIARDY = pIDS->IARDY;
     pIAID = pIDS->IAID;
   } else {
-    pIADT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
     pIAFS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
     pIADS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
     pIAIT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
@@ -257,11 +269,8 @@
     pIARDY = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
     pIAID = pdfium::MakeUnique<CJBig2_ArithIaidDecoder>(SBSYMCODELEN);
   }
-  auto SBREG = pdfium::MakeUnique<CJBig2_Image>(SBW, SBH);
-  SBREG->fill(SBDEFPIXEL);
-  int32_t INITIAL_STRIPT;
-  if (!pIADT->decode(pArithDecoder, &INITIAL_STRIPT))
-    return nullptr;
+
+  SBREG->Fill(SBDEFPIXEL);
 
   FX_SAFE_INT32 STRIPT = INITIAL_STRIPT;
   STRIPT *= SBSTRIPS;
@@ -271,7 +280,7 @@
   while (NINSTANCES < SBNUMINSTANCES) {
     FX_SAFE_INT32 CURS = 0;
     int32_t INITIAL_DT;
-    if (!pIADT->decode(pArithDecoder, &INITIAL_DT))
+    if (!pIADT->Decode(pArithDecoder, &INITIAL_DT))
       return nullptr;
 
     FX_SAFE_INT32 DT = INITIAL_DT;
@@ -281,13 +290,13 @@
     for (;;) {
       if (bFirst) {
         int32_t DFS;
-        pIAFS->decode(pArithDecoder, &DFS);
+        pIAFS->Decode(pArithDecoder, &DFS);
         FIRSTS += DFS;
         CURS = FIRSTS;
         bFirst = false;
       } else {
         int32_t IDS;
-        if (!pIADS->decode(pArithDecoder, &IDS))
+        if (!pIADS->Decode(pArithDecoder, &IDS))
           break;
 
         CURS += IDS;
@@ -298,7 +307,7 @@
 
       int CURT = 0;
       if (SBSTRIPS != 1)
-        pIAIT->decode(pArithDecoder, &CURT);
+        pIAIT->Decode(pArithDecoder, &CURT);
 
       FX_SAFE_INT32 SAFE_TI = STRIPT + CURT;
       if (!SAFE_TI.IsValid())
@@ -306,7 +315,7 @@
 
       int32_t TI = SAFE_TI.ValueOrDie();
       uint32_t IDI;
-      pIAID->decode(pArithDecoder, &IDI);
+      pIAID->Decode(pArithDecoder, &IDI);
       if (IDI >= SBNUMSYMS)
         return nullptr;
 
@@ -314,7 +323,7 @@
       if (SBREFINE == 0)
         RI = 0;
       else
-        pIARI->decode(pArithDecoder, &RI);
+        pIARI->Decode(pArithDecoder, &RI);
 
       MaybeOwned<CJBig2_Image> pIBI;
       if (RI == 0) {
@@ -324,34 +333,39 @@
         int32_t RDHI;
         int32_t RDXI;
         int32_t RDYI;
-        pIARDW->decode(pArithDecoder, &RDWI);
-        pIARDH->decode(pArithDecoder, &RDHI);
-        pIARDX->decode(pArithDecoder, &RDXI);
-        pIARDY->decode(pArithDecoder, &RDYI);
+        pIARDW->Decode(pArithDecoder, &RDWI);
+        pIARDH->Decode(pArithDecoder, &RDHI);
+        pIARDX->Decode(pArithDecoder, &RDXI);
+        pIARDY->Decode(pArithDecoder, &RDYI);
         CJBig2_Image* IBOI = SBSYMS[IDI];
         if (!IBOI)
           return nullptr;
 
-        uint32_t WOI = IBOI->width();
-        uint32_t HOI = IBOI->height();
-        if (static_cast<int>(WOI + RDWI) < 0 ||
-            static_cast<int>(HOI + RDHI) < 0) {
+        Optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
+        Optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
+        if (!WOI || !HOI)
           return nullptr;
-        }
+
+        Optional<int32_t> GRREFERENCEDX =
+            CheckTRDReferenceDimension(RDWI, 1, RDXI);
+        Optional<int32_t> GRREFERENCEDY =
+            CheckTRDReferenceDimension(RDHI, 1, RDYI);
+        if (!GRREFERENCEDX || !GRREFERENCEDY)
+          return nullptr;
 
         auto pGRRD = pdfium::MakeUnique<CJBig2_GRRDProc>();
-        pGRRD->GRW = WOI + RDWI;
-        pGRRD->GRH = HOI + RDHI;
+        pGRRD->GRW = WOI.value();
+        pGRRD->GRH = HOI.value();
         pGRRD->GRTEMPLATE = SBRTEMPLATE;
         pGRRD->GRREFERENCE = IBOI;
-        pGRRD->GRREFERENCEDX = (RDWI >> 1) + RDXI;
-        pGRRD->GRREFERENCEDY = (RDHI >> 1) + RDYI;
+        pGRRD->GRREFERENCEDX = GRREFERENCEDX.value();
+        pGRRD->GRREFERENCEDY = GRREFERENCEDY.value();
         pGRRD->TPGRON = 0;
         pGRRD->GRAT[0] = SBRAT[0];
         pGRRD->GRAT[1] = SBRAT[1];
         pGRRD->GRAT[2] = SBRAT[2];
         pGRRD->GRAT[3] = SBRAT[3];
-        pIBI = pGRRD->decode(pArithDecoder, grContext);
+        pIBI = pGRRD->Decode(pArithDecoder, grContext);
       }
       if (!pIBI)
         return nullptr;
@@ -369,46 +383,63 @@
         return nullptr;
 
       int32_t SI = CURS.ValueOrDie();
-      if (TRANSPOSED == 0) {
-        switch (REFCORNER) {
-          case JBIG2_CORNER_TOPLEFT:
-            SBREG->composeFrom(SI, TI, pIBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_TOPRIGHT:
-            SBREG->composeFrom(SI - WI + 1, TI, pIBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMLEFT:
-            SBREG->composeFrom(SI, TI - HI + 1, pIBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMRIGHT:
-            SBREG->composeFrom(SI - WI + 1, TI - HI + 1, pIBI.Get(), SBCOMBOP);
-            break;
-        }
-      } else {
-        switch (REFCORNER) {
-          case JBIG2_CORNER_TOPLEFT:
-            SBREG->composeFrom(TI, SI, pIBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_TOPRIGHT:
-            SBREG->composeFrom(TI - WI + 1, SI, pIBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMLEFT:
-            SBREG->composeFrom(TI, SI - HI + 1, pIBI.Get(), SBCOMBOP);
-            break;
-          case JBIG2_CORNER_BOTTOMRIGHT:
-            SBREG->composeFrom(TI - WI + 1, SI - HI + 1, pIBI.Get(), SBCOMBOP);
-            break;
-        }
-      }
-      if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) ||
-                              (REFCORNER == JBIG2_CORNER_BOTTOMLEFT))) {
-        CURS += WI - 1;
-      } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) ||
-                                     (REFCORNER == JBIG2_CORNER_TOPRIGHT))) {
-        CURS += HI - 1;
-      }
+      ComposeData compose = GetComposeData(SI, TI, WI, HI);
+      pIBI.Get()->ComposeTo(SBREG.get(), compose.x, compose.y, SBCOMBOP);
+      if (compose.increment)
+        CURS += compose.increment;
       ++NINSTANCES;
     }
   }
   return SBREG;
 }
+
+CJBig2_TRDProc::ComposeData CJBig2_TRDProc::GetComposeData(int32_t SI,
+                                                           int32_t TI,
+                                                           uint32_t WI,
+                                                           uint32_t HI) const {
+  ComposeData results;
+  if (TRANSPOSED == 0) {
+    switch (REFCORNER) {
+      case JBIG2_CORNER_TOPLEFT:
+        results.x = SI;
+        results.y = TI;
+        results.increment = WI - 1;
+        break;
+      case JBIG2_CORNER_TOPRIGHT:
+        results.x = SI - WI + 1;
+        results.y = TI;
+        break;
+      case JBIG2_CORNER_BOTTOMLEFT:
+        results.x = SI;
+        results.y = TI - HI + 1;
+        results.increment = WI - 1;
+        break;
+      case JBIG2_CORNER_BOTTOMRIGHT:
+        results.x = SI - WI + 1;
+        results.y = TI - HI + 1;
+        break;
+    }
+  } else {
+    switch (REFCORNER) {
+      case JBIG2_CORNER_TOPLEFT:
+        results.x = TI;
+        results.y = SI;
+        results.increment = HI - 1;
+        break;
+      case JBIG2_CORNER_TOPRIGHT:
+        results.x = TI - WI + 1;
+        results.y = SI;
+        results.increment = HI - 1;
+        break;
+      case JBIG2_CORNER_BOTTOMLEFT:
+        results.x = TI;
+        results.y = SI - HI + 1;
+        break;
+      case JBIG2_CORNER_BOTTOMRIGHT:
+        results.x = TI - WI + 1;
+        results.y = SI - HI + 1;
+        break;
+    }
+  }
+  return results;
+}
diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.h b/core/fxcodec/jbig2/JBig2_TrdProc.h
index a66415b..8f4e42d 100644
--- a/core/fxcodec/jbig2/JBig2_TrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_TrdProc.h
@@ -12,26 +12,30 @@
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_ArithIaidDecoder;
 class CJBig2_ArithIntDecoder;
 class CJBig2_BitStream;
 class CJBig2_HuffmanTable;
-struct JBig2ArithCtx;
+class JBig2ArithCtx;
 struct JBig2HuffmanCode;
 
 struct JBig2IntDecoderState {
-  CJBig2_ArithIntDecoder* IADT;
-  CJBig2_ArithIntDecoder* IAFS;
-  CJBig2_ArithIntDecoder* IADS;
-  CJBig2_ArithIntDecoder* IAIT;
-  CJBig2_ArithIntDecoder* IARI;
-  CJBig2_ArithIntDecoder* IARDW;
-  CJBig2_ArithIntDecoder* IARDH;
-  CJBig2_ArithIntDecoder* IARDX;
-  CJBig2_ArithIntDecoder* IARDY;
-  CJBig2_ArithIaidDecoder* IAID;
+  JBig2IntDecoderState();
+  ~JBig2IntDecoderState();
+
+  UnownedPtr<CJBig2_ArithIntDecoder> IADT;
+  UnownedPtr<CJBig2_ArithIntDecoder> IAFS;
+  UnownedPtr<CJBig2_ArithIntDecoder> IADS;
+  UnownedPtr<CJBig2_ArithIntDecoder> IAIT;
+  UnownedPtr<CJBig2_ArithIntDecoder> IARI;
+  UnownedPtr<CJBig2_ArithIntDecoder> IARDW;
+  UnownedPtr<CJBig2_ArithIntDecoder> IARDH;
+  UnownedPtr<CJBig2_ArithIntDecoder> IARDX;
+  UnownedPtr<CJBig2_ArithIntDecoder> IARDY;
+  UnownedPtr<CJBig2_ArithIaidDecoder> IAID;
 };
 
 enum JBig2Corner {
@@ -46,42 +50,49 @@
   CJBig2_TRDProc();
   ~CJBig2_TRDProc();
 
-  std::unique_ptr<CJBig2_Image> decode_Huffman(CJBig2_BitStream* pStream,
-                                               JBig2ArithCtx* grContext);
+  std::unique_ptr<CJBig2_Image> DecodeHuffman(CJBig2_BitStream* pStream,
+                                              JBig2ArithCtx* grContext);
 
-  std::unique_ptr<CJBig2_Image> decode_Arith(CJBig2_ArithDecoder* pArithDecoder,
-                                             JBig2ArithCtx* grContext,
-                                             JBig2IntDecoderState* pIDS);
+  std::unique_ptr<CJBig2_Image> DecodeArith(CJBig2_ArithDecoder* pArithDecoder,
+                                            JBig2ArithCtx* grContext,
+                                            JBig2IntDecoderState* pIDS);
 
   bool SBHUFF;
   bool SBREFINE;
+  bool SBRTEMPLATE;
+  bool TRANSPOSED;
+  bool SBDEFPIXEL;
+  int8_t SBDSOFFSET;
+  uint8_t SBSYMCODELEN;
   uint32_t SBW;
   uint32_t SBH;
   uint32_t SBNUMINSTANCES;
   uint32_t SBSTRIPS;
   uint32_t SBNUMSYMS;
-
   std::vector<JBig2HuffmanCode> SBSYMCODES;
-  uint8_t SBSYMCODELEN;
-
   CJBig2_Image** SBSYMS;
-  bool SBDEFPIXEL;
-
   JBig2ComposeOp SBCOMBOP;
-  bool TRANSPOSED;
-
   JBig2Corner REFCORNER;
-  int8_t SBDSOFFSET;
-  CJBig2_HuffmanTable* SBHUFFFS;
-  CJBig2_HuffmanTable* SBHUFFDS;
-  CJBig2_HuffmanTable* SBHUFFDT;
-  CJBig2_HuffmanTable* SBHUFFRDW;
-  CJBig2_HuffmanTable* SBHUFFRDH;
-  CJBig2_HuffmanTable* SBHUFFRDX;
-  CJBig2_HuffmanTable* SBHUFFRDY;
-  CJBig2_HuffmanTable* SBHUFFRSIZE;
-  bool SBRTEMPLATE;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFFS;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFDS;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFDT;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFRDW;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFRDH;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFRDX;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFRDY;
+  UnownedPtr<const CJBig2_HuffmanTable> SBHUFFRSIZE;
   int8_t SBRAT[4];
+
+ private:
+  struct ComposeData {
+    int32_t x;
+    int32_t y;
+    uint32_t increment = 0;
+  };
+  ComposeData GetComposeData(int32_t SI,
+                             int32_t TI,
+                             uint32_t WI,
+                             uint32_t HI) const;
 };
 
 #endif  // CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_
diff --git a/core/fxcodec/jbig2/jbig2_embeddertest.cpp b/core/fxcodec/jbig2/jbig2_embeddertest.cpp
new file mode 100644
index 0000000..e2523fc
--- /dev/null
+++ b/core/fxcodec/jbig2/jbig2_embeddertest.cpp
@@ -0,0 +1,28 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class JBig2EmbedderTest : public EmbedderTest {};
+
+#if defined(_SKIA_SUPPORT_)
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#define MAYBE_Bug_631912 DISABLED_Bug_631912
+#else
+#define MAYBE_Bug_631912 Bug_631912
+#endif
+TEST_F(JBig2EmbedderTest, MAYBE_Bug_631912) {
+  // Test jbig2 image in PDF file can be loaded successfully.
+  // Should not crash.
+  EXPECT_TRUE(OpenDocument("bug_631912.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  CompareBitmap(bitmap.get(), 691, 432, "24d75af646f8772c5ee7ced260452ae4");
+  UnloadPage(page);
+}
diff --git a/core/fxcodec/jbig2/jbig2module.cpp b/core/fxcodec/jbig2/jbig2module.cpp
new file mode 100644
index 0000000..6f99143
--- /dev/null
+++ b/core/fxcodec/jbig2/jbig2module.cpp
@@ -0,0 +1,87 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jbig2/jbig2module.h"
+
+#include "core/fxcodec/jbig2/JBig2_Context.h"
+#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcodec {
+
+JBig2_DocumentContext* GetJBig2DocumentContext(
+    std::unique_ptr<JBig2_DocumentContext>* pContextHolder) {
+  if (!*pContextHolder)
+    *pContextHolder = pdfium::MakeUnique<JBig2_DocumentContext>();
+  return pContextHolder->get();
+}
+
+Jbig2Context::Jbig2Context() = default;
+
+Jbig2Context::~Jbig2Context() = default;
+
+Jbig2Module::Jbig2Module() = default;
+
+Jbig2Module::~Jbig2Module() = default;
+
+FXCODEC_STATUS Jbig2Module::StartDecode(
+    Jbig2Context* pJbig2Context,
+    std::unique_ptr<JBig2_DocumentContext>* pContextHolder,
+    uint32_t width,
+    uint32_t height,
+    pdfium::span<const uint8_t> src_span,
+    uint32_t src_objnum,
+    pdfium::span<const uint8_t> global_span,
+    uint32_t global_objnum,
+    uint8_t* dest_buf,
+    uint32_t dest_pitch,
+    PauseIndicatorIface* pPause) {
+  if (!pJbig2Context)
+    return FXCODEC_STATUS_ERR_PARAMS;
+
+  JBig2_DocumentContext* pJBig2DocumentContext =
+      GetJBig2DocumentContext(pContextHolder);
+  pJbig2Context->m_width = width;
+  pJbig2Context->m_height = height;
+  pJbig2Context->m_pSrcSpan = src_span;
+  pJbig2Context->m_nSrcObjNum = src_objnum;
+  pJbig2Context->m_pGlobalSpan = global_span;
+  pJbig2Context->m_nGlobalObjNum = global_objnum;
+  pJbig2Context->m_dest_buf = dest_buf;
+  pJbig2Context->m_dest_pitch = dest_pitch;
+  memset(dest_buf, 0, height * dest_pitch);
+  pJbig2Context->m_pContext =
+      CJBig2_Context::Create(global_span, global_objnum, src_span, src_objnum,
+                             pJBig2DocumentContext->GetSymbolDictCache());
+  bool succeeded = pJbig2Context->m_pContext->GetFirstPage(
+      dest_buf, width, height, dest_pitch, pPause);
+  return Decode(pJbig2Context, succeeded);
+}
+
+FXCODEC_STATUS Jbig2Module::ContinueDecode(Jbig2Context* pJbig2Context,
+                                           PauseIndicatorIface* pPause) {
+  bool succeeded = pJbig2Context->m_pContext->Continue(pPause);
+  return Decode(pJbig2Context, succeeded);
+}
+
+FXCODEC_STATUS Jbig2Module::Decode(Jbig2Context* pJbig2Context,
+                                   bool decode_success) {
+  FXCODEC_STATUS status = pJbig2Context->m_pContext->GetProcessingStatus();
+  if (status != FXCODEC_STATUS_DECODE_FINISH)
+    return status;
+
+  pJbig2Context->m_pContext.reset();
+  if (!decode_success)
+    return FXCODEC_STATUS_ERROR;
+
+  int dword_size = pJbig2Context->m_height * pJbig2Context->m_dest_pitch / 4;
+  uint32_t* dword_buf = reinterpret_cast<uint32_t*>(pJbig2Context->m_dest_buf);
+  for (int i = 0; i < dword_size; i++)
+    dword_buf[i] = ~dword_buf[i];
+  return FXCODEC_STATUS_DECODE_FINISH;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jbig2/jbig2module.h b/core/fxcodec/jbig2/jbig2module.h
new file mode 100644
index 0000000..6f9b0bb
--- /dev/null
+++ b/core/fxcodec/jbig2/jbig2module.h
@@ -0,0 +1,68 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JBIG2_JBIG2MODULE_H_
+#define CORE_FXCODEC_JBIG2_JBIG2MODULE_H_
+
+#include <memory>
+
+#include "core/fxcodec/fx_codec_def.h"
+#include "third_party/base/span.h"
+
+class CJBig2_Context;
+class CJBig2_Image;
+class JBig2_DocumentContext;
+class PauseIndicatorIface;
+
+namespace fxcodec {
+
+class Jbig2Context {
+ public:
+  Jbig2Context();
+  ~Jbig2Context();
+
+  uint32_t m_width = 0;
+  uint32_t m_height = 0;
+  uint32_t m_nGlobalObjNum = 0;
+  uint32_t m_nSrcObjNum = 0;
+  pdfium::span<const uint8_t> m_pGlobalSpan;
+  pdfium::span<const uint8_t> m_pSrcSpan;
+  uint8_t* m_dest_buf = nullptr;
+  uint32_t m_dest_pitch = 0;
+  std::unique_ptr<CJBig2_Context> m_pContext;
+};
+
+class Jbig2Module {
+ public:
+  Jbig2Module();
+  ~Jbig2Module();
+
+  FXCODEC_STATUS StartDecode(
+      Jbig2Context* pJbig2Context,
+      std::unique_ptr<JBig2_DocumentContext>* pContextHolder,
+      uint32_t width,
+      uint32_t height,
+      pdfium::span<const uint8_t> src_span,
+      uint32_t src_objnum,
+      pdfium::span<const uint8_t> global_span,
+      uint32_t global_objnum,
+      uint8_t* dest_buf,
+      uint32_t dest_pitch,
+      PauseIndicatorIface* pPause);
+
+  FXCODEC_STATUS ContinueDecode(Jbig2Context* pJbig2Context,
+                                PauseIndicatorIface* pPause);
+
+ private:
+  FXCODEC_STATUS Decode(Jbig2Context* pJbig2Context, bool decode_success);
+};
+
+}  // namespace fxcodec
+
+using Jbig2Context = fxcodec::Jbig2Context;
+using Jbig2Module = fxcodec::Jbig2Module;
+
+#endif  // CORE_FXCODEC_JBIG2_JBIG2MODULE_H_
diff --git a/core/fxcodec/jpeg/DEPS b/core/fxcodec/jpeg/DEPS
new file mode 100644
index 0000000..88af241
--- /dev/null
+++ b/core/fxcodec/jpeg/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  '+third_party/libjpeg/jerror.h',
+  '+third_party/libjpeg/jpeglib.h',
+  '+third_party/libjpeg_turbo/jerror.h',
+  '+third_party/libjpeg_turbo/jpeglib.h',
+]
diff --git a/core/fxcodec/jpeg/jpegmodule.cpp b/core/fxcodec/jpeg/jpegmodule.cpp
new file mode 100644
index 0000000..72925f1
--- /dev/null
+++ b/core/fxcodec/jpeg/jpegmodule.cpp
@@ -0,0 +1,685 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jpeg/jpegmodule.h"
+
+#include <setjmp.h>
+
+#include <memory>
+#include <utility>
+
+#include "build/build_config.h"
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/scanlinedecoder.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/fx_dib.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/optional.h"
+#include "third_party/base/ptr_util.h"
+
+extern "C" {
+#undef FAR
+#if defined(USE_SYSTEM_LIBJPEG)
+#include <jerror.h>
+#include <jpeglib.h>
+#elif defined(USE_LIBJPEG_TURBO)
+#include "third_party/libjpeg_turbo/jerror.h"
+#include "third_party/libjpeg_turbo/jpeglib.h"
+#else
+#include "third_party/libjpeg/jerror.h"
+#include "third_party/libjpeg/jpeglib.h"
+#endif
+}  // extern "C"
+
+class CJpegContext final : public ModuleIface::Context {
+ public:
+  CJpegContext();
+  ~CJpegContext() override;
+
+  jmp_buf& GetJumpMark() { return m_JumpMark; }
+
+  jmp_buf m_JumpMark;
+  jpeg_decompress_struct m_Info = {};
+  jpeg_error_mgr m_ErrMgr = {};
+  jpeg_source_mgr m_SrcMgr = {};
+  unsigned int m_SkipSize = 0;
+  void* (*m_AllocFunc)(unsigned int);
+  void (*m_FreeFunc)(void*);
+};
+
+static pdfium::span<const uint8_t> JpegScanSOI(
+    pdfium::span<const uint8_t> src_span) {
+  ASSERT(!src_span.empty());
+
+  for (size_t offset = 0; offset < src_span.size() - 1; ++offset) {
+    if (src_span[offset] == 0xff && src_span[offset + 1] == 0xd8)
+      return src_span.subspan(offset);
+  }
+  return src_span;
+}
+
+extern "C" {
+
+static void src_do_nothing(jpeg_decompress_struct* cinfo) {}
+
+static void error_fatal(j_common_ptr cinfo) {
+  longjmp(*(jmp_buf*)cinfo->client_data, -1);
+}
+
+static void src_skip_data(jpeg_decompress_struct* cinfo, long num) {
+  if (num > (long)cinfo->src->bytes_in_buffer) {
+    error_fatal((j_common_ptr)cinfo);
+  }
+  cinfo->src->next_input_byte += num;
+  cinfo->src->bytes_in_buffer -= num;
+}
+
+static boolean src_fill_buffer(j_decompress_ptr cinfo) {
+  return FALSE;
+}
+
+static boolean src_resync(j_decompress_ptr cinfo, int desired) {
+  return FALSE;
+}
+
+static void error_do_nothing(j_common_ptr cinfo) {}
+
+static void error_do_nothing1(j_common_ptr cinfo, int) {}
+
+static void error_do_nothing2(j_common_ptr cinfo, char*) {}
+
+#if defined(OS_WIN)
+static void dest_do_nothing(j_compress_ptr cinfo) {}
+
+static boolean dest_empty(j_compress_ptr cinfo) {
+  return false;
+}
+#endif  // defined(OS_WIN)
+
+static void error_fatal1(j_common_ptr cinfo) {
+  auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
+  longjmp(pContext->m_JumpMark, -1);
+}
+
+static void src_skip_data1(jpeg_decompress_struct* cinfo, long num) {
+  if (cinfo->src->bytes_in_buffer < static_cast<size_t>(num)) {
+    auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
+    pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer);
+    cinfo->src->bytes_in_buffer = 0;
+  } else {
+    cinfo->src->next_input_byte += num;
+    cinfo->src->bytes_in_buffer -= num;
+  }
+}
+
+static void* jpeg_alloc_func(unsigned int size) {
+  return FX_Alloc(char, size);
+}
+
+static void jpeg_free_func(void* p) {
+  FX_Free(p);
+}
+
+}  // extern "C"
+
+#ifdef PDF_ENABLE_XFA
+static void JpegLoadAttribute(const jpeg_decompress_struct& info,
+                              CFX_DIBAttribute* pAttribute) {
+  pAttribute->m_nXDPI = info.X_density;
+  pAttribute->m_nYDPI = info.Y_density;
+  pAttribute->m_wDPIUnit = info.density_unit;
+}
+#endif  // PDF_ENABLE_XFA
+
+static bool JpegLoadInfo(pdfium::span<const uint8_t> src_span,
+                         int* width,
+                         int* height,
+                         int* num_components,
+                         int* bits_per_components,
+                         bool* color_transform) {
+  src_span = JpegScanSOI(src_span);
+  jpeg_decompress_struct cinfo;
+  jpeg_error_mgr jerr;
+  jerr.error_exit = error_fatal;
+  jerr.emit_message = error_do_nothing1;
+  jerr.output_message = error_do_nothing;
+  jerr.format_message = error_do_nothing2;
+  jerr.reset_error_mgr = error_do_nothing;
+  jerr.trace_level = 0;
+  cinfo.err = &jerr;
+  jmp_buf mark;
+  cinfo.client_data = &mark;
+  if (setjmp(mark) == -1)
+    return false;
+
+  jpeg_create_decompress(&cinfo);
+  jpeg_source_mgr src;
+  src.init_source = src_do_nothing;
+  src.term_source = src_do_nothing;
+  src.skip_input_data = src_skip_data;
+  src.fill_input_buffer = src_fill_buffer;
+  src.resync_to_restart = src_resync;
+  src.bytes_in_buffer = src_span.size();
+  src.next_input_byte = src_span.data();
+  cinfo.src = &src;
+  if (setjmp(mark) == -1) {
+    jpeg_destroy_decompress(&cinfo);
+    return false;
+  }
+  int ret = jpeg_read_header(&cinfo, TRUE);
+  if (ret != JPEG_HEADER_OK) {
+    jpeg_destroy_decompress(&cinfo);
+    return false;
+  }
+  *width = cinfo.image_width;
+  *height = cinfo.image_height;
+  *num_components = cinfo.num_components;
+  *color_transform =
+      cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK;
+  *bits_per_components = cinfo.data_precision;
+  jpeg_destroy_decompress(&cinfo);
+  return true;
+}
+
+CJpegContext::CJpegContext()
+    : m_AllocFunc(jpeg_alloc_func), m_FreeFunc(jpeg_free_func) {
+  m_Info.client_data = this;
+  m_Info.err = &m_ErrMgr;
+
+  m_ErrMgr.error_exit = error_fatal1;
+  m_ErrMgr.emit_message = error_do_nothing1;
+  m_ErrMgr.output_message = error_do_nothing;
+  m_ErrMgr.format_message = error_do_nothing2;
+  m_ErrMgr.reset_error_mgr = error_do_nothing;
+
+  m_SrcMgr.init_source = src_do_nothing;
+  m_SrcMgr.term_source = src_do_nothing;
+  m_SrcMgr.skip_input_data = src_skip_data1;
+  m_SrcMgr.fill_input_buffer = src_fill_buffer;
+  m_SrcMgr.resync_to_restart = src_resync;
+}
+
+CJpegContext::~CJpegContext() {
+  jpeg_destroy_decompress(&m_Info);
+}
+
+namespace fxcodec {
+
+namespace {
+
+constexpr size_t kKnownBadHeaderWithInvalidHeightByteOffsetStarts[] = {94, 163};
+
+class JpegDecoder final : public ScanlineDecoder {
+ public:
+  JpegDecoder();
+  ~JpegDecoder() override;
+
+  bool Create(pdfium::span<const uint8_t> src_span,
+              int width,
+              int height,
+              int nComps,
+              bool ColorTransform);
+
+  // ScanlineDecoder:
+  bool v_Rewind() override;
+  uint8_t* v_GetNextLine() override;
+  uint32_t GetSrcOffset() override;
+
+  bool InitDecode(bool bAcceptKnownBadHeader);
+
+  jmp_buf m_JmpBuf;
+  jpeg_decompress_struct m_Cinfo;
+  jpeg_error_mgr m_Jerr;
+  jpeg_source_mgr m_Src;
+  pdfium::span<const uint8_t> m_SrcSpan;
+  std::unique_ptr<uint8_t, FxFreeDeleter> m_pScanlineBuf;
+  bool m_bInited = false;
+  bool m_bStarted = false;
+  bool m_bJpegTransform = false;
+
+ private:
+  void CalcPitch();
+  void InitDecompressSrc();
+
+  // Can only be called inside a jpeg_read_header() setjmp handler.
+  bool HasKnownBadHeaderWithInvalidHeight(size_t dimension_offset) const;
+
+  // Is a JPEG SOFn marker, which is defined as 0xff, 0xc[0-9a-f].
+  bool IsSofSegment(size_t marker_offset) const;
+
+  // Patch up the in-memory JPEG header for known bad JPEGs.
+  void PatchUpKnownBadHeaderWithInvalidHeight(size_t dimension_offset);
+
+  // Patch up the JPEG trailer, even if it is correct.
+  void PatchUpTrailer();
+
+  uint8_t* GetWritableSrcData();
+
+  // For a given invalid height byte offset in
+  // |kKnownBadHeaderWithInvalidHeightByteOffsetStarts|, the SOFn marker should
+  // be this many bytes before that.
+  static const size_t kSofMarkerByteOffset = 5;
+
+  uint32_t m_nDefaultScaleDenom = 1;
+};
+
+JpegDecoder::JpegDecoder() {
+  memset(&m_Cinfo, 0, sizeof(m_Cinfo));
+  memset(&m_Jerr, 0, sizeof(m_Jerr));
+  memset(&m_Src, 0, sizeof(m_Src));
+}
+
+JpegDecoder::~JpegDecoder() {
+  if (m_bInited)
+    jpeg_destroy_decompress(&m_Cinfo);
+}
+
+bool JpegDecoder::InitDecode(bool bAcceptKnownBadHeader) {
+  m_Cinfo.err = &m_Jerr;
+  m_Cinfo.client_data = &m_JmpBuf;
+  if (setjmp(m_JmpBuf) == -1)
+    return false;
+
+  jpeg_create_decompress(&m_Cinfo);
+  InitDecompressSrc();
+  m_bInited = true;
+
+  if (setjmp(m_JmpBuf) == -1) {
+    Optional<size_t> known_bad_header_offset;
+    if (bAcceptKnownBadHeader) {
+      for (size_t offset : kKnownBadHeaderWithInvalidHeightByteOffsetStarts) {
+        if (HasKnownBadHeaderWithInvalidHeight(offset)) {
+          known_bad_header_offset = offset;
+          break;
+        }
+      }
+    }
+    jpeg_destroy_decompress(&m_Cinfo);
+    if (!known_bad_header_offset.has_value()) {
+      m_bInited = false;
+      return false;
+    }
+
+    PatchUpKnownBadHeaderWithInvalidHeight(known_bad_header_offset.value());
+
+    jpeg_create_decompress(&m_Cinfo);
+    InitDecompressSrc();
+  }
+  m_Cinfo.image_width = m_OrigWidth;
+  m_Cinfo.image_height = m_OrigHeight;
+  int ret = jpeg_read_header(&m_Cinfo, TRUE);
+  if (ret != JPEG_HEADER_OK)
+    return false;
+
+  if (m_Cinfo.saw_Adobe_marker)
+    m_bJpegTransform = true;
+
+  if (m_Cinfo.num_components == 3 && !m_bJpegTransform)
+    m_Cinfo.out_color_space = m_Cinfo.jpeg_color_space;
+
+  m_OrigWidth = m_Cinfo.image_width;
+  m_OrigHeight = m_Cinfo.image_height;
+  m_OutputWidth = m_OrigWidth;
+  m_OutputHeight = m_OrigHeight;
+  m_nDefaultScaleDenom = m_Cinfo.scale_denom;
+  return true;
+}
+
+bool JpegDecoder::Create(pdfium::span<const uint8_t> src_span,
+                         int width,
+                         int height,
+                         int nComps,
+                         bool ColorTransform) {
+  m_SrcSpan = JpegScanSOI(src_span);
+  if (m_SrcSpan.size() < 2)
+    return false;
+
+  PatchUpTrailer();
+
+  m_Jerr.error_exit = error_fatal;
+  m_Jerr.emit_message = error_do_nothing1;
+  m_Jerr.output_message = error_do_nothing;
+  m_Jerr.format_message = error_do_nothing2;
+  m_Jerr.reset_error_mgr = error_do_nothing;
+  m_Src.init_source = src_do_nothing;
+  m_Src.term_source = src_do_nothing;
+  m_Src.skip_input_data = src_skip_data;
+  m_Src.fill_input_buffer = src_fill_buffer;
+  m_Src.resync_to_restart = src_resync;
+  m_bJpegTransform = ColorTransform;
+  m_OutputWidth = m_OrigWidth = width;
+  m_OutputHeight = m_OrigHeight = height;
+  if (!InitDecode(/*bAcceptKnownBadHeader=*/true))
+    return false;
+
+  if (m_Cinfo.num_components < nComps)
+    return false;
+
+  if (static_cast<int>(m_Cinfo.image_width) < width)
+    return false;
+
+  CalcPitch();
+  m_pScanlineBuf.reset(FX_Alloc(uint8_t, m_Pitch));
+  m_nComps = m_Cinfo.num_components;
+  m_bpc = 8;
+  m_bStarted = false;
+  return true;
+}
+
+bool JpegDecoder::v_Rewind() {
+  if (m_bStarted) {
+    jpeg_destroy_decompress(&m_Cinfo);
+    if (!InitDecode(/*bAcceptKnownBadHeader=*/false)) {
+      return false;
+    }
+  }
+  if (setjmp(m_JmpBuf) == -1) {
+    return false;
+  }
+  m_Cinfo.scale_denom = m_nDefaultScaleDenom;
+  m_OutputWidth = m_OrigWidth;
+  m_OutputHeight = m_OrigHeight;
+  if (!jpeg_start_decompress(&m_Cinfo)) {
+    jpeg_destroy_decompress(&m_Cinfo);
+    return false;
+  }
+  if (static_cast<int>(m_Cinfo.output_width) > m_OrigWidth) {
+    NOTREACHED();
+    return false;
+  }
+  m_bStarted = true;
+  return true;
+}
+
+uint8_t* JpegDecoder::v_GetNextLine() {
+  if (setjmp(m_JmpBuf) == -1)
+    return nullptr;
+
+  uint8_t* row_array[] = {m_pScanlineBuf.get()};
+  int nlines = jpeg_read_scanlines(&m_Cinfo, row_array, 1);
+  return nlines > 0 ? m_pScanlineBuf.get() : nullptr;
+}
+
+uint32_t JpegDecoder::GetSrcOffset() {
+  return static_cast<uint32_t>(m_SrcSpan.size() - m_Src.bytes_in_buffer);
+}
+
+void JpegDecoder::CalcPitch() {
+  m_Pitch = static_cast<uint32_t>(m_Cinfo.image_width) * m_Cinfo.num_components;
+  m_Pitch += 3;
+  m_Pitch /= 4;
+  m_Pitch *= 4;
+}
+
+void JpegDecoder::InitDecompressSrc() {
+  m_Cinfo.src = &m_Src;
+  m_Src.bytes_in_buffer = m_SrcSpan.size();
+  m_Src.next_input_byte = m_SrcSpan.data();
+}
+
+bool JpegDecoder::HasKnownBadHeaderWithInvalidHeight(
+    size_t dimension_offset) const {
+  // Perform lots of possibly redundant checks to make sure this has no false
+  // positives.
+  bool bDimensionChecks = m_Cinfo.err->msg_code == JERR_IMAGE_TOO_BIG &&
+                          m_Cinfo.image_width < JPEG_MAX_DIMENSION &&
+                          m_Cinfo.image_height == 0xffff && m_OrigWidth > 0 &&
+                          m_OrigWidth <= JPEG_MAX_DIMENSION &&
+                          m_OrigHeight > 0 &&
+                          m_OrigHeight <= JPEG_MAX_DIMENSION;
+  if (!bDimensionChecks)
+    return false;
+
+  if (m_SrcSpan.size() <= dimension_offset + 3u)
+    return false;
+
+  if (!IsSofSegment(dimension_offset - kSofMarkerByteOffset))
+    return false;
+
+  const uint8_t* pHeaderDimensions = &m_SrcSpan[dimension_offset];
+  uint8_t nExpectedWidthByte1 = (m_OrigWidth >> 8) & 0xff;
+  uint8_t nExpectedWidthByte2 = m_OrigWidth & 0xff;
+  // Height high byte, height low byte, width high byte, width low byte.
+  return pHeaderDimensions[0] == 0xff && pHeaderDimensions[1] == 0xff &&
+         pHeaderDimensions[2] == nExpectedWidthByte1 &&
+         pHeaderDimensions[3] == nExpectedWidthByte2;
+}
+
+bool JpegDecoder::IsSofSegment(size_t marker_offset) const {
+  const uint8_t* pHeaderMarker = &m_SrcSpan[marker_offset];
+  return pHeaderMarker[0] == 0xff && pHeaderMarker[1] >= 0xc0 &&
+         pHeaderMarker[1] <= 0xcf;
+}
+
+void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight(
+    size_t dimension_offset) {
+  ASSERT(m_SrcSpan.size() > dimension_offset + 1u);
+  uint8_t* pData = GetWritableSrcData() + dimension_offset;
+  pData[0] = (m_OrigHeight >> 8) & 0xff;
+  pData[1] = m_OrigHeight & 0xff;
+}
+
+void JpegDecoder::PatchUpTrailer() {
+  uint8_t* pData = GetWritableSrcData();
+  pData[m_SrcSpan.size() - 2] = 0xff;
+  pData[m_SrcSpan.size() - 1] = 0xd9;
+}
+
+uint8_t* JpegDecoder::GetWritableSrcData() {
+  return const_cast<uint8_t*>(m_SrcSpan.data());
+}
+
+}  // namespace
+
+std::unique_ptr<ScanlineDecoder> JpegModule::CreateDecoder(
+    pdfium::span<const uint8_t> src_span,
+    int width,
+    int height,
+    int nComps,
+    bool ColorTransform) {
+  ASSERT(!src_span.empty());
+
+  auto pDecoder = pdfium::MakeUnique<JpegDecoder>();
+  if (!pDecoder->Create(src_span, width, height, nComps, ColorTransform))
+    return nullptr;
+
+  return std::move(pDecoder);
+}
+
+Optional<JpegModule::JpegImageInfo> JpegModule::LoadInfo(
+    pdfium::span<const uint8_t> src_span) {
+  JpegImageInfo info;
+  if (!JpegLoadInfo(src_span, &info.width, &info.height, &info.num_components,
+                    &info.bits_per_components, &info.color_transform)) {
+    return pdfium::nullopt;
+  }
+  return info;
+}
+
+std::unique_ptr<ModuleIface::Context> JpegModule::Start() {
+  // Use ordinary pointer until past the possibility of a longjump.
+  auto* pContext = new CJpegContext();
+  if (setjmp(pContext->m_JumpMark) == -1) {
+    delete pContext;
+    return nullptr;
+  }
+
+  jpeg_create_decompress(&pContext->m_Info);
+  pContext->m_Info.src = &pContext->m_SrcMgr;
+  pContext->m_SkipSize = 0;
+  return pdfium::WrapUnique(pContext);
+}
+
+bool JpegModule::Input(Context* pContext,
+                       RetainPtr<CFX_CodecMemory> codec_memory,
+                       CFX_DIBAttribute*) {
+  pdfium::span<uint8_t> src_buf = codec_memory->GetSpan();
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  if (ctx->m_SkipSize) {
+    if (ctx->m_SkipSize > src_buf.size()) {
+      ctx->m_SrcMgr.bytes_in_buffer = 0;
+      ctx->m_SkipSize -= src_buf.size();
+      return true;
+    }
+    src_buf = src_buf.subspan(ctx->m_SkipSize);
+    ctx->m_SkipSize = 0;
+  }
+  ctx->m_SrcMgr.next_input_byte = src_buf.data();
+  ctx->m_SrcMgr.bytes_in_buffer = src_buf.size();
+  return true;
+}
+
+#ifdef PDF_ENABLE_XFA
+int JpegModule::ReadHeader(Context* pContext,
+                           int* width,
+                           int* height,
+                           int* nComps,
+                           CFX_DIBAttribute* pAttribute) {
+  ASSERT(pAttribute);
+
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  int ret = jpeg_read_header(&ctx->m_Info, TRUE);
+  if (ret == JPEG_SUSPENDED)
+    return 2;
+  if (ret != JPEG_HEADER_OK)
+    return 1;
+
+  *width = ctx->m_Info.image_width;
+  *height = ctx->m_Info.image_height;
+  *nComps = ctx->m_Info.num_components;
+  JpegLoadAttribute(ctx->m_Info, pAttribute);
+  return 0;
+}
+#endif  // PDF_ENABLE_XFA
+
+bool JpegModule::StartScanline(Context* pContext, int down_scale) {
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  ctx->m_Info.scale_denom = static_cast<unsigned int>(down_scale);
+  return !!jpeg_start_decompress(&ctx->m_Info);
+}
+
+bool JpegModule::ReadScanline(Context* pContext, unsigned char* dest_buf) {
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1);
+  return nlines == 1;
+}
+
+FX_FILESIZE JpegModule::GetAvailInput(Context* pContext) const {
+  auto* ctx = static_cast<CJpegContext*>(pContext);
+  return static_cast<FX_FILESIZE>(ctx->m_SrcMgr.bytes_in_buffer);
+}
+
+jmp_buf& JpegModule::GetJumpMark(Context* pContext) {
+  return static_cast<CJpegContext*>(pContext)->GetJumpMark();
+}
+
+#if defined(OS_WIN)
+bool JpegModule::JpegEncode(const RetainPtr<CFX_DIBBase>& pSource,
+                            uint8_t** dest_buf,
+                            size_t* dest_size) {
+  jpeg_error_mgr jerr;
+  jerr.error_exit = error_do_nothing;
+  jerr.emit_message = error_do_nothing1;
+  jerr.output_message = error_do_nothing;
+  jerr.format_message = error_do_nothing2;
+  jerr.reset_error_mgr = error_do_nothing;
+
+  jpeg_compress_struct cinfo;
+  memset(&cinfo, 0, sizeof(cinfo));
+  cinfo.err = &jerr;
+  jpeg_create_compress(&cinfo);
+  int Bpp = pSource->GetBPP() / 8;
+  uint32_t nComponents = Bpp >= 3 ? (pSource->IsCmykImage() ? 4 : 3) : 1;
+  uint32_t pitch = pSource->GetPitch();
+  uint32_t width = pdfium::base::checked_cast<uint32_t>(pSource->GetWidth());
+  uint32_t height = pdfium::base::checked_cast<uint32_t>(pSource->GetHeight());
+  FX_SAFE_UINT32 safe_buf_len = width;
+  safe_buf_len *= height;
+  safe_buf_len *= nComponents;
+  safe_buf_len += 1024;
+  if (!safe_buf_len.IsValid())
+    return false;
+
+  uint32_t dest_buf_length = safe_buf_len.ValueOrDie();
+  *dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
+  const int MIN_TRY_BUF_LEN = 1024;
+  while (!(*dest_buf) && dest_buf_length > MIN_TRY_BUF_LEN) {
+    dest_buf_length >>= 1;
+    *dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
+  }
+  if (!(*dest_buf))
+    return false;
+
+  jpeg_destination_mgr dest;
+  dest.init_destination = dest_do_nothing;
+  dest.term_destination = dest_do_nothing;
+  dest.empty_output_buffer = dest_empty;
+  dest.next_output_byte = *dest_buf;
+  dest.free_in_buffer = dest_buf_length;
+  cinfo.dest = &dest;
+  cinfo.image_width = width;
+  cinfo.image_height = height;
+  cinfo.input_components = nComponents;
+  if (nComponents == 1) {
+    cinfo.in_color_space = JCS_GRAYSCALE;
+  } else if (nComponents == 3) {
+    cinfo.in_color_space = JCS_RGB;
+  } else {
+    cinfo.in_color_space = JCS_CMYK;
+  }
+  uint8_t* line_buf = nullptr;
+  if (nComponents > 1)
+    line_buf = FX_Alloc2D(uint8_t, width, nComponents);
+
+  jpeg_set_defaults(&cinfo);
+  jpeg_start_compress(&cinfo, TRUE);
+  JSAMPROW row_pointer[1];
+  JDIMENSION row;
+  while (cinfo.next_scanline < cinfo.image_height) {
+    const uint8_t* src_scan = pSource->GetScanline(cinfo.next_scanline);
+    if (nComponents > 1) {
+      uint8_t* dest_scan = line_buf;
+      if (nComponents == 3) {
+        for (uint32_t i = 0; i < width; i++) {
+          dest_scan[0] = src_scan[2];
+          dest_scan[1] = src_scan[1];
+          dest_scan[2] = src_scan[0];
+          dest_scan += 3;
+          src_scan += Bpp;
+        }
+      } else {
+        for (uint32_t i = 0; i < pitch; i++) {
+          *dest_scan++ = ~*src_scan++;
+        }
+      }
+      row_pointer[0] = line_buf;
+    } else {
+      row_pointer[0] = const_cast<uint8_t*>(src_scan);
+    }
+    row = cinfo.next_scanline;
+    jpeg_write_scanlines(&cinfo, row_pointer, 1);
+    if (cinfo.next_scanline == row) {
+      constexpr size_t kJpegBlockSize = 1048576;
+      *dest_buf =
+          FX_Realloc(uint8_t, *dest_buf, dest_buf_length + kJpegBlockSize);
+      dest.next_output_byte = *dest_buf + dest_buf_length - dest.free_in_buffer;
+      dest_buf_length += kJpegBlockSize;
+      dest.free_in_buffer += kJpegBlockSize;
+    }
+  }
+  jpeg_finish_compress(&cinfo);
+  jpeg_destroy_compress(&cinfo);
+  FX_Free(line_buf);
+  *dest_size = dest_buf_length - static_cast<size_t>(dest.free_in_buffer);
+
+  return true;
+}
+#endif  // defined(OS_WIN)
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jpeg/jpegmodule.h b/core/fxcodec/jpeg/jpegmodule.h
new file mode 100644
index 0000000..0ecf204
--- /dev/null
+++ b/core/fxcodec/jpeg/jpegmodule.h
@@ -0,0 +1,75 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JPEG_JPEGMODULE_H_
+#define CORE_FXCODEC_JPEG_JPEGMODULE_H_
+
+#include <csetjmp>
+#include <memory>
+
+#include "build/build_config.h"
+#include "core/fxcodec/codec_module_iface.h"
+#include "third_party/base/optional.h"
+#include "third_party/base/span.h"
+
+class CFX_DIBBase;
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+class ScanlineDecoder;
+
+class JpegModule final : public ModuleIface {
+ public:
+  struct JpegImageInfo {
+    int width;
+    int height;
+    int num_components;
+    int bits_per_components;
+    bool color_transform;
+  };
+
+  std::unique_ptr<ScanlineDecoder> CreateDecoder(
+      pdfium::span<const uint8_t> src_span,
+      int width,
+      int height,
+      int nComps,
+      bool ColorTransform);
+
+  // ModuleIface:
+  FX_FILESIZE GetAvailInput(Context* pContext) const override;
+  bool Input(Context* pContext,
+             RetainPtr<CFX_CodecMemory> codec_memory,
+             CFX_DIBAttribute* pAttribute) override;
+
+  jmp_buf& GetJumpMark(Context* pContext);
+  Optional<JpegImageInfo> LoadInfo(pdfium::span<const uint8_t> src_span);
+
+  std::unique_ptr<Context> Start();
+
+#ifdef PDF_ENABLE_XFA
+  int ReadHeader(Context* pContext,
+                 int* width,
+                 int* height,
+                 int* nComps,
+                 CFX_DIBAttribute* pAttribute);
+#endif  // PDF_ENABLE_XFA
+
+  bool StartScanline(Context* pContext, int down_scale);
+  bool ReadScanline(Context* pContext, uint8_t* dest_buf);
+
+#if defined(OS_WIN)
+  static bool JpegEncode(const RetainPtr<CFX_DIBBase>& pSource,
+                         uint8_t** dest_buf,
+                         size_t* dest_size);
+#endif  // defined(OS_WIN)
+};
+
+}  // namespace fxcodec
+
+using JpegModule = fxcodec::JpegModule;
+
+#endif  // CORE_FXCODEC_JPEG_JPEGMODULE_H_
diff --git a/core/fxcodec/jpx/DEPS b/core/fxcodec/jpx/DEPS
new file mode 100644
index 0000000..d728e0f
--- /dev/null
+++ b/core/fxcodec/jpx/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/libopenjpeg20',
+]
diff --git a/core/fxcodec/jpx/cjpx_decoder.cpp b/core/fxcodec/jpx/cjpx_decoder.cpp
new file mode 100644
index 0000000..95f76ff
--- /dev/null
+++ b/core/fxcodec/jpx/cjpx_decoder.cpp
@@ -0,0 +1,572 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jpx/cjpx_decoder.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "core/fxcodec/jpx/jpx_decode_utils.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/optional.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+#if !defined(USE_SYSTEM_LIBOPENJPEG2)
+#include "third_party/libopenjpeg20/opj_malloc.h"
+#endif
+
+namespace fxcodec {
+
+namespace {
+
+// Used with std::unique_ptr to call opj_image_data_free on raw memory.
+struct OpjImageDataDeleter {
+  inline void operator()(void* ptr) const { opj_image_data_free(ptr); }
+};
+
+using ScopedOpjImageData = std::unique_ptr<int, OpjImageDataDeleter>;
+
+struct OpjImageRgbData {
+  ScopedOpjImageData r;
+  ScopedOpjImageData g;
+  ScopedOpjImageData b;
+};
+
+void fx_ignore_callback(const char* msg, void* client_data) {}
+
+opj_stream_t* fx_opj_stream_create_memory_stream(DecodeData* data) {
+  if (!data || !data->src_data || data->src_size <= 0)
+    return nullptr;
+
+  opj_stream_t* stream = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,
+                                           /*p_is_input=*/OPJ_TRUE);
+  if (!stream)
+    return nullptr;
+
+  opj_stream_set_user_data(stream, data, nullptr);
+  opj_stream_set_user_data_length(stream, data->src_size);
+  opj_stream_set_read_function(stream, opj_read_from_memory);
+  opj_stream_set_skip_function(stream, opj_skip_from_memory);
+  opj_stream_set_seek_function(stream, opj_seek_from_memory);
+  return stream;
+}
+
+Optional<OpjImageRgbData> alloc_rgb(size_t size) {
+  OpjImageRgbData data;
+  data.r.reset(static_cast<int*>(opj_image_data_alloc(size)));
+  if (!data.r)
+    return {};
+
+  data.g.reset(static_cast<int*>(opj_image_data_alloc(size)));
+  if (!data.g)
+    return {};
+
+  data.b.reset(static_cast<int*>(opj_image_data_alloc(size)));
+  if (!data.b)
+    return {};
+
+  return data;
+}
+
+void sycc_to_rgb(int offset,
+                 int upb,
+                 int y,
+                 int cb,
+                 int cr,
+                 int* out_r,
+                 int* out_g,
+                 int* out_b) {
+  cb -= offset;
+  cr -= offset;
+  *out_r = pdfium::clamp(y + static_cast<int>(1.402 * cr), 0, upb);
+  *out_g = pdfium::clamp(y - static_cast<int>(0.344 * cb + 0.714 * cr), 0, upb);
+  *out_b = pdfium::clamp(y + static_cast<int>(1.772 * cb), 0, upb);
+}
+
+void sycc444_to_rgb(opj_image_t* img) {
+  int prec = img->comps[0].prec;
+  // If we shift 31 we're going to go negative, then things go bad.
+  if (prec > 30)
+    return;
+  int offset = 1 << (prec - 1);
+  int upb = (1 << prec) - 1;
+  OPJ_UINT32 maxw =
+      std::min({img->comps[0].w, img->comps[1].w, img->comps[2].w});
+  OPJ_UINT32 maxh =
+      std::min({img->comps[0].h, img->comps[1].h, img->comps[2].h});
+  FX_SAFE_SIZE_T max_size = maxw;
+  max_size *= maxh;
+  max_size *= sizeof(int);
+  if (!max_size.IsValid())
+    return;
+
+  const int* y = img->comps[0].data;
+  const int* cb = img->comps[1].data;
+  const int* cr = img->comps[2].data;
+  if (!y || !cb || !cr)
+    return;
+
+  Optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
+  if (!data.has_value())
+    return;
+
+  int* r = data.value().r.get();
+  int* g = data.value().g.get();
+  int* b = data.value().b.get();
+  max_size /= sizeof(int);
+  for (size_t i = 0; i < max_size.ValueOrDie(); ++i)
+    sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
+
+  opj_image_data_free(img->comps[0].data);
+  opj_image_data_free(img->comps[1].data);
+  opj_image_data_free(img->comps[2].data);
+  img->comps[0].data = data.value().r.release();
+  img->comps[1].data = data.value().g.release();
+  img->comps[2].data = data.value().b.release();
+}
+
+bool sycc420_422_size_is_valid(opj_image_t* img) {
+  return img && img->comps[0].w != std::numeric_limits<OPJ_UINT32>::max() &&
+         (img->comps[0].w + 1) / 2 == img->comps[1].w &&
+         img->comps[1].w == img->comps[2].w &&
+         img->comps[1].h == img->comps[2].h;
+}
+
+bool sycc420_size_is_valid(opj_image_t* img) {
+  return sycc420_422_size_is_valid(img) &&
+         img->comps[0].h != std::numeric_limits<OPJ_UINT32>::max() &&
+         (img->comps[0].h + 1) / 2 == img->comps[1].h;
+}
+
+bool sycc420_must_extend_cbcr(OPJ_UINT32 y, OPJ_UINT32 cbcr) {
+  return (y & 1) && (cbcr == y / 2);
+}
+
+void sycc420_to_rgb(opj_image_t* img) {
+  if (!sycc420_size_is_valid(img))
+    return;
+
+  OPJ_UINT32 prec = img->comps[0].prec;
+  if (!prec)
+    return;
+
+  OPJ_UINT32 offset = 1 << (prec - 1);
+  OPJ_UINT32 upb = (1 << prec) - 1;
+  OPJ_UINT32 yw = img->comps[0].w;
+  OPJ_UINT32 yh = img->comps[0].h;
+  OPJ_UINT32 cbw = img->comps[1].w;
+  OPJ_UINT32 cbh = img->comps[1].h;
+  OPJ_UINT32 crw = img->comps[2].w;
+  bool extw = sycc420_must_extend_cbcr(yw, cbw);
+  bool exth = sycc420_must_extend_cbcr(yh, cbh);
+  FX_SAFE_UINT32 safe_size = yw;
+  safe_size *= yh;
+  safe_size *= sizeof(int);
+  if (!safe_size.IsValid())
+    return;
+
+  const int* y = img->comps[0].data;
+  const int* cb = img->comps[1].data;
+  const int* cr = img->comps[2].data;
+  if (!y || !cb || !cr)
+    return;
+
+  Optional<OpjImageRgbData> data = alloc_rgb(safe_size.ValueOrDie());
+  if (!data.has_value())
+    return;
+
+  int* r = data.value().r.get();
+  int* g = data.value().g.get();
+  int* b = data.value().b.get();
+  const int* ny = nullptr;
+  int* nr = nullptr;
+  int* ng = nullptr;
+  int* nb = nullptr;
+  OPJ_UINT32 i = 0;
+  OPJ_UINT32 j = 0;
+  for (i = 0; i < (yh & ~(OPJ_UINT32)1); i += 2) {
+    ny = y + yw;
+    nr = r + yw;
+    ng = g + yw;
+    nb = b + yw;
+    for (j = 0; j < (yw & ~(OPJ_UINT32)1); j += 2) {
+      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+      ++y;
+      ++r;
+      ++g;
+      ++b;
+      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+      ++y;
+      ++r;
+      ++g;
+      ++b;
+      sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
+      ++ny;
+      ++nr;
+      ++ng;
+      ++nb;
+      sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
+      ++ny;
+      ++nr;
+      ++ng;
+      ++nb;
+      ++cb;
+      ++cr;
+    }
+    if (j < yw) {
+      if (extw) {
+        --cb;
+        --cr;
+      }
+      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+      ++y;
+      ++r;
+      ++g;
+      ++b;
+      sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
+      ++ny;
+      ++nr;
+      ++ng;
+      ++nb;
+      ++cb;
+      ++cr;
+    }
+    y += yw;
+    r += yw;
+    g += yw;
+    b += yw;
+  }
+  if (i < yh) {
+    if (exth) {
+      cb -= cbw;
+      cr -= crw;
+    }
+    for (j = 0; j < (yw & ~(OPJ_UINT32)1); j += 2) {
+      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+      ++y;
+      ++r;
+      ++g;
+      ++b;
+      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+      ++y;
+      ++r;
+      ++g;
+      ++b;
+      ++cb;
+      ++cr;
+    }
+    if (j < yw) {
+      if (extw) {
+        --cb;
+        --cr;
+      }
+      sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+    }
+  }
+
+  opj_image_data_free(img->comps[0].data);
+  opj_image_data_free(img->comps[1].data);
+  opj_image_data_free(img->comps[2].data);
+  img->comps[0].data = data.value().r.release();
+  img->comps[1].data = data.value().g.release();
+  img->comps[2].data = data.value().b.release();
+  img->comps[1].w = yw;
+  img->comps[1].h = yh;
+  img->comps[2].w = yw;
+  img->comps[2].h = yh;
+  img->comps[1].dx = img->comps[0].dx;
+  img->comps[2].dx = img->comps[0].dx;
+  img->comps[1].dy = img->comps[0].dy;
+  img->comps[2].dy = img->comps[0].dy;
+}
+
+bool sycc422_size_is_valid(opj_image_t* img) {
+  return sycc420_422_size_is_valid(img) && img->comps[0].h == img->comps[1].h;
+}
+
+void sycc422_to_rgb(opj_image_t* img) {
+  if (!sycc422_size_is_valid(img))
+    return;
+
+  int prec = img->comps[0].prec;
+  if (prec <= 0 || prec >= 32)
+    return;
+
+  int offset = 1 << (prec - 1);
+  int upb = (1 << prec) - 1;
+  OPJ_UINT32 maxw = img->comps[0].w;
+  OPJ_UINT32 maxh = img->comps[0].h;
+  FX_SAFE_SIZE_T max_size = maxw;
+  max_size *= maxh;
+  max_size *= sizeof(int);
+  if (!max_size.IsValid())
+    return;
+
+  const int* y = img->comps[0].data;
+  const int* cb = img->comps[1].data;
+  const int* cr = img->comps[2].data;
+  if (!y || !cb || !cr)
+    return;
+
+  Optional<OpjImageRgbData> data = alloc_rgb(max_size.ValueOrDie());
+  if (!data.has_value())
+    return;
+
+  int* r = data.value().r.get();
+  int* g = data.value().g.get();
+  int* b = data.value().b.get();
+  for (uint32_t i = 0; i < maxh; ++i) {
+    OPJ_UINT32 j;
+    for (j = 0; j < (maxw & ~static_cast<OPJ_UINT32>(1)); j += 2) {
+      sycc_to_rgb(offset, upb, *y++, *cb, *cr, r++, g++, b++);
+      sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
+    }
+    if (j < maxw) {
+      sycc_to_rgb(offset, upb, *y++, *cb++, *cr++, r++, g++, b++);
+    }
+  }
+
+  opj_image_data_free(img->comps[0].data);
+  opj_image_data_free(img->comps[1].data);
+  opj_image_data_free(img->comps[2].data);
+  img->comps[0].data = data.value().r.release();
+  img->comps[1].data = data.value().g.release();
+  img->comps[2].data = data.value().b.release();
+  img->comps[1].w = maxw;
+  img->comps[1].h = maxh;
+  img->comps[2].w = maxw;
+  img->comps[2].h = maxh;
+  img->comps[1].dx = img->comps[0].dx;
+  img->comps[2].dx = img->comps[0].dx;
+  img->comps[1].dy = img->comps[0].dy;
+  img->comps[2].dy = img->comps[0].dy;
+}
+
+bool is_sycc420(const opj_image_t* img) {
+  return img->comps[0].dx == 1 && img->comps[0].dy == 1 &&
+         img->comps[1].dx == 2 && img->comps[1].dy == 2 &&
+         img->comps[2].dx == 2 && img->comps[2].dy == 2;
+}
+
+bool is_sycc422(const opj_image_t* img) {
+  return img->comps[0].dx == 1 && img->comps[0].dy == 1 &&
+         img->comps[1].dx == 2 && img->comps[1].dy == 1 &&
+         img->comps[2].dx == 2 && img->comps[2].dy == 1;
+}
+
+bool is_sycc444(const opj_image_t* img) {
+  return img->comps[0].dx == 1 && img->comps[0].dy == 1 &&
+         img->comps[1].dx == 1 && img->comps[1].dy == 1 &&
+         img->comps[2].dx == 1 && img->comps[2].dy == 1;
+}
+
+void color_sycc_to_rgb(opj_image_t* img) {
+  if (img->numcomps < 3) {
+    img->color_space = OPJ_CLRSPC_GRAY;
+    return;
+  }
+  if (is_sycc420(img))
+    sycc420_to_rgb(img);
+  else if (is_sycc422(img))
+    sycc422_to_rgb(img);
+  else if (is_sycc444(img))
+    sycc444_to_rgb(img);
+  else
+    return;
+
+  img->color_space = OPJ_CLRSPC_SRGB;
+}
+
+}  // namespace
+
+// static
+void CJPX_Decoder::Sycc420ToRgbForTesting(opj_image_t* img) {
+  sycc420_to_rgb(img);
+}
+
+CJPX_Decoder::CJPX_Decoder(ColorSpaceOption option)
+    : m_ColorSpaceOption(option) {}
+
+CJPX_Decoder::~CJPX_Decoder() {
+  if (m_Codec)
+    opj_destroy_codec(m_Codec.Release());
+  if (m_Stream)
+    opj_stream_destroy(m_Stream.Release());
+  if (m_Image)
+    opj_image_destroy(m_Image.Release());
+}
+
+bool CJPX_Decoder::Init(pdfium::span<const uint8_t> src_data) {
+  static const unsigned char szJP2Header[] = {
+      0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a};
+  if (src_data.empty() || src_data.size() < sizeof(szJP2Header))
+    return false;
+
+  m_Image = nullptr;
+  m_SrcData = src_data;
+  m_DecodeData =
+      pdfium::MakeUnique<DecodeData>(src_data.data(), src_data.size());
+  m_Stream = fx_opj_stream_create_memory_stream(m_DecodeData.get());
+  if (!m_Stream)
+    return false;
+
+  opj_set_default_decoder_parameters(&m_Parameters);
+  m_Parameters.decod_format = 0;
+  m_Parameters.cod_format = 3;
+  if (memcmp(m_SrcData.data(), szJP2Header, sizeof(szJP2Header)) == 0) {
+    m_Codec = opj_create_decompress(OPJ_CODEC_JP2);
+    m_Parameters.decod_format = 1;
+  } else {
+    m_Codec = opj_create_decompress(OPJ_CODEC_J2K);
+  }
+  if (!m_Codec)
+    return false;
+
+  if (m_ColorSpaceOption == kIndexedColorSpace)
+    m_Parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
+  opj_set_info_handler(m_Codec.Get(), fx_ignore_callback, nullptr);
+  opj_set_warning_handler(m_Codec.Get(), fx_ignore_callback, nullptr);
+  opj_set_error_handler(m_Codec.Get(), fx_ignore_callback, nullptr);
+  if (!opj_setup_decoder(m_Codec.Get(), &m_Parameters))
+    return false;
+
+  m_Image = nullptr;
+  opj_image_t* pTempImage = nullptr;
+  if (!opj_read_header(m_Stream.Get(), m_Codec.Get(), &pTempImage))
+    return false;
+
+  m_Image = pTempImage;
+  return true;
+}
+
+bool CJPX_Decoder::StartDecode() {
+  if (!m_Parameters.nb_tile_to_decode) {
+    if (!opj_set_decode_area(m_Codec.Get(), m_Image.Get(), m_Parameters.DA_x0,
+                             m_Parameters.DA_y0, m_Parameters.DA_x1,
+                             m_Parameters.DA_y1)) {
+      opj_image_destroy(m_Image.Release());
+      return false;
+    }
+    if (!(opj_decode(m_Codec.Get(), m_Stream.Get(), m_Image.Get()) &&
+          opj_end_decompress(m_Codec.Get(), m_Stream.Get()))) {
+      opj_image_destroy(m_Image.Release());
+      return false;
+    }
+  } else if (!opj_get_decoded_tile(m_Codec.Get(), m_Stream.Get(), m_Image.Get(),
+                                   m_Parameters.tile_index)) {
+    return false;
+  }
+
+  opj_stream_destroy(m_Stream.Release());
+  if (m_Image->color_space != OPJ_CLRSPC_SYCC && m_Image->numcomps == 3 &&
+      m_Image->comps[0].dx == m_Image->comps[0].dy &&
+      m_Image->comps[1].dx != 1) {
+    m_Image->color_space = OPJ_CLRSPC_SYCC;
+  } else if (m_Image->numcomps <= 2) {
+    m_Image->color_space = OPJ_CLRSPC_GRAY;
+  }
+  if (m_Image->color_space == OPJ_CLRSPC_SYCC)
+    color_sycc_to_rgb(m_Image.Get());
+
+  if (m_Image->icc_profile_buf) {
+    // TODO(palmer): Using |opj_free| here resolves the crash described in
+    // https://crbug.com/737033, but ultimately we need to harmonize the
+    // memory allocation strategy across OpenJPEG and its PDFium callers.
+#if !defined(USE_SYSTEM_LIBOPENJPEG2)
+    opj_free(m_Image->icc_profile_buf);
+#else
+    free(m_Image->icc_profile_buf);
+#endif
+    m_Image->icc_profile_buf = nullptr;
+    m_Image->icc_profile_len = 0;
+  }
+  return true;
+}
+
+CJPX_Decoder::JpxImageInfo CJPX_Decoder::GetInfo() const {
+  return {m_Image->x1, m_Image->y1, m_Image->numcomps};
+}
+
+bool CJPX_Decoder::Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb) {
+  if (m_Image->comps[0].w != m_Image->x1 || m_Image->comps[0].h != m_Image->y1)
+    return false;
+
+  if (pitch<(m_Image->comps[0].w * 8 * m_Image->numcomps + 31)>> 5 << 2)
+    return false;
+
+  if (swap_rgb && m_Image->numcomps < 3)
+    return false;
+
+  memset(dest_buf, 0xff, m_Image->y1 * pitch);
+  std::vector<uint8_t*> channel_bufs(m_Image->numcomps);
+  std::vector<int> adjust_comps(m_Image->numcomps);
+  for (uint32_t i = 0; i < m_Image->numcomps; i++) {
+    channel_bufs[i] = dest_buf + i;
+    adjust_comps[i] = m_Image->comps[i].prec - 8;
+    if (i > 0) {
+      if (m_Image->comps[i].dx != m_Image->comps[i - 1].dx ||
+          m_Image->comps[i].dy != m_Image->comps[i - 1].dy ||
+          m_Image->comps[i].prec != m_Image->comps[i - 1].prec) {
+        return false;
+      }
+    }
+  }
+  if (swap_rgb)
+    std::swap(channel_bufs[0], channel_bufs[2]);
+
+  uint32_t width = m_Image->comps[0].w;
+  uint32_t height = m_Image->comps[0].h;
+  for (uint32_t channel = 0; channel < m_Image->numcomps; ++channel) {
+    uint8_t* pChannel = channel_bufs[channel];
+    if (adjust_comps[channel] < 0) {
+      for (uint32_t row = 0; row < height; ++row) {
+        uint8_t* pScanline = pChannel + row * pitch;
+        for (uint32_t col = 0; col < width; ++col) {
+          uint8_t* pPixel = pScanline + col * m_Image->numcomps;
+          if (!m_Image->comps[channel].data)
+            continue;
+
+          int src = m_Image->comps[channel].data[row * width + col];
+          src += m_Image->comps[channel].sgnd
+                     ? 1 << (m_Image->comps[channel].prec - 1)
+                     : 0;
+          if (adjust_comps[channel] > 0) {
+            *pPixel = 0;
+          } else {
+            *pPixel = static_cast<uint8_t>(src << -adjust_comps[channel]);
+          }
+        }
+      }
+    } else {
+      for (uint32_t row = 0; row < height; ++row) {
+        uint8_t* pScanline = pChannel + row * pitch;
+        for (uint32_t col = 0; col < width; ++col) {
+          uint8_t* pPixel = pScanline + col * m_Image->numcomps;
+          if (!m_Image->comps[channel].data)
+            continue;
+
+          int src = m_Image->comps[channel].data[row * width + col];
+          src += m_Image->comps[channel].sgnd
+                     ? 1 << (m_Image->comps[channel].prec - 1)
+                     : 0;
+          if (adjust_comps[channel] - 1 < 0) {
+            *pPixel = static_cast<uint8_t>((src >> adjust_comps[channel]));
+          } else {
+            int tmpPixel = (src >> adjust_comps[channel]) +
+                           ((src >> (adjust_comps[channel] - 1)) % 2);
+            tmpPixel = pdfium::clamp(tmpPixel, 0, 255);
+            *pPixel = static_cast<uint8_t>(tmpPixel);
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jpx/cjpx_decoder.h b/core/fxcodec/jpx/cjpx_decoder.h
new file mode 100644
index 0000000..4d9883c
--- /dev/null
+++ b/core/fxcodec/jpx/cjpx_decoder.h
@@ -0,0 +1,66 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JPX_CJPX_DECODER_H_
+#define CORE_FXCODEC_JPX_CJPX_DECODER_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/span.h"
+
+#if defined(USE_SYSTEM_LIBOPENJPEG2)
+#include <openjpeg.h>
+#else
+#include "third_party/libopenjpeg20/openjpeg.h"
+#endif
+
+namespace fxcodec {
+
+struct DecodeData;
+
+class CJPX_Decoder {
+ public:
+  enum ColorSpaceOption {
+    kNoColorSpace,
+    kNormalColorSpace,
+    kIndexedColorSpace
+  };
+
+  struct JpxImageInfo {
+    uint32_t width;
+    uint32_t height;
+    uint32_t components;
+  };
+
+  static void Sycc420ToRgbForTesting(opj_image_t* img);
+
+  explicit CJPX_Decoder(ColorSpaceOption option);
+  ~CJPX_Decoder();
+
+  bool Init(pdfium::span<const uint8_t> src_data);
+  JpxImageInfo GetInfo() const;
+  bool StartDecode();
+
+  // |swap_rgb| can only be set for images with 3 or more components.
+  bool Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb);
+
+ private:
+  const ColorSpaceOption m_ColorSpaceOption;
+  pdfium::span<const uint8_t> m_SrcData;
+  UnownedPtr<opj_image_t> m_Image;
+  UnownedPtr<opj_codec_t> m_Codec;
+  std::unique_ptr<DecodeData> m_DecodeData;
+  UnownedPtr<opj_stream_t> m_Stream;
+  opj_dparameters_t m_Parameters;
+};
+
+}  // namespace fxcodec
+
+using fxcodec::CJPX_Decoder;
+
+#endif  // CORE_FXCODEC_JPX_CJPX_DECODER_H_
diff --git a/core/fxcodec/jpx/jpx_decode_utils.cpp b/core/fxcodec/jpx/jpx_decode_utils.cpp
new file mode 100644
index 0000000..d3842d7
--- /dev/null
+++ b/core/fxcodec/jpx/jpx_decode_utils.cpp
@@ -0,0 +1,91 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jpx/jpx_decode_utils.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+namespace fxcodec {
+
+OPJ_SIZE_T opj_read_from_memory(void* p_buffer,
+                                OPJ_SIZE_T nb_bytes,
+                                void* p_user_data) {
+  DecodeData* srcData = static_cast<DecodeData*>(p_user_data);
+  if (!srcData || !srcData->src_data || srcData->src_size == 0)
+    return static_cast<OPJ_SIZE_T>(-1);
+
+  // Reads at EOF return an error code.
+  if (srcData->offset >= srcData->src_size)
+    return static_cast<OPJ_SIZE_T>(-1);
+
+  OPJ_SIZE_T bufferLength = srcData->src_size - srcData->offset;
+  OPJ_SIZE_T readlength = nb_bytes < bufferLength ? nb_bytes : bufferLength;
+  memcpy(p_buffer, &srcData->src_data[srcData->offset], readlength);
+  srcData->offset += readlength;
+  return readlength;
+}
+
+OPJ_OFF_T opj_skip_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data) {
+  DecodeData* srcData = static_cast<DecodeData*>(p_user_data);
+  if (!srcData || !srcData->src_data || srcData->src_size == 0)
+    return static_cast<OPJ_OFF_T>(-1);
+
+  // Offsets are signed and may indicate a negative skip. Do not support this
+  // because of the strange return convention where either bytes skipped or
+  // -1 is returned. Following that convention, a successful relative seek of
+  // -1 bytes would be required to to give the same result as the error case.
+  if (nb_bytes < 0)
+    return static_cast<OPJ_OFF_T>(-1);
+
+  auto unsigned_nb_bytes =
+      static_cast<std::make_unsigned<OPJ_OFF_T>::type>(nb_bytes);
+  // Additionally, the offset may take us beyond the range of a size_t (e.g.
+  // 32-bit platforms). If so, just clamp at EOF.
+  if (unsigned_nb_bytes >
+      std::numeric_limits<OPJ_SIZE_T>::max() - srcData->offset) {
+    srcData->offset = srcData->src_size;
+  } else {
+    OPJ_SIZE_T checked_nb_bytes = static_cast<OPJ_SIZE_T>(unsigned_nb_bytes);
+    // Otherwise, mimic fseek() semantics to always succeed, even past EOF,
+    // clamping at EOF.  We can get away with this since we don't actually
+    // provide negative relative skips from beyond EOF back to inside the
+    // data, which would be the only reason to need to know exactly how far
+    // beyond EOF we are.
+    srcData->offset =
+        std::min(srcData->offset + checked_nb_bytes, srcData->src_size);
+  }
+  return nb_bytes;
+}
+
+OPJ_BOOL opj_seek_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data) {
+  DecodeData* srcData = static_cast<DecodeData*>(p_user_data);
+  if (!srcData || !srcData->src_data || srcData->src_size == 0)
+    return OPJ_FALSE;
+
+  // Offsets are signed and may indicate a negative position, which would
+  // be before the start of the file. Do not support this.
+  if (nb_bytes < 0)
+    return OPJ_FALSE;
+
+  auto unsigned_nb_bytes =
+      static_cast<std::make_unsigned<OPJ_OFF_T>::type>(nb_bytes);
+  // Additionally, the offset may take us beyond the range of a size_t (e.g.
+  // 32-bit platforms). If so, just clamp at EOF.
+  if (unsigned_nb_bytes > std::numeric_limits<OPJ_SIZE_T>::max()) {
+    srcData->offset = srcData->src_size;
+  } else {
+    OPJ_SIZE_T checked_nb_bytes = static_cast<OPJ_SIZE_T>(nb_bytes);
+    // Otherwise, mimic fseek() semantics to always succeed, even past EOF,
+    // again clamping at EOF.
+    srcData->offset = std::min(checked_nb_bytes, srcData->src_size);
+  }
+  return OPJ_TRUE;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jpx/jpx_decode_utils.h b/core/fxcodec/jpx/jpx_decode_utils.h
new file mode 100644
index 0000000..4403cb8
--- /dev/null
+++ b/core/fxcodec/jpx/jpx_decode_utils.h
@@ -0,0 +1,38 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JPX_JPX_DECODE_UTILS_H_
+#define CORE_FXCODEC_JPX_JPX_DECODE_UTILS_H_
+
+#include <stdint.h>
+
+#if defined(USE_SYSTEM_LIBOPENJPEG2)
+#include <openjpeg.h>
+#else
+#include "third_party/libopenjpeg20/openjpeg.h"
+#endif
+
+namespace fxcodec {
+
+struct DecodeData {
+  DecodeData(const uint8_t* data, OPJ_SIZE_T size)
+      : src_data(data), src_size(size), offset(0) {}
+
+  const uint8_t* src_data;
+  OPJ_SIZE_T src_size;
+  OPJ_SIZE_T offset;
+};
+
+/* Wrappers for C-style callbacks. */
+OPJ_SIZE_T opj_read_from_memory(void* p_buffer,
+                                OPJ_SIZE_T nb_bytes,
+                                void* p_user_data);
+OPJ_OFF_T opj_skip_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data);
+OPJ_BOOL opj_seek_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data);
+
+}  // namespace fxcodec
+
+#endif  // CORE_FXCODEC_JPX_JPX_DECODE_UTILS_H_
diff --git a/core/fxcodec/jpx/jpx_unittest.cpp b/core/fxcodec/jpx/jpx_unittest.cpp
new file mode 100644
index 0000000..def5065
--- /dev/null
+++ b/core/fxcodec/jpx/jpx_unittest.cpp
@@ -0,0 +1,451 @@
+// 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.
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "core/fxcodec/jpx/cjpx_decoder.h"
+#include "core/fxcodec/jpx/jpx_decode_utils.h"
+#include "core/fxcrt/fx_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libopenjpeg20/opj_malloc.h"
+
+namespace fxcodec {
+
+static const OPJ_OFF_T kSkipError = static_cast<OPJ_OFF_T>(-1);
+static const OPJ_SIZE_T kReadError = static_cast<OPJ_SIZE_T>(-1);
+
+static const uint8_t stream_data[] = {
+    0x00, 0x01, 0x02, 0x03,
+    0x84, 0x85, 0x86, 0x87,  // Include some hi-bytes, too.
+};
+
+TEST(fxcodec, DecodeDataNullDecodeData) {
+  uint8_t buffer[16];
+  DecodeData* ptr = nullptr;
+
+  // Error codes, not segvs, should callers pass us a nullptr pointer.
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), ptr));
+  EXPECT_EQ(kSkipError, opj_skip_from_memory(1, ptr));
+  EXPECT_FALSE(opj_seek_from_memory(1, ptr));
+}
+
+TEST(fxcodec, DecodeDataNullStream) {
+  DecodeData dd(nullptr, 0);
+  uint8_t buffer[16];
+
+  // Reads of size 0 do nothing but return an error code.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
+  EXPECT_EQ(0xbd, buffer[0]);
+
+  // Reads of nonzero size do nothing but return an error code.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
+  EXPECT_EQ(0xbd, buffer[0]);
+
+  // Skips of size 0 always return an error code.
+  EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd));
+
+  // Skips of nonzero size always return an error code.
+  EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd));
+
+  // Seeks to 0 offset return in error.
+  EXPECT_FALSE(opj_seek_from_memory(0, &dd));
+
+  // Seeks to non-zero offsets return in error.
+  EXPECT_FALSE(opj_seek_from_memory(1, &dd));
+}
+
+TEST(fxcodec, DecodeDataZeroSize) {
+  DecodeData dd(stream_data, 0);
+  uint8_t buffer[16];
+
+  // Reads of size 0 do nothing but return an error code.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
+  EXPECT_EQ(0xbd, buffer[0]);
+
+  // Reads of nonzero size do nothing but return an error code.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
+  EXPECT_EQ(0xbd, buffer[0]);
+
+  // Skips of size 0 always return an error code.
+  EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd));
+
+  // Skips of nonzero size always return an error code.
+  EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd));
+
+  // Seeks to 0 offset return in error.
+  EXPECT_FALSE(opj_seek_from_memory(0, &dd));
+
+  // Seeks to non-zero offsets return in error.
+  EXPECT_FALSE(opj_seek_from_memory(1, &dd));
+}
+
+TEST(fxcodec, DecodeDataReadInBounds) {
+  uint8_t buffer[16];
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Exact sized read in a single call.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer), &dd));
+    EXPECT_EQ(0x00, buffer[0]);
+    EXPECT_EQ(0x01, buffer[1]);
+    EXPECT_EQ(0x02, buffer[2]);
+    EXPECT_EQ(0x03, buffer[3]);
+    EXPECT_EQ(0x84, buffer[4]);
+    EXPECT_EQ(0x85, buffer[5]);
+    EXPECT_EQ(0x86, buffer[6]);
+    EXPECT_EQ(0x87, buffer[7]);
+    EXPECT_EQ(0xbd, buffer[8]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Simple read.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(2u, opj_read_from_memory(buffer, 2, &dd));
+    EXPECT_EQ(0x00, buffer[0]);
+    EXPECT_EQ(0x01, buffer[1]);
+    EXPECT_EQ(0xbd, buffer[2]);
+
+    // Read of size 0 doesn't affect things.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(0u, opj_read_from_memory(buffer, 0, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+
+    // Read exactly up to end of data.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
+    EXPECT_EQ(0x02, buffer[0]);
+    EXPECT_EQ(0x03, buffer[1]);
+    EXPECT_EQ(0x84, buffer[2]);
+    EXPECT_EQ(0x85, buffer[3]);
+    EXPECT_EQ(0x86, buffer[4]);
+    EXPECT_EQ(0x87, buffer[5]);
+    EXPECT_EQ(0xbd, buffer[6]);
+
+    // Read of size 0 at EOF is still an error.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+  }
+}
+
+TEST(fxcodec, DecodeDataReadBeyondBounds) {
+  uint8_t buffer[16];
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Read beyond bounds in a single step.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer) + 1, &dd));
+    EXPECT_EQ(0x00, buffer[0]);
+    EXPECT_EQ(0x01, buffer[1]);
+    EXPECT_EQ(0x02, buffer[2]);
+    EXPECT_EQ(0x03, buffer[3]);
+    EXPECT_EQ(0x84, buffer[4]);
+    EXPECT_EQ(0x85, buffer[5]);
+    EXPECT_EQ(0x86, buffer[6]);
+    EXPECT_EQ(0x87, buffer[7]);
+    EXPECT_EQ(0xbd, buffer[8]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Read well beyond bounds in a single step.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(8u, opj_read_from_memory(
+                      buffer, std::numeric_limits<OPJ_SIZE_T>::max(), &dd));
+    EXPECT_EQ(0x00, buffer[0]);
+    EXPECT_EQ(0x01, buffer[1]);
+    EXPECT_EQ(0x02, buffer[2]);
+    EXPECT_EQ(0x03, buffer[3]);
+    EXPECT_EQ(0x84, buffer[4]);
+    EXPECT_EQ(0x85, buffer[5]);
+    EXPECT_EQ(0x86, buffer[6]);
+    EXPECT_EQ(0x87, buffer[7]);
+    EXPECT_EQ(0xbd, buffer[8]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Read of size 6 gets first 6 bytes.
+    // rest of buffer intact.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
+    EXPECT_EQ(0x00, buffer[0]);
+    EXPECT_EQ(0x01, buffer[1]);
+    EXPECT_EQ(0x02, buffer[2]);
+    EXPECT_EQ(0x03, buffer[3]);
+    EXPECT_EQ(0x84, buffer[4]);
+    EXPECT_EQ(0x85, buffer[5]);
+    EXPECT_EQ(0xbd, buffer[6]);
+
+    // Read of size 6 gets remaining two bytes.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(2u, opj_read_from_memory(buffer, 6, &dd));
+    EXPECT_EQ(0x86, buffer[0]);
+    EXPECT_EQ(0x87, buffer[1]);
+    EXPECT_EQ(0xbd, buffer[2]);
+
+    // Read of 6 more gets nothing and leaves rest of buffer intact.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 6, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+  }
+}
+
+// Note: Some care needs to be taken here because the skip/seek functions
+// take OPJ_OFF_T's as arguments, which are typically a signed type.
+TEST(fxcodec, DecodeDataSkip) {
+  uint8_t buffer[16];
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Skiping within buffer is allowed.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(1u, opj_skip_from_memory(1, &dd));
+    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0x01, buffer[0]);
+    EXPECT_EQ(0xbd, buffer[1]);
+
+    // Skiping 0 bytes changes nothing.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(0, opj_skip_from_memory(0, &dd));
+    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0x02, buffer[0]);
+    EXPECT_EQ(0xbd, buffer[1]);
+
+    // Skiping to EOS-1 is possible.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
+    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0x87, buffer[0]);
+    EXPECT_EQ(0xbd, buffer[1]);
+
+    // Next read fails.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Skiping directly to EOS is allowed.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
+
+    // Next read fails.
+    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Skipping beyond end of stream is allowed and returns full distance.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(9u, opj_skip_from_memory(9, &dd));
+
+    // Next read fails.
+    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Skipping way beyond EOS is allowd, doesn't wrap, and returns
+    // full distance.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
+    EXPECT_EQ(std::numeric_limits<OPJ_OFF_T>::max(),
+              opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
+
+    // Next read fails. If it succeeds, it may mean we wrapped.
+    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Negative skip within buffer not is allowed, position unchanged.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
+    EXPECT_EQ(kSkipError, opj_skip_from_memory(-2, &dd));
+
+    // Next read succeeds as if nothing has happenned.
+    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0x84, buffer[0]);
+    EXPECT_EQ(0xbd, buffer[1]);
+
+    // Negative skip before buffer is not allowed, position unchanged.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
+
+    // Next read succeeds as if nothing has happenned.
+    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0x85, buffer[0]);
+    EXPECT_EQ(0xbd, buffer[1]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Negative skip way before buffer is not allowed, doesn't wrap
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
+    EXPECT_EQ(kSkipError,
+              opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
+
+    // Next read succeeds. If it fails, it may mean we wrapped.
+    EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0x84, buffer[0]);
+    EXPECT_EQ(0xbd, buffer[1]);
+  }
+  {
+    DecodeData dd(stream_data, sizeof(stream_data));
+
+    // Negative skip after EOS isn't alowed, still EOS.
+    memset(buffer, 0xbd, sizeof(buffer));
+    EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
+    EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
+
+    // Next read fails.
+    EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+    EXPECT_EQ(0xbd, buffer[0]);
+  }
+}
+
+TEST(fxcodec, DecodeDataSeek) {
+  uint8_t buffer[16];
+  DecodeData dd(stream_data, sizeof(stream_data));
+
+  // Seeking within buffer is allowed and read succeeds
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_TRUE(opj_seek_from_memory(1, &dd));
+  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0x01, buffer[0]);
+  EXPECT_EQ(0xbd, buffer[1]);
+
+  // Seeking before start returns error leaving position unchanged.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_FALSE(opj_seek_from_memory(-1, &dd));
+  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0x02, buffer[0]);
+  EXPECT_EQ(0xbd, buffer[1]);
+
+  // Seeking way before start returns error leaving position unchanged.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_FALSE(
+      opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
+  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0x03, buffer[0]);
+  EXPECT_EQ(0xbd, buffer[1]);
+
+  // Seeking exactly to EOS is allowed but read fails.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_TRUE(opj_seek_from_memory(8, &dd));
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0xbd, buffer[0]);
+
+  // Seeking back to zero offset is allowed and read succeeds.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_TRUE(opj_seek_from_memory(0, &dd));
+  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0x00, buffer[0]);
+  EXPECT_EQ(0xbd, buffer[1]);
+
+  // Seeking beyond end of stream is allowed but read fails.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_TRUE(opj_seek_from_memory(16, &dd));
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0xbd, buffer[0]);
+
+  // Seeking within buffer after seek past EOF restores good state.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_TRUE(opj_seek_from_memory(4, &dd));
+  EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0x84, buffer[0]);
+  EXPECT_EQ(0xbd, buffer[1]);
+
+  // Seeking way beyond EOS is allowed, doesn't wrap, and read fails.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_TRUE(opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
+  EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
+  EXPECT_EQ(0xbd, buffer[0]);
+}
+
+TEST(fxcodec, YUV420ToRGB) {
+  opj_image_comp_t u;
+  memset(&u, 0, sizeof(u));
+  u.dx = 1;
+  u.dy = 1;
+  u.w = 16;
+  u.h = 16;
+  u.prec = 8;
+  u.bpp = 8;
+  opj_image_comp_t v;
+  memset(&v, 0, sizeof(v));
+  v.dx = 1;
+  v.dy = 1;
+  v.w = 16;
+  v.h = 16;
+  v.prec = 8;
+  v.bpp = 8;
+  opj_image_comp_t y;
+  memset(&y, 0, sizeof(y));
+  y.dx = 1;
+  y.dy = 1;
+  y.prec = 8;
+  y.bpp = 8;
+  opj_image_t img;
+  memset(&img, 0, sizeof(img));
+  img.numcomps = 3;
+  img.color_space = OPJ_CLRSPC_SYCC;
+  img.comps = FX_Alloc(opj_image_comp_t, 3);
+  const struct {
+    OPJ_UINT32 w;
+    bool expected;
+  } cases[] = {{0, false}, {1, false},  {30, false}, {31, true},
+               {32, true}, {33, false}, {34, false}, {UINT_MAX, false}};
+  for (const auto& testcase : cases) {
+    y.w = testcase.w;
+    y.h = y.w;
+    img.x1 = y.w;
+    img.y1 = y.h;
+    y.data = static_cast<OPJ_INT32*>(
+        opj_image_data_alloc(y.w * y.h * sizeof(OPJ_INT32)));
+    v.data = static_cast<OPJ_INT32*>(
+        opj_image_data_alloc(v.w * v.h * sizeof(OPJ_INT32)));
+    u.data = static_cast<OPJ_INT32*>(
+        opj_image_data_alloc(u.w * u.h * sizeof(OPJ_INT32)));
+    memset(y.data, 1, y.w * y.h * sizeof(OPJ_INT32));
+    memset(u.data, 0, u.w * u.h * sizeof(OPJ_INT32));
+    memset(v.data, 0, v.w * v.h * sizeof(OPJ_INT32));
+    img.comps[0] = y;
+    img.comps[1] = u;
+    img.comps[2] = v;
+    CJPX_Decoder::Sycc420ToRgbForTesting(&img);
+    if (testcase.expected) {
+      EXPECT_EQ(img.comps[0].w, img.comps[1].w);
+      EXPECT_EQ(img.comps[0].h, img.comps[1].h);
+      EXPECT_EQ(img.comps[0].w, img.comps[2].w);
+      EXPECT_EQ(img.comps[0].h, img.comps[2].h);
+    } else {
+      EXPECT_NE(img.comps[0].w, img.comps[1].w);
+      EXPECT_NE(img.comps[0].h, img.comps[1].h);
+      EXPECT_NE(img.comps[0].w, img.comps[2].w);
+      EXPECT_NE(img.comps[0].h, img.comps[2].h);
+    }
+    opj_image_data_free(img.comps[0].data);
+    opj_image_data_free(img.comps[1].data);
+    opj_image_data_free(img.comps[2].data);
+  }
+  FX_Free(img.comps);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jpx/jpxmodule.cpp b/core/fxcodec/jpx/jpxmodule.cpp
new file mode 100644
index 0000000..4229dc5
--- /dev/null
+++ b/core/fxcodec/jpx/jpxmodule.cpp
@@ -0,0 +1,24 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/jpx/jpxmodule.h"
+
+#include "third_party/base/ptr_util.h"
+
+namespace fxcodec {
+
+// static
+std::unique_ptr<CJPX_Decoder> JpxModule::CreateDecoder(
+    pdfium::span<const uint8_t> src_span,
+    CJPX_Decoder::ColorSpaceOption option) {
+  auto decoder = pdfium::MakeUnique<CJPX_Decoder>(option);
+  if (!decoder->Init(src_span))
+    return nullptr;
+
+  return decoder;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/jpx/jpxmodule.h b/core/fxcodec/jpx/jpxmodule.h
new file mode 100644
index 0000000..0f2fb71
--- /dev/null
+++ b/core/fxcodec/jpx/jpxmodule.h
@@ -0,0 +1,34 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_JPX_JPXMODULE_H_
+#define CORE_FXCODEC_JPX_JPXMODULE_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcodec/jpx/cjpx_decoder.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
+
+namespace fxcodec {
+
+class JpxModule {
+ public:
+  static std::unique_ptr<CJPX_Decoder> CreateDecoder(
+      pdfium::span<const uint8_t> src_span,
+      CJPX_Decoder::ColorSpaceOption option);
+
+  JpxModule() = delete;
+  JpxModule(const JpxModule&) = delete;
+  JpxModule& operator=(const JpxModule&) = delete;
+};
+
+}  // namespace fxcodec
+
+using JpxModule = fxcodec::JpxModule;
+
+#endif  // CORE_FXCODEC_JPX_JPXMODULE_H_
diff --git a/core/fxcodec/lbmp/fx_bmp.cpp b/core/fxcodec/lbmp/fx_bmp.cpp
deleted file mode 100644
index 73be66e..0000000
--- a/core/fxcodec/lbmp/fx_bmp.cpp
+++ /dev/null
@@ -1,671 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcodec/lbmp/fx_bmp.h"
-
-#include <algorithm>
-#include <limits>
-
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-
-static_assert(sizeof(BmpFileHeader) == 14,
-              "BmpFileHeader should have a size of 14");
-
-namespace {
-
-const size_t kBmpCoreHeaderSize = 12;
-const size_t kBmpInfoHeaderSize = 40;
-
-uint8_t HalfRoundUp(uint8_t value) {
-  uint16_t value16 = value;
-  return static_cast<uint8_t>((value16 + 1) / 2);
-}
-
-}  // namespace
-
-BMPDecompressor::BMPDecompressor()
-    : context_ptr(nullptr),
-      next_in(nullptr),
-      header_offset(0),
-      width(0),
-      height(0),
-      compress_flag(0),
-      components(0),
-      src_row_bytes(0),
-      out_row_bytes(0),
-      bitCounts(0),
-      color_used(0),
-      imgTB_flag(false),
-      pal_num(0),
-      pal_type(0),
-      data_size(0),
-      img_data_offset(0),
-      img_ifh_size(0),
-      row_num(0),
-      col_num(0),
-      dpi_x(0),
-      dpi_y(0),
-      mask_red(0),
-      mask_green(0),
-      mask_blue(0),
-      avail_in(0),
-      skip_size(0),
-      decode_status(BMP_D_STATUS_HEADER) {}
-
-BMPDecompressor::~BMPDecompressor() {}
-
-void BMPDecompressor::Error() {
-  longjmp(jmpbuf, 1);
-}
-
-void BMPDecompressor::ReadScanline(uint32_t row_num,
-                                   const std::vector<uint8_t>& row_buf) {
-  auto* p = reinterpret_cast<CBmpContext*>(context_ptr);
-  p->m_pDelegate->BmpReadScanline(row_num, row_buf);
-}
-
-bool BMPDecompressor::GetDataPosition(uint32_t rcd_pos) {
-  auto* p = reinterpret_cast<CBmpContext*>(context_ptr);
-  return p->m_pDelegate->BmpInputImagePositionBuf(rcd_pos);
-}
-
-int32_t BMPDecompressor::ReadHeader() {
-  uint32_t skip_size_org = skip_size;
-  if (decode_status == BMP_D_STATUS_HEADER) {
-    BmpFileHeader* pBmp_header = nullptr;
-    if (!ReadData(reinterpret_cast<uint8_t**>(&pBmp_header),
-                  sizeof(BmpFileHeader))) {
-      return 2;
-    }
-
-    pBmp_header->bfType =
-        FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&pBmp_header->bfType));
-    pBmp_header->bfOffBits = FXDWORD_GET_LSBFIRST(
-        reinterpret_cast<uint8_t*>(&pBmp_header->bfOffBits));
-    data_size =
-        FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&pBmp_header->bfSize));
-    if (pBmp_header->bfType != BMP_SIGNATURE) {
-      Error();
-      NOTREACHED();
-    }
-    if (avail_in < sizeof(uint32_t)) {
-      skip_size = skip_size_org;
-      return 2;
-    }
-    img_ifh_size =
-        FXDWORD_GET_LSBFIRST(static_cast<uint8_t*>(next_in + skip_size));
-    pal_type = 0;
-    static_assert(sizeof(BmpCoreHeader) == kBmpCoreHeaderSize,
-                  "BmpCoreHeader has wrong size");
-    static_assert(sizeof(BmpInfoHeader) == kBmpInfoHeaderSize,
-                  "BmpInfoHeader has wrong size");
-    switch (img_ifh_size) {
-      case kBmpCoreHeaderSize: {
-        pal_type = 1;
-        BmpCoreHeader* pBmp_core_header = nullptr;
-        if (!ReadData(reinterpret_cast<uint8_t**>(&pBmp_core_header),
-                      img_ifh_size)) {
-          skip_size = skip_size_org;
-          return 2;
-        }
-        width = FXWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_core_header->bcWidth));
-        height = FXWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_core_header->bcHeight));
-        bitCounts = FXWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_core_header->bcBitCount));
-        compress_flag = BMP_RGB;
-        imgTB_flag = false;
-      } break;
-      case kBmpInfoHeaderSize: {
-        BmpInfoHeader* pBmp_info_header = nullptr;
-        if (!ReadData(reinterpret_cast<uint8_t**>(&pBmp_info_header),
-                      img_ifh_size)) {
-          skip_size = skip_size_org;
-          return 2;
-        }
-        width = FXDWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_info_header->biWidth));
-        int32_t signed_height = FXDWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_info_header->biHeight));
-        bitCounts = FXWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_info_header->biBitCount));
-        compress_flag = FXDWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_info_header->biCompression));
-        color_used = FXDWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_info_header->biClrUsed));
-        dpi_x = static_cast<int32_t>(FXDWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_info_header->biXPelsPerMeter)));
-        dpi_y = static_cast<int32_t>(FXDWORD_GET_LSBFIRST(
-            reinterpret_cast<uint8_t*>(&pBmp_info_header->biYPelsPerMeter)));
-        SetHeight(signed_height);
-      } break;
-      default: {
-        if (img_ifh_size >
-            std::min(kBmpInfoHeaderSize, sizeof(BmpInfoHeader))) {
-          BmpInfoHeader* pBmp_info_header = nullptr;
-          if (!ReadData(reinterpret_cast<uint8_t**>(&pBmp_info_header),
-                        img_ifh_size)) {
-            skip_size = skip_size_org;
-            return 2;
-          }
-          uint16_t biPlanes;
-          width = FXDWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biWidth));
-          int32_t signed_height = FXDWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biHeight));
-          bitCounts = FXWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biBitCount));
-          compress_flag = FXDWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biCompression));
-          color_used = FXDWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biClrUsed));
-          biPlanes = FXWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biPlanes));
-          dpi_x = FXDWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biXPelsPerMeter));
-          dpi_y = FXDWORD_GET_LSBFIRST(
-              reinterpret_cast<uint8_t*>(&pBmp_info_header->biYPelsPerMeter));
-          SetHeight(signed_height);
-          if (compress_flag == BMP_RGB && biPlanes == 1 && color_used == 0)
-            break;
-        }
-        Error();
-        NOTREACHED();
-      }
-    }
-    if (width > BMP_MAX_WIDTH || compress_flag > BMP_BITFIELDS) {
-      Error();
-      NOTREACHED();
-    }
-    switch (bitCounts) {
-      case 1:
-      case 4:
-      case 8:
-      case 16:
-      case 24: {
-        if (color_used > 1U << bitCounts) {
-          Error();
-          NOTREACHED();
-        }
-      }
-      case 32:
-        break;
-      default:
-        Error();
-        NOTREACHED();
-    }
-    src_row_bytes = BMP_WIDTHBYTES(width, bitCounts);
-    switch (bitCounts) {
-      case 1:
-      case 4:
-      case 8:
-        out_row_bytes = BMP_WIDTHBYTES(width, 8);
-        components = 1;
-        break;
-      case 16:
-      case 24:
-        out_row_bytes = BMP_WIDTHBYTES(width, 24);
-        components = 3;
-        break;
-      case 32:
-        out_row_bytes = src_row_bytes;
-        components = 4;
-        break;
-    }
-    out_row_buffer.clear();
-
-    if (out_row_bytes <= 0) {
-      Error();
-      NOTREACHED();
-    }
-
-    out_row_buffer.resize(out_row_bytes);
-    SaveDecodingStatus(BMP_D_STATUS_PAL);
-  }
-  if (decode_status == BMP_D_STATUS_PAL) {
-    skip_size_org = skip_size;
-    if (compress_flag == BMP_BITFIELDS) {
-      if (bitCounts != 16 && bitCounts != 32) {
-        Error();
-        NOTREACHED();
-      }
-      uint32_t* mask;
-      if (ReadData(reinterpret_cast<uint8_t**>(&mask), 3 * sizeof(uint32_t)) ==
-          nullptr) {
-        skip_size = skip_size_org;
-        return 2;
-      }
-      mask_red = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&mask[0]));
-      mask_green = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&mask[1]));
-      mask_blue = FXDWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&mask[2]));
-      if (mask_red & mask_green || mask_red & mask_blue ||
-          mask_green & mask_blue) {
-        Error();
-        NOTREACHED();
-      }
-      header_offset = std::max(header_offset, 26 + img_ifh_size);
-      SaveDecodingStatus(BMP_D_STATUS_DATA_PRE);
-      return 1;
-    } else if (bitCounts == 16) {
-      mask_red = 0x7C00;
-      mask_green = 0x03E0;
-      mask_blue = 0x001F;
-    }
-    pal_num = 0;
-    if (bitCounts < 16) {
-      pal_num = 1 << bitCounts;
-      if (color_used != 0)
-        pal_num = color_used;
-      uint8_t* src_pal_ptr = nullptr;
-      uint32_t src_pal_size = pal_num * (pal_type ? 3 : 4);
-      if (ReadData(&src_pal_ptr, src_pal_size) == nullptr) {
-        skip_size = skip_size_org;
-        return 2;
-      }
-      palette.resize(pal_num);
-      int32_t src_pal_index = 0;
-      if (pal_type == BMP_PAL_OLD) {
-        while (src_pal_index < pal_num) {
-          palette[src_pal_index++] = BMP_PAL_ENCODE(
-              0x00, src_pal_ptr[2], src_pal_ptr[1], src_pal_ptr[0]);
-          src_pal_ptr += 3;
-        }
-      } else {
-        while (src_pal_index < pal_num) {
-          palette[src_pal_index++] = BMP_PAL_ENCODE(
-              src_pal_ptr[3], src_pal_ptr[2], src_pal_ptr[1], src_pal_ptr[0]);
-          src_pal_ptr += 4;
-        }
-      }
-    }
-    header_offset = std::max(header_offset,
-                             14 + img_ifh_size + pal_num * (pal_type ? 3 : 4));
-    SaveDecodingStatus(BMP_D_STATUS_DATA_PRE);
-  }
-  return 1;
-}
-
-bool BMPDecompressor::ValidateFlag() const {
-  switch (compress_flag) {
-    case BMP_RGB:
-    case BMP_BITFIELDS:
-    case BMP_RLE8:
-    case BMP_RLE4:
-      return true;
-    default:
-      return false;
-  }
-}
-
-int32_t BMPDecompressor::DecodeImage() {
-  if (decode_status == BMP_D_STATUS_DATA_PRE) {
-    avail_in = 0;
-    if (!GetDataPosition(header_offset)) {
-      decode_status = BMP_D_STATUS_TAIL;
-      Error();
-      NOTREACHED();
-    }
-    row_num = 0;
-    SaveDecodingStatus(BMP_D_STATUS_DATA);
-  }
-  if (decode_status != BMP_D_STATUS_DATA || !ValidateFlag()) {
-    Error();
-    NOTREACHED();
-  }
-  switch (compress_flag) {
-    case BMP_RGB:
-    case BMP_BITFIELDS:
-      return DecodeRGB();
-    case BMP_RLE8:
-      return DecodeRLE8();
-    case BMP_RLE4:
-      return DecodeRLE4();
-    default:
-      return 0;
-  }
-}
-
-bool BMPDecompressor::ValidateColorIndex(uint8_t val) {
-  if (val >= pal_num) {
-    Error();
-    NOTREACHED();
-  }
-  return true;
-}
-
-int32_t BMPDecompressor::DecodeRGB() {
-  uint8_t* des_buf = nullptr;
-  while (row_num < height) {
-    size_t idx = 0;
-    if (!ReadData(&des_buf, src_row_bytes))
-      return 2;
-
-    SaveDecodingStatus(BMP_D_STATUS_DATA);
-    switch (bitCounts) {
-      case 1: {
-        for (uint32_t col = 0; col < width; ++col)
-          out_row_buffer[idx++] =
-              des_buf[col >> 3] & (0x80 >> (col % 8)) ? 0x01 : 0x00;
-      } break;
-      case 4: {
-        for (uint32_t col = 0; col < width; ++col) {
-          out_row_buffer[idx++] = (col & 0x01)
-                                      ? (des_buf[col >> 1] & 0x0F)
-                                      : ((des_buf[col >> 1] & 0xF0) >> 4);
-        }
-      } break;
-      case 16: {
-        uint16_t* buf = (uint16_t*)des_buf;
-        uint8_t blue_bits = 0;
-        uint8_t green_bits = 0;
-        uint8_t red_bits = 0;
-        for (int32_t i = 0; i < 16; i++) {
-          if ((mask_blue >> i) & 0x01)
-            blue_bits++;
-          if ((mask_green >> i) & 0x01)
-            green_bits++;
-          if ((mask_red >> i) & 0x01)
-            red_bits++;
-        }
-        green_bits += blue_bits;
-        red_bits += green_bits;
-        if (blue_bits > 8 || green_bits < 8 || red_bits < 8)
-          return 2;
-        blue_bits = 8 - blue_bits;
-        green_bits -= 8;
-        red_bits -= 8;
-        for (uint32_t col = 0; col < width; ++col) {
-          *buf = FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(buf));
-          out_row_buffer[idx++] =
-              static_cast<uint8_t>((*buf & mask_blue) << blue_bits);
-          out_row_buffer[idx++] =
-              static_cast<uint8_t>((*buf & mask_green) >> green_bits);
-          out_row_buffer[idx++] =
-              static_cast<uint8_t>((*buf++ & mask_red) >> red_bits);
-        }
-      } break;
-      case 8:
-      case 24:
-      case 32:
-        std::copy(des_buf, des_buf + src_row_bytes, out_row_buffer.begin());
-        idx += src_row_bytes;
-        break;
-    }
-    for (uint8_t byte : out_row_buffer) {
-      if (!ValidateColorIndex(byte))
-        return 0;
-    }
-    ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++),
-                 out_row_buffer);
-  }
-  SaveDecodingStatus(BMP_D_STATUS_TAIL);
-  return 1;
-}
-
-int32_t BMPDecompressor::DecodeRLE8() {
-  uint8_t* first_byte_ptr = nullptr;
-  uint8_t* second_byte_ptr = nullptr;
-  col_num = 0;
-  while (true) {
-    uint32_t skip_size_org = skip_size;
-    if (!ReadData(&first_byte_ptr, 1))
-      return 2;
-
-    switch (*first_byte_ptr) {
-      case RLE_MARKER: {
-        if (!ReadData(&first_byte_ptr, 1)) {
-          skip_size = skip_size_org;
-          return 2;
-        }
-        switch (*first_byte_ptr) {
-          case RLE_EOL: {
-            if (row_num >= height) {
-              SaveDecodingStatus(BMP_D_STATUS_TAIL);
-              Error();
-              NOTREACHED();
-            }
-            ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++),
-                         out_row_buffer);
-            col_num = 0;
-            std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0);
-            SaveDecodingStatus(BMP_D_STATUS_DATA);
-            continue;
-          }
-          case RLE_EOI: {
-            if (row_num < height) {
-              ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++),
-                           out_row_buffer);
-            }
-            SaveDecodingStatus(BMP_D_STATUS_TAIL);
-            return 1;
-          }
-          case RLE_DELTA: {
-            uint8_t* delta_ptr;
-            if (!ReadData(&delta_ptr, 2)) {
-              skip_size = skip_size_org;
-              return 2;
-            }
-            col_num += delta_ptr[0];
-            size_t bmp_row_num_next = row_num + delta_ptr[1];
-            if (col_num >= out_row_bytes || bmp_row_num_next >= height) {
-              Error();
-              NOTREACHED();
-            }
-            while (row_num < bmp_row_num_next) {
-              std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0);
-              ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++),
-                           out_row_buffer);
-            }
-          } break;
-          default: {
-            int32_t avail_size = out_row_bytes - col_num;
-            if (!avail_size ||
-                static_cast<int32_t>(*first_byte_ptr) > avail_size) {
-              Error();
-              NOTREACHED();
-            }
-            if (!ReadData(&second_byte_ptr, *first_byte_ptr & 1
-                                                ? *first_byte_ptr + 1
-                                                : *first_byte_ptr)) {
-              skip_size = skip_size_org;
-              return 2;
-            }
-            std::copy(second_byte_ptr, second_byte_ptr + *first_byte_ptr,
-                      out_row_buffer.begin() + col_num);
-            for (size_t i = col_num; i < col_num + *first_byte_ptr; ++i) {
-              if (!ValidateColorIndex(out_row_buffer[i]))
-                return 0;
-            }
-            col_num += *first_byte_ptr;
-          }
-        }
-      } break;
-      default: {
-        int32_t avail_size = out_row_bytes - col_num;
-        if (!avail_size || static_cast<int32_t>(*first_byte_ptr) > avail_size) {
-          Error();
-          NOTREACHED();
-        }
-        if (!ReadData(&second_byte_ptr, 1)) {
-          skip_size = skip_size_org;
-          return 2;
-        }
-        std::fill(out_row_buffer.begin() + col_num,
-                  out_row_buffer.begin() + col_num + *first_byte_ptr,
-                  *second_byte_ptr);
-        if (!ValidateColorIndex(out_row_buffer[col_num]))
-          return 0;
-        col_num += *first_byte_ptr;
-      }
-    }
-  }
-  Error();
-  NOTREACHED();
-}
-
-int32_t BMPDecompressor::DecodeRLE4() {
-  uint8_t* first_byte_ptr = nullptr;
-  uint8_t* second_byte_ptr = nullptr;
-  col_num = 0;
-  while (true) {
-    uint32_t skip_size_org = skip_size;
-    if (!ReadData(&first_byte_ptr, 1))
-      return 2;
-
-    switch (*first_byte_ptr) {
-      case RLE_MARKER: {
-        if (!ReadData(&first_byte_ptr, 1)) {
-          skip_size = skip_size_org;
-          return 2;
-        }
-        switch (*first_byte_ptr) {
-          case RLE_EOL: {
-            if (row_num >= height) {
-              SaveDecodingStatus(BMP_D_STATUS_TAIL);
-              Error();
-              NOTREACHED();
-            }
-            ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++),
-                         out_row_buffer);
-            col_num = 0;
-            std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0);
-            SaveDecodingStatus(BMP_D_STATUS_DATA);
-            continue;
-          }
-          case RLE_EOI: {
-            if (row_num < height) {
-              ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++),
-                           out_row_buffer);
-            }
-            SaveDecodingStatus(BMP_D_STATUS_TAIL);
-            return 1;
-          }
-          case RLE_DELTA: {
-            uint8_t* delta_ptr;
-            if (!ReadData(&delta_ptr, 2)) {
-              skip_size = skip_size_org;
-              return 2;
-            }
-            col_num += delta_ptr[0];
-            size_t bmp_row_num_next = row_num + delta_ptr[1];
-            if (col_num >= out_row_bytes || bmp_row_num_next >= height) {
-              Error();
-              NOTREACHED();
-            }
-            while (row_num < bmp_row_num_next) {
-              std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0);
-              ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++),
-                           out_row_buffer);
-            }
-          } break;
-          default: {
-            int32_t avail_size = out_row_bytes - col_num;
-            if (!avail_size) {
-              Error();
-              NOTREACHED();
-            }
-            uint8_t size = HalfRoundUp(*first_byte_ptr);
-            if (static_cast<int32_t>(*first_byte_ptr) > avail_size) {
-              if (size + (col_num >> 1) > src_row_bytes) {
-                Error();
-                NOTREACHED();
-              }
-              *first_byte_ptr = avail_size - 1;
-            }
-            if (!ReadData(&second_byte_ptr, size & 1 ? size + 1 : size)) {
-              skip_size = skip_size_org;
-              return 2;
-            }
-            for (uint8_t i = 0; i < *first_byte_ptr; i++) {
-              uint8_t color = (i & 0x01) ? (*second_byte_ptr++ & 0x0F)
-                                         : (*second_byte_ptr & 0xF0) >> 4;
-              if (!ValidateColorIndex(color))
-                return 0;
-
-              out_row_buffer[col_num++] = color;
-            }
-          }
-        }
-      } break;
-      default: {
-        int32_t avail_size = out_row_bytes - col_num;
-        if (!avail_size) {
-          Error();
-          NOTREACHED();
-        }
-        if (static_cast<int32_t>(*first_byte_ptr) > avail_size) {
-          uint8_t size = HalfRoundUp(*first_byte_ptr);
-          if (size + (col_num >> 1) > src_row_bytes) {
-            Error();
-            NOTREACHED();
-          }
-          *first_byte_ptr = avail_size - 1;
-        }
-        if (!ReadData(&second_byte_ptr, 1)) {
-          skip_size = skip_size_org;
-          return 2;
-        }
-        for (uint8_t i = 0; i < *first_byte_ptr; i++) {
-          uint8_t second_byte = *second_byte_ptr;
-          second_byte =
-              i & 0x01 ? (second_byte & 0x0F) : (second_byte & 0xF0) >> 4;
-          if (!ValidateColorIndex(second_byte))
-            return 0;
-          out_row_buffer[col_num++] = second_byte;
-        }
-      }
-    }
-  }
-  Error();
-  NOTREACHED();
-}
-
-uint8_t* BMPDecompressor::ReadData(uint8_t** des_buf, uint32_t data_size) {
-  if (avail_in < skip_size + data_size)
-    return nullptr;
-
-  *des_buf = next_in + skip_size;
-  skip_size += data_size;
-  return *des_buf;
-}
-
-void BMPDecompressor::SaveDecodingStatus(int32_t status) {
-  decode_status = status;
-  next_in += skip_size;
-  avail_in -= skip_size;
-  skip_size = 0;
-}
-
-void BMPDecompressor::SetInputBuffer(uint8_t* src_buf, uint32_t src_size) {
-  next_in = src_buf;
-  avail_in = src_size;
-  skip_size = 0;
-}
-
-uint32_t BMPDecompressor::GetAvailInput(uint8_t** avail_buf) {
-  if (avail_buf) {
-    *avail_buf = nullptr;
-    if (avail_in > 0)
-      *avail_buf = next_in;
-  }
-  return avail_in;
-}
-
-void BMPDecompressor::SetHeight(int32_t signed_height) {
-  if (signed_height >= 0) {
-    height = signed_height;
-    return;
-  }
-  if (signed_height == std::numeric_limits<int>::min()) {
-    Error();
-    NOTREACHED();
-  }
-  height = -signed_height;
-  imgTB_flag = true;
-}
diff --git a/core/fxcodec/lbmp/fx_bmp.h b/core/fxcodec/lbmp/fx_bmp.h
deleted file mode 100644
index 9dfa849..0000000
--- a/core/fxcodec/lbmp/fx_bmp.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCODEC_LBMP_FX_BMP_H_
-#define CORE_FXCODEC_LBMP_FX_BMP_H_
-
-#include <setjmp.h>
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/codec/ccodec_bmpmodule.h"
-
-#define BMP_WIDTHBYTES(width, bitCount) ((width * bitCount) + 31) / 32 * 4
-#define BMP_PAL_ENCODE(a, r, g, b) \
-  (((uint32_t)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
-#define BMP_D_STATUS_HEADER 0x01
-#define BMP_D_STATUS_PAL 0x02
-#define BMP_D_STATUS_DATA_PRE 0x03
-#define BMP_D_STATUS_DATA 0x04
-#define BMP_D_STATUS_TAIL 0x00
-#define BMP_SIGNATURE 0x4D42
-#define BMP_PAL_OLD 1
-#define RLE_MARKER 0
-#define RLE_EOL 0
-#define RLE_EOI 1
-#define RLE_DELTA 2
-#define BMP_RGB 0L
-#define BMP_RLE8 1L
-#define BMP_RLE4 2L
-#define BMP_BITFIELDS 3L
-// Limit width to (MAXINT32 - 31) / 32
-#define BMP_MAX_WIDTH 67108863
-#pragma pack(1)
-typedef struct tagBmpFileHeader {
-  uint16_t bfType;
-  uint32_t bfSize;
-  uint16_t bfReserved1;
-  uint16_t bfReserved2;
-  uint32_t bfOffBits;
-} BmpFileHeader;
-typedef struct tagBmpCoreHeader {
-  uint32_t bcSize;
-  uint16_t bcWidth;
-  uint16_t bcHeight;
-  uint16_t bcPlanes;
-  uint16_t bcBitCount;
-} BmpCoreHeader;
-typedef struct tagBmpInfoHeader {
-  uint32_t biSize;
-  int32_t biWidth;
-  int32_t biHeight;
-  uint16_t biPlanes;
-  uint16_t biBitCount;
-  uint32_t biCompression;
-  uint32_t biSizeImage;
-  int32_t biXPelsPerMeter;
-  int32_t biYPelsPerMeter;
-  uint32_t biClrUsed;
-  uint32_t biClrImportant;
-} BmpInfoHeader;
-#pragma pack()
-
-class BMPDecompressor {
- public:
-  BMPDecompressor();
-  ~BMPDecompressor();
-
-  void Error();
-  int32_t DecodeImage();
-  int32_t ReadHeader();
-  void SetInputBuffer(uint8_t* src_buf, uint32_t src_size);
-  uint32_t GetAvailInput(uint8_t** avail_buf);
-
-  jmp_buf jmpbuf;
-
-  void* context_ptr;
-
-  std::vector<uint8_t> out_row_buffer;
-  std::vector<uint32_t> palette;
-  uint8_t* next_in;
-
-  uint32_t header_offset;
-  uint32_t width;
-  uint32_t height;
-  uint32_t compress_flag;
-  int32_t components;
-  size_t src_row_bytes;
-  size_t out_row_bytes;
-  uint16_t bitCounts;
-  uint32_t color_used;
-  bool imgTB_flag;
-  int32_t pal_num;
-  int32_t pal_type;
-  uint32_t data_size;
-  uint32_t img_data_offset;
-  uint32_t img_ifh_size;
-  size_t row_num;
-  size_t col_num;
-  int32_t dpi_x;
-  int32_t dpi_y;
-  uint32_t mask_red;
-  uint32_t mask_green;
-  uint32_t mask_blue;
-
-  uint32_t avail_in;
-  uint32_t skip_size;
-  int32_t decode_status;
-
- private:
-  bool GetDataPosition(uint32_t cur_pos);
-  void ReadScanline(uint32_t row_num, const std::vector<uint8_t>& row_buf);
-  int32_t DecodeRGB();
-  int32_t DecodeRLE8();
-  int32_t DecodeRLE4();
-  uint8_t* ReadData(uint8_t** des_buf, uint32_t data_size);
-  void SaveDecodingStatus(int32_t status);
-  bool ValidateColorIndex(uint8_t val);
-  bool ValidateFlag() const;
-  void SetHeight(int32_t signed_height);
-};
-
-class CBmpContext : public CCodec_BmpModule::Context {
- public:
-  CBmpContext(CCodec_BmpModule* pModule, CCodec_BmpModule::Delegate* pDelegate);
-  ~CBmpContext() override;
-
-  BMPDecompressor m_Bmp;
-  UnownedPtr<CCodec_BmpModule> const m_pModule;
-  UnownedPtr<CCodec_BmpModule::Delegate> const m_pDelegate;
-};
-
-#endif  // CORE_FXCODEC_LBMP_FX_BMP_H_
diff --git a/core/fxcodec/png/DEPS b/core/fxcodec/png/DEPS
new file mode 100644
index 0000000..cc73855
--- /dev/null
+++ b/core/fxcodec/png/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/libpng16/png.h',
+]
diff --git a/core/fxcodec/png/pngmodule.cpp b/core/fxcodec/png/pngmodule.cpp
new file mode 100644
index 0000000..d3a3c0b
--- /dev/null
+++ b/core/fxcodec/png/pngmodule.cpp
@@ -0,0 +1,233 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/png/pngmodule.h"
+
+#include <algorithm>
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/fx_dib.h"
+#include "third_party/base/compiler_specific.h"
+#include "third_party/base/ptr_util.h"
+
+#ifdef USE_SYSTEM_LIBPNG
+#include <png.h>
+#else
+#include "third_party/libpng16/png.h"
+#endif
+
+#define PNG_ERROR_SIZE 256
+
+class CPngContext final : public ModuleIface::Context {
+ public:
+  explicit CPngContext(PngModule::Delegate* pDelegate);
+  ~CPngContext() override;
+
+  png_structp m_pPng = nullptr;
+  png_infop m_pInfo = nullptr;
+  UnownedPtr<PngModule::Delegate> const m_pDelegate;
+  char m_szLastError[PNG_ERROR_SIZE];
+};
+
+extern "C" {
+
+void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {
+  if (png_get_error_ptr(png_ptr)) {
+    strncpy(static_cast<char*>(png_get_error_ptr(png_ptr)), error_msg,
+            PNG_ERROR_SIZE - 1);
+  }
+
+  longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {}
+
+void _png_load_bmp_attribute(png_structp png_ptr,
+                             png_infop info_ptr,
+                             CFX_DIBAttribute* pAttribute) {
+  if (pAttribute) {
+#if defined(PNG_pHYs_SUPPORTED)
+    pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr);
+    pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr);
+    png_uint_32 res_x, res_y;
+    int unit_type;
+    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
+    switch (unit_type) {
+      case PNG_RESOLUTION_METER:
+        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;
+        break;
+      default:
+        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE;
+    }
+#endif
+#if defined(PNG_iCCP_SUPPORTED)
+    png_charp icc_name;
+    png_bytep icc_profile;
+    png_uint_32 icc_proflen;
+    int compress_type;
+    png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile,
+                 &icc_proflen);
+#endif
+#if defined(PNG_TEXT_SUPPORTED)
+    int num_text;
+    png_textp text = nullptr;
+    png_get_text(png_ptr, info_ptr, &text, &num_text);
+#endif
+  }
+}
+
+void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) {
+  auto* pContext =
+      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
+  if (!pContext)
+    return;
+
+  png_uint_32 width = 0;
+  png_uint_32 height = 0;
+  int bpc = 0;
+  int color_type = 0;
+  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr,
+               nullptr, nullptr);
+  int color_type1 = color_type;
+  if (bpc > 8)
+    png_set_strip_16(png_ptr);
+  else if (bpc < 8)
+    png_set_expand_gray_1_2_4_to_8(png_ptr);
+
+  bpc = 8;
+  if (color_type == PNG_COLOR_TYPE_PALETTE)
+    png_set_palette_to_rgb(png_ptr);
+
+  int pass = png_set_interlace_handling(png_ptr);
+  double gamma = 1.0;
+  if (!pContext->m_pDelegate->PngReadHeader(width, height, bpc, pass,
+                                            &color_type, &gamma)) {
+    png_error(pContext->m_pPng, "Read Header Callback Error");
+  }
+  int intent;
+  if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
+    png_set_gamma(png_ptr, gamma, 0.45455);
+  } else {
+    double image_gamma;
+    if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
+      png_set_gamma(png_ptr, gamma, image_gamma);
+    else
+      png_set_gamma(png_ptr, gamma, 0.45455);
+  }
+  switch (color_type) {
+    case PNG_COLOR_TYPE_GRAY:
+    case PNG_COLOR_TYPE_GRAY_ALPHA: {
+      if (color_type1 & PNG_COLOR_MASK_COLOR) {
+        png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);
+      }
+    } break;
+    case PNG_COLOR_TYPE_PALETTE:
+      if (color_type1 != PNG_COLOR_TYPE_PALETTE) {
+        png_error(pContext->m_pPng, "Not Support Output Palette Now");
+      }
+      FALLTHROUGH;
+    case PNG_COLOR_TYPE_RGB:
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+      if (!(color_type1 & PNG_COLOR_MASK_COLOR)) {
+        png_set_gray_to_rgb(png_ptr);
+      }
+      png_set_bgr(png_ptr);
+      break;
+  }
+  if (!(color_type & PNG_COLOR_MASK_ALPHA))
+    png_set_strip_alpha(png_ptr);
+
+  if (color_type & PNG_COLOR_MASK_ALPHA &&
+      !(color_type1 & PNG_COLOR_MASK_ALPHA)) {
+    png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+  }
+  png_read_update_info(png_ptr, info_ptr);
+}
+
+void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
+
+void _png_get_row_func(png_structp png_ptr,
+                       png_bytep new_row,
+                       png_uint_32 row_num,
+                       int pass) {
+  auto* pContext =
+      reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
+  if (!pContext)
+    return;
+
+  uint8_t* src_buf;
+  if (!pContext->m_pDelegate->PngAskScanlineBuf(row_num, &src_buf))
+    png_error(png_ptr, "Ask Scanline buffer Callback Error");
+
+  if (src_buf)
+    png_progressive_combine_row(png_ptr, src_buf, new_row);
+
+  pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num);
+}
+
+}  // extern "C"
+
+CPngContext::CPngContext(PngModule::Delegate* pDelegate)
+    : m_pDelegate(pDelegate) {
+  memset(m_szLastError, 0, sizeof(m_szLastError));
+}
+
+CPngContext::~CPngContext() {
+  png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
+                          m_pInfo ? &m_pInfo : nullptr, nullptr);
+}
+
+namespace fxcodec {
+
+PngModule::PngModule() = default;
+
+PngModule::~PngModule() = default;
+
+std::unique_ptr<ModuleIface::Context> PngModule::Start(Delegate* pDelegate) {
+  auto p = pdfium::MakeUnique<CPngContext>(pDelegate);
+  p->m_pPng =
+      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+  if (!p->m_pPng)
+    return nullptr;
+
+  p->m_pInfo = png_create_info_struct(p->m_pPng);
+  if (!p->m_pInfo)
+    return nullptr;
+
+  if (setjmp(png_jmpbuf(p->m_pPng)))
+    return nullptr;
+
+  png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func,
+                              _png_get_row_func, _png_get_end_func);
+  png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data,
+                   _png_warning_data);
+  return p;
+}
+
+FX_FILESIZE PngModule::GetAvailInput(Context* pContext) const {
+  NOTREACHED();
+  return 0;
+}
+
+bool PngModule::Input(Context* pContext,
+                      RetainPtr<CFX_CodecMemory> codec_memory,
+                      CFX_DIBAttribute* pAttribute) {
+  auto* ctx = static_cast<CPngContext*>(pContext);
+  if (setjmp(png_jmpbuf(ctx->m_pPng))) {
+    if (pAttribute &&
+        strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) {
+      _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute);
+    }
+    return false;
+  }
+  pdfium::span<uint8_t> src_buf = codec_memory->GetSpan();
+  png_process_data(ctx->m_pPng, ctx->m_pInfo, src_buf.data(), src_buf.size());
+  return true;
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/png/pngmodule.h b/core/fxcodec/png/pngmodule.h
new file mode 100644
index 0000000..ad3fd39
--- /dev/null
+++ b/core/fxcodec/png/pngmodule.h
@@ -0,0 +1,50 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_PNG_PNGMODULE_H_
+#define CORE_FXCODEC_PNG_PNGMODULE_H_
+
+#include <memory>
+
+#include "core/fxcodec/codec_module_iface.h"
+
+namespace fxcodec {
+
+class PngModule final : public ModuleIface {
+ public:
+  class Delegate {
+   public:
+    virtual bool PngReadHeader(int width,
+                               int height,
+                               int bpc,
+                               int pass,
+                               int* color_type,
+                               double* gamma) = 0;
+
+    // Returns true on success. |pSrcBuf| will be set if this succeeds.
+    // |pSrcBuf| does not take ownership of the buffer.
+    virtual bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) = 0;
+
+    virtual void PngFillScanlineBufCompleted(int pass, int line) = 0;
+  };
+
+  PngModule();
+  ~PngModule() override;
+
+  // ModuleIface:
+  FX_FILESIZE GetAvailInput(Context* pContext) const override;
+  bool Input(Context* pContext,
+             RetainPtr<CFX_CodecMemory> codec_memory,
+             CFX_DIBAttribute* pAttribute) override;
+
+  std::unique_ptr<Context> Start(Delegate* pDelegate);
+};
+
+}  // namespace fxcodec
+
+using PngModule = fxcodec::PngModule;
+
+#endif  // CORE_FXCODEC_PNG_PNGMODULE_H_
diff --git a/core/fxcodec/progressivedecoder.cpp b/core/fxcodec/progressivedecoder.cpp
new file mode 100644
index 0000000..e948b39
--- /dev/null
+++ b/core/fxcodec/progressivedecoder.cpp
@@ -0,0 +1,2313 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/progressivedecoder.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_dib.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/numerics/safe_math.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcodec {
+
+namespace {
+
+constexpr size_t kBlockSize = 4096;
+
+#ifdef PDF_ENABLE_XFA_PNG
+#if defined(OS_MACOSX)
+const double kPngGamma = 1.7;
+#else
+const double kPngGamma = 2.2;
+#endif  // defined(OS_MACOSX)
+#endif  // PDF_ENABLE_XFA_PNG
+
+void RGB2BGR(uint8_t* buffer, int width = 1) {
+  if (buffer && width > 0) {
+    uint8_t temp;
+    int i = 0;
+    int j = 0;
+    for (; i < width; i++, j += 3) {
+      temp = buffer[j];
+      buffer[j] = buffer[j + 2];
+      buffer[j + 2] = temp;
+    }
+  }
+}
+
+}  // namespace
+
+ProgressiveDecoder::CFXCODEC_WeightTable::CFXCODEC_WeightTable() = default;
+
+ProgressiveDecoder::CFXCODEC_WeightTable::~CFXCODEC_WeightTable() = default;
+
+void ProgressiveDecoder::CFXCODEC_WeightTable::Calc(int dest_len, int src_len) {
+  double scale = static_cast<double>(src_len) / dest_len;
+  double base = dest_len < 0 ? src_len : 0.0;
+  m_ItemSize = (int)(sizeof(int) * 2 + sizeof(int) * (ceil(fabs(scale)) + 1));
+  m_DestMin = 0;
+  m_pWeightTables.resize(dest_len * m_ItemSize + 4);
+  if (fabs(scale) < 1.0) {
+    for (int dest_pixel = 0; dest_pixel < dest_len; dest_pixel++) {
+      PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
+      double src_pos = dest_pixel * scale + scale / 2 + base;
+      pixel_weights.m_SrcStart = (int)floor((float)src_pos - 1.0f / 2);
+      pixel_weights.m_SrcEnd = (int)floor((float)src_pos + 1.0f / 2);
+      pixel_weights.m_SrcStart = std::max(pixel_weights.m_SrcStart, 0);
+      pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_len - 1);
+      if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
+        pixel_weights.m_Weights[0] = 65536;
+      } else {
+        pixel_weights.m_Weights[1] = FXSYS_roundf(
+            (float)(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * 65536);
+        pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1];
+      }
+    }
+    return;
+  }
+  for (int dest_pixel = 0; dest_pixel < dest_len; dest_pixel++) {
+    PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
+    double src_start = dest_pixel * scale + base;
+    double src_end = src_start + scale;
+    int start_i;
+    int end_i;
+    if (src_start < src_end) {
+      start_i = (int)floor((float)src_start);
+      end_i = (int)ceil((float)src_end);
+    } else {
+      start_i = (int)floor((float)src_end);
+      end_i = (int)ceil((float)src_start);
+    }
+    start_i = std::max(start_i, 0);
+    end_i = std::min(end_i, src_len - 1);
+    if (start_i > end_i) {
+      pixel_weights.m_SrcStart = start_i;
+      pixel_weights.m_SrcEnd = start_i;
+      continue;
+    }
+    pixel_weights.m_SrcStart = start_i;
+    pixel_weights.m_SrcEnd = end_i;
+    for (int j = start_i; j <= end_i; j++) {
+      double dest_start = ((float)j - base) / scale;
+      double dest_end = ((float)(j + 1) - base) / scale;
+      if (dest_start > dest_end) {
+        double temp = dest_start;
+        dest_start = dest_end;
+        dest_end = temp;
+      }
+      double area_start =
+          dest_start > (float)(dest_pixel) ? dest_start : (float)(dest_pixel);
+      double area_end = dest_end > (float)(dest_pixel + 1)
+                            ? (float)(dest_pixel + 1)
+                            : dest_end;
+      double weight = area_start >= area_end ? 0.0 : area_end - area_start;
+      if (weight == 0 && j == end_i) {
+        pixel_weights.m_SrcEnd--;
+        break;
+      }
+      pixel_weights.m_Weights[j - start_i] =
+          FXSYS_roundf((float)(weight * 65536));
+    }
+  }
+}
+
+ProgressiveDecoder::CFXCODEC_HorzTable::CFXCODEC_HorzTable() = default;
+
+ProgressiveDecoder::CFXCODEC_HorzTable::~CFXCODEC_HorzTable() = default;
+
+void ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len, int src_len) {
+  double scale = (double)dest_len / (double)src_len;
+  m_ItemSize = sizeof(int) * 4;
+  int size = dest_len * m_ItemSize + 4;
+  m_pWeightTables.resize(size, 0);
+  if (scale > 1) {
+    int pre_dest_col = 0;
+    for (int src_col = 0; src_col < src_len; src_col++) {
+      double dest_col_f = src_col * scale;
+      int dest_col = FXSYS_roundf((float)dest_col_f);
+      PixelWeight* pWeight = GetPixelWeight(dest_col);
+      pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
+      pWeight->m_Weights[0] = 65536;
+      pWeight->m_Weights[1] = 0;
+      if (src_col == src_len - 1 && dest_col < dest_len - 1) {
+        for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_len;
+             dest_col_index++) {
+          pWeight = GetPixelWeight(dest_col_index);
+          pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
+          pWeight->m_Weights[0] = 65536;
+          pWeight->m_Weights[1] = 0;
+        }
+        return;
+      }
+      int dest_col_len = dest_col - pre_dest_col;
+      for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_col;
+           dest_col_index++) {
+        pWeight = GetPixelWeight(dest_col_index);
+        pWeight->m_SrcStart = src_col - 1;
+        pWeight->m_SrcEnd = src_col;
+        pWeight->m_Weights[0] =
+            FXSYS_roundf((float)(((float)dest_col - (float)dest_col_index) /
+                                 (float)dest_col_len * 65536));
+        pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
+      }
+      pre_dest_col = dest_col;
+    }
+    return;
+  }
+  for (int dest_col = 0; dest_col < dest_len; dest_col++) {
+    double src_col_f = dest_col / scale;
+    int src_col = FXSYS_roundf((float)src_col_f);
+    PixelWeight* pWeight = GetPixelWeight(dest_col);
+    pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
+    pWeight->m_Weights[0] = 65536;
+    pWeight->m_Weights[1] = 0;
+  }
+}
+
+ProgressiveDecoder::CFXCODEC_VertTable::CFXCODEC_VertTable() = default;
+
+ProgressiveDecoder::CFXCODEC_VertTable::~CFXCODEC_VertTable() = default;
+
+void ProgressiveDecoder::CFXCODEC_VertTable::Calc(int dest_len, int src_len) {
+  double scale = (double)dest_len / (double)src_len;
+  m_ItemSize = sizeof(int) * 4;
+  int size = dest_len * m_ItemSize + 4;
+  m_pWeightTables.resize(size, 0);
+  if (scale <= 1) {
+    for (int dest_row = 0; dest_row < dest_len; dest_row++) {
+      PixelWeight* pWeight = GetPixelWeight(dest_row);
+      pWeight->m_SrcStart = dest_row;
+      pWeight->m_SrcEnd = dest_row;
+      pWeight->m_Weights[0] = 65536;
+      pWeight->m_Weights[1] = 0;
+    }
+    return;
+  }
+
+  double step = 0.0;
+  int src_row = 0;
+  while (step < (double)dest_len) {
+    int start_step = (int)step;
+    step = scale * (++src_row);
+    int end_step = (int)step;
+    if (end_step >= dest_len) {
+      end_step = dest_len;
+      for (int dest_row = start_step; dest_row < end_step; dest_row++) {
+        PixelWeight* pWeight = GetPixelWeight(dest_row);
+        pWeight->m_SrcStart = start_step;
+        pWeight->m_SrcEnd = start_step;
+        pWeight->m_Weights[0] = 65536;
+        pWeight->m_Weights[1] = 0;
+      }
+      return;
+    }
+    int length = end_step - start_step;
+    {
+      PixelWeight* pWeight = GetPixelWeight(start_step);
+      pWeight->m_SrcStart = start_step;
+      pWeight->m_SrcEnd = start_step;
+      pWeight->m_Weights[0] = 65536;
+      pWeight->m_Weights[1] = 0;
+    }
+    for (int dest_row = start_step + 1; dest_row < end_step; dest_row++) {
+      PixelWeight* pWeight = GetPixelWeight(dest_row);
+      pWeight->m_SrcStart = start_step;
+      pWeight->m_SrcEnd = end_step;
+      pWeight->m_Weights[0] =
+          FXSYS_roundf((float)(end_step - dest_row) / (float)length * 65536);
+      pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
+    }
+  }
+}
+
+ProgressiveDecoder::ProgressiveDecoder(ModuleMgr* pCodecMgr)
+    : m_pCodecMgr(pCodecMgr) {}
+
+ProgressiveDecoder::~ProgressiveDecoder() = default;
+
+#ifdef PDF_ENABLE_XFA_PNG
+bool ProgressiveDecoder::PngReadHeader(int width,
+                                       int height,
+                                       int bpc,
+                                       int pass,
+                                       int* color_type,
+                                       double* gamma) {
+  if (!m_pDeviceBitmap) {
+    m_SrcWidth = width;
+    m_SrcHeight = height;
+    m_SrcBPC = bpc;
+    m_SrcPassNumber = pass;
+    switch (*color_type) {
+      case 0:
+        m_SrcComponents = 1;
+        break;
+      case 4:
+        m_SrcComponents = 2;
+        break;
+      case 2:
+        m_SrcComponents = 3;
+        break;
+      case 3:
+      case 6:
+        m_SrcComponents = 4;
+        break;
+      default:
+        m_SrcComponents = 0;
+        break;
+    }
+    m_clipBox = FX_RECT(0, 0, width, height);
+    return false;
+  }
+  FXDIB_Format format = m_pDeviceBitmap->GetFormat();
+  switch (format) {
+    case FXDIB_1bppMask:
+    case FXDIB_1bppRgb:
+      NOTREACHED();
+      return false;
+    case FXDIB_8bppMask:
+    case FXDIB_8bppRgb:
+      *color_type = 0;
+      break;
+    case FXDIB_Rgb:
+      *color_type = 2;
+      break;
+    case FXDIB_Rgb32:
+    case FXDIB_Argb:
+      *color_type = 6;
+      break;
+    default:
+      NOTREACHED();
+      return false;
+  }
+  *gamma = kPngGamma;
+  return true;
+}
+
+bool ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t** pSrcBuf) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  if (!pDIBitmap) {
+    NOTREACHED();
+    return false;
+  }
+  if (line >= m_clipBox.top && line < m_clipBox.bottom) {
+    double scale_y = static_cast<double>(m_sizeY) / m_clipBox.Height();
+    int32_t row = (int32_t)((line - m_clipBox.top) * scale_y) + m_startY;
+    const uint8_t* src_scan = pDIBitmap->GetScanline(row);
+    uint8_t* dest_scan = m_pDecodeBuf.get();
+    *pSrcBuf = m_pDecodeBuf.get();
+    int32_t src_Bpp = pDIBitmap->GetBPP() >> 3;
+    int32_t dest_Bpp = (m_SrcFormat & 0xff) >> 3;
+    int32_t src_left = m_startX;
+    int32_t dest_left = m_clipBox.left;
+    src_scan += src_left * src_Bpp;
+    dest_scan += dest_left * dest_Bpp;
+    for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
+      PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
+      if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) {
+        continue;
+      }
+      switch (pDIBitmap->GetFormat()) {
+        case FXDIB_1bppMask:
+        case FXDIB_1bppRgb:
+          NOTREACHED();
+          return false;
+        case FXDIB_8bppMask:
+        case FXDIB_8bppRgb: {
+          if (pDIBitmap->GetPalette()) {
+            return false;
+          }
+          uint32_t dest_g = 0;
+          dest_g += pPixelWeights->m_Weights[0] * src_scan[src_col];
+          dest_scan[pPixelWeights->m_SrcStart] = (uint8_t)(dest_g >> 16);
+        } break;
+        case FXDIB_Rgb:
+        case FXDIB_Rgb32: {
+          uint32_t dest_b = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_r = 0;
+          const uint8_t* p = src_scan + src_col * src_Bpp;
+          dest_b += pPixelWeights->m_Weights[0] * (*p++);
+          dest_g += pPixelWeights->m_Weights[0] * (*p++);
+          dest_r += pPixelWeights->m_Weights[0] * (*p);
+          uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp];
+          *pDes++ = (uint8_t)((dest_b) >> 16);
+          *pDes++ = (uint8_t)((dest_g) >> 16);
+          *pDes = (uint8_t)((dest_r) >> 16);
+        } break;
+        case FXDIB_Argb: {
+          uint32_t dest_r = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_b = 0;
+          const uint8_t* p = src_scan + src_col * src_Bpp;
+          dest_b += pPixelWeights->m_Weights[0] * (*p++);
+          dest_g += pPixelWeights->m_Weights[0] * (*p++);
+          dest_r += pPixelWeights->m_Weights[0] * (*p++);
+          uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp];
+          *pDes++ = (uint8_t)((dest_b) >> 16);
+          *pDes++ = (uint8_t)((dest_g) >> 16);
+          *pDes++ = (uint8_t)((dest_r) >> 16);
+          *pDes = *p;
+        } break;
+        default:
+          return false;
+      }
+    }
+  }
+  return true;
+}
+
+void ProgressiveDecoder::PngFillScanlineBufCompleted(int pass, int line) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  ASSERT(pDIBitmap);
+  int src_top = m_clipBox.top;
+  int src_bottom = m_clipBox.bottom;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if (line >= src_top && line < src_bottom) {
+    double scale_y = static_cast<double>(dest_height) / src_height;
+    int src_row = line - src_top;
+    int dest_row = (int)(src_row * scale_y) + dest_top;
+    if (dest_row >= dest_top + dest_height) {
+      return;
+    }
+    PngOneOneMapResampleHorz(pDIBitmap, dest_row, m_pDecodeBuf.get(),
+                             m_SrcFormat);
+    if (m_SrcPassNumber == 1 && scale_y > 1.0) {
+      ResampleVert(pDIBitmap, scale_y, dest_row);
+      return;
+    }
+    if (pass == 6 && scale_y > 1.0) {
+      ResampleVert(pDIBitmap, scale_y, dest_row);
+    }
+  }
+}
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_GIF
+void ProgressiveDecoder::GifRecordCurrentPosition(uint32_t& cur_pos) {
+  uint32_t remain_size =
+      m_pCodecMgr->GetGifModule()->GetAvailInput(m_pGifContext.get());
+  cur_pos = m_offSet - remain_size;
+}
+
+bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos,
+                                                   const FX_RECT& img_rc,
+                                                   int32_t pal_num,
+                                                   CFX_GifPalette* pal_ptr,
+                                                   int32_t delay_time,
+                                                   bool user_input,
+                                                   int32_t trans_index,
+                                                   int32_t disposal_method,
+                                                   bool interlace) {
+  m_offSet = rcd_pos;
+  m_InvalidateGifBuffer = true;
+
+  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
+  if (!GifReadMoreData(m_pCodecMgr->GetGifModule(), error_status)) {
+    return false;
+  }
+  CFX_GifPalette* pPalette = nullptr;
+  if (pal_num != 0 && pal_ptr) {
+    pPalette = pal_ptr;
+  } else {
+    if (!m_pGifPalette)
+      return false;
+    pal_num = m_GifPltNumber;
+    pPalette = m_pGifPalette;
+  }
+  if (!m_pSrcPalette)
+    m_pSrcPalette.reset(FX_Alloc(FX_ARGB, pal_num));
+  else if (pal_num > m_SrcPaletteNumber)
+    m_pSrcPalette.reset(FX_Realloc(FX_ARGB, m_pSrcPalette.release(), pal_num));
+  if (!m_pSrcPalette)
+    return false;
+
+  m_SrcPaletteNumber = pal_num;
+  for (int i = 0; i < pal_num; i++) {
+    m_pSrcPalette.get()[i] =
+        ArgbEncode(0xff, pPalette[i].r, pPalette[i].g, pPalette[i].b);
+  }
+  m_GifTransIndex = trans_index;
+  m_GifFrameRect = img_rc;
+  m_SrcPassNumber = interlace ? 4 : 1;
+  int32_t pal_index = m_GifBgIndex;
+  RetainPtr<CFX_DIBitmap> pDevice = m_pDeviceBitmap;
+  if (trans_index >= pal_num)
+    trans_index = -1;
+  if (trans_index != -1) {
+    m_pSrcPalette.get()[trans_index] &= 0x00ffffff;
+    if (pDevice->HasAlpha())
+      pal_index = trans_index;
+  }
+  if (pal_index >= pal_num)
+    return false;
+
+  int startX = m_startX;
+  int startY = m_startY;
+  int sizeX = m_sizeX;
+  int sizeY = m_sizeY;
+  int Bpp = pDevice->GetBPP() / 8;
+  FX_ARGB argb = m_pSrcPalette.get()[pal_index];
+  for (int row = 0; row < sizeY; row++) {
+    uint8_t* pScanline =
+        pDevice->GetWritableScanline(row + startY) + startX * Bpp;
+    switch (m_TransMethod) {
+      case 3: {
+        uint8_t gray =
+            FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb));
+        memset(pScanline, gray, sizeX);
+        break;
+      }
+      case 8: {
+        for (int col = 0; col < sizeX; col++) {
+          *pScanline++ = FXARGB_B(argb);
+          *pScanline++ = FXARGB_G(argb);
+          *pScanline++ = FXARGB_R(argb);
+          pScanline += Bpp - 3;
+        }
+        break;
+      }
+      case 12: {
+        for (int col = 0; col < sizeX; col++) {
+          FXARGB_SETDIB(pScanline, argb);
+          pScanline += 4;
+        }
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+void ProgressiveDecoder::GifReadScanline(int32_t row_num, uint8_t* row_buf) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  ASSERT(pDIBitmap);
+  int32_t img_width = m_GifFrameRect.Width();
+  if (!pDIBitmap->HasAlpha()) {
+    uint8_t* byte_ptr = row_buf;
+    for (int i = 0; i < img_width; i++) {
+      if (*byte_ptr == m_GifTransIndex) {
+        *byte_ptr = m_GifBgIndex;
+      }
+      byte_ptr++;
+    }
+  }
+  int32_t pal_index = m_GifBgIndex;
+  if (m_GifTransIndex != -1 && m_pDeviceBitmap->HasAlpha()) {
+    pal_index = m_GifTransIndex;
+  }
+  memset(m_pDecodeBuf.get(), pal_index, m_SrcWidth);
+  bool bLastPass = (row_num % 2) == 1;
+  int32_t line = row_num + m_GifFrameRect.top;
+  int32_t left = m_GifFrameRect.left;
+  memcpy(m_pDecodeBuf.get() + left, row_buf, img_width);
+  int src_top = m_clipBox.top;
+  int src_bottom = m_clipBox.bottom;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if (line < src_top || line >= src_bottom)
+    return;
+
+  double scale_y = static_cast<double>(dest_height) / src_height;
+  int src_row = line - src_top;
+  int dest_row = (int)(src_row * scale_y) + dest_top;
+  if (dest_row >= dest_top + dest_height)
+    return;
+
+  ReSampleScanline(pDIBitmap, dest_row, m_pDecodeBuf.get(), m_SrcFormat);
+  if (scale_y > 1.0 && m_SrcPassNumber == 1) {
+    ResampleVert(pDIBitmap, scale_y, dest_row);
+    return;
+  }
+  if (scale_y <= 1.0)
+    return;
+
+  int dest_bottom = dest_top + m_sizeY;
+  int dest_Bpp = pDIBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
+  if (dest_row + (int)scale_y >= dest_bottom - 1) {
+    const uint8_t* scan_src = pDIBitmap->GetScanline(dest_row) + dest_ScanOffet;
+    int cur_row = dest_row;
+    while (++cur_row < dest_bottom) {
+      uint8_t* scan_des =
+          pDIBitmap->GetWritableScanline(cur_row) + dest_ScanOffet;
+      uint32_t size = m_sizeX * dest_Bpp;
+      memmove(scan_des, scan_src, size);
+    }
+  }
+  if (bLastPass)
+    GifDoubleLineResampleVert(pDIBitmap, scale_y, dest_row);
+}
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_BMP
+bool ProgressiveDecoder::BmpInputImagePositionBuf(uint32_t rcd_pos) {
+  m_offSet = rcd_pos;
+  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
+  return BmpReadMoreData(m_pCodecMgr->GetBmpModule(), m_pBmpContext.get(),
+                         error_status);
+}
+
+void ProgressiveDecoder::BmpReadScanline(uint32_t row_num,
+                                         pdfium::span<const uint8_t> row_buf) {
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
+  ASSERT(pDIBitmap);
+
+  pdfium::span<const uint8_t> src_span = row_buf.first(m_ScanlineSize);
+  std::copy(std::begin(src_span), std::end(src_span), m_pDecodeBuf.get());
+
+  int src_top = m_clipBox.top;
+  int src_bottom = m_clipBox.bottom;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if ((src_top >= 0 && row_num < static_cast<uint32_t>(src_top)) ||
+      src_bottom < 0 || row_num >= static_cast<uint32_t>(src_bottom)) {
+    return;
+  }
+
+  double scale_y = static_cast<double>(dest_height) / src_height;
+  int src_row = row_num - src_top;
+  int dest_row = (int)(src_row * scale_y) + dest_top;
+  if (dest_row >= dest_top + dest_height)
+    return;
+
+  ReSampleScanline(pDIBitmap, dest_row, m_pDecodeBuf.get(), m_SrcFormat);
+  if (scale_y <= 1.0)
+    return;
+
+  if (m_BmpIsTopBottom) {
+    ResampleVert(pDIBitmap, scale_y, dest_row);
+    return;
+  }
+  ResampleVertBT(pDIBitmap, scale_y, dest_row);
+}
+
+void ProgressiveDecoder::ResampleVertBT(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    double scale_y,
+    int dest_row) {
+  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
+  int dest_top = m_startY;
+  int dest_bottom = m_startY + m_sizeY;
+  pdfium::base::CheckedNumeric<int> check_dest_row_1 = dest_row;
+  check_dest_row_1 += pdfium::base::checked_cast<int>(scale_y);
+  int dest_row_1 = check_dest_row_1.ValueOrDie();
+  if (dest_row_1 >= dest_bottom - 1) {
+    const uint8_t* scan_src =
+        pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet;
+    while (++dest_row < dest_bottom) {
+      uint8_t* scan_des =
+          pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet;
+      uint32_t size = m_sizeX * dest_Bpp;
+      memmove(scan_des, scan_src, size);
+    }
+    return;
+  }
+  for (; dest_row_1 > dest_row; dest_row_1--) {
+    uint8_t* scan_des =
+        pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet;
+    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
+    const uint8_t* scan_src1 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) +
+        dest_ScanOffet;
+    const uint8_t* scan_src2 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) +
+        dest_ScanOffet;
+    for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+      switch (pDeviceBitmap->GetFormat()) {
+        case FXDIB_Invalid:
+        case FXDIB_1bppMask:
+        case FXDIB_1bppRgb:
+          return;
+        case FXDIB_8bppMask:
+        case FXDIB_8bppRgb: {
+          if (pDeviceBitmap->GetPalette()) {
+            return;
+          }
+          int dest_g = 0;
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = (uint8_t)(dest_g >> 16);
+        } break;
+        case FXDIB_Rgb:
+        case FXDIB_Rgb32: {
+          uint32_t dest_b = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_r = 0;
+          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
+          scan_src1 += dest_Bpp - 3;
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          scan_src2 += dest_Bpp - 3;
+          *scan_des++ = (uint8_t)((dest_b) >> 16);
+          *scan_des++ = (uint8_t)((dest_g) >> 16);
+          *scan_des++ = (uint8_t)((dest_r) >> 16);
+          scan_des += dest_Bpp - 3;
+        } break;
+        case FXDIB_Argb: {
+          uint32_t dest_a = 0;
+          uint32_t dest_b = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_r = 0;
+          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_a += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = (uint8_t)((dest_b) >> 16);
+          *scan_des++ = (uint8_t)((dest_g) >> 16);
+          *scan_des++ = (uint8_t)((dest_r) >> 16);
+          *scan_des++ = (uint8_t)((dest_a) >> 16);
+        } break;
+        default:
+          return;
+      }
+    }
+  }
+}
+
+bool ProgressiveDecoder::BmpDetectImageTypeInBuffer(
+    CFX_DIBAttribute* pAttribute) {
+  BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
+  if (!pBmpModule) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return false;
+  }
+
+  std::unique_ptr<ModuleIface::Context> pBmpContext = pBmpModule->Start(this);
+  pBmpModule->Input(pBmpContext.get(), m_pCodecMemory, nullptr);
+
+  const std::vector<uint32_t>* palette;
+  BmpModule::Status read_result = pBmpModule->ReadHeader(
+      pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
+      &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
+  while (read_result == BmpModule::Status::kContinue) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
+    if (!BmpReadMoreData(pBmpModule, pBmpContext.get(), error_status)) {
+      m_status = error_status;
+      return false;
+    }
+    read_result = pBmpModule->ReadHeader(
+        pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
+        &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute);
+  }
+
+  if (read_result != BmpModule::Status::kSuccess) {
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+
+  FXDIB_Format format = FXDIB_Invalid;
+  switch (m_SrcComponents) {
+    case 1:
+      m_SrcFormat = FXCodec_8bppRgb;
+      format = FXDIB_8bppRgb;
+      break;
+    case 3:
+      m_SrcFormat = FXCodec_Rgb;
+      format = FXDIB_Rgb;
+      break;
+    case 4:
+      m_SrcFormat = FXCodec_Rgb32;
+      format = FXDIB_Rgb32;
+      break;
+    default:
+      m_status = FXCODEC_STATUS_ERR_FORMAT;
+      return false;
+  }
+
+  uint32_t pitch = 0;
+  uint32_t neededData = 0;
+  if (!CFX_DIBitmap::CalculatePitchAndSize(m_SrcWidth, m_SrcHeight, format,
+                                           &pitch, &neededData)) {
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+
+  uint32_t availableData = m_pFile->GetSize() - m_offSet +
+                           pBmpModule->GetAvailInput(pBmpContext.get());
+  if (neededData > availableData) {
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+
+  m_SrcBPC = 8;
+  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+  m_pBmpContext = std::move(pBmpContext);
+  if (m_SrcPaletteNumber) {
+    m_pSrcPalette.reset(FX_Alloc(FX_ARGB, m_SrcPaletteNumber));
+    memcpy(m_pSrcPalette.get(), palette->data(),
+           m_SrcPaletteNumber * sizeof(FX_ARGB));
+  } else {
+    m_pSrcPalette.reset();
+  }
+  return true;
+}
+
+bool ProgressiveDecoder::BmpReadMoreData(BmpModule* pBmpModule,
+                                         ModuleIface::Context* pContext,
+                                         FXCODEC_STATUS& err_status) {
+  return ReadMoreData(pBmpModule, pContext, false, err_status);
+}
+
+FXCODEC_STATUS ProgressiveDecoder::BmpStartDecode(
+    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
+  if (!pBmpModule) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
+  m_ScanlineSize = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
+  m_pDecodeBuf.reset(FX_Alloc(uint8_t, m_ScanlineSize));
+  m_WeightHorz.Calc(m_sizeX, m_clipBox.Width());
+  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
+  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::BmpContinueDecode() {
+  BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
+  if (!pBmpModule) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+
+  BmpModule::Status read_res = pBmpModule->LoadImage(m_pBmpContext.get());
+  while (read_res == BmpModule::Status::kContinue) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
+    if (!BmpReadMoreData(pBmpModule, m_pBmpContext.get(), error_status)) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = error_status;
+      return m_status;
+    }
+    read_res = pBmpModule->LoadImage(m_pBmpContext.get());
+  }
+
+  m_pDeviceBitmap = nullptr;
+  m_pFile = nullptr;
+  m_status = read_res == BmpModule::Status::kSuccess
+                 ? FXCODEC_STATUS_DECODE_FINISH
+                 : FXCODEC_STATUS_ERROR;
+  return m_status;
+}
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+bool ProgressiveDecoder::GifReadMoreData(GifModule* pGifModule,
+                                         FXCODEC_STATUS& err_status) {
+  if (!ReadMoreData(pGifModule, m_pGifContext.get(), m_InvalidateGifBuffer,
+                    err_status)) {
+    return false;
+  }
+  m_InvalidateGifBuffer = false;
+  return true;
+}
+
+bool ProgressiveDecoder::GifDetectImageTypeInBuffer() {
+  GifModule* pGifModule = m_pCodecMgr->GetGifModule();
+  if (!pGifModule) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return false;
+  }
+  m_pGifContext = pGifModule->Start(this);
+  pGifModule->Input(m_pGifContext.get(), m_pCodecMemory, nullptr);
+  m_SrcComponents = 1;
+  CFX_GifDecodeStatus readResult =
+      pGifModule->ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight,
+                             &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex);
+  while (readResult == CFX_GifDecodeStatus::Unfinished) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
+    if (!GifReadMoreData(pGifModule, error_status)) {
+      m_pGifContext = nullptr;
+      m_status = error_status;
+      return false;
+    }
+    readResult =
+        pGifModule->ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight,
+                               &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex);
+  }
+  if (readResult == CFX_GifDecodeStatus::Success) {
+    m_SrcBPC = 8;
+    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+    return true;
+  }
+  m_pGifContext = nullptr;
+  m_status = FXCODEC_STATUS_ERR_FORMAT;
+  return false;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::GifStartDecode(
+    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  GifModule* pGifModule = m_pCodecMgr->GetGifModule();
+  if (!pGifModule) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  m_SrcFormat = FXCodec_8bppRgb;
+  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
+  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth);
+  m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
+  m_WeightHorz.Calc(m_sizeX, m_clipBox.Width());
+  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
+  m_FrameCur = 0;
+  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::GifContinueDecode() {
+  GifModule* pGifModule = m_pCodecMgr->GetGifModule();
+  if (!pGifModule) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+
+  CFX_GifDecodeStatus readRes =
+      pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur);
+  while (readRes == CFX_GifDecodeStatus::Unfinished) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
+    if (!GifReadMoreData(pGifModule, error_status)) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = error_status;
+      return m_status;
+    }
+    readRes = pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur);
+  }
+
+  if (readRes == CFX_GifDecodeStatus::Success) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_DECODE_FINISH;
+    return m_status;
+  }
+
+  m_pDeviceBitmap = nullptr;
+  m_pFile = nullptr;
+  m_status = FXCODEC_STATUS_ERROR;
+  return m_status;
+}
+
+void ProgressiveDecoder::GifDoubleLineResampleVert(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    double scale_y,
+    int dest_row) {
+  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
+  int dest_top = m_startY;
+  pdfium::base::CheckedNumeric<double> scale_y2 = scale_y;
+  scale_y2 *= 2;
+  pdfium::base::CheckedNumeric<int> check_dest_row_1 = dest_row;
+  check_dest_row_1 -= scale_y2.ValueOrDie();
+  int dest_row_1 = check_dest_row_1.ValueOrDie();
+  dest_row_1 = std::max(dest_row_1, dest_top);
+  for (; dest_row_1 < dest_row; dest_row_1++) {
+    uint8_t* scan_des =
+        pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet;
+    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
+    const uint8_t* scan_src1 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) +
+        dest_ScanOffet;
+    const uint8_t* scan_src2 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) +
+        dest_ScanOffet;
+    for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+      switch (pDeviceBitmap->GetFormat()) {
+        case FXDIB_Invalid:
+        case FXDIB_1bppMask:
+        case FXDIB_1bppRgb:
+          return;
+        case FXDIB_8bppMask:
+        case FXDIB_8bppRgb: {
+          if (pDeviceBitmap->GetPalette()) {
+            return;
+          }
+          int dest_g = 0;
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = (uint8_t)(dest_g >> 16);
+        } break;
+        case FXDIB_Rgb:
+        case FXDIB_Rgb32: {
+          uint32_t dest_b = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_r = 0;
+          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
+          scan_src1 += dest_Bpp - 3;
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          scan_src2 += dest_Bpp - 3;
+          *scan_des++ = (uint8_t)((dest_b) >> 16);
+          *scan_des++ = (uint8_t)((dest_g) >> 16);
+          *scan_des++ = (uint8_t)((dest_r) >> 16);
+          scan_des += dest_Bpp - 3;
+        } break;
+        case FXDIB_Argb: {
+          uint32_t dest_a = 0;
+          uint32_t dest_b = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_r = 0;
+          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_a += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = (uint8_t)((dest_b) >> 16);
+          *scan_des++ = (uint8_t)((dest_g) >> 16);
+          *scan_des++ = (uint8_t)((dest_r) >> 16);
+          *scan_des++ = (uint8_t)((dest_a) >> 16);
+        } break;
+        default:
+          return;
+      }
+    }
+  }
+  int dest_bottom = dest_top + m_sizeY - 1;
+  if (dest_row + (int)(2 * scale_y) >= dest_bottom &&
+      dest_row + (int)scale_y < dest_bottom) {
+    GifDoubleLineResampleVert(pDeviceBitmap, scale_y, dest_row + (int)scale_y);
+  }
+}
+#endif  // PDF_ENABLE_XFA_GIF
+
+bool ProgressiveDecoder::JpegReadMoreData(JpegModule* pJpegModule,
+                                          FXCODEC_STATUS& err_status) {
+  return ReadMoreData(pJpegModule, m_pJpegContext.get(), false, err_status);
+}
+
+bool ProgressiveDecoder::JpegDetectImageTypeInBuffer(
+    CFX_DIBAttribute* pAttribute) {
+  JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
+  m_pJpegContext = pJpegModule->Start();
+  if (!m_pJpegContext) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return false;
+  }
+  pJpegModule->Input(m_pJpegContext.get(), m_pCodecMemory, nullptr);
+
+  // Setting jump marker before calling ReadHeader, since a longjmp to
+  // the marker indicates a fatal error.
+  if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) {
+    m_pJpegContext.reset();
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+
+  int32_t readResult =
+      pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight,
+                              &m_SrcComponents, pAttribute);
+  while (readResult == 2) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
+    if (!JpegReadMoreData(pJpegModule, error_status)) {
+      m_status = error_status;
+      return false;
+    }
+    readResult =
+        pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight,
+                                &m_SrcComponents, pAttribute);
+  }
+  if (!readResult) {
+    m_SrcBPC = 8;
+    m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+    return true;
+  }
+  m_pJpegContext.reset();
+  m_status = FXCODEC_STATUS_ERR_FORMAT;
+  return false;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::JpegStartDecode(
+    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
+  int down_scale = 1;
+  GetDownScale(down_scale);
+  // Setting jump marker before calling StartScanLine, since a longjmp to
+  // the marker indicates a fatal error.
+  if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) {
+    m_pJpegContext.reset();
+    m_status = FXCODEC_STATUS_ERROR;
+    return FXCODEC_STATUS_ERROR;
+  }
+
+  bool startStatus =
+      pJpegModule->StartScanline(m_pJpegContext.get(), down_scale);
+  while (!startStatus) {
+    FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
+    if (!JpegReadMoreData(pJpegModule, error_status)) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = error_status;
+      return m_status;
+    }
+
+    startStatus = pJpegModule->StartScanline(m_pJpegContext.get(), down_scale);
+  }
+  int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale;
+  scanline_size = FxAlignToBoundary<4>(scanline_size * m_SrcComponents);
+  m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
+  m_WeightHorz.Calc(m_sizeX, m_clipBox.Width());
+  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
+  switch (m_SrcComponents) {
+    case 1:
+      m_SrcFormat = FXCodec_8bppGray;
+      break;
+    case 3:
+      m_SrcFormat = FXCodec_Rgb;
+      break;
+    case 4:
+      m_SrcFormat = FXCodec_Cmyk;
+      break;
+  }
+  GetTransMethod(pDIBitmap->GetFormat(), m_SrcFormat);
+  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::JpegContinueDecode() {
+  JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
+  // Setting jump marker before calling ReadScanLine, since a longjmp to
+  // the marker indicates a fatal error.
+  if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) {
+    m_pJpegContext.reset();
+    m_status = FXCODEC_STATUS_ERROR;
+    return FXCODEC_STATUS_ERROR;
+  }
+
+  while (true) {
+    bool readRes =
+        pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf.get());
+    while (!readRes) {
+      FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
+      if (!JpegReadMoreData(pJpegModule, error_status)) {
+        m_pDeviceBitmap = nullptr;
+        m_pFile = nullptr;
+        m_status = error_status;
+        return m_status;
+      }
+      readRes =
+          pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf.get());
+    }
+    if (m_SrcFormat == FXCodec_Rgb) {
+      int src_Bpp = (m_SrcFormat & 0xff) >> 3;
+      RGB2BGR(m_pDecodeBuf.get() + m_clipBox.left * src_Bpp, m_clipBox.Width());
+    }
+    if (m_SrcRow >= m_clipBox.bottom) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS_DECODE_FINISH;
+      return m_status;
+    }
+    Resample(m_pDeviceBitmap, m_SrcRow, m_pDecodeBuf.get(), m_SrcFormat);
+    m_SrcRow++;
+  }
+}
+
+#ifdef PDF_ENABLE_XFA_PNG
+void ProgressiveDecoder::PngOneOneMapResampleHorz(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    int32_t dest_line,
+    uint8_t* src_scan,
+    FXCodec_Format src_format) {
+  uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line);
+  int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3;
+  int32_t dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  int32_t src_left = m_clipBox.left;
+  int32_t dest_left = m_startX;
+  src_scan += src_left * src_Bpp;
+  dest_scan += dest_left * dest_Bpp;
+  for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) {
+    PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col);
+    switch (pDeviceBitmap->GetFormat()) {
+      case FXDIB_1bppMask:
+      case FXDIB_1bppRgb:
+        NOTREACHED();
+        return;
+      case FXDIB_8bppMask:
+      case FXDIB_8bppRgb: {
+        if (pDeviceBitmap->GetPalette()) {
+          return;
+        }
+        uint32_t dest_g = 0;
+        dest_g +=
+            pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart];
+        dest_g +=
+            pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd];
+        *dest_scan++ = (uint8_t)(dest_g >> 16);
+      } break;
+      case FXDIB_Rgb:
+      case FXDIB_Rgb32: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        const uint8_t* p = src_scan;
+        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
+        dest_b += pPixelWeights->m_Weights[0] * (*p++);
+        dest_g += pPixelWeights->m_Weights[0] * (*p++);
+        dest_r += pPixelWeights->m_Weights[0] * (*p);
+        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
+        dest_b += pPixelWeights->m_Weights[1] * (*p++);
+        dest_g += pPixelWeights->m_Weights[1] * (*p++);
+        dest_r += pPixelWeights->m_Weights[1] * (*p);
+        *dest_scan++ = (uint8_t)((dest_b) >> 16);
+        *dest_scan++ = (uint8_t)((dest_g) >> 16);
+        *dest_scan++ = (uint8_t)((dest_r) >> 16);
+        dest_scan += dest_Bpp - 3;
+      } break;
+      case FXDIB_Argb: {
+        uint32_t dest_a = 0;
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        const uint8_t* p = src_scan;
+        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
+        dest_b += pPixelWeights->m_Weights[0] * (*p++);
+        dest_g += pPixelWeights->m_Weights[0] * (*p++);
+        dest_r += pPixelWeights->m_Weights[0] * (*p++);
+        dest_a += pPixelWeights->m_Weights[0] * (*p);
+        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
+        dest_b += pPixelWeights->m_Weights[1] * (*p++);
+        dest_g += pPixelWeights->m_Weights[1] * (*p++);
+        dest_r += pPixelWeights->m_Weights[1] * (*p++);
+        dest_a += pPixelWeights->m_Weights[1] * (*p);
+        *dest_scan++ = (uint8_t)((dest_b) >> 16);
+        *dest_scan++ = (uint8_t)((dest_g) >> 16);
+        *dest_scan++ = (uint8_t)((dest_r) >> 16);
+        *dest_scan++ = (uint8_t)((dest_a) >> 16);
+      } break;
+      default:
+        return;
+    }
+  }
+}
+
+bool ProgressiveDecoder::PngDetectImageTypeInBuffer(
+    CFX_DIBAttribute* pAttribute) {
+  PngModule* pPngModule = m_pCodecMgr->GetPngModule();
+  if (!pPngModule) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return false;
+  }
+  m_pPngContext = pPngModule->Start(this);
+  if (!m_pPngContext) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return false;
+  }
+  while (pPngModule->Input(m_pPngContext.get(), m_pCodecMemory, pAttribute)) {
+    uint32_t remain_size = static_cast<uint32_t>(m_pFile->GetSize()) - m_offSet;
+    uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize);
+    if (input_size == 0) {
+      m_pPngContext.reset();
+      m_status = FXCODEC_STATUS_ERR_FORMAT;
+      return false;
+    }
+    if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize())
+      m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size);
+
+    if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet,
+                                    input_size)) {
+      m_status = FXCODEC_STATUS_ERR_READ;
+      return false;
+    }
+    m_offSet += input_size;
+  }
+  m_pPngContext.reset();
+  if (m_SrcPassNumber == 0) {
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+  return true;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::PngStartDecode(
+    const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  PngModule* pPngModule = m_pCodecMgr->GetPngModule();
+  if (!pPngModule) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  m_pPngContext = pPngModule->Start(this);
+  if (!m_pPngContext) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  m_offSet = 0;
+  switch (m_pDeviceBitmap->GetFormat()) {
+    case FXDIB_8bppMask:
+    case FXDIB_8bppRgb:
+      m_SrcComponents = 1;
+      m_SrcFormat = FXCodec_8bppGray;
+      break;
+    case FXDIB_Rgb:
+      m_SrcComponents = 3;
+      m_SrcFormat = FXCodec_Rgb;
+      break;
+    case FXDIB_Rgb32:
+    case FXDIB_Argb:
+      m_SrcComponents = 4;
+      m_SrcFormat = FXCodec_Argb;
+      break;
+    default: {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS_ERR_PARAMS;
+      return m_status;
+    }
+  }
+  GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
+  int scanline_size = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents);
+  m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size));
+  m_WeightHorzOO.Calc(m_sizeX, m_clipBox.Width());
+  m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
+  m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+  return m_status;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::PngContinueDecode() {
+  PngModule* pPngModule = m_pCodecMgr->GetPngModule();
+  if (!pPngModule) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  while (true) {
+    uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet;
+    uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize);
+    if (input_size == 0) {
+      m_pPngContext.reset();
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS_DECODE_FINISH;
+      return m_status;
+    }
+    if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize())
+      m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size);
+
+    bool bResult = m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(),
+                                              m_offSet, input_size);
+    if (!bResult) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS_ERR_READ;
+      return m_status;
+    }
+    m_offSet += input_size;
+    bResult = pPngModule->Input(m_pPngContext.get(), m_pCodecMemory, nullptr);
+    if (!bResult) {
+      m_pDeviceBitmap = nullptr;
+      m_pFile = nullptr;
+      m_status = FXCODEC_STATUS_ERROR;
+      return m_status;
+    }
+  }
+}
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+bool ProgressiveDecoder::TiffDetectImageTypeFromFile(
+    CFX_DIBAttribute* pAttribute) {
+  TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
+  if (!pTiffModule) {
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+  m_pTiffContext = pTiffModule->CreateDecoder(m_pFile);
+  if (!m_pTiffContext) {
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+  int32_t dummy_bpc;
+  bool ret = pTiffModule->LoadFrameInfo(m_pTiffContext.get(), 0, &m_SrcWidth,
+                                        &m_SrcHeight, &m_SrcComponents,
+                                        &dummy_bpc, pAttribute);
+  m_SrcComponents = 4;
+  m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
+  if (!ret) {
+    m_pTiffContext.reset();
+    m_status = FXCODEC_STATUS_ERR_FORMAT;
+    return false;
+  }
+  return true;
+}
+
+FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() {
+  TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
+  if (!pTiffModule) {
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  bool ret = false;
+  if (m_pDeviceBitmap->GetBPP() == 32 &&
+      m_pDeviceBitmap->GetWidth() == m_SrcWidth && m_SrcWidth == m_sizeX &&
+      m_pDeviceBitmap->GetHeight() == m_SrcHeight && m_SrcHeight == m_sizeY &&
+      m_startX == 0 && m_startY == 0 && m_clipBox.left == 0 &&
+      m_clipBox.top == 0 && m_clipBox.right == m_SrcWidth &&
+      m_clipBox.bottom == m_SrcHeight) {
+    ret = pTiffModule->Decode(m_pTiffContext.get(), m_pDeviceBitmap);
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    if (!ret) {
+      m_status = FXCODEC_STATUS_ERROR;
+      return m_status;
+    }
+    m_status = FXCODEC_STATUS_DECODE_FINISH;
+    return m_status;
+  }
+
+  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Argb);
+  if (!pDIBitmap->GetBuffer()) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  ret = pTiffModule->Decode(m_pTiffContext.get(), pDIBitmap);
+  if (!ret) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERROR;
+    return m_status;
+  }
+  RetainPtr<CFX_DIBitmap> pClipBitmap =
+      (m_clipBox.left == 0 && m_clipBox.top == 0 &&
+       m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight)
+          ? pDIBitmap
+          : pDIBitmap->Clone(&m_clipBox);
+  if (!pClipBitmap) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  RetainPtr<CFX_DIBitmap> pFormatBitmap;
+  switch (m_pDeviceBitmap->GetFormat()) {
+    case FXDIB_8bppRgb:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_8bppRgb);
+      break;
+    case FXDIB_8bppMask:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_8bppMask);
+      break;
+    case FXDIB_Rgb:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_Rgb);
+      break;
+    case FXDIB_Rgb32:
+      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+      pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(),
+                            FXDIB_Rgb32);
+      break;
+    case FXDIB_Argb:
+      pFormatBitmap = pClipBitmap;
+      break;
+    default:
+      break;
+  }
+  switch (m_pDeviceBitmap->GetFormat()) {
+    case FXDIB_8bppRgb:
+    case FXDIB_8bppMask: {
+      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
+        const uint8_t* src_line = pClipBitmap->GetScanline(row);
+        uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row);
+        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
+          uint8_t _a = 255 - src_line[3];
+          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
+          *dest_line++ = FXRGB2GRAY(r, g, b);
+          src_line += 4;
+        }
+      }
+    } break;
+    case FXDIB_Rgb:
+    case FXDIB_Rgb32: {
+      int32_t desBpp = (m_pDeviceBitmap->GetFormat() == FXDIB_Rgb) ? 3 : 4;
+      for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
+        const uint8_t* src_line = pClipBitmap->GetScanline(row);
+        uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row);
+        for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
+          uint8_t _a = 255 - src_line[3];
+          uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
+          uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
+          *dest_line++ = b;
+          *dest_line++ = g;
+          *dest_line++ = r;
+          dest_line += desBpp - 3;
+          src_line += 4;
+        }
+      }
+    } break;
+    default:
+      break;
+  }
+  if (!pFormatBitmap) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  RetainPtr<CFX_DIBitmap> pStrechBitmap =
+      pFormatBitmap->StretchTo(m_sizeX, m_sizeY, options, nullptr);
+  pFormatBitmap = nullptr;
+  if (!pStrechBitmap) {
+    m_pDeviceBitmap = nullptr;
+    m_pFile = nullptr;
+    m_status = FXCODEC_STATUS_ERR_MEMORY;
+    return m_status;
+  }
+  m_pDeviceBitmap->TransferBitmap(m_startX, m_startY, m_sizeX, m_sizeY,
+                                  pStrechBitmap, 0, 0);
+  m_pDeviceBitmap = nullptr;
+  m_pFile = nullptr;
+  m_status = FXCODEC_STATUS_DECODE_FINISH;
+  return m_status;
+}
+#endif  // PDF_ENABLE_XFA_TIFF
+
+bool ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType,
+                                         CFX_DIBAttribute* pAttribute) {
+#ifdef PDF_ENABLE_XFA_TIFF
+  if (imageType == FXCODEC_IMAGE_TIFF)
+    return TiffDetectImageTypeFromFile(pAttribute);
+#endif  // PDF_ENABLE_XFA_TIFF
+
+  size_t size = std::min<size_t>(m_pFile->GetSize(), kBlockSize);
+  m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(size);
+  m_offSet = 0;
+  if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet,
+                                  size)) {
+    m_status = FXCODEC_STATUS_ERR_READ;
+    return false;
+  }
+  m_offSet += size;
+
+  if (imageType == FXCODEC_IMAGE_JPG)
+    return JpegDetectImageTypeInBuffer(pAttribute);
+
+#ifdef PDF_ENABLE_XFA_BMP
+  if (imageType == FXCODEC_IMAGE_BMP)
+    return BmpDetectImageTypeInBuffer(pAttribute);
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+  if (imageType == FXCODEC_IMAGE_GIF)
+    return GifDetectImageTypeInBuffer();
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+  if (imageType == FXCODEC_IMAGE_PNG)
+    return PngDetectImageTypeInBuffer(pAttribute);
+#endif  // PDF_ENABLE_XFA_PNG
+
+  m_status = FXCODEC_STATUS_ERR_FORMAT;
+  return false;
+}
+
+bool ProgressiveDecoder::ReadMoreData(ModuleIface* pModule,
+                                      ModuleIface::Context* pContext,
+                                      bool invalidate_buffer,
+                                      FXCODEC_STATUS& err_status) {
+  // Check for EOF.
+  if (m_offSet >= static_cast<uint32_t>(m_pFile->GetSize()))
+    return false;
+
+  // Try to get whatever remains.
+  uint32_t dwBytesToFetchFromFile = m_pFile->GetSize() - m_offSet;
+
+  // Figure out if the codec stopped processing midway through the buffer.
+  size_t dwUnconsumed = 0;
+  if (!invalidate_buffer) {
+    FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext);
+    if (!avail_input.IsValid())
+      return false;
+    dwUnconsumed = avail_input.ValueOrDie();
+  }
+
+  if (dwUnconsumed == m_pCodecMemory->GetSize()) {
+    // Codec couldn't make any progress against the bytes in the buffer.
+    // Increase the buffer size so that there might be enough contiguous
+    // bytes to allow whatever operation is having difficulty to succeed.
+    dwBytesToFetchFromFile =
+        std::min<uint32_t>(dwBytesToFetchFromFile, kBlockSize);
+    size_t dwNewSize = m_pCodecMemory->GetSize() + dwBytesToFetchFromFile;
+    if (!m_pCodecMemory->TryResize(dwNewSize)) {
+      err_status = FXCODEC_STATUS_ERR_MEMORY;
+      return false;
+    }
+  } else {
+    size_t dwConsumed = m_pCodecMemory->GetSize() - dwUnconsumed;
+    m_pCodecMemory->Consume(dwConsumed);
+    dwBytesToFetchFromFile =
+        std::min<uint32_t>(dwBytesToFetchFromFile, dwConsumed);
+  }
+
+  // Append new data past the bytes not yet processed by the codec.
+  if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer() + dwUnconsumed,
+                                  m_offSet, dwBytesToFetchFromFile)) {
+    err_status = FXCODEC_STATUS_ERR_READ;
+    return false;
+  }
+  m_offSet += dwBytesToFetchFromFile;
+  return pModule->Input(pContext, m_pCodecMemory, nullptr);
+}
+
+FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo(
+    const RetainPtr<IFX_SeekableReadStream>& pFile,
+    FXCODEC_IMAGE_TYPE imageType,
+    CFX_DIBAttribute* pAttribute,
+    bool bSkipImageTypeCheck) {
+  ASSERT(pAttribute);
+
+  switch (m_status) {
+    case FXCODEC_STATUS_FRAME_READY:
+    case FXCODEC_STATUS_FRAME_TOBECONTINUE:
+    case FXCODEC_STATUS_DECODE_READY:
+    case FXCODEC_STATUS_DECODE_TOBECONTINUE:
+      return FXCODEC_STATUS_ERROR;
+    default:
+      break;
+  }
+  if (!pFile) {
+    m_status = FXCODEC_STATUS_ERR_PARAMS;
+    m_pFile = nullptr;
+    return m_status;
+  }
+  m_pFile = pFile;
+  m_offSet = 0;
+  m_SrcWidth = m_SrcHeight = 0;
+  m_SrcComponents = m_SrcBPC = 0;
+  m_clipBox = FX_RECT();
+  m_startX = m_startY = 0;
+  m_sizeX = m_sizeY = 0;
+  m_SrcPassNumber = 0;
+  if (imageType != FXCODEC_IMAGE_UNKNOWN &&
+      DetectImageType(imageType, pAttribute)) {
+    m_imageType = imageType;
+    m_status = FXCODEC_STATUS_FRAME_READY;
+    return m_status;
+  }
+  // If we got here then the image data does not match the requested decoder.
+  // If we're skipping the type check then bail out at this point and return
+  // the failed status.
+  if (bSkipImageTypeCheck)
+    return m_status;
+
+  for (int type = FXCODEC_IMAGE_UNKNOWN + 1; type < FXCODEC_IMAGE_MAX; type++) {
+    if (DetectImageType(static_cast<FXCODEC_IMAGE_TYPE>(type), pAttribute)) {
+      m_imageType = static_cast<FXCODEC_IMAGE_TYPE>(type);
+      m_status = FXCODEC_STATUS_FRAME_READY;
+      return m_status;
+    }
+  }
+  m_status = FXCODEC_STATUS_ERR_FORMAT;
+  m_pFile = nullptr;
+  return m_status;
+}
+
+void ProgressiveDecoder::SetClipBox(FX_RECT* clip) {
+  if (m_status != FXCODEC_STATUS_FRAME_READY)
+    return;
+
+  if (clip->IsEmpty()) {
+    m_clipBox = FX_RECT();
+    return;
+  }
+  clip->left = std::max(clip->left, 0);
+  clip->right = std::min(clip->right, m_SrcWidth);
+  clip->top = std::max(clip->top, 0);
+  clip->bottom = std::min(clip->bottom, m_SrcHeight);
+  if (clip->IsEmpty()) {
+    m_clipBox = FX_RECT();
+    return;
+  }
+  m_clipBox = *clip;
+}
+
+void ProgressiveDecoder::GetDownScale(int& down_scale) {
+  down_scale = 1;
+  int ratio_w = m_clipBox.Width() / m_sizeX;
+  int ratio_h = m_clipBox.Height() / m_sizeY;
+  int ratio = (ratio_w > ratio_h) ? ratio_h : ratio_w;
+  if (ratio >= 8) {
+    down_scale = 8;
+  } else if (ratio >= 4) {
+    down_scale = 4;
+  } else if (ratio >= 2) {
+    down_scale = 2;
+  }
+  m_clipBox.left /= down_scale;
+  m_clipBox.right /= down_scale;
+  m_clipBox.top /= down_scale;
+  m_clipBox.bottom /= down_scale;
+  if (m_clipBox.right == m_clipBox.left) {
+    m_clipBox.right = m_clipBox.left + 1;
+  }
+  if (m_clipBox.bottom == m_clipBox.top) {
+    m_clipBox.bottom = m_clipBox.top + 1;
+  }
+}
+
+void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format,
+                                        FXCodec_Format src_format) {
+  switch (dest_format) {
+    case FXDIB_1bppMask:
+    case FXDIB_1bppRgb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 0;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    case FXDIB_8bppMask:
+    case FXDIB_8bppRgb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 1;
+          break;
+        case FXCodec_8bppGray:
+          m_TransMethod = 2;
+          break;
+        case FXCodec_1bppRgb:
+        case FXCodec_8bppRgb:
+          m_TransMethod = 3;
+          break;
+        case FXCodec_Rgb:
+        case FXCodec_Rgb32:
+        case FXCodec_Argb:
+          m_TransMethod = 4;
+          break;
+        case FXCodec_Cmyk:
+          m_TransMethod = 5;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    case FXDIB_Rgb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 6;
+          break;
+        case FXCodec_8bppGray:
+          m_TransMethod = 7;
+          break;
+        case FXCodec_1bppRgb:
+        case FXCodec_8bppRgb:
+          m_TransMethod = 8;
+          break;
+        case FXCodec_Rgb:
+        case FXCodec_Rgb32:
+        case FXCodec_Argb:
+          m_TransMethod = 9;
+          break;
+        case FXCodec_Cmyk:
+          m_TransMethod = 10;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    case FXDIB_Rgb32:
+    case FXDIB_Argb: {
+      switch (src_format) {
+        case FXCodec_1bppGray:
+          m_TransMethod = 6;
+          break;
+        case FXCodec_8bppGray:
+          m_TransMethod = 7;
+          break;
+        case FXCodec_1bppRgb:
+        case FXCodec_8bppRgb:
+          if (dest_format == FXDIB_Argb) {
+            m_TransMethod = 12;
+          } else {
+            m_TransMethod = 8;
+          }
+          break;
+        case FXCodec_Rgb:
+        case FXCodec_Rgb32:
+          m_TransMethod = 9;
+          break;
+        case FXCodec_Cmyk:
+          m_TransMethod = 10;
+          break;
+        case FXCodec_Argb:
+          m_TransMethod = 11;
+          break;
+        default:
+          m_TransMethod = -1;
+      }
+    } break;
+    default:
+      m_TransMethod = -1;
+  }
+}
+
+void ProgressiveDecoder::ReSampleScanline(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    int dest_line,
+    uint8_t* src_scan,
+    FXCodec_Format src_format) {
+  int src_left = m_clipBox.left;
+  int dest_left = m_startX;
+  uint8_t* dest_scan =
+      pDeviceBitmap->GetBuffer() + dest_line * pDeviceBitmap->GetPitch();
+  int src_bytes_per_pixel = (src_format & 0xff) / 8;
+  int dest_bytes_per_pixel = pDeviceBitmap->GetBPP() / 8;
+  src_scan += src_left * src_bytes_per_pixel;
+  dest_scan += dest_left * dest_bytes_per_pixel;
+  for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+    PixelWeight* pPixelWeights = m_WeightHorz.GetPixelWeight(dest_col);
+    switch (m_TransMethod) {
+      case -1:
+        return;
+      case 0:
+        return;
+      case 1:
+        return;
+      case 2: {
+        uint32_t dest_g = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          dest_g += pixel_weight * src_scan[j];
+        }
+        *dest_scan++ = (uint8_t)(dest_g >> 16);
+      } break;
+      case 3: {
+        int dest_r = 0;
+        int dest_g = 0;
+        int dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
+          dest_r += pixel_weight * FXARGB_R(argb);
+          dest_g += pixel_weight * FXARGB_G(argb);
+          dest_b += pixel_weight * FXARGB_B(argb);
+        }
+        *dest_scan++ =
+            (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16));
+      } break;
+      case 4: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          dest_b += pixel_weight * (*src_pixel++);
+          dest_g += pixel_weight * (*src_pixel++);
+          dest_r += pixel_weight * (*src_pixel);
+        }
+        *dest_scan++ =
+            (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16));
+      } break;
+      case 5: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          uint8_t src_b = 0;
+          uint8_t src_g = 0;
+          uint8_t src_r = 0;
+          std::tie(src_r, src_g, src_b) =
+              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
+                                 255 - src_pixel[2], 255 - src_pixel[3]);
+          dest_b += pixel_weight * src_b;
+          dest_g += pixel_weight * src_g;
+          dest_r += pixel_weight * src_r;
+        }
+        *dest_scan++ =
+            (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16));
+      } break;
+      case 6:
+        return;
+      case 7: {
+        uint32_t dest_g = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          dest_g += pixel_weight * src_scan[j];
+        }
+        memset(dest_scan, (uint8_t)(dest_g >> 16), 3);
+        dest_scan += dest_bytes_per_pixel;
+      } break;
+      case 8: {
+        int dest_r = 0;
+        int dest_g = 0;
+        int dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
+          dest_r += pixel_weight * FXARGB_R(argb);
+          dest_g += pixel_weight * FXARGB_G(argb);
+          dest_b += pixel_weight * FXARGB_B(argb);
+        }
+        *dest_scan++ = (uint8_t)((dest_b) >> 16);
+        *dest_scan++ = (uint8_t)((dest_g) >> 16);
+        *dest_scan++ = (uint8_t)((dest_r) >> 16);
+        dest_scan += dest_bytes_per_pixel - 3;
+      } break;
+      case 12: {
+#ifdef PDF_ENABLE_XFA_BMP
+        if (m_pBmpContext) {
+          int dest_r = 0;
+          int dest_g = 0;
+          int dest_b = 0;
+          for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+               j++) {
+            int pixel_weight =
+                pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+            unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
+            dest_r += pixel_weight * FXARGB_R(argb);
+            dest_g += pixel_weight * FXARGB_G(argb);
+            dest_b += pixel_weight * FXARGB_B(argb);
+          }
+          *dest_scan++ = (uint8_t)((dest_b) >> 16);
+          *dest_scan++ = (uint8_t)((dest_g) >> 16);
+          *dest_scan++ = (uint8_t)((dest_r) >> 16);
+          *dest_scan++ = 0xFF;
+          break;
+        }
+#endif  // PDF_ENABLE_XFA_BMP
+        int dest_a = 0;
+        int dest_r = 0;
+        int dest_g = 0;
+        int dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          unsigned long argb = m_pSrcPalette.get()[src_scan[j]];
+          dest_a += pixel_weight * FXARGB_A(argb);
+          dest_r += pixel_weight * FXARGB_R(argb);
+          dest_g += pixel_weight * FXARGB_G(argb);
+          dest_b += pixel_weight * FXARGB_B(argb);
+        }
+        *dest_scan++ = (uint8_t)((dest_b) >> 16);
+        *dest_scan++ = (uint8_t)((dest_g) >> 16);
+        *dest_scan++ = (uint8_t)((dest_r) >> 16);
+        *dest_scan++ = (uint8_t)((dest_a) >> 16);
+      } break;
+      case 9: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          dest_b += pixel_weight * (*src_pixel++);
+          dest_g += pixel_weight * (*src_pixel++);
+          dest_r += pixel_weight * (*src_pixel);
+        }
+        *dest_scan++ = (uint8_t)((dest_b) >> 16);
+        *dest_scan++ = (uint8_t)((dest_g) >> 16);
+        *dest_scan++ = (uint8_t)((dest_r) >> 16);
+        dest_scan += dest_bytes_per_pixel - 3;
+      } break;
+      case 10: {
+        uint32_t dest_b = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_r = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          uint8_t src_b = 0;
+          uint8_t src_g = 0;
+          uint8_t src_r = 0;
+          std::tie(src_r, src_g, src_b) =
+              AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
+                                 255 - src_pixel[2], 255 - src_pixel[3]);
+          dest_b += pixel_weight * src_b;
+          dest_g += pixel_weight * src_g;
+          dest_r += pixel_weight * src_r;
+        }
+        *dest_scan++ = (uint8_t)((dest_b) >> 16);
+        *dest_scan++ = (uint8_t)((dest_g) >> 16);
+        *dest_scan++ = (uint8_t)((dest_r) >> 16);
+        dest_scan += dest_bytes_per_pixel - 3;
+      } break;
+      case 11: {
+        uint32_t dest_alpha = 0;
+        uint32_t dest_r = 0;
+        uint32_t dest_g = 0;
+        uint32_t dest_b = 0;
+        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
+             j++) {
+          int pixel_weight =
+              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
+          const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel;
+          pixel_weight = pixel_weight * src_pixel[3] / 255;
+          dest_b += pixel_weight * (*src_pixel++);
+          dest_g += pixel_weight * (*src_pixel++);
+          dest_r += pixel_weight * (*src_pixel);
+          dest_alpha += pixel_weight;
+        }
+        *dest_scan++ = (uint8_t)((dest_b) >> 16);
+        *dest_scan++ = (uint8_t)((dest_g) >> 16);
+        *dest_scan++ = (uint8_t)((dest_r) >> 16);
+        *dest_scan++ = (uint8_t)((dest_alpha * 255) >> 16);
+      } break;
+      default:
+        return;
+    }
+  }
+}
+
+void ProgressiveDecoder::ResampleVert(
+    const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+    double scale_y,
+    int dest_row) {
+  int dest_Bpp = pDeviceBitmap->GetBPP() >> 3;
+  uint32_t dest_ScanOffet = m_startX * dest_Bpp;
+  int dest_top = m_startY;
+  pdfium::base::CheckedNumeric<int> check_dest_row_1 = dest_row;
+  check_dest_row_1 -= pdfium::base::checked_cast<int>(scale_y);
+  int dest_row_1 = check_dest_row_1.ValueOrDie();
+  if (dest_row_1 < dest_top) {
+    int dest_bottom = dest_top + m_sizeY;
+    if (dest_row + (int)scale_y >= dest_bottom - 1) {
+      const uint8_t* scan_src =
+          pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet;
+      while (++dest_row < dest_bottom) {
+        uint8_t* scan_des =
+            pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet;
+        uint32_t size = m_sizeX * dest_Bpp;
+        memmove(scan_des, scan_src, size);
+      }
+    }
+    return;
+  }
+  for (; dest_row_1 < dest_row; dest_row_1++) {
+    uint8_t* scan_des =
+        pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet;
+    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top);
+    const uint8_t* scan_src1 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) +
+        dest_ScanOffet;
+    const uint8_t* scan_src2 =
+        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) +
+        dest_ScanOffet;
+    for (int dest_col = 0; dest_col < m_sizeX; dest_col++) {
+      switch (pDeviceBitmap->GetFormat()) {
+        case FXDIB_Invalid:
+        case FXDIB_1bppMask:
+        case FXDIB_1bppRgb:
+          return;
+        case FXDIB_8bppMask:
+        case FXDIB_8bppRgb: {
+          if (pDeviceBitmap->GetPalette()) {
+            return;
+          }
+          int dest_g = 0;
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = (uint8_t)(dest_g >> 16);
+        } break;
+        case FXDIB_Rgb:
+        case FXDIB_Rgb32: {
+          uint32_t dest_b = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_r = 0;
+          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
+          scan_src1 += dest_Bpp - 3;
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          scan_src2 += dest_Bpp - 3;
+          *scan_des++ = (uint8_t)((dest_b) >> 16);
+          *scan_des++ = (uint8_t)((dest_g) >> 16);
+          *scan_des++ = (uint8_t)((dest_r) >> 16);
+          scan_des += dest_Bpp - 3;
+        } break;
+        case FXDIB_Argb: {
+          uint32_t dest_a = 0;
+          uint32_t dest_b = 0;
+          uint32_t dest_g = 0;
+          uint32_t dest_r = 0;
+          dest_b += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_g += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_r += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_a += pWeight->m_Weights[0] * (*scan_src1++);
+          dest_b += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_g += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_r += pWeight->m_Weights[1] * (*scan_src2++);
+          dest_a += pWeight->m_Weights[1] * (*scan_src2++);
+          *scan_des++ = (uint8_t)((dest_b) >> 16);
+          *scan_des++ = (uint8_t)((dest_g) >> 16);
+          *scan_des++ = (uint8_t)((dest_r) >> 16);
+          *scan_des++ = (uint8_t)((dest_a) >> 16);
+        } break;
+        default:
+          return;
+      }
+    }
+  }
+  int dest_bottom = dest_top + m_sizeY;
+  if (dest_row + (int)scale_y >= dest_bottom - 1) {
+    const uint8_t* scan_src =
+        pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet;
+    while (++dest_row < dest_bottom) {
+      uint8_t* scan_des =
+          pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet;
+      uint32_t size = m_sizeX * dest_Bpp;
+      memmove(scan_des, scan_src, size);
+    }
+  }
+}
+
+void ProgressiveDecoder::Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                                  int32_t src_line,
+                                  uint8_t* src_scan,
+                                  FXCodec_Format src_format) {
+  int src_top = m_clipBox.top;
+  int dest_top = m_startY;
+  int src_height = m_clipBox.Height();
+  int dest_height = m_sizeY;
+  if (src_line >= src_top) {
+    double scale_y = static_cast<double>(dest_height) / src_height;
+    int src_row = src_line - src_top;
+    int dest_row = (int)(src_row * scale_y) + dest_top;
+    if (dest_row >= dest_top + dest_height)
+      return;
+
+    ReSampleScanline(pDeviceBitmap, dest_row, m_pDecodeBuf.get(), src_format);
+    if (scale_y > 1.0)
+      ResampleVert(pDeviceBitmap, scale_y, dest_row);
+  }
+}
+
+std::pair<FXCODEC_STATUS, size_t> ProgressiveDecoder::GetFrames() {
+  if (!(m_status == FXCODEC_STATUS_FRAME_READY ||
+        m_status == FXCODEC_STATUS_FRAME_TOBECONTINUE)) {
+    return {FXCODEC_STATUS_ERROR, 0};
+  }
+
+  switch (m_imageType) {
+#ifdef PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_BMP:
+#endif  // PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_JPG:
+#ifdef PDF_ENABLE_XFA_PNG
+    case FXCODEC_IMAGE_PNG:
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+#endif  // PDF_ENABLE_XFA_TIFF
+      m_FrameNumber = 1;
+      m_status = FXCODEC_STATUS_DECODE_READY;
+      return {m_status, 1};
+#ifdef PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_GIF: {
+      GifModule* pGifModule = m_pCodecMgr->GetGifModule();
+      if (!pGifModule) {
+        m_status = FXCODEC_STATUS_ERR_MEMORY;
+        return {m_status, 0};
+      }
+      while (true) {
+        CFX_GifDecodeStatus readResult;
+        std::tie(readResult, m_FrameNumber) =
+            pGifModule->LoadFrameInfo(m_pGifContext.get());
+        while (readResult == CFX_GifDecodeStatus::Unfinished) {
+          FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_READ;
+          if (!GifReadMoreData(pGifModule, error_status))
+            return {error_status, 0};
+
+          std::tie(readResult, m_FrameNumber) =
+              pGifModule->LoadFrameInfo(m_pGifContext.get());
+        }
+        if (readResult == CFX_GifDecodeStatus::Success) {
+          m_status = FXCODEC_STATUS_DECODE_READY;
+          return {m_status, m_FrameNumber};
+        }
+        m_pGifContext = nullptr;
+        m_status = FXCODEC_STATUS_ERROR;
+        return {m_status, 0};
+      }
+    }
+#endif  // PDF_ENABLE_XFA_GIF
+    default:
+      return {FXCODEC_STATUS_ERROR, 0};
+  }
+}
+
+FXCODEC_STATUS ProgressiveDecoder::StartDecode(
+    const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+    int start_x,
+    int start_y,
+    int size_x,
+    int size_y) {
+  if (m_status != FXCODEC_STATUS_DECODE_READY)
+    return FXCODEC_STATUS_ERROR;
+
+  if (!pDIBitmap || pDIBitmap->GetBPP() < 8 || m_FrameNumber == 0)
+    return FXCODEC_STATUS_ERR_PARAMS;
+
+  m_pDeviceBitmap = pDIBitmap;
+  if (m_clipBox.IsEmpty())
+    return FXCODEC_STATUS_ERR_PARAMS;
+  if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535)
+    return FXCODEC_STATUS_ERR_PARAMS;
+
+  FX_RECT device_rc =
+      FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y);
+  int32_t out_range_x = device_rc.right - pDIBitmap->GetWidth();
+  int32_t out_range_y = device_rc.bottom - pDIBitmap->GetHeight();
+  device_rc.Intersect(
+      FX_RECT(0, 0, pDIBitmap->GetWidth(), pDIBitmap->GetHeight()));
+  if (device_rc.IsEmpty())
+    return FXCODEC_STATUS_ERR_PARAMS;
+
+  m_startX = device_rc.left;
+  m_startY = device_rc.top;
+  m_sizeX = device_rc.Width();
+  m_sizeY = device_rc.Height();
+  m_FrameCur = 0;
+  if (start_x < 0 || out_range_x > 0) {
+    float scaleX = (float)m_clipBox.Width() / (float)size_x;
+    if (start_x < 0) {
+      m_clipBox.left -= (int32_t)ceil((float)start_x * scaleX);
+    }
+    if (out_range_x > 0) {
+      m_clipBox.right -= (int32_t)floor((float)out_range_x * scaleX);
+    }
+  }
+  if (start_y < 0 || out_range_y > 0) {
+    float scaleY = (float)m_clipBox.Height() / (float)size_y;
+    if (start_y < 0) {
+      m_clipBox.top -= (int32_t)ceil((float)start_y * scaleY);
+    }
+    if (out_range_y > 0) {
+      m_clipBox.bottom -= (int32_t)floor((float)out_range_y * scaleY);
+    }
+  }
+  if (m_clipBox.IsEmpty()) {
+    return FXCODEC_STATUS_ERR_PARAMS;
+  }
+  switch (m_imageType) {
+#ifdef PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_BMP:
+      return BmpStartDecode(pDIBitmap);
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_GIF:
+      return GifStartDecode(pDIBitmap);
+#endif  // PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_JPG:
+      return JpegStartDecode(pDIBitmap);
+#ifdef PDF_ENABLE_XFA_PNG
+    case FXCODEC_IMAGE_PNG:
+      return PngStartDecode(pDIBitmap);
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
+      return m_status;
+#endif  // PDF_ENABLE_XFA_TIFF
+    default:
+      return FXCODEC_STATUS_ERROR;
+  }
+}
+
+FXCODEC_STATUS ProgressiveDecoder::ContinueDecode() {
+  if (m_status != FXCODEC_STATUS_DECODE_TOBECONTINUE)
+    return FXCODEC_STATUS_ERROR;
+
+  switch (m_imageType) {
+    case FXCODEC_IMAGE_JPG:
+      return JpegContinueDecode();
+#ifdef PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_BMP:
+      return BmpContinueDecode();
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+    case FXCODEC_IMAGE_GIF:
+      return GifContinueDecode();
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_PNG
+    case FXCODEC_IMAGE_PNG:
+      return PngContinueDecode();
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+      return TiffContinueDecode();
+#endif  // PDF_ENABLE_XFA_TIFF
+    default:
+      return FXCODEC_STATUS_ERROR;
+  }
+}
+
+std::unique_ptr<ProgressiveDecoder> ModuleMgr::CreateProgressiveDecoder() {
+  return pdfium::MakeUnique<ProgressiveDecoder>(this);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/progressivedecoder.h b/core/fxcodec/progressivedecoder.h
new file mode 100644
index 0000000..1852fe9
--- /dev/null
+++ b/core/fxcodec/progressivedecoder.h
@@ -0,0 +1,305 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_PROGRESSIVEDECODER_H_
+#define CORE_FXCODEC_PROGRESSIVEDECODER_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcodec/jpeg/jpegmodule.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/fx_dib.h"
+
+#ifdef PDF_ENABLE_XFA_BMP
+#include "core/fxcodec/bmp/bmpmodule.h"
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+#include "core/fxcodec/gif/gifmodule.h"
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+#include "core/fxcodec/png/pngmodule.h"
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+#include "core/fxcodec/tiff/tiffmodule.h"
+#endif  // PDF_ENABLE_XFA_TIFF
+
+class CFX_DIBitmap;
+class IFX_SeekableReadStream;
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+class ModuleMgr;
+
+class Dummy {};  // Placeholder to work around C++ syntax issues
+
+class ProgressiveDecoder :
+#ifdef PDF_ENABLE_XFA_BMP
+    public BmpModule::Delegate,
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+    public GifModule::Delegate,
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_PNG
+    public PngModule::Delegate,
+#endif  // PDF_ENABLE_XFA_PNG
+    public Dummy {
+ public:
+  enum FXCodec_Format {
+    FXCodec_Invalid = 0,
+    FXCodec_1bppGray = 0x101,
+    FXCodec_1bppRgb = 0x001,
+    FXCodec_8bppGray = 0x108,
+    FXCodec_8bppRgb = 0x008,
+    FXCodec_Rgb = 0x018,
+    FXCodec_Rgb32 = 0x020,
+    FXCodec_Argb = 0x220,
+    FXCodec_Cmyk = 0x120
+  };
+
+  explicit ProgressiveDecoder(ModuleMgr* pCodecMgr);
+  virtual ~ProgressiveDecoder();
+
+  FXCODEC_STATUS LoadImageInfo(const RetainPtr<IFX_SeekableReadStream>& pFile,
+                               FXCODEC_IMAGE_TYPE imageType,
+                               CFX_DIBAttribute* pAttribute,
+                               bool bSkipImageTypeCheck);
+
+  FXCODEC_IMAGE_TYPE GetType() const { return m_imageType; }
+  int32_t GetWidth() const { return m_SrcWidth; }
+  int32_t GetHeight() const { return m_SrcHeight; }
+  int32_t GetNumComponents() const { return m_SrcComponents; }
+  int32_t GetBPC() const { return m_SrcBPC; }
+  void SetClipBox(FX_RECT* clip);
+
+  std::pair<FXCODEC_STATUS, size_t> GetFrames();
+  FXCODEC_STATUS StartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                             int start_x,
+                             int start_y,
+                             int size_x,
+                             int size_y);
+
+  FXCODEC_STATUS ContinueDecode();
+
+  struct PixelWeight {
+    int m_SrcStart;
+    int m_SrcEnd;
+    int m_Weights[1];
+  };
+
+  class CFXCODEC_WeightTable {
+   public:
+    CFXCODEC_WeightTable();
+    ~CFXCODEC_WeightTable();
+
+    void Calc(int dest_len, int src_len);
+    PixelWeight* GetPixelWeight(int pixel) {
+      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
+                                            (pixel - m_DestMin) * m_ItemSize);
+    }
+
+    int m_DestMin;
+    int m_ItemSize;
+    std::vector<uint8_t> m_pWeightTables;
+  };
+
+  class CFXCODEC_HorzTable {
+   public:
+    CFXCODEC_HorzTable();
+    ~CFXCODEC_HorzTable();
+
+    void Calc(int dest_len, int src_len);
+    PixelWeight* GetPixelWeight(int pixel) {
+      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
+                                            pixel * m_ItemSize);
+    }
+
+    int m_ItemSize;
+    std::vector<uint8_t> m_pWeightTables;
+  };
+
+  class CFXCODEC_VertTable {
+   public:
+    CFXCODEC_VertTable();
+    ~CFXCODEC_VertTable();
+
+    void Calc(int dest_len, int src_len);
+    PixelWeight* GetPixelWeight(int pixel) {
+      return reinterpret_cast<PixelWeight*>(m_pWeightTables.data() +
+                                            pixel * m_ItemSize);
+    }
+    int m_ItemSize;
+    std::vector<uint8_t> m_pWeightTables;
+  };
+
+#ifdef PDF_ENABLE_XFA_PNG
+  // PngModule::Delegate
+  bool PngReadHeader(int width,
+                     int height,
+                     int bpc,
+                     int pass,
+                     int* color_type,
+                     double* gamma) override;
+  bool PngAskScanlineBuf(int line, uint8_t** pSrcBuf) override;
+  void PngFillScanlineBufCompleted(int pass, int line) override;
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_GIF
+  // GifModule::Delegate
+  void GifRecordCurrentPosition(uint32_t& cur_pos) override;
+  bool GifInputRecordPositionBuf(uint32_t rcd_pos,
+                                 const FX_RECT& img_rc,
+                                 int32_t pal_num,
+                                 CFX_GifPalette* pal_ptr,
+                                 int32_t delay_time,
+                                 bool user_input,
+                                 int32_t trans_index,
+                                 int32_t disposal_method,
+                                 bool interlace) override;
+  void GifReadScanline(int32_t row_num, uint8_t* row_buf) override;
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_BMP
+  // BmpModule::Delegate
+  bool BmpInputImagePositionBuf(uint32_t rcd_pos) override;
+  void BmpReadScanline(uint32_t row_num,
+                       pdfium::span<const uint8_t> row_buf) override;
+#endif  // PDF_ENABLE_XFA_BMP
+
+ private:
+#ifdef PDF_ENABLE_XFA_BMP
+  bool BmpReadMoreData(BmpModule* pBmpModule,
+                       ModuleIface::Context* pBmpContext,
+                       FXCODEC_STATUS& err_status);
+  bool BmpDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS BmpStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+  FXCODEC_STATUS BmpContinueDecode();
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+  bool GifReadMoreData(GifModule* pGifModule, FXCODEC_STATUS& err_status);
+  bool GifDetectImageTypeInBuffer();
+  FXCODEC_STATUS GifStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+  FXCODEC_STATUS GifContinueDecode();
+  void GifDoubleLineResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                                 double scale_y,
+                                 int dest_row);
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+  void PngOneOneMapResampleHorz(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                                int32_t dest_line,
+                                uint8_t* src_scan,
+                                FXCodec_Format src_format);
+  bool PngDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS PngStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+  FXCODEC_STATUS PngContinueDecode();
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+  bool TiffDetectImageTypeFromFile(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS TiffContinueDecode();
+#endif  // PDF_ENABLE_XFA_TIFF
+
+  bool JpegReadMoreData(JpegModule* pJpegModule, FXCODEC_STATUS& err_status);
+  bool JpegDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute);
+  FXCODEC_STATUS JpegStartDecode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+  FXCODEC_STATUS JpegContinueDecode();
+
+  bool DetectImageType(FXCODEC_IMAGE_TYPE imageType,
+                       CFX_DIBAttribute* pAttribute);
+  bool ReadMoreData(ModuleIface* pModule,
+                    ModuleIface::Context* pContext,
+                    bool invalidate_buffer,
+                    FXCODEC_STATUS& err_status);
+
+  void GetDownScale(int& down_scale);
+  void GetTransMethod(FXDIB_Format dest_format, FXCodec_Format src_format);
+
+  void ReSampleScanline(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                        int32_t dest_line,
+                        uint8_t* src_scan,
+                        FXCodec_Format src_format);
+  void Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                int32_t src_line,
+                uint8_t* src_scan,
+                FXCodec_Format src_format);
+  void ResampleVert(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                    double scale_y,
+                    int dest_row);
+  void ResampleVertBT(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
+                      double scale_y,
+                      int dest_row);
+
+  FXCODEC_STATUS m_status = FXCODEC_STATUS_DECODE_FINISH;
+  FXCODEC_IMAGE_TYPE m_imageType = FXCODEC_IMAGE_UNKNOWN;
+  RetainPtr<IFX_SeekableReadStream> m_pFile;
+  RetainPtr<CFX_DIBitmap> m_pDeviceBitmap;
+  UnownedPtr<ModuleMgr> m_pCodecMgr;
+  RetainPtr<CFX_CodecMemory> m_pCodecMemory;
+  std::unique_ptr<uint8_t, FxFreeDeleter> m_pDecodeBuf;
+  std::unique_ptr<FX_ARGB, FxFreeDeleter> m_pSrcPalette;
+  std::unique_ptr<ModuleIface::Context> m_pJpegContext;
+#ifdef PDF_ENABLE_XFA_BMP
+  std::unique_ptr<ModuleIface::Context> m_pBmpContext;
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_GIF
+  std::unique_ptr<ModuleIface::Context> m_pGifContext;
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_PNG
+  std::unique_ptr<ModuleIface::Context> m_pPngContext;
+#endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+  std::unique_ptr<ModuleIface::Context> m_pTiffContext;
+#endif  // PDF_ENABLE_XFA_TIFF
+  uint32_t m_offSet = 0;
+  int m_ScanlineSize = 0;
+  CFXCODEC_WeightTable m_WeightHorz;
+  CFXCODEC_VertTable m_WeightVert;
+  CFXCODEC_HorzTable m_WeightHorzOO;
+  int m_SrcWidth = 0;
+  int m_SrcHeight = 0;
+  int m_SrcComponents = 0;
+  int m_SrcBPC = 0;
+  FX_RECT m_clipBox;
+  int m_startX = 0;
+  int m_startY = 0;
+  int m_sizeX = 0;
+  int m_sizeY = 0;
+  int m_TransMethod = -1;
+  int m_SrcPaletteNumber = 0;
+  int m_SrcRow = 0;
+  FXCodec_Format m_SrcFormat = FXCodec_Invalid;
+  int m_SrcPassNumber = 0;
+  size_t m_FrameNumber = 0;
+  size_t m_FrameCur = 0;
+#ifdef PDF_ENABLE_XFA_GIF
+  int m_GifBgIndex = 0;
+  CFX_GifPalette* m_pGifPalette = nullptr;
+  int32_t m_GifPltNumber = 0;
+  int m_GifTransIndex = -1;
+  FX_RECT m_GifFrameRect;
+  bool m_InvalidateGifBuffer = true;
+#endif  // PDF_ENABLE_XFA_GIF
+#ifdef PDF_ENABLE_XFA_BMP
+  bool m_BmpIsTopBottom = false;
+#endif  // PDF_ENABLE_XFA_BMP
+};
+
+}  // namespace fxcodec
+
+using ProgressiveDecoder = fxcodec::ProgressiveDecoder;
+
+#endif  // CORE_FXCODEC_PROGRESSIVEDECODER_H_
diff --git a/core/fxcodec/progressivedecoder_unittest.cpp b/core/fxcodec/progressivedecoder_unittest.cpp
new file mode 100644
index 0000000..5c4e26f
--- /dev/null
+++ b/core/fxcodec/progressivedecoder_unittest.cpp
@@ -0,0 +1,406 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcodec/progressivedecoder.h"
+
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
+
+#ifdef PDF_ENABLE_XFA_GIF
+#include "core/fxcodec/gif/gifmodule.h"
+#endif  // PDF_ENABLE_XFA_GIF
+
+namespace fxcodec {
+
+#ifdef PDF_ENABLE_XFA_GIF
+TEST(ProgressiveDecoder, BUG_895009) {
+  static constexpr uint8_t kInput[] = {
+      0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x62, 0x00, 0x21, 0x1b, 0x27, 0x01,
+      0x00, 0x2c, 0x3b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x71, 0x00, 0xf8, 0x20,
+      0x00, 0x71, 0x00, 0xf8, 0x0b, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
+      0x00, 0x01, 0x00, 0x01, 0xbe, 0x00, 0x01, 0xbe, 0x00, 0x21, 0xf9, 0xf9,
+      0x21, 0x01, 0x00, 0x21, 0xf9, 0x00, 0x21, 0xf9, 0x00, 0x21, 0x01, 0x20,
+      0x00, 0x21, 0xf9, 0x00, 0x21, 0x21, 0x00, 0x21, 0x00, 0x00, 0x21, 0x01,
+      0x00, 0x21, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x47,
+      0x49, 0x46, 0x38, 0x39, 0x61, 0x11, 0x00, 0x20, 0x00, 0xf1, 0x03, 0x32,
+      0x34, 0x37, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x11, 0x00,
+      0x20, 0x00, 0xf1, 0x03, 0x32, 0x34, 0x37, 0x21, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xc3, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x5b, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf5,
+      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+      0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x47, 0x49, 0x46, 0x37,
+      0x36, 0x61, 0x01, 0x04, 0x20, 0x00, 0xf1, 0x03, 0x31, 0x33, 0x33, 0x37,
+      0x32, 0x30, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00, 0x01, 0x03,
+      0x01, 0x03, 0x3b, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x3a, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xfd, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0xb4, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+  ModuleMgr::Create();
+  ModuleMgr::GetInstance()->SetGifModule(pdfium::MakeUnique<GifModule>());
+  {
+    std::unique_ptr<ProgressiveDecoder> decoder =
+        ModuleMgr::GetInstance()->CreateProgressiveDecoder();
+
+    auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(kInput);
+    CFX_DIBAttribute attr;
+    FXCODEC_STATUS status =
+        decoder->LoadImageInfo(source, FXCODEC_IMAGE_GIF, &attr, true);
+    ASSERT_EQ(FXCODEC_STATUS_FRAME_READY, status);
+
+    ASSERT_EQ(98, decoder->GetWidth());
+    ASSERT_EQ(6945, decoder->GetHeight());
+
+    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+    bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb);
+
+    size_t frames;
+    std::tie(status, frames) = decoder->GetFrames();
+    ASSERT_EQ(FXCODEC_STATUS_DECODE_READY, status);
+    ASSERT_EQ(1u, frames);
+
+    status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
+                                  bitmap->GetHeight());
+    while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+      status = decoder->ContinueDecode();
+    EXPECT_EQ(FXCODEC_STATUS_DECODE_FINISH, status);
+  }
+  ModuleMgr::Destroy();
+}
+#endif  // PDF_ENABLE_XFA_GIF
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/scanlinedecoder.cpp b/core/fxcodec/scanlinedecoder.cpp
new file mode 100644
index 0000000..2aa4178
--- /dev/null
+++ b/core/fxcodec/scanlinedecoder.cpp
@@ -0,0 +1,73 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/scanlinedecoder.h"
+
+#include "core/fxcrt/pauseindicator_iface.h"
+
+namespace fxcodec {
+
+ScanlineDecoder::ScanlineDecoder() : ScanlineDecoder(0, 0, 0, 0, 0, 0, 0) {}
+
+ScanlineDecoder::ScanlineDecoder(int nOrigWidth,
+                                 int nOrigHeight,
+                                 int nOutputWidth,
+                                 int nOutputHeight,
+                                 int nComps,
+                                 int nBpc,
+                                 uint32_t nPitch)
+    : m_OrigWidth(nOrigWidth),
+      m_OrigHeight(nOrigHeight),
+      m_OutputWidth(nOutputWidth),
+      m_OutputHeight(nOutputHeight),
+      m_nComps(nComps),
+      m_bpc(nBpc),
+      m_Pitch(nPitch) {}
+
+ScanlineDecoder::~ScanlineDecoder() = default;
+
+const uint8_t* ScanlineDecoder::GetScanline(int line) {
+  if (m_NextLine == line + 1)
+    return m_pLastScanline;
+
+  if (m_NextLine < 0 || m_NextLine > line) {
+    if (!v_Rewind())
+      return nullptr;
+    m_NextLine = 0;
+  }
+  while (m_NextLine < line) {
+    ReadNextLine();
+    m_NextLine++;
+  }
+  m_pLastScanline = ReadNextLine();
+  m_NextLine++;
+  return m_pLastScanline;
+}
+
+bool ScanlineDecoder::SkipToScanline(int line, PauseIndicatorIface* pPause) {
+  if (m_NextLine == line || m_NextLine == line + 1)
+    return false;
+
+  if (m_NextLine < 0 || m_NextLine > line) {
+    v_Rewind();
+    m_NextLine = 0;
+  }
+  m_pLastScanline = nullptr;
+  while (m_NextLine < line) {
+    m_pLastScanline = ReadNextLine();
+    m_NextLine++;
+    if (pPause && pPause->NeedToPauseNow()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+uint8_t* ScanlineDecoder::ReadNextLine() {
+  return v_GetNextLine();
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/scanlinedecoder.h b/core/fxcodec/scanlinedecoder.h
new file mode 100644
index 0000000..e0014cf
--- /dev/null
+++ b/core/fxcodec/scanlinedecoder.h
@@ -0,0 +1,59 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_SCANLINEDECODER_H_
+#define CORE_FXCODEC_SCANLINEDECODER_H_
+
+#include "core/fxcrt/fx_system.h"
+
+class PauseIndicatorIface;
+
+namespace fxcodec {
+
+class ScanlineDecoder {
+ public:
+  ScanlineDecoder();
+  ScanlineDecoder(int nOrigWidth,
+                  int nOrigHeight,
+                  int nOutputWidth,
+                  int nOutputHeight,
+                  int nComps,
+                  int nBpc,
+                  uint32_t nPitch);
+  virtual ~ScanlineDecoder();
+
+  const uint8_t* GetScanline(int line);
+  bool SkipToScanline(int line, PauseIndicatorIface* pPause);
+
+  int GetWidth() const { return m_OutputWidth; }
+  int GetHeight() const { return m_OutputHeight; }
+  int CountComps() const { return m_nComps; }
+  int GetBPC() const { return m_bpc; }
+
+  virtual uint32_t GetSrcOffset() = 0;
+
+ protected:
+  virtual bool v_Rewind() = 0;
+  virtual uint8_t* v_GetNextLine() = 0;
+
+  uint8_t* ReadNextLine();
+
+  int m_OrigWidth;
+  int m_OrigHeight;
+  int m_OutputWidth;
+  int m_OutputHeight;
+  int m_nComps;
+  int m_bpc;
+  uint32_t m_Pitch;
+  int m_NextLine = -1;
+  uint8_t* m_pLastScanline = nullptr;
+};
+
+}  // namespace fxcodec
+
+using ScanlineDecoder = fxcodec::ScanlineDecoder;
+
+#endif  // CORE_FXCODEC_SCANLINEDECODER_H_
diff --git a/core/fxcodec/tiff/DEPS b/core/fxcodec/tiff/DEPS
new file mode 100644
index 0000000..83b566a
--- /dev/null
+++ b/core/fxcodec/tiff/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/libtiff/tiffiop.h',
+]
diff --git a/core/fxcodec/tiff/tiffmodule.cpp b/core/fxcodec/tiff/tiffmodule.cpp
new file mode 100644
index 0000000..179fca6
--- /dev/null
+++ b/core/fxcodec/tiff/tiffmodule.cpp
@@ -0,0 +1,526 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcodec/tiff/tiffmodule.h"
+
+#include <limits>
+#include <memory>
+
+#include "core/fxcodec/cfx_codec_memory.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_dib.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/ptr_util.h"
+
+extern "C" {
+#include "third_party/libtiff/tiffiop.h"
+}  // extern C
+
+namespace {
+
+// For use with std::unique_ptr<TIFF>.
+struct TiffDeleter {
+  inline void operator()(TIFF* context) { TIFFClose(context); }
+};
+
+}  // namespace
+
+class CTiffContext final : public ModuleIface::Context {
+ public:
+  CTiffContext() = default;
+  ~CTiffContext() override = default;
+
+  bool InitDecoder(const RetainPtr<IFX_SeekableReadStream>& file_ptr);
+  bool LoadFrameInfo(int32_t frame,
+                     int32_t* width,
+                     int32_t* height,
+                     int32_t* comps,
+                     int32_t* bpc,
+                     CFX_DIBAttribute* pAttribute);
+  bool Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+
+  RetainPtr<IFX_SeekableReadStream> io_in() const { return m_io_in; }
+  uint32_t offset() const { return m_offset; }
+  void set_offset(uint32_t offset) { m_offset = offset; }
+
+ private:
+  bool IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const;
+  void SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap, uint16_t bps);
+  bool Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                     int32_t height,
+                     int32_t width,
+                     uint16_t bps,
+                     uint16_t spp);
+  bool Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                     int32_t height,
+                     int32_t width,
+                     uint16_t bps,
+                     uint16_t spp);
+  bool Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                      int32_t height,
+                      int32_t width,
+                      uint16_t bps,
+                      uint16_t spp);
+
+  RetainPtr<IFX_SeekableReadStream> m_io_in;
+  uint32_t m_offset = 0;
+  std::unique_ptr<TIFF, TiffDeleter> m_tif_ctx;
+};
+
+void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz) {
+  return FXMEM_DefaultCalloc(nmemb, siz);
+}
+
+void* _TIFFmalloc(tmsize_t size) {
+  return FXMEM_DefaultAlloc(size);
+}
+
+void _TIFFfree(void* ptr) {
+  if (ptr)
+    FXMEM_DefaultFree(ptr);
+}
+
+void* _TIFFrealloc(void* ptr, tmsize_t size) {
+  return FXMEM_DefaultRealloc(ptr, size);
+}
+
+void _TIFFmemset(void* ptr, int val, tmsize_t size) {
+  memset(ptr, val, static_cast<size_t>(size));
+}
+
+void _TIFFmemcpy(void* des, const void* src, tmsize_t size) {
+  memcpy(des, src, static_cast<size_t>(size));
+}
+
+int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) {
+  return memcmp(ptr1, ptr2, static_cast<size_t>(size));
+}
+
+TIFFErrorHandler _TIFFwarningHandler = nullptr;
+TIFFErrorHandler _TIFFerrorHandler = nullptr;
+
+namespace {
+
+tsize_t tiff_read(thandle_t context, tdata_t buf, tsize_t length) {
+  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
+  FX_SAFE_UINT32 increment = pTiffContext->offset();
+  increment += length;
+  if (!increment.IsValid())
+    return 0;
+
+  FX_FILESIZE offset = pTiffContext->offset();
+  if (!pTiffContext->io_in()->ReadBlockAtOffset(buf, offset, length))
+    return 0;
+
+  pTiffContext->set_offset(increment.ValueOrDie());
+  if (offset + length > pTiffContext->io_in()->GetSize())
+    return pTiffContext->io_in()->GetSize() - offset;
+
+  return length;
+}
+
+tsize_t tiff_write(thandle_t context, tdata_t buf, tsize_t length) {
+  NOTREACHED();
+  return 0;
+}
+
+toff_t tiff_seek(thandle_t context, toff_t offset, int whence) {
+  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
+  FX_SAFE_FILESIZE safe_offset = offset;
+  if (!safe_offset.IsValid())
+    return static_cast<toff_t>(-1);
+  FX_FILESIZE file_offset = safe_offset.ValueOrDie();
+
+  switch (whence) {
+    case 0: {
+      if (file_offset > pTiffContext->io_in()->GetSize())
+        return static_cast<toff_t>(-1);
+      pTiffContext->set_offset(file_offset);
+      return pTiffContext->offset();
+    }
+    case 1: {
+      FX_SAFE_UINT32 new_increment = pTiffContext->offset();
+      new_increment += file_offset;
+      if (!new_increment.IsValid())
+        return static_cast<toff_t>(-1);
+      pTiffContext->set_offset(new_increment.ValueOrDie());
+      return pTiffContext->offset();
+    }
+    case 2: {
+      if (pTiffContext->io_in()->GetSize() < file_offset)
+        return static_cast<toff_t>(-1);
+      pTiffContext->set_offset(pTiffContext->io_in()->GetSize() - file_offset);
+      return pTiffContext->offset();
+    }
+    default:
+      return static_cast<toff_t>(-1);
+  }
+}
+
+int tiff_close(thandle_t context) {
+  return 0;
+}
+
+toff_t tiff_get_size(thandle_t context) {
+  CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
+  return static_cast<toff_t>(pTiffContext->io_in()->GetSize());
+}
+
+int tiff_map(thandle_t context, tdata_t*, toff_t*) {
+  return 0;
+}
+
+void tiff_unmap(thandle_t context, tdata_t, toff_t) {}
+
+TIFF* tiff_open(void* context, const char* mode) {
+  TIFF* tif = TIFFClientOpen("Tiff Image", mode, (thandle_t)context, tiff_read,
+                             tiff_write, tiff_seek, tiff_close, tiff_get_size,
+                             tiff_map, tiff_unmap);
+  if (tif) {
+    tif->tif_fd = (int)(intptr_t)context;
+  }
+  return tif;
+}
+
+template <class T>
+bool Tiff_Exif_GetInfo(TIFF* tif_ctx, ttag_t tag, CFX_DIBAttribute* pAttr) {
+  T val = 0;
+  TIFFGetField(tif_ctx, tag, &val);
+  if (!val)
+    return false;
+  T* ptr = FX_Alloc(T, 1);
+  *ptr = val;
+  pAttr->m_Exif[tag] = ptr;
+  return true;
+}
+
+void Tiff_Exif_GetStringInfo(TIFF* tif_ctx,
+                             ttag_t tag,
+                             CFX_DIBAttribute* pAttr) {
+  char* buf = nullptr;
+  TIFFGetField(tif_ctx, tag, &buf);
+  if (!buf)
+    return;
+  size_t size = strlen(buf);
+  uint8_t* ptr = FX_Alloc(uint8_t, size + 1);
+  memcpy(ptr, buf, size);
+  ptr[size] = 0;
+  pAttr->m_Exif[tag] = ptr;
+}
+
+void TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) {
+  for (int32_t n = 0; n < pixel; n++) {
+    uint8_t tmp = pBuf[0];
+    pBuf[0] = pBuf[2];
+    pBuf[2] = tmp;
+    pBuf += spp;
+  }
+}
+
+}  // namespace
+
+bool CTiffContext::InitDecoder(
+    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
+  m_io_in = file_ptr;
+  m_tif_ctx.reset(tiff_open(this, "r"));
+  return !!m_tif_ctx;
+}
+
+bool CTiffContext::LoadFrameInfo(int32_t frame,
+                                 int32_t* width,
+                                 int32_t* height,
+                                 int32_t* comps,
+                                 int32_t* bpc,
+                                 CFX_DIBAttribute* pAttribute) {
+  if (!TIFFSetDirectory(m_tif_ctx.get(), (uint16)frame))
+    return false;
+
+  uint32_t tif_width = 0;
+  uint32_t tif_height = 0;
+  uint16_t tif_comps = 0;
+  uint16_t tif_bpc = 0;
+  uint32_t tif_rps = 0;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &tif_width);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &tif_height);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &tif_comps);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &tif_bpc);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, &tif_rps);
+
+  pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_INCH;
+  if (TIFFGetField(m_tif_ctx.get(), TIFFTAG_RESOLUTIONUNIT,
+                   &pAttribute->m_wDPIUnit)) {
+    pAttribute->m_wDPIUnit--;
+  }
+  Tiff_Exif_GetInfo<uint16_t>(m_tif_ctx.get(), TIFFTAG_ORIENTATION, pAttribute);
+  if (Tiff_Exif_GetInfo<float>(m_tif_ctx.get(), TIFFTAG_XRESOLUTION,
+                               pAttribute)) {
+    void* val = pAttribute->m_Exif[TIFFTAG_XRESOLUTION];
+    float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
+    pAttribute->m_nXDPI = (int32_t)(fDpi + 0.5f);
+  }
+  if (Tiff_Exif_GetInfo<float>(m_tif_ctx.get(), TIFFTAG_YRESOLUTION,
+                               pAttribute)) {
+    void* val = pAttribute->m_Exif[TIFFTAG_YRESOLUTION];
+    float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
+    pAttribute->m_nYDPI = (int32_t)(fDpi + 0.5f);
+  }
+  Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_IMAGEDESCRIPTION,
+                          pAttribute);
+  Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_MAKE, pAttribute);
+  Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_MODEL, pAttribute);
+
+  FX_SAFE_INT32 checked_width = tif_width;
+  FX_SAFE_INT32 checked_height = tif_height;
+  if (!checked_width.IsValid() || !checked_height.IsValid())
+    return false;
+
+  *width = checked_width.ValueOrDie();
+  *height = checked_height.ValueOrDie();
+  *comps = tif_comps;
+  *bpc = tif_bpc;
+  if (tif_rps > tif_height) {
+    tif_rps = tif_height;
+    TIFFSetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, tif_rps);
+  }
+  return true;
+}
+
+bool CTiffContext::IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const {
+  if (TIFFIsTiled(m_tif_ctx.get()))
+    return false;
+
+  uint16_t photometric = 0;
+  if (!TIFFGetField(m_tif_ctx.get(), TIFFTAG_PHOTOMETRIC, &photometric))
+    return false;
+
+  switch (pDIBitmap->GetBPP()) {
+    case 1:
+    case 8:
+      if (photometric != PHOTOMETRIC_PALETTE) {
+        return false;
+      }
+      break;
+    case 24:
+      if (photometric != PHOTOMETRIC_RGB) {
+        return false;
+      }
+      break;
+    default:
+      return false;
+  }
+  uint16_t planarconfig = 0;
+  if (!TIFFGetFieldDefaulted(m_tif_ctx.get(), TIFFTAG_PLANARCONFIG,
+                             &planarconfig))
+    return false;
+
+  return planarconfig != PLANARCONFIG_SEPARATE;
+}
+
+void CTiffContext::SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                              uint16_t bps) {
+  uint16_t* red_orig = nullptr;
+  uint16_t* green_orig = nullptr;
+  uint16_t* blue_orig = nullptr;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_COLORMAP, &red_orig, &green_orig,
+               &blue_orig);
+  for (int32_t i = (1L << bps) - 1; i >= 0; i--) {
+#define CVT(x) ((uint16_t)((x) >> 8))
+    red_orig[i] = CVT(red_orig[i]);
+    green_orig[i] = CVT(green_orig[i]);
+    blue_orig[i] = CVT(blue_orig[i]);
+#undef CVT
+  }
+  int32_t len = 1 << bps;
+  for (int32_t index = 0; index < len; index++) {
+    uint32_t r = red_orig[index] & 0xFF;
+    uint32_t g = green_orig[index] & 0xFF;
+    uint32_t b = blue_orig[index] & 0xFF;
+    uint32_t color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) |
+                     (((uint32)0xffL) << 24);
+    pDIBitmap->SetPaletteArgb(index, color);
+  }
+}
+
+bool CTiffContext::Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                                 int32_t height,
+                                 int32_t width,
+                                 uint16_t bps,
+                                 uint16_t spp) {
+  if (pDIBitmap->GetBPP() != 1 || spp != 1 || bps != 1 ||
+      !IsSupport(pDIBitmap)) {
+    return false;
+  }
+  SetPalette(pDIBitmap, bps);
+  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get());
+  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
+  if (!buf) {
+    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
+    return false;
+  }
+  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
+  uint32_t pitch = pDIBitmap->GetPitch();
+  for (int32_t row = 0; row < height; row++) {
+    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
+    for (int32_t j = 0; j < size; j++) {
+      bitMapbuffer[row * pitch + j] = buf[j];
+    }
+  }
+  _TIFFfree(buf);
+  return true;
+}
+
+bool CTiffContext::Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                                 int32_t height,
+                                 int32_t width,
+                                 uint16_t bps,
+                                 uint16_t spp) {
+  if (pDIBitmap->GetBPP() != 8 || spp != 1 || (bps != 4 && bps != 8) ||
+      !IsSupport(pDIBitmap)) {
+    return false;
+  }
+  SetPalette(pDIBitmap, bps);
+  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get());
+  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
+  if (!buf) {
+    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
+    return false;
+  }
+  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
+  uint32_t pitch = pDIBitmap->GetPitch();
+  for (int32_t row = 0; row < height; row++) {
+    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
+    for (int32_t j = 0; j < size; j++) {
+      switch (bps) {
+        case 4:
+          bitMapbuffer[row * pitch + 2 * j + 0] = (buf[j] & 0xF0) >> 4;
+          bitMapbuffer[row * pitch + 2 * j + 1] = (buf[j] & 0x0F) >> 0;
+          break;
+        case 8:
+          bitMapbuffer[row * pitch + j] = buf[j];
+          break;
+      }
+    }
+  }
+  _TIFFfree(buf);
+  return true;
+}
+
+bool CTiffContext::Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
+                                  int32_t height,
+                                  int32_t width,
+                                  uint16_t bps,
+                                  uint16_t spp) {
+  if (pDIBitmap->GetBPP() != 24 || !IsSupport(pDIBitmap))
+    return false;
+
+  int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get());
+  uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
+  if (!buf) {
+    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
+    return false;
+  }
+  uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
+  uint32_t pitch = pDIBitmap->GetPitch();
+  for (int32_t row = 0; row < height; row++) {
+    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
+    for (int32_t j = 0; j < size - 2; j += 3) {
+      bitMapbuffer[row * pitch + j + 0] = buf[j + 2];
+      bitMapbuffer[row * pitch + j + 1] = buf[j + 1];
+      bitMapbuffer[row * pitch + j + 2] = buf[j + 0];
+    }
+  }
+  _TIFFfree(buf);
+  return true;
+}
+
+bool CTiffContext::Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  uint32_t img_width = pDIBitmap->GetWidth();
+  uint32_t img_height = pDIBitmap->GetHeight();
+  uint32_t width = 0;
+  uint32_t height = 0;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &width);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &height);
+  if (img_width != width || img_height != height)
+    return false;
+
+  if (pDIBitmap->GetBPP() == 32) {
+    uint16_t rotation = ORIENTATION_TOPLEFT;
+    TIFFGetField(m_tif_ctx.get(), TIFFTAG_ORIENTATION, &rotation);
+    if (TIFFReadRGBAImageOriented(m_tif_ctx.get(), img_width, img_height,
+                                  (uint32*)pDIBitmap->GetBuffer(), rotation,
+                                  1)) {
+      for (uint32_t row = 0; row < img_height; row++) {
+        uint8_t* row_buf = pDIBitmap->GetWritableScanline(row);
+        TiffBGRA2RGBA(row_buf, img_width, 4);
+      }
+      return true;
+    }
+  }
+  uint16_t spp = 0;
+  uint16_t bps = 0;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &spp);
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &bps);
+  FX_SAFE_UINT32 safe_bpp = bps;
+  safe_bpp *= spp;
+  if (!safe_bpp.IsValid())
+    return false;
+  uint32_t bpp = safe_bpp.ValueOrDie();
+  if (bpp == 1)
+    return Decode1bppRGB(pDIBitmap, height, width, bps, spp);
+  if (bpp <= 8)
+    return Decode8bppRGB(pDIBitmap, height, width, bps, spp);
+  if (bpp <= 24)
+    return Decode24bppRGB(pDIBitmap, height, width, bps, spp);
+  return false;
+}
+
+namespace fxcodec {
+
+std::unique_ptr<ModuleIface::Context> TiffModule::CreateDecoder(
+    const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
+  auto pDecoder = pdfium::MakeUnique<CTiffContext>();
+  if (!pDecoder->InitDecoder(file_ptr))
+    return nullptr;
+
+  return pDecoder;
+}
+
+FX_FILESIZE TiffModule::GetAvailInput(Context* pContext) const {
+  NOTREACHED();
+  return 0;
+}
+
+bool TiffModule::Input(Context* pContext,
+                       RetainPtr<CFX_CodecMemory> codec_memory,
+                       CFX_DIBAttribute*) {
+  NOTREACHED();
+  return false;
+}
+
+bool TiffModule::LoadFrameInfo(Context* pContext,
+                               int32_t frame,
+                               int32_t* width,
+                               int32_t* height,
+                               int32_t* comps,
+                               int32_t* bpc,
+                               CFX_DIBAttribute* pAttribute) {
+  ASSERT(pAttribute);
+
+  auto* ctx = static_cast<CTiffContext*>(pContext);
+  return ctx->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute);
+}
+
+bool TiffModule::Decode(Context* pContext,
+                        const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+  auto* ctx = static_cast<CTiffContext*>(pContext);
+  return ctx->Decode(pDIBitmap);
+}
+
+}  // namespace fxcodec
diff --git a/core/fxcodec/tiff/tiffmodule.h b/core/fxcodec/tiff/tiffmodule.h
new file mode 100644
index 0000000..770973b
--- /dev/null
+++ b/core/fxcodec/tiff/tiffmodule.h
@@ -0,0 +1,46 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCODEC_TIFF_TIFFMODULE_H_
+#define CORE_FXCODEC_TIFF_TIFFMODULE_H_
+
+#include <memory>
+
+#include "core/fxcodec/codec_module_iface.h"
+
+class CFX_DIBitmap;
+class IFX_SeekableReadStream;
+
+namespace fxcodec {
+
+class CFX_DIBAttribute;
+
+class TiffModule final : public ModuleIface {
+ public:
+  std::unique_ptr<Context> CreateDecoder(
+      const RetainPtr<IFX_SeekableReadStream>& file_ptr);
+
+  // ModuleIface:
+  FX_FILESIZE GetAvailInput(Context* pContext) const override;
+  bool Input(Context* pContext,
+             RetainPtr<CFX_CodecMemory> codec_memory,
+             CFX_DIBAttribute* pAttribute) override;
+
+  bool LoadFrameInfo(Context* ctx,
+                     int32_t frame,
+                     int32_t* width,
+                     int32_t* height,
+                     int32_t* comps,
+                     int32_t* bpc,
+                     CFX_DIBAttribute* pAttribute);
+  bool Decode(Context* ctx, const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+};
+
+}  // namespace fxcodec
+
+using TiffModule = fxcodec::TiffModule;
+
+#endif  // CORE_FXCODEC_TIFF_TIFFMODULE_H_
diff --git a/core/fxcrt/Android.bp b/core/fxcrt/Android.bp
new file mode 100644
index 0000000..d48b284
--- /dev/null
+++ b/core/fxcrt/Android.bp
@@ -0,0 +1,30 @@
+cc_library_static {
+    name: "libpdfium-fxcrt",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    export_shared_lib_headers: [
+        "libandroidicu",
+    ],
+
+    static_libs: [
+        "libpdfium-third_party-base",
+    ],
+
+    shared_libs: [
+        "libandroidicu",
+    ],
+
+    exclude_srcs: [
+        // is_win
+        "cfx_fileaccess_windows.cpp",
+        // pdf_enable_xfa
+        "cfx_memorystream.cpp",
+    ],
+
+    srcs: [
+        "*.cpp",
+        "xml/*.cpp",
+    ],
+}
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
new file mode 100644
index 0000000..1512f97
--- /dev/null
+++ b/core/fxcrt/BUILD.gn
@@ -0,0 +1,184 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+source_set("fxcrt") {
+  sources = [
+    "autorestorer.h",
+    "byteorder.h",
+    "bytestring.cpp",
+    "bytestring.h",
+    "cfx_binarybuf.cpp",
+    "cfx_binarybuf.h",
+    "cfx_bitstream.cpp",
+    "cfx_bitstream.h",
+    "cfx_datetime.cpp",
+    "cfx_datetime.h",
+    "cfx_fixedbufgrow.h",
+    "cfx_readonlymemorystream.cpp",
+    "cfx_readonlymemorystream.h",
+    "cfx_seekablestreamproxy.cpp",
+    "cfx_seekablestreamproxy.h",
+    "cfx_timer.cpp",
+    "cfx_timer.h",
+    "cfx_utf8decoder.cpp",
+    "cfx_utf8decoder.h",
+    "cfx_utf8encoder.cpp",
+    "cfx_utf8encoder.h",
+    "cfx_widetextbuf.cpp",
+    "cfx_widetextbuf.h",
+    "fileaccess_iface.h",
+    "fx_bidi.cpp",
+    "fx_bidi.h",
+    "fx_codepage.cpp",
+    "fx_codepage.h",
+    "fx_coordinates.cpp",
+    "fx_coordinates.h",
+    "fx_extension.cpp",
+    "fx_extension.h",
+    "fx_memory.cpp",
+    "fx_memory.h",
+    "fx_memory_wrappers.h",
+    "fx_number.cpp",
+    "fx_number.h",
+    "fx_random.cpp",
+    "fx_random.h",
+    "fx_safe_types.h",
+    "fx_stream.cpp",
+    "fx_stream.h",
+    "fx_string.cpp",
+    "fx_string.h",
+    "fx_system.cpp",
+    "fx_system.h",
+    "fx_unicode.cpp",
+    "fx_unicode.h",
+    "maybe_owned.h",
+    "observed_ptr.cpp",
+    "observed_ptr.h",
+    "pauseindicator_iface.h",
+    "retain_ptr.h",
+    "retained_tree_node.h",
+    "shared_copy_on_write.h",
+    "string_data_template.h",
+    "string_pool_template.h",
+    "string_view_template.h",
+    "timerhandler_iface.h",
+    "tree_node.h",
+    "unowned_ptr.h",
+    "weak_ptr.h",
+    "widestring.cpp",
+    "widestring.h",
+    "xml/cfx_xmlchardata.cpp",
+    "xml/cfx_xmlchardata.h",
+    "xml/cfx_xmldocument.cpp",
+    "xml/cfx_xmldocument.h",
+    "xml/cfx_xmlelement.cpp",
+    "xml/cfx_xmlelement.h",
+    "xml/cfx_xmlinstruction.cpp",
+    "xml/cfx_xmlinstruction.h",
+    "xml/cfx_xmlnode.cpp",
+    "xml/cfx_xmlnode.h",
+    "xml/cfx_xmlparser.cpp",
+    "xml/cfx_xmlparser.h",
+    "xml/cfx_xmltext.cpp",
+    "xml/cfx_xmltext.h",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  visibility = [
+    "../*",
+    "../../:*",
+    "../../fpdfsdk/*",
+    "../../fxbarcode:*",
+    "../../fxjs:*",
+    "../../testing:*",
+    "../../testing/fuzzers/*",
+    "../../third_party:fx_agg",
+    "../../third_party:fx_lcms2",
+    "../../third_party:fx_tiff",
+    "../../third_party:pdfium_base",
+    "../../xfa/*",
+  ]
+  deps = [ "../../third_party:pdfium_base" ]
+  public_deps = [
+    "../../:freetype_common",
+    "../../third_party:pdfium_base",
+    "//third_party/icu:icuuc",
+  ]
+  allow_circular_includes_from = [ "../../third_party:pdfium_base" ]
+
+  if (is_posix || is_fuchsia) {
+    sources += [
+      "cfx_fileaccess_posix.cpp",
+      "cfx_fileaccess_posix.h",
+    ]
+  }
+  if (is_win) {
+    sources += [
+      "cfx_fileaccess_windows.cpp",
+      "cfx_fileaccess_windows.h",
+    ]
+  }
+  if (pdf_enable_xfa) {
+    sources += [
+      "cfx_memorystream.cpp",
+      "cfx_memorystream.h",
+    ]
+  }
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "autorestorer_unittest.cpp",
+    "byteorder_unittest.cpp",
+    "bytestring_unittest.cpp",
+    "cfx_bitstream_unittest.cpp",
+    "cfx_seekablestreamproxy_unittest.cpp",
+    "cfx_timer_unittest.cpp",
+    "cfx_widetextbuf_unittest.cpp",
+    "fx_bidi_unittest.cpp",
+    "fx_coordinates_unittest.cpp",
+    "fx_extension_unittest.cpp",
+    "fx_memory_unittest.cpp",
+    "fx_memory_wrappers_unittest.cpp",
+    "fx_number_unittest.cpp",
+    "fx_random_unittest.cpp",
+    "fx_string_unittest.cpp",
+    "fx_system_unittest.cpp",
+    "maybe_owned_unittest.cpp",
+    "observed_ptr_unittest.cpp",
+    "pdfium_span_unittest.cpp",
+    "retain_ptr_unittest.cpp",
+    "retained_tree_node_unittest.cpp",
+    "shared_copy_on_write_unittest.cpp",
+    "string_pool_template_unittest.cpp",
+    "tree_node_unittest.cpp",
+    "unowned_ptr_unittest.cpp",
+    "weak_ptr_unittest.cpp",
+    "widestring_unittest.cpp",
+    "xml/cfx_xmlchardata_unittest.cpp",
+    "xml/cfx_xmldocument_unittest.cpp",
+    "xml/cfx_xmlelement_unittest.cpp",
+    "xml/cfx_xmlinstruction_unittest.cpp",
+    "xml/cfx_xmlnode_unittest.cpp",
+    "xml/cfx_xmlparser_unittest.cpp",
+    "xml/cfx_xmltext_unittest.cpp",
+  ]
+  deps = []
+  pdfium_root_dir = "../../"
+
+  if (pdf_enable_xfa) {
+    sources += [
+      "cfx_memorystream_unittest.cpp",
+      "css/cfx_cssdeclaration_unittest.cpp",
+      "css/cfx_cssstylesheet_unittest.cpp",
+      "css/cfx_cssvaluelistparser_unittest.cpp",
+    ]
+    deps += [
+      "../fpdfapi/parser",
+      "css",
+    ]
+  }
+}
diff --git a/core/fxcrt/DEPS b/core/fxcrt/DEPS
new file mode 100644
index 0000000..2be0352
--- /dev/null
+++ b/core/fxcrt/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/icu',
+]
diff --git a/core/fxcrt/autorestorer.h b/core/fxcrt/autorestorer.h
index 745a56f..cafa075 100644
--- a/core/fxcrt/autorestorer.h
+++ b/core/fxcrt/autorestorer.h
@@ -12,10 +12,14 @@
  public:
   explicit AutoRestorer(T* location)
       : m_Location(location), m_OldValue(*location) {}
-  ~AutoRestorer() { *m_Location = m_OldValue; }
+  ~AutoRestorer() {
+    if (m_Location)
+      *m_Location = m_OldValue;
+  }
+  void AbandonRestoration() { m_Location = nullptr; }
 
  private:
-  T* const m_Location;
+  T* m_Location;
   const T m_OldValue;
 };
 
diff --git a/core/fxcrt/autorestorer_unittest.cpp b/core/fxcrt/autorestorer_unittest.cpp
new file mode 100644
index 0000000..6430fb6
--- /dev/null
+++ b/core/fxcrt/autorestorer_unittest.cpp
@@ -0,0 +1,166 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/pseudo_retainable.h"
+
+TEST(fxcrt, AutoRestorer) {
+  int x = 5;
+  {
+    AutoRestorer<int> restorer(&x);
+    x = 6;
+    EXPECT_EQ(6, x);
+  }
+  EXPECT_EQ(5, x);
+}
+
+TEST(fxcrt, AutoRestorerNoOp) {
+  int x = 5;
+  {
+    AutoRestorer<int> restorer(&x);
+    EXPECT_EQ(5, x);
+  }
+  EXPECT_EQ(5, x);
+}
+
+TEST(fxcrt, AutoRestorerAbandon) {
+  int x = 5;
+  {
+    AutoRestorer<int> restorer(&x);
+    x = 6;
+    EXPECT_EQ(6, x);
+    restorer.AbandonRestoration();
+  }
+  EXPECT_EQ(6, x);
+}
+
+TEST(fxcrt, AutoRestorerUnownedPtr) {
+  int x = 5;
+  int y = 6;
+  UnownedPtr<int> ptr(&x);
+  {
+    AutoRestorer<UnownedPtr<int>> restorer(&ptr);
+    ptr = &y;
+    EXPECT_EQ(&y, ptr);
+  }
+  EXPECT_EQ(&x, ptr);
+}
+
+TEST(fxcrt, AutoRestorerUnownedNoOp) {
+  int x = 5;
+  UnownedPtr<int> ptr(&x);
+  {
+    AutoRestorer<UnownedPtr<int>> restorer(&ptr);
+    EXPECT_EQ(&x, ptr);
+  }
+  EXPECT_EQ(&x, ptr);
+}
+
+TEST(fxcrt, AutoRestorerUnownedPtrAbandon) {
+  int x = 5;
+  int y = 6;
+  UnownedPtr<int> ptr(&x);
+  {
+    AutoRestorer<UnownedPtr<int>> restorer(&ptr);
+    ptr = &y;
+    EXPECT_EQ(&y, ptr);
+    restorer.AbandonRestoration();
+  }
+  EXPECT_EQ(&y, ptr);
+}
+
+TEST(fxcrt, AutoRestorerRetainPtr) {
+  PseudoRetainable obj1;
+  PseudoRetainable obj2;
+  RetainPtr<PseudoRetainable> ptr(&obj1);
+  EXPECT_EQ(1, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  {
+    AutoRestorer<RetainPtr<PseudoRetainable>> restorer(&ptr);
+    // |obj1| is kept alive by restorer in case it need to be restored.
+    EXPECT_EQ(2, obj1.retain_count());
+    EXPECT_EQ(0, obj1.release_count());
+    ptr.Reset(&obj2);
+    EXPECT_EQ(&obj2, ptr.Get());
+
+    // |obj1| released by |ptr| assignment.
+    EXPECT_TRUE(obj1.alive());
+    EXPECT_EQ(2, obj1.retain_count());
+    EXPECT_EQ(1, obj1.release_count());
+
+    // |obj2| now kept alive by |ptr|.
+    EXPECT_TRUE(obj1.alive());
+    EXPECT_EQ(1, obj2.retain_count());
+    EXPECT_EQ(0, obj2.release_count());
+  }
+  EXPECT_EQ(&obj1, ptr.Get());
+
+  // |obj1| now kept alive again by |ptr|.
+  EXPECT_TRUE(obj1.alive());
+  EXPECT_EQ(3, obj1.retain_count());
+  EXPECT_EQ(2, obj1.release_count());
+
+  // |obj2| dead.
+  EXPECT_FALSE(obj2.alive());
+  EXPECT_EQ(1, obj2.retain_count());
+  EXPECT_EQ(1, obj2.release_count());
+}
+
+TEST(fxcrt, AutoRestorerRetainPtrNoOp) {
+  PseudoRetainable obj1;
+  RetainPtr<PseudoRetainable> ptr(&obj1);
+  EXPECT_EQ(1, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  {
+    AutoRestorer<RetainPtr<PseudoRetainable>> restorer(&ptr);
+    EXPECT_EQ(2, obj1.retain_count());
+    EXPECT_EQ(0, obj1.release_count());
+  }
+  EXPECT_EQ(&obj1, ptr.Get());
+
+  // Self-reassignement avoided ref churn.
+  EXPECT_TRUE(obj1.alive());
+  EXPECT_EQ(2, obj1.retain_count());
+  EXPECT_EQ(1, obj1.release_count());
+}
+
+TEST(fxcrt, AutoRestorerRetainPtrAbandon) {
+  PseudoRetainable obj1;
+  PseudoRetainable obj2;
+  RetainPtr<PseudoRetainable> ptr(&obj1);
+  EXPECT_EQ(1, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  {
+    AutoRestorer<RetainPtr<PseudoRetainable>> restorer(&ptr);
+    // |obj1| is kept alive by restorer in case it need to be restored.
+    EXPECT_EQ(2, obj1.retain_count());
+    EXPECT_EQ(0, obj1.release_count());
+    ptr.Reset(&obj2);
+    EXPECT_EQ(&obj2, ptr.Get());
+
+    // |obj1| released by |ptr| assignment.
+    EXPECT_EQ(2, obj1.retain_count());
+    EXPECT_EQ(1, obj1.release_count());
+
+    // |obj2| now kept alive by |ptr|.
+    EXPECT_EQ(1, obj2.retain_count());
+    EXPECT_EQ(0, obj2.release_count());
+
+    restorer.AbandonRestoration();
+  }
+  EXPECT_EQ(&obj2, ptr.Get());
+
+  // |obj1| now dead as a result of abandonment.
+  EXPECT_FALSE(obj1.alive());
+  EXPECT_EQ(2, obj1.retain_count());
+  EXPECT_EQ(2, obj1.release_count());
+
+  // |obj2| kept alive by |ptr|.
+  EXPECT_TRUE(obj2.alive());
+  EXPECT_EQ(1, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+}
diff --git a/core/fxcrt/byteorder.h b/core/fxcrt/byteorder.h
new file mode 100644
index 0000000..283ac4e
--- /dev/null
+++ b/core/fxcrt/byteorder.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_BYTEORDER_H_
+#define CORE_FXCRT_BYTEORDER_H_
+
+#include "build/build_config.h"
+#include "third_party/base/sys_byteorder.h"
+
+namespace fxcrt {
+
+// Converts the bytes in |x| from host order (endianness) to little endian, and
+// returns the result.
+inline uint16_t ByteSwapToLE16(uint16_t x) {
+  return pdfium::base::ByteSwapToLE16(x);
+}
+
+inline uint32_t ByteSwapToLE32(uint32_t x) {
+  return pdfium::base::ByteSwapToLE32(x);
+}
+
+// Converts the bytes in |x| from host order (endianness) to big endian, and
+// returns the result.
+inline uint16_t ByteSwapToBE16(uint16_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  return pdfium::base::ByteSwap(x);
+#else
+  return x;
+#endif
+}
+
+inline uint32_t ByteSwapToBE32(uint32_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  return pdfium::base::ByteSwap(x);
+#else
+  return x;
+#endif
+}
+
+}  // namespace fxcrt
+
+#endif  // CORE_FXCRT_BYTEORDER_H_
diff --git a/core/fxcrt/byteorder_unittest.cpp b/core/fxcrt/byteorder_unittest.cpp
new file mode 100644
index 0000000..20f7a12
--- /dev/null
+++ b/core/fxcrt/byteorder_unittest.cpp
@@ -0,0 +1,71 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/byteorder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Original code to use as a reference implementation.
+
+#define FXWORD_GET_LSBFIRST(p)                                \
+  (static_cast<uint16_t>((static_cast<uint16_t>(p[1]) << 8) | \
+                         (static_cast<uint16_t>(p[0]))))
+#define FXWORD_GET_MSBFIRST(p)                                \
+  (static_cast<uint16_t>((static_cast<uint16_t>(p[0]) << 8) | \
+                         (static_cast<uint16_t>(p[1]))))
+#define FXDWORD_GET_LSBFIRST(p)                                                \
+  ((static_cast<uint32_t>(p[3]) << 24) | (static_cast<uint32_t>(p[2]) << 16) | \
+   (static_cast<uint32_t>(p[1]) << 8) | (static_cast<uint32_t>(p[0])))
+#define FXDWORD_GET_MSBFIRST(p)                                                \
+  ((static_cast<uint32_t>(p[0]) << 24) | (static_cast<uint32_t>(p[1]) << 16) | \
+   (static_cast<uint32_t>(p[2]) << 8) | (static_cast<uint32_t>(p[3])))
+
+constexpr uint32_t kTestValues32[] = {
+    0x0,      0x1,        0x2,        0x3,        0x4,       0xfe,
+    0xff,     0x100,      0x101,      0xffff,     0x10000,   0x123456,
+    0x345167, 0x2f3e4a5b, 0xff000000, 0xfffffffe, 0xffffffff};
+
+}  // namespace
+
+namespace fxcrt {
+
+TEST(ByteOrder, ByteSwapToLE16) {
+  // Since there are so few values, test them all.
+  for (uint32_t v = 0; v < 0x10000; ++v) {
+    const uint16_t v16 = v;
+    uint16_t expected =
+        FXWORD_GET_LSBFIRST(reinterpret_cast<const uint8_t*>(&v16));
+    EXPECT_EQ(expected, ByteSwapToLE16(v16)) << v;
+  }
+}
+
+TEST(ByteOrder, ByteSwapToLE32) {
+  for (uint32_t v : kTestValues32) {
+    uint32_t expected =
+        FXDWORD_GET_LSBFIRST(reinterpret_cast<const uint8_t*>(&v));
+    EXPECT_EQ(expected, ByteSwapToLE32(v)) << v;
+  }
+}
+
+TEST(ByteOrder, ByteSwapToBE16) {
+  // Since there are so few values, test them all.
+  for (uint32_t v = 0; v < 0x10000; ++v) {
+    const uint16_t v16 = v;
+    uint16_t expected =
+        FXWORD_GET_MSBFIRST(reinterpret_cast<const uint8_t*>(&v16));
+    EXPECT_EQ(expected, ByteSwapToBE16(v16)) << v;
+  }
+}
+
+TEST(ByteOrder, ByteSwapToBE32) {
+  for (uint32_t v : kTestValues32) {
+    uint32_t expected =
+        FXDWORD_GET_MSBFIRST(reinterpret_cast<const uint8_t*>(&v));
+    EXPECT_EQ(expected, ByteSwapToBE32(v)) << v;
+  }
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/bytestring.cpp b/core/fxcrt/bytestring.cpp
index 2ab1379..0778bc2 100644
--- a/core/fxcrt/bytestring.cpp
+++ b/core/fxcrt/bytestring.cpp
@@ -11,6 +11,7 @@
 #include <algorithm>
 #include <cctype>
 #include <string>
+#include <utility>
 
 #include "core/fxcrt/cfx_utf8decoder.h"
 #include "core/fxcrt/fx_codepage.h"
@@ -18,6 +19,7 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "third_party/base/numerics/safe_math.h"
+#include "third_party/base/span.h"
 #include "third_party/base/stl_util.h"
 
 template class fxcrt::StringDataTemplate<char>;
@@ -53,41 +55,6 @@
   return nullptr;
 }
 
-#ifndef NDEBUG
-bool IsValidCodePage(uint16_t codepage) {
-  switch (codepage) {
-    case FX_CODEPAGE_DefANSI:
-    case FX_CODEPAGE_ShiftJIS:
-    case FX_CODEPAGE_ChineseSimplified:
-    case FX_CODEPAGE_Hangul:
-    case FX_CODEPAGE_ChineseTraditional:
-      return true;
-    default:
-      return false;
-  }
-}
-#endif
-
-ByteString GetByteString(uint16_t codepage, const WideStringView& wstr) {
-#ifndef NDEBUG
-  ASSERT(IsValidCodePage(codepage));
-#endif
-
-  int src_len = wstr.GetLength();
-  int dest_len =
-      FXSYS_WideCharToMultiByte(codepage, 0, wstr.unterminated_c_str(), src_len,
-                                nullptr, 0, nullptr, nullptr);
-  if (!dest_len)
-    return ByteString();
-
-  ByteString bstr;
-  char* dest_buf = bstr.GetBuffer(dest_len);
-  FXSYS_WideCharToMultiByte(codepage, 0, wstr.unterminated_c_str(), src_len,
-                            dest_buf, dest_len, nullptr, nullptr);
-  bstr.ReleaseBuffer(dest_len);
-  return bstr;
-}
-
 }  // namespace
 
 namespace fxcrt {
@@ -107,9 +74,9 @@
 }
 
 // static
-ByteString ByteString::FormatFloat(float d) {
+ByteString ByteString::FormatFloat(float f) {
   char buf[32];
-  return ByteString(buf, FX_ftoa(d, buf));
+  return ByteString(buf, FloatToString(f, buf));
 }
 
 // static
@@ -120,19 +87,21 @@
   va_end(argListCopy);
 
   if (nMaxLen <= 0)
-    return "";
+    return ByteString();
 
   ByteString ret;
-  char* buf = ret.GetBuffer(nMaxLen);
-  if (buf) {
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> buf = ret.GetBuffer(nMaxLen);
+
     // In the following two calls, there's always space in the buffer for
     // a terminating NUL that's not included in nMaxLen.
-    memset(buf, 0, nMaxLen + 1);
+    memset(buf.data(), 0, nMaxLen + 1);
     va_copy(argListCopy, argList);
-    vsnprintf(buf, nMaxLen + 1, pFormat, argListCopy);
+    vsnprintf(buf.data(), nMaxLen + 1, pFormat, argListCopy);
     va_end(argListCopy);
-    ret.ReleaseBuffer(ret.GetStringLength());
   }
+  ret.ReleaseBuffer(ret.GetStringLength());
   return ret;
 }
 
@@ -157,7 +126,7 @@
         StringData::Create(reinterpret_cast<const char*>(pStr), nLen));
 }
 
-ByteString::ByteString() {}
+ByteString::ByteString() = default;
 
 ByteString::ByteString(const ByteString& other) : m_pData(other.m_pData) {}
 
@@ -173,13 +142,14 @@
 ByteString::ByteString(const char* ptr)
     : ByteString(ptr, ptr ? strlen(ptr) : 0) {}
 
-ByteString::ByteString(const ByteStringView& stringSrc) {
-  if (!stringSrc.IsEmpty())
-    m_pData.Reset(StringData::Create(stringSrc.unterminated_c_str(),
-                                     stringSrc.GetLength()));
+ByteString::ByteString(ByteStringView bstrc) {
+  if (!bstrc.IsEmpty()) {
+    m_pData.Reset(
+        StringData::Create(bstrc.unterminated_c_str(), bstrc.GetLength()));
+  }
 }
 
-ByteString::ByteString(const ByteStringView& str1, const ByteStringView& str2) {
+ByteString::ByteString(ByteStringView str1, ByteStringView str2) {
   FX_SAFE_SIZE_T nSafeLen = str1.GetLength();
   nSafeLen += str2.GetLength();
 
@@ -220,51 +190,58 @@
 
 ByteString::~ByteString() {}
 
-const ByteString& ByteString::operator=(const char* pStr) {
-  if (!pStr || !pStr[0])
+ByteString& ByteString::operator=(const char* str) {
+  if (!str || !str[0])
     clear();
   else
-    AssignCopy(pStr, strlen(pStr));
+    AssignCopy(str, strlen(str));
 
   return *this;
 }
 
-const ByteString& ByteString::operator=(const ByteStringView& stringSrc) {
-  if (stringSrc.IsEmpty())
+ByteString& ByteString::operator=(ByteStringView str) {
+  if (str.IsEmpty())
     clear();
   else
-    AssignCopy(stringSrc.unterminated_c_str(), stringSrc.GetLength());
+    AssignCopy(str.unterminated_c_str(), str.GetLength());
 
   return *this;
 }
 
-const ByteString& ByteString::operator=(const ByteString& stringSrc) {
-  if (m_pData != stringSrc.m_pData)
-    m_pData = stringSrc.m_pData;
+ByteString& ByteString::operator=(const ByteString& that) {
+  if (m_pData != that.m_pData)
+    m_pData = that.m_pData;
 
   return *this;
 }
 
-const ByteString& ByteString::operator+=(const char* pStr) {
-  if (pStr)
-    Concat(pStr, strlen(pStr));
+ByteString& ByteString::operator=(ByteString&& that) {
+  if (m_pData != that.m_pData)
+    m_pData = std::move(that.m_pData);
 
   return *this;
 }
 
-const ByteString& ByteString::operator+=(char ch) {
+ByteString& ByteString::operator+=(const char* str) {
+  if (str)
+    Concat(str, strlen(str));
+
+  return *this;
+}
+
+ByteString& ByteString::operator+=(char ch) {
   Concat(&ch, 1);
   return *this;
 }
 
-const ByteString& ByteString::operator+=(const ByteString& str) {
+ByteString& ByteString::operator+=(const ByteString& str) {
   if (str.m_pData)
     Concat(str.m_pData->m_String, str.m_pData->m_nDataLength);
 
   return *this;
 }
 
-const ByteString& ByteString::operator+=(const ByteStringView& str) {
+ByteString& ByteString::operator+=(ByteStringView str) {
   if (!str.IsEmpty())
     Concat(str.unterminated_c_str(), str.GetLength());
 
@@ -282,7 +259,7 @@
          memcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
 }
 
-bool ByteString::operator==(const ByteStringView& str) const {
+bool ByteString::operator==(ByteStringView str) const {
   if (!m_pData)
     return str.IsEmpty();
 
@@ -318,7 +295,7 @@
   return result < 0 || (result == 0 && len < other_len);
 }
 
-bool ByteString::operator<(const ByteStringView& str) const {
+bool ByteString::operator<(ByteStringView str) const {
   return Compare(str) < 0;
 }
 
@@ -332,7 +309,7 @@
   return result < 0 || (result == 0 && len < other_len);
 }
 
-bool ByteString::EqualNoCase(const ByteStringView& str) const {
+bool ByteString::EqualNoCase(ByteStringView str) const {
   if (!m_pData)
     return str.IsEmpty();
 
@@ -344,8 +321,8 @@
   const uint8_t* pThat = str.raw_str();
   for (size_t i = 0; i < len; i++) {
     if ((*pThis) != (*pThat)) {
-      uint8_t bThis = FXSYS_tolower(*pThis);
-      uint8_t bThat = FXSYS_tolower(*pThat);
+      uint8_t bThis = tolower(*pThis);
+      uint8_t bThat = tolower(*pThat);
       if (bThis != bThat)
         return false;
     }
@@ -419,29 +396,29 @@
   GetBuffer(len);
 }
 
-char* ByteString::GetBuffer(size_t nMinBufLength) {
+pdfium::span<char> ByteString::GetBuffer(size_t nMinBufLength) {
   if (!m_pData) {
     if (nMinBufLength == 0)
-      return nullptr;
+      return pdfium::span<char>();
 
     m_pData.Reset(StringData::Create(nMinBufLength));
     m_pData->m_nDataLength = 0;
     m_pData->m_String[0] = 0;
-    return m_pData->m_String;
+    return pdfium::span<char>(m_pData->m_String, m_pData->m_nAllocLength);
   }
 
   if (m_pData->CanOperateInPlace(nMinBufLength))
-    return m_pData->m_String;
+    return pdfium::span<char>(m_pData->m_String, m_pData->m_nAllocLength);
 
   nMinBufLength = std::max(nMinBufLength, m_pData->m_nDataLength);
   if (nMinBufLength == 0)
-    return nullptr;
+    return pdfium::span<char>();
 
   RetainPtr<StringData> pNewData(StringData::Create(nMinBufLength));
   pNewData->CopyContents(*m_pData);
   pNewData->m_nDataLength = m_pData->m_nDataLength;
   m_pData.Swap(pNewData);
-  return m_pData->m_String;
+  return pdfium::span<char>(m_pData->m_String, m_pData->m_nAllocLength);
 }
 
 size_t ByteString::Delete(size_t index, size_t count) {
@@ -449,8 +426,7 @@
     return 0;
 
   size_t old_length = m_pData->m_nDataLength;
-  if (count == 0 ||
-      index != pdfium::clamp(index, static_cast<size_t>(0), old_length))
+  if (count == 0 || index != pdfium::clamp<size_t>(index, 0, old_length))
     return old_length;
 
   size_t removal_length = index + count;
@@ -480,14 +456,20 @@
     return;
   }
 
+  size_t nConcatLen = std::max(m_pData->m_nDataLength / 2, nSrcLen);
   RetainPtr<StringData> pNewData(
-      StringData::Create(m_pData->m_nDataLength + nSrcLen));
+      StringData::Create(m_pData->m_nDataLength + nConcatLen));
   pNewData->CopyContents(*m_pData);
   pNewData->CopyContentsAt(m_pData->m_nDataLength, pSrcData, nSrcLen);
+  pNewData->m_nDataLength = m_pData->m_nDataLength + nSrcLen;
   m_pData.Swap(pNewData);
 }
 
-ByteString ByteString::Mid(size_t first, size_t count) const {
+intptr_t ByteString::ReferenceCountForTesting() const {
+  return m_pData ? m_pData->m_nRefs : 0;
+}
+
+ByteString ByteString::Substr(size_t first, size_t count) const {
   if (!m_pData)
     return ByteString();
 
@@ -508,16 +490,16 @@
   return dest;
 }
 
-ByteString ByteString::Left(size_t count) const {
+ByteString ByteString::First(size_t count) const {
   if (count == 0 || !IsValidLength(count))
     return ByteString();
-  return Mid(0, count);
+  return Substr(0, count);
 }
 
-ByteString ByteString::Right(size_t count) const {
+ByteString ByteString::Last(size_t count) const {
   if (count == 0 || !IsValidLength(count))
     return ByteString();
-  return Mid(GetLength() - count, count);
+  return Substr(GetLength() - count, count);
 }
 
 void ByteString::AllocCopy(ByteString& dest,
@@ -537,58 +519,57 @@
   m_pData->m_String[index] = c;
 }
 
-size_t ByteString::Insert(size_t location, char ch) {
-  const size_t cur_length = m_pData ? m_pData->m_nDataLength : 0;
-  if (!IsValidLength(location))
+size_t ByteString::Insert(size_t index, char ch) {
+  const size_t cur_length = GetLength();
+  if (!IsValidLength(index))
     return cur_length;
 
   const size_t new_length = cur_length + 1;
   ReallocBeforeWrite(new_length);
-  memmove(m_pData->m_String + location + 1, m_pData->m_String + location,
-          new_length - location);
-  m_pData->m_String[location] = ch;
+  memmove(m_pData->m_String + index + 1, m_pData->m_String + index,
+          new_length - index);
+  m_pData->m_String[index] = ch;
   m_pData->m_nDataLength = new_length;
   return new_length;
 }
 
 Optional<size_t> ByteString::Find(char ch, size_t start) const {
   if (!m_pData)
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   if (!IsValidIndex(start))
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   const char* pStr = static_cast<const char*>(
       memchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start));
   return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : Optional<size_t>();
+              : pdfium::nullopt;
 }
 
-Optional<size_t> ByteString::Find(const ByteStringView& subStr,
-                                  size_t start) const {
+Optional<size_t> ByteString::Find(ByteStringView subStr, size_t start) const {
   if (!m_pData)
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   if (!IsValidIndex(start))
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   const char* pStr =
       FX_strstr(m_pData->m_String + start, m_pData->m_nDataLength - start,
                 subStr.unterminated_c_str(), subStr.GetLength());
   return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : Optional<size_t>();
+              : pdfium::nullopt;
 }
 
 Optional<size_t> ByteString::ReverseFind(char ch) const {
   if (!m_pData)
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   size_t nLength = m_pData->m_nDataLength;
   while (nLength--) {
     if (m_pData->m_String[nLength] == ch)
-      return Optional<size_t>(nLength);
+      return nLength;
   }
-  return Optional<size_t>();
+  return pdfium::nullopt;
 }
 
 void ByteString::MakeLower() {
@@ -608,7 +589,7 @@
 }
 
 size_t ByteString::Remove(char chRemove) {
-  if (!m_pData || m_pData->m_nDataLength < 1)
+  if (!m_pData || m_pData->m_nDataLength == 0)
     return 0;
 
   char* pstrSource = m_pData->m_String;
@@ -641,8 +622,7 @@
   return nCount;
 }
 
-size_t ByteString::Replace(const ByteStringView& pOld,
-                           const ByteStringView& pNew) {
+size_t ByteString::Replace(ByteStringView pOld, ByteStringView pNew) {
   if (!m_pData || pOld.IsEmpty())
     return 0;
 
@@ -688,20 +668,7 @@
   return nCount;
 }
 
-WideString ByteString::UTF8Decode() const {
-  CFX_UTF8Decoder decoder;
-  for (size_t i = 0; i < GetLength(); i++) {
-    decoder.Input(static_cast<uint8_t>(m_pData->m_String[i]));
-  }
-  return WideString(decoder.GetResult());
-}
-
-// static
-ByteString ByteString::FromUnicode(const WideString& str) {
-  return GetByteString(0, str.AsStringView());
-}
-
-int ByteString::Compare(const ByteStringView& str) const {
+int ByteString::Compare(ByteStringView str) const {
   if (!m_pData)
     return str.IsEmpty() ? 0 : -1;
 
@@ -727,7 +694,7 @@
   TrimLeft(targets);
 }
 
-void ByteString::Trim(const ByteStringView& targets) {
+void ByteString::Trim(ByteStringView targets) {
   TrimRight(targets);
   TrimLeft(targets);
 }
@@ -740,7 +707,7 @@
   TrimLeft(ByteStringView(target));
 }
 
-void ByteString::TrimLeft(const ByteStringView& targets) {
+void ByteString::TrimLeft(ByteStringView targets) {
   if (!m_pData || targets.IsEmpty())
     return;
 
@@ -774,7 +741,7 @@
   TrimRight(ByteStringView(target));
 }
 
-void ByteString::TrimRight(const ByteStringView& targets) {
+void ByteString::TrimRight(ByteStringView targets) {
   if (!m_pData || targets.IsEmpty())
     return;
 
@@ -801,8 +768,32 @@
   return os.write(str.c_str(), str.GetLength());
 }
 
-std::ostream& operator<<(std::ostream& os, const ByteStringView& str) {
+std::ostream& operator<<(std::ostream& os, ByteStringView str) {
   return os.write(str.unterminated_c_str(), str.GetLength());
 }
 
 }  // namespace fxcrt
+
+uint32_t FX_HashCode_GetA(ByteStringView str, bool bIgnoreCase) {
+  uint32_t dwHashCode = 0;
+  if (bIgnoreCase) {
+    for (ByteStringView::UnsignedType c : str)
+      dwHashCode = 31 * dwHashCode + tolower(c);
+  } else {
+    for (ByteStringView::UnsignedType c : str)
+      dwHashCode = 31 * dwHashCode + c;
+  }
+  return dwHashCode;
+}
+
+uint32_t FX_HashCode_GetAsIfW(ByteStringView str, bool bIgnoreCase) {
+  uint32_t dwHashCode = 0;
+  if (bIgnoreCase) {
+    for (ByteStringView::UnsignedType c : str)
+      dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c);
+  } else {
+    for (ByteStringView::UnsignedType c : str)
+      dwHashCode = 1313 * dwHashCode + c;
+  }
+  return dwHashCode;
+}
diff --git a/core/fxcrt/bytestring.h b/core/fxcrt/bytestring.h
index 70f10dc..7c7f2ee 100644
--- a/core/fxcrt/bytestring.h
+++ b/core/fxcrt/bytestring.h
@@ -9,6 +9,7 @@
 
 #include <functional>
 #include <iterator>
+#include <ostream>
 #include <sstream>
 #include <utility>
 
@@ -16,14 +17,12 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_data_template.h"
 #include "core/fxcrt/string_view_template.h"
+#include "third_party/base/logging.h"
 #include "third_party/base/optional.h"
+#include "third_party/base/span.h"
 
 namespace fxcrt {
 
-class ByteString_Concat_Test;
-class StringPool_ByteString_Test;
-class WideString;
-
 // A mutable string with shared buffers using copy-on-write semantics that
 // avoids the cost of std::string's iterator stability guarantees.
 class ByteString {
@@ -34,12 +33,14 @@
 
   static ByteString FormatInteger(int i) WARN_UNUSED_RESULT;
   static ByteString FormatFloat(float f) WARN_UNUSED_RESULT;
-  static ByteString Format(const char* lpszFormat, ...) WARN_UNUSED_RESULT;
-  static ByteString FormatV(const char* lpszFormat,
+  static ByteString Format(const char* pFormat, ...) WARN_UNUSED_RESULT;
+  static ByteString FormatV(const char* pFormat,
                             va_list argList) WARN_UNUSED_RESULT;
 
   ByteString();
   ByteString(const ByteString& other);
+
+  // Move-construct a ByteString. After construction, |other| is empty.
   ByteString(ByteString&& other) noexcept;
 
   // Deliberately implicit to avoid calling on every string literal.
@@ -52,11 +53,11 @@
   // NOLINTNEXTLINE(runtime/explicit)
   ByteString(wchar_t) = delete;
 
-  ByteString(const char* ptr, size_t len);
-  ByteString(const uint8_t* ptr, size_t len);
+  ByteString(const char* pStr, size_t len);
+  ByteString(const uint8_t* pStr, size_t len);
 
-  explicit ByteString(const ByteStringView& bstrc);
-  ByteString(const ByteStringView& bstrc1, const ByteStringView& bstrc2);
+  explicit ByteString(ByteStringView bstrc);
+  ByteString(ByteStringView str1, ByteStringView str2);
   ByteString(const std::initializer_list<ByteStringView>& list);
   explicit ByteString(const std::ostringstream& outStream);
 
@@ -64,8 +65,6 @@
 
   void clear() { m_pData.Reset(); }
 
-  static ByteString FromUnicode(const WideString& str) WARN_UNUSED_RESULT;
-
   // Explicit conversion to C-style string.
   // Note: Any subsequent modification of |this| will invalidate the result.
   const char* c_str() const { return m_pData ? m_pData->m_String : ""; }
@@ -83,6 +82,16 @@
     return ByteStringView(raw_str(), GetLength());
   }
 
+  // Explicit conversion to span.
+  // Note: Any subsequent modification of |this| will invalidate the result.
+  pdfium::span<const char> span() const {
+    return pdfium::make_span(m_pData ? m_pData->m_String : nullptr,
+                             GetLength());
+  }
+  pdfium::span<const uint8_t> raw_span() const {
+    return pdfium::make_span(raw_str(), GetLength());
+  }
+
   // Note: Any subsequent modification of |this| will invalidate iterators.
   const_iterator begin() const { return m_pData ? m_pData->m_String : nullptr; }
   const_iterator end() const {
@@ -105,37 +114,40 @@
   bool IsValidIndex(size_t index) const { return index < GetLength(); }
   bool IsValidLength(size_t length) const { return length <= GetLength(); }
 
-  int Compare(const ByteStringView& str) const;
-  bool EqualNoCase(const ByteStringView& str) const;
+  int Compare(ByteStringView str) const;
+  bool EqualNoCase(ByteStringView str) const;
 
   bool operator==(const char* ptr) const;
-  bool operator==(const ByteStringView& str) const;
+  bool operator==(ByteStringView str) const;
   bool operator==(const ByteString& other) const;
 
   bool operator!=(const char* ptr) const { return !(*this == ptr); }
-  bool operator!=(const ByteStringView& str) const { return !(*this == str); }
+  bool operator!=(ByteStringView str) const { return !(*this == str); }
   bool operator!=(const ByteString& other) const { return !(*this == other); }
 
   bool operator<(const char* ptr) const;
-  bool operator<(const ByteStringView& str) const;
+  bool operator<(ByteStringView str) const;
   bool operator<(const ByteString& other) const;
 
-  const ByteString& operator=(const char* str);
-  const ByteString& operator=(const ByteStringView& bstrc);
-  const ByteString& operator=(const ByteString& stringSrc);
+  ByteString& operator=(const char* str);
+  ByteString& operator=(ByteStringView str);
+  ByteString& operator=(const ByteString& that);
 
-  const ByteString& operator+=(char ch);
-  const ByteString& operator+=(const char* str);
-  const ByteString& operator+=(const ByteString& str);
-  const ByteString& operator+=(const ByteStringView& bstrc);
+  // Move-assign a ByteString. After assignment, |that| is empty.
+  ByteString& operator=(ByteString&& that);
+
+  ByteString& operator+=(char ch);
+  ByteString& operator+=(const char* str);
+  ByteString& operator+=(const ByteString& str);
+  ByteString& operator+=(ByteStringView str);
 
   CharType operator[](const size_t index) const {
-    ASSERT(IsValidIndex(index));
-    return m_pData ? m_pData->m_String[index] : 0;
+    CHECK(IsValidIndex(index));
+    return m_pData->m_String[index];
   }
 
-  CharType First() const { return GetLength() ? (*this)[0] : 0; }
-  CharType Last() const { return GetLength() ? (*this)[GetLength() - 1] : 0; }
+  CharType Front() const { return GetLength() ? (*this)[0] : 0; }
+  CharType Back() const { return GetLength() ? (*this)[GetLength() - 1] : 0; }
 
   void SetAt(size_t index, char c);
 
@@ -145,18 +157,21 @@
   size_t Delete(size_t index, size_t count = 1);
 
   void Reserve(size_t len);
-  char* GetBuffer(size_t len);
-  void ReleaseBuffer(size_t len);
 
-  ByteString Mid(size_t first, size_t count) const;
-  ByteString Left(size_t count) const;
-  ByteString Right(size_t count) const;
+  // Note: any modification of the string (including ReleaseBuffer()) may
+  // invalidate the span, which must not outlive its buffer.
+  pdfium::span<char> GetBuffer(size_t nMinBufLength);
+  void ReleaseBuffer(size_t nNewLength);
 
-  Optional<size_t> Find(const ByteStringView& lpszSub, size_t start = 0) const;
+  ByteString Substr(size_t first, size_t count) const;
+  ByteString First(size_t count) const;
+  ByteString Last(size_t count) const;
+
+  Optional<size_t> Find(ByteStringView subStr, size_t start = 0) const;
   Optional<size_t> Find(char ch, size_t start = 0) const;
   Optional<size_t> ReverseFind(char ch) const;
 
-  bool Contains(const ByteStringView& lpszSub, size_t start = 0) const {
+  bool Contains(ByteStringView lpszSub, size_t start = 0) const {
     return Find(lpszSub, start).has_value();
   }
 
@@ -169,22 +184,19 @@
 
   void Trim();
   void Trim(char target);
-  void Trim(const ByteStringView& targets);
+  void Trim(ByteStringView targets);
 
   void TrimLeft();
   void TrimLeft(char target);
-  void TrimLeft(const ByteStringView& targets);
+  void TrimLeft(ByteStringView targets);
 
   void TrimRight();
   void TrimRight(char target);
-  void TrimRight(const ByteStringView& targets);
+  void TrimRight(ByteStringView targets);
 
-  size_t Replace(const ByteStringView& lpszOld, const ByteStringView& lpszNew);
-
+  size_t Replace(ByteStringView pOld, ByteStringView pNew);
   size_t Remove(char ch);
 
-  WideString UTF8Decode() const;
-
   uint32_t GetID() const { return AsStringView().GetID(); }
 
  protected:
@@ -194,44 +206,46 @@
   void AllocBeforeWrite(size_t nNewLen);
   void AllocCopy(ByteString& dest, size_t nCopyLen, size_t nCopyIndex) const;
   void AssignCopy(const char* pSrcData, size_t nSrcLen);
-  void Concat(const char* lpszSrcData, size_t nSrcLen);
+  void Concat(const char* pSrcData, size_t nSrcLen);
+  intptr_t ReferenceCountForTesting() const;
 
   RetainPtr<StringData> m_pData;
 
-  friend ByteString_Concat_Test;
+  friend class ByteString_Assign_Test;
+  friend class ByteString_Concat_Test;
+  friend class ByteString_Construct_Test;
   friend class StringPool_ByteString_Test;
 };
 
 inline bool operator==(const char* lhs, const ByteString& rhs) {
   return rhs == lhs;
 }
-inline bool operator==(const ByteStringView& lhs, const ByteString& rhs) {
+inline bool operator==(ByteStringView lhs, const ByteString& rhs) {
   return rhs == lhs;
 }
 inline bool operator!=(const char* lhs, const ByteString& rhs) {
   return rhs != lhs;
 }
-inline bool operator!=(const ByteStringView& lhs, const ByteString& rhs) {
+inline bool operator!=(ByteStringView lhs, const ByteString& rhs) {
   return rhs != lhs;
 }
 inline bool operator<(const char* lhs, const ByteString& rhs) {
   return rhs.Compare(lhs) > 0;
 }
 
-inline ByteString operator+(const ByteStringView& str1,
-                            const ByteStringView& str2) {
+inline ByteString operator+(ByteStringView str1, ByteStringView str2) {
   return ByteString(str1, str2);
 }
-inline ByteString operator+(const ByteStringView& str1, const char* str2) {
+inline ByteString operator+(ByteStringView str1, const char* str2) {
   return ByteString(str1, str2);
 }
-inline ByteString operator+(const char* str1, const ByteStringView& str2) {
+inline ByteString operator+(const char* str1, ByteStringView str2) {
   return ByteString(str1, str2);
 }
-inline ByteString operator+(const ByteStringView& str1, char ch) {
+inline ByteString operator+(ByteStringView str1, char ch) {
   return ByteString(str1, ByteStringView(ch));
 }
-inline ByteString operator+(char ch, const ByteStringView& str2) {
+inline ByteString operator+(char ch, ByteStringView str2) {
   return ByteString(ch, str2);
 }
 inline ByteString operator+(const ByteString& str1, const ByteString& str2) {
@@ -249,23 +263,22 @@
 inline ByteString operator+(const char* str1, const ByteString& str2) {
   return ByteString(str1, str2.AsStringView());
 }
-inline ByteString operator+(const ByteString& str1,
-                            const ByteStringView& str2) {
+inline ByteString operator+(const ByteString& str1, ByteStringView str2) {
   return ByteString(str1.AsStringView(), str2);
 }
-inline ByteString operator+(const ByteStringView& str1,
-                            const ByteString& str2) {
+inline ByteString operator+(ByteStringView str1, const ByteString& str2) {
   return ByteString(str1, str2.AsStringView());
 }
 
 std::ostream& operator<<(std::ostream& os, const ByteString& str);
-std::ostream& operator<<(std::ostream& os, const ByteStringView& str);
+std::ostream& operator<<(std::ostream& os, ByteStringView str);
 
 }  // namespace fxcrt
 
 using ByteString = fxcrt::ByteString;
 
-uint32_t FX_HashCode_GetA(const ByteStringView& str, bool bIgnoreCase);
+uint32_t FX_HashCode_GetA(ByteStringView str, bool bIgnoreCase);
+uint32_t FX_HashCode_GetAsIfW(ByteStringView str, bool bIgnoreCase);
 
 namespace std {
 
diff --git a/core/fxcrt/bytestring_unittest.cpp b/core/fxcrt/bytestring_unittest.cpp
index ce1512b..e2e87f0 100644
--- a/core/fxcrt/bytestring_unittest.cpp
+++ b/core/fxcrt/bytestring_unittest.cpp
@@ -5,10 +5,12 @@
 #include "core/fxcrt/bytestring.h"
 
 #include <algorithm>
+#include <iterator>
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/span.h"
 #include "third_party/base/stl_util.h"
 
 namespace fxcrt {
@@ -22,6 +24,14 @@
   EXPECT_DEATH({ abc[3]; }, ".*");
 #endif
 
+  pdfium::span<const char> abc_span = abc.span();
+  EXPECT_EQ(3u, abc_span.size());
+  EXPECT_EQ(0, memcmp(abc_span.data(), "abc", 3));
+
+  pdfium::span<const uint8_t> abc_raw_span = abc.raw_span();
+  EXPECT_EQ(3u, abc_raw_span.size());
+  EXPECT_EQ(0, memcmp(abc_raw_span.data(), "abc", 3));
+
   ByteString mutable_abc = abc;
   EXPECT_EQ(abc.c_str(), mutable_abc.c_str());
   EXPECT_EQ('a', mutable_abc[0]);
@@ -50,6 +60,60 @@
 #endif
 }
 
+TEST(ByteString, Construct) {
+  {
+    // Copy-construct.
+    ByteString string1("abc");
+    ByteString string2(string1);
+    EXPECT_EQ("abc", string1);
+    EXPECT_EQ("abc", string2);
+    EXPECT_EQ(2, string1.ReferenceCountForTesting());
+    EXPECT_EQ(2, string2.ReferenceCountForTesting());
+  }
+  {
+    // Move-construct.
+    ByteString string1("abc");
+    ByteString string2(std::move(string1));
+    EXPECT_TRUE(string1.IsEmpty());
+    EXPECT_EQ("abc", string2);
+    EXPECT_EQ(0, string1.ReferenceCountForTesting());
+    EXPECT_EQ(1, string2.ReferenceCountForTesting());
+  }
+}
+
+TEST(ByteString, Assign) {
+  {
+    // Copy-assign.
+    ByteString string1;
+    EXPECT_EQ(0, string1.ReferenceCountForTesting());
+    {
+      ByteString string2("abc");
+      EXPECT_EQ(1, string2.ReferenceCountForTesting());
+
+      string1 = string2;
+      EXPECT_EQ(2, string1.ReferenceCountForTesting());
+      EXPECT_EQ(2, string2.ReferenceCountForTesting());
+    }
+    EXPECT_EQ(1, string1.ReferenceCountForTesting());
+  }
+  {
+    // Move-assign.
+    ByteString string1;
+    EXPECT_EQ(0, string1.ReferenceCountForTesting());
+    {
+      ByteString string2("abc");
+      EXPECT_EQ(1, string2.ReferenceCountForTesting());
+
+      string1 = std::move(string2);
+      EXPECT_EQ("abc", string1);
+      EXPECT_TRUE(string2.IsEmpty());
+      EXPECT_EQ(1, string1.ReferenceCountForTesting());
+      EXPECT_EQ(0, string2.ReferenceCountForTesting());
+    }
+    EXPECT_EQ(1, string1.ReferenceCountForTesting());
+  }
+}
+
 TEST(ByteString, OperatorLT) {
   ByteString empty;
   ByteString a("a");
@@ -349,43 +413,29 @@
   EXPECT_TRUE(c_string3 != byte_string);
 }
 
-TEST(ByteStringView, Null) {
-  ByteStringView null_string;
-  EXPECT_FALSE(null_string.raw_str());
-  EXPECT_EQ(0u, null_string.GetLength());
-  EXPECT_TRUE(null_string.IsEmpty());
+TEST(ByteString, OperatorPlus) {
+  EXPECT_EQ("I like dogs", "I like " + ByteString("dogs"));
+  EXPECT_EQ("Dogs like me", ByteString("Dogs") + " like me");
+  EXPECT_EQ("Oh no, error number 42",
+            "Oh no, error number " + ByteString::Format("%d", 42));
 
-  ByteStringView another_null_string;
-  EXPECT_EQ(null_string, another_null_string);
-
-  ByteStringView copied_null_string(null_string);
-  EXPECT_FALSE(copied_null_string.raw_str());
-  EXPECT_EQ(0u, copied_null_string.GetLength());
-  EXPECT_TRUE(copied_null_string.IsEmpty());
-  EXPECT_EQ(null_string, copied_null_string);
-
-  ByteStringView empty_string("");  // Pointer to NUL, not NULL pointer.
-  EXPECT_TRUE(empty_string.raw_str());
-  EXPECT_EQ(0u, empty_string.GetLength());
-  EXPECT_TRUE(empty_string.IsEmpty());
-  EXPECT_EQ(null_string, empty_string);
-
-  ByteStringView assigned_null_string("initially not nullptr");
-  assigned_null_string = null_string;
-  EXPECT_FALSE(assigned_null_string.raw_str());
-  EXPECT_EQ(0u, assigned_null_string.GetLength());
-  EXPECT_TRUE(assigned_null_string.IsEmpty());
-  EXPECT_EQ(null_string, assigned_null_string);
-
-  ByteStringView assigned_nullptr_string("initially not nullptr");
-  assigned_nullptr_string = nullptr;
-  EXPECT_FALSE(assigned_nullptr_string.raw_str());
-  EXPECT_EQ(0u, assigned_nullptr_string.GetLength());
-  EXPECT_TRUE(assigned_nullptr_string.IsEmpty());
-  EXPECT_EQ(null_string, assigned_nullptr_string);
-
-  ByteStringView non_null_string("a");
-  EXPECT_NE(null_string, non_null_string);
+  {
+    // Make sure operator+= and Concat() increases string memory allocation
+    // geometrically.
+    int allocations = 0;
+    ByteString str("ABCDEFGHIJKLMN");
+    const char* buffer = str.c_str();
+    for (size_t i = 0; i < 10000; ++i) {
+      str += "!";
+      const char* new_buffer = str.c_str();
+      if (new_buffer != buffer) {
+        buffer = new_buffer;
+        ++allocations;
+      }
+    }
+    EXPECT_LT(allocations, 25);
+    EXPECT_GT(allocations, 10);
+  }
 }
 
 TEST(ByteString, Concat) {
@@ -553,57 +603,57 @@
   EXPECT_EQ("", empty);
 }
 
-TEST(ByteString, Mid) {
+TEST(ByteString, Substr) {
   ByteString fred("FRED");
-  EXPECT_EQ("", fred.Mid(0, 0));
-  EXPECT_EQ("", fred.Mid(3, 0));
-  EXPECT_EQ("FRED", fred.Mid(0, 4));
-  EXPECT_EQ("RED", fred.Mid(1, 3));
-  EXPECT_EQ("ED", fred.Mid(2, 2));
-  EXPECT_EQ("D", fred.Mid(3, 1));
-  EXPECT_EQ("F", fred.Mid(0, 1));
-  EXPECT_EQ("R", fred.Mid(1, 1));
-  EXPECT_EQ("E", fred.Mid(2, 1));
-  EXPECT_EQ("D", fred.Mid(3, 1));
-  EXPECT_EQ("FR", fred.Mid(0, 2));
-  EXPECT_EQ("FRED", fred.Mid(0, 4));
-  EXPECT_EQ("", fred.Mid(0, 10));
+  EXPECT_EQ("", fred.Substr(0, 0));
+  EXPECT_EQ("", fred.Substr(3, 0));
+  EXPECT_EQ("FRED", fred.Substr(0, 4));
+  EXPECT_EQ("RED", fred.Substr(1, 3));
+  EXPECT_EQ("ED", fred.Substr(2, 2));
+  EXPECT_EQ("D", fred.Substr(3, 1));
+  EXPECT_EQ("F", fred.Substr(0, 1));
+  EXPECT_EQ("R", fred.Substr(1, 1));
+  EXPECT_EQ("E", fred.Substr(2, 1));
+  EXPECT_EQ("D", fred.Substr(3, 1));
+  EXPECT_EQ("FR", fred.Substr(0, 2));
+  EXPECT_EQ("FRED", fred.Substr(0, 4));
+  EXPECT_EQ("", fred.Substr(0, 10));
 
-  EXPECT_EQ("RED", fred.Mid(1, 3));
-  EXPECT_EQ("", fred.Mid(4, 1));
+  EXPECT_EQ("RED", fred.Substr(1, 3));
+  EXPECT_EQ("", fred.Substr(4, 1));
 
   ByteString empty;
-  EXPECT_EQ("", empty.Mid(0, 0));
+  EXPECT_EQ("", empty.Substr(0, 0));
 }
 
-TEST(ByteString, Left) {
+TEST(ByteString, First) {
   ByteString fred("FRED");
-  EXPECT_EQ("", fred.Left(0));
-  EXPECT_EQ("F", fred.Left(1));
-  EXPECT_EQ("FR", fred.Left(2));
-  EXPECT_EQ("FRE", fred.Left(3));
-  EXPECT_EQ("FRED", fred.Left(4));
+  EXPECT_EQ("", fred.First(0));
+  EXPECT_EQ("F", fred.First(1));
+  EXPECT_EQ("FR", fred.First(2));
+  EXPECT_EQ("FRE", fred.First(3));
+  EXPECT_EQ("FRED", fred.First(4));
 
-  EXPECT_EQ("", fred.Left(5));
+  EXPECT_EQ("", fred.First(5));
 
   ByteString empty;
-  EXPECT_EQ("", empty.Left(0));
-  EXPECT_EQ("", empty.Left(1));
+  EXPECT_EQ("", empty.First(0));
+  EXPECT_EQ("", empty.First(1));
 }
 
-TEST(ByteString, Right) {
+TEST(ByteString, Last) {
   ByteString fred("FRED");
-  EXPECT_EQ("", fred.Right(0));
-  EXPECT_EQ("D", fred.Right(1));
-  EXPECT_EQ("ED", fred.Right(2));
-  EXPECT_EQ("RED", fred.Right(3));
-  EXPECT_EQ("FRED", fred.Right(4));
+  EXPECT_EQ("", fred.Last(0));
+  EXPECT_EQ("D", fred.Last(1));
+  EXPECT_EQ("ED", fred.Last(2));
+  EXPECT_EQ("RED", fred.Last(3));
+  EXPECT_EQ("FRED", fred.Last(4));
 
-  EXPECT_EQ("", fred.Right(5));
+  EXPECT_EQ("", fred.Last(5));
 
   ByteString empty;
-  EXPECT_EQ("", empty.Right(0));
-  EXPECT_EQ("", empty.Right(1));
+  EXPECT_EQ("", empty.Last(0));
+  EXPECT_EQ("", empty.Last(1));
 }
 
 TEST(ByteString, Find) {
@@ -877,22 +927,23 @@
 }
 
 TEST(ByteString, GetBuffer) {
+  ByteString str1;
   {
-    ByteString str;
-    char* buffer = str.GetBuffer(12);
+    pdfium::span<char> buffer = str1.GetBuffer(12);
     // NOLINTNEXTLINE(runtime/printf)
-    strcpy(buffer, "clams");
-    str.ReleaseBuffer(str.GetStringLength());
-    EXPECT_EQ("clams", str);
+    strcpy(buffer.data(), "clams");
   }
+  str1.ReleaseBuffer(str1.GetStringLength());
+  EXPECT_EQ("clams", str1);
+
+  ByteString str2("cl");
   {
-    ByteString str("cl");
-    char* buffer = str.GetBuffer(12);
+    pdfium::span<char> buffer = str2.GetBuffer(12);
     // NOLINTNEXTLINE(runtime/printf)
-    strcpy(buffer + 2, "ams");
-    str.ReleaseBuffer(str.GetStringLength());
-    EXPECT_EQ("clams", str);
+    strcpy(&buffer[2], "ams");
   }
+  str2.ReleaseBuffer(str2.GetStringLength());
+  EXPECT_EQ("clams", str2);
 }
 
 TEST(ByteString, ReleaseBuffer) {
@@ -959,58 +1010,130 @@
 TEST(ByteString, MultiCharReverseIterator) {
   ByteString multi_str("abcd");
   auto iter = multi_str.rbegin();
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(4, multi_str.rend() - iter);
+  EXPECT_EQ(0, iter - multi_str.rbegin());
 
   char ch = *iter++;
   EXPECT_EQ('d', ch);
   EXPECT_EQ('c', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(3, multi_str.rend() - iter);
+  EXPECT_EQ(1, iter - multi_str.rbegin());
 
   ch = *(++iter);
   EXPECT_EQ('b', ch);
   EXPECT_EQ('b', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(2, multi_str.rend() - iter);
+  EXPECT_EQ(2, iter - multi_str.rbegin());
 
   ch = *iter++;
   EXPECT_EQ('b', ch);
   EXPECT_EQ('a', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(1, multi_str.rend() - iter);
+  EXPECT_EQ(3, iter - multi_str.rbegin());
 
   ch = *iter++;
   EXPECT_EQ('a', ch);
-  EXPECT_TRUE(iter == multi_str.rend());
+  EXPECT_EQ(iter, multi_str.rend());
+  EXPECT_EQ(0, multi_str.rend() - iter);
+  EXPECT_EQ(4, iter - multi_str.rbegin());
 
   ch = *(--iter);
   EXPECT_EQ('a', ch);
   EXPECT_EQ('a', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(1, multi_str.rend() - iter);
+  EXPECT_EQ(3, iter - multi_str.rbegin());
 
   ch = *iter--;
   EXPECT_EQ('a', ch);
   EXPECT_EQ('b', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(2, multi_str.rend() - iter);
+  EXPECT_EQ(2, iter - multi_str.rbegin());
 
   ch = *iter--;
   EXPECT_EQ('b', ch);
   EXPECT_EQ('c', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(3, multi_str.rend() - iter);
+  EXPECT_EQ(1, iter - multi_str.rbegin());
 
   ch = *(--iter);
   EXPECT_EQ('d', ch);
   EXPECT_EQ('d', *iter);
-  EXPECT_TRUE(iter == multi_str.rbegin());
+  EXPECT_EQ(iter, multi_str.rbegin());
+  EXPECT_EQ(4, multi_str.rend() - iter);
+  EXPECT_EQ(0, iter - multi_str.rbegin());
+}
+
+TEST(ByteStringView, Null) {
+  ByteStringView null_string;
+  EXPECT_FALSE(null_string.raw_str());
+  EXPECT_EQ(0u, null_string.GetLength());
+  EXPECT_TRUE(null_string.IsEmpty());
+
+  ByteStringView another_null_string;
+  EXPECT_EQ(null_string, another_null_string);
+
+  ByteStringView copied_null_string(null_string);
+  EXPECT_FALSE(copied_null_string.raw_str());
+  EXPECT_EQ(0u, copied_null_string.GetLength());
+  EXPECT_TRUE(copied_null_string.IsEmpty());
+  EXPECT_EQ(null_string, copied_null_string);
+
+  ByteStringView span_null_string = pdfium::span<const uint8_t>();
+  EXPECT_FALSE(span_null_string.raw_str());
+  EXPECT_EQ(0u, span_null_string.GetLength());
+  EXPECT_TRUE(span_null_string.IsEmpty());
+  EXPECT_EQ(null_string, span_null_string);
+
+  ByteStringView empty_string("");  // Pointer to NUL, not NULL pointer.
+  EXPECT_TRUE(empty_string.raw_str());
+  EXPECT_EQ(0u, empty_string.GetLength());
+  EXPECT_TRUE(empty_string.IsEmpty());
+  EXPECT_EQ(null_string, empty_string);
+
+  ByteStringView assigned_null_string("initially not nullptr");
+  assigned_null_string = null_string;
+  EXPECT_FALSE(assigned_null_string.raw_str());
+  EXPECT_EQ(0u, assigned_null_string.GetLength());
+  EXPECT_TRUE(assigned_null_string.IsEmpty());
+  EXPECT_EQ(null_string, assigned_null_string);
+
+  ByteStringView assigned_nullptr_string("initially not nullptr");
+  assigned_nullptr_string = nullptr;
+  EXPECT_FALSE(assigned_nullptr_string.raw_str());
+  EXPECT_EQ(0u, assigned_nullptr_string.GetLength());
+  EXPECT_TRUE(assigned_nullptr_string.IsEmpty());
+  EXPECT_EQ(null_string, assigned_nullptr_string);
+
+  ByteStringView assigned_span_null_string("initially not null span");
+  assigned_span_null_string = pdfium::span<const uint8_t>();
+  EXPECT_FALSE(assigned_span_null_string.raw_str());
+  EXPECT_EQ(0u, assigned_span_null_string.GetLength());
+  EXPECT_TRUE(assigned_span_null_string.IsEmpty());
+  EXPECT_EQ(null_string, assigned_span_null_string);
+
+  ByteStringView non_null_string("a");
+  EXPECT_NE(null_string, non_null_string);
 }
 
 TEST(ByteStringView, NotNull) {
   ByteStringView string3("abc");
   ByteStringView string6("abcdef");
   ByteStringView alternate_string3("abcdef", 3);
+  ByteStringView span_string4(pdfium::as_bytes(pdfium::make_span("abcd", 4)));
   ByteStringView embedded_nul_string7("abc\0def", 7);
   ByteStringView illegal_string7("abcdef", 7);
 
   EXPECT_EQ(3u, string3.GetLength());
   EXPECT_EQ(6u, string6.GetLength());
   EXPECT_EQ(3u, alternate_string3.GetLength());
+  EXPECT_EQ(4u, span_string4.GetLength());
   EXPECT_EQ(7u, embedded_nul_string7.GetLength());
   EXPECT_EQ(7u, illegal_string7.GetLength());
 
@@ -1137,34 +1260,34 @@
   EXPECT_EQ(2u, result.value());
 }
 
-TEST(ByteStringView, Mid) {
+TEST(ByteStringView, Substr) {
   ByteStringView null_string;
-  EXPECT_EQ(null_string, null_string.Mid(0, 1));
-  EXPECT_EQ(null_string, null_string.Mid(1, 1));
+  EXPECT_EQ(null_string, null_string.Substr(0, 1));
+  EXPECT_EQ(null_string, null_string.Substr(1, 1));
 
   ByteStringView empty_string("");
-  EXPECT_EQ("", empty_string.Mid(0, 1));
-  EXPECT_EQ("", empty_string.Mid(1, 1));
+  EXPECT_EQ("", empty_string.Substr(0, 1));
+  EXPECT_EQ("", empty_string.Substr(1, 1));
 
   ByteStringView single_character("a");
-  EXPECT_EQ("", single_character.Mid(0, 0));
-  EXPECT_EQ(single_character, single_character.Mid(0, 1));
-  EXPECT_EQ("", single_character.Mid(1, 0));
-  EXPECT_EQ("", single_character.Mid(1, 1));
+  EXPECT_EQ("", single_character.Substr(0, 0));
+  EXPECT_EQ(single_character, single_character.Substr(0, 1));
+  EXPECT_EQ("", single_character.Substr(1, 0));
+  EXPECT_EQ("", single_character.Substr(1, 1));
 
   ByteStringView longer_string("abcdef");
-  EXPECT_EQ(longer_string, longer_string.Mid(0, 6));
-  EXPECT_EQ("", longer_string.Mid(0, 187));
+  EXPECT_EQ(longer_string, longer_string.Substr(0, 6));
+  EXPECT_EQ("", longer_string.Substr(0, 187));
 
   ByteStringView leading_substring("ab");
-  EXPECT_EQ(leading_substring, longer_string.Mid(0, 2));
+  EXPECT_EQ(leading_substring, longer_string.Substr(0, 2));
 
   ByteStringView middle_substring("bcde");
-  EXPECT_EQ(middle_substring, longer_string.Mid(1, 4));
+  EXPECT_EQ(middle_substring, longer_string.Substr(1, 4));
 
   ByteStringView trailing_substring("ef");
-  EXPECT_EQ(trailing_substring, longer_string.Mid(4, 2));
-  EXPECT_EQ("", longer_string.Mid(4, 3));
+  EXPECT_EQ(trailing_substring, longer_string.Substr(4, 2));
+  EXPECT_EQ("", longer_string.Substr(4, 3));
 }
 
 TEST(ByteStringView, TrimmedRight) {
@@ -1296,6 +1419,10 @@
   EXPECT_FALSE(c_string1 == byte_string_c);
   EXPECT_FALSE(c_string2 == byte_string_c);
   EXPECT_FALSE(c_string3 == byte_string_c);
+
+  pdfium::span<const uint8_t> span5(
+      pdfium::as_bytes(pdfium::make_span("hello", 5)));
+  EXPECT_EQ(byte_string_c.raw_span(), span5);
 }
 
 TEST(ByteStringView, OperatorNE) {
@@ -1501,8 +1628,21 @@
   ByteString empty_str;
   EXPECT_TRUE(empty_str.IsEmpty());
   EXPECT_EQ(0u, empty_str.GetLength());
+
   const char* cstr = empty_str.c_str();
+  EXPECT_NE(nullptr, cstr);
   EXPECT_EQ(0u, strlen(cstr));
+
+  const uint8_t* rstr = empty_str.raw_str();
+  EXPECT_EQ(nullptr, rstr);
+
+  pdfium::span<const char> cspan = empty_str.span();
+  EXPECT_TRUE(cspan.empty());
+  EXPECT_EQ(nullptr, cspan.data());
+
+  pdfium::span<const uint8_t> rspan = empty_str.raw_span();
+  EXPECT_TRUE(rspan.empty());
+  EXPECT_EQ(nullptr, rspan.data());
 }
 
 TEST(ByteString, InitializerList) {
@@ -1560,6 +1700,15 @@
   EXPECT_EQ('a' + 'b' + 'c', sum);
 }
 
+TEST(ByteString, StdBegin) {
+  ByteString one_str("abc");
+  std::vector<uint8_t> vec(std::begin(one_str), std::end(one_str));
+  ASSERT_EQ(3u, vec.size());
+  EXPECT_EQ('a', vec[0]);
+  EXPECT_EQ('b', vec[1]);
+  EXPECT_EQ('c', vec[2]);
+}
+
 TEST(ByteString, AnyAllNoneOf) {
   ByteString str("aaaaaaaaaaaaaaaaab");
   EXPECT_FALSE(std::all_of(str.begin(), str.end(),
@@ -1724,4 +1873,22 @@
   EXPECT_EQ("-2147483648", ByteString::FormatInteger(INT_MIN));
 }
 
+TEST(ByteString, FX_HashCode_Ascii) {
+  EXPECT_EQ(0u, FX_HashCode_GetA("", false));
+  EXPECT_EQ(65u, FX_HashCode_GetA("A", false));
+  EXPECT_EQ(97u, FX_HashCode_GetA("A", true));
+  EXPECT_EQ(31 * 65u + 66u, FX_HashCode_GetA("AB", false));
+  EXPECT_EQ(31u * 65u + 255u, FX_HashCode_GetA("A\xff", false));
+  EXPECT_EQ(31u * 97u + 255u, FX_HashCode_GetA("A\xff", true));
+}
+
+TEST(ByteString, FX_HashCode_Wide) {
+  EXPECT_EQ(0u, FX_HashCode_GetAsIfW("", false));
+  EXPECT_EQ(65u, FX_HashCode_GetAsIfW("A", false));
+  EXPECT_EQ(97u, FX_HashCode_GetAsIfW("A", true));
+  EXPECT_EQ(1313u * 65u + 66u, FX_HashCode_GetAsIfW("AB", false));
+  EXPECT_EQ(1313u * 65u + 255u, FX_HashCode_GetAsIfW("A\xff", false));
+  EXPECT_EQ(1313u * 97u + 255u, FX_HashCode_GetAsIfW("A\xff", true));
+}
+
 }  // namespace fxcrt
diff --git a/core/fxcrt/cfx_binarybuf.cpp b/core/fxcrt/cfx_binarybuf.cpp
index 1243593..e24590c 100644
--- a/core/fxcrt/cfx_binarybuf.cpp
+++ b/core/fxcrt/cfx_binarybuf.cpp
@@ -9,10 +9,11 @@
 #include <algorithm>
 #include <utility>
 
-CFX_BinaryBuf::CFX_BinaryBuf()
-    : m_AllocStep(0), m_AllocSize(0), m_DataSize(0) {}
+#include "core/fxcrt/fx_safe_types.h"
 
-CFX_BinaryBuf::~CFX_BinaryBuf() {}
+CFX_BinaryBuf::CFX_BinaryBuf() = default;
+
+CFX_BinaryBuf::~CFX_BinaryBuf() = default;
 
 void CFX_BinaryBuf::Delete(size_t start_index, size_t count) {
   if (!m_pBuffer || count > m_DataSize || start_index > m_DataSize - count)
@@ -23,6 +24,14 @@
   m_DataSize -= count;
 }
 
+pdfium::span<uint8_t> CFX_BinaryBuf::GetSpan() {
+  return {GetBuffer(), GetSize()};
+}
+
+pdfium::span<const uint8_t> CFX_BinaryBuf::GetSpan() const {
+  return {GetBuffer(), GetSize()};
+}
+
 size_t CFX_BinaryBuf::GetLength() const {
   return m_DataSize;
 }
@@ -37,8 +46,7 @@
   return std::move(m_pBuffer);
 }
 
-void CFX_BinaryBuf::EstimateSize(size_t size, size_t step) {
-  m_AllocStep = step;
+void CFX_BinaryBuf::EstimateSize(size_t size) {
   if (m_AllocSize < size)
     ExpandBuf(size - m_DataSize);
 }
@@ -60,6 +68,10 @@
                       : FX_Alloc(uint8_t, m_AllocSize));
 }
 
+void CFX_BinaryBuf::AppendSpan(pdfium::span<const uint8_t> span) {
+  return AppendBlock(span.data(), span.size());
+}
+
 void CFX_BinaryBuf::AppendBlock(const void* pBuf, size_t size) {
   if (size == 0)
     return;
diff --git a/core/fxcrt/cfx_binarybuf.h b/core/fxcrt/cfx_binarybuf.h
index 2d4f019..b757c9a 100644
--- a/core/fxcrt/cfx_binarybuf.h
+++ b/core/fxcrt/cfx_binarybuf.h
@@ -9,22 +9,27 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
 
 class CFX_BinaryBuf {
  public:
   CFX_BinaryBuf();
   virtual ~CFX_BinaryBuf();
 
+  pdfium::span<uint8_t> GetSpan();
+  pdfium::span<const uint8_t> GetSpan() const;
   uint8_t* GetBuffer() const { return m_pBuffer.get(); }
   size_t GetSize() const { return m_DataSize; }
   virtual size_t GetLength() const;
   bool IsEmpty() const { return GetLength() == 0; }
 
   void Clear();
-  void EstimateSize(size_t size, size_t alloc_step = 0);
+  void SetAllocStep(size_t step) { m_AllocStep = step; }
+  void EstimateSize(size_t size);
+  void AppendSpan(pdfium::span<const uint8_t> span);
   void AppendBlock(const void* pBuf, size_t size);
   void AppendString(const ByteString& str) {
     AppendBlock(str.c_str(), str.GetLength());
@@ -43,9 +48,9 @@
  protected:
   void ExpandBuf(size_t size);
 
-  size_t m_AllocStep;
-  size_t m_AllocSize;
-  size_t m_DataSize;
+  size_t m_AllocStep = 0;
+  size_t m_AllocSize = 0;
+  size_t m_DataSize = 0;
   std::unique_ptr<uint8_t, FxFreeDeleter> m_pBuffer;
 };
 
diff --git a/core/fxcrt/cfx_bitstream.cpp b/core/fxcrt/cfx_bitstream.cpp
index 7cf6d36..d16fc2f 100644
--- a/core/fxcrt/cfx_bitstream.cpp
+++ b/core/fxcrt/cfx_bitstream.cpp
@@ -8,43 +8,49 @@
 
 #include <limits>
 
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
 
-CFX_BitStream::CFX_BitStream(const uint8_t* pData, uint32_t dwSize)
-    : m_BitPos(0), m_BitSize(dwSize * 8), m_pData(pData) {
-  ASSERT(dwSize <= std::numeric_limits<uint32_t>::max() / 8);
+CFX_BitStream::CFX_BitStream(pdfium::span<const uint8_t> pData)
+    : m_BitPos(0), m_BitSize(pData.size() * 8), m_pData(pData.data()) {
+  ASSERT(pData.size() <= std::numeric_limits<uint32_t>::max() / 8);
 }
 
 CFX_BitStream::~CFX_BitStream() {}
 
 void CFX_BitStream::ByteAlign() {
-  m_BitPos = (m_BitPos + 7) & ~7;
+  m_BitPos = FxAlignToBoundary<8>(m_BitPos);
 }
 
 uint32_t CFX_BitStream::GetBits(uint32_t nBits) {
-  if (nBits > m_BitSize || m_BitPos + nBits > m_BitSize)
+  ASSERT(nBits > 0);
+  ASSERT(nBits <= 32);
+  if (nBits > m_BitSize || m_BitPos > m_BitSize - nBits)
     return 0;
 
+  const uint32_t bit_pos = m_BitPos % 8;
+  uint32_t byte_pos = m_BitPos / 8;
   const uint8_t* data = m_pData.Get();
+  uint8_t current_byte = data[byte_pos];
 
   if (nBits == 1) {
-    int bit = (data[m_BitPos / 8] & (1 << (7 - m_BitPos % 8))) ? 1 : 0;
+    int bit = (current_byte & (1 << (7 - bit_pos))) ? 1 : 0;
     m_BitPos++;
     return bit;
   }
 
-  uint32_t byte_pos = m_BitPos / 8;
-  uint32_t bit_pos = m_BitPos % 8;
   uint32_t bit_left = nBits;
   uint32_t result = 0;
   if (bit_pos) {
-    if (8 - bit_pos >= bit_left) {
-      result = (data[byte_pos] & (0xff >> bit_pos)) >> (8 - bit_pos - bit_left);
+    uint32_t bits_readable = 8 - bit_pos;
+    if (bits_readable >= bit_left) {
+      result = (current_byte & (0xff >> bit_pos)) >> (bits_readable - bit_left);
       m_BitPos += bit_left;
       return result;
     }
-    bit_left -= 8 - bit_pos;
-    result = (data[byte_pos++] & ((1 << (8 - bit_pos)) - 1)) << bit_left;
+    bit_left -= bits_readable;
+    result = (current_byte & ((1 << bits_readable) - 1)) << bit_left;
+    ++byte_pos;
   }
   while (bit_left >= 8) {
     bit_left -= 8;
diff --git a/core/fxcrt/cfx_bitstream.h b/core/fxcrt/cfx_bitstream.h
index 168760d..baa2a9f 100644
--- a/core/fxcrt/cfx_bitstream.h
+++ b/core/fxcrt/cfx_bitstream.h
@@ -10,10 +10,11 @@
 #include <stdint.h>
 
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/span.h"
 
 class CFX_BitStream {
  public:
-  CFX_BitStream(const uint8_t* pData, uint32_t dwSize);
+  explicit CFX_BitStream(pdfium::span<const uint8_t> pData);
   ~CFX_BitStream();
 
   void ByteAlign();
diff --git a/core/fxcrt/cfx_bitstream_unittest.cpp b/core/fxcrt/cfx_bitstream_unittest.cpp
new file mode 100644
index 0000000..2ba3663
--- /dev/null
+++ b/core/fxcrt/cfx_bitstream_unittest.cpp
@@ -0,0 +1,133 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_bitstream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+uint32_t ReferenceGetBits32(const uint8_t* pData, int bitpos, int nbits) {
+  int result = 0;
+  for (int i = 0; i < nbits; i++) {
+    if (pData[(bitpos + i) / 8] & (1 << (7 - (bitpos + i) % 8)))
+      result |= 1 << (nbits - i - 1);
+  }
+  return result;
+}
+
+}  // namespace
+
+TEST(fxcrt, BitStream) {
+  static const uint8_t kData[] = {0x00, 0x11, 0x22, 0x33,
+                                  0x44, 0x55, 0x66, 0x77};
+  CFX_BitStream bitstream(kData);
+
+  // Initial state.
+  EXPECT_FALSE(bitstream.IsEOF());
+  EXPECT_EQ(0U, bitstream.GetPos());
+  EXPECT_EQ(64U, bitstream.BitsRemaining());
+
+  // Read, read, read!
+  EXPECT_EQ(0x00U, bitstream.GetBits(8));
+  EXPECT_EQ(8U, bitstream.GetPos());
+  EXPECT_EQ(56U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x00U, bitstream.GetBits(1));
+  EXPECT_EQ(9U, bitstream.GetPos());
+  EXPECT_EQ(55U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x00U, bitstream.GetBits(2));
+  EXPECT_EQ(11U, bitstream.GetPos());
+  EXPECT_EQ(53U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x04U, bitstream.GetBits(3));
+  EXPECT_EQ(14U, bitstream.GetPos());
+  EXPECT_EQ(50U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x04U, bitstream.GetBits(4));
+  EXPECT_EQ(18U, bitstream.GetPos());
+  EXPECT_EQ(46U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x44U, bitstream.GetBits(7));
+  EXPECT_EQ(25U, bitstream.GetPos());
+  EXPECT_EQ(39U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0xCDU, bitstream.GetBits(9));
+  EXPECT_EQ(34U, bitstream.GetPos());
+  EXPECT_EQ(30U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x08AAU, bitstream.GetBits(15));
+  EXPECT_EQ(49U, bitstream.GetPos());
+  EXPECT_EQ(15U, bitstream.BitsRemaining());
+
+  // Cannot advance past the end.
+  EXPECT_EQ(0x00U, bitstream.GetBits(16));
+  EXPECT_EQ(49U, bitstream.GetPos());
+  EXPECT_EQ(15U, bitstream.BitsRemaining());
+
+  // Make sure SkipBits() works.
+  bitstream.SkipBits(14);
+  EXPECT_EQ(63U, bitstream.GetPos());
+  EXPECT_EQ(1U, bitstream.BitsRemaining());
+  bitstream.SkipBits(2);
+  EXPECT_EQ(65U, bitstream.GetPos());
+  EXPECT_EQ(0U, bitstream.BitsRemaining());
+  EXPECT_TRUE(bitstream.IsEOF());
+
+  // Make sure Rewind() works.
+  bitstream.Rewind();
+  EXPECT_FALSE(bitstream.IsEOF());
+  EXPECT_EQ(0U, bitstream.GetPos());
+  EXPECT_EQ(64U, bitstream.BitsRemaining());
+
+  // Read some more.
+  bitstream.SkipBits(5);
+  EXPECT_EQ(5U, bitstream.GetPos());
+  EXPECT_EQ(59U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x0448U, bitstream.GetBits(17));
+  EXPECT_EQ(22U, bitstream.GetPos());
+  EXPECT_EQ(42U, bitstream.BitsRemaining());
+
+  // Make sure ByteAlign() works.
+  bitstream.ByteAlign();
+  EXPECT_EQ(24U, bitstream.GetPos());
+  EXPECT_EQ(40U, bitstream.BitsRemaining());
+
+  EXPECT_EQ(0x19A22AB3U, bitstream.GetBits(31));
+  EXPECT_EQ(55U, bitstream.GetPos());
+  EXPECT_EQ(9U, bitstream.BitsRemaining());
+
+  // Do some bigger reads.
+  bitstream.Rewind();
+  EXPECT_EQ(0x112233U, bitstream.GetBits(32));
+  EXPECT_EQ(32U, bitstream.GetPos());
+  EXPECT_EQ(32U, bitstream.BitsRemaining());
+
+  bitstream.Rewind();
+  bitstream.SkipBits(31);
+  EXPECT_EQ(0xA22AB33BU, bitstream.GetBits(32));
+  EXPECT_EQ(63U, bitstream.GetPos());
+  EXPECT_EQ(1U, bitstream.BitsRemaining());
+
+  // Skip past the end.
+  bitstream.SkipBits(1000);
+  EXPECT_TRUE(bitstream.IsEOF());
+  EXPECT_EQ(1063U, bitstream.GetPos());
+  EXPECT_EQ(0U, bitstream.BitsRemaining());
+}
+
+TEST(fxcrt, BitStreamSameAsReferenceGetBits32) {
+  static const unsigned char kData[] = {0xDE, 0x3F, 0xB1, 0x7C,
+                                        0x12, 0x9A, 0x04, 0x56};
+  CFX_BitStream bitstream(kData);
+  for (int nbits = 1; nbits <= 32; ++nbits) {
+    for (size_t bitpos = 0; bitpos < sizeof(kData) * 8 - nbits; ++bitpos) {
+      bitstream.Rewind();
+      bitstream.SkipBits(bitpos);
+      EXPECT_EQ(bitstream.GetBits(nbits),
+                ReferenceGetBits32(kData, bitpos, nbits));
+    }
+  }
+}
diff --git a/core/fxcrt/cfx_blockbuffer.cpp b/core/fxcrt/cfx_blockbuffer.cpp
deleted file mode 100644
index 13134f0..0000000
--- a/core/fxcrt/cfx_blockbuffer.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_blockbuffer.h"
-
-#include <algorithm>
-#include <utility>
-
-namespace {
-
-const size_t kAllocStep = 1024 * 1024;
-
-}  // namespace
-
-CFX_BlockBuffer::CFX_BlockBuffer()
-    : m_DataLength(0), m_BufferSize(0), m_StartPosition(0) {}
-
-CFX_BlockBuffer::~CFX_BlockBuffer() {}
-
-size_t CFX_BlockBuffer::GetAllocStep() const {
-  return kAllocStep;
-}
-
-std::pair<wchar_t*, size_t> CFX_BlockBuffer::GetAvailableBlock() {
-  if (m_BlockArray.empty())
-    return {nullptr, 0};
-
-  size_t realIndex = m_StartPosition + m_DataLength;
-  if (realIndex == m_BufferSize) {
-    m_BlockArray.emplace_back(FX_Alloc(wchar_t, kAllocStep));
-    m_BufferSize += kAllocStep;
-    return {m_BlockArray.back().get(), 0};
-  }
-  return {m_BlockArray[realIndex / kAllocStep].get(), realIndex % kAllocStep};
-}
-
-bool CFX_BlockBuffer::InitBuffer() {
-  m_BlockArray.clear();
-  m_BlockArray.emplace_back(FX_Alloc(wchar_t, kAllocStep));
-  m_BufferSize = kAllocStep;
-  return true;
-}
-
-void CFX_BlockBuffer::SetTextChar(size_t index, wchar_t ch) {
-  size_t realIndex = m_StartPosition + index;
-  size_t blockIndex = realIndex / kAllocStep;
-  if (blockIndex >= m_BlockArray.size()) {
-    size_t newBlocks = blockIndex - m_BlockArray.size() + 1;
-    do {
-      m_BlockArray.emplace_back(FX_Alloc(wchar_t, kAllocStep));
-      m_BufferSize += kAllocStep;
-    } while (--newBlocks);
-  }
-  wchar_t* pTextData = m_BlockArray[blockIndex].get();
-  pTextData[realIndex % kAllocStep] = ch;
-  m_DataLength = std::max(m_DataLength, index + 1);
-}
-
-void CFX_BlockBuffer::DeleteTextChars(size_t count) {
-  if (count == 0)
-    return;
-
-  if (count >= m_DataLength) {
-    Reset(false);
-    return;
-  }
-  m_DataLength -= count;
-}
-
-WideString CFX_BlockBuffer::GetTextData(size_t start, size_t length) const {
-  if (m_BufferSize <= m_StartPosition + 1 || length == 0)
-    return WideString();
-
-  size_t maybeDataLength = m_BufferSize - 1 - m_StartPosition;
-  if (start > maybeDataLength)
-    return WideString();
-  length = std::min(length, maybeDataLength);
-
-  WideString wsTextData;
-  wchar_t* pBuf = wsTextData.GetBuffer(length);
-  if (!pBuf)
-    return WideString();
-
-  size_t startBlock = 0;
-  size_t startInner = 0;
-  std::tie(startBlock, startInner) = TextDataIndex2BufIndex(start);
-
-  size_t endBlock = 0;
-  size_t endInner = 0;
-  std::tie(endBlock, endInner) = TextDataIndex2BufIndex(start + length);
-
-  size_t pointer = 0;
-  for (size_t i = startBlock; i <= endBlock; ++i) {
-    size_t bufferPointer = 0;
-    size_t copyLength = kAllocStep;
-    if (i == startBlock) {
-      copyLength -= startInner;
-      bufferPointer = startInner;
-    }
-    if (i == endBlock)
-      copyLength -= ((kAllocStep - 1) - endInner);
-
-    wchar_t* pBlockBuf = m_BlockArray[i].get();
-    memcpy(pBuf + pointer, pBlockBuf + bufferPointer,
-           copyLength * sizeof(wchar_t));
-    pointer += copyLength;
-  }
-  wsTextData.ReleaseBuffer(length);
-  return wsTextData;
-}
-
-std::pair<size_t, size_t> CFX_BlockBuffer::TextDataIndex2BufIndex(
-    const size_t iIndex) const {
-  ASSERT(iIndex >= 0);
-
-  size_t realIndex = m_StartPosition + iIndex;
-  return {realIndex / kAllocStep, realIndex % kAllocStep};
-}
diff --git a/core/fxcrt/cfx_blockbuffer.h b/core/fxcrt/cfx_blockbuffer.h
deleted file mode 100644
index 423aa68..0000000
--- a/core/fxcrt/cfx_blockbuffer.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_BLOCKBUFFER_H_
-#define CORE_FXCRT_CFX_BLOCKBUFFER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-
-class CFX_BlockBuffer {
- public:
-  CFX_BlockBuffer();
-  ~CFX_BlockBuffer();
-
-  bool InitBuffer();
-  bool IsInitialized() { return m_BufferSize / GetAllocStep() >= 1; }
-
-  std::pair<wchar_t*, size_t> GetAvailableBlock();
-  size_t GetAllocStep() const;
-  size_t GetDataLength() const { return m_DataLength; }
-  void IncrementDataLength() { m_DataLength++; }
-  bool IsEmpty() const { return m_DataLength == 0; }
-
-  void Reset(bool bReserveData) {
-    if (!bReserveData)
-      m_StartPosition = 0;
-    m_DataLength = 0;
-  }
-
-  void SetTextChar(size_t iIndex, wchar_t ch);
-  void DeleteTextChars(size_t iCount);
-  WideString GetTextData(size_t iStart, size_t iLength) const;
-
- private:
-  std::pair<size_t, size_t> TextDataIndex2BufIndex(const size_t iIndex) const;
-
-  std::vector<std::unique_ptr<wchar_t, FxFreeDeleter>> m_BlockArray;
-  size_t m_DataLength;
-  size_t m_BufferSize;
-  size_t m_StartPosition;
-};
-
-#endif  // CORE_FXCRT_CFX_BLOCKBUFFER_H_
diff --git a/core/fxcrt/cfx_char.cpp b/core/fxcrt/cfx_char.cpp
deleted file mode 100644
index bc04b0f..0000000
--- a/core/fxcrt/cfx_char.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_char.h"
-
-CFX_Char::CFX_Char(uint16_t wCharCode, uint32_t dwCharProps)
-    : CFX_Char(wCharCode, dwCharProps, 100, 100) {}
-
-CFX_Char::CFX_Char(uint16_t wCharCode,
-                   uint32_t dwCharProps,
-                   int32_t iHorizontalScale,
-                   int32_t iVerticalScale)
-    : m_dwStatus(CFX_BreakType::None),
-      m_nBreakType(0),
-      m_dwCharStyles(0),
-      m_iCharWidth(0),
-      m_iBidiClass(0),
-      m_iBidiLevel(0),
-      m_iBidiPos(0),
-      m_iBidiOrder(0),
-      m_iFontSize(0),
-      m_dwIdentity(0),
-      m_wCharCode(wCharCode),
-      m_dwCharProps(dwCharProps),
-      m_iHorizontalScale(iHorizontalScale),
-      m_iVerticalScale(iVerticalScale) {}
-
-CFX_Char::CFX_Char(const CFX_Char& other) = default;
-
-CFX_Char::~CFX_Char() = default;
-
-FX_CHARTYPE CFX_Char::GetCharType() const {
-  return GetCharTypeFromProp(m_dwCharProps);
-}
diff --git a/core/fxcrt/cfx_char.h b/core/fxcrt/cfx_char.h
deleted file mode 100644
index fb625ee..0000000
--- a/core/fxcrt/cfx_char.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_CHAR_H_
-#define CORE_FXCRT_CFX_CHAR_H_
-
-#include <stdint.h>
-
-#include "core/fxcrt/fx_unicode.h"
-
-enum class CFX_BreakType { None = 0, Piece, Line, Paragraph, Page };
-
-class CFX_Char {
- public:
-  CFX_Char(uint16_t wCharCode, uint32_t dwCharProps);
-  CFX_Char(uint16_t wCharCode,
-           uint32_t dwCharProps,
-           int32_t iHorizontalScale,
-           int32_t iVerticalScale);
-  CFX_Char(const CFX_Char& other);
-  ~CFX_Char();
-
-  FX_CHARTYPE GetCharType() const;
-
-  uint16_t char_code() const { return m_wCharCode; }
-  uint32_t char_props() const { return m_dwCharProps; }
-  int16_t horizonal_scale() const { return m_iHorizontalScale; }
-  int16_t vertical_scale() const { return m_iVerticalScale; }
-
-  CFX_BreakType m_dwStatus;
-  uint8_t m_nBreakType;
-  uint32_t m_dwCharStyles;
-  int32_t m_iCharWidth;
-  int16_t m_iBidiClass;
-  int16_t m_iBidiLevel;
-  int16_t m_iBidiPos;
-  int16_t m_iBidiOrder;
-  int32_t m_iFontSize;
-  uint32_t m_dwIdentity;
-  RetainPtr<Retainable> m_pUserData;
-
- private:
-  uint16_t m_wCharCode;
-  uint32_t m_dwCharProps;
-  int32_t m_iHorizontalScale;
-  int32_t m_iVerticalScale;
-};
-
-#endif  // CORE_FXCRT_CFX_CHAR_H_
diff --git a/core/fxcrt/cfx_checksumcontext.cpp b/core/fxcrt/cfx_checksumcontext.cpp
deleted file mode 100644
index 3d3409d..0000000
--- a/core/fxcrt/cfx_checksumcontext.cpp
+++ /dev/null
@@ -1,152 +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/fxcrt/cfx_checksumcontext.h"
-
-#include "core/fdrm/crypto/fx_crypt.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/xml/cfx_saxreaderhandler.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-struct FX_BASE64DATA {
-  uint32_t data1 : 2;
-  uint32_t data2 : 6;
-  uint32_t data3 : 4;
-  uint32_t data4 : 4;
-  uint32_t data5 : 6;
-  uint32_t data6 : 2;
-  uint32_t data7 : 8;
-};
-
-const char g_FXBase64EncoderMap[64] = {
-    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
-    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
-    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
-    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
-};
-
-void Base64EncodePiece(const FX_BASE64DATA& src, int32_t iBytes, char dst[4]) {
-  dst[0] = g_FXBase64EncoderMap[src.data2];
-  uint32_t b = src.data1 << 4;
-  if (iBytes > 1) {
-    b |= src.data4;
-  }
-  dst[1] = g_FXBase64EncoderMap[b];
-  if (iBytes > 1) {
-    b = src.data3 << 2;
-    if (iBytes > 2) {
-      b |= src.data6;
-    }
-    dst[2] = g_FXBase64EncoderMap[b];
-    if (iBytes > 2) {
-      dst[3] = g_FXBase64EncoderMap[src.data5];
-    } else {
-      dst[3] = '=';
-    }
-  } else {
-    dst[2] = dst[3] = '=';
-  }
-}
-
-int32_t Base64EncodeA(const uint8_t* pSrc, int32_t iSrcLen, char* pDst) {
-  ASSERT(pSrc);
-  if (iSrcLen < 1) {
-    return 0;
-  }
-  if (!pDst) {
-    int32_t iDstLen = iSrcLen / 3 * 4;
-    if ((iSrcLen % 3) != 0) {
-      iDstLen += 4;
-    }
-    return iDstLen;
-  }
-  FX_BASE64DATA srcData;
-  int32_t iBytes = 3;
-  char* pDstEnd = pDst;
-  while (iSrcLen > 0) {
-    if (iSrcLen > 2) {
-      ((uint8_t*)&srcData)[0] = *pSrc++;
-      ((uint8_t*)&srcData)[1] = *pSrc++;
-      ((uint8_t*)&srcData)[2] = *pSrc++;
-      iSrcLen -= 3;
-    } else {
-      *((uint32_t*)&srcData) = 0;
-      ((uint8_t*)&srcData)[0] = *pSrc++;
-      if (iSrcLen > 1) {
-        ((uint8_t*)&srcData)[1] = *pSrc++;
-      }
-      iBytes = iSrcLen;
-      iSrcLen = 0;
-    }
-    Base64EncodePiece(srcData, iBytes, pDstEnd);
-    pDstEnd += 4;
-  }
-  return pDstEnd - pDst;
-}
-
-}  // namespace
-
-CFX_ChecksumContext::CFX_ChecksumContext() {}
-
-CFX_ChecksumContext::~CFX_ChecksumContext() {}
-
-void CFX_ChecksumContext::StartChecksum() {
-  FinishChecksum();
-  m_pByteContext = pdfium::MakeUnique<CRYPT_sha1_context>();
-  CRYPT_SHA1Start(m_pByteContext.get());
-  m_bsChecksum.clear();
-  m_pSAXReader = pdfium::MakeUnique<CFX_SAXReader>();
-}
-
-bool CFX_ChecksumContext::UpdateChecksum(
-    const RetainPtr<IFX_SeekableReadStream>& pSrcFile,
-    FX_FILESIZE offset,
-    size_t size) {
-  if (!m_pSAXReader || !pSrcFile)
-    return false;
-
-  if (size < 1)
-    size = pSrcFile->GetSize();
-
-  CFX_SAXReaderHandler handler(this);
-  m_pSAXReader->SetHandler(&handler);
-  if (m_pSAXReader->StartParse(
-          pSrcFile, (uint32_t)offset, (uint32_t)size,
-          CFX_SaxParseMode_NotSkipSpace | CFX_SaxParseMode_NotConvert_amp |
-              CFX_SaxParseMode_NotConvert_lt | CFX_SaxParseMode_NotConvert_gt |
-              CFX_SaxParseMode_NotConvert_sharp) < 0) {
-    return false;
-  }
-  return m_pSAXReader->ContinueParse() > 99;
-}
-
-void CFX_ChecksumContext::FinishChecksum() {
-  m_pSAXReader.reset();
-  if (m_pByteContext) {
-    uint8_t digest[20];
-    memset(digest, 0, 20);
-    CRYPT_SHA1Finish(m_pByteContext.get(), digest);
-    int32_t nLen = Base64EncodeA(digest, 20, nullptr);
-    char* pBuffer = m_bsChecksum.GetBuffer(nLen);
-    Base64EncodeA(digest, 20, pBuffer);
-    m_bsChecksum.ReleaseBuffer(nLen);
-    m_pByteContext.reset();
-  }
-}
-
-ByteString CFX_ChecksumContext::GetChecksum() const {
-  return m_bsChecksum;
-}
-
-void CFX_ChecksumContext::Update(const ByteStringView& bsText) {
-  if (!m_pByteContext)
-    return;
-
-  CRYPT_SHA1Update(m_pByteContext.get(), bsText.raw_str(), bsText.GetLength());
-}
diff --git a/core/fxcrt/cfx_checksumcontext.h b/core/fxcrt/cfx_checksumcontext.h
deleted file mode 100644
index 6d7963a..0000000
--- a/core/fxcrt/cfx_checksumcontext.h
+++ /dev/null
@@ -1,34 +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_FXCRT_CFX_CHECKSUMCONTEXT_H_
-#define CORE_FXCRT_CFX_CHECKSUMCONTEXT_H_
-
-#include <memory>
-
-#include "core/fdrm/crypto/fx_crypt.h"
-#include "core/fxcrt/xml/cfx_saxreader.h"
-
-class CFX_ChecksumContext {
- public:
-  CFX_ChecksumContext();
-  ~CFX_ChecksumContext();
-
-  void StartChecksum();
-  void Update(const ByteStringView& bsText);
-  bool UpdateChecksum(const RetainPtr<IFX_SeekableReadStream>& pSrcFile,
-                      FX_FILESIZE offset = 0,
-                      size_t size = 0);
-  void FinishChecksum();
-  ByteString GetChecksum() const;
-
- private:
-  std::unique_ptr<CFX_SAXReader> m_pSAXReader;
-  std::unique_ptr<CRYPT_sha1_context> m_pByteContext;
-  ByteString m_bsChecksum;
-};
-
-#endif  // CORE_FXCRT_CFX_CHECKSUMCONTEXT_H_
diff --git a/core/fxcrt/cfx_datetime.cpp b/core/fxcrt/cfx_datetime.cpp
index 04fa8a5..56c6609 100644
--- a/core/fxcrt/cfx_datetime.cpp
+++ b/core/fxcrt/cfx_datetime.cpp
@@ -5,10 +5,12 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/fxcrt/cfx_datetime.h"
+
+#include "build/build_config.h"
 #include "core/fxcrt/fx_system.h"
 
-#if _FX_OS_ == _FX_OS_LINUX_ || _FX_OS_ == _FX_OS_ANDROID_ || \
-    _FX_OS_ == _FX_OS_MACOSX_
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) || \
+    defined(OS_ASMJS) || defined(__wasm__)
 #include <sys/time.h>
 #include <time.h>
 #endif
@@ -28,7 +30,8 @@
 
 int32_t DaysBeforeMonthInYear(int32_t iYear, uint8_t iMonth) {
   ASSERT(iYear != 0);
-  ASSERT(iMonth >= 1 && iMonth <= 12);
+  ASSERT(iMonth >= 1);
+  ASSERT(iMonth <= 12);
 
   const int32_t* p =
       FX_IsLeapYear(iYear) ? g_FXDaysBeforeLeapMonth : g_FXDaysBeforeMonth;
@@ -45,8 +48,10 @@
                    uint8_t iDay,
                    bool bIncludeThisDay) {
   ASSERT(iYear != 0);
-  ASSERT(iMonth >= 1 && iMonth <= 12);
-  ASSERT(iDay >= 1 && iDay <= FX_DaysInMonth(iYear, iMonth));
+  ASSERT(iMonth >= 1);
+  ASSERT(iMonth <= 12);
+  ASSERT(iDay >= 1);
+  ASSERT(iDay <= FX_DaysInMonth(iYear, iMonth));
 
   int64_t iDays = DaysBeforeMonthInYear(iYear, iMonth);
   iDays += iDay;
@@ -78,7 +83,8 @@
 
 uint8_t FX_DaysInMonth(int32_t iYear, uint8_t iMonth) {
   ASSERT(iYear != 0);
-  ASSERT(iMonth >= 1 && iMonth <= 12);
+  ASSERT(iMonth >= 1);
+  ASSERT(iMonth <= 12);
 
   const uint8_t* p =
       FX_IsLeapYear(iYear) ? g_FXDaysPerLeapMonth : g_FXDaysPerMonth;
@@ -93,9 +99,9 @@
 // static
 CFX_DateTime CFX_DateTime::Now() {
   FXUT_SYSTEMTIME utLocal;
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   ::GetLocalTime((LPSYSTEMTIME)&utLocal);
-#else   // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else
   timeval curTime;
   gettimeofday(&curTime, nullptr);
 
@@ -109,7 +115,7 @@
   utLocal.wMinute = st.tm_min;
   utLocal.wSecond = st.tm_sec;
   utLocal.wMillisecond = curTime.tv_usec / 1000;
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif  // defined(OS_WIN)
 
   return CFX_DateTime(utLocal.wYear, static_cast<uint8_t>(utLocal.wMonth),
                       static_cast<uint8_t>(utLocal.wDay),
diff --git a/core/fxcrt/cfx_datetime.h b/core/fxcrt/cfx_datetime.h
index 910a847..a965df7 100644
--- a/core/fxcrt/cfx_datetime.h
+++ b/core/fxcrt/cfx_datetime.h
@@ -91,15 +91,9 @@
   uint16_t millisecond_;
 };
 
-#if _FX_OS_ != _FX_OS_ANDROID_
-#pragma pack(push, 1)
-#endif
 struct FX_TIMEZONE {
   int8_t tzHour;
   uint8_t tzMinute;
 };
-#if _FX_OS_ != _FX_OS_ANDROID_
-#pragma pack(pop)
-#endif
 
 #endif  // CORE_FXCRT_CFX_DATETIME_H_
diff --git a/core/fxcrt/cfx_decimal.cpp b/core/fxcrt/cfx_decimal.cpp
deleted file mode 100644
index 45980db..0000000
--- a/core/fxcrt/cfx_decimal.cpp
+++ /dev/null
@@ -1,473 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_decimal.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "core/fxcrt/fx_extension.h"
-
-#define FXMATH_DECIMAL_SCALELIMIT 0x1c
-#define FXMATH_DECIMAL_NEGMASK (0x80000000L)
-#define FXMATH_DECIMAL_FORCEBOOL(x) (!!(x))
-#define FXMATH_DECIMAL_MAKEFLAGS(NEG, SCALE) \
-  (((SCALE) << 0x10) | ((NEG) ? FXMATH_DECIMAL_NEGMASK : 0))
-#define FXMATH_DECIMAL_FLAGS2NEG(FLAGS) \
-  FXMATH_DECIMAL_FORCEBOOL((FLAGS)&FXMATH_DECIMAL_NEGMASK)
-#define FXMATH_DECIMAL_FLAGS2SCALE(FLAGS) \
-  ((uint8_t)(((FLAGS) & ~FXMATH_DECIMAL_NEGMASK) >> 0x10))
-#define FXMATH_DECIMAL_RSHIFT32BIT(x) ((x) >> 0x10 >> 0x10)
-#define FXMATH_DECIMAL_LSHIFT32BIT(x) ((x) << 0x10 << 0x10)
-
-namespace {
-
-inline uint8_t decimal_helper_div10(uint64_t& phi,
-                                    uint64_t& pmid,
-                                    uint64_t& plo) {
-  uint8_t retVal;
-  pmid += FXMATH_DECIMAL_LSHIFT32BIT(phi % 0xA);
-  phi /= 0xA;
-  plo += FXMATH_DECIMAL_LSHIFT32BIT(pmid % 0xA);
-  pmid /= 0xA;
-  retVal = plo % 0xA;
-  plo /= 0xA;
-  return retVal;
-}
-
-inline uint8_t decimal_helper_div10_any(uint64_t nums[], uint8_t numcount) {
-  uint8_t retVal = 0;
-  for (int i = numcount - 1; i > 0; i--) {
-    nums[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(nums[i] % 0xA);
-    nums[i] /= 0xA;
-  }
-  if (numcount) {
-    retVal = nums[0] % 0xA;
-    nums[0] /= 0xA;
-  }
-  return retVal;
-}
-
-inline void decimal_helper_mul10(uint64_t& phi, uint64_t& pmid, uint64_t& plo) {
-  plo *= 0xA;
-  pmid = pmid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(plo);
-  plo = (uint32_t)plo;
-  phi = phi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(pmid);
-  pmid = (uint32_t)pmid;
-}
-
-inline void decimal_helper_mul10_any(uint64_t nums[], uint8_t numcount) {
-  nums[0] *= 0xA;
-  for (int i = 1; i < numcount; i++) {
-    nums[i] = nums[i] * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(nums[i - 1]);
-    nums[i - 1] = (uint32_t)nums[i - 1];
-  }
-}
-
-inline void decimal_helper_normalize(uint64_t& phi,
-                                     uint64_t& pmid,
-                                     uint64_t& plo) {
-  phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
-  pmid = (uint32_t)pmid;
-  pmid += FXMATH_DECIMAL_RSHIFT32BIT(plo);
-  plo = (uint32_t)plo;
-  phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
-  pmid = (uint32_t)pmid;
-}
-
-inline void decimal_helper_normalize_any(uint64_t nums[], uint8_t len) {
-  for (int i = len - 2; i > 0; i--) {
-    nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
-    nums[i] = (uint32_t)nums[i];
-  }
-  for (int i = 0; i < len - 1; i++) {
-    nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
-    nums[i] = (uint32_t)nums[i];
-  }
-}
-
-inline int8_t decimal_helper_raw_compare_any(uint64_t a[],
-                                             uint8_t al,
-                                             uint64_t b[],
-                                             uint8_t bl) {
-  int8_t retVal = 0;
-  for (int i = std::max(al - 1, bl - 1); i >= 0; i--) {
-    uint64_t l = (i >= al ? 0 : a[i]), r = (i >= bl ? 0 : b[i]);
-    retVal += (l > r ? 1 : (l < r ? -1 : 0));
-    if (retVal)
-      return retVal;
-  }
-  return retVal;
-}
-
-inline void decimal_helper_dec_any(uint64_t a[], uint8_t al) {
-  for (int i = 0; i < al; i++) {
-    if (a[i]--)
-      return;
-  }
-}
-
-inline void decimal_helper_inc_any(uint64_t a[], uint8_t al) {
-  for (int i = 0; i < al; i++) {
-    a[i]++;
-    if ((uint32_t)a[i] == a[i])
-      return;
-    a[i] = 0;
-  }
-}
-
-inline void decimal_helper_raw_mul(uint64_t a[],
-                                   uint8_t al,
-                                   uint64_t b[],
-                                   uint8_t bl,
-                                   uint64_t c[],
-                                   uint8_t cl) {
-  ASSERT(al + bl <= cl);
-  for (int i = 0; i < cl; i++)
-    c[i] = 0;
-
-  for (int i = 0; i < al; i++) {
-    for (int j = 0; j < bl; j++) {
-      uint64_t m = (uint64_t)a[i] * b[j];
-      c[i + j] += (uint32_t)m;
-      c[i + j + 1] += FXMATH_DECIMAL_RSHIFT32BIT(m);
-    }
-  }
-  for (int i = 0; i < cl - 1; i++) {
-    c[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(c[i]);
-    c[i] = (uint32_t)c[i];
-  }
-  for (int i = 0; i < cl; i++)
-    c[i] = (uint32_t)c[i];
-}
-
-inline void decimal_helper_raw_div(uint64_t a[],
-                                   uint8_t al,
-                                   uint64_t b[],
-                                   uint8_t bl,
-                                   uint64_t c[],
-                                   uint8_t cl) {
-  for (int i = 0; i < cl; i++)
-    c[i] = 0;
-
-  uint64_t left[16] = {0};
-  uint64_t right[16] = {0};
-  left[0] = 0;
-  for (int i = 0; i < al; i++)
-    right[i] = a[i];
-
-  uint64_t tmp[16];
-  while (decimal_helper_raw_compare_any(left, al, right, al) <= 0) {
-    uint64_t cur[16];
-    for (int i = 0; i < al; i++)
-      cur[i] = left[i] + right[i];
-
-    for (int i = al - 1; i >= 0; i--) {
-      if (i)
-        cur[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(cur[i] % 2);
-      cur[i] /= 2;
-    }
-
-    decimal_helper_raw_mul(cur, al, b, bl, tmp, 16);
-    switch (decimal_helper_raw_compare_any(tmp, 16, a, al)) {
-      case -1:
-        for (int i = 0; i < 16; i++)
-          left[i] = cur[i];
-
-        left[0]++;
-        decimal_helper_normalize_any(left, al);
-        break;
-      case 1:
-        for (int i = 0; i < 16; i++)
-          right[i] = cur[i];
-        decimal_helper_dec_any(right, al);
-        break;
-      case 0:
-        for (int i = 0; i < std::min(al, cl); i++)
-          c[i] = cur[i];
-        return;
-    }
-  }
-  for (int i = 0; i < std::min(al, cl); i++)
-    c[i] = left[i];
-}
-
-inline bool decimal_helper_outofrange(uint64_t a[], uint8_t al, uint8_t goal) {
-  for (int i = goal; i < al; i++) {
-    if (a[i])
-      return true;
-  }
-  return false;
-}
-
-inline void decimal_helper_shrinkintorange(uint64_t a[],
-                                           uint8_t al,
-                                           uint8_t goal,
-                                           uint8_t& scale) {
-  bool bRoundUp = false;
-  while (scale != 0 && (scale > FXMATH_DECIMAL_SCALELIMIT ||
-                        decimal_helper_outofrange(a, al, goal))) {
-    bRoundUp = decimal_helper_div10_any(a, al) >= 5;
-    scale--;
-  }
-  if (bRoundUp) {
-    decimal_helper_normalize_any(a, goal);
-    decimal_helper_inc_any(a, goal);
-  }
-}
-
-inline void decimal_helper_truncate(uint64_t& phi,
-                                    uint64_t& pmid,
-                                    uint64_t& plo,
-                                    uint8_t& scale,
-                                    uint8_t minscale = 0) {
-  while (scale > minscale) {
-    uint64_t thi = phi, tmid = pmid, tlo = plo;
-    if (decimal_helper_div10(thi, tmid, tlo) != 0)
-      break;
-
-    phi = thi;
-    pmid = tmid;
-    plo = tlo;
-    scale--;
-  }
-}
-
-}  // namespace
-
-CFX_Decimal::CFX_Decimal() : m_uHi(0), m_uLo(0), m_uMid(0), m_uFlags(0) {}
-
-CFX_Decimal::CFX_Decimal(uint64_t val)
-    : m_uHi(0),
-      m_uLo(static_cast<uint32_t>(val)),
-      m_uMid(static_cast<uint32_t>(FXMATH_DECIMAL_RSHIFT32BIT(val))),
-      m_uFlags(0) {}
-
-CFX_Decimal::CFX_Decimal(uint32_t val)
-    : m_uHi(0), m_uLo(static_cast<uint32_t>(val)), m_uMid(0), m_uFlags(0) {}
-
-CFX_Decimal::CFX_Decimal(uint32_t lo,
-                         uint32_t mid,
-                         uint32_t hi,
-                         bool neg,
-                         uint8_t scale)
-    : m_uHi(hi),
-      m_uLo(lo),
-      m_uMid(mid),
-      m_uFlags(FXMATH_DECIMAL_MAKEFLAGS(
-          neg && IsNotZero(),
-          (scale > FXMATH_DECIMAL_SCALELIMIT ? 0 : scale))) {}
-
-CFX_Decimal::CFX_Decimal(int32_t val) {
-  if (val >= 0) {
-    *this = CFX_Decimal(static_cast<uint32_t>(val));
-  } else {
-    *this = CFX_Decimal(static_cast<uint32_t>(-val));
-    SetNegate();
-  }
-}
-
-CFX_Decimal::CFX_Decimal(float val, uint8_t scale) {
-  float newval = fabs(val);
-  uint64_t phi;
-  uint64_t pmid;
-  uint64_t plo;
-  plo = static_cast<uint64_t>(newval);
-  pmid = static_cast<uint64_t>(newval / 1e32);
-  phi = static_cast<uint64_t>(newval / 1e64);
-  newval = fmod(newval, 1.0f);
-  for (uint8_t iter = 0; iter < scale; iter++) {
-    decimal_helper_mul10(phi, pmid, plo);
-    newval *= 10;
-    plo += static_cast<uint64_t>(newval);
-    newval = fmod(newval, 1.0f);
-  }
-
-  plo += FXSYS_round(newval);
-  decimal_helper_normalize(phi, pmid, plo);
-  m_uHi = static_cast<uint32_t>(phi);
-  m_uMid = static_cast<uint32_t>(pmid);
-  m_uLo = static_cast<uint32_t>(plo);
-  m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(val < 0 && IsNotZero(), scale);
-}
-
-CFX_Decimal::CFX_Decimal(const WideStringView& strObj) {
-  const wchar_t* str = strObj.unterminated_c_str();
-  const wchar_t* strBound = str + strObj.GetLength();
-  bool pointmet = false;
-  bool negmet = false;
-  uint8_t scale = 0;
-  m_uHi = 0;
-  m_uMid = 0;
-  m_uLo = 0;
-  while (str != strBound && *str == ' ')
-    str++;
-  if (str != strBound && *str == '-') {
-    negmet = 1;
-    str++;
-  } else if (str != strBound && *str == '+') {
-    str++;
-  }
-
-  while (str != strBound && (std::iswdigit(*str) || *str == '.') &&
-         scale < FXMATH_DECIMAL_SCALELIMIT) {
-    if (*str == '.') {
-      if (!pointmet)
-        pointmet = 1;
-    } else {
-      m_uHi = m_uHi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uMid * 0xA);
-      m_uMid = m_uMid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uLo * 0xA);
-      m_uLo = m_uLo * 0xA + (*str - '0');
-      if (pointmet)
-        scale++;
-    }
-    str++;
-  }
-  m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(negmet && IsNotZero(), scale);
-}
-
-CFX_Decimal::operator WideString() const {
-  WideString retString;
-  WideString tmpbuf;
-  uint64_t phi = m_uHi;
-  uint64_t pmid = m_uMid;
-  uint64_t plo = m_uLo;
-  while (phi || pmid || plo)
-    tmpbuf += decimal_helper_div10(phi, pmid, plo) + '0';
-
-  uint8_t outputlen = (uint8_t)tmpbuf.GetLength();
-  uint8_t scale = (uint8_t)FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
-  while (scale >= outputlen) {
-    tmpbuf += '0';
-    outputlen++;
-  }
-  if (FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) && IsNotZero())
-    retString += '-';
-
-  for (uint8_t idx = 0; idx < outputlen; idx++) {
-    if (idx == (outputlen - scale) && scale != 0)
-      retString += '.';
-    retString += tmpbuf[outputlen - 1 - idx];
-  }
-  return retString;
-}
-
-CFX_Decimal::operator double() const {
-  double pow = (double)(1 << 16) * (1 << 16);
-  double base = static_cast<double>(m_uHi) * pow * pow +
-                static_cast<double>(m_uMid) * pow + static_cast<double>(m_uLo);
-  int8_t scale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
-  bool bNeg = FXMATH_DECIMAL_FLAGS2NEG(m_uFlags);
-  return (bNeg ? -1 : 1) * base * ::pow(10.0, -scale);
-}
-
-void CFX_Decimal::SetScale(uint8_t newscale) {
-  uint8_t oldscale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
-  if (newscale > oldscale) {
-    uint64_t phi = m_uHi;
-    uint64_t pmid = m_uMid;
-    uint64_t plo = m_uLo;
-    for (uint8_t iter = 0; iter < newscale - oldscale; iter++)
-      decimal_helper_mul10(phi, pmid, plo);
-
-    m_uHi = static_cast<uint32_t>(phi);
-    m_uMid = static_cast<uint32_t>(pmid);
-    m_uLo = static_cast<uint32_t>(plo);
-    m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(
-        FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) && IsNotZero(), newscale);
-  } else if (newscale < oldscale) {
-    uint64_t phi;
-    uint64_t pmid;
-    uint64_t plo;
-    phi = 0;
-    pmid = 0;
-    plo = 5;
-    for (uint8_t iter = 0; iter < oldscale - newscale - 1; iter++)
-      decimal_helper_mul10(phi, pmid, plo);
-
-    phi += m_uHi;
-    pmid += m_uMid;
-    plo += m_uLo;
-    decimal_helper_normalize(phi, pmid, plo);
-    for (uint8_t iter = 0; iter < oldscale - newscale; iter++)
-      decimal_helper_div10(phi, pmid, plo);
-
-    m_uHi = static_cast<uint32_t>(phi);
-    m_uMid = static_cast<uint32_t>(pmid);
-    m_uLo = static_cast<uint32_t>(plo);
-    m_uFlags = FXMATH_DECIMAL_MAKEFLAGS(
-        FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) && IsNotZero(), newscale);
-  }
-}
-
-uint8_t CFX_Decimal::GetScale() {
-  return FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
-}
-
-void CFX_Decimal::SetNegate() {
-  if (IsNotZero())
-    m_uFlags ^= FXMATH_DECIMAL_NEGMASK;
-}
-
-void CFX_Decimal::Swap(CFX_Decimal& val) {
-  std::swap(m_uHi, val.m_uHi);
-  std::swap(m_uMid, val.m_uMid);
-  std::swap(m_uLo, val.m_uLo);
-  std::swap(m_uFlags, val.m_uFlags);
-}
-
-CFX_Decimal CFX_Decimal::operator*(const CFX_Decimal& val) const {
-  uint64_t a[3] = {m_uLo, m_uMid, m_uHi},
-           b[3] = {val.m_uLo, val.m_uMid, val.m_uHi};
-  uint64_t c[6];
-  decimal_helper_raw_mul(a, 3, b, 3, c, 6);
-  bool neg = FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) ^
-             FXMATH_DECIMAL_FLAGS2NEG(val.m_uFlags);
-  uint8_t scale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags) +
-                  FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags);
-  decimal_helper_shrinkintorange(c, 6, 3, scale);
-  return CFX_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
-                     static_cast<uint32_t>(c[2]), neg, scale);
-}
-
-CFX_Decimal CFX_Decimal::operator/(const CFX_Decimal& val) const {
-  if (!val.IsNotZero())
-    return CFX_Decimal();
-
-  bool neg = FXMATH_DECIMAL_FLAGS2NEG(m_uFlags) ^
-             FXMATH_DECIMAL_FLAGS2NEG(val.m_uFlags);
-  uint64_t a[7] = {m_uLo, m_uMid, m_uHi},
-           b[3] = {val.m_uLo, val.m_uMid, val.m_uHi}, c[7] = {0};
-  uint8_t scale = 0;
-  if (FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags) <
-      FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags)) {
-    for (int i = FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags) -
-                 FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags);
-         i > 0; i--) {
-      decimal_helper_mul10_any(a, 7);
-    }
-  } else {
-    scale = FXMATH_DECIMAL_FLAGS2SCALE(m_uFlags) -
-            FXMATH_DECIMAL_FLAGS2SCALE(val.m_uFlags);
-  }
-
-  uint8_t minscale = scale;
-  if (!IsNotZero())
-    return CFX_Decimal(0, 0, 0, 0, minscale);
-
-  while (!a[6]) {
-    decimal_helper_mul10_any(a, 7);
-    scale++;
-  }
-
-  decimal_helper_div10_any(a, 7);
-  scale--;
-  decimal_helper_raw_div(a, 6, b, 3, c, 7);
-  decimal_helper_shrinkintorange(c, 6, 3, scale);
-  decimal_helper_truncate(c[2], c[1], c[0], scale, minscale);
-  return CFX_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
-                     static_cast<uint32_t>(c[2]), neg, scale);
-}
diff --git a/core/fxcrt/cfx_decimal.h b/core/fxcrt/cfx_decimal.h
deleted file mode 100644
index de6e3de..0000000
--- a/core/fxcrt/cfx_decimal.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_DECIMAL_H_
-#define CORE_FXCRT_CFX_DECIMAL_H_
-
-#include "core/fxcrt/fx_string.h"
-
-class CFX_Decimal {
- public:
-  CFX_Decimal();
-  explicit CFX_Decimal(uint32_t val);
-  explicit CFX_Decimal(uint64_t val);
-  explicit CFX_Decimal(int32_t val);
-  CFX_Decimal(float val, uint8_t scale);
-  explicit CFX_Decimal(const WideStringView& str);
-
-  operator WideString() const;
-  operator double() const;
-
-  CFX_Decimal operator*(const CFX_Decimal& val) const;
-  CFX_Decimal operator/(const CFX_Decimal& val) const;
-
-  void SetScale(uint8_t newScale);
-  uint8_t GetScale();
-  void SetNegate();
-
- private:
-  CFX_Decimal(uint32_t hi, uint32_t mid, uint32_t lo, bool neg, uint8_t scale);
-  bool IsNotZero() const { return m_uHi || m_uMid || m_uLo; }
-  void Swap(CFX_Decimal& val);
-
-  uint32_t m_uHi;
-  uint32_t m_uLo;
-  uint32_t m_uMid;
-  uint32_t m_uFlags;
-};
-
-#endif  // CORE_FXCRT_CFX_DECIMAL_H_
diff --git a/core/fxcrt/cfx_fileaccess_posix.cpp b/core/fxcrt/cfx_fileaccess_posix.cpp
index 2f85670..f08df66 100644
--- a/core/fxcrt/cfx_fileaccess_posix.cpp
+++ b/core/fxcrt/cfx_fileaccess_posix.cpp
@@ -6,8 +6,13 @@
 
 #include "core/fxcrt/cfx_fileaccess_posix.h"
 
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include <memory>
 
+#include "core/fxcrt/fx_stream.h"
 #include "third_party/base/ptr_util.h"
 
 #ifndef O_BINARY
@@ -18,10 +23,6 @@
 #define O_LARGEFILE 0
 #endif  // O_LARGEFILE
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ || \
-    _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ || \
-    _FX_PLATFORM_ == _FX_PLATFORM_ANDROID_
-
 namespace {
 
 void GetFileMode(uint32_t dwModes, int32_t& nFlags, int32_t& nMasks) {
@@ -41,7 +42,7 @@
 }  // namespace
 
 // static
-std::unique_ptr<IFX_FileAccess> IFX_FileAccess::Create() {
+std::unique_ptr<FileAccessIface> FileAccessIface::Create() {
   return pdfium::MakeUnique<CFX_FileAccess_Posix>();
 }
 
@@ -51,8 +52,7 @@
   Close();
 }
 
-bool CFX_FileAccess_Posix::Open(const ByteStringView& fileName,
-                                uint32_t dwMode) {
+bool CFX_FileAccess_Posix::Open(ByteStringView fileName, uint32_t dwMode) {
   if (m_nFD > -1)
     return false;
 
@@ -65,8 +65,7 @@
   return m_nFD > -1;
 }
 
-bool CFX_FileAccess_Posix::Open(const WideStringView& fileName,
-                                uint32_t dwMode) {
+bool CFX_FileAccess_Posix::Open(WideStringView fileName, uint32_t dwMode) {
   return Open(FX_UTF8Encode(fileName).AsStringView(), dwMode);
 }
 
@@ -149,5 +148,3 @@
 
   return !ftruncate(m_nFD, szFile);
 }
-
-#endif
diff --git a/core/fxcrt/cfx_fileaccess_posix.h b/core/fxcrt/cfx_fileaccess_posix.h
index efa25eb..39fdb23 100644
--- a/core/fxcrt/cfx_fileaccess_posix.h
+++ b/core/fxcrt/cfx_fileaccess_posix.h
@@ -7,19 +7,23 @@
 #ifndef CORE_FXCRT_CFX_FILEACCESS_POSIX_H_
 #define CORE_FXCRT_CFX_FILEACCESS_POSIX_H_
 
-#include "core/fxcrt/ifx_fileaccess.h"
+#include "build/build_config.h"
+#include "core/fxcrt/fileaccess_iface.h"
+#include "core/fxcrt/fx_system.h"
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ || \
-    _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ || \
-    _FX_PLATFORM_ == _FX_PLATFORM_ANDROID_
-class CFX_FileAccess_Posix : public IFX_FileAccess {
+#if _FX_PLATFORM_ != _FX_PLATFORM_LINUX_ && !defined(OS_MACOSX) && \
+    !defined(OS_ANDROID)
+#error "Included on the wrong platform"
+#endif
+
+class CFX_FileAccess_Posix final : public FileAccessIface {
  public:
   CFX_FileAccess_Posix();
   ~CFX_FileAccess_Posix() override;
 
-  // IFX_FileAccess:
-  bool Open(const ByteStringView& fileName, uint32_t dwMode) override;
-  bool Open(const WideStringView& fileName, uint32_t dwMode) override;
+  // FileAccessIface:
+  bool Open(ByteStringView fileName, uint32_t dwMode) override;
+  bool Open(WideStringView fileName, uint32_t dwMode) override;
   void Close() override;
   FX_FILESIZE GetSize() const override;
   FX_FILESIZE GetPosition() const override;
@@ -33,9 +37,8 @@
   bool Flush() override;
   bool Truncate(FX_FILESIZE szFile) override;
 
- protected:
+ private:
   int32_t m_nFD;
 };
-#endif
 
 #endif  // CORE_FXCRT_CFX_FILEACCESS_POSIX_H_
diff --git a/core/fxcrt/cfx_fileaccess_windows.cpp b/core/fxcrt/cfx_fileaccess_windows.cpp
index 1ac4496..d1e5ff1 100644
--- a/core/fxcrt/cfx_fileaccess_windows.cpp
+++ b/core/fxcrt/cfx_fileaccess_windows.cpp
@@ -8,11 +8,10 @@
 
 #include <memory>
 
+#include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_string.h"
 #include "third_party/base/ptr_util.h"
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
 namespace {
 
 void GetFileMode(uint32_t dwMode,
@@ -32,30 +31,17 @@
 }  // namespace
 
 // static
-std::unique_ptr<IFX_FileAccess> IFX_FileAccess::Create() {
+std::unique_ptr<FileAccessIface> FileAccessIface::Create() {
   return pdfium::MakeUnique<CFX_FileAccess_Windows>();
 }
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize);
-WINBASEAPI BOOL WINAPI SetFilePointerEx(HANDLE hFile,
-                                        LARGE_INTEGER liDistanceToMove,
-                                        PLARGE_INTEGER lpNewFilePointer,
-                                        DWORD dwMoveMethod);
-#ifdef __cplusplus
-}
-#endif
-
 CFX_FileAccess_Windows::CFX_FileAccess_Windows() : m_hFile(nullptr) {}
 
 CFX_FileAccess_Windows::~CFX_FileAccess_Windows() {
   Close();
 }
 
-bool CFX_FileAccess_Windows::Open(const ByteStringView& fileName,
-                                  uint32_t dwMode) {
+bool CFX_FileAccess_Windows::Open(ByteStringView fileName, uint32_t dwMode) {
   if (m_hFile)
     return false;
 
@@ -69,8 +55,7 @@
   return !!m_hFile;
 }
 
-bool CFX_FileAccess_Windows::Open(const WideStringView& fileName,
-                                  uint32_t dwMode) {
+bool CFX_FileAccess_Windows::Open(WideStringView fileName, uint32_t dwMode) {
   if (m_hFile)
     return false;
 
@@ -193,4 +178,3 @@
 
   return !!::SetEndOfFile(m_hFile);
 }
-#endif
diff --git a/core/fxcrt/cfx_fileaccess_windows.h b/core/fxcrt/cfx_fileaccess_windows.h
index 6fd07d5..a95466f 100644
--- a/core/fxcrt/cfx_fileaccess_windows.h
+++ b/core/fxcrt/cfx_fileaccess_windows.h
@@ -7,17 +7,22 @@
 #ifndef CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_
 #define CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_
 
-#include "core/fxcrt/ifx_fileaccess.h"
+#include "build/build_config.h"
+#include "core/fxcrt/fileaccess_iface.h"
+#include "core/fxcrt/fx_system.h"
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-class CFX_FileAccess_Windows : public IFX_FileAccess {
+#if !defined(OS_WIN)
+#error "Included on the wrong platform"
+#endif
+
+class CFX_FileAccess_Windows final : public FileAccessIface {
  public:
   CFX_FileAccess_Windows();
   ~CFX_FileAccess_Windows() override;
 
-  // IFX_FileAccess
-  bool Open(const ByteStringView& fileName, uint32_t dwMode) override;
-  bool Open(const WideStringView& fileName, uint32_t dwMode) override;
+  // FileAccessIface
+  bool Open(ByteStringView fileName, uint32_t dwMode) override;
+  bool Open(WideStringView fileName, uint32_t dwMode) override;
   void Close() override;
   FX_FILESIZE GetSize() const override;
   FX_FILESIZE GetPosition() const override;
@@ -31,9 +36,8 @@
   bool Flush() override;
   bool Truncate(FX_FILESIZE szFile) override;
 
- protected:
+ private:
   void* m_hFile;
 };
-#endif
 
 #endif  // CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_
diff --git a/core/fxcrt/cfx_fixedbufgrow.h b/core/fxcrt/cfx_fixedbufgrow.h
index b12b710..daeac13 100644
--- a/core/fxcrt/cfx_fixedbufgrow.h
+++ b/core/fxcrt/cfx_fixedbufgrow.h
@@ -9,12 +9,12 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 
-template <class DataType, int FixedSize>
+template <class DataType, size_t FixedSize>
 class CFX_FixedBufGrow {
  public:
-  explicit CFX_FixedBufGrow(int data_size) {
+  explicit CFX_FixedBufGrow(size_t data_size) {
     if (data_size > FixedSize) {
       m_pGrowData.reset(FX_Alloc(DataType, data_size));
       return;
@@ -24,8 +24,8 @@
   operator DataType*() { return m_pGrowData ? m_pGrowData.get() : m_FixedData; }
 
  private:
-  DataType m_FixedData[FixedSize];
   std::unique_ptr<DataType, FxFreeDeleter> m_pGrowData;
+  DataType m_FixedData[FixedSize];
 };
 
 #endif  // CORE_FXCRT_CFX_FIXEDBUFGROW_H_
diff --git a/core/fxcrt/cfx_memorystream.cpp b/core/fxcrt/cfx_memorystream.cpp
index 345b381..b1f131e 100644
--- a/core/fxcrt/cfx_memorystream.cpp
+++ b/core/fxcrt/cfx_memorystream.cpp
@@ -7,39 +7,18 @@
 #include "core/fxcrt/cfx_memorystream.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "core/fxcrt/fx_safe_types.h"
 
-namespace {
+CFX_MemoryStream::CFX_MemoryStream() : m_nTotalSize(0), m_nCurSize(0) {}
 
-const int32_t kBlockSize = 64 * 1024;
+CFX_MemoryStream::CFX_MemoryStream(
+    std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer,
+    size_t nSize)
+    : m_data(std::move(pBuffer)), m_nTotalSize(nSize), m_nCurSize(nSize) {}
 
-}  // namespace
-
-CFX_MemoryStream::CFX_MemoryStream(bool bConsecutive)
-    : m_nTotalSize(0),
-      m_nCurSize(0),
-      m_nCurPos(0),
-      m_nGrowSize(kBlockSize),
-      m_dwFlags(Type::kTakeOver | (bConsecutive ? Type::kConsecutive : 0)) {}
-
-CFX_MemoryStream::CFX_MemoryStream(uint8_t* pBuffer,
-                                   size_t nSize,
-                                   bool bTakeOver)
-    : m_nTotalSize(nSize),
-      m_nCurSize(nSize),
-      m_nCurPos(0),
-      m_nGrowSize(kBlockSize),
-      m_dwFlags(Type::kConsecutive | (bTakeOver ? Type::kTakeOver : 0)) {
-  m_Blocks.push_back(pBuffer);
-}
-
-CFX_MemoryStream::~CFX_MemoryStream() {
-  if (m_dwFlags & Type::kTakeOver) {
-    for (uint8_t* pBlock : m_Blocks)
-      FX_Free(pBlock);
-  }
-}
+CFX_MemoryStream::~CFX_MemoryStream() = default;
 
 FX_FILESIZE CFX_MemoryStream::GetSize() {
   return static_cast<FX_FILESIZE>(m_nCurSize);
@@ -57,10 +36,10 @@
   return true;
 }
 
-bool CFX_MemoryStream::ReadBlock(void* buffer,
-                                 FX_FILESIZE offset,
-                                 size_t size) {
-  if (!buffer || !size || offset < 0)
+bool CFX_MemoryStream::ReadBlockAtOffset(void* buffer,
+                                         FX_FILESIZE offset,
+                                         size_t size) {
+  if (!buffer || offset < 0 || !size)
     return false;
 
   FX_SAFE_SIZE_T newPos = size;
@@ -71,21 +50,7 @@
   }
 
   m_nCurPos = newPos.ValueOrDie();
-  if (m_dwFlags & Type::kConsecutive) {
-    memcpy(buffer, m_Blocks[0] + static_cast<size_t>(offset), size);
-    return true;
-  }
-
-  size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize;
-  offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize);
-  while (size) {
-    size_t nRead = std::min(size, m_nGrowSize - static_cast<size_t>(offset));
-    memcpy(buffer, m_Blocks[nStartBlock] + offset, nRead);
-    buffer = static_cast<uint8_t*>(buffer) + nRead;
-    size -= nRead;
-    ++nStartBlock;
-    offset = 0;
-  }
+  memcpy(buffer, &GetBuffer()[offset], size);
   return true;
 }
 
@@ -94,106 +59,44 @@
     return 0;
 
   size_t nRead = std::min(size, m_nCurSize - m_nCurPos);
-  if (!ReadBlock(buffer, static_cast<int32_t>(m_nCurPos), nRead))
+  if (!ReadBlockAtOffset(buffer, static_cast<int32_t>(m_nCurPos), nRead))
     return 0;
 
   return nRead;
 }
 
-bool CFX_MemoryStream::WriteBlock(const void* buffer,
-                                  FX_FILESIZE offset,
-                                  size_t size) {
-  if (!buffer || !size)
+bool CFX_MemoryStream::WriteBlockAtOffset(const void* buffer,
+                                          FX_FILESIZE offset,
+                                          size_t size) {
+  if (!buffer || offset < 0 || !size)
     return false;
 
-  if (m_dwFlags & Type::kConsecutive) {
-    FX_SAFE_SIZE_T newPos = size;
-    newPos += offset;
-    if (!newPos.IsValid())
+  FX_SAFE_SIZE_T safe_new_pos = size;
+  safe_new_pos += offset;
+  if (!safe_new_pos.IsValid())
+    return false;
+
+  size_t new_pos = safe_new_pos.ValueOrDie();
+  if (new_pos > m_nTotalSize) {
+    static constexpr size_t kBlockSize = 64 * 1024;
+    FX_SAFE_SIZE_T new_size = new_pos;
+    new_size *= 2;
+    new_size += (kBlockSize - 1);
+    new_size /= kBlockSize;
+    new_size *= kBlockSize;
+    if (!new_size.IsValid())
       return false;
 
-    m_nCurPos = newPos.ValueOrDie();
-    if (m_nCurPos > m_nTotalSize) {
-      m_nTotalSize = (m_nCurPos + m_nGrowSize - 1) / m_nGrowSize * m_nGrowSize;
-      if (m_Blocks.empty())
-        m_Blocks.push_back(FX_Alloc(uint8_t, m_nTotalSize));
-      else
-        m_Blocks[0] = FX_Realloc(uint8_t, m_Blocks[0], m_nTotalSize);
-    }
-
-    memcpy(m_Blocks[0] + offset, buffer, size);
-    m_nCurSize = std::max(m_nCurSize, m_nCurPos);
-
-    return true;
+    m_nTotalSize = new_size.ValueOrDie();
+    if (m_data)
+      m_data.reset(FX_Realloc(uint8_t, m_data.release(), m_nTotalSize));
+    else
+      m_data.reset(FX_Alloc(uint8_t, m_nTotalSize));
   }
+  m_nCurPos = new_pos;
 
-  FX_SAFE_SIZE_T newPos = size;
-  newPos += offset;
-  if (!newPos.IsValid())
-    return false;
-  if (!ExpandBlocks(newPos.ValueOrDie()))
-    return false;
+  memcpy(&m_data.get()[offset], buffer, size);
+  m_nCurSize = std::max(m_nCurSize, m_nCurPos);
 
-  m_nCurPos = newPos.ValueOrDie();
-  size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize;
-  offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize);
-  while (size) {
-    size_t nWrite = std::min(size, m_nGrowSize - static_cast<size_t>(offset));
-    memcpy(m_Blocks[nStartBlock] + offset, buffer, nWrite);
-    buffer = static_cast<const uint8_t*>(buffer) + nWrite;
-    size -= nWrite;
-    ++nStartBlock;
-    offset = 0;
-  }
-  return true;
-}
-
-void CFX_MemoryStream::EstimateSize(size_t nInitSize, size_t nGrowSize) {
-  if (m_dwFlags & Type::kConsecutive) {
-    if (m_Blocks.empty()) {
-      m_Blocks.push_back(
-          FX_Alloc(uint8_t, std::max(nInitSize, static_cast<size_t>(4096))));
-    }
-    m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096));
-  } else if (m_Blocks.empty()) {
-    m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096));
-  }
-}
-
-void CFX_MemoryStream::AttachBuffer(uint8_t* pBuffer, size_t nSize) {
-  if (!(m_dwFlags & Type::kConsecutive))
-    return;
-
-  m_Blocks.clear();
-  m_Blocks.push_back(pBuffer);
-  m_nTotalSize = nSize;
-  m_nCurSize = nSize;
-  m_nCurPos = 0;
-  m_dwFlags = Type::kConsecutive;
-}
-
-void CFX_MemoryStream::DetachBuffer() {
-  if (!(m_dwFlags & Type::kConsecutive))
-    return;
-
-  m_Blocks.clear();
-  m_nTotalSize = 0;
-  m_nCurSize = 0;
-  m_nCurPos = 0;
-  m_dwFlags = Type::kTakeOver;
-}
-
-bool CFX_MemoryStream::ExpandBlocks(size_t size) {
-  m_nCurSize = std::max(m_nCurSize, size);
-  if (size <= m_nTotalSize)
-    return true;
-
-  size = (size - m_nTotalSize + m_nGrowSize - 1) / m_nGrowSize;
-  size_t iCount = m_Blocks.size();
-  m_Blocks.resize(iCount + size);
-  while (size--) {
-    m_Blocks[iCount++] = FX_Alloc(uint8_t, m_nGrowSize);
-    m_nTotalSize += m_nGrowSize;
-  }
   return true;
 }
diff --git a/core/fxcrt/cfx_memorystream.h b/core/fxcrt/cfx_memorystream.h
index ef66080..ce6fd64 100644
--- a/core/fxcrt/cfx_memorystream.h
+++ b/core/fxcrt/cfx_memorystream.h
@@ -7,15 +7,14 @@
 #ifndef CORE_FXCRT_CFX_MEMORYSTREAM_H_
 #define CORE_FXCRT_CFX_MEMORYSTREAM_H_
 
-#include <vector>
+#include <memory>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CFX_MemoryStream : public IFX_SeekableStream {
+class CFX_MemoryStream final : public IFX_SeekableStream {
  public:
-  enum Type { kConsecutive = 1 << 0, kTakeOver = 1 << 1 };
-
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
@@ -23,34 +22,27 @@
   FX_FILESIZE GetSize() override;
   FX_FILESIZE GetPosition() override;
   bool IsEOF() override;
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override;
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override;
   size_t ReadBlock(void* buffer, size_t size) override;
-  bool WriteBlock(const void* buffer, FX_FILESIZE offset, size_t size) override;
+  bool WriteBlockAtOffset(const void* buffer,
+                          FX_FILESIZE offset,
+                          size_t size) override;
   bool Flush() override;
 
-  bool IsConsecutive() const { return !!(m_dwFlags & Type::kConsecutive); }
-
-  uint8_t* GetBuffer() const {
-    return !m_Blocks.empty() ? m_Blocks.front() : nullptr;
-  }
-
-  void EstimateSize(size_t nInitSize, size_t nGrowSize);
-  void AttachBuffer(uint8_t* pBuffer, size_t nSize);
-  void DetachBuffer();
+  const uint8_t* GetBuffer() const { return m_data.get(); }
 
  private:
-  explicit CFX_MemoryStream(bool bConsecutive);
-  CFX_MemoryStream(uint8_t* pBuffer, size_t nSize, bool bTakeOver);
+  CFX_MemoryStream();
+  CFX_MemoryStream(std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer,
+                   size_t nSize);
   ~CFX_MemoryStream() override;
 
-  bool ExpandBlocks(size_t size);
-
-  std::vector<uint8_t*> m_Blocks;
+  std::unique_ptr<uint8_t, FxFreeDeleter> m_data;
   size_t m_nTotalSize;
   size_t m_nCurSize;
-  size_t m_nCurPos;
-  size_t m_nGrowSize;
-  uint32_t m_dwFlags;
+  size_t m_nCurPos = 0;
 };
 
 #endif  // CORE_FXCRT_CFX_MEMORYSTREAM_H_
diff --git a/core/fxcrt/cfx_memorystream_unittest.cpp b/core/fxcrt/cfx_memorystream_unittest.cpp
new file mode 100644
index 0000000..8339e8e
--- /dev/null
+++ b/core/fxcrt/cfx_memorystream_unittest.cpp
@@ -0,0 +1,31 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_memorystream.h"
+
+#include "core/fxcrt/retain_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kSomeText[] = "Lets make holes in streams";
+const size_t kSomeTextLen = sizeof(kSomeText) - 1;
+
+}  // namespace
+
+TEST(CFX_MemoryStreamTest, SparseBlockWrites) {
+  auto stream = pdfium::MakeRetain<CFX_MemoryStream>();
+  for (FX_FILESIZE offset = 0; offset <= 200000; offset += 100000)
+    stream->WriteBlockAtOffset(kSomeText, offset, kSomeTextLen);
+
+  EXPECT_EQ(200000 + kSomeTextLen, static_cast<size_t>(stream->GetSize()));
+}
+
+TEST(CFX_MemoryStreamTest, OverlappingBlockWrites) {
+  auto stream = pdfium::MakeRetain<CFX_MemoryStream>();
+  for (FX_FILESIZE offset = 0; offset <= 100; ++offset)
+    stream->WriteBlockAtOffset(kSomeText, offset, kSomeTextLen);
+
+  EXPECT_EQ(100 + kSomeTextLen, static_cast<size_t>(stream->GetSize()));
+}
diff --git a/core/fxcrt/cfx_readonlymemorystream.cpp b/core/fxcrt/cfx_readonlymemorystream.cpp
new file mode 100644
index 0000000..807788a
--- /dev/null
+++ b/core/fxcrt/cfx_readonlymemorystream.cpp
@@ -0,0 +1,42 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+
+#include <utility>
+
+#include "core/fxcrt/fx_safe_types.h"
+
+CFX_ReadOnlyMemoryStream::CFX_ReadOnlyMemoryStream(
+    std::unique_ptr<uint8_t, FxFreeDeleter> data,
+    size_t size)
+    : m_data(std::move(data)), m_span(m_data.get(), size) {}
+
+CFX_ReadOnlyMemoryStream::CFX_ReadOnlyMemoryStream(
+    pdfium::span<const uint8_t> span)
+    : m_span(span) {}
+
+CFX_ReadOnlyMemoryStream::~CFX_ReadOnlyMemoryStream() = default;
+
+FX_FILESIZE CFX_ReadOnlyMemoryStream::GetSize() {
+  return pdfium::base::checked_cast<FX_FILESIZE>(m_span.size());
+}
+
+bool CFX_ReadOnlyMemoryStream::ReadBlockAtOffset(void* buffer,
+                                                 FX_FILESIZE offset,
+                                                 size_t size) {
+  if (!buffer || offset < 0 || size == 0)
+    return false;
+
+  FX_SAFE_SIZE_T pos = size;
+  pos += offset;
+  if (!pos.IsValid() || pos.ValueOrDie() > m_span.size())
+    return false;
+
+  auto copy_span = m_span.subspan(offset, size);
+  memcpy(buffer, copy_span.data(), copy_span.size());
+  return true;
+}
diff --git a/core/fxcrt/cfx_readonlymemorystream.h b/core/fxcrt/cfx_readonlymemorystream.h
new file mode 100644
index 0000000..ea2b849
--- /dev/null
+++ b/core/fxcrt/cfx_readonlymemorystream.h
@@ -0,0 +1,38 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_
+#define CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/span.h"
+
+class CFX_ReadOnlyMemoryStream final : public IFX_SeekableReadStream {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  // IFX_SeekableReadStream:
+  FX_FILESIZE GetSize() override;
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override;
+
+ private:
+  CFX_ReadOnlyMemoryStream(std::unique_ptr<uint8_t, FxFreeDeleter> data,
+                           size_t size);
+  explicit CFX_ReadOnlyMemoryStream(pdfium::span<const uint8_t> span);
+  ~CFX_ReadOnlyMemoryStream() override;
+
+  std::unique_ptr<uint8_t, FxFreeDeleter> m_data;
+  const pdfium::span<const uint8_t> m_span;
+};
+
+#endif  // CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_
diff --git a/core/fxcrt/cfx_seekablemultistream.cpp b/core/fxcrt/cfx_seekablemultistream.cpp
deleted file mode 100644
index 92286b1..0000000
--- a/core/fxcrt/cfx_seekablemultistream.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_seekablemultistream.h"
-
-#include <algorithm>
-
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
-
-CFX_SeekableMultiStream::CFX_SeekableMultiStream(
-    const std::vector<CPDF_Stream*>& streams) {
-  for (CPDF_Stream* pStream : streams) {
-    m_Data.push_back(pdfium::MakeRetain<CPDF_StreamAcc>(pStream));
-    m_Data.back()->LoadAllDataFiltered();
-  }
-}
-
-CFX_SeekableMultiStream::~CFX_SeekableMultiStream() {}
-
-FX_FILESIZE CFX_SeekableMultiStream::GetSize() {
-  uint32_t dwSize = 0;
-  for (const auto& acc : m_Data)
-    dwSize += acc->GetSize();
-  return dwSize;
-}
-
-bool CFX_SeekableMultiStream::ReadBlock(void* buffer,
-                                        FX_FILESIZE offset,
-                                        size_t size) {
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_Data);
-  int32_t index = 0;
-  while (index < iCount) {
-    const auto& acc = m_Data[index];
-    FX_FILESIZE dwSize = acc->GetSize();
-    if (offset < dwSize)
-      break;
-
-    offset -= dwSize;
-    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->GetData() + offset, dwRead);
-    size -= dwRead;
-    if (size == 0)
-      return true;
-
-    buffer = static_cast<uint8_t*>(buffer) + dwRead;
-    offset = 0;
-    index++;
-  }
-  return false;
-}
-
-size_t CFX_SeekableMultiStream::ReadBlock(void* buffer, size_t size) {
-  NOTREACHED();
-  return 0;
-}
-
-FX_FILESIZE CFX_SeekableMultiStream::GetPosition() {
-  return 0;
-}
-
-bool CFX_SeekableMultiStream::IsEOF() {
-  return false;
-}
-
-bool CFX_SeekableMultiStream::Flush() {
-  NOTREACHED();
-  return false;
-}
-
-bool CFX_SeekableMultiStream::WriteBlock(const void* pData,
-                                         FX_FILESIZE offset,
-                                         size_t size) {
-  NOTREACHED();
-  return false;
-}
diff --git a/core/fxcrt/cfx_seekablemultistream.h b/core/fxcrt/cfx_seekablemultistream.h
deleted file mode 100644
index 9138d7c..0000000
--- a/core/fxcrt/cfx_seekablemultistream.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_SEEKABLEMULTISTREAM_H_
-#define CORE_FXCRT_CFX_SEEKABLEMULTISTREAM_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CPDF_Stream;
-class CPDF_StreamAcc;
-
-class CFX_SeekableMultiStream : public IFX_SeekableStream {
- public:
-  explicit CFX_SeekableMultiStream(const std::vector<CPDF_Stream*>& streams);
-  ~CFX_SeekableMultiStream() override;
-
-  // IFX_SeekableReadStream
-  FX_FILESIZE GetPosition() override;
-  FX_FILESIZE GetSize() override;
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override;
-  size_t ReadBlock(void* buffer, size_t size) override;
-  bool IsEOF() override;
-  bool Flush() override;
-  bool WriteBlock(const void* pData, FX_FILESIZE offset, size_t size) override;
-
- private:
-  std::vector<RetainPtr<CPDF_StreamAcc>> m_Data;
-};
-
-#endif  // CORE_FXCRT_CFX_SEEKABLEMULTISTREAM_H_
diff --git a/core/fxcrt/cfx_seekablemultistream_unittest.cpp b/core/fxcrt/cfx_seekablemultistream_unittest.cpp
deleted file mode 100644
index 89213b1..0000000
--- a/core/fxcrt/cfx_seekablemultistream_unittest.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/cfx_seekablemultistream.h"
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxcrt/fx_memory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-#include "third_party/base/ptr_util.h"
-
-TEST(CFX_SeekableMultiStreamTest, NoStreams) {
-  std::vector<CPDF_Stream*> streams;
-  auto fileread = pdfium::MakeRetain<CFX_SeekableMultiStream>(streams);
-
-  uint8_t output_buffer[16];
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_FALSE(fileread->ReadBlock(output_buffer, 0, 0));
-  EXPECT_EQ(0xbd, output_buffer[0]);
-}
-
-TEST(CXFAFileReadTest, EmptyStreams) {
-  std::vector<CPDF_Stream*> streams;
-  auto stream1 = pdfium::MakeUnique<CPDF_Stream>();
-  streams.push_back(stream1.get());
-  auto fileread = pdfium::MakeRetain<CFX_SeekableMultiStream>(streams);
-
-  uint8_t output_buffer[16];
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_FALSE(fileread->ReadBlock(output_buffer, 0, 0));
-  EXPECT_EQ(0xbd, output_buffer[0]);
-}
-
-TEST(CXFAFileReadTest, NormalStreams) {
-  std::vector<CPDF_Stream*> streams;
-  auto stream1 = pdfium::MakeUnique<CPDF_Stream>();
-  auto stream2 = pdfium::MakeUnique<CPDF_Stream>();
-  auto stream3 = pdfium::MakeUnique<CPDF_Stream>();
-
-  // 16 chars total.
-  stream1->InitStream(reinterpret_cast<const uint8_t*>("one t"), 5,
-                      pdfium::MakeUnique<CPDF_Dictionary>());
-  stream2->InitStream(reinterpret_cast<const uint8_t*>("wo "), 3,
-                      pdfium::MakeUnique<CPDF_Dictionary>());
-  stream3->InitStream(reinterpret_cast<const uint8_t*>("three!!!"), 8,
-                      pdfium::MakeUnique<CPDF_Dictionary>());
-
-  streams.push_back(stream1.get());
-  streams.push_back(stream2.get());
-  streams.push_back(stream3.get());
-  auto fileread = pdfium::MakeRetain<CFX_SeekableMultiStream>(streams);
-
-  uint8_t output_buffer[16];
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlock(output_buffer, 0, 0));
-  EXPECT_EQ(0xbd, output_buffer[0]);
-
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlock(output_buffer, 1, 0));
-  EXPECT_EQ(0xbd, output_buffer[0]);
-
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlock(output_buffer, 0, 1));
-  EXPECT_EQ(0, memcmp(output_buffer, "o", 1));
-  EXPECT_EQ(0xbd, output_buffer[1]);
-
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlock(output_buffer, 0, sizeof(output_buffer)));
-  EXPECT_EQ(0, memcmp(output_buffer, "one two three!!!", 16));
-
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
-  EXPECT_TRUE(fileread->ReadBlock(output_buffer, 2, 10));
-  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->ReadBlock(output_buffer, 1, sizeof(output_buffer)));
-  EXPECT_EQ(0, memcmp(output_buffer, "ne two three!!!", 15));
-  EXPECT_EQ(0xbd, output_buffer[15]);
-}
diff --git a/core/fxcrt/cfx_seekablestreamproxy.cpp b/core/fxcrt/cfx_seekablestreamproxy.cpp
index 989c440..67b304f 100644
--- a/core/fxcrt/cfx_seekablestreamproxy.cpp
+++ b/core/fxcrt/cfx_seekablestreamproxy.cpp
@@ -6,9 +6,9 @@
 
 #include "core/fxcrt/cfx_seekablestreamproxy.h"
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
 #include <io.h>
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif
 
 #include <algorithm>
 #include <limits>
@@ -16,20 +16,23 @@
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/cfx_memorystream.h"
+#include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/stl_util.h"
 
 namespace {
 
-// Returns {src bytes consumed, dst bytes produced}.
+// Returns {src bytes consumed, dst chars produced}.
+// Invalid sequences are silently not output.
 std::pair<size_t, size_t> UTF8Decode(const char* pSrc,
                                      size_t srcLen,
                                      wchar_t* pDst,
                                      size_t dstLen) {
-  ASSERT(pDst && dstLen > 0);
+  ASSERT(pDst);
+  ASSERT(dstLen > 0);
 
   if (srcLen < 1)
     return {0, 0};
@@ -38,122 +41,89 @@
   int32_t iPending = 0;
   size_t iSrcNum = 0;
   size_t iDstNum = 0;
-  size_t iIndex = 0;
-  int32_t k = 1;
-  while (iIndex < srcLen) {
+  for (size_t iIndex = 0; iIndex < srcLen && iDstNum < dstLen; ++iIndex) {
+    ++iSrcNum;
     uint8_t byte = static_cast<uint8_t>(*(pSrc + iIndex));
     if (byte < 0x80) {
       iPending = 0;
-      k = 1;
-      iDstNum++;
-      iSrcNum += k;
+      ++iDstNum;
       *pDst++ = byte;
-      if (iDstNum >= dstLen)
-        break;
     } else if (byte < 0xc0) {
       if (iPending < 1)
-        break;
+        continue;
 
-      iPending--;
-      dwCode |= (byte & 0x3f) << (iPending * 6);
+      dwCode = dwCode << 6;
+      dwCode |= (byte & 0x3f);
+      --iPending;
       if (iPending == 0) {
-        iDstNum++;
-        iSrcNum += k;
+        ++iDstNum;
         *pDst++ = dwCode;
-        if (iDstNum >= dstLen)
-          break;
       }
     } else if (byte < 0xe0) {
       iPending = 1;
-      k = 2;
-      dwCode = (byte & 0x1f) << 6;
+      dwCode = (byte & 0x1f);
     } else if (byte < 0xf0) {
       iPending = 2;
-      k = 3;
-      dwCode = (byte & 0x0f) << 12;
+      dwCode = (byte & 0x0f);
     } else if (byte < 0xf8) {
       iPending = 3;
-      k = 4;
-      dwCode = (byte & 0x07) << 18;
+      dwCode = (byte & 0x07);
     } else if (byte < 0xfc) {
       iPending = 4;
-      k = 5;
-      dwCode = (byte & 0x03) << 24;
+      dwCode = (byte & 0x03);
     } else if (byte < 0xfe) {
       iPending = 5;
-      k = 6;
-      dwCode = (byte & 0x01) << 30;
-    } else {
-      break;
+      dwCode = (byte & 0x01);
     }
-    iIndex++;
   }
   return {iSrcNum, iDstNum};
 }
 
+#if defined(WCHAR_T_IS_UTF32)
+static_assert(sizeof(wchar_t) > 2, "wchar_t is too small");
+
 void UTF16ToWChar(void* pBuffer, size_t iLength) {
   ASSERT(pBuffer);
   ASSERT(iLength > 0);
-  ASSERT(sizeof(wchar_t) > 2);
 
   uint16_t* pSrc = static_cast<uint16_t*>(pBuffer);
   wchar_t* pDst = static_cast<wchar_t*>(pBuffer);
-  for (size_t i = 0; i < iLength; i++)
-    pDst[i] = static_cast<wchar_t>(pSrc[i]);
+
+  // Perform self-intersecting copy in reverse order.
+  for (size_t i = iLength; i > 0; --i)
+    pDst[i - 1] = static_cast<wchar_t>(pSrc[i - 1]);
 }
+#endif  // defined(WCHAR_T_IS_UTF32)
 
-void SwapByteOrder(wchar_t* pStr, size_t iLength) {
-  ASSERT(pStr);
-
-  uint16_t wch;
-  if (sizeof(wchar_t) > 2) {
-    while (iLength-- > 0) {
-      wch = static_cast<uint16_t>(*pStr);
-      wch = (wch >> 8) | (wch << 8);
-      wch &= 0x00FF;
-      *pStr = wch;
-      ++pStr;
-    }
-    return;
-  }
-
+void SwapByteOrder(uint16_t* pStr, size_t iLength) {
   while (iLength-- > 0) {
-    wch = static_cast<uint16_t>(*pStr);
-    wch = (wch >> 8) | (wch << 8);
-    *pStr = wch;
-    ++pStr;
+    uint16_t wch = *pStr;
+    *pStr++ = (wch >> 8) | (wch << 8);
   }
 }
 
 }  // namespace
 
-#define BOM_MASK 0x00FFFFFF
+#define BOM_UTF8_MASK 0x00FFFFFF
 #define BOM_UTF8 0x00BFBBEF
 #define BOM_UTF16_MASK 0x0000FFFF
 #define BOM_UTF16_BE 0x0000FFFE
 #define BOM_UTF16_LE 0x0000FEFF
 
 CFX_SeekableStreamProxy::CFX_SeekableStreamProxy(
-    const RetainPtr<IFX_SeekableStream>& stream,
-    bool isWriteStream)
-    : m_IsWriteStream(isWriteStream),
-      m_wCodePage(FX_CODEPAGE_DefANSI),
+    const RetainPtr<IFX_SeekableReadStream>& stream)
+    : m_wCodePage(FX_CODEPAGE_DefANSI),
       m_wBOMLength(0),
       m_iPosition(0),
       m_pStream(stream) {
   ASSERT(m_pStream);
 
-  if (isWriteStream) {
-    m_iPosition = m_pStream->GetSize();
-    return;
-  }
-
   Seek(From::Begin, 0);
 
   uint32_t bom = 0;
   ReadData(reinterpret_cast<uint8_t*>(&bom), 3);
 
-  bom &= BOM_MASK;
+  bom &= BOM_UTF8_MASK;
   if (bom == BOM_UTF8) {
     m_wBOMLength = 3;
     m_wCodePage = FX_CODEPAGE_UTF8;
@@ -174,12 +144,19 @@
   Seek(From::Begin, static_cast<FX_FILESIZE>(m_wBOMLength));
 }
 
-CFX_SeekableStreamProxy::CFX_SeekableStreamProxy(uint8_t* data, size_t size)
-    : CFX_SeekableStreamProxy(
-          pdfium::MakeRetain<CFX_MemoryStream>(data, size, false),
-          false) {}
+CFX_SeekableStreamProxy::~CFX_SeekableStreamProxy() = default;
 
-CFX_SeekableStreamProxy::~CFX_SeekableStreamProxy() {}
+FX_FILESIZE CFX_SeekableStreamProxy::GetSize() {
+  return m_pStream->GetSize();
+}
+
+FX_FILESIZE CFX_SeekableStreamProxy::GetPosition() {
+  return m_iPosition;
+}
+
+bool CFX_SeekableStreamProxy::IsEOF() {
+  return m_iPosition >= GetSize();
+}
 
 void CFX_SeekableStreamProxy::Seek(From eSeek, FX_FILESIZE iOffset) {
   switch (eSeek) {
@@ -187,14 +164,14 @@
       m_iPosition = iOffset;
       break;
     case From::Current: {
-      pdfium::base::CheckedNumeric<FX_FILESIZE> new_pos = m_iPosition;
+      FX_SAFE_FILESIZE new_pos = m_iPosition;
       new_pos += iOffset;
       m_iPosition =
           new_pos.ValueOrDefault(std::numeric_limits<FX_FILESIZE>::max());
     } break;
   }
   m_iPosition =
-      pdfium::clamp(m_iPosition, static_cast<FX_FILESIZE>(0), GetLength());
+      pdfium::clamp(m_iPosition, static_cast<FX_FILESIZE>(0), GetSize());
 }
 
 void CFX_SeekableStreamProxy::SetCodePage(uint16_t wCodePage) {
@@ -204,83 +181,59 @@
 }
 
 size_t CFX_SeekableStreamProxy::ReadData(uint8_t* pBuffer, size_t iBufferSize) {
-  ASSERT(pBuffer && iBufferSize > 0);
-
-  if (m_IsWriteStream)
-    return 0;
+  ASSERT(pBuffer);
+  ASSERT(iBufferSize > 0);
 
   iBufferSize =
-      std::min(iBufferSize, static_cast<size_t>(GetLength() - m_iPosition));
+      std::min(iBufferSize, static_cast<size_t>(GetSize() - m_iPosition));
   if (iBufferSize <= 0)
     return 0;
 
-  if (!m_pStream->ReadBlock(pBuffer, m_iPosition, iBufferSize))
+  if (!m_pStream->ReadBlockAtOffset(pBuffer, m_iPosition, iBufferSize))
     return 0;
 
-  pdfium::base::CheckedNumeric<FX_FILESIZE> new_pos = m_iPosition;
+  FX_SAFE_FILESIZE new_pos = m_iPosition;
   new_pos += iBufferSize;
   m_iPosition = new_pos.ValueOrDefault(m_iPosition);
   return new_pos.IsValid() ? iBufferSize : 0;
 }
 
-size_t CFX_SeekableStreamProxy::ReadString(wchar_t* pStr,
-                                           size_t iMaxLength,
-                                           bool* bEOS) {
-  if (!pStr || iMaxLength == 0)
-    return 0;
-
-  if (m_IsWriteStream)
+size_t CFX_SeekableStreamProxy::ReadBlock(wchar_t* pStr, size_t size) {
+  if (!pStr || size == 0)
     return 0;
 
   if (m_wCodePage == FX_CODEPAGE_UTF16LE ||
       m_wCodePage == FX_CODEPAGE_UTF16BE) {
-    size_t iBytes = iMaxLength * 2;
+    size_t iBytes = size * 2;
     size_t iLen = ReadData(reinterpret_cast<uint8_t*>(pStr), iBytes);
-    iMaxLength = iLen / 2;
-    if (sizeof(wchar_t) > 2 && iMaxLength > 0)
-      UTF16ToWChar(pStr, iMaxLength);
-
+    size = iLen / 2;
     if (m_wCodePage == FX_CODEPAGE_UTF16BE)
-      SwapByteOrder(pStr, iMaxLength);
+      SwapByteOrder(reinterpret_cast<uint16_t*>(pStr), size);
 
+#if defined(WCHAR_T_IS_UTF32)
+    if (size > 0)
+      UTF16ToWChar(pStr, size);
+#endif
   } else {
     FX_FILESIZE pos = GetPosition();
-    size_t iBytes =
-        std::min(iMaxLength, static_cast<size_t>(GetLength() - pos));
+    size_t iBytes = std::min(size, static_cast<size_t>(GetSize() - pos));
 
     if (iBytes > 0) {
-      std::vector<uint8_t> buf(iBytes);
+      std::vector<uint8_t, FxAllocAllocator<uint8_t>> buf(iBytes);
 
       size_t iLen = ReadData(buf.data(), iBytes);
       if (m_wCodePage != FX_CODEPAGE_UTF8)
         return 0;
 
       size_t iSrc = 0;
-      std::tie(iSrc, iMaxLength) = UTF8Decode(
-          reinterpret_cast<const char*>(buf.data()), iLen, pStr, iMaxLength);
+      std::tie(iSrc, size) =
+          UTF8Decode(reinterpret_cast<const char*>(buf.data()), iLen,
+                     static_cast<wchar_t*>(pStr), size);
       Seek(From::Current, iSrc - iLen);
     } else {
-      iMaxLength = 0;
+      size = 0;
     }
   }
 
-  *bEOS = IsEOF();
-  return iMaxLength;
-}
-
-void CFX_SeekableStreamProxy::WriteString(const WideStringView& str) {
-  if (!m_IsWriteStream || str.GetLength() == 0 ||
-      m_wCodePage != FX_CODEPAGE_UTF8) {
-    return;
-  }
-  if (!m_pStream->WriteBlock(str.unterminated_c_str(), m_iPosition,
-                             str.GetLength() * sizeof(wchar_t))) {
-    return;
-  }
-
-  pdfium::base::CheckedNumeric<FX_FILESIZE> new_pos = m_iPosition;
-  new_pos += str.GetLength() * sizeof(wchar_t);
-  m_iPosition = new_pos.ValueOrDefault(std::numeric_limits<FX_FILESIZE>::max());
-  m_iPosition =
-      pdfium::clamp(m_iPosition, static_cast<FX_FILESIZE>(0), GetLength());
+  return size;
 }
diff --git a/core/fxcrt/cfx_seekablestreamproxy.h b/core/fxcrt/cfx_seekablestreamproxy.h
index c25ab3c..d6ea160 100644
--- a/core/fxcrt/cfx_seekablestreamproxy.h
+++ b/core/fxcrt/cfx_seekablestreamproxy.h
@@ -7,48 +7,42 @@
 #ifndef CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_
 #define CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_
 
-#include <algorithm>
-
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CFX_SeekableStreamProxy : public Retainable {
+class CFX_SeekableStreamProxy final : public Retainable {
  public:
-  enum class From {
-    Begin = 0,
-    Current,
-  };
-
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  FX_FILESIZE GetLength() const { return m_pStream->GetSize(); }
-  FX_FILESIZE GetPosition() { return m_iPosition; }
-  size_t GetBOMLength() const { return m_wBOMLength; }
-  bool IsEOF() const { return m_iPosition >= GetLength(); }
-
-  void Seek(From eSeek, FX_FILESIZE iOffset);
-  size_t ReadString(wchar_t* pStr, size_t iMaxLength, bool* bEOS);
-
-  void WriteString(const WideStringView& str);
+  // Unlike IFX_SeekableStreamProxy, buffers and sizes are always in terms
+  // of the number of wchar_t elementss, not bytes.
+  FX_FILESIZE GetSize();  // Estimate under worst possible expansion.
+  bool IsEOF();
+  size_t ReadBlock(wchar_t* pStr, size_t size);
 
   uint16_t GetCodePage() const { return m_wCodePage; }
   void SetCodePage(uint16_t wCodePage);
 
  private:
-  CFX_SeekableStreamProxy(const RetainPtr<IFX_SeekableStream>& stream,
-                          bool isWriteSteam);
-  CFX_SeekableStreamProxy(uint8_t* data, size_t size);
+  enum class From {
+    Begin = 0,
+    Current,
+  };
+
+  explicit CFX_SeekableStreamProxy(
+      const RetainPtr<IFX_SeekableReadStream>& stream);
   ~CFX_SeekableStreamProxy() override;
 
+  FX_FILESIZE GetPosition();
+  void Seek(From eSeek, FX_FILESIZE iOffset);
   size_t ReadData(uint8_t* pBuffer, size_t iBufferSize);
 
-  bool m_IsWriteStream;
   uint16_t m_wCodePage;
   size_t m_wBOMLength;
   FX_FILESIZE m_iPosition;
-  RetainPtr<IFX_SeekableStream> m_pStream;
+  RetainPtr<IFX_SeekableReadStream> m_pStream;
 };
 
 #endif  // CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_
diff --git a/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
new file mode 100644
index 0000000..b263fa8
--- /dev/null
+++ b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
@@ -0,0 +1,70 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_seekablestreamproxy.h"
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
+
+TEST(SeekableStreamProxyTest, NullStream) {
+  auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+          pdfium::make_span<const uint8_t>(nullptr, 0)));
+
+  wchar_t buffer[16];
+  EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+}
+
+TEST(SeekableStreamProxyTest, DefaultStreamBOMNotRecognized) {
+  const char data[] = "abcd";
+  auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+          reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
+
+  wchar_t buffer[16];
+  EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+}
+
+TEST(SeekableStreamProxyTest, UTF8Stream) {
+  const char data[] = "\xEF\xBB\xBF*\xC2\xA2*";
+  auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+          reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
+
+  wchar_t buffer[16];
+  EXPECT_EQ(3u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(L'*', buffer[0]);
+  EXPECT_EQ(L'\u00A2', buffer[1]);
+  EXPECT_EQ(L'*', buffer[2]);
+}
+
+TEST(SeekableStreamProxyTest, UTF16LEStream) {
+  const char data[] = "\xFF\xFE\x41\x00\x42\x01";
+  auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+          reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
+
+  wchar_t buffer[16];
+  EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(L'A', buffer[0]);
+  EXPECT_EQ(L'\u0142', buffer[1]);
+}
+
+TEST(SeekableStreamProxyTest, UTF16BEStream) {
+  const char data[] = "\xFE\xFF\x00\x41\x01\x42";
+  auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::make_span(
+          reinterpret_cast<const uint8_t*>(data), sizeof(data) - 1)));
+
+  wchar_t buffer[16];
+  EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer)));
+  EXPECT_EQ(L'A', buffer[0]);
+  EXPECT_EQ(L'\u0142', buffer[1]);
+}
diff --git a/core/fxcrt/cfx_timer.cpp b/core/fxcrt/cfx_timer.cpp
new file mode 100644
index 0000000..6ffb625
--- /dev/null
+++ b/core/fxcrt/cfx_timer.cpp
@@ -0,0 +1,46 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/cfx_timer.h"
+
+#include <map>
+
+#include "third_party/base/no_destructor.h"
+
+namespace {
+
+using TimerMap = std::map<int32_t, CFX_Timer*>;
+TimerMap& GetPWLTimerMap() {
+  static pdfium::base::NoDestructor<TimerMap> timer_map;
+  return *timer_map;
+}
+
+}  // namespace
+
+CFX_Timer::CFX_Timer(TimerHandlerIface* pTimerHandler,
+                     CallbackIface* pCallbackIface,
+                     int32_t nInterval)
+    : m_nTimerID(pTimerHandler->SetTimer(nInterval, TimerProc)),
+      m_pTimerHandler(pTimerHandler),
+      m_pCallbackIface(pCallbackIface) {
+  ASSERT(m_pCallbackIface);
+  if (HasValidID())
+    GetPWLTimerMap()[m_nTimerID] = this;
+}
+
+CFX_Timer::~CFX_Timer() {
+  if (HasValidID()) {
+    m_pTimerHandler->KillTimer(m_nTimerID);
+    GetPWLTimerMap().erase(m_nTimerID);
+  }
+}
+
+// static
+void CFX_Timer::TimerProc(int32_t idEvent) {
+  auto it = GetPWLTimerMap().find(idEvent);
+  if (it != GetPWLTimerMap().end())
+    it->second->m_pCallbackIface->OnTimerFired();
+}
diff --git a/core/fxcrt/cfx_timer.h b/core/fxcrt/cfx_timer.h
new file mode 100644
index 0000000..fa97dda
--- /dev/null
+++ b/core/fxcrt/cfx_timer.h
@@ -0,0 +1,40 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_CFX_TIMER_H_
+#define CORE_FXCRT_CFX_TIMER_H_
+
+#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CFX_TimerHandler;
+
+class CFX_Timer {
+ public:
+  class CallbackIface {
+   public:
+    virtual ~CallbackIface() = default;
+    virtual void OnTimerFired() = 0;
+  };
+
+  CFX_Timer(TimerHandlerIface* pTimerHandler,
+            CallbackIface* pCallbackIface,
+            int32_t nInterval);
+  ~CFX_Timer();
+
+  bool HasValidID() const {
+    return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
+  }
+
+ private:
+  static void TimerProc(int32_t idEvent);
+
+  const int32_t m_nTimerID;
+  UnownedPtr<TimerHandlerIface> const m_pTimerHandler;
+  UnownedPtr<CallbackIface> const m_pCallbackIface;
+};
+
+#endif  // CORE_FXCRT_CFX_TIMER_H_
diff --git a/core/fxcrt/cfx_timer_unittest.cpp b/core/fxcrt/cfx_timer_unittest.cpp
new file mode 100644
index 0000000..b95de72
--- /dev/null
+++ b/core/fxcrt/cfx_timer_unittest.cpp
@@ -0,0 +1,85 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_timer.h"
+
+#include <memory>
+
+#include "core/fxcrt/timerhandler_iface.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+
+class MockTimerScheduler : public TimerHandlerIface {
+ public:
+  MOCK_METHOD2(SetTimer, int(int32_t uElapse, TimerCallback lpTimerFunc));
+  MOCK_METHOD1(KillTimer, void(int32_t nID));
+};
+
+class MockTimerCallback : public CFX_Timer::CallbackIface {
+ public:
+  MOCK_METHOD0(OnTimerFired, void());
+};
+
+TEST(CFX_Timer, ValidTimers) {
+  TimerHandlerIface::TimerCallback fn1 = nullptr;
+  TimerHandlerIface::TimerCallback fn2 = nullptr;
+
+  MockTimerScheduler scheduler;
+  EXPECT_CALL(scheduler, SetTimer(100, _))
+      .WillOnce(DoAll(SaveArg<1>(&fn1), Return(1001)));
+  EXPECT_CALL(scheduler, SetTimer(200, _))
+      .WillOnce(DoAll(SaveArg<1>(&fn2), Return(1002)));
+  EXPECT_CALL(scheduler, KillTimer(1001));
+  EXPECT_CALL(scheduler, KillTimer(1002));
+
+  MockTimerCallback cb1;
+  EXPECT_CALL(cb1, OnTimerFired()).Times(1);
+
+  MockTimerCallback cb2;
+  EXPECT_CALL(cb2, OnTimerFired()).Times(2);
+
+  auto timer1 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb1, 100);
+  auto timer2 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb2, 200);
+  EXPECT_TRUE(timer1->HasValidID());
+  EXPECT_TRUE(timer2->HasValidID());
+
+  // Fire some timers.
+  ASSERT_TRUE(fn1);
+  ASSERT_TRUE(fn2);
+  (*fn1)(1001);
+  (*fn1)(1002);
+  (*fn1)(1002);
+}
+
+TEST(CFX_Timer, MisbehavingEmbedder) {
+  TimerHandlerIface::TimerCallback fn1 = nullptr;
+
+  MockTimerScheduler scheduler;
+  EXPECT_CALL(scheduler, SetTimer(100, _))
+      .WillOnce(DoAll(SaveArg<1>(&fn1), Return(1001)));
+  EXPECT_CALL(scheduler, KillTimer(1001));
+
+  MockTimerCallback cb1;
+  EXPECT_CALL(cb1, OnTimerFired()).Times(0);
+
+  {
+    auto timer1 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb1, 100);
+    EXPECT_TRUE(timer1->HasValidID());
+
+    // Fire callback with bad arguments.
+    ASSERT_TRUE(fn1);
+    (*fn1)(-1);
+    (*fn1)(0);
+    (*fn1)(1002);
+  }
+
+  // Fire callback against stale timer.
+  (*fn1)(1001);
+}
diff --git a/core/fxcrt/cfx_utf8decoder.cpp b/core/fxcrt/cfx_utf8decoder.cpp
index bee5e16..68ea30a 100644
--- a/core/fxcrt/cfx_utf8decoder.cpp
+++ b/core/fxcrt/cfx_utf8decoder.cpp
@@ -6,10 +6,9 @@
 
 #include "core/fxcrt/cfx_utf8decoder.h"
 
-void CFX_UTF8Decoder::Clear() {
-  m_Buffer.Clear();
-  m_PendingBytes = 0;
-}
+CFX_UTF8Decoder::CFX_UTF8Decoder() = default;
+
+CFX_UTF8Decoder::~CFX_UTF8Decoder() = default;
 
 void CFX_UTF8Decoder::AppendCodePoint(uint32_t ch) {
   m_Buffer.AppendChar(static_cast<wchar_t>(ch));
@@ -43,5 +42,7 @@
   } else if (byte < 0xfe) {
     m_PendingBytes = 5;
     m_PendingChar = (byte & 0x01) << 30;
+  } else {
+    m_PendingBytes = 0;
   }
 }
diff --git a/core/fxcrt/cfx_utf8decoder.h b/core/fxcrt/cfx_utf8decoder.h
index 1cafbe4..a236aac 100644
--- a/core/fxcrt/cfx_utf8decoder.h
+++ b/core/fxcrt/cfx_utf8decoder.h
@@ -11,17 +11,17 @@
 
 class CFX_UTF8Decoder {
  public:
-  CFX_UTF8Decoder() { m_PendingBytes = 0; }
+  CFX_UTF8Decoder();
+  ~CFX_UTF8Decoder();
 
-  void Clear();
   void Input(uint8_t byte);
   void AppendCodePoint(uint32_t ch);
   void ClearStatus() { m_PendingBytes = 0; }
   WideStringView GetResult() const { return m_Buffer.AsStringView(); }
 
  private:
-  int m_PendingBytes;
-  uint32_t m_PendingChar;
+  int m_PendingBytes = 0;
+  uint32_t m_PendingChar = 0;
   CFX_WideTextBuf m_Buffer;
 };
 
diff --git a/core/fxcrt/cfx_utf8encoder.cpp b/core/fxcrt/cfx_utf8encoder.cpp
new file mode 100644
index 0000000..9ed149f
--- /dev/null
+++ b/core/fxcrt/cfx_utf8encoder.cpp
@@ -0,0 +1,43 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/cfx_utf8encoder.h"
+
+CFX_UTF8Encoder::CFX_UTF8Encoder() = default;
+
+CFX_UTF8Encoder::~CFX_UTF8Encoder() = default;
+
+void CFX_UTF8Encoder::Input(wchar_t unicodeAsWchar) {
+  uint32_t unicode = static_cast<uint32_t>(unicodeAsWchar);
+  if (unicode < 0x80) {
+    m_Buffer.push_back(unicode);
+  } else {
+    if (unicode >= 0x80000000)
+      return;
+
+    int nbytes = 0;
+    if (unicode < 0x800)
+      nbytes = 2;
+    else if (unicode < 0x10000)
+      nbytes = 3;
+    else if (unicode < 0x200000)
+      nbytes = 4;
+    else if (unicode < 0x4000000)
+      nbytes = 5;
+    else
+      nbytes = 6;
+
+    static const uint8_t prefix[] = {0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
+    int order = 1 << ((nbytes - 1) * 6);
+    int code = unicodeAsWchar;
+    m_Buffer.push_back(prefix[nbytes - 2] | (code / order));
+    for (int i = 0; i < nbytes - 1; i++) {
+      code = code % order;
+      order >>= 6;
+      m_Buffer.push_back(0x80 | (code / order));
+    }
+  }
+}
diff --git a/core/fxcrt/cfx_utf8encoder.h b/core/fxcrt/cfx_utf8encoder.h
new file mode 100644
index 0000000..20afe4c
--- /dev/null
+++ b/core/fxcrt/cfx_utf8encoder.h
@@ -0,0 +1,32 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_CFX_UTF8ENCODER_H_
+#define CORE_FXCRT_CFX_UTF8ENCODER_H_
+
+#include <vector>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_string.h"
+
+class CFX_UTF8Encoder {
+ public:
+  CFX_UTF8Encoder();
+  ~CFX_UTF8Encoder();
+
+  void Input(wchar_t unicodeAsWchar);
+
+  // The data returned by GetResult() is invalidated when this is modified by
+  // appending any data.
+  ByteStringView GetResult() const {
+    return ByteStringView(m_Buffer.data(), m_Buffer.size());
+  }
+
+ private:
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_Buffer;
+};
+
+#endif  // CORE_FXCRT_CFX_UTF8ENCODER_H_
diff --git a/core/fxcrt/cfx_widetextbuf.cpp b/core/fxcrt/cfx_widetextbuf.cpp
index a9b59b5..dea620c 100644
--- a/core/fxcrt/cfx_widetextbuf.cpp
+++ b/core/fxcrt/cfx_widetextbuf.cpp
@@ -12,11 +12,20 @@
 
 void CFX_WideTextBuf::AppendChar(wchar_t ch) {
   ExpandBuf(sizeof(wchar_t));
-  *(wchar_t*)(m_pBuffer.get() + m_DataSize) = ch;
+  *reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize) = ch;
   m_DataSize += sizeof(wchar_t);
 }
 
-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(const WideStringView& str) {
+CFX_WideTextBuf& CFX_WideTextBuf::operator<<(ByteStringView ascii) {
+  ExpandBuf(ascii.GetLength() * sizeof(wchar_t));
+  for (uint8_t ch : ascii) {
+    *reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize) = ch;
+    m_DataSize += sizeof(wchar_t);
+  }
+  return *this;
+}
+
+CFX_WideTextBuf& CFX_WideTextBuf::operator<<(WideStringView str) {
   AppendBlock(str.unterminated_c_str(), str.GetLength() * sizeof(wchar_t));
   return *this;
 }
@@ -31,7 +40,7 @@
   FXSYS_itoa(i, buf, 10);
   size_t len = strlen(buf);
   ExpandBuf(len * sizeof(wchar_t));
-  wchar_t* str = (wchar_t*)(m_pBuffer.get() + m_DataSize);
+  wchar_t* str = reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize);
   for (size_t j = 0; j < len; j++) {
     *str++ = buf[j];
   }
@@ -41,9 +50,9 @@
 
 CFX_WideTextBuf& CFX_WideTextBuf::operator<<(double f) {
   char buf[32];
-  size_t len = FX_ftoa((float)f, buf);
+  size_t len = FloatToString((float)f, buf);
   ExpandBuf(len * sizeof(wchar_t));
-  wchar_t* str = (wchar_t*)(m_pBuffer.get() + m_DataSize);
+  wchar_t* str = reinterpret_cast<wchar_t*>(m_pBuffer.get() + m_DataSize);
   for (size_t i = 0; i < len; i++) {
     *str++ = buf[i];
   }
diff --git a/core/fxcrt/cfx_widetextbuf.h b/core/fxcrt/cfx_widetextbuf.h
index 84553a3..0f89197 100644
--- a/core/fxcrt/cfx_widetextbuf.h
+++ b/core/fxcrt/cfx_widetextbuf.h
@@ -11,7 +11,7 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 
-class CFX_WideTextBuf : public CFX_BinaryBuf {
+class CFX_WideTextBuf final : public CFX_BinaryBuf {
  public:
   void AppendChar(wchar_t wch);
   size_t GetLength() const override;
@@ -35,8 +35,9 @@
 
   CFX_WideTextBuf& operator<<(int i);
   CFX_WideTextBuf& operator<<(double f);
+  CFX_WideTextBuf& operator<<(ByteStringView ascii);
   CFX_WideTextBuf& operator<<(const wchar_t* lpsz);
-  CFX_WideTextBuf& operator<<(const WideStringView& str);
+  CFX_WideTextBuf& operator<<(WideStringView str);
   CFX_WideTextBuf& operator<<(const WideString& str);
   CFX_WideTextBuf& operator<<(const CFX_WideTextBuf& buf);
 };
diff --git a/core/fxcrt/cfx_widetextbuf_unittest.cpp b/core/fxcrt/cfx_widetextbuf_unittest.cpp
new file mode 100644
index 0000000..ddca23f
--- /dev/null
+++ b/core/fxcrt/cfx_widetextbuf_unittest.cpp
@@ -0,0 +1,45 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/cfx_widetextbuf.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fxcrt {
+
+TEST(WideTextBuf, EmptyBuf) {
+  CFX_WideTextBuf wtb;
+  EXPECT_EQ(nullptr, wtb.GetBuffer());
+  EXPECT_TRUE(wtb.AsStringView().IsEmpty());
+  EXPECT_TRUE(wtb.MakeString().IsEmpty());
+}
+
+TEST(WideTextBuf, OperatorLtLt) {
+  CFX_WideTextBuf wtb;
+  wtb << 42 << 3.14 << "clams" << L"\u208c\u208e";
+  EXPECT_TRUE(wtb.MakeString() == L"423.14clams\u208c\u208e");
+}
+
+TEST(WideTextBuf, Deletion) {
+  CFX_WideTextBuf wtb;
+  wtb << L"ABCDEFG";
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("ABCDEFG"));
+
+  wtb.Delete(1, 3);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG"));
+
+  wtb.Delete(1, 0);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG"));
+
+  wtb.Delete(0, 2);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII("FG"));
+
+  wtb.Delete(0, 2);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII(""));
+
+  wtb.Delete(0, 0);
+  EXPECT_TRUE(wtb.AsStringView().EqualsASCII(""));
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/css/BUILD.gn b/core/fxcrt/css/BUILD.gn
new file mode 100644
index 0000000..66090c1
--- /dev/null
+++ b/core/fxcrt/css/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("css") {
+  sources = [
+    "cfx_css.h",
+    "cfx_csscolorvalue.cpp",
+    "cfx_csscolorvalue.h",
+    "cfx_csscomputedstyle.cpp",
+    "cfx_csscomputedstyle.h",
+    "cfx_csscustomproperty.cpp",
+    "cfx_csscustomproperty.h",
+    "cfx_cssdata.cpp",
+    "cfx_cssdata.h",
+    "cfx_cssdeclaration.cpp",
+    "cfx_cssdeclaration.h",
+    "cfx_cssenumvalue.cpp",
+    "cfx_cssenumvalue.h",
+    "cfx_cssexttextbuf.cpp",
+    "cfx_cssexttextbuf.h",
+    "cfx_cssnumbervalue.cpp",
+    "cfx_cssnumbervalue.h",
+    "cfx_csspropertyholder.cpp",
+    "cfx_csspropertyholder.h",
+    "cfx_cssrulecollection.cpp",
+    "cfx_cssrulecollection.h",
+    "cfx_cssselector.cpp",
+    "cfx_cssselector.h",
+    "cfx_cssstringvalue.cpp",
+    "cfx_cssstringvalue.h",
+    "cfx_cssstylerule.cpp",
+    "cfx_cssstylerule.h",
+    "cfx_cssstyleselector.cpp",
+    "cfx_cssstyleselector.h",
+    "cfx_cssstylesheet.cpp",
+    "cfx_cssstylesheet.h",
+    "cfx_csssyntaxparser.cpp",
+    "cfx_csssyntaxparser.h",
+    "cfx_csstextbuf.cpp",
+    "cfx_csstextbuf.h",
+    "cfx_cssvalue.cpp",
+    "cfx_cssvalue.h",
+    "cfx_cssvaluelist.cpp",
+    "cfx_cssvaluelist.h",
+    "cfx_cssvaluelistparser.cpp",
+    "cfx_cssvaluelistparser.h",
+  ]
+  deps = [
+    "../",
+    "../../fxge",
+  ]
+  configs += [ "../../../:pdfium_core_config" ]
+  visibility = [ "../../../*" ]
+}
diff --git a/core/fxcrt/css/cfx_css.h b/core/fxcrt/css/cfx_css.h
index 7b1d7d3..30d9ff8 100644
--- a/core/fxcrt/css/cfx_css.h
+++ b/core/fxcrt/css/cfx_css.h
@@ -7,8 +7,7 @@
 #ifndef CORE_FXCRT_CSS_CFX_CSS_H_
 #define CORE_FXCRT_CSS_CFX_CSS_H_
 
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxge/fx_dib.h"
+#include <stdint.h>
 
 enum CFX_CSSVALUETYPE {
   CFX_CSSVALUETYPE_Primitive = 1 << 0,
@@ -31,6 +30,8 @@
   List,
 };
 
+// Any entries added/removed here, will need to be mirrored in
+// propertyValueTable, in core/fxcrt/css/cfx_cssdata.cpp.
 enum class CFX_CSSPropertyValue : uint8_t {
   Bolder = 0,
   None,
@@ -76,9 +77,10 @@
   Large,
   Left,
   TextTop,
-  LAST_MARKER
 };
 
+// Any entries added/removed here, will need to be mirrored in
+// propertyTable, in core/fxcrt/css/cfx_cssdata.cpp.
 enum class CFX_CSSProperty : uint8_t {
   BorderLeft = 0,
   Top,
@@ -120,7 +122,6 @@
   Padding,
   MarginBottom,
   MarginTop,
-  LAST_MARKER
 };
 
 enum class CFX_CSSSelectorType : uint8_t { Element = 0, Descendant };
@@ -185,8 +186,6 @@
  public:
   CFX_CSSLength() {}
 
-  explicit CFX_CSSLength(CFX_CSSLengthUnit eUnit) : m_unit(eUnit) {}
-
   CFX_CSSLength(CFX_CSSLengthUnit eUnit, float fValue)
       : m_unit(eUnit), m_fValue(fValue) {}
 
@@ -236,7 +235,10 @@
     return *this;
   }
 
-  CFX_CSSLength left, top, right, bottom;
+  CFX_CSSLength left;
+  CFX_CSSLength top;
+  CFX_CSSLength right;
+  CFX_CSSLength bottom;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSS_H_
diff --git a/core/fxcrt/css/cfx_csscolorvalue.h b/core/fxcrt/css/cfx_csscolorvalue.h
index f59c0c5..fdc3fcb 100644
--- a/core/fxcrt/css/cfx_csscolorvalue.h
+++ b/core/fxcrt/css/cfx_csscolorvalue.h
@@ -8,8 +8,9 @@
 #define CORE_FXCRT_CSS_CFX_CSSCOLORVALUE_H_
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
+#include "core/fxge/fx_dib.h"
 
-class CFX_CSSColorValue : public CFX_CSSValue {
+class CFX_CSSColorValue final : public CFX_CSSValue {
  public:
   explicit CFX_CSSColorValue(FX_ARGB color);
   ~CFX_CSSColorValue() override;
diff --git a/core/fxcrt/css/cfx_csscomputedstyle.cpp b/core/fxcrt/css/cfx_csscomputedstyle.cpp
index aae88a3..11ae901 100644
--- a/core/fxcrt/css/cfx_csscomputedstyle.cpp
+++ b/core/fxcrt/css/cfx_csscomputedstyle.cpp
@@ -8,18 +8,17 @@
 
 #include "core/fxcrt/css/cfx_cssstringvalue.h"
 #include "core/fxcrt/css/cfx_cssvaluelist.h"
-#include "third_party/base/stl_util.h"
 
-CFX_CSSComputedStyle::CFX_CSSComputedStyle() {}
+CFX_CSSComputedStyle::CFX_CSSComputedStyle() = default;
 
-CFX_CSSComputedStyle::~CFX_CSSComputedStyle() {}
+CFX_CSSComputedStyle::~CFX_CSSComputedStyle() = default;
 
 bool CFX_CSSComputedStyle::GetCustomStyle(const WideString& wsName,
-                                          WideString& wsValue) const {
+                                          WideString* pValue) const {
   for (auto iter = m_CustomProperties.rbegin();
-       iter != m_CustomProperties.rend(); iter++) {
+       iter != m_CustomProperties.rend(); ++iter) {
     if (wsName == iter->name()) {
-      wsValue = iter->value();
+      *pValue = iter->value();
       return true;
     }
   }
@@ -168,8 +167,8 @@
 }
 
 CFX_CSSComputedStyle::InheritedData::InheritedData()
-    : m_LetterSpacing(CFX_CSSLengthUnit::Normal),
-      m_WordSpacing(CFX_CSSLengthUnit::Normal),
+    : m_LetterSpacing(CFX_CSSLengthUnit::Normal, 0),
+      m_WordSpacing(CFX_CSSLengthUnit::Normal, 0),
       m_TextIndent(CFX_CSSLengthUnit::Point, 0),
       m_pFontFamily(nullptr),
       m_fFontSize(12.0f),
diff --git a/core/fxcrt/css/cfx_csscomputedstyle.h b/core/fxcrt/css/cfx_csscomputedstyle.h
index 4b44a6d..d4959ee 100644
--- a/core/fxcrt/css/cfx_csscomputedstyle.h
+++ b/core/fxcrt/css/cfx_csscomputedstyle.h
@@ -12,10 +12,11 @@
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/css/cfx_csscustomproperty.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxge/fx_dib.h"
 
 class CFX_CSSValueList;
 
-class CFX_CSSComputedStyle : public Retainable {
+class CFX_CSSComputedStyle final : public Retainable {
  public:
   class InheritedData {
    public:
@@ -55,6 +56,9 @@
     bool m_bHasPadding;
   };
 
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
   int32_t CountFontFamilies() const;
   const WideString GetFontFamily(int32_t index) const;
   uint16_t GetFontWeight() const;
@@ -91,15 +95,12 @@
   void SetLetterSpacing(const CFX_CSSLength& letterSpacing);
   void AddCustomStyle(const CFX_CSSCustomProperty& prop);
 
-  bool GetCustomStyle(const WideString& wsName, WideString& wsValue) const;
+  bool GetCustomStyle(const WideString& wsName, WideString* pValue) const;
 
   InheritedData m_InheritedData;
   NonInheritedData m_NonInheritedData;
 
  private:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
   CFX_CSSComputedStyle();
   ~CFX_CSSComputedStyle() override;
 
diff --git a/core/fxcrt/css/cfx_cssdata.cpp b/core/fxcrt/css/cfx_cssdata.cpp
new file mode 100644
index 0000000..cfef220
--- /dev/null
+++ b/core/fxcrt/css/cfx_cssdata.cpp
@@ -0,0 +1,347 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/css/cfx_cssdata.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "core/fxcrt/css/cfx_cssstyleselector.h"
+#include "core/fxcrt/css/cfx_cssvaluelistparser.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+
+namespace {
+
+#undef PROP
+#define PROP(a, b, c, d) a, c, d
+
+const CFX_CSSData::Property propertyTable[] = {
+    {PROP(CFX_CSSProperty::BorderLeft,
+          "border-left",
+          0x04080036,
+          CFX_CSSVALUETYPE_Shorthand)},
+    {PROP(CFX_CSSProperty::Top,
+          "top",
+          0x0BEDAF33,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::Margin,
+          "margin",
+          0x0CB016BE,
+          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::TextIndent,
+          "text-indent",
+          0x169ADB74,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::Right,
+          "right",
+          0x193ADE3E,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::PaddingLeft,
+          "padding-left",
+          0x228CF02F,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::MarginLeft,
+          "margin-left",
+          0x297C5656,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+              CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::Border,
+          "border",
+          0x2A23349E,
+          CFX_CSSVALUETYPE_Shorthand)},
+    {PROP(CFX_CSSProperty::BorderTop,
+          "border-top",
+          0x2B866ADE,
+          CFX_CSSVALUETYPE_Shorthand)},
+    {PROP(CFX_CSSProperty::Bottom,
+          "bottom",
+          0x399F02B5,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::PaddingRight,
+          "padding-right",
+          0x3F616AC2,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::BorderBottom,
+          "border-bottom",
+          0x452CE780,
+          CFX_CSSVALUETYPE_Shorthand)},
+    {PROP(CFX_CSSProperty::FontFamily,
+          "font-family",
+          0x574686E6,
+          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeString)},
+    {PROP(CFX_CSSProperty::FontWeight,
+          "font-weight",
+          0x6692F60C,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::Color,
+          "color",
+          0x6E67921F,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeColor)},
+    {PROP(CFX_CSSProperty::LetterSpacing,
+          "letter-spacing",
+          0x70536102,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::TextAlign,
+          "text-align",
+          0x7553F1BD,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::BorderRightWidth,
+          "border-right-width",
+          0x8F5A6036,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::VerticalAlign,
+          "vertical-align",
+          0x934A87D2,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::PaddingTop,
+          "padding-top",
+          0x959D22B7,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::FontVariant,
+          "font-variant",
+          0x9C785779,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::BorderWidth,
+          "border-width",
+          0xA8DE4FEB,
+          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::BorderBottomWidth,
+          "border-bottom-width",
+          0xAE41204D,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::BorderRight,
+          "border-right",
+          0xB78E9EA9,
+          CFX_CSSVALUETYPE_Shorthand)},
+    {PROP(CFX_CSSProperty::FontSize,
+          "font-size",
+          0xB93956DF,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::BorderSpacing,
+          "border-spacing",
+          0xC72030F0,
+          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::FontStyle,
+          "font-style",
+          0xCB1950F5,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::Font,
+          "font",
+          0xCD308B77,
+          CFX_CSSVALUETYPE_Shorthand)},
+    {PROP(CFX_CSSProperty::LineHeight,
+          "line-height",
+          0xCFCACE2E,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::MarginRight,
+          "margin-right",
+          0xD13C58C9,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+              CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::BorderLeftWidth,
+          "border-left-width",
+          0xD1E93D83,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::Display,
+          "display",
+          0xD4224C36,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::PaddingBottom,
+          "padding-bottom",
+          0xE555B3B9,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::BorderTopWidth,
+          "border-top-width",
+          0xED2CB62B,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::WordSpacing,
+          "word-spacing",
+          0xEDA63BAE,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::Left,
+          "left",
+          0xF5AD782B,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
+              CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::TextDecoration,
+          "text-decoration",
+          0xF7C634BA,
+          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::Padding,
+          "padding",
+          0xF8C373F7,
+          CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)},
+    {PROP(CFX_CSSProperty::MarginBottom,
+          "margin-bottom",
+          0xF93485A0,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+              CFX_CSSVALUETYPE_MaybeEnum)},
+    {PROP(CFX_CSSProperty::MarginTop,
+          "margin-top",
+          0xFE51DCFE,
+          CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
+              CFX_CSSVALUETYPE_MaybeEnum)},
+};
+
+#undef PROP
+
+#undef PVAL
+#define PVAL(a, b, c) a, c
+
+const CFX_CSSData::PropertyValue propertyValueTable[] = {
+    {PVAL(CFX_CSSPropertyValue::Bolder, "bolder", 0x009F1058)},
+    {PVAL(CFX_CSSPropertyValue::None, "none", 0x048B6670)},
+    {PVAL(CFX_CSSPropertyValue::Dot, "dot", 0x0A48CB27)},
+    {PVAL(CFX_CSSPropertyValue::Sub, "sub", 0x0BD37FAA)},
+    {PVAL(CFX_CSSPropertyValue::Top, "top", 0x0BEDAF33)},
+    {PVAL(CFX_CSSPropertyValue::Right, "right", 0x193ADE3E)},
+    {PVAL(CFX_CSSPropertyValue::Normal, "normal", 0x247CF3E9)},
+    {PVAL(CFX_CSSPropertyValue::Auto, "auto", 0x2B35B6D9)},
+    {PVAL(CFX_CSSPropertyValue::Text, "text", 0x2D08AF85)},
+    {PVAL(CFX_CSSPropertyValue::XSmall, "x-small", 0x2D2FCAFE)},
+    {PVAL(CFX_CSSPropertyValue::Thin, "thin", 0x2D574D53)},
+    {PVAL(CFX_CSSPropertyValue::Small, "small", 0x316A3739)},
+    {PVAL(CFX_CSSPropertyValue::Bottom, "bottom", 0x399F02B5)},
+    {PVAL(CFX_CSSPropertyValue::Underline, "underline", 0x3A0273A6)},
+    {PVAL(CFX_CSSPropertyValue::Double, "double", 0x3D98515B)},
+    {PVAL(CFX_CSSPropertyValue::Lighter, "lighter", 0x45BEB7AF)},
+    {PVAL(CFX_CSSPropertyValue::Oblique, "oblique", 0x53EBDDB1)},
+    {PVAL(CFX_CSSPropertyValue::Super, "super", 0x6A4F842F)},
+    {PVAL(CFX_CSSPropertyValue::Center, "center", 0x6C51AFC1)},
+    {PVAL(CFX_CSSPropertyValue::XxLarge, "xx-large", 0x70BB1508)},
+    {PVAL(CFX_CSSPropertyValue::Smaller, "smaller", 0x849769F0)},
+    {PVAL(CFX_CSSPropertyValue::Baseline, "baseline", 0x87436BA3)},
+    {PVAL(CFX_CSSPropertyValue::Thick, "thick", 0x8CC35EB3)},
+    {PVAL(CFX_CSSPropertyValue::Justify, "justify", 0x8D269CAE)},
+    {PVAL(CFX_CSSPropertyValue::Middle, "middle", 0x947FA00F)},
+    {PVAL(CFX_CSSPropertyValue::Medium, "medium", 0xA084A381)},
+    {PVAL(CFX_CSSPropertyValue::ListItem, "list-item", 0xA32382B8)},
+    {PVAL(CFX_CSSPropertyValue::XxSmall, "xx-small", 0xADE1FC76)},
+    {PVAL(CFX_CSSPropertyValue::Bold, "bold", 0xB18313A1)},
+    {PVAL(CFX_CSSPropertyValue::SmallCaps, "small-caps", 0xB299428D)},
+    {PVAL(CFX_CSSPropertyValue::Inline, "inline", 0xC02D649F)},
+    {PVAL(CFX_CSSPropertyValue::Overline, "overline", 0xC0EC9FA4)},
+    {PVAL(CFX_CSSPropertyValue::TextBottom, "text-bottom", 0xC7D08D87)},
+    {PVAL(CFX_CSSPropertyValue::Larger, "larger", 0xCD3C409D)},
+    {PVAL(CFX_CSSPropertyValue::InlineTable, "inline-table", 0xD131F494)},
+    {PVAL(CFX_CSSPropertyValue::InlineBlock, "inline-block", 0xD26A8BD7)},
+    {PVAL(CFX_CSSPropertyValue::Blink, "blink", 0xDC36E390)},
+    {PVAL(CFX_CSSPropertyValue::Block, "block", 0xDCD480AB)},
+    {PVAL(CFX_CSSPropertyValue::Italic, "italic", 0xE31D5396)},
+    {PVAL(CFX_CSSPropertyValue::LineThrough, "line-through", 0xE4C5A276)},
+    {PVAL(CFX_CSSPropertyValue::XLarge, "x-large", 0xF008E390)},
+    {PVAL(CFX_CSSPropertyValue::Large, "large", 0xF4434FCB)},
+    {PVAL(CFX_CSSPropertyValue::Left, "left", 0xF5AD782B)},
+    {PVAL(CFX_CSSPropertyValue::TextTop, "text-top", 0xFCB58D45)},
+};
+
+#undef PVAL
+
+const CFX_CSSData::LengthUnit lengthUnitTable[] = {
+    {L"cm", CFX_CSSNumberType::CentiMeters}, {L"em", CFX_CSSNumberType::EMS},
+    {L"ex", CFX_CSSNumberType::EXS},         {L"in", CFX_CSSNumberType::Inches},
+    {L"mm", CFX_CSSNumberType::MilliMeters}, {L"pc", CFX_CSSNumberType::Picas},
+    {L"pt", CFX_CSSNumberType::Points},      {L"px", CFX_CSSNumberType::Pixels},
+};
+
+// 16 colours from CSS 2.0 + alternate spelling of grey/gray.
+const CFX_CSSData::Color colorTable[] = {
+    {L"aqua", 0xff00ffff},    {L"black", 0xff000000}, {L"blue", 0xff0000ff},
+    {L"fuchsia", 0xffff00ff}, {L"gray", 0xff808080},  {L"green", 0xff008000},
+    {L"grey", 0xff808080},    {L"lime", 0xff00ff00},  {L"maroon", 0xff800000},
+    {L"navy", 0xff000080},    {L"olive", 0xff808000}, {L"orange", 0xffffa500},
+    {L"purple", 0xff800080},  {L"red", 0xffff0000},   {L"silver", 0xffc0c0c0},
+    {L"teal", 0xff008080},    {L"white", 0xffffffff}, {L"yellow", 0xffffff00},
+};
+
+}  // namespace
+
+const CFX_CSSData::Property* CFX_CSSData::GetPropertyByName(
+    WideStringView name) {
+  if (name.IsEmpty())
+    return nullptr;
+
+  uint32_t hash = FX_HashCode_GetW(name, true);
+  auto* result =
+      std::lower_bound(std::begin(propertyTable), std::end(propertyTable), hash,
+                       [](const CFX_CSSData::Property& iter,
+                          const uint32_t& hash) { return iter.dwHash < hash; });
+
+  if (result != std::end(propertyTable) && result->dwHash == hash)
+    return result;
+  return nullptr;
+}
+
+const CFX_CSSData::Property* CFX_CSSData::GetPropertyByEnum(
+    CFX_CSSProperty property) {
+  return &propertyTable[static_cast<uint8_t>(property)];
+}
+
+const CFX_CSSData::PropertyValue* CFX_CSSData::GetPropertyValueByName(
+    WideStringView wsName) {
+  if (wsName.IsEmpty())
+    return nullptr;
+
+  uint32_t hash = FX_HashCode_GetW(wsName, true);
+  auto* result = std::lower_bound(
+      std::begin(propertyValueTable), std::end(propertyValueTable), hash,
+      [](const PropertyValue& iter, const uint32_t& hash) {
+        return iter.dwHash < hash;
+      });
+
+  if (result != std::end(propertyValueTable) && result->dwHash == hash)
+    return result;
+  return nullptr;
+}
+
+const CFX_CSSData::LengthUnit* CFX_CSSData::GetLengthUnitByName(
+    WideStringView wsName) {
+  if (wsName.IsEmpty() || wsName.GetLength() != 2)
+    return nullptr;
+
+  WideString lowerName = WideString(wsName);
+  lowerName.MakeLower();
+
+  for (auto* iter = std::begin(lengthUnitTable);
+       iter != std::end(lengthUnitTable); ++iter) {
+    if (lowerName.Compare(iter->value) == 0)
+      return iter;
+  }
+
+  return nullptr;
+}
+
+const CFX_CSSData::Color* CFX_CSSData::GetColorByName(WideStringView wsName) {
+  if (wsName.IsEmpty())
+    return nullptr;
+
+  WideString lowerName = WideString(wsName);
+  lowerName.MakeLower();
+
+  for (auto* iter = std::begin(colorTable); iter != std::end(colorTable);
+       ++iter) {
+    if (lowerName.Compare(iter->name) == 0)
+      return iter;
+  }
+  return nullptr;
+}
diff --git a/core/fxcrt/css/cfx_cssdata.h b/core/fxcrt/css/cfx_cssdata.h
new file mode 100644
index 0000000..d5b6151
--- /dev/null
+++ b/core/fxcrt/css/cfx_cssdata.h
@@ -0,0 +1,46 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_CSS_CFX_CSSDATA_H_
+#define CORE_FXCRT_CSS_CFX_CSSDATA_H_
+
+#include "core/fxcrt/css/cfx_css.h"
+#include "core/fxcrt/css/cfx_cssnumbervalue.h"
+#include "core/fxcrt/css/cfx_cssvalue.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxge/fx_dib.h"
+
+class CFX_CSSData {
+ public:
+  struct Property {
+    CFX_CSSProperty eName;
+    uint32_t dwHash;  // Hashed as wide string.
+    uint32_t dwType;
+  };
+
+  struct PropertyValue {
+    CFX_CSSPropertyValue eName;
+    uint32_t dwHash;  // Hashed as wide string.
+  };
+
+  struct LengthUnit {
+    const wchar_t* value;
+    CFX_CSSNumberType type;
+  };
+
+  struct Color {
+    const wchar_t* name;
+    FX_ARGB value;
+  };
+
+  static const Property* GetPropertyByName(WideStringView name);
+  static const Property* GetPropertyByEnum(CFX_CSSProperty property);
+  static const PropertyValue* GetPropertyValueByName(WideStringView wsName);
+  static const LengthUnit* GetLengthUnitByName(WideStringView wsName);
+  static const Color* GetColorByName(WideStringView wsName);
+};
+
+#endif  // CORE_FXCRT_CSS_CFX_CSSDATA_H_
diff --git a/core/fxcrt/css/cfx_cssdatatable.cpp b/core/fxcrt/css/cfx_cssdatatable.cpp
deleted file mode 100644
index 53617b6..0000000
--- a/core/fxcrt/css/cfx_cssdatatable.cpp
+++ /dev/null
@@ -1,147 +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/fxcrt/css/cfx_cssdatatable.h"
-
-#include <utility>
-
-#include "core/fxcrt/css/cfx_cssstyleselector.h"
-#include "core/fxcrt/css/cfx_cssvaluelistparser.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/fx_extension.h"
-
-static const CFX_CSSPropertyTable g_CFX_CSSProperties[] = {
-    {CFX_CSSProperty::BorderLeft, L"border-left", 0x04080036,
-     CFX_CSSVALUETYPE_Shorthand},
-    {CFX_CSSProperty::Top, L"top", 0x0BEDAF33,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::Margin, L"margin", 0x0CB016BE,
-     CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::TextIndent, L"text-indent", 0x169ADB74,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::Right, L"right", 0x193ADE3E,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::PaddingLeft, L"padding-left", 0x228CF02F,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::MarginLeft, L"margin-left", 0x297C5656,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-         CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::Border, L"border", 0x2A23349E,
-     CFX_CSSVALUETYPE_Shorthand},
-    {CFX_CSSProperty::BorderTop, L"border-top", 0x2B866ADE,
-     CFX_CSSVALUETYPE_Shorthand},
-    {CFX_CSSProperty::Bottom, L"bottom", 0x399F02B5,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::PaddingRight, L"padding-right", 0x3F616AC2,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::BorderBottom, L"border-bottom", 0x452CE780,
-     CFX_CSSVALUETYPE_Shorthand},
-    {CFX_CSSProperty::FontFamily, L"font-family", 0x574686E6,
-     CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeString},
-    {CFX_CSSProperty::FontWeight, L"font-weight", 0x6692F60C,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::Color, L"color", 0x6E67921F,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeColor},
-    {CFX_CSSProperty::LetterSpacing, L"letter-spacing", 0x70536102,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::TextAlign, L"text-align", 0x7553F1BD,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::BorderRightWidth, L"border-right-width", 0x8F5A6036,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::VerticalAlign, L"vertical-align", 0x934A87D2,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::PaddingTop, L"padding-top", 0x959D22B7,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::FontVariant, L"font-variant", 0x9C785779,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::BorderWidth, L"border-width", 0xA8DE4FEB,
-     CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::BorderBottomWidth, L"border-bottom-width", 0xAE41204D,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::BorderRight, L"border-right", 0xB78E9EA9,
-     CFX_CSSVALUETYPE_Shorthand},
-    {CFX_CSSProperty::FontSize, L"font-size", 0xB93956DF,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::BorderSpacing, L"border-spacing", 0xC72030F0,
-     CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::FontStyle, L"font-style", 0xCB1950F5,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::Font, L"font", 0xCD308B77, CFX_CSSVALUETYPE_Shorthand},
-    {CFX_CSSProperty::LineHeight, L"line-height", 0xCFCACE2E,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::MarginRight, L"margin-right", 0xD13C58C9,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-         CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::BorderLeftWidth, L"border-left-width", 0xD1E93D83,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::Display, L"display", 0xD4224C36,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::PaddingBottom, L"padding-bottom", 0xE555B3B9,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::BorderTopWidth, L"border-top-width", 0xED2CB62B,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::WordSpacing, L"word-spacing", 0xEDA63BAE,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::Left, L"left", 0xF5AD782B,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum |
-         CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::TextDecoration, L"text-decoration", 0xF7C634BA,
-     CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::Padding, L"padding", 0xF8C373F7,
-     CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber},
-    {CFX_CSSProperty::MarginBottom, L"margin-bottom", 0xF93485A0,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-         CFX_CSSVALUETYPE_MaybeEnum},
-    {CFX_CSSProperty::MarginTop, L"margin-top", 0xFE51DCFE,
-     CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber |
-         CFX_CSSVALUETYPE_MaybeEnum},
-};
-const int32_t g_iCSSPropertyCount =
-    sizeof(g_CFX_CSSProperties) / sizeof(CFX_CSSPropertyTable);
-static_assert(g_iCSSPropertyCount ==
-                  static_cast<int32_t>(CFX_CSSProperty::LAST_MARKER),
-              "Property table differs in size from property enum");
-
-const CFX_CSSPropertyTable* CFX_GetCSSPropertyByName(
-    const WideStringView& wsName) {
-  ASSERT(!wsName.IsEmpty());
-  uint32_t dwHash = FX_HashCode_GetW(wsName, true);
-  int32_t iEnd = g_iCSSPropertyCount;
-  int32_t iMid, iStart = 0;
-  uint32_t dwMid;
-  do {
-    iMid = (iStart + iEnd) / 2;
-    dwMid = g_CFX_CSSProperties[iMid].dwHash;
-    if (dwHash == dwMid) {
-      return g_CFX_CSSProperties + iMid;
-    } else if (dwHash > dwMid) {
-      iStart = iMid + 1;
-    } else {
-      iEnd = iMid - 1;
-    }
-  } while (iStart <= iEnd);
-  return nullptr;
-}
-
-const CFX_CSSPropertyTable* CFX_GetCSSPropertyByEnum(CFX_CSSProperty eName) {
-  return g_CFX_CSSProperties + static_cast<int>(eName);
-}
diff --git a/core/fxcrt/css/cfx_cssdatatable.h b/core/fxcrt/css/cfx_cssdatatable.h
deleted file mode 100644
index 63c3034..0000000
--- a/core/fxcrt/css/cfx_cssdatatable.h
+++ /dev/null
@@ -1,29 +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_FXCRT_CSS_CFX_CSSDATATABLE_H_
-#define CORE_FXCRT_CSS_CFX_CSSDATATABLE_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/css/cfx_css.h"
-#include "core/fxcrt/css/cfx_cssnumbervalue.h"
-#include "core/fxcrt/css/cfx_cssvalue.h"
-#include "core/fxcrt/fx_system.h"
-
-struct CFX_CSSPropertyTable {
-  CFX_CSSProperty eName;
-  const wchar_t* pszName;
-  uint32_t dwHash;
-  uint32_t dwType;
-};
-
-const CFX_CSSPropertyTable* CFX_GetCSSPropertyByName(
-    const WideStringView& wsName);
-const CFX_CSSPropertyTable* CFX_GetCSSPropertyByEnum(CFX_CSSProperty eName);
-
-#endif  // CORE_FXCRT_CSS_CFX_CSSDATATABLE_H_
diff --git a/core/fxcrt/css/cfx_cssdeclaration.cpp b/core/fxcrt/css/cfx_cssdeclaration.cpp
index 90bdf4e..de97b64 100644
--- a/core/fxcrt/css/cfx_cssdeclaration.cpp
+++ b/core/fxcrt/css/cfx_cssdeclaration.cpp
@@ -6,6 +6,9 @@
 
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 
+#include <cmath>
+#include <utility>
+
 #include "core/fxcrt/css/cfx_csscolorvalue.h"
 #include "core/fxcrt/css/cfx_csscustomproperty.h"
 #include "core/fxcrt/css/cfx_cssenumvalue.h"
@@ -24,177 +27,28 @@
   return (FXSYS_HexCharToInt(hexHigh) << 4) + FXSYS_HexCharToInt(hexLow);
 }
 
-struct CFX_CSSPropertyValueTable {
-  CFX_CSSPropertyValue eName;
-  const wchar_t* pszName;
-  uint32_t dwHash;
-};
-const CFX_CSSPropertyValueTable g_CFX_CSSPropertyValues[] = {
-    {CFX_CSSPropertyValue::Bolder, L"bolder", 0x009F1058},
-    {CFX_CSSPropertyValue::None, L"none", 0x048B6670},
-    {CFX_CSSPropertyValue::Dot, L"dot", 0x0A48CB27},
-    {CFX_CSSPropertyValue::Sub, L"sub", 0x0BD37FAA},
-    {CFX_CSSPropertyValue::Top, L"top", 0x0BEDAF33},
-    {CFX_CSSPropertyValue::Right, L"right", 0x193ADE3E},
-    {CFX_CSSPropertyValue::Normal, L"normal", 0x247CF3E9},
-    {CFX_CSSPropertyValue::Auto, L"auto", 0x2B35B6D9},
-    {CFX_CSSPropertyValue::Text, L"text", 0x2D08AF85},
-    {CFX_CSSPropertyValue::XSmall, L"x-small", 0x2D2FCAFE},
-    {CFX_CSSPropertyValue::Thin, L"thin", 0x2D574D53},
-    {CFX_CSSPropertyValue::Small, L"small", 0x316A3739},
-    {CFX_CSSPropertyValue::Bottom, L"bottom", 0x399F02B5},
-    {CFX_CSSPropertyValue::Underline, L"underline", 0x3A0273A6},
-    {CFX_CSSPropertyValue::Double, L"double", 0x3D98515B},
-    {CFX_CSSPropertyValue::Lighter, L"lighter", 0x45BEB7AF},
-    {CFX_CSSPropertyValue::Oblique, L"oblique", 0x53EBDDB1},
-    {CFX_CSSPropertyValue::Super, L"super", 0x6A4F842F},
-    {CFX_CSSPropertyValue::Center, L"center", 0x6C51AFC1},
-    {CFX_CSSPropertyValue::XxLarge, L"xx-large", 0x70BB1508},
-    {CFX_CSSPropertyValue::Smaller, L"smaller", 0x849769F0},
-    {CFX_CSSPropertyValue::Baseline, L"baseline", 0x87436BA3},
-    {CFX_CSSPropertyValue::Thick, L"thick", 0x8CC35EB3},
-    {CFX_CSSPropertyValue::Justify, L"justify", 0x8D269CAE},
-    {CFX_CSSPropertyValue::Middle, L"middle", 0x947FA00F},
-    {CFX_CSSPropertyValue::Medium, L"medium", 0xA084A381},
-    {CFX_CSSPropertyValue::ListItem, L"list-item", 0xA32382B8},
-    {CFX_CSSPropertyValue::XxSmall, L"xx-small", 0xADE1FC76},
-    {CFX_CSSPropertyValue::Bold, L"bold", 0xB18313A1},
-    {CFX_CSSPropertyValue::SmallCaps, L"small-caps", 0xB299428D},
-    {CFX_CSSPropertyValue::Inline, L"inline", 0xC02D649F},
-    {CFX_CSSPropertyValue::Overline, L"overline", 0xC0EC9FA4},
-    {CFX_CSSPropertyValue::TextBottom, L"text-bottom", 0xC7D08D87},
-    {CFX_CSSPropertyValue::Larger, L"larger", 0xCD3C409D},
-    {CFX_CSSPropertyValue::InlineTable, L"inline-table", 0xD131F494},
-    {CFX_CSSPropertyValue::InlineBlock, L"inline-block", 0xD26A8BD7},
-    {CFX_CSSPropertyValue::Blink, L"blink", 0xDC36E390},
-    {CFX_CSSPropertyValue::Block, L"block", 0xDCD480AB},
-    {CFX_CSSPropertyValue::Italic, L"italic", 0xE31D5396},
-    {CFX_CSSPropertyValue::LineThrough, L"line-through", 0xE4C5A276},
-    {CFX_CSSPropertyValue::XLarge, L"x-large", 0xF008E390},
-    {CFX_CSSPropertyValue::Large, L"large", 0xF4434FCB},
-    {CFX_CSSPropertyValue::Left, L"left", 0xF5AD782B},
-    {CFX_CSSPropertyValue::TextTop, L"text-top", 0xFCB58D45},
-};
-const int32_t g_iCSSPropertyValueCount =
-    sizeof(g_CFX_CSSPropertyValues) / sizeof(CFX_CSSPropertyValueTable);
-static_assert(g_iCSSPropertyValueCount ==
-                  static_cast<int32_t>(CFX_CSSPropertyValue::LAST_MARKER),
-              "Property value table differs in size from property value enum");
-
-struct CFX_CSSLengthUnitTable {
-  uint16_t wHash;
-  CFX_CSSNumberType wValue;
-};
-const CFX_CSSLengthUnitTable g_CFX_CSSLengthUnits[] = {
-    {0x0672, CFX_CSSNumberType::EMS},
-    {0x067D, CFX_CSSNumberType::EXS},
-    {0x1AF7, CFX_CSSNumberType::Inches},
-    {0x2F7A, CFX_CSSNumberType::MilliMeters},
-    {0x3ED3, CFX_CSSNumberType::Picas},
-    {0x3EE4, CFX_CSSNumberType::Points},
-    {0x3EE8, CFX_CSSNumberType::Pixels},
-    {0xFC30, CFX_CSSNumberType::CentiMeters},
-};
-
-struct CFX_CSSColorTable {
-  uint32_t dwHash;
-  FX_ARGB dwValue;
-};
-const CFX_CSSColorTable g_CFX_CSSColors[] = {
-    {0x031B47FE, 0xff000080}, {0x0BB8DF5B, 0xffff0000},
-    {0x0D82A78C, 0xff800000}, {0x2ACC82E8, 0xff00ffff},
-    {0x2D083986, 0xff008080}, {0x4A6A6195, 0xffc0c0c0},
-    {0x546A8EF3, 0xff808080}, {0x65C9169C, 0xffffa500},
-    {0x8422BB61, 0xffffffff}, {0x9271A558, 0xff800080},
-    {0xA65A3EE3, 0xffff00ff}, {0xB1345708, 0xff0000ff},
-    {0xB6D2CF1F, 0xff808000}, {0xD19B5E1C, 0xffffff00},
-    {0xDB64391D, 0xff000000}, {0xF616D507, 0xff00ff00},
-    {0xF6EFFF31, 0xff008000},
-};
-
-const CFX_CSSPropertyValueTable* GetCSSPropertyValueByName(
-    const WideStringView& wsName) {
-  ASSERT(!wsName.IsEmpty());
-  uint32_t dwHash = FX_HashCode_GetW(wsName, true);
-  int32_t iEnd = g_iCSSPropertyValueCount;
-  int32_t iMid, iStart = 0;
-  uint32_t dwMid;
-  do {
-    iMid = (iStart + iEnd) / 2;
-    dwMid = g_CFX_CSSPropertyValues[iMid].dwHash;
-    if (dwHash == dwMid) {
-      return g_CFX_CSSPropertyValues + iMid;
-    } else if (dwHash > dwMid) {
-      iStart = iMid + 1;
-    } else {
-      iEnd = iMid - 1;
-    }
-  } while (iStart <= iEnd);
-  return nullptr;
-}
-
-const CFX_CSSLengthUnitTable* GetCSSLengthUnitByName(
-    const WideStringView& wsName) {
-  ASSERT(!wsName.IsEmpty());
-  uint16_t wHash = FX_HashCode_GetW(wsName, true);
-  int32_t iEnd =
-      sizeof(g_CFX_CSSLengthUnits) / sizeof(CFX_CSSLengthUnitTable) - 1;
-  int32_t iMid, iStart = 0;
-  uint16_t wMid;
-  do {
-    iMid = (iStart + iEnd) / 2;
-    wMid = g_CFX_CSSLengthUnits[iMid].wHash;
-    if (wHash == wMid) {
-      return g_CFX_CSSLengthUnits + iMid;
-    } else if (wHash > wMid) {
-      iStart = iMid + 1;
-    } else {
-      iEnd = iMid - 1;
-    }
-  } while (iStart <= iEnd);
-  return nullptr;
-}
-
-const CFX_CSSColorTable* GetCSSColorByName(const WideStringView& wsName) {
-  ASSERT(!wsName.IsEmpty());
-  uint32_t dwHash = FX_HashCode_GetW(wsName, true);
-  int32_t iEnd = sizeof(g_CFX_CSSColors) / sizeof(CFX_CSSColorTable) - 1;
-  int32_t iMid, iStart = 0;
-  uint32_t dwMid;
-  do {
-    iMid = (iStart + iEnd) / 2;
-    dwMid = g_CFX_CSSColors[iMid].dwHash;
-    if (dwHash == dwMid) {
-      return g_CFX_CSSColors + iMid;
-    } else if (dwHash > dwMid) {
-      iStart = iMid + 1;
-    } else {
-      iEnd = iMid - 1;
-    }
-  } while (iStart <= iEnd);
-  return nullptr;
-}
-
 bool ParseCSSNumber(const wchar_t* pszValue,
                     int32_t iValueLen,
-                    float& fValue,
-                    CFX_CSSNumberType& eUnit) {
-  ASSERT(pszValue && iValueLen > 0);
+                    float* pValue,
+                    CFX_CSSNumberType* pOutUnit) {
+  ASSERT(pszValue);
+  ASSERT(iValueLen > 0);
+
   int32_t iUsedLen = 0;
-  fValue = FXSYS_wcstof(pszValue, iValueLen, &iUsedLen);
-  if (iUsedLen <= 0)
+  *pValue = FXSYS_wcstof(pszValue, iValueLen, &iUsedLen);
+  if (iUsedLen <= 0 || !std::isfinite(*pValue))
     return false;
 
   iValueLen -= iUsedLen;
   pszValue += iUsedLen;
-  eUnit = CFX_CSSNumberType::Number;
+  *pOutUnit = CFX_CSSNumberType::Number;
   if (iValueLen >= 1 && *pszValue == '%') {
-    eUnit = CFX_CSSNumberType::Percent;
+    *pOutUnit = CFX_CSSNumberType::Percent;
   } else if (iValueLen == 2) {
-    const CFX_CSSLengthUnitTable* pUnit =
-        GetCSSLengthUnitByName(WideStringView(pszValue, 2));
+    const CFX_CSSData::LengthUnit* pUnit =
+        CFX_CSSData::GetLengthUnitByName(WideStringView(pszValue, 2));
     if (pUnit)
-      eUnit = pUnit->wValue;
+      *pOutUnit = pUnit->type;
   }
   return true;
 }
@@ -206,7 +60,9 @@
                                         int32_t iValueLen,
                                         int32_t* iOffset,
                                         int32_t* iLength) {
-  ASSERT(pszValue && iValueLen > 0);
+  ASSERT(pszValue);
+  ASSERT(iValueLen > 0);
+
   *iOffset = 0;
   *iLength = iValueLen;
   if (iValueLen >= 2) {
@@ -223,7 +79,8 @@
 bool CFX_CSSDeclaration::ParseCSSColor(const wchar_t* pszValue,
                                        int32_t iValueLen,
                                        FX_ARGB* dwColor) {
-  ASSERT(pszValue && iValueLen > 0);
+  ASSERT(pszValue);
+  ASSERT(iValueLen > 0);
   ASSERT(dwColor);
 
   if (*pszValue == '#') {
@@ -261,23 +118,23 @@
       if (eType != CFX_CSSPrimitiveType::Number)
         return false;
       CFX_CSSNumberType eNumType;
-      if (!ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
+      if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
         return false;
 
       rgb[i] = eNumType == CFX_CSSNumberType::Percent
-                   ? FXSYS_round(fValue * 2.55f)
-                   : FXSYS_round(fValue);
+                   ? FXSYS_roundf(fValue * 2.55f)
+                   : FXSYS_roundf(fValue);
     }
     *dwColor = ArgbEncode(255, rgb[0], rgb[1], rgb[2]);
     return true;
   }
 
-  const CFX_CSSColorTable* pColor =
-      GetCSSColorByName(WideStringView(pszValue, iValueLen));
+  const CFX_CSSData::Color* pColor =
+      CFX_CSSData::GetColorByName(WideStringView(pszValue, iValueLen));
   if (!pColor)
     return false;
 
-  *dwColor = pColor->dwValue;
+  *dwColor = pColor->value;
   return true;
 }
 
@@ -307,8 +164,8 @@
   properties_.push_back(std::move(pHolder));
 }
 
-void CFX_CSSDeclaration::AddProperty(const CFX_CSSPropertyTable* pTable,
-                                     const WideStringView& value) {
+void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property,
+                                     WideStringView value) {
   ASSERT(!value.IsEmpty());
 
   const wchar_t* pszValue = value.unterminated_c_str();
@@ -321,20 +178,20 @@
 
     bImportant = true;
   }
-  const uint32_t dwType = pTable->dwType;
+  const uint32_t dwType = property->dwType;
   switch (dwType & 0x0F) {
     case CFX_CSSVALUETYPE_Primitive: {
-      static const uint32_t g_ValueGuessOrder[] = {
-          CFX_CSSVALUETYPE_MaybeNumber, CFX_CSSVALUETYPE_MaybeEnum,
-          CFX_CSSVALUETYPE_MaybeColor, CFX_CSSVALUETYPE_MaybeString,
+      static constexpr CFX_CSSVALUETYPE kValueGuessOrder[] = {
+          CFX_CSSVALUETYPE_MaybeNumber,
+          CFX_CSSVALUETYPE_MaybeEnum,
+          CFX_CSSVALUETYPE_MaybeColor,
+          CFX_CSSVALUETYPE_MaybeString,
       };
-      static const int32_t g_ValueGuessCount =
-          sizeof(g_ValueGuessOrder) / sizeof(uint32_t);
-      for (int32_t i = 0; i < g_ValueGuessCount; ++i) {
-        const uint32_t dwMatch = dwType & g_ValueGuessOrder[i];
-        if (dwMatch == 0) {
+      for (uint32_t guess : kValueGuessOrder) {
+        const uint32_t dwMatch = dwType & guess;
+        if (dwMatch == 0)
           continue;
-        }
+
         RetainPtr<CFX_CSSValue> pCSSValue;
         switch (dwMatch) {
           case CFX_CSSVALUETYPE_MaybeNumber:
@@ -353,18 +210,18 @@
             break;
         }
         if (pCSSValue) {
-          AddPropertyHolder(pTable->eName, pCSSValue, bImportant);
+          AddPropertyHolder(property->eName, pCSSValue, bImportant);
           return;
         }
 
-        if ((dwType & ~(g_ValueGuessOrder[i])) == CFX_CSSVALUETYPE_Primitive)
+        if ((dwType & ~guess) == CFX_CSSVALUETYPE_Primitive)
           return;
       }
       break;
     }
     case CFX_CSSVALUETYPE_Shorthand: {
       RetainPtr<CFX_CSSValue> pWidth;
-      switch (pTable->eName) {
+      switch (property->eName) {
         case CFX_CSSProperty::Font:
           ParseFontProperty(pszValue, iValueLen, bImportant);
           return;
@@ -414,7 +271,7 @@
       }
     } break;
     case CFX_CSSVALUETYPE_List:
-      ParseValueListProperty(pTable, pszValue, iValueLen, bImportant);
+      ParseValueListProperty(property, pszValue, iValueLen, bImportant);
       return;
     default:
       NOTREACHED();
@@ -432,15 +289,15 @@
                                                         int32_t iValueLen) {
   float fValue;
   CFX_CSSNumberType eUnit;
-  if (!ParseCSSNumber(pszValue, iValueLen, fValue, eUnit))
+  if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eUnit))
     return nullptr;
   return pdfium::MakeRetain<CFX_CSSNumberValue>(eUnit, fValue);
 }
 
 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseEnum(const wchar_t* pszValue,
                                                       int32_t iValueLen) {
-  const CFX_CSSPropertyValueTable* pValue =
-      GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
+  const CFX_CSSData::PropertyValue* pValue =
+      CFX_CSSData::GetPropertyValueByName(WideStringView(pszValue, iValueLen));
   return pValue ? pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName) : nullptr;
 }
 
@@ -466,15 +323,15 @@
 }
 
 void CFX_CSSDeclaration::ParseValueListProperty(
-    const CFX_CSSPropertyTable* pTable,
+    const CFX_CSSData::Property* pProperty,
     const wchar_t* pszValue,
     int32_t iValueLen,
     bool bImportant) {
   wchar_t separator =
-      (pTable->eName == CFX_CSSProperty::FontFamily) ? ',' : ' ';
+      (pProperty->eName == CFX_CSSProperty::FontFamily) ? ',' : ' ';
   CFX_CSSValueListParser parser(pszValue, iValueLen, separator);
 
-  const uint32_t dwType = pTable->dwType;
+  const uint32_t dwType = pProperty->dwType;
   CFX_CSSPrimitiveType eType;
   std::vector<RetainPtr<CFX_CSSValue>> list;
   while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
@@ -483,7 +340,7 @@
         if (dwType & CFX_CSSVALUETYPE_MaybeNumber) {
           float fValue;
           CFX_CSSNumberType eNumType;
-          if (ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
+          if (ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
             list.push_back(
                 pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue));
         }
@@ -497,8 +354,9 @@
           }
         }
         if (dwType & CFX_CSSVALUETYPE_MaybeEnum) {
-          const CFX_CSSPropertyValueTable* pValue =
-              GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
+          const CFX_CSSData::PropertyValue* pValue =
+              CFX_CSSData::GetPropertyValueByName(
+                  WideStringView(pszValue, iValueLen));
           if (pValue) {
             list.push_back(pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName));
             continue;
@@ -524,7 +382,7 @@
   if (list.empty())
     return;
 
-  switch (pTable->eName) {
+  switch (pProperty->eName) {
     case CFX_CSSProperty::BorderWidth:
       Add4ValuesProperty(list, bImportant, CFX_CSSProperty::BorderLeftWidth,
                          CFX_CSSProperty::BorderTopWidth,
@@ -545,7 +403,7 @@
       return;
     default: {
       auto pList = pdfium::MakeRetain<CFX_CSSValueList>(list);
-      AddPropertyHolder(pTable->eName, pList, bImportant);
+      AddPropertyHolder(pProperty->eName, pList, bImportant);
       return;
     }
   }
@@ -604,18 +462,19 @@
 
         float fValue;
         CFX_CSSNumberType eNumType;
-        if (ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
+        if (ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
           pWidth = pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue);
         break;
       }
       case CFX_CSSPrimitiveType::String: {
-        const CFX_CSSColorTable* pColorItem =
-            GetCSSColorByName(WideStringView(pszValue, iValueLen));
+        const CFX_CSSData::Color* pColorItem =
+            CFX_CSSData::GetColorByName(WideStringView(pszValue, iValueLen));
         if (pColorItem)
           continue;
 
-        const CFX_CSSPropertyValueTable* pValue =
-            GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
+        const CFX_CSSData::PropertyValue* pValue =
+            CFX_CSSData::GetPropertyValueByName(
+                WideStringView(pszValue, iValueLen));
         if (!pValue)
           continue;
 
@@ -656,8 +515,9 @@
   while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
     switch (eType) {
       case CFX_CSSPrimitiveType::String: {
-        const CFX_CSSPropertyValueTable* pValue =
-            GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
+        const CFX_CSSData::PropertyValue* pValue =
+            CFX_CSSData::GetPropertyValueByName(
+                WideStringView(pszValue, iValueLen));
         if (pValue) {
           switch (pValue->eName) {
             case CFX_CSSPropertyValue::XxSmall:
@@ -714,7 +574,7 @@
       case CFX_CSSPrimitiveType::Number: {
         float fValue;
         CFX_CSSNumberType eNumType;
-        if (!ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
+        if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType))
           break;
         if (eType == CFX_CSSPrimitiveType::Number) {
           switch ((int32_t)fValue) {
diff --git a/core/fxcrt/css/cfx_cssdeclaration.h b/core/fxcrt/css/cfx_cssdeclaration.h
index 7dd0bba..f3d87cc 100644
--- a/core/fxcrt/css/cfx_cssdeclaration.h
+++ b/core/fxcrt/css/cfx_cssdeclaration.h
@@ -8,10 +8,9 @@
 #define CORE_FXCRT_CSS_CFX_CSSDECLARATION_H_
 
 #include <memory>
-#include <utility>
 #include <vector>
 
-#include "core/fxcrt/css/cfx_cssdatatable.h"
+#include "core/fxcrt/css/cfx_cssdata.h"
 
 class CFX_CSSPropertyHolder;
 class CFX_CSSCustomProperty;
@@ -47,8 +46,7 @@
 
   bool empty() const { return properties_.empty(); }
 
-  void AddProperty(const CFX_CSSPropertyTable* pTable,
-                   const WideStringView& value);
+  void AddProperty(const CFX_CSSData::Property* property, WideStringView value);
   void AddProperty(const WideString& prop, const WideString& value);
 
   size_t PropertyCountForTesting() const;
@@ -64,7 +62,7 @@
   bool ParseBorderProperty(const wchar_t* pszValue,
                            int32_t iValueLen,
                            RetainPtr<CFX_CSSValue>& pWidth) const;
-  void ParseValueListProperty(const CFX_CSSPropertyTable* pTable,
+  void ParseValueListProperty(const CFX_CSSData::Property* pProperty,
                               const wchar_t* pszValue,
                               int32_t iValueLen,
                               bool bImportant);
diff --git a/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp b/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp
index 535e67d..f4a032a 100644
--- a/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp
+++ b/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp
@@ -5,7 +5,6 @@
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 
 TEST(CFX_CSSDeclarationTest, HexEncodingParsing) {
   FX_ARGB color;
diff --git a/core/fxcrt/css/cfx_cssenumvalue.h b/core/fxcrt/css/cfx_cssenumvalue.h
index 0d6b87e..c397761 100644
--- a/core/fxcrt/css/cfx_cssenumvalue.h
+++ b/core/fxcrt/css/cfx_cssenumvalue.h
@@ -9,7 +9,7 @@
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
 
-class CFX_CSSEnumValue : public CFX_CSSValue {
+class CFX_CSSEnumValue final : public CFX_CSSValue {
  public:
   explicit CFX_CSSEnumValue(CFX_CSSPropertyValue value);
   ~CFX_CSSEnumValue() override;
diff --git a/core/fxcrt/css/cfx_cssnumbervalue.h b/core/fxcrt/css/cfx_cssnumbervalue.h
index a49328d..a977750 100644
--- a/core/fxcrt/css/cfx_cssnumbervalue.h
+++ b/core/fxcrt/css/cfx_cssnumbervalue.h
@@ -23,7 +23,7 @@
   Picas,
 };
 
-class CFX_CSSNumberValue : public CFX_CSSValue {
+class CFX_CSSNumberValue final : public CFX_CSSValue {
  public:
   CFX_CSSNumberValue(CFX_CSSNumberType type, float value);
   ~CFX_CSSNumberValue() override;
diff --git a/core/fxcrt/css/cfx_cssrulecollection.cpp b/core/fxcrt/css/cfx_cssrulecollection.cpp
index 2030518..f525821 100644
--- a/core/fxcrt/css/cfx_cssrulecollection.cpp
+++ b/core/fxcrt/css/cfx_cssrulecollection.cpp
@@ -29,7 +29,7 @@
 
 const std::vector<std::unique_ptr<CFX_CSSRuleCollection::Data>>*
 CFX_CSSRuleCollection::GetTagRuleData(const WideString& tagname) const {
-  auto it = m_TagRules.find(FX_HashCode_GetW(tagname.c_str(), true));
+  auto it = m_TagRules.find(FX_HashCode_GetW(tagname.AsStringView(), true));
   return it != m_TagRules.end() ? &it->second : nullptr;
 }
 
diff --git a/core/fxcrt/css/cfx_cssselector.cpp b/core/fxcrt/css/cfx_cssselector.cpp
index 3993dca..cd90f62 100644
--- a/core/fxcrt/css/cfx_cssselector.cpp
+++ b/core/fxcrt/css/cfx_cssselector.cpp
@@ -16,9 +16,9 @@
 int32_t GetCSSNameLen(const wchar_t* psz, const wchar_t* pEnd) {
   const wchar_t* pStart = psz;
   while (psz < pEnd) {
-    wchar_t wch = *psz;
-    if (!FXSYS_iswalnum(wch) && wch != '_' && wch != '-')
+    if (!isascii(*psz) || (!isalnum(*psz) && *psz != '_' && *psz != '-')) {
       break;
+    }
     ++psz;
   }
   return psz - pStart;
@@ -49,7 +49,7 @@
 
 // static.
 std::unique_ptr<CFX_CSSSelector> CFX_CSSSelector::FromString(
-    const WideStringView& str) {
+    WideStringView str) {
   ASSERT(!str.IsEmpty());
 
   const wchar_t* psz = str.unterminated_c_str();
@@ -67,7 +67,7 @@
   std::unique_ptr<CFX_CSSSelector> pFirst = nullptr;
   for (psz = pStart; psz < pEnd;) {
     wchar_t wch = *psz;
-    if (FXSYS_iswalpha(wch) || wch == '*') {
+    if ((isascii(wch) && isalpha(wch)) || wch == '*') {
       int32_t iNameLen = wch == '*' ? 1 : GetCSSNameLen(psz, pEnd);
       auto p = pdfium::MakeUnique<CFX_CSSSelector>(CFX_CSSSelectorType::Element,
                                                    psz, iNameLen, true);
diff --git a/core/fxcrt/css/cfx_cssselector.h b/core/fxcrt/css/cfx_cssselector.h
index 62f8b16..14b61ee 100644
--- a/core/fxcrt/css/cfx_cssselector.h
+++ b/core/fxcrt/css/cfx_cssselector.h
@@ -15,7 +15,7 @@
 
 class CFX_CSSSelector {
  public:
-  static std::unique_ptr<CFX_CSSSelector> FromString(const WideStringView& str);
+  static std::unique_ptr<CFX_CSSSelector> FromString(WideStringView str);
 
   CFX_CSSSelector(CFX_CSSSelectorType eType,
                   const wchar_t* psz,
diff --git a/core/fxcrt/css/cfx_cssstringvalue.h b/core/fxcrt/css/cfx_cssstringvalue.h
index d72078a..b59cef0 100644
--- a/core/fxcrt/css/cfx_cssstringvalue.h
+++ b/core/fxcrt/css/cfx_cssstringvalue.h
@@ -8,8 +8,9 @@
 #define CORE_FXCRT_CSS_CFX_CSSSTRINGVALUE_H_
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
+#include "core/fxcrt/widestring.h"
 
-class CFX_CSSStringValue : public CFX_CSSValue {
+class CFX_CSSStringValue final : public CFX_CSSValue {
  public:
   explicit CFX_CSSStringValue(const WideString& value);
   ~CFX_CSSStringValue() override;
diff --git a/core/fxcrt/css/cfx_cssstyleselector.cpp b/core/fxcrt/css/cfx_cssstyleselector.cpp
index cb9ab2f..f6b48bd 100644
--- a/core/fxcrt/css/cfx_cssstyleselector.cpp
+++ b/core/fxcrt/css/cfx_cssstyleselector.cpp
@@ -75,7 +75,7 @@
       pSel->GetType() == CFX_CSSSelectorType::Descendant) {
     return false;
   }
-  return pSel->GetNameHash() == FX_HashCode_GetW(tagname.c_str(), true);
+  return pSel->GetNameHash() == FX_HashCode_GetW(tagname.AsStringView(), true);
 }
 
 void CFX_CSSStyleSelector::ComputeStyle(
@@ -90,8 +90,9 @@
     if (!styleString.IsEmpty())
       AppendInlineStyle(pDecl.get(), styleString);
     if (!alignString.IsEmpty()) {
-      pDecl->AddProperty(CFX_GetCSSPropertyByEnum(CFX_CSSProperty::TextAlign),
-                         alignString.AsStringView());
+      pDecl->AddProperty(
+          CFX_CSSData::GetPropertyByEnum(CFX_CSSProperty::TextAlign),
+          alignString.AsStringView());
     }
   }
   ApplyDeclarations(declArray, pDecl.get(), pDest);
@@ -138,26 +139,27 @@
 
 void CFX_CSSStyleSelector::AppendInlineStyle(CFX_CSSDeclaration* pDecl,
                                              const WideString& style) {
-  ASSERT(pDecl && !style.IsEmpty());
+  ASSERT(pDecl);
+  ASSERT(!style.IsEmpty());
 
   auto pSyntax = pdfium::MakeUnique<CFX_CSSSyntaxParser>(
       style.c_str(), style.GetLength(), 32, true);
   int32_t iLen2 = 0;
-  const CFX_CSSPropertyTable* table = nullptr;
+  const CFX_CSSData::Property* property = nullptr;
   WideString wsName;
   while (1) {
     CFX_CSSSyntaxStatus eStatus = pSyntax->DoSyntaxParse();
     if (eStatus == CFX_CSSSyntaxStatus::PropertyName) {
       WideStringView strValue = pSyntax->GetCurrentString();
-      table = CFX_GetCSSPropertyByName(strValue);
-      if (!table)
+      property = CFX_CSSData::GetPropertyByName(strValue);
+      if (!property)
         wsName = WideString(strValue);
     } else if (eStatus == CFX_CSSSyntaxStatus::PropertyValue) {
-      if (table || iLen2 > 0) {
+      if (property || iLen2 > 0) {
         WideStringView strValue = pSyntax->GetCurrentString();
         if (!strValue.IsEmpty()) {
-          if (table)
-            pDecl->AddProperty(table, strValue);
+          if (property)
+            pDecl->AddProperty(property, strValue);
           else if (iLen2 > 0)
             pDecl->AddProperty(wsName, WideString(strValue));
         }
diff --git a/core/fxcrt/css/cfx_cssstylesheet.cpp b/core/fxcrt/css/cfx_cssstylesheet.cpp
index 183765f..c7300fb 100644
--- a/core/fxcrt/css/cfx_cssstylesheet.cpp
+++ b/core/fxcrt/css/cfx_cssstylesheet.cpp
@@ -8,7 +8,7 @@
 
 #include <utility>
 
-#include "core/fxcrt/css/cfx_cssdatatable.h"
+#include "core/fxcrt/css/cfx_cssdata.h"
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 #include "core/fxcrt/css/cfx_cssstylerule.h"
 #include "core/fxcrt/fx_codepage.h"
@@ -62,7 +62,7 @@
 
   CFX_CSSStyleRule* pStyleRule = nullptr;
   int32_t iValueLen = 0;
-  const CFX_CSSPropertyTable* propertyTable = nullptr;
+  const CFX_CSSData::Property* property = nullptr;
   WideString wsName;
   while (1) {
     switch (pSyntax->DoSyntaxParse()) {
@@ -75,18 +75,18 @@
       }
       case CFX_CSSSyntaxStatus::PropertyName: {
         WideStringView strValue = pSyntax->GetCurrentString();
-        propertyTable = CFX_GetCSSPropertyByName(strValue);
-        if (!propertyTable)
+        property = CFX_CSSData::GetPropertyByName(strValue);
+        if (!property)
           wsName = WideString(strValue);
         break;
       }
       case CFX_CSSSyntaxStatus::PropertyValue: {
-        if (propertyTable || iValueLen > 0) {
+        if (property || iValueLen > 0) {
           WideStringView strValue = pSyntax->GetCurrentString();
           auto* decl = pStyleRule->GetDeclaration();
           if (!strValue.IsEmpty()) {
-            if (propertyTable) {
-              decl->AddProperty(propertyTable, strValue);
+            if (property) {
+              decl->AddProperty(property, strValue);
             } else {
               decl->AddProperty(wsName, WideString(strValue));
             }
diff --git a/core/fxcrt/css/cfx_csssyntaxparser.cpp b/core/fxcrt/css/cfx_csssyntaxparser.cpp
index 066b82b..9cc9991 100644
--- a/core/fxcrt/css/cfx_csssyntaxparser.cpp
+++ b/core/fxcrt/css/cfx_csssyntaxparser.cpp
@@ -8,16 +8,18 @@
 
 #include <algorithm>
 
-#include "core/fxcrt/css/cfx_cssdatatable.h"
+#include "core/fxcrt/css/cfx_cssdata.h"
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/logging.h"
 
 namespace {
 
 bool IsSelectorStart(wchar_t wch) {
-  return wch == '.' || wch == '#' || wch == '*' || FXSYS_iswalpha(wch);
+  return wch == '.' || wch == '#' || wch == '*' ||
+         (isascii(wch) && isalpha(wch));
 }
 
 }  // namespace
@@ -33,7 +35,10 @@
     : m_iTextDataLen(0),
       m_dwCheck(0xFFFFFFFF),
       m_eStatus(CFX_CSSSyntaxStatus::None) {
-  ASSERT(pBuffer && iBufferSize > 0 && iTextDatSize > 0);
+  ASSERT(pBuffer);
+  ASSERT(iBufferSize > 0);
+  ASSERT(iTextDatSize > 0);
+
   m_eMode = bOnlyDeclaration ? CFX_CSSSyntaxMode::PropertyName
                              : CFX_CSSSyntaxMode::RuleSet;
   m_TextData.InitWithSize(iTextDatSize);
@@ -73,6 +78,7 @@
                 SwitchMode(CFX_CSSSyntaxMode::Comment);
                 break;
               }
+              FALLTHROUGH;
             default:
               if (wch <= ' ') {
                 m_TextPlane.MoveNext();
@@ -109,6 +115,7 @@
                   return CFX_CSSSyntaxStatus::Selector;
                 break;
               }
+              FALLTHROUGH;
             default:
               AppendChar(wch);
               break;
@@ -133,6 +140,7 @@
                   return CFX_CSSSyntaxStatus::PropertyName;
                 break;
               }
+              FALLTHROUGH;
             default:
               AppendChar(wch);
               break;
@@ -142,6 +150,7 @@
           switch (wch) {
             case ';':
               m_TextPlane.MoveNext();
+              FALLTHROUGH;
             case '}':
               SwitchMode(CFX_CSSSyntaxMode::PropertyName);
               return CFX_CSSSyntaxStatus::PropertyValue;
@@ -151,6 +160,7 @@
                   return CFX_CSSSyntaxStatus::PropertyValue;
                 break;
               }
+              FALLTHROUGH;
             default:
               AppendChar(wch);
               break;
diff --git a/core/fxcrt/css/cfx_cssvalue.h b/core/fxcrt/css/cfx_cssvalue.h
index f143e96..30aace2 100644
--- a/core/fxcrt/css/cfx_cssvalue.h
+++ b/core/fxcrt/css/cfx_cssvalue.h
@@ -8,6 +8,7 @@
 #define CORE_FXCRT_CSS_CFX_CSSVALUE_H_
 
 #include "core/fxcrt/css/cfx_css.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CFX_CSSValue : public Retainable {
  public:
diff --git a/core/fxcrt/css/cfx_cssvaluelist.h b/core/fxcrt/css/cfx_cssvaluelist.h
index d2b0c6e..4b87f4c 100644
--- a/core/fxcrt/css/cfx_cssvaluelist.h
+++ b/core/fxcrt/css/cfx_cssvaluelist.h
@@ -11,7 +11,7 @@
 
 #include "core/fxcrt/css/cfx_cssvalue.h"
 
-class CFX_CSSValueList : public CFX_CSSValue {
+class CFX_CSSValueList final : public CFX_CSSValue {
  public:
   explicit CFX_CSSValueList(std::vector<RetainPtr<CFX_CSSValue>>& list);
   ~CFX_CSSValueList() override;
@@ -19,7 +19,7 @@
   int32_t CountValues() const;
   RetainPtr<CFX_CSSValue> GetValue(int32_t index) const;
 
- protected:
+ private:
   std::vector<RetainPtr<CFX_CSSValue>> m_ppList;
 };
 
diff --git a/core/fxcrt/css/cfx_cssvaluelistparser.cpp b/core/fxcrt/css/cfx_cssvaluelistparser.cpp
index 2339dec..447ba9f 100644
--- a/core/fxcrt/css/cfx_cssvaluelistparser.cpp
+++ b/core/fxcrt/css/cfx_cssvaluelistparser.cpp
@@ -12,7 +12,8 @@
                                                int32_t iLen,
                                                wchar_t separator)
     : m_Separator(separator), m_pCur(psz), m_pEnd(psz + iLen) {
-  ASSERT(psz && iLen > 0);
+  ASSERT(psz);
+  ASSERT(iLen > 0);
 }
 
 bool CFX_CSSValueListParser::NextValue(CFX_CSSPrimitiveType* eType,
@@ -32,7 +33,8 @@
     *iLength = SkipTo(' ', false, false);
     if (*iLength == 4 || *iLength == 7)
       *eType = CFX_CSSPrimitiveType::RGB;
-  } else if (std::iswdigit(wch) || wch == '.' || wch == '-' || wch == '+') {
+  } else if (FXSYS_IsDecimalDigit(wch) || wch == '.' || wch == '-' ||
+             wch == '+') {
     while (m_pCur < m_pEnd && (*m_pCur > ' ' && *m_pCur != m_Separator))
       ++m_pCur;
 
diff --git a/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp b/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp
index e232a2d..bb065f8 100644
--- a/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp
+++ b/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp
@@ -6,8 +6,8 @@
 
 #include "core/fxcrt/css/cfx_cssvaluelistparser.h"
 
+#include "core/fxcrt/widestring.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
 
 TEST(CFX_CSSValueListParserTest, rgb_short) {
diff --git a/core/fxcrt/fileaccess_iface.h b/core/fxcrt/fileaccess_iface.h
new file mode 100644
index 0000000..c744b54
--- /dev/null
+++ b/core/fxcrt/fileaccess_iface.h
@@ -0,0 +1,36 @@
+// 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_FXCRT_FILEACCESS_IFACE_H_
+#define CORE_FXCRT_FILEACCESS_IFACE_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_string.h"
+
+class FileAccessIface {
+ public:
+  static std::unique_ptr<FileAccessIface> Create();
+  virtual ~FileAccessIface() = default;
+
+  virtual bool Open(ByteStringView fileName, uint32_t dwMode) = 0;
+  virtual bool Open(WideStringView fileName, uint32_t dwMode) = 0;
+  virtual void Close() = 0;
+  virtual FX_FILESIZE GetSize() const = 0;
+  virtual FX_FILESIZE GetPosition() const = 0;
+  virtual FX_FILESIZE SetPosition(FX_FILESIZE pos) = 0;
+  virtual size_t Read(void* pBuffer, size_t szBuffer) = 0;
+  virtual size_t Write(const void* pBuffer, size_t szBuffer) = 0;
+  virtual size_t ReadPos(void* pBuffer, size_t szBuffer, FX_FILESIZE pos) = 0;
+  virtual size_t WritePos(const void* pBuffer,
+                          size_t szBuffer,
+                          FX_FILESIZE pos) = 0;
+  virtual bool Flush() = 0;
+  virtual bool Truncate(FX_FILESIZE szFile) = 0;
+};
+
+#endif  // CORE_FXCRT_FILEACCESS_IFACE_H_
diff --git a/core/fxcrt/fx_arabic.cpp b/core/fxcrt/fx_arabic.cpp
deleted file mode 100644
index 0d6f039..0000000
--- a/core/fxcrt/fx_arabic.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/fx_arabic.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_unicode.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-struct FX_ARBFORMTABLE {
-  uint16_t wIsolated;
-  uint16_t wFinal;
-  uint16_t wInitial;
-  uint16_t wMedial;
-};
-
-struct FX_ARAALEF {
-  uint16_t wAlef;
-  uint16_t wIsolated;
-};
-
-struct FX_ARASHADDA {
-  uint16_t wShadda;
-  uint16_t wIsolated;
-};
-
-const FX_ARBFORMTABLE g_FX_ArabicFormTables[] = {
-    {0xFE81, 0xFE82, 0xFE81, 0xFE82}, {0xFE83, 0xFE84, 0xFE83, 0xFE84},
-    {0xFE85, 0xFE86, 0xFE85, 0xFE86}, {0xFE87, 0xFE88, 0xFE87, 0xFE88},
-    {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}, {0xFE8D, 0xFE8E, 0xFE8D, 0xFE8E},
-    {0xFE8F, 0xFE90, 0xFE91, 0xFE92}, {0xFE93, 0xFE94, 0xFE93, 0xFE94},
-    {0xFE95, 0xFE96, 0xFE97, 0xFE98}, {0xFE99, 0xFE9A, 0xFE9B, 0xFE9C},
-    {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}, {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4},
-    {0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}, {0xFEA9, 0xFEAA, 0xFEA9, 0xFEAA},
-    {0xFEAB, 0xFEAC, 0xFEAB, 0xFEAC}, {0xFEAD, 0xFEAE, 0xFEAD, 0xFEAE},
-    {0xFEAF, 0xFEB0, 0xFEAF, 0xFEB0}, {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4},
-    {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}, {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC},
-    {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}, {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4},
-    {0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}, {0xFEC9, 0xFECA, 0xFECB, 0xFECC},
-    {0xFECD, 0xFECE, 0xFECF, 0xFED0}, {0x063B, 0x063B, 0x063B, 0x063B},
-    {0x063C, 0x063C, 0x063C, 0x063C}, {0x063D, 0x063D, 0x063D, 0x063D},
-    {0x063E, 0x063E, 0x063E, 0x063E}, {0x063F, 0x063F, 0x063F, 0x063F},
-    {0x0640, 0x0640, 0x0640, 0x0640}, {0xFED1, 0xFED2, 0xFED3, 0xFED4},
-    {0xFED5, 0xFED6, 0xFED7, 0xFED8}, {0xFED9, 0xFEDA, 0xFEDB, 0xFEDC},
-    {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}, {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4},
-    {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}, {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC},
-    {0xFEED, 0xFEEE, 0xFEED, 0xFEEE}, {0xFEEF, 0xFEF0, 0xFBFE, 0xFBFF},
-    {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}, {0x064B, 0x064B, 0x064B, 0x064B},
-    {0x064C, 0x064C, 0x064C, 0x064C}, {0x064D, 0x064D, 0x064D, 0x064D},
-    {0x064E, 0x064E, 0x064E, 0x064E}, {0x064F, 0x064F, 0x064F, 0x064F},
-    {0x0650, 0x0650, 0x0650, 0x0650}, {0x0651, 0x0651, 0x0651, 0x0651},
-    {0x0652, 0x0652, 0x0652, 0x0652}, {0x0653, 0x0653, 0x0653, 0x0653},
-    {0x0654, 0x0654, 0x0654, 0x0654}, {0x0655, 0x0655, 0x0655, 0x0655},
-    {0x0656, 0x0656, 0x0656, 0x0656}, {0x0657, 0x0657, 0x0657, 0x0657},
-    {0x0658, 0x0658, 0x0658, 0x0658}, {0x0659, 0x0659, 0x0659, 0x0659},
-    {0x065A, 0x065A, 0x065A, 0x065A}, {0x065B, 0x065B, 0x065B, 0x065B},
-    {0x065C, 0x065C, 0x065C, 0x065C}, {0x065D, 0x065D, 0x065D, 0x065D},
-    {0x065E, 0x065E, 0x065E, 0x065E}, {0x065F, 0x065F, 0x065F, 0x065F},
-    {0x0660, 0x0660, 0x0660, 0x0660}, {0x0661, 0x0661, 0x0661, 0x0661},
-    {0x0662, 0x0662, 0x0662, 0x0662}, {0x0663, 0x0663, 0x0663, 0x0663},
-    {0x0664, 0x0664, 0x0664, 0x0664}, {0x0665, 0x0665, 0x0665, 0x0665},
-    {0x0666, 0x0666, 0x0666, 0x0666}, {0x0667, 0x0667, 0x0667, 0x0667},
-    {0x0668, 0x0668, 0x0668, 0x0668}, {0x0669, 0x0669, 0x0669, 0x0669},
-    {0x066A, 0x066A, 0x066A, 0x066A}, {0x066B, 0x066B, 0x066B, 0x066B},
-    {0x066C, 0x066C, 0x066C, 0x066C}, {0x066D, 0x066D, 0x066D, 0x066D},
-    {0x066E, 0x066E, 0x066E, 0x066E}, {0x066F, 0x066F, 0x066F, 0x066F},
-    {0x0670, 0x0670, 0x0670, 0x0670}, {0xFB50, 0xFB51, 0xFB50, 0xFB51},
-    {0x0672, 0x0672, 0x0672, 0x0672}, {0x0673, 0x0673, 0x0673, 0x0673},
-    {0x0674, 0x0674, 0x0674, 0x0674}, {0x0675, 0x0675, 0x0675, 0x0675},
-    {0x0676, 0x0676, 0x0676, 0x0676}, {0x0677, 0x0677, 0x0677, 0x0677},
-    {0x0678, 0x0678, 0x0678, 0x0678}, {0xFB66, 0xFB67, 0xFB68, 0xFB69},
-    {0xFB5E, 0xFB5F, 0xFB60, 0xFB61}, {0xFB52, 0xFB53, 0xFB54, 0xFB55},
-    {0x067C, 0x067C, 0x067C, 0x067C}, {0x067D, 0x067D, 0x067D, 0x067D},
-    {0xFB56, 0xFB57, 0xFB58, 0xFB59}, {0xFB62, 0xFB63, 0xFB64, 0xFB65},
-    {0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}, {0x0681, 0x0681, 0x0681, 0x0681},
-    {0x0682, 0x0682, 0x0682, 0x0682}, {0xFB76, 0xFB77, 0xFB78, 0xFB79},
-    {0xFB72, 0xFB73, 0xFB74, 0xFB75}, {0x0685, 0x0685, 0x0685, 0x0685},
-    {0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}, {0xFB7E, 0xFB7F, 0xFB80, 0xFB81},
-    {0xFB88, 0xFB89, 0xFB88, 0xFB89}, {0x0689, 0x0689, 0x0689, 0x0689},
-    {0x068A, 0x068A, 0x068A, 0x068A}, {0x068B, 0x068B, 0x068B, 0x068B},
-    {0xFB84, 0xFB85, 0xFB84, 0xFB85}, {0xFB82, 0xFB83, 0xFB82, 0xFB83},
-    {0xFB86, 0xFB87, 0xFB86, 0xFB87}, {0x068F, 0x068F, 0x068F, 0x068F},
-    {0x0690, 0x0690, 0x0690, 0x0690}, {0xFB8C, 0xFB8D, 0xFB8C, 0xFB8D},
-    {0x0692, 0x0692, 0x0692, 0x0692}, {0x0693, 0x0693, 0x0693, 0x0693},
-    {0x0694, 0x0694, 0x0694, 0x0694}, {0x0695, 0x0695, 0x0695, 0x0695},
-    {0x0696, 0x0696, 0x0696, 0x0696}, {0x0697, 0x0697, 0x0697, 0x0697},
-    {0xFB8A, 0xFB8B, 0xFB8A, 0xFB8B}, {0x0699, 0x0699, 0x0699, 0x0699},
-    {0x069A, 0x069A, 0x069A, 0x069A}, {0x069B, 0x069B, 0x069B, 0x069B},
-    {0x069C, 0x069C, 0x069C, 0x069C}, {0x069D, 0x069D, 0x069D, 0x069D},
-    {0x069E, 0x069E, 0x069E, 0x069E}, {0x069F, 0x069F, 0x069F, 0x069F},
-    {0x06A0, 0x06A0, 0x06A0, 0x06A0}, {0x06A1, 0x06A1, 0x06A1, 0x06A1},
-    {0x06A2, 0x06A2, 0x06A2, 0x06A2}, {0x06A3, 0x06A3, 0x06A3, 0x06A3},
-    {0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}, {0x06A5, 0x06A5, 0x06A5, 0x06A5},
-    {0xFB6E, 0xFB6F, 0xFB70, 0xFB71}, {0x06A7, 0x06A7, 0x06A7, 0x06A7},
-    {0x06A8, 0x06A8, 0x06A8, 0x06A8}, {0xFB8E, 0xFB8F, 0xFB90, 0xFB91},
-    {0x06AA, 0x06AA, 0x06AA, 0x06AA}, {0x06AB, 0x06AB, 0x06AB, 0x06AB},
-    {0x06AC, 0x06AC, 0x06AC, 0x06AC}, {0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6},
-    {0x06AE, 0x06AE, 0x06AE, 0x06AE}, {0xFB92, 0xFB93, 0xFB94, 0xFB95},
-    {0x06B0, 0x06B0, 0x06B0, 0x06B0}, {0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D},
-    {0x06B2, 0x06B2, 0x06B2, 0x06B2}, {0xFB96, 0xFB97, 0xFB98, 0xFB99},
-    {0x06B4, 0x06B4, 0x06B4, 0x06B4}, {0x06B5, 0x06B5, 0x06B5, 0x06B5},
-    {0x06B6, 0x06B6, 0x06B6, 0x06B6}, {0x06B7, 0x06B7, 0x06B7, 0x06B7},
-    {0x06B8, 0x06B8, 0x06B8, 0x06B8}, {0x06B9, 0x06B9, 0x06B9, 0x06B9},
-    {0xFB9E, 0xFB9F, 0xFBE8, 0xFBE9}, {0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3},
-    {0x06BC, 0x06BC, 0x06BC, 0x06BC}, {0x06BD, 0x06BD, 0x06BD, 0x06BD},
-    {0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}, {0x06BF, 0x06BF, 0x06BF, 0x06BF},
-    {0xFBA4, 0xFBA5, 0xFBA4, 0xFBA5}, {0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9},
-    {0x06C2, 0x06C2, 0x06C2, 0x06C2}, {0x06C3, 0x06C3, 0x06C3, 0x06C3},
-    {0x06C4, 0x06C4, 0x06C4, 0x06C4}, {0xFBE0, 0xFBE1, 0xFBE0, 0xFBE1},
-    {0xFBD9, 0xFBDA, 0xFBD9, 0xFBDA}, {0xFBD7, 0xFBD8, 0xFBD7, 0xFBD8},
-    {0xFBDB, 0xFBDC, 0xFBDB, 0xFBDC}, {0xFBE2, 0xFBE3, 0xFBE2, 0xFBE3},
-    {0x06CA, 0x06CA, 0x06CA, 0x06CA}, {0xFBDE, 0xFBDF, 0xFBDE, 0xFBDF},
-    {0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}, {0x06CD, 0x06CD, 0x06CD, 0x06CD},
-    {0x06CE, 0x06CE, 0x06CE, 0x06CE}, {0x06CF, 0x06CF, 0x06CF, 0x06CF},
-    {0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}, {0x06D1, 0x06D1, 0x06D1, 0x06D1},
-    {0xFBAE, 0xFBAF, 0xFBAE, 0xFBAF}, {0xFBB0, 0xFBB1, 0xFBB0, 0xFBB1},
-    {0x06D4, 0x06D4, 0x06D4, 0x06D4}, {0x06D5, 0x06D5, 0x06D5, 0x06D5},
-};
-
-const FX_ARAALEF gs_FX_AlefTable[] = {
-    {0x0622, 0xFEF5},
-    {0x0623, 0xFEF7},
-    {0x0625, 0xFEF9},
-    {0x0627, 0xFEFB},
-};
-
-const FX_ARASHADDA gs_FX_ShaddaTable[] = {
-    {0x064C, 0xFC5E}, {0x064D, 0xFC5F}, {0x064E, 0xFC60},
-    {0x064F, 0xFC61}, {0x0650, 0xFC62},
-};
-
-const FX_ARBFORMTABLE* GetArabicFormTable(wchar_t unicode) {
-  if (unicode < 0x622 || unicode > 0x6d5)
-    return nullptr;
-  return g_FX_ArabicFormTables + unicode - 0x622;
-}
-
-const FX_ARBFORMTABLE* ParseChar(const CFX_Char* pTC,
-                                 wchar_t* wChar,
-                                 FX_CHARTYPE* eType) {
-  if (!pTC) {
-    *eType = FX_CHARTYPE_Unknown;
-    *wChar = 0xFEFF;
-    return nullptr;
-  }
-
-  *eType = pTC->GetCharType();
-  *wChar = static_cast<wchar_t>(pTC->char_code());
-  const FX_ARBFORMTABLE* pFT = GetArabicFormTable(*wChar);
-  if (!pFT || *eType >= FX_CHARTYPE_ArabicNormal)
-    *eType = FX_CHARTYPE_Unknown;
-
-  return pFT;
-}
-
-wchar_t GetArabicFromAlefTable(wchar_t alef) {
-  static const size_t s_iAlefCount = FX_ArraySize(gs_FX_AlefTable);
-  for (size_t iStart = 0; iStart < s_iAlefCount; iStart++) {
-    const FX_ARAALEF& v = gs_FX_AlefTable[iStart];
-    if (v.wAlef == alef)
-      return v.wIsolated;
-  }
-  return alef;
-}
-
-}  // namespace
-
-namespace pdfium {
-namespace arabic {
-
-wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next) {
-  CFX_Char c(wch, FX_GetUnicodeProperties(wch));
-  CFX_Char p(prev, FX_GetUnicodeProperties(prev));
-  CFX_Char n(next, FX_GetUnicodeProperties(next));
-  return GetFormChar(&c, &p, &n);
-}
-
-wchar_t GetFormChar(const CFX_Char* cur,
-                    const CFX_Char* prev,
-                    const CFX_Char* next) {
-  FX_CHARTYPE eCur;
-  wchar_t wCur;
-  const FX_ARBFORMTABLE* ft = ParseChar(cur, &wCur, &eCur);
-  if (eCur < FX_CHARTYPE_ArabicAlef || eCur >= FX_CHARTYPE_ArabicNormal)
-    return wCur;
-
-  FX_CHARTYPE ePrev;
-  wchar_t wPrev;
-  ParseChar(prev, &wPrev, &ePrev);
-  if (wPrev == 0x0644 && eCur == FX_CHARTYPE_ArabicAlef)
-    return 0xFEFF;
-
-  FX_CHARTYPE eNext;
-  wchar_t wNext;
-  ParseChar(next, &wNext, &eNext);
-  bool bAlef = (eNext == FX_CHARTYPE_ArabicAlef && wCur == 0x644);
-  if (ePrev < FX_CHARTYPE_ArabicAlef) {
-    if (bAlef)
-      return GetArabicFromAlefTable(wNext);
-    return (eNext < FX_CHARTYPE_ArabicAlef) ? ft->wIsolated : ft->wInitial;
-  }
-
-  if (bAlef) {
-    wCur = GetArabicFromAlefTable(wNext);
-    return (ePrev != FX_CHARTYPE_ArabicDistortion) ? wCur : ++wCur;
-  }
-
-  if (ePrev == FX_CHARTYPE_ArabicAlef || ePrev == FX_CHARTYPE_ArabicSpecial)
-    return (eNext < FX_CHARTYPE_ArabicAlef) ? ft->wIsolated : ft->wInitial;
-  return (eNext < FX_CHARTYPE_ArabicAlef) ? ft->wFinal : ft->wMedial;
-}
-
-}  // namespace arabic
-}  // namespace pdfium
-
-wchar_t FX_GetArabicFromShaddaTable(wchar_t shadda) {
-  static const size_t s_iShaddaCount = FX_ArraySize(gs_FX_ShaddaTable);
-  for (size_t iStart = 0; iStart < s_iShaddaCount; iStart++) {
-    const FX_ARASHADDA& v = gs_FX_ShaddaTable[iStart];
-    if (v.wShadda == shadda)
-      return v.wIsolated;
-  }
-  return shadda;
-}
diff --git a/core/fxcrt/fx_arabic.h b/core/fxcrt/fx_arabic.h
deleted file mode 100644
index 9d23b15..0000000
--- a/core/fxcrt/fx_arabic.h
+++ /dev/null
@@ -1,28 +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_FXCRT_FX_ARABIC_H_
-#define CORE_FXCRT_FX_ARABIC_H_
-
-#include <vector>
-
-#include "core/fxcrt/cfx_char.h"
-#include "core/fxcrt/fx_system.h"
-
-namespace pdfium {
-namespace arabic {
-
-wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next);
-wchar_t GetFormChar(const CFX_Char* cur,
-                    const CFX_Char* prev,
-                    const CFX_Char* next);
-
-}  // namespace arabic
-}  // namespace pdfium
-
-wchar_t FX_GetArabicFromShaddaTable(wchar_t shadda);
-
-#endif  // CORE_FXCRT_FX_ARABIC_H_
diff --git a/core/fxcrt/fx_bidi.cpp b/core/fxcrt/fx_bidi.cpp
index db46d99..c5fb5c6 100644
--- a/core/fxcrt/fx_bidi.cpp
+++ b/core/fxcrt/fx_bidi.cpp
@@ -9,552 +9,26 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_unicode.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "core/fxcrt/fx_extension.h"
-#endif  // PDF_ENABLE_XFA
-
-namespace {
-
-enum FX_BIDICLASS {
-  FX_BIDICLASS_ON = 0,    // Other Neutral
-  FX_BIDICLASS_L = 1,     // Left Letter
-  FX_BIDICLASS_R = 2,     // Right Letter
-  FX_BIDICLASS_AN = 3,    // Arabic Number
-  FX_BIDICLASS_EN = 4,    // European Number
-  FX_BIDICLASS_AL = 5,    // Arabic Letter
-  FX_BIDICLASS_NSM = 6,   // Non-spacing Mark
-  FX_BIDICLASS_CS = 7,    // Common Number Separator
-  FX_BIDICLASS_ES = 8,    // European Separator
-  FX_BIDICLASS_ET = 9,    // European Number Terminator
-  FX_BIDICLASS_BN = 10,   // Boundary Neutral
-  FX_BIDICLASS_S = 11,    // Segment Separator
-  FX_BIDICLASS_WS = 12,   // Whitespace
-  FX_BIDICLASS_B = 13,    // Paragraph Separator
-  FX_BIDICLASS_RLO = 14,  // Right-to-Left Override
-  FX_BIDICLASS_RLE = 15,  // Right-to-Left Embedding
-  FX_BIDICLASS_LRO = 16,  // Left-to-Right Override
-  FX_BIDICLASS_LRE = 17,  // Left-to-Right Embedding
-  FX_BIDICLASS_PDF = 18,  // Pop Directional Format
-  FX_BIDICLASS_N = FX_BIDICLASS_ON,
-};
-constexpr uint32_t FX_BIDICLASSBITS = 6;
-constexpr uint32_t FX_BIDICLASSBITSMASK = 0x1F << FX_BIDICLASSBITS;
-
-#ifdef PDF_ENABLE_XFA
-
-#ifndef NDEBUG
-constexpr int32_t kBidiMaxLevel = 61;
-#endif  // NDEBUG
-
-enum FX_BIDIWEAKSTATE {
-  FX_BWSxa = 0,
-  FX_BWSxr,
-  FX_BWSxl,
-  FX_BWSao,
-  FX_BWSro,
-  FX_BWSlo,
-  FX_BWSrt,
-  FX_BWSlt,
-  FX_BWScn,
-  FX_BWSra,
-  FX_BWSre,
-  FX_BWSla,
-  FX_BWSle,
-  FX_BWSac,
-  FX_BWSrc,
-  FX_BWSrs,
-  FX_BWSlc,
-  FX_BWSls,
-  FX_BWSret,
-  FX_BWSlet
-};
-
-enum FX_BIDIWEAKACTION {
-  FX_BWAIX = 0x100,
-  FX_BWAXX = 0x0F,
-  FX_BWAxxx = (0x0F << 4) + 0x0F,
-  FX_BWAxIx = 0x100 + FX_BWAxxx,
-  FX_BWAxxN = (0x0F << 4) + FX_BIDICLASS_ON,
-  FX_BWAxxE = (0x0F << 4) + FX_BIDICLASS_EN,
-  FX_BWAxxA = (0x0F << 4) + FX_BIDICLASS_AN,
-  FX_BWAxxR = (0x0F << 4) + FX_BIDICLASS_R,
-  FX_BWAxxL = (0x0F << 4) + FX_BIDICLASS_L,
-  FX_BWANxx = (FX_BIDICLASS_ON << 4) + 0x0F,
-  FX_BWAAxx = (FX_BIDICLASS_AN << 4) + 0x0F,
-  FX_BWAExE = (FX_BIDICLASS_EN << 4) + FX_BIDICLASS_EN,
-  FX_BWANIx = (FX_BIDICLASS_ON << 4) + 0x0F + 0x100,
-  FX_BWANxN = (FX_BIDICLASS_ON << 4) + FX_BIDICLASS_ON,
-  FX_BWANxR = (FX_BIDICLASS_ON << 4) + FX_BIDICLASS_R,
-  FX_BWANxE = (FX_BIDICLASS_ON << 4) + FX_BIDICLASS_EN,
-  FX_BWAAxA = (FX_BIDICLASS_AN << 4) + FX_BIDICLASS_AN,
-  FX_BWANxL = (FX_BIDICLASS_ON << 4) + FX_BIDICLASS_L,
-  FX_BWALxL = (FX_BIDICLASS_L << 4) + FX_BIDICLASS_L,
-  FX_BWAxIL = (0x0F << 4) + FX_BIDICLASS_L + 0x100,
-  FX_BWAAxR = (FX_BIDICLASS_AN << 4) + FX_BIDICLASS_R,
-  FX_BWALxx = (FX_BIDICLASS_L << 4) + 0x0F,
-};
-
-enum FX_BIDINEUTRALSTATE {
-  FX_BNSr = 0,
-  FX_BNSl,
-  FX_BNSrn,
-  FX_BNSln,
-  FX_BNSa,
-  FX_BNSna
-};
-
-enum FX_BIDINEUTRALACTION {
-  FX_BNAnL = FX_BIDICLASS_L,
-  FX_BNAEn = (FX_BIDICLASS_AN << 4),
-  FX_BNARn = (FX_BIDICLASS_R << 4),
-  FX_BNALn = (FX_BIDICLASS_L << 4),
-  FX_BNAIn = FX_BWAIX,
-  FX_BNALnL = (FX_BIDICLASS_L << 4) + FX_BIDICLASS_L,
-};
-
-const int32_t gc_FX_BidiNTypes[] = {
-    FX_BIDICLASS_N,   FX_BIDICLASS_L,   FX_BIDICLASS_R,   FX_BIDICLASS_AN,
-    FX_BIDICLASS_EN,  FX_BIDICLASS_AL,  FX_BIDICLASS_NSM, FX_BIDICLASS_CS,
-    FX_BIDICLASS_ES,  FX_BIDICLASS_ET,  FX_BIDICLASS_BN,  FX_BIDICLASS_BN,
-    FX_BIDICLASS_N,   FX_BIDICLASS_B,   FX_BIDICLASS_RLO, FX_BIDICLASS_RLE,
-    FX_BIDICLASS_LRO, FX_BIDICLASS_LRE, FX_BIDICLASS_PDF, FX_BIDICLASS_ON,
-};
-
-const int32_t gc_FX_BidiWeakStates[][10] = {
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSxa,
-     FX_BWSao, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSxr,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSxl,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
-     FX_BWSao, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSrt,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlt,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWScn,
-     FX_BWSac, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSra,
-     FX_BWSrc, FX_BWSro, FX_BWSrt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSre,
-     FX_BWSrs, FX_BWSrs, FX_BWSret},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSla,
-     FX_BWSlc, FX_BWSlo, FX_BWSlt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSle,
-     FX_BWSls, FX_BWSls, FX_BWSlet},
-    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
-     FX_BWSao, FX_BWSao, FX_BWSao},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
-     FX_BWSro, FX_BWSro, FX_BWSrt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
-     FX_BWSlo, FX_BWSlo, FX_BWSlt},
-    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSret,
-     FX_BWSro, FX_BWSro, FX_BWSret},
-    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlet,
-     FX_BWSlo, FX_BWSlo, FX_BWSlet},
-};
-
-const int32_t gc_FX_BidiWeakActions[][10] = {
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
-     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
-     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
-     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
-     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
-     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxxN},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxE, FX_BWAxIx, FX_BWAxIx, FX_BWAxxE},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxL, FX_BWAxIx, FX_BWAxIx, FX_BWAxxL},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWAAxA, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANxN},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxE, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxL, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
-     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
-     FX_BWAxxE, FX_BWAxxN, FX_BWAxxN, FX_BWAxxE},
-    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
-     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxxL},
-};
-
-const int32_t gc_FX_BidiNeutralStates[][5] = {
-    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
-    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
-    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
-};
-const int32_t gc_FX_BidiNeutralActions[][5] = {
-    {FX_BNAIn, 0, 0, 0, 0},
-    {FX_BNAIn, 0, 0, 0, FX_BIDICLASS_L},
-    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNARn},
-    {FX_BNAIn, FX_BNALn, FX_BNAEn, FX_BNAEn, FX_BNALnL},
-    {FX_BNAIn, 0, 0, 0, FX_BIDICLASS_L},
-    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNAEn},
-};
-
-const int32_t gc_FX_BidiAddLevel[][4] = {
-    {0, 1, 2, 2},
-    {1, 0, 1, 1},
-};
-
-class CFX_BidiLine {
- public:
-  void BidiLine(std::vector<CFX_Char>* chars, size_t iCount) {
-    ASSERT(iCount <= chars->size());
-    if (iCount < 2)
-      return;
-
-    Classify(chars, iCount, false);
-    ResolveExplicit(chars, iCount);
-    ResolveWeak(chars, iCount);
-    ResolveNeutrals(chars, iCount);
-    ResolveImplicit(chars, iCount);
-    Classify(chars, iCount, true);
-    ResolveWhitespace(chars, iCount);
-    Reorder(chars, iCount);
-    Position(chars, iCount);
-  }
-
- private:
-  int32_t Direction(int32_t val) {
-    return FX_IsOdd(val) ? FX_BIDICLASS_R : FX_BIDICLASS_L;
-  }
-
-  int32_t GetDeferredType(int32_t val) { return (val >> 4) & 0x0F; }
-
-  int32_t GetResolvedType(int32_t val) { return val & 0x0F; }
-
-  int32_t GetDeferredNeutrals(int32_t iAction, int32_t iLevel) {
-    iAction = (iAction >> 4) & 0xF;
-    if (iAction == (FX_BNAEn >> 4))
-      return Direction(iLevel);
-    return iAction;
-  }
-
-  int32_t GetResolvedNeutrals(int32_t iAction) {
-    iAction &= 0xF;
-    return iAction == FX_BNAIn ? 0 : iAction;
-  }
-
-  void ReverseString(std::vector<CFX_Char>* chars,
-                     size_t iStart,
-                     size_t iCount) {
-    ASSERT(pdfium::IndexInBounds(*chars, iStart));
-    ASSERT(iStart + iCount <= chars->size());
-
-    std::reverse(chars->begin() + iStart, chars->begin() + iStart + iCount);
-  }
-
-  void SetDeferredRun(std::vector<CFX_Char>* chars,
-                      bool bClass,
-                      size_t iStart,
-                      size_t iCount,
-                      int32_t iValue) {
-    ASSERT(iStart <= chars->size());
-    ASSERT(iStart >= iCount);
-
-    size_t iLast = iStart - iCount;
-    for (size_t i = iStart - 1; i >= iLast; --i) {
-      if (bClass)
-        (*chars)[i].m_iBidiClass = static_cast<int16_t>(iValue);
-      else
-        (*chars)[i].m_iBidiLevel = static_cast<int16_t>(iValue);
-
-      if (i == 0)
-        break;
-    }
-  }
-
-  void Classify(std::vector<CFX_Char>* chars, size_t iCount, bool bWS) {
-    if (bWS) {
-      for (size_t i = 0; i < iCount; ++i) {
-        CFX_Char& cur = (*chars)[i];
-        cur.m_iBidiClass =
-            static_cast<int16_t>(cur.char_props() & FX_BIDICLASSBITSMASK) >>
-            FX_BIDICLASSBITS;
-      }
-      return;
-    }
-
-    for (size_t i = 0; i < iCount; ++i) {
-      CFX_Char& cur = (*chars)[i];
-      cur.m_iBidiClass = static_cast<int16_t>(
-          gc_FX_BidiNTypes[(cur.char_props() & FX_BIDICLASSBITSMASK) >>
-                           FX_BIDICLASSBITS]);
-    }
-  }
-
-  void ResolveExplicit(std::vector<CFX_Char>* chars, size_t iCount) {
-    for (size_t i = 0; i < iCount; ++i)
-      (*chars)[i].m_iBidiLevel = 0;
-  }
-
-  void ResolveWeak(std::vector<CFX_Char>* chars, size_t iCount) {
-    if (iCount <= 1)
-      return;
-    --iCount;
-
-    int32_t iLevelCur = 0;
-    int32_t iState = FX_BWSxl;
-    size_t i = 0;
-    size_t iNum = 0;
-    int32_t iClsCur;
-    int32_t iClsRun;
-    int32_t iClsNew;
-    int32_t iAction;
-    for (; i <= iCount; ++i) {
-      CFX_Char* pTC = &(*chars)[i];
-      iClsCur = pTC->m_iBidiClass;
-      if (iClsCur == FX_BIDICLASS_BN) {
-        pTC->m_iBidiLevel = (int16_t)iLevelCur;
-        if (i == iCount && iLevelCur != 0) {
-          iClsCur = Direction(iLevelCur);
-          pTC->m_iBidiClass = (int16_t)iClsCur;
-        } else if (i < iCount) {
-          CFX_Char* pTCNext = &(*chars)[i + 1];
-          int32_t iLevelNext, iLevelNew;
-          iClsNew = pTCNext->m_iBidiClass;
-          iLevelNext = pTCNext->m_iBidiLevel;
-          if (iClsNew != FX_BIDICLASS_BN && iLevelCur != iLevelNext) {
-            iLevelNew = std::max(iLevelNext, iLevelCur);
-            pTC->m_iBidiLevel = static_cast<int16_t>(iLevelNew);
-            iClsCur = Direction(iLevelNew);
-            pTC->m_iBidiClass = static_cast<int16_t>(iClsCur);
-            iLevelCur = iLevelNext;
-          } else {
-            if (iNum > 0)
-              ++iNum;
-            continue;
-          }
-        } else {
-          if (iNum > 0)
-            ++iNum;
-          continue;
-        }
-      }
-
-      ASSERT(iClsCur <= FX_BIDICLASS_BN);
-      iAction = gc_FX_BidiWeakActions[iState][iClsCur];
-      iClsRun = GetDeferredType(iAction);
-      if (iClsRun != FX_BWAXX && iNum > 0) {
-        SetDeferredRun(chars, true, i, iNum, iClsRun);
-        iNum = 0;
-      }
-      iClsNew = GetResolvedType(iAction);
-      if (iClsNew != FX_BWAXX)
-        pTC->m_iBidiClass = static_cast<int16_t>(iClsNew);
-      if (FX_BWAIX & iAction)
-        ++iNum;
-
-      iState = gc_FX_BidiWeakStates[iState][iClsCur];
-    }
-    if (iNum == 0)
-      return;
-
-    iClsCur = Direction(0);
-    iClsRun = GetDeferredType(gc_FX_BidiWeakActions[iState][iClsCur]);
-    if (iClsRun != FX_BWAXX)
-      SetDeferredRun(chars, true, i, iNum, iClsRun);
-  }
-
-  void ResolveNeutrals(std::vector<CFX_Char>* chars, size_t iCount) {
-    if (iCount <= 1)
-      return;
-    --iCount;
-
-    CFX_Char* pTC;
-    int32_t iLevel = 0;
-    int32_t iState = FX_BNSl;
-    size_t i = 0;
-    size_t iNum = 0;
-    int32_t iClsCur;
-    int32_t iClsRun;
-    int32_t iClsNew;
-    int32_t iAction;
-    for (; i <= iCount; ++i) {
-      pTC = &(*chars)[i];
-      iClsCur = pTC->m_iBidiClass;
-      if (iClsCur == FX_BIDICLASS_BN) {
-        if (iNum)
-          ++iNum;
-        continue;
-      }
-
-      ASSERT(iClsCur < FX_BIDICLASS_AL);
-      iAction = gc_FX_BidiNeutralActions[iState][iClsCur];
-      iClsRun = GetDeferredNeutrals(iAction, iLevel);
-      if (iClsRun != FX_BIDICLASS_N && iNum > 0) {
-        SetDeferredRun(chars, true, i, iNum, iClsRun);
-        iNum = 0;
-      }
-
-      iClsNew = GetResolvedNeutrals(iAction);
-      if (iClsNew != FX_BIDICLASS_N)
-        pTC->m_iBidiClass = (int16_t)iClsNew;
-      if (FX_BNAIn & iAction)
-        ++iNum;
-
-      iState = gc_FX_BidiNeutralStates[iState][iClsCur];
-      iLevel = pTC->m_iBidiLevel;
-    }
-    if (iNum == 0)
-      return;
-
-    iClsCur = Direction(iLevel);
-    iClsRun =
-        GetDeferredNeutrals(gc_FX_BidiNeutralActions[iState][iClsCur], iLevel);
-    if (iClsRun != FX_BIDICLASS_N)
-      SetDeferredRun(chars, true, i, iNum, iClsRun);
-  }
-
-  void ResolveImplicit(std::vector<CFX_Char>* chars, size_t iCount) {
-    for (size_t i = 0; i < iCount; ++i) {
-      int32_t iCls = (*chars)[i].m_iBidiClass;
-      if (iCls == FX_BIDICLASS_BN)
-        continue;
-
-      ASSERT(iCls > FX_BIDICLASS_ON && iCls < FX_BIDICLASS_AL);
-      int32_t iLevel = (*chars)[i].m_iBidiLevel;
-      iLevel += gc_FX_BidiAddLevel[FX_IsOdd(iLevel)][iCls - 1];
-      (*chars)[i].m_iBidiLevel = (int16_t)iLevel;
-    }
-  }
-
-  void ResolveWhitespace(std::vector<CFX_Char>* chars, size_t iCount) {
-    if (iCount <= 1)
-      return;
-    iCount--;
-
-    int32_t iLevel = 0;
-    size_t i = 0;
-    size_t iNum = 0;
-    for (; i <= iCount; ++i) {
-      switch ((*chars)[i].m_iBidiClass) {
-        case FX_BIDICLASS_WS:
-          ++iNum;
-          break;
-        case FX_BIDICLASS_RLE:
-        case FX_BIDICLASS_LRE:
-        case FX_BIDICLASS_LRO:
-        case FX_BIDICLASS_RLO:
-        case FX_BIDICLASS_PDF:
-        case FX_BIDICLASS_BN:
-          (*chars)[i].m_iBidiLevel = static_cast<int16_t>(iLevel);
-          ++iNum;
-          break;
-        case FX_BIDICLASS_S:
-        case FX_BIDICLASS_B:
-          if (iNum > 0)
-            SetDeferredRun(chars, false, i, iNum, 0);
-
-          (*chars)[i].m_iBidiLevel = 0;
-          iNum = 0;
-          break;
-        default:
-          iNum = 0;
-          break;
-      }
-      iLevel = (*chars)[i].m_iBidiLevel;
-    }
-    if (iNum > 0)
-      SetDeferredRun(chars, false, i, iNum, 0);
-  }
-
-  size_t ReorderLevel(std::vector<CFX_Char>* chars,
-                      size_t iCount,
-                      int32_t iBaseLevel,
-                      size_t iStart,
-                      bool bReverse) {
-    ASSERT(iBaseLevel >= 0 && iBaseLevel <= kBidiMaxLevel);
-    ASSERT(iStart < iCount);
-
-    if (iCount < 1)
-      return 0;
-
-    bReverse = bReverse || FX_IsOdd(iBaseLevel);
-    size_t i = iStart;
-    for (; i < iCount; ++i) {
-      int32_t iLevel = (*chars)[i].m_iBidiLevel;
-      if (iLevel == iBaseLevel)
-        continue;
-      if (iLevel < iBaseLevel)
-        break;
-
-      i += ReorderLevel(chars, iCount, iBaseLevel + 1, i, bReverse) - 1;
-    }
-
-    size_t iNum = i - iStart;
-    if (bReverse && iNum > 1)
-      ReverseString(chars, iStart, iNum);
-
-    return iNum;
-  }
-
-  void Reorder(std::vector<CFX_Char>* chars, size_t iCount) {
-    for (size_t i = 0; i < iCount;)
-      i += ReorderLevel(chars, iCount, 0, i, false);
-  }
-
-  void Position(std::vector<CFX_Char>* chars, size_t iCount) {
-    for (size_t i = 0; i < iCount; ++i)
-      (*chars)[(*chars)[i].m_iBidiPos].m_iBidiOrder = i;
-  }
-};
-
-#endif  // PDF_ENABLE_XFA
-
-}  // namespace
+#include "third_party/base/stl_util.h"
 
 CFX_BidiChar::CFX_BidiChar()
     : m_CurrentSegment({0, 0, NEUTRAL}), m_LastSegment({0, 0, NEUTRAL}) {}
 
 bool CFX_BidiChar::AppendChar(wchar_t wch) {
-  uint32_t dwProps = FX_GetUnicodeProperties(wch);
-  int32_t iBidiCls = (dwProps & FX_BIDICLASSBITSMASK) >> FX_BIDICLASSBITS;
-  Direction direction = NEUTRAL;
-  switch (iBidiCls) {
-    case FX_BIDICLASS_L:
-    case FX_BIDICLASS_AN:
-    case FX_BIDICLASS_EN:
+  Direction direction;
+  switch (FX_GetBidiClass(wch)) {
+    case FX_BIDICLASS::kL:
+    case FX_BIDICLASS::kAN:
+    case FX_BIDICLASS::kEN:
       direction = LEFT;
       break;
-    case FX_BIDICLASS_R:
-    case FX_BIDICLASS_AL:
+    case FX_BIDICLASS::kR:
+    case FX_BIDICLASS::kAL:
       direction = RIGHT;
       break;
+    default:
+      direction = NEUTRAL;
+      break;
   }
 
   bool bChangeDirection = (direction != m_CurrentSegment.direction);
@@ -577,16 +51,14 @@
   m_CurrentSegment.direction = direction;
 }
 
-CFX_BidiString::CFX_BidiString(const WideString& str)
-    : m_Str(str),
-      m_pBidiChar(pdfium::MakeUnique<CFX_BidiChar>()),
-      m_eOverallDirection(CFX_BidiChar::LEFT) {
-  for (const auto& c : m_Str) {
-    if (m_pBidiChar->AppendChar(c))
-      m_Order.push_back(m_pBidiChar->GetSegmentInfo());
+CFX_BidiString::CFX_BidiString(const WideString& str) : m_Str(str) {
+  CFX_BidiChar bidi;
+  for (wchar_t c : m_Str) {
+    if (bidi.AppendChar(c))
+      m_Order.push_back(bidi.GetSegmentInfo());
   }
-  if (m_pBidiChar->EndChar())
-    m_Order.push_back(m_pBidiChar->GetSegmentInfo());
+  if (bidi.EndChar())
+    m_Order.push_back(bidi.GetSegmentInfo());
 
   size_t nR2L = std::count_if(m_Order.begin(), m_Order.end(),
                               [](const CFX_BidiChar::Segment& seg) {
@@ -604,16 +76,14 @@
 
 CFX_BidiString::~CFX_BidiString() {}
 
+CFX_BidiChar::Direction CFX_BidiString::OverallDirection() const {
+  ASSERT(m_eOverallDirection != CFX_BidiChar::NEUTRAL);
+  return m_eOverallDirection;
+}
+
 void CFX_BidiString::SetOverallDirectionRight() {
   if (m_eOverallDirection != CFX_BidiChar::RIGHT) {
     std::reverse(m_Order.begin(), m_Order.end());
     m_eOverallDirection = CFX_BidiChar::RIGHT;
   }
 }
-
-#ifdef PDF_ENABLE_XFA
-void FX_BidiLine(std::vector<CFX_Char>* chars, size_t iCount) {
-  CFX_BidiLine blt;
-  blt.BidiLine(chars, iCount);
-}
-#endif  // PDF_ENABLE_XFA
diff --git a/core/fxcrt/fx_bidi.h b/core/fxcrt/fx_bidi.h
index a9b52cf..ade9a2a 100644
--- a/core/fxcrt/fx_bidi.h
+++ b/core/fxcrt/fx_bidi.h
@@ -7,16 +7,11 @@
 #ifndef CORE_FXCRT_FX_BIDI_H_
 #define CORE_FXCRT_FX_BIDI_H_
 
-#include <memory>
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 
-#ifdef PDF_ENABLE_XFA
-#include "core/fxcrt/cfx_char.h"
-#endif  // PDF_ENABLE_XFA
-
 // Processes characters and group them into segments based on text direction.
 class CFX_BidiChar {
  public:
@@ -41,7 +36,7 @@
 
   // Call after a change in direction is indicated by the above to get
   // information about the segment to process.
-  Segment GetSegmentInfo() const { return m_LastSegment; }
+  const Segment& GetSegmentInfo() const { return m_LastSegment; }
 
  private:
   void StartNewSegment(CFX_BidiChar::Direction direction);
@@ -58,26 +53,18 @@
   ~CFX_BidiString();
 
   // Overall direction is always LEFT or RIGHT, never NEUTRAL.
-  CFX_BidiChar::Direction OverallDirection() const {
-    return m_eOverallDirection;
-  }
+  CFX_BidiChar::Direction OverallDirection() const;
 
   // Force the overall direction to be R2L regardless of what was detected.
   void SetOverallDirectionRight();
 
-  wchar_t CharAt(size_t x) const { return m_Str[x]; }
   const_iterator begin() const { return m_Order.begin(); }
   const_iterator end() const { return m_Order.end(); }
 
  private:
-  const WideString m_Str;
-  std::unique_ptr<CFX_BidiChar> m_pBidiChar;
+  const WideString& m_Str;
   std::vector<CFX_BidiChar::Segment> m_Order;
-  CFX_BidiChar::Direction m_eOverallDirection;
+  CFX_BidiChar::Direction m_eOverallDirection = CFX_BidiChar::LEFT;
 };
 
-#if PDF_ENABLE_XFA
-void FX_BidiLine(std::vector<CFX_Char>* chars, size_t iCount);
-#endif  // PDF_ENABLE_XFA
-
 #endif  // CORE_FXCRT_FX_BIDI_H_
diff --git a/core/fxcrt/fx_codepage.cpp b/core/fxcrt/fx_codepage.cpp
new file mode 100644
index 0000000..c85291f
--- /dev/null
+++ b/core/fxcrt/fx_codepage.cpp
@@ -0,0 +1,237 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/fx_codepage.h"
+
+#include <algorithm>
+#include <iterator>
+#include <utility>
+
+namespace {
+
+const uint16_t g_FX_MSDOSThaiUnicodes[128] = {
+    0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00A0, 0x0E01, 0x0E02, 0x0E03,
+    0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C,
+    0x0E0D, 0x0E0E, 0x0E0F, 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15,
+    0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E,
+    0x0E1F, 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
+    0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, 0x0E30,
+    0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39,
+    0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F, 0x0E40, 0x0E41, 0x0E42,
+    0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B,
+    0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54,
+    0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000,
+    0x0000, 0x0000,
+};
+
+const uint16_t g_FX_MSWinEasternEuropeanUnicodes[128] = {
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
+    0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, 0x0000, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0161,
+    0x203A, 0x015B, 0x0165, 0x017E, 0x017A, 0x00A0, 0x02C7, 0x02D8, 0x0141,
+    0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC,
+    0x00AD, 0x00AE, 0x017B, 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5,
+    0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E,
+    0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, 0x0110,
+    0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E,
+    0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155, 0x00E1, 0x00E2,
+    0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB,
+    0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4,
+    0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD,
+    0x0163, 0x02D9,
+};
+
+const uint16_t g_FX_MSWinCyrillicUnicodes[128] = {
+    0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC,
+    0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, 0x0452, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0459,
+    0x203A, 0x045A, 0x045C, 0x045B, 0x045F, 0x00A0, 0x040E, 0x045E, 0x0408,
+    0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC,
+    0x00AD, 0x00AE, 0x0407, 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5,
+    0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455,
+    0x0457, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420,
+    0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429,
+    0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432,
+    0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B,
+    0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444,
+    0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D,
+    0x044E, 0x044F,
+};
+
+const uint16_t g_FX_MSWinGreekUnicodes[128] = {
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
+    0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000,
+    0x203A, 0x0000, 0x0000, 0x0000, 0x0000, 0x00A0, 0x0385, 0x0386, 0x00A3,
+    0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC,
+    0x00AD, 0x00AE, 0x2015, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5,
+    0x00B6, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E,
+    0x038F, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+    0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+    0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,
+    0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, 0x03B1, 0x03B2,
+    0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB,
+    0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4,
+    0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD,
+    0x03CE, 0x0000,
+};
+
+const uint16_t g_FX_MSWinTurkishUnicodes[128] = {
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
+    0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161,
+    0x203A, 0x0153, 0x0000, 0x0000, 0x0178, 0x00A0, 0x00A1, 0x00A2, 0x00A3,
+    0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC,
+    0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
+    0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
+    0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x011E,
+    0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9,
+    0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, 0x00E0, 0x00E1, 0x00E2,
+    0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB,
+    0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4,
+    0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131,
+    0x015F, 0x00FF,
+};
+
+const uint16_t g_FX_MSWinHebrewUnicodes[128] = {
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
+    0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000,
+    0x203A, 0x0000, 0x0000, 0x0000, 0x0000, 0x00A0, 0x00A1, 0x00A2, 0x00A3,
+    0x20AA, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC,
+    0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
+    0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
+    0x00BF, 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
+    0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, 0x05C0,
+    0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x05D0, 0x05D1, 0x05D2,
+    0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB,
+    0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4,
+    0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E,
+    0x200F, 0x0000,
+};
+
+const uint16_t g_FX_MSWinArabicUnicodes[128] = {
+    0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
+    0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, 0x06AF, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691,
+    0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, 0x00A0, 0x060C, 0x00A2, 0x00A3,
+    0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC,
+    0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
+    0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
+    0x061F, 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630,
+    0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, 0x0637, 0x0638,
+    0x0639, 0x063A, 0x0640, 0x0641, 0x0642, 0x0643, 0x00E0, 0x0644, 0x00E2,
+    0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB,
+    0x0649, 0x064A, 0x00EE, 0x00EF, 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4,
+    0x064F, 0x0650, 0x00F7, 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E,
+    0x200F, 0x06D2,
+};
+
+const uint16_t g_FX_MSWinBalticUnicodes[128] = {
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000,
+    0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, 0x0000, 0x2018,
+    0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000,
+    0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000, 0x00A0, 0x0000, 0x00A2, 0x00A3,
+    0x00A4, 0x0000, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC,
+    0x00AD, 0x00AE, 0x00C6, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5,
+    0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE,
+    0x00E6, 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
+    0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, 0x0160,
+    0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141,
+    0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, 0x0105, 0x012F, 0x0101,
+    0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117,
+    0x0123, 0x0137, 0x012B, 0x013C, 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D,
+    0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C,
+    0x017E, 0x02D9,
+};
+
+struct FX_CHARSET_MAP {
+  uint16_t charset;
+  uint16_t codepage;
+};
+
+const FX_CHARSET_MAP g_FXCharset2CodePageTable[] = {
+    {FX_CHARSET_ANSI, FX_CODEPAGE_MSWin_WesternEuropean},
+    {FX_CHARSET_Default, FX_CODEPAGE_DefANSI},
+    {FX_CHARSET_Symbol, FX_CODEPAGE_Symbol},
+    {FX_CHARSET_MAC_Roman, FX_CODEPAGE_MAC_Roman},
+    {FX_CHARSET_MAC_ShiftJIS, FX_CODEPAGE_MAC_ShiftJIS},
+    {FX_CHARSET_MAC_Korean, FX_CODEPAGE_MAC_Korean},
+    {FX_CHARSET_MAC_ChineseSimplified, FX_CODEPAGE_MAC_ChineseSimplified},
+    {FX_CHARSET_MAC_ChineseTraditional, FX_CODEPAGE_MAC_ChineseTraditional},
+    {FX_CHARSET_MAC_Hebrew, FX_CODEPAGE_MAC_Hebrew},
+    {FX_CHARSET_MAC_Arabic, FX_CODEPAGE_MAC_Arabic},
+    {FX_CHARSET_MAC_Greek, FX_CODEPAGE_MAC_Greek},
+    {FX_CHARSET_MAC_Turkish, FX_CODEPAGE_MAC_Turkish},
+    {FX_CHARSET_MAC_Thai, FX_CODEPAGE_MAC_Thai},
+    {FX_CHARSET_MAC_EasternEuropean, FX_CODEPAGE_MAC_EasternEuropean},
+    {FX_CHARSET_MAC_Cyrillic, FX_CODEPAGE_MAC_Cyrillic},
+    {FX_CHARSET_ShiftJIS, FX_CODEPAGE_ShiftJIS},
+    {FX_CHARSET_Hangul, FX_CODEPAGE_Hangul},
+    {FX_CHARSET_Johab, FX_CODEPAGE_Johab},
+    {FX_CHARSET_ChineseSimplified, FX_CODEPAGE_ChineseSimplified},
+    {FX_CHARSET_ChineseTraditional, FX_CODEPAGE_ChineseTraditional},
+    {FX_CHARSET_MSWin_Greek, FX_CODEPAGE_MSWin_Greek},
+    {FX_CHARSET_MSWin_Turkish, FX_CODEPAGE_MSWin_Turkish},
+    {FX_CHARSET_MSWin_Vietnamese, FX_CODEPAGE_MSWin_Vietnamese},
+    {FX_CHARSET_MSWin_Hebrew, FX_CODEPAGE_MSWin_Hebrew},
+    {FX_CHARSET_MSWin_Arabic, FX_CODEPAGE_MSWin_Arabic},
+    {FX_CHARSET_MSWin_Baltic, FX_CODEPAGE_MSWin_Baltic},
+    {FX_CHARSET_MSWin_Cyrillic, FX_CODEPAGE_MSWin_Cyrillic},
+    {FX_CHARSET_Thai, FX_CODEPAGE_MSDOS_Thai},
+    {FX_CHARSET_MSWin_EasternEuropean, FX_CODEPAGE_MSWin_EasternEuropean},
+    {FX_CHARSET_US, FX_CODEPAGE_MSDOS_US},
+    {FX_CHARSET_OEM, FX_CODEPAGE_MSDOS_WesternEuropean},
+};
+
+}  // namespace
+
+const FX_CharsetUnicodes g_FX_CharsetUnicodes[8] = {
+    {FX_CHARSET_Thai, g_FX_MSDOSThaiUnicodes},
+    {FX_CHARSET_MSWin_EasternEuropean, g_FX_MSWinEasternEuropeanUnicodes},
+    {FX_CHARSET_MSWin_Cyrillic, g_FX_MSWinCyrillicUnicodes},
+    {FX_CHARSET_MSWin_Greek, g_FX_MSWinGreekUnicodes},
+    {FX_CHARSET_MSWin_Turkish, g_FX_MSWinTurkishUnicodes},
+    {FX_CHARSET_MSWin_Hebrew, g_FX_MSWinHebrewUnicodes},
+    {FX_CHARSET_MSWin_Arabic, g_FX_MSWinArabicUnicodes},
+    {FX_CHARSET_MSWin_Baltic, g_FX_MSWinBalticUnicodes},
+};
+
+uint16_t FX_GetCodePageFromCharset(uint8_t charset) {
+  auto* result =
+      std::lower_bound(std::begin(g_FXCharset2CodePageTable),
+                       std::end(g_FXCharset2CodePageTable), charset,
+                       [](const FX_CHARSET_MAP& iter, const uint16_t& charset) {
+                         return iter.charset < charset;
+                       });
+  if (result != std::end(g_FXCharset2CodePageTable) &&
+      result->charset == charset) {
+    return result->codepage;
+  }
+  return 0xFFFF;
+}
+
+uint8_t FX_GetCharsetFromCodePage(uint16_t codepage) {
+  for (const auto& it : g_FXCharset2CodePageTable) {
+    if (it.codepage == codepage)
+      return it.charset;
+  }
+  return FX_CHARSET_ANSI;
+}
+
+bool FX_CharSetIsCJK(uint8_t uCharset) {
+  return (uCharset == FX_CHARSET_ChineseSimplified) ||
+         (uCharset == FX_CHARSET_ChineseTraditional) ||
+         (uCharset == FX_CHARSET_Hangul) || (uCharset == FX_CHARSET_ShiftJIS);
+}
diff --git a/core/fxcrt/fx_codepage.h b/core/fxcrt/fx_codepage.h
index 874f96d..4a6c6d8 100644
--- a/core/fxcrt/fx_codepage.h
+++ b/core/fxcrt/fx_codepage.h
@@ -7,6 +7,8 @@
 #ifndef CORE_FXCRT_FX_CODEPAGE_H_
 #define CORE_FXCRT_FX_CODEPAGE_H_
 
+#include <stdint.h>
+
 #define FX_CODEPAGE_DefANSI 0
 #define FX_CODEPAGE_Symbol 42
 #define FX_CODEPAGE_MSDOS_US 437
@@ -88,4 +90,16 @@
 #define FX_CHARSET_US 254
 #define FX_CHARSET_OEM 255
 
+// Hi-bytes to unicode codepoint mapping for various code pages.
+struct FX_CharsetUnicodes {
+  uint8_t m_Charset;
+  const uint16_t* m_pUnicodes;
+};
+
+extern const FX_CharsetUnicodes g_FX_CharsetUnicodes[8];
+
+uint16_t FX_GetCodePageFromCharset(uint8_t charset);
+uint8_t FX_GetCharsetFromCodePage(uint16_t codepage);
+bool FX_CharSetIsCJK(uint8_t uCharset);
+
 #endif  // CORE_FXCRT_FX_CODEPAGE_H_
diff --git a/core/fxcrt/fx_coordinates.cpp b/core/fxcrt/fx_coordinates.cpp
index ac13a32..f76df5f 100644
--- a/core/fxcrt/fx_coordinates.cpp
+++ b/core/fxcrt/fx_coordinates.cpp
@@ -6,24 +6,53 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 
-#include <algorithm>
 #include <utility>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
 void MatchFloatRange(float f1, float f2, int* i1, int* i2) {
-  int length = static_cast<int>(ceil(f2 - f1));
-  int i1_1 = static_cast<int>(floor(f1));
-  int i1_2 = static_cast<int>(ceil(f1));
-  float error1 = f1 - i1_1 + fabsf(f2 - i1_1 - length);
-  float error2 = i1_2 - f1 + fabsf(f2 - i1_2 - length);
-
-  *i1 = error1 > error2 ? i1_2 : i1_1;
-  *i2 = *i1 + length;
+  float length = ceilf(f2 - f1);
+  float f1_floor = floorf(f1);
+  float f1_ceil = ceilf(f1);
+  float error1 = f1 - f1_floor + fabsf(f2 - f1_floor - length);
+  float error2 = f1_ceil - f1 + fabsf(f2 - f1_ceil - length);
+  float start = error1 > error2 ? f1_ceil : f1_floor;
+  FX_SAFE_INT32 safe1 = start;
+  FX_SAFE_INT32 safe2 = start + length;
+  if (safe1.IsValid() && safe2.IsValid()) {
+    *i1 = safe1.ValueOrDie();
+    *i2 = safe2.ValueOrDie();
+  } else {
+    *i1 = 0;
+    *i2 = 0;
+  }
 }
 
+#if defined(OS_WIN)
+static_assert(sizeof(FX_RECT) == sizeof(RECT), "FX_RECT vs. RECT mismatch");
+static_assert(offsetof(FX_RECT, left) == offsetof(RECT, left),
+              "FX_RECT vs. RECT mismatch");
+static_assert(offsetof(FX_RECT, top) == offsetof(RECT, top),
+              "FX_RECT vs. RECT mismatch");
+static_assert(offsetof(FX_RECT, right) == offsetof(RECT, right),
+              "FX_RECT vs. RECT mismatch");
+static_assert(offsetof(FX_RECT, bottom) == offsetof(RECT, bottom),
+              "FX_RECT vs. RECT mismatch");
+static_assert(sizeof(FX_RECT::left) == sizeof(RECT::left),
+              "FX_RECT vs. RECT mismatch");
+static_assert(sizeof(FX_RECT::top) == sizeof(RECT::top),
+              "FX_RECT vs. RECT mismatch");
+static_assert(sizeof(FX_RECT::right) == sizeof(RECT::right),
+              "FX_RECT vs. RECT mismatch");
+static_assert(sizeof(FX_RECT::bottom) == sizeof(RECT::bottom),
+              "FX_RECT vs. RECT mismatch");
+#endif
+
 }  // namespace
 
 void FX_RECT::Normalize() {
@@ -46,12 +75,9 @@
   }
 }
 
-CFX_FloatRect::CFX_FloatRect(const FX_RECT& rect) {
-  left = rect.left;
-  top = rect.bottom;
-  right = rect.right;
-  bottom = rect.top;
-}
+// Y-axis runs the opposite way in FX_RECT.
+CFX_FloatRect::CFX_FloatRect(const FX_RECT& rect)
+    : left(rect.left), bottom(rect.top), right(rect.right), top(rect.bottom) {}
 
 // static
 CFX_FloatRect CFX_FloatRect::GetBBox(const CFX_PointF* pPoints, int nPoints) {
@@ -78,13 +104,6 @@
     std::swap(top, bottom);
 }
 
-void CFX_FloatRect::Reset() {
-  left = 0.0f;
-  right = 0.0f;
-  bottom = 0.0f;
-  top = 0.0f;
-}
-
 void CFX_FloatRect::Intersect(const CFX_FloatRect& other_rect) {
   Normalize();
   CFX_FloatRect other = other_rect;
@@ -94,7 +113,7 @@
   right = std::min(right, other.right);
   top = std::min(top, other.top);
   if (left > right || bottom > top)
-    Reset();
+    *this = CFX_FloatRect();
 }
 
 void CFX_FloatRect::Union(const CFX_FloatRect& other_rect) {
@@ -109,20 +128,20 @@
 
 FX_RECT CFX_FloatRect::GetOuterRect() const {
   FX_RECT rect;
-  rect.left = static_cast<int>(floor(left));
-  rect.bottom = static_cast<int>(ceil(top));
-  rect.right = static_cast<int>(ceil(right));
-  rect.top = static_cast<int>(floor(bottom));
+  rect.left = pdfium::base::saturated_cast<int>(floor(left));
+  rect.bottom = pdfium::base::saturated_cast<int>(ceil(top));
+  rect.right = pdfium::base::saturated_cast<int>(ceil(right));
+  rect.top = pdfium::base::saturated_cast<int>(floor(bottom));
   rect.Normalize();
   return rect;
 }
 
 FX_RECT CFX_FloatRect::GetInnerRect() const {
   FX_RECT rect;
-  rect.left = static_cast<int>(ceil(left));
-  rect.bottom = static_cast<int>(floor(top));
-  rect.right = static_cast<int>(floor(right));
-  rect.top = static_cast<int>(ceil(bottom));
+  rect.left = pdfium::base::saturated_cast<int>(ceil(left));
+  rect.bottom = pdfium::base::saturated_cast<int>(floor(top));
+  rect.right = pdfium::base::saturated_cast<int>(floor(right));
+  rect.top = pdfium::base::saturated_cast<int>(ceil(bottom));
   rect.Normalize();
   return rect;
 }
@@ -136,8 +155,8 @@
 }
 
 CFX_FloatRect CFX_FloatRect::GetCenterSquare() const {
-  float fWidth = right - left;
-  float fHeight = top - bottom;
+  float fWidth = Width();
+  float fHeight = Height();
   float fHalfWidth = (fWidth > fHeight) ? fHeight / 2 : fWidth / 2;
 
   float fCenterX = (left + right) / 2.0f;
@@ -169,6 +188,57 @@
   top = std::max(top, point.y);
 }
 
+void CFX_FloatRect::Inflate(float x, float y) {
+  Inflate(x, y, x, y);
+}
+
+void CFX_FloatRect::Inflate(float other_left,
+                            float other_bottom,
+                            float other_right,
+                            float other_top) {
+  Normalize();
+  left -= other_left;
+  bottom -= other_bottom;
+  right += other_right;
+  top += other_top;
+}
+
+void CFX_FloatRect::Inflate(const CFX_FloatRect& rt) {
+  Inflate(rt.left, rt.bottom, rt.right, rt.top);
+}
+
+void CFX_FloatRect::Deflate(float x, float y) {
+  Deflate(x, y, x, y);
+}
+
+void CFX_FloatRect::Deflate(float other_left,
+                            float other_bottom,
+                            float other_right,
+                            float other_top) {
+  Inflate(-other_left, -other_bottom, -other_right, -other_top);
+}
+
+void CFX_FloatRect::Deflate(const CFX_FloatRect& rt) {
+  Deflate(rt.left, rt.bottom, rt.right, rt.top);
+}
+
+CFX_FloatRect CFX_FloatRect::GetDeflated(float x, float y) const {
+  if (IsEmpty())
+    return CFX_FloatRect();
+
+  CFX_FloatRect that = *this;
+  that.Deflate(x, y);
+  that.Normalize();
+  return that;
+}
+
+void CFX_FloatRect::Translate(float e, float f) {
+  left += e;
+  right += e;
+  top += f;
+  bottom += f;
+}
+
 void CFX_FloatRect::Scale(float fScale) {
   left *= fScale;
   bottom *= fScale;
@@ -195,17 +265,30 @@
 }
 
 FX_RECT CFX_FloatRect::ToRoundedFxRect() const {
-  return FX_RECT(FXSYS_round(left), FXSYS_round(top), FXSYS_round(right),
-                 FXSYS_round(bottom));
+  return FX_RECT(FXSYS_roundf(left), FXSYS_roundf(top), FXSYS_roundf(right),
+                 FXSYS_roundf(bottom));
+}
+
+FX_RECT CFX_RectF::GetOuterRect() const {
+  return FX_RECT(static_cast<int32_t>(floor(left)),
+                 static_cast<int32_t>(floor(top)),
+                 static_cast<int32_t>(ceil(right())),
+                 static_cast<int32_t>(ceil(bottom())));
 }
 
 #ifndef NDEBUG
 std::ostream& operator<<(std::ostream& os, const CFX_FloatRect& rect) {
-  os << "rect[" << rect.Width() << "x" << rect.Height() << " (" << rect.left
-     << ", " << rect.bottom << ")]";
+  os << "rect[w " << rect.Width() << " x h " << rect.Height() << " (left "
+     << rect.left << ", bot " << rect.bottom << ")]";
   return os;
 }
-#endif
+
+std::ostream& operator<<(std::ostream& os, const CFX_RectF& rect) {
+  os << "rect[w " << rect.Width() << " x h " << rect.Height() << " (left "
+     << rect.left << ", top " << rect.top << ")]";
+  return os;
+}
+#endif  // NDEBUG
 
 CFX_Matrix CFX_Matrix::GetInverse() const {
   CFX_Matrix inverse;
@@ -223,14 +306,6 @@
   return inverse;
 }
 
-void CFX_Matrix::Concat(const CFX_Matrix& m, bool bPrepended) {
-  ConcatInternal(m, bPrepended);
-}
-
-void CFX_Matrix::ConcatInverse(const CFX_Matrix& src, bool bPrepended) {
-  Concat(src.GetInverse(), bPrepended);
-}
-
 bool CFX_Matrix::Is90Rotated() const {
   return fabs(a * 1000) < fabs(b) && fabs(d * 1000) < fabs(c);
 }
@@ -239,47 +314,29 @@
   return fabs(b * 1000) < fabs(a) && fabs(c * 1000) < fabs(d);
 }
 
-void CFX_Matrix::Translate(float x, float y, bool bPrepended) {
-  if (bPrepended) {
-    e += x * a + y * c;
-    f += y * d + x * b;
-    return;
-  }
+void CFX_Matrix::Translate(float x, float y) {
   e += x;
   f += y;
 }
 
-void CFX_Matrix::Scale(float sx, float sy, bool bPrepended) {
-  a *= sx;
-  d *= sy;
-  if (bPrepended) {
-    b *= sx;
-    c *= sy;
-    return;
-  }
+void CFX_Matrix::TranslatePrepend(float x, float y) {
+  e += x * a + y * c;
+  f += y * d + x * b;
+}
 
+void CFX_Matrix::Scale(float sx, float sy) {
+  a *= sx;
   b *= sy;
   c *= sx;
+  d *= sy;
   e *= sx;
   f *= sy;
 }
 
-void CFX_Matrix::Rotate(float fRadian, bool bPrepended) {
+void CFX_Matrix::Rotate(float fRadian) {
   float cosValue = cos(fRadian);
   float sinValue = sin(fRadian);
-  ConcatInternal(CFX_Matrix(cosValue, sinValue, -sinValue, cosValue, 0, 0),
-                 bPrepended);
-}
-
-void CFX_Matrix::RotateAt(float fRadian, float dx, float dy, bool bPrepended) {
-  Translate(dx, dy, bPrepended);
-  Rotate(fRadian, bPrepended);
-  Translate(-dx, -dy, bPrepended);
-}
-
-void CFX_Matrix::Shear(float fAlphaRadian, float fBetaRadian, bool bPrepended) {
-  ConcatInternal(CFX_Matrix(1, tan(fAlphaRadian), tan(fBetaRadian), 1, 0, 0),
-                 bPrepended);
+  Concat(CFX_Matrix(cosValue, sinValue, -sinValue, cosValue, 0, 0));
 }
 
 void CFX_Matrix::MatchRect(const CFX_FloatRect& dest,
@@ -329,64 +386,31 @@
   return CFX_PointF(a * point.x + c * point.y + e,
                     b * point.x + d * point.y + f);
 }
-std::tuple<float, float, float, float> CFX_Matrix::TransformRect(
-    const float& left,
-    const float& right,
-    const float& top,
-    const float& bottom) const {
-  CFX_PointF points[] = {
-      {left, top}, {left, bottom}, {right, top}, {right, bottom}};
-  for (int i = 0; i < 4; i++)
-    points[i] = Transform(points[i]);
+
+CFX_RectF CFX_Matrix::TransformRect(const CFX_RectF& rect) const {
+  CFX_FloatRect result_rect = TransformRect(rect.ToFloatRect());
+  return CFX_RectF(result_rect.left, result_rect.bottom, result_rect.Width(),
+                   result_rect.Height());
+}
+
+CFX_FloatRect CFX_Matrix::TransformRect(const CFX_FloatRect& rect) const {
+  CFX_PointF points[] = {{rect.left, rect.top},
+                         {rect.left, rect.bottom},
+                         {rect.right, rect.top},
+                         {rect.right, rect.bottom}};
+  for (CFX_PointF& point : points)
+    point = Transform(point);
 
   float new_right = points[0].x;
   float new_left = points[0].x;
   float new_top = points[0].y;
   float new_bottom = points[0].y;
-  for (int i = 1; i < 4; i++) {
+  for (size_t i = 1; i < FX_ArraySize(points); i++) {
     new_right = std::max(new_right, points[i].x);
     new_left = std::min(new_left, points[i].x);
     new_top = std::max(new_top, points[i].y);
     new_bottom = std::min(new_bottom, points[i].y);
   }
-  return std::make_tuple(new_left, new_right, new_top, new_bottom);
-}
 
-CFX_RectF CFX_Matrix::TransformRect(const CFX_RectF& rect) const {
-  float left;
-  float right;
-  float bottom;
-  float top;
-  std::tie(left, right, bottom, top) =
-      TransformRect(rect.left, rect.right(), rect.bottom(), rect.top);
-  return CFX_RectF(left, top, right - left, bottom - top);
-}
-
-CFX_FloatRect CFX_Matrix::TransformRect(const CFX_FloatRect& rect) const {
-  float left;
-  float right;
-  float top;
-  float bottom;
-  std::tie(left, right, top, bottom) =
-      TransformRect(rect.left, rect.right, rect.top, rect.bottom);
-  return CFX_FloatRect(left, bottom, right, top);
-}
-
-void CFX_Matrix::ConcatInternal(const CFX_Matrix& other, bool prepend) {
-  CFX_Matrix left;
-  CFX_Matrix right;
-  if (prepend) {
-    left = other;
-    right = *this;
-  } else {
-    left = *this;
-    right = other;
-  }
-
-  a = left.a * right.a + left.b * right.c;
-  b = left.a * right.b + left.b * right.d;
-  c = left.c * right.a + left.d * right.c;
-  d = left.c * right.b + left.d * right.d;
-  e = left.e * right.a + left.f * right.c + right.e;
-  f = left.e * right.b + left.f * right.d + right.f;
+  return CFX_FloatRect(new_left, new_bottom, new_right, new_top);
 }
diff --git a/core/fxcrt/fx_coordinates.h b/core/fxcrt/fx_coordinates.h
index 69d16d1..e8d1a8a 100644
--- a/core/fxcrt/fx_coordinates.h
+++ b/core/fxcrt/fx_coordinates.h
@@ -8,12 +8,13 @@
 #define CORE_FXCRT_FX_COORDINATES_H_
 
 #include <algorithm>
-#include <tuple>
 
 #include "core/fxcrt/fx_system.h"
 #include "third_party/base/numerics/safe_math.h"
 
-class CFX_Matrix;
+#ifndef NDEBUG
+#include <ostream>
+#endif
 
 template <class BaseType>
 class CFX_PTemplate {
@@ -22,7 +23,7 @@
   CFX_PTemplate(BaseType new_x, BaseType new_y) : x(new_x), y(new_y) {}
   CFX_PTemplate(const CFX_PTemplate& other) : x(other.x), y(other.y) {}
 
-  CFX_PTemplate operator=(const CFX_PTemplate& other) {
+  CFX_PTemplate& operator=(const CFX_PTemplate& other) {
     if (this != &other) {
       x = other.x;
       y = other.y;
@@ -79,7 +80,7 @@
     width = 0;
     height = 0;
   }
-  CFX_STemplate operator=(const CFX_STemplate& other) {
+  CFX_STemplate& operator=(const CFX_STemplate& other) {
     if (this != &other) {
       width = other.width;
       height = other.height;
@@ -132,7 +133,7 @@
 using CFX_SizeF = CFX_STemplate<float>;
 
 template <class BaseType>
-class CFX_VTemplate : public CFX_PTemplate<BaseType> {
+class CFX_VTemplate final : public CFX_PTemplate<BaseType> {
  public:
   using CFX_PTemplate<BaseType>::x;
   using CFX_PTemplate<BaseType>::y;
@@ -178,8 +179,9 @@
 // TODO(tsepez): Consolidate all these different rectangle classes.
 
 // LTRB rectangles (y-axis runs downwards).
+// Struct layout is compatible with win32 RECT.
 struct FX_RECT {
-  FX_RECT() : left(0), top(0), right(0), bottom(0) {}
+  FX_RECT() = default;
   FX_RECT(int l, int t, int r, int b) : left(l), top(t), right(r), bottom(b) {}
 
   int Width() const { return right - left; }
@@ -195,7 +197,6 @@
   }
 
   void Normalize();
-
   void Intersect(const FX_RECT& src);
   void Intersect(int l, int t, int r, int b) { Intersect(FX_RECT(l, t, r, b)); }
 
@@ -215,17 +216,17 @@
     return x >= left && x < right && y >= top && y < bottom;
   }
 
-  int32_t left;
-  int32_t top;
-  int32_t right;
-  int32_t bottom;
+  int32_t left = 0;
+  int32_t top = 0;
+  int32_t right = 0;
+  int32_t bottom = 0;
 };
 
 // LTRB rectangles (y-axis runs upwards).
 class CFX_FloatRect {
  public:
-  CFX_FloatRect() : CFX_FloatRect(0.0f, 0.0f, 0.0f, 0.0f) {}
-  CFX_FloatRect(float l, float b, float r, float t)
+  constexpr CFX_FloatRect() = default;
+  constexpr CFX_FloatRect(float l, float b, float r, float t)
       : left(l), bottom(b), right(r), top(t) {}
 
   explicit CFX_FloatRect(const float* pArray)
@@ -237,10 +238,7 @@
 
   void Normalize();
 
-  void Reset();
-
   bool IsEmpty() const { return left >= right || bottom >= top; }
-
   bool Contains(const CFX_PointF& point) const;
   bool Contains(const CFX_FloatRect& other_rect) const;
 
@@ -273,69 +271,28 @@
 
   float Width() const { return right - left; }
   float Height() const { return top - bottom; }
+  float Left() const { return left; }
+  float Bottom() const { return bottom; }
+  float Right() const { return right; }
+  float Top() const { return top; }
 
-  void Inflate(float x, float y) {
-    Normalize();
-    left -= x;
-    right += x;
-    bottom -= y;
-    top += y;
-  }
-
+  void Inflate(float x, float y);
   void Inflate(float other_left,
                float other_bottom,
                float other_right,
-               float other_top) {
-    Normalize();
-    left -= other_left;
-    bottom -= other_bottom;
-    right += other_right;
-    top += other_top;
-  }
+               float other_top);
+  void Inflate(const CFX_FloatRect& rt);
 
-  void Inflate(const CFX_FloatRect& rt) {
-    Inflate(rt.left, rt.bottom, rt.right, rt.top);
-  }
-
-  void Deflate(float x, float y) {
-    Normalize();
-    left += x;
-    right -= x;
-    bottom += y;
-    top -= y;
-  }
-
+  void Deflate(float x, float y);
   void Deflate(float other_left,
                float other_bottom,
                float other_right,
-               float other_top) {
-    Normalize();
-    left += other_left;
-    bottom += other_bottom;
-    right -= other_right;
-    top -= other_top;
-  }
+               float other_top);
+  void Deflate(const CFX_FloatRect& rt);
 
-  void Deflate(const CFX_FloatRect& rt) {
-    Deflate(rt.left, rt.bottom, rt.right, rt.top);
-  }
+  CFX_FloatRect GetDeflated(float x, float y) const;
 
-  CFX_FloatRect GetDeflated(float x, float y) const {
-    if (IsEmpty())
-      return CFX_FloatRect();
-
-    CFX_FloatRect that = *this;
-    that.Deflate(x, y);
-    that.Normalize();
-    return that;
-  }
-
-  void Translate(float e, float f) {
-    left += e;
-    right += e;
-    top += f;
-    bottom += f;
-  }
+  void Translate(float e, float f);
 
   void Scale(float fScale);
   void ScaleFromCenterPoint(float fScale);
@@ -351,10 +308,10 @@
   // Rounds LBRT values.
   FX_RECT ToRoundedFxRect() const;
 
-  float left;
-  float bottom;
-  float right;
-  float top;
+  float left = 0.0f;
+  float bottom = 0.0f;
+  float right = 0.0f;
+  float top = 0.0f;
 };
 
 #ifndef NDEBUG
@@ -362,79 +319,44 @@
 #endif
 
 // LTWH rectangles (y-axis runs downwards).
-template <class BaseType>
-class CFX_RTemplate {
+class CFX_RectF {
  public:
-  using PointType = CFX_PTemplate<BaseType>;
-  using SizeType = CFX_STemplate<BaseType>;
-  using VectorType = CFX_VTemplate<BaseType>;
-  using RectType = CFX_RTemplate<BaseType>;
+  using PointType = CFX_PointF;
+  using SizeType = CFX_SizeF;
 
-  CFX_RTemplate() : left(0), top(0), width(0), height(0) {}
-  CFX_RTemplate(BaseType dst_left,
-                BaseType dst_top,
-                BaseType dst_width,
-                BaseType dst_height)
+  CFX_RectF() = default;
+  CFX_RectF(float dst_left, float dst_top, float dst_width, float dst_height)
       : left(dst_left), top(dst_top), width(dst_width), height(dst_height) {}
-  CFX_RTemplate(BaseType dst_left, BaseType dst_top, const SizeType& dst_size)
+  CFX_RectF(float dst_left, float dst_top, const SizeType& dst_size)
       : left(dst_left),
         top(dst_top),
         width(dst_size.width),
         height(dst_size.height) {}
-  CFX_RTemplate(const PointType& p, BaseType dst_width, BaseType dst_height)
+  CFX_RectF(const PointType& p, float dst_width, float dst_height)
       : left(p.x), top(p.y), width(dst_width), height(dst_height) {}
-  CFX_RTemplate(const PointType& p1, const SizeType& s2)
+  CFX_RectF(const PointType& p1, const SizeType& s2)
       : left(p1.x), top(p1.y), width(s2.width), height(s2.height) {}
-  CFX_RTemplate(const PointType& p1, const PointType& p2)
-      : left(p1.x),
-        top(p1.y),
-        width(p2.width - p1.width),
-        height(p2.height - p1.height) {
-    Normalize();
-  }
-  CFX_RTemplate(const PointType& p, const VectorType& v)
-      : left(p.x), top(p.y), width(v.x), height(v.y) {
-    Normalize();
-  }
-
-  explicit CFX_RTemplate(const CFX_FloatRect& r)
-      : left(static_cast<BaseType>(r.left)),
-        top(static_cast<BaseType>(r.top)),
-        width(static_cast<BaseType>(r.Width())),
-        height(static_cast<BaseType>(r.Height())) {}
+  explicit CFX_RectF(const FX_RECT& that)
+      : left(static_cast<float>(that.left)),
+        top(static_cast<float>(that.top)),
+        width(static_cast<float>(that.Width())),
+        height(static_cast<float>(that.Height())) {}
 
   // NOLINTNEXTLINE(runtime/explicit)
-  CFX_RTemplate(const RectType& other)
-      : left(other.left),
-        top(other.top),
-        width(other.width),
-        height(other.height) {}
+  CFX_RectF(const CFX_RectF& other) = default;
 
-  template <typename OtherType>
-  CFX_RTemplate<OtherType> As() const {
-    return CFX_RTemplate<OtherType>(
-        static_cast<OtherType>(left), static_cast<OtherType>(top),
-        static_cast<OtherType>(width), static_cast<OtherType>(height));
-  }
-
-  void Reset() {
-    left = 0;
-    top = 0;
-    width = 0;
-    height = 0;
-  }
-  RectType& operator+=(const PointType& p) {
+  CFX_RectF& operator+=(const PointType& p) {
     left += p.x;
     top += p.y;
     return *this;
   }
-  RectType& operator-=(const PointType& p) {
+  CFX_RectF& operator-=(const PointType& p) {
     left -= p.x;
     top -= p.y;
     return *this;
   }
-  BaseType right() const { return left + width; }
-  BaseType bottom() const { return top + height; }
+  float right() const { return left + width; }
+  float bottom() const { return top + height; }
   void Normalize() {
     if (width < 0) {
       left += width;
@@ -445,46 +367,46 @@
       height = -height;
     }
   }
-  void Offset(BaseType dx, BaseType dy) {
+  void Offset(float dx, float dy) {
     left += dx;
     top += dy;
   }
-  void Inflate(BaseType x, BaseType y) {
+  void Inflate(float x, float y) {
     left -= x;
     width += x * 2;
     top -= y;
     height += y * 2;
   }
   void Inflate(const PointType& p) { Inflate(p.x, p.y); }
-  void Inflate(BaseType off_left,
-               BaseType off_top,
-               BaseType off_right,
-               BaseType off_bottom) {
+  void Inflate(float off_left,
+               float off_top,
+               float off_right,
+               float off_bottom) {
     left -= off_left;
     top -= off_top;
     width += off_left + off_right;
     height += off_top + off_bottom;
   }
-  void Inflate(const RectType& rt) {
+  void Inflate(const CFX_RectF& rt) {
     Inflate(rt.left, rt.top, rt.left + rt.width, rt.top + rt.height);
   }
-  void Deflate(BaseType x, BaseType y) {
+  void Deflate(float x, float y) {
     left += x;
     width -= x * 2;
     top += y;
     height -= y * 2;
   }
   void Deflate(const PointType& p) { Deflate(p.x, p.y); }
-  void Deflate(BaseType off_left,
-               BaseType off_top,
-               BaseType off_right,
-               BaseType off_bottom) {
+  void Deflate(float off_left,
+               float off_top,
+               float off_right,
+               float off_bottom) {
     left += off_left;
     top += off_top;
     width -= off_left + off_right;
     height -= off_top + off_bottom;
   }
-  void Deflate(const RectType& rt) {
+  void Deflate(const CFX_RectF& rt) {
     Deflate(rt.left, rt.top, rt.top + rt.width, rt.top + rt.height);
   }
   bool IsEmpty() const { return width <= 0 || height <= 0; }
@@ -496,12 +418,14 @@
     return p.x >= left && p.x < left + width && p.y >= top &&
            p.y < top + height;
   }
-  bool Contains(const RectType& rt) const {
+  bool Contains(const CFX_RectF& rt) const {
     return rt.left >= left && rt.right() <= right() && rt.top >= top &&
            rt.bottom() <= bottom();
   }
-  BaseType Width() const { return width; }
-  BaseType Height() const { return height; }
+  float Left() const { return left; }
+  float Top() const { return top; }
+  float Width() const { return width; }
+  float Height() const { return height; }
   SizeType Size() const { return SizeType(width, height); }
   PointType TopLeft() const { return PointType(left, top); }
   PointType TopRight() const { return PointType(left + width, top); }
@@ -512,9 +436,9 @@
   PointType Center() const {
     return PointType(left + width / 2, top + height / 2);
   }
-  void Union(BaseType x, BaseType y) {
-    BaseType r = right();
-    BaseType b = bottom();
+  void Union(float x, float y) {
+    float r = right();
+    float b = bottom();
 
     left = std::min(left, x);
     top = std::min(top, y);
@@ -525,9 +449,9 @@
     height = b - top;
   }
   void Union(const PointType& p) { Union(p.x, p.y); }
-  void Union(const RectType& rt) {
-    BaseType r = right();
-    BaseType b = bottom();
+  void Union(const CFX_RectF& rt) {
+    float r = right();
+    float b = bottom();
 
     left = std::min(left, rt.left);
     top = std::min(top, rt.top);
@@ -537,9 +461,9 @@
     width = r - left;
     height = b - top;
   }
-  void Intersect(const RectType& rt) {
-    BaseType r = right();
-    BaseType b = bottom();
+  void Intersect(const CFX_RectF& rt) {
+    float r = right();
+    float b = bottom();
 
     left = std::max(left, rt.left);
     top = std::max(top, rt.top);
@@ -549,21 +473,21 @@
     width = r - left;
     height = b - top;
   }
-  bool IntersectWith(const RectType& rt) const {
-    RectType rect = rt;
+  bool IntersectWith(const CFX_RectF& rt) const {
+    CFX_RectF rect = rt;
     rect.Intersect(*this);
     return !rect.IsEmpty();
   }
-  bool IntersectWith(const RectType& rt, float fEpsilon) const {
-    RectType rect = rt;
+  bool IntersectWith(const CFX_RectF& rt, float fEpsilon) const {
+    CFX_RectF rect = rt;
     rect.Intersect(*this);
     return !rect.IsEmpty(fEpsilon);
   }
-  friend bool operator==(const RectType& rc1, const RectType& rc2) {
+  friend bool operator==(const CFX_RectF& rc1, const CFX_RectF& rc2) {
     return rc1.left == rc2.left && rc1.top == rc2.top &&
            rc1.width == rc2.width && rc1.height == rc2.height;
   }
-  friend bool operator!=(const RectType& rc1, const RectType& rc2) {
+  friend bool operator!=(const CFX_RectF& rc1, const CFX_RectF& rc2) {
     return !(rc1 == rc2);
   }
 
@@ -573,13 +497,19 @@
     return CFX_FloatRect(left, top, right(), bottom());
   }
 
-  BaseType left;
-  BaseType top;
-  BaseType width;
-  BaseType height;
+  // Returned rect has bounds rounded up/down such that the original is
+  // contained in it.
+  FX_RECT GetOuterRect() const;
+
+  float left = 0.0f;
+  float top = 0.0f;
+  float width = 0.0f;
+  float height = 0.0f;
 };
-using CFX_Rect = CFX_RTemplate<int32_t>;
-using CFX_RectF = CFX_RTemplate<float>;
+
+#ifndef NDEBUG
+std::ostream& operator<<(std::ostream& os, const CFX_RectF& rect);
+#endif  // NDEBUG
 
 // The matrix is of the form:
 // | a  b  0 |
@@ -589,57 +519,54 @@
 //
 class CFX_Matrix {
  public:
-  CFX_Matrix() { SetIdentity(); }
+  CFX_Matrix() = default;
 
   explicit CFX_Matrix(const float n[6])
       : a(n[0]), b(n[1]), c(n[2]), d(n[3]), e(n[4]), f(n[5]) {}
 
-  CFX_Matrix(const CFX_Matrix& other) = default;
-
   CFX_Matrix(float a1, float b1, float c1, float d1, float e1, float f1)
       : a(a1), b(b1), c(c1), d(d1), e(e1), f(f1) {}
 
-  void operator=(const CFX_Matrix& other) {
-    a = other.a;
-    b = other.b;
-    c = other.c;
-    d = other.d;
-    e = other.e;
-    f = other.f;
+  CFX_Matrix(const CFX_Matrix& other) = default;
+
+  CFX_Matrix& operator=(const CFX_Matrix& other) = default;
+
+  bool operator==(const CFX_Matrix& other) const {
+    return a == other.a && b == other.b && c == other.c && d == other.d &&
+           e == other.e && f == other.f;
+  }
+  bool operator!=(const CFX_Matrix& other) const { return !(*this == other); }
+
+  CFX_Matrix operator*(const CFX_Matrix& right) const {
+    return CFX_Matrix(a * right.a + b * right.c, a * right.b + b * right.d,
+                      c * right.a + d * right.c, c * right.b + d * right.d,
+                      e * right.a + f * right.c + right.e,
+                      e * right.b + f * right.d + right.f);
+  }
+  CFX_Matrix& operator*=(const CFX_Matrix& other) {
+    *this = *this * other;
+    return *this;
   }
 
-  void SetIdentity() {
-    a = 1;
-    b = 0;
-    c = 0;
-    d = 1;
-    e = 0;
-    f = 0;
-  }
-
+  bool IsIdentity() const { return *this == CFX_Matrix(); }
   CFX_Matrix GetInverse() const;
 
-  void Concat(const CFX_Matrix& m, bool bPrepended = false);
-  void ConcatInverse(const CFX_Matrix& m, bool bPrepended = false);
-
-  bool IsIdentity() const {
-    return a == 1 && b == 0 && c == 0 && d == 1 && e == 0 && f == 0;
-  }
-
   bool Is90Rotated() const;
   bool IsScaled() const;
   bool WillScale() const { return a != 1.0f || b != 0 || c != 0 || d != 1.0f; }
 
-  void Translate(float x, float y, bool bPrepended = false);
-  void Translate(int32_t x, int32_t y, bool bPrepended = false) {
-    Translate(static_cast<float>(x), static_cast<float>(y), bPrepended);
+  void Concat(const CFX_Matrix& right) { *this *= right; }
+  void Translate(float x, float y);
+  void TranslatePrepend(float x, float y);
+  void Translate(int32_t x, int32_t y) {
+    Translate(static_cast<float>(x), static_cast<float>(y));
+  }
+  void TranslatePrepend(int32_t x, int32_t y) {
+    TranslatePrepend(static_cast<float>(x), static_cast<float>(y));
   }
 
-  void Scale(float sx, float sy, bool bPrepended = false);
-  void Rotate(float fRadian, bool bPrepended = false);
-  void RotateAt(float fRadian, float x, float y, bool bPrepended = false);
-
-  void Shear(float fAlphaRadian, float fBetaRadian, bool bPrepended = false);
+  void Scale(float sx, float sy);
+  void Rotate(float fRadian);
 
   void MatchRect(const CFX_FloatRect& dest, const CFX_FloatRect& src);
 
@@ -652,23 +579,15 @@
 
   CFX_PointF Transform(const CFX_PointF& point) const;
 
-  std::tuple<float, float, float, float> TransformRect(
-      const float& left,
-      const float& right,
-      const float& top,
-      const float& bottom) const;
   CFX_RectF TransformRect(const CFX_RectF& rect) const;
   CFX_FloatRect TransformRect(const CFX_FloatRect& rect) const;
 
-  float a;
-  float b;
-  float c;
-  float d;
-  float e;
-  float f;
-
- private:
-  void ConcatInternal(const CFX_Matrix& other, bool prepend);
+  float a = 1.0f;
+  float b = 0.0f;
+  float c = 0.0f;
+  float d = 1.0f;
+  float e = 0.0f;
+  float f = 0.0f;
 };
 
 #endif  // CORE_FXCRT_FX_COORDINATES_H_
diff --git a/core/fxcrt/fx_coordinates_unittest.cpp b/core/fxcrt/fx_coordinates_unittest.cpp
index 3368a40..7539ab7 100644
--- a/core/fxcrt/fx_coordinates_unittest.cpp
+++ b/core/fxcrt/fx_coordinates_unittest.cpp
@@ -4,10 +4,30 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 
+#include <limits>
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+
+constexpr float kMinFloat = std::numeric_limits<float>::min();
+constexpr int kMaxInt = std::numeric_limits<int>::max();
+constexpr int kMinInt = std::numeric_limits<int>::min();
+constexpr float kMinIntAsFloat = static_cast<float>(kMinInt);
+constexpr float kMaxIntAsFloat = static_cast<float>(kMaxInt);
+
+}  // namespace
+
+TEST(CFX_FloatRect, FromFXRect) {
+  FX_RECT downwards(10, 20, 30, 40);
+  CFX_FloatRect rect(downwards);
+  EXPECT_FLOAT_EQ(rect.left, 10.0f);
+  EXPECT_FLOAT_EQ(rect.bottom, 20.0f);
+  EXPECT_FLOAT_EQ(rect.right, 30.0f);
+  EXPECT_FLOAT_EQ(rect.top, 40.0f);
+}
+
 TEST(CFX_FloatRect, GetBBox) {
   CFX_FloatRect rect = CFX_FloatRect::GetBBox(nullptr, 0);
   EXPECT_FLOAT_EQ(0.0f, rect.left);
@@ -64,6 +84,112 @@
   EXPECT_FLOAT_EQ(6.3f, rect.top);
 }
 
+TEST(CFX_FloatRect, GetInnerRect) {
+  FX_RECT inner_rect;
+  CFX_FloatRect rect;
+
+  inner_rect = rect.GetInnerRect();
+  EXPECT_EQ(0, inner_rect.left);
+  EXPECT_EQ(0, inner_rect.bottom);
+  EXPECT_EQ(0, inner_rect.right);
+  EXPECT_EQ(0, inner_rect.top);
+
+  // Function converts from float to int using floor() for top and right, and
+  // ceil() for left and bottom.
+  rect = CFX_FloatRect(-1.1f, 3.6f, 4.4f, -5.7f);
+  inner_rect = rect.GetInnerRect();
+  EXPECT_EQ(-1, inner_rect.left);
+  EXPECT_EQ(4, inner_rect.bottom);
+  EXPECT_EQ(4, inner_rect.right);
+  EXPECT_EQ(-6, inner_rect.top);
+
+  rect = CFX_FloatRect(kMinFloat, kMinFloat, kMinFloat, kMinFloat);
+  inner_rect = rect.GetInnerRect();
+  EXPECT_EQ(0, inner_rect.left);
+  EXPECT_EQ(1, inner_rect.bottom);
+  EXPECT_EQ(1, inner_rect.right);
+  EXPECT_EQ(0, inner_rect.top);
+
+  rect = CFX_FloatRect(-kMinFloat, -kMinFloat, -kMinFloat, -kMinFloat);
+  inner_rect = rect.GetInnerRect();
+  EXPECT_EQ(-1, inner_rect.left);
+  EXPECT_EQ(0, inner_rect.bottom);
+  EXPECT_EQ(0, inner_rect.right);
+  EXPECT_EQ(-1, inner_rect.top);
+
+  // Check at limits of integer range. When saturated would expect to get values
+  // that are clamped to the limits of integers, but instead it is returning all
+  // negative values that represent a rectangle as a dot in a far reach of the
+  // negative coordinate space.  Related to crbug.com/1019026
+  rect = CFX_FloatRect(kMinIntAsFloat, kMinIntAsFloat, kMaxIntAsFloat,
+                       kMaxIntAsFloat);
+  inner_rect = rect.GetInnerRect();
+  EXPECT_EQ(kMinInt, inner_rect.left);
+  EXPECT_EQ(kMaxInt, inner_rect.bottom);
+  EXPECT_EQ(kMaxInt, inner_rect.right);
+  EXPECT_EQ(kMinInt, inner_rect.top);
+
+  rect = CFX_FloatRect(kMinIntAsFloat - 1.0f, kMinIntAsFloat - 1.0f,
+                       kMaxIntAsFloat + 1.0f, kMaxIntAsFloat + 1.0f);
+  inner_rect = rect.GetInnerRect();
+  EXPECT_EQ(kMinInt, inner_rect.left);
+  EXPECT_EQ(kMaxInt, inner_rect.bottom);
+  EXPECT_EQ(kMaxInt, inner_rect.right);
+  EXPECT_EQ(kMinInt, inner_rect.top);
+}
+
+TEST(CFX_FloatRect, GetOuterRect) {
+  FX_RECT outer_rect;
+  CFX_FloatRect rect;
+
+  outer_rect = rect.GetOuterRect();
+  EXPECT_EQ(0, outer_rect.left);
+  EXPECT_EQ(0, outer_rect.bottom);
+  EXPECT_EQ(0, outer_rect.right);
+  EXPECT_EQ(0, outer_rect.top);
+
+  // Function converts from float to int using floor() for left and bottom, and
+  // ceil() for right and top.
+  rect = CFX_FloatRect(-1.1f, 3.6f, 4.4f, -5.7f);
+  outer_rect = rect.GetOuterRect();
+  EXPECT_EQ(-2, outer_rect.left);
+  EXPECT_EQ(3, outer_rect.bottom);
+  EXPECT_EQ(5, outer_rect.right);
+  EXPECT_EQ(-5, outer_rect.top);
+
+  rect = CFX_FloatRect(kMinFloat, kMinFloat, kMinFloat, kMinFloat);
+  outer_rect = rect.GetOuterRect();
+  EXPECT_EQ(0, outer_rect.left);
+  EXPECT_EQ(1, outer_rect.bottom);
+  EXPECT_EQ(1, outer_rect.right);
+  EXPECT_EQ(0, outer_rect.top);
+
+  rect = CFX_FloatRect(-kMinFloat, -kMinFloat, -kMinFloat, -kMinFloat);
+  outer_rect = rect.GetOuterRect();
+  EXPECT_EQ(-1, outer_rect.left);
+  EXPECT_EQ(0, outer_rect.bottom);
+  EXPECT_EQ(0, outer_rect.right);
+  EXPECT_EQ(-1, outer_rect.top);
+
+  // Check at limits of integer range. When saturated would expect to get values
+  // that are clamped to the limits of integers.
+  rect = CFX_FloatRect(kMinIntAsFloat, kMinIntAsFloat, kMaxIntAsFloat,
+                       kMaxIntAsFloat);
+  outer_rect = rect.GetOuterRect();
+  EXPECT_EQ(kMinInt, outer_rect.left);
+  EXPECT_EQ(kMaxInt, outer_rect.bottom);
+  EXPECT_EQ(kMaxInt, outer_rect.right);
+  EXPECT_EQ(kMinInt, outer_rect.top);
+
+  rect = CFX_FloatRect(kMinIntAsFloat - 1.0f, kMinIntAsFloat - 1.0f,
+                       kMaxIntAsFloat + 1.0f, kMaxIntAsFloat + 1.0f);
+  outer_rect = rect.GetOuterRect();
+  EXPECT_EQ(kMinInt, outer_rect.left);
+  EXPECT_EQ(kMaxInt, outer_rect.bottom);
+  EXPECT_EQ(kMaxInt, outer_rect.right);
+  EXPECT_EQ(kMinInt, outer_rect.top);
+}
+
 TEST(CFX_FloatRect, Normalize) {
   CFX_FloatRect rect;
   rect.Normalize();
@@ -197,24 +323,39 @@
   std::ostringstream os;
   CFX_FloatRect rect;
   os << rect;
-  EXPECT_STREQ("rect[0x0 (0, 0)]", os.str().c_str());
+  EXPECT_STREQ("rect[w 0 x h 0 (left 0, bot 0)]", os.str().c_str());
 
   os.str("");
   rect = CFX_FloatRect(10, 20, 14, 23);
   os << rect;
-  EXPECT_STREQ("rect[4x3 (10, 20)]", os.str().c_str());
+  EXPECT_STREQ("rect[w 4 x h 3 (left 10, bot 20)]", os.str().c_str());
 
   os.str("");
   rect = CFX_FloatRect(10.5, 20.5, 14.75, 23.75);
   os << rect;
-  EXPECT_STREQ("rect[4.25x3.25 (10.5, 20.5)]", os.str().c_str());
+  EXPECT_STREQ("rect[w 4.25 x h 3.25 (left 10.5, bot 20.5)]", os.str().c_str());
 }
-#endif
+
+TEST(CFX_RectF, Print) {
+  std::ostringstream os;
+  CFX_RectF rect;
+  os << rect;
+  EXPECT_STREQ("rect[w 0 x h 0 (left 0, top 0)]", os.str().c_str());
+
+  os.str("");
+  rect = CFX_RectF(10, 20, 4, 3);
+  os << rect;
+  EXPECT_STREQ("rect[w 4 x h 3 (left 10, top 20)]", os.str().c_str());
+
+  os.str("");
+  rect = CFX_RectF(10.5, 20.5, 4.25, 3.25);
+  os << rect;
+  EXPECT_STREQ("rect[w 4.25 x h 3.25 (left 10.5, top 20.5)]", os.str().c_str());
+}
+#endif  // NDEBUG
 
 TEST(CFX_Matrix, ReverseIdentity) {
-  CFX_Matrix m;
-  m.SetIdentity();
-  CFX_Matrix rev = m.GetInverse();
+  CFX_Matrix rev = CFX_Matrix().GetInverse();
 
   EXPECT_FLOAT_EQ(1.0, rev.a);
   EXPECT_FLOAT_EQ(0.0, rev.b);
@@ -224,12 +365,35 @@
   EXPECT_FLOAT_EQ(0.0, rev.f);
 
   CFX_PointF expected(2, 3);
-  CFX_PointF result = rev.Transform(m.Transform(CFX_PointF(2, 3)));
+  CFX_PointF result = rev.Transform(CFX_Matrix().Transform(CFX_PointF(2, 3)));
   EXPECT_FLOAT_EQ(expected.x, result.x);
   EXPECT_FLOAT_EQ(expected.y, result.y);
 }
 
-TEST(CFX_Matrix, Reverse) {
+TEST(CFX_Matrix, SetIdentity) {
+  CFX_Matrix m;
+  EXPECT_FLOAT_EQ(1.0f, m.a);
+  EXPECT_FLOAT_EQ(0.0f, m.b);
+  EXPECT_FLOAT_EQ(0.0f, m.c);
+  EXPECT_FLOAT_EQ(1.0f, m.d);
+  EXPECT_FLOAT_EQ(0.0f, m.e);
+  EXPECT_FLOAT_EQ(0.0f, m.f);
+  EXPECT_TRUE(m.IsIdentity());
+
+  m.a = -1;
+  EXPECT_FALSE(m.IsIdentity());
+
+  m = CFX_Matrix();
+  EXPECT_FLOAT_EQ(1.0f, m.a);
+  EXPECT_FLOAT_EQ(0.0f, m.b);
+  EXPECT_FLOAT_EQ(0.0f, m.c);
+  EXPECT_FLOAT_EQ(1.0f, m.d);
+  EXPECT_FLOAT_EQ(0.0f, m.e);
+  EXPECT_FLOAT_EQ(0.0f, m.f);
+  EXPECT_TRUE(m.IsIdentity());
+}
+
+TEST(CFX_Matrix, GetInverse) {
   static constexpr float data[6] = {3, 0, 2, 3, 1, 4};
   CFX_Matrix m(data);
   CFX_Matrix rev = m.GetInverse();
@@ -248,7 +412,7 @@
 }
 
 // Note, I think these are a bug and the matrix should be the identity.
-TEST(CFX_Matrix, ReverseCR702041) {
+TEST(CFX_Matrix, GetInverseCR702041) {
   // The determinate is < std::numeric_limits<float>::epsilon()
   static constexpr float data[6] = {0.947368443f, -0.108947366f, -0.923076928f,
                                     0.106153846f, 18.0f,         787.929993f};
@@ -269,7 +433,7 @@
   EXPECT_FLOAT_EQ(expected.y, result.y);
 }
 
-TEST(CFX_Matrix, ReverseCR714187) {
+TEST(CFX_Matrix, GetInverseCR714187) {
   // The determinate is < std::numeric_limits<float>::epsilon()
   static constexpr float data[6] = {0.000037f,  0.0f,        0.0f,
                                     -0.000037f, 182.413101f, 136.977646f};
@@ -289,3 +453,174 @@
   EXPECT_FLOAT_EQ(expected.x, result.x);
   EXPECT_FLOAT_EQ(expected.y, result.y);
 }
+
+#define EXPECT_NEAR_FIVE_PLACES(a, b) EXPECT_NEAR((a), (b), 1e-5)
+
+TEST(CFX_Matrix, ComposeTransformations) {
+  // sin(FX_PI/2) and cos(FX_PI/2) have a tiny error and are not exactly 1.0f
+  // and 0.0f. The rotation matrix is thus not perfect.
+
+  CFX_Matrix rotate_90;
+  rotate_90.Rotate(FX_PI / 2);
+  EXPECT_NEAR_FIVE_PLACES(0.0f, rotate_90.a);
+  EXPECT_NEAR_FIVE_PLACES(1.0f, rotate_90.b);
+  EXPECT_NEAR_FIVE_PLACES(-1.0f, rotate_90.c);
+  EXPECT_NEAR_FIVE_PLACES(0.0f, rotate_90.d);
+  EXPECT_FLOAT_EQ(0.0f, rotate_90.e);
+  EXPECT_FLOAT_EQ(0.0f, rotate_90.f);
+
+  CFX_Matrix translate_23_11;
+  translate_23_11.Translate(23, 11);
+  EXPECT_FLOAT_EQ(1.0f, translate_23_11.a);
+  EXPECT_FLOAT_EQ(0.0f, translate_23_11.b);
+  EXPECT_FLOAT_EQ(0.0f, translate_23_11.c);
+  EXPECT_FLOAT_EQ(1.0f, translate_23_11.d);
+  EXPECT_FLOAT_EQ(23.0f, translate_23_11.e);
+  EXPECT_FLOAT_EQ(11.0f, translate_23_11.f);
+
+  CFX_Matrix scale_5_13;
+  scale_5_13.Scale(5, 13);
+  EXPECT_FLOAT_EQ(5.0f, scale_5_13.a);
+  EXPECT_FLOAT_EQ(0.0f, scale_5_13.b);
+  EXPECT_FLOAT_EQ(0.0f, scale_5_13.c);
+  EXPECT_FLOAT_EQ(13.0f, scale_5_13.d);
+  EXPECT_FLOAT_EQ(0.0, scale_5_13.e);
+  EXPECT_FLOAT_EQ(0.0, scale_5_13.f);
+
+  // Apply the transforms to points step by step.
+  CFX_PointF origin_transformed(0, 0);
+  CFX_PointF p_10_20_transformed(10, 20);
+
+  origin_transformed = rotate_90.Transform(origin_transformed);
+  EXPECT_FLOAT_EQ(0.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(0.0f, origin_transformed.y);
+  p_10_20_transformed = rotate_90.Transform(p_10_20_transformed);
+  EXPECT_FLOAT_EQ(-20.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(10.0f, p_10_20_transformed.y);
+
+  origin_transformed = translate_23_11.Transform(origin_transformed);
+  EXPECT_FLOAT_EQ(23.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(11.0f, origin_transformed.y);
+  p_10_20_transformed = translate_23_11.Transform(p_10_20_transformed);
+  EXPECT_FLOAT_EQ(3.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(21.0f, p_10_20_transformed.y);
+
+  origin_transformed = scale_5_13.Transform(origin_transformed);
+  EXPECT_FLOAT_EQ(115.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(143.0f, origin_transformed.y);
+  p_10_20_transformed = scale_5_13.Transform(p_10_20_transformed);
+  EXPECT_FLOAT_EQ(15.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(273.0f, p_10_20_transformed.y);
+
+  // Apply the transforms to points in the reverse order.
+  origin_transformed = CFX_PointF(0, 0);
+  p_10_20_transformed = CFX_PointF(10, 20);
+
+  origin_transformed = scale_5_13.Transform(origin_transformed);
+  EXPECT_FLOAT_EQ(0.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(0.0f, origin_transformed.y);
+  p_10_20_transformed = scale_5_13.Transform(p_10_20_transformed);
+  EXPECT_FLOAT_EQ(50.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(260.0f, p_10_20_transformed.y);
+
+  origin_transformed = translate_23_11.Transform(origin_transformed);
+  EXPECT_FLOAT_EQ(23.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(11.0f, origin_transformed.y);
+  p_10_20_transformed = translate_23_11.Transform(p_10_20_transformed);
+  EXPECT_FLOAT_EQ(73.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(271.0f, p_10_20_transformed.y);
+
+  origin_transformed = rotate_90.Transform(origin_transformed);
+  EXPECT_FLOAT_EQ(-11.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(23.0f, origin_transformed.y);
+  p_10_20_transformed = rotate_90.Transform(p_10_20_transformed);
+  EXPECT_FLOAT_EQ(-271.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(73.0f, p_10_20_transformed.y);
+
+  // Compose all transforms.
+  CFX_Matrix m;
+  m.Concat(rotate_90);
+  m.Concat(translate_23_11);
+  m.Concat(scale_5_13);
+  EXPECT_NEAR_FIVE_PLACES(0.0f, m.a);
+  EXPECT_NEAR_FIVE_PLACES(13.0f, m.b);
+  EXPECT_NEAR_FIVE_PLACES(-5.0f, m.c);
+  EXPECT_NEAR_FIVE_PLACES(0.0f, m.d);
+  EXPECT_FLOAT_EQ(115.0, m.e);
+  EXPECT_FLOAT_EQ(143.0, m.f);
+
+  // Note how the results using the combined matrix are equal to the results
+  // when applying the three original matrices step-by-step.
+  origin_transformed = m.Transform(CFX_PointF(0, 0));
+  EXPECT_FLOAT_EQ(115.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(143.0f, origin_transformed.y);
+
+  p_10_20_transformed = m.Transform(CFX_PointF(10, 20));
+  EXPECT_FLOAT_EQ(15.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(273.0f, p_10_20_transformed.y);
+
+  // Now compose all transforms prepending.
+  m = CFX_Matrix();
+  m = rotate_90 * m;
+  m = translate_23_11 * m;
+  m = scale_5_13 * m;
+  EXPECT_NEAR_FIVE_PLACES(0.0f, m.a);
+  EXPECT_NEAR_FIVE_PLACES(5.0f, m.b);
+  EXPECT_NEAR_FIVE_PLACES(-13.0f, m.c);
+  EXPECT_NEAR_FIVE_PLACES(0.0f, m.d);
+  EXPECT_FLOAT_EQ(-11.0, m.e);
+  EXPECT_FLOAT_EQ(23.0, m.f);
+
+  // Note how the results using the combined matrix are equal to the results
+  // when applying the three original matrices step-by-step in the reverse
+  // order.
+  origin_transformed = m.Transform(CFX_PointF(0, 0));
+  EXPECT_FLOAT_EQ(-11.0f, origin_transformed.x);
+  EXPECT_FLOAT_EQ(23.0f, origin_transformed.y);
+
+  p_10_20_transformed = m.Transform(CFX_PointF(10, 20));
+  EXPECT_FLOAT_EQ(-271.0f, p_10_20_transformed.x);
+  EXPECT_FLOAT_EQ(73.0f, p_10_20_transformed.y);
+}
+
+TEST(CFX_Matrix, TransformRectForRectF) {
+  CFX_Matrix rotate_90;
+  rotate_90.Rotate(FX_PI / 2);
+
+  CFX_Matrix scale_5_13;
+  scale_5_13.Scale(5, 13);
+
+  CFX_RectF rect(10.5f, 20.5f, 4.25f, 3.25f);
+  rect = rotate_90.TransformRect(rect);
+  EXPECT_FLOAT_EQ(-23.75f, rect.Left());
+  EXPECT_FLOAT_EQ(10.5f, rect.Top());
+  EXPECT_FLOAT_EQ(3.25f, rect.Width());
+  EXPECT_FLOAT_EQ(4.25f, rect.Height());
+
+  rect = scale_5_13.TransformRect(rect);
+  EXPECT_FLOAT_EQ(-118.75f, rect.Left());
+  EXPECT_FLOAT_EQ(136.5f, rect.Top());
+  EXPECT_FLOAT_EQ(16.25f, rect.Width());
+  EXPECT_FLOAT_EQ(55.25f, rect.Height());
+}
+
+TEST(CFX_Matrix, TransformRectForFloatRect) {
+  CFX_Matrix rotate_90;
+  rotate_90.Rotate(FX_PI / 2);
+
+  CFX_Matrix scale_5_13;
+  scale_5_13.Scale(5, 13);
+
+  CFX_FloatRect rect(5.5f, 0.0f, 12.25f, 2.7f);
+  rect = rotate_90.TransformRect(rect);
+  EXPECT_FLOAT_EQ(-2.7f, rect.Left());
+  EXPECT_FLOAT_EQ(5.5f, rect.Bottom());
+  EXPECT_NEAR(0.0f, rect.Right(), 0.00001f);
+  EXPECT_FLOAT_EQ(12.25f, rect.Top());
+
+  rect = scale_5_13.TransformRect(rect);
+  EXPECT_FLOAT_EQ(-13.5f, rect.Left());
+  EXPECT_FLOAT_EQ(71.5f, rect.Bottom());
+  EXPECT_NEAR(0.0f, rect.Right(), 0.00001f);
+  EXPECT_FLOAT_EQ(159.25f, rect.Top());
+}
diff --git a/core/fxcrt/fx_extension.cpp b/core/fxcrt/fx_extension.cpp
index 62cf8a0..8d76151 100644
--- a/core/fxcrt/fx_extension.cpp
+++ b/core/fxcrt/fx_extension.cpp
@@ -8,9 +8,28 @@
 
 #include <algorithm>
 #include <cwctype>
+#include <limits>
+
+#include "third_party/base/compiler_specific.h"
+
+namespace {
+
+time_t DefaultTimeFunction() {
+  return time(nullptr);
+}
+
+struct tm* DefaultLocaltimeFunction(const time_t* tp) {
+  return localtime(tp);
+}
+
+time_t (*g_time_func)() = DefaultTimeFunction;
+struct tm* (*g_localtime_func)(const time_t*) = DefaultLocaltimeFunction;
+
+}  // namespace
 
 float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen) {
   ASSERT(pwsStr);
+
   if (iLength < 0)
     iLength = static_cast<int32_t>(wcslen(pwsStr));
   if (iLength == 0)
@@ -21,6 +40,7 @@
   switch (pwsStr[iUsedLen]) {
     case '-':
       bNegtive = true;
+      FALLTHROUGH;
     case '+':
       iUsedLen++;
       break;
@@ -29,7 +49,7 @@
   float fValue = 0.0f;
   while (iUsedLen < iLength) {
     wchar_t wch = pwsStr[iUsedLen];
-    if (!std::iswdigit(wch))
+    if (!FXSYS_IsDecimalDigit(wch))
       break;
 
     fValue = fValue * 10.0f + (wch - L'0');
@@ -40,13 +60,55 @@
     float fPrecise = 0.1f;
     while (++iUsedLen < iLength) {
       wchar_t wch = pwsStr[iUsedLen];
-      if (!std::iswdigit(wch))
+      if (!FXSYS_IsDecimalDigit(wch))
         break;
 
       fValue += (wch - L'0') * fPrecise;
       fPrecise *= 0.1f;
     }
   }
+
+  if (iUsedLen < iLength &&
+      (pwsStr[iUsedLen] == 'e' || pwsStr[iUsedLen] == 'E')) {
+    ++iUsedLen;
+
+    bool negative_exponent = false;
+    if (iUsedLen < iLength &&
+        (pwsStr[iUsedLen] == '-' || pwsStr[iUsedLen] == '+')) {
+      negative_exponent = pwsStr[iUsedLen] == '-';
+      ++iUsedLen;
+    }
+
+    int32_t exp_value = 0;
+    while (iUsedLen < iLength) {
+      wchar_t wch = pwsStr[iUsedLen];
+      if (!FXSYS_IsDecimalDigit(wch))
+        break;
+
+      exp_value = exp_value * 10.0f + (wch - L'0');
+      // Exponent is outside the valid range, fail.
+      if ((negative_exponent &&
+           -exp_value < std::numeric_limits<float>::min_exponent10) ||
+          (!negative_exponent &&
+           exp_value > std::numeric_limits<float>::max_exponent10)) {
+        if (pUsedLen)
+          *pUsedLen = 0;
+        return 0.0f;
+      }
+
+      ++iUsedLen;
+    }
+
+    for (size_t i = exp_value; i > 0; --i) {
+      if (exp_value > 0) {
+        if (negative_exponent)
+          fValue /= 10;
+        else
+          fValue *= 10;
+      }
+    }
+  }
+
   if (pUsedLen)
     *pUsedLen = iUsedLen;
 
@@ -54,7 +116,10 @@
 }
 
 wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count) {
-  ASSERT(dstStr && srcStr && count > 0);
+  ASSERT(dstStr);
+  ASSERT(srcStr);
+  ASSERT(count > 0);
+
   for (size_t i = 0; i < count; ++i)
     if ((dstStr[i] = srcStr[i]) == L'\0')
       break;
@@ -62,41 +127,20 @@
 }
 
 int32_t FXSYS_wcsnicmp(const wchar_t* s1, const wchar_t* s2, size_t count) {
-  ASSERT(s1 && s2 && count > 0);
+  ASSERT(s1);
+  ASSERT(s2);
+  ASSERT(count > 0);
+
   wchar_t wch1 = 0, wch2 = 0;
   while (count-- > 0) {
-    wch1 = static_cast<wchar_t>(FXSYS_tolower(*s1++));
-    wch2 = static_cast<wchar_t>(FXSYS_tolower(*s2++));
+    wch1 = static_cast<wchar_t>(FXSYS_towlower(*s1++));
+    wch2 = static_cast<wchar_t>(FXSYS_towlower(*s2++));
     if (wch1 != wch2)
       break;
   }
   return wch1 - wch2;
 }
 
-uint32_t FX_HashCode_GetA(const ByteStringView& str, bool bIgnoreCase) {
-  uint32_t dwHashCode = 0;
-  if (bIgnoreCase) {
-    for (const auto& c : str)
-      dwHashCode = 31 * dwHashCode + FXSYS_tolower(c);
-  } else {
-    for (const auto& c : str)
-      dwHashCode = 31 * dwHashCode + c;
-  }
-  return dwHashCode;
-}
-
-uint32_t FX_HashCode_GetW(const WideStringView& str, bool bIgnoreCase) {
-  uint32_t dwHashCode = 0;
-  if (bIgnoreCase) {
-    for (const auto& c : str)
-      dwHashCode = 1313 * dwHashCode + FXSYS_tolower(c);
-  } else {
-    for (const auto& c : str)
-      dwHashCode = 1313 * dwHashCode + c;
-  }
-  return dwHashCode;
-}
-
 void FXSYS_IntToTwoHexChars(uint8_t n, char* buf) {
   static const char kHex[] = "0123456789ABCDEF";
   buf[0] = kHex[n / 16];
@@ -122,33 +166,21 @@
   return 8;
 }
 
-uint32_t GetBits32(const uint8_t* pData, int bitpos, int nbits) {
-  ASSERT(0 < nbits && nbits <= 32);
-  const uint8_t* dataPtr = &pData[bitpos / 8];
-  int bitShift;
-  int bitMask;
-  int dstShift;
-  int bitCount = bitpos & 0x07;
-  if (nbits < 8 && nbits + bitCount <= 8) {
-    bitShift = 8 - nbits - bitCount;
-    bitMask = (1 << nbits) - 1;
-    dstShift = 0;
-  } else {
-    bitShift = 0;
-    int bitOffset = 8 - bitCount;
-    bitMask = (1 << std::min(bitOffset, nbits)) - 1;
-    dstShift = nbits - bitOffset;
-  }
-  uint32_t result =
-      static_cast<uint32_t>((*dataPtr++ >> bitShift & bitMask) << dstShift);
-  while (dstShift >= 8) {
-    dstShift -= 8;
-    result |= *dataPtr++ << dstShift;
-  }
-  if (dstShift > 0) {
-    bitShift = 8 - dstShift;
-    bitMask = (1 << dstShift) - 1;
-    result |= *dataPtr++ >> bitShift & bitMask;
-  }
-  return result;
+void FXSYS_SetTimeFunction(time_t (*func)()) {
+  g_time_func = func ? func : DefaultTimeFunction;
+}
+
+void FXSYS_SetLocaltimeFunction(struct tm* (*func)(const time_t*)) {
+  g_localtime_func = func ? func : DefaultLocaltimeFunction;
+}
+
+time_t FXSYS_time(time_t* tloc) {
+  time_t ret_val = g_time_func();
+  if (tloc)
+    *tloc = ret_val;
+  return ret_val;
+}
+
+struct tm* FXSYS_localtime(const time_t* tp) {
+  return g_localtime_func(tp);
 }
diff --git a/core/fxcrt/fx_extension.h b/core/fxcrt/fx_extension.h
index e02d58d..28b23cf 100644
--- a/core/fxcrt/fx_extension.h
+++ b/core/fxcrt/fx_extension.h
@@ -7,85 +7,132 @@
 #ifndef CORE_FXCRT_FX_EXTENSION_H_
 #define CORE_FXCRT_FX_EXTENSION_H_
 
+#include <time.h>
+
 #include <cctype>
+#include <cmath>
 #include <cwctype>
 #include <memory>
 
 #include "core/fxcrt/fx_string.h"
 
+#if defined(USE_SYSTEM_ICUUC)
+#include <unicode/uchar.h>
+#else
+#include "third_party/icu/source/common/unicode/uchar.h"
+#endif
+
 #define FX_INVALID_OFFSET static_cast<uint32_t>(-1)
 
 #ifdef PDF_ENABLE_XFA
 #define FX_IsOdd(a) ((a)&1)
 #endif  // PDF_ENABLE_XFA
 
-float FXSYS_wcstof(const wchar_t* pwsStr,
-                   int32_t iLength = -1,
-                   int32_t* pUsedLen = nullptr);
+float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen);
 wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count);
 int32_t FXSYS_wcsnicmp(const wchar_t* s1, const wchar_t* s2, size_t count);
 
-inline bool FXSYS_islower(int32_t ch) {
-  return ch >= 'a' && ch <= 'z';
+inline bool FXSYS_iswlower(int32_t c) {
+  return u_islower(c);
 }
 
-inline bool FXSYS_isupper(int32_t ch) {
-  return ch >= 'A' && ch <= 'Z';
+inline bool FXSYS_iswupper(int32_t c) {
+  return u_isupper(c);
 }
 
-inline int32_t FXSYS_tolower(int32_t ch) {
-  return ch < 'A' || ch > 'Z' ? ch : (ch + 0x20);
+inline int32_t FXSYS_towlower(wchar_t c) {
+  return u_tolower(c);
 }
 
-inline int32_t FXSYS_toupper(int32_t ch) {
-  return ch < 'a' || ch > 'z' ? ch : (ch - 0x20);
+inline int32_t FXSYS_towupper(wchar_t c) {
+  return u_toupper(c);
 }
 
-inline bool FXSYS_iswalpha(wchar_t wch) {
-  return FXSYS_isupper(wch) || FXSYS_islower(wch);
+inline char FXSYS_ToUpperASCII(char c) {
+  return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
 }
 
-inline bool FXSYS_iswalnum(wchar_t wch) {
-  return FXSYS_iswalpha(wch) || std::iswdigit(wch);
+inline bool FXSYS_iswalpha(wchar_t c) {
+  return u_isalpha(c);
+}
+
+inline bool FXSYS_iswalnum(wchar_t c) {
+  return u_isalnum(c);
 }
 
 inline bool FXSYS_iswspace(wchar_t c) {
-  return (c == 0x20) || (c == 0x0d) || (c == 0x0a) || (c == 0x09);
+  return u_isspace(c);
 }
 
-inline bool FXSYS_isHexDigit(const char c) {
+inline bool FXSYS_IsOctalDigit(char c) {
+  return c >= '0' && c <= '7';
+}
+
+inline bool FXSYS_IsHexDigit(char c) {
   return !((c & 0x80) || !std::isxdigit(c));
 }
 
-inline int FXSYS_HexCharToInt(const char c) {
-  if (!FXSYS_isHexDigit(c))
+inline bool FXSYS_IsWideHexDigit(wchar_t c) {
+  return !((c & 0xFFFFFF80) || !std::isxdigit(c));
+}
+
+inline int FXSYS_HexCharToInt(char c) {
+  if (!FXSYS_IsHexDigit(c))
     return 0;
-  char upchar = std::toupper(c);
+  char upchar = FXSYS_ToUpperASCII(c);
   return upchar > '9' ? upchar - 'A' + 10 : upchar - '0';
 }
 
-inline bool FXSYS_isDecimalDigit(const char c) {
+inline int FXSYS_WideHexCharToInt(wchar_t c) {
+  if (!FXSYS_IsWideHexDigit(c))
+    return 0;
+  char upchar = std::toupper(static_cast<char>(c));
+  return upchar > '9' ? upchar - 'A' + 10 : upchar - '0';
+}
+
+inline bool FXSYS_IsDecimalDigit(char c) {
   return !((c & 0x80) || !std::isdigit(c));
 }
 
-inline bool FXSYS_isDecimalDigit(const wchar_t c) {
-  return !!std::iswdigit(c);
+inline bool FXSYS_IsDecimalDigit(wchar_t c) {
+  return !((c & 0xFFFFFF80) || !std::iswdigit(c));
 }
 
-inline int FXSYS_DecimalCharToInt(const char c) {
-  return FXSYS_isDecimalDigit(c) ? c - '0' : 0;
+inline int FXSYS_DecimalCharToInt(char c) {
+  return FXSYS_IsDecimalDigit(c) ? c - '0' : 0;
 }
 
-inline int FXSYS_DecimalCharToInt(const wchar_t c) {
-  return std::iswdigit(c) ? c - L'0' : 0;
+inline int FXSYS_DecimalCharToInt(wchar_t c) {
+  return FXSYS_IsDecimalDigit(c) ? c - L'0' : 0;
 }
 
-void FXSYS_IntToTwoHexChars(uint8_t c, char* buf);
-
-void FXSYS_IntToFourHexChars(uint16_t c, char* buf);
+void FXSYS_IntToTwoHexChars(uint8_t n, char* buf);
+void FXSYS_IntToFourHexChars(uint16_t n, char* buf);
 
 size_t FXSYS_ToUTF16BE(uint32_t unicode, char* buf);
 
-uint32_t GetBits32(const uint8_t* pData, int bitpos, int nbits);
+// Strict order over floating types where NaNs may be present.
+template <typename T>
+bool FXSYS_SafeEQ(const T& lhs, const T& rhs) {
+  return (std::isnan(lhs) && std::isnan(rhs)) ||
+         (!std::isnan(lhs) && !std::isnan(rhs) && lhs == rhs);
+}
+
+template <typename T>
+bool FXSYS_SafeLT(const T& lhs, const T& rhs) {
+  if (std::isnan(lhs) && std::isnan(rhs))
+    return false;
+  if (std::isnan(lhs) || std::isnan(rhs))
+    return std::isnan(lhs) < std::isnan(rhs);
+  return lhs < rhs;
+}
+
+// Override time/localtime functions for test consistency.
+void FXSYS_SetTimeFunction(time_t (*func)());
+void FXSYS_SetLocaltimeFunction(struct tm* (*func)(const time_t*));
+
+// Replacements for time/localtime that respect overrides.
+time_t FXSYS_time(time_t* tloc);
+struct tm* FXSYS_localtime(const time_t* tp);
 
 #endif  // CORE_FXCRT_FX_EXTENSION_H_
diff --git a/core/fxcrt/fx_extension_unittest.cpp b/core/fxcrt/fx_extension_unittest.cpp
index 0500a14..81fc4f7 100644
--- a/core/fxcrt/fx_extension_unittest.cpp
+++ b/core/fxcrt/fx_extension_unittest.cpp
@@ -3,21 +3,11 @@
 // found in the LICENSE file.
 
 #include "core/fxcrt/fx_extension.h"
+
+#include <limits>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace {
-
-uint32_t ReferenceGetBits32(const uint8_t* pData, int bitpos, int nbits) {
-  int result = 0;
-  for (int i = 0; i < nbits; i++) {
-    if (pData[(bitpos + i) / 8] & (1 << (7 - (bitpos + i) % 8)))
-      result |= 1 << (nbits - i - 1);
-  }
-  return result;
-}
-
-}  // namespace
-
 TEST(fxcrt, FXSYS_HexCharToInt) {
   EXPECT_EQ(10, FXSYS_HexCharToInt('a'));
   EXPECT_EQ(10, FXSYS_HexCharToInt('A'));
@@ -30,27 +20,17 @@
   EXPECT_EQ(0, FXSYS_DecimalCharToInt('a'));
   EXPECT_EQ(7, FXSYS_DecimalCharToInt(L'7'));
   EXPECT_EQ(0, FXSYS_DecimalCharToInt(L'a'));
+  EXPECT_EQ(0, FXSYS_DecimalCharToInt(static_cast<char>(-78)));
+  EXPECT_EQ(0, FXSYS_DecimalCharToInt(static_cast<wchar_t>(0xb2)));
 }
 
-TEST(fxcrt, FXSYS_isDecimalDigit) {
-  EXPECT_TRUE(FXSYS_isDecimalDigit('7'));
-  EXPECT_TRUE(FXSYS_isDecimalDigit(L'7'));
-  EXPECT_FALSE(FXSYS_isDecimalDigit('a'));
-  EXPECT_FALSE(FXSYS_isDecimalDigit(L'a'));
-}
-
-TEST(fxcrt, FX_HashCode_Ascii) {
-  EXPECT_EQ(0u, FX_HashCode_GetA("", false));
-  EXPECT_EQ(65u, FX_HashCode_GetA("A", false));
-  EXPECT_EQ(97u, FX_HashCode_GetA("A", true));
-  EXPECT_EQ(31 * 65u + 66u, FX_HashCode_GetA("AB", false));
-}
-
-TEST(fxcrt, FX_HashCode_Wide) {
-  EXPECT_EQ(0u, FX_HashCode_GetW(L"", false));
-  EXPECT_EQ(65u, FX_HashCode_GetW(L"A", false));
-  EXPECT_EQ(97u, FX_HashCode_GetW(L"A", true));
-  EXPECT_EQ(1313 * 65u + 66u, FX_HashCode_GetW(L"AB", false));
+TEST(fxcrt, FXSYS_IsDecimalDigit) {
+  EXPECT_TRUE(FXSYS_IsDecimalDigit('7'));
+  EXPECT_TRUE(FXSYS_IsDecimalDigit(L'7'));
+  EXPECT_FALSE(FXSYS_IsDecimalDigit('a'));
+  EXPECT_FALSE(FXSYS_IsDecimalDigit(L'a'));
+  EXPECT_FALSE(FXSYS_IsDecimalDigit(static_cast<char>(-78)));
+  EXPECT_FALSE(FXSYS_IsDecimalDigit(static_cast<wchar_t>(0xb2)));
 }
 
 TEST(fxcrt, FXSYS_IntToTwoHexChars) {
@@ -103,12 +83,92 @@
   EXPECT_STREQ("D840DC3E", buf);
 }
 
-TEST(fxcrt, GetBits32) {
-  unsigned char data[] = {0xDE, 0x3F, 0xB1, 0x7C, 0x12, 0x9A, 0x04, 0x56};
-  for (int nbits = 1; nbits <= 32; ++nbits) {
-    for (int bitpos = 0; bitpos < (int)sizeof(data) * 8 - nbits; ++bitpos) {
-      EXPECT_EQ(ReferenceGetBits32(data, bitpos, nbits),
-                GetBits32(data, bitpos, nbits));
+TEST(fxcrt, FXSYS_wcstof) {
+  int32_t used_len = 0;
+  EXPECT_FLOAT_EQ(-12.0f, FXSYS_wcstof(L"-12", 3, &used_len));
+  EXPECT_EQ(3, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(1.5362f, FXSYS_wcstof(L"1.5362", 6, &used_len));
+  EXPECT_EQ(6, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(0.875f, FXSYS_wcstof(L"0.875", 5, &used_len));
+  EXPECT_EQ(5, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(5.56e-2f, FXSYS_wcstof(L"5.56e-2", 7, &used_len));
+  EXPECT_EQ(7, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(1.234e10f, FXSYS_wcstof(L"1.234E10", 8, &used_len));
+  EXPECT_EQ(8, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"1.234E100000000000000", 21, &used_len));
+  EXPECT_EQ(0, used_len);
+
+  used_len = 0;
+  EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"1.234E-128", 21, &used_len));
+  EXPECT_EQ(0, used_len);
+
+  // TODO(dsinclair): This should round as per IEEE 64-bit values.
+  // EXPECT_EQ(L"123456789.01234567", FXSYS_wcstof(L"123456789.012345678"));
+  used_len = 0;
+  EXPECT_FLOAT_EQ(123456789.012345678f,
+                  FXSYS_wcstof(L"123456789.012345678", 19, &used_len));
+  EXPECT_EQ(19, used_len);
+
+  // TODO(dsinclair): This is spec'd as rounding when > 16 significant digits
+  // prior to the exponent.
+  // EXPECT_EQ(100000000000000000, FXSYS_wcstof(L"99999999999999999"));
+  used_len = 0;
+  EXPECT_FLOAT_EQ(99999999999999999.0f,
+                  FXSYS_wcstof(L"99999999999999999", 17, &used_len));
+  EXPECT_EQ(17, used_len);
+
+  // For https://crbug.com/pdfium/1217
+  EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"e76", 3, nullptr));
+
+  // Overflow to infinity.
+  used_len = 0;
+  EXPECT_TRUE(std::isinf(FXSYS_wcstof(
+      L"88888888888888888888888888888888888888888888888888888888888888888888888"
+      L"88888888888888888888888888888888888888888888888888888888888",
+      130, &used_len)));
+  EXPECT_EQ(130, used_len);
+
+  used_len = 0;
+  EXPECT_TRUE(std::isinf(FXSYS_wcstof(
+      L"-8888888888888888888888888888888888888888888888888888888888888888888888"
+      L"888888888888888888888888888888888888888888888888888888888888",
+      131, &used_len)));
+  EXPECT_EQ(131, used_len);
+}
+
+TEST(fxcrt, FXSYS_SafeOps) {
+  const float fMin = std::numeric_limits<float>::min();
+  const float fMax = std::numeric_limits<float>::max();
+  const float fInf = std::numeric_limits<float>::infinity();
+  const float fNan = std::numeric_limits<float>::quiet_NaN();
+  const float ascending[] = {fMin, 1.0f, 2.0f, fMax, fInf, fNan};
+
+  for (size_t i = 0; i < FX_ArraySize(ascending); ++i) {
+    for (size_t j = 0; j < FX_ArraySize(ascending); ++j) {
+      if (i == j) {
+        EXPECT_TRUE(FXSYS_SafeEQ(ascending[i], ascending[j]))
+            << " at " << i << " " << j;
+      } else {
+        EXPECT_FALSE(FXSYS_SafeEQ(ascending[i], ascending[j]))
+            << " at " << i << " " << j;
+      }
+      if (i < j) {
+        EXPECT_TRUE(FXSYS_SafeLT(ascending[i], ascending[j]))
+            << " at " << i << " " << j;
+      } else {
+        EXPECT_FALSE(FXSYS_SafeLT(ascending[i], ascending[j]))
+            << " at " << i << " " << j;
+      }
     }
   }
 }
diff --git a/core/fxcrt/fx_memory.cpp b/core/fxcrt/fx_memory.cpp
index 6a135ae..1ed4949 100644
--- a/core/fxcrt/fx_memory.cpp
+++ b/core/fxcrt/fx_memory.cpp
@@ -5,29 +5,45 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_safe_types.h"
 
 #include <stdlib.h>  // For abort().
 
-pdfium::base::PartitionAllocatorGeneric gArrayBufferPartitionAllocator;
-pdfium::base::PartitionAllocatorGeneric gGeneralPartitionAllocator;
-pdfium::base::PartitionAllocatorGeneric gStringPartitionAllocator;
+#include <limits>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/debug/alias.h"
+
+pdfium::base::PartitionAllocatorGeneric& GetArrayBufferPartitionAllocator() {
+  static pdfium::base::PartitionAllocatorGeneric s_array_buffer_allocator;
+  return s_array_buffer_allocator;
+}
+
+pdfium::base::PartitionAllocatorGeneric& GetGeneralPartitionAllocator() {
+  static pdfium::base::PartitionAllocatorGeneric s_general_allocator;
+  return s_general_allocator;
+}
+
+pdfium::base::PartitionAllocatorGeneric& GetStringPartitionAllocator() {
+  static pdfium::base::PartitionAllocatorGeneric s_string_allocator;
+  return s_string_allocator;
+}
 
 void FXMEM_InitializePartitionAlloc() {
-  static bool s_gPartitionAllocatorsInitialized = false;
-  if (!s_gPartitionAllocatorsInitialized) {
+  static bool s_partition_allocators_initialized = false;
+  if (!s_partition_allocators_initialized) {
     pdfium::base::PartitionAllocGlobalInit(FX_OutOfMemoryTerminate);
-    gArrayBufferPartitionAllocator.init();
-    gGeneralPartitionAllocator.init();
-    gStringPartitionAllocator.init();
-    s_gPartitionAllocatorsInitialized = true;
+    GetArrayBufferPartitionAllocator().init();
+    GetGeneralPartitionAllocator().init();
+    GetStringPartitionAllocator().init();
+    s_partition_allocators_initialized = true;
   }
 }
 
 void* FXMEM_DefaultAlloc(size_t byte_size) {
   return pdfium::base::PartitionAllocGenericFlags(
-      gGeneralPartitionAllocator.root(), pdfium::base::PartitionAllocReturnNull,
-      byte_size, "GeneralPartition");
+      GetGeneralPartitionAllocator().root(),
+      pdfium::base::PartitionAllocReturnNull, byte_size, "GeneralPartition");
 }
 
 void* FXMEM_DefaultCalloc(size_t num_elems, size_t byte_size) {
@@ -35,18 +51,87 @@
 }
 
 void* FXMEM_DefaultRealloc(void* pointer, size_t new_size) {
-  return pdfium::base::PartitionReallocGeneric(
-      gGeneralPartitionAllocator.root(), pointer, new_size, "GeneralPartition");
+  return pdfium::base::PartitionReallocGenericFlags(
+      GetGeneralPartitionAllocator().root(),
+      pdfium::base::PartitionAllocReturnNull, pointer, new_size,
+      "GeneralPartition");
 }
 
 void FXMEM_DefaultFree(void* pointer) {
   pdfium::base::PartitionFree(pointer);
 }
 
-NEVER_INLINE void FX_OutOfMemoryTerminate() {
+NOINLINE void FX_OutOfMemoryTerminate() {
+  // Convince the linker this should not be folded with similar functions using
+  // Identical Code Folding.
+  static int make_this_function_aliased = 0xbd;
+  pdfium::base::debug::Alias(&make_this_function_aliased);
+
   // Termimate cleanly if we can, else crash at a specific address (0xbd).
   abort();
-#ifndef _WIN32
+#if !defined(OS_WIN)
   reinterpret_cast<void (*)()>(0xbd)();
 #endif
 }
+
+void* FX_SafeAlloc(size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T total = member_size;
+  total *= num_members;
+  if (!total.IsValid())
+    return nullptr;
+
+  constexpr int kFlags = pdfium::base::PartitionAllocReturnNull |
+                         pdfium::base::PartitionAllocZeroFill;
+  return pdfium::base::PartitionAllocGenericFlags(
+      GetGeneralPartitionAllocator().root(), kFlags, total.ValueOrDie(),
+      "GeneralPartition");
+}
+
+void* FX_SafeRealloc(void* ptr, size_t num_members, size_t member_size) {
+  FX_SAFE_SIZE_T size = num_members;
+  size *= member_size;
+  if (!size.IsValid())
+    return nullptr;
+
+  return pdfium::base::PartitionReallocGenericFlags(
+      GetGeneralPartitionAllocator().root(),
+      pdfium::base::PartitionAllocReturnNull, ptr, size.ValueOrDie(),
+      "GeneralPartition");
+}
+
+void* FX_AllocOrDie(size_t num_members, size_t member_size) {
+  // TODO(tsepez): See if we can avoid the implicit memset(0).
+  void* result = FX_SafeAlloc(num_members, member_size);
+  if (!result)
+    FX_OutOfMemoryTerminate();  // Never returns.
+
+  return result;
+}
+
+void* FX_AllocOrDie2D(size_t w, size_t h, size_t member_size) {
+  if (w >= std::numeric_limits<size_t>::max() / h)
+    FX_OutOfMemoryTerminate();  // Never returns.
+
+  return FX_AllocOrDie(w * h, member_size);
+}
+
+void* FX_ReallocOrDie(void* ptr, size_t num_members, size_t member_size) {
+  void* result = FX_SafeRealloc(ptr, num_members, member_size);
+  if (!result)
+    FX_OutOfMemoryTerminate();  // Never returns.
+
+  return result;
+}
+
+void FX_Free(void* ptr) {
+  // TODO(palmer): Removing this check exposes crashes when PDFium callers
+  // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
+  // other Partition Alloc callers need this tolerant behavior. Additionally,
+  // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
+  // not have to have that.
+  //
+  // So this check is hiding (what I consider to be) bugs, and we should try to
+  // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
+  if (ptr)
+    pdfium::base::PartitionFree(ptr);
+}
diff --git a/core/fxcrt/fx_memory.h b/core/fxcrt/fx_memory.h
index ecf3e42..6cb5f2e 100644
--- a/core/fxcrt/fx_memory.h
+++ b/core/fxcrt/fx_memory.h
@@ -7,7 +7,7 @@
 #ifndef CORE_FXCRT_FX_MEMORY_H_
 #define CORE_FXCRT_FX_MEMORY_H_
 
-#include "core/fxcrt/fx_system.h"
+#include <stddef.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -22,74 +22,16 @@
 #ifdef __cplusplus
 }  // extern "C"
 
-#include <stdlib.h>
-#include <limits>
-#include <memory>
-#include <new>
-
-#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/allocator/partition_allocator/partition_alloc.h"
 
-extern pdfium::base::PartitionAllocatorGeneric gArrayBufferPartitionAllocator;
-extern pdfium::base::PartitionAllocatorGeneric gGeneralPartitionAllocator;
-extern pdfium::base::PartitionAllocatorGeneric gStringPartitionAllocator;
+pdfium::base::PartitionAllocatorGeneric& GetArrayBufferPartitionAllocator();
+pdfium::base::PartitionAllocatorGeneric& GetGeneralPartitionAllocator();
+pdfium::base::PartitionAllocatorGeneric& GetStringPartitionAllocator();
 
 void FXMEM_InitializePartitionAlloc();
-NEVER_INLINE void FX_OutOfMemoryTerminate();
+NOINLINE void FX_OutOfMemoryTerminate();
 
-inline void* FX_SafeAlloc(size_t num_members, size_t member_size) {
-  FX_SAFE_SIZE_T total = member_size;
-  total *= num_members;
-  if (!total.IsValid())
-    return nullptr;
-
-  void* result = pdfium::base::PartitionAllocGenericFlags(
-      gGeneralPartitionAllocator.root(), pdfium::base::PartitionAllocReturnNull,
-      total.ValueOrDie(), "GeneralPartition");
-  if (result)
-    memset(result, 0, total.ValueOrDie());
-  return result;
-}
-
-inline void* FX_SafeRealloc(void* ptr, size_t num_members, size_t member_size) {
-  FX_SAFE_SIZE_T size = num_members;
-  size *= member_size;
-  if (!size.IsValid())
-    return nullptr;
-
-  return pdfium::base::PartitionReallocGeneric(
-      gGeneralPartitionAllocator.root(), ptr, size.ValueOrDie(),
-      "GeneralPartition");
-}
-
-inline void* FX_AllocOrDie(size_t num_members, size_t member_size) {
-  // TODO(tsepez): See if we can avoid the implicit memset(0).
-  if (void* result = FX_SafeAlloc(num_members, member_size))
-    return result;
-
-  FX_OutOfMemoryTerminate();  // Never returns.
-  return nullptr;             // Suppress compiler warning.
-}
-
-inline void* FX_AllocOrDie2D(size_t w, size_t h, size_t member_size) {
-  if (w < std::numeric_limits<size_t>::max() / h)
-    return FX_AllocOrDie(w * h, member_size);
-
-  FX_OutOfMemoryTerminate();  // Never returns.
-  return nullptr;             // Suppress compiler warning.
-}
-
-inline void* FX_ReallocOrDie(void* ptr,
-                             size_t num_members,
-                             size_t member_size) {
-  if (void* result = FX_SafeRealloc(ptr, num_members, member_size))
-    return result;
-
-  FX_OutOfMemoryTerminate();  // Never returns.
-  return nullptr;             // Suppress compiler warning.
-}
-
-// These never return nullptr.
+// These never return nullptr, and must return cleared memory.
 #define FX_Alloc(type, size) \
   static_cast<type*>(FX_AllocOrDie(size, sizeof(type)))
 #define FX_Alloc2D(type, w, h) \
@@ -97,24 +39,18 @@
 #define FX_Realloc(type, ptr, size) \
   static_cast<type*>(FX_ReallocOrDie(ptr, size, sizeof(type)))
 
-// May return nullptr.
+// May return nullptr, but returns cleared memory otherwise.
 #define FX_TryAlloc(type, size) \
   static_cast<type*>(FX_SafeAlloc(size, sizeof(type)))
 #define FX_TryRealloc(type, ptr, size) \
   static_cast<type*>(FX_SafeRealloc(ptr, size, sizeof(type)))
 
-inline void FX_Free(void* ptr) {
-  // TODO(palmer): Removing this check exposes crashes when PDFium callers
-  // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
-  // other Partition Alloc callers need this tolerant behavior. Additionally,
-  // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
-  // not have to have that.
-  //
-  // So this check is hiding (what I consider to be) bugs, and we should try to
-  // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
-  if (ptr)
-    pdfium::base::PartitionFree(ptr);
-}
+void* FX_SafeAlloc(size_t num_members, size_t member_size);
+void* FX_SafeRealloc(void* ptr, size_t num_members, size_t member_size);
+void* FX_AllocOrDie(size_t num_members, size_t member_size);
+void* FX_AllocOrDie2D(size_t w, size_t h, size_t member_size);
+void* FX_ReallocOrDie(void* ptr, size_t num_members, size_t member_size);
+void FX_Free(void* ptr);
 
 // The FX_ArraySize(arr) macro returns the # of elements in an array arr.
 // The expression is a compile-time constant, and therefore can be
@@ -131,10 +67,12 @@
 template <typename T, size_t N>
 char (&ArraySizeHelper(T (&array)[N]))[N];
 
-// Used with std::unique_ptr to FX_Free raw memory.
-struct FxFreeDeleter {
-  inline void operator()(void* ptr) const { FX_Free(ptr); }
-};
+// Round up to the power-of-two boundary N.
+template <int N, typename T>
+inline T FxAlignToBoundary(T size) {
+  static_assert(N > 0 && (N & (N - 1)) == 0, "Not non-zero power of two");
+  return (size + (N - 1)) & ~(N - 1);
+}
 
 #endif  // __cplusplus
 
diff --git a/core/fxcrt/fx_memory_unittest.cpp b/core/fxcrt/fx_memory_unittest.cpp
index 2856bb9..b3fd299 100644
--- a/core/fxcrt/fx_memory_unittest.cpp
+++ b/core/fxcrt/fx_memory_unittest.cpp
@@ -6,6 +6,7 @@
 
 #include <limits>
 
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -18,6 +19,15 @@
 
 }  // namespace
 
+TEST(fxcrt, FX_AllocZero) {
+  uint8_t* ptr = FX_Alloc(uint8_t, 0);
+  uint8_t* ptr2 = FX_Alloc(uint8_t, 0);
+  EXPECT_TRUE(ptr);      // Malloc(0) is distinguishable from OOM.
+  EXPECT_NE(ptr, ptr2);  // Each malloc(0) is distinguishable.
+  FX_Free(ptr2);
+  FX_Free(ptr);
+}
+
 // TODO(tsepez): re-enable OOM tests if we can find a way to
 // prevent it from hosing the bots.
 TEST(fxcrt, DISABLED_FX_AllocOOM) {
@@ -59,6 +69,7 @@
   FX_Free(ptr);
 }
 
+#if !defined(COMPILER_GCC)
 TEST(fxcrt, FX_TryAllocOverflow) {
   // |ptr| needs to be defined and used to avoid Clang optimizes away the
   // calloc() statement overzealously for optimized builds.
@@ -67,9 +78,12 @@
 
   ptr = FX_Alloc(int, 1);
   EXPECT_TRUE(ptr);
+  *ptr = 1492;  // Arbitrary sentinel.
   EXPECT_FALSE(FX_TryRealloc(int, ptr, kOverflowIntAlloc));
+  EXPECT_EQ(1492, *ptr);
   FX_Free(ptr);
 }
+#endif
 
 TEST(fxcrt, DISABLED_FXMEM_DefaultOOM) {
   EXPECT_FALSE(FXMEM_DefaultAlloc(kMaxByteAlloc));
@@ -79,3 +93,26 @@
   EXPECT_FALSE(FXMEM_DefaultRealloc(ptr, kMaxByteAlloc));
   FXMEM_DefaultFree(ptr);
 }
+
+TEST(fxcrt, FXAlign) {
+  static_assert(std::numeric_limits<size_t>::max() % 2 == 1,
+                "numeric limit must be odd for this test");
+
+  size_t s0 = 0;
+  size_t s1 = 1;
+  size_t s2 = 2;
+  size_t sbig = std::numeric_limits<size_t>::max() - 2;
+  EXPECT_EQ(0u, FxAlignToBoundary<2>(s0));
+  EXPECT_EQ(2u, FxAlignToBoundary<2>(s1));
+  EXPECT_EQ(2u, FxAlignToBoundary<2>(s2));
+  EXPECT_EQ(std::numeric_limits<size_t>::max() - 1, FxAlignToBoundary<2>(sbig));
+
+  int i0 = 0;
+  int i511 = 511;
+  int i512 = 512;
+  int ineg = -513;
+  EXPECT_EQ(0, FxAlignToBoundary<512>(i0));
+  EXPECT_EQ(512, FxAlignToBoundary<512>(i511));
+  EXPECT_EQ(512, FxAlignToBoundary<512>(i512));
+  EXPECT_EQ(-512, FxAlignToBoundary<512>(ineg));
+}
diff --git a/core/fxcrt/fx_memory_wrappers.h b/core/fxcrt/fx_memory_wrappers.h
new file mode 100644
index 0000000..823fe76
--- /dev/null
+++ b/core/fxcrt/fx_memory_wrappers.h
@@ -0,0 +1,74 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FX_MEMORY_WRAPPERS_H_
+#define CORE_FXCRT_FX_MEMORY_WRAPPERS_H_
+
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "core/fxcrt/fx_memory.h"
+
+// Used with std::unique_ptr to FX_Free raw memory.
+struct FxFreeDeleter {
+  inline void operator()(void* ptr) const { FX_Free(ptr); }
+};
+
+// Used with std::vector<> to put purely numeric vectors into
+// the same "general" parition used by FX_Alloc(). Otherwise,
+// replacing FX_Alloc/FX_Free pairs with std::vector<> may undo
+// some of the nice segregation that we get from partition alloc.
+template <class T>
+struct FxAllocAllocator {
+ public:
+  static_assert(std::is_arithmetic<T>::value,
+                "Only numeric types allowed in this partition");
+
+  using value_type = T;
+  using pointer = T*;
+  using const_pointer = const T*;
+  using reference = T&;
+  using const_reference = const T&;
+  using size_type = size_t;
+  using difference_type = ptrdiff_t;
+
+  template <class U>
+  struct rebind {
+    using other = FxAllocAllocator<U>;
+  };
+
+  FxAllocAllocator() noexcept = default;
+  FxAllocAllocator(const FxAllocAllocator& other) noexcept = default;
+  ~FxAllocAllocator() = default;
+
+  template <typename U>
+  FxAllocAllocator(const FxAllocAllocator<U>& other) noexcept {}
+
+  pointer address(reference x) const noexcept { return &x; }
+  const_pointer address(const_reference x) const noexcept { return &x; }
+  pointer allocate(size_type n, const void* hint = 0) {
+    return static_cast<pointer>(FX_AllocOrDie(n, sizeof(value_type)));
+  }
+  void deallocate(pointer p, size_type n) { FX_Free(p); }
+  size_type max_size() const noexcept {
+    return std::numeric_limits<size_type>::max() / sizeof(value_type);
+  }
+
+  template <class U, class... Args>
+  void construct(U* p, Args&&... args) {
+    new (reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...);
+  }
+
+  template <class U>
+  void destroy(U* p) {
+    p->~U();
+  }
+
+  // There's no state, so they are all the same,
+  bool operator==(const FxAllocAllocator& that) { return true; }
+  bool operator!=(const FxAllocAllocator& that) { return false; }
+};
+
+#endif  // CORE_FXCRT_FX_MEMORY_WRAPPERS_H_
diff --git a/core/fxcrt/fx_memory_wrappers_unittest.cpp b/core/fxcrt/fx_memory_wrappers_unittest.cpp
new file mode 100644
index 0000000..0927683
--- /dev/null
+++ b/core/fxcrt/fx_memory_wrappers_unittest.cpp
@@ -0,0 +1,33 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+
+#include <memory>
+#include <vector>
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcrt, FxFreeDeleter) {
+  std::unique_ptr<int, FxFreeDeleter> empty(nullptr);
+  std::unique_ptr<int, FxFreeDeleter> thing(FX_Alloc(int, 1));
+  std::unique_ptr<int, FxFreeDeleter> several(FX_Alloc(int, 100));
+  EXPECT_FALSE(empty);
+  EXPECT_TRUE(thing);
+  EXPECT_TRUE(several);
+}
+
+TEST(fxcrt, FxAllocAllocator) {
+  std::vector<int, FxAllocAllocator<int>> vec;
+  vec.push_back(42);
+  vec.reserve(100);
+  vec.resize(20);
+  vec[11] = 42;
+
+  std::vector<int, FxAllocAllocator<int>> vec2 = vec;
+  vec = std::move(vec2);
+  vec2.resize(0);
+  vec2.push_back(42);
+}
diff --git a/core/fxcrt/fx_number.cpp b/core/fxcrt/fx_number.cpp
new file mode 100644
index 0000000..9935451
--- /dev/null
+++ b/core/fxcrt/fx_number.cpp
@@ -0,0 +1,98 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxcrt/fx_number.h"
+
+#include <limits>
+
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_string.h"
+
+FX_Number::FX_Number()
+    : m_bInteger(true), m_bSigned(false), m_UnsignedValue(0) {}
+
+FX_Number::FX_Number(int32_t value)
+    : m_bInteger(true), m_bSigned(true), m_SignedValue(value) {}
+
+FX_Number::FX_Number(float value)
+    : m_bInteger(false), m_bSigned(true), m_FloatValue(value) {}
+
+FX_Number::FX_Number(ByteStringView strc)
+    : m_bInteger(true), m_bSigned(false), m_UnsignedValue(0) {
+  if (strc.IsEmpty())
+    return;
+
+  if (strc.Contains('.')) {
+    m_bInteger = false;
+    m_bSigned = true;
+    m_FloatValue = StringToFloat(strc);
+    return;
+  }
+
+  // Note, numbers in PDF are typically of the form 123, -123, etc. But,
+  // for things like the Permissions on the encryption hash the number is
+  // actually an unsigned value. We use a uint32_t so we can deal with the
+  // unsigned and then check for overflow if the user actually signed the value.
+  // The Permissions flag is listed in Table 3.20 PDF 1.7 spec.
+  FX_SAFE_UINT32 unsigned_val = 0;
+  bool bNegative = false;
+  size_t cc = 0;
+  if (strc[0] == '+') {
+    cc++;
+    m_bSigned = true;
+  } else if (strc[0] == '-') {
+    bNegative = true;
+    m_bSigned = true;
+    cc++;
+  }
+
+  while (cc < strc.GetLength() && std::isdigit(strc[cc])) {
+    unsigned_val = unsigned_val * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc));
+    if (!unsigned_val.IsValid())
+      break;
+    cc++;
+  }
+
+  uint32_t uValue = unsigned_val.ValueOrDefault(0);
+  if (!m_bSigned) {
+    m_UnsignedValue = uValue;
+    return;
+  }
+
+  // We have a sign, so if the value was greater then the signed integer
+  // limits, then we've overflowed and must reset to the default value.
+  constexpr uint32_t uLimit =
+      static_cast<uint32_t>(std::numeric_limits<int>::max());
+
+  if (uValue > (bNegative ? uLimit + 1 : uLimit))
+    uValue = 0;
+
+  // Switch back to the int space so we can flip to a negative if we need.
+  int32_t value = static_cast<int32_t>(uValue);
+  if (bNegative) {
+    // |value| is usually positive, except in the corner case of "-2147483648",
+    // where |uValue| is 2147483648. When it gets casted to an int, |value|
+    // becomes -2147483648. For this case, avoid undefined behavior, because
+    // an int32_t cannot represent 2147483648.
+    static constexpr int kMinInt = std::numeric_limits<int>::min();
+    m_SignedValue = LIKELY(value != kMinInt) ? -value : kMinInt;
+  } else {
+    m_SignedValue = value;
+  }
+}
+
+int32_t FX_Number::GetSigned() const {
+  return m_bInteger ? m_SignedValue : static_cast<int32_t>(m_FloatValue);
+}
+
+float FX_Number::GetFloat() const {
+  if (!m_bInteger)
+    return m_FloatValue;
+
+  return m_bSigned ? static_cast<float>(m_SignedValue)
+                   : static_cast<float>(m_UnsignedValue);
+}
diff --git a/core/fxcrt/fx_number.h b/core/fxcrt/fx_number.h
new file mode 100644
index 0000000..0ee4744
--- /dev/null
+++ b/core/fxcrt/fx_number.h
@@ -0,0 +1,38 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_FX_NUMBER_H_
+#define CORE_FXCRT_FX_NUMBER_H_
+
+#include <stdint.h>
+
+#include "core/fxcrt/bytestring.h"
+
+class FX_Number {
+ public:
+  FX_Number();
+  explicit FX_Number(uint32_t value) = delete;
+  explicit FX_Number(int32_t value);
+  explicit FX_Number(float value);
+  explicit FX_Number(ByteStringView str);
+
+  bool IsInteger() const { return m_bInteger; }
+  bool IsSigned() const { return m_bSigned; }
+
+  int32_t GetSigned() const;  // Underflow/Overflow possible.
+  float GetFloat() const;
+
+ private:
+  bool m_bInteger;  // One of the two integers vs. float type.
+  bool m_bSigned;   // Only valid if |m_bInteger|.
+  union {
+    uint32_t m_UnsignedValue;
+    int32_t m_SignedValue;
+    float m_FloatValue;
+  };
+};
+
+#endif  // CORE_FXCRT_FX_NUMBER_H_
diff --git a/core/fxcrt/fx_number_unittest.cpp b/core/fxcrt/fx_number_unittest.cpp
new file mode 100644
index 0000000..a31dc55
--- /dev/null
+++ b/core/fxcrt/fx_number_unittest.cpp
@@ -0,0 +1,136 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+
+#include "core/fxcrt/fx_number.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxnumber, Default) {
+  FX_Number number;
+  EXPECT_TRUE(number.IsInteger());
+  EXPECT_FALSE(number.IsSigned());
+  EXPECT_EQ(0, number.GetSigned());
+  EXPECT_FLOAT_EQ(0.0f, number.GetFloat());
+}
+
+TEST(fxnumber, FromSigned) {
+  FX_Number number(-128);
+  EXPECT_TRUE(number.IsInteger());
+  EXPECT_TRUE(number.IsSigned());
+  EXPECT_EQ(-128, number.GetSigned());
+  EXPECT_FLOAT_EQ(-128.0f, number.GetFloat());
+
+  // Show that assignment works.
+  FX_Number number2 = number;
+  EXPECT_TRUE(number2.IsInteger());
+  EXPECT_TRUE(number2.IsSigned());
+  EXPECT_EQ(-128, number2.GetSigned());
+  EXPECT_FLOAT_EQ(-128.0f, number2.GetFloat());
+}
+
+TEST(fxnumber, FromFloat) {
+  FX_Number number(-100.001f);
+  EXPECT_FALSE(number.IsInteger());
+  EXPECT_TRUE(number.IsSigned());
+  EXPECT_EQ(-100, number.GetSigned());
+  EXPECT_FLOAT_EQ(-100.001f, number.GetFloat());
+
+  // Show that assignment works.
+  FX_Number number2 = number;
+  EXPECT_FALSE(number2.IsInteger());
+  EXPECT_TRUE(number2.IsSigned());
+  EXPECT_EQ(-100, number2.GetSigned());
+  EXPECT_FLOAT_EQ(-100.001f, number2.GetFloat());
+}
+
+TEST(fxnumber, FromStringUnsigned) {
+  {
+    FX_Number number("");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_FALSE(number.IsSigned());
+  }
+  {
+    FX_Number number("0");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_FALSE(number.IsSigned());
+  }
+  {
+    FX_Number number("10");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_FALSE(number.IsSigned());
+  }
+  {
+    FX_Number number("4294967295");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_FALSE(number.IsSigned());
+  }
+  {
+    // Value overflows.
+    FX_Number number("4223423494965252");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_FALSE(number.IsSigned());
+  }
+  {
+    // No explicit sign will allow the number to go negative if we retrieve
+    // it as a signed value. This is needed for things like the encryption
+    // Permissions flag (Table 3.20 PDF 1.7 spec)
+    FX_Number number("4294965252");
+    EXPECT_EQ(-2044, number.GetSigned());
+  }
+}
+
+TEST(fxnumber, FromStringSigned) {
+  {
+    FX_Number number("-0");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_TRUE(number.IsSigned());
+    EXPECT_EQ(0, number.GetSigned());
+  }
+  {
+    FX_Number number("+0");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_TRUE(number.IsSigned());
+    EXPECT_EQ(0, number.GetSigned());
+  }
+  {
+    FX_Number number("-10");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_TRUE(number.IsSigned());
+    EXPECT_EQ(-10, number.GetSigned());
+  }
+  {
+    FX_Number number("+10");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_TRUE(number.IsSigned());
+    EXPECT_EQ(10, number.GetSigned());
+  }
+  {
+    FX_Number number("-2147483648");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_TRUE(number.IsSigned());
+    EXPECT_EQ(std::numeric_limits<int32_t>::min(), number.GetSigned());
+  }
+  {
+    FX_Number number("+2147483647");
+    EXPECT_TRUE(number.IsInteger());
+    EXPECT_TRUE(number.IsSigned());
+    EXPECT_EQ(std::numeric_limits<int32_t>::max(), number.GetSigned());
+  }
+  {
+    // Value underflows.
+    FX_Number number("-2147483649");
+    EXPECT_EQ(0, number.GetSigned());
+  }
+  {
+    // Value overflows.
+    FX_Number number("+2147483648");
+    EXPECT_EQ(0, number.GetSigned());
+  }
+}
+
+TEST(fxnumber, FromStringFloat) {
+  FX_Number number("3.24");
+  EXPECT_FLOAT_EQ(3.24f, number.GetFloat());
+}
diff --git a/core/fxcrt/fx_random.cpp b/core/fxcrt/fx_random.cpp
index 56f84d5..ce796d9 100644
--- a/core/fxcrt/fx_random.cpp
+++ b/core/fxcrt/fx_random.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fxcrt/fx_random.h"
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
@@ -16,12 +17,12 @@
 #define MT_Upper_Mask 0x80000000
 #define MT_Lower_Mask 0x7fffffff
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
 #include <wincrypt.h>
-#else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else
 #include <sys/time.h>
 #include <unistd.h>
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif
 
 namespace {
 
@@ -33,7 +34,7 @@
 bool g_bHaveGlobalSeed = false;
 uint32_t g_nGlobalSeed = 0;
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
 bool GenerateSeedFromCryptoRandom(uint32_t* pSeed) {
   HCRYPTPROV hCP = 0;
   if (!::CryptAcquireContext(&hCP, nullptr, nullptr, PROV_RSA_FULL, 0) ||
@@ -50,30 +51,30 @@
   char c;
   uintptr_t p = reinterpret_cast<uintptr_t>(&c);
   uint32_t seed = ~static_cast<uint32_t>(p >> 3);
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   SYSTEMTIME st;
   GetSystemTime(&st);
   seed ^= static_cast<uint32_t>(st.wSecond) * 1000000;
   seed ^= static_cast<uint32_t>(st.wMilliseconds) * 1000;
   seed ^= GetCurrentProcessId();
-#else   // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else
   struct timeval tv;
   gettimeofday(&tv, 0);
   seed ^= static_cast<uint32_t>(tv.tv_sec) * 1000000;
   seed ^= static_cast<uint32_t>(tv.tv_usec);
   seed ^= static_cast<uint32_t>(getpid());
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif
   return seed;
 }
 
 void* ContextFromNextGlobalSeed() {
   if (!g_bHaveGlobalSeed) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
     if (!GenerateSeedFromCryptoRandom(&g_nGlobalSeed))
       g_nGlobalSeed = GenerateSeedFromEnvironment();
-#else   // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else
     g_nGlobalSeed = GenerateSeedFromEnvironment();
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif
     g_bHaveGlobalSeed = true;
   }
   return FX_Random_MT_Start(++g_nGlobalSeed);
diff --git a/core/fxcrt/fx_safe_types.h b/core/fxcrt/fx_safe_types.h
index 51eb89c..6f5b967 100644
--- a/core/fxcrt/fx_safe_types.h
+++ b/core/fxcrt/fx_safe_types.h
@@ -10,9 +10,9 @@
 #include "core/fxcrt/fx_system.h"
 #include "third_party/base/numerics/safe_math.h"
 
-typedef pdfium::base::CheckedNumeric<uint32_t> FX_SAFE_UINT32;
-typedef pdfium::base::CheckedNumeric<int32_t> FX_SAFE_INT32;
-typedef pdfium::base::CheckedNumeric<size_t> FX_SAFE_SIZE_T;
-typedef pdfium::base::CheckedNumeric<FX_FILESIZE> FX_SAFE_FILESIZE;
+using FX_SAFE_UINT32 = pdfium::base::CheckedNumeric<uint32_t>;
+using FX_SAFE_INT32 = pdfium::base::CheckedNumeric<int32_t>;
+using FX_SAFE_SIZE_T = pdfium::base::CheckedNumeric<size_t>;
+using FX_SAFE_FILESIZE = pdfium::base::CheckedNumeric<FX_FILESIZE>;
 
 #endif  // CORE_FXCRT_FX_SAFE_TYPES_H_
diff --git a/core/fxcrt/fx_stream.cpp b/core/fxcrt/fx_stream.cpp
index bd75ad6..dd3c1fb 100644
--- a/core/fxcrt/fx_stream.cpp
+++ b/core/fxcrt/fx_stream.cpp
@@ -11,10 +11,31 @@
 #include <utility>
 #include <vector>
 
+#include "build/build_config.h"
+#include "core/fxcrt/fileaccess_iface.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/ifx_fileaccess.h"
 #include "third_party/base/ptr_util.h"
 
+#if defined(OS_WIN)
+#include <direct.h>
+
+struct FX_FolderHandle {
+  HANDLE m_Handle;
+  bool m_bEnd;
+  WIN32_FIND_DATAA m_FindData;
+};
+#else
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+struct FX_FolderHandle {
+  ByteString m_Path;
+  DIR* m_Dir;
+};
+#endif
+
 namespace {
 
 class CFX_CRTFileStream final : public IFX_SeekableStream {
@@ -26,25 +47,27 @@
   FX_FILESIZE GetSize() override { return m_pFile->GetSize(); }
   bool IsEOF() override { return GetPosition() >= GetSize(); }
   FX_FILESIZE GetPosition() override { return m_pFile->GetPosition(); }
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override {
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override {
     return m_pFile->ReadPos(buffer, size, offset) > 0;
   }
   size_t ReadBlock(void* buffer, size_t size) override {
     return m_pFile->Read(buffer, size);
   }
-  bool WriteBlock(const void* buffer,
-                  FX_FILESIZE offset,
-                  size_t size) override {
+  bool WriteBlockAtOffset(const void* buffer,
+                          FX_FILESIZE offset,
+                          size_t size) override {
     return !!m_pFile->WritePos(buffer, size, offset);
   }
   bool Flush() override { return m_pFile->Flush(); }
 
  private:
-  explicit CFX_CRTFileStream(std::unique_ptr<IFX_FileAccess> pFA)
+  explicit CFX_CRTFileStream(std::unique_ptr<FileAccessIface> pFA)
       : m_pFile(std::move(pFA)) {}
   ~CFX_CRTFileStream() override {}
 
-  std::unique_ptr<IFX_FileAccess> m_pFile;
+  std::unique_ptr<FileAccessIface> m_pFile;
 };
 
 }  // namespace
@@ -53,7 +76,7 @@
 RetainPtr<IFX_SeekableStream> IFX_SeekableStream::CreateFromFilename(
     const char* filename,
     uint32_t dwModes) {
-  std::unique_ptr<IFX_FileAccess> pFA = IFX_FileAccess::Create();
+  std::unique_ptr<FileAccessIface> pFA = FileAccessIface::Create();
   if (!pFA->Open(filename, dwModes))
     return nullptr;
   return pdfium::MakeRetain<CFX_CRTFileStream>(std::move(pFA));
@@ -63,7 +86,7 @@
 RetainPtr<IFX_SeekableStream> IFX_SeekableStream::CreateFromFilename(
     const wchar_t* filename,
     uint32_t dwModes) {
-  std::unique_ptr<IFX_FileAccess> pFA = IFX_FileAccess::Create();
+  std::unique_ptr<FileAccessIface> pFA = FileAccessIface::Create();
   if (!pFA->Open(filename, dwModes))
     return nullptr;
   return pdfium::MakeRetain<CFX_CRTFileStream>(std::move(pFA));
@@ -76,7 +99,7 @@
 }
 
 bool IFX_SeekableWriteStream::WriteBlock(const void* pData, size_t size) {
-  return WriteBlock(pData, GetSize(), size);
+  return WriteBlockAtOffset(pData, GetSize(), size);
 }
 
 bool IFX_SeekableReadStream::IsEOF() {
@@ -92,36 +115,41 @@
 }
 
 bool IFX_SeekableStream::WriteBlock(const void* buffer, size_t size) {
-  return WriteBlock(buffer, GetSize(), size);
+  return WriteBlockAtOffset(buffer, GetSize(), size);
 }
 
-bool IFX_SeekableStream::WriteString(const ByteStringView& str) {
+bool IFX_SeekableStream::WriteString(ByteStringView str) {
   return WriteBlock(str.unterminated_c_str(), str.GetLength());
 }
 
-FX_FileHandle* FX_OpenFolder(const char* path) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  auto pData = pdfium::MakeUnique<CFindFileDataA>();
-  pData->m_Handle =
+FX_FolderHandle* FX_OpenFolder(const char* path) {
+  auto handle = pdfium::MakeUnique<FX_FolderHandle>();
+#if defined(OS_WIN)
+  handle->m_Handle =
       FindFirstFileExA((ByteString(path) + "/*.*").c_str(), FindExInfoStandard,
-                       &pData->m_FindData, FindExSearchNameMatch, nullptr, 0);
-  if (pData->m_Handle == INVALID_HANDLE_VALUE)
+                       &handle->m_FindData, FindExSearchNameMatch, nullptr, 0);
+  if (handle->m_Handle == INVALID_HANDLE_VALUE)
     return nullptr;
 
-  pData->m_bEnd = false;
-  return pData.release();
+  handle->m_bEnd = false;
 #else
-  return opendir(path);
+  DIR* dir = opendir(path);
+  if (!dir)
+    return nullptr;
+
+  handle->m_Path = path;
+  handle->m_Dir = dir;
 #endif
+  return handle.release();
 }
 
-bool FX_GetNextFile(FX_FileHandle* handle,
+bool FX_GetNextFile(FX_FolderHandle* handle,
                     ByteString* filename,
                     bool* bFolder) {
   if (!handle)
     return false;
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   if (handle->m_bEnd)
     return false;
 
@@ -132,23 +160,28 @@
     handle->m_bEnd = true;
   return true;
 #else
-  struct dirent* de = readdir(handle);
+  struct dirent* de = readdir(handle->m_Dir);
   if (!de)
     return false;
+  ByteString fullpath = handle->m_Path + "/" + de->d_name;
+  struct stat deStat;
+  if (stat(fullpath.c_str(), &deStat) < 0)
+    return false;
+
   *filename = de->d_name;
-  *bFolder = de->d_type == DT_DIR;
+  *bFolder = S_ISDIR(deStat.st_mode);
   return true;
 #endif
 }
 
-void FX_CloseFolder(FX_FileHandle* handle) {
+void FX_CloseFolder(FX_FolderHandle* handle) {
   if (!handle)
     return;
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   FindClose(handle->m_Handle);
-  delete handle;
 #else
-  closedir(handle);
+  closedir(handle->m_Dir);
 #endif
+  delete handle;
 }
diff --git a/core/fxcrt/fx_stream.h b/core/fxcrt/fx_stream.h
index 8d0a596..ef58660 100644
--- a/core/fxcrt/fx_stream.h
+++ b/core/fxcrt/fx_stream.h
@@ -10,35 +10,31 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/compiler_specific.h"
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#include <direct.h>
+struct FX_FolderHandle;
 
-class CFindFileDataA;
-typedef CFindFileDataA FX_FileHandle;
+FX_FolderHandle* FX_OpenFolder(const char* path);
+bool FX_GetNextFile(FX_FolderHandle* handle,
+                    ByteString* filename,
+                    bool* bFolder);
+void FX_CloseFolder(FX_FolderHandle* handle);
 
-#else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-typedef DIR FX_FileHandle;
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-FX_FileHandle* FX_OpenFolder(const char* path);
-bool FX_GetNextFile(FX_FileHandle* handle, ByteString* filename, bool* bFolder);
-void FX_CloseFolder(FX_FileHandle* handle);
+// Used with std::unique_ptr to automatically call FX_CloseFolder().
+struct FxFolderHandleCloser {
+  inline void operator()(FX_FolderHandle* h) const { FX_CloseFolder(h); }
+};
 
 #define FX_FILEMODE_ReadOnly 1
 #define FX_FILEMODE_Truncate 2
 
-class IFX_WriteStream : virtual public Retainable {
+class IFX_WriteStream {
  public:
   virtual bool WriteBlock(const void* pData, size_t size) = 0;
-  virtual bool WriteString(const ByteStringView& str) = 0;
+  virtual bool WriteString(ByteStringView str) = 0;
+
+ protected:
+  virtual ~IFX_WriteStream() = default;
 };
 
 class IFX_ArchiveStream : public IFX_WriteStream {
@@ -48,37 +44,39 @@
   virtual FX_FILESIZE CurrentOffset() const = 0;
 };
 
-class IFX_ReadStream : virtual public Retainable {
+class IFX_StreamWithSize {
  public:
-  virtual bool IsEOF() = 0;
-  virtual FX_FILESIZE GetPosition() = 0;
-  virtual size_t ReadBlock(void* buffer, size_t size) = 0;
+  virtual FX_FILESIZE GetSize() = 0;
 };
 
-class IFX_SeekableWriteStream : public IFX_WriteStream {
+class IFX_RetainableWriteStream : virtual public Retainable,
+                                  public IFX_WriteStream {};
+
+class IFX_SeekableWriteStream : virtual public IFX_StreamWithSize,
+                                public IFX_RetainableWriteStream {
  public:
   // IFX_WriteStream:
   bool WriteBlock(const void* pData, size_t size) override;
 
-  virtual FX_FILESIZE GetSize() = 0;
   virtual bool Flush() = 0;
-  virtual bool WriteBlock(const void* pData,
-                          FX_FILESIZE offset,
-                          size_t size) = 0;
+  virtual bool WriteBlockAtOffset(const void* pData,
+                                  FX_FILESIZE offset,
+                                  size_t size) = 0;
 };
 
-class IFX_SeekableReadStream : public IFX_ReadStream {
+class IFX_SeekableReadStream : virtual public Retainable,
+                               virtual public IFX_StreamWithSize {
  public:
   static RetainPtr<IFX_SeekableReadStream> CreateFromFilename(
       const char* filename);
 
-  // IFX_ReadStream:
-  bool IsEOF() override;
-  FX_FILESIZE GetPosition() override;
-  size_t ReadBlock(void* buffer, size_t size) override;
+  virtual bool IsEOF();
+  virtual FX_FILESIZE GetPosition();
+  virtual size_t ReadBlock(void* buffer, size_t size);
 
-  virtual bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) = 0;
-  virtual FX_FILESIZE GetSize() = 0;
+  virtual bool ReadBlockAtOffset(void* buffer,
+                                 FX_FILESIZE offset,
+                                 size_t size) WARN_UNUSED_RESULT = 0;
 };
 
 class IFX_SeekableStream : public IFX_SeekableReadStream,
@@ -91,36 +89,9 @@
       const wchar_t* filename,
       uint32_t dwModes);
 
-  // IFX_SeekableReadStream:
-  bool IsEOF() override = 0;
-  FX_FILESIZE GetPosition() override = 0;
-  size_t ReadBlock(void* buffer, size_t size) override = 0;
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override = 0;
-  FX_FILESIZE GetSize() override = 0;
-
   // IFX_SeekableWriteStream:
-  bool WriteBlock(const void* buffer,
-                  FX_FILESIZE offset,
-                  size_t size) override = 0;
   bool WriteBlock(const void* buffer, size_t size) override;
-  bool WriteString(const ByteStringView& str) override;
-
-  bool Flush() override = 0;
+  bool WriteString(ByteStringView str) override;
 };
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-class CFindFileData {
- public:
-  virtual ~CFindFileData() {}
-  HANDLE m_Handle;
-  bool m_bEnd;
-};
-
-class CFindFileDataA : public CFindFileData {
- public:
-  ~CFindFileDataA() override {}
-  WIN32_FIND_DATAA m_FindData;
-};
-#endif
-
 #endif  // CORE_FXCRT_FX_STREAM_H_
diff --git a/core/fxcrt/fx_string.cpp b/core/fxcrt/fx_string.cpp
index 13eb3a5..d4a6d38 100644
--- a/core/fxcrt/fx_string.cpp
+++ b/core/fxcrt/fx_string.cpp
@@ -4,146 +4,52 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include "core/fxcrt/fx_string.h"
+
 #include <limits>
 #include <vector>
 
+#include "core/fxcrt/cfx_utf8decoder.h"
+#include "core/fxcrt/cfx_utf8encoder.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_string.h"
+#include "third_party/base/compiler_specific.h"
 
-namespace {
-
-class CFX_UTF8Encoder {
- public:
-  CFX_UTF8Encoder() {}
-  ~CFX_UTF8Encoder() {}
-
-  void Input(wchar_t unicodeAsWchar) {
-    uint32_t unicode = static_cast<uint32_t>(unicodeAsWchar);
-    if (unicode < 0x80) {
-      m_Buffer.push_back(unicode);
-    } else {
-      if (unicode >= 0x80000000)
-        return;
-
-      int nbytes = 0;
-      if (unicode < 0x800)
-        nbytes = 2;
-      else if (unicode < 0x10000)
-        nbytes = 3;
-      else if (unicode < 0x200000)
-        nbytes = 4;
-      else if (unicode < 0x4000000)
-        nbytes = 5;
-      else
-        nbytes = 6;
-
-      static uint8_t prefix[] = {0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
-      int order = 1 << ((nbytes - 1) * 6);
-      int code = unicodeAsWchar;
-      m_Buffer.push_back(prefix[nbytes - 2] | (code / order));
-      for (int i = 0; i < nbytes - 1; i++) {
-        code = code % order;
-        order >>= 6;
-        m_Buffer.push_back(0x80 | (code / order));
-      }
-    }
-  }
-
-  // The data returned by GetResult() is invalidated when this is modified by
-  // appending any data.
-  ByteStringView GetResult() const {
-    return ByteStringView(m_Buffer.data(), m_Buffer.size());
-  }
-
- private:
-  std::vector<uint8_t> m_Buffer;
-};
-
-}  // namespace
-
-ByteString FX_UTF8Encode(const WideStringView& wsStr) {
-  size_t len = wsStr.GetLength();
-  const wchar_t* pStr = wsStr.unterminated_c_str();
+ByteString FX_UTF8Encode(WideStringView wsStr) {
   CFX_UTF8Encoder encoder;
-  while (len-- > 0)
-    encoder.Input(*pStr++);
+  for (size_t i = 0; i < wsStr.GetLength(); ++i)
+    encoder.Input(wsStr[i]);
 
   return ByteString(encoder.GetResult());
 }
 
+WideString FX_UTF8Decode(ByteStringView bsStr) {
+  if (bsStr.IsEmpty())
+    return WideString();
+
+  CFX_UTF8Decoder decoder;
+  for (size_t i = 0; i < bsStr.GetLength(); i++)
+    decoder.Input(bsStr[i]);
+
+  return WideString(decoder.GetResult());
+}
+
 namespace {
 
-const float fraction_scales[] = {0.1f,          0.01f,         0.001f,
-                                 0.0001f,       0.00001f,      0.000001f,
-                                 0.0000001f,    0.00000001f,   0.000000001f,
-                                 0.0000000001f, 0.00000000001f};
+constexpr float kFractionScalesFloat[] = {
+    0.1f,         0.01f,         0.001f,        0.0001f,
+    0.00001f,     0.000001f,     0.0000001f,    0.00000001f,
+    0.000000001f, 0.0000000001f, 0.00000000001f};
 
-float FractionalScale(size_t scale_factor, int value) {
-  return fraction_scales[scale_factor] * value;
-}
+const double kFractionScalesDouble[] = {
+    0.1,       0.01,       0.001,       0.0001,       0.00001,      0.000001,
+    0.0000001, 0.00000001, 0.000000001, 0.0000000001, 0.00000000001};
 
-}  // namespace
-
-bool FX_atonum(const ByteStringView& strc, void* pData) {
-  if (strc.Contains('.')) {
-    float* pFloat = static_cast<float*>(pData);
-    *pFloat = FX_atof(strc);
-    return false;
-  }
-
-  // Note, numbers in PDF are typically of the form 123, -123, etc. But,
-  // for things like the Permissions on the encryption hash the number is
-  // actually an unsigned value. We use a uint32_t so we can deal with the
-  // unsigned and then check for overflow if the user actually signed the value.
-  // The Permissions flag is listed in Table 3.20 PDF 1.7 spec.
-  pdfium::base::CheckedNumeric<uint32_t> integer = 0;
-  bool bNegative = false;
-  bool bSigned = false;
-  size_t cc = 0;
-  if (strc[0] == '+') {
-    cc++;
-    bSigned = true;
-  } else if (strc[0] == '-') {
-    bNegative = true;
-    bSigned = true;
-    cc++;
-  }
-
-  while (cc < strc.GetLength() && std::isdigit(strc[cc])) {
-    integer = integer * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc));
-    if (!integer.IsValid())
-      break;
-    cc++;
-  }
-
-  // We have a sign, and the value was greater then a regular integer
-  // we've overflowed, reset to the default value.
-  if (bSigned) {
-    if (bNegative) {
-      if (integer.ValueOrDefault(0) >
-          static_cast<uint32_t>(std::numeric_limits<int>::max()) + 1) {
-        integer = 0;
-      }
-    } else if (integer.ValueOrDefault(0) >
-               static_cast<uint32_t>(std::numeric_limits<int>::max())) {
-      integer = 0;
-    }
-  }
-
-  // Switch back to the int space so we can flip to a negative if we need.
-  uint32_t uValue = integer.ValueOrDefault(0);
-  int32_t value = static_cast<int>(uValue);
-  if (bNegative)
-    value = -value;
-
-  int* pInt = static_cast<int*>(pData);
-  *pInt = value;
-  return true;
-}
-
-float FX_atof(const ByteStringView& strc) {
+template <class T>
+T StringTo(ByteStringView strc,
+           const T fractional_scales[],
+           size_t fractional_scales_size) {
   if (strc.IsEmpty())
-    return 0.0;
+    return 0;
 
   int cc = 0;
   bool bNegative = false;
@@ -159,20 +65,21 @@
       break;
     cc++;
   }
-  float value = 0;
+  T value = 0;
   while (cc < len) {
     if (strc[cc] == '.')
       break;
     value = value * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc));
     cc++;
   }
-  int scale = 0;
+  size_t scale = 0;
   if (cc < len && strc[cc] == '.') {
     cc++;
     while (cc < len) {
-      value += FractionalScale(scale, FXSYS_DecimalCharToInt(strc.CharAt(cc)));
+      value +=
+          fractional_scales[scale] * FXSYS_DecimalCharToInt(strc.CharAt(cc));
       scale++;
-      if (scale == FX_ArraySize(fraction_scales))
+      if (scale == fractional_scales_size)
         break;
       cc++;
     }
@@ -180,29 +87,26 @@
   return bNegative ? -value : value;
 }
 
-float FX_atof(const WideStringView& wsStr) {
-  return FX_atof(FX_UTF8Encode(wsStr).c_str());
-}
-
-size_t FX_ftoa(float d, char* buf) {
+template <class T>
+size_t ToString(T value, int (*round_func)(T), char* buf) {
   buf[0] = '0';
   buf[1] = '\0';
-  if (d == 0.0f) {
+  if (value == 0) {
     return 1;
   }
   bool bNegative = false;
-  if (d < 0) {
+  if (value < 0) {
     bNegative = true;
-    d = -d;
+    value = -value;
   }
   int scale = 1;
-  int scaled = FXSYS_round(d);
+  int scaled = round_func(value);
   while (scaled < 100000) {
     if (scale == 1000000) {
       break;
     }
     scale *= 10;
-    scaled = FXSYS_round(d * scale);
+    scaled = round_func(value * scale);
   }
   if (scaled == 0) {
     return 1;
@@ -230,3 +134,31 @@
   }
   return buf_size;
 }
+
+}  // namespace
+
+float StringToFloat(ByteStringView strc) {
+  return StringTo<float>(strc, kFractionScalesFloat,
+                         FX_ArraySize(kFractionScalesFloat));
+}
+
+float StringToFloat(WideStringView wsStr) {
+  return StringToFloat(FX_UTF8Encode(wsStr).c_str());
+}
+
+size_t FloatToString(float f, char* buf) {
+  return ToString<float>(f, FXSYS_roundf, buf);
+}
+
+double StringToDouble(ByteStringView strc) {
+  return StringTo<double>(strc, kFractionScalesDouble,
+                          FX_ArraySize(kFractionScalesDouble));
+}
+
+double StringToDouble(WideStringView wsStr) {
+  return StringToDouble(FX_UTF8Encode(wsStr).c_str());
+}
+
+size_t DoubleToString(double d, char* buf) {
+  return ToString<double>(d, FXSYS_round, buf);
+}
diff --git a/core/fxcrt/fx_string.h b/core/fxcrt/fx_string.h
index 4c24181..e27f43a 100644
--- a/core/fxcrt/fx_string.h
+++ b/core/fxcrt/fx_string.h
@@ -7,6 +7,10 @@
 #ifndef CORE_FXCRT_FX_STRING_H_
 #define CORE_FXCRT_FX_STRING_H_
 
+#include <stdint.h>
+
+#include <vector>
+
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/widestring.h"
 
@@ -14,10 +18,34 @@
   (((uint32_t)c1 << 24) | ((uint32_t)c2 << 16) | ((uint32_t)c3 << 8) | \
    ((uint32_t)c4))
 
-ByteString FX_UTF8Encode(const WideStringView& wsStr);
-float FX_atof(const ByteStringView& str);
-float FX_atof(const WideStringView& wsStr);
-bool FX_atonum(const ByteStringView& str, void* pData);
-size_t FX_ftoa(float f, char* buf);
+ByteString FX_UTF8Encode(WideStringView wsStr);
+WideString FX_UTF8Decode(ByteStringView bsStr);
+
+float StringToFloat(ByteStringView str);
+float StringToFloat(WideStringView wsStr);
+size_t FloatToString(float f, char* buf);
+
+double StringToDouble(ByteStringView str);
+double StringToDouble(WideStringView wsStr);
+size_t DoubleToString(double d, char* buf);
+
+namespace fxcrt {
+
+template <typename StrType>
+std::vector<StrType> Split(const StrType& that, typename StrType::CharType ch) {
+  std::vector<StrType> result;
+  StringViewTemplate<typename StrType::CharType> remaining(that.span());
+  while (1) {
+    Optional<size_t> index = remaining.Find(ch);
+    if (!index.has_value())
+      break;
+    result.emplace_back(remaining.First(index.value()));
+    remaining = remaining.Last(remaining.GetLength() - index.value() - 1);
+  }
+  result.emplace_back(remaining);
+  return result;
+}
+
+}  // namespace fxcrt
 
 #endif  // CORE_FXCRT_FX_STRING_H_
diff --git a/core/fxcrt/fx_string_unittest.cpp b/core/fxcrt/fx_string_unittest.cpp
index b311165..9556360 100644
--- a/core/fxcrt/fx_string_unittest.cpp
+++ b/core/fxcrt/fx_string_unittest.cpp
@@ -7,47 +7,467 @@
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(fxstring, FX_atonum) {
-  int i;
-  EXPECT_TRUE(FX_atonum("10", &i));
-  EXPECT_EQ(10, i);
+char* TerminatedFloatToString(float value, char* buf) {
+  size_t buflen = FloatToString(value, buf);
+  buf[buflen] = '\0';
+  return buf;
+}
 
-  EXPECT_TRUE(FX_atonum("-10", &i));
-  EXPECT_EQ(-10, i);
+char* TerminatedDoubleToString(double value, char* buf) {
+  size_t buflen = DoubleToString(value, buf);
+  buf[buflen] = '\0';
+  return buf;
+}
 
-  EXPECT_TRUE(FX_atonum("+10", &i));
-  EXPECT_EQ(10, i);
+TEST(fxstring, FX_UTF8Encode) {
+  EXPECT_EQ("", FX_UTF8Encode(WideStringView()));
+  EXPECT_EQ(
+      "x"
+      "\xc2\x80"
+      "\xc3\xbf"
+      "\xef\xbc\xac"
+      "y",
+      FX_UTF8Encode(L"x"
+                    L"\u0080"
+                    L"\u00ff"
+                    L"\uff2c"
+                    L"y"));
+}
 
-  EXPECT_TRUE(FX_atonum("-2147483648", &i));
-  EXPECT_EQ(std::numeric_limits<int>::min(), i);
+TEST(fxstring, FX_UTF8Decode) {
+  EXPECT_EQ(L"", FX_UTF8Decode(ByteStringView()));
+  EXPECT_EQ(
+      L"x"
+      L"\u0080"
+      L"\u00ff"
+      L"\uff2c"
+      L"y",
+      FX_UTF8Decode("x"
+                    "\xc2\x80"
+                    "\xc3\xbf"
+                    "\xef\xbc\xac"
+                    "y"));
+  EXPECT_EQ(L"a(A) b() c() d() e().",
+            FX_UTF8Decode("a(\xc2\x41) "      // Invalid continuation.
+                          "b(\xc2\xc2) "      // Invalid continuation.
+                          "c(\xc2\xff\x80) "  // Invalid continuation.
+                          "d(\x80\x80) "      // Invalid leading.
+                          "e(\xff\x80\x80)"   // Invalid leading.
+                          "."));
+}
 
-  EXPECT_TRUE(FX_atonum("2147483647", &i));
-  EXPECT_EQ(2147483647, i);
+TEST(fxstring, FX_UTF8EncodeDecodeConsistency) {
+  WideString wstr;
+  wstr.Reserve(0x10000);
+  for (int w = 0; w < 0x10000; ++w)
+    wstr += static_cast<wchar_t>(w);
 
-  // Value overflows.
-  EXPECT_TRUE(FX_atonum("-2147483649", &i));
-  EXPECT_EQ(0, i);
+  ByteString bstr = FX_UTF8Encode(wstr.AsStringView());
+  WideString wstr2 = FX_UTF8Decode(bstr.AsStringView());
+  EXPECT_EQ(0x10000u, wstr2.GetLength());
+  EXPECT_EQ(wstr, wstr2);
+}
 
-  // Value overflows.
-  EXPECT_TRUE(FX_atonum("+2147483648", &i));
-  EXPECT_EQ(0, i);
+TEST(fxstring, ByteStringToFloat) {
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat(""));
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat("0"));
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat("0.0"));
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat("-0.0"));
 
-  // Value overflows.
-  EXPECT_TRUE(FX_atonum("4223423494965252", &i));
-  EXPECT_EQ(0, i);
+  EXPECT_FLOAT_EQ(0.25f, StringToFloat("0.25"));
+  EXPECT_FLOAT_EQ(-0.25f, StringToFloat("-0.25"));
 
-  // No explicit sign will allow the number to go negative. This is for things
-  // like the encryption Permissions flag (Table 3.20 PDF 1.7 spec)
-  EXPECT_TRUE(FX_atonum("4294965252", &i));
-  EXPECT_EQ(-2044, i);
+  EXPECT_FLOAT_EQ(100.0f, StringToFloat("100"));
+  EXPECT_FLOAT_EQ(100.0f, StringToFloat("100.0"));
+  EXPECT_FLOAT_EQ(100.0f, StringToFloat("    100.0"));
+  EXPECT_FLOAT_EQ(-100.0f, StringToFloat("-100.0000"));
 
-  EXPECT_TRUE(FX_atonum("-4294965252", &i));
-  EXPECT_EQ(0, i);
+  EXPECT_FLOAT_EQ(3.402823e+38f,
+                  StringToFloat("340282300000000000000000000000000000000"));
+  EXPECT_FLOAT_EQ(-3.402823e+38f,
+                  StringToFloat("-340282300000000000000000000000000000000"));
 
-  EXPECT_TRUE(FX_atonum("+4294965252", &i));
-  EXPECT_EQ(0, i);
+  EXPECT_FLOAT_EQ(1.000000119f, StringToFloat("1.000000119"));
+  EXPECT_FLOAT_EQ(1.999999881f, StringToFloat("1.999999881"));
+}
 
-  float f;
-  EXPECT_FALSE(FX_atonum("3.24", &f));
-  EXPECT_FLOAT_EQ(3.24f, f);
+TEST(fxstring, WideStringToFloat) {
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat(L""));
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat(L"0"));
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat(L"0.0"));
+  EXPECT_FLOAT_EQ(0.0f, StringToFloat(L"-0.0"));
+
+  EXPECT_FLOAT_EQ(0.25f, StringToFloat(L"0.25"));
+  EXPECT_FLOAT_EQ(-0.25f, StringToFloat(L"-0.25"));
+
+  EXPECT_FLOAT_EQ(100.0f, StringToFloat(L"100"));
+  EXPECT_FLOAT_EQ(100.0f, StringToFloat(L"100.0"));
+  EXPECT_FLOAT_EQ(100.0f, StringToFloat(L"    100.0"));
+  EXPECT_FLOAT_EQ(-100.0f, StringToFloat(L"-100.0000"));
+
+  EXPECT_FLOAT_EQ(3.402823e+38f,
+                  StringToFloat(L"340282300000000000000000000000000000000"));
+  EXPECT_FLOAT_EQ(-3.402823e+38f,
+                  StringToFloat(L"-340282300000000000000000000000000000000"));
+
+  EXPECT_FLOAT_EQ(1.000000119f, StringToFloat(L"1.000000119"));
+  EXPECT_FLOAT_EQ(1.999999881f, StringToFloat(L"1.999999881"));
+}
+
+TEST(fxstring, FloatToString) {
+  char buf[32];
+
+  EXPECT_STREQ("0", TerminatedFloatToString(0.0f, buf));
+  EXPECT_STREQ("0", TerminatedFloatToString(-0.0f, buf));
+  EXPECT_STREQ("0",
+               TerminatedFloatToString(std::numeric_limits<float>::min(), buf));
+  EXPECT_STREQ(
+      "0", TerminatedFloatToString(-std::numeric_limits<float>::min(), buf));
+
+  EXPECT_STREQ("0.25", TerminatedFloatToString(0.25f, buf));
+  EXPECT_STREQ("-0.25", TerminatedFloatToString(-0.25f, buf));
+
+  EXPECT_STREQ("100", TerminatedFloatToString(100.0f, buf));
+  EXPECT_STREQ("-100", TerminatedFloatToString(-100.0f, buf));
+
+  // FloatToString won't convert beyond the maximum integer, and values
+  // larger than that get converted to a string representing that.
+  EXPECT_STREQ("2147483647", TerminatedFloatToString(2147483647.0f, buf));
+  EXPECT_STREQ("2147483647", TerminatedFloatToString(2147483647.5f, buf));
+  EXPECT_STREQ("2147483647",
+               TerminatedFloatToString(std::numeric_limits<float>::max(), buf));
+
+  // FloatToString won't convert beyond the minimum integer, and values
+  // smaller than that get converted to a string representing that.
+  EXPECT_STREQ("-2147483647", TerminatedFloatToString(-2147483647.0f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedFloatToString(-2147483647.5f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedFloatToString(
+                                  -std::numeric_limits<float>::max(), buf));
+
+  // Conversion only acknowledges precision to 5 digit past decimal, and
+  // rounds beyond that.
+  EXPECT_STREQ("1", TerminatedFloatToString(1.000001119f, buf));
+  EXPECT_STREQ("1.00001", TerminatedFloatToString(1.000011119f, buf));
+  EXPECT_STREQ("1.99999", TerminatedFloatToString(1.999988881f, buf));
+  EXPECT_STREQ("2", TerminatedFloatToString(1.999999881f, buf));
+}
+
+TEST(fxstring, ByteStringToDouble) {
+  EXPECT_FLOAT_EQ(0.0, StringToDouble(""));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble("0"));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble("0.0"));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble("-0.0"));
+
+  EXPECT_FLOAT_EQ(0.25, StringToDouble("0.25"));
+  EXPECT_FLOAT_EQ(-0.25, StringToDouble("-0.25"));
+
+  EXPECT_FLOAT_EQ(100.0, StringToDouble("100"));
+  EXPECT_FLOAT_EQ(100.0, StringToDouble("100.0"));
+  EXPECT_FLOAT_EQ(100.0, StringToDouble("    100.0"));
+  EXPECT_FLOAT_EQ(-100.0, StringToDouble("-100.0000"));
+
+  EXPECT_FLOAT_EQ(3.402823e+38,
+                  StringToDouble("340282300000000000000000000000000000000"));
+  EXPECT_FLOAT_EQ(-3.402823e+38,
+                  StringToDouble("-340282300000000000000000000000000000000"));
+
+  EXPECT_FLOAT_EQ(1.000000119, StringToDouble("1.000000119"));
+  EXPECT_FLOAT_EQ(1.999999881, StringToDouble("1.999999881"));
+}
+
+TEST(fxstring, WideStringToDouble) {
+  EXPECT_FLOAT_EQ(0.0, StringToDouble(L""));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble(L"0"));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble(L"0.0"));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble(L"-0.0"));
+
+  EXPECT_FLOAT_EQ(0.25, StringToDouble(L"0.25"));
+  EXPECT_FLOAT_EQ(-0.25, StringToDouble(L"-0.25"));
+
+  EXPECT_FLOAT_EQ(100.0, StringToDouble(L"100"));
+  EXPECT_FLOAT_EQ(100.0, StringToDouble(L"100.0"));
+  EXPECT_FLOAT_EQ(100.0, StringToDouble(L"    100.0"));
+  EXPECT_FLOAT_EQ(-100.0, StringToDouble(L"-100.0000"));
+
+  EXPECT_FLOAT_EQ(3.402823e+38,
+                  StringToDouble(L"340282300000000000000000000000000000000"));
+  EXPECT_FLOAT_EQ(-3.402823e+38,
+                  StringToDouble(L"-340282300000000000000000000000000000000"));
+
+  EXPECT_FLOAT_EQ(1.000000119, StringToDouble(L"1.000000119"));
+  EXPECT_FLOAT_EQ(1.999999881, StringToDouble(L"1.999999881"));
+}
+
+TEST(fxstring, DoubleToString) {
+  char buf[32];
+
+  EXPECT_STREQ("0", TerminatedDoubleToString(0.0f, buf));
+  EXPECT_STREQ("0", TerminatedDoubleToString(-0.0f, buf));
+  EXPECT_STREQ(
+      "0", TerminatedDoubleToString(std::numeric_limits<double>::min(), buf));
+  EXPECT_STREQ(
+      "0", TerminatedDoubleToString(-std::numeric_limits<double>::min(), buf));
+
+  EXPECT_STREQ("0.25", TerminatedDoubleToString(0.25f, buf));
+  EXPECT_STREQ("-0.25", TerminatedDoubleToString(-0.25f, buf));
+
+  EXPECT_STREQ("100", TerminatedDoubleToString(100.0f, buf));
+  EXPECT_STREQ("-100", TerminatedDoubleToString(-100.0f, buf));
+
+  // DoubleToString won't convert beyond the maximum integer, and values
+  // larger than that get converted to a string representing that.
+  EXPECT_STREQ("2147483647", TerminatedDoubleToString(2147483647.0f, buf));
+  EXPECT_STREQ("2147483647", TerminatedDoubleToString(2147483647.5f, buf));
+  EXPECT_STREQ("2147483647", TerminatedDoubleToString(
+                                 std::numeric_limits<double>::max(), buf));
+
+  // DoubleToString won't convert beyond the minimum integer, and values
+  // smaller than that get converted to a string representing that.
+  EXPECT_STREQ("-2147483647", TerminatedDoubleToString(-2147483647.0f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedDoubleToString(-2147483647.5f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedDoubleToString(
+                                  -std::numeric_limits<double>::max(), buf));
+
+  // Conversion only acknowledges precision to 5 digit past decimal, and
+  // rounds beyond that.
+  EXPECT_STREQ("1", TerminatedDoubleToString(1.000001119f, buf));
+  EXPECT_STREQ("1.00001", TerminatedDoubleToString(1.000011119f, buf));
+  EXPECT_STREQ("1.99999", TerminatedDoubleToString(1.999988881f, buf));
+  EXPECT_STREQ("2", TerminatedDoubleToString(1.999999881f, buf));
+}
+
+TEST(fxstring, SplitByteString) {
+  std::vector<ByteString> result;
+  result = fxcrt::Split(ByteString(""), ',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ("", result[0]);
+
+  result = fxcrt::Split(ByteString("a"), ',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ("a", result[0]);
+
+  result = fxcrt::Split(ByteString(","), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("", result[0]);
+  EXPECT_EQ("", result[1]);
+
+  result = fxcrt::Split(ByteString("a,"), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("", result[1]);
+
+  result = fxcrt::Split(ByteString(",b"), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("", result[0]);
+  EXPECT_EQ("b", result[1]);
+
+  result = fxcrt::Split(ByteString("a,b"), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("b", result[1]);
+
+  result = fxcrt::Split(ByteString("a,b,"), ',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("b", result[1]);
+  EXPECT_EQ("", result[2]);
+
+  result = fxcrt::Split(ByteString("a,,"), ',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("", result[1]);
+  EXPECT_EQ("", result[2]);
+
+  result = fxcrt::Split(ByteString(",,a"), ',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ("", result[0]);
+  EXPECT_EQ("", result[1]);
+  EXPECT_EQ("a", result[2]);
+}
+
+TEST(fxstring, SplitByteStringView) {
+  std::vector<ByteStringView> result;
+  result = fxcrt::Split(ByteStringView(""), ',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ("", result[0]);
+
+  result = fxcrt::Split(ByteStringView("a"), ',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ("a", result[0]);
+
+  result = fxcrt::Split(ByteStringView(","), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("", result[0]);
+  EXPECT_EQ("", result[1]);
+
+  result = fxcrt::Split(ByteStringView("a,"), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("", result[1]);
+
+  result = fxcrt::Split(ByteStringView(",b"), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("", result[0]);
+  EXPECT_EQ("b", result[1]);
+
+  result = fxcrt::Split(ByteStringView("a,b"), ',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("b", result[1]);
+
+  result = fxcrt::Split(ByteStringView("a,b,"), ',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("b", result[1]);
+  EXPECT_EQ("", result[2]);
+
+  result = fxcrt::Split(ByteStringView("a,,"), ',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ("a", result[0]);
+  EXPECT_EQ("", result[1]);
+  EXPECT_EQ("", result[2]);
+
+  result = fxcrt::Split(ByteStringView(",,a"), ',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ("", result[0]);
+  EXPECT_EQ("", result[1]);
+  EXPECT_EQ("a", result[2]);
+}
+
+TEST(fxstring, SplitWideString) {
+  std::vector<WideString> result;
+  result = fxcrt::Split(WideString(L""), L',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ(L"", result[0]);
+
+  result = fxcrt::Split(WideString(L"a"), L',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+
+  result = fxcrt::Split(WideString(L","), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"", result[0]);
+  EXPECT_EQ(L"", result[1]);
+
+  result = fxcrt::Split(WideString(L"a,"), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"", result[1]);
+
+  result = fxcrt::Split(WideString(L",b"), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"", result[0]);
+  EXPECT_EQ(L"b", result[1]);
+
+  result = fxcrt::Split(WideString(L"a,b"), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"b", result[1]);
+
+  result = fxcrt::Split(WideString(L"a,b,"), L',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"b", result[1]);
+  EXPECT_EQ(L"", result[2]);
+
+  result = fxcrt::Split(WideString(L"a,,"), L',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"", result[1]);
+  EXPECT_EQ(L"", result[2]);
+
+  result = fxcrt::Split(WideString(L",,a"), L',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ(L"", result[0]);
+  EXPECT_EQ(L"", result[1]);
+  EXPECT_EQ(L"a", result[2]);
+}
+
+TEST(fxstring, SplitWideStringView) {
+  std::vector<WideStringView> result;
+  result = fxcrt::Split(WideStringView(L""), L',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ(L"", result[0]);
+
+  result = fxcrt::Split(WideStringView(L"a"), L',');
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+
+  result = fxcrt::Split(WideStringView(L","), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"", result[0]);
+  EXPECT_EQ(L"", result[1]);
+
+  result = fxcrt::Split(WideStringView(L"a,"), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"", result[1]);
+
+  result = fxcrt::Split(WideStringView(L",b"), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"", result[0]);
+  EXPECT_EQ(L"b", result[1]);
+
+  result = fxcrt::Split(WideStringView(L"a,b"), L',');
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"b", result[1]);
+
+  result = fxcrt::Split(WideStringView(L"a,b,"), L',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"b", result[1]);
+  EXPECT_EQ(L"", result[2]);
+
+  result = fxcrt::Split(WideStringView(L"a,,"), L',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ(L"a", result[0]);
+  EXPECT_EQ(L"", result[1]);
+  EXPECT_EQ(L"", result[2]);
+
+  result = fxcrt::Split(WideStringView(L",,a"), L',');
+  ASSERT_EQ(3u, result.size());
+  EXPECT_EQ(L"", result[0]);
+  EXPECT_EQ(L"", result[1]);
+  EXPECT_EQ(L"a", result[2]);
+}
+
+TEST(fxstring, ByteStringSplitEfficiency) {
+  std::vector<char> commas(50000, ',');
+  ByteString input(commas.data(), commas.size());
+  std::vector<ByteString> result;
+  result = fxcrt::Split(input, ',');
+  ASSERT_EQ(commas.size() + 1, result.size());
+  EXPECT_EQ("", result.front());
+  EXPECT_EQ("", result.back());
+}
+
+TEST(fxstring, ByteStringViewSplitEfficiency) {
+  std::vector<char> commas(50000, ',');
+  ByteStringView input(commas.data(), commas.size());
+  std::vector<ByteStringView> result;
+  result = fxcrt::Split(input, ',');
+  ASSERT_EQ(commas.size() + 1, result.size());
+  EXPECT_EQ("", result.front());
+  EXPECT_EQ("", result.back());
+}
+
+TEST(fxstring, WideStringSplitEfficiency) {
+  std::vector<wchar_t> commas(50000, L',');
+  WideString input(commas.data(), commas.size());
+  std::vector<WideString> result;
+  result = fxcrt::Split(input, ',');
+  ASSERT_EQ(commas.size() + 1, result.size());
+  EXPECT_EQ(L"", result.front());
+  EXPECT_EQ(L"", result.back());
+}
+
+TEST(fxstring, WideStringViewSplitEfficiency) {
+  std::vector<wchar_t> commas(50000, L',');
+  WideStringView input(commas.data(), commas.size());
+  std::vector<WideStringView> result;
+  result = fxcrt::Split(input, ',');
+  ASSERT_EQ(commas.size() + 1, result.size());
+  EXPECT_EQ(L"", result.front());
+  EXPECT_EQ(L"", result.back());
 }
diff --git a/core/fxcrt/fx_system.cpp b/core/fxcrt/fx_system.cpp
index a5ceec5..7358b75 100644
--- a/core/fxcrt/fx_system.cpp
+++ b/core/fxcrt/fx_system.cpp
@@ -6,12 +6,18 @@
 
 #include "core/fxcrt/fx_system.h"
 
+#include <cmath>
 #include <limits>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
 
 namespace {
 
+#if !defined(OS_WIN)
+uint32_t g_last_error = 0;
+#endif
+
 template <typename IntType, typename CharType>
 IntType FXSYS_StrToInt(const CharType* str) {
   if (!str)
@@ -23,7 +29,7 @@
     str++;
 
   IntType num = 0;
-  while (*str && FXSYS_isDecimalDigit(*str)) {
+  while (*str && FXSYS_IsDecimalDigit(*str)) {
     IntType val = FXSYS_DecimalCharToInt(*str);
     if (num > (std::numeric_limits<IntType>::max() - val) / 10) {
       if (neg && std::numeric_limits<IntType>::is_signed) {
@@ -82,10 +88,22 @@
 
 }  // namespace
 
-int FXSYS_round(float d) {
-  if (d < static_cast<float>(std::numeric_limits<int>::min()))
+int FXSYS_roundf(float f) {
+  if (std::isnan(f))
+    return 0;
+  if (f < static_cast<float>(std::numeric_limits<int>::min()))
     return std::numeric_limits<int>::min();
-  if (d > static_cast<float>(std::numeric_limits<int>::max()))
+  if (f >= static_cast<float>(std::numeric_limits<int>::max()))
+    return std::numeric_limits<int>::max();
+  return static_cast<int>(round(f));
+}
+
+int FXSYS_round(double d) {
+  if (std::isnan(d))
+    return 0;
+  if (d < static_cast<double>(std::numeric_limits<int>::min()))
+    return std::numeric_limits<int>::min();
+  if (d >= static_cast<double>(std::numeric_limits<int>::max()))
     return std::numeric_limits<int>::max();
   return static_cast<int>(round(d));
 }
@@ -106,7 +124,27 @@
   return FXSYS_IntToStr<int64_t, uint64_t, char*>(value, str, radix);
 }
 
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
+
+size_t FXSYS_wcsftime(wchar_t* strDest,
+                      size_t maxsize,
+                      const wchar_t* format,
+                      const struct tm* timeptr) {
+  // Avoid tripping an invalid parameter handler and crashing process.
+  // Note: leap seconds may cause tm_sec == 60.
+  if (timeptr->tm_year < -1900 || timeptr->tm_year > 8099 ||
+      timeptr->tm_mon < 0 || timeptr->tm_mon > 11 || timeptr->tm_mday < 1 ||
+      timeptr->tm_mday > 31 || timeptr->tm_hour < 0 || timeptr->tm_hour > 23 ||
+      timeptr->tm_min < 0 || timeptr->tm_min > 59 || timeptr->tm_sec < 0 ||
+      timeptr->tm_sec > 60 || timeptr->tm_wday < 0 || timeptr->tm_wday > 6 ||
+      timeptr->tm_yday < 0 || timeptr->tm_yday > 365) {
+    strDest[0] = L'\0';
+    return 0;
+  }
+  return wcsftime(strDest, maxsize, format, timeptr);
+}
+
+#else   // defined(OS_WIN)
 
 int FXSYS_GetACP() {
   return 0;
@@ -118,7 +156,7 @@
   }
   char* s = str;
   while (*str) {
-    *str = FXSYS_tolower(*str);
+    *str = tolower(*str);
     str++;
   }
   return s;
@@ -129,7 +167,7 @@
   }
   char* s = str;
   while (*str) {
-    *str = FXSYS_toupper(*str);
+    *str = toupper(*str);
     str++;
   }
   return s;
@@ -140,7 +178,7 @@
   }
   wchar_t* s = str;
   while (*str) {
-    *str = FXSYS_tolower(*str);
+    *str = FXSYS_towlower(*str);
     str++;
   }
   return s;
@@ -151,32 +189,32 @@
   }
   wchar_t* s = str;
   while (*str) {
-    *str = FXSYS_toupper(*str);
+    *str = FXSYS_towupper(*str);
     str++;
   }
   return s;
 }
 
-int FXSYS_stricmp(const char* dst, const char* src) {
+int FXSYS_stricmp(const char* str1, const char* str2) {
   int f;
   int l;
   do {
-    f = FXSYS_toupper(*dst);
-    l = FXSYS_toupper(*src);
-    ++dst;
-    ++src;
+    f = toupper(*str1);
+    l = toupper(*str2);
+    ++str1;
+    ++str2;
   } while (f && f == l);
   return f - l;
 }
 
-int FXSYS_wcsicmp(const wchar_t* dst, const wchar_t* src) {
+int FXSYS_wcsicmp(const wchar_t* str1, const wchar_t* str2) {
   wchar_t f;
   wchar_t l;
   do {
-    f = FXSYS_toupper(*dst);
-    l = FXSYS_toupper(*src);
-    ++dst;
-    ++src;
+    f = FXSYS_towupper(*str1);
+    l = FXSYS_towupper(*str2);
+    ++str1;
+    ++str2;
   } while (f && f == l);
   return f - l;
 }
@@ -203,6 +241,7 @@
   }
   return len;
 }
+
 int FXSYS_MultiByteToWideChar(uint32_t codepage,
                               uint32_t dwFlags,
                               const char* bstr,
@@ -211,32 +250,18 @@
                               int buflen) {
   int wlen = 0;
   for (int i = 0; i < blen; i++) {
-    if (buf && wlen < buflen) {
-      buf[wlen] = bstr[i];
-    }
+    if (buf && wlen < buflen)
+      buf[wlen] = reinterpret_cast<const uint8_t*>(bstr)[i];
     wlen++;
   }
   return wlen;
 }
 
-#else  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
-
-size_t FXSYS_wcsftime(wchar_t* strDest,
-                      size_t maxsize,
-                      const wchar_t* format,
-                      const struct tm* timeptr) {
-  // Avoid tripping an invalid parameter handler and crashing process.
-  // Note: leap seconds may cause tm_sec == 60.
-  if (timeptr->tm_year < -1900 || timeptr->tm_year > 8099 ||
-      timeptr->tm_mon < 0 || timeptr->tm_mon > 11 || timeptr->tm_mday < 1 ||
-      timeptr->tm_mday > 31 || timeptr->tm_hour < 0 || timeptr->tm_hour > 23 ||
-      timeptr->tm_min < 0 || timeptr->tm_min > 59 || timeptr->tm_sec < 0 ||
-      timeptr->tm_sec > 60 || timeptr->tm_wday < 0 || timeptr->tm_wday > 6 ||
-      timeptr->tm_yday < 0 || timeptr->tm_yday > 365) {
-    strDest[0] = L'\0';
-    return 0;
-  }
-  return wcsftime(strDest, maxsize, format, timeptr);
+void FXSYS_SetLastError(uint32_t err) {
+  g_last_error = err;
 }
 
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+uint32_t FXSYS_GetLastError() {
+  return g_last_error;
+}
+#endif  // defined(OS_WIN)
diff --git a/core/fxcrt/fx_system.h b/core/fxcrt/fx_system.h
index 8ad23a1..9963b90 100644
--- a/core/fxcrt/fx_system.h
+++ b/core/fxcrt/fx_system.h
@@ -17,51 +17,28 @@
 #include <string.h>
 #include <wchar.h>
 
-// _FX_OS_ values:
-#define _FX_OS_WIN32_ 1
-#define _FX_OS_WIN64_ 2
-#define _FX_OS_LINUX_ 4
-#define _FX_OS_MACOSX_ 7
-#define _FX_OS_ANDROID_ 12
-#define _FX_OS_WASM_ 13
-
 // _FX_PLATFORM_ values;
-#define _FX_PLATFORM_WINDOWS_ 1  // _FX_OS_WIN32_ or _FX_OS_WIN64_.
-#define _FX_PLATFORM_LINUX_ 2    // _FX_OS_LINUX_ or _FX_OS_WASM_.
-#define _FX_PLATFORM_APPLE_ 3    // _FX_OS_MACOSX_ always.
-#define _FX_PLATFORM_ANDROID_ 4  // _FX_OS_ANDROID_ always.
+#define _FX_PLATFORM_WINDOWS_ 1
+#define _FX_PLATFORM_LINUX_ 2
+#define _FX_PLATFORM_APPLE_ 3
 
-#ifndef _FX_OS_
-#if defined(__ANDROID__)
-#define _FX_OS_ _FX_OS_ANDROID_
-#define _FX_PLATFORM_ _FX_PLATFORM_ANDROID_
-#elif defined(_WIN32)
-#define _FX_OS_ _FX_OS_WIN32_
+#if defined(_WIN32)
 #define _FX_PLATFORM_ _FX_PLATFORM_WINDOWS_
 #elif defined(_WIN64)
-#define _FX_OS_ _FX_OS_WIN64_
 #define _FX_PLATFORM_ _FX_PLATFORM_WINDOWS_
 #elif defined(__linux__)
-#define _FX_OS_ _FX_OS_LINUX_
 #define _FX_PLATFORM_ _FX_PLATFORM_LINUX_
 #elif defined(__APPLE__)
-#define _FX_OS_ _FX_OS_MACOSX_
 #define _FX_PLATFORM_ _FX_PLATFORM_APPLE_
 #elif defined(__asmjs__) || defined(__wasm__)
-#define _FX_OS_ _FX_OS_WASM_
 #define _FX_PLATFORM_ _FX_PLATFORM_LINUX_
 #endif
-#endif  // _FX_OS_
-
-#if !defined(_FX_OS_) || _FX_OS_ == 0
-#error Sorry, can not figure out target OS. Please specify _FX_OS_ macro.
-#endif
 
 #if defined(_MSC_VER) && _MSC_VER < 1900
 #error Sorry, VC++ 2015 or later is required to compile PDFium.
 #endif  // defined(_MSC_VER) && _MSC_VER < 1900
 
-#if _FX_OS_ == _FX_OS_WASM_ && defined(PDF_ENABLE_V8)
+#if defined(__wasm__) && defined(PDF_ENABLE_V8)
 #error Cannot compile v8 with wasm.
 #endif  // PDF_ENABLE_V8
 
@@ -102,12 +79,6 @@
 #endif  // NDEBUG
 #endif  // ASSERT
 
-#if defined(__clang__) || defined(__GNUC__)
-#define PDFIUM_IMMEDIATE_CRASH() __builtin_trap()
-#else
-#define PDFIUM_IMMEDIATE_CRASH() ((void)(*(volatile char*)0 = 0))
-#endif  // defined(__clang__) || defined(__GNUC__)
-
 // M_PI not universally present on all platforms.
 #define FX_PI 3.1415926535897932384626433832795f
 #define FX_BEZIER 0.5522847498308f
@@ -155,32 +126,22 @@
 #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
 #define FXSYS_GetACP GetACP
 #define FXSYS_itoa _itoa
+#define FXSYS_WideCharToMultiByte WideCharToMultiByte
+#define FXSYS_MultiByteToWideChar MultiByteToWideChar
 #define FXSYS_strlwr _strlwr
 #define FXSYS_strupr _strupr
 #define FXSYS_stricmp _stricmp
+#define FXSYS_wcsicmp _wcsicmp
+#define FXSYS_wcslwr _wcslwr
+#define FXSYS_wcsupr _wcsupr
 #define FXSYS_pow(a, b) (float)powf(a, b)
 size_t FXSYS_wcsftime(wchar_t* strDest,
                       size_t maxsize,
                       const wchar_t* format,
                       const struct tm* timeptr);
-#ifdef _NATIVE_WCHAR_T_DEFINED
-#define FXSYS_wcsicmp(str1, str2) _wcsicmp((wchar_t*)(str1), (wchar_t*)(str2))
-#define FXSYS_WideCharToMultiByte(p1, p2, p3, p4, p5, p6, p7, p8) \
-  WideCharToMultiByte(p1, p2, (const wchar_t*)(p3), p4, p5, p6, p7, p8)
-#define FXSYS_MultiByteToWideChar(p1, p2, p3, p4, p5, p6) \
-  MultiByteToWideChar(p1, p2, p3, p4, (wchar_t*)(p5), p6)
-#define FXSYS_wcslwr(str) _wcslwr((wchar_t*)(str))
-#define FXSYS_wcsupr(str) _wcsupr((wchar_t*)(str))
-#else  // _NATIVE_WCHAR_T_DEFINED
-#define FXSYS_wcsicmp _wcsicmp
-#define FXSYS_WideCharToMultiByte WideCharToMultiByte
-#define FXSYS_MultiByteToWideChar MultiByteToWideChar
-#define FXSYS_wcslwr _wcslwr
-#define FXSYS_wcsupr _wcsupr
-#endif  // _NATIVE_WCHAR_T_DEFINED
-
+#define FXSYS_SetLastError SetLastError
+#define FXSYS_GetLastError GetLastError
 #else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
 int FXSYS_GetACP();
 char* FXSYS_itoa(int value, char* str, int radix);
 int FXSYS_WideCharToMultiByte(uint32_t codepage,
@@ -199,12 +160,14 @@
                               int buflen);
 char* FXSYS_strlwr(char* str);
 char* FXSYS_strupr(char* str);
-int FXSYS_stricmp(const char*, const char*);
+int FXSYS_stricmp(const char* str1, const char* str2);
 int FXSYS_wcsicmp(const wchar_t* str1, const wchar_t* str2);
 wchar_t* FXSYS_wcslwr(wchar_t* str);
 wchar_t* FXSYS_wcsupr(wchar_t* str);
 #define FXSYS_pow(a, b) (float)pow(a, b)
 #define FXSYS_wcsftime wcsftime
+void FXSYS_SetLastError(uint32_t err);
+uint32_t FXSYS_GetLastError();
 #endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
 
 #define FXWORD_GET_LSBFIRST(p)                                \
@@ -224,7 +187,8 @@
 int32_t FXSYS_wtoi(const wchar_t* str);
 int64_t FXSYS_atoi64(const char* str);
 const char* FXSYS_i64toa(int64_t value, char* str, int radix);
-int FXSYS_round(float f);
+int FXSYS_roundf(float f);
+int FXSYS_round(double d);
 #define FXSYS_sqrt2(a, b) (float)sqrt((a) * (a) + (b) * (b))
 #ifdef __cplusplus
 }  // extern C
@@ -259,12 +223,4 @@
 
 #endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
 
-// Prevent a function from ever being inlined, typically because we'd
-// like it to appear in stack traces.
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#define NEVER_INLINE __declspec(noinline)
-#else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#define NEVER_INLINE __attribute__((__noinline__))
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
 #endif  // CORE_FXCRT_FX_SYSTEM_H_
diff --git a/core/fxcrt/fx_system_unittest.cpp b/core/fxcrt/fx_system_unittest.cpp
index 73f9c75..bc5dbba 100644
--- a/core/fxcrt/fx_system_unittest.cpp
+++ b/core/fxcrt/fx_system_unittest.cpp
@@ -4,6 +4,7 @@
 
 #include <limits>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -11,7 +12,7 @@
 // Unit test covering cases where PDFium replaces well-known library
 // functionality on any given platformn.
 
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
 
 namespace {
 
@@ -73,6 +74,107 @@
 
 }  // namespace
 
+TEST(fxcrt, FXSYS_roundf) {
+  EXPECT_EQ(0, FXSYS_roundf(0.0f));
+  EXPECT_EQ(0, FXSYS_roundf(-0.0f));
+  EXPECT_EQ(0, FXSYS_roundf(0.00001f));
+  EXPECT_EQ(0, FXSYS_roundf(-0.00001f));
+  EXPECT_EQ(3, FXSYS_roundf(3.14159f));
+  EXPECT_EQ(4, FXSYS_roundf(3.5f));
+
+  // Check for smallest non-zero float values.
+  EXPECT_EQ(0, FXSYS_roundf(std::numeric_limits<float>::min()));
+  EXPECT_EQ(0, FXSYS_roundf(-std::numeric_limits<float>::min()));
+
+  // Function is a wrapper around standard C library function round(), so
+  // returns the integral value that is nearest to x, with halfway cases
+  // rounded away from zero.
+  EXPECT_EQ(-3, FXSYS_roundf(-3.14159f));
+  EXPECT_EQ(-4, FXSYS_roundf(-3.5f));
+
+  // Positive rounding stops at maximum int.
+  // MAX_INT=0x7FFFFFFF=2147483647=2.147483647e+9
+  // In IEEE-754 format, 2^31 yields exponent of 0x9E with mantissa of all
+  // zeroes which is 0x4f000000=2.14748365e+9, which is beyond max integer.
+  // Going to next smallest float by minus one from exponent and mantissa of
+  // all ones yields binary float representation of 0x4EFFFFFF=2.14748352e+9,
+  // which is 2147483520.
+  EXPECT_EQ(2147483520, FXSYS_roundf(2.14748352e+9f));
+
+  // Using a slightly larger value, expect to see it be capped at MAX_INT.
+  EXPECT_EQ(2147483647, FXSYS_roundf(2.14748365e+9f));
+
+  EXPECT_EQ(2147483647, FXSYS_roundf(2.14748365e+10f));
+  EXPECT_EQ(2147483647, FXSYS_roundf(std::numeric_limits<float>::max()));
+
+  // Negative rounding stops at minimum int.
+  // MIN_INT=0x80000000=-2147483648,=-2.147483648e+9
+  // In IEEE-754 format, 2^31 yields exponent of 0x9E with mantissa of all
+  // zeroes which is 0x4f000000=2.14748365e+9, and the sign bit set, which
+  // is 0xCF000000 and exactly matches the minimum integer.  Going to next
+  // smallest negative float by minus one from exponent and mantissa of all
+  // ones yields binary float representation of 0xCEFFFFFF=-2.14748352e+9,
+  // which is -2147483520.
+  EXPECT_EQ(-2147483648, FXSYS_roundf(-2.147483648e+10f));
+  EXPECT_EQ(-2147483648, FXSYS_roundf(-2.147483648e+9f));
+  EXPECT_EQ(-2147483520, FXSYS_roundf(-2.14748352e+9f));
+  EXPECT_EQ(-2147483648, FXSYS_roundf(-std::numeric_limits<float>::max()));
+
+  // NaN should give zero.
+  EXPECT_EQ(0, FXSYS_roundf(NAN));
+}
+
+TEST(fxcrt, FXSYS_round) {
+  EXPECT_EQ(0, FXSYS_round(0.0));
+  EXPECT_EQ(0, FXSYS_round(-0.0));
+  EXPECT_EQ(0, FXSYS_round(0.00001));
+  EXPECT_EQ(0, FXSYS_round(-0.00001));
+  EXPECT_EQ(3, FXSYS_round(3.14159));
+  EXPECT_EQ(4, FXSYS_round(3.5));
+
+  // Check for smallest non-zero double values.
+  EXPECT_EQ(0, FXSYS_round(std::numeric_limits<double>::min()));
+  EXPECT_EQ(0, FXSYS_round(-std::numeric_limits<double>::min()));
+
+  // Function is a wrapper around standard C library function round(), so
+  // returns the integral value that is nearest to x, with halfway cases
+  // rounded away from zero.
+  EXPECT_EQ(-3, FXSYS_round(-3.14159));
+  EXPECT_EQ(-4, FXSYS_round(-3.5));
+
+  // Positive rounding stops at maximum int.
+  // MAX_INT=0x7FFFFFFF=2147483647=2.147483647e+9
+  // In IEEE-754 double precision format, 2^31 yields exponent of 0x41E with
+  // mantissa of all zeroes which is 0x41E0000000000000=2.14748365e+9, which
+  // is beyond max integer.
+  // Going to next smallest float by minus one from exponent and mantissa of
+  // all ones yields binary float representation of
+  // 41DFFFFFFFC00000=2.147483647e+9, which matches the max integer.
+  EXPECT_EQ(2147483647, FXSYS_round(2.147483647e+9));
+
+  // Using a slightly larger value, expect to see it be capped at MAX_INT.
+  EXPECT_EQ(2147483647, FXSYS_round(2.14748365e+9));
+
+  EXPECT_EQ(2147483647, FXSYS_round(2.14748365e+10));
+  EXPECT_EQ(2147483647, FXSYS_round(std::numeric_limits<double>::max()));
+
+  // Negative rounding stops at minimum int.
+  // MIN_INT=0x80000000=-2147483648,=-2.147483648e+9
+  // In IEEE-754 double precision format, 2^31 yields exponent of 0x41E with
+  // mantissa of all zeroes which is 0x41E0000000000000=2.14748365e+9, and the
+  // sign bit set, which is 0xC1E0000000000000 and exactly matches the minimum
+  // integer.  Going to next smallest negative double by minus one from
+  // exponent and mantissa of all ones yields binary float representation of
+  // 0xC1DFFFFFFFFFFFFF=-2.1474836479999998e+9, which is -2147483648.
+  EXPECT_EQ(-2147483648, FXSYS_round(-2.1474836479999998e+9));
+  EXPECT_EQ(-2147483648, FXSYS_round(-2.147483648e+9));
+  EXPECT_EQ(-2147483648, FXSYS_round(-2.147483648e+10));
+  EXPECT_EQ(-2147483648, FXSYS_round(-std::numeric_limits<double>::max()));
+
+  // NaN should give zero.
+  EXPECT_EQ(0, FXSYS_round(NAN));
+}
+
 TEST(fxcrt, FXSYS_itoa_InvalidRadix) {
   char buf[32];
 
@@ -158,7 +260,7 @@
       "111111111111111111111111111111111111111111111111111111111111111");
 }
 
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#endif  // !defined(OS_WIN)
 
 TEST(fxcrt, FXSYS_wcsftime) {
   struct tm good_time = {};
diff --git a/core/fxcrt/fx_ucddata.cpp b/core/fxcrt/fx_ucddata.cpp
deleted file mode 100644
index 527eacf..0000000
--- a/core/fxcrt/fx_ucddata.cpp
+++ /dev/null
@@ -1,11001 +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/fxcrt/fx_ucddata.h"
-
-#include "core/fxcrt/fx_memory.h"
-
-const uint32_t kTextLayoutCodeProperties[] = {
-    0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93,
-    0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe8ae5, 0xfffe9b5c, 0xfffe9ada,
-    0xfffe9b1a, 0xfffe9b5b, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93,
-    0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93,
-    0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9b53, 0xfffe9b53,
-    0xfffe9b53, 0xfffe9ad3, 0xfffe9323, 0xfffeb005, 0xfffeb002, 0xfffeb24b,
-    0xfffeb248, 0xfffeb249, 0xfffeb00b, 0xfffeb002, 0x007eb000, 0x00feb001,
-    0xfffeb00b, 0xfffeb208, 0xfffeb1c7, 0xfffeb20e, 0xfffeb1c7, 0xfffeb1c6,
-    0xfffea90a, 0xfffea90a, 0xfffea90a, 0xfffea90a, 0xfffea90a, 0xfffea90a,
-    0xfffea90a, 0xfffea90a, 0xfffea90a, 0xfffea90a, 0xfffeb1c7, 0xfffeb007,
-    0x017eb000, 0xfffeb00b, 0x01feb001, 0xfffeb005, 0xfffeb00b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0x027eb000, 0xfffeb008, 0x02feb001, 0xfffeb00b, 0xff80b00b,
-    0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0x037eb000, 0xfffeb00f, 0x03feb001,
-    0xfffeb00b, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293,
-    0xfffeb293, 0xfffeb35d, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293,
-    0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293,
-    0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293,
-    0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293,
-    0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffeb293, 0xfffe91c3, 0xfffeb000,
-    0xfffeb249, 0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb00b, 0xfffeb022,
-    0xfffeb022, 0xfffeb00b, 0xfffeb062, 0x047eb000, 0xfffeb00b, 0xfffeb28f,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb249, 0xfffeb248, 0xfffeb122, 0xfffeb122,
-    0xfffeb010, 0xfffeb04b, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb122,
-    0xfffeb062, 0x04feb001, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb000,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb022,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb022, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb010, 0xfffeb022,
-    0xfffeb022, 0xfffeb022, 0xfffeb010, 0xfffeb022, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb062, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022,
-    0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb010, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea183, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea183, 0xfffea183, 0xfffea183, 0xfffea183,
-    0xfffea183, 0xfffea183, 0xfffea183, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb007, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb193, 0xfffeb193, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb047, 0xfffeb00f, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe80a4, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffeb08f, 0xfffea193, 0xfffeb08b, 0xfffea193, 0xfffea193, 0xfffeb08b,
-    0xfffea193, 0xfffea193, 0xfffeb085, 0xfffea193, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffed0cb, 0xfffed0cb, 0xfffed0cb, 0xfffed0cb, 0xfffed164, 0xfffed164,
-    0xfffed00b, 0xfffed00b, 0xfffed14b, 0xfffed249, 0xfffed249, 0xfffed149,
-    0xfffed1c7, 0xfffed147, 0xfffed00b, 0xfffed00b, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffed145, 0xfffed164, 0xfffed164,
-    0xfffed145, 0xfffed145, 0xfffed164, 0xfffed14b, 0xfffeb94b, 0xfffeb94b,
-    0xfffec14b, 0xfffeb94b, 0xfffec94b, 0xfffeb94b, 0xfffec94b, 0xfffec14b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec14b,
-    0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffed14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec14b, 0xfffec94b, 0xfffec94b, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffed164,
-    0xfffed0ca, 0xfffed0ca, 0xfffed0ca, 0xfffed0ca, 0xfffed0ca, 0xfffed0ca,
-    0xfffed0ca, 0xfffed0ca, 0xfffed0ca, 0xfffed0ca, 0xfffed249, 0xfffed0ca,
-    0xfffed0ca, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffea193, 0xfffec14b,
-    0xfffec14b, 0xfffec14b, 0xfffea14b, 0xfffec14b, 0xfffec14b, 0xfffec14b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec14b, 0xfffec14b,
-    0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b,
-    0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b,
-    0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffed14b, 0xfffed14b,
-    0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b, 0xfffec94b,
-    0xfffec14b, 0xfffec94b, 0xfffec94b, 0xfffec14b, 0xfffec14b, 0xfffec14b,
-    0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b, 0xfffec14b,
-    0xfffec94b, 0xfffec14b, 0xfffec94b, 0xfffec14b, 0xfffec94b, 0xfffec94b,
-    0xfffec14b, 0xfffec14b, 0xfffec945, 0xfffec14b, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffed0cb,
-    0xfffed193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffed14b, 0xfffed14b, 0xfffea193, 0xfffea193, 0xfffed00b,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffec14b, 0xfffec14b,
-    0xfffed10a, 0xfffed10a, 0xfffed10a, 0xfffed10a, 0xfffed10a, 0xfffed10a,
-    0xfffed10a, 0xfffed10a, 0xfffed10a, 0xfffed10a, 0xfffed14b, 0xfffed14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffe8164, 0xfffeb28b, 0xfffeb14b, 0xfffea193, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffe8164, 0xfffe8164, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffec14b, 0xfffec14b, 0xfffec14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffec14b, 0xfffec14b, 0xfffed14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffec14b, 0xfffed14b, 0xfffec14b,
-    0xfffec14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffec14b, 0xfffec14b,
-    0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b, 0xfffed14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b, 0xfffeb14b,
-    0xfffeb14b, 0xfffeb14b, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb14b, 0xfffe8164, 0xfffe8164, 0xfffe8164, 0xfffe8164,
-    0xfffe8164, 0xfffe8164, 0xfffe8164, 0xfffe8164, 0xfffe8164, 0xfffe8164,
-    0xfffe8164, 0xfffe8164, 0xfffe8164, 0xfffe8164, 0xfffeb08a, 0xfffeb08a,
-    0xfffeb08a, 0xfffeb08a, 0xfffeb08a, 0xfffeb08a, 0xfffeb08a, 0xfffeb08a,
-    0xfffeb08a, 0xfffeb08a, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb08b, 0xfffeb08b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb007, 0xfffeb005, 0xfffeb08b, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb08b, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb08b, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffeb08b, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffe80a4, 0xfffe80a4, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4, 0xfffe80a4,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffeb04b, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193,
-    0xfffeb053, 0xfffe8024, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffeb04f, 0xfffeb04f,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffea193, 0xfffeb053, 0xfffeb053,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffeb04b, 0xfffeb053, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffe8024, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb053,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193,
-    0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb249, 0xfffeb249, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb049, 0xfffeb04b, 0xfffeb248,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffea193,
-    0xfffea193, 0xfffeb053, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffe8024,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffea193, 0xfffea193, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffeb04b, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffea193,
-    0xfffea193, 0xfffeb053, 0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffea193,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffeb248,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffeb053, 0xfffeb053,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffeb04b, 0xfffeb053, 0xfffea193,
-    0xfffeb053, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffe8024, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffeb053,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193,
-    0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffe8024,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb053, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb248,
-    0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb053, 0xfffeb053,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffeb04b, 0xfffeb053, 0xfffea053,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffe8024,
-    0xfffea053, 0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffeb053, 0xfffeb053,
-    0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193,
-    0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffe8024, 0xfffeb00b, 0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffe8024,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb053, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb049,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffe8024, 0xfffea193, 0xfffe8024, 0xfffeb053, 0xfffeb053,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb053, 0xfffeb053, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb248,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffeb04b, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04f, 0xfffeb04f, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb05e,
-    0xfffeb05e, 0xfffe8024, 0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffeb05e,
-    0xfffeb05e, 0xfffe8024, 0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffeb05e,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffe8024, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffe8024, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffe8024, 0xfffeb05e,
-    0xfffe8024, 0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffeb05e, 0xfffeb05e,
-    0xfffe8024, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e,
-    0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffe8024, 0xfffea19e, 0xfffea19e, 0xfffeb05e,
-    0xfffe8024, 0xfffe8024, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffe8024, 0xfffeb05e, 0xfffe8024, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffe8024, 0xfffe8024,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024,
-    0xfffeb05e, 0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb050, 0xfffeb050, 0xfffeb050, 0xfffeb050, 0xfffeb04b,
-    0xfffeb050, 0xfffeb050, 0xfffeb043, 0xfffeb050, 0xfffeb050, 0xfffeb04f,
-    0xfffeb043, 0xfffeb045, 0xfffeb045, 0xfffeb045, 0xfffeb045, 0xfffeb045,
-    0xfffeb043, 0xfffeb04b, 0xfffeb045, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffea193, 0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04f, 0xfffea193,
-    0xfffeb04b, 0xfffea193, 0xfffeb04b, 0xfffea193, 0x057eb000, 0x05feb001,
-    0x067eb000, 0x06feb001, 0xfffeb053, 0xfffeb053, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb04f, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb04f, 0xfffea193, 0xfffea193, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffeb04f, 0xfffeb04f,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb050, 0xfffeb050,
-    0xfffeb04f, 0xfffeb050, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffeb05e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e,
-    0xfffea19e, 0xfffeb05e, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04f, 0xfffeb04f, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e,
-    0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057, 0xfffe3057,
-    0xfffe3057, 0xfffe3057, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffeb04b, 0xfffeb04f,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb00f, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb30f, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0x077eb000, 0x07feb001, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04f,
-    0xfffeb04f, 0xfffeb04f, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb04f, 0xfffeb04f, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffeb04f, 0xfffeb04f,
-    0xfffeb044, 0xfffeb05e, 0xfffeb04f, 0xfffeb04b, 0xfffeb04f, 0xfffeb248,
-    0xfffeb05e, 0xfffea19e, 0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb005, 0xfffeb005, 0xfffeb00f, 0xfffeb00f,
-    0xfffeb010, 0xfffeb00b, 0xfffeb005, 0xfffeb005, 0xfffeb00b, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb303, 0xfffe8024, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffea193, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb053, 0xfffeb053,
-    0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb005, 0xfffeb005, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffe8024, 0xfffe8024, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb01e, 0xfffeb01e,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffeb05e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffe8024, 0xfffea19e, 0xfffeb05e, 0xfffea19e, 0xfffeb05e,
-    0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffea19e,
-    0xfffea19e, 0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffeb053,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053,
-    0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04f, 0xfffeb04f, 0xfffeb04b, 0xfffeb04f, 0xfffeb04f, 0xfffeb04f,
-    0xfffeb04f, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb053,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb053,
-    0xfffea193, 0xfffea193, 0xfffeb053, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb053, 0xfffeb053,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffea193,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04f, 0xfffeb04f, 0xfffeb04f,
-    0xfffeb04f, 0xfffeb04f, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04f, 0xfffeb04f,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb04b,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb053, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb053, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb00b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb010,
-    0xfffeb00b, 0xfffe8024, 0xfffeb30f, 0xfffeb30f, 0xfffeb30f, 0xfffeb30f,
-    0xfffeb30f, 0xfffeb30f, 0xfffeb30f, 0xfffeb303, 0xfffeb30f, 0xfffeb30f,
-    0xfffeb30f, 0xfffeb292, 0xfffe9a93, 0xfffe9a93, 0xfffe9853, 0xfffe9893,
-    0xfffeb00f, 0xfffeb003, 0xfffeb00f, 0xff82b00f, 0xff84b011, 0xfffeb022,
-    0xfffeb022, 0xfffeb00b, 0x0806b000, 0x0888b002, 0xfffeb000, 0xfffeb002,
-    0x090ab000, 0x098cb001, 0xfffeb000, 0xfffeb002, 0xfffeb022, 0xfffeb022,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00d, 0xfffeb00d, 0xfffeb00d, 0xfffeb00f,
-    0xfffe9b1a, 0xfffe9b5a, 0xfffe9c53, 0xfffe9bd3, 0xfffe9c93, 0xfffe9c13,
-    0xfffe9b93, 0xfffeb1c3, 0xfffeb249, 0xfffeb249, 0xfffeb249, 0xfffeb249,
-    0xfffeb249, 0xfffeb009, 0xfffeb009, 0xfffeb009, 0xfffeb00b, 0x0a7eb000,
-    0x0afeb001, 0xfffeb022, 0xfffeb004, 0xfffeb004, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb1c7, 0x0b7eb000,
-    0x0bfeb001, 0xfffeb004, 0xfffeb004, 0xfffeb004, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00f, 0xfffeb00b,
-    0xfffeb00f, 0xfffeb00f, 0xfffeb00f, 0xfffeb00f, 0xfffeb00b, 0xfffeb00f,
-    0xfffeb00f, 0xfffeb30f, 0xfffeb294, 0xfffeb28b, 0xfffeb28b, 0xfffeb28b,
-    0xfffeb28b, 0xfffe82a4, 0xfffe82a4, 0xfffe82a4, 0xfffe82a4, 0xfffe82a4,
-    0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93, 0xfffe9a93,
-    0xfffeb10b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffeb122, 0xfffeb10b,
-    0xfffeb10b, 0xfffeb10b, 0xfffeb10b, 0xfffeb10b, 0xfffeb20b, 0xfffeb20b,
-    0xfffeb00b, 0x0c7eb000, 0x0cfeb001, 0xfffeb062, 0xfffeb10b, 0xfffeb122,
-    0xfffeb122, 0xfffeb122, 0xfffeb122, 0xfffeb10b, 0xfffeb10b, 0xfffeb10b,
-    0xfffeb10b, 0xfffeb10b, 0xfffeb20b, 0xfffeb20b, 0xfffeb00b, 0x0d7eb000,
-    0x0dfeb001, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248,
-    0xfffeb248, 0xfffeb249, 0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248,
-    0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248,
-    0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb248, 0xfffeb249, 0xfffeb248,
-    0xfffeb248, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb193, 0xfffeb193, 0xfffeb193, 0xfffeb193, 0xfffea193,
-    0xfffeb193, 0xfffeb193, 0xfffeb193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb009, 0xfffeb00b, 0xfffeb022,
-    0xfffeb00b, 0xfffeb04b, 0xfffeb00b, 0xfffeb009, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb062, 0xfffeb00b, 0xfffeb04b, 0xfffeb008, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb022, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb00b, 0xfffeb04b, 0xfffeb00b, 0xfffeb04b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb062, 0xfffeb04b, 0xfffeb04b, 0xfffeb24b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb022, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b,
-    0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062,
-    0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb062, 0xfffeb062,
-    0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062, 0xfffeb062,
-    0xfffeb062, 0xfffeb062, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb022,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022,
-    0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb022, 0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b,
-    0xfffeb022, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022,
-    0x0e7eb022, 0x0efeb00b, 0x0f7eb00b, 0x0ffeb022, 0x107eb00b, 0x10feb00b,
-    0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb022, 0xfffeb208, 0xfffeb248,
-    0xfffeb00b, 0x117eb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb022, 0xfffeb022,
-    0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb022,
-    0xfffeb00b, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022,
-    0xfffeb022, 0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb022, 0xfffeb022, 0xfffeb022,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0x11feb022, 0x127eb022,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0x12feb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0x137eb022, 0x13feb00b, 0x147eb00b, 0x14feb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb022,
-    0xfffeb00b, 0xfffeb00b, 0x157eb022, 0x15feb022, 0x167eb022, 0x16feb022,
-    0x177eb00b, 0x17feb00b, 0x187eb022, 0x18feb022, 0xfffeb00b, 0xfffeb00b,
-    0x197eb022, 0x19feb022, 0x1a7eb00b, 0x1afeb00b, 0x1b7eb00b, 0x1bfeb00b,
-    0x1c7eb00b, 0x1cfeb00b, 0x1d7eb00b, 0x1dfeb00b, 0x1e7eb00b, 0x1efeb00b,
-    0x1f7eb00b, 0x1ffeb00b, 0x207eb00b, 0x20feb00b, 0x217eb00b, 0x21feb00b,
-    0x227eb00b, 0x22feb00b, 0x237eb022, 0x23feb022, 0x247eb00b, 0x24feb00b,
-    0x257eb022, 0x25feb022, 0x267eb00b, 0x26feb00b, 0x277eb00b, 0x27feb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0x287eb00b, 0x28feb00b, 0x297eb00b,
-    0x29feb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b,
-    0x2a7eb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0x2afeb00b, 0x2b7eb00b,
-    0xfffeb00b, 0xfffeb022, 0x2bfeb00b, 0xfffeb00b, 0x2c7eb00b, 0x2cfeb00b,
-    0xfffeb00b, 0x2d7eb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0x2dfeb00b, 0x2e7eb00b, 0x2efeb00b, 0x2f7eb00b, 0x2ffeb00b, 0x307eb00b,
-    0x30feb00b, 0x317eb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0x31feb00b, 0x327eb00b, 0x32feb00b, 0x337eb00b, 0x33feb00b,
-    0xfffeb00b, 0xfffeb00b, 0x347eb00b, 0x34feb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0x357eb00b, 0x35feb00b, 0x367eb00b, 0x36feb00b,
-    0x377eb00b, 0x37feb00b, 0x387eb00b, 0x38feb00b, 0x397eb00b, 0x39feb00b,
-    0x3a7eb00b, 0x3afeb00b, 0x3b7eb00b, 0x3bfeb00b, 0x3c7eb00b, 0x3cfeb00b,
-    0x3d7eb00b, 0x3dfeb00b, 0x3e7eb00b, 0x3efeb00b, 0x3f7eb00b, 0x3ffeb00b,
-    0x407eb00b, 0x40feb00b, 0xfffeb00b, 0xfffeb00b, 0x417eb00b, 0x41feb00b,
-    0x427eb00b, 0x42feb00b, 0x437eb00b, 0xfffeb00b, 0x43feb00b, 0x447eb00b,
-    0xfffeb00b, 0xfffeb00b, 0x44feb00b, 0x457eb00b, 0x45feb00b, 0x467eb00b,
-    0x46feb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0x477eb00b, 0x47feb00b,
-    0x487eb00b, 0x48feb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb022, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0x497eb000, 0x49feb001, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3122, 0xfffe3122,
-    0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122,
-    0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122,
-    0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122, 0xfffe3122,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe3022,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe300b, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe300b, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe300b, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe3022,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe3022, 0xfffe3022,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe304b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe0024, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe0024, 0xfffe3022, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe0024, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024, 0xfffe0024,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024, 0xfffe300b,
-    0xfffe0024, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe300b, 0xfffe3022, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe3002, 0xfffe3002, 0xfffe3002, 0xfffe3002, 0xfffe0024,
-    0xfffe0024, 0xfffe300b, 0xfffe3005, 0xfffe3005, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0x4a7e3000, 0x4afe3001, 0x4b7e3000, 0x4bfe3001,
-    0x4c7e3000, 0x4cfe3001, 0x4d7e3000, 0x4dfe3001, 0x4e7e3000, 0x4efe3001,
-    0x4f7e3000, 0x4ffe3001, 0x507e3000, 0x50fe3001, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe300b, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe0024, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0x517e300b, 0x51fe300b, 0x527e3000,
-    0x52fe3001, 0xfffe300b, 0x537e300b, 0x53fe300b, 0xfffe300b, 0xfffe0024,
-    0xfffe300b, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0x547e300b, 0x54fe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x557e300b,
-    0x55fe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x567e300b, 0x56fe300b,
-    0x577e300b, 0x57fe300b, 0x587e3000, 0x58fe3001, 0x597e3000, 0x59fe3001,
-    0x5a7e3000, 0x5afe3001, 0x5b7e3000, 0x5bfe3001, 0x5c7e3000, 0x5cfe3001,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x5d7e3000, 0x5dfe3001, 0x5e7e3000, 0x5efe3001, 0x5f7e3000,
-    0x5ffe3001, 0x607e3000, 0x60fe3001, 0x617e3000, 0x61fe3001, 0x627e3000,
-    0x62fe3001, 0x637e3000, 0x63fe3001, 0x647e3000, 0x64fe3001, 0x657e3000,
-    0x65fe3001, 0x667e3000, 0x66fe3001, 0x677e3000, 0x67fe3001, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0x687e300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0x68fe300b, 0x697e300b, 0xfffe300b, 0xfffe300b,
-    0x69fe300b, 0x6a7e300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x6afe300b,
-    0x6b7e300b, 0x6bfe300b, 0x6c7e300b, 0xfffe300b, 0x6cfe300b, 0x6d7e300b,
-    0xfffe300b, 0xfffe300b, 0x6dfe3000, 0x6e7e3001, 0x6efe3000, 0x6f7e3001,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x6ffe300b, 0xfffe300b, 0xfffe300b, 0x707e300b, 0x70fe300b,
-    0xfffe300b, 0xfffe300b, 0x717e3000, 0x71fe3001, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x727e300b, 0x72fe300b, 0x737e300b, 0x73fe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x747e300b, 0x74fe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0x757e300b, 0x75fe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x767e300b, 0x76fe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x777e300b, 0x77fe300b, 0xfffe300b, 0xfffe300b, 0x787e300b,
-    0x78fe300b, 0x797e300b, 0x79fe300b, 0x7a7e300b, 0x7afe300b, 0x7b7e300b,
-    0x7bfe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x7c7e300b, 0x7cfe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x7d7e300b, 0x7dfe300b, 0x7e7e300b, 0x7efe300b, 0x7f7e300b,
-    0x7ffe300b, 0x807e300b, 0x80fe300b, 0x817e300b, 0x81fe300b, 0x827e300b,
-    0x82fe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x837e300b,
-    0x83fe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x847e300b, 0x84fe300b,
-    0x857e300b, 0x85fe300b, 0x867e300b, 0x86fe300b, 0x877e300b, 0x87fe300b,
-    0xfffe300b, 0x887e300b, 0x88fe300b, 0xfffe300b, 0xfffe300b, 0x897e300b,
-    0x89fe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x8a7e300b, 0x8afe300b, 0x8b7e300b, 0x8bfe300b, 0x8c7e300b,
-    0x8cfe300b, 0x8d7e300b, 0x8dfe300b, 0x8e7e300b, 0x8efe300b, 0x8f7e300b,
-    0x8ffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x907e300b, 0x90fe300b, 0x917e300b, 0x91fe300b, 0x927e300b,
-    0x92fe300b, 0x937e300b, 0x93fe300b, 0x947e300b, 0x94fe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0x957e300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0x95fe300b,
-    0x967e300b, 0x96fe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0x977e300b, 0x97fe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0x987e300b, 0x98fe300b, 0x997e300b, 0x99fe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe3022,
-    0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe3022, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb005,
-    0xfffeb00f, 0xfffeb00f, 0xfffeb00f, 0xfffeb00b, 0xfffeb005, 0xfffeb00f,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb001, 0xfffeb001,
-    0x9a7eb000, 0x9afeb001, 0x9b7eb000, 0x9bfeb001, 0xfffeb002, 0xfffeb002,
-    0xfffeb002, 0x9c7eb000, 0x9cfeb001, 0xfffeb002, 0x9d7eb000, 0x9dfeb001,
-    0xfffeb00f, 0xfffeb00f, 0xfffeb00f, 0xfffeb00f, 0xfffeb00f, 0xfffeb00f,
-    0xfffeb00f, 0xfffeb00f, 0xfffeb00b, 0xfffeb00f, 0xfffeb000, 0xfffeb00f,
-    0xfffeb00b, 0xfffeb00b, 0x9e7eb000, 0x9efeb001, 0xfffeb00b, 0xfffeb00b,
-    0x9f7eb000, 0x9ffeb001, 0xa07eb000, 0xa0feb001, 0xa17eb000, 0xa1feb001,
-    0xa27eb000, 0xa2feb001, 0xa37eb000, 0xa3feb001, 0xfffeb00f, 0xfffeb00f,
-    0xfffeb00f, 0xfffeb00f, 0xfffeb005, 0xfffeb00b, 0xfffeb00f, 0xfffeb00f,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe330c, 0xffff3001, 0xffff3001, 0xfffe300c, 0xfffe300c, 0xfffe3044,
-    0xfffe304c, 0xfffe304c, 0xa40eb000, 0xa490b001, 0xa512b000, 0xa594b001,
-    0xa616b000, 0xa698b001, 0xa71ab000, 0xa79cb001, 0xa81eb000, 0xa8a0b001,
-    0xfffe300c, 0xfffe300c, 0xa922b000, 0xa9a4b001, 0xaa7eb000, 0xaafeb001,
-    0xab7eb000, 0xabfeb001, 0xac7eb000, 0xacfeb001, 0xfffe3004, 0xfffe3000,
-    0xfffe3001, 0xfffe3001, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe2193, 0xfffe2193, 0xfffe2193, 0xfffe2193, 0xfffe2193, 0xfffe2193,
-    0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe300c, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3044,
-    0xfffe3044, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe3044,
-    0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe3044,
-    0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xffff3044, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xffff3044,
-    0xfffe304c, 0xffff3044, 0xfffe304c, 0xffff3044, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3044, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3044,
-    0xfffe3044, 0xfffe0024, 0xfffe0024, 0xfffe2193, 0xfffe2193, 0xfffe3004,
-    0xfffe3004, 0xfffe3044, 0xfffe3044, 0xfffe304c, 0xfffe3004, 0xfffe3044,
-    0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe3044,
-    0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3044,
-    0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3044, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3044,
-    0xfffe3044, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe3004,
-    0xfffeb044, 0xfffe3044, 0xfffe3044, 0xfffe304c, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffeb04c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe0024, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe3044, 0xfffe3044,
-    0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044,
-    0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044,
-    0xfffe3044, 0xfffe3044, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062,
-    0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe3062, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe0024,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe300c, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe300c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe0064, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe0024, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe0064,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe3044, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304f, 0xfffe304f,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe300f,
-    0xfffe3005, 0xfffe300f, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304a, 0xfffe304a, 0xfffe304a, 0xfffe304a, 0xfffe304a, 0xfffe304a,
-    0xfffe304a, 0xfffe304a, 0xfffe304a, 0xfffe304a, 0xfffe304b, 0xfffe304b,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffea193, 0xfffeb193, 0xfffeb193, 0xfffeb193, 0xfffeb00b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193,
-    0xfffeb04b, 0xfffeb04f, 0xfffeb04f, 0xfffeb04f, 0xfffeb04f, 0xfffeb04f,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb00b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb00b, 0xfffeb00b,
-    0xfffeb00b, 0xfffeb00b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb249, 0xfffeb24b, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb010, 0xfffeb010, 0xfffeb005, 0xfffeb005,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb053, 0xfffeb053, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053,
-    0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04f, 0xfffeb04f, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb04f, 0xfffeb04f, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb057, 0xfffeb057,
-    0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057,
-    0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057,
-    0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057,
-    0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffeb057,
-    0xfffeb057, 0xfffeb057, 0xfffeb057, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffeb053, 0xfffeb053,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffeb053, 0xfffeb053,
-    0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb053, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04f,
-    0xfffeb04f, 0xfffeb04f, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffeb04b, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffea193, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffea193, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffea193, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffea193, 0xfffeb053, 0xfffe8024, 0xfffe8024, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04f,
-    0xfffeb04f, 0xfffeb04f, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e, 0xfffeb05e,
-    0xfffea19e, 0xfffea19e, 0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffea19e,
-    0xfffea19e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e,
-    0xfffea19e, 0xfffea19e, 0xfffeb05e, 0xfffea19e, 0xfffeb05e, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb05e,
-    0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffeb05e, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb053,
-    0xfffeb053, 0xfffea193, 0xfffeb053, 0xfffeb053, 0xfffea193, 0xfffeb053,
-    0xfffeb053, 0xfffeb04f, 0xfffeb053, 0xfffea193, 0xfffe8024, 0xfffe8024,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a,
-    0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffeb04a, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe0064, 0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064,
-    0xfffe3055, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe3056,
-    0xfffe3056, 0xfffe3056, 0xfffe3056, 0xfffe0064, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe3058,
-    0xfffe3058, 0xfffe3058, 0xfffe3058, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059, 0xfffe3059,
-    0xfffe3059, 0xfffe3059, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe0024, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe0024, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f, 0xfffe301f,
-    0xfffe301f, 0xfffe0024, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061,
-    0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe3061, 0xfffe0064,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe300c, 0xfffe0024, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe300c, 0xfffe0024, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe0024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffeb04b, 0xfffeb04b, 0xfffeb04b,
-    0xfffeb04b, 0xfffeb04b, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffeb08b, 0xfffea193, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb20b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffe80a4, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffe80a4, 0xfffeb08b, 0xfffe80a4,
-    0xfffeb08b, 0xfffeb08b, 0xfffe80a4, 0xfffeb08b, 0xfffeb08b, 0xfffe80a4,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b,
-    0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffeb08b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed800, 0xfffed801, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed964, 0xfffed964,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964, 0xfffed964,
-    0xfffed964, 0xfffed964, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4,
-    0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4,
-    0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4,
-    0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4,
-    0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4,
-    0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffedaa4, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed949, 0xfffed80b,
-    0xfffed964, 0xfffed964, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193,
-    0xfffe3007, 0xfffe3001, 0xfffe3001, 0xfffe3007, 0xfffe3007, 0xfffe3005,
-    0xfffe3005, 0xfffe3000, 0xfffe3001, 0xfffe300d, 0xfffe0024, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffea193, 0xfffea193,
-    0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffea193, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024, 0xfffe8024,
-    0xfffe8024, 0xfffe8024, 0xfffe300c, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe3000, 0xfffe3001, 0xfffe3000, 0xfffe3001, 0xfffe3000,
-    0xfffe3001, 0xfffe3000, 0xfffe3001, 0xfffe3000, 0xfffe3001, 0xfffe3000,
-    0xfffe3001, 0xfffe3000, 0xfffe3001, 0xfffe3000, 0xfffe3001, 0xfffe300c,
-    0xfffe300c, 0xfffe3000, 0xfffe3001, 0xfffe300c, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe300c, 0xfffe300c, 0xffa6300c, 0xfffe31c1, 0xfffe300c,
-    0xfffe31c1, 0xfffe0024, 0xfffe3004, 0xfffe31c4, 0xfffe3005, 0xfffe3005,
-    0xfffe300c, 0xad7e3000, 0xadfe3001, 0xae7e3000, 0xaefe3001, 0xaf7e3000,
-    0xaffe3001, 0xfffe324c, 0xfffe300c, 0xfffe300c, 0xfffe320c, 0xfffe320c,
-    0xb07e300c, 0xb0fe300c, 0xfffe300c, 0xfffe0024, 0xfffe300c, 0xfffe3248,
-    0xfffe3249, 0xfffe300c, 0xfffe0024, 0xfffe0024, 0xfffe0024, 0xfffe0024,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed964,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed94b,
-    0xfffed94b, 0xfffed94b, 0xfffed94b, 0xfffed964, 0xfffed964, 0xfffe9a94,
-    0xfffe0024, 0xffff3005, 0xfffe300c, 0xfffe324c, 0xfffe3248, 0xfffe3249,
-    0xfffe300c, 0xfffe300c, 0xb1283000, 0xb1aa3001, 0xfffe300c, 0xfffe320c,
-    0xffff31c1, 0xfffeb20c, 0xffff31c1, 0xfffe31cc, 0xfffe310c, 0xfffe310c,
-    0xfffe310c, 0xfffe310c, 0xfffe310c, 0xfffe310c, 0xfffe310c, 0xfffe310c,
-    0xfffe310c, 0xfffe310c, 0xffff31c4, 0xffff3004, 0xb27e300c, 0xfffe300c,
-    0xb2fe300c, 0xffff3005, 0xfffe300c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xb37eb000,
-    0xfffe300c, 0xb3feb001, 0xfffe300c, 0xfffeb00c, 0xfffe300c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c, 0xfffe304c,
-    0xfffe304c, 0xb42c3000, 0xfffe300c, 0xb4ae3001, 0xfffeb00c, 0xb57e3000,
-    0xb5fe3001, 0xfffeb001, 0xb67eb000, 0xb6feb001, 0xfffeb001, 0xfffe3004,
-    0xfffe304b, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044,
-    0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe3044, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe3044, 0xfffe3044, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe0024, 0xfffe0024, 0xfffe304b, 0xfffe304b,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe0024, 0xfffe0024,
-    0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe304b,
-    0xfffe0024, 0xfffe0024, 0xfffe304b, 0xfffe304b, 0xfffe304b, 0xfffe0024,
-    0xfffe0024, 0xfffe0024, 0xfffe3249, 0xfffe3248, 0xfffe300c, 0xfffe300c,
-    0xfffe300c, 0xfffe3248, 0xfffe3248, 0xfffe0024, 0xfffe300b, 0xfffe300b,
-    0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe300b, 0xfffe0024,
-    0xfffe02a4, 0xfffe02a4, 0xfffe02a4, 0xfffe02a4, 0xfffe02a4, 0xfffe02a4,
-    0xfffe02a4, 0xfffe02a4, 0xfffe02a4, 0xfffe3013, 0xfffe3013, 0xfffe3013,
-    0xfffe3011, 0xfffe3022, 0xfffe1aa4, 0xfffe1aa4,
-};
-
-const size_t kTextLayoutCodePropertiesSize =
-    FX_ArraySize(kTextLayoutCodeProperties);
-
-const uint16_t kFXTextLayoutBidiMirror[] = {
-    0x0029, 0x0028, 0x003E, 0x003C, 0x005D, 0x005B, 0x007D, 0x007B, 0x00BB,
-    0x00AB, 0x0F3B, 0x0F3A, 0x0F3D, 0x0F3C, 0x169C, 0x169B, 0x2019, 0x2018,
-    0x201D, 0x201C, 0x203A, 0x2039, 0x2046, 0x2045, 0x207E, 0x207D, 0x208E,
-    0x208D, 0x220B, 0x220C, 0x220D, 0x2208, 0x2209, 0x220A, 0x29F5, 0x223D,
-    0x223C, 0x22CD, 0x2253, 0x2252, 0x2255, 0x2254, 0x2265, 0x2264, 0x2267,
-    0x2266, 0x2269, 0x2268, 0x226B, 0x226A, 0x226F, 0x226E, 0x2271, 0x2270,
-    0x2273, 0x2272, 0x2275, 0x2274, 0x2277, 0x2276, 0x2279, 0x2278, 0x227B,
-    0x227A, 0x227D, 0x227C, 0x227F, 0x227E, 0x2281, 0x2280, 0x2283, 0x2282,
-    0x2285, 0x2284, 0x2287, 0x2286, 0x2289, 0x2288, 0x228B, 0x228A, 0x2290,
-    0x228F, 0x2292, 0x2291, 0x29B8, 0x22A3, 0x22A2, 0x2ADE, 0x2AE4, 0x2AE3,
-    0x2AE5, 0x22B1, 0x22B0, 0x22B3, 0x22B2, 0x22B5, 0x22B4, 0x22B7, 0x22B6,
-    0x22CA, 0x22C9, 0x22CC, 0x22CB, 0x2243, 0x22D1, 0x22D0, 0x22D7, 0x22D6,
-    0x22D9, 0x22D8, 0x22DB, 0x22DA, 0x22DD, 0x22DC, 0x22DF, 0x22DE, 0x22E1,
-    0x22E0, 0x22E3, 0x22E2, 0x22E5, 0x22E4, 0x22E7, 0x22E6, 0x22E9, 0x22E8,
-    0x22EB, 0x22EA, 0x22ED, 0x22EC, 0x22F1, 0x22F0, 0x22FA, 0x22FB, 0x22FC,
-    0x22FD, 0x22FE, 0x22F2, 0x22F3, 0x22F4, 0x22F6, 0x22F7, 0x2309, 0x2308,
-    0x230B, 0x230A, 0x232A, 0x2329, 0x2769, 0x2768, 0x276B, 0x276A, 0x276D,
-    0x276C, 0x276F, 0x276E, 0x2771, 0x2770, 0x2773, 0x2772, 0x2775, 0x2774,
-    0x27C4, 0x27C3, 0x27C6, 0x27C5, 0x27C9, 0x27C8, 0x27D6, 0x27D5, 0x27DE,
-    0x27DD, 0x27E3, 0x27E2, 0x27E5, 0x27E4, 0x27E7, 0x27E6, 0x27E9, 0x27E8,
-    0x27EB, 0x27EA, 0x27ED, 0x27EC, 0x27EF, 0x27EE, 0x2984, 0x2983, 0x2986,
-    0x2985, 0x2988, 0x2987, 0x298A, 0x2989, 0x298C, 0x298B, 0x2990, 0x298F,
-    0x298E, 0x298D, 0x2992, 0x2991, 0x2994, 0x2993, 0x2996, 0x2995, 0x2998,
-    0x2997, 0x2298, 0x29C1, 0x29C0, 0x29C5, 0x29C4, 0x29D0, 0x29CF, 0x29D2,
-    0x29D1, 0x29D5, 0x29D4, 0x29D9, 0x29D8, 0x29DB, 0x29DA, 0x2215, 0x29F9,
-    0x29F8, 0x29FD, 0x29FC, 0x2A2C, 0x2A2B, 0x2A2E, 0x2A2D, 0x2A35, 0x2A34,
-    0x2A3D, 0x2A3C, 0x2A65, 0x2A64, 0x2A7A, 0x2A79, 0x2A7E, 0x2A7D, 0x2A80,
-    0x2A7F, 0x2A82, 0x2A81, 0x2A84, 0x2A83, 0x2A8C, 0x2A8B, 0x2A92, 0x2A91,
-    0x2A94, 0x2A93, 0x2A96, 0x2A95, 0x2A98, 0x2A97, 0x2A9A, 0x2A99, 0x2A9C,
-    0x2A9B, 0x2AA2, 0x2AA1, 0x2AA7, 0x2AA6, 0x2AA9, 0x2AA8, 0x2AAB, 0x2AAA,
-    0x2AAD, 0x2AAC, 0x2AB0, 0x2AAF, 0x2AB4, 0x2AB3, 0x2ABC, 0x2ABB, 0x2ABE,
-    0x2ABD, 0x2AC0, 0x2ABF, 0x2AC2, 0x2AC1, 0x2AC4, 0x2AC3, 0x2AC6, 0x2AC5,
-    0x2ACE, 0x2ACD, 0x2AD0, 0x2ACF, 0x2AD2, 0x2AD1, 0x2AD4, 0x2AD3, 0x2AD6,
-    0x2AD5, 0x22A6, 0x22A9, 0x22A8, 0x22AB, 0x2AED, 0x2AEC, 0x2AF8, 0x2AF7,
-    0x2AFA, 0x2AF9, 0x2E03, 0x2E02, 0x2E05, 0x2E04, 0x2E0A, 0x2E09, 0x2E0D,
-    0x2E0C, 0x2E1D, 0x2E1C, 0x2E21, 0x2E20, 0x2E23, 0x2E22, 0x2E25, 0x2E24,
-    0x2E27, 0x2E26, 0x2E29, 0x2E28, 0x3009, 0x3008, 0x300B, 0x300A, 0x300D,
-    0x300C, 0x300F, 0x300E, 0x3011, 0x3010, 0x3015, 0x3014, 0x3017, 0x3016,
-    0x3019, 0x3018, 0x301B, 0x301A, 0xFE5A, 0xFE59, 0xFE5C, 0xFE5B, 0xFE5E,
-    0xFE5D, 0xFE65, 0xFE64, 0xFF09, 0xFF08, 0xFF1E, 0xFF1C, 0xFF3D, 0xFF3B,
-    0xFF5D, 0xFF5B, 0xFF60, 0xFF5F, 0xFF63, 0xFF62, 0xFFFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-    0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF, 0xFEFF,
-};
-
-const size_t kFXTextLayoutBidiMirrorSize =
-    FX_ArraySize(kFXTextLayoutBidiMirror);
diff --git a/core/fxcrt/fx_ucddata.h b/core/fxcrt/fx_ucddata.h
deleted file mode 100644
index 78a6b97..0000000
--- a/core/fxcrt/fx_ucddata.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_FX_UCDDATA_H_
-#define CORE_FXCRT_FX_UCDDATA_H_
-
-#include "core/fxcrt/fx_system.h"
-
-extern const uint32_t kTextLayoutCodeProperties[];
-extern const size_t kTextLayoutCodePropertiesSize;
-
-extern const uint16_t kFXTextLayoutBidiMirror[];
-extern const size_t kFXTextLayoutBidiMirrorSize;
-
-#endif  // CORE_FXCRT_FX_UCDDATA_H_
diff --git a/core/fxcrt/fx_ucddata.inc b/core/fxcrt/fx_ucddata.inc
new file mode 100644
index 0000000..bcc09ea
--- /dev/null
+++ b/core/fxcrt/fx_ucddata.inc
@@ -0,0 +1,65536 @@
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0000
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0001
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0002
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0003
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0004
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0005
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0006
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0007
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0008
+  CHARPROP____(0x1ffu, kTab, kS, kTB)  // U+0009
+  CHARPROP____(0x1ffu, kControl, kB, kLF)  // U+000A
+  CHARPROP____(0x1ffu, kControl, kS, kBK)  // U+000B
+  CHARPROP____(0x1ffu, kControl, kWS, kBK)  // U+000C
+  CHARPROP____(0x1ffu, kControl, kB, kCR)  // U+000D
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+000E
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+000F
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0010
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0011
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0012
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0013
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0014
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0015
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0016
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0017
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0018
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+0019
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+001A
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+001B
+  CHARPROP____(0x1ffu, kControl, kB, kCM)  // U+001C
+  CHARPROP____(0x1ffu, kControl, kB, kCM)  // U+001D
+  CHARPROP____(0x1ffu, kControl, kB, kCM)  // U+001E
+  CHARPROP____(0x1ffu, kControl, kS, kCM)  // U+001F
+  CHARPROP____(0x1ffu, kSpace, kWS, kSP)  // U+0020
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+0021
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+0022
+  CHARPROP____(0x1ffu, kNormal, kET, kAL)  // U+0023
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+0024
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+0025
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0026
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+0027
+  CHARPROP____(0x000u, kNormal, kON, kOP)  // U+0028
+  CHARPROP____(0x001u, kNormal, kON, kCL)  // U+0029
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+002A
+  CHARPROP____(0x1ffu, kNormal, kES, kPR)  // U+002B
+  CHARPROP____(0x1ffu, kNormal, kCS, kIS)  // U+002C
+  CHARPROP____(0x1ffu, kNormal, kES, kHY)  // U+002D
+  CHARPROP____(0x1ffu, kNormal, kCS, kIS)  // U+002E
+  CHARPROP____(0x1ffu, kNormal, kCS, kSY)  // U+002F
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0030
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0031
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0032
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0033
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0034
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0035
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0036
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0037
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0038
+  CHARPROP____(0x1ffu, kNumeric, kEN, kNU)  // U+0039
+  CHARPROP____(0x1ffu, kNormal, kCS, kIS)  // U+003A
+  CHARPROP____(0x1ffu, kNormal, kON, kIS)  // U+003B
+  CHARPROP____(0x002u, kNormal, kON, kOP)  // U+003C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+003D
+  CHARPROP____(0x003u, kNormal, kON, kCL)  // U+003E
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+003F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0040
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0041
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0042
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0043
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0044
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0045
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0046
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0047
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0048
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0049
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+004A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+004B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+004C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+004D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+004E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+004F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0050
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0051
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0052
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0053
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0054
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0055
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0056
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0057
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0058
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0059
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+005A
+  CHARPROP____(0x004u, kNormal, kON, kOP)  // U+005B
+  CHARPROP____(0x1ffu, kNormal, kON, kPR)  // U+005C
+  CHARPROP____(0x005u, kNormal, kON, kCL)  // U+005D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+005E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+005F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0060
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0061
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0062
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0063
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0064
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0065
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0066
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0067
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0068
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0069
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+006A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+006B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+006C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+006D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+006E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+006F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0070
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0071
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0072
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0073
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0074
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0075
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0076
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0077
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0078
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0079
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+007A
+  CHARPROP____(0x006u, kNormal, kON, kOP)  // U+007B
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+007C
+  CHARPROP____(0x007u, kNormal, kON, kCL)  // U+007D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+007E
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+007F
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0080
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0081
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0082
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0083
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0084
+  CHARPROP____(0x1ffu, kNormal, kB, kNL)  // U+0085
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0086
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0087
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0088
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0089
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+008A
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+008B
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+008C
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+008D
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+008E
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+008F
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0090
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0091
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0092
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0093
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0094
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0095
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0096
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0097
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0098
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+0099
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+009A
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+009B
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+009C
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+009D
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+009E
+  CHARPROP____(0x1ffu, kNormal, kBN, kCM)  // U+009F
+  CHARPROP____(0x1ffu, kSpace, kCS, kGL)  // U+00A0
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+00A1
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+00A2
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+00A3
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+00A4
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+00A5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+00A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00A7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00A8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+00A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+00AA
+  CHARPROP____(0x008u, kNormal, kON, kOP)  // U+00AB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+00AC
+  CHARPROP____(0x1ffu, kNormal, kBN, kBA)  // U+00AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+00AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+00AF
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+00B0
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+00B1
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+00B2
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+00B3
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+00B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00B5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00B6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00B7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00B8
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+00B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+00BA
+  CHARPROP____(0x009u, kNormal, kON, kCL)  // U+00BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00BE
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+00BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+00F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+00FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0100
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0101
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0102
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0103
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0104
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0105
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0106
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0107
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0108
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0109
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+010A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+010B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+010C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+010D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+010E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+010F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0110
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0111
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0112
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0113
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0114
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0115
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0116
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0117
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0118
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0119
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+011A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+011B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+011C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+011D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+011E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+011F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0120
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0121
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0122
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0123
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0124
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0125
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0126
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0127
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0128
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0129
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+012A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+012B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+012C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+012D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+012E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+012F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0130
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0131
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0132
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0133
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0134
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0135
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0136
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0137
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0138
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0139
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+013A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+013B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+013C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+013D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+013E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+013F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0140
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0141
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0142
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0143
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0144
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0145
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0146
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0147
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0148
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0149
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+014A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+014B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+014C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+014D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+014E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+014F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0150
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0151
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0152
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0153
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0154
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0155
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0156
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0157
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0158
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0159
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+015A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+015B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+015C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+015D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+015E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+015F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0160
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0161
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0162
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0163
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0164
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0165
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0166
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0167
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0168
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0169
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+016A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+016B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+016C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+016D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+016E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+016F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0170
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0171
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0172
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0173
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0174
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0175
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0176
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0177
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0178
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0179
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+017A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+017B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+017C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+017D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+017E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+017F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0180
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0181
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0182
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0183
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0184
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0185
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0186
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0187
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0188
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0189
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+018A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+018B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+018C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+018D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+018E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+018F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0190
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0191
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0192
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0193
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0194
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0195
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0196
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0197
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0198
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0199
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+019A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+019B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+019C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+019D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+019E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+019F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+01FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0200
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0201
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0202
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0203
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0204
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0205
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0206
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0207
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0208
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0209
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+020A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+020B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+020C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+020D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+020E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+020F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0210
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0211
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0212
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0213
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0214
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0215
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0216
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0217
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0218
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0219
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+021A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+021B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+021C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+021D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+021E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+021F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0220
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0221
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0222
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0223
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0224
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0225
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0226
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0227
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0228
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0229
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+022A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+022B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+022C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+022D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+022E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+022F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0230
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0231
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0232
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0233
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0234
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0235
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0236
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0237
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0238
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0239
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+023A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+023B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+023C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+023D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+023E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+023F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0240
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0241
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0242
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0243
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0244
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0245
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0246
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0247
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0248
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0249
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+024A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+024B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+024C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+024D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+024E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+024F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0250
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0251
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0252
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0253
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0254
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0255
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0256
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0257
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0258
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0259
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+025A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+025B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+025C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+025D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+025E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+025F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0260
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0261
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0262
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0263
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0264
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0265
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0266
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0267
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0268
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0269
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+026A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+026B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+026C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+026D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+026E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+026F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0270
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0271
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0272
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0273
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0274
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0275
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0276
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0277
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0278
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0279
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+027A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+027B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+027C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+027D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+027E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+027F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0280
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0281
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0282
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0283
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0284
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0285
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0286
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0287
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0288
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0289
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+028A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+028B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+028C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+028D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+028E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+028F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0290
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0291
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0292
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0293
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0294
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0295
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0296
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0297
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0298
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0299
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+029A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+029B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+029C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+029D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+029E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+029F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02C2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02C3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02C4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02C5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02C7
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+02C8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02C9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02CA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02CB
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+02CC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02CD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02CE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+02D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02D1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02D3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02D4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02D5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02D7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02D8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02D9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02DA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02DB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02DC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+02DD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02DE
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+02DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02E4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02E5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02E6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02E7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02E8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02E9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02EA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02EB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02EC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+02EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02FB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02FC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+02FF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0300
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0301
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0302
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0303
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0304
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0305
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0306
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0307
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0308
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0309
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+030A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+030B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+030C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+030D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+030E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+030F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0310
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0311
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0312
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0313
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0314
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0315
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0316
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0317
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0318
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0319
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+031A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+031B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+031C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+031D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+031E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+031F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0320
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0321
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0322
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0323
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0324
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0325
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0326
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0327
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0328
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0329
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+032A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+032B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+032C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+032D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+032E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+032F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0330
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0331
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0332
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0333
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0334
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0335
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0336
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0337
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0338
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0339
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+033A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+033B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+033C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+033D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+033E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+033F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0340
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0341
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0342
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0343
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0344
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0345
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0346
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0347
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0348
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0349
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+034A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+034B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+034C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+034D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+034E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+034F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0350
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0351
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0352
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0353
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0354
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0355
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0356
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0357
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0358
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0359
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+035A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+035B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+035C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+035D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+035E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+035F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+0360
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+0361
+  CHARPROP____(0x1ffu, kCombination, kNSM, kGL)  // U+0362
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0363
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0364
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0365
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0366
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0367
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0368
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0369
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+036A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+036B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+036C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+036D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+036E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+036F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0370
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0371
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0372
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0373
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0374
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0375
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0376
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0377
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0378
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0379
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+037A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+037B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+037C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+037D
+  CHARPROP____(0x1ffu, kNormal, kON, kIS)  // U+037E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+037F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0380
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0381
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0382
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0383
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0384
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0385
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0386
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0387
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0388
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0389
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+038A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+038B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+038C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+038D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+038E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+038F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0390
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0391
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0392
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0393
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0394
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0395
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0396
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0397
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0398
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0399
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+039A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+039B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+039C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+039D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+039E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+039F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+03A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+03F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+03FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0400
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0401
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0402
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0403
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0404
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0405
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0406
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0407
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0408
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0409
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+040A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+040B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+040C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+040D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+040E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+040F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0410
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0411
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0412
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0413
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0414
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0415
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0416
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0417
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0418
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0419
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+041A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+041B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+041C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+041D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+041E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+041F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0420
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0421
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0422
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0423
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0424
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0425
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0426
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0427
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0428
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0429
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+042A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+042B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+042C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+042D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+042E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+042F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0430
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0431
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0432
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0433
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0434
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0435
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0436
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0437
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0438
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0439
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+043A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+043B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+043C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+043D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+043E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+043F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0440
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0441
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0442
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0443
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0444
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0445
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0446
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0447
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0448
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0449
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+044A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+044B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+044C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+044D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+044E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+044F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0450
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0451
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0452
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0453
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0454
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0455
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0456
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0457
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0458
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0459
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+045A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+045B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+045C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+045D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+045E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+045F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0460
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0461
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0462
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0463
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0464
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0465
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0466
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0467
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0468
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0469
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+046A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+046B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+046C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+046D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+046E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+046F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0470
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0471
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0472
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0473
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0474
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0475
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0476
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0477
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0478
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0479
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+047A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+047B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+047C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+047D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+047E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+047F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0480
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0481
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0482
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0483
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0484
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0485
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0486
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0487
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+0488
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+0489
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+048A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+048B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+048C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+048D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+048E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+048F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0490
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0491
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0492
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0493
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0494
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0495
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0496
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0497
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0498
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0499
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+049A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+049B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+049C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+049D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+049E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+049F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+04FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0500
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0501
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0502
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0503
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0504
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0505
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0506
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0507
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0508
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0509
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+050A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+050B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+050C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+050D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+050E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+050F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0510
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0511
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0512
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0513
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0514
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0515
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0516
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0517
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0518
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0519
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+051A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+051B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+051C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+051D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+051E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+051F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0520
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0521
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0522
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0523
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0524
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0525
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0526
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0527
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0528
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0529
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+052A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+052B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+052C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+052D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+052E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+052F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0530
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0531
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0532
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0533
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0534
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0535
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0536
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0537
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0538
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0539
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+053A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+053B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+053C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+053D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+053E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+053F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0540
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0541
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0542
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0543
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0544
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0545
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0546
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0547
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0548
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0549
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+054A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+054B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+054C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+054D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+054E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+054F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0550
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0551
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0552
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0553
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0554
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0555
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0556
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0557
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0558
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0559
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+055A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+055B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+055C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+055D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+055E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+055F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0560
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0561
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0562
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0563
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0564
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0565
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0566
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0567
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0568
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0569
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+056A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+056B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+056C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+056D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+056E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+056F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0570
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0571
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0572
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0573
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0574
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0575
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0576
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0577
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0578
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0579
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+057A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+057B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+057C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+057D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+057E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+057F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0580
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0581
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0582
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0583
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0584
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0585
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0586
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0587
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0588
+  CHARPROP____(0x1ffu, kNormal, kL, kIS)  // U+0589
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+058A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+058B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+058C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+058D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+058E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+058F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0590
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0591
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0592
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0593
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0594
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0595
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0596
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0597
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0598
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0599
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+059A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+059B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+059C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+059D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+059E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+059F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05A9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05AA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05AB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05AC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05AD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05AE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05AF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05B9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05BA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05BB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05BC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05BD
+  CHARPROP____(0x1ffu, kNormal, kR, kBA)  // U+05BE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05BF
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05C0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05C1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05C2
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05C3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05C4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05C5
+  CHARPROP____(0x1ffu, kNormal, kR, kEX)  // U+05C6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+05C7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05C8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05C9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05CA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05CB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05CC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05CD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05CE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05CF
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D0
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D1
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D2
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D3
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D4
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D5
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D6
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D7
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D8
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05D9
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05DA
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05DB
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05DC
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05DD
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05DE
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05DF
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E0
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E1
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E2
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E3
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E4
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E5
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E6
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E7
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E8
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05E9
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05EA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05EB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05EC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05ED
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05EE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05EF
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05F0
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05F1
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05F2
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05F3
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+05F4
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05F5
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05F6
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05F7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05F8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05F9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05FA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05FB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05FC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05FD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05FE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+05FF
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kAL)  // U+0600
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kAL)  // U+0601
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kAL)  // U+0602
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kAL)  // U+0603
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kNONE)  // U+0604
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kNONE)  // U+0605
+  CHARPROP____(0x1ffu, kArabicNormal, kON, kAL)  // U+0606
+  CHARPROP____(0x1ffu, kArabicNormal, kON, kAL)  // U+0607
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0608
+  CHARPROP____(0x1ffu, kArabicNormal, kET, kPO)  // U+0609
+  CHARPROP____(0x1ffu, kArabicNormal, kET, kPO)  // U+060A
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kPO)  // U+060B
+  CHARPROP____(0x1ffu, kArabicNormal, kCS, kIS)  // U+060C
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kIS)  // U+060D
+  CHARPROP____(0x1ffu, kArabicNormal, kON, kAL)  // U+060E
+  CHARPROP____(0x1ffu, kArabicNormal, kON, kAL)  // U+060F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0610
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0611
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0612
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0613
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0614
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0615
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0616
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0617
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0618
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0619
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+061A
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kEX)  // U+061B
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kNONE)  // U+061C
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kNONE)  // U+061D
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kEX)  // U+061E
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kEX)  // U+061F
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kNONE)  // U+0620
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0621
+  CHARPROP____(0x1ffu, kArabicAlef, kAL, kAL)  // U+0622
+  CHARPROP____(0x1ffu, kArabicAlef, kAL, kAL)  // U+0623
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0624
+  CHARPROP____(0x1ffu, kArabicAlef, kAL, kAL)  // U+0625
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0626
+  CHARPROP____(0x1ffu, kArabicAlef, kAL, kAL)  // U+0627
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0628
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0629
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+062A
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+062B
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+062C
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+062D
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+062E
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+062F
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0630
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0631
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0632
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0633
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0634
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0635
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0636
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0637
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0638
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0639
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+063A
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+063B
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+063C
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+063D
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+063E
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+063F
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0640
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0641
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0642
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0643
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0644
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0645
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0646
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0647
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0648
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0649
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+064A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+064B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+064C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+064D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+064E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+064F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0650
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0651
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0652
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0653
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0654
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0655
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0656
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0657
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0658
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0659
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+065A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+065B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+065C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+065D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+065E
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kNONE)  // U+065F
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0660
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0661
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0662
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0663
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0664
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0665
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0666
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0667
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0668
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+0669
+  CHARPROP____(0x1ffu, kArabicNormal, kET, kPO)  // U+066A
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+066B
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kNU)  // U+066C
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+066D
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+066E
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+066F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0670
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0671
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0672
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0673
+  CHARPROP____(0x1ffu, kCombination, kAL, kAL)  // U+0674
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0675
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0676
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0677
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0678
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0679
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+067A
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+067B
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+067C
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+067D
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+067E
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+067F
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0680
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0681
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0682
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0683
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0684
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0685
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0686
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+0687
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0688
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0689
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+068A
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+068B
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+068C
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+068D
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+068E
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+068F
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0690
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0691
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0692
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0693
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0694
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0695
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0696
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0697
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0698
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0699
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+069A
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+069B
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+069C
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+069D
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+069E
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+069F
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A0
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A1
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A2
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A3
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A4
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A5
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A6
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A7
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A8
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06A9
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06AA
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06AB
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06AC
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06AD
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06AE
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06AF
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B0
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B1
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B2
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B3
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B4
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B5
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B6
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06B7
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06B8
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06B9
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06BA
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06BB
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06BC
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06BD
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06BE
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06BF
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C0
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06C1
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06C2
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C3
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C4
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C5
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C6
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C7
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C8
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06C9
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06CA
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06CB
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06CC
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06CD
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06CE
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06CF
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06D0
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kAL)  // U+06D1
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06D2
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06D3
+  CHARPROP____(0x1ffu, kArabicDistortion, kAL, kEX)  // U+06D4
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06D5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06D6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06D7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06D8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06D9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06DA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06DB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06DC
+  CHARPROP____(0x1ffu, kArabicNormal, kAN, kAL)  // U+06DD
+  CHARPROP____(0x1ffu, kArabicNormal, kNSM, kCM)  // U+06DE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06DF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06E0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06E1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06E2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06E3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06E4
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06E5
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06E6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06E7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06E8
+  CHARPROP____(0x1ffu, kArabicNormal, kON, kAL)  // U+06E9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06EA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06EB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06EC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+06ED
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06EE
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+06EF
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F0
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F1
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F2
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F3
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F4
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F5
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F6
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F7
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F8
+  CHARPROP____(0x1ffu, kArabicNormal, kEN, kNU)  // U+06F9
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06FA
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06FB
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06FC
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06FD
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06FE
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+06FF
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0700
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0701
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0702
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0703
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0704
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0705
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0706
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0707
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0708
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0709
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+070A
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+070B
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+070C
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+070D
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+070E
+  CHARPROP____(0x1ffu, kNormal, kBN, kAL)  // U+070F
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0710
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0711
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0712
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0713
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0714
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0715
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0716
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0717
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0718
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0719
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+071A
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+071B
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+071C
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+071D
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+071E
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+071F
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0720
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0721
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0722
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0723
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0724
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0725
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0726
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0727
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0728
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0729
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+072A
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+072B
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+072C
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+072D
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+072E
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+072F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0730
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0731
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0732
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0733
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0734
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0735
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0736
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0737
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0738
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0739
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+073A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+073B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+073C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+073D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+073E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+073F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0740
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0741
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0742
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0743
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0744
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0745
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0746
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0747
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0748
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0749
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+074A
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+074B
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+074C
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+074D
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+074E
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+074F
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0750
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0751
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0752
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0753
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0754
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0755
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0756
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0757
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0758
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0759
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+075A
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+075B
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+075C
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+075D
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+075E
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+075F
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0760
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0761
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0762
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0763
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0764
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0765
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0766
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0767
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0768
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0769
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+076A
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+076B
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+076C
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+076D
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+076E
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+076F
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0770
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0771
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0772
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0773
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0774
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0775
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0776
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+0777
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0778
+  CHARPROP____(0x1ffu, kArabicSpecial, kAL, kAL)  // U+0779
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+077A
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+077B
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+077C
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+077D
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+077E
+  CHARPROP____(0x1ffu, kArabicNormal, kAL, kAL)  // U+077F
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0780
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0781
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0782
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0783
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0784
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0785
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0786
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0787
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0788
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0789
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+078A
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+078B
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+078C
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+078D
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+078E
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+078F
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0790
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0791
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0792
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0793
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0794
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0795
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0796
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0797
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0798
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+0799
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+079A
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+079B
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+079C
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+079D
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+079E
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+079F
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+07A0
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+07A1
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+07A2
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+07A3
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+07A4
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+07A5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07A6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07A7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07A8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07A9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07AA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07AB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07AC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07AD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07AE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07AF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07B0
+  CHARPROP____(0x1ffu, kNormal, kAL, kAL)  // U+07B1
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B2
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B3
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B4
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B5
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B6
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B7
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B8
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07B9
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07BA
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07BB
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07BC
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07BD
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07BE
+  CHARPROP____(0x1ffu, kUnknown, kAL, kNONE)  // U+07BF
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C0
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C1
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C2
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C3
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C4
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C5
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C6
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C7
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C8
+  CHARPROP____(0x1ffu, kNormal, kR, kNU)  // U+07C9
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07CA
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07CB
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07CC
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07CD
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07CE
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07CF
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D0
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D1
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D2
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D3
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D4
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D5
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D6
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D7
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D8
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07D9
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07DA
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07DB
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07DC
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07DD
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07DE
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07DF
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E0
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E1
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E2
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E3
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E4
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E5
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E6
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E7
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E8
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07E9
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07EA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07EB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07EC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07ED
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07EE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07EF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07F0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07F1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07F2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+07F3
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07F4
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+07F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+07F7
+  CHARPROP____(0x1ffu, kNormal, kON, kIS)  // U+07F8
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+07F9
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+07FA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+07FB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+07FC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+07FD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+07FE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+07FF
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0800
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0801
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0802
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0803
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0804
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0805
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0806
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0807
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0808
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0809
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+080A
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+080B
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+080C
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+080D
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+080E
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+080F
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0810
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0811
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0812
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0813
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0814
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0815
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0816
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0817
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0818
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0819
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+081A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+081B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+081C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+081D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+081E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+081F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0820
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0821
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0822
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0823
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0824
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0825
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0826
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0827
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0828
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0829
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+082A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+082B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+082C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+082D
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+082E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+082F
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0830
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0831
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0832
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0833
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0834
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0835
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0836
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0837
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0838
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+0839
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+083A
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+083B
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+083C
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+083D
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+083E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+083F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0840
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0841
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0842
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0843
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0844
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0845
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0846
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0847
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0848
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0849
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+084A
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+084B
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+084C
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+084D
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+084E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+084F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0850
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0851
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0852
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0853
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0854
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0855
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0856
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0857
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0858
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0859
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+085A
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+085B
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+085C
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+085D
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+085E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+085F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0860
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0861
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0862
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0863
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0864
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0865
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0866
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0867
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0868
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0869
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+086A
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+086B
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+086C
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+086D
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+086E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+086F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0870
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0871
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0872
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0873
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0874
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0875
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0876
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0877
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0878
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0879
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+087A
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+087B
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+087C
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+087D
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+087E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+087F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0880
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0881
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0882
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0883
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0884
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0885
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0886
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0887
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0888
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0889
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+088A
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+088B
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+088C
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+088D
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+088E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+088F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0890
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0891
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0892
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0893
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0894
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0895
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0896
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0897
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0898
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+0899
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+089A
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+089B
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+089C
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+089D
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+089E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+089F
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A0
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A1
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A2
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A3
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A4
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A5
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A6
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08A9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08AA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08AB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08AC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08AD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08AE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08AF
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B0
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B1
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B2
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B3
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B4
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B5
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B6
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08B9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08BA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08BB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08BC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08BD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08BE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08BF
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C0
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C1
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C2
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C3
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C4
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C5
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C6
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08C9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08CA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08CB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08CC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08CD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08CE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08CF
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D0
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D1
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D2
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D3
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D4
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D5
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D6
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08D9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08DA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08DB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08DC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08DD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08DE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08DF
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E0
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E1
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E2
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E3
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E4
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E5
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E6
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08E9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08EA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08EB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08EC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08ED
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08EE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08EF
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F0
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F1
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F2
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F3
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F4
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F5
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F6
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F7
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F8
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08F9
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08FA
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08FB
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08FC
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08FD
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08FE
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+08FF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0900
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0901
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0902
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0903
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0904
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0905
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0906
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0907
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0908
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0909
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+090A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+090B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+090C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+090D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+090E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+090F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0910
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0911
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0912
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0913
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0914
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0915
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0916
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0917
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0918
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0919
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+091A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+091B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+091C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+091D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+091E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+091F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0920
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0921
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0922
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0923
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0924
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0925
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0926
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0927
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0928
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0929
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+092A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+092B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+092C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+092D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+092E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+092F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0930
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0931
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0932
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0933
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0934
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0935
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0936
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0937
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0938
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0939
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+093A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+093B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+093C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+093D
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+093E
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+093F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0940
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0941
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0942
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0943
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0944
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0945
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0946
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0947
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0948
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0949
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+094A
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+094B
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+094C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+094D
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+094E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+094F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0950
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0951
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0952
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0953
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0954
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0955
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0956
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0957
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0958
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0959
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+095A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+095B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+095C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+095D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+095E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+095F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0960
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0961
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0962
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0963
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0964
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0965
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0966
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0967
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0968
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0969
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+096A
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+096B
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+096C
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+096D
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+096E
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+096F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0970
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0971
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0972
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0973
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0974
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0975
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0976
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0977
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0978
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0979
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+097A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+097B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+097C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+097D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+097E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+097F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0980
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0981
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0982
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0983
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0984
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0985
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0986
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0987
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0988
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0989
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+098A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+098B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+098C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+098D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+098E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+098F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0990
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0991
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0992
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0993
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0994
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0995
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0996
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0997
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0998
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0999
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+099A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+099B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+099C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+099D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+099E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+099F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09A8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09B0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09B2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09B3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09B4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09B9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09BA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09BB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09BD
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09BE
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09BF
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09C0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09C1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09C2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09C3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09C4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09C5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09C6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09C7
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09C8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09CA
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09CB
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09CC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09CE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09CF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+09D7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09D9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09DA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09DD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09E1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09E2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+09E3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09E4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09E5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09E6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09E7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09E8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09E9
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09EA
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09EB
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09EC
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09ED
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09EE
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+09EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09F1
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+09F2
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+09F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09F8
+  CHARPROP____(0x1ffu, kNormal, kL, kPO)  // U+09F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+09FA
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+09FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+09FF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A00
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A01
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A02
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0A03
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A0A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A0B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A0C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A0D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A10
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A11
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A28
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A30
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A33
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A36
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A3B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A3C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0A40
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A41
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A42
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A43
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A44
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A45
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A46
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A47
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A48
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A49
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A4A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A4B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A4C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A4F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A50
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A51
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A52
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A53
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A54
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A55
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A56
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A57
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A59
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A5C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A5F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A60
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A61
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A62
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A65
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A66
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A67
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A68
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A69
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0A6F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A70
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A74
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A78
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A79
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A7A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A80
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A81
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0A82
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0A83
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A8D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A91
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0A92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AA8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ABA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ABB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0AC0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AC1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AC2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AC3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AC4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AC5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AC6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AC7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0AC9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0ACC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0ACD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ACE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ADA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ADB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ADC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ADD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ADE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0AE1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AE2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0AE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AED
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0AEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF0
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+0AF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0AFF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B00
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B01
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B02
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B03
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B0C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B0D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B10
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B11
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B28
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B30
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B33
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B3B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B3E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B40
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B41
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B42
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B43
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B44
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B45
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B46
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B47
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B48
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B49
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B4C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B4F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B50
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B51
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B52
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B53
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B54
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B55
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B56
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0B57
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B58
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B5A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B61
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B62
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B65
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B66
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B67
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B68
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B69
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B71
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B72
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B73
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B74
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B78
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B79
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B7A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B80
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B81
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0B82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B83
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B8A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B8B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B8C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B90
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B95
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B96
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B9A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B9C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0B9F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BA0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BA1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BA4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BA5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BA6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BAA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BAB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BAC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BBA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BBB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BBC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BBF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BC2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BC3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BCC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0BCD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BCE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0BD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BDA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BDB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BDC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BDD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BDF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BED
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0BF2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0BF3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0BF4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0BF5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0BF6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0BF7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0BF8
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+0BF9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0BFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0BFF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C00
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C01
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C02
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C03
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C0C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C10
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C28
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C33
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C3B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C3D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C3E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C3F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C40
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C41
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C42
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C43
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C44
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C45
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C46
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C47
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C48
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C49
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C4A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C4B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C4C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C4F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C50
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C51
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C52
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C53
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C54
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C55
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C56
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C57
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C5A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C5B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C5C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C61
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C62
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0C63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C65
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C66
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C67
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C68
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C69
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0C6F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C70
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C71
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C72
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C73
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C74
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C77
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0C78
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0C79
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0C7A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0C7B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0C7C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0C7D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C80
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C81
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C82
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0C83
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C8C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C90
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0C91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CA8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CBA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CBB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CBE
+  CHARPROP____(0x1ffu, kCombination, kL, kCM)  // U+0CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CC5
+  CHARPROP____(0x1ffu, kCombination, kL, kCM)  // U+0CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CCB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0CCC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0CCD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CCE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CCF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0CD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CDA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CDB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CDC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0CE1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0CE2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0CE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CED
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0CEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0CF1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+0CF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0CFF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D00
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D01
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D02
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D03
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D0C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D10
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D28
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D33
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D3B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D40
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0D41
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0D42
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0D43
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0D44
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D45
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D46
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D47
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D48
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D49
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D4C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0D4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D4F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D50
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D51
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D52
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D53
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D54
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D55
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D56
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D57
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D58
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D5A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D5B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D5C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D61
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0D62
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0D63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D65
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D66
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D67
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D68
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D69
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D78
+  CHARPROP____(0x1ffu, kNormal, kL, kPO)  // U+0D79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D80
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D81
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D82
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0D83
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D96
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D98
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0D99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DBB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DBD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DC7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DC9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0DCA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DCB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DCC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DCD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DCE
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DD1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0DD2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0DD3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0DD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DD5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0DD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DDE
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DDF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DF1
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DF2
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0DF3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0DF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0DFF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E00
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E01
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E02
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E03
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E04
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E05
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E06
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E07
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E08
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E09
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E10
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E11
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E12
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E13
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E14
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E15
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E16
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E17
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E18
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E19
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E20
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E21
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E22
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E23
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E24
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E25
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E26
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E27
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E28
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E29
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E30
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E31
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E32
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E33
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E34
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E35
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E36
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E37
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E38
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E39
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E3B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E3C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E3D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E3E
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+0E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E40
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E41
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E42
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E43
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E44
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E45
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E46
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E47
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E48
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E49
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E4A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E4B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E4C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E4D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E50
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E51
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E52
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E53
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E54
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E55
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E56
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E57
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E58
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0E59
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0E5B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E5C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E5F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E60
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E61
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E62
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E65
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E66
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E67
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E68
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E69
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E6A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E6B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E6C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E6D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E6E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E6F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E70
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E71
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E72
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E73
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E74
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E78
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E79
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E7A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E80
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E81
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E82
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E83
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E84
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E85
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E86
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E87
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E88
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E89
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E8A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E8B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E8D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E8E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E8F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E90
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E91
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E92
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E93
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E94
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E95
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E96
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0E98
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E99
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0E9F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EA3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EA5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EA7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EA8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EAB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EB0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EB3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EB4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EB5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EB6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EB7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EB8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EBA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EBB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EBD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EC7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EC8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0EC9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0ECA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0ECB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0ECC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+0ECD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ECE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0ED9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EDA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+0EDD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EDF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F00
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F01
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F02
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F03
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F05
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F06
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F07
+  CHARPROP____(0x1ffu, kNormal, kL, kGL)  // U+0F08
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F09
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kGL)  // U+0F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kEX)  // U+0F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kEX)  // U+0F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kEX)  // U+0F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kEX)  // U+0F10
+  CHARPROP____(0x1ffu, kNormal, kL, kEX)  // U+0F11
+  CHARPROP____(0x1ffu, kNormal, kL, kGL)  // U+0F12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F13
+  CHARPROP____(0x1ffu, kNormal, kL, kEX)  // U+0F14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F17
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F18
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F20
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F21
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F22
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F23
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F24
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F25
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F26
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F27
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F28
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+0F29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F33
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0F34
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F36
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F38
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F39
+  CHARPROP____(0x00au, kNormal, kON, kOP)  // U+0F3A
+  CHARPROP____(0x00bu, kNormal, kON, kCL)  // U+0F3B
+  CHARPROP____(0x00cu, kNormal, kON, kOP)  // U+0F3C
+  CHARPROP____(0x00du, kNormal, kON, kCL)  // U+0F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+0F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F40
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F41
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F42
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F43
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F45
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F46
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F47
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F50
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F51
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F52
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F53
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F54
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F55
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F56
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F57
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F59
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F66
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F67
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F68
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F69
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F6C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F6D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F6E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F6F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F70
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F71
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F72
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F73
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F74
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F75
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F76
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F77
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F78
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F79
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F7A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F7B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F7C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F7D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0F7F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F80
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F81
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F82
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F83
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F84
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0F85
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F86
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0F8B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F8C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F8D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F8E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F8F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F90
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F91
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F92
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F93
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F94
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F95
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F96
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0F98
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F99
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F9A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F9B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F9C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F9D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F9E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0F9F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FA9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FAA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FAB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FAC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FAD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FAE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FAF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FB9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FBA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FBB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FBC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+0FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FCC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FCD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FCE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+0FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kBB)  // U+0FD3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FD4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+0FD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FDA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FDB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FDC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FDD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FDF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+0FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1000
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1001
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1002
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1003
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1004
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1005
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1006
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1007
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1008
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1009
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+100A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+100B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+100C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+100D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+100E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+100F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1010
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1011
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1012
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1013
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1014
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1015
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1016
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1017
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1018
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1019
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+101A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+101B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+101C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+101D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+101E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+101F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1020
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1021
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1022
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1023
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1024
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1025
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1026
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1027
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1028
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1029
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+102A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+102B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+102C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+102D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+102E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+102F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1030
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1031
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1032
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1033
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1034
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1035
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1036
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1037
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1038
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1039
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+103A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+103B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+103C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+103D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+103E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+103F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1040
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1041
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1042
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1043
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1044
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1045
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1046
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1047
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1048
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1049
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+104A
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+104B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+104C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+104D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+104E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+104F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1050
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1051
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1052
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1053
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1054
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1055
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1056
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1057
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1058
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1059
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+105A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+105B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+105C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+105D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+105E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+105F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1060
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1061
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1062
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1063
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1064
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1065
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1066
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1067
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1068
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1069
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+106A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+106B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+106C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+106D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+106E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+106F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1070
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1071
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1072
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1073
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1074
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1075
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1076
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1077
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1078
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1079
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+107A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+107B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+107C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+107D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+107E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+107F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1080
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1081
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1082
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1083
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1084
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1085
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1086
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1087
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1088
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1089
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+108A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+108B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+108C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+108D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+108E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+108F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1090
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1091
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1092
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1093
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1094
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1095
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1096
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1097
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1098
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1099
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+109A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+109B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+109C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+109D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+109E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+109F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10C5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10C6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10C7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10C8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10CA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10CB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10CC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10CE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+10FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+10FF
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1100
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1101
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1102
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1103
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1104
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1105
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1106
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1107
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1108
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1109
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+110A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+110B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+110C
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+110D
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+110E
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+110F
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1110
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1111
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1112
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1113
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1114
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1115
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1116
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1117
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1118
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1119
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+111A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+111B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+111C
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+111D
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+111E
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+111F
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1120
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1121
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1122
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1123
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1124
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1125
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1126
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1127
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1128
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1129
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+112A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+112B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+112C
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+112D
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+112E
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+112F
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1130
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1131
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1132
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1133
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1134
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1135
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1136
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1137
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1138
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1139
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+113A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+113B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+113C
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+113D
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+113E
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+113F
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1140
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1141
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1142
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1143
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1144
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1145
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1146
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1147
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1148
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1149
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+114A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+114B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+114C
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+114D
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+114E
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+114F
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1150
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1151
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1152
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1153
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1154
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1155
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1156
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1157
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1158
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+1159
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+115A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+115B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+115C
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+115D
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+115E
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+115F
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1160
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1161
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1162
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1163
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1164
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1165
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1166
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1167
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1168
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1169
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+116A
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+116B
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+116C
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+116D
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+116E
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+116F
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1170
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1171
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1172
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1173
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1174
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1175
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1176
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1177
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1178
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1179
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+117A
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+117B
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+117C
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+117D
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+117E
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+117F
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1180
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1181
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1182
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1183
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1184
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1185
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1186
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1187
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1188
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1189
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+118A
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+118B
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+118C
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+118D
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+118E
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+118F
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1190
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1191
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1192
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1193
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1194
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1195
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1196
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1197
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1198
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+1199
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+119A
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+119B
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+119C
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+119D
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+119E
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+119F
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A0
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A1
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A2
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A3
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A4
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A5
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A6
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+11A7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11A8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11A9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11AA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11AB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11AC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11AD
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11AE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11AF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11B9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11BA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11BB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11BC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11BD
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11BE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11BF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11C9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11CA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11CB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11CC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11CD
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11CE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11CF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11D9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11DA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11DB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11DC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11DD
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11DE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11DF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11E9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11EA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11EB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11EC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11ED
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11EE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11EF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11F9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11FA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11FB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11FC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11FD
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11FE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+11FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1200
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1201
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1202
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1203
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1204
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1205
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1206
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1207
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1208
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1209
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+120A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+120B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+120C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+120D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+120E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+120F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1210
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1211
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1212
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1213
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1214
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1215
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1216
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1217
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1218
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1219
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+121A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+121B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+121C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+121D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+121E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+121F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1220
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1221
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1222
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1223
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1224
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1225
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1226
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1227
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1228
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1229
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+122A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+122B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+122C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+122D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+122E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+122F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1230
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1231
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1232
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1233
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1234
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1235
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1236
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1237
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1238
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1239
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+123A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+123B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+123C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+123D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+123E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+123F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1240
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1241
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1242
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1243
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1244
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1245
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1246
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1247
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1248
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1249
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+124A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+124B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+124C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+124D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+124E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+124F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1250
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1251
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1252
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1253
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1254
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1255
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1256
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1257
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1258
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1259
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+125A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+125B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+125C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+125D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+125E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+125F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1260
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1261
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1262
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1263
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1264
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1265
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1266
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1267
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1268
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1269
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+126A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+126B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+126C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+126D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+126E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+126F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1270
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1271
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1272
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1273
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1274
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1275
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1276
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1277
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1278
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1279
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+127A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+127B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+127C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+127D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+127E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+127F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1280
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1281
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1282
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1283
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1284
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1285
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1286
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1287
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1288
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1289
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+128A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+128B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+128C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+128D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+128E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+128F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1290
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1291
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1292
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1293
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1294
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1295
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1296
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1297
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1298
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1299
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+129A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+129B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+129C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+129D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+129E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+129F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12B0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12B5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12B6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12BE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12C0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12C5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12C6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+12D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+12FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1300
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1301
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1302
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1303
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1304
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1305
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1306
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1307
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1308
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1309
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+130A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+130B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+130C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+130D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+130E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+130F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1310
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1311
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1312
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1313
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1314
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1315
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1316
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1317
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1318
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1319
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+131A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+131B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+131C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+131D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+131E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+131F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1320
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1321
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1322
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1323
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1324
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1325
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1326
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1327
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1328
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1329
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+132A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+132B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+132C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+132D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+132E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+132F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1330
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1331
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1332
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1333
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1334
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1335
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1336
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1337
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1338
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1339
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+133A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+133B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+133C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+133D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+133E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+133F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1340
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1341
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1342
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1343
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1344
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1345
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1346
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1347
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1348
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1349
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+134A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+134B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+134C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+134D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+134E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+134F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1350
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1351
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1352
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1353
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1354
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1355
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1356
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1357
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1358
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1359
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+135A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+135B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+135C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+135D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+135E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+135F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1360
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1361
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1362
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1363
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1364
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1365
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1366
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1367
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1368
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1369
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+136A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+136B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+136C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+136D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+136E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+136F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1370
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1371
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1372
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1373
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1374
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1375
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1376
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1377
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1378
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1379
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+137A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+137B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+137C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+137D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+137E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+137F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1380
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1381
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1382
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1383
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1384
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1385
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1386
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1387
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1388
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1389
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+138A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+138B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+138C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+138D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+138E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+138F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1390
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1391
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1392
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1393
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1394
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1395
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1396
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1397
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1398
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1399
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+139A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+139B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+139C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+139D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+139E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+139F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+13F4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13F5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13F6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+13FF
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+1400
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1401
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1402
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1403
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1404
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1405
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1406
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1407
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1408
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1409
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+140A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+140B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+140C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+140D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+140E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+140F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1410
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1411
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1412
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1413
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1414
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1415
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1416
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1417
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1418
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1419
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+141A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+141B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+141C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+141D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+141E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+141F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1420
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1421
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1422
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1423
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1424
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1425
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1426
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1427
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1428
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1429
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+142A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+142B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+142C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+142D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+142E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+142F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1430
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1431
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1432
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1433
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1434
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1435
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1436
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1437
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1438
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1439
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+143A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+143B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+143C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+143D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+143E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+143F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1440
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1441
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1442
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1443
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1444
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1445
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1446
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1447
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1448
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1449
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+144A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+144B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+144C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+144D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+144E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+144F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1450
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1451
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1452
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1453
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1454
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1455
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1456
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1457
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1458
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1459
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+145A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+145B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+145C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+145D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+145E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+145F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1460
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1461
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1462
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1463
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1464
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1465
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1466
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1467
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1468
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1469
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+146A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+146B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+146C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+146D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+146E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+146F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1470
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1471
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1472
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1473
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1474
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1475
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1476
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1477
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1478
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1479
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+147A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+147B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+147C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+147D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+147E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+147F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1480
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1481
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1482
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1483
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1484
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1485
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1486
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1487
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1488
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1489
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+148A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+148B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+148C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+148D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+148E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+148F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1490
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1491
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1492
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1493
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1494
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1495
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1496
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1497
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1498
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1499
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+149A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+149B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+149C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+149D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+149E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+149F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+14FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1500
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1501
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1502
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1503
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1504
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1505
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1506
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1507
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1508
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1509
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+150A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+150B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+150C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+150D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+150E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+150F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1510
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1511
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1512
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1513
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1514
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1515
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1516
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1517
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1518
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1519
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+151A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+151B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+151C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+151D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+151E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+151F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1520
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1521
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1522
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1523
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1524
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1525
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1526
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1527
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1528
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1529
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+152A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+152B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+152C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+152D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+152E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+152F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1530
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1531
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1532
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1533
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1534
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1535
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1536
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1537
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1538
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1539
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+153A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+153B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+153C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+153D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+153E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+153F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1540
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1541
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1542
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1543
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1544
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1545
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1546
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1547
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1548
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1549
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+154A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+154B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+154C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+154D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+154E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+154F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1550
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1551
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1552
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1553
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1554
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1555
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1556
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1557
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1558
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1559
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+155A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+155B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+155C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+155D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+155E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+155F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1560
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1561
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1562
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1563
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1564
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1565
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1566
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1567
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1568
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1569
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+156A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+156B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+156C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+156D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+156E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+156F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1570
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1571
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1572
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1573
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1574
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1575
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1576
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1577
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1578
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1579
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+157A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+157B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+157C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+157D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+157E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+157F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1580
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1581
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1582
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1583
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1584
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1585
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1586
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1587
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1588
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1589
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+158A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+158B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+158C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+158D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+158E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+158F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1590
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1591
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1592
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1593
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1594
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1595
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1596
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1597
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1598
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1599
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+159A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+159B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+159C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+159D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+159E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+159F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+15FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1600
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1601
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1602
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1603
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1604
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1605
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1606
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1607
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1608
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1609
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+160A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+160B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+160C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+160D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+160E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+160F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1610
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1611
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1612
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1613
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1614
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1615
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1616
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1617
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1618
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1619
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+161A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+161B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+161C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+161D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+161E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+161F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1620
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1621
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1622
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1623
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1624
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1625
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1626
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1627
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1628
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1629
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+162A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+162B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+162C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+162D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+162E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+162F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1630
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1631
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1632
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1633
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1634
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1635
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1636
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1637
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1638
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1639
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+163A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+163B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+163C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+163D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+163E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+163F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1640
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1641
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1642
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1643
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1644
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1645
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1646
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1647
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1648
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1649
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+164A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+164B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+164C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+164D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+164E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+164F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1650
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1651
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1652
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1653
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1654
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1655
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1656
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1657
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1658
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1659
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+165A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+165B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+165C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+165D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+165E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+165F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1660
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1661
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1662
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1663
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1664
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1665
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1666
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1667
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1668
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1669
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+166A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+166B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+166C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+166D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+166E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+166F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1670
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1671
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1672
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1673
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1674
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1675
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1676
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1677
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1678
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1679
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+167A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+167B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+167C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+167D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+167E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+167F
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+1680
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1681
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1682
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1683
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1684
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1685
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1686
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1687
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1688
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1689
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+168A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+168B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+168C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+168D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+168E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+168F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1690
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1691
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1692
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1693
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1694
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1695
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1696
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1697
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1698
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1699
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+169A
+  CHARPROP____(0x00eu, kNormal, kON, kOP)  // U+169B
+  CHARPROP____(0x00fu, kNormal, kON, kCL)  // U+169C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+169D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+169E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+169F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16EA
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+16EB
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+16EC
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+16ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+16F0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+16FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1700
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1701
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1702
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1703
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1704
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1705
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1706
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1707
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1708
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1709
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+170A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+170B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+170C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+170D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+170E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+170F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1710
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1711
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1712
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1713
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1714
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1715
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1716
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1717
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1718
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1719
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+171A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+171B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+171C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+171D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+171E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+171F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1720
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1721
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1722
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1723
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1724
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1725
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1726
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1727
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1728
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1729
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+172A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+172B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+172C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+172D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+172E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+172F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1730
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1731
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1732
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1733
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1734
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1735
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1736
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1737
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1738
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1739
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+173A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+173B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+173C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+173D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+173E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+173F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1740
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1741
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1742
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1743
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1744
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1745
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1746
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1747
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1748
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1749
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+174A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+174B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+174C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+174D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+174E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+174F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1750
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1751
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1752
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1753
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1754
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1755
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1756
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1757
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1758
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1759
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+175A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+175B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+175C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+175D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+175E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+175F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1760
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1761
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1762
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1763
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1764
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1765
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1766
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1767
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1768
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1769
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+176A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+176B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+176C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+176D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+176E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+176F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1770
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1771
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1772
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1773
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1774
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1775
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1776
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1777
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1778
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1779
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+177A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+177B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+177C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+177D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+177E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+177F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1780
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1781
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1782
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1783
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1784
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1785
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1786
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1787
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1788
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1789
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+178A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+178B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+178C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+178D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+178E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+178F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1790
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1791
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1792
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1793
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1794
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1795
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1796
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1797
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1798
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1799
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+179A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+179B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+179C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+179D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+179E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+179F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A7
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A8
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17A9
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17AA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17AB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17AC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17AD
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17AE
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17AF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17B0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17B1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17B2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17B3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17B4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17B5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17B6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17B7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17B8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17B9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17BA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17BB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17BC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17BD
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17BE
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17BF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17C6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C7
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17C8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17C9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17CA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17CB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17CC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17CD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17CE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17CF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17D0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17D1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17D2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17D3
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+17D4
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+17D5
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+17D6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17D7
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+17D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+17D9
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+17DA
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+17DB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+17DC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+17DD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17DE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17DF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E0
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E1
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E2
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E3
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E4
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+17E9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17EA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17EB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17EC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17ED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17EE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+17F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+17FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1800
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1801
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+1802
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+1803
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+1804
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+1805
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+1806
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1807
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+1808
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+1809
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+180A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+180B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+180C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+180D
+  CHARPROP____(0x1ffu, kNormal, kWS, kGL)  // U+180E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+180F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1810
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1811
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1812
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1813
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1814
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1815
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1816
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1817
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1818
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1819
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+181A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+181B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+181C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+181D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+181E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+181F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1820
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1821
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1822
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1823
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1824
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1825
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1826
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1827
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1828
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1829
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+182A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+182B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+182C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+182D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+182E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+182F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1830
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1831
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1832
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1833
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1834
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1835
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1836
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1837
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1838
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1839
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+183A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+183B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+183C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+183D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+183E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+183F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1840
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1841
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1842
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1843
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1844
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1845
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1846
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1847
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1848
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1849
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+184A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+184B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+184C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+184D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+184E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+184F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1850
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1851
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1852
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1853
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1854
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1855
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1856
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1857
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1858
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1859
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+185A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+185B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+185C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+185D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+185E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+185F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1860
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1861
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1862
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1863
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1864
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1865
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1866
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1867
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1868
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1869
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+186A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+186B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+186C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+186D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+186E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+186F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1870
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1871
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1872
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1873
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1874
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1875
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1876
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1877
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1878
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1879
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+187A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+187B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+187C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+187D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+187E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+187F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1880
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1881
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1882
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1883
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1884
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1885
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1886
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1887
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1888
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1889
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+188A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+188B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+188C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+188D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+188E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+188F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1890
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1891
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1892
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1893
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1894
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1895
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1896
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1897
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1898
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1899
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+189A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+189B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+189C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+189D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+189E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+189F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18A8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+18A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18AA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18AB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18AC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18AD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18AE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+18F5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18F6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+18FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1900
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1901
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1902
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1903
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1904
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1905
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1906
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1907
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1908
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1909
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+190A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+190B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+190C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+190D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+190E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+190F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1910
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1911
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1912
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1913
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1914
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1915
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1916
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1917
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1918
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1919
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+191A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+191B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+191C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+191D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+191E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+191F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1920
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1921
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1922
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1923
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1924
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1925
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1926
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1927
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1928
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1929
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+192A
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+192B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+192C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+192D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+192E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+192F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1930
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1931
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1932
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1933
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1934
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1935
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1936
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1937
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1938
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1939
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+193A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+193B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+193C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+193D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+193E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+193F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1940
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1941
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1942
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1943
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+1944
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+1945
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1946
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1947
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1948
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1949
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+194A
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+194B
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+194C
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+194D
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+194E
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+194F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1950
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1951
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1952
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1953
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1954
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1955
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1956
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1957
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1958
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1959
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+195A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+195B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+195C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+195D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+195E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+195F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1960
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1961
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1962
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1963
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1964
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1965
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1966
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1967
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1968
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1969
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+196A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+196B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+196C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+196D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+196E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+196F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1970
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1971
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1972
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1973
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1974
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1975
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1976
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1977
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1978
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1979
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+197A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+197B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+197C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+197D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+197E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+197F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1980
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1981
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1982
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1983
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1984
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1985
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1986
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1987
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1988
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1989
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+198A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+198B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+198C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+198D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+198E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+198F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1990
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1991
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1992
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1993
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1994
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1995
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1996
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1997
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1998
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1999
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+199A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+199B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+199C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+199D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+199E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+199F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A7
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A8
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19A9
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19AA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19AB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19AC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19AD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19AE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19AF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B7
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B8
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19B9
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19BA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19BB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19BC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19BD
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19BE
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19BF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C7
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C8
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+19C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19CA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19CB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19CC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19CE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19CF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D0
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D1
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D2
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D3
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D4
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19D9
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+19DA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19DB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19DC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+19DD
+  CHARPROP____(0x1ffu, kNormal, kON, kSA)  // U+19DE
+  CHARPROP____(0x1ffu, kNormal, kON, kSA)  // U+19DF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19E9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19EA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19EB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19EC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19ED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19FB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19FC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+19FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A16
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1A17
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1A18
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1A19
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1A1B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A1C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A20
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A21
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A22
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A23
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A24
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A25
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A26
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A27
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A28
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A29
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A30
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A31
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A32
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A33
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A34
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A35
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A36
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A37
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A38
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A39
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A40
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A41
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A42
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A43
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A44
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A45
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A46
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A47
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A48
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A49
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A50
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A51
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A52
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A53
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A54
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A55
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A56
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A57
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A58
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A59
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A5A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A5B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A5C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A5D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A5F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A60
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A61
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A62
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A63
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A64
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A65
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A66
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A67
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A68
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A69
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A6A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A6B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A70
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A71
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1A72
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A73
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A74
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A75
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A76
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A77
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A78
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A79
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A7A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A7B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+1A7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A7E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A80
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A81
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A82
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A83
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A84
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A85
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A86
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A87
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A88
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A89
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A8A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A8B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A8C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A8D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A8E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A90
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A91
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A92
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A93
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A94
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A95
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A96
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A97
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A98
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1A99
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A9A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A9B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A9C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A9D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A9E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+1AAD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AAE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AAF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ABA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ABB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ABC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ABD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ABE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ABF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AC9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ACA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ACB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ACC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ACD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ACE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ACF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ADA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ADB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ADC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ADD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ADE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1ADF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1AFF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B00
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B01
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B02
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B03
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B28
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B33
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B34
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B35
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B36
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B37
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B38
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B39
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B3B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B40
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B41
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B42
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B43
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B45
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B46
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B47
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B4B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1B4C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1B4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1B4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B50
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B51
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B52
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B53
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B54
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B55
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B56
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B57
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B58
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1B59
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1B60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B66
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B67
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B68
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B69
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B6A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B6B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B6C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B6D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B6E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B6F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B70
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B71
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B72
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B75
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B76
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B77
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B78
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1B7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1B7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1B7F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B80
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1B81
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1B82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B83
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1BA1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1BA2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1BA3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1BA4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1BA7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1BA8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1BAA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BAB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BAC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1BB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BBA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BBB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BBC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BBD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BBF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BC9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BCA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BCB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BCC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BCD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BCE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BCF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BDA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BDB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BDC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BDD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BDF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C23
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C24
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C25
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C26
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C27
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C28
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C29
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C2B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C2C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C2D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C2E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C2F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C30
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C31
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C32
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C33
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C34
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1C35
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C36
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1C37
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C38
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C40
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C41
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C42
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C43
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C44
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C45
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C46
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C47
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C48
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C49
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C4A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C4B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C50
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C51
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C52
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C53
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C54
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C55
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C56
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C57
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C58
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+1C59
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C66
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C67
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C68
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C69
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C75
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C76
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C77
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C78
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+1C7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C80
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C81
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C82
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C83
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C84
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C85
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C86
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C87
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C88
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C89
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C8A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C8B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C8C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C8D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C8E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C8F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C90
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C91
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C92
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C93
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C94
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C95
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C96
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C98
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C99
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C9A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C9B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C9C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C9D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C9E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1C9F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CA9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CAA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CAB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CAC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CAD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CAE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CAF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CBA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CBB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CBC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CBD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CBF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CC9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CCA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CCB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CCC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CCD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CCE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CCF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CD3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CD9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CDA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CDB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CDC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CDD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CDE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CDF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1CE1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CEC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1CED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+1CF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D28
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D33
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D39
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D40
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D41
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D42
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D43
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D45
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D46
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D47
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D50
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D51
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D52
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D53
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D54
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D55
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D56
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D57
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D59
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D66
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D67
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D68
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D69
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D75
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D76
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D77
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D78
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D80
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D81
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D83
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DBE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1DBF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DC9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DCA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DCB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DCC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DCD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DCE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DCF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DD9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DDA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DDB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DDC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DDD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DDE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DDF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DE0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DE1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DE2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DE3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DE4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DE5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1DFC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DFD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DFE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+1DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E28
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E33
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E39
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E40
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E41
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E42
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E43
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E45
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E46
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E47
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E50
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E51
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E52
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E53
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E54
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E55
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E56
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E57
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E59
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E66
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E67
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E68
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E69
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E75
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E76
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E77
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E78
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E80
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E81
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E83
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F15
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F16
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F1D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F1E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F28
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F33
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F39
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F40
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F41
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F42
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F43
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F45
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F46
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F47
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F50
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F51
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F52
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F53
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F54
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F55
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F56
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F57
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F5B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F66
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F67
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F68
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F69
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F75
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F76
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F77
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F78
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F80
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F81
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F83
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FBC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FBE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FBF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FC0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FCC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FCD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FCE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FD9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FDA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FDB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FDC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FDD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FDE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FDF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FE9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FEA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FEB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FEC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FEE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FF1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FF2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FF3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FF5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FF6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FF7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FF8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FF9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FFA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FFB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+1FFC
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+1FFD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+1FFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+1FFF
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2000
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2001
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2002
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2003
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2004
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2005
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2006
+  CHARPROP____(0x1ffu, kNormal, kWS, kGL)  // U+2007
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2008
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+2009
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+200A
+  CHARPROP____(0x1ffu, kNormal, kBN, kZW)  // U+200B
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+200C
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+200D
+  CHARPROP____(0x1ffu, kControl, kL, kCM)  // U+200E
+  CHARPROP____(0x1ffu, kControl, kR, kCM)  // U+200F
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2010
+  CHARPROP____(0x1ffu, kNormal, kON, kGL)  // U+2011
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2012
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2013
+  CHARPROP____(0x1ffu, kNormal, kON, kB2)  // U+2014
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2015
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2016
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2017
+  CHARPROP____(0x010u, kNormal, kON, kOP)  // U+2018
+  CHARPROP____(0x011u, kNormal, kON, kQU)  // U+2019
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+201A
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+201B
+  CHARPROP____(0x012u, kNormal, kON, kOP)  // U+201C
+  CHARPROP____(0x013u, kNormal, kON, kCL)  // U+201D
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+201E
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+201F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2020
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2021
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2022
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2023
+  CHARPROP____(0x1ffu, kNormal, kON, kIN)  // U+2024
+  CHARPROP____(0x1ffu, kNormal, kON, kIN)  // U+2025
+  CHARPROP____(0x1ffu, kNormal, kON, kIN)  // U+2026
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2027
+  CHARPROP____(0x1ffu, kControl, kWS, kBK)  // U+2028
+  CHARPROP____(0x1ffu, kControl, kB, kBK)  // U+2029
+  CHARPROP____(0x1ffu, kControl, kLRE, kCM)  // U+202A
+  CHARPROP____(0x1ffu, kControl, kRLE, kCM)  // U+202B
+  CHARPROP____(0x1ffu, kControl, kPDF, kCM)  // U+202C
+  CHARPROP____(0x1ffu, kControl, kLRO, kCM)  // U+202D
+  CHARPROP____(0x1ffu, kControl, kRLO, kCM)  // U+202E
+  CHARPROP____(0x1ffu, kNormal, kCS, kGL)  // U+202F
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+2030
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+2031
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+2032
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+2033
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+2034
+  CHARPROP____(0x1ffu, kNormal, kON, kPO)  // U+2035
+  CHARPROP____(0x1ffu, kNormal, kON, kPO)  // U+2036
+  CHARPROP____(0x1ffu, kNormal, kON, kPO)  // U+2037
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2038
+  CHARPROP____(0x014u, kNormal, kON, kOP)  // U+2039
+  CHARPROP____(0x015u, kNormal, kON, kCL)  // U+203A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+203B
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+203C
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+203D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+203E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+203F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2040
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2041
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2042
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2043
+  CHARPROP____(0x1ffu, kNormal, kCS, kIS)  // U+2044
+  CHARPROP____(0x016u, kNormal, kON, kOP)  // U+2045
+  CHARPROP____(0x017u, kNormal, kON, kCL)  // U+2046
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+2047
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+2048
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+2049
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+204A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+204B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+204C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+204D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+204E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+204F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2050
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2051
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2052
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2053
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2054
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2055
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2056
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2057
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2058
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2059
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+205A
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+205B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+205C
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+205D
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+205E
+  CHARPROP____(0x1ffu, kNormal, kWS, kBA)  // U+205F
+  CHARPROP____(0x1ffu, kNormal, kBN, kWJ)  // U+2060
+  CHARPROP____(0x1ffu, kNormal, kBN, kAL)  // U+2061
+  CHARPROP____(0x1ffu, kNormal, kBN, kAL)  // U+2062
+  CHARPROP____(0x1ffu, kNormal, kBN, kAL)  // U+2063
+  CHARPROP____(0x1ffu, kNormal, kBN, kAL)  // U+2064
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+2065
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+2066
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+2067
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+2068
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+2069
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+206A
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+206B
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+206C
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+206D
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+206E
+  CHARPROP____(0x1ffu, kControl, kBN, kCM)  // U+206F
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2070
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2071
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2072
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2073
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2074
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2075
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2076
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2077
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2078
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2079
+  CHARPROP____(0x1ffu, kNormal, kES, kAL)  // U+207A
+  CHARPROP____(0x1ffu, kNormal, kES, kAL)  // U+207B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+207C
+  CHARPROP____(0x018u, kNormal, kON, kOP)  // U+207D
+  CHARPROP____(0x019u, kNormal, kON, kCL)  // U+207E
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+207F
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2080
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2081
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2082
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2083
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2084
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2085
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2086
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2087
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2088
+  CHARPROP____(0x1ffu, kNormal, kEN, kAL)  // U+2089
+  CHARPROP____(0x1ffu, kNormal, kES, kAL)  // U+208A
+  CHARPROP____(0x1ffu, kNormal, kES, kAL)  // U+208B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+208C
+  CHARPROP____(0x01au, kNormal, kON, kOP)  // U+208D
+  CHARPROP____(0x01bu, kNormal, kON, kCL)  // U+208E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+208F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2090
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2091
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2092
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2093
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2094
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2095
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2096
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2097
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2098
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2099
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+209A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+209B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+209C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+209D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+209E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+209F
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A0
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A1
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A2
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A3
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A4
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A5
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A6
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+20A7
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A8
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20A9
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20AA
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20AB
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20AC
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20AD
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20AE
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20AF
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B0
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B1
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B2
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B3
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B4
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B5
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+20B6
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B7
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+20B8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20B9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20BA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20BB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20BC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20BD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20BE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20BF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20CA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20CB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20CC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20CE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20CF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20D9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20DA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20DB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20DC
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+20DD
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+20DE
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+20DF
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+20E0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20E1
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+20E2
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+20E3
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+20E4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20E5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20E6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20E7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20E8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20E9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20EA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20EB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20EC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20ED
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20EE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20EF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+20F0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+20FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2100
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2101
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2102
+  CHARPROP____(0x1ffu, kNormal, kON, kPO)  // U+2103
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2104
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2105
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2106
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2107
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2108
+  CHARPROP____(0x1ffu, kNormal, kON, kPO)  // U+2109
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+210A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+210B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+210C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+210D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+210E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+210F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2110
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2111
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2112
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2113
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2114
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2115
+  CHARPROP____(0x1ffu, kNormal, kON, kPR)  // U+2116
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2117
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2118
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2119
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+211A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+211B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+211C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+211D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+211E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+211F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2120
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2121
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2122
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2123
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2124
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2125
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2126
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2127
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2128
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2129
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+212A
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+212B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+212C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+212D
+  CHARPROP____(0x1ffu, kNormal, kET, kAL)  // U+212E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+212F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2130
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2131
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2132
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2133
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2134
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2135
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2136
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2137
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2138
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2139
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+213A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+213B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+213C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+213D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+213E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+213F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2140
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2141
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2142
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2143
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2144
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2145
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2146
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2147
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2148
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2149
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+214A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+214B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+214C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+214D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+214E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+214F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2150
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2151
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2152
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2153
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2154
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2155
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2156
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2157
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2158
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2159
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+215A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+215B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+215C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+215D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+215E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+215F
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2160
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2161
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2162
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2163
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2164
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2165
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2166
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2167
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2168
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2169
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+216A
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+216B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+216C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+216D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+216E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+216F
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2170
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2171
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2172
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2173
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2174
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2175
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2176
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2177
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2178
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+2179
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+217A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+217B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+217C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+217D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+217E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+217F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2180
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2181
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2182
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2183
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2184
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2185
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2186
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2187
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2188
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2189
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+218A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+218B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+218C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+218D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+218E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+218F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2190
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2191
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2192
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2193
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2194
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2195
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2196
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2197
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2198
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2199
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+219A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+219B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+219C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+219D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+219E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+219F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21A9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21AA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21AB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21AC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21AF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21BA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21BE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21BF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21C9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21CA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21CB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21CC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21CD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21CE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21CF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D1
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+21D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D3
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+21D4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21D9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21DA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21DB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21DC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21DD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21DE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21DF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21E9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21EA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21EB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21EC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21ED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21FB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21FC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+21FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2200
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2201
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2202
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2203
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2204
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2205
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2206
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2207
+  CHARPROP____(0x01cu, kNormal, kON, kAI)  // U+2208
+  CHARPROP____(0x01du, kNormal, kON, kAL)  // U+2209
+  CHARPROP____(0x01eu, kNormal, kON, kAL)  // U+220A
+  CHARPROP____(0x01fu, kNormal, kON, kAI)  // U+220B
+  CHARPROP____(0x020u, kNormal, kON, kAL)  // U+220C
+  CHARPROP____(0x021u, kNormal, kON, kAL)  // U+220D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+220E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+220F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2210
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2211
+  CHARPROP____(0x1ffu, kNormal, kES, kPR)  // U+2212
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+2213
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2214
+  CHARPROP____(0x022u, kNormal, kON, kAI)  // U+2215
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2216
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2217
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2218
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2219
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+221A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+221B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+221C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+221D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+221E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+221F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2220
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2221
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2222
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2223
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2224
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2225
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2226
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2227
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2228
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2229
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+222A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+222B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+222C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+222D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+222E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+222F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2230
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2231
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2232
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2233
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2234
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2235
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2236
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2237
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2238
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2239
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+223A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+223B
+  CHARPROP____(0x023u, kNormal, kON, kAI)  // U+223C
+  CHARPROP____(0x024u, kNormal, kON, kAI)  // U+223D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+223E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+223F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2240
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2241
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2242
+  CHARPROP____(0x025u, kNormal, kON, kAL)  // U+2243
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2244
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2245
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2246
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2247
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2248
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2249
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+224A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+224B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+224C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+224D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+224E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+224F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2250
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2251
+  CHARPROP____(0x026u, kNormal, kON, kAI)  // U+2252
+  CHARPROP____(0x027u, kNormal, kON, kAL)  // U+2253
+  CHARPROP____(0x028u, kNormal, kON, kAL)  // U+2254
+  CHARPROP____(0x029u, kNormal, kON, kAL)  // U+2255
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2256
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2257
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2258
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2259
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+225A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+225B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+225C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+225D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+225E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+225F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2260
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2261
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2262
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2263
+  CHARPROP____(0x02au, kNormal, kON, kAI)  // U+2264
+  CHARPROP____(0x02bu, kNormal, kON, kAI)  // U+2265
+  CHARPROP____(0x02cu, kNormal, kON, kAI)  // U+2266
+  CHARPROP____(0x02du, kNormal, kON, kAI)  // U+2267
+  CHARPROP____(0x02eu, kNormal, kON, kAL)  // U+2268
+  CHARPROP____(0x02fu, kNormal, kON, kAL)  // U+2269
+  CHARPROP____(0x030u, kNormal, kON, kAI)  // U+226A
+  CHARPROP____(0x031u, kNormal, kON, kAI)  // U+226B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+226C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+226D
+  CHARPROP____(0x032u, kNormal, kON, kAI)  // U+226E
+  CHARPROP____(0x033u, kNormal, kON, kAI)  // U+226F
+  CHARPROP____(0x034u, kNormal, kON, kAL)  // U+2270
+  CHARPROP____(0x035u, kNormal, kON, kAL)  // U+2271
+  CHARPROP____(0x036u, kNormal, kON, kAL)  // U+2272
+  CHARPROP____(0x037u, kNormal, kON, kAL)  // U+2273
+  CHARPROP____(0x038u, kNormal, kON, kAL)  // U+2274
+  CHARPROP____(0x039u, kNormal, kON, kAL)  // U+2275
+  CHARPROP____(0x03au, kNormal, kON, kAL)  // U+2276
+  CHARPROP____(0x03bu, kNormal, kON, kAL)  // U+2277
+  CHARPROP____(0x03cu, kNormal, kON, kAL)  // U+2278
+  CHARPROP____(0x03du, kNormal, kON, kAL)  // U+2279
+  CHARPROP____(0x03eu, kNormal, kON, kAL)  // U+227A
+  CHARPROP____(0x03fu, kNormal, kON, kAL)  // U+227B
+  CHARPROP____(0x040u, kNormal, kON, kAL)  // U+227C
+  CHARPROP____(0x041u, kNormal, kON, kAL)  // U+227D
+  CHARPROP____(0x042u, kNormal, kON, kAL)  // U+227E
+  CHARPROP____(0x043u, kNormal, kON, kAL)  // U+227F
+  CHARPROP____(0x044u, kNormal, kON, kAL)  // U+2280
+  CHARPROP____(0x045u, kNormal, kON, kAL)  // U+2281
+  CHARPROP____(0x046u, kNormal, kON, kAI)  // U+2282
+  CHARPROP____(0x047u, kNormal, kON, kAI)  // U+2283
+  CHARPROP____(0x048u, kNormal, kON, kAL)  // U+2284
+  CHARPROP____(0x049u, kNormal, kON, kAL)  // U+2285
+  CHARPROP____(0x04au, kNormal, kON, kAI)  // U+2286
+  CHARPROP____(0x04bu, kNormal, kON, kAI)  // U+2287
+  CHARPROP____(0x04cu, kNormal, kON, kAL)  // U+2288
+  CHARPROP____(0x04du, kNormal, kON, kAL)  // U+2289
+  CHARPROP____(0x04eu, kNormal, kON, kAL)  // U+228A
+  CHARPROP____(0x04fu, kNormal, kON, kAL)  // U+228B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+228C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+228D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+228E
+  CHARPROP____(0x050u, kNormal, kON, kAL)  // U+228F
+  CHARPROP____(0x051u, kNormal, kON, kAL)  // U+2290
+  CHARPROP____(0x052u, kNormal, kON, kAL)  // U+2291
+  CHARPROP____(0x053u, kNormal, kON, kAL)  // U+2292
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2293
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2294
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2295
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2296
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2297
+  CHARPROP____(0x054u, kNormal, kON, kAL)  // U+2298
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2299
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+229A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+229B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+229C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+229D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+229E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+229F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22A0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22A1
+  CHARPROP____(0x055u, kNormal, kON, kAL)  // U+22A2
+  CHARPROP____(0x056u, kNormal, kON, kAL)  // U+22A3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22A4
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+22A5
+  CHARPROP____(0x057u, kNormal, kON, kAL)  // U+22A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22A7
+  CHARPROP____(0x058u, kNormal, kON, kAL)  // U+22A8
+  CHARPROP____(0x059u, kNormal, kON, kAL)  // U+22A9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22AA
+  CHARPROP____(0x05au, kNormal, kON, kAL)  // U+22AB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22AC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22AF
+  CHARPROP____(0x05bu, kNormal, kON, kAL)  // U+22B0
+  CHARPROP____(0x05cu, kNormal, kON, kAL)  // U+22B1
+  CHARPROP____(0x05du, kNormal, kON, kAL)  // U+22B2
+  CHARPROP____(0x05eu, kNormal, kON, kAL)  // U+22B3
+  CHARPROP____(0x05fu, kNormal, kON, kAL)  // U+22B4
+  CHARPROP____(0x060u, kNormal, kON, kAL)  // U+22B5
+  CHARPROP____(0x061u, kNormal, kON, kAL)  // U+22B6
+  CHARPROP____(0x062u, kNormal, kON, kAL)  // U+22B7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22BA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22BE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+22BF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22C8
+  CHARPROP____(0x063u, kNormal, kON, kAL)  // U+22C9
+  CHARPROP____(0x064u, kNormal, kON, kAL)  // U+22CA
+  CHARPROP____(0x065u, kNormal, kON, kAL)  // U+22CB
+  CHARPROP____(0x066u, kNormal, kON, kAL)  // U+22CC
+  CHARPROP____(0x067u, kNormal, kON, kAL)  // U+22CD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22CE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22CF
+  CHARPROP____(0x068u, kNormal, kON, kAL)  // U+22D0
+  CHARPROP____(0x069u, kNormal, kON, kAL)  // U+22D1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22D3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22D4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22D5
+  CHARPROP____(0x06au, kNormal, kON, kAL)  // U+22D6
+  CHARPROP____(0x06bu, kNormal, kON, kAL)  // U+22D7
+  CHARPROP____(0x06cu, kNormal, kON, kAL)  // U+22D8
+  CHARPROP____(0x06du, kNormal, kON, kAL)  // U+22D9
+  CHARPROP____(0x06eu, kNormal, kON, kAL)  // U+22DA
+  CHARPROP____(0x06fu, kNormal, kON, kAL)  // U+22DB
+  CHARPROP____(0x070u, kNormal, kON, kAL)  // U+22DC
+  CHARPROP____(0x071u, kNormal, kON, kAL)  // U+22DD
+  CHARPROP____(0x072u, kNormal, kON, kAL)  // U+22DE
+  CHARPROP____(0x073u, kNormal, kON, kAL)  // U+22DF
+  CHARPROP____(0x074u, kNormal, kON, kAL)  // U+22E0
+  CHARPROP____(0x075u, kNormal, kON, kAL)  // U+22E1
+  CHARPROP____(0x076u, kNormal, kON, kAL)  // U+22E2
+  CHARPROP____(0x077u, kNormal, kON, kAL)  // U+22E3
+  CHARPROP____(0x078u, kNormal, kON, kAL)  // U+22E4
+  CHARPROP____(0x079u, kNormal, kON, kAL)  // U+22E5
+  CHARPROP____(0x07au, kNormal, kON, kAL)  // U+22E6
+  CHARPROP____(0x07bu, kNormal, kON, kAL)  // U+22E7
+  CHARPROP____(0x07cu, kNormal, kON, kAL)  // U+22E8
+  CHARPROP____(0x07du, kNormal, kON, kAL)  // U+22E9
+  CHARPROP____(0x07eu, kNormal, kON, kAL)  // U+22EA
+  CHARPROP____(0x07fu, kNormal, kON, kAL)  // U+22EB
+  CHARPROP____(0x080u, kNormal, kON, kAL)  // U+22EC
+  CHARPROP____(0x081u, kNormal, kON, kAL)  // U+22ED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22EF
+  CHARPROP____(0x082u, kNormal, kON, kAL)  // U+22F0
+  CHARPROP____(0x083u, kNormal, kON, kAL)  // U+22F1
+  CHARPROP____(0x084u, kNormal, kON, kAL)  // U+22F2
+  CHARPROP____(0x085u, kNormal, kON, kAL)  // U+22F3
+  CHARPROP____(0x086u, kNormal, kON, kAL)  // U+22F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22F5
+  CHARPROP____(0x087u, kNormal, kON, kAL)  // U+22F6
+  CHARPROP____(0x088u, kNormal, kON, kAL)  // U+22F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22F9
+  CHARPROP____(0x089u, kNormal, kON, kAL)  // U+22FA
+  CHARPROP____(0x08au, kNormal, kON, kAL)  // U+22FB
+  CHARPROP____(0x08bu, kNormal, kON, kAL)  // U+22FC
+  CHARPROP____(0x08cu, kNormal, kON, kAL)  // U+22FD
+  CHARPROP____(0x08du, kNormal, kON, kAL)  // U+22FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+22FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2300
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2301
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2302
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2303
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2304
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2305
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2306
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2307
+  CHARPROP____(0x08eu, kNormal, kON, kAL)  // U+2308
+  CHARPROP____(0x08fu, kNormal, kON, kAL)  // U+2309
+  CHARPROP____(0x090u, kNormal, kON, kAL)  // U+230A
+  CHARPROP____(0x091u, kNormal, kON, kAL)  // U+230B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+230C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+230D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+230E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+230F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2310
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2311
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2312
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2313
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2314
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2315
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2316
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2317
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2318
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2319
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+231A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+231B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+231C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+231D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+231E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+231F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2320
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2321
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2322
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2323
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2324
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2325
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2326
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2327
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2328
+  CHARPROP____(0x092u, kNormal, kON, kOP)  // U+2329
+  CHARPROP____(0x093u, kNormal, kON, kCL)  // U+232A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+232B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+232C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+232D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+232E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+232F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2330
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2331
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2332
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2333
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2334
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2335
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2336
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2337
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2338
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2339
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+233A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+233B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+233C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+233D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+233E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+233F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2340
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2341
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2342
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2343
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2344
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2345
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2346
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2347
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2348
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2349
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+234A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+234B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+234C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+234D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+234E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+234F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2350
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2351
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2352
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2353
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2354
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2355
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2356
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2357
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2358
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2359
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+235A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+235B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+235C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+235D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+235E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+235F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2360
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2361
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2362
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2363
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2364
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2365
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2366
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2367
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2368
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2369
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+236A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+236B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+236C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+236D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+236E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+236F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2370
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2371
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2372
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2373
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2374
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2375
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2376
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2377
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2378
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2379
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+237A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+237B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+237C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+237D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+237E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+237F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2380
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2381
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2382
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2383
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2384
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2385
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2386
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2387
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2388
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2389
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+238A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+238B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+238C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+238D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+238E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+238F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2390
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2391
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2392
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2393
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2394
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2395
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2396
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2397
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2398
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2399
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+239A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+239B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+239C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+239D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+239E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+239F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23A9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23AA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23AB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23AC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23AF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23BA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23BE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23BF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23C9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23CA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23CB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23CC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23CD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23CE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23CF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23D9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23DA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23DB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23DC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23DD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23DE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23DF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+23E8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23E9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23EA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23EB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23EC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23ED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23EE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23EF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+23FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2400
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2401
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2402
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2403
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2404
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2405
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2406
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2407
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2408
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2409
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+240A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+240B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+240C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+240D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+240E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+240F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2410
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2411
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2412
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2413
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2414
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2415
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2416
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2417
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2418
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2419
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+241A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+241B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+241C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+241D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+241E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+241F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2420
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2421
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2422
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2423
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2424
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2425
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2426
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2427
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2428
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2429
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+242A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+242B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+242C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+242D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+242E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+242F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2430
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2431
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2432
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2433
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2434
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2435
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2436
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2437
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2438
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2439
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+243A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+243B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+243C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+243D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+243E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+243F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2440
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2441
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2442
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2443
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2444
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2445
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2446
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2447
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2448
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2449
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+244A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+244B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+244C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+244D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+244E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+244F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2450
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2451
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2452
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2453
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2454
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2455
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2456
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2457
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2458
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2459
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+245A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+245B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+245C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+245D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+245E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+245F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2460
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2461
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2462
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2463
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2464
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2465
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2466
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2467
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2468
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2469
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+246A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+246B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+246C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+246D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+246E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+246F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2470
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2471
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2472
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2473
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2474
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2475
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2476
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2477
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2478
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2479
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+247A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+247B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+247C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+247D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+247E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+247F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2480
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2481
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2482
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2483
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2484
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2485
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2486
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2487
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2488
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2489
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+248A
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+248B
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+248C
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+248D
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+248E
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+248F
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2490
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2491
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2492
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2493
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2494
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2495
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2496
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2497
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2498
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+2499
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+249A
+  CHARPROP____(0x1ffu, kNormal, kEN, kAI)  // U+249B
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+249C
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+249D
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+249E
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+249F
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+24E9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24EA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24EB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24EC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24ED
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24FB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24FC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+24FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+24FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2500
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2501
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2502
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2503
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2504
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2505
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2506
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2507
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2508
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2509
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+250A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+250B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+250C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+250D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+250E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+250F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2510
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2511
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2512
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2513
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2514
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2515
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2516
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2517
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2518
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2519
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+251A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+251B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+251C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+251D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+251E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+251F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2520
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2521
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2522
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2523
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2524
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2525
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2526
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2527
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2528
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2529
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+252A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+252B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+252C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+252D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+252E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+252F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2530
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2531
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2532
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2533
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2534
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2535
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2536
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2537
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2538
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2539
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+253A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+253B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+253C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+253D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+253E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+253F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2540
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2541
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2542
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2543
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2544
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2545
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2546
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2547
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2548
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2549
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+254A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+254B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+254C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+254D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+254E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+254F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2550
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2551
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2552
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2553
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2554
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2555
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2556
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2557
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2558
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2559
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+255A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+255B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+255C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+255D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+255E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+255F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2560
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2561
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2562
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2563
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2564
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2565
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2566
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2567
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2568
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2569
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+256A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+256B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+256C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+256D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+256E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+256F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2570
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2571
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2572
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2573
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2574
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2575
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2576
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2577
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2578
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2579
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+257A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+257B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+257C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+257D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+257E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+257F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2580
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2581
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2582
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2583
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2584
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2585
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2586
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2587
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2588
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2589
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+258A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+258B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+258C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+258D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+258E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+258F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2590
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2591
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2592
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2593
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2594
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2595
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2596
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2597
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2598
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2599
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+259A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+259B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+259C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+259D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+259E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+259F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A0
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25A2
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A3
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A4
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25A9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25AA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25AB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25AC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25AF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25B0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25B1
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25B2
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25B3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25B4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25B5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25B6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25B7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25BA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25BE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25BF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25C0
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25C2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25C3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25C4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25C5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25C7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25C8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25C9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25CA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25CB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25CC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25CD
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25CE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25CF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25D0
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25D1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25D9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25DA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25DB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25DC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25DD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25DE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25DF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25E0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25E1
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25E2
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25E3
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25E4
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25E5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25E6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25E7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25E8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25E9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25EA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25EB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25EC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25ED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+25EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25FB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25FC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+25FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2600
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2601
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2602
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2603
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2604
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2605
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2606
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2607
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2608
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2609
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+260A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+260B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+260C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+260D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+260E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+260F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2610
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2611
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2612
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2613
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2614
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2615
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2616
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2617
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2618
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2619
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+261A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+261B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+261C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+261D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+261E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+261F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2620
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2621
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2622
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2623
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2624
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2625
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2626
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2627
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2628
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2629
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+262A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+262B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+262C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+262D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+262E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+262F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2630
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2631
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2632
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2633
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2634
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2635
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2636
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2637
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2638
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2639
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+263A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+263B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+263C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+263D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+263E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+263F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2640
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2641
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2642
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2643
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2644
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2645
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2646
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2647
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2648
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2649
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+264A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+264B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+264C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+264D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+264E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+264F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2650
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2651
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2652
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2653
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2654
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2655
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2656
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2657
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2658
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2659
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+265A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+265B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+265C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+265D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+265E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+265F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2660
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2661
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2662
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2663
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2664
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2665
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2666
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2667
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2668
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2669
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+266A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+266B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+266C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+266D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+266E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+266F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2670
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2671
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2672
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2673
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2674
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2675
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2676
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2677
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2678
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2679
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+267A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+267B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+267C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+267D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+267E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+267F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2680
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2681
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2682
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2683
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2684
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2685
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2686
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2687
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2688
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2689
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+268A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+268B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+268C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+268D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+268E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+268F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2690
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2691
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2692
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2693
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2694
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2695
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2696
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2697
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2698
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2699
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+269A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+269B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+269C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+269D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+269E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+269F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26A9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26AA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+26AC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26AF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26BA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26BE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26BF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26C0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26C2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+26C3
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26C4
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26C5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26C7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26C8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26C9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26CA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26CB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26CC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+26CE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26CF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D0
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D1
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D3
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D4
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26D9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26DA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26DB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26DC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26DD
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26DE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26DF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26E0
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26E1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+26E2
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26E3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+26E4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+26E5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+26E6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+26E7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26E8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26E9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26EA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26EB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26EC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26ED
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26FB
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26FC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+26FF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2700
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2701
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2702
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2703
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2704
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2705
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2706
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2707
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2708
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2709
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+270A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+270B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+270C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+270D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+270E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+270F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2710
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2711
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2712
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2713
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2714
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2715
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2716
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2717
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2718
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2719
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+271A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+271B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+271C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+271D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+271E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+271F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2720
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2721
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2722
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2723
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2724
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2725
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2726
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2727
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2728
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2729
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+272A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+272B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+272C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+272D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+272E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+272F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2730
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2731
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2732
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2733
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2734
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2735
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2736
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2737
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2738
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2739
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+273A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+273B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+273C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+273D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+273E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+273F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2740
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2741
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2742
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2743
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2744
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2745
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2746
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2747
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2748
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2749
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+274A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+274B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+274C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+274D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+274E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+274F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2750
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2751
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2752
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2753
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2754
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2755
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2756
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2757
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2758
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2759
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+275A
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+275B
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+275C
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+275D
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+275E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+275F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2760
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2761
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+2762
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+2763
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2764
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2765
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2766
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2767
+  CHARPROP____(0x094u, kNormal, kON, kOP)  // U+2768
+  CHARPROP____(0x095u, kNormal, kON, kCL)  // U+2769
+  CHARPROP____(0x096u, kNormal, kON, kOP)  // U+276A
+  CHARPROP____(0x097u, kNormal, kON, kCL)  // U+276B
+  CHARPROP____(0x098u, kNormal, kON, kOP)  // U+276C
+  CHARPROP____(0x099u, kNormal, kON, kCL)  // U+276D
+  CHARPROP____(0x09au, kNormal, kON, kOP)  // U+276E
+  CHARPROP____(0x09bu, kNormal, kON, kCL)  // U+276F
+  CHARPROP____(0x09cu, kNormal, kON, kOP)  // U+2770
+  CHARPROP____(0x09du, kNormal, kON, kCL)  // U+2771
+  CHARPROP____(0x09eu, kNormal, kON, kOP)  // U+2772
+  CHARPROP____(0x09fu, kNormal, kON, kCL)  // U+2773
+  CHARPROP____(0x0a0u, kNormal, kON, kOP)  // U+2774
+  CHARPROP____(0x0a1u, kNormal, kON, kCL)  // U+2775
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2776
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2777
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2778
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2779
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+277A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+277B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+277C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+277D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+277E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+277F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2780
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2781
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2782
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2783
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2784
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2785
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2786
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2787
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2788
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2789
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+278A
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+278B
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+278C
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+278D
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+278E
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+278F
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2790
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2791
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2792
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2793
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2794
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2795
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2796
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2797
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2798
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2799
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+279A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+279B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+279C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+279D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+279E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+279F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27A9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27AA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27AB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27AC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27AF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+27B0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27BA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27BE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+27BF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27C0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27C2
+  CHARPROP____(0x0a2u, kNormal, kON, kAL)  // U+27C3
+  CHARPROP____(0x0a3u, kNormal, kON, kAL)  // U+27C4
+  CHARPROP____(0x0a4u, kNormal, kON, kOP)  // U+27C5
+  CHARPROP____(0x0a5u, kNormal, kON, kCL)  // U+27C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27C7
+  CHARPROP____(0x0a6u, kNormal, kON, kAL)  // U+27C8
+  CHARPROP____(0x0a7u, kNormal, kON, kAL)  // U+27C9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27CA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+27CB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27CC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+27CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+27CE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+27CF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D4
+  CHARPROP____(0x0a8u, kNormal, kON, kAL)  // U+27D5
+  CHARPROP____(0x0a9u, kNormal, kON, kAL)  // U+27D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27D9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27DA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27DB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27DC
+  CHARPROP____(0x0aau, kNormal, kON, kAL)  // U+27DD
+  CHARPROP____(0x0abu, kNormal, kON, kAL)  // U+27DE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27DF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27E0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27E1
+  CHARPROP____(0x0acu, kNormal, kON, kAL)  // U+27E2
+  CHARPROP____(0x0adu, kNormal, kON, kAL)  // U+27E3
+  CHARPROP____(0x0aeu, kNormal, kON, kAL)  // U+27E4
+  CHARPROP____(0x0afu, kNormal, kON, kAL)  // U+27E5
+  CHARPROP____(0x0b0u, kNormal, kON, kOP)  // U+27E6
+  CHARPROP____(0x0b1u, kNormal, kON, kCL)  // U+27E7
+  CHARPROP____(0x0b2u, kNormal, kON, kOP)  // U+27E8
+  CHARPROP____(0x0b3u, kNormal, kON, kCL)  // U+27E9
+  CHARPROP____(0x0b4u, kNormal, kON, kOP)  // U+27EA
+  CHARPROP____(0x0b5u, kNormal, kON, kCL)  // U+27EB
+  CHARPROP____(0x0b6u, kNormal, kON, kOP)  // U+27EC
+  CHARPROP____(0x0b7u, kNormal, kON, kCL)  // U+27ED
+  CHARPROP____(0x0b8u, kNormal, kON, kOP)  // U+27EE
+  CHARPROP____(0x0b9u, kNormal, kON, kCL)  // U+27EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27FB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27FC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+27FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2800
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2801
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2802
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2803
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2804
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2805
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2806
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2807
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2808
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2809
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+280A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+280B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+280C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+280D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+280E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+280F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2810
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2811
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2812
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2813
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2814
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2815
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2816
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2817
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2818
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2819
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+281A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+281B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+281C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+281D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+281E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+281F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2820
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2821
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2822
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2823
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2824
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2825
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2826
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2827
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2828
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2829
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+282A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+282B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+282C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+282D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+282E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+282F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2830
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2831
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2832
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2833
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2834
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2835
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2836
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2837
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2838
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2839
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+283A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+283B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+283C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+283D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+283E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+283F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2840
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2841
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2842
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2843
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2844
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2845
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2846
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2847
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2848
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2849
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+284A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+284B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+284C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+284D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+284E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+284F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2850
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2851
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2852
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2853
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2854
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2855
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2856
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2857
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2858
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2859
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+285A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+285B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+285C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+285D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+285E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+285F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2860
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2861
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2862
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2863
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2864
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2865
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2866
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2867
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2868
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2869
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+286A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+286B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+286C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+286D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+286E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+286F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2870
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2871
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2872
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2873
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2874
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2875
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2876
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2877
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2878
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2879
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+287A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+287B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+287C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+287D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+287E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+287F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2880
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2881
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2882
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2883
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2884
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2885
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2886
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2887
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2888
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2889
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+288A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+288B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+288C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+288D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+288E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+288F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2890
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2891
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2892
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2893
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2894
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2895
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2896
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2897
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2898
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2899
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+289A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+289B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+289C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+289D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+289E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+289F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+28FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2900
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2901
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2902
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2903
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2904
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2905
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2906
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2907
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2908
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2909
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+290A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+290B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+290C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+290D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+290E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+290F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2910
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2911
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2912
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2913
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2914
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2915
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2916
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2917
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2918
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2919
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+291A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+291B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+291C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+291D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+291E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+291F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2920
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2921
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2922
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2923
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2924
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2925
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2926
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2927
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2928
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2929
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+292A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+292B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+292C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+292D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+292E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+292F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2930
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2931
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2932
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2933
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2934
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2935
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2936
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2937
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2938
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2939
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+293A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+293B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+293C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+293D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+293E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+293F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2940
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2941
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2942
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2943
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2944
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2945
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2946
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2947
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2948
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2949
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+294A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+294B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+294C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+294D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+294E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+294F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2950
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2951
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2952
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2953
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2954
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2955
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2956
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2957
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2958
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2959
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+295A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+295B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+295C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+295D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+295E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+295F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2960
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2961
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2962
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2963
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2964
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2965
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2966
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2967
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2968
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2969
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+296A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+296B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+296C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+296D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+296E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+296F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2970
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2971
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2972
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2973
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2974
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2975
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2976
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2977
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2978
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2979
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+297A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+297B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+297C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+297D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+297E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+297F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2980
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2981
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2982
+  CHARPROP____(0x0bau, kNormal, kON, kOP)  // U+2983
+  CHARPROP____(0x0bbu, kNormal, kON, kCL)  // U+2984
+  CHARPROP____(0x0bcu, kNormal, kON, kOP)  // U+2985
+  CHARPROP____(0x0bdu, kNormal, kON, kCL)  // U+2986
+  CHARPROP____(0x0beu, kNormal, kON, kOP)  // U+2987
+  CHARPROP____(0x0bfu, kNormal, kON, kCL)  // U+2988
+  CHARPROP____(0x0c0u, kNormal, kON, kOP)  // U+2989
+  CHARPROP____(0x0c1u, kNormal, kON, kCL)  // U+298A
+  CHARPROP____(0x0c2u, kNormal, kON, kOP)  // U+298B
+  CHARPROP____(0x0c3u, kNormal, kON, kCL)  // U+298C
+  CHARPROP____(0x0c4u, kNormal, kON, kOP)  // U+298D
+  CHARPROP____(0x0c5u, kNormal, kON, kCL)  // U+298E
+  CHARPROP____(0x0c6u, kNormal, kON, kOP)  // U+298F
+  CHARPROP____(0x0c7u, kNormal, kON, kCL)  // U+2990
+  CHARPROP____(0x0c8u, kNormal, kON, kOP)  // U+2991
+  CHARPROP____(0x0c9u, kNormal, kON, kCL)  // U+2992
+  CHARPROP____(0x0cau, kNormal, kON, kOP)  // U+2993
+  CHARPROP____(0x0cbu, kNormal, kON, kCL)  // U+2994
+  CHARPROP____(0x0ccu, kNormal, kON, kOP)  // U+2995
+  CHARPROP____(0x0cdu, kNormal, kON, kCL)  // U+2996
+  CHARPROP____(0x0ceu, kNormal, kON, kOP)  // U+2997
+  CHARPROP____(0x0cfu, kNormal, kON, kCL)  // U+2998
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2999
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+299A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+299B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+299C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+299D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+299E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+299F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29A9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29AA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29AB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29AC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29AD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29AE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29AF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B7
+  CHARPROP____(0x0d0u, kNormal, kON, kAL)  // U+29B8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29B9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29BA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29BB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29BC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29BD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29BE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29BF
+  CHARPROP____(0x0d1u, kNormal, kON, kAL)  // U+29C0
+  CHARPROP____(0x0d2u, kNormal, kON, kAL)  // U+29C1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29C2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29C3
+  CHARPROP____(0x0d3u, kNormal, kON, kAL)  // U+29C4
+  CHARPROP____(0x0d4u, kNormal, kON, kAL)  // U+29C5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29C6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29C7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29C8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29C9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29CA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29CB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29CC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29CD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29CE
+  CHARPROP____(0x0d5u, kNormal, kON, kAL)  // U+29CF
+  CHARPROP____(0x0d6u, kNormal, kON, kAL)  // U+29D0
+  CHARPROP____(0x0d7u, kNormal, kON, kAL)  // U+29D1
+  CHARPROP____(0x0d8u, kNormal, kON, kAL)  // U+29D2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29D3
+  CHARPROP____(0x0d9u, kNormal, kON, kAL)  // U+29D4
+  CHARPROP____(0x0dau, kNormal, kON, kAL)  // U+29D5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29D6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29D7
+  CHARPROP____(0x0dbu, kNormal, kON, kOP)  // U+29D8
+  CHARPROP____(0x0dcu, kNormal, kON, kCL)  // U+29D9
+  CHARPROP____(0x0ddu, kNormal, kON, kOP)  // U+29DA
+  CHARPROP____(0x0deu, kNormal, kON, kCL)  // U+29DB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29DC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29DD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29DE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29DF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29E9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29EA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29EB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29EC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29ED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29EE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29EF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29F0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29F1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29F2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29F3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29F4
+  CHARPROP____(0x0dfu, kNormal, kON, kAL)  // U+29F5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29F6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29F7
+  CHARPROP____(0x0e0u, kNormal, kON, kAL)  // U+29F8
+  CHARPROP____(0x0e1u, kNormal, kON, kAL)  // U+29F9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29FA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29FB
+  CHARPROP____(0x0e2u, kNormal, kON, kOP)  // U+29FC
+  CHARPROP____(0x0e3u, kNormal, kON, kCL)  // U+29FD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29FE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+29FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A00
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A01
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A02
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A03
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A04
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A05
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A06
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A07
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A08
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A09
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A0A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A0B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A0C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A0D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A0E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A0F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A10
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A11
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A12
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A13
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A14
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A15
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A16
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A17
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A18
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A19
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A1A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A1B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A1C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A1D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A1E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A1F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A20
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A21
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A22
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A23
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A24
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A25
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A26
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A27
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A28
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A29
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A2A
+  CHARPROP____(0x0e4u, kNormal, kON, kAL)  // U+2A2B
+  CHARPROP____(0x0e5u, kNormal, kON, kAL)  // U+2A2C
+  CHARPROP____(0x0e6u, kNormal, kON, kAL)  // U+2A2D
+  CHARPROP____(0x0e7u, kNormal, kON, kAL)  // U+2A2E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A2F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A30
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A31
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A32
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A33
+  CHARPROP____(0x0e8u, kNormal, kON, kAL)  // U+2A34
+  CHARPROP____(0x0e9u, kNormal, kON, kAL)  // U+2A35
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A36
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A37
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A38
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A39
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A3A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A3B
+  CHARPROP____(0x0eau, kNormal, kON, kAL)  // U+2A3C
+  CHARPROP____(0x0ebu, kNormal, kON, kAL)  // U+2A3D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A3E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A3F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A40
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A41
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A42
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A43
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A44
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A45
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A46
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A47
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A48
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A49
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A4A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A4B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A4C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A4D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A4E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A4F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A50
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A51
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A52
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A53
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A54
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A55
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A56
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A57
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A58
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A59
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A5A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A5B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A5C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A5D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A5E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A5F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A60
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A61
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A62
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A63
+  CHARPROP____(0x0ecu, kNormal, kON, kAL)  // U+2A64
+  CHARPROP____(0x0edu, kNormal, kON, kAL)  // U+2A65
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A66
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A67
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A68
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A69
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A6A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A6B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A6C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A6D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A6E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A6F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A70
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A71
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A72
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A73
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A74
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A75
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A76
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A77
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A78
+  CHARPROP____(0x0eeu, kNormal, kON, kAL)  // U+2A79
+  CHARPROP____(0x0efu, kNormal, kON, kAL)  // U+2A7A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A7B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A7C
+  CHARPROP____(0x0f0u, kNormal, kON, kAL)  // U+2A7D
+  CHARPROP____(0x0f1u, kNormal, kON, kAL)  // U+2A7E
+  CHARPROP____(0x0f2u, kNormal, kON, kAL)  // U+2A7F
+  CHARPROP____(0x0f3u, kNormal, kON, kAL)  // U+2A80
+  CHARPROP____(0x0f4u, kNormal, kON, kAL)  // U+2A81
+  CHARPROP____(0x0f5u, kNormal, kON, kAL)  // U+2A82
+  CHARPROP____(0x0f6u, kNormal, kON, kAL)  // U+2A83
+  CHARPROP____(0x0f7u, kNormal, kON, kAL)  // U+2A84
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A85
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A86
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A87
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A88
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A89
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A8A
+  CHARPROP____(0x0f8u, kNormal, kON, kAL)  // U+2A8B
+  CHARPROP____(0x0f9u, kNormal, kON, kAL)  // U+2A8C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A8D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A8E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A8F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A90
+  CHARPROP____(0x0fau, kNormal, kON, kAL)  // U+2A91
+  CHARPROP____(0x0fbu, kNormal, kON, kAL)  // U+2A92
+  CHARPROP____(0x0fcu, kNormal, kON, kAL)  // U+2A93
+  CHARPROP____(0x0fdu, kNormal, kON, kAL)  // U+2A94
+  CHARPROP____(0x0feu, kNormal, kON, kAL)  // U+2A95
+  CHARPROP____(0x0ffu, kNormal, kON, kAL)  // U+2A96
+  CHARPROP____(0x100u, kNormal, kON, kAL)  // U+2A97
+  CHARPROP____(0x101u, kNormal, kON, kAL)  // U+2A98
+  CHARPROP____(0x102u, kNormal, kON, kAL)  // U+2A99
+  CHARPROP____(0x103u, kNormal, kON, kAL)  // U+2A9A
+  CHARPROP____(0x104u, kNormal, kON, kAL)  // U+2A9B
+  CHARPROP____(0x105u, kNormal, kON, kAL)  // U+2A9C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A9D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A9E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2A9F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AA0
+  CHARPROP____(0x106u, kNormal, kON, kAL)  // U+2AA1
+  CHARPROP____(0x107u, kNormal, kON, kAL)  // U+2AA2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AA3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AA4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AA5
+  CHARPROP____(0x108u, kNormal, kON, kAL)  // U+2AA6
+  CHARPROP____(0x109u, kNormal, kON, kAL)  // U+2AA7
+  CHARPROP____(0x10au, kNormal, kON, kAL)  // U+2AA8
+  CHARPROP____(0x10bu, kNormal, kON, kAL)  // U+2AA9
+  CHARPROP____(0x10cu, kNormal, kON, kAL)  // U+2AAA
+  CHARPROP____(0x10du, kNormal, kON, kAL)  // U+2AAB
+  CHARPROP____(0x10eu, kNormal, kON, kAL)  // U+2AAC
+  CHARPROP____(0x10fu, kNormal, kON, kAL)  // U+2AAD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AAE
+  CHARPROP____(0x110u, kNormal, kON, kAL)  // U+2AAF
+  CHARPROP____(0x111u, kNormal, kON, kAL)  // U+2AB0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AB1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AB2
+  CHARPROP____(0x112u, kNormal, kON, kAL)  // U+2AB3
+  CHARPROP____(0x113u, kNormal, kON, kAL)  // U+2AB4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AB5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AB6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AB7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AB8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AB9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ABA
+  CHARPROP____(0x114u, kNormal, kON, kAL)  // U+2ABB
+  CHARPROP____(0x115u, kNormal, kON, kAL)  // U+2ABC
+  CHARPROP____(0x116u, kNormal, kON, kAL)  // U+2ABD
+  CHARPROP____(0x117u, kNormal, kON, kAL)  // U+2ABE
+  CHARPROP____(0x118u, kNormal, kON, kAL)  // U+2ABF
+  CHARPROP____(0x119u, kNormal, kON, kAL)  // U+2AC0
+  CHARPROP____(0x11au, kNormal, kON, kAL)  // U+2AC1
+  CHARPROP____(0x11bu, kNormal, kON, kAL)  // U+2AC2
+  CHARPROP____(0x11cu, kNormal, kON, kAL)  // U+2AC3
+  CHARPROP____(0x11du, kNormal, kON, kAL)  // U+2AC4
+  CHARPROP____(0x11eu, kNormal, kON, kAL)  // U+2AC5
+  CHARPROP____(0x11fu, kNormal, kON, kAL)  // U+2AC6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AC7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AC8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AC9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ACA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ACB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ACC
+  CHARPROP____(0x120u, kNormal, kON, kAL)  // U+2ACD
+  CHARPROP____(0x121u, kNormal, kON, kAL)  // U+2ACE
+  CHARPROP____(0x122u, kNormal, kON, kAL)  // U+2ACF
+  CHARPROP____(0x123u, kNormal, kON, kAL)  // U+2AD0
+  CHARPROP____(0x124u, kNormal, kON, kAL)  // U+2AD1
+  CHARPROP____(0x125u, kNormal, kON, kAL)  // U+2AD2
+  CHARPROP____(0x126u, kNormal, kON, kAL)  // U+2AD3
+  CHARPROP____(0x127u, kNormal, kON, kAL)  // U+2AD4
+  CHARPROP____(0x128u, kNormal, kON, kAL)  // U+2AD5
+  CHARPROP____(0x129u, kNormal, kON, kAL)  // U+2AD6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AD7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AD8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AD9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ADA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ADB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ADC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ADD
+  CHARPROP____(0x12au, kNormal, kON, kAL)  // U+2ADE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2ADF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AE0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AE1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AE2
+  CHARPROP____(0x12bu, kNormal, kON, kAL)  // U+2AE3
+  CHARPROP____(0x12cu, kNormal, kON, kAL)  // U+2AE4
+  CHARPROP____(0x12du, kNormal, kON, kAL)  // U+2AE5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AE6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AE7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AE8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AE9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AEA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AEB
+  CHARPROP____(0x12eu, kNormal, kON, kAL)  // U+2AEC
+  CHARPROP____(0x12fu, kNormal, kON, kAL)  // U+2AED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AEE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AEF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AF0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AF1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AF2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AF3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AF4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AF5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AF6
+  CHARPROP____(0x130u, kNormal, kON, kAL)  // U+2AF7
+  CHARPROP____(0x131u, kNormal, kON, kAL)  // U+2AF8
+  CHARPROP____(0x132u, kNormal, kON, kAL)  // U+2AF9
+  CHARPROP____(0x133u, kNormal, kON, kAL)  // U+2AFA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AFB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AFC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AFD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AFE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2AFF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B00
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B01
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B02
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B03
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B04
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B05
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B06
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B07
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B08
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B09
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B0A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B0B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B0C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B0D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B0E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B0F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B10
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B11
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B12
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B13
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B14
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B15
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B16
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B17
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B18
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B19
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B1A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B1B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B1C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B1D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B1E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B1F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B20
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B21
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B22
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B23
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B24
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B25
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B26
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B27
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B28
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B29
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B2A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B2B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B2C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B2D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B2E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B2F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B30
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B31
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B32
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B33
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B34
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B35
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B36
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B37
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B38
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B39
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B3A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B3B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B3C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B3D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B3E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B3F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B40
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B41
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B42
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B43
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B44
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B45
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B46
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B47
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B48
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B49
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B4A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B4B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B4C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B4F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B50
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B51
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B52
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B53
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2B54
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2B55
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2B56
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2B57
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2B58
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+2B59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B5A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B5B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B5C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B5F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B60
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B61
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B62
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B65
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B66
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B67
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B68
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B69
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B6A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B6B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B6C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B6D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B6E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B6F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B70
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B71
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B72
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B73
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B74
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B78
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B79
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B7A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B80
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B81
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B82
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B83
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B84
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B85
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B86
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B87
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B88
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B89
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B8A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B8B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B8C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B8D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B8E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B8F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B90
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B91
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B92
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B93
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B94
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B95
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B96
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B98
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B99
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B9A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B9B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B9C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B9D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B9E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2B9F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BA9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BAA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BAB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BAC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BAD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BAE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BAF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BBA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BBB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BBC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BBD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BBF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BC9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BCA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BCB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BCC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BCD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BCE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BCF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BDA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BDB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BDC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BDD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BDF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C28
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C29
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C2E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C33
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C39
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C40
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C41
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C42
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C43
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C45
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C46
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C47
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C50
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C51
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C52
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C53
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C54
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C55
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C56
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C57
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C59
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C66
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C67
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C68
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C69
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C75
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C76
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C77
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C78
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C80
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C81
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C83
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CE4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2CE5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2CE6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2CE7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2CE8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2CE9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2CEE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2CEF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2CF0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2CF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2CF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2CF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2CF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2CF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2CF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2CF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2CF8
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+2CF9
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2CFA
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2CFB
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2CFC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2CFD
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+2CFE
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D25
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D26
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D27
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D28
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D29
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D2A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D2B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D2C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D2D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D2E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D30
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D31
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D32
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D33
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D34
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D35
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D36
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D37
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D38
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D39
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D40
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D41
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D42
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D43
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D45
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D46
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D47
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D50
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D51
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D52
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D53
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D54
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D55
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D56
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D57
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D58
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D59
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D60
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D61
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D62
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D63
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D64
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D65
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D66
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D67
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D68
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D69
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D6A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D6B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D6C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D6D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D6F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D70
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D71
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D72
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D73
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D74
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D78
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D79
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D7A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D80
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D81
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D83
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2D96
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D98
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D99
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D9A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D9B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D9C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D9D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D9E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DAE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DC7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DC9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DCA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DCB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DCC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DCD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DCE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+2DDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2DDF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DE9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DEA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DEB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DEC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DED
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DEE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DEF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DF9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DFA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DFB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DFC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DFD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DFE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+2DFF
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+2E00
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+2E01
+  CHARPROP____(0x134u, kNormal, kON, kOP)  // U+2E02
+  CHARPROP____(0x135u, kNormal, kON, kCL)  // U+2E03
+  CHARPROP____(0x136u, kNormal, kON, kOP)  // U+2E04
+  CHARPROP____(0x137u, kNormal, kON, kCL)  // U+2E05
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+2E06
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+2E07
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+2E08
+  CHARPROP____(0x138u, kNormal, kON, kOP)  // U+2E09
+  CHARPROP____(0x139u, kNormal, kON, kCL)  // U+2E0A
+  CHARPROP____(0x1ffu, kNormal, kON, kQU)  // U+2E0B
+  CHARPROP____(0x13au, kNormal, kON, kOP)  // U+2E0C
+  CHARPROP____(0x13bu, kNormal, kON, kCL)  // U+2E0D
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E0E
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E0F
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E10
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E11
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E12
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E13
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E14
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E15
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2E16
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E17
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+2E18
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E19
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2E1A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2E1B
+  CHARPROP____(0x13cu, kNormal, kON, kOP)  // U+2E1C
+  CHARPROP____(0x13du, kNormal, kON, kCL)  // U+2E1D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2E1E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2E1F
+  CHARPROP____(0x13eu, kNormal, kON, kOP)  // U+2E20
+  CHARPROP____(0x13fu, kNormal, kON, kCL)  // U+2E21
+  CHARPROP____(0x140u, kNormal, kON, kOP)  // U+2E22
+  CHARPROP____(0x141u, kNormal, kON, kCL)  // U+2E23
+  CHARPROP____(0x142u, kNormal, kON, kOP)  // U+2E24
+  CHARPROP____(0x143u, kNormal, kON, kCL)  // U+2E25
+  CHARPROP____(0x144u, kNormal, kON, kOP)  // U+2E26
+  CHARPROP____(0x145u, kNormal, kON, kCL)  // U+2E27
+  CHARPROP____(0x146u, kNormal, kON, kOP)  // U+2E28
+  CHARPROP____(0x147u, kNormal, kON, kCL)  // U+2E29
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E2A
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E2B
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E2C
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E2D
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+2E2E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+2E2F
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E30
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+2E31
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E32
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E33
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E34
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E35
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E36
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E37
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E38
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E3B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E3C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E3D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E3E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E3F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E40
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E41
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E42
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E43
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E44
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E45
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E46
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E47
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E48
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E49
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E4A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E4B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E4C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E4F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E50
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E51
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E52
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E53
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E54
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E55
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E56
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E57
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E58
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E5A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E5B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E5C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E5F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E60
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E61
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E62
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E65
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E66
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E67
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E68
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E69
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E6A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E6B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E6C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E6D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E6E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E6F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E70
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E71
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E72
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E73
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E74
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E78
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E79
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E7A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E7F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E80
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E81
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E82
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E83
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E84
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E85
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E86
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E87
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E88
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E89
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E8A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E8B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E8C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E8D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E8E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E8F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E90
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E91
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E92
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E93
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E94
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E95
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E96
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E97
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E98
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E99
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2E9A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E9B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E9C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E9D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E9E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2E9F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EA9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EAA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EAB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EAC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EAD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EAE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EAF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EB9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EBA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EBB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EBC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EBD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EBE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EBF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EC9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ECA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ECB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ECC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ECD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ECE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ECF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2ED9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EDA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EDB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EDC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EDD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EDE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EDF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EE9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EEA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EEB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EEC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EED
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EEE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EEF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EF0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EF1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EF2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2EF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2EFF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F00
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F01
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F02
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F03
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F04
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F05
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F06
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F07
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F08
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F09
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F0A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F0B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F0C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F0D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F0E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F0F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F10
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F11
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F12
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F13
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F14
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F15
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F16
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F17
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F18
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F19
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F1A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F1B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F1C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F1D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F1E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F1F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F20
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F21
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F22
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F23
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F24
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F25
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F26
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F27
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F28
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F29
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F2A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F2B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F2C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F2D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F2E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F2F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F30
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F31
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F32
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F33
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F34
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F35
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F36
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F37
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F38
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F39
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F3A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F3B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F3C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F3D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F3E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F3F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F40
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F41
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F42
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F43
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F44
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F45
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F46
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F47
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F48
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F49
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F4A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F4B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F4C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F4D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F4E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F4F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F50
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F51
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F52
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F53
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F54
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F55
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F56
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F57
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F58
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F59
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F5A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F5B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F5C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F5D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F5E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F5F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F60
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F61
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F62
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F63
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F64
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F65
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F66
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F67
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F68
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F69
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F6A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F6B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F6C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F6D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F6E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F6F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F70
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F71
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F72
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F73
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F74
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F75
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F76
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F77
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F78
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F79
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F7A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F7B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F7C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F7D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F7E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F7F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F80
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F81
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F82
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F83
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F84
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F85
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F86
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F87
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F88
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F89
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F8A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F8B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F8C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F8D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F8E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F8F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F90
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F91
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F92
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F93
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F94
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F95
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F96
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F97
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F98
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F99
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F9A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F9B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F9C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F9D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F9E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2F9F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FA9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FAA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FAB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FAC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FAD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FAE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FAF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FB9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FBA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FBB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FBC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FBD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FBE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FBF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FC9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FCA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FCB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FCC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FCD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FCE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FCF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FD0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FD1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FD2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FD3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FD4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FD5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FDA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FDB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FDC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FDD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FDF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FEF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FF9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FFA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+2FFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+2FFF
+  CHARPROP____(0x1ffu, kNormal, kWS, kID)  // U+3000
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+3001
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+3002
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3003
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3004
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3007
+  CHARPROP____(0x148u, kNormal, kON, kOP)  // U+3008
+  CHARPROP____(0x149u, kNormal, kON, kCL)  // U+3009
+  CHARPROP____(0x14au, kNormal, kON, kOP)  // U+300A
+  CHARPROP____(0x14bu, kNormal, kON, kCL)  // U+300B
+  CHARPROP____(0x14cu, kNormal, kON, kOP)  // U+300C
+  CHARPROP____(0x14du, kNormal, kON, kCL)  // U+300D
+  CHARPROP____(0x14eu, kNormal, kON, kOP)  // U+300E
+  CHARPROP____(0x14fu, kNormal, kON, kCL)  // U+300F
+  CHARPROP____(0x150u, kNormal, kON, kOP)  // U+3010
+  CHARPROP____(0x151u, kNormal, kON, kCL)  // U+3011
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3012
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3013
+  CHARPROP____(0x152u, kNormal, kON, kOP)  // U+3014
+  CHARPROP____(0x153u, kNormal, kON, kCL)  // U+3015
+  CHARPROP____(0x154u, kNormal, kON, kOP)  // U+3016
+  CHARPROP____(0x155u, kNormal, kON, kCL)  // U+3017
+  CHARPROP____(0x156u, kNormal, kON, kOP)  // U+3018
+  CHARPROP____(0x157u, kNormal, kON, kCL)  // U+3019
+  CHARPROP____(0x158u, kNormal, kON, kOP)  // U+301A
+  CHARPROP____(0x159u, kNormal, kON, kCL)  // U+301B
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+301C
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+301D
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+301E
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+301F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3029
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+302A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+302B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+302C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+302D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+302E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+302F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3035
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3036
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+303A
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+303B
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+303C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+303D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+303E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+303F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3040
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3042
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3044
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3046
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3048
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+304A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+304B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+304C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+304D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+304E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+304F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+305A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+305B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+305C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+305D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+305E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+305F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3062
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+306A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+306B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+306C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+306D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+306E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+306F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+307A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+307B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+307C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+307D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+307E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+307F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3082
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3084
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3086
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+308A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+308B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+308C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+308D
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+308E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+308F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3094
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3095
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+3096
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3097
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3098
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+3099
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+309A
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+309B
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+309C
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+309D
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+309E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+309F
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+30A0
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30A2
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30A4
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30A6
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30A8
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C2
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30E2
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30E4
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30E6
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30ED
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F4
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30F5
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30FA
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+30FB
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30FC
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30FD
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+30FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+30FF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3100
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3101
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3102
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3103
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+310A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+310B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+310C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+310D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+310E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+310F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+311A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+311B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+311C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+311D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+311E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+311F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+312A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+312B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+312C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+312D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+312E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+312F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+3130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+313A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+313B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+313C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+313D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+313E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+313F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+314A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+314B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+314C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+314D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+314E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+314F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+315A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+315B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+315C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+315D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+315E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+315F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+316A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+316B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+316C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+316D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+316E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+316F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+317A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+317B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+317C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+317D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+317E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+317F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+318A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+318B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+318C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+318D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+318E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+318F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+319A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+319B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+319C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+319D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+319E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+319F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+31B7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31B8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31B9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31BA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31BB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31BC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31BD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31BE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31BF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31C9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31CA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31CB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31CC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31CD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31CE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31CF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31D9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31DA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31DB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31DC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31DD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31DE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31DF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31E0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31E1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31E2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+31E3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31E4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31E5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31E6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31E7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31E8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31E9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31EA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31EB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31EC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31ED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31EE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+31EF
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F0
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F1
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F2
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F3
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F4
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F5
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F6
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F7
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F8
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31F9
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31FA
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31FB
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31FC
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31FD
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31FE
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+31FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+320A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+320B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+320C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+320D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+320E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+320F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+321A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+321B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+321C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+321D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+321E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+321F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+322A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+322B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+322C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+322D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+322E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+322F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+323A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+323B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+323C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+323D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+323E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+323F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3247
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+3248
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+3249
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+324A
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+324B
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+324C
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+324D
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+324E
+  CHARPROP____(0x1ffu, kNormal, kL, kAI)  // U+324F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3250
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3251
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3252
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3253
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3254
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3255
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3256
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3257
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3258
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3259
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+325A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+325B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+325C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+325D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+325E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+325F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+326A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+326B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+326C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+326D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+326E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+326F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+327A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+327B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+327C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+327D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+327E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+327F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+328A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+328B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+328C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+328D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+328E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+328F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+329A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+329B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+329C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+329D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+329E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+329F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32B0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32B9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32BA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32BB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32BC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32BD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32BE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32CB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32CC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32CD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32CE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+32CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+32FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+32FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+330A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+330B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+330C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+330D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+330E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+330F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+331A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+331B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+331C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+331D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+331E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+331F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+332A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+332B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+332C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+332D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+332E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+332F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+333A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+333B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+333C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+333D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+333E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+333F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+334A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+334B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+334C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+334D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+334E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+334F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+335A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+335B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+335C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+335D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+335E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+335F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+336A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+336B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+336C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+336D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+336E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+336F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3376
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3377
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3378
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+3379
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+337A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+337B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+337C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+337D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+337E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+337F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+338A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+338B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+338C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+338D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+338E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+338F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+339A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+339B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+339C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+339D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+339E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+339F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33DD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+33DE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+33DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+33FE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+33FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+340A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+340B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+340C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+340D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+340E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+340F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+341A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+341B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+341C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+341D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+341E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+341F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+342A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+342B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+342C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+342D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+342E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+342F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+343A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+343B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+343C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+343D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+343E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+343F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+344A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+344B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+344C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+344D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+344E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+344F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+345A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+345B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+345C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+345D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+345E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+345F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+346A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+346B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+346C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+346D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+346E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+346F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+347A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+347B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+347C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+347D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+347E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+347F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+348A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+348B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+348C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+348D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+348E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+348F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3490
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3491
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3492
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3493
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3494
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3495
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3496
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3497
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3498
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3499
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+349A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+349B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+349C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+349D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+349E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+349F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+34FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3500
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3501
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3502
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3503
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3504
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3505
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3506
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3507
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3508
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3509
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+350A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+350B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+350C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+350D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+350E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+350F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3510
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3511
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3512
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3513
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3514
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3515
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3516
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3517
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3518
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3519
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+351A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+351B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+351C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+351D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+351E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+351F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3520
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3521
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3522
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3523
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3524
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3525
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3526
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3527
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3528
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3529
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+352A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+352B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+352C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+352D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+352E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+352F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3530
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3531
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3532
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3533
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3534
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3535
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3536
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3537
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3538
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3539
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+353A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+353B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+353C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+353D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+353E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+353F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3540
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3541
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3542
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3543
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3544
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3545
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3546
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3547
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3548
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3549
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+354A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+354B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+354C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+354D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+354E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+354F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3550
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3551
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3552
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3553
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3554
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3555
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3556
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3557
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3558
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3559
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+355A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+355B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+355C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+355D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+355E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+355F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3560
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3561
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3562
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3563
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3564
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3565
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3566
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3567
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3568
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3569
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+356A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+356B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+356C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+356D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+356E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+356F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3570
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3571
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3572
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3573
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3574
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3575
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3576
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3577
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3578
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3579
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+357A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+357B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+357C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+357D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+357E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+357F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3580
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3581
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3582
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3583
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3584
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3585
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3586
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3587
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3588
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3589
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+358A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+358B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+358C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+358D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+358E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+358F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3590
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3591
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3592
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3593
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3594
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3595
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3596
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3597
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3598
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3599
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+359A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+359B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+359C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+359D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+359E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+359F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+35FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3600
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3601
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3602
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3603
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3604
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3605
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3606
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3607
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3608
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3609
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+360A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+360B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+360C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+360D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+360E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+360F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3610
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3611
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3612
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3613
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3614
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3615
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3616
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3617
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3618
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3619
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+361A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+361B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+361C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+361D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+361E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+361F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3620
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3621
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3622
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3623
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3624
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3625
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3626
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3627
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3628
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3629
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+362A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+362B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+362C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+362D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+362E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+362F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3630
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3631
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3632
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3633
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3634
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3635
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3636
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3637
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3638
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3639
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+363A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+363B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+363C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+363D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+363E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+363F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3640
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3641
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3642
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3643
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3644
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3645
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3646
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3647
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3648
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3649
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+364A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+364B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+364C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+364D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+364E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+364F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3650
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3651
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3652
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3653
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3654
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3655
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3656
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3657
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3658
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3659
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+365A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+365B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+365C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+365D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+365E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+365F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3660
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3661
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3662
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3663
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3664
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3665
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3666
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3667
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3668
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3669
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+366A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+366B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+366C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+366D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+366E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+366F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3670
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3671
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3672
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3673
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3674
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3675
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3676
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3677
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3678
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3679
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+367A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+367B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+367C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+367D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+367E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+367F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3680
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3681
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3682
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3683
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3684
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3685
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3686
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3687
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3688
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3689
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+368A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+368B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+368C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+368D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+368E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+368F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3690
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3691
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3692
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3693
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3694
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3695
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3696
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3697
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3698
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3699
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+369A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+369B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+369C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+369D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+369E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+369F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+36FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3700
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3701
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3702
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3703
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3704
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3705
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3706
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3707
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3708
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3709
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+370A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+370B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+370C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+370D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+370E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+370F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3710
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3711
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3712
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3713
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3714
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3715
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3716
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3717
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3718
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3719
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+371A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+371B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+371C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+371D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+371E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+371F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3720
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3721
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3722
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3723
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3724
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3725
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3726
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3727
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3728
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3729
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+372A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+372B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+372C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+372D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+372E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+372F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3730
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3731
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3732
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3733
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3734
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3735
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3736
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3737
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3738
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3739
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+373A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+373B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+373C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+373D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+373E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+373F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3740
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3741
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3742
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3743
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3744
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3745
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3746
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3747
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3748
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3749
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+374A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+374B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+374C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+374D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+374E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+374F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3750
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3751
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3752
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3753
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3754
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3755
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3756
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3757
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3758
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3759
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+375A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+375B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+375C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+375D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+375E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+375F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3760
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3761
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3762
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3763
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3764
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3765
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3766
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3767
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3768
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3769
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+376A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+376B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+376C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+376D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+376E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+376F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3770
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3771
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3772
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3773
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3774
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3775
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3776
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3777
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3778
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3779
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+377A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+377B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+377C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+377D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+377E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+377F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3780
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3781
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3782
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3783
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3784
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3785
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3786
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3787
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3788
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3789
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+378A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+378B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+378C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+378D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+378E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+378F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3790
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3791
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3792
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3793
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3794
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3795
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3796
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3797
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3798
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3799
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+379A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+379B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+379C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+379D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+379E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+379F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+37FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3800
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3801
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3802
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3803
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3804
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3805
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3806
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3807
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3808
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3809
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+380A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+380B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+380C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+380D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+380E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+380F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3810
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3811
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3812
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3813
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3814
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3815
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3816
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3817
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3818
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3819
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+381A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+381B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+381C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+381D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+381E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+381F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3820
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3821
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3822
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3823
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3824
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3825
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3826
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3827
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3828
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3829
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+382A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+382B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+382C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+382D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+382E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+382F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3830
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3831
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3832
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3833
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3834
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3835
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3836
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3837
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3838
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3839
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+383A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+383B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+383C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+383D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+383E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+383F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3840
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3841
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3842
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3843
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3844
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3845
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3846
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3847
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3848
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3849
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+384A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+384B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+384C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+384D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+384E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+384F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3850
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3851
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3852
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3853
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3854
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3855
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3856
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3857
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3858
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3859
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+385A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+385B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+385C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+385D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+385E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+385F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3860
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3861
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3862
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3863
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3864
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3865
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3866
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3867
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3868
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3869
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+386A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+386B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+386C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+386D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+386E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+386F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3870
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3871
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3872
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3873
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3874
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3875
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3876
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3877
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3878
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3879
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+387A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+387B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+387C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+387D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+387E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+387F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3880
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3881
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3882
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3883
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3884
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3885
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3886
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3887
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3888
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3889
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+388A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+388B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+388C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+388D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+388E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+388F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3890
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3891
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3892
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3893
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3894
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3895
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3896
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3897
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3898
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3899
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+389A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+389B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+389C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+389D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+389E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+389F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+38FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+390A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+390B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+390C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+390D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+390E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+390F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+391A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+391B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+391C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+391D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+391E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+391F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+392A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+392B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+392C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+392D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+392E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+392F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+393A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+393B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+393C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+393D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+393E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+393F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+394A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+394B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+394C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+394D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+394E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+394F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+395A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+395B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+395C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+395D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+395E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+395F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+396A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+396B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+396C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+396D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+396E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+396F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+397A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+397B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+397C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+397D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+397E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+397F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+398A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+398B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+398C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+398D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+398E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+398F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+399A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+399B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+399C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+399D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+399E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+399F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+39FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ADA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ADB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ADC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ADD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ADE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3AFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+3FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4000
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4001
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4002
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4003
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4004
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4007
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4008
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4009
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+400A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+400B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+400C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+400D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+400E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+400F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4010
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4011
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4012
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4013
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4014
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4015
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4016
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4017
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4018
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4019
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+401A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+401B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+401C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+401D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+401E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+401F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4029
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+402A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+402B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+402C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+402D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+402E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+402F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4035
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4036
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+403A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+403B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+403C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+403D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+403E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+403F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4040
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4042
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4044
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4046
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4048
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+404A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+404B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+404C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+404D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+404E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+404F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+405A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+405B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+405C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+405D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+405E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+405F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4062
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+406A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+406B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+406C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+406D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+406E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+406F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+407A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+407B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+407C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+407D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+407E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+407F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4082
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4084
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4086
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+408A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+408B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+408C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+408D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+408E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+408F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4094
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4095
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4096
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4097
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4098
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4099
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+409A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+409B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+409C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+409D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+409E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+409F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+40FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4100
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4101
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4102
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4103
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+410A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+410B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+410C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+410D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+410E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+410F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+411A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+411B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+411C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+411D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+411E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+411F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+412A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+412B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+412C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+412D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+412E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+412F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+413A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+413B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+413C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+413D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+413E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+413F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+414A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+414B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+414C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+414D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+414E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+414F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+415A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+415B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+415C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+415D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+415E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+415F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+416A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+416B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+416C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+416D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+416E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+416F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+417A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+417B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+417C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+417D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+417E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+417F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+418A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+418B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+418C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+418D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+418E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+418F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+419A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+419B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+419C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+419D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+419E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+419F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+41FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+420A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+420B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+420C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+420D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+420E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+420F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+421A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+421B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+421C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+421D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+421E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+421F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+422A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+422B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+422C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+422D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+422E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+422F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+423A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+423B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+423C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+423D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+423E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+423F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4247
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4248
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4249
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+424A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+424B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+424C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+424D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+424E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+424F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4250
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4251
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4252
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4253
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4254
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4255
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4256
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4257
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4258
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4259
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+425A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+425B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+425C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+425D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+425E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+425F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+426A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+426B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+426C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+426D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+426E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+426F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+427A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+427B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+427C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+427D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+427E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+427F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+428A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+428B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+428C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+428D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+428E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+428F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+429A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+429B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+429C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+429D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+429E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+429F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+42FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+430A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+430B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+430C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+430D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+430E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+430F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+431A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+431B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+431C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+431D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+431E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+431F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+432A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+432B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+432C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+432D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+432E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+432F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+433A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+433B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+433C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+433D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+433E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+433F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+434A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+434B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+434C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+434D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+434E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+434F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+435A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+435B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+435C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+435D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+435E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+435F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+436A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+436B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+436C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+436D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+436E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+436F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4376
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4377
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4378
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4379
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+437A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+437B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+437C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+437D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+437E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+437F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+438A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+438B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+438C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+438D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+438E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+438F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+439A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+439B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+439C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+439D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+439E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+439F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+43FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+440A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+440B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+440C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+440D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+440E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+440F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+441A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+441B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+441C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+441D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+441E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+441F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+442A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+442B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+442C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+442D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+442E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+442F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+443A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+443B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+443C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+443D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+443E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+443F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+444A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+444B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+444C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+444D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+444E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+444F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+445A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+445B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+445C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+445D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+445E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+445F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+446A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+446B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+446C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+446D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+446E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+446F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+447A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+447B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+447C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+447D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+447E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+447F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+448A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+448B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+448C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+448D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+448E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+448F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4490
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4491
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4492
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4493
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4494
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4495
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4496
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4497
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4498
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4499
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+449A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+449B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+449C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+449D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+449E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+449F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+44FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4500
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4501
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4502
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4503
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4504
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4505
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4506
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4507
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4508
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4509
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+450A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+450B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+450C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+450D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+450E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+450F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4510
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4511
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4512
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4513
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4514
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4515
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4516
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4517
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4518
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4519
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+451A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+451B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+451C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+451D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+451E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+451F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4520
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4521
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4522
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4523
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4524
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4525
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4526
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4527
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4528
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4529
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+452A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+452B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+452C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+452D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+452E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+452F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4530
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4531
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4532
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4533
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4534
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4535
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4536
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4537
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4538
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4539
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+453A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+453B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+453C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+453D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+453E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+453F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4540
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4541
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4542
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4543
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4544
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4545
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4546
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4547
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4548
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4549
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+454A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+454B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+454C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+454D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+454E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+454F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4550
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4551
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4552
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4553
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4554
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4555
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4556
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4557
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4558
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4559
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+455A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+455B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+455C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+455D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+455E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+455F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4560
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4561
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4562
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4563
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4564
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4565
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4566
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4567
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4568
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4569
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+456A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+456B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+456C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+456D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+456E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+456F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4570
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4571
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4572
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4573
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4574
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4575
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4576
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4577
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4578
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4579
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+457A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+457B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+457C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+457D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+457E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+457F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4580
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4581
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4582
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4583
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4584
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4585
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4586
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4587
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4588
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4589
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+458A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+458B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+458C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+458D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+458E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+458F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4590
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4591
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4592
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4593
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4594
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4595
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4596
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4597
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4598
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4599
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+459A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+459B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+459C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+459D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+459E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+459F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+45FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4600
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4601
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4602
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4603
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4604
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4605
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4606
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4607
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4608
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4609
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+460A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+460B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+460C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+460D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+460E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+460F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4610
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4611
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4612
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4613
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4614
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4615
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4616
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4617
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4618
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4619
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+461A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+461B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+461C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+461D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+461E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+461F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4620
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4621
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4622
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4623
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4624
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4625
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4626
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4627
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4628
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4629
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+462A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+462B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+462C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+462D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+462E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+462F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4630
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4631
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4632
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4633
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4634
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4635
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4636
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4637
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4638
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4639
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+463A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+463B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+463C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+463D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+463E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+463F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4640
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4641
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4642
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4643
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4644
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4645
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4646
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4647
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4648
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4649
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+464A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+464B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+464C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+464D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+464E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+464F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4650
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4651
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4652
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4653
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4654
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4655
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4656
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4657
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4658
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4659
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+465A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+465B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+465C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+465D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+465E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+465F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4660
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4661
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4662
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4663
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4664
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4665
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4666
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4667
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4668
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4669
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+466A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+466B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+466C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+466D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+466E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+466F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4670
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4671
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4672
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4673
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4674
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4675
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4676
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4677
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4678
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4679
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+467A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+467B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+467C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+467D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+467E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+467F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4680
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4681
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4682
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4683
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4684
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4685
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4686
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4687
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4688
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4689
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+468A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+468B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+468C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+468D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+468E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+468F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4690
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4691
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4692
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4693
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4694
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4695
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4696
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4697
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4698
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4699
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+469A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+469B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+469C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+469D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+469E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+469F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+46FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4700
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4701
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4702
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4703
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4704
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4705
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4706
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4707
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4708
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4709
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+470A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+470B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+470C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+470D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+470E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+470F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4710
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4711
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4712
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4713
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4714
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4715
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4716
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4717
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4718
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4719
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+471A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+471B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+471C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+471D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+471E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+471F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4720
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4721
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4722
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4723
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4724
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4725
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4726
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4727
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4728
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4729
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+472A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+472B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+472C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+472D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+472E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+472F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4730
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4731
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4732
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4733
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4734
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4735
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4736
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4737
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4738
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4739
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+473A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+473B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+473C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+473D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+473E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+473F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4740
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4741
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4742
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4743
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4744
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4745
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4746
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4747
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4748
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4749
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+474A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+474B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+474C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+474D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+474E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+474F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4750
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4751
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4752
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4753
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4754
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4755
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4756
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4757
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4758
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4759
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+475A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+475B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+475C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+475D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+475E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+475F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4760
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4761
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4762
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4763
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4764
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4765
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4766
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4767
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4768
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4769
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+476A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+476B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+476C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+476D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+476E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+476F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4770
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4771
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4772
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4773
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4774
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4775
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4776
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4777
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4778
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4779
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+477A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+477B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+477C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+477D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+477E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+477F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4780
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4781
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4782
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4783
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4784
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4785
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4786
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4787
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4788
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4789
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+478A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+478B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+478C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+478D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+478E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+478F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4790
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4791
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4792
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4793
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4794
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4795
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4796
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4797
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4798
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4799
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+479A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+479B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+479C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+479D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+479E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+479F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+47FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4800
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4801
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4802
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4803
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4804
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4805
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4806
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4807
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4808
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4809
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+480A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+480B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+480C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+480D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+480E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+480F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4810
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4811
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4812
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4813
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4814
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4815
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4816
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4817
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4818
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4819
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+481A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+481B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+481C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+481D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+481E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+481F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4820
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4821
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4822
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4823
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4824
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4825
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4826
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4827
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4828
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4829
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+482A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+482B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+482C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+482D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+482E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+482F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4830
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4831
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4832
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4833
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4834
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4835
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4836
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4837
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4838
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4839
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+483A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+483B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+483C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+483D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+483E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+483F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4840
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4841
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4842
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4843
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4844
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4845
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4846
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4847
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4848
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4849
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+484A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+484B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+484C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+484D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+484E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+484F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4850
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4851
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4852
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4853
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4854
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4855
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4856
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4857
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4858
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4859
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+485A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+485B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+485C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+485D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+485E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+485F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4860
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4861
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4862
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4863
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4864
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4865
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4866
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4867
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4868
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4869
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+486A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+486B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+486C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+486D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+486E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+486F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4870
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4871
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4872
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4873
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4874
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4875
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4876
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4877
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4878
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4879
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+487A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+487B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+487C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+487D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+487E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+487F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4880
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4881
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4882
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4883
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4884
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4885
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4886
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4887
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4888
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4889
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+488A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+488B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+488C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+488D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+488E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+488F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4890
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4891
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4892
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4893
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4894
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4895
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4896
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4897
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4898
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4899
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+489A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+489B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+489C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+489D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+489E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+489F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+48FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+490A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+490B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+490C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+490D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+490E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+490F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+491A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+491B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+491C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+491D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+491E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+491F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+492A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+492B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+492C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+492D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+492E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+492F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+493A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+493B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+493C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+493D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+493E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+493F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+494A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+494B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+494C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+494D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+494E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+494F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+495A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+495B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+495C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+495D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+495E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+495F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+496A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+496B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+496C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+496D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+496E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+496F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+497A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+497B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+497C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+497D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+497E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+497F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+498A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+498B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+498C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+498D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+498E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+498F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+499A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+499B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+499C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+499D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+499E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+499F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+49FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ADA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ADB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ADC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ADD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ADE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4AFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4DB4
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+4DB5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DB6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DB7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DB8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DB9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DBA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DBB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DBC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DBD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+4DBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+4DBF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DC9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DCA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DCB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DCC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DCD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DCE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DCF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DD9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DDA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DDB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DDC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DDD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DDE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DDF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DE9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DEA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DEB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DEC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DEE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DEF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF0
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF1
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF2
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF3
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF4
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF5
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF6
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DF9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DFA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DFB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DFC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DFD
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DFE
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+4DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+4FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5000
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5001
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5002
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5003
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5004
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5007
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5008
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5009
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+500A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+500B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+500C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+500D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+500E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+500F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5010
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5011
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5012
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5013
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5014
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5015
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5016
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5017
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5018
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5019
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+501A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+501B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+501C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+501D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+501E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+501F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5029
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+502A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+502B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+502C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+502D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+502E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+502F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5035
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5036
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+503A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+503B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+503C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+503D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+503E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+503F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5040
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5042
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5044
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5046
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5048
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+504A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+504B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+504C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+504D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+504E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+504F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+505A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+505B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+505C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+505D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+505E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+505F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5062
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+506A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+506B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+506C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+506D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+506E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+506F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+507A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+507B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+507C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+507D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+507E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+507F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5082
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5084
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5086
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+508A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+508B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+508C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+508D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+508E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+508F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5094
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5095
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5096
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5097
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5098
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5099
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+509A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+509B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+509C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+509D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+509E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+509F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+50FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5100
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5101
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5102
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5103
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+510A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+510B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+510C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+510D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+510E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+510F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+511A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+511B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+511C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+511D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+511E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+511F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+512A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+512B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+512C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+512D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+512E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+512F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+513A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+513B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+513C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+513D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+513E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+513F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+514A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+514B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+514C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+514D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+514E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+514F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+515A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+515B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+515C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+515D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+515E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+515F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+516A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+516B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+516C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+516D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+516E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+516F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+517A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+517B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+517C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+517D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+517E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+517F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+518A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+518B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+518C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+518D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+518E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+518F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+519A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+519B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+519C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+519D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+519E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+519F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+51FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+520A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+520B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+520C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+520D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+520E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+520F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+521A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+521B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+521C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+521D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+521E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+521F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+522A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+522B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+522C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+522D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+522E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+522F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+523A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+523B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+523C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+523D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+523E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+523F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5247
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5248
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5249
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+524A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+524B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+524C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+524D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+524E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+524F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5250
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5251
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5252
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5253
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5254
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5255
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5256
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5257
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5258
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5259
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+525A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+525B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+525C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+525D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+525E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+525F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+526A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+526B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+526C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+526D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+526E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+526F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+527A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+527B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+527C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+527D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+527E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+527F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+528A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+528B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+528C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+528D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+528E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+528F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+529A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+529B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+529C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+529D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+529E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+529F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+52FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+530A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+530B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+530C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+530D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+530E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+530F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+531A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+531B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+531C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+531D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+531E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+531F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+532A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+532B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+532C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+532D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+532E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+532F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+533A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+533B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+533C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+533D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+533E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+533F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+534A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+534B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+534C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+534D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+534E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+534F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+535A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+535B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+535C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+535D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+535E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+535F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+536A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+536B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+536C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+536D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+536E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+536F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5376
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5377
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5378
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5379
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+537A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+537B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+537C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+537D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+537E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+537F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+538A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+538B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+538C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+538D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+538E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+538F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+539A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+539B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+539C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+539D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+539E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+539F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+53FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+540A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+540B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+540C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+540D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+540E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+540F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+541A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+541B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+541C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+541D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+541E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+541F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+542A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+542B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+542C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+542D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+542E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+542F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+543A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+543B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+543C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+543D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+543E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+543F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+544A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+544B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+544C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+544D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+544E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+544F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+545A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+545B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+545C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+545D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+545E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+545F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+546A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+546B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+546C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+546D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+546E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+546F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+547A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+547B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+547C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+547D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+547E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+547F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+548A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+548B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+548C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+548D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+548E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+548F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5490
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5491
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5492
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5493
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5494
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5495
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5496
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5497
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5498
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5499
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+549A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+549B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+549C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+549D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+549E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+549F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+54FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5500
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5501
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5502
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5503
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5504
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5505
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5506
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5507
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5508
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5509
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+550A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+550B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+550C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+550D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+550E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+550F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5510
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5511
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5512
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5513
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5514
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5515
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5516
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5517
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5518
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5519
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+551A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+551B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+551C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+551D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+551E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+551F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5520
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5521
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5522
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5523
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5524
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5525
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5526
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5527
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5528
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5529
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+552A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+552B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+552C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+552D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+552E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+552F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5530
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5531
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5532
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5533
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5534
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5535
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5536
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5537
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5538
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5539
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+553A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+553B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+553C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+553D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+553E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+553F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5540
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5541
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5542
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5543
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5544
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5545
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5546
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5547
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5548
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5549
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+554A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+554B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+554C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+554D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+554E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+554F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5550
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5551
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5552
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5553
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5554
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5555
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5556
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5557
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5558
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5559
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+555A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+555B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+555C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+555D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+555E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+555F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5560
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5561
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5562
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5563
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5564
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5565
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5566
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5567
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5568
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5569
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+556A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+556B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+556C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+556D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+556E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+556F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5570
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5571
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5572
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5573
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5574
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5575
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5576
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5577
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5578
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5579
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+557A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+557B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+557C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+557D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+557E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+557F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5580
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5581
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5582
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5583
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5584
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5585
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5586
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5587
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5588
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5589
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+558A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+558B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+558C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+558D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+558E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+558F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5590
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5591
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5592
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5593
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5594
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5595
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5596
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5597
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5598
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5599
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+559A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+559B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+559C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+559D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+559E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+559F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+55FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5600
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5601
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5602
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5603
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5604
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5605
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5606
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5607
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5608
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5609
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+560A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+560B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+560C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+560D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+560E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+560F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5610
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5611
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5612
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5613
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5614
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5615
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5616
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5617
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5618
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5619
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+561A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+561B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+561C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+561D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+561E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+561F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5620
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5621
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5622
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5623
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5624
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5625
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5626
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5627
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5628
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5629
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+562A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+562B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+562C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+562D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+562E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+562F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5630
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5631
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5632
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5633
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5634
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5635
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5636
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5637
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5638
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5639
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+563A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+563B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+563C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+563D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+563E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+563F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5640
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5641
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5642
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5643
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5644
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5645
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5646
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5647
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5648
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5649
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+564A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+564B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+564C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+564D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+564E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+564F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5650
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5651
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5652
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5653
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5654
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5655
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5656
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5657
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5658
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5659
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+565A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+565B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+565C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+565D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+565E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+565F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5660
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5661
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5662
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5663
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5664
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5665
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5666
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5667
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5668
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5669
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+566A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+566B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+566C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+566D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+566E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+566F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5670
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5671
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5672
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5673
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5674
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5675
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5676
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5677
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5678
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5679
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+567A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+567B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+567C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+567D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+567E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+567F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5680
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5681
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5682
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5683
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5684
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5685
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5686
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5687
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5688
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5689
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+568A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+568B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+568C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+568D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+568E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+568F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5690
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5691
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5692
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5693
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5694
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5695
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5696
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5697
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5698
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5699
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+569A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+569B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+569C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+569D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+569E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+569F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+56FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5700
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5701
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5702
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5703
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5704
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5705
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5706
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5707
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5708
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5709
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+570A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+570B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+570C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+570D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+570E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+570F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5710
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5711
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5712
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5713
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5714
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5715
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5716
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5717
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5718
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5719
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+571A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+571B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+571C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+571D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+571E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+571F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5720
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5721
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5722
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5723
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5724
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5725
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5726
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5727
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5728
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5729
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+572A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+572B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+572C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+572D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+572E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+572F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5730
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5731
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5732
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5733
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5734
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5735
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5736
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5737
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5738
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5739
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+573A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+573B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+573C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+573D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+573E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+573F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5740
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5741
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5742
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5743
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5744
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5745
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5746
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5747
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5748
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5749
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+574A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+574B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+574C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+574D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+574E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+574F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5750
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5751
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5752
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5753
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5754
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5755
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5756
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5757
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5758
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5759
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+575A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+575B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+575C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+575D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+575E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+575F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5760
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5761
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5762
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5763
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5764
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5765
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5766
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5767
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5768
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5769
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+576A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+576B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+576C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+576D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+576E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+576F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5770
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5771
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5772
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5773
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5774
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5775
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5776
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5777
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5778
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5779
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+577A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+577B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+577C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+577D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+577E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+577F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5780
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5781
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5782
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5783
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5784
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5785
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5786
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5787
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5788
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5789
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+578A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+578B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+578C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+578D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+578E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+578F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5790
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5791
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5792
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5793
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5794
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5795
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5796
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5797
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5798
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5799
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+579A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+579B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+579C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+579D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+579E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+579F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+57FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5800
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5801
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5802
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5803
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5804
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5805
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5806
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5807
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5808
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5809
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+580A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+580B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+580C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+580D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+580E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+580F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5810
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5811
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5812
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5813
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5814
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5815
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5816
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5817
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5818
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5819
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+581A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+581B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+581C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+581D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+581E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+581F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5820
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5821
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5822
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5823
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5824
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5825
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5826
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5827
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5828
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5829
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+582A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+582B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+582C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+582D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+582E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+582F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5830
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5831
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5832
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5833
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5834
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5835
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5836
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5837
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5838
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5839
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+583A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+583B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+583C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+583D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+583E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+583F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5840
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5841
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5842
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5843
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5844
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5845
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5846
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5847
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5848
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5849
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+584A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+584B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+584C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+584D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+584E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+584F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5850
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5851
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5852
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5853
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5854
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5855
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5856
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5857
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5858
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5859
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+585A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+585B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+585C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+585D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+585E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+585F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5860
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5861
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5862
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5863
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5864
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5865
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5866
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5867
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5868
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5869
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+586A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+586B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+586C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+586D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+586E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+586F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5870
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5871
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5872
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5873
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5874
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5875
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5876
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5877
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5878
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5879
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+587A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+587B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+587C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+587D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+587E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+587F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5880
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5881
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5882
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5883
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5884
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5885
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5886
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5887
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5888
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5889
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+588A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+588B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+588C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+588D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+588E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+588F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5890
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5891
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5892
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5893
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5894
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5895
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5896
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5897
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5898
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5899
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+589A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+589B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+589C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+589D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+589E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+589F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+58FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+590A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+590B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+590C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+590D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+590E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+590F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+591A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+591B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+591C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+591D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+591E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+591F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+592A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+592B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+592C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+592D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+592E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+592F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+593A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+593B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+593C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+593D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+593E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+593F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+594A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+594B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+594C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+594D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+594E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+594F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+595A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+595B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+595C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+595D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+595E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+595F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+596A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+596B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+596C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+596D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+596E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+596F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+597A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+597B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+597C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+597D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+597E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+597F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+598A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+598B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+598C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+598D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+598E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+598F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+599A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+599B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+599C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+599D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+599E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+599F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+59FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ADA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ADB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ADC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ADD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ADE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5AFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+5FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6000
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6001
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6002
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6003
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6004
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6007
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6008
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6009
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+600A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+600B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+600C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+600D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+600E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+600F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6010
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6011
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6012
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6013
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6014
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6015
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6016
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6017
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6018
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6019
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+601A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+601B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+601C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+601D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+601E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+601F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6029
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+602A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+602B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+602C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+602D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+602E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+602F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6035
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6036
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+603A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+603B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+603C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+603D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+603E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+603F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6040
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6042
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6044
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6046
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6048
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+604A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+604B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+604C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+604D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+604E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+604F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+605A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+605B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+605C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+605D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+605E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+605F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6062
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+606A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+606B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+606C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+606D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+606E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+606F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+607A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+607B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+607C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+607D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+607E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+607F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6082
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6084
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6086
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+608A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+608B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+608C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+608D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+608E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+608F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6094
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6095
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6096
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6097
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6098
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6099
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+609A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+609B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+609C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+609D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+609E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+609F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+60FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6100
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6101
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6102
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6103
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+610A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+610B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+610C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+610D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+610E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+610F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+611A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+611B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+611C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+611D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+611E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+611F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+612A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+612B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+612C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+612D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+612E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+612F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+613A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+613B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+613C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+613D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+613E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+613F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+614A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+614B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+614C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+614D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+614E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+614F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+615A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+615B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+615C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+615D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+615E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+615F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+616A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+616B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+616C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+616D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+616E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+616F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+617A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+617B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+617C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+617D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+617E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+617F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+618A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+618B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+618C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+618D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+618E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+618F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+619A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+619B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+619C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+619D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+619E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+619F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+61FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+620A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+620B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+620C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+620D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+620E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+620F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+621A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+621B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+621C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+621D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+621E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+621F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+622A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+622B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+622C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+622D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+622E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+622F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+623A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+623B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+623C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+623D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+623E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+623F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6247
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6248
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6249
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+624A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+624B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+624C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+624D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+624E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+624F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6250
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6251
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6252
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6253
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6254
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6255
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6256
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6257
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6258
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6259
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+625A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+625B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+625C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+625D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+625E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+625F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+626A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+626B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+626C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+626D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+626E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+626F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+627A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+627B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+627C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+627D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+627E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+627F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+628A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+628B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+628C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+628D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+628E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+628F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+629A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+629B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+629C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+629D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+629E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+629F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+62FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+630A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+630B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+630C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+630D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+630E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+630F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+631A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+631B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+631C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+631D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+631E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+631F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+632A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+632B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+632C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+632D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+632E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+632F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+633A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+633B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+633C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+633D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+633E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+633F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+634A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+634B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+634C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+634D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+634E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+634F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+635A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+635B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+635C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+635D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+635E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+635F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+636A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+636B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+636C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+636D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+636E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+636F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6376
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6377
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6378
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6379
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+637A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+637B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+637C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+637D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+637E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+637F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+638A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+638B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+638C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+638D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+638E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+638F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+639A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+639B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+639C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+639D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+639E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+639F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+63FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+640A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+640B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+640C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+640D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+640E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+640F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+641A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+641B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+641C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+641D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+641E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+641F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+642A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+642B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+642C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+642D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+642E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+642F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+643A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+643B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+643C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+643D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+643E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+643F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+644A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+644B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+644C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+644D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+644E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+644F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+645A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+645B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+645C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+645D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+645E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+645F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+646A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+646B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+646C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+646D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+646E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+646F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+647A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+647B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+647C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+647D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+647E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+647F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+648A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+648B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+648C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+648D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+648E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+648F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6490
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6491
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6492
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6493
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6494
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6495
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6496
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6497
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6498
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6499
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+649A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+649B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+649C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+649D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+649E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+649F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+64FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6500
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6501
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6502
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6503
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6504
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6505
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6506
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6507
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6508
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6509
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+650A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+650B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+650C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+650D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+650E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+650F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6510
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6511
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6512
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6513
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6514
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6515
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6516
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6517
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6518
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6519
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+651A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+651B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+651C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+651D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+651E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+651F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6520
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6521
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6522
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6523
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6524
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6525
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6526
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6527
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6528
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6529
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+652A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+652B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+652C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+652D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+652E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+652F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6530
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6531
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6532
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6533
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6534
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6535
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6536
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6537
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6538
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6539
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+653A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+653B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+653C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+653D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+653E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+653F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6540
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6541
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6542
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6543
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6544
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6545
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6546
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6547
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6548
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6549
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+654A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+654B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+654C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+654D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+654E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+654F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6550
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6551
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6552
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6553
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6554
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6555
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6556
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6557
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6558
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6559
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+655A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+655B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+655C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+655D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+655E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+655F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6560
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6561
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6562
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6563
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6564
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6565
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6566
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6567
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6568
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6569
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+656A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+656B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+656C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+656D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+656E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+656F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6570
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6571
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6572
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6573
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6574
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6575
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6576
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6577
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6578
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6579
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+657A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+657B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+657C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+657D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+657E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+657F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6580
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6581
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6582
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6583
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6584
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6585
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6586
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6587
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6588
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6589
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+658A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+658B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+658C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+658D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+658E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+658F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6590
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6591
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6592
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6593
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6594
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6595
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6596
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6597
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6598
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6599
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+659A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+659B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+659C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+659D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+659E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+659F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+65FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6600
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6601
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6602
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6603
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6604
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6605
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6606
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6607
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6608
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6609
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+660A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+660B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+660C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+660D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+660E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+660F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6610
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6611
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6612
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6613
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6614
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6615
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6616
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6617
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6618
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6619
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+661A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+661B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+661C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+661D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+661E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+661F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6620
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6621
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6622
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6623
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6624
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6625
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6626
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6627
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6628
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6629
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+662A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+662B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+662C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+662D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+662E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+662F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6630
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6631
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6632
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6633
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6634
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6635
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6636
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6637
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6638
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6639
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+663A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+663B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+663C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+663D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+663E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+663F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6640
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6641
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6642
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6643
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6644
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6645
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6646
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6647
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6648
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6649
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+664A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+664B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+664C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+664D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+664E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+664F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6650
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6651
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6652
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6653
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6654
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6655
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6656
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6657
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6658
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6659
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+665A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+665B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+665C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+665D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+665E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+665F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6660
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6661
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6662
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6663
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6664
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6665
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6666
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6667
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6668
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6669
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+666A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+666B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+666C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+666D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+666E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+666F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6670
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6671
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6672
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6673
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6674
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6675
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6676
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6677
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6678
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6679
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+667A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+667B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+667C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+667D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+667E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+667F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6680
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6681
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6682
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6683
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6684
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6685
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6686
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6687
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6688
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6689
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+668A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+668B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+668C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+668D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+668E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+668F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6690
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6691
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6692
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6693
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6694
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6695
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6696
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6697
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6698
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6699
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+669A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+669B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+669C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+669D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+669E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+669F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+66FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6700
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6701
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6702
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6703
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6704
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6705
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6706
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6707
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6708
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6709
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+670A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+670B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+670C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+670D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+670E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+670F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6710
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6711
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6712
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6713
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6714
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6715
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6716
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6717
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6718
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6719
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+671A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+671B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+671C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+671D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+671E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+671F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6720
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6721
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6722
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6723
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6724
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6725
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6726
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6727
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6728
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6729
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+672A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+672B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+672C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+672D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+672E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+672F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6730
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6731
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6732
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6733
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6734
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6735
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6736
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6737
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6738
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6739
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+673A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+673B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+673C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+673D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+673E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+673F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6740
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6741
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6742
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6743
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6744
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6745
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6746
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6747
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6748
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6749
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+674A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+674B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+674C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+674D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+674E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+674F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6750
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6751
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6752
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6753
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6754
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6755
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6756
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6757
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6758
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6759
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+675A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+675B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+675C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+675D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+675E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+675F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6760
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6761
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6762
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6763
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6764
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6765
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6766
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6767
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6768
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6769
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+676A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+676B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+676C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+676D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+676E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+676F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6770
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6771
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6772
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6773
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6774
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6775
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6776
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6777
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6778
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6779
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+677A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+677B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+677C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+677D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+677E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+677F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6780
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6781
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6782
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6783
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6784
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6785
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6786
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6787
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6788
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6789
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+678A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+678B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+678C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+678D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+678E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+678F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6790
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6791
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6792
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6793
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6794
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6795
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6796
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6797
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6798
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6799
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+679A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+679B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+679C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+679D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+679E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+679F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+67FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6800
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6801
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6802
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6803
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6804
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6805
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6806
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6807
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6808
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6809
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+680A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+680B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+680C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+680D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+680E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+680F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6810
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6811
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6812
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6813
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6814
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6815
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6816
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6817
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6818
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6819
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+681A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+681B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+681C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+681D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+681E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+681F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6820
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6821
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6822
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6823
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6824
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6825
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6826
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6827
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6828
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6829
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+682A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+682B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+682C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+682D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+682E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+682F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6830
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6831
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6832
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6833
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6834
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6835
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6836
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6837
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6838
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6839
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+683A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+683B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+683C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+683D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+683E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+683F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6840
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6841
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6842
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6843
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6844
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6845
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6846
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6847
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6848
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6849
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+684A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+684B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+684C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+684D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+684E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+684F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6850
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6851
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6852
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6853
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6854
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6855
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6856
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6857
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6858
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6859
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+685A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+685B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+685C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+685D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+685E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+685F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6860
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6861
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6862
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6863
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6864
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6865
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6866
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6867
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6868
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6869
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+686A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+686B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+686C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+686D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+686E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+686F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6870
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6871
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6872
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6873
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6874
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6875
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6876
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6877
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6878
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6879
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+687A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+687B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+687C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+687D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+687E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+687F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6880
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6881
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6882
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6883
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6884
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6885
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6886
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6887
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6888
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6889
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+688A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+688B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+688C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+688D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+688E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+688F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6890
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6891
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6892
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6893
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6894
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6895
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6896
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6897
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6898
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6899
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+689A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+689B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+689C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+689D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+689E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+689F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+68FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+690A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+690B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+690C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+690D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+690E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+690F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+691A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+691B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+691C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+691D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+691E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+691F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+692A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+692B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+692C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+692D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+692E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+692F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+693A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+693B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+693C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+693D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+693E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+693F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+694A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+694B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+694C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+694D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+694E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+694F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+695A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+695B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+695C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+695D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+695E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+695F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+696A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+696B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+696C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+696D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+696E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+696F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+697A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+697B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+697C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+697D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+697E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+697F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+698A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+698B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+698C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+698D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+698E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+698F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+699A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+699B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+699C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+699D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+699E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+699F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+69FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ADA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ADB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ADC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ADD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ADE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6AFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+6FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7000
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7001
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7002
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7003
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7004
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7007
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7008
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7009
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+700A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+700B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+700C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+700D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+700E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+700F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7010
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7011
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7012
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7013
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7014
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7015
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7016
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7017
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7018
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7019
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+701A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+701B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+701C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+701D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+701E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+701F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7029
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+702A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+702B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+702C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+702D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+702E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+702F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7035
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7036
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+703A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+703B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+703C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+703D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+703E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+703F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7040
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7042
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7044
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7046
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7048
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+704A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+704B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+704C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+704D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+704E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+704F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+705A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+705B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+705C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+705D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+705E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+705F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7062
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+706A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+706B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+706C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+706D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+706E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+706F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+707A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+707B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+707C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+707D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+707E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+707F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7082
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7084
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7086
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+708A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+708B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+708C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+708D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+708E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+708F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7094
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7095
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7096
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7097
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7098
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7099
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+709A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+709B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+709C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+709D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+709E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+709F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+70FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7100
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7101
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7102
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7103
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+710A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+710B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+710C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+710D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+710E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+710F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+711A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+711B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+711C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+711D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+711E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+711F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+712A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+712B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+712C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+712D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+712E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+712F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+713A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+713B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+713C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+713D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+713E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+713F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+714A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+714B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+714C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+714D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+714E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+714F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+715A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+715B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+715C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+715D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+715E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+715F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+716A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+716B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+716C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+716D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+716E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+716F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+717A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+717B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+717C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+717D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+717E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+717F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+718A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+718B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+718C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+718D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+718E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+718F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+719A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+719B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+719C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+719D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+719E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+719F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+71FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+720A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+720B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+720C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+720D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+720E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+720F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+721A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+721B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+721C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+721D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+721E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+721F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+722A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+722B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+722C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+722D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+722E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+722F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+723A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+723B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+723C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+723D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+723E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+723F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7247
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7248
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7249
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+724A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+724B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+724C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+724D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+724E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+724F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7250
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7251
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7252
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7253
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7254
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7255
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7256
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7257
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7258
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7259
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+725A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+725B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+725C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+725D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+725E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+725F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+726A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+726B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+726C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+726D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+726E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+726F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+727A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+727B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+727C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+727D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+727E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+727F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+728A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+728B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+728C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+728D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+728E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+728F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+729A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+729B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+729C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+729D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+729E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+729F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+72FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+730A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+730B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+730C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+730D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+730E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+730F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+731A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+731B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+731C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+731D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+731E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+731F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+732A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+732B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+732C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+732D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+732E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+732F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+733A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+733B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+733C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+733D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+733E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+733F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+734A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+734B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+734C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+734D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+734E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+734F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+735A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+735B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+735C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+735D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+735E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+735F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+736A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+736B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+736C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+736D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+736E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+736F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7376
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7377
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7378
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7379
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+737A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+737B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+737C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+737D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+737E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+737F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+738A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+738B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+738C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+738D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+738E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+738F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+739A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+739B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+739C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+739D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+739E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+739F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+73FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+740A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+740B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+740C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+740D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+740E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+740F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+741A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+741B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+741C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+741D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+741E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+741F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+742A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+742B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+742C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+742D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+742E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+742F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+743A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+743B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+743C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+743D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+743E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+743F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+744A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+744B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+744C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+744D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+744E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+744F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+745A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+745B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+745C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+745D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+745E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+745F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+746A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+746B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+746C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+746D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+746E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+746F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+747A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+747B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+747C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+747D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+747E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+747F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+748A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+748B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+748C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+748D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+748E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+748F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7490
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7491
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7492
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7493
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7494
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7495
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7496
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7497
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7498
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7499
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+749A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+749B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+749C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+749D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+749E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+749F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+74FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7500
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7501
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7502
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7503
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7504
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7505
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7506
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7507
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7508
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7509
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+750A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+750B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+750C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+750D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+750E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+750F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7510
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7511
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7512
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7513
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7514
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7515
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7516
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7517
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7518
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7519
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+751A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+751B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+751C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+751D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+751E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+751F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7520
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7521
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7522
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7523
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7524
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7525
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7526
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7527
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7528
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7529
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+752A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+752B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+752C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+752D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+752E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+752F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7530
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7531
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7532
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7533
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7534
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7535
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7536
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7537
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7538
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7539
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+753A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+753B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+753C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+753D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+753E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+753F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7540
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7541
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7542
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7543
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7544
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7545
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7546
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7547
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7548
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7549
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+754A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+754B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+754C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+754D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+754E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+754F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7550
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7551
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7552
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7553
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7554
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7555
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7556
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7557
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7558
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7559
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+755A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+755B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+755C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+755D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+755E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+755F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7560
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7561
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7562
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7563
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7564
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7565
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7566
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7567
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7568
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7569
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+756A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+756B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+756C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+756D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+756E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+756F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7570
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7571
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7572
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7573
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7574
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7575
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7576
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7577
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7578
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7579
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+757A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+757B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+757C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+757D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+757E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+757F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7580
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7581
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7582
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7583
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7584
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7585
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7586
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7587
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7588
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7589
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+758A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+758B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+758C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+758D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+758E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+758F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7590
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7591
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7592
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7593
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7594
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7595
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7596
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7597
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7598
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7599
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+759A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+759B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+759C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+759D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+759E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+759F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+75FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7600
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7601
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7602
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7603
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7604
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7605
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7606
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7607
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7608
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7609
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+760A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+760B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+760C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+760D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+760E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+760F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7610
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7611
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7612
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7613
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7614
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7615
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7616
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7617
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7618
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7619
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+761A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+761B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+761C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+761D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+761E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+761F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7620
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7621
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7622
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7623
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7624
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7625
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7626
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7627
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7628
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7629
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+762A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+762B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+762C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+762D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+762E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+762F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7630
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7631
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7632
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7633
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7634
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7635
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7636
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7637
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7638
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7639
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+763A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+763B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+763C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+763D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+763E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+763F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7640
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7641
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7642
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7643
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7644
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7645
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7646
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7647
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7648
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7649
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+764A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+764B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+764C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+764D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+764E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+764F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7650
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7651
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7652
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7653
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7654
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7655
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7656
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7657
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7658
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7659
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+765A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+765B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+765C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+765D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+765E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+765F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7660
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7661
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7662
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7663
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7664
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7665
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7666
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7667
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7668
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7669
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+766A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+766B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+766C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+766D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+766E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+766F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7670
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7671
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7672
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7673
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7674
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7675
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7676
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7677
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7678
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7679
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+767A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+767B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+767C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+767D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+767E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+767F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7680
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7681
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7682
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7683
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7684
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7685
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7686
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7687
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7688
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7689
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+768A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+768B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+768C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+768D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+768E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+768F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7690
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7691
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7692
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7693
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7694
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7695
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7696
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7697
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7698
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7699
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+769A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+769B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+769C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+769D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+769E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+769F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+76FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7700
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7701
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7702
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7703
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7704
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7705
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7706
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7707
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7708
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7709
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+770A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+770B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+770C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+770D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+770E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+770F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7710
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7711
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7712
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7713
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7714
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7715
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7716
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7717
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7718
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7719
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+771A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+771B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+771C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+771D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+771E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+771F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7720
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7721
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7722
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7723
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7724
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7725
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7726
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7727
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7728
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7729
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+772A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+772B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+772C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+772D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+772E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+772F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7730
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7731
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7732
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7733
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7734
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7735
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7736
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7737
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7738
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7739
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+773A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+773B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+773C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+773D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+773E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+773F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7740
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7741
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7742
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7743
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7744
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7745
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7746
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7747
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7748
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7749
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+774A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+774B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+774C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+774D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+774E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+774F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7750
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7751
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7752
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7753
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7754
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7755
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7756
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7757
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7758
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7759
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+775A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+775B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+775C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+775D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+775E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+775F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7760
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7761
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7762
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7763
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7764
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7765
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7766
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7767
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7768
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7769
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+776A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+776B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+776C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+776D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+776E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+776F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7770
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7771
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7772
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7773
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7774
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7775
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7776
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7777
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7778
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7779
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+777A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+777B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+777C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+777D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+777E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+777F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7780
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7781
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7782
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7783
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7784
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7785
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7786
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7787
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7788
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7789
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+778A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+778B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+778C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+778D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+778E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+778F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7790
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7791
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7792
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7793
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7794
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7795
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7796
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7797
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7798
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7799
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+779A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+779B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+779C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+779D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+779E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+779F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+77FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7800
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7801
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7802
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7803
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7804
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7805
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7806
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7807
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7808
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7809
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+780A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+780B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+780C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+780D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+780E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+780F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7810
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7811
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7812
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7813
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7814
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7815
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7816
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7817
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7818
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7819
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+781A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+781B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+781C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+781D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+781E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+781F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7820
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7821
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7822
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7823
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7824
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7825
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7826
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7827
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7828
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7829
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+782A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+782B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+782C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+782D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+782E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+782F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7830
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7831
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7832
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7833
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7834
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7835
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7836
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7837
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7838
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7839
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+783A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+783B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+783C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+783D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+783E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+783F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7840
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7841
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7842
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7843
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7844
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7845
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7846
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7847
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7848
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7849
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+784A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+784B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+784C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+784D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+784E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+784F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7850
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7851
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7852
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7853
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7854
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7855
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7856
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7857
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7858
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7859
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+785A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+785B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+785C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+785D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+785E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+785F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7860
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7861
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7862
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7863
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7864
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7865
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7866
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7867
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7868
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7869
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+786A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+786B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+786C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+786D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+786E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+786F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7870
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7871
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7872
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7873
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7874
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7875
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7876
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7877
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7878
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7879
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+787A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+787B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+787C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+787D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+787E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+787F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7880
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7881
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7882
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7883
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7884
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7885
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7886
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7887
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7888
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7889
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+788A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+788B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+788C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+788D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+788E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+788F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7890
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7891
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7892
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7893
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7894
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7895
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7896
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7897
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7898
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7899
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+789A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+789B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+789C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+789D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+789E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+789F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+78FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+790A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+790B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+790C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+790D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+790E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+790F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+791A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+791B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+791C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+791D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+791E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+791F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+792A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+792B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+792C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+792D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+792E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+792F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+793A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+793B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+793C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+793D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+793E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+793F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+794A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+794B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+794C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+794D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+794E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+794F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+795A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+795B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+795C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+795D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+795E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+795F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+796A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+796B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+796C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+796D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+796E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+796F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+797A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+797B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+797C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+797D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+797E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+797F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+798A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+798B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+798C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+798D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+798E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+798F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+799A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+799B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+799C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+799D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+799E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+799F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+79FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ADA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ADB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ADC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ADD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ADE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7AFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+7FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8000
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8001
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8002
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8003
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8004
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8007
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8008
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8009
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+800A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+800B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+800C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+800D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+800E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+800F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8010
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8011
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8012
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8013
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8014
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8015
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8016
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8017
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8018
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8019
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+801A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+801B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+801C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+801D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+801E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+801F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8029
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+802A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+802B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+802C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+802D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+802E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+802F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8035
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8036
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+803A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+803B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+803C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+803D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+803E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+803F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8040
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8042
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8044
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8046
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8048
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+804A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+804B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+804C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+804D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+804E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+804F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+805A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+805B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+805C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+805D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+805E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+805F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8062
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+806A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+806B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+806C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+806D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+806E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+806F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+807A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+807B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+807C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+807D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+807E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+807F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8082
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8084
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8086
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+808A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+808B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+808C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+808D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+808E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+808F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8094
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8095
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8096
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8097
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8098
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8099
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+809A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+809B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+809C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+809D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+809E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+809F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+80FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8100
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8101
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8102
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8103
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+810A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+810B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+810C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+810D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+810E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+810F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+811A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+811B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+811C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+811D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+811E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+811F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+812A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+812B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+812C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+812D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+812E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+812F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+813A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+813B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+813C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+813D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+813E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+813F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+814A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+814B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+814C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+814D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+814E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+814F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+815A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+815B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+815C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+815D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+815E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+815F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+816A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+816B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+816C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+816D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+816E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+816F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+817A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+817B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+817C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+817D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+817E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+817F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+818A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+818B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+818C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+818D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+818E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+818F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+819A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+819B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+819C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+819D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+819E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+819F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+81FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+820A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+820B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+820C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+820D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+820E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+820F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+821A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+821B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+821C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+821D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+821E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+821F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+822A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+822B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+822C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+822D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+822E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+822F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+823A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+823B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+823C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+823D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+823E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+823F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8247
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8248
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8249
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+824A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+824B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+824C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+824D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+824E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+824F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8250
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8251
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8252
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8253
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8254
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8255
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8256
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8257
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8258
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8259
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+825A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+825B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+825C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+825D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+825E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+825F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+826A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+826B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+826C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+826D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+826E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+826F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+827A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+827B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+827C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+827D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+827E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+827F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+828A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+828B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+828C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+828D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+828E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+828F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+829A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+829B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+829C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+829D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+829E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+829F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+82FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+830A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+830B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+830C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+830D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+830E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+830F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+831A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+831B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+831C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+831D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+831E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+831F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+832A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+832B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+832C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+832D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+832E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+832F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+833A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+833B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+833C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+833D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+833E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+833F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+834A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+834B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+834C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+834D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+834E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+834F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+835A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+835B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+835C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+835D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+835E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+835F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+836A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+836B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+836C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+836D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+836E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+836F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8376
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8377
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8378
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8379
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+837A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+837B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+837C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+837D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+837E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+837F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+838A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+838B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+838C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+838D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+838E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+838F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+839A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+839B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+839C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+839D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+839E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+839F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+83FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+840A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+840B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+840C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+840D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+840E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+840F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+841A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+841B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+841C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+841D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+841E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+841F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+842A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+842B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+842C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+842D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+842E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+842F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+843A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+843B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+843C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+843D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+843E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+843F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+844A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+844B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+844C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+844D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+844E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+844F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+845A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+845B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+845C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+845D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+845E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+845F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+846A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+846B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+846C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+846D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+846E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+846F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+847A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+847B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+847C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+847D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+847E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+847F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+848A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+848B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+848C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+848D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+848E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+848F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8490
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8491
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8492
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8493
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8494
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8495
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8496
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8497
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8498
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8499
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+849A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+849B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+849C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+849D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+849E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+849F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+84FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8500
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8501
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8502
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8503
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8504
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8505
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8506
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8507
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8508
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8509
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+850A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+850B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+850C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+850D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+850E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+850F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8510
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8511
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8512
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8513
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8514
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8515
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8516
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8517
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8518
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8519
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+851A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+851B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+851C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+851D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+851E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+851F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8520
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8521
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8522
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8523
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8524
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8525
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8526
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8527
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8528
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8529
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+852A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+852B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+852C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+852D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+852E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+852F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8530
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8531
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8532
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8533
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8534
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8535
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8536
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8537
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8538
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8539
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+853A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+853B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+853C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+853D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+853E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+853F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8540
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8541
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8542
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8543
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8544
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8545
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8546
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8547
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8548
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8549
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+854A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+854B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+854C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+854D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+854E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+854F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8550
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8551
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8552
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8553
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8554
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8555
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8556
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8557
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8558
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8559
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+855A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+855B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+855C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+855D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+855E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+855F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8560
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8561
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8562
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8563
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8564
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8565
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8566
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8567
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8568
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8569
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+856A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+856B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+856C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+856D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+856E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+856F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8570
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8571
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8572
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8573
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8574
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8575
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8576
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8577
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8578
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8579
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+857A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+857B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+857C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+857D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+857E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+857F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8580
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8581
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8582
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8583
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8584
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8585
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8586
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8587
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8588
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8589
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+858A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+858B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+858C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+858D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+858E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+858F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8590
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8591
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8592
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8593
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8594
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8595
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8596
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8597
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8598
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8599
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+859A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+859B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+859C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+859D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+859E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+859F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+85FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8600
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8601
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8602
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8603
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8604
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8605
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8606
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8607
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8608
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8609
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+860A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+860B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+860C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+860D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+860E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+860F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8610
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8611
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8612
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8613
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8614
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8615
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8616
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8617
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8618
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8619
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+861A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+861B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+861C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+861D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+861E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+861F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8620
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8621
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8622
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8623
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8624
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8625
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8626
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8627
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8628
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8629
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+862A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+862B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+862C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+862D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+862E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+862F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8630
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8631
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8632
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8633
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8634
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8635
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8636
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8637
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8638
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8639
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+863A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+863B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+863C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+863D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+863E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+863F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8640
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8641
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8642
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8643
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8644
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8645
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8646
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8647
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8648
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8649
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+864A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+864B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+864C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+864D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+864E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+864F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8650
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8651
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8652
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8653
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8654
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8655
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8656
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8657
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8658
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8659
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+865A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+865B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+865C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+865D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+865E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+865F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8660
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8661
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8662
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8663
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8664
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8665
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8666
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8667
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8668
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8669
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+866A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+866B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+866C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+866D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+866E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+866F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8670
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8671
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8672
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8673
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8674
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8675
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8676
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8677
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8678
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8679
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+867A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+867B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+867C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+867D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+867E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+867F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8680
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8681
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8682
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8683
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8684
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8685
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8686
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8687
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8688
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8689
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+868A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+868B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+868C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+868D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+868E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+868F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8690
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8691
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8692
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8693
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8694
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8695
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8696
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8697
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8698
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8699
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+869A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+869B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+869C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+869D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+869E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+869F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+86FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8700
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8701
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8702
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8703
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8704
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8705
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8706
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8707
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8708
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8709
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+870A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+870B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+870C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+870D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+870E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+870F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8710
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8711
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8712
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8713
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8714
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8715
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8716
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8717
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8718
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8719
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+871A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+871B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+871C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+871D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+871E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+871F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8720
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8721
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8722
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8723
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8724
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8725
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8726
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8727
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8728
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8729
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+872A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+872B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+872C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+872D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+872E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+872F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8730
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8731
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8732
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8733
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8734
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8735
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8736
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8737
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8738
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8739
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+873A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+873B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+873C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+873D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+873E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+873F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8740
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8741
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8742
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8743
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8744
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8745
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8746
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8747
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8748
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8749
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+874A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+874B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+874C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+874D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+874E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+874F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8750
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8751
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8752
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8753
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8754
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8755
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8756
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8757
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8758
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8759
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+875A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+875B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+875C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+875D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+875E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+875F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8760
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8761
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8762
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8763
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8764
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8765
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8766
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8767
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8768
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8769
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+876A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+876B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+876C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+876D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+876E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+876F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8770
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8771
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8772
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8773
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8774
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8775
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8776
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8777
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8778
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8779
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+877A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+877B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+877C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+877D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+877E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+877F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8780
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8781
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8782
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8783
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8784
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8785
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8786
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8787
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8788
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8789
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+878A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+878B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+878C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+878D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+878E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+878F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8790
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8791
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8792
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8793
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8794
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8795
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8796
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8797
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8798
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8799
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+879A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+879B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+879C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+879D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+879E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+879F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+87FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8800
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8801
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8802
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8803
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8804
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8805
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8806
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8807
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8808
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8809
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+880A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+880B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+880C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+880D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+880E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+880F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8810
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8811
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8812
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8813
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8814
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8815
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8816
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8817
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8818
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8819
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+881A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+881B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+881C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+881D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+881E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+881F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8820
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8821
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8822
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8823
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8824
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8825
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8826
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8827
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8828
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8829
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+882A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+882B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+882C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+882D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+882E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+882F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8830
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8831
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8832
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8833
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8834
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8835
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8836
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8837
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8838
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8839
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+883A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+883B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+883C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+883D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+883E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+883F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8840
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8841
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8842
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8843
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8844
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8845
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8846
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8847
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8848
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8849
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+884A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+884B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+884C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+884D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+884E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+884F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8850
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8851
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8852
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8853
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8854
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8855
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8856
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8857
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8858
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8859
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+885A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+885B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+885C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+885D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+885E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+885F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8860
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8861
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8862
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8863
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8864
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8865
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8866
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8867
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8868
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8869
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+886A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+886B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+886C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+886D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+886E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+886F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8870
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8871
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8872
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8873
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8874
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8875
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8876
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8877
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8878
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8879
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+887A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+887B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+887C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+887D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+887E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+887F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8880
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8881
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8882
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8883
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8884
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8885
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8886
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8887
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8888
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8889
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+888A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+888B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+888C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+888D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+888E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+888F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8890
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8891
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8892
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8893
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8894
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8895
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8896
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8897
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8898
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8899
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+889A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+889B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+889C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+889D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+889E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+889F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+88FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+890A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+890B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+890C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+890D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+890E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+890F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+891A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+891B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+891C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+891D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+891E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+891F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+892A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+892B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+892C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+892D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+892E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+892F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+893A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+893B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+893C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+893D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+893E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+893F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+894A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+894B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+894C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+894D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+894E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+894F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+895A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+895B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+895C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+895D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+895E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+895F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+896A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+896B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+896C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+896D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+896E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+896F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+897A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+897B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+897C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+897D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+897E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+897F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+898A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+898B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+898C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+898D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+898E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+898F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+899A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+899B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+899C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+899D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+899E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+899F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+89FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ADA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ADB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ADC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ADD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ADE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8AFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+8FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9000
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9001
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9002
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9003
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9004
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9007
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9008
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9009
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+900A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+900B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+900C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+900D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+900E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+900F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9010
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9011
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9012
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9013
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9014
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9015
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9016
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9017
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9018
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9019
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+901A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+901B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+901C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+901D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+901E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+901F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9029
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+902A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+902B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+902C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+902D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+902E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+902F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9035
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9036
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+903A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+903B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+903C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+903D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+903E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+903F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9040
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9042
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9044
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9046
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9048
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+904A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+904B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+904C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+904D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+904E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+904F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+905A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+905B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+905C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+905D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+905E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+905F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9062
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+906A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+906B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+906C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+906D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+906E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+906F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+907A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+907B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+907C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+907D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+907E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+907F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9082
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9084
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9086
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+908A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+908B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+908C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+908D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+908E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+908F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9094
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9095
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9096
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9097
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9098
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9099
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+909A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+909B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+909C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+909D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+909E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+909F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+90FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9100
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9101
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9102
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9103
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+910A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+910B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+910C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+910D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+910E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+910F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+911A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+911B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+911C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+911D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+911E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+911F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+912A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+912B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+912C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+912D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+912E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+912F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+913A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+913B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+913C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+913D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+913E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+913F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+914A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+914B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+914C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+914D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+914E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+914F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+915A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+915B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+915C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+915D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+915E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+915F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+916A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+916B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+916C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+916D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+916E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+916F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+917A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+917B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+917C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+917D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+917E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+917F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+918A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+918B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+918C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+918D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+918E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+918F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+919A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+919B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+919C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+919D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+919E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+919F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+91FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+920A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+920B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+920C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+920D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+920E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+920F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+921A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+921B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+921C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+921D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+921E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+921F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+922A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+922B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+922C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+922D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+922E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+922F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+923A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+923B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+923C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+923D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+923E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+923F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9247
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9248
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9249
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+924A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+924B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+924C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+924D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+924E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+924F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9250
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9251
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9252
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9253
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9254
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9255
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9256
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9257
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9258
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9259
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+925A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+925B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+925C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+925D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+925E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+925F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+926A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+926B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+926C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+926D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+926E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+926F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+927A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+927B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+927C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+927D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+927E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+927F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+928A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+928B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+928C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+928D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+928E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+928F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+929A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+929B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+929C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+929D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+929E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+929F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+92FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+930A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+930B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+930C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+930D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+930E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+930F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+931A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+931B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+931C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+931D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+931E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+931F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+932A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+932B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+932C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+932D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+932E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+932F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+933A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+933B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+933C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+933D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+933E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+933F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+934A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+934B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+934C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+934D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+934E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+934F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+935A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+935B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+935C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+935D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+935E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+935F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+936A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+936B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+936C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+936D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+936E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+936F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9376
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9377
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9378
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9379
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+937A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+937B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+937C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+937D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+937E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+937F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+938A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+938B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+938C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+938D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+938E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+938F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+939A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+939B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+939C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+939D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+939E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+939F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+93FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+940A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+940B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+940C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+940D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+940E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+940F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+941A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+941B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+941C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+941D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+941E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+941F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+942A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+942B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+942C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+942D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+942E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+942F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+943A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+943B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+943C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+943D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+943E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+943F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+944A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+944B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+944C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+944D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+944E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+944F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+945A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+945B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+945C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+945D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+945E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+945F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+946A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+946B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+946C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+946D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+946E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+946F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+947A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+947B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+947C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+947D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+947E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+947F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+948A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+948B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+948C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+948D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+948E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+948F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9490
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9491
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9492
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9493
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9494
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9495
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9496
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9497
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9498
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9499
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+949A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+949B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+949C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+949D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+949E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+949F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+94FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9500
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9501
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9502
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9503
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9504
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9505
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9506
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9507
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9508
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9509
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+950A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+950B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+950C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+950D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+950E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+950F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9510
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9511
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9512
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9513
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9514
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9515
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9516
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9517
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9518
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9519
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+951A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+951B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+951C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+951D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+951E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+951F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9520
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9521
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9522
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9523
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9524
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9525
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9526
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9527
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9528
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9529
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+952A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+952B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+952C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+952D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+952E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+952F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9530
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9531
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9532
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9533
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9534
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9535
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9536
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9537
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9538
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9539
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+953A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+953B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+953C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+953D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+953E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+953F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9540
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9541
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9542
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9543
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9544
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9545
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9546
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9547
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9548
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9549
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+954A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+954B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+954C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+954D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+954E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+954F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9550
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9551
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9552
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9553
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9554
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9555
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9556
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9557
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9558
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9559
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+955A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+955B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+955C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+955D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+955E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+955F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9560
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9561
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9562
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9563
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9564
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9565
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9566
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9567
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9568
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9569
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+956A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+956B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+956C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+956D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+956E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+956F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9570
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9571
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9572
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9573
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9574
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9575
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9576
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9577
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9578
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9579
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+957A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+957B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+957C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+957D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+957E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+957F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9580
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9581
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9582
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9583
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9584
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9585
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9586
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9587
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9588
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9589
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+958A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+958B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+958C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+958D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+958E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+958F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9590
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9591
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9592
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9593
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9594
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9595
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9596
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9597
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9598
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9599
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+959A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+959B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+959C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+959D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+959E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+959F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+95FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9600
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9601
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9602
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9603
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9604
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9605
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9606
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9607
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9608
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9609
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+960A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+960B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+960C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+960D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+960E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+960F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9610
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9611
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9612
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9613
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9614
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9615
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9616
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9617
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9618
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9619
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+961A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+961B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+961C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+961D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+961E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+961F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9620
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9621
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9622
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9623
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9624
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9625
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9626
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9627
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9628
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9629
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+962A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+962B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+962C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+962D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+962E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+962F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9630
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9631
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9632
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9633
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9634
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9635
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9636
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9637
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9638
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9639
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+963A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+963B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+963C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+963D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+963E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+963F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9640
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9641
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9642
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9643
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9644
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9645
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9646
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9647
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9648
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9649
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+964A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+964B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+964C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+964D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+964E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+964F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9650
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9651
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9652
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9653
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9654
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9655
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9656
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9657
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9658
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9659
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+965A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+965B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+965C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+965D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+965E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+965F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9660
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9661
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9662
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9663
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9664
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9665
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9666
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9667
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9668
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9669
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+966A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+966B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+966C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+966D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+966E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+966F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9670
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9671
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9672
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9673
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9674
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9675
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9676
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9677
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9678
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9679
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+967A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+967B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+967C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+967D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+967E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+967F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9680
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9681
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9682
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9683
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9684
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9685
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9686
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9687
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9688
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9689
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+968A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+968B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+968C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+968D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+968E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+968F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9690
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9691
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9692
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9693
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9694
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9695
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9696
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9697
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9698
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9699
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+969A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+969B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+969C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+969D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+969E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+969F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+96FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9700
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9701
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9702
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9703
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9704
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9705
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9706
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9707
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9708
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9709
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+970A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+970B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+970C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+970D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+970E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+970F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9710
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9711
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9712
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9713
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9714
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9715
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9716
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9717
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9718
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9719
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+971A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+971B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+971C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+971D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+971E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+971F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9720
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9721
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9722
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9723
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9724
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9725
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9726
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9727
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9728
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9729
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+972A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+972B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+972C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+972D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+972E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+972F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9730
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9731
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9732
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9733
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9734
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9735
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9736
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9737
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9738
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9739
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+973A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+973B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+973C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+973D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+973E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+973F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9740
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9741
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9742
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9743
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9744
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9745
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9746
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9747
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9748
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9749
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+974A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+974B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+974C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+974D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+974E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+974F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9750
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9751
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9752
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9753
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9754
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9755
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9756
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9757
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9758
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9759
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+975A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+975B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+975C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+975D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+975E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+975F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9760
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9761
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9762
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9763
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9764
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9765
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9766
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9767
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9768
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9769
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+976A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+976B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+976C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+976D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+976E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+976F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9770
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9771
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9772
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9773
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9774
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9775
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9776
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9777
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9778
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9779
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+977A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+977B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+977C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+977D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+977E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+977F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9780
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9781
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9782
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9783
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9784
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9785
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9786
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9787
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9788
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9789
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+978A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+978B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+978C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+978D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+978E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+978F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9790
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9791
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9792
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9793
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9794
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9795
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9796
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9797
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9798
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9799
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+979A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+979B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+979C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+979D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+979E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+979F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+97FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9800
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9801
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9802
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9803
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9804
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9805
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9806
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9807
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9808
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9809
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+980A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+980B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+980C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+980D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+980E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+980F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9810
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9811
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9812
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9813
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9814
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9815
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9816
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9817
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9818
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9819
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+981A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+981B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+981C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+981D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+981E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+981F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9820
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9821
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9822
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9823
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9824
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9825
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9826
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9827
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9828
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9829
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+982A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+982B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+982C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+982D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+982E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+982F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9830
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9831
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9832
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9833
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9834
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9835
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9836
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9837
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9838
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9839
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+983A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+983B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+983C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+983D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+983E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+983F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9840
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9841
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9842
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9843
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9844
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9845
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9846
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9847
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9848
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9849
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+984A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+984B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+984C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+984D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+984E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+984F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9850
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9851
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9852
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9853
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9854
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9855
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9856
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9857
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9858
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9859
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+985A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+985B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+985C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+985D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+985E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+985F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9860
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9861
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9862
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9863
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9864
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9865
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9866
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9867
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9868
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9869
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+986A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+986B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+986C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+986D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+986E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+986F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9870
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9871
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9872
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9873
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9874
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9875
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9876
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9877
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9878
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9879
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+987A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+987B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+987C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+987D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+987E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+987F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9880
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9881
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9882
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9883
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9884
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9885
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9886
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9887
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9888
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9889
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+988A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+988B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+988C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+988D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+988E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+988F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9890
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9891
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9892
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9893
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9894
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9895
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9896
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9897
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9898
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9899
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+989A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+989B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+989C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+989D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+989E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+989F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+98FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+990A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+990B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+990C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+990D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+990E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+990F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+991A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+991B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+991C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+991D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+991E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+991F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+992A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+992B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+992C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+992D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+992E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+992F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+993A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+993B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+993C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+993D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+993E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+993F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+994A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+994B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+994C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+994D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+994E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+994F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+995A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+995B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+995C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+995D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+995E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+995F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+996A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+996B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+996C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+996D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+996E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+996F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+997A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+997B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+997C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+997D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+997E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+997F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+998A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+998B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+998C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+998D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+998E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+998F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+999A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+999B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+999C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+999D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+999E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+999F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+99FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9A9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ADA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ADB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ADC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ADD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ADE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ADF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9AFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9B9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9BFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9C9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9CFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9D9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DCA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DCB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DCC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DCD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DCE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DCF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DD9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9DFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9E9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ECA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ECB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ECC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ECD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ECE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ECF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9ED9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EDA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EDB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EDC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EDD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EDE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EDF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EE9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EEA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EEB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EEC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EEE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EEF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EF9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EFA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EFB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EFC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EFD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EFE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9EFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F6D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F6E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9F9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FBA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FBB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FBC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FBD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FBE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FBF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+9FCA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+9FCB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FCC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FCD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FCE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FCF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FD9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FDA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FDB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FDC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FDD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FDE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FDF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FE9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FEA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FEB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FEC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FED
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FEE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FEF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FF9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FFA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FFB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FFC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FFD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+9FFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+9FFF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A000
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A001
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A002
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A003
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A004
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A005
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A006
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A007
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A008
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A009
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A00A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A00B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A00C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A00D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A00E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A00F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A010
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A011
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A012
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A013
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A014
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+A015
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A016
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A017
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A018
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A019
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A01A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A01B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A01C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A01D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A01E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A01F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A020
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A021
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A022
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A023
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A024
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A025
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A026
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A027
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A028
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A029
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A02A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A02B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A02C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A02D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A02E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A02F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A030
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A031
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A032
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A033
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A034
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A035
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A036
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A037
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A038
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A039
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A03A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A03B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A03C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A03D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A03E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A03F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A040
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A041
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A042
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A043
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A044
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A045
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A046
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A047
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A048
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A049
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A04A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A04B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A04C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A04D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A04E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A04F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A050
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A051
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A052
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A053
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A054
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A055
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A056
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A057
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A058
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A059
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A05A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A05B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A05C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A05D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A05E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A05F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A060
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A061
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A062
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A063
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A064
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A065
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A066
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A067
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A068
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A069
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A06A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A06B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A06C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A06D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A06E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A06F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A070
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A071
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A072
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A073
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A074
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A075
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A076
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A077
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A078
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A079
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A07A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A07B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A07C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A07D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A07E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A07F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A080
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A081
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A082
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A083
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A084
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A085
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A086
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A087
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A088
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A089
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A08A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A08B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A08C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A08D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A08E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A08F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A090
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A091
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A092
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A093
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A094
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A095
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A096
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A097
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A098
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A099
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A09A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A09B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A09C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A09D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A09E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A09F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A0FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A100
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A101
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A102
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A103
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A104
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A105
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A106
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A107
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A108
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A109
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A10A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A10B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A10C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A10D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A10E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A10F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A110
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A111
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A112
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A113
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A114
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A115
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A116
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A117
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A118
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A119
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A11A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A11B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A11C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A11D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A11E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A11F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A120
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A121
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A122
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A123
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A124
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A125
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A126
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A127
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A128
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A129
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A12A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A12B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A12C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A12D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A12E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A12F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A130
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A131
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A132
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A133
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A134
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A135
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A136
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A137
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A138
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A139
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A13A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A13B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A13C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A13D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A13E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A13F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A140
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A141
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A142
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A143
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A144
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A145
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A146
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A147
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A148
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A149
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A14A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A14B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A14C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A14D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A14E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A14F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A150
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A151
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A152
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A153
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A154
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A155
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A156
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A157
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A158
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A159
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A15A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A15B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A15C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A15D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A15E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A15F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A160
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A161
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A162
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A163
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A164
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A165
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A166
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A167
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A168
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A169
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A16A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A16B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A16C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A16D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A16E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A16F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A170
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A171
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A172
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A173
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A174
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A175
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A176
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A177
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A178
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A179
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A17A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A17B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A17C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A17D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A17E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A17F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A180
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A181
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A182
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A183
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A184
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A185
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A186
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A187
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A188
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A189
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A18A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A18B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A18C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A18D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A18E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A18F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A190
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A191
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A192
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A193
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A194
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A195
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A196
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A197
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A198
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A199
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A19A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A19B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A19C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A19D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A19E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A19F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A1FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A200
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A201
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A202
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A203
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A204
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A205
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A206
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A207
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A208
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A209
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A20A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A20B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A20C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A20D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A20E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A20F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A210
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A211
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A212
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A213
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A214
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A215
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A216
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A217
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A218
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A219
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A21A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A21B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A21C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A21D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A21E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A21F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A220
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A221
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A222
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A223
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A224
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A225
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A226
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A227
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A228
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A229
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A22A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A22B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A22C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A22D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A22E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A22F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A230
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A231
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A232
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A233
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A234
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A235
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A236
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A237
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A238
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A239
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A23A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A23B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A23C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A23D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A23E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A23F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A240
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A241
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A242
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A243
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A244
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A245
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A246
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A247
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A248
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A249
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A24A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A24B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A24C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A24D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A24E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A24F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A250
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A251
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A252
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A253
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A254
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A255
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A256
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A257
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A258
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A259
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A25A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A25B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A25C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A25D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A25E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A25F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A260
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A261
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A262
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A263
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A264
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A265
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A266
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A267
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A268
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A269
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A26A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A26B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A26C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A26D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A26E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A26F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A270
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A271
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A272
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A273
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A274
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A275
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A276
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A277
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A278
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A279
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A27A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A27B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A27C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A27D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A27E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A27F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A280
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A281
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A282
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A283
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A284
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A285
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A286
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A287
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A288
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A289
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A28A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A28B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A28C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A28D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A28E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A28F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A290
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A291
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A292
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A293
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A294
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A295
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A296
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A297
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A298
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A299
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A29A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A29B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A29C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A29D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A29E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A29F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A2FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A300
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A301
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A302
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A303
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A304
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A305
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A306
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A307
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A308
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A309
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A30A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A30B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A30C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A30D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A30E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A30F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A310
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A311
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A312
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A313
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A314
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A315
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A316
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A317
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A318
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A319
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A31A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A31B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A31C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A31D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A31E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A31F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A320
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A321
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A322
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A323
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A324
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A325
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A326
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A327
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A328
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A329
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A32A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A32B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A32C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A32D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A32E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A32F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A330
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A331
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A332
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A333
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A334
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A335
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A336
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A337
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A338
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A339
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A33A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A33B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A33C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A33D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A33E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A33F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A340
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A341
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A342
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A343
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A344
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A345
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A346
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A347
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A348
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A349
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A34A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A34B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A34C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A34D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A34E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A34F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A350
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A351
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A352
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A353
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A354
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A355
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A356
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A357
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A358
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A359
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A35A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A35B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A35C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A35D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A35E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A35F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A360
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A361
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A362
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A363
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A364
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A365
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A366
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A367
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A368
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A369
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A36A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A36B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A36C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A36D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A36E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A36F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A370
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A371
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A372
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A373
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A374
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A375
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A376
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A377
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A378
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A379
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A37A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A37B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A37C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A37D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A37E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A37F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A380
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A381
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A382
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A383
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A384
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A385
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A386
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A387
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A388
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A389
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A38A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A38B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A38C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A38D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A38E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A38F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A390
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A391
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A392
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A393
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A394
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A395
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A396
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A397
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A398
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A399
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A39A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A39B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A39C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A39D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A39E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A39F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A3FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A400
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A401
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A402
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A403
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A404
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A405
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A406
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A407
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A408
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A409
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A40A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A40B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A40C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A40D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A40E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A40F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A410
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A411
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A412
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A413
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A414
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A415
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A416
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A417
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A418
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A419
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A41A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A41B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A41C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A41D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A41E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A41F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A420
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A421
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A422
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A423
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A424
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A425
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A426
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A427
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A428
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A429
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A42A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A42B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A42C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A42D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A42E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A42F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A430
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A431
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A432
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A433
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A434
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A435
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A436
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A437
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A438
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A439
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A43A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A43B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A43C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A43D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A43E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A43F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A440
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A441
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A442
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A443
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A444
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A445
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A446
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A447
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A448
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A449
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A44A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A44B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A44C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A44D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A44E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A44F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A450
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A451
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A452
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A453
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A454
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A455
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A456
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A457
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A458
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A459
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A45A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A45B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A45C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A45D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A45E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A45F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A460
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A461
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A462
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A463
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A464
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A465
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A466
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A467
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A468
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A469
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A46A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A46B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A46C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A46D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A46E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A46F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A470
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A471
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A472
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A473
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A474
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A475
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A476
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A477
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A478
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A479
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A47A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A47B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A47C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A47D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A47E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A47F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A480
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A481
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A482
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A483
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A484
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A485
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A486
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A487
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A488
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A489
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A48A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A48B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+A48C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A48D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A48E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A48F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A490
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A491
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A492
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A493
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A494
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A495
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A496
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A497
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A498
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A499
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A49A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A49B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A49C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A49D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A49E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A49F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4A9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4AA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4AB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4AC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4AD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4AE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4AF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4B9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4BA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4BB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4BC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4BD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4BE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4BF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4C0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4C1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4C2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4C3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4C4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4C5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+A4C6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4C7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4C8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4CA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4CB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4CC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4CE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A4CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A4FD
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A4FE
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A4FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A500
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A501
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A502
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A503
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A504
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A505
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A506
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A507
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A508
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A509
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A50A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A50B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A50C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A50D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A50E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A50F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A510
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A511
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A512
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A513
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A514
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A515
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A516
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A517
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A518
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A519
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A51A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A51B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A51C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A51D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A51E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A51F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A520
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A521
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A522
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A523
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A524
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A525
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A526
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A527
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A528
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A529
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A52A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A52B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A52C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A52D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A52E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A52F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A530
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A531
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A532
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A533
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A534
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A535
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A536
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A537
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A538
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A539
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A53A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A53B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A53C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A53D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A53E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A53F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A540
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A541
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A542
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A543
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A544
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A545
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A546
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A547
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A548
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A549
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A54A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A54B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A54C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A54D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A54E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A54F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A550
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A551
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A552
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A553
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A554
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A555
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A556
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A557
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A558
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A559
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A55A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A55B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A55C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A55D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A55E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A55F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A560
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A561
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A562
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A563
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A564
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A565
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A566
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A567
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A568
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A569
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A56A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A56B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A56C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A56D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A56E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A56F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A570
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A571
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A572
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A573
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A574
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A575
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A576
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A577
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A578
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A579
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A57A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A57B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A57C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A57D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A57E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A57F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A580
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A581
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A582
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A583
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A584
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A585
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A586
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A587
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A588
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A589
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A58A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A58B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A58C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A58D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A58E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A58F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A590
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A591
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A592
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A593
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A594
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A595
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A596
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A597
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A598
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A599
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A59A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A59B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A59C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A59D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A59E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A59F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5EF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A5FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A600
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A601
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A602
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A603
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A604
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A605
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A606
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A607
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A608
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A609
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A60A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A60B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A60C
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+A60D
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+A60E
+  CHARPROP____(0x1ffu, kNormal, kON, kBA)  // U+A60F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A610
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A611
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A612
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A613
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A614
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A615
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A616
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A617
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A618
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A619
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A61A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A61B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A61C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A61D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A61E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A61F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A620
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A621
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A622
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A623
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A624
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A625
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A626
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A627
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A628
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A629
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A62A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A62B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A62C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A62D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A62E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A62F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A630
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A631
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A632
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A633
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A634
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A635
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A636
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A637
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A638
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A639
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A63A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A63B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A63C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A63D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A63E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A63F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A640
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A641
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A642
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A643
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A644
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A645
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A646
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A647
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A648
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A649
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A64A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A64B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A64C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A64D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A64E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A64F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A650
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A651
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A652
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A653
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A654
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A655
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A656
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A657
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A658
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A659
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A65A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A65B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A65C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A65D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A65E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A65F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A660
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A661
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A662
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A663
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A664
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A665
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A666
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A667
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A668
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A669
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A66A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A66B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A66C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A66D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A66E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A66F
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+A670
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+A671
+  CHARPROP____(0x1ffu, kNormal, kNSM, kCM)  // U+A672
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A673
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A674
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A675
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A676
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A677
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A678
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A679
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A67A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A67B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A67C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A67D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A67E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A67F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A680
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A681
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A682
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A683
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A684
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A685
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A686
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A687
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A688
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A689
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A68A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A68B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A68C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A68D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A68E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A68F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A690
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A691
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A692
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A693
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A694
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A695
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A696
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A697
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A698
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A699
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A69A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A69B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A69C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A69D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A69E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A69F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6B9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6BA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6BB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6BC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6BD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6BE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6BF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6CD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6CF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6D9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6DA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6DB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6DC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6DF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6E9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6EA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6EB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6EC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6ED
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6EE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6EF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A6F0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A6F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A6F2
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A6F3
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A6F4
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A6F5
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A6F6
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A6F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A6FF
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A700
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A701
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A702
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A703
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A704
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A705
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A706
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A707
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A708
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A709
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A70A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A70B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A70C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A70D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A70E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A70F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A710
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A711
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A712
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A713
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A714
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A715
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A716
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A717
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A718
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A719
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A71A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A71B
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A71C
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A71D
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A71E
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A71F
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A720
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A721
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A722
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A723
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A724
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A725
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A726
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A727
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A728
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A729
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A72A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A72B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A72C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A72D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A72E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A72F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A730
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A731
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A732
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A733
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A734
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A735
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A736
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A737
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A738
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A739
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A73A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A73B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A73C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A73D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A73E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A73F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A740
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A741
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A742
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A743
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A744
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A745
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A746
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A747
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A748
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A749
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A74A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A74B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A74C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A74D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A74E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A74F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A750
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A751
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A752
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A753
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A754
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A755
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A756
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A757
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A758
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A759
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A75A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A75B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A75C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A75D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A75E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A75F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A760
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A761
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A762
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A763
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A764
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A765
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A766
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A767
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A768
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A769
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A76A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A76B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A76C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A76D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A76E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A76F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A770
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A771
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A772
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A773
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A774
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A775
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A776
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A777
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A778
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A779
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A77A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A77B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A77C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A77D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A77E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A77F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A780
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A781
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A782
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A783
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A784
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A785
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A786
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A787
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A788
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A789
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A78A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A78B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A78C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A78D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A78E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A78F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A790
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A791
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A792
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A793
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A794
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A795
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A796
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A797
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A798
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A799
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A79A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A79B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A79C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A79D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A79E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A79F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7A9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7AA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7AB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7AC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7AD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7AE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7AF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7B9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7BA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7BB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7BC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7BD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7BE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7BF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7CA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7CB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7CC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7CE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7CF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7D9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7DA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7DB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7DC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7DD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7DE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7DF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7E9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7EA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7EB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7EC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7ED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7EE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7EF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A7FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A7FB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A7FC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A7FD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A7FE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A7FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A800
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A801
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A802
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A803
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A804
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A805
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A806
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A807
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A808
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A809
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A80A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A80B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A80C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A80D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A80E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A80F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A810
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A811
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A812
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A813
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A814
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A815
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A816
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A817
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A818
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A819
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A81A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A81B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A81C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A81D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A81E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A81F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A820
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A821
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A822
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A823
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A824
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A825
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A826
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A827
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A828
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A829
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A82A
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+A82B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A82C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A82D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A82E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A82F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A830
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A831
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A832
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A833
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A834
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A835
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A836
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A837
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+A838
+  CHARPROP____(0x1ffu, kNormal, kET, kAL)  // U+A839
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A83A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A83B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A83C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A83D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A83E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A83F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A840
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A841
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A842
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A843
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A844
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A845
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A846
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A847
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A848
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A849
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A84A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A84B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A84C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A84D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A84E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A84F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A850
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A851
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A852
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A853
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A854
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A855
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A856
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A857
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A858
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A859
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A85A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A85B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A85C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A85D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A85E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A85F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A860
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A861
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A862
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A863
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A864
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A865
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A866
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A867
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A868
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A869
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A86A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A86B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A86C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A86D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A86E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A86F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A870
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A871
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A872
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A873
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+A874
+  CHARPROP____(0x1ffu, kNormal, kON, kBB)  // U+A875
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+A876
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+A877
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A878
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A879
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A87A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A87B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A87C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A87D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A87E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A87F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A880
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A881
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A882
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A883
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A884
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A885
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A886
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A887
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A888
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A889
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A88A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A88B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A88C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A88D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A88E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A88F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A890
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A891
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A892
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A893
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A894
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A895
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A896
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A897
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A898
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A899
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A89A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A89B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A89C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A89D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A89E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A89F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8B2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8B3
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8B4
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8B5
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8B6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8B7
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8B8
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8B9
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8BA
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8BB
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8BC
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8BD
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8BE
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8BF
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8C0
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8C1
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8C2
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A8C3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8C4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8C5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8C6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8C7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8C8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8CA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8CB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8CC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8CD
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A8CE
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A8CF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D0
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D1
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D2
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D3
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D4
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A8D9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8DA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8DB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8DC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8DD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8DE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8DF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8E9
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8EA
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8EB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8EC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8ED
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8EE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8EF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8F0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A8F1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8F9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8FA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A8FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A8FF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A900
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A901
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A902
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A903
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A904
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A905
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A906
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A907
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A908
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A909
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A90A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A90B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A90C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A90D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A90E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A90F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A910
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A911
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A912
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A913
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A914
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A915
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A916
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A917
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A918
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A919
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A91A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A91B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A91C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A91D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A91E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A91F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A920
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A921
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A922
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A923
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A924
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A925
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A926
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A927
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A928
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A929
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A92A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A92B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A92C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A92D
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A92E
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A92F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A930
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A931
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A932
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A933
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A934
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A935
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A936
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A937
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A938
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A939
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A93A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A93B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A93C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A93D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A93E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A93F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A940
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A941
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A942
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A943
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A944
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A945
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A946
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A947
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A948
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A949
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A94A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A94B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A94C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A94D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A94E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A94F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A950
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A951
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A952
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A953
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A954
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A955
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A956
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A957
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A958
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A959
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A95A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A95B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A95C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A95D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A95E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A95F
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A960
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A961
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A962
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A963
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A964
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A965
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A966
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A967
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A968
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A969
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A96A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A96B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A96C
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A96D
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A96E
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A96F
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A970
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A971
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A972
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A973
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A974
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A975
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A976
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A977
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A978
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A979
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A97A
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A97B
+  CHARPROP____(0x1ffu, kNormal, kL, kJL)  // U+A97C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A97D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A97E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A97F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A980
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A981
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A982
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A983
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A984
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A985
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A986
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A987
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A988
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A989
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A98A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A98B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A98C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A98D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A98E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A98F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A990
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A991
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A992
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A993
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A994
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A995
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A996
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A997
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A998
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A999
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A99A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A99B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A99C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A99D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A99E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A99F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9A9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9AA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9AB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9AC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9AD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9AE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9AF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9B0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9B1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9B2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A9B3
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9B4
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9B5
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A9B6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A9B7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A9B8
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A9B9
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9BA
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9BB
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+A9BC
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9BD
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9BE
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9BF
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+A9C0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9C1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9C2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9C3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9C4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9C5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9C6
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A9C7
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A9C8
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+A9C9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9CA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9CB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9CC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9CD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9CE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9CF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D0
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D1
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D2
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D3
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D4
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+A9D9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9DA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9DB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9DC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9DD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9DE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+A9DF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9E9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9EA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9EB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9EC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9ED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9EE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9EF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9F9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9FA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+A9FF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA06
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA07
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA08
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA09
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA0A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA0B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA0C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA0D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA0E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA0F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA10
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA11
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA17
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA18
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA19
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA1A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA1B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA1C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA1D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA1E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA1F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA20
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA21
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA22
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA23
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA24
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA25
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA26
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA27
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA28
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA29
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA2A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA2B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA2C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA2D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA2E
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+AA2F
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+AA30
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA31
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA32
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+AA33
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+AA34
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA35
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA36
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA37
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA38
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA3B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA3C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA3D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA3E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA3F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA40
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA41
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA42
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA43
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA44
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA45
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA46
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA47
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA48
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA49
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA4A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA4B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+AA4C
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+AA4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA4F
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA50
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA51
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA52
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA53
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA54
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA55
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA56
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA57
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA58
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+AA59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA5A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA5B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+AA5C
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+AA5D
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+AA5E
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+AA5F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA60
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA61
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA62
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA63
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA64
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA65
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA66
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA67
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA68
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA69
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA6A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA6B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA6C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA6D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA6E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA6F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA70
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA71
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA72
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA73
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA74
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA75
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA76
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA77
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA78
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA79
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA7A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AA7F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA80
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA81
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA82
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA83
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA84
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA85
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA86
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA87
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA88
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA89
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA8A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA8B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA8C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA8D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA8E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA8F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA90
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA91
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA92
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA93
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA94
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA95
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA96
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA97
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA98
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA99
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA9A
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA9B
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA9C
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA9D
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA9E
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AA9F
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA2
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA3
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA6
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA7
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA8
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAA9
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAAA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAAB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAAC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAAD
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAAE
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAAF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AAB0
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAB1
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AAB2
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AAB3
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AAB4
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAB5
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAB6
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AAB7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AAB8
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAB9
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AABA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AABB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AABC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AABD
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AABE
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AABF
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAC0
+  CHARPROP____(0x1ffu, kCombination, kNSM, kSA)  // U+AAC1
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AAC2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAC3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAC4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAC5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAC6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAC7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAC9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AACA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AACB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AACC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AACD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AACE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AACF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAD9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AADA
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AADB
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AADC
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AADD
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AADE
+  CHARPROP____(0x1ffu, kNormal, kL, kSA)  // U+AADF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAE9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAEA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAEB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAEC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAEF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AAFF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB00
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB01
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB02
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB03
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB04
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB05
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB06
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB07
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB08
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB09
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB0A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB0B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB0C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB0D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB0E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB0F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB10
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB11
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB12
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB13
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB14
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB15
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB16
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB17
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB18
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB19
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB1A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB1B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB1C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB1D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB1E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB1F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB20
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB21
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB22
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB23
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB24
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB25
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB26
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB27
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB28
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB29
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB2A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB2B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB2C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB2D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB2E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB2F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB30
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB31
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB32
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB33
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB34
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB35
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB36
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB37
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB38
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB39
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB3A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB3B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB3C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB3D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB3E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB3F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB40
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB41
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB42
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB43
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB44
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB45
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB46
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB47
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB48
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB49
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB4A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB4B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB4C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB4D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB4E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB4F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB50
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB51
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB52
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB53
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB54
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB55
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB56
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB57
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB58
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB59
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB5A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB5B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB5C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB5D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB5E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB5F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB60
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB61
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB62
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB63
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB64
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB65
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB66
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB67
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB68
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB69
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB6A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB6B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB6C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB6D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB6E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB6F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB70
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB71
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB72
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB73
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB74
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB75
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB76
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB77
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB78
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB79
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB7A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB7B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB7C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB7D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB7F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB80
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB81
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB82
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB83
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB84
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB85
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB86
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB87
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB88
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB89
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB8A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB8B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB8C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB8D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB8E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB8F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB90
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB91
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB92
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB93
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB94
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB95
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB96
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB97
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB98
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB99
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB9A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB9B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB9C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB9D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB9E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+AB9F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABA9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABAA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABAB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABAC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABAD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABAE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABAF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB1
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB2
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABB9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABBA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABBB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABBC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABBD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABBF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABC9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABCA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABCB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABCC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABCD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABCE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABCF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABD9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABDA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABDB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABDC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABDD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABDE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABDF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABE0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABE1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+ABE2
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+ABE3
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+ABE4
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+ABE5
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+ABE6
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+ABE7
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+ABE8
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+ABE9
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+ABEA
+  CHARPROP____(0x1ffu, kNormal, kL, kBA)  // U+ABEB
+  CHARPROP____(0x1ffu, kNormal, kL, kCM)  // U+ABEC
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+ABED
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABEF
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF0
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF1
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF2
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF3
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF4
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF5
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF6
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF7
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF8
+  CHARPROP____(0x1ffu, kNormal, kL, kNU)  // U+ABF9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABFA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABFB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABFC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABFD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+ABFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AC00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC1A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AC1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AC1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC36
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AC37
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AC38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC52
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AC53
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AC54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC6E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AC6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AC70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC8A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AC8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AC8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AC9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ACA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ACA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ACC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ACC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACDE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ACDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ACE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACFA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ACFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ACFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ACFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD16
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AD17
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AD18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD32
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AD33
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AD34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD4E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AD4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AD50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD6A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AD6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AD6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD86
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AD87
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AD88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AD9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ADA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ADA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADBE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ADBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ADC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADDA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ADDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ADDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+ADF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+ADF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+ADFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE12
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AE13
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AE14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE2E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AE2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AE30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE4A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AE4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AE4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE66
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AE67
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AE68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE82
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AE83
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AE84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AE9E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AE9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AEA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEBA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AEBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AEBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AECA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AECB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AECC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AECD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AECE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AECF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AED7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AED8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AED9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AEF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AEF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AEFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF0E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AF0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AF10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF2A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AF2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AF2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF46
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AF47
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AF48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF62
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AF63
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AF64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF7E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AF7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AF80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF9A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AF9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AF9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AF9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AFB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AFB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AFD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AFD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFEE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+AFEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+AFF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+AFFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B000
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B001
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B002
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B003
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B004
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B005
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B006
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B007
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B008
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B009
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B00A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B00B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B00C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B00D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B00E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B00F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B010
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B011
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B012
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B013
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B014
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B015
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B016
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B017
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B018
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B019
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B01A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B01B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B01C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B01D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B01E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B01F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B020
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B021
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B022
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B023
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B024
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B025
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B026
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B027
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B028
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B029
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B02A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B02B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B02C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B02D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B02E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B02F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B030
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B031
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B032
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B033
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B034
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B035
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B036
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B037
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B038
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B039
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B03A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B03B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B03C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B03D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B03E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B03F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B040
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B041
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B042
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B043
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B044
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B045
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B046
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B047
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B048
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B049
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B04A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B04B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B04C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B04D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B04E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B04F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B050
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B051
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B052
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B053
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B054
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B055
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B056
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B057
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B058
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B059
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B05A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B05B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B05C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B05D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B05E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B05F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B060
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B061
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B062
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B063
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B064
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B065
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B066
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B067
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B068
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B069
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B06A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B06B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B06C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B06D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B06E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B06F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B070
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B071
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B072
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B073
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B074
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B075
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B076
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B077
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B078
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B079
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B07A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B07B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B07C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B07D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B07E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B07F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B080
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B081
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B082
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B083
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B084
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B085
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B086
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B087
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B088
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B089
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B08A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B08B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B08C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B08D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B08E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B08F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B090
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B091
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B092
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B093
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B094
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B095
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B096
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B097
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B098
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B099
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B09A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B09B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B09C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B09D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B09E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B09F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B0B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B0B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0CE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B0CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B0D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0EA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B0EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B0EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B0FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B100
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B101
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B102
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B103
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B104
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B105
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B106
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B107
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B108
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B109
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B10A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B10B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B10C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B10D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B10E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B10F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B110
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B111
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B112
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B113
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B114
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B115
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B116
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B117
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B118
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B119
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B11A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B11B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B11C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B11D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B11E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B11F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B120
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B121
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B122
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B123
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B124
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B125
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B126
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B127
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B128
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B129
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B12A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B12B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B12C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B12D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B12E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B12F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B130
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B131
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B132
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B133
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B134
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B135
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B136
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B137
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B138
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B139
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B13A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B13B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B13C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B13D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B13E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B13F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B140
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B141
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B142
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B143
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B144
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B145
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B146
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B147
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B148
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B149
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B14A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B14B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B14C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B14D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B14E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B14F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B150
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B151
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B152
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B153
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B154
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B155
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B156
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B157
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B158
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B159
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B15A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B15B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B15C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B15D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B15E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B15F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B160
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B161
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B162
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B163
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B164
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B165
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B166
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B167
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B168
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B169
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B16A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B16B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B16C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B16D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B16E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B16F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B170
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B171
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B172
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B173
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B174
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B175
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B176
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B177
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B178
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B179
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B17A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B17B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B17C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B17D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B17E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B17F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B180
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B181
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B182
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B183
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B184
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B185
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B186
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B187
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B188
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B189
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B18A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B18B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B18C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B18D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B18E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B18F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B190
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B191
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B192
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B193
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B194
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B195
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B196
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B197
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B198
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B199
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B19A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B19B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B19C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B19D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B19E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B19F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1AE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B1AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B1B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1CA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B1CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B1CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B1E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B1E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B1FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B200
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B201
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B202
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B203
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B204
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B205
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B206
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B207
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B208
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B209
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B20A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B20B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B20C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B20D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B20E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B20F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B210
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B211
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B212
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B213
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B214
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B215
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B216
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B217
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B218
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B219
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B21A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B21B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B21C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B21D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B21E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B21F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B220
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B221
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B222
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B223
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B224
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B225
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B226
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B227
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B228
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B229
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B22A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B22B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B22C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B22D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B22E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B22F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B230
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B231
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B232
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B233
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B234
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B235
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B236
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B237
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B238
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B239
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B23A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B23B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B23C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B23D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B23E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B23F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B240
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B241
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B242
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B243
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B244
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B245
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B246
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B247
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B248
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B249
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B24A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B24B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B24C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B24D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B24E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B24F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B250
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B251
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B252
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B253
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B254
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B255
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B256
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B257
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B258
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B259
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B25A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B25B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B25C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B25D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B25E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B25F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B260
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B261
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B262
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B263
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B264
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B265
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B266
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B267
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B268
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B269
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B26A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B26B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B26C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B26D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B26E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B26F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B270
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B271
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B272
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B273
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B274
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B275
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B276
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B277
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B278
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B279
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B27A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B27B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B27C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B27D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B27E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B27F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B280
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B281
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B282
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B283
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B284
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B285
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B286
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B287
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B288
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B289
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B28A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B28B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B28C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B28D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B28E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B28F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B290
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B291
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B292
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B293
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B294
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B295
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B296
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B297
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B298
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B299
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B29A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B29B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B29C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B29D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B29E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B29F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2AA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B2AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B2AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B2C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B2C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B2E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B2E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B2FE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B2FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B300
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B301
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B302
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B303
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B304
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B305
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B306
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B307
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B308
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B309
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B30A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B30B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B30C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B30D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B30E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B30F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B310
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B311
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B312
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B313
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B314
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B315
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B316
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B317
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B318
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B319
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B31A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B31B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B31C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B31D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B31E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B31F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B320
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B321
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B322
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B323
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B324
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B325
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B326
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B327
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B328
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B329
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B32A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B32B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B32C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B32D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B32E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B32F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B330
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B331
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B332
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B333
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B334
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B335
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B336
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B337
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B338
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B339
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B33A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B33B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B33C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B33D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B33E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B33F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B340
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B341
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B342
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B343
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B344
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B345
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B346
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B347
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B348
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B349
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B34A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B34B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B34C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B34D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B34E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B34F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B350
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B351
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B352
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B353
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B354
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B355
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B356
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B357
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B358
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B359
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B35A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B35B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B35C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B35D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B35E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B35F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B360
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B361
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B362
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B363
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B364
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B365
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B366
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B367
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B368
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B369
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B36A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B36B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B36C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B36D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B36E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B36F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B370
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B371
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B372
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B373
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B374
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B375
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B376
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B377
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B378
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B379
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B37A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B37B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B37C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B37D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B37E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B37F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B380
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B381
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B382
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B383
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B384
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B385
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B386
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B387
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B388
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B389
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B38A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B38B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B38C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B38D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B38E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B38F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B390
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B391
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B392
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B393
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B394
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B395
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B396
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B397
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B398
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B399
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B39A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B39B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B39C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B39D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B39E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B39F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B3A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B3A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B3C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B3C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3DE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B3DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B3E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3FA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B3FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B3FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B3FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B400
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B401
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B402
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B403
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B404
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B405
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B406
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B407
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B408
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B409
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B40A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B40B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B40C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B40D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B40E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B40F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B410
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B411
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B412
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B413
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B414
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B415
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B416
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B417
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B418
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B419
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B41A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B41B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B41C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B41D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B41E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B41F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B420
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B421
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B422
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B423
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B424
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B425
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B426
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B427
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B428
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B429
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B42A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B42B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B42C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B42D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B42E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B42F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B430
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B431
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B432
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B433
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B434
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B435
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B436
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B437
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B438
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B439
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B43A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B43B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B43C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B43D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B43E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B43F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B440
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B441
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B442
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B443
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B444
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B445
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B446
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B447
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B448
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B449
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B44A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B44B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B44C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B44D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B44E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B44F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B450
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B451
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B452
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B453
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B454
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B455
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B456
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B457
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B458
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B459
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B45A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B45B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B45C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B45D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B45E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B45F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B460
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B461
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B462
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B463
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B464
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B465
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B466
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B467
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B468
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B469
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B46A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B46B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B46C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B46D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B46E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B46F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B470
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B471
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B472
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B473
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B474
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B475
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B476
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B477
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B478
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B479
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B47A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B47B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B47C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B47D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B47E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B47F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B480
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B481
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B482
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B483
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B484
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B485
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B486
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B487
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B488
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B489
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B48A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B48B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B48C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B48D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B48E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B48F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B490
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B491
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B492
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B493
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B494
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B495
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B496
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B497
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B498
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B499
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B49A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B49B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B49C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B49D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B49E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B49F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B4A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B4A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4BE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B4BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B4C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4DA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B4DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B4DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B4F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B4F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B4FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B500
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B501
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B502
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B503
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B504
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B505
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B506
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B507
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B508
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B509
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B50A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B50B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B50C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B50D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B50E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B50F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B510
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B511
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B512
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B513
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B514
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B515
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B516
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B517
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B518
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B519
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B51A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B51B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B51C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B51D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B51E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B51F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B520
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B521
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B522
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B523
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B524
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B525
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B526
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B527
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B528
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B529
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B52A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B52B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B52C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B52D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B52E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B52F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B530
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B531
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B532
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B533
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B534
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B535
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B536
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B537
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B538
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B539
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B53A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B53B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B53C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B53D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B53E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B53F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B540
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B541
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B542
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B543
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B544
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B545
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B546
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B547
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B548
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B549
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B54A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B54B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B54C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B54D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B54E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B54F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B550
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B551
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B552
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B553
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B554
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B555
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B556
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B557
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B558
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B559
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B55A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B55B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B55C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B55D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B55E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B55F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B560
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B561
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B562
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B563
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B564
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B565
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B566
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B567
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B568
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B569
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B56A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B56B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B56C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B56D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B56E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B56F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B570
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B571
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B572
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B573
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B574
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B575
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B576
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B577
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B578
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B579
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B57A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B57B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B57C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B57D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B57E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B57F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B580
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B581
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B582
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B583
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B584
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B585
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B586
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B587
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B588
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B589
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B58A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B58B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B58C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B58D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B58E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B58F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B590
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B591
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B592
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B593
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B594
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B595
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B596
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B597
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B598
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B599
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B59A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B59B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B59C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B59D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B59E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B59F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B5A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5BA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B5BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B5BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B5D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B5D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B5F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B5F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B5FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B600
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B601
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B602
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B603
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B604
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B605
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B606
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B607
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B608
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B609
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B60A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B60B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B60C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B60D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B60E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B60F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B610
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B611
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B612
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B613
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B614
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B615
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B616
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B617
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B618
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B619
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B61A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B61B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B61C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B61D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B61E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B61F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B620
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B621
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B622
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B623
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B624
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B625
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B626
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B627
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B628
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B629
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B62A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B62B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B62C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B62D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B62E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B62F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B630
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B631
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B632
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B633
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B634
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B635
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B636
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B637
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B638
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B639
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B63A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B63B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B63C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B63D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B63E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B63F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B640
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B641
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B642
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B643
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B644
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B645
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B646
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B647
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B648
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B649
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B64A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B64B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B64C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B64D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B64E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B64F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B650
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B651
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B652
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B653
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B654
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B655
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B656
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B657
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B658
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B659
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B65A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B65B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B65C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B65D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B65E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B65F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B660
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B661
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B662
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B663
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B664
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B665
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B666
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B667
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B668
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B669
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B66A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B66B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B66C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B66D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B66E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B66F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B670
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B671
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B672
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B673
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B674
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B675
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B676
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B677
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B678
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B679
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B67A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B67B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B67C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B67D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B67E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B67F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B680
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B681
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B682
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B683
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B684
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B685
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B686
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B687
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B688
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B689
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B68A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B68B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B68C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B68D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B68E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B68F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B690
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B691
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B692
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B693
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B694
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B695
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B696
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B697
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B698
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B699
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B69A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B69B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B69C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B69D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B69E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B69F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B6B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B6B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B6D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B6D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6EE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B6EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B6F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B6FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B700
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B701
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B702
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B703
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B704
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B705
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B706
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B707
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B708
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B709
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B70A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B70B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B70C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B70D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B70E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B70F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B710
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B711
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B712
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B713
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B714
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B715
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B716
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B717
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B718
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B719
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B71A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B71B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B71C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B71D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B71E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B71F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B720
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B721
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B722
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B723
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B724
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B725
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B726
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B727
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B728
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B729
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B72A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B72B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B72C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B72D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B72E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B72F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B730
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B731
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B732
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B733
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B734
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B735
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B736
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B737
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B738
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B739
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B73A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B73B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B73C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B73D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B73E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B73F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B740
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B741
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B742
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B743
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B744
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B745
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B746
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B747
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B748
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B749
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B74A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B74B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B74C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B74D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B74E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B74F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B750
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B751
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B752
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B753
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B754
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B755
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B756
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B757
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B758
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B759
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B75A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B75B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B75C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B75D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B75E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B75F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B760
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B761
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B762
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B763
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B764
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B765
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B766
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B767
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B768
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B769
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B76A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B76B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B76C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B76D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B76E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B76F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B770
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B771
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B772
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B773
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B774
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B775
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B776
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B777
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B778
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B779
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B77A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B77B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B77C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B77D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B77E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B77F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B780
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B781
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B782
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B783
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B784
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B785
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B786
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B787
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B788
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B789
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B78A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B78B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B78C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B78D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B78E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B78F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B790
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B791
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B792
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B793
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B794
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B795
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B796
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B797
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B798
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B799
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B79A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B79B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B79C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B79D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B79E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B79F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B7B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B7B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7CE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B7CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B7D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7EA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B7EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B7EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B7FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B800
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B801
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B802
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B803
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B804
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B805
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B806
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B807
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B808
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B809
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B80A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B80B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B80C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B80D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B80E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B80F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B810
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B811
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B812
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B813
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B814
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B815
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B816
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B817
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B818
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B819
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B81A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B81B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B81C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B81D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B81E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B81F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B820
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B821
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B822
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B823
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B824
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B825
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B826
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B827
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B828
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B829
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B82A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B82B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B82C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B82D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B82E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B82F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B830
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B831
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B832
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B833
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B834
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B835
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B836
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B837
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B838
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B839
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B83A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B83B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B83C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B83D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B83E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B83F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B840
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B841
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B842
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B843
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B844
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B845
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B846
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B847
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B848
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B849
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B84A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B84B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B84C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B84D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B84E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B84F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B850
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B851
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B852
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B853
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B854
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B855
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B856
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B857
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B858
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B859
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B85A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B85B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B85C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B85D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B85E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B85F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B860
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B861
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B862
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B863
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B864
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B865
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B866
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B867
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B868
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B869
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B86A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B86B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B86C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B86D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B86E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B86F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B870
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B871
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B872
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B873
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B874
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B875
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B876
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B877
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B878
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B879
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B87A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B87B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B87C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B87D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B87E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B87F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B880
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B881
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B882
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B883
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B884
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B885
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B886
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B887
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B888
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B889
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B88A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B88B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B88C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B88D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B88E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B88F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B890
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B891
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B892
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B893
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B894
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B895
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B896
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B897
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B898
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B899
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B89A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B89B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B89C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B89D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B89E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B89F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8AE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B8AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B8B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8CA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B8CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B8CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B8E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B8E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B8FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B900
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B901
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B902
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B903
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B904
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B905
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B906
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B907
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B908
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B909
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B90A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B90B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B90C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B90D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B90E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B90F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B910
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B911
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B912
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B913
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B914
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B915
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B916
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B917
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B918
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B919
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B91A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B91B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B91C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B91D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B91E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B91F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B920
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B921
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B922
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B923
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B924
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B925
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B926
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B927
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B928
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B929
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B92A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B92B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B92C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B92D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B92E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B92F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B930
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B931
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B932
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B933
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B934
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B935
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B936
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B937
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B938
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B939
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B93A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B93B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B93C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B93D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B93E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B93F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B940
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B941
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B942
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B943
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B944
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B945
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B946
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B947
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B948
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B949
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B94A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B94B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B94C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B94D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B94E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B94F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B950
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B951
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B952
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B953
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B954
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B955
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B956
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B957
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B958
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B959
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B95A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B95B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B95C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B95D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B95E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B95F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B960
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B961
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B962
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B963
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B964
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B965
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B966
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B967
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B968
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B969
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B96A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B96B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B96C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B96D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B96E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B96F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B970
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B971
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B972
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B973
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B974
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B975
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B976
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B977
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B978
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B979
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B97A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B97B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B97C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B97D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B97E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B97F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B980
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B981
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B982
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B983
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B984
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B985
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B986
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B987
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B988
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B989
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B98A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B98B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B98C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B98D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B98E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B98F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B990
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B991
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B992
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B993
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B994
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B995
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B996
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B997
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B998
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B999
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B99A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B99B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B99C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B99D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B99E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B99F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9AA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B9AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B9AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B9C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B9C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B9E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+B9E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+B9FE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+B9FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BA00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA1A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BA1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BA1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA36
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BA37
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BA38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA52
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BA53
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BA54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA6E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BA6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BA70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA8A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BA8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BA8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BA9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BAA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BAA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BABA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BABB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BABC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BABD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BABE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BABF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BAC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BAC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BACA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BACB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BACC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BACD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BACE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BACF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BADA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BADB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BADC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BADD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BADE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BADF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BAE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAFA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BAFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BAFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BAFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB16
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BB17
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BB18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB32
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BB33
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BB34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB4E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BB4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BB50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB6A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BB6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BB6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB86
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BB87
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BB88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BB9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BBA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BBA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBBE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BBBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BBC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBDA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BBDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BBDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BBF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BBF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BBFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC12
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BC13
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BC14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC2E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BC2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BC30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC4A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BC4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BC4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC66
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BC67
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BC68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC82
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BC83
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BC84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BC9E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BC9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BCA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCBA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BCBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BCBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BCD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BCD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BCF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BCF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BCFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD0E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BD0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BD10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD2A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BD2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BD2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD46
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BD47
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BD48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD62
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BD63
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BD64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD7E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BD7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BD80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD9A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BD9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BD9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BD9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BDB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BDB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BDD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BDD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDEE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BDEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BDF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BDFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE0A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BE0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BE0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE26
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BE27
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BE28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE42
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BE43
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BE44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE5E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BE5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BE60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE7A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BE7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BE7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE96
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BE97
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BE98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BE9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BEB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BEB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BECA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BECB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BECC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BECD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BECE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BECF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BED0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BED9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEEA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BEEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BEEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BEFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF06
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BF07
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BF08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF22
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BF23
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BF24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF3E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BF3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BF40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF5A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BF5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BF5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF76
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BF77
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BF78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF92
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BF93
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BF94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BF9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFAE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BFAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BFB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFCA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BFCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BFCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+BFE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+BFE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+BFFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C000
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C001
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C002
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C003
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C004
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C005
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C006
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C007
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C008
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C009
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C00A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C00B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C00C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C00D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C00E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C00F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C010
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C011
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C012
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C013
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C014
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C015
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C016
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C017
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C018
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C019
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C01A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C01B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C01C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C01D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C01E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C01F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C020
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C021
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C022
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C023
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C024
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C025
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C026
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C027
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C028
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C029
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C02A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C02B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C02C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C02D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C02E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C02F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C030
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C031
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C032
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C033
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C034
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C035
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C036
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C037
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C038
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C039
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C03A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C03B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C03C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C03D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C03E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C03F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C040
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C041
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C042
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C043
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C044
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C045
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C046
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C047
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C048
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C049
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C04A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C04B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C04C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C04D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C04E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C04F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C050
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C051
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C052
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C053
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C054
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C055
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C056
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C057
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C058
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C059
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C05A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C05B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C05C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C05D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C05E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C05F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C060
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C061
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C062
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C063
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C064
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C065
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C066
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C067
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C068
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C069
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C06A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C06B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C06C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C06D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C06E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C06F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C070
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C071
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C072
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C073
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C074
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C075
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C076
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C077
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C078
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C079
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C07A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C07B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C07C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C07D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C07E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C07F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C080
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C081
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C082
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C083
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C084
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C085
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C086
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C087
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C088
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C089
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C08A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C08B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C08C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C08D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C08E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C08F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C090
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C091
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C092
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C093
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C094
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C095
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C096
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C097
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C098
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C099
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C09A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C09B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C09C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C09D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C09E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C09F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0AA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C0AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C0AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C0C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C0C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C0E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C0E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C0FE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C0FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C100
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C101
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C102
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C103
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C104
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C105
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C106
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C107
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C108
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C109
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C10A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C10B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C10C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C10D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C10E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C10F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C110
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C111
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C112
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C113
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C114
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C115
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C116
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C117
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C118
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C119
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C11A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C11B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C11C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C11D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C11E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C11F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C120
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C121
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C122
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C123
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C124
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C125
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C126
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C127
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C128
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C129
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C12A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C12B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C12C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C12D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C12E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C12F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C130
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C131
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C132
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C133
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C134
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C135
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C136
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C137
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C138
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C139
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C13A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C13B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C13C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C13D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C13E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C13F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C140
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C141
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C142
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C143
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C144
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C145
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C146
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C147
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C148
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C149
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C14A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C14B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C14C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C14D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C14E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C14F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C150
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C151
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C152
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C153
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C154
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C155
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C156
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C157
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C158
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C159
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C15A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C15B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C15C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C15D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C15E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C15F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C160
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C161
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C162
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C163
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C164
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C165
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C166
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C167
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C168
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C169
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C16A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C16B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C16C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C16D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C16E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C16F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C170
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C171
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C172
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C173
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C174
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C175
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C176
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C177
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C178
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C179
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C17A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C17B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C17C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C17D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C17E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C17F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C180
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C181
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C182
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C183
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C184
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C185
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C186
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C187
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C188
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C189
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C18A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C18B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C18C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C18D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C18E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C18F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C190
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C191
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C192
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C193
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C194
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C195
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C196
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C197
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C198
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C199
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C19A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C19B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C19C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C19D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C19E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C19F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C1A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C1A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C1C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C1C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1DE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C1DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C1E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1FA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C1FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C1FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C1FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C200
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C201
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C202
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C203
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C204
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C205
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C206
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C207
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C208
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C209
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C20A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C20B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C20C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C20D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C20E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C20F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C210
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C211
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C212
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C213
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C214
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C215
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C216
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C217
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C218
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C219
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C21A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C21B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C21C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C21D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C21E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C21F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C220
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C221
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C222
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C223
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C224
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C225
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C226
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C227
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C228
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C229
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C22A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C22B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C22C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C22D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C22E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C22F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C230
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C231
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C232
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C233
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C234
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C235
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C236
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C237
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C238
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C239
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C23A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C23B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C23C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C23D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C23E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C23F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C240
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C241
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C242
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C243
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C244
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C245
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C246
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C247
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C248
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C249
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C24A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C24B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C24C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C24D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C24E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C24F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C250
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C251
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C252
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C253
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C254
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C255
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C256
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C257
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C258
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C259
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C25A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C25B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C25C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C25D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C25E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C25F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C260
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C261
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C262
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C263
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C264
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C265
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C266
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C267
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C268
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C269
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C26A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C26B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C26C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C26D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C26E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C26F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C270
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C271
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C272
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C273
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C274
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C275
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C276
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C277
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C278
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C279
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C27A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C27B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C27C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C27D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C27E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C27F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C280
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C281
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C282
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C283
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C284
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C285
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C286
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C287
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C288
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C289
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C28A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C28B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C28C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C28D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C28E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C28F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C290
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C291
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C292
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C293
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C294
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C295
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C296
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C297
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C298
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C299
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C29A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C29B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C29C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C29D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C29E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C29F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C2A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C2A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2BE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C2BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C2C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2DA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C2DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C2DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C2F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C2F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C2FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C300
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C301
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C302
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C303
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C304
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C305
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C306
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C307
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C308
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C309
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C30A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C30B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C30C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C30D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C30E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C30F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C310
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C311
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C312
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C313
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C314
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C315
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C316
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C317
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C318
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C319
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C31A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C31B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C31C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C31D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C31E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C31F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C320
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C321
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C322
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C323
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C324
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C325
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C326
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C327
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C328
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C329
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C32A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C32B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C32C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C32D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C32E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C32F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C330
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C331
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C332
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C333
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C334
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C335
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C336
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C337
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C338
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C339
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C33A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C33B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C33C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C33D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C33E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C33F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C340
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C341
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C342
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C343
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C344
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C345
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C346
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C347
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C348
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C349
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C34A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C34B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C34C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C34D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C34E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C34F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C350
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C351
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C352
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C353
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C354
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C355
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C356
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C357
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C358
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C359
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C35A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C35B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C35C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C35D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C35E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C35F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C360
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C361
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C362
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C363
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C364
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C365
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C366
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C367
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C368
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C369
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C36A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C36B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C36C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C36D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C36E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C36F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C370
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C371
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C372
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C373
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C374
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C375
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C376
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C377
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C378
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C379
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C37A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C37B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C37C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C37D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C37E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C37F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C380
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C381
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C382
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C383
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C384
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C385
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C386
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C387
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C388
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C389
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C38A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C38B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C38C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C38D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C38E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C38F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C390
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C391
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C392
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C393
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C394
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C395
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C396
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C397
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C398
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C399
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C39A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C39B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C39C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C39D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C39E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C39F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C3A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3BA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C3BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C3BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C3D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C3D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C3F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C3F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C3FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C400
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C401
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C402
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C403
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C404
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C405
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C406
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C407
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C408
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C409
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C40A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C40B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C40C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C40D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C40E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C40F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C410
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C411
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C412
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C413
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C414
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C415
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C416
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C417
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C418
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C419
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C41A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C41B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C41C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C41D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C41E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C41F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C420
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C421
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C422
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C423
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C424
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C425
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C426
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C427
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C428
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C429
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C42A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C42B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C42C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C42D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C42E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C42F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C430
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C431
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C432
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C433
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C434
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C435
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C436
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C437
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C438
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C439
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C43A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C43B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C43C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C43D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C43E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C43F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C440
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C441
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C442
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C443
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C444
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C445
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C446
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C447
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C448
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C449
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C44A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C44B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C44C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C44D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C44E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C44F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C450
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C451
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C452
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C453
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C454
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C455
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C456
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C457
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C458
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C459
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C45A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C45B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C45C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C45D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C45E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C45F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C460
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C461
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C462
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C463
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C464
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C465
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C466
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C467
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C468
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C469
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C46A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C46B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C46C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C46D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C46E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C46F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C470
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C471
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C472
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C473
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C474
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C475
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C476
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C477
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C478
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C479
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C47A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C47B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C47C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C47D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C47E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C47F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C480
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C481
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C482
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C483
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C484
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C485
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C486
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C487
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C488
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C489
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C48A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C48B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C48C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C48D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C48E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C48F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C490
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C491
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C492
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C493
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C494
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C495
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C496
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C497
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C498
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C499
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C49A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C49B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C49C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C49D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C49E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C49F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C4B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C4B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C4D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C4D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4EE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C4EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C4F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C4FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C500
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C501
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C502
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C503
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C504
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C505
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C506
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C507
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C508
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C509
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C50A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C50B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C50C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C50D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C50E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C50F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C510
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C511
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C512
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C513
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C514
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C515
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C516
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C517
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C518
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C519
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C51A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C51B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C51C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C51D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C51E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C51F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C520
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C521
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C522
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C523
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C524
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C525
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C526
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C527
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C528
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C529
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C52A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C52B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C52C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C52D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C52E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C52F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C530
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C531
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C532
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C533
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C534
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C535
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C536
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C537
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C538
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C539
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C53A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C53B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C53C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C53D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C53E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C53F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C540
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C541
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C542
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C543
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C544
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C545
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C546
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C547
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C548
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C549
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C54A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C54B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C54C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C54D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C54E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C54F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C550
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C551
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C552
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C553
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C554
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C555
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C556
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C557
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C558
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C559
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C55A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C55B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C55C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C55D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C55E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C55F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C560
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C561
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C562
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C563
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C564
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C565
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C566
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C567
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C568
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C569
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C56A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C56B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C56C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C56D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C56E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C56F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C570
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C571
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C572
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C573
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C574
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C575
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C576
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C577
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C578
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C579
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C57A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C57B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C57C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C57D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C57E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C57F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C580
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C581
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C582
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C583
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C584
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C585
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C586
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C587
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C588
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C589
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C58A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C58B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C58C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C58D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C58E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C58F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C590
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C591
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C592
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C593
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C594
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C595
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C596
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C597
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C598
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C599
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C59A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C59B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C59C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C59D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C59E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C59F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C5B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C5B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5CE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C5CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C5D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5EA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C5EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C5EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C5FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C600
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C601
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C602
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C603
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C604
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C605
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C606
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C607
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C608
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C609
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C60A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C60B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C60C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C60D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C60E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C60F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C610
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C611
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C612
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C613
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C614
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C615
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C616
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C617
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C618
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C619
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C61A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C61B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C61C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C61D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C61E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C61F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C620
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C621
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C622
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C623
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C624
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C625
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C626
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C627
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C628
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C629
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C62A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C62B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C62C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C62D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C62E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C62F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C630
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C631
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C632
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C633
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C634
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C635
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C636
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C637
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C638
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C639
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C63A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C63B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C63C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C63D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C63E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C63F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C640
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C641
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C642
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C643
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C644
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C645
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C646
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C647
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C648
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C649
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C64A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C64B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C64C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C64D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C64E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C64F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C650
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C651
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C652
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C653
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C654
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C655
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C656
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C657
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C658
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C659
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C65A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C65B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C65C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C65D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C65E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C65F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C660
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C661
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C662
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C663
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C664
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C665
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C666
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C667
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C668
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C669
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C66A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C66B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C66C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C66D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C66E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C66F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C670
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C671
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C672
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C673
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C674
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C675
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C676
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C677
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C678
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C679
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C67A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C67B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C67C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C67D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C67E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C67F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C680
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C681
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C682
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C683
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C684
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C685
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C686
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C687
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C688
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C689
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C68A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C68B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C68C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C68D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C68E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C68F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C690
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C691
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C692
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C693
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C694
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C695
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C696
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C697
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C698
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C699
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C69A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C69B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C69C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C69D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C69E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C69F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6AE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C6AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C6B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6CA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C6CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C6CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C6E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C6E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C6FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C700
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C701
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C702
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C703
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C704
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C705
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C706
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C707
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C708
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C709
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C70A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C70B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C70C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C70D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C70E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C70F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C710
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C711
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C712
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C713
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C714
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C715
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C716
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C717
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C718
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C719
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C71A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C71B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C71C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C71D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C71E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C71F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C720
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C721
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C722
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C723
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C724
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C725
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C726
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C727
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C728
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C729
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C72A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C72B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C72C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C72D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C72E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C72F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C730
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C731
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C732
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C733
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C734
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C735
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C736
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C737
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C738
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C739
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C73A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C73B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C73C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C73D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C73E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C73F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C740
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C741
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C742
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C743
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C744
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C745
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C746
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C747
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C748
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C749
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C74A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C74B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C74C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C74D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C74E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C74F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C750
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C751
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C752
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C753
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C754
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C755
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C756
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C757
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C758
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C759
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C75A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C75B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C75C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C75D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C75E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C75F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C760
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C761
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C762
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C763
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C764
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C765
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C766
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C767
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C768
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C769
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C76A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C76B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C76C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C76D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C76E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C76F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C770
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C771
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C772
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C773
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C774
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C775
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C776
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C777
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C778
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C779
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C77A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C77B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C77C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C77D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C77E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C77F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C780
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C781
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C782
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C783
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C784
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C785
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C786
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C787
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C788
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C789
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C78A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C78B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C78C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C78D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C78E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C78F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C790
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C791
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C792
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C793
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C794
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C795
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C796
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C797
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C798
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C799
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C79A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C79B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C79C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C79D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C79E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C79F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7AA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C7AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C7AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C7C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C7C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C7E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C7E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C7FE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C7FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C800
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C801
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C802
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C803
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C804
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C805
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C806
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C807
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C808
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C809
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C80A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C80B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C80C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C80D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C80E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C80F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C810
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C811
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C812
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C813
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C814
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C815
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C816
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C817
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C818
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C819
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C81A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C81B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C81C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C81D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C81E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C81F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C820
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C821
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C822
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C823
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C824
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C825
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C826
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C827
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C828
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C829
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C82A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C82B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C82C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C82D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C82E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C82F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C830
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C831
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C832
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C833
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C834
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C835
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C836
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C837
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C838
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C839
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C83A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C83B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C83C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C83D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C83E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C83F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C840
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C841
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C842
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C843
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C844
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C845
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C846
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C847
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C848
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C849
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C84A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C84B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C84C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C84D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C84E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C84F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C850
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C851
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C852
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C853
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C854
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C855
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C856
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C857
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C858
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C859
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C85A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C85B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C85C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C85D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C85E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C85F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C860
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C861
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C862
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C863
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C864
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C865
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C866
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C867
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C868
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C869
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C86A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C86B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C86C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C86D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C86E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C86F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C870
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C871
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C872
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C873
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C874
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C875
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C876
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C877
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C878
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C879
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C87A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C87B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C87C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C87D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C87E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C87F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C880
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C881
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C882
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C883
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C884
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C885
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C886
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C887
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C888
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C889
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C88A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C88B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C88C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C88D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C88E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C88F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C890
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C891
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C892
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C893
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C894
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C895
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C896
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C897
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C898
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C899
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C89A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C89B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C89C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C89D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C89E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C89F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C8A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C8A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C8C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C8C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8DE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C8DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C8E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8FA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C8FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C8FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C8FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C900
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C901
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C902
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C903
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C904
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C905
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C906
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C907
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C908
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C909
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C90A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C90B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C90C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C90D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C90E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C90F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C910
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C911
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C912
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C913
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C914
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C915
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C916
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C917
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C918
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C919
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C91A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C91B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C91C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C91D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C91E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C91F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C920
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C921
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C922
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C923
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C924
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C925
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C926
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C927
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C928
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C929
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C92A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C92B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C92C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C92D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C92E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C92F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C930
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C931
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C932
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C933
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C934
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C935
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C936
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C937
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C938
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C939
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C93A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C93B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C93C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C93D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C93E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C93F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C940
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C941
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C942
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C943
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C944
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C945
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C946
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C947
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C948
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C949
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C94A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C94B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C94C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C94D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C94E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C94F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C950
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C951
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C952
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C953
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C954
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C955
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C956
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C957
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C958
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C959
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C95A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C95B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C95C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C95D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C95E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C95F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C960
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C961
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C962
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C963
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C964
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C965
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C966
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C967
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C968
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C969
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C96A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C96B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C96C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C96D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C96E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C96F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C970
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C971
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C972
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C973
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C974
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C975
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C976
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C977
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C978
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C979
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C97A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C97B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C97C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C97D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C97E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C97F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C980
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C981
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C982
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C983
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C984
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C985
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C986
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C987
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C988
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C989
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C98A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C98B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C98C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C98D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C98E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C98F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C990
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C991
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C992
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C993
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C994
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C995
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C996
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C997
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C998
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C999
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C99A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C99B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C99C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C99D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C99E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C99F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C9A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C9A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9BE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C9BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C9C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9DA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C9DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C9DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+C9F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+C9F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+C9FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA12
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CA13
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CA14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA2E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CA2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CA30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA4A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CA4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CA4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA66
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CA67
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CA68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA82
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CA83
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CA84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CA9E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CA9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CAA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CABA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CABB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CABC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CABD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CABE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CABF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CACA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CACB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CACC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CACD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CACE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CACF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CAD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CAD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CADA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CADB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CADC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CADD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CADE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CADF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CAF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CAF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CAFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB0E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CB0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CB10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB2A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CB2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CB2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB46
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CB47
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CB48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB62
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CB63
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CB64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB7E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CB7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CB80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB9A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CB9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CB9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CB9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CBB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CBB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CBD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CBD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBEE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CBEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CBF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CBFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC0A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CC0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CC0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC26
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CC27
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CC28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC42
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CC43
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CC44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC5E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CC5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CC60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC7A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CC7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CC7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC96
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CC97
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CC98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CC9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CCB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CCB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCCE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CCCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CCD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCEA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CCEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CCEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CCFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD06
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CD07
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CD08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD22
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CD23
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CD24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD3E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CD3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CD40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD5A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CD5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CD5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD76
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CD77
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CD78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD92
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CD93
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CD94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CD9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDAE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CDAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CDB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDCA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CDCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CDCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CDE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CDE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CDFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE02
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CE03
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CE04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE1A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE1E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CE1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CE20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE36
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE37
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE3A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CE3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CE3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE52
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE53
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE56
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CE57
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CE58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE6E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE72
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CE73
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CE74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE8A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE8E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CE8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CE90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CE9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEAA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CEAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CEAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CEC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CEC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CECA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CECB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CECC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CECD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CECE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CECF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CED9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEDE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CEE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CEE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEFA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CEFE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CEFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CF00
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF01
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF02
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF03
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF04
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF05
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF06
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF07
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF08
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF09
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF0A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF0B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF0C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF0D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF0E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF0F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF10
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF11
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF12
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF13
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF14
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF15
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF16
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF17
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF18
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF19
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF1A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CF1B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CF1C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF1D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF1E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF1F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF20
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF21
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF22
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF23
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF24
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF25
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF26
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF27
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF28
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF29
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF2A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF2B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF2C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF2D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF2E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF2F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF30
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF31
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF32
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF33
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF34
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF35
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF36
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CF37
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CF38
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF39
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF3A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF3B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF3C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF3D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF3E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF3F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF40
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF41
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF42
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF43
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF44
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF45
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF46
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF47
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF48
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF49
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF4A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF4B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF4C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF4D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF4E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF4F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF50
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF51
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF52
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CF53
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CF54
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF55
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF56
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF57
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF58
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF59
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF5A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF5B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF5C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF5D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF5E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF5F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF60
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF61
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF62
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF63
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF64
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF65
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF66
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF67
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF68
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF69
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF6A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF6B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF6C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF6D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF6E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CF6F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CF70
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF71
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF72
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF73
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF74
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF75
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF76
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF77
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF78
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF79
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF7A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF7B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF7C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF7D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF7E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF7F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF80
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF81
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF82
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF83
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF84
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF85
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF86
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF87
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF88
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF89
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF8A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CF8B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CF8C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF8D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF8E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF8F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF90
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF91
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF92
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF93
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF94
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF95
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF96
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF97
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF98
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF99
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF9A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF9B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF9C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF9D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF9E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CF9F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CFA7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CFA8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFA9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFAA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFAB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFAC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFAD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFAE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFAF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFB9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFBA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFBB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFBC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFBD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFBE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFBF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CFC3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CFC4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFC9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFCA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFCB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFCC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFCD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFCE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFCF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFD9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFDA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFDB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFDC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFDD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFDE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CFDF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CFE0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFE9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFEA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFEB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFEC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFEE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFEF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFF9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFFA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+CFFB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+CFFC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFFD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFFE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+CFFF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D000
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D001
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D002
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D003
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D004
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D005
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D006
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D007
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D008
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D009
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D00A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D00B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D00C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D00D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D00E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D00F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D010
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D011
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D012
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D013
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D014
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D015
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D016
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D017
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D018
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D019
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D01A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D01B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D01C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D01D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D01E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D01F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D020
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D021
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D022
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D023
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D024
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D025
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D026
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D027
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D028
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D029
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D02A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D02B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D02C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D02D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D02E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D02F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D030
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D031
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D032
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D033
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D034
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D035
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D036
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D037
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D038
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D039
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D03A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D03B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D03C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D03D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D03E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D03F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D040
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D041
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D042
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D043
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D044
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D045
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D046
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D047
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D048
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D049
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D04A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D04B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D04C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D04D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D04E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D04F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D050
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D051
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D052
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D053
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D054
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D055
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D056
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D057
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D058
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D059
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D05A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D05B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D05C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D05D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D05E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D05F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D060
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D061
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D062
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D063
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D064
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D065
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D066
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D067
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D068
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D069
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D06A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D06B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D06C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D06D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D06E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D06F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D070
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D071
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D072
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D073
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D074
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D075
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D076
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D077
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D078
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D079
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D07A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D07B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D07C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D07D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D07E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D07F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D080
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D081
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D082
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D083
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D084
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D085
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D086
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D087
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D088
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D089
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D08A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D08B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D08C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D08D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D08E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D08F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D090
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D091
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D092
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D093
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D094
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D095
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D096
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D097
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D098
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D099
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D09A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D09B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D09C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D09D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D09E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D09F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D0A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D0A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0BE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D0BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D0C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0DA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D0DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D0DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D0F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D0F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D0FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D100
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D101
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D102
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D103
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D104
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D105
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D106
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D107
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D108
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D109
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D10A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D10B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D10C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D10D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D10E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D10F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D110
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D111
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D112
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D113
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D114
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D115
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D116
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D117
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D118
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D119
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D11A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D11B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D11C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D11D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D11E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D11F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D120
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D121
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D122
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D123
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D124
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D125
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D126
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D127
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D128
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D129
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D12A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D12B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D12C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D12D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D12E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D12F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D130
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D131
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D132
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D133
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D134
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D135
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D136
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D137
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D138
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D139
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D13A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D13B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D13C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D13D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D13E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D13F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D140
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D141
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D142
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D143
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D144
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D145
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D146
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D147
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D148
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D149
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D14A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D14B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D14C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D14D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D14E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D14F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D150
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D151
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D152
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D153
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D154
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D155
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D156
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D157
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D158
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D159
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D15A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D15B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D15C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D15D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D15E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D15F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D160
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D161
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D162
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D163
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D164
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D165
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D166
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D167
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D168
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D169
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D16A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D16B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D16C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D16D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D16E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D16F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D170
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D171
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D172
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D173
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D174
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D175
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D176
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D177
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D178
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D179
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D17A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D17B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D17C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D17D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D17E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D17F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D180
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D181
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D182
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D183
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D184
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D185
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D186
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D187
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D188
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D189
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D18A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D18B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D18C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D18D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D18E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D18F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D190
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D191
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D192
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D193
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D194
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D195
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D196
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D197
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D198
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D199
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D19A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D19B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D19C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D19D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D19E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D19F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D1A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1BA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D1BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D1BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D1D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D1D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D1F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D1F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D1FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D200
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D201
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D202
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D203
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D204
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D205
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D206
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D207
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D208
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D209
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D20A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D20B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D20C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D20D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D20E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D20F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D210
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D211
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D212
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D213
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D214
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D215
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D216
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D217
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D218
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D219
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D21A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D21B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D21C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D21D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D21E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D21F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D220
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D221
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D222
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D223
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D224
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D225
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D226
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D227
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D228
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D229
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D22A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D22B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D22C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D22D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D22E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D22F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D230
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D231
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D232
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D233
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D234
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D235
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D236
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D237
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D238
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D239
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D23A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D23B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D23C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D23D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D23E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D23F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D240
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D241
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D242
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D243
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D244
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D245
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D246
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D247
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D248
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D249
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D24A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D24B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D24C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D24D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D24E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D24F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D250
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D251
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D252
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D253
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D254
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D255
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D256
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D257
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D258
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D259
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D25A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D25B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D25C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D25D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D25E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D25F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D260
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D261
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D262
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D263
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D264
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D265
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D266
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D267
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D268
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D269
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D26A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D26B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D26C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D26D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D26E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D26F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D270
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D271
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D272
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D273
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D274
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D275
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D276
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D277
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D278
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D279
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D27A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D27B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D27C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D27D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D27E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D27F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D280
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D281
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D282
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D283
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D284
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D285
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D286
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D287
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D288
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D289
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D28A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D28B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D28C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D28D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D28E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D28F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D290
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D291
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D292
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D293
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D294
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D295
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D296
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D297
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D298
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D299
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D29A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D29B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D29C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D29D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D29E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D29F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D2B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D2B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D2D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D2D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2EE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D2EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D2F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D2FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D300
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D301
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D302
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D303
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D304
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D305
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D306
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D307
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D308
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D309
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D30A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D30B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D30C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D30D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D30E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D30F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D310
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D311
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D312
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D313
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D314
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D315
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D316
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D317
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D318
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D319
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D31A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D31B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D31C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D31D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D31E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D31F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D320
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D321
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D322
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D323
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D324
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D325
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D326
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D327
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D328
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D329
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D32A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D32B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D32C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D32D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D32E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D32F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D330
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D331
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D332
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D333
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D334
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D335
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D336
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D337
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D338
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D339
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D33A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D33B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D33C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D33D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D33E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D33F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D340
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D341
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D342
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D343
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D344
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D345
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D346
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D347
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D348
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D349
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D34A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D34B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D34C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D34D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D34E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D34F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D350
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D351
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D352
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D353
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D354
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D355
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D356
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D357
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D358
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D359
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D35A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D35B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D35C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D35D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D35E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D35F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D360
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D361
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D362
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D363
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D364
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D365
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D366
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D367
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D368
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D369
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D36A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D36B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D36C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D36D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D36E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D36F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D370
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D371
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D372
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D373
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D374
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D375
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D376
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D377
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D378
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D379
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D37A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D37B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D37C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D37D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D37E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D37F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D380
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D381
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D382
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D383
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D384
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D385
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D386
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D387
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D388
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D389
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D38A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D38B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D38C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D38D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D38E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D38F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D390
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D391
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D392
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D393
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D394
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D395
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D396
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D397
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D398
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D399
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D39A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D39B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D39C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D39D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D39E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D39F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D3B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D3B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3CE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D3CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D3D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3EA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D3EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D3EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D3FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D400
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D401
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D402
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D403
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D404
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D405
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D406
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D407
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D408
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D409
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D40A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D40B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D40C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D40D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D40E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D40F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D410
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D411
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D412
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D413
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D414
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D415
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D416
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D417
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D418
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D419
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D41A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D41B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D41C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D41D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D41E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D41F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D420
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D421
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D422
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D423
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D424
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D425
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D426
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D427
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D428
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D429
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D42A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D42B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D42C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D42D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D42E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D42F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D430
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D431
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D432
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D433
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D434
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D435
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D436
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D437
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D438
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D439
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D43A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D43B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D43C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D43D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D43E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D43F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D440
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D441
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D442
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D443
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D444
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D445
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D446
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D447
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D448
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D449
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D44A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D44B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D44C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D44D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D44E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D44F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D450
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D451
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D452
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D453
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D454
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D455
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D456
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D457
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D458
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D459
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D45A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D45B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D45C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D45D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D45E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D45F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D460
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D461
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D462
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D463
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D464
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D465
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D466
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D467
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D468
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D469
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D46A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D46B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D46C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D46D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D46E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D46F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D470
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D471
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D472
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D473
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D474
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D475
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D476
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D477
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D478
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D479
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D47A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D47B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D47C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D47D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D47E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D47F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D480
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D481
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D482
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D483
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D484
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D485
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D486
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D487
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D488
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D489
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D48A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D48B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D48C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D48D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D48E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D48F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D490
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D491
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D492
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D493
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D494
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D495
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D496
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D497
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D498
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D499
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D49A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D49B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D49C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D49D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D49E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D49F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4AE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D4AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D4B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4CA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D4CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D4CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D4E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D4E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D4FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D500
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D501
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D502
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D503
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D504
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D505
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D506
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D507
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D508
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D509
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D50A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D50B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D50C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D50D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D50E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D50F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D510
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D511
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D512
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D513
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D514
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D515
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D516
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D517
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D518
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D519
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D51A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D51B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D51C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D51D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D51E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D51F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D520
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D521
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D522
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D523
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D524
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D525
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D526
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D527
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D528
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D529
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D52A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D52B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D52C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D52D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D52E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D52F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D530
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D531
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D532
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D533
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D534
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D535
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D536
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D537
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D538
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D539
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D53A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D53B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D53C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D53D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D53E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D53F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D540
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D541
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D542
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D543
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D544
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D545
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D546
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D547
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D548
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D549
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D54A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D54B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D54C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D54D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D54E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D54F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D550
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D551
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D552
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D553
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D554
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D555
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D556
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D557
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D558
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D559
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D55A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D55B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D55C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D55D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D55E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D55F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D560
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D561
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D562
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D563
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D564
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D565
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D566
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D567
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D568
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D569
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D56A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D56B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D56C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D56D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D56E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D56F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D570
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D571
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D572
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D573
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D574
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D575
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D576
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D577
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D578
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D579
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D57A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D57B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D57C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D57D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D57E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D57F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D580
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D581
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D582
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D583
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D584
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D585
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D586
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D587
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D588
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D589
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D58A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D58B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D58C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D58D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D58E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D58F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D590
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D591
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D592
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D593
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D594
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D595
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D596
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D597
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D598
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D599
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D59A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D59B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D59C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D59D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D59E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D59F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5AA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D5AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D5AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D5C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D5C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5DE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D5E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D5E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5FA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D5FE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D5FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D600
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D601
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D602
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D603
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D604
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D605
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D606
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D607
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D608
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D609
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D60A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D60B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D60C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D60D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D60E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D60F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D610
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D611
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D612
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D613
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D614
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D615
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D616
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D617
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D618
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D619
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D61A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D61B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D61C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D61D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D61E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D61F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D620
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D621
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D622
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D623
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D624
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D625
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D626
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D627
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D628
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D629
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D62A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D62B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D62C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D62D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D62E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D62F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D630
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D631
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D632
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D633
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D634
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D635
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D636
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D637
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D638
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D639
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D63A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D63B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D63C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D63D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D63E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D63F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D640
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D641
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D642
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D643
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D644
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D645
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D646
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D647
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D648
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D649
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D64A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D64B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D64C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D64D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D64E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D64F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D650
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D651
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D652
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D653
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D654
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D655
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D656
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D657
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D658
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D659
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D65A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D65B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D65C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D65D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D65E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D65F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D660
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D661
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D662
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D663
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D664
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D665
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D666
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D667
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D668
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D669
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D66A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D66B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D66C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D66D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D66E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D66F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D670
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D671
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D672
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D673
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D674
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D675
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D676
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D677
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D678
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D679
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D67A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D67B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D67C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D67D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D67E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D67F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D680
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D681
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D682
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D683
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D684
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D685
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D686
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D687
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D688
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D689
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D68A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D68B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D68C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D68D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D68E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D68F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D690
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D691
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D692
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D693
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D694
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D695
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D696
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D697
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D698
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D699
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D69A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D69B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D69C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D69D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D69E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D69F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A6
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D6A7
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D6A8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6A9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6AA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6AB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6AC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6AD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6AE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6AF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6B9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6BA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6BB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6BC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6BD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6BE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6BF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D6C3
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D6C4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6C9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6CA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6CB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6CC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6CD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6CE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6CF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6D9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6DA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6DB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6DC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6DD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6DE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D6DF
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D6E0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6E9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6EA
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6EB
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6EC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6ED
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6EE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6EF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F2
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F3
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F4
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F5
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F6
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F7
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F8
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6F9
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6FA
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D6FB
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D6FC
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6FD
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6FE
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D6FF
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D700
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D701
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D702
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D703
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D704
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D705
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D706
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D707
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D708
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D709
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D70A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D70B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D70C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D70D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D70E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D70F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D710
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D711
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D712
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D713
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D714
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D715
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D716
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D717
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D718
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D719
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D71A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D71B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D71C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D71D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D71E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D71F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D720
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D721
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D722
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D723
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D724
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D725
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D726
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D727
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D728
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D729
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D72A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D72B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D72C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D72D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D72E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D72F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D730
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D731
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D732
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D733
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D734
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D735
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D736
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D737
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D738
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D739
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D73A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D73B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D73C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D73D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D73E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D73F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D740
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D741
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D742
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D743
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D744
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D745
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D746
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D747
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D748
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D749
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D74A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D74B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D74C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D74D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D74E
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D74F
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D750
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D751
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D752
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D753
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D754
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D755
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D756
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D757
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D758
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D759
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D75A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D75B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D75C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D75D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D75E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D75F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D760
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D761
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D762
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D763
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D764
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D765
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D766
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D767
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D768
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D769
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D76A
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D76B
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D76C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D76D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D76E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D76F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D770
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D771
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D772
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D773
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D774
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D775
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D776
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D777
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D778
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D779
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D77A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D77B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D77C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D77D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D77E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D77F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D780
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D781
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D782
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D783
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D784
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D785
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D786
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D787
+  CHARPROP____(0x1ffu, kNormal, kL, kH2)  // U+D788
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D789
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D78A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D78B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D78C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D78D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D78E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D78F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D790
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D791
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D792
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D793
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D794
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D795
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D796
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D797
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D798
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D799
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D79A
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D79B
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D79C
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D79D
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D79E
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D79F
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D7A0
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D7A1
+  CHARPROP____(0x1ffu, kNormal, kL, kH3)  // U+D7A2
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+D7A3
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7A4
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7A5
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7A6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7A7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7A8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7A9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7AA
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7AB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7AC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7AD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7AE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7AF
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B0
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B1
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B2
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B3
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B4
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B5
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B6
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B7
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B8
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7B9
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7BA
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7BB
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7BC
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7BD
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7BE
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7BF
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7C0
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7C1
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7C2
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7C3
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7C4
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7C5
+  CHARPROP____(0x1ffu, kNormal, kL, kJV)  // U+D7C6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7C7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7C8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7C9
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7CA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7CB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7CC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7CD
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7CE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7CF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7D9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7DA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7DB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7DC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7DD
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7DE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7DF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7E9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7EA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7EB
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7EC
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7ED
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7EE
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7EF
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F0
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F1
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F2
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F3
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F4
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F5
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F6
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F7
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F8
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7F9
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7FA
+  CHARPROP____(0x1ffu, kNormal, kL, kJT)  // U+D7FB
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7FC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7FD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7FE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+D7FF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D800
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D801
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D802
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D803
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D804
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D805
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D806
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D807
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D808
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D809
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D80A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D80B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D80C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D80D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D80E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D80F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D810
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D811
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D812
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D813
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D814
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D815
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D816
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D817
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D818
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D819
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D81A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D81B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D81C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D81D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D81E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D81F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D820
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D821
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D822
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D823
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D824
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D825
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D826
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D827
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D828
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D829
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D82A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D82B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D82C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D82D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D82E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D82F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D830
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D831
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D832
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D833
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D834
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D835
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D836
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D837
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D838
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D839
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D83A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D83B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D83C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D83D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D83E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D83F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D840
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D841
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D842
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D843
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D844
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D845
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D846
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D847
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D848
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D849
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D84A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D84B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D84C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D84D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D84E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D84F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D850
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D851
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D852
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D853
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D854
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D855
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D856
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D857
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D858
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D859
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D85A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D85B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D85C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D85D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D85E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D85F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D860
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D861
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D862
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D863
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D864
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D865
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D866
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D867
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D868
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D869
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D86A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D86B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D86C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D86D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D86E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D86F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D870
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D871
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D872
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D873
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D874
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D875
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D876
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D877
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D878
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D879
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D87A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D87B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D87C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D87D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D87E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D87F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D880
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D881
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D882
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D883
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D884
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D885
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D886
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D887
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D888
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D889
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D88A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D88B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D88C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D88D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D88E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D88F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D890
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D891
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D892
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D893
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D894
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D895
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D896
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D897
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D898
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D899
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D89A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D89B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D89C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D89D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D89E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D89F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8A9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8AA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8AB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8AC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8AD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8AE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8AF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8B9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8BA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8BB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8BC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8BD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8BE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8BF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8C9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8CA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8CB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8CC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8CD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8CE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8CF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8D9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8DA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8DB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8DC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8DD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8DE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8DF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8E9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8EA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8EB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8EC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8ED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8EE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8EF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8F9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8FA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8FB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8FC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8FD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8FE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D8FF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D900
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D901
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D902
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D903
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D904
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D905
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D906
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D907
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D908
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D909
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D90A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D90B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D90C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D90D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D90E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D90F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D910
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D911
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D912
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D913
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D914
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D915
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D916
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D917
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D918
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D919
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D91A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D91B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D91C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D91D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D91E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D91F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D920
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D921
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D922
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D923
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D924
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D925
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D926
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D927
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D928
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D929
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D92A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D92B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D92C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D92D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D92E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D92F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D930
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D931
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D932
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D933
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D934
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D935
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D936
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D937
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D938
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D939
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D93A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D93B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D93C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D93D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D93E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D93F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D940
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D941
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D942
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D943
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D944
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D945
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D946
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D947
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D948
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D949
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D94A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D94B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D94C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D94D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D94E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D94F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D950
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D951
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D952
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D953
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D954
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D955
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D956
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D957
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D958
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D959
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D95A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D95B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D95C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D95D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D95E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D95F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D960
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D961
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D962
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D963
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D964
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D965
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D966
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D967
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D968
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D969
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D96A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D96B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D96C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D96D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D96E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D96F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D970
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D971
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D972
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D973
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D974
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D975
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D976
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D977
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D978
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D979
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D97A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D97B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D97C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D97D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D97E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D97F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D980
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D981
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D982
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D983
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D984
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D985
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D986
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D987
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D988
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D989
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D98A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D98B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D98C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D98D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D98E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D98F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D990
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D991
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D992
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D993
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D994
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D995
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D996
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D997
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D998
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D999
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D99A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D99B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D99C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D99D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D99E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D99F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9A9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9AA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9AB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9AC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9AD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9AE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9AF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9B9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9BA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9BB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9BC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9BD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9BE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9BF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9C9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9CA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9CB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9CC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9CD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9CE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9CF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9D9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9DA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9DB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9DC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9DD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9DE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9DF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9E9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9EA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9EB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9EC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9ED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9EE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9EF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9F9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9FA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9FB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9FC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9FD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9FE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+D9FF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA00
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA01
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA02
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA03
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA04
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA05
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA06
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA07
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA08
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA09
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA0A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA0B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA0C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA0D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA0E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA0F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA10
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA11
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA12
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA13
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA14
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA15
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA16
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA17
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA18
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA19
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA1A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA1B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA1C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA1D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA1E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA1F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA20
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA21
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA22
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA23
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA24
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA25
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA26
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA27
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA28
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA29
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA2A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA2B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA2C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA2D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA2E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA2F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA30
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA31
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA32
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA33
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA34
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA35
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA36
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA37
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA38
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA39
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA3A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA3B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA3C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA3D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA3E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA3F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA40
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA41
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA42
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA43
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA44
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA45
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA46
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA47
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA48
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA49
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA4A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA4B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA4C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA4D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA4E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA4F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA50
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA51
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA52
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA53
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA54
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA55
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA56
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA57
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA58
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA59
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA5A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA5B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA5C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA5D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA5E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA5F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA60
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA61
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA62
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA63
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA64
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA65
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA66
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA67
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA68
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA69
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA6A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA6B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA6C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA6D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA6E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA6F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA70
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA71
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA72
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA73
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA74
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA75
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA76
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA77
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA78
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA79
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA7A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA7B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA7C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA7D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA7E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA7F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA80
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA81
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA82
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA83
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA84
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA85
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA86
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA87
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA88
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA89
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA8A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA8B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA8C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA8D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA8E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA8F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA90
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA91
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA92
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA93
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA94
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA95
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA96
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA97
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA98
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA99
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA9A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA9B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA9C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA9D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA9E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DA9F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAA9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAAA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAAB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAAC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAAD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAAE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAAF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAB9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DABA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DABB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DABC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DABD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DABE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DABF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAC9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DACA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DACB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DACC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DACD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DACE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DACF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAD9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DADA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DADB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DADC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DADD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DADE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DADF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAE9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAEA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAEB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAEC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAEE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAEF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAF9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAFA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAFB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAFC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAFD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAFE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DAFF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB00
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB01
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB02
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB03
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB04
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB05
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB06
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB07
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB08
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB09
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB0A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB0B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB0C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB0D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB0E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB0F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB10
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB11
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB12
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB13
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB14
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB15
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB16
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB17
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB18
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB19
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB1A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB1B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB1C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB1D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB1E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB1F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB20
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB21
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB22
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB23
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB24
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB25
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB26
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB27
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB28
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB29
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB2A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB2B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB2C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB2D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB2E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB2F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB30
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB31
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB32
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB33
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB34
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB35
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB36
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB37
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB38
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB39
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB3A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB3B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB3C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB3D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB3E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB3F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB40
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB41
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB42
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB43
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB44
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB45
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB46
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB47
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB48
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB49
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB4A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB4B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB4C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB4D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB4E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB4F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB50
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB51
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB52
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB53
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB54
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB55
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB56
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB57
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB58
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB59
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB5A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB5B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB5C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB5D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB5E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB5F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB60
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB61
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB62
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB63
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB64
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB65
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB66
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB67
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB68
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB69
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB6A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB6B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB6C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB6D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB6E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB6F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB70
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB71
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB72
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB73
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB74
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB75
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB76
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB77
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB78
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB79
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB7A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB7B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB7C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB7D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB7E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+DB7F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB80
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB81
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB82
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB83
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB84
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB85
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB86
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB87
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB88
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB89
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB8A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB8B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB8C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB8D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB8E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB8F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB90
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB91
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB92
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB93
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB94
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB95
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB96
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB97
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB98
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB99
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB9A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB9B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB9C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB9D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB9E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DB9F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBA9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBAA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBAB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBAC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBAD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBAE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBAF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBB9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBBA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBBB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBBC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBBD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBBE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBBF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBC9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBCA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBCB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBCC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBCD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBCE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBCF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBD9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBDA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBDB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBDC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBDD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBDE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBDF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBE9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBEA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBEB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBEC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBEE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBEF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBF9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBFA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBFB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBFC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBFD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DBFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+DBFF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC00
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC01
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC02
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC03
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC04
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC05
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC06
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC07
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC08
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC09
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC0A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC0B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC0C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC0D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC0E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC0F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC10
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC11
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC12
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC13
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC14
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC15
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC16
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC17
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC18
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC19
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC1A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC1B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC1C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC1D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC1E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC1F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC20
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC21
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC22
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC23
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC24
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC25
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC26
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC27
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC28
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC29
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC2A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC2B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC2C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC2D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC2E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC2F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC30
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC31
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC32
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC33
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC34
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC35
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC36
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC37
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC38
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC39
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC3A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC3B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC3C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC3D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC3E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC3F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC40
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC41
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC42
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC43
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC44
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC45
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC46
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC47
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC48
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC49
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC4A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC4B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC4C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC4D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC4E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC4F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC50
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC51
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC52
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC53
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC54
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC55
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC56
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC57
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC58
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC59
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC5A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC5B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC5C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC5D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC5E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC5F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC60
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC61
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC62
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC63
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC64
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC65
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC66
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC67
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC68
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC69
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC6A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC6B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC6C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC6D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC6E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC6F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC70
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC71
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC72
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC73
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC74
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC75
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC76
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC77
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC78
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC79
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC7A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC7B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC7C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC7D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC7E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC7F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC80
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC81
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC82
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC83
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC84
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC85
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC86
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC87
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC88
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC89
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC8A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC8B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC8C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC8D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC8E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC8F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC90
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC91
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC92
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC93
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC94
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC95
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC96
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC97
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC98
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC99
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC9A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC9B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC9C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC9D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC9E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DC9F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCA9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCAA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCAB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCAC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCAD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCAE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCAF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCB9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCBA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCBB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCBC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCBD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCBE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCBF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCC9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCCA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCCB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCCC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCCD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCCE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCCF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCD9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCDA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCDB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCDC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCDD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCDE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCDF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCE9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCEA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCEB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCEC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCEE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCEF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCF9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCFA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCFB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCFC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCFD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCFE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DCFF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD00
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD01
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD02
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD03
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD04
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD05
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD06
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD07
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD08
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD09
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD0A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD0B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD0C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD0D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD0E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD0F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD10
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD11
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD12
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD13
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD14
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD15
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD16
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD17
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD18
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD19
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD1A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD1B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD1C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD1D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD1E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD1F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD20
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD21
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD22
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD23
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD24
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD25
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD26
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD27
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD28
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD29
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD2A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD2B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD2C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD2D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD2E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD2F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD30
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD31
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD32
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD33
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD34
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD35
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD36
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD37
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD38
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD39
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD3A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD3B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD3C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD3D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD3E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD3F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD40
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD41
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD42
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD43
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD44
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD45
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD46
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD47
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD48
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD49
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD4A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD4B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD4C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD4D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD4E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD4F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD50
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD51
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD52
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD53
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD54
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD55
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD56
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD57
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD58
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD59
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD5A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD5B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD5C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD5D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD5E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD5F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD60
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD61
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD62
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD63
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD64
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD65
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD66
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD67
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD68
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD69
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD6A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD6B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD6C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD6D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD6E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD6F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD70
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD71
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD72
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD73
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD74
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD75
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD76
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD77
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD78
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD79
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD7A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD7B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD7C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD7D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD7E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD7F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD80
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD81
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD82
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD83
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD84
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD85
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD86
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD87
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD88
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD89
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD8A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD8B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD8C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD8D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD8E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD8F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD90
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD91
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD92
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD93
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD94
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD95
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD96
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD97
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD98
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD99
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD9A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD9B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD9C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD9D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD9E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DD9F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDA9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDAA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDAB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDAC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDAD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDAE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDAF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDB9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDBA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDBB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDBC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDBD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDBE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDBF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDC9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDCA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDCB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDCC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDCD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDCE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDCF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDD9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDDA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDDB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDDC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDDD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDDE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDDF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDE9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDEA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDEB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDEC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDEE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDEF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDF9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDFA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDFB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDFC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDFD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDFE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DDFF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE00
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE01
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE02
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE03
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE04
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE05
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE06
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE07
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE08
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE09
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE0A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE0B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE0C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE0D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE0E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE0F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE10
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE11
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE12
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE13
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE14
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE15
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE16
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE17
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE18
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE19
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE1A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE1B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE1C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE1D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE1E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE1F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE20
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE21
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE22
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE23
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE24
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE25
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE26
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE27
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE28
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE29
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE2A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE2B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE2C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE2D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE2E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE2F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE30
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE31
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE32
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE33
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE34
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE35
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE36
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE37
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE38
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE39
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE3A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE3B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE3C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE3D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE3E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE3F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE40
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE41
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE42
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE43
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE44
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE45
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE46
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE47
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE48
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE49
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE4A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE4B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE4C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE4D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE4E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE4F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE50
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE51
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE52
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE53
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE54
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE55
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE56
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE57
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE58
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE59
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE5A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE5B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE5C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE5D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE5E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE5F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE60
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE61
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE62
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE63
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE64
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE65
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE66
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE67
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE68
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE69
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE6A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE6B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE6C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE6D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE6E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE6F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE70
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE71
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE72
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE73
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE74
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE75
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE76
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE77
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE78
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE79
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE7A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE7B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE7C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE7D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE7E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE7F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE80
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE81
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE82
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE83
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE84
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE85
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE86
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE87
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE88
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE89
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE8A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE8B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE8C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE8D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE8E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE8F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE90
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE91
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE92
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE93
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE94
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE95
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE96
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE97
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE98
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE99
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE9A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE9B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE9C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE9D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE9E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DE9F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEA9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEAA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEAB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEAC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEAD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEAE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEAF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEB9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEBA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEBB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEBC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEBD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEBE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEBF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEC9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DECA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DECB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DECC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DECD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DECE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DECF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DED9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEDA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEDB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEDC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEDD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEDE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEDF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEE9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEEA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEEB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEEC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEEE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEEF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEF9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEFA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEFB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEFC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEFD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEFE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DEFF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF00
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF01
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF02
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF03
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF04
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF05
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF06
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF07
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF08
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF09
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF0A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF0B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF0C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF0D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF0E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF0F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF10
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF11
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF12
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF13
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF14
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF15
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF16
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF17
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF18
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF19
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF1A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF1B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF1C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF1D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF1E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF1F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF20
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF21
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF22
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF23
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF24
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF25
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF26
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF27
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF28
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF29
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF2A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF2B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF2C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF2D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF2E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF2F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF30
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF31
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF32
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF33
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF34
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF35
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF36
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF37
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF38
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF39
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF3A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF3B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF3C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF3D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF3E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF3F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF40
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF41
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF42
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF43
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF44
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF45
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF46
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF47
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF48
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF49
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF4A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF4B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF4C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF4D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF4E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF4F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF50
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF51
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF52
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF53
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF54
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF55
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF56
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF57
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF58
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF59
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF5A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF5B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF5C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF5D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF5E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF5F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF60
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF61
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF62
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF63
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF64
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF65
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF66
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF67
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF68
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF69
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF6A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF6B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF6C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF6D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF6E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF6F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF70
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF71
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF72
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF73
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF74
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF75
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF76
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF77
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF78
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF79
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF7A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF7B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF7C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF7D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF7E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF7F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF80
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF81
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF82
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF83
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF84
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF85
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF86
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF87
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF88
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF89
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF8A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF8B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF8C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF8D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF8E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF8F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF90
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF91
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF92
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF93
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF94
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF95
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF96
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF97
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF98
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF99
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF9A
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF9B
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF9C
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF9D
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF9E
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DF9F
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFA9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFAA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFAB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFAC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFAD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFAE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFAF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFB9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFBA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFBB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFBC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFBD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFBE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFBF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFC9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFCA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFCB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFCC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFCD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFCE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFCF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFD9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFDA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFDB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFDC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFDD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFDE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFDF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFE9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFEA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFEB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFEC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFED
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFEE
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFEF
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF0
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF1
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF2
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF3
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF4
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF5
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF6
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF7
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF8
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFF9
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFFA
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFFB
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFFC
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFFD
+  CHARPROP____(0x1ffu, kNormal, kON, kSG)  // U+DFFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+DFFF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E000
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E001
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E002
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E003
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E004
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E005
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E006
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E007
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E008
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E009
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E00A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E00B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E00C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E00D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E00E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E00F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E010
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E011
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E012
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E013
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E014
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E015
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E016
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E017
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E018
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E019
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E01A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E01B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E01C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E01D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E01E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E01F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E020
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E021
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E022
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E023
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E024
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E025
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E026
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E027
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E028
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E029
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E02A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E02B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E02C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E02D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E02E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E02F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E030
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E031
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E032
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E033
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E034
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E035
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E036
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E037
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E038
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E039
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E03A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E03B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E03C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E03D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E03E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E03F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E040
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E041
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E042
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E043
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E044
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E045
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E046
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E047
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E048
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E049
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E04A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E04B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E04C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E04D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E04E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E04F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E050
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E051
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E052
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E053
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E054
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E055
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E056
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E057
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E058
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E059
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E05A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E05B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E05C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E05D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E05E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E05F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E060
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E061
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E062
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E063
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E064
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E065
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E066
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E067
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E068
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E069
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E06A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E06B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E06C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E06D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E06E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E06F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E070
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E071
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E072
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E073
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E074
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E075
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E076
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E077
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E078
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E079
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E07A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E07B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E07C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E07D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E07E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E07F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E080
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E081
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E082
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E083
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E084
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E085
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E086
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E087
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E088
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E089
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E08A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E08B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E08C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E08D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E08E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E08F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E090
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E091
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E092
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E093
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E094
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E095
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E096
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E097
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E098
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E099
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E09A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E09B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E09C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E09D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E09E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E09F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E0FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E100
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E101
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E102
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E103
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E104
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E105
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E106
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E107
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E108
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E109
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E10A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E10B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E10C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E10D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E10E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E10F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E110
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E111
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E112
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E113
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E114
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E115
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E116
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E117
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E118
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E119
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E11A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E11B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E11C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E11D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E11E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E11F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E120
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E121
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E122
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E123
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E124
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E125
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E126
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E127
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E128
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E129
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E12A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E12B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E12C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E12D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E12E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E12F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E130
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E131
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E132
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E133
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E134
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E135
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E136
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E137
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E138
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E139
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E13A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E13B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E13C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E13D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E13E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E13F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E140
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E141
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E142
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E143
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E144
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E145
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E146
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E147
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E148
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E149
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E14A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E14B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E14C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E14D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E14E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E14F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E150
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E151
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E152
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E153
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E154
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E155
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E156
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E157
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E158
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E159
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E15A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E15B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E15C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E15D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E15E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E15F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E160
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E161
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E162
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E163
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E164
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E165
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E166
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E167
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E168
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E169
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E16A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E16B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E16C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E16D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E16E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E16F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E170
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E171
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E172
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E173
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E174
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E175
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E176
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E177
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E178
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E179
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E17A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E17B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E17C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E17D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E17E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E17F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E180
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E181
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E182
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E183
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E184
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E185
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E186
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E187
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E188
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E189
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E18A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E18B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E18C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E18D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E18E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E18F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E190
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E191
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E192
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E193
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E194
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E195
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E196
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E197
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E198
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E199
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E19A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E19B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E19C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E19D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E19E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E19F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E1FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E200
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E201
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E202
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E203
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E204
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E205
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E206
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E207
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E208
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E209
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E20A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E20B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E20C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E20D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E20E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E20F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E210
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E211
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E212
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E213
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E214
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E215
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E216
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E217
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E218
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E219
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E21A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E21B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E21C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E21D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E21E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E21F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E220
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E221
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E222
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E223
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E224
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E225
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E226
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E227
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E228
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E229
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E22A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E22B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E22C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E22D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E22E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E22F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E230
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E231
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E232
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E233
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E234
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E235
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E236
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E237
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E238
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E239
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E23A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E23B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E23C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E23D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E23E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E23F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E240
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E241
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E242
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E243
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E244
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E245
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E246
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E247
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E248
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E249
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E24A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E24B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E24C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E24D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E24E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E24F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E250
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E251
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E252
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E253
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E254
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E255
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E256
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E257
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E258
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E259
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E25A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E25B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E25C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E25D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E25E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E25F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E260
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E261
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E262
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E263
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E264
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E265
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E266
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E267
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E268
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E269
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E26A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E26B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E26C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E26D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E26E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E26F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E270
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E271
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E272
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E273
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E274
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E275
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E276
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E277
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E278
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E279
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E27A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E27B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E27C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E27D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E27E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E27F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E280
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E281
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E282
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E283
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E284
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E285
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E286
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E287
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E288
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E289
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E28A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E28B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E28C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E28D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E28E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E28F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E290
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E291
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E292
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E293
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E294
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E295
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E296
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E297
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E298
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E299
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E29A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E29B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E29C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E29D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E29E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E29F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E2FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E300
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E301
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E302
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E303
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E304
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E305
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E306
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E307
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E308
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E309
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E30A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E30B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E30C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E30D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E30E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E30F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E310
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E311
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E312
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E313
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E314
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E315
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E316
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E317
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E318
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E319
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E31A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E31B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E31C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E31D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E31E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E31F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E320
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E321
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E322
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E323
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E324
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E325
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E326
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E327
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E328
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E329
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E32A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E32B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E32C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E32D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E32E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E32F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E330
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E331
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E332
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E333
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E334
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E335
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E336
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E337
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E338
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E339
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E33A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E33B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E33C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E33D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E33E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E33F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E340
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E341
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E342
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E343
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E344
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E345
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E346
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E347
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E348
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E349
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E34A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E34B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E34C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E34D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E34E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E34F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E350
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E351
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E352
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E353
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E354
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E355
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E356
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E357
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E358
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E359
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E35A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E35B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E35C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E35D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E35E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E35F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E360
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E361
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E362
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E363
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E364
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E365
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E366
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E367
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E368
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E369
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E36A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E36B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E36C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E36D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E36E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E36F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E370
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E371
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E372
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E373
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E374
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E375
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E376
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E377
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E378
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E379
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E37A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E37B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E37C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E37D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E37E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E37F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E380
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E381
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E382
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E383
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E384
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E385
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E386
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E387
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E388
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E389
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E38A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E38B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E38C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E38D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E38E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E38F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E390
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E391
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E392
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E393
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E394
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E395
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E396
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E397
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E398
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E399
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E39A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E39B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E39C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E39D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E39E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E39F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E3FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E400
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E401
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E402
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E403
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E404
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E405
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E406
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E407
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E408
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E409
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E40A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E40B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E40C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E40D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E40E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E40F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E410
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E411
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E412
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E413
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E414
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E415
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E416
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E417
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E418
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E419
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E41A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E41B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E41C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E41D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E41E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E41F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E420
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E421
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E422
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E423
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E424
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E425
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E426
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E427
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E428
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E429
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E42A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E42B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E42C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E42D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E42E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E42F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E430
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E431
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E432
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E433
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E434
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E435
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E436
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E437
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E438
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E439
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E43A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E43B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E43C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E43D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E43E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E43F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E440
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E441
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E442
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E443
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E444
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E445
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E446
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E447
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E448
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E449
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E44A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E44B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E44C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E44D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E44E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E44F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E450
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E451
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E452
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E453
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E454
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E455
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E456
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E457
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E458
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E459
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E45A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E45B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E45C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E45D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E45E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E45F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E460
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E461
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E462
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E463
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E464
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E465
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E466
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E467
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E468
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E469
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E46A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E46B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E46C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E46D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E46E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E46F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E470
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E471
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E472
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E473
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E474
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E475
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E476
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E477
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E478
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E479
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E47A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E47B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E47C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E47D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E47E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E47F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E480
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E481
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E482
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E483
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E484
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E485
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E486
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E487
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E488
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E489
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E48A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E48B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E48C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E48D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E48E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E48F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E490
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E491
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E492
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E493
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E494
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E495
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E496
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E497
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E498
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E499
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E49A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E49B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E49C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E49D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E49E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E49F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E4FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E500
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E501
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E502
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E503
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E504
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E505
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E506
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E507
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E508
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E509
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E50A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E50B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E50C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E50D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E50E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E50F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E510
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E511
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E512
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E513
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E514
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E515
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E516
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E517
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E518
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E519
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E51A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E51B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E51C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E51D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E51E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E51F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E520
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E521
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E522
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E523
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E524
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E525
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E526
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E527
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E528
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E529
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E52A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E52B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E52C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E52D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E52E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E52F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E530
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E531
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E532
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E533
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E534
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E535
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E536
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E537
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E538
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E539
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E53A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E53B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E53C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E53D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E53E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E53F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E540
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E541
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E542
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E543
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E544
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E545
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E546
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E547
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E548
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E549
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E54A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E54B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E54C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E54D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E54E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E54F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E550
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E551
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E552
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E553
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E554
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E555
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E556
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E557
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E558
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E559
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E55A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E55B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E55C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E55D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E55E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E55F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E560
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E561
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E562
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E563
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E564
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E565
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E566
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E567
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E568
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E569
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E56A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E56B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E56C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E56D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E56E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E56F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E570
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E571
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E572
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E573
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E574
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E575
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E576
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E577
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E578
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E579
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E57A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E57B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E57C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E57D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E57E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E57F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E580
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E581
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E582
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E583
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E584
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E585
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E586
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E587
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E588
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E589
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E58A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E58B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E58C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E58D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E58E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E58F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E590
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E591
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E592
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E593
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E594
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E595
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E596
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E597
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E598
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E599
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E59A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E59B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E59C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E59D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E59E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E59F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E5FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E600
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E601
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E602
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E603
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E604
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E605
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E606
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E607
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E608
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E609
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E60A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E60B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E60C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E60D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E60E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E60F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E610
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E611
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E612
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E613
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E614
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E615
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E616
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E617
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E618
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E619
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E61A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E61B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E61C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E61D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E61E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E61F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E620
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E621
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E622
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E623
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E624
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E625
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E626
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E627
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E628
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E629
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E62A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E62B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E62C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E62D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E62E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E62F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E630
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E631
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E632
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E633
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E634
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E635
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E636
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E637
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E638
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E639
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E63A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E63B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E63C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E63D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E63E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E63F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E640
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E641
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E642
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E643
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E644
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E645
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E646
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E647
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E648
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E649
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E64A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E64B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E64C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E64D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E64E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E64F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E650
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E651
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E652
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E653
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E654
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E655
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E656
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E657
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E658
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E659
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E65A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E65B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E65C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E65D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E65E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E65F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E660
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E661
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E662
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E663
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E664
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E665
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E666
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E667
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E668
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E669
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E66A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E66B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E66C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E66D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E66E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E66F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E670
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E671
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E672
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E673
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E674
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E675
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E676
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E677
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E678
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E679
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E67A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E67B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E67C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E67D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E67E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E67F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E680
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E681
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E682
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E683
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E684
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E685
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E686
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E687
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E688
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E689
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E68A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E68B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E68C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E68D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E68E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E68F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E690
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E691
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E692
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E693
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E694
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E695
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E696
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E697
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E698
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E699
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E69A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E69B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E69C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E69D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E69E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E69F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E6FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E700
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E701
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E702
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E703
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E704
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E705
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E706
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E707
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E708
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E709
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E70A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E70B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E70C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E70D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E70E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E70F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E710
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E711
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E712
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E713
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E714
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E715
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E716
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E717
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E718
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E719
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E71A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E71B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E71C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E71D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E71E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E71F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E720
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E721
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E722
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E723
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E724
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E725
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E726
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E727
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E728
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E729
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E72A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E72B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E72C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E72D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E72E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E72F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E730
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E731
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E732
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E733
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E734
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E735
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E736
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E737
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E738
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E739
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E73A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E73B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E73C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E73D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E73E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E73F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E740
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E741
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E742
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E743
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E744
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E745
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E746
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E747
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E748
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E749
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E74A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E74B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E74C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E74D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E74E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E74F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E750
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E751
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E752
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E753
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E754
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E755
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E756
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E757
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E758
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E759
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E75A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E75B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E75C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E75D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E75E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E75F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E760
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E761
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E762
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E763
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E764
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E765
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E766
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E767
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E768
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E769
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E76A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E76B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E76C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E76D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E76E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E76F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E770
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E771
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E772
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E773
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E774
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E775
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E776
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E777
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E778
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E779
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E77A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E77B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E77C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E77D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E77E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E77F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E780
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E781
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E782
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E783
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E784
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E785
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E786
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E787
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E788
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E789
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E78A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E78B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E78C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E78D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E78E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E78F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E790
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E791
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E792
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E793
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E794
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E795
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E796
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E797
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E798
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E799
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E79A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E79B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E79C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E79D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E79E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E79F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E7FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E800
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E801
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E802
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E803
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E804
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E805
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E806
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E807
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E808
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E809
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E80A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E80B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E80C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E80D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E80E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E80F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E810
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E811
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E812
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E813
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E814
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E815
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E816
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E817
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E818
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E819
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E81A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E81B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E81C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E81D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E81E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E81F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E820
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E821
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E822
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E823
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E824
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E825
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E826
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E827
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E828
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E829
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E82A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E82B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E82C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E82D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E82E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E82F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E830
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E831
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E832
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E833
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E834
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E835
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E836
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E837
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E838
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E839
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E83A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E83B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E83C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E83D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E83E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E83F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E840
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E841
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E842
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E843
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E844
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E845
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E846
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E847
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E848
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E849
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E84A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E84B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E84C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E84D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E84E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E84F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E850
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E851
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E852
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E853
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E854
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E855
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E856
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E857
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E858
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E859
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E85A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E85B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E85C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E85D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E85E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E85F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E860
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E861
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E862
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E863
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E864
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E865
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E866
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E867
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E868
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E869
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E86A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E86B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E86C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E86D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E86E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E86F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E870
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E871
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E872
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E873
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E874
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E875
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E876
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E877
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E878
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E879
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E87A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E87B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E87C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E87D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E87E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E87F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E880
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E881
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E882
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E883
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E884
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E885
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E886
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E887
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E888
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E889
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E88A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E88B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E88C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E88D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E88E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E88F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E890
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E891
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E892
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E893
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E894
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E895
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E896
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E897
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E898
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E899
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E89A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E89B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E89C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E89D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E89E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E89F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E8FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E900
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E901
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E902
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E903
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E904
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E905
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E906
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E907
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E908
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E909
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E90A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E90B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E90C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E90D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E90E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E90F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E910
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E911
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E912
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E913
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E914
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E915
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E916
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E917
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E918
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E919
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E91A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E91B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E91C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E91D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E91E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E91F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E920
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E921
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E922
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E923
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E924
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E925
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E926
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E927
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E928
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E929
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E92A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E92B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E92C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E92D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E92E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E92F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E930
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E931
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E932
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E933
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E934
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E935
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E936
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E937
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E938
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E939
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E93A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E93B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E93C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E93D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E93E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E93F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E940
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E941
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E942
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E943
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E944
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E945
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E946
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E947
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E948
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E949
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E94A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E94B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E94C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E94D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E94E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E94F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E950
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E951
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E952
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E953
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E954
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E955
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E956
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E957
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E958
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E959
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E95A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E95B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E95C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E95D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E95E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E95F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E960
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E961
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E962
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E963
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E964
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E965
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E966
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E967
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E968
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E969
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E96A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E96B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E96C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E96D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E96E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E96F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E970
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E971
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E972
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E973
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E974
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E975
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E976
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E977
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E978
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E979
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E97A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E97B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E97C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E97D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E97E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E97F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E980
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E981
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E982
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E983
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E984
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E985
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E986
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E987
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E988
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E989
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E98A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E98B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E98C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E98D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E98E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E98F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E990
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E991
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E992
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E993
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E994
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E995
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E996
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E997
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E998
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E999
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E99A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E99B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E99C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E99D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E99E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E99F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+E9FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA00
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA01
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA02
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA03
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA04
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA05
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA06
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA07
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA08
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA09
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA0A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA0B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA0C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA0D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA0E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA0F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA10
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA11
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA12
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA13
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA14
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA15
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA16
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA17
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA18
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA19
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA1A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA1B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA1C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA1D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA1E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA1F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA20
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA21
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA22
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA23
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA24
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA25
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA26
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA27
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA28
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA29
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA2A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA2B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA2C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA2D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA2E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA2F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA30
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA31
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA32
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA33
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA34
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA35
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA36
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA37
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA38
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA39
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA3A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA3B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA3C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA3D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA3E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA3F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA40
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA41
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA42
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA43
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA44
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA45
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA46
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA47
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA48
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA49
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA4A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA4B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA4C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA4D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA4E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA4F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA50
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA51
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA52
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA53
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA54
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA55
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA56
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA57
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA58
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA59
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA5A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA5B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA5C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA5D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA5E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA5F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA60
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA61
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA62
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA63
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA64
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA65
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA66
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA67
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA68
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA69
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA6A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA6B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA6C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA6D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA6E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA6F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA70
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA71
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA72
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA73
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA74
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA75
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA76
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA77
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA78
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA79
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA7A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA7B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA7C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA7D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA7E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA7F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA80
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA81
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA82
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA83
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA84
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA85
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA86
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA87
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA88
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA89
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA8A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA8B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA8C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA8D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA8E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA8F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA90
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA91
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA92
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA93
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA94
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA95
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA96
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA97
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA98
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA99
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA9A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA9B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA9C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA9D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA9E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EA9F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAA9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAAA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAAB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAAC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAAD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAAE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAAF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAB9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EABA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EABB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EABC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EABD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EABE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EABF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAC9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EACA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EACB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EACC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EACD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EACE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EACF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAD9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EADA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EADB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EADC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EADD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EADE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EADF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAE9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAEA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAEB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAEC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAEE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAEF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAF9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAFA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAFB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAFC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAFD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAFE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EAFF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB00
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB01
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB02
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB03
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB04
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB05
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB06
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB07
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB08
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB09
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB0A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB0B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB0C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB0D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB0E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB0F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB10
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB11
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB12
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB13
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB14
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB15
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB16
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB17
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB18
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB19
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB1A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB1B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB1C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB1D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB1E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB1F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB20
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB21
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB22
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB23
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB24
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB25
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB26
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB27
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB28
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB29
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB2A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB2B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB2C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB2D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB2E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB2F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB30
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB31
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB32
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB33
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB34
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB35
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB36
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB37
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB38
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB39
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB3A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB3B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB3C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB3D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB3E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB3F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB40
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB41
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB42
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB43
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB44
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB45
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB46
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB47
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB48
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB49
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB4A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB4B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB4C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB4D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB4E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB4F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB50
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB51
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB52
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB53
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB54
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB55
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB56
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB57
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB58
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB59
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB5A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB5B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB5C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB5D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB5E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB5F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB60
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB61
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB62
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB63
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB64
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB65
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB66
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB67
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB68
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB69
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB6A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB6B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB6C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB6D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB6E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB6F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB70
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB71
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB72
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB73
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB74
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB75
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB76
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB77
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB78
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB79
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB7A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB7B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB7C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB7D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB7E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB7F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB80
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB81
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB82
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB83
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB84
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB85
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB86
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB87
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB88
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB89
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB8A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB8B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB8C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB8D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB8E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB8F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB90
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB91
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB92
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB93
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB94
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB95
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB96
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB97
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB98
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB99
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB9A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB9B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB9C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB9D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB9E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EB9F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBA9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBAA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBAB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBAC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBAD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBAE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBAF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBB9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBBA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBBB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBBC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBBD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBBE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBBF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBC9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBCA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBCB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBCC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBCD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBCE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBCF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBD9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBDA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBDB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBDC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBDD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBDE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBDF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBE9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBEA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBEB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBEC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBEE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBEF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBF9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBFA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBFB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBFC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBFD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBFE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EBFF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC00
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC01
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC02
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC03
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC04
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC05
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC06
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC07
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC08
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC09
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC0A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC0B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC0C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC0D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC0E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC0F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC10
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC11
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC12
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC13
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC14
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC15
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC16
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC17
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC18
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC19
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC1A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC1B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC1C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC1D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC1E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC1F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC20
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC21
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC22
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC23
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC24
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC25
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC26
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC27
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC28
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC29
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC2A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC2B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC2C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC2D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC2E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC2F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC30
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC31
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC32
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC33
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC34
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC35
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC36
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC37
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC38
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC39
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC3A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC3B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC3C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC3D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC3E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC3F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC40
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC41
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC42
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC43
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC44
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC45
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC46
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC47
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC48
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC49
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC4A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC4B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC4C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC4D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC4E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC4F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC50
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC51
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC52
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC53
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC54
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC55
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC56
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC57
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC58
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC59
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC5A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC5B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC5C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC5D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC5E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC5F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC60
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC61
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC62
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC63
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC64
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC65
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC66
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC67
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC68
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC69
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC6A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC6B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC6C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC6D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC6E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC6F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC70
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC71
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC72
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC73
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC74
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC75
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC76
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC77
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC78
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC79
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC7A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC7B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC7C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC7D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC7E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC7F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC80
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC81
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC82
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC83
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC84
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC85
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC86
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC87
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC88
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC89
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC8A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC8B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC8C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC8D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC8E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC8F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC90
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC91
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC92
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC93
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC94
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC95
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC96
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC97
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC98
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC99
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC9A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC9B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC9C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC9D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC9E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EC9F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECA9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECAA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECAB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECAC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECAD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECAE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECAF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECB9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECBA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECBB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECBC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECBD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECBE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECBF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECC9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECCA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECCB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECCC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECCD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECCE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECCF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECD9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECDA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECDB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECDC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECDD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECDE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECDF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECE9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECEA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECEB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECEC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECEE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECEF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECF9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECFA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECFB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECFC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECFD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECFE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ECFF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED00
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED01
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED02
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED03
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED04
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED05
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED06
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED07
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED08
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED09
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED0A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED0B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED0C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED0D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED0E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED0F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED10
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED11
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED12
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED13
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED14
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED15
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED16
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED17
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED18
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED19
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED1A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED1B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED1C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED1D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED1E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED1F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED20
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED21
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED22
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED23
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED24
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED25
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED26
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED27
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED28
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED29
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED2A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED2B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED2C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED2D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED2E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED2F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED30
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED31
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED32
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED33
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED34
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED35
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED36
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED37
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED38
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED39
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED3A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED3B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED3C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED3D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED3E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED3F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED40
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED41
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED42
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED43
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED44
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED45
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED46
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED47
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED48
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED49
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED4A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED4B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED4C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED4D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED4E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED4F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED50
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED51
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED52
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED53
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED54
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED55
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED56
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED57
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED58
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED59
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED5A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED5B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED5C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED5D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED5E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED5F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED60
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED61
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED62
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED63
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED64
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED65
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED66
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED67
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED68
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED69
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED6A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED6B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED6C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED6D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED6E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED6F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED70
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED71
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED72
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED73
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED74
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED75
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED76
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED77
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED78
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED79
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED7A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED7B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED7C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED7D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED7E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED7F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED80
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED81
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED82
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED83
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED84
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED85
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED86
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED87
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED88
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED89
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED8A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED8B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED8C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED8D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED8E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED8F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED90
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED91
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED92
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED93
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED94
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED95
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED96
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED97
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED98
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED99
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED9A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED9B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED9C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED9D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED9E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+ED9F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDA9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDAA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDAB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDAC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDAD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDAE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDAF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDB9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDBA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDBB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDBC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDBD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDBE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDBF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDC9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDCA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDCB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDCC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDCD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDCE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDCF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDD9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDDA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDDB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDDC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDDD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDDE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDDF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDE9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDEA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDEB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDEC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDEE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDEF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDF9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDFA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDFB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDFC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDFD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDFE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EDFF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE00
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE01
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE02
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE03
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE04
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE05
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE06
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE07
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE08
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE09
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE0A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE0B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE0C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE0D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE0E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE0F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE10
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE11
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE12
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE13
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE14
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE15
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE16
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE17
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE18
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE19
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE1A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE1B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE1C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE1D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE1E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE1F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE20
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE21
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE22
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE23
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE24
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE25
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE26
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE27
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE28
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE29
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE2A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE2B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE2C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE2D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE2E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE2F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE30
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE31
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE32
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE33
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE34
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE35
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE36
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE37
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE38
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE39
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE3A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE3B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE3C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE3D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE3E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE3F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE40
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE41
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE42
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE43
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE44
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE45
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE46
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE47
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE48
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE49
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE4A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE4B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE4C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE4D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE4E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE4F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE50
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE51
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE52
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE53
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE54
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE55
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE56
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE57
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE58
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE59
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE5A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE5B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE5C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE5D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE5E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE5F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE60
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE61
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE62
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE63
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE64
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE65
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE66
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE67
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE68
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE69
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE6A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE6B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE6C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE6D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE6E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE6F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE70
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE71
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE72
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE73
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE74
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE75
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE76
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE77
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE78
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE79
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE7A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE7B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE7C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE7D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE7E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE7F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE80
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE81
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE82
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE83
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE84
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE85
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE86
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE87
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE88
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE89
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE8A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE8B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE8C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE8D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE8E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE8F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE90
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE91
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE92
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE93
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE94
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE95
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE96
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE97
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE98
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE99
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE9A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE9B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE9C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE9D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE9E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EE9F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEA9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEAA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEAB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEAC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEAD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEAE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEAF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEB9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEBA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEBB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEBC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEBD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEBE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEBF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEC9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EECA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EECB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EECC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EECD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EECE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EECF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EED9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEDA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEDB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEDC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEDD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEDE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEDF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEE9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEEA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEEB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEEC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEEE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEEF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEF9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEFA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEFB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEFC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEFD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEFE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EEFF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF00
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF01
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF02
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF03
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF04
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF05
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF06
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF07
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF08
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF09
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF0A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF0B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF0C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF0D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF0E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF0F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF10
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF11
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF12
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF13
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF14
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF15
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF16
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF17
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF18
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF19
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF1A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF1B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF1C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF1D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF1E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF1F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF20
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF21
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF22
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF23
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF24
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF25
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF26
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF27
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF28
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF29
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF2A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF2B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF2C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF2D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF2E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF2F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF30
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF31
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF32
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF33
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF34
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF35
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF36
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF37
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF38
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF39
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF3A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF3B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF3C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF3D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF3E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF3F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF40
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF41
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF42
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF43
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF44
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF45
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF46
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF47
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF48
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF49
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF4A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF4B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF4C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF4D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF4E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF4F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF50
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF51
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF52
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF53
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF54
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF55
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF56
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF57
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF58
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF59
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF5A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF5B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF5C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF5D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF5E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF5F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF60
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF61
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF62
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF63
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF64
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF65
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF66
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF67
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF68
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF69
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF6A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF6B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF6C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF6D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF6E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF6F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF70
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF71
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF72
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF73
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF74
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF75
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF76
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF77
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF78
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF79
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF7A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF7B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF7C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF7D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF7E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF7F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF80
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF81
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF82
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF83
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF84
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF85
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF86
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF87
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF88
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF89
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF8A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF8B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF8C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF8D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF8E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF8F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF90
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF91
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF92
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF93
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF94
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF95
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF96
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF97
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF98
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF99
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF9A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF9B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF9C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF9D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF9E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EF9F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFA9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFAA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFAB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFAC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFAD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFAE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFAF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFB9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFBA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFBB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFBC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFBD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFBE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFBF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFC9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFCA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFCB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFCC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFCD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFCE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFCF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFD9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFDA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFDB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFDC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFDD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFDE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFDF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFE9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFEA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFEB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFEC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFEE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFEF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFF9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFFA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFFB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFFC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFFD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFFE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+EFFF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F000
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F001
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F002
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F003
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F004
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F005
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F006
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F007
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F008
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F009
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F00A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F00B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F00C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F00D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F00E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F00F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F010
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F011
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F012
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F013
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F014
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F015
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F016
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F017
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F018
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F019
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F01A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F01B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F01C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F01D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F01E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F01F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F020
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F021
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F022
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F023
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F024
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F025
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F026
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F027
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F028
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F029
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F02A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F02B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F02C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F02D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F02E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F02F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F030
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F031
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F032
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F033
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F034
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F035
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F036
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F037
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F038
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F039
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F03A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F03B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F03C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F03D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F03E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F03F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F040
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F041
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F042
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F043
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F044
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F045
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F046
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F047
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F048
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F049
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F04A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F04B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F04C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F04D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F04E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F04F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F050
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F051
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F052
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F053
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F054
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F055
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F056
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F057
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F058
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F059
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F05A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F05B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F05C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F05D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F05E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F05F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F060
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F061
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F062
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F063
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F064
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F065
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F066
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F067
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F068
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F069
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F06A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F06B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F06C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F06D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F06E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F06F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F070
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F071
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F072
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F073
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F074
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F075
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F076
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F077
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F078
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F079
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F07A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F07B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F07C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F07D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F07E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F07F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F080
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F081
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F082
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F083
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F084
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F085
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F086
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F087
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F088
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F089
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F08A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F08B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F08C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F08D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F08E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F08F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F090
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F091
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F092
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F093
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F094
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F095
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F096
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F097
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F098
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F099
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F09A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F09B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F09C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F09D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F09E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F09F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F0FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F100
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F101
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F102
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F103
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F104
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F105
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F106
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F107
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F108
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F109
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F10A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F10B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F10C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F10D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F10E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F10F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F110
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F111
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F112
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F113
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F114
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F115
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F116
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F117
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F118
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F119
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F11A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F11B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F11C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F11D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F11E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F11F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F120
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F121
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F122
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F123
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F124
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F125
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F126
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F127
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F128
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F129
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F12A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F12B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F12C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F12D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F12E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F12F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F130
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F131
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F132
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F133
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F134
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F135
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F136
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F137
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F138
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F139
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F13A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F13B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F13C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F13D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F13E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F13F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F140
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F141
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F142
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F143
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F144
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F145
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F146
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F147
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F148
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F149
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F14A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F14B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F14C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F14D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F14E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F14F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F150
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F151
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F152
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F153
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F154
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F155
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F156
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F157
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F158
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F159
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F15A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F15B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F15C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F15D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F15E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F15F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F160
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F161
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F162
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F163
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F164
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F165
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F166
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F167
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F168
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F169
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F16A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F16B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F16C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F16D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F16E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F16F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F170
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F171
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F172
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F173
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F174
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F175
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F176
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F177
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F178
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F179
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F17A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F17B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F17C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F17D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F17E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F17F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F180
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F181
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F182
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F183
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F184
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F185
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F186
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F187
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F188
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F189
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F18A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F18B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F18C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F18D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F18E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F18F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F190
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F191
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F192
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F193
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F194
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F195
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F196
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F197
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F198
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F199
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F19A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F19B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F19C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F19D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F19E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F19F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F1FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F200
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F201
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F202
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F203
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F204
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F205
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F206
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F207
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F208
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F209
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F20A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F20B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F20C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F20D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F20E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F20F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F210
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F211
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F212
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F213
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F214
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F215
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F216
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F217
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F218
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F219
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F21A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F21B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F21C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F21D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F21E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F21F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F220
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F221
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F222
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F223
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F224
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F225
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F226
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F227
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F228
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F229
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F22A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F22B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F22C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F22D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F22E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F22F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F230
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F231
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F232
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F233
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F234
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F235
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F236
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F237
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F238
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F239
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F23A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F23B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F23C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F23D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F23E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F23F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F240
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F241
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F242
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F243
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F244
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F245
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F246
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F247
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F248
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F249
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F24A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F24B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F24C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F24D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F24E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F24F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F250
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F251
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F252
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F253
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F254
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F255
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F256
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F257
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F258
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F259
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F25A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F25B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F25C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F25D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F25E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F25F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F260
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F261
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F262
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F263
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F264
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F265
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F266
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F267
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F268
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F269
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F26A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F26B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F26C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F26D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F26E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F26F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F270
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F271
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F272
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F273
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F274
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F275
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F276
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F277
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F278
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F279
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F27A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F27B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F27C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F27D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F27E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F27F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F280
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F281
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F282
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F283
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F284
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F285
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F286
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F287
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F288
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F289
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F28A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F28B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F28C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F28D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F28E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F28F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F290
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F291
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F292
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F293
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F294
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F295
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F296
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F297
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F298
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F299
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F29A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F29B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F29C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F29D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F29E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F29F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F2FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F300
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F301
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F302
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F303
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F304
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F305
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F306
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F307
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F308
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F309
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F30A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F30B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F30C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F30D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F30E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F30F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F310
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F311
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F312
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F313
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F314
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F315
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F316
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F317
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F318
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F319
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F31A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F31B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F31C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F31D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F31E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F31F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F320
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F321
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F322
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F323
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F324
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F325
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F326
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F327
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F328
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F329
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F32A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F32B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F32C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F32D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F32E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F32F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F330
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F331
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F332
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F333
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F334
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F335
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F336
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F337
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F338
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F339
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F33A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F33B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F33C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F33D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F33E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F33F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F340
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F341
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F342
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F343
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F344
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F345
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F346
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F347
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F348
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F349
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F34A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F34B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F34C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F34D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F34E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F34F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F350
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F351
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F352
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F353
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F354
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F355
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F356
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F357
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F358
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F359
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F35A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F35B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F35C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F35D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F35E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F35F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F360
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F361
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F362
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F363
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F364
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F365
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F366
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F367
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F368
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F369
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F36A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F36B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F36C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F36D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F36E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F36F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F370
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F371
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F372
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F373
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F374
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F375
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F376
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F377
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F378
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F379
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F37A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F37B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F37C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F37D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F37E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F37F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F380
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F381
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F382
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F383
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F384
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F385
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F386
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F387
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F388
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F389
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F38A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F38B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F38C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F38D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F38E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F38F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F390
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F391
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F392
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F393
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F394
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F395
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F396
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F397
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F398
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F399
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F39A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F39B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F39C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F39D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F39E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F39F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F3FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F400
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F401
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F402
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F403
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F404
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F405
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F406
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F407
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F408
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F409
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F40A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F40B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F40C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F40D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F40E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F40F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F410
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F411
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F412
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F413
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F414
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F415
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F416
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F417
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F418
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F419
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F41A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F41B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F41C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F41D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F41E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F41F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F420
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F421
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F422
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F423
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F424
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F425
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F426
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F427
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F428
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F429
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F42A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F42B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F42C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F42D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F42E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F42F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F430
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F431
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F432
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F433
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F434
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F435
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F436
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F437
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F438
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F439
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F43A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F43B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F43C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F43D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F43E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F43F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F440
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F441
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F442
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F443
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F444
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F445
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F446
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F447
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F448
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F449
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F44A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F44B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F44C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F44D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F44E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F44F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F450
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F451
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F452
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F453
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F454
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F455
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F456
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F457
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F458
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F459
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F45A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F45B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F45C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F45D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F45E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F45F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F460
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F461
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F462
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F463
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F464
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F465
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F466
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F467
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F468
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F469
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F46A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F46B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F46C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F46D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F46E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F46F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F470
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F471
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F472
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F473
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F474
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F475
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F476
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F477
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F478
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F479
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F47A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F47B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F47C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F47D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F47E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F47F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F480
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F481
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F482
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F483
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F484
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F485
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F486
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F487
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F488
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F489
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F48A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F48B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F48C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F48D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F48E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F48F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F490
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F491
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F492
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F493
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F494
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F495
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F496
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F497
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F498
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F499
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F49A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F49B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F49C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F49D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F49E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F49F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F4FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F500
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F501
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F502
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F503
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F504
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F505
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F506
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F507
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F508
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F509
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F50A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F50B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F50C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F50D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F50E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F50F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F510
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F511
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F512
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F513
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F514
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F515
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F516
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F517
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F518
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F519
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F51A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F51B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F51C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F51D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F51E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F51F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F520
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F521
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F522
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F523
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F524
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F525
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F526
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F527
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F528
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F529
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F52A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F52B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F52C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F52D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F52E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F52F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F530
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F531
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F532
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F533
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F534
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F535
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F536
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F537
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F538
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F539
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F53A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F53B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F53C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F53D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F53E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F53F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F540
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F541
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F542
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F543
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F544
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F545
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F546
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F547
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F548
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F549
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F54A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F54B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F54C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F54D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F54E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F54F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F550
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F551
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F552
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F553
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F554
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F555
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F556
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F557
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F558
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F559
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F55A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F55B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F55C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F55D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F55E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F55F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F560
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F561
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F562
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F563
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F564
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F565
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F566
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F567
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F568
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F569
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F56A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F56B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F56C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F56D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F56E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F56F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F570
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F571
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F572
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F573
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F574
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F575
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F576
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F577
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F578
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F579
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F57A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F57B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F57C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F57D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F57E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F57F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F580
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F581
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F582
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F583
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F584
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F585
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F586
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F587
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F588
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F589
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F58A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F58B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F58C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F58D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F58E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F58F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F590
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F591
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F592
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F593
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F594
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F595
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F596
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F597
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F598
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F599
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F59A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F59B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F59C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F59D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F59E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F59F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F5FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F600
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F601
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F602
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F603
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F604
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F605
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F606
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F607
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F608
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F609
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F60A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F60B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F60C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F60D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F60E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F60F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F610
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F611
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F612
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F613
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F614
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F615
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F616
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F617
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F618
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F619
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F61A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F61B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F61C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F61D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F61E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F61F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F620
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F621
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F622
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F623
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F624
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F625
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F626
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F627
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F628
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F629
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F62A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F62B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F62C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F62D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F62E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F62F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F630
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F631
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F632
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F633
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F634
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F635
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F636
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F637
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F638
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F639
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F63A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F63B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F63C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F63D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F63E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F63F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F640
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F641
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F642
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F643
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F644
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F645
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F646
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F647
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F648
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F649
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F64A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F64B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F64C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F64D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F64E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F64F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F650
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F651
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F652
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F653
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F654
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F655
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F656
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F657
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F658
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F659
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F65A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F65B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F65C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F65D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F65E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F65F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F660
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F661
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F662
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F663
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F664
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F665
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F666
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F667
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F668
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F669
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F66A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F66B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F66C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F66D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F66E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F66F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F670
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F671
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F672
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F673
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F674
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F675
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F676
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F677
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F678
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F679
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F67A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F67B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F67C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F67D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F67E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F67F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F680
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F681
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F682
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F683
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F684
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F685
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F686
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F687
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F688
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F689
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F68A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F68B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F68C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F68D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F68E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F68F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F690
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F691
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F692
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F693
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F694
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F695
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F696
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F697
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F698
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F699
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F69A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F69B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F69C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F69D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F69E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F69F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F6FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F700
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F701
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F702
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F703
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F704
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F705
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F706
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F707
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F708
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F709
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F70A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F70B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F70C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F70D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F70E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F70F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F710
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F711
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F712
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F713
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F714
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F715
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F716
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F717
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F718
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F719
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F71A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F71B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F71C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F71D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F71E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F71F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F720
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F721
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F722
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F723
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F724
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F725
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F726
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F727
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F728
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F729
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F72A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F72B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F72C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F72D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F72E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F72F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F730
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F731
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F732
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F733
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F734
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F735
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F736
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F737
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F738
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F739
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F73A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F73B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F73C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F73D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F73E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F73F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F740
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F741
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F742
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F743
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F744
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F745
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F746
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F747
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F748
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F749
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F74A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F74B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F74C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F74D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F74E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F74F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F750
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F751
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F752
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F753
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F754
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F755
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F756
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F757
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F758
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F759
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F75A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F75B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F75C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F75D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F75E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F75F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F760
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F761
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F762
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F763
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F764
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F765
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F766
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F767
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F768
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F769
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F76A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F76B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F76C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F76D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F76E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F76F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F770
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F771
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F772
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F773
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F774
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F775
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F776
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F777
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F778
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F779
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F77A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F77B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F77C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F77D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F77E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F77F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F780
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F781
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F782
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F783
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F784
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F785
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F786
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F787
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F788
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F789
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F78A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F78B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F78C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F78D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F78E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F78F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F790
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F791
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F792
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F793
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F794
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F795
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F796
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F797
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F798
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F799
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F79A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F79B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F79C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F79D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F79E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F79F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7FE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F7FF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F800
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F801
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F802
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F803
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F804
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F805
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F806
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F807
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F808
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F809
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F80A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F80B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F80C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F80D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F80E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F80F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F810
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F811
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F812
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F813
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F814
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F815
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F816
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F817
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F818
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F819
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F81A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F81B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F81C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F81D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F81E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F81F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F820
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F821
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F822
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F823
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F824
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F825
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F826
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F827
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F828
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F829
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F82A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F82B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F82C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F82D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F82E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F82F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F830
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F831
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F832
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F833
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F834
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F835
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F836
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F837
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F838
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F839
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F83A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F83B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F83C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F83D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F83E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F83F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F840
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F841
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F842
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F843
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F844
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F845
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F846
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F847
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F848
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F849
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F84A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F84B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F84C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F84D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F84E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F84F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F850
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F851
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F852
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F853
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F854
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F855
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F856
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F857
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F858
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F859
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F85A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F85B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F85C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F85D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F85E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F85F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F860
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F861
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F862
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F863
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F864
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F865
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F866
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F867
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F868
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F869
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F86A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F86B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F86C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F86D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F86E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F86F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F870
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F871
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F872
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F873
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F874
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F875
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F876
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F877
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F878
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F879
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F87A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F87B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F87C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F87D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F87E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F87F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F880
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F881
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F882
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F883
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F884
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F885
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F886
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F887
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F888
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F889
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F88A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F88B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F88C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F88D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F88E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F88F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F890
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F891
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F892
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F893
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F894
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F895
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F896
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F897
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F898
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F899
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F89A
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F89B
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F89C
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F89D
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F89E
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F89F
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8A9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8AA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8AB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8AC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8AD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8AE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8AF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8B9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8BA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8BB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8BC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8BD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8BE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8BF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8C9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8CA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8CB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8CC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8CD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8CE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8CF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8D9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8DA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8DB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8DC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8DD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8DE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8DF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8E9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8EA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8EB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8EC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8ED
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8EE
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8EF
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F0
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F1
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F2
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F3
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F4
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F5
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F6
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F7
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F8
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8F9
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8FA
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8FB
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8FC
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8FD
+  CHARPROP____(0x1ffu, kNormal, kL, kXX)  // U+F8FE
+  CHARPROP____(0x1ffu, kUnknown, kL, kNONE)  // U+F8FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F900
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F901
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F902
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F903
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F904
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F905
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F906
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F907
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F908
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F909
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F90A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F90B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F90C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F90D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F90E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F90F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F910
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F911
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F912
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F913
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F914
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F915
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F916
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F917
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F918
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F919
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F91A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F91B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F91C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F91D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F91E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F91F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F920
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F921
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F922
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F923
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F924
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F925
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F926
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F927
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F928
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F929
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F92A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F92B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F92C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F92D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F92E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F92F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F930
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F931
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F932
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F933
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F934
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F935
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F936
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F937
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F938
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F939
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F93A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F93B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F93C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F93D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F93E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F93F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F940
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F941
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F942
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F943
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F944
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F945
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F946
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F947
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F948
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F949
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F94A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F94B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F94C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F94D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F94E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F94F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F950
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F951
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F952
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F953
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F954
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F955
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F956
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F957
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F958
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F959
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F95A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F95B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F95C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F95D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F95E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F95F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F960
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F961
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F962
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F963
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F964
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F965
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F966
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F967
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F968
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F969
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F96A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F96B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F96C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F96D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F96E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F96F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F970
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F971
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F972
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F973
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F974
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F975
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F976
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F977
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F978
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F979
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F97A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F97B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F97C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F97D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F97E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F97F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F980
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F981
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F982
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F983
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F984
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F985
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F986
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F987
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F988
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F989
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F98A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F98B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F98C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F98D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F98E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F98F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F990
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F991
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F992
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F993
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F994
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F995
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F996
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F997
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F998
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F999
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F99A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F99B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F99C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F99D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F99E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F99F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9A9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9AA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9AB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9AC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9AD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9AE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9AF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9B9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9BA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9BB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9BC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9BD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9BE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9BF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9C9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9CA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9CB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9CC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9CD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9CE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9CF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9D9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9DA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9DB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9DC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9DD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9DE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9DF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9E9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9EA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9EB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9EC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9ED
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9EE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9EF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9F9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9FA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9FB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9FC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9FD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9FE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+F9FF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA00
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA01
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA02
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA03
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA04
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA05
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA06
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA07
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA08
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA09
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA0A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA0B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA0C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA0D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA0E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA0F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA10
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA11
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA12
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA13
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA14
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA15
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA16
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA17
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA18
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA19
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA1A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA1B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA1C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA1D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA1E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA1F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA2D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FA2E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FA2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA3A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA3B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA3C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA3D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA3E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA3F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA5A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA5B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA5C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA5D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA5E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA5F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA60
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA61
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA62
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA63
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA64
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA65
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA66
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA67
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA68
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA69
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA6A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA6B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA6C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA6D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FA6E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FA6F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA70
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA71
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA72
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA73
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA74
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA75
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA76
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA77
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA78
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA79
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA7A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA7B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA7C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA7D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA7E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA7F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA80
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA81
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA82
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA83
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA84
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA85
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA86
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA87
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA88
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA89
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA8A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA8B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA8C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA8D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA8E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA8F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA90
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA91
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA92
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA93
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA94
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA95
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA96
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA97
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA98
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA99
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA9A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA9B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA9C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA9D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA9E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FA9F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAA9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAAA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAAB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAAC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAAD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAAE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAAF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAB9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FABA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FABB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FABC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FABD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FABE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FABF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAC9
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FACA
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FACB
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FACC
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FACD
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FACE
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FACF
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD0
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD1
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD2
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD3
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD4
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD5
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD6
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD7
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD8
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FAD9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FADA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FADB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FADC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FADD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FADE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FADF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAE9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAEA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAEB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAEC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAED
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAEE
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAEF
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF0
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF4
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF5
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF6
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF7
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF8
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAF9
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAFA
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAFB
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAFC
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAFD
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FAFE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FAFF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB00
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB01
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB02
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB03
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB04
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB05
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB06
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB07
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB08
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB09
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB0A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB0B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB0C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB0D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB0E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB0F
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB10
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB11
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB12
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB13
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB14
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB15
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB16
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FB17
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB18
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB19
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB1A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB1B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FB1C
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB1D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FB1E
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB1F
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB20
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB21
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB22
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB23
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB24
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB25
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB26
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB27
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB28
+  CHARPROP____(0x1ffu, kNormal, kES, kAL)  // U+FB29
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB2A
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB2B
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB2C
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB2D
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB2E
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB2F
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB30
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB31
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB32
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB33
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB34
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB35
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB36
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+FB37
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB38
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB39
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB3A
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB3B
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB3C
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+FB3D
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB3E
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+FB3F
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB40
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB41
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+FB42
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB43
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB44
+  CHARPROP____(0x1ffu, kUnknown, kR, kNONE)  // U+FB45
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB46
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB47
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB48
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB49
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB4A
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB4B
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB4C
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB4D
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB4E
+  CHARPROP____(0x1ffu, kNormal, kR, kAL)  // U+FB4F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB50
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB51
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB52
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB53
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB54
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB55
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB56
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB57
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB58
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB59
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB5A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB5B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB5C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB5D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB5E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB5F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB60
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB61
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB62
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB63
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB64
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB65
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB66
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB67
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB68
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB69
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB6A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB6B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB6C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB6D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB6E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB6F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB70
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB71
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB72
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB73
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB74
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB75
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB76
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB77
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB78
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB79
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB7A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB7B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB7C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB7D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB7E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB7F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB80
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB81
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB82
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB83
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB84
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB85
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB86
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB87
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB88
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB89
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB8A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB8B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB8C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB8D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB8E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB8F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB90
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB91
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB92
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB93
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB94
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB95
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB96
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB97
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB98
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB99
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB9A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB9B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB9C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB9D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB9E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FB9F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBA9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBAA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBAB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBAC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBAD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBAE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBAF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBB0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBB1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBB9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBBA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBBB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBBC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBBD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBBE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBBF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBC9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBCA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBCB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBCC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBCD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBCE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBCF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBD0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBD1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FBD2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBD3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBD4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBD5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBD6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBD7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBD8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBD9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBDA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBDB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBDC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBDD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBDE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBDF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBE9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBEA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBEB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBEC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBED
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBEE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBEF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBF9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBFA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBFB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBFC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBFD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBFE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FBFF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC00
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC01
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC02
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC03
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC04
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC05
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC06
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC07
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC08
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC09
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC0A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC0B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC0C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC0D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC0E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC0F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC10
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC11
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC12
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC13
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC14
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC15
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC16
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC17
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC18
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC19
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC1A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC1B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC1C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC1D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC1E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC1F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC20
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC21
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC22
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC23
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC24
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC25
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC26
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC27
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC28
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC29
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC2A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC2B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC2C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC2D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC2E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC2F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC30
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC31
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC32
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC33
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC34
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC35
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC36
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC37
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC38
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC39
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC3A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC3B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC3C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC3D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC3E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC3F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC40
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC41
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC42
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC43
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC44
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC45
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC46
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC47
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC48
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC49
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC4A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC4B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC4C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC4D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC4E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC4F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC50
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC51
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC52
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC53
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC54
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC55
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC56
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC57
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC58
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC59
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC5A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC5B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC5C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC5D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC5E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC5F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC60
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC61
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC62
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC63
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC64
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC65
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC66
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC67
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC68
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC69
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC6A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC6B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC6C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC6D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC6E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC6F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC70
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC71
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC72
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC73
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC74
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC75
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC76
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC77
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC78
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC79
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC7A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC7B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC7C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC7D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC7E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC7F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC80
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC81
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC82
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC83
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC84
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC85
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC86
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC87
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC88
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC89
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC8A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC8B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC8C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC8D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC8E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC8F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC90
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC91
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC92
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC93
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC94
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC95
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC96
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC97
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC98
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC99
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC9A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC9B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC9C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC9D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC9E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FC9F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCA9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCAA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCAB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCAC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCAD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCAE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCAF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCB9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCBA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCBB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCBC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCBD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCBE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCBF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCC9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCCA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCCB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCCC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCCD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCCE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCCF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCD9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCDA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCDB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCDC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCDD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCDE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCDF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCE9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCEA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCEB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCEC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCED
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCEE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCEF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCF9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCFA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCFB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCFC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCFD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCFE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FCFF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD00
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD01
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD02
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD03
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD04
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD05
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD06
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD07
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD08
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD09
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD0A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD0B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD0C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD0D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD0E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD0F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD10
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD11
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD12
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD13
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD14
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD15
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD16
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD17
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD18
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD19
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD1A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD1B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD1C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD1D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD1E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD1F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD20
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD21
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD22
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD23
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD24
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD25
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD26
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD27
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD28
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD29
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD2A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD2B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD2C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD2D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD2E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD2F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD30
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD31
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD32
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD33
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD34
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD35
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD36
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD37
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD38
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD39
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD3A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD3B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD3C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD3D
+  CHARPROP____(0x1ffu, kArabicForm, kON, kOP)  // U+FD3E
+  CHARPROP____(0x1ffu, kArabicForm, kON, kCL)  // U+FD3F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD40
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD41
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD42
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD43
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD44
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD45
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD46
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD47
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD48
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD49
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD4A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD4B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD4C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD4D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD4E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD4F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD50
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD51
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD52
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD53
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD54
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD55
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD56
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD57
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD58
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD59
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD5A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD5B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD5C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD5D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD5E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD5F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD60
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD61
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD62
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD63
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD64
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD65
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD66
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD67
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD68
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD69
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD6A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD6B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD6C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD6D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD6E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD6F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD70
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD71
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD72
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD73
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD74
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD75
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD76
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD77
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD78
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD79
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD7A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD7B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD7C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD7D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD7E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD7F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD80
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD81
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD82
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD83
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD84
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD85
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD86
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD87
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD88
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD89
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD8A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD8B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD8C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD8D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD8E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD8F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD90
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FD91
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD92
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD93
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD94
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD95
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD96
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD97
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD98
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD99
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD9A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD9B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD9C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD9D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD9E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FD9F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDA9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDAA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDAB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDAC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDAD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDAE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDAF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDB9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDBA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDBB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDBC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDBD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDBE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDBF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDC7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDC8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDC9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDCA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDCB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDCC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDCD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDCE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDCF
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD0
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD1
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD2
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD3
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD4
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD5
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD6
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD7
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD8
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDD9
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDDA
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDDB
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDDC
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDDD
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDDE
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDDF
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE0
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE1
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE2
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE3
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE4
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE5
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE6
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE7
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE8
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDE9
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDEA
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDEB
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDEC
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDED
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDEE
+  CHARPROP____(0x1ffu, kArabicForm, kBN, kNONE)  // U+FDEF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDF9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDFA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FDFB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kPO)  // U+FDFC
+  CHARPROP____(0x1ffu, kArabicForm, kON, kAL)  // U+FDFD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDFE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FDFF
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE00
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE01
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE02
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE03
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE04
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE05
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE06
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE07
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE08
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE09
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE0A
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE0B
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE0C
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE0D
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE0E
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE0F
+  CHARPROP____(0x1ffu, kNormal, kON, kIS)  // U+FE10
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE11
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE12
+  CHARPROP____(0x1ffu, kNormal, kON, kIS)  // U+FE13
+  CHARPROP____(0x1ffu, kNormal, kON, kIS)  // U+FE14
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+FE15
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+FE16
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE17
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE18
+  CHARPROP____(0x1ffu, kNormal, kON, kIN)  // U+FE19
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE1A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE1B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE1C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE1D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE1E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE1F
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE20
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE21
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE22
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE23
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE24
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE25
+  CHARPROP____(0x1ffu, kCombination, kNSM, kCM)  // U+FE26
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE27
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE28
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE29
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE2A
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE2B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE2C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE2D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE2E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE2F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE30
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE31
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE32
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE33
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE34
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE35
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE36
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE37
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE38
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE39
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE3A
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE3B
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE3C
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE3D
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE3E
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE3F
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE40
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE41
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE42
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE43
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE44
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE45
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE46
+  CHARPROP____(0x1ffu, kNormal, kON, kOP)  // U+FE47
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FE48
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE49
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE4A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE4B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE4C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE4D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE4E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE4F
+  CHARPROP____(0x1ffu, kNormal, kCS, kCL)  // U+FE50
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE51
+  CHARPROP____(0x1ffu, kNormal, kCS, kCL)  // U+FE52
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE53
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+FE54
+  CHARPROP____(0x1ffu, kNormal, kCS, kNS)  // U+FE55
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+FE56
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+FE57
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE58
+  CHARPROP____(0x15au, kNormal, kON, kOP)  // U+FE59
+  CHARPROP____(0x15bu, kNormal, kON, kCL)  // U+FE5A
+  CHARPROP____(0x15cu, kNormal, kON, kOP)  // U+FE5B
+  CHARPROP____(0x15du, kNormal, kON, kCL)  // U+FE5C
+  CHARPROP____(0x15eu, kNormal, kON, kOP)  // U+FE5D
+  CHARPROP____(0x15fu, kNormal, kON, kCL)  // U+FE5E
+  CHARPROP____(0x1ffu, kNormal, kET, kID)  // U+FE5F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE60
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE61
+  CHARPROP____(0x1ffu, kNormal, kES, kID)  // U+FE62
+  CHARPROP____(0x1ffu, kNormal, kES, kID)  // U+FE63
+  CHARPROP____(0x160u, kNormal, kON, kID)  // U+FE64
+  CHARPROP____(0x161u, kNormal, kON, kID)  // U+FE65
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE66
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE67
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE68
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+FE69
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+FE6A
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FE6B
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE6C
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE6D
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE6E
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FE6F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE70
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE71
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE72
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE73
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE74
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FE75
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE76
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE77
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE78
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE79
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE7A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE7B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE7C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE7D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE7E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE7F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE80
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE81
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE82
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE83
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE84
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE85
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE86
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE87
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE88
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE89
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE8A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE8B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE8C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE8D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE8E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE8F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE90
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE91
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE92
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE93
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE94
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE95
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE96
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE97
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE98
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE99
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE9A
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE9B
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE9C
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE9D
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE9E
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FE9F
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEA9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEAA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEAB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEAC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEAD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEAE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEAF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEB9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEBA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEBB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEBC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEBD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEBE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEBF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEC9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FECA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FECB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FECC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FECD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FECE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FECF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FED9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEDA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEDB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEDC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEDD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEDE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEDF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEE9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEEA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEEB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEEC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEED
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEEE
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEEF
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF0
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF1
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF2
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF3
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF4
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF5
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF6
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF7
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF8
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEF9
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEFA
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEFB
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kAL)  // U+FEFC
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FEFD
+  CHARPROP____(0x1ffu, kArabicForm, kAL, kNONE)  // U+FEFE
+  CHARPROP____(0x1ffu, kControl, kBN, kWJ)  // U+FEFF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FF00
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+FF01
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF02
+  CHARPROP____(0x1ffu, kNormal, kET, kID)  // U+FF03
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+FF04
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+FF05
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF06
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF07
+  CHARPROP____(0x162u, kNormal, kON, kOP)  // U+FF08
+  CHARPROP____(0x163u, kNormal, kON, kCL)  // U+FF09
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF0A
+  CHARPROP____(0x1ffu, kNormal, kES, kID)  // U+FF0B
+  CHARPROP____(0x1ffu, kNormal, kCS, kCL)  // U+FF0C
+  CHARPROP____(0x1ffu, kNormal, kES, kID)  // U+FF0D
+  CHARPROP____(0x1ffu, kNormal, kCS, kCL)  // U+FF0E
+  CHARPROP____(0x1ffu, kNormal, kCS, kID)  // U+FF0F
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF10
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF11
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF12
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF13
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF14
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF15
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF16
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF17
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF18
+  CHARPROP____(0x1ffu, kNormal, kEN, kID)  // U+FF19
+  CHARPROP____(0x1ffu, kNormal, kCS, kNS)  // U+FF1A
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+FF1B
+  CHARPROP____(0x164u, kNormal, kON, kID)  // U+FF1C
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF1D
+  CHARPROP____(0x165u, kNormal, kON, kID)  // U+FF1E
+  CHARPROP____(0x1ffu, kNormal, kON, kEX)  // U+FF1F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF20
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF21
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF22
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF23
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF24
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF25
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF26
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF27
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF28
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF29
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF2A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF2B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF2C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF2D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF2E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF2F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF30
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF31
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF32
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF33
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF34
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF35
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF36
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF37
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF38
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF39
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF3A
+  CHARPROP____(0x166u, kNormal, kON, kOP)  // U+FF3B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF3C
+  CHARPROP____(0x167u, kNormal, kON, kCL)  // U+FF3D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF3E
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF3F
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF40
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF41
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF42
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF43
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF44
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF45
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF46
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF47
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF48
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF49
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF4A
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF4B
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF4C
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF4D
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF4E
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF4F
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF50
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF51
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF52
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF53
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF54
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF55
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF56
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF57
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF58
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF59
+  CHARPROP____(0x1ffu, kNormal, kL, kID)  // U+FF5A
+  CHARPROP____(0x168u, kNormal, kON, kOP)  // U+FF5B
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF5C
+  CHARPROP____(0x169u, kNormal, kON, kCL)  // U+FF5D
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FF5E
+  CHARPROP____(0x16au, kNormal, kON, kOP)  // U+FF5F
+  CHARPROP____(0x16bu, kNormal, kON, kCL)  // U+FF60
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FF61
+  CHARPROP____(0x16cu, kNormal, kON, kOP)  // U+FF62
+  CHARPROP____(0x16du, kNormal, kON, kCL)  // U+FF63
+  CHARPROP____(0x1ffu, kNormal, kON, kCL)  // U+FF64
+  CHARPROP____(0x1ffu, kNormal, kON, kNS)  // U+FF65
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF66
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF67
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF68
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF69
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF6A
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF6B
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF6C
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF6D
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF6E
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF6F
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF70
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF71
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF72
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF73
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF74
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF75
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF76
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF77
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF78
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF79
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF7A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF7B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF7C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF7D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF7E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF7F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF80
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF81
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF82
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF83
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF84
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF85
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF86
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF87
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF88
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF89
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF8A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF8B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF8C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF8D
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF8E
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF8F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF90
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF91
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF92
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF93
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF94
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF95
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF96
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF97
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF98
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF99
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF9A
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF9B
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF9C
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FF9D
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF9E
+  CHARPROP____(0x1ffu, kNormal, kL, kNS)  // U+FF9F
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFA9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFAA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFAB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFAC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFAD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFAE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFAF
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB0
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB7
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB8
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFB9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFBA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFBB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFBC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFBD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFBE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFBF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFC0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFC1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFC2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFC3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFC4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFC5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFC6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFC7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFC8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFC9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFCA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFCB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFCC
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFCD
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFCE
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFCF
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFD0
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFD1
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFD2
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFD3
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFD4
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFD5
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFD6
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFD7
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFD8
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFD9
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFDA
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFDB
+  CHARPROP____(0x1ffu, kNormal, kL, kAL)  // U+FFDC
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFDD
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFDE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFDF
+  CHARPROP____(0x1ffu, kNormal, kET, kPO)  // U+FFE0
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+FFE1
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FFE2
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FFE3
+  CHARPROP____(0x1ffu, kNormal, kON, kID)  // U+FFE4
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+FFE5
+  CHARPROP____(0x1ffu, kNormal, kET, kPR)  // U+FFE6
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFE7
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+FFE8
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+FFE9
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+FFEA
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+FFEB
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+FFEC
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+FFED
+  CHARPROP____(0x1ffu, kNormal, kON, kAL)  // U+FFEE
+  CHARPROP____(0x1ffu, kUnknown, kON, kNONE)  // U+FFEF
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF0
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF1
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF2
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF3
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF4
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF5
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF6
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF7
+  CHARPROP____(0x1ffu, kUnknown, kBN, kNONE)  // U+FFF8
+  CHARPROP____(0x1ffu, kNormal, kON, kCM)  // U+FFF9
+  CHARPROP____(0x1ffu, kNormal, kON, kCM)  // U+FFFA
+  CHARPROP____(0x1ffu, kNormal, kON, kCM)  // U+FFFB
+  CHARPROP____(0x1ffu, kNormal, kON, kB2)  // U+FFFC
+  CHARPROP____(0x1ffu, kNormal, kON, kAI)  // U+FFFD
+  CHARPROP____(0x1ffu, kControl, kBN, kNONE)  // U+FFFE
+  CHARPROP____(0x1ffu, kControl, kBN, kNONE)  // U+FFFF
diff --git a/core/fxcrt/fx_unicode.cpp b/core/fxcrt/fx_unicode.cpp
index aafdba1..cbd4f0d 100644
--- a/core/fxcrt/fx_unicode.cpp
+++ b/core/fxcrt/fx_unicode.cpp
@@ -6,36 +6,162 @@
 
 #include "core/fxcrt/fx_unicode.h"
 
-#include "core/fxcrt/fx_ucddata.h"
+#include "core/fxcrt/fx_memory.h"
 
 namespace {
 
-constexpr uint32_t kMirrorBits = 23;
-constexpr uint32_t kMirrorMask = 0x1FFU << kMirrorBits;
+// Format of uint16_t values in kTextLayoutCodeProperties[].
+constexpr uint16_t kBidiClassBitPos = 0;
+constexpr uint16_t kBidiClassBitCount = 5;
+constexpr uint16_t kBidiClassBitMask =
+    (((1u << kBidiClassBitCount) - 1) << kBidiClassBitPos);
 
-wchar_t GetMirrorChar(wchar_t wch, uint32_t dwProps) {
-  uint32_t dwTemp = (dwProps & kMirrorMask);
-  if (dwTemp == kMirrorMask)
-    return wch;
-  size_t idx = dwTemp >> kMirrorBits;
-  return idx < kFXTextLayoutBidiMirrorSize ? kFXTextLayoutBidiMirror[idx] : wch;
-}
+constexpr uint16_t kMirrorBitPos = 5;
+constexpr uint16_t kMirrorBitCount = 9;
+constexpr uint16_t kMirrorMax = (1 << kMirrorBitCount) - 1;
 
-}  // namespace
+#undef CHARPROP____
+#define CHARPROP____(mirror, ct, bd, bt) \
+  ((mirror << kMirrorBitPos) |           \
+   (static_cast<uint16_t>(FX_BIDICLASS::bd) << kBidiClassBitPos)),
+constexpr uint16_t kTextLayoutCodeProperties[] = {
+#include "core/fxcrt/fx_ucddata.inc"
+};
+#undef CHARPROP____
 
-uint32_t FX_GetUnicodeProperties(wchar_t wch) {
+constexpr size_t kTextLayoutCodePropertiesSize =
+    FX_ArraySize(kTextLayoutCodeProperties);
+
+static_assert(kTextLayoutCodePropertiesSize == 65536, "missing characters");
+
+uint16_t GetUnicodeProperties(wchar_t wch) {
   size_t idx = static_cast<size_t>(wch);
   if (idx < kTextLayoutCodePropertiesSize)
     return kTextLayoutCodeProperties[idx];
   return 0;
 }
 
+#ifdef PDF_ENABLE_XFA
+// Format of uint16_t values in kExtendedTextLayoutCodeProperties[].
+constexpr uint16_t kBreakTypeBitPos = 0;
+constexpr uint16_t kBreakTypeBitCount = 6;
+constexpr uint16_t kBreakTypeBitMask =
+    (((1u << kBreakTypeBitCount) - 1) << kBreakTypeBitPos);
+
+constexpr uint16_t kCharTypeBitPos = 6;
+constexpr uint16_t kCharTypeBitCount = 4;
+constexpr uint16_t kCharTypeBitMask =
+    (((1u << kCharTypeBitCount) - 1) << kCharTypeBitPos);
+
+#undef CHARPROP____
+#define CHARPROP____(mirror, ct, bd, bt)                         \
+  ((static_cast<uint16_t>(FX_CHARTYPE::ct) << kCharTypeBitPos) | \
+   (static_cast<uint16_t>(FX_BREAKPROPERTY::bt) << kBreakTypeBitPos)),
+constexpr uint16_t kExtendedTextLayoutCodeProperties[] = {
+#include "core/fxcrt/fx_ucddata.inc"
+};
+#undef CHARPROP____
+
+constexpr size_t kExtendedTextLayoutCodePropertiesSize =
+    FX_ArraySize(kExtendedTextLayoutCodeProperties);
+
+static_assert(kExtendedTextLayoutCodePropertiesSize == 65536,
+              "missing characters");
+
+uint16_t GetExtendedUnicodeProperties(wchar_t wch) {
+  size_t idx = static_cast<size_t>(wch);
+  if (idx < kExtendedTextLayoutCodePropertiesSize)
+    return kExtendedTextLayoutCodeProperties[idx];
+  return 0;
+}
+
+#endif  // PDF_ENABLE_XFA
+
+constexpr uint16_t kFXTextLayoutBidiMirror[] = {
+    0x0029, 0x0028, 0x003E, 0x003C, 0x005D, 0x005B, 0x007D, 0x007B, 0x00BB,
+    0x00AB, 0x0F3B, 0x0F3A, 0x0F3D, 0x0F3C, 0x169C, 0x169B, 0x2019, 0x2018,
+    0x201D, 0x201C, 0x203A, 0x2039, 0x2046, 0x2045, 0x207E, 0x207D, 0x208E,
+    0x208D, 0x220B, 0x220C, 0x220D, 0x2208, 0x2209, 0x220A, 0x29F5, 0x223D,
+    0x223C, 0x22CD, 0x2253, 0x2252, 0x2255, 0x2254, 0x2265, 0x2264, 0x2267,
+    0x2266, 0x2269, 0x2268, 0x226B, 0x226A, 0x226F, 0x226E, 0x2271, 0x2270,
+    0x2273, 0x2272, 0x2275, 0x2274, 0x2277, 0x2276, 0x2279, 0x2278, 0x227B,
+    0x227A, 0x227D, 0x227C, 0x227F, 0x227E, 0x2281, 0x2280, 0x2283, 0x2282,
+    0x2285, 0x2284, 0x2287, 0x2286, 0x2289, 0x2288, 0x228B, 0x228A, 0x2290,
+    0x228F, 0x2292, 0x2291, 0x29B8, 0x22A3, 0x22A2, 0x2ADE, 0x2AE4, 0x2AE3,
+    0x2AE5, 0x22B1, 0x22B0, 0x22B3, 0x22B2, 0x22B5, 0x22B4, 0x22B7, 0x22B6,
+    0x22CA, 0x22C9, 0x22CC, 0x22CB, 0x2243, 0x22D1, 0x22D0, 0x22D7, 0x22D6,
+    0x22D9, 0x22D8, 0x22DB, 0x22DA, 0x22DD, 0x22DC, 0x22DF, 0x22DE, 0x22E1,
+    0x22E0, 0x22E3, 0x22E2, 0x22E5, 0x22E4, 0x22E7, 0x22E6, 0x22E9, 0x22E8,
+    0x22EB, 0x22EA, 0x22ED, 0x22EC, 0x22F1, 0x22F0, 0x22FA, 0x22FB, 0x22FC,
+    0x22FD, 0x22FE, 0x22F2, 0x22F3, 0x22F4, 0x22F6, 0x22F7, 0x2309, 0x2308,
+    0x230B, 0x230A, 0x232A, 0x2329, 0x2769, 0x2768, 0x276B, 0x276A, 0x276D,
+    0x276C, 0x276F, 0x276E, 0x2771, 0x2770, 0x2773, 0x2772, 0x2775, 0x2774,
+    0x27C4, 0x27C3, 0x27C6, 0x27C5, 0x27C9, 0x27C8, 0x27D6, 0x27D5, 0x27DE,
+    0x27DD, 0x27E3, 0x27E2, 0x27E5, 0x27E4, 0x27E7, 0x27E6, 0x27E9, 0x27E8,
+    0x27EB, 0x27EA, 0x27ED, 0x27EC, 0x27EF, 0x27EE, 0x2984, 0x2983, 0x2986,
+    0x2985, 0x2988, 0x2987, 0x298A, 0x2989, 0x298C, 0x298B, 0x2990, 0x298F,
+    0x298E, 0x298D, 0x2992, 0x2991, 0x2994, 0x2993, 0x2996, 0x2995, 0x2998,
+    0x2997, 0x2298, 0x29C1, 0x29C0, 0x29C5, 0x29C4, 0x29D0, 0x29CF, 0x29D2,
+    0x29D1, 0x29D5, 0x29D4, 0x29D9, 0x29D8, 0x29DB, 0x29DA, 0x2215, 0x29F9,
+    0x29F8, 0x29FD, 0x29FC, 0x2A2C, 0x2A2B, 0x2A2E, 0x2A2D, 0x2A35, 0x2A34,
+    0x2A3D, 0x2A3C, 0x2A65, 0x2A64, 0x2A7A, 0x2A79, 0x2A7E, 0x2A7D, 0x2A80,
+    0x2A7F, 0x2A82, 0x2A81, 0x2A84, 0x2A83, 0x2A8C, 0x2A8B, 0x2A92, 0x2A91,
+    0x2A94, 0x2A93, 0x2A96, 0x2A95, 0x2A98, 0x2A97, 0x2A9A, 0x2A99, 0x2A9C,
+    0x2A9B, 0x2AA2, 0x2AA1, 0x2AA7, 0x2AA6, 0x2AA9, 0x2AA8, 0x2AAB, 0x2AAA,
+    0x2AAD, 0x2AAC, 0x2AB0, 0x2AAF, 0x2AB4, 0x2AB3, 0x2ABC, 0x2ABB, 0x2ABE,
+    0x2ABD, 0x2AC0, 0x2ABF, 0x2AC2, 0x2AC1, 0x2AC4, 0x2AC3, 0x2AC6, 0x2AC5,
+    0x2ACE, 0x2ACD, 0x2AD0, 0x2ACF, 0x2AD2, 0x2AD1, 0x2AD4, 0x2AD3, 0x2AD6,
+    0x2AD5, 0x22A6, 0x22A9, 0x22A8, 0x22AB, 0x2AED, 0x2AEC, 0x2AF8, 0x2AF7,
+    0x2AFA, 0x2AF9, 0x2E03, 0x2E02, 0x2E05, 0x2E04, 0x2E0A, 0x2E09, 0x2E0D,
+    0x2E0C, 0x2E1D, 0x2E1C, 0x2E21, 0x2E20, 0x2E23, 0x2E22, 0x2E25, 0x2E24,
+    0x2E27, 0x2E26, 0x2E29, 0x2E28, 0x3009, 0x3008, 0x300B, 0x300A, 0x300D,
+    0x300C, 0x300F, 0x300E, 0x3011, 0x3010, 0x3015, 0x3014, 0x3017, 0x3016,
+    0x3019, 0x3018, 0x301B, 0x301A, 0xFE5A, 0xFE59, 0xFE5C, 0xFE5B, 0xFE5E,
+    0xFE5D, 0xFE65, 0xFE64, 0xFF09, 0xFF08, 0xFF1E, 0xFF1C, 0xFF3D, 0xFF3B,
+    0xFF5D, 0xFF5B, 0xFF60, 0xFF5F, 0xFF63, 0xFF62,
+};
+
+constexpr size_t kFXTextLayoutBidiMirrorSize =
+    FX_ArraySize(kFXTextLayoutBidiMirror);
+
+// Check that the mirror indicies in the fx_ucddata.inc table are in bounds.
+#undef CHARPROP____
+#define CHARPROP____(mirror, ct, bd, bt)                                      \
+  static_assert(mirror == kMirrorMax || mirror < kFXTextLayoutBidiMirrorSize, \
+                "Bad mirror index");
+#include "core/fxcrt/fx_ucddata.inc"
+#undef CHARPROP____
+
+}  // namespace
+
 wchar_t FX_GetMirrorChar(wchar_t wch) {
-  return GetMirrorChar(wch, FX_GetUnicodeProperties(wch));
+  uint16_t prop = GetUnicodeProperties(wch);
+  size_t idx = prop >> kMirrorBitPos;
+  if (idx == kMirrorMax)
+    return wch;
+  ASSERT(idx < kFXTextLayoutBidiMirrorSize);
+  return kFXTextLayoutBidiMirror[idx];
+}
+
+FX_BIDICLASS FX_GetBidiClass(wchar_t wch) {
+  uint16_t prop = GetUnicodeProperties(wch);
+  uint16_t result = (prop & kBidiClassBitMask) >> kBidiClassBitPos;
+  ASSERT(result <= static_cast<uint16_t>(FX_BIDICLASS::kPDF));
+  return static_cast<FX_BIDICLASS>(result);
 }
 
 #ifdef PDF_ENABLE_XFA
-wchar_t FX_GetMirrorChar(wchar_t wch, uint32_t dwProps) {
-  return GetMirrorChar(wch, dwProps);
+FX_CHARTYPE FX_GetCharType(wchar_t wch) {
+  uint16_t prop = GetExtendedUnicodeProperties(wch);
+  uint16_t result = (prop & kCharTypeBitMask) >> kCharTypeBitPos;
+  ASSERT(result <= static_cast<uint16_t>(FX_CHARTYPE::kArabic));
+  return static_cast<FX_CHARTYPE>(result);
+}
+
+FX_BREAKPROPERTY FX_GetBreakProperty(wchar_t wch) {
+  uint16_t prop = GetExtendedUnicodeProperties(wch);
+  uint16_t result = (prop & kBreakTypeBitMask) >> kBreakTypeBitPos;
+  ASSERT(result <= static_cast<uint16_t>(FX_BREAKPROPERTY::kTB));
+  return static_cast<FX_BREAKPROPERTY>(result);
 }
 #endif  // PDF_ENABLE_XFA
diff --git a/core/fxcrt/fx_unicode.h b/core/fxcrt/fx_unicode.h
index 18657bd..a889304 100644
--- a/core/fxcrt/fx_unicode.h
+++ b/core/fxcrt/fx_unicode.h
@@ -7,42 +7,99 @@
 #ifndef CORE_FXCRT_FX_UNICODE_H_
 #define CORE_FXCRT_FX_UNICODE_H_
 
-#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/fx_system.h"
 
-uint32_t FX_GetUnicodeProperties(wchar_t wch);
-wchar_t FX_GetMirrorChar(wchar_t wch);
-
-#ifdef PDF_ENABLE_XFA
-
-// As defined in http://www.unicode.org/reports/tr14
-constexpr uint8_t kBreakPropertySpace = 35;
-constexpr uint8_t kBreakPropertyTB = 37;  // Don't know what this is ...
-
-constexpr uint32_t FX_CHARTYPEBITS = 11;
-constexpr uint32_t FX_CHARTYPEBITSMASK = 0xF << FX_CHARTYPEBITS;
-
-enum FX_CHARTYPE {
-  FX_CHARTYPE_Unknown = 0,
-  FX_CHARTYPE_Tab = (1 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_Space = (2 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_Control = (3 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_Combination = (4 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_Numeric = (5 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_Normal = (6 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_ArabicAlef = (7 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_ArabicSpecial = (8 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_ArabicDistortion = (9 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_ArabicNormal = (10 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_ArabicForm = (11 << FX_CHARTYPEBITS),
-  FX_CHARTYPE_Arabic = (12 << FX_CHARTYPEBITS),
+// NOTE: Order matters, less-than/greater-than comparisons are used.
+enum class FX_BIDICLASS : uint8_t {
+  kON = 0,    // Other Neutral
+  kL = 1,     // Left Letter
+  kR = 2,     // Right Letter
+  kAN = 3,    // Arabic Number
+  kEN = 4,    // European Number
+  kAL = 5,    // Arabic Letter
+  kNSM = 6,   // Non-spacing Mark
+  kCS = 7,    // Common Number Separator
+  kES = 8,    // European Separator
+  kET = 9,    // European Number Terminator
+  kBN = 10,   // Boundary Neutral
+  kS = 11,    // Segment Separator
+  kWS = 12,   // Whitespace
+  kB = 13,    // Paragraph Separator
+  kRLO = 14,  // Right-to-Left Override
+  kRLE = 15,  // Right-to-Left Embedding
+  kLRO = 16,  // Left-to-Right Override
+  kLRE = 17,  // Left-to-Right Embedding
+  kPDF = 18,  // Pop Directional Format
+  kN = kON,
 };
 
-inline FX_CHARTYPE GetCharTypeFromProp(uint32_t prop) {
-  return static_cast<FX_CHARTYPE>(prop & FX_CHARTYPEBITSMASK);
-}
+wchar_t FX_GetMirrorChar(wchar_t wch);
+FX_BIDICLASS FX_GetBidiClass(wchar_t wch);
 
-wchar_t FX_GetMirrorChar(wchar_t wch, uint32_t dwProps);
+#ifdef PDF_ENABLE_XFA
+// As defined in http://www.unicode.org/reports/tr14
+enum class FX_BREAKPROPERTY : uint8_t {
+  kOP = 0,
+  kCL = 1,
+  kQU = 2,
+  kGL = 3,
+  kNS = 4,
+  kEX = 5,
+  kSY = 6,
+  kIS = 7,
+  kPR = 8,
+  kPO = 9,
+  kNU = 10,
+  kAL = 11,
+  kID = 12,
+  kIN = 13,
+  kHY = 14,
+  kBA = 15,
+  kBB = 16,
+  kB2 = 17,
+  kZW = 18,
+  kCM = 19,
+  kWJ = 20,
+  kH2 = 21,
+  kH3 = 22,
+  kJL = 23,
+  kJV = 24,
+  kJT = 25,
+  kBK = 26,
+  kCR = 27,
+  kLF = 28,
+  kNL = 29,
+  kSA = 30,
+  kSG = 31,
+  kCB = 32,
+  kXX = 33,
+  kAI = 34,
+  kSP = 35,
+  kNONE = 36,
+  kTB = 37,
+};
 
+enum class FX_CHARTYPE : uint8_t {
+  kUnknown = 0,
+  kTab,
+  kSpace,
+  kControl,
+  kCombination,
+  kNumeric,
+  kNormal,
+  kArabicAlef,
+  kArabicSpecial,
+  kArabicDistortion,
+  kArabicNormal,
+  kArabicForm,
+  kArabic,
+};
+
+FX_CHARTYPE FX_GetCharType(wchar_t wch);
+
+// Analagous to ULineBreak in icu's uchar.h, but permuted order, and a
+// subset lacking some more recent additions.
+FX_BREAKPROPERTY FX_GetBreakProperty(wchar_t wch);
 #endif  // PDF_ENABLE_XFA
 
 #endif  // CORE_FXCRT_FX_UNICODE_H_
diff --git a/core/fxcrt/ifx_fileaccess.h b/core/fxcrt/ifx_fileaccess.h
deleted file mode 100644
index 9bfe2b4..0000000
--- a/core/fxcrt/ifx_fileaccess.h
+++ /dev/null
@@ -1,38 +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_FXCRT_IFX_FILEACCESS_H_
-#define CORE_FXCRT_IFX_FILEACCESS_H_
-
-#include <algorithm>
-#include <memory>
-
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/fx_string.h"
-
-class IFX_FileAccess {
- public:
-  static std::unique_ptr<IFX_FileAccess> Create();
-  virtual ~IFX_FileAccess() {}
-
-  virtual bool Open(const ByteStringView& fileName, uint32_t dwMode) = 0;
-  virtual bool Open(const WideStringView& fileName, uint32_t dwMode) = 0;
-  virtual void Close() = 0;
-  virtual FX_FILESIZE GetSize() const = 0;
-  virtual FX_FILESIZE GetPosition() const = 0;
-  virtual FX_FILESIZE SetPosition(FX_FILESIZE pos) = 0;
-  virtual size_t Read(void* pBuffer, size_t szBuffer) = 0;
-  virtual size_t Write(const void* pBuffer, size_t szBuffer) = 0;
-  virtual size_t ReadPos(void* pBuffer, size_t szBuffer, FX_FILESIZE pos) = 0;
-  virtual size_t WritePos(const void* pBuffer,
-                          size_t szBuffer,
-                          FX_FILESIZE pos) = 0;
-  virtual bool Flush() = 0;
-  virtual bool Truncate(FX_FILESIZE szFile) = 0;
-};
-
-#endif  // CORE_FXCRT_IFX_FILEACCESS_H_
diff --git a/core/fxcrt/ifx_locale.h b/core/fxcrt/ifx_locale.h
deleted file mode 100644
index 5918e7a..0000000
--- a/core/fxcrt/ifx_locale.h
+++ /dev/null
@@ -1,76 +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_FXCRT_IFX_LOCALE_H_
-#define CORE_FXCRT_IFX_LOCALE_H_
-
-#include "core/fxcrt/cfx_datetime.h"
-#include "core/fxcrt/fx_string.h"
-
-enum FX_LOCALENUMSYMBOL {
-  FX_LOCALENUMSYMBOL_Decimal,
-  FX_LOCALENUMSYMBOL_Grouping,
-  FX_LOCALENUMSYMBOL_Percent,
-  FX_LOCALENUMSYMBOL_Minus,
-  FX_LOCALENUMSYMBOL_Zero,
-  FX_LOCALENUMSYMBOL_CurrencySymbol,
-  FX_LOCALENUMSYMBOL_CurrencyName,
-};
-
-enum FX_LOCALEDATETIMESUBCATEGORY {
-  FX_LOCALEDATETIMESUBCATEGORY_Default,
-  FX_LOCALEDATETIMESUBCATEGORY_Short,
-  FX_LOCALEDATETIMESUBCATEGORY_Medium,
-  FX_LOCALEDATETIMESUBCATEGORY_Full,
-  FX_LOCALEDATETIMESUBCATEGORY_Long,
-};
-
-enum FX_LOCALENUMSUBCATEGORY {
-  FX_LOCALENUMPATTERN_Percent,
-  FX_LOCALENUMPATTERN_Currency,
-  FX_LOCALENUMPATTERN_Decimal,
-  FX_LOCALENUMPATTERN_Integer,
-};
-
-enum FX_LOCALECATEGORY {
-  FX_LOCALECATEGORY_Unknown,
-  FX_LOCALECATEGORY_Date,
-  FX_LOCALECATEGORY_Time,
-  FX_LOCALECATEGORY_DateTime,
-  FX_LOCALECATEGORY_Num,
-  FX_LOCALECATEGORY_Text,
-  FX_LOCALECATEGORY_Zero,
-  FX_LOCALECATEGORY_Null,
-};
-
-enum FX_DATETIMETYPE {
-  FX_DATETIMETYPE_Unknown,
-  FX_DATETIMETYPE_Date,
-  FX_DATETIMETYPE_Time,
-  FX_DATETIMETYPE_DateTime,
-  FX_DATETIMETYPE_TimeDate,
-};
-
-class IFX_Locale {
- public:
-  virtual ~IFX_Locale() {}
-
-  virtual WideString GetName() const = 0;
-  virtual WideString GetNumbericSymbol(FX_LOCALENUMSYMBOL eType) const = 0;
-  virtual WideString GetDateTimeSymbols() const = 0;
-  virtual WideString GetMonthName(int32_t nMonth, bool bAbbr) const = 0;
-  virtual WideString GetDayName(int32_t nWeek, bool bAbbr) const = 0;
-  virtual WideString GetMeridiemName(bool bAM) const = 0;
-  virtual FX_TIMEZONE GetTimeZone() const = 0;
-  virtual WideString GetEraName(bool bAD) const = 0;
-  virtual WideString GetDatePattern(
-      FX_LOCALEDATETIMESUBCATEGORY eType) const = 0;
-  virtual WideString GetTimePattern(
-      FX_LOCALEDATETIMESUBCATEGORY eType) const = 0;
-  virtual WideString GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const = 0;
-};
-
-#endif  // CORE_FXCRT_IFX_LOCALE_H_
diff --git a/core/fxcrt/ifx_pauseindicator.h b/core/fxcrt/ifx_pauseindicator.h
deleted file mode 100644
index 79c6016..0000000
--- a/core/fxcrt/ifx_pauseindicator.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_IFX_PAUSEINDICATOR_H_
-#define CORE_FXCRT_IFX_PAUSEINDICATOR_H_
-
-class IFX_PauseIndicator {
- public:
-  virtual ~IFX_PauseIndicator() {}
-  virtual bool NeedToPauseNow() = 0;
-};
-
-#endif  // CORE_FXCRT_IFX_PAUSEINDICATOR_H_
diff --git a/core/fxcrt/maybe_owned.h b/core/fxcrt/maybe_owned.h
index 11dd686..bbdcd40 100644
--- a/core/fxcrt/maybe_owned.h
+++ b/core/fxcrt/maybe_owned.h
@@ -5,12 +5,11 @@
 #ifndef CORE_FXCRT_MAYBE_OWNED_H_
 #define CORE_FXCRT_MAYBE_OWNED_H_
 
-#include <algorithm>
 #include <memory>
 #include <utility>
 
-#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 namespace fxcrt {
 
@@ -21,8 +20,9 @@
 template <typename T, typename D = std::default_delete<T>>
 class MaybeOwned {
  public:
-  MaybeOwned() : m_pObj(nullptr) {}
+  MaybeOwned() = default;
   explicit MaybeOwned(T* ptr) : m_pObj(ptr) {}
+  explicit MaybeOwned(const UnownedPtr<T>& ptr) : m_pObj(ptr.Get()) {}
   explicit MaybeOwned(std::unique_ptr<T, D> ptr)
       : m_pOwnedObj(std::move(ptr)), m_pObj(m_pOwnedObj.get()) {}
 
@@ -33,25 +33,39 @@
   }
 
   void Reset(std::unique_ptr<T, D> ptr) {
+    m_pObj = ptr.get();
     m_pOwnedObj = std::move(ptr);
-    m_pObj = m_pOwnedObj.get();
   }
   void Reset(T* ptr = nullptr) {
-    m_pOwnedObj.reset();
     m_pObj = ptr;
+    m_pOwnedObj.reset();
+  }
+  // Helpful for untangling a collection of intertwined MaybeOwned<>.
+  void ResetIfUnowned() {
+    if (!IsOwned())
+      Reset();
   }
 
+  T* Get() const { return m_pObj.Get(); }
   bool IsOwned() const { return !!m_pOwnedObj; }
-  T* Get() const { return m_pObj; }
+
+  // Downgrades to unowned, caller takes ownership.
   std::unique_ptr<T, D> Release() {
     ASSERT(IsOwned());
     return std::move(m_pOwnedObj);
   }
 
+  // Downgrades to empty, caller takes ownership.
+  std::unique_ptr<T, D> ReleaseAndClear() {
+    ASSERT(IsOwned());
+    m_pObj = nullptr;
+    return std::move(m_pOwnedObj);
+  }
+
   MaybeOwned& operator=(const MaybeOwned& that) = delete;
   MaybeOwned& operator=(MaybeOwned&& that) {
-    m_pOwnedObj = std::move(that.m_pOwnedObj);
     m_pObj = that.m_pObj;
+    m_pOwnedObj = std::move(that.m_pOwnedObj);
     that.m_pObj = nullptr;
     return *this;
   }
@@ -59,6 +73,10 @@
     Reset(ptr);
     return *this;
   }
+  MaybeOwned& operator=(const UnownedPtr<T>& ptr) {
+    Reset(ptr.Get());
+    return *this;
+  }
   MaybeOwned& operator=(std::unique_ptr<T, D> ptr) {
     Reset(std::move(ptr));
     return *this;
@@ -78,11 +96,11 @@
 
   explicit operator bool() const { return !!m_pObj; }
   T& operator*() const { return *m_pObj; }
-  T* operator->() const { return m_pObj; }
+  T* operator->() const { return m_pObj.Get(); }
 
  private:
   std::unique_ptr<T, D> m_pOwnedObj;
-  T* m_pObj;
+  UnownedPtr<T> m_pObj;
 };
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/maybe_owned_unittest.cpp b/core/fxcrt/maybe_owned_unittest.cpp
index 7182647..53cab6c 100644
--- a/core/fxcrt/maybe_owned_unittest.cpp
+++ b/core/fxcrt/maybe_owned_unittest.cpp
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/ptr_util.h"
 
@@ -80,6 +80,21 @@
   EXPECT_EQ(1, owned_delete_count);
 }
 
+TEST(MaybeOwned, UnownedPtr) {
+  int delete_count = 0;
+  PseudoDeletable thing1(100, &delete_count);
+  PseudoDeletable thing2(200, &delete_count);
+  UnownedPtr<PseudoDeletable> unowned1(&thing1);
+  UnownedPtr<PseudoDeletable> unowned2(&thing2);
+  {
+    MaybeOwned<PseudoDeletable> ptr1(unowned1);
+    MaybeOwned<PseudoDeletable> ptr2(unowned2);
+    ptr2 = unowned1;
+    ptr1 = unowned2;
+  }
+  EXPECT_EQ(0, delete_count);
+}
+
 TEST(MaybeOwned, Owned) {
   int delete_count = 0;
   {
diff --git a/core/fxcrt/observable.h b/core/fxcrt/observable.h
deleted file mode 100644
index 2013b75..0000000
--- a/core/fxcrt/observable.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CORE_FXCRT_OBSERVABLE_H_
-#define CORE_FXCRT_OBSERVABLE_H_
-
-#include <set>
-
-#include "core/fxcrt/fx_system.h"
-#include "third_party/base/stl_util.h"
-
-namespace fxcrt {
-
-template <class T>
-class Observable {
- public:
-  class ObservedPtr {
-   public:
-    ObservedPtr() : m_pObservable(nullptr) {}
-    explicit ObservedPtr(T* pObservable) : m_pObservable(pObservable) {
-      if (m_pObservable)
-        m_pObservable->AddObservedPtr(this);
-    }
-    ObservedPtr(const ObservedPtr& that) : ObservedPtr(that.Get()) {}
-    ~ObservedPtr() {
-      if (m_pObservable)
-        m_pObservable->RemoveObservedPtr(this);
-    }
-    void Reset(T* pObservable = nullptr) {
-      if (m_pObservable)
-        m_pObservable->RemoveObservedPtr(this);
-      m_pObservable = pObservable;
-      if (m_pObservable)
-        m_pObservable->AddObservedPtr(this);
-    }
-    void OnDestroy() {
-      ASSERT(m_pObservable);
-      m_pObservable = nullptr;
-    }
-    ObservedPtr& operator=(const ObservedPtr& that) {
-      Reset(that.Get());
-      return *this;
-    }
-    bool operator==(const ObservedPtr& that) const {
-      return m_pObservable == that.m_pObservable;
-    }
-    bool operator!=(const ObservedPtr& that) const { return !(*this == that); }
-    explicit operator bool() const { return !!m_pObservable; }
-    T* Get() const { return m_pObservable; }
-    T& operator*() const { return *m_pObservable; }
-    T* operator->() const { return m_pObservable; }
-
-   private:
-    T* m_pObservable;
-  };
-
-  Observable() = default;
-  Observable(const Observable& that) = delete;
-  ~Observable() { NotifyObservedPtrs(); }
-  void AddObservedPtr(ObservedPtr* pObservedPtr) {
-    ASSERT(!pdfium::ContainsKey(m_ObservedPtrs, pObservedPtr));
-    m_ObservedPtrs.insert(pObservedPtr);
-  }
-  void RemoveObservedPtr(ObservedPtr* pObservedPtr) {
-    ASSERT(pdfium::ContainsKey(m_ObservedPtrs, pObservedPtr));
-    m_ObservedPtrs.erase(pObservedPtr);
-  }
-  void NotifyObservedPtrs() {
-    for (auto* pObservedPtr : m_ObservedPtrs)
-      pObservedPtr->OnDestroy();
-    m_ObservedPtrs.clear();
-  }
-  Observable& operator=(const Observable& that) = delete;
-
- protected:
-  size_t ActiveObservedPtrsForTesting() const { return m_ObservedPtrs.size(); }
-
- private:
-  std::set<ObservedPtr*> m_ObservedPtrs;
-};
-
-}  // namespace fxcrt
-
-using fxcrt::Observable;
-
-#endif  // CORE_FXCRT_OBSERVABLE_H_
diff --git a/core/fxcrt/observable_unittest.cpp b/core/fxcrt/observable_unittest.cpp
deleted file mode 100644
index 5e64743..0000000
--- a/core/fxcrt/observable_unittest.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/observable.h"
-
-#include <utility>
-#include <vector>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace fxcrt {
-namespace {
-
-class PseudoObservable : public Observable<PseudoObservable> {
- public:
-  PseudoObservable() {}
-  int SomeMethod() { return 42; }
-  size_t ActiveObservedPtrs() const { return ActiveObservedPtrsForTesting(); }
-};
-
-}  // namespace
-
-TEST(ObservePtr, Null) {
-  PseudoObservable::ObservedPtr ptr;
-  EXPECT_EQ(nullptr, ptr.Get());
-}
-
-TEST(ObservePtr, LivesLonger) {
-  PseudoObservable* pObs = new PseudoObservable;
-  PseudoObservable::ObservedPtr ptr(pObs);
-  EXPECT_NE(nullptr, ptr.Get());
-  EXPECT_EQ(1u, pObs->ActiveObservedPtrs());
-  delete pObs;
-  EXPECT_EQ(nullptr, ptr.Get());
-}
-
-TEST(ObservePtr, LivesShorter) {
-  PseudoObservable obs;
-  {
-    PseudoObservable::ObservedPtr ptr(&obs);
-    EXPECT_NE(nullptr, ptr.Get());
-    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
-  }
-  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
-}
-
-TEST(ObservePtr, CopyConstruct) {
-  PseudoObservable obs;
-  {
-    PseudoObservable::ObservedPtr ptr(&obs);
-    EXPECT_NE(nullptr, ptr.Get());
-    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
-    {
-      PseudoObservable::ObservedPtr ptr2(ptr);
-      EXPECT_NE(nullptr, ptr2.Get());
-      EXPECT_EQ(2u, obs.ActiveObservedPtrs());
-    }
-    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
-  }
-  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
-}
-
-TEST(ObservePtr, CopyAssign) {
-  PseudoObservable obs;
-  {
-    PseudoObservable::ObservedPtr ptr(&obs);
-    EXPECT_NE(nullptr, ptr.Get());
-    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
-    {
-      PseudoObservable::ObservedPtr ptr2;
-      ptr2 = ptr;
-      EXPECT_NE(nullptr, ptr2.Get());
-      EXPECT_EQ(2u, obs.ActiveObservedPtrs());
-    }
-    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
-  }
-  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
-}
-
-TEST(ObservePtr, Vector) {
-  PseudoObservable obs;
-  {
-    std::vector<PseudoObservable::ObservedPtr> vec1;
-    std::vector<PseudoObservable::ObservedPtr> vec2;
-    vec1.emplace_back(&obs);
-    vec1.emplace_back(&obs);
-    EXPECT_NE(nullptr, vec1[0].Get());
-    EXPECT_NE(nullptr, vec1[1].Get());
-    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
-    vec2 = vec1;
-    EXPECT_NE(nullptr, vec2[0].Get());
-    EXPECT_NE(nullptr, vec2[1].Get());
-    EXPECT_EQ(4u, obs.ActiveObservedPtrs());
-    vec1.clear();
-    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
-    vec2.resize(10000);
-    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
-    vec2.resize(0);
-    EXPECT_EQ(0u, obs.ActiveObservedPtrs());
-  }
-  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
-}
-
-TEST(ObservePtr, VectorAutoClear) {
-  std::vector<PseudoObservable::ObservedPtr> vec1;
-  {
-    PseudoObservable obs;
-    vec1.emplace_back(&obs);
-    vec1.emplace_back(&obs);
-    EXPECT_NE(nullptr, vec1[0].Get());
-    EXPECT_NE(nullptr, vec1[1].Get());
-    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
-  }
-  EXPECT_EQ(nullptr, vec1[0].Get());
-  EXPECT_EQ(nullptr, vec1[1].Get());
-}
-
-TEST(ObservePtr, ResetNull) {
-  PseudoObservable obs;
-  PseudoObservable::ObservedPtr ptr(&obs);
-  EXPECT_EQ(1u, obs.ActiveObservedPtrs());
-  ptr.Reset();
-  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
-}
-
-TEST(ObservePtr, Reset) {
-  PseudoObservable obs1;
-  PseudoObservable obs2;
-  PseudoObservable::ObservedPtr ptr(&obs1);
-  EXPECT_EQ(1u, obs1.ActiveObservedPtrs());
-  EXPECT_EQ(0u, obs2.ActiveObservedPtrs());
-  ptr.Reset(&obs2);
-  EXPECT_EQ(0u, obs1.ActiveObservedPtrs());
-  EXPECT_EQ(1u, obs2.ActiveObservedPtrs());
-}
-
-TEST(ObservePtr, Equals) {
-  PseudoObservable obj1;
-  PseudoObservable obj2;
-  PseudoObservable::ObservedPtr null_ptr1;
-  PseudoObservable::ObservedPtr obj1_ptr1(&obj1);
-  PseudoObservable::ObservedPtr obj2_ptr1(&obj2);
-  {
-    PseudoObservable::ObservedPtr null_ptr2;
-    EXPECT_TRUE(null_ptr1 == null_ptr2);
-
-    PseudoObservable::ObservedPtr obj1_ptr2(&obj1);
-    EXPECT_TRUE(obj1_ptr1 == obj1_ptr2);
-
-    PseudoObservable::ObservedPtr obj2_ptr2(&obj2);
-    EXPECT_TRUE(obj2_ptr1 == obj2_ptr2);
-  }
-  EXPECT_FALSE(null_ptr1 == obj1_ptr1);
-  EXPECT_FALSE(null_ptr1 == obj2_ptr1);
-  EXPECT_FALSE(obj1_ptr1 == obj2_ptr1);
-}
-
-TEST(ObservePtr, NotEquals) {
-  PseudoObservable obj1;
-  PseudoObservable obj2;
-  PseudoObservable::ObservedPtr null_ptr1;
-  PseudoObservable::ObservedPtr obj1_ptr1(&obj1);
-  PseudoObservable::ObservedPtr obj2_ptr1(&obj2);
-  {
-    PseudoObservable::ObservedPtr null_ptr2;
-    PseudoObservable::ObservedPtr obj1_ptr2(&obj1);
-    PseudoObservable::ObservedPtr obj2_ptr2(&obj2);
-    EXPECT_FALSE(null_ptr1 != null_ptr2);
-    EXPECT_FALSE(obj1_ptr1 != obj1_ptr2);
-    EXPECT_FALSE(obj2_ptr1 != obj2_ptr2);
-  }
-  EXPECT_TRUE(null_ptr1 != obj1_ptr1);
-  EXPECT_TRUE(null_ptr1 != obj2_ptr1);
-  EXPECT_TRUE(obj1_ptr1 != obj2_ptr1);
-}
-
-TEST(ObservePtr, Bool) {
-  PseudoObservable obj1;
-  PseudoObservable::ObservedPtr null_ptr;
-  PseudoObservable::ObservedPtr obj1_ptr(&obj1);
-  bool null_bool = !!null_ptr;
-  bool obj1_bool = !!obj1_ptr;
-  EXPECT_FALSE(null_bool);
-  EXPECT_TRUE(obj1_bool);
-}
-
-}  // namespace fxcrt
diff --git a/core/fxcrt/observed_ptr.cpp b/core/fxcrt/observed_ptr.cpp
new file mode 100644
index 0000000..f192e69
--- /dev/null
+++ b/core/fxcrt/observed_ptr.cpp
@@ -0,0 +1,15 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/observed_ptr.h"
+
+namespace fxcrt {
+
+Observable::Observable() = default;
+
+Observable::~Observable() {
+  NotifyObservers();
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/observed_ptr.h b/core/fxcrt/observed_ptr.h
new file mode 100644
index 0000000..9568705
--- /dev/null
+++ b/core/fxcrt/observed_ptr.h
@@ -0,0 +1,118 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_OBSERVED_PTR_H_
+#define CORE_FXCRT_OBSERVED_PTR_H_
+
+#include <set>
+
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/stl_util.h"
+
+namespace fxcrt {
+
+class Observable {
+ public:
+  // General-purpose interface for more complicated cleanup.
+  class ObserverIface {
+   public:
+    virtual ~ObserverIface() = default;
+    virtual void OnObservableDestroyed() = 0;
+  };
+
+  Observable();
+  Observable(const Observable& that) = delete;
+  ~Observable();
+  void AddObserver(ObserverIface* pObserver) {
+    ASSERT(!pdfium::ContainsKey(m_Observers, pObserver));
+    m_Observers.insert(pObserver);
+  }
+  void RemoveObserver(ObserverIface* pObserver) {
+    ASSERT(pdfium::ContainsKey(m_Observers, pObserver));
+    m_Observers.erase(pObserver);
+  }
+  void NotifyObservers() {
+    for (auto* pObserver : m_Observers)
+      pObserver->OnObservableDestroyed();
+    m_Observers.clear();
+  }
+  Observable& operator=(const Observable& that) = delete;
+
+ protected:
+  size_t ActiveObserversForTesting() const { return m_Observers.size(); }
+
+ private:
+  std::set<ObserverIface*> m_Observers;
+};
+
+// Simple case of a self-nulling pointer.
+template <typename T>
+class ObservedPtr final : public Observable::ObserverIface {
+ public:
+  ObservedPtr() = default;
+  explicit ObservedPtr(T* pObservable) : m_pObservable(pObservable) {
+    if (m_pObservable)
+      m_pObservable->AddObserver(this);
+  }
+  ObservedPtr(const ObservedPtr& that) : ObservedPtr(that.Get()) {}
+  ~ObservedPtr() override {
+    if (m_pObservable)
+      m_pObservable->RemoveObserver(this);
+  }
+  void Reset(T* pObservable = nullptr) {
+    if (m_pObservable)
+      m_pObservable->RemoveObserver(this);
+    m_pObservable = pObservable;
+    if (m_pObservable)
+      m_pObservable->AddObserver(this);
+  }
+  void OnObservableDestroyed() override {
+    ASSERT(m_pObservable);
+    m_pObservable = nullptr;
+  }
+  bool HasObservable() const { return !!m_pObservable; }
+  ObservedPtr& operator=(const ObservedPtr& that) {
+    Reset(that.Get());
+    return *this;
+  }
+  bool operator==(const ObservedPtr& that) const {
+    return m_pObservable == that.m_pObservable;
+  }
+  bool operator!=(const ObservedPtr& that) const { return !(*this == that); }
+
+  template <typename U>
+  bool operator==(const U* that) const {
+    return Get() == that;
+  }
+
+  template <typename U>
+  bool operator!=(const U* that) const {
+    return !(*this == that);
+  }
+
+  explicit operator bool() const { return HasObservable(); }
+  T* Get() const { return m_pObservable; }
+  T& operator*() const { return *m_pObservable; }
+  T* operator->() const { return m_pObservable; }
+
+ private:
+  T* m_pObservable = nullptr;
+};
+
+template <typename T, typename U>
+inline bool operator==(const U* lhs, const ObservedPtr<T>& rhs) {
+  return rhs == lhs;
+}
+
+template <typename T, typename U>
+inline bool operator!=(const U* lhs, const ObservedPtr<T>& rhs) {
+  return rhs != lhs;
+}
+
+}  // namespace fxcrt
+
+using fxcrt::Observable;
+using fxcrt::ObservedPtr;
+
+#endif  // CORE_FXCRT_OBSERVED_PTR_H_
diff --git a/core/fxcrt/observed_ptr_unittest.cpp b/core/fxcrt/observed_ptr_unittest.cpp
new file mode 100644
index 0000000..81e114f
--- /dev/null
+++ b/core/fxcrt/observed_ptr_unittest.cpp
@@ -0,0 +1,223 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/observed_ptr.h"
+
+#include <utility>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcrt {
+namespace {
+
+class PseudoObservable final : public Observable {
+ public:
+  int SomeMethod() { return 42; }
+  size_t ActiveObservedPtrs() const { return ActiveObserversForTesting(); }
+};
+
+class SelfObservable final : public Observable {
+ public:
+  ObservedPtr<SelfObservable> m_pOther;
+};
+
+}  // namespace
+
+TEST(ObservePtr, Null) {
+  ObservedPtr<PseudoObservable> ptr;
+  EXPECT_EQ(nullptr, ptr.Get());
+}
+
+TEST(ObservePtr, LivesLonger) {
+  ObservedPtr<PseudoObservable> ptr;
+  {
+    auto pObs = pdfium::MakeUnique<PseudoObservable>();
+    ptr.Reset(pObs.get());
+    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_EQ(1u, pObs->ActiveObservedPtrs());
+  }
+  EXPECT_EQ(nullptr, ptr.Get());
+}
+
+TEST(ObservePtr, LivesShorter) {
+  PseudoObservable obs;
+  {
+    ObservedPtr<PseudoObservable> ptr(&obs);
+    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
+  }
+  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
+}
+
+TEST(ObservePtr, CopyConstruct) {
+  PseudoObservable obs;
+  {
+    ObservedPtr<PseudoObservable> ptr(&obs);
+    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
+    {
+      ObservedPtr<PseudoObservable> ptr2(ptr);
+      EXPECT_NE(nullptr, ptr2.Get());
+      EXPECT_EQ(2u, obs.ActiveObservedPtrs());
+    }
+    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
+  }
+  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
+}
+
+TEST(ObservePtr, CopyAssign) {
+  PseudoObservable obs;
+  {
+    ObservedPtr<PseudoObservable> ptr(&obs);
+    EXPECT_NE(nullptr, ptr.Get());
+    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
+    {
+      ObservedPtr<PseudoObservable> ptr2;
+      ptr2 = ptr;
+      EXPECT_NE(nullptr, ptr2.Get());
+      EXPECT_EQ(2u, obs.ActiveObservedPtrs());
+    }
+    EXPECT_EQ(1u, obs.ActiveObservedPtrs());
+  }
+  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
+}
+
+TEST(ObservePtr, Vector) {
+  PseudoObservable obs;
+  {
+    std::vector<ObservedPtr<PseudoObservable>> vec1;
+    std::vector<ObservedPtr<PseudoObservable>> vec2;
+    vec1.emplace_back(&obs);
+    vec1.emplace_back(&obs);
+    EXPECT_NE(nullptr, vec1[0].Get());
+    EXPECT_NE(nullptr, vec1[1].Get());
+    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
+    vec2 = vec1;
+    EXPECT_NE(nullptr, vec2[0].Get());
+    EXPECT_NE(nullptr, vec2[1].Get());
+    EXPECT_EQ(4u, obs.ActiveObservedPtrs());
+    vec1.clear();
+    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
+    vec2.resize(10000);
+    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
+    vec2.resize(0);
+    EXPECT_EQ(0u, obs.ActiveObservedPtrs());
+  }
+  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
+}
+
+TEST(ObservePtr, VectorAutoClear) {
+  std::vector<ObservedPtr<PseudoObservable>> vec1;
+  {
+    PseudoObservable obs;
+    vec1.emplace_back(&obs);
+    vec1.emplace_back(&obs);
+    EXPECT_NE(nullptr, vec1[0].Get());
+    EXPECT_NE(nullptr, vec1[1].Get());
+    EXPECT_EQ(2u, obs.ActiveObservedPtrs());
+  }
+  EXPECT_EQ(nullptr, vec1[0].Get());
+  EXPECT_EQ(nullptr, vec1[1].Get());
+}
+
+TEST(ObservePtr, ResetNull) {
+  PseudoObservable obs;
+  ObservedPtr<PseudoObservable> ptr(&obs);
+  EXPECT_EQ(1u, obs.ActiveObservedPtrs());
+  ptr.Reset();
+  EXPECT_EQ(0u, obs.ActiveObservedPtrs());
+}
+
+TEST(ObservePtr, Reset) {
+  PseudoObservable obs1;
+  PseudoObservable obs2;
+  ObservedPtr<PseudoObservable> ptr(&obs1);
+  EXPECT_EQ(1u, obs1.ActiveObservedPtrs());
+  EXPECT_EQ(0u, obs2.ActiveObservedPtrs());
+  ptr.Reset(&obs2);
+  EXPECT_EQ(0u, obs1.ActiveObservedPtrs());
+  EXPECT_EQ(1u, obs2.ActiveObservedPtrs());
+}
+
+TEST(ObservePtr, Equals) {
+  PseudoObservable obj1;
+  PseudoObservable obj2;
+  ObservedPtr<PseudoObservable> null_ptr1;
+  ObservedPtr<PseudoObservable> obj1_ptr1(&obj1);
+  ObservedPtr<PseudoObservable> obj2_ptr1(&obj2);
+  EXPECT_TRUE(&obj1 == obj1_ptr1);
+  EXPECT_TRUE(obj1_ptr1 == &obj1);
+  EXPECT_TRUE(&obj2 == obj2_ptr1);
+  EXPECT_TRUE(obj2_ptr1 == &obj2);
+  {
+    ObservedPtr<PseudoObservable> null_ptr2;
+    EXPECT_TRUE(null_ptr1 == null_ptr2);
+
+    ObservedPtr<PseudoObservable> obj1_ptr2(&obj1);
+    EXPECT_TRUE(obj1_ptr1 == obj1_ptr2);
+
+    ObservedPtr<PseudoObservable> obj2_ptr2(&obj2);
+    EXPECT_TRUE(obj2_ptr1 == obj2_ptr2);
+  }
+  EXPECT_FALSE(null_ptr1 == obj1_ptr1);
+  EXPECT_FALSE(null_ptr1 == obj2_ptr1);
+  EXPECT_FALSE(obj1_ptr1 == obj2_ptr1);
+}
+
+TEST(ObservePtr, NotEquals) {
+  PseudoObservable obj1;
+  PseudoObservable obj2;
+  ObservedPtr<PseudoObservable> null_ptr1;
+  ObservedPtr<PseudoObservable> obj1_ptr1(&obj1);
+  ObservedPtr<PseudoObservable> obj2_ptr1(&obj2);
+  EXPECT_FALSE(&obj1 != obj1_ptr1);
+  EXPECT_FALSE(obj1_ptr1 != &obj1);
+  EXPECT_FALSE(&obj2 != obj2_ptr1);
+  EXPECT_FALSE(obj2_ptr1 != &obj2);
+  {
+    ObservedPtr<PseudoObservable> null_ptr2;
+    ObservedPtr<PseudoObservable> obj1_ptr2(&obj1);
+    ObservedPtr<PseudoObservable> obj2_ptr2(&obj2);
+    EXPECT_FALSE(null_ptr1 != null_ptr2);
+    EXPECT_FALSE(obj1_ptr1 != obj1_ptr2);
+    EXPECT_FALSE(obj2_ptr1 != obj2_ptr2);
+  }
+  EXPECT_TRUE(null_ptr1 != obj1_ptr1);
+  EXPECT_TRUE(null_ptr1 != obj2_ptr1);
+  EXPECT_TRUE(obj1_ptr1 != obj2_ptr1);
+}
+
+TEST(ObservePtr, Bool) {
+  PseudoObservable obj1;
+  ObservedPtr<PseudoObservable> null_ptr;
+  ObservedPtr<PseudoObservable> obj1_ptr(&obj1);
+  bool null_bool = !!null_ptr;
+  bool obj1_bool = !!obj1_ptr;
+  EXPECT_FALSE(null_bool);
+  EXPECT_TRUE(obj1_bool);
+}
+
+TEST(ObservePtr, SelfObservable) {
+  SelfObservable thing;
+  thing.m_pOther.Reset(&thing);
+  EXPECT_EQ(&thing, thing.m_pOther.Get());
+  // Must be no ASAN violations upon cleanup here.
+}
+
+TEST(ObservePtr, PairwiseObservable) {
+  SelfObservable thing1;
+  {
+    SelfObservable thing2;
+    thing1.m_pOther.Reset(&thing2);
+    thing2.m_pOther.Reset(&thing1);
+    EXPECT_EQ(&thing2, thing1.m_pOther.Get());
+    EXPECT_EQ(&thing1, thing2.m_pOther.Get());
+  }
+  EXPECT_EQ(nullptr, thing1.m_pOther.Get());
+  // Must be no ASAN violations upon cleanup here.
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/pauseindicator_iface.h b/core/fxcrt/pauseindicator_iface.h
new file mode 100644
index 0000000..d5d856d
--- /dev/null
+++ b/core/fxcrt/pauseindicator_iface.h
@@ -0,0 +1,16 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_PAUSEINDICATOR_IFACE_H_
+#define CORE_FXCRT_PAUSEINDICATOR_IFACE_H_
+
+class PauseIndicatorIface {
+ public:
+  virtual ~PauseIndicatorIface() = default;
+  virtual bool NeedToPauseNow() = 0;
+};
+
+#endif  // CORE_FXCRT_PAUSEINDICATOR_IFACE_H_
diff --git a/core/fxcrt/pdfium_span_unittest.cpp b/core/fxcrt/pdfium_span_unittest.cpp
new file mode 100644
index 0000000..6778bbb
--- /dev/null
+++ b/core/fxcrt/pdfium_span_unittest.cpp
@@ -0,0 +1,29 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/span.h"
+
+// Tests PDFium-modifications to base::span. The name of this file is
+// chosen to avoid collisions with base's span_unittest.cc
+
+TEST(PdfiumSpan, EmptySpan) {
+  int stuff[] = {1, 2, 3};
+  pdfium::span<int> stuff_span(stuff);
+  pdfium::span<int> empty_first_span = stuff_span.first(0);
+  pdfium::span<int> empty_last_span = stuff_span.last(0);
+  pdfium::span<int> empty_sub_span1 = stuff_span.subspan(0, 0);
+  pdfium::span<int> empty_sub_span2 = stuff_span.subspan(3, 0);
+  EXPECT_TRUE(empty_first_span.empty());
+  EXPECT_TRUE(empty_last_span.empty());
+  EXPECT_TRUE(empty_sub_span1.empty());
+  EXPECT_TRUE(empty_sub_span2.empty());
+}
+
+TEST(PdfiumSpan, EmptySpanDeath) {
+  int stuff[] = {1, 2, 3};
+  pdfium::span<int> stuff_span(stuff);
+  pdfium::span<int> empty_span = stuff_span.last(0);
+  EXPECT_DEATH(empty_span[0] += 1, ".*");
+}
diff --git a/core/fxcrt/retain_ptr.h b/core/fxcrt/retain_ptr.h
index e14b1ef..916a12e 100644
--- a/core/fxcrt/retain_ptr.h
+++ b/core/fxcrt/retain_ptr.h
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 namespace fxcrt {
 
@@ -30,6 +31,8 @@
 
   RetainPtr() = default;
   RetainPtr(const RetainPtr& that) : RetainPtr(that.Get()) {}
+
+  // Move-construct a RetainPtr. After construction, |that| will be NULL.
   RetainPtr(RetainPtr&& that) noexcept { Swap(that); }
 
   // Deliberately implicit to allow returning nullptrs.
@@ -51,6 +54,7 @@
   }
 
   T* Get() const { return m_pObj.get(); }
+  UnownedPtr<T> BackPointer() const { return UnownedPtr<T>(Get()); }
   void Swap(RetainPtr& that) { m_pObj.swap(that.m_pObj); }
 
   // Useful for passing notion of object ownership across a C API.
@@ -63,20 +67,34 @@
     return *this;
   }
 
+  // Move-assign a RetainPtr. After assignment, |that| will be NULL.
   RetainPtr& operator=(RetainPtr&& that) {
     m_pObj.reset(that.Leak());
     return *this;
   }
 
+  // Assigment from raw pointers is intentially not provided to make
+  // reference count churn more visible where possible.
+
   bool operator==(const RetainPtr& that) const { return Get() == that.Get(); }
   bool operator!=(const RetainPtr& that) const { return !(*this == that); }
 
+  template <typename U>
+  bool operator==(const U& that) const {
+    return Get() == that;
+  }
+
+  template <typename U>
+  bool operator!=(const U& that) const {
+    return !(*this == that);
+  }
+
   bool operator<(const RetainPtr& that) const {
     return std::less<T*>()(Get(), that.Get());
   }
 
   explicit operator bool() const { return !!m_pObj; }
-  T& operator*() const { return *m_pObj.get(); }
+  T& operator*() const { return *m_pObj; }
   T* operator->() const { return m_pObj.get(); }
 
  private:
@@ -86,6 +104,8 @@
 // Trivial implementation - internal ref count with virtual destructor.
 class Retainable {
  public:
+  Retainable() = default;
+
   bool HasOneRef() const { return m_nRefCount == 1; }
 
  protected:
@@ -98,21 +118,34 @@
   template <typename U>
   friend class RetainPtr;
 
-  void Retain() { ++m_nRefCount; }
-  void Release() {
+  Retainable(const Retainable& that) = delete;
+  Retainable& operator=(const Retainable& that) = delete;
+
+  void Retain() const { ++m_nRefCount; }
+  void Release() const {
     ASSERT(m_nRefCount > 0);
     if (--m_nRefCount == 0)
       delete this;
   }
 
-  intptr_t m_nRefCount = 0;
+  mutable intptr_t m_nRefCount = 0;
 };
 
+template <typename T, typename U>
+inline bool operator==(const U* lhs, const RetainPtr<T>& rhs) {
+  return rhs == lhs;
+}
+
+template <typename T, typename U>
+inline bool operator!=(const U* lhs, const RetainPtr<T>& rhs) {
+  return rhs != lhs;
+}
+
 }  // namespace fxcrt
 
 using fxcrt::ReleaseDeleter;
-using fxcrt::RetainPtr;
 using fxcrt::Retainable;
+using fxcrt::RetainPtr;
 
 namespace pdfium {
 
@@ -125,6 +158,12 @@
   return RetainPtr<T>(new T(std::forward<Args>(args)...));
 }
 
+// Type-deducing wrapper to make a RetainPtr from an ordinary pointer.
+template <typename T>
+RetainPtr<T> WrapRetain(T* that) {
+  return RetainPtr<T>(that);
+}
+
 }  // namespace pdfium
 
 #endif  // CORE_FXCRT_RETAIN_PTR_H_
diff --git a/core/fxcrt/retain_ptr_unittest.cpp b/core/fxcrt/retain_ptr_unittest.cpp
index f4b2994..47277df 100644
--- a/core/fxcrt/retain_ptr_unittest.cpp
+++ b/core/fxcrt/retain_ptr_unittest.cpp
@@ -8,24 +8,9 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/pseudo_retainable.h"
 
 namespace fxcrt {
-namespace {
-
-class PseudoRetainable {
- public:
-  PseudoRetainable() : retain_count_(0), release_count_(0) {}
-  void Retain() { ++retain_count_; }
-  void Release() { ++release_count_; }
-  int retain_count() const { return retain_count_; }
-  int release_count() const { return release_count_; }
-
- private:
-  int retain_count_;
-  int release_count_;
-};
-
-}  // namespace
 
 TEST(RetainPtr, Null) {
   RetainPtr<PseudoRetainable> ptr;
@@ -218,9 +203,11 @@
   {
     RetainPtr<PseudoRetainable> null_ptr2;
     EXPECT_TRUE(null_ptr1 == null_ptr2);
+    EXPECT_TRUE(null_ptr1 == nullptr);
 
     RetainPtr<PseudoRetainable> obj1_ptr2(&obj1);
     EXPECT_TRUE(obj1_ptr1 == obj1_ptr2);
+    EXPECT_TRUE(obj1_ptr2 == &obj1);
 
     RetainPtr<PseudoRetainable> obj2_ptr2(&obj2);
     EXPECT_TRUE(obj2_ptr1 == obj2_ptr2);
@@ -230,6 +217,17 @@
   EXPECT_FALSE(obj1_ptr1 == obj2_ptr1);
 }
 
+TEST(RetainPtr, EqualsReflexive) {
+  PseudoRetainable obj1;
+  PseudoRetainable obj2;
+  RetainPtr<PseudoRetainable> obj1_ptr(&obj1);
+  RetainPtr<PseudoRetainable> obj2_ptr(&obj2);
+  EXPECT_TRUE(&obj1 == obj1_ptr);
+  EXPECT_FALSE(&obj1 == obj2_ptr);
+  EXPECT_FALSE(&obj2 == obj1_ptr);
+  EXPECT_TRUE(&obj2 == obj2_ptr);
+}
+
 TEST(RetainPtr, NotEquals) {
   PseudoRetainable obj1;
   PseudoRetainable obj2;
@@ -249,6 +247,17 @@
   EXPECT_TRUE(obj1_ptr1 != obj2_ptr1);
 }
 
+TEST(RetainPtr, NotEqualsReflexive) {
+  PseudoRetainable obj1;
+  PseudoRetainable obj2;
+  RetainPtr<PseudoRetainable> obj1_ptr(&obj1);
+  RetainPtr<PseudoRetainable> obj2_ptr(&obj2);
+  EXPECT_FALSE(&obj1 != obj1_ptr);
+  EXPECT_TRUE(&obj1 != obj2_ptr);
+  EXPECT_TRUE(&obj2 != obj1_ptr);
+  EXPECT_FALSE(&obj2 != obj2_ptr);
+}
+
 TEST(RetainPtr, LessThan) {
   PseudoRetainable objs[2];
   RetainPtr<PseudoRetainable> obj1_ptr(&objs[0]);
diff --git a/core/fxcrt/retained_tree_node.h b/core/fxcrt/retained_tree_node.h
new file mode 100644
index 0000000..a859ae5
--- /dev/null
+++ b/core/fxcrt/retained_tree_node.h
@@ -0,0 +1,81 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_RETAINED_TREE_NODE_H_
+#define CORE_FXCRT_RETAINED_TREE_NODE_H_
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/tree_node.h"
+#include "third_party/base/logging.h"
+
+namespace fxcrt {
+
+// For DOM/XML-ish trees, where references outside the tree are RetainPtr<T>,
+// and the parent node also "retains" its children but doesn't always have
+// a direct pointer to them.
+template <typename T>
+class RetainedTreeNode : public TreeNode<T> {
+ public:
+  template <typename U, typename... Args>
+  friend RetainPtr<U> pdfium::MakeRetain(Args&&... args);
+
+  void AppendFirstChild(const RetainPtr<T>& child) {
+    TreeNode<T>::AppendFirstChild(child.Get());
+  }
+
+  void AppendLastChild(const RetainPtr<T>& child) {
+    TreeNode<T>::AppendLastChild(child.Get());
+  }
+
+  void InsertBefore(const RetainPtr<T>& child, T* other) {
+    TreeNode<T>::InsertBefore(child.Get(), other);
+  }
+
+  void InsertAfter(const RetainPtr<T>& child, T* other) {
+    TreeNode<T>::InsertAfter(child.Get(), other);
+  }
+
+  void RemoveChild(const RetainPtr<T>& child) {
+    TreeNode<T>::RemoveChild(child.Get());
+  }
+
+  void RemoveSelfIfParented() {
+    if (T* parent = TreeNode<T>::GetParent()) {
+      parent->TreeNode<T>::RemoveChild(
+          pdfium::WrapRetain(static_cast<T*>(this)).Get());
+    }
+  }
+
+ protected:
+  RetainedTreeNode() = default;
+  ~RetainedTreeNode() override {
+    while (auto* pChild = TreeNode<T>::GetFirstChild())
+      RemoveChild(pdfium::WrapRetain(pChild));
+  }
+
+ private:
+  template <typename U>
+  friend struct ReleaseDeleter;
+
+  template <typename U>
+  friend class RetainPtr;
+
+  RetainedTreeNode(const RetainedTreeNode& that) = delete;
+  RetainedTreeNode& operator=(const RetainedTreeNode& that) = delete;
+
+  void Retain() { ++m_nRefCount; }
+  void Release() {
+    ASSERT(m_nRefCount > 0);
+    if (--m_nRefCount == 0 && !TreeNode<T>::GetParent())
+      delete this;
+  }
+
+  intptr_t m_nRefCount = 0;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::RetainedTreeNode;
+
+#endif  // CORE_FXCRT_RETAINED_TREE_NODE_H_
diff --git a/core/fxcrt/retained_tree_node_unittest.cpp b/core/fxcrt/retained_tree_node_unittest.cpp
new file mode 100644
index 0000000..d469ab3
--- /dev/null
+++ b/core/fxcrt/retained_tree_node_unittest.cpp
@@ -0,0 +1,188 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/retained_tree_node.h"
+
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fxcrt {
+namespace {
+
+class ObservableRetainedTreeNodeForTest
+    : public RetainedTreeNode<ObservableRetainedTreeNodeForTest>,
+      public Observable {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+ private:
+  ObservableRetainedTreeNodeForTest() = default;
+};
+
+void AddClutterToFront(
+    const RetainPtr<ObservableRetainedTreeNodeForTest>& parent) {
+  for (int i = 0; i < 4; ++i) {
+    parent->AppendFirstChild(
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>());
+  }
+}
+
+void AddClutterToBack(
+    const RetainPtr<ObservableRetainedTreeNodeForTest>& parent) {
+  for (int i = 0; i < 4; ++i) {
+    parent->AppendLastChild(
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>());
+  }
+}
+
+}  // namespace
+
+TEST(RetainedTreeNode, NoParent) {
+  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
+  {
+    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    EXPECT_FALSE(ptr->HasChild(ptr.Get()));
+    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
+    EXPECT_TRUE(watcher.Get());
+  }
+  EXPECT_FALSE(watcher.Get());
+}
+
+TEST(RetainedTreeNode, FirstHasParent) {
+  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
+  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
+      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+  {
+    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
+    parent->AppendFirstChild(ptr);
+    EXPECT_FALSE(parent->HasChild(parent.Get()));
+    EXPECT_TRUE(parent->HasChild(ptr.Get()));
+    EXPECT_TRUE(watcher.Get());
+  }
+  EXPECT_TRUE(watcher.Get());
+  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
+  EXPECT_FALSE(watcher.Get());
+  // Now add some clutter.
+  {
+    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
+    parent->AppendFirstChild(ptr);
+    AddClutterToFront(parent);
+    AddClutterToBack(parent);
+    EXPECT_TRUE(watcher.Get());
+  }
+  EXPECT_TRUE(watcher.Get());
+  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
+  EXPECT_FALSE(watcher.Get());
+}
+
+TEST(RetainedTreeNode, LastHasParent) {
+  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
+  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
+      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+  {
+    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
+    parent->AppendLastChild(ptr);
+    EXPECT_FALSE(parent->HasChild(parent.Get()));
+    EXPECT_TRUE(parent->HasChild(ptr.Get()));
+    EXPECT_TRUE(watcher.Get());
+  }
+  {
+    // Now add some clutter.
+    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
+    parent->AppendLastChild(ptr);
+    AddClutterToFront(parent);
+    AddClutterToBack(parent);
+    EXPECT_TRUE(watcher.Get());
+  }
+  EXPECT_TRUE(watcher.Get());
+  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
+  EXPECT_FALSE(watcher.Get());
+}
+
+TEST(RetainedTreeNode, GrandChildCleanedUp) {
+  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
+  RetainPtr<ObservableRetainedTreeNodeForTest> grandparent =
+      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+  {
+    RetainPtr<ObservableRetainedTreeNodeForTest> parent =
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    grandparent->AppendFirstChild(parent);
+    {
+      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
+          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
+      parent->AppendFirstChild(ptr);
+      EXPECT_TRUE(watcher.Get());
+    }
+    grandparent->RemoveChild(parent);
+    EXPECT_TRUE(watcher.Get());
+  }
+  EXPECT_FALSE(watcher.Get());
+}
+
+TEST(RetainedTreeNode, RemoveSelf) {
+  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
+  auto parent = pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+  {
+    auto child = pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(child.Get());
+    parent->AppendFirstChild(child);
+  }
+  EXPECT_TRUE(watcher.Get());
+  watcher->RemoveSelfIfParented();
+  EXPECT_FALSE(watcher.Get());
+}
+
+TEST(RetainedTreeNode, InsertBeforeAfter) {
+  ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
+  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
+      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+
+  AddClutterToFront(parent);
+  {
+    RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
+        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+    watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
+    parent->AppendFirstChild(ptr);
+    parent->InsertBefore(pdfium::WrapRetain(parent->GetFirstChild()),
+                         parent->GetLastChild());
+    parent->InsertAfter(pdfium::WrapRetain(parent->GetLastChild()),
+                        parent->GetFirstChild());
+    EXPECT_TRUE(watcher.Get());
+  }
+  parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
+  EXPECT_FALSE(watcher.Get());
+}
+
+TEST(RetainedTreeNode, Traversal) {
+  RetainPtr<ObservableRetainedTreeNodeForTest> parent =
+      pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
+
+  AddClutterToFront(parent);
+  int count = 0;
+  for (ObservableRetainedTreeNodeForTest* pNode = parent->GetFirstChild();
+       pNode; pNode = pNode->GetNextSibling()) {
+    ++count;
+  }
+  EXPECT_EQ(4, count);
+  count = 0;
+  for (ObservableRetainedTreeNodeForTest* pNode = parent->GetLastChild(); pNode;
+       pNode = pNode->GetPrevSibling()) {
+    ++count;
+  }
+  EXPECT_EQ(4, count);
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/shared_copy_on_write.h b/core/fxcrt/shared_copy_on_write.h
index f7d7a2a..a672ddf 100644
--- a/core/fxcrt/shared_copy_on_write.h
+++ b/core/fxcrt/shared_copy_on_write.h
@@ -12,8 +12,11 @@
 
 namespace fxcrt {
 
-// A shared object with Copy on Write semantics that makes it appear as
-// if each one were independent.
+// A shared pointer to an object with Copy on Write semantics that makes it
+// appear as if all instances were independent. |ObjClass| must implement the
+// requirements of |Retainable| from retain_ptr.h, and must also provide a
+// Clone() method. Often this will just call MakeRetain<>(*this) but will need
+// to be virtual if |ObjClass| is subclassed.
 template <class ObjClass>
 class SharedCopyOnWrite {
  public:
@@ -24,7 +27,7 @@
 
   template <typename... Args>
   ObjClass* Emplace(Args... params) {
-    m_pObject.Reset(new ObjClass(params...));
+    m_pObject = pdfium::MakeRetain<ObjClass>(params...);
     return m_pObject.Get();
   }
 
@@ -42,7 +45,7 @@
     if (!m_pObject)
       return Emplace(params...);
     if (!m_pObject->HasOneRef())
-      m_pObject.Reset(new ObjClass(*m_pObject));
+      m_pObject = m_pObject->Clone();
     return m_pObject.Get();
   }
 
diff --git a/core/fxcrt/shared_copy_on_write_unittest.cpp b/core/fxcrt/shared_copy_on_write_unittest.cpp
index 57e33d1..f579358 100644
--- a/core/fxcrt/shared_copy_on_write_unittest.cpp
+++ b/core/fxcrt/shared_copy_on_write_unittest.cpp
@@ -29,7 +29,7 @@
   std::map<std::string, int> destruction_counts_;
 };
 
-class Object : public Retainable {
+class Object final : public Retainable {
  public:
   Object(Observer* observer, const std::string& name)
       : name_(name), observer_(observer) {
@@ -40,6 +40,8 @@
   }
   ~Object() override { observer_->OnDestruct(name_); }
 
+  RetainPtr<Object> Clone() const { return pdfium::MakeRetain<Object>(*this); }
+
  private:
   std::string name_;
   Observer* observer_;
diff --git a/core/fxcrt/string_data_template.h b/core/fxcrt/string_data_template.h
index bc797c0..cda41e0 100644
--- a/core/fxcrt/string_data_template.h
+++ b/core/fxcrt/string_data_template.h
@@ -36,17 +36,11 @@
     size_t usableLen = (totalSize - overhead) / sizeof(CharType);
     ASSERT(usableLen >= nLen);
 
-    void* pData = pdfium::base::PartitionAllocGeneric(
-        gStringPartitionAllocator.root(), totalSize, "StringDataTemplate");
+    void* pData = GetStringPartitionAllocator().root()->Alloc(
+        totalSize, "StringDataTemplate");
     return new (pData) StringDataTemplate(nLen, usableLen);
   }
 
-  static StringDataTemplate* Create(const StringDataTemplate& other) {
-    StringDataTemplate* result = Create(other.m_nDataLength);
-    result->CopyContents(other);
-    return result;
-  }
-
   static StringDataTemplate* Create(const CharType* pStr, size_t nLen) {
     StringDataTemplate* result = Create(nLen);
     result->CopyContents(pStr, nLen);
@@ -56,8 +50,7 @@
   void Retain() { ++m_nRefs; }
   void Release() {
     if (--m_nRefs <= 0)
-      pdfium::base::PartitionFreeGeneric(gStringPartitionAllocator.root(),
-                                         this);
+      GetStringPartitionAllocator().root()->Free(this);
   }
 
   bool CanOperateInPlace(size_t nTotalLen) const {
@@ -71,13 +64,18 @@
   }
 
   void CopyContents(const CharType* pStr, size_t nLen) {
-    ASSERT(nLen >= 0 && nLen <= m_nAllocLength);
+    ASSERT(nLen >= 0);
+    ASSERT(nLen <= m_nAllocLength);
+
     memcpy(m_String, pStr, nLen * sizeof(CharType));
     m_String[nLen] = 0;
   }
 
   void CopyContentsAt(size_t offset, const CharType* pStr, size_t nLen) {
-    ASSERT(offset >= 0 && nLen >= 0 && offset + nLen <= m_nAllocLength);
+    ASSERT(offset >= 0);
+    ASSERT(nLen >= 0);
+    ASSERT(offset + nLen <= m_nAllocLength);
+
     memcpy(m_String + offset, pStr, nLen * sizeof(CharType));
     m_String[offset + nLen] = 0;
   }
diff --git a/core/fxcrt/string_view_template.h b/core/fxcrt/string_view_template.h
index 1e436d3..e50a71d 100644
--- a/core/fxcrt/string_view_template.h
+++ b/core/fxcrt/string_view_template.h
@@ -10,19 +10,21 @@
 #include <algorithm>
 #include <iterator>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
 #include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
 #include "third_party/base/optional.h"
+#include "third_party/base/span.h"
 #include "third_party/base/stl_util.h"
 
 namespace fxcrt {
 
 // An immutable string with caller-provided storage which must outlive the
 // string itself. These are not necessarily nul-terminated, so that substring
-// extraction (via the Mid(), Left(), and Right() methods) is copy-free.
+// extraction (via the Substr(), First(), and Last() methods) is copy-free.
+//
+// String view arguments should be passed by value, since they are small,
+// rather than const-ref, even if they are not modified.
 template <typename T>
 class StringViewTemplate {
  public:
@@ -31,63 +33,68 @@
   using const_iterator = const CharType*;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
-  StringViewTemplate() : m_Ptr(nullptr), m_Length(0) {}
+  constexpr StringViewTemplate() noexcept = default;
+  constexpr StringViewTemplate(const StringViewTemplate& src) noexcept =
+      default;
 
   // Deliberately implicit to avoid calling on every string literal.
   // NOLINTNEXTLINE(runtime/explicit)
-  StringViewTemplate(const CharType* ptr)
-      : m_Ptr(reinterpret_cast<const UnsignedType*>(ptr)),
-        m_Length(ptr ? FXSYS_len(ptr) : 0) {}
+  StringViewTemplate(const CharType* ptr) noexcept
+      : m_Span(reinterpret_cast<const UnsignedType*>(ptr),
+               ptr ? FXSYS_len(ptr) : 0) {}
 
-  StringViewTemplate(const CharType* ptr, size_t len)
-      : m_Ptr(reinterpret_cast<const UnsignedType*>(ptr)), m_Length(len) {}
+  constexpr StringViewTemplate(const CharType* ptr, size_t len) noexcept
+      : m_Span(reinterpret_cast<const UnsignedType*>(ptr), len) {}
+
+  explicit constexpr StringViewTemplate(
+      const pdfium::span<const CharType>& other) noexcept
+      : m_Span(reinterpret_cast<const UnsignedType*>(other.data()),
+               other.size()) {}
+
+  template <typename U = UnsignedType>
+  constexpr StringViewTemplate(
+      const UnsignedType* ptr,
+      size_t size,
+      typename std::enable_if<!std::is_same<U, CharType>::value>::type* =
+          0) noexcept
+      : m_Span(ptr, size) {}
 
   template <typename U = UnsignedType>
   StringViewTemplate(
-      const UnsignedType* ptr,
-      size_t size,
-      typename std::enable_if<!std::is_same<U, CharType>::value>::type* = 0)
-      : m_Ptr(ptr), m_Length(size) {}
+      const pdfium::span<U> other,
+      typename std::enable_if<!std::is_same<U, CharType>::value>::type* =
+          0) noexcept
+      : m_Span(other) {}
 
   // Deliberately implicit to avoid calling on every string literal.
   // |ch| must be an lvalue that outlives the StringViewTemplate.
   // NOLINTNEXTLINE(runtime/explicit)
-  StringViewTemplate(CharType& ch) {
-    m_Ptr = reinterpret_cast<const UnsignedType*>(&ch);
-    m_Length = 1;
-  }
-
-  StringViewTemplate(const StringViewTemplate& src) {
-    m_Ptr = src.m_Ptr;
-    m_Length = src.m_Length;
-  }
+  constexpr StringViewTemplate(CharType& ch) noexcept
+      : m_Span(reinterpret_cast<const UnsignedType*>(&ch), 1) {}
 
   // Any changes to |vec| invalidate the string.
-  explicit StringViewTemplate(const std::vector<UnsignedType>& vec) {
-    m_Length = vec.size();
-    m_Ptr = m_Length ? vec.data() : nullptr;
-  }
+  template <typename AllocType>
+  explicit StringViewTemplate(
+      const std::vector<UnsignedType, AllocType>& vec) noexcept
+      : m_Span(!vec.empty() ? vec.data() : nullptr, vec.size()) {}
 
   StringViewTemplate& operator=(const CharType* src) {
-    m_Ptr = reinterpret_cast<const UnsignedType*>(src);
-    m_Length = src ? FXSYS_len(src) : 0;
+    m_Span = pdfium::span<const UnsignedType>(
+        reinterpret_cast<const UnsignedType*>(src), src ? FXSYS_len(src) : 0);
     return *this;
   }
 
   StringViewTemplate& operator=(const StringViewTemplate& src) {
-    m_Ptr = src.m_Ptr;
-    m_Length = src.m_Length;
+    m_Span = src.m_Span;
     return *this;
   }
 
   const_iterator begin() const {
-    return reinterpret_cast<const CharType*>(m_Ptr.Get());
+    return reinterpret_cast<const_iterator>(m_Span.begin());
   }
   const_iterator end() const {
-    return m_Ptr ? reinterpret_cast<const CharType*>(m_Ptr.Get()) + m_Length
-                 : nullptr;
+    return reinterpret_cast<const_iterator>(m_Span.end());
   }
-
   const_reverse_iterator rbegin() const {
     return const_reverse_iterator(end());
   }
@@ -95,71 +102,103 @@
     return const_reverse_iterator(begin());
   }
 
-  bool operator==(const CharType* ptr) const {
-    return FXSYS_len(ptr) == m_Length &&
-           FXSYS_cmp(ptr, reinterpret_cast<const CharType*>(m_Ptr.Get()),
-                     m_Length) == 0;
-  }
   bool operator==(const StringViewTemplate& other) const {
-    return other.m_Length == m_Length &&
-           FXSYS_cmp(reinterpret_cast<const CharType*>(other.m_Ptr.Get()),
-                     reinterpret_cast<const CharType*>(m_Ptr.Get()),
-                     m_Length) == 0;
+    return m_Span == other.m_Span;
+  }
+  bool operator==(const CharType* ptr) const {
+    StringViewTemplate other(ptr);
+    return *this == other;
   }
   bool operator!=(const CharType* ptr) const { return !(*this == ptr); }
   bool operator!=(const StringViewTemplate& other) const {
     return !(*this == other);
   }
 
+  bool IsASCII() const {
+    for (auto c : *this) {
+      if (c <= 0 || c > 127)  // Questionable signedness of |c|.
+        return false;
+    }
+    return true;
+  }
+
+  bool EqualsASCII(const StringViewTemplate<char>& that) const {
+    size_t length = GetLength();
+    if (length != that.GetLength())
+      return false;
+
+    for (size_t i = 0; i < length; ++i) {
+      auto c = (*this)[i];
+      if (c <= 0 || c > 127 || c != that[i])  // Questionable signedness of |c|.
+        return false;
+    }
+    return true;
+  }
+
+  bool EqualsASCIINoCase(const StringViewTemplate<char>& that) const {
+    size_t length = GetLength();
+    if (length != that.GetLength())
+      return false;
+
+    for (size_t i = 0; i < length; ++i) {
+      auto c = (*this)[i];
+      if (c <= 0 || c > 127 || tolower(c) != tolower(that[i]))
+        return false;
+    }
+    return true;
+  }
+
   uint32_t GetID() const {
-    if (m_Length == 0)
+    if (m_Span.empty())
       return 0;
 
     uint32_t strid = 0;
-    size_t size = std::min(static_cast<size_t>(4), m_Length);
+    size_t size = std::min(static_cast<size_t>(4), m_Span.size());
     for (size_t i = 0; i < size; i++)
-      strid = strid * 256 + m_Ptr.Get()[i];
+      strid = strid * 256 + m_Span[i];
 
     return strid << ((4 - size) * 8);
   }
 
-  const UnsignedType* raw_str() const { return m_Ptr.Get(); }
+  pdfium::span<const UnsignedType> raw_span() const { return m_Span; }
+  pdfium::span<const CharType> span() const {
+    return pdfium::make_span(reinterpret_cast<const CharType*>(m_Span.data()),
+                             m_Span.size());
+  }
+  const UnsignedType* raw_str() const { return m_Span.data(); }
   const CharType* unterminated_c_str() const {
-    return reinterpret_cast<const CharType*>(m_Ptr.Get());
+    return reinterpret_cast<const CharType*>(m_Span.data());
   }
 
-  size_t GetLength() const { return m_Length; }
-  bool IsEmpty() const { return m_Length == 0; }
-  bool IsValidIndex(size_t index) const { return index < GetLength(); }
-  bool IsValidLength(size_t length) const { return length <= GetLength(); }
+  size_t GetLength() const { return m_Span.size(); }
+  bool IsEmpty() const { return m_Span.empty(); }
+  bool IsValidIndex(size_t index) const { return index < m_Span.size(); }
+  bool IsValidLength(size_t length) const { return length <= m_Span.size(); }
 
   const UnsignedType& operator[](const size_t index) const {
-    ASSERT(IsValidIndex(index));
-    return m_Ptr.Get()[index];
+    return m_Span[index];
   }
 
-  UnsignedType First() const { return GetLength() ? (*this)[0] : 0; }
-
-  UnsignedType Last() const {
-    return GetLength() ? (*this)[GetLength() - 1] : 0;
+  UnsignedType Front() const { return !m_Span.empty() ? m_Span[0] : 0; }
+  UnsignedType Back() const {
+    return !m_Span.empty() ? m_Span[m_Span.size() - 1] : 0;
   }
 
   const CharType CharAt(const size_t index) const {
-    ASSERT(IsValidIndex(index));
-    return static_cast<CharType>(m_Ptr.Get()[index]);
+    return static_cast<CharType>(m_Span[index]);
   }
 
   Optional<size_t> Find(CharType ch) const {
     const auto* found = reinterpret_cast<const UnsignedType*>(FXSYS_chr(
-        reinterpret_cast<const CharType*>(m_Ptr.Get()), ch, m_Length));
+        reinterpret_cast<const CharType*>(m_Span.data()), ch, m_Span.size()));
 
-    return found ? Optional<size_t>(found - m_Ptr.Get()) : Optional<size_t>();
+    return found ? Optional<size_t>(found - m_Span.data()) : Optional<size_t>();
   }
 
   bool Contains(CharType ch) const { return Find(ch).has_value(); }
 
-  StringViewTemplate Mid(size_t first, size_t count) const {
-    if (!m_Ptr.Get())
+  StringViewTemplate Substr(size_t first, size_t count) const {
+    if (!m_Span.data())
       return StringViewTemplate();
 
     if (!IsValidIndex(first))
@@ -171,19 +210,19 @@
     if (!IsValidIndex(first + count - 1))
       return StringViewTemplate();
 
-    return StringViewTemplate(m_Ptr.Get() + first, count);
+    return StringViewTemplate(m_Span.data() + first, count);
   }
 
-  StringViewTemplate Left(size_t count) const {
+  StringViewTemplate First(size_t count) const {
     if (count == 0 || !IsValidLength(count))
       return StringViewTemplate();
-    return Mid(0, count);
+    return Substr(0, count);
   }
 
-  StringViewTemplate Right(size_t count) const {
+  StringViewTemplate Last(size_t count) const {
     if (count == 0 || !IsValidLength(count))
       return StringViewTemplate();
-    return Mid(GetLength() - count, count);
+    return Substr(GetLength() - count, count);
   }
 
   StringViewTemplate TrimmedRight(CharType ch) const {
@@ -197,26 +236,27 @@
     if (pos == 0)
       return StringViewTemplate();
 
-    return StringViewTemplate(m_Ptr.Get(), pos);
+    return StringViewTemplate(m_Span.data(), pos);
   }
 
   bool operator<(const StringViewTemplate& that) const {
-    int result = FXSYS_cmp(reinterpret_cast<const CharType*>(m_Ptr.Get()),
-                           reinterpret_cast<const CharType*>(that.m_Ptr.Get()),
-                           std::min(m_Length, that.m_Length));
-    return result < 0 || (result == 0 && m_Length < that.m_Length);
+    int result =
+        FXSYS_cmp(reinterpret_cast<const CharType*>(m_Span.data()),
+                  reinterpret_cast<const CharType*>(that.m_Span.data()),
+                  std::min(m_Span.size(), that.m_Span.size()));
+    return result < 0 || (result == 0 && m_Span.size() < that.m_Span.size());
   }
 
   bool operator>(const StringViewTemplate& that) const {
-    int result = FXSYS_cmp(reinterpret_cast<const CharType*>(m_Ptr.Get()),
-                           reinterpret_cast<const CharType*>(that.m_Ptr.Get()),
-                           std::min(m_Length, that.m_Length));
-    return result > 0 || (result == 0 && m_Length > that.m_Length);
+    int result =
+        FXSYS_cmp(reinterpret_cast<const CharType*>(m_Span.data()),
+                  reinterpret_cast<const CharType*>(that.m_Span.data()),
+                  std::min(m_Span.size(), that.m_Span.size()));
+    return result > 0 || (result == 0 && m_Span.size() > that.m_Span.size());
   }
 
  protected:
-  UnownedPtr<const UnsignedType> m_Ptr;
-  size_t m_Length;
+  pdfium::span<const UnsignedType> m_Span;
 
  private:
   void* operator new(size_t) throw() { return nullptr; }
@@ -235,8 +275,12 @@
   return rhs > lhs;
 }
 
+// Workaround for one of the cases external template classes are
+// failing in GCC before version 7 with -O0
+#if !defined(__GNUC__) || __GNUC__ >= 7
 extern template class StringViewTemplate<char>;
 extern template class StringViewTemplate<wchar_t>;
+#endif
 
 using ByteStringView = StringViewTemplate<char>;
 using WideStringView = StringViewTemplate<wchar_t>;
diff --git a/core/fxcrt/timerhandler_iface.h b/core/fxcrt/timerhandler_iface.h
new file mode 100644
index 0000000..04e781d
--- /dev/null
+++ b/core/fxcrt/timerhandler_iface.h
@@ -0,0 +1,29 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_TIMERHANDLER_IFACE_H_
+#define CORE_FXCRT_TIMERHANDLER_IFACE_H_
+
+#include "core/fxcrt/fx_system.h"
+
+namespace fxcrt {
+
+class TimerHandlerIface {
+ public:
+  static constexpr int32_t kInvalidTimerID = 0;
+  using TimerCallback = void (*)(int32_t idEvent);
+
+  virtual ~TimerHandlerIface() = default;
+
+  virtual int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) = 0;
+  virtual void KillTimer(int32_t nTimerID) = 0;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::TimerHandlerIface;
+
+#endif  // CORE_FXCRT_TIMERHANDLER_IFACE_H_
diff --git a/core/fxcrt/tree_node.h b/core/fxcrt/tree_node.h
new file mode 100644
index 0000000..5b1d7df
--- /dev/null
+++ b/core/fxcrt/tree_node.h
@@ -0,0 +1,155 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_TREE_NODE_H_
+#define CORE_FXCRT_TREE_NODE_H_
+
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/logging.h"
+
+namespace fxcrt {
+
+// Implements the usual DOM/XML-ish trees.
+template <typename T>
+class TreeNode {
+ public:
+  TreeNode() = default;
+  virtual ~TreeNode() = default;
+
+  T* GetParent() const { return m_pParent; }
+  T* GetFirstChild() const { return m_pFirstChild; }
+  T* GetLastChild() const { return m_pLastChild; }
+  T* GetNextSibling() const { return m_pNextSibling; }
+  T* GetPrevSibling() const { return m_pPrevSibling; }
+
+  bool HasChild(const T* child) const {
+    return child != this && child->m_pParent == this;
+  }
+
+  T* GetNthChild(int32_t n) {
+    if (n < 0)
+      return nullptr;
+    T* result = GetFirstChild();
+    while (n-- && result) {
+      result = result->GetNextSibling();
+    }
+    return result;
+  }
+
+  void AppendFirstChild(T* child) {
+    BecomeParent(child);
+    if (m_pFirstChild) {
+      CHECK(m_pLastChild);
+      m_pFirstChild->m_pPrevSibling = child;
+      child->m_pNextSibling = m_pFirstChild;
+      m_pFirstChild = child;
+    } else {
+      CHECK(!m_pLastChild);
+      m_pFirstChild = child;
+      m_pLastChild = child;
+    }
+  }
+
+  void AppendLastChild(T* child) {
+    BecomeParent(child);
+    if (m_pLastChild) {
+      CHECK(m_pFirstChild);
+      m_pLastChild->m_pNextSibling = child;
+      child->m_pPrevSibling = m_pLastChild;
+      m_pLastChild = child;
+    } else {
+      CHECK(!m_pFirstChild);
+      m_pFirstChild = child;
+      m_pLastChild = child;
+    }
+  }
+
+  void InsertBefore(T* child, T* other) {
+    if (!other) {
+      AppendLastChild(child);
+      return;
+    }
+    BecomeParent(child);
+    CHECK(HasChild(other));
+    child->m_pNextSibling = other;
+    child->m_pPrevSibling = other->m_pPrevSibling;
+    if (m_pFirstChild == other) {
+      CHECK(!other->m_pPrevSibling);
+      m_pFirstChild = child;
+    } else {
+      other->m_pPrevSibling->m_pNextSibling = child;
+    }
+    other->m_pPrevSibling = child;
+  }
+
+  void InsertAfter(T* child, T* other) {
+    if (!other) {
+      AppendFirstChild(child);
+      return;
+    }
+    BecomeParent(child);
+    CHECK(HasChild(other));
+    child->m_pNextSibling = other->m_pNextSibling;
+    child->m_pPrevSibling = other;
+    if (m_pLastChild == other) {
+      CHECK(!other->m_pNextSibling);
+      m_pLastChild = child;
+    } else {
+      other->m_pNextSibling->m_pPrevSibling = child;
+    }
+    other->m_pNextSibling = child;
+  }
+
+  void RemoveChild(T* child) {
+    CHECK(HasChild(child));
+    if (m_pLastChild == child) {
+      CHECK(!child->m_pNextSibling);
+      m_pLastChild = child->m_pPrevSibling;
+    } else {
+      child->m_pNextSibling->m_pPrevSibling = child->m_pPrevSibling;
+    }
+    if (m_pFirstChild == child) {
+      CHECK(!child->m_pPrevSibling);
+      m_pFirstChild = child->m_pNextSibling;
+    } else {
+      child->m_pPrevSibling->m_pNextSibling = child->m_pNextSibling;
+    }
+    child->m_pParent = nullptr;
+    child->m_pPrevSibling = nullptr;
+    child->m_pNextSibling = nullptr;
+  }
+
+  void RemoveAllChildren() {
+    while (T* child = GetFirstChild())
+      RemoveChild(child);
+  }
+
+  void RemoveSelfIfParented() {
+    if (T* parent = GetParent())
+      parent->RemoveChild(static_cast<T*>(this));
+  }
+
+ private:
+  // Child left in state where sibling members need subsequent adjustment.
+  void BecomeParent(T* child) {
+    CHECK(child != this);  // Detect attempts at self-insertion.
+    if (child->m_pParent)
+      child->m_pParent->TreeNode<T>::RemoveChild(child);
+    child->m_pParent = static_cast<T*>(this);
+    CHECK(!child->m_pNextSibling);
+    CHECK(!child->m_pPrevSibling);
+  }
+
+  T* m_pParent = nullptr;       // Raw, intra-tree pointer.
+  T* m_pFirstChild = nullptr;   // Raw, intra-tree pointer.
+  T* m_pLastChild = nullptr;    // Raw, intra-tree pointer.
+  T* m_pNextSibling = nullptr;  // Raw, intra-tree pointer
+  T* m_pPrevSibling = nullptr;  // Raw, intra-tree pointer
+};
+
+}  // namespace fxcrt
+
+using fxcrt::TreeNode;
+
+#endif  // CORE_FXCRT_TREE_NODE_H_
diff --git a/core/fxcrt/tree_node_unittest.cpp b/core/fxcrt/tree_node_unittest.cpp
new file mode 100644
index 0000000..26dd610
--- /dev/null
+++ b/core/fxcrt/tree_node_unittest.cpp
@@ -0,0 +1,164 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/tree_node.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+namespace fxcrt {
+
+class TestTreeNode : public TreeNode<TestTreeNode> {};
+
+// NOTE: Successful cases are covered via RetainedTreeNode tests.
+// These tests check that we trip CHECKS given bad calls.
+
+TEST(TreeNode, SelfAppendFirstChild) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  EXPECT_DEATH(pNode->AppendFirstChild(pNode.get()), "");
+}
+
+TEST(TreeNode, SelfAppendLastChild) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  EXPECT_DEATH(pNode->AppendLastChild(pNode.get()), "");
+}
+
+TEST(TreeNode, SelfInsertBeforeOther) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  pNode->AppendFirstChild(pOther.get());
+  EXPECT_DEATH(pNode->InsertBefore(pNode.get(), pOther.get()), "");
+}
+
+TEST(TreeNode, InsertOtherBeforeSelf) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  pNode->AppendFirstChild(pOther.get());
+  EXPECT_DEATH(pNode->InsertBefore(pOther.get(), pNode.get()), "");
+}
+
+TEST(TreeNode, SelfInsertAfterOther) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  pNode->AppendFirstChild(pOther.get());
+  EXPECT_DEATH(pNode->InsertBefore(pNode.get(), pOther.get()), "");
+}
+
+TEST(TreeNode, InsertOtherAfterSelf) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  auto pOther = pdfium::MakeUnique<TestTreeNode>();
+  pNode->AppendFirstChild(pOther.get());
+  EXPECT_DEATH(pNode->InsertBefore(pOther.get(), pNode.get()), "");
+}
+
+TEST(TreeNode, RemoveParentless) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  EXPECT_DEATH(pNode->GetParent()->RemoveChild(pNode.get()), "");
+}
+
+TEST(TreeNode, RemoveFromWrongParent) {
+  auto pGoodParent = pdfium::MakeUnique<TestTreeNode>();
+  auto pBadParent = pdfium::MakeUnique<TestTreeNode>();
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  pGoodParent->AppendFirstChild(pNode.get());
+  EXPECT_DEATH(pBadParent->RemoveChild(pNode.get()), "");
+}
+
+TEST(TreeNode, SafeRemove) {
+  auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  auto pChild = pdfium::MakeUnique<TestTreeNode>();
+  pParent->AppendFirstChild(pChild.get());
+  pChild->RemoveSelfIfParented();
+  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+  EXPECT_EQ(nullptr, pChild->GetParent());
+}
+
+TEST(TreeNode, SafeRemoveParentless) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  pNode->RemoveSelfIfParented();
+  EXPECT_EQ(nullptr, pNode->GetParent());
+}
+
+TEST(TreeNode, RemoveAllChildren) {
+  auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  pParent->RemoveAllChildren();
+  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+
+  auto p0 = pdfium::MakeUnique<TestTreeNode>();
+  auto p1 = pdfium::MakeUnique<TestTreeNode>();
+  auto p2 = pdfium::MakeUnique<TestTreeNode>();
+  auto p3 = pdfium::MakeUnique<TestTreeNode>();
+  pParent->AppendLastChild(p0.get());
+  pParent->AppendLastChild(p1.get());
+  pParent->AppendLastChild(p2.get());
+  pParent->AppendLastChild(p3.get());
+  pParent->RemoveAllChildren();
+  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+}
+
+TEST(TreeNode, NthChild) {
+  auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  EXPECT_EQ(nullptr, pParent->GetNthChild(-1));
+  EXPECT_EQ(nullptr, pParent->GetNthChild(0));
+
+  auto p0 = pdfium::MakeUnique<TestTreeNode>();
+  auto p1 = pdfium::MakeUnique<TestTreeNode>();
+  auto p2 = pdfium::MakeUnique<TestTreeNode>();
+  auto p3 = pdfium::MakeUnique<TestTreeNode>();
+  pParent->AppendLastChild(p0.get());
+  pParent->AppendLastChild(p1.get());
+  pParent->AppendLastChild(p2.get());
+  pParent->AppendLastChild(p3.get());
+  EXPECT_EQ(nullptr, pParent->GetNthChild(-1));
+  EXPECT_EQ(p0.get(), pParent->GetNthChild(0));
+  EXPECT_EQ(p1.get(), pParent->GetNthChild(1));
+  EXPECT_EQ(p2.get(), pParent->GetNthChild(2));
+  EXPECT_EQ(p3.get(), pParent->GetNthChild(3));
+  EXPECT_EQ(nullptr, pParent->GetNthChild(4));
+  pParent->RemoveAllChildren();
+}
+
+TEST(TreeNode, AppendFirstChild) {
+  auto parent = pdfium::MakeUnique<TestTreeNode>();
+  auto child0 = pdfium::MakeUnique<TestTreeNode>();
+  auto child1 = pdfium::MakeUnique<TestTreeNode>();
+  parent->AppendFirstChild(child0.get());
+  EXPECT_EQ(child0.get(), parent->GetFirstChild());
+  parent->AppendFirstChild(child1.get());
+  EXPECT_EQ(child1.get(), parent->GetFirstChild());
+  EXPECT_EQ(child1.get(), parent->GetNthChild(0));
+  EXPECT_EQ(child0.get(), parent->GetNthChild(1));
+}
+
+TEST(TreeNode, RemoveChild) {
+  auto parent = pdfium::MakeUnique<TestTreeNode>();
+  auto child0 = pdfium::MakeUnique<TestTreeNode>();
+  auto child1 = pdfium::MakeUnique<TestTreeNode>();
+
+  parent->AppendFirstChild(child0.get());
+  parent->AppendLastChild(child1.get());
+  EXPECT_EQ(child0.get(), parent->GetFirstChild());
+  EXPECT_EQ(child1.get(), parent->GetLastChild());
+  parent->RemoveChild(child0.get());
+  EXPECT_EQ(child1.get(), parent->GetFirstChild());
+  EXPECT_EQ(child1.get(), parent->GetLastChild());
+  parent->RemoveChild(child1.get());
+  EXPECT_EQ(nullptr, parent->GetFirstChild());
+  EXPECT_EQ(nullptr, parent->GetLastChild());
+
+  parent->AppendFirstChild(child0.get());
+  parent->AppendLastChild(child1.get());
+  EXPECT_EQ(child0.get(), parent->GetFirstChild());
+  EXPECT_EQ(child1.get(), parent->GetLastChild());
+  parent->RemoveChild(child1.get());
+  EXPECT_EQ(child0.get(), parent->GetFirstChild());
+  EXPECT_EQ(child0.get(), parent->GetLastChild());
+  parent->RemoveChild(child0.get());
+  EXPECT_EQ(nullptr, parent->GetFirstChild());
+  EXPECT_EQ(nullptr, parent->GetLastChild());
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/unowned_ptr.h b/core/fxcrt/unowned_ptr.h
index f9753cd..f7ff480 100644
--- a/core/fxcrt/unowned_ptr.h
+++ b/core/fxcrt/unowned_ptr.h
@@ -19,7 +19,7 @@
 //    an unowned ptr will fail to compile rather than silently succeeding,
 //    since it is a class and not a raw pointer.
 //
-// 2. When built for a memory tool like ASAN, the class provides a destructor
+// 2. When built using the memory tool ASAN, the class provides a destructor
 //    which checks that the object being pointed to is still alive.
 //
 // Hence, when using UnownedPtr, no dangling pointers are ever permitted,
@@ -32,35 +32,56 @@
 //
 // The array indexing operation [] is not supported on an unowned ptr,
 // because an unowned ptr expresses a one to one relationship with some
-// other heap object.
+// other heap object. Use pdfium::span<> for the cases where indexing
+// into an unowned array is desired, which performs the same checks.
+
+namespace pdfium {
+
+template <typename T>
+class span;
+
+}  // namespace pdfium
 
 namespace fxcrt {
 
 template <class T>
 class UnownedPtr {
  public:
-  UnownedPtr() = default;
-  UnownedPtr(const UnownedPtr& that) : UnownedPtr(that.Get()) {}
+  constexpr UnownedPtr() noexcept = default;
+  constexpr UnownedPtr(const UnownedPtr& that) noexcept = default;
+
+  // Move-construct an UnownedPtr. After construction, |that| will be NULL.
+  constexpr UnownedPtr(UnownedPtr&& that) noexcept : m_pObj(that.Release()) {}
 
   template <typename U>
-  explicit UnownedPtr(U* pObj) : m_pObj(pObj) {}
+  explicit constexpr UnownedPtr(U* pObj) noexcept : m_pObj(pObj) {}
 
   // Deliberately implicit to allow returning nullptrs.
   // NOLINTNEXTLINE(runtime/explicit)
-  UnownedPtr(std::nullptr_t ptr) {}
+  constexpr UnownedPtr(std::nullptr_t ptr) noexcept {}
 
   ~UnownedPtr() { ProbeForLowSeverityLifetimeIssue(); }
 
-  UnownedPtr& operator=(T* that) {
+  void Reset(T* obj = nullptr) {
     ProbeForLowSeverityLifetimeIssue();
-    m_pObj = that;
+    m_pObj = obj;
+  }
+
+  UnownedPtr& operator=(T* that) noexcept {
+    Reset(that);
     return *this;
   }
 
-  UnownedPtr& operator=(const UnownedPtr& that) {
-    ProbeForLowSeverityLifetimeIssue();
+  UnownedPtr& operator=(const UnownedPtr& that) noexcept {
     if (*this != that)
-      m_pObj = that.Get();
+      Reset(that.Get());
+    return *this;
+  }
+
+  // Move-assign an UnownedPtr. After assignment, |that| will be NULL.
+  UnownedPtr& operator=(UnownedPtr&& that) noexcept {
+    if (*this != that)
+      Reset(that.Release());
     return *this;
   }
 
@@ -80,7 +101,7 @@
     return !(*this == that);
   }
 
-  T* Get() const { return m_pObj; }
+  T* Get() const noexcept { return m_pObj; }
 
   T* Release() {
     ProbeForLowSeverityLifetimeIssue();
@@ -94,13 +115,21 @@
   T* operator->() const { return m_pObj; }
 
  private:
+  friend class pdfium::span<T>;
+
   inline void ProbeForLowSeverityLifetimeIssue() {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+#if defined(ADDRESS_SANITIZER)
     if (m_pObj)
       reinterpret_cast<const volatile uint8_t*>(m_pObj)[0];
 #endif
   }
 
+  inline void ReleaseBadPointer() {
+#if defined(ADDRESS_SANITIZER)
+    m_pObj = nullptr;
+#endif
+  }
+
   T* m_pObj = nullptr;
 };
 
diff --git a/core/fxcrt/unowned_ptr_unittest.cpp b/core/fxcrt/unowned_ptr_unittest.cpp
index 26dffd4..fa884d4 100644
--- a/core/fxcrt/unowned_ptr_unittest.cpp
+++ b/core/fxcrt/unowned_ptr_unittest.cpp
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
 
 namespace fxcrt {
 namespace {
@@ -18,39 +19,48 @@
 };
 
 void DeleteDangling() {
-  Clink* ptr1 = new Clink();
-  Clink* ptr2 = new Clink();
-  ptr2->next_ = ptr1;
-  delete ptr1;
-  delete ptr2;
+  auto ptr2 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr1 = pdfium::MakeUnique<Clink>();
+    ptr2->next_ = ptr1.get();
+  }
+}
+
+void ResetDangling() {
+  auto ptr2 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr1 = pdfium::MakeUnique<Clink>();
+    ptr2->next_.Reset(ptr1.get());
+  }
+  ptr2->next_.Reset();
 }
 
 void AssignDangling() {
-  Clink* ptr1 = new Clink();
-  Clink* ptr2 = new Clink();
-  ptr2->next_ = ptr1;
-  delete ptr1;
+  auto ptr2 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr1 = pdfium::MakeUnique<Clink>();
+    ptr2->next_ = ptr1.get();
+  }
   ptr2->next_ = nullptr;
-  delete ptr2;
 }
 
 void ReleaseDangling() {
-  Clink* ptr1 = new Clink();
-  Clink* ptr2 = new Clink();
-  ptr2->next_ = ptr1;
-  delete ptr1;
+  auto ptr2 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr1 = pdfium::MakeUnique<Clink>();
+    ptr2->next_ = ptr1.get();
+  }
   ptr2->next_.Release();
-  delete ptr2;
 }
 
 }  // namespace
 
 TEST(UnownedPtr, PtrOk) {
-  Clink* ptr1 = new Clink();
-  Clink* ptr2 = new Clink();
-  ptr2->next_ = ptr1;
-  delete ptr2;
-  delete ptr1;
+  auto ptr1 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr2 = pdfium::MakeUnique<Clink>();
+    ptr2->next_ = ptr1.get();
+  }
 }
 
 TEST(UnownedPtr, PtrNotOk) {
@@ -61,13 +71,30 @@
 #endif
 }
 
+TEST(UnownedPtr, ResetOk) {
+  auto ptr1 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr2 = pdfium::MakeUnique<Clink>();
+    ptr2->next_.Reset(ptr1.get());
+    ptr2->next_.Reset(nullptr);
+  }
+}
+
+TEST(UnownedPtr, ResetNotOk) {
+#if defined(ADDRESS_SANITIZER)
+  EXPECT_DEATH(ResetDangling(), "");
+#else
+  ResetDangling();
+#endif
+}
+
 TEST(UnownedPtr, AssignOk) {
-  Clink* ptr1 = new Clink();
-  Clink* ptr2 = new Clink();
-  ptr2->next_ = ptr1;
-  ptr2->next_ = nullptr;
-  delete ptr2;
-  delete ptr1;
+  auto ptr1 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr2 = pdfium::MakeUnique<Clink>();
+    ptr2->next_ = ptr1.get();
+    ptr2->next_ = nullptr;
+  }
 }
 
 TEST(UnownedPtr, AssignNotOk) {
@@ -79,12 +106,33 @@
 }
 
 TEST(UnownedPtr, ReleaseOk) {
-  Clink* ptr1 = new Clink();
-  Clink* ptr2 = new Clink();
-  ptr2->next_ = ptr1;
-  ptr2->next_.Release();
-  delete ptr1;
-  delete ptr2;
+  auto ptr2 = pdfium::MakeUnique<Clink>();
+  {
+    auto ptr1 = pdfium::MakeUnique<Clink>();
+    ptr2->next_ = ptr1.get();
+    ptr2->next_.Release();
+  }
+}
+
+TEST(UnownedPtr, MoveCtorOk) {
+  UnownedPtr<Clink> outer;
+  {
+    auto owned = pdfium::MakeUnique<Clink>();
+    outer = owned.get();
+    UnownedPtr<Clink> inner(std::move(outer));
+    EXPECT_EQ(nullptr, outer.Get());
+  }
+}
+
+TEST(UnownedPtr, MoveAssignOk) {
+  UnownedPtr<Clink> outer;
+  {
+    auto owned = pdfium::MakeUnique<Clink>();
+    outer = owned.get();
+    UnownedPtr<Clink> inner;
+    inner = std::move(outer);
+    EXPECT_EQ(nullptr, outer.Get());
+  }
 }
 
 TEST(UnownedPtr, ReleaseNotOk) {
diff --git a/core/fxcrt/weak_ptr.h b/core/fxcrt/weak_ptr.h
index 3ec3942..7b345aa 100644
--- a/core/fxcrt/weak_ptr.h
+++ b/core/fxcrt/weak_ptr.h
@@ -58,8 +58,8 @@
  private:
   class Handle {
    public:
-    explicit Handle(std::unique_ptr<T, D> ptr)
-        : m_nCount(0), m_pObj(std::move(ptr)) {}
+    explicit Handle(std::unique_ptr<T, D> ptr) : m_pObj(std::move(ptr)) {}
+
     void Reset(std::unique_ptr<T, D> ptr) { m_pObj = std::move(ptr); }
     void Clear() {     // Now you're all weak ptrs ...
       m_pObj.reset();  // unique_ptr nulls first before invoking delete.
@@ -78,7 +78,7 @@
    private:
     ~Handle() = default;
 
-    intptr_t m_nCount;
+    intptr_t m_nCount = 0;
     std::unique_ptr<T, D> m_pObj;
   };
 
diff --git a/core/fxcrt/weak_ptr_unittest.cpp b/core/fxcrt/weak_ptr_unittest.cpp
index 22a7230..f4026fb 100644
--- a/core/fxcrt/weak_ptr_unittest.cpp
+++ b/core/fxcrt/weak_ptr_unittest.cpp
@@ -7,7 +7,6 @@
 #include <memory>
 #include <utility>
 
-#include "core/fxcrt/fx_memory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace fxcrt {
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index 8d47564..10a31c3 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -12,7 +12,6 @@
 #include <cctype>
 #include <cwctype>
 
-#include "core/fxcrt/cfx_utf8decoder.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
@@ -78,11 +77,11 @@
     }
     if (nWidth == 0) {
       nWidth = FXSYS_wtoi(pStr);
-      while (std::iswdigit(*pStr))
+      while (FXSYS_IsDecimalDigit(*pStr))
         ++pStr;
     }
     if (nWidth < 0 || nWidth > 128 * 1024)
-      return Optional<size_t>();
+      return pdfium::nullopt;
     int nPrecision = 0;
     if (*pStr == '.') {
       pStr++;
@@ -91,12 +90,12 @@
         pStr++;
       } else {
         nPrecision = FXSYS_wtoi(pStr);
-        while (std::iswdigit(*pStr))
+        while (FXSYS_IsDecimalDigit(*pStr))
           ++pStr;
       }
     }
     if (nPrecision < 0 || nPrecision > 128 * 1024)
-      return Optional<size_t>();
+      return pdfium::nullopt;
     int nModifier = 0;
     if (*pStr == L'I' && *(pStr + 1) == L'6' && *(pStr + 2) == L'4') {
       pStr += 3;
@@ -245,67 +244,38 @@
     nMaxLen += nItemLen;
   }
   nMaxLen += 32;  // Fudge factor.
-  return Optional<size_t>(nMaxLen);
+  return nMaxLen;
 }
 
 // Returns string unless we ran out of space.
 Optional<WideString> TryVSWPrintf(size_t size,
                                   const wchar_t* pFormat,
                                   va_list argList) {
-  WideString str;
-  wchar_t* buffer = str.GetBuffer(size);
-
-  // In the following two calls, there's always space in the buffer for
-  // a terminating NUL that's not included in nMaxLen.
-  // For vswprintf(), MSAN won't untaint the buffer on a truncated write's
-  // -1 return code even though the buffer is written. Probably just as well
-  // not to trust the vendor's implementation to write anything anyways.
-  // See https://crbug.com/705912.
-  memset(buffer, 0, (size + 1) * sizeof(wchar_t));
-  int ret = vswprintf(buffer, size + 1, pFormat, argList);
-
-  bool bSufficientBuffer = ret >= 0 || buffer[size - 1] == 0;
-  if (!bSufficientBuffer)
+  if (!size)
     return {};
 
+  WideString str;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> buffer = str.GetBuffer(size);
+
+    // In the following two calls, there's always space in the WideString
+    // for a terminating NUL that's not included in the span.
+    // For vswprintf(), MSAN won't untaint the buffer on a truncated write's
+    // -1 return code even though the buffer is written. Probably just as well
+    // not to trust the vendor's implementation to write anything anyways.
+    // See https://crbug.com/705912.
+    memset(buffer.data(), 0, (size + 1) * sizeof(wchar_t));
+    int ret = vswprintf(buffer.data(), size + 1, pFormat, argList);
+
+    bool bSufficientBuffer = ret >= 0 || buffer[size - 1] == 0;
+    if (!bSufficientBuffer)
+      return {};
+  }
   str.ReleaseBuffer(str.GetStringLength());
   return {str};
 }
 
-#ifndef NDEBUG
-bool IsValidWideCodePage(uint16_t codepage) {
-  switch (codepage) {
-    case FX_CODEPAGE_DefANSI:
-    case FX_CODEPAGE_ShiftJIS:
-    case FX_CODEPAGE_ChineseSimplified:
-    case FX_CODEPAGE_Hangul:
-    case FX_CODEPAGE_ChineseTraditional:
-      return true;
-    default:
-      return false;
-  }
-}
-#endif
-
-WideString GetWideString(uint16_t codepage, const ByteStringView& bstr) {
-#ifndef NDEBUG
-  ASSERT(IsValidWideCodePage(codepage));
-#endif
-
-  int src_len = bstr.GetLength();
-  int dest_len = FXSYS_MultiByteToWideChar(
-      codepage, 0, bstr.unterminated_c_str(), src_len, nullptr, 0);
-  if (!dest_len)
-    return WideString();
-
-  WideString wstr;
-  wchar_t* dest_buf = wstr.GetBuffer(dest_len);
-  FXSYS_MultiByteToWideChar(codepage, 0, bstr.unterminated_c_str(), src_len,
-                            dest_buf, dest_len);
-  wstr.ReleaseBuffer(dest_len);
-  return wstr;
-}
-
 }  // namespace
 
 namespace fxcrt {
@@ -326,7 +296,7 @@
     va_end(argListCopy);
 
     if (!guess.has_value())
-      return L"";
+      return WideString();
     maxLen = pdfium::base::checked_cast<int>(guess.value());
   }
 
@@ -340,7 +310,7 @@
       return *ret;
     maxLen *= 2;
   }
-  return L"";
+  return WideString();
 }
 
 // static
@@ -373,14 +343,14 @@
 WideString::WideString(const wchar_t* ptr)
     : WideString(ptr, ptr ? wcslen(ptr) : 0) {}
 
-WideString::WideString(const WideStringView& stringSrc) {
+WideString::WideString(WideStringView stringSrc) {
   if (!stringSrc.IsEmpty()) {
     m_pData.Reset(StringData::Create(stringSrc.unterminated_c_str(),
                                      stringSrc.GetLength()));
   }
 }
 
-WideString::WideString(const WideStringView& str1, const WideStringView& str2) {
+WideString::WideString(WideStringView str1, WideStringView str2) {
   FX_SAFE_SIZE_T nSafeLen = str1.GetLength();
   nSafeLen += str2.GetLength();
 
@@ -415,51 +385,58 @@
 
 WideString::~WideString() {}
 
-const WideString& WideString::operator=(const wchar_t* pStr) {
-  if (!pStr || !pStr[0])
+WideString& WideString::operator=(const wchar_t* str) {
+  if (!str || !str[0])
     clear();
   else
-    AssignCopy(pStr, wcslen(pStr));
+    AssignCopy(str, wcslen(str));
 
   return *this;
 }
 
-const WideString& WideString::operator=(const WideStringView& stringSrc) {
-  if (stringSrc.IsEmpty())
+WideString& WideString::operator=(WideStringView str) {
+  if (str.IsEmpty())
     clear();
   else
-    AssignCopy(stringSrc.unterminated_c_str(), stringSrc.GetLength());
+    AssignCopy(str.unterminated_c_str(), str.GetLength());
 
   return *this;
 }
 
-const WideString& WideString::operator=(const WideString& stringSrc) {
-  if (m_pData != stringSrc.m_pData)
-    m_pData = stringSrc.m_pData;
+WideString& WideString::operator=(const WideString& that) {
+  if (m_pData != that.m_pData)
+    m_pData = that.m_pData;
 
   return *this;
 }
 
-const WideString& WideString::operator+=(const wchar_t* pStr) {
-  if (pStr)
-    Concat(pStr, wcslen(pStr));
+WideString& WideString::operator=(WideString&& that) {
+  if (m_pData != that.m_pData)
+    m_pData = std::move(that.m_pData);
 
   return *this;
 }
 
-const WideString& WideString::operator+=(wchar_t ch) {
+WideString& WideString::operator+=(const wchar_t* str) {
+  if (str)
+    Concat(str, wcslen(str));
+
+  return *this;
+}
+
+WideString& WideString::operator+=(wchar_t ch) {
   Concat(&ch, 1);
   return *this;
 }
 
-const WideString& WideString::operator+=(const WideString& str) {
+WideString& WideString::operator+=(const WideString& str) {
   if (str.m_pData)
     Concat(str.m_pData->m_String, str.m_pData->m_nDataLength);
 
   return *this;
 }
 
-const WideString& WideString::operator+=(const WideStringView& str) {
+WideString& WideString::operator+=(WideStringView str) {
   if (!str.IsEmpty())
     Concat(str.unterminated_c_str(), str.GetLength());
 
@@ -477,7 +454,7 @@
          wmemcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
 }
 
-bool WideString::operator==(const WideStringView& str) const {
+bool WideString::operator==(WideStringView str) const {
   if (!m_pData)
     return str.IsEmpty();
 
@@ -505,7 +482,7 @@
   return Compare(ptr) < 0;
 }
 
-bool WideString::operator<(const WideStringView& str) const {
+bool WideString::operator<(WideStringView str) const {
   if (!m_pData && !str.unterminated_c_str())
     return false;
   if (c_str() == str.unterminated_c_str())
@@ -586,29 +563,29 @@
   GetBuffer(len);
 }
 
-wchar_t* WideString::GetBuffer(size_t nMinBufLength) {
+pdfium::span<wchar_t> WideString::GetBuffer(size_t nMinBufLength) {
   if (!m_pData) {
     if (nMinBufLength == 0)
-      return nullptr;
+      return pdfium::span<wchar_t>();
 
     m_pData.Reset(StringData::Create(nMinBufLength));
     m_pData->m_nDataLength = 0;
     m_pData->m_String[0] = 0;
-    return m_pData->m_String;
+    return pdfium::span<wchar_t>(m_pData->m_String, m_pData->m_nAllocLength);
   }
 
   if (m_pData->CanOperateInPlace(nMinBufLength))
-    return m_pData->m_String;
+    return pdfium::span<wchar_t>(m_pData->m_String, m_pData->m_nAllocLength);
 
   nMinBufLength = std::max(nMinBufLength, m_pData->m_nDataLength);
   if (nMinBufLength == 0)
-    return nullptr;
+    return pdfium::span<wchar_t>();
 
   RetainPtr<StringData> pNewData(StringData::Create(nMinBufLength));
   pNewData->CopyContents(*m_pData);
   pNewData->m_nDataLength = m_pData->m_nDataLength;
   m_pData.Swap(pNewData);
-  return m_pData->m_String;
+  return pdfium::span<wchar_t>(m_pData->m_String, m_pData->m_nAllocLength);
 }
 
 size_t WideString::Delete(size_t index, size_t count) {
@@ -616,8 +593,7 @@
     return 0;
 
   size_t old_length = m_pData->m_nDataLength;
-  if (count == 0 ||
-      index != pdfium::clamp(index, static_cast<size_t>(0), old_length))
+  if (count == 0 || index != pdfium::clamp<size_t>(index, 0, old_length))
     return old_length;
 
   size_t removal_length = index + count;
@@ -647,35 +623,78 @@
     return;
   }
 
+  size_t nConcatLen = std::max(m_pData->m_nDataLength / 2, nSrcLen);
   RetainPtr<StringData> pNewData(
-      StringData::Create(m_pData->m_nDataLength + nSrcLen));
+      StringData::Create(m_pData->m_nDataLength + nConcatLen));
   pNewData->CopyContents(*m_pData);
   pNewData->CopyContentsAt(m_pData->m_nDataLength, pSrcData, nSrcLen);
+  pNewData->m_nDataLength = m_pData->m_nDataLength + nSrcLen;
   m_pData.Swap(pNewData);
 }
 
-ByteString WideString::UTF8Encode() const {
+intptr_t WideString::ReferenceCountForTesting() const {
+  return m_pData ? m_pData->m_nRefs : 0;
+}
+
+ByteString WideString::ToASCII() const {
+  ByteString result;
+  result.Reserve(GetLength());
+  for (wchar_t wc : *this)
+    result.InsertAtBack(static_cast<char>(wc & 0x7f));
+  return result;
+}
+
+ByteString WideString::ToLatin1() const {
+  ByteString result;
+  result.Reserve(GetLength());
+  for (wchar_t wc : *this)
+    result.InsertAtBack(static_cast<char>(wc & 0xff));
+  return result;
+}
+
+ByteString WideString::ToDefANSI() const {
+  int src_len = GetLength();
+  int dest_len = FXSYS_WideCharToMultiByte(
+      FX_CODEPAGE_DefANSI, 0, c_str(), src_len, nullptr, 0, nullptr, nullptr);
+  if (!dest_len)
+    return ByteString();
+
+  ByteString bstr;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> dest_buf = bstr.GetBuffer(dest_len);
+    FXSYS_WideCharToMultiByte(FX_CODEPAGE_DefANSI, 0, c_str(), src_len,
+                              dest_buf.data(), dest_len, nullptr, nullptr);
+  }
+  bstr.ReleaseBuffer(dest_len);
+  return bstr;
+}
+
+ByteString WideString::ToUTF8() const {
   return FX_UTF8Encode(AsStringView());
 }
 
-ByteString WideString::UTF16LE_Encode() const {
-  if (!m_pData) {
+ByteString WideString::ToUTF16LE() const {
+  if (!m_pData)
     return ByteString("\0\0", 2);
-  }
-  int len = m_pData->m_nDataLength;
+
   ByteString result;
-  char* buffer = result.GetBuffer(len * 2 + 2);
-  for (int i = 0; i < len; i++) {
-    buffer[i * 2] = m_pData->m_String[i] & 0xff;
-    buffer[i * 2 + 1] = m_pData->m_String[i] >> 8;
+  int len = m_pData->m_nDataLength;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> buffer = result.GetBuffer(len * 2 + 2);
+    for (int i = 0; i < len; i++) {
+      buffer[i * 2] = m_pData->m_String[i] & 0xff;
+      buffer[i * 2 + 1] = m_pData->m_String[i] >> 8;
+    }
+    buffer[len * 2] = 0;
+    buffer[len * 2 + 1] = 0;
   }
-  buffer[len * 2] = 0;
-  buffer[len * 2 + 1] = 0;
   result.ReleaseBuffer(len * 2 + 2);
   return result;
 }
 
-WideString WideString::Mid(size_t first, size_t count) const {
+WideString WideString::Substr(size_t first, size_t count) const {
   if (!m_pData)
     return WideString();
 
@@ -696,16 +715,16 @@
   return dest;
 }
 
-WideString WideString::Left(size_t count) const {
+WideString WideString::First(size_t count) const {
   if (count == 0 || !IsValidLength(count))
     return WideString();
-  return Mid(0, count);
+  return Substr(0, count);
 }
 
-WideString WideString::Right(size_t count) const {
+WideString WideString::Last(size_t count) const {
   if (count == 0 || !IsValidLength(count))
     return WideString();
-  return Mid(GetLength() - count, count);
+  return Substr(GetLength() - count, count);
 }
 
 void WideString::AllocCopy(WideString& dest,
@@ -719,46 +738,57 @@
   dest.m_pData.Swap(pNewData);
 }
 
-size_t WideString::Insert(size_t location, wchar_t ch) {
-  const size_t cur_length = m_pData ? m_pData->m_nDataLength : 0;
-  if (!IsValidLength(location))
+size_t WideString::Insert(size_t index, wchar_t ch) {
+  const size_t cur_length = GetLength();
+  if (!IsValidLength(index))
     return cur_length;
 
   const size_t new_length = cur_length + 1;
   ReallocBeforeWrite(new_length);
-  wmemmove(m_pData->m_String + location + 1, m_pData->m_String + location,
-           new_length - location);
-  m_pData->m_String[location] = ch;
+  wmemmove(m_pData->m_String + index + 1, m_pData->m_String + index,
+           new_length - index);
+  m_pData->m_String[index] = ch;
   m_pData->m_nDataLength = new_length;
   return new_length;
 }
 
 Optional<size_t> WideString::Find(wchar_t ch, size_t start) const {
   if (!m_pData)
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   if (!IsValidIndex(start))
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   const wchar_t* pStr =
       wmemchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start);
   return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : Optional<size_t>();
+              : pdfium::nullopt;
 }
 
-Optional<size_t> WideString::Find(const WideStringView& subStr,
-                                  size_t start) const {
+Optional<size_t> WideString::Find(WideStringView subStr, size_t start) const {
   if (!m_pData)
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   if (!IsValidIndex(start))
-    return Optional<size_t>();
+    return pdfium::nullopt;
 
   const wchar_t* pStr =
       FX_wcsstr(m_pData->m_String + start, m_pData->m_nDataLength - start,
                 subStr.unterminated_c_str(), subStr.GetLength());
   return pStr ? Optional<size_t>(static_cast<size_t>(pStr - m_pData->m_String))
-              : Optional<size_t>();
+              : pdfium::nullopt;
+}
+
+Optional<size_t> WideString::ReverseFind(wchar_t ch) const {
+  if (!m_pData)
+    return pdfium::nullopt;
+
+  size_t nLength = m_pData->m_nDataLength;
+  while (nLength--) {
+    if (m_pData->m_String[nLength] == ch)
+      return nLength;
+  }
+  return pdfium::nullopt;
 }
 
 void WideString::MakeLower() {
@@ -778,7 +808,7 @@
 }
 
 size_t WideString::Remove(wchar_t chRemove) {
-  if (!m_pData || m_pData->m_nDataLength < 1)
+  if (!m_pData || m_pData->m_nDataLength == 0)
     return 0;
 
   wchar_t* pstrSource = m_pData->m_String;
@@ -811,8 +841,7 @@
   return count;
 }
 
-size_t WideString::Replace(const WideStringView& pOld,
-                           const WideStringView& pNew) {
+size_t WideString::Replace(WideStringView pOld, WideStringView pNew) {
   if (!m_pData || pOld.IsEmpty())
     return 0;
 
@@ -861,38 +890,76 @@
 }
 
 // static
-WideString WideString::FromLocal(const ByteStringView& str) {
-  return FromCodePage(str, 0);
+WideString WideString::FromASCII(ByteStringView bstr) {
+  WideString result;
+  result.Reserve(bstr.GetLength());
+  for (char c : bstr)
+    result.InsertAtBack(static_cast<wchar_t>(c & 0x7f));
+  return result;
 }
 
 // static
-WideString WideString::FromCodePage(const ByteStringView& str,
-                                    uint16_t codepage) {
-  return GetWideString(codepage, str);
+WideString WideString::FromLatin1(ByteStringView bstr) {
+  WideString result;
+  result.Reserve(bstr.GetLength());
+  for (char c : bstr)
+    result.InsertAtBack(static_cast<wchar_t>(c & 0xff));
+  return result;
 }
 
 // static
-WideString WideString::FromUTF8(const ByteStringView& str) {
-  if (str.IsEmpty())
+WideString WideString::FromDefANSI(ByteStringView bstr) {
+  int src_len = bstr.GetLength();
+  int dest_len = FXSYS_MultiByteToWideChar(
+      FX_CODEPAGE_DefANSI, 0, bstr.unterminated_c_str(), src_len, nullptr, 0);
+  if (!dest_len)
     return WideString();
 
-  CFX_UTF8Decoder decoder;
-  for (size_t i = 0; i < str.GetLength(); i++)
-    decoder.Input(str[i]);
+  WideString wstr;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> dest_buf = wstr.GetBuffer(dest_len);
+    FXSYS_MultiByteToWideChar(FX_CODEPAGE_DefANSI, 0, bstr.unterminated_c_str(),
+                              src_len, dest_buf.data(), dest_len);
+  }
+  wstr.ReleaseBuffer(dest_len);
+  return wstr;
+}
 
-  return WideString(decoder.GetResult());
+// static
+WideString WideString::FromUTF8(ByteStringView str) {
+  return FX_UTF8Decode(str);
 }
 
 // static
 WideString WideString::FromUTF16LE(const unsigned short* wstr, size_t wlen) {
-  if (!wstr || wlen == 0) {
+  if (!wstr || wlen == 0)
     return WideString();
-  }
 
   WideString result;
-  wchar_t* buf = result.GetBuffer(wlen);
-  for (size_t i = 0; i < wlen; i++) {
-    buf[i] = wstr[i];
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> buf = result.GetBuffer(wlen);
+    for (size_t i = 0; i < wlen; i++)
+      buf[i] = wstr[i];
+  }
+  result.ReleaseBuffer(wlen);
+  return result;
+}
+
+WideString WideString::FromUTF16BE(const unsigned short* wstr, size_t wlen) {
+  if (!wstr || wlen == 0)
+    return WideString();
+
+  WideString result;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> buf = result.GetBuffer(wlen);
+    for (size_t i = 0; i < wlen; i++) {
+      auto wch = wstr[i];
+      wch = (wch >> 8) | (wch << 8);
+      buf[i] = wch;
+    }
   }
   result.ReleaseBuffer(wlen);
   return result;
@@ -904,10 +971,10 @@
   m_pData->m_String[index] = c;
 }
 
-int WideString::Compare(const wchar_t* lpsz) const {
+int WideString::Compare(const wchar_t* str) const {
   if (m_pData)
-    return lpsz ? wcscmp(m_pData->m_String, lpsz) : 1;
-  return (!lpsz || lpsz[0] == 0) ? 0 : -1;
+    return str ? wcscmp(m_pData->m_String, str) : 1;
+  return (!str || str[0] == 0) ? 0 : -1;
 }
 
 int WideString::Compare(const WideString& str) const {
@@ -924,13 +991,13 @@
     return result;
   if (this_len == that_len)
     return 0;
-  return this_len < that_len;
+  return this_len < that_len ? -1 : 1;
 }
 
-int WideString::CompareNoCase(const wchar_t* lpsz) const {
+int WideString::CompareNoCase(const wchar_t* str) const {
   if (m_pData)
-    return lpsz ? FXSYS_wcsicmp(m_pData->m_String, lpsz) : 1;
-  return (!lpsz || lpsz[0] == 0) ? 0 : -1;
+    return str ? FXSYS_wcsicmp(m_pData->m_String, str) : 1;
+  return (!str || str[0] == 0) ? 0 : -1;
 }
 
 size_t WideString::WStringLength(const unsigned short* str) {
@@ -952,7 +1019,7 @@
   TrimLeft(str);
 }
 
-void WideString::Trim(const WideStringView& targets) {
+void WideString::Trim(WideStringView targets) {
   TrimRight(targets);
   TrimLeft(targets);
 }
@@ -966,7 +1033,7 @@
   TrimLeft(str);
 }
 
-void WideString::TrimLeft(const WideStringView& targets) {
+void WideString::TrimLeft(WideStringView targets) {
   if (!m_pData || targets.IsEmpty())
     return;
 
@@ -1004,7 +1071,7 @@
   TrimRight(str);
 }
 
-void WideString::TrimRight(const WideStringView& targets) {
+void WideString::TrimRight(WideStringView targets) {
   if (IsEmpty() || targets.IsEmpty())
     return;
 
@@ -1019,64 +1086,38 @@
   }
 }
 
-float FX_wtof(const wchar_t* str, int len) {
-  if (len == 0) {
-    return 0.0;
-  }
-  int cc = 0;
-  bool bNegative = false;
-  if (str[0] == '+') {
-    cc++;
-  } else if (str[0] == '-') {
-    bNegative = true;
-    cc++;
-  }
-  int integer = 0;
-  while (cc < len) {
-    if (str[cc] == '.') {
-      break;
-    }
-    integer = integer * 10 + FXSYS_DecimalCharToInt(str[cc]);
-    cc++;
-  }
-  float fraction = 0;
-  if (str[cc] == '.') {
-    cc++;
-    float scale = 0.1f;
-    while (cc < len) {
-      fraction += scale * FXSYS_DecimalCharToInt(str[cc]);
-      scale *= 0.1f;
-      cc++;
-    }
-  }
-  fraction += static_cast<float>(integer);
-  return bNegative ? -fraction : fraction;
-}
-
 int WideString::GetInteger() const {
   return m_pData ? FXSYS_wtoi(m_pData->m_String) : 0;
 }
 
-float WideString::GetFloat() const {
-  return m_pData ? FX_wtof(m_pData->m_String, m_pData->m_nDataLength) : 0.0f;
-}
-
 std::wostream& operator<<(std::wostream& os, const WideString& str) {
   return os.write(str.c_str(), str.GetLength());
 }
 
 std::ostream& operator<<(std::ostream& os, const WideString& str) {
-  os << str.UTF8Encode();
+  os << str.ToUTF8();
   return os;
 }
 
-std::wostream& operator<<(std::wostream& os, const WideStringView& str) {
+std::wostream& operator<<(std::wostream& os, WideStringView str) {
   return os.write(str.unterminated_c_str(), str.GetLength());
 }
 
-std::ostream& operator<<(std::ostream& os, const WideStringView& str) {
+std::ostream& operator<<(std::ostream& os, WideStringView str) {
   os << FX_UTF8Encode(str);
   return os;
 }
 
 }  // namespace fxcrt
+
+uint32_t FX_HashCode_GetW(WideStringView str, bool bIgnoreCase) {
+  uint32_t dwHashCode = 0;
+  if (bIgnoreCase) {
+    for (wchar_t c : str)  // match FXSYS_towlower() arg type.
+      dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c);
+  } else {
+    for (WideStringView::UnsignedType c : str)
+      dwHashCode = 1313 * dwHashCode + c;
+  }
+  return dwHashCode;
+}
diff --git a/core/fxcrt/widestring.h b/core/fxcrt/widestring.h
index 30a423d..58e7544 100644
--- a/core/fxcrt/widestring.h
+++ b/core/fxcrt/widestring.h
@@ -9,21 +9,20 @@
 
 #include <functional>
 #include <iterator>
+#include <ostream>
 #include <utility>
 
-#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_data_template.h"
 #include "core/fxcrt/string_view_template.h"
+#include "third_party/base/logging.h"
 #include "third_party/base/optional.h"
-
+#include "third_party/base/span.h"
 
 namespace fxcrt {
 
 class ByteString;
-class StringPool_WideString_Test;
-class WideString_ConcatInPlace_Test;
 
 // A mutable string with shared buffers using copy-on-write semantics that
 // avoids the cost of std::string's iterator stability guarantees.
@@ -33,12 +32,14 @@
   using const_iterator = const CharType*;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
-  static WideString Format(const wchar_t* lpszFormat, ...) WARN_UNUSED_RESULT;
+  static WideString Format(const wchar_t* pFormat, ...) WARN_UNUSED_RESULT;
   static WideString FormatV(const wchar_t* lpszFormat,
                             va_list argList) WARN_UNUSED_RESULT;
 
   WideString();
   WideString(const WideString& other);
+
+  // Move-construct a WideString. After construction, |other| is empty.
   WideString(WideString&& other) noexcept;
 
   // Deliberately implicit to avoid calling on every string literal.
@@ -51,21 +52,22 @@
   // NOLINTNEXTLINE(runtime/explicit)
   WideString(char) = delete;
 
-  WideString(const wchar_t* ptr, size_t len);
+  WideString(const wchar_t* pStr, size_t len);
 
-  explicit WideString(const WideStringView& str);
-  WideString(const WideStringView& str1, const WideStringView& str2);
+  explicit WideString(WideStringView str);
+  WideString(WideStringView str1, WideStringView str2);
   WideString(const std::initializer_list<WideStringView>& list);
 
   ~WideString();
 
-  static WideString FromLocal(const ByteStringView& str) WARN_UNUSED_RESULT;
-  static WideString FromCodePage(const ByteStringView& str,
-                                 uint16_t codepage) WARN_UNUSED_RESULT;
-
-  static WideString FromUTF8(const ByteStringView& str) WARN_UNUSED_RESULT;
+  static WideString FromASCII(ByteStringView str) WARN_UNUSED_RESULT;
+  static WideString FromLatin1(ByteStringView str) WARN_UNUSED_RESULT;
+  static WideString FromDefANSI(ByteStringView str) WARN_UNUSED_RESULT;
+  static WideString FromUTF8(ByteStringView str) WARN_UNUSED_RESULT;
   static WideString FromUTF16LE(const unsigned short* str,
                                 size_t len) WARN_UNUSED_RESULT;
+  static WideString FromUTF16BE(const unsigned short* wstr,
+                                size_t wlen) WARN_UNUSED_RESULT;
 
   static size_t WStringLength(const unsigned short* str) WARN_UNUSED_RESULT;
 
@@ -79,6 +81,13 @@
     return WideStringView(c_str(), GetLength());
   }
 
+  // Explicit conversion to span.
+  // Note: Any subsequent modification of |this| will invalidate the result.
+  pdfium::span<const wchar_t> span() const {
+    return pdfium::make_span(m_pData ? m_pData->m_String : nullptr,
+                             GetLength());
+  }
+
   // Note: Any subsequent modification of |this| will invalidate iterators.
   const_iterator begin() const { return m_pData ? m_pData->m_String : nullptr; }
   const_iterator end() const {
@@ -103,34 +112,37 @@
   bool IsValidIndex(size_t index) const { return index < GetLength(); }
   bool IsValidLength(size_t length) const { return length <= GetLength(); }
 
-  const WideString& operator=(const wchar_t* str);
-  const WideString& operator=(const WideString& stringSrc);
-  const WideString& operator=(const WideStringView& stringSrc);
+  WideString& operator=(const wchar_t* str);
+  WideString& operator=(WideStringView str);
+  WideString& operator=(const WideString& that);
 
-  const WideString& operator+=(const wchar_t* str);
-  const WideString& operator+=(wchar_t ch);
-  const WideString& operator+=(const WideString& str);
-  const WideString& operator+=(const WideStringView& str);
+  // Move-assign a WideString. After assignment, |that| is empty.
+  WideString& operator=(WideString&& that);
+
+  WideString& operator+=(const wchar_t* str);
+  WideString& operator+=(wchar_t ch);
+  WideString& operator+=(const WideString& str);
+  WideString& operator+=(WideStringView str);
 
   bool operator==(const wchar_t* ptr) const;
-  bool operator==(const WideStringView& str) const;
+  bool operator==(WideStringView str) const;
   bool operator==(const WideString& other) const;
 
   bool operator!=(const wchar_t* ptr) const { return !(*this == ptr); }
-  bool operator!=(const WideStringView& str) const { return !(*this == str); }
+  bool operator!=(WideStringView str) const { return !(*this == str); }
   bool operator!=(const WideString& other) const { return !(*this == other); }
 
   bool operator<(const wchar_t* ptr) const;
-  bool operator<(const WideStringView& str) const;
+  bool operator<(WideStringView str) const;
   bool operator<(const WideString& other) const;
 
   CharType operator[](const size_t index) const {
-    ASSERT(IsValidIndex(index));
-    return m_pData ? m_pData->m_String[index] : 0;
+    CHECK(IsValidIndex(index));
+    return m_pData->m_String[index];
   }
 
-  CharType First() const { return GetLength() ? (*this)[0] : 0; }
-  CharType Last() const { return GetLength() ? (*this)[GetLength() - 1] : 0; }
+  CharType Front() const { return GetLength() ? (*this)[0] : 0; }
+  CharType Back() const { return GetLength() ? (*this)[GetLength() - 1] : 0; }
 
   void SetAt(size_t index, wchar_t c);
 
@@ -138,9 +150,9 @@
   int Compare(const WideString& str) const;
   int CompareNoCase(const wchar_t* str) const;
 
-  WideString Mid(size_t first, size_t count) const;
-  WideString Left(size_t count) const;
-  WideString Right(size_t count) const;
+  WideString Substr(size_t first, size_t count) const;
+  WideString First(size_t count) const;
+  WideString Last(size_t count) const;
 
   size_t Insert(size_t index, wchar_t ch);
   size_t InsertAtFront(wchar_t ch) { return Insert(0, ch); }
@@ -152,27 +164,30 @@
 
   void Trim();
   void Trim(wchar_t target);
-  void Trim(const WideStringView& targets);
+  void Trim(WideStringView targets);
 
   void TrimLeft();
   void TrimLeft(wchar_t target);
-  void TrimLeft(const WideStringView& targets);
+  void TrimLeft(WideStringView targets);
 
   void TrimRight();
   void TrimRight(wchar_t target);
-  void TrimRight(const WideStringView& targets);
+  void TrimRight(WideStringView targets);
 
   void Reserve(size_t len);
-  wchar_t* GetBuffer(size_t len);
-  void ReleaseBuffer(size_t len);
+
+  // Note: any modification of the string (including ReleaseBuffer()) may
+  // invalidate the span, which must not outlive its buffer.
+  pdfium::span<wchar_t> GetBuffer(size_t nMinBufLength);
+  void ReleaseBuffer(size_t nNewLength);
 
   int GetInteger() const;
-  float GetFloat() const;
 
-  Optional<size_t> Find(const WideStringView& pSub, size_t start = 0) const;
+  Optional<size_t> Find(WideStringView subStr, size_t start = 0) const;
   Optional<size_t> Find(wchar_t ch, size_t start = 0) const;
+  Optional<size_t> ReverseFind(wchar_t ch) const;
 
-  bool Contains(const WideStringView& lpszSub, size_t start = 0) const {
+  bool Contains(WideStringView lpszSub, size_t start = 0) const {
     return Find(lpszSub, start).has_value();
   }
 
@@ -180,45 +195,58 @@
     return Find(ch, start).has_value();
   }
 
-  size_t Replace(const WideStringView& pOld, const WideStringView& pNew);
+  size_t Replace(WideStringView pOld, WideStringView pNew);
   size_t Remove(wchar_t ch);
 
-  ByteString UTF8Encode() const;
+  bool IsASCII() const { return AsStringView().IsASCII(); }
+  bool EqualsASCII(ByteStringView that) const {
+    return AsStringView().EqualsASCII(that);
+  }
+  bool EqualsASCIINoCase(ByteStringView that) const {
+    return AsStringView().EqualsASCIINoCase(that);
+  }
+
+  ByteString ToASCII() const;
+  ByteString ToLatin1() const;
+  ByteString ToDefANSI() const;
+  ByteString ToUTF8() const;
 
   // This method will add \0\0 to the end of the string to represent the
   // wide string terminator. These values are in the string, not just the data,
   // so GetLength() will include them.
-  ByteString UTF16LE_Encode() const;
+  ByteString ToUTF16LE() const;
 
  protected:
   using StringData = StringDataTemplate<wchar_t>;
 
-  void ReallocBeforeWrite(size_t nLen);
-  void AllocBeforeWrite(size_t nLen);
+  void ReallocBeforeWrite(size_t nNewLength);
+  void AllocBeforeWrite(size_t nNewLength);
   void AllocCopy(WideString& dest, size_t nCopyLen, size_t nCopyIndex) const;
   void AssignCopy(const wchar_t* pSrcData, size_t nSrcLen);
-  void Concat(const wchar_t* lpszSrcData, size_t nSrcLen);
+  void Concat(const wchar_t* pSrcData, size_t nSrcLen);
+  intptr_t ReferenceCountForTesting() const;
 
   RetainPtr<StringData> m_pData;
 
-  friend WideString_ConcatInPlace_Test;
-  friend StringPool_WideString_Test;
+  friend class WideString_Assign_Test;
+  friend class WideString_ConcatInPlace_Test;
+  friend class WideString_Construct_Test;
+  friend class StringPool_WideString_Test;
 };
 
-inline WideString operator+(const WideStringView& str1,
-                            const WideStringView& str2) {
+inline WideString operator+(WideStringView str1, WideStringView str2) {
   return WideString(str1, str2);
 }
-inline WideString operator+(const WideStringView& str1, const wchar_t* str2) {
+inline WideString operator+(WideStringView str1, const wchar_t* str2) {
   return WideString(str1, str2);
 }
-inline WideString operator+(const wchar_t* str1, const WideStringView& str2) {
+inline WideString operator+(const wchar_t* str1, WideStringView str2) {
   return WideString(str1, str2);
 }
-inline WideString operator+(const WideStringView& str1, wchar_t ch) {
+inline WideString operator+(WideStringView str1, wchar_t ch) {
   return WideString(str1, WideStringView(ch));
 }
-inline WideString operator+(wchar_t ch, const WideStringView& str2) {
+inline WideString operator+(wchar_t ch, WideStringView str2) {
   return WideString(ch, str2);
 }
 inline WideString operator+(const WideString& str1, const WideString& str2) {
@@ -236,24 +264,22 @@
 inline WideString operator+(const wchar_t* str1, const WideString& str2) {
   return WideString(str1, str2.AsStringView());
 }
-inline WideString operator+(const WideString& str1,
-                            const WideStringView& str2) {
+inline WideString operator+(const WideString& str1, WideStringView str2) {
   return WideString(str1.AsStringView(), str2);
 }
-inline WideString operator+(const WideStringView& str1,
-                            const WideString& str2) {
+inline WideString operator+(WideStringView str1, const WideString& str2) {
   return WideString(str1, str2.AsStringView());
 }
 inline bool operator==(const wchar_t* lhs, const WideString& rhs) {
   return rhs == lhs;
 }
-inline bool operator==(const WideStringView& lhs, const WideString& rhs) {
+inline bool operator==(WideStringView lhs, const WideString& rhs) {
   return rhs == lhs;
 }
 inline bool operator!=(const wchar_t* lhs, const WideString& rhs) {
   return rhs != lhs;
 }
-inline bool operator!=(const WideStringView& lhs, const WideString& rhs) {
+inline bool operator!=(WideStringView lhs, const WideString& rhs) {
   return rhs != lhs;
 }
 inline bool operator<(const wchar_t* lhs, const WideString& rhs) {
@@ -262,14 +288,14 @@
 
 std::wostream& operator<<(std::wostream& os, const WideString& str);
 std::ostream& operator<<(std::ostream& os, const WideString& str);
-std::wostream& operator<<(std::wostream& os, const WideStringView& str);
-std::ostream& operator<<(std::ostream& os, const WideStringView& str);
+std::wostream& operator<<(std::wostream& os, WideStringView str);
+std::ostream& operator<<(std::ostream& os, WideStringView str);
 
 }  // namespace fxcrt
 
 using WideString = fxcrt::WideString;
 
-uint32_t FX_HashCode_GetW(const WideStringView& str, bool bIgnoreCase);
+uint32_t FX_HashCode_GetW(WideStringView str, bool bIgnoreCase);
 
 namespace std {
 
diff --git a/core/fxcrt/widestring_unittest.cpp b/core/fxcrt/widestring_unittest.cpp
index 2fb9e8c..2445a47 100644
--- a/core/fxcrt/widestring_unittest.cpp
+++ b/core/fxcrt/widestring_unittest.cpp
@@ -5,10 +5,14 @@
 #include "core/fxcrt/widestring.h"
 
 #include <algorithm>
+#include <iterator>
 #include <vector>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/span.h"
+#include "third_party/base/stl_util.h"
 
 namespace fxcrt {
 
@@ -21,6 +25,10 @@
   EXPECT_DEATH({ abc[4]; }, ".*");
 #endif
 
+  pdfium::span<const wchar_t> abc_span = abc.span();
+  EXPECT_EQ(3u, abc_span.size());
+  EXPECT_EQ(0, wmemcmp(abc_span.data(), L"abc", 3));
+
   WideString mutable_abc = abc;
   EXPECT_EQ(abc.c_str(), mutable_abc.c_str());
   EXPECT_EQ(L'a', mutable_abc[0]);
@@ -49,18 +57,75 @@
 #endif
 }
 
+TEST(WideString, Construct) {
+  {
+    // Copy-construct.
+    WideString string1(L"abc");
+    WideString string2(string1);
+    EXPECT_EQ(L"abc", string1);
+    EXPECT_EQ(L"abc", string2);
+    EXPECT_EQ(2, string1.ReferenceCountForTesting());
+    EXPECT_EQ(2, string2.ReferenceCountForTesting());
+  }
+  {
+    // Move-construct.
+    WideString string1(L"abc");
+    WideString string2(std::move(string1));
+    EXPECT_TRUE(string1.IsEmpty());
+    EXPECT_EQ(L"abc", string2);
+    EXPECT_EQ(0, string1.ReferenceCountForTesting());
+    EXPECT_EQ(1, string2.ReferenceCountForTesting());
+  }
+}
+
+TEST(WideString, Assign) {
+  {
+    // Copy-assign.
+    WideString string1;
+    EXPECT_EQ(0, string1.ReferenceCountForTesting());
+    {
+      WideString string2(L"abc");
+      EXPECT_EQ(1, string2.ReferenceCountForTesting());
+
+      string1 = string2;
+      EXPECT_EQ(2, string1.ReferenceCountForTesting());
+      EXPECT_EQ(2, string2.ReferenceCountForTesting());
+    }
+    EXPECT_EQ(1, string1.ReferenceCountForTesting());
+  }
+  {
+    // Move-assign.
+    WideString string1;
+    EXPECT_EQ(0, string1.ReferenceCountForTesting());
+    {
+      WideString string2(L"abc");
+      EXPECT_EQ(1, string2.ReferenceCountForTesting());
+
+      string1 = std::move(string2);
+      EXPECT_EQ(L"abc", string1);
+      EXPECT_TRUE(string2.IsEmpty());
+      EXPECT_EQ(1, string1.ReferenceCountForTesting());
+      EXPECT_EQ(0, string2.ReferenceCountForTesting());
+    }
+    EXPECT_EQ(1, string1.ReferenceCountForTesting());
+  }
+}
+
 TEST(WideString, OperatorLT) {
   WideString empty;
   WideString a(L"a");
+  WideString ab(L"ab");
   WideString abc(L"\x0110qq");  // Comes before despite endianness.
   WideString def(L"\x1001qq");  // Comes after despite endianness.
   WideStringView v_empty;
   WideStringView v_a(L"a");
+  WideStringView v_ab(L"ab");
   WideStringView v_abc(L"\x0110qq");
   WideStringView v_def(L"\x1001qq");
   const wchar_t* const c_null = nullptr;
   const wchar_t* const c_empty = L"";
   const wchar_t* const c_a = L"a";
+  const wchar_t* const c_ab = L"ab";
   const wchar_t* const c_abc = L"\x0110qq";
   const wchar_t* const c_def = L"\x1001qq";
 
@@ -142,6 +207,14 @@
   EXPECT_FALSE(def < c_abc);
   EXPECT_TRUE(abc < v_def);
   EXPECT_FALSE(def < v_abc);
+
+  EXPECT_TRUE(a < ab);
+  EXPECT_TRUE(a < c_ab);
+  EXPECT_TRUE(a < v_ab);
+  EXPECT_TRUE(c_a < ab);
+  EXPECT_TRUE(c_a < v_ab);
+  EXPECT_TRUE(v_a < c_ab);
+  EXPECT_TRUE(v_a < v_ab);
 }
 
 TEST(WideString, OperatorEQ) {
@@ -348,6 +421,31 @@
   EXPECT_TRUE(c_string3 != wide_string);
 }
 
+TEST(WideString, OperatorPlus) {
+  EXPECT_EQ(L"I like dogs", L"I like " + WideString(L"dogs"));
+  EXPECT_EQ(L"Dogs like me", WideString(L"Dogs") + L" like me");
+  EXPECT_EQ(L"Oh no, error number 42",
+            L"Oh no, error number " + WideString::Format(L"%d", 42));
+
+  {
+    // Make sure operator+= and Concat() increases string memory allocation
+    // geometrically.
+    int allocations = 0;
+    WideString str(L"ABCDEFGHIJKLMN");
+    const wchar_t* buffer = str.c_str();
+    for (size_t i = 0; i < 10000; ++i) {
+      str += L"!";
+      const wchar_t* new_buffer = str.c_str();
+      if (new_buffer != buffer) {
+        buffer = new_buffer;
+        ++allocations;
+      }
+    }
+    EXPECT_LT(allocations, 25);
+    EXPECT_GT(allocations, 10);
+  }
+}
+
 TEST(WideString, ConcatInPlace) {
   WideString fred;
   fred.Concat(L"FRED", 4);
@@ -513,57 +611,57 @@
   EXPECT_EQ(L"", empty);
 }
 
-TEST(WideString, Mid) {
+TEST(WideString, Substr) {
   WideString fred(L"FRED");
-  EXPECT_EQ(L"", fred.Mid(0, 0));
-  EXPECT_EQ(L"", fred.Mid(3, 0));
-  EXPECT_EQ(L"FRED", fred.Mid(0, 4));
-  EXPECT_EQ(L"RED", fred.Mid(1, 3));
-  EXPECT_EQ(L"ED", fred.Mid(2, 2));
-  EXPECT_EQ(L"D", fred.Mid(3, 1));
-  EXPECT_EQ(L"F", fred.Mid(0, 1));
-  EXPECT_EQ(L"R", fred.Mid(1, 1));
-  EXPECT_EQ(L"E", fred.Mid(2, 1));
-  EXPECT_EQ(L"D", fred.Mid(3, 1));
-  EXPECT_EQ(L"FR", fred.Mid(0, 2));
-  EXPECT_EQ(L"FRED", fred.Mid(0, 4));
-  EXPECT_EQ(L"", fred.Mid(0, 10));
+  EXPECT_EQ(L"", fred.Substr(0, 0));
+  EXPECT_EQ(L"", fred.Substr(3, 0));
+  EXPECT_EQ(L"FRED", fred.Substr(0, 4));
+  EXPECT_EQ(L"RED", fred.Substr(1, 3));
+  EXPECT_EQ(L"ED", fred.Substr(2, 2));
+  EXPECT_EQ(L"D", fred.Substr(3, 1));
+  EXPECT_EQ(L"F", fred.Substr(0, 1));
+  EXPECT_EQ(L"R", fred.Substr(1, 1));
+  EXPECT_EQ(L"E", fred.Substr(2, 1));
+  EXPECT_EQ(L"D", fred.Substr(3, 1));
+  EXPECT_EQ(L"FR", fred.Substr(0, 2));
+  EXPECT_EQ(L"FRED", fred.Substr(0, 4));
+  EXPECT_EQ(L"", fred.Substr(0, 10));
 
-  EXPECT_EQ(L"", fred.Mid(1, 4));
-  EXPECT_EQ(L"", fred.Mid(4, 1));
+  EXPECT_EQ(L"", fred.Substr(1, 4));
+  EXPECT_EQ(L"", fred.Substr(4, 1));
 
   WideString empty;
-  EXPECT_EQ(L"", empty.Mid(0, 0));
+  EXPECT_EQ(L"", empty.Substr(0, 0));
 }
 
-TEST(WideString, Left) {
+TEST(WideString, First) {
   WideString fred(L"FRED");
-  EXPECT_EQ(L"", fred.Left(0));
-  EXPECT_EQ(L"F", fred.Left(1));
-  EXPECT_EQ(L"FR", fred.Left(2));
-  EXPECT_EQ(L"FRE", fred.Left(3));
-  EXPECT_EQ(L"FRED", fred.Left(4));
+  EXPECT_EQ(L"", fred.First(0));
+  EXPECT_EQ(L"F", fred.First(1));
+  EXPECT_EQ(L"FR", fred.First(2));
+  EXPECT_EQ(L"FRE", fred.First(3));
+  EXPECT_EQ(L"FRED", fred.First(4));
 
-  EXPECT_EQ(L"", fred.Left(5));
+  EXPECT_EQ(L"", fred.First(5));
 
   WideString empty;
-  EXPECT_EQ(L"", empty.Left(0));
-  EXPECT_EQ(L"", empty.Left(1));
+  EXPECT_EQ(L"", empty.First(0));
+  EXPECT_EQ(L"", empty.First(1));
 }
 
-TEST(WideString, Right) {
+TEST(WideString, Last) {
   WideString fred(L"FRED");
-  EXPECT_EQ(L"", fred.Right(0));
-  EXPECT_EQ(L"D", fred.Right(1));
-  EXPECT_EQ(L"ED", fred.Right(2));
-  EXPECT_EQ(L"RED", fred.Right(3));
-  EXPECT_EQ(L"FRED", fred.Right(4));
+  EXPECT_EQ(L"", fred.Last(0));
+  EXPECT_EQ(L"D", fred.Last(1));
+  EXPECT_EQ(L"ED", fred.Last(2));
+  EXPECT_EQ(L"RED", fred.Last(3));
+  EXPECT_EQ(L"FRED", fred.Last(4));
 
-  EXPECT_EQ(L"", fred.Right(5));
+  EXPECT_EQ(L"", fred.Last(5));
 
   WideString empty;
-  EXPECT_EQ(L"", empty.Right(0));
-  EXPECT_EQ(L"", empty.Right(1));
+  EXPECT_EQ(L"", empty.Last(0));
+  EXPECT_EQ(L"", empty.Last(1));
 }
 
 TEST(WideString, Find) {
@@ -614,6 +712,40 @@
   EXPECT_EQ(2u, result.value());
 }
 
+TEST(WideString, ReverseFind) {
+  WideString null_string;
+  EXPECT_FALSE(null_string.ReverseFind(L'a').has_value());
+  EXPECT_FALSE(null_string.ReverseFind(L'\0').has_value());
+
+  WideString empty_string(L"");
+  EXPECT_FALSE(empty_string.ReverseFind(L'a').has_value());
+  EXPECT_FALSE(empty_string.ReverseFind(L'\0').has_value());
+
+  Optional<size_t> result;
+  WideString single_string(L"a");
+  result = single_string.ReverseFind(L'a');
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(0u, result.value());
+  EXPECT_FALSE(single_string.ReverseFind(L'b').has_value());
+  EXPECT_FALSE(single_string.ReverseFind(L'\0').has_value());
+
+  WideString longer_string(L"abccc");
+  result = longer_string.ReverseFind(L'a');
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(0u, result.value());
+  result = longer_string.ReverseFind(L'c');
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(4u, result.value());
+  EXPECT_FALSE(longer_string.ReverseFind(L'\0').has_value());
+
+  WideString hibyte_string(
+      L"ab\xff8c"
+      L"def");
+  result = hibyte_string.ReverseFind(L'\xff8c');
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(2u, result.value());
+}
+
 TEST(WideString, UpperLower) {
   WideString fred(L"F-Re.42D");
   fred.MakeLower();
@@ -802,20 +934,21 @@
 }
 
 TEST(WideString, GetBuffer) {
+  WideString str1;
   {
-    WideString str;
-    wchar_t* buffer = str.GetBuffer(12);
-    wcscpy(buffer, L"clams");
-    str.ReleaseBuffer(str.GetStringLength());
-    EXPECT_EQ(L"clams", str);
+    pdfium::span<wchar_t> buffer = str1.GetBuffer(12);
+    wcscpy(buffer.data(), L"clams");
   }
+  str1.ReleaseBuffer(str1.GetStringLength());
+  EXPECT_EQ(L"clams", str1);
+
+  WideString str2(L"cl");
   {
-    WideString str(L"cl");
-    wchar_t* buffer = str.GetBuffer(12);
-    wcscpy(buffer + 2, L"ams");
-    str.ReleaseBuffer(str.GetStringLength());
-    EXPECT_EQ(L"clams", str);
+    pdfium::span<wchar_t> buffer = str2.GetBuffer(12);
+    wcscpy(buffer.data() + 2, L"ams");
   }
+  str2.ReleaseBuffer(str2.GetStringLength());
+  EXPECT_EQ(L"clams", str2);
 }
 
 TEST(WideString, ReleaseBuffer) {
@@ -882,49 +1015,67 @@
 TEST(WideString, MultiCharReverseIterator) {
   WideString multi_str(L"abcd");
   auto iter = multi_str.rbegin();
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(4, multi_str.rend() - iter);
+  EXPECT_EQ(0, iter - multi_str.rbegin());
 
   char ch = *iter++;
   EXPECT_EQ('d', ch);
   EXPECT_EQ('c', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(3, multi_str.rend() - iter);
+  EXPECT_EQ(1, iter - multi_str.rbegin());
 
   ch = *(++iter);
   EXPECT_EQ('b', ch);
   EXPECT_EQ('b', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(2, multi_str.rend() - iter);
+  EXPECT_EQ(2, iter - multi_str.rbegin());
 
   ch = *iter++;
   EXPECT_EQ('b', ch);
   EXPECT_EQ('a', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(1, multi_str.rend() - iter);
+  EXPECT_EQ(3, iter - multi_str.rbegin());
 
   ch = *iter++;
   EXPECT_EQ('a', ch);
-  EXPECT_TRUE(iter == multi_str.rend());
+  EXPECT_EQ(iter, multi_str.rend());
+  EXPECT_EQ(0, multi_str.rend() - iter);
+  EXPECT_EQ(4, iter - multi_str.rbegin());
 
   ch = *(--iter);
   EXPECT_EQ('a', ch);
   EXPECT_EQ('a', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(1, multi_str.rend() - iter);
+  EXPECT_EQ(3, iter - multi_str.rbegin());
 
   ch = *iter--;
   EXPECT_EQ('a', ch);
   EXPECT_EQ('b', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(2, multi_str.rend() - iter);
+  EXPECT_EQ(2, iter - multi_str.rbegin());
 
   ch = *iter--;
   EXPECT_EQ('b', ch);
   EXPECT_EQ('c', *iter);
-  EXPECT_FALSE(iter == multi_str.rend());
+  EXPECT_NE(iter, multi_str.rend());
+  EXPECT_EQ(3, multi_str.rend() - iter);
+  EXPECT_EQ(1, iter - multi_str.rbegin());
 
   ch = *(--iter);
   EXPECT_EQ('d', ch);
   EXPECT_EQ('d', *iter);
-  EXPECT_TRUE(iter == multi_str.rbegin());
+  EXPECT_EQ(iter, multi_str.rbegin());
+  EXPECT_EQ(4, multi_str.rend() - iter);
+  EXPECT_EQ(0, iter - multi_str.rbegin());
 }
 
-TEST(WideString, UTF16LE_Encode) {
+TEST(WideString, ToUTF16LE) {
   struct UTF16LEEncodeCase {
     WideString ws;
     ByteString bs;
@@ -939,11 +1090,144 @@
 
   for (size_t i = 0; i < FX_ArraySize(utf16le_encode_cases); ++i) {
     EXPECT_EQ(utf16le_encode_cases[i].bs,
-              utf16le_encode_cases[i].ws.UTF16LE_Encode())
+              utf16le_encode_cases[i].ws.ToUTF16LE())
         << " for case number " << i;
   }
 }
 
+TEST(WideString, IsASCII) {
+  EXPECT_TRUE(WideString(L"xy\u007fz").IsASCII());
+  EXPECT_FALSE(WideString(L"xy\u0080z").IsASCII());
+  EXPECT_FALSE(WideString(L"xy\u2041z").IsASCII());
+}
+
+TEST(WideString, EqualsASCII) {
+  EXPECT_TRUE(WideString(L"").EqualsASCII(""));
+  EXPECT_FALSE(WideString(L"A").EqualsASCII(""));
+  EXPECT_FALSE(WideString(L"").EqualsASCII("A"));
+  EXPECT_FALSE(WideString(L"A").EqualsASCII("B"));
+  EXPECT_TRUE(WideString(L"ABC").EqualsASCII("ABC"));
+  EXPECT_FALSE(WideString(L"ABC").EqualsASCII("AEC"));
+  EXPECT_FALSE(WideString(L"\u00c1").EqualsASCII("\x41"));
+  EXPECT_FALSE(WideString(L"\u0141").EqualsASCII("\x41"));
+}
+
+TEST(WideString, EqualsASCIINoCase) {
+  EXPECT_TRUE(WideString(L"").EqualsASCIINoCase(""));
+  EXPECT_FALSE(WideString(L"A").EqualsASCIINoCase("b"));
+  EXPECT_TRUE(WideString(L"AbC").EqualsASCIINoCase("aBc"));
+  EXPECT_FALSE(WideString(L"ABc").EqualsASCIINoCase("AeC"));
+  EXPECT_FALSE(WideString(L"\u00c1").EqualsASCIINoCase("\x41"));
+  EXPECT_FALSE(WideString(L"\u0141").EqualsASCIINoCase("\x41"));
+}
+
+TEST(WideString, ToASCII) {
+  const char* kResult =
+      "x"
+      "\x02"
+      "\x7f"
+      "\x22"
+      "\x0c"
+      "y";
+  EXPECT_EQ(kResult, WideString(L"x"
+                                L"\u0082"
+                                L"\u00ff"
+                                L"\u0122"
+                                L"\u208c"
+                                L"y")
+                         .ToASCII());
+}
+
+TEST(WideString, ToLatin1) {
+  const char* kResult =
+      "x"
+      "\x82"
+      "\xff"
+      "\x22"
+      "\x8c"
+      "y";
+  EXPECT_EQ(kResult, WideString(L"x"
+                                L"\u0082"
+                                L"\u00ff"
+                                L"\u0122"
+                                L"\u208c"
+                                L"y")
+                         .ToLatin1());
+}
+
+TEST(WideString, ToDefANSI) {
+  EXPECT_EQ("", WideString().ToDefANSI());
+#if defined(OS_WIN)
+  const char* kResult =
+      "x"
+      "?"
+      "\xff"
+      "A"
+      "?"
+      "y";
+#else
+  const char* kResult =
+      "x"
+      "\x80"
+      "\xff"
+      "y";
+#endif
+  EXPECT_EQ(kResult, WideString(L"x"
+                                L"\u0080"
+                                L"\u00ff"
+                                L"\u0100"
+                                L"\u208c"
+                                L"y")
+                         .ToDefANSI());
+}
+
+TEST(WideString, FromASCII) {
+  EXPECT_EQ(L"", WideString::FromASCII(ByteStringView()));
+  const wchar_t* kResult =
+      L"x"
+      L"\u0002"
+      L"\u007f"
+      L"y";
+  EXPECT_EQ(kResult, WideString::FromASCII("x"
+                                           "\x82"
+                                           "\xff"
+                                           "y"));
+}
+
+TEST(WideString, FromLatin1) {
+  EXPECT_EQ(L"", WideString::FromLatin1(ByteStringView()));
+  const wchar_t* kResult =
+      L"x"
+      L"\u0082"
+      L"\u00ff"
+      L"y";
+  EXPECT_EQ(kResult, WideString::FromLatin1("x"
+                                            "\x82"
+                                            "\xff"
+                                            "y"));
+}
+
+TEST(WideString, FromDefANSI) {
+  EXPECT_EQ(L"", WideString::FromDefANSI(ByteStringView()));
+#if defined(OS_WIN)
+  const wchar_t* kResult =
+      L"x"
+      L"\u20ac"
+      L"\u00ff"
+      L"y";
+#else
+  const wchar_t* kResult =
+      L"x"
+      L"\u0080"
+      L"\u00ff"
+      L"y";
+#endif
+  EXPECT_EQ(kResult, WideString::FromDefANSI("x"
+                                             "\x80"
+                                             "\xff"
+                                             "y"));
+}
+
 TEST(WideStringView, FromVector) {
   std::vector<WideStringView::UnsignedType> null_vec;
   WideStringView null_string(null_vec);
@@ -1333,12 +1617,34 @@
   EXPECT_NE(L"", WideString::Format(L"unsupported char '%c'", 0x00FF00FF));
 }
 
+TEST(WideString, FormatString) {
+  // %ls and wide characters are the reliable combination across platforms.
+  EXPECT_EQ(L"", WideString::Format(L"%ls", L""));
+  EXPECT_EQ(L"", WideString::Format(L"%ls", WideString().c_str()));
+  EXPECT_EQ(L"clams", WideString::Format(L"%ls", L"clams"));
+  EXPECT_EQ(L"cla", WideString::Format(L"%.3ls", L"clams"));
+  EXPECT_EQ(L"\u043e\u043f", WideString(L"\u043e\u043f"));
+
+#if !defined(OS_MACOSX)
+  // See https://bugs.chromium.org/p/pdfium/issues/detail?id=1132
+  EXPECT_EQ(L"\u043e\u043f", WideString::Format(L"\u043e\u043f"));
+  EXPECT_EQ(L"\u043e\u043f", WideString::Format(L"%ls", L"\u043e\u043f"));
+  EXPECT_EQ(L"\u043e", WideString::Format(L"%.1ls", L"\u043e\u043f"));
+#endif
+}
+
 TEST(WideString, Empty) {
   WideString empty_str;
   EXPECT_TRUE(empty_str.IsEmpty());
   EXPECT_EQ(0u, empty_str.GetLength());
+
   const wchar_t* cstr = empty_str.c_str();
+  EXPECT_NE(nullptr, cstr);
   EXPECT_EQ(0u, wcslen(cstr));
+
+  pdfium::span<const wchar_t> cspan = empty_str.span();
+  EXPECT_TRUE(cspan.empty());
+  EXPECT_EQ(nullptr, cspan.data());
 }
 
 TEST(CFX_WidString, InitializerList) {
@@ -1396,6 +1702,15 @@
   EXPECT_EQ(static_cast<int32_t>(L'a' + L'b' + L'c'), sum);
 }
 
+TEST(WideString, StdBegin) {
+  WideString one_str(L"abc");
+  std::vector<wchar_t> vec(std::begin(one_str), std::end(one_str));
+  ASSERT_EQ(3u, vec.size());
+  EXPECT_EQ(L'a', vec[0]);
+  EXPECT_EQ(L'b', vec[1]);
+  EXPECT_EQ(L'c', vec[2]);
+}
+
 TEST(WideString, AnyAllNoneOf) {
   WideString str(L"aaaaaaaaaaaaaaaaab");
   EXPECT_FALSE(std::all_of(str.begin(), str.end(),
@@ -1676,4 +1991,15 @@
   }
 }
 
+TEST(WideString, FX_HashCode_Wide) {
+  EXPECT_EQ(0u, FX_HashCode_GetW(L"", false));
+  EXPECT_EQ(65u, FX_HashCode_GetW(L"A", false));
+  EXPECT_EQ(97u, FX_HashCode_GetW(L"A", true));
+  EXPECT_EQ(1313 * 65u + 66u, FX_HashCode_GetW(L"AB", false));
+  EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff", false),
+            FX_HashCode_GetW(L"AB\xff", false));
+  EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff", true),
+            FX_HashCode_GetW(L"AB\xff", true));
+}
+
 }  // namespace fxcrt
diff --git a/core/fxcrt/xml/cfx_saxcontext.cpp b/core/fxcrt/xml/cfx_saxcontext.cpp
deleted file mode 100644
index 4e2f0c5..0000000
--- a/core/fxcrt/xml/cfx_saxcontext.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/xml/cfx_saxcontext.h"
-
-CFX_SAXContext::CFX_SAXContext() : m_eNode(CFX_SAXItem::Type::Unknown) {}
-
-CFX_SAXContext::~CFX_SAXContext() {}
diff --git a/core/fxcrt/xml/cfx_saxcontext.h b/core/fxcrt/xml/cfx_saxcontext.h
deleted file mode 100644
index d4d74a3..0000000
--- a/core/fxcrt/xml/cfx_saxcontext.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CFX_SAXCONTEXT_H_
-#define CORE_FXCRT_XML_CFX_SAXCONTEXT_H_
-
-#include <sstream>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/xml/cfx_saxreader.h"
-
-class CFX_SAXContext {
- public:
-  CFX_SAXContext();
-  ~CFX_SAXContext();
-
-  std::ostringstream m_TextBuf;
-  ByteString m_bsTagName;
-  CFX_SAXItem::Type m_eNode;
-};
-
-#endif  // CORE_FXCRT_XML_CFX_SAXCONTEXT_H_
diff --git a/core/fxcrt/xml/cfx_saxreader.cpp b/core/fxcrt/xml/cfx_saxreader.cpp
deleted file mode 100644
index 762144f..0000000
--- a/core/fxcrt/xml/cfx_saxreader.cpp
+++ /dev/null
@@ -1,744 +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/fxcrt/xml/cfx_saxreader.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/xml/cfx_saxreaderhandler.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-enum class CFX_SaxMode {
-  Text = 0,
-  NodeStart,
-  DeclOrComment,
-  DeclNode,
-  Comment,
-  CommentContent,
-  TagName,
-  TagAttributeName,
-  TagAttributeEqual,
-  TagAttributeValue,
-  TagMaybeClose,
-  TagClose,
-  TagEnd,
-  TargetData,
-};
-
-class CFX_SAXCommentContext {
- public:
-  CFX_SAXCommentContext() : m_iHeaderCount(0), m_iTailCount(0) {}
-  int32_t m_iHeaderCount;
-  int32_t m_iTailCount;
-};
-
-namespace {
-
-const uint32_t kSaxFileBufSize = 32768;
-
-}  // namespace
-
-CFX_SAXFile::CFX_SAXFile()
-    : m_dwStart(0),
-      m_dwEnd(0),
-      m_dwCur(0),
-      m_pBuf(nullptr),
-      m_dwBufSize(0),
-      m_dwBufIndex(0) {}
-
-CFX_SAXFile::~CFX_SAXFile() {}
-
-bool CFX_SAXFile::StartFile(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                            uint32_t dwStart,
-                            uint32_t dwLen) {
-  ASSERT(!m_pFile && pFile);
-  uint32_t dwSize = pFile->GetSize();
-  if (dwStart >= dwSize)
-    return false;
-
-  if (dwLen == static_cast<uint32_t>(-1) || dwStart + dwLen > dwSize)
-    dwLen = dwSize - dwStart;
-
-  if (dwLen == 0)
-    return false;
-
-  m_dwBufSize = std::min(dwLen, kSaxFileBufSize);
-  m_pBuf = FX_Alloc(uint8_t, m_dwBufSize);
-  if (!pFile->ReadBlock(m_pBuf, dwStart, m_dwBufSize))
-    return false;
-
-  m_dwStart = dwStart;
-  m_dwEnd = dwStart + dwLen;
-  m_dwCur = dwStart;
-  m_pFile = pFile;
-  m_dwBufIndex = 0;
-  return true;
-}
-
-bool CFX_SAXFile::ReadNextBlock() {
-  ASSERT(m_pFile);
-  uint32_t dwSize = m_dwEnd - m_dwCur;
-  if (dwSize == 0) {
-    return false;
-  }
-  m_dwBufSize = std::min(dwSize, kSaxFileBufSize);
-  if (!m_pFile->ReadBlock(m_pBuf, m_dwCur, m_dwBufSize)) {
-    return false;
-  }
-  m_dwBufIndex = 0;
-  return true;
-}
-
-void CFX_SAXFile::Reset() {
-  if (m_pBuf) {
-    FX_Free(m_pBuf);
-    m_pBuf = nullptr;
-  }
-  m_pFile = nullptr;
-}
-
-CFX_SAXReader::CFX_SAXReader()
-    : m_File(),
-      m_pHandler(nullptr),
-      m_iState(-1),
-      m_dwItemID(0),
-      m_dwParseMode(0) {
-  m_Data.reserve(256);
-  m_Name.reserve(256);
-}
-
-CFX_SAXReader::~CFX_SAXReader() {
-  Reset();
-}
-
-void CFX_SAXReader::Reset() {
-  m_File.Reset();
-  m_iState = -1;
-  m_Stack = std::stack<std::unique_ptr<CFX_SAXItem>>();
-  m_dwItemID = 0;
-  m_SkipStack = std::stack<char>();
-  m_SkipChar = 0;
-  m_pCommentContext.reset();
-  ClearData();
-  ClearName();
-}
-
-void CFX_SAXReader::Push() {
-  std::unique_ptr<CFX_SAXItem> pNew =
-      pdfium::MakeUnique<CFX_SAXItem>(++m_dwItemID);
-  if (!m_Stack.empty())
-    pNew->m_bSkip = m_Stack.top()->m_bSkip;
-  m_Stack.push(std::move(pNew));
-}
-
-void CFX_SAXReader::Pop() {
-  if (!m_Stack.empty())
-    m_Stack.pop();
-}
-
-CFX_SAXItem* CFX_SAXReader::GetCurrentItem() const {
-  return m_Stack.empty() ? nullptr : m_Stack.top().get();
-}
-
-void CFX_SAXReader::ClearData() {
-  m_Data.clear();
-  m_iEntityStart = -1;
-}
-
-void CFX_SAXReader::ClearName() {
-  m_Name.clear();
-}
-
-void CFX_SAXReader::AppendToData(uint8_t ch) {
-  m_Data.push_back(ch);
-}
-
-void CFX_SAXReader::AppendToName(uint8_t ch) {
-  m_Name.push_back(ch);
-}
-
-void CFX_SAXReader::BackUpAndReplaceDataAt(int32_t index, uint8_t ch) {
-  ASSERT(index > -1);
-  m_Data.erase(m_Data.begin() + index, m_Data.end());
-  AppendToData(ch);
-}
-
-int32_t CFX_SAXReader::CurrentDataIndex() const {
-  return pdfium::CollectionSize<int32_t>(m_Data) - 1;
-}
-
-bool CFX_SAXReader::IsEntityStart(uint8_t ch) const {
-  return m_iEntityStart == -1 && ch == '&';
-}
-
-bool CFX_SAXReader::IsEntityEnd(uint8_t ch) const {
-  return m_iEntityStart != -1 && ch == ';';
-}
-
-bool CFX_SAXReader::SkipSpace(uint8_t ch) {
-  return (m_dwParseMode & CFX_SaxParseMode_NotSkipSpace) == 0 && ch < 0x21;
-}
-
-int32_t CFX_SAXReader::StartParse(
-    const RetainPtr<IFX_SeekableReadStream>& pFile,
-    uint32_t dwStart,
-    uint32_t dwLen,
-    uint32_t dwParseMode) {
-  Reset();
-  if (!m_File.StartFile(pFile, dwStart, dwLen))
-    return -1;
-
-  m_iState = 0;
-  m_eMode = CFX_SaxMode::Text;
-  m_ePrevMode = CFX_SaxMode::Text;
-  m_bCharData = false;
-  m_dwDataOffset = 0;
-  m_dwParseMode = dwParseMode;
-  m_Stack.push(pdfium::MakeUnique<CFX_SAXItem>(++m_dwItemID));
-  return 0;
-}
-
-int32_t CFX_SAXReader::ContinueParse() {
-  if (m_iState < 0 || m_iState > 99)
-    return m_iState;
-
-  while (m_File.m_dwCur < m_File.m_dwEnd) {
-    uint32_t& index = m_File.m_dwBufIndex;
-    uint32_t size = m_File.m_dwBufSize;
-    const uint8_t* pBuf = m_File.m_pBuf;
-    while (index < size) {
-      m_CurByte = pBuf[index];
-      ParseInternal();
-      index++;
-    }
-    m_File.m_dwCur += index;
-    m_iState = (m_File.m_dwCur - m_File.m_dwStart) * 100 /
-               (m_File.m_dwEnd - m_File.m_dwStart);
-    if (m_File.m_dwCur >= m_File.m_dwEnd)
-      break;
-    if (!m_File.ReadNextBlock()) {
-      m_iState = -2;
-      break;
-    }
-    m_dwDataOffset = 0;
-  }
-  return m_iState;
-}
-
-void CFX_SAXReader::ParseInternal() {
-  switch (m_eMode) {
-    case CFX_SaxMode::Text:
-      ParseText();
-      break;
-    case CFX_SaxMode::NodeStart:
-      ParseNodeStart();
-      break;
-    case CFX_SaxMode::DeclOrComment:
-      ParseDeclOrComment();
-      break;
-    case CFX_SaxMode::DeclNode:
-      ParseDeclNode();
-      break;
-    case CFX_SaxMode::Comment:
-      ParseComment();
-      break;
-    case CFX_SaxMode::CommentContent:
-      ParseCommentContent();
-      break;
-    case CFX_SaxMode::TagName:
-      ParseTagName();
-      break;
-    case CFX_SaxMode::TagAttributeName:
-      ParseTagAttributeName();
-      break;
-    case CFX_SaxMode::TagAttributeEqual:
-      ParseTagAttributeEqual();
-      break;
-    case CFX_SaxMode::TagAttributeValue:
-      ParseTagAttributeValue();
-      break;
-    case CFX_SaxMode::TagMaybeClose:
-      ParseMaybeClose();
-      break;
-    case CFX_SaxMode::TagClose:
-      ParseTagClose();
-      break;
-    case CFX_SaxMode::TagEnd:
-      ParseTagEnd();
-      break;
-    case CFX_SaxMode::TargetData:
-      ParseTargetData();
-      break;
-  }
-}
-
-void CFX_SAXReader::ParseChar(uint8_t ch) {
-  AppendToData(ch);
-  if (IsEntityStart(ch)) {
-    m_iEntityStart = CurrentDataIndex();
-    return;
-  }
-  if (!IsEntityEnd(ch))
-    return;
-
-  // No matter what, we're no longer in an entity.
-  ASSERT(m_iEntityStart > -1);
-  int32_t iSaveStart = m_iEntityStart;
-  m_iEntityStart = -1;
-
-  // NOTE: Relies on negative lengths being treated as empty strings.
-  ByteString csEntity(m_Data.data() + iSaveStart + 1,
-                      CurrentDataIndex() - iSaveStart - 1);
-  int32_t iLen = csEntity.GetLength();
-  if (iLen == 0)
-    return;
-
-  if (csEntity[0] == '#') {
-    if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_sharp) == 0) {
-      ch = 0;
-      uint8_t w;
-      if (iLen > 1 && csEntity[1] == 'x') {
-        for (int32_t i = 2; i < iLen; i++) {
-          w = csEntity[i];
-          if (w >= '0' && w <= '9')
-            ch = (ch << 4) + w - '0';
-          else if (w >= 'A' && w <= 'F')
-            ch = (ch << 4) + w - 55;
-          else if (w >= 'a' && w <= 'f')
-            ch = (ch << 4) + w - 87;
-          else
-            break;
-        }
-      } else {
-        for (int32_t i = 1; i < iLen; i++) {
-          w = csEntity[i];
-          if (w < '0' || w > '9')
-            break;
-          ch = ch * 10 + w - '0';
-        }
-      }
-      if (ch != 0)
-        BackUpAndReplaceDataAt(iSaveStart, ch);
-    }
-    return;
-  }
-  if (csEntity == "amp") {
-    if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_amp) == 0)
-      BackUpAndReplaceDataAt(iSaveStart, '&');
-    return;
-  }
-  if (csEntity == "lt") {
-    if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_lt) == 0)
-      BackUpAndReplaceDataAt(iSaveStart, '<');
-    return;
-  }
-  if (csEntity == "gt") {
-    if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_gt) == 0)
-      BackUpAndReplaceDataAt(iSaveStart, '>');
-    return;
-  }
-  if (csEntity == "apos") {
-    if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_apos) == 0)
-      BackUpAndReplaceDataAt(iSaveStart, '\'');
-    return;
-  }
-  if (csEntity == "quot") {
-    if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_quot) == 0)
-      BackUpAndReplaceDataAt(iSaveStart, '\"');
-    return;
-  }
-}
-
-void CFX_SAXReader::ParseText() {
-  if (m_CurByte == '<') {
-    if (!m_Data.empty()) {
-      NotifyData();
-      ClearData();
-    }
-    Push();
-    m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
-    m_eMode = CFX_SaxMode::NodeStart;
-    return;
-  }
-  if (m_Data.empty() && SkipSpace(m_CurByte))
-    return;
-
-  ParseChar(m_CurByte);
-}
-
-void CFX_SAXReader::ParseNodeStart() {
-  if (m_CurByte == '?') {
-    GetCurrentItem()->m_eNode = CFX_SAXItem::Type::Instruction;
-    m_eMode = CFX_SaxMode::TagName;
-    return;
-  }
-  if (m_CurByte == '!') {
-    m_eMode = CFX_SaxMode::DeclOrComment;
-    return;
-  }
-  if (m_CurByte == '/') {
-    m_eMode = CFX_SaxMode::TagEnd;
-    return;
-  }
-  if (m_CurByte == '>') {
-    Pop();
-    m_eMode = CFX_SaxMode::Text;
-    return;
-  }
-  if (m_CurByte > 0x20) {
-    m_dwDataOffset = m_File.m_dwBufIndex;
-    GetCurrentItem()->m_eNode = CFX_SAXItem::Type::Tag;
-    m_eMode = CFX_SaxMode::TagName;
-    AppendToData(m_CurByte);
-  }
-}
-
-void CFX_SAXReader::ParseDeclOrComment() {
-  if (m_CurByte == '-') {
-    m_eMode = CFX_SaxMode::Comment;
-    GetCurrentItem()->m_eNode = CFX_SAXItem::Type::Comment;
-    if (!m_pCommentContext)
-      m_pCommentContext = pdfium::MakeUnique<CFX_SAXCommentContext>();
-    m_pCommentContext->m_iHeaderCount = 1;
-    m_pCommentContext->m_iTailCount = 0;
-    return;
-  }
-  m_eMode = CFX_SaxMode::DeclNode;
-  m_dwDataOffset = m_File.m_dwBufIndex;
-  m_SkipChar = '>';
-  m_SkipStack.push('>');
-  SkipNode();
-}
-
-void CFX_SAXReader::ParseComment() {
-  m_pCommentContext->m_iHeaderCount = 2;
-  m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
-  m_eMode = CFX_SaxMode::CommentContent;
-}
-
-void CFX_SAXReader::ParseCommentContent() {
-  if (m_CurByte == '-') {
-    m_pCommentContext->m_iTailCount++;
-    return;
-  }
-  if (m_CurByte == '>' && m_pCommentContext->m_iTailCount == 2) {
-    NotifyTargetData();
-    ClearData();
-    Pop();
-    m_eMode = CFX_SaxMode::Text;
-    return;
-  }
-  while (m_pCommentContext->m_iTailCount > 0) {
-    AppendToData('-');
-    m_pCommentContext->m_iTailCount--;
-  }
-  AppendToData(m_CurByte);
-}
-
-void CFX_SAXReader::ParseDeclNode() {
-  SkipNode();
-}
-
-void CFX_SAXReader::ParseTagName() {
-  if (m_CurByte < 0x21 || m_CurByte == '/' || m_CurByte == '>' ||
-      m_CurByte == '?') {
-    NotifyEnter();
-    ClearData();
-    if (m_CurByte < 0x21) {
-      ClearName();
-      m_eMode = CFX_SaxMode::TagAttributeName;
-    } else if (m_CurByte == '/' || m_CurByte == '?') {
-      m_ePrevMode = m_eMode;
-      m_eMode = CFX_SaxMode::TagMaybeClose;
-    } else {
-      NotifyBreak();
-      m_eMode = CFX_SaxMode::Text;
-    }
-  } else {
-    AppendToData(m_CurByte);
-  }
-}
-
-void CFX_SAXReader::ParseTagAttributeName() {
-  if (m_CurByte < 0x21 || m_CurByte == '=') {
-    if (m_Name.empty() && m_CurByte < 0x21)
-      return;
-
-    m_SkipChar = 0;
-    m_eMode = m_CurByte == '=' ? CFX_SaxMode::TagAttributeValue
-                               : CFX_SaxMode::TagAttributeEqual;
-    ClearData();
-    return;
-  }
-  if (m_CurByte == '/' || m_CurByte == '>' || m_CurByte == '?') {
-    if (m_CurByte == '/' || m_CurByte == '?') {
-      m_ePrevMode = m_eMode;
-      m_eMode = CFX_SaxMode::TagMaybeClose;
-    } else {
-      NotifyBreak();
-      m_eMode = CFX_SaxMode::Text;
-    }
-    return;
-  }
-  if (m_Name.empty())
-    m_dwDataOffset = m_File.m_dwBufIndex;
-  AppendToName(m_CurByte);
-}
-
-void CFX_SAXReader::ParseTagAttributeEqual() {
-  if (m_CurByte == '=') {
-    m_SkipChar = 0;
-    m_eMode = CFX_SaxMode::TagAttributeValue;
-    return;
-  }
-  if (GetCurrentItem()->m_eNode == CFX_SAXItem::Type::Instruction) {
-    AppendToName(0x20);
-    m_eMode = CFX_SaxMode::TargetData;
-    ParseTargetData();
-  }
-}
-
-void CFX_SAXReader::ParseTagAttributeValue() {
-  if (m_SkipChar) {
-    if (m_SkipChar == m_CurByte) {
-      NotifyAttribute();
-      ClearData();
-      ClearName();
-      m_SkipChar = 0;
-      m_eMode = CFX_SaxMode::TagAttributeName;
-      return;
-    }
-    ParseChar(m_CurByte);
-    return;
-  }
-  if (m_CurByte < 0x21) {
-    return;
-  }
-  if (m_Data.empty()) {
-    if (m_CurByte == '\'' || m_CurByte == '\"')
-      m_SkipChar = m_CurByte;
-  }
-}
-
-void CFX_SAXReader::ParseMaybeClose() {
-  if (m_CurByte == '>') {
-    if (GetCurrentItem()->m_eNode == CFX_SAXItem::Type::Instruction) {
-      NotifyTargetData();
-      ClearData();
-      ClearName();
-    }
-    ParseTagClose();
-    m_eMode = CFX_SaxMode::Text;
-  } else if (m_ePrevMode == CFX_SaxMode::TagName) {
-    AppendToData('/');
-    m_eMode = CFX_SaxMode::TagName;
-    m_ePrevMode = CFX_SaxMode::Text;
-    ParseTagName();
-  } else if (m_ePrevMode == CFX_SaxMode::TagAttributeName) {
-    AppendToName('/');
-    m_eMode = CFX_SaxMode::TagAttributeName;
-    m_ePrevMode = CFX_SaxMode::Text;
-    ParseTagAttributeName();
-  } else if (m_ePrevMode == CFX_SaxMode::TargetData) {
-    AppendToName('?');
-    m_eMode = CFX_SaxMode::TargetData;
-    m_ePrevMode = CFX_SaxMode::Text;
-    ParseTargetData();
-  }
-}
-void CFX_SAXReader::ParseTagClose() {
-  m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
-  NotifyClose();
-  Pop();
-}
-void CFX_SAXReader::ParseTagEnd() {
-  if (m_CurByte < 0x21) {
-    return;
-  }
-  if (m_CurByte == '>') {
-    Pop();
-    m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
-    NotifyEnd();
-    ClearData();
-    Pop();
-    m_eMode = CFX_SaxMode::Text;
-  } else {
-    ParseChar(m_CurByte);
-  }
-}
-void CFX_SAXReader::ParseTargetData() {
-  if (m_CurByte == '?') {
-    m_ePrevMode = m_eMode;
-    m_eMode = CFX_SaxMode::TagMaybeClose;
-  } else {
-    AppendToName(m_CurByte);
-  }
-}
-void CFX_SAXReader::SkipNode() {
-  if (m_SkipChar == '\'' || m_SkipChar == '\"') {
-    if (m_CurByte != m_SkipChar)
-      return;
-
-    ASSERT(!m_SkipStack.empty());
-    m_SkipStack.pop();
-    m_SkipChar = !m_SkipStack.empty() ? m_SkipStack.top() : 0;
-    return;
-  }
-  switch (m_CurByte) {
-    case '<':
-      m_SkipChar = '>';
-      m_SkipStack.push('>');
-      break;
-    case '[':
-      m_SkipChar = ']';
-      m_SkipStack.push(']');
-      break;
-    case '(':
-      m_SkipChar = ')';
-      m_SkipStack.push(')');
-      break;
-    case '\'':
-      m_SkipChar = '\'';
-      m_SkipStack.push('\'');
-      break;
-    case '\"':
-      m_SkipChar = '\"';
-      m_SkipStack.push('\"');
-      break;
-    default:
-      if (m_CurByte == m_SkipChar) {
-        m_SkipStack.pop();
-        m_SkipChar = !m_SkipStack.empty() ? m_SkipStack.top() : 0;
-        if (m_SkipStack.empty() && m_CurByte == '>') {
-          if (m_Data.size() >= 9 && memcmp(m_Data.data(), "[CDATA[", 7) == 0 &&
-              memcmp(m_Data.data() + m_Data.size() - 2, "]]", 2) == 0) {
-            Pop();
-            m_Data.erase(m_Data.begin(), m_Data.begin() + 7);
-            m_Data.erase(m_Data.end() - 2, m_Data.end());
-            m_bCharData = true;
-            NotifyData();
-            m_bCharData = false;
-          } else {
-            Pop();
-          }
-          ClearData();
-          m_eMode = CFX_SaxMode::Text;
-        }
-      }
-      break;
-  }
-  if (!m_SkipStack.empty())
-    ParseChar(m_CurByte);
-}
-
-void CFX_SAXReader::NotifyData() {
-  if (!m_pHandler)
-    return;
-
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (!pItem)
-    return;
-
-  if (pItem->m_eNode == CFX_SAXItem::Type::Tag)
-    m_pHandler->OnTagData(
-        pItem->m_pNode,
-        m_bCharData ? CFX_SAXItem::Type::CharData : CFX_SAXItem::Type::Text,
-        ByteStringView(m_Data), m_File.m_dwCur + m_dwDataOffset);
-}
-
-void CFX_SAXReader::NotifyEnter() {
-  if (!m_pHandler)
-    return;
-
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (!pItem)
-    return;
-
-  if (pItem->m_eNode == CFX_SAXItem::Type::Tag ||
-      pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
-    pItem->m_pNode = m_pHandler->OnTagEnter(ByteStringView(m_Data),
-                                            pItem->m_eNode, m_dwNodePos);
-  }
-}
-
-void CFX_SAXReader::NotifyAttribute() {
-  if (!m_pHandler)
-    return;
-
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (!pItem)
-    return;
-
-  if (pItem->m_eNode == CFX_SAXItem::Type::Tag ||
-      pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
-    m_pHandler->OnTagAttribute(pItem->m_pNode, ByteStringView(m_Name),
-                               ByteStringView(m_Data));
-  }
-}
-
-void CFX_SAXReader::NotifyBreak() {
-  if (!m_pHandler)
-    return;
-
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (!pItem)
-    return;
-
-  if (pItem->m_eNode == CFX_SAXItem::Type::Tag)
-    m_pHandler->OnTagBreak(pItem->m_pNode);
-}
-
-void CFX_SAXReader::NotifyClose() {
-  if (!m_pHandler)
-    return;
-
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (!pItem)
-    return;
-
-  if (pItem->m_eNode == CFX_SAXItem::Type::Tag ||
-      pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
-    m_pHandler->OnTagClose(pItem->m_pNode, m_dwNodePos);
-  }
-}
-
-void CFX_SAXReader::NotifyEnd() {
-  if (!m_pHandler)
-    return;
-
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (!pItem)
-    return;
-
-  if (pItem->m_eNode == CFX_SAXItem::Type::Tag)
-    m_pHandler->OnTagEnd(pItem->m_pNode, ByteStringView(m_Data), m_dwNodePos);
-}
-
-void CFX_SAXReader::NotifyTargetData() {
-  if (!m_pHandler)
-    return;
-
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (!pItem)
-    return;
-
-  if (pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
-    m_pHandler->OnTargetData(pItem->m_pNode, pItem->m_eNode,
-                             ByteStringView(m_Name), m_dwNodePos);
-  } else if (pItem->m_eNode == CFX_SAXItem::Type::Comment) {
-    m_pHandler->OnTargetData(pItem->m_pNode, pItem->m_eNode,
-                             ByteStringView(m_Data), m_dwNodePos);
-  }
-}
-
-void CFX_SAXReader::SkipCurrentNode() {
-  CFX_SAXItem* pItem = GetCurrentItem();
-  if (pItem)
-    pItem->m_bSkip = true;
-}
diff --git a/core/fxcrt/xml/cfx_saxreader.h b/core/fxcrt/xml/cfx_saxreader.h
deleted file mode 100644
index 9ff755d..0000000
--- a/core/fxcrt/xml/cfx_saxreader.h
+++ /dev/null
@@ -1,174 +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_FXCRT_XML_CFX_SAXREADER_H_
-#define CORE_FXCRT_XML_CFX_SAXREADER_H_
-
-#include <memory>
-#include <stack>
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CFX_SAXCommentContext;
-class CFX_SAXContext;
-class IFX_SeekableReadStream;
-enum class CFX_SaxMode;
-
-class CFX_SAXItem {
- public:
-  enum class Type {
-    Unknown = 0,
-    Instruction,
-    Declaration,
-    Comment,
-    Tag,
-    Text,
-    CharData,
-  };
-
-  explicit CFX_SAXItem(uint32_t id)
-      : m_pNode(nullptr), m_eNode(Type::Unknown), m_dwID(id), m_bSkip(false) {}
-
-  CFX_SAXContext* m_pNode;
-  Type m_eNode;
-  const uint32_t m_dwID;
-  bool m_bSkip;
-};
-
-class CFX_SAXFile {
- public:
-  CFX_SAXFile();
-  ~CFX_SAXFile();
-
-  bool StartFile(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                 uint32_t dwStart,
-                 uint32_t dwLen);
-  bool ReadNextBlock();
-  void Reset();
-
-  RetainPtr<IFX_SeekableReadStream> m_pFile;
-  uint32_t m_dwStart;
-  uint32_t m_dwEnd;
-  uint32_t m_dwCur;
-  uint8_t* m_pBuf;
-  uint32_t m_dwBufSize;
-  uint32_t m_dwBufIndex;
-};
-
-enum CFX_SaxParseMode {
-  CFX_SaxParseMode_NotConvert_amp = 1 << 0,
-  CFX_SaxParseMode_NotConvert_lt = 1 << 1,
-  CFX_SaxParseMode_NotConvert_gt = 1 << 2,
-  CFX_SaxParseMode_NotConvert_apos = 1 << 3,
-  CFX_SaxParseMode_NotConvert_quot = 1 << 4,
-  CFX_SaxParseMode_NotConvert_sharp = 1 << 5,
-  CFX_SaxParseMode_NotSkipSpace = 1 << 6
-};
-
-class CFX_SAXReader {
- public:
-  class HandlerIface {
-   public:
-    virtual ~HandlerIface() {}
-    virtual CFX_SAXContext* OnTagEnter(const ByteStringView& bsTagName,
-                                       CFX_SAXItem::Type eType,
-                                       uint32_t dwStartPos) = 0;
-    virtual void OnTagAttribute(CFX_SAXContext* pTag,
-                                const ByteStringView& bsAttri,
-                                const ByteStringView& bsValue) = 0;
-    virtual void OnTagBreak(CFX_SAXContext* pTag) = 0;
-    virtual void OnTagData(CFX_SAXContext* pTag,
-                           CFX_SAXItem::Type eType,
-                           const ByteStringView& bsData,
-                           uint32_t dwStartPos) = 0;
-    virtual void OnTagClose(CFX_SAXContext* pTag, uint32_t dwEndPos) = 0;
-    virtual void OnTagEnd(CFX_SAXContext* pTag,
-                          const ByteStringView& bsTagName,
-                          uint32_t dwEndPos) = 0;
-    virtual void OnTargetData(CFX_SAXContext* pTag,
-                              CFX_SAXItem::Type eType,
-                              const ByteStringView& bsData,
-                              uint32_t dwStartPos) = 0;
-  };
-
-  CFX_SAXReader();
-  ~CFX_SAXReader();
-
-  int32_t StartParse(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                     uint32_t dwStart = 0,
-                     uint32_t dwLen = -1,
-                     uint32_t dwParseMode = 0);
-  int32_t ContinueParse();
-  void SetHandler(HandlerIface* pHandler) { m_pHandler = pHandler; }
-
- private:
-  void ParseInternal();
-  void SkipCurrentNode();
-  void AppendData(uint8_t ch);
-  void AppendName(uint8_t ch);
-  void ParseText();
-  void ParseNodeStart();
-  void ParseInstruction();
-  void ParseDeclOrComment();
-  void ParseDeclNode();
-  void ParseComment();
-  void ParseCommentContent();
-  void ParseTagName();
-  void ParseTagAttributeName();
-  void ParseTagAttributeEqual();
-  void ParseTagAttributeValue();
-  void ParseMaybeClose();
-  void ParseTagClose();
-  void ParseTagEnd();
-  void ParseTargetData();
-  void Reset();
-  void ClearData();
-  void ClearName();
-  void AppendToData(uint8_t ch);
-  void AppendToName(uint8_t ch);
-  void BackUpAndReplaceDataAt(int32_t index, uint8_t ch);
-  bool IsEntityStart(uint8_t ch) const;
-  bool IsEntityEnd(uint8_t ch) const;
-  int32_t CurrentDataIndex() const;
-  void Push();
-  void Pop();
-  CFX_SAXItem* GetCurrentItem() const;
-  bool SkipSpace(uint8_t ch);
-  void SkipNode();
-  void NotifyData();
-  void NotifyEnter();
-  void NotifyAttribute();
-  void NotifyBreak();
-  void NotifyClose();
-  void NotifyEnd();
-  void NotifyTargetData();
-  void ReallocDataBuffer();
-  void ReallocNameBuffer();
-  void ParseChar(uint8_t ch);
-
-  CFX_SAXFile m_File;
-  HandlerIface* m_pHandler;
-  int32_t m_iState;
-  std::stack<std::unique_ptr<CFX_SAXItem>> m_Stack;
-  uint32_t m_dwItemID;
-  CFX_SaxMode m_eMode;
-  CFX_SaxMode m_ePrevMode;
-  bool m_bCharData;
-  uint8_t m_CurByte;
-  uint32_t m_dwDataOffset;
-  std::stack<char> m_SkipStack;
-  uint8_t m_SkipChar;
-  uint32_t m_dwNodePos;
-  std::vector<uint8_t> m_Data;
-  int32_t m_iEntityStart;  // Index into m_Data.
-  std::vector<uint8_t> m_Name;
-  uint32_t m_dwParseMode;
-  std::unique_ptr<CFX_SAXCommentContext> m_pCommentContext;
-};
-
-#endif  // CORE_FXCRT_XML_CFX_SAXREADER_H_
diff --git a/core/fxcrt/xml/cfx_saxreader_unittest.cpp b/core/fxcrt/xml/cfx_saxreader_unittest.cpp
deleted file mode 100644
index 13d0632..0000000
--- a/core/fxcrt/xml/cfx_saxreader_unittest.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/xml/cfx_saxreader.h"
-#include "core/fxcrt/cfx_memorystream.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-using testing::_;
-using testing::Eq;
-using testing::Return;
-
-namespace {
-
-class MockHandler : public CFX_SAXReader::HandlerIface {
- public:
-  MOCK_METHOD3(OnTagEnter,
-               CFX_SAXContext*(const ByteStringView& bsTagName,
-                               CFX_SAXItem::Type eType,
-                               uint32_t dwStartPos));
-  MOCK_METHOD3(OnTagAttribute,
-               void(CFX_SAXContext* pTag,
-                    const ByteStringView& bsAttri,
-                    const ByteStringView& bsValue));
-  MOCK_METHOD1(OnTagBreak, void(CFX_SAXContext* pTag));
-  MOCK_METHOD4(OnTagData,
-               void(CFX_SAXContext* pTag,
-                    CFX_SAXItem::Type eType,
-                    const ByteStringView& bsData,
-                    uint32_t dwStartPos));
-  MOCK_METHOD2(OnTagClose, void(CFX_SAXContext* pTag, uint32_t dwEndPos));
-  MOCK_METHOD3(OnTagEnd,
-               void(CFX_SAXContext* pTag,
-                    const ByteStringView& bsTagName,
-                    uint32_t dwEndPos));
-  MOCK_METHOD4(OnTargetData,
-               void(CFX_SAXContext* pTag,
-                    CFX_SAXItem::Type eType,
-                    const ByteStringView& bsData,
-                    uint32_t dwStartPos));
-};
-
-}  // namespace
-
-class CFX_SAXReaderTest : public testing::Test {
- public:
-  void SetHandler(CFX_SAXReader::HandlerIface* handler) {
-    reader_.SetHandler(handler);
-  }
-
-  bool StartParse(char* str) {
-    return reader_.StartParse(
-               pdfium::MakeRetain<CFX_MemoryStream>(
-                   reinterpret_cast<uint8_t*>(str), strlen(str), false),
-               0, static_cast<uint32_t>(-1),
-               CFX_SaxParseMode_NotSkipSpace) >= 0;
-  }
-
-  int32_t ContinueParse() {
-    int32_t ret;
-    do {
-      ret = reader_.ContinueParse();
-    } while (ret >= 0 && ret < 100);
-    return ret;
-  }
-
- private:
-  CFX_SAXReader reader_;
-};
-
-TEST_F(CFX_SAXReaderTest, Null) {
-  char data[] = "";
-  ASSERT_FALSE(StartParse(data));
-}
-
-TEST_F(CFX_SAXReaderTest, SimpleText) {
-  MockHandler mock;
-  SetHandler(&mock);
-
-  char data[] = "clams";
-  ASSERT_TRUE(StartParse(data));
-  EXPECT_EQ(100, ContinueParse());
-}
-
-TEST_F(CFX_SAXReaderTest, SimpleTag) {
-  MockHandler mock;
-  EXPECT_CALL(mock, OnTagEnter(Eq("clams"), _, _));
-  EXPECT_CALL(mock, OnTagBreak(_));
-  SetHandler(&mock);
-
-  char data[] = "<clams>";
-  ASSERT_TRUE(StartParse(data));
-  EXPECT_EQ(100, ContinueParse());
-}
-
-TEST_F(CFX_SAXReaderTest, AttributeTag) {
-  MockHandler mock;
-  EXPECT_CALL(mock, OnTagEnter(Eq("clams"), _, _));
-  EXPECT_CALL(mock, OnTagAttribute(_, Eq("size"), Eq("small")));
-  EXPECT_CALL(mock, OnTagAttribute(_, Eq("color"), Eq("red")));
-  EXPECT_CALL(mock, OnTagBreak(_));
-  SetHandler(&mock);
-
-  char data[] = "<clams size='small' color='red'>";
-  ASSERT_TRUE(StartParse(data));
-  EXPECT_EQ(100, ContinueParse());
-}
-
-TEST_F(CFX_SAXReaderTest, AttributeEntityTag) {
-  MockHandler mock;
-  EXPECT_CALL(mock, OnTagEnter(Eq("clams"), _, _));
-  EXPECT_CALL(mock, OnTagAttribute(_, Eq("predicate"), Eq("1 < 2")));
-  EXPECT_CALL(mock, OnTagBreak(_));
-  SetHandler(&mock);
-
-  char data[] = "<clams predicate='1 &lt; 2'>";
-  ASSERT_TRUE(StartParse(data));
-  EXPECT_EQ(100, ContinueParse());
-}
-
-TEST_F(CFX_SAXReaderTest, TextWithinTag) {
-  MockHandler mock;
-  EXPECT_CALL(mock, OnTagEnter(Eq("b"), _, _));
-  EXPECT_CALL(mock, OnTagBreak(_));
-  EXPECT_CALL(mock, OnTagData(_, _, Eq("biff"), _));
-  EXPECT_CALL(mock, OnTagEnd(_, Eq("b"), _));
-  SetHandler(&mock);
-
-  char data[] = "<b>biff</b>";
-  ASSERT_TRUE(StartParse(data));
-  EXPECT_EQ(100, ContinueParse());
-}
-
-TEST_F(CFX_SAXReaderTest, bug_711459) {
-  char data[] =
-      "&a<tag "
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-      ">x;";
-  ASSERT_TRUE(StartParse(data));
-  EXPECT_EQ(100, ContinueParse());
-}
diff --git a/core/fxcrt/xml/cfx_saxreaderhandler.cpp b/core/fxcrt/xml/cfx_saxreaderhandler.cpp
deleted file mode 100644
index d255ce9..0000000
--- a/core/fxcrt/xml/cfx_saxreaderhandler.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cfx_saxreaderhandler.h"
-
-#include <string>
-
-#include "core/fxcrt/cfx_checksumcontext.h"
-
-CFX_SAXReaderHandler::CFX_SAXReaderHandler(CFX_ChecksumContext* pContext)
-    : m_pContext(pContext) {
-  ASSERT(m_pContext);
-}
-
-CFX_SAXReaderHandler::~CFX_SAXReaderHandler() {}
-
-CFX_SAXContext* CFX_SAXReaderHandler::OnTagEnter(
-    const ByteStringView& bsTagName,
-    CFX_SAXItem::Type eType,
-    uint32_t dwStartPos) {
-  UpdateChecksum(true);
-  if (eType != CFX_SAXItem::Type::Tag &&
-      eType != CFX_SAXItem::Type::Instruction) {
-    return nullptr;
-  }
-
-  m_SAXContext.m_eNode = eType;
-  m_SAXContext.m_TextBuf << "<";
-  if (eType == CFX_SAXItem::Type::Instruction)
-    m_SAXContext.m_TextBuf << "?";
-
-  m_SAXContext.m_TextBuf << bsTagName;
-  m_SAXContext.m_bsTagName = bsTagName;
-  return &m_SAXContext;
-}
-
-void CFX_SAXReaderHandler::OnTagAttribute(CFX_SAXContext* pTag,
-                                          const ByteStringView& bsAttri,
-                                          const ByteStringView& bsValue) {
-  if (!pTag)
-    return;
-  pTag->m_TextBuf << " " << bsAttri << "=\"" << bsValue << "\"";
-}
-
-void CFX_SAXReaderHandler::OnTagBreak(CFX_SAXContext* pTag) {
-  if (!pTag)
-    return;
-
-  pTag->m_TextBuf << ">";
-  UpdateChecksum(false);
-}
-
-void CFX_SAXReaderHandler::OnTagData(CFX_SAXContext* pTag,
-                                     CFX_SAXItem::Type eType,
-                                     const ByteStringView& bsData,
-                                     uint32_t dwStartPos) {
-  if (!pTag)
-    return;
-
-  if (eType == CFX_SAXItem::Type::CharData)
-    pTag->m_TextBuf << "<![CDATA[";
-
-  pTag->m_TextBuf << bsData;
-  if (eType == CFX_SAXItem::Type::CharData)
-    pTag->m_TextBuf << "]]>";
-}
-
-void CFX_SAXReaderHandler::OnTagClose(CFX_SAXContext* pTag, uint32_t dwEndPos) {
-  if (!pTag)
-    return;
-
-  if (pTag->m_eNode == CFX_SAXItem::Type::Instruction)
-    pTag->m_TextBuf << "?>";
-  else if (pTag->m_eNode == CFX_SAXItem::Type::Tag)
-    pTag->m_TextBuf << "></" << pTag->m_bsTagName.AsStringView() << ">";
-
-  UpdateChecksum(false);
-}
-
-void CFX_SAXReaderHandler::OnTagEnd(CFX_SAXContext* pTag,
-                                    const ByteStringView& bsTagName,
-                                    uint32_t dwEndPos) {
-  if (!pTag)
-    return;
-
-  pTag->m_TextBuf << "</" << bsTagName << ">";
-  UpdateChecksum(false);
-}
-
-void CFX_SAXReaderHandler::OnTargetData(CFX_SAXContext* pTag,
-                                        CFX_SAXItem::Type eType,
-                                        const ByteStringView& bsData,
-                                        uint32_t dwStartPos) {
-  if (!pTag && eType != CFX_SAXItem::Type::Comment)
-    return;
-
-  if (eType == CFX_SAXItem::Type::Comment) {
-    m_SAXContext.m_TextBuf << "<!--" << bsData << "-->";
-    UpdateChecksum(false);
-  } else {
-    pTag->m_TextBuf << " " << bsData;
-  }
-}
-
-void CFX_SAXReaderHandler::UpdateChecksum(bool bCheckSpace) {
-  int32_t iLength = m_SAXContext.m_TextBuf.tellp();
-  if (iLength < 1)
-    return;
-
-  std::string sBuffer = m_SAXContext.m_TextBuf.str();
-  const uint8_t* pBuffer = reinterpret_cast<const uint8_t*>(sBuffer.c_str());
-  bool bUpdata = true;
-  if (bCheckSpace) {
-    bUpdata = false;
-    for (int32_t i = 0; i < iLength; i++) {
-      bUpdata = (pBuffer[i] > 0x20);
-      if (bUpdata)
-        break;
-    }
-  }
-  if (bUpdata)
-    m_pContext->Update(ByteStringView(pBuffer, iLength));
-
-  m_SAXContext.m_TextBuf.str("");
-}
diff --git a/core/fxcrt/xml/cfx_saxreaderhandler.h b/core/fxcrt/xml/cfx_saxreaderhandler.h
deleted file mode 100644
index 263008f..0000000
--- a/core/fxcrt/xml/cfx_saxreaderhandler.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CFX_SAXREADERHANDLER_H_
-#define CORE_FXCRT_XML_CFX_SAXREADERHANDLER_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/xml/cfx_saxcontext.h"
-#include "core/fxcrt/xml/cfx_saxreader.h"
-
-class CFX_ChecksumContext;
-
-class CFX_SAXReaderHandler : public CFX_SAXReader::HandlerIface {
- public:
-  explicit CFX_SAXReaderHandler(CFX_ChecksumContext* pContext);
-  ~CFX_SAXReaderHandler() override;
-
-  CFX_SAXContext* OnTagEnter(const ByteStringView& bsTagName,
-                             CFX_SAXItem::Type eType,
-                             uint32_t dwStartPos) override;
-  void OnTagAttribute(CFX_SAXContext* pTag,
-                      const ByteStringView& bsAttri,
-                      const ByteStringView& bsValue) override;
-  void OnTagBreak(CFX_SAXContext* pTag) override;
-  void OnTagData(CFX_SAXContext* pTag,
-                 CFX_SAXItem::Type eType,
-                 const ByteStringView& bsData,
-                 uint32_t dwStartPos) override;
-  void OnTagClose(CFX_SAXContext* pTag, uint32_t dwEndPos) override;
-  void OnTagEnd(CFX_SAXContext* pTag,
-                const ByteStringView& bsTagName,
-                uint32_t dwEndPos) override;
-  void OnTargetData(CFX_SAXContext* pTag,
-                    CFX_SAXItem::Type eType,
-                    const ByteStringView& bsData,
-                    uint32_t dwStartPos) override;
-
- private:
-  void UpdateChecksum(bool bCheckSpace);
-
-  CFX_ChecksumContext* m_pContext;
-  CFX_SAXContext m_SAXContext;
-};
-
-#endif  // CORE_FXCRT_XML_CFX_SAXREADERHANDLER_H_
diff --git a/core/fxcrt/xml/cfx_xmlattributenode.cpp b/core/fxcrt/xml/cfx_xmlattributenode.cpp
deleted file mode 100644
index 6104747..0000000
--- a/core/fxcrt/xml/cfx_xmlattributenode.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cfx_xmlattributenode.h"
-
-#include "core/fxcrt/fx_extension.h"
-
-CFX_XMLAttributeNode::CFX_XMLAttributeNode(const WideString& name)
-    : CFX_XMLNode(), name_(name) {
-  ASSERT(name_.GetLength() > 0);
-}
-
-CFX_XMLAttributeNode::~CFX_XMLAttributeNode() {}
-
-bool CFX_XMLAttributeNode::HasAttribute(const WideString& name) const {
-  return attrs_.find(name) != attrs_.end();
-}
-
-WideString CFX_XMLAttributeNode::GetString(const WideString& name) const {
-  auto it = attrs_.find(name);
-  return it != attrs_.end() ? it->second : WideString();
-}
-
-void CFX_XMLAttributeNode::SetString(const WideString& name,
-                                     const WideString& value) {
-  attrs_[name] = value;
-}
-
-void CFX_XMLAttributeNode::RemoveAttribute(const WideString& name) {
-  attrs_.erase(name);
-}
diff --git a/core/fxcrt/xml/cfx_xmlattributenode.h b/core/fxcrt/xml/cfx_xmlattributenode.h
deleted file mode 100644
index 1ac9b84..0000000
--- a/core/fxcrt/xml/cfx_xmlattributenode.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CFX_XMLATTRIBUTENODE_H_
-#define CORE_FXCRT_XML_CFX_XMLATTRIBUTENODE_H_
-
-#include <map>
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-
-class CFX_XMLAttributeNode : public CFX_XMLNode {
- public:
-  explicit CFX_XMLAttributeNode(const WideString& name);
-  ~CFX_XMLAttributeNode() override;
-
-  // CFX_XMLNode
-  FX_XMLNODETYPE GetType() const override = 0;
-  std::unique_ptr<CFX_XMLNode> Clone() override = 0;
-
-  WideString GetName() const { return name_; }
-  const std::map<WideString, WideString>& GetAttributes() const {
-    return attrs_;
-  }
-  void SetAttributes(const std::map<WideString, WideString>& attrs) {
-    attrs_ = attrs;
-  }
-  bool HasAttribute(const WideString& name) const;
-
-  void SetString(const WideString& name, const WideString& value);
-  WideString GetString(const WideString& name) const;
-
-  void RemoveAttribute(const WideString& name);
-
- private:
-  WideString name_;
-  std::map<WideString, WideString> attrs_;
-};
-
-#endif  // CORE_FXCRT_XML_CFX_XMLATTRIBUTENODE_H_
diff --git a/core/fxcrt/xml/cfx_xmlchardata.cpp b/core/fxcrt/xml/cfx_xmlchardata.cpp
index 902d139..1d42bd0 100644
--- a/core/fxcrt/xml/cfx_xmlchardata.cpp
+++ b/core/fxcrt/xml/cfx_xmlchardata.cpp
@@ -6,17 +6,24 @@
 
 #include "core/fxcrt/xml/cfx_xmlchardata.h"
 
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
 
 CFX_XMLCharData::CFX_XMLCharData(const WideString& wsCData)
     : CFX_XMLText(wsCData) {}
 
-CFX_XMLCharData::~CFX_XMLCharData() {}
+CFX_XMLCharData::~CFX_XMLCharData() = default;
 
-FX_XMLNODETYPE CFX_XMLCharData::GetType() const {
-  return FX_XMLNODE_CharData;
+CFX_XMLNode::Type CFX_XMLCharData::GetType() const {
+  return Type::kCharData;
 }
 
-std::unique_ptr<CFX_XMLNode> CFX_XMLCharData::Clone() {
-  return pdfium::MakeUnique<CFX_XMLCharData>(GetText());
+CFX_XMLNode* CFX_XMLCharData::Clone(CFX_XMLDocument* doc) {
+  return doc->CreateNode<CFX_XMLCharData>(GetText());
+}
+
+void CFX_XMLCharData::Save(
+    const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
+  pXMLStream->WriteString("<![CDATA[");
+  pXMLStream->WriteString(GetText().ToUTF8().AsStringView());
+  pXMLStream->WriteString("]]>");
 }
diff --git a/core/fxcrt/xml/cfx_xmlchardata.h b/core/fxcrt/xml/cfx_xmlchardata.h
index 9a4710f..4d3a7f0 100644
--- a/core/fxcrt/xml/cfx_xmlchardata.h
+++ b/core/fxcrt/xml/cfx_xmlchardata.h
@@ -7,18 +7,26 @@
 #ifndef CORE_FXCRT_XML_CFX_XMLCHARDATA_H_
 #define CORE_FXCRT_XML_CFX_XMLCHARDATA_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 
-class CFX_XMLCharData : public CFX_XMLText {
+class CFX_XMLDocument;
+
+class CFX_XMLCharData final : public CFX_XMLText {
  public:
   explicit CFX_XMLCharData(const WideString& wsCData);
   ~CFX_XMLCharData() override;
 
-  FX_XMLNODETYPE GetType() const override;
-  std::unique_ptr<CFX_XMLNode> Clone() override;
+  // CFX_XMLNode
+  Type GetType() const override;
+  CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
+  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
 };
 
+inline CFX_XMLCharData* ToXMLCharData(CFX_XMLNode* pNode) {
+  return pNode && pNode->GetType() == CFX_XMLNode::Type::kCharData
+             ? static_cast<CFX_XMLCharData*>(pNode)
+             : nullptr;
+}
+
 #endif  // CORE_FXCRT_XML_CFX_XMLCHARDATA_H_
diff --git a/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp b/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp
new file mode 100644
index 0000000..60798bd
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp
@@ -0,0 +1,36 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/xml/cfx_xmlchardata.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/string_write_stream.h"
+
+TEST(CFX_XMLCharDataTest, GetType) {
+  CFX_XMLCharData data(L"My Data");
+  EXPECT_EQ(CFX_XMLNode::Type::kCharData, data.GetType());
+}
+
+TEST(CFX_XMLCharDataTest, GetText) {
+  CFX_XMLCharData data(L"My Data");
+  EXPECT_EQ(L"My Data", data.GetText());
+}
+
+TEST(CFX_XMLCharDataTest, Clone) {
+  CFX_XMLDocument doc;
+
+  CFX_XMLCharData data(L"My Data");
+  CFX_XMLNode* clone = data.Clone(&doc);
+  EXPECT_TRUE(clone != nullptr);
+  EXPECT_NE(&data, clone);
+  ASSERT_EQ(CFX_XMLNode::Type::kCharData, clone->GetType());
+  EXPECT_EQ(L"My Data", ToXMLCharData(clone)->GetText());
+}
+
+TEST(CFX_XMLCharDataTest, Save) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLCharData data(L"My Data");
+  data.Save(stream);
+  EXPECT_EQ("<![CDATA[My Data]]>", stream->ToString());
+}
diff --git a/core/fxcrt/xml/cfx_xmldoc.cpp b/core/fxcrt/xml/cfx_xmldoc.cpp
deleted file mode 100644
index 36c3606..0000000
--- a/core/fxcrt/xml/cfx_xmldoc.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cfx_xmldoc.h"
-
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/xml/cfx_xmlchardata.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "core/fxcrt/xml/cfx_xmlinstruction.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "core/fxcrt/xml/cfx_xmltext.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-CFX_XMLDoc::CFX_XMLDoc()
-    : m_iStatus(0), m_pRoot(pdfium::MakeUnique<CFX_XMLNode>()) {
-  m_pRoot->InsertChildNode(new CFX_XMLInstruction(L"xml"));
-}
-
-CFX_XMLDoc::~CFX_XMLDoc() {}
-
-bool CFX_XMLDoc::LoadXML(std::unique_ptr<CFX_XMLParser> pXMLParser) {
-  if (!pXMLParser)
-    return false;
-
-  m_iStatus = 0;
-  m_pStream.Reset();
-  m_pRoot->DeleteChildren();
-  m_pXMLParser = std::move(pXMLParser);
-  return true;
-}
-
-int32_t CFX_XMLDoc::DoLoad() {
-  if (m_iStatus < 100)
-    m_iStatus = m_pXMLParser->DoParser();
-
-  return m_iStatus;
-}
-
-void CFX_XMLDoc::CloseXML() {
-  m_pXMLParser.reset();
-}
-
-void CFX_XMLDoc::SaveXMLNode(
-    const RetainPtr<CFX_SeekableStreamProxy>& pXMLStream,
-    CFX_XMLNode* pINode) {
-  CFX_XMLNode* pNode = (CFX_XMLNode*)pINode;
-  switch (pNode->GetType()) {
-    case FX_XMLNODE_Instruction: {
-      CFX_XMLInstruction* pInstruction = (CFX_XMLInstruction*)pNode;
-      if (pInstruction->GetName().CompareNoCase(L"xml") == 0) {
-        WideString ws = L"<?xml version=\"1.0\" encoding=\"";
-        uint16_t wCodePage = pXMLStream->GetCodePage();
-        if (wCodePage == FX_CODEPAGE_UTF16LE) {
-          ws += L"UTF-16";
-        } else if (wCodePage == FX_CODEPAGE_UTF16BE) {
-          ws += L"UTF-16be";
-        } else {
-          ws += L"UTF-8";
-        }
-        ws += L"\"?>";
-        pXMLStream->WriteString(ws.AsStringView());
-      } else {
-        WideString ws =
-            WideString::Format(L"<?%ls", pInstruction->GetName().c_str());
-        pXMLStream->WriteString(ws.AsStringView());
-
-        for (auto it : pInstruction->GetAttributes()) {
-          WideString wsValue = it.second;
-          wsValue.Replace(L"&", L"&amp;");
-          wsValue.Replace(L"<", L"&lt;");
-          wsValue.Replace(L">", L"&gt;");
-          wsValue.Replace(L"\'", L"&apos;");
-          wsValue.Replace(L"\"", L"&quot;");
-
-          ws = L" ";
-          ws += it.first;
-          ws += L"=\"";
-          ws += wsValue;
-          ws += L"\"";
-          pXMLStream->WriteString(ws.AsStringView());
-        }
-
-        for (auto target : pInstruction->GetTargetData()) {
-          ws = L" \"";
-          ws += target;
-          ws += L"\"";
-          pXMLStream->WriteString(ws.AsStringView());
-        }
-        ws = L"?>";
-        pXMLStream->WriteString(ws.AsStringView());
-      }
-      break;
-    }
-    case FX_XMLNODE_Element: {
-      WideString ws;
-      ws = L"<";
-      ws += static_cast<CFX_XMLElement*>(pNode)->GetName();
-      pXMLStream->WriteString(ws.AsStringView());
-
-      for (auto it : static_cast<CFX_XMLElement*>(pNode)->GetAttributes()) {
-        WideString wsValue = it.second;
-        wsValue.Replace(L"&", L"&amp;");
-        wsValue.Replace(L"<", L"&lt;");
-        wsValue.Replace(L">", L"&gt;");
-        wsValue.Replace(L"\'", L"&apos;");
-        wsValue.Replace(L"\"", L"&quot;");
-
-        ws = L" ";
-        ws += it.first;
-        ws += L"=\"";
-        ws += wsValue;
-        ws += L"\"";
-        pXMLStream->WriteString(ws.AsStringView());
-      }
-      if (pNode->m_pChild) {
-        ws = L"\n>";
-        pXMLStream->WriteString(ws.AsStringView());
-        CFX_XMLNode* pChild = pNode->m_pChild;
-        while (pChild) {
-          SaveXMLNode(pXMLStream, static_cast<CFX_XMLNode*>(pChild));
-          pChild = pChild->m_pNext;
-        }
-        ws = L"</";
-        ws += static_cast<CFX_XMLElement*>(pNode)->GetName();
-        ws += L"\n>";
-      } else {
-        ws = L"\n/>";
-      }
-      pXMLStream->WriteString(ws.AsStringView());
-      break;
-    }
-    case FX_XMLNODE_Text: {
-      WideString ws = static_cast<CFX_XMLText*>(pNode)->GetText();
-      ws.Replace(L"&", L"&amp;");
-      ws.Replace(L"<", L"&lt;");
-      ws.Replace(L">", L"&gt;");
-      ws.Replace(L"\'", L"&apos;");
-      ws.Replace(L"\"", L"&quot;");
-      pXMLStream->WriteString(ws.AsStringView());
-      break;
-    }
-    case FX_XMLNODE_CharData: {
-      WideString ws = L"<![CDATA[";
-      ws += static_cast<CFX_XMLCharData*>(pNode)->GetText();
-      ws += L"]]>";
-      pXMLStream->WriteString(ws.AsStringView());
-      break;
-    }
-    case FX_XMLNODE_Unknown:
-    default:
-      break;
-  }
-}
diff --git a/core/fxcrt/xml/cfx_xmldoc.h b/core/fxcrt/xml/cfx_xmldoc.h
deleted file mode 100644
index 28de7fd..0000000
--- a/core/fxcrt/xml/cfx_xmldoc.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CFX_XMLDOC_H_
-#define CORE_FXCRT_XML_CFX_XMLDOC_H_
-
-#include <memory>
-
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "core/fxcrt/xml/cfx_xmlparser.h"
-
-class CFX_XMLDoc {
- public:
-  CFX_XMLDoc();
-  ~CFX_XMLDoc();
-
-  bool LoadXML(std::unique_ptr<CFX_XMLParser> pXMLParser);
-  int32_t DoLoad();
-  void CloseXML();
-
-  CFX_XMLNode* GetRoot() const { return m_pRoot.get(); }
-  void SaveXMLNode(const RetainPtr<CFX_SeekableStreamProxy>& pXMLStream,
-                   CFX_XMLNode* pNode);
-
- private:
-  int32_t m_iStatus;
-  std::unique_ptr<CFX_XMLNode> m_pRoot;
-  std::unique_ptr<CFX_XMLParser> m_pXMLParser;
-  RetainPtr<CFX_SeekableStreamProxy> m_pStream;
-};
-
-#endif  // CORE_FXCRT_XML_CFX_XMLDOC_H_
diff --git a/core/fxcrt/xml/cfx_xmldocument.cpp b/core/fxcrt/xml/cfx_xmldocument.cpp
new file mode 100644
index 0000000..1c6656d
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmldocument.cpp
@@ -0,0 +1,21 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/xml/cfx_xmlinstruction.h"
+
+CFX_XMLDocument::CFX_XMLDocument() {
+  root_ = CreateNode<CFX_XMLElement>(L"root");
+}
+
+CFX_XMLDocument::~CFX_XMLDocument() = default;
+
+void CFX_XMLDocument::AppendNodesFrom(CFX_XMLDocument* other) {
+  nodes_.reserve(nodes_.size() + other->nodes_.size());
+  nodes_.insert(nodes_.end(), std::make_move_iterator(other->nodes_.begin()),
+                std::make_move_iterator(other->nodes_.end()));
+  other->nodes_.clear();
+}
diff --git a/core/fxcrt/xml/cfx_xmldocument.h b/core/fxcrt/xml/cfx_xmldocument.h
new file mode 100644
index 0000000..9931314
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmldocument.h
@@ -0,0 +1,42 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_XML_CFX_XMLDOCUMENT_H_
+#define CORE_FXCRT_XML_CFX_XMLDOCUMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "third_party/base/ptr_util.h"
+
+class CFX_XMLNode;
+
+class CFX_XMLDocument {
+ public:
+  CFX_XMLDocument();
+  ~CFX_XMLDocument();
+
+  CFX_XMLElement* GetRoot() const { return root_.Get(); }
+
+  template <typename T, typename... Args>
+  T* CreateNode(Args&&... args) {
+    nodes_.push_back(pdfium::MakeUnique<T>(std::forward<Args>(args)...));
+    return static_cast<T*>(nodes_.back().get());
+  }
+
+  // Transfers ownership of entries in |nodes_| from |other| to |this|.
+  // This is used in CJX_Node::loadXML to transfer ownership of the newly
+  // created nodes to the top-level XML doc for the PDF, after parsing an XML
+  // blob.
+  void AppendNodesFrom(CFX_XMLDocument* other);
+
+ private:
+  std::vector<std::unique_ptr<CFX_XMLNode>> nodes_;
+  UnownedPtr<CFX_XMLElement> root_;
+};
+
+#endif  // CORE_FXCRT_XML_CFX_XMLDOCUMENT_H_
diff --git a/core/fxcrt/xml/cfx_xmldocument_unittest.cpp b/core/fxcrt/xml/cfx_xmldocument_unittest.cpp
new file mode 100644
index 0000000..8043cc6
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmldocument_unittest.cpp
@@ -0,0 +1,21 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlinstruction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CFX_XMLDocumentTest, Root) {
+  CFX_XMLDocument doc;
+  EXPECT_TRUE(doc.GetRoot() != nullptr);
+}
+
+TEST(CFX_XMLDocumentTest, CreateNode) {
+  CFX_XMLDocument doc;
+  auto* node = doc.CreateNode<CFX_XMLElement>(L"elem");
+
+  ASSERT_EQ(CFX_XMLNode::Type::kElement, node->GetType());
+  EXPECT_EQ(L"elem", node->GetName());
+}
diff --git a/core/fxcrt/xml/cfx_xmlelement.cpp b/core/fxcrt/xml/cfx_xmlelement.cpp
index 1317e9a..b999f5c 100644
--- a/core/fxcrt/xml/cfx_xmlelement.cpp
+++ b/core/fxcrt/xml/cfx_xmlelement.cpp
@@ -11,93 +11,146 @@
 #include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/xml/cfx_xmlchardata.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
-CFX_XMLElement::CFX_XMLElement(const WideString& wsTag)
-    : CFX_XMLAttributeNode(wsTag) {}
-
-CFX_XMLElement::~CFX_XMLElement() {}
-
-FX_XMLNODETYPE CFX_XMLElement::GetType() const {
-  return FX_XMLNODE_Element;
+CFX_XMLElement::CFX_XMLElement(const WideString& wsTag) : name_(wsTag) {
+  ASSERT(!name_.IsEmpty());
 }
 
-std::unique_ptr<CFX_XMLNode> CFX_XMLElement::Clone() {
-  auto pClone = pdfium::MakeUnique<CFX_XMLElement>(GetName());
-  pClone->SetAttributes(GetAttributes());
+CFX_XMLElement::~CFX_XMLElement() = default;
 
-  WideString wsText;
-  CFX_XMLNode* pChild = m_pChild;
-  while (pChild) {
-    switch (pChild->GetType()) {
-      case FX_XMLNODE_Text:
-        wsText += static_cast<CFX_XMLText*>(pChild)->GetText();
-        break;
-      default:
-        break;
-    }
-    pChild = pChild->m_pNext;
+CFX_XMLNode::Type CFX_XMLElement::GetType() const {
+  return Type::kElement;
+}
+
+CFX_XMLNode* CFX_XMLElement::Clone(CFX_XMLDocument* doc) {
+  auto* node = doc->CreateNode<CFX_XMLElement>(name_);
+  node->attrs_ = attrs_;
+
+  // TODO(dsinclair): This clone is wrong. It doesn't clone all child nodes just
+  // text nodes?
+  for (CFX_XMLNode* pChild = GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    if (pChild->GetType() == Type::kText)
+      node->AppendLastChild(pChild->Clone(doc));
   }
-  pClone->SetTextData(wsText);
-  return std::move(pClone);
+  return node;
 }
 
 WideString CFX_XMLElement::GetLocalTagName() const {
-  auto pos = GetName().Find(L':');
-  return pos.has_value()
-             ? GetName().Right(GetName().GetLength() - pos.value() - 1)
-             : GetName();
+  auto pos = name_.Find(L':');
+  return pos.has_value() ? name_.Last(name_.GetLength() - pos.value() - 1)
+                         : name_;
 }
 
 WideString CFX_XMLElement::GetNamespacePrefix() const {
-  auto pos = GetName().Find(L':');
-  return pos.has_value() ? GetName().Left(pos.value()) : WideString();
+  auto pos = name_.Find(L':');
+  return pos.has_value() ? name_.First(pos.value()) : WideString();
 }
 
 WideString CFX_XMLElement::GetNamespaceURI() const {
-  WideString wsAttri(L"xmlns");
+  WideString attr(L"xmlns");
   WideString wsPrefix = GetNamespacePrefix();
-  if (wsPrefix.GetLength() > 0) {
-    wsAttri += L":";
-    wsAttri += wsPrefix;
+  if (!wsPrefix.IsEmpty()) {
+    attr += L":";
+    attr += wsPrefix;
   }
-
-  auto* pNode = static_cast<const CFX_XMLNode*>(this);
-  while (pNode) {
-    if (pNode->GetType() != FX_XMLNODE_Element)
-      break;
-
+  const CFX_XMLNode* pNode = this;
+  while (pNode && pNode->GetType() == Type::kElement) {
     auto* pElement = static_cast<const CFX_XMLElement*>(pNode);
-    if (!pElement->HasAttribute(wsAttri)) {
-      pNode = pNode->GetNodeItem(CFX_XMLNode::Parent);
+    if (!pElement->HasAttribute(attr)) {
+      pNode = pNode->GetParent();
       continue;
     }
-    return pElement->GetString(wsAttri);
+    return pElement->GetAttribute(attr);
   }
   return WideString();
 }
 
 WideString CFX_XMLElement::GetTextData() const {
   CFX_WideTextBuf buffer;
-  CFX_XMLNode* pChild = m_pChild;
-  while (pChild) {
-    switch (pChild->GetType()) {
-      case FX_XMLNODE_Text:
-      case FX_XMLNODE_CharData:
-        buffer << static_cast<CFX_XMLText*>(pChild)->GetText();
-        break;
-      default:
-        break;
-    }
-    pChild = pChild->m_pNext;
+  for (CFX_XMLNode* pChild = GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    CFX_XMLText* pText = ToXMLText(pChild);
+    if (pText)
+      buffer << pText->GetText();
   }
   return buffer.MakeString();
 }
 
-void CFX_XMLElement::SetTextData(const WideString& wsText) {
-  if (wsText.GetLength() < 1)
+void CFX_XMLElement::Save(
+    const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
+  ByteString bsNameEncoded = name_.ToUTF8();
+
+  pXMLStream->WriteString("<");
+  pXMLStream->WriteString(bsNameEncoded.AsStringView());
+
+  for (auto it : attrs_) {
+    // Note, the space between attributes is added by AttributeToString which
+    // writes a blank as the first character.
+    pXMLStream->WriteString(
+        AttributeToString(it.first, it.second).ToUTF8().AsStringView());
+  }
+
+  if (!GetFirstChild()) {
+    pXMLStream->WriteString(" />\n");
     return;
-  InsertChildNode(new CFX_XMLText(wsText));
+  }
+
+  pXMLStream->WriteString(">\n");
+
+  for (CFX_XMLNode* pChild = GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    pChild->Save(pXMLStream);
+  }
+  pXMLStream->WriteString("</");
+  pXMLStream->WriteString(bsNameEncoded.AsStringView());
+  pXMLStream->WriteString(">\n");
+}
+
+CFX_XMLElement* CFX_XMLElement::GetFirstChildNamed(WideStringView name) const {
+  return GetNthChildNamed(name, 0);
+}
+
+CFX_XMLElement* CFX_XMLElement::GetNthChildNamed(WideStringView name,
+                                                 size_t idx) const {
+  for (auto* child = GetFirstChild(); child; child = child->GetNextSibling()) {
+    CFX_XMLElement* elem = ToXMLElement(child);
+    if (!elem || elem->name_ != name)
+      continue;
+    if (idx == 0)
+      return elem;
+
+    --idx;
+  }
+  return nullptr;
+}
+
+bool CFX_XMLElement::HasAttribute(const WideString& name) const {
+  return attrs_.find(name) != attrs_.end();
+}
+
+WideString CFX_XMLElement::GetAttribute(const WideString& name) const {
+  auto it = attrs_.find(name);
+  return it != attrs_.end() ? it->second : WideString();
+}
+
+void CFX_XMLElement::SetAttribute(const WideString& name,
+                                  const WideString& value) {
+  attrs_[name] = value;
+}
+
+void CFX_XMLElement::RemoveAttribute(const WideString& name) {
+  attrs_.erase(name);
+}
+
+WideString CFX_XMLElement::AttributeToString(const WideString& name,
+                                             const WideString& value) {
+  WideString ret = L" ";
+  ret += name;
+  ret += L"=\"";
+  ret += EncodeEntities(value);
+  ret += L"\"";
+  return ret;
 }
diff --git a/core/fxcrt/xml/cfx_xmlelement.h b/core/fxcrt/xml/cfx_xmlelement.h
index 59e3af6..efc9fbb 100644
--- a/core/fxcrt/xml/cfx_xmlelement.h
+++ b/core/fxcrt/xml/cfx_xmlelement.h
@@ -7,27 +7,60 @@
 #ifndef CORE_FXCRT_XML_CFX_XMLELEMENT_H_
 #define CORE_FXCRT_XML_CFX_XMLELEMENT_H_
 
-#include <memory>
-#include <vector>
+#include <map>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/xml/cfx_xmlattributenode.h"
+#include "core/fxcrt/xml/cfx_xmlnode.h"
 
-class CFX_XMLElement : public CFX_XMLAttributeNode {
+class CFX_XMLDocument;
+
+class CFX_XMLElement final : public CFX_XMLNode {
  public:
   explicit CFX_XMLElement(const WideString& wsTag);
   ~CFX_XMLElement() override;
 
   // CFX_XMLNode
-  FX_XMLNODETYPE GetType() const override;
-  std::unique_ptr<CFX_XMLNode> Clone() override;
+  Type GetType() const override;
+  CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
+  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
+
+  const WideString& GetName() const { return name_; }
+
+  const std::map<WideString, WideString>& GetAttributes() const {
+    return attrs_;
+  }
+  bool HasAttribute(const WideString& name) const;
+  void SetAttribute(const WideString& name, const WideString& value);
+  WideString GetAttribute(const WideString& name) const;
+
+  void RemoveAttribute(const WideString& name);
+
+  CFX_XMLElement* GetFirstChildNamed(WideStringView name) const;
+  CFX_XMLElement* GetNthChildNamed(WideStringView name, size_t idx) const;
 
   WideString GetLocalTagName() const;
   WideString GetNamespacePrefix() const;
   WideString GetNamespaceURI() const;
 
   WideString GetTextData() const;
-  void SetTextData(const WideString& wsText);
+
+ private:
+  WideString AttributeToString(const WideString& name, const WideString& value);
+
+  const WideString name_;
+  std::map<WideString, WideString> attrs_;
 };
 
+inline CFX_XMLElement* ToXMLElement(CFX_XMLNode* pNode) {
+  return pNode && pNode->GetType() == CFX_XMLNode::Type::kElement
+             ? static_cast<CFX_XMLElement*>(pNode)
+             : nullptr;
+}
+
+inline const CFX_XMLElement* ToXMLElement(const CFX_XMLNode* pNode) {
+  return pNode && pNode->GetType() == CFX_XMLNode::Type::kElement
+             ? static_cast<const CFX_XMLElement*>(pNode)
+             : nullptr;
+}
+
 #endif  // CORE_FXCRT_XML_CFX_XMLELEMENT_H_
diff --git a/core/fxcrt/xml/cfx_xmlelement_unittest.cpp b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
new file mode 100644
index 0000000..0053358
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
@@ -0,0 +1,226 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlchardata.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmltext.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/string_write_stream.h"
+
+TEST(CFX_XMLElementTest, GetType) {
+  CFX_XMLElement node(L"node");
+  EXPECT_EQ(CFX_XMLNode::Type::kElement, node.GetType());
+}
+
+TEST(CFX_XMLElementTest, GetName) {
+  CFX_XMLElement node(L"node");
+  EXPECT_EQ(L"node", node.GetName());
+}
+
+TEST(CFX_XMLElementTest, GetLocalTagName) {
+  CFX_XMLElement node1(L"node1");
+  EXPECT_EQ(L"node1", node1.GetLocalTagName());
+
+  CFX_XMLElement node2(L"test:node2");
+  EXPECT_EQ(L"node2", node2.GetLocalTagName());
+}
+
+TEST(CFX_XMLElementTest, GetNamespacePrefix) {
+  CFX_XMLElement node1(L"node1");
+  EXPECT_EQ(L"", node1.GetNamespacePrefix());
+
+  CFX_XMLElement node2(L"test:node2");
+  EXPECT_EQ(L"test", node2.GetNamespacePrefix());
+}
+
+TEST(CFX_XMLElementTest, GetNamespaceURI) {
+  CFX_XMLElement node1(L"node1");
+  EXPECT_EQ(L"", node1.GetNamespaceURI());
+
+  node1.SetAttribute(L"xmlns", L"https://example.org/ns1");
+  EXPECT_EQ(L"https://example.org/ns1", node1.GetNamespaceURI());
+
+  CFX_XMLElement node2(L"test:node2");
+  EXPECT_EQ(L"", node2.GetNamespaceURI());
+
+  node2.SetAttribute(L"xmlns", L"https://example.org/ns2");
+  EXPECT_EQ(L"", node2.GetNamespaceURI());
+
+  node2.SetAttribute(L"xmlns:test", L"https://example.org/ns2");
+  EXPECT_EQ(L"https://example.org/ns2", node2.GetNamespaceURI());
+}
+
+TEST(CFX_XMLElementTest, Attributes) {
+  CFX_XMLElement node(L"test:node");
+  node.SetAttribute(L"first", L"one");
+  node.SetAttribute(L"second", L"two");
+
+  ASSERT_TRUE(node.HasAttribute(L"first"));
+  EXPECT_EQ(L"one", node.GetAttribute(L"first"));
+  ASSERT_TRUE(node.HasAttribute(L"second"));
+  EXPECT_EQ(L"two", node.GetAttribute(L"second"));
+
+  ASSERT_EQ(2U, node.GetAttributes().size());
+
+  node.RemoveAttribute(L"first");
+  EXPECT_FALSE(node.HasAttribute(L"first"));
+
+  ASSERT_EQ(1U, node.GetAttributes().size());
+}
+
+TEST(CFX_XMLElementTest, Clone) {
+  CFX_XMLDocument doc;
+  CFX_XMLElement node(L"test:node");
+  node.SetAttribute(L"first", L"one");
+  node.SetAttribute(L"second", L"two");
+  node.SetAttribute(L"xmlns:test", L"https://example.org/test");
+
+  CFX_XMLText text_child1(L"Text Child");
+  node.AppendLastChild(&text_child1);
+
+  CFX_XMLElement node_child1(L"Node child");
+  node.AppendLastChild(&node_child1);
+
+  CFX_XMLNode* clone = node.Clone(&doc);
+  EXPECT_TRUE(clone != nullptr);
+  ASSERT_EQ(CFX_XMLNode::Type::kElement, clone->GetType());
+
+  CFX_XMLElement* inst = ToXMLElement(clone);
+  EXPECT_EQ(L"test:node", inst->GetName());
+  EXPECT_EQ(L"node", inst->GetLocalTagName());
+  EXPECT_EQ(L"test", inst->GetNamespacePrefix());
+  EXPECT_EQ(L"https://example.org/test", inst->GetNamespaceURI());
+
+  ASSERT_TRUE(inst->HasAttribute(L"first"));
+  EXPECT_EQ(L"one", inst->GetAttribute(L"first"));
+  ASSERT_TRUE(inst->HasAttribute(L"second"));
+  EXPECT_EQ(L"two", inst->GetAttribute(L"second"));
+
+  // Only clone the Text node, so expect only one child.
+  ASSERT_TRUE(inst->GetFirstChild() != nullptr);
+  EXPECT_TRUE(inst->GetFirstChild()->GetNextSibling() == nullptr);
+
+  ASSERT_EQ(CFX_XMLNode::Type::kText, inst->GetFirstChild()->GetType());
+  auto* text = ToXMLText(inst->GetFirstChild());
+  EXPECT_EQ(L"Text Child", text->GetText());
+}
+
+TEST(CFX_XMLElementTest, Save) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLElement node(L"root");
+
+  node.Save(stream);
+  EXPECT_EQ("<root />\n", stream->ToString());
+}
+
+TEST(CFX_XMLElementTest, SaveWithAttributes) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLElement node(L"root");
+  node.SetAttribute(L"first", L"one");
+  node.SetAttribute(L"second", L"two");
+
+  node.Save(stream);
+  EXPECT_EQ("<root first=\"one\" second=\"two\" />\n", stream->ToString());
+}
+
+TEST(CFX_XMLElementTest, SaveWithChildren) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLElement node(L"node");
+
+  CFX_XMLText text_child1(L"Text Child 1");
+  node.AppendLastChild(&text_child1);
+
+  CFX_XMLElement node_child1(L"node-child");
+  node.AppendLastChild(&node_child1);
+
+  CFX_XMLText text_child2(L"Text Child 2");
+  node_child1.AppendLastChild(&text_child2);
+
+  CFX_XMLCharData char_data1(L"Char Data");
+  node.AppendLastChild(&char_data1);
+
+  node.Save(stream);
+  EXPECT_EQ(
+      "<node>\n"
+      "Text Child 1"
+      "<node-child>\nText Child 2</node-child>\n"
+      "<![CDATA[Char Data]]>"
+      "</node>\n",
+      stream->ToString());
+}
+
+TEST(CFX_XMLElementTest, SaveWithNamespace) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLElement node(L"test:root");
+  node.SetAttribute(L"xmlns:test", L"https://example.org/ns1");
+
+  node.Save(stream);
+  EXPECT_EQ("<test:root xmlns:test=\"https://example.org/ns1\" />\n",
+            stream->ToString());
+}
+
+TEST(CFX_XMLElementTest, GetFirstChildNamed) {
+  CFX_XMLElement node(L"node");
+  CFX_XMLElement node_child1(L"node-child");
+  node.AppendLastChild(&node_child1);
+
+  auto* found = node.GetFirstChildNamed(L"node-child");
+  EXPECT_TRUE(found != nullptr);
+  EXPECT_EQ(&node_child1, found);
+}
+
+TEST(CFX_XMLElementTest, GetFirstChildNamedMissing) {
+  CFX_XMLElement node(L"node");
+  CFX_XMLElement node_child1(L"node-child");
+  node.AppendLastChild(&node_child1);
+
+  auto* found = node.GetFirstChildNamed(L"node-sibling");
+  EXPECT_TRUE(found == nullptr);
+}
+
+TEST(CFX_XMLElementTest, GetNthChildNamed) {
+  CFX_XMLElement node(L"node");
+  CFX_XMLElement node_child1(L"node-child");
+  CFX_XMLElement node_child2(L"node-child");
+  CFX_XMLElement node_child3(L"node-child");
+  node.AppendLastChild(&node_child1);
+  node.AppendLastChild(&node_child2);
+  node.AppendLastChild(&node_child3);
+
+  auto* found = node.GetNthChildNamed(L"node-child", 2);
+  EXPECT_TRUE(found != nullptr);
+  EXPECT_EQ(&node_child3, found);
+}
+
+TEST(CFX_XMLElementTest, GetNthChildNamedMissingChild) {
+  CFX_XMLElement node(L"node");
+  CFX_XMLElement node_child1(L"node-child");
+  CFX_XMLElement node_child2(L"node-child");
+  CFX_XMLElement node_child3(L"node-child");
+  node.AppendLastChild(&node_child1);
+  node.AppendLastChild(&node_child2);
+  node.AppendLastChild(&node_child3);
+
+  auto* found = node.GetNthChildNamed(L"node-child", 5);
+  EXPECT_TRUE(found == nullptr);
+}
+
+TEST(CFX_XMLElementTest, GetTextData) {
+  CFX_XMLElement node(L"node");
+
+  CFX_XMLText text_child1(L"Text Child 1");
+  node.AppendLastChild(&text_child1);
+
+  CFX_XMLElement node_child1(L"Node child");
+  node.AppendLastChild(&node_child1);
+
+  CFX_XMLText text_child2(L"Text Child 2");
+  node_child1.AppendLastChild(&text_child2);
+
+  CFX_XMLCharData char_data1(L"Char Data");
+  node.AppendLastChild(&char_data1);
+
+  EXPECT_EQ(L"Text Child 1Char Data", node.GetTextData());
+}
diff --git a/core/fxcrt/xml/cfx_xmlinstruction.cpp b/core/fxcrt/xml/cfx_xmlinstruction.cpp
index 75a4a2e..ac01f4e 100644
--- a/core/fxcrt/xml/cfx_xmlinstruction.cpp
+++ b/core/fxcrt/xml/cfx_xmlinstruction.cpp
@@ -8,31 +8,52 @@
 
 #include <utility>
 
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
 
 CFX_XMLInstruction::CFX_XMLInstruction(const WideString& wsTarget)
-    : CFX_XMLAttributeNode(wsTarget) {}
+    : name_(wsTarget) {}
 
-CFX_XMLInstruction::~CFX_XMLInstruction() {}
+CFX_XMLInstruction::~CFX_XMLInstruction() = default;
 
-FX_XMLNODETYPE CFX_XMLInstruction::GetType() const {
-  return FX_XMLNODE_Instruction;
+CFX_XMLNode::Type CFX_XMLInstruction::GetType() const {
+  return Type::kInstruction;
 }
 
-std::unique_ptr<CFX_XMLNode> CFX_XMLInstruction::Clone() {
-  auto pClone = pdfium::MakeUnique<CFX_XMLInstruction>(GetName());
-  pClone->SetAttributes(GetAttributes());
-  pClone->m_TargetData = m_TargetData;
-  return std::move(pClone);
+CFX_XMLNode* CFX_XMLInstruction::Clone(CFX_XMLDocument* doc) {
+  auto* node = doc->CreateNode<CFX_XMLInstruction>(name_);
+  node->target_data_ = target_data_;
+  return node;
 }
 
 void CFX_XMLInstruction::AppendData(const WideString& wsData) {
-  m_TargetData.push_back(wsData);
+  target_data_.push_back(wsData);
 }
 
-void CFX_XMLInstruction::RemoveData(int32_t index) {
-  if (pdfium::IndexInBounds(m_TargetData, index))
-    m_TargetData.erase(m_TargetData.begin() + index);
+bool CFX_XMLInstruction::IsOriginalXFAVersion() const {
+  return name_.EqualsASCII("originalXFAVersion");
+}
+
+bool CFX_XMLInstruction::IsAcrobat() const {
+  return name_.EqualsASCII("acrobat");
+}
+
+void CFX_XMLInstruction::Save(
+    const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
+  if (name_.EqualsASCIINoCase("xml")) {
+    pXMLStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    return;
+  }
+
+  pXMLStream->WriteString("<?");
+  pXMLStream->WriteString(name_.ToUTF8().AsStringView());
+  pXMLStream->WriteString(" ");
+
+  for (const WideString& target : target_data_) {
+    pXMLStream->WriteString(target.ToUTF8().AsStringView());
+    pXMLStream->WriteString(" ");
+  }
+
+  pXMLStream->WriteString("?>\n");
 }
diff --git a/core/fxcrt/xml/cfx_xmlinstruction.h b/core/fxcrt/xml/cfx_xmlinstruction.h
index ff27dae..f4ba112 100644
--- a/core/fxcrt/xml/cfx_xmlinstruction.h
+++ b/core/fxcrt/xml/cfx_xmlinstruction.h
@@ -7,27 +7,38 @@
 #ifndef CORE_FXCRT_XML_CFX_XMLINSTRUCTION_H_
 #define CORE_FXCRT_XML_CFX_XMLINSTRUCTION_H_
 
-#include <memory>
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/xml/cfx_xmlattributenode.h"
+#include "core/fxcrt/xml/cfx_xmlnode.h"
 
-class CFX_XMLInstruction : public CFX_XMLAttributeNode {
+class CFX_XMLDocument;
+
+class CFX_XMLInstruction final : public CFX_XMLNode {
  public:
   explicit CFX_XMLInstruction(const WideString& wsTarget);
   ~CFX_XMLInstruction() override;
 
   // CFX_XMLNode
-  FX_XMLNODETYPE GetType() const override;
-  std::unique_ptr<CFX_XMLNode> Clone() override;
+  Type GetType() const override;
+  CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
+  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
 
-  const std::vector<WideString>& GetTargetData() const { return m_TargetData; }
+  bool IsOriginalXFAVersion() const;
+  bool IsAcrobat() const;
+
+  const std::vector<WideString>& GetTargetData() const { return target_data_; }
   void AppendData(const WideString& wsData);
-  void RemoveData(int32_t index);
 
  private:
-  std::vector<WideString> m_TargetData;
+  const WideString name_;
+  std::vector<WideString> target_data_;
 };
 
+inline CFX_XMLInstruction* ToXMLInstruction(CFX_XMLNode* pNode) {
+  return pNode && pNode->GetType() == CFX_XMLNode::Type::kInstruction
+             ? static_cast<CFX_XMLInstruction*>(pNode)
+             : nullptr;
+}
+
 #endif  // CORE_FXCRT_XML_CFX_XMLINSTRUCTION_H_
diff --git a/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp b/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp
new file mode 100644
index 0000000..97d9ddf
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp
@@ -0,0 +1,157 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/xml/cfx_xmlinstruction.h"
+
+#include <memory>
+
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/string_write_stream.h"
+
+TEST(CFX_XMLInstructionTest, GetType) {
+  CFX_XMLInstruction node(L"acrobat");
+  EXPECT_EQ(CFX_XMLNode::Type::kInstruction, node.GetType());
+}
+
+TEST(CFX_XMLInstructionTest, AcrobatInstruction) {
+  CFX_XMLInstruction node(L"acrobat");
+  EXPECT_TRUE(node.IsAcrobat());
+  EXPECT_FALSE(node.IsOriginalXFAVersion());
+}
+
+TEST(CFX_XMLInstructionTest, OriginalXFAInstruction) {
+  CFX_XMLInstruction node(L"originalXFAVersion");
+  EXPECT_TRUE(node.IsOriginalXFAVersion());
+  EXPECT_FALSE(node.IsAcrobat());
+}
+
+TEST(CFX_XMLInstructionTest, TargetData) {
+  CFX_XMLInstruction node(L"acrobat");
+  EXPECT_EQ(0U, node.GetTargetData().size());
+
+  node.AppendData(L"firstString");
+  node.AppendData(L"secondString");
+
+  auto& data = node.GetTargetData();
+  ASSERT_EQ(2U, data.size());
+  EXPECT_EQ(L"firstString", data[0]);
+  EXPECT_EQ(L"secondString", data[1]);
+}
+
+TEST(CFX_XMLInstructionTest, Clone) {
+  CFX_XMLDocument doc;
+
+  CFX_XMLInstruction node(L"acrobat");
+  node.AppendData(L"firstString");
+  node.AppendData(L"secondString");
+
+  CFX_XMLNode* clone = node.Clone(&doc);
+  EXPECT_TRUE(clone != nullptr);
+
+  ASSERT_EQ(CFX_XMLNode::Type::kInstruction, clone->GetType());
+  CFX_XMLInstruction* inst = ToXMLInstruction(clone);
+  EXPECT_TRUE(inst->IsAcrobat());
+
+  auto& data = inst->GetTargetData();
+  ASSERT_EQ(2U, data.size());
+  EXPECT_EQ(L"firstString", data[0]);
+  EXPECT_EQ(L"secondString", data[1]);
+}
+
+TEST(CFX_XMLInstructionTest, SaveXML) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLInstruction node(L"xml");
+  node.Save(stream);
+  EXPECT_EQ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", stream->ToString());
+}
+
+TEST(CFX_XMLInstructionTest, SaveAcrobat) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLInstruction node(L"acrobat");
+  node.AppendData(L"http://www.xfa.org/schema/xfa-template/3.3/");
+  node.AppendData(L"Display:1");
+
+  node.Save(stream);
+  EXPECT_EQ(
+      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n",
+      stream->ToString());
+}
+
+TEST(CFX_XMLInstructionTest, ParseAndReSave) {
+  static const char input[] =
+      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n"
+      "<node></node>";
+
+  auto in_stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::as_bytes(pdfium::make_span(input)));
+
+  CFX_XMLParser parser(in_stream);
+  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* root = doc->GetRoot();
+  ASSERT_TRUE(root->GetFirstChild() != nullptr);
+  ASSERT_EQ(CFX_XMLNode::Type::kInstruction, root->GetFirstChild()->GetType());
+
+  CFX_XMLInstruction* node = ToXMLInstruction(root->GetFirstChild());
+  ASSERT_TRUE(node != nullptr);
+  EXPECT_TRUE(node->IsAcrobat());
+
+  auto& data = node->GetTargetData();
+  ASSERT_EQ(2U, data.size());
+  EXPECT_EQ(L"http://www.xfa.org/schema/xfa-template/3.3/", data[0]);
+  EXPECT_EQ(L"Display:1", data[1]);
+
+  auto out_stream = pdfium::MakeRetain<StringWriteStream>();
+  node->Save(out_stream);
+  EXPECT_EQ(
+      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n",
+      out_stream->ToString());
+}
+
+TEST(CFX_XMLInstructionTest, ParseAndReSaveInnerInstruction) {
+  static const char input[] =
+      "<node>\n"
+      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n"
+      "</node>";
+
+  auto in_stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::as_bytes(pdfium::make_span(input)));
+
+  CFX_XMLParser parser(in_stream);
+  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* root = doc->GetRoot();
+  ASSERT_TRUE(root->GetFirstChild() != nullptr);
+  ASSERT_TRUE(root->GetFirstChild()->GetType() == CFX_XMLNode::Type::kElement);
+
+  CFX_XMLElement* node = ToXMLElement(root->GetFirstChild());
+  EXPECT_EQ(L"node", node->GetName());
+
+  CFX_XMLInstruction* instruction = nullptr;
+  for (auto* elem = node->GetFirstChild(); elem && !instruction;
+       elem = elem->GetNextSibling()) {
+    instruction = ToXMLInstruction(elem);
+  }
+  ASSERT_TRUE(instruction != nullptr);
+  EXPECT_TRUE(instruction->IsAcrobat());
+
+  auto& data = instruction->GetTargetData();
+  ASSERT_EQ(2U, data.size());
+  EXPECT_EQ(L"http://www.xfa.org/schema/xfa-template/3.3/", data[0]);
+  EXPECT_EQ(L"Display:1", data[1]);
+
+  auto out_stream = pdfium::MakeRetain<StringWriteStream>();
+  node->Save(out_stream);
+  EXPECT_EQ(
+      "<node>\n\n"
+      "<?acrobat http://www.xfa.org/schema/xfa-template/3.3/ Display:1 ?>\n\n"
+      "</node>\n",
+      out_stream->ToString());
+}
diff --git a/core/fxcrt/xml/cfx_xmlnode.cpp b/core/fxcrt/xml/cfx_xmlnode.cpp
index 41889e2..94b035b 100644
--- a/core/fxcrt/xml/cfx_xmlnode.cpp
+++ b/core/fxcrt/xml/cfx_xmlnode.cpp
@@ -6,434 +6,28 @@
 
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 
-#include <vector>
+CFX_XMLNode::CFX_XMLNode() = default;
 
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/xml/cfx_xmlchardata.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "core/fxcrt/xml/cfx_xmlinstruction.h"
-#include "core/fxcrt/xml/cfx_xmltext.h"
-#include "third_party/base/stl_util.h"
+CFX_XMLNode::~CFX_XMLNode() = default;
 
-CFX_XMLNode::CFX_XMLNode()
-    : m_pParent(nullptr),
-      m_pChild(nullptr),
-      m_pPrior(nullptr),
-      m_pNext(nullptr) {}
-
-FX_XMLNODETYPE CFX_XMLNode::GetType() const {
-  return FX_XMLNODE_Unknown;
+void CFX_XMLNode::InsertChildNode(CFX_XMLNode* pNode, int32_t index) {
+  InsertBefore(pNode, GetNthChild(index));
 }
 
-CFX_XMLNode::~CFX_XMLNode() {
-  DeleteChildren();
+CFX_XMLNode* CFX_XMLNode::GetRoot() {
+  CFX_XMLNode* pParent = this;
+  while (pParent->GetParent())
+    pParent = pParent->GetParent();
+
+  return pParent;
 }
 
-void CFX_XMLNode::DeleteChildren() {
-  CFX_XMLNode* pChild = m_pChild;
-  while (pChild) {
-    CFX_XMLNode* pNext = pChild->m_pNext;
-    delete pChild;
-    pChild = pNext;
-  }
-  m_pChild = nullptr;
-}
-
-int32_t CFX_XMLNode::CountChildNodes() const {
-  int32_t iCount = 0;
-  CFX_XMLNode* pChild = m_pChild;
-  while (pChild) {
-    iCount++;
-    pChild = pChild->m_pNext;
-  }
-  return iCount;
-}
-
-CFX_XMLNode* CFX_XMLNode::GetChildNode(int32_t index) const {
-  CFX_XMLNode* pChild = m_pChild;
-  while (pChild) {
-    if (index == 0) {
-      return pChild;
-    }
-    index--;
-    pChild = pChild->m_pNext;
-  }
-  return nullptr;
-}
-
-int32_t CFX_XMLNode::GetChildNodeIndex(CFX_XMLNode* pNode) const {
-  int32_t index = 0;
-  CFX_XMLNode* pChild = m_pChild;
-  while (pChild) {
-    if (pChild == pNode) {
-      return index;
-    }
-    index++;
-    pChild = pChild->m_pNext;
-  }
-  return -1;
-}
-
-CFX_XMLNode* CFX_XMLNode::GetPath(const wchar_t* pPath,
-                                  int32_t iLength,
-                                  bool bQualifiedName) const {
-  ASSERT(pPath);
-  if (iLength < 0) {
-    iLength = wcslen(pPath);
-  }
-  if (iLength == 0) {
-    return nullptr;
-  }
-  WideString csPath;
-  const wchar_t* pStart = pPath;
-  const wchar_t* pEnd = pPath + iLength;
-  wchar_t ch;
-  while (pStart < pEnd) {
-    ch = *pStart++;
-    if (ch == L'/')
-      break;
-    csPath += ch;
-  }
-  iLength -= pStart - pPath;
-  CFX_XMLNode* pFind = nullptr;
-  if (csPath.GetLength() < 1) {
-    pFind = GetNodeItem(CFX_XMLNode::Root);
-  } else if (csPath.Compare(L"..") == 0) {
-    pFind = m_pParent;
-  } else if (csPath.Compare(L".") == 0) {
-    pFind = (CFX_XMLNode*)this;
-  } else {
-    WideString wsTag;
-    CFX_XMLNode* pNode = m_pChild;
-    while (pNode) {
-      if (pNode->GetType() == FX_XMLNODE_Element) {
-        if (bQualifiedName)
-          wsTag = static_cast<CFX_XMLElement*>(pNode)->GetName();
-        else
-          wsTag = static_cast<CFX_XMLElement*>(pNode)->GetLocalTagName();
-
-        if (wsTag.Compare(csPath) == 0) {
-          if (iLength < 1)
-            pFind = pNode;
-          else
-            pFind = pNode->GetPath(pStart, iLength, bQualifiedName);
-
-          if (pFind)
-            return pFind;
-        }
-      }
-      pNode = pNode->m_pNext;
-    }
-  }
-  if (!pFind || iLength < 1)
-    return pFind;
-  return pFind->GetPath(pStart, iLength, bQualifiedName);
-}
-
-int32_t CFX_XMLNode::InsertChildNode(CFX_XMLNode* pNode, int32_t index) {
-  pNode->m_pParent = this;
-  if (!m_pChild) {
-    m_pChild = pNode;
-    pNode->m_pPrior = nullptr;
-    pNode->m_pNext = nullptr;
-    return 0;
-  }
-  if (index == 0) {
-    pNode->m_pNext = m_pChild;
-    pNode->m_pPrior = nullptr;
-    m_pChild->m_pPrior = pNode;
-    m_pChild = pNode;
-    return 0;
-  }
-  int32_t iCount = 0;
-  CFX_XMLNode* pFind = m_pChild;
-  while (++iCount != index && pFind->m_pNext) {
-    pFind = pFind->m_pNext;
-  }
-  pNode->m_pPrior = pFind;
-  pNode->m_pNext = pFind->m_pNext;
-  if (pFind->m_pNext)
-    pFind->m_pNext->m_pPrior = pNode;
-  pFind->m_pNext = pNode;
-  return iCount;
-}
-
-void CFX_XMLNode::RemoveChildNode(CFX_XMLNode* pNode) {
-  ASSERT(m_pChild && pNode);
-  if (m_pChild == pNode) {
-    m_pChild = pNode->m_pNext;
-  } else {
-    pNode->m_pPrior->m_pNext = pNode->m_pNext;
-  }
-  if (pNode->m_pNext)
-    pNode->m_pNext->m_pPrior = pNode->m_pPrior;
-  pNode->m_pParent = nullptr;
-  pNode->m_pNext = nullptr;
-  pNode->m_pPrior = nullptr;
-}
-
-CFX_XMLNode* CFX_XMLNode::GetNodeItem(CFX_XMLNode::NodeItem eItem) const {
-  switch (eItem) {
-    case CFX_XMLNode::Root: {
-      CFX_XMLNode* pParent = (CFX_XMLNode*)this;
-      while (pParent->m_pParent) {
-        pParent = pParent->m_pParent;
-      }
-      return pParent;
-    }
-    case CFX_XMLNode::Parent:
-      return m_pParent;
-    case CFX_XMLNode::FirstSibling: {
-      CFX_XMLNode* pItem = (CFX_XMLNode*)this;
-      while (pItem->m_pPrior) {
-        pItem = pItem->m_pPrior;
-      }
-      return pItem == (CFX_XMLNode*)this ? nullptr : pItem;
-    }
-    case CFX_XMLNode::PriorSibling:
-      return m_pPrior;
-    case CFX_XMLNode::NextSibling:
-      return m_pNext;
-    case CFX_XMLNode::LastSibling: {
-      CFX_XMLNode* pItem = (CFX_XMLNode*)this;
-      while (pItem->m_pNext)
-        pItem = pItem->m_pNext;
-      return pItem == (CFX_XMLNode*)this ? nullptr : pItem;
-    }
-    case CFX_XMLNode::FirstNeighbor: {
-      CFX_XMLNode* pParent = (CFX_XMLNode*)this;
-      while (pParent->m_pParent)
-        pParent = pParent->m_pParent;
-      return pParent == (CFX_XMLNode*)this ? nullptr : pParent;
-    }
-    case CFX_XMLNode::PriorNeighbor: {
-      if (!m_pPrior)
-        return m_pParent;
-
-      CFX_XMLNode* pItem = m_pPrior;
-      while (pItem->m_pChild) {
-        pItem = pItem->m_pChild;
-        while (pItem->m_pNext)
-          pItem = pItem->m_pNext;
-      }
-      return pItem;
-    }
-    case CFX_XMLNode::NextNeighbor: {
-      if (m_pChild)
-        return m_pChild;
-      if (m_pNext)
-        return m_pNext;
-      CFX_XMLNode* pItem = m_pParent;
-      while (pItem) {
-        if (pItem->m_pNext)
-          return pItem->m_pNext;
-        pItem = pItem->m_pParent;
-      }
-      return nullptr;
-    }
-    case CFX_XMLNode::LastNeighbor: {
-      CFX_XMLNode* pItem = (CFX_XMLNode*)this;
-      while (pItem->m_pParent) {
-        pItem = pItem->m_pParent;
-      }
-      while (true) {
-        while (pItem->m_pNext)
-          pItem = pItem->m_pNext;
-        if (!pItem->m_pChild)
-          break;
-        pItem = pItem->m_pChild;
-      }
-      return pItem == (CFX_XMLNode*)this ? nullptr : pItem;
-    }
-    case CFX_XMLNode::FirstChild:
-      return m_pChild;
-    case CFX_XMLNode::LastChild: {
-      if (!m_pChild)
-        return nullptr;
-
-      CFX_XMLNode* pChild = m_pChild;
-      while (pChild->m_pNext)
-        pChild = pChild->m_pNext;
-      return pChild;
-    }
-    default:
-      break;
-  }
-  return nullptr;
-}
-
-int32_t CFX_XMLNode::GetNodeLevel() const {
-  int32_t iLevel = 0;
-  const CFX_XMLNode* pItem = m_pParent;
-  while (pItem) {
-    iLevel++;
-    pItem = pItem->m_pParent;
-  }
-  return iLevel;
-}
-
-bool CFX_XMLNode::InsertNodeItem(CFX_XMLNode::NodeItem eItem,
-                                 CFX_XMLNode* pNode) {
-  switch (eItem) {
-    case CFX_XMLNode::NextSibling: {
-      pNode->m_pParent = m_pParent;
-      pNode->m_pNext = m_pNext;
-      pNode->m_pPrior = this;
-      if (m_pNext) {
-        m_pNext->m_pPrior = pNode;
-      }
-      m_pNext = pNode;
-      return true;
-    }
-    case CFX_XMLNode::PriorSibling: {
-      pNode->m_pParent = m_pParent;
-      pNode->m_pNext = this;
-      pNode->m_pPrior = m_pPrior;
-      if (m_pPrior) {
-        m_pPrior->m_pNext = pNode;
-      } else if (m_pParent) {
-        m_pParent->m_pChild = pNode;
-      }
-      m_pPrior = pNode;
-      return true;
-    }
-    default:
-      return false;
-  }
-}
-
-CFX_XMLNode* CFX_XMLNode::RemoveNodeItem(CFX_XMLNode::NodeItem eItem) {
-  CFX_XMLNode* pNode = nullptr;
-  switch (eItem) {
-    case CFX_XMLNode::NextSibling:
-      if (m_pNext) {
-        pNode = m_pNext;
-        m_pNext = pNode->m_pNext;
-        if (m_pNext) {
-          m_pNext->m_pPrior = this;
-        }
-        pNode->m_pParent = nullptr;
-        pNode->m_pNext = nullptr;
-        pNode->m_pPrior = nullptr;
-      }
-      break;
-    default:
-      break;
-  }
-  return pNode;
-}
-
-std::unique_ptr<CFX_XMLNode> CFX_XMLNode::Clone() {
-  return nullptr;
-}
-
-void CFX_XMLNode::SaveXMLNode(
-    const RetainPtr<CFX_SeekableStreamProxy>& pXMLStream) {
-  CFX_XMLNode* pNode = (CFX_XMLNode*)this;
-  switch (pNode->GetType()) {
-    case FX_XMLNODE_Instruction: {
-      WideString ws;
-      CFX_XMLInstruction* pInstruction = (CFX_XMLInstruction*)pNode;
-      if (pInstruction->GetName().CompareNoCase(L"xml") == 0) {
-        ws = L"<?xml version=\"1.0\" encoding=\"";
-        uint16_t wCodePage = pXMLStream->GetCodePage();
-        if (wCodePage == FX_CODEPAGE_UTF16LE) {
-          ws += L"UTF-16";
-        } else if (wCodePage == FX_CODEPAGE_UTF16BE) {
-          ws += L"UTF-16be";
-        } else {
-          ws += L"UTF-8";
-        }
-        ws += L"\"?>";
-        pXMLStream->WriteString(ws.AsStringView());
-      } else {
-        ws = WideString::Format(L"<?%ls", pInstruction->GetName().c_str());
-        pXMLStream->WriteString(ws.AsStringView());
-
-        for (auto it : pInstruction->GetAttributes()) {
-          WideString wsValue = it.second;
-          wsValue.Replace(L"&", L"&amp;");
-          wsValue.Replace(L"<", L"&lt;");
-          wsValue.Replace(L">", L"&gt;");
-          wsValue.Replace(L"\'", L"&apos;");
-          wsValue.Replace(L"\"", L"&quot;");
-
-          ws = L" ";
-          ws += it.first;
-          ws += L"=\"";
-          ws += wsValue;
-          ws += L"\"";
-          pXMLStream->WriteString(ws.AsStringView());
-        }
-
-        for (auto target : pInstruction->GetTargetData()) {
-          ws = L" \"";
-          ws += target;
-          ws += L"\"";
-          pXMLStream->WriteString(ws.AsStringView());
-        }
-        ws = L"?>";
-        pXMLStream->WriteString(ws.AsStringView());
-      }
-      break;
-    }
-    case FX_XMLNODE_Element: {
-      WideString ws;
-      ws = L"<";
-      ws += static_cast<CFX_XMLElement*>(pNode)->GetName();
-      pXMLStream->WriteString(ws.AsStringView());
-
-      for (auto it : static_cast<CFX_XMLElement*>(pNode)->GetAttributes()) {
-        WideString wsValue = it.second;
-        wsValue.Replace(L"&", L"&amp;");
-        wsValue.Replace(L"<", L"&lt;");
-        wsValue.Replace(L">", L"&gt;");
-        wsValue.Replace(L"\'", L"&apos;");
-        wsValue.Replace(L"\"", L"&quot;");
-
-        ws = L" ";
-        ws += it.first;
-        ws += L"=\"";
-        ws += wsValue;
-        ws += L"\"";
-        pXMLStream->WriteString(ws.AsStringView());
-      }
-      if (pNode->m_pChild) {
-        ws = L"\n>";
-        pXMLStream->WriteString(ws.AsStringView());
-        CFX_XMLNode* pChild = pNode->m_pChild;
-        while (pChild) {
-          pChild->SaveXMLNode(pXMLStream);
-          pChild = pChild->m_pNext;
-        }
-        ws = L"</";
-        ws += static_cast<CFX_XMLElement*>(pNode)->GetName();
-        ws += L"\n>";
-      } else {
-        ws = L"\n/>";
-      }
-      pXMLStream->WriteString(ws.AsStringView());
-      break;
-    }
-    case FX_XMLNODE_Text: {
-      WideString ws = static_cast<CFX_XMLText*>(pNode)->GetText();
-      ws.Replace(L"&", L"&amp;");
-      ws.Replace(L"<", L"&lt;");
-      ws.Replace(L">", L"&gt;");
-      ws.Replace(L"\'", L"&apos;");
-      ws.Replace(L"\"", L"&quot;");
-      pXMLStream->WriteString(ws.AsStringView());
-      break;
-    }
-    case FX_XMLNODE_CharData: {
-      WideString ws = L"<![CDATA[";
-      ws += static_cast<CFX_XMLCharData*>(pNode)->GetText();
-      ws += L"]]>";
-      pXMLStream->WriteString(ws.AsStringView());
-      break;
-    }
-    case FX_XMLNODE_Unknown:
-    default:
-      break;
-  }
+WideString CFX_XMLNode::EncodeEntities(const WideString& value) {
+  WideString ret = value;
+  ret.Replace(L"&", L"&amp;");
+  ret.Replace(L"<", L"&lt;");
+  ret.Replace(L">", L"&gt;");
+  ret.Replace(L"\'", L"&apos;");
+  ret.Replace(L"\"", L"&quot;");
+  return ret;
 }
diff --git a/core/fxcrt/xml/cfx_xmlnode.h b/core/fxcrt/xml/cfx_xmlnode.h
index 278b3bf..d4d48b1 100644
--- a/core/fxcrt/xml/cfx_xmlnode.h
+++ b/core/fxcrt/xml/cfx_xmlnode.h
@@ -7,69 +7,34 @@
 #ifndef CORE_FXCRT_XML_CFX_XMLNODE_H_
 #define CORE_FXCRT_XML_CFX_XMLNODE_H_
 
-#include <memory>
-
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/tree_node.h"
 
-enum FX_XMLNODETYPE {
-  FX_XMLNODE_Unknown = 0,
-  FX_XMLNODE_Instruction,
-  FX_XMLNODE_Element,
-  FX_XMLNODE_Text,
-  FX_XMLNODE_CharData,
-};
+class CFX_XMLDocument;
 
-struct FX_XMLNODE {
-  int32_t iNodeNum;
-  FX_XMLNODETYPE eNodeType;
-};
-
-class CFX_XMLNode {
+class CFX_XMLNode : public TreeNode<CFX_XMLNode> {
  public:
-  enum NodeItem {
-    Root = 0,
-    Parent,
-    FirstSibling,
-    PriorSibling,
-    NextSibling,
-    LastSibling,
-    FirstNeighbor,
-    PriorNeighbor,
-    NextNeighbor,
-    LastNeighbor,
-    FirstChild,
-    LastChild
+  enum class Type {
+    kInstruction = 0,
+    kElement,
+    kText,
+    kCharData,
   };
 
   CFX_XMLNode();
-  virtual ~CFX_XMLNode();
+  ~CFX_XMLNode() override;
 
-  virtual FX_XMLNODETYPE GetType() const;
-  virtual std::unique_ptr<CFX_XMLNode> Clone();
+  virtual Type GetType() const = 0;
+  virtual CFX_XMLNode* Clone(CFX_XMLDocument* doc) = 0;
+  virtual void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) = 0;
 
-  int32_t CountChildNodes() const;
-  CFX_XMLNode* GetChildNode(int32_t index) const;
-  int32_t GetChildNodeIndex(CFX_XMLNode* pNode) const;
-  int32_t InsertChildNode(CFX_XMLNode* pNode, int32_t index = -1);
-  void RemoveChildNode(CFX_XMLNode* pNode);
-  void DeleteChildren();
+  CFX_XMLNode* GetRoot();
+  void InsertChildNode(CFX_XMLNode* pNode, int32_t index);
 
-  CFX_XMLNode* GetPath(const wchar_t* pPath,
-                       int32_t iLength = -1,
-                       bool bQualifiedName = true) const;
-
-  int32_t GetNodeLevel() const;
-  CFX_XMLNode* GetNodeItem(CFX_XMLNode::NodeItem eItem) const;
-  bool InsertNodeItem(CFX_XMLNode::NodeItem eItem, CFX_XMLNode* pNode);
-  CFX_XMLNode* RemoveNodeItem(CFX_XMLNode::NodeItem eItem);
-
-  void SaveXMLNode(const RetainPtr<CFX_SeekableStreamProxy>& pXMLStream);
-
-  CFX_XMLNode* m_pParent;
-  CFX_XMLNode* m_pChild;
-  CFX_XMLNode* m_pPrior;
-  CFX_XMLNode* m_pNext;
+ protected:
+  WideString EncodeEntities(const WideString& value);
 };
 
 #endif  // CORE_FXCRT_XML_CFX_XMLNODE_H_
diff --git a/core/fxcrt/xml/cfx_xmlnode_unittest.cpp b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
new file mode 100644
index 0000000..c879cfd
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
@@ -0,0 +1,260 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+WideString ChildrenString(CFX_XMLElement* pParent) {
+  WideString result;
+  for (CFX_XMLNode* pChild = pParent->GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    result += static_cast<CFX_XMLElement*>(pChild)->GetName();
+  }
+  return result;
+}
+
+WideString ReverseChildrenString(CFX_XMLElement* pParent) {
+  WideString result;
+  for (CFX_XMLNode* pChild = pParent->GetLastChild(); pChild;
+       pChild = pChild->GetPrevSibling()) {
+    result = static_cast<CFX_XMLElement*>(pChild)->GetName() + result;
+  }
+  return result;
+}
+
+}  // namespace
+
+TEST(CFX_XMLNodeTest, GetParent) {
+  CFX_XMLElement node1(L"node");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+
+  node1.AppendLastChild(&node2);
+  node2.AppendLastChild(&node3);
+
+  EXPECT_EQ(nullptr, node1.GetParent());
+  EXPECT_EQ(&node1, node2.GetParent());
+  EXPECT_EQ(&node2, node3.GetParent());
+}
+
+TEST(CFX_XMLNodeTest, GetRoot) {
+  CFX_XMLElement node1(L"node");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+
+  node1.AppendLastChild(&node2);
+  node2.AppendLastChild(&node3);
+
+  EXPECT_EQ(&node1, node1.GetRoot());
+  EXPECT_EQ(&node1, node2.GetRoot());
+  EXPECT_EQ(&node1, node3.GetRoot());
+}
+
+TEST(CFX_XMLNodeTest, GetChildren) {
+  CFX_XMLElement node1(L"node");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+  CFX_XMLElement node4(L"node4");
+
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node4);
+  node2.AppendLastChild(&node3);
+
+  EXPECT_EQ(&node2, node1.GetFirstChild());
+
+  EXPECT_EQ(&node4, node2.GetNextSibling());
+  EXPECT_EQ(&node3, node2.GetFirstChild());
+
+  EXPECT_TRUE(node3.GetNextSibling() == nullptr);
+  EXPECT_TRUE(node3.GetFirstChild() == nullptr);
+
+  EXPECT_TRUE(node4.GetNextSibling() == nullptr);
+  EXPECT_TRUE(node4.GetFirstChild() == nullptr);
+}
+
+TEST(CFX_XMLNodeTest, DeleteChildren) {
+  CFX_XMLElement node1(L"node");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+  CFX_XMLElement node4(L"node4");
+
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node4);
+  node2.AppendLastChild(&node3);
+
+  node1.RemoveAllChildren();
+  EXPECT_TRUE(node1.GetFirstChild() == nullptr);
+  EXPECT_TRUE(node2.GetParent() == nullptr);
+  EXPECT_TRUE(node4.GetParent() == nullptr);
+
+  // node2 and node4 should no longer be siblings.
+  EXPECT_TRUE(node2.GetNextSibling() == nullptr);
+  EXPECT_TRUE(node4.GetPrevSibling() == nullptr);
+
+  // Deleting children doesn't change deleted substructure
+  EXPECT_EQ(&node3, node2.GetFirstChild());
+  EXPECT_TRUE(node3.GetParent() == &node2);
+}
+
+TEST(CFX_XMLNodeTest, AddingChildren) {
+  CFX_XMLElement parent(L"Root");
+  CFX_XMLElement nodeA(L"A");
+  CFX_XMLElement nodeB(L"B");
+
+  parent.AppendLastChild(&nodeA);
+  parent.AppendLastChild(&nodeB);
+
+  EXPECT_EQ(L"AB", ChildrenString(&parent));
+  EXPECT_EQ(L"AB", ReverseChildrenString(&parent));
+  EXPECT_EQ(&parent, nodeA.GetParent());
+  EXPECT_EQ(&parent, nodeB.GetParent());
+  EXPECT_EQ(&nodeA, parent.GetFirstChild());
+  EXPECT_EQ(&nodeB, nodeA.GetNextSibling());
+  EXPECT_TRUE(nodeB.GetNextSibling() == nullptr);
+
+  // Insert to negative appends last child.
+  CFX_XMLElement nodeC(L"C");
+  parent.InsertChildNode(&nodeC, -1);
+  EXPECT_EQ(L"ABC", ChildrenString(&parent));
+  EXPECT_EQ(L"ABC", ReverseChildrenString(&parent));
+  EXPECT_EQ(&parent, nodeC.GetParent());
+  EXPECT_EQ(&nodeC, nodeB.GetNextSibling());
+  EXPECT_TRUE(nodeC.GetNextSibling() == nullptr);
+
+  // Insertion occurs before a zero based index.
+  CFX_XMLElement nodeD(L"D");
+  parent.InsertChildNode(&nodeD, 1);
+  EXPECT_EQ(L"ADBC", ChildrenString(&parent));
+  EXPECT_EQ(L"ADBC", ReverseChildrenString(&parent));
+
+  // Insert to 0 appends first child.
+  CFX_XMLElement nodeE(L"E");
+  parent.InsertChildNode(&nodeE, 0);
+  EXPECT_EQ(L"EADBC", ChildrenString(&parent));
+  EXPECT_EQ(L"EADBC", ReverseChildrenString(&parent));
+
+  // Insert to out-of-bounds index appends last child.
+  CFX_XMLElement nodeF(L"F");
+  parent.InsertChildNode(&nodeF, 10);
+  EXPECT_EQ(L"EADBCF", ChildrenString(&parent));
+  EXPECT_EQ(L"EADBCF", ReverseChildrenString(&parent));
+}
+
+TEST(CFX_XMLNodeTest, RemovingMiddleChild) {
+  CFX_XMLElement node1(L"node1");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+  CFX_XMLElement node4(L"node4");
+
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node3);
+  node1.AppendLastChild(&node4);
+
+  EXPECT_EQ(L"node2node3node4", ChildrenString(&node1));
+  EXPECT_EQ(L"node2node3node4", ReverseChildrenString(&node1));
+  EXPECT_EQ(&node2, node1.GetFirstChild());
+  EXPECT_EQ(&node3, node2.GetNextSibling());
+  EXPECT_EQ(&node4, node3.GetNextSibling());
+  EXPECT_TRUE(node4.GetNextSibling() == nullptr);
+
+  node1.RemoveChild(&node3);
+
+  EXPECT_EQ(L"node2node4", ChildrenString(&node1));
+  EXPECT_EQ(L"node2node4", ReverseChildrenString(&node1));
+  EXPECT_TRUE(node3.GetParent() == nullptr);
+  EXPECT_TRUE(node3.GetNextSibling() == nullptr);
+  EXPECT_TRUE(node3.GetPrevSibling() == nullptr);
+  EXPECT_EQ(&node2, node1.GetFirstChild());
+  EXPECT_EQ(&node4, node2.GetNextSibling());
+  EXPECT_EQ(&node2, node4.GetPrevSibling());
+  EXPECT_TRUE(node4.GetNextSibling() == nullptr);
+}
+
+TEST(CFX_XMLNodeTest, RemovingFirstChild) {
+  CFX_XMLElement node1(L"node1");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+  CFX_XMLElement node4(L"node4");
+
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node3);
+  node1.AppendLastChild(&node4);
+
+  EXPECT_EQ(L"node2node3node4", ChildrenString(&node1));
+  EXPECT_EQ(L"node2node3node4", ReverseChildrenString(&node1));
+  EXPECT_EQ(&node2, node1.GetFirstChild());
+  EXPECT_EQ(&node3, node2.GetNextSibling());
+  EXPECT_EQ(&node4, node3.GetNextSibling());
+  EXPECT_TRUE(node4.GetNextSibling() == nullptr);
+
+  node1.RemoveChild(&node2);
+
+  EXPECT_EQ(L"node3node4", ChildrenString(&node1));
+  EXPECT_EQ(L"node3node4", ReverseChildrenString(&node1));
+  EXPECT_TRUE(node2.GetParent() == nullptr);
+  EXPECT_TRUE(node2.GetNextSibling() == nullptr);
+  EXPECT_TRUE(node2.GetPrevSibling() == nullptr);
+  EXPECT_EQ(&node3, node1.GetFirstChild());
+  EXPECT_TRUE(node3.GetPrevSibling() == nullptr);
+  EXPECT_EQ(&node4, node3.GetNextSibling());
+  EXPECT_TRUE(node4.GetNextSibling() == nullptr);
+}
+
+TEST(CFX_XMLNodeTest, RemovingLastChild) {
+  CFX_XMLElement node1(L"node1");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+  CFX_XMLElement node4(L"node4");
+
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node3);
+  node1.AppendLastChild(&node4);
+
+  EXPECT_EQ(L"node2node3node4", ChildrenString(&node1));
+  EXPECT_EQ(L"node2node3node4", ReverseChildrenString(&node1));
+  EXPECT_EQ(&node2, node1.GetFirstChild());
+  EXPECT_EQ(&node3, node2.GetNextSibling());
+  EXPECT_EQ(&node4, node3.GetNextSibling());
+  EXPECT_TRUE(node4.GetNextSibling() == nullptr);
+
+  node1.RemoveChild(&node4);
+
+  EXPECT_EQ(L"node2node3", ChildrenString(&node1));
+  EXPECT_EQ(L"node2node3", ReverseChildrenString(&node1));
+  EXPECT_TRUE(node4.GetParent() == nullptr);
+  EXPECT_TRUE(node4.GetNextSibling() == nullptr);
+  EXPECT_TRUE(node4.GetPrevSibling() == nullptr);
+  EXPECT_EQ(&node2, node1.GetFirstChild());
+  EXPECT_EQ(&node3, node2.GetNextSibling());
+  EXPECT_TRUE(node3.GetNextSibling() == nullptr);
+}
+
+TEST(CFX_XMLNodeTest, RemovingOnlyChild) {
+  CFX_XMLElement node1(L"node1");
+  CFX_XMLElement node2(L"node2");
+
+  node1.AppendLastChild(&node2);
+
+  EXPECT_EQ(&node2, node1.GetFirstChild());
+  EXPECT_TRUE(node2.GetNextSibling() == nullptr);
+
+  node1.RemoveChild(&node2);
+  EXPECT_TRUE(node2.GetParent() == nullptr);
+
+  EXPECT_TRUE(node1.GetFirstChild() == nullptr);
+  EXPECT_TRUE(node2.GetNextSibling() == nullptr);
+  EXPECT_TRUE(node2.GetPrevSibling() == nullptr);
+}
+
+TEST(CFX_XMLNodeTest, RemoveMissingChild) {
+  CFX_XMLElement node1(L"node1");
+  CFX_XMLElement node2(L"node2");
+  CFX_XMLElement node3(L"node3");
+
+  node1.AppendLastChild(&node2);
+  EXPECT_DEATH(node1.RemoveChild(&node3), "");
+}
diff --git a/core/fxcrt/xml/cfx_xmlparser.cpp b/core/fxcrt/xml/cfx_xmlparser.cpp
index 0f08b06..9393bbd 100644
--- a/core/fxcrt/xml/cfx_xmlparser.cpp
+++ b/core/fxcrt/xml/cfx_xmlparser.cpp
@@ -6,162 +6,537 @@
 
 #include "core/fxcrt/xml/cfx_xmlparser.h"
 
+#include <algorithm>
+#include <cwctype>
+#include <iterator>
+#include <stack>
+#include <utility>
+
+#include "core/fxcrt/cfx_seekablestreamproxy.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/xml/cfx_xmlchardata.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlinstruction.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "third_party/base/ptr_util.h"
 
-CFX_XMLParser::CFX_XMLParser(CFX_XMLNode* pParent,
-                             const RetainPtr<CFX_SeekableStreamProxy>& pStream)
-    : m_nElementStart(0),
-      m_dwCheckStatus(0),
-      m_dwCurrentCheckStatus(0),
-      m_pStream(pStream),
-      m_pParser(pdfium::MakeUnique<CFX_XMLSyntaxParser>(m_pStream)),
-      m_pParent(pParent),
-      m_pChild(nullptr),
-      m_syntaxParserResult(FX_XmlSyntaxResult::None) {
-  ASSERT(m_pParent && m_pStream);
-  m_NodeStack.push(m_pParent);
+namespace {
+
+constexpr size_t kCurrentTextReserve = 128;
+constexpr uint32_t kMaxCharRange = 0x10ffff;
+
+bool IsXMLWhiteSpace(wchar_t ch) {
+  return ch == L' ' || ch == 0x0A || ch == 0x0D || ch == 0x09;
 }
 
-CFX_XMLParser::~CFX_XMLParser() {}
+struct FX_XMLNAMECHAR {
+  uint16_t wStart;
+  uint16_t wEnd;
+  bool bStartChar;
+};
 
-int32_t CFX_XMLParser::DoParser() {
-  if (m_syntaxParserResult == FX_XmlSyntaxResult::Error)
-    return -1;
-  if (m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString)
-    return 100;
+const FX_XMLNAMECHAR g_XMLNameChars[] = {
+    {L'-', L'.', false},    {L'0', L'9', false},     {L':', L':', false},
+    {L'A', L'Z', true},     {L'_', L'_', true},      {L'a', L'z', true},
+    {0xB7, 0xB7, false},    {0xC0, 0xD6, true},      {0xD8, 0xF6, true},
+    {0xF8, 0x02FF, true},   {0x0300, 0x036F, false}, {0x0370, 0x037D, true},
+    {0x037F, 0x1FFF, true}, {0x200C, 0x200D, true},  {0x203F, 0x2040, false},
+    {0x2070, 0x218F, true}, {0x2C00, 0x2FEF, true},  {0x3001, 0xD7FF, true},
+    {0xF900, 0xFDCF, true}, {0xFDF0, 0xFFFD, true},
+};
 
+}  // namespace
+
+// static
+bool CFX_XMLParser::IsXMLNameChar(wchar_t ch, bool bFirstChar) {
+  auto* it = std::lower_bound(
+      std::begin(g_XMLNameChars), std::end(g_XMLNameChars), ch,
+      [](const FX_XMLNAMECHAR& arg, wchar_t ch) { return arg.wEnd < ch; });
+  return it != std::end(g_XMLNameChars) && ch >= it->wStart &&
+         (!bFirstChar || it->bStartChar);
+}
+
+CFX_XMLParser::CFX_XMLParser(const RetainPtr<IFX_SeekableReadStream>& pStream) {
+  ASSERT(pStream);
+
+  auto proxy = pdfium::MakeRetain<CFX_SeekableStreamProxy>(pStream);
+  uint16_t wCodePage = proxy->GetCodePage();
+  if (wCodePage != FX_CODEPAGE_UTF16LE && wCodePage != FX_CODEPAGE_UTF16BE &&
+      wCodePage != FX_CODEPAGE_UTF8) {
+    proxy->SetCodePage(FX_CODEPAGE_UTF8);
+  }
+  stream_ = proxy;
+
+  xml_plane_size_ = std::min(
+      xml_plane_size_, pdfium::base::checked_cast<size_t>(stream_->GetSize()));
+
+  current_text_.reserve(kCurrentTextReserve);
+}
+
+CFX_XMLParser::~CFX_XMLParser() = default;
+
+std::unique_ptr<CFX_XMLDocument> CFX_XMLParser::Parse() {
+  auto doc = pdfium::MakeUnique<CFX_XMLDocument>();
+  current_node_ = doc->GetRoot();
+
+  return DoSyntaxParse(doc.get()) ? std::move(doc) : nullptr;
+}
+
+bool CFX_XMLParser::DoSyntaxParse(CFX_XMLDocument* doc) {
+  if (xml_plane_size_ <= 0)
+    return false;
+
+  FX_SAFE_SIZE_T alloc_size_safe = xml_plane_size_;
+  alloc_size_safe += 1;  // For NUL.
+  if (!alloc_size_safe.IsValid())
+    return false;
+
+  FX_FILESIZE current_buffer_idx = 0;
+  FX_FILESIZE buffer_size = 0;
+
+  std::vector<wchar_t, FxAllocAllocator<wchar_t>> buffer;
+  buffer.resize(alloc_size_safe.ValueOrDie());
+
+  std::stack<wchar_t> character_to_skip_too_stack;
+  std::stack<CFX_XMLNode::Type> node_type_stack;
+  WideString current_attribute_name;
+  FDE_XmlSyntaxState current_parser_state = FDE_XmlSyntaxState::Text;
   int32_t iCount = 0;
-  while (true) {
-    m_syntaxParserResult = m_pParser->DoSyntaxParse();
-    switch (m_syntaxParserResult) {
-      case FX_XmlSyntaxResult::InstructionOpen:
-        break;
-      case FX_XmlSyntaxResult::InstructionClose:
-        if (m_pChild) {
-          if (m_pChild->GetType() != FX_XMLNODE_Instruction) {
-            m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-            break;
-          }
-        }
-        m_pChild = m_pParent;
-        break;
-      case FX_XmlSyntaxResult::ElementOpen:
-        if (m_dwCheckStatus != 0x03 && m_NodeStack.size() == 2)
-          m_nElementStart = m_pParser->GetCurrentPos() - 1;
-        break;
-      case FX_XmlSyntaxResult::ElementBreak:
-        break;
-      case FX_XmlSyntaxResult::ElementClose:
-        if (m_pChild->GetType() != FX_XMLNODE_Element) {
-          m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-          break;
-        }
-        m_ws1 = m_pParser->GetTagName();
-        m_ws2 = static_cast<CFX_XMLElement*>(m_pChild)->GetName();
-        if (m_ws1.GetLength() > 0 && m_ws1 != m_ws2) {
-          m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-          break;
-        }
-        if (!m_NodeStack.empty())
-          m_NodeStack.pop();
-        if (m_NodeStack.empty()) {
-          m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-          break;
-        }
-        if (m_dwCurrentCheckStatus != 0 && m_NodeStack.size() == 2) {
-          m_nSize[m_dwCurrentCheckStatus - 1] =
-              m_pParser->GetCurrentBinaryPos() -
-              m_nStart[m_dwCurrentCheckStatus - 1];
-          m_dwCurrentCheckStatus = 0;
-        }
-        m_pParent = m_NodeStack.top();
-        m_pChild = m_pParent;
-        iCount++;
-        break;
-      case FX_XmlSyntaxResult::TargetName:
-        m_ws1 = m_pParser->GetTargetName();
-        if (m_ws1 == L"originalXFAVersion" || m_ws1 == L"acrobat") {
-          m_pChild = new CFX_XMLInstruction(m_ws1);
-          m_pParent->InsertChildNode(m_pChild);
-        } else {
-          m_pChild = nullptr;
-        }
-        m_ws1.clear();
-        break;
-      case FX_XmlSyntaxResult::TagName:
-        m_ws1 = m_pParser->GetTagName();
-        m_pChild = new CFX_XMLElement(m_ws1);
-        m_pParent->InsertChildNode(m_pChild);
-        m_NodeStack.push(m_pChild);
-        m_pParent = m_pChild;
+  wchar_t current_quote_character = 0;
+  wchar_t current_character_to_skip_to = 0;
 
-        if (m_dwCheckStatus != 0x03 && m_NodeStack.size() == 3) {
-          WideString wsTag =
-              static_cast<CFX_XMLElement*>(m_pChild)->GetLocalTagName();
-          if (wsTag == L"template") {
-            m_dwCheckStatus |= 0x01;
-            m_dwCurrentCheckStatus = 0x01;
-            m_nStart[0] = m_pParser->GetCurrentBinaryPos() -
-                          (m_pParser->GetCurrentPos() - m_nElementStart);
-          } else if (wsTag == L"datasets") {
-            m_dwCheckStatus |= 0x02;
-            m_dwCurrentCheckStatus = 0x02;
-            m_nStart[1] = m_pParser->GetCurrentBinaryPos() -
-                          (m_pParser->GetCurrentPos() - m_nElementStart);
+  while (true) {
+    if (current_buffer_idx >= buffer_size) {
+      if (stream_->IsEOF())
+        return true;
+
+      size_t buffer_chars = stream_->ReadBlock(buffer.data(), xml_plane_size_);
+      if (buffer_chars == 0)
+        return true;
+
+      current_buffer_idx = 0;
+      buffer_size = buffer_chars;
+    }
+
+    while (current_buffer_idx < buffer_size) {
+      wchar_t ch = buffer[current_buffer_idx];
+      switch (current_parser_state) {
+        case FDE_XmlSyntaxState::Text:
+          if (ch == L'<') {
+            if (!current_text_.empty()) {
+              current_node_->AppendLastChild(
+                  doc->CreateNode<CFX_XMLText>(GetTextData()));
+            } else {
+              current_buffer_idx++;
+              current_parser_state = FDE_XmlSyntaxState::Node;
+            }
+          } else {
+            // Fail if there is text outside of the root element, ignore
+            // whitespace/null.
+            if (node_type_stack.empty() && ch && !FXSYS_iswspace(ch))
+              return false;
+            ProcessTextChar(ch);
+            current_buffer_idx++;
           }
-        }
-        break;
-      case FX_XmlSyntaxResult::AttriName:
-        m_ws1 = m_pParser->GetAttributeName();
-        break;
-      case FX_XmlSyntaxResult::AttriValue:
-        if (m_pChild) {
-          m_ws2 = m_pParser->GetAttributeName();
-          if (m_pChild->GetType() == FX_XMLNODE_Element)
-            static_cast<CFX_XMLElement*>(m_pChild)->SetString(m_ws1, m_ws2);
-        }
-        m_ws1.clear();
-        break;
-      case FX_XmlSyntaxResult::Text:
-        m_ws1 = m_pParser->GetTextData();
-        m_pChild = new CFX_XMLText(m_ws1);
-        m_pParent->InsertChildNode(m_pChild);
-        m_pChild = m_pParent;
-        break;
-      case FX_XmlSyntaxResult::CData:
-        m_ws1 = m_pParser->GetTextData();
-        m_pChild = new CFX_XMLCharData(m_ws1);
-        m_pParent->InsertChildNode(m_pChild);
-        m_pChild = m_pParent;
-        break;
-      case FX_XmlSyntaxResult::TargetData:
-        if (m_pChild) {
-          if (m_pChild->GetType() != FX_XMLNODE_Instruction) {
-            m_syntaxParserResult = FX_XmlSyntaxResult::Error;
+          break;
+        case FDE_XmlSyntaxState::Node:
+          if (ch == L'!') {
+            current_buffer_idx++;
+            current_parser_state = FDE_XmlSyntaxState::SkipCommentOrDecl;
+          } else if (ch == L'/') {
+            current_buffer_idx++;
+            current_parser_state = FDE_XmlSyntaxState::CloseElement;
+          } else if (ch == L'?') {
+            node_type_stack.push(CFX_XMLNode::Type::kInstruction);
+            current_buffer_idx++;
+            current_parser_state = FDE_XmlSyntaxState::Target;
+          } else {
+            node_type_stack.push(CFX_XMLNode::Type::kElement);
+            current_parser_state = FDE_XmlSyntaxState::Tag;
+          }
+          break;
+        case FDE_XmlSyntaxState::Target:
+          if (!IsXMLNameChar(ch, current_text_.empty())) {
+            if (current_text_.empty())
+              return false;
+
+            current_parser_state = FDE_XmlSyntaxState::TargetData;
+
+            WideString target_name = GetTextData();
+            if (target_name.EqualsASCII("originalXFAVersion") ||
+                target_name.EqualsASCII("acrobat")) {
+              auto* node = doc->CreateNode<CFX_XMLInstruction>(target_name);
+              current_node_->AppendLastChild(node);
+              current_node_ = node;
+            }
+          } else {
+            current_text_.push_back(ch);
+            current_buffer_idx++;
+          }
+          break;
+        case FDE_XmlSyntaxState::Tag:
+          if (!IsXMLNameChar(ch, current_text_.empty())) {
+            if (current_text_.empty())
+              return false;
+
+            current_parser_state = FDE_XmlSyntaxState::AttriName;
+
+            auto* child = doc->CreateNode<CFX_XMLElement>(GetTextData());
+            current_node_->AppendLastChild(child);
+            current_node_ = child;
+          } else {
+            current_text_.push_back(ch);
+            current_buffer_idx++;
+          }
+          break;
+        case FDE_XmlSyntaxState::AttriName:
+          if (current_text_.empty() && IsXMLWhiteSpace(ch)) {
+            current_buffer_idx++;
             break;
           }
-          auto* instruction = static_cast<CFX_XMLInstruction*>(m_pChild);
-          if (!m_ws1.IsEmpty())
-            instruction->AppendData(m_ws1);
-          instruction->AppendData(m_pParser->GetTargetData());
+          if (!IsXMLNameChar(ch, current_text_.empty())) {
+            if (current_text_.empty()) {
+              if (node_type_stack.top() == CFX_XMLNode::Type::kElement) {
+                if (ch == L'>' || ch == L'/') {
+                  current_parser_state = FDE_XmlSyntaxState::BreakElement;
+                  break;
+                }
+              } else if (node_type_stack.top() ==
+                         CFX_XMLNode::Type::kInstruction) {
+                if (ch == L'?') {
+                  current_parser_state = FDE_XmlSyntaxState::CloseInstruction;
+                  current_buffer_idx++;
+                } else {
+                  current_parser_state = FDE_XmlSyntaxState::TargetData;
+                }
+                break;
+              }
+              return false;
+            } else {
+              if (node_type_stack.top() == CFX_XMLNode::Type::kInstruction) {
+                if (ch != '=' && !IsXMLWhiteSpace(ch)) {
+                  current_parser_state = FDE_XmlSyntaxState::TargetData;
+                  break;
+                }
+              }
+              current_parser_state = FDE_XmlSyntaxState::AttriEqualSign;
+              current_attribute_name = GetTextData();
+            }
+          } else {
+            current_text_.push_back(ch);
+            current_buffer_idx++;
+          }
+          break;
+        case FDE_XmlSyntaxState::AttriEqualSign:
+          if (IsXMLWhiteSpace(ch)) {
+            current_buffer_idx++;
+            break;
+          }
+          if (ch != L'=') {
+            if (node_type_stack.top() == CFX_XMLNode::Type::kInstruction) {
+              current_parser_state = FDE_XmlSyntaxState::TargetData;
+              break;
+            }
+            return false;
+          } else {
+            current_parser_state = FDE_XmlSyntaxState::AttriQuotation;
+            current_buffer_idx++;
+          }
+          break;
+        case FDE_XmlSyntaxState::AttriQuotation:
+          if (IsXMLWhiteSpace(ch)) {
+            current_buffer_idx++;
+            break;
+          }
+          if (ch != L'\"' && ch != L'\'') {
+            return false;
+          }
+
+          current_quote_character = ch;
+          current_parser_state = FDE_XmlSyntaxState::AttriValue;
+          current_buffer_idx++;
+          break;
+        case FDE_XmlSyntaxState::AttriValue:
+          if (ch == current_quote_character) {
+            if (entity_start_ > -1)
+              return false;
+
+            current_quote_character = 0;
+            current_buffer_idx++;
+            current_parser_state = FDE_XmlSyntaxState::AttriName;
+
+            CFX_XMLElement* elem = ToXMLElement(current_node_);
+            if (elem)
+              elem->SetAttribute(current_attribute_name, GetTextData());
+
+            current_attribute_name.clear();
+          } else {
+            ProcessTextChar(ch);
+            current_buffer_idx++;
+          }
+          break;
+        case FDE_XmlSyntaxState::CloseInstruction:
+          if (ch != L'>') {
+            current_text_.push_back(ch);
+            current_parser_state = FDE_XmlSyntaxState::TargetData;
+          } else if (!current_text_.empty()) {
+            ProcessTargetData();
+          } else {
+            current_buffer_idx++;
+            if (node_type_stack.empty())
+              return false;
+
+            node_type_stack.pop();
+            current_parser_state = FDE_XmlSyntaxState::Text;
+
+            if (current_node_ &&
+                current_node_->GetType() == CFX_XMLNode::Type::kInstruction)
+              current_node_ = current_node_->GetParent();
+          }
+          break;
+        case FDE_XmlSyntaxState::BreakElement:
+          if (ch == L'>') {
+            current_parser_state = FDE_XmlSyntaxState::Text;
+          } else if (ch == L'/') {
+            current_parser_state = FDE_XmlSyntaxState::CloseElement;
+          } else {
+            return false;
+          }
+          current_buffer_idx++;
+          break;
+        case FDE_XmlSyntaxState::CloseElement:
+          if (!IsXMLNameChar(ch, current_text_.empty())) {
+            if (ch == L'>') {
+              if (node_type_stack.empty())
+                return false;
+
+              node_type_stack.pop();
+              current_parser_state = FDE_XmlSyntaxState::Text;
+
+              CFX_XMLElement* element = ToXMLElement(current_node_);
+              if (!element)
+                return false;
+
+              WideString element_name = GetTextData();
+              if (element_name.GetLength() > 0 &&
+                  element_name != element->GetName()) {
+                return false;
+              }
+
+              current_node_ = current_node_->GetParent();
+              iCount++;
+            } else if (!IsXMLWhiteSpace(ch)) {
+              return false;
+            }
+          } else {
+            current_text_.push_back(ch);
+          }
+          current_buffer_idx++;
+          break;
+        case FDE_XmlSyntaxState::SkipCommentOrDecl: {
+          auto current_span =
+              pdfium::make_span(buffer).subspan(current_buffer_idx);
+          if (FXSYS_wcsnicmp(current_span.data(), L"--", 2) == 0) {
+            current_buffer_idx += 2;
+            current_parser_state = FDE_XmlSyntaxState::SkipComment;
+          } else if (FXSYS_wcsnicmp(current_span.data(), L"[CDATA[", 7) == 0) {
+            current_buffer_idx += 7;
+            current_parser_state = FDE_XmlSyntaxState::SkipCData;
+          } else {
+            current_parser_state = FDE_XmlSyntaxState::SkipDeclNode;
+            current_character_to_skip_to = L'>';
+            character_to_skip_too_stack.push(L'>');
+          }
+          break;
         }
-        m_ws1.clear();
-        break;
-      default:
-        break;
-    }
-    if (m_syntaxParserResult == FX_XmlSyntaxResult::Error ||
-        m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString) {
-      break;
+        case FDE_XmlSyntaxState::SkipCData: {
+          auto current_span =
+              pdfium::make_span(buffer).subspan(current_buffer_idx);
+          if (FXSYS_wcsnicmp(current_span.data(), L"]]>", 3) == 0) {
+            current_buffer_idx += 3;
+            current_parser_state = FDE_XmlSyntaxState::Text;
+            current_node_->AppendLastChild(
+                doc->CreateNode<CFX_XMLCharData>(GetTextData()));
+          } else {
+            current_text_.push_back(ch);
+            current_buffer_idx++;
+          }
+          break;
+        }
+        case FDE_XmlSyntaxState::SkipDeclNode:
+          if (current_character_to_skip_to == L'\'' ||
+              current_character_to_skip_to == L'\"') {
+            current_buffer_idx++;
+            if (ch != current_character_to_skip_to)
+              break;
+
+            character_to_skip_too_stack.pop();
+            if (character_to_skip_too_stack.empty())
+              current_parser_state = FDE_XmlSyntaxState::Text;
+            else
+              current_character_to_skip_to = character_to_skip_too_stack.top();
+          } else {
+            switch (ch) {
+              case L'<':
+                current_character_to_skip_to = L'>';
+                character_to_skip_too_stack.push(L'>');
+                break;
+              case L'[':
+                current_character_to_skip_to = L']';
+                character_to_skip_too_stack.push(L']');
+                break;
+              case L'(':
+                current_character_to_skip_to = L')';
+                character_to_skip_too_stack.push(L')');
+                break;
+              case L'\'':
+                current_character_to_skip_to = L'\'';
+                character_to_skip_too_stack.push(L'\'');
+                break;
+              case L'\"':
+                current_character_to_skip_to = L'\"';
+                character_to_skip_too_stack.push(L'\"');
+                break;
+              default:
+                if (ch == current_character_to_skip_to) {
+                  character_to_skip_too_stack.pop();
+                  if (character_to_skip_too_stack.empty()) {
+                    current_parser_state = FDE_XmlSyntaxState::Text;
+                  } else {
+                    current_character_to_skip_to =
+                        character_to_skip_too_stack.top();
+                  }
+                }
+                break;
+            }
+            current_buffer_idx++;
+          }
+          break;
+        case FDE_XmlSyntaxState::SkipComment: {
+          auto current_span =
+              pdfium::make_span(buffer).subspan(current_buffer_idx);
+          if (FXSYS_wcsnicmp(current_span.data(), L"-->", 3) == 0) {
+            current_buffer_idx += 2;
+            current_parser_state = FDE_XmlSyntaxState::Text;
+          }
+          current_buffer_idx++;
+          break;
+        }
+        case FDE_XmlSyntaxState::TargetData:
+          if (IsXMLWhiteSpace(ch)) {
+            if (current_text_.empty()) {
+              current_buffer_idx++;
+              break;
+            }
+            if (current_quote_character == 0) {
+              current_buffer_idx++;
+              ProcessTargetData();
+              break;
+            }
+          }
+          if (ch == '?') {
+            current_parser_state = FDE_XmlSyntaxState::CloseInstruction;
+            current_buffer_idx++;
+          } else if (ch == '\"') {
+            if (current_quote_character == 0) {
+              current_quote_character = ch;
+              current_buffer_idx++;
+            } else if (ch == current_quote_character) {
+              current_quote_character = 0;
+              current_buffer_idx++;
+              ProcessTargetData();
+            } else {
+              return false;
+            }
+          } else {
+            current_text_.push_back(ch);
+            current_buffer_idx++;
+          }
+          break;
+        default:
+          break;
+      }
     }
   }
-  return (m_syntaxParserResult == FX_XmlSyntaxResult::Error ||
-          m_NodeStack.size() != 1)
-             ? -1
-             : m_pParser->GetStatus();
+
+  NOTREACHED();
+  return false;
+}
+
+void CFX_XMLParser::ProcessTextChar(wchar_t character) {
+  current_text_.push_back(character);
+
+  if (entity_start_ > -1 && character == L';') {
+    // Copy the entity out into a string and remove from the vector. When we
+    // copy the entity we don't want to copy out the & or the ; so we start
+    // shifted by one and want to copy 2 less characters in total.
+    WideString csEntity(current_text_.data() + entity_start_ + 1,
+                        current_text_.size() - entity_start_ - 2);
+    current_text_.erase(current_text_.begin() + entity_start_,
+                        current_text_.end());
+
+    int32_t iLen = csEntity.GetLength();
+    if (iLen > 0) {
+      if (csEntity[0] == L'#') {
+        uint32_t ch = 0;
+        if (iLen > 1 && csEntity[1] == L'x') {
+          for (int32_t i = 2; i < iLen; i++) {
+            if (!FXSYS_IsHexDigit(csEntity[i]))
+              break;
+            ch = (ch << 4) + FXSYS_HexCharToInt(csEntity[i]);
+          }
+        } else {
+          for (int32_t i = 1; i < iLen; i++) {
+            if (!FXSYS_IsDecimalDigit(csEntity[i]))
+              break;
+            ch = ch * 10 + FXSYS_DecimalCharToInt(csEntity[i]);
+          }
+        }
+        if (ch > kMaxCharRange)
+          ch = ' ';
+
+        character = static_cast<wchar_t>(ch);
+        if (character != 0)
+          current_text_.push_back(character);
+      } else {
+        if (csEntity.Compare(L"amp") == 0) {
+          current_text_.push_back(L'&');
+        } else if (csEntity.Compare(L"lt") == 0) {
+          current_text_.push_back(L'<');
+        } else if (csEntity.Compare(L"gt") == 0) {
+          current_text_.push_back(L'>');
+        } else if (csEntity.Compare(L"apos") == 0) {
+          current_text_.push_back(L'\'');
+        } else if (csEntity.Compare(L"quot") == 0) {
+          current_text_.push_back(L'"');
+        }
+      }
+    }
+
+    entity_start_ = -1;
+  } else if (entity_start_ < 0 && character == L'&') {
+    entity_start_ = current_text_.size() - 1;
+  }
+}
+
+void CFX_XMLParser::ProcessTargetData() {
+  WideString target_data = GetTextData();
+  if (target_data.IsEmpty())
+    return;
+
+  CFX_XMLInstruction* instruction = ToXMLInstruction(current_node_);
+  if (instruction)
+    instruction->AppendData(target_data);
+}
+
+WideString CFX_XMLParser::GetTextData() {
+  WideString ret(current_text_.data(), current_text_.size());
+  entity_start_ = -1;
+  current_text_.clear();
+  current_text_.reserve(kCurrentTextReserve);
+  return ret;
 }
diff --git a/core/fxcrt/xml/cfx_xmlparser.h b/core/fxcrt/xml/cfx_xmlparser.h
index 0038f6d..ef171c6 100644
--- a/core/fxcrt/xml/cfx_xmlparser.h
+++ b/core/fxcrt/xml/cfx_xmlparser.h
@@ -8,39 +8,57 @@
 #define CORE_FXCRT_XML_CFX_XMLPARSER_H_
 
 #include <memory>
-#include <stack>
+#include <vector>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/xml/cfx_xmlsyntaxparser.h"
 
+class CFX_SeekableStreamProxy;
+class CFX_XMLDocument;
 class CFX_XMLElement;
 class CFX_XMLNode;
-class CFX_SeekableStreamProxy;
+class IFX_SeekableReadStream;
 
-class CFX_XMLParser {
+class CFX_XMLParser final {
  public:
-  CFX_XMLParser(CFX_XMLNode* pParent,
-                const RetainPtr<CFX_SeekableStreamProxy>& pStream);
+  static bool IsXMLNameChar(wchar_t ch, bool bFirstChar);
+
+  explicit CFX_XMLParser(const RetainPtr<IFX_SeekableReadStream>& pStream);
   ~CFX_XMLParser();
 
-  int32_t DoParser();
-
-  FX_FILESIZE m_nStart[2];
-  size_t m_nSize[2];
-  FX_FILESIZE m_nElementStart;
-  uint16_t m_dwCheckStatus;
-  uint16_t m_dwCurrentCheckStatus;
+  std::unique_ptr<CFX_XMLDocument> Parse();
 
  private:
-  RetainPtr<CFX_SeekableStreamProxy> m_pStream;
-  std::unique_ptr<CFX_XMLSyntaxParser> m_pParser;
-  CFX_XMLNode* m_pParent;
-  CFX_XMLNode* m_pChild;
-  std::stack<CFX_XMLNode*> m_NodeStack;
-  WideString m_ws1;
-  WideString m_ws2;
-  FX_XmlSyntaxResult m_syntaxParserResult;
+  enum class FDE_XmlSyntaxState {
+    Text,
+    Node,
+    Target,
+    Tag,
+    AttriName,
+    AttriEqualSign,
+    AttriQuotation,
+    AttriValue,
+    CloseInstruction,
+    BreakElement,
+    CloseElement,
+    SkipDeclNode,
+    SkipComment,
+    SkipCommentOrDecl,
+    SkipCData,
+    TargetData
+  };
+
+  bool DoSyntaxParse(CFX_XMLDocument* doc);
+  WideString GetTextData();
+  void ProcessTextChar(wchar_t ch);
+  void ProcessTargetData();
+
+  CFX_XMLNode* current_node_ = nullptr;
+  RetainPtr<CFX_SeekableStreamProxy> stream_;
+  std::vector<wchar_t, FxAllocAllocator<wchar_t>> current_text_;
+  size_t xml_plane_size_ = 1024;
+  int32_t entity_start_ = -1;
 };
 
 #endif  // CORE_FXCRT_XML_CFX_XMLPARSER_H_
diff --git a/core/fxcrt/xml/cfx_xmlparser_unittest.cpp b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp
new file mode 100644
index 0000000..a1fa8f3
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp
@@ -0,0 +1,336 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+
+#include <memory>
+
+#include "core/fxcrt/cfx_readonlymemorystream.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_xmlinstruction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CFX_XMLParserTest : public testing::Test {
+ public:
+  std::unique_ptr<CFX_XMLDocument> Parse(pdfium::span<const char> input) {
+    CFX_XMLParser parser(
+        pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pdfium::as_bytes(input)));
+    return parser.Parse();
+  }
+};
+
+TEST_F(CFX_XMLParserTest, AttributesMustBeQuoted) {
+  static const char input[] =
+      "<script display=1>\n"
+      "</script>";
+  ASSERT_TRUE(Parse(input) == nullptr);
+}
+
+TEST_F(CFX_XMLParserTest, Attributes) {
+  static const char input[] =
+      "<script contentType=\"application/x-javascript\" display=\"1\">\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+
+  EXPECT_EQ(L"application/x-javascript", script->GetAttribute(L"contentType"));
+  EXPECT_EQ(L"1", script->GetAttribute(L"display"));
+}
+
+TEST_F(CFX_XMLParserTest, CData) {
+  static const char input[] =
+      "<script>\n"
+      "  <![CDATA[\n"
+      "    if (a[1] < 3)\n"
+      "      app.alert(\"Tclams\");\n"
+      "  ]]>\n"
+      "</script>";
+
+  static const wchar_t cdata[] =
+      L"\n  \n"
+      L"    if (a[1] < 3)\n"
+      L"      app.alert(\"Tclams\");\n"
+      L"  \n";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(cdata, script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, CDataWithInnerScript) {
+  static const char input[] =
+      "<script>\n"
+      "  <![CDATA[\n"
+      "    if (a[1] < 3)\n"
+      "      app.alert(\"Tclams\");\n"
+      "    </script>\n"
+      "  ]]>\n"
+      "</script>";
+
+  static const wchar_t cdata[] =
+      L"\n  \n"
+      L"    if (a[1] < 3)\n"
+      L"      app.alert(\"Tclams\");\n"
+      L"    </script>\n"
+      L"  \n";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(cdata, script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, ArrowBangArrow) {
+  static const char input[] =
+      "<script>\n"
+      "  <!>\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  \n", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, ArrowBangBracketArrow) {
+  static const char input[] =
+      "<script>\n"
+      "  <![>\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  ", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, IncompleteCData) {
+  static const char input[] =
+      "<script>\n"
+      "  <![CDATA>\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  ", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, UnClosedCData) {
+  static const char input[] =
+      "<script>\n"
+      "  <![CDATA[\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  ", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, EmptyCData) {
+  static const char input[] =
+      "<script>\n"
+      "  <![CDATA[]]>\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  \n", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, Comment) {
+  static const char input[] =
+      "<script>\n"
+      "  <!-- A Comment -->\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  \n", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, IncorrectCommentStart) {
+  static const char input[] =
+      "<script>\n"
+      "  <!- A Comment -->\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  \n", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, CommentEmpty) {
+  static const char input[] =
+      "<script>\n"
+      "  <!---->\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  \n", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, CommentThreeDash) {
+  static const char input[] =
+      "<script>\n"
+      "  <!--->\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"\n  ", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, CommentTwoDash) {
+  static const char input[] =
+      "<script>\n"
+      "  <!-->\n"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  EXPECT_EQ(L"\n  ", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, Entities) {
+  static const char input[] =
+      "<script>"
+      "&#66;"                     // B
+      "&#x54;"                    // T
+      "&#x6a;"                    // j
+      "&#x00000000000000000048;"  // H
+      "&#x0000000000000000AB48;"  // \xab48
+      "&#x0000000000000000000;"
+      "&amp;"
+      "&lt;"
+      "&gt;"
+      "&apos;"
+      "&quot;"
+      "&something_else;"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"BTjH\xab48&<>'\"", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, EntityOverflowHex) {
+  static const char input[] =
+      "<script>"
+      "&#xaDBDFFFFF;"
+      "&#xafffffffffffffffffffffffffffffffff;"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"  ", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, EntityOverflowDecimal) {
+  static const char input[] =
+      "<script>"
+      "&#2914910205;"
+      "&#29149102052342342134521341234512351234213452315;"
+      "</script>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* script = doc->GetRoot()->GetFirstChildNamed(L"script");
+  ASSERT_TRUE(script != nullptr);
+  EXPECT_EQ(L"  ", script->GetTextData());
+}
+
+TEST_F(CFX_XMLParserTest, IsXMLNameChar) {
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'-', true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'-', false));
+
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(0x2069, true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0x2070, true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0x2073, true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0x218F, true));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(0x2190, true));
+
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(0xFDEF, true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0xFDF0, true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0xFDF1, true));
+  EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0xFFFD, true));
+  EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(0xFFFE, true));
+}
+
+TEST_F(CFX_XMLParserTest, BadElementClose) {
+  ASSERT_TRUE(Parse("</endtag>") == nullptr);
+}
+
+TEST_F(CFX_XMLParserTest, DoubleElementClose) {
+  ASSERT_TRUE(Parse("<p></p></p>") == nullptr);
+}
+
+TEST_F(CFX_XMLParserTest, ParseInstruction) {
+  static const char input[] =
+      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/3.3/ ?>"
+      "<form></form>";
+
+  std::unique_ptr<CFX_XMLDocument> doc = Parse(input);
+  ASSERT_TRUE(doc != nullptr);
+
+  CFX_XMLElement* root = doc->GetRoot();
+  ASSERT_TRUE(root->GetFirstChild() != nullptr);
+  ASSERT_EQ(CFX_XMLNode::Type::kInstruction, root->GetFirstChild()->GetType());
+
+  CFX_XMLInstruction* instruction = ToXMLInstruction(root->GetFirstChild());
+  EXPECT_TRUE(instruction->IsOriginalXFAVersion());
+}
+
+TEST_F(CFX_XMLParserTest, BadEntity) {
+  static const char input[] =
+      "<script>"
+      "Test &<p>; thing"
+      "</script>";
+  ASSERT_TRUE(Parse(input) == nullptr);
+}
diff --git a/core/fxcrt/xml/cfx_xmlsyntaxparser.cpp b/core/fxcrt/xml/cfx_xmlsyntaxparser.cpp
deleted file mode 100644
index c0cc1a6..0000000
--- a/core/fxcrt/xml/cfx_xmlsyntaxparser.cpp
+++ /dev/null
@@ -1,694 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cfx_xmlsyntaxparser.h"
-
-#include <algorithm>
-#include <cwctype>
-#include <iterator>
-
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_safe_types.h"
-
-namespace {
-
-const uint32_t kMaxCharRange = 0x10ffff;
-
-bool IsXMLWhiteSpace(wchar_t ch) {
-  return ch == L' ' || ch == 0x0A || ch == 0x0D || ch == 0x09;
-}
-
-struct FX_XMLNAMECHAR {
-  uint16_t wStart;
-  uint16_t wEnd;
-  bool bStartChar;
-};
-
-const FX_XMLNAMECHAR g_XMLNameChars[] = {
-    {L'-', L'.', false},    {L'0', L'9', false},     {L':', L':', false},
-    {L'A', L'Z', true},     {L'_', L'_', true},      {L'a', L'z', true},
-    {0xB7, 0xB7, false},    {0xC0, 0xD6, true},      {0xD8, 0xF6, true},
-    {0xF8, 0x02FF, true},   {0x0300, 0x036F, false}, {0x0370, 0x037D, true},
-    {0x037F, 0x1FFF, true}, {0x200C, 0x200D, true},  {0x203F, 0x2040, false},
-    {0x2070, 0x218F, true}, {0x2C00, 0x2FEF, true},  {0x3001, 0xD7FF, true},
-    {0xF900, 0xFDCF, true}, {0xFDF0, 0xFFFD, true},
-};
-
-
-int32_t GetUTF8EncodeLength(const std::vector<wchar_t>& src,
-                            FX_FILESIZE iSrcLen) {
-  uint32_t unicode = 0;
-  int32_t iDstNum = 0;
-  const wchar_t* pSrc = src.data();
-  while (iSrcLen-- > 0) {
-    unicode = *pSrc++;
-    int nbytes = 0;
-    if ((uint32_t)unicode < 0x80) {
-      nbytes = 1;
-    } else if ((uint32_t)unicode < 0x800) {
-      nbytes = 2;
-    } else if ((uint32_t)unicode < 0x10000) {
-      nbytes = 3;
-    } else if ((uint32_t)unicode < 0x200000) {
-      nbytes = 4;
-    } else if ((uint32_t)unicode < 0x4000000) {
-      nbytes = 5;
-    } else {
-      nbytes = 6;
-    }
-    iDstNum += nbytes;
-  }
-  return iDstNum;
-}
-
-}  // namespace
-
-// static
-bool CFX_XMLSyntaxParser::IsXMLNameChar(wchar_t ch, bool bFirstChar) {
-  auto* it = std::lower_bound(
-      std::begin(g_XMLNameChars), std::end(g_XMLNameChars), ch,
-      [](const FX_XMLNAMECHAR& arg, wchar_t ch) { return arg.wEnd < ch; });
-  return it != std::end(g_XMLNameChars) && ch >= it->wStart &&
-         (!bFirstChar || it->bStartChar);
-}
-
-CFX_XMLSyntaxParser::CFX_XMLSyntaxParser(
-    const RetainPtr<CFX_SeekableStreamProxy>& pStream)
-    : m_pStream(pStream),
-      m_iXMLPlaneSize(32 * 1024),
-      m_iCurrentPos(0),
-      m_iCurrentNodeNum(-1),
-      m_iLastNodeNum(-1),
-      m_iParsedBytes(0),
-      m_ParsedChars(0),
-      m_iBufferChars(0),
-      m_bEOS(false),
-      m_Start(0),
-      m_End(0),
-      m_iAllocStep(m_BlockBuffer.GetAllocStep()),
-      m_pCurrentBlock(nullptr),
-      m_iIndexInBlock(0),
-      m_iTextDataLength(0),
-      m_syntaxParserResult(FX_XmlSyntaxResult::None),
-      m_syntaxParserState(FDE_XmlSyntaxState::Text),
-      m_wQuotationMark(0),
-      m_iEntityStart(-1) {
-  ASSERT(pStream);
-
-  m_CurNode.iNodeNum = -1;
-  m_CurNode.eNodeType = FX_XMLNODE_Unknown;
-
-  m_iXMLPlaneSize =
-      std::min(m_iXMLPlaneSize,
-               pdfium::base::checked_cast<size_t>(m_pStream->GetLength()));
-  m_iCurrentPos = m_pStream->GetBOMLength();
-
-  FX_SAFE_SIZE_T alloc_size_safe = m_iXMLPlaneSize;
-  alloc_size_safe += 1;  // For NUL.
-  if (!alloc_size_safe.IsValid() || alloc_size_safe.ValueOrDie() <= 0) {
-    m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-    return;
-  }
-
-  m_Buffer.resize(pdfium::base::ValueOrDieForType<size_t>(alloc_size_safe));
-
-  m_BlockBuffer.InitBuffer();
-  std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-      m_BlockBuffer.GetAvailableBlock();
-}
-
-CFX_XMLSyntaxParser::~CFX_XMLSyntaxParser() {}
-
-FX_XmlSyntaxResult CFX_XMLSyntaxParser::DoSyntaxParse() {
-  if (m_syntaxParserResult == FX_XmlSyntaxResult::Error ||
-      m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString) {
-    return m_syntaxParserResult;
-  }
-
-  FX_FILESIZE iStreamLength = m_pStream->GetLength();
-  FX_FILESIZE iPos;
-
-  FX_XmlSyntaxResult syntaxParserResult = FX_XmlSyntaxResult::None;
-  while (true) {
-    if (m_Start >= m_End) {
-      if (m_bEOS || m_iCurrentPos >= iStreamLength) {
-        m_syntaxParserResult = FX_XmlSyntaxResult::EndOfString;
-        return m_syntaxParserResult;
-      }
-      m_ParsedChars += m_End;
-      m_iParsedBytes = m_iCurrentPos;
-      if (m_pStream->GetPosition() != m_iCurrentPos)
-        m_pStream->Seek(CFX_SeekableStreamProxy::From::Begin, m_iCurrentPos);
-
-      m_iBufferChars =
-          m_pStream->ReadString(m_Buffer.data(), m_iXMLPlaneSize, &m_bEOS);
-      iPos = m_pStream->GetPosition();
-      if (m_iBufferChars < 1) {
-        m_iCurrentPos = iStreamLength;
-        m_syntaxParserResult = FX_XmlSyntaxResult::EndOfString;
-        return m_syntaxParserResult;
-      }
-      m_iCurrentPos = iPos;
-      m_Start = 0;
-      m_End = m_iBufferChars;
-    }
-
-    while (m_Start < m_End) {
-      wchar_t ch = m_Buffer[m_Start];
-      switch (m_syntaxParserState) {
-        case FDE_XmlSyntaxState::Text:
-          if (ch == L'<') {
-            if (!m_BlockBuffer.IsEmpty()) {
-              m_iTextDataLength = m_BlockBuffer.GetDataLength();
-              m_BlockBuffer.Reset(true);
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              m_iEntityStart = -1;
-              syntaxParserResult = FX_XmlSyntaxResult::Text;
-            } else {
-              m_Start++;
-              m_syntaxParserState = FDE_XmlSyntaxState::Node;
-            }
-          } else {
-            ParseTextChar(ch);
-          }
-          break;
-        case FDE_XmlSyntaxState::Node:
-          if (ch == L'!') {
-            m_Start++;
-            m_syntaxParserState = FDE_XmlSyntaxState::SkipCommentOrDecl;
-          } else if (ch == L'/') {
-            m_Start++;
-            m_syntaxParserState = FDE_XmlSyntaxState::CloseElement;
-          } else if (ch == L'?') {
-            m_iLastNodeNum++;
-            m_iCurrentNodeNum = m_iLastNodeNum;
-            m_CurNode.iNodeNum = m_iLastNodeNum;
-            m_CurNode.eNodeType = FX_XMLNODE_Instruction;
-            m_XMLNodeStack.push(m_CurNode);
-            m_Start++;
-            m_syntaxParserState = FDE_XmlSyntaxState::Target;
-            syntaxParserResult = FX_XmlSyntaxResult::InstructionOpen;
-          } else {
-            m_iLastNodeNum++;
-            m_iCurrentNodeNum = m_iLastNodeNum;
-            m_CurNode.iNodeNum = m_iLastNodeNum;
-            m_CurNode.eNodeType = FX_XMLNODE_Element;
-            m_XMLNodeStack.push(m_CurNode);
-            m_syntaxParserState = FDE_XmlSyntaxState::Tag;
-            syntaxParserResult = FX_XmlSyntaxResult::ElementOpen;
-          }
-          break;
-        case FDE_XmlSyntaxState::Target:
-        case FDE_XmlSyntaxState::Tag:
-          if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) {
-            if (m_BlockBuffer.IsEmpty()) {
-              m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-              return m_syntaxParserResult;
-            }
-
-            m_iTextDataLength = m_BlockBuffer.GetDataLength();
-            m_BlockBuffer.Reset(true);
-            std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                m_BlockBuffer.GetAvailableBlock();
-            if (m_syntaxParserState != FDE_XmlSyntaxState::Target)
-              syntaxParserResult = FX_XmlSyntaxResult::TagName;
-            else
-              syntaxParserResult = FX_XmlSyntaxResult::TargetName;
-
-            m_syntaxParserState = FDE_XmlSyntaxState::AttriName;
-          } else {
-            if (m_iIndexInBlock == m_iAllocStep) {
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              if (!m_pCurrentBlock) {
-                return FX_XmlSyntaxResult::Error;
-              }
-            }
-            m_pCurrentBlock[m_iIndexInBlock++] = ch;
-            m_BlockBuffer.IncrementDataLength();
-            m_Start++;
-          }
-          break;
-        case FDE_XmlSyntaxState::AttriName:
-          if (m_BlockBuffer.IsEmpty() && IsXMLWhiteSpace(ch)) {
-            m_Start++;
-            break;
-          }
-          if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) {
-            if (m_BlockBuffer.IsEmpty()) {
-              if (m_CurNode.eNodeType == FX_XMLNODE_Element) {
-                if (ch == L'>' || ch == L'/') {
-                  m_syntaxParserState = FDE_XmlSyntaxState::BreakElement;
-                  break;
-                }
-              } else if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) {
-                if (ch == L'?') {
-                  m_syntaxParserState = FDE_XmlSyntaxState::CloseInstruction;
-                  m_Start++;
-                } else {
-                  m_syntaxParserState = FDE_XmlSyntaxState::TargetData;
-                }
-                break;
-              }
-              m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-              return m_syntaxParserResult;
-            } else {
-              if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) {
-                if (ch != '=' && !IsXMLWhiteSpace(ch)) {
-                  m_syntaxParserState = FDE_XmlSyntaxState::TargetData;
-                  break;
-                }
-              }
-              m_iTextDataLength = m_BlockBuffer.GetDataLength();
-              m_BlockBuffer.Reset(true);
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              m_syntaxParserState = FDE_XmlSyntaxState::AttriEqualSign;
-              syntaxParserResult = FX_XmlSyntaxResult::AttriName;
-            }
-          } else {
-            if (m_iIndexInBlock == m_iAllocStep) {
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              if (!m_pCurrentBlock) {
-                return FX_XmlSyntaxResult::Error;
-              }
-            }
-            m_pCurrentBlock[m_iIndexInBlock++] = ch;
-            m_BlockBuffer.IncrementDataLength();
-            m_Start++;
-          }
-          break;
-        case FDE_XmlSyntaxState::AttriEqualSign:
-          if (IsXMLWhiteSpace(ch)) {
-            m_Start++;
-            break;
-          }
-          if (ch != L'=') {
-            if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) {
-              m_syntaxParserState = FDE_XmlSyntaxState::TargetData;
-              break;
-            }
-            m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-            return m_syntaxParserResult;
-          } else {
-            m_syntaxParserState = FDE_XmlSyntaxState::AttriQuotation;
-            m_Start++;
-          }
-          break;
-        case FDE_XmlSyntaxState::AttriQuotation:
-          if (IsXMLWhiteSpace(ch)) {
-            m_Start++;
-            break;
-          }
-          if (ch != L'\"' && ch != L'\'') {
-            m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-            return m_syntaxParserResult;
-          } else {
-            m_wQuotationMark = ch;
-            m_syntaxParserState = FDE_XmlSyntaxState::AttriValue;
-            m_Start++;
-          }
-          break;
-        case FDE_XmlSyntaxState::AttriValue:
-          if (ch == m_wQuotationMark) {
-            if (m_iEntityStart > -1) {
-              m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-              return m_syntaxParserResult;
-            }
-            m_iTextDataLength = m_BlockBuffer.GetDataLength();
-            m_wQuotationMark = 0;
-            m_BlockBuffer.Reset(true);
-            std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                m_BlockBuffer.GetAvailableBlock();
-            m_Start++;
-            m_syntaxParserState = FDE_XmlSyntaxState::AttriName;
-            syntaxParserResult = FX_XmlSyntaxResult::AttriValue;
-          } else {
-            ParseTextChar(ch);
-          }
-          break;
-        case FDE_XmlSyntaxState::CloseInstruction:
-          if (ch != L'>') {
-            if (m_iIndexInBlock == m_iAllocStep) {
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              if (!m_pCurrentBlock) {
-                return FX_XmlSyntaxResult::Error;
-              }
-            }
-            m_pCurrentBlock[m_iIndexInBlock++] = ch;
-            m_BlockBuffer.IncrementDataLength();
-            m_syntaxParserState = FDE_XmlSyntaxState::TargetData;
-          } else if (!m_BlockBuffer.IsEmpty()) {
-            m_iTextDataLength = m_BlockBuffer.GetDataLength();
-            m_BlockBuffer.Reset(true);
-            std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                m_BlockBuffer.GetAvailableBlock();
-            syntaxParserResult = FX_XmlSyntaxResult::TargetData;
-          } else {
-            m_Start++;
-            if (m_XMLNodeStack.empty()) {
-              m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-              return m_syntaxParserResult;
-            }
-            m_XMLNodeStack.pop();
-            if (!m_XMLNodeStack.empty()) {
-              m_CurNode = m_XMLNodeStack.top();
-            } else {
-              m_CurNode.iNodeNum = -1;
-              m_CurNode.eNodeType = FX_XMLNODE_Unknown;
-            }
-            m_iCurrentNodeNum = m_CurNode.iNodeNum;
-            m_BlockBuffer.Reset(true);
-            std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                m_BlockBuffer.GetAvailableBlock();
-            m_syntaxParserState = FDE_XmlSyntaxState::Text;
-            syntaxParserResult = FX_XmlSyntaxResult::InstructionClose;
-          }
-          break;
-        case FDE_XmlSyntaxState::BreakElement:
-          if (ch == L'>') {
-            m_syntaxParserState = FDE_XmlSyntaxState::Text;
-            syntaxParserResult = FX_XmlSyntaxResult::ElementBreak;
-          } else if (ch == L'/') {
-            m_syntaxParserState = FDE_XmlSyntaxState::CloseElement;
-          } else {
-            m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-            return m_syntaxParserResult;
-          }
-          m_Start++;
-          break;
-        case FDE_XmlSyntaxState::CloseElement:
-          if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) {
-            if (ch == L'>') {
-              if (m_XMLNodeStack.empty()) {
-                m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-                return m_syntaxParserResult;
-              }
-              m_XMLNodeStack.pop();
-              if (!m_XMLNodeStack.empty()) {
-                m_CurNode = m_XMLNodeStack.top();
-              } else {
-                m_CurNode.iNodeNum = -1;
-                m_CurNode.eNodeType = FX_XMLNODE_Unknown;
-              }
-              m_iCurrentNodeNum = m_CurNode.iNodeNum;
-              m_iTextDataLength = m_BlockBuffer.GetDataLength();
-              m_BlockBuffer.Reset(true);
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              m_syntaxParserState = FDE_XmlSyntaxState::Text;
-              syntaxParserResult = FX_XmlSyntaxResult::ElementClose;
-            } else if (!IsXMLWhiteSpace(ch)) {
-              m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-              return m_syntaxParserResult;
-            }
-          } else {
-            if (m_iIndexInBlock == m_iAllocStep) {
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              if (!m_pCurrentBlock) {
-                return FX_XmlSyntaxResult::Error;
-              }
-            }
-            m_pCurrentBlock[m_iIndexInBlock++] = ch;
-            m_BlockBuffer.IncrementDataLength();
-          }
-          m_Start++;
-          break;
-        case FDE_XmlSyntaxState::SkipCommentOrDecl:
-          if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"--", 2) == 0) {
-            m_Start += 2;
-            m_syntaxParserState = FDE_XmlSyntaxState::SkipComment;
-          } else if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"[CDATA[", 7) ==
-                     0) {
-            m_Start += 7;
-            m_syntaxParserState = FDE_XmlSyntaxState::SkipCData;
-          } else {
-            m_syntaxParserState = FDE_XmlSyntaxState::SkipDeclNode;
-            m_SkipChar = L'>';
-            m_SkipStack.push(L'>');
-          }
-          break;
-        case FDE_XmlSyntaxState::SkipCData: {
-          if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"]]>", 3) == 0) {
-            m_Start += 3;
-            syntaxParserResult = FX_XmlSyntaxResult::CData;
-            m_iTextDataLength = m_BlockBuffer.GetDataLength();
-            m_BlockBuffer.Reset(true);
-            std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                m_BlockBuffer.GetAvailableBlock();
-            m_syntaxParserState = FDE_XmlSyntaxState::Text;
-          } else {
-            if (m_iIndexInBlock == m_iAllocStep) {
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              if (!m_pCurrentBlock)
-                return FX_XmlSyntaxResult::Error;
-            }
-            m_pCurrentBlock[m_iIndexInBlock++] = ch;
-            m_BlockBuffer.IncrementDataLength();
-            m_Start++;
-          }
-          break;
-        }
-        case FDE_XmlSyntaxState::SkipDeclNode:
-          if (m_SkipChar == L'\'' || m_SkipChar == L'\"') {
-            m_Start++;
-            if (ch != m_SkipChar)
-              break;
-
-            m_SkipStack.pop();
-            if (m_SkipStack.empty())
-              m_syntaxParserState = FDE_XmlSyntaxState::Text;
-            else
-              m_SkipChar = m_SkipStack.top();
-          } else {
-            switch (ch) {
-              case L'<':
-                m_SkipChar = L'>';
-                m_SkipStack.push(L'>');
-                break;
-              case L'[':
-                m_SkipChar = L']';
-                m_SkipStack.push(L']');
-                break;
-              case L'(':
-                m_SkipChar = L')';
-                m_SkipStack.push(L')');
-                break;
-              case L'\'':
-                m_SkipChar = L'\'';
-                m_SkipStack.push(L'\'');
-                break;
-              case L'\"':
-                m_SkipChar = L'\"';
-                m_SkipStack.push(L'\"');
-                break;
-              default:
-                if (ch == m_SkipChar) {
-                  m_SkipStack.pop();
-                  if (m_SkipStack.empty()) {
-                    if (m_BlockBuffer.GetDataLength() >= 9)
-                      (void)m_BlockBuffer.GetTextData(0, 7);
-
-                    m_iTextDataLength = m_BlockBuffer.GetDataLength();
-                    m_BlockBuffer.Reset(true);
-                    std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                        m_BlockBuffer.GetAvailableBlock();
-                    m_syntaxParserState = FDE_XmlSyntaxState::Text;
-                  } else {
-                    m_SkipChar = m_SkipStack.top();
-                  }
-                }
-                break;
-            }
-            if (!m_SkipStack.empty()) {
-              if (m_iIndexInBlock == m_iAllocStep) {
-                std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                    m_BlockBuffer.GetAvailableBlock();
-                if (!m_pCurrentBlock) {
-                  return FX_XmlSyntaxResult::Error;
-                }
-              }
-              m_pCurrentBlock[m_iIndexInBlock++] = ch;
-              m_BlockBuffer.IncrementDataLength();
-            }
-            m_Start++;
-          }
-          break;
-        case FDE_XmlSyntaxState::SkipComment:
-          if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"-->", 3) == 0) {
-            m_Start += 2;
-            m_syntaxParserState = FDE_XmlSyntaxState::Text;
-          }
-
-          m_Start++;
-          break;
-        case FDE_XmlSyntaxState::TargetData:
-          if (IsXMLWhiteSpace(ch)) {
-            if (m_BlockBuffer.IsEmpty()) {
-              m_Start++;
-              break;
-            }
-            if (m_wQuotationMark == 0) {
-              m_iTextDataLength = m_BlockBuffer.GetDataLength();
-              m_wQuotationMark = 0;
-              m_BlockBuffer.Reset(true);
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              m_Start++;
-              syntaxParserResult = FX_XmlSyntaxResult::TargetData;
-              break;
-            }
-          }
-          if (ch == '?') {
-            m_syntaxParserState = FDE_XmlSyntaxState::CloseInstruction;
-            m_Start++;
-          } else if (ch == '\"') {
-            if (m_wQuotationMark == 0) {
-              m_wQuotationMark = ch;
-              m_Start++;
-            } else if (ch == m_wQuotationMark) {
-              m_iTextDataLength = m_BlockBuffer.GetDataLength();
-              m_wQuotationMark = 0;
-              m_BlockBuffer.Reset(true);
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              m_Start++;
-              syntaxParserResult = FX_XmlSyntaxResult::TargetData;
-            } else {
-              m_syntaxParserResult = FX_XmlSyntaxResult::Error;
-              return m_syntaxParserResult;
-            }
-          } else {
-            if (m_iIndexInBlock == m_iAllocStep) {
-              std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-                  m_BlockBuffer.GetAvailableBlock();
-              if (!m_pCurrentBlock) {
-                return FX_XmlSyntaxResult::Error;
-              }
-            }
-            m_pCurrentBlock[m_iIndexInBlock++] = ch;
-            m_BlockBuffer.IncrementDataLength();
-            m_Start++;
-          }
-          break;
-        default:
-          break;
-      }
-      if (syntaxParserResult != FX_XmlSyntaxResult::None)
-        return syntaxParserResult;
-    }
-  }
-  return FX_XmlSyntaxResult::Text;
-}
-
-int32_t CFX_XMLSyntaxParser::GetStatus() const {
-  if (!m_pStream)
-    return -1;
-
-  int32_t iStreamLength = m_pStream->GetLength();
-  if (iStreamLength < 1)
-    return 100;
-
-  if (m_syntaxParserResult == FX_XmlSyntaxResult::Error)
-    return -1;
-
-  if (m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString)
-    return 100;
-  return m_iParsedBytes * 100 / iStreamLength;
-}
-
-FX_FILESIZE CFX_XMLSyntaxParser::GetCurrentBinaryPos() const {
-  if (!m_pStream)
-    return 0;
-
-  int32_t nDstLen = GetUTF8EncodeLength(m_Buffer, m_Start);
-  return m_iParsedBytes + nDstLen;
-}
-
-void CFX_XMLSyntaxParser::ParseTextChar(wchar_t character) {
-  if (m_iIndexInBlock == m_iAllocStep) {
-    std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-        m_BlockBuffer.GetAvailableBlock();
-    if (!m_pCurrentBlock)
-      return;
-  }
-
-  m_pCurrentBlock[m_iIndexInBlock++] = character;
-  m_BlockBuffer.IncrementDataLength();
-  if (m_iEntityStart > -1 && character == L';') {
-    WideString csEntity = m_BlockBuffer.GetTextData(
-        m_iEntityStart + 1,
-        m_BlockBuffer.GetDataLength() - 1 - m_iEntityStart - 1);
-    int32_t iLen = csEntity.GetLength();
-    if (iLen > 0) {
-      if (csEntity[0] == L'#') {
-        uint32_t ch = 0;
-        wchar_t w;
-        if (iLen > 1 && csEntity[1] == L'x') {
-          for (int32_t i = 2; i < iLen; i++) {
-            w = csEntity[i];
-            if (std::iswdigit(w))
-              ch = (ch << 4) + w - L'0';
-            else if (w >= L'A' && w <= L'F')
-              ch = (ch << 4) + w - 55;
-            else if (w >= L'a' && w <= L'f')
-              ch = (ch << 4) + w - 87;
-            else
-              break;
-          }
-        } else {
-          for (int32_t i = 1; i < iLen; i++) {
-            w = csEntity[i];
-            if (!std::iswdigit(w))
-              break;
-            ch = ch * 10 + w - L'0';
-          }
-        }
-        if (ch > kMaxCharRange)
-          ch = ' ';
-
-        character = static_cast<wchar_t>(ch);
-        if (character != 0) {
-          m_BlockBuffer.SetTextChar(m_iEntityStart, character);
-          m_iEntityStart++;
-        }
-      } else {
-        if (csEntity.Compare(L"amp") == 0) {
-          m_BlockBuffer.SetTextChar(m_iEntityStart, L'&');
-          m_iEntityStart++;
-        } else if (csEntity.Compare(L"lt") == 0) {
-          m_BlockBuffer.SetTextChar(m_iEntityStart, L'<');
-          m_iEntityStart++;
-        } else if (csEntity.Compare(L"gt") == 0) {
-          m_BlockBuffer.SetTextChar(m_iEntityStart, L'>');
-          m_iEntityStart++;
-        } else if (csEntity.Compare(L"apos") == 0) {
-          m_BlockBuffer.SetTextChar(m_iEntityStart, L'\'');
-          m_iEntityStart++;
-        } else if (csEntity.Compare(L"quot") == 0) {
-          m_BlockBuffer.SetTextChar(m_iEntityStart, L'\"');
-          m_iEntityStart++;
-        }
-      }
-    }
-    if (m_iEntityStart >= 0 &&
-        m_BlockBuffer.GetDataLength() > static_cast<size_t>(m_iEntityStart)) {
-      m_BlockBuffer.DeleteTextChars(m_BlockBuffer.GetDataLength() -
-                                    m_iEntityStart);
-    }
-    std::tie(m_pCurrentBlock, m_iIndexInBlock) =
-        m_BlockBuffer.GetAvailableBlock();
-    m_iEntityStart = -1;
-  } else if (m_iEntityStart < 0 && character == L'&') {
-    m_iEntityStart = m_BlockBuffer.GetDataLength() - 1;
-  }
-  m_Start++;
-}
diff --git a/core/fxcrt/xml/cfx_xmlsyntaxparser.h b/core/fxcrt/xml/cfx_xmlsyntaxparser.h
deleted file mode 100644
index b93bbb6..0000000
--- a/core/fxcrt/xml/cfx_xmlsyntaxparser.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CFX_XMLSYNTAXPARSER_H_
-#define CORE_FXCRT_XML_CFX_XMLSYNTAXPARSER_H_
-
-#include <stack>
-#include <vector>
-
-#include "core/fxcrt/cfx_blockbuffer.h"
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-
-enum class FX_XmlSyntaxResult {
-  None,
-  InstructionOpen,
-  InstructionClose,
-  ElementOpen,
-  ElementBreak,
-  ElementClose,
-  TargetName,
-  TagName,
-  AttriName,
-  AttriValue,
-  Text,
-  CData,
-  TargetData,
-  Error,
-  EndOfString
-};
-
-class CFX_XMLSyntaxParser {
- public:
-  static bool IsXMLNameChar(wchar_t ch, bool bFirstChar);
-
-  explicit CFX_XMLSyntaxParser(
-      const RetainPtr<CFX_SeekableStreamProxy>& pStream);
-  ~CFX_XMLSyntaxParser();
-
-  FX_XmlSyntaxResult DoSyntaxParse();
-
-  int32_t GetStatus() const;
-  FX_FILESIZE GetCurrentPos() const { return m_ParsedChars + m_Start; }
-  FX_FILESIZE GetCurrentBinaryPos() const;
-  int32_t GetCurrentNodeNumber() const { return m_iCurrentNodeNum; }
-  int32_t GetLastNodeNumber() const { return m_iLastNodeNum; }
-
-  WideString GetTargetName() const {
-    return m_BlockBuffer.GetTextData(0, m_iTextDataLength);
-  }
-
-  WideString GetTagName() const {
-    return m_BlockBuffer.GetTextData(0, m_iTextDataLength);
-  }
-
-  WideString GetAttributeName() const {
-    return m_BlockBuffer.GetTextData(0, m_iTextDataLength);
-  }
-
-  WideString GetAttributeValue() const {
-    return m_BlockBuffer.GetTextData(0, m_iTextDataLength);
-  }
-
-  WideString GetTextData() const {
-    return m_BlockBuffer.GetTextData(0, m_iTextDataLength);
-  }
-
-  WideString GetTargetData() const {
-    return m_BlockBuffer.GetTextData(0, m_iTextDataLength);
-  }
-
- protected:
-  enum class FDE_XmlSyntaxState {
-    Text,
-    Node,
-    Target,
-    Tag,
-    AttriName,
-    AttriEqualSign,
-    AttriQuotation,
-    AttriValue,
-    Entity,
-    EntityDecimal,
-    EntityHex,
-    CloseInstruction,
-    BreakElement,
-    CloseElement,
-    SkipDeclNode,
-    DeclCharData,
-    SkipComment,
-    SkipCommentOrDecl,
-    SkipCData,
-    TargetData
-  };
-
-  void ParseTextChar(wchar_t ch);
-
-  RetainPtr<CFX_SeekableStreamProxy> m_pStream;
-  size_t m_iXMLPlaneSize;
-  FX_FILESIZE m_iCurrentPos;
-  int32_t m_iCurrentNodeNum;
-  int32_t m_iLastNodeNum;
-  int32_t m_iParsedBytes;
-  FX_FILESIZE m_ParsedChars;
-  std::vector<wchar_t> m_Buffer;
-  size_t m_iBufferChars;
-  bool m_bEOS;
-  FX_FILESIZE m_Start;  // Start position in m_Buffer
-  FX_FILESIZE m_End;    // End position in m_Buffer
-  FX_XMLNODE m_CurNode;
-  std::stack<FX_XMLNODE> m_XMLNodeStack;
-  CFX_BlockBuffer m_BlockBuffer;
-  int32_t m_iAllocStep;
-  wchar_t* m_pCurrentBlock;  // Pointer into CFX_BlockBuffer
-  int32_t m_iIndexInBlock;
-  int32_t m_iTextDataLength;
-  FX_XmlSyntaxResult m_syntaxParserResult;
-  FDE_XmlSyntaxState m_syntaxParserState;
-  wchar_t m_wQuotationMark;
-  int32_t m_iEntityStart;
-  std::stack<wchar_t> m_SkipStack;
-  wchar_t m_SkipChar;
-};
-
-#endif  // CORE_FXCRT_XML_CFX_XMLSYNTAXPARSER_H_
diff --git a/core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp b/core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp
deleted file mode 100644
index b7f7c41..0000000
--- a/core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp
+++ /dev/null
@@ -1,542 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/xml/cfx_xmlsyntaxparser.h"
-
-#include <memory>
-
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-TEST(CFX_XMLSyntaxParserTest, CData) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <![CDATA[\n"
-      "    if (a[1] < 3)\n"
-      "      app.alert(\"Tclams\");\n"
-      "  ]]>\n"
-      "</script>";
-
-  const wchar_t* cdata =
-      L"\n"
-      L"    if (a[1] < 3)\n"
-      L"      app.alert(\"Tclams\");\n"
-      L"  ";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse());
-  ASSERT_EQ(cdata, parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, CDataWithInnerScript) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <![CDATA[\n"
-      "    if (a[1] < 3)\n"
-      "      app.alert(\"Tclams\");\n"
-      "    </script>\n"
-      "  ]]>\n"
-      "</script>";
-
-  const wchar_t* cdata =
-      L"\n"
-      L"    if (a[1] < 3)\n"
-      L"      app.alert(\"Tclams\");\n"
-      L"    </script>\n"
-      L"  ";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse());
-  ASSERT_EQ(cdata, parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, ArrowBangArrow) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <!>\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, ArrowBangBracketArrow) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <![>\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  // Parser walks to end of input.
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, IncompleteCData) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <![CDATA>\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  // Parser walks to end of input.
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, UnClosedCData) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <![CDATA[\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  // Parser walks to end of input.
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, EmptyCData) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <![CDATA[]]>\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse());
-  ASSERT_EQ(L"", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, Comment) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <!-- A Comment -->\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, IncorrectCommentStart) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <!- A Comment -->\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, CommentEmpty) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <!---->\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, CommentThreeDash) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <!--->\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, CommentTwoDash) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">\n"
-      "  <!-->\n"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"\n  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, Entities) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">"
-      "&#66;"
-      "&#x54;"
-      "&#x00000000000000000048;"
-      "&#x0000000000000000AB48;"
-      "&#x0000000000000000000;"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"BTH\xab48", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, EntityOverflowHex) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">"
-      "&#xaDBDFFFFF;"
-      "&#xafffffffffffffffffffffffffffffffff;"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, EntityOverflowDecimal) {
-  const char* input =
-      "<script contentType=\"application/x-javascript\">"
-      "&#2914910205;"
-      "&#29149102052342342134521341234512351234213452315;"
-      "</script>";
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-          reinterpret_cast<uint8_t*>(const_cast<char*>(input)), strlen(input));
-  stream->SetCodePage(FX_CODEPAGE_UTF8);
-
-  CFX_XMLSyntaxParser parser(stream);
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse());
-  ASSERT_EQ(L"contentType", parser.GetAttributeName());
-  ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse());
-  ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse());
-  ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse());
-  ASSERT_EQ(L"  ", parser.GetTextData());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse());
-  ASSERT_EQ(L"script", parser.GetTagName());
-
-  ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse());
-}
-
-TEST(CFX_XMLSyntaxParserTest, IsXMLNameChar) {
-  EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(L'-', true));
-  EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(L'-', false));
-
-  EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2069, true));
-  EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2070, true));
-  EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2073, true));
-  EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0x218F, true));
-  EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2190, true));
-
-  EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFDEF, true));
-  EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFDF0, true));
-  EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFDF1, true));
-  EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFFFD, true));
-  EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFFFE, true));
-}
diff --git a/core/fxcrt/xml/cfx_xmltext.cpp b/core/fxcrt/xml/cfx_xmltext.cpp
index 83ad043..67c35a5 100644
--- a/core/fxcrt/xml/cfx_xmltext.cpp
+++ b/core/fxcrt/xml/cfx_xmltext.cpp
@@ -6,17 +6,20 @@
 
 #include "core/fxcrt/xml/cfx_xmltext.h"
 
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
 
-CFX_XMLText::CFX_XMLText(const WideString& wsText)
-    : CFX_XMLNode(), m_wsText(wsText) {}
+CFX_XMLText::CFX_XMLText(const WideString& wsText) : text_(wsText) {}
 
-CFX_XMLText::~CFX_XMLText() {}
+CFX_XMLText::~CFX_XMLText() = default;
 
-FX_XMLNODETYPE CFX_XMLText::GetType() const {
-  return FX_XMLNODE_Text;
+CFX_XMLNode::Type CFX_XMLText::GetType() const {
+  return Type::kText;
 }
 
-std::unique_ptr<CFX_XMLNode> CFX_XMLText::Clone() {
-  return pdfium::MakeUnique<CFX_XMLText>(m_wsText);
+CFX_XMLNode* CFX_XMLText::Clone(CFX_XMLDocument* doc) {
+  return doc->CreateNode<CFX_XMLText>(text_);
+}
+
+void CFX_XMLText::Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) {
+  pXMLStream->WriteString(EncodeEntities(GetText()).ToUTF8().AsStringView());
 }
diff --git a/core/fxcrt/xml/cfx_xmltext.h b/core/fxcrt/xml/cfx_xmltext.h
index e9f3585..72ca242 100644
--- a/core/fxcrt/xml/cfx_xmltext.h
+++ b/core/fxcrt/xml/cfx_xmltext.h
@@ -7,25 +7,41 @@
 #ifndef CORE_FXCRT_XML_CFX_XMLTEXT_H_
 #define CORE_FXCRT_XML_CFX_XMLTEXT_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 
+class CFX_XMLDocument;
+
 class CFX_XMLText : public CFX_XMLNode {
  public:
   explicit CFX_XMLText(const WideString& wsText);
   ~CFX_XMLText() override;
 
   // CFX_XMLNode
-  FX_XMLNODETYPE GetType() const override;
-  std::unique_ptr<CFX_XMLNode> Clone() override;
+  Type GetType() const override;
+  CFX_XMLNode* Clone(CFX_XMLDocument* doc) override;
+  void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) override;
 
-  WideString GetText() const { return m_wsText; }
-  void SetText(const WideString& wsText) { m_wsText = wsText; }
+  const WideString& GetText() const { return text_; }
+  void SetText(const WideString& wsText) { text_ = wsText; }
 
  private:
-  WideString m_wsText;
+  WideString text_;
 };
 
+inline bool IsXMLText(const CFX_XMLNode* pNode) {
+  CFX_XMLNode::Type type = pNode->GetType();
+  return type == CFX_XMLNode::Type::kText ||
+         type == CFX_XMLNode::Type::kCharData;
+}
+
+inline CFX_XMLText* ToXMLText(CFX_XMLNode* pNode) {
+  return pNode && IsXMLText(pNode) ? static_cast<CFX_XMLText*>(pNode) : nullptr;
+}
+
+inline const CFX_XMLText* ToXMLText(const CFX_XMLNode* pNode) {
+  return pNode && IsXMLText(pNode) ? static_cast<const CFX_XMLText*>(pNode)
+                                   : nullptr;
+}
+
 #endif  // CORE_FXCRT_XML_CFX_XMLTEXT_H_
diff --git a/core/fxcrt/xml/cfx_xmltext_unittest.cpp b/core/fxcrt/xml/cfx_xmltext_unittest.cpp
new file mode 100644
index 0000000..0df003a
--- /dev/null
+++ b/core/fxcrt/xml/cfx_xmltext_unittest.cpp
@@ -0,0 +1,43 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/xml/cfx_xmltext.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/string_write_stream.h"
+
+TEST(CFX_XMLTextTest, GetType) {
+  CFX_XMLText text(L"My Text");
+  EXPECT_EQ(CFX_XMLNode::Type::kText, text.GetType());
+}
+
+TEST(CFX_XMLTextTest, GetText) {
+  CFX_XMLText data(L"My Data");
+  EXPECT_EQ(L"My Data", data.GetText());
+}
+
+TEST(CFX_XMLTextTest, Clone) {
+  CFX_XMLDocument doc;
+
+  CFX_XMLText data(L"My Data");
+  CFX_XMLNode* clone = data.Clone(&doc);
+  EXPECT_TRUE(clone != nullptr);
+  ASSERT_EQ(CFX_XMLNode::Type::kText, clone->GetType());
+  EXPECT_EQ(L"My Data", ToXMLText(clone)->GetText());
+}
+
+TEST(CFX_XMLTextTest, Save) {
+  auto stream = pdfium::MakeRetain<StringWriteStream>();
+  CFX_XMLText data(L"My Data & this is < and > and ' and \" stuff.");
+  data.Save(stream);
+  EXPECT_EQ("My Data &amp; this is &lt; and &gt; and &apos; and &quot; stuff.",
+            stream->ToString());
+}
+
+TEST(CFX_XMLTextTest, SetText) {
+  CFX_XMLText data(L"My Data");
+  EXPECT_EQ(L"My Data", data.GetText());
+  data.SetText(L"New Text");
+  EXPECT_EQ(L"New Text", data.GetText());
+}
diff --git a/core/fxcrt/xml/cxml_attritem.cpp b/core/fxcrt/xml/cxml_attritem.cpp
deleted file mode 100644
index cbbf3f6..0000000
--- a/core/fxcrt/xml/cxml_attritem.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cxml_attritem.h"
-
-bool CXML_AttrItem::Matches(const ByteString& space,
-                            const ByteString& name) const {
-  return (space.IsEmpty() || m_QSpaceName == space) && m_AttrName == name;
-}
diff --git a/core/fxcrt/xml/cxml_attritem.h b/core/fxcrt/xml/cxml_attritem.h
deleted file mode 100644
index 84d8295..0000000
--- a/core/fxcrt/xml/cxml_attritem.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CXML_ATTRITEM_H_
-#define CORE_FXCRT_XML_CXML_ATTRITEM_H_
-
-#include "core/fxcrt/fx_string.h"
-
-class CXML_AttrItem {
- public:
-  bool Matches(const ByteString& space, const ByteString& name) const;
-
-  ByteString m_QSpaceName;
-  ByteString m_AttrName;
-  WideString m_Value;
-};
-
-#endif  // CORE_FXCRT_XML_CXML_ATTRITEM_H_
diff --git a/core/fxcrt/xml/cxml_content.cpp b/core/fxcrt/xml/cxml_content.cpp
deleted file mode 100644
index a235009..0000000
--- a/core/fxcrt/xml/cxml_content.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cxml_content.h"
-
-CXML_Content::CXML_Content(bool bCDATA, const WideStringView& content)
-    : m_bCDATA(bCDATA), m_Content(content) {}
-
-CXML_Content::~CXML_Content() {}
-
-CXML_Content* CXML_Content::AsContent() {
-  return this;
-}
-
-const CXML_Content* CXML_Content::AsContent() const {
-  return this;
-}
diff --git a/core/fxcrt/xml/cxml_content.h b/core/fxcrt/xml/cxml_content.h
deleted file mode 100644
index 97c1abf..0000000
--- a/core/fxcrt/xml/cxml_content.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CXML_CONTENT_H_
-#define CORE_FXCRT_XML_CXML_CONTENT_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/xml/cxml_object.h"
-
-class CXML_Content : public CXML_Object {
- public:
-  CXML_Content(bool bCDATA, const WideStringView& content);
-  ~CXML_Content() override;
-
-  // CXML_Object:
-  CXML_Content* AsContent() override;
-  const CXML_Content* AsContent() const override;
-
-  bool m_bCDATA;
-  WideString m_Content;
-};
-
-#endif  // CORE_FXCRT_XML_CXML_CONTENT_H_
diff --git a/core/fxcrt/xml/cxml_databufacc.cpp b/core/fxcrt/xml/cxml_databufacc.cpp
deleted file mode 100644
index b3e2e1f..0000000
--- a/core/fxcrt/xml/cxml_databufacc.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cxml_databufacc.h"
-
-CXML_DataBufAcc::CXML_DataBufAcc(const uint8_t* pBuffer, size_t size)
-    : m_pBuffer(pBuffer), m_dwSize(size), m_dwCurPos(0) {}
-
-CXML_DataBufAcc::~CXML_DataBufAcc() {}
-
-bool CXML_DataBufAcc::ReadNextBlock() {
-  if (m_dwCurPos >= m_dwSize)
-    return false;
-
-  m_dwCurPos = m_dwSize;
-  return true;
-}
diff --git a/core/fxcrt/xml/cxml_databufacc.h b/core/fxcrt/xml/cxml_databufacc.h
deleted file mode 100644
index 4fb44b3..0000000
--- a/core/fxcrt/xml/cxml_databufacc.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CXML_DATABUFACC_H_
-#define CORE_FXCRT_XML_CXML_DATABUFACC_H_
-
-#include "core/fxcrt/fx_system.h"
-
-class CXML_DataBufAcc {
- public:
-  CXML_DataBufAcc(const uint8_t* pBuffer, size_t size);
-  ~CXML_DataBufAcc();
-
-  bool IsEOF() const { return m_dwCurPos >= m_dwSize; }
-  FX_FILESIZE GetPosition() const {
-    return static_cast<FX_FILESIZE>(m_dwCurPos);
-  }
-  bool ReadNextBlock();
-  const uint8_t* GetBlockBuffer() const { return m_pBuffer; }
-  size_t GetBlockSize() const { return m_dwSize; }
-
- private:
-  const uint8_t* m_pBuffer;
-  size_t m_dwSize;
-  size_t m_dwCurPos;
-};
-
-#endif  // CORE_FXCRT_XML_CXML_DATABUFACC_H_
diff --git a/core/fxcrt/xml/cxml_element.cpp b/core/fxcrt/xml/cxml_element.cpp
deleted file mode 100644
index 1d42e8e..0000000
--- a/core/fxcrt/xml/cxml_element.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cxml_element.h"
-
-#include "core/fxcrt/xml/cxml_content.h"
-#include "core/fxcrt/xml/cxml_parser.h"
-
-namespace {
-
-void SplitQualifiedName(const ByteStringView& bsFullName,
-                        ByteStringView* bsSpace,
-                        ByteStringView* bsName) {
-  if (bsFullName.IsEmpty())
-    return;
-
-  auto iStart = bsFullName.Find(':');
-  if (iStart.has_value()) {
-    *bsSpace = bsFullName.Left(iStart.value());
-    *bsName = bsFullName.Right(bsFullName.GetLength() - (iStart.value() + 1));
-  } else {
-    *bsName = bsFullName;
-  }
-}
-
-}  // namespace
-
-// static
-std::unique_ptr<CXML_Element> CXML_Element::Parse(const void* pBuffer,
-                                                  size_t size) {
-  CXML_Parser parser;
-  if (!parser.Init(static_cast<const uint8_t*>(pBuffer), size))
-    return nullptr;
-  return parser.ParseElement(nullptr, false);
-}
-
-CXML_Element::CXML_Element(const CXML_Element* pParent,
-                           const ByteStringView& qSpace,
-                           const ByteStringView& tagname)
-    : m_pParent(pParent), m_QSpaceName(qSpace), m_TagName(tagname) {}
-
-CXML_Element::~CXML_Element() {}
-
-CXML_Element* CXML_Element::AsElement() {
-  return this;
-}
-
-const CXML_Element* CXML_Element::AsElement() const {
-  return this;
-}
-
-ByteString CXML_Element::GetTagName() const {
-  return m_TagName;
-}
-
-ByteString CXML_Element::GetNamespaceURI(const ByteString& qName) const {
-  const CXML_Element* pElement = this;
-  do {
-    const WideString* pwsSpace;
-    if (qName.IsEmpty())
-      pwsSpace = pElement->Lookup("", "xmlns");
-    else
-      pwsSpace = pElement->Lookup("xmlns", qName);
-    if (pwsSpace)
-      return pwsSpace->UTF8Encode();
-
-    pElement = pElement->GetParent();
-  } while (pElement);
-  return ByteString();
-}
-
-void CXML_Element::GetAttrByIndex(size_t index,
-                                  ByteString* space,
-                                  ByteString* name,
-                                  WideString* value) const {
-  if (index >= m_AttrMap.size())
-    return;
-
-  const CXML_AttrItem& item = m_AttrMap[index];
-  *space = item.m_QSpaceName;
-  *name = item.m_AttrName;
-  *value = item.m_Value;
-}
-
-WideString CXML_Element::GetAttrValue(const ByteStringView& name) const {
-  ByteStringView bsSpace;
-  ByteStringView bsName;
-  SplitQualifiedName(name, &bsSpace, &bsName);
-
-  WideString attr;
-  const WideString* pValue = Lookup(ByteString(bsSpace), ByteString(bsName));
-  if (pValue)
-    attr = *pValue;
-  return attr;
-}
-
-int CXML_Element::GetAttrInteger(const ByteStringView& name) const {
-  ByteStringView bsSpace;
-  ByteStringView bsName;
-  SplitQualifiedName(name, &bsSpace, &bsName);
-
-  const WideString* pwsValue = Lookup(ByteString(bsSpace), ByteString(bsName));
-  return pwsValue ? pwsValue->GetInteger() : 0;
-}
-
-size_t CXML_Element::CountElements(const ByteStringView& space,
-                                   const ByteStringView& tag) const {
-  size_t count = 0;
-  for (const auto& pChild : m_Children) {
-    const CXML_Element* pKid = pChild->AsElement();
-    if (MatchesElement(pKid, space, tag))
-      count++;
-  }
-  return count;
-}
-
-CXML_Object* CXML_Element::GetChild(size_t index) const {
-  return index < m_Children.size() ? m_Children[index].get() : nullptr;
-}
-
-CXML_Element* CXML_Element::GetElement(const ByteStringView& space,
-                                       const ByteStringView& tag,
-                                       size_t nth) const {
-  for (const auto& pChild : m_Children) {
-    CXML_Element* pKid = pChild->AsElement();
-    if (MatchesElement(pKid, space, tag)) {
-      if (nth == 0)
-        return pKid;
-      --nth;
-    }
-  }
-  return nullptr;
-}
-
-void CXML_Element::SetAttribute(const ByteString& space,
-                                const ByteString& name,
-                                const WideString& value) {
-  for (CXML_AttrItem& item : m_AttrMap) {
-    if (item.Matches(space, name)) {
-      item.m_Value = value;
-      return;
-    }
-  }
-  m_AttrMap.push_back({space, name, WideString(value)});
-}
-
-// static
-bool CXML_Element::MatchesElement(const CXML_Element* pKid,
-                                  const ByteStringView& space,
-                                  const ByteStringView& tag) {
-  return pKid && pKid->m_TagName == tag &&
-         (space.IsEmpty() || pKid->m_QSpaceName == space);
-}
-
-const WideString* CXML_Element::Lookup(const ByteString& space,
-                                       const ByteString& name) const {
-  for (const CXML_AttrItem& item : m_AttrMap) {
-    if (item.Matches(space, name))
-      return &item.m_Value;
-  }
-  return nullptr;
-}
diff --git a/core/fxcrt/xml/cxml_element.h b/core/fxcrt/xml/cxml_element.h
deleted file mode 100644
index d3049d7..0000000
--- a/core/fxcrt/xml/cxml_element.h
+++ /dev/null
@@ -1,73 +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_FXCRT_XML_CXML_ELEMENT_H_
-#define CORE_FXCRT_XML_CXML_ELEMENT_H_
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/xml/cxml_attritem.h"
-#include "core/fxcrt/xml/cxml_object.h"
-
-class CXML_Element : public CXML_Object {
- public:
-  static std::unique_ptr<CXML_Element> Parse(const void* pBuffer, size_t size);
-
-  CXML_Element(const CXML_Element* pParent,
-               const ByteStringView& qSpace,
-               const ByteStringView& tagname);
-  ~CXML_Element() override;
-
-  // CXML_Object:
-  CXML_Element* AsElement() override;
-  const CXML_Element* AsElement() const override;
-
-  ByteString GetTagName() const;
-  ByteString GetNamespaceURI(const ByteString& qName) const;
-  const CXML_Element* GetParent() const { return m_pParent.Get(); }
-  size_t CountAttrs() const { return m_AttrMap.size(); }
-  void GetAttrByIndex(size_t index,
-                      ByteString* space,
-                      ByteString* name,
-                      WideString* value) const;
-  WideString GetAttrValue(const ByteStringView& name) const;
-
-  int GetAttrInteger(const ByteStringView& name) const;
-
-  void AppendChild(std::unique_ptr<CXML_Object> child) {
-    m_Children.push_back(std::move(child));
-  }
-
-  size_t CountChildren() const { return m_Children.size(); }
-  size_t CountElements(const ByteStringView& space,
-                       const ByteStringView& tag) const;
-  CXML_Object* GetChild(size_t index) const;
-  CXML_Element* GetElement(const ByteStringView& space,
-                           const ByteStringView& tag,
-                           size_t nth) const;
-
-  void SetAttribute(const ByteString& space,
-                    const ByteString& name,
-                    const WideString& value);
-
- private:
-  static bool MatchesElement(const CXML_Element* pKid,
-                             const ByteStringView& space,
-                             const ByteStringView& tag);
-
-  const WideString* Lookup(const ByteString& space,
-                           const ByteString& name) const;
-
-  UnownedPtr<const CXML_Element> const m_pParent;
-  const ByteString m_QSpaceName;
-  const ByteString m_TagName;
-  std::vector<CXML_AttrItem> m_AttrMap;
-  std::vector<std::unique_ptr<CXML_Object>> m_Children;
-};
-
-#endif  // CORE_FXCRT_XML_CXML_ELEMENT_H_
diff --git a/core/fxcrt/xml/cxml_object.cpp b/core/fxcrt/xml/cxml_object.cpp
deleted file mode 100644
index 61e88cb..0000000
--- a/core/fxcrt/xml/cxml_object.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/xml/cxml_object.h"
-
-CXML_Object::~CXML_Object() {}
-
-CXML_Content* CXML_Object::AsContent() {
-  return nullptr;
-}
-
-CXML_Element* CXML_Object::AsElement() {
-  return nullptr;
-}
-
-const CXML_Content* CXML_Object::AsContent() const {
-  return nullptr;
-}
-
-const CXML_Element* CXML_Object::AsElement() const {
-  return nullptr;
-}
diff --git a/core/fxcrt/xml/cxml_object.h b/core/fxcrt/xml/cxml_object.h
deleted file mode 100644
index d009359..0000000
--- a/core/fxcrt/xml/cxml_object.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_XML_CXML_OBJECT_H_
-#define CORE_FXCRT_XML_CXML_OBJECT_H_
-
-class CXML_Content;
-class CXML_Element;
-
-class CXML_Object {
- public:
-  virtual ~CXML_Object();
-
-  virtual CXML_Content* AsContent();
-  virtual const CXML_Content* AsContent() const;
-
-  virtual CXML_Element* AsElement();
-  virtual const CXML_Element* AsElement() const;
-
- protected:
-  CXML_Object() {}
-};
-
-inline CXML_Content* ToContent(CXML_Object* pObj) {
-  return pObj ? pObj->AsContent() : nullptr;
-}
-
-inline const CXML_Content* ToContent(const CXML_Object* pObj) {
-  return pObj ? pObj->AsContent() : nullptr;
-}
-
-inline CXML_Element* ToElement(CXML_Object* pObj) {
-  return pObj ? pObj->AsElement() : nullptr;
-}
-
-inline const CXML_Element* ToElement(const CXML_Object* pObj) {
-  return pObj ? pObj->AsElement() : nullptr;
-}
-
-#endif  // CORE_FXCRT_XML_CXML_OBJECT_H_
diff --git a/core/fxcrt/xml/cxml_parser.cpp b/core/fxcrt/xml/cxml_parser.cpp
deleted file mode 100644
index ae5fd6d..0000000
--- a/core/fxcrt/xml/cxml_parser.cpp
+++ /dev/null
@@ -1,528 +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 <algorithm>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/cfx_utf8decoder.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/xml/cxml_content.h"
-#include "core/fxcrt/xml/cxml_element.h"
-#include "core/fxcrt/xml/cxml_parser.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-#define FXCRTM_XML_CHARTYPE_Normal 0x00
-#define FXCRTM_XML_CHARTYPE_SpaceChar 0x01
-#define FXCRTM_XML_CHARTYPE_Letter 0x02
-#define FXCRTM_XML_CHARTYPE_Digital 0x04
-#define FXCRTM_XML_CHARTYPE_NameIntro 0x08
-#define FXCRTM_XML_CHARTYPE_NameChar 0x10
-#define FXCRTM_XML_CHARTYPE_HexDigital 0x20
-#define FXCRTM_XML_CHARTYPE_HexLowerLetter 0x40
-#define FXCRTM_XML_CHARTYPE_HexUpperLetter 0x60
-#define FXCRTM_XML_CHARTYPE_HexChar 0x60
-
-const uint8_t g_FXCRT_XML_ByteTypes[256] = {
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,
-    0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x08, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x18,
-    0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
-    0x1A, 0x1A, 0x01, 0x01,
-};
-
-constexpr int kMaxDepth = 1024;
-
-bool g_FXCRT_XML_IsWhiteSpace(uint8_t ch) {
-  return !!(g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_SpaceChar);
-}
-
-bool g_FXCRT_XML_IsDigital(uint8_t ch) {
-  return !!(g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_Digital);
-}
-
-bool g_FXCRT_XML_IsNameIntro(uint8_t ch) {
-  return !!(g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameIntro);
-}
-
-bool g_FXCRT_XML_IsNameChar(uint8_t ch) {
-  return !!(g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameChar);
-}
-
-}  // namespace
-
-CXML_Parser::CXML_Parser()
-    : m_nOffset(0),
-      m_pBuffer(nullptr),
-      m_dwBufferSize(0),
-      m_nBufferOffset(0),
-      m_dwIndex(0) {}
-
-CXML_Parser::~CXML_Parser() {}
-
-bool CXML_Parser::Init(const uint8_t* pBuffer, size_t size) {
-  m_pDataAcc = pdfium::MakeUnique<CXML_DataBufAcc>(pBuffer, size);
-  m_nOffset = 0;
-  return ReadNextBlock();
-}
-
-bool CXML_Parser::ReadNextBlock() {
-  if (!m_pDataAcc->ReadNextBlock())
-    return false;
-
-  m_pBuffer = m_pDataAcc->GetBlockBuffer();
-  m_dwBufferSize = m_pDataAcc->GetBlockSize();
-  m_nBufferOffset = 0;
-  m_dwIndex = 0;
-  return m_dwBufferSize > 0;
-}
-
-bool CXML_Parser::IsEOF() {
-  return m_pDataAcc->IsEOF() && m_dwIndex >= m_dwBufferSize;
-}
-
-void CXML_Parser::SkipWhiteSpaces() {
-  m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-  if (IsEOF())
-    return;
-
-  do {
-    while (m_dwIndex < m_dwBufferSize &&
-           g_FXCRT_XML_IsWhiteSpace(m_pBuffer[m_dwIndex])) {
-      m_dwIndex++;
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (m_dwIndex < m_dwBufferSize || IsEOF())
-      break;
-  } while (ReadNextBlock());
-}
-
-void CXML_Parser::GetName(ByteString* space, ByteString* name) {
-  m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-  if (IsEOF())
-    return;
-
-  std::ostringstream buf;
-  do {
-    while (m_dwIndex < m_dwBufferSize) {
-      uint8_t ch = m_pBuffer[m_dwIndex];
-      if (ch == ':') {
-        *space = ByteString(buf);
-        buf.str("");
-      } else if (g_FXCRT_XML_IsNameChar(ch)) {
-        buf << static_cast<char>(ch);
-      } else {
-        break;
-      }
-      m_dwIndex++;
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (m_dwIndex < m_dwBufferSize || IsEOF())
-      break;
-  } while (ReadNextBlock());
-  *name = ByteString(buf);
-}
-
-void CXML_Parser::SkipLiterals(const ByteStringView& str) {
-  m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-  if (IsEOF()) {
-    return;
-  }
-  int32_t i = 0, iLen = str.GetLength();
-  do {
-    while (m_dwIndex < m_dwBufferSize) {
-      if (str[i] != m_pBuffer[m_dwIndex++]) {
-        i = 0;
-        continue;
-      }
-      i++;
-      if (i == iLen)
-        break;
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (i == iLen)
-      return;
-
-    if (m_dwIndex < m_dwBufferSize || IsEOF())
-      break;
-  } while (ReadNextBlock());
-  while (!m_pDataAcc->IsEOF()) {
-    ReadNextBlock();
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwBufferSize);
-  }
-  m_dwIndex = m_dwBufferSize;
-}
-
-uint32_t CXML_Parser::GetCharRef() {
-  m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-  if (IsEOF())
-    return 0;
-
-  uint8_t ch;
-  int32_t iState = 0;
-  std::ostringstream buf;
-  uint32_t code = 0;
-  do {
-    while (m_dwIndex < m_dwBufferSize) {
-      ch = m_pBuffer[m_dwIndex];
-      switch (iState) {
-        case 0:
-          if (ch == '#') {
-            m_dwIndex++;
-            iState = 2;
-            break;
-          }
-          iState = 1;
-        case 1:
-          m_dwIndex++;
-          if (ch == ';') {
-            std::string ref = buf.str();
-            if (ref == "gt")
-              code = '>';
-            else if (ref == "lt")
-              code = '<';
-            else if (ref == "amp")
-              code = '&';
-            else if (ref == "apos")
-              code = '\'';
-            else if (ref == "quot")
-              code = '"';
-            iState = 10;
-            break;
-          }
-          buf << static_cast<char>(ch);
-          break;
-        case 2:
-          if (ch == 'x') {
-            m_dwIndex++;
-            iState = 4;
-            break;
-          }
-          iState = 3;
-        case 3:
-          m_dwIndex++;
-          if (ch == ';') {
-            iState = 10;
-            break;
-          }
-          if (g_FXCRT_XML_IsDigital(ch))
-            code = code * 10 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
-          break;
-        case 4:
-          m_dwIndex++;
-          if (ch == ';') {
-            iState = 10;
-            break;
-          }
-          uint8_t nHex =
-              g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar;
-          if (nHex) {
-            if (nHex == FXCRTM_XML_CHARTYPE_HexDigital) {
-              code = (code << 4) +
-                     FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
-            } else if (nHex == FXCRTM_XML_CHARTYPE_HexLowerLetter) {
-              code = (code << 4) + ch - 87;
-            } else {
-              code = (code << 4) + ch - 55;
-            }
-          }
-          break;
-      }
-      if (iState == 10)
-        break;
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF()) {
-      break;
-    }
-  } while (ReadNextBlock());
-  return code;
-}
-
-WideString CXML_Parser::GetAttrValue() {
-  m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-  if (IsEOF())
-    return WideString();
-
-  CFX_UTF8Decoder decoder;
-  uint8_t mark = 0;
-  uint8_t ch = 0;
-  do {
-    while (m_dwIndex < m_dwBufferSize) {
-      ch = m_pBuffer[m_dwIndex];
-      if (mark == 0) {
-        if (ch != '\'' && ch != '"')
-          return WideString();
-
-        mark = ch;
-        m_dwIndex++;
-        ch = 0;
-        continue;
-      }
-      m_dwIndex++;
-      if (ch == mark)
-        break;
-
-      if (ch == '&') {
-        decoder.AppendCodePoint(GetCharRef());
-        if (IsEOF())
-          return WideString(decoder.GetResult());
-      } else {
-        decoder.Input(ch);
-      }
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (ch == mark || m_dwIndex < m_dwBufferSize || IsEOF())
-      break;
-  } while (ReadNextBlock());
-  return WideString(decoder.GetResult());
-}
-
-void CXML_Parser::GetTagName(bool bStartTag,
-                             bool* bEndTag,
-                             ByteString* space,
-                             ByteString* name) {
-  m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-  if (IsEOF())
-    return;
-
-  *bEndTag = false;
-  uint8_t ch;
-  int32_t iState = bStartTag ? 1 : 0;
-  do {
-    while (m_dwIndex < m_dwBufferSize) {
-      ch = m_pBuffer[m_dwIndex];
-      switch (iState) {
-        case 0:
-          m_dwIndex++;
-          if (ch != '<')
-            break;
-
-          iState = 1;
-          break;
-        case 1:
-          if (ch == '?') {
-            m_dwIndex++;
-            SkipLiterals("?>");
-            iState = 0;
-            break;
-          }
-          if (ch == '!') {
-            m_dwIndex++;
-            SkipLiterals("-->");
-            iState = 0;
-            break;
-          }
-          if (ch == '/') {
-            m_dwIndex++;
-            GetName(space, name);
-            *bEndTag = true;
-          } else {
-            GetName(space, name);
-            *bEndTag = false;
-          }
-          return;
-      }
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (m_dwIndex < m_dwBufferSize || IsEOF())
-      break;
-  } while (ReadNextBlock());
-}
-
-std::unique_ptr<CXML_Element> CXML_Parser::ParseElement(CXML_Element* pParent,
-                                                        bool bStartTag) {
-  return ParseElementInternal(pParent, bStartTag, 0);
-}
-
-std::unique_ptr<CXML_Element> CXML_Parser::ParseElementInternal(
-    CXML_Element* pParent,
-    bool bStartTag,
-    int nDepth) {
-  if (nDepth > kMaxDepth)
-    return nullptr;
-
-  m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-  if (IsEOF())
-    return nullptr;
-
-  ByteString tag_name;
-  ByteString tag_space;
-  bool bEndTag;
-  GetTagName(bStartTag, &bEndTag, &tag_space, &tag_name);
-  if (tag_name.IsEmpty() || bEndTag)
-    return nullptr;
-
-  auto pElement = pdfium::MakeUnique<CXML_Element>(
-      pParent, tag_space.AsStringView(), tag_name.AsStringView());
-  do {
-    ByteString attr_space;
-    ByteString attr_name;
-    while (m_dwIndex < m_dwBufferSize) {
-      SkipWhiteSpaces();
-      if (IsEOF())
-        break;
-
-      if (!g_FXCRT_XML_IsNameIntro(m_pBuffer[m_dwIndex]))
-        break;
-
-      GetName(&attr_space, &attr_name);
-      SkipWhiteSpaces();
-      if (IsEOF())
-        break;
-
-      if (m_pBuffer[m_dwIndex] != '=')
-        break;
-
-      m_dwIndex++;
-      SkipWhiteSpaces();
-      if (IsEOF())
-        break;
-
-      WideString attr_value = GetAttrValue();
-      pElement->SetAttribute(attr_space, attr_name, attr_value);
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (m_dwIndex < m_dwBufferSize || IsEOF())
-      break;
-  } while (ReadNextBlock());
-  SkipWhiteSpaces();
-  if (IsEOF())
-    return pElement;
-
-  uint8_t ch = m_pBuffer[m_dwIndex++];
-  if (ch == '/') {
-    m_dwIndex++;
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    return pElement;
-  }
-  if (ch != '>') {
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    return nullptr;
-  }
-  SkipWhiteSpaces();
-  if (IsEOF())
-    return pElement;
-
-  CFX_UTF8Decoder decoder;
-  CFX_WideTextBuf content;
-  bool bCDATA = false;
-  int32_t iState = 0;
-  do {
-    while (m_dwIndex < m_dwBufferSize) {
-      ch = m_pBuffer[m_dwIndex++];
-      switch (iState) {
-        case 0:
-          if (ch == '<') {
-            iState = 1;
-          } else if (ch == '&') {
-            decoder.ClearStatus();
-            decoder.AppendCodePoint(GetCharRef());
-          } else {
-            decoder.Input(ch);
-          }
-          break;
-        case 1:
-          if (ch == '!') {
-            iState = 2;
-          } else if (ch == '?') {
-            SkipLiterals("?>");
-            SkipWhiteSpaces();
-            iState = 0;
-          } else if (ch == '/') {
-            ByteString space;
-            ByteString name;
-            GetName(&space, &name);
-            SkipWhiteSpaces();
-            m_dwIndex++;
-            iState = 10;
-          } else {
-            content << decoder.GetResult();
-            WideString dataStr = content.MakeString();
-            if (!bCDATA)
-              dataStr.TrimRight(L" \t\r\n");
-
-            InsertContentSegment(bCDATA, dataStr.AsStringView(),
-                                 pElement.get());
-            content.Clear();
-            decoder.Clear();
-            bCDATA = false;
-            iState = 0;
-            m_dwIndex--;
-            std::unique_ptr<CXML_Element> pSubElement =
-                ParseElementInternal(pElement.get(), true, nDepth + 1);
-            if (!pSubElement)
-              break;
-
-            pElement->AppendChild(std::move(pSubElement));
-            SkipWhiteSpaces();
-          }
-          break;
-        case 2:
-          if (ch == '[') {
-            SkipLiterals("]]>");
-          } else if (ch == '-') {
-            m_dwIndex++;
-            SkipLiterals("-->");
-          } else {
-            SkipLiterals(">");
-          }
-          decoder.Clear();
-          SkipWhiteSpaces();
-          iState = 0;
-          break;
-      }
-      if (iState == 10) {
-        break;
-      }
-    }
-    m_nOffset = m_nBufferOffset + static_cast<FX_FILESIZE>(m_dwIndex);
-    if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF())
-      break;
-  } while (ReadNextBlock());
-  content << decoder.GetResult();
-  WideString dataStr = content.MakeString();
-  dataStr.TrimRight(L" \t\r\n");
-
-  InsertContentSegment(bCDATA, dataStr.AsStringView(), pElement.get());
-  content.Clear();
-  decoder.Clear();
-  bCDATA = false;
-  return pElement;
-}
-
-void CXML_Parser::InsertContentSegment(bool bCDATA,
-                                       const WideStringView& content,
-                                       CXML_Element* pElement) {
-  if (content.IsEmpty())
-    return;
-
-  pElement->AppendChild(pdfium::MakeUnique<CXML_Content>(bCDATA, content));
-}
diff --git a/core/fxcrt/xml/cxml_parser.h b/core/fxcrt/xml/cxml_parser.h
deleted file mode 100644
index a6f1303..0000000
--- a/core/fxcrt/xml/cxml_parser.h
+++ /dev/null
@@ -1,57 +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_FXCRT_XML_CXML_PARSER_H_
-#define CORE_FXCRT_XML_CXML_PARSER_H_
-
-#include <algorithm>
-#include <memory>
-
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/xml/cxml_databufacc.h"
-
-class CFX_UTF8Decoder;
-class CXML_Element;
-
-class CXML_Parser {
- public:
-  CXML_Parser();
-  ~CXML_Parser();
-
-  bool Init(const uint8_t* pBuffer, size_t size);
-  bool ReadNextBlock();
-  bool IsEOF();
-  bool HaveAvailData();
-  void SkipWhiteSpaces();
-  void GetName(ByteString* space, ByteString* name);
-  WideString GetAttrValue();
-  uint32_t GetCharRef();
-  void GetTagName(bool bStartTag,
-                  bool* bEndTag,
-                  ByteString* space,
-                  ByteString* name);
-  void SkipLiterals(const ByteStringView& str);
-  std::unique_ptr<CXML_Element> ParseElement(CXML_Element* pParent,
-                                             bool bStartTag);
-  void InsertContentSegment(bool bCDATA,
-                            const WideStringView& content,
-                            CXML_Element* pElement);
-  void InsertCDATASegment(CFX_UTF8Decoder& decoder, CXML_Element* pElement);
-
- private:
-  std::unique_ptr<CXML_Element> ParseElementInternal(CXML_Element* pParent,
-                                                     bool bStartTag,
-                                                     int nDepth);
-
-  std::unique_ptr<CXML_DataBufAcc> m_pDataAcc;
-  FX_FILESIZE m_nOffset;
-  const uint8_t* m_pBuffer;
-  size_t m_dwBufferSize;
-  FX_FILESIZE m_nBufferOffset;
-  size_t m_dwIndex;
-};
-
-#endif  // CORE_FXCRT_XML_CXML_PARSER_H_
diff --git a/core/fxge/Android.bp b/core/fxge/Android.bp
new file mode 100644
index 0000000..fe648c6
--- /dev/null
+++ b/core/fxge/Android.bp
@@ -0,0 +1,36 @@
+cc_library_static {
+    name: "libpdfium-fxge",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    static_libs: [
+        "libpdfium-agg",
+        "libpdfium-fxcrt",
+    ],
+
+    exclude_srcs: [
+        // pdf_enable_xfa
+        "cfx_unicodeencodingex.cpp",
+        // is_win
+        "dib/cfx_dibextractor.cpp",
+        // is_linux
+        "fx_ge_linux.cpp",
+    ],
+
+    srcs: [
+        "*.cpp",
+        "dib/*.cpp",
+        "freetype/*.cpp",
+        "fontdata/**/*.cpp",
+        // !pdf_use_skia
+        "agg/*.cpp",
+        // is_android
+        "android/*.cpp",
+    ],
+
+    include_dirs: [
+        "external/freetype/include",
+        "external/freetype/include/freetype",
+    ],
+}
diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn
new file mode 100644
index 0000000..04742d0
--- /dev/null
+++ b/core/fxge/BUILD.gn
@@ -0,0 +1,234 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/freetype/freetype.gni")
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+config("fxge_warnings") {
+  visibility = [ ":*" ]
+  if (is_clang) {
+    cflags = [
+      # http://code.google.com/p/pdfium/issues/detail?id=188
+      "-Wno-switch",
+    ]
+  }
+}
+
+source_set("fxge") {
+  sources = [
+    "cfx_cliprgn.cpp",
+    "cfx_cliprgn.h",
+    "cfx_color.cpp",
+    "cfx_color.h",
+    "cfx_defaultrenderdevice.h",
+    "cfx_face.cpp",
+    "cfx_face.h",
+    "cfx_folderfontinfo.cpp",
+    "cfx_folderfontinfo.h",
+    "cfx_font.cpp",
+    "cfx_font.h",
+    "cfx_fontcache.cpp",
+    "cfx_fontcache.h",
+    "cfx_fontmapper.cpp",
+    "cfx_fontmapper.h",
+    "cfx_fontmgr.cpp",
+    "cfx_fontmgr.h",
+    "cfx_gemodule.cpp",
+    "cfx_gemodule.h",
+    "cfx_glyphbitmap.cpp",
+    "cfx_glyphbitmap.h",
+    "cfx_glyphcache.cpp",
+    "cfx_glyphcache.h",
+    "cfx_graphstate.cpp",
+    "cfx_graphstate.h",
+    "cfx_graphstatedata.cpp",
+    "cfx_graphstatedata.h",
+    "cfx_pathdata.cpp",
+    "cfx_pathdata.h",
+    "cfx_renderdevice.cpp",
+    "cfx_renderdevice.h",
+    "cfx_substfont.cpp",
+    "cfx_substfont.h",
+    "cfx_unicodeencoding.cpp",
+    "cfx_unicodeencoding.h",
+    "dib/cfx_bitmapcomposer.cpp",
+    "dib/cfx_bitmapcomposer.h",
+    "dib/cfx_bitmapstorer.cpp",
+    "dib/cfx_bitmapstorer.h",
+    "dib/cfx_cmyk_to_srgb.cpp",
+    "dib/cfx_cmyk_to_srgb.h",
+    "dib/cfx_dibbase.cpp",
+    "dib/cfx_dibbase.h",
+    "dib/cfx_dibitmap.cpp",
+    "dib/cfx_dibitmap.h",
+    "dib/cfx_imagerenderer.cpp",
+    "dib/cfx_imagerenderer.h",
+    "dib/cfx_imagestretcher.cpp",
+    "dib/cfx_imagestretcher.h",
+    "dib/cfx_imagetransformer.cpp",
+    "dib/cfx_imagetransformer.h",
+    "dib/cfx_scanlinecompositor.cpp",
+    "dib/cfx_scanlinecompositor.h",
+    "dib/cstretchengine.cpp",
+    "dib/cstretchengine.h",
+    "dib/fx_dib_main.cpp",
+    "dib/scanlinecomposer_iface.h",
+    "fontdata/chromefontdata/FoxitDingbats.cpp",
+    "fontdata/chromefontdata/FoxitFixed.cpp",
+    "fontdata/chromefontdata/FoxitFixedBold.cpp",
+    "fontdata/chromefontdata/FoxitFixedBoldItalic.cpp",
+    "fontdata/chromefontdata/FoxitFixedItalic.cpp",
+    "fontdata/chromefontdata/FoxitSans.cpp",
+    "fontdata/chromefontdata/FoxitSansBold.cpp",
+    "fontdata/chromefontdata/FoxitSansBoldItalic.cpp",
+    "fontdata/chromefontdata/FoxitSansItalic.cpp",
+    "fontdata/chromefontdata/FoxitSansMM.cpp",
+    "fontdata/chromefontdata/FoxitSerif.cpp",
+    "fontdata/chromefontdata/FoxitSerifBold.cpp",
+    "fontdata/chromefontdata/FoxitSerifBoldItalic.cpp",
+    "fontdata/chromefontdata/FoxitSerifItalic.cpp",
+    "fontdata/chromefontdata/FoxitSerifMM.cpp",
+    "fontdata/chromefontdata/FoxitSymbol.cpp",
+    "fontdata/chromefontdata/chromefontdata.h",
+    "freetype/fx_freetype.cpp",
+    "fx_dib.h",
+    "fx_font.cpp",
+    "fx_font.h",
+    "fx_freetype.h",
+    "fx_ge_fontmap.cpp",
+    "render_defines.h",
+    "renderdevicedriver_iface.cpp",
+    "renderdevicedriver_iface.h",
+    "scoped_font_transform.cpp",
+    "scoped_font_transform.h",
+    "scoped_font_transform.h",
+    "systemfontinfo_iface.h",
+    "text_char_pos.cpp",
+    "text_char_pos.h",
+    "text_glyph_pos.cpp",
+    "text_glyph_pos.h",
+  ]
+
+  configs += [
+    ":fxge_warnings",
+    "../../:pdfium_core_config",
+  ]
+
+  deps = [ "../fxcrt" ]
+
+  if (is_component_build || use_system_freetype) {
+    # ft_adobe_glyph_list is not exported from the Freetype shared library so we
+    # need it defined in component builds and builds using system freetype.
+    defines = [ "DEFINE_PS_TABLES_DATA" ]
+  }
+
+  if (pdf_enable_xfa) {
+    sources += [
+      "cfx_unicodeencodingex.cpp",
+      "cfx_unicodeencodingex.h",
+    ]
+  }
+
+  if (pdf_use_skia || pdf_use_skia_paths) {
+    sources += [ "skia/fx_skia_device.cpp" ]
+    deps += [ "//skia" ]
+  } else {
+    sources += [
+      "agg/fx_agg_driver.cpp",
+      "agg/fx_agg_driver.h",
+    ]
+    deps += [ "../../third_party:fx_agg" ]
+  }
+
+  if (is_android) {
+    sources += [
+      "android/cfpf_skiadevicemodule.cpp",
+      "android/cfpf_skiadevicemodule.h",
+      "android/cfpf_skiafont.cpp",
+      "android/cfpf_skiafont.h",
+      "android/cfpf_skiafontmgr.cpp",
+      "android/cfpf_skiafontmgr.h",
+      "android/cfpf_skiapathfont.cpp",
+      "android/cfpf_skiapathfont.h",
+      "android/cfx_androidfontinfo.cpp",
+      "android/cfx_androidfontinfo.h",
+      "android/fx_android_imp.cpp",
+    ]
+  }
+
+  if (is_linux) {
+    sources += [ "fx_ge_linux.cpp" ]
+  }
+
+  if (is_mac) {
+    sources += [
+      "apple/apple_int.h",
+      "apple/fx_apple_platform.cpp",
+      "apple/fx_mac_imp.cpp",
+      "apple/fx_quartz_device.cpp",
+    ]
+    libs = [ "CoreGraphics.framework" ]
+  }
+
+  if (is_win) {
+    sources += [
+      "cfx_windowsrenderdevice.h",
+      "dib/cfx_dibextractor.cpp",
+      "dib/cfx_dibextractor.h",
+      "win32/cfx_psrenderer.cpp",
+      "win32/cfx_psrenderer.h",
+      "win32/cfx_windowsdib.h",
+      "win32/cpsoutput.cpp",
+      "win32/cpsoutput.h",
+      "win32/fx_win32_device.cpp",
+      "win32/fx_win32_dib.cpp",
+      "win32/fx_win32_gdipext.cpp",
+      "win32/fx_win32_print.cpp",
+      "win32/win32_int.h",
+    ]
+    configs -= [ "//build/config/win:lean_and_mean" ]
+  }
+
+  visibility = [ "../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cfx_fontmapper_unittest.cpp",
+    "dib/cfx_cmyk_to_srgb_unittest.cpp",
+    "dib/cfx_dibitmap_unittest.cpp",
+    "dib/cstretchengine_unittest.cpp",
+    "fx_font_unittest.cpp",
+  ]
+  deps = [
+    ":fxge",
+    "../fpdfapi/page",
+    "../fpdfapi/parser",
+  ]
+  pdfium_root_dir = "../../"
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "fx_ge_text_embeddertest.cpp" ]
+  deps = []
+  pdfium_root_dir = "../../"
+
+  if (pdf_use_skia || pdf_use_skia_paths) {
+    sources += [ "skia/fx_skia_device_embeddertest.cpp" ]
+    deps += [
+      ":fxge",
+      "../../fpdfsdk",
+      "//skia",
+    ]
+  }
+
+  if (is_win) {
+    sources += [ "win32/fx_win32_device_embeddertest.cpp" ]
+    deps += [
+      ":fxge",
+      "../fxcodec",
+    ]
+  }
+}
diff --git a/core/fxge/agg/fx_agg_driver.cpp b/core/fxge/agg/fx_agg_driver.cpp
index b394fac..b2a7986 100644
--- a/core/fxge/agg/fx_agg_driver.cpp
+++ b/core/fxge/agg/fx_agg_driver.cpp
@@ -9,12 +9,22 @@
 #include <algorithm>
 #include <utility>
 
+#include "build/build_config.h"
 #include "core/fxge/cfx_cliprgn.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+// Ignore fallthrough warnings in agg23 headers.
+#if defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#endif
 #include "third_party/agg23/agg_conv_dash.h"
 #include "third_party/agg23/agg_conv_stroke.h"
 #include "third_party/agg23/agg_curves.h"
@@ -23,8 +33,9 @@
 #include "third_party/agg23/agg_rasterizer_scanline_aa.h"
 #include "third_party/agg23/agg_renderer_scanline.h"
 #include "third_party/agg23/agg_scanline_u.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#if defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
 
 namespace {
 
@@ -100,7 +111,7 @@
         uint8_t back_alpha = dest_scan[3];
         if (back_alpha == 0) {
           FXARGB_SETRGBORDERDIB(dest_scan,
-                                FXARGB_MAKE(src_alpha, src_r, src_g, src_b));
+                                ArgbEncode(src_alpha, src_r, src_g, src_b));
           dest_scan += 4;
           continue;
         }
@@ -135,17 +146,17 @@
                                 int dest_top,
                                 int width,
                                 int height,
-                                const RetainPtr<CFX_DIBSource>& pSrcBitmap,
+                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                                 int src_left,
                                 int src_top) {
   if (!pBitmap)
     return;
 
-  pBitmap->GetOverlapRect(dest_left, dest_top, width, height,
-                          pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(),
-                          src_left, src_top, nullptr);
-  if (width == 0 || height == 0)
+  if (!pBitmap->GetOverlapRect(dest_left, dest_top, width, height,
+                               pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(),
+                               src_left, src_top, nullptr)) {
     return;
+  }
 
   int Bpp = pBitmap->GetBPP() / 8;
   FXDIB_Format dest_format = pBitmap->GetFormat();
@@ -155,13 +166,12 @@
   if (dest_format == src_format) {
     for (int row = 0; row < height; row++) {
       uint8_t* dest_scan = buffer + (dest_top + row) * pitch + dest_left * Bpp;
-      uint8_t* src_scan =
-          const_cast<uint8_t*>(pSrcBitmap->GetScanline(src_top + row)) +
-          src_left * Bpp;
+      const uint8_t* src_scan =
+          pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
       if (Bpp == 4) {
         for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, FXARGB_MAKE(src_scan[3], src_scan[0],
-                                               src_scan[1], src_scan[2]));
+          FXARGB_SETDIB(dest_scan, ArgbEncode(src_scan[3], src_scan[0],
+                                              src_scan[1], src_scan[2]));
           dest_scan += 4;
           src_scan += 4;
         }
@@ -182,9 +192,8 @@
     ASSERT(src_format == FXDIB_Rgb32);
     for (int row = 0; row < height; row++) {
       uint8_t* dest_scan = dest_buf + row * pitch;
-      uint8_t* src_scan =
-          const_cast<uint8_t*>(pSrcBitmap->GetScanline(src_top + row)) +
-          src_left * 4;
+      const uint8_t* src_scan =
+          pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
       for (int col = 0; col < width; col++) {
         *dest_scan++ = src_scan[2];
         *dest_scan++ = src_scan[1];
@@ -199,12 +208,11 @@
   if (src_format == FXDIB_Rgb) {
     for (int row = 0; row < height; row++) {
       uint8_t* dest_scan = dest_buf + row * pitch;
-      uint8_t* src_scan =
-          const_cast<uint8_t*>(pSrcBitmap->GetScanline(src_top + row)) +
-          src_left * 3;
+      const uint8_t* src_scan =
+          pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
       for (int col = 0; col < width; col++) {
         FXARGB_SETDIB(dest_scan,
-                      FXARGB_MAKE(0xff, src_scan[0], src_scan[1], src_scan[2]));
+                      ArgbEncode(0xff, src_scan[0], src_scan[1], src_scan[2]));
         dest_scan += 4;
         src_scan += 3;
       }
@@ -216,12 +224,11 @@
   ASSERT(dest_format == FXDIB_Argb);
   for (int row = 0; row < height; row++) {
     uint8_t* dest_scan = dest_buf + row * pitch;
-    uint8_t* src_scan =
-        const_cast<uint8_t*>(pSrcBitmap->GetScanline(src_top + row)) +
-        src_left * 4;
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
     for (int col = 0; col < width; col++) {
       FXARGB_SETDIB(dest_scan,
-                    FXARGB_MAKE(0xff, src_scan[0], src_scan[1], src_scan[2]));
+                    ArgbEncode(0xff, src_scan[0], src_scan[1], src_scan[2]));
       src_scan += 4;
       dest_scan += 4;
     }
@@ -247,7 +254,6 @@
                      const CFX_Matrix* pObject2Device,
                      const CFX_GraphStateData* pGraphState,
                      float scale,
-                     bool bStrokeAdjust,
                      bool bTextMode) {
   agg::line_cap_e cap;
   switch (pGraphState->m_LineCap) {
@@ -280,14 +286,14 @@
         1.0f / ((pObject2Device->GetXUnit() + pObject2Device->GetYUnit()) / 2);
   }
   width = std::max(width, unit);
-  if (pGraphState->m_DashArray) {
+  if (!pGraphState->m_DashArray.empty()) {
     typedef agg::conv_dash<agg::path_storage> dash_converter;
     dash_converter dash(*path_data);
-    for (int i = 0; i < (pGraphState->m_DashCount + 1) / 2; i++) {
+    for (size_t i = 0; i < (pGraphState->m_DashArray.size() + 1) / 2; i++) {
       float on = pGraphState->m_DashArray[i * 2];
       if (on <= 0.000001f)
         on = 1.0f / 10;
-      float off = i * 2 + 1 == pGraphState->m_DashCount
+      float off = i * 2 + 1 == pGraphState->m_DashArray.size()
                       ? on
                       : pGraphState->m_DashArray[i * 2 + 1];
       off = std::max(off, 0.0f);
@@ -311,13 +317,30 @@
   rasterizer->add_path_transformed(stroke, pObject2Device);
 }
 
+constexpr int kAlternateOrWindingFillModeMask =
+    FXFILL_ALTERNATE | FXFILL_WINDING;
+
+int GetAlternateOrWindingFillMode(int fill_mode) {
+  return fill_mode & kAlternateOrWindingFillModeMask;
+}
+
+bool IsAlternateOrWindingFillMode(int fill_mode) {
+  return !!GetAlternateOrWindingFillMode(fill_mode);
+}
+
+agg::filling_rule_e GetAlternateOrWindingFillType(int fill_mode) {
+  return GetAlternateOrWindingFillMode(fill_mode) == FXFILL_WINDING
+             ? agg::fill_non_zero
+             : agg::fill_even_odd;
+}
+
 class CFX_Renderer {
  public:
   // Needed for agg caller
   void prepare(unsigned) {}
 
   void CompositeSpan(uint8_t* dest_scan,
-                     uint8_t* ori_scan,
+                     uint8_t* backdrop_scan,
                      int Bpp,
                      bool bDestAlpha,
                      int span_left,
@@ -378,7 +401,7 @@
                          uint8_t* dest_extra_alpha_scan);
 
   bool Init(const RetainPtr<CFX_DIBitmap>& pDevice,
-            const RetainPtr<CFX_DIBitmap>& pOriDevice,
+            const RetainPtr<CFX_DIBitmap>& pBackdropDevice,
             const CFX_ClipRgn* pClipRgn,
             uint32_t color,
             bool bFullCover,
@@ -434,14 +457,14 @@
   bool m_bFullCover;
   bool m_bRgbByteOrder;
   FX_RECT m_ClipBox;
-  RetainPtr<CFX_DIBitmap> m_pOriDevice;
+  RetainPtr<CFX_DIBitmap> m_pBackdropDevice;
   RetainPtr<CFX_DIBitmap> m_pClipMask;
   RetainPtr<CFX_DIBitmap> m_pDevice;
   UnownedPtr<const CFX_ClipRgn> m_pClipRgn;
 };
 
 void CFX_Renderer::CompositeSpan(uint8_t* dest_scan,
-                                 uint8_t* ori_scan,
+                                 uint8_t* backdrop_scan,
                                  int Bpp,
                                  bool bDestAlpha,
                                  int span_left,
@@ -455,30 +478,33 @@
   int col_end = GetColEnd(span_left, span_len, clip_right);
   if (Bpp) {
     dest_scan += col_start * Bpp;
-    ori_scan += col_start * Bpp;
+    backdrop_scan += col_start * Bpp;
   } else {
     dest_scan += col_start / 8;
-    ori_scan += col_start / 8;
+    backdrop_scan += col_start / 8;
   }
   if (m_bRgbByteOrder) {
     if (Bpp == 4 && bDestAlpha) {
       for (int col = col_start; col < col_end; col++) {
         int src_alpha = GetSrcAlpha(clip_scan, col);
         uint8_t dest_alpha =
-            ori_scan[3] + src_alpha - ori_scan[3] * src_alpha / 255;
+            backdrop_scan[3] + src_alpha - backdrop_scan[3] * src_alpha / 255;
         dest_scan[3] = dest_alpha;
         int alpha_ratio = src_alpha * 255 / dest_alpha;
         if (m_bFullCover) {
-          *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Red, alpha_ratio);
-          *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, alpha_ratio);
-          *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, alpha_ratio);
+          *dest_scan++ =
+              FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Red, alpha_ratio);
+          *dest_scan++ =
+              FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Green, alpha_ratio);
+          *dest_scan++ =
+              FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Blue, alpha_ratio);
           dest_scan++;
-          ori_scan++;
+          backdrop_scan++;
         } else {
-          int r = FXDIB_ALPHA_MERGE(*ori_scan++, m_Red, alpha_ratio);
-          int g = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, alpha_ratio);
-          int b = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, alpha_ratio);
-          ori_scan++;
+          int r = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Red, alpha_ratio);
+          int g = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Green, alpha_ratio);
+          int b = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Blue, alpha_ratio);
+          backdrop_scan++;
           *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, r, cover_scan[col]);
           dest_scan++;
           *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, g, cover_scan[col]);
@@ -492,10 +518,10 @@
     if (Bpp == 3 || Bpp == 4) {
       for (int col = col_start; col < col_end; col++) {
         int src_alpha = GetSrcAlpha(clip_scan, col);
-        int r = FXDIB_ALPHA_MERGE(*ori_scan++, m_Red, src_alpha);
-        int g = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, src_alpha);
-        int b = FXDIB_ALPHA_MERGE(*ori_scan, m_Blue, src_alpha);
-        ori_scan += Bpp - 2;
+        int r = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Red, src_alpha);
+        int g = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Green, src_alpha);
+        int b = FXDIB_ALPHA_MERGE(*backdrop_scan, m_Blue, src_alpha);
+        backdrop_scan += Bpp - 2;
         *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, r, cover_scan[col]);
         dest_scan++;
         *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, g, cover_scan[col]);
@@ -545,24 +571,23 @@
     for (int col = col_start; col < col_end; col++) {
       int src_alpha = GetSrcAlpha(clip_scan, col);
       if (m_bFullCover) {
-        *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, src_alpha);
-        *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, src_alpha);
-        *dest_scan = FXDIB_ALPHA_MERGE(*ori_scan, m_Red, src_alpha);
+        *dest_scan++ = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Blue, src_alpha);
+        *dest_scan++ = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Green, src_alpha);
+        *dest_scan = FXDIB_ALPHA_MERGE(*backdrop_scan, m_Red, src_alpha);
         dest_scan += Bpp - 2;
-        ori_scan += Bpp - 2;
+        backdrop_scan += Bpp - 2;
         continue;
       }
-      int b = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, src_alpha);
-      int g = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, src_alpha);
-      int r = FXDIB_ALPHA_MERGE(*ori_scan, m_Red, src_alpha);
-      ori_scan += Bpp - 2;
+      int b = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Blue, src_alpha);
+      int g = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Green, src_alpha);
+      int r = FXDIB_ALPHA_MERGE(*backdrop_scan, m_Red, src_alpha);
+      backdrop_scan += Bpp - 2;
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, b, cover_scan[col]);
       dest_scan++;
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, g, cover_scan[col]);
       dest_scan++;
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, r, cover_scan[col]);
       dest_scan += Bpp - 2;
-      continue;
     }
     return;
   }
@@ -570,10 +595,10 @@
     for (int col = col_start; col < col_end; col++) {
       int src_alpha = GetSrcAlpha(clip_scan, col);
       if (m_bFullCover) {
-        *dest_scan = FXDIB_ALPHA_MERGE(*ori_scan++, m_Gray, src_alpha);
+        *dest_scan = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Gray, src_alpha);
         continue;
       }
-      int gray = FXDIB_ALPHA_MERGE(*ori_scan++, m_Gray, src_alpha);
+      int gray = FXDIB_ALPHA_MERGE(*backdrop_scan++, m_Gray, src_alpha);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, cover_scan[col]);
       dest_scan++;
     }
@@ -877,7 +902,7 @@
 }
 
 bool CFX_Renderer::Init(const RetainPtr<CFX_DIBitmap>& pDevice,
-                        const RetainPtr<CFX_DIBitmap>& pOriDevice,
+                        const RetainPtr<CFX_DIBitmap>& pBackdropDevice,
                         const CFX_ClipRgn* pClipRgn,
                         uint32_t color,
                         bool bFullCover,
@@ -886,7 +911,7 @@
   m_pClipRgn = pClipRgn;
   composite_span = nullptr;
   m_bRgbByteOrder = bRgbByteOrder;
-  m_pOriDevice = pOriDevice;
+  m_pBackdropDevice = pBackdropDevice;
   if (m_pClipRgn) {
     m_ClipBox = m_pClipRgn->GetBox();
   } else {
@@ -929,7 +954,7 @@
 
 template <class Scanline>
 void CFX_Renderer::render(const Scanline& sl) {
-  if (!m_pOriDevice && !composite_span)
+  if (!m_pBackdropDevice && !composite_span)
     return;
 
   int y = sl.y();
@@ -943,9 +968,11 @@
     dest_scan_extra_alpha =
         pAlphaMask->GetBuffer() + pAlphaMask->GetPitch() * y;
   }
-  uint8_t* ori_scan = nullptr;
-  if (m_pOriDevice)
-    ori_scan = m_pOriDevice->GetBuffer() + m_pOriDevice->GetPitch() * y;
+  uint8_t* backdrop_scan = nullptr;
+  if (m_pBackdropDevice) {
+    backdrop_scan =
+        m_pBackdropDevice->GetBuffer() + m_pBackdropDevice->GetPitch() * y;
+  }
   int Bpp = m_pDevice->GetBPP() / 8;
   bool bDestAlpha = m_pDevice->HasAlpha() || m_pDevice->IsAlphaMask();
   unsigned num_spans = sl.num_spans();
@@ -957,15 +984,15 @@
     int x = span->x;
     uint8_t* dest_pos = nullptr;
     uint8_t* dest_extra_alpha_pos = nullptr;
-    uint8_t* ori_pos = nullptr;
+    uint8_t* backdrop_pos = nullptr;
     if (Bpp) {
-      ori_pos = ori_scan ? ori_scan + x * Bpp : nullptr;
+      backdrop_pos = backdrop_scan ? backdrop_scan + x * Bpp : nullptr;
       dest_pos = dest_scan + x * Bpp;
       dest_extra_alpha_pos =
           dest_scan_extra_alpha ? dest_scan_extra_alpha + x : nullptr;
     } else {
       dest_pos = dest_scan + x / 8;
-      ori_pos = ori_scan ? ori_scan + x / 8 : nullptr;
+      backdrop_pos = backdrop_scan ? backdrop_scan + x / 8 : nullptr;
     }
     uint8_t* clip_pos = nullptr;
     if (m_pClipMask) {
@@ -973,8 +1000,8 @@
                  (y - m_ClipBox.top) * m_pClipMask->GetPitch() + x -
                  m_ClipBox.left;
     }
-    if (ori_pos) {
-      CompositeSpan(dest_pos, ori_pos, Bpp, bDestAlpha, x, span->len,
+    if (backdrop_pos) {
+      CompositeSpan(dest_pos, backdrop_pos, Bpp, bDestAlpha, x, span->len,
                     span->covers, m_ClipBox.left, m_ClipBox.right, clip_pos);
     } else {
       (this->*composite_span)(dest_pos, Bpp, x, span->len, span->covers,
@@ -1106,16 +1133,13 @@
 CFX_AggDeviceDriver::CFX_AggDeviceDriver(
     const RetainPtr<CFX_DIBitmap>& pBitmap,
     bool bRgbByteOrder,
-    const RetainPtr<CFX_DIBitmap>& pOriDevice,
+    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
     bool bGroupKnockout)
     : m_pBitmap(pBitmap),
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-      m_pPlatformGraphics(nullptr),
-#endif
-      m_FillFlags(0),
       m_bRgbByteOrder(bRgbByteOrder),
-      m_pOriDevice(pOriDevice),
-      m_bGroupKnockout(bGroupKnockout) {
+      m_bGroupKnockout(bGroupKnockout),
+      m_pBackdropBitmap(pBackdropBitmap) {
+  ASSERT(m_pBitmap);
   InitPlatform();
 }
 
@@ -1127,25 +1151,27 @@
   return m_pBitmap->GetBuffer();
 }
 
-#if _FX_PLATFORM_ != _FX_PLATFORM_APPLE_
+#if !defined(OS_MACOSX)
 void CFX_AggDeviceDriver::InitPlatform() {}
 
 void CFX_AggDeviceDriver::DestroyPlatform() {}
 
 bool CFX_AggDeviceDriver::DrawDeviceText(int nChars,
-                                         const FXTEXT_CHARPOS* pCharPos,
+                                         const TextCharPos* pCharPos,
                                          CFX_Font* pFont,
-                                         const CFX_Matrix* pObject2Device,
+                                         const CFX_Matrix& mtObject2Device,
                                          float font_size,
                                          uint32_t color) {
   return false;
 }
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_APPLE_
+#endif  // !defined(OS_MACOSX)
+
+DeviceType CFX_AggDeviceDriver::GetDeviceType() const {
+  return DeviceType::kDisplay;
+}
 
 int CFX_AggDeviceDriver::GetDeviceCaps(int caps_id) const {
   switch (caps_id) {
-    case FXDC_DEVICE_CLASS:
-      return FXDC_DISPLAY;
     case FXDC_PIXEL_WIDTH:
       return m_pBitmap->GetWidth();
     case FXDC_PIXEL_HEIGHT:
@@ -1171,6 +1197,7 @@
       return flags;
     }
     default:
+      NOTREACHED();
       return 0;
   }
 }
@@ -1221,6 +1248,10 @@
 bool CFX_AggDeviceDriver::SetClip_PathFill(const CFX_PathData* pPathData,
                                            const CFX_Matrix* pObject2Device,
                                            int fill_mode) {
+  ASSERT(IsAlternateOrWindingFillMode(fill_mode));
+  ASSERT(GetAlternateOrWindingFillMode(fill_mode) !=
+         kAlternateOrWindingFillModeMask);
+
   m_FillFlags = fill_mode;
   if (!m_pClipRgn) {
     m_pClipRgn = pdfium::MakeUnique<CFX_ClipRgn>(
@@ -1246,9 +1277,7 @@
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
   rasterizer.add_path(path_data.m_PathData);
-  rasterizer.filling_rule((fill_mode & 3) == FXFILL_WINDING
-                              ? agg::fill_non_zero
-                              : agg::fill_even_odd);
+  rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_mode));
   SetClipMask(rasterizer);
   return true;
 }
@@ -1268,7 +1297,7 @@
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
   RasterizeStroke(&rasterizer, &path_data.m_PathData, pObject2Device,
-                  pGraphState, 1.0f, false, false);
+                  pGraphState, 1.0f, false);
   rasterizer.filling_rule(agg::fill_non_zero);
   SetClipMask(rasterizer);
   return true;
@@ -1283,7 +1312,7 @@
     uint32_t color,
     bool bFullCover,
     bool bGroupKnockout) {
-  RetainPtr<CFX_DIBitmap> pt = bGroupKnockout ? m_pOriDevice : nullptr;
+  RetainPtr<CFX_DIBitmap> pt = bGroupKnockout ? m_pBackdropBitmap : nullptr;
   CFX_Renderer render;
   if (!render.Init(m_pBitmap, pt, m_pClipRgn.get(), color, bFullCover,
                    m_bRgbByteOrder)) {
@@ -1301,15 +1330,18 @@
                                    uint32_t fill_color,
                                    uint32_t stroke_color,
                                    int fill_mode,
-                                   int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                   BlendMode blend_type) {
+  ASSERT(GetAlternateOrWindingFillMode(fill_mode) !=
+         kAlternateOrWindingFillModeMask);
+
+  if (blend_type != BlendMode::kNormal)
     return false;
 
   if (!GetBuffer())
     return true;
 
   m_FillFlags = fill_mode;
-  if ((fill_mode & 3) && fill_color) {
+  if (IsAlternateOrWindingFillMode(fill_mode) && fill_color) {
     CAgg_PathData path_data;
     path_data.BuildPath(pPathData, pObject2Device);
     agg::rasterizer_scanline_aa rasterizer;
@@ -1317,9 +1349,7 @@
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
     rasterizer.add_path(path_data.m_PathData);
-    rasterizer.filling_rule((fill_mode & 3) == FXFILL_WINDING
-                                ? agg::fill_non_zero
-                                : agg::fill_even_odd);
+    rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_mode));
     if (!RenderRasterizer(rasterizer, fill_color,
                           !!(fill_mode & FXFILL_FULLCOVER), false)) {
       return false;
@@ -1337,7 +1367,7 @@
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                         static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
     RasterizeStroke(&rasterizer, &path_data.m_PathData, nullptr, pGraphState, 1,
-                    false, !!(fill_mode & FX_STROKE_TEXT_MODE));
+                    !!(fill_mode & FX_STROKE_TEXT_MODE));
     return RenderRasterizer(rasterizer, stroke_color,
                             !!(fill_mode & FXFILL_FULLCOVER), m_bGroupKnockout);
   }
@@ -1350,8 +1380,7 @@
         pObject2Device->a / matrix1.a, pObject2Device->b / matrix1.a,
         pObject2Device->c / matrix1.d, pObject2Device->d / matrix1.d, 0, 0);
 
-    matrix1 = *pObject2Device;
-    matrix1.Concat(matrix2.GetInverse());
+    matrix1 = *pObject2Device * matrix2.GetInverse();
   }
 
   CAgg_PathData path_data;
@@ -1361,7 +1390,7 @@
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_WIDTH)),
                       static_cast<float>(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
   RasterizeStroke(&rasterizer, &path_data.m_PathData, &matrix2, pGraphState,
-                  matrix1.a, false, !!(fill_mode & FX_STROKE_TEXT_MODE));
+                  matrix1.a, !!(fill_mode & FX_STROKE_TEXT_MODE));
   return RenderRasterizer(rasterizer, stroke_color,
                           !!(fill_mode & FXFILL_FULLCOVER), m_bGroupKnockout);
 }
@@ -1398,10 +1427,10 @@
   return DibSetPixel(m_pBitmap, x, y, color);
 }
 
-bool CFX_AggDeviceDriver::FillRectWithBlend(const FX_RECT* pRect,
+bool CFX_AggDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
                                             uint32_t fill_color,
-                                            int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                            BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
 
   if (!m_pBitmap->GetBuffer())
@@ -1410,8 +1439,7 @@
   FX_RECT clip_rect;
   GetClipBox(&clip_rect);
   FX_RECT draw_rect = clip_rect;
-  if (pRect)
-    draw_rect.Intersect(*pRect);
+  draw_rect.Intersect(rect);
   if (draw_rect.IsEmpty())
     return true;
 
@@ -1429,8 +1457,8 @@
   m_pBitmap->CompositeMask(draw_rect.left, draw_rect.top, draw_rect.Width(),
                            draw_rect.Height(), m_pClipRgn->GetMask(),
                            fill_color, draw_rect.left - clip_rect.left,
-                           draw_rect.top - clip_rect.top, FXDIB_BLEND_NORMAL,
-                           nullptr, m_bRgbByteOrder, 0);
+                           draw_rect.top - clip_rect.top, BlendMode::kNormal,
+                           nullptr, m_bRgbByteOrder);
   return true;
 }
 
@@ -1448,19 +1476,19 @@
 bool CFX_AggDeviceDriver::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
                                     int left,
                                     int top) {
-  if (!m_pBitmap || !m_pBitmap->GetBuffer())
+  if (!m_pBitmap->GetBuffer())
     return true;
 
   FX_RECT rect(left, top, left + pBitmap->GetWidth(),
                top + pBitmap->GetHeight());
   RetainPtr<CFX_DIBitmap> pBack;
-  if (m_pOriDevice) {
-    pBack = m_pOriDevice->Clone(&rect);
+  if (m_pBackdropBitmap) {
+    pBack = m_pBackdropBitmap->Clone(&rect);
     if (!pBack)
       return true;
 
     pBack->CompositeBitmap(0, 0, pBack->GetWidth(), pBack->GetHeight(),
-                           m_pBitmap, 0, 0);
+                           m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false);
   } else {
     pBack = m_pBitmap->Clone(&rect);
     if (!pBack)
@@ -1479,45 +1507,45 @@
 }
 
 RetainPtr<CFX_DIBitmap> CFX_AggDeviceDriver::GetBackDrop() {
-  return m_pOriDevice;
+  return m_pBackdropBitmap;
 }
 
-bool CFX_AggDeviceDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CFX_AggDeviceDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                                     uint32_t argb,
-                                    const FX_RECT* pSrcRect,
+                                    const FX_RECT& src_rect,
                                     int left,
                                     int top,
-                                    int blend_type) {
+                                    BlendMode blend_type) {
   if (!m_pBitmap->GetBuffer())
     return true;
 
   if (pBitmap->IsAlphaMask()) {
-    return m_pBitmap->CompositeMask(left, top, pSrcRect->Width(),
-                                    pSrcRect->Height(), pBitmap, argb,
-                                    pSrcRect->left, pSrcRect->top, blend_type,
-                                    m_pClipRgn.get(), m_bRgbByteOrder, 0);
+    return m_pBitmap->CompositeMask(left, top, src_rect.Width(),
+                                    src_rect.Height(), pBitmap, argb,
+                                    src_rect.left, src_rect.top, blend_type,
+                                    m_pClipRgn.get(), m_bRgbByteOrder);
   }
   return m_pBitmap->CompositeBitmap(
-      left, top, pSrcRect->Width(), pSrcRect->Height(), pBitmap, pSrcRect->left,
-      pSrcRect->top, blend_type, m_pClipRgn.get(), m_bRgbByteOrder);
+      left, top, src_rect.Width(), src_rect.Height(), pBitmap, src_rect.left,
+      src_rect.top, blend_type, m_pClipRgn.get(), m_bRgbByteOrder);
 }
 
-bool CFX_AggDeviceDriver::StretchDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CFX_AggDeviceDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                         uint32_t argb,
                                         int dest_left,
                                         int dest_top,
                                         int dest_width,
                                         int dest_height,
                                         const FX_RECT* pClipRect,
-                                        uint32_t flags,
-                                        int blend_type) {
+                                        const FXDIB_ResampleOptions& options,
+                                        BlendMode blend_type) {
   if (!m_pBitmap->GetBuffer())
     return true;
 
   if (dest_width == pSource->GetWidth() &&
       dest_height == pSource->GetHeight()) {
     FX_RECT rect(0, 0, dest_width, dest_height);
-    return SetDIBits(pSource, argb, &rect, dest_left, dest_top, blend_type);
+    return SetDIBits(pSource, argb, rect, dest_left, dest_top, blend_type);
   }
   FX_RECT dest_rect(dest_left, dest_top, dest_left + dest_width,
                     dest_top + dest_height);
@@ -1526,35 +1554,35 @@
   dest_clip.Intersect(*pClipRect);
   CFX_BitmapComposer composer;
   composer.Compose(m_pBitmap, m_pClipRgn.get(), 255, argb, dest_clip, false,
-                   false, false, m_bRgbByteOrder, 0, blend_type);
+                   false, false, m_bRgbByteOrder, blend_type);
   dest_clip.Offset(-dest_rect.left, -dest_rect.top);
   CFX_ImageStretcher stretcher(&composer, pSource, dest_width, dest_height,
-                               dest_clip, flags);
+                               dest_clip, options);
   if (stretcher.Start())
     stretcher.Continue(nullptr);
   return true;
 }
 
 bool CFX_AggDeviceDriver::StartDIBits(
-    const RetainPtr<CFX_DIBSource>& pSource,
+    const RetainPtr<CFX_DIBBase>& pSource,
     int bitmap_alpha,
     uint32_t argb,
-    const CFX_Matrix* pMatrix,
-    uint32_t render_flags,
+    const CFX_Matrix& matrix,
+    const FXDIB_ResampleOptions& options,
     std::unique_ptr<CFX_ImageRenderer>* handle,
-    int blend_type) {
+    BlendMode blend_type) {
   if (!m_pBitmap->GetBuffer())
     return true;
 
   *handle = pdfium::MakeUnique<CFX_ImageRenderer>(
-      m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, pMatrix,
-      render_flags, m_bRgbByteOrder);
+      m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, matrix, options,
+      m_bRgbByteOrder);
   return true;
 }
 
 bool CFX_AggDeviceDriver::ContinueDIBits(CFX_ImageRenderer* pHandle,
-                                         IFX_PauseIndicator* pPause) {
-  return m_pBitmap->GetBuffer() ? pHandle->Continue(pPause) : true;
+                                         PauseIndicatorIface* pPause) {
+  return !m_pBitmap->GetBuffer() || pHandle->Continue(pPause);
 }
 
 #ifndef _SKIA_SUPPORT_
@@ -1562,16 +1590,17 @@
 
 CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() {}
 
-bool CFX_DefaultRenderDevice::Attach(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                     bool bRgbByteOrder,
-                                     const RetainPtr<CFX_DIBitmap>& pOriDevice,
-                                     bool bGroupKnockout) {
+bool CFX_DefaultRenderDevice::Attach(
+    const RetainPtr<CFX_DIBitmap>& pBitmap,
+    bool bRgbByteOrder,
+    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+    bool bGroupKnockout) {
   if (!pBitmap)
     return false;
 
   SetBitmap(pBitmap);
   SetDeviceDriver(pdfium::MakeUnique<CFX_AggDeviceDriver>(
-      pBitmap, bRgbByteOrder, pOriDevice, bGroupKnockout));
+      pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout));
   return true;
 }
 
@@ -1579,14 +1608,14 @@
     int width,
     int height,
     FXDIB_Format format,
-    const RetainPtr<CFX_DIBitmap>& pOriDevice) {
+    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap) {
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!pBitmap->Create(width, height, format))
     return false;
 
   SetBitmap(pBitmap);
-  SetDeviceDriver(pdfium::MakeUnique<CFX_AggDeviceDriver>(pBitmap, false,
-                                                          pOriDevice, false));
+  SetDeviceDriver(pdfium::MakeUnique<CFX_AggDeviceDriver>(
+      pBitmap, false, pBackdropBitmap, false));
   return true;
 }
 
diff --git a/core/fxge/agg/fx_agg_driver.h b/core/fxge/agg/fx_agg_driver.h
index 92500c3..0578421 100644
--- a/core/fxge/agg/fx_agg_driver.h
+++ b/core/fxge/agg/fx_agg_driver.h
@@ -10,7 +10,8 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxge/ifx_renderdevicedriver.h"
+#include "build/build_config.h"
+#include "core/fxge/renderdevicedriver_iface.h"
 #include "third_party/agg23/agg_clip_liang_barsky.h"
 #include "third_party/agg23/agg_path_storage.h"
 #include "third_party/agg23/agg_rasterizer_scanline_aa.h"
@@ -30,18 +31,19 @@
   agg::path_storage m_PathData;
 };
 
-class CFX_AggDeviceDriver : public IFX_RenderDeviceDriver {
+class CFX_AggDeviceDriver final : public RenderDeviceDriverIface {
  public:
   CFX_AggDeviceDriver(const RetainPtr<CFX_DIBitmap>& pBitmap,
                       bool bRgbByteOrder,
-                      const RetainPtr<CFX_DIBitmap>& pOriDevice,
+                      const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
                       bool bGroupKnockout);
   ~CFX_AggDeviceDriver() override;
 
   void InitPlatform();
   void DestroyPlatform();
 
-  // IFX_RenderDeviceDriver
+  // RenderDeviceDriverIface:
+  DeviceType GetDeviceType() const override;
   int GetDeviceCaps(int caps_id) const override;
   void SaveState() override;
   void RestoreState(bool bKeepSaved) override;
@@ -57,44 +59,44 @@
                 uint32_t fill_color,
                 uint32_t stroke_color,
                 int fill_mode,
-                int blend_type) override;
+                BlendMode blend_type) override;
   bool SetPixel(int x, int y, uint32_t color) override;
-  bool FillRectWithBlend(const FX_RECT* pRect,
+  bool FillRectWithBlend(const FX_RECT& rect,
                          uint32_t fill_color,
-                         int blend_type) override;
+                         BlendMode blend_type) override;
   bool GetClipBox(FX_RECT* pRect) override;
   bool GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
                  int left,
                  int top) override;
   RetainPtr<CFX_DIBitmap> GetBackDrop() override;
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
-                 uint32_t color,
-                 const FX_RECT* pSrcRect,
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                 uint32_t argb,
+                 const FX_RECT& src_rect,
                  int left,
                  int top,
-                 int blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
-                     uint32_t color,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
+                     uint32_t argb,
                      int dest_left,
                      int dest_top,
                      int dest_width,
                      int dest_height,
                      const FX_RECT* pClipRect,
-                     uint32_t flags,
-                     int blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                    int bitmap_alpha,
-                   uint32_t color,
-                   const CFX_Matrix* pMatrix,
-                   uint32_t flags,
+                   uint32_t argb,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                   int blend_type) override;
+                   BlendMode blend_type) override;
   bool ContinueDIBits(CFX_ImageRenderer* handle,
-                      IFX_PauseIndicator* pPause) override;
+                      PauseIndicatorIface* pPause) override;
   bool DrawDeviceText(int nChars,
-                      const FXTEXT_CHARPOS* pCharPos,
+                      const TextCharPos* pCharPos,
                       CFX_Font* pFont,
-                      const CFX_Matrix* pObject2Device,
+                      const CFX_Matrix& mtObject2Device,
                       float font_size,
                       uint32_t color) override;
   int GetDriverType() const override;
@@ -109,16 +111,16 @@
   virtual uint8_t* GetBuffer() const;
 
  private:
-  RetainPtr<CFX_DIBitmap> m_pBitmap;
+  RetainPtr<CFX_DIBitmap> const m_pBitmap;
   std::unique_ptr<CFX_ClipRgn> m_pClipRgn;
   std::vector<std::unique_ptr<CFX_ClipRgn>> m_StateStack;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  void* m_pPlatformGraphics;
+#if defined(OS_MACOSX)
+  void* m_pPlatformGraphics = nullptr;
 #endif
-  int m_FillFlags;
-  bool m_bRgbByteOrder;
-  RetainPtr<CFX_DIBitmap> m_pOriDevice;
-  bool m_bGroupKnockout;
+  int m_FillFlags = 0;
+  const bool m_bRgbByteOrder;
+  const bool m_bGroupKnockout;
+  RetainPtr<CFX_DIBitmap> m_pBackdropBitmap;
 };
 
 #endif  // CORE_FXGE_AGG_FX_AGG_DRIVER_H_
diff --git a/core/fxge/android/cfpf_skiabufferfont.cpp b/core/fxge/android/cfpf_skiabufferfont.cpp
deleted file mode 100644
index 3fce483..0000000
--- a/core/fxge/android/cfpf_skiabufferfont.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/android/cfpf_skiabufferfont.h"
-
-CFPF_SkiaBufferFont::CFPF_SkiaBufferFont()
-    : m_pBuffer(nullptr), m_szBuffer(0) {}
-
-CFPF_SkiaBufferFont::~CFPF_SkiaBufferFont() = default;
-
-int32_t CFPF_SkiaBufferFont::GetType() const {
-  return FPF_SKIAFONTTYPE_Buffer;
-}
diff --git a/core/fxge/android/cfpf_skiabufferfont.h b/core/fxge/android/cfpf_skiabufferfont.h
deleted file mode 100644
index 3a94a51..0000000
--- a/core/fxge/android/cfpf_skiabufferfont.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_FXGE_ANDROID_CFPF_SKIABUFFERFONT_H_
-#define CORE_FXGE_ANDROID_CFPF_SKIABUFFERFONT_H_
-
-#include "core/fxge/android/cfpf_skiafontdescriptor.h"
-
-#define FPF_SKIAFONTTYPE_Buffer 3
-
-class CFPF_SkiaBufferFont : public CFPF_SkiaFontDescriptor {
- public:
-  CFPF_SkiaBufferFont();
-  ~CFPF_SkiaBufferFont() override;
-
-  // CFPF_SkiaFontDescriptor
-  int32_t GetType() const override;
-
-  void* m_pBuffer;
-  size_t m_szBuffer;
-};
-
-#endif  // CORE_FXGE_ANDROID_CFPF_SKIABUFFERFONT_H_
diff --git a/core/fxge/android/cfpf_skiafilefont.cpp b/core/fxge/android/cfpf_skiafilefont.cpp
deleted file mode 100644
index 2cdf58a..0000000
--- a/core/fxge/android/cfpf_skiafilefont.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/android/cfpf_skiafilefont.h"
-
-#include "core/fxcrt/fx_stream.h"
-
-CFPF_SkiaFileFont::CFPF_SkiaFileFont() = default;
-
-CFPF_SkiaFileFont::~CFPF_SkiaFileFont() = default;
-
-int32_t CFPF_SkiaFileFont::GetType() const {
-  return FPF_SKIAFONTTYPE_File;
-}
diff --git a/core/fxge/android/cfpf_skiafilefont.h b/core/fxge/android/cfpf_skiafilefont.h
deleted file mode 100644
index 5e2ebbc..0000000
--- a/core/fxge/android/cfpf_skiafilefont.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_ANDROID_CFPF_SKIAFILEFONT_H_
-#define CORE_FXGE_ANDROID_CFPF_SKIAFILEFONT_H_
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/android/cfpf_skiafontdescriptor.h"
-
-class IFX_SeekableReadStream;
-
-#define FPF_SKIAFONTTYPE_File 2
-
-class CFPF_SkiaFileFont : public CFPF_SkiaFontDescriptor {
- public:
-  CFPF_SkiaFileFont();
-  ~CFPF_SkiaFileFont() override;
-
-  // CFPF_SkiaFontDescriptor
-  int32_t GetType() const override;
-
-  RetainPtr<IFX_SeekableReadStream> m_pFile;
-};
-
-#endif  // CORE_FXGE_ANDROID_CFPF_SKIAFILEFONT_H_
diff --git a/core/fxge/android/cfpf_skiafont.cpp b/core/fxge/android/cfpf_skiafont.cpp
index 1ca0f57..1c4eece 100644
--- a/core/fxge/android/cfpf_skiafont.cpp
+++ b/core/fxge/android/cfpf_skiafont.cpp
@@ -8,129 +8,114 @@
 
 #include <algorithm>
 
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/android/cfpf_skiabufferfont.h"
-#include "core/fxge/android/cfpf_skiafilefont.h"
-#include "core/fxge/android/cfpf_skiafontdescriptor.h"
 #include "core/fxge/android/cfpf_skiafontmgr.h"
 #include "core/fxge/android/cfpf_skiapathfont.h"
 #include "core/fxge/fx_freetype.h"
 
 #define FPF_EM_ADJUST(em, a) (em == 0 ? (a) : (a)*1000 / em)
 
-CFPF_SkiaFont::CFPF_SkiaFont()
-    : m_pFontMgr(nullptr),
-      m_pFontDes(nullptr),
-      m_Face(nullptr),
-      m_dwStyle(0),
-      m_uCharset(0),
-      m_dwRefCount(0) {}
+CFPF_SkiaFont::CFPF_SkiaFont(CFPF_SkiaFontMgr* pFontMgr,
+                             const CFPF_SkiaPathFont* pFont,
+                             uint32_t dwStyle,
+                             uint8_t uCharset)
+    : m_pFontMgr(pFontMgr),
+      m_pFont(pFont),
+      m_Face(m_pFontMgr->GetFontFace(m_pFont->path(), m_pFont->face_index())),
+      m_dwStyle(dwStyle),
+      m_uCharset(uCharset) {}
 
-CFPF_SkiaFont::~CFPF_SkiaFont() {
-  if (m_Face)
-    FXFT_Done_Face(m_Face);
-}
-
-void CFPF_SkiaFont::Release() {
-  if (--m_dwRefCount == 0)
-    delete this;
-}
-
-CFPF_SkiaFont* CFPF_SkiaFont::Retain() {
-  m_dwRefCount++;
-  return this;
-}
+CFPF_SkiaFont::~CFPF_SkiaFont() = default;
 
 ByteString CFPF_SkiaFont::GetFamilyName() {
   if (!m_Face)
     return ByteString();
-  return ByteString(FXFT_Get_Face_Family_Name(m_Face));
+  return ByteString(FXFT_Get_Face_Family_Name(GetFaceRec()));
 }
 
 ByteString CFPF_SkiaFont::GetPsName() {
   if (!m_Face)
     return ByteString();
-  return FXFT_Get_Postscript_Name(m_Face);
+  return FT_Get_Postscript_Name(GetFaceRec());
 }
 
 int32_t CFPF_SkiaFont::GetGlyphIndex(wchar_t wUnicode) {
   if (!m_Face)
     return wUnicode;
-  if (FXFT_Select_Charmap(m_Face, FXFT_ENCODING_UNICODE))
+  if (FXFT_Select_Charmap(GetFaceRec(), FT_ENCODING_UNICODE))
     return 0;
-  return FXFT_Get_Char_Index(m_Face, wUnicode);
+  return FT_Get_Char_Index(GetFaceRec(), wUnicode);
 }
 
 int32_t CFPF_SkiaFont::GetGlyphWidth(int32_t iGlyphIndex) {
   if (!m_Face)
     return 0;
-  if (FXFT_Load_Glyph(
-          m_Face, iGlyphIndex,
-          FXFT_LOAD_NO_SCALE | FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
+  if (FT_Load_Glyph(GetFaceRec(), iGlyphIndex,
+                    FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
     return 0;
   }
-  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                       FXFT_Get_Glyph_HoriAdvance(m_Face));
+  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                       FXFT_Get_Glyph_HoriAdvance(GetFaceRec()));
 }
 
 int32_t CFPF_SkiaFont::GetAscent() const {
   if (!m_Face)
     return 0;
-  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                       FXFT_Get_Face_Ascender(m_Face));
+  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                       FXFT_Get_Face_Ascender(GetFaceRec()));
 }
 
 int32_t CFPF_SkiaFont::GetDescent() const {
   if (!m_Face)
     return 0;
-  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                       FXFT_Get_Face_Descender(m_Face));
+  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                       FXFT_Get_Face_Descender(GetFaceRec()));
 }
 
 bool CFPF_SkiaFont::GetGlyphBBox(int32_t iGlyphIndex, FX_RECT& rtBBox) {
   if (!m_Face)
     return false;
-  if (FXFT_Is_Face_Tricky(m_Face)) {
-    if (FXFT_Set_Char_Size(m_Face, 0, 1000 * 64, 72, 72))
+  if (FXFT_Is_Face_Tricky(GetFaceRec())) {
+    if (FT_Set_Char_Size(GetFaceRec(), 0, 1000 * 64, 72, 72))
       return false;
-    if (FXFT_Load_Glyph(m_Face, iGlyphIndex,
-                        FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
-      FXFT_Set_Pixel_Sizes(m_Face, 0, 64);
+    if (FT_Load_Glyph(GetFaceRec(), iGlyphIndex,
+                      FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
+      FT_Set_Pixel_Sizes(GetFaceRec(), 0, 64);
       return false;
     }
-    FXFT_Glyph glyph;
-    if (FXFT_Get_Glyph(m_Face->glyph, &glyph)) {
-      FXFT_Set_Pixel_Sizes(m_Face, 0, 64);
+    FT_Glyph glyph;
+    if (FT_Get_Glyph(GetFaceRec()->glyph, &glyph)) {
+      FT_Set_Pixel_Sizes(GetFaceRec(), 0, 64);
       return false;
     }
-    FXFT_BBox cbox;
-    FXFT_Glyph_Get_CBox(glyph, FXFT_GLYPH_BBOX_PIXELS, &cbox);
-    int32_t x_ppem = m_Face->size->metrics.x_ppem;
-    int32_t y_ppem = m_Face->size->metrics.y_ppem;
+    FT_BBox cbox;
+    FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox);
+    int32_t x_ppem = GetFaceRec()->size->metrics.x_ppem;
+    int32_t y_ppem = GetFaceRec()->size->metrics.y_ppem;
     rtBBox.left = FPF_EM_ADJUST(x_ppem, cbox.xMin);
     rtBBox.right = FPF_EM_ADJUST(x_ppem, cbox.xMax);
     rtBBox.top = FPF_EM_ADJUST(y_ppem, cbox.yMax);
     rtBBox.bottom = FPF_EM_ADJUST(y_ppem, cbox.yMin);
     rtBBox.top = std::min(rtBBox.top, GetAscent());
     rtBBox.bottom = std::max(rtBBox.bottom, GetDescent());
-    FXFT_Done_Glyph(glyph);
-    return FXFT_Set_Pixel_Sizes(m_Face, 0, 64) == 0;
+    FT_Done_Glyph(glyph);
+    return FT_Set_Pixel_Sizes(GetFaceRec(), 0, 64) == 0;
   }
-  if (FXFT_Load_Glyph(
-          m_Face, iGlyphIndex,
-          FXFT_LOAD_NO_SCALE | FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
+  if (FT_Load_Glyph(GetFaceRec(), iGlyphIndex,
+                    FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
     return false;
   }
-  rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                              FXFT_Get_Glyph_HoriBearingX(m_Face));
-  rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                                FXFT_Get_Glyph_HoriBearingY(m_Face));
-  rtBBox.right = FPF_EM_ADJUST(
-      FXFT_Get_Face_UnitsPerEM(m_Face),
-      FXFT_Get_Glyph_HoriBearingX(m_Face) + FXFT_Get_Glyph_Width(m_Face));
-  rtBBox.top = FPF_EM_ADJUST(
-      FXFT_Get_Face_UnitsPerEM(m_Face),
-      FXFT_Get_Glyph_HoriBearingY(m_Face) - FXFT_Get_Glyph_Height(m_Face));
+  rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                              FXFT_Get_Glyph_HoriBearingX(GetFaceRec()));
+  rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                                FXFT_Get_Glyph_HoriBearingY(GetFaceRec()));
+  rtBBox.right = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                               FXFT_Get_Glyph_HoriBearingX(GetFaceRec()) +
+                                   FXFT_Get_Glyph_Width(GetFaceRec()));
+  rtBBox.top = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                             FXFT_Get_Glyph_HoriBearingY(GetFaceRec()) -
+                                 FXFT_Get_Glyph_Height(GetFaceRec()));
   return true;
 }
 
@@ -138,82 +123,40 @@
   if (!m_Face) {
     return false;
   }
-  rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                              FXFT_Get_Face_xMin(m_Face));
-  rtBBox.top = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                             FXFT_Get_Face_yMin(m_Face));
-  rtBBox.right = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                               FXFT_Get_Face_xMax(m_Face));
-  rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                                FXFT_Get_Face_yMax(m_Face));
+  rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                              FXFT_Get_Face_xMin(GetFaceRec()));
+  rtBBox.top = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                             FXFT_Get_Face_yMin(GetFaceRec()));
+  rtBBox.right = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                               FXFT_Get_Face_xMax(GetFaceRec()));
+  rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                                FXFT_Get_Face_yMax(GetFaceRec()));
   return true;
 }
 
 int32_t CFPF_SkiaFont::GetHeight() const {
   if (!m_Face)
     return 0;
-  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face),
-                       FXFT_Get_Face_Height(m_Face));
+  return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()),
+                       FXFT_Get_Face_Height(GetFaceRec()));
 }
 
 int32_t CFPF_SkiaFont::GetItalicAngle() const {
   if (!m_Face)
     return 0;
 
-  TT_Postscript* ttInfo =
-      (TT_Postscript*)FT_Get_Sfnt_Table(m_Face, ft_sfnt_post);
-  if (ttInfo)
-    return ttInfo->italicAngle;
-  return 0;
+  auto* info = static_cast<TT_Postscript*>(
+      FT_Get_Sfnt_Table(GetFaceRec(), ft_sfnt_post));
+  return info ? info->italicAngle : 0;
 }
 
 uint32_t CFPF_SkiaFont::GetFontData(uint32_t dwTable,
-                                    uint8_t* pBuffer,
-                                    uint32_t dwSize) {
+                                    pdfium::span<uint8_t> pBuffer) {
   if (!m_Face)
     return 0;
 
-  FT_ULong ulSize = pdfium::base::checked_cast<FT_ULong>(dwSize);
-  if (FXFT_Load_Sfnt_Table(m_Face, dwTable, 0, pBuffer, &ulSize))
+  FT_ULong ulSize = pdfium::base::checked_cast<FT_ULong>(pBuffer.size());
+  if (FT_Load_Sfnt_Table(GetFaceRec(), dwTable, 0, pBuffer.data(), &ulSize))
     return 0;
   return pdfium::base::checked_cast<uint32_t>(ulSize);
 }
-
-bool CFPF_SkiaFont::InitFont(CFPF_SkiaFontMgr* pFontMgr,
-                             CFPF_SkiaFontDescriptor* pFontDes,
-                             const ByteStringView& bsFamily,
-                             uint32_t dwStyle,
-                             uint8_t uCharset) {
-  if (!pFontMgr || !pFontDes)
-    return false;
-
-  switch (pFontDes->GetType()) {
-    case FPF_SKIAFONTTYPE_Path: {
-      CFPF_SkiaPathFont* pFont = (CFPF_SkiaPathFont*)pFontDes;
-      m_Face = pFontMgr->GetFontFace(pFont->m_pPath, pFont->m_iFaceIndex);
-      break;
-    }
-    case FPF_SKIAFONTTYPE_File: {
-      CFPF_SkiaFileFont* pFont = (CFPF_SkiaFileFont*)pFontDes;
-      m_Face = pFontMgr->GetFontFace(pFont->m_pFile, pFont->m_iFaceIndex);
-      break;
-    }
-    case FPF_SKIAFONTTYPE_Buffer: {
-      CFPF_SkiaBufferFont* pFont = (CFPF_SkiaBufferFont*)pFontDes;
-      m_Face = pFontMgr->GetFontFace((const uint8_t*)pFont->m_pBuffer,
-                                     pFont->m_szBuffer, pFont->m_iFaceIndex);
-      break;
-    }
-    default:
-      return false;
-  }
-  if (!m_Face)
-    return false;
-
-  m_dwStyle = dwStyle;
-  m_uCharset = uCharset;
-  m_pFontMgr = pFontMgr;
-  m_pFontDes = pFontDes;
-  m_dwRefCount = 1;
-  return true;
-}
diff --git a/core/fxge/android/cfpf_skiafont.h b/core/fxge/android/cfpf_skiafont.h
index 1b2f605..702a38e 100644
--- a/core/fxge/android/cfpf_skiafont.h
+++ b/core/fxge/android/cfpf_skiafont.h
@@ -7,20 +7,26 @@
 #ifndef CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_
 #define CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_
 
+#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_font.h"
+#include "core/fxge/cfx_face.h"
+#include "core/fxge/fx_freetype.h"
+#include "third_party/base/span.h"
 
-class CFPF_SkiaFontDescriptor;
 class CFPF_SkiaFontMgr;
+class CFPF_SkiaPathFont;
+struct FX_RECT;
 
 class CFPF_SkiaFont {
  public:
-  CFPF_SkiaFont();
+  CFPF_SkiaFont(CFPF_SkiaFontMgr* pFontMgr,
+                const CFPF_SkiaPathFont* pFont,
+                uint32_t dwStyle,
+                uint8_t uCharset);
   ~CFPF_SkiaFont();
 
-  void Release();
-  CFPF_SkiaFont* Retain();
+  bool IsValid() const { return !!m_Face; }
 
   ByteString GetFamilyName();
   ByteString GetPsName();
@@ -34,21 +40,15 @@
   bool GetBBox(FX_RECT& rtBBox);
   int32_t GetHeight() const;
   int32_t GetItalicAngle() const;
-  uint32_t GetFontData(uint32_t dwTable, uint8_t* pBuffer, uint32_t dwSize);
-
-  bool InitFont(CFPF_SkiaFontMgr* pFontMgr,
-                CFPF_SkiaFontDescriptor* pFontDes,
-                const ByteStringView& bsFamily,
-                uint32_t dwStyle,
-                uint8_t uCharset);
+  uint32_t GetFontData(uint32_t dwTable, pdfium::span<uint8_t> pBuffer);
+  FXFT_FaceRec* GetFaceRec() const { return m_Face->GetRec(); }
 
  private:
-  UnownedPtr<CFPF_SkiaFontMgr> m_pFontMgr;
-  UnownedPtr<CFPF_SkiaFontDescriptor> m_pFontDes;
-  FXFT_Face m_Face;
-  uint32_t m_dwStyle;
-  uint8_t m_uCharset;
-  uint32_t m_dwRefCount;
+  UnownedPtr<CFPF_SkiaFontMgr> const m_pFontMgr;
+  UnownedPtr<const CFPF_SkiaPathFont> const m_pFont;
+  RetainPtr<CFX_Face> const m_Face;
+  const uint32_t m_dwStyle;
+  const uint8_t m_uCharset;
 };
 
 #endif  // CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_
diff --git a/core/fxge/android/cfpf_skiafontdescriptor.cpp b/core/fxge/android/cfpf_skiafontdescriptor.cpp
deleted file mode 100644
index 8dd4481..0000000
--- a/core/fxge/android/cfpf_skiafontdescriptor.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/android/cfpf_skiafontdescriptor.h"
-
-#include "core/fxcrt/fx_memory.h"
-
-CFPF_SkiaFontDescriptor::CFPF_SkiaFontDescriptor()
-    : m_pFamily(nullptr),
-      m_dwStyle(0),
-      m_iFaceIndex(0),
-      m_dwCharsets(0),
-      m_iGlyphNum(0) {}
-
-CFPF_SkiaFontDescriptor::~CFPF_SkiaFontDescriptor() {
-  FX_Free(m_pFamily);
-}
-
-int32_t CFPF_SkiaFontDescriptor::GetType() const {
-  return FPF_SKIAFONTTYPE_Unknown;
-}
-
-void CFPF_SkiaFontDescriptor::SetFamily(const char* pFamily) {
-  FX_Free(m_pFamily);
-  int32_t iSize = strlen(pFamily);
-  m_pFamily = FX_Alloc(char, iSize + 1);
-  memcpy(m_pFamily, pFamily, iSize * sizeof(char));
-  m_pFamily[iSize] = 0;
-}
diff --git a/core/fxge/android/cfpf_skiafontdescriptor.h b/core/fxge/android/cfpf_skiafontdescriptor.h
deleted file mode 100644
index 00a19ec..0000000
--- a/core/fxge/android/cfpf_skiafontdescriptor.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_ANDROID_CFPF_SKIAFONTDESCRIPTOR_H_
-#define CORE_FXGE_ANDROID_CFPF_SKIAFONTDESCRIPTOR_H_
-
-#include "core/fxcrt/fx_system.h"
-
-#define FPF_SKIAFONTTYPE_Unknown 0
-
-class CFPF_SkiaFontDescriptor {
- public:
-  CFPF_SkiaFontDescriptor();
-  virtual ~CFPF_SkiaFontDescriptor();
-
-  virtual int32_t GetType() const;
-
-  void SetFamily(const char* pFamily);
-
-  char* m_pFamily;
-  uint32_t m_dwStyle;
-  int32_t m_iFaceIndex;
-  uint32_t m_dwCharsets;
-  int32_t m_iGlyphNum;
-};
-
-#endif  // CORE_FXGE_ANDROID_CFPF_SKIAFONTDESCRIPTOR_H_
diff --git a/core/fxge/android/cfpf_skiafontmgr.cpp b/core/fxge/android/cfpf_skiafontmgr.cpp
index d8b751d..b5e7a20 100644
--- a/core/fxge/android/cfpf_skiafontmgr.cpp
+++ b/core/fxge/android/cfpf_skiafontmgr.cpp
@@ -6,51 +6,27 @@
 
 #include "core/fxge/android/cfpf_skiafontmgr.h"
 
-#define FPF_SKIAMATCHWEIGHT_NAME1 62
-#define FPF_SKIAMATCHWEIGHT_NAME2 60
-#define FPF_SKIAMATCHWEIGHT_1 16
-#define FPF_SKIAMATCHWEIGHT_2 8
-
 #include <algorithm>
+#include <utility>
 
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxge/android/cfpf_skiafont.h"
-#include "core/fxge/android/cfpf_skiafontdescriptor.h"
 #include "core/fxge/android/cfpf_skiapathfont.h"
+#include "core/fxge/fx_font.h"
 #include "core/fxge/fx_freetype.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-static unsigned long FPF_SkiaStream_Read(FXFT_Stream stream,
-                                         unsigned long offset,
-                                         unsigned char* buffer,
-                                         unsigned long count) {
-  if (count == 0)
-    return 0;
-
-  IFX_SeekableReadStream* pFileRead =
-      static_cast<IFX_SeekableReadStream*>(stream->descriptor.pointer);
-  if (!pFileRead)
-    return 0;
-
-  if (!pFileRead->ReadBlock(buffer, (FX_FILESIZE)offset,
-                            static_cast<size_t>(count)))
-    return 0;
-
-  return count;
-}
-
-static void FPF_SkiaStream_Close(FXFT_Stream stream) {}
-#ifdef __cplusplus
-};
-#endif
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
+constexpr int FPF_SKIAMATCHWEIGHT_NAME1 = 62;
+constexpr int FPF_SKIAMATCHWEIGHT_NAME2 = 60;
+constexpr int FPF_SKIAMATCHWEIGHT_1 = 16;
+constexpr int FPF_SKIAMATCHWEIGHT_2 = 8;
+
 struct FPF_SKIAFONTMAP {
   uint32_t dwFamily;
   uint32_t dwSubSt;
@@ -97,7 +73,7 @@
   const char* pStrEnd = pStr + iLength;
   uint32_t uHashCode = 0;
   while (pStr < pStrEnd)
-    uHashCode = 31 * uHashCode + FXSYS_tolower(*pStr++);
+    uHashCode = 31 * uHashCode + tolower(*pStr++);
   return uHashCode;
 }
 
@@ -159,7 +135,7 @@
   return FPF_SKIACHARSET_Default;
 }
 
-uint32_t FPF_SKIANormalizeFontName(const ByteStringView& bsfamily) {
+uint32_t FPF_SKIANormalizeFontName(ByteStringView bsfamily) {
   uint32_t dwHash = 0;
   int32_t iLength = bsfamily.GetLength();
   const char* pBuffer = bsfamily.unterminated_c_str();
@@ -167,16 +143,16 @@
     char ch = pBuffer[i];
     if (ch == ' ' || ch == '-' || ch == ',')
       continue;
-    dwHash = 31 * dwHash + FXSYS_tolower(ch);
+    dwHash = 31 * dwHash + tolower(ch);
   }
   return dwHash;
 }
 
-uint32_t FPF_SKIAGetFamilyHash(const ByteStringView& bsFamily,
+uint32_t FPF_SKIAGetFamilyHash(ByteStringView bsFamily,
                                uint32_t dwStyle,
                                uint8_t uCharset) {
   ByteString bsFont(bsFamily);
-  if (FontStyleIsBold(dwStyle))
+  if (FontStyleIsForceBold(dwStyle))
     bsFont += "Bold";
   if (FontStyleIsItalic(dwStyle))
     bsFont += "Italic";
@@ -187,18 +163,16 @@
 }
 
 bool FPF_SkiaIsCJK(uint8_t uCharset) {
-  return (uCharset == FX_CHARSET_ChineseSimplified) ||
-         (uCharset == FX_CHARSET_ChineseTraditional) ||
-         (uCharset == FX_CHARSET_Hangul) || (uCharset == FX_CHARSET_ShiftJIS);
+  return FX_CharSetIsCJK(uCharset);
 }
 
-bool FPF_SkiaMaybeSymbol(const ByteStringView& bsFacename) {
+bool FPF_SkiaMaybeSymbol(ByteStringView bsFacename) {
   ByteString bsName(bsFacename);
   bsName.MakeLower();
   return bsName.Contains("symbol");
 }
 
-bool FPF_SkiaMaybeArabic(const ByteStringView& bsFacename) {
+bool FPF_SkiaMaybeArabic(ByteStringView bsFacename) {
   ByteString bsName(bsFacename);
   bsName.MakeLower();
   return bsName.Contains("arabic");
@@ -253,25 +227,24 @@
 
 }  // namespace
 
-CFPF_SkiaFontMgr::CFPF_SkiaFontMgr() : m_bLoaded(false), m_FTLibrary(nullptr) {}
+CFPF_SkiaFontMgr::CFPF_SkiaFontMgr() = default;
 
 CFPF_SkiaFontMgr::~CFPF_SkiaFontMgr() {
-  for (const auto& pair : m_FamilyFonts) {
-    if (pair.second)
-      pair.second->Release();
-  }
   m_FamilyFonts.clear();
-  for (auto it = m_FontFaces.begin(); it != m_FontFaces.end(); ++it)
-    delete *it;
   m_FontFaces.clear();
-  if (m_FTLibrary)
-    FXFT_Done_FreeType(m_FTLibrary);
 }
 
 bool CFPF_SkiaFontMgr::InitFTLibrary() {
-  if (!m_FTLibrary)
-    FXFT_Init_FreeType(&m_FTLibrary);
-  return !!m_FTLibrary;
+  if (m_FTLibrary)
+    return true;
+
+  FXFT_LibraryRec* pLibrary = nullptr;
+  FT_Init_FreeType(&pLibrary);
+  if (!pLibrary)
+    return false;
+
+  m_FTLibrary.reset(pLibrary);
+  return true;
 }
 
 void CFPF_SkiaFontMgr::LoadSystemFonts() {
@@ -281,14 +254,13 @@
   m_bLoaded = true;
 }
 
-CFPF_SkiaFont* CFPF_SkiaFontMgr::CreateFont(const ByteStringView& bsFamilyname,
+CFPF_SkiaFont* CFPF_SkiaFontMgr::CreateFont(ByteStringView bsFamilyname,
                                             uint8_t uCharset,
-                                            uint32_t dwStyle,
-                                            uint32_t dwMatch) {
+                                            uint32_t dwStyle) {
   uint32_t dwHash = FPF_SKIAGetFamilyHash(bsFamilyname, dwStyle, uCharset);
-  auto it = m_FamilyFonts.find(dwHash);
-  if (it != m_FamilyFonts.end() && it->second)
-    return it->second->Retain();
+  auto family_iter = m_FamilyFonts.find(dwHash);
+  if (family_iter != m_FamilyFonts.end())
+    return family_iter->second.get();
 
   uint32_t dwFaceName = FPF_SKIANormalizeFontName(bsFamilyname);
   uint32_t dwSubst = FPF_SkiaGetSubstFont(dwFaceName, g_SkiaFontmap,
@@ -299,35 +271,35 @@
   if (uCharset != FX_CHARSET_MSWin_Arabic &&
       FPF_SkiaMaybeArabic(bsFamilyname)) {
     uCharset = FX_CHARSET_MSWin_Arabic;
-  } else if (uCharset == FX_CHARSET_ANSI &&
-             (dwMatch & FPF_MATCHFONT_REPLACEANSI)) {
+  } else if (uCharset == FX_CHARSET_ANSI) {
     uCharset = FX_CHARSET_Default;
   }
   int32_t nExpectVal = FPF_SKIAMATCHWEIGHT_NAME1 + FPF_SKIAMATCHWEIGHT_1 * 3 +
                        FPF_SKIAMATCHWEIGHT_2 * 2;
-  CFPF_SkiaFontDescriptor* pBestFontDes = nullptr;
+  const CFPF_SkiaPathFont* pBestFont = nullptr;
   int32_t nMax = -1;
   int32_t nGlyphNum = 0;
-  for (auto it = m_FontFaces.rbegin(); it != m_FontFaces.rend(); ++it) {
-    CFPF_SkiaPathFont* pFontDes = static_cast<CFPF_SkiaPathFont*>(*it);
-    if (!(pFontDes->m_dwCharsets & FPF_SkiaGetCharset(uCharset)))
+  for (auto face_iter = m_FontFaces.rbegin(); face_iter != m_FontFaces.rend();
+       ++face_iter) {
+    const CFPF_SkiaPathFont* pFont = face_iter->get();
+    if (!(pFont->charsets() & FPF_SkiaGetCharset(uCharset)))
       continue;
     int32_t nFind = 0;
-    uint32_t dwSysFontName = FPF_SKIANormalizeFontName(pFontDes->m_pFamily);
+    uint32_t dwSysFontName = FPF_SKIANormalizeFontName(pFont->family());
     if (dwFaceName == dwSysFontName)
       nFind += FPF_SKIAMATCHWEIGHT_NAME1;
     bool bMatchedName = (nFind == FPF_SKIAMATCHWEIGHT_NAME1);
-    if (FontStyleIsBold(dwStyle) == FontStyleIsBold(pFontDes->m_dwStyle))
+    if (FontStyleIsForceBold(dwStyle) == FontStyleIsForceBold(pFont->style()))
       nFind += FPF_SKIAMATCHWEIGHT_1;
-    if (FontStyleIsItalic(dwStyle) == FontStyleIsItalic(pFontDes->m_dwStyle))
+    if (FontStyleIsItalic(dwStyle) == FontStyleIsItalic(pFont->style()))
       nFind += FPF_SKIAMATCHWEIGHT_1;
     if (FontStyleIsFixedPitch(dwStyle) ==
-        FontStyleIsFixedPitch(pFontDes->m_dwStyle)) {
+        FontStyleIsFixedPitch(pFont->style())) {
       nFind += FPF_SKIAMATCHWEIGHT_2;
     }
-    if (FontStyleIsSerif(dwStyle) == FontStyleIsSerif(pFontDes->m_dwStyle))
+    if (FontStyleIsSerif(dwStyle) == FontStyleIsSerif(pFont->style()))
       nFind += FPF_SKIAMATCHWEIGHT_1;
-    if (FontStyleIsScript(dwStyle) == FontStyleIsScript(pFontDes->m_dwStyle))
+    if (FontStyleIsScript(dwStyle) == FontStyleIsScript(pFont->style()))
       nFind += FPF_SKIAMATCHWEIGHT_2;
     if (dwSubst == dwSysFontName || dwSubstSans == dwSysFontName) {
       nFind += FPF_SKIAMATCHWEIGHT_NAME2;
@@ -336,104 +308,69 @@
     if (uCharset == FX_CHARSET_Default || bMaybeSymbol) {
       if (nFind > nMax && bMatchedName) {
         nMax = nFind;
-        pBestFontDes = *it;
+        pBestFont = face_iter->get();
       }
     } else if (FPF_SkiaIsCJK(uCharset)) {
-      if (bMatchedName || pFontDes->m_iGlyphNum > nGlyphNum) {
-        pBestFontDes = *it;
-        nGlyphNum = pFontDes->m_iGlyphNum;
+      if (bMatchedName || pFont->glyph_num() > nGlyphNum) {
+        pBestFont = face_iter->get();
+        nGlyphNum = pFont->glyph_num();
       }
     } else if (nFind > nMax) {
       nMax = nFind;
-      pBestFontDes = *it;
+      pBestFont = face_iter->get();
     }
     if (nExpectVal <= nFind) {
-      pBestFontDes = *it;
+      pBestFont = face_iter->get();
       break;
     }
   }
-  if (pBestFontDes) {
-    CFPF_SkiaFont* pFont = new CFPF_SkiaFont;
-    if (pFont->InitFont(this, pBestFontDes, bsFamilyname, dwStyle, uCharset)) {
-      m_FamilyFonts[dwHash] = pFont;
-      return pFont->Retain();
-    }
-    pFont->Release();
-  }
-  return nullptr;
+  if (!pBestFont)
+    return nullptr;
+
+  auto pFont =
+      pdfium::MakeUnique<CFPF_SkiaFont>(this, pBestFont, dwStyle, uCharset);
+  if (!pFont->IsValid())
+    return nullptr;
+
+  CFPF_SkiaFont* pRet = pFont.get();
+  m_FamilyFonts[dwHash] = std::move(pFont);
+  return pRet;
 }
 
-FXFT_Face CFPF_SkiaFontMgr::GetFontFace(
-    const RetainPtr<IFX_SeekableReadStream>& pFileRead,
-    int32_t iFaceIndex) {
-  if (!pFileRead)
-    return nullptr;
-  if (pFileRead->GetSize() == 0)
-    return nullptr;
-  if (iFaceIndex < 0)
-    return nullptr;
-  FXFT_StreamRec streamRec;
-  memset(&streamRec, 0, sizeof(FXFT_StreamRec));
-  streamRec.size = pFileRead->GetSize();
-  streamRec.descriptor.pointer = static_cast<void*>(pFileRead.Get());
-  streamRec.read = FPF_SkiaStream_Read;
-  streamRec.close = FPF_SkiaStream_Close;
-  FXFT_Open_Args args;
-  args.flags = FT_OPEN_STREAM;
-  args.stream = &streamRec;
-  FXFT_Face face;
-  if (FXFT_Open_Face(m_FTLibrary, &args, iFaceIndex, &face))
-    return nullptr;
-  FXFT_Set_Pixel_Sizes(face, 0, 64);
-  return face;
-}
-
-FXFT_Face CFPF_SkiaFontMgr::GetFontFace(const ByteStringView& bsFile,
-                                        int32_t iFaceIndex) {
+RetainPtr<CFX_Face> CFPF_SkiaFontMgr::GetFontFace(ByteStringView bsFile,
+                                                  int32_t iFaceIndex) {
   if (bsFile.IsEmpty())
     return nullptr;
+
   if (iFaceIndex < 0)
     return nullptr;
-  FXFT_Open_Args args;
+
+  FT_Open_Args args;
   args.flags = FT_OPEN_PATHNAME;
   args.pathname = const_cast<FT_String*>(bsFile.unterminated_c_str());
-  FXFT_Face face;
-  if (FXFT_Open_Face(m_FTLibrary, &args, iFaceIndex, &face))
+  RetainPtr<CFX_Face> face =
+      CFX_Face::Open(m_FTLibrary.get(), &args, iFaceIndex);
+  if (!face)
     return nullptr;
-  FXFT_Set_Pixel_Sizes(face, 0, 64);
-  return face;
-}
 
-FXFT_Face CFPF_SkiaFontMgr::GetFontFace(const uint8_t* pBuffer,
-                                        size_t szBuffer,
-                                        int32_t iFaceIndex) {
-  if (!pBuffer || szBuffer < 1)
-    return nullptr;
-  if (iFaceIndex < 0)
-    return nullptr;
-  FXFT_Open_Args args;
-  args.flags = FT_OPEN_MEMORY;
-  args.memory_base = pBuffer;
-  args.memory_size = szBuffer;
-  FXFT_Face face;
-  if (FXFT_Open_Face(m_FTLibrary, &args, iFaceIndex, &face))
-    return nullptr;
-  FXFT_Set_Pixel_Sizes(face, 0, 64);
+  FT_Set_Pixel_Sizes(face->GetRec(), 0, 64);
   return face;
 }
 
 void CFPF_SkiaFontMgr::ScanPath(const ByteString& path) {
-  DIR* handle = FX_OpenFolder(path.c_str());
+  std::unique_ptr<FX_FolderHandle, FxFolderHandleCloser> handle(
+      FX_OpenFolder(path.c_str()));
   if (!handle)
     return;
+
   ByteString filename;
   bool bFolder = false;
-  while (FX_GetNextFile(handle, &filename, &bFolder)) {
+  while (FX_GetNextFile(handle.get(), &filename, &bFolder)) {
     if (bFolder) {
       if (filename == "." || filename == "..")
         continue;
     } else {
-      ByteString ext = filename.Right(4);
+      ByteString ext = filename.Last(4);
       ext.MakeLower();
       if (ext != ".ttf" && ext != ".ttc" && ext != ".otf")
         continue;
@@ -446,44 +383,42 @@
     else
       ScanFile(fullpath);
   }
-  FX_CloseFolder(handle);
 }
 
 void CFPF_SkiaFontMgr::ScanFile(const ByteString& file) {
-  FXFT_Face face = GetFontFace(file.AsStringView());
+  RetainPtr<CFX_Face> face = GetFontFace(file.AsStringView(), 0);
   if (!face)
     return;
-  CFPF_SkiaPathFont* pFontDesc = new CFPF_SkiaPathFont;
-  pFontDesc->SetPath(file.c_str());
-  ReportFace(face, pFontDesc);
-  m_FontFaces.push_back(pFontDesc);
-  FXFT_Done_Face(face);
+
+  m_FontFaces.push_back(ReportFace(face, file));
 }
 
-void CFPF_SkiaFontMgr::ReportFace(FXFT_Face face,
-                                  CFPF_SkiaFontDescriptor* pFontDesc) {
-  if (!face || !pFontDesc)
-    return;
-  pFontDesc->SetFamily(FXFT_Get_Face_Family_Name(face));
-  if (FXFT_Is_Face_Bold(face))
-    pFontDesc->m_dwStyle |= FXFONT_BOLD;
-  if (FXFT_Is_Face_Italic(face))
-    pFontDesc->m_dwStyle |= FXFONT_ITALIC;
-  if (FT_IS_FIXED_WIDTH(face))
-    pFontDesc->m_dwStyle |= FXFONT_FIXED_PITCH;
-  TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+std::unique_ptr<CFPF_SkiaPathFont> CFPF_SkiaFontMgr::ReportFace(
+    RetainPtr<CFX_Face> face,
+    const ByteString& file) {
+  uint32_t dwStyle = 0;
+  if (FXFT_Is_Face_Bold(face->GetRec()))
+    dwStyle |= FXFONT_FORCE_BOLD;
+  if (FXFT_Is_Face_Italic(face->GetRec()))
+    dwStyle |= FXFONT_ITALIC;
+  if (FT_IS_FIXED_WIDTH(face->GetRec()))
+    dwStyle |= FXFONT_FIXED_PITCH;
+  TT_OS2* pOS2 =
+      static_cast<TT_OS2*>(FT_Get_Sfnt_Table(face->GetRec(), ft_sfnt_os2));
   if (pOS2) {
     if (pOS2->ulCodePageRange1 & (1 << 31))
-      pFontDesc->m_dwStyle |= FXFONT_SYMBOLIC;
+      dwStyle |= FXFONT_SYMBOLIC;
     if (pOS2->panose[0] == 2) {
       uint8_t uSerif = pOS2->panose[1];
       if ((uSerif > 1 && uSerif < 10) || uSerif > 13)
-        pFontDesc->m_dwStyle |= FXFONT_SERIF;
+        dwStyle |= FXFONT_SERIF;
     }
   }
   if (pOS2 && (pOS2->ulCodePageRange1 & (1 << 31)))
-    pFontDesc->m_dwStyle |= FXFONT_SYMBOLIC;
-  pFontDesc->m_dwCharsets = FPF_SkiaGetFaceCharset(pOS2);
-  pFontDesc->m_iFaceIndex = face->face_index;
-  pFontDesc->m_iGlyphNum = face->num_glyphs;
+    dwStyle |= FXFONT_SYMBOLIC;
+
+  return pdfium::MakeUnique<CFPF_SkiaPathFont>(
+      file, FXFT_Get_Face_Family_Name(face->GetRec()), dwStyle,
+      face->GetRec()->face_index, FPF_SkiaGetFaceCharset(pOS2),
+      face->GetRec()->num_glyphs);
 }
diff --git a/core/fxge/android/cfpf_skiafontmgr.h b/core/fxge/android/cfpf_skiafontmgr.h
index 11ad47a..4d5c788 100644
--- a/core/fxge/android/cfpf_skiafontmgr.h
+++ b/core/fxge/android/cfpf_skiafontmgr.h
@@ -8,16 +8,16 @@
 #define CORE_FXGE_ANDROID_CFPF_SKIAFONTMGR_H_
 
 #include <map>
+#include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxge/fx_font.h"
-
-#define FPF_MATCHFONT_REPLACEANSI 1
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_face.h"
+#include "core/fxge/fx_freetype.h"
 
 class CFPF_SkiaFont;
-class CFPF_SkiaFontDescriptor;
+class CFPF_SkiaPathFont;
 
 class CFPF_SkiaFontMgr {
  public:
@@ -25,28 +25,23 @@
   ~CFPF_SkiaFontMgr();
 
   void LoadSystemFonts();
-  CFPF_SkiaFont* CreateFont(const ByteStringView& bsFamilyname,
+  CFPF_SkiaFont* CreateFont(ByteStringView bsFamilyname,
                             uint8_t uCharset,
-                            uint32_t dwStyle,
-                            uint32_t dwMatch = 0);
+                            uint32_t dwStyle);
 
   bool InitFTLibrary();
-  FXFT_Face GetFontFace(const RetainPtr<IFX_SeekableReadStream>& pFileRead,
-                        int32_t iFaceIndex = 0);
-  FXFT_Face GetFontFace(const ByteStringView& bsFile, int32_t iFaceIndex = 0);
-  FXFT_Face GetFontFace(const uint8_t* pBuffer,
-                        size_t szBuffer,
-                        int32_t iFaceIndex = 0);
+  RetainPtr<CFX_Face> GetFontFace(ByteStringView bsFile, int32_t iFaceIndex);
 
  private:
   void ScanPath(const ByteString& path);
   void ScanFile(const ByteString& file);
-  void ReportFace(FXFT_Face face, CFPF_SkiaFontDescriptor* pFontDesc);
+  std::unique_ptr<CFPF_SkiaPathFont> ReportFace(RetainPtr<CFX_Face> face,
+                                                const ByteString& file);
 
-  bool m_bLoaded;
-  FXFT_Library m_FTLibrary;
-  std::vector<CFPF_SkiaFontDescriptor*> m_FontFaces;
-  std::map<uint32_t, CFPF_SkiaFont*> m_FamilyFonts;
+  bool m_bLoaded = false;
+  ScopedFXFTLibraryRec m_FTLibrary;
+  std::vector<std::unique_ptr<CFPF_SkiaPathFont>> m_FontFaces;
+  std::map<uint32_t, std::unique_ptr<CFPF_SkiaFont>> m_FamilyFonts;
 };
 
 #endif  // CORE_FXGE_ANDROID_CFPF_SKIAFONTMGR_H_
diff --git a/core/fxge/android/cfpf_skiapathfont.cpp b/core/fxge/android/cfpf_skiapathfont.cpp
index 27f4fde..a9f96ac 100644
--- a/core/fxge/android/cfpf_skiapathfont.cpp
+++ b/core/fxge/android/cfpf_skiapathfont.cpp
@@ -6,22 +6,17 @@
 
 #include "core/fxge/android/cfpf_skiapathfont.h"
 
-#include "core/fxcrt/fx_memory.h"
+CFPF_SkiaPathFont::CFPF_SkiaPathFont(const ByteString& path,
+                                     const char* pFamily,
+                                     uint32_t dwStyle,
+                                     int32_t iFaceIndex,
+                                     uint32_t dwCharsets,
+                                     int32_t iGlyphNum)
+    : m_bsPath(path),
+      m_bsFamily(pFamily),
+      m_dwStyle(dwStyle),
+      m_iFaceIndex(iFaceIndex),
+      m_dwCharsets(dwCharsets),
+      m_iGlyphNum(iGlyphNum) {}
 
-CFPF_SkiaPathFont::CFPF_SkiaPathFont() : m_pPath(nullptr) {}
-
-CFPF_SkiaPathFont::~CFPF_SkiaPathFont() {
-  FX_Free(m_pPath);
-}
-
-int32_t CFPF_SkiaPathFont::GetType() const {
-  return FPF_SKIAFONTTYPE_Path;
-}
-
-void CFPF_SkiaPathFont::SetPath(const char* pPath) {
-  FX_Free(m_pPath);
-  int32_t iSize = strlen(pPath);
-  m_pPath = FX_Alloc(char, iSize + 1);
-  memcpy(m_pPath, pPath, iSize * sizeof(char));
-  m_pPath[iSize] = 0;
-}
+CFPF_SkiaPathFont::~CFPF_SkiaPathFont() = default;
diff --git a/core/fxge/android/cfpf_skiapathfont.h b/core/fxge/android/cfpf_skiapathfont.h
index 6cba42a..d091dc9 100644
--- a/core/fxge/android/cfpf_skiapathfont.h
+++ b/core/fxge/android/cfpf_skiapathfont.h
@@ -7,22 +7,33 @@
 #ifndef CORE_FXGE_ANDROID_CFPF_SKIAPATHFONT_H_
 #define CORE_FXGE_ANDROID_CFPF_SKIAPATHFONT_H_
 
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/android/cfpf_skiafontdescriptor.h"
 
-#define FPF_SKIAFONTTYPE_Path 1
-
-class CFPF_SkiaPathFont : public CFPF_SkiaFontDescriptor {
+class CFPF_SkiaPathFont {
  public:
-  CFPF_SkiaPathFont();
-  ~CFPF_SkiaPathFont() override;
+  CFPF_SkiaPathFont(const ByteString& path,
+                    const char* pFamily,
+                    uint32_t dwStyle,
+                    int32_t iFaceIndex,
+                    uint32_t dwCharsets,
+                    int32_t iGlyphNum);
+  ~CFPF_SkiaPathFont();
 
-  // CFPF_SkiaFontDescriptor
-  int32_t GetType() const override;
+  const char* path() const { return m_bsPath.c_str(); }
+  const char* family() const { return m_bsFamily.c_str(); }
+  uint32_t style() const { return m_dwStyle; }
+  int32_t face_index() const { return m_iFaceIndex; }
+  uint32_t charsets() const { return m_dwCharsets; }
+  int32_t glyph_num() const { return m_iGlyphNum; }
 
-  void SetPath(const char* pPath);
-
-  char* m_pPath;
+ private:
+  const ByteString m_bsPath;
+  const ByteString m_bsFamily;
+  const uint32_t m_dwStyle;
+  const int32_t m_iFaceIndex;
+  const uint32_t m_dwCharsets;
+  const int32_t m_iGlyphNum;
 };
 
 #endif  // CORE_FXGE_ANDROID_CFPF_SKIAPATHFONT_H_
diff --git a/core/fxge/android/cfx_androidfontinfo.cpp b/core/fxge/android/cfx_androidfontinfo.cpp
index 61d213e..206003d 100644
--- a/core/fxge/android/cfx_androidfontinfo.cpp
+++ b/core/fxge/android/cfx_androidfontinfo.cpp
@@ -10,6 +10,7 @@
 #include "core/fxge/android/cfpf_skiafont.h"
 #include "core/fxge/android/cfpf_skiafontmgr.h"
 #include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/fx_font.h"
 
 CFX_AndroidFontInfo::CFX_AndroidFontInfo() : m_pFontMgr(nullptr) {}
 CFX_AndroidFontInfo::~CFX_AndroidFontInfo() {}
@@ -36,7 +37,7 @@
 
   uint32_t dwStyle = 0;
   if (weight >= 700)
-    dwStyle |= FXFONT_BOLD;
+    dwStyle |= FXFONT_FORCE_BOLD;
   if (bItalic)
     dwStyle |= FXFONT_ITALIC;
   if (FontFamilyIsFixedPitch(pitch_family))
@@ -45,8 +46,7 @@
     dwStyle |= FXFONT_SCRIPT;
   if (FontFamilyIsRoman(pitch_family))
     dwStyle |= FXFONT_SERIF;
-  return m_pFontMgr->CreateFont(face, charset, dwStyle,
-                                FPF_MATCHFONT_REPLACEANSI);
+  return m_pFontMgr->CreateFont(face, charset, dwStyle);
 }
 
 void* CFX_AndroidFontInfo::GetFont(const char* face) {
@@ -55,11 +55,10 @@
 
 uint32_t CFX_AndroidFontInfo::GetFontData(void* hFont,
                                           uint32_t table,
-                                          uint8_t* buffer,
-                                          uint32_t size) {
+                                          pdfium::span<uint8_t> buffer) {
   if (!hFont)
     return 0;
-  return static_cast<CFPF_SkiaFont*>(hFont)->GetFontData(table, buffer, size);
+  return static_cast<CFPF_SkiaFont*>(hFont)->GetFontData(table, buffer);
 }
 
 bool CFX_AndroidFontInfo::GetFaceName(void* hFont, ByteString* name) {
@@ -78,9 +77,4 @@
   return false;
 }
 
-void CFX_AndroidFontInfo::DeleteFont(void* hFont) {
-  if (!hFont)
-    return;
-
-  static_cast<CFPF_SkiaFont*>(hFont)->Release();
-}
+void CFX_AndroidFontInfo::DeleteFont(void* hFont) {}
diff --git a/core/fxge/android/cfx_androidfontinfo.h b/core/fxge/android/cfx_androidfontinfo.h
index e49b161..c8b6d24 100644
--- a/core/fxge/android/cfx_androidfontinfo.h
+++ b/core/fxge/android/cfx_androidfontinfo.h
@@ -10,18 +10,19 @@
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "third_party/base/span.h"
 
 class CFPF_SkiaFontMgr;
 
-class CFX_AndroidFontInfo : public IFX_SystemFontInfo {
+class CFX_AndroidFontInfo final : public SystemFontInfoIface {
  public:
   CFX_AndroidFontInfo();
   ~CFX_AndroidFontInfo() override;
 
   bool Init(CFPF_SkiaFontMgr* pFontMgr);
 
-  // IFX_SystemFontInfo:
+  // SystemFontInfoIface:
   bool EnumFontList(CFX_FontMapper* pMapper) override;
   void* MapFont(int weight,
                 bool bItalic,
@@ -31,13 +32,12 @@
   void* GetFont(const char* face) override;
   uint32_t GetFontData(void* hFont,
                        uint32_t table,
-                       uint8_t* buffer,
-                       uint32_t size) override;
+                       pdfium::span<uint8_t> buffer) override;
   bool GetFaceName(void* hFont, ByteString* name) override;
   bool GetFontCharset(void* hFont, int* charset) override;
   void DeleteFont(void* hFont) override;
 
- protected:
+ private:
   UnownedPtr<CFPF_SkiaFontMgr> m_pFontMgr;
 };
 
diff --git a/core/fxge/android/fx_android_imp.cpp b/core/fxge/android/fx_android_imp.cpp
index db2f3ef..147011c 100644
--- a/core/fxge/android/fx_android_imp.cpp
+++ b/core/fxge/android/fx_android_imp.cpp
@@ -14,21 +14,31 @@
 #include "core/fxge/cfx_fontmgr.h"
 #include "third_party/base/ptr_util.h"
 
-void CFX_GEModule::InitPlatform() {
-  CFPF_SkiaDeviceModule* pDeviceModule = CFPF_GetSkiaDeviceModule();
-  if (!pDeviceModule)
-    return;
+class CAndroidPlatform : public CFX_GEModule::PlatformIface {
+ public:
+  CAndroidPlatform() = default;
+  ~CAndroidPlatform() override {
+    if (m_pDeviceModule)
+      m_pDeviceModule->Destroy();
+  }
 
-  CFPF_SkiaFontMgr* pFontMgr = pDeviceModule->GetFontMgr();
-  if (pFontMgr) {
+  void Init() override {
+    m_pDeviceModule = CFPF_GetSkiaDeviceModule();
+    CFPF_SkiaFontMgr* pFontMgr = m_pDeviceModule->GetFontMgr();
+    if (!pFontMgr)
+      return;
+
     auto pFontInfo = pdfium::MakeUnique<CFX_AndroidFontInfo>();
     pFontInfo->Init(pFontMgr);
-    m_pFontMgr->SetSystemFontInfo(std::move(pFontInfo));
+    CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(std::move(pFontInfo));
   }
-  m_pPlatformData = pDeviceModule;
-}
 
-void CFX_GEModule::DestroyPlatform() {
-  if (m_pPlatformData)
-    static_cast<CFPF_SkiaDeviceModule*>(m_pPlatformData)->Destroy();
+ private:
+  CFPF_SkiaDeviceModule* m_pDeviceModule = nullptr;
+};
+
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return pdfium::MakeUnique<CAndroidPlatform>();
 }
diff --git a/core/fxge/apple/apple_int.h b/core/fxge/apple/apple_int.h
index 82415ec..c58e75c 100644
--- a/core/fxge/apple/apple_int.h
+++ b/core/fxge/apple/apple_int.h
@@ -9,38 +9,38 @@
 
 #include "core/fxcrt/fx_system.h"
 
+#include <Carbon/Carbon.h>
+
+#include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/fx_dib.h"
-#include "core/fxge/ifx_renderdevicedriver.h"
-
-#include <Carbon/Carbon.h>
+#include "core/fxge/renderdevicedriver_iface.h"
 
 class CQuartz2D {
  public:
-  void* createGraphics(const RetainPtr<CFX_DIBitmap>& bitmap);
-  void destroyGraphics(void* graphics);
+  void* CreateGraphics(const RetainPtr<CFX_DIBitmap>& bitmap);
+  void DestroyGraphics(void* graphics);
 
   void* CreateFont(const uint8_t* pFontData, uint32_t dwFontSize);
   void DestroyFont(void* pFont);
-  void setGraphicsTextMatrix(void* graphics, CFX_Matrix* matrix);
-  bool drawGraphicsString(void* graphics,
+  void SetGraphicsTextMatrix(void* graphics, const CFX_Matrix& matrix);
+  bool DrawGraphicsString(void* graphics,
                           void* font,
                           float fontSize,
                           uint16_t* glyphIndices,
                           CGPoint* glyphPositions,
                           int32_t chars,
-                          FX_ARGB argb,
-                          CFX_Matrix* matrix = nullptr);
-  void saveGraphicsState(void* graphics);
-  void restoreGraphicsState(void* graphics);
+                          FX_ARGB argb);
 };
 
-class CApplePlatform {
+class CApplePlatform : public CFX_GEModule::PlatformIface {
  public:
-  CApplePlatform() {}
-  ~CApplePlatform() {}
+  CApplePlatform();
+  ~CApplePlatform() override;
+
+  // CFX_GEModule::PlatformIface:
+  void Init() override;
 
   CQuartz2D m_quartz2d;
 };
diff --git a/core/fxge/apple/fx_apple_platform.cpp b/core/fxge/apple/fx_apple_platform.cpp
index 3c142c6..96adc88 100644
--- a/core/fxge/apple/fx_apple_platform.cpp
+++ b/core/fxge/apple/fx_apple_platform.cpp
@@ -5,22 +5,27 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include <memory>
+#include <vector>
 
-#include "core/fxcrt/cfx_fixedbufgrow.h"
 #include "core/fxcrt/fx_system.h"
 
+#include "core/fxge/apple/apple_int.h"
+#include "core/fxge/cfx_cliprgn.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_glyphbitmap.h"
+#include "core/fxge/cfx_glyphcache.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/cfx_substfont.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_freetype.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/span.h"
+
 #ifndef _SKIA_SUPPORT_
 #include "core/fxge/agg/fx_agg_driver.h"
 #endif
 
-#include "core/fxge/apple/apple_int.h"
-#include "core/fxge/cfx_cliprgn.h"
-#include "core/fxge/cfx_facecache.h"
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/fx_freetype.h"
-
 #ifndef _SKIA_SUPPORT_
 
 namespace {
@@ -29,9 +34,9 @@
 
 bool CGDrawGlyphRun(CGContextRef pContext,
                     int nChars,
-                    const FXTEXT_CHARPOS* pCharPos,
+                    const TextCharPos* pCharPos,
                     CFX_Font* pFont,
-                    const CFX_Matrix* pObject2Device,
+                    const CFX_Matrix& mtObject2Device,
                     float font_size,
                     uint32_t argb) {
   if (nChars == 0)
@@ -41,24 +46,21 @@
   if (bNegSize)
     font_size = -font_size;
 
-  CFX_Matrix new_matrix;
-  if (pObject2Device)
-    new_matrix.Concat(*pObject2Device);
-
+  CFX_Matrix new_matrix = mtObject2Device;
   CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatformData())
+      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
           ->m_quartz2d;
   if (!pFont->GetPlatformFont()) {
     if (pFont->GetPsName() == "DFHeiStd-W5")
       return false;
 
-    pFont->SetPlatformFont(
-        quartz2d.CreateFont(pFont->GetFontData(), pFont->GetSize()));
+    pdfium::span<const uint8_t> span = pFont->GetFontSpan();
+    pFont->SetPlatformFont(quartz2d.CreateFont(span.data(), span.size()));
     if (!pFont->GetPlatformFont())
       return false;
   }
-  CFX_FixedBufGrow<uint16_t, 32> glyph_indices(nChars);
-  CFX_FixedBufGrow<CGPoint, 32> glyph_positions(nChars);
+  std::vector<uint16_t> glyph_indices(nChars);
+  std::vector<CGPoint> glyph_positions(nChars);
   for (int i = 0; i < nChars; i++) {
     glyph_indices[i] =
         pCharPos[i].m_ExtGID ? pCharPos[i].m_ExtGID : pCharPos[i].m_GlyphIndex;
@@ -75,35 +77,35 @@
     new_matrix.b = -new_matrix.b;
     new_matrix.d = -new_matrix.d;
   }
-  quartz2d.setGraphicsTextMatrix(pContext, &new_matrix);
-  return quartz2d.drawGraphicsString(pContext, pFont->GetPlatformFont(),
-                                     font_size, glyph_indices, glyph_positions,
-                                     nChars, argb, nullptr);
+  quartz2d.SetGraphicsTextMatrix(pContext, new_matrix);
+  return quartz2d.DrawGraphicsString(pContext, pFont->GetPlatformFont(),
+                                     font_size, glyph_indices.data(),
+                                     glyph_positions.data(), nChars, argb);
 }
 
 }  // namespace
 
 void CFX_AggDeviceDriver::InitPlatform() {
   CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatformData())
+      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
           ->m_quartz2d;
-  m_pPlatformGraphics = quartz2d.createGraphics(m_pBitmap);
+  m_pPlatformGraphics = quartz2d.CreateGraphics(m_pBitmap);
 }
 
 void CFX_AggDeviceDriver::DestroyPlatform() {
   CQuartz2D& quartz2d =
-      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatformData())
+      static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
           ->m_quartz2d;
   if (m_pPlatformGraphics) {
-    quartz2d.destroyGraphics(m_pPlatformGraphics);
+    quartz2d.DestroyGraphics(m_pPlatformGraphics);
     m_pPlatformGraphics = nullptr;
   }
 }
 
 bool CFX_AggDeviceDriver::DrawDeviceText(int nChars,
-                                         const FXTEXT_CHARPOS* pCharPos,
+                                         const TextCharPos* pCharPos,
                                          CFX_Font* pFont,
-                                         const CFX_Matrix* pObject2Device,
+                                         const CFX_Matrix& mtObject2Device,
                                          float font_size,
                                          uint32_t argb) {
   if (!pFont)
@@ -151,7 +153,7 @@
   else
     CGContextClipToRect(ctx, rect_cg);
 
-  bool ret = CGDrawGlyphRun(ctx, nChars, pCharPos, pFont, pObject2Device,
+  bool ret = CGDrawGlyphRun(ctx, nChars, pCharPos, pFont, mtObject2Device,
                             font_size, argb);
   if (pImageCG)
     CGImageRelease(pImageCG);
@@ -161,15 +163,15 @@
 
 #endif  // _SKIA_SUPPORT_
 
-void CFX_FaceCache::InitPlatform() {}
+void CFX_GlyphCache::InitPlatform() {}
 
-void CFX_FaceCache::DestroyPlatform() {}
+void CFX_GlyphCache::DestroyPlatform() {}
 
-std::unique_ptr<CFX_GlyphBitmap> CFX_FaceCache::RenderGlyph_Nativetext(
+std::unique_ptr<CFX_GlyphBitmap> CFX_GlyphCache::RenderGlyph_Nativetext(
     const CFX_Font* pFont,
     uint32_t glyph_index,
-    const CFX_Matrix* pMatrix,
-    int dest_width,
+    const CFX_Matrix& matrix,
+    uint32_t dest_width,
     int anti_alias) {
   return nullptr;
 }
@@ -177,7 +179,7 @@
 void CFX_Font::ReleasePlatformResource() {
   if (m_pPlatformFont) {
     CQuartz2D& quartz2d =
-        static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatformData())
+        static_cast<CApplePlatform*>(CFX_GEModule::Get()->GetPlatform())
             ->m_quartz2d;
     quartz2d.DestroyFont(m_pPlatformFont);
     m_pPlatformFont = nullptr;
diff --git a/core/fxge/apple/fx_mac_imp.cpp b/core/fxge/apple/fx_mac_imp.cpp
index b212f15..64e50cc 100644
--- a/core/fxge/apple/fx_mac_imp.cpp
+++ b/core/fxge/apple/fx_mac_imp.cpp
@@ -12,7 +12,8 @@
 #include "core/fxge/cfx_folderfontinfo.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/systemfontinfo_iface.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
@@ -35,7 +36,7 @@
     {"Times-Italic", "Times New Roman Italic"},
 };
 
-class CFX_MacFontInfo : public CFX_FolderFontInfo {
+class CFX_MacFontInfo final : public CFX_FolderFontInfo {
  public:
   CFX_MacFontInfo() {}
   ~CFX_MacFontInfo() override {}
@@ -46,6 +47,8 @@
                 int charset,
                 int pitch_family,
                 const char* family) override;
+
+  bool ParseFontCfg(const char** pUserPaths);
 };
 
 const char JAPAN_GOTHIC[] = "Hiragino Kaku Gothic Pro W6";
@@ -118,23 +121,39 @@
   return it != m_FontList.end() ? it->second.get() : nullptr;
 }
 
+bool CFX_MacFontInfo::ParseFontCfg(const char** pUserPaths) {
+  if (!pUserPaths)
+    return false;
+
+  for (const char** pPath = pUserPaths; *pPath; ++pPath)
+    AddPath(*pPath);
+  return true;
+}
 }  // namespace
 
-std::unique_ptr<IFX_SystemFontInfo> IFX_SystemFontInfo::CreateDefault(
-    const char** pUnused) {
+std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
+    const char** pUserPaths) {
   auto pInfo = pdfium::MakeUnique<CFX_MacFontInfo>();
-  pInfo->AddPath("~/Library/Fonts");
-  pInfo->AddPath("/Library/Fonts");
-  pInfo->AddPath("/System/Library/Fonts");
+  if (!pInfo->ParseFontCfg(pUserPaths)) {
+    pInfo->AddPath("~/Library/Fonts");
+    pInfo->AddPath("/Library/Fonts");
+    pInfo->AddPath("/System/Library/Fonts");
+  }
   return std::move(pInfo);
 }
 
-void CFX_GEModule::InitPlatform() {
-  m_pPlatformData = new CApplePlatform;
-  m_pFontMgr->SetSystemFontInfo(IFX_SystemFontInfo::CreateDefault(nullptr));
+CApplePlatform::CApplePlatform() = default;
+
+CApplePlatform::~CApplePlatform() = default;
+
+void CApplePlatform::Init() {
+  CFX_GEModule* pModule = CFX_GEModule::Get();
+  pModule->GetFontMgr()->SetSystemFontInfo(
+      SystemFontInfoIface::CreateDefault(pModule->GetUserFontPaths()));
 }
 
-void CFX_GEModule::DestroyPlatform() {
-  delete reinterpret_cast<CApplePlatform*>(m_pPlatformData);
-  m_pPlatformData = nullptr;
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return pdfium::MakeUnique<CApplePlatform>();
 }
diff --git a/core/fxge/apple/fx_quartz_device.cpp b/core/fxge/apple/fx_quartz_device.cpp
index 709f201..2281ba5 100644
--- a/core/fxge/apple/fx_quartz_device.cpp
+++ b/core/fxge/apple/fx_quartz_device.cpp
@@ -10,20 +10,18 @@
 #include "core/fxge/agg/fx_agg_driver.h"
 #endif
 
-#include "core/fxcrt/fx_memory.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_freetype.h"
-#include "third_party/base/ptr_util.h"
 
 #include "core/fxge/apple/apple_int.h"
 #ifndef CGFLOAT_IS_DOUBLE
 #error Expected CGFLOAT_IS_DOUBLE to be defined by CoreGraphics headers
 #endif
 
-void* CQuartz2D::createGraphics(const RetainPtr<CFX_DIBitmap>& pBitmap) {
+void* CQuartz2D::CreateGraphics(const RetainPtr<CFX_DIBitmap>& pBitmap) {
   if (!pBitmap)
     return nullptr;
   CGBitmapInfo bmpInfo = kCGBitmapByteOrder32Little;
@@ -43,7 +41,7 @@
   return context;
 }
 
-void CQuartz2D::destroyGraphics(void* graphics) {
+void CQuartz2D::DestroyGraphics(void* graphics) {
   if (graphics)
     CGContextRelease((CGContextRef)graphics);
 }
@@ -63,36 +61,31 @@
   CGFontRelease((CGFontRef)pFont);
 }
 
-void CQuartz2D::setGraphicsTextMatrix(void* graphics, CFX_Matrix* matrix) {
-  if (!graphics || !matrix)
+void CQuartz2D::SetGraphicsTextMatrix(void* graphics,
+                                      const CFX_Matrix& matrix) {
+  if (!graphics)
     return;
-  CGContextRef context = (CGContextRef)graphics;
-  CGFloat ty = CGBitmapContextGetHeight(context) - matrix->f;
+  CGContextRef context = reinterpret_cast<CGContextRef>(graphics);
+  CGFloat ty = CGBitmapContextGetHeight(context) - matrix.f;
   CGContextSetTextMatrix(
-      context, CGAffineTransformMake(matrix->a, matrix->b, matrix->c, matrix->d,
-                                     matrix->e, ty));
+      context, CGAffineTransformMake(matrix.a, matrix.b, matrix.c, matrix.d,
+                                     matrix.e, ty));
 }
 
-bool CQuartz2D::drawGraphicsString(void* graphics,
+bool CQuartz2D::DrawGraphicsString(void* graphics,
                                    void* font,
                                    float fontSize,
                                    uint16_t* glyphIndices,
                                    CGPoint* glyphPositions,
                                    int32_t charsCount,
-                                   FX_ARGB argb,
-                                   CFX_Matrix* matrix) {
+                                   FX_ARGB argb) {
   if (!graphics)
     return false;
+
   CGContextRef context = (CGContextRef)graphics;
   CGContextSetFont(context, (CGFontRef)font);
   CGContextSetFontSize(context, fontSize);
-  if (matrix) {
-    CGAffineTransform m = CGContextGetTextMatrix(context);
-    m = CGAffineTransformConcat(
-        m, CGAffineTransformMake(matrix->a, matrix->b, matrix->c, matrix->d,
-                                 matrix->e, matrix->f));
-    CGContextSetTextMatrix(context, m);
-  }
+
   int32_t a;
   int32_t r;
   int32_t g;
@@ -107,9 +100,10 @@
     glyphPositionsCG[index].y = glyphPositions[index].y;
   }
 #else
-  CGPoint* glyphPositionsCG = (CGPoint*)glyphPositions;
+  CGPoint* glyphPositionsCG = glyphPositions;
 #endif
-  CGContextShowGlyphsAtPositions(context, (CGGlyph*)glyphIndices,
+  CGContextShowGlyphsAtPositions(context,
+                                 reinterpret_cast<CGGlyph*>(glyphIndices),
                                  glyphPositionsCG, charsCount);
 #if CGFLOAT_IS_DOUBLE
   delete[] glyphPositionsCG;
@@ -117,13 +111,3 @@
   CGContextRestoreGState(context);
   return true;
 }
-
-void CQuartz2D::saveGraphicsState(void* graphics) {
-  if (graphics)
-    CGContextSaveGState((CGContextRef)graphics);
-}
-
-void CQuartz2D::restoreGraphicsState(void* graphics) {
-  if (graphics)
-    CGContextRestoreGState((CGContextRef)graphics);
-}
diff --git a/core/fxge/cfx_cliprgn.cpp b/core/fxge/cfx_cliprgn.cpp
index 9569ee9..8a3f946 100644
--- a/core/fxge/cfx_cliprgn.cpp
+++ b/core/fxge/cfx_cliprgn.cpp
@@ -14,19 +14,9 @@
 CFX_ClipRgn::CFX_ClipRgn(int width, int height)
     : m_Type(RectI), m_Box(0, 0, width, height) {}
 
-CFX_ClipRgn::CFX_ClipRgn(const CFX_ClipRgn& src) {
-  m_Type = src.m_Type;
-  m_Box = src.m_Box;
-  m_Mask = src.m_Mask;
-}
+CFX_ClipRgn::CFX_ClipRgn(const CFX_ClipRgn& src) = default;
 
-CFX_ClipRgn::~CFX_ClipRgn() {}
-
-void CFX_ClipRgn::Reset(const FX_RECT& rect) {
-  m_Type = RectI;
-  m_Box = rect;
-  m_Mask = nullptr;
-}
+CFX_ClipRgn::~CFX_ClipRgn() = default;
 
 void CFX_ClipRgn::IntersectRect(const FX_RECT& rect) {
   if (m_Type == RectI) {
diff --git a/core/fxge/cfx_cliprgn.h b/core/fxge/cfx_cliprgn.h
index 58ebb4d..e1f9e22 100644
--- a/core/fxge/cfx_cliprgn.h
+++ b/core/fxge/cfx_cliprgn.h
@@ -24,13 +24,12 @@
   const FX_RECT& GetBox() const { return m_Box; }
   RetainPtr<CFX_DIBitmap> GetMask() const { return m_Mask; }
 
-  void Reset(const FX_RECT& rect);
   void IntersectRect(const FX_RECT& rect);
   void IntersectMaskF(int left, int top, const RetainPtr<CFX_DIBitmap>& Mask);
 
  private:
   void IntersectMaskRect(FX_RECT rect,
-                         FX_RECT mask_box,
+                         FX_RECT mask_rect,
                          const RetainPtr<CFX_DIBitmap>& Mask);
 
   ClipType m_Type;
diff --git a/core/fxge/cfx_color.cpp b/core/fxge/cfx_color.cpp
index 42ab39d..99330bc 100644
--- a/core/fxge/cfx_color.cpp
+++ b/core/fxge/cfx_color.cpp
@@ -8,8 +8,14 @@
 
 #include <algorithm>
 
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_simple_parser.h"
+// Color types are orded by increasing number of components so we can
+// choose a best color type during some conversions.
+static_assert(CFX_Color::kTransparent < CFX_Color::kGray,
+              "color type values must be ordered");
+static_assert(CFX_Color::kGray < CFX_Color::kRGB,
+              "color type values must be ordered");
+static_assert(CFX_Color::kRGB < CFX_Color::kCMYK,
+              "color type values must be ordered");
 
 namespace {
 
@@ -63,47 +69,6 @@
 
 }  // namespace
 
-// Static.
-CFX_Color CFX_Color::ParseColor(const CPDF_Array& array) {
-  CFX_Color rt;
-  switch (array.GetCount()) {
-    case 1:
-      rt = CFX_Color(CFX_Color::kGray, array.GetFloatAt(0));
-      break;
-    case 3:
-      rt = CFX_Color(CFX_Color::kRGB, array.GetFloatAt(0), array.GetFloatAt(1),
-                     array.GetFloatAt(2));
-      break;
-    case 4:
-      rt = CFX_Color(CFX_Color::kCMYK, array.GetFloatAt(0), array.GetFloatAt(1),
-                     array.GetFloatAt(2), array.GetFloatAt(3));
-      break;
-  }
-  return rt;
-}
-
-// Static.
-CFX_Color CFX_Color::ParseColor(const ByteString& str) {
-  CPDF_SimpleParser syntax(str.AsStringView());
-  if (syntax.FindTagParamFromStart("g", 1))
-    return CFX_Color(CFX_Color::kGray, FX_atof(syntax.GetWord()));
-
-  if (syntax.FindTagParamFromStart("rg", 3)) {
-    float f1 = FX_atof(syntax.GetWord());
-    float f2 = FX_atof(syntax.GetWord());
-    float f3 = FX_atof(syntax.GetWord());
-    return CFX_Color(CFX_Color::kRGB, f1, f2, f3);
-  }
-  if (syntax.FindTagParamFromStart("k", 4)) {
-    float f1 = FX_atof(syntax.GetWord());
-    float f2 = FX_atof(syntax.GetWord());
-    float f3 = FX_atof(syntax.GetWord());
-    float f4 = FX_atof(syntax.GetWord());
-    return CFX_Color(CFX_Color::kCMYK, f1, f2, f3, f4);
-  }
-  return CFX_Color(CFX_Color::kTransparent);
-}
-
 CFX_Color CFX_Color::ConvertColorType(int32_t nConvertColorType) const {
   if (nColorType == nConvertColorType)
     return *this;
diff --git a/core/fxge/cfx_color.h b/core/fxge/cfx_color.h
index 099e19b..fc941db 100644
--- a/core/fxge/cfx_color.h
+++ b/core/fxge/cfx_color.h
@@ -7,13 +7,10 @@
 #ifndef CORE_FXGE_CFX_COLOR_H_
 #define CORE_FXGE_CFX_COLOR_H_
 
-#include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fxge/fx_dib.h"
 
 struct CFX_Color {
-  static CFX_Color ParseColor(const CPDF_Array& array);
-  static CFX_Color ParseColor(const ByteString& str);
-
+  // Ordered by increasing number of components.
   enum Type { kTransparent = 0, kGray, kRGB, kCMYK };
 
   explicit CFX_Color(FX_COLORREF ref)
@@ -42,7 +39,7 @@
   CFX_Color operator/(float fColorDivide) const;
   CFX_Color operator-(float fColorSub) const;
 
-  CFX_Color ConvertColorType(int32_t other_nColorType) const;
+  CFX_Color ConvertColorType(int32_t nConvertColorType) const;
 
   FX_COLORREF ToFXColor(int32_t nTransparency) const;
 
@@ -61,4 +58,18 @@
   float fColor4;
 };
 
+inline bool operator==(const CFX_Color& c1, const CFX_Color& c2) {
+  return c1.nColorType == c2.nColorType && c1.fColor1 - c2.fColor1 < 0.0001 &&
+         c1.fColor1 - c2.fColor1 > -0.0001 &&
+         c1.fColor2 - c2.fColor2 < 0.0001 &&
+         c1.fColor2 - c2.fColor2 > -0.0001 &&
+         c1.fColor3 - c2.fColor3 < 0.0001 &&
+         c1.fColor3 - c2.fColor3 > -0.0001 &&
+         c1.fColor4 - c2.fColor4 < 0.0001 && c1.fColor4 - c2.fColor4 > -0.0001;
+}
+
+inline bool operator!=(const CFX_Color& c1, const CFX_Color& c2) {
+  return !(c1 == c2);
+}
+
 #endif  // CORE_FXGE_CFX_COLOR_H_
diff --git a/core/fxge/cfx_defaultrenderdevice.h b/core/fxge/cfx_defaultrenderdevice.h
index 59415df..d001775 100644
--- a/core/fxge/cfx_defaultrenderdevice.h
+++ b/core/fxge/cfx_defaultrenderdevice.h
@@ -12,31 +12,31 @@
 
 class SkPictureRecorder;
 
-class CFX_DefaultRenderDevice : public CFX_RenderDevice {
+class CFX_DefaultRenderDevice final : public CFX_RenderDevice {
  public:
   CFX_DefaultRenderDevice();
   ~CFX_DefaultRenderDevice() override;
 
   bool Attach(const RetainPtr<CFX_DIBitmap>& pBitmap,
               bool bRgbByteOrder,
-              const RetainPtr<CFX_DIBitmap>& pOriDevice,
+              const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
               bool bGroupKnockout);
   bool Create(int width,
               int height,
               FXDIB_Format format,
-              const RetainPtr<CFX_DIBitmap>& pOriDevice);
+              const RetainPtr<CFX_DIBitmap>& pBackdropBitmap);
 
 #ifdef _SKIA_SUPPORT_
   bool AttachRecorder(SkPictureRecorder* recorder);
   void Clear(uint32_t color);
   SkPictureRecorder* CreateRecorder(int size_x, int size_y);
   void DebugVerifyBitmapIsPreMultiplied() const override;
-  bool SetBitsWithMask(const RetainPtr<CFX_DIBSource>& pBitmap,
-                       const RetainPtr<CFX_DIBSource>& pMask,
+  bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                       const RetainPtr<CFX_DIBBase>& pMask,
                        int left,
                        int top,
                        int bitmap_alpha,
-                       int blend_type) override;
+                       BlendMode blend_type) override;
 #endif
 };
 
diff --git a/core/fxge/cfx_face.cpp b/core/fxge/cfx_face.cpp
new file mode 100644
index 0000000..6a41376
--- /dev/null
+++ b/core/fxge/cfx_face.cpp
@@ -0,0 +1,38 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_face.h"
+
+#include "third_party/base/ptr_util.h"
+
+// static
+RetainPtr<CFX_Face> CFX_Face::New(FT_Library library,
+                                  const RetainPtr<Retainable>& pDesc,
+                                  pdfium::span<const FT_Byte> data,
+                                  FT_Long face_index) {
+  FXFT_FaceRec* pRec = nullptr;
+  if (FT_New_Memory_Face(library, data.data(), data.size(), face_index,
+                         &pRec) != 0) {
+    return nullptr;
+  }
+  return pdfium::WrapRetain(new CFX_Face(pRec, pDesc));
+}
+
+// static
+RetainPtr<CFX_Face> CFX_Face::Open(FT_Library library,
+                                   const FT_Open_Args* args,
+                                   FT_Long face_index) {
+  FXFT_FaceRec* pRec = nullptr;
+  if (FT_Open_Face(library, args, face_index, &pRec) != 0)
+    return nullptr;
+
+  return pdfium::WrapRetain(new CFX_Face(pRec, nullptr));
+}
+
+CFX_Face::CFX_Face(FXFT_FaceRec* rec, const RetainPtr<Retainable>& pDesc)
+    : m_pRec(rec), m_pDesc(pDesc) {
+  ASSERT(m_pRec);
+}
+
+CFX_Face::~CFX_Face() = default;
diff --git a/core/fxge/cfx_face.h b/core/fxge/cfx_face.h
new file mode 100644
index 0000000..2412043
--- /dev/null
+++ b/core/fxge/cfx_face.h
@@ -0,0 +1,35 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXGE_CFX_FACE_H_
+#define CORE_FXGE_CFX_FACE_H_
+
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/fx_freetype.h"
+#include "third_party/base/span.h"
+
+class CFX_Face : public Retainable, public Observable {
+ public:
+  static RetainPtr<CFX_Face> New(FT_Library library,
+                                 const RetainPtr<Retainable>& pDesc,
+                                 pdfium::span<const FT_Byte> data,
+                                 FT_Long face_index);
+
+  static RetainPtr<CFX_Face> Open(FT_Library library,
+                                  const FT_Open_Args* args,
+                                  FT_Long face_index);
+
+  ~CFX_Face() override;
+
+  FXFT_FaceRec* GetRec() { return m_pRec.get(); }
+
+ private:
+  CFX_Face(FXFT_FaceRec* pRec, const RetainPtr<Retainable>& pDesc);
+
+  ScopedFXFTFaceRec const m_pRec;
+  RetainPtr<Retainable> const m_pDesc;
+};
+
+#endif  // CORE_FXGE_CFX_FACE_H_
diff --git a/core/fxge/cfx_facecache.cpp b/core/fxge/cfx_facecache.cpp
deleted file mode 100644
index 4f373ff..0000000
--- a/core/fxge/cfx_facecache.cpp
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/cfx_facecache.h"
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-#include <utility>
-
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_substfont.h"
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
-
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-#include "third_party/skia/include/core/SkStream.h"
-#include "third_party/skia/include/core/SkTypeface.h"
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#include "third_party/skia/include/ports/SkFontMgr.h"
-#include "third_party/skia/include/ports/SkFontMgr_empty.h"
-#endif
-#endif
-
-namespace {
-
-constexpr uint32_t kInvalidGlyphIndex = static_cast<uint32_t>(-1);
-
-constexpr int kMaxGlyphDimension = 2048;
-
-struct UniqueKeyGen {
-  void Generate(int count, ...);
-
-  char key_[128];
-  int key_len_;
-};
-
-void 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*>(key_)[i] = p;
-  }
-  va_end(argList);
-  key_len_ = count * sizeof(uint32_t);
-}
-
-}  // namespace
-
-CFX_FaceCache::CFX_FaceCache(FXFT_Face face)
-    : m_Face(face)
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-      ,
-      m_pTypeface(nullptr)
-#endif
-{
-}
-
-CFX_FaceCache::~CFX_FaceCache() {}
-
-std::unique_ptr<CFX_GlyphBitmap> CFX_FaceCache::RenderGlyph(
-    const CFX_Font* pFont,
-    uint32_t glyph_index,
-    bool bFontStyle,
-    const CFX_Matrix* pMatrix,
-    int dest_width,
-    int anti_alias) {
-  if (!m_Face)
-    return nullptr;
-
-  FXFT_Matrix ft_matrix;
-  ft_matrix.xx = (signed long)(pMatrix->a / 64 * 65536);
-  ft_matrix.xy = (signed long)(pMatrix->c / 64 * 65536);
-  ft_matrix.yx = (signed long)(pMatrix->b / 64 * 65536);
-  ft_matrix.yy = (signed long)(pMatrix->d / 64 * 65536);
-  bool bUseCJKSubFont = false;
-  const CFX_SubstFont* pSubstFont = pFont->GetSubstFont();
-  if (pSubstFont) {
-    bUseCJKSubFont = pSubstFont->m_bSubstCJK && bFontStyle;
-    int skew = 0;
-    if (bUseCJKSubFont)
-      skew = pSubstFont->m_bItalicCJK ? -15 : 0;
-    else
-      skew = pSubstFont->m_ItalicAngle;
-    if (skew) {
-      // |skew| is nonpositive so |-skew| is used as the index. We need to make
-      // sure |skew| != INT_MIN since -INT_MIN is undefined.
-      if (skew <= 0 && skew != std::numeric_limits<int>::min() &&
-          static_cast<size_t>(-skew) < CFX_Font::kAngleSkewArraySize) {
-        skew = -CFX_Font::s_AngleSkew[-skew];
-      } else {
-        skew = -58;
-      }
-      if (pFont->IsVertical())
-        ft_matrix.yx += ft_matrix.yy * skew / 100;
-      else
-        ft_matrix.xy -= ft_matrix.xx * skew / 100;
-    }
-    if (pSubstFont->m_bFlagMM) {
-      pFont->AdjustMMParams(glyph_index, dest_width,
-                            pFont->GetSubstFont()->m_Weight);
-    }
-  }
-  ScopedFontTransform scoped_transform(m_Face, &ft_matrix);
-  int load_flags = (m_Face->face_flags & FT_FACE_FLAG_SFNT)
-                       ? FXFT_LOAD_NO_BITMAP
-                       : (FXFT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
-  int error = FXFT_Load_Glyph(m_Face, glyph_index, load_flags);
-  if (error) {
-    // if an error is returned, try to reload glyphs without hinting.
-    if (load_flags & FT_LOAD_NO_HINTING || load_flags & FT_LOAD_NO_SCALE)
-      return nullptr;
-
-    load_flags |= FT_LOAD_NO_HINTING;
-    error = FXFT_Load_Glyph(m_Face, glyph_index, load_flags);
-
-    if (error)
-      return nullptr;
-  }
-  int weight = 0;
-  if (bUseCJKSubFont)
-    weight = pSubstFont->m_WeightCJK;
-  else
-    weight = pSubstFont ? pSubstFont->m_Weight : 0;
-  if (pSubstFont && !pSubstFont->m_bFlagMM && weight > 400) {
-    uint32_t index = (weight - 400) / 10;
-    if (index >= CFX_Font::kWeightPowArraySize)
-      return nullptr;
-    pdfium::base::CheckedNumeric<signed long> level = 0;
-    if (pSubstFont->m_Charset == FX_CHARSET_ShiftJIS)
-      level = CFX_Font::s_WeightPow_SHIFTJIS[index] * 2;
-    else
-      level = CFX_Font::s_WeightPow_11[index];
-
-    level = level *
-            (abs(static_cast<int>(ft_matrix.xx)) +
-             abs(static_cast<int>(ft_matrix.xy))) /
-            36655;
-    FXFT_Outline_Embolden(FXFT_Get_Glyph_Outline(m_Face),
-                          level.ValueOrDefault(0));
-  }
-  FXFT_Library_SetLcdFilter(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(),
-                            FT_LCD_FILTER_DEFAULT);
-  error = FXFT_Render_Glyph(m_Face, anti_alias);
-  if (error)
-    return nullptr;
-  int bmwidth = FXFT_Get_Bitmap_Width(FXFT_Get_Glyph_Bitmap(m_Face));
-  int bmheight = FXFT_Get_Bitmap_Rows(FXFT_Get_Glyph_Bitmap(m_Face));
-  if (bmwidth > kMaxGlyphDimension || bmheight > kMaxGlyphDimension)
-    return nullptr;
-  int dib_width = bmwidth;
-  auto pGlyphBitmap = pdfium::MakeUnique<CFX_GlyphBitmap>();
-  pGlyphBitmap->m_pBitmap->Create(
-      dib_width, bmheight,
-      anti_alias == FXFT_RENDER_MODE_MONO ? FXDIB_1bppMask : FXDIB_8bppMask);
-  pGlyphBitmap->m_Left = FXFT_Get_Glyph_BitmapLeft(m_Face);
-  pGlyphBitmap->m_Top = FXFT_Get_Glyph_BitmapTop(m_Face);
-  int dest_pitch = pGlyphBitmap->m_pBitmap->GetPitch();
-  int src_pitch = FXFT_Get_Bitmap_Pitch(FXFT_Get_Glyph_Bitmap(m_Face));
-  uint8_t* pDestBuf = pGlyphBitmap->m_pBitmap->GetBuffer();
-  uint8_t* pSrcBuf =
-      (uint8_t*)FXFT_Get_Bitmap_Buffer(FXFT_Get_Glyph_Bitmap(m_Face));
-  if (anti_alias != FXFT_RENDER_MODE_MONO &&
-      FXFT_Get_Bitmap_PixelMode(FXFT_Get_Glyph_Bitmap(m_Face)) ==
-          FXFT_PIXEL_MODE_MONO) {
-    int bytes = anti_alias == FXFT_RENDER_MODE_LCD ? 3 : 1;
-    for (int i = 0; i < bmheight; i++) {
-      for (int n = 0; n < bmwidth; n++) {
-        uint8_t data =
-            (pSrcBuf[i * src_pitch + n / 8] & (0x80 >> (n % 8))) ? 255 : 0;
-        for (int b = 0; b < bytes; b++)
-          pDestBuf[i * dest_pitch + n * bytes + b] = data;
-      }
-    }
-  } else {
-    memset(pDestBuf, 0, dest_pitch * bmheight);
-    int rowbytes = std::min(abs(src_pitch), dest_pitch);
-    for (int row = 0; row < bmheight; row++)
-      memcpy(pDestBuf + row * dest_pitch, pSrcBuf + row * src_pitch, rowbytes);
-  }
-  return pGlyphBitmap;
-}
-
-const CFX_PathData* CFX_FaceCache::LoadGlyphPath(const CFX_Font* pFont,
-                                                 uint32_t glyph_index,
-                                                 int dest_width) {
-  if (!m_Face || glyph_index == kInvalidGlyphIndex)
-    return nullptr;
-
-  const auto* pSubstFont = pFont->GetSubstFont();
-  int weight = pSubstFont ? pSubstFont->m_Weight : 0;
-  int angle = pSubstFont ? pSubstFont->m_ItalicAngle : 0;
-  bool vertical = pSubstFont ? pFont->IsVertical() : false;
-  const PathMapKey key =
-      std::make_tuple(glyph_index, dest_width, weight, angle, vertical);
-  auto it = m_PathMap.find(key);
-  if (it != m_PathMap.end())
-    return it->second.get();
-
-  CFX_PathData* pGlyphPath = pFont->LoadGlyphPathImpl(glyph_index, dest_width);
-  m_PathMap[key] = std::unique_ptr<CFX_PathData>(pGlyphPath);
-  return pGlyphPath;
-}
-
-const CFX_GlyphBitmap* CFX_FaceCache::LoadGlyphBitmap(const CFX_Font* pFont,
-                                                      uint32_t glyph_index,
-                                                      bool bFontStyle,
-                                                      const CFX_Matrix* pMatrix,
-                                                      int dest_width,
-                                                      int anti_alias,
-                                                      int& text_flags) {
-  if (glyph_index == kInvalidGlyphIndex)
-    return nullptr;
-
-  UniqueKeyGen keygen;
-  int nMatrixA = static_cast<int>(pMatrix->a * 10000);
-  int nMatrixB = static_cast<int>(pMatrix->b * 10000);
-  int nMatrixC = static_cast<int>(pMatrix->c * 10000);
-  int nMatrixD = static_cast<int>(pMatrix->d * 10000);
-#if _FX_PLATFORM_ != _FX_PLATFORM_APPLE_
-  if (pFont->GetSubstFont()) {
-    keygen.Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                    anti_alias, pFont->GetSubstFont()->m_Weight,
-                    pFont->GetSubstFont()->m_ItalicAngle, pFont->IsVertical());
-  } else {
-    keygen.Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                    anti_alias);
-  }
-#else
-  if (text_flags & FXTEXT_NO_NATIVETEXT) {
-    if (pFont->GetSubstFont()) {
-      keygen.Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                      anti_alias, pFont->GetSubstFont()->m_Weight,
-                      pFont->GetSubstFont()->m_ItalicAngle,
-                      pFont->IsVertical());
-    } else {
-      keygen.Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                      anti_alias);
-    }
-  } else {
-    if (pFont->GetSubstFont()) {
-      keygen.Generate(10, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                      anti_alias, pFont->GetSubstFont()->m_Weight,
-                      pFont->GetSubstFont()->m_ItalicAngle, pFont->IsVertical(),
-                      3);
-    } else {
-      keygen.Generate(7, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                      anti_alias, 3);
-    }
-  }
-#endif
-  ByteString FaceGlyphsKey(keygen.key_, keygen.key_len_);
-#if _FX_PLATFORM_ != _FX_PLATFORM_APPLE_ || defined _SKIA_SUPPORT_ || \
-    defined _SKIA_SUPPORT_PATHS_
-  return LookUpGlyphBitmap(pFont, pMatrix, FaceGlyphsKey, glyph_index,
-                           bFontStyle, dest_width, anti_alias);
-#else
-  if (text_flags & FXTEXT_NO_NATIVETEXT) {
-    return LookUpGlyphBitmap(pFont, pMatrix, FaceGlyphsKey, glyph_index,
-                             bFontStyle, dest_width, anti_alias);
-  }
-  std::unique_ptr<CFX_GlyphBitmap> pGlyphBitmap;
-  auto it = m_SizeMap.find(FaceGlyphsKey);
-  if (it != m_SizeMap.end()) {
-    SizeGlyphCache* pSizeCache = &(it->second);
-    auto it2 = pSizeCache->find(glyph_index);
-    if (it2 != pSizeCache->end())
-      return it2->second.get();
-
-    pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, pMatrix,
-                                          dest_width, anti_alias);
-    if (pGlyphBitmap) {
-      CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
-      (*pSizeCache)[glyph_index] = std::move(pGlyphBitmap);
-      return pResult;
-    }
-  } else {
-    pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, pMatrix,
-                                          dest_width, anti_alias);
-    if (pGlyphBitmap) {
-      CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
-
-      SizeGlyphCache cache;
-      cache[glyph_index] = std::move(pGlyphBitmap);
-
-      m_SizeMap[FaceGlyphsKey] = std::move(cache);
-      return pResult;
-    }
-  }
-  if (pFont->GetSubstFont()) {
-    keygen.Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                    anti_alias, pFont->GetSubstFont()->m_Weight,
-                    pFont->GetSubstFont()->m_ItalicAngle, pFont->IsVertical());
-  } else {
-    keygen.Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
-                    anti_alias);
-  }
-  ByteString FaceGlyphsKey2(keygen.key_, keygen.key_len_);
-  text_flags |= FXTEXT_NO_NATIVETEXT;
-  return LookUpGlyphBitmap(pFont, pMatrix, FaceGlyphsKey2, glyph_index,
-                           bFontStyle, dest_width, anti_alias);
-#endif
-}
-
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-CFX_TypeFace* CFX_FaceCache::GetDeviceCache(const CFX_Font* pFont) {
-  if (!m_pTypeface) {
-    m_pTypeface = SkTypeface::MakeFromStream(
-        new SkMemoryStream(pFont->GetFontData(), pFont->GetSize()));
-  }
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  if (!m_pTypeface) {
-    sk_sp<SkFontMgr> customMgr(SkFontMgr_New_Custom_Empty());
-    m_pTypeface = customMgr->makeFromStream(pdfium::MakeUnique<SkMemoryStream>(
-        pFont->GetFontData(), pFont->GetSize()));
-  }
-#endif
-  return m_pTypeface.get();
-}
-#endif
-
-#if _FX_PLATFORM_ != _FX_PLATFORM_APPLE_
-void CFX_FaceCache::InitPlatform() {}
-#endif
-
-CFX_GlyphBitmap* CFX_FaceCache::LookUpGlyphBitmap(
-    const CFX_Font* pFont,
-    const CFX_Matrix* pMatrix,
-    const ByteString& FaceGlyphsKey,
-    uint32_t glyph_index,
-    bool bFontStyle,
-    int dest_width,
-    int anti_alias) {
-  SizeGlyphCache* pSizeCache;
-  auto it = m_SizeMap.find(FaceGlyphsKey);
-  if (it == m_SizeMap.end()) {
-    m_SizeMap[FaceGlyphsKey] = SizeGlyphCache();
-    pSizeCache = &(m_SizeMap[FaceGlyphsKey]);
-  } else {
-    pSizeCache = &(it->second);
-  }
-
-  auto it2 = pSizeCache->find(glyph_index);
-  if (it2 != pSizeCache->end())
-    return it2->second.get();
-
-  std::unique_ptr<CFX_GlyphBitmap> pGlyphBitmap = RenderGlyph(
-      pFont, glyph_index, bFontStyle, pMatrix, dest_width, anti_alias);
-  CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
-  (*pSizeCache)[glyph_index] = std::move(pGlyphBitmap);
-  return pResult;
-}
diff --git a/core/fxge/cfx_facecache.h b/core/fxge/cfx_facecache.h
deleted file mode 100644
index a39da88..0000000
--- a/core/fxge/cfx_facecache.h
+++ /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
-
-#ifndef CORE_FXGE_CFX_FACECACHE_H_
-#define CORE_FXGE_CFX_FACECACHE_H_
-
-#include <map>
-#include <memory>
-#include <tuple>
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-#include "third_party/skia/include/core/SkTypeface.h"
-#endif
-
-class CFX_Font;
-class CFX_PathData;
-
-class CFX_FaceCache {
- public:
-  explicit CFX_FaceCache(FXFT_Face face);
-  ~CFX_FaceCache();
-  const CFX_GlyphBitmap* LoadGlyphBitmap(const CFX_Font* pFont,
-                                         uint32_t glyph_index,
-                                         bool bFontStyle,
-                                         const CFX_Matrix* pMatrix,
-                                         int dest_width,
-                                         int anti_alias,
-                                         int& text_flags);
-  const CFX_PathData* LoadGlyphPath(const CFX_Font* pFont,
-                                    uint32_t glyph_index,
-                                    int dest_width);
-
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-  CFX_TypeFace* GetDeviceCache(const CFX_Font* pFont);
-#endif
-
- private:
-  using SizeGlyphCache = std::map<uint32_t, std::unique_ptr<CFX_GlyphBitmap>>;
-  // <glyph_index, width, weight, angle, vertical>
-  using PathMapKey = std::tuple<uint32_t, int, int, int, bool>;
-
-  std::unique_ptr<CFX_GlyphBitmap> RenderGlyph(const CFX_Font* pFont,
-                                               uint32_t glyph_index,
-                                               bool bFontStyle,
-                                               const CFX_Matrix* pMatrix,
-                                               int dest_width,
-                                               int anti_alias);
-  std::unique_ptr<CFX_GlyphBitmap> RenderGlyph_Nativetext(
-      const CFX_Font* pFont,
-      uint32_t glyph_index,
-      const CFX_Matrix* pMatrix,
-      int dest_width,
-      int anti_alias);
-  CFX_GlyphBitmap* LookUpGlyphBitmap(const CFX_Font* pFont,
-                                     const CFX_Matrix* pMatrix,
-                                     const ByteString& FaceGlyphsKey,
-                                     uint32_t glyph_index,
-                                     bool bFontStyle,
-                                     int dest_width,
-                                     int anti_alias);
-  void InitPlatform();
-  void DestroyPlatform();
-
-  FXFT_Face const m_Face;
-  std::map<ByteString, SizeGlyphCache> m_SizeMap;
-  std::map<PathMapKey, std::unique_ptr<CFX_PathData>> m_PathMap;
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-  sk_sp<SkTypeface> m_pTypeface;
-#endif
-};
-
-#endif  //  CORE_FXGE_CFX_FACECACHE_H_
diff --git a/core/fxge/cfx_folderfontinfo.cpp b/core/fxge/cfx_folderfontinfo.cpp
index 532824d..c93b1f0 100644
--- a/core/fxge/cfx_folderfontinfo.cpp
+++ b/core/fxge/cfx_folderfontinfo.cpp
@@ -9,7 +9,10 @@
 #include <limits>
 #include <utility>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/fx_font.h"
@@ -43,19 +46,31 @@
     {"Times-Italic", "Times New Roman Italic"},
 };
 
-ByteString FPDF_ReadStringFromFile(FILE* pFile, uint32_t size) {
-  ByteString buffer;
-  if (!fread(buffer.GetBuffer(size), size, 1, pFile))
-    return ByteString();
-  buffer.ReleaseBuffer(size);
-  return buffer;
+// Used with std::unique_ptr to automatically call fclose().
+struct FxFileCloser {
+  inline void operator()(FILE* h) const {
+    if (h)
+      fclose(h);
+  }
+};
+
+ByteString ReadStringFromFile(FILE* pFile, uint32_t size) {
+  ByteString result;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> buffer = result.GetBuffer(size);
+    if (!fread(buffer.data(), size, 1, pFile))
+      return ByteString();
+  }
+  result.ReleaseBuffer(size);
+  return result;
 }
 
-ByteString FPDF_LoadTableFromTT(FILE* pFile,
-                                const uint8_t* pTables,
-                                uint32_t nTables,
-                                uint32_t tag,
-                                uint32_t fileSize) {
+ByteString LoadTableFromTT(FILE* pFile,
+                           const uint8_t* pTables,
+                           uint32_t nTables,
+                           uint32_t tag,
+                           uint32_t fileSize) {
   for (uint32_t i = 0; i < nTables; i++) {
     const uint8_t* p = pTables + i * 16;
     if (GET_TT_LONG(p) == tag) {
@@ -65,7 +80,7 @@
           offset + size > fileSize || fseek(pFile, offset, SEEK_SET) < 0) {
         return ByteString();
       }
-      return FPDF_ReadStringFromFile(pFile, size);
+      return ReadStringFromFile(pFile, size);
     }
   }
   return ByteString();
@@ -96,7 +111,7 @@
                         int pitch_family,
                         uint32_t style) {
   int32_t iSimilarValue = 0;
-  if (FontStyleIsBold(style) == (weight > 400))
+  if (FontStyleIsForceBold(style) == (weight > 400))
     iSimilarValue += 16;
   if (FontStyleIsItalic(style) == bItalic)
     iSimilarValue += 16;
@@ -111,9 +126,9 @@
 
 }  // namespace
 
-CFX_FolderFontInfo::CFX_FolderFontInfo() {}
+CFX_FolderFontInfo::CFX_FolderFontInfo() = default;
 
-CFX_FolderFontInfo::~CFX_FolderFontInfo() {}
+CFX_FolderFontInfo::~CFX_FolderFontInfo() = default;
 
 void CFX_FolderFontInfo::AddPath(const ByteString& path) {
   m_PathList.push_back(path);
@@ -127,25 +142,26 @@
 }
 
 void CFX_FolderFontInfo::ScanPath(const ByteString& path) {
-  FX_FileHandle* handle = FX_OpenFolder(path.c_str());
+  std::unique_ptr<FX_FolderHandle, FxFolderHandleCloser> handle(
+      FX_OpenFolder(path.c_str()));
   if (!handle)
     return;
 
   ByteString filename;
   bool bFolder;
-  while (FX_GetNextFile(handle, &filename, &bFolder)) {
+  while (FX_GetNextFile(handle.get(), &filename, &bFolder)) {
     if (bFolder) {
       if (filename == "." || filename == "..")
         continue;
     } else {
-      ByteString ext = filename.Right(4);
-      ext.MakeUpper();
-      if (ext != ".TTF" && ext != ".OTF" && ext != ".TTC")
+      ByteString ext = filename.Last(4);
+      ext.MakeLower();
+      if (ext != ".ttf" && ext != ".ttc" && ext != ".otf")
         continue;
     }
 
     ByteString fullpath = path;
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
     fullpath += "\\";
 #else
     fullpath += "/";
@@ -154,49 +170,44 @@
     fullpath += filename;
     bFolder ? ScanPath(fullpath) : ScanFile(fullpath);
   }
-  FX_CloseFolder(handle);
 }
 
 void CFX_FolderFontInfo::ScanFile(const ByteString& path) {
-  FILE* pFile = fopen(path.c_str(), "rb");
+  std::unique_ptr<FILE, FxFileCloser> pFile(fopen(path.c_str(), "rb"));
   if (!pFile)
     return;
 
-  fseek(pFile, 0, SEEK_END);
+  fseek(pFile.get(), 0, SEEK_END);
 
-  uint32_t filesize = ftell(pFile);
+  uint32_t filesize = ftell(pFile.get());
   uint8_t buffer[16];
-  fseek(pFile, 0, SEEK_SET);
+  fseek(pFile.get(), 0, SEEK_SET);
 
-  size_t readCnt = fread(buffer, 12, 1, pFile);
-  if (readCnt != 1) {
-    fclose(pFile);
+  size_t readCnt = fread(buffer, 12, 1, pFile.get());
+  if (readCnt != 1)
+    return;
+
+  if (GET_TT_LONG(buffer) != kTableTTCF) {
+    ReportFace(path, pFile.get(), filesize, 0);
     return;
   }
 
-  if (GET_TT_LONG(buffer) == kTableTTCF) {
-    uint32_t nFaces = GET_TT_LONG(buffer + 8);
-    if (nFaces > std::numeric_limits<uint32_t>::max() / 4) {
-      fclose(pFile);
-      return;
-    }
-    uint32_t face_bytes = nFaces * 4;
-    uint8_t* offsets = FX_Alloc(uint8_t, face_bytes);
-    readCnt = fread(offsets, 1, face_bytes, pFile);
-    if (readCnt != face_bytes) {
-      FX_Free(offsets);
-      fclose(pFile);
-      return;
-    }
-    for (uint32_t i = 0; i < nFaces; i++) {
-      uint8_t* p = offsets + i * 4;
-      ReportFace(path, pFile, filesize, GET_TT_LONG(p));
-    }
-    FX_Free(offsets);
-  } else {
-    ReportFace(path, pFile, filesize, 0);
-  }
-  fclose(pFile);
+  uint32_t nFaces = GET_TT_LONG(buffer + 8);
+  FX_SAFE_SIZE_T safe_face_bytes = nFaces;
+  safe_face_bytes *= 4;
+  if (!safe_face_bytes.IsValid())
+    return;
+
+  const size_t face_bytes = safe_face_bytes.ValueOrDie();
+  std::unique_ptr<uint8_t, FxFreeDeleter> offsets(
+      FX_Alloc(uint8_t, face_bytes));
+  readCnt = fread(offsets.get(), 1, face_bytes, pFile.get());
+  if (readCnt != face_bytes)
+    return;
+
+  auto offsets_span = pdfium::make_span(offsets.get(), face_bytes);
+  for (uint32_t i = 0; i < nFaces; i++)
+    ReportFace(path, pFile.get(), filesize, GET_TT_LONG(&offsets_span[i * 4]));
 }
 
 void CFX_FolderFontInfo::ReportFace(const ByteString& path,
@@ -208,20 +219,20 @@
     return;
 
   uint32_t nTables = GET_TT_SHORT(buffer + 4);
-  ByteString tables = FPDF_ReadStringFromFile(pFile, nTables * 16);
+  ByteString tables = ReadStringFromFile(pFile, nTables * 16);
   if (tables.IsEmpty())
     return;
 
-  ByteString names = FPDF_LoadTableFromTT(pFile, tables.raw_str(), nTables,
-                                          0x6e616d65, filesize);
+  ByteString names =
+      LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x6e616d65, filesize);
   if (names.IsEmpty())
     return;
 
-  ByteString facename = GetNameFromTT(names.raw_str(), names.GetLength(), 1);
+  ByteString facename = GetNameFromTT(names.raw_span(), 1);
   if (facename.IsEmpty())
     return;
 
-  ByteString style = GetNameFromTT(names.raw_str(), names.GetLength(), 2);
+  ByteString style = GetNameFromTT(names.raw_span(), 2);
   if (style != "Regular")
     facename += " " + style;
 
@@ -230,28 +241,28 @@
 
   auto pInfo = pdfium::MakeUnique<FontFaceInfo>(path, facename, tables, offset,
                                                 filesize);
-  ByteString os2 = FPDF_LoadTableFromTT(pFile, tables.raw_str(), nTables,
-                                        0x4f532f32, filesize);
+  ByteString os2 =
+      LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x4f532f32, filesize);
   if (os2.GetLength() >= 86) {
     const uint8_t* p = os2.raw_str() + 78;
     uint32_t codepages = GET_TT_LONG(p);
-    if (codepages & (1 << 17)) {
+    if (codepages & (1U << 17)) {
       m_pMapper->AddInstalledFont(facename, FX_CHARSET_ShiftJIS);
       pInfo->m_Charsets |= CHARSET_FLAG_SHIFTJIS;
     }
-    if (codepages & (1 << 18)) {
+    if (codepages & (1U << 18)) {
       m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseSimplified);
       pInfo->m_Charsets |= CHARSET_FLAG_GB;
     }
-    if (codepages & (1 << 20)) {
+    if (codepages & (1U << 20)) {
       m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseTraditional);
       pInfo->m_Charsets |= CHARSET_FLAG_BIG5;
     }
-    if ((codepages & (1 << 19)) || (codepages & (1 << 21))) {
+    if ((codepages & (1U << 19)) || (codepages & (1U << 21))) {
       m_pMapper->AddInstalledFont(facename, FX_CHARSET_Hangul);
       pInfo->m_Charsets |= CHARSET_FLAG_KOREAN;
     }
-    if (codepages & (1 << 31)) {
+    if (codepages & (1U << 31)) {
       m_pMapper->AddInstalledFont(facename, FX_CHARSET_Symbol);
       pInfo->m_Charsets |= CHARSET_FLAG_SYMBOL;
     }
@@ -260,7 +271,7 @@
   pInfo->m_Charsets |= CHARSET_FLAG_ANSI;
   pInfo->m_Styles = 0;
   if (style.Contains("Bold"))
-    pInfo->m_Styles |= FXFONT_BOLD;
+    pInfo->m_Styles |= FXFONT_FORCE_BOLD;
   if (style.Contains("Italic") || style.Contains("Oblique"))
     pInfo->m_Styles |= FXFONT_ITALIC;
   if (facename.Contains("Serif"))
@@ -288,6 +299,7 @@
   if (charset == FX_CHARSET_ANSI && FontFamilyIsFixedPitch(pitch_family))
     return GetFont("Courier New");
 
+  ByteStringView bsFamily(family);
   uint32_t charset_flag = GetCharset(charset);
   int32_t iBestSimilar = 0;
   for (const auto& it : m_FontList) {
@@ -296,7 +308,7 @@
     if (!(pFont->m_Charsets & charset_flag) && charset != FX_CHARSET_Default)
       continue;
 
-    if (bMatchName && !bsName.Contains(family))
+    if (bMatchName && !bsName.Contains(bsFamily))
       continue;
 
     int32_t iSimilarValue =
@@ -317,15 +329,6 @@
   return nullptr;
 }
 
-#ifdef PDF_ENABLE_XFA
-void* CFX_FolderFontInfo::MapFontByUnicode(uint32_t dwUnicode,
-                                           int weight,
-                                           bool bItalic,
-                                           int pitch_family) {
-  return nullptr;
-}
-#endif  // PDF_ENABLE_XFA
-
 void* CFX_FolderFontInfo::GetFont(const char* face) {
   auto it = m_FontList.find(face);
   return it != m_FontList.end() ? it->second.get() : nullptr;
@@ -333,8 +336,7 @@
 
 uint32_t CFX_FolderFontInfo::GetFontData(void* hFont,
                                          uint32_t table,
-                                         uint8_t* buffer,
-                                         uint32_t size) {
+                                         pdfium::span<uint8_t> buffer) {
   if (!hFont)
     return 0;
 
@@ -356,18 +358,18 @@
     }
   }
 
-  if (!datasize || size < datasize)
+  if (!datasize || buffer.size() < datasize)
     return datasize;
 
-  FILE* pFile = fopen(pFont->m_FilePath.c_str(), "rb");
+  std::unique_ptr<FILE, FxFileCloser> pFile(
+      fopen(pFont->m_FilePath.c_str(), "rb"));
   if (!pFile)
     return 0;
 
-  if (fseek(pFile, offset, SEEK_SET) < 0 ||
-      fread(buffer, datasize, 1, pFile) != 1) {
-    datasize = 0;
+  if (fseek(pFile.get(), offset, SEEK_SET) < 0 ||
+      fread(buffer.data(), datasize, 1, pFile.get()) != 1) {
+    return 0;
   }
-  fclose(pFile);
   return datasize;
 }
 
diff --git a/core/fxge/cfx_folderfontinfo.h b/core/fxge/cfx_folderfontinfo.h
index 397b386..4fd516d 100644
--- a/core/fxge/cfx_folderfontinfo.h
+++ b/core/fxge/cfx_folderfontinfo.h
@@ -13,9 +13,9 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "core/fxge/systemfontinfo_iface.h"
 
-class CFX_FolderFontInfo : public IFX_SystemFontInfo {
+class CFX_FolderFontInfo : public SystemFontInfoIface {
  public:
   CFX_FolderFontInfo();
   ~CFX_FolderFontInfo() override;
@@ -28,18 +28,11 @@
                 bool bItalic,
                 int charset,
                 int pitch_family,
-                const char* face) override;
-#ifdef PDF_ENABLE_XFA
-  void* MapFontByUnicode(uint32_t dwUnicode,
-                         int weight,
-                         bool bItalic,
-                         int pitch_family) override;
-#endif  // PDF_ENABLE_XFA
+                const char* family) override;
   void* GetFont(const char* face) override;
   uint32_t GetFontData(void* hFont,
                        uint32_t table,
-                       uint8_t* buffer,
-                       uint32_t size) override;
+                       pdfium::span<uint8_t> buffer) override;
   void DeleteFont(void* hFont) override;
   bool GetFaceName(void* hFont, ByteString* name) override;
   bool GetFontCharset(void* hFont, int* charset) override;
diff --git a/core/fxge/cfx_font.cpp b/core/fxge/cfx_font.cpp
index 6d969a3..5a51b6d 100644
--- a/core/fxge/cfx_font.cpp
+++ b/core/fxge/cfx_font.cpp
@@ -12,16 +12,17 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/font/cpdf_font.h"
+#include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_stream.h"
-#include "core/fxge/cfx_facecache.h"
 #include "core/fxge/cfx_fontcache.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_glyphcache.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_substfont.h"
-#include "core/fxge/fx_freetype.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/scoped_font_transform.h"
 #include "third_party/base/ptr_util.h"
 
 #define EM_ADJUST(em, a) (em == 0 ? (a) : (a)*1000 / em)
@@ -40,7 +41,7 @@
 
 #ifdef PDF_ENABLE_XFA
 
-unsigned long FTStreamRead(FXFT_Stream stream,
+unsigned long FTStreamRead(FXFT_StreamRec* stream,
                            unsigned long offset,
                            unsigned char* buffer,
                            unsigned long count) {
@@ -49,16 +50,15 @@
 
   IFX_SeekableReadStream* pFile =
       static_cast<IFX_SeekableReadStream*>(stream->descriptor.pointer);
-  return pFile && pFile->ReadBlock(buffer, offset, count) ? count : 0;
+  return pFile && pFile->ReadBlockAtOffset(buffer, offset, count) ? count : 0;
 }
 
-void FTStreamClose(FXFT_Stream stream) {}
+void FTStreamClose(FXFT_StreamRec* stream) {}
 
-bool LoadFileImp(FXFT_Library library,
-                 FXFT_Face* Face,
-                 const RetainPtr<IFX_SeekableReadStream>& pFile,
-                 int32_t faceIndex,
-                 std::unique_ptr<FXFT_StreamRec>* stream) {
+RetainPtr<CFX_Face> LoadFileImp(FXFT_LibraryRec* library,
+                                const RetainPtr<IFX_SeekableReadStream>& pFile,
+                                int32_t faceIndex,
+                                std::unique_ptr<FXFT_StreamRec>* stream) {
   auto stream1 = pdfium::MakeUnique<FXFT_StreamRec>();
   stream1->base = nullptr;
   stream1->size = static_cast<unsigned long>(pFile->GetSize());
@@ -66,21 +66,20 @@
   stream1->descriptor.pointer = static_cast<void*>(pFile.Get());
   stream1->close = FTStreamClose;
   stream1->read = FTStreamRead;
-  FXFT_Open_Args args;
+
+  FT_Open_Args args;
   args.flags = FT_OPEN_STREAM;
   args.stream = stream1.get();
-  if (FXFT_Open_Face(library, &args, faceIndex, Face))
-    return false;
-  if (stream)
-    *stream = std::move(stream1);
-  return true;
+
+  RetainPtr<CFX_Face> face = CFX_Face::Open(library, &args, faceIndex);
+  if (!face)
+    return nullptr;
+
+  *stream = std::move(stream1);
+  return face;
 }
 #endif  // PDF_ENABLE_XFA
 
-FXFT_Face FT_LoadFont(const uint8_t* pData, int size) {
-  return CFX_GEModule::Get()->GetFontMgr()->GetFixedFace(pData, size, 0);
-}
-
 void Outline_CheckEmptyContour(OUTLINE_PARAMS* param) {
   std::vector<FX_PATHPOINT>& points = param->m_pPath->GetPoints();
   size_t size = points.size();
@@ -99,7 +98,7 @@
   points.resize(size);
 }
 
-int Outline_MoveTo(const FXFT_Vector* to, void* user) {
+int Outline_MoveTo(const FT_Vector* to, void* user) {
   OUTLINE_PARAMS* param = static_cast<OUTLINE_PARAMS*>(user);
 
   Outline_CheckEmptyContour(param);
@@ -114,7 +113,7 @@
   return 0;
 }
 
-int Outline_LineTo(const FXFT_Vector* to, void* user) {
+int Outline_LineTo(const FT_Vector* to, void* user) {
   OUTLINE_PARAMS* param = static_cast<OUTLINE_PARAMS*>(user);
 
   param->m_pPath->AppendPoint(
@@ -126,9 +125,7 @@
   return 0;
 }
 
-int Outline_ConicTo(const FXFT_Vector* control,
-                    const FXFT_Vector* to,
-                    void* user) {
+int Outline_ConicTo(const FT_Vector* control, const FT_Vector* to, void* user) {
   OUTLINE_PARAMS* param = static_cast<OUTLINE_PARAMS*>(user);
 
   param->m_pPath->AppendPoint(
@@ -152,9 +149,9 @@
   return 0;
 }
 
-int Outline_CubicTo(const FXFT_Vector* control1,
-                    const FXFT_Vector* control2,
-                    const FXFT_Vector* to,
+int Outline_CubicTo(const FT_Vector* control1,
+                    const FT_Vector* control2,
+                    const FT_Vector* to,
                     void* user) {
   OUTLINE_PARAMS* param = static_cast<OUTLINE_PARAMS*>(user);
 
@@ -175,6 +172,10 @@
   return 0;
 }
 
+bool ShouldAppendStyle(const ByteString& style) {
+  return !style.IsEmpty() && style != "Regular";
+}
+
 }  // namespace
 
 const char CFX_Font::s_AngleSkew[] = {
@@ -209,54 +210,136 @@
     59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60,
 };
 
-CFX_Font::CFX_Font()
-    :
-      m_Face(nullptr),
-      m_FaceCache(nullptr),
-      m_pFontData(nullptr),
-      m_pGsubData(nullptr),
-      m_dwSize(0),
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-      m_pPlatformFont(nullptr),
+const CFX_Font::CharsetFontMap CFX_Font::defaultTTFMap[] = {
+    {FX_CHARSET_ANSI, kDefaultAnsiFontName},
+    {FX_CHARSET_ChineseSimplified, "SimSun"},
+    {FX_CHARSET_ChineseTraditional, "MingLiU"},
+    {FX_CHARSET_ShiftJIS, "MS Gothic"},
+    {FX_CHARSET_Hangul, "Batang"},
+    {FX_CHARSET_MSWin_Cyrillic, "Arial"},
+#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ || defined(OS_MACOSX)
+    {FX_CHARSET_MSWin_EasternEuropean, "Arial"},
+#else
+    {FX_CHARSET_MSWin_EasternEuropean, "Tahoma"},
 #endif
-      m_bEmbedded(false),
-      m_bVertical(false) {
+    {FX_CHARSET_MSWin_Arabic, "Arial"},
+    {-1, nullptr}};
+
+// static
+const char CFX_Font::kUntitledFontName[] = "Untitled";
+
+// static
+const char CFX_Font::kDefaultAnsiFontName[] = "Helvetica";
+
+// static
+const char CFX_Font::kUniversalDefaultFontName[] = "Arial Unicode MS";
+
+// static
+ByteString CFX_Font::GetDefaultFontNameByCharset(uint8_t nCharset) {
+  int i = 0;
+  while (defaultTTFMap[i].charset != -1) {
+    if (nCharset == static_cast<uint8_t>(defaultTTFMap[i].charset))
+      return defaultTTFMap[i].fontname;
+    ++i;
+  }
+  return kUniversalDefaultFontName;
+}
+
+// static
+uint8_t CFX_Font::GetCharSetFromUnicode(uint16_t word) {
+  // to avoid CJK Font to show ASCII
+  if (word < 0x7F)
+    return FX_CHARSET_ANSI;
+
+  // find new charset
+  if ((word >= 0x4E00 && word <= 0x9FA5) ||
+      (word >= 0xE7C7 && word <= 0xE7F3) ||
+      (word >= 0x3000 && word <= 0x303F) ||
+      (word >= 0x2000 && word <= 0x206F)) {
+    return FX_CHARSET_ChineseSimplified;
+  }
+
+  if (((word >= 0x3040) && (word <= 0x309F)) ||
+      ((word >= 0x30A0) && (word <= 0x30FF)) ||
+      ((word >= 0x31F0) && (word <= 0x31FF)) ||
+      ((word >= 0xFF00) && (word <= 0xFFEF))) {
+    return FX_CHARSET_ShiftJIS;
+  }
+
+  if (((word >= 0xAC00) && (word <= 0xD7AF)) ||
+      ((word >= 0x1100) && (word <= 0x11FF)) ||
+      ((word >= 0x3130) && (word <= 0x318F))) {
+    return FX_CHARSET_Hangul;
+  }
+
+  if (word >= 0x0E00 && word <= 0x0E7F)
+    return FX_CHARSET_Thai;
+
+  if ((word >= 0x0370 && word <= 0x03FF) || (word >= 0x1F00 && word <= 0x1FFF))
+    return FX_CHARSET_MSWin_Greek;
+
+  if ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC))
+    return FX_CHARSET_MSWin_Arabic;
+
+  if (word >= 0x0590 && word <= 0x05FF)
+    return FX_CHARSET_MSWin_Hebrew;
+
+  if (word >= 0x0400 && word <= 0x04FF)
+    return FX_CHARSET_MSWin_Cyrillic;
+
+  if (word >= 0x0100 && word <= 0x024F)
+    return FX_CHARSET_MSWin_EasternEuropean;
+
+  if (word >= 0x1E00 && word <= 0x1EFF)
+    return FX_CHARSET_MSWin_Vietnamese;
+
+  return FX_CHARSET_ANSI;
+}
+
+CFX_Font::CFX_Font() = default;
+
+int CFX_Font::GetSubstFontItalicAngle() const {
+  CFX_SubstFont* subst_font = GetSubstFont();
+  return subst_font ? subst_font->m_ItalicAngle : 0;
 }
 
 #ifdef PDF_ENABLE_XFA
-void CFX_Font::SetFace(FXFT_Face face) {
-  ClearFaceCache();
+bool CFX_Font::LoadFile(const RetainPtr<IFX_SeekableReadStream>& pFile,
+                        int nFaceIndex) {
+  m_bEmbedded = false;
+
+  CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
+  std::unique_ptr<FXFT_StreamRec> stream;
+  m_Face = LoadFileImp(pFontMgr->GetFTLibrary(), pFile, nFaceIndex, &stream);
+  if (!m_Face)
+    return false;
+
+  m_pOwnedStream = std::move(stream);
+  FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64);
+  return true;
+}
+
+#if !defined(OS_WIN)
+void CFX_Font::SetFace(RetainPtr<CFX_Face> face) {
+  ClearGlyphCache();
   m_Face = face;
 }
 
 void CFX_Font::SetSubstFont(std::unique_ptr<CFX_SubstFont> subst) {
   m_pSubstFont = std::move(subst);
 }
+#endif  // !defined(OS_WIN)
 #endif  // PDF_ENABLE_XFA
 
 CFX_Font::~CFX_Font() {
-  if (m_Face) {
-#ifndef PDF_ENABLE_XFA
-    if (FXFT_Get_Face_External_Stream(m_Face)) {
-      FXFT_Clear_Face_External_Stream(m_Face);
-    }
-#endif  // PDF_ENABLE_XFA
-    DeleteFace();
-  }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+  m_FontData = {};  // m_FontData can't outive m_Face.
+  m_Face.Reset();
+
+#if defined(OS_MACOSX)
   ReleasePlatformResource();
 #endif
 }
 
-void CFX_Font::DeleteFace() {
-  ClearFaceCache();
-  if (m_bEmbedded)
-    FXFT_Done_Face(m_Face);
-  else
-    CFX_GEModule::Get()->GetFontMgr()->ReleaseFace(m_Face);
-  m_Face = nullptr;
-}
-
 void CFX_Font::LoadSubst(const ByteString& face_name,
                          bool bTrueType,
                          uint32_t flags,
@@ -271,145 +354,130 @@
       face_name, bTrueType, flags, weight, italic_angle, CharsetCP,
       m_pSubstFont.get());
   if (m_Face) {
-    m_pFontData = FXFT_Get_Face_Stream_Base(m_Face);
-    m_dwSize = FXFT_Get_Face_Stream_Size(m_Face);
+    m_FontData = {FXFT_Get_Face_Stream_Base(m_Face->GetRec()),
+                  FXFT_Get_Face_Stream_Size(m_Face->GetRec())};
   }
 }
 
-#ifdef PDF_ENABLE_XFA
-bool CFX_Font::LoadFile(const RetainPtr<IFX_SeekableReadStream>& pFile,
-                        int nFaceIndex) {
-  m_bEmbedded = false;
-
-  CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
-  pFontMgr->InitFTLibrary();
-
-  FXFT_Library library = pFontMgr->GetFTLibrary();
-  std::unique_ptr<FXFT_StreamRec> stream;
-  if (!LoadFileImp(library, &m_Face, pFile, nFaceIndex, &stream))
-    return false;
-
-  m_pOwnedStream = std::move(stream);
-  FXFT_Set_Pixel_Sizes(m_Face, 0, 64);
-  return true;
-}
-#endif  // PDF_ENABLE_XFA
-
-int CFX_Font::GetGlyphWidth(uint32_t glyph_index) {
+uint32_t CFX_Font::GetGlyphWidth(uint32_t glyph_index) {
   if (!m_Face)
     return 0;
   if (m_pSubstFont && m_pSubstFont->m_bFlagMM)
     AdjustMMParams(glyph_index, 0, 0);
-  int err = FXFT_Load_Glyph(
-      m_Face, glyph_index,
-      FXFT_LOAD_NO_SCALE | FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
+  int err =
+      FT_Load_Glyph(m_Face->GetRec(), glyph_index,
+                    FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
   if (err)
     return 0;
 
-  int horiAdvance = FXFT_Get_Glyph_HoriAdvance(m_Face);
-  if (horiAdvance < kThousandthMinInt || horiAdvance > kThousandthMaxInt)
+  int horiAdvance = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec());
+  if (horiAdvance < 0 || horiAdvance > kThousandthMaxInt)
     return 0;
 
-  return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face), horiAdvance);
+  return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), horiAdvance);
 }
 
-bool CFX_Font::LoadEmbedded(const uint8_t* data, uint32_t size) {
-  std::vector<uint8_t> temp(data, data + size);
-  m_pFontDataAllocation.swap(temp);
-  m_Face = FT_LoadFont(m_pFontDataAllocation.data(), size);
-  m_pFontData = m_pFontDataAllocation.data();
+bool CFX_Font::LoadEmbedded(pdfium::span<const uint8_t> src_span,
+                            bool bForceAsVertical) {
+  if (bForceAsVertical)
+    m_bVertical = true;
+  m_FontDataAllocation = std::vector<uint8_t, FxAllocAllocator<uint8_t>>(
+      src_span.begin(), src_span.end());
+  m_Face = CFX_GEModule::Get()->GetFontMgr()->NewFixedFace(
+      nullptr, m_FontDataAllocation, 0);
   m_bEmbedded = true;
-  m_dwSize = size;
+  m_FontData = m_FontDataAllocation;
   return !!m_Face;
 }
 
 bool CFX_Font::IsTTFont() const {
-  return m_Face && FXFT_Is_Face_TT_OT(m_Face) == FXFT_FACE_FLAG_SFNT;
+  return m_Face && FXFT_Is_Face_TT_OT(m_Face->GetRec()) == FT_FACE_FLAG_SFNT;
 }
 
 int CFX_Font::GetAscent() const {
   if (!m_Face)
     return 0;
 
-  int ascender = FXFT_Get_Face_Ascender(m_Face);
+  int ascender = FXFT_Get_Face_Ascender(m_Face->GetRec());
   if (ascender < kThousandthMinInt || ascender > kThousandthMaxInt)
     return 0;
 
-  return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face), ascender);
+  return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), ascender);
 }
 
 int CFX_Font::GetDescent() const {
   if (!m_Face)
     return 0;
 
-  int descender = FXFT_Get_Face_Descender(m_Face);
+  int descender = FXFT_Get_Face_Descender(m_Face->GetRec());
   if (descender < kThousandthMinInt || descender > kThousandthMaxInt)
     return 0;
 
-  return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face), descender);
+  return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), descender);
 }
 
-bool CFX_Font::GetGlyphBBox(uint32_t glyph_index, FX_RECT& bbox) {
+bool CFX_Font::GetGlyphBBox(uint32_t glyph_index, FX_RECT* pBBox) {
   if (!m_Face)
     return false;
 
-  if (FXFT_Is_Face_Tricky(m_Face)) {
-    int error = FXFT_Set_Char_Size(m_Face, 0, 1000 * 64, 72, 72);
+  if (FXFT_Is_Face_Tricky(m_Face->GetRec())) {
+    int error = FT_Set_Char_Size(m_Face->GetRec(), 0, 1000 * 64, 72, 72);
     if (error)
       return false;
 
-    error = FXFT_Load_Glyph(m_Face, glyph_index,
-                            FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
+    error = FT_Load_Glyph(m_Face->GetRec(), glyph_index,
+                          FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
     if (error)
       return false;
 
-    FXFT_BBox cbox;
+    FT_BBox cbox;
     FT_Glyph glyph;
-    error = FXFT_Get_Glyph(m_Face->glyph, &glyph);
+    error = FT_Get_Glyph(m_Face->GetRec()->glyph, &glyph);
     if (error)
       return false;
 
-    FXFT_Glyph_Get_CBox(glyph, FXFT_GLYPH_BBOX_PIXELS, &cbox);
-    int pixel_size_x = m_Face->size->metrics.x_ppem,
-        pixel_size_y = m_Face->size->metrics.y_ppem;
+    FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox);
+    int pixel_size_x = m_Face->GetRec()->size->metrics.x_ppem;
+    int pixel_size_y = m_Face->GetRec()->size->metrics.y_ppem;
     if (pixel_size_x == 0 || pixel_size_y == 0) {
-      bbox.left = cbox.xMin;
-      bbox.right = cbox.xMax;
-      bbox.top = cbox.yMax;
-      bbox.bottom = cbox.yMin;
+      pBBox->left = cbox.xMin;
+      pBBox->right = cbox.xMax;
+      pBBox->top = cbox.yMax;
+      pBBox->bottom = cbox.yMin;
     } else {
-      bbox.left = cbox.xMin * 1000 / pixel_size_x;
-      bbox.right = cbox.xMax * 1000 / pixel_size_x;
-      bbox.top = cbox.yMax * 1000 / pixel_size_y;
-      bbox.bottom = cbox.yMin * 1000 / pixel_size_y;
+      pBBox->left = cbox.xMin * 1000 / pixel_size_x;
+      pBBox->right = cbox.xMax * 1000 / pixel_size_x;
+      pBBox->top = cbox.yMax * 1000 / pixel_size_y;
+      pBBox->bottom = cbox.yMin * 1000 / pixel_size_y;
     }
-    bbox.top = std::min(bbox.top,
-                        static_cast<int32_t>(FXFT_Get_Face_Ascender(m_Face)));
-    bbox.bottom = std::max(
-        bbox.bottom, static_cast<int32_t>(FXFT_Get_Face_Descender(m_Face)));
+    pBBox->top = std::min(
+        pBBox->top,
+        static_cast<int32_t>(FXFT_Get_Face_Ascender(m_Face->GetRec())));
+    pBBox->bottom = std::max(
+        pBBox->bottom,
+        static_cast<int32_t>(FXFT_Get_Face_Descender(m_Face->GetRec())));
     FT_Done_Glyph(glyph);
-    return FXFT_Set_Pixel_Sizes(m_Face, 0, 64) == 0;
+    return FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64) == 0;
   }
-  if (FXFT_Load_Glyph(
-          m_Face, glyph_index,
-          FXFT_LOAD_NO_SCALE | FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
+  if (FT_Load_Glyph(m_Face->GetRec(), glyph_index,
+                    FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) {
     return false;
   }
-  int em = FXFT_Get_Face_UnitsPerEM(m_Face);
+  int em = FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
   if (em == 0) {
-    bbox.left = FXFT_Get_Glyph_HoriBearingX(m_Face);
-    bbox.bottom = FXFT_Get_Glyph_HoriBearingY(m_Face);
-    bbox.top = bbox.bottom - FXFT_Get_Glyph_Height(m_Face);
-    bbox.right = bbox.left + FXFT_Get_Glyph_Width(m_Face);
+    pBBox->left = FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec());
+    pBBox->bottom = FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec());
+    pBBox->top = pBBox->bottom - FXFT_Get_Glyph_Height(m_Face->GetRec());
+    pBBox->right = pBBox->left + FXFT_Get_Glyph_Width(m_Face->GetRec());
   } else {
-    bbox.left = FXFT_Get_Glyph_HoriBearingX(m_Face) * 1000 / em;
-    bbox.top =
-        (FXFT_Get_Glyph_HoriBearingY(m_Face) - FXFT_Get_Glyph_Height(m_Face)) *
-        1000 / em;
-    bbox.right =
-        (FXFT_Get_Glyph_HoriBearingX(m_Face) + FXFT_Get_Glyph_Width(m_Face)) *
-        1000 / em;
-    bbox.bottom = (FXFT_Get_Glyph_HoriBearingY(m_Face)) * 1000 / em;
+    pBBox->left = FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) * 1000 / em;
+    pBBox->top = (FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()) -
+                  FXFT_Get_Glyph_Height(m_Face->GetRec())) *
+                 1000 / em;
+    pBBox->right = (FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) +
+                    FXFT_Get_Glyph_Width(m_Face->GetRec())) *
+                   1000 / em;
+    pBBox->bottom = (FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec())) * 1000 / em;
   }
   return true;
 }
@@ -417,29 +485,29 @@
 bool CFX_Font::IsItalic() const {
   if (!m_Face)
     return false;
-  if (FXFT_Is_Face_Italic(m_Face) == FXFT_STYLE_FLAG_ITALIC)
+  if (FXFT_Is_Face_Italic(m_Face->GetRec()) == FT_STYLE_FLAG_ITALIC)
     return true;
 
-  ByteString str(FXFT_Get_Face_Style_Name(m_Face));
+  ByteString str(FXFT_Get_Face_Style_Name(m_Face->GetRec()));
   str.MakeLower();
   return str.Contains("italic");
 }
 
 bool CFX_Font::IsBold() const {
-  return m_Face && FXFT_Is_Face_Bold(m_Face) == FXFT_STYLE_FLAG_BOLD;
+  return m_Face && FXFT_Is_Face_Bold(m_Face->GetRec()) == FT_STYLE_FLAG_BOLD;
 }
 
 bool CFX_Font::IsFixedWidth() const {
-  return m_Face && FXFT_Is_Face_fixedwidth(m_Face) != 0;
+  return m_Face && FXFT_Is_Face_fixedwidth(m_Face->GetRec()) != 0;
 }
 
 ByteString CFX_Font::GetPsName() const {
   if (!m_Face)
     return ByteString();
 
-  ByteString psName = FXFT_Get_Postscript_Name(m_Face);
+  ByteString psName = FT_Get_Postscript_Name(m_Face->GetRec());
   if (psName.IsEmpty())
-    psName = "Untitled";
+    psName = kUntitledFontName;
   return psName;
 }
 
@@ -447,64 +515,81 @@
   if (!m_Face && !m_pSubstFont)
     return ByteString();
   if (m_Face)
-    return ByteString(FXFT_Get_Face_Family_Name(m_Face));
-
+    return ByteString(FXFT_Get_Face_Family_Name(m_Face->GetRec()));
   return m_pSubstFont->m_Family;
 }
 
+ByteString CFX_Font::GetFamilyNameOrUntitled() const {
+  ByteString facename = GetFamilyName();
+  return facename.IsEmpty() ? kUntitledFontName : facename;
+}
+
 ByteString CFX_Font::GetFaceName() const {
   if (!m_Face && !m_pSubstFont)
     return ByteString();
   if (m_Face) {
-    ByteString style = ByteString(FXFT_Get_Face_Style_Name(m_Face));
-    ByteString facename = GetFamilyName();
-    if (facename.IsEmpty())
-      facename = "Untitled";
-    if (!style.IsEmpty() && style != "Regular")
+    ByteString style = ByteString(FXFT_Get_Face_Style_Name(m_Face->GetRec()));
+    ByteString facename = GetFamilyNameOrUntitled();
+    if (ShouldAppendStyle(style))
       facename += " " + style;
     return facename;
   }
   return m_pSubstFont->m_Family;
 }
 
-bool CFX_Font::GetBBox(FX_RECT& bbox) {
+ByteString CFX_Font::GetBaseFontName(bool restrict_to_psname) const {
+  ByteString psname = GetPsName();
+  if (restrict_to_psname || (!psname.IsEmpty() && psname != kUntitledFontName))
+    return psname;
+  if (!m_Face && !m_pSubstFont)
+    return ByteString();
+  if (m_Face) {
+    ByteString style = ByteString(FXFT_Get_Face_Style_Name(m_Face->GetRec()));
+    ByteString facename = GetFamilyNameOrUntitled();
+    if (IsTTFont())
+      facename.Remove(' ');
+    if (ShouldAppendStyle(style))
+      facename += (IsTTFont() ? "," : " ") + style;
+    return facename;
+  }
+  return m_pSubstFont->m_Family;
+}
+
+bool CFX_Font::GetBBox(FX_RECT* pBBox) {
   if (!m_Face)
     return false;
 
-  int em = FXFT_Get_Face_UnitsPerEM(m_Face);
+  int em = FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
   if (em == 0) {
-    bbox.left = FXFT_Get_Face_xMin(m_Face);
-    bbox.bottom = FXFT_Get_Face_yMax(m_Face);
-    bbox.top = FXFT_Get_Face_yMin(m_Face);
-    bbox.right = FXFT_Get_Face_xMax(m_Face);
+    pBBox->left = FXFT_Get_Face_xMin(m_Face->GetRec());
+    pBBox->bottom = FXFT_Get_Face_yMax(m_Face->GetRec());
+    pBBox->top = FXFT_Get_Face_yMin(m_Face->GetRec());
+    pBBox->right = FXFT_Get_Face_xMax(m_Face->GetRec());
   } else {
-    bbox.left = FXFT_Get_Face_xMin(m_Face) * 1000 / em;
-    bbox.top = FXFT_Get_Face_yMin(m_Face) * 1000 / em;
-    bbox.right = FXFT_Get_Face_xMax(m_Face) * 1000 / em;
-    bbox.bottom = FXFT_Get_Face_yMax(m_Face) * 1000 / em;
+    pBBox->left = FXFT_Get_Face_xMin(m_Face->GetRec()) * 1000 / em;
+    pBBox->top = FXFT_Get_Face_yMin(m_Face->GetRec()) * 1000 / em;
+    pBBox->right = FXFT_Get_Face_xMax(m_Face->GetRec()) * 1000 / em;
+    pBBox->bottom = FXFT_Get_Face_yMax(m_Face->GetRec()) * 1000 / em;
   }
   return true;
 }
 
-CFX_FaceCache* CFX_Font::GetFaceCache() const {
-  if (!m_FaceCache)
-    m_FaceCache = CFX_GEModule::Get()->GetFontCache()->GetCachedFace(this);
-  return m_FaceCache.Get();
+RetainPtr<CFX_GlyphCache> CFX_Font::GetOrCreateGlyphCache() const {
+  if (!m_GlyphCache)
+    m_GlyphCache = CFX_GEModule::Get()->GetFontCache()->GetGlyphCache(this);
+  return m_GlyphCache;
 }
 
-void CFX_Font::ClearFaceCache() {
-  if (!m_FaceCache)
-    return;
-
-  m_FaceCache = nullptr;
-  CFX_GEModule::Get()->GetFontCache()->ReleaseCachedFace(this);
+void CFX_Font::ClearGlyphCache() {
+  m_GlyphCache = nullptr;
 }
 
 void CFX_Font::AdjustMMParams(int glyph_index,
                               int dest_width,
                               int weight) const {
-  FXFT_MM_Var pMasters = nullptr;
-  FXFT_Get_MM_Var(m_Face, &pMasters);
+  ASSERT(dest_width >= 0);
+  FXFT_MM_VarPtr pMasters = nullptr;
+  FT_Get_MM_Var(m_Face->GetRec(), &pMasters);
   if (!pMasters)
     return;
 
@@ -520,35 +605,36 @@
     int min_param = FXFT_Get_MM_Axis_Min(FXFT_Get_MM_Axis(pMasters, 1)) / 65536;
     int max_param = FXFT_Get_MM_Axis_Max(FXFT_Get_MM_Axis(pMasters, 1)) / 65536;
     coords[1] = min_param;
-    FXFT_Set_MM_Design_Coordinates(m_Face, 2, coords);
-    FXFT_Load_Glyph(m_Face, glyph_index,
-                    FXFT_LOAD_NO_SCALE | FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
-    int min_width = FXFT_Get_Glyph_HoriAdvance(m_Face) * 1000 /
-                    FXFT_Get_Face_UnitsPerEM(m_Face);
+    FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords);
+    FT_Load_Glyph(m_Face->GetRec(), glyph_index,
+                  FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
+    int min_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 /
+                    FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
     coords[1] = max_param;
-    FXFT_Set_MM_Design_Coordinates(m_Face, 2, coords);
-    FXFT_Load_Glyph(m_Face, glyph_index,
-                    FXFT_LOAD_NO_SCALE | FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
-    int max_width = FXFT_Get_Glyph_HoriAdvance(m_Face) * 1000 /
-                    FXFT_Get_Face_UnitsPerEM(m_Face);
+    FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords);
+    FT_Load_Glyph(m_Face->GetRec(), glyph_index,
+                  FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
+    int max_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 /
+                    FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
     if (max_width == min_width) {
-      FXFT_Free(m_Face, pMasters);
+      FXFT_Free(m_Face->GetRec(), pMasters);
       return;
     }
     int param = min_param + (max_param - min_param) * (dest_width - min_width) /
                                 (max_width - min_width);
     coords[1] = param;
   }
-  FXFT_Free(m_Face, pMasters);
-  FXFT_Set_MM_Design_Coordinates(m_Face, 2, coords);
+  FXFT_Free(m_Face->GetRec(), pMasters);
+  FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords);
 }
 
 CFX_PathData* CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index,
-                                          int dest_width) const {
+                                          uint32_t dest_width) const {
   if (!m_Face)
     return nullptr;
-  FXFT_Set_Pixel_Sizes(m_Face, 0, 64);
-  FXFT_Matrix ft_matrix = {65536, 0, 0, 65536};
+
+  FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64);
+  FT_Matrix ft_matrix = {65536, 0, 0, 65536};
   if (m_pSubstFont) {
     if (m_pSubstFont->m_ItalicAngle) {
       int skew = m_pSubstFont->m_ItalicAngle;
@@ -569,10 +655,11 @@
       AdjustMMParams(glyph_index, dest_width, m_pSubstFont->m_Weight);
   }
   ScopedFontTransform scoped_transform(m_Face, &ft_matrix);
-  int load_flags = FXFT_LOAD_NO_BITMAP;
-  if (!(m_Face->face_flags & FT_FACE_FLAG_SFNT) || !FT_IS_TRICKY(m_Face))
+  int load_flags = FT_LOAD_NO_BITMAP;
+  if (!(m_Face->GetRec()->face_flags & FT_FACE_FLAG_SFNT) ||
+      !FT_IS_TRICKY(m_Face->GetRec()))
     load_flags |= FT_LOAD_NO_HINTING;
-  if (FXFT_Load_Glyph(m_Face, glyph_index, load_flags))
+  if (FT_Load_Glyph(m_Face->GetRec(), glyph_index, load_flags))
     return nullptr;
   if (m_pSubstFont && !m_pSubstFont->m_bFlagMM &&
       m_pSubstFont->m_Weight > 400) {
@@ -583,10 +670,10 @@
       level = s_WeightPow_SHIFTJIS[index] * 2 * 65536 / 36655;
     else
       level = s_WeightPow[index] * 2;
-    FXFT_Outline_Embolden(FXFT_Get_Glyph_Outline(m_Face), level);
+    FT_Outline_Embolden(FXFT_Get_Glyph_Outline(m_Face->GetRec()), level);
   }
 
-  FXFT_Outline_Funcs funcs;
+  FT_Outline_Funcs funcs;
   funcs.move_to = Outline_MoveTo;
   funcs.line_to = Outline_LineTo;
   funcs.conic_to = Outline_ConicTo;
@@ -600,7 +687,8 @@
   params.m_CurX = params.m_CurY = 0;
   params.m_CoordUnit = 64 * 64.0;
 
-  FXFT_Outline_Decompose(FXFT_Get_Glyph_Outline(m_Face), &funcs, &params);
+  FT_Outline_Decompose(FXFT_Get_Glyph_Outline(m_Face->GetRec()), &funcs,
+                       &params);
   if (pPath->GetPoints().empty())
     return nullptr;
 
@@ -612,21 +700,22 @@
 
 const CFX_GlyphBitmap* CFX_Font::LoadGlyphBitmap(uint32_t glyph_index,
                                                  bool bFontStyle,
-                                                 const CFX_Matrix* pMatrix,
-                                                 int dest_width,
+                                                 const CFX_Matrix& matrix,
+                                                 uint32_t dest_width,
                                                  int anti_alias,
-                                                 int& text_flags) const {
-  return GetFaceCache()->LoadGlyphBitmap(this, glyph_index, bFontStyle, pMatrix,
-                                         dest_width, anti_alias, text_flags);
+                                                 int* pTextFlags) const {
+  return GetOrCreateGlyphCache()->LoadGlyphBitmap(this, glyph_index, bFontStyle,
+                                                  matrix, dest_width,
+                                                  anti_alias, pTextFlags);
 }
 
 const CFX_PathData* CFX_Font::LoadGlyphPath(uint32_t glyph_index,
-                                            int dest_width) const {
-  return GetFaceCache()->LoadGlyphPath(this, glyph_index, dest_width);
+                                            uint32_t dest_width) const {
+  return GetOrCreateGlyphCache()->LoadGlyphPath(this, glyph_index, dest_width);
 }
 
 #if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
 CFX_TypeFace* CFX_Font::GetDeviceCache() const {
-  return GetFaceCache()->GetDeviceCache(this);
+  return GetOrCreateGlyphCache()->GetDeviceCache(this);
 }
 #endif
diff --git a/core/fxge/cfx_font.h b/core/fxge/cfx_font.h
index c8c4cf7..ead60f7 100644
--- a/core/fxge/cfx_font.h
+++ b/core/fxge/cfx_font.h
@@ -10,16 +10,19 @@
 #include <memory>
 #include <vector>
 
+#include "build/build_config.h"
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxge/cfx_face.h"
 #include "core/fxge/fx_freetype.h"
+#include "third_party/base/span.h"
 
 #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
 #include "core/fxge/fx_font.h"
 #endif
 
-class CFX_FaceCache;
+class CFX_GlyphCache;
 class CFX_GlyphBitmap;
 class CFX_PathData;
 class CFX_SubstFont;
@@ -30,6 +33,14 @@
   CFX_Font();
   ~CFX_Font();
 
+  // Used when the font name is empty.
+  static const char kUntitledFontName[];
+
+  static const char kDefaultAnsiFontName[];
+  static const char kUniversalDefaultFontName[];
+  static ByteString GetDefaultFontNameByCharset(uint8_t nCharset);
+  static uint8_t GetCharSetFromUnicode(uint16_t word);
+
   void LoadSubst(const ByteString& face_name,
                  bool bTrueType,
                  uint32_t flags,
@@ -38,33 +49,42 @@
                  int CharsetCP,
                  bool bVertical);
 
-  bool LoadEmbedded(const uint8_t* data, uint32_t size);
-  FXFT_Face GetFace() const { return m_Face; }
+  bool LoadEmbedded(pdfium::span<const uint8_t> src_span,
+                    bool bForceAsVertical);
+  RetainPtr<CFX_Face> GetFace() const { return m_Face; }
+  FXFT_FaceRec* GetFaceRec() const {
+    return m_Face ? m_Face->GetRec() : nullptr;
+  }
   CFX_SubstFont* GetSubstFont() const { return m_pSubstFont.get(); }
+  int GetSubstFontItalicAngle() const;
 
-#ifdef PDF_ENABLE_XFA
+#if defined(PDF_ENABLE_XFA)
   bool LoadFile(const RetainPtr<IFX_SeekableReadStream>& pFile, int nFaceIndex);
 
-  void SetFace(FXFT_Face face);
+#if !defined(OS_WIN)
+  void SetFace(RetainPtr<CFX_Face> face);
+  void SetFontSpan(pdfium::span<uint8_t> pSpan) { m_FontData = pSpan; }
   void SetSubstFont(std::unique_ptr<CFX_SubstFont> subst);
-#endif  // PDF_ENABLE_XFA
+#endif  // !defined(OS_WIN)
+#endif  // defined(PDF_ENABLE_XFA)
 
   const CFX_GlyphBitmap* LoadGlyphBitmap(uint32_t glyph_index,
                                          bool bFontStyle,
-                                         const CFX_Matrix* pMatrix,
-                                         int dest_width,
+                                         const CFX_Matrix& matrix,
+                                         uint32_t dest_width,
                                          int anti_alias,
-                                         int& text_flags) const;
-  const CFX_PathData* LoadGlyphPath(uint32_t glyph_index, int dest_width) const;
+                                         int* pTextFlags) const;
+  const CFX_PathData* LoadGlyphPath(uint32_t glyph_index,
+                                    uint32_t dest_width) const;
 
 #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
   CFX_TypeFace* GetDeviceCache() const;
 #endif
 
-  int GetGlyphWidth(uint32_t glyph_index);
+  uint32_t GetGlyphWidth(uint32_t glyph_index);
   int GetAscent() const;
   int GetDescent() const;
-  bool GetGlyphBBox(uint32_t glyph_index, FX_RECT& bbox);
+  bool GetGlyphBBox(uint32_t glyph_index, FX_RECT* pBBox);
   bool IsItalic() const;
   bool IsBold() const;
   bool IsFixedWidth() const;
@@ -72,20 +92,20 @@
   ByteString GetPsName() const;
   ByteString GetFamilyName() const;
   ByteString GetFaceName() const;
+  ByteString GetBaseFontName(bool restrict_to_psname) const;
   bool IsTTFont() const;
-  bool GetBBox(FX_RECT& bbox);
+  bool GetBBox(FX_RECT* pBBox);
   bool IsEmbedded() const { return m_bEmbedded; }
   uint8_t* GetSubData() const { return m_pGsubData.get(); }
   void SetSubData(uint8_t* data) { m_pGsubData.reset(data); }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+  pdfium::span<uint8_t> GetFontSpan() const { return m_FontData; }
+  void AdjustMMParams(int glyph_index, int dest_width, int weight) const;
+  CFX_PathData* LoadGlyphPathImpl(uint32_t glyph_index,
+                                  uint32_t dest_width) const;
+#if defined(OS_MACOSX)
   void* GetPlatformFont() const { return m_pPlatformFont; }
   void SetPlatformFont(void* font) { m_pPlatformFont = font; }
 #endif
-  uint8_t* GetFontData() const { return m_pFontData; }
-  uint32_t GetSize() const { return m_dwSize; }
-  void AdjustMMParams(int glyph_index, int width, int weight) const;
-
-  CFX_PathData* LoadGlyphPathImpl(uint32_t glyph_index, int dest_width) const;
 
   static const size_t kAngleSkewArraySize = 30;
   static const char s_AngleSkew[kAngleSkewArraySize];
@@ -94,31 +114,41 @@
   static const uint8_t s_WeightPow_11[kWeightPowArraySize];
   static const uint8_t s_WeightPow_SHIFTJIS[kWeightPowArraySize];
 
-#ifdef PDF_ENABLE_XFA
- protected:
-  std::unique_ptr<FXFT_StreamRec> m_pOwnedStream;
-#endif  // PDF_ENABLE_XFA
+  // This struct should be the same as FPDF_CharsetFontMap.
+  struct CharsetFontMap {
+    int charset;           // Character Set Enum value, see FX_CHARSET_XXX.
+    const char* fontname;  // Name of default font to use with that charset.
+  };
+
+  /**
+   *    Pointer to the default character set to TT Font name map. The
+   *    map is an array of CharsetFontMap structs, with its end indicated
+   *    by a { -1, NULL } entry.
+   **/
+  static const CharsetFontMap defaultTTFMap[];
 
  private:
-  CFX_FaceCache* GetFaceCache() const;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+  RetainPtr<CFX_GlyphCache> GetOrCreateGlyphCache() const;
+  void ClearGlyphCache();
+#if defined(OS_MACOSX)
   void ReleasePlatformResource();
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  void DeleteFace();
-  void ClearFaceCache();
-
-  FXFT_Face m_Face;
-  mutable UnownedPtr<CFX_FaceCache> m_FaceCache;
-  std::unique_ptr<CFX_SubstFont> m_pSubstFont;
-  std::vector<uint8_t> m_pFontDataAllocation;
-  uint8_t* m_pFontData;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pGsubData;
-  uint32_t m_dwSize;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  void* m_pPlatformFont;
 #endif
-  bool m_bEmbedded;
-  bool m_bVertical;
+  ByteString GetFamilyNameOrUntitled() const;
+
+#if defined(PDF_ENABLE_XFA)
+  std::unique_ptr<FXFT_StreamRec> m_pOwnedStream;  // Must outlive |m_Face|.
+#endif
+  mutable RetainPtr<CFX_Face> m_Face;
+  mutable RetainPtr<CFX_GlyphCache> m_GlyphCache;
+  std::unique_ptr<CFX_SubstFont> m_pSubstFont;
+  std::unique_ptr<uint8_t, FxFreeDeleter> m_pGsubData;
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> m_FontDataAllocation;
+  pdfium::span<uint8_t> m_FontData;
+  bool m_bEmbedded = false;
+  bool m_bVertical = false;
+#if defined(OS_MACOSX)
+  void* m_pPlatformFont = nullptr;
+#endif
 };
 
 #endif  // CORE_FXGE_CFX_FONT_H_
diff --git a/core/fxge/cfx_fontcache.cpp b/core/fxge/cfx_fontcache.cpp
index 5ccac8c..7289026 100644
--- a/core/fxge/cfx_fontcache.cpp
+++ b/core/fxge/cfx_fontcache.cpp
@@ -9,62 +9,31 @@
 #include <memory>
 #include <utility>
 
-#include "core/fxge/cfx_facecache.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_glyphcache.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/fx_freetype.h"
 #include "third_party/base/ptr_util.h"
 
-CFX_FontCache::CountedFaceCache::CountedFaceCache() {}
+CFX_FontCache::CFX_FontCache() = default;
 
-CFX_FontCache::CountedFaceCache::~CountedFaceCache() {}
+CFX_FontCache::~CFX_FontCache() = default;
 
-CFX_FontCache::CFX_FontCache() {}
-
-CFX_FontCache::~CFX_FontCache() {
-  ASSERT(m_ExtFaceMap.empty());
-  ASSERT(m_FTFaceMap.empty());
-}
-
-CFX_FaceCache* CFX_FontCache::GetCachedFace(const CFX_Font* pFont) {
-  FXFT_Face face = pFont->GetFace();
+RetainPtr<CFX_GlyphCache> CFX_FontCache::GetGlyphCache(const CFX_Font* pFont) {
+  RetainPtr<CFX_Face> face = pFont->GetFace();
   const bool bExternal = !face;
-  CFX_FTCacheMap& map = bExternal ? m_ExtFaceMap : m_FTFaceMap;
-  auto it = map.find(face);
-  if (it != map.end()) {
-    CountedFaceCache* counted_face_cache = it->second.get();
-    counted_face_cache->m_nCount++;
-    return counted_face_cache->m_Obj.get();
-  }
+  auto& map = bExternal ? m_ExtGlyphCacheMap : m_GlyphCacheMap;
+  auto it = map.find(face.Get());
+  if (it != map.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
 
-  auto counted_face_cache = pdfium::MakeUnique<CountedFaceCache>();
-  counted_face_cache->m_nCount = 2;
-  auto new_cache =
-      pdfium::MakeUnique<CFX_FaceCache>(bExternal ? nullptr : face);
-  CFX_FaceCache* face_cache = new_cache.get();
-  counted_face_cache->m_Obj = std::move(new_cache);
-  map[face] = std::move(counted_face_cache);
-  return face_cache;
+  auto new_cache = pdfium::MakeRetain<CFX_GlyphCache>(face);
+  map[face.Get()].Reset(new_cache.Get());
+  return new_cache;
 }
 
 #ifdef _SKIA_SUPPORT_
 CFX_TypeFace* CFX_FontCache::GetDeviceCache(const CFX_Font* pFont) {
-  return GetCachedFace(pFont)->GetDeviceCache(pFont);
+  return GetGlyphCache(pFont)->GetDeviceCache(pFont);
 }
 #endif
-
-void CFX_FontCache::ReleaseCachedFace(const CFX_Font* pFont) {
-  FXFT_Face face = pFont->GetFace();
-  const bool bExternal = !face;
-  CFX_FTCacheMap& map = bExternal ? m_ExtFaceMap : m_FTFaceMap;
-
-  auto it = map.find(face);
-  if (it == map.end())
-    return;
-
-  CountedFaceCache* counted_face_cache = it->second.get();
-  if (counted_face_cache->m_nCount > 2) {
-    counted_face_cache->m_nCount--;
-  } else {
-    map.erase(it);
-  }
-}
diff --git a/core/fxge/cfx_fontcache.h b/core/fxge/cfx_fontcache.h
index a14ed09..515811b 100644
--- a/core/fxge/cfx_fontcache.h
+++ b/core/fxge/cfx_fontcache.h
@@ -11,33 +11,24 @@
 #include <memory>
 
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/fx_font.h"
+#include "core/fxge/cfx_glyphcache.h"
 #include "core/fxge/fx_freetype.h"
 
-class CFX_FaceCache;
+class CFX_Font;
 
 class CFX_FontCache {
  public:
   CFX_FontCache();
   ~CFX_FontCache();
-  CFX_FaceCache* GetCachedFace(const CFX_Font* pFont);
-  void ReleaseCachedFace(const CFX_Font* pFont);
+
+  RetainPtr<CFX_GlyphCache> GetGlyphCache(const CFX_Font* pFont);
 #ifdef _SKIA_SUPPORT_
   CFX_TypeFace* GetDeviceCache(const CFX_Font* pFont);
 #endif
 
  private:
-  struct CountedFaceCache {
-    CountedFaceCache();
-    ~CountedFaceCache();
-    std::unique_ptr<CFX_FaceCache> m_Obj;
-    uint32_t m_nCount;
-  };
-
-  using CFX_FTCacheMap = std::map<FXFT_Face, std::unique_ptr<CountedFaceCache>>;
-  CFX_FTCacheMap m_FTFaceMap;
-  CFX_FTCacheMap m_ExtFaceMap;
+  std::map<CFX_Face*, ObservedPtr<CFX_GlyphCache>> m_GlyphCacheMap;
+  std::map<CFX_Face*, ObservedPtr<CFX_GlyphCache>> m_ExtGlyphCacheMap;
 };
 
 #endif  // CORE_FXGE_CFX_FONTCACHE_H_
diff --git a/core/fxge/cfx_fontmapper.cpp b/core/fxge/cfx_fontmapper.cpp
index 7dba8f6..3b0bcae 100644
--- a/core/fxge/cfx_fontmapper.cpp
+++ b/core/fxge/cfx_fontmapper.cpp
@@ -13,16 +13,20 @@
 #include <utility>
 #include <vector>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_substfont.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "core/fxge/systemfontinfo_iface.h"
 #include "third_party/base/stl_util.h"
 
 namespace {
 
 const int kNumStandardFonts = 14;
+static_assert(CFX_FontMapper::kDingbats + 1 == kNumStandardFonts,
+              "StandardFont enum count mismatch");
 
 const char* const g_Base14FontNames[kNumStandardFonts] = {
     "Courier",
@@ -41,122 +45,121 @@
     "ZapfDingbats",
 };
 
-const struct AltFontName {
-  const char* m_pName;
-  int m_Index;
-} g_AltFontNames[] = {
-    {"Arial", 4},
-    {"Arial,Bold", 5},
-    {"Arial,BoldItalic", 6},
-    {"Arial,Italic", 7},
-    {"Arial-Bold", 5},
-    {"Arial-BoldItalic", 6},
-    {"Arial-BoldItalicMT", 6},
-    {"Arial-BoldMT", 5},
-    {"Arial-Italic", 7},
-    {"Arial-ItalicMT", 7},
-    {"ArialBold", 5},
-    {"ArialBoldItalic", 6},
-    {"ArialItalic", 7},
-    {"ArialMT", 4},
-    {"ArialMT,Bold", 5},
-    {"ArialMT,BoldItalic", 6},
-    {"ArialMT,Italic", 7},
-    {"ArialRoundedMTBold", 5},
-    {"Courier", 0},
-    {"Courier,Bold", 1},
-    {"Courier,BoldItalic", 2},
-    {"Courier,Italic", 3},
-    {"Courier-Bold", 1},
-    {"Courier-BoldOblique", 2},
-    {"Courier-Oblique", 3},
-    {"CourierBold", 1},
-    {"CourierBoldItalic", 2},
-    {"CourierItalic", 3},
-    {"CourierNew", 0},
-    {"CourierNew,Bold", 1},
-    {"CourierNew,BoldItalic", 2},
-    {"CourierNew,Italic", 3},
-    {"CourierNew-Bold", 1},
-    {"CourierNew-BoldItalic", 2},
-    {"CourierNew-Italic", 3},
-    {"CourierNewBold", 1},
-    {"CourierNewBoldItalic", 2},
-    {"CourierNewItalic", 3},
-    {"CourierNewPS-BoldItalicMT", 2},
-    {"CourierNewPS-BoldMT", 1},
-    {"CourierNewPS-ItalicMT", 3},
-    {"CourierNewPSMT", 0},
-    {"CourierStd", 0},
-    {"CourierStd-Bold", 1},
-    {"CourierStd-BoldOblique", 2},
-    {"CourierStd-Oblique", 3},
-    {"Helvetica", 4},
-    {"Helvetica,Bold", 5},
-    {"Helvetica,BoldItalic", 6},
-    {"Helvetica,Italic", 7},
-    {"Helvetica-Bold", 5},
-    {"Helvetica-BoldItalic", 6},
-    {"Helvetica-BoldOblique", 6},
-    {"Helvetica-Italic", 7},
-    {"Helvetica-Oblique", 7},
-    {"HelveticaBold", 5},
-    {"HelveticaBoldItalic", 6},
-    {"HelveticaItalic", 7},
-    {"Symbol", 12},
-    {"SymbolMT", 12},
-    {"Times-Bold", 9},
-    {"Times-BoldItalic", 10},
-    {"Times-Italic", 11},
-    {"Times-Roman", 8},
-    {"TimesBold", 9},
-    {"TimesBoldItalic", 10},
-    {"TimesItalic", 11},
-    {"TimesNewRoman", 8},
-    {"TimesNewRoman,Bold", 9},
-    {"TimesNewRoman,BoldItalic", 10},
-    {"TimesNewRoman,Italic", 11},
-    {"TimesNewRoman-Bold", 9},
-    {"TimesNewRoman-BoldItalic", 10},
-    {"TimesNewRoman-Italic", 11},
-    {"TimesNewRomanBold", 9},
-    {"TimesNewRomanBoldItalic", 10},
-    {"TimesNewRomanItalic", 11},
-    {"TimesNewRomanPS", 8},
-    {"TimesNewRomanPS-Bold", 9},
-    {"TimesNewRomanPS-BoldItalic", 10},
-    {"TimesNewRomanPS-BoldItalicMT", 10},
-    {"TimesNewRomanPS-BoldMT", 9},
-    {"TimesNewRomanPS-Italic", 11},
-    {"TimesNewRomanPS-ItalicMT", 11},
-    {"TimesNewRomanPSMT", 8},
-    {"TimesNewRomanPSMT,Bold", 9},
-    {"TimesNewRomanPSMT,BoldItalic", 10},
-    {"TimesNewRomanPSMT,Italic", 11},
-    {"ZapfDingbats", 13},
+struct AltFontName {
+  const char* m_pName;  // Raw, POD struct.
+  CFX_FontMapper::StandardFont m_Index;
 };
 
-const struct AltFontFamily {
-  const char* m_pFontName;
-  const char* m_pFontFamily;
-} g_AltFontFamilies[] = {
+const AltFontName g_AltFontNames[] = {
+    {"Arial", CFX_FontMapper::kHelvetica},
+    {"Arial,Bold", CFX_FontMapper::kHelveticaBold},
+    {"Arial,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
+    {"Arial,Italic", CFX_FontMapper::kHelveticaOblique},
+    {"Arial-Bold", CFX_FontMapper::kHelveticaBold},
+    {"Arial-BoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
+    {"Arial-BoldItalicMT", CFX_FontMapper::kHelveticaBoldOblique},
+    {"Arial-BoldMT", CFX_FontMapper::kHelveticaBold},
+    {"Arial-Italic", CFX_FontMapper::kHelveticaOblique},
+    {"Arial-ItalicMT", CFX_FontMapper::kHelveticaOblique},
+    {"ArialBold", CFX_FontMapper::kHelveticaBold},
+    {"ArialBoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
+    {"ArialItalic", CFX_FontMapper::kHelveticaOblique},
+    {"ArialMT", CFX_FontMapper::kHelvetica},
+    {"ArialMT,Bold", CFX_FontMapper::kHelveticaBold},
+    {"ArialMT,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
+    {"ArialMT,Italic", CFX_FontMapper::kHelveticaOblique},
+    {"ArialRoundedMTBold", CFX_FontMapper::kHelveticaBold},
+    {"Courier", CFX_FontMapper::kCourier},
+    {"Courier,Bold", CFX_FontMapper::kCourierBold},
+    {"Courier,BoldItalic", CFX_FontMapper::kCourierBoldOblique},
+    {"Courier,Italic", CFX_FontMapper::kCourierOblique},
+    {"Courier-Bold", CFX_FontMapper::kCourierBold},
+    {"Courier-BoldOblique", CFX_FontMapper::kCourierBoldOblique},
+    {"Courier-Oblique", CFX_FontMapper::kCourierOblique},
+    {"CourierBold", CFX_FontMapper::kCourierBold},
+    {"CourierBoldItalic", CFX_FontMapper::kCourierBoldOblique},
+    {"CourierItalic", CFX_FontMapper::kCourierOblique},
+    {"CourierNew", CFX_FontMapper::kCourier},
+    {"CourierNew,Bold", CFX_FontMapper::kCourierBold},
+    {"CourierNew,BoldItalic", CFX_FontMapper::kCourierBoldOblique},
+    {"CourierNew,Italic", CFX_FontMapper::kCourierOblique},
+    {"CourierNew-Bold", CFX_FontMapper::kCourierBold},
+    {"CourierNew-BoldItalic", CFX_FontMapper::kCourierBoldOblique},
+    {"CourierNew-Italic", CFX_FontMapper::kCourierOblique},
+    {"CourierNewBold", CFX_FontMapper::kCourierBold},
+    {"CourierNewBoldItalic", CFX_FontMapper::kCourierBoldOblique},
+    {"CourierNewItalic", CFX_FontMapper::kCourierOblique},
+    {"CourierNewPS-BoldItalicMT", CFX_FontMapper::kCourierBoldOblique},
+    {"CourierNewPS-BoldMT", CFX_FontMapper::kCourierBold},
+    {"CourierNewPS-ItalicMT", CFX_FontMapper::kCourierOblique},
+    {"CourierNewPSMT", CFX_FontMapper::kCourier},
+    {"CourierStd", CFX_FontMapper::kCourier},
+    {"CourierStd-Bold", CFX_FontMapper::kCourierBold},
+    {"CourierStd-BoldOblique", CFX_FontMapper::kCourierBoldOblique},
+    {"CourierStd-Oblique", CFX_FontMapper::kCourierOblique},
+    {"Helvetica", CFX_FontMapper::kHelvetica},
+    {"Helvetica,Bold", CFX_FontMapper::kHelveticaBold},
+    {"Helvetica,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
+    {"Helvetica,Italic", CFX_FontMapper::kHelveticaOblique},
+    {"Helvetica-Bold", CFX_FontMapper::kHelveticaBold},
+    {"Helvetica-BoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
+    {"Helvetica-BoldOblique", CFX_FontMapper::kHelveticaBoldOblique},
+    {"Helvetica-Italic", CFX_FontMapper::kHelveticaOblique},
+    {"Helvetica-Oblique", CFX_FontMapper::kHelveticaOblique},
+    {"HelveticaBold", CFX_FontMapper::kHelveticaBold},
+    {"HelveticaBoldItalic", CFX_FontMapper::kHelveticaBoldOblique},
+    {"HelveticaItalic", CFX_FontMapper::kHelveticaOblique},
+    {"Symbol", CFX_FontMapper::kSymbol},
+    {"SymbolMT", CFX_FontMapper::kSymbol},
+    {"Times-Bold", CFX_FontMapper::kTimesBold},
+    {"Times-BoldItalic", CFX_FontMapper::kTimesBoldOblique},
+    {"Times-Italic", CFX_FontMapper::kTimesOblique},
+    {"Times-Roman", CFX_FontMapper::kTimes},
+    {"TimesBold", CFX_FontMapper::kTimesBold},
+    {"TimesBoldItalic", CFX_FontMapper::kTimesBoldOblique},
+    {"TimesItalic", CFX_FontMapper::kTimesOblique},
+    {"TimesNewRoman", CFX_FontMapper::kTimes},
+    {"TimesNewRoman,Bold", CFX_FontMapper::kTimesBold},
+    {"TimesNewRoman,BoldItalic", CFX_FontMapper::kTimesBoldOblique},
+    {"TimesNewRoman,Italic", CFX_FontMapper::kTimesOblique},
+    {"TimesNewRoman-Bold", CFX_FontMapper::kTimesBold},
+    {"TimesNewRoman-BoldItalic", CFX_FontMapper::kTimesBoldOblique},
+    {"TimesNewRoman-Italic", CFX_FontMapper::kTimesOblique},
+    {"TimesNewRomanBold", CFX_FontMapper::kTimesBold},
+    {"TimesNewRomanBoldItalic", CFX_FontMapper::kTimesBoldOblique},
+    {"TimesNewRomanItalic", CFX_FontMapper::kTimesOblique},
+    {"TimesNewRomanPS", CFX_FontMapper::kTimes},
+    {"TimesNewRomanPS-Bold", CFX_FontMapper::kTimesBold},
+    {"TimesNewRomanPS-BoldItalic", CFX_FontMapper::kTimesBoldOblique},
+    {"TimesNewRomanPS-BoldItalicMT", CFX_FontMapper::kTimesBoldOblique},
+    {"TimesNewRomanPS-BoldMT", CFX_FontMapper::kTimesBold},
+    {"TimesNewRomanPS-Italic", CFX_FontMapper::kTimesOblique},
+    {"TimesNewRomanPS-ItalicMT", CFX_FontMapper::kTimesOblique},
+    {"TimesNewRomanPSMT", CFX_FontMapper::kTimes},
+    {"TimesNewRomanPSMT,Bold", CFX_FontMapper::kTimesBold},
+    {"TimesNewRomanPSMT,BoldItalic", CFX_FontMapper::kTimesBoldOblique},
+    {"TimesNewRomanPSMT,Italic", CFX_FontMapper::kTimesOblique},
+    {"ZapfDingbats", CFX_FontMapper::kDingbats},
+};
+
+struct AltFontFamily {
+  const char* m_pFontName;    // Raw, POD struct.
+  const char* m_pFontFamily;  // Raw, POD struct.
+};
+
+const AltFontFamily g_AltFontFamilies[] = {
     {"AGaramondPro", "Adobe Garamond Pro"},
     {"BankGothicBT-Medium", "BankGothic Md BT"},
     {"ForteMT", "Forte"},
 };
 
-const struct CODEPAGE_MAP {
-  uint16_t codepage;
-  uint8_t charset;
-} g_Codepage2CharsetTable[] = {
-    {0, 1},      {42, 2},     {437, 254},  {850, 255},  {874, 222},
-    {932, 128},  {936, 134},  {949, 129},  {950, 136},  {1250, 238},
-    {1251, 204}, {1252, 0},   {1253, 161}, {1254, 162}, {1255, 177},
-    {1256, 178}, {1257, 186}, {1258, 163}, {1361, 130}, {10000, 77},
-    {10001, 78}, {10002, 81}, {10003, 79}, {10004, 84}, {10005, 83},
-    {10006, 85}, {10007, 89}, {10008, 80}, {10021, 87}, {10029, 88},
-    {10081, 86},
-};
+#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+const char kNarrowFamily[] = "LiberationSansNarrow";
+#elif defined(OS_ANDROID)
+const char kNarrowFamily[] = "RobotoCondensed";
+#else
+const char kNarrowFamily[] = "ArialNarrow";
+#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
 
 ByteString TT_NormalizeName(const char* family) {
   ByteString norm(family);
@@ -165,27 +168,14 @@
   norm.Remove(',');
   auto pos = norm.Find('+');
   if (pos.has_value() && pos.value() != 0)
-    norm = norm.Left(pos.value());
+    norm = norm.First(pos.value());
   norm.MakeLower();
   return norm;
 }
 
-uint8_t GetCharsetFromCodePage(uint16_t codepage) {
-  const CODEPAGE_MAP* pEnd =
-      g_Codepage2CharsetTable + FX_ArraySize(g_Codepage2CharsetTable);
-  const CODEPAGE_MAP* pCharmap =
-      std::lower_bound(g_Codepage2CharsetTable, pEnd, codepage,
-                       [](const CODEPAGE_MAP& charset, uint16_t page) {
-                         return charset.codepage < page;
-                       });
-  if (pCharmap < pEnd && codepage == pCharmap->codepage)
-    return pCharmap->charset;
-  return FX_CHARSET_Default;
-}
-
 void GetFontFamily(uint32_t nStyle, ByteString* fontName) {
   if (fontName->Contains("Script")) {
-    if (FontStyleIsBold(nStyle))
+    if (FontStyleIsForceBold(nStyle))
       *fontName = "ScriptMTBold";
     else if (fontName->Contains("Palace"))
       *fontName = "PalaceScriptMT";
@@ -221,9 +211,9 @@
   size_t len;
   uint32_t style;
 } g_FontStyles[] = {
-    {"Bold", 4, FXFONT_BOLD},
+    {"Bold", 4, FXFONT_FORCE_BOLD},
     {"Italic", 6, FXFONT_ITALIC},
-    {"BoldItalic", 10, FXFONT_BOLD | FXFONT_ITALIC},
+    {"BoldItalic", 10, FXFONT_FORCE_BOLD | FXFONT_ITALIC},
     {"Reg", 3, FXFONT_NORMAL},
     {"Regular", 7, FXFONT_NORMAL},
 };
@@ -240,10 +230,10 @@
       continue;
 
     if (bReverse) {
-      if (bsStyle.Right(pStyle->len).Compare(pStyle->name) == 0)
+      if (bsStyle.Last(pStyle->len).Compare(pStyle->name) == 0)
         return std::make_tuple(true, pStyle->style, pStyle->len);
     } else {
-      if (bsStyle.Left(pStyle->len).Compare(pStyle->name) == 0)
+      if (bsStyle.First(pStyle->len).Compare(pStyle->name) == 0)
         return std::make_tuple(true, pStyle->style, pStyle->len);
     }
   }
@@ -268,46 +258,38 @@
 
 }  // namespace
 
-CFX_FontMapper::CFX_FontMapper(CFX_FontMgr* mgr)
-    : m_bListLoaded(false), m_pFontMgr(mgr) {
-  m_MMFaces[0] = nullptr;
-  m_MMFaces[1] = nullptr;
-  memset(m_FoxitFaces, 0, sizeof(m_FoxitFaces));
-}
+CFX_FontMapper::CFX_FontMapper(CFX_FontMgr* mgr) : m_pFontMgr(mgr) {}
 
-CFX_FontMapper::~CFX_FontMapper() {
-  for (size_t i = 0; i < FX_ArraySize(m_FoxitFaces); ++i) {
-    if (m_FoxitFaces[i])
-      FXFT_Done_Face(m_FoxitFaces[i]);
-  }
-  if (m_MMFaces[0])
-    FXFT_Done_Face(m_MMFaces[0]);
-  if (m_MMFaces[1])
-    FXFT_Done_Face(m_MMFaces[1]);
-}
+CFX_FontMapper::~CFX_FontMapper() = default;
 
 void CFX_FontMapper::SetSystemFontInfo(
-    std::unique_ptr<IFX_SystemFontInfo> pFontInfo) {
+    std::unique_ptr<SystemFontInfoIface> pFontInfo) {
   if (!pFontInfo)
     return;
 
   m_pFontInfo = std::move(pFontInfo);
 }
 
-ByteString CFX_FontMapper::GetPSNameFromTT(void* hFont) {
-  if (!m_pFontInfo)
-    return ByteString();
+uint32_t CFX_FontMapper::GetChecksumFromTT(void* hFont) {
+  uint32_t buffer[256];
+  m_pFontInfo->GetFontData(
+      hFont, kTableTTCF, pdfium::as_writable_bytes(pdfium::make_span(buffer)));
 
-  uint32_t size = m_pFontInfo->GetFontData(hFont, kTableNAME, nullptr, 0);
+  uint32_t checksum = 0;
+  for (auto x : buffer)
+    checksum += x;
+
+  return checksum;
+}
+
+ByteString CFX_FontMapper::GetPSNameFromTT(void* hFont) {
+  uint32_t size = m_pFontInfo->GetFontData(hFont, kTableNAME, {});
   if (!size)
     return ByteString();
 
   std::vector<uint8_t> buffer(size);
-  uint8_t* buffer_ptr = buffer.data();
-  uint32_t bytes_read =
-      m_pFontInfo->GetFontData(hFont, kTableNAME, buffer_ptr, size);
-  return bytes_read == size ? GetNameFromTT(buffer_ptr, bytes_read, 6)
-                            : ByteString();
+  uint32_t bytes_read = m_pFontInfo->GetFontData(hFont, kTableNAME, buffer);
+  return bytes_read == size ? GetNameFromTT(buffer, 6) : ByteString();
 }
 
 void CFX_FontMapper::AddInstalledFont(const ByteString& name, int charset) {
@@ -363,18 +345,19 @@
   return ByteString();
 }
 
-FXFT_Face CFX_FontMapper::UseInternalSubst(CFX_SubstFont* pSubstFont,
-                                           int iBaseFont,
-                                           int italic_angle,
-                                           int weight,
-                                           int pitch_family) {
+RetainPtr<CFX_Face> CFX_FontMapper::UseInternalSubst(CFX_SubstFont* pSubstFont,
+                                                     int iBaseFont,
+                                                     int italic_angle,
+                                                     int weight,
+                                                     int pitch_family) {
   if (iBaseFont < kNumStandardFonts) {
     if (m_FoxitFaces[iBaseFont])
       return m_FoxitFaces[iBaseFont];
-    const uint8_t* pFontData = nullptr;
-    uint32_t size = 0;
-    if (m_pFontMgr->GetBuiltinFont(iBaseFont, &pFontData, &size)) {
-      m_FoxitFaces[iBaseFont] = m_pFontMgr->GetFixedFace(pFontData, size, 0);
+    Optional<pdfium::span<const uint8_t>> font_data =
+        m_pFontMgr->GetBuiltinFont(iBaseFont);
+    if (font_data.has_value()) {
+      m_FoxitFaces[iBaseFont] =
+          m_pFontMgr->NewFixedFace(nullptr, font_data.value(), 0);
       return m_FoxitFaces[iBaseFont];
     }
   }
@@ -385,31 +368,27 @@
   if (FontFamilyIsRoman(pitch_family)) {
     pSubstFont->m_Weight = pSubstFont->m_Weight * 4 / 5;
     pSubstFont->m_Family = "Chrome Serif";
-    if (m_MMFaces[1])
-      return m_MMFaces[1];
-    const uint8_t* pFontData = nullptr;
-    uint32_t size = 0;
-    m_pFontMgr->GetBuiltinFont(14, &pFontData, &size);
-    m_MMFaces[1] = m_pFontMgr->GetFixedFace(pFontData, size, 0);
+    if (!m_MMFaces[1]) {
+      m_MMFaces[1] = m_pFontMgr->NewFixedFace(
+          nullptr, m_pFontMgr->GetBuiltinFont(14).value(), 0);
+    }
     return m_MMFaces[1];
   }
   pSubstFont->m_Family = "Chrome Sans";
-  if (m_MMFaces[0])
-    return m_MMFaces[0];
-  const uint8_t* pFontData = nullptr;
-  uint32_t size = 0;
-  m_pFontMgr->GetBuiltinFont(15, &pFontData, &size);
-  m_MMFaces[0] = m_pFontMgr->GetFixedFace(pFontData, size, 0);
+  if (!m_MMFaces[0]) {
+    m_MMFaces[0] = m_pFontMgr->NewFixedFace(
+        nullptr, m_pFontMgr->GetBuiltinFont(15).value(), 0);
+  }
   return m_MMFaces[0];
 }
 
-FXFT_Face CFX_FontMapper::FindSubstFont(const ByteString& name,
-                                        bool bTrueType,
-                                        uint32_t flags,
-                                        int weight,
-                                        int italic_angle,
-                                        int WindowCP,
-                                        CFX_SubstFont* pSubstFont) {
+RetainPtr<CFX_Face> CFX_FontMapper::FindSubstFont(const ByteString& name,
+                                                  bool bTrueType,
+                                                  uint32_t flags,
+                                                  int weight,
+                                                  int italic_angle,
+                                                  int CharsetCP,
+                                                  CFX_SubstFont* pSubstFont) {
   if (weight == 0)
     weight = FXFONT_FW_NORMAL;
 
@@ -420,8 +399,8 @@
   ByteString SubstName = name;
   SubstName.Remove(' ');
   if (bTrueType && name.GetLength() > 0 && name[0] == '@')
-    SubstName = name.Right(name.GetLength() - 1);
-  PDF_GetStandardFontName(&SubstName);
+    SubstName = name.Last(name.GetLength() - 1);
+  GetStandardFontName(&SubstName);
   if (SubstName == "Symbol" && !bTrueType) {
     pSubstFont->m_Family = "Chrome Symbol";
     pSubstFont->m_Charset = FX_CHARSET_Symbol;
@@ -437,26 +416,27 @@
   ByteString style;
   bool bHasComma = false;
   bool bHasHyphen = false;
-  auto pos = SubstName.Find(",", 0);
-  if (pos.has_value()) {
-    family = SubstName.Left(pos.value());
-    PDF_GetStandardFontName(&family);
-    style = SubstName.Right(SubstName.GetLength() - (pos.value() + 1));
-    bHasComma = true;
-  } else {
-    family = SubstName;
+  {
+    Optional<size_t> pos = SubstName.Find(",", 0);
+    if (pos.has_value()) {
+      family = SubstName.First(pos.value());
+      GetStandardFontName(&family);
+      style = SubstName.Last(SubstName.GetLength() - (pos.value() + 1));
+      bHasComma = true;
+    } else {
+      family = SubstName;
+    }
   }
   for (; iBaseFont < 12; iBaseFont++) {
     if (family == ByteStringView(g_Base14FontNames[iBaseFont]))
       break;
   }
   int PitchFamily = 0;
-  bool bItalic = false;
   uint32_t nStyle = FXFONT_NORMAL;
   bool bStyleAvail = false;
   if (iBaseFont < 12) {
     if ((iBaseFont % 4) == 1 || (iBaseFont % 4) == 2)
-      nStyle |= FXFONT_BOLD;
+      nStyle |= FXFONT_FORCE_BOLD;
     if ((iBaseFont % 4) / 2)
       nStyle |= FXFONT_ITALIC;
     if (iBaseFont < 4)
@@ -466,10 +446,10 @@
   } else {
     iBaseFont = kNumStandardFonts;
     if (!bHasComma) {
-      pos = family.ReverseFind('-');
+      Optional<size_t> pos = family.ReverseFind('-');
       if (pos.has_value()) {
-        style = family.Right(family.GetLength() - (pos.value() + 1));
-        family = family.Left(pos.value());
+        style = family.Last(family.GetLength() - (pos.value() + 1));
+        family = family.First(pos.value());
         bHasHyphen = true;
       }
     }
@@ -480,15 +460,15 @@
       size_t len;
       std::tie(hasStyleType, styleType, len) = GetStyleType(family, true);
       if (hasStyleType) {
-        family = family.Left(nLen - len);
+        family = family.First(nLen - len);
         nStyle |= styleType;
       }
     }
     UpdatePitchFamily(flags, &PitchFamily);
   }
 
-  int old_weight = weight;
-  if (FontStyleIsBold(nStyle))
+  const int old_weight = weight;
+  if (FontStyleIsForceBold(nStyle))
     weight = FXFONT_FW_BOLD;
 
   if (!style.IsEmpty()) {
@@ -512,18 +492,18 @@
       if (hasStyleType)
         bStyleAvail = true;
 
-      if (FontStyleIsBold(styleType)) {
+      if (FontStyleIsForceBold(styleType)) {
         // If we're already bold, then we're double bold, use special weight.
-        if (FontStyleIsBold(nStyle)) {
+        if (FontStyleIsForceBold(nStyle)) {
           weight = FXFONT_FW_BOLD_BOLD;
         } else {
           weight = FXFONT_FW_BOLD;
-          nStyle |= FXFONT_BOLD;
+          nStyle |= FXFONT_FORCE_BOLD;
         }
 
         bFirstItem = false;
       }
-      if (FontStyleIsItalic(styleType) && FontStyleIsBold(styleType)) {
+      if (FontStyleIsItalic(styleType) && FontStyleIsForceBold(styleType)) {
         nStyle |= FXFONT_ITALIC;
       } else if (FontStyleIsItalic(styleType)) {
         if (bFirstItem) {
@@ -537,23 +517,20 @@
       i += buf.GetLength() + 1;
     }
   }
-  if (FontStyleIsItalic(nStyle))
-    bItalic = true;
 
-  int Charset = FX_CHARSET_ANSI;
-  if (WindowCP)
-    Charset = GetCharsetFromCodePage(WindowCP);
-  else if (iBaseFont == kNumStandardFonts && FontStyleIsSymbolic(flags))
-    Charset = FX_CHARSET_Symbol;
-
-  bool bCJK = (Charset == FX_CHARSET_ShiftJIS ||
-               Charset == FX_CHARSET_ChineseSimplified ||
-               Charset == FX_CHARSET_Hangul ||
-               Charset == FX_CHARSET_ChineseTraditional);
   if (!m_pFontInfo) {
     return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight,
                             PitchFamily);
   }
+
+  int Charset = FX_CHARSET_ANSI;
+  if (CharsetCP)
+    Charset = FX_GetCharsetFromCodePage(CharsetCP);
+  else if (iBaseFont == kNumStandardFonts && FontStyleIsSymbolic(flags))
+    Charset = FX_CHARSET_Symbol;
+  const bool bCJK = FX_CharSetIsCJK(Charset);
+  bool bItalic = FontStyleIsItalic(nStyle);
+
   GetFontFamily(nStyle, &family);
   ByteString match = MatchInstalledFonts(TT_NormalizeName(family.c_str()));
   if (match.IsEmpty() && family != SubstName &&
@@ -566,19 +543,12 @@
         bItalic = italic_angle != 0;
         weight = old_weight;
       }
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-      const char* narrow_family = "LiberationSansNarrow";
-#elif _FX_PLATFORM_ == _FX_PLATFORM_ANDROID_
-      const char* narrow_family = "RobotoCondensed";
-#else
-      const char* narrow_family = "ArialNarrow";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-      auto pos = SubstName.Find("Narrow");
+      Optional<size_t> pos = SubstName.Find("Narrow");
       if (pos.has_value() && pos.value() != 0)
-        family = narrow_family;
+        family = kNarrowFamily;
       pos = SubstName.Find("Condensed");
       if (pos.has_value() && pos.value() != 0)
-        family = narrow_family;
+        family = kNarrowFamily;
     } else {
       pSubstFont->m_bSubstCJK = true;
       if (nStyle)
@@ -588,6 +558,8 @@
     }
   } else {
     italic_angle = 0;
+    if (nStyle == FXFONT_NORMAL)
+      weight = FXFONT_FW_NORMAL;
   }
 
   if (!match.IsEmpty() || iBaseFont < kNumStandardFonts) {
@@ -595,9 +567,9 @@
       family = match;
     if (iBaseFont < kNumStandardFonts) {
       if (nStyle && !(iBaseFont % 4)) {
-        if (FontStyleIsBold(nStyle) && FontStyleIsItalic(nStyle))
+        if (FontStyleIsForceBold(nStyle) && FontStyleIsItalic(nStyle))
           iBaseFont += 2;
-        else if (FontStyleIsBold(nStyle))
+        else if (FontStyleIsForceBold(nStyle))
           iBaseFont += 1;
         else if (FontStyleIsItalic(nStyle))
           iBaseFont += 3;
@@ -610,10 +582,6 @@
   void* hFont = m_pFontInfo->MapFont(weight, bItalic, Charset, PitchFamily,
                                      family.c_str());
   if (!hFont) {
-#ifdef PDF_ENABLE_XFA
-    if (flags & FXFONT_EXACTMATCH)
-      return nullptr;
-#endif  // PDF_ENABLE_XFA
     if (bCJK) {
       bItalic = italic_angle != 0;
       weight = old_weight;
@@ -626,8 +594,7 @@
       }
     } else {
       if (Charset == FX_CHARSET_Symbol) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ || \
-    _FX_PLATFORM_ == _FX_PLATFORM_ANDROID_
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
         if (SubstName == "Symbol") {
           pSubstFont->m_Family = "Chrome Symbol";
           pSubstFont->m_Charset = FX_CHARSET_Symbol;
@@ -661,15 +628,15 @@
   m_pFontInfo->GetFaceName(hFont, &SubstName);
   if (Charset == FX_CHARSET_Default)
     m_pFontInfo->GetFontCharset(hFont, &Charset);
-  uint32_t ttc_size = m_pFontInfo->GetFontData(hFont, kTableTTCF, nullptr, 0);
-  uint32_t font_size = m_pFontInfo->GetFontData(hFont, 0, nullptr, 0);
+  uint32_t ttc_size = m_pFontInfo->GetFontData(hFont, kTableTTCF, {});
+  uint32_t font_size = m_pFontInfo->GetFontData(hFont, 0, {});
   if (font_size == 0 && ttc_size == 0) {
     m_pFontInfo->DeleteFont(hFont);
     return nullptr;
   }
-  FXFT_Face face = nullptr;
+  RetainPtr<CFX_Face> face;
   if (ttc_size)
-    face = GetCachedTTCFace(hFont, kTableTTCF, ttc_size, font_size);
+    face = GetCachedTTCFace(hFont, ttc_size, font_size);
   else
     face = GetCachedFace(hFont, SubstName, weight, bItalic, font_size);
   if (!face) {
@@ -679,13 +646,13 @@
   pSubstFont->m_Family = SubstName;
   pSubstFont->m_Charset = Charset;
   bool bNeedUpdateWeight = false;
-  if (FXFT_Is_Face_Bold(face))
+  if (FXFT_Is_Face_Bold(face->GetRec()))
     bNeedUpdateWeight = weight != FXFONT_FW_BOLD;
   else
     bNeedUpdateWeight = weight != FXFONT_FW_NORMAL;
   if (bNeedUpdateWeight)
     pSubstFont->m_Weight = weight;
-  if (bItalic && !FXFT_Is_Face_Italic(face)) {
+  if (bItalic && !FXFT_Is_Face_Italic(face->GetRec())) {
     if (italic_angle == 0)
       italic_angle = -12;
     else if (abs(italic_angle) < 5)
@@ -696,46 +663,35 @@
   return face;
 }
 
-#ifdef PDF_ENABLE_XFA
-FXFT_Face CFX_FontMapper::FindSubstFontByUnicode(uint32_t dwUnicode,
-                                                 uint32_t flags,
-                                                 int weight,
-                                                 int italic_angle) {
-  if (!m_pFontInfo)
-    return nullptr;
-
-  bool bItalic = (flags & FXFONT_ITALIC) != 0;
-  int PitchFamily = 0;
-  UpdatePitchFamily(flags, &PitchFamily);
-  void* hFont =
-      m_pFontInfo->MapFontByUnicode(dwUnicode, weight, bItalic, PitchFamily);
-  if (!hFont)
-    return nullptr;
-
-  uint32_t ttc_size = m_pFontInfo->GetFontData(hFont, 0x74746366, nullptr, 0);
-  uint32_t font_size = m_pFontInfo->GetFontData(hFont, 0, nullptr, 0);
-  if (font_size == 0 && ttc_size == 0) {
-    m_pFontInfo->DeleteFont(hFont);
-    return nullptr;
-  }
-  FXFT_Face face = nullptr;
-  if (ttc_size) {
-    face = GetCachedTTCFace(hFont, 0x74746366, ttc_size, font_size);
-  } else {
-    ByteString SubstName;
-    m_pFontInfo->GetFaceName(hFont, &SubstName);
-    face = GetCachedFace(hFont, SubstName, weight, bItalic, font_size);
-  }
-  m_pFontInfo->DeleteFont(hFont);
-  return face;
-}
-#endif  // PDF_ENABLE_XFA
-
 int CFX_FontMapper::GetFaceSize() const {
   return pdfium::CollectionSize<int>(m_FaceArray);
 }
 
-bool CFX_FontMapper::IsBuiltinFace(const FXFT_Face face) const {
+#ifdef PDF_ENABLE_XFA
+std::unique_ptr<uint8_t, FxFreeDeleter> CFX_FontMapper::RawBytesForIndex(
+    uint32_t index,
+    size_t* returned_length) {
+  if (!m_pFontInfo)
+    return nullptr;
+
+  void* hFont = m_pFontInfo->MapFont(0, 0, FX_CHARSET_Default, 0,
+                                     GetFaceName(index).c_str());
+  if (!hFont)
+    return nullptr;
+
+  uint32_t required_size = m_pFontInfo->GetFontData(hFont, 0, {});
+  if (required_size == 0)
+    return nullptr;
+
+  std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer(
+      FX_Alloc(uint8_t, required_size + 1));
+  *returned_length =
+      m_pFontInfo->GetFontData(hFont, 0, {pBuffer.get(), required_size});
+  return pBuffer;
+}
+#endif  // PDF_ENABLE_XFA
+
+bool CFX_FontMapper::IsBuiltinFace(const RetainPtr<CFX_Face>& face) const {
   for (size_t i = 0; i < MM_FACE_COUNT; ++i) {
     if (m_MMFaces[i] == face)
       return true;
@@ -747,47 +703,67 @@
   return false;
 }
 
-FXFT_Face CFX_FontMapper::GetCachedTTCFace(void* hFont,
-                                           const uint32_t tableTTCF,
-                                           uint32_t ttc_size,
-                                           uint32_t font_size) {
-  uint8_t buffer[1024];
-  m_pFontInfo->GetFontData(hFont, tableTTCF, buffer, FX_ArraySize(buffer));
-  uint32_t* pBuffer = reinterpret_cast<uint32_t*>(buffer);
-  uint32_t checksum = 0;
-  for (int i = 0; i < 256; i++)
-    checksum += pBuffer[i];
-  uint8_t* pFontData;
-  FXFT_Face face = m_pFontMgr->GetCachedTTCFace(
-      ttc_size, checksum, ttc_size - font_size, &pFontData);
-  if (!face) {
-    pFontData = FX_Alloc(uint8_t, ttc_size);
-    m_pFontInfo->GetFontData(hFont, tableTTCF, pFontData, ttc_size);
-    face = m_pFontMgr->AddCachedTTCFace(ttc_size, checksum, pFontData, ttc_size,
-                                        ttc_size - font_size);
+RetainPtr<CFX_Face> CFX_FontMapper::GetCachedTTCFace(void* hFont,
+                                                     uint32_t ttc_size,
+                                                     uint32_t font_size) {
+  uint32_t checksum = GetChecksumFromTT(hFont);
+  RetainPtr<CFX_FontMgr::FontDesc> pFontDesc =
+      m_pFontMgr->GetCachedTTCFontDesc(ttc_size, checksum);
+  if (!pFontDesc) {
+    std::unique_ptr<uint8_t, FxFreeDeleter> pFontData(
+        FX_Alloc(uint8_t, ttc_size));
+    m_pFontInfo->GetFontData(hFont, kTableTTCF, {pFontData.get(), ttc_size});
+    pFontDesc = m_pFontMgr->AddCachedTTCFontDesc(
+        ttc_size, checksum, std::move(pFontData), ttc_size);
   }
-  return face;
+  ASSERT(ttc_size >= font_size);
+  uint32_t font_offset = ttc_size - font_size;
+  int face_index =
+      GetTTCIndex(pFontDesc->FontData().first(ttc_size), font_offset);
+  RetainPtr<CFX_Face> pFace(pFontDesc->GetFace(face_index));
+  if (pFace)
+    return pFace;
+
+  pFace = m_pFontMgr->NewFixedFace(
+      pFontDesc, pFontDesc->FontData().first(ttc_size), face_index);
+  if (!pFace)
+    return nullptr;
+
+  pFontDesc->SetFace(face_index, pFace.Get());
+  return pFace;
 }
 
-FXFT_Face CFX_FontMapper::GetCachedFace(void* hFont,
-                                        ByteString SubstName,
-                                        int weight,
-                                        bool bItalic,
-                                        uint32_t font_size) {
-  uint8_t* pFontData;
-  FXFT_Face face =
-      m_pFontMgr->GetCachedFace(SubstName, weight, bItalic, &pFontData);
-  if (!face) {
-    pFontData = FX_Alloc(uint8_t, font_size);
-    m_pFontInfo->GetFontData(hFont, 0, pFontData, font_size);
-    face =
-        m_pFontMgr->AddCachedFace(SubstName, weight, bItalic, pFontData,
-                                  font_size, m_pFontInfo->GetFaceIndex(hFont));
+RetainPtr<CFX_Face> CFX_FontMapper::GetCachedFace(void* hFont,
+                                                  ByteString SubstName,
+                                                  int weight,
+                                                  bool bItalic,
+                                                  uint32_t font_size) {
+  RetainPtr<CFX_FontMgr::FontDesc> pFontDesc =
+      m_pFontMgr->GetCachedFontDesc(SubstName, weight, bItalic);
+  if (!pFontDesc) {
+    std::unique_ptr<uint8_t, FxFreeDeleter> pFontData(
+        FX_Alloc(uint8_t, font_size));
+    m_pFontInfo->GetFontData(hFont, 0, {pFontData.get(), font_size});
+    pFontDesc = m_pFontMgr->AddCachedFontDesc(SubstName, weight, bItalic,
+                                              std::move(pFontData), font_size);
   }
-  return face;
+  RetainPtr<CFX_Face> pFace(pFontDesc->GetFace(0));
+  if (pFace)
+    return pFace;
+
+  pFace = m_pFontMgr->NewFixedFace(pFontDesc,
+                                   pFontDesc->FontData().first(font_size),
+                                   m_pFontInfo->GetFaceIndex(hFont));
+  if (!pFace)
+    return nullptr;
+
+  pFontDesc->SetFace(0, pFace.Get());
+  return pFace;
 }
 
-int PDF_GetStandardFontName(ByteString* name) {
+// static
+Optional<CFX_FontMapper::StandardFont> CFX_FontMapper::GetStandardFontName(
+    ByteString* name) {
   const auto* end = std::end(g_AltFontNames);
   const auto* found =
       std::lower_bound(std::begin(g_AltFontNames), end, name->c_str(),
@@ -795,8 +771,20 @@
                          return FXSYS_stricmp(element.m_pName, name) < 0;
                        });
   if (found == end || FXSYS_stricmp(found->m_pName, name->c_str()))
-    return -1;
+    return {};
 
-  *name = g_Base14FontNames[found->m_Index];
+  *name = g_Base14FontNames[static_cast<size_t>(found->m_Index)];
   return found->m_Index;
 }
+
+// static
+bool CFX_FontMapper::IsSymbolicFont(StandardFont font) {
+  return font == StandardFont::kSymbol || font == StandardFont::kDingbats;
+}
+
+// static
+bool CFX_FontMapper::IsFixedFont(StandardFont font) {
+  return font == StandardFont::kCourier || font == StandardFont::kCourierBold ||
+         font == StandardFont::kCourierBoldOblique ||
+         font == StandardFont::kCourierOblique;
+}
diff --git a/core/fxge/cfx_fontmapper.h b/core/fxge/cfx_fontmapper.h
index ed8ab61..c21c6cb 100644
--- a/core/fxge/cfx_fontmapper.h
+++ b/core/fxge/cfx_fontmapper.h
@@ -11,39 +11,67 @@
 #include <utility>
 #include <vector>
 
-#include "core/fxge/fx_font.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxge/cfx_face.h"
+#include "third_party/base/optional.h"
 
 class CFX_FontMgr;
 class CFX_SubstFont;
-class IFX_SystemFontInfo;
+class SystemFontInfoIface;
 
 class CFX_FontMapper {
  public:
+  enum StandardFont {
+    kCourier = 0,
+    kCourierBold,
+    kCourierBoldOblique,
+    kCourierOblique,
+    kHelvetica,
+    kHelveticaBold,
+    kHelveticaBoldOblique,
+    kHelveticaOblique,
+    kTimes,
+    kTimesBold,
+    kTimesBoldOblique,
+    kTimesOblique,
+    kSymbol,
+    kDingbats,
+  };
+
   explicit CFX_FontMapper(CFX_FontMgr* mgr);
   ~CFX_FontMapper();
 
-  void SetSystemFontInfo(std::unique_ptr<IFX_SystemFontInfo> pFontInfo);
-  IFX_SystemFontInfo* GetSystemFontInfo() { return m_pFontInfo.get(); }
+  static Optional<StandardFont> GetStandardFontName(ByteString* name);
+  static bool IsSymbolicFont(StandardFont font);
+  static bool IsFixedFont(StandardFont font);
+  static constexpr uint32_t MakeTag(char c1, char c2, char c3, char c4) {
+    return static_cast<uint8_t>(c1) << 24 | static_cast<uint8_t>(c2) << 16 |
+           static_cast<uint8_t>(c3) << 8 | static_cast<uint8_t>(c4);
+  }
+
+  void SetSystemFontInfo(std::unique_ptr<SystemFontInfoIface> pFontInfo);
   void AddInstalledFont(const ByteString& name, int charset);
   void LoadInstalledFonts();
 
-  FXFT_Face FindSubstFont(const ByteString& face_name,
-                          bool bTrueType,
-                          uint32_t flags,
-                          int weight,
-                          int italic_angle,
-                          int CharsetCP,
-                          CFX_SubstFont* pSubstFont);
-#ifdef PDF_ENABLE_XFA
-  FXFT_Face FindSubstFontByUnicode(uint32_t dwUnicode,
-                                   uint32_t flags,
-                                   int weight,
-                                   int italic_angle);
-#endif  // PDF_ENABLE_XFA
-  bool IsBuiltinFace(const FXFT_Face face) const;
+  RetainPtr<CFX_Face> FindSubstFont(const ByteString& face_name,
+                                    bool bTrueType,
+                                    uint32_t flags,
+                                    int weight,
+                                    int italic_angle,
+                                    int CharsetCP,
+                                    CFX_SubstFont* pSubstFont);
+
+  bool IsBuiltinFace(const RetainPtr<CFX_Face>& face) const;
   int GetFaceSize() const;
   ByteString GetFaceName(int index) const { return m_FaceArray[index].name; }
 
+#ifdef PDF_ENABLE_XFA
+  std::unique_ptr<uint8_t, FxFreeDeleter> RawBytesForIndex(
+      uint32_t index,
+      size_t* returned_length);
+#endif  // PDF_ENABLE_XFA
+
   std::vector<ByteString> m_InstalledTTFonts;
   std::vector<std::pair<ByteString, ByteString>> m_LocalizedTTFonts;
 
@@ -51,35 +79,35 @@
   static const size_t MM_FACE_COUNT = 2;
   static const size_t FOXIT_FACE_COUNT = 14;
 
+  uint32_t GetChecksumFromTT(void* hFont);
   ByteString GetPSNameFromTT(void* hFont);
   ByteString MatchInstalledFonts(const ByteString& norm_name);
-  FXFT_Face UseInternalSubst(CFX_SubstFont* pSubstFont,
-                             int iBaseFont,
-                             int italic_angle,
-                             int weight,
-                             int picthfamily);
-  FXFT_Face GetCachedTTCFace(void* hFont,
-                             const uint32_t tableTTCF,
-                             uint32_t ttc_size,
-                             uint32_t font_size);
-  FXFT_Face GetCachedFace(void* hFont,
-                          ByteString SubstName,
-                          int weight,
-                          bool bItalic,
-                          uint32_t font_size);
+  RetainPtr<CFX_Face> UseInternalSubst(CFX_SubstFont* pSubstFont,
+                                       int iBaseFont,
+                                       int italic_angle,
+                                       int weight,
+                                       int pitch_family);
+  RetainPtr<CFX_Face> GetCachedTTCFace(void* hFont,
+                                       uint32_t ttc_size,
+                                       uint32_t font_size);
+  RetainPtr<CFX_Face> GetCachedFace(void* hFont,
+                                    ByteString SubstName,
+                                    int weight,
+                                    bool bItalic,
+                                    uint32_t font_size);
 
   struct FaceData {
     ByteString name;
     uint32_t charset;
   };
 
-  bool m_bListLoaded;
-  FXFT_Face m_MMFaces[MM_FACE_COUNT];
+  bool m_bListLoaded = false;
   ByteString m_LastFamily;
   std::vector<FaceData> m_FaceArray;
-  std::unique_ptr<IFX_SystemFontInfo> m_pFontInfo;
-  FXFT_Face m_FoxitFaces[FOXIT_FACE_COUNT];
+  std::unique_ptr<SystemFontInfoIface> m_pFontInfo;
   UnownedPtr<CFX_FontMgr> const m_pFontMgr;
+  RetainPtr<CFX_Face> m_MMFaces[MM_FACE_COUNT];
+  RetainPtr<CFX_Face> m_FoxitFaces[FOXIT_FACE_COUNT];
 };
 
 #endif  // CORE_FXGE_CFX_FONTMAPPER_H_
diff --git a/core/fxge/cfx_fontmapper_unittest.cpp b/core/fxge/cfx_fontmapper_unittest.cpp
new file mode 100644
index 0000000..e69387d
--- /dev/null
+++ b/core/fxge/cfx_fontmapper_unittest.cpp
@@ -0,0 +1,20 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_fontmapper.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Deliberately give this global variable external linkage.
+char g_maybe_changes = '\xff';
+
+TEST(CFX_FontMapper, MakeTag) {
+  EXPECT_EQ(0x61626364u, CFX_FontMapper::MakeTag('a', 'b', 'c', 'd'));
+  EXPECT_EQ(0x00000000u, CFX_FontMapper::MakeTag('\0', '\0', '\0', '\0'));
+  EXPECT_EQ(0xfffe0a08u, CFX_FontMapper::MakeTag('\xff', '\xfe', '\n', '\b'));
+  EXPECT_EQ(0xffffffffu,
+            CFX_FontMapper::MakeTag('\xff', '\xff', '\xff', '\xff'));
+  EXPECT_EQ(0xffffffffu,
+            CFX_FontMapper::MakeTag(g_maybe_changes, '\xff', '\xff', '\xff'));
+}
diff --git a/core/fxge/cfx_fontmgr.cpp b/core/fxge/cfx_fontmgr.cpp
index e2be93e..7a08d0f 100644
--- a/core/fxge/cfx_fontmgr.cpp
+++ b/core/fxge/cfx_fontmgr.cpp
@@ -9,18 +9,18 @@
 #include <memory>
 #include <utility>
 
+#include "core/fxge/cfx_face.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_substfont.h"
-#include "core/fxge/cttfontdesc.h"
 #include "core/fxge/fontdata/chromefontdata/chromefontdata.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "core/fxge/systemfontinfo_iface.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
 struct BuiltinFont {
-  const uint8_t* m_pFontData;
+  const uint8_t* m_pFontData;  // Raw, POD struct.
   uint32_t m_dwSize;
 };
 
@@ -60,186 +60,124 @@
   return ByteString::Format("%d:%d", ttc_size, checksum);
 }
 
-int GetTTCIndex(const uint8_t* pFontData,
-                uint32_t ttc_size,
-                uint32_t font_offset) {
-  const uint8_t* p = pFontData + 8;
-  uint32_t nfont = GET_TT_LONG(p);
-  uint32_t index;
-  for (index = 0; index < nfont; index++) {
-    p = pFontData + 12 + index * 4;
-    if (GET_TT_LONG(p) == font_offset)
-      break;
-  }
-  return index < nfont ? index : 0;
+FXFT_LibraryRec* FTLibraryInitHelper() {
+  FXFT_LibraryRec* pLibrary = nullptr;
+  FT_Init_FreeType(&pLibrary);
+  return pLibrary;
 }
 
 }  // namespace
 
+CFX_FontMgr::FontDesc::FontDesc(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
+                                size_t size)
+    : m_Size(size), m_pFontData(std::move(pData)) {}
+
+CFX_FontMgr::FontDesc::~FontDesc() = default;
+
+void CFX_FontMgr::FontDesc::SetFace(size_t index, CFX_Face* face) {
+  ASSERT(index < FX_ArraySize(m_TTCFaces));
+  m_TTCFaces[index].Reset(face);
+}
+
+CFX_Face* CFX_FontMgr::FontDesc::GetFace(size_t index) const {
+  ASSERT(index < FX_ArraySize(m_TTCFaces));
+  return m_TTCFaces[index].Get();
+}
+
 CFX_FontMgr::CFX_FontMgr()
-    : m_FTLibrary(nullptr), m_FTLibrarySupportsHinting(false) {
-  m_pBuiltinMapper = pdfium::MakeUnique<CFX_FontMapper>(this);
-}
+    : m_FTLibrary(FTLibraryInitHelper()),
+      m_pBuiltinMapper(pdfium::MakeUnique<CFX_FontMapper>(this)),
+      m_FTLibrarySupportsHinting(SetLcdFilterMode() ||
+                                 FreeTypeVersionSupportsHinting()) {}
 
-CFX_FontMgr::~CFX_FontMgr() {
-  // |m_FaceMap| and |m_pBuiltinMapper| reference |m_FTLibrary|, so they must
-  // be destroyed first.
-  m_FaceMap.clear();
-  m_pBuiltinMapper.reset();
-  FXFT_Done_FreeType(m_FTLibrary);
-}
-
-void CFX_FontMgr::InitFTLibrary() {
-  if (m_FTLibrary)
-    return;
-
-  FXFT_Init_FreeType(&m_FTLibrary);
-  m_FTLibrarySupportsHinting =
-      SetLcdFilterMode() || FreeTypeVersionSupportsHinting();
-}
+CFX_FontMgr::~CFX_FontMgr() = default;
 
 void CFX_FontMgr::SetSystemFontInfo(
-    std::unique_ptr<IFX_SystemFontInfo> pFontInfo) {
+    std::unique_ptr<SystemFontInfoIface> pFontInfo) {
   m_pBuiltinMapper->SetSystemFontInfo(std::move(pFontInfo));
 }
 
-FXFT_Face CFX_FontMgr::FindSubstFont(const ByteString& face_name,
-                                     bool bTrueType,
-                                     uint32_t flags,
-                                     int weight,
-                                     int italic_angle,
-                                     int CharsetCP,
-                                     CFX_SubstFont* pSubstFont) {
-  InitFTLibrary();
+RetainPtr<CFX_Face> CFX_FontMgr::FindSubstFont(const ByteString& face_name,
+                                               bool bTrueType,
+                                               uint32_t flags,
+                                               int weight,
+                                               int italic_angle,
+                                               int CharsetCP,
+                                               CFX_SubstFont* pSubstFont) {
   return m_pBuiltinMapper->FindSubstFont(face_name, bTrueType, flags, weight,
                                          italic_angle, CharsetCP, pSubstFont);
 }
 
-FXFT_Face CFX_FontMgr::GetCachedFace(const ByteString& face_name,
-                                     int weight,
-                                     bool bItalic,
-                                     uint8_t** pFontData) {
+RetainPtr<CFX_FontMgr::FontDesc> CFX_FontMgr::GetCachedFontDesc(
+    const ByteString& face_name,
+    int weight,
+    bool bItalic) {
   auto it = m_FaceMap.find(KeyNameFromFace(face_name, weight, bItalic));
-  if (it == m_FaceMap.end())
-    return nullptr;
-
-  CTTFontDesc* pFontDesc = it->second.get();
-  *pFontData = pFontDesc->FontData();
-  pFontDesc->AddRef();
-  return pFontDesc->SingleFace();
+  return it != m_FaceMap.end() ? pdfium::WrapRetain(it->second.Get()) : nullptr;
 }
 
-FXFT_Face CFX_FontMgr::AddCachedFace(const ByteString& face_name,
-                                     int weight,
-                                     bool bItalic,
-                                     uint8_t* pData,
-                                     uint32_t size,
-                                     int face_index) {
-  InitFTLibrary();
-
-  FXFT_Face face = nullptr;
-  int ret = FXFT_New_Memory_Face(m_FTLibrary, pData, size, face_index, &face);
-  if (ret)
-    return nullptr;
-
-  ret = FXFT_Set_Pixel_Sizes(face, 64, 64);
-  if (ret)
-    return nullptr;
-
-  auto pFontDesc = pdfium::MakeUnique<CTTFontDesc>(pData, face);
-  CTTFontDesc* pResult = pFontDesc.get();
-  m_FaceMap[KeyNameFromFace(face_name, weight, bItalic)] = std::move(pFontDesc);
-  return pResult->SingleFace();
+RetainPtr<CFX_FontMgr::FontDesc> CFX_FontMgr::AddCachedFontDesc(
+    const ByteString& face_name,
+    int weight,
+    bool bItalic,
+    std::unique_ptr<uint8_t, FxFreeDeleter> pData,
+    uint32_t size) {
+  auto pFontDesc = pdfium::MakeRetain<FontDesc>(std::move(pData), size);
+  m_FaceMap[KeyNameFromFace(face_name, weight, bItalic)].Reset(pFontDesc.Get());
+  return pFontDesc;
 }
 
-FXFT_Face CFX_FontMgr::GetCachedTTCFace(int ttc_size,
-                                        uint32_t checksum,
-                                        int font_offset,
-                                        uint8_t** pFontData) {
+RetainPtr<CFX_FontMgr::FontDesc> CFX_FontMgr::GetCachedTTCFontDesc(
+    int ttc_size,
+    uint32_t checksum) {
   auto it = m_FaceMap.find(KeyNameFromSize(ttc_size, checksum));
-  if (it == m_FaceMap.end())
-    return nullptr;
-
-  CTTFontDesc* pFontDesc = it->second.get();
-  *pFontData = pFontDesc->FontData();
-  int face_index = GetTTCIndex(pFontDesc->FontData(), ttc_size, font_offset);
-  if (!pFontDesc->TTCFace(face_index)) {
-    pFontDesc->SetTTCFace(
-        face_index, GetFixedFace(pFontDesc->FontData(), ttc_size, face_index));
-  }
-  pFontDesc->AddRef();
-  return pFontDesc->TTCFace(face_index);
+  return it != m_FaceMap.end() ? pdfium::WrapRetain(it->second.Get()) : nullptr;
 }
 
-FXFT_Face CFX_FontMgr::AddCachedTTCFace(int ttc_size,
-                                        uint32_t checksum,
-                                        uint8_t* pData,
-                                        uint32_t size,
-                                        int font_offset) {
-  int face_index = GetTTCIndex(pData, ttc_size, font_offset);
-  FXFT_Face face = GetFixedFace(pData, ttc_size, face_index);
-  auto pFontDesc = pdfium::MakeUnique<CTTFontDesc>(pData, face_index, face);
-  m_FaceMap[KeyNameFromSize(ttc_size, checksum)] = std::move(pFontDesc);
+RetainPtr<CFX_FontMgr::FontDesc> CFX_FontMgr::AddCachedTTCFontDesc(
+    int ttc_size,
+    uint32_t checksum,
+    std::unique_ptr<uint8_t, FxFreeDeleter> pData,
+    uint32_t size) {
+  auto pNewDesc = pdfium::MakeRetain<FontDesc>(std::move(pData), size);
+  m_FaceMap[KeyNameFromSize(ttc_size, checksum)].Reset(pNewDesc.Get());
+  return pNewDesc;
+}
+
+RetainPtr<CFX_Face> CFX_FontMgr::NewFixedFace(const RetainPtr<FontDesc>& pDesc,
+                                              pdfium::span<const uint8_t> span,
+                                              int face_index) {
+  RetainPtr<CFX_Face> face =
+      CFX_Face::New(m_FTLibrary.get(), pDesc, span, face_index);
+  if (!face)
+    return nullptr;
+
+  if (FT_Set_Pixel_Sizes(face->GetRec(), 64, 64) != 0)
+    return nullptr;
+
   return face;
 }
 
-FXFT_Face CFX_FontMgr::GetFixedFace(const uint8_t* pData,
-                                    uint32_t size,
-                                    int face_index) {
-  InitFTLibrary();
-  FXFT_Face face = nullptr;
-  if (FXFT_New_Memory_Face(m_FTLibrary, pData, size, face_index, &face))
-    return nullptr;
-  return FXFT_Set_Pixel_Sizes(face, 64, 64) ? nullptr : face;
-}
-
-FXFT_Face CFX_FontMgr::GetFileFace(const char* filename, int face_index) {
-  InitFTLibrary();
-  FXFT_Face face = nullptr;
-  if (FXFT_New_Face(m_FTLibrary, filename, face_index, &face))
-    return nullptr;
-  return FXFT_Set_Pixel_Sizes(face, 64, 64) ? nullptr : face;
-}
-
-void CFX_FontMgr::ReleaseFace(FXFT_Face face) {
-  if (!face)
-    return;
-  bool bNeedFaceDone = true;
-  for (auto it = m_FaceMap.begin(); it != m_FaceMap.end(); ++it) {
-    CTTFontDesc::ReleaseStatus nRet = it->second->ReleaseFace(face);
-    if (nRet == CTTFontDesc::kNotAppropriate)
-      continue;
-    bNeedFaceDone = false;
-    if (nRet == CTTFontDesc::kReleased)
-      m_FaceMap.erase(it);
-    break;
-  }
-  if (bNeedFaceDone && !m_pBuiltinMapper->IsBuiltinFace(face))
-    FXFT_Done_Face(face);
-}
-
-bool CFX_FontMgr::GetBuiltinFont(size_t index,
-                                 const uint8_t** pFontData,
-                                 uint32_t* size) {
+// static
+Optional<pdfium::span<const uint8_t>> CFX_FontMgr::GetBuiltinFont(
+    size_t index) {
   if (index < FX_ArraySize(g_FoxitFonts)) {
-    *pFontData = g_FoxitFonts[index].m_pFontData;
-    *size = g_FoxitFonts[index].m_dwSize;
-    return true;
+    return pdfium::make_span(g_FoxitFonts[index].m_pFontData,
+                             g_FoxitFonts[index].m_dwSize);
   }
   size_t mm_index = index - FX_ArraySize(g_FoxitFonts);
   if (mm_index < FX_ArraySize(g_MMFonts)) {
-    *pFontData = g_MMFonts[mm_index].m_pFontData;
-    *size = g_MMFonts[mm_index].m_dwSize;
-    return true;
+    return pdfium::make_span(g_MMFonts[mm_index].m_pFontData,
+                             g_MMFonts[mm_index].m_dwSize);
   }
-  return false;
+  return {};
 }
 
 bool CFX_FontMgr::FreeTypeVersionSupportsHinting() const {
   FT_Int major;
   FT_Int minor;
   FT_Int patch;
-  FXFT_Library_Version(m_FTLibrary, &major, &minor, &patch);
+  FT_Library_Version(m_FTLibrary.get(), &major, &minor, &patch);
   // Freetype versions >= 2.8.1 support hinting even if subpixel rendering is
   // disabled. https://sourceforge.net/projects/freetype/files/freetype2/2.8.1/
   return major > 2 || (major == 2 && minor > 8) ||
@@ -247,6 +185,6 @@
 }
 
 bool CFX_FontMgr::SetLcdFilterMode() const {
-  return FXFT_Library_SetLcdFilter(m_FTLibrary, FT_LCD_FILTER_DEFAULT) !=
+  return FT_Library_SetLcdFilter(m_FTLibrary.get(), FT_LCD_FILTER_DEFAULT) !=
          FT_Err_Unimplemented_Feature;
 }
diff --git a/core/fxge/cfx_fontmgr.h b/core/fxge/cfx_fontmgr.h
index e53a4ca..144961e 100644
--- a/core/fxge/cfx_fontmgr.h
+++ b/core/fxge/cfx_fontmgr.h
@@ -10,63 +10,92 @@
 #include <map>
 #include <memory>
 
-#include "core/fxge/fx_font.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/fx_freetype.h"
+#include "third_party/base/optional.h"
+#include "third_party/base/span.h"
 
+class CFX_Face;
 class CFX_FontMapper;
 class CFX_SubstFont;
-class CTTFontDesc;
-class IFX_SystemFontInfo;
+class SystemFontInfoIface;
 
 class CFX_FontMgr {
  public:
+  class FontDesc final : public Retainable, public Observable {
+   public:
+    template <typename T, typename... Args>
+    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+    ~FontDesc() override;
+
+    pdfium::span<uint8_t> FontData() const {
+      return {m_pFontData.get(), m_Size};
+    }
+    void SetFace(size_t index, CFX_Face* face);
+    CFX_Face* GetFace(size_t index) const;
+
+   private:
+    FontDesc(std::unique_ptr<uint8_t, FxFreeDeleter> pData, size_t size);
+
+    const size_t m_Size;
+    std::unique_ptr<uint8_t, FxFreeDeleter> const m_pFontData;
+    ObservedPtr<CFX_Face> m_TTCFaces[16];
+  };
+
+  static Optional<pdfium::span<const uint8_t>> GetBuiltinFont(size_t index);
+
   CFX_FontMgr();
   ~CFX_FontMgr();
 
-  void InitFTLibrary();
+  RetainPtr<FontDesc> GetCachedFontDesc(const ByteString& face_name,
+                                        int weight,
+                                        bool bItalic);
+  RetainPtr<FontDesc> AddCachedFontDesc(
+      const ByteString& face_name,
+      int weight,
+      bool bItalic,
+      std::unique_ptr<uint8_t, FxFreeDeleter> pData,
+      uint32_t size);
 
-  FXFT_Face GetCachedFace(const ByteString& face_name,
-                          int weight,
-                          bool bItalic,
-                          uint8_t** pFontData);
-  FXFT_Face AddCachedFace(const ByteString& face_name,
-                          int weight,
-                          bool bItalic,
-                          uint8_t* pData,
-                          uint32_t size,
-                          int face_index);
-  FXFT_Face GetCachedTTCFace(int ttc_size,
-                             uint32_t checksum,
-                             int font_offset,
-                             uint8_t** pFontData);
-  FXFT_Face AddCachedTTCFace(int ttc_size,
-                             uint32_t checksum,
-                             uint8_t* pData,
-                             uint32_t size,
-                             int font_offset);
-  FXFT_Face GetFileFace(const char* filename, int face_index);
-  FXFT_Face GetFixedFace(const uint8_t* pData, uint32_t size, int face_index);
-  void ReleaseFace(FXFT_Face face);
-  void SetSystemFontInfo(std::unique_ptr<IFX_SystemFontInfo> pFontInfo);
-  FXFT_Face FindSubstFont(const ByteString& face_name,
-                          bool bTrueType,
-                          uint32_t flags,
-                          int weight,
-                          int italic_angle,
-                          int CharsetCP,
-                          CFX_SubstFont* pSubstFont);
-  bool GetBuiltinFont(size_t index, const uint8_t** pFontData, uint32_t* size);
+  RetainPtr<FontDesc> GetCachedTTCFontDesc(int ttc_size, uint32_t checksum);
+  RetainPtr<FontDesc> AddCachedTTCFontDesc(
+      int ttc_size,
+      uint32_t checksum,
+      std::unique_ptr<uint8_t, FxFreeDeleter> pData,
+      uint32_t size);
+
+  RetainPtr<CFX_Face> NewFixedFace(const RetainPtr<FontDesc>& pDesc,
+                                   pdfium::span<const uint8_t> span,
+                                   int face_index);
+  RetainPtr<CFX_Face> FindSubstFont(const ByteString& face_name,
+                                    bool bTrueType,
+                                    uint32_t flags,
+                                    int weight,
+                                    int italic_angle,
+                                    int CharsetCP,
+                                    CFX_SubstFont* pSubstFont);
+
+  void SetSystemFontInfo(std::unique_ptr<SystemFontInfoIface> pFontInfo);
+
+  // Always present.
   CFX_FontMapper* GetBuiltinMapper() const { return m_pBuiltinMapper.get(); }
-  FXFT_Library GetFTLibrary() const { return m_FTLibrary; }
+
+  FXFT_LibraryRec* GetFTLibrary() const { return m_FTLibrary.get(); }
   bool FTLibrarySupportsHinting() const { return m_FTLibrarySupportsHinting; }
 
  private:
   bool FreeTypeVersionSupportsHinting() const;
   bool SetLcdFilterMode() const;
 
+  // Must come before |m_pBuiltinMapper| and |m_FaceMap|.
+  ScopedFXFTLibraryRec const m_FTLibrary;
   std::unique_ptr<CFX_FontMapper> m_pBuiltinMapper;
-  std::map<ByteString, std::unique_ptr<CTTFontDesc>> m_FaceMap;
-  FXFT_Library m_FTLibrary;
-  bool m_FTLibrarySupportsHinting;
+  std::map<ByteString, ObservedPtr<FontDesc>> m_FaceMap;
+  const bool m_FTLibrarySupportsHinting;
 };
 
 #endif  // CORE_FXGE_CFX_FONTMGR_H_
diff --git a/core/fxge/cfx_gemodule.cpp b/core/fxge/cfx_gemodule.cpp
index cbc66c3..5d268bf 100644
--- a/core/fxge/cfx_gemodule.cpp
+++ b/core/fxge/cfx_gemodule.cpp
@@ -17,20 +17,19 @@
 
 }  // namespace
 
-CFX_GEModule::CFX_GEModule()
-    : m_pFontMgr(pdfium::MakeUnique<CFX_FontMgr>()),
-      m_pPlatformData(nullptr),
-      m_pUserFontPaths(nullptr) {}
+CFX_GEModule::CFX_GEModule(const char** pUserFontPaths)
+    : m_pPlatform(PlatformIface::Create()),
+      m_pFontMgr(pdfium::MakeUnique<CFX_FontMgr>()),
+      m_pFontCache(pdfium::MakeUnique<CFX_FontCache>()),
+      m_pUserFontPaths(pUserFontPaths) {}
 
-CFX_GEModule::~CFX_GEModule() {
-  DestroyPlatform();
-}
+CFX_GEModule::~CFX_GEModule() = default;
 
 // static
-CFX_GEModule* CFX_GEModule::Get() {
-  if (!g_pGEModule)
-    g_pGEModule = new CFX_GEModule();
-  return g_pGEModule;
+void CFX_GEModule::Create(const char** pUserFontPaths) {
+  ASSERT(!g_pGEModule);
+  g_pGEModule = new CFX_GEModule(pUserFontPaths);
+  g_pGEModule->m_pPlatform->Init();
 }
 
 // static
@@ -40,14 +39,8 @@
   g_pGEModule = nullptr;
 }
 
-void CFX_GEModule::Init(const char** userFontPaths) {
+// static
+CFX_GEModule* CFX_GEModule::Get() {
   ASSERT(g_pGEModule);
-  m_pUserFontPaths = userFontPaths;
-  InitPlatform();
-}
-
-CFX_FontCache* CFX_GEModule::GetFontCache() {
-  if (!m_pFontCache)
-    m_pFontCache = pdfium::MakeUnique<CFX_FontCache>();
-  return m_pFontCache.get();
+  return g_pGEModule;
 }
diff --git a/core/fxge/cfx_gemodule.h b/core/fxge/cfx_gemodule.h
index f48d065..47e2667 100644
--- a/core/fxge/cfx_gemodule.h
+++ b/core/fxge/cfx_gemodule.h
@@ -14,26 +14,31 @@
 
 class CFX_GEModule {
  public:
-  static CFX_GEModule* Get();
+  class PlatformIface {
+   public:
+    static std::unique_ptr<PlatformIface> Create();
+    virtual ~PlatformIface() {}
+
+    virtual void Init() = 0;
+  };
+
+  static void Create(const char** pUserFontPaths);
   static void Destroy();
+  static CFX_GEModule* Get();
 
-  void Init(const char** pUserFontPaths);
-  CFX_FontCache* GetFontCache();
-  CFX_FontMgr* GetFontMgr() { return m_pFontMgr.get(); }
-
-  void* GetPlatformData() { return m_pPlatformData; }
+  CFX_FontCache* GetFontCache() const { return m_pFontCache.get(); }
+  CFX_FontMgr* GetFontMgr() const { return m_pFontMgr.get(); }
+  PlatformIface* GetPlatform() const { return m_pPlatform.get(); }
+  const char** GetUserFontPaths() const { return m_pUserFontPaths; }
 
  private:
-  CFX_GEModule();
+  explicit CFX_GEModule(const char** pUserFontPaths);
   ~CFX_GEModule();
 
-  void InitPlatform();
-  void DestroyPlatform();
-
-  std::unique_ptr<CFX_FontCache> m_pFontCache;
-  std::unique_ptr<CFX_FontMgr> m_pFontMgr;
-  void* m_pPlatformData;
-  const char** m_pUserFontPaths;
+  std::unique_ptr<PlatformIface> const m_pPlatform;
+  std::unique_ptr<CFX_FontMgr> const m_pFontMgr;
+  std::unique_ptr<CFX_FontCache> const m_pFontCache;
+  const char** const m_pUserFontPaths;
 };
 
 #endif  // CORE_FXGE_CFX_GEMODULE_H_
diff --git a/core/fxge/cfx_glyphbitmap.cpp b/core/fxge/cfx_glyphbitmap.cpp
new file mode 100644
index 0000000..cc28d51
--- /dev/null
+++ b/core/fxge/cfx_glyphbitmap.cpp
@@ -0,0 +1,12 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_glyphbitmap.h"
+
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+CFX_GlyphBitmap::CFX_GlyphBitmap(int left, int top)
+    : m_Left(left), m_Top(top), m_pBitmap(pdfium::MakeRetain<CFX_DIBitmap>()) {}
+
+CFX_GlyphBitmap::~CFX_GlyphBitmap() = default;
diff --git a/core/fxge/cfx_glyphbitmap.h b/core/fxge/cfx_glyphbitmap.h
new file mode 100644
index 0000000..81a0547
--- /dev/null
+++ b/core/fxge/cfx_glyphbitmap.h
@@ -0,0 +1,34 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_CFX_GLYPHBITMAP_H_
+#define CORE_FXGE_CFX_GLYPHBITMAP_H_
+
+#include <vector>
+
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_DIBitmap;
+
+class CFX_GlyphBitmap {
+ public:
+  CFX_GlyphBitmap(int left, int top);
+  ~CFX_GlyphBitmap();
+
+  CFX_GlyphBitmap(const CFX_GlyphBitmap&) = delete;
+  CFX_GlyphBitmap& operator=(const CFX_GlyphBitmap&) = delete;
+
+  const RetainPtr<CFX_DIBitmap>& GetBitmap() const { return m_pBitmap; }
+  int left() const { return m_Left; }
+  int top() const { return m_Top; }
+
+ private:
+  const int m_Left;
+  const int m_Top;
+  RetainPtr<CFX_DIBitmap> m_pBitmap;
+};
+
+#endif  // CORE_FXGE_CFX_GLYPHBITMAP_H_
diff --git a/core/fxge/cfx_glyphcache.cpp b/core/fxge/cfx_glyphcache.cpp
new file mode 100644
index 0000000..ec9c886
--- /dev/null
+++ b/core/fxge/cfx_glyphcache.cpp
@@ -0,0 +1,364 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/cfx_glyphcache.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_glyphbitmap.h"
+#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_substfont.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_freetype.h"
+#include "core/fxge/render_defines.h"
+#include "core/fxge/scoped_font_transform.h"
+#include "third_party/base/numerics/safe_math.h"
+#include "third_party/base/ptr_util.h"
+
+#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+#if defined(OS_WIN)
+#include "third_party/skia/include/core/SkFontMgr.h"
+#include "third_party/skia/include/ports/SkFontMgr_empty.h"
+#endif
+#endif
+
+namespace {
+
+constexpr uint32_t kInvalidGlyphIndex = static_cast<uint32_t>(-1);
+
+constexpr int kMaxGlyphDimension = 2048;
+
+struct UniqueKeyGen {
+  void Generate(int count, ...);
+
+  int key_len_;
+  char key_[128];
+};
+
+void 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*>(key_)[i] = p;
+  }
+  va_end(argList);
+  key_len_ = count * sizeof(uint32_t);
+}
+
+void GenKey(UniqueKeyGen* pKeyGen,
+            const CFX_Font* pFont,
+            const CFX_Matrix& matrix,
+            uint32_t dest_width,
+            int anti_alias,
+            bool bNative) {
+  int nMatrixA = static_cast<int>(matrix.a * 10000);
+  int nMatrixB = static_cast<int>(matrix.b * 10000);
+  int nMatrixC = static_cast<int>(matrix.c * 10000);
+  int nMatrixD = static_cast<int>(matrix.d * 10000);
+
+  if (bNative) {
+    if (pFont->GetSubstFont()) {
+      pKeyGen->Generate(10, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
+                        anti_alias, pFont->GetSubstFont()->m_Weight,
+                        pFont->GetSubstFont()->m_ItalicAngle,
+                        pFont->IsVertical(), 3);
+    } else {
+      pKeyGen->Generate(7, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
+                        anti_alias, 3);
+    }
+  } else {
+    if (pFont->GetSubstFont()) {
+      pKeyGen->Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
+                        anti_alias, pFont->GetSubstFont()->m_Weight,
+                        pFont->GetSubstFont()->m_ItalicAngle,
+                        pFont->IsVertical());
+    } else {
+      pKeyGen->Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
+                        anti_alias);
+    }
+  }
+}
+
+}  // namespace
+
+CFX_GlyphCache::CFX_GlyphCache(RetainPtr<CFX_Face> face) : m_Face(face) {}
+
+CFX_GlyphCache::~CFX_GlyphCache() = default;
+
+std::unique_ptr<CFX_GlyphBitmap> CFX_GlyphCache::RenderGlyph(
+    const CFX_Font* pFont,
+    uint32_t glyph_index,
+    bool bFontStyle,
+    const CFX_Matrix& matrix,
+    uint32_t dest_width,
+    int anti_alias) {
+  if (!GetFaceRec())
+    return nullptr;
+
+  FT_Matrix ft_matrix;
+  ft_matrix.xx = matrix.a / 64 * 65536;
+  ft_matrix.xy = matrix.c / 64 * 65536;
+  ft_matrix.yx = matrix.b / 64 * 65536;
+  ft_matrix.yy = matrix.d / 64 * 65536;
+  bool bUseCJKSubFont = false;
+  const CFX_SubstFont* pSubstFont = pFont->GetSubstFont();
+  if (pSubstFont) {
+    bUseCJKSubFont = pSubstFont->m_bSubstCJK && bFontStyle;
+    int skew = 0;
+    if (bUseCJKSubFont)
+      skew = pSubstFont->m_bItalicCJK ? -15 : 0;
+    else
+      skew = pSubstFont->m_ItalicAngle;
+    if (skew) {
+      // |skew| is nonpositive so |-skew| is used as the index. We need to make
+      // sure |skew| != INT_MIN since -INT_MIN is undefined.
+      if (skew <= 0 && skew != std::numeric_limits<int>::min() &&
+          static_cast<size_t>(-skew) < CFX_Font::kAngleSkewArraySize) {
+        skew = -CFX_Font::s_AngleSkew[-skew];
+      } else {
+        skew = -58;
+      }
+      if (pFont->IsVertical())
+        ft_matrix.yx += ft_matrix.yy * skew / 100;
+      else
+        ft_matrix.xy -= ft_matrix.xx * skew / 100;
+    }
+    if (pSubstFont->m_bFlagMM) {
+      pFont->AdjustMMParams(glyph_index, dest_width,
+                            pFont->GetSubstFont()->m_Weight);
+    }
+  }
+  ScopedFontTransform scoped_transform(GetFace(), &ft_matrix);
+  int load_flags = (GetFaceRec()->face_flags & FT_FACE_FLAG_SFNT)
+                       ? FT_LOAD_NO_BITMAP
+                       : (FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
+  int error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags);
+  if (error) {
+    // if an error is returned, try to reload glyphs without hinting.
+    if (load_flags & FT_LOAD_NO_HINTING || load_flags & FT_LOAD_NO_SCALE)
+      return nullptr;
+
+    load_flags |= FT_LOAD_NO_HINTING;
+    error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags);
+    if (error)
+      return nullptr;
+  }
+  int weight = 0;
+  if (bUseCJKSubFont)
+    weight = pSubstFont->m_WeightCJK;
+  else
+    weight = pSubstFont ? pSubstFont->m_Weight : 0;
+  if (pSubstFont && !pSubstFont->m_bFlagMM && weight > 400) {
+    uint32_t index = (weight - 400) / 10;
+    if (index >= CFX_Font::kWeightPowArraySize)
+      return nullptr;
+    pdfium::base::CheckedNumeric<signed long> level = 0;
+    if (pSubstFont->m_Charset == FX_CHARSET_ShiftJIS)
+      level = CFX_Font::s_WeightPow_SHIFTJIS[index] * 2;
+    else
+      level = CFX_Font::s_WeightPow_11[index];
+
+    level = level *
+            (abs(static_cast<int>(ft_matrix.xx)) +
+             abs(static_cast<int>(ft_matrix.xy))) /
+            36655;
+    FT_Outline_Embolden(FXFT_Get_Glyph_Outline(GetFaceRec()),
+                        level.ValueOrDefault(0));
+  }
+  FT_Library_SetLcdFilter(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(),
+                          FT_LCD_FILTER_DEFAULT);
+  error = FXFT_Render_Glyph(GetFaceRec(), anti_alias);
+  if (error)
+    return nullptr;
+
+  int bmwidth = FXFT_Get_Bitmap_Width(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
+  int bmheight = FXFT_Get_Bitmap_Rows(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
+  if (bmwidth > kMaxGlyphDimension || bmheight > kMaxGlyphDimension)
+    return nullptr;
+  int dib_width = bmwidth;
+  auto pGlyphBitmap = pdfium::MakeUnique<CFX_GlyphBitmap>(
+      FXFT_Get_Glyph_BitmapLeft(GetFaceRec()),
+      FXFT_Get_Glyph_BitmapTop(GetFaceRec()));
+  pGlyphBitmap->GetBitmap()->Create(
+      dib_width, bmheight,
+      anti_alias == FT_RENDER_MODE_MONO ? FXDIB_1bppMask : FXDIB_8bppMask);
+  int dest_pitch = pGlyphBitmap->GetBitmap()->GetPitch();
+  int src_pitch = FXFT_Get_Bitmap_Pitch(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
+  uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->GetBuffer();
+  uint8_t* pSrcBuf = static_cast<uint8_t*>(
+      FXFT_Get_Bitmap_Buffer(FXFT_Get_Glyph_Bitmap(GetFaceRec())));
+  if (anti_alias != FT_RENDER_MODE_MONO &&
+      FXFT_Get_Bitmap_PixelMode(FXFT_Get_Glyph_Bitmap(GetFaceRec())) ==
+          FT_PIXEL_MODE_MONO) {
+    int bytes = anti_alias == FT_RENDER_MODE_LCD ? 3 : 1;
+    for (int i = 0; i < bmheight; i++) {
+      for (int n = 0; n < bmwidth; n++) {
+        uint8_t data =
+            (pSrcBuf[i * src_pitch + n / 8] & (0x80 >> (n % 8))) ? 255 : 0;
+        for (int b = 0; b < bytes; b++)
+          pDestBuf[i * dest_pitch + n * bytes + b] = data;
+      }
+    }
+  } else {
+    memset(pDestBuf, 0, dest_pitch * bmheight);
+    int rowbytes = std::min(abs(src_pitch), dest_pitch);
+    for (int row = 0; row < bmheight; row++)
+      memcpy(pDestBuf + row * dest_pitch, pSrcBuf + row * src_pitch, rowbytes);
+  }
+  return pGlyphBitmap;
+}
+
+const CFX_PathData* CFX_GlyphCache::LoadGlyphPath(const CFX_Font* pFont,
+                                                  uint32_t glyph_index,
+                                                  uint32_t dest_width) {
+  if (!GetFaceRec() || glyph_index == kInvalidGlyphIndex)
+    return nullptr;
+
+  const auto* pSubstFont = pFont->GetSubstFont();
+  int weight = pSubstFont ? pSubstFont->m_Weight : 0;
+  int angle = pSubstFont ? pSubstFont->m_ItalicAngle : 0;
+  bool vertical = pSubstFont && pFont->IsVertical();
+  const PathMapKey key =
+      std::make_tuple(glyph_index, dest_width, weight, angle, vertical);
+  auto it = m_PathMap.find(key);
+  if (it != m_PathMap.end())
+    return it->second.get();
+
+  CFX_PathData* pGlyphPath = pFont->LoadGlyphPathImpl(glyph_index, dest_width);
+  m_PathMap[key] = std::unique_ptr<CFX_PathData>(pGlyphPath);
+  return pGlyphPath;
+}
+
+const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap(const CFX_Font* pFont,
+                                                       uint32_t glyph_index,
+                                                       bool bFontStyle,
+                                                       const CFX_Matrix& matrix,
+                                                       uint32_t dest_width,
+                                                       int anti_alias,
+                                                       int* pTextFlags) {
+  if (glyph_index == kInvalidGlyphIndex)
+    return nullptr;
+
+  UniqueKeyGen keygen;
+#if defined(OS_MACOSX)
+  const bool bNative = !(*pTextFlags & FXTEXT_NO_NATIVETEXT);
+#else
+  const bool bNative = false;
+#endif
+  GenKey(&keygen, pFont, matrix, dest_width, anti_alias, bNative);
+  ByteString FaceGlyphsKey(keygen.key_, keygen.key_len_);
+
+#if defined(OS_MACOSX) && !defined _SKIA_SUPPORT_ && \
+    !defined _SKIA_SUPPORT_PATHS_
+  const bool bDoLookUp = !!(*pTextFlags & FXTEXT_NO_NATIVETEXT);
+#else
+  const bool bDoLookUp = true;
+#endif
+  if (bDoLookUp) {
+    return LookUpGlyphBitmap(pFont, matrix, FaceGlyphsKey, glyph_index,
+                             bFontStyle, dest_width, anti_alias);
+  }
+
+#if defined(OS_MACOSX) && !defined _SKIA_SUPPORT_ && \
+    !defined _SKIA_SUPPORT_PATHS_
+  std::unique_ptr<CFX_GlyphBitmap> pGlyphBitmap;
+  auto it = m_SizeMap.find(FaceGlyphsKey);
+  if (it != m_SizeMap.end()) {
+    SizeGlyphCache* pSizeCache = &(it->second);
+    auto it2 = pSizeCache->find(glyph_index);
+    if (it2 != pSizeCache->end())
+      return it2->second.get();
+
+    pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, matrix,
+                                          dest_width, anti_alias);
+    if (pGlyphBitmap) {
+      CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
+      (*pSizeCache)[glyph_index] = std::move(pGlyphBitmap);
+      return pResult;
+    }
+  } else {
+    pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, matrix,
+                                          dest_width, anti_alias);
+    if (pGlyphBitmap) {
+      CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
+
+      SizeGlyphCache cache;
+      cache[glyph_index] = std::move(pGlyphBitmap);
+
+      m_SizeMap[FaceGlyphsKey] = std::move(cache);
+      return pResult;
+    }
+  }
+  GenKey(&keygen, pFont, matrix, dest_width, anti_alias, /*bNative=*/false);
+  ByteString FaceGlyphsKey2(keygen.key_, keygen.key_len_);
+  *pTextFlags |= FXTEXT_NO_NATIVETEXT;
+  return LookUpGlyphBitmap(pFont, matrix, FaceGlyphsKey2, glyph_index,
+                           bFontStyle, dest_width, anti_alias);
+#endif
+}
+
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+CFX_TypeFace* CFX_GlyphCache::GetDeviceCache(const CFX_Font* pFont) {
+  if (!m_pTypeface) {
+    pdfium::span<const uint8_t> span = pFont->GetFontSpan();
+    m_pTypeface = SkTypeface::MakeFromStream(
+        pdfium::MakeUnique<SkMemoryStream>(span.data(), span.size()));
+  }
+#if defined(OS_WIN)
+  if (!m_pTypeface) {
+    sk_sp<SkFontMgr> customMgr(SkFontMgr_New_Custom_Empty());
+    pdfium::span<const uint8_t> span = pFont->GetFontSpan();
+    m_pTypeface = customMgr->makeFromStream(
+        pdfium::MakeUnique<SkMemoryStream>(span.data(), span.size()));
+  }
+#endif  // defined(OS_WIN)
+  return m_pTypeface.get();
+}
+#endif  // defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+
+#if !defined(OS_MACOSX)
+void CFX_GlyphCache::InitPlatform() {}
+#endif
+
+CFX_GlyphBitmap* CFX_GlyphCache::LookUpGlyphBitmap(
+    const CFX_Font* pFont,
+    const CFX_Matrix& matrix,
+    const ByteString& FaceGlyphsKey,
+    uint32_t glyph_index,
+    bool bFontStyle,
+    uint32_t dest_width,
+    int anti_alias) {
+  SizeGlyphCache* pSizeCache;
+  auto it = m_SizeMap.find(FaceGlyphsKey);
+  if (it == m_SizeMap.end()) {
+    m_SizeMap[FaceGlyphsKey] = SizeGlyphCache();
+    pSizeCache = &(m_SizeMap[FaceGlyphsKey]);
+  } else {
+    pSizeCache = &(it->second);
+  }
+
+  auto it2 = pSizeCache->find(glyph_index);
+  if (it2 != pSizeCache->end())
+    return it2->second.get();
+
+  std::unique_ptr<CFX_GlyphBitmap> pGlyphBitmap = RenderGlyph(
+      pFont, glyph_index, bFontStyle, matrix, dest_width, anti_alias);
+  CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
+  (*pSizeCache)[glyph_index] = std::move(pGlyphBitmap);
+  return pResult;
+}
diff --git a/core/fxge/cfx_glyphcache.h b/core/fxge/cfx_glyphcache.h
new file mode 100644
index 0000000..5a24424
--- /dev/null
+++ b/core/fxge/cfx_glyphcache.h
@@ -0,0 +1,91 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_CFX_GLYPHCACHE_H_
+#define CORE_FXGE_CFX_GLYPHCACHE_H_
+
+#include <map>
+#include <memory>
+#include <tuple>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_face.h"
+
+#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+#include "core/fxge/fx_font.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+#endif
+
+class CFX_Font;
+class CFX_GlyphBitmap;
+class CFX_Matrix;
+class CFX_PathData;
+
+class CFX_GlyphCache : public Retainable, public Observable {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  ~CFX_GlyphCache() override;
+
+  const CFX_GlyphBitmap* LoadGlyphBitmap(const CFX_Font* pFont,
+                                         uint32_t glyph_index,
+                                         bool bFontStyle,
+                                         const CFX_Matrix& matrix,
+                                         uint32_t dest_width,
+                                         int anti_alias,
+                                         int* pTextFlags);
+  const CFX_PathData* LoadGlyphPath(const CFX_Font* pFont,
+                                    uint32_t glyph_index,
+                                    uint32_t dest_width);
+
+  RetainPtr<CFX_Face> GetFace() { return m_Face; }
+  FXFT_FaceRec* GetFaceRec() { return m_Face ? m_Face->GetRec() : nullptr; }
+
+#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+  CFX_TypeFace* GetDeviceCache(const CFX_Font* pFont);
+#endif
+
+ private:
+  explicit CFX_GlyphCache(RetainPtr<CFX_Face> face);
+
+  using SizeGlyphCache = std::map<uint32_t, std::unique_ptr<CFX_GlyphBitmap>>;
+  // <glyph_index, width, weight, angle, vertical>
+  using PathMapKey = std::tuple<uint32_t, uint32_t, int, int, bool>;
+
+  std::unique_ptr<CFX_GlyphBitmap> RenderGlyph(const CFX_Font* pFont,
+                                               uint32_t glyph_index,
+                                               bool bFontStyle,
+                                               const CFX_Matrix& matrix,
+                                               uint32_t dest_width,
+                                               int anti_alias);
+  std::unique_ptr<CFX_GlyphBitmap> RenderGlyph_Nativetext(
+      const CFX_Font* pFont,
+      uint32_t glyph_index,
+      const CFX_Matrix& matrix,
+      uint32_t dest_width,
+      int anti_alias);
+  CFX_GlyphBitmap* LookUpGlyphBitmap(const CFX_Font* pFont,
+                                     const CFX_Matrix& matrix,
+                                     const ByteString& FaceGlyphsKey,
+                                     uint32_t glyph_index,
+                                     bool bFontStyle,
+                                     uint32_t dest_width,
+                                     int anti_alias);
+  void InitPlatform();
+  void DestroyPlatform();
+
+  RetainPtr<CFX_Face> const m_Face;
+  std::map<ByteString, SizeGlyphCache> m_SizeMap;
+  std::map<PathMapKey, std::unique_ptr<CFX_PathData>> m_PathMap;
+#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+  sk_sp<SkTypeface> m_pTypeface;
+#endif
+};
+
+#endif  //  CORE_FXGE_CFX_GLYPHCACHE_H_
diff --git a/core/fxge/cfx_graphstate.cpp b/core/fxge/cfx_graphstate.cpp
index ad6b5fc..726c0c6 100644
--- a/core/fxge/cfx_graphstate.cpp
+++ b/core/fxge/cfx_graphstate.cpp
@@ -6,25 +6,27 @@
 
 #include "core/fxge/cfx_graphstate.h"
 
-#include "core/fpdfapi/parser/cpdf_array.h"
+#include <utility>
 
-CFX_GraphState::CFX_GraphState() {}
+CFX_GraphState::CFX_GraphState() = default;
 
 CFX_GraphState::CFX_GraphState(const CFX_GraphState& that)
     : m_Ref(that.m_Ref) {}
 
-CFX_GraphState::~CFX_GraphState() {}
+CFX_GraphState::~CFX_GraphState() = default;
 
 void CFX_GraphState::Emplace() {
   m_Ref.Emplace();
 }
 
-void CFX_GraphState::SetLineDash(CPDF_Array* pArray, float phase, float scale) {
+void CFX_GraphState::SetLineDash(std::vector<float> dashes,
+                                 float phase,
+                                 float scale) {
   CFX_GraphStateData* pData = m_Ref.GetPrivateCopy();
   pData->m_DashPhase = phase * scale;
-  pData->SetDashCount(static_cast<int>(pArray->GetCount()));
-  for (size_t i = 0; i < pArray->GetCount(); i++)
-    pData->m_DashArray[i] = pArray->GetNumberAt(i) * scale;
+  for (float& val : dashes)
+    val *= scale;
+  pData->m_DashArray = std::move(dashes);
 }
 
 float CFX_GraphState::GetLineWidth() const {
diff --git a/core/fxge/cfx_graphstate.h b/core/fxge/cfx_graphstate.h
index bf4fccd..20955f8 100644
--- a/core/fxge/cfx_graphstate.h
+++ b/core/fxge/cfx_graphstate.h
@@ -7,11 +7,11 @@
 #ifndef CORE_FXGE_CFX_GRAPHSTATE_H_
 #define CORE_FXGE_CFX_GRAPHSTATE_H_
 
+#include <vector>
+
 #include "core/fxcrt/shared_copy_on_write.h"
 #include "core/fxge/cfx_graphstatedata.h"
 
-class CPDF_Array;
-
 class CFX_GraphState {
  public:
   CFX_GraphState();
@@ -20,7 +20,7 @@
 
   void Emplace();
 
-  void SetLineDash(CPDF_Array* pArray, float phase, float scale);
+  void SetLineDash(std::vector<float> dashes, float phase, float scale);
 
   float GetLineWidth() const;
   void SetLineWidth(float width);
@@ -38,7 +38,7 @@
   const CFX_GraphStateData* GetObject() const { return m_Ref.GetObject(); }
 
  private:
-  SharedCopyOnWrite<CFX_GraphStateData> m_Ref;
+  SharedCopyOnWrite<CFX_RetainableGraphStateData> m_Ref;
 };
 
 #endif  // CORE_FXGE_CFX_GRAPHSTATE_H_
diff --git a/core/fxge/cfx_graphstatedata.cpp b/core/fxge/cfx_graphstatedata.cpp
index 82fede1..43f6254 100644
--- a/core/fxge/cfx_graphstatedata.cpp
+++ b/core/fxge/cfx_graphstatedata.cpp
@@ -6,47 +6,34 @@
 
 #include "core/fxge/cfx_graphstatedata.h"
 
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_system.h"
+CFX_GraphStateData::CFX_GraphStateData() = default;
 
-CFX_GraphStateData::CFX_GraphStateData()
-    : m_LineCap(LineCapButt),
-      m_DashCount(0),
-      m_DashArray(nullptr),
-      m_DashPhase(0),
-      m_LineJoin(LineJoinMiter),
-      m_MiterLimit(10 * 1.0f),
-      m_LineWidth(1.0f) {}
+CFX_GraphStateData::CFX_GraphStateData(const CFX_GraphStateData& src) = default;
 
-CFX_GraphStateData::CFX_GraphStateData(const CFX_GraphStateData& src) {
-  m_DashArray = nullptr;
-  Copy(src);
-}
+CFX_GraphStateData::CFX_GraphStateData(CFX_GraphStateData&& src) = default;
 
-void CFX_GraphStateData::Copy(const CFX_GraphStateData& src) {
-  m_LineCap = src.m_LineCap;
-  m_DashCount = src.m_DashCount;
-  FX_Free(m_DashArray);
-  m_DashArray = nullptr;
-  m_DashPhase = src.m_DashPhase;
-  m_LineJoin = src.m_LineJoin;
-  m_MiterLimit = src.m_MiterLimit;
-  m_LineWidth = src.m_LineWidth;
-  if (m_DashCount) {
-    m_DashArray = FX_Alloc(float, m_DashCount);
-    memcpy(m_DashArray, src.m_DashArray, m_DashCount * sizeof(float));
-  }
-}
+CFX_GraphStateData::~CFX_GraphStateData() = default;
 
-CFX_GraphStateData::~CFX_GraphStateData() {
-  FX_Free(m_DashArray);
-}
+CFX_GraphStateData& CFX_GraphStateData::operator=(
+    const CFX_GraphStateData& that) = default;
 
-void CFX_GraphStateData::SetDashCount(int count) {
-  FX_Free(m_DashArray);
-  m_DashArray = nullptr;
-  m_DashCount = count;
-  if (count == 0)
-    return;
-  m_DashArray = FX_Alloc(float, count);
+CFX_GraphStateData& CFX_GraphStateData::operator=(CFX_GraphStateData&& that) =
+    default;
+
+CFX_RetainableGraphStateData::CFX_RetainableGraphStateData() = default;
+
+// Note: can't default the copy constructor since Retainable has a deleted
+// copy constructor (as it should). Instead, we want the default Retainable
+// constructor to be invoked so as to create a copy with a ref-count of 1 as
+// of the time it is created, then populate the remainder of the members from
+// the |src| object.
+CFX_RetainableGraphStateData::CFX_RetainableGraphStateData(
+    const CFX_RetainableGraphStateData& src)
+    : CFX_GraphStateData(src) {}
+
+CFX_RetainableGraphStateData::~CFX_RetainableGraphStateData() = default;
+
+RetainPtr<CFX_RetainableGraphStateData> CFX_RetainableGraphStateData::Clone()
+    const {
+  return pdfium::MakeRetain<CFX_RetainableGraphStateData>(*this);
 }
diff --git a/core/fxge/cfx_graphstatedata.h b/core/fxge/cfx_graphstatedata.h
index 1afff83..e8ece9f 100644
--- a/core/fxge/cfx_graphstatedata.h
+++ b/core/fxge/cfx_graphstatedata.h
@@ -7,33 +7,53 @@
 #ifndef CORE_FXGE_CFX_GRAPHSTATEDATA_H_
 #define CORE_FXGE_CFX_GRAPHSTATEDATA_H_
 
+#include <vector>
+
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 
-class CFX_GraphStateData : public Retainable {
+class CFX_GraphStateData {
  public:
-  enum LineCap { LineCapButt = 0, LineCapRound = 1, LineCapSquare = 2 };
+  enum LineCap : uint8_t {
+    LineCapButt = 0,
+    LineCapRound = 1,
+    LineCapSquare = 2
+  };
+
+  enum LineJoin : uint8_t {
+    LineJoinMiter = 0,
+    LineJoinRound = 1,
+    LineJoinBevel = 2
+  };
 
   CFX_GraphStateData();
   CFX_GraphStateData(const CFX_GraphStateData& src);
-  ~CFX_GraphStateData() override;
+  CFX_GraphStateData(CFX_GraphStateData&& src);
+  ~CFX_GraphStateData();
 
-  void Copy(const CFX_GraphStateData& src);
-  void SetDashCount(int count);
+  CFX_GraphStateData& operator=(const CFX_GraphStateData& that);
+  CFX_GraphStateData& operator=(CFX_GraphStateData&& that);
 
-  LineCap m_LineCap;
-  int m_DashCount;
-  float* m_DashArray;
-  float m_DashPhase;
+  LineCap m_LineCap = LineCapButt;
+  LineJoin m_LineJoin = LineJoinMiter;
+  float m_DashPhase = 0.0f;
+  float m_MiterLimit = 10.0f;
+  float m_LineWidth = 1.0f;
+  std::vector<float> m_DashArray;
+};
 
-  enum LineJoin {
-    LineJoinMiter = 0,
-    LineJoinRound = 1,
-    LineJoinBevel = 2,
-  };
-  LineJoin m_LineJoin;
-  float m_MiterLimit;
-  float m_LineWidth;
+class CFX_RetainableGraphStateData : public Retainable,
+                                     public CFX_GraphStateData {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  RetainPtr<CFX_RetainableGraphStateData> Clone() const;
+
+ private:
+  CFX_RetainableGraphStateData();
+  CFX_RetainableGraphStateData(const CFX_RetainableGraphStateData& src);
+  ~CFX_RetainableGraphStateData() override;
 };
 
 #endif  // CORE_FXGE_CFX_GRAPHSTATEDATA_H_
diff --git a/core/fxge/cfx_pathdata.cpp b/core/fxge/cfx_pathdata.cpp
index 4ac5cf6..55ce854 100644
--- a/core/fxge/cfx_pathdata.cpp
+++ b/core/fxge/cfx_pathdata.cpp
@@ -11,6 +11,25 @@
 
 namespace {
 
+bool IsFoldingVerticalLine(const CFX_PointF& a,
+                           const CFX_PointF& b,
+                           const CFX_PointF& c) {
+  return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0;
+}
+
+bool IsFoldingHorizontalLine(const CFX_PointF& a,
+                             const CFX_PointF& b,
+                             const CFX_PointF& c) {
+  return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0;
+}
+
+bool IsFoldingDiagonalLine(const CFX_PointF& a,
+                           const CFX_PointF& b,
+                           const CFX_PointF& c) {
+  return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y &&
+         (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x);
+}
+
 void UpdateLineEndPoints(CFX_FloatRect* rect,
                          const CFX_PointF& start_pos,
                          const CFX_PointF& end_pos,
@@ -166,11 +185,13 @@
 
 FX_PATHPOINT::~FX_PATHPOINT() = default;
 
-CFX_PathData::CFX_PathData() {}
+CFX_PathData::CFX_PathData() = default;
 
-CFX_PathData::~CFX_PathData() {}
+CFX_PathData::CFX_PathData(const CFX_PathData& src) = default;
 
-CFX_PathData::CFX_PathData(const CFX_PathData& src) : m_Points(src.m_Points) {}
+CFX_PathData::CFX_PathData(CFX_PathData&& src) = default;
+
+CFX_PathData::~CFX_PathData() = default;
 
 void CFX_PathData::Clear() {
   m_Points.clear();
@@ -210,6 +231,10 @@
   AppendPoint(pt2, FXPT_TYPE::LineTo, false);
 }
 
+void CFX_PathData::AppendFloatRect(const CFX_FloatRect& rect) {
+  return AppendRect(rect.left, rect.bottom, rect.right, rect.top);
+}
+
 void CFX_PathData::AppendRect(float left,
                               float bottom,
                               float right,
@@ -287,11 +312,9 @@
   return rect;
 }
 
-void CFX_PathData::Transform(const CFX_Matrix* pMatrix) {
-  if (!pMatrix)
-    return;
+void CFX_PathData::Transform(const CFX_Matrix& matrix) {
   for (auto& point : m_Points)
-    point.m_Point = pMatrix->Transform(point.m_Point);
+    point.m_Point = matrix.Transform(point.m_Point);
 }
 
 bool CFX_PathData::GetZeroAreaPath(const CFX_Matrix* pMatrix,
@@ -354,59 +377,47 @@
   }
 
   int startPoint = 0;
-  int next = 0;
   for (size_t i = 0; i < m_Points.size(); i++) {
     FXPT_TYPE point_type = m_Points[i].m_Type;
     if (point_type == FXPT_TYPE::MoveTo) {
       startPoint = i;
-    } else if (point_type == FXPT_TYPE::LineTo) {
-      next = (i + 1 - startPoint) % (m_Points.size() - startPoint) + startPoint;
-      if (m_Points[next].m_Type != FXPT_TYPE::BezierTo &&
-          m_Points[next].m_Type != FXPT_TYPE::MoveTo) {
-        if ((m_Points[i - 1].m_Point.x == m_Points[i].m_Point.x &&
-             m_Points[i].m_Point.x == m_Points[next].m_Point.x) &&
-            ((m_Points[i].m_Point.y - m_Points[i - 1].m_Point.y) *
-                 (m_Points[i].m_Point.y - m_Points[next].m_Point.y) >
-             0)) {
-          int pre = i;
-          if (fabs(m_Points[i].m_Point.y - m_Points[i - 1].m_Point.y) <
-              fabs(m_Points[i].m_Point.y - m_Points[next].m_Point.y)) {
-            pre--;
-            next--;
-          }
+      continue;
+    }
 
-          NewPath->AppendPoint(m_Points[pre].m_Point, FXPT_TYPE::MoveTo, false);
-          NewPath->AppendPoint(m_Points[next].m_Point, FXPT_TYPE::LineTo,
-                               false);
-        } else if ((m_Points[i - 1].m_Point.y == m_Points[i].m_Point.y &&
-                    m_Points[i].m_Point.y == m_Points[next].m_Point.y) &&
-                   ((m_Points[i].m_Point.x - m_Points[i - 1].m_Point.x) *
-                        (m_Points[i].m_Point.x - m_Points[next].m_Point.x) >
-                    0)) {
-          int pre = i;
-          if (fabs(m_Points[i].m_Point.x - m_Points[i - 1].m_Point.x) <
-              fabs(m_Points[i].m_Point.x - m_Points[next].m_Point.x)) {
-            pre--;
-            next--;
-          }
-
-          NewPath->AppendPoint(m_Points[pre].m_Point, FXPT_TYPE::MoveTo, false);
-          NewPath->AppendPoint(m_Points[next].m_Point, FXPT_TYPE::LineTo,
-                               false);
-        } else if (m_Points[i - 1].m_Type == FXPT_TYPE::MoveTo &&
-                   m_Points[next].m_Type == FXPT_TYPE::LineTo &&
-                   m_Points[i - 1].m_Point == m_Points[next].m_Point &&
-                   m_Points[next].m_CloseFigure) {
-          NewPath->AppendPoint(m_Points[i - 1].m_Point, FXPT_TYPE::MoveTo,
-                               false);
-          NewPath->AppendPoint(m_Points[i].m_Point, FXPT_TYPE::LineTo, false);
-          *bThin = true;
-        }
-      }
-    } else if (point_type == FXPT_TYPE::BezierTo) {
+    if (point_type == FXPT_TYPE::BezierTo) {
       i += 2;
       continue;
     }
+
+    ASSERT(point_type == FXPT_TYPE::LineTo);
+    int next_index =
+        (i + 1 - startPoint) % (m_Points.size() - startPoint) + startPoint;
+    const FX_PATHPOINT& next = m_Points[next_index];
+    if (next.m_Type == FXPT_TYPE::BezierTo || next.m_Type == FXPT_TYPE::MoveTo)
+      continue;
+
+    const FX_PATHPOINT& prev = m_Points[i - 1];
+    const FX_PATHPOINT& cur = m_Points[i];
+    if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
+      bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) <
+                      fabs(cur.m_Point.y - next.m_Point.y);
+      const FX_PATHPOINT& start = use_prev ? prev : cur;
+      const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
+      NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false);
+      NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false);
+      continue;
+    }
+
+    if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) ||
+        IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
+      bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) <
+                      fabs(cur.m_Point.x - next.m_Point.x);
+      const FX_PATHPOINT& start = use_prev ? prev : cur;
+      const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
+      NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false);
+      NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false);
+      continue;
+    }
   }
 
   size_t new_path_size = NewPath->GetPoints().size();
@@ -492,3 +503,20 @@
   }
   return true;
 }
+
+CFX_RetainablePathData::CFX_RetainablePathData() = default;
+
+// Note: can't default the copy constructor since Retainable<> has a deleted
+// copy constructor (as it should). Instead, we want the default Retainable<>
+// constructor to be invoked so as to create a copy with a ref-count of 1 as
+// of the time it is created, then populate the remainder of the members from
+// the |src| object.
+CFX_RetainablePathData::CFX_RetainablePathData(
+    const CFX_RetainablePathData& src)
+    : CFX_PathData(src) {}
+
+CFX_RetainablePathData::~CFX_RetainablePathData() = default;
+
+RetainPtr<CFX_RetainablePathData> CFX_RetainablePathData::Clone() const {
+  return pdfium::MakeRetain<CFX_RetainablePathData>(*this);
+}
diff --git a/core/fxge/cfx_pathdata.h b/core/fxge/cfx_pathdata.h
index d346ba0..3fd57e2 100644
--- a/core/fxge/cfx_pathdata.h
+++ b/core/fxge/cfx_pathdata.h
@@ -11,7 +11,9 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxcrt/retain_ptr.h"
+
+enum class FXPT_TYPE : uint8_t { LineTo, BezierTo, MoveTo };
 
 class FX_PATHPOINT {
  public:
@@ -29,11 +31,12 @@
   bool m_CloseFigure;
 };
 
-class CFX_PathData : public Retainable {
+class CFX_PathData {
  public:
   CFX_PathData();
   CFX_PathData(const CFX_PathData& src);
-  ~CFX_PathData() override;
+  CFX_PathData(CFX_PathData&& src);
+  ~CFX_PathData();
 
   void Clear();
 
@@ -49,7 +52,7 @@
   CFX_FloatRect GetBoundingBox() const;
   CFX_FloatRect GetBoundingBox(float line_width, float miter_limit) const;
 
-  void Transform(const CFX_Matrix* pMatrix);
+  void Transform(const CFX_Matrix& matrix);
   bool IsRect() const;
   bool GetZeroAreaPath(const CFX_Matrix* pMatrix,
                        bool bAdjust,
@@ -59,13 +62,27 @@
   bool IsRect(const CFX_Matrix* pMatrix, CFX_FloatRect* rect) const;
 
   void Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix);
+  void AppendFloatRect(const CFX_FloatRect& rect);
   void AppendRect(float left, float bottom, float right, float top);
   void AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2);
-  void AppendPoint(const CFX_PointF& pos, FXPT_TYPE type, bool closeFigure);
+  void AppendPoint(const CFX_PointF& point, FXPT_TYPE type, bool closeFigure);
   void ClosePath();
 
  private:
   std::vector<FX_PATHPOINT> m_Points;
 };
 
+class CFX_RetainablePathData final : public Retainable, public CFX_PathData {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  RetainPtr<CFX_RetainablePathData> Clone() const;
+
+ private:
+  CFX_RetainablePathData();
+  CFX_RetainablePathData(const CFX_RetainablePathData& src);
+  ~CFX_RetainablePathData() override;
+};
+
 #endif  // CORE_FXGE_CFX_PATHDATA_H_
diff --git a/core/fxge/cfx_renderdevice.cpp b/core/fxge/cfx_renderdevice.cpp
index 919a7a9..b1474c5 100644
--- a/core/fxge/cfx_renderdevice.cpp
+++ b/core/fxge/cfx_renderdevice.cpp
@@ -9,18 +9,24 @@
 #include <algorithm>
 #include <memory>
 #include <utility>
-#include <vector>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/cfx_color.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_facecache.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_glyphbitmap.h"
+#include "core/fxge/cfx_glyphcache.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
-#include "core/fxge/ifx_renderdevicedriver.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"
 
 #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
 #include "third_party/skia/include/core/SkTypes.h"
@@ -28,28 +34,40 @@
 
 namespace {
 
-void AdjustGlyphSpace(std::vector<FXTEXT_GLYPHPOS>* pGlyphAndPos) {
+void AdjustGlyphSpace(std::vector<TextGlyphPos>* pGlyphAndPos) {
   ASSERT(pGlyphAndPos->size() > 1);
-  std::vector<FXTEXT_GLYPHPOS>& glyphs = *pGlyphAndPos;
+  std::vector<TextGlyphPos>& glyphs = *pGlyphAndPos;
   bool bVertical = glyphs.back().m_Origin.x == glyphs.front().m_Origin.x;
   if (!bVertical && (glyphs.back().m_Origin.y != glyphs.front().m_Origin.y))
     return;
 
   for (size_t i = glyphs.size() - 1; i > 1; --i) {
-    FXTEXT_GLYPHPOS& next = glyphs[i];
+    const TextGlyphPos& next = glyphs[i];
     int next_origin = bVertical ? next.m_Origin.y : next.m_Origin.x;
     float next_origin_f = bVertical ? next.m_fOrigin.y : next.m_fOrigin.x;
 
-    FXTEXT_GLYPHPOS& current = glyphs[i - 1];
+    TextGlyphPos& current = glyphs[i - 1];
     int& current_origin = bVertical ? current.m_Origin.y : current.m_Origin.x;
     float current_origin_f =
         bVertical ? current.m_fOrigin.y : current.m_fOrigin.x;
 
-    int space = next_origin - current_origin;
+    FX_SAFE_INT32 safe_space = next_origin;
+    safe_space -= current_origin;
+    if (!safe_space.IsValid())
+      continue;
+
+    int space = safe_space.ValueOrDie();
     float space_f = next_origin_f - current_origin_f;
     float error = fabs(space_f) - fabs(static_cast<float>(space));
-    if (error > 0.5f)
-      current_origin += space > 0 ? -1 : 1;
+    if (error <= 0.5f)
+      continue;
+
+    FX_SAFE_INT32 safe_origin = current_origin;
+    safe_origin += space > 0 ? -1 : 1;
+    if (!safe_origin.IsValid())
+      continue;
+
+    current_origin = safe_origin.ValueOrDie();
   }
 }
 
@@ -149,7 +167,7 @@
                    int src_alpha) {
   uint8_t back_alpha = dest[3];
   if (back_alpha == 0)
-    FXARGB_SETDIB(dest, FXARGB_MAKE(src_alpha, r, g, b));
+    FXARGB_SETDIB(dest, ArgbEncode(src_alpha, r, g, b));
   else if (src_alpha != 0)
     ApplyDestAlpha(back_alpha, src_alpha, r, g, b, dest);
 }
@@ -334,7 +352,7 @@
 }
 
 bool ShouldDrawDeviceText(const CFX_Font* pFont, uint32_t text_flags) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
   if (text_flags & FXFONT_CIDFONT)
     return false;
 
@@ -350,29 +368,7 @@
 
 }  // namespace
 
-FXTEXT_CHARPOS::FXTEXT_CHARPOS()
-    : m_Unicode(0),
-      m_GlyphIndex(0),
-      m_FontCharWidth(0),
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-      m_ExtGID(0),
-#endif
-      m_FallbackFontPosition(0),
-      m_bGlyphAdjust(false),
-      m_bFontStyle(false) {
-}
-
-FXTEXT_CHARPOS::FXTEXT_CHARPOS(const FXTEXT_CHARPOS&) = default;
-
-FXTEXT_CHARPOS::~FXTEXT_CHARPOS(){};
-
-CFX_RenderDevice::CFX_RenderDevice()
-    : m_pBitmap(nullptr),
-      m_Width(0),
-      m_Height(0),
-      m_bpp(0),
-      m_RenderCaps(0),
-      m_DeviceClass(0) {}
+CFX_RenderDevice::CFX_RenderDevice() = default;
 
 CFX_RenderDevice::~CFX_RenderDevice() {
   RestoreState(false);
@@ -381,6 +377,14 @@
 #endif
 }
 
+// static
+CFX_Matrix CFX_RenderDevice::GetFlipMatrix(float width,
+                                           float height,
+                                           float left,
+                                           float top) {
+  return CFX_Matrix(width, 0, 0, -height, left, top + height);
+}
+
 #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
 void CFX_RenderDevice::Flush(bool release) {
   if (release)
@@ -391,7 +395,7 @@
 #endif
 
 void CFX_RenderDevice::SetDeviceDriver(
-    std::unique_ptr<IFX_RenderDeviceDriver> pDriver) {
+    std::unique_ptr<RenderDeviceDriverIface> pDriver) {
   m_pDeviceDriver = std::move(pDriver);
   InitDeviceInfo();
 }
@@ -401,7 +405,7 @@
   m_Height = m_pDeviceDriver->GetDeviceCaps(FXDC_PIXEL_HEIGHT);
   m_bpp = m_pDeviceDriver->GetDeviceCaps(FXDC_BITS_PIXEL);
   m_RenderCaps = m_pDeviceDriver->GetDeviceCaps(FXDC_RENDER_CAPS);
-  m_DeviceClass = m_pDeviceDriver->GetDeviceCaps(FXDC_DEVICE_CLASS);
+  m_DeviceType = m_pDeviceDriver->GetDeviceType();
   if (!m_pDeviceDriver->GetClipBox(&m_ClipBox)) {
     m_ClipBox.left = 0;
     m_ClipBox.top = 0;
@@ -425,10 +429,6 @@
   return m_pDeviceDriver->GetDeviceCaps(caps_id);
 }
 
-CFX_Matrix CFX_RenderDevice::GetCTM() const {
-  return m_pDeviceDriver->GetCTM();
-}
-
 RetainPtr<CFX_DIBitmap> CFX_RenderDevice::GetBitmap() const {
   return m_pBitmap;
 }
@@ -448,14 +448,18 @@
   }
   if (m_RenderCaps & FXRC_BYTEMASK_OUTPUT)
     return pDIB->Create(width, height, FXDIB_8bppMask);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ || defined _SKIA_SUPPORT_PATHS_
+#if defined(OS_MACOSX) || defined _SKIA_SUPPORT_PATHS_
+  constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32;
+#else
+  constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb;
+#endif
   return pDIB->Create(
       width, height,
-      m_RenderCaps & FXRC_ALPHA_OUTPUT ? FXDIB_Argb : FXDIB_Rgb32);
-#else
-  return pDIB->Create(
-      width, height, m_RenderCaps & FXRC_ALPHA_OUTPUT ? FXDIB_Argb : FXDIB_Rgb);
-#endif
+      m_RenderCaps & FXRC_ALPHA_OUTPUT ? FXDIB_Argb : kPlatformFormat);
+}
+
+void CFX_RenderDevice::SetBaseClip(const FX_RECT& rect) {
+  m_pDeviceDriver->SetBaseClip(rect);
 }
 
 bool CFX_RenderDevice::SetClip_PathFill(const CFX_PathData* pPathData,
@@ -481,13 +485,6 @@
   return true;
 }
 
-bool CFX_RenderDevice::SetClip_Rect(const CFX_RectF& rtClip) {
-  return SetClip_Rect(FX_RECT(static_cast<int32_t>(floor(rtClip.left)),
-                              static_cast<int32_t>(floor(rtClip.top)),
-                              static_cast<int32_t>(ceil(rtClip.right())),
-                              static_cast<int32_t>(ceil(rtClip.bottom()))));
-}
-
 bool CFX_RenderDevice::SetClip_Rect(const FX_RECT& rect) {
   CFX_PathData path;
   path.AppendRect(rect.left, rect.bottom, rect.right, rect.top);
@@ -513,7 +510,7 @@
                                          uint32_t fill_color,
                                          uint32_t stroke_color,
                                          int fill_mode,
-                                         int blend_type) {
+                                         BlendMode blend_type) {
   uint8_t stroke_alpha = pGraphState ? FXARGB_A(stroke_color) : 0;
   uint8_t fill_alpha = (fill_mode & 3) ? FXARGB_A(fill_color) : 0;
   const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints();
@@ -568,7 +565,7 @@
           --rect_i.bottom;
         }
       }
-      if (FillRectWithBlend(&rect_i, fill_color, blend_type))
+      if (FillRectWithBlend(rect_i, fill_color, blend_type))
         return true;
     }
   }
@@ -621,7 +618,7 @@
                                           uint32_t fill_color,
                                           uint32_t stroke_color,
                                           int fill_mode,
-                                          int blend_type) {
+                                          BlendMode blend_type) {
   if (!(m_RenderCaps & FXRC_GET_BITS))
     return false;
   CFX_FloatRect bbox;
@@ -634,32 +631,30 @@
   if (pObject2Device)
     bbox = pObject2Device->TransformRect(bbox);
 
-  CFX_Matrix ctm = GetCTM();
-  float fScaleX = fabs(ctm.a);
-  float fScaleY = fabs(ctm.d);
   FX_RECT rect = bbox.GetOuterRect();
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  auto Backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!CreateCompatibleBitmap(bitmap, FXSYS_round(rect.Width() * fScaleX),
-                              FXSYS_round(rect.Height() * fScaleY))) {
+  if (!rect.Valid())
     return false;
-  }
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  auto backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!CreateCompatibleBitmap(bitmap, rect.Width(), rect.Height()))
+    return false;
+
   if (bitmap->HasAlpha()) {
     bitmap->Clear(0);
-    Backdrop->Copy(bitmap);
+    backdrop->Copy(bitmap);
   } else {
     if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top))
       return false;
-    Backdrop->Copy(bitmap);
+    backdrop->Copy(bitmap);
   }
   CFX_DefaultRenderDevice bitmap_device;
-  bitmap_device.Attach(bitmap, false, Backdrop, true);
+  bitmap_device.Attach(bitmap, false, backdrop, true);
 
   CFX_Matrix matrix;
   if (pObject2Device)
     matrix = *pObject2Device;
   matrix.Translate(-rect.left, -rect.top);
-  matrix.Concat(CFX_Matrix(fScaleX, 0, 0, fScaleY, 0, 0));
   if (!bitmap_device.GetDeviceDriver()->DrawPath(
           pPathData, &matrix, pGraphState, fill_color, stroke_color, fill_mode,
           blend_type)) {
@@ -668,35 +663,34 @@
 #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
   bitmap_device.GetDeviceDriver()->Flush();
 #endif
-  FX_RECT src_rect(0, 0, FXSYS_round(rect.Width() * fScaleX),
-                   FXSYS_round(rect.Height() * fScaleY));
-  return m_pDeviceDriver->SetDIBits(bitmap, 0, &src_rect, rect.left, rect.top,
-                                    FXDIB_BLEND_NORMAL);
+  FX_RECT src_rect(0, 0, rect.Width(), rect.Height());
+  return m_pDeviceDriver->SetDIBits(bitmap, 0, src_rect, rect.left, rect.top,
+                                    BlendMode::kNormal);
 }
 
-bool CFX_RenderDevice::FillRectWithBlend(const FX_RECT* pRect,
+bool CFX_RenderDevice::FillRectWithBlend(const FX_RECT& rect,
                                          uint32_t fill_color,
-                                         int blend_type) {
-  if (m_pDeviceDriver->FillRectWithBlend(pRect, fill_color, blend_type))
+                                         BlendMode blend_type) {
+  if (m_pDeviceDriver->FillRectWithBlend(rect, fill_color, blend_type))
     return true;
 
   if (!(m_RenderCaps & FXRC_GET_BITS))
     return false;
 
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!CreateCompatibleBitmap(bitmap, pRect->Width(), pRect->Height()))
+  if (!CreateCompatibleBitmap(bitmap, rect.Width(), rect.Height()))
     return false;
 
-  if (!m_pDeviceDriver->GetDIBits(bitmap, pRect->left, pRect->top))
+  if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top))
     return false;
 
-  if (!bitmap->CompositeRect(0, 0, pRect->Width(), pRect->Height(), fill_color,
+  if (!bitmap->CompositeRect(0, 0, rect.Width(), rect.Height(), fill_color,
                              0)) {
     return false;
   }
-  FX_RECT src_rect(0, 0, pRect->Width(), pRect->Height());
-  m_pDeviceDriver->SetDIBits(bitmap, 0, &src_rect, pRect->left, pRect->top,
-                             FXDIB_BLEND_NORMAL);
+  FX_RECT src_rect(0, 0, rect.Width(), rect.Height());
+  m_pDeviceDriver->SetDIBits(bitmap, 0, src_rect, rect.left, rect.top,
+                             BlendMode::kNormal);
   return true;
 }
 
@@ -704,7 +698,7 @@
                                         const CFX_PointF& ptLineTo,
                                         uint32_t color,
                                         int fill_mode,
-                                        int blend_type) {
+                                        BlendMode blend_type) {
   if ((color >= 0xff000000) && m_pDeviceDriver->DrawCosmeticLine(
                                    ptMoveTo, ptLineTo, color, blend_type)) {
     return true;
@@ -728,18 +722,13 @@
   return m_pDeviceDriver->GetBackDrop();
 }
 
-bool CFX_RenderDevice::SetDIBitsWithBlend(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
-    int left,
-    int top,
-    int blend_mode) {
+bool CFX_RenderDevice::SetDIBitsWithBlend(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                          int left,
+                                          int top,
+                                          BlendMode blend_mode) {
   ASSERT(!pBitmap->IsAlphaMask());
-  CFX_Matrix ctm = GetCTM();
-  float fScaleX = fabs(ctm.a);
-  float fScaleY = fabs(ctm.d);
-  FX_RECT dest_rect(left, top,
-                    FXSYS_round(left + pBitmap->GetWidth() / fScaleX),
-                    FXSYS_round(top + pBitmap->GetHeight() / fScaleY));
+  FX_RECT dest_rect(left, top, left + pBitmap->GetWidth(),
+                    top + pBitmap->GetHeight());
   dest_rect.Intersect(m_ClipBox);
   if (dest_rect.IsEmpty())
     return true;
@@ -747,20 +736,16 @@
   FX_RECT src_rect(dest_rect.left - left, dest_rect.top - top,
                    dest_rect.left - left + dest_rect.Width(),
                    dest_rect.top - top + dest_rect.Height());
-  src_rect.left = FXSYS_round(src_rect.left * fScaleX);
-  src_rect.top = FXSYS_round(src_rect.top * fScaleY);
-  src_rect.right = FXSYS_round(src_rect.right * fScaleX);
-  src_rect.bottom = FXSYS_round(src_rect.bottom * fScaleY);
-  if ((blend_mode == FXDIB_BLEND_NORMAL || (m_RenderCaps & FXRC_BLEND_MODE)) &&
+  if ((blend_mode == BlendMode::kNormal || (m_RenderCaps & FXRC_BLEND_MODE)) &&
       (!pBitmap->HasAlpha() || (m_RenderCaps & FXRC_ALPHA_IMAGE))) {
-    return m_pDeviceDriver->SetDIBits(pBitmap, 0, &src_rect, dest_rect.left,
+    return m_pDeviceDriver->SetDIBits(pBitmap, 0, src_rect, dest_rect.left,
                                       dest_rect.top, blend_mode);
   }
   if (!(m_RenderCaps & FXRC_GET_BITS))
     return false;
 
-  int bg_pixel_width = FXSYS_round(dest_rect.Width() * fScaleX);
-  int bg_pixel_height = FXSYS_round(dest_rect.Height() * fScaleY);
+  int bg_pixel_width = dest_rect.Width();
+  int bg_pixel_height = dest_rect.Height();
   auto background = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!background->Create(
           bg_pixel_width, bg_pixel_height,
@@ -776,107 +761,107 @@
     return false;
   }
   FX_RECT rect(0, 0, bg_pixel_width, bg_pixel_height);
-  return m_pDeviceDriver->SetDIBits(background, 0, &rect, dest_rect.left,
-                                    dest_rect.top, FXDIB_BLEND_NORMAL);
+  return m_pDeviceDriver->SetDIBits(background, 0, rect, dest_rect.left,
+                                    dest_rect.top, BlendMode::kNormal);
 }
 
 bool CFX_RenderDevice::StretchDIBitsWithFlagsAndBlend(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pBitmap,
     int left,
     int top,
     int dest_width,
     int dest_height,
-    uint32_t flags,
-    int blend_mode) {
+    const FXDIB_ResampleOptions& options,
+    BlendMode blend_mode) {
   FX_RECT dest_rect(left, top, left + dest_width, top + dest_height);
   FX_RECT clip_box = m_ClipBox;
   clip_box.Intersect(dest_rect);
   return clip_box.IsEmpty() || m_pDeviceDriver->StretchDIBits(
                                    pBitmap, 0, left, top, dest_width,
-                                   dest_height, &clip_box, flags, blend_mode);
+                                   dest_height, &clip_box, options, blend_mode);
 }
 
-bool CFX_RenderDevice::SetBitMask(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CFX_RenderDevice::SetBitMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                                   int left,
                                   int top,
                                   uint32_t argb) {
   FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
-  return m_pDeviceDriver->SetDIBits(pBitmap, argb, &src_rect, left, top,
-                                    FXDIB_BLEND_NORMAL);
+  return m_pDeviceDriver->SetDIBits(pBitmap, argb, src_rect, left, top,
+                                    BlendMode::kNormal);
 }
 
-bool CFX_RenderDevice::StretchBitMask(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CFX_RenderDevice::StretchBitMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                                       int left,
                                       int top,
                                       int dest_width,
                                       int dest_height,
                                       uint32_t color) {
   return StretchBitMaskWithFlags(pBitmap, left, top, dest_width, dest_height,
-                                 color, 0);
+                                 color, FXDIB_ResampleOptions());
 }
 
 bool CFX_RenderDevice::StretchBitMaskWithFlags(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pBitmap,
     int left,
     int top,
     int dest_width,
     int dest_height,
     uint32_t argb,
-    uint32_t flags) {
+    const FXDIB_ResampleOptions& options) {
   FX_RECT dest_rect(left, top, left + dest_width, top + dest_height);
   FX_RECT clip_box = m_ClipBox;
   clip_box.Intersect(dest_rect);
   return m_pDeviceDriver->StretchDIBits(pBitmap, argb, left, top, dest_width,
-                                        dest_height, &clip_box, flags,
-                                        FXDIB_BLEND_NORMAL);
+                                        dest_height, &clip_box, options,
+                                        BlendMode::kNormal);
 }
 
 bool CFX_RenderDevice::StartDIBitsWithBlend(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pBitmap,
     int bitmap_alpha,
     uint32_t argb,
-    const CFX_Matrix* pMatrix,
-    uint32_t flags,
+    const CFX_Matrix& matrix,
+    const FXDIB_ResampleOptions& options,
     std::unique_ptr<CFX_ImageRenderer>* handle,
-    int blend_mode) {
-  return m_pDeviceDriver->StartDIBits(pBitmap, bitmap_alpha, argb, pMatrix,
-                                      flags, handle, blend_mode);
+    BlendMode blend_mode) {
+  return m_pDeviceDriver->StartDIBits(pBitmap, bitmap_alpha, argb, matrix,
+                                      options, handle, blend_mode);
 }
 
 bool CFX_RenderDevice::ContinueDIBits(CFX_ImageRenderer* handle,
-                                      IFX_PauseIndicator* pPause) {
+                                      PauseIndicatorIface* pPause) {
   return m_pDeviceDriver->ContinueDIBits(handle, pPause);
 }
 
 #ifdef _SKIA_SUPPORT_
 void CFX_RenderDevice::DebugVerifyBitmapIsPreMultiplied() const {
-  SkASSERT(0);
+  NOTREACHED();
 }
 
-bool CFX_RenderDevice::SetBitsWithMask(const RetainPtr<CFX_DIBSource>& pBitmap,
-                                       const RetainPtr<CFX_DIBSource>& pMask,
+bool CFX_RenderDevice::SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                                       const RetainPtr<CFX_DIBBase>& pMask,
                                        int left,
                                        int top,
                                        int bitmap_alpha,
-                                       int blend_type) {
+                                       BlendMode blend_type) {
   return m_pDeviceDriver->SetBitsWithMask(pBitmap, pMask, left, top,
                                           bitmap_alpha, blend_type);
 }
 #endif
 
 bool CFX_RenderDevice::DrawNormalText(int nChars,
-                                      const FXTEXT_CHARPOS* pCharPos,
+                                      const TextCharPos* pCharPos,
                                       CFX_Font* pFont,
                                       float font_size,
-                                      const CFX_Matrix* pText2Device,
+                                      const CFX_Matrix& mtText2Device,
                                       uint32_t fill_color,
                                       uint32_t text_flags) {
   int nativetext_flags = text_flags;
-  if (m_DeviceClass != FXDC_DISPLAY) {
+  if (m_DeviceType != DeviceType::kDisplay) {
     if (!(text_flags & FXTEXT_PRINTGRAPHICTEXT)) {
       if (ShouldDrawDeviceText(pFont, text_flags) &&
-          m_pDeviceDriver->DrawDeviceText(nChars, pCharPos, pFont, pText2Device,
-                                          font_size, fill_color)) {
+          m_pDeviceDriver->DrawDeviceText(
+              nChars, pCharPos, pFont, mtText2Device, font_size, fill_color)) {
         return true;
       }
     }
@@ -884,74 +869,63 @@
       return false;
   } else if (!(text_flags & FXTEXT_NO_NATIVETEXT)) {
     if (ShouldDrawDeviceText(pFont, text_flags) &&
-        m_pDeviceDriver->DrawDeviceText(nChars, pCharPos, pFont, pText2Device,
+        m_pDeviceDriver->DrawDeviceText(nChars, pCharPos, pFont, mtText2Device,
                                         font_size, fill_color)) {
       return true;
     }
   }
-  CFX_Matrix char2device;
-  CFX_Matrix text2Device;
-  if (pText2Device) {
-    char2device = *pText2Device;
-    text2Device = *pText2Device;
-  }
-
+  CFX_Matrix char2device = mtText2Device;
+  CFX_Matrix text2Device = mtText2Device;
   char2device.Scale(font_size, -font_size);
   if (fabs(char2device.a) + fabs(char2device.b) > 50 * 1.0f ||
-      ((m_DeviceClass == FXDC_PRINTER) &&
+      (m_DeviceType == DeviceType::kPrinter &&
        !(text_flags & FXTEXT_PRINTIMAGETEXT))) {
-    if (pFont->GetFace()) {
+    if (pFont->GetFaceRec()) {
       int nPathFlags =
           (text_flags & FXTEXT_NOSMOOTH) == 0 ? 0 : FXFILL_NOPATHSMOOTH;
-      return DrawTextPath(nChars, pCharPos, pFont, font_size, pText2Device,
+      return DrawTextPath(nChars, pCharPos, pFont, font_size, mtText2Device,
                           nullptr, nullptr, fill_color, 0, nullptr, nPathFlags);
     }
   }
-  int anti_alias = FXFT_RENDER_MODE_MONO;
+  int anti_alias = FT_RENDER_MODE_MONO;
   bool bNormal = false;
   if ((text_flags & FXTEXT_NOSMOOTH) == 0) {
-    if (m_DeviceClass == FXDC_DISPLAY && m_bpp > 1) {
+    if (m_DeviceType == DeviceType::kDisplay && m_bpp > 1) {
       if (!CFX_GEModule::Get()->GetFontMgr()->FTLibrarySupportsHinting()) {
         // Some Freetype implementations (like the one packaged with Fedora) do
         // not support hinting due to patents 6219025, 6239783, 6307566,
         // 6225973, 6243070, 6393145, 6421054, 6282327, and 6624828; the latest
         // one expires 10/7/19.  This makes LCD antialiasing very ugly, so we
         // instead fall back on NORMAL antialiasing.
-        anti_alias = FXFT_RENDER_MODE_NORMAL;
+        anti_alias = FT_RENDER_MODE_NORMAL;
       } else if ((m_RenderCaps & (FXRC_ALPHA_OUTPUT | FXRC_CMYK_OUTPUT))) {
-        anti_alias = FXFT_RENDER_MODE_LCD;
+        anti_alias = FT_RENDER_MODE_LCD;
         bNormal = true;
       } else if (m_bpp < 16) {
-        anti_alias = FXFT_RENDER_MODE_NORMAL;
+        anti_alias = FT_RENDER_MODE_NORMAL;
       } else {
-        anti_alias = FXFT_RENDER_MODE_LCD;
+        anti_alias = FT_RENDER_MODE_LCD;
 
         bool bClearType = false;
-        if (pFont->GetFace())
+        if (pFont->GetFaceRec())
           bClearType = !!(text_flags & FXTEXT_CLEARTYPE);
         bNormal = !bClearType;
       }
     }
   }
-  std::vector<FXTEXT_GLYPHPOS> glyphs(nChars);
-  CFX_Matrix matrixCTM = GetCTM();
-  float scale_x = fabs(matrixCTM.a);
-  float scale_y = fabs(matrixCTM.d);
+  std::vector<TextGlyphPos> glyphs(nChars);
   CFX_Matrix deviceCtm = char2device;
-  CFX_Matrix m(scale_x, 0, 0, scale_y, 0, 0);
-  deviceCtm.Concat(m);
-  text2Device.Concat(m);
 
   for (size_t i = 0; i < glyphs.size(); ++i) {
-    FXTEXT_GLYPHPOS& glyph = glyphs[i];
-    const FXTEXT_CHARPOS& charpos = pCharPos[i];
+    TextGlyphPos& glyph = glyphs[i];
+    const TextCharPos& charpos = pCharPos[i];
 
     glyph.m_fOrigin = text2Device.Transform(charpos.m_Origin);
-    if (anti_alias < FXFT_RENDER_MODE_LCD)
-      glyph.m_Origin.x = FXSYS_round(glyph.m_fOrigin.x);
+    if (anti_alias < FT_RENDER_MODE_LCD)
+      glyph.m_Origin.x = FXSYS_roundf(glyph.m_fOrigin.x);
     else
       glyph.m_Origin.x = static_cast<int>(floor(glyph.m_fOrigin.x));
-    glyph.m_Origin.y = FXSYS_round(glyph.m_fOrigin.y);
+    glyph.m_Origin.y = FXSYS_roundf(glyph.m_fOrigin.y);
 
     if (charpos.m_bGlyphAdjust) {
       CFX_Matrix new_matrix(
@@ -959,49 +933,43 @@
           charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0);
       new_matrix.Concat(deviceCtm);
       glyph.m_pGlyph = pFont->LoadGlyphBitmap(
-          charpos.m_GlyphIndex, charpos.m_bFontStyle, &new_matrix,
-          charpos.m_FontCharWidth, anti_alias, nativetext_flags);
+          charpos.m_GlyphIndex, charpos.m_bFontStyle, new_matrix,
+          charpos.m_FontCharWidth, anti_alias, &nativetext_flags);
     } else {
       glyph.m_pGlyph = pFont->LoadGlyphBitmap(
-          charpos.m_GlyphIndex, charpos.m_bFontStyle, &deviceCtm,
-          charpos.m_FontCharWidth, anti_alias, nativetext_flags);
+          charpos.m_GlyphIndex, charpos.m_bFontStyle, deviceCtm,
+          charpos.m_FontCharWidth, anti_alias, &nativetext_flags);
     }
   }
-  if (anti_alias < FXFT_RENDER_MODE_LCD && glyphs.size() > 1)
+  if (anti_alias < FT_RENDER_MODE_LCD && glyphs.size() > 1)
     AdjustGlyphSpace(&glyphs);
 
-  FX_RECT bmp_rect1 = FXGE_GetGlyphsBBox(glyphs, anti_alias, 1.0f, 1.0f);
-  if (scale_x > 1 && scale_y > 1) {
-    --bmp_rect1.left;
-    --bmp_rect1.top;
-    ++bmp_rect1.right;
-    ++bmp_rect1.bottom;
-  }
-  FX_RECT bmp_rect(FXSYS_round((float)(bmp_rect1.left) / scale_x),
-                   FXSYS_round((float)(bmp_rect1.top) / scale_y),
-                   FXSYS_round((float)bmp_rect1.right / scale_x),
-                   FXSYS_round((float)bmp_rect1.bottom / scale_y));
+  FX_RECT bmp_rect = GetGlyphsBBox(glyphs, anti_alias);
   bmp_rect.Intersect(m_ClipBox);
   if (bmp_rect.IsEmpty())
     return true;
 
-  int pixel_width = FXSYS_round(bmp_rect.Width() * scale_x);
-  int pixel_height = FXSYS_round(bmp_rect.Height() * scale_y);
-  int pixel_left = FXSYS_round(bmp_rect.left * scale_x);
-  int pixel_top = FXSYS_round(bmp_rect.top * scale_y);
-  if (anti_alias == FXFT_RENDER_MODE_MONO) {
+  int pixel_width = bmp_rect.Width();
+  int pixel_height = bmp_rect.Height();
+  int pixel_left = bmp_rect.left;
+  int pixel_top = bmp_rect.top;
+  if (anti_alias == FT_RENDER_MODE_MONO) {
     auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
     if (!bitmap->Create(pixel_width, pixel_height, FXDIB_1bppMask))
       return false;
     bitmap->Clear(0);
-    for (const FXTEXT_GLYPHPOS& glyph : glyphs) {
+    for (const TextGlyphPos& glyph : glyphs) {
       if (!glyph.m_pGlyph)
         continue;
-      RetainPtr<CFX_DIBitmap> pGlyph = glyph.m_pGlyph->m_pBitmap;
-      bitmap->TransferBitmap(
-          glyph.m_Origin.x + glyph.m_pGlyph->m_Left - pixel_left,
-          glyph.m_Origin.y - glyph.m_pGlyph->m_Top - pixel_top,
-          pGlyph->GetWidth(), pGlyph->GetHeight(), pGlyph, 0, 0);
+
+      Optional<CFX_Point> point = glyph.GetOrigin({pixel_left, pixel_top});
+      if (!point.has_value())
+        continue;
+
+      const RetainPtr<CFX_DIBitmap>& pGlyph = glyph.m_pGlyph->GetBitmap();
+      bitmap->TransferBitmap(point.value().x, point.value().y,
+                             pGlyph->GetWidth(), pGlyph->GetHeight(), pGlyph, 0,
+                             0);
     }
     return SetBitMask(bitmap, bmp_rect.left, bmp_rect.top, fill_color);
   }
@@ -1027,32 +995,24 @@
   int r = 0;
   int g = 0;
   int b = 0;
-  if (anti_alias == FXFT_RENDER_MODE_LCD)
+  if (anti_alias == FT_RENDER_MODE_LCD)
     std::tie(a, r, g, b) = ArgbDecode(fill_color);
 
-  for (const FXTEXT_GLYPHPOS& glyph : glyphs) {
+  for (const TextGlyphPos& glyph : glyphs) {
     if (!glyph.m_pGlyph)
       continue;
 
-    pdfium::base::CheckedNumeric<int> left = glyph.m_Origin.x;
-    left += glyph.m_pGlyph->m_Left;
-    left -= pixel_left;
-    if (!left.IsValid())
-      return false;
+    Optional<CFX_Point> point = glyph.GetOrigin({pixel_left, pixel_top});
+    if (!point.has_value())
+      continue;
 
-    pdfium::base::CheckedNumeric<int> top = glyph.m_Origin.y;
-    top -= glyph.m_pGlyph->m_Top;
-    top -= pixel_top;
-    if (!top.IsValid())
-      return false;
-
-    RetainPtr<CFX_DIBitmap> pGlyph = glyph.m_pGlyph->m_pBitmap;
+    const RetainPtr<CFX_DIBitmap>& pGlyph = glyph.m_pGlyph->GetBitmap();
     int ncols = pGlyph->GetWidth();
     int nrows = pGlyph->GetHeight();
-    if (anti_alias == FXFT_RENDER_MODE_NORMAL) {
-      if (!bitmap->CompositeMask(left.ValueOrDie(), top.ValueOrDie(), ncols,
-                                 nrows, pGlyph, fill_color, 0, 0,
-                                 FXDIB_BLEND_NORMAL, nullptr, false, 0)) {
+    if (anti_alias == FT_RENDER_MODE_NORMAL) {
+      if (!bitmap->CompositeMask(point.value().x, point.value().y, ncols, nrows,
+                                 pGlyph, fill_color, 0, 0, BlendMode::kNormal,
+                                 nullptr, false)) {
         return false;
       }
       continue;
@@ -1060,21 +1020,18 @@
     bool bBGRStripe = !!(text_flags & FXTEXT_BGR_STRIPE);
     ncols /= 3;
     int x_subpixel = static_cast<int>(glyph.m_fOrigin.x * 3) % 3;
-    int start_col =
-        pdfium::base::ValueOrDieForType<int>(pdfium::base::CheckMax(left, 0));
-    pdfium::base::CheckedNumeric<int> end_col_safe = left;
+    int start_col = std::max(point->x, 0);
+    FX_SAFE_INT32 end_col_safe = point->x;
     end_col_safe += ncols;
     if (!end_col_safe.IsValid())
-      return false;
+      continue;
 
-    int end_col =
-        std::min(static_cast<int>(end_col_safe.ValueOrDie<int>()), dest_width);
+    int end_col = std::min<int>(end_col_safe.ValueOrDie(), dest_width);
     if (start_col >= end_col)
       continue;
 
-    DrawNormalTextHelper(bitmap, pGlyph, nrows, left.ValueOrDie(),
-                         top.ValueOrDie(), start_col, end_col, bNormal,
-                         bBGRStripe, x_subpixel, a, r, g, b);
+    DrawNormalTextHelper(bitmap, pGlyph, nrows, point->x, point->y, start_col,
+                         end_col, bNormal, bBGRStripe, x_subpixel, a, r, g, b);
   }
   if (bitmap->IsAlphaMask())
     SetBitMask(bitmap, bmp_rect.left, bmp_rect.top, fill_color);
@@ -1084,10 +1041,10 @@
 }
 
 bool CFX_RenderDevice::DrawTextPath(int nChars,
-                                    const FXTEXT_CHARPOS* pCharPos,
+                                    const TextCharPos* pCharPos,
                                     CFX_Font* pFont,
                                     float font_size,
-                                    const CFX_Matrix* pText2User,
+                                    const CFX_Matrix& mtText2User,
                                     const CFX_Matrix* pUser2Device,
                                     const CFX_GraphStateData* pGraphState,
                                     uint32_t fill_color,
@@ -1095,7 +1052,7 @@
                                     CFX_PathData* pClippingPath,
                                     int nFlag) {
   for (int iChar = 0; iChar < nChars; ++iChar) {
-    const FXTEXT_CHARPOS& charpos = pCharPos[iChar];
+    const TextCharPos& charpos = pCharPos[iChar];
     CFX_Matrix matrix;
     if (charpos.m_bGlyphAdjust) {
       matrix = CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
@@ -1109,10 +1066,10 @@
     if (!pPath)
       continue;
 
-    matrix.Concat(*pText2User);
+    matrix.Concat(mtText2User);
 
     CFX_PathData TransformedPath(*pPath);
-    TransformedPath.Transform(&matrix);
+    TransformedPath.Transform(matrix);
     if (fill_color || stroke_color) {
       int fill_mode = nFlag;
       if (fill_color)
@@ -1120,7 +1077,7 @@
       fill_mode |= FX_FILL_TEXT_MODE;
       if (!DrawPathWithBlend(&TransformedPath, pUser2Device, pGraphState,
                              fill_color, stroke_color, fill_mode,
-                             FXDIB_BLEND_NORMAL)) {
+                             BlendMode::kNormal)) {
         return false;
       }
     }
@@ -1134,35 +1091,32 @@
                                     const CFX_FloatRect& rect,
                                     const FX_COLORREF& color) {
   CFX_PathData path;
-  CFX_FloatRect rcTemp(rect);
-  path.AppendRect(rcTemp.left, rcTemp.bottom, rcTemp.right, rcTemp.top);
+  path.AppendFloatRect(rect);
   DrawPath(&path, pUser2Device, nullptr, color, 0, FXFILL_WINDING);
 }
 
-void CFX_RenderDevice::DrawFillArea(const CFX_Matrix* pUser2Device,
-                                    const CFX_PointF* pPts,
-                                    int32_t nCount,
+void CFX_RenderDevice::DrawFillArea(const CFX_Matrix& mtUser2Device,
+                                    const std::vector<CFX_PointF>& points,
                                     const FX_COLORREF& color) {
+  ASSERT(!points.empty());
   CFX_PathData path;
-  path.AppendPoint(pPts[0], FXPT_TYPE::MoveTo, false);
-  for (int32_t i = 1; i < nCount; i++)
-    path.AppendPoint(pPts[i], FXPT_TYPE::LineTo, false);
+  path.AppendPoint(points[0], FXPT_TYPE::MoveTo, false);
+  for (size_t i = 1; i < points.size(); ++i)
+    path.AppendPoint(points[i], FXPT_TYPE::LineTo, false);
 
-  DrawPath(&path, pUser2Device, nullptr, color, 0, FXFILL_ALTERNATE);
+  DrawPath(&path, &mtUser2Device, nullptr, color, 0, FXFILL_ALTERNATE);
 }
 
-void CFX_RenderDevice::DrawStrokeRect(const CFX_Matrix* pUser2Device,
+void CFX_RenderDevice::DrawStrokeRect(const CFX_Matrix& mtUser2Device,
                                       const CFX_FloatRect& rect,
                                       const FX_COLORREF& color,
                                       float fWidth) {
-  CFX_PathData path;
-  CFX_FloatRect rcTemp(rect);
-  path.AppendRect(rcTemp.left, rcTemp.bottom, rcTemp.right, rcTemp.top);
-
   CFX_GraphStateData gsd;
   gsd.m_LineWidth = fWidth;
 
-  DrawPath(&path, pUser2Device, &gsd, 0, color, FXFILL_ALTERNATE);
+  CFX_PathData path;
+  path.AppendFloatRect(rect);
+  DrawPath(&path, &mtUser2Device, &gsd, 0, color, FXFILL_ALTERNATE);
 }
 
 void CFX_RenderDevice::DrawStrokeLine(const CFX_Matrix* pUser2Device,
@@ -1187,10 +1141,10 @@
   DrawFillRect(pUser2Device, rect, color.ToFXColor(nTransparency));
 }
 
-void CFX_RenderDevice::DrawShadow(const CFX_Matrix* pUser2Device,
+void CFX_RenderDevice::DrawShadow(const CFX_Matrix& mtUser2Device,
                                   bool bVertical,
                                   bool bHorizontal,
-                                  CFX_FloatRect rect,
+                                  const CFX_FloatRect& rect,
                                   int32_t nTransparency,
                                   int32_t nStartGray,
                                   int32_t nEndGray) {
@@ -1201,7 +1155,7 @@
 
     for (float fy = rect.bottom + 0.5f; fy <= rect.top - 0.5f; fy += 1.0f) {
       int32_t nGray = nStartGray + (int32_t)(fStepGray * (fy - rect.bottom));
-      DrawStrokeLine(pUser2Device, CFX_PointF(rect.left, fy),
+      DrawStrokeLine(&mtUser2Device, CFX_PointF(rect.left, fy),
                      CFX_PointF(rect.right, fy),
                      ArgbEncode(nTransparency, nGray, nGray, nGray), 1.5f);
     }
@@ -1212,7 +1166,7 @@
 
     for (float fx = rect.left + 0.5f; fx <= rect.right - 0.5f; fx += 1.0f) {
       int32_t nGray = nStartGray + (int32_t)(fStepGray * (fx - rect.left));
-      DrawStrokeLine(pUser2Device, CFX_PointF(fx, rect.bottom),
+      DrawStrokeLine(&mtUser2Device, CFX_PointF(fx, rect.bottom),
                      CFX_PointF(fx, rect.top),
                      ArgbEncode(nTransparency, nGray, nGray, nGray), 1.5f);
     }
@@ -1265,11 +1219,8 @@
             FXPT_TYPE::LineTo, false);
 
         CFX_GraphStateData gsd;
-        gsd.SetDashCount(2);
-        gsd.m_DashArray[0] = 3.0f;
-        gsd.m_DashArray[1] = 3.0f;
+        gsd.m_DashArray = {3.0f, 3.0f};
         gsd.m_DashPhase = 0;
-
         gsd.m_LineWidth = fWidth;
         DrawPath(&path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency),
                  FXFILL_WINDING);
diff --git a/core/fxge/cfx_renderdevice.h b/core/fxge/cfx_renderdevice.h
index d8cb9a6..755f445 100644
--- a/core/fxge/cfx_renderdevice.h
+++ b/core/fxge/cfx_renderdevice.h
@@ -8,82 +8,26 @@
 #define CORE_FXGE_CFX_RENDERDEVICE_H_
 
 #include <memory>
+#include <vector>
 
+#include "build/build_config.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/cfx_color.h"
 #include "core/fxge/fx_dib.h"
+#include "core/fxge/render_defines.h"
+#include "core/fxge/renderdevicedriver_iface.h"
 
+class CFX_DIBBase;
 class CFX_DIBitmap;
 class CFX_Font;
 class CFX_GraphStateData;
 class CFX_ImageRenderer;
-class IFX_PauseIndicator;
-class IFX_RenderDeviceDriver;
-
-#define FXDC_DEVICE_CLASS 1
-#define FXDC_PIXEL_WIDTH 2
-#define FXDC_PIXEL_HEIGHT 3
-#define FXDC_BITS_PIXEL 4
-#define FXDC_HORZ_SIZE 5
-#define FXDC_VERT_SIZE 6
-#define FXDC_RENDER_CAPS 7
-#define FXDC_DISPLAY 1
-#define FXDC_PRINTER 2
-
-#define FXRC_GET_BITS 0x01
-#define FXRC_BIT_MASK 0x02
-#define FXRC_ALPHA_PATH 0x10
-#define FXRC_ALPHA_IMAGE 0x20
-#define FXRC_ALPHA_OUTPUT 0x40
-#define FXRC_BLEND_MODE 0x80
-#define FXRC_SOFT_CLIP 0x100
-#define FXRC_CMYK_OUTPUT 0x200
-#define FXRC_BITMASK_OUTPUT 0x400
-#define FXRC_BYTEMASK_OUTPUT 0x800
-#define FXRENDER_IMAGE_LOSSY 0x1000
-#define FXRC_FILLSTROKE_PATH 0x2000
-#define FXRC_SHADING 0x4000
-
-#define FXFILL_ALTERNATE 1
-#define FXFILL_WINDING 2
-#define FXFILL_FULLCOVER 4
-#define FXFILL_RECT_AA 8
-#define FX_FILL_STROKE 16
-#define FX_STROKE_ADJUST 32
-#define FX_STROKE_TEXT_MODE 64
-#define FX_FILL_TEXT_MODE 128
-#define FX_ZEROAREA_FILL 256
-#define FXFILL_NOPATHSMOOTH 512
-
-#define FXTEXT_CLEARTYPE 0x01
-#define FXTEXT_BGR_STRIPE 0x02
-#define FXTEXT_PRINTGRAPHICTEXT 0x04
-#define FXTEXT_NO_NATIVETEXT 0x08
-#define FXTEXT_PRINTIMAGETEXT 0x10
-#define FXTEXT_NOSMOOTH 0x20
-
 class CFX_PathData;
+class PauseIndicatorIface;
+class TextCharPos;
+struct CFX_Color;
 
-enum class FXPT_TYPE : uint8_t { LineTo, BezierTo, MoveTo };
-
-class FXTEXT_CHARPOS {
- public:
-  FXTEXT_CHARPOS();
-  FXTEXT_CHARPOS(const FXTEXT_CHARPOS&);
-  ~FXTEXT_CHARPOS();
-
-  float m_AdjustMatrix[4];
-  CFX_PointF m_Origin;
-  uint32_t m_Unicode;
-  uint32_t m_GlyphIndex;
-  int32_t m_FontCharWidth;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  uint32_t m_ExtGID;
-#endif
-  int32_t m_FallbackFontPosition;
-  bool m_bGlyphAdjust;
-  bool m_bFontStyle;
-};
+enum class BorderStyle { SOLID, DASH, BEVELED, INSET, UNDERLINE };
 
 class CFX_RenderDevice {
  public:
@@ -99,9 +43,13 @@
   CFX_RenderDevice();
   virtual ~CFX_RenderDevice();
 
-  // Take ownership of |pDriver|.
-  void SetDeviceDriver(std::unique_ptr<IFX_RenderDeviceDriver> pDriver);
-  IFX_RenderDeviceDriver* GetDeviceDriver() const {
+  static CFX_Matrix GetFlipMatrix(float width,
+                                  float height,
+                                  float left,
+                                  float top);
+
+  void SetDeviceDriver(std::unique_ptr<RenderDeviceDriverIface> pDriver);
+  RenderDeviceDriverIface* GetDeviceDriver() const {
     return m_pDeviceDriver.get();
   }
 
@@ -110,24 +58,23 @@
 
   int GetWidth() const { return m_Width; }
   int GetHeight() const { return m_Height; }
-  int GetDeviceClass() const { return m_DeviceClass; }
+  DeviceType GetDeviceType() const { return m_DeviceType; }
   int GetRenderCaps() const { return m_RenderCaps; }
   int GetDeviceCaps(int id) const;
-  CFX_Matrix GetCTM() const;
   RetainPtr<CFX_DIBitmap> GetBitmap() const;
   void SetBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap);
   bool CreateCompatibleBitmap(const RetainPtr<CFX_DIBitmap>& pDIB,
                               int width,
                               int height) const;
   const FX_RECT& GetClipBox() const { return m_ClipBox; }
+  void SetBaseClip(const FX_RECT& rect);
   bool SetClip_PathFill(const CFX_PathData* pPathData,
                         const CFX_Matrix* pObject2Device,
                         int fill_mode);
-  bool SetClip_Rect(const CFX_RectF& pRect);
-  bool SetClip_Rect(const FX_RECT& pRect);
   bool SetClip_PathStroke(const CFX_PathData* pPathData,
                           const CFX_Matrix* pObject2Device,
                           const CFX_GraphStateData* pGraphState);
+  bool SetClip_Rect(const FX_RECT& pRect);
   bool DrawPath(const CFX_PathData* pPathData,
                 const CFX_Matrix* pObject2Device,
                 const CFX_GraphStateData* pGraphState,
@@ -135,7 +82,7 @@
                 uint32_t stroke_color,
                 int fill_mode) {
     return DrawPathWithBlend(pPathData, pObject2Device, pGraphState, fill_color,
-                             stroke_color, fill_mode, FXDIB_BLEND_NORMAL);
+                             stroke_color, fill_mode, BlendMode::kNormal);
   }
   bool DrawPathWithBlend(const CFX_PathData* pPathData,
                          const CFX_Matrix* pObject2Device,
@@ -143,82 +90,83 @@
                          uint32_t fill_color,
                          uint32_t stroke_color,
                          int fill_mode,
-                         int blend_type);
-  bool FillRect(const FX_RECT* pRect, uint32_t color) {
-    return FillRectWithBlend(pRect, color, FXDIB_BLEND_NORMAL);
+                         BlendMode blend_type);
+  bool FillRect(const FX_RECT& rect, uint32_t color) {
+    return FillRectWithBlend(rect, color, BlendMode::kNormal);
   }
 
   RetainPtr<CFX_DIBitmap> GetBackDrop();
   bool GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap, int left, int top);
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap, int left, int top) {
-    return SetDIBitsWithBlend(pBitmap, left, top, FXDIB_BLEND_NORMAL);
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap, int left, int top) {
+    return SetDIBitsWithBlend(pBitmap, left, top, BlendMode::kNormal);
   }
-  bool SetDIBitsWithBlend(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool SetDIBitsWithBlend(const RetainPtr<CFX_DIBBase>& pBitmap,
                           int left,
                           int top,
-                          int blend_type);
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                          BlendMode blend_mode);
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      int left,
                      int top,
                      int dest_width,
                      int dest_height) {
     return StretchDIBitsWithFlagsAndBlend(pBitmap, left, top, dest_width,
-                                          dest_height, 0, FXDIB_BLEND_NORMAL);
+                                          dest_height, FXDIB_ResampleOptions(),
+                                          BlendMode::kNormal);
   }
-  bool StretchDIBitsWithFlagsAndBlend(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool StretchDIBitsWithFlagsAndBlend(const RetainPtr<CFX_DIBBase>& pBitmap,
                                       int left,
                                       int top,
                                       int dest_width,
                                       int dest_height,
-                                      uint32_t flags,
-                                      int blend_type);
-  bool SetBitMask(const RetainPtr<CFX_DIBSource>& pBitmap,
+                                      const FXDIB_ResampleOptions& options,
+                                      BlendMode blend_mode);
+  bool SetBitMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                   int left,
                   int top,
-                  uint32_t color);
-  bool StretchBitMask(const RetainPtr<CFX_DIBSource>& pBitmap,
+                  uint32_t argb);
+  bool StretchBitMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                       int left,
                       int top,
                       int dest_width,
                       int dest_height,
                       uint32_t color);
-  bool StretchBitMaskWithFlags(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool StretchBitMaskWithFlags(const RetainPtr<CFX_DIBBase>& pBitmap,
                                int left,
                                int top,
                                int dest_width,
                                int dest_height,
-                               uint32_t color,
-                               uint32_t flags);
-  bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                               uint32_t argb,
+                               const FXDIB_ResampleOptions& options);
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                    int bitmap_alpha,
                    uint32_t color,
-                   const CFX_Matrix* pMatrix,
-                   uint32_t flags,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
                    std::unique_ptr<CFX_ImageRenderer>* handle) {
-    return StartDIBitsWithBlend(pBitmap, bitmap_alpha, color, pMatrix, flags,
-                                handle, FXDIB_BLEND_NORMAL);
+    return StartDIBitsWithBlend(pBitmap, bitmap_alpha, color, matrix, options,
+                                handle, BlendMode::kNormal);
   }
-  bool StartDIBitsWithBlend(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool StartDIBitsWithBlend(const RetainPtr<CFX_DIBBase>& pBitmap,
                             int bitmap_alpha,
-                            uint32_t color,
-                            const CFX_Matrix* pMatrix,
-                            uint32_t flags,
+                            uint32_t argb,
+                            const CFX_Matrix& matrix,
+                            const FXDIB_ResampleOptions& options,
                             std::unique_ptr<CFX_ImageRenderer>* handle,
-                            int blend_type);
-  bool ContinueDIBits(CFX_ImageRenderer* handle, IFX_PauseIndicator* pPause);
+                            BlendMode blend_mode);
+  bool ContinueDIBits(CFX_ImageRenderer* handle, PauseIndicatorIface* pPause);
 
   bool DrawNormalText(int nChars,
-                      const FXTEXT_CHARPOS* pCharPos,
+                      const TextCharPos* pCharPos,
                       CFX_Font* pFont,
                       float font_size,
-                      const CFX_Matrix* pText2Device,
+                      const CFX_Matrix& mtText2Device,
                       uint32_t fill_color,
                       uint32_t text_flags);
   bool DrawTextPath(int nChars,
-                    const FXTEXT_CHARPOS* pCharPos,
+                    const TextCharPos* pCharPos,
                     CFX_Font* pFont,
                     float font_size,
-                    const CFX_Matrix* pText2User,
+                    const CFX_Matrix& mtText2User,
                     const CFX_Matrix* pUser2Device,
                     const CFX_GraphStateData* pGraphState,
                     uint32_t fill_color,
@@ -233,7 +181,7 @@
   void DrawFillRect(const CFX_Matrix* pUser2Device,
                     const CFX_FloatRect& rect,
                     const FX_COLORREF& color);
-  void DrawStrokeRect(const CFX_Matrix* pUser2Device,
+  void DrawStrokeRect(const CFX_Matrix& mtUser2Device,
                       const CFX_FloatRect& rect,
                       const FX_COLORREF& color,
                       float fWidth);
@@ -250,26 +198,25 @@
                   const CFX_Color& crRightBottom,
                   BorderStyle nStyle,
                   int32_t nTransparency);
-  void DrawFillArea(const CFX_Matrix* pUser2Device,
-                    const CFX_PointF* pPts,
-                    int32_t nCount,
+  void DrawFillArea(const CFX_Matrix& mtUser2Device,
+                    const std::vector<CFX_PointF>& points,
                     const FX_COLORREF& color);
-  void DrawShadow(const CFX_Matrix* pUser2Device,
+  void DrawShadow(const CFX_Matrix& mtUser2Device,
                   bool bVertical,
                   bool bHorizontal,
-                  CFX_FloatRect rect,
+                  const CFX_FloatRect& rect,
                   int32_t nTransparency,
                   int32_t nStartGray,
                   int32_t nEndGray);
 
 #ifdef _SKIA_SUPPORT_
   virtual void DebugVerifyBitmapIsPreMultiplied() const;
-  virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBSource>& pBitmap,
-                               const RetainPtr<CFX_DIBSource>& pMask,
+  virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                               const RetainPtr<CFX_DIBBase>& pMask,
                                int left,
                                int top,
                                int bitmap_alpha,
-                               int blend_type);
+                               BlendMode blend_type);
 #endif
 #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
   void Flush(bool release);
@@ -284,22 +231,24 @@
                           uint32_t fill_color,
                           uint32_t stroke_color,
                           int fill_mode,
-                          int blend_type);
+                          BlendMode blend_type);
   bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
                         const CFX_PointF& ptLineTo,
                         uint32_t color,
                         int fill_mode,
-                        int blend_type);
-  bool FillRectWithBlend(const FX_RECT* pRect, uint32_t color, int blend_type);
+                        BlendMode blend_type);
+  bool FillRectWithBlend(const FX_RECT& rect,
+                         uint32_t color,
+                         BlendMode blend_type);
 
   RetainPtr<CFX_DIBitmap> m_pBitmap;
-  int m_Width;
-  int m_Height;
-  int m_bpp;
-  int m_RenderCaps;
-  int m_DeviceClass;
+  int m_Width = 0;
+  int m_Height = 0;
+  int m_bpp = 0;
+  int m_RenderCaps = 0;
+  DeviceType m_DeviceType = DeviceType::kUnknown;
   FX_RECT m_ClipBox;
-  std::unique_ptr<IFX_RenderDeviceDriver> m_pDeviceDriver;
+  std::unique_ptr<RenderDeviceDriverIface> m_pDeviceDriver;
 };
 
 #endif  // CORE_FXGE_CFX_RENDERDEVICE_H_
diff --git a/core/fxge/cfx_substfont.cpp b/core/fxge/cfx_substfont.cpp
index 6c5459e..b9936df 100644
--- a/core/fxge/cfx_substfont.cpp
+++ b/core/fxge/cfx_substfont.cpp
@@ -6,18 +6,6 @@
 
 #include "core/fxge/cfx_substfont.h"
 
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxge/fx_font.h"
+CFX_SubstFont::CFX_SubstFont() = default;
 
-CFX_SubstFont::CFX_SubstFont()
-    : m_Charset(FX_CHARSET_ANSI),
-      m_Weight(0),
-      m_ItalicAngle(0),
-      m_WeightCJK(0),
-      m_bSubstCJK(false),
-      m_bItalicCJK(false),
-#ifdef PDF_ENABLE_XFA
-      m_bFlagItalic(false),
-#endif  // PDF_ENABLE_XFA
-      m_bFlagMM(false) {
-}
+CFX_SubstFont::~CFX_SubstFont() = default;
diff --git a/core/fxge/cfx_substfont.h b/core/fxge/cfx_substfont.h
index e6d93a8..c9ffa40 100644
--- a/core/fxge/cfx_substfont.h
+++ b/core/fxge/cfx_substfont.h
@@ -7,24 +7,22 @@
 #ifndef CORE_FXGE_CFX_SUBSTFONT_H_
 #define CORE_FXGE_CFX_SUBSTFONT_H_
 
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_string.h"
 
 class CFX_SubstFont {
  public:
   CFX_SubstFont();
+  ~CFX_SubstFont();
 
   ByteString m_Family;
-  int m_Charset;
-  int m_Weight;
-  int m_ItalicAngle;
-  int m_WeightCJK;
-  bool m_bSubstCJK;
-  bool m_bItalicCJK;
-
-#ifdef PDF_ENABLE_XFA
-  bool m_bFlagItalic;
-#endif  // PDF_ENABLE_XFA
-  bool m_bFlagMM;
+  int m_Charset = FX_CHARSET_ANSI;
+  int m_Weight = 0;
+  int m_ItalicAngle = 0;
+  int m_WeightCJK = 0;
+  bool m_bSubstCJK = false;
+  bool m_bItalicCJK = false;
+  bool m_bFlagMM = false;
 };
 
 #endif  // CORE_FXGE_CFX_SUBSTFONT_H_
diff --git a/core/fxge/cfx_unicodeencoding.cpp b/core/fxge/cfx_unicodeencoding.cpp
index 446b62c..6673fca 100644
--- a/core/fxge/cfx_unicodeencoding.cpp
+++ b/core/fxge/cfx_unicodeencoding.cpp
@@ -8,6 +8,7 @@
 
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_substfont.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/fx_freetype.h"
 
@@ -16,20 +17,20 @@
 CFX_UnicodeEncoding::~CFX_UnicodeEncoding() {}
 
 uint32_t CFX_UnicodeEncoding::GlyphFromCharCode(uint32_t charcode) {
-  FXFT_Face face = m_pFont->GetFace();
+  FXFT_FaceRec* face = m_pFont->GetFaceRec();
   if (!face)
     return charcode;
 
-  if (FXFT_Select_Charmap(face, FXFT_ENCODING_UNICODE) == 0)
-    return FXFT_Get_Char_Index(face, charcode);
+  if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == 0)
+    return FT_Get_Char_Index(face, charcode);
 
   if (m_pFont->GetSubstFont() &&
       m_pFont->GetSubstFont()->m_Charset == FX_CHARSET_Symbol) {
     uint32_t index = 0;
-    if (FXFT_Select_Charmap(face, FXFT_ENCODING_MS_SYMBOL) == 0)
-      index = FXFT_Get_Char_Index(face, charcode);
-    if (!index && !FXFT_Select_Charmap(face, FXFT_ENCODING_APPLE_ROMAN))
-      return FXFT_Get_Char_Index(face, charcode);
+    if (FT_Select_Charmap(face, FT_ENCODING_MS_SYMBOL) == 0)
+      index = FT_Get_Char_Index(face, charcode);
+    if (!index && !FT_Select_Charmap(face, FT_ENCODING_APPLE_ROMAN))
+      return FT_Get_Char_Index(face, charcode);
   }
   return charcode;
 }
diff --git a/core/fxge/cfx_unicodeencoding.h b/core/fxge/cfx_unicodeencoding.h
index f8bcff3..a730dc9 100644
--- a/core/fxge/cfx_unicodeencoding.h
+++ b/core/fxge/cfx_unicodeencoding.h
@@ -9,25 +9,7 @@
 
 #include <stdint.h>
 
-#ifdef PDF_ENABLE_XFA
-#define FXFM_ENC_TAG(a, b, c, d)                                          \
-  (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | \
-   (uint32_t)(d))
-#define FXFM_ENCODING_NONE FXFM_ENC_TAG(0, 0, 0, 0)
-#define FXFM_ENCODING_MS_SYMBOL FXFM_ENC_TAG('s', 'y', 'm', 'b')
-#define FXFM_ENCODING_UNICODE FXFM_ENC_TAG('u', 'n', 'i', 'c')
-#define FXFM_ENCODING_MS_SJIS FXFM_ENC_TAG('s', 'j', 'i', 's')
-#define FXFM_ENCODING_MS_GB2312 FXFM_ENC_TAG('g', 'b', ' ', ' ')
-#define FXFM_ENCODING_MS_BIG5 FXFM_ENC_TAG('b', 'i', 'g', '5')
-#define FXFM_ENCODING_MS_WANSUNG FXFM_ENC_TAG('w', 'a', 'n', 's')
-#define FXFM_ENCODING_MS_JOHAB FXFM_ENC_TAG('j', 'o', 'h', 'a')
-#define FXFM_ENCODING_ADOBE_STANDARD FXFM_ENC_TAG('A', 'D', 'O', 'B')
-#define FXFM_ENCODING_ADOBE_EXPERT FXFM_ENC_TAG('A', 'D', 'B', 'E')
-#define FXFM_ENCODING_ADOBE_CUSTOM FXFM_ENC_TAG('A', 'D', 'B', 'C')
-#define FXFM_ENCODING_ADOBE_LATIN_1 FXFM_ENC_TAG('l', 'a', 't', '1')
-#define FXFM_ENCODING_OLD_LATIN_2 FXFM_ENC_TAG('l', 'a', 't', '2')
-#define FXFM_ENCODING_APPLE_ROMAN FXFM_ENC_TAG('a', 'r', 'm', 'n')
-#endif  // PDF_ENABLE_XFA
+#include "core/fxcrt/unowned_ptr.h"
 
 class CFX_Font;
 
@@ -39,7 +21,7 @@
   virtual uint32_t GlyphFromCharCode(uint32_t charcode);
 
  protected:
-  CFX_Font* m_pFont;  // Unowned, not nullptr.
+  UnownedPtr<CFX_Font> const m_pFont;
 };
 
 #endif  // CORE_FXGE_CFX_UNICODEENCODING_H_
diff --git a/core/fxge/cfx_unicodeencodingex.cpp b/core/fxge/cfx_unicodeencodingex.cpp
index a8db745..a2fa24a 100644
--- a/core/fxge/cfx_unicodeencodingex.cpp
+++ b/core/fxge/cfx_unicodeencodingex.cpp
@@ -8,11 +8,28 @@
 
 #include <memory>
 
-#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fxge/cfx_font.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/fx_freetype.h"
 #include "third_party/base/ptr_util.h"
 
+#define FXFM_ENC_TAG(a, b, c, d)                                          \
+  (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | \
+   (uint32_t)(d))
+#define FXFM_ENCODING_MS_SYMBOL FXFM_ENC_TAG('s', 'y', 'm', 'b')
+#define FXFM_ENCODING_UNICODE FXFM_ENC_TAG('u', 'n', 'i', 'c')
+#define FXFM_ENCODING_MS_SJIS FXFM_ENC_TAG('s', 'j', 'i', 's')
+#define FXFM_ENCODING_MS_GB2312 FXFM_ENC_TAG('g', 'b', ' ', ' ')
+#define FXFM_ENCODING_MS_BIG5 FXFM_ENC_TAG('b', 'i', 'g', '5')
+#define FXFM_ENCODING_MS_WANSUNG FXFM_ENC_TAG('w', 'a', 'n', 's')
+#define FXFM_ENCODING_MS_JOHAB FXFM_ENC_TAG('j', 'o', 'h', 'a')
+#define FXFM_ENCODING_ADOBE_STANDARD FXFM_ENC_TAG('A', 'D', 'O', 'B')
+#define FXFM_ENCODING_ADOBE_EXPERT FXFM_ENC_TAG('A', 'D', 'B', 'E')
+#define FXFM_ENCODING_ADOBE_CUSTOM FXFM_ENC_TAG('A', 'D', 'B', 'C')
+#define FXFM_ENCODING_ADOBE_LATIN_1 FXFM_ENC_TAG('l', 'a', 't', '1')
+#define FXFM_ENCODING_OLD_LATIN_2 FXFM_ENC_TAG('l', 'a', 't', '2')
+#define FXFM_ENCODING_APPLE_ROMAN FXFM_ENC_TAG('a', 'r', 'm', 'n')
+
 namespace {
 
 const uint32_t g_EncodingID[] = {
@@ -28,7 +45,7 @@
 std::unique_ptr<CFX_UnicodeEncodingEx> FXFM_CreateFontEncoding(
     CFX_Font* pFont,
     uint32_t nEncodingID) {
-  if (FXFT_Select_Charmap(pFont->GetFace(), nEncodingID))
+  if (FXFT_Select_Charmap(pFont->GetFaceRec(), nEncodingID))
     return nullptr;
   return pdfium::MakeUnique<CFX_UnicodeEncodingEx>(pFont, nEncodingID);
 }
@@ -42,8 +59,8 @@
 CFX_UnicodeEncodingEx::~CFX_UnicodeEncodingEx() {}
 
 uint32_t CFX_UnicodeEncodingEx::GlyphFromCharCode(uint32_t charcode) {
-  FXFT_Face face = m_pFont->GetFace();
-  FT_UInt nIndex = FXFT_Get_Char_Index(face, charcode);
+  FXFT_FaceRec* face = m_pFont->GetFaceRec();
+  FT_UInt nIndex = FT_Get_Char_Index(face, charcode);
   if (nIndex > 0)
     return nIndex;
   int nmaps = FXFT_Get_Face_CharmapCount(face);
@@ -56,7 +73,7 @@
     int error = FXFT_Select_Charmap(face, nEncodingID);
     if (error)
       continue;
-    nIndex = FXFT_Get_Char_Index(face, charcode);
+    nIndex = FT_Get_Char_Index(face, charcode);
     if (nIndex > 0) {
       m_nEncodingID = nEncodingID;
       return nIndex;
@@ -71,7 +88,7 @@
       m_nEncodingID == FXFM_ENCODING_MS_SYMBOL) {
     return Unicode;
   }
-  FXFT_Face face = m_pFont->GetFace();
+  FXFT_FaceRec* face = m_pFont->GetFaceRec();
   int nmaps = FXFT_Get_Face_CharmapCount(face);
   for (int i = 0; i < nmaps; i++) {
     int nEncodingID =
@@ -81,18 +98,14 @@
       return Unicode;
     }
   }
-  return CPDF_Font::kInvalidCharCode;
+  return kInvalidCharCode;
 }
 
 std::unique_ptr<CFX_UnicodeEncodingEx> FX_CreateFontEncodingEx(
-    CFX_Font* pFont,
-    uint32_t nEncodingID) {
-  if (!pFont || !pFont->GetFace())
+    CFX_Font* pFont) {
+  if (!pFont || !pFont->GetFaceRec())
     return nullptr;
 
-  if (nEncodingID != FXFM_ENCODING_NONE)
-    return FXFM_CreateFontEncoding(pFont, nEncodingID);
-
   for (uint32_t id : g_EncodingID) {
     auto pFontEncoding = FXFM_CreateFontEncoding(pFont, id);
     if (pFontEncoding)
diff --git a/core/fxge/cfx_unicodeencodingex.h b/core/fxge/cfx_unicodeencodingex.h
index 5632d96..a9c1f58 100644
--- a/core/fxge/cfx_unicodeencodingex.h
+++ b/core/fxge/cfx_unicodeencodingex.h
@@ -7,30 +7,28 @@
 #ifndef CORE_FXGE_CFX_UNICODEENCODINGEX_H_
 #define CORE_FXGE_CFX_UNICODEENCODINGEX_H_
 
-#include <map>
 #include <memory>
-#include <vector>
 
 #include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_unicodeencoding.h"
-#include "core/fxge/fx_dib.h"
 
-class CFX_UnicodeEncodingEx : public CFX_UnicodeEncoding {
+class CFX_UnicodeEncodingEx final : public CFX_UnicodeEncoding {
  public:
+  static constexpr uint32_t kInvalidCharCode = static_cast<uint32_t>(-1);
+
   CFX_UnicodeEncodingEx(CFX_Font* pFont, uint32_t EncodingID);
   ~CFX_UnicodeEncodingEx() override;
 
   // CFX_UnicodeEncoding:
   uint32_t GlyphFromCharCode(uint32_t charcode) override;
 
+  // Returns |kInvalidCharCode| on error.
   uint32_t CharCodeFromUnicode(wchar_t Unicode) const;
 
  private:
   uint32_t m_nEncodingID;
 };
 
-std::unique_ptr<CFX_UnicodeEncodingEx> FX_CreateFontEncodingEx(
-    CFX_Font* pFont,
-    uint32_t nEncodingID);
+std::unique_ptr<CFX_UnicodeEncodingEx> FX_CreateFontEncodingEx(CFX_Font* pFont);
 
 #endif  // CORE_FXGE_CFX_UNICODEENCODINGEX_H_
diff --git a/core/fxge/cfx_windowsrenderdevice.h b/core/fxge/cfx_windowsrenderdevice.h
index f1e0cbd..3d96207 100644
--- a/core/fxge/cfx_windowsrenderdevice.h
+++ b/core/fxge/cfx_windowsrenderdevice.h
@@ -7,10 +7,7 @@
 #ifndef CORE_FXGE_CFX_WINDOWSRENDERDEVICE_H_
 #define CORE_FXGE_CFX_WINDOWSRENDERDEVICE_H_
 
-#ifdef _WIN32
-#ifndef _WINDOWS_
 #include <windows.h>
-#endif
 
 #include "core/fxge/cfx_renderdevice.h"
 
@@ -19,9 +16,12 @@
   kModeTextOnly = 1,
   kModePostScript2 = 2,
   kModePostScript3 = 3,
+  kModePostScript2PassThrough = 4,
+  kModePostScript3PassThrough = 5,
 };
 
-class IFX_RenderDeviceDriver;
+class RenderDeviceDriverIface;
+struct EncoderIface;
 
 #if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
 typedef void (*PDFiumEnsureTypefaceCharactersAccessible)(const LOGFONT* font,
@@ -32,16 +32,17 @@
 extern PDFiumEnsureTypefaceCharactersAccessible
     g_pdfium_typeface_accessible_func;
 #endif
-extern int g_pdfium_print_mode;
+extern WindowsPrintMode g_pdfium_print_mode;
 
 class CFX_WindowsRenderDevice : public CFX_RenderDevice {
  public:
-  static IFX_RenderDeviceDriver* CreateDriver(HDC hDC);
-
-  explicit CFX_WindowsRenderDevice(HDC hDC);
+  CFX_WindowsRenderDevice(HDC hDC, const EncoderIface* pEncoderIface);
   ~CFX_WindowsRenderDevice() override;
-};
 
-#endif  // _WIN32
+ private:
+  static RenderDeviceDriverIface* CreateDriver(
+      HDC hDC,
+      const EncoderIface* pEncoderIface);
+};
 
 #endif  // CORE_FXGE_CFX_WINDOWSRENDERDEVICE_H_
diff --git a/core/fxge/cttfontdesc.cpp b/core/fxge/cttfontdesc.cpp
deleted file mode 100644
index bdf2451..0000000
--- a/core/fxge/cttfontdesc.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/cttfontdesc.h"
-
-#include "core/fxge/fx_freetype.h"
-#include "third_party/base/stl_util.h"
-
-CTTFontDesc::CTTFontDesc(uint8_t* pData, FXFT_Face face)
-    : m_bIsTTC(false), m_SingleFace(face), m_pFontData(pData) {}
-
-CTTFontDesc::CTTFontDesc(uint8_t* pData, size_t index, FXFT_Face face)
-    : m_bIsTTC(true), m_pFontData(pData) {
-  for (size_t i = 0; i < FX_ArraySize(m_TTCFaces); i++)
-    m_TTCFaces[i] = nullptr;
-  SetTTCFace(index, face);
-}
-
-CTTFontDesc::~CTTFontDesc() {
-  ASSERT(m_RefCount == 0);
-  if (m_bIsTTC) {
-    for (size_t i = 0; i < FX_ArraySize(m_TTCFaces); i++) {
-      if (m_TTCFaces[i])
-        FXFT_Done_Face(m_TTCFaces[i]);
-    }
-  } else {
-    if (m_SingleFace)
-      FXFT_Done_Face(m_SingleFace);
-  }
-  FX_Free(m_pFontData);
-}
-
-void CTTFontDesc::SetTTCFace(size_t index, FXFT_Face face) {
-  ASSERT(m_bIsTTC);
-  ASSERT(index < FX_ArraySize(m_TTCFaces));
-  m_TTCFaces[index] = face;
-}
-
-void CTTFontDesc::AddRef() {
-  ASSERT(m_RefCount > 0);
-  ++m_RefCount;
-}
-
-CTTFontDesc::ReleaseStatus CTTFontDesc::ReleaseFace(FXFT_Face face) {
-  if (m_bIsTTC) {
-    if (!pdfium::ContainsValue(m_TTCFaces, face))
-      return kNotAppropriate;
-  } else {
-    if (m_SingleFace != face)
-      return kNotAppropriate;
-  }
-  ASSERT(m_RefCount > 0);
-  return --m_RefCount == 0 ? kReleased : kNotReleased;
-}
-
-FXFT_Face CTTFontDesc::SingleFace() const {
-  ASSERT(!m_bIsTTC);
-  return m_SingleFace;
-}
-
-FXFT_Face CTTFontDesc::TTCFace(size_t index) const {
-  ASSERT(m_bIsTTC);
-  ASSERT(index < FX_ArraySize(m_TTCFaces));
-  return m_TTCFaces[index];
-}
diff --git a/core/fxge/cttfontdesc.h b/core/fxge/cttfontdesc.h
deleted file mode 100644
index b48e78c..0000000
--- a/core/fxge/cttfontdesc.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_CTTFONTDESC_H_
-#define CORE_FXGE_CTTFONTDESC_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_font.h"
-
-class CTTFontDesc {
- public:
-  enum ReleaseStatus {
-    kNotAppropriate,  // ReleaseFace() not appropriate for given object.
-    kReleased,
-    kNotReleased  // Object still alive.
-  };
-
-  // Single face ctor.
-  CTTFontDesc(uint8_t* pData, FXFT_Face face);
-
-  // TTC face ctor.
-  CTTFontDesc(uint8_t* pData, size_t index, FXFT_Face face);
-
-  ~CTTFontDesc();
-
-  void SetTTCFace(size_t index, FXFT_Face face);
-
-  void AddRef();
-
-  // May not decrement refcount, depending on the value of |face|.
-  ReleaseStatus ReleaseFace(FXFT_Face face);
-
-  uint8_t* FontData() const { return m_pFontData; }
-
-  FXFT_Face SingleFace() const;
-  FXFT_Face TTCFace(size_t index) const;
-
- private:
-  const bool m_bIsTTC;
-
-  union {
-    const FXFT_Face m_SingleFace;
-    FXFT_Face m_TTCFaces[16];
-  };
-  uint8_t* const m_pFontData;
-  int m_RefCount = 1;
-};
-
-#endif  // CORE_FXGE_CTTFONTDESC_H_
diff --git a/core/fxge/dib/cfx_bitmapcomposer.cpp b/core/fxge/dib/cfx_bitmapcomposer.cpp
index 3854949..beb02b7 100644
--- a/core/fxge/dib/cfx_bitmapcomposer.cpp
+++ b/core/fxge/dib/cfx_bitmapcomposer.cpp
@@ -9,10 +9,9 @@
 #include "core/fxge/cfx_cliprgn.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 
-CFX_BitmapComposer::CFX_BitmapComposer()
-    : m_bRgbByteOrder(false), m_BlendType(FXDIB_BLEND_NORMAL) {}
+CFX_BitmapComposer::CFX_BitmapComposer() = default;
 
-CFX_BitmapComposer::~CFX_BitmapComposer() {}
+CFX_BitmapComposer::~CFX_BitmapComposer() = default;
 
 void CFX_BitmapComposer::Compose(const RetainPtr<CFX_DIBitmap>& pDest,
                                  const CFX_ClipRgn* pClipRgn,
@@ -23,8 +22,7 @@
                                  bool bFlipX,
                                  bool bFlipY,
                                  bool bRgbByteOrder,
-                                 int alpha_flag,
-                                 int blend_type) {
+                                 BlendMode blend_type) {
   m_pBitmap = pDest;
   m_pClipRgn = pClipRgn;
   m_DestLeft = dest_rect.left;
@@ -39,7 +37,6 @@
   m_bVertical = bVertical;
   m_bFlipX = bFlipX;
   m_bFlipY = bFlipY;
-  m_AlphaFlag = alpha_flag;
   m_bRgbByteOrder = bRgbByteOrder;
   m_BlendType = blend_type;
 }
@@ -50,9 +47,9 @@
                                  uint32_t* pSrcPalette) {
   m_SrcFormat = src_format;
   if (!m_Compositor.Init(m_pBitmap->GetFormat(), src_format, width, pSrcPalette,
-                         m_MaskColor, FXDIB_BLEND_NORMAL,
+                         m_MaskColor, BlendMode::kNormal,
                          m_pClipMask != nullptr || (m_BitmapAlpha < 255),
-                         m_bRgbByteOrder, m_AlphaFlag)) {
+                         m_bRgbByteOrder)) {
     return false;
   }
   if (m_bVertical) {
@@ -87,7 +84,7 @@
   if (m_SrcFormat == FXDIB_8bppMask) {
     m_Compositor.CompositeByteMaskLine(dest_scan, src_scan, dest_width,
                                        clip_scan, dst_extra_alpha);
-  } else if ((m_SrcFormat & 0xff) == 8) {
+  } else if (GetBppFromFormat(m_SrcFormat) == 8) {
     m_Compositor.CompositePalBitmapLine(dest_scan, src_scan, 0, dest_width,
                                         clip_scan, src_extra_alpha,
                                         dst_extra_alpha);
@@ -112,13 +109,11 @@
                     m_pClipMask->GetPitch() +
                 (m_DestLeft - m_pClipRgn->GetBox().left);
   }
-  uint8_t* dest_scan =
-      const_cast<uint8_t*>(m_pBitmap->GetScanline(line + m_DestTop)) +
-      m_DestLeft * m_pBitmap->GetBPP() / 8;
+  uint8_t* dest_scan = m_pBitmap->GetWritableScanline(line + m_DestTop) +
+                       m_DestLeft * m_pBitmap->GetBPP() / 8;
   uint8_t* dest_alpha_scan =
       m_pBitmap->m_pAlphaMask
-          ? const_cast<uint8_t*>(
-                m_pBitmap->m_pAlphaMask->GetScanline(line + m_DestTop)) +
+          ? m_pBitmap->m_pAlphaMask->GetWritableScanline(line + m_DestTop) +
                 m_DestLeft
           : nullptr;
   DoCompose(dest_scan, scanline, m_DestWidth, clip_scan, scan_extra_alpha,
diff --git a/core/fxge/dib/cfx_bitmapcomposer.h b/core/fxge/dib/cfx_bitmapcomposer.h
index 98c1f77..74a7efa 100644
--- a/core/fxge/dib/cfx_bitmapcomposer.h
+++ b/core/fxge/dib/cfx_bitmapcomposer.h
@@ -13,12 +13,13 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_scanlinecompositor.h"
-#include "core/fxge/dib/ifx_scanlinecomposer.h"
+#include "core/fxge/dib/scanlinecomposer_iface.h"
+#include "core/fxge/fx_dib.h"
 
 class CFX_ClipRgn;
 class CFX_DIBitmap;
 
-class CFX_BitmapComposer : public IFX_ScanlineComposer {
+class CFX_BitmapComposer final : public ScanlineComposerIface {
  public:
   CFX_BitmapComposer();
   ~CFX_BitmapComposer() override;
@@ -32,10 +33,9 @@
                bool bFlipX,
                bool bFlipY,
                bool bRgbByteOrder,
-               int alpha_flag,
-               int blend_type);
+               BlendMode blend_type);
 
-  // IFX_ScanlineComposer
+  // ScanlineComposerIface
   bool SetInfo(int width,
                int height,
                FXDIB_Format src_format,
@@ -70,9 +70,8 @@
   bool m_bVertical;
   bool m_bFlipX;
   bool m_bFlipY;
-  int m_AlphaFlag;
-  bool m_bRgbByteOrder;
-  int m_BlendType;
+  bool m_bRgbByteOrder = false;
+  BlendMode m_BlendType = BlendMode::kNormal;
   std::vector<uint8_t> m_pScanlineV;
   std::vector<uint8_t> m_pClipScanV;
   std::vector<uint8_t> m_pAddClipScan;
diff --git a/core/fxge/dib/cfx_bitmapstorer.cpp b/core/fxge/dib/cfx_bitmapstorer.cpp
index f649e0c..97ddf6f 100644
--- a/core/fxge/dib/cfx_bitmapstorer.cpp
+++ b/core/fxge/dib/cfx_bitmapstorer.cpp
@@ -8,7 +8,7 @@
 
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 
 CFX_BitmapStorer::CFX_BitmapStorer() {}
 
@@ -25,10 +25,10 @@
 void CFX_BitmapStorer::ComposeScanline(int line,
                                        const uint8_t* scanline,
                                        const uint8_t* scan_extra_alpha) {
-  uint8_t* dest_buf = const_cast<uint8_t*>(m_pBitmap->GetScanline(line));
+  uint8_t* dest_buf = m_pBitmap->GetWritableScanline(line);
   uint8_t* dest_alpha_buf =
       m_pBitmap->m_pAlphaMask
-          ? const_cast<uint8_t*>(m_pBitmap->m_pAlphaMask->GetScanline(line))
+          ? m_pBitmap->m_pAlphaMask->GetWritableScanline(line)
           : nullptr;
   if (dest_buf)
     memcpy(dest_buf, scanline, m_pBitmap->GetPitch());
diff --git a/core/fxge/dib/cfx_bitmapstorer.h b/core/fxge/dib/cfx_bitmapstorer.h
index 3d3416d..c8158a3 100644
--- a/core/fxge/dib/cfx_bitmapstorer.h
+++ b/core/fxge/dib/cfx_bitmapstorer.h
@@ -7,21 +7,18 @@
 #ifndef CORE_FXGE_DIB_CFX_BITMAPSTORER_H_
 #define CORE_FXGE_DIB_CFX_BITMAPSTORER_H_
 
-#include <memory>
-#include <vector>
-
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/ifx_scanlinecomposer.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxge/dib/scanlinecomposer_iface.h"
 
-class CFX_BitmapStorer : public IFX_ScanlineComposer {
+class CFX_DIBitmap;
+
+class CFX_BitmapStorer final : public ScanlineComposerIface {
  public:
   CFX_BitmapStorer();
   ~CFX_BitmapStorer() override;
 
-  // IFX_ScanlineComposer
+  // ScanlineComposerIface
   void ComposeScanline(int line,
                        const uint8_t* scanline,
                        const uint8_t* scan_extra_alpha) override;
diff --git a/core/fxge/dib/cfx_cmyk_to_srgb.cpp b/core/fxge/dib/cfx_cmyk_to_srgb.cpp
new file mode 100644
index 0000000..d75987c
--- /dev/null
+++ b/core/fxge/dib/cfx_cmyk_to_srgb.cpp
@@ -0,0 +1,1757 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
+
+#include <algorithm>
+#include <tuple>
+
+#include "core/fxcrt/fx_system.h"
+
+namespace fxge {
+
+namespace {
+
+constexpr uint8_t kCMYK[81 * 81][3] = {
+    {255, 255, 255}, {225, 226, 228}, {199, 200, 202}, {173, 174, 178},
+    {147, 149, 152}, {123, 125, 128}, {99, 99, 102},   {69, 70, 71},
+    {34, 30, 31},    {255, 253, 229}, {226, 224, 203}, {200, 199, 182},
+    {173, 173, 158}, {149, 148, 135}, {125, 124, 113}, {99, 99, 90},
+    {70, 69, 63},    {33, 29, 24},    {255, 251, 204}, {228, 223, 182},
+    {201, 198, 163}, {174, 172, 142}, {150, 147, 122}, {125, 123, 101},
+    {99, 98, 80},    {70, 68, 54},    {32, 28, 16},    {255, 249, 179},
+    {230, 222, 160}, {203, 197, 144}, {174, 170, 124}, {150, 145, 105},
+    {125, 122, 88},  {99, 97, 69},    {70, 68, 46},    {31, 28, 6},
+    {255, 247, 154}, {229, 220, 138}, {203, 195, 122}, {176, 169, 107},
+    {150, 145, 91},  {125, 121, 74},  {100, 96, 57},   {70, 67, 35},
+    {29, 26, 0},     {255, 246, 128}, {231, 217, 114}, {205, 194, 101},
+    {176, 167, 88},  {150, 144, 75},  {125, 120, 60},  {100, 96, 44},
+    {70, 66, 24},    {28, 26, 0},     {255, 244, 96},  {231, 217, 87},
+    {203, 192, 78},  {175, 167, 66},  {150, 143, 56},  {125, 119, 43},
+    {100, 95, 29},   {69, 66, 7},     {26, 26, 0},     {255, 243, 51},
+    {232, 215, 51},  {204, 191, 43},  {176, 165, 38},  {150, 142, 28},
+    {125, 118, 17},  {99, 94, 0},     {68, 65, 0},     {24, 25, 0},
+    {255, 241, 0},   {231, 215, 0},   {203, 190, 0},   {176, 164, 0},
+    {150, 141, 0},   {126, 117, 0},   {99, 93, 0},     {68, 65, 0},
+    {24, 25, 0},     {252, 228, 238}, {222, 201, 211}, {197, 180, 190},
+    {171, 156, 166}, {147, 133, 143}, {123, 111, 119}, {99, 88, 94},
+    {71, 61, 66},    {34, 22, 26},    {254, 226, 213}, {224, 201, 191},
+    {199, 179, 171}, {172, 155, 148}, {147, 133, 128}, {123, 110, 106},
+    {98, 87, 83},    {70, 59, 57},    {33, 21, 18},    {254, 224, 191},
+    {224, 199, 172}, {200, 177, 153}, {173, 154, 133}, {147, 132, 115},
+    {123, 109, 94},  {98, 86, 74},    {70, 59, 49},    {32, 21, 9},
+    {255, 222, 168}, {227, 198, 150}, {200, 175, 135}, {173, 153, 118},
+    {148, 130, 99},  {123, 109, 82},  {98, 86, 64},    {69, 58, 40},
+    {31, 19, 0},     {255, 221, 145}, {227, 196, 129}, {201, 174, 115},
+    {173, 151, 99},  {148, 129, 85},  {124, 108, 69},  {98, 85, 52},
+    {69, 58, 30},    {30, 19, 0},     {255, 219, 121}, {227, 195, 109},
+    {201, 174, 97},  {174, 150, 83},  {148, 129, 70},  {124, 107, 55},
+    {98, 84, 40},    {69, 58, 19},    {28, 18, 0},     {255, 218, 92},
+    {229, 194, 82},  {202, 173, 75},  {174, 150, 63},  {149, 128, 51},
+    {124, 106, 39},  {98, 84, 24},    {68, 57, 3},     {26, 18, 0},
+    {255, 217, 54},  {228, 193, 52},  {201, 172, 46},  {174, 148, 36},
+    {148, 127, 27},  {123, 105, 14},  {98, 83, 0},     {68, 56, 0},
+    {25, 18, 0},     {255, 216, 0},   {229, 192, 2},   {202, 171, 4},
+    {173, 148, 0},   {148, 126, 0},   {124, 105, 0},   {98, 83, 0},
+    {68, 56, 0},     {24, 17, 0},     {249, 204, 223}, {219, 181, 199},
+    {195, 160, 178}, {170, 140, 156}, {146, 119, 134}, {123, 99, 112},
+    {98, 77, 88},    {70, 52, 61},    {34, 11, 20},    {250, 201, 200},
+    {221, 180, 178}, {197, 159, 161}, {171, 139, 139}, {147, 119, 120},
+    {123, 98, 99},   {98, 77, 78},    {69, 51, 52},    {34, 11, 10},
+    {252, 201, 180}, {223, 179, 162}, {197, 159, 144}, {170, 138, 125},
+    {146, 117, 107}, {122, 97, 89},   {98, 76, 69},    {69, 50, 44},
+    {32, 11, 2},     {252, 199, 158}, {222, 177, 143}, {199, 158, 127},
+    {171, 137, 110}, {147, 117, 93},  {122, 96, 76},   {97, 75, 58},
+    {69, 50, 36},    {32, 10, 0},     {253, 198, 137}, {223, 177, 123},
+    {198, 156, 110}, {171, 136, 95},  {146, 116, 80},  {122, 96, 65},
+    {97, 75, 47},    {69, 50, 25},    {30, 10, 0},     {254, 197, 115},
+    {225, 175, 104}, {198, 156, 92},  {172, 135, 79},  {147, 115, 66},
+    {123, 95, 52},   {98, 74, 37},    {69, 49, 15},    {29, 10, 0},
+    {254, 196, 89},  {224, 175, 80},  {199, 154, 70},  {172, 134, 59},
+    {146, 114, 48},  {122, 95, 36},   {97, 74, 21},    {68, 49, 0},
+    {27, 9, 0},      {255, 195, 57},  {225, 173, 52},  {198, 154, 44},
+    {172, 133, 36},  {147, 113, 26},  {123, 94, 14},   {98, 74, 0},
+    {68, 49, 0},     {26, 10, 0},     {254, 194, 15},  {225, 172, 12},
+    {198, 153, 7},   {172, 132, 3},   {146, 113, 0},   {123, 93, 0},
+    {98, 73, 0},     {68, 49, 0},     {26, 9, 0},      {246, 178, 209},
+    {218, 159, 186}, {194, 140, 166}, {168, 122, 145}, {144, 104, 125},
+    {121, 85, 103},  {97, 65, 81},    {69, 41, 55},    {34, 0, 12},
+    {248, 176, 186}, {219, 157, 166}, {195, 139, 149}, {168, 121, 130},
+    {144, 103, 111}, {121, 85, 91},   {97, 65, 71},    {69, 41, 46},
+    {34, 0, 4},      {249, 175, 168}, {220, 156, 150}, {196, 139, 135},
+    {169, 121, 116}, {144, 103, 100}, {122, 84, 83},   {98, 65, 63},
+    {70, 41, 39},    {33, 0, 0},      {249, 175, 148}, {220, 155, 133},
+    {196, 138, 119}, {169, 120, 103}, {145, 101, 87},  {121, 83, 71},
+    {97, 65, 54},    {69, 41, 31},    {32, 0, 0},      {249, 173, 128},
+    {222, 154, 115}, {195, 137, 102}, {170, 119, 88},  {145, 101, 74},
+    {122, 83, 59},   {97, 64, 43},    {68, 40, 20},    {30, 0, 0},
+    {250, 172, 108}, {221, 154, 98},  {195, 136, 86},  {170, 118, 73},
+    {145, 100, 61},  {122, 82, 48},   {97, 63, 32},    {69, 40, 11},
+    {28, 0, 0},      {250, 171, 85},  {221, 153, 76},  {196, 136, 67},
+    {170, 117, 56},  {145, 99, 44},   {121, 82, 33},   {97, 63, 17},
+    {68, 40, 0},     {28, 0, 0},      {251, 171, 58},  {222, 152, 50},
+    {197, 135, 43},  {169, 117, 34},  {146, 99, 25},   {121, 81, 10},
+    {96, 63, 0},     {68, 40, 0},     {27, 0, 0},      {250, 170, 26},
+    {222, 151, 19},  {196, 134, 13},  {169, 116, 4},   {145, 99, 0},
+    {122, 81, 0},    {97, 63, 0},     {67, 40, 0},     {26, 0, 0},
+    {244, 153, 194}, {215, 136, 173}, {192, 121, 155}, {167, 104, 135},
+    {143, 89, 115},  {121, 72, 96},   {97, 54, 75},    {70, 31, 49},
+    {34, 0, 6},      {245, 153, 173}, {216, 136, 155}, {192, 120, 138},
+    {167, 104, 121}, {144, 88, 103},  {121, 71, 85},   {97, 54, 66},
+    {69, 31, 42},    {34, 0, 0},      {246, 152, 157}, {217, 135, 140},
+    {193, 120, 126}, {167, 103, 109}, {143, 88, 92},   {121, 72, 76},
+    {97, 54, 58},    {69, 31, 35},    {33, 0, 0},      {245, 150, 139},
+    {218, 134, 125}, {193, 119, 111}, {167, 103, 96},  {144, 87, 80},
+    {121, 71, 66},   {96, 53, 49},    {68, 31, 26},    {32, 0, 0},
+    {246, 151, 122}, {218, 133, 108}, {194, 118, 96},  {168, 102, 81},
+    {144, 86, 69},   {120, 71, 55},   {95, 53, 39},    {68, 30, 17},
+    {31, 0, 0},      {248, 150, 103}, {218, 133, 91},  {193, 118, 81},
+    {168, 102, 69},  {143, 86, 56},   {120, 70, 43},   {96, 53, 28},
+    {68, 31, 6},     {29, 0, 0},      {247, 149, 81},  {218, 132, 72},
+    {194, 117, 62},  {168, 101, 52},  {144, 86, 42},   {121, 70, 29},
+    {96, 52, 13},    {68, 30, 0},     {28, 0, 0},      {247, 148, 55},
+    {219, 131, 50},  {194, 117, 43},  {167, 101, 32},  {144, 85, 22},
+    {120, 69, 8},    {96, 52, 0},     {67, 30, 0},     {27, 0, 0},
+    {247, 147, 29},  {218, 131, 24},  {194, 116, 20},  {168, 100, 11},
+    {144, 85, 0},    {120, 69, 0},    {96, 52, 0},     {67, 30, 0},
+    {26, 0, 0},      {242, 130, 179}, {214, 114, 160}, {190, 101, 143},
+    {166, 87, 125},  {143, 72, 107},  {120, 58, 88},   {96, 42, 68},
+    {69, 17, 44},    {35, 0, 0},      {243, 129, 161}, {215, 114, 143},
+    {191, 101, 128}, {166, 87, 113},  {143, 73, 96},   {120, 58, 79},
+    {96, 41, 60},    {69, 18, 37},    {33, 0, 0},      {243, 129, 146},
+    {216, 114, 130}, {192, 101, 117}, {166, 87, 101},  {143, 72, 86},
+    {121, 58, 69},   {96, 42, 52},    {69, 18, 29},    {31, 0, 0},
+    {243, 128, 130}, {216, 114, 115}, {191, 101, 102}, {165, 86, 88},
+    {142, 72, 75},   {120, 58, 60},   {95, 42, 43},    {68, 19, 21},
+    {30, 0, 0},      {244, 127, 112}, {217, 113, 101}, {192, 99, 89},
+    {166, 85, 75},   {142, 72, 63},   {119, 57, 50},   {96, 41, 35},
+    {68, 19, 13},    {30, 0, 0},      {244, 127, 96},  {216, 112, 86},
+    {191, 99, 75},   {166, 86, 64},   {143, 72, 52},   {120, 57, 40},
+    {95, 41, 24},    {67, 20, 1},     {29, 0, 0},      {245, 126, 77},
+    {216, 113, 68},  {191, 100, 59},  {166, 85, 49},   {142, 71, 38},
+    {119, 57, 26},   {95, 41, 10},    {67, 20, 0},     {28, 0, 0},
+    {244, 126, 55},  {216, 112, 48},  {191, 99, 40},   {166, 85, 31},
+    {143, 71, 20},   {119, 57, 6},    {95, 42, 0},     {67, 20, 0},
+    {28, 0, 0},      {245, 126, 33},  {217, 112, 26},  {192, 99, 22},
+    {166, 84, 11},   {142, 70, 0},    {119, 57, 0},    {95, 41, 0},
+    {66, 20, 0},     {27, 0, 0},      {241, 102, 167}, {213, 90, 149},
+    {189, 79, 133},  {165, 66, 115},  {141, 54, 98},   {119, 41, 81},
+    {96, 25, 63},    {69, 0, 38},     {30, 0, 0},      {241, 102, 149},
+    {213, 90, 133},  {189, 79, 119},  {165, 66, 103},  {142, 55, 88},
+    {119, 41, 71},   {96, 25, 53},    {69, 0, 31},     {28, 0, 0},
+    {241, 102, 135}, {214, 90, 121},  {190, 79, 108},  {165, 66, 92},
+    {141, 55, 78},   {119, 42, 63},   {96, 26, 46},    {69, 0, 24},
+    {28, 0, 0},      {241, 101, 120}, {214, 90, 107},  {189, 79, 95},
+    {165, 67, 83},   {141, 54, 68},   {118, 41, 54},   {95, 27, 39},
+    {68, 0, 16},     {27, 0, 0},      {241, 102, 106}, {213, 90, 93},
+    {189, 78, 82},   {164, 67, 70},   {141, 55, 58},   {118, 42, 45},
+    {94, 27, 29},    {67, 2, 6},      {27, 0, 0},      {242, 101, 90},
+    {214, 89, 79},   {190, 79, 69},   {166, 67, 59},   {141, 55, 47},
+    {118, 41, 35},   {95, 27, 19},    {67, 3, 0},      {26, 0, 0},
+    {242, 102, 72},  {213, 89, 63},   {191, 79, 56},   {164, 67, 45},
+    {141, 55, 34},   {118, 42, 22},   {94, 28, 6},     {67, 3, 0},
+    {26, 0, 0},      {242, 100, 51},  {214, 89, 45},   {190, 78, 38},
+    {164, 67, 30},   {141, 55, 18},   {118, 42, 3},    {95, 28, 0},
+    {66, 4, 0},      {26, 0, 0},      {243, 100, 33},  {214, 90, 27},
+    {190, 78, 22},   {165, 67, 13},   {141, 55, 0},    {118, 43, 0},
+    {94, 29, 0},     {66, 5, 0},      {26, 0, 0},      {237, 69, 153},
+    {211, 58, 135},  {187, 51, 121},  {163, 41, 105},  {141, 28, 90},
+    {118, 15, 73},   {96, 0, 56},     {68, 0, 33},     {25, 0, 0},
+    {239, 67, 137},  {212, 60, 123},  {189, 50, 110},  {163, 41, 94},
+    {141, 29, 79},   {118, 17, 65},   {95, 0, 48},     {69, 0, 26},
+    {25, 0, 0},      {240, 69, 124},  {211, 60, 111},  {188, 50, 98},
+    {163, 42, 85},   {141, 31, 72},   {118, 18, 57},   {94, 0, 41},
+    {68, 0, 19},     {25, 0, 0},      {240, 70, 112},  {212, 61, 99},
+    {188, 52, 87},   {163, 41, 74},   {140, 31, 62},   {118, 20, 48},
+    {94, 2, 32},     {68, 0, 11},     {24, 0, 0},      {239, 70, 98},
+    {212, 62, 86},   {188, 53, 77},   {164, 42, 64},   {140, 32, 52},
+    {118, 20, 40},   {94, 3, 24},     {67, 0, 3},      {23, 0, 0},
+    {239, 71, 85},   {212, 61, 74},   {187, 53, 65},   {163, 44, 54},
+    {140, 34, 43},   {118, 22, 30},   {95, 3, 14},     {67, 0, 0},
+    {23, 0, 0},      {239, 70, 67},   {212, 62, 59},   {188, 53, 51},
+    {163, 45, 42},   {141, 34, 31},   {117, 22, 17},   {94, 5, 2},
+    {66, 0, 0},      {23, 0, 0},      {239, 71, 50},   {213, 62, 43},
+    {188, 54, 37},   {164, 45, 28},   {139, 34, 16},   {117, 22, 2},
+    {94, 7, 0},      {65, 0, 0},      {23, 0, 0},      {240, 71, 34},
+    {212, 63, 29},   {189, 54, 24},   {163, 46, 15},   {139, 36, 2},
+    {117, 25, 0},    {94, 8, 0},      {66, 0, 0},      {23, 0, 0},
+    {237, 0, 140},   {209, 0, 124},   {186, 0, 112},   {162, 0, 97},
+    {141, 0, 82},    {118, 0, 67},    {95, 0, 49},     {68, 0, 27},
+    {20, 0, 0},      {237, 0, 126},   {210, 0, 113},   {187, 0, 99},
+    {163, 0, 86},    {139, 0, 72},    {118, 0, 58},    {95, 0, 42},
+    {67, 0, 20},     {20, 0, 0},      {237, 1, 114},   {209, 1, 102},
+    {187, 0, 90},    {163, 0, 78},    {139, 0, 64},    {118, 0, 50},
+    {95, 0, 35},     {67, 0, 13},     {20, 0, 0},      {236, 16, 102},
+    {209, 7, 91},    {186, 0, 80},    {162, 0, 68},    {139, 0, 56},
+    {117, 0, 43},    {94, 0, 27},     {67, 0, 6},      {20, 0, 0},
+    {238, 15, 89},   {209, 13, 79},   {186, 6, 69},    {162, 0, 58},
+    {139, 0, 47},    {117, 0, 34},    {93, 0, 20},     {66, 0, 2},
+    {20, 0, 0},      {237, 20, 78},   {210, 12, 68},   {187, 4, 59},
+    {163, 0, 49},    {139, 0, 38},    {116, 0, 26},    {94, 0, 11},
+    {66, 0, 0},      {20, 0, 0},      {237, 25, 64},   {210, 18, 56},
+    {186, 11, 48},   {162, 4, 39},    {138, 0, 27},    {117, 0, 14},
+    {93, 0, 0},      {66, 0, 0},      {20, 0, 0},      {238, 25, 48},
+    {210, 22, 43},   {186, 15, 35},   {162, 8, 26},    {140, 0, 14},
+    {117, 0, 0},     {93, 0, 0},      {65, 0, 0},      {20, 0, 0},
+    {238, 28, 35},   {210, 21, 30},   {187, 15, 24},   {162, 8, 16},
+    {139, 1, 2},     {117, 0, 0},     {93, 0, 0},      {65, 0, 0},
+    {22, 0, 0},      {219, 242, 252}, {195, 214, 225}, {172, 191, 201},
+    {148, 165, 175}, {127, 142, 150}, {106, 119, 126}, {84, 95, 101},
+    {58, 66, 72},    {24, 27, 32},    {222, 239, 226}, {196, 213, 202},
+    {173, 189, 180}, {150, 165, 158}, {129, 141, 135}, {107, 118, 113},
+    {85, 94, 90},    {58, 66, 63},    {21, 26, 24},    {223, 237, 203},
+    {198, 211, 182}, {175, 188, 163}, {152, 164, 141}, {129, 140, 121},
+    {107, 117, 101}, {85, 93, 80},    {58, 64, 54},    {21, 26, 18},
+    {226, 236, 179}, {201, 210, 160}, {177, 187, 143}, {153, 162, 125},
+    {130, 139, 106}, {108, 116, 89},  {85, 92, 69},    {58, 64, 45},
+    {20, 25, 8},     {227, 234, 153}, {201, 208, 139}, {178, 185, 124},
+    {154, 161, 107}, {131, 138, 91},  {108, 115, 75},  {85, 91, 58},
+    {58, 63, 35},    {17, 25, 0},     {229, 233, 130}, {203, 207, 116},
+    {178, 184, 104}, {154, 160, 90},  {131, 137, 76},  {109, 114, 62},
+    {85, 90, 46},    {58, 63, 25},    {16, 24, 0},     {230, 231, 100},
+    {202, 205, 90},  {179, 183, 80},  {154, 159, 69},  {131, 136, 57},
+    {109, 113, 46},  {86, 90, 32},    {58, 63, 10},    {14, 24, 0},
+    {230, 230, 65},  {204, 204, 58},  {180, 182, 52},  {155, 157, 44},
+    {132, 135, 35},  {110, 113, 24},  {86, 89, 9},     {57, 62, 0},
+    {11, 24, 0},     {232, 230, 19},  {204, 204, 19},  {180, 181, 17},
+    {155, 157, 10},  {131, 134, 2},   {109, 112, 0},   {85, 89, 0},
+    {57, 62, 0},     {10, 23, 0},     {218, 216, 236}, {194, 192, 211},
+    {172, 171, 188}, {149, 149, 164}, {128, 127, 141}, {106, 106, 119},
+    {84, 84, 94},    {59, 57, 66},    {25, 18, 26},    {221, 214, 211},
+    {196, 191, 190}, {174, 170, 170}, {150, 148, 148}, {128, 126, 127},
+    {107, 105, 106}, {85, 83, 84},    {59, 56, 58},    {23, 17, 18},
+    {222, 213, 190}, {197, 189, 170}, {175, 169, 153}, {151, 147, 133},
+    {129, 126, 113}, {108, 105, 94},  {85, 82, 74},    {59, 56, 49},
+    {22, 17, 11},    {224, 211, 168}, {199, 188, 151}, {175, 168, 135},
+    {152, 146, 117}, {129, 124, 99},  {107, 103, 82},  {84, 82, 64},
+    {59, 55, 41},    {21, 17, 1},     {224, 210, 145}, {199, 187, 130},
+    {176, 166, 117}, {152, 145, 101}, {129, 123, 86},  {107, 103, 70},
+    {85, 81, 53},    {58, 55, 31},    {19, 17, 0},     {227, 208, 123},
+    {200, 186, 110}, {177, 165, 98},  {153, 143, 84},  {130, 122, 70},
+    {108, 102, 57},  {85, 80, 41},    {58, 54, 20},    {18, 16, 0},
+    {227, 208, 97},  {202, 185, 86},  {177, 164, 77},  {153, 142, 65},
+    {130, 122, 54},  {108, 101, 42},  {85, 80, 27},    {58, 54, 7},
+    {16, 16, 0},     {228, 206, 66},  {202, 184, 58},  {178, 163, 50},
+    {154, 141, 42},  {131, 121, 33},  {109, 101, 21},  {86, 79, 5},
+    {58, 54, 0},     {13, 16, 0},     {228, 206, 29},  {202, 183, 25},
+    {178, 163, 20},  {154, 141, 15},  {131, 121, 5},   {108, 100, 0},
+    {85, 79, 0},     {58, 53, 0},     {13, 16, 0},     {217, 193, 221},
+    {193, 172, 198}, {172, 153, 178}, {149, 133, 154}, {128, 114, 132},
+    {107, 94, 111},  {85, 74, 89},    {59, 49, 61},    {25, 8, 22},
+    {219, 191, 198}, {195, 171, 178}, {173, 153, 159}, {149, 132, 139},
+    {128, 113, 119}, {107, 94, 100},  {85, 73, 79},    {59, 48, 52},
+    {25, 7, 14},     {221, 191, 180}, {196, 170, 160}, {174, 152, 144},
+    {150, 132, 125}, {129, 113, 107}, {107, 93, 89},   {85, 73, 69},
+    {59, 48, 45},    {23, 7, 4},      {222, 189, 159}, {197, 169, 142},
+    {174, 151, 127}, {151, 131, 110}, {129, 112, 94},  {108, 93, 78},
+    {85, 72, 60},    {58, 47, 37},    {22, 7, 0},      {223, 188, 138},
+    {197, 168, 123}, {175, 150, 109}, {151, 130, 95},  {130, 111, 81},
+    {108, 92, 65},   {85, 72, 49},    {59, 47, 27},    {21, 7, 0},
+    {224, 187, 118}, {198, 167, 105}, {176, 149, 93},  {152, 129, 79},
+    {130, 110, 68},  {108, 91, 54},   {85, 71, 38},    {59, 47, 17},
+    {18, 7, 0},      {224, 187, 93},  {199, 166, 83},  {176, 148, 73},
+    {152, 128, 62},  {129, 109, 51},  {108, 90, 39},   {85, 71, 25},
+    {58, 46, 3},     {16, 8, 0},      {226, 186, 64},  {200, 165, 57},
+    {177, 147, 50},  {153, 127, 40},  {130, 108, 31},  {108, 90, 19},
+    {85, 70, 3},     {58, 46, 0},     {16, 8, 0},      {227, 185, 35},
+    {200, 165, 30},  {176, 146, 25},  {152, 127, 18},  {130, 108, 7},
+    {108, 89, 0},    {85, 70, 0},     {57, 46, 0},     {14, 8, 0},
+    {216, 169, 205}, {192, 150, 184}, {171, 134, 164}, {149, 116, 144},
+    {128, 99, 124},  {107, 81, 103},  {85, 63, 81},    {60, 39, 55},
+    {26, 0, 15},     {217, 168, 186}, {193, 150, 165}, {172, 134, 149},
+    {150, 116, 130}, {128, 99, 111},  {107, 81, 92},   {85, 62, 72},
+    {59, 39, 47},    {25, 0, 6},      {219, 168, 168}, {194, 149, 150},
+    {173, 133, 135}, {150, 116, 117}, {128, 98, 99},   {107, 80, 82},
+    {86, 62, 63},    {59, 38, 39},    {24, 0, 0},      {219, 166, 148},
+    {195, 149, 133}, {173, 133, 119}, {150, 115, 103}, {128, 98, 88},
+    {107, 80, 72},   {85, 61, 54},    {59, 38, 32},    {23, 0, 0},
+    {220, 166, 129}, {196, 148, 116}, {174, 132, 103}, {151, 114, 89},
+    {129, 97, 75},   {107, 79, 60},   {85, 61, 44},    {59, 38, 22},
+    {21, 0, 0},      {222, 164, 110}, {197, 147, 99},  {175, 131, 87},
+    {151, 113, 75},  {129, 96, 63},   {107, 79, 49},   {85, 61, 33},
+    {58, 38, 12},    {19, 0, 0},      {222, 164, 88},  {197, 146, 79},
+    {174, 130, 69},  {151, 113, 58},  {129, 95, 47},   {107, 78, 35},
+    {85, 60, 20},    {58, 38, 0},     {18, 0, 0},      {223, 164, 63},
+    {198, 145, 55},  {175, 129, 48},  {151, 112, 39},  {129, 95, 29},
+    {107, 78, 16},   {85, 60, 1},     {58, 38, 0},     {17, 0, 0},
+    {223, 163, 36},  {198, 145, 32},  {174, 129, 26},  {151, 111, 17},
+    {129, 95, 7},    {107, 78, 0},    {84, 60, 0},     {57, 37, 0},
+    {15, 0, 0},      {215, 147, 192}, {191, 130, 172}, {170, 116, 153},
+    {148, 100, 133}, {127, 85, 115},  {107, 69, 96},   {85, 51, 75},
+    {60, 28, 50},    {25, 0, 8},      {217, 146, 173}, {192, 130, 154},
+    {171, 115, 138}, {149, 100, 121}, {128, 84, 103},  {107, 68, 85},
+    {85, 51, 66},    {60, 28, 42},    {25, 0, 0},      {217, 145, 157},
+    {193, 129, 140}, {173, 115, 125}, {149, 100, 109}, {128, 84, 92},
+    {107, 68, 76},   {85, 51, 58},    {59, 28, 35},    {23, 0, 0},
+    {218, 145, 140}, {193, 129, 125}, {172, 114, 110}, {149, 99, 96},
+    {128, 83, 81},   {107, 67, 65},   {84, 51, 49},    {59, 29, 27},
+    {22, 0, 0},      {219, 144, 121}, {194, 128, 108}, {172, 113, 96},
+    {149, 98, 83},   {128, 83, 69},   {107, 68, 55},   {85, 50, 40},
+    {59, 28, 18},    {20, 0, 0},      {220, 143, 104}, {195, 128, 93},
+    {173, 114, 82},  {150, 98, 69},   {127, 82, 58},   {107, 67, 45},
+    {85, 50, 30},    {59, 28, 7},     {19, 0, 0},      {220, 143, 84},
+    {195, 127, 74},  {173, 113, 65},  {149, 97, 55},   {128, 82, 44},
+    {106, 67, 32},   {84, 50, 16},    {58, 28, 0},     {18, 0, 0},
+    {221, 142, 62},  {196, 126, 53},  {173, 112, 46},  {150, 97, 37},
+    {128, 82, 26},   {107, 66, 14},   {84, 50, 0},     {58, 28, 0},
+    {16, 0, 0},      {222, 142, 38},  {196, 126, 34},  {174, 112, 27},
+    {150, 96, 17},   {128, 82, 6},    {106, 66, 0},    {84, 50, 0},
+    {57, 29, 0},     {16, 0, 0},      {214, 123, 179}, {191, 110, 159},
+    {169, 98, 143},  {147, 84, 124},  {126, 70, 106},  {107, 55, 88},
+    {85, 39, 69},    {60, 15, 45},    {23, 0, 2},      {216, 123, 161},
+    {192, 110, 144}, {170, 98, 129},  {148, 84, 112},  {127, 70, 95},
+    {107, 55, 79},   {85, 39, 61},    {60, 15, 37},    {20, 0, 0},
+    {217, 122, 145}, {192, 110, 130}, {170, 97, 116},  {149, 84, 101},
+    {127, 70, 85},   {106, 55, 70},   {85, 39, 53},    {59, 16, 30},
+    {19, 0, 0},      {217, 123, 131}, {192, 109, 116}, {171, 96, 103},
+    {149, 83, 89},   {127, 70, 75},   {106, 55, 60},   {85, 40, 45},
+    {59, 16, 23},    {17, 0, 0},      {217, 122, 114}, {193, 109, 101},
+    {172, 96, 91},   {149, 82, 77},   {128, 69, 64},   {106, 55, 50},
+    {84, 39, 35},    {59, 17, 14},    {17, 0, 0},      {218, 122, 98},
+    {194, 108, 87},  {171, 96, 77},   {149, 82, 65},   {127, 69, 52},
+    {106, 55, 40},   {84, 40, 25},    {59, 18, 3},     {15, 0, 0},
+    {219, 122, 80},  {193, 108, 70},  {172, 95, 61},   {149, 82, 51},
+    {127, 69, 40},   {106, 55, 28},   {84, 39, 12},    {58, 17, 0},
+    {13, 0, 0},      {219, 121, 59},  {194, 108, 52},  {172, 96, 44},
+    {149, 82, 35},   {127, 68, 24},   {106, 55, 11},   {84, 40, 0},
+    {57, 18, 0},     {13, 0, 0},      {219, 121, 40},  {193, 108, 33},
+    {172, 95, 26},   {149, 81, 19},   {128, 68, 6},    {106, 54, 0},
+    {84, 39, 0},     {57, 18, 0},     {13, 0, 0},      {213, 99, 165},
+    {189, 87, 148},  {169, 76, 132},  {147, 64, 115},  {126, 52, 98},
+    {106, 39, 81},   {85, 23, 63},    {60, 0, 39},     {16, 0, 0},
+    {214, 98, 149},  {191, 87, 133},  {170, 76, 119},  {148, 65, 103},
+    {127, 53, 88},   {106, 39, 72},   {85, 24, 55},    {60, 0, 32},
+    {15, 0, 0},      {215, 99, 136},  {191, 87, 121},  {170, 77, 108},
+    {148, 65, 93},   {126, 53, 79},   {106, 40, 64},   {85, 24, 47},
+    {59, 0, 25},     {14, 0, 0},      {215, 99, 121},  {192, 87, 108},
+    {170, 77, 96},   {148, 65, 82},   {126, 53, 69},   {106, 40, 55},
+    {85, 25, 39},    {59, 0, 18},     {13, 0, 0},      {216, 99, 106},
+    {191, 87, 95},   {170, 76, 83},   {148, 65, 71},   {126, 53, 58},
+    {106, 41, 45},   {85, 26, 30},    {59, 0, 8},      {11, 0, 0},
+    {216, 98, 91},   {192, 88, 82},   {170, 77, 71},   {148, 65, 60},
+    {127, 53, 48},   {105, 41, 36},   {83, 26, 21},    {58, 1, 2},
+    {11, 0, 0},      {217, 99, 75},   {192, 87, 66},   {170, 76, 57},
+    {148, 65, 47},   {126, 53, 36},   {105, 41, 24},   {83, 26, 8},
+    {57, 2, 0},      {9, 0, 0},       {217, 98, 57},   {192, 87, 49},
+    {171, 77, 41},   {147, 65, 32},   {126, 53, 21},   {105, 41, 8},
+    {84, 27, 0},     {57, 3, 0},      {9, 0, 0},       {217, 98, 40},
+    {193, 87, 34},   {171, 76, 27},   {148, 65, 19},   {126, 53, 6},
+    {105, 41, 0},    {83, 27, 0},     {57, 4, 0},      {9, 0, 0},
+    {211, 67, 152},  {189, 58, 136},  {168, 50, 122},  {147, 39, 105},
+    {127, 28, 89},   {106, 14, 74},   {85, 0, 56},     {59, 0, 33},
+    {9, 0, 0},       {213, 68, 138},  {190, 59, 123},  {169, 51, 109},
+    {148, 40, 95},   {126, 30, 80},   {106, 16, 65},   {85, 0, 48},
+    {59, 0, 27},     {9, 0, 0},       {214, 69, 125},  {190, 59, 111},
+    {168, 51, 99},   {148, 41, 86},   {126, 31, 72},   {106, 18, 58},
+    {85, 0, 41},     {59, 0, 20},     {7, 0, 0},       {215, 70, 112},
+    {190, 61, 100},  {169, 52, 88},   {147, 42, 76},   {126, 32, 63},
+    {106, 19, 49},   {84, 1, 34},     {58, 0, 13},     {7, 0, 0},
+    {214, 70, 99},   {190, 62, 88},   {169, 53, 77},   {147, 43, 65},
+    {125, 32, 53},   {106, 20, 40},   {84, 3, 26},     {58, 0, 4},
+    {7, 0, 0},       {214, 71, 86},   {190, 61, 75},   {169, 53, 65},
+    {146, 43, 54},   {126, 33, 44},   {105, 21, 31},   {83, 4, 17},
+    {57, 0, 0},      {7, 0, 0},       {215, 71, 71},   {191, 62, 62},
+    {169, 53, 53},   {147, 44, 44},   {126, 34, 33},   {105, 22, 20},
+    {83, 5, 4},      {57, 0, 0},      {7, 0, 0},       {215, 71, 54},
+    {191, 62, 47},   {169, 54, 39},   {147, 44, 30},   {126, 35, 20},
+    {105, 23, 6},    {83, 6, 0},      {56, 0, 0},      {5, 0, 0},
+    {215, 71, 41},   {191, 63, 34},   {170, 54, 27},   {147, 45, 17},
+    {126, 35, 6},    {105, 23, 0},    {83, 8, 0},      {56, 0, 0},
+    {5, 0, 0},       {210, 13, 140},  {189, 1, 125},   {167, 0, 110},
+    {146, 0, 96},    {126, 0, 81},    {106, 0, 67},    {85, 0, 51},
+    {59, 0, 28},     {4, 0, 0},       {212, 18, 126},  {190, 7, 113},
+    {168, 0, 100},   {146, 0, 86},    {126, 0, 73},    {106, 0, 59},
+    {84, 0, 43},     {59, 0, 22},     {4, 0, 0},       {212, 21, 115},
+    {190, 13, 103},  {168, 3, 91},    {146, 0, 78},    {125, 0, 65},
+    {105, 0, 52},    {84, 0, 36},     {58, 0, 16},     {4, 0, 0},
+    {213, 24, 103},  {189, 19, 91},   {168, 9, 82},    {146, 0, 69},
+    {125, 0, 57},    {105, 0, 44},    {84, 0, 29},     {58, 0, 7},
+    {4, 0, 0},       {213, 27, 92},   {188, 21, 81},   {168, 14, 71},
+    {146, 1, 59},    {125, 0, 48},    {105, 0, 36},    {84, 0, 21},
+    {58, 0, 4},      {4, 0, 0},       {213, 30, 80},   {189, 22, 69},
+    {168, 17, 61},   {146, 5, 50},    {125, 0, 39},    {104, 0, 27},
+    {83, 0, 12},     {57, 0, 0},      {4, 0, 0},       {214, 30, 67},
+    {189, 25, 57},   {168, 20, 50},   {146, 9, 40},    {125, 0, 29},
+    {104, 0, 17},    {83, 0, 2},      {56, 0, 0},      {4, 0, 0},
+    {214, 32, 53},   {189, 27, 44},   {169, 20, 38},   {146, 13, 28},
+    {124, 2, 17},    {104, 0, 4},     {83, 0, 0},      {56, 0, 0},
+    {4, 0, 0},       {214, 33, 41},   {190, 27, 33},   {168, 23, 27},
+    {146, 13, 18},   {125, 3, 5},     {105, 0, 0},     {83, 0, 0},
+    {56, 0, 0},      {4, 0, 0},       {185, 229, 250}, {164, 204, 223},
+    {146, 182, 199}, {127, 158, 174}, {108, 136, 149}, {89, 113, 125},
+    {70, 90, 100},   {46, 62, 71},    {10, 25, 33},    {189, 227, 225},
+    {168, 202, 201}, {148, 181, 179}, {129, 157, 156}, {109, 135, 134},
+    {90, 113, 113},  {70, 89, 90},    {46, 62, 62},    {8, 24, 25},
+    {192, 226, 202}, {170, 202, 182}, {151, 179, 162}, {130, 156, 141},
+    {110, 133, 121}, {91, 112, 101},  {71, 89, 80},    {46, 61, 54},
+    {7, 24, 19},     {194, 224, 179}, {173, 200, 160}, {153, 178, 144},
+    {132, 155, 125}, {112, 133, 107}, {92, 111, 89},   {71, 88, 69},
+    {46, 61, 45},    {6, 23, 10},     {196, 223, 155}, {174, 198, 139},
+    {154, 176, 124}, {132, 153, 107}, {113, 131, 91},  {92, 110, 75},
+    {72, 87, 58},    {47, 60, 37},    {4, 23, 0},      {198, 221, 131},
+    {175, 197, 117}, {155, 175, 105}, {133, 152, 91},  {113, 130, 76},
+    {92, 109, 63},   {72, 86, 47},    {46, 60, 26},    {3, 23, 0},
+    {200, 220, 104}, {176, 196, 94},  {156, 175, 84},  {134, 151, 72},
+    {113, 129, 59},  {93, 108, 47},   {72, 85, 33},    {46, 59, 13},
+    {0, 23, 0},      {201, 219, 73},  {179, 195, 65},  {157, 173, 57},
+    {135, 150, 48},  {114, 129, 39},  {94, 108, 28},   {72, 85, 15},
+    {47, 59, 0},     {0, 22, 0},      {203, 219, 42},  {178, 195, 37},
+    {157, 173, 32},  {135, 150, 26},  {114, 128, 16},  {94, 107, 6},
+    {73, 85, 0},     {46, 58, 0},     {0, 22, 0},      {186, 205, 233},
+    {165, 183, 209}, {148, 163, 187}, {128, 142, 163}, {109, 121, 140},
+    {91, 101, 118},  {71, 80, 94},    {48, 54, 66},    {12, 15, 27},
+    {189, 204, 211}, {169, 182, 189}, {151, 163, 169}, {131, 141, 147},
+    {111, 121, 126}, {92, 101, 105},  {72, 79, 84},    {48, 54, 58},
+    {11, 15, 19},    {192, 202, 190}, {171, 181, 170}, {152, 161, 152},
+    {131, 141, 133}, {112, 120, 113}, {93, 100, 94},   {72, 79, 74},
+    {48, 53, 50},    {10, 15, 11},    {195, 201, 169}, {172, 179, 151},
+    {153, 160, 135}, {132, 139, 117}, {113, 119, 100}, {93, 99, 82},
+    {72, 78, 64},    {48, 53, 41},    {9, 14, 3},      {195, 200, 146},
+    {174, 179, 131}, {154, 159, 117}, {133, 138, 101}, {113, 118, 86},
+    {93, 98, 70},    {73, 77, 53},    {48, 52, 32},    {8, 15, 0},
+    {198, 199, 125}, {175, 177, 111}, {155, 158, 100}, {133, 137, 85},
+    {113, 117, 71},  {93, 97, 57},    {72, 77, 42},    {47, 52, 22},
+    {5, 14, 0},      {199, 198, 101}, {176, 177, 89},  {155, 157, 79},
+    {134, 136, 68},  {113, 116, 56},  {94, 97, 44},    {73, 76, 30},
+    {47, 52, 10},    {2, 15, 0},      {200, 197, 72},  {178, 176, 63},
+    {157, 156, 56},  {135, 136, 46},  {114, 116, 37},  {94, 96, 26},
+    {73, 76, 11},    {47, 51, 0},     {0, 14, 0},      {201, 197, 45},
+    {177, 175, 38},  {156, 155, 31},  {135, 135, 25},  {114, 115, 17},
+    {94, 96, 5},     {73, 75, 0},     {46, 51, 0},     {0, 14, 0},
+    {187, 183, 218}, {167, 165, 197}, {149, 147, 176}, {129, 127, 153},
+    {111, 109, 132}, {92, 90, 111},   {73, 70, 89},    {49, 46, 62},
+    {15, 4, 22},     {190, 183, 197}, {170, 164, 177}, {151, 146, 159},
+    {130, 127, 139}, {112, 109, 119}, {93, 90, 99},    {72, 70, 78},
+    {49, 45, 53},    {14, 4, 15},     {192, 182, 179}, {171, 163, 161},
+    {153, 145, 144}, {132, 126, 125}, {113, 108, 107}, {93, 89, 88},
+    {73, 70, 69},    {49, 45, 45},    {13, 5, 6},      {195, 181, 159},
+    {172, 162, 142}, {152, 145, 127}, {132, 125, 111}, {113, 107, 94},
+    {93, 88, 77},    {73, 69, 59},    {48, 45, 37},    {11, 5, 0},
+    {195, 180, 139}, {173, 161, 124}, {153, 143, 110}, {133, 125, 96},
+    {113, 106, 81},  {94, 88, 66},    {73, 68, 49},    {49, 44, 28},
+    {9, 6, 0},       {196, 179, 118}, {174, 160, 106}, {154, 142, 94},
+    {133, 124, 81},  {113, 105, 68},  {94, 87, 54},    {73, 68, 39},
+    {48, 44, 18},    {5, 5, 0},       {197, 178, 96},  {176, 159, 86},
+    {155, 141, 75},  {134, 123, 64},  {114, 105, 53},  {94, 87, 40},
+    {73, 68, 26},    {48, 44, 5},     {2, 6, 0},       {199, 178, 70},
+    {176, 158, 62},  {156, 141, 54},  {134, 122, 44},  {114, 104, 35},
+    {94, 86, 23},    {73, 67, 8},     {47, 44, 0},     {2, 6, 0},
+    {199, 177, 45},  {178, 158, 40},  {156, 140, 32},  {135, 122, 26},
+    {114, 104, 16},  {94, 86, 4},     {73, 67, 0},     {47, 44, 0},
+    {0, 7, 0},       {188, 161, 204}, {168, 144, 183}, {149, 129, 164},
+    {130, 112, 144}, {112, 95, 123},  {93, 78, 103},   {74, 60, 81},
+    {50, 36, 56},    {16, 0, 16},     {190, 160, 185}, {170, 144, 165},
+    {151, 128, 148}, {132, 111, 130}, {112, 95, 110},  {93, 78, 92},
+    {74, 59, 72},    {50, 36, 48},    {16, 0, 8},      {192, 160, 167},
+    {171, 143, 150}, {153, 128, 134}, {132, 111, 117}, {112, 94, 100},
+    {94, 77, 82},    {74, 59, 63},    {50, 36, 40},    {14, 0, 0},
+    {193, 159, 149}, {172, 143, 134}, {153, 127, 119}, {133, 110, 103},
+    {113, 94, 87},   {93, 77, 72},    {73, 59, 54},    {50, 36, 32},
+    {12, 0, 0},      {195, 159, 131}, {173, 142, 117}, {153, 127, 104},
+    {132, 110, 90},  {113, 93, 76},   {93, 76, 61},    {74, 59, 45},
+    {49, 36, 23},    {9, 0, 0},       {196, 158, 113}, {174, 141, 101},
+    {155, 126, 89},  {133, 109, 76},  {113, 93, 64},   {94, 76, 51},
+    {74, 58, 35},    {49, 36, 14},    {6, 0, 0},       {197, 157, 92},
+    {174, 141, 80},  {154, 125, 71},  {134, 108, 60},  {114, 92, 50},
+    {94, 75, 37},    {73, 58, 22},    {48, 36, 1},     {5, 0, 0},
+    {197, 157, 68},  {175, 140, 59},  {155, 124, 51},  {134, 108, 41},
+    {113, 91, 32},   {94, 75, 21},    {73, 57, 5},     {48, 35, 0},
+    {5, 0, 0},       {198, 156, 46},  {176, 140, 40},  {155, 124, 32},
+    {134, 107, 24},  {114, 91, 14},   {94, 75, 2},     {73, 57, 0},
+    {48, 36, 0},     {3, 0, 0},       {189, 140, 191}, {168, 126, 172},
+    {150, 112, 154}, {131, 97, 134},  {112, 82, 115},  {94, 66, 96},
+    {74, 49, 75},    {51, 25, 50},    {12, 0, 10},     {191, 139, 173},
+    {170, 125, 154}, {152, 111, 138}, {132, 96, 121},  {113, 81, 103},
+    {94, 66, 85},    {74, 48, 66},    {50, 26, 42},    {12, 0, 1},
+    {192, 139, 157}, {171, 125, 140}, {152, 111, 125}, {132, 96, 109},
+    {113, 81, 92},   {94, 65, 76},    {74, 48, 58},    {50, 26, 35},
+    {9, 0, 0},       {193, 139, 140}, {172, 124, 125}, {153, 110, 112},
+    {133, 95, 96},   {113, 80, 82},   {94, 65, 66},    {74, 49, 50},
+    {50, 26, 28},    {7, 0, 0},       {194, 138, 123}, {172, 123, 109},
+    {153, 110, 97},  {133, 95, 84},   {113, 80, 70},   {94, 65, 56},
+    {74, 48, 40},    {50, 26, 20},    {6, 0, 0},       {194, 138, 105},
+    {173, 123, 94},  {153, 109, 83},  {133, 94, 70},   {112, 79, 59},
+    {94, 64, 46},    {74, 48, 31},    {50, 26, 9},     {4, 0, 0},
+    {196, 138, 87},  {174, 122, 77},  {153, 109, 67},  {133, 93, 56},
+    {113, 79, 46},   {94, 64, 34},    {73, 48, 18},    {49, 27, 0},
+    {4, 0, 0},       {196, 137, 65},  {174, 122, 57},  {154, 108, 49},
+    {133, 93, 39},   {113, 79, 29},   {94, 64, 18},    {74, 48, 3},
+    {49, 27, 0},     {2, 0, 0},       {197, 137, 47},  {175, 122, 40},
+    {155, 108, 32},  {133, 93, 23},   {114, 79, 14},   {94, 64, 1},
+    {73, 48, 0},     {48, 27, 0},     {2, 0, 0},       {189, 119, 177},
+    {168, 106, 159}, {150, 94, 142},  {131, 81, 124},  {113, 67, 107},
+    {94, 53, 89},    {74, 37, 69},    {51, 11, 45},    {6, 0, 3},
+    {191, 119, 161}, {170, 106, 144}, {152, 94, 129},  {132, 81, 112},
+    {113, 67, 96},   {94, 53, 79},    {74, 37, 61},    {51, 13, 38},
+    {6, 0, 0},       {192, 119, 146}, {170, 106, 131}, {152, 94, 117},
+    {132, 80, 101},  {112, 67, 85},   {94, 53, 70},    {74, 37, 53},
+    {50, 14, 31},    {4, 0, 0},       {192, 119, 131}, {171, 106, 117},
+    {153, 94, 105},  {132, 80, 89},   {113, 67, 75},   {94, 54, 61},
+    {74, 38, 45},    {51, 14, 23},    {3, 0, 0},       {193, 118, 114},
+    {171, 106, 102}, {153, 93, 90},   {132, 80, 78},   {113, 67, 65},
+    {94, 53, 52},    {74, 37, 36},    {50, 15, 16},    {1, 0, 0},
+    {194, 118, 99},  {172, 105, 89},  {153, 93, 78},   {132, 80, 66},
+    {113, 67, 54},   {94, 53, 42},    {74, 38, 27},    {50, 16, 5},
+    {1, 0, 0},       {194, 118, 82},  {173, 105, 72},  {153, 93, 63},
+    {132, 79, 53},   {113, 67, 42},   {94, 53, 30},    {74, 38, 15},
+    {49, 16, 0},     {0, 0, 0},       {195, 117, 63},  {173, 105, 55},
+    {154, 93, 47},   {133, 79, 37},   {113, 66, 27},   {94, 53, 15},
+    {73, 38, 0},     {48, 16, 0},     {0, 0, 0},       {195, 117, 46},
+    {173, 104, 39},  {154, 92, 32},   {133, 79, 22},   {113, 66, 13},
+    {94, 53, 0},     {73, 38, 0},     {48, 17, 0},     {0, 0, 0},
+    {189, 96, 166},  {168, 85, 147},  {150, 74, 132},  {131, 62, 115},
+    {113, 51, 99},   {94, 38, 82},    {74, 21, 63},    {51, 0, 40},
+    {1, 0, 0},       {190, 96, 150},  {170, 85, 133},  {152, 75, 119},
+    {132, 63, 104},  {113, 51, 88},   {94, 38, 72},    {75, 22, 55},
+    {51, 0, 33},     {1, 0, 0},       {192, 96, 137},  {170, 85, 121},
+    {152, 74, 108},  {132, 64, 94},   {113, 52, 79},   {94, 39, 64},
+    {74, 23, 48},    {50, 0, 26},     {0, 0, 0},       {192, 96, 122},
+    {171, 86, 109},  {152, 75, 96},   {132, 63, 83},   {113, 52, 69},
+    {94, 39, 56},    {74, 24, 41},    {50, 0, 19},     {0, 0, 0},
+    {193, 96, 107},  {171, 85, 96},   {152, 75, 84},   {132, 64, 72},
+    {113, 52, 60},   {94, 39, 47},    {74, 24, 32},    {50, 1, 10},
+    {0, 0, 0},       {193, 96, 93},   {172, 85, 82},   {152, 75, 72},
+    {133, 63, 61},   {113, 51, 49},   {94, 39, 37},    {73, 25, 23},
+    {49, 2, 2},      {0, 0, 0},       {194, 96, 78},   {172, 85, 68},
+    {152, 75, 59},   {132, 63, 49},   {113, 52, 39},   {94, 40, 26},
+    {73, 25, 11},    {48, 3, 0},      {0, 0, 0},       {194, 96, 60},
+    {173, 85, 52},   {153, 75, 44},   {132, 64, 35},   {112, 52, 25},
+    {94, 40, 12},    {73, 26, 0},     {48, 4, 0},      {0, 0, 0},
+    {195, 96, 46},   {173, 85, 38},   {154, 74, 31},   {133, 63, 22},
+    {113, 52, 11},   {93, 40, 0},     {73, 26, 0},     {47, 5, 0},
+    {0, 0, 0},       {188, 67, 153},  {168, 58, 137},  {151, 49, 122},
+    {131, 39, 106},  {113, 28, 90},   {94, 13, 75},    {75, 0, 57},
+    {51, 0, 35},     {0, 0, 0},       {190, 68, 138},  {170, 59, 123},
+    {152, 50, 110},  {132, 41, 96},   {113, 29, 80},   {94, 16, 66},
+    {75, 0, 49},     {50, 0, 27},     {0, 0, 0},       {191, 69, 126},
+    {170, 59, 112},  {151, 52, 100},  {132, 42, 86},   {113, 30, 73},
+    {95, 17, 58},    {75, 0, 42},     {50, 0, 21},     {0, 0, 0},
+    {192, 70, 113},  {170, 61, 100},  {151, 52, 89},   {132, 42, 77},
+    {113, 31, 64},   {94, 19, 50},    {74, 1, 35},     {50, 0, 14},
+    {0, 0, 0},       {192, 70, 100},  {170, 62, 89},   {151, 53, 77},
+    {131, 43, 66},   {112, 32, 54},   {94, 20, 42},    {74, 2, 27},
+    {49, 0, 5},      {0, 0, 0},       {192, 71, 87},   {171, 61, 77},
+    {152, 53, 67},   {131, 44, 57},   {112, 33, 45},   {94, 21, 33},
+    {74, 4, 19},     {49, 0, 1},      {0, 0, 0},       {193, 71, 74},
+    {171, 62, 64},   {152, 53, 55},   {132, 44, 45},   {113, 34, 34},
+    {94, 22, 23},    {73, 5, 7},      {48, 0, 0},      {0, 0, 0},
+    {193, 70, 58},   {172, 62, 50},   {152, 54, 42},   {132, 44, 32},
+    {112, 35, 22},   {93, 23, 10},    {73, 6, 0},      {47, 0, 0},
+    {0, 0, 0},       {193, 70, 45},   {172, 62, 38},   {153, 54, 31},
+    {132, 44, 21},   {112, 35, 9},    {94, 23, 0},     {73, 7, 0},
+    {47, 0, 0},      {0, 0, 0},       {189, 26, 141},  {169, 15, 126},
+    {150, 2, 112},   {131, 0, 97},    {113, 0, 82},    {94, 0, 67},
+    {75, 0, 51},     {50, 0, 29},     {0, 0, 0},       {190, 28, 128},
+    {170, 18, 114},  {151, 8, 101},   {132, 0, 88},    {113, 0, 74},
+    {94, 0, 60},     {75, 0, 44},     {50, 0, 23},     {0, 0, 0},
+    {191, 30, 117},  {170, 23, 104},  {152, 11, 92},   {132, 1, 79},
+    {113, 0, 67},    {95, 0, 53},     {75, 0, 37},     {50, 0, 17},
+    {0, 0, 0},       {191, 33, 105},  {170, 26, 93},   {151, 18, 83},
+    {132, 6, 70},    {112, 0, 58},    {94, 0, 45},     {75, 0, 30},
+    {49, 0, 8},      {0, 0, 0},       {191, 34, 93},   {170, 27, 82},
+    {151, 20, 72},   {131, 8, 61},    {112, 0, 49},    {94, 0, 38},
+    {74, 0, 23},     {49, 0, 4},      {0, 0, 0},       {191, 36, 82},
+    {170, 29, 71},   {151, 22, 63},   {131, 11, 52},   {112, 0, 41},
+    {93, 0, 29},     {74, 0, 14},     {48, 0, 1},      {0, 0, 0},
+    {191, 38, 69},   {170, 31, 60},   {151, 24, 51},   {131, 14, 41},
+    {112, 1, 31},    {93, 0, 19},     {73, 0, 3},      {48, 0, 0},
+    {0, 0, 0},       {192, 37, 56},   {171, 31, 47},   {152, 25, 40},
+    {131, 17, 30},   {112, 4, 19},    {93, 0, 7},      {73, 0, 0},
+    {47, 0, 0},      {0, 0, 0},       {192, 38, 45},   {171, 33, 36},
+    {152, 26, 30},   {131, 18, 21},   {111, 7, 9},     {93, 0, 0},
+    {73, 0, 0},      {47, 0, 0},      {0, 0, 0},       {149, 218, 248},
+    {133, 194, 222}, {119, 173, 198}, {102, 151, 173}, {86, 130, 148},
+    {70, 108, 125},  {53, 85, 100},   {32, 59, 71},    {0, 22, 33},
+    {154, 216, 223}, {137, 193, 200}, {122, 172, 178}, {106, 150, 156},
+    {89, 128, 133},  {73, 107, 112},  {54, 85, 89},    {31, 59, 63},
+    {0, 22, 26},     {159, 215, 202}, {141, 192, 181}, {126, 171, 161},
+    {108, 149, 141}, {90, 128, 121},  {74, 107, 100},  {55, 85, 80},
+    {32, 58, 55},    {0, 22, 19},     {161, 213, 179}, {144, 190, 160},
+    {126, 170, 143}, {109, 148, 125}, {92, 127, 107},  {74, 106, 89},
+    {56, 84, 69},    {32, 58, 46},    {0, 21, 11},     {163, 211, 156},
+    {144, 189, 139}, {129, 168, 125}, {110, 147, 108}, {93, 126, 92},
+    {75, 105, 76},   {57, 83, 58},    {33, 58, 37},    {0, 21, 1},
+    {167, 211, 133}, {147, 188, 120}, {130, 167, 105}, {110, 145, 92},
+    {93, 125, 78},   {76, 104, 64},   {58, 83, 48},    {33, 57, 27},
+    {0, 21, 0},      {169, 210, 108}, {149, 187, 96},  {131, 166, 86},
+    {112, 144, 74},  {94, 124, 62},   {77, 103, 49},   {58, 82, 35},
+    {33, 57, 15},    {0, 21, 0},      {170, 209, 80},  {151, 186, 71},
+    {133, 165, 62},  {114, 143, 52},  {95, 123, 42},   {77, 103, 32},
+    {58, 81, 18},    {33, 56, 0},     {0, 21, 0},      {173, 208, 55},
+    {152, 186, 49},  {134, 165, 41},  {114, 143, 34},  {95, 122, 25},
+    {77, 102, 14},   {58, 81, 0},     {33, 56, 0},     {0, 21, 0},
+    {154, 195, 232}, {137, 174, 207}, {122, 156, 185}, {105, 136, 163},
+    {89, 116, 140},  {73, 97, 117},   {56, 76, 94},    {35, 51, 66},
+    {0, 13, 28},     {158, 194, 209}, {141, 174, 187}, {125, 155, 167},
+    {109, 135, 146}, {91, 116, 125},  {75, 96, 105},   {57, 75, 83},
+    {35, 50, 57},    {0, 12, 21},     {161, 193, 189}, {144, 173, 169},
+    {128, 154, 151}, {110, 134, 132}, {93, 115, 113},  {77, 95, 94},
+    {58, 75, 74},    {35, 50, 50},    {0, 12, 13},     {164, 192, 168},
+    {145, 171, 151}, {129, 153, 134}, {111, 133, 117}, {94, 114, 100},
+    {76, 95, 82},    {58, 75, 64},    {36, 50, 42},    {0, 12, 5},
+    {165, 191, 147}, {147, 170, 131}, {130, 152, 117}, {113, 132, 102},
+    {95, 113, 86},   {77, 94, 71},    {58, 74, 54},    {35, 50, 33},
+    {0, 13, 0},      {167, 189, 126}, {148, 169, 113}, {132, 151, 100},
+    {113, 131, 86},  {96, 112, 73},   {77, 93, 59},    {59, 73, 43},
+    {35, 49, 23},    {0, 12, 0},      {170, 189, 104}, {150, 168, 91},
+    {133, 150, 81},  {114, 130, 69},  {96, 111, 57},   {78, 92, 46},
+    {59, 73, 31},    {35, 49, 11},    {0, 13, 0},      {171, 188, 78},
+    {152, 168, 68},  {134, 149, 60},  {115, 130, 50},  {96, 111, 41},
+    {78, 92, 29},    {60, 73, 15},    {35, 49, 0},     {0, 12, 0},
+    {173, 187, 55},  {153, 167, 47},  {134, 149, 39},  {115, 129, 33},
+    {97, 110, 24},   {79, 92, 13},    {60, 72, 0},     {35, 48, 0},
+    {0, 12, 0},      {157, 175, 217}, {139, 157, 196}, {125, 141, 175},
+    {109, 122, 153}, {92, 104, 132},  {76, 86, 110},   {59, 67, 88},
+    {37, 43, 61},    {1, 1, 23},      {161, 174, 196}, {144, 156, 176},
+    {127, 140, 158}, {110, 121, 137}, {94, 104, 118},  {77, 85, 98},
+    {59, 67, 78},    {37, 43, 53},    {0, 2, 16},      {163, 174, 178},
+    {146, 156, 160}, {130, 139, 143}, {112, 121, 124}, {95, 103, 106},
+    {78, 85, 88},    {60, 66, 69},    {37, 42, 46},    {0, 2, 7},
+    {166, 173, 159}, {147, 154, 142}, {130, 138, 127}, {113, 120, 111},
+    {96, 103, 95},   {78, 84, 77},    {60, 66, 59},    {37, 43, 37},
+    {0, 2, 0},       {166, 172, 139}, {148, 154, 125}, {131, 137, 112},
+    {113, 120, 96},  {96, 102, 81},   {78, 84, 66},    {60, 65, 50},
+    {37, 42, 29},    {0, 3, 0},       {167, 171, 120}, {149, 153, 107},
+    {133, 137, 95},  {114, 118, 81},  {97, 101, 69},   {79, 84, 56},
+    {60, 65, 40},    {37, 42, 19},    {0, 3, 0},       {170, 170, 99},
+    {151, 152, 87},  {134, 136, 77},  {115, 118, 66},  {97, 101, 55},
+    {79, 83, 42},    {61, 65, 28},    {37, 42, 7},     {0, 3, 0},
+    {172, 170, 75},  {152, 151, 65},  {134, 135, 57},  {115, 117, 48},
+    {97, 100, 38},   {79, 83, 27},    {61, 64, 12},    {36, 42, 0},
+    {0, 3, 0},       {172, 169, 55},  {154, 151, 46},  {135, 134, 40},
+    {116, 116, 32},  {97, 99, 21},    {80, 82, 10},    {61, 64, 0},
+    {36, 41, 0},     {0, 3, 0},       {160, 154, 203}, {143, 139, 182},
+    {127, 124, 164}, {111, 107, 143}, {95, 91, 122},   {78, 75, 103},
+    {60, 57, 81},    {39, 33, 56},    {1, 0, 18},      {163, 154, 184},
+    {146, 138, 165}, {130, 123, 148}, {113, 107, 129}, {96, 90, 110},
+    {79, 74, 92},    {61, 56, 72},    {39, 34, 48},    {2, 0, 9},
+    {165, 154, 167}, {147, 137, 149}, {131, 122, 134}, {114, 106, 117},
+    {96, 90, 100},   {79, 74, 82},    {61, 56, 64},    {39, 33, 40},
+    {2, 0, 1},       {166, 153, 150}, {149, 137, 133}, {132, 122, 119},
+    {114, 106, 104}, {97, 90, 88},    {79, 74, 72},    {61, 56, 55},
+    {39, 34, 33},    {0, 0, 0},       {168, 152, 132}, {149, 136, 117},
+    {132, 121, 104}, {114, 105, 90},  {97, 89, 76},    {79, 73, 62},
+    {61, 56, 46},    {38, 34, 25},    {0, 0, 0},       {169, 151, 114},
+    {150, 135, 101}, {133, 121, 90},  {114, 104, 77},  {97, 89, 65},
+    {80, 73, 51},    {61, 56, 36},    {38, 34, 16},    {0, 0, 0},
+    {170, 150, 94},  {151, 135, 83},  {134, 120, 73},  {115, 104, 62},
+    {98, 88, 51},    {80, 72, 39},    {61, 56, 24},    {38, 34, 3},
+    {0, 0, 0},       {172, 150, 72},  {153, 134, 63},  {135, 119, 55},
+    {115, 103, 45},  {98, 88, 36},    {80, 72, 24},    {61, 55, 9},
+    {38, 34, 0},     {0, 0, 0},       {172, 150, 54},  {153, 134, 47},
+    {135, 119, 38},  {116, 103, 30},  {98, 87, 21},    {80, 72, 8},
+    {62, 55, 0},     {37, 34, 0},     {0, 0, 0},       {162, 134, 190},
+    {145, 120, 171}, {129, 108, 153}, {113, 93, 134},  {97, 78, 115},
+    {80, 63, 96},    {62, 46, 75},    {41, 23, 51},    {0, 0, 11},
+    {165, 134, 173}, {147, 120, 154}, {131, 107, 138}, {114, 92, 120},
+    {97, 78, 103},   {80, 63, 85},    {62, 46, 66},    {40, 23, 43},
+    {0, 0, 2},       {166, 134, 157}, {148, 120, 140}, {132, 106, 125},
+    {114, 92, 109},  {97, 77, 93},    {81, 63, 77},    {62, 46, 58},
+    {40, 24, 36},    {0, 0, 0},       {168, 133, 140}, {149, 119, 125},
+    {132, 106, 112}, {115, 92, 97},   {98, 77, 82},    {81, 62, 67},
+    {62, 46, 50},    {40, 24, 29},    {0, 0, 0},       {168, 133, 123},
+    {150, 119, 110}, {133, 106, 97},  {115, 91, 84},   {98, 77, 70},
+    {81, 62, 57},    {62, 46, 41},    {40, 24, 20},    {0, 0, 0},
+    {169, 132, 107}, {150, 118, 94},  {133, 105, 84},  {115, 91, 72},
+    {98, 76, 60},    {80, 62, 47},    {62, 46, 32},    {39, 25, 11},
+    {0, 0, 0},       {171, 132, 89},  {152, 118, 79},  {135, 105, 69},
+    {115, 90, 58},   {98, 76, 47},    {80, 62, 36},    {62, 46, 21},
+    {39, 25, 0},     {0, 0, 0},       {171, 132, 69},  {153, 117, 60},
+    {135, 104, 52},  {116, 90, 42},   {98, 76, 33},    {81, 61, 21},
+    {62, 46, 6},     {38, 25, 0},     {0, 0, 0},       {172, 132, 54},
+    {153, 118, 45},  {135, 104, 38},  {116, 90, 28},   {98, 76, 18},
+    {81, 61, 6},     {62, 46, 0},     {38, 25, 0},     {0, 0, 0},
+    {164, 115, 177}, {146, 103, 159}, {130, 91, 143},  {114, 78, 125},
+    {97, 65, 107},   {81, 51, 89},    {63, 34, 69},    {41, 9, 46},
+    {0, 0, 4},       {166, 115, 161}, {148, 103, 144}, {132, 91, 129},
+    {115, 78, 112},  {98, 65, 96},    {81, 51, 79},    {63, 35, 61},
+    {41, 11, 38},    {0, 0, 0},       {167, 115, 146}, {150, 102, 131},
+    {132, 91, 117},  {115, 78, 101},  {98, 65, 86},    {81, 51, 71},
+    {63, 35, 54},    {41, 12, 32},    {0, 0, 0},       {168, 114, 132},
+    {150, 103, 118}, {133, 91, 105},  {116, 78, 91},   {98, 64, 76},
+    {82, 51, 61},    {63, 36, 46},    {41, 13, 24},    {0, 0, 0},
+    {169, 114, 116}, {150, 102, 103}, {134, 90, 91},   {116, 78, 79},
+    {98, 65, 66},    {81, 51, 53},    {63, 36, 37},    {40, 14, 17},
+    {0, 0, 0},       {169, 114, 101}, {151, 101, 89},  {134, 90, 79},
+    {116, 77, 67},   {98, 64, 56},    {81, 51, 44},    {63, 36, 29},
+    {40, 15, 7},     {0, 0, 0},       {170, 114, 85},  {152, 101, 75},
+    {135, 90, 65},   {116, 77, 54},   {98, 64, 44},    {81, 51, 32},
+    {63, 36, 17},    {39, 15, 0},     {0, 0, 0},       {172, 113, 66},
+    {152, 101, 58},  {135, 89, 49},   {116, 77, 40},   {99, 64, 30},
+    {81, 51, 18},    {62, 36, 3},     {38, 16, 0},     {0, 0, 0},
+    {171, 113, 51},  {153, 101, 44},  {136, 89, 36},   {116, 77, 28},
+    {99, 64, 18},    {81, 51, 5},     {62, 36, 0},     {38, 16, 0},
+    {0, 0, 0},       {165, 94, 166},  {147, 82, 147},  {132, 72, 132},
+    {115, 61, 115},  {98, 49, 99},    {82, 36, 82},    {64, 19, 64},
+    {42, 0, 41},     {0, 0, 0},       {167, 93, 150},  {150, 83, 134},
+    {133, 73, 120},  {116, 62, 104},  {99, 49, 88},    {82, 36, 72},
+    {64, 20, 55},    {41, 0, 33},     {0, 0, 0},       {169, 93, 137},
+    {150, 83, 122},  {134, 73, 109},  {116, 61, 94},   {99, 50, 80},
+    {82, 37, 65},    {64, 21, 49},    {41, 0, 27},     {0, 0, 0},
+    {169, 94, 123},  {150, 83, 110},  {133, 73, 97},   {116, 61, 83},
+    {99, 50, 70},    {82, 38, 57},    {63, 23, 42},    {41, 0, 20},
+    {0, 0, 0},       {169, 94, 109},  {150, 84, 97},   {134, 73, 85},
+    {116, 62, 73},   {99, 51, 61},    {81, 38, 48},    {63, 23, 33},
+    {41, 1, 11},     {0, 0, 0},       {170, 94, 96},   {150, 83, 84},
+    {134, 73, 74},   {116, 61, 62},   {99, 50, 51},    {82, 38, 39},
+    {64, 23, 24},    {40, 3, 4},      {0, 0, 0},       {171, 93, 79},
+    {152, 82, 70},   {135, 73, 61},   {116, 62, 51},   {98, 51, 40},
+    {81, 38, 28},    {63, 24, 14},    {39, 4, 0},      {0, 0, 0},
+    {171, 94, 64},   {152, 83, 55},   {135, 73, 47},   {116, 62, 37},
+    {98, 50, 27},    {81, 38, 15},    {63, 24, 1},     {39, 4, 0},
+    {0, 0, 0},       {172, 93, 51},   {153, 82, 42},   {135, 73, 35},
+    {117, 62, 26},   {99, 51, 16},    {81, 39, 3},     {63, 25, 0},
+    {38, 5, 0},      {0, 0, 0},       {166, 68, 153},  {148, 59, 137},
+    {133, 49, 121},  {115, 39, 106},  {99, 28, 91},    {82, 13, 75},
+    {65, 0, 58},     {42, 0, 36},     {0, 0, 0},       {168, 68, 139},
+    {150, 59, 124},  {134, 50, 110},  {116, 40, 96},   {99, 30, 81},
+    {82, 16, 66},    {64, 0, 50},     {41, 0, 29},     {0, 0, 0},
+    {169, 69, 126},  {150, 59, 113},  {134, 51, 101},  {117, 42, 87},
+    {100, 30, 73},   {82, 17, 59},    {65, 0, 43},     {41, 0, 23},
+    {0, 0, 0},       {169, 70, 115},  {150, 61, 102},  {134, 52, 89},
+    {116, 42, 77},   {99, 32, 65},    {82, 19, 52},    {64, 0, 36},
+    {41, 0, 15},     {0, 0, 0},       {169, 70, 101},  {150, 61, 90},
+    {134, 52, 79},   {116, 43, 68},   {99, 32, 55},    {82, 21, 43},
+    {64, 2, 28},     {41, 0, 6},      {0, 0, 0},       {170, 70, 89},
+    {151, 62, 79},   {134, 53, 69},   {116, 44, 58},   {99, 33, 46},
+    {81, 21, 34},    {64, 3, 20},     {41, 0, 2},      {0, 0, 0},
+    {170, 71, 76},   {152, 62, 66},   {134, 53, 57},   {116, 43, 46},
+    {99, 33, 36},    {82, 22, 24},    {64, 5, 10},     {40, 0, 0},
+    {0, 0, 0},       {171, 70, 61},   {152, 62, 52},   {135, 53, 44},
+    {116, 44, 35},   {99, 34, 24},    {82, 22, 12},    {63, 6, 0},
+    {39, 0, 0},      {0, 0, 0},       {171, 71, 49},   {153, 62, 41},
+    {135, 54, 33},   {117, 45, 25},   {98, 34, 13},    {81, 23, 0},
+    {63, 7, 0},      {39, 0, 0},      {0, 0, 0},       {167, 33, 142},
+    {149, 24, 127},  {134, 10, 113},  {116, 0, 97},    {100, 0, 83},
+    {83, 0, 68},     {65, 0, 52},     {40, 0, 30},     {0, 0, 0},
+    {169, 33, 129},  {150, 26, 115},  {134, 17, 102},  {116, 3, 89},
+    {100, 0, 75},    {83, 0, 60},     {65, 0, 45},     {40, 0, 24},
+    {0, 0, 0},       {169, 36, 118},  {151, 27, 104},  {134, 19, 93},
+    {116, 7, 80},    {100, 0, 67},    {83, 0, 54},     {65, 0, 38},
+    {41, 0, 17},     {0, 0, 0},       {169, 39, 107},  {150, 30, 94},
+    {134, 22, 84},   {116, 11, 71},   {99, 0, 59},     {83, 0, 46},
+    {64, 0, 31},     {40, 0, 9},      {0, 0, 0},       {169, 39, 95},
+    {151, 31, 83},   {134, 24, 73},   {116, 15, 62},   {100, 1, 51},
+    {83, 0, 38},     {64, 0, 24},     {40, 0, 5},      {0, 0, 0},
+    {169, 41, 83},   {151, 33, 73},   {134, 26, 64},   {117, 17, 54},
+    {99, 4, 42},     {82, 0, 30},     {64, 0, 16},     {40, 0, 1},
+    {0, 0, 0},       {170, 42, 71},   {152, 34, 62},   {134, 28, 53},
+    {117, 19, 44},   {99, 6, 33},     {82, 0, 21},     {63, 0, 4},
+    {39, 0, 0},      {0, 0, 0},       {171, 42, 59},   {152, 35, 50},
+    {134, 29, 42},   {117, 21, 32},   {99, 9, 22},     {82, 0, 9},
+    {63, 0, 0},      {38, 0, 0},      {0, 0, 0},       {172, 42, 48},
+    {152, 36, 40},   {135, 29, 32},   {117, 21, 23},   {99, 10, 12},
+    {82, 0, 0},      {63, 0, 0},      {38, 0, 0},      {0, 0, 0},
+    {107, 207, 246}, {96, 185, 220},  {86, 165, 196},  {73, 144, 171},
+    {60, 123, 147},  {46, 103, 125},  {32, 82, 100},   {9, 56, 71},
+    {0, 20, 33},     {115, 206, 221}, {104, 184, 198}, {92, 164, 178},
+    {78, 143, 154},  {64, 123, 133},  {51, 102, 111},  {34, 81, 89},
+    {10, 56, 63},    {0, 20, 27},     {122, 204, 200}, {108, 183, 180},
+    {95, 163, 161},  {82, 142, 140},  {68, 122, 120},  {54, 102, 101},
+    {36, 81, 79},    {11, 56, 55},    {0, 20, 20},     {125, 203, 179},
+    {111, 181, 160}, {97, 162, 143},  {85, 141, 124},  {70, 121, 107},
+    {55, 101, 89},   {38, 80, 69},    {14, 55, 46},    {0, 19, 10},
+    {128, 202, 156}, {113, 180, 140}, {102, 161, 125}, {87, 140, 108},
+    {71, 120, 92},   {56, 100, 76},   {39, 79, 59},    {14, 55, 38},
+    {0, 20, 3},      {132, 200, 135}, {117, 179, 121}, {103, 159, 106},
+    {88, 139, 93},   {73, 119, 79},   {57, 100, 65},   {41, 79, 49},
+    {15, 54, 28},    {0, 19, 0},      {134, 200, 111}, {119, 178, 98},
+    {105, 158, 87},  {89, 138, 76},   {74, 118, 64},   {58, 99, 51},
+    {41, 78, 37},    {16, 54, 17},    {0, 19, 0},      {137, 199, 85},
+    {122, 177, 75},  {108, 158, 66},  {91, 137, 56},   {75, 118, 46},
+    {59, 98, 35},    {42, 78, 22},    {16, 54, 3},     {0, 19, 0},
+    {140, 198, 62},  {125, 177, 55},  {109, 158, 47},  {92, 137, 40},
+    {76, 117, 32},   {59, 98, 21},    {42, 78, 6},     {16, 54, 0},
+    {0, 18, 0},      {118, 186, 231}, {106, 167, 206}, {93, 149, 184},
+    {81, 130, 161},  {67, 111, 139},  {54, 92, 117},   {39, 72, 93},
+    {17, 48, 66},    {0, 10, 29},     {123, 185, 207}, {110, 166, 186},
+    {98, 148, 167},  {85, 129, 145},  {71, 111, 125},  {56, 92, 104},
+    {40, 72, 83},    {18, 48, 57},    {0, 10, 22},     {128, 184, 188},
+    {113, 165, 168}, {102, 147, 151}, {88, 128, 131},  {73, 110, 113},
+    {58, 91, 94},    {42, 71, 74},    {19, 48, 50},    {0, 9, 15},
+    {131, 183, 168}, {116, 164, 151}, {104, 146, 134}, {89, 127, 117},
+    {73, 109, 100},  {58, 90, 83},    {42, 71, 65},    {20, 48, 42},
+    {0, 9, 5},       {134, 182, 148}, {120, 163, 131}, {105, 145, 118},
+    {90, 126, 102},  {75, 108, 86},   {59, 90, 72},    {43, 71, 55},
+    {19, 47, 34},    {0, 9, 0},       {136, 181, 128}, {122, 162, 115},
+    {107, 144, 102}, {92, 125, 87},   {76, 107, 74},   {61, 89, 60},
+    {44, 70, 45},    {20, 47, 24},    {0, 8, 0},       {139, 180, 106},
+    {124, 161, 95},  {109, 144, 83},  {93, 124, 71},   {77, 107, 60},
+    {61, 89, 47},    {44, 70, 33},    {20, 47, 13},    {0, 8, 0},
+    {142, 179, 82},  {125, 160, 72},  {111, 143, 63},  {94, 124, 54},
+    {77, 106, 44},   {61, 88, 32},    {44, 69, 18},    {20, 46, 0},
+    {0, 8, 0},       {143, 179, 62},  {127, 160, 54},  {111, 142, 47},
+    {94, 124, 39},   {78, 106, 29},   {62, 88, 18},    {45, 69, 3},
+    {20, 46, 0},     {0, 8, 0},       {124, 167, 216}, {112, 150, 194},
+    {99, 134, 174},  {87, 117, 153},  {73, 100, 131},  {58, 82, 110},
+    {43, 64, 88},    {23, 40, 61},    {0, 0, 24},      {129, 166, 195},
+    {116, 150, 175}, {103, 134, 158}, {89, 116, 137},  {75, 99, 118},
+    {60, 82, 98},    {44, 63, 78},    {23, 40, 53},    {0, 0, 17},
+    {132, 166, 177}, {119, 149, 160}, {106, 133, 143}, {90, 115, 124},
+    {76, 99, 107},   {61, 81, 88},    {45, 63, 69},    {24, 40, 46},
+    {0, 0, 9},       {136, 166, 159}, {121, 148, 143}, {107, 132, 126},
+    {92, 115, 111},  {77, 98, 94},    {62, 81, 78},    {46, 63, 60},
+    {23, 40, 38},    {0, 0, 0},       {138, 164, 140}, {122, 147, 125},
+    {108, 131, 111}, {93, 114, 97},   {79, 98, 82},    {63, 80, 67},
+    {46, 62, 50},    {24, 40, 29},    {0, 0, 0},       {139, 163, 122},
+    {124, 146, 109}, {110, 131, 96},  {94, 114, 83},   {79, 97, 70},
+    {63, 81, 57},    {46, 62, 41},    {24, 40, 21},    {0, 0, 0},
+    {141, 163, 101}, {126, 145, 90},  {111, 130, 79},  {95, 113, 68},
+    {79, 96, 56},    {63, 80, 44},    {47, 62, 30},    {23, 40, 10},
+    {0, 0, 0},       {144, 162, 79},  {127, 145, 70},  {112, 129, 60},
+    {95, 112, 51},   {79, 96, 41},    {64, 79, 30},    {47, 61, 15},
+    {23, 40, 0},     {0, 0, 0},       {145, 162, 60},  {129, 145, 52},
+    {113, 129, 46},  {96, 112, 37},   {79, 95, 27},    {64, 79, 16},
+    {47, 61, 1},     {23, 39, 0},     {0, 0, 0},       {131, 147, 202},
+    {117, 133, 181}, {105, 119, 162}, {91, 103, 142},  {77, 87, 122},
+    {62, 71, 102},   {47, 54, 81},    {26, 31, 56},    {0, 0, 18},
+    {135, 147, 183}, {120, 132, 164}, {107, 118, 147}, {93, 102, 128},
+    {78, 87, 110},   {63, 71, 92},    {47, 54, 72},    {26, 31, 48},
+    {0, 0, 10},      {138, 147, 166}, {123, 131, 149}, {108, 118, 133},
+    {94, 102, 116},  {79, 86, 100},   {64, 71, 82},    {48, 54, 64},
+    {27, 31, 41},    {0, 0, 2},       {139, 146, 149}, {124, 131, 134},
+    {111, 117, 119}, {94, 101, 103},  {79, 86, 88},    {64, 70, 72},
+    {48, 53, 55},    {27, 31, 33},    {0, 0, 0},       {141, 146, 132},
+    {125, 131, 117}, {111, 117, 104}, {95, 101, 91},   {80, 86, 77},
+    {65, 70, 62},    {48, 53, 46},    {26, 31, 25},    {0, 0, 0},
+    {143, 145, 115}, {126, 130, 101}, {112, 116, 90},  {96, 100, 78},
+    {80, 85, 65},    {65, 70, 52},    {49, 53, 37},    {27, 32, 17},
+    {0, 0, 0},       {144, 144, 96},  {128, 129, 85},  {112, 115, 75},
+    {97, 100, 64},   {81, 85, 52},    {65, 69, 40},    {49, 53, 26},
+    {26, 31, 5},     {0, 0, 0},       {146, 144, 76},  {129, 129, 67},
+    {114, 115, 58},  {97, 99, 48},    {82, 84, 38},    {66, 69, 27},
+    {49, 53, 12},    {26, 32, 0},     {0, 0, 0},       {146, 144, 59},
+    {130, 128, 51},  {114, 114, 43},  {98, 99, 35},    {82, 84, 25},
+    {66, 69, 13},    {49, 53, 0},     {26, 32, 0},     {0, 0, 0},
+    {135, 129, 189}, {122, 115, 170}, {107, 103, 152}, {94, 89, 133},
+    {79, 74, 114},   {64, 60, 95},    {49, 43, 75},    {29, 20, 51},
+    {0, 0, 12},      {138, 129, 171}, {124, 115, 153}, {110, 103, 138},
+    {95, 89, 120},   {81, 74, 103},   {66, 60, 86},    {50, 44, 67},
+    {28, 21, 43},    {0, 0, 3},       {140, 129, 156}, {125, 115, 140},
+    {111, 103, 125}, {96, 89, 109},   {81, 74, 93},    {67, 60, 76},
+    {50, 44, 59},    {29, 22, 36},    {0, 0, 0},       {142, 128, 140},
+    {127, 115, 125}, {112, 102, 112}, {97, 88, 97},    {82, 74, 83},
+    {67, 60, 67},    {50, 44, 51},    {29, 22, 29},    {0, 0, 0},
+    {142, 128, 124}, {127, 114, 111}, {113, 102, 98},  {98, 88, 85},
+    {82, 74, 71},    {66, 60, 58},    {50, 44, 42},    {29, 22, 21},
+    {0, 0, 0},       {144, 127, 108}, {128, 114, 96},  {113, 101, 85},
+    {98, 87, 73},    {82, 74, 61},    {67, 60, 48},    {50, 44, 33},
+    {28, 23, 12},    {0, 0, 0},       {145, 127, 91},  {129, 114, 81},
+    {115, 101, 71},  {98, 87, 60},    {82, 73, 48},    {67, 59, 37},
+    {50, 44, 22},    {29, 23, 1},     {0, 0, 0},       {147, 127, 73},
+    {130, 113, 63},  {115, 101, 55},  {98, 87, 45},    {83, 73, 35},
+    {67, 59, 24},    {50, 44, 10},    {28, 24, 0},     {0, 0, 0},
+    {147, 127, 58},  {131, 113, 49},  {115, 100, 42},  {99, 86, 33},
+    {83, 73, 23},    {67, 59, 10},    {50, 44, 0},     {27, 24, 0},
+    {0, 0, 0},       {138, 110, 177}, {124, 99, 159},  {110, 88, 142},
+    {96, 75, 125},   {82, 62, 107},   {66, 48, 89},    {51, 33, 70},
+    {30, 8, 46},     {0, 0, 5},       {142, 111, 160}, {127, 99, 144},
+    {113, 88, 130},  {98, 75, 112},   {82, 62, 96},    {68, 49, 80},
+    {51, 33, 61},    {30, 10, 39},    {0, 0, 0},       {143, 111, 146},
+    {128, 99, 131},  {114, 88, 118},  {98, 75, 101},   {83, 62, 86},
+    {68, 49, 71},    {52, 33, 54},    {30, 11, 32},    {0, 0, 0},
+    {144, 111, 132}, {128, 99, 118},  {113, 88, 106},  {99, 75, 91},
+    {83, 62, 77},    {68, 49, 62},    {52, 34, 46},    {30, 12, 25},
+    {0, 0, 0},       {144, 111, 117}, {129, 98, 104},  {114, 87, 92},
+    {99, 75, 80},    {83, 62, 67},    {68, 49, 53},    {51, 34, 38},
+    {30, 13, 18},    {0, 0, 0},       {145, 111, 103}, {130, 98, 91},
+    {114, 87, 80},   {99, 75, 68},    {83, 63, 57},    {68, 50, 45},
+    {51, 34, 30},    {30, 14, 8},     {0, 0, 0},       {146, 110, 87},
+    {131, 98, 76},   {115, 87, 67},   {99, 75, 56},    {83, 62, 45},
+    {68, 49, 33},    {52, 35, 19},    {30, 15, 2},     {0, 0, 0},
+    {148, 110, 70},  {131, 98, 60},   {116, 86, 52},   {99, 74, 43},
+    {84, 62, 33},    {69, 49, 21},    {52, 35, 6},     {29, 15, 0},
+    {0, 0, 0},       {148, 110, 56},  {132, 97, 48},   {117, 87, 40},
+    {100, 75, 31},   {84, 62, 22},    {68, 49, 9},     {51, 35, 0},
+    {28, 15, 0},     {0, 0, 0},       {142, 91, 166},  {126, 80, 148},
+    {113, 71, 132},  {98, 59, 115},   {83, 47, 99},    {69, 34, 82},
+    {53, 17, 64},    {32, 0, 41},     {0, 0, 0},       {143, 91, 150},
+    {128, 81, 135},  {114, 71, 120},  {99, 60, 104},   {85, 48, 89},
+    {69, 35, 73},    {53, 19, 56},    {32, 0, 34},     {0, 0, 0},
+    {145, 91, 137},  {129, 81, 122},  {115, 71, 109},  {100, 60, 94},
+    {85, 48, 81},    {69, 35, 65},    {53, 19, 49},    {32, 0, 28},
+    {0, 0, 0},       {146, 92, 124},  {130, 81, 110},  {115, 71, 98},
+    {100, 60, 84},   {85, 49, 71},    {69, 36, 57},    {53, 21, 42},
+    {32, 0, 21},     {0, 0, 0},       {147, 91, 110},  {130, 81, 97},
+    {115, 71, 86},   {100, 60, 74},   {84, 49, 62},    {69, 36, 48},
+    {53, 22, 34},    {32, 0, 13},     {0, 0, 0},       {147, 92, 97},
+    {130, 81, 85},   {116, 72, 76},   {100, 60, 63},   {85, 49, 52},
+    {69, 37, 40},    {53, 22, 26},    {31, 1, 5},      {0, 0, 0},
+    {148, 92, 82},   {131, 81, 71},   {116, 71, 62},   {100, 60, 53},
+    {84, 49, 42},    {69, 37, 30},    {52, 23, 16},    {31, 2, 0},
+    {0, 0, 0},       {148, 91, 67},   {132, 81, 57},   {117, 71, 49},
+    {100, 60, 39},   {84, 49, 30},    {69, 37, 18},    {52, 23, 2},
+    {30, 2, 0},      {0, 0, 0},       {149, 91, 54},   {132, 81, 46},
+    {118, 71, 39},   {101, 60, 29},   {85, 49, 19},    {69, 37, 6},
+    {52, 23, 0},     {29, 3, 0},      {0, 0, 0},       {143, 68, 153},
+    {128, 59, 137},  {115, 49, 122},  {99, 39, 107},   {85, 28, 91},
+    {70, 13, 75},    {54, 0, 58},     {32, 0, 36},     {0, 0, 0},
+    {146, 68, 140},  {131, 59, 125},  {116, 51, 111},  {100, 40, 97},
+    {85, 29, 82},    {70, 15, 67},    {54, 0, 50},     {32, 0, 29},
+    {0, 0, 0},       {147, 68, 127},  {131, 59, 114},  {117, 51, 102},
+    {101, 41, 88},   {86, 30, 74},    {70, 17, 60},    {54, 0, 44},
+    {32, 0, 23},     {0, 0, 0},       {147, 70, 115},  {131, 60, 103},
+    {116, 52, 91},   {100, 42, 78},   {85, 32, 65},    {70, 19, 53},
+    {54, 1, 38},     {32, 0, 17},     {0, 0, 0},       {147, 70, 103},
+    {131, 61, 91},   {117, 53, 81},   {101, 43, 69},   {86, 32, 57},
+    {70, 20, 44},    {54, 2, 30},     {32, 0, 7},      {0, 0, 0},
+    {148, 70, 91},   {132, 61, 80},   {117, 52, 70},   {101, 43, 59},
+    {85, 33, 48},    {70, 21, 36},    {53, 4, 22},     {32, 0, 3},
+    {0, 0, 0},       {148, 70, 78},   {132, 62, 68},   {117, 53, 58},
+    {101, 43, 48},   {85, 34, 38},    {70, 22, 26},    {53, 6, 12},
+    {31, 0, 0},      {0, 0, 0},       {149, 71, 64},   {132, 62, 54},
+    {118, 54, 46},   {101, 44, 37},   {85, 34, 27},    {69, 23, 15},
+    {53, 7, 1},      {30, 0, 0},      {0, 0, 0},       {150, 70, 53},
+    {134, 61, 44},   {118, 54, 36},   {101, 44, 28},   {85, 35, 17},
+    {69, 23, 4},     {52, 8, 0},      {30, 0, 0},      {0, 0, 0},
+    {145, 38, 143},  {130, 29, 128},  {117, 18, 114},  {101, 3, 98},
+    {87, 0, 84},     {72, 0, 69},     {54, 0, 53},     {30, 0, 31},
+    {0, 0, 0},       {147, 38, 130},  {132, 30, 116},  {117, 22, 103},
+    {101, 8, 89},    {87, 0, 76},     {72, 0, 62},     {54, 0, 46},
+    {30, 0, 24},     {0, 0, 0},       {148, 40, 119},  {132, 31, 105},
+    {117, 23, 94},   {101, 13, 81},   {87, 0, 68},     {71, 0, 55},
+    {54, 0, 39},     {30, 0, 18},     {0, 0, 0},       {148, 42, 108},
+    {132, 34, 96},   {117, 25, 85},   {102, 15, 73},   {86, 2, 60},
+    {71, 0, 47},     {54, 0, 33},     {30, 0, 11},     {0, 0, 0},
+    {148, 43, 96},   {133, 35, 85},   {117, 28, 75},   {102, 18, 64},
+    {87, 5, 52},     {71, 0, 40},     {54, 0, 25},     {30, 0, 5},
+    {0, 0, 0},       {149, 44, 85},   {132, 36, 75},   {118, 29, 66},
+    {101, 20, 55},   {86, 8, 44},     {70, 0, 32},     {53, 0, 18},
+    {29, 0, 2},      {0, 0, 0},       {149, 45, 74},   {133, 37, 64},
+    {118, 31, 55},   {102, 21, 45},   {85, 10, 34},    {70, 0, 22},
+    {53, 0, 6},      {28, 0, 0},      {0, 0, 0},       {150, 46, 61},
+    {133, 39, 52},   {118, 31, 44},   {102, 23, 34},   {85, 12, 24},
+    {70, 0, 12},     {52, 0, 0},      {28, 0, 0},      {0, 0, 0},
+    {150, 46, 51},   {133, 40, 42},   {119, 32, 35},   {102, 24, 25},
+    {85, 13, 14},    {70, 0, 1},      {52, 0, 0},      {27, 0, 0},
+    {0, 0, 0},       {53, 198, 244},  {49, 177, 218},  {41, 158, 195},
+    {32, 138, 171},  {22, 118, 147},  {11, 98, 124},   {0, 78, 100},
+    {0, 54, 71},     {0, 18, 34},     {69, 196, 220},  {64, 175, 196},
+    {54, 157, 176},  {45, 137, 154},  {32, 117, 133},  {19, 98, 111},
+    {0, 78, 89},     {0, 53, 63},     {0, 17, 27},     {80, 195, 198},
+    {69, 175, 179},  {60, 156, 159},  {50, 136, 139},  {38, 116, 120},
+    {25, 98, 101},   {4, 77, 80},     {0, 53, 55},     {0, 17, 21},
+    {84, 193, 177},  {75, 173, 159},  {64, 155, 142},  {55, 135, 124},
+    {41, 116, 107},  {27, 97, 89},    {9, 76, 70},     {0, 53, 47},
+    {0, 17, 11},     {89, 193, 157},  {79, 172, 140},  {70, 154, 125},
+    {57, 134, 109},  {44, 115, 92},   {32, 96, 76},    {13, 76, 59},
+    {0, 52, 39},     {0, 16, 4},      {94, 191, 135},  {85, 171, 121},
+    {72, 152, 108},  {60, 133, 94},   {47, 114, 80},   {32, 95, 65},
+    {15, 76, 49},    {0, 52, 29},     {0, 16, 0},      {98, 190, 113},
+    {87, 170, 100},  {76, 152, 89},   {62, 132, 77},   {49, 113, 65},
+    {35, 95, 52},    {18, 75, 37},    {0, 52, 18},     {0, 15, 0},
+    {103, 190, 89},  {90, 169, 80},   {78, 151, 70},   {64, 132, 60},
+    {51, 113, 49},   {37, 94, 38},    {20, 75, 25},    {0, 52, 5},
+    {0, 15, 0},      {106, 189, 69},  {93, 169, 61},   {80, 151, 53},
+    {66, 131, 45},   {52, 113, 36},   {37, 94, 25},    {19, 74, 11},
+    {0, 51, 0},      {0, 15, 0},      {76, 178, 229},  {68, 159, 205},
+    {61, 142, 183},  {50, 124, 160},  {40, 106, 138},  {28, 88, 116},
+    {12, 69, 93},    {0, 45, 66},     {0, 5, 29},      {86, 177, 207},
+    {78, 158, 184},  {67, 142, 166},  {56, 123, 145},  {45, 106, 125},
+    {31, 88, 105},   {16, 69, 83},    {0, 45, 58},     {0, 6, 22},
+    {93, 176, 187},  {81, 158, 168},  {71, 141, 150},  {61, 123, 131},
+    {47, 105, 113},  {35, 87, 94},    {20, 68, 74},    {0, 45, 51},
+    {0, 5, 16},      {98, 175, 168},  {84, 157, 150},  {75, 140, 134},
+    {63, 122, 117},  {50, 104, 100},  {37, 87, 83},    {21, 68, 65},
+    {0, 45, 42},     {0, 4, 7},       {100, 174, 149}, {89, 155, 132},
+    {76, 139, 117},  {65, 121, 102},  {53, 104, 87},   {39, 86, 72},
+    {23, 67, 55},    {0, 45, 34},     {0, 3, 0},       {103, 173, 130},
+    {92, 155, 115},  {80, 138, 102},  {68, 120, 88},   {53, 103, 75},
+    {40, 86, 61},    {24, 67, 45},    {0, 45, 25},     {0, 3, 0},
+    {107, 172, 108}, {95, 154, 96},   {82, 137, 85},   {70, 119, 73},
+    {55, 102, 61},   {42, 85, 49},    {25, 67, 34},    {0, 45, 14},
+    {0, 3, 0},       {110, 172, 86},  {97, 153, 76},   {85, 137, 67},
+    {70, 119, 57},   {56, 102, 46},   {42, 84, 35},    {26, 66, 21},
+    {0, 44, 1},      {0, 3, 0},       {112, 171, 67},  {98, 153, 59},
+    {86, 137, 52},   {71, 119, 44},   {58, 102, 34},   {44, 85, 22},
+    {27, 66, 7},     {0, 44, 0},      {0, 3, 0},       {90, 160, 215},
+    {81, 144, 193},  {70, 129, 173},  {61, 112, 151},  {49, 95, 131},
+    {37, 79, 109},   {22, 61, 87},    {0, 38, 61},     {0, 0, 25},
+    {96, 160, 194},  {86, 143, 174},  {75, 128, 157},  {65, 112, 137},
+    {53, 95, 117},   {40, 78, 98},    {25, 60, 78},    {0, 38, 53},
+    {0, 0, 17},      {100, 159, 177}, {89, 143, 159},  {79, 128, 143},
+    {67, 111, 124},  {55, 95, 107},   {42, 78, 89},    {27, 60, 70},
+    {2, 38, 46},     {0, 0, 9},       {104, 158, 159}, {92, 142, 143},
+    {81, 127, 127},  {69, 110, 110},  {56, 94, 94},    {43, 78, 78},
+    {28, 60, 60},    {2, 38, 38},     {0, 0, 1},       {107, 157, 140},
+    {94, 141, 125},  {82, 126, 112},  {71, 110, 97},   {59, 94, 82},
+    {45, 77, 67},    {29, 59, 51},    {4, 37, 30},     {0, 0, 0},
+    {110, 156, 122}, {97, 140, 109},  {85, 125, 97},   {72, 109, 83},
+    {58, 93, 71},    {45, 77, 57},    {29, 60, 42},    {5, 38, 22},
+    {0, 0, 0},       {111, 156, 103}, {99, 139, 91},   {87, 125, 81},
+    {73, 108, 69},   {60, 92, 58},    {46, 77, 45},    {30, 59, 31},
+    {5, 38, 12},     {0, 0, 0},       {115, 156, 82},  {101, 140, 73},
+    {88, 124, 63},   {74, 108, 53},   {60, 92, 44},    {46, 76, 32},
+    {31, 59, 18},    {6, 37, 0},      {0, 0, 0},       {116, 155, 65},
+    {102, 139, 58},  {89, 124, 49},   {75, 108, 41},   {61, 92, 32},
+    {48, 76, 21},    {31, 59, 6},     {5, 37, 0},      {0, 0, 0},
+    {100, 141, 201}, {88, 127, 181},  {79, 114, 162},  {69, 99, 142},
+    {57, 83, 122},   {44, 68, 102},   {30, 51, 81},    {7, 28, 56},
+    {0, 0, 19},      {105, 141, 182}, {94, 127, 163},  {83, 114, 146},
+    {71, 98, 128},   {59, 83, 110},   {46, 68, 91},    {31, 51, 72},
+    {10, 28, 48},    {0, 0, 11},      {108, 141, 166}, {96, 127, 149},
+    {85, 113, 133},  {73, 98, 116},   {60, 83, 99},    {46, 68, 82},
+    {32, 51, 64},    {11, 29, 41},    {0, 0, 2},       {111, 141, 149},
+    {98, 126, 134},  {88, 112, 119},  {74, 97, 103},   {61, 83, 88},
+    {48, 67, 72},    {33, 51, 56},    {11, 29, 34},    {0, 0, 0},
+    {112, 140, 132}, {100, 125, 118}, {89, 112, 105},  {75, 97, 91},
+    {62, 82, 77},    {49, 68, 62},    {33, 51, 47},    {12, 29, 26},
+    {0, 0, 0},       {115, 140, 116}, {102, 125, 103}, {90, 111, 91},
+    {76, 96, 78},    {62, 82, 65},    {49, 67, 52},    {34, 51, 38},
+    {13, 29, 18},    {0, 0, 0},       {117, 139, 97},  {103, 124, 87},
+    {91, 111, 77},   {78, 96, 65},    {63, 81, 54},    {49, 67, 41},
+    {34, 51, 27},    {12, 29, 7},     {0, 0, 0},       {119, 138, 78},
+    {105, 124, 69},  {92, 110, 60},   {78, 95, 50},    {65, 81, 40},
+    {50, 67, 29},    {34, 51, 15},    {13, 30, 0},     {0, 0, 0},
+    {120, 138, 64},  {106, 124, 54},  {93, 110, 47},   {78, 95, 38},
+    {65, 81, 29},    {50, 66, 17},    {34, 50, 2},     {13, 29, 0},
+    {0, 0, 0},       {107, 124, 189}, {96, 111, 169},  {85, 99, 152},
+    {73, 85, 132},   {61, 71, 114},   {48, 57, 95},    {34, 41, 75},
+    {14, 18, 51},    {0, 0, 13},      {111, 124, 171}, {100, 111, 153},
+    {88, 99, 137},   {75, 85, 120},   {63, 72, 103},   {50, 58, 85},
+    {36, 41, 66},    {15, 19, 43},    {0, 0, 4},       {113, 124, 156},
+    {101, 111, 139}, {90, 99, 125},   {77, 85, 109},   {64, 71, 93},
+    {51, 57, 77},    {36, 42, 59},    {17, 20, 37},    {0, 0, 0},
+    {115, 124, 140}, {103, 111, 125}, {90, 99, 112},   {78, 85, 97},
+    {64, 71, 82},    {52, 57, 67},    {36, 42, 50},    {16, 20, 30},
+    {0, 0, 0},       {117, 123, 125}, {104, 110, 111}, {92, 98, 99},
+    {79, 85, 86},    {65, 71, 72},    {51, 58, 59},    {37, 42, 43},
+    {17, 21, 22},    {0, 0, 0},       {118, 123, 110}, {105, 110, 97},
+    {93, 98, 86},    {78, 84, 74},    {66, 71, 62},    {52, 57, 49},
+    {37, 42, 34},    {17, 22, 14},    {0, 0, 0},       {120, 123, 93},
+    {106, 109, 82},  {94, 97, 72},    {80, 84, 61},    {66, 71, 50},
+    {52, 57, 38},    {37, 42, 24},    {17, 22, 2},     {0, 0, 0},
+    {121, 122, 75},  {108, 109, 66},  {95, 97, 58},    {80, 84, 48},
+    {66, 71, 37},    {52, 57, 26},    {37, 42, 12},    {16, 22, 0},
+    {0, 0, 0},       {122, 123, 62},  {108, 109, 52},  {95, 97, 45},
+    {81, 84, 36},    {67, 70, 26},    {52, 57, 14},    {37, 42, 0},
+    {15, 22, 0},     {0, 0, 0},       {113, 107, 177}, {102, 96, 159},
+    {89, 85, 141},   {78, 72, 124},   {65, 60, 107},   {52, 46, 89},
+    {37, 30, 70},    {18, 5, 46},     {0, 0, 6},       {116, 107, 160},
+    {104, 96, 144},  {92, 85, 129},   {80, 72, 112},   {67, 60, 96},
+    {53, 47, 80},    {38, 31, 62},    {19, 7, 39},     {0, 0, 0},
+    {118, 107, 147}, {105, 96, 131},  {93, 85, 118},   {80, 72, 101},
+    {67, 60, 87},    {54, 47, 71},    {39, 31, 54},    {19, 8, 32},
+    {0, 0, 0},       {119, 107, 132}, {106, 96, 118},  {94, 85, 106},
+    {81, 73, 91},    {67, 60, 77},    {54, 47, 63},    {39, 32, 47},
+    {20, 9, 25},     {0, 0, 0},       {119, 107, 118}, {106, 95, 105},
+    {94, 85, 93},    {81, 72, 80},    {68, 60, 68},    {54, 47, 54},
+    {39, 32, 39},    {20, 11, 18},    {0, 0, 0},       {121, 107, 104},
+    {107, 96, 92},   {95, 84, 80},    {81, 72, 69},    {68, 61, 58},
+    {54, 48, 46},    {39, 33, 31},    {20, 12, 9},     {0, 0, 0},
+    {123, 107, 88},  {108, 95, 77},   {96, 84, 68},    {82, 72, 57},
+    {68, 60, 46},    {54, 47, 35},    {39, 33, 20},    {19, 13, 2},
+    {0, 0, 0},       {123, 106, 72},  {110, 95, 63},   {96, 84, 54},
+    {82, 72, 45},    {69, 60, 35},    {55, 48, 23},    {39, 33, 9},
+    {18, 14, 0},     {0, 0, 0},       {125, 106, 60},  {110, 94, 50},
+    {98, 84, 42},    {83, 72, 34},    {69, 60, 25},    {55, 48, 12},
+    {39, 33, 0},     {17, 13, 0},     {0, 0, 0},       {118, 89, 165},
+    {105, 79, 148},  {93, 69, 132},   {81, 57, 115},   {68, 45, 99},
+    {55, 32, 82},    {41, 15, 64},    {21, 0, 41},     {0, 0, 0},
+    {120, 89, 150},  {107, 79, 135},  {96, 69, 121},   {82, 58, 105},
+    {70, 46, 89},    {56, 34, 73},    {41, 17, 56},    {21, 0, 34},
+    {0, 0, 0},       {121, 89, 137},  {108, 79, 123},  {96, 69, 109},
+    {82, 58, 95},    {70, 47, 81},    {56, 34, 66},    {41, 18, 49},
+    {21, 0, 28},     {0, 0, 0},       {122, 90, 124},  {109, 79, 110},
+    {96, 69, 99},    {83, 58, 85},    {70, 47, 72},    {56, 35, 58},
+    {41, 19, 42},    {21, 0, 22},     {0, 0, 0},       {123, 90, 111},
+    {110, 79, 98},   {97, 69, 87},    {83, 59, 75},    {70, 47, 63},
+    {56, 35, 50},    {41, 20, 35},    {21, 0, 14},     {0, 0, 0},
+    {123, 90, 98},   {110, 79, 87},   {97, 70, 76},    {84, 58, 64},
+    {70, 48, 53},    {56, 36, 41},    {40, 21, 26},    {21, 0, 5},
+    {0, 0, 0},       {125, 89, 84},   {111, 79, 73},   {97, 69, 64},
+    {84, 59, 54},    {70, 48, 43},    {56, 36, 31},    {40, 22, 17},
+    {20, 1, 1},      {0, 0, 0},       {125, 89, 69},   {112, 79, 60},
+    {98, 70, 51},    {84, 59, 42},    {70, 48, 32},    {56, 36, 20},
+    {41, 22, 5},     {19, 2, 0},      {0, 0, 0},       {126, 89, 57},
+    {112, 79, 49},   {99, 70, 41},    {84, 59, 32},    {70, 48, 22},
+    {56, 36, 10},    {40, 22, 0},     {18, 2, 0},      {0, 0, 0},
+    {121, 67, 154},  {108, 58, 138},  {97, 50, 124},   {84, 39, 107},
+    {71, 28, 92},    {58, 12, 76},    {43, 0, 59},     {20, 0, 37},
+    {0, 0, 0},       {124, 68, 140},  {111, 59, 126},  {98, 50, 112},
+    {84, 40, 98},    {71, 29, 83},    {58, 15, 67},    {42, 0, 51},
+    {20, 0, 30},     {0, 0, 0},       {124, 68, 129},  {111, 59, 114},
+    {99, 51, 102},   {86, 41, 88},    {71, 30, 75},    {58, 17, 60},
+    {42, 0, 45},     {20, 0, 24},     {0, 0, 0},       {125, 70, 116},
+    {111, 60, 103},  {99, 51, 92},    {85, 41, 79},    {71, 31, 66},
+    {58, 19, 53},    {42, 3, 38},     {20, 0, 17},     {0, 0, 0},
+    {125, 70, 104},  {111, 61, 93},   {99, 52, 81},    {85, 43, 69},
+    {72, 32, 58},    {58, 20, 45},    {42, 4, 31},     {20, 0, 8},
+    {0, 0, 0},       {126, 70, 92},   {111, 61, 81},   {99, 52, 71},
+    {85, 42, 60},    {71, 33, 49},    {57, 21, 37},    {42, 6, 23},
+    {20, 0, 3},      {0, 0, 0},       {126, 70, 79},   {112, 61, 70},
+    {99, 53, 60},    {85, 43, 50},    {71, 33, 39},    {57, 22, 28},
+    {41, 7, 13},     {19, 0, 0},      {0, 0, 0},       {127, 71, 66},
+    {113, 62, 56},   {100, 53, 48},   {86, 44, 39},    {71, 34, 29},
+    {57, 23, 18},    {41, 8, 2},      {18, 0, 0},      {0, 0, 0},
+    {128, 70, 55},   {114, 62, 46},   {100, 54, 39},   {86, 44, 30},
+    {71, 34, 20},    {57, 23, 7},     {41, 9, 0},      {18, 0, 0},
+    {0, 0, 0},       {124, 41, 145},  {111, 32, 128},  {99, 23, 114},
+    {86, 10, 100},   {73, 0, 85},     {60, 0, 71},     {43, 0, 54},
+    {17, 0, 32},     {0, 0, 0},       {126, 42, 131},  {113, 33, 117},
+    {100, 25, 104},  {86, 14, 90},    {73, 0, 77},     {60, 0, 63},
+    {44, 0, 47},     {18, 0, 25},     {0, 0, 0},       {127, 43, 120},
+    {113, 34, 106},  {101, 26, 95},   {86, 17, 82},    {73, 2, 69},
+    {59, 0, 56},     {43, 0, 41},     {18, 0, 19},     {0, 0, 0},
+    {127, 45, 109},  {113, 37, 97},   {101, 28, 85},   {86, 19, 74},
+    {73, 5, 61},     {59, 0, 48},     {43, 0, 34},     {19, 0, 11},
+    {0, 0, 0},       {127, 46, 98},   {114, 38, 86},   {100, 30, 76},
+    {87, 21, 65},    {73, 9, 54},     {59, 0, 41},     {43, 0, 26},
+    {18, 0, 5},      {0, 0, 0},       {127, 47, 87},   {113, 39, 76},
+    {101, 31, 67},   {86, 22, 56},    {72, 11, 45},    {59, 0, 33},
+    {43, 0, 19},     {18, 0, 2},      {0, 0, 0},       {128, 48, 75},
+    {114, 39, 65},   {101, 33, 56},   {86, 23, 46},    {72, 12, 36},
+    {58, 0, 24},     {42, 0, 9},      {17, 0, 0},      {0, 0, 0},
+    {129, 48, 63},   {114, 41, 54},   {102, 33, 46},   {87, 24, 36},
+    {72, 14, 26},    {58, 1, 14},     {42, 0, 2},      {16, 0, 0},
+    {0, 0, 0},       {128, 48, 53},   {114, 41, 44},   {102, 34, 37},
+    {87, 25, 27},    {72, 15, 17},    {58, 1, 3},      {41, 0, 0},
+    {15, 0, 0},      {0, 0, 0},       {0, 189, 242},   {0, 169, 217},
+    {0, 151, 194},   {0, 132, 170},   {0, 113, 147},   {0, 94, 123},
+    {0, 74, 99},     {0, 51, 71},     {0, 15, 34},     {1, 187, 219},
+    {1, 167, 195},   {0, 150, 175},   {0, 131, 153},   {0, 113, 132},
+    {0, 94, 111},    {0, 74, 89},     {0, 50, 63},     {0, 13, 28},
+    {1, 186, 198},   {1, 167, 178},   {0, 149, 158},   {0, 130, 139},
+    {0, 111, 119},   {0, 93, 100},    {0, 74, 80},     {0, 50, 55},
+    {0, 13, 22},     {1, 185, 176},   {1, 165, 159},   {1, 148, 142},
+    {0, 129, 123},   {0, 111, 106},   {0, 93, 89},     {0, 73, 70},
+    {0, 50, 47},     {0, 13, 13},     {1, 184, 157},   {1, 164, 141},
+    {1, 147, 125},   {0, 128, 110},   {0, 110, 93},    {0, 92, 77},
+    {0, 73, 60},     {0, 50, 39},     {0, 12, 5},      {25, 182, 137},
+    {25, 163, 122},  {17, 146, 109},  {0, 128, 96},    {0, 110, 81},
+    {0, 92, 66},     {0, 73, 51},     {0, 50, 30},     {0, 10, 0},
+    {42, 181, 114},  {35, 163, 102},  {30, 145, 91},   {14, 127, 80},
+    {0, 109, 67},    {0, 91, 53},     {0, 72, 39},     {0, 50, 19},
+    {0, 10, 0},      {52, 181, 92},   {43, 162, 83},   {32, 145, 73},
+    {19, 126, 63},   {0, 108, 52},    {0, 90, 40},     {0, 72, 27},
+    {0, 50, 7},      {0, 10, 0},      {57, 181, 74},   {48, 162, 66},
+    {37, 144, 57},   {24, 126, 49},   {7, 108, 40},    {0, 90, 29},
+    {0, 72, 15},     {0, 49, 0},      {0, 10, 0},      {1, 170, 227},
+    {1, 152, 203},   {0, 136, 182},   {0, 119, 159},   {0, 101, 137},
+    {0, 84, 115},    {0, 65, 92},     {0, 43, 66},     {0, 1, 29},
+    {1, 169, 206},   {1, 151, 184},   {1, 136, 165},   {0, 118, 144},
+    {0, 102, 125},   {0, 84, 105},    {0, 65, 83},     {0, 43, 58},
+    {0, 0, 22},      {29, 168, 186},  {21, 151, 167},  {14, 135, 150},
+    {4, 118, 131},   {0, 101, 112},   {0, 83, 94},     {0, 65, 75},
+    {0, 43, 51},     {0, 0, 16},      {41, 167, 167},  {33, 150, 150},
+    {31, 134, 134},  {19, 117, 117},  {4, 100, 100},   {0, 83, 83},
+    {0, 65, 65},     {0, 42, 43},     {0, 0, 8},       {48, 167, 149},
+    {41, 149, 133},  {33, 133, 118},  {25, 116, 103},  {13, 99, 88},
+    {0, 83, 73},     {0, 65, 56},     {0, 42, 35},     {0, 0, 0},
+    {58, 165, 130},  {49, 148, 115},  {42, 132, 103},  {31, 115, 89},
+    {18, 99, 75},    {0, 82, 61},     {0, 64, 46},     {0, 42, 26},
+    {0, 0, 0},       {62, 164, 110},  {55, 147, 97},   {45, 132, 87},
+    {35, 115, 75},   {22, 98, 63},    {5, 82, 50},     {0, 64, 36},
+    {0, 42, 16},     {0, 0, 0},       {69, 164, 89},   {60, 147, 78},
+    {50, 131, 70},   {37, 114, 59},   {26, 98, 49},    {10, 81, 37},
+    {0, 64, 24},     {0, 42, 4},      {0, 0, 0},       {71, 164, 71},
+    {63, 147, 63},   {53, 131, 55},   {40, 114, 47},   {28, 98, 38},
+    {13, 81, 26},    {0, 64, 12},     {0, 42, 0},      {0, 0, 0},
+    {28, 153, 214},  {24, 138, 193},  {23, 123, 171},  {16, 107, 150},
+    {0, 91, 130},    {0, 75, 109},    {0, 58, 87},     {0, 35, 61},
+    {0, 0, 25},      {48, 153, 194},  {41, 138, 174},  {34, 123, 156},
+    {27, 107, 136},  {16, 91, 117},   {1, 75, 98},     {0, 57, 78},
+    {0, 35, 53},     {0, 0, 17},      {55, 153, 177},  {47, 137, 158},
+    {42, 122, 142},  {33, 107, 124},  {22, 91, 106},   {6, 75, 88},
+    {0, 57, 70},     {0, 35, 46},     {0, 0, 9},       {61, 152, 158},
+    {53, 136, 143},  {45, 122, 127},  {36, 106, 111},  {24, 90, 94},
+    {10, 74, 78},    {0, 57, 61},     {0, 35, 39},     {0, 0, 2},
+    {67, 151, 141},  {59, 135, 126},  {49, 121, 112},  {39, 105, 98},
+    {29, 90, 83},    {14, 74, 68},    {0, 57, 52},     {0, 35, 31},
+    {0, 0, 0},       {71, 150, 123},  {62, 135, 110},  {54, 120, 98},
+    {42, 105, 84},   {31, 89, 71},    {16, 74, 58},    {0, 57, 43},
+    {0, 35, 22},     {0, 0, 0},       {74, 150, 105},  {64, 134, 92},
+    {55, 120, 83},   {45, 104, 71},   {34, 89, 59},    {20, 73, 47},
+    {0, 57, 32},     {0, 35, 13},     {0, 0, 0},       {78, 149, 84},
+    {69, 134, 75},   {59, 120, 66},   {47, 103, 56},   {34, 88, 46},
+    {22, 73, 34},    {1, 57, 20},     {0, 35, 1},      {0, 0, 0},
+    {80, 149, 69},   {70, 133, 61},   {60, 119, 53},   {49, 103, 44},
+    {36, 88, 35},    {23, 73, 24},    {2, 56, 10},     {0, 35, 0},
+    {0, 0, 0},       {58, 136, 200},  {50, 122, 180},  {45, 109, 162},
+    {38, 94, 141},   {27, 80, 121},   {15, 65, 102},   {0, 48, 81},
+    {0, 26, 56},     {0, 0, 19},      {66, 136, 182},  {59, 122, 163},
+    {52, 109, 146},  {42, 94, 128},   {32, 80, 109},   {20, 65, 91},
+    {2, 48, 72},     {0, 26, 49},     {0, 0, 11},      {70, 136, 165},
+    {62, 122, 149},  {55, 108, 133},  {46, 94, 116},   {35, 80, 99},
+    {21, 65, 82},    {4, 49, 64},     {0, 26, 41},     {0, 0, 3},
+    {76, 135, 149},  {66, 121, 133},  {58, 108, 119},  {48, 94, 103},
+    {36, 79, 88},    {23, 65, 73},    {7, 49, 56},     {0, 27, 34},
+    {0, 0, 0},       {78, 135, 133},  {69, 120, 118},  {60, 107, 106},
+    {50, 93, 92},    {39, 79, 77},    {26, 65, 63},    {8, 49, 47},
+    {0, 27, 26},     {0, 0, 0},       {82, 134, 117},  {71, 120, 104},
+    {62, 107, 92},   {51, 93, 79},    {39, 78, 66},    {27, 64, 53},
+    {10, 48, 39},    {0, 27, 18},     {0, 0, 0},       {84, 134, 99},
+    {73, 119, 87},   {64, 106, 77},   {53, 92, 66},    {42, 78, 55},
+    {28, 64, 42},    {11, 48, 29},    {0, 28, 9},      {0, 0, 0},
+    {87, 133, 81},   {76, 119, 72},   {66, 106, 62},   {55, 92, 52},
+    {43, 78, 42},    {29, 64, 31},    {12, 48, 17},    {0, 28, 0},
+    {0, 0, 0},       {88, 134, 67},   {77, 119, 58},   {68, 106, 51},
+    {56, 92, 42},    {44, 78, 32},    {30, 64, 20},    {12, 48, 6},
+    {0, 28, 0},      {0, 0, 0},       {73, 120, 189},  {64, 107, 168},
+    {57, 96, 151},   {47, 82, 133},   {38, 69, 114},   {26, 55, 95},
+    {11, 39, 75},    {0, 16, 51},     {0, 0, 14},      {78, 120, 171},
+    {69, 107, 153},  {62, 95, 137},   {51, 82, 119},   {40, 69, 102},
+    {29, 55, 85},    {15, 39, 66},    {0, 17, 44},     {0, 0, 4},
+    {81, 120, 156},  {71, 107, 140},  {64, 95, 125},   {53, 82, 109},
+    {42, 69, 93},    {31, 55, 77},    {16, 39, 59},    {0, 18, 37},
+    {0, 0, 0},       {85, 120, 141},  {74, 107, 126},  {65, 95, 112},
+    {54, 82, 97},    {43, 69, 82},    {32, 55, 67},    {17, 39, 51},
+    {0, 19, 30},     {0, 0, 0},       {86, 119, 126},  {76, 106, 112},
+    {66, 95, 100},   {56, 81, 85},    {45, 69, 72},    {33, 55, 59},
+    {18, 40, 43},    {0, 19, 22},     {0, 0, 0},       {89, 119, 110},
+    {78, 106, 98},   {69, 94, 87},    {56, 81, 75},    {46, 68, 62},
+    {33, 55, 49},    {18, 40, 35},    {0, 20, 15},     {0, 0, 0},
+    {89, 119, 95},   {80, 106, 83},   {70, 94, 73},    {58, 81, 63},
+    {46, 68, 51},    {34, 55, 39},    {19, 40, 25},    {0, 20, 4},
+    {0, 0, 0},       {92, 118, 78},   {82, 106, 68},   {70, 93, 59},
+    {59, 81, 49},    {47, 68, 39},    {34, 55, 28},    {19, 40, 14},
+    {0, 20, 0},      {0, 0, 0},       {93, 118, 65},   {82, 105, 55},
+    {72, 93, 48},    {60, 81, 39},    {47, 68, 29},    {34, 55, 18},
+    {20, 40, 2},     {0, 20, 0},      {0, 0, 0},       {83, 104, 177},
+    {74, 93, 159},   {65, 82, 142},   {56, 70, 124},   {45, 57, 106},
+    {33, 44, 89},    {20, 28, 70},    {1, 4, 46},      {0, 0, 8},
+    {86, 104, 161},  {78, 93, 145},   {68, 82, 128},   {58, 70, 112},
+    {48, 58, 96},    {35, 45, 80},    {21, 29, 62},    {1, 6, 40},
+    {0, 0, 0},       {89, 104, 147},  {79, 93, 131},   {69, 82, 118},
+    {59, 70, 102},   {47, 58, 87},    {36, 45, 72},    {23, 29, 55},
+    {3, 7, 33},      {0, 0, 0},       {90, 104, 132},  {80, 93, 119},
+    {71, 82, 106},   {60, 70, 91},    {48, 58, 77},    {37, 45, 62},
+    {23, 30, 47},    {4, 7, 26},      {0, 0, 0},       {92, 104, 118},
+    {82, 93, 105},   {72, 82, 93},    {61, 69, 80},    {50, 58, 68},
+    {37, 45, 55},    {23, 30, 39},    {4, 8, 18},      {0, 0, 0},
+    {94, 104, 105},  {82, 92, 93},    {72, 82, 82},    {61, 70, 70},
+    {50, 58, 58},    {38, 46, 46},    {23, 31, 31},    {6, 10, 11},
+    {0, 0, 0},       {95, 104, 90},   {84, 92, 79},    {74, 82, 70},
+    {62, 70, 58},    {50, 58, 48},    {37, 46, 36},    {23, 31, 22},
+    {4, 11, 3},      {0, 0, 0},       {96, 103, 74},   {85, 92, 65},
+    {75, 81, 56},    {63, 70, 47},    {50, 58, 37},    {38, 46, 25},
+    {24, 31, 11},    {3, 11, 0},      {0, 0, 0},       {97, 103, 62},
+    {86, 92, 53},    {76, 81, 45},    {63, 69, 36},    {51, 58, 27},
+    {38, 46, 15},    {23, 31, 0},     {3, 11, 0},      {0, 0, 0},
+    {90, 87, 165},   {81, 77, 148},   {72, 67, 132},   {62, 55, 116},
+    {50, 44, 99},    {39, 31, 82},    {25, 14, 64},    {1, 0, 42},
+    {0, 0, 1},       {93, 87, 150},   {83, 77, 135},   {74, 67, 121},
+    {63, 56, 105},   {52, 45, 90},    {40, 32, 74},    {25, 16, 57},
+    {3, 0, 35},      {0, 0, 0},       {95, 87, 138},   {85, 77, 123},
+    {75, 67, 109},   {63, 57, 95},    {53, 45, 81},    {41, 33, 66},
+    {26, 17, 50},    {4, 0, 28},      {0, 0, 0},       {95, 88, 124},
+    {85, 77, 111},   {75, 67, 99},    {63, 57, 86},    {53, 45, 72},
+    {41, 33, 58},    {26, 18, 43},    {5, 1, 22},      {0, 0, 0},
+    {97, 88, 112},   {87, 77, 100},   {76, 68, 88},    {64, 57, 76},
+    {53, 46, 63},    {41, 34, 50},    {26, 19, 35},    {5, 2, 14},
+    {0, 0, 0},       {99, 87, 99},    {87, 78, 88},    {76, 68, 77},
+    {65, 57, 65},    {53, 46, 54},    {41, 35, 42},    {27, 20, 27},
+    {6, 2, 5},       {0, 0, 0},       {100, 87, 85},   {88, 77, 75},
+    {77, 68, 65},    {65, 57, 54},    {53, 46, 44},    {41, 35, 32},
+    {27, 21, 19},    {5, 3, 2},       {0, 0, 0},       {100, 88, 71},
+    {89, 77, 61},    {78, 68, 53},    {66, 57, 44},    {53, 47, 33},
+    {41, 35, 22},    {27, 21, 7},     {5, 3, 0},       {0, 0, 0},
+    {101, 87, 60},   {90, 77, 52},    {79, 68, 44},    {66, 58, 34},
+    {53, 47, 25},    {41, 35, 13},    {26, 22, 0},     {5, 3, 0},
+    {0, 0, 0},       {97, 67, 155},   {86, 58, 138},   {77, 50, 125},
+    {66, 39, 108},   {55, 28, 92},    {43, 12, 76},    {29, 0, 59},
+    {2, 0, 37},      {0, 0, 0},       {99, 67, 141},   {88, 59, 127},
+    {78, 50, 113},   {68, 40, 98},    {56, 29, 83},    {44, 15, 68},
+    {29, 0, 52},     {3, 0, 30},      {0, 0, 0},       {100, 68, 129},
+    {89, 59, 115},   {80, 51, 103},   {68, 41, 89},    {56, 30, 75},
+    {44, 16, 61},    {30, 0, 45},     {4, 0, 24},      {0, 0, 0},
+    {100, 69, 118},  {90, 60, 104},   {80, 51, 92},    {67, 41, 79},
+    {56, 31, 66},    {44, 18, 53},    {29, 2, 38},     {4, 0, 18},
+    {0, 0, 0},       {101, 69, 104},  {90, 61, 93},    {79, 51, 82},
+    {67, 42, 70},    {56, 32, 59},    {44, 20, 46},    {29, 4, 31},
+    {6, 0, 9},       {0, 0, 0},       {102, 69, 93},   {90, 61, 83},
+    {80, 52, 72},    {68, 42, 61},    {56, 33, 50},    {43, 20, 38},
+    {29, 5, 23},     {7, 0, 4},       {0, 0, 0},       {102, 70, 80},
+    {91, 61, 71},    {80, 52, 61},    {68, 43, 51},    {56, 32, 40},
+    {44, 21, 29},    {30, 6, 14},     {7, 0, 0},       {0, 0, 0},
+    {103, 70, 68},   {92, 61, 58},    {81, 53, 50},    {69, 43, 41},
+    {56, 34, 31},    {43, 22, 19},    {29, 7, 3},      {7, 0, 0},
+    {0, 0, 0},       {104, 70, 57},   {92, 61, 48},    {82, 53, 40},
+    {69, 43, 32},    {56, 34, 22},    {43, 23, 10},    {29, 8, 0},
+    {6, 0, 0},       {0, 0, 0},       {101, 45, 145},  {91, 35, 129},
+    {80, 26, 116},   {69, 15, 101},   {59, 0, 86},     {46, 0, 71},
+    {31, 0, 55},     {0, 0, 33},      {0, 0, 0},       {104, 44, 132},
+    {92, 36, 118},   {82, 28, 105},   {71, 17, 91},    {58, 3, 77},
+    {46, 0, 63},     {31, 0, 48},     {2, 0, 26},      {0, 0, 0},
+    {104, 46, 121},  {93, 37, 107},   {82, 30, 96},    {70, 20, 83},
+    {58, 6, 70},     {46, 0, 57},     {32, 0, 41},     {4, 0, 20},
+    {0, 0, 0},       {104, 48, 110},  {93, 40, 98},    {82, 31, 87},
+    {70, 22, 74},    {59, 9, 62},     {45, 0, 49},     {31, 0, 35},
+    {6, 0, 13},      {0, 0, 0},       {104, 48, 99},   {92, 41, 88},
+    {82, 32, 77},    {70, 23, 65},    {58, 11, 54},    {46, 0, 42},
+    {32, 0, 27},     {7, 0, 5},       {0, 0, 0},       {105, 50, 88},
+    {93, 41, 77},    {82, 34, 68},    {71, 24, 57},    {58, 13, 46},
+    {45, 1, 35},     {31, 0, 21},     {7, 0, 2},       {0, 0, 0},
+    {105, 50, 76},   {94, 41, 66},    {83, 34, 57},    {71, 25, 47},
+    {58, 15, 37},    {45, 2, 25},     {32, 0, 11},     {7, 0, 0},
+    {0, 0, 0},       {106, 50, 64},   {94, 42, 55},    {83, 35, 47},
+    {71, 26, 38},    {58, 16, 27},    {45, 4, 17},     {31, 0, 4},
+    {7, 0, 0},       {0, 0, 0},       {106, 51, 54},   {95, 42, 45},
+    {83, 35, 38},    {71, 27, 30},    {58, 16, 19},    {45, 5, 7},
+    {30, 0, 0},      {6, 0, 0},       {0, 0, 0},       {0, 181, 240},
+    {0, 162, 216},   {0, 144, 193},   {0, 126, 168},   {0, 109, 146},
+    {0, 91, 123},    {0, 71, 98},     {0, 48, 71},     {0, 9, 34},
+    {0, 179, 218},   {0, 161, 195},   {0, 144, 174},   {0, 126, 153},
+    {0, 108, 132},   {0, 90, 110},    {0, 71, 88},     {0, 48, 63},
+    {0, 8, 29},      {0, 178, 197},   {0, 159, 177},   {0, 143, 159},
+    {0, 125, 139},   {0, 107, 119},   {0, 90, 99},     {0, 71, 79},
+    {0, 48, 55},     {0, 8, 22},      {0, 177, 177},   {0, 158, 158},
+    {0, 142, 141},   {0, 124, 123},   {0, 107, 106},   {0, 89, 88},
+    {0, 71, 70},     {0, 48, 47},     {0, 8, 14},      {0, 176, 157},
+    {0, 158, 141},   {0, 141, 126},   {0, 123, 109},   {0, 106, 93},
+    {0, 89, 78},     {0, 70, 60},     {0, 47, 39},     {0, 7, 5},
+    {0, 175, 138},   {0, 157, 123},   {0, 141, 110},   {0, 123, 96},
+    {0, 105, 81},    {0, 88, 67},     {0, 70, 51},     {0, 48, 30},
+    {0, 6, 0},       {0, 173, 115},   {0, 155, 104},   {0, 140, 92},
+    {0, 122, 80},    {0, 105, 67},    {0, 88, 55},     {0, 69, 40},
+    {0, 47, 20},     {0, 6, 0},       {0, 173, 94},    {0, 155, 85},
+    {0, 139, 75},    {0, 121, 64},    {0, 104, 53},    {0, 88, 42},
+    {0, 70, 28},     {0, 47, 9},      {0, 6, 0},       {0, 173, 76},
+    {0, 155, 70},    {0, 138, 61},    {0, 122, 53},    {0, 104, 44},
+    {0, 87, 32},     {0, 69, 18},     {0, 47, 0},      {0, 6, 0},
+    {0, 164, 226},   {0, 147, 203},   {0, 131, 181},   {0, 114, 158},
+    {0, 97, 136},    {0, 80, 115},    {0, 63, 92},     {0, 40, 65},
+    {0, 0, 30},      {0, 162, 205},   {0, 145, 184},   {0, 130, 164},
+    {0, 114, 143},   {0, 97, 124},    {0, 81, 104},    {0, 63, 83},
+    {0, 40, 58},     {0, 0, 23},      {0, 162, 187},   {0, 145, 167},
+    {0, 130, 150},   {0, 113, 131},   {0, 96, 112},    {0, 80, 93},
+    {0, 62, 74},     {0, 40, 50},     {0, 0, 16},      {0, 160, 167},
+    {0, 144, 150},   {0, 129, 134},   {0, 112, 116},   {0, 96, 100},
+    {0, 80, 82},     {0, 62, 65},     {0, 40, 43},     {0, 0, 7},
+    {0, 160, 148},   {0, 143, 133},   {0, 128, 118},   {0, 111, 103},
+    {0, 96, 88},     {0, 80, 73},     {0, 62, 56},     {0, 40, 35},
+    {0, 0, 0},       {0, 158, 130},   {0, 142, 117},   {0, 127, 104},
+    {0, 111, 89},    {0, 95, 76},     {0, 79, 62},     {0, 62, 46},
+    {0, 40, 26},     {0, 0, 0},       {0, 158, 111},   {0, 141, 99},
+    {0, 127, 88},    {0, 111, 76},    {0, 95, 63},     {0, 79, 51},
+    {0, 62, 37},     {0, 40, 18},     {0, 0, 0},       {0, 158, 91},
+    {0, 141, 81},    {0, 126, 72},    {0, 110, 62},    {0, 94, 50},
+    {0, 79, 39},     {0, 62, 25},     {0, 40, 5},      {0, 0, 0},
+    {0, 157, 74},    {0, 141, 66},    {0, 126, 59},    {0, 110, 49},
+    {0, 94, 40},     {0, 78, 29},     {0, 61, 15},     {0, 40, 0},
+    {0, 0, 0},       {0, 148, 214},   {0, 133, 192},   {0, 119, 171},
+    {0, 103, 150},   {0, 87, 129},    {0, 72, 108},    {0, 55, 86},
+    {0, 32, 61},     {0, 0, 25},      {0, 147, 193},   {0, 132, 173},
+    {0, 118, 155},   {0, 103, 136},   {0, 87, 116},    {0, 72, 98},
+    {0, 55, 78},     {0, 32, 53},     {0, 0, 17},      {0, 147, 176},
+    {0, 132, 158},   {0, 118, 142},   {0, 102, 124},   {0, 87, 106},
+    {0, 72, 88},     {0, 55, 69},     {0, 33, 46},     {0, 0, 9},
+    {0, 146, 159},   {0, 131, 142},   {0, 117, 127},   {0, 102, 111},
+    {0, 87, 95},     {0, 71, 79},     {0, 55, 61},     {0, 33, 39},
+    {0, 0, 2},       {0, 145, 140},   {0, 130, 126},   {0, 117, 112},
+    {0, 101, 98},    {0, 86, 83},     {0, 71, 68},     {0, 55, 52},
+    {0, 33, 31},     {0, 0, 0},       {0, 144, 124},   {0, 130, 111},
+    {0, 116, 99},    {0, 101, 84},    {0, 86, 72},     {0, 71, 59},
+    {0, 55, 43},     {0, 33, 23},     {0, 0, 0},       {0, 144, 106},
+    {0, 129, 94},    {0, 115, 83},    {0, 101, 72},    {0, 85, 60},
+    {0, 71, 48},     {0, 55, 34},     {0, 33, 14},     {0, 0, 0},
+    {3, 143, 86},    {0, 129, 77},    {0, 115, 68},    {0, 100, 58},
+    {0, 85, 48},     {0, 70, 36},     {0, 54, 22},     {0, 33, 3},
+    {0, 0, 0},       {18, 143, 72},   {13, 128, 63},   {0, 115, 57},
+    {0, 100, 47},    {0, 85, 37},     {0, 70, 26},     {0, 54, 13},
+    {0, 33, 0},      {0, 0, 0},       {0, 132, 200},   {0, 118, 179},
+    {0, 105, 161},   {0, 91, 140},    {0, 76, 121},    {0, 62, 101},
+    {0, 46, 81},     {0, 24, 56},     {0, 0, 19},      {0, 131, 182},
+    {0, 118, 163},   {0, 105, 146},   {0, 91, 128},    {0, 77, 110},
+    {0, 62, 91},     {0, 46, 72},     {0, 25, 48},     {0, 0, 11},
+    {0, 131, 165},   {0, 117, 149},   {0, 104, 133},   {0, 91, 116},
+    {0, 77, 99},     {0, 62, 82},     {0, 46, 64},     {0, 25, 41},
+    {0, 0, 4},       {0, 131, 149},   {0, 116, 134},   {0, 104, 119},
+    {0, 91, 104},    {0, 77, 89},     {0, 62, 73},     {0, 46, 56},
+    {0, 25, 34},     {0, 0, 0},       {10, 130, 133},  {2, 116, 119},
+    {0, 104, 106},   {0, 90, 91},     {0, 76, 78},     {0, 62, 64},
+    {0, 46, 48},     {0, 26, 27},     {0, 0, 0},       {23, 130, 118},
+    {20, 116, 104},  {13, 103, 93},   {3, 89, 79},     {0, 76, 67},
+    {0, 62, 54},     {0, 46, 39},     {0, 26, 19},     {0, 0, 0},
+    {33, 129, 101},  {27, 115, 89},   {19, 103, 79},   {9, 89, 67},
+    {0, 75, 56},     {0, 61, 43},     {0, 46, 29},     {0, 26, 10},
+    {0, 0, 0},       {41, 128, 83},   {35, 115, 73},   {27, 102, 64},
+    {15, 89, 55},    {0, 76, 45},     {0, 62, 33},     {0, 46, 18},
+    {0, 26, 0},      {0, 0, 0},       {43, 129, 69},   {38, 115, 61},
+    {30, 102, 54},   {17, 89, 45},    {2, 75, 34},     {0, 61, 23},
+    {0, 46, 9},      {0, 26, 0},      {0, 0, 0},       {1, 116, 188},
+    {1, 104, 168},   {0, 92, 151},    {0, 79, 132},    {0, 66, 113},
+    {0, 52, 94},     {0, 36, 75},     {0, 14, 52},     {0, 0, 14},
+    {17, 116, 171},  {16, 104, 153},  {14, 92, 137},   {8, 79, 119},
+    {0, 67, 102},    {0, 53, 85},     {0, 37, 67},     {0, 16, 44},
+    {0, 0, 4},       {31, 116, 155},  {27, 104, 140},  {21, 92, 125},
+    {13, 79, 109},   {3, 66, 93},     {0, 53, 77},     {0, 37, 59},
+    {0, 16, 38},     {0, 0, 0},       {37, 115, 141},  {30, 103, 126},
+    {26, 92, 112},   {16, 79, 98},    {5, 66, 83},     {0, 53, 67},
+    {0, 38, 51},     {0, 17, 31},     {0, 0, 0},       {41, 115, 126},
+    {37, 103, 112},  {31, 92, 100},   {22, 79, 86},    {10, 66, 72},
+    {0, 53, 59},     {0, 38, 44},     {0, 17, 23},     {0, 0, 0},
+    {48, 115, 111},  {41, 102, 99},   {34, 91, 88},    {24, 78, 76},
+    {14, 66, 63},    {0, 53, 50},     {0, 38, 36},     {0, 18, 15},
+    {0, 0, 0},       {51, 115, 95},   {46, 102, 85},   {37, 91, 74},
+    {26, 78, 63},    {16, 66, 52},    {0, 53, 40},     {0, 38, 26},
+    {0, 18, 5},      {0, 0, 0},       {55, 114, 80},   {47, 102, 69},
+    {40, 90, 60},    {30, 78, 51},    {19, 66, 41},    {3, 53, 29},
+    {0, 38, 15},     {0, 17, 0},      {0, 0, 0},       {56, 114, 66},
+    {50, 102, 58},   {40, 91, 50},    {32, 78, 41},    {18, 66, 32},
+    {4, 53, 21},     {0, 38, 5},      {0, 17, 0},      {0, 0, 0},
+    {39, 102, 178},  {37, 90, 159},   {30, 79, 142},   {21, 68, 124},
+    {14, 55, 106},   {0, 42, 89},     {0, 26, 70},     {0, 4, 46},
+    {0, 0, 8},       {48, 102, 161},  {42, 90, 145},   {35, 79, 128},
+    {26, 68, 112},   {19, 55, 96},    {3, 43, 79},     {0, 27, 62},
+    {0, 6, 40},      {0, 0, 0},       {50, 102, 147},  {44, 90, 132},
+    {37, 79, 118},   {30, 68, 102},   {20, 56, 87},    {7, 43, 72},
+    {0, 28, 55},     {0, 6, 34},      {0, 0, 0},       {53, 101, 133},
+    {47, 90, 118},   {41, 79, 106},   {32, 68, 91},    {21, 56, 78},
+    {9, 43, 63},     {0, 28, 47},     {0, 6, 26},      {0, 0, 0},
+    {57, 101, 119},  {50, 89, 106},   {42, 79, 94},    {34, 67, 81},
+    {24, 56, 68},    {9, 44, 55},     {0, 29, 40},     {0, 6, 19},
+    {0, 0, 0},       {60, 100, 105},  {50, 90, 94},    {45, 80, 83},
+    {36, 68, 71},    {24, 56, 59},    {11, 44, 46},    {0, 29, 32},
+    {0, 7, 12},      {0, 0, 0},       {63, 101, 91},   {55, 90, 80},
+    {46, 79, 70},    {37, 68, 59},    {26, 56, 49},    {12, 44, 37},
+    {1, 29, 23},     {0, 7, 3},       {0, 0, 0},       {64, 101, 75},
+    {56, 89, 67},    {48, 79, 57},    {37, 68, 48},    {27, 56, 37},
+    {15, 44, 26},    {0, 29, 12},     {0, 7, 0},       {0, 0, 0},
+    {66, 101, 64},   {58, 89, 55},    {49, 79, 47},    {39, 68, 38},
+    {27, 56, 29},    {14, 44, 18},    {1, 30, 2},      {0, 7, 0},
+    {0, 0, 0},       {57, 86, 165},   {51, 75, 148},   {45, 65, 133},
+    {38, 54, 116},   {28, 43, 100},   {16, 29, 83},    {0, 13, 64},
+    {0, 0, 42},      {0, 0, 3},       {60, 86, 151},   {55, 75, 135},
+    {47, 66, 121},   {39, 55, 105},   {30, 44, 90},    {18, 31, 74},
+    {3, 16, 57},     {0, 1, 35},      {0, 0, 0},       {62, 86, 139},
+    {56, 75, 123},   {49, 66, 110},   {40, 55, 95},    {30, 44, 81},
+    {19, 31, 66},    {4, 17, 51},     {0, 1, 29},      {0, 0, 0},
+    {65, 86, 125},   {56, 76, 112},   {49, 66, 99},    {39, 55, 86},
+    {31, 44, 72},    {19, 32, 59},    {5, 18, 44},     {0, 1, 23},
+    {0, 0, 0},       {67, 86, 113},   {58, 75, 100},   {51, 66, 88},
+    {41, 56, 77},    {31, 45, 64},    {20, 32, 51},    {6, 18, 35},
+    {0, 1, 14},      {0, 0, 0},       {69, 86, 99},    {61, 76, 88},
+    {52, 66, 78},    {43, 56, 66},    {32, 45, 55},    {20, 33, 42},
+    {7, 19, 27},     {0, 1, 6},       {0, 0, 0},       {69, 86, 86},
+    {61, 76, 75},    {53, 67, 66},    {43, 56, 55},    {33, 45, 45},
+    {21, 34, 34},    {8, 20, 20},     {0, 2, 2},       {0, 0, 0},
+    {71, 86, 72},    {63, 75, 62},    {54, 66, 55},    {45, 56, 45},
+    {33, 45, 35},    {22, 34, 23},    {7, 20, 8},      {0, 2, 0},
+    {0, 0, 0},       {71, 86, 62},    {64, 75, 53},    {55, 66, 46},
+    {45, 56, 36},    {33, 46, 27},    {22, 34, 15},    {8, 20, 0},
+    {0, 2, 0},       {0, 0, 0},       {69, 67, 156},   {61, 58, 140},
+    {53, 50, 125},   {45, 39, 108},   {35, 28, 93},    {25, 12, 77},
+    {12, 0, 59},     {0, 0, 37},      {0, 0, 0},       {71, 68, 142},
+    {63, 59, 126},   {56, 50, 114},   {47, 40, 98},    {37, 28, 84},
+    {26, 15, 68},    {12, 0, 53},     {0, 0, 30},      {0, 0, 0},
+    {72, 68, 130},   {63, 59, 116},   {56, 50, 104},   {47, 40, 90},
+    {38, 30, 75},    {27, 16, 61},    {13, 0, 46},     {0, 0, 24},
+    {0, 0, 0},       {73, 69, 118},   {65, 59, 105},   {57, 51, 92},
+    {47, 41, 80},    {37, 30, 67},    {26, 18, 53},    {14, 1, 39},
+    {0, 0, 18},      {0, 0, 0},       {74, 69, 106},   {65, 60, 93},
+    {57, 51, 82},    {48, 41, 70},    {38, 31, 59},    {26, 19, 46},
+    {13, 2, 32},     {0, 0, 10},      {0, 0, 0},       {76, 69, 95},
+    {66, 61, 84},    {58, 52, 73},    {48, 42, 61},    {37, 32, 50},
+    {26, 20, 38},    {14, 4, 24},     {0, 0, 4},       {0, 0, 0},
+    {76, 69, 81},    {68, 60, 72},    {58, 52, 62},    {48, 42, 51},
+    {38, 32, 41},    {27, 21, 30},    {14, 4, 16},     {0, 0, 1},
+    {0, 0, 0},       {76, 69, 68},    {68, 61, 60},    {60, 52, 51},
+    {49, 43, 41},    {38, 33, 32},    {27, 21, 20},    {14, 5, 5},
+    {0, 0, 0},       {0, 0, 0},       {78, 70, 59},    {69, 61, 50},
+    {60, 52, 42},    {49, 43, 34},    {39, 33, 24},    {27, 22, 13},
+    {14, 7, 1},      {0, 0, 0},       {0, 0, 0},       {75, 46, 146},
+    {68, 38, 131},   {60, 30, 117},   {50, 19, 102},   {41, 4, 87},
+    {29, 0, 72},     {13, 0, 55},     {0, 0, 33},      {0, 0, 0},
+    {78, 47, 132},   {70, 39, 119},   {61, 30, 105},   {53, 20, 92},
+    {42, 5, 78},     {30, 0, 64},     {13, 0, 49},     {0, 0, 27},
+    {0, 0, 0},       {79, 48, 122},   {70, 40, 108},   {62, 32, 96},
+    {52, 22, 84},    {42, 9, 71},     {30, 0, 58},     {14, 0, 42},
+    {0, 0, 20},      {0, 0, 0},       {79, 50, 111},   {70, 42, 99},
+    {62, 33, 88},    {52, 23, 74},    {41, 11, 63},    {29, 0, 50},
+    {14, 0, 36},     {0, 0, 14},      {0, 0, 0},       {80, 50, 99},
+    {70, 42, 89},    {61, 34, 78},    {52, 25, 67},    {41, 14, 55},
+    {30, 0, 42},     {15, 0, 28},     {0, 0, 6},       {0, 0, 0},
+    {81, 51, 89},    {71, 43, 78},    {62, 35, 69},    {52, 25, 58},
+    {42, 15, 47},    {30, 3, 36},     {15, 0, 22},     {0, 0, 3},
+    {0, 0, 0},       {81, 51, 77},    {71, 44, 68},    {63, 36, 59},
+    {53, 26, 49},    {41, 16, 38},    {31, 4, 27},     {16, 0, 12},
+    {0, 0, 0},       {0, 0, 0},       {81, 52, 65},    {72, 43, 56},
+    {63, 36, 48},    {53, 27, 39},    {41, 17, 29},    {30, 4, 18},
+    {14, 0, 3},      {0, 0, 0},       {0, 0, 0},       {81, 52, 55},
+    {73, 44, 47},    {64, 36, 39},    {53, 28, 32},    {42, 18, 21},
+    {31, 6, 9},      {14, 0, 0},      {0, 0, 0},       {0, 0, 0},
+    {0, 174, 239},   {0, 156, 214},   {0, 139, 192},   {0, 121, 168},
+    {0, 105, 145},   {0, 87, 123},    {0, 68, 98},     {0, 46, 70},
+    {0, 3, 35},      {0, 172, 217},   {0, 155, 194},   {0, 139, 173},
+    {0, 121, 152},   {0, 104, 130},   {0, 87, 110},    {0, 69, 88},
+    {0, 46, 63},     {0, 4, 28},      {0, 171, 197},   {0, 153, 175},
+    {0, 138, 158},   {0, 121, 139},   {0, 103, 118},   {0, 86, 100},
+    {0, 68, 79},     {0, 46, 55},     {0, 4, 22},      {0, 170, 177},
+    {0, 152, 158},   {0, 136, 141},   {0, 119, 124},   {0, 103, 106},
+    {0, 86, 88},     {0, 68, 70},     {0, 45, 47},     {0, 3, 14},
+    {0, 169, 157},   {0, 152, 141},   {0, 136, 126},   {0, 119, 109},
+    {0, 102, 94},    {0, 86, 78},     {0, 68, 60},     {0, 46, 39},
+    {0, 3, 5},       {0, 167, 138},   {0, 150, 124},   {0, 135, 111},
+    {0, 118, 97},    {0, 102, 82},    {0, 85, 68},     {0, 68, 52},
+    {0, 46, 31},     {0, 3, 0},       {0, 167, 118},   {0, 150, 104},
+    {0, 135, 94},    {0, 118, 81},    {0, 101, 69},    {0, 84, 56},
+    {0, 67, 41},     {0, 45, 21},     {0, 3, 0},       {0, 166, 97},
+    {0, 149, 87},    {0, 134, 77},    {0, 117, 67},    {0, 101, 56},
+    {0, 85, 44},     {0, 67, 30},     {0, 45, 10},     {0, 3, 0},
+    {0, 165, 79},    {0, 149, 73},    {0, 133, 64},    {0, 117, 56},
+    {0, 101, 46},    {0, 85, 34},     {0, 68, 21},     {0, 46, 1},
+    {0, 3, 0},       {0, 158, 225},   {0, 141, 201},   {0, 126, 180},
+    {0, 109, 158},   {0, 94, 136},    {0, 78, 114},    {0, 60, 91},
+    {0, 38, 66},     {0, 0, 30},      {0, 156, 203},   {0, 140, 183},
+    {0, 125, 164},   {0, 109, 143},   {0, 94, 124},    {0, 78, 104},
+    {0, 61, 83},     {0, 38, 57},     {0, 0, 23},      {0, 156, 186},
+    {0, 140, 166},   {0, 125, 150},   {0, 109, 130},   {0, 93, 111},
+    {0, 77, 93},     {0, 60, 74},     {0, 38, 50},     {0, 0, 17},
+    {0, 155, 167},   {0, 138, 149},   {0, 124, 134},   {0, 109, 117},
+    {0, 93, 100},    {0, 76, 83},     {0, 60, 65},     {0, 38, 43},
+    {0, 0, 9},       {0, 153, 147},   {0, 138, 134},   {0, 124, 120},
+    {0, 107, 103},   {0, 92, 88},     {0, 77, 73},     {0, 60, 56},
+    {0, 38, 35},     {0, 0, 0},       {0, 153, 131},   {0, 137, 118},
+    {0, 122, 105},   {0, 107, 90},    {0, 91, 76},     {0, 76, 63},
+    {0, 60, 47},     {0, 39, 28},     {0, 0, 0},       {0, 153, 111},
+    {0, 136, 100},   {0, 123, 90},    {0, 107, 77},    {0, 92, 65},
+    {0, 76, 52},     {0, 60, 37},     {0, 38, 18},     {0, 0, 0},
+    {0, 152, 93},    {0, 136, 82},    {0, 122, 74},    {0, 106, 63},
+    {0, 91, 52},     {0, 76, 40},     {0, 59, 26},     {0, 38, 6},
+    {0, 0, 0},       {0, 151, 78},    {0, 136, 69},    {0, 121, 61},
+    {0, 106, 52},    {0, 91, 43},     {0, 76, 32},     {0, 59, 17},
+    {0, 38, 0},      {0, 0, 0},       {0, 143, 213},   {0, 128, 191},
+    {0, 115, 171},   {0, 100, 149},   {0, 84, 128},    {0, 69, 108},
+    {0, 52, 86},     {0, 30, 61},     {0, 0, 25},      {0, 142, 193},
+    {0, 127, 173},   {0, 114, 154},   {0, 99, 134},    {0, 84, 116},
+    {0, 69, 98},     {0, 52, 77},     {0, 31, 53},     {0, 0, 18},
+    {0, 141, 176},   {0, 127, 158},   {0, 114, 141},   {0, 98, 122},
+    {0, 84, 105},    {0, 69, 88},     {0, 53, 69},     {0, 31, 46},
+    {0, 0, 9},       {0, 141, 159},   {0, 126, 142},   {0, 113, 127},
+    {0, 98, 110},    {0, 83, 95},     {0, 69, 78},     {0, 53, 60},
+    {0, 32, 39},     {0, 0, 2},       {0, 140, 140},   {0, 126, 126},
+    {0, 112, 112},   {0, 98, 98},     {0, 83, 83},     {0, 68, 69},
+    {0, 52, 52},     {0, 31, 31},     {0, 0, 0},       {0, 140, 124},
+    {0, 125, 112},   {0, 112, 100},   {0, 97, 86},     {0, 83, 72},
+    {0, 68, 59},     {0, 52, 44},     {0, 31, 23},     {0, 0, 0},
+    {0, 139, 106},   {0, 125, 96},    {0, 111, 85},    {0, 97, 72},
+    {0, 83, 62},     {0, 68, 49},     {0, 52, 35},     {0, 31, 15},
+    {0, 0, 0},       {0, 138, 88},    {0, 124, 79},    {0, 111, 70},
+    {0, 96, 59},     {0, 82, 48},     {0, 68, 38},     {0, 52, 24},
+    {0, 31, 4},      {0, 0, 0},       {0, 139, 76},    {0, 124, 66},
+    {0, 111, 58},    {0, 96, 50},     {0, 82, 40},     {0, 68, 29},
+    {0, 52, 15},     {0, 31, 0},      {0, 0, 0},       {0, 129, 200},
+    {0, 114, 179},   {0, 102, 160},   {0, 87, 139},    {0, 74, 120},
+    {0, 60, 101},    {0, 44, 81},     {0, 22, 56},     {0, 0, 19},
+    {0, 127, 181},   {0, 114, 163},   {0, 102, 146},   {0, 88, 127},
+    {0, 74, 109},    {0, 60, 91},     {0, 44, 72},     {0, 23, 48},
+    {0, 0, 11},      {0, 127, 166},   {0, 113, 148},   {0, 101, 133},
+    {0, 87, 115},    {0, 74, 99},     {0, 60, 82},     {0, 44, 64},
+    {0, 23, 42},     {0, 0, 4},       {0, 127, 150},   {0, 113, 134},
+    {0, 101, 119},   {0, 87, 104},    {0, 74, 89},     {0, 60, 73},
+    {0, 44, 56},     {0, 23, 35},     {0, 0, 0},       {0, 125, 134},
+    {0, 112, 118},   {0, 100, 106},   {0, 87, 92},     {0, 73, 78},
+    {0, 60, 64},     {0, 44, 48},     {0, 23, 27},     {0, 0, 0},
+    {0, 125, 118},   {0, 112, 105},   {0, 100, 94},    {0, 86, 80},
+    {0, 73, 68},     {0, 60, 54},     {0, 44, 39},     {0, 23, 20},
+    {0, 0, 0},       {0, 125, 101},   {0, 111, 90},    {0, 99, 80},
+    {0, 86, 69},     {0, 73, 58},     {0, 59, 45},     {0, 44, 30},
+    {0, 23, 11},     {0, 0, 0},       {0, 124, 85},    {0, 111, 75},
+    {0, 99, 66},     {0, 86, 56},     {0, 73, 45},     {0, 59, 34},
+    {0, 44, 20},     {0, 23, 1},      {0, 0, 0},       {0, 125, 72},
+    {0, 111, 62},    {0, 99, 56},     {0, 86, 46},     {0, 73, 36},
+    {0, 60, 26},     {0, 44, 12},     {0, 23, 0},      {0, 0, 0},
+    {0, 114, 188},   {0, 101, 167},   {0, 89, 150},    {0, 77, 131},
+    {0, 64, 113},    {0, 50, 95},     {0, 34, 75},     {0, 12, 52},
+    {0, 0, 14},      {0, 113, 170},   {0, 101, 153},   {0, 89, 137},
+    {0, 77, 120},    {0, 64, 102},    {0, 50, 85},     {0, 35, 67},
+    {0, 12, 44},     {0, 0, 4},       {0, 113, 156},   {0, 100, 139},
+    {0, 89, 125},    {0, 77, 109},    {0, 64, 92},     {0, 51, 77},
+    {0, 35, 60},     {0, 12, 38},     {0, 0, 0},       {0, 112, 141},
+    {0, 100, 126},   {0, 89, 113},    {0, 77, 98},     {0, 64, 83},
+    {0, 51, 68},     {0, 35, 51},     {0, 12, 30},     {0, 0, 0},
+    {0, 112, 127},   {0, 100, 112},   {0, 89, 100},    {0, 76, 87},
+    {0, 64, 74},     {0, 51, 59},     {0, 35, 44},     {0, 13, 24},
+    {0, 0, 0},       {0, 112, 111},   {0, 100, 100},   {0, 88, 88},
+    {0, 76, 76},     {0, 64, 64},     {0, 51, 52},     {0, 36, 37},
+    {0, 13, 17},     {0, 0, 0},       {0, 111, 96},    {0, 99, 85},
+    {0, 88, 76},     {0, 76, 64},     {0, 64, 53},     {0, 51, 41},
+    {0, 36, 27},     {0, 13, 6},      {0, 0, 0},       {0, 111, 81},
+    {0, 99, 71},     {0, 88, 62},     {0, 76, 52},     {0, 64, 43},
+    {0, 51, 31},     {0, 36, 17},     {0, 13, 0},      {0, 0, 0},
+    {0, 111, 69},    {0, 99, 60},     {0, 88, 52},     {0, 75, 43},
+    {0, 63, 34},     {0, 51, 21},     {0, 36, 7},      {0, 13, 0},
+    {0, 0, 0},       {0, 99, 177},    {0, 88, 158},    {0, 77, 141},
+    {0, 66, 123},    {0, 53, 106},    {0, 40, 89},     {0, 25, 71},
+    {0, 5, 47},      {0, 0, 8},       {0, 99, 160},    {0, 88, 144},
+    {0, 77, 129},    {0, 66, 112},    {0, 54, 97},     {0, 41, 80},
+    {0, 26, 62},     {0, 5, 40},      {0, 0, 0},       {0, 99, 147},
+    {0, 87, 132},    {0, 78, 117},    {0, 66, 102},    {0, 54, 87},
+    {0, 42, 72},     {0, 26, 55},     {0, 5, 34},      {0, 0, 0},
+    {0, 99, 134},    {0, 88, 119},    {0, 77, 107},    {0, 66, 92},
+    {0, 54, 78},     {0, 42, 64},     {0, 27, 48},     {0, 5, 27},
+    {0, 0, 0},       {0, 99, 120},    {0, 87, 107},    {0, 78, 94},
+    {0, 66, 81},     {0, 54, 68},     {0, 42, 55},     {0, 27, 40},
+    {0, 6, 20},      {0, 0, 0},       {0, 98, 105},    {0, 87, 94},
+    {0, 77, 84},     {0, 65, 71},     {0, 55, 59},     {0, 42, 47},
+    {0, 28, 33},     {0, 6, 12},      {0, 0, 0},       {0, 98, 93},
+    {0, 87, 81},     {0, 77, 72},     {0, 66, 61},     {0, 54, 49},
+    {0, 42, 37},     {0, 28, 24},     {0, 6, 4},       {0, 0, 0},
+    {0, 98, 77},     {0, 87, 68},     {0, 77, 59},     {0, 65, 49},
+    {0, 54, 39},     {0, 42, 27},     {0, 29, 14},     {0, 6, 0},
+    {0, 0, 0},       {1, 98, 65},     {7, 87, 56},     {0, 77, 49},
+    {0, 66, 41},     {0, 54, 30},     {0, 42, 19},     {0, 29, 3},
+    {0, 6, 0},       {0, 0, 0},       {0, 84, 166},    {0, 74, 149},
+    {0, 64, 134},    {0, 53, 117},    {0, 41, 100},    {0, 28, 83},
+    {0, 11, 64},     {0, 0, 42},      {0, 0, 3},       {0, 84, 151},
+    {0, 74, 135},    {0, 64, 121},    {0, 53, 105},    {0, 42, 90},
+    {0, 30, 75},     {0, 14, 58},     {0, 0, 36},      {0, 0, 0},
+    {0, 84, 138},    {0, 74, 124},    {1, 64, 110},    {0, 54, 95},
+    {0, 43, 81},     {0, 30, 67},     {0, 15, 51},     {0, 1, 29},
+    {0, 0, 0},       {14, 84, 126},   {12, 74, 112},   {2, 65, 99},
+    {0, 54, 85},     {0, 44, 73},     {0, 31, 59},     {0, 16, 44},
+    {0, 1, 23},      {0, 0, 0},       {16, 84, 113},   {13, 74, 100},
+    {6, 65, 89},     {0, 54, 77},     {0, 44, 65},     {0, 31, 51},
+    {0, 17, 36},     {0, 1, 16},      {0, 0, 0},       {24, 84, 100},
+    {18, 74, 88},    {13, 65, 78},    {2, 55, 68},     {0, 44, 55},
+    {0, 32, 43},     {0, 18, 28},     {0, 1, 6},       {0, 0, 0},
+    {26, 84, 87},    {24, 74, 76},    {17, 65, 67},    {7, 54, 57},
+    {0, 44, 46},     {0, 32, 35},     {0, 19, 21},     {0, 2, 3},
+    {0, 0, 0},       {30, 84, 74},    {28, 74, 64},    {20, 65, 55},
+    {12, 55, 46},    {0, 44, 35},     {0, 32, 24},     {0, 18, 9},
+    {0, 1, 0},       {0, 0, 0},       {32, 84, 63},    {28, 74, 54},
+    {21, 65, 47},    {13, 54, 38},    {0, 44, 28},     {0, 32, 16},
+    {0, 18, 1},      {0, 1, 0},       {0, 0, 0},       {30, 67, 155},
+    {20, 58, 139},   {20, 49, 126},   {12, 39, 110},   {0, 27, 94},
+    {0, 13, 77},     {0, 0, 60},      {0, 0, 37},      {0, 0, 0},
+    {35, 67, 142},   {30, 58, 126},   {23, 50, 114},   {16, 40, 99},
+    {7, 29, 85},     {0, 15, 69},     {0, 0, 52},      {0, 0, 30},
+    {0, 0, 0},       {35, 68, 131},   {30, 59, 116},   {27, 50, 104},
+    {18, 40, 90},    {9, 29, 76},     {0, 17, 62},     {0, 2, 46},
+    {0, 0, 24},      {0, 0, 0},       {37, 69, 119},   {33, 59, 106},
+    {27, 51, 94},    {21, 41, 80},    {9, 30, 67},     {0, 18, 54},
+    {0, 3, 39},      {0, 0, 18},      {0, 0, 0},       {40, 69, 107},
+    {36, 59, 94},    {28, 51, 84},    {18, 41, 72},    {10, 31, 60},
+    {0, 19, 47},     {0, 4, 32},      {0, 0, 10},      {0, 0, 0},
+    {42, 69, 95},    {36, 59, 84},    {29, 51, 74},    {19, 41, 63},
+    {10, 31, 52},    {0, 20, 39},     {0, 4, 25},      {0, 0, 4},
+    {0, 0, 0},       {43, 69, 83},    {38, 60, 73},    {32, 51, 62},
+    {23, 42, 53},    {11, 31, 42},    {0, 20, 31},     {0, 5, 17},
+    {0, 0, 1},       {0, 0, 0},       {45, 69, 70},    {39, 60, 60},
+    {33, 51, 52},    {24, 42, 43},    {13, 32, 33},    {0, 21, 21},
+    {0, 5, 6},       {0, 0, 0},       {0, 0, 0},       {47, 69, 59},
+    {41, 60, 51},    {34, 51, 43},    {24, 42, 35},    {12, 33, 26},
+    {1, 22, 14},     {0, 5, 1},       {0, 0, 0},       {0, 0, 0},
+    {46, 48, 146},   {42, 40, 131},   {36, 32, 118},   {27, 22, 103},
+    {17, 6, 88},     {5, 0, 73},      {0, 0, 55},      {0, 0, 33},
+    {0, 0, 0},       {48, 48, 133},   {44, 40, 119},   {37, 32, 107},
+    {28, 22, 93},    {20, 8, 79},     {7, 0, 65},      {0, 0, 49},
+    {0, 0, 27},      {0, 0, 0},       {48, 50, 123},   {44, 41, 109},
+    {37, 33, 97},    {30, 23, 83},    {21, 11, 71},    {8, 0, 58},
+    {0, 0, 42},      {0, 0, 21},      {0, 0, 0},       {49, 51, 111},
+    {45, 42, 99},    {38, 34, 87},    {29, 25, 75},    {20, 13, 63},
+    {8, 0, 51},      {0, 0, 36},      {0, 0, 14},      {0, 0, 0},
+    {52, 52, 100},   {44, 43, 89},    {38, 35, 79},    {29, 26, 68},
+    {19, 15, 56},    {10, 1, 43},     {0, 0, 28},      {0, 0, 6},
+    {0, 0, 0},       {52, 52, 90},    {47, 44, 79},    {39, 36, 70},
+    {30, 27, 59},    {20, 16, 47},    {9, 2, 36},      {0, 0, 22},
+    {0, 0, 2},       {0, 0, 0},       {52, 53, 78},    {46, 44, 68},
+    {39, 37, 60},    {32, 27, 49},    {22, 17, 39},    {10, 3, 28},
+    {0, 0, 12},      {0, 0, 0},       {0, 0, 0},       {53, 53, 66},
+    {47, 44, 57},    {40, 36, 48},    {32, 27, 39},    {22, 18, 30},
+    {9, 4, 18},      {0, 0, 3},       {0, 0, 0},       {0, 0, 0},
+    {54, 53, 57},    {48, 45, 49},    {41, 37, 41},    {33, 28, 32},
+    {22, 19, 23},    {11, 6, 10},     {1, 0, 0},       {0, 0, 0},
+    {0, 0, 0},
+};
+
+}  // namespace
+
+std::tuple<uint8_t, uint8_t, uint8_t> AdobeCMYK_to_sRGB1(uint8_t c,
+                                                         uint8_t m,
+                                                         uint8_t y,
+                                                         uint8_t k) {
+  int fix_c = c << 8;
+  int fix_m = m << 8;
+  int fix_y = y << 8;
+  int fix_k = k << 8;
+  int c_index = (fix_c + 4096) >> 13;
+  int m_index = (fix_m + 4096) >> 13;
+  int y_index = (fix_y + 4096) >> 13;
+  int k_index = (fix_k + 4096) >> 13;
+  const int pos = c_index * 9 * 9 * 9 + m_index * 9 * 9 + y_index * 9 + k_index;
+  int fix_r = kCMYK[pos][0] << 8;
+  int fix_g = kCMYK[pos][1] << 8;
+  int fix_b = kCMYK[pos][2] << 8;
+  int c1_index = fix_c >> 13;
+  if (c1_index == c_index)
+    c1_index = c1_index == 8 ? c1_index - 1 : c1_index + 1;
+  int m1_index = fix_m >> 13;
+  if (m1_index == m_index)
+    m1_index = m1_index == 8 ? m1_index - 1 : m1_index + 1;
+  int y1_index = fix_y >> 13;
+  if (y1_index == y_index)
+    y1_index = y1_index == 8 ? y1_index - 1 : y1_index + 1;
+  int k1_index = fix_k >> 13;
+  if (k1_index == k_index)
+    k1_index = k1_index == 8 ? k1_index - 1 : k1_index + 1;
+
+  const int c1_pos = pos + (c1_index - c_index) * 9 * 9 * 9;
+  const int c_rate = (fix_c - (c_index << 13)) * (c_index - c1_index);
+  fix_r += (kCMYK[pos][0] - kCMYK[c1_pos][0]) * c_rate / 32;
+  fix_g += (kCMYK[pos][1] - kCMYK[c1_pos][1]) * c_rate / 32;
+  fix_b += (kCMYK[pos][2] - kCMYK[c1_pos][2]) * c_rate / 32;
+
+  const int m1_pos = pos + (m1_index - m_index) * 9 * 9;
+  const int m_rate = (fix_m - (m_index << 13)) * (m_index - m1_index);
+  fix_r += (kCMYK[pos][0] - kCMYK[m1_pos][0]) * m_rate / 32;
+  fix_g += (kCMYK[pos][1] - kCMYK[m1_pos][1]) * m_rate / 32;
+  fix_b += (kCMYK[pos][2] - kCMYK[m1_pos][2]) * m_rate / 32;
+
+  const int y1_pos = pos + (y1_index - y_index) * 9;
+  const int y_rate = (fix_y - (y_index << 13)) * (y_index - y1_index);
+  fix_r += (kCMYK[pos][0] - kCMYK[y1_pos][0]) * y_rate / 32;
+  fix_g += (kCMYK[pos][1] - kCMYK[y1_pos][1]) * y_rate / 32;
+  fix_b += (kCMYK[pos][2] - kCMYK[y1_pos][2]) * y_rate / 32;
+
+  const int k1_pos = pos + (k1_index - k_index);
+  const int k_rate = (fix_k - (k_index << 13)) * (k_index - k1_index);
+  fix_r += (kCMYK[pos][0] - kCMYK[k1_pos][0]) * k_rate / 32;
+  fix_g += (kCMYK[pos][1] - kCMYK[k1_pos][1]) * k_rate / 32;
+  fix_b += (kCMYK[pos][2] - kCMYK[k1_pos][2]) * k_rate / 32;
+
+  fix_r = std::max(fix_r, 0);
+  fix_g = std::max(fix_g, 0);
+  fix_b = std::max(fix_b, 0);
+
+  return std::make_tuple(fix_r >> 8, fix_g >> 8, fix_b >> 8);
+}
+
+std::tuple<float, float, float> AdobeCMYK_to_sRGB(float c,
+                                                  float m,
+                                                  float y,
+                                                  float k) {
+  // Convert to uint8_t with round-to-nearest. Avoid using FXSYS_roundf because
+  // it is incredibly expensive with VC++ (tested on VC++ 2015) because round()
+  // is very expensive.
+  // The 'magic' value of 0.49999997f, the float that precedes 0.5f, was chosen
+  // because it gives identical results to FXSYS_roundf(). Using the constant
+  // 0.5f gives different results (1 instead of 0) for one value, 0.0019607842.
+  // That value is close to the cusp but zero is the correct answer, and
+  // getting the same answer as before is desirable.
+  // All floats from 0.0 to 1.0 were tested and now give the same results.
+  const float rounding_offset = 0.49999997f;
+  uint8_t c1 = static_cast<int>(c * 255.f + rounding_offset);
+  uint8_t m1 = static_cast<int>(m * 255.f + rounding_offset);
+  uint8_t y1 = static_cast<int>(y * 255.f + rounding_offset);
+  uint8_t k1 = static_cast<int>(k * 255.f + rounding_offset);
+
+  ASSERT(c1 == FXSYS_roundf(c * 255));
+  ASSERT(m1 == FXSYS_roundf(m * 255));
+  ASSERT(y1 == FXSYS_roundf(y * 255));
+  ASSERT(k1 == FXSYS_roundf(k * 255));
+
+  uint8_t r;
+  uint8_t g;
+  uint8_t b;
+  std::tie(r, g, b) = AdobeCMYK_to_sRGB1(c1, m1, y1, k1);
+  // Multiply by a constant rather than dividing because division is much
+  // more expensive.
+  return std::make_tuple(r * (1.0f / 255), g * (1.0f / 255), b * (1.0f / 255));
+}
+
+}  // namespace fxge
diff --git a/core/fxge/dib/cfx_cmyk_to_srgb.h b/core/fxge/dib/cfx_cmyk_to_srgb.h
new file mode 100644
index 0000000..aebd27a
--- /dev/null
+++ b/core/fxge/dib/cfx_cmyk_to_srgb.h
@@ -0,0 +1,30 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_DIB_CFX_CMYK_TO_SRGB_H_
+#define CORE_FXGE_DIB_CFX_CMYK_TO_SRGB_H_
+
+#include <stdint.h>
+
+#include <tuple>
+
+namespace fxge {
+
+std::tuple<float, float, float> AdobeCMYK_to_sRGB(float c,
+                                                  float m,
+                                                  float y,
+                                                  float k);
+std::tuple<uint8_t, uint8_t, uint8_t> AdobeCMYK_to_sRGB1(uint8_t c,
+                                                         uint8_t m,
+                                                         uint8_t y,
+                                                         uint8_t k);
+
+}  // namespace fxge
+
+using fxge::AdobeCMYK_to_sRGB;
+using fxge::AdobeCMYK_to_sRGB1;
+
+#endif  // CORE_FXGE_DIB_CFX_CMYK_TO_SRGB_H_
diff --git a/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp b/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp
new file mode 100644
index 0000000..e1c0ffd
--- /dev/null
+++ b/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp
@@ -0,0 +1,32 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+union Float_t {
+  Float_t(float num = 0.0f) : f(num) {}
+
+  int32_t i;
+  float f;
+};
+
+TEST(fxge, CMYK_Rounding) {
+  // Testing all floats from 0.0 to 1.0 takes about 35 seconds in release
+  // builds and much longer in debug builds, so just test the known-dangerous
+  // range.
+  constexpr float kStartValue = 0.001f;
+  constexpr float kEndValue = 0.003f;
+  float R = 0.0f;
+  float G = 0.0f;
+  float B = 0.0f;
+  // Iterate through floats by incrementing the representation, as discussed in
+  // https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/
+  for (Float_t f = kStartValue; f.f < kEndValue; f.i++) {
+    std::tie(R, G, B) = AdobeCMYK_to_sRGB(f.f, f.f, f.f, f.f);
+  }
+  // Check various other 'special' numbers.
+  std::tie(R, G, B) = AdobeCMYK_to_sRGB(0.0f, 0.25f, 0.5f, 1.0f);
+}
diff --git a/core/fxge/dib/cfx_dibbase.cpp b/core/fxge/dib/cfx_dibbase.cpp
new file mode 100644
index 0000000..4a4223a
--- /dev/null
+++ b/core/fxge/dib/cfx_dibbase.cpp
@@ -0,0 +1,1295 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/dib/cfx_dibbase.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxge/cfx_cliprgn.h"
+#include "core/fxge/dib/cfx_bitmapstorer.h"
+#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/cfx_imagestretcher.h"
+#include "core/fxge/dib/cfx_imagetransformer.h"
+#include "third_party/base/logging.h"
+
+namespace {
+
+void ColorDecode(uint32_t pal_v, uint8_t* r, uint8_t* g, uint8_t* b) {
+  *r = static_cast<uint8_t>((pal_v & 0xf00) >> 4);
+  *g = static_cast<uint8_t>(pal_v & 0x0f0);
+  *b = static_cast<uint8_t>((pal_v & 0x00f) << 4);
+}
+
+void Obtain_Pal(std::pair<uint32_t, uint32_t>* luts,
+                uint32_t* dest_pal,
+                uint32_t lut) {
+  uint32_t lut_1 = lut - 1;
+  for (int row = 0; row < 256; ++row) {
+    int lut_offset = lut_1 - row;
+    if (lut_offset < 0)
+      lut_offset += 256;
+    uint32_t color = luts[lut_offset].second;
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    ColorDecode(color, &r, &g, &b);
+    dest_pal[row] = (static_cast<uint32_t>(r) << 16) |
+                    (static_cast<uint32_t>(g) << 8) | b | 0xff000000;
+    luts[lut_offset].first = row;
+  }
+}
+
+class CFX_Palette {
+ public:
+  explicit CFX_Palette(const RetainPtr<CFX_DIBBase>& pBitmap);
+  ~CFX_Palette();
+
+  const uint32_t* GetPalette() { return m_Palette.data(); }
+  const std::pair<uint32_t, uint32_t>* GetLuts() const { return m_Luts.data(); }
+  int32_t GetLutCount() const { return m_lut; }
+  void SetAmountLut(int row, uint32_t value) { m_Luts[row].first = value; }
+
+ private:
+  std::vector<uint32_t> m_Palette;
+  // (Amount, Color) pairs
+  std::vector<std::pair<uint32_t, uint32_t>> m_Luts;
+  int m_lut;
+};
+
+CFX_Palette::CFX_Palette(const RetainPtr<CFX_DIBBase>& pBitmap)
+    : m_Palette(256), m_Luts(4096), m_lut(0) {
+  int bpp = pBitmap->GetBPP() / 8;
+  int width = pBitmap->GetWidth();
+  int height = pBitmap->GetHeight();
+  for (int row = 0; row < height; ++row) {
+    const uint8_t* scan_line = pBitmap->GetScanline(row);
+    for (int col = 0; col < width; ++col) {
+      const uint8_t* src_port = scan_line + col * bpp;
+      uint32_t b = src_port[0] & 0xf0;
+      uint32_t g = src_port[1] & 0xf0;
+      uint32_t r = src_port[2] & 0xf0;
+      uint32_t index = (r << 4) + g + (b >> 4);
+      ++m_Luts[index].first;
+    }
+  }
+  // Move non-zeros to the front and count them
+  for (int row = 0; row < 4096; ++row) {
+    if (m_Luts[row].first != 0) {
+      m_Luts[m_lut].first = m_Luts[row].first;
+      m_Luts[m_lut].second = row;
+      ++m_lut;
+    }
+  }
+  std::sort(m_Luts.begin(), m_Luts.begin() + m_lut,
+            [](const std::pair<uint32_t, uint32_t>& arg1,
+               const std::pair<uint32_t, uint32_t>& arg2) {
+              return arg1.first < arg2.first;
+            });
+  Obtain_Pal(m_Luts.data(), m_Palette.data(), m_lut);
+}
+
+CFX_Palette::~CFX_Palette() {}
+
+void ConvertBuffer_1bppMask2Gray(uint8_t* dest_buf,
+                                 int dest_pitch,
+                                 int width,
+                                 int height,
+                                 const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                 int src_left,
+                                 int src_top) {
+  static constexpr uint8_t kSetGray = 0xff;
+  static constexpr uint8_t kResetGray = 0x00;
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    memset(dest_scan, kResetGray, width);
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    for (int col = src_left; col < src_left + width; ++col) {
+      if (src_scan[col / 8] & (1 << (7 - col % 8)))
+        *dest_scan = kSetGray;
+      ++dest_scan;
+    }
+  }
+}
+
+void ConvertBuffer_8bppMask2Gray(uint8_t* dest_buf,
+                                 int dest_pitch,
+                                 int width,
+                                 int height,
+                                 const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                 int src_left,
+                                 int src_top) {
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
+    memcpy(dest_scan, src_scan, width);
+  }
+}
+
+void ConvertBuffer_1bppPlt2Gray(uint8_t* dest_buf,
+                                int dest_pitch,
+                                int width,
+                                int height,
+                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                int src_left,
+                                int src_top) {
+  uint32_t* src_plt = pSrcBitmap->GetPalette();
+  uint8_t gray[2];
+  uint8_t reset_r;
+  uint8_t reset_g;
+  uint8_t reset_b;
+  uint8_t set_r;
+  uint8_t set_g;
+  uint8_t set_b;
+  if (pSrcBitmap->IsCmykImage()) {
+    std::tie(reset_r, reset_g, reset_b) = AdobeCMYK_to_sRGB1(
+        FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]),
+        FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0]));
+    std::tie(set_r, set_g, set_b) = AdobeCMYK_to_sRGB1(
+        FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]),
+        FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1]));
+  } else {
+    reset_r = FXARGB_R(src_plt[0]);
+    reset_g = FXARGB_G(src_plt[0]);
+    reset_b = FXARGB_B(src_plt[0]);
+    set_r = FXARGB_R(src_plt[1]);
+    set_g = FXARGB_G(src_plt[1]);
+    set_b = FXARGB_B(src_plt[1]);
+  }
+  gray[0] = FXRGB2GRAY(reset_r, reset_g, reset_b);
+  gray[1] = FXRGB2GRAY(set_r, set_g, set_b);
+
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    memset(dest_scan, gray[0], width);
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    for (int col = src_left; col < src_left + width; ++col) {
+      if (src_scan[col / 8] & (1 << (7 - col % 8)))
+        *dest_scan = gray[1];
+      ++dest_scan;
+    }
+  }
+}
+
+void ConvertBuffer_8bppPlt2Gray(uint8_t* dest_buf,
+                                int dest_pitch,
+                                int width,
+                                int height,
+                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                int src_left,
+                                int src_top) {
+  uint32_t* src_plt = pSrcBitmap->GetPalette();
+  uint8_t gray[256];
+  if (pSrcBitmap->IsCmykImage()) {
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    for (size_t i = 0; i < FX_ArraySize(gray); ++i) {
+      std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
+          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
+          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
+      gray[i] = FXRGB2GRAY(r, g, b);
+    }
+  } else {
+    for (size_t i = 0; i < FX_ArraySize(gray); ++i) {
+      gray[i] = FXRGB2GRAY(FXARGB_R(src_plt[i]), FXARGB_G(src_plt[i]),
+                           FXARGB_B(src_plt[i]));
+    }
+  }
+
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
+    for (int col = 0; col < width; ++col)
+      *dest_scan++ = gray[*src_scan++];
+  }
+}
+
+void ConvertBuffer_RgbOrCmyk2Gray(uint8_t* dest_buf,
+                                  int dest_pitch,
+                                  int width,
+                                  int height,
+                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                  int src_left,
+                                  int src_top) {
+  int Bpp = pSrcBitmap->GetBPP() / 8;
+  if (pSrcBitmap->IsCmykImage()) {
+    for (int row = 0; row < height; ++row) {
+      uint8_t* dest_scan = dest_buf + row * dest_pitch;
+      const uint8_t* src_scan =
+          pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+      for (int col = 0; col < width; ++col) {
+        uint8_t r;
+        uint8_t g;
+        uint8_t b;
+        std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
+            FXSYS_GetCValue(static_cast<uint32_t>(src_scan[0])),
+            FXSYS_GetMValue(static_cast<uint32_t>(src_scan[1])),
+            FXSYS_GetYValue(static_cast<uint32_t>(src_scan[2])),
+            FXSYS_GetKValue(static_cast<uint32_t>(src_scan[3])));
+        *dest_scan++ = FXRGB2GRAY(r, g, b);
+        src_scan += 4;
+      }
+    }
+  } else {
+    for (int row = 0; row < height; ++row) {
+      uint8_t* dest_scan = dest_buf + row * dest_pitch;
+      const uint8_t* src_scan =
+          pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
+      for (int col = 0; col < width; ++col) {
+        *dest_scan++ = FXRGB2GRAY(src_scan[2], src_scan[1], src_scan[0]);
+        src_scan += Bpp;
+      }
+    }
+  }
+}
+
+void ConvertBuffer_IndexCopy(uint8_t* dest_buf,
+                             int dest_pitch,
+                             int width,
+                             int height,
+                             const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                             int src_left,
+                             int src_top) {
+  if (pSrcBitmap->GetBPP() == 1) {
+    for (int row = 0; row < height; ++row) {
+      uint8_t* dest_scan = dest_buf + row * dest_pitch;
+      // Set all destination pixels to be white initially.
+      memset(dest_scan, 255, width);
+      const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+      for (int col = src_left; col < src_left + width; ++col) {
+        // If the source bit is set, then set the destination pixel to be black.
+        if (src_scan[col / 8] & (1 << (7 - col % 8)))
+          *dest_scan = 0;
+
+        ++dest_scan;
+      }
+    }
+  } else {
+    for (int row = 0; row < height; ++row) {
+      uint8_t* dest_scan = dest_buf + row * dest_pitch;
+      const uint8_t* src_scan =
+          pSrcBitmap->GetScanline(src_top + row) + src_left;
+      memcpy(dest_scan, src_scan, width);
+    }
+  }
+}
+
+void ConvertBuffer_Plt2PltRgb8(uint8_t* dest_buf,
+                               int dest_pitch,
+                               int width,
+                               int height,
+                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               int src_left,
+                               int src_top,
+                               uint32_t* dst_plt) {
+  ConvertBuffer_IndexCopy(dest_buf, dest_pitch, width, height, pSrcBitmap,
+                          src_left, src_top);
+  uint32_t* src_plt = pSrcBitmap->GetPalette();
+  size_t plt_size = pSrcBitmap->GetPaletteSize();
+  if (pSrcBitmap->IsCmykImage()) {
+    for (size_t i = 0; i < plt_size; ++i) {
+      uint8_t r;
+      uint8_t g;
+      uint8_t b;
+      std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
+          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
+          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
+      dst_plt[i] = ArgbEncode(0xff, r, g, b);
+    }
+  } else {
+    memcpy(dst_plt, src_plt, plt_size * 4);
+  }
+}
+
+void ConvertBuffer_Rgb2PltRgb8(uint8_t* dest_buf,
+                               int dest_pitch,
+                               int width,
+                               int height,
+                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               int src_left,
+                               int src_top,
+                               uint32_t* dst_plt) {
+  int bpp = pSrcBitmap->GetBPP() / 8;
+  CFX_Palette palette(pSrcBitmap);
+  const std::pair<uint32_t, uint32_t>* Luts = palette.GetLuts();
+  int lut = palette.GetLutCount();
+  const uint32_t* pal = palette.GetPalette();
+  if (lut > 256) {
+    int err;
+    int min_err;
+    int lut_256 = lut - 256;
+    for (int row = 0; row < lut_256; ++row) {
+      min_err = 1000000;
+      uint8_t r;
+      uint8_t g;
+      uint8_t b;
+      ColorDecode(Luts[row].second, &r, &g, &b);
+      uint32_t clrindex = 0;
+      for (int col = 0; col < 256; ++col) {
+        uint32_t p_color = pal[col];
+        int d_r = r - static_cast<uint8_t>(p_color >> 16);
+        int d_g = g - static_cast<uint8_t>(p_color >> 8);
+        int d_b = b - static_cast<uint8_t>(p_color);
+        err = d_r * d_r + d_g * d_g + d_b * d_b;
+        if (err < min_err) {
+          min_err = err;
+          clrindex = col;
+        }
+      }
+      palette.SetAmountLut(row, clrindex);
+    }
+  }
+  int32_t lut_1 = lut - 1;
+  for (int row = 0; row < height; ++row) {
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    for (int col = 0; col < width; ++col) {
+      const uint8_t* src_port = src_scan + col * bpp;
+      int r = src_port[2] & 0xf0;
+      int g = src_port[1] & 0xf0;
+      int b = src_port[0] & 0xf0;
+      uint32_t clrindex = (r << 4) + g + (b >> 4);
+      for (int i = lut_1; i >= 0; --i)
+        if (clrindex == Luts[i].second) {
+          *(dest_scan + col) = static_cast<uint8_t>(Luts[i].first);
+          break;
+        }
+    }
+  }
+  memcpy(dst_plt, pal, sizeof(uint32_t) * 256);
+}
+
+void ConvertBuffer_1bppMask2Rgb(FXDIB_Format dest_format,
+                                uint8_t* dest_buf,
+                                int dest_pitch,
+                                int width,
+                                int height,
+                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                int src_left,
+                                int src_top) {
+  int comps = GetCompsFromFormat(dest_format);
+  static constexpr uint8_t kSetGray = 0xff;
+  static constexpr uint8_t kResetGray = 0x00;
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    for (int col = src_left; col < src_left + width; ++col) {
+      if (src_scan[col / 8] & (1 << (7 - col % 8))) {
+        dest_scan[0] = kSetGray;
+        dest_scan[1] = kSetGray;
+        dest_scan[2] = kSetGray;
+      } else {
+        dest_scan[0] = kResetGray;
+        dest_scan[1] = kResetGray;
+        dest_scan[2] = kResetGray;
+      }
+      dest_scan += comps;
+    }
+  }
+}
+
+void ConvertBuffer_8bppMask2Rgb(FXDIB_Format dest_format,
+                                uint8_t* dest_buf,
+                                int dest_pitch,
+                                int width,
+                                int height,
+                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                int src_left,
+                                int src_top) {
+  int comps = GetCompsFromFormat(dest_format);
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
+    uint8_t src_pixel;
+    for (int col = 0; col < width; ++col) {
+      src_pixel = *src_scan++;
+      *dest_scan++ = src_pixel;
+      *dest_scan++ = src_pixel;
+      *dest_scan = src_pixel;
+      dest_scan += comps - 2;
+    }
+  }
+}
+
+void ConvertBuffer_1bppPlt2Rgb(FXDIB_Format dest_format,
+                               uint8_t* dest_buf,
+                               int dest_pitch,
+                               int width,
+                               int height,
+                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               int src_left,
+                               int src_top) {
+  int comps = GetCompsFromFormat(dest_format);
+  uint32_t* src_plt = pSrcBitmap->GetPalette();
+  uint32_t plt[2];
+  uint8_t* bgr_ptr = reinterpret_cast<uint8_t*>(plt);
+  if (pSrcBitmap->IsCmykImage()) {
+    plt[0] = FXCMYK_TODIB(src_plt[0]);
+    plt[1] = FXCMYK_TODIB(src_plt[1]);
+  } else {
+    bgr_ptr[0] = FXARGB_B(src_plt[0]);
+    bgr_ptr[1] = FXARGB_G(src_plt[0]);
+    bgr_ptr[2] = FXARGB_R(src_plt[0]);
+    bgr_ptr[3] = FXARGB_B(src_plt[1]);
+    bgr_ptr[4] = FXARGB_G(src_plt[1]);
+    bgr_ptr[5] = FXARGB_R(src_plt[1]);
+  }
+
+  if (pSrcBitmap->IsCmykImage()) {
+    std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1(
+        FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]),
+        FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0]));
+    std::tie(bgr_ptr[5], bgr_ptr[4], bgr_ptr[3]) = AdobeCMYK_to_sRGB1(
+        FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]),
+        FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1]));
+  }
+
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    for (int col = src_left; col < src_left + width; ++col) {
+      if (src_scan[col / 8] & (1 << (7 - col % 8))) {
+        *dest_scan++ = bgr_ptr[3];
+        *dest_scan++ = bgr_ptr[4];
+        *dest_scan = bgr_ptr[5];
+      } else {
+        *dest_scan++ = bgr_ptr[0];
+        *dest_scan++ = bgr_ptr[1];
+        *dest_scan = bgr_ptr[2];
+      }
+      dest_scan += comps - 2;
+    }
+  }
+}
+
+void ConvertBuffer_8bppPlt2Rgb(FXDIB_Format dest_format,
+                               uint8_t* dest_buf,
+                               int dest_pitch,
+                               int width,
+                               int height,
+                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               int src_left,
+                               int src_top) {
+  int comps = GetCompsFromFormat(dest_format);
+  uint32_t* src_plt = pSrcBitmap->GetPalette();
+  uint32_t plt[256];
+  uint8_t* bgr_ptr = reinterpret_cast<uint8_t*>(plt);
+  if (!pSrcBitmap->IsCmykImage()) {
+    for (int i = 0; i < 256; ++i) {
+      *bgr_ptr++ = FXARGB_B(src_plt[i]);
+      *bgr_ptr++ = FXARGB_G(src_plt[i]);
+      *bgr_ptr++ = FXARGB_R(src_plt[i]);
+    }
+    bgr_ptr = reinterpret_cast<uint8_t*>(plt);
+  }
+
+  if (pSrcBitmap->IsCmykImage()) {
+    for (int i = 0; i < 256; ++i) {
+      std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1(
+          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
+          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
+      bgr_ptr += 3;
+    }
+    bgr_ptr = reinterpret_cast<uint8_t*>(plt);
+  }
+
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
+    for (int col = 0; col < width; ++col) {
+      uint8_t* src_pixel = bgr_ptr + 3 * (*src_scan++);
+      *dest_scan++ = *src_pixel++;
+      *dest_scan++ = *src_pixel++;
+      *dest_scan = *src_pixel++;
+      dest_scan += comps - 2;
+    }
+  }
+}
+
+void ConvertBuffer_24bppRgb2Rgb24(uint8_t* dest_buf,
+                                  int dest_pitch,
+                                  int width,
+                                  int height,
+                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                  int src_left,
+                                  int src_top) {
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
+    memcpy(dest_scan, src_scan, width * 3);
+  }
+}
+
+void ConvertBuffer_32bppRgb2Rgb24(uint8_t* dest_buf,
+                                  int dest_pitch,
+                                  int width,
+                                  int height,
+                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                  int src_left,
+                                  int src_top) {
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+    for (int col = 0; col < width; ++col) {
+      *dest_scan++ = *src_scan++;
+      *dest_scan++ = *src_scan++;
+      *dest_scan++ = *src_scan++;
+      ++src_scan;
+    }
+  }
+}
+
+void ConvertBuffer_Rgb2Rgb32(uint8_t* dest_buf,
+                             int dest_pitch,
+                             int width,
+                             int height,
+                             const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                             int src_left,
+                             int src_top) {
+  int comps = pSrcBitmap->GetBPP() / 8;
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row) + src_left * comps;
+    for (int col = 0; col < width; ++col) {
+      *dest_scan++ = *src_scan++;
+      *dest_scan++ = *src_scan++;
+      *dest_scan++ = *src_scan++;
+      ++dest_scan;
+      src_scan += comps - 3;
+    }
+  }
+}
+
+void ConvertBuffer_32bppCmyk2Rgb32(uint8_t* dest_buf,
+                                   int dest_pitch,
+                                   int width,
+                                   int height,
+                                   const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                   int src_left,
+                                   int src_top) {
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = dest_buf + row * dest_pitch;
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+    for (int col = 0; col < width; ++col) {
+      std::tie(dest_scan[2], dest_scan[1], dest_scan[0]) = AdobeCMYK_to_sRGB1(
+          src_scan[0], src_scan[1], src_scan[2], src_scan[3]);
+      dest_scan += 4;
+      src_scan += 4;
+    }
+  }
+}
+
+bool ConvertBuffer_8bppMask(int bpp,
+                            uint8_t* dest_buf,
+                            int dest_pitch,
+                            int width,
+                            int height,
+                            const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                            int src_left,
+                            int src_top) {
+  switch (bpp) {
+    case 1:
+      if (pSrcBitmap->GetPalette()) {
+        ConvertBuffer_1bppPlt2Gray(dest_buf, dest_pitch, width, height,
+                                   pSrcBitmap, src_left, src_top);
+      } else {
+        ConvertBuffer_1bppMask2Gray(dest_buf, dest_pitch, width, height,
+                                    pSrcBitmap, src_left, src_top);
+      }
+      return true;
+    case 8:
+      if (pSrcBitmap->GetPalette()) {
+        ConvertBuffer_8bppPlt2Gray(dest_buf, dest_pitch, width, height,
+                                   pSrcBitmap, src_left, src_top);
+      } else {
+        ConvertBuffer_8bppMask2Gray(dest_buf, dest_pitch, width, height,
+                                    pSrcBitmap, src_left, src_top);
+      }
+      return true;
+    case 24:
+    case 32:
+      ConvertBuffer_RgbOrCmyk2Gray(dest_buf, dest_pitch, width, height,
+                                   pSrcBitmap, src_left, src_top);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ConvertBuffer_Rgb(int bpp,
+                       FXDIB_Format dest_format,
+                       uint8_t* dest_buf,
+                       int dest_pitch,
+                       int width,
+                       int height,
+                       const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                       int src_left,
+                       int src_top) {
+  switch (bpp) {
+    case 1:
+      if (pSrcBitmap->GetPalette()) {
+        ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                  height, pSrcBitmap, src_left, src_top);
+      } else {
+        ConvertBuffer_1bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                   height, pSrcBitmap, src_left, src_top);
+      }
+      return true;
+    case 8:
+      if (pSrcBitmap->GetPalette()) {
+        ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                  height, pSrcBitmap, src_left, src_top);
+      } else {
+        ConvertBuffer_8bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                   height, pSrcBitmap, src_left, src_top);
+      }
+      return true;
+    case 24:
+      ConvertBuffer_24bppRgb2Rgb24(dest_buf, dest_pitch, width, height,
+                                   pSrcBitmap, src_left, src_top);
+      return true;
+    case 32:
+      ConvertBuffer_32bppRgb2Rgb24(dest_buf, dest_pitch, width, height,
+                                   pSrcBitmap, src_left, src_top);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ConvertBuffer_Argb(int bpp,
+                        bool cmyk,
+                        FXDIB_Format dest_format,
+                        uint8_t* dest_buf,
+                        int dest_pitch,
+                        int width,
+                        int height,
+                        const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                        int src_left,
+                        int src_top) {
+  switch (bpp) {
+    case 1:
+      if (pSrcBitmap->GetPalette()) {
+        ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                  height, pSrcBitmap, src_left, src_top);
+      } else {
+        ConvertBuffer_1bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                   height, pSrcBitmap, src_left, src_top);
+      }
+      return true;
+    case 8:
+      if (pSrcBitmap->GetPalette()) {
+        ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                  height, pSrcBitmap, src_left, src_top);
+      } else {
+        ConvertBuffer_8bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
+                                   height, pSrcBitmap, src_left, src_top);
+      }
+      return true;
+    case 24:
+    case 32:
+      if (cmyk) {
+        ConvertBuffer_32bppCmyk2Rgb32(dest_buf, dest_pitch, width, height,
+                                      pSrcBitmap, src_left, src_top);
+      } else {
+        ConvertBuffer_Rgb2Rgb32(dest_buf, dest_pitch, width, height, pSrcBitmap,
+                                src_left, src_top);
+      }
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace
+
+CFX_DIBBase::CFX_DIBBase()
+    : m_Width(0), m_Height(0), m_bpp(0), m_AlphaFlag(0), m_Pitch(0) {}
+
+CFX_DIBBase::~CFX_DIBBase() {}
+
+uint8_t* CFX_DIBBase::GetBuffer() const {
+  return nullptr;
+}
+
+bool CFX_DIBBase::SkipToScanline(int line, PauseIndicatorIface* pPause) const {
+  return false;
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::Clone(const FX_RECT* pClip) const {
+  FX_RECT rect(0, 0, m_Width, m_Height);
+  if (pClip) {
+    rect.Intersect(*pClip);
+    if (rect.IsEmpty())
+      return nullptr;
+  }
+  auto pNewBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pNewBitmap->Create(rect.Width(), rect.Height(), GetFormat()))
+    return nullptr;
+
+  pNewBitmap->SetPalette(m_pPalette.get());
+  pNewBitmap->SetAlphaMask(m_pAlphaMask, pClip);
+  if (GetBPP() == 1 && rect.left % 8 != 0) {
+    int left_shift = rect.left % 32;
+    int right_shift = 32 - left_shift;
+    int dword_count = pNewBitmap->m_Pitch / 4;
+    for (int row = rect.top; row < rect.bottom; ++row) {
+      const uint32_t* src_scan =
+          reinterpret_cast<const uint32_t*>(GetScanline(row)) + rect.left / 32;
+      uint32_t* dest_scan = reinterpret_cast<uint32_t*>(
+          pNewBitmap->GetWritableScanline(row - rect.top));
+      for (int i = 0; i < dword_count; ++i) {
+        dest_scan[i] =
+            (src_scan[i] << left_shift) | (src_scan[i + 1] >> right_shift);
+      }
+    }
+  } else {
+    int copy_len = (pNewBitmap->GetWidth() * pNewBitmap->GetBPP() + 7) / 8;
+    if (m_Pitch < static_cast<uint32_t>(copy_len))
+      copy_len = m_Pitch;
+
+    for (int row = rect.top; row < rect.bottom; ++row) {
+      const uint8_t* src_scan = GetScanline(row) + rect.left * m_bpp / 8;
+      uint8_t* dest_scan = pNewBitmap->GetWritableScanline(row - rect.top);
+      memcpy(dest_scan, src_scan, copy_len);
+    }
+  }
+  return pNewBitmap;
+}
+
+void CFX_DIBBase::BuildPalette() {
+  if (m_pPalette)
+    return;
+
+  if (GetBPP() == 1) {
+    m_pPalette.reset(FX_Alloc(uint32_t, 2));
+    if (IsCmykImage()) {
+      m_pPalette.get()[0] = 0xff;
+      m_pPalette.get()[1] = 0;
+    } else {
+      m_pPalette.get()[0] = 0xff000000;
+      m_pPalette.get()[1] = 0xffffffff;
+    }
+  } else if (GetBPP() == 8) {
+    m_pPalette.reset(FX_Alloc(uint32_t, 256));
+    if (IsCmykImage()) {
+      for (int i = 0; i < 256; ++i)
+        m_pPalette.get()[i] = 0xff - i;
+    } else {
+      for (int i = 0; i < 256; ++i)
+        m_pPalette.get()[i] = 0xff000000 | (i * 0x10101);
+    }
+  }
+}
+
+bool CFX_DIBBase::BuildAlphaMask() {
+  if (m_pAlphaMask)
+    return true;
+
+  m_pAlphaMask = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!m_pAlphaMask->Create(m_Width, m_Height, FXDIB_8bppMask)) {
+    m_pAlphaMask = nullptr;
+    return false;
+  }
+  memset(m_pAlphaMask->GetBuffer(), 0xff,
+         m_pAlphaMask->GetHeight() * m_pAlphaMask->GetPitch());
+  return true;
+}
+
+size_t CFX_DIBBase::GetPaletteSize() const {
+  if (IsAlphaMask())
+    return 0;
+
+  switch (m_bpp) {
+    case 1:
+      return 2;
+    case 8:
+      return 256;
+    default:
+      return 0;
+  }
+}
+
+uint32_t CFX_DIBBase::GetPaletteArgb(int index) const {
+  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
+  if (m_pPalette)
+    return m_pPalette.get()[index];
+
+  if (IsCmykImage()) {
+    if (GetBPP() == 1)
+      return index ? 0 : 0xff;
+
+    return 0xff - index;
+  }
+  if (GetBPP() == 1)
+    return index ? 0xffffffff : 0xff000000;
+
+  return index * 0x10101 | 0xff000000;
+}
+
+void CFX_DIBBase::SetPaletteArgb(int index, uint32_t color) {
+  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
+  if (!m_pPalette) {
+    BuildPalette();
+  }
+  m_pPalette.get()[index] = color;
+}
+
+int CFX_DIBBase::FindPalette(uint32_t color) const {
+  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
+  if (!m_pPalette) {
+    if (IsCmykImage()) {
+      if (GetBPP() == 1)
+        return (static_cast<uint8_t>(color) == 0xff) ? 0 : 1;
+
+      return 0xff - static_cast<uint8_t>(color);
+    }
+    if (GetBPP() == 1)
+      return (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
+
+    return static_cast<uint8_t>(color);
+  }
+  int palsize = (1 << GetBPP());
+  for (int i = 0; i < palsize; ++i) {
+    if (m_pPalette.get()[i] == color)
+      return i;
+  }
+  return -1;
+}
+
+bool CFX_DIBBase::GetOverlapRect(int& dest_left,
+                                 int& dest_top,
+                                 int& width,
+                                 int& height,
+                                 int src_width,
+                                 int src_height,
+                                 int& src_left,
+                                 int& src_top,
+                                 const CFX_ClipRgn* pClipRgn) {
+  if (width == 0 || height == 0)
+    return false;
+
+  ASSERT(width > 0);
+  ASSERT(height > 0);
+
+  if (dest_left > m_Width || dest_top > m_Height) {
+    width = 0;
+    height = 0;
+    return false;
+  }
+  int x_offset = dest_left - src_left;
+  int y_offset = dest_top - src_top;
+  FX_RECT src_rect(src_left, src_top, src_left + width, src_top + height);
+  FX_RECT src_bound(0, 0, src_width, src_height);
+  src_rect.Intersect(src_bound);
+  FX_RECT dest_rect(src_rect.left + x_offset, src_rect.top + y_offset,
+                    src_rect.right + x_offset, src_rect.bottom + y_offset);
+  FX_RECT dest_bound(0, 0, m_Width, m_Height);
+  dest_rect.Intersect(dest_bound);
+  if (pClipRgn)
+    dest_rect.Intersect(pClipRgn->GetBox());
+  dest_left = dest_rect.left;
+  dest_top = dest_rect.top;
+
+  pdfium::base::CheckedNumeric<int> safe_src_left = dest_left;
+  safe_src_left -= x_offset;
+  if (!safe_src_left.IsValid())
+    return false;
+  src_left = safe_src_left.ValueOrDie();
+
+  pdfium::base::CheckedNumeric<int> safe_src_top = dest_top;
+  safe_src_top -= y_offset;
+  if (!safe_src_top.IsValid())
+    return false;
+  src_top = safe_src_top.ValueOrDie();
+
+  width = dest_rect.right - dest_rect.left;
+  height = dest_rect.bottom - dest_rect.top;
+  return width != 0 && height != 0;
+}
+
+void CFX_DIBBase::SetPalette(const uint32_t* pSrc) {
+  static const uint32_t kPaletteSize = 256;
+  if (!pSrc || GetBPP() > 8) {
+    m_pPalette.reset();
+    return;
+  }
+  uint32_t pal_size = 1 << GetBPP();
+  if (!m_pPalette)
+    m_pPalette.reset(FX_Alloc(uint32_t, pal_size));
+  pal_size = std::min(pal_size, kPaletteSize);
+  memcpy(m_pPalette.get(), pSrc, pal_size * sizeof(uint32_t));
+}
+
+void CFX_DIBBase::GetPalette(uint32_t* pal, int alpha) const {
+  ASSERT(GetBPP() <= 8);
+  ASSERT(!IsCmykImage());
+
+  if (GetBPP() == 1) {
+    pal[0] = ((m_pPalette ? m_pPalette.get()[0] : 0xff000000) & 0xffffff) |
+             (alpha << 24);
+    pal[1] = ((m_pPalette ? m_pPalette.get()[1] : 0xffffffff) & 0xffffff) |
+             (alpha << 24);
+    return;
+  }
+  if (m_pPalette) {
+    for (int i = 0; i < 256; ++i)
+      pal[i] = (m_pPalette.get()[i] & 0x00ffffff) | (alpha << 24);
+  } else {
+    for (int i = 0; i < 256; ++i)
+      pal[i] = (i * 0x10101) | (alpha << 24);
+  }
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::CloneAlphaMask() const {
+  ASSERT(GetFormat() == FXDIB_Argb);
+  FX_RECT rect(0, 0, m_Width, m_Height);
+  auto pMask = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pMask->Create(rect.Width(), rect.Height(), FXDIB_8bppMask))
+    return nullptr;
+
+  for (int row = rect.top; row < rect.bottom; ++row) {
+    const uint8_t* src_scan = GetScanline(row) + rect.left * 4 + 3;
+    uint8_t* dest_scan = pMask->GetWritableScanline(row - rect.top);
+    for (int col = rect.left; col < rect.right; ++col) {
+      *dest_scan++ = *src_scan;
+      src_scan += 4;
+    }
+  }
+  return pMask;
+}
+
+bool CFX_DIBBase::SetAlphaMask(const RetainPtr<CFX_DIBBase>& pAlphaMask,
+                               const FX_RECT* pClip) {
+  if (!HasAlpha() || GetFormat() == FXDIB_Argb)
+    return false;
+
+  if (!pAlphaMask) {
+    m_pAlphaMask->Clear(0xff000000);
+    return true;
+  }
+  FX_RECT rect(0, 0, pAlphaMask->m_Width, pAlphaMask->m_Height);
+  if (pClip) {
+    rect.Intersect(*pClip);
+    if (rect.IsEmpty() || rect.Width() != m_Width ||
+        rect.Height() != m_Height) {
+      return false;
+    }
+  } else {
+    if (pAlphaMask->m_Width != m_Width || pAlphaMask->m_Height != m_Height)
+      return false;
+  }
+  for (int row = 0; row < m_Height; ++row) {
+    memcpy(m_pAlphaMask->GetWritableScanline(row),
+           pAlphaMask->GetScanline(row + rect.top) + rect.left,
+           m_pAlphaMask->m_Pitch);
+  }
+  return true;
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::FlipImage(bool bXFlip, bool bYFlip) const {
+  auto pFlipped = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pFlipped->Create(m_Width, m_Height, GetFormat()))
+    return nullptr;
+
+  pFlipped->SetPalette(m_pPalette.get());
+  uint8_t* pDestBuffer = pFlipped->GetBuffer();
+  int Bpp = m_bpp / 8;
+  for (int row = 0; row < m_Height; ++row) {
+    const uint8_t* src_scan = GetScanline(row);
+    uint8_t* dest_scan =
+        pDestBuffer + m_Pitch * (bYFlip ? (m_Height - row - 1) : row);
+    if (!bXFlip) {
+      memcpy(dest_scan, src_scan, m_Pitch);
+      continue;
+    }
+    if (m_bpp == 1) {
+      memset(dest_scan, 0, m_Pitch);
+      for (int col = 0; col < m_Width; ++col) {
+        if (src_scan[col / 8] & (1 << (7 - col % 8))) {
+          int dest_col = m_Width - col - 1;
+          dest_scan[dest_col / 8] |= (1 << (7 - dest_col % 8));
+        }
+      }
+      continue;
+    }
+
+    dest_scan += (m_Width - 1) * Bpp;
+    if (Bpp == 1) {
+      for (int col = 0; col < m_Width; ++col) {
+        *dest_scan = *src_scan;
+        --dest_scan;
+        ++src_scan;
+      }
+    } else if (Bpp == 3) {
+      for (int col = 0; col < m_Width; ++col) {
+        dest_scan[0] = src_scan[0];
+        dest_scan[1] = src_scan[1];
+        dest_scan[2] = src_scan[2];
+        dest_scan -= 3;
+        src_scan += 3;
+      }
+    } else {
+      ASSERT(Bpp == 4);
+      for (int col = 0; col < m_Width; ++col) {
+        const auto* src_scan32 = reinterpret_cast<const uint32_t*>(src_scan);
+        uint32_t* dest_scan32 = reinterpret_cast<uint32_t*>(dest_scan);
+        *dest_scan32 = *src_scan32;
+        dest_scan -= 4;
+        src_scan += 4;
+      }
+    }
+  }
+  if (m_pAlphaMask) {
+    pDestBuffer = pFlipped->m_pAlphaMask->GetBuffer();
+    uint32_t dest_pitch = pFlipped->m_pAlphaMask->GetPitch();
+    for (int row = 0; row < m_Height; ++row) {
+      const uint8_t* src_scan = m_pAlphaMask->GetScanline(row);
+      uint8_t* dest_scan =
+          pDestBuffer + dest_pitch * (bYFlip ? (m_Height - row - 1) : row);
+      if (!bXFlip) {
+        memcpy(dest_scan, src_scan, dest_pitch);
+        continue;
+      }
+      dest_scan += (m_Width - 1);
+      for (int col = 0; col < m_Width; ++col) {
+        *dest_scan = *src_scan;
+        --dest_scan;
+        ++src_scan;
+      }
+    }
+  }
+  return pFlipped;
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::CloneConvert(FXDIB_Format dest_format) {
+  if (dest_format == GetFormat())
+    return Clone(nullptr);
+
+  auto pClone = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pClone->Create(m_Width, m_Height, dest_format))
+    return nullptr;
+
+  RetainPtr<CFX_DIBitmap> pSrcAlpha;
+  if (HasAlpha()) {
+    pSrcAlpha = (GetFormat() == FXDIB_Argb) ? CloneAlphaMask() : m_pAlphaMask;
+    if (!pSrcAlpha)
+      return nullptr;
+  }
+  if (GetIsAlphaFromFormat(dest_format)) {
+    bool ret;
+    if (dest_format == FXDIB_Argb) {
+      ret = pSrcAlpha ? pClone->LoadChannelFromAlpha(FXDIB_Alpha, pSrcAlpha)
+                      : pClone->LoadChannel(FXDIB_Alpha, 0xff);
+    } else {
+      ret = pClone->SetAlphaMask(pSrcAlpha, nullptr);
+    }
+    if (!ret)
+      return nullptr;
+  }
+
+  RetainPtr<CFX_DIBBase> holder(this);
+  std::unique_ptr<uint32_t, FxFreeDeleter> pal_8bpp;
+  if (!ConvertBuffer(dest_format, pClone->GetBuffer(), pClone->GetPitch(),
+                     m_Width, m_Height, holder, 0, 0, &pal_8bpp)) {
+    return nullptr;
+  }
+  if (pal_8bpp)
+    pClone->SetPalette(pal_8bpp.get());
+
+  return pClone;
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::SwapXY(bool bXFlip, bool bYFlip) const {
+  FX_RECT dest_clip(0, 0, m_Height, m_Width);
+  if (dest_clip.IsEmpty())
+    return nullptr;
+
+  auto pTransBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  int result_height = dest_clip.Height();
+  int result_width = dest_clip.Width();
+  if (!pTransBitmap->Create(result_width, result_height, GetFormat()))
+    return nullptr;
+
+  pTransBitmap->SetPalette(m_pPalette.get());
+  int dest_pitch = pTransBitmap->GetPitch();
+  uint8_t* dest_buf = pTransBitmap->GetBuffer();
+  int row_start = bXFlip ? m_Height - dest_clip.right : dest_clip.left;
+  int row_end = bXFlip ? m_Height - dest_clip.left : dest_clip.right;
+  int col_start = bYFlip ? m_Width - dest_clip.bottom : dest_clip.top;
+  int col_end = bYFlip ? m_Width - dest_clip.top : dest_clip.bottom;
+  if (GetBPP() == 1) {
+    memset(dest_buf, 0xff, dest_pitch * result_height);
+    for (int row = row_start; row < row_end; ++row) {
+      const uint8_t* src_scan = GetScanline(row);
+      int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
+                     dest_clip.left;
+      uint8_t* dest_scan = dest_buf;
+      if (bYFlip)
+        dest_scan += (result_height - 1) * dest_pitch;
+      int dest_step = bYFlip ? -dest_pitch : dest_pitch;
+      for (int col = col_start; col < col_end; ++col) {
+        if (!(src_scan[col / 8] & (1 << (7 - col % 8))))
+          dest_scan[dest_col / 8] &= ~(1 << (7 - dest_col % 8));
+        dest_scan += dest_step;
+      }
+    }
+  } else {
+    int nBytes = GetBPP() / 8;
+    int dest_step = bYFlip ? -dest_pitch : dest_pitch;
+    if (nBytes == 3)
+      dest_step -= 2;
+    for (int row = row_start; row < row_end; ++row) {
+      int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
+                     dest_clip.left;
+      uint8_t* dest_scan = dest_buf + dest_col * nBytes;
+      if (bYFlip)
+        dest_scan += (result_height - 1) * dest_pitch;
+      if (nBytes == 4) {
+        const uint32_t* src_scan =
+            reinterpret_cast<const uint32_t*>(GetScanline(row)) + col_start;
+        for (int col = col_start; col < col_end; ++col) {
+          uint32_t* dest_scan32 = reinterpret_cast<uint32_t*>(dest_scan);
+          *dest_scan32 = *src_scan++;
+          dest_scan += dest_step;
+        }
+      } else {
+        const uint8_t* src_scan = GetScanline(row) + col_start * nBytes;
+        if (nBytes == 1) {
+          for (int col = col_start; col < col_end; ++col) {
+            *dest_scan = *src_scan++;
+            dest_scan += dest_step;
+          }
+        } else {
+          for (int col = col_start; col < col_end; ++col) {
+            *dest_scan++ = *src_scan++;
+            *dest_scan++ = *src_scan++;
+            *dest_scan = *src_scan++;
+            dest_scan += dest_step;
+          }
+        }
+      }
+    }
+  }
+  if (m_pAlphaMask) {
+    dest_pitch = pTransBitmap->m_pAlphaMask->GetPitch();
+    dest_buf = pTransBitmap->m_pAlphaMask->GetBuffer();
+    int dest_step = bYFlip ? -dest_pitch : dest_pitch;
+    for (int row = row_start; row < row_end; ++row) {
+      int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
+                     dest_clip.left;
+      uint8_t* dest_scan = dest_buf + dest_col;
+      if (bYFlip)
+        dest_scan += (result_height - 1) * dest_pitch;
+      const uint8_t* src_scan = m_pAlphaMask->GetScanline(row) + col_start;
+      for (int col = col_start; col < col_end; ++col) {
+        *dest_scan = *src_scan++;
+        dest_scan += dest_step;
+      }
+    }
+  }
+  return pTransBitmap;
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::TransformTo(const CFX_Matrix& mtDest,
+                                                 int* result_left,
+                                                 int* result_top) {
+  RetainPtr<CFX_DIBBase> holder(this);
+  CFX_ImageTransformer transformer(holder, mtDest, FXDIB_ResampleOptions(),
+                                   nullptr);
+  transformer.Continue(nullptr);
+  *result_left = transformer.result().left;
+  *result_top = transformer.result().top;
+  return transformer.DetachBitmap();
+}
+
+RetainPtr<CFX_DIBitmap> CFX_DIBBase::StretchTo(
+    int dest_width,
+    int dest_height,
+    const FXDIB_ResampleOptions& options,
+    const FX_RECT* pClip) {
+  RetainPtr<CFX_DIBBase> holder(this);
+  FX_RECT clip_rect(0, 0, abs(dest_width), abs(dest_height));
+  if (pClip)
+    clip_rect.Intersect(*pClip);
+
+  if (clip_rect.IsEmpty())
+    return nullptr;
+
+  if (dest_width == m_Width && dest_height == m_Height)
+    return Clone(&clip_rect);
+
+  CFX_BitmapStorer storer;
+  CFX_ImageStretcher stretcher(&storer, holder, dest_width, dest_height,
+                               clip_rect, options);
+  if (stretcher.Start())
+    stretcher.Continue(nullptr);
+
+  return storer.Detach();
+}
+
+// static
+bool CFX_DIBBase::ConvertBuffer(
+    FXDIB_Format dest_format,
+    uint8_t* dest_buf,
+    int dest_pitch,
+    int width,
+    int height,
+    const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+    int src_left,
+    int src_top,
+    std::unique_ptr<uint32_t, FxFreeDeleter>* p_pal) {
+  FXDIB_Format src_format = pSrcBitmap->GetFormat();
+  const int bpp = GetBppFromFormat(src_format);
+  switch (dest_format) {
+    case FXDIB_8bppMask: {
+      return ConvertBuffer_8bppMask(bpp, dest_buf, dest_pitch, width, height,
+                                    pSrcBitmap, src_left, src_top);
+    }
+    case FXDIB_8bppRgb:
+    case FXDIB_8bppRgba: {
+      const bool bpp_1_or_8 = (bpp == 1 || bpp == 8);
+      if (bpp_1_or_8 && !pSrcBitmap->GetPalette()) {
+        return ConvertBuffer(FXDIB_8bppMask, dest_buf, dest_pitch, width,
+                             height, pSrcBitmap, src_left, src_top, p_pal);
+      }
+      p_pal->reset(FX_Alloc(uint32_t, 256));
+      if (bpp_1_or_8 && pSrcBitmap->GetPalette()) {
+        ConvertBuffer_Plt2PltRgb8(dest_buf, dest_pitch, width, height,
+                                  pSrcBitmap, src_left, src_top, p_pal->get());
+        return true;
+      }
+      if (bpp >= 24) {
+        ConvertBuffer_Rgb2PltRgb8(dest_buf, dest_pitch, width, height,
+                                  pSrcBitmap, src_left, src_top, p_pal->get());
+        return true;
+      }
+      return false;
+    }
+    case FXDIB_Rgb:
+    case FXDIB_Rgba: {
+      return ConvertBuffer_Rgb(bpp, dest_format, dest_buf, dest_pitch, width,
+                               height, pSrcBitmap, src_left, src_top);
+    }
+    case FXDIB_Argb:
+    case FXDIB_Rgb32: {
+      return ConvertBuffer_Argb(bpp, GetIsCmykFromFormat(src_format),
+                                dest_format, dest_buf, dest_pitch, width,
+                                height, pSrcBitmap, src_left, src_top);
+    }
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
diff --git a/core/fxge/dib/cfx_dibbase.h b/core/fxge/dib/cfx_dibbase.h
new file mode 100644
index 0000000..15d37b7
--- /dev/null
+++ b/core/fxge/dib/cfx_dibbase.h
@@ -0,0 +1,135 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_DIB_CFX_DIBBASE_H_
+#define CORE_FXGE_DIB_CFX_DIBBASE_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/fx_dib.h"
+
+enum FXDIB_Channel {
+  FXDIB_Red = 1,
+  FXDIB_Green,
+  FXDIB_Blue,
+  FXDIB_Cyan,
+  FXDIB_Magenta,
+  FXDIB_Yellow,
+  FXDIB_Black,
+  FXDIB_Alpha
+};
+
+class CFX_ClipRgn;
+class CFX_DIBitmap;
+class PauseIndicatorIface;
+
+// Base class for all Device-Independent Bitmaps.
+class CFX_DIBBase : public Retainable {
+ public:
+  ~CFX_DIBBase() override;
+
+  virtual uint8_t* GetBuffer() const;
+  virtual const uint8_t* GetScanline(int line) const = 0;
+  virtual bool SkipToScanline(int line, PauseIndicatorIface* pPause) const;
+  virtual void DownSampleScanline(int line,
+                                  uint8_t* dest_scan,
+                                  int dest_bpp,
+                                  int dest_width,
+                                  bool bFlipX,
+                                  int clip_left,
+                                  int clip_width) const = 0;
+
+  uint8_t* GetWritableScanline(int line) {
+    return const_cast<uint8_t*>(GetScanline(line));
+  }
+  int GetWidth() const { return m_Width; }
+  int GetHeight() const { return m_Height; }
+
+  FXDIB_Format GetFormat() const {
+    return static_cast<FXDIB_Format>(m_AlphaFlag * 0x100 + m_bpp);
+  }
+  uint32_t GetPitch() const { return m_Pitch; }
+  uint32_t* GetPalette() const { return m_pPalette.get(); }
+  int GetBPP() const { return m_bpp; }
+
+  bool IsAlphaMask() const { return !!(m_AlphaFlag & 1); }
+  bool HasAlpha() const { return !!(m_AlphaFlag & 2); }
+  bool IsCmykImage() const { return !!(m_AlphaFlag & 4); }
+  bool IsOpaqueImage() const { return !IsAlphaMask() && !HasAlpha(); }
+
+  size_t GetPaletteSize() const;
+
+  uint32_t GetPaletteArgb(int index) const;
+  void SetPaletteArgb(int index, uint32_t color);
+
+  // Copies into internally-owned palette.
+  void SetPalette(const uint32_t* pSrcPal);
+
+  RetainPtr<CFX_DIBitmap> Clone(const FX_RECT* pClip) const;
+  RetainPtr<CFX_DIBitmap> CloneConvert(FXDIB_Format format);
+  RetainPtr<CFX_DIBitmap> StretchTo(int dest_width,
+                                    int dest_height,
+                                    const FXDIB_ResampleOptions& options,
+                                    const FX_RECT* pClip);
+  RetainPtr<CFX_DIBitmap> TransformTo(const CFX_Matrix& mtDest,
+                                      int* left,
+                                      int* top);
+  RetainPtr<CFX_DIBitmap> SwapXY(bool bXFlip, bool bYFlip) const;
+  RetainPtr<CFX_DIBitmap> FlipImage(bool bXFlip, bool bYFlip) const;
+
+  RetainPtr<CFX_DIBitmap> CloneAlphaMask() const;
+
+  // Copies into internally-owned mask.
+  bool SetAlphaMask(const RetainPtr<CFX_DIBBase>& pAlphaMask,
+                    const FX_RECT* pClip);
+
+  bool GetOverlapRect(int& dest_left,
+                      int& dest_top,
+                      int& width,
+                      int& height,
+                      int src_width,
+                      int src_height,
+                      int& src_left,
+                      int& src_top,
+                      const CFX_ClipRgn* pClipRgn);
+
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+  void DebugVerifyBitmapIsPreMultiplied(void* buffer) const;
+#endif
+
+  RetainPtr<CFX_DIBitmap> m_pAlphaMask;
+
+ protected:
+  CFX_DIBBase();
+
+  static bool ConvertBuffer(FXDIB_Format dest_format,
+                            uint8_t* dest_buf,
+                            int dest_pitch,
+                            int width,
+                            int height,
+                            const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                            int src_left,
+                            int src_top,
+                            std::unique_ptr<uint32_t, FxFreeDeleter>* pal);
+
+  void BuildPalette();
+  bool BuildAlphaMask();
+  int FindPalette(uint32_t color) const;
+  void GetPalette(uint32_t* pal, int alpha) const;
+
+  int m_Width;
+  int m_Height;
+  int m_bpp;
+  uint32_t m_AlphaFlag;
+  uint32_t m_Pitch;
+  // TODO(weili): Use std::vector for this.
+  std::unique_ptr<uint32_t, FxFreeDeleter> m_pPalette;
+};
+
+#endif  // CORE_FXGE_DIB_CFX_DIBBASE_H_
diff --git a/core/fxge/dib/cfx_dibextractor.cpp b/core/fxge/dib/cfx_dibextractor.cpp
index 13ad461..f6f31e8 100644
--- a/core/fxge/dib/cfx_dibextractor.cpp
+++ b/core/fxge/dib/cfx_dibextractor.cpp
@@ -6,17 +6,18 @@
 
 #include "core/fxge/dib/cfx_dibextractor.h"
 
-#include "core/fxge/dib/cfx_dibsource.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 
-CFX_DIBExtractor::CFX_DIBExtractor(const RetainPtr<CFX_DIBSource>& pSrc) {
+CFX_DIBExtractor::CFX_DIBExtractor(const RetainPtr<CFX_DIBBase>& pSrc) {
   if (!pSrc->GetBuffer()) {
     m_pBitmap = pSrc->Clone(nullptr);
     return;
   }
-  RetainPtr<CFX_DIBSource> pOldSrc(pSrc);
+  RetainPtr<CFX_DIBBase> pOldSrc(pSrc);
   m_pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!m_pBitmap->Create(pOldSrc->GetWidth(), pOldSrc->GetHeight(),
-                         pOldSrc->GetFormat(), pOldSrc->GetBuffer())) {
+                         pOldSrc->GetFormat(), pOldSrc->GetBuffer(), 0)) {
     m_pBitmap.Reset();
     return;
   }
diff --git a/core/fxge/dib/cfx_dibextractor.h b/core/fxge/dib/cfx_dibextractor.h
index b6c27a7..eff96b1 100644
--- a/core/fxge/dib/cfx_dibextractor.h
+++ b/core/fxge/dib/cfx_dibextractor.h
@@ -8,13 +8,13 @@
 #define CORE_FXGE_DIB_CFX_DIBEXTRACTOR_H_
 
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
 
-class CFX_DIBSource;
+class CFX_DIBBase;
+class CFX_DIBitmap;
 
 class CFX_DIBExtractor {
  public:
-  explicit CFX_DIBExtractor(const RetainPtr<CFX_DIBSource>& pSrc);
+  explicit CFX_DIBExtractor(const RetainPtr<CFX_DIBBase>& pSrc);
   ~CFX_DIBExtractor();
 
   RetainPtr<CFX_DIBitmap> GetBitmap() { return m_pBitmap; }
diff --git a/core/fxge/dib/cfx_dibitmap.cpp b/core/fxge/dib/cfx_dibitmap.cpp
index 857ca9a..c5ee2c5 100644
--- a/core/fxge/dib/cfx_dibitmap.cpp
+++ b/core/fxge/dib/cfx_dibitmap.cpp
@@ -10,16 +10,15 @@
 #include <memory>
 #include <utility>
 
-#include "core/fxcodec/fx_codec.h"
+#include "build/build_config.h"
 #include "core/fxge/cfx_cliprgn.h"
+#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
 #include "core/fxge/dib/cfx_scanlinecompositor.h"
-#include "third_party/base/ptr_util.h"
-
-#define MAX_OOM_LIMIT 12000000
 
 namespace {
 
-const int8_t g_ChannelOffset[] = {0, 2, 1, 0, 0, 1, 2, 3, 3};
+constexpr size_t kMaxOOMLimit = 12000000;
+constexpr int8_t kChannelOffset[] = {0, 2, 1, 0, 0, 1, 2, 3, 3};
 
 }  // namespace
 
@@ -30,29 +29,31 @@
 #endif
 }
 
+bool CFX_DIBitmap::Create(int width, int height, FXDIB_Format format) {
+  return Create(width, height, format, nullptr, 0);
+}
+
 bool CFX_DIBitmap::Create(int width,
                           int height,
                           FXDIB_Format format,
                           uint8_t* pBuffer,
                           uint32_t pitch) {
   m_pBuffer = nullptr;
-  m_bpp = static_cast<uint8_t>(format);
-  m_AlphaFlag = static_cast<uint8_t>(format >> 8);
+  m_bpp = GetBppFromFormat(format);
+  m_AlphaFlag = GetAlphaFlagFromFormat(format);
   m_Width = 0;
   m_Height = 0;
   m_Pitch = 0;
 
   uint32_t calculatedSize;
-  if (!CFX_DIBitmap::CalculatePitchAndSize(height, width, format, &pitch,
-                                           &calculatedSize))
+  if (!CalculatePitchAndSize(height, width, format, &pitch, &calculatedSize))
     return false;
 
   if (pBuffer) {
     m_pBuffer.Reset(pBuffer);
   } else {
     size_t bufferSize = calculatedSize + 4;
-    size_t oomlimit = MAX_OOM_LIMIT;
-    if (bufferSize >= oomlimit) {
+    if (bufferSize >= kMaxOOMLimit) {
       m_pBuffer = std::unique_ptr<uint8_t, FxFreeDeleter>(
           FX_TryAlloc(uint8_t, bufferSize));
       if (!m_pBuffer)
@@ -81,7 +82,7 @@
   return false;
 }
 
-bool CFX_DIBitmap::Copy(const RetainPtr<CFX_DIBSource>& pSrc) {
+bool CFX_DIBitmap::Copy(const RetainPtr<CFX_DIBBase>& pSrc) {
   if (m_pBuffer)
     return false;
 
@@ -184,98 +185,117 @@
                                   int dest_top,
                                   int width,
                                   int height,
-                                  const RetainPtr<CFX_DIBSource>& pSrcBitmap,
+                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                                   int src_left,
                                   int src_top) {
   if (!m_pBuffer)
     return false;
 
-  GetOverlapRect(dest_left, dest_top, width, height, pSrcBitmap->GetWidth(),
-                 pSrcBitmap->GetHeight(), src_left, src_top, nullptr);
-  if (width == 0 || height == 0)
+  if (!GetOverlapRect(dest_left, dest_top, width, height,
+                      pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), src_left,
+                      src_top, nullptr)) {
     return true;
+  }
 
   FXDIB_Format dest_format = GetFormat();
   FXDIB_Format src_format = pSrcBitmap->GetFormat();
-  if (dest_format == src_format) {
-    if (GetBPP() == 1) {
-      for (int row = 0; row < height; row++) {
-        uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch;
-        const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
-        for (int col = 0; col < width; col++) {
-          if (src_scan[(src_left + col) / 8] &
-              (1 << (7 - (src_left + col) % 8))) {
-            dest_scan[(dest_left + col) / 8] |= 1
-                                                << (7 - (dest_left + col) % 8);
-          } else {
-            dest_scan[(dest_left + col) / 8] &=
-                ~(1 << (7 - (dest_left + col) % 8));
-          }
-        }
-      }
-    } else {
-      int Bpp = GetBPP() / 8;
-      for (int row = 0; row < height; row++) {
-        uint8_t* dest_scan =
-            m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * Bpp;
-        const uint8_t* src_scan =
-            pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
-        memcpy(dest_scan, src_scan, width * Bpp);
-      }
-    }
-  } else {
-    if (m_pPalette)
-      return false;
+  if (dest_format != src_format) {
+    return TransferWithUnequalFormats(dest_format, dest_left, dest_top, width,
+                                      height, pSrcBitmap, src_left, src_top);
+  }
 
-    if (m_bpp == 8)
-      dest_format = FXDIB_8bppMask;
+  if (GetBPP() != 1) {
+    TransferWithMultipleBPP(dest_left, dest_top, width, height, pSrcBitmap,
+                            src_left, src_top);
+    return true;
+  }
 
-    uint8_t* dest_buf =
-        m_pBuffer.Get() + dest_top * m_Pitch + dest_left * GetBPP() / 8;
-    std::unique_ptr<uint32_t, FxFreeDeleter> d_plt;
-    if (!ConvertBuffer(dest_format, dest_buf, m_Pitch, width, height,
-                       pSrcBitmap, src_left, src_top, &d_plt)) {
-      return false;
-    }
+  TransferEqualFormatsOneBPP(dest_left, dest_top, width, height, pSrcBitmap,
+                             src_left, src_top);
+  return true;
+}
+
+bool CFX_DIBitmap::TransferWithUnequalFormats(
+    FXDIB_Format dest_format,
+    int dest_left,
+    int dest_top,
+    int width,
+    int height,
+    const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+    int src_left,
+    int src_top) {
+  if (m_pPalette)
+    return false;
+
+  if (m_bpp == 8)
+    dest_format = FXDIB_8bppMask;
+
+  uint8_t* dest_buf =
+      m_pBuffer.Get() + dest_top * m_Pitch + dest_left * GetBPP() / 8;
+  std::unique_ptr<uint32_t, FxFreeDeleter> d_plt;
+  if (!ConvertBuffer(dest_format, dest_buf, m_Pitch, width, height, pSrcBitmap,
+                     src_left, src_top, &d_plt)) {
+    return false;
   }
   return true;
 }
 
-bool CFX_DIBitmap::LoadChannel(FXDIB_Channel destChannel,
-                               const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                               FXDIB_Channel srcChannel) {
+void CFX_DIBitmap::TransferWithMultipleBPP(
+    int dest_left,
+    int dest_top,
+    int width,
+    int height,
+    const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+    int src_left,
+    int src_top) {
+  int Bpp = GetBPP() / 8;
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan =
+        m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * Bpp;
+    const uint8_t* src_scan =
+        pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
+    memcpy(dest_scan, src_scan, width * Bpp);
+  }
+}
+
+void CFX_DIBitmap::TransferEqualFormatsOneBPP(
+    int dest_left,
+    int dest_top,
+    int width,
+    int height,
+    const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+    int src_left,
+    int src_top) {
+  for (int row = 0; row < height; ++row) {
+    uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch;
+    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
+    for (int col = 0; col < width; ++col) {
+      int src_idx = src_left + col;
+      int dest_idx = dest_left + col;
+      if (src_scan[(src_idx) / 8] & (1 << (7 - (src_idx) % 8)))
+        dest_scan[(dest_idx) / 8] |= 1 << (7 - (dest_idx) % 8);
+      else
+        dest_scan[(dest_idx) / 8] &= ~(1 << (7 - (dest_idx) % 8));
+    }
+  }
+}
+
+bool CFX_DIBitmap::LoadChannelFromAlpha(
+    FXDIB_Channel destChannel,
+    const RetainPtr<CFX_DIBBase>& pSrcBitmap) {
   if (!m_pBuffer)
     return false;
 
-  RetainPtr<CFX_DIBSource> pSrcClone = pSrcBitmap;
-  int srcOffset;
-  if (srcChannel == FXDIB_Alpha) {
-    if (!pSrcBitmap->HasAlpha() && !pSrcBitmap->IsAlphaMask())
-      return false;
+  RetainPtr<CFX_DIBBase> pSrcClone = pSrcBitmap;
+  if (!pSrcBitmap->HasAlpha() && !pSrcBitmap->IsAlphaMask())
+    return false;
 
-    if (pSrcBitmap->GetBPP() == 1) {
-      pSrcClone = pSrcBitmap->CloneConvert(FXDIB_8bppMask);
-      if (!pSrcClone)
-        return false;
-    }
-    srcOffset = pSrcBitmap->GetFormat() == FXDIB_Argb ? 3 : 0;
-  } else {
-    if (pSrcBitmap->IsAlphaMask())
+  if (pSrcBitmap->GetBPP() == 1) {
+    pSrcClone = pSrcBitmap->CloneConvert(FXDIB_8bppMask);
+    if (!pSrcClone)
       return false;
-
-    if (pSrcBitmap->GetBPP() < 24) {
-      if (pSrcBitmap->IsCmykImage()) {
-        pSrcClone = pSrcBitmap->CloneConvert(static_cast<FXDIB_Format>(
-            (pSrcBitmap->GetFormat() & 0xff00) | 0x20));
-      } else {
-        pSrcClone = pSrcBitmap->CloneConvert(static_cast<FXDIB_Format>(
-            (pSrcBitmap->GetFormat() & 0xff00) | 0x18));
-      }
-      if (!pSrcClone)
-        return false;
-    }
-    srcOffset = g_ChannelOffset[srcChannel];
   }
+  int srcOffset = pSrcBitmap->GetFormat() == FXDIB_Argb ? 3 : 0;
   int destOffset = 0;
   if (destChannel == FXDIB_Alpha) {
     if (IsAlphaMask()) {
@@ -296,22 +316,25 @@
       if (HasAlpha()) {
         if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb))
           return false;
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-      } else if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : FXDIB_Rgb32)) {
+      } else {
+#if defined(OS_MACOSX)
+        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32;
 #else
-      } else if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : FXDIB_Rgb)) {
+        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb;
 #endif
-        return false;
+        if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : kPlatformFormat))
+          return false;
       }
     }
-    destOffset = g_ChannelOffset[destChannel];
+    destOffset = kChannelOffset[destChannel];
   }
-  if (srcChannel == FXDIB_Alpha && pSrcClone->m_pAlphaMask) {
-    RetainPtr<CFX_DIBSource> pAlphaMask = pSrcClone->m_pAlphaMask;
+  if (pSrcClone->m_pAlphaMask) {
+    RetainPtr<CFX_DIBBase> pAlphaMask = pSrcClone->m_pAlphaMask;
     if (pSrcClone->GetWidth() != m_Width ||
         pSrcClone->GetHeight() != m_Height) {
       if (pAlphaMask) {
-        pAlphaMask = pAlphaMask->StretchTo(m_Width, m_Height, 0, nullptr);
+        pAlphaMask = pAlphaMask->StretchTo(m_Width, m_Height,
+                                           FXDIB_ResampleOptions(), nullptr);
         if (!pAlphaMask)
           return false;
       }
@@ -320,12 +343,12 @@
     srcOffset = 0;
   } else if (pSrcClone->GetWidth() != m_Width ||
              pSrcClone->GetHeight() != m_Height) {
-    RetainPtr<CFX_DIBitmap> pSrcMatched =
-        pSrcClone->StretchTo(m_Width, m_Height, 0, nullptr);
+    RetainPtr<CFX_DIBitmap> pSrcMatched = pSrcClone->StretchTo(
+        m_Width, m_Height, FXDIB_ResampleOptions(), nullptr);
     if (!pSrcMatched)
       return false;
 
-    pSrcClone = std::move(pSrcMatched);
+    pSrcClone = pSrcMatched;
   }
   RetainPtr<CFX_DIBitmap> pDst(this);
   if (destChannel == FXDIB_Alpha && m_pAlphaMask) {
@@ -335,7 +358,7 @@
   int srcBytes = pSrcClone->GetBPP() / 8;
   int destBytes = pDst->GetBPP() / 8;
   for (int row = 0; row < m_Height; row++) {
-    uint8_t* dest_pos = (uint8_t*)pDst->GetScanline(row) + destOffset;
+    uint8_t* dest_pos = pDst->GetWritableScanline(row) + destOffset;
     const uint8_t* src_pos = pSrcClone->GetScanline(row) + srcOffset;
     for (int col = 0; col < m_Width; col++) {
       *dest_pos = *src_pos;
@@ -375,15 +398,17 @@
         if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb)) {
           return false;
         }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-      } else if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : FXDIB_Rgb)) {
+      } else {
+#if defined(OS_MACOSX)
+        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb;
 #else
-      } else if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : FXDIB_Rgb32)) {
+        constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32;
 #endif
-        return false;
+        if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : kPlatformFormat))
+          return false;
       }
     }
-    destOffset = g_ChannelOffset[destChannel];
+    destOffset = kChannelOffset[destChannel];
   }
   int Bpp = GetBPP() / 8;
   if (Bpp == 1) {
@@ -405,21 +430,23 @@
   return true;
 }
 
-bool CFX_DIBitmap::MultiplyAlpha(const RetainPtr<CFX_DIBSource>& pSrcBitmap) {
+bool CFX_DIBitmap::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& pSrcBitmap) {
   if (!m_pBuffer)
     return false;
 
-  ASSERT(pSrcBitmap->IsAlphaMask());
-  if (!pSrcBitmap->IsAlphaMask())
+  if (!pSrcBitmap->IsAlphaMask()) {
+    NOTREACHED();
     return false;
+  }
 
   if (!IsAlphaMask() && !HasAlpha())
-    return LoadChannel(FXDIB_Alpha, pSrcBitmap, FXDIB_Alpha);
+    return LoadChannelFromAlpha(FXDIB_Alpha, pSrcBitmap);
 
   RetainPtr<CFX_DIBitmap> pSrcClone = pSrcBitmap.As<CFX_DIBitmap>();
   if (pSrcBitmap->GetWidth() != m_Width ||
       pSrcBitmap->GetHeight() != m_Height) {
-    pSrcClone = pSrcBitmap->StretchTo(m_Width, m_Height, 0, nullptr);
+    pSrcClone = pSrcBitmap->StretchTo(m_Width, m_Height,
+                                      FXDIB_ResampleOptions(), nullptr);
     if (!pSrcClone)
       return false;
   }
@@ -682,7 +709,7 @@
   }
 }
 
-void CFX_DIBitmap::ConvertRGBColorScale(uint32_t forecolor,
+void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor,
                                         uint32_t backcolor) {
   int fr = FXSYS_GetRValue(forecolor);
   int fg = FXSYS_GetGValue(forecolor);
@@ -701,8 +728,8 @@
                             FXARGB_G(m_pPalette.get()[i]),
                             FXARGB_B(m_pPalette.get()[i]));
       m_pPalette.get()[i] =
-          FXARGB_MAKE(0xff, br + (fr - br) * gray / 255,
-                      bg + (fg - bg) * gray / 255, bb + (fb - bb) * gray / 255);
+          ArgbEncode(0xff, br + (fr - br) * gray / 255,
+                     bg + (fg - bg) * gray / 255, bb + (fb - bb) * gray / 255);
     }
     return;
   }
@@ -800,17 +827,17 @@
 }
 
 bool CFX_DIBitmap::ConvertColorScale(uint32_t forecolor, uint32_t backcolor) {
-  ASSERT(!IsAlphaMask());
   if (!m_pBuffer || IsAlphaMask())
     return false;
 
   if (IsCmykImage())
     ConvertCMYKColorScale(forecolor, backcolor);
   else
-    ConvertRGBColorScale(forecolor, backcolor);
+    ConvertBGRColorScale(forecolor, backcolor);
   return true;
 }
 
+// static
 bool CFX_DIBitmap::CalculatePitchAndSize(int height,
                                          int width,
                                          FXDIB_Format format,
@@ -819,11 +846,15 @@
   if (width <= 0 || height <= 0)
     return false;
 
-  if ((INT_MAX - 31) / width < (format & 0xFF))
+  int bpp = GetBppFromFormat(format);
+  if (!bpp)
+    return false;
+
+  if ((INT_MAX - 31) / width < bpp)
     return false;
 
   if (!*pitch)
-    *pitch = static_cast<uint32_t>((width * (format & 0xff) + 31) / 32 * 4);
+    *pitch = static_cast<uint32_t>((width * bpp + 31) / 32 * 4);
 
   if ((1 << 30) / *pitch < static_cast<uint32_t>(height))
     return false;
@@ -836,25 +867,26 @@
                                    int dest_top,
                                    int width,
                                    int height,
-                                   const RetainPtr<CFX_DIBSource>& pSrcBitmap,
+                                   const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                                    int src_left,
                                    int src_top,
-                                   int blend_type,
+                                   BlendMode blend_type,
                                    const CFX_ClipRgn* pClipRgn,
                                    bool bRgbByteOrder) {
   if (!m_pBuffer)
     return false;
 
-  ASSERT(!pSrcBitmap->IsAlphaMask());
-  ASSERT(m_bpp >= 8);
   if (pSrcBitmap->IsAlphaMask() || m_bpp < 8) {
+    NOTREACHED();
     return false;
   }
-  GetOverlapRect(dest_left, dest_top, width, height, pSrcBitmap->GetWidth(),
-                 pSrcBitmap->GetHeight(), src_left, src_top, pClipRgn);
-  if (width == 0 || height == 0) {
+
+  if (!GetOverlapRect(dest_left, dest_top, width, height,
+                      pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), src_left,
+                      src_top, pClipRgn)) {
     return true;
   }
+
   RetainPtr<CFX_DIBitmap> pClipMask;
   FX_RECT clip_box;
   if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::RectI) {
@@ -865,7 +897,7 @@
   CFX_ScanlineCompositor compositor;
   if (!compositor.Init(GetFormat(), pSrcBitmap->GetFormat(), width,
                        pSrcBitmap->GetPalette(), 0, blend_type,
-                       pClipMask != nullptr, bRgbByteOrder, 0)) {
+                       pClipMask != nullptr, bRgbByteOrder)) {
     return false;
   }
   int dest_Bpp = m_bpp / 8;
@@ -882,7 +914,7 @@
                       : nullptr;
     uint8_t* dst_scan_extra_alpha =
         m_pAlphaMask
-            ? (uint8_t*)m_pAlphaMask->GetScanline(dest_top + row) + dest_left
+            ? m_pAlphaMask->GetWritableScanline(dest_top + row) + dest_left
             : nullptr;
     const uint8_t* clip_scan = nullptr;
     if (pClipMask) {
@@ -907,29 +939,27 @@
                                  int dest_top,
                                  int width,
                                  int height,
-                                 const RetainPtr<CFX_DIBSource>& pMask,
+                                 const RetainPtr<CFX_DIBBase>& pMask,
                                  uint32_t color,
                                  int src_left,
                                  int src_top,
-                                 int blend_type,
+                                 BlendMode blend_type,
                                  const CFX_ClipRgn* pClipRgn,
-                                 bool bRgbByteOrder,
-                                 int alpha_flag) {
+                                 bool bRgbByteOrder) {
   if (!m_pBuffer)
     return false;
 
-  ASSERT(pMask->IsAlphaMask());
-  ASSERT(m_bpp >= 8);
-  if (!pMask->IsAlphaMask() || m_bpp < 8)
+  if (!pMask->IsAlphaMask() || m_bpp < 8) {
+    NOTREACHED();
     return false;
+  }
 
-  GetOverlapRect(dest_left, dest_top, width, height, pMask->GetWidth(),
-                 pMask->GetHeight(), src_left, src_top, pClipRgn);
-  if (width == 0 || height == 0)
+  if (!GetOverlapRect(dest_left, dest_top, width, height, pMask->GetWidth(),
+                      pMask->GetHeight(), src_left, src_top, pClipRgn)) {
     return true;
+  }
 
-  int src_alpha =
-      (uint8_t)(alpha_flag >> 8) ? (alpha_flag & 0xff) : FXARGB_A(color);
+  int src_alpha = FXARGB_A(color);
   if (src_alpha == 0)
     return true;
 
@@ -944,8 +974,7 @@
   int Bpp = GetBPP() / 8;
   CFX_ScanlineCompositor compositor;
   if (!compositor.Init(GetFormat(), pMask->GetFormat(), width, nullptr, color,
-                       blend_type, pClipMask != nullptr, bRgbByteOrder,
-                       alpha_flag)) {
+                       blend_type, pClipMask != nullptr, bRgbByteOrder)) {
     return false;
   }
   for (int row = 0; row < height; row++) {
@@ -954,7 +983,7 @@
     const uint8_t* src_scan = pMask->GetScanline(src_top + row);
     uint8_t* dst_scan_extra_alpha =
         m_pAlphaMask
-            ? (uint8_t*)m_pAlphaMask->GetScanline(dest_top + row) + dest_left
+            ? m_pAlphaMask->GetWritableScanline(dest_top + row) + dest_left
             : nullptr;
     const uint8_t* clip_scan = nullptr;
     if (pClipMask) {
@@ -997,7 +1026,7 @@
     dst_color = FXCMYK_TODIB(color);
   else
     dst_color = FXARGB_TODIB(color);
-  uint8_t* color_p = (uint8_t*)&dst_color;
+  uint8_t* color_p = reinterpret_cast<uint8_t*>(&dst_color);
   if (m_bpp == 8) {
     uint8_t gray = 255;
     if (!IsAlphaMask()) {
@@ -1029,7 +1058,9 @@
     return true;
   }
   if (m_bpp == 1) {
-    ASSERT(!IsCmykImage() && static_cast<uint8_t>(alpha_flag >> 8) == 0);
+    ASSERT(!IsCmykImage());
+    ASSERT(static_cast<uint8_t>(alpha_flag >> 8) == 0);
+
     int left_shift = rect.left % 8;
     int right_shift = rect.right % 8;
     int new_width = rect.right / 8 - rect.left / 8;
@@ -1043,10 +1074,8 @@
       index = (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
     }
     for (int row = rect.top; row < rect.bottom; row++) {
-      uint8_t* dest_scan_top =
-          const_cast<uint8_t*>(GetScanline(row)) + rect.left / 8;
-      uint8_t* dest_scan_top_r =
-          const_cast<uint8_t*>(GetScanline(row)) + rect.right / 8;
+      uint8_t* dest_scan_top = GetWritableScanline(row) + rect.left / 8;
+      uint8_t* dest_scan_top_r = GetWritableScanline(row) + rect.right / 8;
       uint8_t left_flag = *dest_scan_top & (255 << (8 - left_shift));
       uint8_t right_flag = *dest_scan_top_r & (255 >> right_shift);
       if (new_width) {
@@ -1068,9 +1097,15 @@
     }
     return true;
   }
-  ASSERT(m_bpp >= 24);
-  if (m_bpp < 24 || (!(alpha_flag >> 8) && IsCmykImage()))
+
+  if (m_bpp < 24) {
+    NOTREACHED();
     return false;
+  }
+
+  if (!(alpha_flag >> 8) && IsCmykImage())
+    return false;
+
   if (alpha_flag >> 8 && !IsCmykImage()) {
     std::tie(color_p[2], color_p[1], color_p[0]) =
         AdobeCMYK_to_sRGB1(FXSYS_GetCValue(color), FXSYS_GetMValue(color),
@@ -1085,16 +1120,15 @@
     for (int row = rect.top; row < rect.bottom; row++) {
       uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left * Bpp;
       uint8_t* dest_scan_alpha =
-          m_pAlphaMask ? (uint8_t*)m_pAlphaMask->GetScanline(row) + rect.left
+          m_pAlphaMask ? m_pAlphaMask->GetWritableScanline(row) + rect.left
                        : nullptr;
-      if (dest_scan_alpha) {
+      if (dest_scan_alpha)
         memset(dest_scan_alpha, 0xff, width);
-      }
+
       if (Bpp == 4) {
-        uint32_t* scan = (uint32_t*)dest_scan;
-        for (int col = 0; col < width; col++) {
+        uint32_t* scan = reinterpret_cast<uint32_t*>(dest_scan);
+        for (int col = 0; col < width; col++)
           *scan++ = dst_color;
-        }
       } else {
         for (int col = 0; col < width; col++) {
           *dest_scan++ = color_p[0];
@@ -1112,8 +1146,8 @@
         for (int col = 0; col < width; col++) {
           uint8_t back_alpha = dest_scan[3];
           if (back_alpha == 0) {
-            FXARGB_SETDIB(dest_scan, FXARGB_MAKE(src_alpha, color_p[2],
-                                                 color_p[1], color_p[0]));
+            FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, color_p[2],
+                                                color_p[1], color_p[0]));
             dest_scan += 4;
             continue;
           }
@@ -1130,7 +1164,7 @@
         }
       } else {
         uint8_t* dest_scan_alpha =
-            (uint8_t*)m_pAlphaMask->GetScanline(row) + rect.left;
+            m_pAlphaMask->GetWritableScanline(row) + rect.left;
         for (int col = 0; col < width; col++) {
           uint8_t back_alpha = *dest_scan_alpha;
           if (back_alpha == 0) {
@@ -1187,7 +1221,7 @@
     }
     return true;
   }
-  int dest_bpp = dest_format & 0xff;
+  int dest_bpp = GetBppFromFormat(dest_format);
   int dest_pitch = (dest_bpp * m_Width + 31) / 32 * 4;
   std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf(
       FX_TryAlloc(uint8_t, dest_pitch * m_Height + 4));
@@ -1207,7 +1241,7 @@
         }
       }
     }
-  } else if (dest_format & 0x0200) {
+  } else if (GetIsAlphaFromFormat(dest_format)) {
     if (src_format == FXDIB_Argb) {
       pAlphaMask = CloneAlphaMask();
       if (!pAlphaMask)
@@ -1223,7 +1257,7 @@
     }
   }
   bool ret = false;
-  RetainPtr<CFX_DIBSource> holder(this);
+  RetainPtr<CFX_DIBBase> holder(this);
   std::unique_ptr<uint32_t, FxFreeDeleter> pal_8bpp;
   ret = ConvertBuffer(dest_format, dest_buf.get(), dest_pitch, m_Width,
                       m_Height, holder, 0, 0, &pal_8bpp);
@@ -1233,8 +1267,8 @@
   m_pAlphaMask = pAlphaMask;
   m_pPalette = std::move(pal_8bpp);
   m_pBuffer = std::move(dest_buf);
-  m_bpp = static_cast<uint8_t>(dest_format);
-  m_AlphaFlag = static_cast<uint8_t>(dest_format >> 8);
+  m_bpp = GetBppFromFormat(dest_format);
+  m_AlphaFlag = GetAlphaFlagFromFormat(dest_format);
   m_Pitch = dest_pitch;
   return true;
 }
diff --git a/core/fxge/dib/cfx_dibitmap.h b/core/fxge/dib/cfx_dibitmap.h
index 7240829..ce53df8 100644
--- a/core/fxge/dib/cfx_dibitmap.h
+++ b/core/fxge/dib/cfx_dibitmap.h
@@ -8,27 +8,28 @@
 #define CORE_FXGE_DIB_CFX_DIBITMAP_H_
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/maybe_owned.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibsource.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/fx_dib.h"
 
-class CFX_DIBitmap : public CFX_DIBSource {
+class CFX_DIBitmap : public CFX_DIBBase {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  ~CFX_DIBitmap() override;
+  bool Create(int width, int height, FXDIB_Format format);
 
   bool Create(int width,
               int height,
               FXDIB_Format format,
-              uint8_t* pBuffer = nullptr,
-              uint32_t pitch = 0);
+              uint8_t* pBuffer,
+              uint32_t pitch);
 
-  bool Copy(const RetainPtr<CFX_DIBSource>& pSrc);
+  bool Copy(const RetainPtr<CFX_DIBBase>& pSrc);
 
-  // CFX_DIBSource
+  // CFX_DIBBase
   uint8_t* GetBuffer() const override;
   const uint8_t* GetScanline(int line) const override;
   void DownSampleScanline(int line,
@@ -46,19 +47,18 @@
   uint32_t GetPixel(int x, int y) const;
   void SetPixel(int x, int y, uint32_t color);
 
-  bool LoadChannel(FXDIB_Channel destChannel,
-                   const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                   FXDIB_Channel srcChannel);
+  bool LoadChannelFromAlpha(FXDIB_Channel destChannel,
+                            const RetainPtr<CFX_DIBBase>& pSrcBitmap);
   bool LoadChannel(FXDIB_Channel destChannel, int value);
 
   bool MultiplyAlpha(int alpha);
-  bool MultiplyAlpha(const RetainPtr<CFX_DIBSource>& pAlphaMask);
+  bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& pSrcBitmap);
 
   bool TransferBitmap(int dest_left,
                       int dest_top,
                       int width,
                       int height,
-                      const RetainPtr<CFX_DIBSource>& pSrcBitmap,
+                      const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                       int src_left,
                       int src_top);
 
@@ -66,25 +66,24 @@
                        int dest_top,
                        int width,
                        int height,
-                       const RetainPtr<CFX_DIBSource>& pSrcBitmap,
+                       const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                        int src_left,
                        int src_top,
-                       int blend_type = FXDIB_BLEND_NORMAL,
-                       const CFX_ClipRgn* pClipRgn = nullptr,
-                       bool bRgbByteOrder = false);
+                       BlendMode blend_type,
+                       const CFX_ClipRgn* pClipRgn,
+                       bool bRgbByteOrder);
 
   bool CompositeMask(int dest_left,
                      int dest_top,
                      int width,
                      int height,
-                     const RetainPtr<CFX_DIBSource>& pMask,
+                     const RetainPtr<CFX_DIBBase>& pMask,
                      uint32_t color,
                      int src_left,
                      int src_top,
-                     int blend_type = FXDIB_BLEND_NORMAL,
-                     const CFX_ClipRgn* pClipRgn = nullptr,
-                     bool bRgbByteOrder = false,
-                     int alpha_flag = 0);
+                     BlendMode blend_type,
+                     const CFX_ClipRgn* pClipRgn,
+                     bool bRgbByteOrder);
 
   bool CompositeRect(int dest_left,
                      int dest_top,
@@ -111,6 +110,7 @@
  protected:
   CFX_DIBitmap();
   CFX_DIBitmap(const CFX_DIBitmap& src);
+  ~CFX_DIBitmap() override;
 
 #if defined _SKIA_SUPPORT_PATHS_
   enum class Format { kCleared, kPreMultiplied, kUnPreMultiplied };
@@ -122,8 +122,30 @@
 #endif
 
  private:
-  void ConvertRGBColorScale(uint32_t forecolor, uint32_t backcolor);
+  void ConvertBGRColorScale(uint32_t forecolor, uint32_t backcolor);
   void ConvertCMYKColorScale(uint32_t forecolor, uint32_t backcolor);
+  bool TransferWithUnequalFormats(FXDIB_Format dest_format,
+                                  int dest_left,
+                                  int dest_top,
+                                  int width,
+                                  int height,
+                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                  int src_left,
+                                  int src_top);
+  void TransferWithMultipleBPP(int dest_left,
+                               int dest_top,
+                               int width,
+                               int height,
+                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               int src_left,
+                               int src_top);
+  void TransferEqualFormatsOneBPP(int dest_left,
+                                  int dest_top,
+                                  int width,
+                                  int height,
+                                  const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                                  int src_left,
+                                  int src_top);
 };
 
 #endif  // CORE_FXGE_DIB_CFX_DIBITMAP_H_
diff --git a/core/fxge/dib/cfx_dibitmap_unittest.cpp b/core/fxge/dib/cfx_dibitmap_unittest.cpp
new file mode 100644
index 0000000..67ca705
--- /dev/null
+++ b/core/fxge/dib/cfx_dibitmap_unittest.cpp
@@ -0,0 +1,15 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CFX_DIBitmap, Create) {
+  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  EXPECT_FALSE(pBitmap->Create(400, 300, FXDIB_Invalid));
+
+  pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  EXPECT_TRUE(pBitmap->Create(400, 300, FXDIB_1bppRgb));
+}
diff --git a/core/fxge/dib/cfx_dibsource.cpp b/core/fxge/dib/cfx_dibsource.cpp
deleted file mode 100644
index eda6d9e..0000000
--- a/core/fxge/dib/cfx_dibsource.cpp
+++ /dev/null
@@ -1,1229 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/dib/cfx_dibsource.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxge/cfx_cliprgn.h"
-#include "core/fxge/dib/cfx_bitmapstorer.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/cfx_imagestretcher.h"
-#include "core/fxge/dib/cfx_imagetransformer.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-class CFX_Palette {
- public:
-  explicit CFX_Palette(const RetainPtr<CFX_DIBSource>& pBitmap);
-  ~CFX_Palette();
-
-  const uint32_t* GetPalette() { return m_Palette.data(); }
-  const std::pair<uint32_t, uint32_t>* GetLuts() const { return m_Luts.data(); }
-  int32_t GetLutCount() const { return m_lut; }
-  void SetAmountLut(int row, uint32_t value) { m_Luts[row].first = value; }
-
- private:
-  std::vector<uint32_t> m_Palette;
-  // (Amount, Color) pairs
-  std::vector<std::pair<uint32_t, uint32_t>> m_Luts;
-  int m_lut;
-};
-
-void ColorDecode(uint32_t pal_v, uint8_t* r, uint8_t* g, uint8_t* b) {
-  *r = static_cast<uint8_t>((pal_v & 0xf00) >> 4);
-  *g = static_cast<uint8_t>(pal_v & 0x0f0);
-  *b = static_cast<uint8_t>((pal_v & 0x00f) << 4);
-}
-
-void Obtain_Pal(std::pair<uint32_t, uint32_t>* luts,
-                uint32_t* dest_pal,
-                uint32_t lut) {
-  uint32_t lut_1 = lut - 1;
-  for (int row = 0; row < 256; ++row) {
-    int lut_offset = lut_1 - row;
-    if (lut_offset < 0)
-      lut_offset += 256;
-    uint32_t color = luts[lut_offset].second;
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    ColorDecode(color, &r, &g, &b);
-    dest_pal[row] = (static_cast<uint32_t>(r) << 16) |
-                    (static_cast<uint32_t>(g) << 8) | b | 0xff000000;
-    luts[lut_offset].first = row;
-  }
-}
-
-CFX_Palette::CFX_Palette(const RetainPtr<CFX_DIBSource>& pBitmap)
-    : m_Palette(256), m_Luts(4096), m_lut(0) {
-  int bpp = pBitmap->GetBPP() / 8;
-  int width = pBitmap->GetWidth();
-  int height = pBitmap->GetHeight();
-  for (int row = 0; row < height; ++row) {
-    const uint8_t* scan_line = pBitmap->GetScanline(row);
-    for (int col = 0; col < width; ++col) {
-      const uint8_t* src_port = scan_line + col * bpp;
-      uint32_t b = src_port[0] & 0xf0;
-      uint32_t g = src_port[1] & 0xf0;
-      uint32_t r = src_port[2] & 0xf0;
-      uint32_t index = (r << 4) + g + (b >> 4);
-      ++m_Luts[index].first;
-    }
-  }
-  // Move non-zeros to the front and count them
-  for (int row = 0; row < 4096; ++row) {
-    if (m_Luts[row].first != 0) {
-      m_Luts[m_lut].first = m_Luts[row].first;
-      m_Luts[m_lut].second = row;
-      ++m_lut;
-    }
-  }
-  std::sort(m_Luts.begin(), m_Luts.begin() + m_lut,
-            [](const std::pair<uint32_t, uint32_t>& arg1,
-               const std::pair<uint32_t, uint32_t>& arg2) {
-              return arg1.first < arg2.first;
-            });
-  Obtain_Pal(m_Luts.data(), m_Palette.data(), m_lut);
-}
-
-CFX_Palette::~CFX_Palette() {}
-
-void ConvertBuffer_1bppMask2Gray(uint8_t* dest_buf,
-                                 int dest_pitch,
-                                 int width,
-                                 int height,
-                                 const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                 int src_left,
-                                 int src_top) {
-  uint8_t set_gray, reset_gray;
-  set_gray = 0xff;
-  reset_gray = 0x00;
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    memset(dest_scan, reset_gray, width);
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
-    for (int col = src_left; col < src_left + width; ++col) {
-      if (src_scan[col / 8] & (1 << (7 - col % 8)))
-        *dest_scan = set_gray;
-      ++dest_scan;
-    }
-  }
-}
-
-void ConvertBuffer_8bppMask2Gray(uint8_t* dest_buf,
-                                 int dest_pitch,
-                                 int width,
-                                 int height,
-                                 const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                 int src_left,
-                                 int src_top) {
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    memcpy(dest_scan, src_scan, width);
-  }
-}
-
-void ConvertBuffer_1bppPlt2Gray(uint8_t* dest_buf,
-                                int dest_pitch,
-                                int width,
-                                int height,
-                                const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                int src_left,
-                                int src_top) {
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  uint8_t gray[2];
-  uint8_t reset_r;
-  uint8_t reset_g;
-  uint8_t reset_b;
-  uint8_t set_r;
-  uint8_t set_g;
-  uint8_t set_b;
-  if (pSrcBitmap->IsCmykImage()) {
-    std::tie(reset_r, reset_g, reset_b) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]),
-        FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0]));
-    std::tie(set_r, set_g, set_b) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]),
-        FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1]));
-  } else {
-    reset_r = FXARGB_R(src_plt[0]);
-    reset_g = FXARGB_G(src_plt[0]);
-    reset_b = FXARGB_B(src_plt[0]);
-    set_r = FXARGB_R(src_plt[1]);
-    set_g = FXARGB_G(src_plt[1]);
-    set_b = FXARGB_B(src_plt[1]);
-  }
-  gray[0] = FXRGB2GRAY(reset_r, reset_g, reset_b);
-  gray[1] = FXRGB2GRAY(set_r, set_g, set_b);
-
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    memset(dest_scan, gray[0], width);
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
-    for (int col = src_left; col < src_left + width; ++col) {
-      if (src_scan[col / 8] & (1 << (7 - col % 8)))
-        *dest_scan = gray[1];
-      ++dest_scan;
-    }
-  }
-}
-
-void ConvertBuffer_8bppPlt2Gray(uint8_t* dest_buf,
-                                int dest_pitch,
-                                int width,
-                                int height,
-                                const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                int src_left,
-                                int src_top) {
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  uint8_t gray[256];
-  if (pSrcBitmap->IsCmykImage()) {
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    for (size_t i = 0; i < FX_ArraySize(gray); ++i) {
-      std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
-          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
-          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
-      gray[i] = FXRGB2GRAY(r, g, b);
-    }
-  } else {
-    for (size_t i = 0; i < FX_ArraySize(gray); ++i) {
-      gray[i] = FXRGB2GRAY(FXARGB_R(src_plt[i]), FXARGB_G(src_plt[i]),
-                           FXARGB_B(src_plt[i]));
-    }
-  }
-
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    for (int col = 0; col < width; ++col)
-      *dest_scan++ = gray[*src_scan++];
-  }
-}
-
-void ConvertBuffer_RgbOrCmyk2Gray(uint8_t* dest_buf,
-                                  int dest_pitch,
-                                  int width,
-                                  int height,
-                                  const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                  int src_left,
-                                  int src_top) {
-  int Bpp = pSrcBitmap->GetBPP() / 8;
-  if (pSrcBitmap->IsCmykImage()) {
-    for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
-      const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
-      for (int col = 0; col < width; ++col) {
-        uint8_t r;
-        uint8_t g;
-        uint8_t b;
-        std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
-            FXSYS_GetCValue(static_cast<uint32_t>(src_scan[0])),
-            FXSYS_GetMValue(static_cast<uint32_t>(src_scan[1])),
-            FXSYS_GetYValue(static_cast<uint32_t>(src_scan[2])),
-            FXSYS_GetKValue(static_cast<uint32_t>(src_scan[3])));
-        *dest_scan++ = FXRGB2GRAY(r, g, b);
-        src_scan += 4;
-      }
-    }
-  } else {
-    for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
-      const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
-      for (int col = 0; col < width; ++col) {
-        *dest_scan++ = FXRGB2GRAY(src_scan[2], src_scan[1], src_scan[0]);
-        src_scan += Bpp;
-      }
-    }
-  }
-}
-
-void ConvertBuffer_IndexCopy(uint8_t* dest_buf,
-                             int dest_pitch,
-                             int width,
-                             int height,
-                             const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                             int src_left,
-                             int src_top) {
-  if (pSrcBitmap->GetBPP() == 1) {
-    for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
-      // Set all destination pixels to be white initially.
-      memset(dest_scan, 255, width);
-      const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
-      for (int col = src_left; col < src_left + width; ++col) {
-        // If the source bit is set, then set the destination pixel to be black.
-        if (src_scan[col / 8] & (1 << (7 - col % 8)))
-          *dest_scan = 0;
-
-        ++dest_scan;
-      }
-    }
-  } else {
-    for (int row = 0; row < height; ++row) {
-      uint8_t* dest_scan = dest_buf + row * dest_pitch;
-      const uint8_t* src_scan =
-          pSrcBitmap->GetScanline(src_top + row) + src_left;
-      memcpy(dest_scan, src_scan, width);
-    }
-  }
-}
-
-void ConvertBuffer_Plt2PltRgb8(uint8_t* dest_buf,
-                               int dest_pitch,
-                               int width,
-                               int height,
-                               const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                               int src_left,
-                               int src_top,
-                               uint32_t* dst_plt) {
-  ConvertBuffer_IndexCopy(dest_buf, dest_pitch, width, height, pSrcBitmap,
-                          src_left, src_top);
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  int plt_size = pSrcBitmap->GetPaletteSize();
-  if (pSrcBitmap->IsCmykImage()) {
-    for (int i = 0; i < plt_size; ++i) {
-      uint8_t r;
-      uint8_t g;
-      uint8_t b;
-      std::tie(r, g, b) = AdobeCMYK_to_sRGB1(
-          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
-          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
-      dst_plt[i] = FXARGB_MAKE(0xff, r, g, b);
-    }
-  } else {
-    memcpy(dst_plt, src_plt, plt_size * 4);
-  }
-}
-
-void ConvertBuffer_Rgb2PltRgb8(uint8_t* dest_buf,
-                               int dest_pitch,
-                               int width,
-                               int height,
-                               const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                               int src_left,
-                               int src_top,
-                               uint32_t* dst_plt) {
-  int bpp = pSrcBitmap->GetBPP() / 8;
-  CFX_Palette palette(pSrcBitmap);
-  const std::pair<uint32_t, uint32_t>* Luts = palette.GetLuts();
-  int lut = palette.GetLutCount();
-  const uint32_t* pal = palette.GetPalette();
-  if (lut > 256) {
-    int err;
-    int min_err;
-    int lut_256 = lut - 256;
-    for (int row = 0; row < lut_256; ++row) {
-      min_err = 1000000;
-      uint8_t r;
-      uint8_t g;
-      uint8_t b;
-      ColorDecode(Luts[row].second, &r, &g, &b);
-      uint32_t clrindex = 0;
-      for (int col = 0; col < 256; ++col) {
-        uint32_t p_color = pal[col];
-        int d_r = r - static_cast<uint8_t>(p_color >> 16);
-        int d_g = g - static_cast<uint8_t>(p_color >> 8);
-        int d_b = b - static_cast<uint8_t>(p_color);
-        err = d_r * d_r + d_g * d_g + d_b * d_b;
-        if (err < min_err) {
-          min_err = err;
-          clrindex = col;
-        }
-      }
-      palette.SetAmountLut(row, clrindex);
-    }
-  }
-  int32_t lut_1 = lut - 1;
-  for (int row = 0; row < height; ++row) {
-    uint8_t* src_scan =
-        const_cast<uint8_t*>(pSrcBitmap->GetScanline(src_top + row)) + src_left;
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    for (int col = 0; col < width; ++col) {
-      uint8_t* src_port = src_scan + col * bpp;
-      int r = src_port[2] & 0xf0;
-      int g = src_port[1] & 0xf0;
-      int b = src_port[0] & 0xf0;
-      uint32_t clrindex = (r << 4) + g + (b >> 4);
-      for (int i = lut_1; i >= 0; --i)
-        if (clrindex == Luts[i].second) {
-          *(dest_scan + col) = static_cast<uint8_t>(Luts[i].first);
-          break;
-        }
-    }
-  }
-  memcpy(dst_plt, pal, sizeof(uint32_t) * 256);
-}
-
-void ConvertBuffer_1bppMask2Rgb(FXDIB_Format dst_format,
-                                uint8_t* dest_buf,
-                                int dest_pitch,
-                                int width,
-                                int height,
-                                const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                int src_left,
-                                int src_top) {
-  int comps = (dst_format & 0xff) / 8;
-  uint8_t set_gray, reset_gray;
-  set_gray = 0xff;
-  reset_gray = 0x00;
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
-    for (int col = src_left; col < src_left + width; ++col) {
-      if (src_scan[col / 8] & (1 << (7 - col % 8))) {
-        dest_scan[0] = set_gray;
-        dest_scan[1] = set_gray;
-        dest_scan[2] = set_gray;
-      } else {
-        dest_scan[0] = reset_gray;
-        dest_scan[1] = reset_gray;
-        dest_scan[2] = reset_gray;
-      }
-      dest_scan += comps;
-    }
-  }
-}
-
-void ConvertBuffer_8bppMask2Rgb(FXDIB_Format dst_format,
-                                uint8_t* dest_buf,
-                                int dest_pitch,
-                                int width,
-                                int height,
-                                const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                int src_left,
-                                int src_top) {
-  int comps = (dst_format & 0xff) / 8;
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    uint8_t src_pixel;
-    for (int col = 0; col < width; ++col) {
-      src_pixel = *src_scan++;
-      *dest_scan++ = src_pixel;
-      *dest_scan++ = src_pixel;
-      *dest_scan = src_pixel;
-      dest_scan += comps - 2;
-    }
-  }
-}
-
-void ConvertBuffer_1bppPlt2Rgb(FXDIB_Format dst_format,
-                               uint8_t* dest_buf,
-                               int dest_pitch,
-                               int width,
-                               int height,
-                               const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                               int src_left,
-                               int src_top) {
-  int comps = (dst_format & 0xff) / 8;
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  uint32_t plt[2];
-  uint8_t* bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  if (pSrcBitmap->IsCmykImage()) {
-    plt[0] = FXCMYK_TODIB(src_plt[0]);
-    plt[1] = FXCMYK_TODIB(src_plt[1]);
-  } else {
-    bgr_ptr[0] = FXARGB_B(src_plt[0]);
-    bgr_ptr[1] = FXARGB_G(src_plt[0]);
-    bgr_ptr[2] = FXARGB_R(src_plt[0]);
-    bgr_ptr[3] = FXARGB_B(src_plt[1]);
-    bgr_ptr[4] = FXARGB_G(src_plt[1]);
-    bgr_ptr[5] = FXARGB_R(src_plt[1]);
-  }
-
-  if (pSrcBitmap->IsCmykImage()) {
-    std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]),
-        FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0]));
-    std::tie(bgr_ptr[5], bgr_ptr[4], bgr_ptr[3]) = AdobeCMYK_to_sRGB1(
-        FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]),
-        FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1]));
-  }
-
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row);
-    for (int col = src_left; col < src_left + width; ++col) {
-      if (src_scan[col / 8] & (1 << (7 - col % 8))) {
-        *dest_scan++ = bgr_ptr[3];
-        *dest_scan++ = bgr_ptr[4];
-        *dest_scan = bgr_ptr[5];
-      } else {
-        *dest_scan++ = bgr_ptr[0];
-        *dest_scan++ = bgr_ptr[1];
-        *dest_scan = bgr_ptr[2];
-      }
-      dest_scan += comps - 2;
-    }
-  }
-}
-
-void ConvertBuffer_8bppPlt2Rgb(FXDIB_Format dst_format,
-                               uint8_t* dest_buf,
-                               int dest_pitch,
-                               int width,
-                               int height,
-                               const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                               int src_left,
-                               int src_top) {
-  int comps = (dst_format & 0xff) / 8;
-  uint32_t* src_plt = pSrcBitmap->GetPalette();
-  uint32_t plt[256];
-  uint8_t* bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  if (!pSrcBitmap->IsCmykImage()) {
-    for (int i = 0; i < 256; ++i) {
-      *bgr_ptr++ = FXARGB_B(src_plt[i]);
-      *bgr_ptr++ = FXARGB_G(src_plt[i]);
-      *bgr_ptr++ = FXARGB_R(src_plt[i]);
-    }
-    bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  }
-
-  if (pSrcBitmap->IsCmykImage()) {
-    for (int i = 0; i < 256; ++i) {
-      std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1(
-          FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]),
-          FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i]));
-      bgr_ptr += 3;
-    }
-    bgr_ptr = reinterpret_cast<uint8_t*>(plt);
-  }
-
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left;
-    for (int col = 0; col < width; ++col) {
-      uint8_t* src_pixel = bgr_ptr + 3 * (*src_scan++);
-      *dest_scan++ = *src_pixel++;
-      *dest_scan++ = *src_pixel++;
-      *dest_scan = *src_pixel++;
-      dest_scan += comps - 2;
-    }
-  }
-}
-
-void ConvertBuffer_24bppRgb2Rgb24(uint8_t* dest_buf,
-                                  int dest_pitch,
-                                  int width,
-                                  int height,
-                                  const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                  int src_left,
-                                  int src_top) {
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
-    memcpy(dest_scan, src_scan, width * 3);
-  }
-}
-
-void ConvertBuffer_32bppRgb2Rgb24(uint8_t* dest_buf,
-                                  int dest_pitch,
-                                  int width,
-                                  int height,
-                                  const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                  int src_left,
-                                  int src_top) {
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
-    for (int col = 0; col < width; ++col) {
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      ++src_scan;
-    }
-  }
-}
-
-void ConvertBuffer_Rgb2Rgb32(uint8_t* dest_buf,
-                             int dest_pitch,
-                             int width,
-                             int height,
-                             const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                             int src_left,
-                             int src_top) {
-  int comps = pSrcBitmap->GetBPP() / 8;
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * comps;
-    for (int col = 0; col < width; ++col) {
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      *dest_scan++ = *src_scan++;
-      ++dest_scan;
-      src_scan += comps - 3;
-    }
-  }
-}
-
-void ConvertBuffer_32bppCmyk2Rgb32(uint8_t* dest_buf,
-                                   int dest_pitch,
-                                   int width,
-                                   int height,
-                                   const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                                   int src_left,
-                                   int src_top) {
-  for (int row = 0; row < height; ++row) {
-    uint8_t* dest_scan = dest_buf + row * dest_pitch;
-    const uint8_t* src_scan =
-        pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
-    for (int col = 0; col < width; ++col) {
-      std::tie(dest_scan[2], dest_scan[1], dest_scan[0]) = AdobeCMYK_to_sRGB1(
-          src_scan[0], src_scan[1], src_scan[2], src_scan[3]);
-      dest_scan += 4;
-      src_scan += 4;
-    }
-  }
-}
-
-}  // namespace
-
-CFX_DIBSource::CFX_DIBSource()
-    : m_Width(0), m_Height(0), m_bpp(0), m_AlphaFlag(0), m_Pitch(0) {}
-
-CFX_DIBSource::~CFX_DIBSource() {}
-
-uint8_t* CFX_DIBSource::GetBuffer() const {
-  return nullptr;
-}
-
-bool CFX_DIBSource::SkipToScanline(int line, IFX_PauseIndicator* pPause) const {
-  return false;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_DIBSource::Clone(const FX_RECT* pClip) const {
-  FX_RECT rect(0, 0, m_Width, m_Height);
-  if (pClip) {
-    rect.Intersect(*pClip);
-    if (rect.IsEmpty())
-      return nullptr;
-  }
-  auto pNewBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pNewBitmap->Create(rect.Width(), rect.Height(), GetFormat()))
-    return nullptr;
-
-  pNewBitmap->SetPalette(m_pPalette.get());
-  pNewBitmap->SetAlphaMask(m_pAlphaMask, pClip);
-  if (GetBPP() == 1 && rect.left % 8 != 0) {
-    int left_shift = rect.left % 32;
-    int right_shift = 32 - left_shift;
-    int dword_count = pNewBitmap->m_Pitch / 4;
-    for (int row = rect.top; row < rect.bottom; ++row) {
-      uint32_t* src_scan = (uint32_t*)GetScanline(row) + rect.left / 32;
-      uint32_t* dest_scan = (uint32_t*)pNewBitmap->GetScanline(row - rect.top);
-      for (int i = 0; i < dword_count; ++i) {
-        dest_scan[i] =
-            (src_scan[i] << left_shift) | (src_scan[i + 1] >> right_shift);
-      }
-    }
-  } else {
-    int copy_len = (pNewBitmap->GetWidth() * pNewBitmap->GetBPP() + 7) / 8;
-    if (m_Pitch < static_cast<uint32_t>(copy_len))
-      copy_len = m_Pitch;
-
-    for (int row = rect.top; row < rect.bottom; ++row) {
-      const uint8_t* src_scan = GetScanline(row) + rect.left * m_bpp / 8;
-      uint8_t* dest_scan = (uint8_t*)pNewBitmap->GetScanline(row - rect.top);
-      memcpy(dest_scan, src_scan, copy_len);
-    }
-  }
-  return pNewBitmap;
-}
-
-void CFX_DIBSource::BuildPalette() {
-  if (m_pPalette)
-    return;
-
-  if (GetBPP() == 1) {
-    m_pPalette.reset(FX_Alloc(uint32_t, 2));
-    if (IsCmykImage()) {
-      m_pPalette.get()[0] = 0xff;
-      m_pPalette.get()[1] = 0;
-    } else {
-      m_pPalette.get()[0] = 0xff000000;
-      m_pPalette.get()[1] = 0xffffffff;
-    }
-  } else if (GetBPP() == 8) {
-    m_pPalette.reset(FX_Alloc(uint32_t, 256));
-    if (IsCmykImage()) {
-      for (int i = 0; i < 256; ++i)
-        m_pPalette.get()[i] = 0xff - i;
-    } else {
-      for (int i = 0; i < 256; ++i)
-        m_pPalette.get()[i] = 0xff000000 | (i * 0x10101);
-    }
-  }
-}
-
-bool CFX_DIBSource::BuildAlphaMask() {
-  if (m_pAlphaMask)
-    return true;
-
-  m_pAlphaMask = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!m_pAlphaMask->Create(m_Width, m_Height, FXDIB_8bppMask)) {
-    m_pAlphaMask = nullptr;
-    return false;
-  }
-  memset(m_pAlphaMask->GetBuffer(), 0xff,
-         m_pAlphaMask->GetHeight() * m_pAlphaMask->GetPitch());
-  return true;
-}
-
-uint32_t CFX_DIBSource::GetPaletteArgb(int index) const {
-  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
-  if (m_pPalette)
-    return m_pPalette.get()[index];
-
-  if (IsCmykImage()) {
-    if (GetBPP() == 1)
-      return index ? 0 : 0xff;
-
-    return 0xff - index;
-  }
-  if (GetBPP() == 1)
-    return index ? 0xffffffff : 0xff000000;
-
-  return index * 0x10101 | 0xff000000;
-}
-
-void CFX_DIBSource::SetPaletteArgb(int index, uint32_t color) {
-  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
-  if (!m_pPalette) {
-    BuildPalette();
-  }
-  m_pPalette.get()[index] = color;
-}
-
-int CFX_DIBSource::FindPalette(uint32_t color) const {
-  ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask());
-  if (!m_pPalette) {
-    if (IsCmykImage()) {
-      if (GetBPP() == 1)
-        return (static_cast<uint8_t>(color) == 0xff) ? 0 : 1;
-
-      return 0xff - static_cast<uint8_t>(color);
-    }
-    if (GetBPP() == 1)
-      return (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
-
-    return static_cast<uint8_t>(color);
-  }
-  int palsize = (1 << GetBPP());
-  for (int i = 0; i < palsize; ++i) {
-    if (m_pPalette.get()[i] == color)
-      return i;
-  }
-  return -1;
-}
-
-void CFX_DIBSource::GetOverlapRect(int& dest_left,
-                                   int& dest_top,
-                                   int& width,
-                                   int& height,
-                                   int src_width,
-                                   int src_height,
-                                   int& src_left,
-                                   int& src_top,
-                                   const CFX_ClipRgn* pClipRgn) {
-  if (width == 0 || height == 0)
-    return;
-
-  ASSERT(width > 0 && height > 0);
-  if (dest_left > m_Width || dest_top > m_Height) {
-    width = 0;
-    height = 0;
-    return;
-  }
-  int x_offset = dest_left - src_left;
-  int y_offset = dest_top - src_top;
-  FX_RECT src_rect(src_left, src_top, src_left + width, src_top + height);
-  FX_RECT src_bound(0, 0, src_width, src_height);
-  src_rect.Intersect(src_bound);
-  FX_RECT dest_rect(src_rect.left + x_offset, src_rect.top + y_offset,
-                    src_rect.right + x_offset, src_rect.bottom + y_offset);
-  FX_RECT dest_bound(0, 0, m_Width, m_Height);
-  dest_rect.Intersect(dest_bound);
-  if (pClipRgn)
-    dest_rect.Intersect(pClipRgn->GetBox());
-  dest_left = dest_rect.left;
-  dest_top = dest_rect.top;
-  src_left = dest_left - x_offset;
-  src_top = dest_top - y_offset;
-  width = dest_rect.right - dest_rect.left;
-  height = dest_rect.bottom - dest_rect.top;
-}
-
-void CFX_DIBSource::SetPalette(const uint32_t* pSrc) {
-  static const uint32_t kPaletteSize = 256;
-  if (!pSrc || GetBPP() > 8) {
-    m_pPalette.reset();
-    return;
-  }
-  uint32_t pal_size = 1 << GetBPP();
-  if (!m_pPalette)
-    m_pPalette.reset(FX_Alloc(uint32_t, pal_size));
-  pal_size = std::min(pal_size, kPaletteSize);
-  memcpy(m_pPalette.get(), pSrc, pal_size * sizeof(uint32_t));
-}
-
-void CFX_DIBSource::GetPalette(uint32_t* pal, int alpha) const {
-  ASSERT(GetBPP() <= 8 && !IsCmykImage());
-  if (GetBPP() == 1) {
-    pal[0] = ((m_pPalette ? m_pPalette.get()[0] : 0xff000000) & 0xffffff) |
-             (alpha << 24);
-    pal[1] = ((m_pPalette ? m_pPalette.get()[1] : 0xffffffff) & 0xffffff) |
-             (alpha << 24);
-    return;
-  }
-  if (m_pPalette) {
-    for (int i = 0; i < 256; ++i)
-      pal[i] = (m_pPalette.get()[i] & 0x00ffffff) | (alpha << 24);
-  } else {
-    for (int i = 0; i < 256; ++i)
-      pal[i] = (i * 0x10101) | (alpha << 24);
-  }
-}
-
-RetainPtr<CFX_DIBitmap> CFX_DIBSource::CloneAlphaMask() const {
-  ASSERT(GetFormat() == FXDIB_Argb);
-  FX_RECT rect(0, 0, m_Width, m_Height);
-  auto pMask = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pMask->Create(rect.Width(), rect.Height(), FXDIB_8bppMask))
-    return nullptr;
-
-  for (int row = rect.top; row < rect.bottom; ++row) {
-    const uint8_t* src_scan = GetScanline(row) + rect.left * 4 + 3;
-    uint8_t* dest_scan =
-        const_cast<uint8_t*>(pMask->GetScanline(row - rect.top));
-    for (int col = rect.left; col < rect.right; ++col) {
-      *dest_scan++ = *src_scan;
-      src_scan += 4;
-    }
-  }
-  return pMask;
-}
-
-bool CFX_DIBSource::SetAlphaMask(const RetainPtr<CFX_DIBSource>& pAlphaMask,
-                                 const FX_RECT* pClip) {
-  if (!HasAlpha() || GetFormat() == FXDIB_Argb)
-    return false;
-
-  if (!pAlphaMask) {
-    m_pAlphaMask->Clear(0xff000000);
-    return true;
-  }
-  FX_RECT rect(0, 0, pAlphaMask->m_Width, pAlphaMask->m_Height);
-  if (pClip) {
-    rect.Intersect(*pClip);
-    if (rect.IsEmpty() || rect.Width() != m_Width ||
-        rect.Height() != m_Height) {
-      return false;
-    }
-  } else {
-    if (pAlphaMask->m_Width != m_Width || pAlphaMask->m_Height != m_Height)
-      return false;
-  }
-  for (int row = 0; row < m_Height; ++row) {
-    memcpy(const_cast<uint8_t*>(m_pAlphaMask->GetScanline(row)),
-           pAlphaMask->GetScanline(row + rect.top) + rect.left,
-           m_pAlphaMask->m_Pitch);
-  }
-  return true;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_DIBSource::FlipImage(bool bXFlip,
-                                                 bool bYFlip) const {
-  auto pFlipped = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pFlipped->Create(m_Width, m_Height, GetFormat()))
-    return nullptr;
-
-  pFlipped->SetPalette(m_pPalette.get());
-  uint8_t* pDestBuffer = pFlipped->GetBuffer();
-  int Bpp = m_bpp / 8;
-  for (int row = 0; row < m_Height; ++row) {
-    const uint8_t* src_scan = GetScanline(row);
-    uint8_t* dest_scan =
-        pDestBuffer + m_Pitch * (bYFlip ? (m_Height - row - 1) : row);
-    if (!bXFlip) {
-      memcpy(dest_scan, src_scan, m_Pitch);
-      continue;
-    }
-    if (m_bpp == 1) {
-      memset(dest_scan, 0, m_Pitch);
-      for (int col = 0; col < m_Width; ++col)
-        if (src_scan[col / 8] & (1 << (7 - col % 8))) {
-          int dest_col = m_Width - col - 1;
-          dest_scan[dest_col / 8] |= (1 << (7 - dest_col % 8));
-        }
-    } else {
-      dest_scan += (m_Width - 1) * Bpp;
-      if (Bpp == 1) {
-        for (int col = 0; col < m_Width; ++col) {
-          *dest_scan = *src_scan;
-          --dest_scan;
-          ++src_scan;
-        }
-      } else if (Bpp == 3) {
-        for (int col = 0; col < m_Width; ++col) {
-          dest_scan[0] = src_scan[0];
-          dest_scan[1] = src_scan[1];
-          dest_scan[2] = src_scan[2];
-          dest_scan -= 3;
-          src_scan += 3;
-        }
-      } else {
-        ASSERT(Bpp == 4);
-        for (int col = 0; col < m_Width; ++col) {
-          *(uint32_t*)dest_scan = *(uint32_t*)src_scan;
-          dest_scan -= 4;
-          src_scan += 4;
-        }
-      }
-    }
-  }
-  if (m_pAlphaMask) {
-    pDestBuffer = pFlipped->m_pAlphaMask->GetBuffer();
-    uint32_t dest_pitch = pFlipped->m_pAlphaMask->GetPitch();
-    for (int row = 0; row < m_Height; ++row) {
-      const uint8_t* src_scan = m_pAlphaMask->GetScanline(row);
-      uint8_t* dest_scan =
-          pDestBuffer + dest_pitch * (bYFlip ? (m_Height - row - 1) : row);
-      if (!bXFlip) {
-        memcpy(dest_scan, src_scan, dest_pitch);
-        continue;
-      }
-      dest_scan += (m_Width - 1);
-      for (int col = 0; col < m_Width; ++col) {
-        *dest_scan = *src_scan;
-        --dest_scan;
-        ++src_scan;
-      }
-    }
-  }
-  return pFlipped;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_DIBSource::CloneConvert(FXDIB_Format dest_format) {
-  if (dest_format == GetFormat())
-    return Clone(nullptr);
-
-  auto pClone = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pClone->Create(m_Width, m_Height, dest_format))
-    return nullptr;
-
-  RetainPtr<CFX_DIBitmap> pSrcAlpha;
-  if (HasAlpha()) {
-    if (GetFormat() == FXDIB_Argb)
-      pSrcAlpha = CloneAlphaMask();
-    else
-      pSrcAlpha = m_pAlphaMask;
-
-    if (!pSrcAlpha)
-      return nullptr;
-  }
-  bool ret = true;
-  if (dest_format & 0x0200) {
-    if (dest_format == FXDIB_Argb) {
-      ret = pSrcAlpha ? pClone->LoadChannel(FXDIB_Alpha, pSrcAlpha, FXDIB_Alpha)
-                      : pClone->LoadChannel(FXDIB_Alpha, 0xff);
-    } else {
-      ret = pClone->SetAlphaMask(pSrcAlpha, nullptr);
-    }
-  }
-  if (!ret)
-    return nullptr;
-
-  RetainPtr<CFX_DIBSource> holder(this);
-  std::unique_ptr<uint32_t, FxFreeDeleter> pal_8bpp;
-  if (!ConvertBuffer(dest_format, pClone->GetBuffer(), pClone->GetPitch(),
-                     m_Width, m_Height, holder, 0, 0, &pal_8bpp)) {
-    return nullptr;
-  }
-  if (pal_8bpp)
-    pClone->SetPalette(pal_8bpp.get());
-
-  return pClone;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_DIBSource::SwapXY(bool bXFlip, bool bYFlip) const {
-  FX_RECT dest_clip(0, 0, m_Height, m_Width);
-  if (dest_clip.IsEmpty())
-    return nullptr;
-
-  auto pTransBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  int result_height = dest_clip.Height();
-  int result_width = dest_clip.Width();
-  if (!pTransBitmap->Create(result_width, result_height, GetFormat()))
-    return nullptr;
-
-  pTransBitmap->SetPalette(m_pPalette.get());
-  int dest_pitch = pTransBitmap->GetPitch();
-  uint8_t* dest_buf = pTransBitmap->GetBuffer();
-  int row_start = bXFlip ? m_Height - dest_clip.right : dest_clip.left;
-  int row_end = bXFlip ? m_Height - dest_clip.left : dest_clip.right;
-  int col_start = bYFlip ? m_Width - dest_clip.bottom : dest_clip.top;
-  int col_end = bYFlip ? m_Width - dest_clip.top : dest_clip.bottom;
-  if (GetBPP() == 1) {
-    memset(dest_buf, 0xff, dest_pitch * result_height);
-    for (int row = row_start; row < row_end; ++row) {
-      const uint8_t* src_scan = GetScanline(row);
-      int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
-                     dest_clip.left;
-      uint8_t* dest_scan = dest_buf;
-      if (bYFlip)
-        dest_scan += (result_height - 1) * dest_pitch;
-      int dest_step = bYFlip ? -dest_pitch : dest_pitch;
-      for (int col = col_start; col < col_end; ++col) {
-        if (!(src_scan[col / 8] & (1 << (7 - col % 8))))
-          dest_scan[dest_col / 8] &= ~(1 << (7 - dest_col % 8));
-        dest_scan += dest_step;
-      }
-    }
-  } else {
-    int nBytes = GetBPP() / 8;
-    int dest_step = bYFlip ? -dest_pitch : dest_pitch;
-    if (nBytes == 3)
-      dest_step -= 2;
-    for (int row = row_start; row < row_end; ++row) {
-      int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
-                     dest_clip.left;
-      uint8_t* dest_scan = dest_buf + dest_col * nBytes;
-      if (bYFlip)
-        dest_scan += (result_height - 1) * dest_pitch;
-      if (nBytes == 4) {
-        uint32_t* src_scan = (uint32_t*)GetScanline(row) + col_start;
-        for (int col = col_start; col < col_end; ++col) {
-          *(uint32_t*)dest_scan = *src_scan++;
-          dest_scan += dest_step;
-        }
-      } else {
-        const uint8_t* src_scan = GetScanline(row) + col_start * nBytes;
-        if (nBytes == 1) {
-          for (int col = col_start; col < col_end; ++col) {
-            *dest_scan = *src_scan++;
-            dest_scan += dest_step;
-          }
-        } else {
-          for (int col = col_start; col < col_end; ++col) {
-            *dest_scan++ = *src_scan++;
-            *dest_scan++ = *src_scan++;
-            *dest_scan = *src_scan++;
-            dest_scan += dest_step;
-          }
-        }
-      }
-    }
-  }
-  if (m_pAlphaMask) {
-    dest_pitch = pTransBitmap->m_pAlphaMask->GetPitch();
-    dest_buf = pTransBitmap->m_pAlphaMask->GetBuffer();
-    int dest_step = bYFlip ? -dest_pitch : dest_pitch;
-    for (int row = row_start; row < row_end; ++row) {
-      int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) -
-                     dest_clip.left;
-      uint8_t* dest_scan = dest_buf + dest_col;
-      if (bYFlip)
-        dest_scan += (result_height - 1) * dest_pitch;
-      const uint8_t* src_scan = m_pAlphaMask->GetScanline(row) + col_start;
-      for (int col = col_start; col < col_end; ++col) {
-        *dest_scan = *src_scan++;
-        dest_scan += dest_step;
-      }
-    }
-  }
-  return pTransBitmap;
-}
-
-RetainPtr<CFX_DIBitmap> CFX_DIBSource::TransformTo(
-    const CFX_Matrix* pDestMatrix,
-    int* result_left,
-    int* result_top) {
-  RetainPtr<CFX_DIBSource> holder(this);
-  CFX_ImageTransformer transformer(holder, pDestMatrix, 0, nullptr);
-  transformer.Continue(nullptr);
-  *result_left = transformer.result().left;
-  *result_top = transformer.result().top;
-  return transformer.DetachBitmap();
-}
-
-RetainPtr<CFX_DIBitmap> CFX_DIBSource::StretchTo(int dest_width,
-                                                 int dest_height,
-                                                 uint32_t flags,
-                                                 const FX_RECT* pClip) {
-  RetainPtr<CFX_DIBSource> holder(this);
-  FX_RECT clip_rect(0, 0, abs(dest_width), abs(dest_height));
-  if (pClip)
-    clip_rect.Intersect(*pClip);
-
-  if (clip_rect.IsEmpty())
-    return nullptr;
-
-  if (dest_width == m_Width && dest_height == m_Height)
-    return Clone(&clip_rect);
-
-  CFX_BitmapStorer storer;
-  CFX_ImageStretcher stretcher(&storer, holder, dest_width, dest_height,
-                               clip_rect, flags);
-  if (stretcher.Start())
-    stretcher.Continue(nullptr);
-
-  return storer.Detach();
-}
-
-// static
-bool CFX_DIBSource::ConvertBuffer(
-    FXDIB_Format dest_format,
-    uint8_t* dest_buf,
-    int dest_pitch,
-    int width,
-    int height,
-    const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-    int src_left,
-    int src_top,
-    std::unique_ptr<uint32_t, FxFreeDeleter>* p_pal) {
-  FXDIB_Format src_format = pSrcBitmap->GetFormat();
-  switch (dest_format) {
-    case FXDIB_Invalid:
-    case FXDIB_1bppCmyk:
-    case FXDIB_1bppMask:
-    case FXDIB_1bppRgb:
-      NOTREACHED();
-      return false;
-    case FXDIB_8bppMask: {
-      if ((src_format & 0xff) == 1) {
-        if (pSrcBitmap->GetPalette()) {
-          ConvertBuffer_1bppPlt2Gray(dest_buf, dest_pitch, width, height,
-                                     pSrcBitmap, src_left, src_top);
-          return true;
-        }
-        ConvertBuffer_1bppMask2Gray(dest_buf, dest_pitch, width, height,
-                                    pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      if ((src_format & 0xff) == 8) {
-        if (pSrcBitmap->GetPalette()) {
-          ConvertBuffer_8bppPlt2Gray(dest_buf, dest_pitch, width, height,
-                                     pSrcBitmap, src_left, src_top);
-          return true;
-        }
-        ConvertBuffer_8bppMask2Gray(dest_buf, dest_pitch, width, height,
-                                    pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      if ((src_format & 0xff) >= 24) {
-        ConvertBuffer_RgbOrCmyk2Gray(dest_buf, dest_pitch, width, height,
-                                     pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      return false;
-    }
-    case FXDIB_8bppRgb:
-    case FXDIB_8bppRgba: {
-      if ((src_format & 0xff) == 8 && !pSrcBitmap->GetPalette()) {
-        return ConvertBuffer(FXDIB_8bppMask, dest_buf, dest_pitch, width,
-                             height, pSrcBitmap, src_left, src_top, p_pal);
-      }
-      p_pal->reset(FX_Alloc(uint32_t, 256));
-      if (((src_format & 0xff) == 1 || (src_format & 0xff) == 8) &&
-          pSrcBitmap->GetPalette()) {
-        ConvertBuffer_Plt2PltRgb8(dest_buf, dest_pitch, width, height,
-                                  pSrcBitmap, src_left, src_top, p_pal->get());
-        return true;
-      }
-      if ((src_format & 0xff) >= 24) {
-        ConvertBuffer_Rgb2PltRgb8(dest_buf, dest_pitch, width, height,
-                                  pSrcBitmap, src_left, src_top, p_pal->get());
-        return true;
-      }
-      return false;
-    }
-    case FXDIB_Rgb:
-    case FXDIB_Rgba: {
-      if ((src_format & 0xff) == 1) {
-        if (pSrcBitmap->GetPalette()) {
-          ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                    height, pSrcBitmap, src_left, src_top);
-          return true;
-        }
-        ConvertBuffer_1bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                   height, pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      if ((src_format & 0xff) == 8) {
-        if (pSrcBitmap->GetPalette()) {
-          ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                    height, pSrcBitmap, src_left, src_top);
-          return true;
-        }
-        ConvertBuffer_8bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                   height, pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      if ((src_format & 0xff) == 24) {
-        ConvertBuffer_24bppRgb2Rgb24(dest_buf, dest_pitch, width, height,
-                                     pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      if ((src_format & 0xff) == 32) {
-        ConvertBuffer_32bppRgb2Rgb24(dest_buf, dest_pitch, width, height,
-                                     pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      return false;
-    }
-    case FXDIB_Argb:
-    case FXDIB_Rgb32: {
-      if ((src_format & 0xff) == 1) {
-        if (pSrcBitmap->GetPalette()) {
-          ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                    height, pSrcBitmap, src_left, src_top);
-          return true;
-        }
-        ConvertBuffer_1bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                   height, pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      if ((src_format & 0xff) == 8) {
-        if (pSrcBitmap->GetPalette()) {
-          ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                    height, pSrcBitmap, src_left, src_top);
-          return true;
-        }
-        ConvertBuffer_8bppMask2Rgb(dest_format, dest_buf, dest_pitch, width,
-                                   height, pSrcBitmap, src_left, src_top);
-        return true;
-      }
-      if ((src_format & 0xff) >= 24) {
-        if (src_format & 0x0400) {
-          ConvertBuffer_32bppCmyk2Rgb32(dest_buf, dest_pitch, width, height,
-                                        pSrcBitmap, src_left, src_top);
-          return true;
-        }
-        ConvertBuffer_Rgb2Rgb32(dest_buf, dest_pitch, width, height, pSrcBitmap,
-                                src_left, src_top);
-        return true;
-      }
-      return false;
-    }
-    default:
-      return false;
-  }
-}
diff --git a/core/fxge/dib/cfx_dibsource.h b/core/fxge/dib/cfx_dibsource.h
deleted file mode 100644
index 02cae5d..0000000
--- a/core/fxge/dib/cfx_dibsource.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_DIB_CFX_DIBSOURCE_H_
-#define CORE_FXGE_DIB_CFX_DIBSOURCE_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
-
-enum FXDIB_Channel {
-  FXDIB_Red = 1,
-  FXDIB_Green,
-  FXDIB_Blue,
-  FXDIB_Cyan,
-  FXDIB_Magenta,
-  FXDIB_Yellow,
-  FXDIB_Black,
-  FXDIB_Alpha
-};
-
-class CFX_ClipRgn;
-class CFX_DIBitmap;
-class IFX_PauseIndicator;
-
-class CFX_DIBSource : public Retainable {
- public:
-  ~CFX_DIBSource() override;
-
-  virtual uint8_t* GetBuffer() const;
-  virtual const uint8_t* GetScanline(int line) const = 0;
-  virtual bool SkipToScanline(int line, IFX_PauseIndicator* pPause) const;
-  virtual void DownSampleScanline(int line,
-                                  uint8_t* dest_scan,
-                                  int dest_bpp,
-                                  int dest_width,
-                                  bool bFlipX,
-                                  int clip_left,
-                                  int clip_width) const = 0;
-
-  int GetWidth() const { return m_Width; }
-  int GetHeight() const { return m_Height; }
-
-  FXDIB_Format GetFormat() const {
-    return static_cast<FXDIB_Format>(m_AlphaFlag * 0x100 + m_bpp);
-  }
-  uint32_t GetPitch() const { return m_Pitch; }
-  uint32_t* GetPalette() const { return m_pPalette.get(); }
-  int GetBPP() const { return m_bpp; }
-
-  // TODO(thestig): Investigate this. Given the possible values of FXDIB_Format,
-  // it feels as though this should be implemented as !!(m_AlphaFlag & 1) and
-  // IsOpaqueImage() below should never be able to return true.
-  bool IsAlphaMask() const { return m_AlphaFlag == 1; }
-  bool HasAlpha() const { return !!(m_AlphaFlag & 2); }
-  bool IsOpaqueImage() const { return !(m_AlphaFlag & 3); }
-  bool IsCmykImage() const { return !!(m_AlphaFlag & 4); }
-
-  int GetPaletteSize() const {
-    return IsAlphaMask() ? 0 : (m_bpp == 1 ? 2 : (m_bpp == 8 ? 256 : 0));
-  }
-
-  uint32_t GetPaletteArgb(int index) const;
-  void SetPaletteArgb(int index, uint32_t color);
-
-  // Copies into internally-owned palette.
-  void SetPalette(const uint32_t* pSrcPal);
-
-  RetainPtr<CFX_DIBitmap> Clone(const FX_RECT* pClip) const;
-  RetainPtr<CFX_DIBitmap> CloneConvert(FXDIB_Format format);
-  RetainPtr<CFX_DIBitmap> StretchTo(int dest_width,
-                                    int dest_height,
-                                    uint32_t flags,
-                                    const FX_RECT* pClip);
-  RetainPtr<CFX_DIBitmap> TransformTo(const CFX_Matrix* pMatrix,
-                                      int* left,
-                                      int* top);
-  RetainPtr<CFX_DIBitmap> SwapXY(bool bXFlip, bool bYFlip) const;
-  RetainPtr<CFX_DIBitmap> FlipImage(bool bXFlip, bool bYFlip) const;
-
-  RetainPtr<CFX_DIBitmap> CloneAlphaMask() const;
-
-  // Copies into internally-owned mask.
-  bool SetAlphaMask(const RetainPtr<CFX_DIBSource>& pAlphaMask,
-                    const FX_RECT* pClip);
-
-  void GetOverlapRect(int& dest_left,
-                      int& dest_top,
-                      int& width,
-                      int& height,
-                      int src_width,
-                      int src_height,
-                      int& src_left,
-                      int& src_top,
-                      const CFX_ClipRgn* pClipRgn);
-
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  void DebugVerifyBitmapIsPreMultiplied(void* buffer) const;
-#endif
-
-  RetainPtr<CFX_DIBitmap> m_pAlphaMask;
-
- protected:
-  CFX_DIBSource();
-
-  static bool ConvertBuffer(FXDIB_Format dest_format,
-                            uint8_t* dest_buf,
-                            int dest_pitch,
-                            int width,
-                            int height,
-                            const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                            int src_left,
-                            int src_top,
-                            std::unique_ptr<uint32_t, FxFreeDeleter>* pal);
-
-  void BuildPalette();
-  bool BuildAlphaMask();
-  int FindPalette(uint32_t color) const;
-  void GetPalette(uint32_t* pal, int alpha) const;
-
-  int m_Width;
-  int m_Height;
-  int m_bpp;
-  uint32_t m_AlphaFlag;
-  uint32_t m_Pitch;
-  // TODO(weili): Use std::vector for this.
-  std::unique_ptr<uint32_t, FxFreeDeleter> m_pPalette;
-};
-
-#endif  // CORE_FXGE_DIB_CFX_DIBSOURCE_H_
diff --git a/core/fxge/dib/cfx_filtereddib.cpp b/core/fxge/dib/cfx_filtereddib.cpp
deleted file mode 100644
index 96fc806..0000000
--- a/core/fxge/dib/cfx_filtereddib.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/dib/cfx_filtereddib.h"
-
-#include "core/fxge/fx_dib.h"
-
-CFX_FilteredDIB::CFX_FilteredDIB() {}
-
-CFX_FilteredDIB::~CFX_FilteredDIB() {}
-
-void CFX_FilteredDIB::LoadSrc(const RetainPtr<CFX_DIBSource>& pSrc) {
-  m_pSrc = pSrc;
-  m_Width = pSrc->GetWidth();
-  m_Height = pSrc->GetHeight();
-  FXDIB_Format format = GetDestFormat();
-  m_bpp = static_cast<uint8_t>(format);
-  m_AlphaFlag = static_cast<uint8_t>(format >> 8);
-  m_Pitch = (m_Width * (format & 0xff) + 31) / 32 * 4;
-  m_pPalette.reset(GetDestPalette());
-  m_Scanline.resize(m_Pitch);
-}
-
-const uint8_t* CFX_FilteredDIB::GetScanline(int line) const {
-  TranslateScanline(m_pSrc->GetScanline(line), &m_Scanline);
-  return m_Scanline.data();
-}
-
-void CFX_FilteredDIB::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);
-}
diff --git a/core/fxge/dib/cfx_filtereddib.h b/core/fxge/dib/cfx_filtereddib.h
deleted file mode 100644
index e998c71..0000000
--- a/core/fxge/dib/cfx_filtereddib.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_DIB_CFX_FILTEREDDIB_H_
-#define CORE_FXGE_DIB_CFX_FILTEREDDIB_H_
-
-#include <vector>
-
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibsource.h"
-
-class CFX_FilteredDIB : public CFX_DIBSource {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  ~CFX_FilteredDIB() override;
-
-  virtual FXDIB_Format GetDestFormat() = 0;
-  virtual uint32_t* GetDestPalette() = 0;
-  virtual void TranslateScanline(const uint8_t* src_buf,
-                                 std::vector<uint8_t>* dest_buf) const = 0;
-  virtual void TranslateDownSamples(uint8_t* dest_buf,
-                                    const uint8_t* src_buf,
-                                    int pixels,
-                                    int Bpp) const = 0;
-
-  void LoadSrc(const RetainPtr<CFX_DIBSource>& pSrc);
-
- protected:
-  CFX_FilteredDIB();
-
-  // CFX_DIBSource
-  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;
-
-  RetainPtr<CFX_DIBSource> m_pSrc;
-  mutable std::vector<uint8_t> m_Scanline;
-};
-
-#endif  // CORE_FXGE_DIB_CFX_FILTEREDDIB_H_
diff --git a/core/fxge/dib/cfx_imagerenderer.cpp b/core/fxge/dib/cfx_imagerenderer.cpp
index 658d363..5c15a9c 100644
--- a/core/fxge/dib/cfx_imagerenderer.cpp
+++ b/core/fxge/dib/cfx_imagerenderer.cpp
@@ -9,30 +9,29 @@
 #include <memory>
 
 #include "core/fxge/cfx_cliprgn.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
 #include "third_party/base/ptr_util.h"
 
 CFX_ImageRenderer::CFX_ImageRenderer(const RetainPtr<CFX_DIBitmap>& pDevice,
                                      const CFX_ClipRgn* pClipRgn,
-                                     const RetainPtr<CFX_DIBSource>& pSource,
+                                     const RetainPtr<CFX_DIBBase>& pSource,
                                      int bitmap_alpha,
                                      uint32_t mask_color,
-                                     const CFX_Matrix* pMatrix,
-                                     uint32_t dib_flags,
+                                     const CFX_Matrix& matrix,
+                                     const FXDIB_ResampleOptions& options,
                                      bool bRgbByteOrder)
     : m_pDevice(pDevice),
       m_pClipRgn(pClipRgn),
-      m_Matrix(*pMatrix),
+      m_Matrix(matrix),
       m_BitmapAlpha(bitmap_alpha),
-      m_BlendType(FXDIB_BLEND_NORMAL),
-      m_bRgbByteOrder(bRgbByteOrder),
       m_MaskColor(mask_color),
-      m_Status(0),
-      m_AlphaFlag(0) {
+      m_bRgbByteOrder(bRgbByteOrder) {
   FX_RECT image_rect = m_Matrix.GetUnitRect().GetOuterRect();
-  m_ClipBox = pClipRgn ? pClipRgn->GetBox() : FX_RECT(0, 0, pDevice->GetWidth(),
-                                                      pDevice->GetHeight());
+  m_ClipBox = pClipRgn
+                  ? pClipRgn->GetBox()
+                  : FX_RECT(0, 0, pDevice->GetWidth(), pDevice->GetHeight());
   m_ClipBox.Intersect(image_rect);
   if (m_ClipBox.IsEmpty())
     return;
@@ -50,17 +49,16 @@
                                       m_Matrix.c > 0, m_Matrix.b < 0);
       m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox,
                          true, m_Matrix.c > 0, m_Matrix.b < 0, m_bRgbByteOrder,
-                         0, m_BlendType);
+                         BlendMode::kNormal);
       m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
-          &m_Composer, pSource, dest_height, dest_width, bitmap_clip,
-          dib_flags);
+          &m_Composer, pSource, dest_height, dest_width, bitmap_clip, options);
       if (m_Stretcher->Start())
         m_Status = 1;
       return;
     }
     m_Status = 2;
     m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
-        pSource, &m_Matrix, dib_flags, &m_ClipBox);
+        pSource, m_Matrix, options, &m_ClipBox);
     return;
   }
 
@@ -78,16 +76,16 @@
   FX_RECT bitmap_clip = m_ClipBox;
   bitmap_clip.Offset(-image_rect.left, -image_rect.top);
   m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox,
-                     false, false, false, m_bRgbByteOrder, 0, m_BlendType);
+                     false, false, false, m_bRgbByteOrder, BlendMode::kNormal);
   m_Status = 1;
   m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
-      &m_Composer, pSource, dest_width, dest_height, bitmap_clip, dib_flags);
+      &m_Composer, pSource, dest_width, dest_height, bitmap_clip, options);
   m_Stretcher->Start();
 }
 
 CFX_ImageRenderer::~CFX_ImageRenderer() {}
 
-bool CFX_ImageRenderer::Continue(IFX_PauseIndicator* pPause) {
+bool CFX_ImageRenderer::Continue(PauseIndicatorIface* pPause) {
   if (m_Status == 1)
     return m_Stretcher->Continue(pPause);
   if (m_Status != 2)
@@ -100,25 +98,19 @@
     return false;
 
   if (pBitmap->IsAlphaMask()) {
-    if (m_BitmapAlpha != 255) {
-      if (m_AlphaFlag >> 8) {
-        m_AlphaFlag = (((uint8_t)((m_AlphaFlag & 0xff) * m_BitmapAlpha / 255)) |
-                       ((m_AlphaFlag >> 8) << 8));
-      } else {
-        m_MaskColor = FXARGB_MUL_ALPHA(m_MaskColor, m_BitmapAlpha);
-      }
-    }
+    if (m_BitmapAlpha != 255)
+      m_MaskColor = FXARGB_MUL_ALPHA(m_MaskColor, m_BitmapAlpha);
     m_pDevice->CompositeMask(
         m_pTransformer->result().left, m_pTransformer->result().top,
         pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, m_MaskColor, 0, 0,
-        m_BlendType, m_pClipRgn.Get(), m_bRgbByteOrder, m_AlphaFlag);
+        BlendMode::kNormal, m_pClipRgn.Get(), m_bRgbByteOrder);
   } else {
     if (m_BitmapAlpha != 255)
       pBitmap->MultiplyAlpha(m_BitmapAlpha);
     m_pDevice->CompositeBitmap(
         m_pTransformer->result().left, m_pTransformer->result().top,
-        pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, 0, 0, m_BlendType,
-        m_pClipRgn.Get(), m_bRgbByteOrder);
+        pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, 0, 0,
+        BlendMode::kNormal, m_pClipRgn.Get(), m_bRgbByteOrder);
   }
   return false;
 }
diff --git a/core/fxge/dib/cfx_imagerenderer.h b/core/fxge/dib/cfx_imagerenderer.h
index 18a56e2..a64fd4a 100644
--- a/core/fxge/dib/cfx_imagerenderer.h
+++ b/core/fxge/dib/cfx_imagerenderer.h
@@ -13,42 +13,39 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_bitmapcomposer.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/cfx_dibsource.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/stl_util.h"
 
+class CFX_DIBBase;
+class CFX_DIBitmap;
 class CFX_ImageTransformer;
 class CFX_ImageStretcher;
+class PauseIndicatorIface;
 
 class CFX_ImageRenderer {
  public:
   CFX_ImageRenderer(const RetainPtr<CFX_DIBitmap>& pDevice,
                     const CFX_ClipRgn* pClipRgn,
-                    const RetainPtr<CFX_DIBSource>& pSource,
+                    const RetainPtr<CFX_DIBBase>& pSource,
                     int bitmap_alpha,
                     uint32_t mask_color,
-                    const CFX_Matrix* pMatrix,
-                    uint32_t dib_flags,
+                    const CFX_Matrix& matrix,
+                    const FXDIB_ResampleOptions& options,
                     bool bRgbByteOrder);
   ~CFX_ImageRenderer();
 
-  bool Continue(IFX_PauseIndicator* pPause);
+  bool Continue(PauseIndicatorIface* pPause);
 
  private:
-  const RetainPtr<CFX_DIBitmap> m_pDevice;
-  const UnownedPtr<const CFX_ClipRgn> m_pClipRgn;
+  RetainPtr<CFX_DIBitmap> const m_pDevice;
+  UnownedPtr<const CFX_ClipRgn> const m_pClipRgn;
   const CFX_Matrix m_Matrix;
-  const int m_BitmapAlpha;
-  const int m_BlendType;
-  const bool m_bRgbByteOrder;
-  uint32_t m_MaskColor;
   std::unique_ptr<CFX_ImageTransformer> m_pTransformer;
   std::unique_ptr<CFX_ImageStretcher> m_Stretcher;
   CFX_BitmapComposer m_Composer;
-  int m_Status;
   FX_RECT m_ClipBox;
-  int m_AlphaFlag;
+  const int m_BitmapAlpha;
+  int m_Status = 0;
+  uint32_t m_MaskColor;
+  const bool m_bRgbByteOrder;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_IMAGERENDERER_H_
diff --git a/core/fxge/dib/cfx_imagestretcher.cpp b/core/fxge/dib/cfx_imagestretcher.cpp
index 03518c6..1499e8a 100644
--- a/core/fxge/dib/cfx_imagestretcher.cpp
+++ b/core/fxge/dib/cfx_imagestretcher.cpp
@@ -9,11 +9,13 @@
 #include <climits>
 #include <tuple>
 
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/cfx_dibsource.h"
 #include "core/fxge/dib/cstretchengine.h"
 #include "core/fxge/fx_dib.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -23,7 +25,7 @@
   return !height || width < kMaxProgressiveStretchPixels / height;
 }
 
-FXDIB_Format GetStretchedFormat(const CFX_DIBSource& src) {
+FXDIB_Format GetStretchedFormat(const CFX_DIBBase& src) {
   FXDIB_Format format = src.GetFormat();
   if (format == FXDIB_1bppMask)
     return FXDIB_8bppMask;
@@ -42,23 +44,22 @@
 
 }  // namespace
 
-CFX_ImageStretcher::CFX_ImageStretcher(IFX_ScanlineComposer* pDest,
-                                       const RetainPtr<CFX_DIBSource>& pSource,
+CFX_ImageStretcher::CFX_ImageStretcher(ScanlineComposerIface* pDest,
+                                       const RetainPtr<CFX_DIBBase>& pSource,
                                        int dest_width,
                                        int dest_height,
                                        const FX_RECT& bitmap_rect,
-                                       uint32_t flags)
+                                       const FXDIB_ResampleOptions& options)
     : m_pDest(pDest),
       m_pSource(pSource),
-      m_Flags(flags),
-      m_bFlipX(false),
-      m_bFlipY(false),
+      m_ResampleOptions(options),
       m_DestWidth(dest_width),
       m_DestHeight(dest_height),
       m_ClipRect(bitmap_rect),
       m_DestFormat(GetStretchedFormat(*pSource)),
-      m_DestBPP(m_DestFormat & 0xff),
-      m_LineIndex(0) {}
+      m_DestBPP(GetBppFromFormat(m_DestFormat)) {
+  ASSERT(m_ClipRect.Valid());
+}
 
 CFX_ImageStretcher::~CFX_ImageStretcher() {}
 
@@ -117,23 +118,21 @@
                                m_DestFormat, nullptr)) {
     return false;
   }
-
-  if (m_Flags & FXDIB_DOWNSAMPLE)
-    return StartQuickStretch();
-
   return StartStretch();
 }
 
-bool CFX_ImageStretcher::Continue(IFX_PauseIndicator* pPause) {
-  if (m_Flags & FXDIB_DOWNSAMPLE)
-    return ContinueQuickStretch(pPause);
+bool CFX_ImageStretcher::Continue(PauseIndicatorIface* pPause) {
   return ContinueStretch(pPause);
 }
 
+RetainPtr<CFX_DIBBase> CFX_ImageStretcher::source() {
+  return m_pSource;
+}
+
 bool CFX_ImageStretcher::StartStretch() {
   m_pStretchEngine = pdfium::MakeUnique<CStretchEngine>(
       m_pDest.Get(), m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect,
-      m_pSource, m_Flags);
+      m_pSource, m_ResampleOptions);
   m_pStretchEngine->StartStretchHorz();
   if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) {
     m_pStretchEngine->Continue(nullptr);
@@ -142,67 +141,6 @@
   return true;
 }
 
-bool CFX_ImageStretcher::ContinueStretch(IFX_PauseIndicator* pPause) {
+bool CFX_ImageStretcher::ContinueStretch(PauseIndicatorIface* pPause) {
   return m_pStretchEngine && m_pStretchEngine->Continue(pPause);
 }
-
-bool CFX_ImageStretcher::StartQuickStretch() {
-  if (m_DestWidth < 0) {
-    m_bFlipX = true;
-    m_DestWidth = -m_DestWidth;
-  }
-  if (m_DestHeight < 0) {
-    m_bFlipY = true;
-    m_DestHeight = -m_DestHeight;
-  }
-  uint32_t size = m_ClipRect.Width();
-  if (size && m_DestBPP > static_cast<int>(INT_MAX / size))
-    return false;
-
-  size *= m_DestBPP;
-  m_pScanline.reset(FX_Alloc(uint8_t, (size / 8 + 3) / 4 * 4));
-  if (m_pSource->m_pAlphaMask)
-    m_pMaskScanline.reset(FX_Alloc(uint8_t, (m_ClipRect.Width() + 3) / 4 * 4));
-
-  if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) {
-    ContinueQuickStretch(nullptr);
-    return false;
-  }
-  return true;
-}
-
-bool CFX_ImageStretcher::ContinueQuickStretch(IFX_PauseIndicator* pPause) {
-  if (!m_pScanline)
-    return false;
-
-  int result_width = m_ClipRect.Width();
-  int result_height = m_ClipRect.Height();
-  int src_height = m_pSource->GetHeight();
-  for (; m_LineIndex < result_height; ++m_LineIndex) {
-    int dest_y;
-    int src_y;
-    if (m_bFlipY) {
-      dest_y = result_height - m_LineIndex - 1;
-      src_y = (m_DestHeight - (dest_y + m_ClipRect.top) - 1) * src_height /
-              m_DestHeight;
-    } else {
-      dest_y = m_LineIndex;
-      src_y = (dest_y + m_ClipRect.top) * src_height / m_DestHeight;
-    }
-    src_y = pdfium::clamp(src_y, 0, src_height - 1);
-
-    if (m_pSource->SkipToScanline(src_y, pPause))
-      return true;
-
-    m_pSource->DownSampleScanline(src_y, m_pScanline.get(), m_DestBPP,
-                                  m_DestWidth, m_bFlipX, m_ClipRect.left,
-                                  result_width);
-    if (m_pMaskScanline) {
-      m_pSource->m_pAlphaMask->DownSampleScanline(
-          src_y, m_pMaskScanline.get(), 1, m_DestWidth, m_bFlipX,
-          m_ClipRect.left, result_width);
-    }
-    m_pDest->ComposeScanline(dest_y, m_pScanline.get(), m_pMaskScanline.get());
-  }
-  return false;
-}
diff --git a/core/fxge/dib/cfx_imagestretcher.h b/core/fxge/dib/cfx_imagestretcher.h
index 2ecb2a0..1ed9695 100644
--- a/core/fxge/dib/cfx_imagestretcher.h
+++ b/core/fxge/dib/cfx_imagestretcher.h
@@ -10,50 +10,46 @@
 #include <memory>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/dib/ifx_scanlinecomposer.h"
+#include "core/fxge/dib/scanlinecomposer_iface.h"
 #include "core/fxge/fx_dib.h"
 
-class CFX_DIBSource;
-class IFX_PauseIndicator;
+class CFX_DIBBase;
+class CStretchEngine;
+class PauseIndicatorIface;
 
 class CFX_ImageStretcher {
  public:
-  CFX_ImageStretcher(IFX_ScanlineComposer* pDest,
-                     const RetainPtr<CFX_DIBSource>& pSource,
+  CFX_ImageStretcher(ScanlineComposerIface* pDest,
+                     const RetainPtr<CFX_DIBBase>& pSource,
                      int dest_width,
                      int dest_height,
                      const FX_RECT& bitmap_rect,
-                     uint32_t flags);
+                     const FXDIB_ResampleOptions& options);
   ~CFX_ImageStretcher();
 
   bool Start();
-  bool Continue(IFX_PauseIndicator* pPause);
+  bool Continue(PauseIndicatorIface* pPause);
 
-  RetainPtr<CFX_DIBSource> source() { return m_pSource; }
+  RetainPtr<CFX_DIBBase> source();
 
  private:
-  bool StartQuickStretch();
   bool StartStretch();
-  bool ContinueQuickStretch(IFX_PauseIndicator* pPause);
-  bool ContinueStretch(IFX_PauseIndicator* pPause);
+  bool ContinueStretch(PauseIndicatorIface* pPause);
 
-  UnownedPtr<IFX_ScanlineComposer> const m_pDest;
-  RetainPtr<CFX_DIBSource> m_pSource;
+  UnownedPtr<ScanlineComposerIface> const m_pDest;
+  RetainPtr<CFX_DIBBase> m_pSource;
   std::unique_ptr<CStretchEngine> m_pStretchEngine;
   std::unique_ptr<uint8_t, FxFreeDeleter> m_pScanline;
   std::unique_ptr<uint8_t, FxFreeDeleter> m_pMaskScanline;
-  const uint32_t m_Flags;
-  bool m_bFlipX;
-  bool m_bFlipY;
+  const FXDIB_ResampleOptions m_ResampleOptions;
   int m_DestWidth;
   int m_DestHeight;
-  FX_RECT m_ClipRect;
+  const FX_RECT m_ClipRect;
   const FXDIB_Format m_DestFormat;
   const int m_DestBPP;
-  int m_LineIndex;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_IMAGESTRETCHER_H_
diff --git a/core/fxge/dib/cfx_imagetransformer.cpp b/core/fxge/dib/cfx_imagetransformer.cpp
index 8e01127..3afc48c 100644
--- a/core/fxge/dib/cfx_imagetransformer.cpp
+++ b/core/fxge/dib/cfx_imagetransformer.cpp
@@ -10,10 +10,13 @@
 #include <memory>
 #include <utility>
 
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "core/fxge/fx_dib.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -98,7 +101,7 @@
   v_w[3] = SDP_Table[512 - res_y];
 }
 
-FXDIB_Format GetTransformedFormat(const RetainPtr<CFX_DIBSource>& pDrc) {
+FXDIB_Format GetTransformedFormat(const RetainPtr<CFX_DIBBase>& pDrc) {
   if (pDrc->IsAlphaMask())
     return FXDIB_8bppMask;
 
@@ -110,10 +113,6 @@
   return FXDIB_Rgba;
 }
 
-bool NeedAlpha(bool bHasAlpha, FXDIB_Format format) {
-  return bHasAlpha || format == FXDIB_Cmyka;
-}
-
 void WriteMonoResult(uint32_t r_bgra_cmyk, FXDIB_Format format, uint8_t* dest) {
   if (format == FXDIB_Rgba) {
     dest[0] = static_cast<uint8_t>(r_bgra_cmyk >> 24);
@@ -124,46 +123,47 @@
   }
 }
 
-void WriteColorResult(std::function<uint8_t(int offset)> func,
+// Let the compiler deduce the type for |func|, which cheaper than specifying it
+// with std::function.
+template <typename F>
+void WriteColorResult(const F& func,
                       bool bHasAlpha,
                       FXDIB_Format format,
                       uint8_t* dest) {
   uint8_t blue_c = func(0);
   uint8_t green_m = func(1);
   uint8_t red_y = func(2);
-  uint8_t alpha_k = NeedAlpha(bHasAlpha, format) ? func(3) : kOpaqueAlpha;
 
   uint32_t* dest32 = reinterpret_cast<uint32_t*>(dest);
   if (bHasAlpha) {
     if (format == FXDIB_Argb) {
-      *dest32 = FXARGB_TODIB(FXARGB_MAKE(alpha_k, red_y, green_m, blue_c));
+      *dest32 = FXARGB_TODIB(ArgbEncode(func(3), red_y, green_m, blue_c));
     } else if (format == FXDIB_Rgba) {
       dest[0] = blue_c;
       dest[1] = green_m;
       dest[2] = red_y;
     } else {
-      *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, alpha_k));
+      *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3)));
     }
     return;
   }
 
   if (format == FXDIB_Cmyka) {
-    *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, alpha_k));
+    *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3)));
   } else {
-    ASSERT(alpha_k == kOpaqueAlpha);
-    *dest32 = FXARGB_TODIB(FXARGB_MAKE(kOpaqueAlpha, red_y, green_m, blue_c));
+    *dest32 = FXARGB_TODIB(ArgbEncode(kOpaqueAlpha, red_y, green_m, blue_c));
   }
 }
 
 class CPDF_FixedMatrix {
  public:
   explicit CPDF_FixedMatrix(const CFX_Matrix& src)
-      : a(FXSYS_round(src.a * kBase)),
-        b(FXSYS_round(src.b * kBase)),
-        c(FXSYS_round(src.c * kBase)),
-        d(FXSYS_round(src.d * kBase)),
-        e(FXSYS_round(src.e * kBase)),
-        f(FXSYS_round(src.f * kBase)) {}
+      : a(FXSYS_roundf(src.a * kBase)),
+        b(FXSYS_roundf(src.b * kBase)),
+        c(FXSYS_roundf(src.c * kBase)),
+        d(FXSYS_roundf(src.d * kBase)),
+        e(FXSYS_roundf(src.e * kBase)),
+        f(FXSYS_roundf(src.f * kBase)) {}
 
   void Transform(int x, int y, int* x1, int* y1) const {
     std::pair<float, float> val = TransformInternal(x, y);
@@ -185,7 +185,7 @@
   const int f;
 };
 
-class CFX_BilinearMatrix : public CPDF_FixedMatrix {
+class CFX_BilinearMatrix final : public CPDF_FixedMatrix {
  public:
   explicit CFX_BilinearMatrix(const CFX_Matrix& src) : CPDF_FixedMatrix(src) {}
 
@@ -194,109 +194,232 @@
     *x1 = pdfium::base::saturated_cast<int>(val.first / kBase);
     *y1 = pdfium::base::saturated_cast<int>(val.second / kBase);
 
-    *res_x = static_cast<int>(fmodf(val.first, kBase));
-    *res_y = static_cast<int>(fmodf(val.second, kBase));
+    *res_x = static_cast<int>(val.first) % kBase;
+    *res_y = static_cast<int>(val.second) % kBase;
     if (*res_x < 0 && *res_x > -kBase)
       *res_x = kBase + *res_x;
-    if (*res_y < 0 && *res_x > -kBase)
+    if (*res_y < 0 && *res_y > -kBase)
       *res_y = kBase + *res_y;
   }
 };
 
+bool InStretchBounds(const FX_RECT& clip_rect, int col, int row) {
+  return col >= 0 && col <= clip_rect.Width() && row >= 0 &&
+         row <= clip_rect.Height();
+}
+
+void AdjustCoords(const FX_RECT& clip_rect, int* col, int* row) {
+  int& src_col = *col;
+  int& src_row = *row;
+  if (src_col == clip_rect.Width())
+    src_col--;
+  if (src_row == clip_rect.Height())
+    src_row--;
+}
+
+// Let the compiler deduce the type for |func|, which cheaper than specifying it
+// with std::function.
+template <typename F>
+void DoBilinearLoop(const CFX_ImageTransformer::CalcData& cdata,
+                    const FX_RECT& result_rect,
+                    const FX_RECT& clip_rect,
+                    int increment,
+                    const F& func) {
+  CFX_BilinearMatrix matrix_fix(cdata.matrix);
+  for (int row = 0; row < result_rect.Height(); row++) {
+    uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
+    for (int col = 0; col < result_rect.Width(); col++) {
+      CFX_ImageTransformer::BilinearData d;
+      d.res_x = 0;
+      d.res_y = 0;
+      d.src_col_l = 0;
+      d.src_row_l = 0;
+      matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
+                           &d.res_y);
+      if (LIKELY(InStretchBounds(clip_rect, d.src_col_l, d.src_row_l))) {
+        AdjustCoords(clip_rect, &d.src_col_l, &d.src_row_l);
+        d.src_col_r = d.src_col_l + 1;
+        d.src_row_r = d.src_row_l + 1;
+        AdjustCoords(clip_rect, &d.src_col_r, &d.src_row_r);
+        d.row_offset_l = d.src_row_l * cdata.pitch;
+        d.row_offset_r = d.src_row_r * cdata.pitch;
+        func(d, dest);
+      }
+      dest += increment;
+    }
+  }
+}
+
+// Let the compiler deduce the type for |func|, which cheaper than specifying it
+// with std::function.
+template <typename F>
+void DoBicubicLoop(const CFX_ImageTransformer::CalcData& cdata,
+                   const FX_RECT& result_rect,
+                   const FX_RECT& clip_rect,
+                   int increment,
+                   const F& func) {
+  CFX_BilinearMatrix matrix_fix(cdata.matrix);
+  for (int row = 0; row < result_rect.Height(); row++) {
+    uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
+    for (int col = 0; col < result_rect.Width(); col++) {
+      CFX_ImageTransformer::BicubicData d;
+      d.res_x = 0;
+      d.res_y = 0;
+      d.src_col_l = 0;
+      d.src_row_l = 0;
+      matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
+                           &d.res_y);
+      if (LIKELY(InStretchBounds(clip_rect, d.src_col_l, d.src_row_l))) {
+        AdjustCoords(clip_rect, &d.src_col_l, &d.src_row_l);
+        bicubic_get_pos_weight(d.pos_pixel, d.u_w, d.v_w, d.src_col_l,
+                               d.src_row_l, d.res_x, d.res_y, clip_rect.Width(),
+                               clip_rect.Height());
+        func(d, dest);
+      }
+      dest += increment;
+    }
+  }
+}
+
+// Let the compiler deduce the type for |func|, which cheaper than specifying it
+// with std::function.
+template <typename F>
+void DoDownSampleLoop(const CFX_ImageTransformer::CalcData& cdata,
+                      const FX_RECT& result_rect,
+                      const FX_RECT& clip_rect,
+                      int increment,
+                      const F& func) {
+  CPDF_FixedMatrix matrix_fix(cdata.matrix);
+  for (int row = 0; row < result_rect.Height(); row++) {
+    uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
+    for (int col = 0; col < result_rect.Width(); col++) {
+      CFX_ImageTransformer::DownSampleData d;
+      d.src_col = 0;
+      d.src_row = 0;
+      matrix_fix.Transform(col, row, &d.src_col, &d.src_row);
+      if (LIKELY(InStretchBounds(clip_rect, d.src_col, d.src_row))) {
+        AdjustCoords(clip_rect, &d.src_col, &d.src_row);
+        func(d, dest);
+      }
+      dest += increment;
+    }
+  }
+}
+
 }  // namespace
 
-CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr<CFX_DIBSource>& pSrc,
-                                           const CFX_Matrix* pMatrix,
-                                           int flags,
+CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr<CFX_DIBBase>& pSrc,
+                                           const CFX_Matrix& matrix,
+                                           const FXDIB_ResampleOptions& options,
                                            const FX_RECT* pClip)
-    : m_pSrc(pSrc),
-      m_pMatrix(pMatrix),
-      m_pClip(pClip),
-      m_Flags(flags),
-      m_Status(0) {
-  FX_RECT result_rect = m_pMatrix->GetUnitRect().GetClosestRect();
+    : m_pSrc(pSrc), m_matrix(matrix), m_ResampleOptions(options) {
+  FX_RECT result_rect = m_matrix.GetUnitRect().GetClosestRect();
   FX_RECT result_clip = result_rect;
-  if (m_pClip)
-    result_clip.Intersect(*m_pClip);
+  if (pClip)
+    result_clip.Intersect(*pClip);
 
   if (result_clip.IsEmpty())
     return;
 
   m_result = result_clip;
-  if (fabs(m_pMatrix->a) < fabs(m_pMatrix->b) / 20 &&
-      fabs(m_pMatrix->d) < fabs(m_pMatrix->c) / 20 &&
-      fabs(m_pMatrix->a) < 0.5f && fabs(m_pMatrix->d) < 0.5f) {
+  if (fabs(m_matrix.a) < fabs(m_matrix.b) / 20 &&
+      fabs(m_matrix.d) < fabs(m_matrix.c) / 20 && fabs(m_matrix.a) < 0.5f &&
+      fabs(m_matrix.d) < 0.5f) {
     int dest_width = result_rect.Width();
     int dest_height = result_rect.Height();
     result_clip.Offset(-result_rect.left, -result_rect.top);
     result_clip = FXDIB_SwapClipBox(result_clip, dest_width, dest_height,
-                                    m_pMatrix->c > 0, m_pMatrix->b < 0);
+                                    m_matrix.c > 0, m_matrix.b < 0);
     m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
-        &m_Storer, m_pSrc, dest_height, dest_width, result_clip, m_Flags);
+        &m_Storer, m_pSrc, dest_height, dest_width, result_clip,
+        m_ResampleOptions);
     m_Stretcher->Start();
-    m_Status = 1;
+    m_type = kRotate;
     return;
   }
-  if (fabs(m_pMatrix->b) < kFix16 && fabs(m_pMatrix->c) < kFix16) {
-    int dest_width = static_cast<int>(m_pMatrix->a > 0 ? ceil(m_pMatrix->a)
-                                                       : floor(m_pMatrix->a));
-    int dest_height = static_cast<int>(m_pMatrix->d > 0 ? -ceil(m_pMatrix->d)
-                                                        : -floor(m_pMatrix->d));
+  if (fabs(m_matrix.b) < kFix16 && fabs(m_matrix.c) < kFix16) {
+    int dest_width =
+        static_cast<int>(m_matrix.a > 0 ? ceil(m_matrix.a) : floor(m_matrix.a));
+    int dest_height = static_cast<int>(m_matrix.d > 0 ? -ceil(m_matrix.d)
+                                                      : -floor(m_matrix.d));
     result_clip.Offset(-result_rect.left, -result_rect.top);
     m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
-        &m_Storer, m_pSrc, dest_width, dest_height, result_clip, m_Flags);
+        &m_Storer, m_pSrc, dest_width, dest_height, result_clip,
+        m_ResampleOptions);
     m_Stretcher->Start();
-    m_Status = 2;
+    m_type = kNormal;
     return;
   }
-  int stretch_width =
-      static_cast<int>(ceil(FXSYS_sqrt2(m_pMatrix->a, m_pMatrix->b)));
-  int stretch_height =
-      static_cast<int>(ceil(FXSYS_sqrt2(m_pMatrix->c, m_pMatrix->d)));
-  CFX_Matrix stretch2dest(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, stretch_height);
-  stretch2dest.Concat(
-      CFX_Matrix(m_pMatrix->a / stretch_width, m_pMatrix->b / stretch_width,
-                 m_pMatrix->c / stretch_height, m_pMatrix->d / stretch_height,
-                 m_pMatrix->e, m_pMatrix->f));
-  m_dest2stretch = stretch2dest.GetInverse();
 
-  m_StretchClip =
-      m_dest2stretch.TransformRect(CFX_FloatRect(result_clip)).GetOuterRect();
-  m_StretchClip.Intersect(0, 0, stretch_width, stretch_height);
+  int stretch_width =
+      static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.a, m_matrix.b)));
+  int stretch_height =
+      static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.c, m_matrix.d)));
+  CFX_Matrix stretch_to_dest(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, stretch_height);
+  stretch_to_dest.Concat(
+      CFX_Matrix(m_matrix.a / stretch_width, m_matrix.b / stretch_width,
+                 m_matrix.c / stretch_height, m_matrix.d / stretch_height,
+                 m_matrix.e, m_matrix.f));
+  CFX_Matrix dest_to_strech = stretch_to_dest.GetInverse();
+
+  FX_RECT stretch_clip =
+      dest_to_strech.TransformRect(CFX_FloatRect(result_clip)).GetOuterRect();
+  if (!stretch_clip.Valid())
+    return;
+
+  stretch_clip.Intersect(0, 0, stretch_width, stretch_height);
+  if (!stretch_clip.Valid())
+    return;
+
+  m_dest2stretch = dest_to_strech;
+  m_StretchClip = stretch_clip;
   m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
-      &m_Storer, m_pSrc, stretch_width, stretch_height, m_StretchClip, m_Flags);
+      &m_Storer, m_pSrc, stretch_width, stretch_height, m_StretchClip,
+      m_ResampleOptions);
   m_Stretcher->Start();
-  m_Status = 3;
+  m_type = kOther;
 }
 
-CFX_ImageTransformer::~CFX_ImageTransformer() {}
+CFX_ImageTransformer::~CFX_ImageTransformer() = default;
 
-bool CFX_ImageTransformer::Continue(IFX_PauseIndicator* pPause) {
-  if (m_Status == 1) {
-    if (m_Stretcher->Continue(pPause))
-      return true;
-
-    if (m_Storer.GetBitmap()) {
-      m_Storer.Replace(
-          m_Storer.GetBitmap()->SwapXY(m_pMatrix->c > 0, m_pMatrix->b < 0));
-    }
+bool CFX_ImageTransformer::Continue(PauseIndicatorIface* pPause) {
+  if (m_type == kNone)
     return false;
-  }
 
-  if (m_Status == 2)
-    return m_Stretcher->Continue(pPause);
-  if (m_Status != 3)
-    return false;
   if (m_Stretcher->Continue(pPause))
     return true;
 
+  switch (m_type) {
+    case kNormal:
+      break;
+    case kRotate:
+      ContinueRotate(pPause);
+      break;
+    case kOther:
+      ContinueOther(pPause);
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+  return false;
+}
+
+void CFX_ImageTransformer::ContinueRotate(PauseIndicatorIface* pPause) {
+  if (m_Storer.GetBitmap()) {
+    m_Storer.Replace(
+        m_Storer.GetBitmap()->SwapXY(m_matrix.c > 0, m_matrix.b < 0));
+  }
+}
+
+void CFX_ImageTransformer::ContinueOther(PauseIndicatorIface* pPause) {
   if (!m_Storer.GetBitmap())
-    return false;
+    return;
 
   auto pTransformed = pdfium::MakeRetain<CFX_DIBitmap>();
   FXDIB_Format format = GetTransformedFormat(m_Stretcher->source());
   if (!pTransformed->Create(m_result.Width(), m_result.Height(), format))
-    return false;
+    return;
 
   const auto& pSrcMask = m_Storer.GetBitmap()->m_pAlphaMask;
   const uint8_t* pSrcMaskBuf = pSrcMask ? pSrcMask->GetBuffer() : nullptr;
@@ -314,7 +437,9 @@
     pDestMask->Clear(0xff000000);
   } else if (pDestMask) {
     CalcData cdata = {
-        pDestMask.Get(), result2stretch, pSrcMaskBuf,
+        pDestMask.Get(),
+        result2stretch,
+        pSrcMaskBuf,
         m_Storer.GetBitmap()->m_pAlphaMask->GetPitch(),
     };
     CalcMask(cdata);
@@ -333,7 +458,6 @@
       CalcColor(cdata, format, Bpp);
   }
   m_Storer.Replace(std::move(pTransformed));
-  return false;
 }
 
 RetainPtr<CFX_DIBitmap> CFX_ImageTransformer::DetachBitmap() {
@@ -347,18 +471,18 @@
                                 data.src_col_l, data.src_col_r, data.res_x,
                                 data.res_y, 1, 0);
     };
-    DoBilinearLoop(cdata, 1, func);
+    DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func);
   } else if (IsBiCubic()) {
     auto func = [&cdata](const BicubicData& data, uint8_t* dest) {
       *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w,
                                data.v_w, data.res_x, data.res_y, 1, 0);
     };
-    DoBicubicLoop(cdata, 1, func);
+    DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func);
   } else {
     auto func = [&cdata](const DownSampleData& data, uint8_t* dest) {
       *dest = cdata.buf[data.src_row * cdata.pitch + data.src_col];
     };
-    DoDownSampleLoop(cdata, 1, func);
+    DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func);
   }
 }
 
@@ -369,20 +493,20 @@
                                 data.src_col_l, data.src_col_r, data.res_x,
                                 data.res_y, 1, 0);
     };
-    DoBilinearLoop(cdata, 1, func);
+    DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func);
   } else if (IsBiCubic()) {
     auto func = [&cdata](const BicubicData& data, uint8_t* dest) {
       *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w,
                                data.v_w, data.res_x, data.res_y, 1, 0);
     };
-    DoBicubicLoop(cdata, 1, func);
+    DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func);
   } else {
     auto func = [&cdata](const DownSampleData& data, uint8_t* dest) {
       const uint8_t* src_pixel =
           cdata.buf + cdata.pitch * data.src_row + data.src_col;
       *dest = *src_pixel;
     };
-    DoDownSampleLoop(cdata, 1, func);
+    DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func);
   }
 }
 
@@ -410,7 +534,7 @@
       uint32_t r_bgra_cmyk = argb[idx];
       WriteMonoResult(r_bgra_cmyk, format, dest);
     };
-    DoBilinearLoop(cdata, destBpp, func);
+    DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func);
   } else if (IsBiCubic()) {
     auto func = [&cdata, format, &argb](const BicubicData& data,
                                         uint8_t* dest) {
@@ -419,7 +543,7 @@
           data.res_x, data.res_y, 1, 0)];
       WriteMonoResult(r_bgra_cmyk, format, dest);
     };
-    DoBicubicLoop(cdata, destBpp, func);
+    DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func);
   } else {
     auto func = [&cdata, format, &argb](const DownSampleData& data,
                                         uint8_t* dest) {
@@ -427,7 +551,7 @@
           argb[cdata.buf[data.src_row * cdata.pitch + data.src_col]];
       WriteMonoResult(r_bgra_cmyk, format, dest);
     };
-    DoDownSampleLoop(cdata, destBpp, func);
+    DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func);
   }
 }
 
@@ -446,7 +570,7 @@
       };
       WriteColorResult(bilinear_interpol_func, bHasAlpha, format, dest);
     };
-    DoBilinearLoop(cdata, destBpp, func);
+    DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func);
   } else if (IsBiCubic()) {
     auto func = [&cdata, format, Bpp, bHasAlpha](const BicubicData& data,
                                                  uint8_t* dest) {
@@ -457,7 +581,7 @@
       };
       WriteColorResult(bicubic_interpol_func, bHasAlpha, format, dest);
     };
-    DoBicubicLoop(cdata, destBpp, func);
+    DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func);
   } else {
     auto func = [&cdata, format, bHasAlpha, Bpp](const DownSampleData& data,
                                                  uint8_t* dest) {
@@ -466,92 +590,14 @@
       auto sample_func = [src_pos](int offset) { return src_pos[offset]; };
       WriteColorResult(sample_func, bHasAlpha, format, dest);
     };
-    DoDownSampleLoop(cdata, destBpp, func);
+    DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func);
   }
 }
 
-void CFX_ImageTransformer::AdjustCoords(int* col, int* row) const {
-  int& src_col = *col;
-  int& src_row = *row;
-  if (src_col == stretch_width())
-    src_col--;
-  if (src_row == stretch_height())
-    src_row--;
+bool CFX_ImageTransformer::IsBilinear() const {
+  return !IsBiCubic();
 }
 
-void CFX_ImageTransformer::DoBilinearLoop(
-    const CalcData& cdata,
-    int increment,
-    std::function<void(const BilinearData&, uint8_t*)> func) {
-  CFX_BilinearMatrix matrix_fix(cdata.matrix);
-  for (int row = 0; row < m_result.Height(); row++) {
-    uint8_t* dest = const_cast<uint8_t*>(cdata.bitmap->GetScanline(row));
-    for (int col = 0; col < m_result.Width(); col++) {
-      BilinearData d;
-      d.res_x = 0;
-      d.res_y = 0;
-      d.src_col_l = 0;
-      d.src_row_l = 0;
-      matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
-                           &d.res_y);
-      if (InStretchBounds(d.src_col_l, d.src_row_l)) {
-        AdjustCoords(&d.src_col_l, &d.src_row_l);
-        d.src_col_r = d.src_col_l + 1;
-        d.src_row_r = d.src_row_l + 1;
-        AdjustCoords(&d.src_col_r, &d.src_row_r);
-        d.row_offset_l = d.src_row_l * cdata.pitch;
-        d.row_offset_r = d.src_row_r * cdata.pitch;
-        func(d, dest);
-      }
-      dest += increment;
-    }
-  }
-}
-
-void CFX_ImageTransformer::DoBicubicLoop(
-    const CalcData& cdata,
-    int increment,
-    std::function<void(const BicubicData&, uint8_t*)> func) {
-  CFX_BilinearMatrix matrix_fix(cdata.matrix);
-  for (int row = 0; row < m_result.Height(); row++) {
-    uint8_t* dest = const_cast<uint8_t*>(cdata.bitmap->GetScanline(row));
-    for (int col = 0; col < m_result.Width(); col++) {
-      BicubicData d;
-      d.res_x = 0;
-      d.res_y = 0;
-      d.src_col_l = 0;
-      d.src_row_l = 0;
-      matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
-                           &d.res_y);
-      if (InStretchBounds(d.src_col_l, d.src_row_l)) {
-        AdjustCoords(&d.src_col_l, &d.src_row_l);
-        bicubic_get_pos_weight(d.pos_pixel, d.u_w, d.v_w, d.src_col_l,
-                               d.src_row_l, d.res_x, d.res_y, stretch_width(),
-                               stretch_height());
-        func(d, dest);
-      }
-      dest += increment;
-    }
-  }
-}
-
-void CFX_ImageTransformer::DoDownSampleLoop(
-    const CalcData& cdata,
-    int increment,
-    std::function<void(const DownSampleData&, uint8_t*)> func) {
-  CPDF_FixedMatrix matrix_fix(cdata.matrix);
-  for (int row = 0; row < m_result.Height(); row++) {
-    uint8_t* dest = const_cast<uint8_t*>(cdata.bitmap->GetScanline(row));
-    for (int col = 0; col < m_result.Width(); col++) {
-      DownSampleData d;
-      d.src_col = 0;
-      d.src_row = 0;
-      matrix_fix.Transform(col, row, &d.src_col, &d.src_row);
-      if (InStretchBounds(d.src_col, d.src_row)) {
-        AdjustCoords(&d.src_col, &d.src_row);
-        func(d, dest);
-      }
-      dest += increment;
-    }
-  }
+bool CFX_ImageTransformer::IsBiCubic() const {
+  return m_ResampleOptions.bInterpolateBicubic;
 }
diff --git a/core/fxge/dib/cfx_imagetransformer.h b/core/fxge/dib/cfx_imagetransformer.h
index d13ce3f..3372003 100644
--- a/core/fxge/dib/cfx_imagetransformer.h
+++ b/core/fxge/dib/cfx_imagetransformer.h
@@ -11,27 +11,15 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_bitmapstorer.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/cfx_dibsource.h"
 
+class CFX_DIBBase;
+class CFX_DIBitmap;
 class CFX_ImageStretcher;
+class PauseIndicatorIface;
 
 class CFX_ImageTransformer {
  public:
-  CFX_ImageTransformer(const RetainPtr<CFX_DIBSource>& pSrc,
-                       const CFX_Matrix* pMatrix,
-                       int flags,
-                       const FX_RECT* pClip);
-  ~CFX_ImageTransformer();
-
-  bool Continue(IFX_PauseIndicator* pPause);
-
-  const FX_RECT& result() const { return m_result; }
-  RetainPtr<CFX_DIBitmap> DetachBitmap();
-
- private:
   struct BilinearData {
     int res_x;
     int res_y;
@@ -61,53 +49,51 @@
   };
 
   struct CalcData {
-    const CFX_DIBitmap* bitmap;
+    CFX_DIBitmap* bitmap;
     const CFX_Matrix& matrix;
     const uint8_t* buf;
     uint32_t pitch;
   };
 
+  CFX_ImageTransformer(const RetainPtr<CFX_DIBBase>& pSrc,
+                       const CFX_Matrix& matrix,
+                       const FXDIB_ResampleOptions& options,
+                       const FX_RECT* pClip);
+  ~CFX_ImageTransformer();
+
+  bool Continue(PauseIndicatorIface* pPause);
+
+  const FX_RECT& result() const { return m_result; }
+  RetainPtr<CFX_DIBitmap> DetachBitmap();
+
+ private:
+  enum StretchType {
+    kNone,
+    kNormal,
+    kRotate,
+    kOther,
+  };
+
+  void ContinueRotate(PauseIndicatorIface* pPause);
+  void ContinueOther(PauseIndicatorIface* pPause);
+
   void CalcMask(const CalcData& cdata);
   void CalcAlpha(const CalcData& cdata);
   void CalcMono(const CalcData& cdata, FXDIB_Format format);
   void CalcColor(const CalcData& cdata, FXDIB_Format format, int Bpp);
 
-  bool IsBilinear() const {
-    return !(m_Flags & FXDIB_DOWNSAMPLE) && !IsBiCubic();
-  }
-  bool IsBiCubic() const { return !!(m_Flags & FXDIB_BICUBIC_INTERPOL); }
+  bool IsBilinear() const;
+  bool IsBiCubic() const;
 
-  int stretch_width() const { return m_StretchClip.Width(); }
-  int stretch_height() const { return m_StretchClip.Height(); }
-
-  bool InStretchBounds(int col, int row) const {
-    return col >= 0 && col <= stretch_width() && row >= 0 &&
-           row <= stretch_height();
-  }
-
-  void AdjustCoords(int* col, int* row) const;
-
-  void DoBilinearLoop(const CalcData& cdata,
-                      int increment,
-                      std::function<void(const BilinearData&, uint8_t*)> func);
-  void DoBicubicLoop(const CalcData& cdata,
-                     int increment,
-                     std::function<void(const BicubicData&, uint8_t*)> func);
-  void DoDownSampleLoop(
-      const CalcData& cdata,
-      int increment,
-      std::function<void(const DownSampleData&, uint8_t*)> func);
-
-  const RetainPtr<CFX_DIBSource> m_pSrc;
-  UnownedPtr<const CFX_Matrix> const m_pMatrix;
-  const FX_RECT* const m_pClip;
+  RetainPtr<CFX_DIBBase> const m_pSrc;
+  const CFX_Matrix m_matrix;
   FX_RECT m_StretchClip;
   FX_RECT m_result;
   CFX_Matrix m_dest2stretch;
   std::unique_ptr<CFX_ImageStretcher> m_Stretcher;
   CFX_BitmapStorer m_Storer;
-  const uint32_t m_Flags;
-  int m_Status;
+  const FXDIB_ResampleOptions m_ResampleOptions;
+  StretchType m_type = kNone;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_IMAGETRANSFORMER_H_
diff --git a/core/fxge/dib/cfx_scanlinecompositor.cpp b/core/fxge/dib/cfx_scanlinecompositor.cpp
index a1254e2..3c90d96 100644
--- a/core/fxge/dib/cfx_scanlinecompositor.cpp
+++ b/core/fxge/dib/cfx_scanlinecompositor.cpp
@@ -8,7 +8,7 @@
 
 #include <algorithm>
 
-#include "core/fxcodec/fx_codec.h"
+#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
 
 #define FX_CCOLOR(val) (255 - (val))
 #define FXDIB_ALPHA_UNION(dest, src) ((dest) + (src) - (dest) * (src) / 255)
@@ -45,54 +45,53 @@
     0xF7, 0xF8, 0xF8, 0xF9, 0xF9, 0xFA, 0xFA, 0xFB, 0xFB, 0xFC, 0xFC, 0xFD,
     0xFD, 0xFE, 0xFE, 0xFF};
 
-int Blend(int blend_mode, int back_color, int src_color) {
+int Blend(BlendMode blend_mode, int back_color, int src_color) {
   switch (blend_mode) {
-    case FXDIB_BLEND_NORMAL:
+    case BlendMode::kNormal:
       return src_color;
-    case FXDIB_BLEND_MULTIPLY:
+    case BlendMode::kMultiply:
       return src_color * back_color / 255;
-    case FXDIB_BLEND_SCREEN:
+    case BlendMode::kScreen:
       return src_color + back_color - src_color * back_color / 255;
-    case FXDIB_BLEND_OVERLAY:
-      return Blend(FXDIB_BLEND_HARDLIGHT, src_color, back_color);
-    case FXDIB_BLEND_DARKEN:
+    case BlendMode::kOverlay:
+      return Blend(BlendMode::kHardLight, src_color, back_color);
+    case BlendMode::kDarken:
       return src_color < back_color ? src_color : back_color;
-    case FXDIB_BLEND_LIGHTEN:
+    case BlendMode::kLighten:
       return src_color > back_color ? src_color : back_color;
-    case FXDIB_BLEND_COLORDODGE: {
+    case BlendMode::kColorDodge: {
       if (src_color == 255)
         return src_color;
 
       return std::min(back_color * 255 / (255 - src_color), 255);
     }
-    case FXDIB_BLEND_COLORBURN: {
+    case BlendMode::kColorBurn: {
       if (src_color == 0)
         return src_color;
 
       return 255 - std::min((255 - back_color) * 255 / src_color, 255);
     }
-    case FXDIB_BLEND_HARDLIGHT:
+    case BlendMode::kHardLight:
       if (src_color < 128)
         return (src_color * back_color * 2) / 255;
 
-      return Blend(FXDIB_BLEND_SCREEN, back_color, 2 * src_color - 255);
-    case FXDIB_BLEND_SOFTLIGHT: {
+      return Blend(BlendMode::kScreen, back_color, 2 * src_color - 255);
+    case BlendMode::kSoftLight: {
       if (src_color < 128) {
-        return back_color -
-               (255 - 2 * src_color) * back_color * (255 - back_color) / 255 /
-                   255;
+        return back_color - (255 - 2 * src_color) * back_color *
+                                (255 - back_color) / 255 / 255;
       }
-      return back_color +
-             (2 * src_color - 255) * (color_sqrt[back_color] - back_color) /
-                 255;
+      return back_color + (2 * src_color - 255) *
+                              (color_sqrt[back_color] - back_color) / 255;
     }
-    case FXDIB_BLEND_DIFFERENCE:
+    case BlendMode::kDifference:
       return back_color < src_color ? src_color - back_color
                                     : back_color - src_color;
-    case FXDIB_BLEND_EXCLUSION:
+    case BlendMode::kExclusion:
       return back_color + src_color - 2 * back_color * src_color / 255;
+    default:
+      return src_color;
   }
-  return src_color;
 }
 
 struct RGB {
@@ -147,7 +146,7 @@
   return color;
 }
 
-void RGB_Blend(int blend_mode,
+void RGB_Blend(BlendMode blend_mode,
                const uint8_t* src_scan,
                const uint8_t* dest_scan,
                int results[3]) {
@@ -161,18 +160,20 @@
   back.green = dest_scan[1];
   back.blue = dest_scan[0];
   switch (blend_mode) {
-    case FXDIB_BLEND_HUE:
+    case BlendMode::kHue:
       result = SetLum(SetSat(src, Sat(back)), Lum(back));
       break;
-    case FXDIB_BLEND_SATURATION:
+    case BlendMode::kSaturation:
       result = SetLum(SetSat(back, Sat(src)), Lum(back));
       break;
-    case FXDIB_BLEND_COLOR:
+    case BlendMode::kColor:
       result = SetLum(src, Lum(back));
       break;
-    case FXDIB_BLEND_LUMINOSITY:
+    case BlendMode::kLuminosity:
       result = SetLum(back, Lum(src));
       break;
+    default:
+      break;
   }
   results[0] = result.blue;
   results[1] = result.green;
@@ -183,6 +184,18 @@
   return clip_scan ? clip_scan[col] * src_alpha / 255 : src_alpha;
 }
 
+int GetAlphaWithSrc(uint8_t src_alpha,
+                    const uint8_t* clip_scan,
+                    const uint8_t* src_scan,
+                    int col) {
+  int result = src_alpha * src_scan[col];
+  if (clip_scan) {
+    result *= clip_scan[col];
+    result /= 255;
+  }
+  return result / 255;
+}
+
 void CompositeRow_AlphaToMask(uint8_t* dest_scan,
                               const uint8_t* src_scan,
                               int pixel_count,
@@ -216,17 +229,29 @@
   }
 }
 
+bool IsNonSeparableBlendMode(BlendMode mode) {
+  switch (mode) {
+    case BlendMode::kHue:
+    case BlendMode::kSaturation:
+    case BlendMode::kColor:
+    case BlendMode::kLuminosity:
+      return true;
+    default:
+      return false;
+  }
+}
+
 uint8_t GetGray(const uint8_t* src_scan) {
   return FXRGB2GRAY(src_scan[2], src_scan[1], *src_scan);
 }
 
 uint8_t GetGrayWithBlend(const uint8_t* src_scan,
                          const uint8_t* dest_scan,
-                         int blend_type) {
+                         BlendMode blend_type) {
   uint8_t gray = GetGray(src_scan);
-  if (blend_type >= FXDIB_BLEND_NONSEPARABLE)
-    gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
-  else if (blend_type)
+  if (IsNonSeparableBlendMode(blend_type))
+    gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
+  else if (blend_type != BlendMode::kNormal)
     gray = Blend(blend_type, *dest_scan, gray);
   return gray;
 }
@@ -234,7 +259,7 @@
 void CompositeRow_Argb2Graya(uint8_t* dest_scan,
                              const uint8_t* src_scan,
                              int pixel_count,
-                             int blend_type,
+                             BlendMode blend_type,
                              const uint8_t* clip_scan,
                              const uint8_t* src_alpha_scan,
                              uint8_t* dst_alpha_scan) {
@@ -265,9 +290,9 @@
     int alpha_ratio = src_alpha * 255 / (*dst_alpha_scan);
     uint8_t gray = GetGray(src_scan);
     // TODO(npm): Does this if really need src_alpha_scan or was that a bug?
-    if (blend_type && src_alpha_scan) {
-      if (blend_type >= FXDIB_BLEND_NONSEPARABLE)
-        gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
+    if (blend_type != BlendMode::kNormal && src_alpha_scan) {
+      if (IsNonSeparableBlendMode(blend_type))
+        gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
       else
         gray = Blend(blend_type, *dest_scan, gray);
     }
@@ -281,7 +306,7 @@
 void CompositeRow_Argb2Gray(uint8_t* dest_scan,
                             const uint8_t* src_scan,
                             int pixel_count,
-                            int blend_type,
+                            BlendMode blend_type,
                             const uint8_t* clip_scan,
                             const uint8_t* src_alpha_scan) {
   uint8_t gray;
@@ -303,7 +328,7 @@
                            const uint8_t* src_scan,
                            int src_Bpp,
                            int pixel_count,
-                           int blend_type,
+                           BlendMode blend_type,
                            const uint8_t* clip_scan) {
   uint8_t gray;
   for (int col = 0; col < pixel_count; ++col) {
@@ -321,11 +346,11 @@
                             const uint8_t* src_scan,
                             int src_Bpp,
                             int pixel_count,
-                            int blend_type,
+                            BlendMode blend_type,
                             const uint8_t* clip_scan,
                             uint8_t* dest_alpha_scan) {
   for (int col = 0; col < pixel_count; ++col) {
-    if (blend_type && *dest_alpha_scan == 0) {
+    if (blend_type != BlendMode::kNormal && *dest_alpha_scan == 0) {
       *dest_scan = GetGray(src_scan);
       ++dest_scan;
       ++dest_alpha_scan;
@@ -362,14 +387,14 @@
 void CompositeRow_Argb2Argb(uint8_t* dest_scan,
                             const uint8_t* src_scan,
                             int pixel_count,
-                            int blend_type,
+                            BlendMode blend_type,
                             const uint8_t* clip_scan,
                             uint8_t* dest_alpha_scan,
                             const uint8_t* src_alpha_scan) {
   int blended_colors[3];
   uint8_t dest_offset = dest_alpha_scan ? 3 : 4;
   uint8_t src_offset = src_alpha_scan ? 3 : 4;
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   bool has_src = !!src_alpha_scan;
   bool has_dest = !!dest_alpha_scan;
   for (int col = 0; col < pixel_count; ++col) {
@@ -394,8 +419,8 @@
         if (!has_src)
           ++src_scan;
       } else {
-        FXARGB_SETDIB(dest_scan, FXARGB_MAKE((src_alpha << 24), src_scan[2],
-                                             src_scan[1], *src_scan));
+        FXARGB_SETDIB(dest_scan, ArgbEncode((src_alpha << 24), src_scan[2],
+                                            src_scan[1], *src_scan));
       }
       if (!has_dest) {
         dest_scan += dest_offset;
@@ -421,7 +446,7 @@
     if (bNonseparableBlend)
       RGB_Blend(blend_type, src_scan, dest_scan, blended_colors);
     for (int color = 0; color < 3; ++color) {
-      if (blend_type) {
+      if (blend_type != BlendMode::kNormal) {
         int blended = bNonseparableBlend
                           ? blended_colors[color]
                           : Blend(blend_type, *dest_scan, *src_scan);
@@ -443,11 +468,11 @@
 void CompositeRow_Rgb2Argb_Blend_NoClip(uint8_t* dest_scan,
                                         const uint8_t* src_scan,
                                         int width,
-                                        int blend_type,
+                                        BlendMode blend_type,
                                         int src_Bpp,
                                         uint8_t* dest_alpha_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; ++col) {
     uint8_t* dest_alpha = dest_alpha_scan ? dest_alpha_scan : &dest_scan[3];
@@ -464,8 +489,8 @@
         if (src_Bpp == 4) {
           FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
         } else {
-          FXARGB_SETDIB(dest_scan, FXARGB_MAKE(0xff, src_scan[2], src_scan[1],
-                                               src_scan[0]));
+          FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[2], src_scan[1],
+                                              src_scan[0]));
         }
         dest_scan += 4;
       }
@@ -495,12 +520,12 @@
 void CompositeRow_Rgb2Argb_Blend_Clip(uint8_t* dest_scan,
                                       const uint8_t* src_scan,
                                       int width,
-                                      int blend_type,
+                                      BlendMode blend_type,
                                       int src_Bpp,
                                       const uint8_t* clip_scan,
                                       uint8_t* dest_alpha_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
   bool has_dest = !!dest_alpha_scan;
   for (int col = 0; col < width; ++col) {
@@ -637,7 +662,7 @@
         FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
       } else {
         FXARGB_SETDIB(dest_scan,
-                      FXARGB_MAKE(0xff, src_scan[2], src_scan[1], src_scan[0]));
+                      ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0]));
       }
       dest_scan += 4;
       src_scan += src_Bpp;
@@ -648,12 +673,12 @@
 void CompositeRow_Argb2Rgb_Blend(uint8_t* dest_scan,
                                  const uint8_t* src_scan,
                                  int width,
-                                 int blend_type,
+                                 BlendMode blend_type,
                                  int dest_Bpp,
                                  const uint8_t* clip_scan,
                                  const uint8_t* src_alpha_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int dest_gap = dest_Bpp - 3;
   if (src_alpha_scan) {
     for (int col = 0; col < width; col++) {
@@ -782,11 +807,11 @@
 void CompositeRow_Rgb2Rgb_Blend_NoClip(uint8_t* dest_scan,
                                        const uint8_t* src_scan,
                                        int width,
-                                       int blend_type,
+                                       BlendMode blend_type,
                                        int dest_Bpp,
                                        int src_Bpp) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int dest_gap = dest_Bpp - 3;
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
@@ -811,12 +836,12 @@
 void CompositeRow_Rgb2Rgb_Blend_Clip(uint8_t* dest_scan,
                                      const uint8_t* src_scan,
                                      int width,
-                                     int blend_type,
+                                     BlendMode blend_type,
                                      int dest_Bpp,
                                      int src_Bpp,
                                      const uint8_t* clip_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int dest_gap = dest_Bpp - 3;
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
@@ -895,17 +920,17 @@
                                const uint8_t* src_scan,
                                const uint8_t* pPalette,
                                int pixel_count,
-                               int blend_type,
+                               BlendMode blend_type,
                                const uint8_t* clip_scan,
                                const uint8_t* src_alpha_scan) {
   if (src_alpha_scan) {
-    if (blend_type) {
-      bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+    if (blend_type != BlendMode::kNormal) {
+      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
       for (int col = 0; col < pixel_count; col++) {
         uint8_t gray = pPalette[*src_scan];
         int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col);
         if (bNonseparableBlend)
-          gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
+          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
         else
           gray = Blend(blend_type, *dest_scan, gray);
         if (src_alpha)
@@ -928,12 +953,12 @@
       src_scan++;
     }
   } else {
-    if (blend_type) {
-      bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+    if (blend_type != BlendMode::kNormal) {
+      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
       for (int col = 0; col < pixel_count; col++) {
         uint8_t gray = pPalette[*src_scan];
         if (bNonseparableBlend)
-          gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
+          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
         else
           gray = Blend(blend_type, *dest_scan, gray);
         if (clip_scan && clip_scan[col] < 255)
@@ -961,13 +986,13 @@
                                 const uint8_t* src_scan,
                                 const uint8_t* pPalette,
                                 int pixel_count,
-                                int blend_type,
+                                BlendMode blend_type,
                                 const uint8_t* clip_scan,
                                 uint8_t* dest_alpha_scan,
                                 const uint8_t* src_alpha_scan) {
   if (src_alpha_scan) {
-    if (blend_type) {
-      bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+    if (blend_type != BlendMode::kNormal) {
+      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
       for (int col = 0; col < pixel_count; col++) {
         uint8_t gray = pPalette[*src_scan];
         src_scan++;
@@ -992,7 +1017,7 @@
             back_alpha + src_alpha - back_alpha * src_alpha / 255;
         int alpha_ratio = src_alpha * 255 / (*dest_alpha_scan);
         if (bNonseparableBlend)
-          gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
+          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
         else
           gray = Blend(blend_type, *dest_scan, gray);
         *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
@@ -1028,8 +1053,8 @@
       dest_scan++;
     }
   } else {
-    if (blend_type) {
-      bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+    if (blend_type != BlendMode::kNormal) {
+      bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
       for (int col = 0; col < pixel_count; col++) {
         uint8_t gray = pPalette[*src_scan];
         src_scan++;
@@ -1050,7 +1075,7 @@
         *dest_alpha_scan++ = dest_alpha;
         int alpha_ratio = src_alpha * 255 / dest_alpha;
         if (bNonseparableBlend)
-          gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
+          gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
         else
           gray = Blend(blend_type, *dest_scan, gray);
         *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
@@ -1088,19 +1113,19 @@
                                int src_left,
                                const uint8_t* pPalette,
                                int pixel_count,
-                               int blend_type,
+                               BlendMode blend_type,
                                const uint8_t* clip_scan) {
   int reset_gray = pPalette[0];
   int set_gray = pPalette[1];
-  if (blend_type) {
-    bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  if (blend_type != BlendMode::kNormal) {
+    bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
     for (int col = 0; col < pixel_count; col++) {
       uint8_t gray =
           (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8)))
               ? set_gray
               : reset_gray;
       if (bNonseparableBlend)
-        gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
+        gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
       else
         gray = Blend(blend_type, *dest_scan, gray);
       if (clip_scan && clip_scan[col] < 255) {
@@ -1131,13 +1156,13 @@
                                 int src_left,
                                 const uint8_t* pPalette,
                                 int pixel_count,
-                                int blend_type,
+                                BlendMode blend_type,
                                 const uint8_t* clip_scan,
                                 uint8_t* dest_alpha_scan) {
   int reset_gray = pPalette[0];
   int set_gray = pPalette[1];
-  if (blend_type) {
-    bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  if (blend_type != BlendMode::kNormal) {
+    bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
     for (int col = 0; col < pixel_count; col++) {
       uint8_t gray =
           (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8)))
@@ -1160,7 +1185,7 @@
       *dest_alpha_scan++ = dest_alpha;
       int alpha_ratio = src_alpha * 255 / dest_alpha;
       if (bNonseparableBlend)
-        gray = blend_type == FXDIB_BLEND_LUMINOSITY ? gray : *dest_scan;
+        gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan;
       else
         gray = Blend(blend_type, *dest_scan, gray);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio);
@@ -1322,21 +1347,17 @@
       if (back_alpha == 0) {
         if (clip_scan) {
           int src_alpha = clip_scan[col] * (*src_alpha_scan) / 255;
-          FXARGB_SETDIB(dest_scan, FXARGB_MAKE(src_alpha, src_r, src_g, src_b));
+          FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, src_r, src_g, src_b));
         } else {
           FXARGB_SETDIB(dest_scan,
-                        FXARGB_MAKE(*src_alpha_scan, src_r, src_g, src_b));
+                        ArgbEncode(*src_alpha_scan, src_r, src_g, src_b));
         }
         dest_scan += 4;
         src_alpha_scan++;
         continue;
       }
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = clip_scan[col] * (*src_alpha_scan++) / 255;
-      } else {
-        src_alpha = *src_alpha_scan++;
-      }
+      uint8_t src_alpha = GetAlpha(*src_alpha_scan, clip_scan, col);
+      ++src_alpha_scan;
       if (src_alpha == 0) {
         dest_scan += 4;
         continue;
@@ -1500,18 +1521,13 @@
                                 int src_g,
                                 int src_b,
                                 int pixel_count,
-                                int blend_type,
+                                BlendMode blend_type,
                                 const uint8_t* clip_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
-      FXARGB_SETDIB(dest_scan, FXARGB_MAKE(src_alpha, src_r, src_g, src_b));
+      FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, src_r, src_g, src_b));
       dest_scan += 4;
       continue;
     }
@@ -1522,7 +1538,7 @@
     uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
     dest_scan[3] = dest_alpha;
     int alpha_ratio = src_alpha * 255 / dest_alpha;
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -1536,7 +1552,7 @@
       dest_scan++;
       *dest_scan =
           FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], alpha_ratio);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, *dest_scan, src_b);
       blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio);
@@ -1566,16 +1582,11 @@
                                 int src_g,
                                 int src_b,
                                 int pixel_count,
-                                int blend_type,
+                                BlendMode blend_type,
                                 const uint8_t* clip_scan,
                                 uint8_t* dest_alpha_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = *dest_alpha_scan;
     if (back_alpha == 0) {
       *dest_scan++ = src_b;
@@ -1592,7 +1603,7 @@
     uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
     *dest_alpha_scan++ = dest_alpha;
     int alpha_ratio = src_alpha * 255 / dest_alpha;
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -1607,7 +1618,7 @@
       *dest_scan =
           FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], alpha_ratio);
       dest_scan++;
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, *dest_scan, src_b);
       blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio);
@@ -1638,21 +1649,16 @@
                                int src_g,
                                int src_b,
                                int pixel_count,
-                               int blend_type,
+                               BlendMode blend_type,
                                int Bpp,
                                const uint8_t* clip_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     if (src_alpha == 0) {
       dest_scan += Bpp;
       continue;
     }
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -1663,7 +1669,7 @@
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[1], src_alpha);
       dest_scan++;
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], src_alpha);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, *dest_scan, src_b);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, src_alpha);
       dest_scan++;
@@ -1689,12 +1695,7 @@
                                 int pixel_count,
                                 const uint8_t* clip_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = *dest_scan;
     if (!back_alpha) {
       *dest_scan = src_alpha;
@@ -1712,12 +1713,7 @@
                                 int pixel_count,
                                 const uint8_t* clip_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     if (src_alpha) {
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_gray, src_alpha);
     }
@@ -1733,12 +1729,7 @@
                                  const uint8_t* clip_scan,
                                  uint8_t* dest_alpha_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = *dest_alpha_scan;
     if (back_alpha == 0) {
       *dest_scan++ = src_gray;
@@ -1766,10 +1757,10 @@
                                int src_b,
                                int src_left,
                                int pixel_count,
-                               int blend_type,
+                               BlendMode blend_type,
                                const uint8_t* clip_scan) {
-  if (blend_type == FXDIB_BLEND_NORMAL && !clip_scan && mask_alpha == 255) {
-    FX_ARGB argb = FXARGB_MAKE(0xff, src_r, src_g, src_b);
+  if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
+    FX_ARGB argb = ArgbEncode(0xff, src_r, src_g, src_b);
     for (int col = 0; col < pixel_count; col++) {
       if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) {
         FXARGB_SETDIB(dest_scan, argb);
@@ -1783,22 +1774,17 @@
       dest_scan += 4;
       continue;
     }
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] / 255;
-    } else {
-      src_alpha = mask_alpha;
-    }
+    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
-      FXARGB_SETDIB(dest_scan, FXARGB_MAKE(src_alpha, src_r, src_g, src_b));
+      FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, src_r, src_g, src_b));
       dest_scan += 4;
       continue;
     }
     uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
     dest_scan[3] = dest_alpha;
     int alpha_ratio = src_alpha * 255 / dest_alpha;
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -1812,7 +1798,7 @@
       dest_scan++;
       *dest_scan =
           FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], alpha_ratio);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, *dest_scan, src_b);
       blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio);
@@ -1843,10 +1829,10 @@
                               int src_b,
                               int src_left,
                               int pixel_count,
-                              int blend_type,
+                              BlendMode blend_type,
                               int Bpp,
                               const uint8_t* clip_scan) {
-  if (blend_type == FXDIB_BLEND_NORMAL && !clip_scan && mask_alpha == 255) {
+  if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
     for (int col = 0; col < pixel_count; col++) {
       if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) {
         dest_scan[2] = src_r;
@@ -1862,17 +1848,12 @@
       dest_scan += Bpp;
       continue;
     }
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] / 255;
-    } else {
-      src_alpha = mask_alpha;
-    }
+    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
     if (src_alpha == 0) {
       dest_scan += Bpp;
       continue;
     }
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -1883,7 +1864,7 @@
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[1], src_alpha);
       dest_scan++;
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], src_alpha);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, *dest_scan, src_b);
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, src_alpha);
       dest_scan++;
@@ -1914,12 +1895,7 @@
       dest_scan++;
       continue;
     }
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] / 255;
-    } else {
-      src_alpha = mask_alpha;
-    }
+    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
     uint8_t back_alpha = *dest_scan;
     if (!back_alpha) {
       *dest_scan = src_alpha;
@@ -1942,12 +1918,7 @@
       dest_scan++;
       continue;
     }
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] / 255;
-    } else {
-      src_alpha = mask_alpha;
-    }
+    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
     if (src_alpha) {
       *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_gray, src_alpha);
     }
@@ -1969,12 +1940,7 @@
       dest_alpha_scan++;
       continue;
     }
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] / 255;
-    } else {
-      src_alpha = mask_alpha;
-    }
+    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
     uint8_t back_alpha = *dest_alpha_scan;
     if (back_alpha == 0) {
       *dest_scan++ = src_gray;
@@ -1997,10 +1963,10 @@
 void CompositeRow_Argb2Argb_RgbByteOrder(uint8_t* dest_scan,
                                          const uint8_t* src_scan,
                                          int pixel_count,
-                                         int blend_type,
+                                         BlendMode blend_type,
                                          const uint8_t* clip_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   for (int col = 0; col < pixel_count; col++) {
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
@@ -2017,12 +1983,7 @@
       src_scan += 4;
       continue;
     }
-    uint8_t src_alpha;
-    if (clip_scan) {
-      src_alpha = clip_scan[col] * src_scan[3] / 255;
-    } else {
-      src_alpha = src_scan[3];
-    }
+    uint8_t src_alpha = GetAlpha(src_scan[3], clip_scan, col);
     if (src_alpha == 0) {
       dest_scan += 4;
       src_scan += 4;
@@ -2040,7 +2001,7 @@
     }
     for (int color = 0; color < 3; color++) {
       int index = 2 - color;
-      if (blend_type) {
+      if (blend_type != BlendMode::kNormal) {
         int blended = bNonseparableBlend
                           ? blended_colors[color]
                           : Blend(blend_type, dest_scan[index], *src_scan);
@@ -2061,10 +2022,10 @@
 void CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan,
                                                      const uint8_t* src_scan,
                                                      int width,
-                                                     int blend_type,
+                                                     BlendMode blend_type,
                                                      int src_Bpp) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
     uint8_t back_alpha = dest_scan[3];
@@ -2072,8 +2033,8 @@
       if (src_Bpp == 4) {
         FXARGB_SETRGBORDERDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
       } else {
-        FXARGB_SETRGBORDERDIB(dest_scan, FXARGB_MAKE(0xff, src_scan[2],
-                                                     src_scan[1], src_scan[0]));
+        FXARGB_SETRGBORDERDIB(
+            dest_scan, ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0]));
       }
       dest_scan += 4;
       src_scan += src_Bpp;
@@ -2104,11 +2065,11 @@
 void CompositeRow_Argb2Rgb_Blend_RgbByteOrder(uint8_t* dest_scan,
                                               const uint8_t* src_scan,
                                               int width,
-                                              int blend_type,
+                                              BlendMode blend_type,
                                               int dest_Bpp,
                                               const uint8_t* clip_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   for (int col = 0; col < width; col++) {
     uint8_t src_alpha;
     if (clip_scan) {
@@ -2151,7 +2112,7 @@
       FXARGB_SETRGBORDERDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan));
     } else {
       FXARGB_SETRGBORDERDIB(
-          dest_scan, FXARGB_MAKE(0xff, src_scan[2], src_scan[1], src_scan[0]));
+          dest_scan, ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0]));
     }
     dest_scan += 4;
     src_scan += src_Bpp;
@@ -2161,11 +2122,11 @@
 void CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan,
                                                     const uint8_t* src_scan,
                                                     int width,
-                                                    int blend_type,
+                                                    BlendMode blend_type,
                                                     int dest_Bpp,
                                                     int src_Bpp) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
     if (bNonseparableBlend) {
@@ -2243,11 +2204,11 @@
 void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan,
                                                    const uint8_t* src_scan,
                                                    int width,
-                                                   int blend_type,
+                                                   BlendMode blend_type,
                                                    int src_Bpp,
                                                    const uint8_t* clip_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
     int src_alpha = *clip_scan++;
@@ -2294,12 +2255,12 @@
 void CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan,
                                                   const uint8_t* src_scan,
                                                   int width,
-                                                  int blend_type,
+                                                  BlendMode blend_type,
                                                   int dest_Bpp,
                                                   int src_Bpp,
                                                   const uint8_t* clip_scan) {
   int blended_colors[3];
-  bool bNonseparableBlend = blend_type >= FXDIB_BLEND_NONSEPARABLE;
+  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
   int src_gap = src_Bpp - 3;
   for (int col = 0; col < width; col++) {
     uint8_t src_alpha = *clip_scan++;
@@ -2566,19 +2527,14 @@
                                              int src_g,
                                              int src_b,
                                              int pixel_count,
-                                             int blend_type,
+                                             BlendMode blend_type,
                                              const uint8_t* clip_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
       FXARGB_SETRGBORDERDIB(dest_scan,
-                            FXARGB_MAKE(src_alpha, src_r, src_g, src_b));
+                            ArgbEncode(src_alpha, src_r, src_g, src_b));
       dest_scan += 4;
       continue;
     }
@@ -2589,7 +2545,7 @@
     uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
     dest_scan[3] = dest_alpha;
     int alpha_ratio = src_alpha * 255 / dest_alpha;
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -2602,7 +2558,7 @@
           FXDIB_ALPHA_MERGE(dest_scan[1], blended_colors[1], alpha_ratio);
       dest_scan[0] =
           FXDIB_ALPHA_MERGE(dest_scan[0], blended_colors[2], alpha_ratio);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, dest_scan[2], src_b);
       blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha);
       dest_scan[2] = FXDIB_ALPHA_MERGE(dest_scan[2], blended, alpha_ratio);
@@ -2628,21 +2584,16 @@
                                             int src_g,
                                             int src_b,
                                             int pixel_count,
-                                            int blend_type,
+                                            BlendMode blend_type,
                                             int Bpp,
                                             const uint8_t* clip_scan) {
   for (int col = 0; col < pixel_count; col++) {
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] * src_scan[col] / 255 / 255;
-    } else {
-      src_alpha = mask_alpha * src_scan[col] / 255;
-    }
+    int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col);
     if (src_alpha == 0) {
       dest_scan += Bpp;
       continue;
     }
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -2655,7 +2606,7 @@
           FXDIB_ALPHA_MERGE(dest_scan[1], blended_colors[1], src_alpha);
       dest_scan[0] =
           FXDIB_ALPHA_MERGE(dest_scan[0], blended_colors[2], src_alpha);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, dest_scan[2], src_b);
       dest_scan[2] = FXDIB_ALPHA_MERGE(dest_scan[2], blended, src_alpha);
       blended = Blend(blend_type, dest_scan[1], src_g);
@@ -2679,10 +2630,10 @@
                                             int src_b,
                                             int src_left,
                                             int pixel_count,
-                                            int blend_type,
+                                            BlendMode blend_type,
                                             const uint8_t* clip_scan) {
-  if (blend_type == FXDIB_BLEND_NORMAL && !clip_scan && mask_alpha == 255) {
-    FX_ARGB argb = FXARGB_MAKE(0xff, src_r, src_g, src_b);
+  if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
+    FX_ARGB argb = ArgbEncode(0xff, src_r, src_g, src_b);
     for (int col = 0; col < pixel_count; col++) {
       if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) {
         FXARGB_SETRGBORDERDIB(dest_scan, argb);
@@ -2696,23 +2647,18 @@
       dest_scan += 4;
       continue;
     }
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] / 255;
-    } else {
-      src_alpha = mask_alpha;
-    }
+    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
     uint8_t back_alpha = dest_scan[3];
     if (back_alpha == 0) {
       FXARGB_SETRGBORDERDIB(dest_scan,
-                            FXARGB_MAKE(src_alpha, src_r, src_g, src_b));
+                            ArgbEncode(src_alpha, src_r, src_g, src_b));
       dest_scan += 4;
       continue;
     }
     uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
     dest_scan[3] = dest_alpha;
     int alpha_ratio = src_alpha * 255 / dest_alpha;
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -2725,7 +2671,7 @@
           FXDIB_ALPHA_MERGE(dest_scan[1], blended_colors[1], alpha_ratio);
       dest_scan[0] =
           FXDIB_ALPHA_MERGE(dest_scan[0], blended_colors[2], alpha_ratio);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int blended = Blend(blend_type, dest_scan[2], src_b);
       blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha);
       dest_scan[2] = FXDIB_ALPHA_MERGE(dest_scan[2], blended, alpha_ratio);
@@ -2752,10 +2698,10 @@
                                            int src_b,
                                            int src_left,
                                            int pixel_count,
-                                           int blend_type,
+                                           BlendMode blend_type,
                                            int Bpp,
                                            const uint8_t* clip_scan) {
-  if (blend_type == FXDIB_BLEND_NORMAL && !clip_scan && mask_alpha == 255) {
+  if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) {
     for (int col = 0; col < pixel_count; col++) {
       if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) {
         dest_scan[2] = src_b;
@@ -2771,17 +2717,12 @@
       dest_scan += Bpp;
       continue;
     }
-    int src_alpha;
-    if (clip_scan) {
-      src_alpha = mask_alpha * clip_scan[col] / 255;
-    } else {
-      src_alpha = mask_alpha;
-    }
+    int src_alpha = GetAlpha(mask_alpha, clip_scan, col);
     if (src_alpha == 0) {
       dest_scan += Bpp;
       continue;
     }
-    if (blend_type >= FXDIB_BLEND_NONSEPARABLE) {
+    if (IsNonSeparableBlendMode(blend_type)) {
       int blended_colors[3];
       uint8_t scan[3] = {static_cast<uint8_t>(src_b),
                          static_cast<uint8_t>(src_g),
@@ -2794,7 +2735,7 @@
           FXDIB_ALPHA_MERGE(dest_scan[1], blended_colors[1], src_alpha);
       dest_scan[0] =
           FXDIB_ALPHA_MERGE(dest_scan[0], blended_colors[2], src_alpha);
-    } else if (blend_type) {
+    } else if (blend_type != BlendMode::kNormal) {
       int back_color = dest_scan[2];
       int blended = Blend(blend_type, back_color, src_b);
       dest_scan[2] = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha);
@@ -2815,107 +2756,78 @@
 
 }  // namespace
 
-CFX_ScanlineCompositor::CFX_ScanlineCompositor()
-    : m_BlendType(FXDIB_BLEND_NORMAL), m_bRgbByteOrder(false) {}
+CFX_ScanlineCompositor::CFX_ScanlineCompositor() = default;
 
-CFX_ScanlineCompositor::~CFX_ScanlineCompositor() {}
+CFX_ScanlineCompositor::~CFX_ScanlineCompositor() = default;
 
 bool CFX_ScanlineCompositor::Init(FXDIB_Format dest_format,
                                   FXDIB_Format src_format,
                                   int32_t width,
                                   uint32_t* pSrcPalette,
                                   uint32_t mask_color,
-                                  int blend_type,
+                                  BlendMode blend_type,
                                   bool bClip,
-                                  bool bRgbByteOrder,
-                                  int alpha_flag) {
+                                  bool bRgbByteOrder) {
   m_SrcFormat = src_format;
   m_DestFormat = dest_format;
   m_BlendType = blend_type;
   m_bRgbByteOrder = bRgbByteOrder;
-  if ((dest_format & 0xff) == 1)
+  if (GetBppFromFormat(dest_format) == 1)
     return false;
   if (m_SrcFormat == FXDIB_1bppMask || m_SrcFormat == FXDIB_8bppMask) {
-    InitSourceMask(alpha_flag, mask_color);
+    InitSourceMask(mask_color);
     return true;
   }
-  if ((~src_format & 0x0400) && (dest_format & 0x0400))
+  if (!GetIsCmykFromFormat(src_format) && GetIsCmykFromFormat(dest_format))
     return false;
-  if ((m_SrcFormat & 0xff) <= 8) {
+  if (GetBppFromFormat(m_SrcFormat) <= 8) {
     if (dest_format == FXDIB_8bppMask)
       return true;
 
     InitSourcePalette(src_format, dest_format, pSrcPalette);
-    m_iTransparency =
-        (dest_format == FXDIB_Argb ? 1 : 0) + (dest_format & 0x0200 ? 2 : 0) +
-        (dest_format & 0x0400 ? 4 : 0) + ((src_format & 0xff) == 1 ? 8 : 0);
+    m_iTransparency = (dest_format == FXDIB_Argb ? 1 : 0) +
+                      (GetIsAlphaFromFormat(dest_format) ? 2 : 0) +
+                      (GetIsCmykFromFormat(dest_format) ? 4 : 0) +
+                      (GetBppFromFormat(src_format) == 1 ? 8 : 0);
     return true;
   }
-  m_iTransparency =
-      (src_format & 0x0200 ? 0 : 1) + (dest_format & 0x0200 ? 0 : 2) +
-      (blend_type == FXDIB_BLEND_NORMAL ? 4 : 0) + (bClip ? 8 : 0) +
-      (src_format & 0x0400 ? 16 : 0) + (dest_format & 0x0400 ? 32 : 0);
+  m_iTransparency = (GetIsAlphaFromFormat(src_format) ? 0 : 1) +
+                    (GetIsAlphaFromFormat(dest_format) ? 0 : 2) +
+                    (blend_type == BlendMode::kNormal ? 4 : 0) +
+                    (bClip ? 8 : 0) +
+                    (GetIsCmykFromFormat(src_format) ? 16 : 0) +
+                    (GetIsCmykFromFormat(dest_format) ? 32 : 0);
   return true;
 }
 
-void CFX_ScanlineCompositor::InitSourceMask(int alpha_flag,
-                                            uint32_t mask_color) {
-  int mask_black = 0;
-  if (alpha_flag >> 8) {
-    m_MaskAlpha = alpha_flag & 0xff;
-    m_MaskRed = FXSYS_GetCValue(mask_color);
-    m_MaskGreen = FXSYS_GetMValue(mask_color);
-    m_MaskBlue = FXSYS_GetYValue(mask_color);
-    mask_black = FXSYS_GetKValue(mask_color);
-  } else {
-    m_MaskAlpha = FXARGB_A(mask_color);
-    m_MaskRed = FXARGB_R(mask_color);
-    m_MaskGreen = FXARGB_G(mask_color);
-    m_MaskBlue = FXARGB_B(mask_color);
-  }
+void CFX_ScanlineCompositor::InitSourceMask(uint32_t mask_color) {
+  m_MaskAlpha = FXARGB_A(mask_color);
+  m_MaskRed = FXARGB_R(mask_color);
+  m_MaskGreen = FXARGB_G(mask_color);
+  m_MaskBlue = FXARGB_B(mask_color);
   if (m_DestFormat == FXDIB_8bppMask)
     return;
 
-  if ((m_DestFormat & 0xff) == 8) {
-    if (alpha_flag >> 8) {
-      uint8_t r;
-      uint8_t g;
-      uint8_t b;
-      std::tie(r, g, b) =
-          AdobeCMYK_to_sRGB1(m_MaskRed, m_MaskGreen, m_MaskBlue, mask_black);
-      m_MaskRed = FXRGB2GRAY(r, g, b);
-    } else {
-      m_MaskRed = FXRGB2GRAY(m_MaskRed, m_MaskGreen, m_MaskBlue);
-    }
-    if (m_DestFormat & 0x0400)
+  if (GetBppFromFormat(m_DestFormat) == 8) {
+    m_MaskRed = FXRGB2GRAY(m_MaskRed, m_MaskGreen, m_MaskBlue);
+    if (GetIsCmykFromFormat(m_DestFormat))
       m_MaskRed = FX_CCOLOR(m_MaskRed);
-    return;
-  }
-  uint8_t* mask_color_p = (uint8_t*)&mask_color;
-  mask_color =
-      (alpha_flag >> 8) ? FXCMYK_TODIB(mask_color) : FXARGB_TODIB(mask_color);
-  if (alpha_flag >> 8) {
-    std::tie(mask_color_p[2], mask_color_p[1], mask_color_p[0]) =
-        AdobeCMYK_to_sRGB1(mask_color_p[0], mask_color_p[1], mask_color_p[2],
-                           mask_color_p[3]);
-    m_MaskRed = mask_color_p[2];
-    m_MaskGreen = mask_color_p[1];
-    m_MaskBlue = mask_color_p[0];
   }
 }
 
 void CFX_ScanlineCompositor::InitSourcePalette(FXDIB_Format src_format,
                                                FXDIB_Format dest_format,
                                                const uint32_t* pSrcPalette) {
-  bool isSrcCmyk = !!(src_format & 0x0400);
-  bool isDstCmyk = !!(dest_format & 0x0400);
+  bool bIsSrcCmyk = GetIsCmykFromFormat(src_format);
+  bool bIsDstCmyk = GetIsCmykFromFormat(dest_format);
+  bool bIsDestBpp8 = GetBppFromFormat(dest_format) == 8;
+  int pal_count = 1 << GetBppFromFormat(src_format);
   m_pSrcPalette = nullptr;
   if (pSrcPalette) {
-    if ((dest_format & 0xff) == 8) {
-      int pal_count = 1 << (src_format & 0xff);
+    if (bIsDestBpp8) {
       uint8_t* gray_pal = FX_Alloc(uint8_t, pal_count);
       m_pSrcPalette.reset(reinterpret_cast<uint32_t*>(gray_pal));
-      if (isSrcCmyk) {
+      if (bIsSrcCmyk) {
         for (int i = 0; i < pal_count; ++i) {
           FX_CMYK cmyk = pSrcPalette[i];
           uint8_t r;
@@ -2935,13 +2847,12 @@
       }
       return;
     }
-    int palsize = 1 << (src_format & 0xff);
-    m_pSrcPalette.reset(FX_Alloc(uint32_t, palsize));
+    m_pSrcPalette.reset(FX_Alloc(uint32_t, pal_count));
     uint32_t* pPalette = m_pSrcPalette.get();
-    if (isDstCmyk == isSrcCmyk) {
-      memcpy(pPalette, pSrcPalette, palsize * sizeof(uint32_t));
+    if (bIsDstCmyk == bIsSrcCmyk) {
+      memcpy(pPalette, pSrcPalette, pal_count * sizeof(uint32_t));
     } else {
-      for (int i = 0; i < palsize; ++i) {
+      for (int i = 0; i < pal_count; ++i) {
         FX_CMYK cmyk = pSrcPalette[i];
         uint8_t r;
         uint8_t g;
@@ -2949,13 +2860,12 @@
         std::tie(r, g, b) =
             AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk),
                                FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk));
-        pPalette[i] = FXARGB_MAKE(0xff, r, g, b);
+        pPalette[i] = ArgbEncode(0xff, r, g, b);
       }
     }
     return;
   }
-  if ((dest_format & 0xff) == 8) {
-    int pal_count = 1 << (src_format & 0xff);
+  if (bIsDestBpp8) {
     uint8_t* gray_pal = FX_Alloc(uint8_t, pal_count);
     if (pal_count == 2) {
       gray_pal[0] = 0;
@@ -2967,18 +2877,17 @@
     m_pSrcPalette.reset(reinterpret_cast<uint32_t*>(gray_pal));
     return;
   }
-  int palsize = 1 << (src_format & 0xff);
-  m_pSrcPalette.reset(FX_Alloc(uint32_t, palsize));
+  m_pSrcPalette.reset(FX_Alloc(uint32_t, pal_count));
   uint32_t* pPalette = m_pSrcPalette.get();
-  if (palsize == 2) {
-    pPalette[0] = isSrcCmyk ? 255 : 0xff000000;
-    pPalette[1] = isSrcCmyk ? 0 : 0xffffffff;
+  if (pal_count == 2) {
+    pPalette[0] = bIsSrcCmyk ? 255 : 0xff000000;
+    pPalette[1] = bIsSrcCmyk ? 0 : 0xffffffff;
   } else {
-    for (int i = 0; i < palsize; ++i)
-      pPalette[i] = isSrcCmyk ? FX_CCOLOR(i) : (i * 0x10101);
+    for (int i = 0; i < pal_count; ++i)
+      pPalette[i] = bIsSrcCmyk ? FX_CCOLOR(i) : (i * 0x10101);
   }
-  if (isSrcCmyk != isDstCmyk) {
-    for (int i = 0; i < palsize; ++i) {
+  if (bIsSrcCmyk != bIsDstCmyk) {
+    for (int i = 0; i < pal_count; ++i) {
       FX_CMYK cmyk = pPalette[i];
       uint8_t r;
       uint8_t g;
@@ -2986,7 +2895,7 @@
       std::tie(r, g, b) =
           AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk),
                              FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk));
-      pPalette[i] = FXARGB_MAKE(0xff, r, g, b);
+      pPalette[i] = ArgbEncode(0xff, r, g, b);
     }
   }
 }
@@ -2998,8 +2907,8 @@
     const uint8_t* clip_scan,
     const uint8_t* src_extra_alpha,
     uint8_t* dst_extra_alpha) {
-  int src_Bpp = (m_SrcFormat & 0xff) >> 3;
-  int dest_Bpp = (m_DestFormat & 0xff) >> 3;
+  int src_Bpp = GetCompsFromFormat(m_SrcFormat);
+  int dest_Bpp = GetCompsFromFormat(m_DestFormat);
   if (m_bRgbByteOrder) {
     switch (m_iTransparency) {
       case 0:
@@ -3056,7 +2965,7 @@
     return;
   }
   if (m_DestFormat == FXDIB_8bppMask) {
-    if (m_SrcFormat & 0x0200) {
+    if (GetIsAlphaFromFormat(m_SrcFormat)) {
       if (m_SrcFormat == FXDIB_Argb) {
         CompositeRow_AlphaToMask(dest_scan, src_scan, width, clip_scan, 4);
       } else {
@@ -3066,15 +2975,15 @@
     } else {
       CompositeRow_Rgb2Mask(dest_scan, src_scan, width, clip_scan);
     }
-  } else if ((m_DestFormat & 0xff) == 8) {
-    if (m_DestFormat & 0x0400) {
+  } else if (GetBppFromFormat(m_DestFormat) == 8) {
+    if (GetIsCmykFromFormat(m_DestFormat)) {
       for (int i = 0; i < width; ++i) {
         *dest_scan = ~*dest_scan;
         dest_scan++;
       }
     }
-    if (m_SrcFormat & 0x0200) {
-      if (m_DestFormat & 0x0200) {
+    if (GetIsAlphaFromFormat(m_SrcFormat)) {
+      if (GetIsAlphaFromFormat(m_DestFormat)) {
         CompositeRow_Argb2Graya(dest_scan, src_scan, width, m_BlendType,
                                 clip_scan, src_extra_alpha, dst_extra_alpha);
       } else {
@@ -3082,7 +2991,7 @@
                                clip_scan, src_extra_alpha);
       }
     } else {
-      if (m_DestFormat & 0x0200) {
+      if (GetIsAlphaFromFormat(m_DestFormat)) {
         CompositeRow_Rgb2Graya(dest_scan, src_scan, src_Bpp, width, m_BlendType,
                                clip_scan, dst_extra_alpha);
       } else {
@@ -3090,7 +2999,7 @@
                               clip_scan);
       }
     }
-    if (m_DestFormat & 0x0400) {
+    if (GetIsCmykFromFormat(m_DestFormat)) {
       for (int i = 0; i < width; ++i) {
         *dest_scan = ~*dest_scan;
         dest_scan++;
@@ -3172,7 +3081,7 @@
       } else {
         CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder(
             dest_scan, src_scan, src_left, m_pSrcPalette.get(), width,
-            (m_DestFormat & 0xff) >> 3, clip_scan);
+            GetCompsFromFormat(m_DestFormat), clip_scan);
       }
     } else {
       if (m_DestFormat == FXDIB_8bppRgb) {
@@ -3184,7 +3093,7 @@
       } else {
         CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder(
             dest_scan, src_scan, m_pSrcPalette.get(), width,
-            (m_DestFormat & 0xff) >> 3, clip_scan);
+            GetCompsFromFormat(m_DestFormat), clip_scan);
       }
     }
     return;
@@ -3193,9 +3102,9 @@
     CompositeRow_Rgb2Mask(dest_scan, src_scan, width, clip_scan);
     return;
   }
-  if ((m_DestFormat & 0xff) == 8) {
+  if (GetBppFromFormat(m_DestFormat) == 8) {
     if (m_iTransparency & 8) {
-      if (m_DestFormat & 0x0200) {
+      if (GetIsAlphaFromFormat(m_DestFormat)) {
         CompositeRow_1bppPal2Graya(
             dest_scan, src_scan, src_left,
             reinterpret_cast<const uint8_t*>(m_pSrcPalette.get()), width,
@@ -3207,16 +3116,17 @@
             m_BlendType, clip_scan);
       }
     } else {
-      if (m_DestFormat & 0x0200)
+      if (GetIsAlphaFromFormat(m_DestFormat)) {
         CompositeRow_8bppPal2Graya(
             dest_scan, src_scan,
             reinterpret_cast<const uint8_t*>(m_pSrcPalette.get()), width,
             m_BlendType, clip_scan, dst_extra_alpha, src_extra_alpha);
-      else
+      } else {
         CompositeRow_8bppPal2Gray(
             dest_scan, src_scan,
             reinterpret_cast<const uint8_t*>(m_pSrcPalette.get()), width,
             m_BlendType, clip_scan, src_extra_alpha);
+      }
     }
   } else {
     switch (m_iTransparency) {
@@ -3232,24 +3142,23 @@
       case 0:
         CompositeRow_8bppRgb2Rgb_NoBlend(
             dest_scan, src_scan, m_pSrcPalette.get(), width,
-            (m_DestFormat & 0xff) >> 3, clip_scan, src_extra_alpha);
+            GetCompsFromFormat(m_DestFormat), clip_scan, src_extra_alpha);
         break;
       case 0 + 8:
-        CompositeRow_1bppRgb2Rgb_NoBlend(dest_scan, src_scan, src_left,
-                                         m_pSrcPalette.get(), width,
-                                         (m_DestFormat & 0xff) >> 3, clip_scan);
+        CompositeRow_1bppRgb2Rgb_NoBlend(
+            dest_scan, src_scan, src_left, m_pSrcPalette.get(), width,
+            GetCompsFromFormat(m_DestFormat), clip_scan);
         break;
       case 0 + 2:
         CompositeRow_8bppRgb2Rgb_NoBlend(
             dest_scan, src_scan, m_pSrcPalette.get(), width,
-            (m_DestFormat & 0xff) >> 3, clip_scan, src_extra_alpha);
+            GetCompsFromFormat(m_DestFormat), clip_scan, src_extra_alpha);
         break;
       case 0 + 2 + 8:
         CompositeRow_1bppRgb2Rgba_NoBlend(dest_scan, src_scan, src_left, width,
                                           m_pSrcPalette.get(), clip_scan,
                                           dst_extra_alpha);
         break;
-        break;
     }
   }
 }
@@ -3262,8 +3171,8 @@
   if (m_DestFormat == FXDIB_8bppMask) {
     CompositeRow_ByteMask2Mask(dest_scan, src_scan, m_MaskAlpha, width,
                                clip_scan);
-  } else if ((m_DestFormat & 0xff) == 8) {
-    if (m_DestFormat & 0x0200) {
+  } else if (GetBppFromFormat(m_DestFormat) == 8) {
+    if (GetIsAlphaFromFormat(m_DestFormat)) {
       CompositeRow_ByteMask2Graya(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                                   width, clip_scan, dst_extra_alpha);
     } else {
@@ -3278,9 +3187,8 @@
     } else {
       CompositeRow_ByteMask2Rgb_RgbByteOrder(
           dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue,
-          width, m_BlendType, (m_DestFormat & 0xff) >> 3, clip_scan);
+          width, m_BlendType, GetCompsFromFormat(m_DestFormat), clip_scan);
     }
-    return;
   } else if (m_DestFormat == FXDIB_Argb) {
     CompositeRow_ByteMask2Argb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                                m_MaskGreen, m_MaskBlue, width, m_BlendType,
@@ -3288,7 +3196,7 @@
   } else if (m_DestFormat == FXDIB_Rgb || m_DestFormat == FXDIB_Rgb32) {
     CompositeRow_ByteMask2Rgb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                               m_MaskGreen, m_MaskBlue, width, m_BlendType,
-                              (m_DestFormat & 0xff) >> 3, clip_scan);
+                              GetCompsFromFormat(m_DestFormat), clip_scan);
   } else if (m_DestFormat == FXDIB_Rgba) {
     CompositeRow_ByteMask2Rgba(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                                m_MaskGreen, m_MaskBlue, width, m_BlendType,
@@ -3305,8 +3213,8 @@
   if (m_DestFormat == FXDIB_8bppMask) {
     CompositeRow_BitMask2Mask(dest_scan, src_scan, m_MaskAlpha, src_left, width,
                               clip_scan);
-  } else if ((m_DestFormat & 0xff) == 8) {
-    if (m_DestFormat & 0x0200) {
+  } else if (GetBppFromFormat(m_DestFormat) == 8) {
+    if (GetIsAlphaFromFormat(m_DestFormat)) {
       CompositeRow_BitMask2Graya(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                                  src_left, width, clip_scan, dst_extra_alpha);
     } else {
@@ -3321,16 +3229,17 @@
     } else {
       CompositeRow_BitMask2Rgb_RgbByteOrder(
           dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue,
-          src_left, width, m_BlendType, (m_DestFormat & 0xff) >> 3, clip_scan);
+          src_left, width, m_BlendType, GetCompsFromFormat(m_DestFormat),
+          clip_scan);
     }
-    return;
   } else if (m_DestFormat == FXDIB_Argb) {
     CompositeRow_BitMask2Argb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
                               m_MaskGreen, m_MaskBlue, src_left, width,
                               m_BlendType, clip_scan);
   } else if (m_DestFormat == FXDIB_Rgb || m_DestFormat == FXDIB_Rgb32) {
-    CompositeRow_BitMask2Rgb(
-        dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue,
-        src_left, width, m_BlendType, (m_DestFormat & 0xff) >> 3, clip_scan);
+    CompositeRow_BitMask2Rgb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed,
+                             m_MaskGreen, m_MaskBlue, src_left, width,
+                             m_BlendType, GetCompsFromFormat(m_DestFormat),
+                             clip_scan);
   }
 }
diff --git a/core/fxge/dib/cfx_scanlinecompositor.h b/core/fxge/dib/cfx_scanlinecompositor.h
index 75ab578..0ec842d 100644
--- a/core/fxge/dib/cfx_scanlinecompositor.h
+++ b/core/fxge/dib/cfx_scanlinecompositor.h
@@ -9,12 +9,12 @@
 
 #include <memory>
 
-#include "core/fxge/dib/cfx_dibsource.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxge/fx_dib.h"
 
 class CFX_ScanlineCompositor {
  public:
   CFX_ScanlineCompositor();
-
   ~CFX_ScanlineCompositor();
 
   bool Init(FXDIB_Format dest_format,
@@ -22,10 +22,9 @@
             int32_t width,
             uint32_t* pSrcPalette,
             uint32_t mask_color,
-            int blend_type,
+            BlendMode blend_type,
             bool bClip,
-            bool bRgbByteOrder,
-            int alpha_flag);
+            bool bRgbByteOrder);
 
   void CompositeRgbBitmapLine(uint8_t* dest_scan,
                               const uint8_t* src_scan,
@@ -60,7 +59,7 @@
                          FXDIB_Format dest_format,
                          const uint32_t* pSrcPalette);
 
-  void InitSourceMask(int alpha_flag, uint32_t mask_color);
+  void InitSourceMask(uint32_t mask_color);
 
   int m_iTransparency;
   FXDIB_Format m_SrcFormat;
@@ -70,8 +69,8 @@
   int m_MaskRed;
   int m_MaskGreen;
   int m_MaskBlue;
-  int m_BlendType;
-  bool m_bRgbByteOrder;
+  BlendMode m_BlendType = BlendMode::kNormal;
+  bool m_bRgbByteOrder = false;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_SCANLINECOMPOSITOR_H_
diff --git a/core/fxge/dib/cstretchengine.cpp b/core/fxge/dib/cstretchengine.cpp
index 6803959..8636430 100644
--- a/core/fxge/dib/cstretchengine.cpp
+++ b/core/fxge/dib/cstretchengine.cpp
@@ -9,24 +9,26 @@
 #include <algorithm>
 #include <utility>
 
-#include "core/fxcrt/ifx_pauseindicator.h"
+#include "core/fxcrt/pauseindicator_iface.h"
+#include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/dib/cfx_dibsource.h"
-#include "core/fxge/dib/ifx_scanlinecomposer.h"
+#include "core/fxge/dib/scanlinecomposer_iface.h"
 #include "core/fxge/fx_dib.h"
+#include "third_party/base/stl_util.h"
 
 namespace {
 
-const int kMaxDestValue = 16711680;
+constexpr int kMaxDestValue = 16711680;
+
+int GetPitchRoundUpTo4Bytes(int bits_per_pixel) {
+  return (bits_per_pixel + 31) / 32 * 4;
+}
 
 }  // namespace
 
-CStretchEngine::CWeightTable::CWeightTable()
-    : m_DestMin(0),
-      m_ItemSize(0),
-      m_dwWeightTablesSize(0) {}
+CStretchEngine::CWeightTable::CWeightTable() = default;
 
-CStretchEngine::CWeightTable::~CWeightTable() {}
+CStretchEngine::CWeightTable::~CWeightTable() = default;
 
 size_t CStretchEngine::CWeightTable::GetPixelWeightSize() const {
   return m_ItemSize / sizeof(int) - 2;
@@ -38,12 +40,12 @@
                                         int src_len,
                                         int src_min,
                                         int src_max,
-                                        int flags) {
+                                        const FXDIB_ResampleOptions& options) {
   m_WeightTables.clear();
   m_dwWeightTablesSize = 0;
   const double scale = static_cast<float>(src_len) / dest_len;
   const double base = dest_len < 0 ? src_len : 0;
-  const int ext_size = flags & FXDIB_BICUBIC_INTERPOL ? 3 : 1;
+  const int ext_size = options.bInterpolateBicubic ? 3 : 1;
   m_ItemSize =
       sizeof(int) * 2 +
       static_cast<int>(sizeof(int) *
@@ -55,11 +57,11 @@
 
   m_dwWeightTablesSize = (dest_max - dest_min) * m_ItemSize + 4;
   m_WeightTables.resize(m_dwWeightTablesSize);
-  if ((flags & FXDIB_NOSMOOTH) != 0 || fabs(static_cast<float>(scale)) < 1.0f) {
+  if (options.bNoSmoothing || fabs(static_cast<float>(scale)) < 1.0f) {
     for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
       PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
       double src_pos = dest_pixel * scale + scale / 2 + base;
-      if (flags & FXDIB_INTERPOL) {
+      if (options.bInterpolateBilinear) {
         pixel_weights.m_SrcStart =
             static_cast<int>(floor(static_cast<float>(src_pos) - 1.0f / 2));
         pixel_weights.m_SrcEnd =
@@ -70,12 +72,12 @@
           pixel_weights.m_Weights[0] = 65536;
         } else {
           pixel_weights.m_Weights[1] =
-              FXSYS_round(static_cast<float>(
-                              src_pos - pixel_weights.m_SrcStart - 1.0f / 2) *
-                          65536);
+              FXSYS_roundf(static_cast<float>(
+                               src_pos - pixel_weights.m_SrcStart - 1.0f / 2) *
+                           65536);
           pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1];
         }
-      } else if (flags & FXDIB_BICUBIC_INTERPOL) {
+      } else if (options.bInterpolateBicubic) {
         pixel_weights.m_SrcStart =
             static_cast<int>(floor(static_cast<float>(src_pos) - 1.0f / 2));
         pixel_weights.m_SrcEnd =
@@ -89,7 +91,7 @@
           pixel_weights.m_SrcStart = src_min;
         }
         pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_max - 1);
-        int weight = FXSYS_round(
+        int weight = FXSYS_roundf(
             static_cast<float>(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) *
             256);
         if (start == end) {
@@ -196,16 +198,17 @@
       if (idx >= GetPixelWeightSize())
         return false;
 
-      pixel_weights.m_Weights[idx] = FXSYS_round(weight * 65536);
+      pixel_weights.m_Weights[idx] = FXSYS_roundf(weight * 65536);
     }
   }
   return true;
 }
 
-PixelWeight* CStretchEngine::CWeightTable::GetPixelWeight(int pixel) const {
+const PixelWeight* CStretchEngine::CWeightTable::GetPixelWeight(
+    int pixel) const {
   ASSERT(pixel >= m_DestMin);
-  return reinterpret_cast<PixelWeight*>(const_cast<uint8_t*>(
-      m_WeightTables.data() + (pixel - m_DestMin) * m_ItemSize));
+  return reinterpret_cast<const PixelWeight*>(
+      &m_WeightTables[(pixel - m_DestMin) * m_ItemSize]);
 }
 
 int* CStretchEngine::CWeightTable::GetValueFromPixelWeight(PixelWeight* pWeight,
@@ -217,23 +220,25 @@
   return idx < GetPixelWeightSize() ? &pWeight->m_Weights[idx] : nullptr;
 }
 
-CStretchEngine::CStretchEngine(IFX_ScanlineComposer* pDestBitmap,
+CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap,
                                FXDIB_Format dest_format,
                                int dest_width,
                                int dest_height,
                                const FX_RECT& clip_rect,
-                               const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                               int flags) {
-  m_State = 0;
-  m_DestFormat = dest_format;
-  m_DestBpp = dest_format & 0xff;
-  m_SrcBpp = pSrcBitmap->GetFormat() & 0xff;
-  m_bHasAlpha = pSrcBitmap->GetFormat() & 0x200;
-  m_pSrcPalette = pSrcBitmap->GetPalette();
-  m_pDestBitmap = pDestBitmap;
-  m_DestWidth = dest_width;
-  m_DestHeight = dest_height;
-  m_DestClip = clip_rect;
+                               const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                               const FXDIB_ResampleOptions& options)
+    : m_DestFormat(dest_format),
+      m_DestBpp(GetBppFromFormat(dest_format)),
+      m_SrcBpp(GetBppFromFormat(pSrcBitmap->GetFormat())),
+      m_bHasAlpha(GetIsAlphaFromFormat(pSrcBitmap->GetFormat())),
+      m_pSource(pSrcBitmap),
+      m_pSrcPalette(pSrcBitmap->GetPalette()),
+      m_SrcWidth(pSrcBitmap->GetWidth()),
+      m_SrcHeight(pSrcBitmap->GetHeight()),
+      m_pDestBitmap(pDestBitmap),
+      m_DestWidth(dest_width),
+      m_DestHeight(dest_height),
+      m_DestClip(clip_rect) {
   uint32_t size = clip_rect.Width();
   if (size && m_DestBpp > static_cast<int>(INT_MAX / size))
     return;
@@ -242,29 +247,24 @@
   if (size > INT_MAX - 31)
     return;
 
-  size += 31;
-  size = size / 32 * 4;
+  size = GetPitchRoundUpTo4Bytes(size);
   m_DestScanline.resize(size);
   if (dest_format == FXDIB_Rgb32)
     std::fill(m_DestScanline.begin(), m_DestScanline.end(), 255);
-  m_InterPitch = (m_DestClip.Width() * m_DestBpp + 31) / 32 * 4;
-  m_ExtraMaskPitch = (m_DestClip.Width() * 8 + 31) / 32 * 4;
-  m_pSource = pSrcBitmap;
-  m_SrcWidth = pSrcBitmap->GetWidth();
-  m_SrcHeight = pSrcBitmap->GetHeight();
-  m_SrcPitch = (m_SrcWidth * m_SrcBpp + 31) / 32 * 4;
-  if ((flags & FXDIB_NOSMOOTH) == 0) {
-    bool bInterpol = flags & FXDIB_INTERPOL || flags & FXDIB_BICUBIC_INTERPOL;
+  m_InterPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * m_DestBpp);
+  m_ExtraMaskPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * 8);
+  if (options.bNoSmoothing) {
+    m_ResampleOptions.bNoSmoothing = true;
+  } else {
+    bool bInterpol =
+        options.bInterpolateBilinear || options.bInterpolateBicubic;
     if (!bInterpol && abs(dest_width) != 0 &&
         abs(dest_height) / 8 < static_cast<long long>(m_SrcWidth) *
                                    m_SrcHeight / abs(dest_width)) {
-      flags = FXDIB_INTERPOL;
+      m_ResampleOptions.bInterpolateBilinear = true;
+    } else {
+      m_ResampleOptions = options;
     }
-    m_Flags = flags;
-  } else {
-    m_Flags = FXDIB_NOSMOOTH;
-    if (flags & FXDIB_DOWNSAMPLE)
-      m_Flags |= FXDIB_DOWNSAMPLE;
   }
   double scale_x = static_cast<float>(m_SrcWidth) / m_DestWidth;
   double scale_y = static_cast<float>(m_SrcHeight) / m_DestHeight;
@@ -284,39 +284,36 @@
   m_SrcClip.bottom = static_cast<int>(ceil(src_bottom));
   FX_RECT src_rect(0, 0, m_SrcWidth, m_SrcHeight);
   m_SrcClip.Intersect(src_rect);
-  if (m_SrcBpp == 1) {
-    if (m_DestBpp == 8)
-      m_TransMethod = 1;
-    else
-      m_TransMethod = 2;
-  } else if (m_SrcBpp == 8) {
-    if (m_DestBpp == 8) {
-      if (!m_bHasAlpha)
-        m_TransMethod = 3;
-      else
-        m_TransMethod = 4;
-    } else {
-      if (!m_bHasAlpha)
-        m_TransMethod = 5;
-      else
-        m_TransMethod = 6;
-    }
-  } else {
-    if (!m_bHasAlpha)
-      m_TransMethod = 7;
-    else
-      m_TransMethod = 8;
+
+  switch (m_SrcBpp) {
+    case 1:
+      m_TransMethod = m_DestBpp == 8 ? TransformMethod::k1BppTo8Bpp
+                                     : TransformMethod::k1BppToManyBpp;
+      break;
+    case 8:
+      if (m_DestBpp == 8) {
+        m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppTo8BppWithAlpha
+                                    : TransformMethod::k8BppTo8Bpp;
+      } else {
+        m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppToManyBppWithAlpha
+                                    : TransformMethod::k8BppToManyBpp;
+      }
+      break;
+    default:
+      m_TransMethod = m_bHasAlpha ? TransformMethod::kManyBpptoManyBppWithAlpha
+                                  : TransformMethod::kManyBpptoManyBpp;
+      break;
   }
 }
 
 CStretchEngine::~CStretchEngine() {}
 
-bool CStretchEngine::Continue(IFX_PauseIndicator* pPause) {
-  while (m_State == 1) {
+bool CStretchEngine::Continue(PauseIndicatorIface* pPause) {
+  while (m_State == State::kHorizontal) {
     if (ContinueStretchHorz(pPause))
       return true;
 
-    m_State = 2;
+    m_State = State::kVertical;
     StretchVert();
   }
   return false;
@@ -334,21 +331,20 @@
   m_InterBuf.resize(m_SrcClip.Height() * m_InterPitch);
   if (m_pSource && m_bHasAlpha && m_pSource->m_pAlphaMask) {
     m_ExtraAlphaBuf.resize(m_SrcClip.Height(), m_ExtraMaskPitch);
-    uint32_t size = (m_DestClip.Width() * 8 + 31) / 32 * 4;
-    m_DestMaskScanline.resize(size);
+    m_DestMaskScanline.resize(m_ExtraMaskPitch);
   }
-  bool ret =
-      m_WeightTable.Calc(m_DestWidth, m_DestClip.left, m_DestClip.right,
-                         m_SrcWidth, m_SrcClip.left, m_SrcClip.right, m_Flags);
+  bool ret = m_WeightTable.Calc(m_DestWidth, m_DestClip.left, m_DestClip.right,
+                                m_SrcWidth, m_SrcClip.left, m_SrcClip.right,
+                                m_ResampleOptions);
   if (!ret)
     return false;
 
   m_CurRow = m_SrcClip.top;
-  m_State = 1;
+  m_State = State::kHorizontal;
   return true;
 }
 
-bool CStretchEngine::ContinueStretchHorz(IFX_PauseIndicator* pPause) {
+bool CStretchEngine::ContinueStretchHorz(PauseIndicatorIface* pPause) {
   if (!m_DestWidth)
     return false;
   if (m_pSource->SkipToScanline(m_CurRow, pPause))
@@ -377,8 +373,8 @@
     }
     // TODO(npm): reduce duplicated code here
     switch (m_TransMethod) {
-      case 1:
-      case 2: {
+      case TransformMethod::k1BppTo8Bpp:
+      case TransformMethod::k1BppToManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
           int dest_a = 0;
@@ -391,13 +387,13 @@
             if (src_scan[j / 8] & (1 << (7 - j % 8)))
               dest_a += pixel_weight * 255;
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL)
+          if (m_ResampleOptions.bInterpolateBicubic)
             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
           *dest_scan++ = static_cast<uint8_t>(dest_a >> 16);
         }
         break;
       }
-      case 3: {
+      case TransformMethod::k8BppTo8Bpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
           int dest_a = 0;
@@ -409,13 +405,13 @@
             int pixel_weight = *pWeight;
             dest_a += pixel_weight * src_scan[j];
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL)
+          if (m_ResampleOptions.bInterpolateBicubic)
             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
           *dest_scan++ = static_cast<uint8_t>(dest_a >> 16);
         }
         break;
       }
-      case 4: {
+      case TransformMethod::k8BppTo8BppWithAlpha: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
           int dest_a = 0;
@@ -430,7 +426,7 @@
             dest_r += pixel_weight * src_scan[j];
             dest_a += pixel_weight;
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_r = pdfium::clamp(dest_r, 0, kMaxDestValue);
             dest_a = pdfium::clamp(dest_a, 0, 65536);
           }
@@ -439,7 +435,7 @@
         }
         break;
       }
-      case 5: {
+      case TransformMethod::k8BppToManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
           int dest_r_y = 0;
@@ -462,7 +458,7 @@
               dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
             }
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
@@ -473,7 +469,7 @@
         }
         break;
       }
-      case 6: {
+      case TransformMethod::k8BppToManyBppWithAlpha: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
           int dest_a = 0;
@@ -499,7 +495,7 @@
             }
             dest_a += pixel_weight;
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
@@ -512,7 +508,7 @@
         }
         break;
       }
-      case 7: {
+      case TransformMethod::kManyBpptoManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
           int dest_r_y = 0;
@@ -529,7 +525,7 @@
             dest_g_m += pixel_weight * (*src_pixel++);
             dest_r_y += pixel_weight * (*src_pixel);
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
@@ -541,7 +537,7 @@
         }
         break;
       }
-      case 8: {
+      case TransformMethod::kManyBpptoManyBppWithAlpha: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
           int dest_a = 0;
@@ -565,7 +561,7 @@
             dest_r_y += pixel_weight * (*src_pixel);
             dest_a += pixel_weight;
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
@@ -593,8 +589,9 @@
     return;
 
   CWeightTable table;
-  bool ret = table.Calc(m_DestHeight, m_DestClip.top, m_DestClip.bottom,
-                        m_SrcHeight, m_SrcClip.top, m_SrcClip.bottom, m_Flags);
+  bool ret =
+      table.Calc(m_DestHeight, m_DestClip.top, m_DestClip.bottom, m_SrcHeight,
+                 m_SrcClip.top, m_SrcClip.bottom, m_ResampleOptions);
   if (!ret)
     return;
 
@@ -604,9 +601,9 @@
     unsigned char* dest_scan_mask = m_DestMaskScanline.data();
     PixelWeight* pWeights = table.GetPixelWeight(row);
     switch (m_TransMethod) {
-      case 1:
-      case 2:
-      case 3: {
+      case TransformMethod::k1BppTo8Bpp:
+      case TransformMethod::k1BppToManyBpp:
+      case TransformMethod::k8BppTo8Bpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           unsigned char* src_scan =
               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
@@ -620,14 +617,14 @@
             dest_a +=
                 pixel_weight * src_scan[(j - m_SrcClip.top) * m_InterPitch];
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL)
+          if (m_ResampleOptions.bInterpolateBicubic)
             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
           *dest_scan = static_cast<uint8_t>(dest_a >> 16);
           dest_scan += DestBpp;
         }
         break;
       }
-      case 4: {
+      case TransformMethod::k8BppTo8BppWithAlpha: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           unsigned char* src_scan =
               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
@@ -646,7 +643,7 @@
             dest_a += pixel_weight *
                       src_scan_mask[(j - m_SrcClip.top) * m_ExtraMaskPitch];
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_k = pdfium::clamp(dest_k, 0, kMaxDestValue);
             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
           }
@@ -656,8 +653,8 @@
         }
         break;
       }
-      case 5:
-      case 7: {
+      case TransformMethod::k8BppToManyBpp:
+      case TransformMethod::kManyBpptoManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           unsigned char* src_scan =
               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
@@ -676,7 +673,7 @@
             dest_g_m += pixel_weight * (*src_pixel++);
             dest_r_y += pixel_weight * (*src_pixel);
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
@@ -688,8 +685,8 @@
         }
         break;
       }
-      case 6:
-      case 8: {
+      case TransformMethod::k8BppToManyBppWithAlpha:
+      case TransformMethod::kManyBpptoManyBppWithAlpha: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           unsigned char* src_scan =
               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
@@ -719,7 +716,7 @@
             else
               dest_a += pixel_weight * mask_v;
           }
-          if (m_Flags & FXDIB_BICUBIC_INTERPOL) {
+          if (m_ResampleOptions.bInterpolateBicubic) {
             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
diff --git a/core/fxge/dib/cstretchengine.h b/core/fxge/dib/cstretchengine.h
index 8ce8351..1792963 100644
--- a/core/fxge/dib/cstretchengine.h
+++ b/core/fxge/dib/cstretchengine.h
@@ -14,24 +14,25 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/fx_dib.h"
 
-class IFX_PauseIndicator;
-class IFX_ScanlineComposer;
+class CFX_DIBBase;
+class PauseIndicatorIface;
+class ScanlineComposerIface;
 
 class CStretchEngine {
  public:
-  CStretchEngine(IFX_ScanlineComposer* pDestBitmap,
+  CStretchEngine(ScanlineComposerIface* pDestBitmap,
                  FXDIB_Format dest_format,
                  int dest_width,
                  int dest_height,
                  const FX_RECT& clip_rect,
-                 const RetainPtr<CFX_DIBSource>& pSrcBitmap,
-                 int flags);
+                 const RetainPtr<CFX_DIBBase>& pSrcBitmap,
+                 const FXDIB_ResampleOptions& options);
   ~CStretchEngine();
 
-  bool Continue(IFX_PauseIndicator* pPause);
+  bool Continue(PauseIndicatorIface* pPause);
 
   bool StartStretchHorz();
-  bool ContinueStretchHorz(IFX_PauseIndicator* pPause);
+  bool ContinueStretchHorz(PauseIndicatorIface* pPause);
   void StretchVert();
 
   class CWeightTable {
@@ -45,43 +46,61 @@
               int src_len,
               int src_min,
               int src_max,
-              int flags);
-    PixelWeight* GetPixelWeight(int pixel) const;
+              const FXDIB_ResampleOptions& options);
+
+    const PixelWeight* GetPixelWeight(int pixel) const;
+    PixelWeight* GetPixelWeight(int pixel) {
+      return const_cast<PixelWeight*>(
+          static_cast<const CWeightTable*>(this)->GetPixelWeight(pixel));
+    }
+
     int* GetValueFromPixelWeight(PixelWeight* pWeight, int index) const;
     size_t GetPixelWeightSize() const;
 
    private:
-    int m_DestMin;
-    int m_ItemSize;
+    int m_DestMin = 0;
+    int m_ItemSize = 0;
+    size_t m_dwWeightTablesSize = 0;
     std::vector<uint8_t> m_WeightTables;
-    size_t m_dwWeightTablesSize;
   };
 
-  FXDIB_Format m_DestFormat;
-  int m_DestBpp;
-  int m_SrcBpp;
-  int m_bHasAlpha;
-  UnownedPtr<IFX_ScanlineComposer> m_pDestBitmap;
-  int m_DestWidth;
-  int m_DestHeight;
-  FX_RECT m_DestClip;
+  enum class State : uint8_t { kInitial, kHorizontal, kVertical };
+
+  enum class TransformMethod : uint8_t {
+    k1BppTo8Bpp,
+    k1BppToManyBpp,
+    k8BppTo8Bpp,
+    k8BppTo8BppWithAlpha,
+    k8BppToManyBpp,
+    k8BppToManyBppWithAlpha,
+    kManyBpptoManyBpp,
+    kManyBpptoManyBppWithAlpha
+  };
+
+  const FXDIB_Format m_DestFormat;
+  const int m_DestBpp;
+  const int m_SrcBpp;
+  const int m_bHasAlpha;
+  RetainPtr<CFX_DIBBase> const m_pSource;
+  const uint32_t* m_pSrcPalette;
+  const int m_SrcWidth;
+  const int m_SrcHeight;
+  UnownedPtr<ScanlineComposerIface> const m_pDestBitmap;
+  const int m_DestWidth;
+  const int m_DestHeight;
+  const FX_RECT m_DestClip;
   std::vector<uint8_t> m_DestScanline;
   std::vector<uint8_t> m_DestMaskScanline;
-  FX_RECT m_SrcClip;
-  RetainPtr<CFX_DIBSource> m_pSource;
-  uint32_t* m_pSrcPalette;
-  int m_SrcWidth;
-  int m_SrcHeight;
-  int m_SrcPitch;
-  int m_InterPitch;
-  int m_ExtraMaskPitch;
   std::vector<uint8_t> m_InterBuf;
   std::vector<uint8_t> m_ExtraAlphaBuf;
-  int m_TransMethod;
-  int m_Flags;
-  CWeightTable m_WeightTable;
+  FX_RECT m_SrcClip;
+  int m_InterPitch;
+  int m_ExtraMaskPitch;
+  FXDIB_ResampleOptions m_ResampleOptions;
+  TransformMethod m_TransMethod;
+  State m_State = State::kInitial;
   int m_CurRow;
-  int m_State;
+  CWeightTable m_WeightTable;
 };
 
 #endif  // CORE_FXGE_DIB_CSTRETCHENGINE_H_
diff --git a/core/fxge/dib/cstretchengine_unittest.cpp b/core/fxge/dib/cstretchengine_unittest.cpp
index 2c7e034..8c360e7 100644
--- a/core/fxge/dib/cstretchengine_unittest.cpp
+++ b/core/fxge/dib/cstretchengine_unittest.cpp
@@ -7,26 +7,28 @@
 #include <memory>
 #include <utility>
 
+#include "core/fpdfapi/page/cpdf_dib.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/render/cpdf_dibsource.h"
-#include "core/fxcrt/fx_memory.h"
 #include "core/fxge/fx_dib.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/ptr_util.h"
 
 TEST(CStretchEngine, OverflowInCtor) {
   FX_RECT clip_rect;
-  std::unique_ptr<CPDF_Dictionary> dict_obj =
-      pdfium::MakeUnique<CPDF_Dictionary>();
+  RetainPtr<CPDF_Dictionary> dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
   dict_obj->SetNewFor<CPDF_Number>("Width", 71000);
   dict_obj->SetNewFor<CPDF_Number>("Height", 12500);
-  std::unique_ptr<CPDF_Stream> stream =
-      pdfium::MakeUnique<CPDF_Stream>(nullptr, 0, std::move(dict_obj));
-  auto dib_source = pdfium::MakeRetain<CPDF_DIBSource>();
-  dib_source->Load(nullptr, stream.get());
+  RetainPtr<CPDF_Stream> stream =
+      pdfium::MakeRetain<CPDF_Stream>(nullptr, 0, std::move(dict_obj));
+  auto dib_source = pdfium::MakeRetain<CPDF_DIB>();
+  dib_source->Load(nullptr, stream.Get());
   CStretchEngine engine(nullptr, FXDIB_8bppRgb, 500, 500, clip_rect, dib_source,
-                        0);
-  EXPECT_EQ(FXDIB_INTERPOL, engine.m_Flags);
+                        FXDIB_ResampleOptions());
+  EXPECT_TRUE(engine.m_ResampleOptions.bInterpolateBilinear);
+  EXPECT_FALSE(engine.m_ResampleOptions.bInterpolateBicubic);
+  EXPECT_FALSE(engine.m_ResampleOptions.bHalftone);
+  EXPECT_FALSE(engine.m_ResampleOptions.bNoSmoothing);
+  EXPECT_FALSE(engine.m_ResampleOptions.bLossy);
 }
diff --git a/core/fxge/dib/fx_dib_main.cpp b/core/fxge/dib/fx_dib_main.cpp
index 68e06a6..31e5919 100644
--- a/core/fxge/dib/fx_dib_main.cpp
+++ b/core/fxge/dib/fx_dib_main.cpp
@@ -9,8 +9,13 @@
 #include <tuple>
 #include <utility>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
+
+#if defined(OS_WIN)
+static_assert(sizeof(FX_COLORREF) == sizeof(COLORREF),
+              "FX_COLORREF vs. COLORREF mismatch");
+#endif
 
 const int16_t SDP_Table[513] = {
     256, 256, 256, 256, 256, 256, 256, 256, 256, 255, 255, 255, 255, 255, 255,
@@ -50,7 +55,14 @@
     0,   0,   0,
 };
 
-FX_RECT FXDIB_SwapClipBox(FX_RECT& clip,
+FXDIB_ResampleOptions::FXDIB_ResampleOptions() = default;
+
+bool FXDIB_ResampleOptions::HasAnyOptions() const {
+  return bInterpolateBilinear || bInterpolateBicubic || bHalftone ||
+         bNoSmoothing || bLossy;
+}
+
+FX_RECT FXDIB_SwapClipBox(const FX_RECT& clip,
                           int width,
                           int height,
                           bool bFlipX,
@@ -79,32 +91,38 @@
                          FXARGB_B(argb));
 }
 
-std::pair<int, FX_COLORREF> ArgbToColorRef(FX_ARGB argb) {
-  return {FXARGB_A(argb),
-          FXSYS_RGB(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb))};
+std::pair<int, FX_COLORREF> ArgbToAlphaAndColorRef(FX_ARGB argb) {
+  return {FXARGB_A(argb), ArgbToColorRef(argb)};
 }
 
-uint32_t ArgbEncode(int a, FX_COLORREF rgb) {
-  return FXARGB_MAKE(a, FXSYS_GetRValue(rgb), FXSYS_GetGValue(rgb),
-                     FXSYS_GetBValue(rgb));
+FX_COLORREF ArgbToColorRef(FX_ARGB argb) {
+  return FXSYS_BGR(FXARGB_B(argb), FXARGB_G(argb), FXARGB_R(argb));
 }
 
-FX_ARGB StringToFXARGB(const WideStringView& wsValue) {
-  uint8_t r = 0, g = 0, b = 0;
-  if (wsValue.GetLength() == 0)
-    return 0xff000000;
+FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref) {
+  return ArgbEncode(a, FXSYS_GetRValue(colorref), FXSYS_GetGValue(colorref),
+                    FXSYS_GetBValue(colorref));
+}
+
+FX_ARGB StringToFXARGB(WideStringView view) {
+  static constexpr FX_ARGB kDefaultValue = 0xff000000;
+  if (view.IsEmpty())
+    return kDefaultValue;
 
   int cc = 0;
-  const wchar_t* str = wsValue.unterminated_c_str();
-  int len = wsValue.GetLength();
-  while (FXSYS_iswspace(str[cc]) && cc < len)
+  const wchar_t* str = view.unterminated_c_str();
+  int len = view.GetLength();
+  while (cc < len && FXSYS_iswspace(str[cc]))
     cc++;
 
   if (cc >= len)
-    return 0xff000000;
+    return kDefaultValue;
 
+  uint8_t r = 0;
+  uint8_t g = 0;
+  uint8_t b = 0;
   while (cc < len) {
-    if (str[cc] == ',' || !FXSYS_isDecimalDigit(str[cc]))
+    if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
       break;
 
     r = r * 10 + str[cc] - '0';
@@ -112,11 +130,11 @@
   }
   if (cc < len && str[cc] == ',') {
     cc++;
-    while (FXSYS_iswspace(str[cc]) && cc < len)
+    while (cc < len && FXSYS_iswspace(str[cc]))
       cc++;
 
     while (cc < len) {
-      if (str[cc] == ',' || !FXSYS_isDecimalDigit(str[cc]))
+      if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
         break;
 
       g = g * 10 + str[cc] - '0';
@@ -124,11 +142,11 @@
     }
     if (cc < len && str[cc] == ',') {
       cc++;
-      while (FXSYS_iswspace(str[cc]) && cc < len)
+      while (cc < len && FXSYS_iswspace(str[cc]))
         cc++;
 
       while (cc < len) {
-        if (str[cc] == ',' || !FXSYS_isDecimalDigit(str[cc]))
+        if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc]))
           break;
 
         b = b * 10 + str[cc] - '0';
@@ -136,5 +154,5 @@
       }
     }
   }
-  return (0xff << 24) | (r << 16) | (g << 8) | b;
+  return (0xffU << 24) | (r << 16) | (g << 8) | b;
 }
diff --git a/core/fxge/dib/ifx_scanlinecomposer.h b/core/fxge/dib/ifx_scanlinecomposer.h
deleted file mode 100644
index 7b070c0..0000000
--- a/core/fxge/dib/ifx_scanlinecomposer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_DIB_IFX_SCANLINECOMPOSER_H_
-#define CORE_FXGE_DIB_IFX_SCANLINECOMPOSER_H_
-
-#include "core/fxge/fx_dib.h"
-
-class IFX_ScanlineComposer {
- public:
-  virtual ~IFX_ScanlineComposer() {}
-
-  virtual void ComposeScanline(int line,
-                               const uint8_t* scanline,
-                               const uint8_t* scan_extra_alpha) = 0;
-
-  virtual bool SetInfo(int width,
-                       int height,
-                       FXDIB_Format src_format,
-                       uint32_t* pSrcPalette) = 0;
-};
-
-#endif  // CORE_FXGE_DIB_IFX_SCANLINECOMPOSER_H_
diff --git a/core/fxge/dib/scanlinecomposer_iface.h b/core/fxge/dib/scanlinecomposer_iface.h
new file mode 100644
index 0000000..316736f
--- /dev/null
+++ b/core/fxge/dib/scanlinecomposer_iface.h
@@ -0,0 +1,26 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_
+#define CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_
+
+#include "core/fxge/fx_dib.h"
+
+class ScanlineComposerIface {
+ public:
+  virtual ~ScanlineComposerIface() = default;
+
+  virtual void ComposeScanline(int line,
+                               const uint8_t* scanline,
+                               const uint8_t* scan_extra_alpha) = 0;
+
+  virtual bool SetInfo(int width,
+                       int height,
+                       FXDIB_Format src_format,
+                       uint32_t* pSrcPalette) = 0;
+};
+
+#endif  // CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_
diff --git a/core/fxge/fontdata/chromefontdata/chromefontdata.h b/core/fxge/fontdata/chromefontdata/chromefontdata.h
index 3cd8d1a..cbd1ad2 100644
--- a/core/fxge/fontdata/chromefontdata/chromefontdata.h
+++ b/core/fxge/fontdata/chromefontdata/chromefontdata.h
@@ -7,10 +7,6 @@
 #ifndef CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_
 #define CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 extern const unsigned char g_FoxitFixedItalicFontData[18746];
 extern const unsigned char g_FoxitFixedFontData[17597];
 extern const unsigned char g_FoxitSansItalicFontData[16339];
@@ -28,8 +24,4 @@
 extern const unsigned char g_FoxitSerifMMFontData[113417];
 extern const unsigned char g_FoxitSansMMFontData[66919];
 
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
 #endif  // CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_
diff --git a/core/fxge/freetype/fx_freetype.cpp b/core/fxge/freetype/fx_freetype.cpp
index b0f56ab..fb1c29a 100644
--- a/core/fxge/freetype/fx_freetype.cpp
+++ b/core/fxge/freetype/fx_freetype.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fxge/fx_freetype.h"
 
+#define DEFINE_PS_TABLES
 #include "third_party/freetype/include/pstables.h"
 
 static int xyq_search_node(char* glyph_name,
diff --git a/core/fxge/fx_dib.h b/core/fxge/fx_dib.h
index f7626af..b2dbe8a 100644
--- a/core/fxge/fx_dib.h
+++ b/core/fxge/fx_dib.h
@@ -37,72 +37,121 @@
   int m_Weights[1];
 };
 
-typedef uint32_t FX_ARGB;
-typedef uint32_t FX_COLORREF;
-typedef uint32_t FX_CMYK;
-class CFX_ClipRgn;
-class CFX_DIBSource;
-class CStretchEngine;
+using FX_ARGB = uint32_t;
+
+// FX_COLORREF, like win32 COLORREF, is BGR.
+using FX_COLORREF = uint32_t;
+
+using FX_CMYK = uint32_t;
 
 extern const int16_t SDP_Table[513];
 
-#define FXDIB_DOWNSAMPLE 0x04
-#define FXDIB_INTERPOL 0x20
-#define FXDIB_BICUBIC_INTERPOL 0x80
-#define FXDIB_NOSMOOTH 0x100
+struct FXDIB_ResampleOptions {
+  FXDIB_ResampleOptions();
 
-#define FXDIB_BLEND_NORMAL 0
-#define FXDIB_BLEND_MULTIPLY 1
-#define FXDIB_BLEND_SCREEN 2
-#define FXDIB_BLEND_OVERLAY 3
-#define FXDIB_BLEND_DARKEN 4
-#define FXDIB_BLEND_LIGHTEN 5
-#define FXDIB_BLEND_COLORDODGE 6
-#define FXDIB_BLEND_COLORBURN 7
-#define FXDIB_BLEND_HARDLIGHT 8
-#define FXDIB_BLEND_SOFTLIGHT 9
-#define FXDIB_BLEND_DIFFERENCE 10
-#define FXDIB_BLEND_EXCLUSION 11
-#define FXDIB_BLEND_NONSEPARABLE 21
-#define FXDIB_BLEND_HUE 21
-#define FXDIB_BLEND_SATURATION 22
-#define FXDIB_BLEND_COLOR 23
-#define FXDIB_BLEND_LUMINOSITY 24
-#define FXDIB_BLEND_UNSUPPORTED -1
+  bool HasAnyOptions() const;
 
-#define FXSYS_RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
-#define FXSYS_GetRValue(rgb) ((rgb)&0xff)
-#define FXSYS_GetGValue(rgb) (((rgb) >> 8) & 0xff)
-#define FXSYS_GetBValue(rgb) (((rgb) >> 16) & 0xff)
+  bool bInterpolateBilinear = false;
+  bool bInterpolateBicubic = false;
+  bool bHalftone = false;
+  bool bNoSmoothing = false;
+  bool bLossy = false;
+};
+
+// See PDF 1.7 spec, table 7.2 and 7.3. The enum values need to be in the same
+// order as listed in the spec.
+enum class BlendMode {
+  kNormal = 0,
+  kMultiply,
+  kScreen,
+  kOverlay,
+  kDarken,
+  kLighten,
+  kColorDodge,
+  kColorBurn,
+  kHardLight,
+  kSoftLight,
+  kDifference,
+  kExclusion,
+  kHue,
+  kSaturation,
+  kColor,
+  kLuminosity,
+  kLast = kLuminosity,
+};
+
+constexpr uint32_t FXSYS_BGR(uint8_t b, uint8_t g, uint8_t r) {
+  return (b << 16) | (g << 8) | r;
+}
+
+constexpr uint8_t FXSYS_GetRValue(uint32_t bgr) {
+  return bgr & 0xff;
+}
+
+constexpr uint8_t FXSYS_GetGValue(uint32_t bgr) {
+  return (bgr >> 8) & 0xff;
+}
+
+constexpr uint8_t FXSYS_GetBValue(uint32_t bgr) {
+  return (bgr >> 16) & 0xff;
+}
+
+constexpr unsigned int FXSYS_GetUnsignedAlpha(float alpha) {
+  return static_cast<unsigned int>(alpha * 255.f + 0.5f);
+}
 
 #define FXSYS_GetCValue(cmyk) ((uint8_t)((cmyk) >> 24) & 0xff)
 #define FXSYS_GetMValue(cmyk) ((uint8_t)((cmyk) >> 16) & 0xff)
 #define FXSYS_GetYValue(cmyk) ((uint8_t)((cmyk) >> 8) & 0xff)
 #define FXSYS_GetKValue(cmyk) ((uint8_t)(cmyk)&0xff)
 
+// Bits per pixel, not bytes.
+inline int GetBppFromFormat(FXDIB_Format format) {
+  return format & 0xff;
+}
+
+// AKA bytes per pixel, assuming 8-bits per component.
+inline int GetCompsFromFormat(FXDIB_Format format) {
+  return (format & 0xff) / 8;
+}
+
+inline uint32_t GetAlphaFlagFromFormat(FXDIB_Format format) {
+  return (format >> 8) & 0xff;
+}
+
+inline bool GetIsAlphaFromFormat(FXDIB_Format format) {
+  return format & 0x200;
+}
+
+inline bool GetIsCmykFromFormat(FXDIB_Format format) {
+  return format & 0x400;
+}
+
 inline FX_CMYK CmykEncode(int c, int m, int y, int k) {
   return (c << 24) | (m << 16) | (y << 8) | k;
 }
 
-// Returns tuple a, r, g, b
+// Returns (a, r, g, b)
 std::tuple<int, int, int, int> ArgbDecode(FX_ARGB argb);
 
-// Returns pair a, rgb
-std::pair<int, FX_COLORREF> ArgbToColorRef(FX_ARGB argb);
+// Returns (a, FX_COLORREF)
+std::pair<int, FX_COLORREF> ArgbToAlphaAndColorRef(FX_ARGB argb);
 
-inline FX_ARGB ArgbEncode(int a, int r, int g, int b) {
+// Returns FX_COLORREF.
+FX_COLORREF ArgbToColorRef(FX_ARGB argb);
+
+constexpr FX_ARGB ArgbEncode(int a, int r, int g, int b) {
   return (a << 24) | (r << 16) | (g << 8) | b;
 }
-FX_ARGB ArgbEncode(int a, FX_COLORREF rgb);
 
-FX_ARGB StringToFXARGB(const WideStringView& view);
+FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref);
+
+FX_ARGB StringToFXARGB(WideStringView view);
 
 #define FXARGB_A(argb) ((uint8_t)((argb) >> 24))
 #define FXARGB_R(argb) ((uint8_t)((argb) >> 16))
 #define FXARGB_G(argb) ((uint8_t)((argb) >> 8))
 #define FXARGB_B(argb) ((uint8_t)(argb))
-#define FXARGB_MAKE(a, r, g, b) \
-  (((uint32_t)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
 #define FXARGB_MUL_ALPHA(argb, alpha) \
   (((((argb) >> 24) * (alpha) / 255) << 24) | ((argb)&0xffffff))
 
@@ -130,7 +179,7 @@
   ((uint8_t)(argb >> 16) | ((uint8_t)(argb >> 8)) << 8 | \
    ((uint8_t)(argb)) << 16 | ((uint8_t)(argb >> 24) << 24))
 
-FX_RECT FXDIB_SwapClipBox(FX_RECT& clip,
+FX_RECT FXDIB_SwapClipBox(const FX_RECT& clip,
                           int width,
                           int height,
                           bool bFlipX,
diff --git a/core/fxge/fx_font.cpp b/core/fxge/fx_font.cpp
new file mode 100644
index 0000000..fe2d6b1
--- /dev/null
+++ b/core/fxge/fx_font.cpp
@@ -0,0 +1,147 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/fx_font.h"
+
+#include <algorithm>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/cfx_glyphbitmap.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/text_glyph_pos.h"
+
+namespace {
+
+// These numbers come from the OpenType name table specification.
+constexpr uint16_t kNamePlatformMac = 1;
+constexpr uint16_t kNameMacEncodingRoman = 0;
+constexpr uint16_t kNamePlatformWindows = 3;
+constexpr uint16_t kNameWindowsEncodingUnicode = 1;
+
+ByteString GetStringFromTable(pdfium::span<const uint8_t> string_span,
+                              uint16_t offset,
+                              uint16_t length) {
+  if (string_span.size() < static_cast<uint32_t>(offset + length))
+    return ByteString();
+
+  string_span = string_span.subspan(offset, length);
+  return ByteString(string_span.data(), string_span.size());
+}
+
+}  // namespace
+
+FX_RECT GetGlyphsBBox(const std::vector<TextGlyphPos>& glyphs, int anti_alias) {
+  FX_RECT rect;
+  bool bStarted = false;
+  for (const TextGlyphPos& glyph : glyphs) {
+    if (!glyph.m_pGlyph)
+      continue;
+
+    Optional<CFX_Point> point = glyph.GetOrigin({0, 0});
+    if (!point.has_value())
+      continue;
+
+    int char_width = glyph.m_pGlyph->GetBitmap()->GetWidth();
+    if (anti_alias == FT_RENDER_MODE_LCD)
+      char_width /= 3;
+
+    FX_SAFE_INT32 char_right = point.value().x;
+    char_right += char_width;
+    if (!char_right.IsValid())
+      continue;
+
+    FX_SAFE_INT32 char_bottom = point.value().y;
+    char_bottom += glyph.m_pGlyph->GetBitmap()->GetHeight();
+    if (!char_bottom.IsValid())
+      continue;
+
+    if (bStarted) {
+      rect.left = std::min(rect.left, point.value().x);
+      rect.top = std::min(rect.top, point.value().y);
+      rect.right = pdfium::base::ValueOrDieForType<int32_t>(
+          pdfium::base::CheckMax(rect.right, char_right));
+      rect.bottom = pdfium::base::ValueOrDieForType<int32_t>(
+          pdfium::base::CheckMax(rect.bottom, char_bottom));
+      continue;
+    }
+
+    rect.left = point.value().x;
+    rect.top = point.value().y;
+    rect.right = char_right.ValueOrDie();
+    rect.bottom = char_bottom.ValueOrDie();
+    bStarted = true;
+  }
+  return rect;
+}
+
+ByteString GetNameFromTT(pdfium::span<const uint8_t> name_table,
+                         uint32_t name_id) {
+  if (name_table.size() < 6)
+    return ByteString();
+
+  uint32_t name_count = GET_TT_SHORT(&name_table[2]);
+  uint32_t string_offset = GET_TT_SHORT(&name_table[4]);
+  // We will ignore the possibility of overlap of structures and
+  // string table as if it's all corrupt there's not a lot we can do.
+  if (name_table.size() < string_offset)
+    return ByteString();
+
+  pdfium::span<const uint8_t> string_span = name_table.subspan(string_offset);
+  name_table = name_table.subspan(6);
+  if (name_table.size() < name_count * 12)
+    return ByteString();
+
+  for (uint32_t i = 0; i < name_count;
+       i++, name_table = name_table.subspan(12)) {
+    if (GET_TT_SHORT(&name_table[6]) == name_id) {
+      const uint16_t platform_identifier = GET_TT_SHORT(name_table);
+      const uint16_t platform_encoding = GET_TT_SHORT(&name_table[2]);
+
+      if (platform_identifier == kNamePlatformMac &&
+          platform_encoding == kNameMacEncodingRoman) {
+        return GetStringFromTable(string_span, GET_TT_SHORT(&name_table[10]),
+                                  GET_TT_SHORT(&name_table[8]));
+      }
+      if (platform_identifier == kNamePlatformWindows &&
+          platform_encoding == kNameWindowsEncodingUnicode) {
+        // This name is always UTF16-BE and we have to convert it to UTF8.
+        ByteString utf16_be =
+            GetStringFromTable(string_span, GET_TT_SHORT(&name_table[10]),
+                               GET_TT_SHORT(&name_table[8]));
+        if (utf16_be.IsEmpty() || utf16_be.GetLength() % 2 != 0) {
+          return ByteString();
+        }
+
+        pdfium::span<const uint8_t> raw_span = utf16_be.raw_span();
+        return WideString::FromUTF16BE(
+                   reinterpret_cast<const uint16_t*>(raw_span.data()),
+                   raw_span.size() / 2)
+            .ToUTF8();
+      }
+    }
+  }
+  return ByteString();
+}
+
+int GetTTCIndex(pdfium::span<const uint8_t> pFontData, uint32_t font_offset) {
+  const uint8_t* p = pFontData.data() + 8;
+  uint32_t nfont = GET_TT_LONG(p);
+  uint32_t index;
+  for (index = 0; index < nfont; index++) {
+    p = pFontData.data() + 12 + index * 4;
+    if (GET_TT_LONG(p) == font_offset)
+      return index;
+  }
+  return 0;
+}
+
+wchar_t PDF_UnicodeFromAdobeName(const char* name) {
+  return (wchar_t)(FXFT_unicode_from_adobe_name(name) & 0x7FFFFFFF);
+}
+
+ByteString PDF_AdobeNameFromUnicode(wchar_t unicode) {
+  char glyph_name[64];
+  FXFT_adobe_name_from_unicode(glyph_name, unicode);
+  return ByteString(glyph_name);
+}
diff --git a/core/fxge/fx_font.h b/core/fxge/fx_font.h
index c617376..b748d55 100644
--- a/core/fxge/fx_font.h
+++ b/core/fxge/fx_font.h
@@ -7,22 +7,13 @@
 #ifndef CORE_FXGE_FX_FONT_H_
 #define CORE_FXGE_FX_FONT_H_
 
-#include <memory>
-#include <utility>
 #include <vector>
 
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/cfx_substfont.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
 #include "core/fxge/fx_freetype.h"
-
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-class SkTypeface;
-
-using CFX_TypeFace = SkTypeface;
-#endif
+#include "third_party/base/span.h"
 
 /* Font pitch and family flags */
 #define FXFONT_FF_FIXEDPITCH (1 << 0)
@@ -44,69 +35,31 @@
 #define FXFONT_ITALIC (1 << 6)
 #define FXFONT_ALLCAP (1 << 16)
 #define FXFONT_SMALLCAP (1 << 17)
-#define FXFONT_BOLD (1 << 18)
+#define FXFONT_FORCE_BOLD (1 << 18)
 
 /* Other font flags */
 #define FXFONT_USEEXTERNATTR 0x80000
 #define FXFONT_CIDFONT 0x100000
-#ifdef PDF_ENABLE_XFA
-#define FXFONT_EXACTMATCH 0x80000000
-#endif  // PDF_ENABLE_XFA
 
 #define GET_TT_SHORT(w) (uint16_t)(((w)[0] << 8) | (w)[1])
 #define GET_TT_LONG(w) \
   (uint32_t)(((w)[0] << 24) | ((w)[1] << 16) | ((w)[2] << 8) | (w)[3])
 
-// Sets the given transform on the font, and resets it to the identity when it
-// goes out of scope.
-class ScopedFontTransform {
- public:
-  ScopedFontTransform(FT_Face face, FXFT_Matrix* matrix);
-  ~ScopedFontTransform();
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+class SkTypeface;
 
- private:
-  FT_Face m_Face;
-};
+using CFX_TypeFace = SkTypeface;
+#endif
 
-class CFX_GlyphBitmap {
- public:
-  CFX_GlyphBitmap();
-  ~CFX_GlyphBitmap();
+class TextGlyphPos;
 
-  int m_Top;
-  int m_Left;
-  RetainPtr<CFX_DIBitmap> m_pBitmap;
-};
+FX_RECT GetGlyphsBBox(const std::vector<TextGlyphPos>& glyphs, int anti_alias);
 
-inline CFX_GlyphBitmap::CFX_GlyphBitmap()
-    : m_pBitmap(pdfium::MakeRetain<CFX_DIBitmap>()) {}
+ByteString GetNameFromTT(pdfium::span<const uint8_t> name_table, uint32_t name);
+int GetTTCIndex(pdfium::span<const uint8_t> pFontData, uint32_t font_offset);
 
-inline CFX_GlyphBitmap::~CFX_GlyphBitmap() = default;
-
-class FXTEXT_GLYPHPOS {
- public:
-  FXTEXT_GLYPHPOS();
-  FXTEXT_GLYPHPOS(const FXTEXT_GLYPHPOS&);
-  ~FXTEXT_GLYPHPOS();
-
-  const CFX_GlyphBitmap* m_pGlyph;
-  CFX_Point m_Origin;
-  CFX_PointF m_fOrigin;
-};
-
-FX_RECT FXGE_GetGlyphsBBox(const std::vector<FXTEXT_GLYPHPOS>& glyphs,
-                           int anti_alias,
-                           float retinaScaleX,
-                           float retinaScaleY);
-
-ByteString GetNameFromTT(const uint8_t* name_table,
-                         uint32_t name_table_size,
-                         uint32_t name);
-
-int PDF_GetStandardFontName(ByteString* name);
-
-inline bool FontStyleIsBold(uint32_t style) {
-  return !!(style & FXFONT_BOLD);
+inline bool FontStyleIsForceBold(uint32_t style) {
+  return !!(style & FXFONT_FORCE_BOLD);
 }
 inline bool FontStyleIsItalic(uint32_t style) {
   return !!(style & FXFONT_ITALIC);
@@ -140,4 +93,7 @@
   return !!(family & FXFONT_FF_SCRIPT);
 }
 
+wchar_t PDF_UnicodeFromAdobeName(const char* name);
+ByteString PDF_AdobeNameFromUnicode(wchar_t unicode);
+
 #endif  // CORE_FXGE_FX_FONT_H_
diff --git a/core/fxge/fx_font_unittest.cpp b/core/fxge/fx_font_unittest.cpp
new file mode 100644
index 0000000..e550f0a
--- /dev/null
+++ b/core/fxge/fx_font_unittest.cpp
@@ -0,0 +1,44 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "core/fxge/cfx_folderfontinfo.h"
+#include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/fx_font.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/path_service.h"
+
+TEST(FXFontTest, PDF_AdobeNameFromUnicode) {
+  EXPECT_STREQ("", PDF_AdobeNameFromUnicode(0x0000).c_str());
+  EXPECT_STREQ("divide", PDF_AdobeNameFromUnicode(0x00f7).c_str());
+  EXPECT_STREQ("Lslash", PDF_AdobeNameFromUnicode(0x0141).c_str());
+  EXPECT_STREQ("tonos", PDF_AdobeNameFromUnicode(0x0384).c_str());
+  EXPECT_STREQ("afii57513", PDF_AdobeNameFromUnicode(0x0691).c_str());
+  EXPECT_STREQ("angkhankhuthai", PDF_AdobeNameFromUnicode(0x0e5a).c_str());
+  EXPECT_STREQ("Euro", PDF_AdobeNameFromUnicode(0x20ac).c_str());
+}
+
+TEST(FXFontTest, ReadFontNameFromMicrosoftEntries) {
+  std::string test_data_dir;
+  PathService::GetTestDataDir(&test_data_dir);
+  ASSERT(!test_data_dir.empty());
+
+  CFX_FontMapper font_mapper(nullptr);
+
+  {
+    // |folder_font_info| has to be deallocated before the |font_mapper| or we
+    // run into UnownedPtr class issues with ASAN.
+    CFX_FolderFontInfo folder_font_info;
+    folder_font_info.AddPath(
+        (test_data_dir + PATH_SEPARATOR + "font_tests").c_str());
+
+    font_mapper.SetSystemFontInfo(SystemFontInfoIface::CreateDefault(nullptr));
+    ASSERT_TRUE(folder_font_info.EnumFontList(&font_mapper));
+  }
+
+  ASSERT_EQ(1, font_mapper.GetFaceSize());
+  ASSERT_EQ("Test", font_mapper.GetFaceName(0));
+}
diff --git a/core/fxge/fx_freetype.h b/core/fxge/fx_freetype.h
index 8a5fd5d..2dd379a 100644
--- a/core/fxge/fx_freetype.h
+++ b/core/fxge/fx_freetype.h
@@ -8,6 +8,9 @@
 #define CORE_FXGE_FX_FREETYPE_H_
 
 #include <ft2build.h>
+
+#include <memory>
+
 #include FT_FREETYPE_H
 #include FT_GLYPH_H
 #include FT_LCD_FILTER_H
@@ -15,180 +18,86 @@
 #include FT_OUTLINE_H
 #include FT_TRUETYPE_TABLES_H
 
-using FXFT_Face = struct FT_FaceRec_*;
-using FXFT_Library = void*;
+using FXFT_LibraryRec = struct FT_LibraryRec_;
+using FXFT_FaceRec = struct FT_FaceRec_;
+using FXFT_StreamRec = struct FT_StreamRec_;
+using FXFT_MM_VarPtr = FT_MM_Var*;
 
-using FXFT_MM_Var = FT_MM_Var*;
-using FXFT_Open_Args = FT_Open_Args;
-using FXFT_StreamRec = FT_StreamRec;
-using FXFT_Stream = FT_StreamRec*;
-using FXFT_BBox = FT_BBox;
-using FXFT_Glyph = FT_Glyph;
-using FXFT_CharMap = FT_CharMap;
+struct FXFTFaceRecDeleter {
+  inline void operator()(FXFT_FaceRec* pRec) {
+    if (pRec)
+      FT_Done_Face(pRec);
+  }
+};
 
-using FXFT_Matrix = FT_Matrix;
-using FXFT_Vector = FT_Vector;
-using FXFT_Outline_Funcs = FT_Outline_Funcs;
+struct FXFTLibraryRecDeleter {
+  inline void operator()(FXFT_LibraryRec* pRec) {
+    if (pRec)
+      FT_Done_FreeType(pRec);
+  }
+};
 
-#define FXFT_ENCODING_UNICODE FT_ENCODING_UNICODE
-#define FXFT_ENCODING_ADOBE_STANDARD FT_ENCODING_ADOBE_STANDARD
-#define FXFT_ENCODING_ADOBE_EXPERT FT_ENCODING_ADOBE_EXPERT
-#define FXFT_ENCODING_ADOBE_LATIN_1 FT_ENCODING_ADOBE_LATIN_1
-#define FXFT_ENCODING_APPLE_ROMAN FT_ENCODING_APPLE_ROMAN
-#define FXFT_ENCODING_ADOBE_CUSTOM FT_ENCODING_ADOBE_CUSTOM
-#define FXFT_ENCODING_MS_SYMBOL FT_ENCODING_MS_SYMBOL
-#define FXFT_ENCODING_GB2312 FT_ENCODING_GB2312
-#define FXFT_ENCODING_BIG5 FT_ENCODING_BIG5
-#define FXFT_ENCODING_SJIS FT_ENCODING_SJIS
-#define FXFT_ENCODING_JOHAB FT_ENCODING_JOHAB
+using ScopedFXFTFaceRec = std::unique_ptr<FXFT_FaceRec, FXFTFaceRecDeleter>;
+using ScopedFXFTLibraryRec =
+    std::unique_ptr<FXFT_LibraryRec, FXFTLibraryRecDeleter>;
 
-#define FXFT_LOAD_NO_SCALE FT_LOAD_NO_SCALE
-#define FXFT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH \
-  FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
-#define FXFT_LOAD_NO_BITMAP FT_LOAD_NO_BITMAP
-
-#define FXFT_RENDER_MODE_LCD FT_RENDER_MODE_LCD
-#define FXFT_RENDER_MODE_MONO FT_RENDER_MODE_MONO
-#define FXFT_RENDER_MODE_NORMAL FT_RENDER_MODE_NORMAL
-
-#define FXFT_PIXEL_MODE_MONO FT_PIXEL_MODE_MONO
-
-#define FXFT_STYLE_FLAG_ITALIC FT_STYLE_FLAG_ITALIC
-#define FXFT_STYLE_FLAG_BOLD FT_STYLE_FLAG_BOLD
-
-#define FXFT_FACE_FLAG_SFNT FT_FACE_FLAG_SFNT
-#define FXFT_FACE_FLAG_TRICKY (1L << 13)
-
-#define FXFT_GLYPH_BBOX_PIXELS FT_GLYPH_BBOX_PIXELS
-
-#define FXFT_Open_Face(library, args, index, face)            \
-  FT_Open_Face(static_cast<FT_Library>(library), args, index, \
-               static_cast<FT_Face*>(face))
-#define FXFT_Done_Face(face) FT_Done_Face(static_cast<FT_Face>(face))
-#define FXFT_Done_FreeType(library) \
-  FT_Done_FreeType(static_cast<FT_Library>(library))
-#define FXFT_Init_FreeType(library) \
-  FT_Init_FreeType(reinterpret_cast<FT_Library*>(library))
-#define FXFT_Library_Version(library, amajor, aminor, apatch)               \
-  FT_Library_Version(reinterpret_cast<FT_Library>(library), amajor, aminor, \
-                     apatch)
-#define FXFT_New_Memory_Face(library, base, size, index, face)            \
-  FT_New_Memory_Face(static_cast<FT_Library>(library), base, size, index, \
-                     static_cast<FT_Face*>(face))
-#define FXFT_New_Face(library, filename, index, face)            \
-  FT_New_Face(static_cast<FT_Library>(library), filename, index, \
-              static_cast<FT_Face*>(face))
-#define FXFT_Select_Charmap(face, encoding)     \
-  FT_Select_Charmap(static_cast<FT_Face>(face), \
-                    static_cast<FT_Encoding>(encoding))
-#define FXFT_Set_Charmap(face, charmap) \
-  FT_Set_Charmap(static_cast<FT_Face>(face), static_cast<FT_CharMap>(charmap))
-#define FXFT_Load_Glyph(face, glyph_index, flags) \
-  FT_Load_Glyph(static_cast<FT_Face>(face), glyph_index, flags)
-#define FXFT_Get_Char_Index(face, code) \
-  FT_Get_Char_Index(static_cast<FT_Face>(face), code)
-#define FXFT_Get_Glyph_Name(face, index, buffer, size) \
-  FT_Get_Glyph_Name(static_cast<FT_Face>(face), index, buffer, size)
+#define FXFT_Select_Charmap(face, encoding) \
+  FT_Select_Charmap(face, static_cast<FT_Encoding>(encoding))
 #define FXFT_Get_Name_Index(face, name) \
-  FT_Get_Name_Index(static_cast<FT_Face>(face), name)
+  FT_Get_Name_Index(face, const_cast<char*>(name))
+#define FXFT_Get_Glyph_Outline(face) &((face)->glyph->outline)
+#define FXFT_Render_Glyph(face, mode) \
+  FT_Render_Glyph((face)->glyph, static_cast<enum FT_Render_Mode_>(mode))
+
 #define FXFT_Has_Glyph_Names(face) \
-  (static_cast<FT_Face>(face)->face_flags & FT_FACE_FLAG_GLYPH_NAMES)
-#define FXFT_Get_Postscript_Name(face) \
-  FT_Get_Postscript_Name(static_cast<FT_Face>(face))
-#define FXFT_Load_Sfnt_Table(face, tag, offset, buffer, length) \
-  FT_Load_Sfnt_Table(static_cast<FT_Face>(face), tag, offset, buffer, length)
-#define FXFT_Get_First_Char(face, glyph_index) \
-  FT_Get_First_Char(static_cast<FT_Face>(face), glyph_index)
-#define FXFT_Get_Next_Char(face, code, glyph_index) \
-  FT_Get_Next_Char(static_cast<FT_Face>(face), code, glyph_index)
+  (((face)->face_flags) & FT_FACE_FLAG_GLYPH_NAMES)
 #define FXFT_Clear_Face_External_Stream(face) \
-  (static_cast<FT_Face>(face)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM)
+  ((face)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM)
 #define FXFT_Get_Face_External_Stream(face) \
-  (static_cast<FT_Face>(face)->face_flags & FT_FACE_FLAG_EXTERNAL_STREAM)
-#define FXFT_Is_Face_TT_OT(face) \
-  (static_cast<FT_Face>(face)->face_flags & FT_FACE_FLAG_SFNT)
-#define FXFT_Is_Face_Tricky(face) \
-  (static_cast<FT_Face>(face)->face_flags & FXFT_FACE_FLAG_TRICKY)
+  (((face)->face_flags) & FT_FACE_FLAG_EXTERNAL_STREAM)
+#define FXFT_Is_Face_TT_OT(face) (((face)->face_flags) & FT_FACE_FLAG_SFNT)
+#define FXFT_Is_Face_Tricky(face) (((face)->face_flags) & FT_FACE_FLAG_TRICKY)
 #define FXFT_Is_Face_fixedwidth(face) \
-  (static_cast<FT_Face>(face)->face_flags & FT_FACE_FLAG_FIXED_WIDTH)
-#define FXFT_Get_Face_Stream_Base(face) static_cast<FT_Face>(face)->stream->base
-#define FXFT_Get_Face_Stream_Size(face) static_cast<FT_Face>(face)->stream->size
-#define FXFT_Get_Face_Family_Name(face) static_cast<FT_Face>(face)->family_name
-#define FXFT_Get_Face_Style_Name(face) static_cast<FT_Face>(face)->style_name
-#define FXFT_Is_Face_Italic(face) \
-  (static_cast<FT_Face>(face)->style_flags & FT_STYLE_FLAG_ITALIC)
-#define FXFT_Is_Face_Bold(face) \
-  (static_cast<FT_Face>(face)->style_flags & FT_STYLE_FLAG_BOLD)
-#define FXFT_Get_Face_Charmaps(face) static_cast<FT_Face>(face)->charmaps
-#define FXFT_Get_Glyph_HoriBearingX(face) \
-  static_cast<FT_Face>(face)->glyph->metrics.horiBearingX
-#define FXFT_Get_Glyph_HoriBearingY(face) \
-  static_cast<FT_Face>(face)->glyph->metrics.horiBearingY
-#define FXFT_Get_Glyph_Width(face) \
-  static_cast<FT_Face>(face)->glyph->metrics.width
-#define FXFT_Get_Glyph_Height(face) \
-  static_cast<FT_Face>(face)->glyph->metrics.height
-#define FXFT_Get_Face_CharmapCount(face) \
-  static_cast<FT_Face>(face)->num_charmaps
-#define FXFT_Get_Charmap_Encoding(charmap) \
-  static_cast<FT_CharMap>(charmap)->encoding
-#define FXFT_Get_Face_Charmap(face) static_cast<FT_Face>(face)->charmap
-#define FXFT_Get_Charmap_PlatformID(charmap) \
-  static_cast<FT_CharMap>(charmap)->platform_id
-#define FXFT_Get_Charmap_EncodingID(charmap) \
-  static_cast<FT_CharMap>(charmap)->encoding_id
-#define FXFT_Get_Face_UnitsPerEM(face) static_cast<FT_Face>(face)->units_per_EM
-#define FXFT_Get_Face_xMin(face) static_cast<FT_Face>(face)->bbox.xMin
-#define FXFT_Get_Face_xMax(face) static_cast<FT_Face>(face)->bbox.xMax
-#define FXFT_Get_Face_yMin(face) static_cast<FT_Face>(face)->bbox.yMin
-#define FXFT_Get_Face_yMax(face) static_cast<FT_Face>(face)->bbox.yMax
-#define FXFT_Get_Face_Height(face) static_cast<FT_Face>(face)->height
-#define FXFT_Get_Face_Ascender(face) static_cast<FT_Face>(face)->ascender
-#define FXFT_Get_Face_Descender(face) static_cast<FT_Face>(face)->descender
-#define FXFT_Get_Glyph_HoriAdvance(face) \
-  static_cast<FT_Face>(face)->glyph->metrics.horiAdvance
-#define FXFT_Get_MM_Axis(var, index) &static_cast<FT_MM_Var*>(var)->axis[index]
-#define FXFT_Get_MM_Axis_Min(axis) static_cast<FT_Var_Axis*>(axis)->minimum
-#define FXFT_Get_MM_Axis_Max(axis) static_cast<FT_Var_Axis*>(axis)->maximum
-#define FXFT_Get_MM_Axis_Def(axis) static_cast<FT_Var_Axis*>(axis)->def
-#define FXFT_Free(face, p)                                                     \
-  static_cast<FT_Face>(face)->memory->free(static_cast<FT_Face>(face)->memory, \
-                                           p)
-#define FXFT_Get_Glyph_Outline(face) &static_cast<FT_Face>(face)->glyph->outline
-#define FXFT_Render_Glyph(face, mode)                \
-  FT_Render_Glyph(static_cast<FT_Face>(face)->glyph, \
-                  static_cast<enum FT_Render_Mode_>(mode))
-#define FXFT_Get_MM_Var(face, p) FT_Get_MM_Var(static_cast<FT_Face>(face), p)
-#define FXFT_Set_MM_Design_Coordinates(face, n, p) \
-  FT_Set_MM_Design_Coordinates(static_cast<FT_Face>(face), n, p)
-#define FXFT_Set_Pixel_Sizes(face, w, h) \
-  FT_Set_Pixel_Sizes(static_cast<FT_Face>(face), w, h)
-#define FXFT_Set_Transform(face, m, d) \
-  FT_Set_Transform(static_cast<FT_Face>(face), m, d)
-#define FXFT_Outline_Embolden(outline, s) FT_Outline_Embolden(outline, s)
-#define FXFT_Get_Glyph_Bitmap(face) &static_cast<FT_Face>(face)->glyph->bitmap
-#define FXFT_Get_Bitmap_Width(bitmap) static_cast<FT_Bitmap*>(bitmap)->width
-#define FXFT_Get_Bitmap_Rows(bitmap) static_cast<FT_Bitmap*>(bitmap)->rows
-#define FXFT_Get_Bitmap_PixelMode(bitmap) \
-  static_cast<FT_Bitmap*>(bitmap)->pixel_mode
-#define FXFT_Get_Bitmap_Pitch(bitmap) static_cast<FT_Bitmap*>(bitmap)->pitch
-#define FXFT_Get_Bitmap_Buffer(bitmap) static_cast<FT_Bitmap*>(bitmap)->buffer
-#define FXFT_Get_Glyph_BitmapLeft(face) \
-  static_cast<FT_Face>(face)->glyph->bitmap_left
-#define FXFT_Get_Glyph_BitmapTop(face) \
-  static_cast<FT_Face>(face)->glyph->bitmap_top
-#define FXFT_Outline_Decompose(outline, funcs, params) \
-  FT_Outline_Decompose(outline, funcs, params)
-#define FXFT_Set_Char_Size(face, char_width, char_height, horz_resolution, \
-                           vert_resolution)                                \
-  FT_Set_Char_Size(face, char_width, char_height, horz_resolution,         \
-                   vert_resolution)
-#define FXFT_Get_Glyph(slot, aglyph) FT_Get_Glyph(slot, aglyph)
-#define FXFT_Glyph_Get_CBox(glyph, bbox_mode, acbox) \
-  FT_Glyph_Get_CBox(glyph, bbox_mode, acbox)
-#define FXFT_Done_Glyph(glyph) FT_Done_Glyph(glyph)
-#define FXFT_Library_SetLcdFilter(library, filter) \
-  FT_Library_SetLcdFilter(static_cast<FT_Library>(library), filter)
+  (((face)->face_flags) & FT_FACE_FLAG_FIXED_WIDTH)
+#define FXFT_Get_Face_Stream_Base(face) (face)->stream->base
+#define FXFT_Get_Face_Stream_Size(face) (face)->stream->size
+#define FXFT_Get_Face_Family_Name(face) (face)->family_name
+#define FXFT_Get_Face_Style_Name(face) (face)->style_name
+#define FXFT_Is_Face_Italic(face) (((face)->style_flags) & FT_STYLE_FLAG_ITALIC)
+#define FXFT_Is_Face_Bold(face) (((face)->style_flags) & FT_STYLE_FLAG_BOLD)
+#define FXFT_Get_Face_Charmaps(face) (face)->charmaps
+#define FXFT_Get_Glyph_HoriBearingX(face) (face)->glyph->metrics.horiBearingX
+#define FXFT_Get_Glyph_HoriBearingY(face) (face)->glyph->metrics.horiBearingY
+#define FXFT_Get_Glyph_Width(face) (face)->glyph->metrics.width
+#define FXFT_Get_Glyph_Height(face) (face)->glyph->metrics.height
+#define FXFT_Get_Face_CharmapCount(face) (face)->num_charmaps
+#define FXFT_Get_Charmap_Encoding(charmap) (charmap)->encoding
+#define FXFT_Get_Face_Charmap(face) (face)->charmap
+#define FXFT_Get_Charmap_PlatformID(charmap) (charmap)->platform_id
+#define FXFT_Get_Charmap_EncodingID(charmap) (charmap)->encoding_id
+#define FXFT_Get_Face_UnitsPerEM(face) (face)->units_per_EM
+#define FXFT_Get_Face_xMin(face) (face)->bbox.xMin
+#define FXFT_Get_Face_xMax(face) (face)->bbox.xMax
+#define FXFT_Get_Face_yMin(face) (face)->bbox.yMin
+#define FXFT_Get_Face_yMax(face) (face)->bbox.yMax
+#define FXFT_Get_Face_Height(face) (face)->height
+#define FXFT_Get_Face_Ascender(face) (face)->ascender
+#define FXFT_Get_Face_Descender(face) (face)->descender
+#define FXFT_Get_Glyph_HoriAdvance(face) (face)->glyph->metrics.horiAdvance
+#define FXFT_Get_MM_Axis(var, index) (var)->axis[index]
+#define FXFT_Get_MM_Axis_Min(axis) (axis).minimum
+#define FXFT_Get_MM_Axis_Max(axis) (axis).maximum
+#define FXFT_Get_MM_Axis_Def(axis) (axis).def
+#define FXFT_Free(face, p) (face)->memory->free((face)->memory, p)
+#define FXFT_Get_Glyph_Outline(face) &((face)->glyph->outline)
+#define FXFT_Get_Glyph_Bitmap(face) (face)->glyph->bitmap
+#define FXFT_Get_Bitmap_Width(bitmap) (bitmap).width
+#define FXFT_Get_Bitmap_Rows(bitmap) (bitmap).rows
+#define FXFT_Get_Bitmap_PixelMode(bitmap) (bitmap).pixel_mode
+#define FXFT_Get_Bitmap_Pitch(bitmap) (bitmap).pitch
+#define FXFT_Get_Bitmap_Buffer(bitmap) (bitmap).buffer
+#define FXFT_Get_Glyph_BitmapLeft(face) (face)->glyph->bitmap_left
+#define FXFT_Get_Glyph_BitmapTop(face) (face)->glyph->bitmap_top
 
 int FXFT_unicode_from_adobe_name(const char* glyph_name);
 void FXFT_adobe_name_from_unicode(char* name, wchar_t unicode);
diff --git a/core/fxge/fx_ge_fontmap.cpp b/core/fxge/fx_ge_fontmap.cpp
index 6e3d8ab..c99d08d 100644
--- a/core/fxge/fx_ge_fontmap.cpp
+++ b/core/fxge/fx_ge_fontmap.cpp
@@ -6,74 +6,15 @@
 
 #include <memory>
 
-#include "core/fxge/cfx_fontmapper.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "build/build_config.h"
+#include "core/fxge/systemfontinfo_iface.h"
 
-static ByteString GetStringFromTable(const uint8_t* string_ptr,
-                                     uint32_t string_ptr_length,
-                                     uint16_t offset,
-                                     uint16_t length) {
-  if (string_ptr_length < static_cast<uint32_t>(offset + length)) {
-    return ByteString();
-  }
-  return ByteString(string_ptr + offset, length);
-}
-
-ByteString GetNameFromTT(const uint8_t* name_table,
-                         uint32_t name_table_size,
-                         uint32_t name_id) {
-  if (!name_table || name_table_size < 6) {
-    return ByteString();
-  }
-  uint32_t name_count = GET_TT_SHORT(name_table + 2);
-  uint32_t string_offset = GET_TT_SHORT(name_table + 4);
-  // We will ignore the possibility of overlap of structures and
-  // string table as if it's all corrupt there's not a lot we can do.
-  if (name_table_size < string_offset) {
-    return ByteString();
-  }
-
-  const uint8_t* string_ptr = name_table + string_offset;
-  uint32_t string_ptr_size = name_table_size - string_offset;
-  name_table += 6;
-  name_table_size -= 6;
-  if (name_table_size < name_count * 12) {
-    return ByteString();
-  }
-
-  for (uint32_t i = 0; i < name_count; i++, name_table += 12) {
-    if (GET_TT_SHORT(name_table + 6) == name_id &&
-        GET_TT_SHORT(name_table) == 1 && GET_TT_SHORT(name_table + 2) == 0) {
-      return GetStringFromTable(string_ptr, string_ptr_size,
-                                GET_TT_SHORT(name_table + 10),
-                                GET_TT_SHORT(name_table + 8));
-    }
-  }
-  return ByteString();
-}
-#ifdef PDF_ENABLE_XFA
-void* IFX_SystemFontInfo::MapFontByUnicode(uint32_t dwUnicode,
-                                           int weight,
-                                           bool bItalic,
-                                           int pitch_family) {
-  return nullptr;
-}
-#endif  // PDF_ENABLE_XFA
-
-int IFX_SystemFontInfo::GetFaceIndex(void* hFont) {
+int SystemFontInfoIface::GetFaceIndex(void* hFont) {
   return 0;
 }
 
-extern "C" {
-unsigned long _FTStreamRead(FXFT_Stream stream,
-                            unsigned long offset,
-                            unsigned char* buffer,
-                            unsigned long count);
-void _FTStreamClose(FXFT_Stream stream);
-};
-
-#if _FX_OS_ == _FX_OS_ANDROID_
-std::unique_ptr<IFX_SystemFontInfo> IFX_SystemFontInfo::CreateDefault(
+#if defined(OS_ANDROID)
+std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
     const char** pUnused) {
   return nullptr;
 }
diff --git a/core/fxge/fx_ge_linux.cpp b/core/fxge/fx_ge_linux.cpp
index d5069f3..d76d39d 100644
--- a/core/fxge/fx_ge_linux.cpp
+++ b/core/fxge/fx_ge_linux.cpp
@@ -11,71 +11,80 @@
 #include "core/fxge/cfx_folderfontinfo.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/systemfontinfo_iface.h"
 #include "third_party/base/ptr_util.h"
 
 #if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
 namespace {
 
-const size_t kLinuxGpNameSize = 6;
+enum JpFontFamily : uint8_t {
+  kJpFontPGothic,
+  kJpFontGothic,
+  kJpFontPMincho,
+  kJpFontMincho,
+  kCount
+};
 
-const char* const g_LinuxGpFontList[][kLinuxGpNameSize] = {
-    {"TakaoPGothic", "VL PGothic", "IPAPGothic", "VL Gothic", "Kochi Gothic",
-     "VL Gothic regular"},
-    {"TakaoGothic", "VL Gothic", "IPAGothic", "Kochi Gothic", nullptr,
-     "VL Gothic regular"},
-    {"TakaoPMincho", "IPAPMincho", "VL Gothic", "Kochi Mincho", nullptr,
-     "VL Gothic regular"},
-    {"TakaoMincho", "IPAMincho", "VL Gothic", "Kochi Mincho", nullptr,
-     "VL Gothic regular"},
+const char* const g_LinuxJpFontList[][JpFontFamily::kCount] = {
+    {"TakaoPGothic", "VL PGothic", "IPAPGothic", "VL Gothic"},
+    {"TakaoGothic", "VL Gothic", "IPAGothic", "Kochi Gothic"},
+    {"TakaoPMincho", "IPAPMincho", "VL Gothic", "Kochi Mincho"},
+    {"TakaoMincho", "IPAMincho", "VL Gothic", "Kochi Mincho"},
 };
 
 const char* const g_LinuxGbFontList[] = {
-    "AR PL UMing CN Light", "WenQuanYi Micro Hei", "AR PL UKai CN",
+    "AR PL UMing CN Light",
+    "WenQuanYi Micro Hei",
+    "AR PL UKai CN",
 };
 
 const char* const g_LinuxB5FontList[] = {
-    "AR PL UMing TW Light", "WenQuanYi Micro Hei", "AR PL UKai TW",
+    "AR PL UMing TW Light",
+    "WenQuanYi Micro Hei",
+    "AR PL UKai TW",
 };
 
 const char* const g_LinuxHGFontList[] = {
     "UnDotum",
 };
 
-size_t GetJapanesePreference(const char* facearr,
-                             int weight,
-                             int pitch_family) {
+uint8_t GetJapanesePreference(const char* facearr,
+                              int weight,
+                              int pitch_family) {
   ByteString face = facearr;
   if (face.Contains("Gothic") ||
       face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) {
     if (face.Contains("PGothic") ||
         face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) {
-      return 0;
+      return kJpFontPGothic;
     }
-    return 1;
+    return kJpFontGothic;
   }
   if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) {
     if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) {
-      return 2;
+      return kJpFontPMincho;
     }
-    return 3;
+    return kJpFontMincho;
   }
   if (!FontFamilyIsRoman(pitch_family) && weight > 400)
-    return 0;
+    return kJpFontPGothic;
 
-  return 2;
+  return kJpFontPMincho;
 }
 
-class CFX_LinuxFontInfo : public CFX_FolderFontInfo {
+class CFX_LinuxFontInfo final : public CFX_FolderFontInfo {
  public:
-  CFX_LinuxFontInfo() {}
-  ~CFX_LinuxFontInfo() override {}
+  CFX_LinuxFontInfo() = default;
+  ~CFX_LinuxFontInfo() override = default;
 
+  // CFX_LinuxFontInfo:
   void* MapFont(int weight,
                 bool bItalic,
                 int charset,
                 int pitch_family,
                 const char* family) override;
+
   bool ParseFontCfg(const char** pUserPaths);
 };
 
@@ -83,18 +92,18 @@
                                  bool bItalic,
                                  int charset,
                                  int pitch_family,
-                                 const char* cstr_face) {
-  void* font = GetSubstFont(cstr_face);
+                                 const char* family) {
+  void* font = GetSubstFont(family);
   if (font)
     return font;
 
   bool bCJK = true;
   switch (charset) {
     case FX_CHARSET_ShiftJIS: {
-      size_t index = GetJapanesePreference(cstr_face, weight, pitch_family);
-      ASSERT(index < FX_ArraySize(g_LinuxGpFontList));
-      for (size_t i = 0; i < kLinuxGpNameSize; i++) {
-        auto it = m_FontList.find(g_LinuxGpFontList[index][i]);
+      uint8_t index = GetJapanesePreference(family, weight, pitch_family);
+      ASSERT(index < FX_ArraySize(g_LinuxJpFontList));
+      for (const char* name : g_LinuxJpFontList[index]) {
+        auto it = m_FontList.find(name);
         if (it != m_FontList.end())
           return it->second.get();
       }
@@ -128,7 +137,7 @@
       bCJK = false;
       break;
   }
-  return FindFont(weight, bItalic, charset, pitch_family, cstr_face, !bCJK);
+  return FindFont(weight, bItalic, charset, pitch_family, family, !bCJK);
 }
 
 bool CFX_LinuxFontInfo::ParseFontCfg(const char** pUserPaths) {
@@ -142,7 +151,7 @@
 
 }  // namespace
 
-std::unique_ptr<IFX_SystemFontInfo> IFX_SystemFontInfo::CreateDefault(
+std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
     const char** pUserPaths) {
   auto pInfo = pdfium::MakeUnique<CFX_LinuxFontInfo>();
   if (!pInfo->ParseFontCfg(pUserPaths)) {
@@ -154,10 +163,21 @@
   return std::move(pInfo);
 }
 
-void CFX_GEModule::InitPlatform() {
-  m_pFontMgr->SetSystemFontInfo(
-      IFX_SystemFontInfo::CreateDefault(m_pUserFontPaths));
-}
+class CLinuxPlatform : public CFX_GEModule::PlatformIface {
+ public:
+  CLinuxPlatform() = default;
+  ~CLinuxPlatform() override = default;
 
-void CFX_GEModule::DestroyPlatform() {}
+  void Init() override {
+    CFX_GEModule* pModule = CFX_GEModule::Get();
+    pModule->GetFontMgr()->SetSystemFontInfo(
+        SystemFontInfoIface::CreateDefault(pModule->GetUserFontPaths()));
+  }
+};
+
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return pdfium::MakeUnique<CLinuxPlatform>();
+}
 #endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
diff --git a/core/fxge/fx_ge_text.cpp b/core/fxge/fx_ge_text.cpp
deleted file mode 100644
index 147e712..0000000
--- a/core/fxge/fx_ge_text.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <algorithm>
-#include <limits>
-#include <vector>
-
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/fx_font.h"
-#include "core/fxge/fx_freetype.h"
-#include "core/fxge/ifx_renderdevicedriver.h"
-
-namespace {
-
-void ResetTransform(FT_Face face) {
-  FXFT_Matrix matrix;
-  matrix.xx = 0x10000L;
-  matrix.xy = 0;
-  matrix.yx = 0;
-  matrix.yy = 0x10000L;
-  FXFT_Set_Transform(face, &matrix, 0);
-}
-
-}  // namespace
-
-FXTEXT_GLYPHPOS::FXTEXT_GLYPHPOS() : m_pGlyph(nullptr) {}
-
-FXTEXT_GLYPHPOS::FXTEXT_GLYPHPOS(const FXTEXT_GLYPHPOS&) = default;
-
-FXTEXT_GLYPHPOS::~FXTEXT_GLYPHPOS(){};
-
-ScopedFontTransform::ScopedFontTransform(FT_Face face, FXFT_Matrix* matrix)
-    : m_Face(face) {
-  FXFT_Set_Transform(m_Face, matrix, 0);
-}
-
-ScopedFontTransform::~ScopedFontTransform() {
-  ResetTransform(m_Face);
-}
-
-FX_RECT FXGE_GetGlyphsBBox(const std::vector<FXTEXT_GLYPHPOS>& glyphs,
-                           int anti_alias,
-                           float retinaScaleX,
-                           float retinaScaleY) {
-  FX_RECT rect(0, 0, 0, 0);
-  bool bStarted = false;
-  for (const FXTEXT_GLYPHPOS& glyph : glyphs) {
-    const CFX_GlyphBitmap* pGlyph = glyph.m_pGlyph;
-    if (!pGlyph)
-      continue;
-
-    FX_SAFE_INT32 char_left = glyph.m_Origin.x;
-    char_left += pGlyph->m_Left;
-    if (!char_left.IsValid())
-      continue;
-
-    FX_SAFE_INT32 char_width = pGlyph->m_pBitmap->GetWidth();
-    char_width /= retinaScaleX;
-    if (anti_alias == FXFT_RENDER_MODE_LCD)
-      char_width /= 3;
-    if (!char_width.IsValid())
-      continue;
-
-    FX_SAFE_INT32 char_right = char_left + char_width;
-    if (!char_right.IsValid())
-      continue;
-
-    FX_SAFE_INT32 char_top = glyph.m_Origin.y;
-    char_top -= pGlyph->m_Top;
-    if (!char_top.IsValid())
-      continue;
-
-    FX_SAFE_INT32 char_height = pGlyph->m_pBitmap->GetHeight();
-    char_height /= retinaScaleY;
-    if (!char_height.IsValid())
-      continue;
-
-    FX_SAFE_INT32 char_bottom = char_top + char_height;
-    if (!char_bottom.IsValid())
-      continue;
-
-    if (bStarted) {
-      rect.left = pdfium::base::ValueOrDieForType<int32_t>(
-          pdfium::base::CheckMin(rect.left, char_left));
-      rect.right = pdfium::base::ValueOrDieForType<int32_t>(
-          pdfium::base::CheckMax(rect.right, char_right));
-      rect.top = pdfium::base::ValueOrDieForType<int32_t>(
-          pdfium::base::CheckMin(rect.top, char_top));
-      rect.bottom = pdfium::base::ValueOrDieForType<int32_t>(
-          pdfium::base::CheckMax(rect.bottom, char_bottom));
-      continue;
-    }
-
-    rect.left = char_left.ValueOrDie();
-    rect.right = char_right.ValueOrDie();
-    rect.top = char_top.ValueOrDie();
-    rect.bottom = char_bottom.ValueOrDie();
-    bStarted = true;
-  }
-  return rect;
-}
diff --git a/core/fxge/fx_ge_text_embeddertest.cpp b/core/fxge/fx_ge_text_embeddertest.cpp
index 045b6dc..e47ed5f 100644
--- a/core/fxge/fx_ge_text_embeddertest.cpp
+++ b/core/fxge/fx_ge_text_embeddertest.cpp
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+
+#include "public/cpp/fpdf_scopers.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -11,8 +14,9 @@
   // Shouldn't crash.
   EXPECT_TRUE(OpenDocument("bug_601362.pdf"));
   FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  FPDF_BITMAP bitmap = RenderPage(page);
-  FPDFBitmap_Destroy(bitmap);
+  ASSERT_TRUE(page);
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  EXPECT_EQ(612, FPDFBitmap_GetWidth(bitmap.get()));
+  EXPECT_EQ(792, FPDFBitmap_GetHeight(bitmap.get()));
   UnloadPage(page);
 }
diff --git a/core/fxge/ifx_renderdevicedriver.cpp b/core/fxge/ifx_renderdevicedriver.cpp
deleted file mode 100644
index eae773d..0000000
--- a/core/fxge/ifx_renderdevicedriver.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/ifx_renderdevicedriver.h"
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-
-IFX_RenderDeviceDriver::~IFX_RenderDeviceDriver() {}
-
-CFX_Matrix IFX_RenderDeviceDriver::GetCTM() const {
-  return CFX_Matrix();
-}
-
-bool IFX_RenderDeviceDriver::StartRendering() {
-  return true;
-}
-
-void IFX_RenderDeviceDriver::EndRendering() {}
-
-bool IFX_RenderDeviceDriver::SetClip_PathStroke(
-    const CFX_PathData* pPathData,
-    const CFX_Matrix* pObject2Device,
-    const CFX_GraphStateData* pGraphState) {
-  return false;
-}
-
-bool IFX_RenderDeviceDriver::SetPixel(int x, int y, uint32_t color) {
-  return false;
-}
-
-bool IFX_RenderDeviceDriver::FillRectWithBlend(const FX_RECT* pRect,
-                                               uint32_t fill_color,
-                                               int blend_type) {
-  return false;
-}
-
-bool IFX_RenderDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
-                                              const CFX_PointF& ptLineTo,
-                                              uint32_t color,
-                                              int blend_type) {
-  return false;
-}
-
-bool IFX_RenderDeviceDriver::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                       int left,
-                                       int top) {
-  return false;
-}
-
-RetainPtr<CFX_DIBitmap> IFX_RenderDeviceDriver::GetBackDrop() {
-  return RetainPtr<CFX_DIBitmap>();
-}
-
-bool IFX_RenderDeviceDriver::ContinueDIBits(CFX_ImageRenderer* handle,
-                                            IFX_PauseIndicator* pPause) {
-  return false;
-}
-
-bool IFX_RenderDeviceDriver::DrawDeviceText(int nChars,
-                                            const FXTEXT_CHARPOS* pCharPos,
-                                            CFX_Font* pFont,
-                                            const CFX_Matrix* pObject2Device,
-                                            float font_size,
-                                            uint32_t color) {
-  return false;
-}
-
-int IFX_RenderDeviceDriver::GetDriverType() const {
-  return 0;
-}
-
-void IFX_RenderDeviceDriver::ClearDriver() {}
-
-bool IFX_RenderDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern,
-                                         const CFX_Matrix* pMatrix,
-                                         const FX_RECT& clip_rect,
-                                         int alpha,
-                                         bool bAlphaMode) {
-  return false;
-}
-
-bool IFX_RenderDeviceDriver::SetBitsWithMask(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
-    const RetainPtr<CFX_DIBSource>& pMask,
-    int left,
-    int top,
-    int bitmap_alpha,
-    int blend_type) {
-  return false;
-}
-
-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
-void IFX_RenderDeviceDriver::Flush() {}
-#endif
diff --git a/core/fxge/ifx_renderdevicedriver.h b/core/fxge/ifx_renderdevicedriver.h
deleted file mode 100644
index e9498bd..0000000
--- a/core/fxge/ifx_renderdevicedriver.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_IFX_RENDERDEVICEDRIVER_H_
-#define CORE_FXGE_IFX_RENDERDEVICEDRIVER_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CFX_DIBitmap;
-class CFX_DIBSource;
-class CFX_Font;
-class CFX_GraphStateData;
-class CFX_ImageRenderer;
-class CFX_Matrix;
-class CFX_PathData;
-class CPDF_ShadingPattern;
-class FXTEXT_CHARPOS;
-class IFX_PauseIndicator;
-struct FX_RECT;
-
-class IFX_RenderDeviceDriver {
- public:
-  virtual ~IFX_RenderDeviceDriver();
-
-  virtual int GetDeviceCaps(int caps_id) const = 0;
-  virtual CFX_Matrix GetCTM() const;
-
-  virtual bool StartRendering();
-  virtual void EndRendering();
-  virtual void SaveState() = 0;
-  virtual void RestoreState(bool bKeepSaved) = 0;
-
-  virtual bool SetClip_PathFill(const CFX_PathData* pPathData,
-                                const CFX_Matrix* pObject2Device,
-                                int fill_mode) = 0;
-  virtual bool SetClip_PathStroke(const CFX_PathData* pPathData,
-                                  const CFX_Matrix* pObject2Device,
-                                  const CFX_GraphStateData* pGraphState);
-  virtual bool DrawPath(const CFX_PathData* pPathData,
-                        const CFX_Matrix* pObject2Device,
-                        const CFX_GraphStateData* pGraphState,
-                        uint32_t fill_color,
-                        uint32_t stroke_color,
-                        int fill_mode,
-                        int blend_type) = 0;
-  virtual bool SetPixel(int x, int y, uint32_t color);
-  virtual bool FillRectWithBlend(const FX_RECT* pRect,
-                                 uint32_t fill_color,
-                                 int blend_type);
-  virtual bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
-                                const CFX_PointF& ptLineTo,
-                                uint32_t color,
-                                int blend_type);
-
-  virtual bool GetClipBox(FX_RECT* pRect) = 0;
-  virtual bool GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                         int left,
-                         int top);
-  virtual RetainPtr<CFX_DIBitmap> GetBackDrop();
-  virtual bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
-                         uint32_t color,
-                         const FX_RECT* pSrcRect,
-                         int dest_left,
-                         int dest_top,
-                         int blend_type) = 0;
-  virtual bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
-                             uint32_t color,
-                             int dest_left,
-                             int dest_top,
-                             int dest_width,
-                             int dest_height,
-                             const FX_RECT* pClipRect,
-                             uint32_t flags,
-                             int blend_type) = 0;
-  virtual bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
-                           int bitmap_alpha,
-                           uint32_t color,
-                           const CFX_Matrix* pMatrix,
-                           uint32_t flags,
-                           std::unique_ptr<CFX_ImageRenderer>* handle,
-                           int blend_type) = 0;
-  virtual bool ContinueDIBits(CFX_ImageRenderer* handle,
-                              IFX_PauseIndicator* pPause);
-  virtual bool DrawDeviceText(int nChars,
-                              const FXTEXT_CHARPOS* pCharPos,
-                              CFX_Font* pFont,
-                              const CFX_Matrix* pObject2Device,
-                              float font_size,
-                              uint32_t color);
-  virtual int GetDriverType() const;
-  virtual void ClearDriver();
-  virtual bool DrawShading(const CPDF_ShadingPattern* pPattern,
-                           const CFX_Matrix* pMatrix,
-                           const FX_RECT& clip_rect,
-                           int alpha,
-                           bool bAlphaMode);
-  virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBSource>& pBitmap,
-                               const RetainPtr<CFX_DIBSource>& pMask,
-                               int left,
-                               int top,
-                               int bitmap_alpha,
-                               int blend_type);
-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
-  virtual void Flush();
-#endif
-};
-
-#endif  // CORE_FXGE_IFX_RENDERDEVICEDRIVER_H_
diff --git a/core/fxge/ifx_systemfontinfo.h b/core/fxge/ifx_systemfontinfo.h
deleted file mode 100644
index 1ae097b..0000000
--- a/core/fxge/ifx_systemfontinfo.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_IFX_SYSTEMFONTINFO_H_
-#define CORE_FXGE_IFX_SYSTEMFONTINFO_H_
-
-#include <memory>
-
-#include "core/fxge/cfx_fontmapper.h"
-
-const uint32_t kTableNAME = FXDWORD_GET_MSBFIRST("name");
-const uint32_t kTableTTCF = FXDWORD_GET_MSBFIRST("ttcf");
-
-class IFX_SystemFontInfo {
- public:
-  static std::unique_ptr<IFX_SystemFontInfo> CreateDefault(
-      const char** pUserPaths);
-
-  virtual ~IFX_SystemFontInfo() = default;
-
-  virtual bool EnumFontList(CFX_FontMapper* pMapper) = 0;
-  virtual void* MapFont(int weight,
-                        bool bItalic,
-                        int charset,
-                        int pitch_family,
-                        const char* face) = 0;
-
-#ifdef PDF_ENABLE_XFA
-  virtual void* MapFontByUnicode(uint32_t dwUnicode,
-                                 int weight,
-                                 bool bItalic,
-                                 int pitch_family);
-#endif  // PDF_ENABLE_XFA
-
-  virtual void* GetFont(const char* face) = 0;
-  virtual uint32_t GetFontData(void* hFont,
-                               uint32_t table,
-                               uint8_t* buffer,
-                               uint32_t size) = 0;
-  virtual bool GetFaceName(void* hFont, ByteString* name) = 0;
-  virtual bool GetFontCharset(void* hFont, int* charset) = 0;
-  virtual int GetFaceIndex(void* hFont);
-  virtual void DeleteFont(void* hFont) = 0;
-};
-
-#endif  // CORE_FXGE_IFX_SYSTEMFONTINFO_H_
diff --git a/core/fxge/render_defines.h b/core/fxge/render_defines.h
new file mode 100644
index 0000000..ff52b4e
--- /dev/null
+++ b/core/fxge/render_defines.h
@@ -0,0 +1,49 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_RENDER_DEFINES_H_
+#define CORE_FXGE_RENDER_DEFINES_H_
+
+#define FXDC_PIXEL_WIDTH 2
+#define FXDC_PIXEL_HEIGHT 3
+#define FXDC_BITS_PIXEL 4
+#define FXDC_HORZ_SIZE 5
+#define FXDC_VERT_SIZE 6
+#define FXDC_RENDER_CAPS 7
+
+#define FXRC_GET_BITS 0x01
+#define FXRC_BIT_MASK 0x02
+#define FXRC_ALPHA_PATH 0x10
+#define FXRC_ALPHA_IMAGE 0x20
+#define FXRC_ALPHA_OUTPUT 0x40
+#define FXRC_BLEND_MODE 0x80
+#define FXRC_SOFT_CLIP 0x100
+#define FXRC_CMYK_OUTPUT 0x200
+#define FXRC_BITMASK_OUTPUT 0x400
+#define FXRC_BYTEMASK_OUTPUT 0x800
+#define FXRENDER_IMAGE_LOSSY 0x1000
+#define FXRC_FILLSTROKE_PATH 0x2000
+#define FXRC_SHADING 0x4000
+
+#define FXFILL_ALTERNATE 1
+#define FXFILL_WINDING 2
+#define FXFILL_FULLCOVER 4
+#define FXFILL_RECT_AA 8
+#define FX_FILL_STROKE 16
+#define FX_STROKE_ADJUST 32
+#define FX_STROKE_TEXT_MODE 64
+#define FX_FILL_TEXT_MODE 128
+#define FX_ZEROAREA_FILL 256
+#define FXFILL_NOPATHSMOOTH 512
+
+#define FXTEXT_CLEARTYPE 0x01
+#define FXTEXT_BGR_STRIPE 0x02
+#define FXTEXT_PRINTGRAPHICTEXT 0x04
+#define FXTEXT_NO_NATIVETEXT 0x08
+#define FXTEXT_PRINTIMAGETEXT 0x10
+#define FXTEXT_NOSMOOTH 0x20
+
+#endif  // CORE_FXGE_RENDER_DEFINES_H_
diff --git a/core/fxge/renderdevicedriver_iface.cpp b/core/fxge/renderdevicedriver_iface.cpp
new file mode 100644
index 0000000..f8fcccf
--- /dev/null
+++ b/core/fxge/renderdevicedriver_iface.cpp
@@ -0,0 +1,97 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/renderdevicedriver_iface.h"
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+RenderDeviceDriverIface::~RenderDeviceDriverIface() {}
+
+bool RenderDeviceDriverIface::StartRendering() {
+  return true;
+}
+
+void RenderDeviceDriverIface::EndRendering() {}
+
+bool RenderDeviceDriverIface::SetClip_PathStroke(
+    const CFX_PathData* pPathData,
+    const CFX_Matrix* pObject2Device,
+    const CFX_GraphStateData* pGraphState) {
+  return false;
+}
+
+void RenderDeviceDriverIface::SetBaseClip(const FX_RECT& rect) {}
+
+bool RenderDeviceDriverIface::SetPixel(int x, int y, uint32_t color) {
+  return false;
+}
+
+bool RenderDeviceDriverIface::FillRectWithBlend(const FX_RECT& rect,
+                                                uint32_t fill_color,
+                                                BlendMode blend_type) {
+  return false;
+}
+
+bool RenderDeviceDriverIface::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
+                                               const CFX_PointF& ptLineTo,
+                                               uint32_t color,
+                                               BlendMode blend_type) {
+  return false;
+}
+
+bool RenderDeviceDriverIface::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                                        int left,
+                                        int top) {
+  return false;
+}
+
+RetainPtr<CFX_DIBitmap> RenderDeviceDriverIface::GetBackDrop() {
+  return RetainPtr<CFX_DIBitmap>();
+}
+
+bool RenderDeviceDriverIface::ContinueDIBits(CFX_ImageRenderer* handle,
+                                             PauseIndicatorIface* pPause) {
+  return false;
+}
+
+bool RenderDeviceDriverIface::DrawDeviceText(int nChars,
+                                             const TextCharPos* pCharPos,
+                                             CFX_Font* pFont,
+                                             const CFX_Matrix& mtObject2Device,
+                                             float font_size,
+                                             uint32_t color) {
+  return false;
+}
+
+int RenderDeviceDriverIface::GetDriverType() const {
+  return 0;
+}
+
+void RenderDeviceDriverIface::ClearDriver() {}
+
+bool RenderDeviceDriverIface::DrawShading(const CPDF_ShadingPattern* pPattern,
+                                          const CFX_Matrix* pMatrix,
+                                          const FX_RECT& clip_rect,
+                                          int alpha,
+                                          bool bAlphaMode) {
+  return false;
+}
+
+bool RenderDeviceDriverIface::SetBitsWithMask(
+    const RetainPtr<CFX_DIBBase>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pMask,
+    int left,
+    int top,
+    int bitmap_alpha,
+    BlendMode blend_type) {
+  return false;
+}
+
+#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
+void RenderDeviceDriverIface::Flush() {}
+#endif
diff --git a/core/fxge/renderdevicedriver_iface.h b/core/fxge/renderdevicedriver_iface.h
new file mode 100644
index 0000000..69e404b
--- /dev/null
+++ b/core/fxge/renderdevicedriver_iface.h
@@ -0,0 +1,123 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_
+#define CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/fx_dib.h"
+
+class CFX_DIBBase;
+class CFX_DIBitmap;
+class CFX_Font;
+class CFX_GraphStateData;
+class CFX_ImageRenderer;
+class CFX_Matrix;
+class CFX_PathData;
+class CPDF_ShadingPattern;
+class PauseIndicatorIface;
+class TextCharPos;
+struct FX_RECT;
+
+enum class DeviceType : uint8_t {
+  kUnknown,
+  kDisplay,
+  kPrinter,
+};
+
+class RenderDeviceDriverIface {
+ public:
+  virtual ~RenderDeviceDriverIface();
+
+  virtual DeviceType GetDeviceType() const = 0;
+  virtual int GetDeviceCaps(int caps_id) const = 0;
+
+  virtual bool StartRendering();
+  virtual void EndRendering();
+  virtual void SaveState() = 0;
+  virtual void RestoreState(bool bKeepSaved) = 0;
+
+  virtual void SetBaseClip(const FX_RECT& rect);
+  virtual bool SetClip_PathFill(const CFX_PathData* pPathData,
+                                const CFX_Matrix* pObject2Device,
+                                int fill_mode) = 0;
+  virtual bool SetClip_PathStroke(const CFX_PathData* pPathData,
+                                  const CFX_Matrix* pObject2Device,
+                                  const CFX_GraphStateData* pGraphState);
+  virtual bool DrawPath(const CFX_PathData* pPathData,
+                        const CFX_Matrix* pObject2Device,
+                        const CFX_GraphStateData* pGraphState,
+                        uint32_t fill_color,
+                        uint32_t stroke_color,
+                        int fill_mode,
+                        BlendMode blend_type) = 0;
+  virtual bool SetPixel(int x, int y, uint32_t color);
+  virtual bool FillRectWithBlend(const FX_RECT& rect,
+                                 uint32_t fill_color,
+                                 BlendMode blend_type);
+  virtual bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
+                                const CFX_PointF& ptLineTo,
+                                uint32_t color,
+                                BlendMode blend_type);
+
+  virtual bool GetClipBox(FX_RECT* pRect) = 0;
+  virtual bool GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
+                         int left,
+                         int top);
+  virtual RetainPtr<CFX_DIBitmap> GetBackDrop();
+  virtual bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                         uint32_t color,
+                         const FX_RECT& src_rect,
+                         int dest_left,
+                         int dest_top,
+                         BlendMode blend_type) = 0;
+  virtual bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                             uint32_t color,
+                             int dest_left,
+                             int dest_top,
+                             int dest_width,
+                             int dest_height,
+                             const FX_RECT* pClipRect,
+                             const FXDIB_ResampleOptions& options,
+                             BlendMode blend_type) = 0;
+  virtual bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
+                           int bitmap_alpha,
+                           uint32_t color,
+                           const CFX_Matrix& matrix,
+                           const FXDIB_ResampleOptions& options,
+                           std::unique_ptr<CFX_ImageRenderer>* handle,
+                           BlendMode blend_type) = 0;
+  virtual bool ContinueDIBits(CFX_ImageRenderer* handle,
+                              PauseIndicatorIface* pPause);
+  virtual bool DrawDeviceText(int nChars,
+                              const TextCharPos* pCharPos,
+                              CFX_Font* pFont,
+                              const CFX_Matrix& mtObject2Device,
+                              float font_size,
+                              uint32_t color);
+  virtual int GetDriverType() const;
+  virtual void ClearDriver();
+  virtual bool DrawShading(const CPDF_ShadingPattern* pPattern,
+                           const CFX_Matrix* pMatrix,
+                           const FX_RECT& clip_rect,
+                           int alpha,
+                           bool bAlphaMode);
+  virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                               const RetainPtr<CFX_DIBBase>& pMask,
+                               int left,
+                               int top,
+                               int bitmap_alpha,
+                               BlendMode blend_type);
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+  virtual void Flush();
+#endif
+};
+
+#endif  // CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_
diff --git a/core/fxge/scoped_font_transform.cpp b/core/fxge/scoped_font_transform.cpp
new file mode 100644
index 0000000..2bd5396
--- /dev/null
+++ b/core/fxge/scoped_font_transform.cpp
@@ -0,0 +1,32 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/scoped_font_transform.h"
+
+#include <utility>
+
+namespace {
+
+void ResetTransform(FT_Face face) {
+  FT_Matrix matrix;
+  matrix.xx = 0x10000L;
+  matrix.xy = 0;
+  matrix.yx = 0;
+  matrix.yy = 0x10000L;
+  FT_Set_Transform(face, &matrix, 0);
+}
+
+}  // namespace
+
+ScopedFontTransform::ScopedFontTransform(RetainPtr<CFX_Face> face,
+                                         FT_Matrix* matrix)
+    : m_Face(std::move(face)) {
+  FT_Set_Transform(m_Face->GetRec(), matrix, 0);
+}
+
+ScopedFontTransform::~ScopedFontTransform() {
+  ResetTransform(m_Face->GetRec());
+}
diff --git a/core/fxge/scoped_font_transform.h b/core/fxge/scoped_font_transform.h
new file mode 100644
index 0000000..bcbbb50
--- /dev/null
+++ b/core/fxge/scoped_font_transform.h
@@ -0,0 +1,24 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_SCOPED_FONT_TRANSFORM_H_
+#define CORE_FXGE_SCOPED_FONT_TRANSFORM_H_
+
+#include "core/fxge/cfx_face.h"
+#include "core/fxge/fx_freetype.h"
+
+// Sets the given transform on the font, and resets it to the identity when it
+// goes out of scope.
+class ScopedFontTransform {
+ public:
+  ScopedFontTransform(RetainPtr<CFX_Face> face, FT_Matrix* matrix);
+  ~ScopedFontTransform();
+
+ private:
+  RetainPtr<CFX_Face> m_Face;
+};
+
+#endif  // CORE_FXGE_SCOPED_FONT_TRANSFORM_H_
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index ab8efff..f29022f 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "core/fxge/skia/fx_skia_device.h"
+
 #include <algorithm>
 #include <utility>
 #include <vector>
 
+#include "build/build_config.h"
 #include "core/fpdfapi/page/cpdf_expintfunc.h"
 #include "core/fpdfapi/page/cpdf_function.h"
 #include "core/fpdfapi/page/cpdf_meshstream.h"
@@ -14,28 +17,33 @@
 #include "core/fpdfapi/page/cpdf_stitchfunc.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_extension.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/cfx_bitstream.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_bitmapcomposer.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/dib/cfx_imagestretcher.h"
-#include "core/fxge/skia/fx_skia_device.h"
+#include "core/fxge/text_char_pos.h"
 #include "third_party/base/logging.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkClipOp.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkRSXform.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
@@ -47,7 +55,6 @@
 
 #ifdef _SKIA_SUPPORT_
 #include "third_party/skia/include/core/SkColorFilter.h"
-#include "third_party/skia/include/core/SkColorPriv.h"
 #include "third_party/skia/include/core/SkMaskFilter.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
 #endif  // _SKIA_SUPPORT_
@@ -60,17 +67,17 @@
                                 int dest_top,
                                 int width,
                                 int height,
-                                const RetainPtr<CFX_DIBSource>& pSrcBitmap,
+                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
                                 int src_left,
                                 int src_top) {
   if (!pBitmap)
     return;
 
-  pBitmap->GetOverlapRect(dest_left, dest_top, width, height,
-                          pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(),
-                          src_left, src_top, nullptr);
-  if (width == 0 || height == 0)
+  if (!pBitmap->GetOverlapRect(dest_left, dest_top, width, height,
+                               pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(),
+                               src_left, src_top, nullptr)) {
     return;
+  }
 
   int Bpp = pBitmap->GetBPP() / 8;
   FXDIB_Format dest_format = pBitmap->GetFormat();
@@ -80,12 +87,12 @@
   if (dest_format == src_format) {
     for (int row = 0; row < height; row++) {
       uint8_t* dest_scan = buffer + (dest_top + row) * pitch + dest_left * Bpp;
-      uint8_t* src_scan =
-          (uint8_t*)pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
+      const uint8_t* src_scan =
+          pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
       if (Bpp == 4) {
         for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, FXARGB_MAKE(src_scan[3], src_scan[0],
-                                               src_scan[1], src_scan[2]));
+          FXARGB_SETDIB(dest_scan, ArgbEncode(src_scan[3], src_scan[0],
+                                              src_scan[1], src_scan[2]));
           dest_scan += 4;
           src_scan += 4;
         }
@@ -106,8 +113,8 @@
     if (src_format == FXDIB_Rgb32) {
       for (int row = 0; row < height; row++) {
         uint8_t* dest_scan = dest_buf + row * pitch;
-        uint8_t* src_scan =
-            (uint8_t*)pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
+        const uint8_t* src_scan =
+            pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
         for (int col = 0; col < width; col++) {
           *dest_scan++ = src_scan[2];
           *dest_scan++ = src_scan[1];
@@ -125,11 +132,11 @@
     if (src_format == FXDIB_Rgb) {
       for (int row = 0; row < height; row++) {
         uint8_t* dest_scan = (uint8_t*)(dest_buf + row * pitch);
-        uint8_t* src_scan =
-            (uint8_t*)pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
+        const uint8_t* src_scan =
+            pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
         for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, FXARGB_MAKE(0xff, src_scan[0], src_scan[1],
-                                               src_scan[2]));
+          FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[0], src_scan[1],
+                                              src_scan[2]));
           dest_scan += 4;
           src_scan += 3;
         }
@@ -138,11 +145,11 @@
       ASSERT(dest_format == FXDIB_Argb);
       for (int row = 0; row < height; row++) {
         uint8_t* dest_scan = dest_buf + row * pitch;
-        uint8_t* src_scan =
-            (uint8_t*)(pSrcBitmap->GetScanline(src_top + row) + src_left * 4);
+        const uint8_t* src_scan =
+            pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
         for (int col = 0; col < width; col++) {
-          FXARGB_SETDIB(dest_scan, FXARGB_MAKE(0xff, src_scan[0], src_scan[1],
-                                               src_scan[2]));
+          FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[0], src_scan[1],
+                                              src_scan[2]));
           src_scan += 4;
           dest_scan += 4;
         }
@@ -266,13 +273,13 @@
 static void DebugValidate(const RetainPtr<CFX_DIBitmap>& bitmap,
                           const RetainPtr<CFX_DIBitmap>& device) {
   if (bitmap) {
-    SkASSERT(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32);
+    ASSERT(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32);
     if (bitmap->GetBPP() == 32) {
       bitmap->DebugVerifyBitmapIsPreMultiplied(nullptr);
     }
   }
   if (device) {
-    SkASSERT(device->GetBPP() == 8 || device->GetBPP() == 32);
+    ASSERT(device->GetBPP() == 8 || device->GetBPP() == 32);
     if (device->GetBPP() == 32) {
       device->DebugVerifyBitmapIsPreMultiplied(nullptr);
     }
@@ -280,6 +287,29 @@
 }
 #endif  // _SKIA_SUPPORT_
 
+constexpr int kAlternateOrWindingFillModeMask =
+    FXFILL_ALTERNATE | FXFILL_WINDING;
+
+int GetAlternateOrWindingFillMode(int fill_mode) {
+  return fill_mode & kAlternateOrWindingFillModeMask;
+}
+
+bool IsAlternateFillMode(int fill_mode) {
+  // TODO(thestig): This function should be able to assert
+  // GetAlternateOrWindingFillMode(fill_mode) != 0.
+  return GetAlternateOrWindingFillMode(fill_mode) == FXFILL_ALTERNATE;
+}
+
+SkPathFillType GetAlternateOrWindingFillType(int fill_mode) {
+  return IsAlternateFillMode(fill_mode) ? SkPathFillType::kEvenOdd
+                                        : SkPathFillType::kWinding;
+}
+
+bool IsEvenOddFillType(SkPathFillType fill) {
+  return fill == SkPathFillType::kEvenOdd ||
+         fill == SkPathFillType::kInverseEvenOdd;
+}
+
 SkPath BuildPath(const CFX_PathData* pPathData) {
   SkPath skPath;
   const CFX_PathData* pFPath = pPathData;
@@ -317,64 +347,78 @@
   return skMatrix;
 }
 
-SkBlendMode GetSkiaBlendMode(int blend_type) {
+SkBlendMode GetSkiaBlendMode(BlendMode blend_type) {
   switch (blend_type) {
-    case FXDIB_BLEND_MULTIPLY:
+    case BlendMode::kMultiply:
       return SkBlendMode::kMultiply;
-    case FXDIB_BLEND_SCREEN:
+    case BlendMode::kScreen:
       return SkBlendMode::kScreen;
-    case FXDIB_BLEND_OVERLAY:
+    case BlendMode::kOverlay:
       return SkBlendMode::kOverlay;
-    case FXDIB_BLEND_DARKEN:
+    case BlendMode::kDarken:
       return SkBlendMode::kDarken;
-    case FXDIB_BLEND_LIGHTEN:
+    case BlendMode::kLighten:
       return SkBlendMode::kLighten;
-    case FXDIB_BLEND_COLORDODGE:
+    case BlendMode::kColorDodge:
       return SkBlendMode::kColorDodge;
-    case FXDIB_BLEND_COLORBURN:
+    case BlendMode::kColorBurn:
       return SkBlendMode::kColorBurn;
-    case FXDIB_BLEND_HARDLIGHT:
+    case BlendMode::kHardLight:
       return SkBlendMode::kHardLight;
-    case FXDIB_BLEND_SOFTLIGHT:
+    case BlendMode::kSoftLight:
       return SkBlendMode::kSoftLight;
-    case FXDIB_BLEND_DIFFERENCE:
+    case BlendMode::kDifference:
       return SkBlendMode::kDifference;
-    case FXDIB_BLEND_EXCLUSION:
+    case BlendMode::kExclusion:
       return SkBlendMode::kExclusion;
-    case FXDIB_BLEND_HUE:
+    case BlendMode::kHue:
       return SkBlendMode::kHue;
-    case FXDIB_BLEND_SATURATION:
+    case BlendMode::kSaturation:
       return SkBlendMode::kSaturation;
-    case FXDIB_BLEND_COLOR:
+    case BlendMode::kColor:
       return SkBlendMode::kColor;
-    case FXDIB_BLEND_LUMINOSITY:
+    case BlendMode::kLuminosity:
       return SkBlendMode::kLuminosity;
-    case FXDIB_BLEND_NORMAL:
+    case BlendMode::kNormal:
     default:
       return SkBlendMode::kSrcOver;
   }
 }
 
-bool AddColors(const CPDF_ExpIntFunc* pFunc, SkTDArray<SkColor>* skColors) {
+// Add begin & end colors into |skColors| array for each gradient transition.
+//
+// |is_encode_reversed| must be set to true when the parent function of |pFunc|
+// has an Encode array, and the matching pair of encode values for |pFunc| are
+// in decreasing order.
+bool AddColors(const CPDF_ExpIntFunc* pFunc,
+               SkTDArray<SkColor>* skColors,
+               bool is_encode_reversed) {
   if (pFunc->CountInputs() != 1)
     return false;
   if (pFunc->m_Exponent != 1)
     return false;
   if (pFunc->m_nOrigOutputs != 3)
     return false;
-  skColors->push(
-      SkColorSetARGB(0xFF, SkUnitScalarClampToByte(pFunc->m_pBeginValues[0]),
-                     SkUnitScalarClampToByte(pFunc->m_pBeginValues[1]),
-                     SkUnitScalarClampToByte(pFunc->m_pBeginValues[2])));
-  skColors->push(
-      SkColorSetARGB(0xFF, SkUnitScalarClampToByte(pFunc->m_pEndValues[0]),
-                     SkUnitScalarClampToByte(pFunc->m_pEndValues[1]),
-                     SkUnitScalarClampToByte(pFunc->m_pEndValues[2])));
+
+  auto begin_values = pFunc->m_BeginValues.begin();
+  auto end_values = pFunc->m_EndValues.begin();
+  if (is_encode_reversed)
+    std::swap(begin_values, end_values);
+
+  skColors->push_back(SkColorSetARGB(0xFF,
+                                     SkUnitScalarClampToByte(begin_values[0]),
+                                     SkUnitScalarClampToByte(begin_values[1]),
+                                     SkUnitScalarClampToByte(begin_values[2])));
+  skColors->push_back(SkColorSetARGB(0xFF,
+                                     SkUnitScalarClampToByte(end_values[0]),
+                                     SkUnitScalarClampToByte(end_values[1]),
+                                     SkUnitScalarClampToByte(end_values[2])));
   return true;
 }
 
 uint8_t FloatToByte(float f) {
-  ASSERT(0 <= f && f <= 1);
+  ASSERT(f >= 0);
+  ASSERT(f <= 1);
   return (uint8_t)(f * 255.99f);
 }
 
@@ -406,19 +450,20 @@
     colorsMin[i] = pFunc->GetRange(i * 2);
     colorsMax[i] = pFunc->GetRange(i * 2 + 1);
   }
-  const uint8_t* pSampleData = pFunc->GetSampleStream()->GetData();
+  pdfium::span<const uint8_t> pSampleData = pFunc->GetSampleStream()->GetSpan();
+  CFX_BitStream bitstream(pSampleData);
   for (uint32_t i = 0; i < sampleCount; ++i) {
     float floatColors[3];
     for (uint32_t j = 0; j < 3; ++j) {
-      int sample = GetBits32(pSampleData, (i * 3 + j) * sampleSize, sampleSize);
-      float interp = (float)sample / (sampleCount - 1);
+      float sample = static_cast<float>(bitstream.GetBits(sampleSize));
+      float interp = sample / (sampleCount - 1);
       floatColors[j] = colorsMin[j] + (colorsMax[j] - colorsMin[j]) * interp;
     }
     SkColor color =
         SkPackARGB32(0xFF, FloatToByte(floatColors[0]),
                      FloatToByte(floatColors[1]), FloatToByte(floatColors[2]));
-    skColors->push(color);
-    skPos->push((float)i / (sampleCount - 1));
+    skColors->push_back(color);
+    skPos->push_back((float)i / (sampleCount - 1));
   }
   return true;
 }
@@ -434,12 +479,15 @@
     const CPDF_ExpIntFunc* pSubFunc = subFunctions[i]->ToExpIntFunc();
     if (!pSubFunc)
       return false;
-    if (!AddColors(pSubFunc, skColors))
+    // Check if the matching encode values are reversed
+    bool is_encode_reversed =
+        pFunc->GetEncode(2 * i) > pFunc->GetEncode(2 * i + 1);
+    if (!AddColors(pSubFunc, skColors, is_encode_reversed))
       return false;
     float boundsEnd =
         i < subFunctionCount - 1 ? pFunc->GetBound(i + 1) : pFunc->GetDomain(1);
-    skPos->push(boundsStart);
-    skPos->push(boundsEnd);
+    skPos->push_back(boundsStart);
+    skPos->push_back(boundsEnd);
     boundsStart = boundsEnd;
   }
   return true;
@@ -502,15 +550,23 @@
   }
   if (minPerpPtIndex < 0 && maxPerpPtIndex < 0)  // nothing's outside
     return;
+
   // determine if negative distances are before start or after end
   SkPoint beforeStart = {pts[0].fX * 2 - pts[1].fX, pts[0].fY * 2 - pts[1].fY};
   bool beforeNeg = LineSide(startPerp, beforeStart) < 0;
-  const SkPoint& startEdgePt =
-      clipStart ? pts[0] : beforeNeg ? rectPts[minPerpPtIndex]
-                                     : rectPts[maxPerpPtIndex];
-  const SkPoint& endEdgePt = clipEnd ? pts[1] : beforeNeg
-                                                    ? rectPts[maxPerpPtIndex]
-                                                    : rectPts[minPerpPtIndex];
+
+  int noClipStartIndex = maxPerpPtIndex;
+  int noClipEndIndex = minPerpPtIndex;
+  if (beforeNeg)
+    std::swap(noClipStartIndex, noClipEndIndex);
+  if ((!clipStart && noClipStartIndex < 0) ||
+      (!clipEnd && noClipEndIndex < 0)) {
+    return;
+  }
+
+  const SkPoint& startEdgePt = clipStart ? pts[0] : rectPts[noClipStartIndex];
+  const SkPoint& endEdgePt = clipEnd ? pts[1] : rectPts[noClipEndIndex];
+
   // find the corners that bound the gradient
   SkScalar minDist = SK_ScalarMax;
   SkScalar maxDist = SK_ScalarMin;
@@ -541,11 +597,10 @@
 }
 
 #ifdef _SKIA_SUPPORT_
-void SetBitmapMatrix(const CFX_Matrix* pMatrix,
+void SetBitmapMatrix(const CFX_Matrix& m,
                      int width,
                      int height,
                      SkMatrix* skMatrix) {
-  const CFX_Matrix& m = *pMatrix;
   skMatrix->setAll(m.a / width, -m.c / height, m.c + m.e, m.b / width,
                    -m.d / height, m.d + m.f, 0, 0, 1);
 }
@@ -553,19 +608,18 @@
 void SetBitmapPaint(bool isAlphaMask,
                     uint32_t argb,
                     int bitmap_alpha,
-                    int blend_type,
+                    BlendMode blend_type,
                     SkPaint* paint) {
   paint->setAntiAlias(true);
-  if (isAlphaMask) {
-    paint->setColorFilter(
-        SkColorFilter::MakeModeFilter(argb, SkBlendMode::kSrc));
-  }
+  if (isAlphaMask)
+    paint->setColorFilter(SkColorFilters::Blend(argb, SkBlendMode::kSrc));
+
   // paint->setFilterQuality(kHigh_SkFilterQuality);
   paint->setBlendMode(GetSkiaBlendMode(blend_type));
   paint->setAlpha(bitmap_alpha);
 }
 
-bool Upsample(const RetainPtr<CFX_DIBSource>& pSource,
+bool Upsample(const RetainPtr<CFX_DIBBase>& pSource,
               std::unique_ptr<uint8_t, FxFreeDeleter>& dst8Storage,
               std::unique_ptr<uint32_t, FxFreeDeleter>& dst32Storage,
               SkBitmap* skBitmap,
@@ -646,7 +700,7 @@
       pSource->DebugVerifyBitmapIsPreMultiplied(buffer);
       break;
     default:
-      SkASSERT(0);  // TODO(caryclark) ensure that all cases are covered
+      NOTREACHED();  // TODO(bug_11) ensure that all cases are covered
       colorType = SkColorType::kUnknown_SkColorType;
   }
   SkImageInfo imageInfo =
@@ -677,28 +731,7 @@
   };
 
   // mark all cached state as uninitialized
-  explicit SkiaState(CFX_SkiaDeviceDriver* pDriver)
-      : m_pDriver(pDriver),
-        m_pTypeFace(nullptr),
-        m_fontSize(0),
-        m_scaleX(0),
-        m_fillColor(0),
-        m_strokeColor(0),
-        m_blendType(0),
-        m_commandIndex(0),
-        m_drawIndex(INT_MAX),
-        m_clipIndex(0),
-        m_type(Accumulator::kNone),
-        m_fillFullCover(false),
-        m_fillPath(false),
-        m_groupKnockout(false),
-        m_debugDisable(false)
-#if SHOW_SKIA_PATH
-        ,
-        m_debugSaveCounter(0)
-#endif
-  {
-  }
+  explicit SkiaState(CFX_SkiaDeviceDriver* pDriver) : m_pDriver(pDriver) {}
 
   bool DrawPath(const CFX_PathData* pPathData,
                 const CFX_Matrix* pMatrix,
@@ -706,7 +739,7 @@
                 uint32_t fill_color,
                 uint32_t stroke_color,
                 int fill_mode,
-                int blend_type) {
+                BlendMode blend_type) {
     if (m_debugDisable)
       return false;
     Dump(__func__);
@@ -720,12 +753,10 @@
     if (Accumulator::kPath != m_type) {
       m_skPath.reset();
       m_fillFullCover = !!(fill_mode & FXFILL_FULLCOVER);
-      m_fillPath = (fill_mode & 3) && fill_color;
-      m_skPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE
-                               ? SkPath::kEvenOdd_FillType
-                               : SkPath::kWinding_FillType);
+      m_fillPath = GetAlternateOrWindingFillMode(fill_mode) && fill_color;
+      m_skPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
       if (pDrawState)
-        m_drawState.Copy(*pDrawState);
+        m_drawState = *pDrawState;
       m_fillColor = fill_color;
       m_strokeColor = stroke_color;
       m_blendType = blend_type;
@@ -754,17 +785,23 @@
     if (stroke_alpha)
       m_pDriver->PaintStroke(&skPaint, &m_drawState, skMatrix);
     SkCanvas* skCanvas = m_pDriver->SkiaCanvas();
-    skCanvas->save();
+    SkAutoCanvasRestore scoped_save_restore(skCanvas, /*doSave=*/true);
     skCanvas->concat(skMatrix);
+    bool do_stroke = true;
     if (m_fillPath) {
       SkPath strokePath;
       const SkPath* fillPath = &m_skPath;
       if (stroke_alpha) {
         if (m_groupKnockout) {
           skPaint.getFillPath(m_skPath, &strokePath);
-          if (Op(m_skPath, strokePath, SkPathOp::kDifference_SkPathOp,
+          if (m_strokeColor == m_fillColor &&
+              Op(m_skPath, strokePath, SkPathOp::kUnion_SkPathOp,
                  &strokePath)) {
             fillPath = &strokePath;
+            do_stroke = false;
+          } else if (Op(m_skPath, strokePath, SkPathOp::kDifference_SkPathOp,
+                        &strokePath)) {
+            fillPath = &strokePath;
           }
         }
       }
@@ -776,7 +813,7 @@
       DebugShowSkiaDrawPath(m_pDriver.Get(), skCanvas, skPaint, *fillPath);
       skCanvas->drawPath(*fillPath, skPaint);
     }
-    if (stroke_alpha) {
+    if (stroke_alpha && do_stroke) {
       skPaint.setStyle(SkPaint::kStroke_Style);
       skPaint.setColor(m_strokeColor);
 #ifdef _SKIA_SUPPORT_PATHS_
@@ -785,20 +822,20 @@
       DebugShowSkiaDrawPath(m_pDriver.Get(), skCanvas, skPaint, m_skPath);
       skCanvas->drawPath(m_skPath, skPaint);
     }
-    skCanvas->restore();
     m_drawIndex = INT_MAX;
     m_type = Accumulator::kNone;
+    m_drawMatrix = CFX_Matrix();
   }
 
   bool HasRSX(int nChars,
-              const FXTEXT_CHARPOS* pCharPos,
+              const TextCharPos* pCharPos,
               float* scaleXPtr,
-              bool* oneAtATimePtr) {
+              bool* oneAtATimePtr) const {
     bool useRSXform = false;
     bool oneAtATime = false;
     float scaleX = 1;
     for (int index = 0; index < nChars; ++index) {
-      const FXTEXT_CHARPOS& cp = pCharPos[index];
+      const TextCharPos& cp = pCharPos[index];
       if (!cp.m_bGlyphAdjust)
         continue;
       bool upright = 0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2];
@@ -823,9 +860,9 @@
   }
 
   bool DrawText(int nChars,
-                const FXTEXT_CHARPOS* pCharPos,
+                const TextCharPos* pCharPos,
                 CFX_Font* pFont,
-                const CFX_Matrix* pMatrix,
+                const CFX_Matrix& matrix,
                 float font_size,
                 uint32_t color) {
     if (m_debugDisable)
@@ -841,60 +878,74 @@
     int drawIndex = SkTMin(m_drawIndex, m_commands.count());
     if (Accumulator::kPath == m_type || drawIndex != m_commandIndex ||
         (Accumulator::kText == m_type &&
-         (FontChanged(pFont, pMatrix, font_size, scaleX, color) ||
-          hasRSX == !m_rsxform.count()))) {
+         (FontChanged(pFont, matrix, font_size, scaleX, color) ||
+          hasRSX == m_rsxform.isEmpty()))) {
       Flush();
     }
     if (Accumulator::kText != m_type) {
-      m_positions.setCount(0);
-      m_glyphs.setCount(0);
-      m_pTypeFace = pFont->GetFace() ? pFont->GetDeviceCache() : nullptr;
+      m_italicAngle = pFont->GetSubstFontItalicAngle();
+      m_charDetails.SetCount(0);
+      m_rsxform.setCount(0);
+      if (pFont->GetFaceRec())
+        m_pTypeFace.reset(SkSafeRef(pFont->GetDeviceCache()));
+      else
+        m_pTypeFace.reset();
       m_fontSize = font_size;
       m_scaleX = scaleX;
       m_fillColor = color;
-      m_drawMatrix = *pMatrix;
+      m_drawMatrix = matrix;
       m_drawIndex = m_commandIndex;
       m_type = Accumulator::kText;
+      m_pFont = pFont;
     }
-    int count = m_positions.count();
-    m_positions.setCount(nChars + count);
-    m_glyphs.setCount(nChars + count);
-    if (hasRSX) {
+    if (!hasRSX && !m_rsxform.isEmpty())
+      FlushText();
+
+    int count = m_charDetails.Count();
+    m_charDetails.SetCount(nChars + count);
+    if (hasRSX)
       m_rsxform.setCount(nChars + count);
-    }
+
     SkScalar flip = m_fontSize < 0 ? -1 : 1;
     SkScalar vFlip = flip;
     if (pFont->IsVertical())
       vFlip *= -1;
     for (int index = 0; index < nChars; ++index) {
-      const FXTEXT_CHARPOS& cp = pCharPos[index];
-      m_positions[index + count] = {cp.m_Origin.x * flip,
-                                    cp.m_Origin.y * vFlip};
-      m_glyphs[index + count] = static_cast<uint16_t>(cp.m_GlyphIndex);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-      if (cp.m_ExtGID)
-        m_glyphs[index + count] = static_cast<uint16_t>(cp.m_ExtGID);
+      const TextCharPos& cp = pCharPos[index];
+      int cur_index = index + count;
+      m_charDetails.SetPositionAt(
+          cur_index, {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip});
+      m_charDetails.SetGlyphAt(cur_index,
+                               static_cast<uint16_t>(cp.m_GlyphIndex));
+      m_charDetails.SetFontCharWidthAt(cur_index, cp.m_FontCharWidth);
+#if defined(OS_MACOSX)
+      if (cp.m_ExtGID) {
+        m_charDetails.SetGlyphAt(cur_index, static_cast<uint16_t>(cp.m_ExtGID));
+      }
 #endif
     }
     SkPoint delta;
-    if (MatrixOffset(pMatrix, &delta)) {
-      for (int index = 0; index < nChars; ++index)
-        m_positions[index + count].offset(delta.fX * flip, -delta.fY * flip);
+    if (MatrixOffset(&matrix, &delta)) {
+      for (int index = 0; index < nChars; ++index) {
+        m_charDetails.OffsetPositionAt(index + count, delta.fX * flip,
+                                       -delta.fY * flip);
+      }
     }
     if (hasRSX) {
+      const SkTDArray<SkPoint>& positions = m_charDetails.GetPositions();
       for (int index = 0; index < nChars; ++index) {
-        const FXTEXT_CHARPOS& cp = pCharPos[index];
+        const TextCharPos& cp = pCharPos[index];
         SkRSXform* rsxform = &m_rsxform[index + count];
         if (cp.m_bGlyphAdjust) {
           rsxform->fSCos = cp.m_AdjustMatrix[0];
           rsxform->fSSin = cp.m_AdjustMatrix[1];
-          rsxform->fTx = cp.m_AdjustMatrix[0] * m_positions[index].fX;
-          rsxform->fTy = cp.m_AdjustMatrix[1] * m_positions[index].fY;
+          rsxform->fTx = cp.m_AdjustMatrix[0] * positions[index].fX;
+          rsxform->fTy = cp.m_AdjustMatrix[1] * positions[index].fY;
         } else {
           rsxform->fSCos = 1;
           rsxform->fSSin = 0;
-          rsxform->fTx = m_positions[index].fX;
-          rsxform->fTy = m_positions[index].fY;
+          rsxform->fTx = positions[index].fX;
+          rsxform->fTy = positions[index].fY;
         }
       }
     }
@@ -906,44 +957,69 @@
     SkPaint skPaint;
     skPaint.setAntiAlias(true);
     skPaint.setColor(m_fillColor);
+
+    SkFont font;
     if (m_pTypeFace) {  // exclude placeholder test fonts
-      sk_sp<SkTypeface> typeface(SkSafeRef(m_pTypeFace.Get()));
-      skPaint.setTypeface(typeface);
+      font.setTypeface(m_pTypeFace);
     }
-    skPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    skPaint.setHinting(SkPaint::kNo_Hinting);
-    skPaint.setTextScaleX(m_scaleX);
-    skPaint.setTextSize(SkTAbs(m_fontSize));
-    skPaint.setSubpixelText(true);
+    font.setHinting(SkFontHinting::kNone);
+    font.setScaleX(m_scaleX);
+    font.setSkewX(tanf(m_italicAngle * FX_PI / 180.0));
+    font.setSize(SkTAbs(m_fontSize));
+    font.setSubpixel(true);
+
     SkCanvas* skCanvas = m_pDriver->SkiaCanvas();
-    skCanvas->save();
+    SkAutoCanvasRestore scoped_save_restore(skCanvas, /*doSave=*/true);
     SkScalar flip = m_fontSize < 0 ? -1 : 1;
     SkMatrix skMatrix = ToFlippedSkMatrix(m_drawMatrix, flip);
     skCanvas->concat(skMatrix);
+    const SkTDArray<uint16_t>& glyphs = m_charDetails.GetGlyphs();
 #ifdef _SKIA_SUPPORT_PATHS_
     m_pDriver->PreMultiply();
 #endif  // _SKIA_SUPPORT_PATHS_
 #if SHOW_TEXT_GLYPHS
     SkTDArray<SkUnichar> text;
+    // TODO(nigi): |m_glyphs| are deprecated and glyphToUnichars() takes 4
+    // parameters now.
     text.setCount(m_glyphs.count());
     skPaint.glyphsToUnichars(m_glyphs.begin(), m_glyphs.count(), text.begin());
-    for (size_t i = 0; i < m_glyphs.count(); ++i)
+    for (int i = 0; i < m_glyphs.count(); ++i)
       printf("%lc", m_glyphs[i]);
     printf("\n");
 #endif
+
     if (m_rsxform.count()) {
-      skCanvas->drawTextRSXform(m_glyphs.begin(), m_glyphs.count() * 2,
-                                m_rsxform.begin(), nullptr, skPaint);
+      sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromRSXform(
+          glyphs.begin(), glyphs.bytes(), m_rsxform.begin(), font,
+          SkTextEncoding::kGlyphID);
+      skCanvas->drawTextBlob(blob, 0, 0, skPaint);
     } else {
-      skCanvas->drawPosText(m_glyphs.begin(), m_glyphs.count() * 2,
-                            m_positions.begin(), skPaint);
+      const SkTDArray<SkPoint>& positions = m_charDetails.GetPositions();
+      const SkTDArray<uint32_t>& widths = m_charDetails.GetFontCharWidths();
+      for (int i = 0; i < m_charDetails.Count(); ++i) {
+        uint32_t font_glyph_width =
+            m_pFont ? m_pFont->GetGlyphWidth(glyphs[i]) : 0;
+        uint32_t pdf_glyph_width = widths[i];
+        if (font_glyph_width && pdf_glyph_width &&
+            font_glyph_width > pdf_glyph_width) {
+          font.setScaleX(SkIntToScalar(pdf_glyph_width) / font_glyph_width);
+        } else {
+          font.setScaleX(SkIntToScalar(1));
+        }
+        sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromText(
+            &glyphs[i], sizeof(glyphs[i]), font, SkTextEncoding::kGlyphID);
+        skCanvas->drawTextBlob(blob, positions[i].fX, positions[i].fY, skPaint);
+      }
     }
-    skCanvas->restore();
+
     m_drawIndex = INT_MAX;
     m_type = Accumulator::kNone;
+    m_drawMatrix = CFX_Matrix();
+    m_pFont = nullptr;
+    m_italicAngle = 0;
   }
 
-  bool IsEmpty() { return !m_commands.count(); }
+  bool IsEmpty() const { return !m_commands.count(); }
 
   bool SetClipFill(const CFX_PathData* pPathData,
                    const CFX_Matrix* pMatrix,
@@ -968,9 +1044,7 @@
     }
     if (skClipPath.isEmpty()) {
       skClipPath = BuildPath(pPathData);
-      skClipPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE
-                                 ? SkPath::kEvenOdd_FillType
-                                 : SkPath::kWinding_FillType);
+      skClipPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
       SkMatrix skMatrix = ToSkMatrix(*pMatrix);
       skClipPath.transform(skMatrix);
     }
@@ -990,7 +1064,7 @@
     while (m_clipIndex > m_commandIndex) {
       do {
         --m_clipIndex;
-        SkASSERT(m_clipIndex >= 0);
+        ASSERT(m_clipIndex >= 0);
       } while (m_commands[m_clipIndex] != Clip::kSave);
       m_pDriver->SkiaCanvas()->restore();
     }
@@ -998,7 +1072,7 @@
       m_commands[m_commandIndex] = Clip::kPath;
       m_clips[m_commandIndex] = skClipPath;
     } else {
-      m_commands.push(Clip::kPath);
+      m_commands.push_back(Clip::kPath);
       m_clips.push_back(skClipPath);
     }
     ++m_commandIndex;
@@ -1060,7 +1134,7 @@
       m_clips[m_commandIndex] = m_skEmptyPath;
     } else {
       AdjustClip(m_commandIndex);
-      m_commands.push(Clip::kSave);
+      m_commands.push_back(Clip::kSave);
       m_clips.push_back(m_skEmptyPath);
     }
     ++m_commandIndex;
@@ -1072,7 +1146,7 @@
       return false;
     Dump(__func__);
     while (Clip::kSave != m_commands[--m_commandIndex]) {
-      SkASSERT(m_commandIndex > 0);
+      ASSERT(m_commandIndex > 0);
     }
     return true;
   }
@@ -1082,35 +1156,30 @@
                    uint32_t fill_color,
                    uint32_t stroke_color,
                    int fill_mode,
-                   int blend_type,
+                   BlendMode blend_type,
                    bool group_knockout) const {
-    return MatrixChanged(pMatrix, m_drawMatrix) ||
-           StateChanged(pState, m_drawState) || fill_color != m_fillColor ||
-           stroke_color != m_strokeColor ||
-           ((fill_mode & 3) == FXFILL_ALTERNATE) !=
-               (m_skPath.getFillType() == SkPath::kEvenOdd_FillType) ||
-           blend_type != m_blendType || group_knockout != m_groupKnockout;
+    return MatrixChanged(pMatrix) || StateChanged(pState, m_drawState) ||
+           fill_color != m_fillColor || stroke_color != m_strokeColor ||
+           IsEvenOddFillType(m_skPath.getFillType()) ||
+           IsAlternateFillMode(fill_mode) || blend_type != m_blendType ||
+           group_knockout != m_groupKnockout;
   }
 
   bool FontChanged(CFX_Font* pFont,
-                   const CFX_Matrix* pMatrix,
+                   const CFX_Matrix& matrix,
                    float font_size,
                    float scaleX,
                    uint32_t color) const {
     CFX_TypeFace* typeface =
-        pFont->GetFace() ? pFont->GetDeviceCache() : nullptr;
-    return typeface != m_pTypeFace || MatrixChanged(pMatrix, m_drawMatrix) ||
+        pFont->GetFaceRec() ? pFont->GetDeviceCache() : nullptr;
+    return typeface != m_pTypeFace.get() || MatrixChanged(&matrix) ||
            font_size != m_fontSize || scaleX != m_scaleX ||
-           color != m_fillColor;
+           color != m_fillColor ||
+           pFont->GetSubstFontItalicAngle() != m_italicAngle;
   }
 
-  bool MatrixChanged(const CFX_Matrix* pMatrix,
-                     const CFX_Matrix& refMatrix) const {
-    CFX_Matrix identityMatrix;
-    if (!pMatrix)
-      pMatrix = &identityMatrix;
-    return pMatrix->a != refMatrix.a || pMatrix->b != refMatrix.b ||
-           pMatrix->c != refMatrix.c || pMatrix->d != refMatrix.d;
+  bool MatrixChanged(const CFX_Matrix* pMatrix) const {
+    return pMatrix ? *pMatrix != m_drawMatrix : m_drawMatrix.IsIdentity();
   }
 
   bool StateChanged(const CFX_GraphStateData* pState,
@@ -1127,27 +1196,20 @@
 
   bool DashChanged(const CFX_GraphStateData* pState,
                    const CFX_GraphStateData& refState) const {
-    bool dashArray = pState && pState->m_DashArray;
-    if (!dashArray && !refState.m_DashArray)
+    bool dashArray = pState && !pState->m_DashArray.empty();
+    if (!dashArray && refState.m_DashArray.empty())
       return false;
-    if (!dashArray || !refState.m_DashArray)
+    if (!dashArray || refState.m_DashArray.empty())
       return true;
-    if (pState->m_DashPhase != refState.m_DashPhase ||
-        pState->m_DashCount != refState.m_DashCount) {
-      return true;
-    }
-    for (int index = 0; index < pState->m_DashCount; ++index) {
-      if (pState->m_DashArray[index] != refState.m_DashArray[index])
-        return true;
-    }
-    return true;
+    return pState->m_DashPhase != refState.m_DashPhase ||
+           pState->m_DashArray != refState.m_DashArray;
   }
 
   void AdjustClip(int limit) {
     while (m_clipIndex > limit) {
       do {
         --m_clipIndex;
-        SkASSERT(m_clipIndex >= 0);
+        ASSERT(m_clipIndex >= 0);
       } while (m_commands[m_clipIndex] != Clip::kSave);
       m_pDriver->SkiaCanvas()->restore();
     }
@@ -1155,7 +1217,7 @@
       if (Clip::kSave == m_commands[m_clipIndex]) {
         m_pDriver->SkiaCanvas()->save();
       } else {
-        SkASSERT(Clip::kPath == m_commands[m_clipIndex]);
+        ASSERT(Clip::kPath == m_commands[m_clipIndex]);
         m_pDriver->SkiaCanvas()->clipPath(m_clips[m_clipIndex],
                                           SkClipOp::kIntersect, true);
       }
@@ -1207,9 +1269,9 @@
     printf(
         "\n%s\nSkia Save Count %d  Agg Save Stack/Count %d/%d"
         "  Cache Save Index/Count %d/%d\n",
-        where, m_pDriver->m_pCanvas->getSaveCount(),
-        (int)m_pDriver->m_StateStack.size(), AggSaveCount(m_pDriver),
-        m_commandIndex, CacheSaveCount(m_commands, m_commandIndex));
+        where, m_pDriver->SkiaCanvas()->getSaveCount(),
+        (int)m_pDriver->stack().size(), AggSaveCount(m_pDriver), m_commandIndex,
+        CacheSaveCount(m_commands, m_commandIndex));
     printf("Cache:\n");
 #if SHOW_SKIA_PATH_SHORTHAND
     bool dumpedPath = false;
@@ -1243,12 +1305,12 @@
       printf("\n");
 #endif
     DumpEndPrefix();
-    int skCanvasSaveCount = m_pDriver->m_pCanvas->getSaveCount();
+    int skCanvasSaveCount = m_pDriver->SkiaCanvas()->getSaveCount();
     int cacheSaveCount = 1;
-    SkASSERT(m_clipIndex <= m_commands.count());
+    ASSERT(m_clipIndex <= m_commands.count());
     for (int index = 0; index < m_clipIndex; ++index)
       cacheSaveCount += Clip::kSave == m_commands[index];
-    SkASSERT(skCanvasSaveCount == cacheSaveCount);
+    ASSERT(skCanvasSaveCount == cacheSaveCount);
 #endif  // SHOW_SKIA_PATH
   }
 
@@ -1257,26 +1319,26 @@
     FX_RECT last;
     int aggSaveCount = 0;
     bool foundLast = false;
-    for (int index = 0; index < (int)driver->m_StateStack.size(); ++index) {
-      if (!driver->m_StateStack[index]) {
+    for (int index = 0; index < (int)driver->stack().size(); ++index) {
+      if (!driver->stack()[index]) {
         continue;
       }
-      if (driver->m_StateStack[index]->GetType() != CFX_ClipRgn::RectI) {
+      if (driver->stack()[index]->GetType() != CFX_ClipRgn::RectI) {
         aggSaveCount += 1;
         foundLast = false;
         continue;
       }
-      if (!foundLast || memcmp(&last, &driver->m_StateStack[index]->GetBox(),
-                               sizeof(FX_RECT))) {
+      if (!foundLast ||
+          memcmp(&last, &driver->stack()[index]->GetBox(), sizeof(FX_RECT))) {
         aggSaveCount += 1;
         foundLast = true;
-        last = driver->m_StateStack[index]->GetBox();
+        last = driver->stack()[index]->GetBox();
       }
     }
-    if (driver->m_pClipRgn) {
-      CFX_ClipRgn::ClipType clipType = driver->m_pClipRgn->GetType();
+    if (driver->clip_region()) {
+      CFX_ClipRgn::ClipType clipType = driver->clip_region()->GetType();
       if (clipType != CFX_ClipRgn::RectI || !foundLast ||
-          memcmp(&last, &driver->m_pClipRgn->GetBox(), sizeof(FX_RECT))) {
+          memcmp(&last, &driver->clip_region()->GetBox(), sizeof(FX_RECT))) {
         aggSaveCount += 1;
       }
     }
@@ -1305,20 +1367,20 @@
       return;
     int aggSaveCount = AggSaveCount(m_pDriver);
     int cacheSaveCount = CacheSaveCount(m_commands, m_commandIndex);
-    SkASSERT(m_clipIndex <= m_commands.count());
+    ASSERT(m_clipIndex <= m_commands.count());
     if (aggSaveCount != cacheSaveCount) {
       // may not signify a bug if counts don't match
       printf("aggSaveCount %d != cacheSaveCount %d\n", aggSaveCount,
              cacheSaveCount);
       DumpClipStacks();
     }
-    for (int aggIndex = 0; aggIndex < (int)m_pDriver->m_StateStack.size();
+    for (int aggIndex = 0; aggIndex < (int)m_pDriver->stack().size();
          ++aggIndex) {
-      if (!m_pDriver->m_StateStack[aggIndex])
+      if (!m_pDriver->stack()[aggIndex])
         continue;
-      if (m_pDriver->m_StateStack[aggIndex]->GetType() != CFX_ClipRgn::RectI)
+      if (m_pDriver->stack()[aggIndex]->GetType() != CFX_ClipRgn::RectI)
         continue;
-      const FX_RECT& aggRect = m_pDriver->m_StateStack[aggIndex]->GetBox();
+      const FX_RECT& aggRect = m_pDriver->stack()[aggIndex]->GetBox();
       SkRect skRect = SkRect::MakeLTRB(aggRect.left, aggRect.top, aggRect.right,
                                        aggRect.bottom);
       bool foundMatch = false;
@@ -1337,7 +1399,7 @@
       }
       if (!foundMatch) {
         DumpClipStacks();
-        SkASSERT(0);
+        NOTREACHED();
       }
     }
 #endif  // SHOW_SKIA_PATH
@@ -1362,23 +1424,22 @@
       }
     }
     printf("\nagg\n");
-    for (int index = 0; index < (int)m_pDriver->m_StateStack.size(); ++index) {
-      if (!m_pDriver->m_StateStack[index]) {
+    for (int index = 0; index < (int)m_pDriver->stack().size(); ++index) {
+      if (!m_pDriver->stack()[index]) {
         printf("null\n");
         continue;
       }
-      CFX_ClipRgn::ClipType clipType =
-          m_pDriver->m_StateStack[index]->GetType();
-      const FX_RECT& box = m_pDriver->m_StateStack[index]->GetBox();
+      CFX_ClipRgn::ClipType clipType = m_pDriver->stack()[index]->GetType();
+      const FX_RECT& box = m_pDriver->stack()[index]->GetBox();
       printf("stack rect: %d,%d,%d,%d mask=%s\n", box.left, box.top, box.right,
              box.bottom,
              CFX_ClipRgn::MaskF == clipType
                  ? "1"
                  : CFX_ClipRgn::RectI == clipType ? "0" : "?");
     }
-    if (m_pDriver->m_pClipRgn) {
-      const FX_RECT& box = m_pDriver->m_pClipRgn->GetBox();
-      CFX_ClipRgn::ClipType clipType = m_pDriver->m_pClipRgn->GetType();
+    if (m_pDriver->clip_region()) {
+      const FX_RECT& box = m_pDriver->clip_region()->GetBox();
+      CFX_ClipRgn::ClipType clipType = m_pDriver->clip_region()->GetType();
       printf("clip rect: %d,%d,%d,%d mask=%s\n", box.left, box.top, box.right,
              box.bottom,
              CFX_ClipRgn::MaskF == clipType
@@ -1389,35 +1450,74 @@
 #endif  // SHOW_SKIA_PATH
 
  private:
+  class CharDetail {
+   public:
+    CharDetail() = default;
+    ~CharDetail() = default;
+
+    const SkTDArray<SkPoint>& GetPositions() const { return m_positions; }
+    void SetPositionAt(int index, const SkPoint& position) {
+      m_positions[index] = position;
+    }
+    void OffsetPositionAt(int index, SkScalar dx, SkScalar dy) {
+      m_positions[index].offset(dx, dy);
+    }
+    const SkTDArray<uint16_t>& GetGlyphs() const { return m_glyphs; }
+    void SetGlyphAt(int index, uint16_t glyph) { m_glyphs[index] = glyph; }
+    const SkTDArray<uint32_t>& GetFontCharWidths() const {
+      return m_fontCharWidths;
+    }
+    void SetFontCharWidthAt(int index, uint32_t width) {
+      m_fontCharWidths[index] = width;
+    }
+    int Count() const {
+      ASSERT(m_positions.count() == m_glyphs.count());
+      return m_glyphs.count();
+    }
+    void SetCount(int count) {
+      ASSERT(count >= 0);
+      m_positions.setCount(count);
+      m_glyphs.setCount(count);
+      m_fontCharWidths.setCount(count);
+    }
+
+   private:
+    SkTDArray<SkPoint> m_positions;  // accumulator for text positions
+    SkTDArray<uint16_t> m_glyphs;    // accumulator for text glyphs
+    // accumulator for glyphs' width defined in pdf
+    SkTDArray<uint32_t> m_fontCharWidths;
+  };
+
   SkTArray<SkPath> m_clips;        // stack of clips that may be reused
   SkTDArray<Clip> m_commands;      // stack of clip-related commands
-  SkTDArray<SkPoint> m_positions;  // accumulator for text positions
+  CharDetail m_charDetails;
   SkTDArray<SkRSXform> m_rsxform;  // accumulator for txt rotate/scale/translate
-  SkTDArray<uint16_t> m_glyphs;    // accumulator for text glyphs
   SkPath m_skPath;                 // accumulator for path contours
   SkPath m_skEmptyPath;            // used as placehold in the clips array
+  UnownedPtr<CFX_Font> m_pFont;
   CFX_Matrix m_drawMatrix;
   CFX_GraphStateData m_clipState;
   CFX_GraphStateData m_drawState;
   CFX_Matrix m_clipMatrix;
-  UnownedPtr<CFX_SkiaDeviceDriver> m_pDriver;
-  UnownedPtr<CFX_TypeFace> m_pTypeFace;
-  float m_fontSize;
-  float m_scaleX;
-  uint32_t m_fillColor;
-  uint32_t m_strokeColor;
-  int m_blendType;
-  int m_commandIndex;  // active position in clip command stack
-  int m_drawIndex;     // position of the pending path or text draw
-  int m_clipIndex;     // position reflecting depth of canvas clip stacck
-  Accumulator m_type;  // type of pending draw
-  bool m_fillFullCover;
-  bool m_fillPath;
-  bool m_groupKnockout;
-  bool m_debugDisable;  // turn off cache for debugging
+  UnownedPtr<CFX_SkiaDeviceDriver> const m_pDriver;
+  sk_sp<CFX_TypeFace> m_pTypeFace;
+  float m_fontSize = 0;
+  float m_scaleX = 0;
+  uint32_t m_fillColor = 0;
+  uint32_t m_strokeColor = 0;
+  BlendMode m_blendType = BlendMode::kNormal;
+  int m_commandIndex = 0;     // active position in clip command stack
+  int m_drawIndex = INT_MAX;  // position of the pending path or text draw
+  int m_clipIndex = 0;        // position reflecting depth of canvas clip stacck
+  int m_italicAngle = 0;
+  Accumulator m_type = Accumulator::kNone;  // type of pending draw
+  bool m_fillFullCover = false;
+  bool m_fillPath = false;
+  bool m_groupKnockout = false;
+  bool m_debugDisable = false;  // turn off cache for debugging
 #if SHOW_SKIA_PATH
  public:
-  mutable int m_debugSaveCounter;
+  mutable int m_debugSaveCounter = 0;
   static int m_debugInitCounter;
 #endif
 };
@@ -1464,16 +1564,16 @@
   float width =
       SkTMax(pGraphState->m_LineWidth,
              SkTMin(deviceUnits[0].length(), deviceUnits[1].length()));
-  if (pGraphState->m_DashArray) {
-    int count = (pGraphState->m_DashCount + 1) / 2;
+  if (!pGraphState->m_DashArray.empty()) {
+    size_t count = (pGraphState->m_DashArray.size() + 1) / 2;
     std::unique_ptr<SkScalar, FxFreeDeleter> intervals(
         FX_Alloc2D(SkScalar, count, sizeof(SkScalar)));
     // Set dash pattern
-    for (int i = 0; i < count; i++) {
+    for (size_t i = 0; i < count; i++) {
       float on = pGraphState->m_DashArray[i * 2];
       if (on <= 0.000001f)
         on = 1.f / 10;
-      float off = i * 2 + 1 == pGraphState->m_DashCount
+      float off = i * 2 + 1 == pGraphState->m_DashArray.size()
                       ? on
                       : pGraphState->m_DashArray[i * 2 + 1];
       if (off < 0)
@@ -1495,10 +1595,10 @@
 CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(
     const RetainPtr<CFX_DIBitmap>& pBitmap,
     bool bRgbByteOrder,
-    const RetainPtr<CFX_DIBitmap>& pOriDevice,
+    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
     bool bGroupKnockout)
     : m_pBitmap(pBitmap),
-      m_pOriDevice(pOriDevice),
+      m_pBackdropBitmap(pBackdropBitmap),
       m_pRecorder(nullptr),
       m_pCache(new SkiaState(this)),
 #ifdef _SKIA_SUPPORT_PATHS_
@@ -1508,7 +1608,7 @@
 #endif  // _SKIA_SUPPORT_PATHS_
       m_bGroupKnockout(bGroupKnockout) {
   SkBitmap skBitmap;
-  SkASSERT(pBitmap->GetBPP() == 8 || pBitmap->GetBPP() == 32);
+  ASSERT(pBitmap->GetBPP() == 8 || pBitmap->GetBPP() == 32);
   SkImageInfo imageInfo = SkImageInfo::Make(
       pBitmap->GetWidth(), pBitmap->GetHeight(),
       pBitmap->GetBPP() == 8 ? kAlpha_8_SkColorType : kN32_SkColorType,
@@ -1520,7 +1620,7 @@
 #ifdef _SKIA_SUPPORT_
 CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(int size_x, int size_y)
     : m_pBitmap(nullptr),
-      m_pOriDevice(nullptr),
+      m_pBackdropBitmap(nullptr),
       m_pRecorder(new SkPictureRecorder),
       m_pCache(new SkiaState(this)),
       m_bGroupKnockout(false) {
@@ -1530,7 +1630,7 @@
 
 CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkPictureRecorder* recorder)
     : m_pBitmap(nullptr),
-      m_pOriDevice(nullptr),
+      m_pBackdropBitmap(nullptr),
       m_pRecorder(recorder),
       m_pCache(new SkiaState(this)),
       m_bGroupKnockout(false) {
@@ -1553,12 +1653,12 @@
 }
 
 bool CFX_SkiaDeviceDriver::DrawDeviceText(int nChars,
-                                          const FXTEXT_CHARPOS* pCharPos,
+                                          const TextCharPos* pCharPos,
                                           CFX_Font* pFont,
-                                          const CFX_Matrix* pObject2Device,
+                                          const CFX_Matrix& mtObject2Device,
                                           float font_size,
                                           uint32_t color) {
-  if (m_pCache->DrawText(nChars, pCharPos, pFont, pObject2Device, font_size,
+  if (m_pCache->DrawText(nChars, pCharPos, pFont, mtObject2Device, font_size,
                          color)) {
     return true;
   }
@@ -1566,17 +1666,20 @@
   SkPaint paint;
   paint.setAntiAlias(true);
   paint.setColor(color);
-  paint.setTypeface(typeface);
-  paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-  paint.setHinting(SkPaint::kNo_Hinting);
-  paint.setTextSize(SkTAbs(font_size));
-  paint.setSubpixelText(true);
-  m_pCanvas->save();
+
+  SkFont font;
+  font.setTypeface(typeface);
+  font.setHinting(SkFontHinting::kNone);
+  font.setSize(SkTAbs(font_size));
+  font.setSubpixel(true);
+  font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FX_PI / 180.0));
+
+  SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
   SkScalar flip = font_size < 0 ? -1 : 1;
   SkScalar vFlip = flip;
   if (pFont->IsVertical())
     vFlip *= -1;
-  SkMatrix skMatrix = ToFlippedSkMatrix(*pObject2Device, flip);
+  SkMatrix skMatrix = ToFlippedSkMatrix(mtObject2Device, flip);
   m_pCanvas->concat(skMatrix);
   SkTDArray<SkPoint> positions;
   positions.setCount(nChars);
@@ -1585,7 +1688,7 @@
   bool useRSXform = false;
   bool oneAtATime = false;
   for (int index = 0; index < nChars; ++index) {
-    const FXTEXT_CHARPOS& cp = pCharPos[index];
+    const TextCharPos& cp = pCharPos[index];
     positions[index] = {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip};
     if (cp.m_bGlyphAdjust) {
       useRSXform = true;
@@ -1595,7 +1698,7 @@
       }
     }
     glyphs[index] = static_cast<uint16_t>(cp.m_GlyphIndex);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
     if (cp.m_ExtGID)
       glyphs[index] = static_cast<uint16_t>(cp.m_ExtGID);
 #endif
@@ -1606,7 +1709,7 @@
   SkTDArray<SkUnichar> text;
   text.setCount(glyphs.count());
   paint.glyphsToUnichars(glyphs.begin(), glyphs.count(), text.begin());
-  for (size_t i = 0; i < glyphs.count(); ++i)
+  for (int i = 0; i < glyphs.count(); ++i)
     printf("%lc", text[i]);
   printf("\n");
 #endif
@@ -1617,7 +1720,7 @@
     SkTDArray<SkRSXform> xforms;
     xforms.setCount(nChars);
     for (int index = 0; index < nChars; ++index) {
-      const FXTEXT_CHARPOS& cp = pCharPos[index];
+      const TextCharPos& cp = pCharPos[index];
       SkRSXform* rsxform = &xforms[index];
       if (cp.m_bGlyphAdjust) {
         rsxform->fSCos = cp.m_AdjustMatrix[0];
@@ -1631,20 +1734,25 @@
         rsxform->fTy = positions[index].fY;
       }
     }
-    m_pCanvas->drawTextRSXform(glyphs.begin(), nChars * 2, xforms.begin(),
-                               nullptr, paint);
+    m_pCanvas->drawTextBlob(
+        SkTextBlob::MakeFromRSXform(glyphs.begin(), nChars * 2, xforms.begin(),
+                                    font, SkTextEncoding::kGlyphID),
+        0, 0, paint);
   } else if (oneAtATime) {
     for (int index = 0; index < nChars; ++index) {
-      const FXTEXT_CHARPOS& cp = pCharPos[index];
+      const TextCharPos& cp = pCharPos[index];
       if (cp.m_bGlyphAdjust) {
         if (0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2] &&
             1 == cp.m_AdjustMatrix[3]) {
-          paint.setTextScaleX(cp.m_AdjustMatrix[0]);
-          m_pCanvas->drawText(&glyphs[index], 1, positions[index].fX,
-                              positions[index].fY, paint);
-          paint.setTextScaleX(1);
+          font.setScaleX(cp.m_AdjustMatrix[0]);
+          auto blob =
+              SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
+                                       font, SkTextEncoding::kGlyphID);
+          m_pCanvas->drawTextBlob(blob, positions[index].fX,
+                                  positions[index].fY, paint);
+          font.setScaleX(SkIntToScalar(1));
         } else {
-          m_pCanvas->save();
+          SkAutoCanvasRestore scoped_save_restore2(m_pCanvas, /*doSave=*/true);
           SkMatrix adjust;
           adjust.reset();
           adjust.setScaleX(cp.m_AdjustMatrix[0]);
@@ -1653,27 +1761,52 @@
           adjust.setScaleY(cp.m_AdjustMatrix[3]);
           adjust.preTranslate(positions[index].fX, positions[index].fY);
           m_pCanvas->concat(adjust);
-          m_pCanvas->drawText(&glyphs[index], 1, 0, 0, paint);
-          m_pCanvas->restore();
+          auto blob =
+              SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
+                                       font, SkTextEncoding::kGlyphID);
+          m_pCanvas->drawTextBlob(blob, 0, 0, paint);
         }
       } else {
-        m_pCanvas->drawText(&glyphs[index], 1, positions[index].fX,
-                            positions[index].fY, paint);
+        auto blob =
+            SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
+                                     font, SkTextEncoding::kGlyphID);
+        m_pCanvas->drawTextBlob(blob, positions[index].fX, positions[index].fY,
+                                paint);
       }
     }
   } else {
-    m_pCanvas->drawPosText(glyphs.begin(), nChars * 2, positions.begin(),
-                           paint);
+    for (int index = 0; index < nChars; ++index) {
+      const TextCharPos& cp = pCharPos[index];
+      uint32_t font_glyph_width =
+          pFont ? pFont->GetGlyphWidth(cp.m_GlyphIndex) : 0;
+      uint32_t pdf_glyph_width = cp.m_FontCharWidth;
+      if (font_glyph_width && pdf_glyph_width &&
+          font_glyph_width > pdf_glyph_width) {
+        font.setScaleX(SkIntToScalar(pdf_glyph_width) / font_glyph_width);
+      } else {
+        font.setScaleX(SkIntToScalar(1));
+      }
+      auto blob =
+          SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), font,
+                                   SkTextEncoding::kGlyphID);
+      m_pCanvas->drawTextBlob(blob, positions[index].fX, positions[index].fY,
+                              paint);
+    }
   }
-  m_pCanvas->restore();
 
   return true;
 }
 
+int CFX_SkiaDeviceDriver::GetDriverType() const {
+  return 1;
+}
+
+DeviceType CFX_SkiaDeviceDriver::GetDeviceType() const {
+  return DeviceType::kDisplay;
+}
+
 int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) const {
   switch (caps_id) {
-    case FXDC_DEVICE_CLASS:
-      return FXDC_DISPLAY;
 #ifdef _SKIA_SUPPORT_
     case FXDC_PIXEL_WIDTH:
       return m_pCanvas->imageInfo().width();
@@ -1718,8 +1851,11 @@
       return flags;
     }
 #endif  // _SKIA_SUPPORT_PATHS_
+
+    default:
+      NOTREACHED();
+      return 0;
   }
-  return 0;
 }
 
 void CFX_SkiaDeviceDriver::SaveState() {
@@ -1729,7 +1865,7 @@
 
 #ifdef _SKIA_SUPPORT_PATHS_
 #if SHOW_SKIA_PATH
-  printf("SaveState %zd\n", m_StateStack.size());
+  printf("SaveState %zd\n", stack().size());
 #endif
   std::unique_ptr<CFX_ClipRgn> pClip;
   if (m_pClipRgn)
@@ -1835,9 +1971,7 @@
     }
   }
   SkPath skClipPath = BuildPath(pPathData);
-  skClipPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE
-                             ? SkPath::kEvenOdd_FillType
-                             : SkPath::kWinding_FillType);
+  skClipPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
   SkMatrix skMatrix = ToSkMatrix(*deviceMatrix);
   skClipPath.transform(skMatrix);
   DebugShowSkiaPath(skClipPath);
@@ -1856,7 +1990,7 @@
 
 bool CFX_SkiaDeviceDriver::SetClip_PathStroke(
     const CFX_PathData* pPathData,         // path info
-    const CFX_Matrix* pObject2Device,      // optional transformation
+    const CFX_Matrix* pObject2Device,      // required transformation
     const CFX_GraphStateData* pGraphState  // graphic state, for pen attributes
     ) {
   bool cached = m_pCache->SetClipStroke(pPathData, pObject2Device, pGraphState);
@@ -1895,9 +2029,9 @@
     uint32_t fill_color,                    // fill color
     uint32_t stroke_color,                  // stroke color
     int fill_mode,  // fill mode, WINDING or ALTERNATE. 0 for not filled
-    int blend_type) {
-  if (fill_mode & FX_ZEROAREA_FILL)
-    return true;
+    BlendMode blend_type) {
+  ASSERT(GetAlternateOrWindingFillMode(fill_mode) !=
+         kAlternateOrWindingFillModeMask);
   if (m_pCache->DrawPath(pPathData, pObject2Device, pGraphState, fill_color,
                          stroke_color, fill_mode, blend_type)) {
     return true;
@@ -1912,22 +2046,26 @@
   if (fill_mode & FXFILL_FULLCOVER)
     skPaint.setBlendMode(SkBlendMode::kPlus);
   int stroke_alpha = FXARGB_A(stroke_color);
-  if (pGraphState && stroke_alpha)
+  bool is_paint_stroke = pGraphState && stroke_alpha;
+  if (is_paint_stroke)
     PaintStroke(&skPaint, pGraphState, skMatrix);
   SkPath skPath = BuildPath(pPathData);
-  m_pCanvas->save();
+  SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
   m_pCanvas->concat(skMatrix);
-  if ((fill_mode & 3) && fill_color) {
-    skPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE
-                           ? SkPath::kEvenOdd_FillType
-                           : SkPath::kWinding_FillType);
+  bool do_stroke = true;
+  if (GetAlternateOrWindingFillMode(fill_mode) && fill_color) {
+    skPath.setFillType(GetAlternateOrWindingFillType(fill_mode));
     SkPath strokePath;
     const SkPath* fillPath = &skPath;
-    if (pGraphState && stroke_alpha) {
+    if (is_paint_stroke) {
       if (m_bGroupKnockout) {
         skPaint.getFillPath(skPath, &strokePath);
-        if (Op(skPath, strokePath, SkPathOp::kDifference_SkPathOp,
-               &strokePath)) {
+        if (stroke_color == fill_color &&
+            Op(skPath, strokePath, SkPathOp::kUnion_SkPathOp, &strokePath)) {
+          fillPath = &strokePath;
+          do_stroke = false;
+        } else if (Op(skPath, strokePath, SkPathOp::kDifference_SkPathOp,
+                      &strokePath)) {
           fillPath = &strokePath;
         }
       }
@@ -1940,7 +2078,7 @@
     DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, *fillPath);
     m_pCanvas->drawPath(*fillPath, skPaint);
   }
-  if (pGraphState && stroke_alpha) {
+  if (is_paint_stroke && do_stroke) {
     skPaint.setStyle(SkPaint::kStroke_Style);
     skPaint.setColor(stroke_color);
 #ifdef _SKIA_SUPPORT_PATHS_
@@ -1949,30 +2087,28 @@
     DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, skPath);
     m_pCanvas->drawPath(skPath, skPaint);
   }
-  m_pCanvas->restore();
   return true;
 }
 
 bool CFX_SkiaDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
                                             const CFX_PointF& ptLineTo,
                                             uint32_t color,
-                                            int blend_type) {
+                                            BlendMode blend_type) {
   return false;
 }
 
-bool CFX_SkiaDeviceDriver::FillRectWithBlend(const FX_RECT* pRect,
+bool CFX_SkiaDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
                                              uint32_t fill_color,
-                                             int blend_type) {
+                                             BlendMode blend_type) {
   m_pCache->FlushForDraw();
   SkPaint spaint;
   spaint.setAntiAlias(true);
   spaint.setColor(fill_color);
   spaint.setBlendMode(GetSkiaBlendMode(blend_type));
-  SkRect rect =
-      SkRect::MakeLTRB(pRect->left, SkTMin(pRect->top, pRect->bottom),
-                       pRect->right, SkTMax(pRect->bottom, pRect->top));
-  DebugShowSkiaDrawRect(this, m_pCanvas, spaint, rect);
-  m_pCanvas->drawRect(rect, spaint);
+  SkRect srect = SkRect::MakeLTRB(rect.left, SkTMin(rect.top, rect.bottom),
+                                  rect.right, SkTMax(rect.bottom, rect.top));
+  DebugShowSkiaDrawRect(this, m_pCanvas, spaint, srect);
+  m_pCanvas->drawRect(srect, spaint);
   return true;
 }
 
@@ -1996,8 +2132,8 @@
   int nFuncs = pFuncs.size();
   if (nFuncs > 1)  // TODO(caryclark) remove this restriction
     return false;
-  CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict();
-  CPDF_Array* pCoords = pDict->GetArrayFor("Coords");
+  const CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict();
+  const CPDF_Array* pCoords = pDict->GetArrayFor("Coords");
   if (!pCoords && kCoonsPatchMeshShading != shadingType)
     return false;
   // TODO(caryclark) Respect Domain[0], Domain[1]. (Don't know what they do
@@ -2016,10 +2152,10 @@
       if (!AddSamples(pSampledFunc, &skColors, &skPos))
         return false;
     } else if (const CPDF_ExpIntFunc* pExpIntFuc = pFuncs[j]->ToExpIntFunc()) {
-      if (!AddColors(pExpIntFuc, &skColors))
+      if (!AddColors(pExpIntFuc, &skColors, /*is_encode_reversed=*/false))
         return false;
-      skPos.push(0);
-      skPos.push(1);
+      skPos.push_back(0);
+      skPos.push_back(1);
     } else if (const CPDF_StitchFunc* pStitchFunc = pFuncs[j]->ToStitchFunc()) {
       if (!AddStitching(pStitchFunc, &skColors, &skPos))
         return false;
@@ -2027,7 +2163,7 @@
       return false;
     }
   }
-  CPDF_Array* pArray = pDict->GetArrayFor("Extend");
+  const CPDF_Array* pArray = pDict->GetArrayFor("Extend");
   bool clipStart = !pArray || !pArray->GetIntegerAt(0);
   bool clipEnd = !pArray || !pArray->GetIntegerAt(1);
   SkPaint paint;
@@ -2045,15 +2181,15 @@
     float end_y = pCoords->GetNumberAt(3);
     SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
     skMatrix.mapPoints(pts, SK_ARRAY_COUNT(pts));
-    paint.setShader(SkGradientShader::MakeLinear(
-        pts, skColors.begin(), skPos.begin(), skColors.count(),
-        SkShader::kClamp_TileMode));
+    paint.setShader(
+        SkGradientShader::MakeLinear(pts, skColors.begin(), skPos.begin(),
+                                     skColors.count(), SkTileMode::kClamp));
     if (clipStart || clipEnd) {
       // if the gradient is horizontal or vertical, modify the draw rectangle
       if (pts[0].fX == pts[1].fX) {  // vertical
         if (pts[0].fY > pts[1].fY) {
-          SkTSwap(pts[0].fY, pts[1].fY);
-          SkTSwap(clipStart, clipEnd);
+          std::swap(pts[0].fY, pts[1].fY);
+          std::swap(clipStart, clipEnd);
         }
         if (clipStart)
           skRect.fTop = SkTMax(skRect.fTop, pts[0].fY);
@@ -2061,8 +2197,8 @@
           skRect.fBottom = SkTMin(skRect.fBottom, pts[1].fY);
       } else if (pts[0].fY == pts[1].fY) {  // horizontal
         if (pts[0].fX > pts[1].fX) {
-          SkTSwap(pts[0].fX, pts[1].fX);
-          SkTSwap(clipStart, clipEnd);
+          std::swap(pts[0].fX, pts[1].fX);
+          std::swap(clipStart, clipEnd);
         }
         if (clipStart)
           skRect.fLeft = SkTMax(skRect.fLeft, pts[0].fX);
@@ -2089,14 +2225,14 @@
 
     paint.setShader(SkGradientShader::MakeTwoPointConical(
         pts[0], start_r, pts[1], end_r, skColors.begin(), skPos.begin(),
-        skColors.count(), SkShader::kClamp_TileMode));
+        skColors.count(), SkTileMode::kClamp));
     if (clipStart || clipEnd) {
       if (clipStart && start_r)
         skClip.addCircle(pts[0].fX, pts[0].fY, start_r);
       if (clipEnd)
-        skClip.addCircle(pts[1].fX, pts[1].fY, end_r, SkPath::kCCW_Direction);
+        skClip.addCircle(pts[1].fX, pts[1].fY, end_r, SkPathDirection::kCCW);
       else
-        skClip.setFillType(SkPath::kInverseWinding_FillType);
+        skClip.setFillType(SkPathFillType::kInverseWinding);
       skClip.transform(skMatrix);
     }
     SkMatrix inverse;
@@ -2106,7 +2242,7 @@
     skPath.transform(inverse);
   } else {
     ASSERT(kCoonsPatchMeshShading == shadingType);
-    CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject());
+    const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject());
     if (!pStream)
       return false;
     CPDF_MeshStream stream(shadingType, pPattern->GetFuncs(), pStream,
@@ -2115,7 +2251,7 @@
       return false;
     SkPoint cubics[12];
     SkColor colors[4];
-    m_pCanvas->save();
+    SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
     if (!skClip.isEmpty())
       m_pCanvas->clipPath(skClip, SkClipOp::kIntersect, true);
     m_pCanvas->concat(skMatrix);
@@ -2143,20 +2279,18 @@
         float g;
         float b;
         std::tie(r, g, b) = stream.ReadColor();
-        colors[i] = SkColorSetARGBInline(0xFF, (U8CPU)(r * 255),
-                                         (U8CPU)(g * 255), (U8CPU)(b * 255));
+        colors[i] = SkColorSetARGB(0xFF, (U8CPU)(r * 255), (U8CPU)(g * 255),
+                                   (U8CPU)(b * 255));
       }
       m_pCanvas->drawPatch(cubics, colors, nullptr, paint);
     }
-    m_pCanvas->restore();
     return true;
   }
-  m_pCanvas->save();
+  SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
   if (!skClip.isEmpty())
     m_pCanvas->clipPath(skClip, SkClipOp::kIntersect, true);
   m_pCanvas->concat(skMatrix);
   m_pCanvas->drawPath(skPath, paint);
-  m_pCanvas->restore();
   return true;
 }
 
@@ -2201,9 +2335,8 @@
       srcWidth, srcHeight, SkColorType::kN32_SkColorType, kPremul_SkAlphaType);
   SkBitmap skSrcBitmap;
   skSrcBitmap.installPixels(srcImageInfo, srcBuffer, srcRowBytes);
-  SkASSERT(pBitmap);
   uint8_t* dstBuffer = pBitmap->GetBuffer();
-  SkASSERT(dstBuffer);
+  ASSERT(dstBuffer);
   int dstWidth = pBitmap->GetWidth();
   int dstHeight = pBitmap->GetHeight();
   int dstRowBytes = dstWidth * sizeof(uint32_t);
@@ -2222,13 +2355,13 @@
   FX_RECT rect(left, top, left + pBitmap->GetWidth(),
                top + pBitmap->GetHeight());
   RetainPtr<CFX_DIBitmap> pBack;
-  if (m_pOriDevice) {
-    pBack = m_pOriDevice->Clone(&rect);
+  if (m_pBackdropBitmap) {
+    pBack = m_pBackdropBitmap->Clone(&rect);
     if (!pBack)
       return true;
 
     pBack->CompositeBitmap(0, 0, pBack->GetWidth(), pBack->GetHeight(),
-                           m_pBitmap, 0, 0);
+                           m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false);
   } else {
     pBack = m_pBitmap->Clone(&rect);
     if (!pBack)
@@ -2250,72 +2383,70 @@
 }
 
 RetainPtr<CFX_DIBitmap> CFX_SkiaDeviceDriver::GetBackDrop() {
-  return m_pOriDevice;
+  return m_pBackdropBitmap;
 }
 
-bool CFX_SkiaDeviceDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CFX_SkiaDeviceDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                                      uint32_t argb,
-                                     const FX_RECT* pSrcRect,
+                                     const FX_RECT& src_rect,
                                      int left,
                                      int top,
-                                     int blend_type) {
+                                     BlendMode blend_type) {
   if (!m_pBitmap || !m_pBitmap->GetBuffer())
     return true;
 
 #ifdef _SKIA_SUPPORT_
-  CFX_Matrix m(pBitmap->GetWidth(), 0, 0, -pBitmap->GetHeight(), left,
-               top + pBitmap->GetHeight());
+  CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(
+      pBitmap->GetWidth(), pBitmap->GetHeight(), left, top);
   std::unique_ptr<CFX_ImageRenderer> dummy;
-  return StartDIBits(pBitmap, 0xFF, argb, &m, 0, &dummy, blend_type);
+  return StartDIBits(pBitmap, 0xFF, argb, m, FXDIB_ResampleOptions(), &dummy,
+                     blend_type);
 #endif  // _SKIA_SUPPORT_
 
 #ifdef _SKIA_SUPPORT_PATHS_
   Flush();
   if (pBitmap->IsAlphaMask()) {
-    return m_pBitmap->CompositeMask(left, top, pSrcRect->Width(),
-                                    pSrcRect->Height(), pBitmap, argb,
-                                    pSrcRect->left, pSrcRect->top, blend_type,
-                                    m_pClipRgn.get(), m_bRgbByteOrder, 0);
+    return m_pBitmap->CompositeMask(left, top, src_rect.Width(),
+                                    src_rect.Height(), pBitmap, argb,
+                                    src_rect.left, src_rect.top, blend_type,
+                                    m_pClipRgn.get(), m_bRgbByteOrder);
   }
   return m_pBitmap->CompositeBitmap(
-      left, top, pSrcRect->Width(), pSrcRect->Height(), pBitmap, pSrcRect->left,
-      pSrcRect->top, blend_type, m_pClipRgn.get(), m_bRgbByteOrder);
+      left, top, src_rect.Width(), src_rect.Height(), pBitmap, src_rect.left,
+      src_rect.top, blend_type, m_pClipRgn.get(), m_bRgbByteOrder);
 #endif  // _SKIA_SUPPORT_PATHS_
 }
 
-bool CFX_SkiaDeviceDriver::StretchDIBits(
-    const RetainPtr<CFX_DIBSource>& pSource,
-    uint32_t argb,
-    int dest_left,
-    int dest_top,
-    int dest_width,
-    int dest_height,
-    const FX_RECT* pClipRect,
-    uint32_t flags,
-    int blend_type) {
+bool CFX_SkiaDeviceDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
+                                         uint32_t argb,
+                                         int dest_left,
+                                         int dest_top,
+                                         int dest_width,
+                                         int dest_height,
+                                         const FX_RECT* pClipRect,
+                                         const FXDIB_ResampleOptions& options,
+                                         BlendMode blend_type) {
 #ifdef _SKIA_SUPPORT_
   m_pCache->FlushForDraw();
   if (!m_pBitmap->GetBuffer())
     return true;
-  CFX_Matrix m(dest_width, 0, 0, -dest_height, dest_left,
-               dest_top + dest_height);
 
-  m_pCanvas->save();
+  CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(dest_width, dest_height,
+                                                 dest_left, dest_top);
+  SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
   SkRect skClipRect = SkRect::MakeLTRB(pClipRect->left, pClipRect->bottom,
                                        pClipRect->right, pClipRect->top);
   m_pCanvas->clipRect(skClipRect, SkClipOp::kIntersect, true);
   std::unique_ptr<CFX_ImageRenderer> dummy;
-  bool result = StartDIBits(pSource, 0xFF, argb, &m, 0, &dummy, blend_type);
-  m_pCanvas->restore();
-
-  return result;
+  return StartDIBits(pSource, 0xFF, argb, m, FXDIB_ResampleOptions(), &dummy,
+                     blend_type);
 #endif  // _SKIA_SUPPORT_
 
 #ifdef _SKIA_SUPPORT_PATHS_
   if (dest_width == pSource->GetWidth() &&
       dest_height == pSource->GetHeight()) {
     FX_RECT rect(0, 0, dest_width, dest_height);
-    return SetDIBits(pSource, argb, &rect, dest_left, dest_top, blend_type);
+    return SetDIBits(pSource, argb, rect, dest_left, dest_top, blend_type);
   }
   Flush();
   FX_RECT dest_rect(dest_left, dest_top, dest_left + dest_width,
@@ -2325,10 +2456,10 @@
   dest_clip.Intersect(*pClipRect);
   CFX_BitmapComposer composer;
   composer.Compose(m_pBitmap, m_pClipRgn.get(), 255, argb, dest_clip, false,
-                   false, false, m_bRgbByteOrder, 0, blend_type);
+                   false, false, m_bRgbByteOrder, blend_type);
   dest_clip.Offset(-dest_rect.left, -dest_rect.top);
   CFX_ImageStretcher stretcher(&composer, pSource, dest_width, dest_height,
-                               dest_clip, flags);
+                               dest_clip, options);
   if (stretcher.Start())
     stretcher.Continue(nullptr);
   return true;
@@ -2336,16 +2467,16 @@
 }
 
 bool CFX_SkiaDeviceDriver::StartDIBits(
-    const RetainPtr<CFX_DIBSource>& pSource,
+    const RetainPtr<CFX_DIBBase>& pSource,
     int bitmap_alpha,
     uint32_t argb,
-    const CFX_Matrix* pMatrix,
-    uint32_t render_flags,
+    const CFX_Matrix& matrix,
+    const FXDIB_ResampleOptions& options,
     std::unique_ptr<CFX_ImageRenderer>* handle,
-    int blend_type) {
+    BlendMode blend_type) {
 #ifdef _SKIA_SUPPORT_
   m_pCache->FlushForDraw();
-  DebugValidate(m_pBitmap, m_pOriDevice);
+  DebugValidate(m_pBitmap, m_pBackdropBitmap);
   std::unique_ptr<uint8_t, FxFreeDeleter> dst8Storage;
   std::unique_ptr<uint32_t, FxFreeDeleter> dst32Storage;
   SkBitmap skBitmap;
@@ -2354,32 +2485,33 @@
                 false)) {
     return false;
   }
-  m_pCanvas->save();
-  SkMatrix skMatrix;
-  SetBitmapMatrix(pMatrix, width, height, &skMatrix);
-  m_pCanvas->concat(skMatrix);
-  SkPaint paint;
-  SetBitmapPaint(pSource->IsAlphaMask(), argb, bitmap_alpha, blend_type,
-                 &paint);
-  // TODO(caryclark) Once Skia supports 8 bit src to 8 bit dst remove this
-  if (m_pBitmap && m_pBitmap->GetBPP() == 8 && pSource->GetBPP() == 8) {
-    SkMatrix inv;
-    SkAssertResult(skMatrix.invert(&inv));
-    for (int y = 0; y < m_pBitmap->GetHeight(); ++y) {
-      for (int x = 0; x < m_pBitmap->GetWidth(); ++x) {
-        SkPoint src = {x + 0.5f, y + 0.5f};
-        inv.mapPoints(&src, 1);
-        // TODO(caryclark) Why does the matrix map require clamping?
-        src.fX = SkTMax(0.5f, SkTMin(src.fX, width - 0.5f));
-        src.fY = SkTMax(0.5f, SkTMin(src.fY, height - 0.5f));
-        m_pBitmap->SetPixel(x, y, skBitmap.getColor(src.fX, src.fY));
+  {
+    SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
+    SkMatrix skMatrix;
+    SetBitmapMatrix(matrix, width, height, &skMatrix);
+    m_pCanvas->concat(skMatrix);
+    SkPaint paint;
+    SetBitmapPaint(pSource->IsAlphaMask(), argb, bitmap_alpha, blend_type,
+                   &paint);
+    // TODO(caryclark) Once Skia supports 8 bit src to 8 bit dst remove this
+    if (m_pBitmap && m_pBitmap->GetBPP() == 8 && pSource->GetBPP() == 8) {
+      SkMatrix inv;
+      SkAssertResult(skMatrix.invert(&inv));
+      for (int y = 0; y < m_pBitmap->GetHeight(); ++y) {
+        for (int x = 0; x < m_pBitmap->GetWidth(); ++x) {
+          SkPoint src = {x + 0.5f, y + 0.5f};
+          inv.mapPoints(&src, 1);
+          // TODO(caryclark) Why does the matrix map require clamping?
+          src.fX = SkTMax(0.5f, SkTMin(src.fX, width - 0.5f));
+          src.fY = SkTMax(0.5f, SkTMin(src.fY, height - 0.5f));
+          m_pBitmap->SetPixel(x, y, skBitmap.getColor(src.fX, src.fY));
+        }
       }
+    } else {
+      m_pCanvas->drawBitmap(skBitmap, 0, 0, &paint);
     }
-  } else {
-    m_pCanvas->drawBitmap(skBitmap, 0, 0, &paint);
   }
-  m_pCanvas->restore();
-  DebugValidate(m_pBitmap, m_pOriDevice);
+  DebugValidate(m_pBitmap, m_pBackdropBitmap);
 #endif  // _SKIA_SUPPORT_
 
 #ifdef _SKIA_SUPPORT_PATHS_
@@ -2388,14 +2520,14 @@
     return true;
   m_pBitmap->UnPreMultiply();
   *handle = pdfium::MakeUnique<CFX_ImageRenderer>(
-      m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, pMatrix,
-      render_flags, m_bRgbByteOrder);
+      m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, matrix, options,
+      m_bRgbByteOrder);
 #endif  // _SKIA_SUPPORT_PATHS_
   return true;
 }
 
 bool CFX_SkiaDeviceDriver::ContinueDIBits(CFX_ImageRenderer* handle,
-                                          IFX_PauseIndicator* pPause) {
+                                          PauseIndicatorIface* pPause) {
 #ifdef _SKIA_SUPPORT_
   m_pCache->FlushForDraw();
   return false;
@@ -2469,12 +2601,12 @@
 
 #ifdef _SKIA_SUPPORT_
 bool CFX_SkiaDeviceDriver::DrawBitsWithMask(
-    const RetainPtr<CFX_DIBSource>& pSource,
-    const RetainPtr<CFX_DIBSource>& pMask,
+    const RetainPtr<CFX_DIBBase>& pSource,
+    const RetainPtr<CFX_DIBBase>& pMask,
     int bitmap_alpha,
-    const CFX_Matrix* pMatrix,
-    int blend_type) {
-  DebugValidate(m_pBitmap, m_pOriDevice);
+    const CFX_Matrix& matrix,
+    BlendMode blend_type) {
+  DebugValidate(m_pBitmap, m_pBackdropBitmap);
   std::unique_ptr<uint8_t, FxFreeDeleter> src8Storage, mask8Storage;
   std::unique_ptr<uint32_t, FxFreeDeleter> src32Storage, mask32Storage;
   SkBitmap skBitmap, skMask;
@@ -2487,40 +2619,42 @@
                 &maskHeight, true)) {
     return false;
   }
-  m_pCanvas->save();
-  SkMatrix skMatrix;
-  SetBitmapMatrix(pMatrix, srcWidth, srcHeight, &skMatrix);
-  m_pCanvas->concat(skMatrix);
-  SkPaint paint;
-  SetBitmapPaint(pSource->IsAlphaMask(), 0xFFFFFFFF, bitmap_alpha, blend_type,
-                 &paint);
-  sk_sp<SkImage> skSrc = SkImage::MakeFromBitmap(skBitmap);
-  sk_sp<SkShader> skSrcShader =
-      skSrc->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-  sk_sp<SkImage> skMaskImage = SkImage::MakeFromBitmap(skMask);
-  sk_sp<SkShader> skMaskShader = skMaskImage->makeShader(
-      SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-  paint.setShader(SkShader::MakeComposeShader(skMaskShader, skSrcShader,
-                                              SkBlendMode::kSrcIn));
-  SkRect r = {0, 0, SkIntToScalar(srcWidth), SkIntToScalar(srcHeight)};
-  m_pCanvas->drawRect(r, paint);
-  m_pCanvas->restore();
-  DebugValidate(m_pBitmap, m_pOriDevice);
+  {
+    SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
+    SkMatrix skMatrix;
+    SetBitmapMatrix(matrix, srcWidth, srcHeight, &skMatrix);
+    m_pCanvas->concat(skMatrix);
+    SkPaint paint;
+    SetBitmapPaint(pSource->IsAlphaMask(), 0xFFFFFFFF, bitmap_alpha, blend_type,
+                   &paint);
+    sk_sp<SkImage> skSrc = SkImage::MakeFromBitmap(skBitmap);
+    sk_sp<SkShader> skSrcShader =
+        skSrc->makeShader(SkTileMode::kClamp, SkTileMode::kClamp);
+    sk_sp<SkImage> skMaskImage = SkImage::MakeFromBitmap(skMask);
+    sk_sp<SkShader> skMaskShader =
+        skMaskImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp);
+    paint.setShader(
+        SkShaders::Blend(SkBlendMode::kSrcIn, skMaskShader, skSrcShader));
+    SkRect r = {0, 0, SkIntToScalar(srcWidth), SkIntToScalar(srcHeight)};
+    m_pCanvas->drawRect(r, paint);
+  }
+  DebugValidate(m_pBitmap, m_pBackdropBitmap);
   return true;
 }
 
 bool CFX_SkiaDeviceDriver::SetBitsWithMask(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
-    const RetainPtr<CFX_DIBSource>& pMask,
+    const RetainPtr<CFX_DIBBase>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pMask,
     int dest_left,
     int dest_top,
     int bitmap_alpha,
-    int blend_type) {
+    BlendMode blend_type) {
   if (!m_pBitmap || !m_pBitmap->GetBuffer())
     return true;
-  CFX_Matrix m(pBitmap->GetWidth(), 0, 0, -pBitmap->GetHeight(), dest_left,
-               dest_top + pBitmap->GetHeight());
-  return DrawBitsWithMask(pBitmap, pMask, bitmap_alpha, &m, blend_type);
+
+  CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(
+      pBitmap->GetWidth(), pBitmap->GetHeight(), dest_left, dest_top);
+  return DrawBitsWithMask(pBitmap, pMask, bitmap_alpha, m, blend_type);
 }
 
 void CFX_SkiaDeviceDriver::Clear(uint32_t color) {
@@ -2537,8 +2671,8 @@
 
 #ifdef _SKIA_SUPPORT_
 void CFX_SkiaDeviceDriver::DebugVerifyBitmapIsPreMultiplied() const {
-  if (m_pOriDevice)
-    m_pOriDevice->DebugVerifyBitmapIsPreMultiplied(nullptr);
+  if (m_pBackdropBitmap)
+    m_pBackdropBitmap->DebugVerifyBitmapIsPreMultiplied(nullptr);
 }
 #endif  // _SKIA_SUPPORT_
 
@@ -2559,15 +2693,16 @@
 }
 #endif  // _SKIA_SUPPORT_
 
-bool CFX_DefaultRenderDevice::Attach(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                     bool bRgbByteOrder,
-                                     const RetainPtr<CFX_DIBitmap>& pOriDevice,
-                                     bool bGroupKnockout) {
+bool CFX_DefaultRenderDevice::Attach(
+    const RetainPtr<CFX_DIBitmap>& pBitmap,
+    bool bRgbByteOrder,
+    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
+    bool bGroupKnockout) {
   if (!pBitmap)
     return false;
   SetBitmap(pBitmap);
   SetDeviceDriver(pdfium::MakeUnique<CFX_SkiaDeviceDriver>(
-      pBitmap, bRgbByteOrder, pOriDevice, bGroupKnockout));
+      pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout));
   return true;
 }
 
@@ -2584,14 +2719,14 @@
     int width,
     int height,
     FXDIB_Format format,
-    const RetainPtr<CFX_DIBitmap>& pOriDevice) {
+    const RetainPtr<CFX_DIBitmap>& pBackdropBitmap) {
   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   if (!pBitmap->Create(width, height, format)) {
     return false;
   }
   SetBitmap(pBitmap);
-  SetDeviceDriver(pdfium::MakeUnique<CFX_SkiaDeviceDriver>(pBitmap, false,
-                                                           pOriDevice, false));
+  SetDeviceDriver(pdfium::MakeUnique<CFX_SkiaDeviceDriver>(
+      pBitmap, false, pBackdropBitmap, false));
   return true;
 }
 
@@ -2610,12 +2745,12 @@
 }
 
 bool CFX_DefaultRenderDevice::SetBitsWithMask(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
-    const RetainPtr<CFX_DIBSource>& pMask,
+    const RetainPtr<CFX_DIBBase>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pMask,
     int left,
     int top,
     int bitmap_alpha,
-    int blend_type) {
+    BlendMode blend_type) {
   CFX_SkiaDeviceDriver* skDriver =
       static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver());
   if (skDriver)
@@ -2625,9 +2760,9 @@
 }
 #endif  // _SKIA_SUPPORT_
 
-void CFX_DIBSource::DebugVerifyBitmapIsPreMultiplied(void* opt) const {
+void CFX_DIBBase::DebugVerifyBitmapIsPreMultiplied(void* opt) const {
 #ifdef SK_DEBUG
-  SkASSERT(32 == GetBPP());
+  ASSERT(GetBPP() == 32);
   const uint32_t* buffer = (const uint32_t*)(opt ? opt : GetBuffer());
   int width = GetWidth();
   int height = GetHeight();
@@ -2640,9 +2775,9 @@
       uint8_t g = SkGetPackedG32(srcRow[x]);
       uint8_t b = SkGetPackedB32(srcRow[x]);
       SkA32Assert(a);
-      SkASSERT(r <= a);
-      SkASSERT(g <= a);
-      SkASSERT(b <= a);
+      ASSERT(r <= a);
+      ASSERT(g <= a);
+      ASSERT(b <= a);
     }
   }
 #endif  // SK_DEBUG
diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h
index 7dfdef4..2571769 100644
--- a/core/fxge/skia/fx_skia_device.h
+++ b/core/fxge/skia/fx_skia_device.h
@@ -11,22 +11,23 @@
 #include <vector>
 
 #include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/ifx_renderdevicedriver.h"
+#include "core/fxge/renderdevicedriver_iface.h"
 
-class FXTEXT_CHARPOS;
+class CFX_ClipRgn;
 class SkCanvas;
 class SkMatrix;
 class SkPaint;
 class SkPath;
 class SkPictureRecorder;
 class SkiaState;
+class TextCharPos;
 struct SkIRect;
 
-class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver {
+class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface {
  public:
   CFX_SkiaDeviceDriver(const RetainPtr<CFX_DIBitmap>& pBitmap,
                        bool bRgbByteOrder,
-                       const RetainPtr<CFX_DIBitmap>& pOriDevice,
+                       const RetainPtr<CFX_DIBitmap>& pBackdropBitmap,
                        bool bGroupKnockout);
 #ifdef _SKIA_SUPPORT_
   explicit CFX_SkiaDeviceDriver(SkPictureRecorder* recorder);
@@ -35,6 +36,7 @@
   ~CFX_SkiaDeviceDriver() override;
 
   /** Options */
+  DeviceType GetDeviceType() const override;
   int GetDeviceCaps(int caps_id) const override;
 
   /** Save and restore all graphic states */
@@ -50,7 +52,7 @@
   /** Set clipping path using stroked region */
   bool SetClip_PathStroke(
       const CFX_PathData* pPathData,     // path info
-      const CFX_Matrix* pObject2Device,  // optional transformation
+      const CFX_Matrix* pObject2Device,  // required transformation
       const CFX_GraphStateData*
           pGraphState)  // graphic state, for pen attributes
       override;
@@ -62,17 +64,17 @@
                 uint32_t fill_color,
                 uint32_t stroke_color,
                 int fill_mode,
-                int blend_type) override;
+                BlendMode blend_type) override;
 
-  bool FillRectWithBlend(const FX_RECT* pRect,
+  bool FillRectWithBlend(const FX_RECT& rect,
                          uint32_t fill_color,
-                         int blend_type) override;
+                         BlendMode blend_type) override;
 
   /** Draw a single pixel (device dependant) line */
   bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
                         const CFX_PointF& ptLineTo,
                         uint32_t color,
-                        int blend_type) override;
+                        BlendMode blend_type) override;
 
   bool GetClipBox(FX_RECT* pRect) override;
 
@@ -83,59 +85,61 @@
 
   RetainPtr<CFX_DIBitmap> GetBackDrop() override;
 
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                  uint32_t color,
-                 const FX_RECT* pSrcRect,
+                 const FX_RECT& src_rect,
                  int dest_left,
                  int dest_top,
-                 int blend_type) override;
+                 BlendMode blend_type) override;
 #ifdef _SKIA_SUPPORT_
-  bool SetBitsWithMask(const RetainPtr<CFX_DIBSource>& pBitmap,
-                       const RetainPtr<CFX_DIBSource>& pMask,
+  bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                       const RetainPtr<CFX_DIBBase>& pMask,
                        int dest_left,
                        int dest_top,
                        int bitmap_alpha,
-                       int blend_type) override;
+                       BlendMode blend_type) override;
 #endif
 
 #ifdef _SKIA_SUPPORT_PATHS_
   void SetClipMask(const FX_RECT& clipBox, const SkPath& skClipPath);
 #endif
 
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      uint32_t color,
                      int dest_left,
                      int dest_top,
                      int dest_width,
                      int dest_height,
                      const FX_RECT* pClipRect,
-                     uint32_t flags,
-                     int blend_type) override;
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
 
-  bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                    int bitmap_alpha,
                    uint32_t color,
-                   const CFX_Matrix* pMatrix,
-                   uint32_t flags,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                   int blend_type) override;
+                   BlendMode blend_type) override;
 
   bool ContinueDIBits(CFX_ImageRenderer* handle,
-                      IFX_PauseIndicator* pPause) override;
+                      PauseIndicatorIface* pPause) override;
 
-  bool DrawBitsWithMask(const RetainPtr<CFX_DIBSource>& pBitmap,
-                        const RetainPtr<CFX_DIBSource>& pMask,
+  bool DrawBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                        const RetainPtr<CFX_DIBBase>& pMask,
                         int bitmap_alpha,
-                        const CFX_Matrix* pMatrix,
-                        int blend_type);
+                        const CFX_Matrix& matrix,
+                        BlendMode blend_type);
 
   bool DrawDeviceText(int nChars,
-                      const FXTEXT_CHARPOS* pCharPos,
+                      const TextCharPos* pCharPos,
                       CFX_Font* pFont,
-                      const CFX_Matrix* pObject2Device,
+                      const CFX_Matrix& mtObject2Device,
                       float font_size,
                       uint32_t color) override;
 
+  int GetDriverType() const override;
+
   bool DrawShading(const CPDF_ShadingPattern* pPattern,
                    const CFX_Matrix* pMatrix,
                    const FX_RECT& clip_rect,
@@ -158,9 +162,16 @@
 
   bool GetGroupKnockout() const { return m_bGroupKnockout; }
 
+#ifdef _SKIA_SUPPORT_PATHS_
+  const CFX_ClipRgn* clip_region() const { return m_pClipRgn.get(); }
+  const std::vector<std::unique_ptr<CFX_ClipRgn>>& stack() const {
+    return m_StateStack;
+  }
+#endif
+
  private:
   RetainPtr<CFX_DIBitmap> m_pBitmap;
-  RetainPtr<CFX_DIBitmap> m_pOriDevice;
+  RetainPtr<CFX_DIBitmap> m_pBackdropBitmap;
   SkCanvas* m_pCanvas;
   SkPictureRecorder* const m_pRecorder;
   std::unique_ptr<SkiaState> m_pCache;
diff --git a/core/fxge/skia/fx_skia_device_embeddertest.cpp b/core/fxge/skia/fx_skia_device_embeddertest.cpp
new file mode 100644
index 0000000..6631e4d
--- /dev/null
+++ b/core/fxge/skia/fx_skia_device_embeddertest.cpp
@@ -0,0 +1,174 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/skia/fx_skia_device.h"
+#include "core/fxge/text_char_pos.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdfview.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+
+namespace {
+
+struct State {
+  enum class Change { kNo, kYes };
+  enum class Save { kNo, kYes };
+  enum class Clip { kNo, kSame, kDifferentPath, kDifferentMatrix };
+  enum class Graphic { kNone, kPath, kText };
+
+  Change m_change;
+  Save m_save;
+  Clip m_clip;
+  Graphic m_graphic;
+  uint32_t m_pixel;
+};
+
+void EmptyTest(CFX_SkiaDeviceDriver* driver, const State&) {
+  driver->SaveState();
+  driver->RestoreState(true);
+  driver->RestoreState(false);
+}
+
+void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) {
+  TextCharPos charPos[1];
+  charPos[0].m_Origin = CFX_PointF(0, 1);
+  charPos[0].m_GlyphIndex = 1;
+  charPos[0].m_FontCharWidth = 4u;
+
+  CFX_Font font;
+  float fontSize = 1;
+  CFX_PathData clipPath, clipPath2;
+  clipPath.AppendRect(0, 0, 3, 1);
+  clipPath2.AppendRect(0, 0, 2, 1);
+  CFX_Matrix clipMatrix;
+  CFX_Matrix clipMatrix2(1, 0, 0, 1, 0, 1);
+  driver->SaveState();
+  CFX_PathData path1;
+  path1.AppendRect(0, 0, 1, 2);
+
+  CFX_Matrix matrix;
+  CFX_Matrix matrix2;
+  matrix2.Translate(1, 0);
+  CFX_GraphStateData graphState;
+  if (state.m_save == State::Save::kYes)
+    driver->SaveState();
+  if (state.m_clip != State::Clip::kNo)
+    driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  if (state.m_graphic == State::Graphic::kPath) {
+    driver->DrawPath(&path1, &matrix, &graphState, 0xFF112233, 0,
+                     FXFILL_WINDING, BlendMode::kNormal);
+  } else if (state.m_graphic == State::Graphic::kText) {
+    driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix,
+                           fontSize, 0xFF445566);
+  }
+  if (state.m_save == State::Save::kYes)
+    driver->RestoreState(true);
+  CFX_PathData path2;
+  path2.AppendRect(0, 0, 2, 2);
+  if (state.m_change == State::Change::kYes) {
+    if (state.m_graphic == State::Graphic::kPath)
+      graphState.m_LineCap = CFX_GraphStateData::LineCapRound;
+    else if (state.m_graphic == State::Graphic::kText)
+      fontSize = 2;
+  }
+  if (state.m_clip == State::Clip::kSame)
+    driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  else if (state.m_clip == State::Clip::kDifferentPath)
+    driver->SetClip_PathFill(&clipPath2, &clipMatrix, 0);
+  else if (state.m_clip == State::Clip::kDifferentMatrix)
+    driver->SetClip_PathFill(&clipPath, &clipMatrix2, 0);
+  if (state.m_graphic == State::Graphic::kPath) {
+    driver->DrawPath(&path2, &matrix2, &graphState, 0xFF112233, 0,
+                     FXFILL_WINDING, BlendMode::kNormal);
+  } else if (state.m_graphic == State::Graphic::kText) {
+    driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix2,
+                           fontSize, 0xFF445566);
+  }
+  if (state.m_save == State::Save::kYes)
+    driver->RestoreState(false);
+  driver->RestoreState(false);
+}
+
+void OutOfSequenceClipTest(CFX_SkiaDeviceDriver* driver, const State&) {
+  CFX_PathData clipPath;
+  clipPath.AppendRect(1, 0, 3, 1);
+  CFX_Matrix clipMatrix;
+  driver->SaveState();
+  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->RestoreState(true);
+  driver->SaveState();
+  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->RestoreState(false);
+  driver->RestoreState(false);
+
+  driver->SaveState();
+  driver->SaveState();
+  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->RestoreState(true);
+  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
+  driver->RestoreState(false);
+  driver->RestoreState(false);
+}
+
+void Harness(void (*Test)(CFX_SkiaDeviceDriver*, const State&),
+             const State& state) {
+  constexpr int kWidth = 4;
+  constexpr int kHeight = 1;
+  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(kWidth, kHeight, 1));
+  ASSERT_TRUE(bitmap);
+  FPDFBitmap_FillRect(bitmap.get(), 0, 0, kWidth, kHeight, 0x00000000);
+  CFX_DefaultRenderDevice device;
+  RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap.get()));
+  device.Attach(pBitmap, false, nullptr, false);
+  auto* driver = static_cast<CFX_SkiaDeviceDriver*>(device.GetDeviceDriver());
+  (*Test)(driver, state);
+  driver->Flush();
+  uint32_t pixel = pBitmap->GetPixel(0, 0);
+  EXPECT_EQ(state.m_pixel, pixel);
+#ifdef SK_DEBUG
+  if (!driver)  // force dump to be linked in so it can be called from debugger
+    driver->Dump();
+#endif
+}
+
+}  // namespace
+
+TEST(fxge, SkiaStateEmpty) {
+  Harness(&EmptyTest, {});
+}
+
+TEST(fxge, SkiaStatePath) {
+  Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
+                        State::Clip::kSame, State::Graphic::kPath, 0xFF112233});
+  Harness(&CommonTest,
+          {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentPath,
+           State::Graphic::kPath, 0xFF112233});
+  Harness(&CommonTest, {State::Change::kNo, State::Save::kYes, State::Clip::kNo,
+                        State::Graphic::kPath, 0xFF112233});
+  Harness(&CommonTest, {State::Change::kYes, State::Save::kNo, State::Clip::kNo,
+                        State::Graphic::kPath, 0xFF112233});
+  Harness(&CommonTest, {State::Change::kNo, State::Save::kNo, State::Clip::kNo,
+                        State::Graphic::kPath, 0xFF112233});
+}
+
+#ifdef _SKIA_SUPPORT_
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+TEST(fxge, DISABLED_SkiaStateText) {
+  Harness(&CommonTest,
+          {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentMatrix,
+           State::Graphic::kText, 0xFF445566});
+  Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
+                        State::Clip::kSame, State::Graphic::kText, 0xFF445566});
+}
+#endif
+
+TEST(fxge, SkiaStateOOSClip) {
+  Harness(&OutOfSequenceClipTest, {});
+}
diff --git a/core/fxge/skia/fx_skia_device_unittest.cpp b/core/fxge/skia/fx_skia_device_unittest.cpp
deleted file mode 100644
index 7cb28cf..0000000
--- a/core/fxge/skia/fx_skia_device_unittest.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxge/skia/fx_skia_device.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "public/fpdfview.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
-
-namespace {
-
-struct State {
-  enum class Change { kNo, kYes };
-  enum class Save { kNo, kYes };
-  enum class Clip { kNo, kSame, kDifferentPath, kDifferentMatrix };
-  enum class Graphic { kNone, kPath, kText };
-
-  Change m_change;
-  Save m_save;
-  Clip m_clip;
-  Graphic m_graphic;
-  uint32_t m_pixel;
-};
-
-void EmptyTest(CFX_SkiaDeviceDriver* driver, const State&) {
-  driver->SaveState();
-  driver->RestoreState(true);
-  driver->RestoreState(false);
-}
-
-void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) {
-  FXTEXT_CHARPOS charPos[1];
-  charPos[0].m_Origin = CFX_PointF(0, 1);
-  charPos[0].m_GlyphIndex = 1;
-  charPos[0].m_FontCharWidth = 4;
-
-  CFX_Font font;
-  float fontSize = 1;
-  CFX_PathData clipPath, clipPath2;
-  clipPath.AppendRect(0, 0, 3, 1);
-  clipPath2.AppendRect(0, 0, 2, 1);
-  CFX_Matrix clipMatrix;
-  CFX_Matrix clipMatrix2(1, 0, 0, 1, 0, 1);
-  driver->SaveState();
-  CFX_PathData path1;
-  path1.AppendRect(0, 0, 1, 2);
-
-  CFX_Matrix matrix;
-  CFX_Matrix matrix2;
-  matrix2.Translate(1, 0);
-  CFX_GraphStateData graphState;
-  if (state.m_save == State::Save::kYes)
-    driver->SaveState();
-  if (state.m_clip != State::Clip::kNo)
-    driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
-  if (state.m_graphic == State::Graphic::kPath) {
-    driver->DrawPath(&path1, &matrix, &graphState, 0xFF112233, 0,
-                     FXFILL_WINDING, 0);
-  } else if (state.m_graphic == State::Graphic::kText) {
-    driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, &matrix,
-                           fontSize, 0xFF445566);
-  }
-  if (state.m_save == State::Save::kYes)
-    driver->RestoreState(true);
-  CFX_PathData path2;
-  path2.AppendRect(0, 0, 2, 2);
-  if (state.m_change == State::Change::kYes) {
-    if (state.m_graphic == State::Graphic::kPath)
-      graphState.m_LineCap = CFX_GraphStateData::LineCapRound;
-    else if (state.m_graphic == State::Graphic::kText)
-      fontSize = 2;
-  }
-  if (state.m_clip == State::Clip::kSame)
-    driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
-  else if (state.m_clip == State::Clip::kDifferentPath)
-    driver->SetClip_PathFill(&clipPath2, &clipMatrix, 0);
-  else if (state.m_clip == State::Clip::kDifferentMatrix)
-    driver->SetClip_PathFill(&clipPath, &clipMatrix2, 0);
-  if (state.m_graphic == State::Graphic::kPath) {
-    driver->DrawPath(&path2, &matrix2, &graphState, 0xFF112233, 0,
-                     FXFILL_WINDING, 0);
-  } else if (state.m_graphic == State::Graphic::kText) {
-    driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, &matrix2,
-                           fontSize, 0xFF445566);
-  }
-  if (state.m_save == State::Save::kYes)
-    driver->RestoreState(false);
-  driver->RestoreState(false);
-}
-
-void OutOfSequenceClipTest(CFX_SkiaDeviceDriver* driver, const State&) {
-  CFX_PathData clipPath;
-  clipPath.AppendRect(1, 0, 3, 1);
-  CFX_Matrix clipMatrix;
-  driver->SaveState();
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
-  driver->RestoreState(true);
-  driver->SaveState();
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
-  driver->RestoreState(false);
-  driver->RestoreState(false);
-
-  driver->SaveState();
-  driver->SaveState();
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
-  driver->RestoreState(true);
-  driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
-  driver->RestoreState(false);
-  driver->RestoreState(false);
-}
-
-void Harness(void (*Test)(CFX_SkiaDeviceDriver*, const State&),
-             const State& state) {
-  int h = 1;
-  int w = 4;
-  FPDF_BITMAP bitmap = FPDFBitmap_Create(w, h, 1);
-  EXPECT_NE(nullptr, bitmap);
-  if (!bitmap)
-    return;
-  FPDFBitmap_FillRect(bitmap, 0, 0, w, h, 0x00000000);
-  CFX_DefaultRenderDevice geDevice;
-  RetainPtr<CFX_DIBitmap> pBitmap(CFXBitmapFromFPDFBitmap(bitmap));
-  geDevice.Attach(pBitmap, false, nullptr, false);
-  CFX_SkiaDeviceDriver* driver =
-      static_cast<CFX_SkiaDeviceDriver*>(geDevice.GetDeviceDriver());
-  (*Test)(driver, state);
-  driver->Flush();
-  uint32_t pixel = pBitmap->GetPixel(0, 0);
-  EXPECT_EQ(state.m_pixel, pixel);
-#ifdef SK_DEBUG
-  if (!driver)  // force dump to be linked in so it can be called from debugger
-    driver->Dump();
-#endif
-}
-
-}  // namespace
-
-TEST(fxge, SkiaStateEmpty) {
-  Harness(&EmptyTest, {});
-}
-
-TEST(fxge, SkiaStatePath) {
-  Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
-                        State::Clip::kSame, State::Graphic::kPath, 0xFF112233});
-  Harness(&CommonTest,
-          {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentPath,
-           State::Graphic::kPath, 0xFF112233});
-  Harness(&CommonTest, {State::Change::kNo, State::Save::kYes, State::Clip::kNo,
-                        State::Graphic::kPath, 0xFF112233});
-  Harness(&CommonTest, {State::Change::kYes, State::Save::kNo, State::Clip::kNo,
-                        State::Graphic::kPath, 0xFF112233});
-  Harness(&CommonTest, {State::Change::kNo, State::Save::kNo, State::Clip::kNo,
-                        State::Graphic::kPath, 0xFF112233});
-}
-
-#ifdef _SKIA_SUPPORT_
-TEST(fxge, SkiaStateText) {
-  Harness(&CommonTest,
-          {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentMatrix,
-           State::Graphic::kText, 0xFF445566});
-  Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
-                        State::Clip::kSame, State::Graphic::kText, 0xFF445566});
-}
-#endif
-
-TEST(fxge, SkiaStateOOSClip) {
-  Harness(&OutOfSequenceClipTest, {});
-}
diff --git a/core/fxge/systemfontinfo_iface.h b/core/fxge/systemfontinfo_iface.h
new file mode 100644
index 0000000..9a6fbc7
--- /dev/null
+++ b/core/fxge/systemfontinfo_iface.h
@@ -0,0 +1,41 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_SYSTEMFONTINFO_IFACE_H_
+#define CORE_FXGE_SYSTEMFONTINFO_IFACE_H_
+
+#include <memory>
+
+#include "core/fxge/cfx_fontmapper.h"
+#include "third_party/base/span.h"
+
+constexpr uint32_t kTableNAME = CFX_FontMapper::MakeTag('n', 'a', 'm', 'e');
+constexpr uint32_t kTableTTCF = CFX_FontMapper::MakeTag('t', 't', 'c', 'f');
+
+class SystemFontInfoIface {
+ public:
+  static std::unique_ptr<SystemFontInfoIface> CreateDefault(
+      const char** pUserPaths);
+
+  virtual ~SystemFontInfoIface() = default;
+
+  virtual bool EnumFontList(CFX_FontMapper* pMapper) = 0;
+  virtual void* MapFont(int weight,
+                        bool bItalic,
+                        int charset,
+                        int pitch_family,
+                        const char* face) = 0;
+  virtual void* GetFont(const char* face) = 0;
+  virtual uint32_t GetFontData(void* hFont,
+                               uint32_t table,
+                               pdfium::span<uint8_t> buffer) = 0;
+  virtual bool GetFaceName(void* hFont, ByteString* name) = 0;
+  virtual bool GetFontCharset(void* hFont, int* charset) = 0;
+  virtual int GetFaceIndex(void* hFont);
+  virtual void DeleteFont(void* hFont) = 0;
+};
+
+#endif  // CORE_FXGE_SYSTEMFONTINFO_IFACE_H_
diff --git a/core/fxge/text_char_pos.cpp b/core/fxge/text_char_pos.cpp
new file mode 100644
index 0000000..cf88b96
--- /dev/null
+++ b/core/fxge/text_char_pos.cpp
@@ -0,0 +1,13 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/text_char_pos.h"
+
+TextCharPos::TextCharPos() = default;
+
+TextCharPos::TextCharPos(const TextCharPos&) = default;
+
+TextCharPos::~TextCharPos() = default;
diff --git a/core/fxge/text_char_pos.h b/core/fxge/text_char_pos.h
new file mode 100644
index 0000000..c211be8
--- /dev/null
+++ b/core/fxge/text_char_pos.h
@@ -0,0 +1,31 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_TEXT_CHAR_POS_H_
+#define CORE_FXGE_TEXT_CHAR_POS_H_
+
+#include "core/fxcrt/fx_coordinates.h"
+
+class TextCharPos {
+ public:
+  TextCharPos();
+  TextCharPos(const TextCharPos&);
+  ~TextCharPos();
+
+  CFX_PointF m_Origin;
+  uint32_t m_Unicode = 0;
+  uint32_t m_GlyphIndex = 0;
+  uint32_t m_FontCharWidth = 0;
+#if defined(OS_MACOSX)
+  uint32_t m_ExtGID = 0;
+#endif
+  int32_t m_FallbackFontPosition = 0;
+  bool m_bGlyphAdjust = false;
+  bool m_bFontStyle = false;
+  float m_AdjustMatrix[4];
+};
+
+#endif  // CORE_FXGE_TEXT_CHAR_POS_H_
diff --git a/core/fxge/text_glyph_pos.cpp b/core/fxge/text_glyph_pos.cpp
new file mode 100644
index 0000000..3e3c4ad
--- /dev/null
+++ b/core/fxge/text_glyph_pos.cpp
@@ -0,0 +1,32 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fxge/text_glyph_pos.h"
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/cfx_glyphbitmap.h"
+
+TextGlyphPos::TextGlyphPos() = default;
+
+TextGlyphPos::TextGlyphPos(const TextGlyphPos&) = default;
+
+TextGlyphPos::~TextGlyphPos() = default;
+
+Optional<CFX_Point> TextGlyphPos::GetOrigin(const CFX_Point& offset) const {
+  FX_SAFE_INT32 left = m_Origin.x;
+  left += m_pGlyph->left();
+  left -= offset.x;
+  if (!left.IsValid())
+    return {};
+
+  FX_SAFE_INT32 top = m_Origin.y;
+  top -= m_pGlyph->top();
+  top -= offset.y;
+  if (!top.IsValid())
+    return {};
+
+  return CFX_Point(left.ValueOrDie(), top.ValueOrDie());
+}
diff --git a/core/fxge/text_glyph_pos.h b/core/fxge/text_glyph_pos.h
new file mode 100644
index 0000000..b786432
--- /dev/null
+++ b/core/fxge/text_glyph_pos.h
@@ -0,0 +1,30 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXGE_TEXT_GLYPH_POS_H_
+#define CORE_FXGE_TEXT_GLYPH_POS_H_
+
+#include "core/fxcrt/fx_coordinates.h"
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
+
+class CFX_GlyphBitmap;
+
+class TextGlyphPos {
+ public:
+  TextGlyphPos();
+  TextGlyphPos(const TextGlyphPos&);
+  ~TextGlyphPos();
+
+  Optional<CFX_Point> GetOrigin(const CFX_Point& offset) const;
+
+  UnownedPtr<const CFX_GlyphBitmap> m_pGlyph;
+  CFX_Point m_Origin;
+  CFX_PointF m_fOrigin;
+};
+
+#endif  // CORE_FXGE_TEXT_GLYPH_POS_H_
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
index 1513c07..f9f7de2 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -6,79 +6,24 @@
 
 #include "core/fxge/win32/cfx_psrenderer.h"
 
+#include <algorithm>
 #include <memory>
 #include <sstream>
+#include <utility>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fxcodec/codec/ccodec_basicmodule.h"
-#include "core/fxcodec/codec/ccodec_faxmodule.h"
-#include "core/fxcodec/codec/ccodec_flatemodule.h"
-#include "core/fxcodec/codec/ccodec_jpegmodule.h"
-#include "core/fxcodec/fx_codec.h"
 #include "core/fxcrt/maybe_owned.h"
-#include "core/fxge/cfx_facecache.h"
 #include "core/fxge/cfx_fontcache.h"
 #include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_glyphcache.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/cfx_dibextractor.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_dib.h"
+#include "core/fxge/text_char_pos.h"
 #include "core/fxge/win32/cpsoutput.h"
 #include "third_party/base/ptr_util.h"
 
-namespace {
-
-void FaxCompressData(uint8_t* src_buf,
-                     int width,
-                     int height,
-                     std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
-                     uint32_t* dest_size) {
-  if (width * height > 128) {
-    CCodec_FaxModule::FaxEncode(src_buf, width, height, (width + 7) / 8,
-                                dest_buf, dest_size);
-    FX_Free(src_buf);
-  } else {
-    dest_buf->reset(src_buf);
-    *dest_size = (width + 7) / 8 * height;
-  }
-}
-
-void PSCompressData(int PSLevel,
-                    uint8_t* src_buf,
-                    uint32_t src_size,
-                    uint8_t** output_buf,
-                    uint32_t* output_size,
-                    const char** filter) {
-  *output_buf = src_buf;
-  *output_size = src_size;
-  *filter = "";
-  if (src_size < 1024)
-    return;
-
-  CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
-  uint8_t* dest_buf = nullptr;
-  uint32_t dest_size = src_size;
-  if (PSLevel >= 3) {
-    if (pEncoders->GetFlateModule()->Encode(src_buf, src_size, &dest_buf,
-                                            &dest_size)) {
-      *filter = "/FlateDecode filter ";
-    }
-  } else {
-    if (pEncoders->GetBasicModule()->RunLengthEncode(src_buf, src_size,
-                                                     &dest_buf, &dest_size)) {
-      *filter = "/RunLengthDecode filter ";
-    }
-  }
-  if (dest_size < src_size) {
-    *output_buf = dest_buf;
-    *output_size = dest_size;
-  } else {
-    *filter = nullptr;
-    FX_Free(dest_buf);
-  }
-}
-
-}  // namespace
-
 struct PSGlyph {
   UnownedPtr<CFX_Font> m_pFont;
   uint32_t m_GlyphIndex;
@@ -88,19 +33,16 @@
 
 class CPSFont {
  public:
-  PSGlyph m_Glyphs[256];
   int m_nGlyphs;
+  PSGlyph m_Glyphs[256];
 };
 
-CFX_PSRenderer::CFX_PSRenderer()
-    : m_pStream(nullptr),
-      m_bGraphStateSet(false),
-      m_bColorSet(false),
-      m_bInited(false) {}
+CFX_PSRenderer::CFX_PSRenderer(const EncoderIface* pEncoderIface)
+    : m_pEncoderIface(pEncoderIface) {}
 
-CFX_PSRenderer::~CFX_PSRenderer() {}
+CFX_PSRenderer::~CFX_PSRenderer() = default;
 
-void CFX_PSRenderer::Init(const RetainPtr<IFX_WriteStream>& pStream,
+void CFX_PSRenderer::Init(const RetainPtr<IFX_RetainableWriteStream>& pStream,
                           int pslevel,
                           int width,
                           int height,
@@ -206,7 +148,7 @@
       }
     }
   }
-  m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+  WriteToStream(&buf);
 }
 
 void CFX_PSRenderer::SetClip_PathFill(const CFX_PathData* pPathData,
@@ -234,23 +176,19 @@
                                         const CFX_GraphStateData* pGraphState) {
   StartRendering();
   SetGraphState(pGraphState);
-  if (pObject2Device) {
-    std::ostringstream buf;
-    buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
-        << pObject2Device->c << " " << pObject2Device->d << " "
-        << pObject2Device->e << " " << pObject2Device->f << "]cm ";
-    m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
-  }
+
+  std::ostringstream buf;
+  buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
+      << pObject2Device->c << " " << pObject2Device->d << " "
+      << pObject2Device->e << " " << pObject2Device->f << "]cm ";
+  WriteToStream(&buf);
 
   OutputPath(pPathData, nullptr);
   CFX_FloatRect rect = pPathData->GetBoundingBox(pGraphState->m_LineWidth,
                                                  pGraphState->m_MiterLimit);
   m_ClipBox.Intersect(pObject2Device->TransformRect(rect).GetOuterRect());
 
-  m_pStream->WriteString("strokepath W n");
-  if (pObject2Device)
-    m_pStream->WriteString(" sm");
-  m_pStream->WriteString("\n");
+  m_pStream->WriteString("strokepath W n sm\n");
 }
 
 bool CFX_PSRenderer::DrawPath(const CFX_PathData* pPathData,
@@ -276,7 +214,7 @@
       buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
           << pObject2Device->c << " " << pObject2Device->d << " "
           << pObject2Device->e << " " << pObject2Device->f << "]cm ";
-      m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+      WriteToStream(&buf);
     }
   }
 
@@ -311,21 +249,18 @@
   std::ostringstream buf;
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_LineCap != pGraphState->m_LineCap) {
-    buf << pGraphState->m_LineCap << " J\n";
+    buf << static_cast<int>(pGraphState->m_LineCap) << " J\n";
   }
   if (!m_bGraphStateSet ||
-      m_CurGraphState.m_DashCount != pGraphState->m_DashCount ||
-      memcmp(m_CurGraphState.m_DashArray, pGraphState->m_DashArray,
-             sizeof(float) * m_CurGraphState.m_DashCount)) {
+      m_CurGraphState.m_DashArray != pGraphState->m_DashArray) {
     buf << "[";
-    for (int i = 0; i < pGraphState->m_DashCount; ++i)
-      buf << pGraphState->m_DashArray[i] << " ";
-
+    for (const auto& dash : pGraphState->m_DashArray)
+      buf << dash << " ";
     buf << "]" << pGraphState->m_DashPhase << " d\n";
   }
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_LineJoin != pGraphState->m_LineJoin) {
-    buf << pGraphState->m_LineJoin << " j\n";
+    buf << static_cast<int>(pGraphState->m_LineJoin) << " j\n";
   }
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_LineWidth != pGraphState->m_LineWidth) {
@@ -335,45 +270,42 @@
       m_CurGraphState.m_MiterLimit != pGraphState->m_MiterLimit) {
     buf << pGraphState->m_MiterLimit << " M\n";
   }
-  m_CurGraphState.Copy(*pGraphState);
+  m_CurGraphState = *pGraphState;
   m_bGraphStateSet = true;
-  if (buf.tellp())
-    m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+  WriteToStream(&buf);
 }
 
-bool CFX_PSRenderer::SetDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CFX_PSRenderer::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                uint32_t color,
                                int left,
                                int top) {
   StartRendering();
-  CFX_Matrix matrix((float)(pSource->GetWidth()), 0.0f, 0.0f,
-                    -(float)(pSource->GetHeight()), (float)(left),
-                    (float)(top + pSource->GetHeight()));
-  return DrawDIBits(pSource, color, &matrix, 0);
+  CFX_Matrix matrix = CFX_RenderDevice::GetFlipMatrix(
+      pSource->GetWidth(), pSource->GetHeight(), left, top);
+  return DrawDIBits(pSource, color, matrix, FXDIB_ResampleOptions());
 }
 
-bool CFX_PSRenderer::StretchDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CFX_PSRenderer::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                    uint32_t color,
                                    int dest_left,
                                    int dest_top,
                                    int dest_width,
                                    int dest_height,
-                                   uint32_t flags) {
+                                   const FXDIB_ResampleOptions& options) {
   StartRendering();
-  CFX_Matrix matrix((float)(dest_width), 0.0f, 0.0f, (float)(-dest_height),
-                    (float)(dest_left), (float)(dest_top + dest_height));
-  return DrawDIBits(pSource, color, &matrix, flags);
+  CFX_Matrix matrix = CFX_RenderDevice::GetFlipMatrix(dest_width, dest_height,
+                                                      dest_left, dest_top);
+  return DrawDIBits(pSource, color, matrix, options);
 }
 
-bool CFX_PSRenderer::DrawDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CFX_PSRenderer::DrawDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                 uint32_t color,
-                                const CFX_Matrix* pMatrix,
-                                uint32_t flags) {
+                                const CFX_Matrix& matrix,
+                                const FXDIB_ResampleOptions& options) {
   StartRendering();
-  if ((pMatrix->a == 0 && pMatrix->b == 0) ||
-      (pMatrix->c == 0 && pMatrix->d == 0)) {
+  if ((matrix.a == 0 && matrix.b == 0) || (matrix.c == 0 && matrix.d == 0))
     return true;
-  }
+
   if (pSource->HasAlpha())
     return false;
 
@@ -384,8 +316,8 @@
   m_pStream->WriteString("q\n");
 
   std::ostringstream buf;
-  buf << "[" << pMatrix->a << " " << pMatrix->b << " " << pMatrix->c << " "
-      << pMatrix->d << " " << pMatrix->e << " " << pMatrix->f << "]cm ";
+  buf << "[" << matrix.a << " " << matrix.b << " " << matrix.c << " "
+      << matrix.d << " " << matrix.e << " " << matrix.f << "]cm ";
 
   int width = pSource->GetWidth();
   int height = pSource->GetHeight();
@@ -394,15 +326,17 @@
   if (pSource->GetBPP() == 1 && !pSource->GetPalette()) {
     int pitch = (width + 7) / 8;
     uint32_t src_size = height * pitch;
-    uint8_t* src_buf = FX_Alloc(uint8_t, src_size);
+    std::unique_ptr<uint8_t, FxFreeDeleter> src_buf(
+        FX_Alloc(uint8_t, src_size));
     for (int row = 0; row < height; row++) {
       const uint8_t* src_scan = pSource->GetScanline(row);
-      memcpy(src_buf + row * pitch, src_scan, pitch);
+      memcpy(src_buf.get() + row * pitch, src_scan, pitch);
     }
 
     std::unique_ptr<uint8_t, FxFreeDeleter> output_buf;
     uint32_t output_size;
-    FaxCompressData(src_buf, width, height, &output_buf, &output_size);
+    bool compressed = FaxCompressData(std::move(src_buf), width, height,
+                                      &output_buf, &output_size);
     if (pSource->IsAlphaMask()) {
       SetColor(color);
       m_bColorSet = false;
@@ -413,7 +347,7 @@
     buf << width << " 0 0 -" << height << " 0 " << height
         << "]currentfile/ASCII85Decode filter ";
 
-    if (output_buf.get() != src_buf) {
+    if (compressed) {
       buf << "<</K -1/EndOfBlock false/Columns " << width << "/Rows " << height
           << ">>/CCITTFaxDecode filter ";
     }
@@ -422,12 +356,11 @@
     else
       buf << "false 1 colorimage\n";
 
-    m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+    WriteToStream(&buf);
     WritePSBinary(output_buf.get(), output_size);
-    output_buf.release();
   } else {
     CFX_DIBExtractor source_extractor(pSource);
-    RetainPtr<CFX_DIBSource> pConverted = source_extractor.GetBitmap();
+    RetainPtr<CFX_DIBBase> pConverted = source_extractor.GetBitmap();
     if (!pConverted)
       return false;
     switch (pSource->GetFormat()) {
@@ -460,8 +393,9 @@
     uint8_t* output_buf = nullptr;
     size_t output_size = 0;
     const char* filter = nullptr;
-    if ((m_PSLevel == 2 || flags & FXRENDER_IMAGE_LOSSY) &&
-        CCodec_JpegModule::JpegEncode(pConverted, &output_buf, &output_size)) {
+    if ((m_PSLevel == 2 || options.bLossy) &&
+        m_pEncoderIface->pJpegEncodeFunc(pConverted, &output_buf,
+                                         &output_size)) {
       filter = "/DCTDecode filter ";
     }
     if (!filter) {
@@ -484,8 +418,8 @@
       }
       uint8_t* compressed_buf;
       uint32_t compressed_size;
-      PSCompressData(m_PSLevel, output_buf, output_size, &compressed_buf,
-                     &compressed_size, &filter);
+      PSCompressData(output_buf, output_size, &compressed_buf, &compressed_size,
+                     &filter);
       if (output_buf != compressed_buf)
         FX_Free(output_buf);
 
@@ -500,7 +434,7 @@
 
     buf << "false " << bpp;
     buf << " colorimage\n";
-    m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+    WriteToStream(&buf);
 
     WritePSBinary(output_buf, output_size);
     FX_Free(output_buf);
@@ -526,13 +460,13 @@
       m_bColorSet = true;
       m_LastColor = color;
     }
-    m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+    WriteToStream(&buf);
   }
 }
 
-void CFX_PSRenderer::FindPSFontGlyph(CFX_FaceCache* pFaceCache,
+void CFX_PSRenderer::FindPSFontGlyph(CFX_GlyphCache* pGlyphCache,
                                      CFX_Font* pFont,
-                                     const FXTEXT_CHARPOS& charpos,
+                                     const TextCharPos& charpos,
                                      int* ps_fontnum,
                                      int* ps_glyphindex) {
   int i = 0;
@@ -573,7 +507,7 @@
            "currentdict end\n";
     buf << "/X" << static_cast<uint32_t>(m_PSFontList.size() - 1)
         << " exch definefont pop\n";
-    m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+    WriteToStream(&buf);
     buf.str("");
   }
 
@@ -598,15 +532,14 @@
         CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
                    charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0);
   }
-  matrix.Concat(CFX_Matrix(1.0f, 0, 0, 1.0f, 0, 0));
-  const CFX_PathData* pPathData = pFaceCache->LoadGlyphPath(
+  const CFX_PathData* pPathData = pGlyphCache->LoadGlyphPath(
       pFont, charpos.m_GlyphIndex, charpos.m_FontCharWidth);
   if (!pPathData)
     return;
 
   CFX_PathData TransformedPath(*pPathData);
   if (charpos.m_bGlyphAdjust)
-    TransformedPath.Transform(&matrix);
+    TransformedPath.Transform(matrix);
 
   std::ostringstream buf;
   buf << "/X" << *ps_fontnum << " Ff/CharProcs get begin/" << glyphindex
@@ -635,24 +568,28 @@
   buf << "f}bind def end\n";
   buf << "/X" << *ps_fontnum << " Ff/Encoding get " << glyphindex << "/"
       << glyphindex << " put\n";
-  m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
+  WriteToStream(&buf);
 }
 
 bool CFX_PSRenderer::DrawText(int nChars,
-                              const FXTEXT_CHARPOS* pCharPos,
+                              const TextCharPos* pCharPos,
                               CFX_Font* pFont,
-                              const CFX_Matrix* pObject2Device,
+                              const CFX_Matrix& mtObject2Device,
                               float font_size,
                               uint32_t color) {
-  // Do not send zero or negative font sizes to printers. See crbug.com/767343.
-  if (font_size <= 0.0)
-    return true;
-
-  if ((pObject2Device->a == 0 && pObject2Device->b == 0) ||
-      (pObject2Device->c == 0 && pObject2Device->d == 0)) {
+  // Check object to device matrix first, since it can scale the font size.
+  if ((mtObject2Device.a == 0 && mtObject2Device.b == 0) ||
+      (mtObject2Device.c == 0 && mtObject2Device.d == 0)) {
     return true;
   }
 
+  // Do not send near zero font sizes to printers. See crbug.com/767343.
+  float scale =
+      std::min(mtObject2Device.GetXUnit(), mtObject2Device.GetYUnit());
+  static constexpr float kEpsilon = 0.01f;
+  if (std::fabs(font_size * scale) < kEpsilon)
+    return true;
+
   StartRendering();
   int alpha = FXARGB_A(color);
   if (alpha < 255)
@@ -660,16 +597,16 @@
 
   SetColor(color);
   std::ostringstream buf;
-  buf << "q[" << pObject2Device->a << " " << pObject2Device->b << " "
-      << pObject2Device->c << " " << pObject2Device->d << " "
-      << pObject2Device->e << " " << pObject2Device->f << "]cm\n";
+  buf << "q[" << mtObject2Device.a << " " << mtObject2Device.b << " "
+      << mtObject2Device.c << " " << mtObject2Device.d << " "
+      << mtObject2Device.e << " " << mtObject2Device.f << "]cm\n";
 
   CFX_FontCache* pCache = CFX_GEModule::Get()->GetFontCache();
-  CFX_FaceCache* pFaceCache = pCache->GetCachedFace(pFont);
+  RetainPtr<CFX_GlyphCache> pGlyphCache = pCache->GetGlyphCache(pFont);
   int last_fontnum = -1;
   for (int i = 0; i < nChars; i++) {
     int ps_fontnum, ps_glyphindex;
-    FindPSFontGlyph(pFaceCache, pFont, pCharPos[i], &ps_fontnum,
+    FindPSFontGlyph(pGlyphCache.Get(), pFont, pCharPos[i], &ps_fontnum,
                     &ps_glyphindex);
     if (last_fontnum != ps_fontnum) {
       buf << "/X" << ps_fontnum << " Ff " << font_size << " Fs Sf ";
@@ -680,20 +617,76 @@
     buf << hex.AsStringView() << "Tj\n";
   }
   buf << "Q\n";
-  m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
-  pCache->ReleaseCachedFace(pFont);
+  WriteToStream(&buf);
   return true;
 }
 
-void CFX_PSRenderer::WritePSBinary(const uint8_t* data, int len) {
-  uint8_t* dest_buf;
-  uint32_t dest_size;
-  CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
-  if (pEncoders->GetBasicModule()->A85Encode(data, len, &dest_buf,
-                                             &dest_size)) {
-    m_pStream->WriteBlock(dest_buf, dest_size);
+bool CFX_PSRenderer::FaxCompressData(
+    std::unique_ptr<uint8_t, FxFreeDeleter> src_buf,
+    int width,
+    int height,
+    std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+    uint32_t* dest_size) const {
+  if (width * height <= 128) {
+    *dest_buf = std::move(src_buf);
+    *dest_size = (width + 7) / 8 * height;
+    return false;
+  }
+
+  m_pEncoderIface->pFaxEncodeFunc(src_buf.get(), width, height, (width + 7) / 8,
+                                  dest_buf, dest_size);
+  return true;
+}
+
+void CFX_PSRenderer::PSCompressData(uint8_t* src_buf,
+                                    uint32_t src_size,
+                                    uint8_t** output_buf,
+                                    uint32_t* output_size,
+                                    const char** filter) const {
+  *output_buf = src_buf;
+  *output_size = src_size;
+  *filter = "";
+  if (src_size < 1024)
+    return;
+
+  uint8_t* dest_buf = nullptr;
+  uint32_t dest_size = src_size;
+  if (m_PSLevel >= 3) {
+    std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf_unique;
+    if (m_pEncoderIface->pFlateEncodeFunc(src_buf, src_size, &dest_buf_unique,
+                                          &dest_size)) {
+      dest_buf = dest_buf_unique.release();
+      *filter = "/FlateDecode filter ";
+    }
+  } else {
+    std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf_unique;
+    if (m_pEncoderIface->pRunLengthEncodeFunc({src_buf, src_size},
+                                              &dest_buf_unique, &dest_size)) {
+      dest_buf = dest_buf_unique.release();
+      *filter = "/RunLengthDecode filter ";
+    }
+  }
+  if (dest_size < src_size) {
+    *output_buf = dest_buf;
+    *output_size = dest_size;
+  } else {
+    *filter = nullptr;
     FX_Free(dest_buf);
+  }
+}
+
+void CFX_PSRenderer::WritePSBinary(const uint8_t* data, int len) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size;
+  if (m_pEncoderIface->pA85EncodeFunc({data, static_cast<size_t>(len)},
+                                      &dest_buf, &dest_size)) {
+    m_pStream->WriteBlock(dest_buf.get(), dest_size);
   } else {
     m_pStream->WriteBlock(data, len);
   }
 }
+
+void CFX_PSRenderer::WriteToStream(std::ostringstream* stringStream) {
+  if (stringStream->tellp() > 0)
+    m_pStream->WriteBlock(stringStream->str().c_str(), stringStream->tellp());
+}
diff --git a/core/fxge/win32/cfx_psrenderer.h b/core/fxge/win32/cfx_psrenderer.h
index 33829ea..50e98a4 100644
--- a/core/fxge/win32/cfx_psrenderer.h
+++ b/core/fxge/win32/cfx_psrenderer.h
@@ -11,26 +11,49 @@
 #include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_graphstatedata.h"
 
-class CFX_DIBSource;
-class CFX_FaceCache;
+class CFX_DIBBase;
+class CFX_GlyphCache;
 class CFX_Font;
-class CFX_FontCache;
-class CFX_Matrix;
 class CFX_PathData;
 class CPSFont;
-class FXTEXT_CHARPOS;
+class TextCharPos;
+struct FXDIB_ResampleOptions;
+
+struct EncoderIface {
+  bool (*pA85EncodeFunc)(pdfium::span<const uint8_t> src_buf,
+                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                         uint32_t* dest_size);
+  void (*pFaxEncodeFunc)(const uint8_t* src_buf,
+                         int width,
+                         int height,
+                         int pitch,
+                         std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                         uint32_t* dest_size);
+  bool (*pFlateEncodeFunc)(const uint8_t* src_buf,
+                           uint32_t src_size,
+                           std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                           uint32_t* dest_size);
+  bool (*pJpegEncodeFunc)(const RetainPtr<CFX_DIBBase>& pSource,
+                          uint8_t** dest_buf,
+                          size_t* dest_size);
+  bool (*pRunLengthEncodeFunc)(
+      pdfium::span<const uint8_t> src_buf,
+      std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+      uint32_t* dest_size);
+};
 
 class CFX_PSRenderer {
  public:
-  CFX_PSRenderer();
+  explicit CFX_PSRenderer(const EncoderIface* pEncoderIface);
   ~CFX_PSRenderer();
 
-  void Init(const RetainPtr<IFX_WriteStream>& stream,
+  void Init(const RetainPtr<IFX_RetainableWriteStream>& stream,
             int pslevel,
             int width,
             int height,
@@ -52,25 +75,25 @@
                 uint32_t fill_color,
                 uint32_t stroke_color,
                 int fill_mode);
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                  uint32_t color,
                  int dest_left,
                  int dest_top);
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      uint32_t color,
                      int dest_left,
                      int dest_top,
                      int dest_width,
                      int dest_height,
-                     uint32_t flags);
-  bool DrawDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                     const FXDIB_ResampleOptions& options);
+  bool DrawDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                   uint32_t color,
-                  const CFX_Matrix* pMatrix,
-                  uint32_t flags);
+                  const CFX_Matrix& matrix,
+                  const FXDIB_ResampleOptions& options);
   bool DrawText(int nChars,
-                const FXTEXT_CHARPOS* pCharPos,
+                const TextCharPos* pCharPos,
                 CFX_Font* pFont,
-                const CFX_Matrix* pObject2Device,
+                const CFX_Matrix& mtObject2Device,
                 float font_size,
                 uint32_t color);
 
@@ -79,24 +102,36 @@
                   const CFX_Matrix* pObject2Device);
   void SetGraphState(const CFX_GraphStateData* pGraphState);
   void SetColor(uint32_t color);
-  void FindPSFontGlyph(CFX_FaceCache* pFaceCache,
+  void FindPSFontGlyph(CFX_GlyphCache* pGlyphCache,
                        CFX_Font* pFont,
-                       const FXTEXT_CHARPOS& charpos,
+                       const TextCharPos& charpos,
                        int* ps_fontnum,
                        int* ps_glyphindex);
+  bool FaxCompressData(std::unique_ptr<uint8_t, FxFreeDeleter> src_buf,
+                       int width,
+                       int height,
+                       std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
+                       uint32_t* dest_size) const;
+  void PSCompressData(uint8_t* src_buf,
+                      uint32_t src_size,
+                      uint8_t** output_buf,
+                      uint32_t* output_size,
+                      const char** filter) const;
   void WritePSBinary(const uint8_t* data, int len);
+  void WriteToStream(std::ostringstream* stringStream);
 
-  RetainPtr<IFX_WriteStream> m_pStream;
-  int m_PSLevel;
-  CFX_GraphStateData m_CurGraphState;
-  bool m_bGraphStateSet;
+  bool m_bInited = false;
+  bool m_bGraphStateSet = false;
   bool m_bCmykOutput;
-  bool m_bColorSet;
-  uint32_t m_LastColor;
+  bool m_bColorSet = false;
+  int m_PSLevel = 0;
+  uint32_t m_LastColor = 0;
   FX_RECT m_ClipBox;
+  CFX_GraphStateData m_CurGraphState;
+  const EncoderIface* const m_pEncoderIface;
+  RetainPtr<IFX_RetainableWriteStream> m_pStream;
   std::vector<std::unique_ptr<CPSFont>> m_PSFontList;
   std::vector<FX_RECT> m_ClipBoxStack;
-  bool m_bInited;
 };
 
 #endif  // CORE_FXGE_WIN32_CFX_PSRENDERER_H_
diff --git a/core/fxge/win32/cfx_windowsdib.h b/core/fxge/win32/cfx_windowsdib.h
index 7c2447d..ef6127b 100644
--- a/core/fxge/win32/cfx_windowsdib.h
+++ b/core/fxge/win32/cfx_windowsdib.h
@@ -6,10 +6,8 @@
 
 #ifndef CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_
 #define CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_
-#ifdef _WIN32
-#ifndef _WINDOWS_
+
 #include <windows.h>
-#endif
 
 #include "core/fxcrt/bytestring.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
@@ -17,23 +15,18 @@
 #define WINDIB_OPEN_MEMORY 0x1
 #define WINDIB_OPEN_PATHNAME 0x2
 
-typedef struct WINDIB_Open_Args_ {
+struct WINDIB_Open_Args_ {
   int flags;
-
   const uint8_t* memory_base;
-
   size_t memory_size;
-
   const wchar_t* path_name;
-} WINDIB_Open_Args_;
+};
 
-class CFX_WindowsDIB : public CFX_DIBitmap {
+class CFX_WindowsDIB final : public CFX_DIBitmap {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  ~CFX_WindowsDIB() override;
-
   static ByteString GetBitmapInfo(const RetainPtr<CFX_DIBitmap>& pBitmap);
   static HBITMAP GetDDBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap, HDC hDC);
 
@@ -47,14 +40,13 @@
   void LoadFromDevice(HDC hDC, int left, int top);
   void SetToDevice(HDC hDC, int left, int top);
 
- protected:
+ private:
   CFX_WindowsDIB(HDC hDC, int width, int height);
+  ~CFX_WindowsDIB() override;
 
   HDC m_hMemDC;
   HBITMAP m_hBitmap;
   HBITMAP m_hOldBitmap;
 };
 
-#endif  // _WIN32
-
 #endif  // CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_
diff --git a/core/fxge/win32/cpsoutput.cpp b/core/fxge/win32/cpsoutput.cpp
index 83f2fc0..4170db1 100644
--- a/core/fxge/win32/cpsoutput.cpp
+++ b/core/fxge/win32/cpsoutput.cpp
@@ -9,28 +9,34 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
 
-CPSOutput::CPSOutput(HDC hDC) : m_hDC(hDC) {}
+CPSOutput::CPSOutput(HDC hDC, OutputMode mode) : m_hDC(hDC), m_mode(mode) {}
 
-CPSOutput::~CPSOutput() {}
+CPSOutput::~CPSOutput() = default;
 
 bool CPSOutput::WriteBlock(const void* str, size_t len) {
-  int sent_len = 0;
-  while (len > 0) {
-    char buffer[1026];
-    size_t send_len = std::min(len, static_cast<size_t>(1024));
-    *(reinterpret_cast<uint16_t*>(buffer)) = send_len;
-    memcpy(buffer + 2, static_cast<const char*>(str) + sent_len, send_len);
+  pdfium::span<const uint8_t> input(static_cast<const uint8_t*>(str), len);
+  while (!input.empty()) {
+    uint8_t buffer[1026];
+    size_t send_len = std::min<size_t>(input.size(), 1024);
+    *(reinterpret_cast<uint16_t*>(buffer)) = static_cast<uint16_t>(send_len);
+    memcpy(buffer + 2, input.data(), send_len);
 
-    // TODO(thestig/rbpotter): Do PASSTHROUGH for non-Chromium usage.
-    // ExtEscape(m_hDC, PASSTHROUGH, send_len + 2, buffer, 0, nullptr);
-    ::GdiComment(m_hDC, send_len + 2, reinterpret_cast<const BYTE*>(buffer));
-    sent_len += send_len;
-    len -= send_len;
+    switch (m_mode) {
+      case OutputMode::kExtEscape:
+        ExtEscape(m_hDC, PASSTHROUGH, send_len + 2,
+                  reinterpret_cast<const char*>(buffer), 0, nullptr);
+        break;
+      case OutputMode::kGdiComment:
+        GdiComment(m_hDC, send_len + 2, buffer);
+        break;
+    }
+    input = input.subspan(send_len);
   }
   return true;
 }
 
-bool CPSOutput::WriteString(const ByteStringView& str) {
+bool CPSOutput::WriteString(ByteStringView str) {
   return WriteBlock(str.unterminated_c_str(), str.GetLength());
 }
diff --git a/core/fxge/win32/cpsoutput.h b/core/fxge/win32/cpsoutput.h
index d140de9..939f4cc 100644
--- a/core/fxge/win32/cpsoutput.h
+++ b/core/fxge/win32/cpsoutput.h
@@ -12,17 +12,20 @@
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_system.h"
 
-class CPSOutput : public IFX_WriteStream {
+class CPSOutput final : public IFX_RetainableWriteStream {
  public:
-  explicit CPSOutput(HDC hDC);
+  enum class OutputMode { kExtEscape, kGdiComment };
+
+  CPSOutput(HDC hDC, OutputMode mode);
   ~CPSOutput() override;
 
   // IFX_Writestream
   bool WriteBlock(const void* str, size_t len) override;
-  bool WriteString(const ByteStringView& str) override;
+  bool WriteString(ByteStringView str) override;
 
  private:
-  HDC m_hDC;
+  const HDC m_hDC;
+  const OutputMode m_mode;
 };
 
 #endif  // CORE_FXGE_WIN32_CPSOUTPUT_H_
diff --git a/core/fxge/win32/dwrite_int.h b/core/fxge/win32/dwrite_int.h
deleted file mode 100644
index 7fbb929..0000000
--- a/core/fxge/win32/dwrite_int.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_WIN32_DWRITE_INT_H_
-#define CORE_FXGE_WIN32_DWRITE_INT_H_
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-
-#ifndef DECLSPEC_UUID
-#if defined(__cplusplus)
-#define DECLSPEC_UUID(x) __declspec(uuid(x))
-#else
-#define DECLSPEC_UUID(x)
-#endif
-#endif
-#ifndef DECLSPEC_NOVTABLE
-#if defined(__cplusplus)
-#define DECLSPEC_NOVTABLE __declspec(novtable)
-#else
-#define DECLSPEC_NOVTABLE
-#endif
-#endif
-#if (WINVER < 0x0500)
-#ifndef _MAC
-DECLARE_HANDLE(HMONITOR);
-#endif
-#endif
-class CDWriteExt {
- public:
-  CDWriteExt();
-  ~CDWriteExt();
-
-  void Load();
-  void Unload();
-
-  bool IsAvailable() { return !!m_pDWriteFactory; }
-
-  void* DwCreateFontFaceFromStream(uint8_t* pData,
-                                   uint32_t size,
-                                   int simulation_style);
-  bool DwCreateRenderingTarget(const RetainPtr<CFX_DIBitmap>& pSrc,
-                               void** renderTarget);
-  void DwDeleteRenderingTarget(void* renderTarget);
-  bool DwRendingString(void* renderTarget,
-                       CFX_ClipRgn* pClipRgn,
-                       FX_RECT& stringRect,
-                       CFX_Matrix* pMatrix,
-                       void* font,
-                       float font_size,
-                       FX_ARGB text_color,
-                       int glyph_count,
-                       unsigned short* glyph_indices,
-                       float baselineOriginX,
-                       float baselineOriginY,
-                       void* glyph_offsets,
-                       float* glyph_advances);
-  void DwDeleteFont(void* pFont);
-
- protected:
-  void* m_hModule;
-  void* m_pDWriteFactory;
-  void* m_pDwFontContext;
-  void* m_pDwTextRenderer;
-};
-
-#endif  // CORE_FXGE_WIN32_DWRITE_INT_H_
diff --git a/core/fxge/win32/fx_win32_device.cpp b/core/fxge/win32/fx_win32_device.cpp
index 492b0b5..58cb504 100644
--- a/core/fxge/win32/fx_win32_device.cpp
+++ b/core/fxge/win32/fx_win32_device.cpp
@@ -23,12 +23,12 @@
 #include "core/fxge/dib/cstretchengine.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/fx_freetype.h"
-#include "core/fxge/ifx_systemfontinfo.h"
+#include "core/fxge/systemfontinfo_iface.h"
 #include "core/fxge/win32/cfx_windowsdib.h"
-#include "core/fxge/win32/dwrite_int.h"
 #include "core/fxge/win32/win32_int.h"
 #include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/span.h"
+#include "third_party/base/win/win_util.h"
 
 #ifndef _SKIA_SUPPORT_
 #include "core/fxge/agg/fx_agg_driver.h"
@@ -38,9 +38,9 @@
 
 const struct {
   const char* m_pFaceName;
-  const char* m_pVariantName;
+  const char* m_pVariantName;  // Note: UTF-16LE terminator required.
 } g_VariantNames[] = {
-    {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A"},
+    {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A\x00\x00"},
 };
 
 const struct {
@@ -82,86 +82,69 @@
   return false;
 }
 
-bool IsGDIEnabled() {
-  // If GDI is disabled then GetDC for the desktop will fail.
-  HDC hdc = ::GetDC(nullptr);
-  if (!hdc)
-    return false;
-  ::ReleaseDC(nullptr, hdc);
-  return true;
-}
+HPEN CreateExtPen(const CFX_GraphStateData* pGraphState,
+                  const CFX_Matrix* pMatrix,
+                  uint32_t argb) {
+  ASSERT(pGraphState);
 
-HPEN CreatePen(const CFX_GraphStateData* pGraphState,
-               const CFX_Matrix* pMatrix,
-               uint32_t argb) {
-  float width;
-  float scale = 1.f;
-  if (pMatrix)
+  float scale = 1.0f;
+  if (pMatrix) {
     scale = fabs(pMatrix->a) > fabs(pMatrix->b) ? fabs(pMatrix->a)
                                                 : fabs(pMatrix->b);
-  if (pGraphState) {
-    width = scale * pGraphState->m_LineWidth;
-  } else {
-    width = 1.0f;
   }
+  float width = std::max(scale * pGraphState->m_LineWidth, 1.0f);
+
   uint32_t PenStyle = PS_GEOMETRIC;
-  if (width < 1) {
-    width = 1;
-  }
-  if (pGraphState->m_DashCount) {
+  if (!pGraphState->m_DashArray.empty())
     PenStyle |= PS_USERSTYLE;
-  } else {
+  else
     PenStyle |= PS_SOLID;
-  }
+
   switch (pGraphState->m_LineCap) {
-    case 0:
+    case CFX_GraphStateData::LineCapButt:
       PenStyle |= PS_ENDCAP_FLAT;
       break;
-    case 1:
+    case CFX_GraphStateData::LineCapRound:
       PenStyle |= PS_ENDCAP_ROUND;
       break;
-    case 2:
+    case CFX_GraphStateData::LineCapSquare:
       PenStyle |= PS_ENDCAP_SQUARE;
       break;
   }
   switch (pGraphState->m_LineJoin) {
-    case 0:
+    case CFX_GraphStateData::LineJoinMiter:
       PenStyle |= PS_JOIN_MITER;
       break;
-    case 1:
+    case CFX_GraphStateData::LineJoinRound:
       PenStyle |= PS_JOIN_ROUND;
       break;
-    case 2:
+    case CFX_GraphStateData::LineJoinBevel:
       PenStyle |= PS_JOIN_BEVEL;
       break;
   }
-  int a;
-  FX_COLORREF rgb;
-  std::tie(a, rgb) = ArgbToColorRef(argb);
+
+  FX_COLORREF colorref = ArgbToColorRef(argb);
   LOGBRUSH lb;
-  lb.lbColor = rgb;
+  lb.lbColor = colorref;
   lb.lbStyle = BS_SOLID;
   lb.lbHatch = 0;
   std::vector<uint32_t> dashes;
-  if (pGraphState->m_DashCount) {
-    dashes.resize(pGraphState->m_DashCount);
-    for (int i = 0; i < pGraphState->m_DashCount; i++) {
-      dashes[i] = FXSYS_round(
+  if (!pGraphState->m_DashArray.empty()) {
+    dashes.resize(pGraphState->m_DashArray.size());
+    for (size_t i = 0; i < pGraphState->m_DashArray.size(); i++) {
+      dashes[i] = FXSYS_roundf(
           pMatrix ? pMatrix->TransformDistance(pGraphState->m_DashArray[i])
                   : pGraphState->m_DashArray[i]);
       dashes[i] = std::max(dashes[i], 1U);
     }
   }
   return ExtCreatePen(PenStyle, (DWORD)ceil(width), &lb,
-                      pGraphState->m_DashCount,
+                      pGraphState->m_DashArray.size(),
                       reinterpret_cast<const DWORD*>(dashes.data()));
 }
 
 HBRUSH CreateBrush(uint32_t argb) {
-  int a;
-  FX_COLORREF rgb;
-  std::tie(a, rgb) = ArgbToColorRef(argb);
-  return CreateSolidBrush(rgb);
+  return CreateSolidBrush(ArgbToColorRef(argb));
 }
 
 void SetPathToDC(HDC hDC,
@@ -175,7 +158,7 @@
     if (pMatrix)
       pos = pMatrix->Transform(pos);
 
-    CFX_Point screen(FXSYS_round(pos.x), FXSYS_round(pos.y));
+    CFX_Point screen(FXSYS_roundf(pos.x), FXSYS_roundf(pos.y));
     FXPT_TYPE point_type = pPoints[i].m_Type;
     if (point_type == FXPT_TYPE::MoveTo) {
       MoveToEx(hDC, screen.x, screen.y, nullptr);
@@ -193,15 +176,15 @@
       if (pMatrix)
         pos = pMatrix->Transform(pos);
 
-      lppt[1].x = FXSYS_round(pos.x);
-      lppt[1].y = FXSYS_round(pos.y);
+      lppt[1].x = FXSYS_roundf(pos.x);
+      lppt[1].y = FXSYS_roundf(pos.y);
 
       pos = pPoints[i + 2].m_Point;
       if (pMatrix)
         pos = pMatrix->Transform(pos);
 
-      lppt[2].x = FXSYS_round(pos.x);
-      lppt[2].y = FXSYS_round(pos.y);
+      lppt[2].x = FXSYS_roundf(pos.x);
+      lppt[2].y = FXSYS_roundf(pos.y);
       PolyBezierTo(hDC, lppt, 3);
       i += 2;
     }
@@ -318,8 +301,8 @@
 
 class CFX_Win32FallbackFontInfo final : public CFX_FolderFontInfo {
  public:
-  CFX_Win32FallbackFontInfo() {}
-  ~CFX_Win32FallbackFontInfo() override {}
+  CFX_Win32FallbackFontInfo() = default;
+  ~CFX_Win32FallbackFontInfo() override = default;
 
   // CFX_FolderFontInfo:
   void* MapFont(int weight,
@@ -329,12 +312,12 @@
                 const char* family) override;
 };
 
-class CFX_Win32FontInfo final : public IFX_SystemFontInfo {
+class CFX_Win32FontInfo final : public SystemFontInfoIface {
  public:
   CFX_Win32FontInfo();
   ~CFX_Win32FontInfo() override;
 
-  // IFX_SystemFontInfo
+  // SystemFontInfoIface
   bool EnumFontList(CFX_FontMapper* pMapper) override;
   void* MapFont(int weight,
                 bool bItalic,
@@ -344,8 +327,7 @@
   void* GetFont(const char* face) override { return nullptr; }
   uint32_t GetFontData(void* hFont,
                        uint32_t table,
-                       uint8_t* buffer,
-                       uint32_t size) override;
+                       pdfium::span<uint8_t> buffer) override;
   bool GetFaceName(void* hFont, ByteString* name) override;
   bool GetFontCharset(void* hFont, int* charset) override;
   void DeleteFont(void* hFont) override;
@@ -357,10 +339,11 @@
   void GetJapanesePreference(ByteString& face, int weight, int picth_family);
   ByteString FindFont(const ByteString& name);
 
-  HDC m_hDC;
+  const HDC m_hDC;
   UnownedPtr<CFX_FontMapper> m_pMapper;
   ByteString m_LastFamily;
-  ByteString m_KaiTi, m_FangSong;
+  ByteString m_KaiTi;
+  ByteString m_FangSong;
 };
 
 int CALLBACK FontEnumProc(const LOGFONTA* plf,
@@ -381,10 +364,10 @@
 bool CFX_Win32FontInfo::IsOpenTypeFromDiv(const LOGFONTA* plf) {
   HFONT hFont = CreateFontIndirectA(plf);
   bool ret = false;
-  uint32_t font_size = GetFontData(hFont, 0, nullptr, 0);
+  uint32_t font_size = GetFontData(hFont, 0, {});
   if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) {
     uint32_t lVersion = 0;
-    GetFontData(hFont, 0, (uint8_t*)(&lVersion), sizeof(uint32_t));
+    GetFontData(hFont, 0, {(uint8_t*)(&lVersion), sizeof(uint32_t)});
     lVersion = (((uint32_t)(uint8_t)(lVersion)) << 24) |
                ((uint32_t)((uint8_t)(lVersion >> 8))) << 16 |
                ((uint32_t)((uint8_t)(lVersion >> 16))) << 8 |
@@ -402,10 +385,10 @@
 bool CFX_Win32FontInfo::IsSupportFontFormDiv(const LOGFONTA* plf) {
   HFONT hFont = CreateFontIndirectA(plf);
   bool ret = false;
-  uint32_t font_size = GetFontData(hFont, 0, nullptr, 0);
+  uint32_t font_size = GetFontData(hFont, 0, {});
   if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) {
     uint32_t lVersion = 0;
-    GetFontData(hFont, 0, (uint8_t*)(&lVersion), sizeof(uint32_t));
+    GetFontData(hFont, 0, {(uint8_t*)(&lVersion), sizeof(lVersion)});
     lVersion = (((uint32_t)(uint8_t)(lVersion)) << 24) |
                ((uint32_t)((uint8_t)(lVersion >> 8))) << 16 |
                ((uint32_t)((uint8_t)(lVersion >> 16))) << 8 |
@@ -448,7 +431,7 @@
   lf.lfCharSet = FX_CHARSET_Default;
   lf.lfFaceName[0] = 0;
   lf.lfPitchAndFamily = 0;
-  EnumFontFamiliesExA(m_hDC, &lf, (FONTENUMPROCA)FontEnumProc, (uintptr_t) this,
+  EnumFontFamiliesExA(m_hDC, &lf, (FONTENUMPROCA)FontEnumProc, (uintptr_t)this,
                       0);
   return true;
 }
@@ -459,12 +442,12 @@
 
   for (size_t i = 0; i < m_pMapper->m_InstalledTTFonts.size(); ++i) {
     ByteString thisname = m_pMapper->m_InstalledTTFonts[i];
-    if (thisname.Left(name.GetLength()) == name)
+    if (thisname.First(name.GetLength()) == name)
       return m_pMapper->m_InstalledTTFonts[i];
   }
   for (size_t i = 0; i < m_pMapper->m_LocalizedTTFonts.size(); ++i) {
     ByteString thisname = m_pMapper->m_LocalizedTTFonts[i].first;
-    if (thisname.Left(name.GetLength()) == name)
+    if (thisname.First(name.GetLength()) == name)
       return m_pMapper->m_LocalizedTTFonts[i].second;
   }
   return ByteString();
@@ -599,7 +582,7 @@
   if (face.EqualNoCase(facebuf))
     return hFont;
 
-  WideString wsFace = WideString::FromLocal(facebuf);
+  WideString wsFace = WideString::FromDefANSI(facebuf);
   for (size_t i = 0; i < FX_ArraySize(g_VariantNames); ++i) {
     if (face != g_VariantNames[i].m_pFaceName)
       continue;
@@ -645,11 +628,10 @@
 
 uint32_t CFX_Win32FontInfo::GetFontData(void* hFont,
                                         uint32_t table,
-                                        uint8_t* buffer,
-                                        uint32_t size) {
+                                        pdfium::span<uint8_t> buffer) {
   HFONT hOldFont = (HFONT)::SelectObject(m_hDC, (HFONT)hFont);
   table = FXDWORD_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&table));
-  size = ::GetFontData(m_hDC, table, 0, buffer, size);
+  uint32_t size = ::GetFontData(m_hDC, table, 0, buffer.data(), buffer.size());
   ::SelectObject(m_hDC, hOldFont);
   if (size == GDI_ERROR) {
     return 0;
@@ -680,12 +662,12 @@
 
 }  // namespace
 
-int g_pdfium_print_mode = WindowsPrintMode::kModeEmf;
+WindowsPrintMode g_pdfium_print_mode = WindowsPrintMode::kModeEmf;
 
-std::unique_ptr<IFX_SystemFontInfo> IFX_SystemFontInfo::CreateDefault(
+std::unique_ptr<SystemFontInfoIface> SystemFontInfoIface::CreateDefault(
     const char** pUnused) {
-  if (IsGDIEnabled())
-    return std::unique_ptr<IFX_SystemFontInfo>(new CFX_Win32FontInfo);
+  if (pdfium::base::win::IsUser32AndGdi32Available())
+    return std::unique_ptr<SystemFontInfoIface>(new CFX_Win32FontInfo);
 
   // Select the fallback font information class if GDI is disabled.
   CFX_Win32FallbackFontInfo* pInfoFallback = new CFX_Win32FallbackFontInfo;
@@ -698,32 +680,35 @@
     fonts_path += "\\Fonts";
     pInfoFallback->AddPath(fonts_path);
   }
-  return std::unique_ptr<IFX_SystemFontInfo>(pInfoFallback);
+  return std::unique_ptr<SystemFontInfoIface>(pInfoFallback);
 }
 
-void CFX_GEModule::InitPlatform() {
-  CWin32Platform* pPlatformData = new CWin32Platform;
+CWin32Platform::CWin32Platform() = default;
+
+CWin32Platform::~CWin32Platform() = default;
+
+void CWin32Platform::Init() {
   OSVERSIONINFO ver;
   ver.dwOSVersionInfoSize = sizeof(ver);
   GetVersionEx(&ver);
-  pPlatformData->m_bHalfTone = ver.dwMajorVersion >= 5;
-  if (IsGDIEnabled())
-    pPlatformData->m_GdiplusExt.Load();
-  m_pPlatformData = pPlatformData;
-  m_pFontMgr->SetSystemFontInfo(IFX_SystemFontInfo::CreateDefault(nullptr));
+  m_bHalfTone = ver.dwMajorVersion >= 5;
+  if (pdfium::base::win::IsUser32AndGdi32Available())
+    m_GdiplusExt.Load();
+  CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(
+      SystemFontInfoIface::CreateDefault(nullptr));
 }
 
-void CFX_GEModule::DestroyPlatform() {
-  delete (CWin32Platform*)m_pPlatformData;
-  m_pPlatformData = nullptr;
+// static
+std::unique_ptr<CFX_GEModule::PlatformIface>
+CFX_GEModule::PlatformIface::Create() {
+  return pdfium::MakeUnique<CWin32Platform>();
 }
 
-CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, int device_class) {
-  m_hDC = hDC;
-  m_DeviceClass = device_class;
-  CWin32Platform* pPlatform =
-      (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
-  SetStretchBltMode(hDC, pPlatform->m_bHalfTone ? HALFTONE : COLORONCOLOR);
+CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, DeviceType device_type)
+    : m_hDC(hDC), m_DeviceType(device_type) {
+  auto* pPlatform =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
+  SetStretchBltMode(m_hDC, pPlatform->m_bHalfTone ? HALFTONE : COLORONCOLOR);
   DWORD obj_type = GetObjectType(m_hDC);
   m_bMetafileDCType = obj_type == OBJ_ENHMETADC || obj_type == OBJ_ENHMETAFILE;
   if (obj_type == OBJ_MEMDC) {
@@ -741,19 +726,21 @@
     m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
     m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
   }
-  if (m_DeviceClass != FXDC_DISPLAY) {
+  if (m_DeviceType != DeviceType::kDisplay) {
     m_RenderCaps = FXRC_BIT_MASK;
   } else {
     m_RenderCaps = FXRC_GET_BITS | FXRC_BIT_MASK;
   }
 }
 
-CGdiDeviceDriver::~CGdiDeviceDriver() {}
+CGdiDeviceDriver::~CGdiDeviceDriver() = default;
+
+DeviceType CGdiDeviceDriver::GetDeviceType() const {
+  return m_DeviceType;
+}
 
 int CGdiDeviceDriver::GetDeviceCaps(int caps_id) const {
   switch (caps_id) {
-    case FXDC_DEVICE_CLASS:
-      return m_DeviceClass;
     case FXDC_PIXEL_WIDTH:
       return m_Width;
     case FXDC_PIXEL_HEIGHT:
@@ -762,8 +749,10 @@
       return m_nBitsPerPixel;
     case FXDC_RENDER_CAPS:
       return m_RenderCaps;
+    default:
+      NOTREACHED();
+      return 0;
   }
-  return 0;
 }
 
 void CGdiDeviceDriver::SaveState() {
@@ -777,10 +766,10 @@
 }
 
 bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap1,
-                                     const FX_RECT* pSrcRect,
+                                     const FX_RECT& src_rect,
                                      int left,
                                      int top) {
-  if (m_DeviceClass == FXDC_PRINTER) {
+  if (m_DeviceType == DeviceType::kPrinter) {
     RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1->FlipImage(false, true);
     if (!pBitmap)
       return false;
@@ -788,32 +777,31 @@
     if (pBitmap->IsCmykImage() && !pBitmap->ConvertFormat(FXDIB_Rgb))
       return false;
 
-    int width = pSrcRect->Width(), height = pSrcRect->Height();
     LPBYTE pBuffer = pBitmap->GetBuffer();
     ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap);
     ((BITMAPINFOHEADER*)info.c_str())->biHeight *= -1;
-    FX_RECT dst_rect(0, 0, width, height);
+    FX_RECT dst_rect(0, 0, src_rect.Width(), src_rect.Height());
     dst_rect.Intersect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
     int dst_width = dst_rect.Width();
     int dst_height = dst_rect.Height();
     ::StretchDIBits(m_hDC, left, top, dst_width, dst_height, 0, 0, dst_width,
                     dst_height, pBuffer, (BITMAPINFO*)info.c_str(),
                     DIB_RGB_COLORS, SRCCOPY);
-  } else {
-    RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
-    if (pBitmap->IsCmykImage()) {
-      pBitmap = pBitmap->CloneConvert(FXDIB_Rgb);
-      if (!pBitmap)
-        return false;
-    }
-    int width = pSrcRect->Width(), height = pSrcRect->Height();
-    LPBYTE pBuffer = pBitmap->GetBuffer();
-    ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap);
-    ::SetDIBitsToDevice(m_hDC, left, top, width, height, pSrcRect->left,
-                        pBitmap->GetHeight() - pSrcRect->bottom, 0,
-                        pBitmap->GetHeight(), pBuffer,
-                        (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
+    return true;
   }
+
+  RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
+  if (pBitmap->IsCmykImage()) {
+    pBitmap = pBitmap->CloneConvert(FXDIB_Rgb);
+    if (!pBitmap)
+      return false;
+  }
+  LPBYTE pBuffer = pBitmap->GetBuffer();
+  ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap);
+  ::SetDIBitsToDevice(m_hDC, left, top, src_rect.Width(), src_rect.Height(),
+                      src_rect.left, pBitmap->GetHeight() - src_rect.bottom, 0,
+                      pBitmap->GetHeight(), pBuffer, (BITMAPINFO*)info.c_str(),
+                      DIB_RGB_COLORS);
   return true;
 }
 
@@ -823,7 +811,7 @@
     int dest_top,
     int dest_width,
     int dest_height,
-    uint32_t flags) {
+    const FXDIB_ResampleOptions& options) {
   RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
   if (!pBitmap || dest_width == 0 || dest_height == 0)
     return false;
@@ -834,16 +822,17 @@
   ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap);
   if ((int64_t)abs(dest_width) * abs(dest_height) <
           (int64_t)pBitmap1->GetWidth() * pBitmap1->GetHeight() * 4 ||
-      (flags & FXDIB_INTERPOL) || (flags & FXDIB_BICUBIC_INTERPOL)) {
+      options.bInterpolateBilinear || options.bInterpolateBicubic) {
     SetStretchBltMode(m_hDC, HALFTONE);
   } else {
     SetStretchBltMode(m_hDC, COLORONCOLOR);
   }
   RetainPtr<CFX_DIBitmap> pToStrechBitmap = pBitmap;
-  if (m_DeviceClass == FXDC_PRINTER &&
+  if (m_DeviceType == DeviceType::kPrinter &&
       ((int64_t)pBitmap->GetWidth() * pBitmap->GetHeight() >
        (int64_t)abs(dest_width) * abs(dest_height))) {
-    pToStrechBitmap = pBitmap->StretchTo(dest_width, dest_height, 0, nullptr);
+    pToStrechBitmap = pBitmap->StretchTo(dest_width, dest_height,
+                                         FXDIB_ResampleOptions(), nullptr);
   }
   ByteString toStrechBitmapInfo =
       CFX_WindowsDIB::GetBitmapInfo(pToStrechBitmap);
@@ -861,8 +850,7 @@
     int dest_top,
     int dest_width,
     int dest_height,
-    uint32_t bitmap_color,
-    uint32_t flags) {
+    uint32_t bitmap_color) {
   RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
   if (!pBitmap || dest_width == 0 || dest_height == 0)
     return false;
@@ -885,7 +873,7 @@
   bmi.bmiColors[0] = 0xffffff;
   bmi.bmiColors[1] = 0;
 
-  HBRUSH hPattern = CreateSolidBrush(bitmap_color & 0xffffff);
+  HBRUSH hPattern = CreateBrush(bitmap_color);
   HBRUSH hOld = (HBRUSH)SelectObject(m_hDC, hPattern);
 
   // In PDF, when image mask is 1, use device bitmap; when mask is 0, use brush
@@ -959,8 +947,8 @@
     }
   }
 
-  MoveToEx(m_hDC, FXSYS_round(x1), FXSYS_round(y1), nullptr);
-  LineTo(m_hDC, FXSYS_round(x2), FXSYS_round(y2));
+  MoveToEx(m_hDC, FXSYS_roundf(x1), FXSYS_roundf(y1), nullptr);
+  LineTo(m_hDC, FXSYS_roundf(x2), FXSYS_roundf(y2));
 }
 
 bool CGdiDeviceDriver::DrawPath(const CFX_PathData* pPathData,
@@ -969,12 +957,12 @@
                                 uint32_t fill_color,
                                 uint32_t stroke_color,
                                 int fill_mode,
-                                int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
 
-  CWin32Platform* pPlatform =
-      (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
+  auto* pPlatform =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
   if (!(pGraphState || stroke_color == 0) &&
       !pPlatform->m_GdiplusExt.IsAvailable()) {
     CFX_FloatRect bbox_f = pPathData->GetBoundingBox();
@@ -985,12 +973,12 @@
     if (bbox.Width() <= 0) {
       return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
                               CFX_PointF(bbox.left, bbox.bottom + 1),
-                              fill_color, FXDIB_BLEND_NORMAL);
+                              fill_color, BlendMode::kNormal);
     }
     if (bbox.Height() <= 0) {
       return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
                               CFX_PointF(bbox.right + 1, bbox.top), fill_color,
-                              FXDIB_BLEND_NORMAL);
+                              BlendMode::kNormal);
     }
   }
   int fill_alpha = FXARGB_A(fill_color);
@@ -1001,11 +989,11 @@
     return false;
 
   if (pPlatform->m_GdiplusExt.IsAvailable()) {
-    if (bDrawAlpha ||
-        ((m_DeviceClass != FXDC_PRINTER && !(fill_mode & FXFILL_FULLCOVER)) ||
-         (pGraphState && pGraphState->m_DashCount))) {
+    if (bDrawAlpha || ((m_DeviceType != DeviceType::kPrinter &&
+                        !(fill_mode & FXFILL_FULLCOVER)) ||
+                       (pGraphState && !pGraphState->m_DashArray.empty()))) {
       if (!((!pMatrix || !pMatrix->WillScale()) && pGraphState &&
-            pGraphState->m_LineWidth == 1.f &&
+            pGraphState->m_LineWidth == 1.0f &&
             (pPathData->GetPoints().size() == 5 ||
              pPathData->GetPoints().size() == 4) &&
             pPathData->IsRect())) {
@@ -1023,7 +1011,7 @@
   HBRUSH hBrush = nullptr;
   if (pGraphState && stroke_alpha) {
     SetMiterLimit(m_hDC, pGraphState->m_MiterLimit, nullptr);
-    hPen = CreatePen(pGraphState, pMatrix, stroke_color);
+    hPen = CreateExtPen(pGraphState, pMatrix, stroke_color);
     hPen = (HPEN)SelectObject(m_hDC, hPen);
   }
   if (fill_mode && fill_alpha) {
@@ -1032,7 +1020,7 @@
     hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
   }
   if (pPathData->GetPoints().size() == 2 && pGraphState &&
-      pGraphState->m_DashCount) {
+      !pGraphState->m_DashArray.empty()) {
     CFX_PointF pos1 = pPathData->GetPoint(0);
     CFX_PointF pos2 = pPathData->GetPoint(1);
     if (pMatrix) {
@@ -1069,27 +1057,32 @@
   return true;
 }
 
-bool CGdiDeviceDriver::FillRectWithBlend(const FX_RECT* pRect,
+bool CGdiDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
                                          uint32_t fill_color,
-                                         int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                         BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
 
   int alpha;
-  FX_COLORREF rgb;
-  std::tie(alpha, rgb) = ArgbToColorRef(fill_color);
+  FX_COLORREF colorref;
+  std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(fill_color);
   if (alpha == 0)
     return true;
 
   if (alpha < 255)
     return false;
 
-  HBRUSH hBrush = CreateSolidBrush(rgb);
-  ::FillRect(m_hDC, (RECT*)pRect, hBrush);
+  HBRUSH hBrush = CreateSolidBrush(colorref);
+  const RECT* pRect = reinterpret_cast<const RECT*>(&rect);
+  ::FillRect(m_hDC, pRect, hBrush);
   DeleteObject(hBrush);
   return true;
 }
 
+void CGdiDeviceDriver::SetBaseClip(const FX_RECT& rect) {
+  m_BaseClipBox = rect;
+}
+
 bool CGdiDeviceDriver::SetClip_PathFill(const CFX_PathData* pPathData,
                                         const CFX_Matrix* pMatrix,
                                         int fill_mode) {
@@ -1097,8 +1090,12 @@
     CFX_FloatRect rectf;
     if (pPathData->IsRect(pMatrix, &rectf)) {
       FX_RECT rect = rectf.GetOuterRect();
-      IntersectClipRect(m_hDC, rect.left, rect.top, rect.right, rect.bottom);
-      return true;
+      // Can easily apply base clip to protect against wildly large rectangular
+      // clips. crbug.com/1019026
+      if (m_BaseClipBox.has_value())
+        rect.Intersect(m_BaseClipBox.value());
+      return IntersectClipRect(m_hDC, rect.left, rect.top, rect.right,
+                               rect.bottom) != ERROR;
     }
   }
   SetPathToDC(m_hDC, pPathData, pMatrix);
@@ -1111,7 +1108,7 @@
     const CFX_PathData* pPathData,
     const CFX_Matrix* pMatrix,
     const CFX_GraphStateData* pGraphState) {
-  HPEN hPen = CreatePen(pGraphState, pMatrix, 0xff000000);
+  HPEN hPen = CreateExtPen(pGraphState, pMatrix, 0xff000000);
   hPen = (HPEN)SelectObject(m_hDC, hPen);
   SetPathToDC(m_hDC, pPathData, pMatrix);
   WidenPath(m_hDC);
@@ -1125,35 +1122,41 @@
 bool CGdiDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
                                         const CFX_PointF& ptLineTo,
                                         uint32_t color,
-                                        int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                        BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
 
-  int a;
-  FX_COLORREF rgb;
-  std::tie(a, rgb) = ArgbToColorRef(color);
-  if (a == 0)
+  int alpha;
+  FX_COLORREF colorref;
+  std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(color);
+  if (alpha == 0)
     return true;
 
-  HPEN hPen = CreatePen(PS_SOLID, 1, rgb);
+  HPEN hPen = CreatePen(PS_SOLID, 1, colorref);
   hPen = (HPEN)SelectObject(m_hDC, hPen);
-  MoveToEx(m_hDC, FXSYS_round(ptMoveTo.x), FXSYS_round(ptMoveTo.y), nullptr);
-  LineTo(m_hDC, FXSYS_round(ptLineTo.x), FXSYS_round(ptLineTo.y));
+  MoveToEx(m_hDC, FXSYS_roundf(ptMoveTo.x), FXSYS_roundf(ptMoveTo.y), nullptr);
+  LineTo(m_hDC, FXSYS_roundf(ptLineTo.x), FXSYS_roundf(ptLineTo.y));
   hPen = (HPEN)SelectObject(m_hDC, hPen);
   DeleteObject(hPen);
   return true;
 }
 
 CGdiDisplayDriver::CGdiDisplayDriver(HDC hDC)
-    : CGdiDeviceDriver(hDC, FXDC_DISPLAY) {
-  CWin32Platform* pPlatform =
-      (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
+    : CGdiDeviceDriver(hDC, DeviceType::kDisplay) {
+  auto* pPlatform =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
   if (pPlatform->m_GdiplusExt.IsAvailable()) {
     m_RenderCaps |= FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE;
   }
 }
 
-CGdiDisplayDriver::~CGdiDisplayDriver() {}
+CGdiDisplayDriver::~CGdiDisplayDriver() = default;
+
+int CGdiDisplayDriver::GetDeviceCaps(int caps_id) const {
+  if (caps_id == FXDC_HORZ_SIZE || caps_id == FXDC_VERT_SIZE)
+    return 0;
+  return CGdiDeviceDriver::GetDeviceCaps(caps_id);
+}
 
 bool CGdiDisplayDriver::GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
                                   int left,
@@ -1195,13 +1198,13 @@
   return ret;
 }
 
-bool CGdiDisplayDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CGdiDisplayDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                   uint32_t color,
-                                  const FX_RECT* pSrcRect,
+                                  const FX_RECT& src_rect,
                                   int left,
                                   int top,
-                                  int blend_type) {
-  ASSERT(blend_type == FXDIB_BLEND_NORMAL);
+                                  BlendMode blend_type) {
+  ASSERT(blend_type == BlendMode::kNormal);
   if (pSource->IsAlphaMask()) {
     int width = pSource->GetWidth(), height = pSource->GetHeight();
     int alpha = FXARGB_A(color);
@@ -1210,47 +1213,49 @@
       if (!background->Create(width, height, FXDIB_Rgb32) ||
           !GetDIBits(background, left, top) ||
           !background->CompositeMask(0, 0, width, height, pSource, color, 0, 0,
-                                     FXDIB_BLEND_NORMAL, nullptr, false, 0)) {
+                                     BlendMode::kNormal, nullptr, false)) {
         return false;
       }
-      FX_RECT src_rect(0, 0, width, height);
-      return SetDIBits(background, 0, &src_rect, left, top, FXDIB_BLEND_NORMAL);
+      FX_RECT alpha_src_rect(0, 0, width, height);
+      return SetDIBits(background, 0, alpha_src_rect, left, top,
+                       BlendMode::kNormal);
     }
-    FX_RECT clip_rect(left, top, left + pSrcRect->Width(),
-                      top + pSrcRect->Height());
-    return StretchDIBits(pSource, color, left - pSrcRect->left,
-                         top - pSrcRect->top, width, height, &clip_rect, 0,
-                         FXDIB_BLEND_NORMAL);
+    FX_RECT clip_rect(left, top, left + src_rect.Width(),
+                      top + src_rect.Height());
+    return StretchDIBits(pSource, color, left - src_rect.left,
+                         top - src_rect.top, width, height, &clip_rect,
+                         FXDIB_ResampleOptions(), BlendMode::kNormal);
   }
-  int width = pSrcRect->Width(), height = pSrcRect->Height();
+  int width = src_rect.Width();
+  int height = src_rect.Height();
   if (pSource->HasAlpha()) {
     auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
     if (!bitmap->Create(width, height, FXDIB_Rgb) ||
         !GetDIBits(bitmap, left, top) ||
-        !bitmap->CompositeBitmap(0, 0, width, height, pSource, pSrcRect->left,
-                                 pSrcRect->top, FXDIB_BLEND_NORMAL, nullptr,
+        !bitmap->CompositeBitmap(0, 0, width, height, pSource, src_rect.left,
+                                 src_rect.top, BlendMode::kNormal, nullptr,
                                  false)) {
       return false;
     }
-    FX_RECT src_rect(0, 0, width, height);
-    return SetDIBits(bitmap, 0, &src_rect, left, top, FXDIB_BLEND_NORMAL);
+    FX_RECT alpha_src_rect(0, 0, width, height);
+    return SetDIBits(bitmap, 0, alpha_src_rect, left, top, BlendMode::kNormal);
   }
   CFX_DIBExtractor temp(pSource);
   RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
   if (!pBitmap)
     return false;
-  return GDI_SetDIBits(pBitmap, pSrcRect, left, top);
+  return GDI_SetDIBits(pBitmap, src_rect, left, top);
 }
 
 bool CGdiDisplayDriver::UseFoxitStretchEngine(
-    const RetainPtr<CFX_DIBSource>& pSource,
+    const RetainPtr<CFX_DIBBase>& pSource,
     uint32_t color,
     int dest_left,
     int dest_top,
     int dest_width,
     int dest_height,
     const FX_RECT* pClipRect,
-    int render_flags) {
+    const FXDIB_ResampleOptions& options) {
   FX_RECT bitmap_clip = *pClipRect;
   if (dest_width < 0)
     dest_left += dest_width;
@@ -1260,29 +1265,31 @@
 
   bitmap_clip.Offset(-dest_left, -dest_top);
   RetainPtr<CFX_DIBitmap> pStretched =
-      pSource->StretchTo(dest_width, dest_height, render_flags, &bitmap_clip);
+      pSource->StretchTo(dest_width, dest_height, options, &bitmap_clip);
   if (!pStretched)
     return true;
 
   FX_RECT src_rect(0, 0, pStretched->GetWidth(), pStretched->GetHeight());
-  return SetDIBits(pStretched, color, &src_rect, pClipRect->left,
-                   pClipRect->top, FXDIB_BLEND_NORMAL);
+  return SetDIBits(pStretched, color, src_rect, pClipRect->left, pClipRect->top,
+                   BlendMode::kNormal);
 }
 
-bool CGdiDisplayDriver::StretchDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CGdiDisplayDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                       uint32_t color,
                                       int dest_left,
                                       int dest_top,
                                       int dest_width,
                                       int dest_height,
                                       const FX_RECT* pClipRect,
-                                      uint32_t flags,
-                                      int blend_type) {
-  ASSERT(pSource && pClipRect);
-  if (flags || dest_width > 10000 || dest_width < -10000 ||
+                                      const FXDIB_ResampleOptions& options,
+                                      BlendMode blend_type) {
+  ASSERT(pSource);
+  ASSERT(pClipRect);
+
+  if (options.HasAnyOptions() || dest_width > 10000 || dest_width < -10000 ||
       dest_height > 10000 || dest_height < -10000) {
     return UseFoxitStretchEngine(pSource, color, dest_left, dest_top,
-                                 dest_width, dest_height, pClipRect, flags);
+                                 dest_width, dest_height, pClipRect, options);
   }
   if (pSource->IsAlphaMask()) {
     FX_RECT image_rect;
@@ -1294,8 +1301,8 @@
     clip_rect.Intersect(*pClipRect);
     clip_rect.Offset(-image_rect.left, -image_rect.top);
     int clip_width = clip_rect.Width(), clip_height = clip_rect.Height();
-    RetainPtr<CFX_DIBitmap> pStretched(
-        pSource->StretchTo(dest_width, dest_height, flags, &clip_rect));
+    RetainPtr<CFX_DIBitmap> pStretched(pSource->StretchTo(
+        dest_width, dest_height, FXDIB_ResampleOptions(), &clip_rect));
     if (!pStretched)
       return true;
 
@@ -1304,18 +1311,18 @@
         !GetDIBits(background, image_rect.left + clip_rect.left,
                    image_rect.top + clip_rect.top) ||
         !background->CompositeMask(0, 0, clip_width, clip_height, pStretched,
-                                   color, 0, 0, FXDIB_BLEND_NORMAL, nullptr,
-                                   false, 0)) {
+                                   color, 0, 0, BlendMode::kNormal, nullptr,
+                                   false)) {
       return false;
     }
 
     FX_RECT src_rect(0, 0, clip_width, clip_height);
-    return SetDIBits(background, 0, &src_rect, image_rect.left + clip_rect.left,
-                     image_rect.top + clip_rect.top, FXDIB_BLEND_NORMAL);
+    return SetDIBits(background, 0, src_rect, image_rect.left + clip_rect.left,
+                     image_rect.top + clip_rect.top, BlendMode::kNormal);
   }
   if (pSource->HasAlpha()) {
-    CWin32Platform* pPlatform =
-        (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
+    auto* pPlatform =
+        static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
     if (pPlatform->m_GdiplusExt.IsAvailable() && !pSource->IsCmykImage()) {
       CFX_DIBExtractor temp(pSource);
       RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
@@ -1323,37 +1330,42 @@
         return false;
       return pPlatform->m_GdiplusExt.StretchDIBits(
           m_hDC, pBitmap, dest_left, dest_top, dest_width, dest_height,
-          pClipRect, flags);
+          pClipRect, FXDIB_ResampleOptions());
     }
     return UseFoxitStretchEngine(pSource, color, dest_left, dest_top,
-                                 dest_width, dest_height, pClipRect, flags);
+                                 dest_width, dest_height, pClipRect,
+                                 FXDIB_ResampleOptions());
   }
   CFX_DIBExtractor temp(pSource);
   RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
   if (!pBitmap)
     return false;
   return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width,
-                           dest_height, flags);
+                           dest_height, FXDIB_ResampleOptions());
 }
 
-bool CGdiDisplayDriver::StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CGdiDisplayDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                                     int bitmap_alpha,
                                     uint32_t color,
-                                    const CFX_Matrix* pMatrix,
-                                    uint32_t render_flags,
+                                    const CFX_Matrix& matrix,
+                                    const FXDIB_ResampleOptions& options,
                                     std::unique_ptr<CFX_ImageRenderer>* handle,
-                                    int blend_type) {
+                                    BlendMode blend_type) {
   return false;
 }
 
-CFX_WindowsRenderDevice::CFX_WindowsRenderDevice(HDC hDC) {
-  SetDeviceDriver(pdfium::WrapUnique(CreateDriver(hDC)));
+CFX_WindowsRenderDevice::CFX_WindowsRenderDevice(
+    HDC hDC,
+    const EncoderIface* pEncoderIface) {
+  SetDeviceDriver(pdfium::WrapUnique(CreateDriver(hDC, pEncoderIface)));
 }
 
-CFX_WindowsRenderDevice::~CFX_WindowsRenderDevice() {}
+CFX_WindowsRenderDevice::~CFX_WindowsRenderDevice() = default;
 
 // static
-IFX_RenderDeviceDriver* CFX_WindowsRenderDevice::CreateDriver(HDC hDC) {
+RenderDeviceDriverIface* CFX_WindowsRenderDevice::CreateDriver(
+    HDC hDC,
+    const EncoderIface* pEncoderIface) {
   int device_type = ::GetDeviceCaps(hDC, TECHNOLOGY);
   int obj_type = ::GetObjectType(hDC);
   bool use_printer = device_type == DT_RASPRINTER ||
@@ -1369,8 +1381,5 @@
   if (g_pdfium_print_mode == WindowsPrintMode::kModeTextOnly)
     return new CTextOnlyPrinterDriver(hDC);
 
-  // Should be PostScript
-  ASSERT(g_pdfium_print_mode == WindowsPrintMode::kModePostScript2 ||
-         g_pdfium_print_mode == WindowsPrintMode::kModePostScript3);
-  return new CPSPrinterDriver(hDC, g_pdfium_print_mode, false);
+  return new CPSPrinterDriver(hDC, g_pdfium_print_mode, false, pEncoderIface);
 }
diff --git a/core/fxge/win32/fx_win32_device_embeddertest.cpp b/core/fxge/win32/fx_win32_device_embeddertest.cpp
new file mode 100644
index 0000000..cb088e7
--- /dev/null
+++ b/core/fxge/win32/fx_win32_device_embeddertest.cpp
@@ -0,0 +1,92 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/win32/win32_int.h"
+
+#include <windows.h>
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+constexpr CFX_Matrix kIdentityMatrix;
+
+}  // namespace
+
+class CFX_WindowsRenderDeviceTest : public testing::Test {
+ public:
+  void SetUp() override {
+    // Get a device context with Windows GDI.
+    m_hDC = CreateCompatibleDC(nullptr);
+    ASSERT_TRUE(m_hDC);
+    CFX_GEModule::Create(nullptr);
+    m_driver = pdfium::MakeUnique<CFX_WindowsRenderDevice>(m_hDC, nullptr);
+    m_driver->SaveState();
+  }
+
+  void TearDown() override {
+    m_driver->RestoreState(false);
+    m_driver.reset();
+    CFX_GEModule::Destroy();
+    DeleteDC(m_hDC);
+  }
+
+ protected:
+  HDC m_hDC;
+  std::unique_ptr<CFX_WindowsRenderDevice> m_driver;
+};
+
+TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipTriangle) {
+  CFX_PathData path_data;
+  CFX_PointF p1(0.0f, 0.0f);
+  CFX_PointF p2(0.0f, 100.0f);
+  CFX_PointF p3(100.0f, 100.0f);
+
+  path_data.AppendLine(p1, p2);
+  path_data.AppendLine(p2, p3);
+  path_data.AppendLine(p3, p1);
+  path_data.ClosePath();
+  EXPECT_TRUE(
+      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
+}
+
+TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipRect) {
+  CFX_PathData path_data;
+
+  path_data.AppendRect(0.0f, 100.0f, 200.0f, 0.0f);
+  path_data.ClosePath();
+  EXPECT_TRUE(
+      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
+}
+
+TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRect) {
+  CFX_PathData path_data;
+
+  path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
+                       257698812.0f);
+  path_data.ClosePath();
+  // These coordinates for a clip path are valid, just very large. Using these
+  // for a clip path should allow IntersectClipRect() to return success;
+  // however they do not because the GDI API IntersectClipRect() errors out and
+  // affect subsequent imaging.  crbug.com/1019026
+  EXPECT_FALSE(
+      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
+}
+
+TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRectWithBaseClip) {
+  CFX_PathData path_data;
+  const FX_RECT kBaseClip(0, 0, 5100, 6600);
+
+  m_driver->SetBaseClip(kBaseClip);
+  path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
+                       257698812.0f);
+  path_data.ClosePath();
+  // Use of a reasonable base clip ensures that we avoid getting an error back
+  // from GDI API IntersectClipRect().
+  EXPECT_TRUE(
+      m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING));
+}
diff --git a/core/fxge/win32/fx_win32_dib.cpp b/core/fxge/win32/fx_win32_dib.cpp
index 6416b5a..5f9d42c 100644
--- a/core/fxge/win32/fx_win32_dib.cpp
+++ b/core/fxge/win32/fx_win32_dib.cpp
@@ -13,48 +13,52 @@
 
 ByteString CFX_WindowsDIB::GetBitmapInfo(
     const RetainPtr<CFX_DIBitmap>& pBitmap) {
-  ByteString result;
   int len = sizeof(BITMAPINFOHEADER);
-  if (pBitmap->GetBPP() == 1 || pBitmap->GetBPP() == 8) {
+  if (pBitmap->GetBPP() == 1 || pBitmap->GetBPP() == 8)
     len += sizeof(DWORD) * (int)(1 << pBitmap->GetBPP());
-  }
-  BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)result.GetBuffer(len);
-  memset(pbmih, 0, sizeof(BITMAPINFOHEADER));
-  pbmih->biSize = sizeof(BITMAPINFOHEADER);
-  pbmih->biBitCount = pBitmap->GetBPP();
-  pbmih->biCompression = BI_RGB;
-  pbmih->biHeight = -(int)pBitmap->GetHeight();
-  pbmih->biPlanes = 1;
-  pbmih->biWidth = pBitmap->GetWidth();
-  if (pBitmap->GetBPP() == 8) {
-    uint32_t* pPalette = (uint32_t*)(pbmih + 1);
-    if (pBitmap->GetPalette()) {
-      for (int i = 0; i < 256; i++) {
-        pPalette[i] = pBitmap->GetPalette()[i];
-      }
-    } else {
-      for (int i = 0; i < 256; i++) {
-        pPalette[i] = i * 0x010101;
+
+  ByteString result;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> cspan = result.GetBuffer(len);
+    BITMAPINFOHEADER* pbmih = reinterpret_cast<BITMAPINFOHEADER*>(cspan.data());
+    memset(pbmih, 0, sizeof(BITMAPINFOHEADER));
+    pbmih->biSize = sizeof(BITMAPINFOHEADER);
+    pbmih->biBitCount = pBitmap->GetBPP();
+    pbmih->biCompression = BI_RGB;
+    pbmih->biHeight = -(int)pBitmap->GetHeight();
+    pbmih->biPlanes = 1;
+    pbmih->biWidth = pBitmap->GetWidth();
+    if (pBitmap->GetBPP() == 8) {
+      uint32_t* pPalette = (uint32_t*)(pbmih + 1);
+      if (pBitmap->GetPalette()) {
+        for (int i = 0; i < 256; i++) {
+          pPalette[i] = pBitmap->GetPalette()[i];
+        }
+      } else {
+        for (int i = 0; i < 256; i++) {
+          pPalette[i] = i * 0x010101;
+        }
       }
     }
-  }
-  if (pBitmap->GetBPP() == 1) {
-    uint32_t* pPalette = (uint32_t*)(pbmih + 1);
-    if (pBitmap->GetPalette()) {
-      pPalette[0] = pBitmap->GetPalette()[0];
-      pPalette[1] = pBitmap->GetPalette()[1];
-    } else {
-      pPalette[0] = 0;
-      pPalette[1] = 0xffffff;
+    if (pBitmap->GetBPP() == 1) {
+      uint32_t* pPalette = (uint32_t*)(pbmih + 1);
+      if (pBitmap->GetPalette()) {
+        pPalette[0] = pBitmap->GetPalette()[0];
+        pPalette[1] = pBitmap->GetPalette()[1];
+      } else {
+        pPalette[0] = 0;
+        pPalette[1] = 0xffffff;
+      }
     }
   }
   result.ReleaseBuffer(len);
   return result;
 }
 
-RetainPtr<CFX_DIBitmap> _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
-                                                   LPVOID pData,
-                                                   bool bAlpha) {
+RetainPtr<CFX_DIBitmap> FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
+                                                  LPVOID pData,
+                                                  bool bAlpha) {
   int width = pbmi->bmiHeader.biWidth;
   int height = pbmi->bmiHeader.biHeight;
   BOOL bBottomUp = true;
@@ -72,18 +76,18 @@
 
   memcpy(pBitmap->GetBuffer(), pData, pitch * height);
   if (bBottomUp) {
-    uint8_t* temp_buf = FX_Alloc(uint8_t, pitch);
-    int top = 0, bottom = height - 1;
+    std::vector<uint8_t> temp_buf(pitch);
+    int top = 0;
+    int bottom = height - 1;
     while (top < bottom) {
-      memcpy(temp_buf, pBitmap->GetBuffer() + top * pitch, pitch);
-      memcpy(pBitmap->GetBuffer() + top * pitch,
-             pBitmap->GetBuffer() + bottom * pitch, pitch);
-      memcpy(pBitmap->GetBuffer() + bottom * pitch, temp_buf, pitch);
+      uint8_t* top_ptr = pBitmap->GetBuffer() + top * pitch;
+      uint8_t* bottom_ptr = pBitmap->GetBuffer() + bottom * pitch;
+      memcpy(temp_buf.data(), top_ptr, pitch);
+      memcpy(top_ptr, bottom_ptr, pitch);
+      memcpy(bottom_ptr, temp_buf.data(), pitch);
       top++;
       bottom--;
     }
-    FX_Free(temp_buf);
-    temp_buf = nullptr;
   }
   if (pbmi->bmiHeader.biBitCount == 1) {
     for (int i = 0; i < 2; i++)
@@ -97,7 +101,7 @@
 
 RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadFromBuf(BITMAPINFO* pbmi,
                                                     LPVOID pData) {
-  return _FX_WindowsDIB_LoadFromBuf(pbmi, pData, false);
+  return FX_WindowsDIB_LoadFromBuf(pbmi, pData, false);
 }
 
 HBITMAP CFX_WindowsDIB::GetDDBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap,
@@ -116,8 +120,8 @@
 }
 
 RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadFromFile(const wchar_t* filename) {
-  CWin32Platform* pPlatform =
-      (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
+  auto* pPlatform =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
   if (pPlatform->m_GdiplusExt.IsAvailable()) {
     WINDIB_Open_Args_ args;
     args.flags = WINDIB_OPEN_PATHNAME;
@@ -148,12 +152,12 @@
 }
 
 RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadFromFile(const char* filename) {
-  return LoadFromFile(WideString::FromLocal(filename).c_str());
+  return LoadFromFile(WideString::FromDefANSI(filename).c_str());
 }
 
 RetainPtr<CFX_DIBitmap> CFX_WindowsDIB::LoadDIBitmap(WINDIB_Open_Args_ args) {
-  CWin32Platform* pPlatform =
-      (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
+  auto* pPlatform =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
   if (pPlatform->m_GdiplusExt.IsAvailable()) {
     return pPlatform->m_GdiplusExt.LoadDIBitmap(args);
   }
@@ -183,7 +187,7 @@
 }
 
 CFX_WindowsDIB::CFX_WindowsDIB(HDC hDC, int width, int height) {
-  Create(width, height, FXDIB_Rgb, (uint8_t*)1);
+  Create(width, height, FXDIB_Rgb, (uint8_t*)1, 0);
   BITMAPINFOHEADER bmih;
   memset(&bmih, 0, sizeof bmih);
   bmih.biSize = sizeof bmih;
diff --git a/core/fxge/win32/fx_win32_dwrite.cpp b/core/fxge/win32/fx_win32_dwrite.cpp
deleted file mode 100644
index 4d4f45f..0000000
--- a/core/fxge/win32/fx_win32_dwrite.cpp
+++ /dev/null
@@ -1,446 +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 <dwrite.h>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_cliprgn.h"
-#include "core/fxge/win32/dwrite_int.h"
-
-typedef HRESULT(__stdcall* FuncType_DWriteCreateFactory)(
-    __in DWRITE_FACTORY_TYPE,
-    __in REFIID,
-    __out IUnknown**);
-template <typename InterfaceType>
-inline void SafeRelease(InterfaceType** currentObject) {
-  if (*currentObject) {
-    (*currentObject)->Release();
-    *currentObject = nullptr;
-  }
-}
-template <typename InterfaceType>
-inline InterfaceType* SafeAcquire(InterfaceType* newObject) {
-  if (newObject) {
-    newObject->AddRef();
-  }
-  return newObject;
-}
-
-class CDwFontFileStream final : public IDWriteFontFileStream {
- public:
-  explicit CDwFontFileStream(void const* fontFileReferenceKey,
-                             UINT32 fontFileReferenceKeySize);
-
-  // IUnknown.
-  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
-                                           void** ppvObject) override;
-  ULONG STDMETHODCALLTYPE AddRef() override;
-  ULONG STDMETHODCALLTYPE Release() override;
-
-  // IDWriteFontFileStream.
-  HRESULT STDMETHODCALLTYPE
-  ReadFileFragment(void const** fragmentStart,
-                   UINT64 fileOffset,
-                   UINT64 fragmentSize,
-                   OUT void** fragmentContext) override;
-  void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext) override;
-  HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize) override;
-  HRESULT STDMETHODCALLTYPE
-  GetLastWriteTime(OUT UINT64* lastWriteTime) override;
-
-  bool IsInitialized() { return !!resourcePtr_; }
-
- private:
-  ULONG refCount_;
-  void const* resourcePtr_;
-  DWORD resourceSize_;
-};
-
-class CDwFontFileLoader final : public IDWriteFontFileLoader {
- public:
-  // IUnknown.
-  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
-                                           void** ppvObject) override;
-  ULONG STDMETHODCALLTYPE AddRef() override;
-  ULONG STDMETHODCALLTYPE Release() override;
-
-  // IDWriteFontFileLoader.
-  HRESULT STDMETHODCALLTYPE
-  CreateStreamFromKey(void const* fontFileReferenceKey,
-                      UINT32 fontFileReferenceKeySize,
-                      OUT IDWriteFontFileStream** fontFileStream) override;
-
-  static IDWriteFontFileLoader* GetLoader() {
-    if (!instance_) {
-      instance_ = new CDwFontFileLoader();
-    }
-    return instance_;
-  }
-  static bool IsLoaderInitialized() { return !!instance_; }
-
- private:
-  CDwFontFileLoader();
-  ULONG refCount_;
-  static IDWriteFontFileLoader* instance_;
-};
-
-class CDwFontContext {
- public:
-  explicit CDwFontContext(IDWriteFactory* dwriteFactory);
-  ~CDwFontContext();
-
-  HRESULT Initialize();
-
- private:
-  CDwFontContext(CDwFontContext const&);
-  void operator=(CDwFontContext const&);
-  HRESULT hr_;
-  IDWriteFactory* dwriteFactory_;
-};
-
-class CDwGdiTextRenderer {
- public:
-  CDwGdiTextRenderer(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                     IDWriteBitmapRenderTarget* bitmapRenderTarget,
-                     IDWriteRenderingParams* renderingParams);
-  ~CDwGdiTextRenderer();
-
-  HRESULT STDMETHODCALLTYPE DrawGlyphRun(const FX_RECT& text_bbox,
-                                         __in_opt CFX_ClipRgn* pClipRgn,
-                                         __in_opt DWRITE_MATRIX const* pMatrix,
-                                         FLOAT baselineOriginX,
-                                         FLOAT baselineOriginY,
-                                         DWRITE_MEASURING_MODE measuringMode,
-                                         __in DWRITE_GLYPH_RUN const* glyphRun,
-                                         const COLORREF& textColor);
-
- private:
-  RetainPtr<CFX_DIBitmap> pBitmap_;
-  IDWriteBitmapRenderTarget* pRenderTarget_;
-  IDWriteRenderingParams* pRenderingParams_;
-};
-
-CDWriteExt::CDWriteExt()
-    : m_hModule(nullptr),
-      m_pDWriteFactory(nullptr),
-      m_pDwFontContext(nullptr),
-      m_pDwTextRenderer(nullptr) {}
-
-void CDWriteExt::Load() {}
-
-void CDWriteExt::Unload() {
-  if (m_pDwFontContext) {
-    delete (CDwFontContext*)m_pDwFontContext;
-    m_pDwFontContext = nullptr;
-  }
-  SafeRelease((IDWriteFactory**)&m_pDWriteFactory);
-}
-
-CDWriteExt::~CDWriteExt() {
-  Unload();
-}
-
-LPVOID CDWriteExt::DwCreateFontFaceFromStream(uint8_t* pData,
-                                              uint32_t size,
-                                              int simulation_style) {
-  IDWriteFactory* pDwFactory = (IDWriteFactory*)m_pDWriteFactory;
-  IDWriteFontFile* pDwFontFile = nullptr;
-  IDWriteFontFace* pDwFontFace = nullptr;
-  BOOL isSupportedFontType = false;
-  DWRITE_FONT_FILE_TYPE fontFileType;
-  DWRITE_FONT_FACE_TYPE fontFaceType;
-  UINT32 numberOfFaces;
-  DWRITE_FONT_SIMULATIONS fontStyle =
-      (DWRITE_FONT_SIMULATIONS)(simulation_style & 3);
-  HRESULT hr = S_OK;
-  hr = pDwFactory->CreateCustomFontFileReference(
-      (void const*)pData, (UINT32)size, CDwFontFileLoader::GetLoader(),
-      &pDwFontFile);
-  if (FAILED(hr)) {
-    goto failed;
-  }
-  hr = pDwFontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType,
-                            &numberOfFaces);
-  if (FAILED(hr) || !isSupportedFontType ||
-      fontFaceType == DWRITE_FONT_FACE_TYPE_UNKNOWN) {
-    goto failed;
-  }
-  hr = pDwFactory->CreateFontFace(fontFaceType, 1, &pDwFontFile, 0, fontStyle,
-                                  &pDwFontFace);
-  if (FAILED(hr)) {
-    goto failed;
-  }
-  SafeRelease(&pDwFontFile);
-  return pDwFontFace;
-failed:
-  SafeRelease(&pDwFontFile);
-  return nullptr;
-}
-
-bool CDWriteExt::DwCreateRenderingTarget(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                         void** renderTarget) {
-  if (pBitmap->GetFormat() > FXDIB_Argb) {
-    return false;
-  }
-  IDWriteFactory* pDwFactory = (IDWriteFactory*)m_pDWriteFactory;
-  IDWriteGdiInterop* pGdiInterop = nullptr;
-  IDWriteBitmapRenderTarget* pBitmapRenderTarget = nullptr;
-  IDWriteRenderingParams* pRenderingParams = nullptr;
-  HRESULT hr = S_OK;
-  hr = pDwFactory->GetGdiInterop(&pGdiInterop);
-  if (FAILED(hr)) {
-    goto failed;
-  }
-  hr = pGdiInterop->CreateBitmapRenderTarget(
-      nullptr, pBitmap->GetWidth(), pBitmap->GetHeight(), &pBitmapRenderTarget);
-  if (FAILED(hr)) {
-    goto failed;
-  }
-  hr = pDwFactory->CreateCustomRenderingParams(
-      1.0f, 0.0f, 1.0f, DWRITE_PIXEL_GEOMETRY_RGB,
-      DWRITE_RENDERING_MODE_DEFAULT, &pRenderingParams);
-  if (FAILED(hr)) {
-    goto failed;
-  }
-  hr = pBitmapRenderTarget->SetPixelsPerDip(1.0f);
-  if (FAILED(hr)) {
-    goto failed;
-  }
-  *(CDwGdiTextRenderer**)renderTarget =
-      new CDwGdiTextRenderer(pBitmap, pBitmapRenderTarget, pRenderingParams);
-  SafeRelease(&pGdiInterop);
-  SafeRelease(&pBitmapRenderTarget);
-  SafeRelease(&pRenderingParams);
-  return true;
-failed:
-  SafeRelease(&pGdiInterop);
-  SafeRelease(&pBitmapRenderTarget);
-  SafeRelease(&pRenderingParams);
-  return false;
-}
-
-bool CDWriteExt::DwRendingString(void* renderTarget,
-                                 CFX_ClipRgn* pClipRgn,
-                                 FX_RECT& stringRect,
-                                 CFX_Matrix* pMatrix,
-                                 void* font,
-                                 float font_size,
-                                 FX_ARGB text_color,
-                                 int glyph_count,
-                                 unsigned short* glyph_indices,
-                                 float baselineOriginX,
-                                 float baselineOriginY,
-                                 void* glyph_offsets,
-                                 float* glyph_advances) {
-  if (!renderTarget) {
-    return true;
-  }
-  CDwGdiTextRenderer* pTextRenderer = (CDwGdiTextRenderer*)renderTarget;
-  DWRITE_MATRIX transform;
-  DWRITE_GLYPH_RUN glyphRun;
-  HRESULT hr = S_OK;
-  if (pMatrix) {
-    transform.m11 = pMatrix->a;
-    transform.m12 = pMatrix->b;
-    transform.m21 = pMatrix->c;
-    transform.m22 = pMatrix->d;
-    transform.dx = pMatrix->e;
-    transform.dy = pMatrix->f;
-  }
-  glyphRun.fontFace = (IDWriteFontFace*)font;
-  glyphRun.fontEmSize = font_size;
-  glyphRun.glyphCount = glyph_count;
-  glyphRun.glyphIndices = glyph_indices;
-  glyphRun.glyphAdvances = glyph_advances;
-  glyphRun.glyphOffsets = (DWRITE_GLYPH_OFFSET*)glyph_offsets;
-  glyphRun.isSideways = false;
-  glyphRun.bidiLevel = 0;
-  hr = pTextRenderer->DrawGlyphRun(
-      stringRect, pClipRgn, pMatrix ? &transform : nullptr, baselineOriginX,
-      baselineOriginY, DWRITE_MEASURING_MODE_NATURAL, &glyphRun,
-      RGB(FXARGB_R(text_color), FXARGB_G(text_color), FXARGB_B(text_color)));
-  return SUCCEEDED(hr);
-}
-
-void CDWriteExt::DwDeleteRenderingTarget(void* renderTarget) {
-  delete (CDwGdiTextRenderer*)renderTarget;
-}
-
-void CDWriteExt::DwDeleteFont(void* pFont) {
-  if (pFont) {
-    SafeRelease((IDWriteFontFace**)&pFont);
-  }
-}
-
-CDwFontFileStream::CDwFontFileStream(void const* fontFileReferenceKey,
-                                     UINT32 fontFileReferenceKeySize) {
-  refCount_ = 0;
-  resourcePtr_ = fontFileReferenceKey;
-  resourceSize_ = fontFileReferenceKeySize;
-}
-
-HRESULT STDMETHODCALLTYPE CDwFontFileStream::QueryInterface(REFIID iid,
-                                                            void** ppvObject) {
-  if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
-    *ppvObject = this;
-    AddRef();
-    return S_OK;
-  }
-  *ppvObject = nullptr;
-  return E_NOINTERFACE;
-}
-
-ULONG STDMETHODCALLTYPE CDwFontFileStream::AddRef() {
-  return InterlockedIncrement((long*)(&refCount_));
-}
-
-ULONG STDMETHODCALLTYPE CDwFontFileStream::Release() {
-  ULONG newCount = InterlockedDecrement((long*)(&refCount_));
-  if (newCount == 0) {
-    delete this;
-  }
-  return newCount;
-}
-
-HRESULT STDMETHODCALLTYPE
-CDwFontFileStream::ReadFileFragment(void const** fragmentStart,
-                                    UINT64 fileOffset,
-                                    UINT64 fragmentSize,
-                                    OUT void** fragmentContext) {
-  if (fileOffset <= resourceSize_ &&
-      fragmentSize <= resourceSize_ - fileOffset) {
-    *fragmentStart = static_cast<uint8_t const*>(resourcePtr_) +
-                     static_cast<size_t>(fileOffset);
-    *fragmentContext = nullptr;
-    return S_OK;
-  }
-  *fragmentStart = nullptr;
-  *fragmentContext = nullptr;
-  return E_FAIL;
-}
-
-void STDMETHODCALLTYPE
-CDwFontFileStream::ReleaseFileFragment(void* fragmentContext) {}
-HRESULT STDMETHODCALLTYPE CDwFontFileStream::GetFileSize(OUT UINT64* fileSize) {
-  *fileSize = resourceSize_;
-  return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE
-CDwFontFileStream::GetLastWriteTime(OUT UINT64* lastWriteTime) {
-  *lastWriteTime = 0;
-  return E_NOTIMPL;
-}
-
-IDWriteFontFileLoader* CDwFontFileLoader::instance_ = nullptr;
-CDwFontFileLoader::CDwFontFileLoader() : refCount_(0) {}
-HRESULT STDMETHODCALLTYPE CDwFontFileLoader::QueryInterface(REFIID iid,
-                                                            void** ppvObject) {
-  if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
-    *ppvObject = this;
-    AddRef();
-    return S_OK;
-  }
-  *ppvObject = nullptr;
-  return E_NOINTERFACE;
-}
-
-ULONG STDMETHODCALLTYPE CDwFontFileLoader::AddRef() {
-  return InterlockedIncrement((long*)(&refCount_));
-}
-
-ULONG STDMETHODCALLTYPE CDwFontFileLoader::Release() {
-  ULONG newCount = InterlockedDecrement((long*)(&refCount_));
-  if (newCount == 0) {
-    instance_ = nullptr;
-    delete this;
-  }
-  return newCount;
-}
-
-HRESULT STDMETHODCALLTYPE CDwFontFileLoader::CreateStreamFromKey(
-    void const* fontFileReferenceKey,
-    UINT32 fontFileReferenceKeySize,
-    OUT IDWriteFontFileStream** fontFileStream) {
-  *fontFileStream = nullptr;
-  CDwFontFileStream* stream =
-      new CDwFontFileStream(fontFileReferenceKey, fontFileReferenceKeySize);
-  if (!stream->IsInitialized()) {
-    delete stream;
-    return E_FAIL;
-  }
-  *fontFileStream = SafeAcquire(stream);
-  return S_OK;
-}
-
-CDwFontContext::CDwFontContext(IDWriteFactory* dwriteFactory)
-    : hr_(S_FALSE), dwriteFactory_(SafeAcquire(dwriteFactory)) {}
-
-CDwFontContext::~CDwFontContext() {
-  if (dwriteFactory_ && hr_ == S_OK) {
-    dwriteFactory_->UnregisterFontFileLoader(CDwFontFileLoader::GetLoader());
-  }
-  SafeRelease(&dwriteFactory_);
-}
-
-HRESULT CDwFontContext::Initialize() {
-  if (hr_ == S_FALSE) {
-    return hr_ = dwriteFactory_->RegisterFontFileLoader(
-               CDwFontFileLoader::GetLoader());
-  }
-  return hr_;
-}
-
-CDwGdiTextRenderer::CDwGdiTextRenderer(
-    const RetainPtr<CFX_DIBitmap>& pBitmap,
-    IDWriteBitmapRenderTarget* bitmapRenderTarget,
-    IDWriteRenderingParams* renderingParams)
-    : pBitmap_(pBitmap),
-      pRenderTarget_(SafeAcquire(bitmapRenderTarget)),
-      pRenderingParams_(SafeAcquire(renderingParams)) {}
-CDwGdiTextRenderer::~CDwGdiTextRenderer() {
-  SafeRelease(&pRenderTarget_);
-  SafeRelease(&pRenderingParams_);
-}
-
-STDMETHODIMP CDwGdiTextRenderer::DrawGlyphRun(
-    const FX_RECT& text_bbox,
-    __in_opt CFX_ClipRgn* pClipRgn,
-    __in_opt DWRITE_MATRIX const* pMatrix,
-    FLOAT baselineOriginX,
-    FLOAT baselineOriginY,
-    DWRITE_MEASURING_MODE measuringMode,
-    __in DWRITE_GLYPH_RUN const* glyphRun,
-    const COLORREF& textColor) {
-  HRESULT hr = S_OK;
-  if (pMatrix) {
-    hr = pRenderTarget_->SetCurrentTransform(pMatrix);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-  HDC hDC = pRenderTarget_->GetMemoryDC();
-  HBITMAP hBitmap = (HBITMAP)::GetCurrentObject(hDC, OBJ_BITMAP);
-  BITMAP bitmap;
-  GetObject(hBitmap, sizeof bitmap, &bitmap);
-  auto dib = pdfium::MakeRetain<CFX_DIBitmap>();
-  dib->Create(bitmap.bmWidth, bitmap.bmHeight,
-              bitmap.bmBitsPixel == 24 ? FXDIB_Rgb : FXDIB_Rgb32,
-              (uint8_t*)bitmap.bmBits);
-  dib->CompositeBitmap(text_bbox.left, text_bbox.top, text_bbox.Width(),
-                       text_bbox.Height(), pBitmap_, text_bbox.left,
-                       text_bbox.top, FXDIB_BLEND_NORMAL, nullptr);
-  hr = pRenderTarget_->DrawGlyphRun(baselineOriginX, baselineOriginY,
-                                    measuringMode, glyphRun, pRenderingParams_,
-                                    textColor);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  pBitmap_->CompositeBitmap(text_bbox.left, text_bbox.top, text_bbox.Width(),
-                            text_bbox.Height(), dib, text_bbox.left,
-                            text_bbox.top, FXDIB_BLEND_NORMAL, pClipRgn);
-  return hr;
-}
diff --git a/core/fxge/win32/fx_win32_gdipext.cpp b/core/fxge/win32/fx_win32_gdipext.cpp
index a617036..272e408 100644
--- a/core/fxge/win32/fx_win32_gdipext.cpp
+++ b/core/fxge/win32/fx_win32_gdipext.cpp
@@ -6,9 +6,12 @@
 
 #include <windows.h>
 
+#include <objidl.h>
+
 #include <algorithm>
 #include <memory>
 #include <sstream>
+#include <utility>
 
 #include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_gemodule.h"
@@ -16,86 +19,21 @@
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/win32/cfx_windowsdib.h"
 #include "core/fxge/win32/win32_int.h"
-#include "third_party/base/ptr_util.h"
 
 // Has to come before gdiplus.h
 namespace Gdiplus {
-using std::min;
 using std::max;
+using std::min;
 }  // namespace Gdiplus
 
 #include <gdiplus.h>  // NOLINT
 
-// TODO(thestig): Remove the infrequently used ones.
-using Gdiplus::CombineMode;
-using Gdiplus::DashCap;
-using Gdiplus::DashCapFlat;
-using Gdiplus::DashCapRound;
-using Gdiplus::FillModeAlternate;
-using Gdiplus::FillModeWinding;
-using Gdiplus::GdiplusStartupInput;
-using Gdiplus::GdiplusStartupOutput;
-using Gdiplus::GpBitmap;
-using Gdiplus::GpBrush;
-using Gdiplus::GpDashCap;
-using Gdiplus::GpDashStyle;
-using Gdiplus::GpFillMode;
-using Gdiplus::GpFont;
-using Gdiplus::GpFontCollection;
-using Gdiplus::GpFontFamily;
-using Gdiplus::GpGraphics;
-using Gdiplus::GpImage;
-using Gdiplus::GpLineCap;
-using Gdiplus::GpLineJoin;
-using Gdiplus::GpMatrix;
-using Gdiplus::GpPath;
-using Gdiplus::GpPen;
-using Gdiplus::GpPoint;
-using Gdiplus::GpPointF;
-using Gdiplus::GpRect;
-using Gdiplus::GpRegion;
-using Gdiplus::GpSolidFill;
-using Gdiplus::GpStatus;
-using Gdiplus::GpStringFormat;
-using Gdiplus::GpUnit;
-using Gdiplus::ImageLockModeRead;
-using Gdiplus::InterpolationMode;
-using Gdiplus::InterpolationModeBilinear;
-using Gdiplus::InterpolationModeHighQuality;
-using Gdiplus::InterpolationModeNearestNeighbor;
-using Gdiplus::LineCap;
-using Gdiplus::LineCapFlat;
-using Gdiplus::LineCapRound;
-using Gdiplus::LineCapSquare;
-using Gdiplus::LineJoin;
-using Gdiplus::LineJoinBevel;
-using Gdiplus::LineJoinMiterClipped;
-using Gdiplus::LineJoinRound;
-using Gdiplus::PaletteFlagsHasAlpha;
-using Gdiplus::PathPointTypeBezier;
-using Gdiplus::PathPointTypeCloseSubpath;
-using Gdiplus::PathPointTypeLine;
-using Gdiplus::PathPointTypeStart;
-using Gdiplus::PixelOffsetMode;
-using Gdiplus::PixelOffsetModeHalf;
-using Gdiplus::REAL;
-using Gdiplus::SmoothingMode;
-using Gdiplus::SmoothingModeAntiAlias;
-using Gdiplus::SmoothingModeNone;
-using Gdiplus::TextRenderingHint;
-using Gdiplus::UnitPixel;
-using Gdiplus::UnitWorld;
-
-#define GdiFillType2Gdip(fill_type) \
-  (fill_type == ALTERNATE ? FillModeAlternate : FillModeWinding)
+namespace {
 
 enum {
   FuncId_GdipCreatePath2,
-  FuncId_GdipSetPenDashStyle,
   FuncId_GdipSetPenDashArray,
-  FuncId_GdipSetPenDashCap197819,
   FuncId_GdipSetPenLineJoin,
-  FuncId_GdipSetPenWidth,
   FuncId_GdipCreateFromHDC,
   FuncId_GdipSetPageUnit,
   FuncId_GdipSetSmoothingMode,
@@ -118,65 +56,31 @@
   FuncId_GdipGetImagePalette,
   FuncId_GdipBitmapUnlockBits,
   FuncId_GdipDisposeImage,
-  FuncId_GdipFillRectangle,
   FuncId_GdipCreateBitmapFromScan0,
   FuncId_GdipSetImagePalette,
   FuncId_GdipSetInterpolationMode,
   FuncId_GdipDrawImagePointsI,
-  FuncId_GdipCreateBitmapFromGdiDib,
   FuncId_GdiplusStartup,
   FuncId_GdipDrawLineI,
-  FuncId_GdipResetClip,
   FuncId_GdipCreatePath,
-  FuncId_GdipAddPathPath,
   FuncId_GdipSetPathFillMode,
-  FuncId_GdipSetClipPath,
-  FuncId_GdipGetClip,
-  FuncId_GdipCreateRegion,
-  FuncId_GdipGetClipBoundsI,
   FuncId_GdipSetClipRegion,
   FuncId_GdipWidenPath,
   FuncId_GdipAddPathLine,
   FuncId_GdipAddPathRectangle,
   FuncId_GdipDeleteRegion,
-  FuncId_GdipGetDC,
-  FuncId_GdipReleaseDC,
   FuncId_GdipSetPenLineCap197819,
   FuncId_GdipSetPenDashOffset,
-  FuncId_GdipResetPath,
-  FuncId_GdipCreateRegionPath,
-  FuncId_GdipCreateFont,
-  FuncId_GdipGetFontSize,
-  FuncId_GdipCreateFontFamilyFromName,
-  FuncId_GdipSetTextRenderingHint,
-  FuncId_GdipDrawDriverString,
   FuncId_GdipCreateMatrix2,
   FuncId_GdipDeleteMatrix,
   FuncId_GdipSetWorldTransform,
-  FuncId_GdipResetWorldTransform,
-  FuncId_GdipDeleteFontFamily,
-  FuncId_GdipDeleteFont,
-  FuncId_GdipNewPrivateFontCollection,
-  FuncId_GdipDeletePrivateFontCollection,
-  FuncId_GdipPrivateAddMemoryFont,
-  FuncId_GdipGetFontCollectionFamilyList,
-  FuncId_GdipGetFontCollectionFamilyCount,
-  FuncId_GdipSetTextContrast,
   FuncId_GdipSetPixelOffsetMode,
-  FuncId_GdipGetImageGraphicsContext,
-  FuncId_GdipDrawImageI,
-  FuncId_GdipDrawImageRectI,
-  FuncId_GdipDrawString,
-  FuncId_GdipSetPenTransform,
 };
 
-static LPCSTR g_GdipFuncNames[] = {
+LPCSTR g_GdipFuncNames[] = {
     "GdipCreatePath2",
-    "GdipSetPenDashStyle",
     "GdipSetPenDashArray",
-    "GdipSetPenDashCap197819",
     "GdipSetPenLineJoin",
-    "GdipSetPenWidth",
     "GdipCreateFromHDC",
     "GdipSetPageUnit",
     "GdipSetSmoothingMode",
@@ -199,485 +103,225 @@
     "GdipGetImagePalette",
     "GdipBitmapUnlockBits",
     "GdipDisposeImage",
-    "GdipFillRectangle",
     "GdipCreateBitmapFromScan0",
     "GdipSetImagePalette",
     "GdipSetInterpolationMode",
     "GdipDrawImagePointsI",
-    "GdipCreateBitmapFromGdiDib",
     "GdiplusStartup",
     "GdipDrawLineI",
-    "GdipResetClip",
     "GdipCreatePath",
-    "GdipAddPathPath",
     "GdipSetPathFillMode",
-    "GdipSetClipPath",
-    "GdipGetClip",
-    "GdipCreateRegion",
-    "GdipGetClipBoundsI",
     "GdipSetClipRegion",
     "GdipWidenPath",
     "GdipAddPathLine",
     "GdipAddPathRectangle",
     "GdipDeleteRegion",
-    "GdipGetDC",
-    "GdipReleaseDC",
     "GdipSetPenLineCap197819",
     "GdipSetPenDashOffset",
-    "GdipResetPath",
-    "GdipCreateRegionPath",
-    "GdipCreateFont",
-    "GdipGetFontSize",
-    "GdipCreateFontFamilyFromName",
-    "GdipSetTextRenderingHint",
-    "GdipDrawDriverString",
     "GdipCreateMatrix2",
     "GdipDeleteMatrix",
     "GdipSetWorldTransform",
-    "GdipResetWorldTransform",
-    "GdipDeleteFontFamily",
-    "GdipDeleteFont",
-    "GdipNewPrivateFontCollection",
-    "GdipDeletePrivateFontCollection",
-    "GdipPrivateAddMemoryFont",
-    "GdipGetFontCollectionFamilyList",
-    "GdipGetFontCollectionFamilyCount",
-    "GdipSetTextContrast",
     "GdipSetPixelOffsetMode",
-    "GdipGetImageGraphicsContext",
-    "GdipDrawImageI",
-    "GdipDrawImageRectI",
-    "GdipDrawString",
-    "GdipSetPenTransform",
 };
 static_assert(FX_ArraySize(g_GdipFuncNames) ==
-                  static_cast<size_t>(FuncId_GdipSetPenTransform) + 1,
+                  static_cast<size_t>(FuncId_GdipSetPixelOffsetMode) + 1,
               "g_GdipFuncNames has wrong size");
 
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePath2)(GDIPCONST GpPointF*,
-                                                       GDIPCONST BYTE*,
-                                                       INT,
-                                                       GpFillMode,
-                                                       GpPath** path);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashStyle)(
-    GpPen* pen,
-    GpDashStyle dashstyle);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashArray)(GpPen* pen,
-                                                           GDIPCONST REAL* dash,
-                                                           INT count);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashCap197819)(
-    GpPen* pen,
-    GpDashCap dashCap);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineJoin)(GpPen* pen,
-                                                          GpLineJoin lineJoin);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenWidth)(GpPen* pen, REAL width);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFromHDC)(HDC hdc,
-                                                         GpGraphics** graphics);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPageUnit)(GpGraphics* graphics,
-                                                       GpUnit unit);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetSmoothingMode)(
-    GpGraphics* graphics,
-    SmoothingMode smoothingMode);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateSolidFill)(Gdiplus::ARGB color,
-                                                           GpSolidFill** brush);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipFillPath)(GpGraphics* graphics,
-                                                    GpBrush* brush,
-                                                    GpPath* path);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteBrush)(GpBrush* brush);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePen1)(Gdiplus::ARGB color,
-                                                      REAL width,
-                                                      GpUnit unit,
-                                                      GpPen** pen);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenMiterLimit)(GpPen* pen,
-                                                            REAL miterLimit);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawPath)(GpGraphics* graphics,
-                                                    GpPen* pen,
-                                                    GpPath* path);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePen)(GpPen* pen);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePath)(GpPath* path);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteGraphics)(GpGraphics* graphics);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromFileICM)(
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath2)(
+    GDIPCONST Gdiplus::GpPointF*,
+    GDIPCONST BYTE*,
+    INT,
+    Gdiplus::GpFillMode,
+    Gdiplus::GpPath** path);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashArray)(
+    Gdiplus::GpPen* pen,
+    GDIPCONST Gdiplus::REAL* dash,
+    INT count);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineJoin)(
+    Gdiplus::GpPen* pen,
+    Gdiplus::GpLineJoin lineJoin);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateFromHDC)(
+    HDC hdc,
+    Gdiplus::GpGraphics** graphics);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPageUnit)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::GpUnit unit);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetSmoothingMode)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::SmoothingMode smoothingMode);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateSolidFill)(
+    Gdiplus::ARGB color,
+    Gdiplus::GpSolidFill** brush);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipFillPath)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::GpBrush* brush,
+    Gdiplus::GpPath* path);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteBrush)(
+    Gdiplus::GpBrush* brush);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePen1)(
+    Gdiplus::ARGB color,
+    Gdiplus::REAL width,
+    Gdiplus::GpUnit unit,
+    Gdiplus::GpPen** pen);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenMiterLimit)(
+    Gdiplus::GpPen* pen,
+    Gdiplus::REAL miterLimit);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawPath)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::GpPen* pen,
+    Gdiplus::GpPath* path);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePen)(
+    Gdiplus::GpPen* pen);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePath)(
+    Gdiplus::GpPath* path);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteGraphics)(
+    Gdiplus::GpGraphics* graphics);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromFileICM)(
     GDIPCONST WCHAR* filename,
-    GpBitmap** bitmap);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromStreamICM)(
+    Gdiplus::GpBitmap** bitmap);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromStreamICM)(
     IStream* stream,
-    GpBitmap** bitmap);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageWidth)(GpImage* image,
-                                                         UINT* width);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageHeight)(GpImage* image,
-                                                          UINT* height);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePixelFormat)(
-    GpImage* image,
+    Gdiplus::GpBitmap** bitmap);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageWidth)(
+    Gdiplus::GpImage* image,
+    UINT* width);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageHeight)(
+    Gdiplus::GpImage* image,
+    UINT* height);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePixelFormat)(
+    Gdiplus::GpImage* image,
     Gdiplus::PixelFormat* format);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipBitmapLockBits)(
-    GpBitmap* bitmap,
-    GDIPCONST GpRect* rect,
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapLockBits)(
+    Gdiplus::GpBitmap* bitmap,
+    GDIPCONST Gdiplus::GpRect* rect,
     UINT flags,
     Gdiplus::PixelFormat format,
     Gdiplus::BitmapData* lockedBitmapData);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePalette)(
-    GpImage* image,
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePalette)(
+    Gdiplus::GpImage* image,
     Gdiplus::ColorPalette* palette,
     INT size);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePaletteSize)(GpImage* image,
-                                                               INT* size);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipBitmapUnlockBits)(
-    GpBitmap* bitmap,
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePaletteSize)(
+    Gdiplus::GpImage* image,
+    INT* size);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapUnlockBits)(
+    Gdiplus::GpBitmap* bitmap,
     Gdiplus::BitmapData* lockedBitmapData);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDisposeImage)(GpImage* image);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipFillRectangle)(GpGraphics* graphics,
-                                                         GpBrush* brush,
-                                                         REAL x,
-                                                         REAL y,
-                                                         REAL width,
-                                                         REAL height);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromScan0)(
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDisposeImage)(
+    Gdiplus::GpImage* image);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromScan0)(
     INT width,
     INT height,
     INT stride,
     Gdiplus::PixelFormat format,
     BYTE* scan0,
-    GpBitmap** bitmap);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetImagePalette)(
-    GpImage* image,
+    Gdiplus::GpBitmap** bitmap);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetImagePalette)(
+    Gdiplus::GpImage* image,
     GDIPCONST Gdiplus::ColorPalette* palette);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetInterpolationMode)(
-    GpGraphics* graphics,
-    InterpolationMode interpolationMode);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImagePointsI)(
-    GpGraphics* graphics,
-    GpImage* image,
-    GDIPCONST GpPoint* dstpoints,
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetInterpolationMode)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::InterpolationMode interpolationMode);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawImagePointsI)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::GpImage* image,
+    GDIPCONST Gdiplus::GpPoint* dstpoints,
     INT count);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromGdiDib)(
-    GDIPCONST BITMAPINFO* gdiBitmapInfo,
-    VOID* gdiBitmapData,
-    GpBitmap** bitmap);
 typedef Gdiplus::Status(WINAPI* FuncType_GdiplusStartup)(
     OUT uintptr_t* token,
-    const GdiplusStartupInput* input,
-    OUT GdiplusStartupOutput* output);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawLineI)(GpGraphics* graphics,
-                                                     GpPen* pen,
-                                                     int x1,
-                                                     int y1,
-                                                     int x2,
-                                                     int y2);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipResetClip)(GpGraphics* graphics);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePath)(GpFillMode brushMode,
-                                                      GpPath** path);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathPath)(
-    GpPath* path,
-    GDIPCONST GpPath* addingPath,
-    BOOL connect);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPathFillMode)(GpPath* path,
-                                                           GpFillMode fillmode);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetClipPath)(GpGraphics* graphics,
-                                                       GpPath* path,
-                                                       CombineMode combineMode);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetClip)(GpGraphics* graphics,
-                                                   GpRegion* region);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateRegion)(GpRegion** region);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetClipBoundsI)(GpGraphics* graphics,
-                                                          GpRect* rect);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetClipRegion)(
-    GpGraphics* graphics,
-    GpRegion* region,
-    CombineMode combineMode);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipWidenPath)(GpPath* nativePath,
-                                                     GpPen* pen,
-                                                     GpMatrix* matrix,
-                                                     REAL flatness);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathLine)(GpPath* path,
-                                                       REAL x1,
-                                                       REAL y1,
-                                                       REAL x2,
-                                                       REAL y2);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathRectangle)(GpPath* path,
-                                                            REAL x,
-                                                            REAL y,
-                                                            REAL width,
-                                                            REAL height);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteRegion)(GpRegion* region);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetDC)(GpGraphics* graphics,
-                                                 HDC* hdc);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipReleaseDC)(GpGraphics* graphics,
-                                                     HDC hdc);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineCap197819)(
-    GpPen* pen,
-    GpLineCap startCap,
-    GpLineCap endCap,
-    GpDashCap dashCap);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashOffset)(GpPen* pen,
-                                                            REAL offset);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipResetPath)(GpPath* path);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateRegionPath)(GpPath* path,
-                                                            GpRegion** region);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFont)(
-    GDIPCONST GpFontFamily* fontFamily,
-    REAL emSize,
-    INT style,
-    Gdiplus::Unit unit,
-    GpFont** font);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontSize)(GpFont* font,
-                                                       REAL* size);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFontFamilyFromName)(
-    GDIPCONST WCHAR* name,
-    GpFontCollection* fontCollection,
-    GpFontFamily** FontFamily);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetTextRenderingHint)(
-    GpGraphics* graphics,
-    TextRenderingHint mode);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawDriverString)(
-    GpGraphics* graphics,
-    GDIPCONST UINT16* text,
-    INT length,
-    GDIPCONST GpFont* font,
-    GDIPCONST GpBrush* brush,
-    GDIPCONST Gdiplus::PointF* positions,
-    INT flags,
-    GDIPCONST GpMatrix* matrix);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateMatrix2)(REAL m11,
-                                                         REAL m12,
-                                                         REAL m21,
-                                                         REAL m22,
-                                                         REAL dx,
-                                                         REAL dy,
-                                                         GpMatrix** matrix);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteMatrix)(GpMatrix* matrix);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetWorldTransform)(
-    GpGraphics* graphics,
-    GpMatrix* matrix);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipResetWorldTransform)(
-    GpGraphics* graphics);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteFontFamily)(
-    GpFontFamily* FontFamily);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteFont)(GpFont* font);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipNewPrivateFontCollection)(
-    GpFontCollection** fontCollection);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePrivateFontCollection)(
-    GpFontCollection** fontCollection);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipPrivateAddMemoryFont)(
-    GpFontCollection* fontCollection,
-    GDIPCONST void* memory,
-    INT length);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontCollectionFamilyList)(
-    GpFontCollection* fontCollection,
-    INT numSought,
-    GpFontFamily* gpfamilies[],
-    INT* numFound);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontCollectionFamilyCount)(
-    GpFontCollection* fontCollection,
-    INT* numFound);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetTextContrast)(GpGraphics* graphics,
-                                                           UINT contrast);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPixelOffsetMode)(
-    GpGraphics* graphics,
-    PixelOffsetMode pixelOffsetMode);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageGraphicsContext)(
-    GpImage* image,
-    GpGraphics** graphics);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImageI)(GpGraphics* graphics,
-                                                      GpImage* image,
-                                                      INT x,
-                                                      INT y);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImageRectI)(GpGraphics* graphics,
-                                                          GpImage* image,
-                                                          INT x,
-                                                          INT y,
-                                                          INT width,
-                                                          INT height);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawString)(
-    GpGraphics* graphics,
-    GDIPCONST WCHAR* str,
-    INT length,
-    GDIPCONST GpFont* font,
-    GDIPCONST Gdiplus::RectF* layoutRect,
-    GDIPCONST GpStringFormat* stringFormat,
-    GDIPCONST GpBrush* brush);
-typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenTransform)(GpPen* pen,
-                                                           GpMatrix* matrix);
-#define CallFunc(funcname) \
-  ((FuncType_##funcname)GdiplusExt.m_Functions[FuncId_##funcname])
+    const Gdiplus::GdiplusStartupInput* input,
+    OUT Gdiplus::GdiplusStartupOutput* output);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawLineI)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::GpPen* pen,
+    int x1,
+    int y1,
+    int x2,
+    int y2);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath)(
+    Gdiplus::GpFillMode brushMode,
+    Gdiplus::GpPath** path);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPathFillMode)(
+    Gdiplus::GpPath* path,
+    Gdiplus::GpFillMode fillmode);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetClipRegion)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::GpRegion* region,
+    Gdiplus::CombineMode combineMode);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipWidenPath)(
+    Gdiplus::GpPath* nativePath,
+    Gdiplus::GpPen* pen,
+    Gdiplus::GpMatrix* matrix,
+    Gdiplus::REAL flatness);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathLine)(
+    Gdiplus::GpPath* path,
+    Gdiplus::REAL x1,
+    Gdiplus::REAL y1,
+    Gdiplus::REAL x2,
+    Gdiplus::REAL y2);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathRectangle)(
+    Gdiplus::GpPath* path,
+    Gdiplus::REAL x,
+    Gdiplus::REAL y,
+    Gdiplus::REAL width,
+    Gdiplus::REAL height);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteRegion)(
+    Gdiplus::GpRegion* region);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineCap197819)(
+    Gdiplus::GpPen* pen,
+    Gdiplus::GpLineCap startCap,
+    Gdiplus::GpLineCap endCap,
+    Gdiplus::GpDashCap dashCap);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashOffset)(
+    Gdiplus::GpPen* pen,
+    Gdiplus::REAL offset);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateMatrix2)(
+    Gdiplus::REAL m11,
+    Gdiplus::REAL m12,
+    Gdiplus::REAL m21,
+    Gdiplus::REAL m22,
+    Gdiplus::REAL dx,
+    Gdiplus::REAL dy,
+    Gdiplus::GpMatrix** matrix);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteMatrix)(
+    Gdiplus::GpMatrix* matrix);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetWorldTransform)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::GpMatrix* matrix);
+typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPixelOffsetMode)(
+    Gdiplus::GpGraphics* graphics,
+    Gdiplus::PixelOffsetMode pixelOffsetMode);
+#define CallFunc(funcname)               \
+  reinterpret_cast<FuncType_##funcname>( \
+      GdiplusExt.m_Functions[FuncId_##funcname])
 
-void* CGdiplusExt::GdiAddFontMemResourceEx(void* pFontdata,
-                                           uint32_t size,
-                                           void* pdv,
-                                           uint32_t* num_face) {
-  if (!m_pGdiAddFontMemResourceEx)
-    return nullptr;
-
-  return m_pGdiAddFontMemResourceEx((PVOID)pFontdata, (DWORD)size, (PVOID)pdv,
-                                    (DWORD*)num_face);
+Gdiplus::GpFillMode GdiFillType2Gdip(int fill_type) {
+  return fill_type == ALTERNATE ? Gdiplus::FillModeAlternate
+                                : Gdiplus::FillModeWinding;
 }
 
-bool CGdiplusExt::GdiRemoveFontMemResourceEx(void* handle) {
-  return m_pGdiRemoveFontMemResourseEx &&
-         m_pGdiRemoveFontMemResourseEx((HANDLE)handle);
+const CGdiplusExt& GetGdiplusExt() {
+  auto* pData =
+      static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
+  return pData->m_GdiplusExt;
 }
 
-static GpBrush* _GdipCreateBrush(DWORD argb) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  GpSolidFill* solidBrush = nullptr;
+Gdiplus::GpBrush* GdipCreateBrushImpl(DWORD argb) {
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  Gdiplus::GpSolidFill* solidBrush = nullptr;
   CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)argb, &solidBrush);
   return solidBrush;
 }
 
-static RetainPtr<CFX_DIBitmap> StretchMonoToGray(
-    int dest_width,
-    int dest_height,
-    const RetainPtr<CFX_DIBitmap>& pSource,
-    FX_RECT* pClipRect) {
-  bool bFlipX = dest_width < 0;
-  if (bFlipX)
-    dest_width = -dest_width;
-
-  bool bFlipY = dest_height < 0;
-  if (bFlipY)
-    dest_height = -dest_height;
-
-  int result_width = pClipRect->Width();
-  int result_height = pClipRect->Height();
-  int result_pitch = (result_width + 3) / 4 * 4;
-  auto pStretched = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb))
-    return nullptr;
-
-  LPBYTE dest_buf = pStretched->GetBuffer();
-  int src_width = pSource->GetWidth();
-  int src_height = pSource->GetHeight();
-  int y_unit = src_height / dest_height;
-  int x_unit = src_width / dest_width;
-  int area_unit = y_unit * x_unit;
-  LPBYTE src_buf = pSource->GetBuffer();
-  int src_pitch = pSource->GetPitch();
-  for (int dest_y = 0; dest_y < result_height; dest_y++) {
-    LPBYTE dest_scan = dest_buf + dest_y * result_pitch;
-    int src_y_start = bFlipY ? (dest_height - 1 - dest_y - pClipRect->top)
-                             : (dest_y + pClipRect->top);
-    src_y_start = src_y_start * src_height / dest_height;
-    LPBYTE src_scan = src_buf + src_y_start * src_pitch;
-    for (int dest_x = 0; dest_x < result_width; dest_x++) {
-      int sum = 0;
-      int src_x_start = bFlipX ? (dest_width - 1 - dest_x - pClipRect->left)
-                               : (dest_x + pClipRect->left);
-      src_x_start = src_x_start * src_width / dest_width;
-      int src_x_end = src_x_start + x_unit;
-      LPBYTE src_line = src_scan;
-      for (int src_y = 0; src_y < y_unit; src_y++) {
-        for (int src_x = src_x_start; src_x < src_x_end; src_x++) {
-          if (!(src_line[src_x / 8] & (1 << (7 - src_x % 8)))) {
-            sum += 255;
-          }
-        }
-        src_line += src_pitch;
-      }
-      dest_scan[dest_x] = 255 - sum / area_unit;
-    }
-  }
-  return pStretched;
-}
-
-static void OutputImageMask(GpGraphics* pGraphics,
-                            BOOL bMonoDevice,
-                            const RetainPtr<CFX_DIBitmap>& pBitmap,
-                            int dest_left,
-                            int dest_top,
-                            int dest_width,
-                            int dest_height,
-                            FX_ARGB argb,
-                            const FX_RECT* pClipRect) {
-  ASSERT(pBitmap->GetBPP() == 1);
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  int src_width = pBitmap->GetWidth(), src_height = pBitmap->GetHeight();
-  int src_pitch = pBitmap->GetPitch();
-  uint8_t* scan0 = pBitmap->GetBuffer();
-  if (src_width == 1 && src_height == 1) {
-    if ((scan0[0] & 0x80) == 0)
-      return;
-
-    GpSolidFill* solidBrush;
-    CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)argb, &solidBrush);
-    if (dest_width < 0) {
-      dest_width = -dest_width;
-      dest_left -= dest_width;
-    }
-    if (dest_height < 0) {
-      dest_height = -dest_height;
-      dest_top -= dest_height;
-    }
-    CallFunc(GdipFillRectangle)(pGraphics, solidBrush, (float)dest_left,
-                                (float)dest_top, (float)dest_width,
-                                (float)dest_height);
-    CallFunc(GdipDeleteBrush)(solidBrush);
-    return;
-  }
-  if (!bMonoDevice && abs(dest_width) < src_width &&
-      abs(dest_height) < src_height) {
-    FX_RECT image_rect(dest_left, dest_top, dest_left + dest_width,
-                       dest_top + dest_height);
-    image_rect.Normalize();
-    FX_RECT image_clip = image_rect;
-    image_clip.Intersect(*pClipRect);
-    if (image_clip.IsEmpty())
-      return;
-
-    image_clip.Offset(-image_rect.left, -image_rect.top);
-    RetainPtr<CFX_DIBitmap> pStretched;
-    if (src_width * src_height > 10000) {
-      pStretched =
-          StretchMonoToGray(dest_width, dest_height, pBitmap, &image_clip);
-    } else {
-      pStretched =
-          pBitmap->StretchTo(dest_width, dest_height, false, &image_clip);
-    }
-    GpBitmap* bitmap;
-    CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),
-                                        (image_clip.Width() + 3) / 4 * 4,
-                                        PixelFormat8bppIndexed,
-                                        pStretched->GetBuffer(), &bitmap);
-    int a;
-    int r;
-    int g;
-    int b;
-    std::tie(a, r, g, b) = ArgbDecode(argb);
-    UINT pal[258];
-    pal[0] = 0;
-    pal[1] = 256;
-    for (int i = 0; i < 256; i++) {
-      pal[i + 2] = ArgbEncode(i * a / 255, r, g, b);
-    }
-    CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal);
-    CallFunc(GdipDrawImageI)(pGraphics, bitmap,
-                             image_rect.left + image_clip.left,
-                             image_rect.top + image_clip.top);
-    CallFunc(GdipDisposeImage)(bitmap);
-    return;
-  }
-  GpBitmap* bitmap;
-  CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
-                                      PixelFormat1bppIndexed, scan0, &bitmap);
-  UINT palette[4] = {PaletteFlagsHasAlpha, 2, 0, argb};
-  CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)palette);
-  Gdiplus::Point destinationPoints[] = {
-      Gdiplus::Point(dest_left, dest_top),
-      Gdiplus::Point(dest_left + dest_width, dest_top),
-      Gdiplus::Point(dest_left, dest_top + dest_height)};
-  CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
-  CallFunc(GdipDisposeImage)(bitmap);
-}
-static void OutputImage(GpGraphics* pGraphics,
-                        const RetainPtr<CFX_DIBitmap>& pBitmap,
-                        const FX_RECT* pSrcRect,
-                        int dest_left,
-                        int dest_top,
-                        int dest_width,
-                        int dest_height) {
+void OutputImage(Gdiplus::GpGraphics* pGraphics,
+                 const RetainPtr<CFX_DIBitmap>& pBitmap,
+                 const FX_RECT* pSrcRect,
+                 int dest_left,
+                 int dest_top,
+                 int dest_width,
+                 int dest_height) {
   int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
   if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
     FX_RECT new_rect(0, 0, src_width, src_height);
     RetainPtr<CFX_DIBitmap> pCloned = pBitmap->Clone(pSrcRect);
@@ -690,7 +334,7 @@
   int src_pitch = pBitmap->GetPitch();
   uint8_t* scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch +
                    pBitmap->GetBPP() * pSrcRect->left / 8;
-  GpBitmap* bitmap = nullptr;
+  Gdiplus::GpBitmap* bitmap = nullptr;
   switch (pBitmap->GetFormat()) {
     case FXDIB_Argb:
       CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
@@ -737,341 +381,61 @@
   CallFunc(GdipDisposeImage)(bitmap);
 }
 
-CGdiplusExt::CGdiplusExt() {}
-
-CGdiplusExt::~CGdiplusExt() {
-  FreeLibrary(m_GdiModule);
-  FreeLibrary(m_hModule);
-}
-
-void CGdiplusExt::Load() {
-  char buf[MAX_PATH];
-  GetSystemDirectoryA(buf, MAX_PATH);
-  ByteString dllpath = buf;
-  dllpath += "\\GDIPLUS.DLL";
-  m_hModule = LoadLibraryA(dllpath.c_str());
-  if (!m_hModule)
-    return;
-
-  m_Functions.resize(FX_ArraySize(g_GdipFuncNames));
-  for (size_t i = 0; i < FX_ArraySize(g_GdipFuncNames); ++i) {
-    m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
-    if (!m_Functions[i]) {
-      m_hModule = nullptr;
-      return;
-    }
-  }
-
-  uintptr_t gdiplusToken;
-  GdiplusStartupInput gdiplusStartupInput;
-  ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
-      &gdiplusToken, &gdiplusStartupInput, nullptr);
-  m_GdiModule = LoadLibraryA("GDI32.DLL");
-  if (!m_GdiModule)
-    return;
-
-  m_pGdiAddFontMemResourceEx =
-      reinterpret_cast<FuncType_GdiAddFontMemResourceEx>(
-          GetProcAddress(m_GdiModule, "AddFontMemResourceEx"));
-  m_pGdiRemoveFontMemResourseEx =
-      reinterpret_cast<FuncType_GdiRemoveFontMemResourceEx>(
-          GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx"));
-}
-
-LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, uint32_t size) {
-  GpFontCollection* pCollection = nullptr;
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipNewPrivateFontCollection)(&pCollection);
-  GpStatus status =
-      CallFunc(GdipPrivateAddMemoryFont)(pCollection, pData, size);
-  if (status == Gdiplus::Ok)
-    return pCollection;
-
-  CallFunc(GdipDeletePrivateFontCollection)(&pCollection);
-  return nullptr;
-}
-void CGdiplusExt::DeleteMemFont(LPVOID pCollection) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipDeletePrivateFontCollection)((GpFontCollection**)&pCollection);
-}
-bool CGdiplusExt::GdipCreateBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                   void** bitmap) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  Gdiplus::PixelFormat format;
-  switch (pBitmap->GetFormat()) {
-    case FXDIB_Rgb:
-      format = PixelFormat24bppRGB;
-      break;
-    case FXDIB_Rgb32:
-      format = PixelFormat32bppRGB;
-      break;
-    case FXDIB_Argb:
-      format = PixelFormat32bppARGB;
-      break;
-    default:
-      return false;
-  }
-  GpStatus status = CallFunc(GdipCreateBitmapFromScan0)(
-      pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap->GetPitch(), format,
-      pBitmap->GetBuffer(), (GpBitmap**)bitmap);
-  return status == Gdiplus::Ok;
-}
-bool CGdiplusExt::GdipCreateFromImage(void* bitmap, void** graphics) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  GpStatus status = CallFunc(GdipGetImageGraphicsContext)(
-      (GpBitmap*)bitmap, (GpGraphics**)graphics);
-  return status == Gdiplus::Ok;
-}
-bool CGdiplusExt::GdipCreateFontFamilyFromName(const wchar_t* name,
-                                               void* pFontCollection,
-                                               void** pFamily) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  GpStatus status = CallFunc(GdipCreateFontFamilyFromName)(
-      (GDIPCONST WCHAR*)name, (GpFontCollection*)pFontCollection,
-      (GpFontFamily**)pFamily);
-  return status == Gdiplus::Ok;
-}
-bool CGdiplusExt::GdipCreateFontFromFamily(void* pFamily,
-                                           float font_size,
-                                           int fontstyle,
-                                           int flag,
-                                           void** pFont) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  GpStatus status =
-      CallFunc(GdipCreateFont)((GpFontFamily*)pFamily, font_size, fontstyle,
-                               Gdiplus::Unit(flag), (GpFont**)pFont);
-  return status == Gdiplus::Ok;
-}
-void CGdiplusExt::GdipGetFontSize(void* pFont, float* size) {
-  REAL get_size;
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  GpStatus status = CallFunc(GdipGetFontSize)((GpFont*)pFont, &get_size);
-  *size = (status == Gdiplus::Ok) ? static_cast<float>(get_size) : 0;
-}
-void CGdiplusExt::GdipSetTextRenderingHint(void* graphics, int mode) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipSetTextRenderingHint)((GpGraphics*)graphics,
-                                     (TextRenderingHint)mode);
-}
-void CGdiplusExt::GdipSetPageUnit(void* graphics, uint32_t unit) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipSetPageUnit)((GpGraphics*)graphics, (GpUnit)unit);
-}
-bool CGdiplusExt::GdipDrawDriverString(void* graphics,
-                                       unsigned short* text,
-                                       int length,
-                                       void* font,
-                                       void* brush,
-                                       void* positions,
-                                       int flags,
-                                       const void* matrix) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  GpStatus status = CallFunc(GdipDrawDriverString)(
-      (GpGraphics*)graphics, (GDIPCONST UINT16*)text, (INT)length,
-      (GDIPCONST GpFont*)font, (GDIPCONST GpBrush*)brush,
-      (GDIPCONST Gdiplus::PointF*)positions, (INT)flags,
-      (GDIPCONST GpMatrix*)matrix);
-  return status == Gdiplus::Ok;
-}
-void CGdiplusExt::GdipCreateBrush(uint32_t fill_argb, void** pBrush) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)fill_argb,
-                                (GpSolidFill**)pBrush);
-}
-void CGdiplusExt::GdipDeleteBrush(void* pBrush) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipDeleteBrush)((GpSolidFill*)pBrush);
-}
-void* CGdiplusExt::GdipCreateFontFromCollection(void* pFontCollection,
-                                                float font_size,
-                                                int fontstyle) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  int numFamilies = 0;
-  GpStatus status = CallFunc(GdipGetFontCollectionFamilyCount)(
-      (GpFontCollection*)pFontCollection, &numFamilies);
-  if (status != Gdiplus::Ok)
-    return nullptr;
-
-  GpFontFamily* family_list[1];
-  status = CallFunc(GdipGetFontCollectionFamilyList)(
-      (GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);
-  if (status != Gdiplus::Ok)
-    return nullptr;
-
-  GpFont* pFont = nullptr;
-  status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle,
-                                    UnitPixel, &pFont);
-  if (status != Gdiplus::Ok)
-    return nullptr;
-
-  return pFont;
-}
-void CGdiplusExt::GdipCreateMatrix(float a,
-                                   float b,
-                                   float c,
-                                   float d,
-                                   float e,
-                                   float f,
-                                   void** matrix) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipCreateMatrix2)(a, b, c, d, e, f, (GpMatrix**)matrix);
-}
-void CGdiplusExt::GdipDeleteMatrix(void* matrix) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipDeleteMatrix)((GpMatrix*)matrix);
-}
-void CGdiplusExt::GdipDeleteFontFamily(void* pFamily) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipDeleteFontFamily)((GpFontFamily*)pFamily);
-}
-void CGdiplusExt::GdipDeleteFont(void* pFont) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipDeleteFont)((GpFont*)pFont);
-}
-void CGdiplusExt::GdipSetWorldTransform(void* graphics, void* pMatrix) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipSetWorldTransform)((GpGraphics*)graphics, (GpMatrix*)pMatrix);
-}
-void CGdiplusExt::GdipDisposeImage(void* bitmap) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipDisposeImage)((GpBitmap*)bitmap);
-}
-void CGdiplusExt::GdipDeleteGraphics(void* graphics) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipDeleteGraphics)((GpGraphics*)graphics);
-}
-bool CGdiplusExt::StretchBitMask(HDC hDC,
-                                 BOOL bMonoDevice,
-                                 const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                 int dest_left,
-                                 int dest_top,
-                                 int dest_width,
-                                 int dest_height,
-                                 uint32_t argb,
-                                 const FX_RECT* pClipRect,
-                                 int flags) {
-  ASSERT(pBitmap->GetBPP() == 1);
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  GpGraphics* pGraphics = nullptr;
-  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
-  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
-  if (flags & FXDIB_NOSMOOTH) {
-    CallFunc(GdipSetInterpolationMode)(pGraphics,
-                                       InterpolationModeNearestNeighbor);
-  } else {
-    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
-  }
-  OutputImageMask(pGraphics, bMonoDevice, pBitmap, dest_left, dest_top,
-                  dest_width, dest_height, argb, pClipRect);
-  CallFunc(GdipDeleteGraphics)(pGraphics);
-  return true;
-}
-bool CGdiplusExt::StretchDIBits(HDC hDC,
-                                const RetainPtr<CFX_DIBitmap>& pBitmap,
-                                int dest_left,
-                                int dest_top,
-                                int dest_width,
-                                int dest_height,
-                                const FX_RECT* pClipRect,
-                                int flags) {
-  GpGraphics* pGraphics;
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
-  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
-  if (flags & FXDIB_NOSMOOTH) {
-    CallFunc(GdipSetInterpolationMode)(pGraphics,
-                                       InterpolationModeNearestNeighbor);
-  } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
-             pBitmap->GetHeight() > abs(dest_height) / 2) {
-    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
-  } else {
-    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeBilinear);
-  }
-  FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
-  OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width,
-              dest_height);
-  CallFunc(GdipDeleteGraphics)(pGraphics);
-  CallFunc(GdipDeleteGraphics)(pGraphics);
-  return true;
-}
-static GpPen* _GdipCreatePen(const CFX_GraphStateData* pGraphState,
-                             const CFX_Matrix* pMatrix,
-                             DWORD argb,
-                             bool bTextMode = false) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  float width = pGraphState ? pGraphState->m_LineWidth : 1.0f;
+Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState,
+                                  const CFX_Matrix* pMatrix,
+                                  DWORD argb,
+                                  bool bTextMode) {
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  float width = pGraphState->m_LineWidth;
   if (!bTextMode) {
     float unit = pMatrix
                      ? 1.0f / ((pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
                      : 1.0f;
-    if (width < unit)
-      width = unit;
+    width = std::max(width, unit);
   }
-  GpPen* pPen = nullptr;
-  CallFunc(GdipCreatePen1)((Gdiplus::ARGB)argb, width, UnitWorld, &pPen);
-  LineCap lineCap = LineCapFlat;
-  DashCap dashCap = DashCapFlat;
+  Gdiplus::GpPen* pPen = nullptr;
+  CallFunc(GdipCreatePen1)((Gdiplus::ARGB)argb, width, Gdiplus::UnitWorld,
+                           &pPen);
+  Gdiplus::LineCap lineCap = Gdiplus::LineCapFlat;
+  Gdiplus::DashCap dashCap = Gdiplus::DashCapFlat;
   bool bDashExtend = false;
   switch (pGraphState->m_LineCap) {
     case CFX_GraphStateData::LineCapButt:
-      lineCap = LineCapFlat;
+      lineCap = Gdiplus::LineCapFlat;
       break;
     case CFX_GraphStateData::LineCapRound:
-      lineCap = LineCapRound;
-      dashCap = DashCapRound;
+      lineCap = Gdiplus::LineCapRound;
+      dashCap = Gdiplus::DashCapRound;
       bDashExtend = true;
       break;
     case CFX_GraphStateData::LineCapSquare:
-      lineCap = LineCapSquare;
+      lineCap = Gdiplus::LineCapSquare;
       bDashExtend = true;
       break;
   }
   CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
-  LineJoin lineJoin = LineJoinMiterClipped;
+  Gdiplus::LineJoin lineJoin = Gdiplus::LineJoinMiterClipped;
   switch (pGraphState->m_LineJoin) {
     case CFX_GraphStateData::LineJoinMiter:
-      lineJoin = LineJoinMiterClipped;
+      lineJoin = Gdiplus::LineJoinMiterClipped;
       break;
     case CFX_GraphStateData::LineJoinRound:
-      lineJoin = LineJoinRound;
+      lineJoin = Gdiplus::LineJoinRound;
       break;
     case CFX_GraphStateData::LineJoinBevel:
-      lineJoin = LineJoinBevel;
+      lineJoin = Gdiplus::LineJoinBevel;
       break;
   }
   CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
-  if (pGraphState->m_DashCount) {
-    float* pDashArray = FX_Alloc(
-        float, pGraphState->m_DashCount + pGraphState->m_DashCount % 2);
+  if (!pGraphState->m_DashArray.empty()) {
+    float* pDashArray =
+        FX_Alloc(float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size()));
     int nCount = 0;
     float on_leftover = 0, off_leftover = 0;
-    for (int i = 0; i < pGraphState->m_DashCount; i += 2) {
+    for (size_t i = 0; i < pGraphState->m_DashArray.size(); i += 2) {
       float on_phase = pGraphState->m_DashArray[i];
       float off_phase;
-      if (i == pGraphState->m_DashCount - 1)
+      if (i == pGraphState->m_DashArray.size() - 1)
         off_phase = on_phase;
       else
         off_phase = pGraphState->m_DashArray[i + 1];
@@ -1118,14 +482,13 @@
   CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
   return pPen;
 }
-static bool IsSmallTriangle(Gdiplus::PointF* points,
-                            const CFX_Matrix* pMatrix,
-                            int& v1,
-                            int& v2) {
-  int pairs[] = {1, 2, 0, 2, 0, 1};
-  for (int i = 0; i < 3; i++) {
-    int pair1 = pairs[i * 2];
-    int pair2 = pairs[i * 2 + 1];
+
+Optional<std::pair<size_t, size_t>> IsSmallTriangle(Gdiplus::PointF* points,
+                                                    const CFX_Matrix* pMatrix) {
+  size_t pairs[] = {1, 2, 0, 2, 0, 1};
+  for (size_t i = 0; i < FX_ArraySize(pairs) / 2; i++) {
+    size_t pair1 = pairs[i * 2];
+    size_t pair2 = pairs[i * 2 + 1];
 
     CFX_PointF p1(points[pair1].X, points[pair1].Y);
     CFX_PointF p2(points[pair2].X, points[pair2].Y);
@@ -1136,170 +499,16 @@
 
     CFX_PointF diff = p1 - p2;
     float distance_square = (diff.x * diff.x) + (diff.y * diff.y);
-    if (distance_square < (1.0f * 2 + 1.0f / 4)) {
-      v1 = i;
-      v2 = pair1;
-      return true;
-    }
+    if (distance_square < (1.0f * 2 + 1.0f / 4))
+      return std::make_pair(i, pair1);
   }
-  return false;
-}
-bool CGdiplusExt::DrawPath(HDC hDC,
-                           const CFX_PathData* pPathData,
-                           const CFX_Matrix* pObject2Device,
-                           const CFX_GraphStateData* pGraphState,
-                           uint32_t fill_argb,
-                           uint32_t stroke_argb,
-                           int fill_mode) {
-  auto& pPoints = pPathData->GetPoints();
-  if (pPoints.empty())
-    return true;
-
-  GpGraphics* pGraphics = nullptr;
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
-  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
-  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
-  CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);
-  GpMatrix* pMatrix = nullptr;
-  if (pObject2Device) {
-    CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
-                                pObject2Device->c, pObject2Device->d,
-                                pObject2Device->e, pObject2Device->f, &pMatrix);
-    CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
-  }
-  Gdiplus::PointF* points = FX_Alloc(Gdiplus::PointF, pPoints.size());
-  BYTE* types = FX_Alloc(BYTE, pPoints.size());
-  int nSubPathes = 0;
-  bool bSubClose = false;
-  int pos_subclose = 0;
-  bool bSmooth = false;
-  int startpoint = 0;
-  for (size_t i = 0; i < pPoints.size(); i++) {
-    points[i].X = pPoints[i].m_Point.x;
-    points[i].Y = pPoints[i].m_Point.y;
-
-    CFX_PointF pos = pPoints[i].m_Point;
-    if (pObject2Device)
-      pos = pObject2Device->Transform(pos);
-
-    if (pos.x > 50000 * 1.0f)
-      points[i].X = 50000 * 1.0f;
-    if (pos.x < -50000 * 1.0f)
-      points[i].X = -50000 * 1.0f;
-    if (pos.y > 50000 * 1.0f)
-      points[i].Y = 50000 * 1.0f;
-    if (pos.y < -50000 * 1.0f)
-      points[i].Y = -50000 * 1.0f;
-
-    FXPT_TYPE point_type = pPoints[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
-      types[i] = PathPointTypeStart;
-      nSubPathes++;
-      bSubClose = false;
-      startpoint = i;
-    } else if (point_type == FXPT_TYPE::LineTo) {
-      types[i] = PathPointTypeLine;
-      if (pPoints[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
-          (i == pPoints.size() - 1 ||
-           pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) &&
-          points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
-        points[i].X += 0.01f;
-        continue;
-      }
-      if (!bSmooth && points[i].X != points[i - 1].X &&
-          points[i].Y != points[i - 1].Y)
-        bSmooth = true;
-    } else if (point_type == FXPT_TYPE::BezierTo) {
-      types[i] = PathPointTypeBezier;
-      bSmooth = true;
-    }
-    if (pPoints[i].m_CloseFigure) {
-      if (bSubClose)
-        types[pos_subclose] &= ~PathPointTypeCloseSubpath;
-      else
-        bSubClose = true;
-      pos_subclose = i;
-      types[i] |= PathPointTypeCloseSubpath;
-      if (!bSmooth && points[i].X != points[startpoint].X &&
-          points[i].Y != points[startpoint].Y)
-        bSmooth = true;
-    }
-  }
-  if (fill_mode & FXFILL_NOPATHSMOOTH) {
-    bSmooth = false;
-    CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);
-  } else if (!(fill_mode & FXFILL_FULLCOVER)) {
-    if (!bSmooth && (fill_mode & 3))
-      bSmooth = true;
-
-    if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2))
-      CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);
-  }
-  int new_fill_mode = fill_mode & 3;
-  if (pPoints.size() == 4 && !pGraphState) {
-    int v1, v2;
-    if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
-      GpPen* pPen = nullptr;
-      CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);
-      CallFunc(GdipDrawLineI)(
-          pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),
-          FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));
-      CallFunc(GdipDeletePen)(pPen);
-      return true;
-    }
-  }
-  GpPath* pGpPath = nullptr;
-  CallFunc(GdipCreatePath2)(points, types, pPoints.size(),
-                            GdiFillType2Gdip(new_fill_mode), &pGpPath);
-  if (!pGpPath) {
-    if (pMatrix)
-      CallFunc(GdipDeleteMatrix)(pMatrix);
-
-    FX_Free(points);
-    FX_Free(types);
-    CallFunc(GdipDeleteGraphics)(pGraphics);
-    return false;
-  }
-  if (new_fill_mode) {
-    GpBrush* pBrush = _GdipCreateBrush(fill_argb);
-    CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
-    CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
-    CallFunc(GdipDeleteBrush)(pBrush);
-  }
-  if (pGraphState && stroke_argb) {
-    GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb,
-                                 !!(fill_mode & FX_STROKE_TEXT_MODE));
-    if (nSubPathes == 1) {
-      CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
-    } else {
-      int iStart = 0;
-      for (size_t i = 0; i < pPoints.size(); i++) {
-        if (i == pPoints.size() - 1 || types[i + 1] == PathPointTypeStart) {
-          GpPath* pSubPath;
-          CallFunc(GdipCreatePath2)(points + iStart, types + iStart,
-                                    i - iStart + 1,
-                                    GdiFillType2Gdip(new_fill_mode), &pSubPath);
-          iStart = i + 1;
-          CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
-          CallFunc(GdipDeletePath)(pSubPath);
-        }
-      }
-    }
-    CallFunc(GdipDeletePen)(pPen);
-  }
-  if (pMatrix)
-    CallFunc(GdipDeleteMatrix)(pMatrix);
-  FX_Free(points);
-  FX_Free(types);
-  CallFunc(GdipDeletePath)(pGpPath);
-  CallFunc(GdipDeleteGraphics)(pGraphics);
-  return true;
+  return {};
 }
 
 class GpStream final : public IStream {
  public:
   GpStream() : m_RefCount(1), m_ReadPos(0) {}
+  ~GpStream() = default;
 
   // IUnknown
   HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
@@ -1395,6 +604,8 @@
         start = m_ReadPos;
         break;
       case STREAM_SEEK_END:
+        if (m_InterStream.tellp() < 0)
+          return STG_E_SEEKERROR;
         start = m_InterStream.tellp();
         break;
       default:
@@ -1416,6 +627,10 @@
       return STG_E_INVALIDFUNCTION;
 
     ZeroMemory(pStatstg, sizeof(STATSTG));
+
+    if (m_InterStream.tellp() < 0)
+      return STG_E_SEEKERROR;
+
     pStatstg->cbSize.QuadPart = m_InterStream.tellp();
     return S_OK;
   }
@@ -1426,24 +641,22 @@
   std::ostringstream m_InterStream;
 };
 
-typedef struct {
+struct PREVIEW3_DIBITMAP {
   BITMAPINFO* pbmi;
   int Stride;
   LPBYTE pScan0;
-  GpBitmap* pBitmap;
+  Gdiplus::GpBitmap* pBitmap;
   Gdiplus::BitmapData* pBitmapData;
   GpStream* pStream;
-} PREVIEW3_DIBITMAP;
+};
 
-static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) {
-  GpBitmap* pBitmap;
+PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) {
+  Gdiplus::GpBitmap* pBitmap;
   GpStream* pStream = nullptr;
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
   Gdiplus::Status status = Gdiplus::Ok;
   if (args.flags == WINDIB_OPEN_PATHNAME) {
-    status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name,
-                                                   &pBitmap);
+    status = CallFunc(GdipCreateBitmapFromFileICM)(args.path_name, &pBitmap);
   } else {
     if (args.memory_size == 0 || !args.memory_base)
       return nullptr;
@@ -1487,7 +700,7 @@
   pbmih->biWidth = width;
   Gdiplus::Rect rect(0, 0, width, height);
   Gdiplus::BitmapData* pBitmapData = FX_Alloc(Gdiplus::BitmapData, 1);
-  CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,
+  CallFunc(GdipBitmapLockBits)(pBitmap, &rect, Gdiplus::ImageLockModeRead,
                                dest_pixel_format, pBitmapData);
   if (pixel_format == PixelFormat1bppIndexed ||
       pixel_format == PixelFormat8bppIndexed) {
@@ -1515,9 +728,8 @@
   return pInfo;
 }
 
-static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) {
-  CGdiplusExt& GdiplusExt =
-      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) {
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
   CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
   CallFunc(GdipDisposeImage)(pInfo->pBitmap);
   FX_Free(pInfo->pBitmapData);
@@ -1527,10 +739,230 @@
   FX_Free(pInfo);
 }
 
-// TODO(tsepez): Really? Really? Move to header.
-RetainPtr<CFX_DIBitmap> _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
-                                                   LPVOID pData,
-                                                   bool bAlpha);
+}  // namespace
+
+CGdiplusExt::CGdiplusExt() {}
+
+CGdiplusExt::~CGdiplusExt() {
+  FreeLibrary(m_GdiModule);
+  FreeLibrary(m_hModule);
+}
+
+void CGdiplusExt::Load() {
+  char buf[MAX_PATH];
+  GetSystemDirectoryA(buf, MAX_PATH);
+  ByteString dllpath = buf;
+  dllpath += "\\GDIPLUS.DLL";
+  m_hModule = LoadLibraryA(dllpath.c_str());
+  if (!m_hModule)
+    return;
+
+  m_Functions.resize(FX_ArraySize(g_GdipFuncNames));
+  for (size_t i = 0; i < FX_ArraySize(g_GdipFuncNames); ++i) {
+    m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
+    if (!m_Functions[i]) {
+      m_hModule = nullptr;
+      return;
+    }
+  }
+
+  uintptr_t gdiplusToken;
+  Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+  ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
+      &gdiplusToken, &gdiplusStartupInput, nullptr);
+  m_GdiModule = LoadLibraryA("GDI32.DLL");
+}
+
+bool CGdiplusExt::StretchDIBits(HDC hDC,
+                                const RetainPtr<CFX_DIBitmap>& pBitmap,
+                                int dest_left,
+                                int dest_top,
+                                int dest_width,
+                                int dest_height,
+                                const FX_RECT* pClipRect,
+                                const FXDIB_ResampleOptions& options) {
+  Gdiplus::GpGraphics* pGraphics;
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
+  CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
+  if (options.bNoSmoothing) {
+    CallFunc(GdipSetInterpolationMode)(
+        pGraphics, Gdiplus::InterpolationModeNearestNeighbor);
+  } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
+             pBitmap->GetHeight() > abs(dest_height) / 2) {
+    CallFunc(GdipSetInterpolationMode)(pGraphics,
+                                       Gdiplus::InterpolationModeHighQuality);
+  } else {
+    CallFunc(GdipSetInterpolationMode)(pGraphics,
+                                       Gdiplus::InterpolationModeBilinear);
+  }
+  FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
+  OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width,
+              dest_height);
+  CallFunc(GdipDeleteGraphics)(pGraphics);
+  CallFunc(GdipDeleteGraphics)(pGraphics);
+  return true;
+}
+
+bool CGdiplusExt::DrawPath(HDC hDC,
+                           const CFX_PathData* pPathData,
+                           const CFX_Matrix* pObject2Device,
+                           const CFX_GraphStateData* pGraphState,
+                           uint32_t fill_argb,
+                           uint32_t stroke_argb,
+                           int fill_mode) {
+  auto& pPoints = pPathData->GetPoints();
+  if (pPoints.empty())
+    return true;
+
+  Gdiplus::GpGraphics* pGraphics = nullptr;
+  const CGdiplusExt& GdiplusExt = GetGdiplusExt();
+  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
+  CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
+  CallFunc(GdipSetPixelOffsetMode)(pGraphics, Gdiplus::PixelOffsetModeHalf);
+  Gdiplus::GpMatrix* pMatrix = nullptr;
+  if (pObject2Device) {
+    CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
+                                pObject2Device->c, pObject2Device->d,
+                                pObject2Device->e, pObject2Device->f, &pMatrix);
+    CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
+  }
+  Gdiplus::PointF* points = FX_Alloc(Gdiplus::PointF, pPoints.size());
+  BYTE* types = FX_Alloc(BYTE, pPoints.size());
+  int nSubPathes = 0;
+  bool bSubClose = false;
+  int pos_subclose = 0;
+  bool bSmooth = false;
+  int startpoint = 0;
+  for (size_t i = 0; i < pPoints.size(); i++) {
+    points[i].X = pPoints[i].m_Point.x;
+    points[i].Y = pPoints[i].m_Point.y;
+
+    CFX_PointF pos = pPoints[i].m_Point;
+    if (pObject2Device)
+      pos = pObject2Device->Transform(pos);
+
+    if (pos.x > 50000 * 1.0f)
+      points[i].X = 50000 * 1.0f;
+    if (pos.x < -50000 * 1.0f)
+      points[i].X = -50000 * 1.0f;
+    if (pos.y > 50000 * 1.0f)
+      points[i].Y = 50000 * 1.0f;
+    if (pos.y < -50000 * 1.0f)
+      points[i].Y = -50000 * 1.0f;
+
+    FXPT_TYPE point_type = pPoints[i].m_Type;
+    if (point_type == FXPT_TYPE::MoveTo) {
+      types[i] = Gdiplus::PathPointTypeStart;
+      nSubPathes++;
+      bSubClose = false;
+      startpoint = i;
+    } else if (point_type == FXPT_TYPE::LineTo) {
+      types[i] = Gdiplus::PathPointTypeLine;
+      if (pPoints[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
+          (i == pPoints.size() - 1 ||
+           pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) &&
+          points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
+        points[i].X += 0.01f;
+        continue;
+      }
+      if (!bSmooth && points[i].X != points[i - 1].X &&
+          points[i].Y != points[i - 1].Y)
+        bSmooth = true;
+    } else if (point_type == FXPT_TYPE::BezierTo) {
+      types[i] = Gdiplus::PathPointTypeBezier;
+      bSmooth = true;
+    }
+    if (pPoints[i].m_CloseFigure) {
+      if (bSubClose)
+        types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath;
+      else
+        bSubClose = true;
+      pos_subclose = i;
+      types[i] |= Gdiplus::PathPointTypeCloseSubpath;
+      if (!bSmooth && points[i].X != points[startpoint].X &&
+          points[i].Y != points[startpoint].Y)
+        bSmooth = true;
+    }
+  }
+  if (fill_mode & FXFILL_NOPATHSMOOTH) {
+    bSmooth = false;
+    CallFunc(GdipSetSmoothingMode)(pGraphics, Gdiplus::SmoothingModeNone);
+  } else if (!(fill_mode & FXFILL_FULLCOVER)) {
+    if (!bSmooth && (fill_mode & 3))
+      bSmooth = true;
+
+    if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) {
+      CallFunc(GdipSetSmoothingMode)(pGraphics,
+                                     Gdiplus::SmoothingModeAntiAlias);
+    }
+  }
+  int new_fill_mode = fill_mode & 3;
+  if (pPoints.size() == 4 && !pGraphState) {
+    auto indices = IsSmallTriangle(points, pObject2Device);
+    if (indices.has_value()) {
+      size_t v1;
+      size_t v2;
+      std::tie(v1, v2) = indices.value();
+      Gdiplus::GpPen* pPen = nullptr;
+      CallFunc(GdipCreatePen1)(fill_argb, 1.0f, Gdiplus::UnitPixel, &pPen);
+      CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_roundf(points[v1].X),
+                              FXSYS_roundf(points[v1].Y),
+                              FXSYS_roundf(points[v2].X),
+                              FXSYS_roundf(points[v2].Y));
+      CallFunc(GdipDeletePen)(pPen);
+      return true;
+    }
+  }
+  Gdiplus::GpPath* pGpPath = nullptr;
+  CallFunc(GdipCreatePath2)(points, types, pPoints.size(),
+                            GdiFillType2Gdip(new_fill_mode), &pGpPath);
+  if (!pGpPath) {
+    if (pMatrix)
+      CallFunc(GdipDeleteMatrix)(pMatrix);
+
+    FX_Free(points);
+    FX_Free(types);
+    CallFunc(GdipDeleteGraphics)(pGraphics);
+    return false;
+  }
+  if (new_fill_mode) {
+    Gdiplus::GpBrush* pBrush = GdipCreateBrushImpl(fill_argb);
+    CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
+    CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
+    CallFunc(GdipDeleteBrush)(pBrush);
+  }
+  if (pGraphState && stroke_argb) {
+    Gdiplus::GpPen* pPen =
+        GdipCreatePenImpl(pGraphState, pObject2Device, stroke_argb,
+                          !!(fill_mode & FX_STROKE_TEXT_MODE));
+    if (nSubPathes == 1) {
+      CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
+    } else {
+      int iStart = 0;
+      for (size_t i = 0; i < pPoints.size(); i++) {
+        if (i == pPoints.size() - 1 ||
+            types[i + 1] == Gdiplus::PathPointTypeStart) {
+          Gdiplus::GpPath* pSubPath;
+          CallFunc(GdipCreatePath2)(points + iStart, types + iStart,
+                                    i - iStart + 1,
+                                    GdiFillType2Gdip(new_fill_mode), &pSubPath);
+          iStart = i + 1;
+          CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
+          CallFunc(GdipDeletePath)(pSubPath);
+        }
+      }
+    }
+    CallFunc(GdipDeletePen)(pPen);
+  }
+  if (pMatrix)
+    CallFunc(GdipDeleteMatrix)(pMatrix);
+  FX_Free(points);
+  FX_Free(types);
+  CallFunc(GdipDeletePath)(pGpPath);
+  CallFunc(GdipDeleteGraphics)(pGraphics);
+  return true;
+}
 
 RetainPtr<CFX_DIBitmap> CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args) {
   PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
@@ -1549,7 +981,7 @@
              dest_pitch);
     }
   }
-  RetainPtr<CFX_DIBitmap> pDIBitmap = _FX_WindowsDIB_LoadFromBuf(
+  RetainPtr<CFX_DIBitmap> pDIBitmap = FX_WindowsDIB_LoadFromBuf(
       pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
   FX_Free(pData);
   FreeDIBitmap(pInfo);
diff --git a/core/fxge/win32/fx_win32_print.cpp b/core/fxge/win32/fx_win32_print.cpp
index df9e838..9ae092a 100644
--- a/core/fxge/win32/fx_win32_print.cpp
+++ b/core/fxge/win32/fx_win32_print.cpp
@@ -14,22 +14,27 @@
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_windowsrenderdevice.h"
 #include "core/fxge/dib/cfx_dibextractor.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/dib/cstretchengine.h"
 #include "core/fxge/fx_freetype.h"
+#include "core/fxge/text_char_pos.h"
 #include "core/fxge/win32/cpsoutput.h"
 #include "core/fxge/win32/win32_int.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 
 #if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
 namespace {
 
 class ScopedState {
  public:
-  ScopedState(HDC hDC, HFONT hFont) : m_hDC(hDC) {
-    m_iState = SaveDC(m_hDC);
-    m_hFont = SelectObject(m_hDC, hFont);
-  }
+  ScopedState(HDC hDC, HFONT hFont)
+      : m_hDC(hDC),
+        m_iState(SaveDC(m_hDC)),
+        m_hFont(SelectObject(m_hDC, hFont)) {}
+
+  ScopedState(const ScopedState&) = delete;
+  ScopedState& operator=(const ScopedState&) = delete;
 
   ~ScopedState() {
     HGDIOBJ hFont = SelectObject(m_hDC, m_hFont);
@@ -38,12 +43,9 @@
   }
 
  private:
-  HDC m_hDC;
-  HGDIOBJ m_hFont;
-  int m_iState;
-
-  ScopedState(const ScopedState&) = delete;
-  void operator=(const ScopedState&) = delete;
+  const HDC m_hDC;
+  const int m_iState;
+  const HGDIOBJ m_hFont;
 };
 
 }  // namespace
@@ -55,11 +57,11 @@
 #endif
 
 CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC)
-    : CGdiDeviceDriver(hDC, FXDC_PRINTER),
+    : CGdiDeviceDriver(hDC, DeviceType::kPrinter),
       m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)),
       m_VertSize(::GetDeviceCaps(m_hDC, VERTSIZE)) {}
 
-CGdiPrinterDriver::~CGdiPrinterDriver() {}
+CGdiPrinterDriver::~CGdiPrinterDriver() = default;
 
 int CGdiPrinterDriver::GetDeviceCaps(int caps_id) const {
   if (caps_id == FXDC_HORZ_SIZE)
@@ -69,22 +71,23 @@
   return CGdiDeviceDriver::GetDeviceCaps(caps_id);
 }
 
-bool CGdiPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CGdiPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                   uint32_t color,
-                                  const FX_RECT* pSrcRect,
+                                  const FX_RECT& src_rect,
                                   int left,
                                   int top,
-                                  int blend_type) {
+                                  BlendMode blend_type) {
   if (pSource->IsAlphaMask()) {
-    FX_RECT clip_rect(left, top, left + pSrcRect->Width(),
-                      top + pSrcRect->Height());
-    return StretchDIBits(pSource, color, left - pSrcRect->left,
-                         top - pSrcRect->top, pSource->GetWidth(),
-                         pSource->GetHeight(), &clip_rect, 0,
-                         FXDIB_BLEND_NORMAL);
+    FX_RECT clip_rect(left, top, left + src_rect.Width(),
+                      top + src_rect.Height());
+    return StretchDIBits(pSource, color, left - src_rect.left,
+                         top - src_rect.top, pSource->GetWidth(),
+                         pSource->GetHeight(), &clip_rect,
+                         FXDIB_ResampleOptions(), BlendMode::kNormal);
   }
-  ASSERT(pSource && !pSource->IsAlphaMask() && pSrcRect);
-  ASSERT(blend_type == FXDIB_BLEND_NORMAL);
+  ASSERT(pSource);
+  ASSERT(!pSource->IsAlphaMask());
+  ASSERT(blend_type == BlendMode::kNormal);
   if (pSource->HasAlpha())
     return false;
 
@@ -93,18 +96,18 @@
   if (!pBitmap)
     return false;
 
-  return GDI_SetDIBits(pBitmap, pSrcRect, left, top);
+  return GDI_SetDIBits(pBitmap, src_rect, left, top);
 }
 
-bool CGdiPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CGdiPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                       uint32_t color,
                                       int dest_left,
                                       int dest_top,
                                       int dest_width,
                                       int dest_height,
                                       const FX_RECT* pClipRect,
-                                      uint32_t flags,
-                                      int blend_type) {
+                                      const FXDIB_ResampleOptions& options,
+                                      BlendMode blend_type) {
   if (pSource->IsAlphaMask()) {
     int alpha = FXARGB_A(color);
     if (pSource->GetBPP() != 1 || alpha != 255)
@@ -122,7 +125,7 @@
         dest_top += dest_height;
 
       return GDI_StretchBitMask(pFlipped, dest_left, dest_top, abs(dest_width),
-                                abs(dest_height), color, flags);
+                                abs(dest_height), color);
     }
 
     CFX_DIBExtractor temp(pSource);
@@ -130,7 +133,7 @@
     if (!pBitmap)
       return false;
     return GDI_StretchBitMask(pBitmap, dest_left, dest_top, dest_width,
-                              dest_height, color, flags);
+                              dest_height, color);
   }
 
   if (pSource->HasAlpha())
@@ -148,7 +151,7 @@
       dest_top += dest_height;
 
     return GDI_StretchDIBits(pFlipped, dest_left, dest_top, abs(dest_width),
-                             abs(dest_height), flags);
+                             abs(dest_height), options);
   }
 
   CFX_DIBExtractor temp(pSource);
@@ -156,50 +159,50 @@
   if (!pBitmap)
     return false;
   return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width,
-                           dest_height, flags);
+                           dest_height, options);
 }
 
-bool CGdiPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBSource>& pSource,
+bool CGdiPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pSource,
                                     int bitmap_alpha,
                                     uint32_t color,
-                                    const CFX_Matrix* pMatrix,
-                                    uint32_t render_flags,
+                                    const CFX_Matrix& matrix,
+                                    const FXDIB_ResampleOptions& options,
                                     std::unique_ptr<CFX_ImageRenderer>* handle,
-                                    int blend_type) {
+                                    BlendMode blend_type) {
   if (bitmap_alpha < 255 || pSource->HasAlpha() ||
       (pSource->IsAlphaMask() && (pSource->GetBPP() != 1))) {
     return false;
   }
-  CFX_FloatRect unit_rect = pMatrix->GetUnitRect();
+  CFX_FloatRect unit_rect = matrix.GetUnitRect();
   FX_RECT full_rect = unit_rect.GetOuterRect();
-  if (fabs(pMatrix->b) < 0.5f && pMatrix->a != 0 && fabs(pMatrix->c) < 0.5f &&
-      pMatrix->d != 0) {
-    bool bFlipX = pMatrix->a < 0;
-    bool bFlipY = pMatrix->d > 0;
+  if (fabs(matrix.b) < 0.5f && matrix.a != 0 && fabs(matrix.c) < 0.5f &&
+      matrix.d != 0) {
+    bool bFlipX = matrix.a < 0;
+    bool bFlipY = matrix.d > 0;
     return StretchDIBits(pSource, color,
                          bFlipX ? full_rect.right : full_rect.left,
                          bFlipY ? full_rect.bottom : full_rect.top,
                          bFlipX ? -full_rect.Width() : full_rect.Width(),
                          bFlipY ? -full_rect.Height() : full_rect.Height(),
-                         nullptr, 0, blend_type);
+                         nullptr, FXDIB_ResampleOptions(), blend_type);
   }
-  if (fabs(pMatrix->a) >= 0.5f || fabs(pMatrix->d) >= 0.5f)
+  if (fabs(matrix.a) >= 0.5f || fabs(matrix.d) >= 0.5f)
     return false;
 
   RetainPtr<CFX_DIBitmap> pTransformed =
-      pSource->SwapXY(pMatrix->c > 0, pMatrix->b < 0);
+      pSource->SwapXY(matrix.c > 0, matrix.b < 0);
   if (!pTransformed)
     return false;
 
   return StretchDIBits(pTransformed, color, full_rect.left, full_rect.top,
-                       full_rect.Width(), full_rect.Height(), nullptr, 0,
-                       blend_type);
+                       full_rect.Width(), full_rect.Height(), nullptr,
+                       FXDIB_ResampleOptions(), blend_type);
 }
 
 bool CGdiPrinterDriver::DrawDeviceText(int nChars,
-                                       const FXTEXT_CHARPOS* pCharPos,
+                                       const TextCharPos* pCharPos,
                                        CFX_Font* pFont,
-                                       const CFX_Matrix* pObject2Device,
+                                       const CFX_Matrix& mtObject2Device,
                                        float font_size,
                                        uint32_t color) {
 #if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
@@ -231,7 +234,8 @@
   lf.lfItalic = pFont->IsItalic();
   lf.lfCharSet = DEFAULT_CHARSET;
 
-  const WideString wsName = pFont->GetFaceName().UTF8Decode();
+  const WideString wsName =
+      WideString::FromUTF8(pFont->GetFaceName().AsStringView());
   size_t iNameLen =
       std::min(wsName.GetLength(), static_cast<size_t>(LF_FACESIZE - 1));
   memcpy(lf.lfFaceName, wsName.c_str(), sizeof(lf.lfFaceName[0]) * iNameLen);
@@ -271,19 +275,17 @@
   // Transforms
   SetGraphicsMode(m_hDC, GM_ADVANCED);
   XFORM xform;
-  xform.eM11 = pObject2Device->a / kScaleFactor;
-  xform.eM12 = pObject2Device->b / kScaleFactor;
-  xform.eM21 = -pObject2Device->c / kScaleFactor;
-  xform.eM22 = -pObject2Device->d / kScaleFactor;
-  xform.eDx = pObject2Device->e;
-  xform.eDy = pObject2Device->f;
+  xform.eM11 = mtObject2Device.a / kScaleFactor;
+  xform.eM12 = mtObject2Device.b / kScaleFactor;
+  xform.eM21 = -mtObject2Device.c / kScaleFactor;
+  xform.eM22 = -mtObject2Device.d / kScaleFactor;
+  xform.eDx = mtObject2Device.e;
+  xform.eDy = mtObject2Device.f;
   ModifyWorldTransform(m_hDC, &xform, MWT_LEFTMULTIPLY);
 
   // Color
-  int iUnusedAlpha;
-  FX_COLORREF rgb;
-  std::tie(iUnusedAlpha, rgb) = ArgbToColorRef(color);
-  SetTextColor(m_hDC, rgb);
+  FX_COLORREF colorref = ArgbToColorRef(color);
+  SetTextColor(m_hDC, colorref);
   SetBkMode(m_hDC, TRANSPARENT);
 
   // Text
@@ -293,7 +295,7 @@
   for (int i = 0; i < nChars; ++i) {
     // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary
     // values from PDFs.
-    const FXTEXT_CHARPOS& charpos = pCharPos[i];
+    const TextCharPos& charpos = pCharPos[i];
     ASSERT(charpos.m_AdjustMatrix[0] == 0);
     ASSERT(charpos.m_AdjustMatrix[1] == 0);
     ASSERT(charpos.m_AdjustMatrix[2] == 0);
@@ -304,7 +306,7 @@
     // error for calculating the next spacing value.
     float fOriginX = charpos.m_Origin.x * kScaleFactor;
     float fPixelSpacing = fOriginX - fPreviousOriginX;
-    spacing[i] = FXSYS_round(fPixelSpacing);
+    spacing[i] = FXSYS_roundf(fPixelSpacing);
     fPreviousOriginX = fOriginX - (fPixelSpacing - spacing[i]);
 
     wsText += charpos.m_GlyphIndex;
@@ -330,24 +332,41 @@
 #endif
 }
 
-CPSPrinterDriver::CPSPrinterDriver(HDC hDC, int pslevel, bool bCmykOutput)
-    : m_hDC(hDC), m_bCmykOutput(bCmykOutput) {
+CPSPrinterDriver::CPSPrinterDriver(HDC hDC,
+                                   WindowsPrintMode mode,
+                                   bool bCmykOutput,
+                                   const EncoderIface* pEncoderIface)
+    : m_hDC(hDC), m_bCmykOutput(bCmykOutput), m_PSRenderer(pEncoderIface) {
+  // |mode| should be PostScript.
+  ASSERT(mode == WindowsPrintMode::kModePostScript2 ||
+         mode == WindowsPrintMode::kModePostScript3 ||
+         mode == WindowsPrintMode::kModePostScript2PassThrough ||
+         mode == WindowsPrintMode::kModePostScript3PassThrough);
+  int pslevel = (mode == WindowsPrintMode::kModePostScript2 ||
+                 mode == WindowsPrintMode::kModePostScript2PassThrough)
+                    ? 2
+                    : 3;
+  CPSOutput::OutputMode output_mode =
+      (mode == WindowsPrintMode::kModePostScript2 ||
+       mode == WindowsPrintMode::kModePostScript3)
+          ? CPSOutput::OutputMode::kGdiComment
+          : CPSOutput::OutputMode::kExtEscape;
+
   m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE);
   m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE);
   m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
   m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
   m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
 
-  m_PSRenderer.Init(pdfium::MakeRetain<CPSOutput>(m_hDC), pslevel, m_Width,
-                    m_Height, bCmykOutput);
+  m_PSRenderer.Init(pdfium::MakeRetain<CPSOutput>(m_hDC, output_mode), pslevel,
+                    m_Width, m_Height, bCmykOutput);
   HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1);
-  int ret = ::GetClipRgn(hDC, hRgn);
-  if (ret == 1) {
-    ret = ::GetRegionData(hRgn, 0, NULL);
-    if (ret) {
-      RGNDATA* pData = reinterpret_cast<RGNDATA*>(FX_Alloc(uint8_t, ret));
-      ret = ::GetRegionData(hRgn, ret, pData);
-      if (ret) {
+  if (::GetClipRgn(m_hDC, hRgn) == 1) {
+    DWORD dwCount = ::GetRegionData(hRgn, 0, nullptr);
+    if (dwCount) {
+      std::vector<uint8_t> buffer(dwCount);
+      RGNDATA* pData = reinterpret_cast<RGNDATA*>(buffer.data());
+      if (::GetRegionData(hRgn, dwCount, pData)) {
         CFX_PathData path;
         for (uint32_t i = 0; i < pData->rdh.nCount; i++) {
           RECT* pRect =
@@ -359,7 +378,6 @@
         }
         m_PSRenderer.SetClip_PathFill(&path, nullptr, FXFILL_WINDING);
       }
-      FX_Free(pData);
     }
   }
   ::DeleteObject(hRgn);
@@ -369,10 +387,12 @@
   EndRendering();
 }
 
+DeviceType CPSPrinterDriver::GetDeviceType() const {
+  return DeviceType::kPrinter;
+}
+
 int CPSPrinterDriver::GetDeviceCaps(int caps_id) const {
   switch (caps_id) {
-    case FXDC_DEVICE_CLASS:
-      return FXDC_PRINTER;
     case FXDC_PIXEL_WIDTH:
       return m_Width;
     case FXDC_PIXEL_HEIGHT:
@@ -385,8 +405,10 @@
       return m_HorzSize;
     case FXDC_VERT_SIZE:
       return m_VertSize;
+    default:
+      NOTREACHED();
+      return 0;
   }
-  return 0;
 }
 
 bool CPSPrinterDriver::StartRendering() {
@@ -426,10 +448,9 @@
                                 FX_ARGB fill_color,
                                 FX_ARGB stroke_color,
                                 int fill_mode,
-                                int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL) {
+                                BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
-  }
   return m_PSRenderer.DrawPath(pPathData, pObject2Device, pGraphState,
                                fill_color, stroke_color, fill_mode & 3);
 }
@@ -439,56 +460,56 @@
   return true;
 }
 
-bool CPSPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CPSPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                                  uint32_t color,
-                                 const FX_RECT* pSrcRect,
+                                 const FX_RECT& src_rect,
                                  int left,
                                  int top,
-                                 int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                 BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
   return m_PSRenderer.SetDIBits(pBitmap, color, left, top);
 }
 
-bool CPSPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CPSPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                                      uint32_t color,
                                      int dest_left,
                                      int dest_top,
                                      int dest_width,
                                      int dest_height,
                                      const FX_RECT* pClipRect,
-                                     uint32_t flags,
-                                     int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                     const FXDIB_ResampleOptions& options,
+                                     BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
   return m_PSRenderer.StretchDIBits(pBitmap, color, dest_left, dest_top,
-                                    dest_width, dest_height, flags);
+                                    dest_width, dest_height, options);
 }
 
-bool CPSPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CPSPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                                    int bitmap_alpha,
                                    uint32_t color,
-                                   const CFX_Matrix* pMatrix,
-                                   uint32_t render_flags,
+                                   const CFX_Matrix& matrix,
+                                   const FXDIB_ResampleOptions& options,
                                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                                   int blend_type) {
-  if (blend_type != FXDIB_BLEND_NORMAL)
+                                   BlendMode blend_type) {
+  if (blend_type != BlendMode::kNormal)
     return false;
 
   if (bitmap_alpha < 255)
     return false;
 
   *handle = nullptr;
-  return m_PSRenderer.DrawDIBits(pBitmap, color, pMatrix, render_flags);
+  return m_PSRenderer.DrawDIBits(pBitmap, color, matrix, options);
 }
 
 bool CPSPrinterDriver::DrawDeviceText(int nChars,
-                                      const FXTEXT_CHARPOS* pCharPos,
+                                      const TextCharPos* pCharPos,
                                       CFX_Font* pFont,
-                                      const CFX_Matrix* pObject2Device,
+                                      const CFX_Matrix& mtObject2Device,
                                       float font_size,
                                       uint32_t color) {
-  return m_PSRenderer.DrawText(nChars, pCharPos, pFont, pObject2Device,
+  return m_PSRenderer.DrawText(nChars, pCharPos, pFont, mtObject2Device,
                                font_size, color);
 }
 
@@ -507,10 +528,12 @@
   EndRendering();
 }
 
+DeviceType CTextOnlyPrinterDriver::GetDeviceType() const {
+  return DeviceType::kPrinter;
+}
+
 int CTextOnlyPrinterDriver::GetDeviceCaps(int caps_id) const {
   switch (caps_id) {
-    case FXDC_DEVICE_CLASS:
-      return FXDC_PRINTER;
     case FXDC_PIXEL_WIDTH:
       return m_Width;
     case FXDC_PIXEL_HEIGHT:
@@ -523,8 +546,10 @@
       return m_HorzSize;
     case FXDC_VERT_SIZE:
       return m_VertSize;
+    default:
+      NOTREACHED();
+      return 0;
   }
-  return 0;
 }
 
 bool CTextOnlyPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData,
@@ -546,16 +571,16 @@
                                       uint32_t fill_color,
                                       uint32_t stroke_color,
                                       int fill_mode,
-                                      int blend_type) {
+                                      BlendMode blend_type) {
   return false;
 }
 
-bool CTextOnlyPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+bool CTextOnlyPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                                        uint32_t color,
-                                       const FX_RECT* pSrcRect,
+                                       const FX_RECT& src_rect,
                                        int left,
                                        int top,
-                                       int blend_type) {
+                                       BlendMode blend_type) {
   return false;
 }
 
@@ -568,33 +593,33 @@
 }
 
 bool CTextOnlyPrinterDriver::StretchDIBits(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pBitmap,
     uint32_t color,
     int dest_left,
     int dest_top,
     int dest_width,
     int dest_height,
     const FX_RECT* pClipRect,
-    uint32_t flags,
-    int blend_type) {
+    const FXDIB_ResampleOptions& options,
+    BlendMode blend_type) {
   return false;
 }
 
 bool CTextOnlyPrinterDriver::StartDIBits(
-    const RetainPtr<CFX_DIBSource>& pBitmap,
+    const RetainPtr<CFX_DIBBase>& pBitmap,
     int bitmap_alpha,
     uint32_t color,
-    const CFX_Matrix* pMatrix,
-    uint32_t render_flags,
+    const CFX_Matrix& matrix,
+    const FXDIB_ResampleOptions& options,
     std::unique_ptr<CFX_ImageRenderer>* handle,
-    int blend_type) {
+    BlendMode blend_type) {
   return false;
 }
 
 bool CTextOnlyPrinterDriver::DrawDeviceText(int nChars,
-                                            const FXTEXT_CHARPOS* pCharPos,
+                                            const TextCharPos* pCharPos,
                                             CFX_Font* pFont,
-                                            const CFX_Matrix* pObject2Device,
+                                            const CFX_Matrix& mtObject2Device,
                                             float font_size,
                                             uint32_t color) {
   if (g_pdfium_print_mode != 1)
@@ -606,26 +631,26 @@
   // errors below. Value chosen based on the title of https://crbug.com/18383
   const double kScaleFactor = 10;
 
-  WideString wsText;
-  int totalLength = nChars;
-
   // Detect new lines and add clrf characters (since this is Windows only).
   // These characters are removed by SkPDF, but the new line information is
   // preserved in the text location. clrf characters seem to be ignored by
   // label printers that use this driver.
-  if (m_SetOrigin &&
-      FXSYS_round(m_OriginY) != FXSYS_round(pObject2Device->f * kScaleFactor)) {
+  WideString wsText;
+  size_t len = nChars;
+  float fOffsetY = mtObject2Device.f * kScaleFactor;
+  if (m_SetOrigin && FXSYS_roundf(m_OriginY) != FXSYS_roundf(fOffsetY)) {
     wsText += L"\r\n";
-    totalLength += 2;
+    len += 2;
   }
-  m_OriginY = pObject2Device->f * kScaleFactor;
+  wsText.Reserve(len);
+  m_OriginY = fOffsetY;
   m_SetOrigin = true;
 
   // Text
   for (int i = 0; i < nChars; ++i) {
     // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary
     // values from PDFs.
-    const FXTEXT_CHARPOS& charpos = pCharPos[i];
+    const TextCharPos& charpos = pCharPos[i];
     ASSERT(charpos.m_AdjustMatrix[0] == 0);
     ASSERT(charpos.m_AdjustMatrix[1] == 0);
     ASSERT(charpos.m_AdjustMatrix[2] == 0);
@@ -634,16 +659,15 @@
 
     wsText += charpos.m_Unicode;
   }
-  size_t len = totalLength;
-  ByteString text = ByteString::FromUnicode(wsText);
-  while (len > 0) {
-    char buffer[1026];
-    size_t send_len = std::min(len, static_cast<size_t>(1024));
-    *(reinterpret_cast<uint16_t*>(buffer)) = send_len;
-    memcpy(buffer + 2, text.c_str(), send_len);
-    ::GdiComment(m_hDC, send_len + 2, reinterpret_cast<const BYTE*>(buffer));
-    len -= send_len;
-    text.Right(len);
+  ByteString text = wsText.ToDefANSI();
+  auto text_span = text.span();
+  while (!text_span.empty()) {
+    uint8_t buffer[1026];
+    size_t send_len = std::min<size_t>(text_span.size(), 1024);
+    *(reinterpret_cast<uint16_t*>(buffer)) = static_cast<uint16_t>(send_len);
+    memcpy(buffer + 2, text_span.data(), send_len);
+    ::GdiComment(m_hDC, send_len + 2, buffer);
+    text_span = text_span.subspan(send_len);
   }
   return true;
 }
diff --git a/core/fxge/win32/win32_int.h b/core/fxge/win32/win32_int.h
index d4f2597..7f299ef 100644
--- a/core/fxge/win32/win32_int.h
+++ b/core/fxge/win32/win32_int.h
@@ -13,22 +13,21 @@
 #include <vector>
 
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_pathdata.h"
-#include "core/fxge/ifx_renderdevicedriver.h"
+#include "core/fxge/cfx_windowsrenderdevice.h"
+#include "core/fxge/renderdevicedriver_iface.h"
 #include "core/fxge/win32/cfx_psrenderer.h"
 #include "core/fxge/win32/cpsoutput.h"
-#include "core/fxge/win32/dwrite_int.h"
+#include "third_party/base/optional.h"
 
 class CFX_ImageRenderer;
-class FXTEXT_CHARPOS;
+class TextCharPos;
 struct WINDIB_Open_Args_;
 
-typedef HANDLE(__stdcall* FuncType_GdiAddFontMemResourceEx)(PVOID pbFont,
-                                                            DWORD cbFont,
-                                                            PVOID pdv,
-                                                            DWORD* pcFonts);
-typedef BOOL(__stdcall* FuncType_GdiRemoveFontMemResourceEx)(HANDLE handle);
-
+RetainPtr<CFX_DIBitmap> FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
+                                                  LPVOID pData,
+                                                  bool bAlpha);
 class CGdiplusExt {
  public:
   CGdiplusExt();
@@ -36,16 +35,6 @@
 
   void Load();
   bool IsAvailable() { return !!m_hModule; }
-  bool StretchBitMask(HDC hDC,
-                      BOOL bMonoDevice,
-                      const RetainPtr<CFX_DIBitmap>& pBitmap,
-                      int dest_left,
-                      int dest_top,
-                      int dest_width,
-                      int dest_height,
-                      uint32_t argb,
-                      const FX_RECT* pClipRect,
-                      int flags);
   bool StretchDIBits(HDC hDC,
                      const RetainPtr<CFX_DIBitmap>& pBitmap,
                      int dest_left,
@@ -53,7 +42,7 @@
                      int dest_width,
                      int dest_height,
                      const FX_RECT* pClipRect,
-                     int flags);
+                     const FXDIB_ResampleOptions& options);
   bool DrawPath(HDC hDC,
                 const CFX_PathData* pPathData,
                 const CFX_Matrix* pObject2Device,
@@ -62,79 +51,38 @@
                 uint32_t stroke_argb,
                 int fill_mode);
 
-  void* LoadMemFont(uint8_t* pData, uint32_t size);
-  void DeleteMemFont(void* pFontCollection);
-  bool GdipCreateFromImage(void* bitmap, void** graphics);
-  void GdipDeleteGraphics(void* graphics);
-  void GdipSetTextRenderingHint(void* graphics, int mode);
-  void GdipSetPageUnit(void* graphics, uint32_t unit);
-  void GdipSetWorldTransform(void* graphics, void* pMatrix);
-  bool GdipDrawDriverString(void* graphics,
-                            unsigned short* text,
-                            int length,
-                            void* font,
-                            void* brush,
-                            void* positions,
-                            int flags,
-                            const void* matrix);
-  void GdipCreateBrush(uint32_t fill_argb, void** pBrush);
-  void GdipDeleteBrush(void* pBrush);
-  void GdipCreateMatrix(float a,
-                        float b,
-                        float c,
-                        float d,
-                        float e,
-                        float f,
-                        void** matrix);
-  void GdipDeleteMatrix(void* matrix);
-  bool GdipCreateFontFamilyFromName(const wchar_t* name,
-                                    void* pFontCollection,
-                                    void** pFamily);
-  void GdipDeleteFontFamily(void* pFamily);
-  bool GdipCreateFontFromFamily(void* pFamily,
-                                float font_size,
-                                int fontstyle,
-                                int flag,
-                                void** pFont);
-  void* GdipCreateFontFromCollection(void* pFontCollection,
-                                     float font_size,
-                                     int fontstyle);
-  void GdipDeleteFont(void* pFont);
-  bool GdipCreateBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap, void** bitmap);
-  void GdipDisposeImage(void* bitmap);
-  void GdipGetFontSize(void* pFont, float* size);
-  void* GdiAddFontMemResourceEx(void* pFontdata,
-                                uint32_t size,
-                                void* pdv,
-                                uint32_t* num_face);
-  bool GdiRemoveFontMemResourceEx(void* handle);
   RetainPtr<CFX_DIBitmap> LoadDIBitmap(WINDIB_Open_Args_ args);
 
   std::vector<FARPROC> m_Functions;
-  FuncType_GdiAddFontMemResourceEx m_pGdiAddFontMemResourceEx = nullptr;
-  FuncType_GdiRemoveFontMemResourceEx m_pGdiRemoveFontMemResourseEx = nullptr;
 
  protected:
   HMODULE m_hModule = nullptr;
   HMODULE m_GdiModule = nullptr;
 };
 
-class CWin32Platform {
+class CWin32Platform : public CFX_GEModule::PlatformIface {
  public:
-  bool m_bHalfTone;
+  CWin32Platform();
+  ~CWin32Platform() override;
+
+  // CFX_GEModule::PlatformIface:
+  void Init() override;
+
+  bool m_bHalfTone = false;
   CGdiplusExt m_GdiplusExt;
-  CDWriteExt m_DWriteExt;
 };
 
-class CGdiDeviceDriver : public IFX_RenderDeviceDriver {
+class CGdiDeviceDriver : public RenderDeviceDriverIface {
  protected:
-  CGdiDeviceDriver(HDC hDC, int device_class);
+  CGdiDeviceDriver(HDC hDC, DeviceType device_type);
   ~CGdiDeviceDriver() override;
 
-  // IFX_RenderDeviceDriver
+  // RenderDeviceDriverIface:
+  DeviceType GetDeviceType() const override;
   int GetDeviceCaps(int caps_id) const override;
   void SaveState() override;
   void RestoreState(bool bKeepSaved) override;
+  void SetBaseClip(const FX_RECT& rect) override;
   bool SetClip_PathFill(const CFX_PathData* pPathData,
                         const CFX_Matrix* pObject2Device,
                         int fill_mode) override;
@@ -147,20 +95,20 @@
                 uint32_t fill_color,
                 uint32_t stroke_color,
                 int fill_mode,
-                int blend_type) override;
-  bool FillRectWithBlend(const FX_RECT* pRect,
+                BlendMode blend_type) override;
+  bool FillRectWithBlend(const FX_RECT& rect,
                          uint32_t fill_color,
-                         int blend_type) override;
+                         BlendMode blend_type) override;
   bool DrawCosmeticLine(const CFX_PointF& ptMoveTo,
                         const CFX_PointF& ptLineTo,
                         uint32_t color,
-                        int blend_type) override;
+                        BlendMode blend_type) override;
   bool GetClipBox(FX_RECT* pRect) override;
 
   void DrawLine(float x1, float y1, float x2, float y2);
 
   bool GDI_SetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
-                     const FX_RECT* pSrcRect,
+                     const FX_RECT& src_rect,
                      int left,
                      int top);
   bool GDI_StretchDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
@@ -168,98 +116,101 @@
                          int dest_top,
                          int dest_width,
                          int dest_height,
-                         uint32_t flags);
+                         const FXDIB_ResampleOptions& options);
   bool GDI_StretchBitMask(const RetainPtr<CFX_DIBitmap>& pBitmap,
                           int dest_left,
                           int dest_top,
                           int dest_width,
                           int dest_height,
-                          uint32_t bitmap_color,
-                          uint32_t flags);
+                          uint32_t bitmap_color);
 
-  HDC m_hDC;
+  const HDC m_hDC;
   bool m_bMetafileDCType;
   int m_Width;
   int m_Height;
   int m_nBitsPerPixel;
-  int m_DeviceClass;
+  const DeviceType m_DeviceType;
   int m_RenderCaps;
+  pdfium::Optional<FX_RECT> m_BaseClipBox;
 };
 
-class CGdiDisplayDriver : public CGdiDeviceDriver {
+class CGdiDisplayDriver final : public CGdiDeviceDriver {
  public:
   explicit CGdiDisplayDriver(HDC hDC);
   ~CGdiDisplayDriver() override;
 
- protected:
+ private:
+  // CGdiDisplayDriver:
+  int GetDeviceCaps(int caps_id) const override;
   bool GetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
                  int left,
                  int top) override;
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                  uint32_t color,
-                 const FX_RECT* pSrcRect,
+                 const FX_RECT& src_rect,
                  int left,
                  int top,
-                 int blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      uint32_t color,
                      int dest_left,
                      int dest_top,
                      int dest_width,
                      int dest_height,
                      const FX_RECT* pClipRect,
-                     uint32_t flags,
-                     int blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                    int bitmap_alpha,
                    uint32_t color,
-                   const CFX_Matrix* pMatrix,
-                   uint32_t render_flags,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                   int blend_type) override;
-  bool UseFoxitStretchEngine(const RetainPtr<CFX_DIBSource>& pSource,
+                   BlendMode blend_type) override;
+  bool UseFoxitStretchEngine(const RetainPtr<CFX_DIBBase>& pSource,
                              uint32_t color,
                              int dest_left,
                              int dest_top,
                              int dest_width,
                              int dest_height,
                              const FX_RECT* pClipRect,
-                             int render_flags);
+                             const FXDIB_ResampleOptions& options);
 };
 
-class CGdiPrinterDriver : public CGdiDeviceDriver {
+class CGdiPrinterDriver final : public CGdiDeviceDriver {
  public:
   explicit CGdiPrinterDriver(HDC hDC);
   ~CGdiPrinterDriver() override;
 
- protected:
+ private:
+  // CGdiPrinterDriver:
   int GetDeviceCaps(int caps_id) const override;
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                  uint32_t color,
-                 const FX_RECT* pSrcRect,
+                 const FX_RECT& src_rect,
                  int left,
                  int top,
-                 int blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      uint32_t color,
                      int dest_left,
                      int dest_top,
                      int dest_width,
                      int dest_height,
                      const FX_RECT* pClipRect,
-                     uint32_t flags,
-                     int blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                    int bitmap_alpha,
                    uint32_t color,
-                   const CFX_Matrix* pMatrix,
-                   uint32_t render_flags,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                   int blend_type) override;
+                   BlendMode blend_type) override;
   bool DrawDeviceText(int nChars,
-                      const FXTEXT_CHARPOS* pCharPos,
+                      const TextCharPos* pCharPos,
                       CFX_Font* pFont,
-                      const CFX_Matrix* pObject2Device,
+                      const CFX_Matrix& mtObject2Device,
                       float font_size,
                       uint32_t color) override;
 
@@ -267,13 +218,17 @@
   const int m_VertSize;
 };
 
-class CPSPrinterDriver : public IFX_RenderDeviceDriver {
+class CPSPrinterDriver final : public RenderDeviceDriverIface {
  public:
-  CPSPrinterDriver(HDC hDC, int ps_level, bool bCmykOutput);
+  CPSPrinterDriver(HDC hDC,
+                   WindowsPrintMode mode,
+                   bool bCmykOutput,
+                   const EncoderIface* pEncoderIface);
   ~CPSPrinterDriver() override;
 
- protected:
-  // IFX_RenderDeviceDriver
+ private:
+  // RenderDeviceDriverIface:
+  DeviceType GetDeviceType() const override;
   int GetDeviceCaps(int caps_id) const override;
   bool StartRendering() override;
   void EndRendering() override;
@@ -291,39 +246,39 @@
                 uint32_t fill_color,
                 uint32_t stroke_color,
                 int fill_mode,
-                int blend_type) override;
+                BlendMode blend_type) override;
   bool GetClipBox(FX_RECT* pRect) override;
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                  uint32_t color,
-                 const FX_RECT* pSrcRect,
+                 const FX_RECT& src_rect,
                  int left,
                  int top,
-                 int blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      uint32_t color,
                      int dest_left,
                      int dest_top,
                      int dest_width,
                      int dest_height,
                      const FX_RECT* pClipRect,
-                     uint32_t flags,
-                     int blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                    int bitmap_alpha,
                    uint32_t color,
-                   const CFX_Matrix* pMatrix,
-                   uint32_t render_flags,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                   int blend_type) override;
+                   BlendMode blend_type) override;
   bool DrawDeviceText(int nChars,
-                      const FXTEXT_CHARPOS* pCharPos,
+                      const TextCharPos* pCharPos,
                       CFX_Font* pFont,
-                      const CFX_Matrix* pObject2Device,
+                      const CFX_Matrix& mtObject2Device,
                       float font_size,
                       uint32_t color) override;
 
   HDC m_hDC;
-  bool m_bCmykOutput;
+  const bool m_bCmykOutput;
   int m_Width;
   int m_Height;
   int m_nBitsPerPixel;
@@ -332,16 +287,17 @@
   CFX_PSRenderer m_PSRenderer;
 };
 
-class CTextOnlyPrinterDriver : public IFX_RenderDeviceDriver {
+class CTextOnlyPrinterDriver final : public RenderDeviceDriverIface {
  public:
   explicit CTextOnlyPrinterDriver(HDC hDC);
   ~CTextOnlyPrinterDriver() override;
 
- protected:
-  // IFX_RenderDeviceDriver
+ private:
+  // RenderDeviceDriverIface:
+  DeviceType GetDeviceType() const override;
   int GetDeviceCaps(int caps_id) const override;
-  void SaveState() override{};
-  void RestoreState(bool bKeepSaved) override{};
+  void SaveState() override {}
+  void RestoreState(bool bKeepSaved) override {}
   bool SetClip_PathFill(const CFX_PathData* pPathData,
                         const CFX_Matrix* pObject2Device,
                         int fill_mode) override;
@@ -354,43 +310,43 @@
                 uint32_t fill_color,
                 uint32_t stroke_color,
                 int fill_mode,
-                int blend_type) override;
+                BlendMode blend_type) override;
   bool GetClipBox(FX_RECT* pRect) override;
-  bool SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+  bool SetDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                  uint32_t color,
-                 const FX_RECT* pSrcRect,
+                 const FX_RECT& src_rect,
                  int left,
                  int top,
-                 int blend_type) override;
-  bool StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                 BlendMode blend_type) override;
+  bool StretchDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                      uint32_t color,
                      int dest_left,
                      int dest_top,
                      int dest_width,
                      int dest_height,
                      const FX_RECT* pClipRect,
-                     uint32_t flags,
-                     int blend_type) override;
-  bool StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap,
+                     const FXDIB_ResampleOptions& options,
+                     BlendMode blend_type) override;
+  bool StartDIBits(const RetainPtr<CFX_DIBBase>& pBitmap,
                    int bitmap_alpha,
                    uint32_t color,
-                   const CFX_Matrix* pMatrix,
-                   uint32_t render_flags,
+                   const CFX_Matrix& matrix,
+                   const FXDIB_ResampleOptions& options,
                    std::unique_ptr<CFX_ImageRenderer>* handle,
-                   int blend_type) override;
+                   BlendMode blend_type) override;
   bool DrawDeviceText(int nChars,
-                      const FXTEXT_CHARPOS* pCharPos,
+                      const TextCharPos* pCharPos,
                       CFX_Font* pFont,
-                      const CFX_Matrix* pObject2Device,
+                      const CFX_Matrix& mtObject2Device,
                       float font_size,
                       uint32_t color) override;
 
   HDC m_hDC;
-  int m_Width;
-  int m_Height;
+  const int m_Width;
+  const int m_Height;
   int m_nBitsPerPixel;
-  int m_HorzSize;
-  int m_VertSize;
+  const int m_HorzSize;
+  const int m_VertSize;
   float m_OriginY;
   bool m_SetOrigin;
 };
diff --git a/docs/code-coverage.md b/docs/code-coverage.md
index 200a674..3c4e226 100644
--- a/docs/code-coverage.md
+++ b/docs/code-coverage.md
@@ -10,52 +10,28 @@
 You will need the PDFium source code on your computer. You can see
 the [README](/README.md) for instructions on checking out PDFium's source.
 
-The tools used for code coverage are known to work on Ubuntu 14.04. They should
-work correctly on newer versions of Ubuntu and related Linux distros. They have
-not been tested on Windows and Mac.
+The tools used for code coverage are known to work on Ubuntu and Debian. They
+should work correctly on newer versions of Ubuntu, Debian and related Linux
+distros. They have not been tested on Windows and Mac.
 
-### lcov
-
-The code coverage scripts depend on having a version of `lcov` of 1.11 or
-greater available, which is enforced by the script. Unfortunately the default
-version of `lcov` for Ubuntu 14.04 is 1.10, thus you will need to install a
-newer version.
-
-You can build a newer version of `lcov` from source, which is
-available [here](http://ltp.sourceforge.net/coverage/lcov.php).
-
-If you don't want to build from source and use an RPM based Linux, not
-Ubuntu/Debian, then there are pre-built RPMs
-available [here](http://downloads.sourceforge.net/ltp/lcov-1.13-1.noarch.rpm).
-
-For Ubuntu/Debian users these RPMs can be converted to .deb using `alien`. More
-information about how to do this can be found in `man alien`.
-
-### llvm-cov
-
-The other external dependency for generating code coverage information is having
-a version of `llvm-cov` that supports the `gcov` command. This should be all
-versions of 3.5.0 or greater.
-
-Again, unfortunately, the default llvm-cov that comes with Ubuntu 14.04, 3.4, is
-lower then what is needed. The 14.04 repositories do support having multiple
-versions of the `llvm` package, and thus `llvm-cov`. Through your favourite
-package manager you should be able to install any version of `llvm` of 3.5 or
-greater and the coverage scripts should find it.
+Previously, the code coverage scripts required specific versions of `lcov` and
+`llvm-cov` to be present. This is no longer true, so if you have no other need
+for them they can be removed. All of the required tools will be pulled into the
+Clang build tools directory by the script.
 
 ## Generating Code Coverage
 
 ### Setup
 
-This step assumes that you have already checked out the PDFium source code and
-installed the proper versions of the external tools. If you have not, please
-consult the above Prerequisites section.
+This step assumes that you have already checked out the PDFium source code. If
+you have not, please consult the Prerequisites section above.
 
 Before generating code coverage information, you will need to have a build
 directory with coverage enabled. This can be done by running the `gn args`
-command and adding `use_coverage = true` in the editor that is opened. If not
-using the default directory, `out/Coverage`, then replace it with the correct
-location in the following command.
+command and adding `use_clang_coverage = true` in the editor that is opened.
+
+If not using the default directory, `out/Coverage`, then replace it with the
+correct location in the following command.
 
 ```shell
 gn args out/Coverage
@@ -67,23 +43,24 @@
 command.
 
 ```shell
-echo "use_coverage = true" >> out/Coverage/args.gn
+echo "use_clang_coverage = true" >> out/Coverage/args.gn
 ```
 
+Previous versions of code coverage used **use_coverage = true** in args.gn; this
+needs to be changed to **use_clang_coverage = true**
 
 ### Usage
 
 Generating code coverage information is done via the
-`testing/tools/coverage/coverage_report.py` script. This script will build any binaries
-that it needs, perform test runs, collect coverage data, and finally generate a
-nice HTML coverage report.
+`testing/tools/coverage/coverage_report.py` script. This script will download
+the Clang coverage tools if needed, build any binaries that it needs, perform
+test runs, collect coverage data, and generate a HTML coverage report. It is
+based on the Chromium coverage scripts, so will generate the same style of
+report.
 
-Running the script with no arguments, as below, will assume that you are
+Running the script with no arguments, as below, it will assume that you are
 currently at the root of your PDFium checkout, the build directory to use is
-`./out/Coverage/` and that HTML should be outputted to `./coverage_report/`. By
-default, it will also only run `pdfium_unittests` and `pdfium_embeddertests` for
-coverage data. This is because the other tests are known to take a long time to
-run, so they are not included in the defaults.
+`./out/Coverage/` and that HTML should be outputted to `./coverage_report/`.
 
 ```shell
 testing/tools/coverage/coverage_report.py
@@ -104,29 +81,21 @@
     --output-directory ~/Documents/PDFium_coverage
 ```
 
-To run different tests then the default set, there are two ways to achieve
-this. If you want to run everything, including tests that are known to take a
-long time, then you just need to add the `--slow` flag.
+To run different tests than the default set, including running just a single
+test, you can specify the test names on the command line. The list of supported
+tests can be found by running the script with `--help`.
 
-```shell
-testing/tools/coverage/coverage_report.py --slow
-```
-
-If you want more fine grained control, including running just a single test, you
-can specify the test names on the command line. The `--slow` flag is not needed
-if you are explicitly invoking tests. The list of supported tests can be found
-by running the script with `--help`.
-
-An example running the default tests explicitly:
+For example, running all of the tests that don't use pdfium_test:
 
 ```shell
 testing/tools/coverage/coverage_report.py pdfium_unittests pdfium_embeddertests
 ```
 
-NOTE:
-At the present time, there is no mechanism for combining data from different
-invocations of `coverage_report.py`. Instead you must specify all of the tests
-to be included in the report in a single invocation.
+NOTE: At the present time, there is no mechanism for combining data from
+different invocations of `coverage_report.py`. Instead, you must specify all of
+the tests to be included in the report in a single invocation. Alternatively,
+you can collect the profiling data that is generated from each run and manually
+invoke `tools/code_coverage/coverage.py` to generate a combined report.
 
 There are additional developer debugging flags available, `--dry-run` and
 `--verbose`. `--dry-run` will output a trace of commands that would have been
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 0cf2ff3..6077d21 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -30,10 +30,10 @@
 
 ## Initializing PDFium
 
-The first step to using PDFium is to initialize the library. Having done so,
-you'll need to destroy the library when you're finished. When initializing the
-library you provide the `FPDF_LIBRARY_CONFIG` parameters to
-`FPDF_InitLibraryWithConfig`.
+The first step to using PDFium is to initialize the library. Having done so, one
+must destroy the library with `FPDF_DestroyLibrary()` when finished. When
+initializing the library, provide the `FPDF_LIBRARY_CONFIG` parameters to
+`FPDF_InitLibraryWithConfig()`.
 
 ```c
 #include <fpdfview.h>
@@ -52,16 +52,19 @@
 }
 ```
 
-Currently the `config.version` must be set to `2`. `m_pUserFontPaths` can be
-used to override the font paths searched by PDFium. If you wish to use your
-own font paths pass a `NULL` terminated list of `const char*` paths to use.
+Currently the `config.version` must be set to `2`. Older versions works for
+backwards compatibility, but will be deprecated eventually.
+
+`m_pUserFontPaths` can be used to override the font paths searched by PDFium. To
+use a custom font paths, pass a `NULL` terminated list of `const char*` paths to
+use.
 
 `m_pIsolate` and `m_v8EmbedderSlot` are both used to configure the V8
-javascript engine. In the first case, you can provide an isolate through
-`m_pIsolate` for PDFium to use to store per-isolate data. Passing `NULL` will
-case PDFium to allocate a new isolate. `m_v8EmbedderSlot` is the embedder data
-slot to use in the v8::Isolate to store PDFium data. The value must be between
-0 and v8::Internals::kNumIsolateDataSlots. Typically, 0 is a good choice.
+JavaScript engine. For the V8 isolate, either provide an isolate through
+`m_pIsolate` for PDFium to use to store per-isolate data, or pass `NULL` to tell
+PDFium to allocate a new isolate. `m_v8EmbedderSlot` is the embedder data slot
+to use in the v8::Isolate to store PDFium data. The value must be in the range
+[0, `v8::Internals::kNumIsolateDataSlots`). Typically, 0 is a good choice.
 
 For more information on using Javascript see the [V8 Getting Started][pdfium-v8]
 guide.
diff --git a/docs/safetynet.md b/docs/safetynet.md
index 540f7db..bd0a791 100644
--- a/docs/safetynet.md
+++ b/docs/safetynet.md
@@ -106,6 +106,9 @@
 $ valgrind
 ```
 
+Add `ro_segment_workaround_for_valgrind=true` to `args.gn` for symbols to appear
+correctly.
+
 This is a slow and accurate profiler. Expect variations of around 100
 instructions. However, this takes about 50 times longer to run than perf stat.
 
@@ -116,6 +119,11 @@
 can be analyzed to find the cause of a regression. KCachegrind is a good
 visualizer for these files.
 
+#### none
+
+Run without any profiler, giving a performance score of 1 always. useful for
+running image comparisons or debugging the script.
+
 ### Common Options
 
 Arguments commonly passed to safetynet_compare.py.
@@ -156,4 +164,43 @@
 
 ## Setup a nightly job
 
-TODO: Complete with safetynet_job.py setup and usage.
+Create a separate checkout of pdfium in a new directory, for example `~/job`.
+The safetynet_job.py script will run from this directory. This checkout needs to
+be `git pull`'ed when there are changes to the SafetyNet scripts, but otherwise
+it can be left alone.
+
+Create a directory to contain the job results, for example `~/job_results`. In
+each run, a `.log` file with the results will be written to this directory and a
+subdirectory will be created with the other artifacts.
+
+Setup a cron job to run safetynet_job.py nightly. The example below runs it at
+1:42 AM, over the corpus in two directories: `~/pdf_samples/thousand_pdfs` and
+`~/pdf_samples/i18n`
+
+```shell
+@ crontab -e
+42 1 * * * bash -lc '~/job/pdfium/testing/tools/safetynet_job.py ~/job_results ~/pdf_samples/thousand_pdfs ~/pdf_samples/i18n --output-to-log >> ~/job_results/cron_nightly.log 2>&1'
+```
+
+The first time the job runs, it will just create a checkpoint as
+`~/job_results/last_revision_covered`. From then on, since a checkpoint is
+available, each run will compare performance with the last checkpoint and update
+the checkpoint.
+
+## Run image comparison
+
+Pass the `--png-dir` option pointing at an output directory to compare the output
+images from rendering the "before" and the "after" branches with pdfium_test.
+
+```shell
+$ mkdir ~/output_images
+$ testing/tools/safetynet_compare.py ~/pdf_samples --branch-before before_visual_changes --branch-after after_visual_changes --png-dir ~/output_images
+```
+
+This will output and automatically open a `~/output_images/compare.html` file
+showing the before/after and the diff. Hover the mouse cursor over the
+before/after image on the left for an easier visual comparison. The "before"
+image is displayed until the cursor hovers over the image, which is then
+replaced with the "after" image.
+
+It is recommended to use `--profiler=none` with this option.
diff --git a/fpdfsdk/Android.bp b/fpdfsdk/Android.bp
new file mode 100644
index 0000000..af37009
--- /dev/null
+++ b/fpdfsdk/Android.bp
@@ -0,0 +1,35 @@
+cc_library_static {
+    name: "libpdfium-fpdfsdk",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-fdrm",
+        "libpdfium-edit",
+        "libpdfium-font",
+        "libpdfium-page",
+        "libpdfium-parser",
+        "libpdfium-render",
+        "libpdfium-fpdfdoc",
+        "libpdfium-fpdftext",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+        "libpdfium-fxjs",
+        "libpdfium-formfiller",
+        "libpdfium-pwl",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+
+    include_dirs: [
+        "external/freetype/include",
+        "external/freetype/include/freetype",
+    ],
+}
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
new file mode 100644
index 0000000..9eb9562
--- /dev/null
+++ b/fpdfsdk/BUILD.gn
@@ -0,0 +1,166 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../pdfium.gni")
+import("../testing/test.gni")
+
+source_set("fpdfsdk") {
+  sources = [
+    "cpdfsdk_actionhandler.cpp",
+    "cpdfsdk_actionhandler.h",
+    "cpdfsdk_annot.cpp",
+    "cpdfsdk_annot.h",
+    "cpdfsdk_annothandlermgr.cpp",
+    "cpdfsdk_annothandlermgr.h",
+    "cpdfsdk_annotiteration.cpp",
+    "cpdfsdk_annotiteration.h",
+    "cpdfsdk_annotiterator.cpp",
+    "cpdfsdk_annotiterator.h",
+    "cpdfsdk_appstream.cpp",
+    "cpdfsdk_appstream.h",
+    "cpdfsdk_baannot.cpp",
+    "cpdfsdk_baannot.h",
+    "cpdfsdk_baannothandler.cpp",
+    "cpdfsdk_baannothandler.h",
+    "cpdfsdk_customaccess.cpp",
+    "cpdfsdk_customaccess.h",
+    "cpdfsdk_fieldaction.cpp",
+    "cpdfsdk_fieldaction.h",
+    "cpdfsdk_filewriteadapter.cpp",
+    "cpdfsdk_filewriteadapter.h",
+    "cpdfsdk_formfillenvironment.cpp",
+    "cpdfsdk_formfillenvironment.h",
+    "cpdfsdk_helpers.cpp",
+    "cpdfsdk_helpers.h",
+    "cpdfsdk_interactiveform.cpp",
+    "cpdfsdk_interactiveform.h",
+    "cpdfsdk_pageview.cpp",
+    "cpdfsdk_pageview.h",
+    "cpdfsdk_pauseadapter.cpp",
+    "cpdfsdk_pauseadapter.h",
+    "cpdfsdk_renderpage.cpp",
+    "cpdfsdk_renderpage.h",
+    "cpdfsdk_widget.cpp",
+    "cpdfsdk_widget.h",
+    "cpdfsdk_widgethandler.cpp",
+    "cpdfsdk_widgethandler.h",
+    "fpdf_annot.cpp",
+    "fpdf_attachment.cpp",
+    "fpdf_catalog.cpp",
+    "fpdf_dataavail.cpp",
+    "fpdf_doc.cpp",
+    "fpdf_editimg.cpp",
+    "fpdf_editpage.cpp",
+    "fpdf_editpath.cpp",
+    "fpdf_edittext.cpp",
+    "fpdf_ext.cpp",
+    "fpdf_flatten.cpp",
+    "fpdf_formfill.cpp",
+    "fpdf_javascript.cpp",
+    "fpdf_ppo.cpp",
+    "fpdf_progressive.cpp",
+    "fpdf_save.cpp",
+    "fpdf_searchex.cpp",
+    "fpdf_structtree.cpp",
+    "fpdf_sysfontinfo.cpp",
+    "fpdf_text.cpp",
+    "fpdf_thumbnail.cpp",
+    "fpdf_transformpage.cpp",
+    "fpdf_view.cpp",
+    "ipdfsdk_annothandler.h",
+  ]
+
+  configs += [ "../:pdfium_core_config" ]
+  deps = [
+    "../:pdfium_public_headers",
+    "../constants",
+    "../core/fdrm",
+    "../core/fpdfapi/edit",
+    "../core/fpdfapi/font",
+    "../core/fpdfapi/page",
+    "../core/fpdfapi/parser",
+    "../core/fpdfapi/render",
+    "../core/fpdfdoc",
+    "../core/fpdftext",
+    "../core/fxcrt",
+    "../core/fxge",
+    "../fxjs",
+    "formfiller",
+    "pwl",
+  ]
+  allow_circular_includes_from = [
+    "../fxjs",
+    "formfiller",
+  ]
+  visibility = [ "../*" ]
+
+  if (pdf_enable_xfa) {
+    deps += [
+      "../fxbarcode",
+      "../xfa/fxfa",
+      "../xfa/fxfa/parser",
+      "fpdfxfa",
+    ]
+    allow_circular_includes_from += [ "fpdfxfa" ]
+  }
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "fpdf_annot_unittest.cpp",
+    "fpdf_catalog_unittest.cpp",
+    "fpdf_doc_unittest.cpp",
+    "fpdf_edit_unittest.cpp",
+    "fpdf_editimg_unittest.cpp",
+    "fpdf_view_unittest.cpp",
+  ]
+  deps = [
+    ":fpdfsdk",
+    "../:pdfium_public_headers",
+    "../constants",
+    "../core/fpdfapi/page",
+    "../core/fpdfapi/parser",
+    "../core/fpdfapi/render",
+    "../core/fpdfdoc",
+  ]
+  pdfium_root_dir = "../"
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [
+    "cpdfsdk_annotiterator_embeddertest.cpp",
+    "cpdfsdk_baannothandler_embeddertest.cpp",
+    "fpdf_annot_embeddertest.cpp",
+    "fpdf_attachment_embeddertest.cpp",
+    "fpdf_dataavail_embeddertest.cpp",
+    "fpdf_doc_embeddertest.cpp",
+    "fpdf_edit_embeddertest.cpp",
+    "fpdf_editpage_embeddertest.cpp",
+    "fpdf_editpath_embeddertest.cpp",
+    "fpdf_ext_embeddertest.cpp",
+    "fpdf_flatten_embeddertest.cpp",
+    "fpdf_formfill_embeddertest.cpp",
+    "fpdf_javascript_embeddertest.cpp",
+    "fpdf_ppo_embeddertest.cpp",
+    "fpdf_save_embeddertest.cpp",
+    "fpdf_searchex_embeddertest.cpp",
+    "fpdf_structtree_embeddertest.cpp",
+    "fpdf_sysfontinfo_embeddertest.cpp",
+    "fpdf_text_embeddertest.cpp",
+    "fpdf_thumbnail_embeddertest.cpp",
+    "fpdf_transformpage_embeddertest.cpp",
+    "fpdf_view_c_api_test.c",
+    "fpdf_view_c_api_test.h",
+    "fpdf_view_embeddertest.cpp",
+  ]
+  deps = [
+    ":fpdfsdk",
+    "../constants",
+    "../core/fpdfapi/font",
+    "../core/fpdfapi/page",
+    "../core/fpdfapi/parser",
+    "../core/fxge",
+  ]
+  pdfium_root_dir = "../"
+}
diff --git a/fpdfsdk/DEPS b/fpdfsdk/DEPS
index 95ddfe1..5edee08 100644
--- a/fpdfsdk/DEPS
+++ b/fpdfsdk/DEPS
@@ -7,4 +7,4 @@
   '+fxbarcode',
   '+xfa/fxfa',
   '+xfa/fxgraphics',
-  ]
+]
diff --git a/fpdfsdk/PRESUBMIT.py b/fpdfsdk/PRESUBMIT.py
new file mode 100644
index 0000000..17f7c6a
--- /dev/null
+++ b/fpdfsdk/PRESUBMIT.py
@@ -0,0 +1,42 @@
+# Copyright 2019 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit script for pdfium.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def _CheckApiTestFile(input_api, output_api):
+  """Checks that the public headers match the API tests."""
+  api_test_file = input_api.os_path.normpath('fpdfsdk/fpdf_view_c_api_test.c')
+
+  def is_api_test_file(f):
+    return input_api.os_path.normpath(f.LocalPath()) == api_test_file
+
+  if all([not is_api_test_file(f) for f in input_api.AffectedSourceFiles([])]):
+    return []
+
+  src_path = input_api.os_path.dirname(input_api.PresubmitLocalPath())
+  check_script = input_api.os_path.join(
+      src_path, 'testing' , 'tools' , 'api_check.py')
+  cmd = [input_api.python_executable, check_script]
+  try:
+    input_api.subprocess.check_output(cmd)
+    return []
+  except input_api.subprocess.CalledProcessError as error:
+    return [output_api.PresubmitError('api_check.py failed:',
+                                      long_text=error.output)]
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results += _CheckApiTestFile(input_api, output_api)
+  return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  results = []
+  results += _CheckApiTestFile(input_api, output_api)
+  return results
diff --git a/fpdfsdk/cba_annotiterator.cpp b/fpdfsdk/cba_annotiterator.cpp
deleted file mode 100644
index d1c9599..0000000
--- a/fpdfsdk/cba_annotiterator.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cba_annotiterator.h"
-
-#include <algorithm>
-
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-
-namespace {
-
-CFX_FloatRect GetAnnotRect(const CPDFSDK_Annot* pAnnot) {
-  return pAnnot->GetPDFAnnot()->GetRect();
-}
-
-bool CompareByLeftAscending(const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) {
-  return GetAnnotRect(p1).left < GetAnnotRect(p2).left;
-}
-
-bool CompareByTopDescending(const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) {
-  return GetAnnotRect(p1).top > GetAnnotRect(p2).top;
-}
-
-}  // namespace
-
-CBA_AnnotIterator::CBA_AnnotIterator(CPDFSDK_PageView* pPageView,
-                                     CPDF_Annot::Subtype nAnnotSubtype)
-    : m_eTabOrder(STRUCTURE),
-      m_pPageView(pPageView),
-      m_nAnnotSubtype(nAnnotSubtype) {
-  CPDF_Page* pPDFPage = m_pPageView->GetPDFPage();
-  ByteString sTabs = pPDFPage->m_pFormDict->GetStringFor("Tabs");
-  if (sTabs == "R")
-    m_eTabOrder = ROW;
-  else if (sTabs == "C")
-    m_eTabOrder = COLUMN;
-
-  GenerateResults();
-}
-
-CBA_AnnotIterator::~CBA_AnnotIterator() {}
-
-CPDFSDK_Annot* CBA_AnnotIterator::GetFirstAnnot() {
-  return m_Annots.empty() ? nullptr : m_Annots.front();
-}
-
-CPDFSDK_Annot* CBA_AnnotIterator::GetLastAnnot() {
-  return m_Annots.empty() ? nullptr : m_Annots.back();
-}
-
-CPDFSDK_Annot* CBA_AnnotIterator::GetNextAnnot(CPDFSDK_Annot* pAnnot) {
-  auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot);
-  if (iter == m_Annots.end())
-    return nullptr;
-  ++iter;
-  if (iter == m_Annots.end())
-    iter = m_Annots.begin();
-  return *iter;
-}
-
-CPDFSDK_Annot* CBA_AnnotIterator::GetPrevAnnot(CPDFSDK_Annot* pAnnot) {
-  auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot);
-  if (iter == m_Annots.end())
-    return nullptr;
-  if (iter == m_Annots.begin())
-    iter = m_Annots.end();
-  return *(--iter);
-}
-
-void CBA_AnnotIterator::CollectAnnots(std::vector<CPDFSDK_Annot*>* pArray) {
-  for (auto* pAnnot : m_pPageView->GetAnnotList()) {
-    if (pAnnot->GetAnnotSubtype() == m_nAnnotSubtype &&
-        !pAnnot->IsSignatureWidget()) {
-      pArray->push_back(pAnnot);
-    }
-  }
-}
-
-CFX_FloatRect CBA_AnnotIterator::AddToAnnotsList(
-    std::vector<CPDFSDK_Annot*>* sa,
-    size_t idx) {
-  CPDFSDK_Annot* pLeftTopAnnot = sa->at(idx);
-  CFX_FloatRect rcLeftTop = GetAnnotRect(pLeftTopAnnot);
-  m_Annots.push_back(pLeftTopAnnot);
-  sa->erase(sa->begin() + idx);
-  return rcLeftTop;
-}
-
-void CBA_AnnotIterator::AddSelectedToAnnots(std::vector<CPDFSDK_Annot*>* sa,
-                                            std::vector<size_t>* aSelect) {
-  for (size_t i = 0; i < aSelect->size(); ++i)
-    m_Annots.push_back(sa->at(aSelect->at(i)));
-
-  for (int i = aSelect->size() - 1; i >= 0; --i)
-    sa->erase(sa->begin() + aSelect->at(i));
-}
-
-void CBA_AnnotIterator::GenerateResults() {
-  switch (m_eTabOrder) {
-    case STRUCTURE:
-      CollectAnnots(&m_Annots);
-      break;
-
-    case ROW: {
-      std::vector<CPDFSDK_Annot*> sa;
-      CollectAnnots(&sa);
-      std::sort(sa.begin(), sa.end(), CompareByLeftAscending);
-
-      while (!sa.empty()) {
-        int nLeftTopIndex = -1;
-        float fTop = 0.0f;
-        for (int i = sa.size() - 1; i >= 0; i--) {
-          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
-          if (rcAnnot.top > fTop) {
-            nLeftTopIndex = i;
-            fTop = rcAnnot.top;
-          }
-        }
-        if (nLeftTopIndex < 0)
-          continue;
-
-        CFX_FloatRect rcLeftTop = AddToAnnotsList(&sa, nLeftTopIndex);
-
-        std::vector<size_t> aSelect;
-        for (size_t i = 0; i < sa.size(); ++i) {
-          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
-          float fCenterY = (rcAnnot.top + rcAnnot.bottom) / 2.0f;
-          if (fCenterY > rcLeftTop.bottom && fCenterY < rcLeftTop.top)
-            aSelect.push_back(i);
-        }
-        AddSelectedToAnnots(&sa, &aSelect);
-      }
-      break;
-    }
-
-    case COLUMN: {
-      std::vector<CPDFSDK_Annot*> sa;
-      CollectAnnots(&sa);
-      std::sort(sa.begin(), sa.end(), CompareByTopDescending);
-
-      while (!sa.empty()) {
-        int nLeftTopIndex = -1;
-        float fLeft = -1.0f;
-        for (int i = sa.size() - 1; i >= 0; --i) {
-          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
-          if (fLeft < 0) {
-            nLeftTopIndex = 0;
-            fLeft = rcAnnot.left;
-          } else if (rcAnnot.left < fLeft) {
-            nLeftTopIndex = i;
-            fLeft = rcAnnot.left;
-          }
-        }
-        if (nLeftTopIndex < 0)
-          continue;
-
-        CFX_FloatRect rcLeftTop = AddToAnnotsList(&sa, nLeftTopIndex);
-
-        std::vector<size_t> aSelect;
-        for (size_t i = 0; i < sa.size(); ++i) {
-          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
-          float fCenterX = (rcAnnot.left + rcAnnot.right) / 2.0f;
-          if (fCenterX > rcLeftTop.left && fCenterX < rcLeftTop.right)
-            aSelect.push_back(i);
-        }
-        AddSelectedToAnnots(&sa, &aSelect);
-      }
-      break;
-    }
-  }
-}
diff --git a/fpdfsdk/cba_annotiterator.h b/fpdfsdk/cba_annotiterator.h
deleted file mode 100644
index 8f9768d..0000000
--- a/fpdfsdk/cba_annotiterator.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CBA_ANNOTITERATOR_H_
-#define FPDFSDK_CBA_ANNOTITERATOR_H_
-
-#include <vector>
-
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDFSDK_Annot;
-class CPDFSDK_PageView;
-
-class CBA_AnnotIterator {
- public:
-  enum TabOrder { STRUCTURE = 0, ROW, COLUMN };
-
-  CBA_AnnotIterator(CPDFSDK_PageView* pPageView,
-                    CPDF_Annot::Subtype nAnnotSubtype);
-  ~CBA_AnnotIterator();
-
-  CPDFSDK_Annot* GetFirstAnnot();
-  CPDFSDK_Annot* GetLastAnnot();
-  CPDFSDK_Annot* GetNextAnnot(CPDFSDK_Annot* pAnnot);
-  CPDFSDK_Annot* GetPrevAnnot(CPDFSDK_Annot* pAnnot);
-
- private:
-  void GenerateResults();
-  void CollectAnnots(std::vector<CPDFSDK_Annot*>* pArray);
-  CFX_FloatRect AddToAnnotsList(std::vector<CPDFSDK_Annot*>* sa, size_t idx);
-  void AddSelectedToAnnots(std::vector<CPDFSDK_Annot*>* sa,
-                           std::vector<size_t>* aSelect);
-
-  TabOrder m_eTabOrder;
-  UnownedPtr<CPDFSDK_PageView> m_pPageView;
-  CPDF_Annot::Subtype m_nAnnotSubtype;
-  std::vector<CPDFSDK_Annot*> m_Annots;
-};
-
-#endif  // FPDFSDK_CBA_ANNOTITERATOR_H_
diff --git a/fpdfsdk/cfx_systemhandler.cpp b/fpdfsdk/cfx_systemhandler.cpp
deleted file mode 100644
index bbb0293..0000000
--- a/fpdfsdk/cfx_systemhandler.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cfx_systemhandler.h"
-
-#include <memory>
-
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxge/cfx_font.h"
-#include "core/fxge/cfx_fontmapper.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
-
-namespace {
-
-int CharSet2CP(int charset) {
-  if (charset == FX_CHARSET_ShiftJIS)
-    return FX_CODEPAGE_ShiftJIS;
-  if (charset == FX_CHARSET_ChineseSimplified)
-    return FX_CODEPAGE_ChineseSimplified;
-  if (charset == FX_CHARSET_Hangul)
-    return FX_CODEPAGE_Hangul;
-  if (charset == FX_CHARSET_ChineseTraditional)
-    return FX_CODEPAGE_ChineseTraditional;
-  return FX_CODEPAGE_DefANSI;
-}
-
-}  // namespace
-
-CFX_SystemHandler::CFX_SystemHandler(CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv) {}
-
-CFX_SystemHandler::~CFX_SystemHandler() {}
-
-void CFX_SystemHandler::InvalidateRect(CPDFSDK_Widget* widget,
-                                       const CFX_FloatRect& rect) {
-  CPDFSDK_PageView* pPageView = widget->GetPageView();
-  UnderlyingPageType* pPage = widget->GetUnderlyingPage();
-  if (!pPage || !pPageView)
-    return;
-
-  CFX_Matrix page2device;
-  pPageView->GetCurrentMatrix(page2device);
-
-  CFX_Matrix device2page = page2device.GetInverse();
-
-  CFX_PointF left_top = device2page.Transform(CFX_PointF(rect.left, rect.top));
-  CFX_PointF right_bottom =
-      device2page.Transform(CFX_PointF(rect.right, rect.bottom));
-
-  CFX_FloatRect rcPDF(left_top.x, right_bottom.y, right_bottom.x, left_top.y);
-  rcPDF.Normalize();
-  m_pFormFillEnv->Invalidate(pPage, rcPDF.GetOuterRect());
-}
-
-void CFX_SystemHandler::OutputSelectedRect(CFFL_FormFiller* pFormFiller,
-                                           CFX_FloatRect& rect) {
-  if (!pFormFiller)
-    return;
-
-  CFX_PointF ptA = pFormFiller->PWLtoFFL(CFX_PointF(rect.left, rect.bottom));
-  CFX_PointF ptB = pFormFiller->PWLtoFFL(CFX_PointF(rect.right, rect.top));
-
-  CPDFSDK_Annot* pAnnot = pFormFiller->GetSDKAnnot();
-  UnderlyingPageType* pPage = pAnnot->GetUnderlyingPage();
-  ASSERT(pPage);
-
-  m_pFormFillEnv->OutputSelectedRect(pPage,
-                                     CFX_FloatRect(ptA.x, ptA.y, ptB.x, ptB.y));
-}
-
-bool CFX_SystemHandler::IsSelectionImplemented() const {
-  if (!m_pFormFillEnv)
-    return false;
-
-  FPDF_FORMFILLINFO* pInfo = m_pFormFillEnv->GetFormFillInfo();
-  return pInfo && pInfo->FFI_OutputSelectedRect;
-}
-
-void CFX_SystemHandler::SetCursor(int32_t nCursorType) {
-  m_pFormFillEnv->SetCursor(nCursorType);
-}
-
-bool CFX_SystemHandler::FindNativeTrueTypeFont(ByteString sFontFaceName) {
-  CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
-  if (!pFontMgr)
-    return false;
-
-  CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper();
-  if (!pFontMapper)
-    return false;
-
-  if (pFontMapper->m_InstalledTTFonts.empty())
-    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;
-}
-
-CPDF_Font* CFX_SystemHandler::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, CharSet2CP(nCharset), false);
-  return pDoc->AddFont(pFXFont.get(), nCharset, false);
-}
-
-int32_t CFX_SystemHandler::SetTimer(int32_t uElapse,
-                                    TimerCallback lpTimerFunc) {
-  return m_pFormFillEnv->SetTimer(uElapse, lpTimerFunc);
-}
-
-void CFX_SystemHandler::KillTimer(int32_t nID) {
-  m_pFormFillEnv->KillTimer(nID);
-}
diff --git a/fpdfsdk/cfx_systemhandler.h b/fpdfsdk/cfx_systemhandler.h
deleted file mode 100644
index f9138cc..0000000
--- a/fpdfsdk/cfx_systemhandler.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CFX_SYSTEMHANDLER_H_
-#define FPDFSDK_CFX_SYSTEMHANDLER_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"
-
-using TimerCallback = void (*)(int32_t idEvent);
-
-struct FX_SYSTEMTIME {
-  FX_SYSTEMTIME()
-      : wYear(0),
-        wMonth(0),
-        wDayOfWeek(0),
-        wDay(0),
-        wHour(0),
-        wMinute(0),
-        wSecond(0),
-        wMilliseconds(0) {}
-
-  uint16_t wYear;
-  uint16_t wMonth;
-  uint16_t wDayOfWeek;
-  uint16_t wDay;
-  uint16_t wHour;
-  uint16_t wMinute;
-  uint16_t wSecond;
-  uint16_t wMilliseconds;
-};
-
-// Cursor style. These must match the values in public/fpdf_formfill.h
-#define FXCT_ARROW 0
-#define FXCT_NESW 1
-#define FXCT_NWSE 2
-#define FXCT_VBEAM 3
-#define FXCT_HBEAM 4
-#define FXCT_HAND 5
-
-class CFFL_FormFiller;
-class CPDF_Document;
-class CPDF_Font;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_Widget;
-
-class CFX_SystemHandler {
- public:
-  explicit CFX_SystemHandler(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  ~CFX_SystemHandler();
-
-  void InvalidateRect(CPDFSDK_Widget* widget, const CFX_FloatRect& rect);
-  void OutputSelectedRect(CFFL_FormFiller* pFormFiller, CFX_FloatRect& rect);
-  bool IsSelectionImplemented() const;
-  void SetCursor(int32_t nCursorType);
-  bool FindNativeTrueTypeFont(ByteString sFontFaceName);
-  CPDF_Font* AddNativeTrueTypeFontToPDF(CPDF_Document* pDoc,
-                                        ByteString sFontFaceName,
-                                        uint8_t nCharset);
-
-  int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc);
-  void KillTimer(int32_t nID);
-
- private:
-  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-};
-
-#endif  // FPDFSDK_CFX_SYSTEMHANDLER_H_
diff --git a/fpdfsdk/cpdfsdk_actionhandler.cpp b/fpdfsdk/cpdfsdk_actionhandler.cpp
new file mode 100644
index 0000000..6b2b4b9
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_actionhandler.cpp
@@ -0,0 +1,426 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_actionhandler.h"
+
+#include <set>
+#include <vector>
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
+#include "fxjs/ijs_event_context.h"
+#include "fxjs/ijs_runtime.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/stl_util.h"
+
+bool CPDFSDK_ActionHandler::DoAction_DocOpen(
+    const CPDF_Action& action,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteDocumentOpenAction(action, pFormFillEnv, &visited);
+}
+
+bool CPDFSDK_ActionHandler::DoAction_JavaScript(
+    const CPDF_Action& JsAction,
+    WideString csJSName,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  if (JsAction.GetType() == CPDF_Action::JavaScript) {
+    WideString swJS = JsAction.GetJavaScript();
+    if (!swJS.IsEmpty()) {
+      RunDocumentOpenJavaScript(pFormFillEnv, csJSName, swJS);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool CPDFSDK_ActionHandler::DoAction_FieldJavaScript(
+    const CPDF_Action& JsAction,
+    CPDF_AAction::AActionType type,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    CPDF_FormField* pFormField,
+    CPDFSDK_FieldAction* data) {
+  ASSERT(pFormFillEnv);
+  if (pFormFillEnv->IsJSPlatformPresent() &&
+      JsAction.GetType() == CPDF_Action::JavaScript) {
+    WideString swJS = JsAction.GetJavaScript();
+    if (!swJS.IsEmpty()) {
+      RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS);
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CPDFSDK_ActionHandler::DoAction_Page(
+    const CPDF_Action& action,
+    enum CPDF_AAction::AActionType eType,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited);
+}
+
+bool CPDFSDK_ActionHandler::DoAction_Document(
+    const CPDF_Action& action,
+    enum CPDF_AAction::AActionType eType,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited);
+}
+
+bool CPDFSDK_ActionHandler::DoAction_Field(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType type,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    CPDF_FormField* pFormField,
+    CPDFSDK_FieldAction* data) {
+  std::set<const CPDF_Dictionary*> visited;
+  return ExecuteFieldAction(action, type, pFormFillEnv, pFormField, data,
+                            &visited);
+}
+
+bool CPDFSDK_ActionHandler::ExecuteDocumentOpenAction(
+    const CPDF_Action& action,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    std::set<const CPDF_Dictionary*>* visited) {
+  const CPDF_Dictionary* pDict = action.GetDict();
+  if (pdfium::ContainsKey(*visited, pDict))
+    return false;
+
+  visited->insert(pDict);
+
+  ASSERT(pFormFillEnv);
+  if (action.GetType() == CPDF_Action::JavaScript) {
+    if (pFormFillEnv->IsJSPlatformPresent()) {
+      WideString swJS = action.GetJavaScript();
+      if (!swJS.IsEmpty())
+        RunDocumentOpenJavaScript(pFormFillEnv, WideString(), swJS);
+    }
+  } else {
+    DoAction_NoJs(action, CPDF_AAction::AActionType::kDocumentOpen,
+                  pFormFillEnv);
+  }
+
+  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
+    CPDF_Action subaction = action.GetSubAction(i);
+    if (!ExecuteDocumentOpenAction(subaction, pFormFillEnv, visited))
+      return false;
+  }
+
+  return true;
+}
+
+bool CPDFSDK_ActionHandler::ExecuteDocumentPageAction(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType type,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    std::set<const CPDF_Dictionary*>* visited) {
+  const CPDF_Dictionary* pDict = action.GetDict();
+  if (pdfium::ContainsKey(*visited, pDict))
+    return false;
+
+  visited->insert(pDict);
+
+  ASSERT(pFormFillEnv);
+  if (action.GetType() == CPDF_Action::JavaScript) {
+    if (pFormFillEnv->IsJSPlatformPresent()) {
+      WideString swJS = action.GetJavaScript();
+      if (!swJS.IsEmpty())
+        RunDocumentPageJavaScript(pFormFillEnv, type, swJS);
+    }
+  } else {
+    DoAction_NoJs(action, type, pFormFillEnv);
+  }
+
+  ASSERT(pFormFillEnv);
+
+  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
+    CPDF_Action subaction = action.GetSubAction(i);
+    if (!ExecuteDocumentPageAction(subaction, type, pFormFillEnv, visited))
+      return false;
+  }
+
+  return true;
+}
+
+bool CPDFSDK_ActionHandler::IsValidField(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    CPDF_Dictionary* pFieldDict) {
+  ASSERT(pFieldDict);
+
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  return !!pPDFForm->GetFieldByDict(pFieldDict);
+}
+
+bool CPDFSDK_ActionHandler::ExecuteFieldAction(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType type,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    CPDF_FormField* pFormField,
+    CPDFSDK_FieldAction* data,
+    std::set<const CPDF_Dictionary*>* visited) {
+  const CPDF_Dictionary* pDict = action.GetDict();
+  if (pdfium::ContainsKey(*visited, pDict))
+    return false;
+
+  visited->insert(pDict);
+
+  ASSERT(pFormFillEnv);
+  if (action.GetType() == CPDF_Action::JavaScript) {
+    if (pFormFillEnv->IsJSPlatformPresent()) {
+      WideString swJS = action.GetJavaScript();
+      if (!swJS.IsEmpty()) {
+        RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS);
+        if (!IsValidField(pFormFillEnv, pFormField->GetFieldDict()))
+          return false;
+      }
+    }
+  } else {
+    DoAction_NoJs(action, type, pFormFillEnv);
+  }
+
+  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
+    CPDF_Action subaction = action.GetSubAction(i);
+    if (!ExecuteFieldAction(subaction, type, pFormFillEnv, pFormField, data,
+                            visited))
+      return false;
+  }
+
+  return true;
+}
+
+void CPDFSDK_ActionHandler::DoAction_NoJs(
+    const CPDF_Action& action,
+    CPDF_AAction::AActionType type,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  ASSERT(pFormFillEnv);
+
+  switch (action.GetType()) {
+    case CPDF_Action::GoTo:
+      DoAction_GoTo(pFormFillEnv, action);
+      break;
+    case CPDF_Action::URI:
+      if (CPDF_AAction::IsUserClick(type))
+        DoAction_URI(pFormFillEnv, action);
+      break;
+    case CPDF_Action::Hide:
+      DoAction_Hide(action, pFormFillEnv);
+      break;
+    case CPDF_Action::Named:
+      DoAction_Named(pFormFillEnv, action);
+      break;
+    case CPDF_Action::SubmitForm:
+      if (CPDF_AAction::IsUserClick(type))
+        DoAction_SubmitForm(action, pFormFillEnv);
+      break;
+    case CPDF_Action::ResetForm:
+      DoAction_ResetForm(action, pFormFillEnv);
+      break;
+    case CPDF_Action::JavaScript:
+      NOTREACHED();
+      break;
+    case CPDF_Action::SetOCGState:
+    case CPDF_Action::Thread:
+    case CPDF_Action::Sound:
+    case CPDF_Action::Movie:
+    case CPDF_Action::Rendition:
+    case CPDF_Action::Trans:
+    case CPDF_Action::GoTo3DView:
+    case CPDF_Action::GoToR:
+    case CPDF_Action::GoToE:
+    case CPDF_Action::Launch:
+    case CPDF_Action::ImportData:
+      // Unimplemented
+      break;
+    default:
+      break;
+  }
+}
+
+void CPDFSDK_ActionHandler::DoAction_GoTo(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    const CPDF_Action& action) {
+  ASSERT(action.GetDict());
+
+  CPDF_Document* pPDFDocument = pFormFillEnv->GetPDFDocument();
+  ASSERT(pPDFDocument);
+
+  CPDF_Dest MyDest = action.GetDest(pPDFDocument);
+  int nPageIndex = MyDest.GetDestPageIndex(pPDFDocument);
+  int nFitType = MyDest.GetZoomMode();
+  const CPDF_Array* pMyArray = MyDest.GetArray();
+  std::vector<float> posArray;
+  if (pMyArray) {
+    for (size_t i = 2; i < pMyArray->size(); i++)
+      posArray.push_back(pMyArray->GetNumberAt(i));
+  }
+  pFormFillEnv->DoGoToAction(nPageIndex, nFitType, posArray.data(),
+                             posArray.size());
+}
+
+void CPDFSDK_ActionHandler::DoAction_URI(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    const CPDF_Action& action) {
+  ASSERT(action.GetDict());
+
+  ByteString sURI = action.GetURI(pFormFillEnv->GetPDFDocument());
+  pFormFillEnv->DoURIAction(sURI.c_str());
+}
+
+void CPDFSDK_ActionHandler::DoAction_Named(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    const CPDF_Action& action) {
+  ASSERT(action.GetDict());
+
+  ByteString csName = action.GetNamedAction();
+  pFormFillEnv->ExecuteNamedAction(csName.c_str());
+}
+
+void CPDFSDK_ActionHandler::RunFieldJavaScript(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    CPDF_FormField* pFormField,
+    CPDF_AAction::AActionType type,
+    CPDFSDK_FieldAction* data,
+    const WideString& script) {
+  ASSERT(type != CPDF_AAction::kCalculate);
+  ASSERT(type != CPDF_AAction::kFormat);
+
+  RunScript(pFormFillEnv, script,
+            [type, data, pFormField](IJS_EventContext* context) {
+              switch (type) {
+                case CPDF_AAction::kCursorEnter:
+                  context->OnField_MouseEnter(data->bModifier, data->bShift,
+                                              pFormField);
+                  break;
+                case CPDF_AAction::kCursorExit:
+                  context->OnField_MouseExit(data->bModifier, data->bShift,
+                                             pFormField);
+                  break;
+                case CPDF_AAction::kButtonDown:
+                  context->OnField_MouseDown(data->bModifier, data->bShift,
+                                             pFormField);
+                  break;
+                case CPDF_AAction::kButtonUp:
+                  context->OnField_MouseUp(data->bModifier, data->bShift,
+                                           pFormField);
+                  break;
+                case CPDF_AAction::kGetFocus:
+                  context->OnField_Focus(data->bModifier, data->bShift,
+                                         pFormField, &data->sValue);
+                  break;
+                case CPDF_AAction::kLoseFocus:
+                  context->OnField_Blur(data->bModifier, data->bShift,
+                                        pFormField, &data->sValue);
+                  break;
+                case CPDF_AAction::kKeyStroke:
+                  context->OnField_Keystroke(
+                      &data->sChange, data->sChangeEx, data->bKeyDown,
+                      data->bModifier, &data->nSelEnd, &data->nSelStart,
+                      data->bShift, pFormField, &data->sValue,
+                      data->bWillCommit, data->bFieldFull, &data->bRC);
+                  break;
+                case CPDF_AAction::kValidate:
+                  context->OnField_Validate(&data->sChange, data->sChangeEx,
+                                            data->bKeyDown, data->bModifier,
+                                            data->bShift, pFormField,
+                                            &data->sValue, &data->bRC);
+                  break;
+                default:
+                  NOTREACHED();
+                  break;
+              }
+            });
+}
+
+void CPDFSDK_ActionHandler::RunDocumentOpenJavaScript(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    const WideString& sScriptName,
+    const WideString& script) {
+  RunScript(pFormFillEnv, script,
+            [pFormFillEnv, sScriptName](IJS_EventContext* context) {
+              context->OnDoc_Open(pFormFillEnv, sScriptName);
+            });
+}
+
+void CPDFSDK_ActionHandler::RunDocumentPageJavaScript(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    CPDF_AAction::AActionType type,
+    const WideString& script) {
+  RunScript(pFormFillEnv, script,
+            [type, pFormFillEnv](IJS_EventContext* context) {
+              switch (type) {
+                case CPDF_AAction::kOpenPage:
+                  context->OnPage_Open(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kClosePage:
+                  context->OnPage_Close(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kCloseDocument:
+                  context->OnDoc_WillClose(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kSaveDocument:
+                  context->OnDoc_WillSave(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kDocumentSaved:
+                  context->OnDoc_DidSave(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kPrintDocument:
+                  context->OnDoc_WillPrint(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kDocumentPrinted:
+                  context->OnDoc_DidPrint(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kPageVisible:
+                  context->OnPage_InView(pFormFillEnv);
+                  break;
+                case CPDF_AAction::kPageInvisible:
+                  context->OnPage_OutView(pFormFillEnv);
+                  break;
+                default:
+                  NOTREACHED();
+                  break;
+              }
+            });
+}
+
+bool CPDFSDK_ActionHandler::DoAction_Hide(
+    const CPDF_Action& action,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  if (pForm->DoAction_Hide(action)) {
+    pFormFillEnv->SetChangeMark();
+    return true;
+  }
+  return false;
+}
+
+bool CPDFSDK_ActionHandler::DoAction_SubmitForm(
+    const CPDF_Action& action,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  return pForm->DoAction_SubmitForm(action);
+}
+
+void CPDFSDK_ActionHandler::DoAction_ResetForm(
+    const CPDF_Action& action,
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  pForm->DoAction_ResetForm(action);
+}
+
+void CPDFSDK_ActionHandler::RunScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                                      const WideString& script,
+                                      const RunScriptCallback& cb) {
+  IJS_Runtime::ScopedEventContext pContext(pFormFillEnv->GetIJSRuntime());
+  cb(pContext.Get());
+  pContext->RunScript(script);
+  // TODO(dsinclair): Return error if RunScript returns a IJS_Runtime::JS_Error.
+}
diff --git a/fpdfsdk/cpdfsdk_actionhandler.h b/fpdfsdk/cpdfsdk_actionhandler.h
new file mode 100644
index 0000000..a8bd9cf
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_actionhandler.h
@@ -0,0 +1,104 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_ACTIONHANDLER_H_
+#define FPDFSDK_CPDFSDK_ACTIONHANDLER_H_
+
+#include <set>
+#include <utility>
+
+#include "core/fpdfdoc/cpdf_aaction.h"
+#include "core/fpdfdoc/cpdf_action.h"
+#include "core/fxcrt/fx_string.h"
+#include "fpdfsdk/cpdfsdk_fieldaction.h"
+
+class CPDFSDK_Annot;
+class CPDFSDK_FormFillEnvironment;
+class CPDF_Dictionary;
+class CPDF_FormField;
+class IJS_EventContext;
+
+class CPDFSDK_ActionHandler {
+ public:
+  bool DoAction_DocOpen(const CPDF_Action& action,
+                        CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  bool DoAction_JavaScript(const CPDF_Action& JsAction,
+                           WideString csJSName,
+                           CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  bool DoAction_Page(const CPDF_Action& action,
+                     enum CPDF_AAction::AActionType eType,
+                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  bool DoAction_Document(const CPDF_Action& action,
+                         enum CPDF_AAction::AActionType eType,
+                         CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  bool DoAction_Field(const CPDF_Action& action,
+                      CPDF_AAction::AActionType type,
+                      CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                      CPDF_FormField* pFormField,
+                      CPDFSDK_FieldAction* data);
+  bool DoAction_FieldJavaScript(const CPDF_Action& JsAction,
+                                CPDF_AAction::AActionType type,
+                                CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                                CPDF_FormField* pFormField,
+                                CPDFSDK_FieldAction* data);
+
+ private:
+  using RunScriptCallback = std::function<void(IJS_EventContext* context)>;
+
+  void RunScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                 const WideString& script,
+                 const RunScriptCallback& cb);
+
+  bool ExecuteDocumentOpenAction(const CPDF_Action& action,
+                                 CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                                 std::set<const CPDF_Dictionary*>* visited);
+  bool ExecuteDocumentPageAction(const CPDF_Action& action,
+                                 CPDF_AAction::AActionType type,
+                                 CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                                 std::set<const CPDF_Dictionary*>* visited);
+  bool ExecuteFieldAction(const CPDF_Action& action,
+                          CPDF_AAction::AActionType type,
+                          CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                          CPDF_FormField* pFormField,
+                          CPDFSDK_FieldAction* data,
+                          std::set<const CPDF_Dictionary*>* visited);
+
+  void DoAction_NoJs(const CPDF_Action& action,
+                     CPDF_AAction::AActionType type,
+                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void RunDocumentPageJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                                 CPDF_AAction::AActionType type,
+                                 const WideString& script);
+  void RunDocumentOpenJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                                 const WideString& sScriptName,
+                                 const WideString& script);
+  void RunFieldJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                          CPDF_FormField* pFormField,
+                          CPDF_AAction::AActionType type,
+                          CPDFSDK_FieldAction* data,
+                          const WideString& script);
+
+  bool IsValidField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                    CPDF_Dictionary* pFieldDict);
+
+  void DoAction_GoTo(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                     const CPDF_Action& action);
+  void DoAction_Launch(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                       const CPDF_Action& action);
+  void DoAction_URI(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                    const CPDF_Action& action);
+  void DoAction_Named(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                      const CPDF_Action& action);
+
+  bool DoAction_Hide(const CPDF_Action& action,
+                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  bool DoAction_SubmitForm(const CPDF_Action& action,
+                           CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void DoAction_ResetForm(const CPDF_Action& action,
+                          CPDFSDK_FormFillEnvironment* pFormFillEnv);
+};
+
+#endif  // FPDFSDK_CPDFSDK_ACTIONHANDLER_H_
diff --git a/fpdfsdk/cpdfsdk_annot.cpp b/fpdfsdk/cpdfsdk_annot.cpp
index e105ed9..d6712d0 100644
--- a/fpdfsdk/cpdfsdk_annot.cpp
+++ b/fpdfsdk/cpdfsdk_annot.cpp
@@ -9,46 +9,26 @@
 #include <algorithm>
 
 #include "fpdfsdk/cpdfsdk_pageview.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#endif  // PDF_ENABLE_XFA
-
-namespace {
-
-const float kMinWidth = 1.0f;
-const float kMinHeight = 1.0f;
-
-}  // namespace
 
 CPDFSDK_Annot::CPDFSDK_Annot(CPDFSDK_PageView* pPageView)
     : m_pPageView(pPageView) {}
 
 CPDFSDK_Annot::~CPDFSDK_Annot() {}
 
-#ifdef PDF_ENABLE_XFA
-
-bool CPDFSDK_Annot::IsXFAField() {
-  return false;
-}
-
-CXFA_FFWidget* CPDFSDK_Annot::GetXFAWidget() const {
+CPDFSDK_BAAnnot* CPDFSDK_Annot::AsBAAnnot() {
   return nullptr;
 }
 
-CPDFXFA_Page* CPDFSDK_Annot::GetPDFXFAPage() {
-  return m_pPageView ? m_pPageView->GetPDFXFAPage() : nullptr;
+CPDFXFA_Widget* CPDFSDK_Annot::AsXFAWidget() {
+  return nullptr;
 }
 
-#endif  // PDF_ENABLE_XFA
-
-float CPDFSDK_Annot::GetMinWidth() const {
-  return kMinWidth;
-}
-
-float CPDFSDK_Annot::GetMinHeight() const {
-  return kMinHeight;
+IPDF_Page* CPDFSDK_Annot::GetXFAPage() {
+#ifdef PDF_ENABLE_XFA
+  if (m_pPageView)
+    return m_pPageView->GetXFAPage();
+#endif
+  return nullptr;
 }
 
 int CPDFSDK_Annot::GetLayoutOrder() const {
@@ -73,12 +53,13 @@
   return CFX_FloatRect();
 }
 
-UnderlyingPageType* CPDFSDK_Annot::GetUnderlyingPage() {
+IPDF_Page* CPDFSDK_Annot::GetPage() {
 #ifdef PDF_ENABLE_XFA
-  return GetPDFXFAPage();
-#else   // PDF_ENABLE_XFA
-  return GetPDFPage();
+  IPDF_Page* pXFAPage = GetXFAPage();
+  if (pXFAPage)
+    return pXFAPage;
 #endif  // PDF_ENABLE_XFA
+  return GetPDFPage();
 }
 
 CPDF_Page* CPDFSDK_Annot::GetPDFPage() {
diff --git a/fpdfsdk/cpdfsdk_annot.h b/fpdfsdk/cpdfsdk_annot.h
index a6c4066..e722de1 100644
--- a/fpdfsdk/cpdfsdk_annot.h
+++ b/fpdfsdk/cpdfsdk_annot.h
@@ -10,30 +10,26 @@
 #include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
-#include "core/fxcrt/observable.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/cfx_systemhandler.h"
-#include "fpdfsdk/fsdk_common.h"
-#include "fpdfsdk/fsdk_define.h"
 
 class CFX_Matrix;
 class CFX_RenderDevice;
 class CPDF_Page;
 class CPDF_RenderOptions;
+class CPDFSDK_BAAnnot;
 class CPDFSDK_PageView;
+class CPDFXFA_Widget;
+class IPDF_Page;
 
-class CPDFSDK_Annot : public Observable<CPDFSDK_Annot> {
+class CPDFSDK_Annot : public Observable {
  public:
   explicit CPDFSDK_Annot(CPDFSDK_PageView* pPageView);
   virtual ~CPDFSDK_Annot();
 
-#ifdef PDF_ENABLE_XFA
-  virtual bool IsXFAField();
-  virtual CXFA_FFWidget* GetXFAWidget() const;
-#endif  // PDF_ENABLE_XFA
+  virtual CPDFSDK_BAAnnot* AsBAAnnot();
+  virtual CPDFXFA_Widget* AsXFAWidget();
 
-  virtual float GetMinWidth() const;
-  virtual float GetMinHeight() const;
   virtual int GetLayoutOrder() const;
   virtual CPDF_Annot* GetPDFAnnot() const;
   virtual CPDF_Annot::Subtype GetAnnotSubtype() const;
@@ -41,11 +37,10 @@
   virtual CFX_FloatRect GetRect() const;
   virtual void SetRect(const CFX_FloatRect& rect);
 
-  UnderlyingPageType* GetUnderlyingPage();
-  CPDF_Page* GetPDFPage();
-#ifdef PDF_ENABLE_XFA
-  CPDFXFA_Page* GetPDFXFAPage();
-#endif  // PDF_ENABLE_XFA
+  // Three cases: PDF page only, XFA page only, or XFA page backed by PDF page.
+  IPDF_Page* GetPage();     // Returns XFA Page if possible, else PDF page.
+  CPDF_Page* GetPDFPage();  // Returns PDF page or nullptr.
+  IPDF_Page* GetXFAPage();  // Returns XFA page or nullptr.
 
   CPDFSDK_PageView* GetPageView() const { return m_pPageView.Get(); }
 
@@ -53,4 +48,12 @@
   UnownedPtr<CPDFSDK_PageView> const m_pPageView;
 };
 
+inline CPDFSDK_BAAnnot* ToBAAnnot(CPDFSDK_Annot* pAnnot) {
+  return pAnnot ? pAnnot->AsBAAnnot() : nullptr;
+}
+
+inline CPDFXFA_Widget* ToXFAWidget(CPDFSDK_Annot* pAnnot) {
+  return pAnnot ? pAnnot->AsXFAWidget() : nullptr;
+}
+
 #endif  // FPDFSDK_CPDFSDK_ANNOT_H_
diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.cpp b/fpdfsdk/cpdfsdk_annothandlermgr.cpp
index 2966b4d..2a45b0d 100644
--- a/fpdfsdk/cpdfsdk_annothandlermgr.cpp
+++ b/fpdfsdk/cpdfsdk_annothandlermgr.cpp
@@ -6,69 +6,72 @@
 
 #include "fpdfsdk/cpdfsdk_annothandlermgr.h"
 
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfdoc/cpdf_annot.h"
-#include "fpdfsdk/cba_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fpdfsdk/cpdfsdk_baannothandler.h"
-#include "fpdfsdk/cpdfsdk_datetime.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/cpdfsdk_widgethandler.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "public/fpdf_fwlevent.h"
 #include "third_party/base/ptr_util.h"
 
 #ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/cpdfsdk_xfawidgethandler.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h"
 #endif  // PDF_ENABLE_XFA
 
 CPDFSDK_AnnotHandlerMgr::CPDFSDK_AnnotHandlerMgr(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pBAAnnotHandler(pdfium::MakeUnique<CPDFSDK_BAAnnotHandler>()),
-      m_pWidgetHandler(pdfium::MakeUnique<CPDFSDK_WidgetHandler>(pFormFillEnv))
-#ifdef PDF_ENABLE_XFA
-      ,
-      m_pXFAWidgetHandler(
-          pdfium::MakeUnique<CPDFSDK_XFAWidgetHandler>(pFormFillEnv))
-#endif  // PDF_ENABLE_XFA
-{
+    std::unique_ptr<CPDFSDK_BAAnnotHandler> pBAAnnotHandler,
+    std::unique_ptr<CPDFSDK_WidgetHandler> pWidgetHandler,
+    std::unique_ptr<IPDFSDK_AnnotHandler> pXFAWidgetHandler)
+    : m_pBAAnnotHandler(std::move(pBAAnnotHandler)),
+      m_pWidgetHandler(std::move(pWidgetHandler)),
+      m_pXFAWidgetHandler(std::move(pXFAWidgetHandler)) {
+  ASSERT(m_pBAAnnotHandler);
+  ASSERT(m_pWidgetHandler);
 }
 
-CPDFSDK_AnnotHandlerMgr::~CPDFSDK_AnnotHandlerMgr() {}
+CPDFSDK_AnnotHandlerMgr::~CPDFSDK_AnnotHandlerMgr() = default;
+
+void CPDFSDK_AnnotHandlerMgr::SetFormFillEnv(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  m_pBAAnnotHandler->SetFormFillEnvironment(pFormFillEnv);
+  m_pWidgetHandler->SetFormFillEnvironment(pFormFillEnv);
+  if (m_pXFAWidgetHandler)
+    m_pXFAWidgetHandler->SetFormFillEnvironment(pFormFillEnv);
+}
 
 CPDFSDK_Annot* CPDFSDK_AnnotHandlerMgr::NewAnnot(CPDF_Annot* pAnnot,
                                                  CPDFSDK_PageView* pPageView) {
   ASSERT(pPageView);
-  return GetAnnotHandler(pAnnot->GetSubtype())->NewAnnot(pAnnot, pPageView);
+  return GetAnnotHandlerOfType(pAnnot->GetSubtype())
+      ->NewAnnot(pAnnot, pPageView);
 }
 
 #ifdef PDF_ENABLE_XFA
-CPDFSDK_Annot* CPDFSDK_AnnotHandlerMgr::NewAnnot(CXFA_FFWidget* pAnnot,
-                                                 CPDFSDK_PageView* pPageView) {
+std::unique_ptr<CPDFSDK_Annot> CPDFSDK_AnnotHandlerMgr::NewXFAAnnot(
+    CXFA_FFWidget* pAnnot,
+    CPDFSDK_PageView* pPageView) {
   ASSERT(pAnnot);
   ASSERT(pPageView);
-
-  return GetAnnotHandler(CPDF_Annot::Subtype::XFAWIDGET)
-      ->NewAnnot(pAnnot, pPageView);
+  return static_cast<CPDFXFA_WidgetHandler*>(m_pXFAWidgetHandler.get())
+      ->NewAnnotForXFA(pAnnot, pPageView);
 }
 #endif  // PDF_ENABLE_XFA
 
-void CPDFSDK_AnnotHandlerMgr::ReleaseAnnot(CPDFSDK_Annot* pAnnot) {
-  IPDFSDK_AnnotHandler* pAnnotHandler = GetAnnotHandler(pAnnot);
-  pAnnotHandler->ReleaseAnnot(pAnnot);
-}
-
-void CPDFSDK_AnnotHandlerMgr::Annot_OnCreate(CPDFSDK_Annot* pAnnot) {
-  CPDF_Annot* pPDFAnnot = pAnnot->GetPDFAnnot();
-
-  CPDFSDK_DateTime curTime;
-  pPDFAnnot->GetAnnotDict()->SetNewFor<CPDF_String>(
-      "M", curTime.ToPDFDateTimeString(), false);
-  pPDFAnnot->GetAnnotDict()->SetNewFor<CPDF_Number>("F", 0);
+void CPDFSDK_AnnotHandlerMgr::ReleaseAnnot(
+    std::unique_ptr<CPDFSDK_Annot> pAnnot) {
+  IPDFSDK_AnnotHandler* pAnnotHandler = GetAnnotHandler(pAnnot.get());
+  pAnnotHandler->ReleaseAnnot(std::move(pAnnot));
 }
 
 void CPDFSDK_AnnotHandlerMgr::Annot_OnLoad(CPDFSDK_Annot* pAnnot) {
@@ -76,6 +79,10 @@
   GetAnnotHandler(pAnnot)->OnLoad(pAnnot);
 }
 
+WideString CPDFSDK_AnnotHandlerMgr::Annot_GetText(CPDFSDK_Annot* pAnnot) {
+  return GetAnnotHandler(pAnnot)->GetText(pAnnot);
+}
+
 WideString CPDFSDK_AnnotHandlerMgr::Annot_GetSelectedText(
     CPDFSDK_Annot* pAnnot) {
   return GetAnnotHandler(pAnnot)->GetSelectedText(pAnnot);
@@ -86,12 +93,28 @@
   GetAnnotHandler(pAnnot)->ReplaceSelection(pAnnot, text);
 }
 
-IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandler(
-    CPDFSDK_Annot* pAnnot) const {
-  return GetAnnotHandler(pAnnot->GetAnnotSubtype());
+bool CPDFSDK_AnnotHandlerMgr::Annot_CanUndo(CPDFSDK_Annot* pAnnot) {
+  return GetAnnotHandler(pAnnot)->CanUndo(pAnnot);
+}
+
+bool CPDFSDK_AnnotHandlerMgr::Annot_CanRedo(CPDFSDK_Annot* pAnnot) {
+  return GetAnnotHandler(pAnnot)->CanRedo(pAnnot);
+}
+
+bool CPDFSDK_AnnotHandlerMgr::Annot_Undo(CPDFSDK_Annot* pAnnot) {
+  return GetAnnotHandler(pAnnot)->Undo(pAnnot);
+}
+
+bool CPDFSDK_AnnotHandlerMgr::Annot_Redo(CPDFSDK_Annot* pAnnot) {
+  return GetAnnotHandler(pAnnot)->Redo(pAnnot);
 }
 
 IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandler(
+    CPDFSDK_Annot* pAnnot) const {
+  return GetAnnotHandlerOfType(pAnnot->GetAnnotSubtype());
+}
+
+IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandlerOfType(
     CPDF_Annot::Subtype nAnnotSubtype) const {
   if (nAnnotSubtype == CPDF_Annot::Subtype::WIDGET)
     return m_pWidgetHandler.get();
@@ -107,97 +130,97 @@
 void CPDFSDK_AnnotHandlerMgr::Annot_OnDraw(CPDFSDK_PageView* pPageView,
                                            CPDFSDK_Annot* pAnnot,
                                            CFX_RenderDevice* pDevice,
-                                           CFX_Matrix* pUser2Device,
+                                           const CFX_Matrix& mtUser2Device,
                                            bool bDrawAnnots) {
   ASSERT(pAnnot);
-  GetAnnotHandler(pAnnot)->OnDraw(pPageView, pAnnot, pDevice, pUser2Device,
+  GetAnnotHandler(pAnnot)->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device,
                                   bDrawAnnots);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonDown(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())
       ->OnLButtonDown(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonUp(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())
       ->OnLButtonUp(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonDblClk(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())
       ->OnLButtonDblClk(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnMouseMove(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())
       ->OnMouseMove(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnMouseWheel(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     short zDelta,
     const CFX_PointF& point) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())
       ->OnMouseWheel(pPageView, pAnnot, nFlags, zDelta, point);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnRButtonDown(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())
       ->OnRButtonDown(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnRButtonUp(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())
       ->OnRButtonUp(pPageView, pAnnot, nFlags, point);
 }
 
 void CPDFSDK_AnnotHandlerMgr::Annot_OnMouseEnter(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlag) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   GetAnnotHandler(pAnnot->Get())->OnMouseEnter(pPageView, pAnnot, nFlag);
 }
 
 void CPDFSDK_AnnotHandlerMgr::Annot_OnMouseExit(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlag) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   GetAnnotHandler(pAnnot->Get())->OnMouseExit(pPageView, pAnnot, nFlag);
 }
 
@@ -210,59 +233,67 @@
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnKeyDown(CPDFSDK_Annot* pAnnot,
                                               int nKeyCode,
                                               int nFlag) {
-  if (CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag) ||
-      CPDFSDK_FormFillEnvironment::IsALTKeyDown(nFlag)) {
+  if (CPWL_Wnd::IsCTRLKeyDown(nFlag) || CPWL_Wnd::IsALTKeyDown(nFlag)) {
     return GetAnnotHandler(pAnnot)->OnKeyDown(pAnnot, nKeyCode, nFlag);
   }
-
+  ObservedPtr<CPDFSDK_Annot> pObservedAnnot(pAnnot);
   CPDFSDK_PageView* pPage = pAnnot->GetPageView();
   CPDFSDK_Annot* pFocusAnnot = pPage->GetFocusAnnot();
   if (pFocusAnnot && (nKeyCode == FWL_VKEY_Tab)) {
-    CPDFSDK_Annot::ObservedPtr pNext(GetNextAnnot(
-        pFocusAnnot, !CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag)));
+    ObservedPtr<CPDFSDK_Annot> pNext(
+        GetNextAnnot(pFocusAnnot, !CPWL_Wnd::IsSHIFTKeyDown(nFlag)));
     if (pNext && pNext.Get() != pFocusAnnot) {
       pPage->GetFormFillEnv()->SetFocusAnnot(&pNext);
       return true;
     }
   }
 
+  // Check |pAnnot| again because JS may have destroyed it in |GetNextAnnot|
+  if (!pObservedAnnot)
+    return false;
+
   return GetAnnotHandler(pAnnot)->OnKeyDown(pAnnot, nKeyCode, nFlag);
 }
 
-bool CPDFSDK_AnnotHandlerMgr::Annot_OnKeyUp(CPDFSDK_Annot* pAnnot,
-                                            int nKeyCode,
-                                            int nFlag) {
-  return false;
-}
-
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnSetFocus(
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlag) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())->OnSetFocus(pAnnot, nFlag);
 }
 
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnKillFocus(
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlag) {
-  ASSERT(*pAnnot);
+  ASSERT(pAnnot->HasObservable());
   return GetAnnotHandler(pAnnot->Get())->OnKillFocus(pAnnot, nFlag);
 }
 
+bool CPDFSDK_AnnotHandlerMgr::Annot_SetIndexSelected(
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+    int index,
+    bool selected) {
+  return GetAnnotHandler(pAnnot->Get())
+      ->SetIndexSelected(pAnnot, index, selected);
+}
+
+bool CPDFSDK_AnnotHandlerMgr::Annot_IsIndexSelected(
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+    int index) {
+  return GetAnnotHandler(pAnnot->Get())->IsIndexSelected(pAnnot, index);
+}
+
 #ifdef PDF_ENABLE_XFA
 bool CPDFSDK_AnnotHandlerMgr::Annot_OnChangeFocus(
-    CPDFSDK_Annot::ObservedPtr* pSetAnnot,
-    CPDFSDK_Annot::ObservedPtr* pKillAnnot) {
-  bool bXFA = (*pSetAnnot && (*pSetAnnot)->GetXFAWidget()) ||
-              (*pKillAnnot && (*pKillAnnot)->GetXFAWidget());
+    ObservedPtr<CPDFSDK_Annot>* pSetAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pKillAnnot) {
+  CPDFXFA_Widget* pSetXFAWidget = ToXFAWidget(pSetAnnot->Get());
+  CPDFXFA_Widget* pKillXFAWidget = ToXFAWidget(pKillAnnot->Get());
+  bool bXFA = (pSetXFAWidget && pSetXFAWidget->GetXFAFFWidget()) ||
+              (pKillXFAWidget && pKillXFAWidget->GetXFAFFWidget());
 
-  if (bXFA) {
-    if (IPDFSDK_AnnotHandler* pXFAAnnotHandler =
-            GetAnnotHandler(CPDF_Annot::Subtype::XFAWIDGET))
-      return pXFAAnnotHandler->OnXFAChangedFocus(pKillAnnot, pSetAnnot);
-  }
-
-  return true;
+  return !bXFA || static_cast<CPDFXFA_WidgetHandler*>(m_pXFAWidgetHandler.get())
+                      ->OnXFAChangedFocus(pKillAnnot, pSetAnnot);
 }
 #endif  // PDF_ENABLE_XFA
 
@@ -287,35 +318,15 @@
 CPDFSDK_Annot* CPDFSDK_AnnotHandlerMgr::GetNextAnnot(CPDFSDK_Annot* pSDKAnnot,
                                                      bool bNext) {
 #ifdef PDF_ENABLE_XFA
-  CPDFSDK_PageView* pPageView = pSDKAnnot->GetPageView();
-  CPDFXFA_Page* pPage = pPageView->GetPDFXFAPage();
-  if (!pPage)
-    return nullptr;
-  if (pPage->GetPDFPage()) {  // for pdf annots.
-    CBA_AnnotIterator ai(pSDKAnnot->GetPageView(),
-                         pSDKAnnot->GetAnnotSubtype());
-    CPDFSDK_Annot* pNext =
-        bNext ? ai.GetNextAnnot(pSDKAnnot) : ai.GetPrevAnnot(pSDKAnnot);
-    return pNext;
+  IPDF_Page* pPage = pSDKAnnot->GetPageView()->GetXFAPage();
+  if (pPage && !pPage->AsPDFPage()) {
+    // For xfa annots in XFA pages not backed by PDF pages.
+    return static_cast<CPDFXFA_Page*>(pPage)->GetNextXFAAnnot(pSDKAnnot, bNext);
   }
-  // for xfa annots
-  std::unique_ptr<IXFA_WidgetIterator> pWidgetIterator(
-      pPage->GetXFAPageView()->CreateWidgetIterator(
-          XFA_TRAVERSEWAY_Tranvalse, XFA_WidgetStatus_Visible |
-                                         XFA_WidgetStatus_Viewable |
-                                         XFA_WidgetStatus_Focused));
-  if (!pWidgetIterator)
-    return nullptr;
-  if (pWidgetIterator->GetCurrentWidget() != pSDKAnnot->GetXFAWidget())
-    pWidgetIterator->SetCurrentWidget(pSDKAnnot->GetXFAWidget());
-  CXFA_FFWidget* hNextFocus =
-      bNext ? pWidgetIterator->MoveToNext() : pWidgetIterator->MoveToPrevious();
-  if (!hNextFocus && pSDKAnnot)
-    hNextFocus = pWidgetIterator->MoveToFirst();
-
-  return pPageView->GetAnnotByXFAWidget(hNextFocus);
-#else   // PDF_ENABLE_XFA
-  CBA_AnnotIterator ai(pSDKAnnot->GetPageView(), CPDF_Annot::Subtype::WIDGET);
-  return bNext ? ai.GetNextAnnot(pSDKAnnot) : ai.GetPrevAnnot(pSDKAnnot);
 #endif  // PDF_ENABLE_XFA
+
+  // For PDF annots.
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pSDKAnnot);
+  CPDFSDK_AnnotIterator ai(pWidget->GetPageView(), pWidget->GetAnnotSubtype());
+  return bNext ? ai.GetNextAnnot(pWidget) : ai.GetPrevAnnot(pWidget);
 }
diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.h b/fpdfsdk/cpdfsdk_annothandlermgr.h
index df4e1a2..b88f613 100644
--- a/fpdfsdk/cpdfsdk_annothandlermgr.h
+++ b/fpdfsdk/cpdfsdk_annothandlermgr.h
@@ -7,12 +7,10 @@
 #ifndef FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_
 #define FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_
 
-#include <map>
 #include <memory>
 
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 
 class CFX_Matrix;
@@ -24,79 +22,91 @@
 class IPDFSDK_AnnotHandler;
 
 #ifdef PDF_ENABLE_XFA
-class CPDFSDK_XFAWidgetHandler;
 class CXFA_FFWidget;
 #endif  // PDF_ENABLE_XFA
 
 class CPDFSDK_AnnotHandlerMgr {
  public:
-  explicit CPDFSDK_AnnotHandlerMgr(CPDFSDK_FormFillEnvironment* pApp);
+  CPDFSDK_AnnotHandlerMgr(
+      std::unique_ptr<CPDFSDK_BAAnnotHandler> pBAAnnotHandler,
+      std::unique_ptr<CPDFSDK_WidgetHandler> pWidgetHandler,
+      std::unique_ptr<IPDFSDK_AnnotHandler> pXFAWidgetHandler);
+
   ~CPDFSDK_AnnotHandlerMgr();
 
+  void SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+
   CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPageView);
 #ifdef PDF_ENABLE_XFA
-  CPDFSDK_Annot* NewAnnot(CXFA_FFWidget* pAnnot, CPDFSDK_PageView* pPageView);
+  std::unique_ptr<CPDFSDK_Annot> NewXFAAnnot(CXFA_FFWidget* pAnnot,
+                                             CPDFSDK_PageView* pPageView);
 #endif  // PDF_ENABLE_XFA
-  void ReleaseAnnot(CPDFSDK_Annot* pAnnot);
+  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot);
 
-  void Annot_OnCreate(CPDFSDK_Annot* pAnnot);
   void Annot_OnLoad(CPDFSDK_Annot* pAnnot);
 
+  WideString Annot_GetText(CPDFSDK_Annot* pAnnot);
   WideString Annot_GetSelectedText(CPDFSDK_Annot* pAnnot);
   void Annot_ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text);
 
-  IPDFSDK_AnnotHandler* GetAnnotHandler(CPDFSDK_Annot* pAnnot) const;
+  bool Annot_CanUndo(CPDFSDK_Annot* pAnnot);
+  bool Annot_CanRedo(CPDFSDK_Annot* pAnnot);
+  bool Annot_Undo(CPDFSDK_Annot* pAnnot);
+  bool Annot_Redo(CPDFSDK_Annot* pAnnot);
+
   void Annot_OnDraw(CPDFSDK_PageView* pPageView,
                     CPDFSDK_Annot* pAnnot,
                     CFX_RenderDevice* pDevice,
-                    CFX_Matrix* pUser2Device,
+                    const CFX_Matrix& mtUser2Device,
                     bool bDrawAnnots);
 
   void Annot_OnMouseEnter(CPDFSDK_PageView* pPageView,
-                          CPDFSDK_Annot::ObservedPtr* pAnnot,
+                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
                           uint32_t nFlags);
   void Annot_OnMouseExit(CPDFSDK_PageView* pPageView,
-                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                          uint32_t nFlags);
   bool Annot_OnLButtonDown(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                            uint32_t nFlags,
                            const CFX_PointF& point);
   bool Annot_OnLButtonUp(CPDFSDK_PageView* pPageView,
-                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                          uint32_t nFlags,
                          const CFX_PointF& point);
   bool Annot_OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                              uint32_t nFlags,
                              const CFX_PointF& point);
   bool Annot_OnMouseMove(CPDFSDK_PageView* pPageView,
-                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                          uint32_t nFlags,
                          const CFX_PointF& point);
   bool Annot_OnMouseWheel(CPDFSDK_PageView* pPageView,
-                          CPDFSDK_Annot::ObservedPtr* pAnnot,
+                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
                           uint32_t nFlags,
                           short zDelta,
                           const CFX_PointF& point);
   bool Annot_OnRButtonDown(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                            uint32_t nFlags,
                            const CFX_PointF& point);
   bool Annot_OnRButtonUp(CPDFSDK_PageView* pPageView,
-                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                          uint32_t nFlags,
                          const CFX_PointF& point);
   bool Annot_OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags);
   bool Annot_OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag);
-  bool Annot_OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag);
-
-  bool Annot_OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag);
-  bool Annot_OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag);
+  bool Annot_OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
+  bool Annot_OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
+  bool Annot_SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                              int index,
+                              bool selected);
+  bool Annot_IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index);
 
 #ifdef PDF_ENABLE_XFA
-  bool Annot_OnChangeFocus(CPDFSDK_Annot::ObservedPtr* pSetAnnot,
-                           CPDFSDK_Annot::ObservedPtr* pKillAnnot);
+  bool Annot_OnChangeFocus(ObservedPtr<CPDFSDK_Annot>* pSetAnnot,
+                           ObservedPtr<CPDFSDK_Annot>* pKillAnnot);
 #endif  // PDF_ENABLE_XFA
 
   CFX_FloatRect Annot_OnGetViewBBox(CPDFSDK_PageView* pPageView,
@@ -106,14 +116,18 @@
                        const CFX_PointF& point);
 
  private:
-  IPDFSDK_AnnotHandler* GetAnnotHandler(CPDF_Annot::Subtype nSubtype) const;
+  friend class CPDFSDK_BAAnnotHandlerTest;
+
+  IPDFSDK_AnnotHandler* GetAnnotHandler(CPDFSDK_Annot* pAnnot) const;
+  IPDFSDK_AnnotHandler* GetAnnotHandlerOfType(
+      CPDF_Annot::Subtype nAnnotSubtype) const;
   CPDFSDK_Annot* GetNextAnnot(CPDFSDK_Annot* pSDKAnnot, bool bNext);
 
-  std::unique_ptr<CPDFSDK_BAAnnotHandler> m_pBAAnnotHandler;
-  std::unique_ptr<CPDFSDK_WidgetHandler> m_pWidgetHandler;
-#ifdef PDF_ENABLE_XFA
-  std::unique_ptr<CPDFSDK_XFAWidgetHandler> m_pXFAWidgetHandler;
-#endif  // PDF_ENABLE_XFA
+  // |m_pBAAnnotHandler| and |m_pWidgetHandler| are always present, but
+  // |m_pXFAWidgetHandler| is only present in XFA mode.
+  std::unique_ptr<CPDFSDK_BAAnnotHandler> const m_pBAAnnotHandler;
+  std::unique_ptr<CPDFSDK_WidgetHandler> const m_pWidgetHandler;
+  std::unique_ptr<IPDFSDK_AnnotHandler> const m_pXFAWidgetHandler;
 };
 
 #endif  // FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_
diff --git a/fpdfsdk/cpdfsdk_annotiteration.h b/fpdfsdk/cpdfsdk_annotiteration.h
index 70edfd1..d3a0f39 100644
--- a/fpdfsdk/cpdfsdk_annotiteration.h
+++ b/fpdfsdk/cpdfsdk_annotiteration.h
@@ -16,7 +16,7 @@
 class CPDFSDK_AnnotIteration {
  public:
   using const_iterator =
-      std::vector<CPDFSDK_Annot::ObservedPtr>::const_iterator;
+      std::vector<ObservedPtr<CPDFSDK_Annot>>::const_iterator;
 
   CPDFSDK_AnnotIteration(CPDFSDK_PageView* pPageView, bool bReverse);
   ~CPDFSDK_AnnotIteration();
@@ -25,7 +25,7 @@
   const_iterator end() const { return m_List.end(); }
 
  private:
-  std::vector<CPDFSDK_Annot::ObservedPtr> m_List;
+  std::vector<ObservedPtr<CPDFSDK_Annot>> m_List;
 };
 
 #endif  // FPDFSDK_CPDFSDK_ANNOTITERATION_H_
diff --git a/fpdfsdk/cpdfsdk_annotiterator.cpp b/fpdfsdk/cpdfsdk_annotiterator.cpp
new file mode 100644
index 0000000..f1919ab
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_annotiterator.cpp
@@ -0,0 +1,180 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
+
+#include <algorithm>
+
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+
+namespace {
+
+CFX_FloatRect GetAnnotRect(const CPDFSDK_Annot* pAnnot) {
+  return pAnnot->GetPDFAnnot()->GetRect();
+}
+
+bool CompareByLeftAscending(const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) {
+  return GetAnnotRect(p1).left < GetAnnotRect(p2).left;
+}
+
+bool CompareByTopDescending(const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) {
+  return GetAnnotRect(p1).top > GetAnnotRect(p2).top;
+}
+
+CPDFSDK_AnnotIterator::TabOrder GetTabOrder(CPDFSDK_PageView* pPageView) {
+  CPDF_Page* pPDFPage = pPageView->GetPDFPage();
+  ByteString sTabs = pPDFPage->GetDict()->GetStringFor("Tabs");
+  if (sTabs == "R")
+    return CPDFSDK_AnnotIterator::ROW;
+  if (sTabs == "C")
+    return CPDFSDK_AnnotIterator::COLUMN;
+  return CPDFSDK_AnnotIterator::STRUCTURE;
+}
+
+}  // namespace
+
+CPDFSDK_AnnotIterator::CPDFSDK_AnnotIterator(CPDFSDK_PageView* pPageView,
+                                             CPDF_Annot::Subtype nAnnotSubtype)
+    : m_pPageView(pPageView),
+      m_nAnnotSubtype(nAnnotSubtype),
+      m_eTabOrder(GetTabOrder(pPageView)) {
+  GenerateResults();
+}
+
+CPDFSDK_AnnotIterator::~CPDFSDK_AnnotIterator() = default;
+
+CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetFirstAnnot() {
+  return m_Annots.empty() ? nullptr : m_Annots.front();
+}
+
+CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetLastAnnot() {
+  return m_Annots.empty() ? nullptr : m_Annots.back();
+}
+
+CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetNextAnnot(CPDFSDK_Annot* pAnnot) {
+  auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot);
+  if (iter == m_Annots.end())
+    return nullptr;
+  ++iter;
+  if (iter == m_Annots.end())
+    iter = m_Annots.begin();
+  return *iter;
+}
+
+CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetPrevAnnot(CPDFSDK_Annot* pAnnot) {
+  auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot);
+  if (iter == m_Annots.end())
+    return nullptr;
+  if (iter == m_Annots.begin())
+    iter = m_Annots.end();
+  return *(--iter);
+}
+
+void CPDFSDK_AnnotIterator::CollectAnnots(std::vector<CPDFSDK_Annot*>* pArray) {
+  for (auto* pAnnot : m_pPageView->GetAnnotList()) {
+    if (pAnnot->GetAnnotSubtype() == m_nAnnotSubtype &&
+        !pAnnot->IsSignatureWidget()) {
+      pArray->push_back(pAnnot);
+    }
+  }
+}
+
+CFX_FloatRect CPDFSDK_AnnotIterator::AddToAnnotsList(
+    std::vector<CPDFSDK_Annot*>* sa,
+    size_t idx) {
+  CPDFSDK_Annot* pLeftTopAnnot = sa->at(idx);
+  CFX_FloatRect rcLeftTop = GetAnnotRect(pLeftTopAnnot);
+  m_Annots.push_back(pLeftTopAnnot);
+  sa->erase(sa->begin() + idx);
+  return rcLeftTop;
+}
+
+void CPDFSDK_AnnotIterator::AddSelectedToAnnots(std::vector<CPDFSDK_Annot*>* sa,
+                                                std::vector<size_t>* aSelect) {
+  for (size_t i = 0; i < aSelect->size(); ++i)
+    m_Annots.push_back(sa->at(aSelect->at(i)));
+
+  for (int i = aSelect->size() - 1; i >= 0; --i)
+    sa->erase(sa->begin() + aSelect->at(i));
+}
+
+void CPDFSDK_AnnotIterator::GenerateResults() {
+  switch (m_eTabOrder) {
+    case STRUCTURE:
+      CollectAnnots(&m_Annots);
+      break;
+
+    case ROW: {
+      std::vector<CPDFSDK_Annot*> sa;
+      CollectAnnots(&sa);
+      std::sort(sa.begin(), sa.end(), CompareByLeftAscending);
+
+      while (!sa.empty()) {
+        int nLeftTopIndex = -1;
+        float fTop = 0.0f;
+        for (int i = sa.size() - 1; i >= 0; i--) {
+          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
+          if (rcAnnot.top > fTop) {
+            nLeftTopIndex = i;
+            fTop = rcAnnot.top;
+          }
+        }
+        if (nLeftTopIndex < 0)
+          continue;
+
+        CFX_FloatRect rcLeftTop = AddToAnnotsList(&sa, nLeftTopIndex);
+
+        std::vector<size_t> aSelect;
+        for (size_t i = 0; i < sa.size(); ++i) {
+          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
+          float fCenterY = (rcAnnot.top + rcAnnot.bottom) / 2.0f;
+          if (fCenterY > rcLeftTop.bottom && fCenterY < rcLeftTop.top)
+            aSelect.push_back(i);
+        }
+        AddSelectedToAnnots(&sa, &aSelect);
+      }
+      break;
+    }
+
+    case COLUMN: {
+      std::vector<CPDFSDK_Annot*> sa;
+      CollectAnnots(&sa);
+      std::sort(sa.begin(), sa.end(), CompareByTopDescending);
+
+      while (!sa.empty()) {
+        int nLeftTopIndex = -1;
+        float fLeft = -1.0f;
+        for (int i = sa.size() - 1; i >= 0; --i) {
+          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
+          if (fLeft < 0) {
+            nLeftTopIndex = 0;
+            fLeft = rcAnnot.left;
+          } else if (rcAnnot.left < fLeft) {
+            nLeftTopIndex = i;
+            fLeft = rcAnnot.left;
+          }
+        }
+        if (nLeftTopIndex < 0)
+          continue;
+
+        CFX_FloatRect rcLeftTop = AddToAnnotsList(&sa, nLeftTopIndex);
+
+        std::vector<size_t> aSelect;
+        for (size_t i = 0; i < sa.size(); ++i) {
+          CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
+          float fCenterX = (rcAnnot.left + rcAnnot.right) / 2.0f;
+          if (fCenterX > rcLeftTop.left && fCenterX < rcLeftTop.right)
+            aSelect.push_back(i);
+        }
+        AddSelectedToAnnots(&sa, &aSelect);
+      }
+      break;
+    }
+  }
+}
diff --git a/fpdfsdk/cpdfsdk_annotiterator.h b/fpdfsdk/cpdfsdk_annotiterator.h
new file mode 100644
index 0000000..fd7cbdc
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_annotiterator.h
@@ -0,0 +1,46 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_ANNOTITERATOR_H_
+#define FPDFSDK_CPDFSDK_ANNOTITERATOR_H_
+
+#include <vector>
+
+#include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CPDFSDK_Annot;
+class CPDFSDK_PageView;
+
+class CPDFSDK_AnnotIterator {
+ public:
+  enum TabOrder : uint8_t { STRUCTURE = 0, ROW, COLUMN };
+
+  CPDFSDK_AnnotIterator(CPDFSDK_PageView* pPageView,
+                        CPDF_Annot::Subtype nAnnotSubtype);
+  ~CPDFSDK_AnnotIterator();
+
+  CPDFSDK_Annot* GetFirstAnnot();
+  CPDFSDK_Annot* GetLastAnnot();
+  CPDFSDK_Annot* GetNextAnnot(CPDFSDK_Annot* pAnnot);
+  CPDFSDK_Annot* GetPrevAnnot(CPDFSDK_Annot* pAnnot);
+
+ private:
+  void GenerateResults();
+  void CollectAnnots(std::vector<CPDFSDK_Annot*>* pArray);
+  CFX_FloatRect AddToAnnotsList(std::vector<CPDFSDK_Annot*>* sa, size_t idx);
+  void AddSelectedToAnnots(std::vector<CPDFSDK_Annot*>* sa,
+                           std::vector<size_t>* aSelect);
+
+  UnownedPtr<CPDFSDK_PageView> const m_pPageView;
+  CPDF_Annot::Subtype m_nAnnotSubtype;
+  const TabOrder m_eTabOrder;
+  std::vector<CPDFSDK_Annot*> m_Annots;
+};
+
+#endif  // FPDFSDK_CPDFSDK_ANNOTITERATOR_H_
diff --git a/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp b/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp
new file mode 100644
index 0000000..161ae95
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp
@@ -0,0 +1,126 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "testing/embedder_test.h"
+#include "testing/embedder_test_mock_delegate.h"
+#include "testing/embedder_test_timer_handling_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void CheckRect(const CFX_FloatRect& actual, const CFX_FloatRect& expected) {
+  EXPECT_EQ(expected.left, actual.left);
+  EXPECT_EQ(expected.bottom, actual.bottom);
+  EXPECT_EQ(expected.right, actual.right);
+  EXPECT_EQ(expected.top, actual.top);
+}
+
+}  // namespace
+
+class CPDFSDK_AnnotIteratorTest : public EmbedderTest {};
+
+TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) {
+  EXPECT_TRUE(OpenDocument("annotiter.pdf"));
+  FPDF_PAGE page0 = LoadPage(0);
+  FPDF_PAGE page1 = LoadPage(1);
+  FPDF_PAGE page2 = LoadPage(2);
+  ASSERT_TRUE(page0);
+  ASSERT_TRUE(page1);
+  ASSERT_TRUE(page2);
+
+  CFX_FloatRect LeftBottom(200, 200, 220, 220);
+  CFX_FloatRect RightBottom(400, 201, 420, 221);
+  CFX_FloatRect LeftTop(201, 400, 221, 420);
+  CFX_FloatRect RightTop(401, 401, 421, 421);
+
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
+
+  {
+    // Page 0 specifies "row order".
+    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(0),
+                               CPDF_Annot::Subtype::WIDGET);
+    CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
+    CheckRect(pAnnot->GetRect(), RightTop);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftTop);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightBottom);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftBottom);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
+
+    pAnnot = iter.GetLastAnnot();
+    CheckRect(pAnnot->GetRect(), LeftBottom);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightBottom);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftTop);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightTop);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
+  }
+  {
+    // Page 1 specifies "column order"
+    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(1),
+                               CPDF_Annot::Subtype::WIDGET);
+    CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
+    CheckRect(pAnnot->GetRect(), RightTop);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightBottom);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftTop);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftBottom);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
+
+    pAnnot = iter.GetLastAnnot();
+    CheckRect(pAnnot->GetRect(), LeftBottom);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftTop);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightBottom);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightTop);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
+  }
+  {
+    // Page 2 specifies "struct order"
+    CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(2),
+                               CPDF_Annot::Subtype::WIDGET);
+    CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
+    CheckRect(pAnnot->GetRect(), LeftBottom);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightTop);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftTop);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightBottom);
+    pAnnot = iter.GetNextAnnot(pAnnot);
+    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
+
+    pAnnot = iter.GetLastAnnot();
+    CheckRect(pAnnot->GetRect(), RightBottom);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftTop);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), RightTop);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    CheckRect(pAnnot->GetRect(), LeftBottom);
+    pAnnot = iter.GetPrevAnnot(pAnnot);
+    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
+  }
+  UnloadPage(page2);
+  UnloadPage(page1);
+  UnloadPage(page0);
+}
diff --git a/fpdfsdk/cpdfsdk_appstream.cpp b/fpdfsdk/cpdfsdk_appstream.cpp
new file mode 100644
index 0000000..b6600d2
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_appstream.cpp
@@ -0,0 +1,1988 @@
+// 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 "fpdfsdk/cpdfsdk_appstream.h"
+
+#include <utility>
+
+#include "constants/form_flags.h"
+#include "core/fpdfapi/font/cpdf_font.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/fpdfdoc/cba_fontmap.h"
+#include "core/fpdfdoc/cpdf_formcontrol.h"
+#include "core/fpdfdoc/cpdf_icon.h"
+#include "core/fpdfdoc/cpvt_word.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/pwl/cpwl_edit.h"
+#include "fpdfsdk/pwl/cpwl_edit_impl.h"
+#include "fpdfsdk/pwl/cpwl_icon.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+// Checkbox & radiobutton styles.
+enum class CheckStyle { kCheck = 0, kCircle, kCross, kDiamond, kSquare, kStar };
+
+// Pushbutton layout styles.
+enum class ButtonStyle {
+  kLabel = 0,
+  kIcon,
+  kIconTopLabelBottom,
+  kIconBottomLabelTop,
+  kIconLeftLabelRight,
+  kIconRightLabelLeft,
+  kLabelOverIcon
+};
+
+const char kAppendRectOperator[] = "re";
+const char kConcatMatrixOperator[] = "cm";
+const char kCurveToOperator[] = "c";
+const char kEndPathNoFillOrStrokeOperator[] = "n";
+const char kFillOperator[] = "f";
+const char kFillEvenOddOperator[] = "f*";
+const char kInvokeNamedXObjectOperator[] = "Do";
+const char kLineToOperator[] = "l";
+const char kMarkedSequenceBeginOperator[] = "BMC";
+const char kMarkedSequenceEndOperator[] = "EMC";
+const char kMoveTextPositionOperator[] = "Td";
+const char kMoveToOperator[] = "m";
+const char kSetCharacterSpacingOperator[] = "Tc";
+const char kSetCMYKOperator[] = "k";
+const char kSetCMKYStrokedOperator[] = "K";
+const char kSetDashOperator[] = "d";
+const char kSetGrayOperator[] = "g";
+const char kSetGrayStrokedOperator[] = "G";
+const char kSetLineCapStyleOperator[] = "J";
+const char kSetLineJoinStyleOperator[] = "j";
+const char kSetLineWidthOperator[] = "w";
+const char kSetNonZeroWindingClipOperator[] = "W";
+const char kSetRGBOperator[] = "rg";
+const char kSetRGBStrokedOperator[] = "RG";
+const char kSetTextFontAndSizeOperator[] = "Tf";
+const char kShowTextOperator[] = "Tj";
+const char kStateRestoreOperator[] = "Q";
+const char kStateSaveOperator[] = "q";
+const char kStrokeOperator[] = "S";
+const char kTextBeginOperator[] = "BT";
+const char kTextEndOperator[] = "ET";
+
+class AutoClosedCommand {
+ public:
+  AutoClosedCommand(std::ostringstream* stream,
+                    ByteString open,
+                    ByteString close)
+      : stream_(stream), close_(close) {
+    *stream_ << open << "\n";
+  }
+
+  virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; }
+
+ private:
+  std::ostringstream* stream_;
+  ByteString close_;
+};
+
+class AutoClosedQCommand final : public AutoClosedCommand {
+ public:
+  explicit AutoClosedQCommand(std::ostringstream* stream)
+      : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {}
+  ~AutoClosedQCommand() override {}
+};
+
+ByteString GetColorAppStream(const CFX_Color& color,
+                             const bool& bFillOrStroke) {
+  std::ostringstream sColorStream;
+
+  switch (color.nColorType) {
+    case CFX_Color::kRGB:
+      sColorStream << color.fColor1 << " " << color.fColor2 << " "
+                   << color.fColor3 << " "
+                   << (bFillOrStroke ? kSetRGBOperator : kSetRGBStrokedOperator)
+                   << "\n";
+      break;
+    case CFX_Color::kGray:
+      sColorStream << color.fColor1 << " "
+                   << (bFillOrStroke ? kSetGrayOperator
+                                     : kSetGrayStrokedOperator)
+                   << "\n";
+      break;
+    case CFX_Color::kCMYK:
+      sColorStream << color.fColor1 << " " << color.fColor2 << " "
+                   << color.fColor3 << " " << color.fColor4 << " "
+                   << (bFillOrStroke ? kSetCMYKOperator
+                                     : kSetCMKYStrokedOperator)
+                   << "\n";
+      break;
+  }
+
+  return ByteString(sColorStream);
+}
+
+ByteString GetAP_Check(const CFX_FloatRect& crBBox) {
+  const float fWidth = crBBox.Width();
+  const float fHeight = crBBox.Height();
+
+  CFX_PointF pts[8][3] = {{CFX_PointF(0.28f, 0.52f), CFX_PointF(0.27f, 0.48f),
+                           CFX_PointF(0.29f, 0.40f)},
+                          {CFX_PointF(0.30f, 0.33f), CFX_PointF(0.31f, 0.29f),
+                           CFX_PointF(0.31f, 0.28f)},
+                          {CFX_PointF(0.39f, 0.28f), CFX_PointF(0.49f, 0.29f),
+                           CFX_PointF(0.77f, 0.67f)},
+                          {CFX_PointF(0.76f, 0.68f), CFX_PointF(0.78f, 0.69f),
+                           CFX_PointF(0.76f, 0.75f)},
+                          {CFX_PointF(0.76f, 0.75f), CFX_PointF(0.73f, 0.80f),
+                           CFX_PointF(0.68f, 0.75f)},
+                          {CFX_PointF(0.68f, 0.74f), CFX_PointF(0.68f, 0.74f),
+                           CFX_PointF(0.44f, 0.47f)},
+                          {CFX_PointF(0.43f, 0.47f), CFX_PointF(0.40f, 0.47f),
+                           CFX_PointF(0.41f, 0.58f)},
+                          {CFX_PointF(0.40f, 0.60f), CFX_PointF(0.28f, 0.66f),
+                           CFX_PointF(0.30f, 0.56f)}};
+
+  for (size_t i = 0; i < FX_ArraySize(pts); ++i) {
+    for (size_t j = 0; j < FX_ArraySize(pts[0]); ++j) {
+      pts[i][j].x = pts[i][j].x * fWidth + crBBox.left;
+      pts[i][j].y *= pts[i][j].y * fHeight + crBBox.bottom;
+    }
+  }
+
+  std::ostringstream csAP;
+  csAP << pts[0][0].x << " " << pts[0][0].y << " " << kMoveToOperator << "\n";
+
+  for (size_t i = 0; i < FX_ArraySize(pts); ++i) {
+    size_t nNext = i < FX_ArraySize(pts) - 1 ? i + 1 : 0;
+
+    float px1 = pts[i][1].x - pts[i][0].x;
+    float py1 = pts[i][1].y - pts[i][0].y;
+    float px2 = pts[i][2].x - pts[nNext][0].x;
+    float py2 = pts[i][2].y - pts[nNext][0].y;
+
+    csAP << pts[i][0].x + px1 * FX_BEZIER << " "
+         << pts[i][0].y + py1 * FX_BEZIER << " "
+         << pts[nNext][0].x + px2 * FX_BEZIER << " "
+         << pts[nNext][0].y + py2 * FX_BEZIER << " " << pts[nNext][0].x << " "
+         << pts[nNext][0].y << " " << kCurveToOperator << "\n";
+  }
+
+  return ByteString(csAP);
+}
+
+ByteString GetAP_Circle(const CFX_FloatRect& crBBox) {
+  std::ostringstream csAP;
+
+  float fWidth = crBBox.Width();
+  float fHeight = crBBox.Height();
+
+  CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2);
+  CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top);
+  CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2);
+  CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom);
+
+  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
+
+  float px = pt2.x - pt1.x;
+  float py = pt2.y - pt1.y;
+
+  csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " "
+       << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y
+       << " " << kCurveToOperator << "\n";
+
+  px = pt3.x - pt2.x;
+  py = pt2.y - pt3.y;
+
+  csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " "
+       << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " "
+       << kCurveToOperator << "\n";
+
+  px = pt3.x - pt4.x;
+  py = pt3.y - pt4.y;
+
+  csAP << pt3.x << " " << pt3.y - py * FX_BEZIER << " "
+       << pt4.x + px * FX_BEZIER << " " << pt4.y << " " << pt4.x << " " << pt4.y
+       << " " << kCurveToOperator << "\n";
+
+  px = pt4.x - pt1.x;
+  py = pt1.y - pt4.y;
+
+  csAP << pt4.x - px * FX_BEZIER << " " << pt4.y << " " << pt1.x << " "
+       << pt1.y - py * FX_BEZIER << " " << pt1.x << " " << pt1.y << " "
+       << kCurveToOperator << "\n";
+
+  return ByteString(csAP);
+}
+
+ByteString GetAP_Cross(const CFX_FloatRect& crBBox) {
+  std::ostringstream csAP;
+
+  csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n";
+  csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator
+       << "\n";
+  csAP << crBBox.left << " " << crBBox.bottom << " " << kMoveToOperator << "\n";
+  csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n";
+
+  return ByteString(csAP);
+}
+
+ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) {
+  std::ostringstream csAP;
+
+  float fWidth = crBBox.Width();
+  float fHeight = crBBox.Height();
+
+  CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2);
+  CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top);
+  CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2);
+  CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom);
+
+  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
+  csAP << pt2.x << " " << pt2.y << " " << kLineToOperator << "\n";
+  csAP << pt3.x << " " << pt3.y << " " << kLineToOperator << "\n";
+  csAP << pt4.x << " " << pt4.y << " " << kLineToOperator << "\n";
+  csAP << pt1.x << " " << pt1.y << " " << kLineToOperator << "\n";
+
+  return ByteString(csAP);
+}
+
+ByteString GetAP_Square(const CFX_FloatRect& crBBox) {
+  std::ostringstream csAP;
+
+  csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n";
+  csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n";
+  csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator
+       << "\n";
+  csAP << crBBox.left << " " << crBBox.bottom << " " << kLineToOperator << "\n";
+  csAP << crBBox.left << " " << crBBox.top << " " << kLineToOperator << "\n";
+
+  return ByteString(csAP);
+}
+
+ByteString GetAP_Star(const CFX_FloatRect& crBBox) {
+  std::ostringstream csAP;
+
+  float fRadius = (crBBox.top - crBBox.bottom) / (1 + (float)cos(FX_PI / 5.0f));
+  CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f,
+                                   (crBBox.top + crBBox.bottom) / 2.0f);
+
+  float px[5];
+  float py[5];
+  float fAngel = FX_PI / 10.0f;
+  for (int32_t i = 0; i < 5; i++) {
+    px[i] = ptCenter.x + fRadius * (float)cos(fAngel);
+    py[i] = ptCenter.y + fRadius * (float)sin(fAngel);
+    fAngel += FX_PI * 2 / 5.0f;
+  }
+
+  csAP << px[0] << " " << py[0] << " " << kMoveToOperator << "\n";
+
+  int32_t nNext = 0;
+  for (int32_t j = 0; j < 5; j++) {
+    nNext += 2;
+    if (nNext >= 5)
+      nNext -= 5;
+    csAP << px[nNext] << " " << py[nNext] << " " << kLineToOperator << "\n";
+  }
+
+  return ByteString(csAP);
+}
+
+ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) {
+  std::ostringstream csAP;
+
+  float fWidth = crBBox.Width();
+  float fHeight = crBBox.Height();
+
+  CFX_PointF pt1(-fWidth / 2, 0);
+  CFX_PointF pt2(0, fHeight / 2);
+  CFX_PointF pt3(fWidth / 2, 0);
+
+  float px;
+  float py;
+
+  csAP << cos(fRotate) << " " << sin(fRotate) << " " << -sin(fRotate) << " "
+       << cos(fRotate) << " " << crBBox.left + fWidth / 2 << " "
+       << crBBox.bottom + fHeight / 2 << " " << kConcatMatrixOperator << "\n";
+
+  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
+
+  px = pt2.x - pt1.x;
+  py = pt2.y - pt1.y;
+
+  csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " "
+       << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y
+       << " " << kCurveToOperator << "\n";
+
+  px = pt3.x - pt2.x;
+  py = pt2.y - pt3.y;
+
+  csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " "
+       << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " "
+       << kCurveToOperator << "\n";
+
+  return ByteString(csAP);
+}
+
+ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox,
+                              const CFX_Color& crText) {
+  std::ostringstream sAP;
+  {
+    AutoClosedQCommand q(&sAP);
+    sAP << GetColorAppStream(crText, true) << GetAP_Check(rcBBox)
+        << kFillOperator << "\n";
+  }
+  return ByteString(sAP);
+}
+
+ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox,
+                               const CFX_Color& crText) {
+  std::ostringstream sAP;
+  {
+    AutoClosedQCommand q(&sAP);
+    sAP << GetColorAppStream(crText, true) << GetAP_Circle(rcBBox)
+        << kFillOperator << "\n";
+  }
+  return ByteString(sAP);
+}
+
+ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox,
+                              const CFX_Color& crText) {
+  std::ostringstream sAP;
+  {
+    AutoClosedQCommand q(&sAP);
+    sAP << GetColorAppStream(crText, false) << GetAP_Cross(rcBBox)
+        << kStrokeOperator << "\n";
+  }
+  return ByteString(sAP);
+}
+
+ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox,
+                                const CFX_Color& crText) {
+  std::ostringstream sAP;
+  {
+    AutoClosedQCommand q(&sAP);
+    sAP << "1 " << kSetLineWidthOperator << "\n"
+        << GetColorAppStream(crText, true) << GetAP_Diamond(rcBBox)
+        << kFillOperator << "\n";
+  }
+  return ByteString(sAP);
+}
+
+ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox,
+                               const CFX_Color& crText) {
+  std::ostringstream sAP;
+  {
+    AutoClosedQCommand q(&sAP);
+    sAP << GetColorAppStream(crText, true) << GetAP_Square(rcBBox)
+        << kFillOperator << "\n";
+  }
+  return ByteString(sAP);
+}
+
+ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox,
+                             const CFX_Color& crText) {
+  std::ostringstream sAP;
+  {
+    AutoClosedQCommand q(&sAP);
+    sAP << GetColorAppStream(crText, true) << GetAP_Star(rcBBox)
+        << kFillOperator << "\n";
+  }
+  return ByteString(sAP);
+}
+
+ByteString GetCircleFillAppStream(const CFX_FloatRect& rect,
+                                  const CFX_Color& color) {
+  std::ostringstream sAppStream;
+  ByteString sColor = GetColorAppStream(color, true);
+  if (sColor.GetLength() > 0) {
+    AutoClosedQCommand q(&sAppStream);
+    sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n";
+  }
+  return ByteString(sAppStream);
+}
+
+ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect,
+                                    float fWidth,
+                                    const CFX_Color& color,
+                                    const CFX_Color& crLeftTop,
+                                    const CFX_Color& crRightBottom,
+                                    BorderStyle nStyle,
+                                    const CPWL_Dash& dash) {
+  std::ostringstream sAppStream;
+  ByteString sColor;
+
+  if (fWidth > 0.0f) {
+    AutoClosedQCommand q(&sAppStream);
+
+    float fHalfWidth = fWidth / 2.0f;
+    CFX_FloatRect rect_by_2 = rect.GetDeflated(fHalfWidth, fHalfWidth);
+
+    float div = fHalfWidth * 0.75f;
+    CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div);
+    switch (nStyle) {
+      default:
+      case BorderStyle::SOLID:
+      case BorderStyle::UNDERLINE: {
+        sColor = GetColorAppStream(color, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
+                     << sColor << GetAP_Circle(rect_by_2) << " "
+                     << kStrokeOperator << "\n";
+        }
+      } break;
+      case BorderStyle::DASH: {
+        sColor = GetColorAppStream(color, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
+                     << "[" << dash.nDash << " " << dash.nGap << "] "
+                     << dash.nPhase << " " << kSetDashOperator << "\n"
+                     << sColor << GetAP_Circle(rect_by_2) << " "
+                     << kStrokeOperator << "\n";
+        }
+      } break;
+      case BorderStyle::BEVELED: {
+        sColor = GetColorAppStream(color, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
+                     << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
+                     << "\n";
+        }
+
+        sColor = GetColorAppStream(crLeftTop, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
+                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f)
+                     << " " << kStrokeOperator << "\n";
+        }
+
+        sColor = GetColorAppStream(crRightBottom, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
+                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f)
+                     << " " << kStrokeOperator << "\n";
+        }
+      } break;
+      case BorderStyle::INSET: {
+        sColor = GetColorAppStream(color, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
+                     << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
+                     << "\n";
+        }
+
+        sColor = GetColorAppStream(crLeftTop, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
+                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f)
+                     << " " << kStrokeOperator << "\n";
+        }
+
+        sColor = GetColorAppStream(crRightBottom, false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q2(&sAppStream);
+          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
+                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f)
+                     << " " << kStrokeOperator << "\n";
+        }
+      } break;
+    }
+  }
+  return ByteString(sAppStream);
+}
+
+ByteString GetCheckBoxAppStream(const CFX_FloatRect& rcBBox,
+                                CheckStyle nStyle,
+                                const CFX_Color& crText) {
+  CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
+  switch (nStyle) {
+    default:
+    case CheckStyle::kCheck:
+      return GetAppStream_Check(rcCenter, crText);
+    case CheckStyle::kCircle:
+      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
+      return GetAppStream_Circle(rcCenter, crText);
+    case CheckStyle::kCross:
+      return GetAppStream_Cross(rcCenter, crText);
+    case CheckStyle::kDiamond:
+      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
+      return GetAppStream_Diamond(rcCenter, crText);
+    case CheckStyle::kSquare:
+      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
+      return GetAppStream_Square(rcCenter, crText);
+    case CheckStyle::kStar:
+      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
+      return GetAppStream_Star(rcCenter, crText);
+  }
+}
+
+ByteString GetRadioButtonAppStream(const CFX_FloatRect& rcBBox,
+                                   CheckStyle nStyle,
+                                   const CFX_Color& crText) {
+  CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
+  switch (nStyle) {
+    default:
+    case CheckStyle::kCheck:
+      return GetAppStream_Check(rcCenter, crText);
+    case CheckStyle::kCircle:
+      rcCenter.ScaleFromCenterPoint(1.0f / 2.0f);
+      return GetAppStream_Circle(rcCenter, crText);
+    case CheckStyle::kCross:
+      return GetAppStream_Cross(rcCenter, crText);
+    case CheckStyle::kDiamond:
+      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
+      return GetAppStream_Diamond(rcCenter, crText);
+    case CheckStyle::kSquare:
+      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
+      return GetAppStream_Square(rcCenter, crText);
+    case CheckStyle::kStar:
+      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
+      return GetAppStream_Star(rcCenter, crText);
+  }
+}
+
+ByteString GetFontSetString(IPVT_FontMap* pFontMap,
+                            int32_t nFontIndex,
+                            float fFontSize) {
+  if (!pFontMap)
+    return ByteString();
+
+  ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
+  if (sFontAlias.GetLength() <= 0 || fFontSize <= 0)
+    return ByteString();
+
+  std::ostringstream sRet;
+  sRet << "/" << sFontAlias << " " << fFontSize << " "
+       << kSetTextFontAndSizeOperator << "\n";
+  return ByteString(sRet);
+}
+
+ByteString GetWordRenderString(const ByteString& strWords) {
+  if (strWords.GetLength() > 0) {
+    return PDF_EncodeString(strWords, false) + " " + kShowTextOperator + "\n";
+  }
+  return ByteString();
+}
+
+ByteString GetEditAppStream(CPWL_EditImpl* pEdit,
+                            const CFX_PointF& ptOffset,
+                            bool bContinuous,
+                            uint16_t SubWord) {
+  CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator();
+  pIterator->SetAt(0);
+
+  std::ostringstream sEditStream;
+  std::ostringstream sWords;
+  int32_t nCurFontIndex = -1;
+  CFX_PointF ptOld;
+  CFX_PointF ptNew;
+  CPVT_WordPlace oldplace;
+
+  while (pIterator->NextWord()) {
+    CPVT_WordPlace place = pIterator->GetAt();
+    if (bContinuous) {
+      if (place.LineCmp(oldplace) != 0) {
+        if (sWords.tellp() > 0) {
+          sEditStream << GetWordRenderString(ByteString(sWords));
+          sWords.str("");
+        }
+
+        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.x != ptOld.x || ptNew.y != ptOld.y) {
+          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " "
+                      << kMoveTextPositionOperator << "\n";
+
+          ptOld = ptNew;
+        }
+      }
+
+      CPVT_Word word;
+      if (pIterator->GetWord(word)) {
+        if (word.nFontIndex != nCurFontIndex) {
+          if (sWords.tellp() > 0) {
+            sEditStream << GetWordRenderString(ByteString(sWords));
+            sWords.str("");
+          }
+          sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
+                                          word.fFontSize);
+          nCurFontIndex = word.nFontIndex;
+        }
+
+        sWords << pEdit->GetPDFWordString(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.x != ptOld.x || ptNew.y != ptOld.y) {
+          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " "
+                      << kMoveTextPositionOperator << "\n";
+          ptOld = ptNew;
+        }
+
+        if (word.nFontIndex != nCurFontIndex) {
+          sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
+                                          word.fFontSize);
+          nCurFontIndex = word.nFontIndex;
+        }
+
+        sEditStream << GetWordRenderString(
+            pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord));
+      }
+    }
+  }
+
+  if (sWords.tellp() > 0) {
+    sEditStream << GetWordRenderString(ByteString(sWords));
+    sWords.str("");
+  }
+
+  std::ostringstream sAppStream;
+  if (sEditStream.tellp() > 0) {
+    float fCharSpace = pEdit->GetCharSpace();
+    if (!IsFloatZero(fCharSpace))
+      sAppStream << fCharSpace << " " << kSetCharacterSpacingOperator << "\n";
+
+    sAppStream << sEditStream.str();
+  }
+
+  return ByteString(sAppStream);
+}
+
+ByteString GenerateIconAppStream(CPDF_IconFit& fit,
+                                 CPDF_Stream* pIconStream,
+                                 const CFX_FloatRect& rcIcon) {
+  if (rcIcon.IsEmpty() || !pIconStream)
+    return ByteString();
+
+  CPWL_Wnd::CreateParams cp;
+  cp.dwFlags = PWS_VISIBLE;
+
+  CPWL_Icon icon(cp, pdfium::MakeUnique<CPDF_Icon>(pIconStream), &fit);
+  icon.Realize();
+  if (!icon.Move(rcIcon, false, false))
+    return ByteString();
+
+  ByteString sAlias = icon.GetImageAlias();
+  if (sAlias.GetLength() <= 0)
+    return ByteString();
+
+  CFX_FloatRect rcPlate = icon.GetClientRect();
+  CFX_Matrix mt = icon.GetImageMatrix().GetInverse();
+
+  float fHScale;
+  float fVScale;
+  std::tie(fHScale, fVScale) = icon.GetScale();
+
+  float fx;
+  float fy;
+  std::tie(fx, fy) = icon.GetImageOffset();
+
+  std::ostringstream str;
+  {
+    AutoClosedQCommand q(&str);
+    str << rcPlate.left << " " << rcPlate.bottom << " "
+        << rcPlate.right - rcPlate.left << " " << rcPlate.top - rcPlate.bottom
+        << " " << kAppendRectOperator << " " << kSetNonZeroWindingClipOperator
+        << " " << kEndPathNoFillOrStrokeOperator << "\n";
+
+    str << fHScale << " 0 0 " << fVScale << " " << rcPlate.left + fx << " "
+        << rcPlate.bottom + fy << " " << kConcatMatrixOperator << "\n";
+    str << mt.a << " " << mt.b << " " << mt.c << " " << mt.d << " " << mt.e
+        << " " << mt.f << " " << kConcatMatrixOperator << "\n";
+
+    str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 "
+        << kSetLineWidthOperator << " /" << sAlias << " "
+        << kInvokeNamedXObjectOperator << "\n";
+  }
+  icon.Destroy();
+
+  return ByteString(str);
+}
+
+ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox,
+                                  IPVT_FontMap* pFontMap,
+                                  CPDF_Stream* pIconStream,
+                                  CPDF_IconFit& IconFit,
+                                  const WideString& sLabel,
+                                  const CFX_Color& crText,
+                                  float fFontSize,
+                                  ButtonStyle nLayOut) {
+  const float fAutoFontScale = 1.0f / 3.0f;
+
+  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  pEdit->SetFontMap(pFontMap);
+  pEdit->SetAlignmentH(1, true);
+  pEdit->SetAlignmentV(1, true);
+  pEdit->SetMultiLine(false, true);
+  pEdit->SetAutoReturn(false, true);
+  if (IsFloatZero(fFontSize))
+    pEdit->SetAutoFontSize(true, true);
+  else
+    pEdit->SetFontSize(fFontSize);
+
+  pEdit->Initialize();
+  pEdit->SetText(sLabel);
+
+  CFX_FloatRect rcLabelContent = pEdit->GetContentRect();
+  CFX_FloatRect rcLabel;
+  CFX_FloatRect rcIcon;
+  float fWidth = 0.0f;
+  float fHeight = 0.0f;
+
+  switch (nLayOut) {
+    case ButtonStyle::kLabel:
+      rcLabel = rcBBox;
+      break;
+    case ButtonStyle::kIcon:
+      rcIcon = rcBBox;
+      break;
+    case ButtonStyle::kIconTopLabelBottom:
+      if (pIconStream) {
+        if (IsFloatZero(fFontSize)) {
+          fHeight = rcBBox.Height();
+          rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
+                                  rcBBox.bottom + fHeight * fAutoFontScale);
+          rcIcon =
+              CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, rcBBox.top);
+        } else {
+          fHeight = rcLabelContent.Height();
+
+          if (rcBBox.bottom + fHeight > rcBBox.top) {
+            rcLabel = rcBBox;
+          } else {
+            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
+                                    rcBBox.bottom + fHeight);
+            rcIcon = CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right,
+                                   rcBBox.top);
+          }
+        }
+      } else {
+        rcLabel = rcBBox;
+      }
+      break;
+    case ButtonStyle::kIconBottomLabelTop:
+      if (pIconStream) {
+        if (IsFloatZero(fFontSize)) {
+          fHeight = rcBBox.Height();
+          rcLabel =
+              CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale,
+                            rcBBox.right, rcBBox.top);
+          rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
+                                 rcLabel.bottom);
+        } else {
+          fHeight = rcLabelContent.Height();
+
+          if (rcBBox.bottom + fHeight > rcBBox.top) {
+            rcLabel = rcBBox;
+          } else {
+            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight,
+                                    rcBBox.right, rcBBox.top);
+            rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
+                                   rcLabel.bottom);
+          }
+        }
+      } else {
+        rcLabel = rcBBox;
+      }
+      break;
+    case ButtonStyle::kIconLeftLabelRight:
+      if (pIconStream) {
+        if (IsFloatZero(fFontSize)) {
+          fWidth = rcBBox.right - rcBBox.left;
+          if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
+            rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale,
+                                    rcBBox.bottom, rcBBox.right, rcBBox.top);
+            rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
+                                   rcBBox.top);
+          } else {
+            if (rcLabelContent.Width() < fWidth) {
+              rcLabel = CFX_FloatRect(rcBBox.right - rcLabelContent.Width(),
+                                      rcBBox.bottom, rcBBox.right, rcBBox.top);
+              rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
+                                     rcBBox.top);
+            } else {
+              rcLabel = rcBBox;
+            }
+          }
+        } else {
+          fWidth = rcLabelContent.Width();
+          if (rcBBox.left + fWidth > rcBBox.right) {
+            rcLabel = rcBBox;
+          } else {
+            rcLabel = CFX_FloatRect(rcBBox.right - fWidth, rcBBox.bottom,
+                                    rcBBox.right, rcBBox.top);
+            rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
+                                   rcBBox.top);
+          }
+        }
+      } else {
+        rcLabel = rcBBox;
+      }
+      break;
+    case ButtonStyle::kIconRightLabelLeft:
+      if (pIconStream) {
+        if (IsFloatZero(fFontSize)) {
+          fWidth = rcBBox.right - rcBBox.left;
+          if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
+            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
+                                    rcBBox.left + fWidth * fAutoFontScale,
+                                    rcBBox.top);
+            rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
+                                   rcBBox.top);
+          } else {
+            if (rcLabelContent.Width() < fWidth) {
+              rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
+                                      rcBBox.left + rcLabelContent.Width(),
+                                      rcBBox.top);
+              rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
+                                     rcBBox.top);
+            } else {
+              rcLabel = rcBBox;
+            }
+          }
+        } else {
+          fWidth = rcLabelContent.Width();
+          if (rcBBox.left + fWidth > rcBBox.right) {
+            rcLabel = rcBBox;
+          } else {
+            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
+                                    rcBBox.left + fWidth, rcBBox.top);
+            rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
+                                   rcBBox.top);
+          }
+        }
+      } else {
+        rcLabel = rcBBox;
+      }
+      break;
+    case ButtonStyle::kLabelOverIcon:
+      rcLabel = rcBBox;
+      rcIcon = rcBBox;
+      break;
+  }
+
+  std::ostringstream sTemp;
+  sTemp << GenerateIconAppStream(IconFit, pIconStream, rcIcon);
+
+  if (!rcLabel.IsEmpty()) {
+    pEdit->SetPlateRect(rcLabel);
+    ByteString sEdit =
+        GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0);
+    if (sEdit.GetLength() > 0) {
+      AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator);
+      sTemp << GetColorAppStream(crText, true) << sEdit;
+    }
+  }
+
+  if (sTemp.tellp() <= 0)
+    return ByteString();
+
+  std::ostringstream sAppStream;
+  {
+    AutoClosedQCommand q(&sAppStream);
+    sAppStream << rcBBox.left << " " << rcBBox.bottom << " "
+               << rcBBox.right - rcBBox.left << " "
+               << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator
+               << " " << kSetNonZeroWindingClipOperator << " "
+               << kEndPathNoFillOrStrokeOperator << "\n";
+    sAppStream << sTemp.str().c_str();
+  }
+  return ByteString(sAppStream);
+}
+
+ByteString GetBorderAppStreamInternal(const CFX_FloatRect& rect,
+                                      float fWidth,
+                                      const CFX_Color& color,
+                                      const CFX_Color& crLeftTop,
+                                      const CFX_Color& crRightBottom,
+                                      BorderStyle nStyle,
+                                      const CPWL_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;
+    AutoClosedQCommand q(&sAppStream);
+
+    switch (nStyle) {
+      default:
+      case BorderStyle::SOLID:
+        sColor = GetColorAppStream(color, true);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
+                     << fTop - fBottom << " " << kAppendRectOperator << "\n";
+          sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " "
+                     << fRight - fLeft - fWidth * 2 << " "
+                     << fTop - fBottom - fWidth * 2 << " "
+                     << kAppendRectOperator << "\n";
+          sAppStream << kFillEvenOddOperator << "\n";
+        }
+        break;
+      case BorderStyle::DASH:
+        sColor = GetColorAppStream(color, false);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fWidth << " " << kSetLineWidthOperator << " ["
+                     << dash.nDash << " " << dash.nGap << "] " << dash.nPhase
+                     << " " << kSetDashOperator << "\n";
+          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " "
+                     << kMoveToOperator << "\n";
+          sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2 << " "
+                     << kLineToOperator << "\n";
+          sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2 << " "
+                     << kLineToOperator << "\n";
+          sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2
+                     << " " << kLineToOperator << "\n";
+          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " "
+                     << kLineToOperator << " " << kStrokeOperator << "\n";
+        }
+        break;
+      case BorderStyle::BEVELED:
+      case BorderStyle::INSET:
+        sColor = GetColorAppStream(crLeftTop, true);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
+                     << kMoveToOperator << "\n";
+          sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth << " "
+                     << kLineToOperator << "\n";
+          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " "
+                     << kLineToOperator << "\n";
+          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+                     << " " << kLineToOperator << "\n";
+          sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+                     << " " << kLineToOperator << "\n";
+          sAppStream << fLeft + fHalfWidth * 2 << " "
+                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
+                     << " " << kFillOperator << "\n";
+        }
+
+        sColor = GetColorAppStream(crRightBottom, true);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " "
+                     << kMoveToOperator << "\n";
+          sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth
+                     << " " << kLineToOperator << "\n";
+          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
+                     << kLineToOperator << "\n";
+          sAppStream << fLeft + fHalfWidth * 2 << " "
+                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
+                     << "\n";
+          sAppStream << fRight - fHalfWidth * 2 << " "
+                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
+                     << "\n";
+          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+                     << " " << kLineToOperator << " " << kFillOperator << "\n";
+        }
+
+        sColor = GetColorAppStream(color, true);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
+                     << fTop - fBottom << " " << kAppendRectOperator << "\n";
+          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
+                     << fRight - fLeft - fHalfWidth * 2 << " "
+                     << fTop - fBottom - fHalfWidth * 2 << " "
+                     << kAppendRectOperator << " " << kFillEvenOddOperator
+                     << "\n";
+        }
+        break;
+      case BorderStyle::UNDERLINE:
+        sColor = GetColorAppStream(color, false);
+        if (sColor.GetLength() > 0) {
+          sAppStream << sColor;
+          sAppStream << fWidth << " " << kSetLineWidthOperator << "\n";
+          sAppStream << fLeft << " " << fBottom + fWidth / 2 << " "
+                     << kMoveToOperator << "\n";
+          sAppStream << fRight << " " << fBottom + fWidth / 2 << " "
+                     << kLineToOperator << " " << kStrokeOperator << "\n";
+        }
+        break;
+    }
+  }
+
+  return ByteString(sAppStream);
+}
+
+ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) {
+  if (rcBBox.IsEmpty())
+    return ByteString();
+
+  std::ostringstream sAppStream;
+  {
+    AutoClosedQCommand q(&sAppStream);
+    sAppStream << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f,
+                                              220.0f / 255.0f, 220.0f / 255.0f),
+                                    true)
+               << rcBBox.left << " " << rcBBox.bottom << " "
+               << rcBBox.right - rcBBox.left << " "
+               << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator
+               << " " << kFillOperator << "\n";
+  }
+
+  {
+    AutoClosedQCommand q(&sAppStream);
+    sAppStream << GetBorderAppStreamInternal(
+        rcBBox, 2, CFX_Color(CFX_Color::kGray, 0),
+        CFX_Color(CFX_Color::kGray, 1), CFX_Color(CFX_Color::kGray, 0.5),
+        BorderStyle::BEVELED, CPWL_Dash(3, 0, 0));
+  }
+
+  CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2,
+                                   (rcBBox.top + rcBBox.bottom) / 2);
+  if (IsFloatBigger(rcBBox.right - rcBBox.left, 6) &&
+      IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) {
+    AutoClosedQCommand q(&sAppStream);
+    sAppStream << " 0 " << kSetGrayOperator << "\n"
+               << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " "
+               << kMoveToOperator << "\n"
+               << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " "
+               << kLineToOperator << "\n"
+               << ptCenter.x << " " << ptCenter.y - 1.5f << " "
+               << kLineToOperator << "\n"
+               << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " "
+               << kLineToOperator << " " << kFillOperator << "\n";
+  }
+
+  return ByteString(sAppStream);
+}
+
+ByteString GetRectFillAppStream(const CFX_FloatRect& rect,
+                                const CFX_Color& color) {
+  std::ostringstream sAppStream;
+  ByteString sColor = GetColorAppStream(color, true);
+  if (sColor.GetLength() > 0) {
+    AutoClosedQCommand q(&sAppStream);
+    sAppStream << sColor << rect.left << " " << rect.bottom << " "
+               << rect.right - rect.left << " " << rect.top - rect.bottom << " "
+               << kAppendRectOperator << " " << kFillOperator << "\n";
+  }
+
+  return ByteString(sAppStream);
+}
+
+void SetDefaultIconName(CPDF_Stream* pIcon, const char* name) {
+  if (!pIcon)
+    return;
+
+  CPDF_Dictionary* pImageDict = pIcon->GetDict();
+  if (!pImageDict)
+    return;
+
+  if (pImageDict->KeyExist("Name"))
+    return;
+
+  pImageDict->SetNewFor<CPDF_String>("Name", name, false);
+}
+
+}  // namespace
+
+CPDFSDK_AppStream::CPDFSDK_AppStream(CPDFSDK_Widget* widget,
+                                     CPDF_Dictionary* dict)
+    : widget_(widget), dict_(dict) {}
+
+CPDFSDK_AppStream::~CPDFSDK_AppStream() {}
+
+void CPDFSDK_AppStream::SetAsPushButton() {
+  CPDF_FormControl* pControl = widget_->GetFormControl();
+  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
+  ButtonStyle nLayout = ButtonStyle::kLabel;
+  switch (pControl->GetTextPosition()) {
+    case TEXTPOS_ICON:
+      nLayout = ButtonStyle::kIcon;
+      break;
+    case TEXTPOS_BELOW:
+      nLayout = ButtonStyle::kIconTopLabelBottom;
+      break;
+    case TEXTPOS_ABOVE:
+      nLayout = ButtonStyle::kIconBottomLabelTop;
+      break;
+    case TEXTPOS_RIGHT:
+      nLayout = ButtonStyle::kIconLeftLabelRight;
+      break;
+    case TEXTPOS_LEFT:
+      nLayout = ButtonStyle::kIconRightLabelLeft;
+      break;
+    case TEXTPOS_OVERLAID:
+      nLayout = ButtonStyle::kLabelOverIcon;
+      break;
+    default:
+      nLayout = ButtonStyle::kLabel;
+      break;
+  }
+
+  CFX_Color crBackground;
+  CFX_Color crBorder;
+  int iColorType;
+  float fc[4];
+  pControl->GetOriginalBackgroundColor(iColorType, fc);
+  if (iColorType > 0)
+    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+
+  pControl->GetOriginalBorderColor(iColorType, fc);
+  if (iColorType > 0)
+    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+
+  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
+  CPWL_Dash dsBorder(3, 0, 0);
+  CFX_Color crLeftTop;
+  CFX_Color crRightBottom;
+
+  BorderStyle nBorderStyle = widget_->GetBorderStyle();
+  switch (nBorderStyle) {
+    case BorderStyle::DASH:
+      dsBorder = CPWL_Dash(3, 3, 0);
+      break;
+    case BorderStyle::BEVELED:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crRightBottom = crBackground / 2.0f;
+      break;
+    case BorderStyle::INSET:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      break;
+    default:
+      break;
+  }
+
+  CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
+  CFX_Color crText(CFX_Color::kGray, 0);
+  ByteString csNameTag;
+  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
+  Optional<CFX_Color::Type> color = da.GetColor(fc);
+  if (color) {
+    iColorType = *color;
+    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+  }
+
+  float fFontSize;
+  Optional<ByteString> font = da.GetFont(&fFontSize);
+  if (font)
+    csNameTag = *font;
+  else
+    fFontSize = 12.0f;
+
+  WideString csWCaption;
+  WideString csNormalCaption;
+  WideString csRolloverCaption;
+  WideString csDownCaption;
+  if (pControl->HasMKEntry("CA"))
+    csNormalCaption = pControl->GetNormalCaption();
+
+  if (pControl->HasMKEntry("RC"))
+    csRolloverCaption = pControl->GetRolloverCaption();
+
+  if (pControl->HasMKEntry("AC"))
+    csDownCaption = pControl->GetDownCaption();
+
+  CPDF_Stream* pNormalIcon = nullptr;
+  CPDF_Stream* pRolloverIcon = nullptr;
+  CPDF_Stream* pDownIcon = nullptr;
+  if (pControl->HasMKEntry("I"))
+    pNormalIcon = pControl->GetNormalIcon();
+
+  if (pControl->HasMKEntry("RI"))
+    pRolloverIcon = pControl->GetRolloverIcon();
+
+  if (pControl->HasMKEntry("IX"))
+    pDownIcon = pControl->GetDownIcon();
+
+  SetDefaultIconName(pNormalIcon, "ImgA");
+  SetDefaultIconName(pRolloverIcon, "ImgB");
+  SetDefaultIconName(pDownIcon, "ImgC");
+
+  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                       widget_->GetPDFAnnot()->GetAnnotDict());
+  font_map.SetAPType("N");
+
+  CPDF_IconFit iconFit = pControl->GetIconFit();
+  ByteString csAP =
+      GetRectFillAppStream(rcWindow, crBackground) +
+      GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                 crRightBottom, nBorderStyle, dsBorder) +
+      GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
+                             &font_map, pNormalIcon, iconFit, csNormalCaption,
+                             crText, fFontSize, nLayout);
+
+  Write("N", csAP, ByteString());
+  if (pNormalIcon)
+    AddImage("N", pNormalIcon);
+
+  CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode();
+  if (eHLM == CPDF_FormControl::Push || eHLM == CPDF_FormControl::Toggle) {
+    if (csRolloverCaption.IsEmpty() && !pRolloverIcon) {
+      csRolloverCaption = csNormalCaption;
+      pRolloverIcon = pNormalIcon;
+    }
+
+    font_map.SetAPType("R");
+
+    csAP =
+        GetRectFillAppStream(rcWindow, crBackground) +
+        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                   crRightBottom, nBorderStyle, dsBorder) +
+        GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
+                               &font_map, pRolloverIcon, iconFit,
+                               csRolloverCaption, crText, fFontSize, nLayout);
+
+    Write("R", csAP, ByteString());
+    if (pRolloverIcon)
+      AddImage("R", pRolloverIcon);
+
+    if (csDownCaption.IsEmpty() && !pDownIcon) {
+      csDownCaption = csNormalCaption;
+      pDownIcon = pNormalIcon;
+    }
+
+    switch (nBorderStyle) {
+      case BorderStyle::BEVELED: {
+        CFX_Color crTemp = crLeftTop;
+        crLeftTop = crRightBottom;
+        crRightBottom = crTemp;
+        break;
+      }
+      case BorderStyle::INSET: {
+        crLeftTop = CFX_Color(CFX_Color::kGray, 0);
+        crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+        break;
+      }
+      default:
+        break;
+    }
+
+    font_map.SetAPType("D");
+
+    csAP =
+        GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
+        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                   crRightBottom, nBorderStyle, dsBorder) +
+        GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
+                               &font_map, pDownIcon, iconFit, csDownCaption,
+                               crText, fFontSize, nLayout);
+
+    Write("D", csAP, ByteString());
+    if (pDownIcon)
+      AddImage("D", pDownIcon);
+  } else {
+    Remove("D");
+    Remove("R");
+  }
+}
+
+void CPDFSDK_AppStream::SetAsCheckBox() {
+  CPDF_FormControl* pControl = widget_->GetFormControl();
+  CFX_Color crBackground, crBorder, crText;
+  int iColorType;
+  float fc[4];
+
+  pControl->GetOriginalBackgroundColor(iColorType, fc);
+  if (iColorType > 0)
+    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+
+  pControl->GetOriginalBorderColor(iColorType, fc);
+  if (iColorType > 0)
+    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+
+  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
+  CPWL_Dash dsBorder(3, 0, 0);
+  CFX_Color crLeftTop, crRightBottom;
+
+  BorderStyle nBorderStyle = widget_->GetBorderStyle();
+  switch (nBorderStyle) {
+    case BorderStyle::DASH:
+      dsBorder = CPWL_Dash(3, 3, 0);
+      break;
+    case BorderStyle::BEVELED:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crRightBottom = crBackground / 2.0f;
+      break;
+    case BorderStyle::INSET:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      break;
+    default:
+      break;
+  }
+
+  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
+  CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
+  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
+  Optional<CFX_Color::Type> color = da.GetColor(fc);
+  if (color) {
+    iColorType = *color;
+    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+  }
+
+  CheckStyle nStyle = CheckStyle::kCheck;
+  WideString csWCaption = pControl->GetNormalCaption();
+  if (csWCaption.GetLength() > 0) {
+    switch (csWCaption[0]) {
+      case L'l':
+        nStyle = CheckStyle::kCircle;
+        break;
+      case L'8':
+        nStyle = CheckStyle::kCross;
+        break;
+      case L'u':
+        nStyle = CheckStyle::kDiamond;
+        break;
+      case L'n':
+        nStyle = CheckStyle::kSquare;
+        break;
+      case L'H':
+        nStyle = CheckStyle::kStar;
+        break;
+      case L'4':
+      default:
+        nStyle = CheckStyle::kCheck;
+    }
+  }
+
+  ByteString csAP_N_ON =
+      GetRectFillAppStream(rcWindow, crBackground) +
+      GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                 crRightBottom, nBorderStyle, dsBorder);
+
+  ByteString csAP_N_OFF = csAP_N_ON;
+
+  switch (nBorderStyle) {
+    case BorderStyle::BEVELED: {
+      CFX_Color crTemp = crLeftTop;
+      crLeftTop = crRightBottom;
+      crRightBottom = crTemp;
+      break;
+    }
+    case BorderStyle::INSET: {
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+      break;
+    }
+    default:
+      break;
+  }
+
+  ByteString csAP_D_ON =
+      GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
+      GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                 crRightBottom, nBorderStyle, dsBorder);
+
+  ByteString csAP_D_OFF = csAP_D_ON;
+
+  csAP_N_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
+  csAP_D_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
+
+  Write("N", csAP_N_ON, pControl->GetCheckedAPState());
+  Write("N", csAP_N_OFF, "Off");
+
+  Write("D", csAP_D_ON, pControl->GetCheckedAPState());
+  Write("D", csAP_D_OFF, "Off");
+
+  ByteString csAS = widget_->GetAppState();
+  if (csAS.IsEmpty())
+    widget_->SetAppState("Off");
+}
+
+void CPDFSDK_AppStream::SetAsRadioButton() {
+  CPDF_FormControl* pControl = widget_->GetFormControl();
+  CFX_Color crBackground;
+  CFX_Color crBorder;
+  CFX_Color crText;
+  int iColorType;
+  float fc[4];
+
+  pControl->GetOriginalBackgroundColor(iColorType, fc);
+  if (iColorType > 0)
+    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+
+  pControl->GetOriginalBorderColor(iColorType, fc);
+  if (iColorType > 0)
+    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+
+  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
+  CPWL_Dash dsBorder(3, 0, 0);
+  CFX_Color crLeftTop;
+  CFX_Color crRightBottom;
+  BorderStyle nBorderStyle = widget_->GetBorderStyle();
+  switch (nBorderStyle) {
+    case BorderStyle::DASH:
+      dsBorder = CPWL_Dash(3, 3, 0);
+      break;
+    case BorderStyle::BEVELED:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crRightBottom = crBackground / 2.0f;
+      break;
+    case BorderStyle::INSET:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      break;
+    default:
+      break;
+  }
+
+  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
+  CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
+  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
+  Optional<CFX_Color::Type> color = da.GetColor(fc);
+  if (color) {
+    iColorType = *color;
+    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
+  }
+
+  CheckStyle nStyle = CheckStyle::kCircle;
+  WideString csWCaption = pControl->GetNormalCaption();
+  if (csWCaption.GetLength() > 0) {
+    switch (csWCaption[0]) {
+      case L'8':
+        nStyle = CheckStyle::kCross;
+        break;
+      case L'u':
+        nStyle = CheckStyle::kDiamond;
+        break;
+      case L'n':
+        nStyle = CheckStyle::kSquare;
+        break;
+      case L'H':
+        nStyle = CheckStyle::kStar;
+        break;
+      case L'4':
+        nStyle = CheckStyle::kCheck;
+        break;
+      case L'l':
+      default:
+        nStyle = CheckStyle::kCircle;
+    }
+  }
+
+  ByteString csAP_N_ON;
+  CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f);
+  if (nStyle == CheckStyle::kCircle) {
+    if (nBorderStyle == BorderStyle::BEVELED) {
+      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crRightBottom = crBackground - 0.25f;
+    } else if (nBorderStyle == BorderStyle::INSET) {
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5f);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75f);
+    }
+
+    csAP_N_ON =
+        GetCircleFillAppStream(rcCenter, crBackground) +
+        GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
+                                 crRightBottom, nBorderStyle, dsBorder);
+  } else {
+    csAP_N_ON =
+        GetRectFillAppStream(rcWindow, crBackground) +
+        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                   crRightBottom, nBorderStyle, dsBorder);
+  }
+
+  ByteString csAP_N_OFF = csAP_N_ON;
+
+  switch (nBorderStyle) {
+    case BorderStyle::BEVELED: {
+      CFX_Color crTemp = crLeftTop;
+      crLeftTop = crRightBottom;
+      crRightBottom = crTemp;
+      break;
+    }
+    case BorderStyle::INSET: {
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+      break;
+    }
+    default:
+      break;
+  }
+
+  ByteString csAP_D_ON;
+
+  if (nStyle == CheckStyle::kCircle) {
+    CFX_Color crBK = crBackground - 0.25f;
+    if (nBorderStyle == BorderStyle::BEVELED) {
+      crLeftTop = crBackground - 0.25f;
+      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+      crBK = crBackground;
+    } else if (nBorderStyle == BorderStyle::INSET) {
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
+    }
+
+    csAP_D_ON =
+        GetCircleFillAppStream(rcCenter, crBK) +
+        GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
+                                 crRightBottom, nBorderStyle, dsBorder);
+  } else {
+    csAP_D_ON =
+        GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
+        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                   crRightBottom, nBorderStyle, dsBorder);
+  }
+
+  ByteString csAP_D_OFF = csAP_D_ON;
+
+  csAP_N_ON += GetRadioButtonAppStream(rcClient, nStyle, crText);
+  csAP_D_ON += GetRadioButtonAppStream(rcClient, nStyle, crText);
+
+  Write("N", csAP_N_ON, pControl->GetCheckedAPState());
+  Write("N", csAP_N_OFF, "Off");
+
+  Write("D", csAP_D_ON, pControl->GetCheckedAPState());
+  Write("D", csAP_D_OFF, "Off");
+
+  ByteString csAS = widget_->GetAppState();
+  if (csAS.IsEmpty())
+    widget_->SetAppState("Off");
+}
+
+void CPDFSDK_AppStream::SetAsComboBox(Optional<WideString> sValue) {
+  CPDF_FormControl* pControl = widget_->GetFormControl();
+  CPDF_FormField* pField = pControl->GetField();
+  std::ostringstream sBody;
+
+  CFX_FloatRect rcClient = widget_->GetClientRect();
+  CFX_FloatRect rcButton = rcClient;
+  rcButton.left = rcButton.right - 13;
+  rcButton.Normalize();
+
+  // Font map must outlive |pEdit|.
+  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                       widget_->GetPDFAnnot()->GetAnnotDict());
+
+  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  pEdit->EnableRefresh(false);
+  pEdit->SetFontMap(&font_map);
+
+  CFX_FloatRect rcEdit = rcClient;
+  rcEdit.right = rcButton.left;
+  rcEdit.Normalize();
+
+  pEdit->SetPlateRect(rcEdit);
+  pEdit->SetAlignmentV(1, true);
+
+  float fFontSize = widget_->GetFontSize();
+  if (IsFloatZero(fFontSize))
+    pEdit->SetAutoFontSize(true, true);
+  else
+    pEdit->SetFontSize(fFontSize);
+
+  pEdit->Initialize();
+
+  if (sValue.has_value()) {
+    pEdit->SetText(sValue.value());
+  } else {
+    int32_t nCurSel = pField->GetSelectedIndex(0);
+    if (nCurSel < 0)
+      pEdit->SetText(pField->GetValue());
+    else
+      pEdit->SetText(pField->GetOptionLabel(nCurSel));
+  }
+
+  CFX_FloatRect rcContent = pEdit->GetContentRect();
+  ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0);
+  if (sEdit.GetLength() > 0) {
+    sBody << "/Tx ";
+    AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
+                          kMarkedSequenceEndOperator);
+    AutoClosedQCommand q(&sBody);
+
+    if (rcContent.Width() > rcEdit.Width() ||
+        rcContent.Height() > rcEdit.Height()) {
+      sBody << rcEdit.left << " " << rcEdit.bottom << " " << rcEdit.Width()
+            << " " << rcEdit.Height() << " " << kAppendRectOperator << "\n"
+            << kSetNonZeroWindingClipOperator << "\n"
+            << kEndPathNoFillOrStrokeOperator << "\n";
+    }
+
+    CFX_Color crText = widget_->GetTextPWLColor();
+    AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
+    sBody << GetColorAppStream(crText, true) << sEdit;
+  }
+
+  sBody << GetDropButtonAppStream(rcButton);
+  Write("N",
+        GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
+        ByteString());
+}
+
+void CPDFSDK_AppStream::SetAsListBox() {
+  CPDF_FormControl* pControl = widget_->GetFormControl();
+  CPDF_FormField* pField = pControl->GetField();
+  CFX_FloatRect rcClient = widget_->GetClientRect();
+  std::ostringstream sBody;
+
+  // Font map must outlive |pEdit|.
+  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                       widget_->GetPDFAnnot()->GetAnnotDict());
+
+  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  pEdit->EnableRefresh(false);
+  pEdit->SetFontMap(&font_map);
+  pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f));
+
+  float fFontSize = widget_->GetFontSize();
+  pEdit->SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize);
+  pEdit->Initialize();
+
+  std::ostringstream sList;
+  float fy = rcClient.top;
+
+  int32_t nTop = pField->GetTopVisibleIndex();
+  int32_t nCount = pField->CountOptions();
+  int32_t nSelCount = pField->CountSelectedItems();
+
+  for (int32_t i = nTop; i < nCount; ++i) {
+    bool bSelected = false;
+    for (int32_t j = 0; j < nSelCount; ++j) {
+      if (pField->GetSelectedIndex(j) == i) {
+        bSelected = true;
+        break;
+      }
+    }
+
+    pEdit->SetText(pField->GetOptionLabel(i));
+
+    CFX_FloatRect rcContent = pEdit->GetContentRect();
+    float fItemHeight = rcContent.Height();
+
+    if (bSelected) {
+      CFX_FloatRect rcItem =
+          CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy);
+      {
+        AutoClosedQCommand q(&sList);
+        sList << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 0, 51.0f / 255.0f,
+                                             113.0f / 255.0f),
+                                   true)
+              << rcItem.left << " " << rcItem.bottom << " " << rcItem.Width()
+              << " " << rcItem.Height() << " " << kAppendRectOperator << " "
+              << kFillOperator << "\n";
+      }
+
+      AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
+      sList << GetColorAppStream(CFX_Color(CFX_Color::kGray, 1), true)
+            << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
+    } else {
+      CFX_Color crText = widget_->GetTextPWLColor();
+
+      AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
+      sList << GetColorAppStream(crText, true)
+            << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
+    }
+
+    fy -= fItemHeight;
+  }
+
+  if (sList.tellp() > 0) {
+    sBody << "/Tx ";
+    AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
+                          kMarkedSequenceEndOperator);
+    AutoClosedQCommand q(&sBody);
+
+    sBody << rcClient.left << " " << rcClient.bottom << " " << rcClient.Width()
+          << " " << rcClient.Height() << " " << kAppendRectOperator << "\n"
+          << kSetNonZeroWindingClipOperator << "\n"
+          << kEndPathNoFillOrStrokeOperator << "\n"
+          << sList.str();
+  }
+  Write("N",
+        GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
+        ByteString());
+}
+
+void CPDFSDK_AppStream::SetAsTextField(Optional<WideString> sValue) {
+  CPDF_FormControl* pControl = widget_->GetFormControl();
+  CPDF_FormField* pField = pControl->GetField();
+  std::ostringstream sBody;
+  std::ostringstream sLines;
+
+  // Font map must outlive |pEdit|.
+  CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(),
+                       widget_->GetPDFAnnot()->GetAnnotDict());
+
+  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
+  pEdit->EnableRefresh(false);
+  pEdit->SetFontMap(&font_map);
+
+  CFX_FloatRect rcClient = widget_->GetClientRect();
+  pEdit->SetPlateRect(rcClient);
+  pEdit->SetAlignmentH(pControl->GetControlAlignment(), true);
+
+  uint32_t dwFieldFlags = pField->GetFieldFlags();
+  bool bMultiLine = dwFieldFlags & pdfium::form_flags::kTextMultiline;
+  if (bMultiLine) {
+    pEdit->SetMultiLine(true, true);
+    pEdit->SetAutoReturn(true, true);
+  } else {
+    pEdit->SetAlignmentV(1, true);
+  }
+
+  uint16_t subWord = 0;
+  if (dwFieldFlags & pdfium::form_flags::kTextPassword) {
+    subWord = '*';
+    pEdit->SetPasswordChar(subWord, true);
+  }
+
+  int nMaxLen = pField->GetMaxLen();
+  bool bCharArray = dwFieldFlags & pdfium::form_flags::kTextComb;
+  float fFontSize = widget_->GetFontSize();
+
+#ifdef PDF_ENABLE_XFA
+  if (!sValue.has_value() && widget_->GetMixXFAWidget())
+    sValue = widget_->GetValue();
+#endif  // PDF_ENABLE_XFA
+
+  if (nMaxLen > 0) {
+    if (bCharArray) {
+      pEdit->SetCharArray(nMaxLen);
+      if (IsFloatZero(fFontSize)) {
+        fFontSize = CPWL_Edit::GetCharArrayAutoFontSize(
+            font_map.GetPDFFont(0).Get(), rcClient, nMaxLen);
+      }
+    } else {
+      if (sValue.has_value())
+        nMaxLen = sValue.value().GetLength();
+      pEdit->SetLimitChar(nMaxLen);
+    }
+  }
+
+  if (IsFloatZero(fFontSize))
+    pEdit->SetAutoFontSize(true, true);
+  else
+    pEdit->SetFontSize(fFontSize);
+
+  pEdit->Initialize();
+  pEdit->SetText(sValue.value_or(pField->GetValue()));
+
+  CFX_FloatRect rcContent = pEdit->GetContentRect();
+  ByteString sEdit =
+      GetEditAppStream(pEdit.get(), CFX_PointF(), !bCharArray, subWord);
+
+  if (sEdit.GetLength() > 0) {
+    sBody << "/Tx ";
+    AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
+                          kMarkedSequenceEndOperator);
+    AutoClosedQCommand q(&sBody);
+
+    if (rcContent.Width() > rcClient.Width() ||
+        rcContent.Height() > rcClient.Height()) {
+      sBody << rcClient.left << " " << rcClient.bottom << " "
+            << rcClient.Width() << " " << rcClient.Height() << " "
+            << kAppendRectOperator << "\n"
+            << kSetNonZeroWindingClipOperator << "\n"
+            << kEndPathNoFillOrStrokeOperator << "\n";
+    }
+    CFX_Color crText = widget_->GetTextPWLColor();
+
+    AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
+    sBody << GetColorAppStream(crText, true) << sEdit;
+  }
+
+  if (bCharArray) {
+    switch (widget_->GetBorderStyle()) {
+      case BorderStyle::SOLID: {
+        ByteString sColor =
+            GetColorAppStream(widget_->GetBorderPWLColor(), false);
+        if (sColor.GetLength() > 0) {
+          AutoClosedQCommand q(&sLines);
+          sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
+                 << "\n"
+                 << GetColorAppStream(widget_->GetBorderPWLColor(), false)
+                 << " 2 " << kSetLineCapStyleOperator << " 0 "
+                 << kSetLineJoinStyleOperator << "\n";
+
+          for (int32_t i = 1; i < nMaxLen; ++i) {
+            sLines << rcClient.left +
+                          ((rcClient.right - rcClient.left) / nMaxLen) * i
+                   << " " << rcClient.bottom << " " << kMoveToOperator << "\n"
+                   << rcClient.left +
+                          ((rcClient.right - rcClient.left) / nMaxLen) * i
+                   << " " << rcClient.top << " " << kLineToOperator << " "
+                   << kStrokeOperator << "\n";
+          }
+        }
+        break;
+      }
+      case BorderStyle::DASH: {
+        ByteString sColor =
+            GetColorAppStream(widget_->GetBorderPWLColor(), false);
+        if (sColor.GetLength() > 0) {
+          CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0);
+          AutoClosedQCommand q(&sLines);
+          sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
+                 << "\n"
+                 << GetColorAppStream(widget_->GetBorderPWLColor(), false)
+                 << "[" << dsBorder.nDash << " " << dsBorder.nGap << "] "
+                 << dsBorder.nPhase << " " << kSetDashOperator << "\n";
+
+          for (int32_t i = 1; i < nMaxLen; ++i) {
+            sLines << rcClient.left +
+                          ((rcClient.right - rcClient.left) / nMaxLen) * i
+                   << " " << rcClient.bottom << " " << kMoveToOperator << "\n"
+                   << rcClient.left +
+                          ((rcClient.right - rcClient.left) / nMaxLen) * i
+                   << " " << rcClient.top << " " << kLineToOperator << " "
+                   << kStrokeOperator << "\n";
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  Write("N",
+        GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sLines) +
+            ByteString(sBody),
+        ByteString());
+}
+
+void CPDFSDK_AppStream::AddImage(const ByteString& sAPType,
+                                 CPDF_Stream* pImage) {
+  CPDF_Stream* pStream = dict_->GetStreamFor(sAPType);
+  CPDF_Dictionary* pStreamDict = pStream->GetDict();
+  ByteString sImageAlias = "IMG";
+
+  if (CPDF_Dictionary* pImageDict = pImage->GetDict()) {
+    sImageAlias = pImageDict->GetStringFor("Name");
+    if (sImageAlias.IsEmpty())
+      sImageAlias = "IMG";
+  }
+
+  CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
+  if (!pStreamResList)
+    pStreamResList = pStreamDict->SetNewFor<CPDF_Dictionary>("Resources");
+
+  CPDF_Dictionary* pXObject =
+      pStreamResList->SetNewFor<CPDF_Dictionary>("XObject");
+  pXObject->SetNewFor<CPDF_Reference>(sImageAlias,
+                                      widget_->GetPageView()->GetPDFDocument(),
+                                      pImage->GetObjNum());
+}
+
+void CPDFSDK_AppStream::Write(const ByteString& sAPType,
+                              const ByteString& sContents,
+                              const ByteString& sAPState) {
+  CPDF_Stream* pStream = nullptr;
+  CPDF_Dictionary* pParentDict = nullptr;
+  if (sAPState.IsEmpty()) {
+    pParentDict = dict_.Get();
+    pStream = dict_->GetStreamFor(sAPType);
+  } else {
+    CPDF_Dictionary* pAPTypeDict = dict_->GetDictFor(sAPType);
+    if (!pAPTypeDict)
+      pAPTypeDict = dict_->SetNewFor<CPDF_Dictionary>(sAPType);
+
+    pParentDict = pAPTypeDict;
+    pStream = pAPTypeDict->GetStreamFor(sAPState);
+  }
+
+  if (!pStream) {
+    CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument();
+    pStream = doc->NewIndirect<CPDF_Stream>();
+    pParentDict->SetNewFor<CPDF_Reference>(sAPType, doc, pStream->GetObjNum());
+  }
+
+  CPDF_Dictionary* pStreamDict = pStream->GetDict();
+  if (!pStreamDict) {
+    auto pNewDict =
+        widget_->GetPDFAnnot()->GetDocument()->New<CPDF_Dictionary>();
+    pStreamDict = pNewDict.Get();
+    pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
+    pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
+    pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
+    pStream->InitStream({}, std::move(pNewDict));
+  }
+  pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix());
+  pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect());
+  pStream->SetDataAndRemoveFilter(sContents.raw_span());
+}
+
+void CPDFSDK_AppStream::Remove(const ByteString& sAPType) {
+  dict_->RemoveFor(sAPType);
+}
+
+ByteString CPDFSDK_AppStream::GetBackgroundAppStream() const {
+  CFX_Color crBackground = widget_->GetFillPWLColor();
+  if (crBackground.nColorType != CFX_Color::kTransparent)
+    return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground);
+
+  return ByteString();
+}
+
+ByteString CPDFSDK_AppStream::GetBorderAppStream() const {
+  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
+  CFX_Color crBorder = widget_->GetBorderPWLColor();
+  CFX_Color crBackground = widget_->GetFillPWLColor();
+  CFX_Color crLeftTop;
+  CFX_Color crRightBottom;
+
+  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
+  CPWL_Dash dsBorder(3, 0, 0);
+
+  BorderStyle nBorderStyle = widget_->GetBorderStyle();
+  switch (nBorderStyle) {
+    case BorderStyle::DASH:
+      dsBorder = CPWL_Dash(3, 3, 0);
+      break;
+    case BorderStyle::BEVELED:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
+      crRightBottom = crBackground / 2.0f;
+      break;
+    case BorderStyle::INSET:
+      fBorderWidth *= 2;
+      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
+      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
+      break;
+    default:
+      break;
+  }
+
+  return GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
+                                    crRightBottom, nBorderStyle, dsBorder);
+}
diff --git a/fpdfsdk/cpdfsdk_appstream.h b/fpdfsdk/cpdfsdk_appstream.h
new file mode 100644
index 0000000..8a38a86
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_appstream.h
@@ -0,0 +1,44 @@
+// 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 FPDFSDK_CPDFSDK_APPSTREAM_H_
+#define FPDFSDK_CPDFSDK_APPSTREAM_H_
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
+
+class CPDFSDK_Widget;
+class CPDF_Dictionary;
+class CPDF_Stream;
+
+class CPDFSDK_AppStream {
+ public:
+  CPDFSDK_AppStream(CPDFSDK_Widget* widget, CPDF_Dictionary* dict);
+  ~CPDFSDK_AppStream();
+
+  void SetAsPushButton();
+  void SetAsCheckBox();
+  void SetAsRadioButton();
+  void SetAsComboBox(Optional<WideString> sValue);
+  void SetAsListBox();
+  void SetAsTextField(Optional<WideString> sValue);
+
+ private:
+  void AddImage(const ByteString& sAPType, CPDF_Stream* pImage);
+  void Write(const ByteString& sAPType,
+             const ByteString& sContents,
+             const ByteString& sAPState);
+  void Remove(const ByteString& sAPType);
+
+  ByteString GetBackgroundAppStream() const;
+  ByteString GetBorderAppStream() const;
+
+  UnownedPtr<CPDFSDK_Widget> const widget_;
+  RetainPtr<CPDF_Dictionary> const dict_;
+};
+
+#endif  // FPDFSDK_CPDFSDK_APPSTREAM_H_
diff --git a/fpdfsdk/cpdfsdk_baannot.cpp b/fpdfsdk/cpdfsdk_baannot.cpp
index a157880..e214163 100644
--- a/fpdfsdk/cpdfsdk_baannot.cpp
+++ b/fpdfsdk/cpdfsdk_baannot.cpp
@@ -9,7 +9,10 @@
 #include <algorithm>
 #include <utility>
 
+#include "constants/annotation_common.h"
+#include "constants/annotation_flags.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
@@ -17,7 +20,6 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "fpdfsdk/cpdfsdk_datetime.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 
 CPDFSDK_BAAnnot::CPDFSDK_BAAnnot(CPDF_Annot* pAnnot,
@@ -26,6 +28,10 @@
 
 CPDFSDK_BAAnnot::~CPDFSDK_BAAnnot() {}
 
+CPDFSDK_BAAnnot* CPDFSDK_BAAnnot::AsBAAnnot() {
+  return this;
+}
+
 CPDF_Annot* CPDFSDK_BAAnnot::GetPDFAnnot() const {
   return m_pAnnot.Get();
 }
@@ -39,17 +45,17 @@
 }
 
 CPDF_Dictionary* CPDFSDK_BAAnnot::GetAPDict() const {
-  CPDF_Dictionary* pAPDict = m_pAnnot->GetAnnotDict()->GetDictFor("AP");
-  if (!pAPDict)
-    pAPDict = m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_Dictionary>("AP");
-  return pAPDict;
+  CPDF_Dictionary* pAPDict =
+      GetAnnotDict()->GetDictFor(pdfium::annotation::kAP);
+  if (pAPDict)
+    return pAPDict;
+  return GetAnnotDict()->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
 }
 
 void CPDFSDK_BAAnnot::SetRect(const CFX_FloatRect& rect) {
-  ASSERT(rect.right - rect.left >= GetMinWidth());
-  ASSERT(rect.top - rect.bottom >= GetMinHeight());
-
-  m_pAnnot->GetAnnotDict()->SetRectFor("Rect", rect);
+  ASSERT(rect.right - rect.left >= 1.0f);
+  ASSERT(rect.top - rect.bottom >= 1.0f);
+  GetAnnotDict()->SetRectFor(pdfium::annotation::kRect, rect);
 }
 
 CFX_FloatRect CPDFSDK_BAAnnot::GetRect() const {
@@ -69,161 +75,97 @@
 }
 
 bool CPDFSDK_BAAnnot::IsAppearanceValid() {
-  return !!m_pAnnot->GetAnnotDict()->GetDictFor("AP");
-}
-
-bool CPDFSDK_BAAnnot::IsAppearanceValid(CPDF_Annot::AppearanceMode mode) {
-  CPDF_Dictionary* pAP = m_pAnnot->GetAnnotDict()->GetDictFor("AP");
-  if (!pAP)
-    return false;
-
-  // Choose the right sub-ap
-  const char* ap_entry = "N";
-  if (mode == CPDF_Annot::Down)
-    ap_entry = "D";
-  else if (mode == CPDF_Annot::Rollover)
-    ap_entry = "R";
-  if (!pAP->KeyExist(ap_entry))
-    ap_entry = "N";
-
-  // Get the AP stream or subdirectory
-  CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry);
-  return !!psub;
-}
-
-void CPDFSDK_BAAnnot::DrawBorder(CFX_RenderDevice* pDevice,
-                                 const CFX_Matrix* pUser2Device,
-                                 const CPDF_RenderOptions* pOptions) {
-  m_pAnnot->DrawBorder(pDevice, pUser2Device, pOptions);
-}
-
-void CPDFSDK_BAAnnot::ClearCachedAP() {
-  m_pAnnot->ClearCachedAP();
-}
-
-void CPDFSDK_BAAnnot::SetContents(const WideString& sContents) {
-  if (sContents.IsEmpty()) {
-    m_pAnnot->GetAnnotDict()->RemoveFor("Contents");
-  } else {
-    m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_String>(
-        "Contents", PDF_EncodeText(sContents), false);
-  }
-}
-
-WideString CPDFSDK_BAAnnot::GetContents() const {
-  return m_pAnnot->GetAnnotDict()->GetUnicodeTextFor("Contents");
+  return !!GetAnnotDict()->GetDictFor(pdfium::annotation::kAP);
 }
 
 void CPDFSDK_BAAnnot::SetAnnotName(const WideString& sName) {
-  if (sName.IsEmpty()) {
-    m_pAnnot->GetAnnotDict()->RemoveFor("NM");
-  } else {
-    m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_String>(
-        "NM", PDF_EncodeText(sName), false);
-  }
+  CPDF_Dictionary* pDict = GetAnnotDict();
+  if (sName.IsEmpty())
+    pDict->RemoveFor(pdfium::annotation::kNM);
+  else
+    pDict->SetNewFor<CPDF_String>(pdfium::annotation::kNM, sName);
 }
 
 WideString CPDFSDK_BAAnnot::GetAnnotName() const {
-  return m_pAnnot->GetAnnotDict()->GetUnicodeTextFor("NM");
-}
-
-void CPDFSDK_BAAnnot::SetModifiedDate(const FX_SYSTEMTIME& st) {
-  CPDFSDK_DateTime dt(st);
-  ByteString str = dt.ToPDFDateTimeString();
-  if (str.IsEmpty())
-    m_pAnnot->GetAnnotDict()->RemoveFor("M");
-  else
-    m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_String>("M", str, false);
-}
-
-FX_SYSTEMTIME CPDFSDK_BAAnnot::GetModifiedDate() const {
-  FX_SYSTEMTIME systime;
-  ByteString str = m_pAnnot->GetAnnotDict()->GetStringFor("M");
-  CPDFSDK_DateTime dt(str);
-  dt.ToSystemTime(systime);
-  return systime;
+  return GetAnnotDict()->GetUnicodeTextFor(pdfium::annotation::kNM);
 }
 
 void CPDFSDK_BAAnnot::SetFlags(uint32_t nFlags) {
-  m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_Number>("F",
-                                                   static_cast<int>(nFlags));
+  GetAnnotDict()->SetNewFor<CPDF_Number>(pdfium::annotation::kF,
+                                         static_cast<int>(nFlags));
 }
 
 uint32_t CPDFSDK_BAAnnot::GetFlags() const {
-  return m_pAnnot->GetAnnotDict()->GetIntegerFor("F");
+  return GetAnnotDict()->GetIntegerFor(pdfium::annotation::kF);
 }
 
 void CPDFSDK_BAAnnot::SetAppState(const ByteString& str) {
+  CPDF_Dictionary* pDict = GetAnnotDict();
   if (str.IsEmpty())
-    m_pAnnot->GetAnnotDict()->RemoveFor("AS");
+    pDict->RemoveFor(pdfium::annotation::kAS);
   else
-    m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_String>("AS", str, false);
+    pDict->SetNewFor<CPDF_String>(pdfium::annotation::kAS, str, false);
 }
 
 ByteString CPDFSDK_BAAnnot::GetAppState() const {
-  return m_pAnnot->GetAnnotDict()->GetStringFor("AS");
+  return GetAnnotDict()->GetStringFor(pdfium::annotation::kAS);
 }
 
-void CPDFSDK_BAAnnot::SetStructParent(int key) {
-  m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_Number>("StructParent", key);
-}
-
-int CPDFSDK_BAAnnot::GetStructParent() const {
-  return m_pAnnot->GetAnnotDict()->GetIntegerFor("StructParent");
-}
-
-// border
 void CPDFSDK_BAAnnot::SetBorderWidth(int nWidth) {
-  CPDF_Array* pBorder = m_pAnnot->GetAnnotDict()->GetArrayFor("Border");
+  CPDF_Array* pBorder =
+      GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder);
   if (pBorder) {
     pBorder->SetNewAt<CPDF_Number>(2, nWidth);
   } else {
-    CPDF_Dictionary* pBSDict = m_pAnnot->GetAnnotDict()->GetDictFor("BS");
+    CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS");
     if (!pBSDict)
-      pBSDict = m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_Dictionary>("BS");
-
+      pBSDict = GetAnnotDict()->SetNewFor<CPDF_Dictionary>("BS");
     pBSDict->SetNewFor<CPDF_Number>("W", nWidth);
   }
 }
 
 int CPDFSDK_BAAnnot::GetBorderWidth() const {
-  if (CPDF_Array* pBorder = m_pAnnot->GetAnnotDict()->GetArrayFor("Border"))
+  if (const CPDF_Array* pBorder =
+          GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder)) {
     return pBorder->GetIntegerAt(2);
+  }
 
-  if (CPDF_Dictionary* pBSDict = m_pAnnot->GetAnnotDict()->GetDictFor("BS"))
+  if (CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS"))
     return pBSDict->GetIntegerFor("W", 1);
 
   return 1;
 }
 
 void CPDFSDK_BAAnnot::SetBorderStyle(BorderStyle nStyle) {
-  CPDF_Dictionary* pBSDict = m_pAnnot->GetAnnotDict()->GetDictFor("BS");
+  CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS");
   if (!pBSDict)
-    pBSDict = m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_Dictionary>("BS");
+    pBSDict = GetAnnotDict()->SetNewFor<CPDF_Dictionary>("BS");
 
+  const char* name = nullptr;
   switch (nStyle) {
     case BorderStyle::SOLID:
-      pBSDict->SetNewFor<CPDF_Name>("S", "S");
+      name = "S";
       break;
     case BorderStyle::DASH:
-      pBSDict->SetNewFor<CPDF_Name>("S", "D");
+      name = "D";
       break;
     case BorderStyle::BEVELED:
-      pBSDict->SetNewFor<CPDF_Name>("S", "B");
+      name = "B";
       break;
     case BorderStyle::INSET:
-      pBSDict->SetNewFor<CPDF_Name>("S", "I");
+      name = "I";
       break;
     case BorderStyle::UNDERLINE:
-      pBSDict->SetNewFor<CPDF_Name>("S", "U");
+      name = "U";
       break;
     default:
-      break;
+      return;
   }
+  pBSDict->SetNewFor<CPDF_Name>("S", name);
 }
 
 BorderStyle CPDFSDK_BAAnnot::GetBorderStyle() const {
-  CPDF_Dictionary* pBSDict = m_pAnnot->GetAnnotDict()->GetDictFor("BS");
+  CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS");
   if (pBSDict) {
     ByteString sBorderStyle = pBSDict->GetStringFor("S", "S");
     if (sBorderStyle == "S")
@@ -238,11 +180,12 @@
       return BorderStyle::UNDERLINE;
   }
 
-  CPDF_Array* pBorder = m_pAnnot->GetAnnotDict()->GetArrayFor("Border");
+  const CPDF_Array* pBorder =
+      GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder);
   if (pBorder) {
-    if (pBorder->GetCount() >= 4) {
-      CPDF_Array* pDP = pBorder->GetArrayAt(3);
-      if (pDP && pDP->GetCount() > 0)
+    if (pBorder->size() >= 4) {
+      const CPDF_Array* pDP = pBorder->GetArrayAt(3);
+      if (pDP && pDP->size() > 0)
         return BorderStyle::DASH;
     }
   }
@@ -250,92 +193,19 @@
   return BorderStyle::SOLID;
 }
 
-void CPDFSDK_BAAnnot::SetColor(FX_COLORREF color) {
-  CPDF_Array* pArray = m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_Array>("C");
-  pArray->AddNew<CPDF_Number>(static_cast<float>(FXSYS_GetRValue(color)) /
-                              255.0f);
-  pArray->AddNew<CPDF_Number>(static_cast<float>(FXSYS_GetGValue(color)) /
-                              255.0f);
-  pArray->AddNew<CPDF_Number>(static_cast<float>(FXSYS_GetBValue(color)) /
-                              255.0f);
-}
-
-void CPDFSDK_BAAnnot::RemoveColor() {
-  m_pAnnot->GetAnnotDict()->RemoveFor("C");
-}
-
-bool CPDFSDK_BAAnnot::GetColor(FX_COLORREF& color) const {
-  if (CPDF_Array* pEntry = m_pAnnot->GetAnnotDict()->GetArrayFor("C")) {
-    size_t nCount = pEntry->GetCount();
-    if (nCount == 1) {
-      float g = pEntry->GetNumberAt(0) * 255;
-
-      color = FXSYS_RGB((int)g, (int)g, (int)g);
-
-      return true;
-    } else if (nCount == 3) {
-      float r = pEntry->GetNumberAt(0) * 255;
-      float g = pEntry->GetNumberAt(1) * 255;
-      float b = pEntry->GetNumberAt(2) * 255;
-
-      color = FXSYS_RGB((int)r, (int)g, (int)b);
-
-      return true;
-    } else if (nCount == 4) {
-      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);
-
-      color = FXSYS_RGB((int)(r * 255), (int)(g * 255), (int)(b * 255));
-
-      return true;
-    }
-  }
-
-  return false;
-}
-
 bool CPDFSDK_BAAnnot::IsVisible() const {
   uint32_t nFlags = GetFlags();
-  return !((nFlags & ANNOTFLAG_INVISIBLE) || (nFlags & ANNOTFLAG_HIDDEN) ||
-           (nFlags & ANNOTFLAG_NOVIEW));
+  return !((nFlags & pdfium::annotation_flags::kInvisible) ||
+           (nFlags & pdfium::annotation_flags::kHidden) ||
+           (nFlags & pdfium::annotation_flags::kNoView));
 }
 
 CPDF_Action CPDFSDK_BAAnnot::GetAction() const {
-  return CPDF_Action(m_pAnnot->GetAnnotDict()->GetDictFor("A"));
-}
-
-void CPDFSDK_BAAnnot::SetAction(const CPDF_Action& action) {
-  CPDF_Dictionary* pDict = action.GetDict();
-  if (pDict != m_pAnnot->GetAnnotDict()->GetDictFor("A")) {
-    CPDF_Document* pDoc = m_pPageView->GetPDFDocument();
-    if (pDict->IsInline())
-      pDict = pDoc->AddIndirectObject(pDict->Clone())->AsDictionary();
-    m_pAnnot->GetAnnotDict()->SetNewFor<CPDF_Reference>("A", pDoc,
-                                                        pDict->GetObjNum());
-  }
-}
-
-void CPDFSDK_BAAnnot::RemoveAction() {
-  m_pAnnot->GetAnnotDict()->RemoveFor("A");
+  return CPDF_Action(GetAnnotDict()->GetDictFor("A"));
 }
 
 CPDF_AAction CPDFSDK_BAAnnot::GetAAction() const {
-  return CPDF_AAction(m_pAnnot->GetAnnotDict()->GetDictFor("AA"));
-}
-
-void CPDFSDK_BAAnnot::SetAAction(const CPDF_AAction& aa) {
-  if (aa.GetDict() != m_pAnnot->GetAnnotDict()->GetDictFor("AA"))
-    m_pAnnot->GetAnnotDict()->SetFor("AA", pdfium::WrapUnique(aa.GetDict()));
-}
-
-void CPDFSDK_BAAnnot::RemoveAAction() {
-  m_pAnnot->GetAnnotDict()->RemoveFor("AA");
+  return CPDF_AAction(GetAnnotDict()->GetDictFor("AA"));
 }
 
 CPDF_Action CPDFSDK_BAAnnot::GetAAction(CPDF_AAction::AActionType eAAT) {
@@ -343,7 +213,7 @@
   if (AAction.ActionExist(eAAT))
     return AAction.GetAction(eAAT);
 
-  if (eAAT == CPDF_AAction::ButtonUp)
+  if (eAAT == CPDF_AAction::kButtonUp)
     return GetAction();
 
   return CPDF_Action(nullptr);
diff --git a/fpdfsdk/cpdfsdk_baannot.h b/fpdfsdk/cpdfsdk_baannot.h
index ac3c0cb..482aa0a 100644
--- a/fpdfsdk/cpdfsdk_baannot.h
+++ b/fpdfsdk/cpdfsdk_baannot.h
@@ -10,14 +10,12 @@
 #include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fpdfdoc/cpdf_defaultappearance.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
-#include "fpdfsdk/cfx_systemhandler.h"
+#include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 
 class CFX_Matrix;
-class CFX_RenderDevice;
 class CPDF_Dictionary;
 class CPDF_RenderOptions;
 class CPDFSDK_PageView;
@@ -28,73 +26,49 @@
   ~CPDFSDK_BAAnnot() override;
 
   // CPDFSDK_Annot
+  CPDFSDK_BAAnnot* AsBAAnnot() override;
   CPDF_Annot::Subtype GetAnnotSubtype() const override;
   void SetRect(const CFX_FloatRect& rect) override;
   CFX_FloatRect GetRect() const override;
   CPDF_Annot* GetPDFAnnot() const override;
+  int GetLayoutOrder() const override;
+
+  virtual CPDF_Action GetAAction(CPDF_AAction::AActionType eAAT);
+  virtual bool IsAppearanceValid();
+  virtual void DrawAppearance(CFX_RenderDevice* pDevice,
+                              const CFX_Matrix& mtUser2Device,
+                              CPDF_Annot::AppearanceMode mode,
+                              const CPDF_RenderOptions* pOptions);
 
   CPDF_Dictionary* GetAnnotDict() const;
   CPDF_Annot* GetPDFPopupAnnot() const;
 
-  CPDF_Dictionary* GetAPDict() const;
-
-  void SetContents(const WideString& sContents);
-  WideString GetContents() const;
-
   void SetAnnotName(const WideString& sName);
   WideString GetAnnotName() const;
 
-  void SetModifiedDate(const FX_SYSTEMTIME& st);
-  FX_SYSTEMTIME GetModifiedDate() const;
-
   void SetFlags(uint32_t nFlags);
   uint32_t GetFlags() const;
 
   void SetAppState(const ByteString& str);
   ByteString GetAppState() const;
 
-  void SetStructParent(int key);
-  int GetStructParent() const;
-
   void SetBorderWidth(int nWidth);
   int GetBorderWidth() const;
 
   void SetBorderStyle(BorderStyle nStyle);
   BorderStyle GetBorderStyle() const;
 
-  void SetColor(FX_COLORREF color);
-  void RemoveColor();
-  bool GetColor(FX_COLORREF& color) const;
-
   bool IsVisible() const;
 
   CPDF_Action GetAction() const;
-  void SetAction(const CPDF_Action& a);
-  void RemoveAction();
 
   CPDF_AAction GetAAction() const;
-  void SetAAction(const CPDF_AAction& aa);
-  void RemoveAAction();
 
-  virtual CPDF_Action GetAAction(CPDF_AAction::AActionType eAAT);
-  virtual bool IsAppearanceValid();
-  virtual bool IsAppearanceValid(CPDF_Annot::AppearanceMode mode);
-  virtual void DrawAppearance(CFX_RenderDevice* pDevice,
-                              const CFX_Matrix& mtUser2Device,
-                              CPDF_Annot::AppearanceMode mode,
-                              const CPDF_RenderOptions* pOptions);
-
-  void DrawBorder(CFX_RenderDevice* pDevice,
-                  const CFX_Matrix* pUser2Device,
-                  const CPDF_RenderOptions* pOptions);
-
-  void ClearCachedAP();
-
-  void SetOpenState(bool bState);
-
-  int GetLayoutOrder() const override;
+  void SetOpenState(bool bOpenState);
 
  protected:
+  CPDF_Dictionary* GetAPDict() const;
+
   UnownedPtr<CPDF_Annot> const m_pAnnot;
 };
 
diff --git a/fpdfsdk/cpdfsdk_baannothandler.cpp b/fpdfsdk/cpdfsdk_baannothandler.cpp
index 027527e..6aa6de7 100644
--- a/fpdfsdk/cpdfsdk_baannothandler.cpp
+++ b/fpdfsdk/cpdfsdk_baannothandler.cpp
@@ -11,16 +11,12 @@
 
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfdoc/cpdf_interform.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
 
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#endif  // PDF_ENABLE_XFA
-
 namespace {
 
 void UpdateAnnotRects(CPDFSDK_PageView* pPageView, CPDFSDK_BAAnnot* pBAAnnot) {
@@ -42,6 +38,11 @@
 
 CPDFSDK_BAAnnotHandler::~CPDFSDK_BAAnnotHandler() {}
 
+void CPDFSDK_BAAnnotHandler::SetFormFillEnvironment(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  // CPDFSDK_BAAnnotHandler does not need it.
+}
+
 bool CPDFSDK_BAAnnotHandler::CanAnswer(CPDFSDK_Annot* pAnnot) {
   return false;
 }
@@ -51,78 +52,71 @@
   return new CPDFSDK_BAAnnot(pAnnot, pPage);
 }
 
-#ifdef PDF_ENABLE_XFA
-CPDFSDK_Annot* CPDFSDK_BAAnnotHandler::NewAnnot(CXFA_FFWidget* hWidget,
-                                                CPDFSDK_PageView* pPage) {
-  return nullptr;
-}
-#endif  // PDF_ENABLE_XFA
-
-void CPDFSDK_BAAnnotHandler::ReleaseAnnot(CPDFSDK_Annot* pAnnot) {
-  delete pAnnot;
+void CPDFSDK_BAAnnotHandler::ReleaseAnnot(
+    std::unique_ptr<CPDFSDK_Annot> pAnnot) {
+  // pAnnot deleted by unique_ptr going out of scope.
 }
 
 void CPDFSDK_BAAnnotHandler::OnDraw(CPDFSDK_PageView* pPageView,
                                     CPDFSDK_Annot* pAnnot,
                                     CFX_RenderDevice* pDevice,
-                                    CFX_Matrix* pUser2Device,
+                                    const CFX_Matrix& mtUser2Device,
                                     bool bDrawAnnots) {
-#ifdef PDF_ENABLE_XFA
-  if (pAnnot->IsXFAField())
+  if (pAnnot->AsXFAWidget())
     return;
-#endif  // PDF_ENABLE_XFA
+
   if (bDrawAnnots && pAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::POPUP) {
-    static_cast<CPDFSDK_BAAnnot*>(pAnnot)->DrawAppearance(
-        pDevice, *pUser2Device, CPDF_Annot::Normal, nullptr);
+    pAnnot->AsBAAnnot()->DrawAppearance(pDevice, mtUser2Device,
+                                        CPDF_Annot::Normal, nullptr);
   }
 }
 
 void CPDFSDK_BAAnnotHandler::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                                          CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                           uint32_t nFlag) {
-  CPDFSDK_BAAnnot* pBAAnnot = static_cast<CPDFSDK_BAAnnot*>(pAnnot->Get());
+  CPDFSDK_BAAnnot* pBAAnnot = (*pAnnot)->AsBAAnnot();
   pBAAnnot->SetOpenState(true);
   UpdateAnnotRects(pPageView, pBAAnnot);
 }
 
 void CPDFSDK_BAAnnotHandler::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          uint32_t nFlag) {
-  CPDFSDK_BAAnnot* pBAAnnot = static_cast<CPDFSDK_BAAnnot*>(pAnnot->Get());
+  CPDFSDK_BAAnnot* pBAAnnot = (*pAnnot)->AsBAAnnot();
   pBAAnnot->SetOpenState(false);
   UpdateAnnotRects(pPageView, pBAAnnot);
 }
 
 bool CPDFSDK_BAAnnotHandler::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                            uint32_t nFlags,
                                            const CFX_PointF& point) {
   return false;
 }
 
 bool CPDFSDK_BAAnnotHandler::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          uint32_t nFlags,
                                          const CFX_PointF& point) {
   return false;
 }
 
 bool CPDFSDK_BAAnnotHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              uint32_t nFlags,
                                              const CFX_PointF& point) {
   return false;
 }
 
 bool CPDFSDK_BAAnnotHandler::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          uint32_t nFlags,
                                          const CFX_PointF& point) {
   return false;
 }
 
 bool CPDFSDK_BAAnnotHandler::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                          CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                           uint32_t nFlags,
                                           short zDelta,
                                           const CFX_PointF& point) {
@@ -130,21 +124,21 @@
 }
 
 bool CPDFSDK_BAAnnotHandler::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                            uint32_t nFlags,
                                            const CFX_PointF& point) {
   return false;
 }
 
 bool CPDFSDK_BAAnnotHandler::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          uint32_t nFlags,
                                          const CFX_PointF& point) {
   return false;
 }
 
 bool CPDFSDK_BAAnnotHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              uint32_t nFlags,
                                              const CFX_PointF& point) {
   return false;
@@ -170,29 +164,37 @@
 
 void CPDFSDK_BAAnnotHandler::OnLoad(CPDFSDK_Annot* pAnnot) {}
 
-bool CPDFSDK_BAAnnotHandler::OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CPDFSDK_BAAnnotHandler::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                         uint32_t nFlag) {
   return false;
 }
 
-bool CPDFSDK_BAAnnotHandler::OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CPDFSDK_BAAnnotHandler::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          uint32_t nFlag) {
   return false;
 }
 
-#ifdef PDF_ENABLE_XFA
-bool CPDFSDK_BAAnnotHandler::OnXFAChangedFocus(
-    CPDFSDK_Annot::ObservedPtr* pOldAnnot,
-    CPDFSDK_Annot::ObservedPtr* pNewAnnot) {
-  return true;
+bool CPDFSDK_BAAnnotHandler::SetIndexSelected(
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+    int index,
+    bool selected) {
+  return false;
 }
-#endif  // PDF_ENABLE_XFA
+
+bool CPDFSDK_BAAnnotHandler::IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                             int index) {
+  return false;
+}
 
 CFX_FloatRect CPDFSDK_BAAnnotHandler::GetViewBBox(CPDFSDK_PageView* pPageView,
                                                   CPDFSDK_Annot* pAnnot) {
   return pAnnot->GetRect();
 }
 
+WideString CPDFSDK_BAAnnotHandler::GetText(CPDFSDK_Annot* pAnnot) {
+  return WideString();
+}
+
 WideString CPDFSDK_BAAnnotHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
   return WideString();
 }
@@ -200,6 +202,22 @@
 void CPDFSDK_BAAnnotHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot,
                                               const WideString& text) {}
 
+bool CPDFSDK_BAAnnotHandler::CanUndo(CPDFSDK_Annot* pAnnot) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnotHandler::CanRedo(CPDFSDK_Annot* pAnnot) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnotHandler::Undo(CPDFSDK_Annot* pAnnot) {
+  return false;
+}
+
+bool CPDFSDK_BAAnnotHandler::Redo(CPDFSDK_Annot* pAnnot) {
+  return false;
+}
+
 bool CPDFSDK_BAAnnotHandler::HitTest(CPDFSDK_PageView* pPageView,
                                      CPDFSDK_Annot* pAnnot,
                                      const CFX_PointF& point) {
diff --git a/fpdfsdk/cpdfsdk_baannothandler.h b/fpdfsdk/cpdfsdk_baannothandler.h
index 7bf8034..295bf0d 100644
--- a/fpdfsdk/cpdfsdk_baannothandler.h
+++ b/fpdfsdk/cpdfsdk_baannothandler.h
@@ -7,6 +7,8 @@
 #ifndef FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_
 #define FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_
 
+#include <memory>
+
 #include "core/fxcrt/fx_coordinates.h"
 #include "fpdfsdk/ipdfsdk_annothandler.h"
 
@@ -18,84 +20,84 @@
 class CPDFSDK_Annot;
 class CPDFSDK_PageView;
 
-#ifdef PDF_ENABLE_XFA
-class CXFA_FFWidget;
-#endif  // PDF_ENABLE_XFA
-
-class CPDFSDK_BAAnnotHandler : public IPDFSDK_AnnotHandler {
+class CPDFSDK_BAAnnotHandler final : public IPDFSDK_AnnotHandler {
  public:
   CPDFSDK_BAAnnotHandler();
   ~CPDFSDK_BAAnnotHandler() override;
 
+  // IPDFSDK_AnnotHandler:
+  void SetFormFillEnvironment(
+      CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
   bool CanAnswer(CPDFSDK_Annot* pAnnot) override;
   CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override;
-#ifdef PDF_ENABLE_XFA
-  CPDFSDK_Annot* NewAnnot(CXFA_FFWidget* hWidget,
-                          CPDFSDK_PageView* pPage) override;
-#endif  // PDF_ENABLE_XFA
-  void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override;
+  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) override;
   CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
                             CPDFSDK_Annot* pAnnot) override;
+  WideString GetText(CPDFSDK_Annot* pAnnot) override;
   WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
   void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override;
+  bool CanUndo(CPDFSDK_Annot* pAnnot) override;
+  bool CanRedo(CPDFSDK_Annot* pAnnot) override;
+  bool Undo(CPDFSDK_Annot* pAnnot) override;
+  bool Redo(CPDFSDK_Annot* pAnnot) override;
   bool HitTest(CPDFSDK_PageView* pPageView,
                CPDFSDK_Annot* pAnnot,
                const CFX_PointF& point) override;
   void OnDraw(CPDFSDK_PageView* pPageView,
               CPDFSDK_Annot* pAnnot,
               CFX_RenderDevice* pDevice,
-              CFX_Matrix* pUser2Device,
+              const CFX_Matrix& mtUser2Device,
               bool bDrawAnnots) override;
   void OnLoad(CPDFSDK_Annot* pAnnot) override;
 
   void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
                     uint32_t nFlag) override;
   void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlag) override;
   bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
                      uint32_t nFlags,
                      const CFX_PointF& point) override;
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot::ObservedPtr* pAnnot,
+                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
                        uint32_t nFlags,
                        const CFX_PointF& point) override;
   bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
                     uint32_t nFlags,
                     short zDelta,
                     const CFX_PointF& point) override;
   bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
                      uint32_t nFlags,
                      const CFX_PointF& point) override;
   bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot::ObservedPtr* pAnnot,
+                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
                        uint32_t nFlags,
                        const CFX_PointF& point) override;
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
   bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
   bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag) override;
-  bool OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag) override;
-#ifdef PDF_ENABLE_XFA
-  bool OnXFAChangedFocus(CPDFSDK_Annot::ObservedPtr* pOldAnnot,
-                         CPDFSDK_Annot::ObservedPtr* pNewAnnot) override;
-#endif  // PDF_ENABLE_XFA
+  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
+  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
+  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                        int index,
+                        bool selected) override;
+  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index) override;
 };
 
 #endif  // FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_
diff --git a/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp b/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp
new file mode 100644
index 0000000..9a26e83
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp
@@ -0,0 +1,88 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
+#include "fpdfsdk/cpdfsdk_baannothandler.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "public/fpdf_annot.h"
+#include "testing/embedder_test.h"
+
+class CPDFSDK_BAAnnotHandlerTest : public EmbedderTest {
+ public:
+  void SetUp() override {
+    // Test behaviour with currently supported annot i.e. Widget.
+    // TODO(crbug.com/994500): Add an API that can set list of focusable
+    // subtypes once other annots(links & highlights) are also supported.
+    EmbedderTest::SetUp();
+    SetUpBAAnnotHandler();
+  }
+
+  void TearDown() override {
+    UnloadPage(m_page);
+    EmbedderTest::TearDown();
+  }
+
+  void SetUpBAAnnotHandler() {
+    EXPECT_TRUE(OpenDocument("links_highlights_annots.pdf"));
+    m_page = LoadPage(0);
+    ASSERT_TRUE(m_page);
+
+    CPDFSDK_FormFillEnvironment* pFormFillEnv =
+        CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
+    ASSERT_TRUE(pFormFillEnv);
+    m_pPageView = pFormFillEnv->GetPageView(IPDFPageFromFPDFPage(m_page), true);
+    ASSERT_TRUE(m_pPageView);
+
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        pFormFillEnv->GetAnnotHandlerMgr();
+    ASSERT_TRUE(pAnnotHandlerMgr);
+    m_pBAAnnotHandler = pAnnotHandlerMgr->m_pBAAnnotHandler.get();
+    ASSERT_TRUE(m_pBAAnnotHandler);
+  }
+
+  CPDFSDK_PageView* GetPageView() const { return m_pPageView; }
+  CPDFSDK_BAAnnotHandler* GetBAAnnotHandler() const {
+    return m_pBAAnnotHandler;
+  }
+
+ private:
+  FPDF_PAGE m_page = nullptr;
+  CPDFSDK_PageView* m_pPageView = nullptr;
+  CPDFSDK_BAAnnotHandler* m_pBAAnnotHandler = nullptr;
+};
+
+TEST_F(CPDFSDK_BAAnnotHandlerTest, TabToLinkOrHighlightAnnot) {
+  // TODO(crbug.com/994500): Create annot iterator with list of supported
+  // focusable subtypes as provided by host.
+  CPDFSDK_AnnotIterator ai(GetPageView(), CPDF_Annot::Subtype::LINK);
+  CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot();
+  ASSERT_TRUE(pAnnot);
+  EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::LINK);
+
+  ObservedPtr<CPDFSDK_Annot> pNonWidgetAnnot(pAnnot);
+
+  // TODO(crbug.com/994500): Change expected value as true once
+  // links & highlights are supported.
+  EXPECT_FALSE(GetBAAnnotHandler()->OnSetFocus(&pNonWidgetAnnot, 0));
+
+  EXPECT_FALSE(GetBAAnnotHandler()->OnKillFocus(&pNonWidgetAnnot, 0));
+}
+
+TEST_F(CPDFSDK_BAAnnotHandlerTest, TabToInvalidAnnot) {
+  // TODO(crbug.com/994500): Create annot iterator with list of supported
+  // focusable subtypes as provided by host.
+  CPDFSDK_AnnotIterator ai(GetPageView(), CPDF_Annot::Subtype::WIDGET);
+  CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot();
+  ASSERT_TRUE(pAnnot);
+  EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::WIDGET);
+
+  ObservedPtr<CPDFSDK_Annot> pWidgetAnnot(pAnnot);
+
+  // Passing wrong subtype to BAAnnotHandler, API should return false.
+  EXPECT_FALSE(GetBAAnnotHandler()->OnSetFocus(&pWidgetAnnot, 0));
+
+  EXPECT_FALSE(GetBAAnnotHandler()->OnKillFocus(&pWidgetAnnot, 0));
+}
diff --git a/fpdfsdk/cpdfsdk_customaccess.cpp b/fpdfsdk/cpdfsdk_customaccess.cpp
new file mode 100644
index 0000000..108ffbc
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_customaccess.cpp
@@ -0,0 +1,34 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_customaccess.h"
+
+#include "core/fxcrt/fx_safe_types.h"
+
+CPDFSDK_CustomAccess::CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess)
+    : m_FileAccess(*pFileAccess) {}
+
+CPDFSDK_CustomAccess::~CPDFSDK_CustomAccess() = default;
+
+FX_FILESIZE CPDFSDK_CustomAccess::GetSize() {
+  return m_FileAccess.m_FileLen;
+}
+
+bool CPDFSDK_CustomAccess::ReadBlockAtOffset(void* buffer,
+                                             FX_FILESIZE offset,
+                                             size_t size) {
+  if (!buffer || offset < 0 || !size)
+    return false;
+
+  if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(size))
+    return false;
+
+  FX_SAFE_FILESIZE new_pos = size;
+  new_pos += offset;
+  return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() &&
+         m_FileAccess.m_GetBlock(m_FileAccess.m_Param, offset,
+                                 static_cast<uint8_t*>(buffer), size);
+}
diff --git a/fpdfsdk/cpdfsdk_customaccess.h b/fpdfsdk/cpdfsdk_customaccess.h
new file mode 100644
index 0000000..76940ce
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_customaccess.h
@@ -0,0 +1,31 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_CUSTOMACCESS_H_
+#define FPDFSDK_CPDFSDK_CUSTOMACCESS_H_
+
+#include "core/fxcrt/fx_stream.h"
+#include "public/fpdfview.h"
+
+class CPDFSDK_CustomAccess final : public IFX_SeekableReadStream {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  // IFX_SeekableReadStream
+  FX_FILESIZE GetSize() override;
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override;
+
+ private:
+  explicit CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess);
+  ~CPDFSDK_CustomAccess() override;
+
+  FPDF_FILEACCESS m_FileAccess;
+};
+
+#endif  // FPDFSDK_CPDFSDK_CUSTOMACCESS_H_
diff --git a/fpdfsdk/cpdfsdk_datetime.cpp b/fpdfsdk/cpdfsdk_datetime.cpp
deleted file mode 100644
index 332ae8e..0000000
--- a/fpdfsdk/cpdfsdk_datetime.cpp
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_datetime.h"
-
-#include "core/fxcrt/fx_extension.h"
-
-namespace {
-
-int GetTimeZoneInSeconds(int8_t tzhour, uint8_t tzminute) {
-  return (int)tzhour * 3600 + (int)tzminute * (tzhour >= 0 ? 60 : -60);
-}
-
-bool IsLeapYear(int16_t year) {
-  return ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0)));
-}
-
-uint16_t GetYearDays(int16_t year) {
-  return (IsLeapYear(year) ? 366 : 365);
-}
-
-uint8_t GetMonthDays(int16_t year, uint8_t month) {
-  uint8_t mDays;
-  switch (month) {
-    case 1:
-    case 3:
-    case 5:
-    case 7:
-    case 8:
-    case 10:
-    case 12:
-      mDays = 31;
-      break;
-
-    case 4:
-    case 6:
-    case 9:
-    case 11:
-      mDays = 30;
-      break;
-
-    case 2:
-      if (IsLeapYear(year))
-        mDays = 29;
-      else
-        mDays = 28;
-      break;
-
-    default:
-      mDays = 0;
-      break;
-  }
-
-  return mDays;
-}
-
-}  // namespace
-
-CPDFSDK_DateTime::CPDFSDK_DateTime() {
-  ResetDateTime();
-}
-
-CPDFSDK_DateTime::CPDFSDK_DateTime(const ByteString& dtStr) {
-  ResetDateTime();
-  FromPDFDateTimeString(dtStr);
-}
-
-CPDFSDK_DateTime::CPDFSDK_DateTime(const CPDFSDK_DateTime& that)
-    : m_year(that.m_year),
-      m_month(that.m_month),
-      m_day(that.m_day),
-      m_hour(that.m_hour),
-      m_minute(that.m_minute),
-      m_second(that.m_second),
-      m_tzHour(that.m_tzHour),
-      m_tzMinute(that.m_tzMinute) {}
-
-CPDFSDK_DateTime::CPDFSDK_DateTime(const FX_SYSTEMTIME& st) {
-  tzset();
-
-  m_year = static_cast<int16_t>(st.wYear);
-  m_month = static_cast<uint8_t>(st.wMonth);
-  m_day = static_cast<uint8_t>(st.wDay);
-  m_hour = static_cast<uint8_t>(st.wHour);
-  m_minute = static_cast<uint8_t>(st.wMinute);
-  m_second = static_cast<uint8_t>(st.wSecond);
-}
-
-void CPDFSDK_DateTime::ResetDateTime() {
-  tzset();
-
-  time_t curTime;
-  time(&curTime);
-
-  struct tm* newtime = localtime(&curTime);
-  m_year = newtime->tm_year + 1900;
-  m_month = newtime->tm_mon + 1;
-  m_day = newtime->tm_mday;
-  m_hour = newtime->tm_hour;
-  m_minute = newtime->tm_min;
-  m_second = newtime->tm_sec;
-}
-
-bool CPDFSDK_DateTime::operator==(const CPDFSDK_DateTime& that) const {
-  return m_year == that.m_year && m_month == that.m_month &&
-         m_day == that.m_day && m_hour == that.m_hour &&
-         m_minute == that.m_minute && m_second == that.m_second &&
-         m_tzHour == that.m_tzHour && m_tzMinute == that.m_tzMinute;
-}
-
-bool CPDFSDK_DateTime::operator!=(const CPDFSDK_DateTime& datetime) const {
-  return !(*this == datetime);
-}
-
-time_t CPDFSDK_DateTime::ToTime_t() const {
-  struct tm newtime;
-
-  newtime.tm_year = m_year - 1900;
-  newtime.tm_mon = m_month - 1;
-  newtime.tm_mday = m_day;
-  newtime.tm_hour = m_hour;
-  newtime.tm_min = m_minute;
-  newtime.tm_sec = m_second;
-
-  return mktime(&newtime);
-}
-
-CPDFSDK_DateTime& CPDFSDK_DateTime::FromPDFDateTimeString(
-    const ByteString& dtStr) {
-  int strLength = dtStr.GetLength();
-  if (strLength <= 0)
-    return *this;
-
-  int i = 0;
-  while (i < strLength && !std::isdigit(dtStr[i]))
-    ++i;
-
-  if (i >= strLength)
-    return *this;
-
-  int j = 0;
-  int k = 0;
-  char ch;
-  while (i < strLength && j < 4) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_year = static_cast<int16_t>(k);
-  if (i >= strLength || j < 4)
-    return *this;
-
-  j = 0;
-  k = 0;
-  while (i < strLength && j < 2) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_month = static_cast<uint8_t>(k);
-  if (i >= strLength || j < 2)
-    return *this;
-
-  j = 0;
-  k = 0;
-  while (i < strLength && j < 2) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_day = static_cast<uint8_t>(k);
-  if (i >= strLength || j < 2)
-    return *this;
-
-  j = 0;
-  k = 0;
-  while (i < strLength && j < 2) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_hour = static_cast<uint8_t>(k);
-  if (i >= strLength || j < 2)
-    return *this;
-
-  j = 0;
-  k = 0;
-  while (i < strLength && j < 2) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_minute = static_cast<uint8_t>(k);
-  if (i >= strLength || j < 2)
-    return *this;
-
-  j = 0;
-  k = 0;
-  while (i < strLength && j < 2) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_second = static_cast<uint8_t>(k);
-  if (i >= strLength || j < 2)
-    return *this;
-
-  ch = dtStr[i++];
-  if (ch != '-' && ch != '+')
-    return *this;
-  if (ch == '-')
-    m_tzHour = -1;
-  else
-    m_tzHour = 1;
-  j = 0;
-  k = 0;
-  while (i < strLength && j < 2) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_tzHour *= static_cast<int8_t>(k);
-  if (i >= strLength || j < 2)
-    return *this;
-
-  if (dtStr[i++] != '\'')
-    return *this;
-  j = 0;
-  k = 0;
-  while (i < strLength && j < 2) {
-    ch = dtStr[i];
-    k = k * 10 + FXSYS_DecimalCharToInt(ch);
-    j++;
-    if (!std::isdigit(ch))
-      break;
-    i++;
-  }
-  m_tzMinute = static_cast<uint8_t>(k);
-  return *this;
-}
-
-ByteString CPDFSDK_DateTime::ToCommonDateTimeString() {
-  return ByteString::Format("%04d-%02u-%02u %02u:%02u:%02u ", m_year, m_month,
-                            m_day, m_hour, m_minute, m_second) +
-         (m_tzHour < 0 ? "-" : "+") +
-         ByteString::Format("%02d:%02u", std::abs(static_cast<int>(m_tzHour)),
-                            m_tzMinute);
-}
-
-ByteString CPDFSDK_DateTime::ToPDFDateTimeString() {
-  ByteString dtStr;
-  char tempStr[32];
-  memset(tempStr, 0, sizeof(tempStr));
-  FXSYS_snprintf(tempStr, sizeof(tempStr) - 1, "D:%04d%02u%02u%02u%02u%02u",
-                 m_year, m_month, m_day, m_hour, m_minute, m_second);
-  dtStr = ByteString(tempStr);
-  if (m_tzHour < 0)
-    dtStr += ByteString("-");
-  else
-    dtStr += ByteString("+");
-  memset(tempStr, 0, sizeof(tempStr));
-  FXSYS_snprintf(tempStr, sizeof(tempStr) - 1, "%02d'%02u'",
-                 std::abs(static_cast<int>(m_tzHour)), m_tzMinute);
-  dtStr += ByteString(tempStr);
-  return dtStr;
-}
-
-void CPDFSDK_DateTime::ToSystemTime(FX_SYSTEMTIME& st) {
-  time_t t = this->ToTime_t();
-  struct tm* pTime = localtime(&t);
-
-  if (!pTime)
-    return;
-
-  st.wYear = static_cast<uint16_t>(pTime->tm_year) + 1900;
-  st.wMonth = static_cast<uint16_t>(pTime->tm_mon) + 1;
-  st.wDay = static_cast<uint16_t>(pTime->tm_mday);
-  st.wDayOfWeek = static_cast<uint16_t>(pTime->tm_wday);
-  st.wHour = static_cast<uint16_t>(pTime->tm_hour);
-  st.wMinute = static_cast<uint16_t>(pTime->tm_min);
-  st.wSecond = static_cast<uint16_t>(pTime->tm_sec);
-  st.wMilliseconds = 0;
-}
-
-CPDFSDK_DateTime CPDFSDK_DateTime::ToGMT() const {
-  CPDFSDK_DateTime new_dt = *this;
-  new_dt.AddSeconds(-GetTimeZoneInSeconds(new_dt.m_tzHour, new_dt.m_tzMinute));
-  new_dt.m_tzHour = 0;
-  new_dt.m_tzMinute = 0;
-  return new_dt;
-}
-
-CPDFSDK_DateTime& CPDFSDK_DateTime::AddDays(short days) {
-  if (days == 0)
-    return *this;
-
-  int16_t y = m_year;
-  uint8_t m = m_month;
-  uint8_t d = m_day;
-
-  int ldays = days;
-  if (ldays > 0) {
-    int16_t yy = y;
-    if ((static_cast<uint16_t>(m) * 100 + d) > 300)
-      yy++;
-    int ydays = GetYearDays(yy);
-    int mdays;
-    while (ldays >= ydays) {
-      y++;
-      ldays -= ydays;
-      yy++;
-      mdays = GetMonthDays(y, m);
-      if (d > mdays) {
-        m++;
-        d -= mdays;
-      }
-      ydays = GetYearDays(yy);
-    }
-    mdays = GetMonthDays(y, m) - d + 1;
-    while (ldays >= mdays) {
-      ldays -= mdays;
-      m++;
-      d = 1;
-      mdays = GetMonthDays(y, m);
-    }
-    d += ldays;
-  } else {
-    ldays *= -1;
-    int16_t yy = y;
-    if ((static_cast<uint16_t>(m) * 100 + d) < 300)
-      yy--;
-    int ydays = GetYearDays(yy);
-    while (ldays >= ydays) {
-      y--;
-      ldays -= ydays;
-      yy--;
-      int mdays = GetMonthDays(y, m);
-      if (d > mdays) {
-        m++;
-        d -= mdays;
-      }
-      ydays = GetYearDays(yy);
-    }
-    while (ldays >= d) {
-      ldays -= d;
-      m--;
-      d = GetMonthDays(y, m);
-    }
-    d -= ldays;
-  }
-
-  m_year = y;
-  m_month = m;
-  m_day = d;
-
-  return *this;
-}
-
-CPDFSDK_DateTime& CPDFSDK_DateTime::AddSeconds(int seconds) {
-  if (seconds == 0)
-    return *this;
-
-  int n;
-  int days;
-
-  n = m_hour * 3600 + m_minute * 60 + m_second + seconds;
-  if (n < 0) {
-    days = (n - 86399) / 86400;
-    n -= days * 86400;
-  } else {
-    days = n / 86400;
-    n %= 86400;
-  }
-  m_hour = static_cast<uint8_t>(n / 3600);
-  m_hour %= 24;
-  n %= 3600;
-  m_minute = static_cast<uint8_t>(n / 60);
-  m_second = static_cast<uint8_t>(n % 60);
-  if (days != 0)
-    AddDays(days);
-
-  return *this;
-}
diff --git a/fpdfsdk/cpdfsdk_datetime.h b/fpdfsdk/cpdfsdk_datetime.h
deleted file mode 100644
index cbd3c36..0000000
--- a/fpdfsdk/cpdfsdk_datetime.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_DATETIME_H_
-#define FPDFSDK_CPDFSDK_DATETIME_H_
-
-#if _FX_OS_ == _FX_OS_ANDROID_
-#include <time.h>
-#else
-#include <ctime>
-#endif
-
-#include "fpdfsdk/cfx_systemhandler.h"
-
-class CPDFSDK_DateTime {
- public:
-  CPDFSDK_DateTime();
-  explicit CPDFSDK_DateTime(const ByteString& dtStr);
-  explicit CPDFSDK_DateTime(const FX_SYSTEMTIME& st);
-  CPDFSDK_DateTime(const CPDFSDK_DateTime& datetime);
-
-  bool operator==(const CPDFSDK_DateTime& datetime) const;
-  bool operator!=(const CPDFSDK_DateTime& datetime) const;
-
-  CPDFSDK_DateTime& FromPDFDateTimeString(const ByteString& dtStr);
-  ByteString ToCommonDateTimeString();
-  ByteString ToPDFDateTimeString();
-  void ToSystemTime(FX_SYSTEMTIME& st);
-  time_t ToTime_t() const;
-  CPDFSDK_DateTime ToGMT() const;
-  CPDFSDK_DateTime& AddDays(short days);
-  CPDFSDK_DateTime& AddSeconds(int seconds);
-  void ResetDateTime();
-
- private:
-  int16_t m_year;
-  uint8_t m_month;
-  uint8_t m_day;
-  uint8_t m_hour;
-  uint8_t m_minute;
-  uint8_t m_second;
-  int8_t m_tzHour;
-  uint8_t m_tzMinute;
-};
-
-#endif  // FPDFSDK_CPDFSDK_DATETIME_H_
diff --git a/fpdfsdk/cpdfsdk_fieldaction.cpp b/fpdfsdk/cpdfsdk_fieldaction.cpp
new file mode 100644
index 0000000..57fa1b6
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_fieldaction.cpp
@@ -0,0 +1,11 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_fieldaction.h"
+
+CPDFSDK_FieldAction::CPDFSDK_FieldAction() = default;
+
+CPDFSDK_FieldAction::~CPDFSDK_FieldAction() = default;
diff --git a/fpdfsdk/cpdfsdk_fieldaction.h b/fpdfsdk/cpdfsdk_fieldaction.h
new file mode 100644
index 0000000..e287911
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_fieldaction.h
@@ -0,0 +1,30 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_FIELDACTION_H_
+#define FPDFSDK_CPDFSDK_FIELDACTION_H_
+
+#include "core/fxcrt/fx_string.h"
+
+struct CPDFSDK_FieldAction {
+  CPDFSDK_FieldAction();
+  CPDFSDK_FieldAction(const CPDFSDK_FieldAction& other) = delete;
+  ~CPDFSDK_FieldAction();
+
+  bool bModifier = false;
+  bool bShift = false;
+  bool bKeyDown = false;
+  bool bWillCommit = false;
+  bool bFieldFull = false;
+  bool bRC = true;
+  int nSelEnd = 0;
+  int nSelStart = 0;
+  WideString sChange;
+  WideString sChangeEx;
+  WideString sValue;
+};
+
+#endif  // FPDFSDK_CPDFSDK_FIELDACTION_H_
diff --git a/fpdfsdk/cpdfsdk_filewriteadapter.cpp b/fpdfsdk/cpdfsdk_filewriteadapter.cpp
new file mode 100644
index 0000000..39a69a5
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_filewriteadapter.cpp
@@ -0,0 +1,22 @@
+// 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 "fpdfsdk/cpdfsdk_filewriteadapter.h"
+
+CPDFSDK_FileWriteAdapter::CPDFSDK_FileWriteAdapter(FPDF_FILEWRITE* file_write)
+    : file_write_(file_write) {
+  ASSERT(file_write_);
+}
+
+CPDFSDK_FileWriteAdapter::~CPDFSDK_FileWriteAdapter() {}
+
+bool CPDFSDK_FileWriteAdapter::WriteBlock(const void* data, size_t size) {
+  return file_write_->WriteBlock(file_write_.Get(), data, size) != 0;
+}
+
+bool CPDFSDK_FileWriteAdapter::WriteString(ByteStringView str) {
+  return WriteBlock(str.unterminated_c_str(), str.GetLength());
+}
diff --git a/fpdfsdk/cpdfsdk_filewriteadapter.h b/fpdfsdk/cpdfsdk_filewriteadapter.h
new file mode 100644
index 0000000..fc42b7f
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_filewriteadapter.h
@@ -0,0 +1,31 @@
+// 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 FPDFSDK_CPDFSDK_FILEWRITEADAPTER_H_
+#define FPDFSDK_CPDFSDK_FILEWRITEADAPTER_H_
+
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "public/fpdf_save.h"
+
+class CPDFSDK_FileWriteAdapter final : public IFX_RetainableWriteStream {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  // IFX_WriteStream:
+  bool WriteBlock(const void* data, size_t size) override;
+  bool WriteString(ByteStringView str) override;
+
+ private:
+  explicit CPDFSDK_FileWriteAdapter(FPDF_FILEWRITE* file_write);
+  ~CPDFSDK_FileWriteAdapter() override;
+
+  UnownedPtr<FPDF_FILEWRITE> file_write_;
+};
+
+#endif  // FPDFSDK_CPDFSDK_FILEWRITEADAPTER_H_
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.cpp b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
index 955c184..021ec60 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.cpp
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
@@ -8,44 +8,49 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfdoc/cpdf_docjsactions.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfdoc/cpdf_nametree.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "fpdfsdk/cpdfsdk_actionhandler.h"
 #include "fpdfsdk/cpdfsdk_annothandlermgr.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_formfiller.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
-#include "fpdfsdk/fsdk_actionhandler.h"
 #include "fxjs/ijs_runtime.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
-namespace {
-
-// NOTE: |bsUTF16LE| must outlive the use of the result. Care must be taken
-// since modifying the result would impact |bsUTF16LE|.
 FPDF_WIDESTRING AsFPDFWideString(ByteString* bsUTF16LE) {
+  // Force a private version of the string, since we're about to hand it off
+  // to the embedder. Should the embedder modify it by accident, it won't
+  // corrupt other shares of the string beyond |bsUTF16LE|.
   return reinterpret_cast<FPDF_WIDESTRING>(
-      bsUTF16LE->GetBuffer(bsUTF16LE->GetLength()));
+      bsUTF16LE->GetBuffer(bsUTF16LE->GetLength()).data());
 }
 
-}  // namespace
-
 CPDFSDK_FormFillEnvironment::CPDFSDK_FormFillEnvironment(
-    UnderlyingDocumentType* pDoc,
-    FPDF_FORMFILLINFO* pFFinfo)
+    CPDF_Document* pDoc,
+    FPDF_FORMFILLINFO* pFFinfo,
+    std::unique_ptr<CPDFSDK_AnnotHandlerMgr> pHandlerMgr)
     : m_pInfo(pFFinfo),
-      m_pUnderlyingDoc(pDoc),
-      m_pSysHandler(pdfium::MakeUnique<CFX_SystemHandler>(this)),
-      m_bChangeMask(false),
-      m_bBeingDestroyed(false) {}
+      m_pCPDFDoc(pDoc),
+      m_pAnnotHandlerMgr(std::move(pHandlerMgr)) {
+  ASSERT(m_pCPDFDoc);
+  m_pAnnotHandlerMgr->SetFormFillEnv(this);
+}
 
 CPDFSDK_FormFillEnvironment::~CPDFSDK_FormFillEnvironment() {
   m_bBeingDestroyed = true;
   ClearAllFocusedAnnots();
 
-  // |m_PageMap| will try to access |m_pInterForm| when it cleans itself up.
-  // Make sure it is deleted before |m_pInterForm|.
+  // |m_PageMap| will try to access |m_pInteractiveForm| when it cleans itself
+  // up. Make sure it is deleted before |m_pInteractiveForm|.
   m_PageMap.clear();
 
   // |m_pAnnotHandlerMgr| will try to access |m_pFormFiller| when it cleans
@@ -61,25 +66,125 @@
     m_pInfo->Release(m_pInfo);
 }
 
-int CPDFSDK_FormFillEnvironment::JS_appAlert(const wchar_t* Msg,
-                                             const wchar_t* Title,
-                                             uint32_t Type,
-                                             uint32_t Icon) {
+void CPDFSDK_FormFillEnvironment::InvalidateRect(PerWindowData* pWidgetData,
+                                                 const CFX_FloatRect& rect) {
+  auto* pPrivateData = static_cast<CFFL_PrivateData*>(pWidgetData);
+  CPDFSDK_Widget* widget = pPrivateData->pWidget.Get();
+  if (!widget)
+    return;
+
+  CPDFSDK_PageView* pPageView = widget->GetPageView();
+  IPDF_Page* pPage = widget->GetPage();
+  if (!pPage || !pPageView)
+    return;
+
+  CFX_Matrix device2page = pPageView->GetCurrentMatrix().GetInverse();
+  CFX_PointF left_top = device2page.Transform(CFX_PointF(rect.left, rect.top));
+  CFX_PointF right_bottom =
+      device2page.Transform(CFX_PointF(rect.right, rect.bottom));
+
+  CFX_FloatRect rcPDF(left_top.x, right_bottom.y, right_bottom.x, left_top.y);
+  rcPDF.Normalize();
+  Invalidate(pPage, rcPDF.GetOuterRect());
+}
+
+void CPDFSDK_FormFillEnvironment::OutputSelectedRect(
+    CFFL_FormFiller* pFormFiller,
+    const CFX_FloatRect& rect) {
+  if (!pFormFiller || !m_pInfo || !m_pInfo->FFI_OutputSelectedRect)
+    return;
+
+  auto* pPage = FPDFPageFromIPDFPage(pFormFiller->GetSDKAnnot()->GetPage());
+  ASSERT(pPage);
+
+  CFX_PointF ptA = pFormFiller->PWLtoFFL(CFX_PointF(rect.left, rect.bottom));
+  CFX_PointF ptB = pFormFiller->PWLtoFFL(CFX_PointF(rect.right, rect.top));
+  m_pInfo->FFI_OutputSelectedRect(m_pInfo, pPage, ptA.x, ptB.y, ptB.x, ptA.y);
+}
+
+bool CPDFSDK_FormFillEnvironment::IsSelectionImplemented() const {
+  FPDF_FORMFILLINFO* pInfo = GetFormFillInfo();
+  return pInfo && pInfo->FFI_OutputSelectedRect;
+}
+
+#ifdef PDF_ENABLE_V8
+CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetCurrentView() {
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(GetCurrentPage());
+  return pPage ? GetPageView(pPage, true) : nullptr;
+}
+
+FPDF_PAGE CPDFSDK_FormFillEnvironment::GetCurrentPage() const {
+  if (m_pInfo && m_pInfo->FFI_GetCurrentPage) {
+    return m_pInfo->FFI_GetCurrentPage(
+        m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()));
+  }
+  return nullptr;
+}
+
+WideString CPDFSDK_FormFillEnvironment::GetLanguage() {
+#ifdef PDF_ENABLE_XFA
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_GetLanguage)
+    return WideString();
+
+  int nRequiredLen = m_pInfo->FFI_GetLanguage(m_pInfo, nullptr, 0);
+  if (nRequiredLen <= 0)
+    return WideString();
+
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
+  int nActualLen =
+      m_pInfo->FFI_GetLanguage(m_pInfo, pBuff.data(), nRequiredLen);
+  if (nActualLen <= 0 || nActualLen > nRequiredLen)
+    return WideString();
+
+  return WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
+                                 nActualLen / sizeof(uint16_t));
+#else   // PDF_ENABLE_XFA
+  return WideString();
+#endif  // PDF_ENABLE_XFA
+}
+
+WideString CPDFSDK_FormFillEnvironment::GetPlatform() {
+#ifdef PDF_ENABLE_XFA
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_GetPlatform)
+    return WideString();
+
+  int nRequiredLen = m_pInfo->FFI_GetPlatform(m_pInfo, nullptr, 0);
+  if (nRequiredLen <= 0)
+    return WideString();
+
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
+  int nActualLen =
+      m_pInfo->FFI_GetPlatform(m_pInfo, pBuff.data(), nRequiredLen);
+  if (nActualLen <= 0 || nActualLen > nRequiredLen)
+    return WideString();
+
+  return WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
+                                 nActualLen / sizeof(uint16_t));
+#else   // PDF_ENABLE_XFA
+  return WideString();
+#endif  // PDF_ENABLE_XFA
+}
+
+int CPDFSDK_FormFillEnvironment::JS_appAlert(const WideString& Msg,
+                                             const WideString& Title,
+                                             int Type,
+                                             int Icon) {
   if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
       !m_pInfo->m_pJsPlatform->app_alert) {
     return -1;
   }
-  ByteString bsMsg = WideString(Msg).UTF16LE_Encode();
-  ByteString bsTitle = WideString(Title).UTF16LE_Encode();
+
+  ByteString bsMsg = Msg.ToUTF16LE();
+  ByteString bsTitle = Title.ToUTF16LE();
   return m_pInfo->m_pJsPlatform->app_alert(
       m_pInfo->m_pJsPlatform, AsFPDFWideString(&bsMsg),
       AsFPDFWideString(&bsTitle), Type, Icon);
 }
 
-int CPDFSDK_FormFillEnvironment::JS_appResponse(const wchar_t* Question,
-                                                const wchar_t* Title,
-                                                const wchar_t* Default,
-                                                const wchar_t* cLabel,
+int CPDFSDK_FormFillEnvironment::JS_appResponse(const WideString& Question,
+                                                const WideString& Title,
+                                                const WideString& Default,
+                                                const WideString& Label,
                                                 FPDF_BOOL bPassword,
                                                 void* response,
                                                 int length) {
@@ -87,10 +192,10 @@
       !m_pInfo->m_pJsPlatform->app_response) {
     return -1;
   }
-  ByteString bsQuestion = WideString(Question).UTF16LE_Encode();
-  ByteString bsTitle = WideString(Title).UTF16LE_Encode();
-  ByteString bsDefault = WideString(Default).UTF16LE_Encode();
-  ByteString bsLabel = WideString(cLabel).UTF16LE_Encode();
+  ByteString bsQuestion = Question.ToUTF16LE();
+  ByteString bsTitle = Title.ToUTF16LE();
+  ByteString bsDefault = Default.ToUTF16LE();
+  ByteString bsLabel = Label.ToUTF16LE();
   return m_pInfo->m_pJsPlatform->app_response(
       m_pInfo->m_pJsPlatform, AsFPDFWideString(&bsQuestion),
       AsFPDFWideString(&bsTitle), AsFPDFWideString(&bsDefault),
@@ -115,66 +220,34 @@
   if (nRequiredLen <= 0)
     return WideString();
 
-  std::vector<uint8_t> pBuff(nRequiredLen);
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
   const int nActualLen = m_pInfo->m_pJsPlatform->Field_browse(
       m_pInfo->m_pJsPlatform, pBuff.data(), nRequiredLen);
   if (nActualLen <= 0 || nActualLen > nRequiredLen)
     return WideString();
 
-  pBuff.resize(nActualLen);
-  return WideString::FromLocal(ByteStringView(pBuff));
-}
-
-WideString CPDFSDK_FormFillEnvironment::JS_docGetFilePath() {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Doc_getFilePath) {
-    return WideString();
-  }
-  const int nRequiredLen = m_pInfo->m_pJsPlatform->Doc_getFilePath(
-      m_pInfo->m_pJsPlatform, nullptr, 0);
-  if (nRequiredLen <= 0)
-    return WideString();
-
-  std::vector<uint8_t> pBuff(nRequiredLen);
-  const int nActualLen = m_pInfo->m_pJsPlatform->Doc_getFilePath(
-      m_pInfo->m_pJsPlatform, pBuff.data(), nRequiredLen);
-  if (nActualLen <= 0 || nActualLen > nRequiredLen)
-    return WideString();
-
-  pBuff.resize(nActualLen);
-  return WideString::FromLocal(ByteStringView(pBuff));
-}
-
-void CPDFSDK_FormFillEnvironment::JS_docSubmitForm(void* formData,
-                                                   int length,
-                                                   const wchar_t* URL) {
-  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
-      !m_pInfo->m_pJsPlatform->Doc_submitForm) {
-    return;
-  }
-  ByteString bsDestination = WideString(URL).UTF16LE_Encode();
-  m_pInfo->m_pJsPlatform->Doc_submitForm(m_pInfo->m_pJsPlatform, formData,
-                                         length,
-                                         AsFPDFWideString(&bsDestination));
+  // Don't include trailing NUL.
+  pBuff.resize(nActualLen - 1);
+  return WideString::FromDefANSI(ByteStringView(pBuff));
 }
 
 void CPDFSDK_FormFillEnvironment::JS_docmailForm(void* mailData,
                                                  int length,
                                                  FPDF_BOOL bUI,
-                                                 const wchar_t* To,
-                                                 const wchar_t* Subject,
-                                                 const wchar_t* CC,
-                                                 const wchar_t* BCC,
-                                                 const wchar_t* Msg) {
+                                                 const WideString& To,
+                                                 const WideString& Subject,
+                                                 const WideString& CC,
+                                                 const WideString& BCC,
+                                                 const WideString& Msg) {
   if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
       !m_pInfo->m_pJsPlatform->Doc_mail) {
     return;
   }
-  ByteString bsTo = WideString(To).UTF16LE_Encode();
-  ByteString bsSubject = WideString(Subject).UTF16LE_Encode();
-  ByteString bsCC = WideString(CC).UTF16LE_Encode();
-  ByteString bsBcc = WideString(BCC).UTF16LE_Encode();
-  ByteString bsMsg = WideString(Msg).UTF16LE_Encode();
+  ByteString bsTo = To.ToUTF16LE();
+  ByteString bsSubject = Subject.ToUTF16LE();
+  ByteString bsCC = CC.ToUTF16LE();
+  ByteString bsBcc = BCC.ToUTF16LE();
+  ByteString bsMsg = Msg.ToUTF16LE();
   m_pInfo->m_pJsPlatform->Doc_mail(
       m_pInfo->m_pJsPlatform, mailData, length, bUI, AsFPDFWideString(&bsTo),
       AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
@@ -206,17 +279,51 @@
   m_pInfo->m_pJsPlatform->Doc_gotoPage(m_pInfo->m_pJsPlatform, nPageNum);
 }
 
-IJS_Runtime* CPDFSDK_FormFillEnvironment::GetJSRuntime() {
-  if (!IsJSInitiated())
-    return nullptr;
-  if (!m_pJSRuntime)
-    m_pJSRuntime.reset(IJS_Runtime::Create(this));
-  return m_pJSRuntime.get();
+WideString CPDFSDK_FormFillEnvironment::JS_docGetFilePath() {
+  return GetFilePath();
+}
+#endif  // PDF_ENABLE_V8
+
+WideString CPDFSDK_FormFillEnvironment::GetFilePath() const {
+  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
+      !m_pInfo->m_pJsPlatform->Doc_getFilePath) {
+    return WideString();
+  }
+  const int nRequiredLen = m_pInfo->m_pJsPlatform->Doc_getFilePath(
+      m_pInfo->m_pJsPlatform, nullptr, 0);
+  if (nRequiredLen <= 0)
+    return WideString();
+
+  std::vector<uint8_t, FxAllocAllocator<uint8_t>> pBuff(nRequiredLen);
+  const int nActualLen = m_pInfo->m_pJsPlatform->Doc_getFilePath(
+      m_pInfo->m_pJsPlatform, pBuff.data(), nRequiredLen);
+  if (nActualLen <= 0 || nActualLen > nRequiredLen)
+    return WideString();
+
+  // Don't include trailing NUL.
+  pBuff.resize(nActualLen - 1);
+  return WideString::FromDefANSI(ByteStringView(pBuff));
+}
+
+void CPDFSDK_FormFillEnvironment::SubmitForm(pdfium::span<uint8_t> form_data,
+                                             const WideString& URL) {
+  if (!m_pInfo || !m_pInfo->m_pJsPlatform ||
+      !m_pInfo->m_pJsPlatform->Doc_submitForm) {
+    return;
+  }
+  ByteString bsUrl = URL.ToUTF16LE();
+  m_pInfo->m_pJsPlatform->Doc_submitForm(m_pInfo->m_pJsPlatform,
+                                         form_data.data(), form_data.size(),
+                                         AsFPDFWideString(&bsUrl));
+}
+
+IJS_Runtime* CPDFSDK_FormFillEnvironment::GetIJSRuntime() {
+  if (!m_pIJSRuntime)
+    m_pIJSRuntime = IJS_Runtime::Create(this);
+  return m_pIJSRuntime.get();
 }
 
 CPDFSDK_AnnotHandlerMgr* CPDFSDK_FormFillEnvironment::GetAnnotHandlerMgr() {
-  if (!m_pAnnotHandlerMgr)
-    m_pAnnotHandlerMgr = pdfium::MakeUnique<CPDFSDK_AnnotHandlerMgr>(this);
   return m_pAnnotHandlerMgr.get();
 }
 
@@ -233,20 +340,11 @@
   return m_pFormFiller.get();
 }
 
-void CPDFSDK_FormFillEnvironment::Invalidate(UnderlyingPageType* page,
+void CPDFSDK_FormFillEnvironment::Invalidate(IPDF_Page* page,
                                              const FX_RECT& rect) {
   if (m_pInfo && m_pInfo->FFI_Invalidate) {
-    m_pInfo->FFI_Invalidate(m_pInfo, page, rect.left, rect.top, rect.right,
-                            rect.bottom);
-  }
-}
-
-void CPDFSDK_FormFillEnvironment::OutputSelectedRect(
-    UnderlyingPageType* page,
-    const CFX_FloatRect& rect) {
-  if (m_pInfo && m_pInfo->FFI_OutputSelectedRect) {
-    m_pInfo->FFI_OutputSelectedRect(m_pInfo, page, rect.left, rect.top,
-                                    rect.right, rect.bottom);
+    m_pInfo->FFI_Invalidate(m_pInfo, FPDFPageFromIPDFPage(page), rect.left,
+                            rect.top, rect.right, rect.bottom);
   }
 }
 
@@ -259,7 +357,7 @@
                                           TimerCallback lpTimerFunc) {
   if (m_pInfo && m_pInfo->FFI_SetTimer)
     return m_pInfo->FFI_SetTimer(m_pInfo, uElapse, lpTimerFunc);
-  return -1;
+  return TimerHandlerIface::kInvalidTimerID;
 }
 
 void CPDFSDK_FormFillEnvironment::KillTimer(int nTimerID) {
@@ -267,35 +365,11 @@
     m_pInfo->FFI_KillTimer(m_pInfo, nTimerID);
 }
 
-FX_SYSTEMTIME CPDFSDK_FormFillEnvironment::GetLocalTime() const {
-  FX_SYSTEMTIME fxtime;
-  if (!m_pInfo || !m_pInfo->FFI_GetLocalTime)
-    return fxtime;
-
-  FPDF_SYSTEMTIME systime = m_pInfo->FFI_GetLocalTime(m_pInfo);
-  fxtime.wDay = systime.wDay;
-  fxtime.wDayOfWeek = systime.wDayOfWeek;
-  fxtime.wHour = systime.wHour;
-  fxtime.wMilliseconds = systime.wMilliseconds;
-  fxtime.wMinute = systime.wMinute;
-  fxtime.wMonth = systime.wMonth;
-  fxtime.wSecond = systime.wSecond;
-  fxtime.wYear = systime.wYear;
-  return fxtime;
-}
-
 void CPDFSDK_FormFillEnvironment::OnChange() {
   if (m_pInfo && m_pInfo->FFI_OnChange)
     m_pInfo->FFI_OnChange(m_pInfo);
 }
 
-FPDF_PAGE CPDFSDK_FormFillEnvironment::GetCurrentPage(
-    UnderlyingDocumentType* document) {
-  if (m_pInfo && m_pInfo->FFI_GetCurrentPage)
-    return m_pInfo->FFI_GetCurrentPage(m_pInfo, document);
-  return nullptr;
-}
-
 void CPDFSDK_FormFillEnvironment::ExecuteNamedAction(const char* namedAction) {
   if (m_pInfo && m_pInfo->FFI_ExecuteNamedAction)
     m_pInfo->FFI_ExecuteNamedAction(m_pInfo, namedAction);
@@ -325,95 +399,71 @@
 }
 
 #ifdef PDF_ENABLE_XFA
-void CPDFSDK_FormFillEnvironment::DisplayCaret(CPDFXFA_Page* page,
+int CPDFSDK_FormFillEnvironment::GetPageViewCount() const {
+  return pdfium::CollectionSize<int>(m_PageMap);
+}
+
+void CPDFSDK_FormFillEnvironment::DisplayCaret(IPDF_Page* page,
                                                FPDF_BOOL bVisible,
                                                double left,
                                                double top,
                                                double right,
                                                double bottom) {
-  if (m_pInfo && m_pInfo->FFI_DisplayCaret) {
-    m_pInfo->FFI_DisplayCaret(m_pInfo, page, bVisible, left, top, right,
-                              bottom);
+  if (m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_DisplayCaret) {
+    m_pInfo->FFI_DisplayCaret(m_pInfo, FPDFPageFromIPDFPage(page), bVisible,
+                              left, top, right, bottom);
   }
 }
 
-int CPDFSDK_FormFillEnvironment::GetCurrentPageIndex(
-    CPDFXFA_Context* document) {
-  if (!m_pInfo || !m_pInfo->FFI_GetCurrentPageIndex)
+int CPDFSDK_FormFillEnvironment::GetCurrentPageIndex() const {
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_GetCurrentPageIndex)
     return -1;
-  return m_pInfo->FFI_GetCurrentPageIndex(m_pInfo, document);
+  return m_pInfo->FFI_GetCurrentPageIndex(
+      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()));
 }
 
-void CPDFSDK_FormFillEnvironment::SetCurrentPage(CPDFXFA_Context* document,
-                                                 int iCurPage) {
-  if (m_pInfo && m_pInfo->FFI_SetCurrentPage)
-    m_pInfo->FFI_SetCurrentPage(m_pInfo, document, iCurPage);
+void CPDFSDK_FormFillEnvironment::SetCurrentPage(int iCurPage) {
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_SetCurrentPage)
+    return;
+  m_pInfo->FFI_SetCurrentPage(
+      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()), iCurPage);
 }
 
-WideString CPDFSDK_FormFillEnvironment::GetPlatform() {
-  if (!m_pInfo || !m_pInfo->FFI_GetPlatform)
-    return WideString();
-
-  int nRequiredLen = m_pInfo->FFI_GetPlatform(m_pInfo, nullptr, 0);
-  if (nRequiredLen <= 0)
-    return WideString();
-
-  std::vector<uint8_t> pBuff(nRequiredLen);
-  int nActualLen =
-      m_pInfo->FFI_GetPlatform(m_pInfo, pBuff.data(), nRequiredLen);
-  if (nActualLen <= 0 || nActualLen > nRequiredLen)
-    return WideString();
-
-  return WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
-                                 nActualLen / sizeof(uint16_t));
-}
-
-void CPDFSDK_FormFillEnvironment::GotoURL(CPDFXFA_Context* document,
-                                          const WideStringView& wsURL) {
-  if (!m_pInfo || !m_pInfo->FFI_GotoURL)
+void CPDFSDK_FormFillEnvironment::GotoURL(const WideString& wsURL) {
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_GotoURL)
     return;
 
-  ByteString bsTo = WideString(wsURL).UTF16LE_Encode();
-  FPDF_WIDESTRING pTo = (FPDF_WIDESTRING)bsTo.GetBuffer(wsURL.GetLength());
-  m_pInfo->FFI_GotoURL(m_pInfo, document, pTo);
-  bsTo.ReleaseBuffer(bsTo.GetStringLength());
+  ByteString bsTo = wsURL.ToUTF16LE();
+  m_pInfo->FFI_GotoURL(m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()),
+                       AsFPDFWideString(&bsTo));
 }
 
-void CPDFSDK_FormFillEnvironment::GetPageViewRect(CPDFXFA_Page* page,
-                                                  FS_RECTF& dstRect) {
-  if (!m_pInfo || !m_pInfo->FFI_GetPageViewRect)
-    return;
+FS_RECTF CPDFSDK_FormFillEnvironment::GetPageViewRect(IPDF_Page* page) {
+  FS_RECTF rect = {0.0f, 0.0f, 0.0f, 0.0f};
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_GetPageViewRect)
+    return rect;
 
   double left;
   double top;
   double right;
   double bottom;
-  m_pInfo->FFI_GetPageViewRect(m_pInfo, page, &left, &top, &right, &bottom);
-  if (top < bottom)
-    std::swap(top, bottom);
+  m_pInfo->FFI_GetPageViewRect(m_pInfo, FPDFPageFromIPDFPage(page), &left, &top,
+                               &right, &bottom);
 
-  dstRect.left = static_cast<float>(left);
-  dstRect.top = static_cast<float>(top);
-  dstRect.bottom = static_cast<float>(bottom);
-  dstRect.right = static_cast<float>(right);
+  rect.left = static_cast<float>(left);
+  rect.top = static_cast<float>(top);
+  rect.bottom = static_cast<float>(bottom);
+  rect.right = static_cast<float>(right);
+  return rect;
 }
 
-bool CPDFSDK_FormFillEnvironment::PopupMenu(CPDFXFA_Page* page,
+bool CPDFSDK_FormFillEnvironment::PopupMenu(IPDF_Page* page,
                                             FPDF_WIDGET hWidget,
                                             int menuFlag,
-                                            CFX_PointF pt) {
-  return m_pInfo && m_pInfo->FFI_PopupMenu &&
-         m_pInfo->FFI_PopupMenu(m_pInfo, page, hWidget, menuFlag, pt.x, pt.y);
-}
-
-void CPDFSDK_FormFillEnvironment::Alert(FPDF_WIDESTRING Msg,
-                                        FPDF_WIDESTRING Title,
-                                        int Type,
-                                        int Icon) {
-  if (m_pInfo && m_pInfo->m_pJsPlatform && m_pInfo->m_pJsPlatform->app_alert) {
-    m_pInfo->m_pJsPlatform->app_alert(m_pInfo->m_pJsPlatform, Msg, Title, Type,
-                                      Icon);
-  }
+                                            const CFX_PointF& pt) {
+  return m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_PopupMenu &&
+         m_pInfo->FFI_PopupMenu(m_pInfo, FPDFPageFromIPDFPage(page), hWidget,
+                                menuFlag, pt.x, pt.y);
 }
 
 void CPDFSDK_FormFillEnvironment::EmailTo(FPDF_FILEHANDLER* fileHandler,
@@ -422,117 +472,86 @@
                                           FPDF_WIDESTRING pCC,
                                           FPDF_WIDESTRING pBcc,
                                           FPDF_WIDESTRING pMsg) {
-  if (m_pInfo && m_pInfo->FFI_EmailTo)
+  if (m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_EmailTo)
     m_pInfo->FFI_EmailTo(m_pInfo, fileHandler, pTo, pSubject, pCC, pBcc, pMsg);
 }
 
 void CPDFSDK_FormFillEnvironment::UploadTo(FPDF_FILEHANDLER* fileHandler,
                                            int fileFlag,
                                            FPDF_WIDESTRING uploadTo) {
-  if (m_pInfo && m_pInfo->FFI_UploadTo)
+  if (m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_UploadTo)
     m_pInfo->FFI_UploadTo(m_pInfo, fileHandler, fileFlag, uploadTo);
 }
 
 FPDF_FILEHANDLER* CPDFSDK_FormFillEnvironment::OpenFile(int fileType,
                                                         FPDF_WIDESTRING wsURL,
                                                         const char* mode) {
-  if (m_pInfo && m_pInfo->FFI_OpenFile)
+  if (m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_OpenFile)
     return m_pInfo->FFI_OpenFile(m_pInfo, fileType, wsURL, mode);
   return nullptr;
 }
 
 RetainPtr<IFX_SeekableReadStream> CPDFSDK_FormFillEnvironment::DownloadFromURL(
-    const wchar_t* url) {
-  if (!m_pInfo || !m_pInfo->FFI_DownloadFromURL)
+    const WideString& url) {
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_DownloadFromURL)
     return nullptr;
 
-  ByteString bstrURL = WideString(url).UTF16LE_Encode();
-  FPDF_WIDESTRING wsURL =
-      (FPDF_WIDESTRING)bstrURL.GetBuffer(bstrURL.GetLength());
+  ByteString bstrURL = url.ToUTF16LE();
+  FPDF_FILEHANDLER* fileHandler =
+      m_pInfo->FFI_DownloadFromURL(m_pInfo, AsFPDFWideString(&bstrURL));
 
-  FPDF_LPFILEHANDLER fileHandler = m_pInfo->FFI_DownloadFromURL(m_pInfo, wsURL);
   return MakeSeekableStream(fileHandler);
 }
 
 WideString CPDFSDK_FormFillEnvironment::PostRequestURL(
-    const wchar_t* wsURL,
-    const wchar_t* wsData,
-    const wchar_t* wsContentType,
-    const wchar_t* wsEncode,
-    const wchar_t* wsHeader) {
-  if (!m_pInfo || !m_pInfo->FFI_PostRequestURL)
-    return L"";
+    const WideString& wsURL,
+    const WideString& wsData,
+    const WideString& wsContentType,
+    const WideString& wsEncode,
+    const WideString& wsHeader) {
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_PostRequestURL)
+    return WideString();
 
-  ByteString bsURL = WideString(wsURL).UTF16LE_Encode();
-  FPDF_WIDESTRING URL = (FPDF_WIDESTRING)bsURL.GetBuffer(bsURL.GetLength());
-
-  ByteString bsData = WideString(wsData).UTF16LE_Encode();
-  FPDF_WIDESTRING data = (FPDF_WIDESTRING)bsData.GetBuffer(bsData.GetLength());
-
-  ByteString bsContentType = WideString(wsContentType).UTF16LE_Encode();
-  FPDF_WIDESTRING contentType =
-      (FPDF_WIDESTRING)bsContentType.GetBuffer(bsContentType.GetLength());
-
-  ByteString bsEncode = WideString(wsEncode).UTF16LE_Encode();
-  FPDF_WIDESTRING encode =
-      (FPDF_WIDESTRING)bsEncode.GetBuffer(bsEncode.GetLength());
-
-  ByteString bsHeader = WideString(wsHeader).UTF16LE_Encode();
-  FPDF_WIDESTRING header =
-      (FPDF_WIDESTRING)bsHeader.GetBuffer(bsHeader.GetLength());
+  ByteString bsURL = wsURL.ToUTF16LE();
+  ByteString bsData = wsData.ToUTF16LE();
+  ByteString bsContentType = wsContentType.ToUTF16LE();
+  ByteString bsEncode = wsEncode.ToUTF16LE();
+  ByteString bsHeader = wsHeader.ToUTF16LE();
 
   FPDF_BSTR response;
   FPDF_BStr_Init(&response);
-  m_pInfo->FFI_PostRequestURL(m_pInfo, URL, data, contentType, encode, header,
-                              &response);
+  m_pInfo->FFI_PostRequestURL(
+      m_pInfo, AsFPDFWideString(&bsURL), AsFPDFWideString(&bsData),
+      AsFPDFWideString(&bsContentType), AsFPDFWideString(&bsEncode),
+      AsFPDFWideString(&bsHeader), &response);
 
-  WideString wsRet = WideString::FromUTF16LE(
-      (FPDF_WIDESTRING)response.str, response.len / sizeof(FPDF_WIDESTRING));
+  WideString wsRet =
+      WideString::FromUTF16LE(reinterpret_cast<FPDF_WIDESTRING>(response.str),
+                              response.len / sizeof(FPDF_WIDESTRING));
+
   FPDF_BStr_Clear(&response);
-
   return wsRet;
 }
 
-FPDF_BOOL CPDFSDK_FormFillEnvironment::PutRequestURL(const wchar_t* wsURL,
-                                                     const wchar_t* wsData,
-                                                     const wchar_t* wsEncode) {
-  if (!m_pInfo || !m_pInfo->FFI_PutRequestURL)
+FPDF_BOOL CPDFSDK_FormFillEnvironment::PutRequestURL(
+    const WideString& wsURL,
+    const WideString& wsData,
+    const WideString& wsEncode) {
+  if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_PutRequestURL)
     return false;
 
-  ByteString bsURL = WideString(wsURL).UTF16LE_Encode();
-  FPDF_WIDESTRING URL = (FPDF_WIDESTRING)bsURL.GetBuffer(bsURL.GetLength());
+  ByteString bsURL = wsURL.ToUTF16LE();
+  ByteString bsData = wsData.ToUTF16LE();
+  ByteString bsEncode = wsEncode.ToUTF16LE();
 
-  ByteString bsData = WideString(wsData).UTF16LE_Encode();
-  FPDF_WIDESTRING data = (FPDF_WIDESTRING)bsData.GetBuffer(bsData.GetLength());
-
-  ByteString bsEncode = WideString(wsEncode).UTF16LE_Encode();
-  FPDF_WIDESTRING encode =
-      (FPDF_WIDESTRING)bsEncode.GetBuffer(bsEncode.GetLength());
-
-  return m_pInfo->FFI_PutRequestURL(m_pInfo, URL, data, encode);
-}
-
-WideString CPDFSDK_FormFillEnvironment::GetLanguage() {
-  if (!m_pInfo || !m_pInfo->FFI_GetLanguage)
-    return WideString();
-
-  int nRequiredLen = m_pInfo->FFI_GetLanguage(m_pInfo, nullptr, 0);
-  if (nRequiredLen <= 0)
-    return WideString();
-
-  std::vector<uint8_t> pBuff(nRequiredLen);
-  int nActualLen =
-      m_pInfo->FFI_GetLanguage(m_pInfo, pBuff.data(), nRequiredLen);
-  if (nActualLen <= 0 || nActualLen > nRequiredLen)
-    return WideString();
-
-  return WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
-                                 nActualLen / sizeof(uint16_t));
+  return m_pInfo->FFI_PutRequestURL(m_pInfo, AsFPDFWideString(&bsURL),
+                                    AsFPDFWideString(&bsData),
+                                    AsFPDFWideString(&bsEncode));
 }
 
 void CPDFSDK_FormFillEnvironment::PageEvent(int iPageCount,
                                             uint32_t dwEventType) const {
-  if (m_pInfo && m_pInfo->FFI_PageEvent)
+  if (m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_PageEvent)
     m_pInfo->FFI_PageEvent(m_pInfo, iPageCount, dwEventType);
 }
 #endif  // PDF_ENABLE_XFA
@@ -545,7 +564,7 @@
 }
 
 CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView(
-    UnderlyingPageType* pUnderlyingPage,
+    IPDF_Page* pUnderlyingPage,
     bool renew) {
   auto it = m_PageMap.find(pUnderlyingPage);
   if (it != m_PageMap.end())
@@ -563,14 +582,8 @@
   return pPageView;
 }
 
-CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetCurrentView() {
-  UnderlyingPageType* pPage =
-      UnderlyingFromFPDFPage(GetCurrentPage(m_pUnderlyingDoc.Get()));
-  return pPage ? GetPageView(pPage, true) : nullptr;
-}
-
 CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView(int nIndex) {
-  UnderlyingPageType* pTempPage = GetPage(nIndex);
+  IPDF_Page* pTempPage = GetPage(nIndex);
   if (!pTempPage)
     return nullptr;
 
@@ -578,22 +591,18 @@
   return it != m_PageMap.end() ? it->second.get() : nullptr;
 }
 
-void CPDFSDK_FormFillEnvironment::ProcJavascriptFun() {
-  CPDF_Document* pPDFDoc = GetPDFDocument();
-  CPDF_DocJSActions docJS(pPDFDoc);
-  int iCount = docJS.CountJSActions();
+void CPDFSDK_FormFillEnvironment::ProcJavascriptAction() {
+  CPDF_NameTree docJS(m_pCPDFDoc.Get(), "JavaScript");
+  int iCount = docJS.GetCount();
   for (int i = 0; i < iCount; i++) {
-    WideString csJSName;
-    CPDF_Action jsAction = docJS.GetJSActionAndName(i, &csJSName);
-    GetActionHandler()->DoAction_JavaScript(jsAction, csJSName, this);
+    WideString name;
+    CPDF_Action action(ToDictionary(docJS.LookupValueAndName(i, &name)));
+    GetActionHandler()->DoAction_JavaScript(action, name, this);
   }
 }
 
 bool CPDFSDK_FormFillEnvironment::ProcOpenAction() {
-  if (!m_pUnderlyingDoc)
-    return false;
-
-  const CPDF_Dictionary* pRoot = GetPDFDocument()->GetRoot();
+  CPDF_Dictionary* pRoot = m_pCPDFDoc->GetRoot();
   if (!pRoot)
     return false;
 
@@ -615,8 +624,7 @@
   return true;
 }
 
-void CPDFSDK_FormFillEnvironment::RemovePageView(
-    UnderlyingPageType* pUnderlyingPage) {
+void CPDFSDK_FormFillEnvironment::RemovePageView(IPDF_Page* pUnderlyingPage) {
   auto it = m_PageMap.find(pUnderlyingPage);
   if (it == m_PageMap.end())
     return;
@@ -642,17 +650,17 @@
   m_PageMap.erase(it);
 }
 
-UnderlyingPageType* CPDFSDK_FormFillEnvironment::GetPage(int nIndex) {
+IPDF_Page* CPDFSDK_FormFillEnvironment::GetPage(int nIndex) {
   if (!m_pInfo || !m_pInfo->FFI_GetPage)
     return nullptr;
-  return UnderlyingFromFPDFPage(
-      m_pInfo->FFI_GetPage(m_pInfo, m_pUnderlyingDoc.Get(), nIndex));
+  return IPDFPageFromFPDFPage(m_pInfo->FFI_GetPage(
+      m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()), nIndex));
 }
 
-CPDFSDK_InterForm* CPDFSDK_FormFillEnvironment::GetInterForm() {
-  if (!m_pInterForm)
-    m_pInterForm = pdfium::MakeUnique<CPDFSDK_InterForm>(this);
-  return m_pInterForm.get();
+CPDFSDK_InteractiveForm* CPDFSDK_FormFillEnvironment::GetInteractiveForm() {
+  if (!m_pInteractiveForm)
+    m_pInteractiveForm = pdfium::MakeUnique<CPDFSDK_InteractiveForm>(this);
+  return m_pInteractiveForm.get();
 }
 
 void CPDFSDK_FormFillEnvironment::UpdateAllViews(CPDFSDK_PageView* pSender,
@@ -665,14 +673,14 @@
 }
 
 bool CPDFSDK_FormFillEnvironment::SetFocusAnnot(
-    CPDFSDK_Annot::ObservedPtr* pAnnot) {
+    ObservedPtr<CPDFSDK_Annot>* pAnnot) {
   if (m_bBeingDestroyed)
     return false;
   if (m_pFocusAnnot == *pAnnot)
     return true;
   if (m_pFocusAnnot && !KillFocusAnnot(0))
     return false;
-  if (!*pAnnot)
+  if (!pAnnot->HasObservable())
     return false;
 
   CPDFSDK_PageView* pPageView = (*pAnnot)->GetPageView();
@@ -684,9 +692,13 @@
     return false;
 
 #ifdef PDF_ENABLE_XFA
-  CPDFSDK_Annot::ObservedPtr pLastFocusAnnot(m_pFocusAnnot.Get());
+  ObservedPtr<CPDFSDK_Annot> pLastFocusAnnot(m_pFocusAnnot.Get());
   if (!pAnnotHandler->Annot_OnChangeFocus(pAnnot, &pLastFocusAnnot))
     return false;
+
+  // |pAnnot| may be destroyed in |Annot_OnChangeFocus|.
+  if (!pAnnot->HasObservable())
+    return false;
 #endif  // PDF_ENABLE_XFA
   if (!pAnnotHandler->Annot_OnSetFocus(pAnnot, 0))
     return false;
@@ -702,11 +714,11 @@
     return false;
 
   CPDFSDK_AnnotHandlerMgr* pAnnotHandler = GetAnnotHandlerMgr();
-  CPDFSDK_Annot::ObservedPtr pFocusAnnot(m_pFocusAnnot.Get());
+  ObservedPtr<CPDFSDK_Annot> pFocusAnnot(m_pFocusAnnot.Get());
   m_pFocusAnnot.Reset();
 
 #ifdef PDF_ENABLE_XFA
-  CPDFSDK_Annot::ObservedPtr pNull;
+  ObservedPtr<CPDFSDK_Annot> pNull;
   if (!pAnnotHandler->Annot_OnChangeFocus(&pNull, &pFocusAnnot))
     return false;
 #endif  // PDF_ENABLE_XFA
@@ -717,7 +729,7 @@
   }
 
   if (pFocusAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::WIDGET) {
-    CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pFocusAnnot.Get());
+    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pFocusAnnot.Get());
     FormFieldType fieldType = pWidget->GetFieldType();
     if (fieldType == FormFieldType::kTextField ||
         fieldType == FormFieldType::kComboBox) {
@@ -727,6 +739,11 @@
   return !m_pFocusAnnot;
 }
 
+int CPDFSDK_FormFillEnvironment::GetPageCount() const {
+  CPDF_Document::Extension* pExtension = m_pCPDFDoc->GetExtension();
+  return pExtension ? pExtension->GetPageCount() : m_pCPDFDoc->GetPageCount();
+}
+
 bool CPDFSDK_FormFillEnvironment::GetPermissions(int nFlag) const {
-  return !!(GetPDFDocument()->GetUserPermissions() & nFlag);
+  return !!(m_pCPDFDoc->GetUserPermissions() & nFlag);
 }
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.h b/fpdfsdk/cpdfsdk_formfillenvironment.h
index 31a1c51..ea67394 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.h
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.h
@@ -9,26 +9,27 @@
 
 #include <map>
 #include <memory>
-#include <vector>
 
+#include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfdoc/cpdf_occontext.h"
-#include "core/fxcrt/observable.h"
-#include "fpdfsdk/cfx_systemhandler.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/timerhandler_iface.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/fsdk_define.h"
+#include "fpdfsdk/pwl/ipwl_systemhandler.h"
 #include "public/fpdf_formfill.h"
-#include "public/fpdf_fwlevent.h"
 
 class CFFL_InteractiveFormFiller;
-class CFX_SystemHandler;
 class CPDFSDK_ActionHandler;
 class CPDFSDK_AnnotHandlerMgr;
-class CPDFSDK_InterForm;
+class CPDFSDK_InteractiveForm;
 class CPDFSDK_PageView;
 class IJS_Runtime;
 
+// NOTE: |bsUTF16LE| must outlive the use of the result. Care must be taken
+// since modifying the result would impact |bsUTF16LE|.
+FPDF_WIDESTRING AsFPDFWideString(ByteString* bsUTF16LE);
+
 // The CPDFSDK_FormFillEnvironment is "owned" by the embedder across the
 // C API as a FPDF_FormHandle, and may pop out of existence at any time,
 // so long as the associated embedder-owned FPDF_Document outlives it.
@@ -40,65 +41,52 @@
 // hierarcy back to the form fill environment itself, so as to flag any
 // lingering lifetime issues via the memory tools.
 
-class CPDFSDK_FormFillEnvironment
-    : public Observable<CPDFSDK_FormFillEnvironment> {
+class CPDFSDK_FormFillEnvironment final : public Observable,
+                                          public TimerHandlerIface,
+                                          public IPWL_SystemHandler {
  public:
-  CPDFSDK_FormFillEnvironment(UnderlyingDocumentType* pDoc,
-                              FPDF_FORMFILLINFO* pFFinfo);
-  ~CPDFSDK_FormFillEnvironment();
+  CPDFSDK_FormFillEnvironment(
+      CPDF_Document* pDoc,
+      FPDF_FORMFILLINFO* pFFinfo,
+      std::unique_ptr<CPDFSDK_AnnotHandlerMgr> pHandlerMgr);
 
-  static bool IsSHIFTKeyDown(uint32_t nFlag) {
-    return !!(nFlag & FWL_EVENTFLAG_ShiftKey);
-  }
-  static bool IsCTRLKeyDown(uint32_t nFlag) {
-    return !!(nFlag & FWL_EVENTFLAG_ControlKey);
-  }
-  static bool IsALTKeyDown(uint32_t nFlag) {
-    return !!(nFlag & FWL_EVENTFLAG_AltKey);
-  }
+  ~CPDFSDK_FormFillEnvironment() override;
 
-  CPDFSDK_PageView* GetPageView(UnderlyingPageType* pPage, bool renew);
+  // TimerHandlerIface:
+  int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) override;
+  void KillTimer(int32_t nTimerID) override;
+
+  // IPWL_SystemHandler:
+  void InvalidateRect(PerWindowData* pWidgetData,
+                      const CFX_FloatRect& rect) override;
+  void OutputSelectedRect(CFFL_FormFiller* pFormFiller,
+                          const CFX_FloatRect& rect) override;
+  bool IsSelectionImplemented() const override;
+  void SetCursor(int32_t nCursorType) override;
+
+  CPDFSDK_PageView* GetPageView(IPDF_Page* pUnderlyingPage, bool renew);
   CPDFSDK_PageView* GetPageView(int nIndex);
-  CPDFSDK_PageView* GetCurrentView();
-  void RemovePageView(UnderlyingPageType* pPage);
+
+  void RemovePageView(IPDF_Page* pUnderlyingPage);
   void UpdateAllViews(CPDFSDK_PageView* pSender, CPDFSDK_Annot* pAnnot);
 
-  CPDFSDK_Annot* GetFocusAnnot() { return m_pFocusAnnot.Get(); }
-  bool SetFocusAnnot(CPDFSDK_Annot::ObservedPtr* pAnnot);
+  CPDFSDK_Annot* GetFocusAnnot() const { return m_pFocusAnnot.Get(); }
+  bool SetFocusAnnot(ObservedPtr<CPDFSDK_Annot>* pAnnot);
   bool KillFocusAnnot(uint32_t nFlag);
   void ClearAllFocusedAnnots();
 
-  bool ExtractPages(const std::vector<uint16_t>& arrExtraPages,
-                    CPDF_Document* pDstDoc);
-  bool InsertPages(int nInsertAt,
-                   const CPDF_Document* pSrcDoc,
-                   const std::vector<uint16_t>& arrSrcPages);
-  bool ReplacePages(int nPage,
-                    const CPDF_Document* pSrcDoc,
-                    const std::vector<uint16_t>& arrSrcPages);
-
-  int GetPageCount() const { return m_pUnderlyingDoc->GetPageCount(); }
+  int GetPageCount() const;
   bool GetPermissions(int nFlag) const;
 
   bool GetChangeMark() const { return m_bChangeMask; }
   void SetChangeMark() { m_bChangeMask = true; }
   void ClearChangeMark() { m_bChangeMask = false; }
 
-  void ProcJavascriptFun();
+  void ProcJavascriptAction();
   bool ProcOpenAction();
-
-  void Invalidate(UnderlyingPageType* page, const FX_RECT& rect);
-  void OutputSelectedRect(UnderlyingPageType* page, const CFX_FloatRect& rect);
-
-  void SetCursor(int nCursorType);
-  int SetTimer(int uElapse, TimerCallback lpTimerFunc);
-  void KillTimer(int nTimerID);
-  FX_SYSTEMTIME GetLocalTime() const;
+  void Invalidate(IPDF_Page* page, const FX_RECT& rect);
 
   void OnChange();
-
-  FPDF_PAGE GetCurrentPage(UnderlyingDocumentType* document);
-
   void ExecuteNamedAction(const char* namedAction);
   void OnSetFieldInputFocus(FPDF_WIDESTRING focusText,
                             FPDF_DWORD nTextLen,
@@ -109,41 +97,72 @@
                     float* fPosArray,
                     int sizeOfArray);
 
-  UnderlyingDocumentType* GetUnderlyingDocument() const {
-    return m_pUnderlyingDoc.Get();
+  CPDF_Document* GetPDFDocument() const { return m_pCPDFDoc.Get(); }
+  CPDF_Document::Extension* GetDocExtension() const {
+    return m_pCPDFDoc->GetExtension();
   }
 
+  bool IsJSPlatformPresent() const { return m_pInfo && m_pInfo->m_pJsPlatform; }
+
+#ifdef PDF_ENABLE_V8
+  CPDFSDK_PageView* GetCurrentView();
+  FPDF_PAGE GetCurrentPage() const;
+
+  WideString GetLanguage();
+  WideString GetPlatform();
+
+  int JS_appAlert(const WideString& Msg,
+                  const WideString& Title,
+                  int Type,
+                  int Icon);
+  int JS_appResponse(const WideString& Question,
+                     const WideString& Title,
+                     const WideString& Default,
+                     const WideString& cLabel,
+                     FPDF_BOOL bPassword,
+                     void* response,
+                     int length);
+  void JS_appBeep(int nType);
+  WideString JS_fieldBrowse();
+  void JS_docmailForm(void* mailData,
+                      int length,
+                      FPDF_BOOL bUI,
+                      const WideString& To,
+                      const WideString& Subject,
+                      const WideString& CC,
+                      const WideString& BCC,
+                      const WideString& Msg);
+  void JS_docprint(FPDF_BOOL bUI,
+                   int nStart,
+                   int nEnd,
+                   FPDF_BOOL bSilent,
+                   FPDF_BOOL bShrinkToFit,
+                   FPDF_BOOL bPrintAsImage,
+                   FPDF_BOOL bReverse,
+                   FPDF_BOOL bAnnotations);
+  void JS_docgotoPage(int nPageNum);
+  WideString JS_docGetFilePath();
+
 #ifdef PDF_ENABLE_XFA
-  CPDF_Document* GetPDFDocument() const {
-    return m_pUnderlyingDoc ? m_pUnderlyingDoc->GetPDFDoc() : nullptr;
-  }
-
-  CPDFXFA_Context* GetXFAContext() const { return m_pUnderlyingDoc.Get(); }
-  void ResetXFADocument() { m_pUnderlyingDoc = nullptr; }
-
-  int GetPageViewCount() const { return m_PageMap.size(); }
-
-  void DisplayCaret(CPDFXFA_Page* page,
+  int GetPageViewCount() const;
+  void DisplayCaret(IPDF_Page* page,
                     FPDF_BOOL bVisible,
                     double left,
                     double top,
                     double right,
                     double bottom);
-  int GetCurrentPageIndex(CPDFXFA_Context* document);
-  void SetCurrentPage(CPDFXFA_Context* document, int iCurPage);
+  int GetCurrentPageIndex() const;
+  void SetCurrentPage(int iCurPage);
 
   // TODO(dsinclair): This should probably change to PDFium?
   WideString FFI_GetAppName() const { return WideString(L"Acrobat"); }
 
-  WideString GetPlatform();
-  void GotoURL(CPDFXFA_Context* document, const WideStringView& wsURL);
-  void GetPageViewRect(CPDFXFA_Page* page, FS_RECTF& dstRect);
-  bool PopupMenu(CPDFXFA_Page* page,
+  void GotoURL(const WideString& wsURL);
+  FS_RECTF GetPageViewRect(IPDF_Page* page);
+  bool PopupMenu(IPDF_Page* page,
                  FPDF_WIDGET hWidget,
                  int menuFlag,
-                 CFX_PointF pt);
-
-  void Alert(FPDF_WIDESTRING Msg, FPDF_WIDESTRING Title, int Type, int Icon);
+                 const CFX_PointF& pt);
   void EmailTo(FPDF_FILEHANDLER* fileHandler,
                FPDF_WIDESTRING pTo,
                FPDF_WIDESTRING pSubject,
@@ -156,82 +175,49 @@
   FPDF_FILEHANDLER* OpenFile(int fileType,
                              FPDF_WIDESTRING wsURL,
                              const char* mode);
-  RetainPtr<IFX_SeekableReadStream> DownloadFromURL(const wchar_t* url);
-  WideString PostRequestURL(const wchar_t* wsURL,
-                            const wchar_t* wsData,
-                            const wchar_t* wsContentType,
-                            const wchar_t* wsEncode,
-                            const wchar_t* wsHeader);
-  FPDF_BOOL PutRequestURL(const wchar_t* wsURL,
-                          const wchar_t* wsData,
-                          const wchar_t* wsEncode);
-  WideString GetLanguage();
+  RetainPtr<IFX_SeekableReadStream> DownloadFromURL(const WideString& url);
+  WideString PostRequestURL(const WideString& wsURL,
+                            const WideString& wsData,
+                            const WideString& wsContentType,
+                            const WideString& wsEncode,
+                            const WideString& wsHeader);
+  FPDF_BOOL PutRequestURL(const WideString& wsURL,
+                          const WideString& wsData,
+                          const WideString& wsEncode);
 
   void PageEvent(int iPageCount, uint32_t dwEventType) const;
-#else   // PDF_ENABLE_XFA
-  CPDF_Document* GetPDFDocument() const { return m_pUnderlyingDoc.Get(); }
 #endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 
-  int JS_appAlert(const wchar_t* Msg,
-                  const wchar_t* Title,
-                  uint32_t Type,
-                  uint32_t Icon);
-  int JS_appResponse(const wchar_t* Question,
-                     const wchar_t* Title,
-                     const wchar_t* Default,
-                     const wchar_t* cLabel,
-                     FPDF_BOOL bPassword,
-                     void* response,
-                     int length);
-  void JS_appBeep(int nType);
-  WideString JS_fieldBrowse();
-  WideString JS_docGetFilePath();
-  void JS_docSubmitForm(void* formData, int length, const wchar_t* URL);
-  void JS_docmailForm(void* mailData,
-                      int length,
-                      FPDF_BOOL bUI,
-                      const wchar_t* To,
-                      const wchar_t* Subject,
-                      const wchar_t* CC,
-                      const wchar_t* BCC,
-                      const wchar_t* Msg);
-  void JS_docprint(FPDF_BOOL bUI,
-                   int nStart,
-                   int nEnd,
-                   FPDF_BOOL bSilent,
-                   FPDF_BOOL bShrinkToFit,
-                   FPDF_BOOL bPrintAsImage,
-                   FPDF_BOOL bReverse,
-                   FPDF_BOOL bAnnotations);
-  void JS_docgotoPage(int nPageNum);
-
-  bool IsJSInitiated() const { return m_pInfo && m_pInfo->m_pJsPlatform; }
-  ByteString GetAppName() const { return ""; }
-  CFX_SystemHandler* GetSysHandler() const { return m_pSysHandler.get(); }
+  WideString GetFilePath() const;
+  ByteString GetAppName() const { return ByteString(); }
+  TimerHandlerIface* GetTimerHandler() { return this; }
+  IPWL_SystemHandler* GetSysHandler() { return this; }
   FPDF_FORMFILLINFO* GetFormFillInfo() const { return m_pInfo; }
+  void SubmitForm(pdfium::span<uint8_t> form_data, const WideString& URL);
+
+  CPDFSDK_AnnotHandlerMgr* GetAnnotHandlerMgr();  // Always present.
 
   // Creates if not present.
   CFFL_InteractiveFormFiller* GetInteractiveFormFiller();
-  CPDFSDK_AnnotHandlerMgr* GetAnnotHandlerMgr();  // Creates if not present.
-  IJS_Runtime* GetJSRuntime();                    // Creates if not present.
+  IJS_Runtime* GetIJSRuntime();                   // Creates if not present.
   CPDFSDK_ActionHandler* GetActionHandler();      // Creates if not present.
-  CPDFSDK_InterForm* GetInterForm();              // Creates if not present.
+  CPDFSDK_InteractiveForm* GetInteractiveForm();  // Creates if not present.
 
  private:
-  UnderlyingPageType* GetPage(int nIndex);
+  IPDF_Page* GetPage(int nIndex);
 
   FPDF_FORMFILLINFO* const m_pInfo;
-  std::unique_ptr<CPDFSDK_AnnotHandlerMgr> m_pAnnotHandlerMgr;
   std::unique_ptr<CPDFSDK_ActionHandler> m_pActionHandler;
-  std::unique_ptr<IJS_Runtime> m_pJSRuntime;
-  std::map<UnderlyingPageType*, std::unique_ptr<CPDFSDK_PageView>> m_PageMap;
-  std::unique_ptr<CPDFSDK_InterForm> m_pInterForm;
-  CPDFSDK_Annot::ObservedPtr m_pFocusAnnot;
-  UnownedPtr<UnderlyingDocumentType> m_pUnderlyingDoc;
+  std::unique_ptr<IJS_Runtime> m_pIJSRuntime;
+  std::map<IPDF_Page*, std::unique_ptr<CPDFSDK_PageView>> m_PageMap;
+  std::unique_ptr<CPDFSDK_InteractiveForm> m_pInteractiveForm;
+  ObservedPtr<CPDFSDK_Annot> m_pFocusAnnot;
+  UnownedPtr<CPDF_Document> const m_pCPDFDoc;
+  std::unique_ptr<CPDFSDK_AnnotHandlerMgr> m_pAnnotHandlerMgr;
   std::unique_ptr<CFFL_InteractiveFormFiller> m_pFormFiller;
-  std::unique_ptr<CFX_SystemHandler> m_pSysHandler;
-  bool m_bChangeMask;
-  bool m_bBeingDestroyed;
+  bool m_bChangeMask = false;
+  bool m_bBeingDestroyed = false;
 };
 
 #endif  // FPDFSDK_CPDFSDK_FORMFILLENVIRONMENT_H_
diff --git a/fpdfsdk/cpdfsdk_helpers.cpp b/fpdfsdk/cpdfsdk_helpers.cpp
new file mode 100644
index 0000000..144149d
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_helpers.cpp
@@ -0,0 +1,430 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_helpers.h"
+
+#include "build/build_config.h"
+#include "constants/form_fields.h"
+#include "constants/stream_dict_common.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "core/fpdfdoc/cpdf_metadata.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+
+namespace {
+
+constexpr char kQuadPoints[] = "QuadPoints";
+
+// 0 bit: FPDF_POLICY_MACHINETIME_ACCESS
+uint32_t g_sandbox_policy = 0xFFFFFFFF;
+
+UNSUPPORT_INFO* g_unsupport_info = nullptr;
+
+bool RaiseUnsupportedError(int nError) {
+  if (!g_unsupport_info)
+    return false;
+
+  if (g_unsupport_info->FSDK_UnSupport_Handler)
+    g_unsupport_info->FSDK_UnSupport_Handler(g_unsupport_info, nError);
+  return true;
+}
+
+unsigned long GetStreamMaybeCopyAndReturnLengthImpl(const CPDF_Stream* stream,
+                                                    void* buffer,
+                                                    unsigned long buflen,
+                                                    bool decode) {
+  ASSERT(stream);
+  auto stream_acc = pdfium::MakeRetain<CPDF_StreamAcc>(stream);
+
+  if (decode)
+    stream_acc->LoadAllDataFiltered();
+  else
+    stream_acc->LoadAllDataRaw();
+
+  const auto stream_data_size = stream_acc->GetSize();
+  if (!buffer || buflen < stream_data_size)
+    return stream_data_size;
+
+  memcpy(buffer, stream_acc->GetData(), stream_data_size);
+  return stream_data_size;
+}
+
+#ifdef PDF_ENABLE_XFA
+class FPDF_FileHandlerContext final : public IFX_SeekableStream {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  // IFX_SeekableStream:
+  FX_FILESIZE GetSize() override;
+  bool IsEOF() override;
+  FX_FILESIZE GetPosition() override;
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override;
+  size_t ReadBlock(void* buffer, size_t size) override;
+  bool WriteBlockAtOffset(const void* buffer,
+                          FX_FILESIZE offset,
+                          size_t size) override;
+  bool Flush() override;
+
+  void SetPosition(FX_FILESIZE pos) { m_nCurPos = pos; }
+
+ private:
+  explicit FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS);
+  ~FPDF_FileHandlerContext() override;
+
+  FPDF_FILEHANDLER* m_pFS;
+  FX_FILESIZE m_nCurPos;
+};
+
+FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS) {
+  m_pFS = pFS;
+  m_nCurPos = 0;
+}
+
+FPDF_FileHandlerContext::~FPDF_FileHandlerContext() {
+  if (m_pFS && m_pFS->Release)
+    m_pFS->Release(m_pFS->clientData);
+}
+
+FX_FILESIZE FPDF_FileHandlerContext::GetSize() {
+  if (m_pFS && m_pFS->GetSize)
+    return (FX_FILESIZE)m_pFS->GetSize(m_pFS->clientData);
+  return 0;
+}
+
+bool FPDF_FileHandlerContext::IsEOF() {
+  return m_nCurPos >= GetSize();
+}
+
+FX_FILESIZE FPDF_FileHandlerContext::GetPosition() {
+  return m_nCurPos;
+}
+
+bool FPDF_FileHandlerContext::ReadBlockAtOffset(void* buffer,
+                                                FX_FILESIZE offset,
+                                                size_t size) {
+  if (!buffer || !size || !m_pFS->ReadBlock)
+    return false;
+
+  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
+                       (FPDF_DWORD)size) == 0) {
+    m_nCurPos = offset + size;
+    return true;
+  }
+  return false;
+}
+
+size_t FPDF_FileHandlerContext::ReadBlock(void* buffer, size_t size) {
+  if (!buffer || !size || !m_pFS->ReadBlock)
+    return 0;
+
+  FX_FILESIZE nSize = GetSize();
+  if (m_nCurPos >= nSize)
+    return 0;
+  FX_FILESIZE dwAvail = nSize - m_nCurPos;
+  if (dwAvail < (FX_FILESIZE)size)
+    size = static_cast<size_t>(dwAvail);
+  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)m_nCurPos, buffer,
+                       (FPDF_DWORD)size) == 0) {
+    m_nCurPos += size;
+    return size;
+  }
+
+  return 0;
+}
+
+bool FPDF_FileHandlerContext::WriteBlockAtOffset(const void* buffer,
+                                                 FX_FILESIZE offset,
+                                                 size_t size) {
+  if (!m_pFS || !m_pFS->WriteBlock)
+    return false;
+
+  if (m_pFS->WriteBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
+                        (FPDF_DWORD)size) == 0) {
+    m_nCurPos = offset + size;
+    return true;
+  }
+  return false;
+}
+
+bool FPDF_FileHandlerContext::Flush() {
+  if (!m_pFS || !m_pFS->Flush)
+    return true;
+
+  return m_pFS->Flush(m_pFS->clientData) == 0;
+}
+#endif  // PDF_ENABLE_XFA
+
+}  // namespace
+
+IPDF_Page* IPDFPageFromFPDFPage(FPDF_PAGE page) {
+  return reinterpret_cast<IPDF_Page*>(page);
+}
+
+FPDF_PAGE FPDFPageFromIPDFPage(IPDF_Page* page) {
+  return reinterpret_cast<FPDF_PAGE>(page);
+}
+
+CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc) {
+  return reinterpret_cast<CPDF_Document*>(doc);
+}
+
+FPDF_DOCUMENT FPDFDocumentFromCPDFDocument(CPDF_Document* doc) {
+  return reinterpret_cast<FPDF_DOCUMENT>(doc);
+}
+
+CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page) {
+  return page ? IPDFPageFromFPDFPage(page)->AsPDFPage() : nullptr;
+}
+
+CPDFSDK_InteractiveForm* FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  return pFormFillEnv ? pFormFillEnv->GetInteractiveForm() : nullptr;
+}
+
+ByteString ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string) {
+  return WideStringFromFPDFWideString(wide_string).ToUTF8();
+}
+
+WideString WideStringFromFPDFWideString(FPDF_WIDESTRING wide_string) {
+  return WideString::FromUTF16LE(wide_string,
+                                 WideString::WStringLength(wide_string));
+}
+
+#ifdef PDF_ENABLE_XFA
+RetainPtr<IFX_SeekableStream> MakeSeekableStream(
+    FPDF_FILEHANDLER* pFilehandler) {
+  return pdfium::MakeRetain<FPDF_FileHandlerContext>(pFilehandler);
+}
+#endif  // PDF_ENABLE_XFA
+
+const CPDF_Array* GetQuadPointsArrayFromDictionary(
+    const CPDF_Dictionary* dict) {
+  return dict->GetArrayFor("QuadPoints");
+}
+
+CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict) {
+  return dict->GetArrayFor("QuadPoints");
+}
+
+CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) {
+  return dict->SetNewFor<CPDF_Array>(kQuadPoints);
+}
+
+bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index) {
+  return array && index < array->size() / 8;
+}
+
+bool GetQuadPointsAtIndex(const CPDF_Array* array,
+                          size_t quad_index,
+                          FS_QUADPOINTSF* quad_points) {
+  ASSERT(quad_points);
+  ASSERT(array);
+
+  if (!IsValidQuadPointsIndex(array, quad_index))
+    return false;
+
+  quad_index *= 8;
+  quad_points->x1 = array->GetNumberAt(quad_index);
+  quad_points->y1 = array->GetNumberAt(quad_index + 1);
+  quad_points->x2 = array->GetNumberAt(quad_index + 2);
+  quad_points->y2 = array->GetNumberAt(quad_index + 3);
+  quad_points->x3 = array->GetNumberAt(quad_index + 4);
+  quad_points->y3 = array->GetNumberAt(quad_index + 5);
+  quad_points->x4 = array->GetNumberAt(quad_index + 6);
+  quad_points->y4 = array->GetNumberAt(quad_index + 7);
+  return true;
+}
+
+CFX_PointF CFXPointFFromFSPointF(const FS_POINTF& point) {
+  return CFX_PointF(point.x, point.y);
+}
+
+CFX_FloatRect CFXFloatRectFromFSRectF(const FS_RECTF& rect) {
+  return CFX_FloatRect(rect.left, rect.bottom, rect.right, rect.top);
+}
+
+FS_RECTF FSRectFFromCFXFloatRect(const CFX_FloatRect& rect) {
+  return {rect.left, rect.top, rect.right, rect.bottom};
+}
+
+CFX_Matrix CFXMatrixFromFSMatrix(const FS_MATRIX& matrix) {
+  return CFX_Matrix(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f);
+}
+
+FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix) {
+  return {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f};
+}
+
+unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
+                                                  void* buffer,
+                                                  unsigned long buflen) {
+  ByteString encoded_text = text.ToUTF16LE();
+  unsigned long len = encoded_text.GetLength();
+  if (buffer && len <= buflen)
+    memcpy(buffer, encoded_text.c_str(), len);
+  return len;
+}
+
+unsigned long GetRawStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
+                                                   void* buffer,
+                                                   unsigned long buflen) {
+  return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen,
+                                               /*decode=*/false);
+}
+
+unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
+                                                   void* buffer,
+                                                   unsigned long buflen) {
+  return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen,
+                                               /*decode=*/true);
+}
+
+void SetPDFSandboxPolicy(FPDF_DWORD policy, FPDF_BOOL enable) {
+  switch (policy) {
+    case FPDF_POLICY_MACHINETIME_ACCESS: {
+      uint32_t mask = 1 << policy;
+      if (enable)
+        g_sandbox_policy |= mask;
+      else
+        g_sandbox_policy &= ~mask;
+    } break;
+    default:
+      break;
+  }
+}
+
+FPDF_BOOL IsPDFSandboxPolicyEnabled(FPDF_DWORD policy) {
+  switch (policy) {
+    case FPDF_POLICY_MACHINETIME_ACCESS: {
+      uint32_t mask = 1 << policy;
+      return !!(g_sandbox_policy & mask);
+    }
+    default:
+      return false;
+  }
+}
+
+void SetPDFUnsupportInfo(UNSUPPORT_INFO* unsp_info) {
+  g_unsupport_info = unsp_info;
+}
+
+UNSUPPORT_INFO* GetPDFUnssuportInto() {
+  return g_unsupport_info;
+}
+
+void ReportUnsupportedFeatures(CPDF_Document* pDoc) {
+  const CPDF_Dictionary* pRootDict = pDoc->GetRoot();
+  if (pRootDict) {
+    // Portfolios and Packages
+    if (pRootDict->KeyExist("Collection")) {
+      RaiseUnsupportedError(FPDF_UNSP_DOC_PORTABLECOLLECTION);
+      return;
+    }
+    if (pRootDict->KeyExist("Names")) {
+      const CPDF_Dictionary* pNameDict = pRootDict->GetDictFor("Names");
+      if (pNameDict && pNameDict->KeyExist("EmbeddedFiles")) {
+        RaiseUnsupportedError(FPDF_UNSP_DOC_ATTACHMENT);
+        return;
+      }
+      if (pNameDict && pNameDict->KeyExist("JavaScript")) {
+        const CPDF_Dictionary* pJSDict = pNameDict->GetDictFor("JavaScript");
+        const CPDF_Array* pArray =
+            pJSDict ? pJSDict->GetArrayFor("Names") : nullptr;
+        if (pArray) {
+          for (size_t i = 0; i < pArray->size(); i++) {
+            ByteString cbStr = pArray->GetStringAt(i);
+            if (cbStr.Compare("com.adobe.acrobat.SharedReview.Register") == 0) {
+              RaiseUnsupportedError(FPDF_UNSP_DOC_SHAREDREVIEW);
+              return;
+            }
+          }
+        }
+      }
+    }
+
+    // SharedForm
+    const CPDF_Stream* pStream = pRootDict->GetStreamFor("Metadata");
+    if (pStream) {
+      CPDF_Metadata metaData(pStream);
+      for (const auto& err : metaData.CheckForSharedForm())
+        RaiseUnsupportedError(static_cast<int>(err));
+    }
+  }
+}
+
+void ReportUnsupportedXFA(CPDF_Document* pDoc) {
+  // XFA Forms
+  if (!pDoc->GetExtension() && CPDF_InteractiveForm(pDoc).HasXFAForm())
+    RaiseUnsupportedError(FPDF_UNSP_DOC_XFAFORM);
+}
+
+void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot) {
+  switch (pAnnot->GetSubtype()) {
+    case CPDF_Annot::Subtype::FILEATTACHMENT:
+      RaiseUnsupportedError(FPDF_UNSP_ANNOT_ATTACHMENT);
+      break;
+    case CPDF_Annot::Subtype::MOVIE:
+      RaiseUnsupportedError(FPDF_UNSP_ANNOT_MOVIE);
+      break;
+    case CPDF_Annot::Subtype::RICHMEDIA:
+      RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA);
+      break;
+    case CPDF_Annot::Subtype::SCREEN: {
+      const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
+      ByteString cbString = pAnnotDict->GetStringFor("IT");
+      if (cbString != "Img")
+        RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_MEDIA);
+      break;
+    }
+    case CPDF_Annot::Subtype::SOUND:
+      RaiseUnsupportedError(FPDF_UNSP_ANNOT_SOUND);
+      break;
+    case CPDF_Annot::Subtype::THREED:
+      RaiseUnsupportedError(FPDF_UNSP_ANNOT_3DANNOT);
+      break;
+    case CPDF_Annot::Subtype::WIDGET: {
+      const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
+      ByteString cbString = pAnnotDict->GetStringFor(pdfium::form_fields::kFT);
+      if (cbString == pdfium::form_fields::kSig)
+        RaiseUnsupportedError(FPDF_UNSP_ANNOT_SIG);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void ProcessParseError(CPDF_Parser::Error err) {
+  uint32_t err_code = FPDF_ERR_SUCCESS;
+  // Translate FPDFAPI error code to FPDFVIEW error code
+  switch (err) {
+    case CPDF_Parser::SUCCESS:
+      err_code = FPDF_ERR_SUCCESS;
+      break;
+    case CPDF_Parser::FILE_ERROR:
+      err_code = FPDF_ERR_FILE;
+      break;
+    case CPDF_Parser::FORMAT_ERROR:
+      err_code = FPDF_ERR_FORMAT;
+      break;
+    case CPDF_Parser::PASSWORD_ERROR:
+      err_code = FPDF_ERR_PASSWORD;
+      break;
+    case CPDF_Parser::HANDLER_ERROR:
+      err_code = FPDF_ERR_SECURITY;
+      break;
+  }
+  FXSYS_SetLastError(err_code);
+}
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
new file mode 100644
index 0000000..327ec4c
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -0,0 +1,271 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_HELPERS_H_
+#define FPDFSDK_CPDFSDK_HELPERS_H_
+
+#include "build/build_config.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "public/fpdf_doc.h"
+#include "public/fpdf_ext.h"
+#include "public/fpdfview.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "core/fxcrt/fx_stream.h"
+#endif  // PDF_ENABLE_XFA
+
+#if defined(OS_WIN)
+#include <math.h>
+#include <tchar.h>
+#endif
+
+class CPDF_Annot;
+class CPDF_AnnotContext;
+class CPDF_ClipPath;
+class CPDF_ContentMarkItem;
+class CPDF_Object;
+class CPDF_Font;
+class CPDF_LinkExtract;
+class CPDF_PageObject;
+class CPDF_Stream;
+class CPDF_StructElement;
+class CPDF_StructTree;
+class CPDF_TextPage;
+class CPDF_TextPageFind;
+class CPDFSDK_FormFillEnvironment;
+class CPDFSDK_InteractiveForm;
+class FX_PATHPOINT;
+struct CPDF_JavaScript;
+
+// Conversions to/from underlying types.
+IPDF_Page* IPDFPageFromFPDFPage(FPDF_PAGE page);
+FPDF_PAGE FPDFPageFromIPDFPage(IPDF_Page* page);
+CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page);
+FPDF_DOCUMENT FPDFDocumentFromCPDFDocument(CPDF_Document* doc);
+CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc);
+
+// Conversions to/from incomplete FPDF_ API types.
+inline FPDF_ACTION FPDFActionFromCPDFDictionary(const CPDF_Dictionary* action) {
+  return reinterpret_cast<FPDF_ACTION>(const_cast<CPDF_Dictionary*>(action));
+}
+inline CPDF_Dictionary* CPDFDictionaryFromFPDFAction(FPDF_ACTION action) {
+  return reinterpret_cast<CPDF_Dictionary*>(action);
+}
+
+inline FPDF_ANNOTATION FPDFAnnotationFromCPDFAnnotContext(
+    CPDF_AnnotContext* annot) {
+  return reinterpret_cast<FPDF_ANNOTATION>(annot);
+}
+inline CPDF_AnnotContext* CPDFAnnotContextFromFPDFAnnotation(
+    FPDF_ANNOTATION annot) {
+  return reinterpret_cast<CPDF_AnnotContext*>(annot);
+}
+
+inline FPDF_ATTACHMENT FPDFAttachmentFromCPDFObject(CPDF_Object* attachment) {
+  return reinterpret_cast<FPDF_ATTACHMENT>(attachment);
+}
+inline CPDF_Object* CPDFObjectFromFPDFAttachment(FPDF_ATTACHMENT attachment) {
+  return reinterpret_cast<CPDF_Object*>(attachment);
+}
+
+inline FPDF_BITMAP FPDFBitmapFromCFXDIBitmap(CFX_DIBitmap* bitmap) {
+  return reinterpret_cast<FPDF_BITMAP>(bitmap);
+}
+inline CFX_DIBitmap* CFXDIBitmapFromFPDFBitmap(FPDF_BITMAP bitmap) {
+  return reinterpret_cast<CFX_DIBitmap*>(bitmap);
+}
+
+inline FPDF_BOOKMARK FPDFBookmarkFromCPDFDictionary(
+    const CPDF_Dictionary* bookmark) {
+  return reinterpret_cast<FPDF_BOOKMARK>(
+      const_cast<CPDF_Dictionary*>(bookmark));
+}
+inline CPDF_Dictionary* CPDFDictionaryFromFPDFBookmark(FPDF_BOOKMARK bookmark) {
+  return reinterpret_cast<CPDF_Dictionary*>(bookmark);
+}
+
+inline FPDF_CLIPPATH FPDFClipPathFromCPDFClipPath(CPDF_ClipPath* path) {
+  return reinterpret_cast<FPDF_CLIPPATH>(path);
+}
+inline CPDF_ClipPath* CPDFClipPathFromFPDFClipPath(FPDF_CLIPPATH path) {
+  return reinterpret_cast<CPDF_ClipPath*>(path);
+}
+
+inline FPDF_DEST FPDFDestFromCPDFArray(const CPDF_Array* dest) {
+  return reinterpret_cast<FPDF_DEST>(const_cast<CPDF_Array*>(dest));
+}
+inline CPDF_Array* CPDFArrayFromFPDFDest(FPDF_DEST dest) {
+  return reinterpret_cast<CPDF_Array*>(dest);
+}
+
+inline FPDF_FONT FPDFFontFromCPDFFont(CPDF_Font* font) {
+  return reinterpret_cast<FPDF_FONT>(font);
+}
+inline CPDF_Font* CPDFFontFromFPDFFont(FPDF_FONT font) {
+  return reinterpret_cast<CPDF_Font*>(font);
+}
+
+inline FPDF_JAVASCRIPT_ACTION FPDFJavaScriptActionFromCPDFJavaScriptAction(
+    CPDF_JavaScript* javascript) {
+  return reinterpret_cast<FPDF_JAVASCRIPT_ACTION>(javascript);
+}
+inline CPDF_JavaScript* CPDFJavaScriptActionFromFPDFJavaScriptAction(
+    FPDF_JAVASCRIPT_ACTION javascript) {
+  return reinterpret_cast<CPDF_JavaScript*>(javascript);
+}
+
+inline FPDF_LINK FPDFLinkFromCPDFDictionary(CPDF_Dictionary* link) {
+  return reinterpret_cast<FPDF_LINK>(link);
+}
+inline CPDF_Dictionary* CPDFDictionaryFromFPDFLink(FPDF_LINK link) {
+  return reinterpret_cast<CPDF_Dictionary*>(link);
+}
+
+inline FPDF_PAGELINK FPDFPageLinkFromCPDFLinkExtract(CPDF_LinkExtract* link) {
+  return reinterpret_cast<FPDF_PAGELINK>(link);
+}
+inline CPDF_LinkExtract* CPDFLinkExtractFromFPDFPageLink(FPDF_PAGELINK link) {
+  return reinterpret_cast<CPDF_LinkExtract*>(link);
+}
+
+inline FPDF_PAGEOBJECT FPDFPageObjectFromCPDFPageObject(
+    CPDF_PageObject* page_object) {
+  return reinterpret_cast<FPDF_PAGEOBJECT>(page_object);
+}
+inline CPDF_PageObject* CPDFPageObjectFromFPDFPageObject(
+    FPDF_PAGEOBJECT page_object) {
+  return reinterpret_cast<CPDF_PageObject*>(page_object);
+}
+
+inline FPDF_PAGEOBJECTMARK FPDFPageObjectMarkFromCPDFContentMarkItem(
+    CPDF_ContentMarkItem* mark) {
+  return reinterpret_cast<FPDF_PAGEOBJECTMARK>(mark);
+}
+inline CPDF_ContentMarkItem* CPDFContentMarkItemFromFPDFPageObjectMark(
+    FPDF_PAGEOBJECTMARK mark) {
+  return reinterpret_cast<CPDF_ContentMarkItem*>(mark);
+}
+
+inline FPDF_PAGERANGE FPDFPageRangeFromCPDFArray(CPDF_Array* range) {
+  return reinterpret_cast<FPDF_PAGERANGE>(range);
+}
+inline CPDF_Array* CPDFArrayFromFPDFPageRange(FPDF_PAGERANGE range) {
+  return reinterpret_cast<CPDF_Array*>(range);
+}
+
+inline FPDF_PATHSEGMENT FPDFPathSegmentFromFXPathPoint(
+    const FX_PATHPOINT* segment) {
+  return reinterpret_cast<FPDF_PATHSEGMENT>(segment);
+}
+inline const FX_PATHPOINT* FXPathPointFromFPDFPathSegment(
+    FPDF_PATHSEGMENT segment) {
+  return reinterpret_cast<const FX_PATHPOINT*>(segment);
+}
+
+inline FPDF_STRUCTTREE FPDFStructTreeFromCPDFStructTree(
+    CPDF_StructTree* struct_tree) {
+  return reinterpret_cast<FPDF_STRUCTTREE>(struct_tree);
+}
+inline CPDF_StructTree* CPDFStructTreeFromFPDFStructTree(
+    FPDF_STRUCTTREE struct_tree) {
+  return reinterpret_cast<CPDF_StructTree*>(struct_tree);
+}
+
+inline FPDF_STRUCTELEMENT FPDFStructElementFromCPDFStructElement(
+    CPDF_StructElement* struct_element) {
+  return reinterpret_cast<FPDF_STRUCTELEMENT>(struct_element);
+}
+inline CPDF_StructElement* CPDFStructElementFromFPDFStructElement(
+    FPDF_STRUCTELEMENT struct_element) {
+  return reinterpret_cast<CPDF_StructElement*>(struct_element);
+}
+
+inline FPDF_TEXTPAGE FPDFTextPageFromCPDFTextPage(CPDF_TextPage* page) {
+  return reinterpret_cast<FPDF_TEXTPAGE>(page);
+}
+inline CPDF_TextPage* CPDFTextPageFromFPDFTextPage(FPDF_TEXTPAGE page) {
+  return reinterpret_cast<CPDF_TextPage*>(page);
+}
+
+inline FPDF_SCHHANDLE FPDFSchHandleFromCPDFTextPageFind(
+    CPDF_TextPageFind* handle) {
+  return reinterpret_cast<FPDF_SCHHANDLE>(handle);
+}
+inline CPDF_TextPageFind* CPDFTextPageFindFromFPDFSchHandle(
+    FPDF_SCHHANDLE handle) {
+  return reinterpret_cast<CPDF_TextPageFind*>(handle);
+}
+
+inline FPDF_FORMHANDLE FPDFFormHandleFromCPDFSDKFormFillEnvironment(
+    CPDFSDK_FormFillEnvironment* handle) {
+  return reinterpret_cast<FPDF_FORMHANDLE>(handle);
+}
+inline CPDFSDK_FormFillEnvironment*
+CPDFSDKFormFillEnvironmentFromFPDFFormHandle(FPDF_FORMHANDLE handle) {
+  return reinterpret_cast<CPDFSDK_FormFillEnvironment*>(handle);
+}
+
+CPDFSDK_InteractiveForm* FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle);
+
+ByteString ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string);
+WideString WideStringFromFPDFWideString(FPDF_WIDESTRING wide_string);
+
+#ifdef PDF_ENABLE_XFA
+// Layering prevents fxcrt from knowing about FPDF_FILEHANDLER, so this can't
+// be a static method of IFX_SeekableStream.
+RetainPtr<IFX_SeekableStream> MakeSeekableStream(
+    FPDF_FILEHANDLER* pFileHandler);
+#endif  // PDF_ENABLE_XFA
+
+const CPDF_Array* GetQuadPointsArrayFromDictionary(const CPDF_Dictionary* dict);
+CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict);
+CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict);
+bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index);
+bool GetQuadPointsAtIndex(const CPDF_Array* array,
+                          size_t quad_index,
+                          FS_QUADPOINTSF* quad_points);
+
+CFX_PointF CFXPointFFromFSPointF(const FS_POINTF& point);
+
+CFX_FloatRect CFXFloatRectFromFSRectF(const FS_RECTF& rect);
+FS_RECTF FSRectFFromCFXFloatRect(const CFX_FloatRect& rect);
+
+CFX_Matrix CFXMatrixFromFSMatrix(const FS_MATRIX& matrix);
+FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix);
+
+unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
+                                                  void* buffer,
+                                                  unsigned long buflen);
+
+// Returns the length of the raw stream data from |stream|. The raw data is the
+// stream's data as stored in the PDF without applying any filters. If |buffer|
+// is non-nullptr and |buflen| is large enough to contain the raw data, then
+// the raw data is copied into |buffer|.
+unsigned long GetRawStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
+                                                   void* buffer,
+                                                   unsigned long buflen);
+
+// Return the length of the decoded stream data of |stream|. The decoded data is
+// the uncompressed stream data, i.e. the raw stream data after having all
+// filters applied. If |buffer| is non-nullptr and |buflen| is large enough to
+// contain the decoded data, then the decoded data is copied into |buffer|.
+unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
+                                                   void* buffer,
+                                                   unsigned long buflen);
+
+void SetPDFSandboxPolicy(FPDF_DWORD policy, FPDF_BOOL enable);
+FPDF_BOOL IsPDFSandboxPolicyEnabled(FPDF_DWORD policy);
+
+void SetPDFUnsupportInfo(UNSUPPORT_INFO* unsp_info);
+UNSUPPORT_INFO* GetPDFUnssuportInto();
+void ReportUnsupportedFeatures(CPDF_Document* pDoc);
+void ReportUnsupportedXFA(CPDF_Document* pDoc);
+void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot);
+void ProcessParseError(CPDF_Parser::Error err);
+
+#endif  // FPDFSDK_CPDFSDK_HELPERS_H_
diff --git a/fpdfsdk/cpdfsdk_interactiveform.cpp b/fpdfsdk/cpdfsdk_interactiveform.cpp
new file mode 100644
index 0000000..51145b9
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_interactiveform.cpp
@@ -0,0 +1,632 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
+
+#include <algorithm>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "constants/annotation_flags.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#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_stream.h"
+#include "core/fpdfdoc/cpdf_action.h"
+#include "core/fpdfdoc/cpdf_formcontrol.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "core/fxcrt/autorestorer.h"
+#include "core/fxge/cfx_graphstatedata.h"
+#include "core/fxge/cfx_pathdata.h"
+#include "fpdfsdk/cpdfsdk_actionhandler.h"
+#include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "fpdfsdk/formfiller/cffl_formfiller.h"
+#include "fpdfsdk/ipdfsdk_annothandler.h"
+#include "fxjs/ijs_event_context.h"
+#include "fxjs/ijs_runtime.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+constexpr uint32_t kWhiteBGR = FXSYS_BGR(255, 255, 255);
+
+bool IsFormFieldTypeComboOrText(FormFieldType fieldType) {
+  switch (fieldType) {
+    case FormFieldType::kComboBox:
+    case FormFieldType::kTextField:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#ifdef PDF_ENABLE_XFA
+bool IsFormFieldTypeXFA(FormFieldType fieldType) {
+  switch (fieldType) {
+    case FormFieldType::kXFA:
+    case FormFieldType::kXFA_CheckBox:
+    case FormFieldType::kXFA_ComboBox:
+    case FormFieldType::kXFA_ImageField:
+    case FormFieldType::kXFA_ListBox:
+    case FormFieldType::kXFA_PushButton:
+    case FormFieldType::kXFA_Signature:
+    case FormFieldType::kXFA_TextField:
+      return true;
+    default:
+      return false;
+  }
+}
+#endif  // PDF_ENABLE_XFA
+
+bool FDFToURLEncodedData(std::vector<uint8_t>* pBuffer) {
+  std::unique_ptr<CFDF_Document> pFDF = CFDF_Document::ParseMemory(*pBuffer);
+  if (!pFDF)
+    return true;
+
+  CPDF_Dictionary* pMainDict = pFDF->GetRoot()->GetDictFor("FDF");
+  if (!pMainDict)
+    return false;
+
+  CPDF_Array* pFields = pMainDict->GetArrayFor("Fields");
+  if (!pFields)
+    return false;
+
+  std::ostringstream fdfEncodedData;
+  for (uint32_t i = 0; i < pFields->size(); i++) {
+    CPDF_Dictionary* pField = pFields->GetDictAt(i);
+    if (!pField)
+      continue;
+    WideString name;
+    name = pField->GetUnicodeTextFor("T");
+    ByteString name_b = name.ToDefANSI();
+    ByteString csBValue = pField->GetStringFor("V");
+    WideString csWValue = PDF_DecodeText(csBValue.raw_span());
+    ByteString csValue_b = csWValue.ToDefANSI();
+    fdfEncodedData << name_b << "=" << csValue_b;
+    if (i != pFields->size() - 1)
+      fdfEncodedData << "&";
+  }
+
+  size_t nBufSize = fdfEncodedData.tellp();
+  if (nBufSize <= 0)
+    return false;
+
+  pBuffer->resize(nBufSize);
+  memcpy(pBuffer->data(), fdfEncodedData.str().c_str(), nBufSize);
+  return true;
+}
+
+}  // namespace
+
+CPDFSDK_InteractiveForm::CPDFSDK_InteractiveForm(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv)
+    : m_pFormFillEnv(pFormFillEnv),
+      m_pInteractiveForm(pdfium::MakeUnique<CPDF_InteractiveForm>(
+          m_pFormFillEnv->GetPDFDocument())) {
+  m_pInteractiveForm->SetNotifierIface(this);
+  RemoveAllHighLights();
+}
+
+CPDFSDK_InteractiveForm::~CPDFSDK_InteractiveForm() = default;
+
+CPDFSDK_Widget* CPDFSDK_InteractiveForm::GetWidget(
+    CPDF_FormControl* pControl) const {
+  if (!pControl)
+    return nullptr;
+
+  CPDFSDK_Widget* pWidget = nullptr;
+  const auto it = m_Map.find(pControl);
+  if (it != m_Map.end())
+    pWidget = it->second;
+  if (pWidget)
+    return pWidget;
+
+  CPDF_Dictionary* pControlDict = pControl->GetWidget();
+  CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
+  CPDFSDK_PageView* pPage = nullptr;
+
+  if (CPDF_Dictionary* pPageDict = pControlDict->GetDictFor("P")) {
+    int nPageIndex = pDocument->GetPageIndex(pPageDict->GetObjNum());
+    if (nPageIndex >= 0)
+      pPage = m_pFormFillEnv->GetPageView(nPageIndex);
+  }
+
+  if (!pPage) {
+    int nPageIndex = GetPageIndexByAnnotDict(pDocument, pControlDict);
+    if (nPageIndex >= 0)
+      pPage = m_pFormFillEnv->GetPageView(nPageIndex);
+  }
+
+  return pPage ? ToCPDFSDKWidget(pPage->GetAnnotByDict(pControlDict)) : nullptr;
+}
+
+void CPDFSDK_InteractiveForm::GetWidgets(
+    const WideString& sFieldName,
+    std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const {
+  for (int i = 0, sz = m_pInteractiveForm->CountFields(sFieldName); i < sz;
+       ++i) {
+    CPDF_FormField* pFormField = m_pInteractiveForm->GetField(i, sFieldName);
+    ASSERT(pFormField);
+    GetWidgets(pFormField, widgets);
+  }
+}
+
+void CPDFSDK_InteractiveForm::GetWidgets(
+    CPDF_FormField* pField,
+    std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const {
+  for (int i = 0, sz = pField->CountControls(); i < sz; ++i) {
+    CPDF_FormControl* pFormCtrl = pField->GetControl(i);
+    ASSERT(pFormCtrl);
+    CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl);
+    if (pWidget)
+      widgets->emplace_back(pWidget);
+  }
+}
+
+int CPDFSDK_InteractiveForm::GetPageIndexByAnnotDict(
+    CPDF_Document* pDocument,
+    CPDF_Dictionary* pAnnotDict) const {
+  ASSERT(pAnnotDict);
+
+  for (int i = 0, sz = pDocument->GetPageCount(); i < sz; i++) {
+    if (CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(i)) {
+      if (CPDF_Array* pAnnots = pPageDict->GetArrayFor("Annots")) {
+        for (int j = 0, jsz = pAnnots->size(); j < jsz; j++) {
+          CPDF_Object* pDict = pAnnots->GetDirectObjectAt(j);
+          if (pAnnotDict == pDict)
+            return i;
+        }
+      }
+    }
+  }
+
+  return -1;
+}
+
+void CPDFSDK_InteractiveForm::AddMap(CPDF_FormControl* pControl,
+                                     CPDFSDK_Widget* pWidget) {
+  m_Map[pControl] = pWidget;
+}
+
+void CPDFSDK_InteractiveForm::RemoveMap(CPDF_FormControl* pControl) {
+  m_Map.erase(pControl);
+}
+
+void CPDFSDK_InteractiveForm::EnableCalculate(bool bEnabled) {
+  m_bCalculate = bEnabled;
+}
+
+bool CPDFSDK_InteractiveForm::IsCalculateEnabled() const {
+  return m_bCalculate;
+}
+
+#ifdef PDF_ENABLE_XFA
+void CPDFSDK_InteractiveForm::XfaEnableCalculate(bool bEnabled) {
+  m_bXfaCalculate = bEnabled;
+}
+
+bool CPDFSDK_InteractiveForm::IsXfaCalculateEnabled() const {
+  return m_bXfaCalculate;
+}
+
+bool CPDFSDK_InteractiveForm::IsXfaValidationsEnabled() {
+  return m_bXfaValidationsEnabled;
+}
+void CPDFSDK_InteractiveForm::XfaSetValidationsEnabled(bool bEnabled) {
+  m_bXfaValidationsEnabled = bEnabled;
+}
+
+void CPDFSDK_InteractiveForm::SynchronizeField(CPDF_FormField* pFormField) {
+  for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
+    CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
+    if (CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl))
+      pWidget->Synchronize(false);
+  }
+}
+#endif  // PDF_ENABLE_XFA
+
+void CPDFSDK_InteractiveForm::OnCalculate(CPDF_FormField* pFormField) {
+  if (!m_pFormFillEnv->IsJSPlatformPresent())
+    return;
+
+  if (m_bBusy)
+    return;
+
+  AutoRestorer<bool> restorer(&m_bBusy);
+  m_bBusy = true;
+
+  if (!IsCalculateEnabled())
+    return;
+
+  IJS_Runtime* pRuntime = m_pFormFillEnv->GetIJSRuntime();
+  int nSize = m_pInteractiveForm->CountFieldsInCalculationOrder();
+  for (int i = 0; i < nSize; i++) {
+    CPDF_FormField* pField = m_pInteractiveForm->GetFieldInCalculationOrder(i);
+    if (!pField)
+      continue;
+
+    FormFieldType fieldType = pField->GetFieldType();
+    if (!IsFormFieldTypeComboOrText(fieldType))
+      continue;
+
+    CPDF_AAction aAction = pField->GetAdditionalAction();
+    if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kCalculate))
+      continue;
+
+    CPDF_Action action = aAction.GetAction(CPDF_AAction::kCalculate);
+    if (!action.GetDict())
+      continue;
+
+    WideString csJS = action.GetJavaScript();
+    if (csJS.IsEmpty())
+      continue;
+
+    WideString sOldValue = pField->GetValue();
+    WideString sValue = sOldValue;
+    bool bRC = true;
+    IJS_Runtime::ScopedEventContext pContext(pRuntime);
+    pContext->OnField_Calculate(pFormField, pField, &sValue, &bRC);
+
+    Optional<IJS_Runtime::JS_Error> err = pContext->RunScript(csJS);
+    if (!err && bRC && sValue.Compare(sOldValue) != 0)
+      pField->SetValue(sValue, NotificationOption::kNotify);
+  }
+}
+
+Optional<WideString> CPDFSDK_InteractiveForm::OnFormat(
+    CPDF_FormField* pFormField) {
+  if (!m_pFormFillEnv->IsJSPlatformPresent())
+    return {};
+
+  WideString sValue = pFormField->GetValue();
+  IJS_Runtime* pRuntime = m_pFormFillEnv->GetIJSRuntime();
+  if (pFormField->GetFieldType() == FormFieldType::kComboBox &&
+      pFormField->CountSelectedItems() > 0) {
+    int index = pFormField->GetSelectedIndex(0);
+    if (index >= 0)
+      sValue = pFormField->GetOptionLabel(index);
+  }
+
+  CPDF_AAction aAction = pFormField->GetAdditionalAction();
+  if (aAction.GetDict() && aAction.ActionExist(CPDF_AAction::kFormat)) {
+    CPDF_Action action = aAction.GetAction(CPDF_AAction::kFormat);
+    if (action.GetDict()) {
+      WideString script = action.GetJavaScript();
+      if (!script.IsEmpty()) {
+        IJS_Runtime::ScopedEventContext pContext(pRuntime);
+        pContext->OnField_Format(pFormField, &sValue);
+        Optional<IJS_Runtime::JS_Error> err = pContext->RunScript(script);
+        if (!err)
+          return sValue;
+      }
+    }
+  }
+  return {};
+}
+
+void CPDFSDK_InteractiveForm::ResetFieldAppearance(
+    CPDF_FormField* pFormField,
+    Optional<WideString> sValue) {
+  for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
+    CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
+    ASSERT(pFormCtrl);
+    if (CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl))
+      pWidget->ResetAppearance(sValue, true);
+  }
+}
+
+void CPDFSDK_InteractiveForm::UpdateField(CPDF_FormField* pFormField) {
+  auto* formfiller = m_pFormFillEnv->GetInteractiveFormFiller();
+  for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
+    CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
+    ASSERT(pFormCtrl);
+
+    CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl);
+    if (!pWidget)
+      continue;
+
+    IPDF_Page* pPage = pWidget->GetPage();
+    FX_RECT rect = formfiller->GetViewBBox(
+        m_pFormFillEnv->GetPageView(pPage, false), pWidget);
+    m_pFormFillEnv->Invalidate(pPage, rect);
+  }
+}
+
+bool CPDFSDK_InteractiveForm::OnKeyStrokeCommit(CPDF_FormField* pFormField,
+                                                const WideString& csValue) {
+  CPDF_AAction aAction = pFormField->GetAdditionalAction();
+  if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kKeyStroke))
+    return true;
+
+  CPDF_Action action = aAction.GetAction(CPDF_AAction::kKeyStroke);
+  if (!action.GetDict())
+    return true;
+
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = false;
+  fa.bShift = false;
+  fa.sValue = csValue;
+  m_pFormFillEnv->GetActionHandler()->DoAction_FieldJavaScript(
+      action, CPDF_AAction::kKeyStroke, m_pFormFillEnv.Get(), pFormField, &fa);
+  return fa.bRC;
+}
+
+bool CPDFSDK_InteractiveForm::OnValidate(CPDF_FormField* pFormField,
+                                         const WideString& csValue) {
+  CPDF_AAction aAction = pFormField->GetAdditionalAction();
+  if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kValidate))
+    return true;
+
+  CPDF_Action action = aAction.GetAction(CPDF_AAction::kValidate);
+  if (!action.GetDict())
+    return true;
+
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = false;
+  fa.bShift = false;
+  fa.sValue = csValue;
+  m_pFormFillEnv->GetActionHandler()->DoAction_FieldJavaScript(
+      action, CPDF_AAction::kValidate, m_pFormFillEnv.Get(), pFormField, &fa);
+  return fa.bRC;
+}
+
+bool CPDFSDK_InteractiveForm::DoAction_Hide(const CPDF_Action& action) {
+  ASSERT(action.GetDict());
+  std::vector<CPDF_FormField*> fields =
+      GetFieldFromObjects(action.GetAllFields());
+  bool bHide = action.GetHideStatus();
+  bool bChanged = false;
+
+  for (CPDF_FormField* pField : fields) {
+    for (int i = 0, sz = pField->CountControls(); i < sz; ++i) {
+      CPDF_FormControl* pControl = pField->GetControl(i);
+      ASSERT(pControl);
+
+      if (CPDFSDK_Widget* pWidget = GetWidget(pControl)) {
+        uint32_t nFlags = pWidget->GetFlags();
+        nFlags &= ~pdfium::annotation_flags::kInvisible;
+        nFlags &= ~pdfium::annotation_flags::kNoView;
+        if (bHide)
+          nFlags |= pdfium::annotation_flags::kHidden;
+        else
+          nFlags &= ~pdfium::annotation_flags::kHidden;
+        pWidget->SetFlags(nFlags);
+        pWidget->GetPageView()->UpdateView(pWidget);
+        bChanged = true;
+      }
+    }
+  }
+
+  return bChanged;
+}
+
+bool CPDFSDK_InteractiveForm::DoAction_SubmitForm(const CPDF_Action& action) {
+  WideString sDestination = action.GetFilePath();
+  if (sDestination.IsEmpty())
+    return false;
+
+  const CPDF_Dictionary* pActionDict = action.GetDict();
+  if (pActionDict->KeyExist("Fields")) {
+    uint32_t dwFlags = action.GetFlags();
+    std::vector<CPDF_FormField*> fields =
+        GetFieldFromObjects(action.GetAllFields());
+    if (!fields.empty()) {
+      bool bIncludeOrExclude = !(dwFlags & 0x01);
+      if (!m_pInteractiveForm->CheckRequiredFields(&fields, bIncludeOrExclude))
+        return false;
+
+      return SubmitFields(sDestination, fields, bIncludeOrExclude, false);
+    }
+  }
+  if (!m_pInteractiveForm->CheckRequiredFields(nullptr, true))
+    return false;
+
+  return SubmitForm(sDestination, false);
+}
+
+bool CPDFSDK_InteractiveForm::SubmitFields(
+    const WideString& csDestination,
+    const std::vector<CPDF_FormField*>& fields,
+    bool bIncludeOrExclude,
+    bool bUrlEncoded) {
+  ByteString textBuf = ExportFieldsToFDFTextBuf(fields, bIncludeOrExclude);
+  if (textBuf.IsEmpty())
+    return false;
+
+  std::vector<uint8_t> buffer(textBuf.begin(), textBuf.end());
+  if (bUrlEncoded && !FDFToURLEncodedData(&buffer))
+    return false;
+
+  m_pFormFillEnv->SubmitForm(buffer, csDestination);
+  return true;
+}
+
+ByteString CPDFSDK_InteractiveForm::ExportFieldsToFDFTextBuf(
+    const std::vector<CPDF_FormField*>& fields,
+    bool bIncludeOrExclude) {
+  std::unique_ptr<CFDF_Document> pFDF = m_pInteractiveForm->ExportToFDF(
+      m_pFormFillEnv->GetFilePath(), fields, bIncludeOrExclude, false);
+
+  return pFDF ? pFDF->WriteToString() : ByteString();
+}
+
+bool CPDFSDK_InteractiveForm::SubmitForm(const WideString& sDestination,
+                                         bool bUrlEncoded) {
+  if (sDestination.IsEmpty())
+    return false;
+
+  std::unique_ptr<CFDF_Document> pFDFDoc =
+      m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath(), false);
+  if (!pFDFDoc)
+    return false;
+
+  ByteString fdfBuffer = pFDFDoc->WriteToString();
+  if (fdfBuffer.IsEmpty())
+    return false;
+
+  std::vector<uint8_t> buffer(fdfBuffer.begin(), fdfBuffer.end());
+  if (bUrlEncoded && !FDFToURLEncodedData(&buffer))
+    return false;
+
+  m_pFormFillEnv->SubmitForm(buffer, sDestination);
+  return true;
+}
+
+ByteString CPDFSDK_InteractiveForm::ExportFormToFDFTextBuf() {
+  std::unique_ptr<CFDF_Document> pFDF =
+      m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath(), false);
+
+  return pFDF ? pFDF->WriteToString() : ByteString();
+}
+
+void CPDFSDK_InteractiveForm::DoAction_ResetForm(const CPDF_Action& action) {
+  ASSERT(action.GetDict());
+  const CPDF_Dictionary* pActionDict = action.GetDict();
+  if (!pActionDict->KeyExist("Fields")) {
+    m_pInteractiveForm->ResetForm(NotificationOption::kNotify);
+    return;
+  }
+  uint32_t dwFlags = action.GetFlags();
+  std::vector<CPDF_FormField*> fields =
+      GetFieldFromObjects(action.GetAllFields());
+  m_pInteractiveForm->ResetForm(fields, !(dwFlags & 0x01),
+                                NotificationOption::kNotify);
+}
+
+std::vector<CPDF_FormField*> CPDFSDK_InteractiveForm::GetFieldFromObjects(
+    const std::vector<const CPDF_Object*>& objects) const {
+  std::vector<CPDF_FormField*> fields;
+  for (const CPDF_Object* pObject : objects) {
+    if (!pObject || !pObject->IsString())
+      continue;
+
+    WideString csName = pObject->GetUnicodeText();
+    CPDF_FormField* pField = m_pInteractiveForm->GetField(0, csName);
+    if (pField)
+      fields.push_back(pField);
+  }
+  return fields;
+}
+
+bool CPDFSDK_InteractiveForm::BeforeValueChange(CPDF_FormField* pField,
+                                                const WideString& csValue) {
+  FormFieldType fieldType = pField->GetFieldType();
+  if (!IsFormFieldTypeComboOrText(fieldType))
+    return true;
+  if (!OnKeyStrokeCommit(pField, csValue))
+    return false;
+  return OnValidate(pField, csValue);
+}
+
+void CPDFSDK_InteractiveForm::AfterValueChange(CPDF_FormField* pField) {
+#ifdef PDF_ENABLE_XFA
+  SynchronizeField(pField);
+#endif  // PDF_ENABLE_XFA
+
+  FormFieldType fieldType = pField->GetFieldType();
+  if (!IsFormFieldTypeComboOrText(fieldType))
+    return;
+
+  OnCalculate(pField);
+  ResetFieldAppearance(pField, OnFormat(pField));
+  UpdateField(pField);
+}
+
+bool CPDFSDK_InteractiveForm::BeforeSelectionChange(CPDF_FormField* pField,
+                                                    const WideString& csValue) {
+  if (pField->GetFieldType() != FormFieldType::kListBox)
+    return true;
+  if (!OnKeyStrokeCommit(pField, csValue))
+    return false;
+  return OnValidate(pField, csValue);
+}
+
+void CPDFSDK_InteractiveForm::AfterSelectionChange(CPDF_FormField* pField) {
+  if (pField->GetFieldType() != FormFieldType::kListBox)
+    return;
+
+  OnCalculate(pField);
+  ResetFieldAppearance(pField, pdfium::nullopt);
+  UpdateField(pField);
+}
+
+void CPDFSDK_InteractiveForm::AfterCheckedStatusChange(CPDF_FormField* pField) {
+  FormFieldType fieldType = pField->GetFieldType();
+  if (fieldType != FormFieldType::kCheckBox &&
+      fieldType != FormFieldType::kRadioButton)
+    return;
+
+  OnCalculate(pField);
+  UpdateField(pField);
+}
+
+void CPDFSDK_InteractiveForm::AfterFormReset(CPDF_InteractiveForm* pForm) {
+  OnCalculate(nullptr);
+}
+
+bool CPDFSDK_InteractiveForm::IsNeedHighLight(FormFieldType fieldType) const {
+  if (fieldType == FormFieldType::kUnknown)
+    return false;
+
+#ifdef PDF_ENABLE_XFA
+  // For the XFA fields, we need to return if the specific field type has
+  // highlight enabled or if the general XFA field type has it enabled.
+  if (IsFormFieldTypeXFA(fieldType)) {
+    if (!m_NeedsHighlight[static_cast<size_t>(fieldType)])
+      return m_NeedsHighlight[static_cast<size_t>(FormFieldType::kXFA)];
+  }
+#endif  // PDF_ENABLE_XFA
+  return m_NeedsHighlight[static_cast<size_t>(fieldType)];
+}
+
+void CPDFSDK_InteractiveForm::RemoveAllHighLights() {
+  std::fill(m_HighlightColor, m_HighlightColor + kFormFieldTypeCount,
+            kWhiteBGR);
+  std::fill(m_NeedsHighlight, m_NeedsHighlight + kFormFieldTypeCount, false);
+}
+
+void CPDFSDK_InteractiveForm::SetHighlightColor(FX_COLORREF clr,
+                                                FormFieldType fieldType) {
+  if (fieldType == FormFieldType::kUnknown)
+    return;
+
+  m_HighlightColor[static_cast<size_t>(fieldType)] = clr;
+  m_NeedsHighlight[static_cast<size_t>(fieldType)] = true;
+}
+
+void CPDFSDK_InteractiveForm::SetAllHighlightColors(FX_COLORREF clr) {
+  for (size_t i = 0; i < kFormFieldTypeCount; ++i) {
+    m_HighlightColor[i] = clr;
+    m_NeedsHighlight[i] = true;
+  }
+}
+
+FX_COLORREF CPDFSDK_InteractiveForm::GetHighlightColor(
+    FormFieldType fieldType) {
+  if (fieldType == FormFieldType::kUnknown)
+    return kWhiteBGR;
+
+#ifdef PDF_ENABLE_XFA
+  // For the XFA fields, we need to return the specific field type highlight
+  // colour or the general XFA field type colour if present.
+  if (IsFormFieldTypeXFA(fieldType)) {
+    if (!m_NeedsHighlight[static_cast<size_t>(fieldType)] &&
+        m_NeedsHighlight[static_cast<size_t>(FormFieldType::kXFA)]) {
+      return m_HighlightColor[static_cast<size_t>(FormFieldType::kXFA)];
+    }
+  }
+#endif  // PDF_ENABLE_XFA
+  return m_HighlightColor[static_cast<size_t>(fieldType)];
+}
diff --git a/fpdfsdk/cpdfsdk_interactiveform.h b/fpdfsdk/cpdfsdk_interactiveform.h
new file mode 100644
index 0000000..df931d1
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_interactiveform.h
@@ -0,0 +1,121 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_INTERACTIVEFORM_H_
+#define FPDFSDK_CPDFSDK_INTERACTIVEFORM_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fpdfdoc/cpdf_action.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/fx_dib.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
+#include "third_party/base/optional.h"
+
+class CPDF_Dictionary;
+class CPDF_FormControl;
+class CPDF_FormField;
+class CPDF_Object;
+class CPDFSDK_FormFillEnvironment;
+
+class CPDFSDK_InteractiveForm final
+    : public CPDF_InteractiveForm::NotifierIface {
+ public:
+  explicit CPDFSDK_InteractiveForm(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  ~CPDFSDK_InteractiveForm() override;
+
+  CPDF_InteractiveForm* GetInteractiveForm() const {
+    return m_pInteractiveForm.get();
+  }
+  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
+    return m_pFormFillEnv.Get();
+  }
+
+  CPDFSDK_Widget* GetWidget(CPDF_FormControl* pControl) const;
+  void GetWidgets(const WideString& sFieldName,
+                  std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const;
+  void GetWidgets(CPDF_FormField* pField,
+                  std::vector<ObservedPtr<CPDFSDK_Annot>>* widgets) const;
+
+  void AddMap(CPDF_FormControl* pControl, CPDFSDK_Widget* pWidget);
+  void RemoveMap(CPDF_FormControl* pControl);
+
+  void EnableCalculate(bool bEnabled);
+  bool IsCalculateEnabled() const;
+
+#ifdef PDF_ENABLE_XFA
+  void XfaEnableCalculate(bool bEnabled);
+  bool IsXfaCalculateEnabled() const;
+  bool IsXfaValidationsEnabled();
+  void XfaSetValidationsEnabled(bool bEnabled);
+  void SynchronizeField(CPDF_FormField* pFormField);
+#endif  // PDF_ENABLE_XFA
+
+  bool OnKeyStrokeCommit(CPDF_FormField* pFormField, const WideString& csValue);
+  bool OnValidate(CPDF_FormField* pFormField, const WideString& csValue);
+  void OnCalculate(CPDF_FormField* pFormField);
+  Optional<WideString> OnFormat(CPDF_FormField* pFormField);
+
+  void ResetFieldAppearance(CPDF_FormField* pFormField,
+                            Optional<WideString> sValue);
+  void UpdateField(CPDF_FormField* pFormField);
+
+  bool DoAction_Hide(const CPDF_Action& action);
+  bool DoAction_SubmitForm(const CPDF_Action& action);
+  void DoAction_ResetForm(const CPDF_Action& action);
+
+  std::vector<CPDF_FormField*> GetFieldFromObjects(
+      const std::vector<const CPDF_Object*>& objects) const;
+  bool SubmitFields(const WideString& csDestination,
+                    const std::vector<CPDF_FormField*>& fields,
+                    bool bIncludeOrExclude,
+                    bool bUrlEncoded);
+  bool SubmitForm(const WideString& sDestination, bool bUrlEncoded);
+  ByteString ExportFormToFDFTextBuf();
+  ByteString ExportFieldsToFDFTextBuf(
+      const std::vector<CPDF_FormField*>& fields,
+      bool bIncludeOrExclude);
+
+  bool IsNeedHighLight(FormFieldType fieldType) const;
+  void RemoveAllHighLights();
+  void SetHighlightAlpha(uint8_t alpha) { m_HighlightAlpha = alpha; }
+  uint8_t GetHighlightAlpha() { return m_HighlightAlpha; }
+  void SetHighlightColor(FX_COLORREF clr, FormFieldType fieldType);
+  void SetAllHighlightColors(FX_COLORREF clr);
+  FX_COLORREF GetHighlightColor(FormFieldType fieldType);
+
+ private:
+  // CPDF_InteractiveForm::NotifierIface:
+  bool BeforeValueChange(CPDF_FormField* pField,
+                         const WideString& csValue) override;
+  void AfterValueChange(CPDF_FormField* pField) override;
+  bool BeforeSelectionChange(CPDF_FormField* pField,
+                             const WideString& csValue) override;
+  void AfterSelectionChange(CPDF_FormField* pField) override;
+  void AfterCheckedStatusChange(CPDF_FormField* pField) override;
+  void AfterFormReset(CPDF_InteractiveForm* pForm) override;
+
+  int GetPageIndexByAnnotDict(CPDF_Document* pDocument,
+                              CPDF_Dictionary* pAnnotDict) const;
+
+  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
+  std::unique_ptr<CPDF_InteractiveForm> const m_pInteractiveForm;
+  std::map<CPDF_FormControl*, CPDFSDK_Widget*> m_Map;
+#ifdef PDF_ENABLE_XFA
+  bool m_bXfaCalculate = true;
+  bool m_bXfaValidationsEnabled = true;
+#endif  // PDF_ENABLE_XFA
+  bool m_bCalculate = true;
+  bool m_bBusy = false;
+  uint8_t m_HighlightAlpha = 0;
+  FX_COLORREF m_HighlightColor[kFormFieldTypeCount];
+  bool m_NeedsHighlight[kFormFieldTypeCount];
+};
+
+#endif  // FPDFSDK_CPDFSDK_INTERACTIVEFORM_H_
diff --git a/fpdfsdk/cpdfsdk_interform.cpp b/fpdfsdk/cpdfsdk_interform.cpp
deleted file mode 100644
index e19d47b..0000000
--- a/fpdfsdk/cpdfsdk_interform.cpp
+++ /dev/null
@@ -1,759 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_interform.h"
-
-#include <algorithm>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cfdf_document.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfdoc/cpdf_actionfields.h"
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
-#include "fpdfsdk/cba_annotiterator.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cffl_formfiller.h"
-#include "fpdfsdk/fsdk_actionhandler.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fpdfsdk/ipdfsdk_annothandler.h"
-#include "fxjs/ijs_event_context.h"
-#include "fxjs/ijs_runtime.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/cpdfsdk_xfawidget.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
-#include "xfa/fxfa/cxfa_eventparam.h"
-#include "xfa/fxfa/cxfa_ffdocview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#endif  // PDF_ENABLE_XFA
-
-namespace {
-
-bool IsFormFieldTypeComboOrText(FormFieldType fieldType) {
-  switch (fieldType) {
-    case FormFieldType::kComboBox:
-    case FormFieldType::kTextField:
-      return true;
-    default:
-      return false;
-  }
-}
-
-#ifdef PDF_ENABLE_XFA
-bool IsFormFieldTypeXFA(FormFieldType fieldType) {
-  switch (fieldType) {
-    case FormFieldType::kXFA:
-    case FormFieldType::kXFA_CheckBox:
-    case FormFieldType::kXFA_ComboBox:
-    case FormFieldType::kXFA_ImageField:
-    case FormFieldType::kXFA_ListBox:
-    case FormFieldType::kXFA_PushButton:
-    case FormFieldType::kXFA_Signature:
-    case FormFieldType::kXFA_TextField:
-      return true;
-    default:
-      return false;
-  }
-}
-#endif  // PDF_ENABLE_XFA
-
-}  // namespace
-
-CPDFSDK_InterForm::CPDFSDK_InterForm(CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv),
-      m_pInterForm(
-          pdfium::MakeUnique<CPDF_InterForm>(m_pFormFillEnv->GetPDFDocument())),
-#ifdef PDF_ENABLE_XFA
-      m_bXfaCalculate(true),
-      m_bXfaValidationsEnabled(true),
-#endif  // PDF_ENABLE_XFA
-      m_bCalculate(true),
-      m_bBusy(false),
-      m_HighlightAlpha(0) {
-  m_pInterForm->SetFormNotify(this);
-  RemoveAllHighLights();
-}
-
-CPDFSDK_InterForm::~CPDFSDK_InterForm() {
-  m_Map.clear();
-#ifdef PDF_ENABLE_XFA
-  m_XFAMap.clear();
-#endif  // PDF_ENABLE_XFA
-}
-
-bool CPDFSDK_InterForm::HighlightWidgets() {
-  return false;
-}
-
-CPDFSDK_Widget* CPDFSDK_InterForm::GetSibling(CPDFSDK_Widget* pWidget,
-                                              bool bNext) const {
-  auto pIterator = pdfium::MakeUnique<CBA_AnnotIterator>(
-      pWidget->GetPageView(), CPDF_Annot::Subtype::WIDGET);
-
-  if (bNext)
-    return static_cast<CPDFSDK_Widget*>(pIterator->GetNextAnnot(pWidget));
-
-  return static_cast<CPDFSDK_Widget*>(pIterator->GetPrevAnnot(pWidget));
-}
-
-CPDFSDK_Widget* CPDFSDK_InterForm::GetWidget(CPDF_FormControl* pControl) const {
-  if (!pControl || !m_pInterForm)
-    return nullptr;
-
-  CPDFSDK_Widget* pWidget = nullptr;
-  const auto it = m_Map.find(pControl);
-  if (it != m_Map.end())
-    pWidget = it->second;
-  if (pWidget)
-    return pWidget;
-
-  CPDF_Dictionary* pControlDict = pControl->GetWidget();
-  CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
-  CPDFSDK_PageView* pPage = nullptr;
-
-  if (CPDF_Dictionary* pPageDict = pControlDict->GetDictFor("P")) {
-    int nPageIndex = pDocument->GetPageIndex(pPageDict->GetObjNum());
-    if (nPageIndex >= 0)
-      pPage = m_pFormFillEnv->GetPageView(nPageIndex);
-  }
-
-  if (!pPage) {
-    int nPageIndex = GetPageIndexByAnnotDict(pDocument, pControlDict);
-    if (nPageIndex >= 0)
-      pPage = m_pFormFillEnv->GetPageView(nPageIndex);
-  }
-
-  if (!pPage)
-    return nullptr;
-
-  return static_cast<CPDFSDK_Widget*>(pPage->GetAnnotByDict(pControlDict));
-}
-
-void CPDFSDK_InterForm::GetWidgets(
-    const WideString& sFieldName,
-    std::vector<CPDFSDK_Annot::ObservedPtr>* widgets) const {
-  for (int i = 0, sz = m_pInterForm->CountFields(sFieldName); i < sz; ++i) {
-    CPDF_FormField* pFormField = m_pInterForm->GetField(i, sFieldName);
-    ASSERT(pFormField);
-    GetWidgets(pFormField, widgets);
-  }
-}
-
-void CPDFSDK_InterForm::GetWidgets(
-    CPDF_FormField* pField,
-    std::vector<CPDFSDK_Annot::ObservedPtr>* widgets) const {
-  for (int i = 0, sz = pField->CountControls(); i < sz; ++i) {
-    CPDF_FormControl* pFormCtrl = pField->GetControl(i);
-    ASSERT(pFormCtrl);
-    CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl);
-    if (pWidget)
-      widgets->emplace_back(pWidget);
-  }
-}
-
-int CPDFSDK_InterForm::GetPageIndexByAnnotDict(
-    CPDF_Document* pDocument,
-    CPDF_Dictionary* pAnnotDict) const {
-  ASSERT(pAnnotDict);
-
-  for (int i = 0, sz = pDocument->GetPageCount(); i < sz; i++) {
-    if (CPDF_Dictionary* pPageDict = pDocument->GetPage(i)) {
-      if (CPDF_Array* pAnnots = pPageDict->GetArrayFor("Annots")) {
-        for (int j = 0, jsz = pAnnots->GetCount(); j < jsz; j++) {
-          CPDF_Object* pDict = pAnnots->GetDirectObjectAt(j);
-          if (pAnnotDict == pDict)
-            return i;
-        }
-      }
-    }
-  }
-
-  return -1;
-}
-
-void CPDFSDK_InterForm::AddMap(CPDF_FormControl* pControl,
-                               CPDFSDK_Widget* pWidget) {
-  m_Map[pControl] = pWidget;
-}
-
-void CPDFSDK_InterForm::RemoveMap(CPDF_FormControl* pControl) {
-  m_Map.erase(pControl);
-}
-
-void CPDFSDK_InterForm::EnableCalculate(bool bEnabled) {
-  m_bCalculate = bEnabled;
-}
-
-bool CPDFSDK_InterForm::IsCalculateEnabled() const {
-  return m_bCalculate;
-}
-
-#ifdef PDF_ENABLE_XFA
-void CPDFSDK_InterForm::AddXFAMap(CXFA_FFWidget* hWidget,
-                                  CPDFSDK_XFAWidget* pWidget) {
-  ASSERT(hWidget);
-  m_XFAMap[hWidget] = pWidget;
-}
-
-void CPDFSDK_InterForm::RemoveXFAMap(CXFA_FFWidget* hWidget) {
-  ASSERT(hWidget);
-  m_XFAMap.erase(hWidget);
-}
-
-CPDFSDK_XFAWidget* CPDFSDK_InterForm::GetXFAWidget(CXFA_FFWidget* hWidget) {
-  ASSERT(hWidget);
-  auto it = m_XFAMap.find(hWidget);
-  return it != m_XFAMap.end() ? it->second : nullptr;
-}
-
-void CPDFSDK_InterForm::XfaEnableCalculate(bool bEnabled) {
-  m_bXfaCalculate = bEnabled;
-}
-bool CPDFSDK_InterForm::IsXfaCalculateEnabled() const {
-  return m_bXfaCalculate;
-}
-
-bool CPDFSDK_InterForm::IsXfaValidationsEnabled() {
-  return m_bXfaValidationsEnabled;
-}
-void CPDFSDK_InterForm::XfaSetValidationsEnabled(bool bEnabled) {
-  m_bXfaValidationsEnabled = bEnabled;
-}
-
-void CPDFSDK_InterForm::SynchronizeField(CPDF_FormField* pFormField,
-                                         bool bSynchronizeElse) {
-  for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
-    CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
-    if (CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl))
-      pWidget->Synchronize(bSynchronizeElse);
-  }
-}
-#endif  // PDF_ENABLE_XFA
-
-void CPDFSDK_InterForm::OnCalculate(CPDF_FormField* pFormField) {
-  if (!m_pFormFillEnv->IsJSInitiated())
-    return;
-
-  if (m_bBusy)
-    return;
-
-  m_bBusy = true;
-
-  if (!IsCalculateEnabled()) {
-    m_bBusy = false;
-    return;
-  }
-
-  IJS_Runtime* pRuntime = m_pFormFillEnv->GetJSRuntime();
-  int nSize = m_pInterForm->CountFieldsInCalculationOrder();
-  for (int i = 0; i < nSize; i++) {
-    CPDF_FormField* pField = m_pInterForm->GetFieldInCalculationOrder(i);
-    if (!pField)
-      continue;
-
-    FormFieldType fieldType = pField->GetFieldType();
-    if (!IsFormFieldTypeComboOrText(fieldType))
-      continue;
-
-    CPDF_AAction aAction = pField->GetAdditionalAction();
-    if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::Calculate))
-      continue;
-
-    CPDF_Action action = aAction.GetAction(CPDF_AAction::Calculate);
-    if (!action.GetDict())
-      continue;
-
-    WideString csJS = action.GetJavaScript();
-    if (csJS.IsEmpty())
-      continue;
-
-    IJS_EventContext* pContext = pRuntime->NewEventContext();
-    WideString sOldValue = pField->GetValue();
-    WideString sValue = sOldValue;
-    bool bRC = true;
-    pContext->OnField_Calculate(pFormField, pField, sValue, bRC);
-
-    WideString sInfo;
-    bool bRet = pContext->RunScript(csJS, &sInfo);
-    pRuntime->ReleaseEventContext(pContext);
-    if (bRet && bRC && sValue.Compare(sOldValue) != 0)
-      pField->SetValue(sValue, true);
-  }
-  m_bBusy = false;
-}
-
-WideString CPDFSDK_InterForm::OnFormat(CPDF_FormField* pFormField,
-                                       bool& bFormatted) {
-  WideString sValue = pFormField->GetValue();
-  if (!m_pFormFillEnv->IsJSInitiated()) {
-    bFormatted = false;
-    return sValue;
-  }
-
-  IJS_Runtime* pRuntime = m_pFormFillEnv->GetJSRuntime();
-  if (pFormField->GetFieldType() == FormFieldType::kComboBox &&
-      pFormField->CountSelectedItems() > 0) {
-    int index = pFormField->GetSelectedIndex(0);
-    if (index >= 0)
-      sValue = pFormField->GetOptionLabel(index);
-  }
-
-  bFormatted = false;
-
-  CPDF_AAction aAction = pFormField->GetAdditionalAction();
-  if (aAction.GetDict() && aAction.ActionExist(CPDF_AAction::Format)) {
-    CPDF_Action action = aAction.GetAction(CPDF_AAction::Format);
-    if (action.GetDict()) {
-      WideString script = action.GetJavaScript();
-      if (!script.IsEmpty()) {
-        WideString Value = sValue;
-
-        IJS_EventContext* pContext = pRuntime->NewEventContext();
-        pContext->OnField_Format(pFormField, Value, true);
-        WideString sInfo;
-        bool bRet = pContext->RunScript(script, &sInfo);
-        pRuntime->ReleaseEventContext(pContext);
-        if (bRet) {
-          sValue = Value;
-          bFormatted = true;
-        }
-      }
-    }
-  }
-  return sValue;
-}
-
-void CPDFSDK_InterForm::ResetFieldAppearance(CPDF_FormField* pFormField,
-                                             const WideString* sValue,
-                                             bool bValueChanged) {
-  for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
-    CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
-    ASSERT(pFormCtrl);
-    if (CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl))
-      pWidget->ResetAppearance(sValue, bValueChanged);
-  }
-}
-
-void CPDFSDK_InterForm::UpdateField(CPDF_FormField* pFormField) {
-  auto* formfiller = m_pFormFillEnv->GetInteractiveFormFiller();
-  for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
-    CPDF_FormControl* pFormCtrl = pFormField->GetControl(i);
-    ASSERT(pFormCtrl);
-
-    CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl);
-    if (!pWidget)
-      continue;
-
-    UnderlyingPageType* pPage = pWidget->GetUnderlyingPage();
-    FX_RECT rect = formfiller->GetViewBBox(
-        m_pFormFillEnv->GetPageView(pPage, false), pWidget);
-    m_pFormFillEnv->Invalidate(pPage, rect);
-  }
-}
-
-bool CPDFSDK_InterForm::OnKeyStrokeCommit(CPDF_FormField* pFormField,
-                                          const WideString& csValue) {
-  CPDF_AAction aAction = pFormField->GetAdditionalAction();
-  if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::KeyStroke))
-    return true;
-
-  CPDF_Action action = aAction.GetAction(CPDF_AAction::KeyStroke);
-  if (!action.GetDict())
-    return true;
-
-  CPDFSDK_ActionHandler* pActionHandler = m_pFormFillEnv->GetActionHandler();
-  PDFSDK_FieldAction fa;
-  fa.bModifier = false;
-  fa.bShift = false;
-  fa.sValue = csValue;
-  pActionHandler->DoAction_FieldJavaScript(
-      action, CPDF_AAction::KeyStroke, m_pFormFillEnv.Get(), pFormField, fa);
-  return fa.bRC;
-}
-
-bool CPDFSDK_InterForm::OnValidate(CPDF_FormField* pFormField,
-                                   const WideString& csValue) {
-  CPDF_AAction aAction = pFormField->GetAdditionalAction();
-  if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::Validate))
-    return true;
-
-  CPDF_Action action = aAction.GetAction(CPDF_AAction::Validate);
-  if (!action.GetDict())
-    return true;
-
-  CPDFSDK_ActionHandler* pActionHandler = m_pFormFillEnv->GetActionHandler();
-  PDFSDK_FieldAction fa;
-  fa.bModifier = false;
-  fa.bShift = false;
-  fa.sValue = csValue;
-  pActionHandler->DoAction_FieldJavaScript(
-      action, CPDF_AAction::Validate, m_pFormFillEnv.Get(), pFormField, fa);
-  return fa.bRC;
-}
-
-bool CPDFSDK_InterForm::DoAction_Hide(const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  CPDF_ActionFields af(&action);
-  std::vector<CPDF_Object*> fieldObjects = af.GetAllFields();
-  std::vector<CPDF_FormField*> fields = GetFieldFromObjects(fieldObjects);
-
-  bool bHide = action.GetHideStatus();
-  bool bChanged = false;
-
-  for (CPDF_FormField* pField : fields) {
-    for (int i = 0, sz = pField->CountControls(); i < sz; ++i) {
-      CPDF_FormControl* pControl = pField->GetControl(i);
-      ASSERT(pControl);
-
-      if (CPDFSDK_Widget* pWidget = GetWidget(pControl)) {
-        uint32_t nFlags = pWidget->GetFlags();
-        nFlags &= ~ANNOTFLAG_INVISIBLE;
-        nFlags &= ~ANNOTFLAG_NOVIEW;
-        if (bHide)
-          nFlags |= ANNOTFLAG_HIDDEN;
-        else
-          nFlags &= ~ANNOTFLAG_HIDDEN;
-        pWidget->SetFlags(nFlags);
-        pWidget->GetPageView()->UpdateView(pWidget);
-        bChanged = true;
-      }
-    }
-  }
-
-  return bChanged;
-}
-
-bool CPDFSDK_InterForm::DoAction_SubmitForm(const CPDF_Action& action) {
-  WideString sDestination = action.GetFilePath();
-  if (sDestination.IsEmpty())
-    return false;
-
-  CPDF_Dictionary* pActionDict = action.GetDict();
-  if (pActionDict->KeyExist("Fields")) {
-    CPDF_ActionFields af(&action);
-    uint32_t dwFlags = action.GetFlags();
-    std::vector<CPDF_Object*> fieldObjects = af.GetAllFields();
-    std::vector<CPDF_FormField*> fields = GetFieldFromObjects(fieldObjects);
-    if (!fields.empty()) {
-      bool bIncludeOrExclude = !(dwFlags & 0x01);
-      if (!m_pInterForm->CheckRequiredFields(&fields, bIncludeOrExclude))
-        return false;
-
-      return SubmitFields(sDestination, fields, bIncludeOrExclude, false);
-    }
-  }
-  if (!m_pInterForm->CheckRequiredFields(nullptr, true))
-    return false;
-
-  return SubmitForm(sDestination, false);
-}
-
-bool CPDFSDK_InterForm::SubmitFields(const WideString& csDestination,
-                                     const std::vector<CPDF_FormField*>& fields,
-                                     bool bIncludeOrExclude,
-                                     bool bUrlEncoded) {
-  ByteString textBuf = ExportFieldsToFDFTextBuf(fields, bIncludeOrExclude);
-
-  size_t nBufSize = textBuf.GetLength();
-  if (nBufSize == 0)
-    return false;
-
-  uint8_t* pLocalBuffer = FX_Alloc(uint8_t, nBufSize);
-  memcpy(pLocalBuffer, textBuf.c_str(), nBufSize);
-  uint8_t* pBuffer = pLocalBuffer;
-
-  if (bUrlEncoded && !FDFToURLEncodedData(pBuffer, nBufSize)) {
-    FX_Free(pLocalBuffer);
-    return false;
-  }
-
-  m_pFormFillEnv->JS_docSubmitForm(pBuffer, nBufSize, csDestination.c_str());
-
-  if (pBuffer != pLocalBuffer)
-    FX_Free(pBuffer);
-
-  FX_Free(pLocalBuffer);
-
-  return true;
-}
-
-bool CPDFSDK_InterForm::FDFToURLEncodedData(uint8_t*& pBuf, size_t& nBufSize) {
-  std::unique_ptr<CFDF_Document> pFDF =
-      CFDF_Document::ParseMemory(pBuf, nBufSize);
-  if (!pFDF)
-    return true;
-
-  CPDF_Dictionary* pMainDict = pFDF->GetRoot()->GetDictFor("FDF");
-  if (!pMainDict)
-    return false;
-
-  CPDF_Array* pFields = pMainDict->GetArrayFor("Fields");
-  if (!pFields)
-    return false;
-
-  std::ostringstream fdfEncodedData;
-  for (uint32_t i = 0; i < pFields->GetCount(); i++) {
-    CPDF_Dictionary* pField = pFields->GetDictAt(i);
-    if (!pField)
-      continue;
-    WideString name;
-    name = pField->GetUnicodeTextFor("T");
-    ByteString name_b = ByteString::FromUnicode(name);
-    ByteString csBValue = pField->GetStringFor("V");
-    WideString csWValue = PDF_DecodeText(csBValue);
-    ByteString csValue_b = ByteString::FromUnicode(csWValue);
-
-    fdfEncodedData << name_b.GetBuffer(name_b.GetLength());
-    name_b.ReleaseBuffer(name_b.GetStringLength());
-    fdfEncodedData << "=";
-    fdfEncodedData << csValue_b.GetBuffer(csValue_b.GetLength());
-    csValue_b.ReleaseBuffer(csValue_b.GetStringLength());
-    if (i != pFields->GetCount() - 1)
-      fdfEncodedData << "&";
-  }
-
-  nBufSize = fdfEncodedData.tellp();
-  if (nBufSize == 0)
-    return false;
-
-  pBuf = FX_Alloc(uint8_t, nBufSize);
-  memcpy(pBuf, fdfEncodedData.str().c_str(), nBufSize);
-  return true;
-}
-
-ByteString CPDFSDK_InterForm::ExportFieldsToFDFTextBuf(
-    const std::vector<CPDF_FormField*>& fields,
-    bool bIncludeOrExclude) {
-  std::unique_ptr<CFDF_Document> pFDF = m_pInterForm->ExportToFDF(
-      m_pFormFillEnv->JS_docGetFilePath(), fields, bIncludeOrExclude, false);
-
-  return pFDF ? pFDF->WriteToString() : ByteString();
-}
-
-bool CPDFSDK_InterForm::SubmitForm(const WideString& sDestination,
-                                   bool bUrlEncoded) {
-  if (sDestination.IsEmpty())
-    return false;
-
-  if (!m_pFormFillEnv || !m_pInterForm)
-    return false;
-
-  std::unique_ptr<CFDF_Document> pFDFDoc =
-      m_pInterForm->ExportToFDF(m_pFormFillEnv->JS_docGetFilePath(), false);
-  if (!pFDFDoc)
-    return false;
-
-  ByteString fdfBuffer = pFDFDoc->WriteToString();
-
-  if (fdfBuffer.IsEmpty())
-    return false;
-
-  uint8_t* pLocalBuffer = FX_Alloc(uint8_t, fdfBuffer.GetLength());
-  memcpy(pLocalBuffer, fdfBuffer.c_str(), fdfBuffer.GetLength());
-  uint8_t* pBuffer = pLocalBuffer;
-
-  size_t nBufSize = fdfBuffer.GetLength();
-  if (bUrlEncoded && !FDFToURLEncodedData(pBuffer, nBufSize)) {
-    FX_Free(pLocalBuffer);
-    return false;
-  }
-
-  m_pFormFillEnv->JS_docSubmitForm(pBuffer, nBufSize, sDestination.c_str());
-
-  if (pBuffer != pLocalBuffer)
-    FX_Free(pBuffer);
-
-  FX_Free(pLocalBuffer);
-
-  return true;
-}
-
-ByteString CPDFSDK_InterForm::ExportFormToFDFTextBuf() {
-  std::unique_ptr<CFDF_Document> pFDF =
-      m_pInterForm->ExportToFDF(m_pFormFillEnv->JS_docGetFilePath(), false);
-
-  return pFDF ? pFDF->WriteToString() : ByteString();
-}
-
-bool CPDFSDK_InterForm::DoAction_ResetForm(const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  CPDF_Dictionary* pActionDict = action.GetDict();
-  if (!pActionDict->KeyExist("Fields"))
-    return m_pInterForm->ResetForm(true);
-
-  CPDF_ActionFields af(&action);
-  uint32_t dwFlags = action.GetFlags();
-
-  std::vector<CPDF_Object*> fieldObjects = af.GetAllFields();
-  std::vector<CPDF_FormField*> fields = GetFieldFromObjects(fieldObjects);
-  return m_pInterForm->ResetForm(fields, !(dwFlags & 0x01), true);
-}
-
-bool CPDFSDK_InterForm::DoAction_ImportData(const CPDF_Action& action) {
-  return false;
-}
-
-std::vector<CPDF_FormField*> CPDFSDK_InterForm::GetFieldFromObjects(
-    const std::vector<CPDF_Object*>& objects) const {
-  std::vector<CPDF_FormField*> fields;
-  for (CPDF_Object* pObject : objects) {
-    if (pObject && pObject->IsString()) {
-      WideString csName = pObject->GetUnicodeText();
-      CPDF_FormField* pField = m_pInterForm->GetField(0, csName);
-      if (pField)
-        fields.push_back(pField);
-    }
-  }
-  return fields;
-}
-
-int CPDFSDK_InterForm::BeforeValueChange(CPDF_FormField* pField,
-                                         const WideString& csValue) {
-  FormFieldType fieldType = pField->GetFieldType();
-  if (!IsFormFieldTypeComboOrText(fieldType))
-    return 0;
-
-  if (!OnKeyStrokeCommit(pField, csValue))
-    return -1;
-
-  if (!OnValidate(pField, csValue))
-    return -1;
-
-  return 1;
-}
-
-void CPDFSDK_InterForm::AfterValueChange(CPDF_FormField* pField) {
-#ifdef PDF_ENABLE_XFA
-  SynchronizeField(pField, false);
-#endif  // PDF_ENABLE_XFA
-  FormFieldType fieldType = pField->GetFieldType();
-  if (IsFormFieldTypeComboOrText(fieldType)) {
-    OnCalculate(pField);
-    bool bFormatted = false;
-    WideString sValue = OnFormat(pField, bFormatted);
-    ResetFieldAppearance(pField, bFormatted ? &sValue : nullptr, true);
-    UpdateField(pField);
-  }
-}
-
-int CPDFSDK_InterForm::BeforeSelectionChange(CPDF_FormField* pField,
-                                             const WideString& csValue) {
-  if (pField->GetFieldType() != FormFieldType::kListBox)
-    return 0;
-
-  if (!OnKeyStrokeCommit(pField, csValue))
-    return -1;
-
-  if (!OnValidate(pField, csValue))
-    return -1;
-
-  return 1;
-}
-
-void CPDFSDK_InterForm::AfterSelectionChange(CPDF_FormField* pField) {
-  if (pField->GetFieldType() != FormFieldType::kListBox)
-    return;
-
-  OnCalculate(pField);
-  ResetFieldAppearance(pField, nullptr, true);
-  UpdateField(pField);
-}
-
-void CPDFSDK_InterForm::AfterCheckedStatusChange(CPDF_FormField* pField) {
-  FormFieldType fieldType = pField->GetFieldType();
-  if (fieldType != FormFieldType::kCheckBox &&
-      fieldType != FormFieldType::kRadioButton)
-    return;
-
-  OnCalculate(pField);
-  UpdateField(pField);
-}
-
-int CPDFSDK_InterForm::BeforeFormReset(CPDF_InterForm* pForm) {
-  return 0;
-}
-
-void CPDFSDK_InterForm::AfterFormReset(CPDF_InterForm* pForm) {
-  OnCalculate(nullptr);
-}
-
-int CPDFSDK_InterForm::BeforeFormImportData(CPDF_InterForm* pForm) {
-  return 0;
-}
-
-void CPDFSDK_InterForm::AfterFormImportData(CPDF_InterForm* pForm) {
-  OnCalculate(nullptr);
-}
-
-bool CPDFSDK_InterForm::IsNeedHighLight(FormFieldType fieldType) {
-  if (fieldType == FormFieldType::kUnknown)
-    return false;
-
-#ifdef PDF_ENABLE_XFA
-  // For the XFA fields, we need to return if the specific field type has
-  // highlight enabled or if the general XFA field type has it enabled.
-  if (IsFormFieldTypeXFA(fieldType)) {
-    if (!m_NeedsHighlight[static_cast<size_t>(fieldType)])
-      return m_NeedsHighlight[static_cast<size_t>(FormFieldType::kXFA)];
-  }
-#endif  // PDF_ENABLE_XFA
-  return m_NeedsHighlight[static_cast<size_t>(fieldType)];
-}
-
-void CPDFSDK_InterForm::RemoveAllHighLights() {
-  std::fill(m_HighlightColor, m_HighlightColor + kFormFieldTypeCount,
-            FXSYS_RGB(255, 255, 255));
-  std::fill(m_NeedsHighlight, m_NeedsHighlight + kFormFieldTypeCount, false);
-}
-
-void CPDFSDK_InterForm::SetHighlightColor(FX_COLORREF clr,
-                                          FormFieldType fieldType) {
-  if (fieldType == FormFieldType::kUnknown)
-    return;
-
-  m_HighlightColor[static_cast<size_t>(fieldType)] = clr;
-  m_NeedsHighlight[static_cast<size_t>(fieldType)] = true;
-}
-
-void CPDFSDK_InterForm::SetAllHighlightColors(FX_COLORREF clr) {
-  for (auto type : kFormFieldTypes) {
-    m_HighlightColor[static_cast<size_t>(type)] = clr;
-    m_NeedsHighlight[static_cast<size_t>(type)] = true;
-  }
-}
-
-FX_COLORREF CPDFSDK_InterForm::GetHighlightColor(FormFieldType fieldType) {
-  if (fieldType == FormFieldType::kUnknown)
-    return FXSYS_RGB(255, 255, 255);
-
-#ifdef PDF_ENABLE_XFA
-  // For the XFA fields, we need to return the specific field type highlight
-  // colour or the general XFA field type colour if present.
-  if (IsFormFieldTypeXFA(fieldType)) {
-    if (!m_NeedsHighlight[static_cast<size_t>(fieldType)] &&
-        m_NeedsHighlight[static_cast<size_t>(FormFieldType::kXFA)]) {
-      return m_HighlightColor[static_cast<size_t>(FormFieldType::kXFA)];
-    }
-  }
-#endif  // PDF_ENABLE_XFA
-  return m_HighlightColor[static_cast<size_t>(fieldType)];
-}
diff --git a/fpdfsdk/cpdfsdk_interform.h b/fpdfsdk/cpdfsdk_interform.h
deleted file mode 100644
index ee960ff..0000000
--- a/fpdfsdk/cpdfsdk_interform.h
+++ /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
-
-#ifndef FPDFSDK_CPDFSDK_INTERFORM_H_
-#define FPDFSDK_CPDFSDK_INTERFORM_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "core/fpdfdoc/cpdf_action.h"
-#include "core/fpdfdoc/ipdf_formnotify.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/fx_dib.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-
-class CPDF_Dictionary;
-class CPDF_FormControl;
-class CPDF_FormField;
-class CPDF_InterForm;
-class CPDF_Object;
-class CPDFSDK_FormFillEnvironment;
-
-#ifdef PDF_ENABLE_XFA
-class CPDFSDK_XFAWidget;
-class CXFA_FFWidget;
-#endif  // PDF_ENABLE_XFA
-
-class CPDFSDK_InterForm : public IPDF_FormNotify {
- public:
-  explicit CPDFSDK_InterForm(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  ~CPDFSDK_InterForm() override;
-
-  CPDF_InterForm* GetInterForm() const { return m_pInterForm.get(); }
-  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
-    return m_pFormFillEnv.Get();
-  }
-
-  bool HighlightWidgets();
-
-  CPDFSDK_Widget* GetSibling(CPDFSDK_Widget* pWidget, bool bNext) const;
-  CPDFSDK_Widget* GetWidget(CPDF_FormControl* pControl) const;
-  void GetWidgets(const WideString& sFieldName,
-                  std::vector<CPDFSDK_Annot::ObservedPtr>* widgets) const;
-  void GetWidgets(CPDF_FormField* pField,
-                  std::vector<CPDFSDK_Annot::ObservedPtr>* widgets) const;
-
-  void AddMap(CPDF_FormControl* pControl, CPDFSDK_Widget* pWidget);
-  void RemoveMap(CPDF_FormControl* pControl);
-
-  void EnableCalculate(bool bEnabled);
-  bool IsCalculateEnabled() const;
-
-#ifdef PDF_ENABLE_XFA
-  void AddXFAMap(CXFA_FFWidget* hWidget, CPDFSDK_XFAWidget* pWidget);
-  void RemoveXFAMap(CXFA_FFWidget* hWidget);
-  CPDFSDK_XFAWidget* GetXFAWidget(CXFA_FFWidget* hWidget);
-  void XfaEnableCalculate(bool bEnabled);
-  bool IsXfaCalculateEnabled() const;
-  bool IsXfaValidationsEnabled();
-  void XfaSetValidationsEnabled(bool bEnabled);
-  void SynchronizeField(CPDF_FormField* pFormField, bool bSynchronizeElse);
-#endif  // PDF_ENABLE_XFA
-
-  bool OnKeyStrokeCommit(CPDF_FormField* pFormField, const WideString& csValue);
-  bool OnValidate(CPDF_FormField* pFormField, const WideString& csValue);
-  void OnCalculate(CPDF_FormField* pFormField = nullptr);
-  WideString OnFormat(CPDF_FormField* pFormField, bool& bFormatted);
-
-  void ResetFieldAppearance(CPDF_FormField* pFormField,
-                            const WideString* sValue,
-                            bool bValueChanged);
-  void UpdateField(CPDF_FormField* pFormField);
-
-  bool DoAction_Hide(const CPDF_Action& action);
-  bool DoAction_SubmitForm(const CPDF_Action& action);
-  bool DoAction_ResetForm(const CPDF_Action& action);
-  bool DoAction_ImportData(const CPDF_Action& action);
-
-  std::vector<CPDF_FormField*> GetFieldFromObjects(
-      const std::vector<CPDF_Object*>& objects) const;
-  bool IsValidField(CPDF_Dictionary* pFieldDict);
-  bool SubmitFields(const WideString& csDestination,
-                    const std::vector<CPDF_FormField*>& fields,
-                    bool bIncludeOrExclude,
-                    bool bUrlEncoded);
-  bool SubmitForm(const WideString& sDestination, bool bUrlEncoded);
-  ByteString ExportFormToFDFTextBuf();
-  ByteString ExportFieldsToFDFTextBuf(
-      const std::vector<CPDF_FormField*>& fields,
-      bool bIncludeOrExclude);
-
-  bool IsNeedHighLight(FormFieldType fieldType);
-  void RemoveAllHighLights();
-  void SetHighlightAlpha(uint8_t alpha) { m_HighlightAlpha = alpha; }
-  uint8_t GetHighlightAlpha() { return m_HighlightAlpha; }
-  void SetHighlightColor(FX_COLORREF clr, FormFieldType fieldType);
-  void SetAllHighlightColors(FX_COLORREF clr);
-  FX_COLORREF GetHighlightColor(FormFieldType fieldType);
-
- private:
-  // IPDF_FormNotify:
-  int BeforeValueChange(CPDF_FormField* pField,
-                        const WideString& csValue) override;
-  void AfterValueChange(CPDF_FormField* pField) override;
-  int BeforeSelectionChange(CPDF_FormField* pField,
-                            const WideString& csValue) override;
-  void AfterSelectionChange(CPDF_FormField* pField) override;
-  void AfterCheckedStatusChange(CPDF_FormField* pField) override;
-  int BeforeFormReset(CPDF_InterForm* pForm) override;
-  void AfterFormReset(CPDF_InterForm* pForm) override;
-  int BeforeFormImportData(CPDF_InterForm* pForm) override;
-  void AfterFormImportData(CPDF_InterForm* pForm) override;
-
-  bool FDFToURLEncodedData(uint8_t*& pBuf, size_t& nBufSize);
-  int GetPageIndexByAnnotDict(CPDF_Document* pDocument,
-                              CPDF_Dictionary* pAnnotDict) const;
-
-  using CPDFSDK_WidgetMap = std::map<CPDF_FormControl*, CPDFSDK_Widget*>;
-
-  UnownedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
-  std::unique_ptr<CPDF_InterForm> m_pInterForm;
-  CPDFSDK_WidgetMap m_Map;
-#ifdef PDF_ENABLE_XFA
-  std::map<CXFA_FFWidget*, CPDFSDK_XFAWidget*> m_XFAMap;
-  bool m_bXfaCalculate;
-  bool m_bXfaValidationsEnabled;
-#endif  // PDF_ENABLE_XFA
-  bool m_bCalculate;
-  bool m_bBusy;
-
-  uint8_t m_HighlightAlpha;
-  FX_COLORREF m_HighlightColor[kFormFieldTypeCount];
-  bool m_NeedsHighlight[kFormFieldTypeCount];
-};
-
-#endif  // FPDFSDK_CPDFSDK_INTERFORM_H_
diff --git a/fpdfsdk/cpdfsdk_pageview.cpp b/fpdfsdk/cpdfsdk_pageview.cpp
index c1b5221..9e4adc8 100644
--- a/fpdfsdk/cpdfsdk_pageview.cpp
+++ b/fpdfsdk/cpdfsdk_pageview.cpp
@@ -9,106 +9,72 @@
 #include <memory>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_annotlist.h"
-#include "core/fpdfdoc/cpdf_interform.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/autorestorer.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_annotiteration.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#include "xfa/fxfa/cxfa_rendercontext.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 #endif  // PDF_ENABLE_XFA
 
 CPDFSDK_PageView::CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                   UnderlyingPageType* page)
-    : m_page(page),
-      m_pFormFillEnv(pFormFillEnv),
-#ifndef PDF_ENABLE_XFA
-      m_bOwnsPage(false),
-#endif  // PDF_ENABLE_XFA
-      m_bOnWidget(false),
-      m_bValid(false),
-      m_bLocked(false),
-      m_bBeingDestroyed(false) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pPDFInterForm = pInterForm->GetInterForm();
-#ifdef PDF_ENABLE_XFA
-  if (page->GetPDFPage())
-    pPDFInterForm->FixPageFields(page->GetPDFPage());
-#else   // PDF_ENABLE_XFA
-  pPDFInterForm->FixPageFields(page);
-  m_page->SetView(this);
-#endif  // PDF_ENABLE_XFA
+                                   IPDF_Page* page)
+    : m_page(page), m_pFormFillEnv(pFormFillEnv) {
+  ASSERT(m_page);
+  CPDF_Page* pPDFPage = ToPDFPage(page);
+  if (pPDFPage) {
+    CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+    CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+    pPDFForm->FixPageFields(pPDFPage);
+    if (!page->AsXFAPage())
+      pPDFPage->SetView(this);
+  }
 }
 
 CPDFSDK_PageView::~CPDFSDK_PageView() {
-#ifndef PDF_ENABLE_XFA
-  // The call to |ReleaseAnnot| can cause the page pointed to by |m_page| to
-  // be freed, which will cause issues if we try to cleanup the pageview pointer
-  // in |m_page|. So, reset the pageview pointer before doing anything else.
-  m_page->SetView(nullptr);
-#endif  // PDF_ENABLE_XFA
+  if (!m_page->AsXFAPage()) {
+    // The call to |ReleaseAnnot| can cause the page pointed to by |m_page| to
+    // be freed, which will cause issues if we try to cleanup the pageview
+    // pointer in |m_page|. So, reset the pageview pointer before doing anything
+    // else.
+    m_page->AsPDFPage()->SetView(nullptr);
+  }
 
   CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
       m_pFormFillEnv->GetAnnotHandlerMgr();
   for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray)
-    pAnnotHandlerMgr->ReleaseAnnot(pAnnot);
+    pAnnotHandlerMgr->ReleaseAnnot(pdfium::WrapUnique(pAnnot));
 
   m_SDKAnnotArray.clear();
   m_pAnnotList.reset();
-
-#ifndef PDF_ENABLE_XFA
-  if (m_bOwnsPage)
-    delete m_page;
-#endif  // PDF_ENABLE_XFA
 }
 
 void CPDFSDK_PageView::PageView_OnDraw(CFX_RenderDevice* pDevice,
-                                       CFX_Matrix* pUser2Device,
-#ifdef PDF_ENABLE_XFA
+                                       const CFX_Matrix& mtUser2Device,
                                        CPDF_RenderOptions* pOptions,
                                        const FX_RECT& pClip) {
-#else
-                                       CPDF_RenderOptions* pOptions) {
-#endif  // PDF_ENABLE_XFA
-  m_curMatrix = *pUser2Device;
+  m_curMatrix = mtUser2Device;
 
 #ifdef PDF_ENABLE_XFA
-  CPDFXFA_Page* pPage = GetPDFXFAPage();
-  if (!pPage)
-    return;
-
-  if (pPage->GetContext()->GetFormType() == FormType::kXFAFull) {
-    CFX_RectF rectClip(
-        static_cast<float>(pClip.left), static_cast<float>(pClip.top),
-        static_cast<float>(pClip.Width()), static_cast<float>(pClip.Height()));
-
-    CXFA_Graphics gs(pDevice);
-    gs.SetClipRect(rectClip);
-
-    CXFA_FFPageView* xfaView = pPage->GetXFAPageView();
-    CXFA_RenderContext renderContext(xfaView, rectClip, *pUser2Device);
-    renderContext.DoRender(&gs);
-
-    CXFA_FFDocView* docView = xfaView->GetDocView();
-    if (!docView)
-      return;
-    CPDFSDK_Annot* annot = GetFocusAnnot();
-    if (!annot)
-      return;
-    // Render the focus widget
-    docView->GetWidgetHandler()->RenderWidget(annot->GetXFAWidget(), &gs,
-                                              *pUser2Device, false);
+  IPDF_Page* pPage = GetXFAPage();
+  CPDF_Document::Extension* pContext =
+      pPage ? pPage->GetDocument()->GetExtension() : nullptr;
+  if (pContext && pContext->ContainsExtensionFullForm()) {
+    static_cast<CPDFXFA_Page*>(pPage)->DrawFocusAnnot(pDevice, GetFocusAnnot(),
+                                                      mtUser2Device, pClip);
     return;
   }
 #endif  // PDF_ENABLE_XFA
@@ -117,7 +83,7 @@
   CPDFSDK_AnnotIteration annotIteration(this, true);
   for (const auto& pSDKAnnot : annotIteration) {
     m_pFormFillEnv->GetAnnotHandlerMgr()->Annot_OnDraw(
-        this, pSDKAnnot.Get(), pDevice, pUser2Device,
+        this, pSDKAnnot.Get(), pDevice, mtUser2Device,
         pOptions->GetDrawAnnots());
   }
 }
@@ -155,31 +121,31 @@
 
 #ifdef PDF_ENABLE_XFA
 CPDFSDK_Annot* CPDFSDK_PageView::AddAnnot(CXFA_FFWidget* pPDFAnnot) {
-  if (!pPDFAnnot)
-    return nullptr;
-
   CPDFSDK_Annot* pSDKAnnot = GetAnnotByXFAWidget(pPDFAnnot);
   if (pSDKAnnot)
     return pSDKAnnot;
 
   CPDFSDK_AnnotHandlerMgr* pAnnotHandler = m_pFormFillEnv->GetAnnotHandlerMgr();
-  pSDKAnnot = pAnnotHandler->NewAnnot(pPDFAnnot, this);
-  if (!pSDKAnnot)
-    return nullptr;
-
-  m_SDKAnnotArray.push_back(pSDKAnnot);
+  std::unique_ptr<CPDFSDK_Annot> pNewAnnot =
+      pAnnotHandler->NewXFAAnnot(pPDFAnnot, this);
+  ASSERT(pNewAnnot);
+  pSDKAnnot = pNewAnnot.get();
+  // TODO(thestig): See if |m_SDKAnnotArray|, which takes ownership of
+  // |pNewAnnot|, can hold std::unique_ptrs instead of raw pointers.
+  m_SDKAnnotArray.push_back(pNewAnnot.release());
   return pSDKAnnot;
 }
 
 bool CPDFSDK_PageView::DeleteAnnot(CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot)
+  IPDF_Page* pPage = pAnnot->GetXFAPage();
+  if (!pPage)
     return false;
 
-  CPDFXFA_Page* pPage = pAnnot->GetPDFXFAPage();
-  if (!pPage || !pPage->GetContext()->ContainsXFAForm())
+  CPDF_Document::Extension* pContext = pPage->GetDocument()->GetExtension();
+  if (pContext && !pContext->ContainsExtensionForm())
     return false;
 
-  CPDFSDK_Annot::ObservedPtr pObserved(pAnnot);
+  ObservedPtr<CPDFSDK_Annot> pObserved(pAnnot);
   if (GetFocusAnnot() == pAnnot)
     m_pFormFillEnv->KillFocusAnnot(0);  // May invoke JS, invalidating pAnnot.
 
@@ -187,7 +153,7 @@
     CPDFSDK_AnnotHandlerMgr* pAnnotHandler =
         m_pFormFillEnv->GetAnnotHandlerMgr();
     if (pAnnotHandler)
-      pAnnotHandler->ReleaseAnnot(pObserved.Get());
+      pAnnotHandler->ReleaseAnnot(pdfium::WrapUnique(pObserved.Get()));
   }
 
   auto it = std::find(m_SDKAnnotArray.begin(), m_SDKAnnotArray.end(), pAnnot);
@@ -201,22 +167,11 @@
 #endif  // PDF_ENABLE_XFA
 
 CPDF_Document* CPDFSDK_PageView::GetPDFDocument() {
-  if (m_page) {
-#ifdef PDF_ENABLE_XFA
-    return m_page->GetContext()->GetPDFDoc();
-#else   // PDF_ENABLE_XFA
-    return m_page->m_pDocument.Get();
-#endif  // PDF_ENABLE_XFA
-  }
-  return nullptr;
+  return m_page->GetDocument();
 }
 
 CPDF_Page* CPDFSDK_PageView::GetPDFPage() const {
-#ifdef PDF_ENABLE_XFA
-  return m_page ? m_page->GetPDFPage() : nullptr;
-#else   // PDF_ENABLE_XFA
-  return m_page;
-#endif  // PDF_ENABLE_XFA
+  return ToPDFPage(m_page);
 }
 
 CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByDict(CPDF_Dictionary* pDict) {
@@ -233,13 +188,27 @@
     return nullptr;
 
   for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray) {
-    if (pAnnot->GetXFAWidget() == hWidget)
+    if (ToXFAWidget(pAnnot)->GetXFAFFWidget() == hWidget)
       return pAnnot;
   }
   return nullptr;
 }
+
+IPDF_Page* CPDFSDK_PageView::GetXFAPage() {
+  return ToXFAPage(m_page);
+}
 #endif  // PDF_ENABLE_XFA
 
+WideString CPDFSDK_PageView::GetFocusedFormText() {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_GetText(pAnnot);
+  }
+
+  return WideString();
+}
+
 WideString CPDFSDK_PageView::GetSelectedText() {
   if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
     CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
@@ -258,8 +227,44 @@
   }
 }
 
+bool CPDFSDK_PageView::CanUndo() {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_CanUndo(pAnnot);
+  }
+  return false;
+}
+
+bool CPDFSDK_PageView::CanRedo() {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_CanRedo(pAnnot);
+  }
+  return false;
+}
+
+bool CPDFSDK_PageView::Undo() {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_Undo(pAnnot);
+  }
+  return false;
+}
+
+bool CPDFSDK_PageView::Redo() {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_Redo(pAnnot);
+  }
+  return false;
+}
+
 bool CPDFSDK_PageView::OnFocus(const CFX_PointF& point, uint32_t nFlag) {
-  CPDFSDK_Annot::ObservedPtr pAnnot(GetFXWidgetAtPoint(point));
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot) {
     m_pFormFillEnv->KillFocusAnnot(nFlag);
     return false;
@@ -270,7 +275,7 @@
 }
 
 bool CPDFSDK_PageView::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPDFSDK_Annot::ObservedPtr pAnnot(GetFXWidgetAtPoint(point));
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot) {
     m_pFormFillEnv->KillFocusAnnot(nFlag);
     return false;
@@ -288,9 +293,42 @@
   return true;
 }
 
-#ifdef PDF_ENABLE_XFA
+bool CPDFSDK_PageView::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
+  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+      m_pFormFillEnv->GetAnnotHandlerMgr();
+  ObservedPtr<CPDFSDK_Annot> pFXAnnot(GetFXWidgetAtPoint(point));
+  ObservedPtr<CPDFSDK_Annot> pFocusAnnot(GetFocusAnnot());
+  if (pFocusAnnot && pFocusAnnot != pFXAnnot) {
+    // Last focus Annot gets a chance to handle the event.
+    if (pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFocusAnnot, nFlag, point))
+      return true;
+  }
+  return pFXAnnot &&
+         pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFXAnnot, nFlag, point);
+}
+
+bool CPDFSDK_PageView::OnLButtonDblClk(const CFX_PointF& point,
+                                       uint32_t nFlag) {
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
+  if (!pAnnot) {
+    m_pFormFillEnv->KillFocusAnnot(nFlag);
+    return false;
+  }
+
+  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+      m_pFormFillEnv->GetAnnotHandlerMgr();
+  if (!pAnnotHandlerMgr->Annot_OnLButtonDblClk(this, &pAnnot, nFlag, point))
+    return false;
+
+  if (!pAnnot)
+    return false;
+
+  m_pFormFillEnv->SetFocusAnnot(&pAnnot);
+  return true;
+}
+
 bool CPDFSDK_PageView::OnRButtonDown(const CFX_PointF& point, uint32_t nFlag) {
-  CPDFSDK_Annot::ObservedPtr pAnnot(GetFXWidgetAtPoint(point));
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot)
     return false;
 
@@ -307,61 +345,54 @@
 }
 
 bool CPDFSDK_PageView::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) {
-  CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
-      m_pFormFillEnv->GetAnnotHandlerMgr();
-  CPDFSDK_Annot::ObservedPtr pFXAnnot(GetFXWidgetAtPoint(point));
-  if (!pFXAnnot)
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
+  if (!pAnnot)
     return false;
 
-  if (pAnnotHandlerMgr->Annot_OnRButtonUp(this, &pFXAnnot, nFlag, point))
-    m_pFormFillEnv->SetFocusAnnot(&pFXAnnot);
-
-  return true;
-}
-#endif  // PDF_ENABLE_XFA
-
-bool CPDFSDK_PageView::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
   CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
       m_pFormFillEnv->GetAnnotHandlerMgr();
-  CPDFSDK_Annot::ObservedPtr pFXAnnot(GetFXWidgetAtPoint(point));
-  CPDFSDK_Annot::ObservedPtr pFocusAnnot(GetFocusAnnot());
-  if (pFocusAnnot && pFocusAnnot != pFXAnnot) {
-    // Last focus Annot gets a chance to handle the event.
-    if (pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFocusAnnot, nFlag, point))
-      return true;
-  }
-  return pFXAnnot &&
-         pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFXAnnot, nFlag, point);
+  bool ok = pAnnotHandlerMgr->Annot_OnRButtonUp(this, &pAnnot, nFlag, point);
+  if (!pAnnot)
+    return false;
+
+  if (ok)
+    m_pFormFillEnv->SetFocusAnnot(&pAnnot);
+
+  return true;
 }
 
 bool CPDFSDK_PageView::OnMouseMove(const CFX_PointF& point, int nFlag) {
   CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
       m_pFormFillEnv->GetAnnotHandlerMgr();
-  CPDFSDK_Annot::ObservedPtr pFXAnnot(GetFXAnnotAtPoint(point));
+
+  ObservedPtr<CPDFSDK_Annot> pFXAnnot(GetFXAnnotAtPoint(point));
+  ObservedPtr<CPDFSDK_PageView> pThis(this);
 
   if (m_bOnWidget && m_pCaptureWidget != pFXAnnot)
     ExitWidget(pAnnotHandlerMgr, true, nFlag);
 
-  if (pFXAnnot) {
-    if (!m_bOnWidget) {
-      EnterWidget(pAnnotHandlerMgr, &pFXAnnot, nFlag);
+  // ExitWidget() may have invalidated objects.
+  if (!pThis || !pFXAnnot)
+    return false;
 
-      // Annot_OnMouseEnter may have invalidated pFXAnnot.
-      if (!pFXAnnot) {
-        ExitWidget(pAnnotHandlerMgr, false, nFlag);
-        return true;
-      }
+  if (!m_bOnWidget) {
+    EnterWidget(pAnnotHandlerMgr, &pFXAnnot, nFlag);
+
+    // EnterWidget() may have invalidated objects.
+    if (!pThis)
+      return false;
+
+    if (!pFXAnnot) {
+      ExitWidget(pAnnotHandlerMgr, false, nFlag);
+      return true;
     }
-
-    pAnnotHandlerMgr->Annot_OnMouseMove(this, &pFXAnnot, nFlag, point);
-    return true;
   }
-
-  return false;
+  pAnnotHandlerMgr->Annot_OnMouseMove(this, &pFXAnnot, nFlag, point);
+  return true;
 }
 
 void CPDFSDK_PageView::EnterWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr,
-                                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                    uint32_t nFlag) {
   m_bOnWidget = true;
   m_pCaptureWidget.Reset(pAnnot->Get());
@@ -384,7 +415,7 @@
                                     double deltaY,
                                     const CFX_PointF& point,
                                     int nFlag) {
-  CPDFSDK_Annot::ObservedPtr pAnnot(GetFXWidgetAtPoint(point));
+  ObservedPtr<CPDFSDK_Annot> pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot)
     return false;
 
@@ -394,6 +425,29 @@
                                               static_cast<int>(deltaY), point);
 }
 
+bool CPDFSDK_PageView::SetIndexSelected(int index, bool selected) {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    ObservedPtr<CPDFSDK_Annot> pAnnotObserved(pAnnot);
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_SetIndexSelected(&pAnnotObserved, index,
+                                                    selected);
+  }
+
+  return false;
+}
+
+bool CPDFSDK_PageView::IsIndexSelected(int index) {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    ObservedPtr<CPDFSDK_Annot> pAnnotObserved(pAnnot);
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_IsIndexSelected(&pAnnotObserved, index);
+  }
+
+  return false;
+}
+
 bool CPDFSDK_PageView::OnChar(int nChar, uint32_t nFlag) {
   if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
     CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
@@ -425,22 +479,20 @@
   m_bLocked = true;
 
 #ifdef PDF_ENABLE_XFA
-  RetainPtr<CPDFXFA_Page> protector(m_page);
-  if (m_pFormFillEnv->GetXFAContext()->GetFormType() == FormType::kXFAFull) {
-    CXFA_FFPageView* pageView = m_page->GetXFAPageView();
-    std::unique_ptr<IXFA_WidgetIterator> pWidgetHandler(
-        pageView->CreateWidgetIterator(
-            XFA_TRAVERSEWAY_Form,
-            XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable));
-    if (!pWidgetHandler) {
-      return;
-    }
+  RetainPtr<CPDFXFA_Page> protector(ToXFAPage(m_page));
+  CPDF_Document::Extension* pContext = m_pFormFillEnv->GetDocExtension();
+  if (pContext && pContext->ContainsExtensionFullForm()) {
+    CXFA_FFPageView* pageView = protector->GetXFAPageView();
+    std::unique_ptr<IXFA_WidgetIterator> pWidgetHandler =
+        pageView->CreateFormWidgetIterator(XFA_WidgetStatus_Visible |
+                                           XFA_WidgetStatus_Viewable);
 
     while (CXFA_FFWidget* pXFAAnnot = pWidgetHandler->MoveToNext()) {
-      CPDFSDK_Annot* pAnnot = pAnnotHandlerMgr->NewAnnot(pXFAAnnot, this);
-      if (!pAnnot)
-        continue;
-      m_SDKAnnotArray.push_back(pAnnot);
+      std::unique_ptr<CPDFSDK_Annot> pNewAnnot =
+          pAnnotHandlerMgr->NewXFAAnnot(pXFAAnnot, this);
+      ASSERT(pNewAnnot);
+      CPDFSDK_Annot* pAnnot = pNewAnnot.get();
+      m_SDKAnnotArray.push_back(pNewAnnot.release());
       pAnnotHandlerMgr->Annot_OnLoad(pAnnot);
     }
 
@@ -450,16 +502,16 @@
 
   CPDF_Page* pPage = GetPDFPage();
   ASSERT(pPage);
-  bool bUpdateAP = CPDF_InterForm::IsUpdateAPEnabled();
+  bool bUpdateAP = CPDF_InteractiveForm::IsUpdateAPEnabled();
   // Disable the default AP construction.
-  CPDF_InterForm::SetUpdateAP(false);
+  CPDF_InteractiveForm::SetUpdateAP(false);
   m_pAnnotList = pdfium::MakeUnique<CPDF_AnnotList>(pPage);
-  CPDF_InterForm::SetUpdateAP(bUpdateAP);
+  CPDF_InteractiveForm::SetUpdateAP(bUpdateAP);
 
   const size_t nCount = m_pAnnotList->Count();
   for (size_t i = 0; i < nCount; ++i) {
     CPDF_Annot* pPDFAnnot = m_pAnnotList->GetAt(i);
-    CheckUnSupportAnnot(GetPDFDocument(), pPDFAnnot);
+    CheckForUnsupportedAnnot(pPDFAnnot);
     CPDFSDK_Annot* pAnnot = pAnnotHandlerMgr->NewAnnot(pPDFAnnot, this);
     if (!pAnnot)
       continue;
@@ -479,19 +531,11 @@
 }
 
 int CPDFSDK_PageView::GetPageIndex() const {
-  if (!m_page)
-    return -1;
-
 #ifdef PDF_ENABLE_XFA
-  switch (m_page->GetContext()->GetFormType()) {
-    case FormType::kXFAFull: {
-      CXFA_FFPageView* pPageView = m_page->GetXFAPageView();
-      return pPageView ? pPageView->GetPageIndex() : -1;
-    }
-    case FormType::kNone:
-    case FormType::kAcroForm:
-    case FormType::kXFAForeground:
-      break;
+  CPDF_Document::Extension* pContext = m_page->GetDocument()->GetExtension();
+  if (pContext && pContext->ContainsExtensionFullForm()) {
+    CXFA_FFPageView* pPageView = m_page->AsXFAPage()->GetXFAPageView();
+    return pPageView ? pPageView->GetLayoutItem()->GetPageIndex() : -1;
   }
 #endif  // PDF_ENABLE_XFA
   return GetPageIndexForStaticPDF();
@@ -521,7 +565,7 @@
 }
 
 int CPDFSDK_PageView::GetPageIndexForStaticPDF() const {
-  CPDF_Dictionary* pDict = GetPDFPage()->m_pFormDict.Get();
+  const CPDF_Dictionary* pDict = GetPDFPage()->GetDict();
   CPDF_Document* pDoc = m_pFormFillEnv->GetPDFDocument();
-  return (pDoc && pDict) ? pDoc->GetPageIndex(pDict->GetObjNum()) : -1;
+  return pDoc->GetPageIndex(pDict->GetObjNum());
 }
diff --git a/fpdfsdk/cpdfsdk_pageview.h b/fpdfsdk/cpdfsdk_pageview.h
index 03f3ab6..9b040db 100644
--- a/fpdfsdk/cpdfsdk_pageview.h
+++ b/fpdfsdk/cpdfsdk_pageview.h
@@ -19,23 +19,17 @@
 class CFX_RenderDevice;
 class CPDF_AnnotList;
 class CPDF_RenderOptions;
+class CPDFSDK_FormFillEnvironment;
 
 class CPDFSDK_PageView final : public CPDF_Page::View {
  public:
-  CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   UnderlyingPageType* page);
+  CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv, IPDF_Page* page);
   ~CPDFSDK_PageView();
 
-#ifdef PDF_ENABLE_XFA
   void PageView_OnDraw(CFX_RenderDevice* pDevice,
-                       CFX_Matrix* pUser2Device,
+                       const CFX_Matrix& mtUser2Device,
                        CPDF_RenderOptions* pOptions,
                        const FX_RECT& pClip);
-#else   // PDF_ENABLE_XFA
-  void PageView_OnDraw(CFX_RenderDevice* pDevice,
-                       CFX_Matrix* pUser2Device,
-                       CPDF_RenderOptions* pOptions);
-#endif  // PDF_ENABLE_XFA
 
   void LoadFXAnnots();
   CPDFSDK_Annot* GetFocusAnnot();
@@ -51,8 +45,7 @@
   bool DeleteAnnot(CPDFSDK_Annot* pAnnot);
   CPDFSDK_Annot* AddAnnot(CXFA_FFWidget* pPDFAnnot);
   CPDFSDK_Annot* GetAnnotByXFAWidget(CXFA_FFWidget* hWidget);
-
-  CPDFXFA_Page* GetPDFXFAPage() { return m_page; }
+  IPDF_Page* GetXFAPage();
 #endif  // PDF_ENABLE_XFA
 
   CPDF_Page* GetPDFPage() const;
@@ -61,44 +54,45 @@
     return m_pFormFillEnv.Get();
   }
 
+  WideString GetFocusedFormText();
   WideString GetSelectedText();
   void ReplaceSelection(const WideString& text);
 
+  bool CanUndo();
+  bool CanRedo();
+  bool Undo();
+  bool Redo();
+
   bool OnFocus(const CFX_PointF& point, uint32_t nFlag);
   bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag);
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag);
-#ifdef PDF_ENABLE_XFA
+  bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag);
   bool OnRButtonDown(const CFX_PointF& point, uint32_t nFlag);
   bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag);
-#endif  // PDF_ENABLE_XFA
   bool OnChar(int nChar, uint32_t nFlag);
   bool OnKeyDown(int nKeyCode, int nFlag);
   bool OnKeyUp(int nKeyCode, int nFlag);
-
   bool OnMouseMove(const CFX_PointF& point, int nFlag);
   bool OnMouseWheel(double deltaX,
                     double deltaY,
                     const CFX_PointF& point,
                     int nFlag);
 
-  void GetCurrentMatrix(CFX_Matrix& matrix) { matrix = m_curMatrix; }
+  bool SetIndexSelected(int index, bool selected);
+  bool IsIndexSelected(int index);
+
+  const CFX_Matrix& GetCurrentMatrix() const { return m_curMatrix; }
   void UpdateRects(const std::vector<CFX_FloatRect>& rects);
   void UpdateView(CPDFSDK_Annot* pAnnot);
 
   int GetPageIndex() const;
 
   void SetValid(bool bValid) { m_bValid = bValid; }
-  bool IsValid() { return m_bValid; }
-
-  bool IsLocked() { return m_bLocked; }
-
+  bool IsValid() const { return m_bValid; }
+  bool IsLocked() const { return m_bLocked; }
   void SetBeingDestroyed() { m_bBeingDestroyed = true; }
   bool IsBeingDestroyed() const { return m_bBeingDestroyed; }
-
-#ifndef PDF_ENABLE_XFA
-  bool OwnsPage() const { return m_bOwnsPage; }
-  void TakePageOwnership() { m_bOwnsPage = true; }
-#endif  // PDF_ENABLE_XFA
+  void TakePageOwnership() { m_pOwnsPage.Reset(ToPDFPage(m_page)); }
 
  private:
   CPDFSDK_Annot* GetFXAnnotAtPoint(const CFX_PointF& point);
@@ -107,25 +101,23 @@
   int GetPageIndexForStaticPDF() const;
 
   void EnterWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlag);
   void ExitWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr,
                   bool callExitCallback,
                   uint32_t nFlag);
 
   CFX_Matrix m_curMatrix;
-  UnderlyingPageType* const m_page;
+  IPDF_Page* const m_page;
   std::unique_ptr<CPDF_AnnotList> m_pAnnotList;
   std::vector<CPDFSDK_Annot*> m_SDKAnnotArray;
   UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-  CPDFSDK_Annot::ObservedPtr m_pCaptureWidget;
-#ifndef PDF_ENABLE_XFA
-  bool m_bOwnsPage;
-#endif  // PDF_ENABLE_XFA
-  bool m_bOnWidget;
-  bool m_bValid;
-  bool m_bLocked;
-  bool m_bBeingDestroyed;
+  ObservedPtr<CPDFSDK_Annot> m_pCaptureWidget;
+  RetainPtr<CPDF_Page> m_pOwnsPage;
+  bool m_bOnWidget = false;
+  bool m_bValid = false;
+  bool m_bLocked = false;
+  bool m_bBeingDestroyed = false;
 };
 
 #endif  // FPDFSDK_CPDFSDK_PAGEVIEW_H_
diff --git a/fpdfsdk/cpdfsdk_pauseadapter.cpp b/fpdfsdk/cpdfsdk_pauseadapter.cpp
new file mode 100644
index 0000000..bf3f1c7
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_pauseadapter.cpp
@@ -0,0 +1,16 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_pauseadapter.h"
+
+CPDFSDK_PauseAdapter::CPDFSDK_PauseAdapter(IFSDK_PAUSE* IPause)
+    : m_IPause(IPause) {}
+
+CPDFSDK_PauseAdapter::~CPDFSDK_PauseAdapter() = default;
+
+bool CPDFSDK_PauseAdapter::NeedToPauseNow() {
+  return m_IPause->NeedToPauseNow && m_IPause->NeedToPauseNow(m_IPause.Get());
+}
diff --git a/fpdfsdk/cpdfsdk_pauseadapter.h b/fpdfsdk/cpdfsdk_pauseadapter.h
new file mode 100644
index 0000000..dfbc2b4
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_pauseadapter.h
@@ -0,0 +1,26 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_PAUSEADAPTER_H_
+#define FPDFSDK_CPDFSDK_PAUSEADAPTER_H_
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/pauseindicator_iface.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "public/fpdf_progressive.h"
+
+class CPDFSDK_PauseAdapter final : public PauseIndicatorIface {
+ public:
+  explicit CPDFSDK_PauseAdapter(IFSDK_PAUSE* IPause);
+  ~CPDFSDK_PauseAdapter() override;
+
+  bool NeedToPauseNow() override;
+
+ private:
+  UnownedPtr<IFSDK_PAUSE> const m_IPause;
+};
+
+#endif  // FPDFSDK_CPDFSDK_PAUSEADAPTER_H_
diff --git a/fpdfsdk/cpdfsdk_renderpage.cpp b/fpdfsdk/cpdfsdk_renderpage.cpp
new file mode 100644
index 0000000..ce492e6
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_renderpage.cpp
@@ -0,0 +1,102 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/cpdfsdk_renderpage.h"
+
+#include <utility>
+
+#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfapi/render/cpdf_pagerendercontext.h"
+#include "core/fpdfapi/render/cpdf_progressiverenderer.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fpdfdoc/cpdf_annotlist.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "fpdfsdk/cpdfsdk_pauseadapter.h"
+#include "public/fpdfview.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+void RenderPageImpl(CPDF_PageRenderContext* pContext,
+                    CPDF_Page* pPage,
+                    const CFX_Matrix& matrix,
+                    const FX_RECT& clipping_rect,
+                    int flags,
+                    bool need_to_restore,
+                    CPDFSDK_PauseAdapter* pause) {
+  if (!pContext->m_pOptions)
+    pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
+
+  auto& options = pContext->m_pOptions->GetOptions();
+  options.bClearType = !!(flags & FPDF_LCD_TEXT);
+  options.bNoNativeText = !!(flags & FPDF_NO_NATIVETEXT);
+  options.bLimitedImageCache = !!(flags & FPDF_RENDER_LIMITEDIMAGECACHE);
+  options.bForceHalftone = !!(flags & FPDF_RENDER_FORCEHALFTONE);
+  options.bNoTextSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHTEXT);
+  options.bNoImageSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHIMAGE);
+  options.bNoPathSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHPATH);
+
+  // Grayscale output
+  if (flags & FPDF_GRAYSCALE)
+    pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kGray);
+
+  const CPDF_OCContext::UsageType usage =
+      (flags & FPDF_PRINTING) ? CPDF_OCContext::Print : CPDF_OCContext::View;
+  pContext->m_pOptions->SetOCContext(
+      pdfium::MakeRetain<CPDF_OCContext>(pPage->GetDocument(), usage));
+
+  pContext->m_pDevice->SaveState();
+  pContext->m_pDevice->SetBaseClip(clipping_rect);
+  pContext->m_pDevice->SetClip_Rect(clipping_rect);
+  pContext->m_pContext = pdfium::MakeUnique<CPDF_RenderContext>(
+      pPage->GetDocument(), pPage->m_pPageResources.Get(),
+      static_cast<CPDF_PageRenderCache*>(pPage->GetRenderCache()));
+
+  pContext->m_pContext->AppendLayer(pPage, &matrix);
+
+  if (flags & FPDF_ANNOT) {
+    auto pOwnedList = pdfium::MakeUnique<CPDF_AnnotList>(pPage);
+    CPDF_AnnotList* pList = pOwnedList.get();
+    pContext->m_pAnnots = std::move(pOwnedList);
+    bool bPrinting =
+        pContext->m_pDevice->GetDeviceType() != DeviceType::kDisplay;
+    pList->DisplayAnnots(pPage, pContext->m_pContext.get(), bPrinting, &matrix,
+                         false, nullptr);
+  }
+
+  pContext->m_pRenderer = pdfium::MakeUnique<CPDF_ProgressiveRenderer>(
+      pContext->m_pContext.get(), pContext->m_pDevice.get(),
+      pContext->m_pOptions.get());
+  pContext->m_pRenderer->Start(pause);
+  if (need_to_restore)
+    pContext->m_pDevice->RestoreState(false);
+}
+
+}  // namespace
+
+void CPDFSDK_RenderPage(CPDF_PageRenderContext* pContext,
+                        CPDF_Page* pPage,
+                        const CFX_Matrix& matrix,
+                        const FX_RECT& clipping_rect,
+                        int flags) {
+  RenderPageImpl(pContext, pPage, matrix, clipping_rect, flags,
+                 /*need_to_restore=*/true, /*pause=*/nullptr);
+}
+
+void CPDFSDK_RenderPageWithContext(CPDF_PageRenderContext* pContext,
+                                   CPDF_Page* pPage,
+                                   int start_x,
+                                   int start_y,
+                                   int size_x,
+                                   int size_y,
+                                   int rotate,
+                                   int flags,
+                                   bool need_to_restore,
+                                   CPDFSDK_PauseAdapter* pause) {
+  const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
+  RenderPageImpl(pContext, pPage, pPage->GetDisplayMatrix(rect, rotate), rect,
+                 flags, need_to_restore, pause);
+}
diff --git a/fpdfsdk/cpdfsdk_renderpage.h b/fpdfsdk/cpdfsdk_renderpage.h
new file mode 100644
index 0000000..cb7a600
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_renderpage.h
@@ -0,0 +1,35 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_CPDFSDK_RENDERPAGE_H_
+#define FPDFSDK_CPDFSDK_RENDERPAGE_H_
+
+class CFX_Matrix;
+class CPDFSDK_PauseAdapter;
+class CPDF_Page;
+class CPDF_PageRenderContext;
+struct FX_RECT;
+
+void CPDFSDK_RenderPage(CPDF_PageRenderContext* pContext,
+                        CPDF_Page* pPage,
+                        const CFX_Matrix& matrix,
+                        const FX_RECT& clipping_rect,
+                        int flags);
+
+// TODO(thestig): Consider giving this a better name, and make its parameters
+// more similar to those of CPDFSDK_RenderPage().
+void CPDFSDK_RenderPageWithContext(CPDF_PageRenderContext* pContext,
+                                   CPDF_Page* pPage,
+                                   int start_x,
+                                   int start_y,
+                                   int size_x,
+                                   int size_y,
+                                   int rotate,
+                                   int flags,
+                                   bool need_to_restore,
+                                   CPDFSDK_PauseAdapter* pause);
+
+#endif  // FPDFSDK_CPDFSDK_RENDERPAGE_H_
diff --git a/fpdfsdk/cpdfsdk_widget.cpp b/fpdfsdk/cpdfsdk_widget.cpp
index 2adc46f..6d92b74 100644
--- a/fpdfsdk/cpdfsdk_widget.cpp
+++ b/fpdfsdk/cpdfsdk_widget.cpp
@@ -9,27 +9,28 @@
 #include <memory>
 #include <sstream>
 
+#include "constants/annotation_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_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfdoc/cba_fontmap.h"
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fpdfdoc/cpdf_iconfit.h"
-#include "core/fpdfdoc/cpdf_interform.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
+#include "fpdfsdk/cpdfsdk_actionhandler.h"
+#include "fpdfsdk/cpdfsdk_appstream.h"
+#include "fpdfsdk/cpdfsdk_fieldaction.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/formfiller/cba_fontmap.h"
-#include "fpdfsdk/fsdk_actionhandler.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fpdfsdk/pwl/cpwl_appstream.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
 
 #ifdef PDF_ENABLE_XFA
@@ -38,68 +39,52 @@
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #endif  // PDF_ENABLE_XFA
 
-namespace {
-
-// Convert a FX_ARGB to a FX_COLORREF.
-FX_COLORREF ARGBToColorRef(FX_ARGB argb) {
-  return (((static_cast<uint32_t>(argb) & 0x00FF0000) >> 16) |
-          (static_cast<uint32_t>(argb) & 0x0000FF00) |
-          ((static_cast<uint32_t>(argb) & 0x000000FF) << 16));
-}
-
-}  // namespace
-
 CPDFSDK_Widget::CPDFSDK_Widget(CPDF_Annot* pAnnot,
                                CPDFSDK_PageView* pPageView,
-                               CPDFSDK_InterForm* pInterForm)
+                               CPDFSDK_InteractiveForm* pInteractiveForm)
     : CPDFSDK_BAAnnot(pAnnot, pPageView),
-      m_pInterForm(pInterForm),
-      m_nAppearanceAge(0),
-      m_nValueAge(0)
-#ifdef PDF_ENABLE_XFA
-      ,
-      m_hMixXFAWidget(nullptr),
-      m_pWidgetHandler(nullptr)
-#endif  // PDF_ENABLE_XFA
-{
-}
+      m_pInteractiveForm(pInteractiveForm) {}
 
-CPDFSDK_Widget::~CPDFSDK_Widget() {}
+CPDFSDK_Widget::~CPDFSDK_Widget() = default;
 
 #ifdef PDF_ENABLE_XFA
 CXFA_FFWidget* CPDFSDK_Widget::GetMixXFAWidget() const {
-  CPDFXFA_Context* pContext = m_pPageView->GetFormFillEnv()->GetXFAContext();
-  if (pContext->GetFormType() == FormType::kXFAForeground) {
-    if (!m_hMixXFAWidget) {
-      if (CXFA_FFDocView* pDocView = pContext->GetXFADocView()) {
-        WideString sName;
-        if (GetFieldType() == FormFieldType::kRadioButton) {
-          sName = GetAnnotName();
-          if (sName.IsEmpty())
-            sName = GetName();
-        } else {
-          sName = GetName();
-        }
-
-        if (!sName.IsEmpty())
-          m_hMixXFAWidget = pDocView->GetWidgetByName(sName, nullptr);
-      }
-    }
-    return m_hMixXFAWidget.Get();
-  }
-  return nullptr;
-}
-
-CXFA_FFWidget* CPDFSDK_Widget::GetGroupMixXFAWidget() {
-  CPDFXFA_Context* pContext = m_pPageView->GetFormFillEnv()->GetXFAContext();
-  if (pContext->GetFormType() != FormType::kXFAForeground)
+  CPDF_Document::Extension* pContext =
+      m_pPageView->GetFormFillEnv()->GetDocExtension();
+  if (!pContext || !pContext->ContainsExtensionForegroundForm())
     return nullptr;
 
-  CXFA_FFDocView* pDocView = pContext->GetXFADocView();
+  CXFA_FFDocView* pDocView =
+      static_cast<CPDFXFA_Context*>(pContext)->GetXFADocView();
+  if (!pDocView)
+    return nullptr;
+
+  WideString sName;
+  if (GetFieldType() == FormFieldType::kRadioButton) {
+    sName = GetAnnotName();
+    if (sName.IsEmpty())
+      sName = GetName();
+  } else {
+    sName = GetName();
+  }
+
+  if (sName.IsEmpty())
+    return nullptr;
+
+  return pDocView->GetWidgetByName(sName, nullptr);
+}
+
+CXFA_FFWidget* CPDFSDK_Widget::GetGroupMixXFAWidget() const {
+  CPDF_Document::Extension* pContext =
+      m_pPageView->GetFormFillEnv()->GetDocExtension();
+  if (!pContext || !pContext->ContainsExtensionForegroundForm())
+    return nullptr;
+
+  CXFA_FFDocView* pDocView =
+      static_cast<CPDFXFA_Context*>(pContext)->GetXFADocView();
   if (!pDocView)
     return nullptr;
 
@@ -108,12 +93,14 @@
 }
 
 CXFA_FFWidgetHandler* CPDFSDK_Widget::GetXFAWidgetHandler() const {
-  CPDFXFA_Context* pContext = m_pPageView->GetFormFillEnv()->GetXFAContext();
-  if (pContext->GetFormType() != FormType::kXFAForeground)
+  CPDF_Document::Extension* pContext =
+      m_pPageView->GetFormFillEnv()->GetDocExtension();
+  if (!pContext || !pContext->ContainsExtensionForegroundForm())
     return nullptr;
 
   if (!m_pWidgetHandler) {
-    CXFA_FFDocView* pDocView = pContext->GetXFADocView();
+    CXFA_FFDocView* pDocView =
+        static_cast<CPDFXFA_Context*>(pContext)->GetXFADocView();
     if (pDocView)
       m_pWidgetHandler = pDocView->GetWidgetHandler();
   }
@@ -146,47 +133,48 @@
   XFA_EVENTTYPE eEventType = XFA_EVENT_Unknown;
 
   switch (eAAT) {
-    case CPDF_AAction::CursorEnter:
+    case CPDF_AAction::kCursorEnter:
       eEventType = XFA_EVENT_MouseEnter;
       break;
-    case CPDF_AAction::CursorExit:
+    case CPDF_AAction::kCursorExit:
       eEventType = XFA_EVENT_MouseExit;
       break;
-    case CPDF_AAction::ButtonDown:
+    case CPDF_AAction::kButtonDown:
       eEventType = XFA_EVENT_MouseDown;
       break;
-    case CPDF_AAction::ButtonUp:
+    case CPDF_AAction::kButtonUp:
       eEventType = XFA_EVENT_MouseUp;
       break;
-    case CPDF_AAction::GetFocus:
+    case CPDF_AAction::kGetFocus:
       eEventType = XFA_EVENT_Enter;
       break;
-    case CPDF_AAction::LoseFocus:
+    case CPDF_AAction::kLoseFocus:
       eEventType = XFA_EVENT_Exit;
       break;
-    case CPDF_AAction::PageOpen:
-    case CPDF_AAction::PageClose:
-    case CPDF_AAction::PageVisible:
-    case CPDF_AAction::PageInvisible:
+    case CPDF_AAction::kPageOpen:
+    case CPDF_AAction::kPageClose:
+    case CPDF_AAction::kPageVisible:
+    case CPDF_AAction::kPageInvisible:
       break;
-    case CPDF_AAction::KeyStroke:
+    case CPDF_AAction::kKeyStroke:
       if (!bWillCommit)
         eEventType = XFA_EVENT_Change;
       break;
-    case CPDF_AAction::Validate:
+    case CPDF_AAction::kValidate:
       eEventType = XFA_EVENT_Validate;
       break;
-    case CPDF_AAction::OpenPage:
-    case CPDF_AAction::ClosePage:
-    case CPDF_AAction::Format:
-    case CPDF_AAction::Calculate:
-    case CPDF_AAction::CloseDocument:
-    case CPDF_AAction::SaveDocument:
-    case CPDF_AAction::DocumentSaved:
-    case CPDF_AAction::PrintDocument:
-    case CPDF_AAction::DocumentPrinted:
+    case CPDF_AAction::kOpenPage:
+    case CPDF_AAction::kClosePage:
+    case CPDF_AAction::kFormat:
+    case CPDF_AAction::kCalculate:
+    case CPDF_AAction::kCloseDocument:
+    case CPDF_AAction::kSaveDocument:
+    case CPDF_AAction::kDocumentSaved:
+    case CPDF_AAction::kPrintDocument:
+    case CPDF_AAction::kDocumentPrinted:
       break;
-    case CPDF_AAction::NumberOfActions:
+    case CPDF_AAction::kDocumentOpen:
+    case CPDF_AAction::kNumberOfActions:
       NOTREACHED();
       break;
   }
@@ -194,9 +182,9 @@
   return eEventType;
 }
 
-bool CPDFSDK_Widget::HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) {
-  CXFA_FFWidget* hWidget = GetMixXFAWidget();
-  if (!hWidget)
+bool CPDFSDK_Widget::HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const {
+  ObservedPtr<CXFA_FFWidget> pWidget(GetMixXFAWidget());
+  if (!pWidget)
     return false;
 
   CXFA_FFWidgetHandler* pXFAWidgetHandler = GetXFAWidgetHandler();
@@ -204,28 +192,30 @@
     return false;
 
   XFA_EVENTTYPE eEventType = GetXFAEventType(eXFAAAT);
-
   if ((eEventType == XFA_EVENT_Click || eEventType == XFA_EVENT_Change) &&
       GetFieldType() == FormFieldType::kRadioButton) {
-    if (CXFA_FFWidget* hGroupWidget = GetGroupMixXFAWidget()) {
-      if (pXFAWidgetHandler->HasEvent(hGroupWidget->GetNode()->GetWidgetAcc(),
-                                      eEventType)) {
-        return true;
-      }
+    CXFA_FFWidget* hGroupWidget = GetGroupMixXFAWidget();
+    if (hGroupWidget &&
+        hGroupWidget->HasEventUnderHandler(eEventType, pXFAWidgetHandler)) {
+      return true;
     }
   }
 
-  return pXFAWidgetHandler->HasEvent(hWidget->GetNode()->GetWidgetAcc(),
-                                     eEventType);
+  // Check |pWidget| again because JS may have destroyed it in the block above.
+  return pWidget &&
+         pWidget->HasEventUnderHandler(eEventType, pXFAWidgetHandler);
 }
 
 bool CPDFSDK_Widget::OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT,
-                                  PDFSDK_FieldAction& data,
+                                  CPDFSDK_FieldAction* data,
                                   CPDFSDK_PageView* pPageView) {
-  CPDFXFA_Context* pContext = m_pPageView->GetFormFillEnv()->GetXFAContext();
+  auto* pContext = static_cast<CPDFXFA_Context*>(
+      m_pPageView->GetFormFillEnv()->GetDocExtension());
+  if (!pContext)
+    return false;
 
-  CXFA_FFWidget* hWidget = GetMixXFAWidget();
-  if (!hWidget)
+  ObservedPtr<CXFA_FFWidget> pWidget(GetMixXFAWidget());
+  if (!pWidget)
     return false;
 
   XFA_EVENTTYPE eEventType = GetXFAEventType(eXFAAAT);
@@ -238,41 +228,33 @@
 
   CXFA_EventParam param;
   param.m_eType = eEventType;
-  param.m_wsChange = data.sChange;
-  param.m_iCommitKey = data.nCommitKey;
-  param.m_bShift = data.bShift;
-  param.m_iSelStart = data.nSelStart;
-  param.m_iSelEnd = data.nSelEnd;
-  param.m_wsFullText = data.sValue;
-  param.m_bKeyDown = data.bKeyDown;
-  param.m_bModifier = data.bModifier;
-  param.m_wsNewText = data.sValue;
-  if (data.nSelEnd > data.nSelStart)
-    param.m_wsNewText.Delete(data.nSelStart, data.nSelEnd - data.nSelStart);
-
-  for (const auto& c : data.sChange)
-    param.m_wsNewText.Insert(data.nSelStart, c);
-
-  param.m_wsPrevText = data.sValue;
+  param.m_wsChange = data->sChange;
+  param.m_iCommitKey = 0;
+  param.m_bShift = data->bShift;
+  param.m_iSelStart = data->nSelStart;
+  param.m_iSelEnd = data->nSelEnd;
+  param.m_wsFullText = data->sValue;
+  param.m_bKeyDown = data->bKeyDown;
+  param.m_bModifier = data->bModifier;
+  param.m_wsPrevText = data->sValue;
   if ((eEventType == XFA_EVENT_Click || eEventType == XFA_EVENT_Change) &&
       GetFieldType() == FormFieldType::kRadioButton) {
-    if (CXFA_FFWidget* hGroupWidget = GetGroupMixXFAWidget()) {
-      CXFA_WidgetAcc* pAcc = hGroupWidget->GetNode()->GetWidgetAcc();
-      param.m_pTarget = pAcc;
-      if (pXFAWidgetHandler->ProcessEvent(pAcc, &param) !=
-          XFA_EVENTERROR_Success) {
-        return false;
-      }
+    CXFA_FFWidget* hGroupWidget = GetGroupMixXFAWidget();
+    if (hGroupWidget &&
+        !hGroupWidget->ProcessEventUnderHandler(&param, pXFAWidgetHandler)) {
+      return false;
     }
   }
-  CXFA_WidgetAcc* pAcc = hWidget->GetNode()->GetWidgetAcc();
-  param.m_pTarget = pAcc;
-  int32_t nRet = pXFAWidgetHandler->ProcessEvent(pAcc, &param);
 
+  // Check |pWidget| again because JS may have destroyed it in the block above.
+  if (!pWidget)
+    return false;
+
+  bool ret = pWidget->ProcessEventUnderHandler(&param, pXFAWidgetHandler);
   if (CXFA_FFDocView* pDocView = pContext->GetXFADocView())
     pDocView->UpdateDocView();
 
-  return nRet == XFA_EVENTERROR_Success;
+  return ret;
 }
 
 void CPDFSDK_Widget::Synchronize(bool bSynchronizeElse) {
@@ -280,8 +262,8 @@
   if (!hWidget)
     return;
 
-  CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc();
-  if (!pWidgetAcc)
+  CXFA_Node* node = hWidget->GetNode();
+  if (!node->IsWidgetReady())
     return;
 
   CPDF_FormField* pFormField = GetFormField();
@@ -291,31 +273,23 @@
       CPDF_FormControl* pFormCtrl = GetFormControl();
       XFA_CHECKSTATE eCheckState =
           pFormCtrl->IsChecked() ? XFA_CHECKSTATE_On : XFA_CHECKSTATE_Off;
-      pWidgetAcc->SetCheckState(eCheckState, true);
+      node->SetCheckState(eCheckState, true);
       break;
     }
     case FormFieldType::kTextField:
-      pWidgetAcc->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue());
+      node->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue());
       break;
+    case FormFieldType::kComboBox:
     case FormFieldType::kListBox: {
-      pWidgetAcc->ClearAllSelections();
+      node->ClearAllSelections();
 
-      for (int i = 0, sz = pFormField->CountSelectedItems(); i < sz; i++) {
+      for (int i = 0; i < pFormField->CountSelectedItems(); ++i) {
         int nIndex = pFormField->GetSelectedIndex(i);
-        if (nIndex > -1 && nIndex < pWidgetAcc->CountChoiceListItems(false))
-          pWidgetAcc->SetItemState(nIndex, true, false, false, true);
+        if (nIndex > -1 && nIndex < node->CountChoiceListItems(false))
+          node->SetItemState(nIndex, true, false, false, true);
       }
-      break;
-    }
-    case FormFieldType::kComboBox: {
-      pWidgetAcc->ClearAllSelections();
-
-      for (int i = 0, sz = pFormField->CountSelectedItems(); i < sz; i++) {
-        int nIndex = pFormField->GetSelectedIndex(i);
-        if (nIndex > -1 && nIndex < pWidgetAcc->CountChoiceListItems(false))
-          pWidgetAcc->SetItemState(nIndex, true, false, false, true);
-      }
-      pWidgetAcc->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue());
+      if (GetFieldType() == FormFieldType::kComboBox)
+        node->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue());
       break;
     }
     default:
@@ -323,148 +297,16 @@
   }
 
   if (bSynchronizeElse) {
-    CPDFXFA_Context* context = m_pPageView->GetFormFillEnv()->GetXFAContext();
-    context->GetXFADocView()->ProcessValueChanged(pWidgetAcc);
-  }
-}
-
-void CPDFSDK_Widget::SynchronizeXFAValue() {
-  CPDFXFA_Context* pContext = m_pPageView->GetFormFillEnv()->GetXFAContext();
-  CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
-  if (!pXFADocView)
-    return;
-
-  if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
-    if (GetXFAWidgetHandler()) {
-      CPDFSDK_Widget::SynchronizeXFAValue(pXFADocView, hWidget, GetFormField(),
-                                          GetFormControl());
-    }
-  }
-}
-
-void CPDFSDK_Widget::SynchronizeXFAItems() {
-  CPDFXFA_Context* pContext = m_pPageView->GetFormFillEnv()->GetXFAContext();
-  CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
-  if (!pXFADocView)
-    return;
-
-  if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
-    if (GetXFAWidgetHandler())
-      SynchronizeXFAItems(pXFADocView, hWidget, GetFormField(), nullptr);
-  }
-}
-
-void CPDFSDK_Widget::SynchronizeXFAValue(CXFA_FFDocView* pXFADocView,
-                                         CXFA_FFWidget* hWidget,
-                                         CPDF_FormField* pFormField,
-                                         CPDF_FormControl* pFormControl) {
-  ASSERT(hWidget);
-  ASSERT(pFormControl);
-
-  switch (pFormField->GetFieldType()) {
-    case FormFieldType::kCheckBox: {
-      if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-        pFormField->CheckControl(
-            pFormField->GetControlIndex(pFormControl),
-            pWidgetAcc->GetCheckState() == XFA_CHECKSTATE_On, true);
-      }
-      break;
-    }
-    case FormFieldType::kRadioButton: {
-      // TODO(weili): Check whether we need to handle checkbox and radio
-      // button differently, otherwise, merge these two cases.
-      if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-        pFormField->CheckControl(
-            pFormField->GetControlIndex(pFormControl),
-            pWidgetAcc->GetCheckState() == XFA_CHECKSTATE_On, true);
-      }
-      break;
-    }
-    case FormFieldType::kTextField: {
-      if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-        pFormField->SetValue(pWidgetAcc->GetValue(XFA_VALUEPICTURE_Display),
-                             true);
-      }
-      break;
-    }
-    case FormFieldType::kListBox: {
-      pFormField->ClearSelection(false);
-
-      if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-        for (int i = 0, sz = pWidgetAcc->CountSelectedItems(); i < sz; i++) {
-          int nIndex = pWidgetAcc->GetSelectedItem(i);
-
-          if (nIndex > -1 && nIndex < pFormField->CountOptions()) {
-            pFormField->SetItemSelection(nIndex, true, true);
-          }
-        }
-      }
-      break;
-    }
-    case FormFieldType::kComboBox: {
-      pFormField->ClearSelection(false);
-
-      if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-        for (int i = 0, sz = pWidgetAcc->CountSelectedItems(); i < sz; i++) {
-          int nIndex = pWidgetAcc->GetSelectedItem(i);
-
-          if (nIndex > -1 && nIndex < pFormField->CountOptions()) {
-            pFormField->SetItemSelection(nIndex, true, true);
-          }
-        }
-        pFormField->SetValue(pWidgetAcc->GetValue(XFA_VALUEPICTURE_Display),
-                             true);
-      }
-      break;
-    }
-    default:
-      break;
-  }
-}
-
-void CPDFSDK_Widget::SynchronizeXFAItems(CXFA_FFDocView* pXFADocView,
-                                         CXFA_FFWidget* hWidget,
-                                         CPDF_FormField* pFormField,
-                                         CPDF_FormControl* pFormControl) {
-  ASSERT(hWidget);
-
-  switch (pFormField->GetFieldType()) {
-    case FormFieldType::kListBox: {
-      pFormField->ClearSelection(false);
-      pFormField->ClearOptions(true);
-
-      if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-        for (int i = 0, sz = pWidgetAcc->CountChoiceListItems(false); i < sz;
-             i++) {
-          pFormField->InsertOption(
-              pWidgetAcc->GetChoiceListItem(i, false).value_or(L""), i, true);
-        }
-      }
-      break;
-    }
-    case FormFieldType::kComboBox: {
-      pFormField->ClearSelection(false);
-      pFormField->ClearOptions(false);
-
-      if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-        for (int i = 0, sz = pWidgetAcc->CountChoiceListItems(false); i < sz;
-             i++) {
-          pFormField->InsertOption(
-              pWidgetAcc->GetChoiceListItem(i, false).value_or(L""), i, false);
-        }
-      }
-
-      pFormField->SetValue(L"", true);
-      break;
-    }
-    default:
-      break;
+    auto* context = static_cast<CPDFXFA_Context*>(
+        m_pPageView->GetFormFillEnv()->GetDocExtension());
+    context->GetXFADocView()->ProcessValueChanged(node);
   }
 }
 #endif  // PDF_ENABLE_XFA
 
 bool CPDFSDK_Widget::IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode) {
-  CPDF_Dictionary* pAP = m_pAnnot->GetAnnotDict()->GetDictFor("AP");
+  const CPDF_Dictionary* pAP =
+      GetAnnotDict()->GetDictFor(pdfium::annotation::kAP);
   if (!pAP)
     return false;
 
@@ -478,8 +320,8 @@
     ap_entry = "N";
 
   // Get the AP stream or subdirectory
-  CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry);
-  if (!psub)
+  const CPDF_Object* pSub = pAP->GetDirectObjectFor(ap_entry);
+  if (!pSub)
     return false;
 
   FormFieldType fieldType = GetFieldType();
@@ -489,10 +331,10 @@
     case FormFieldType::kListBox:
     case FormFieldType::kTextField:
     case FormFieldType::kSignature:
-      return psub->IsStream();
+      return pSub->IsStream();
     case FormFieldType::kCheckBox:
     case FormFieldType::kRadioButton:
-      if (CPDF_Dictionary* pSubDict = psub->AsDictionary()) {
+      if (const CPDF_Dictionary* pSubDict = pSub->AsDictionary()) {
         return !!pSubDict->GetStreamFor(GetAppState());
       }
       return false;
@@ -508,9 +350,9 @@
 
 bool CPDFSDK_Widget::IsAppearanceValid() {
 #ifdef PDF_ENABLE_XFA
-  CPDFXFA_Context* pContext = m_pPageView->GetFormFillEnv()->GetXFAContext();
-  FormType formType = pContext->GetFormType();
-  if (formType == FormType::kXFAFull)
+  CPDF_Document::Extension* pContext =
+      m_pPageView->GetFormFillEnv()->GetDocExtension();
+  if (pContext && pContext->ContainsExtensionFullForm())
     return true;
 #endif  // PDF_ENABLE_XFA
   return CPDFSDK_BAAnnot::IsAppearanceValid();
@@ -521,11 +363,7 @@
 }
 
 int CPDFSDK_Widget::GetFieldFlags() const {
-  CPDF_InterForm* pPDFInterForm = m_pInterForm->GetInterForm();
-  CPDF_FormControl* pFormControl =
-      pPDFInterForm->GetControlByDict(m_pAnnot->GetAnnotDict());
-  CPDF_FormField* pFormField = pFormControl->GetField();
-  return pFormField->GetFieldFlags();
+  return GetFormField()->GetFieldFlags();
 }
 
 bool CPDFSDK_Widget::IsSignatureWidget() const {
@@ -538,15 +376,9 @@
 }
 
 CPDF_FormControl* CPDFSDK_Widget::GetFormControl() const {
-  CPDF_InterForm* pPDFInterForm = m_pInterForm->GetInterForm();
-  return pPDFInterForm->GetControlByDict(GetAnnotDict());
-}
-
-CPDF_FormControl* CPDFSDK_Widget::GetFormControl(
-    CPDF_InterForm* pInterForm,
-    const CPDF_Dictionary* pAnnotDict) {
-  ASSERT(pAnnotDict);
-  return pInterForm->GetControlByDict(pAnnotDict);
+  CPDF_InteractiveForm* pPDFInteractiveForm =
+      m_pInteractiveForm->GetInteractiveForm();
+  return pPDFInteractiveForm->GetControlByDict(GetAnnotDict());
 }
 
 int CPDFSDK_Widget::GetRotate() const {
@@ -556,36 +388,41 @@
 
 #ifdef PDF_ENABLE_XFA
 WideString CPDFSDK_Widget::GetName() const {
-  CPDF_FormField* pFormField = GetFormField();
-  return pFormField->GetFullName();
+  return GetFormField()->GetFullName();
 }
 #endif  // PDF_ENABLE_XFA
 
-bool CPDFSDK_Widget::GetFillColor(FX_COLORREF& color) const {
+Optional<FX_COLORREF> CPDFSDK_Widget::GetFillColor() const {
   CPDF_FormControl* pFormCtrl = GetFormControl();
   int iColorType = 0;
-  color = ARGBToColorRef(pFormCtrl->GetBackgroundColor(iColorType));
-  return iColorType != CFX_Color::kTransparent;
+  FX_COLORREF color = ArgbToColorRef(pFormCtrl->GetBackgroundColor(iColorType));
+  if (iColorType == CFX_Color::kTransparent)
+    return {};
+  return color;
 }
 
-bool CPDFSDK_Widget::GetBorderColor(FX_COLORREF& color) const {
+Optional<FX_COLORREF> CPDFSDK_Widget::GetBorderColor() const {
   CPDF_FormControl* pFormCtrl = GetFormControl();
   int iColorType = 0;
-  color = ARGBToColorRef(pFormCtrl->GetBorderColor(iColorType));
-  return iColorType != CFX_Color::kTransparent;
+  FX_COLORREF color = ArgbToColorRef(pFormCtrl->GetBorderColor(iColorType));
+  if (iColorType == CFX_Color::kTransparent)
+    return {};
+  return color;
 }
 
-bool CPDFSDK_Widget::GetTextColor(FX_COLORREF& color) const {
+Optional<FX_COLORREF> CPDFSDK_Widget::GetTextColor() const {
   CPDF_FormControl* pFormCtrl = GetFormControl();
   CPDF_DefaultAppearance da = pFormCtrl->GetDefaultAppearance();
-  if (!da.HasColor())
-    return false;
-
   FX_ARGB argb;
-  int iColorType = CFX_Color::kTransparent;
-  da.GetColor(argb, iColorType);
-  color = ARGBToColorRef(argb);
-  return iColorType != CFX_Color::kTransparent;
+  Optional<CFX_Color::Type> iColorType;
+  std::tie(iColorType, argb) = da.GetColor();
+  if (!iColorType.has_value())
+    return {};
+
+  FX_COLORREF color = ArgbToColorRef(argb);
+  if (iColorType.value() == CFX_Color::kTransparent)
+    return {};
+  return color;
 }
 
 float CPDFSDK_Widget::GetFontSize() const {
@@ -599,9 +436,10 @@
 int CPDFSDK_Widget::GetSelectedIndex(int nIndex) const {
 #ifdef PDF_ENABLE_XFA
   if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
-    if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-      if (nIndex < pWidgetAcc->CountSelectedItems())
-        return pWidgetAcc->GetSelectedItem(nIndex);
+    CXFA_Node* node = hWidget->GetNode();
+    if (node->IsWidgetReady()) {
+      if (nIndex < node->CountSelectedItems())
+        return node->GetSelectedItem(nIndex);
     }
   }
 #endif  // PDF_ENABLE_XFA
@@ -609,16 +447,13 @@
   return pFormField->GetSelectedIndex(nIndex);
 }
 
-#ifdef PDF_ENABLE_XFA
-WideString CPDFSDK_Widget::GetValue(bool bDisplay) const {
-  if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
-    if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-      return pWidgetAcc->GetValue(bDisplay ? XFA_VALUEPICTURE_Display
-                                           : XFA_VALUEPICTURE_Edit);
-    }
-  }
-#else
 WideString CPDFSDK_Widget::GetValue() const {
+#ifdef PDF_ENABLE_XFA
+  if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
+    CXFA_Node* node = hWidget->GetNode();
+    if (node->IsWidgetReady())
+      return node->GetValue(XFA_VALUEPICTURE_Display);
+  }
 #endif  // PDF_ENABLE_XFA
   CPDF_FormField* pFormField = GetFormField();
   return pFormField->GetValue();
@@ -642,9 +477,10 @@
 bool CPDFSDK_Widget::IsOptionSelected(int nIndex) const {
 #ifdef PDF_ENABLE_XFA
   if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
-    if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc()) {
-      if (nIndex > -1 && nIndex < pWidgetAcc->CountChoiceListItems(false))
-        return pWidgetAcc->GetItemState(nIndex);
+    CXFA_Node* node = hWidget->GetNode();
+    if (node->IsWidgetReady()) {
+      if (nIndex > -1 && nIndex < node->CountChoiceListItems(false))
+        return node->GetItemState(nIndex);
 
       return false;
     }
@@ -662,8 +498,9 @@
 bool CPDFSDK_Widget::IsChecked() const {
 #ifdef PDF_ENABLE_XFA
   if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
-    if (CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc())
-      return pWidgetAcc->GetCheckState() == XFA_CHECKSTATE_On;
+    CXFA_Node* node = hWidget->GetNode();
+    if (node->IsWidgetReady())
+      return node->GetCheckState() == XFA_CHECKSTATE_On;
   }
 #endif  // PDF_ENABLE_XFA
   CPDF_FormControl* pFormCtrl = GetFormControl();
@@ -680,45 +517,45 @@
   return pFormField->GetMaxLen();
 }
 
-void CPDFSDK_Widget::SetCheck(bool bChecked, bool bNotify) {
+void CPDFSDK_Widget::SetCheck(bool bChecked, NotificationOption notify) {
   CPDF_FormControl* pFormCtrl = GetFormControl();
   CPDF_FormField* pFormField = pFormCtrl->GetField();
   pFormField->CheckControl(pFormField->GetControlIndex(pFormCtrl), bChecked,
-                           bNotify);
+                           notify);
 #ifdef PDF_ENABLE_XFA
   if (!IsWidgetAppearanceValid(CPDF_Annot::Normal))
-    ResetAppearance(true);
-  if (!bNotify)
+    ResetXFAAppearance(true);
+  if (notify == NotificationOption::kDoNotNotify)
     Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
 
-void CPDFSDK_Widget::SetValue(const WideString& sValue, bool bNotify) {
+void CPDFSDK_Widget::SetValue(const WideString& sValue,
+                              NotificationOption notify) {
   CPDF_FormField* pFormField = GetFormField();
-  pFormField->SetValue(sValue, bNotify);
+  pFormField->SetValue(sValue, notify);
 #ifdef PDF_ENABLE_XFA
-  if (!bNotify)
+  if (notify == NotificationOption::kDoNotNotify)
     Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
 
-void CPDFSDK_Widget::SetDefaultValue(const WideString& sValue) {}
 void CPDFSDK_Widget::SetOptionSelection(int index,
                                         bool bSelected,
-                                        bool bNotify) {
+                                        NotificationOption notify) {
   CPDF_FormField* pFormField = GetFormField();
-  pFormField->SetItemSelection(index, bSelected, bNotify);
+  pFormField->SetItemSelection(index, bSelected, notify);
 #ifdef PDF_ENABLE_XFA
-  if (!bNotify)
+  if (notify == NotificationOption::kDoNotNotify)
     Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
 
-void CPDFSDK_Widget::ClearSelection(bool bNotify) {
+void CPDFSDK_Widget::ClearSelection(NotificationOption notify) {
   CPDF_FormField* pFormField = GetFormField();
-  pFormField->ClearSelection(bNotify);
+  pFormField->ClearSelection(notify);
 #ifdef PDF_ENABLE_XFA
-  if (!bNotify)
+  if (notify == NotificationOption::kDoNotNotify)
     Synchronize(true);
 #endif  // PDF_ENABLE_XFA
 }
@@ -738,23 +575,21 @@
 }
 
 #ifdef PDF_ENABLE_XFA
-void CPDFSDK_Widget::ResetAppearance(bool bValueChanged) {
+void CPDFSDK_Widget::ResetXFAAppearance(bool bValueChanged) {
   switch (GetFieldType()) {
     case FormFieldType::kTextField:
     case FormFieldType::kComboBox: {
-      bool bFormatted = false;
-      WideString sValue = OnFormat(bFormatted);
-      ResetAppearance(bFormatted ? &sValue : nullptr, true);
+      ResetAppearance(OnFormat(), true);
       break;
     }
     default:
-      ResetAppearance(nullptr, false);
+      ResetAppearance(pdfium::nullopt, false);
       break;
   }
 }
 #endif  // PDF_ENABLE_XFA
 
-void CPDFSDK_Widget::ResetAppearance(const WideString* sValue,
+void CPDFSDK_Widget::ResetAppearance(Optional<WideString> sValue,
                                      bool bValueChanged) {
   SetAppModified();
 
@@ -762,7 +597,7 @@
   if (bValueChanged)
     m_nValueAge++;
 
-  CPWL_AppStream appStream(this, GetAPDict());
+  CPDFSDK_AppStream appStream(this, GetAPDict());
   switch (GetFieldType()) {
     case FormFieldType::kPushButton:
       appStream.SetAsPushButton();
@@ -789,16 +624,16 @@
   m_pAnnot->ClearCachedAP();
 }
 
-WideString CPDFSDK_Widget::OnFormat(bool& bFormatted) {
+Optional<WideString> CPDFSDK_Widget::OnFormat() {
   CPDF_FormField* pFormField = GetFormField();
   ASSERT(pFormField);
-  return m_pInterForm->OnFormat(pFormField, bFormatted);
+  return m_pInteractiveForm->OnFormat(pFormField);
 }
 
-void CPDFSDK_Widget::ResetFieldAppearance(bool bValueChanged) {
+void CPDFSDK_Widget::ResetFieldAppearance() {
   CPDF_FormField* pFormField = GetFormField();
   ASSERT(pFormField);
-  m_pInterForm->ResetFieldAppearance(pFormField, nullptr, bValueChanged);
+  m_pInteractiveForm->ResetFieldAppearance(pFormField, pdfium::nullopt);
 }
 
 void CPDFSDK_Widget::DrawAppearance(CFX_RenderDevice* pDevice,
@@ -811,16 +646,11 @@
        fieldType == FormFieldType::kRadioButton) &&
       mode == CPDF_Annot::Normal &&
       !IsWidgetAppearanceValid(CPDF_Annot::Normal)) {
-    CFX_PathData pathData;
-
-    CFX_FloatRect rcAnnot = GetRect();
-
-    pathData.AppendRect(rcAnnot.left, rcAnnot.bottom, rcAnnot.right,
-                        rcAnnot.top);
-
     CFX_GraphStateData gsd;
     gsd.m_LineWidth = 0.0f;
 
+    CFX_PathData pathData;
+    pathData.AppendFloatRect(GetRect());
     pDevice->DrawPath(&pathData, &mtUser2Device, &gsd, 0, 0xFFAAAAAA,
                       FXFILL_ALTERNATE);
   } else {
@@ -831,18 +661,16 @@
 void CPDFSDK_Widget::UpdateField() {
   CPDF_FormField* pFormField = GetFormField();
   ASSERT(pFormField);
-  m_pInterForm->UpdateField(pFormField);
+  m_pInteractiveForm->UpdateField(pFormField);
 }
 
 void CPDFSDK_Widget::DrawShadow(CFX_RenderDevice* pDevice,
                                 CPDFSDK_PageView* pPageView) {
   FormFieldType fieldType = GetFieldType();
-  if (!m_pInterForm->IsNeedHighLight(fieldType))
+  if (!m_pInteractiveForm->IsNeedHighLight(fieldType))
     return;
 
-  CFX_Matrix page2device;
-  pPageView->GetCurrentMatrix(page2device);
-
+  CFX_Matrix page2device = pPageView->GetCurrentMatrix();
   CFX_FloatRect rcDevice = GetRect();
   CFX_PointF tmp =
       page2device.Transform(CFX_PointF(rcDevice.left, rcDevice.bottom));
@@ -854,15 +682,16 @@
   rcDevice.top = tmp.y;
   rcDevice.Normalize();
 
-  FX_RECT rcDev = rcDevice.ToFxRect();
   pDevice->FillRect(
-      &rcDev, ArgbEncode(static_cast<int>(m_pInterForm->GetHighlightAlpha()),
-                         m_pInterForm->GetHighlightColor(fieldType)));
+      rcDevice.ToFxRect(),
+      AlphaAndColorRefToArgb(
+          static_cast<int>(m_pInteractiveForm->GetHighlightAlpha()),
+          m_pInteractiveForm->GetHighlightColor(fieldType)));
 }
 
 CFX_FloatRect CPDFSDK_Widget::GetClientRect() const {
   CFX_FloatRect rcWindow = GetRotatedRect();
-  float fBorderWidth = (float)GetBorderWidth();
+  float fBorderWidth = GetBorderWidth();
   switch (GetBorderStyle()) {
     case BorderStyle::BEVELED:
     case BorderStyle::INSET:
@@ -876,32 +705,32 @@
 
 CFX_FloatRect CPDFSDK_Widget::GetRotatedRect() const {
   CFX_FloatRect rectAnnot = GetRect();
-  float fWidth = rectAnnot.right - rectAnnot.left;
-  float fHeight = rectAnnot.top - rectAnnot.bottom;
+  float fWidth = rectAnnot.Width();
+  float fHeight = rectAnnot.Height();
 
   CPDF_FormControl* pControl = GetFormControl();
-  CFX_FloatRect rcPDFWindow;
+  CFX_FloatRect rcPWLWindow;
   switch (abs(pControl->GetRotation() % 360)) {
     case 0:
     case 180:
     default:
-      rcPDFWindow = CFX_FloatRect(0, 0, fWidth, fHeight);
+      rcPWLWindow = CFX_FloatRect(0, 0, fWidth, fHeight);
       break;
     case 90:
     case 270:
-      rcPDFWindow = CFX_FloatRect(0, 0, fHeight, fWidth);
+      rcPWLWindow = CFX_FloatRect(0, 0, fHeight, fWidth);
       break;
   }
 
-  return rcPDFWindow;
+  return rcPWLWindow;
 }
 
 CFX_Matrix CPDFSDK_Widget::GetMatrix() const {
   CFX_Matrix mt;
   CPDF_FormControl* pControl = GetFormControl();
   CFX_FloatRect rcAnnot = GetRect();
-  float fWidth = rcAnnot.right - rcAnnot.left;
-  float fHeight = rcAnnot.top - rcAnnot.bottom;
+  float fWidth = rcAnnot.Width();
+  float fHeight = rcAnnot.Height();
 
   switch (abs(pControl->GetRotation() % 360)) {
     default:
@@ -926,12 +755,11 @@
 
   CPDF_FormControl* pFormCtrl = GetFormControl();
   CPDF_DefaultAppearance da = pFormCtrl->GetDefaultAppearance();
-  if (da.HasColor()) {
-    int32_t iColorType;
-    float fc[4];
-    da.GetColor(iColorType, fc);
-    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-  }
+
+  float fc[4];
+  Optional<CFX_Color::Type> iColorType = da.GetColor(fc);
+  if (iColorType)
+    crText = CFX_Color(*iColorType, fc[0], fc[1], fc[2], fc[3]);
 
   return crText;
 }
@@ -963,76 +791,68 @@
 }
 
 bool CPDFSDK_Widget::OnAAction(CPDF_AAction::AActionType type,
-                               PDFSDK_FieldAction& data,
+                               CPDFSDK_FieldAction* data,
                                CPDFSDK_PageView* pPageView) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
 
 #ifdef PDF_ENABLE_XFA
-  CPDFXFA_Context* pContext = pFormFillEnv->GetXFAContext();
-  if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) {
-    XFA_EVENTTYPE eEventType = GetXFAEventType(type, data.bWillCommit);
-
-    if (eEventType != XFA_EVENT_Unknown) {
-      if (CXFA_FFWidgetHandler* pXFAWidgetHandler = GetXFAWidgetHandler()) {
-        CXFA_EventParam param;
-        param.m_eType = eEventType;
-        param.m_wsChange = data.sChange;
-        param.m_iCommitKey = data.nCommitKey;
-        param.m_bShift = data.bShift;
-        param.m_iSelStart = data.nSelStart;
-        param.m_iSelEnd = data.nSelEnd;
-        param.m_wsFullText = data.sValue;
-        param.m_bKeyDown = data.bKeyDown;
-        param.m_bModifier = data.bModifier;
-        param.m_wsNewText = data.sValue;
-        if (data.nSelEnd > data.nSelStart)
-          param.m_wsNewText.Delete(data.nSelStart,
-                                   data.nSelEnd - data.nSelStart);
-        for (int i = data.sChange.GetLength() - 1; i >= 0; i--)
-          param.m_wsNewText.Insert(data.nSelStart, data.sChange[i]);
-        param.m_wsPrevText = data.sValue;
-
-        CXFA_WidgetAcc* pAcc = hWidget->GetNode()->GetWidgetAcc();
-        param.m_pTarget = pAcc;
-        int32_t nRet = pXFAWidgetHandler->ProcessEvent(pAcc, &param);
-
-        if (CXFA_FFDocView* pDocView = pContext->GetXFADocView())
-          pDocView->UpdateDocView();
-
-        if (nRet == XFA_EVENTERROR_Success)
-          return true;
+  auto* pContext =
+      static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
+  if (pContext) {
+    CXFA_FFWidget* hWidget = GetMixXFAWidget();
+    if (hWidget) {
+      XFA_EVENTTYPE eEventType = GetXFAEventType(type, data->bWillCommit);
+      if (eEventType != XFA_EVENT_Unknown) {
+        if (CXFA_FFWidgetHandler* pXFAWidgetHandler = GetXFAWidgetHandler()) {
+          CXFA_EventParam param;
+          param.m_eType = eEventType;
+          param.m_wsChange = data->sChange;
+          param.m_iCommitKey = 0;
+          param.m_bShift = data->bShift;
+          param.m_iSelStart = data->nSelStart;
+          param.m_iSelEnd = data->nSelEnd;
+          param.m_wsFullText = data->sValue;
+          param.m_bKeyDown = data->bKeyDown;
+          param.m_bModifier = data->bModifier;
+          param.m_wsPrevText = data->sValue;
+          bool ret =
+              hWidget->ProcessEventUnderHandler(&param, pXFAWidgetHandler);
+          if (CXFA_FFDocView* pDocView = pContext->GetXFADocView())
+            pDocView->UpdateDocView();
+          if (ret)
+            return true;
+        }
       }
     }
   }
 #endif  // PDF_ENABLE_XFA
 
   CPDF_Action action = GetAAction(type);
-  if (action.GetDict() && action.GetType() != CPDF_Action::Unknown) {
-    CPDFSDK_ActionHandler* pActionHandler = pFormFillEnv->GetActionHandler();
-    return pActionHandler->DoAction_Field(action, type, pFormFillEnv,
-                                          GetFormField(), data);
+  if (action.GetType() != CPDF_Action::Unknown) {
+    pFormFillEnv->GetActionHandler()->DoAction_Field(action, type, pFormFillEnv,
+                                                     GetFormField(), data);
   }
   return false;
 }
 
 CPDF_Action CPDFSDK_Widget::GetAAction(CPDF_AAction::AActionType eAAT) {
   switch (eAAT) {
-    case CPDF_AAction::CursorEnter:
-    case CPDF_AAction::CursorExit:
-    case CPDF_AAction::ButtonDown:
-    case CPDF_AAction::ButtonUp:
-    case CPDF_AAction::GetFocus:
-    case CPDF_AAction::LoseFocus:
-    case CPDF_AAction::PageOpen:
-    case CPDF_AAction::PageClose:
-    case CPDF_AAction::PageVisible:
-    case CPDF_AAction::PageInvisible:
+    case CPDF_AAction::kCursorEnter:
+    case CPDF_AAction::kCursorExit:
+    case CPDF_AAction::kButtonDown:
+    case CPDF_AAction::kButtonUp:
+    case CPDF_AAction::kGetFocus:
+    case CPDF_AAction::kLoseFocus:
+    case CPDF_AAction::kPageOpen:
+    case CPDF_AAction::kPageClose:
+    case CPDF_AAction::kPageVisible:
+    case CPDF_AAction::kPageInvisible:
       return CPDFSDK_BAAnnot::GetAAction(eAAT);
 
-    case CPDF_AAction::KeyStroke:
-    case CPDF_AAction::Format:
-    case CPDF_AAction::Validate:
-    case CPDF_AAction::Calculate: {
+    case CPDF_AAction::kKeyStroke:
+    case CPDF_AAction::kFormat:
+    case CPDF_AAction::kValidate:
+    case CPDF_AAction::kCalculate: {
       CPDF_FormField* pField = GetFormField();
       if (pField->GetAdditionalAction().GetDict())
         return pField->GetAdditionalAction().GetAction(eAAT);
diff --git a/fpdfsdk/cpdfsdk_widget.h b/fpdfsdk/cpdfsdk_widget.h
index 4b11150..3037a16 100644
--- a/fpdfsdk/cpdfsdk_widget.h
+++ b/fpdfsdk/cpdfsdk_widget.h
@@ -7,17 +7,16 @@
 #ifndef FPDFSDK_CPDFSDK_WIDGET_H_
 #define FPDFSDK_CPDFSDK_WIDGET_H_
 
-#include <set>
-
 #include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_color.h"
 #include "fpdfsdk/cpdfsdk_baannot.h"
-#include "fpdfsdk/pdfsdk_fieldaction.h"
+#include "third_party/base/optional.h"
 
 class CFX_RenderDevice;
 class CPDF_Annot;
@@ -26,43 +25,39 @@
 class CPDF_FormField;
 class CPDF_RenderOptions;
 class CPDF_Stream;
-class CPDFSDK_InterForm;
+class CPDFSDK_InteractiveForm;
 class CPDFSDK_PageView;
+struct CPDFSDK_FieldAction;
 
 #ifdef PDF_ENABLE_XFA
 class CXFA_FFWidget;
 class CXFA_FFWidgetHandler;
+
+enum PDFSDK_XFAAActionType {
+  PDFSDK_XFA_Click = 0,
+  PDFSDK_XFA_Full,
+  PDFSDK_XFA_PreOpen,
+  PDFSDK_XFA_PostOpen
+};
 #endif  // PDF_ENABLE_XFA
 
-class CPDFSDK_Widget : public CPDFSDK_BAAnnot {
+class CPDFSDK_Widget final : public CPDFSDK_BAAnnot {
  public:
 #ifdef PDF_ENABLE_XFA
   CXFA_FFWidget* GetMixXFAWidget() const;
-  CXFA_FFWidget* GetGroupMixXFAWidget();
   CXFA_FFWidgetHandler* GetXFAWidgetHandler() const;
 
-  bool HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT);
+  bool HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const;
   bool OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT,
-                    PDFSDK_FieldAction& data,
+                    CPDFSDK_FieldAction* data,
                     CPDFSDK_PageView* pPageView);
 
   void Synchronize(bool bSynchronizeElse);
-  void SynchronizeXFAValue();
-  void SynchronizeXFAItems();
-
-  static void SynchronizeXFAValue(CXFA_FFDocView* pXFADocView,
-                                  CXFA_FFWidget* hWidget,
-                                  CPDF_FormField* pFormField,
-                                  CPDF_FormControl* pFormControl);
-  static void SynchronizeXFAItems(CXFA_FFDocView* pXFADocView,
-                                  CXFA_FFWidget* hWidget,
-                                  CPDF_FormField* pFormField,
-                                  CPDF_FormControl* pFormControl);
 #endif  // PDF_ENABLE_XFA
 
   CPDFSDK_Widget(CPDF_Annot* pAnnot,
                  CPDFSDK_PageView* pPageView,
-                 CPDFSDK_InterForm* pInterForm);
+                 CPDFSDK_InteractiveForm* pInteractiveForm);
   ~CPDFSDK_Widget() override;
 
   bool IsSignatureWidget() const override;
@@ -75,17 +70,13 @@
   int GetFieldFlags() const;
   int GetRotate() const;
 
-  bool GetFillColor(FX_COLORREF& color) const;
-  bool GetBorderColor(FX_COLORREF& color) const;
-  bool GetTextColor(FX_COLORREF& color) const;
+  Optional<FX_COLORREF> GetFillColor() const;
+  Optional<FX_COLORREF> GetBorderColor() const;
+  Optional<FX_COLORREF> GetTextColor() const;
   float GetFontSize() const;
 
   int GetSelectedIndex(int nIndex) const;
-#ifndef PDF_ENABLE_XFA
   WideString GetValue() const;
-#else
-  WideString GetValue(bool bDisplay = true) const;
-#endif  // PDF_ENABLE_XFA
   WideString GetDefaultValue() const;
   WideString GetOptionLabel(int nIndex) const;
   int CountOptions() const;
@@ -94,35 +85,32 @@
   bool IsChecked() const;
   int GetAlignment() const;
   int GetMaxLen() const;
-#ifdef PDF_ENABLE_XFA
-  WideString GetName() const;
-#endif  // PDF_ENABLE_XFA
   WideString GetAlternateName() const;
 
-  void SetCheck(bool bChecked, bool bNotify);
-  void SetValue(const WideString& sValue, bool bNotify);
-  void SetDefaultValue(const WideString& sValue);
-  void SetOptionSelection(int index, bool bSelected, bool bNotify);
-  void ClearSelection(bool bNotify);
+  void SetCheck(bool bChecked, NotificationOption notify);
+  void SetValue(const WideString& sValue, NotificationOption notify);
+  void SetOptionSelection(int index, bool bSelected, NotificationOption notify);
+  void ClearSelection(NotificationOption notify);
   void SetTopVisibleIndex(int index);
 
 #ifdef PDF_ENABLE_XFA
-  void ResetAppearance(bool bValueChanged);
+  // TODO(thestig): Figure out if the parameter should be used or removed.
+  void ResetXFAAppearance(bool bValueChanged);
 #endif  // PDF_ENABLE_XFA
-  void ResetAppearance(const WideString* sValue, bool bValueChanged);
-  void ResetFieldAppearance(bool bValueChanged);
+  void ResetAppearance(Optional<WideString> sValue, bool bValueChanged);
+  void ResetFieldAppearance();
   void UpdateField();
-  WideString OnFormat(bool& bFormatted);
+  Optional<WideString> OnFormat();
 
   bool OnAAction(CPDF_AAction::AActionType type,
-                 PDFSDK_FieldAction& data,
+                 CPDFSDK_FieldAction* data,
                  CPDFSDK_PageView* pPageView);
 
-  CPDFSDK_InterForm* GetInterForm() const { return m_pInterForm.Get(); }
+  CPDFSDK_InteractiveForm* GetInteractiveForm() const {
+    return m_pInteractiveForm.Get();
+  }
   CPDF_FormField* GetFormField() const;
   CPDF_FormControl* GetFormControl() const;
-  static CPDF_FormControl* GetFormControl(CPDF_InterForm* pInterForm,
-                                          const CPDF_Dictionary* pAnnotDict);
 
   void DrawShadow(CFX_RenderDevice* pDevice, CPDFSDK_PageView* pPageView);
 
@@ -147,15 +135,25 @@
   CFX_Color GetFillPWLColor() const;
 
  private:
-  UnownedPtr<CPDFSDK_InterForm> const m_pInterForm;
-  bool m_bAppModified;
-  uint32_t m_nAppearanceAge;
-  uint32_t m_nValueAge;
+#ifdef PDF_ENABLE_XFA
+  CXFA_FFWidget* GetGroupMixXFAWidget() const;
+  WideString GetName() const;
+#endif  // PDF_ENABLE_XFA
+
+  UnownedPtr<CPDFSDK_InteractiveForm> const m_pInteractiveForm;
+  bool m_bAppModified = false;
+  uint32_t m_nAppearanceAge = 0;
+  uint32_t m_nValueAge = 0;
 
 #ifdef PDF_ENABLE_XFA
-  mutable UnownedPtr<CXFA_FFWidget> m_hMixXFAWidget;
   mutable UnownedPtr<CXFA_FFWidgetHandler> m_pWidgetHandler;
 #endif  // PDF_ENABLE_XFA
 };
 
+inline CPDFSDK_Widget* ToCPDFSDKWidget(CPDFSDK_Annot* pAnnot) {
+  return pAnnot && pAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::WIDGET
+             ? static_cast<CPDFSDK_Widget*>(pAnnot)
+             : nullptr;
+}
+
 #endif  // FPDFSDK_CPDFSDK_WIDGET_H_
diff --git a/fpdfsdk/cpdfsdk_widgethandler.cpp b/fpdfsdk/cpdfsdk_widgethandler.cpp
index dcce7b6..a3b2394 100644
--- a/fpdfsdk/cpdfsdk_widgethandler.cpp
+++ b/fpdfsdk/cpdfsdk_widgethandler.cpp
@@ -9,187 +9,160 @@
 #include <memory>
 #include <vector>
 
+#include "constants/form_flags.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfdoc/cpdf_interform.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
 
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#endif  // PDF_ENABLE_XFA
+CPDFSDK_WidgetHandler::CPDFSDK_WidgetHandler() = default;
 
-CPDFSDK_WidgetHandler::CPDFSDK_WidgetHandler(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv),
-      m_pFormFiller(pFormFillEnv->GetInteractiveFormFiller()) {}
+CPDFSDK_WidgetHandler::~CPDFSDK_WidgetHandler() = default;
 
-CPDFSDK_WidgetHandler::~CPDFSDK_WidgetHandler() {}
+void CPDFSDK_WidgetHandler::SetFormFillEnvironment(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  m_pFormFillEnv = pFormFillEnv;
+  m_pFormFiller = m_pFormFillEnv->GetInteractiveFormFiller();
+}
 
 bool CPDFSDK_WidgetHandler::CanAnswer(CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::WIDGET);
-  if (pAnnot->IsSignatureWidget())
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
+  if (pWidget->IsSignatureWidget())
     return false;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot);
   if (!pWidget->IsVisible())
     return false;
 
   int nFieldFlags = pWidget->GetFieldFlags();
-  if ((nFieldFlags & FIELDFLAG_READONLY) == FIELDFLAG_READONLY)
+  if (nFieldFlags & pdfium::form_flags::kReadOnly)
     return false;
 
   if (pWidget->GetFieldType() == FormFieldType::kPushButton)
     return true;
 
   CPDF_Page* pPage = pWidget->GetPDFPage();
-  uint32_t dwPermissions = pPage->m_pDocument->GetUserPermissions();
+  uint32_t dwPermissions = pPage->GetDocument()->GetUserPermissions();
   return (dwPermissions & FPDFPERM_FILL_FORM) ||
          (dwPermissions & FPDFPERM_ANNOT_FORM);
 }
 
 CPDFSDK_Annot* CPDFSDK_WidgetHandler::NewAnnot(CPDF_Annot* pAnnot,
                                                CPDFSDK_PageView* pPage) {
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_FormControl* pCtrl = CPDFSDK_Widget::GetFormControl(
-      pInterForm->GetInterForm(), pAnnot->GetAnnotDict());
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  CPDF_FormControl* pCtrl = pPDFForm->GetControlByDict(pAnnot->GetAnnotDict());
   if (!pCtrl)
     return nullptr;
 
-  CPDFSDK_Widget* pWidget = new CPDFSDK_Widget(pAnnot, pPage, pInterForm);
-  pInterForm->AddMap(pCtrl, pWidget);
-  CPDF_InterForm* pPDFInterForm = pInterForm->GetInterForm();
-  if (pPDFInterForm && pPDFInterForm->NeedConstructAP())
-    pWidget->ResetAppearance(nullptr, false);
-
+  CPDFSDK_Widget* pWidget = new CPDFSDK_Widget(pAnnot, pPage, pForm);
+  pForm->AddMap(pCtrl, pWidget);
+  if (pPDFForm->NeedConstructAP())
+    pWidget->ResetAppearance(pdfium::nullopt, false);
   return pWidget;
 }
 
-#ifdef PDF_ENABLE_XFA
-CPDFSDK_Annot* CPDFSDK_WidgetHandler::NewAnnot(CXFA_FFWidget* hWidget,
-                                               CPDFSDK_PageView* pPage) {
-  return nullptr;
-}
-#endif  // PDF_ENABLE_XFA
-
-void CPDFSDK_WidgetHandler::ReleaseAnnot(CPDFSDK_Annot* pAnnot) {
+void CPDFSDK_WidgetHandler::ReleaseAnnot(
+    std::unique_ptr<CPDFSDK_Annot> pAnnot) {
   ASSERT(pAnnot);
+  m_pFormFiller->OnDelete(pAnnot.get());
 
-  if (m_pFormFiller)
-    m_pFormFiller->OnDelete(pAnnot);
-
-  std::unique_ptr<CPDFSDK_Widget> pWidget(static_cast<CPDFSDK_Widget*>(pAnnot));
-  CPDFSDK_InterForm* pInterForm = pWidget->GetInterForm();
+  std::unique_ptr<CPDFSDK_Widget> pWidget(ToCPDFSDKWidget(pAnnot.release()));
+  CPDFSDK_InteractiveForm* pForm = pWidget->GetInteractiveForm();
   CPDF_FormControl* pControl = pWidget->GetFormControl();
-  pInterForm->RemoveMap(pControl);
+  pForm->RemoveMap(pControl);
 }
 
 void CPDFSDK_WidgetHandler::OnDraw(CPDFSDK_PageView* pPageView,
                                    CPDFSDK_Annot* pAnnot,
                                    CFX_RenderDevice* pDevice,
-                                   CFX_Matrix* pUser2Device,
+                                   const CFX_Matrix& mtUser2Device,
                                    bool bDrawAnnots) {
   if (pAnnot->IsSignatureWidget()) {
-    static_cast<CPDFSDK_BAAnnot*>(pAnnot)->DrawAppearance(
-        pDevice, *pUser2Device, CPDF_Annot::Normal, nullptr);
+    pAnnot->AsBAAnnot()->DrawAppearance(pDevice, mtUser2Device,
+                                        CPDF_Annot::Normal, nullptr);
   } else {
-    if (m_pFormFiller)
-      m_pFormFiller->OnDraw(pPageView, pAnnot, pDevice, pUser2Device);
+    m_pFormFiller->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device);
   }
 }
 
 void CPDFSDK_WidgetHandler::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          uint32_t nFlag) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
+  if (!(*pAnnot)->IsSignatureWidget())
     m_pFormFiller->OnMouseEnter(pPageView, pAnnot, nFlag);
 }
 
 void CPDFSDK_WidgetHandler::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                        CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                         uint32_t nFlag) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
+  if (!(*pAnnot)->IsSignatureWidget())
     m_pFormFiller->OnMouseExit(pPageView, pAnnot, nFlag);
 }
 
 bool CPDFSDK_WidgetHandler::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                          CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                           uint32_t nFlags,
                                           const CFX_PointF& point) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnLButtonDown(pPageView, pAnnot, nFlags, point);
-
-  return false;
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->OnLButtonDown(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_WidgetHandler::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                        CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                         uint32_t nFlags,
                                         const CFX_PointF& point) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnLButtonUp(pPageView, pAnnot, nFlags, point);
-
-  return false;
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->OnLButtonUp(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_WidgetHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                                            CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                             uint32_t nFlags,
                                             const CFX_PointF& point) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnLButtonDblClk(pPageView, pAnnot, nFlags, point);
-
-  return false;
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->OnLButtonDblClk(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_WidgetHandler::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                        CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                         uint32_t nFlags,
                                         const CFX_PointF& point) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnMouseMove(pPageView, pAnnot, nFlags, point);
-
-  return false;
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->OnMouseMove(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_WidgetHandler::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                         CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          uint32_t nFlags,
                                          short zDelta,
                                          const CFX_PointF& point) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnMouseWheel(pPageView, pAnnot, nFlags, zDelta,
-                                       point);
-
-  return false;
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->OnMouseWheel(pPageView, pAnnot, nFlags, zDelta, point);
 }
 
 bool CPDFSDK_WidgetHandler::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                          CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                           uint32_t nFlags,
                                           const CFX_PointF& point) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnRButtonDown(pPageView, pAnnot, nFlags, point);
-
-  return false;
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->OnRButtonDown(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_WidgetHandler::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                        CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                         uint32_t nFlags,
                                         const CFX_PointF& point) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnRButtonUp(pPageView, pAnnot, nFlags, point);
-
-  return false;
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->OnRButtonUp(pPageView, pAnnot, nFlags, point);
 }
 
 bool CPDFSDK_WidgetHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                                            CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                             uint32_t nFlags,
                                             const CFX_PointF& point) {
   return false;
@@ -198,19 +171,15 @@
 bool CPDFSDK_WidgetHandler::OnChar(CPDFSDK_Annot* pAnnot,
                                    uint32_t nChar,
                                    uint32_t nFlags) {
-  if (!pAnnot->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnChar(pAnnot, nChar, nFlags);
-
-  return false;
+  return !pAnnot->IsSignatureWidget() &&
+         m_pFormFiller->OnChar(pAnnot, nChar, nFlags);
 }
 
 bool CPDFSDK_WidgetHandler::OnKeyDown(CPDFSDK_Annot* pAnnot,
                                       int nKeyCode,
                                       int nFlag) {
-  if (!pAnnot->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnKeyDown(pAnnot, nKeyCode, nFlag);
-
-  return false;
+  return !pAnnot->IsSignatureWidget() &&
+         m_pFormFiller->OnKeyDown(pAnnot, nKeyCode, nFlag);
 }
 
 bool CPDFSDK_WidgetHandler::OnKeyUp(CPDFSDK_Annot* pAnnot,
@@ -223,77 +192,98 @@
   if (pAnnot->IsSignatureWidget())
     return;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot);
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
   if (!pWidget->IsAppearanceValid())
-    pWidget->ResetAppearance(nullptr, false);
+    pWidget->ResetAppearance(pdfium::nullopt, false);
 
   FormFieldType fieldType = pWidget->GetFieldType();
   if (fieldType == FormFieldType::kTextField ||
       fieldType == FormFieldType::kComboBox) {
-    bool bFormatted = false;
-    CPDFSDK_Annot::ObservedPtr pObserved(pWidget);
-    WideString sValue = pWidget->OnFormat(bFormatted);
+    ObservedPtr<CPDFSDK_Annot> pObserved(pWidget);
+    Optional<WideString> sValue = pWidget->OnFormat();
     if (!pObserved)
       return;
 
-    if (bFormatted && fieldType == FormFieldType::kComboBox)
-      pWidget->ResetAppearance(&sValue, false);
+    if (sValue.has_value() && fieldType == FormFieldType::kComboBox)
+      pWidget->ResetAppearance(sValue, false);
   }
 
 #ifdef PDF_ENABLE_XFA
   CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
-  CPDFXFA_Context* pContext = pPageView->GetFormFillEnv()->GetXFAContext();
-  if (pContext->GetFormType() == FormType::kXFAForeground) {
+  auto* pContext = pPageView->GetFormFillEnv()->GetDocExtension();
+  if (pContext && pContext->ContainsExtensionForegroundForm()) {
     if (!pWidget->IsAppearanceValid() && !pWidget->GetValue().IsEmpty())
-      pWidget->ResetAppearance(false);
+      pWidget->ResetXFAAppearance(false);
   }
 #endif  // PDF_ENABLE_XFA
 }
 
-bool CPDFSDK_WidgetHandler::OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CPDFSDK_WidgetHandler::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                        uint32_t nFlag) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnSetFocus(pAnnot, nFlag);
-
-  return true;
+  return (*pAnnot)->IsSignatureWidget() ||
+         m_pFormFiller->OnSetFocus(pAnnot, nFlag);
 }
 
-bool CPDFSDK_WidgetHandler::OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CPDFSDK_WidgetHandler::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                         uint32_t nFlag) {
-  if (!(*pAnnot)->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->OnKillFocus(pAnnot, nFlag);
-
-  return true;
+  return (*pAnnot)->IsSignatureWidget() ||
+         m_pFormFiller->OnKillFocus(pAnnot, nFlag);
 }
 
-#ifdef PDF_ENABLE_XFA
-bool CPDFSDK_WidgetHandler::OnXFAChangedFocus(
-    CPDFSDK_Annot::ObservedPtr* pOldAnnot,
-    CPDFSDK_Annot::ObservedPtr* pNewAnnot) {
-  return true;
+bool CPDFSDK_WidgetHandler::SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                             int index,
+                                             bool selected) {
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->SetIndexSelected(pAnnot, index, selected);
 }
-#endif  // PDF_ENABLE_XFA
+
+bool CPDFSDK_WidgetHandler::IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                            int index) {
+  return !(*pAnnot)->IsSignatureWidget() &&
+         m_pFormFiller->IsIndexSelected(pAnnot, index);
+}
 
 CFX_FloatRect CPDFSDK_WidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView,
                                                  CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot->IsSignatureWidget() && m_pFormFiller)
+  if (!pAnnot->IsSignatureWidget())
     return CFX_FloatRect(m_pFormFiller->GetViewBBox(pPageView, pAnnot));
   return CFX_FloatRect();
 }
 
-WideString CPDFSDK_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot->IsSignatureWidget() && m_pFormFiller)
-    return m_pFormFiller->GetSelectedText(pAnnot);
+WideString CPDFSDK_WidgetHandler::GetText(CPDFSDK_Annot* pAnnot) {
+  if (!pAnnot->IsSignatureWidget())
+    return m_pFormFiller->GetText(pAnnot);
+  return WideString();
+}
 
+WideString CPDFSDK_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
+  if (!pAnnot->IsSignatureWidget())
+    return m_pFormFiller->GetSelectedText(pAnnot);
   return WideString();
 }
 
 void CPDFSDK_WidgetHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot,
                                              const WideString& text) {
-  if (!pAnnot->IsSignatureWidget() && m_pFormFiller)
+  if (!pAnnot->IsSignatureWidget())
     m_pFormFiller->ReplaceSelection(pAnnot, text);
 }
 
+bool CPDFSDK_WidgetHandler::CanUndo(CPDFSDK_Annot* pAnnot) {
+  return !pAnnot->IsSignatureWidget() && m_pFormFiller->CanUndo(pAnnot);
+}
+
+bool CPDFSDK_WidgetHandler::CanRedo(CPDFSDK_Annot* pAnnot) {
+  return !pAnnot->IsSignatureWidget() && m_pFormFiller->CanRedo(pAnnot);
+}
+
+bool CPDFSDK_WidgetHandler::Undo(CPDFSDK_Annot* pAnnot) {
+  return !pAnnot->IsSignatureWidget() && m_pFormFiller->Undo(pAnnot);
+}
+
+bool CPDFSDK_WidgetHandler::Redo(CPDFSDK_Annot* pAnnot) {
+  return !pAnnot->IsSignatureWidget() && m_pFormFiller->Redo(pAnnot);
+}
+
 bool CPDFSDK_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView,
                                     CPDFSDK_Annot* pAnnot,
                                     const CFX_PointF& point) {
diff --git a/fpdfsdk/cpdfsdk_widgethandler.h b/fpdfsdk/cpdfsdk_widgethandler.h
index f8aa7de..fa2ac94 100644
--- a/fpdfsdk/cpdfsdk_widgethandler.h
+++ b/fpdfsdk/cpdfsdk_widgethandler.h
@@ -7,6 +7,8 @@
 #ifndef FPDFSDK_CPDFSDK_WIDGETHANDLER_H_
 #define FPDFSDK_CPDFSDK_WIDGETHANDLER_H_
 
+#include <memory>
+
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/ipdfsdk_annothandler.h"
@@ -19,92 +21,87 @@
 class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_PageView;
 
-#ifdef PDF_ENABLE_XFA
-class CXFA_FFWidget;
-#endif  // PDF_ENABLE_XFA
-
-class CPDFSDK_WidgetHandler : public IPDFSDK_AnnotHandler {
+class CPDFSDK_WidgetHandler final : public IPDFSDK_AnnotHandler {
  public:
-  explicit CPDFSDK_WidgetHandler(CPDFSDK_FormFillEnvironment* pApp);
+  CPDFSDK_WidgetHandler();
   ~CPDFSDK_WidgetHandler() override;
 
+  void SetFormFillEnvironment(
+      CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
   bool CanAnswer(CPDFSDK_Annot* pAnnot) override;
   CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override;
-#ifdef PDF_ENABLE_XFA
-  CPDFSDK_Annot* NewAnnot(CXFA_FFWidget* hWidget,
-                          CPDFSDK_PageView* pPage) override;
-#endif  // PDF_ENABLE_XFA
-  void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override;
+  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) override;
   CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
                             CPDFSDK_Annot* pAnnot) override;
+  WideString GetText(CPDFSDK_Annot* pAnnot) override;
   WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
   void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override;
+  bool CanUndo(CPDFSDK_Annot* pAnnot) override;
+  bool CanRedo(CPDFSDK_Annot* pAnnot) override;
+  bool Undo(CPDFSDK_Annot* pAnnot) override;
+  bool Redo(CPDFSDK_Annot* pAnnot) override;
   bool HitTest(CPDFSDK_PageView* pPageView,
                CPDFSDK_Annot* pAnnot,
                const CFX_PointF& point) override;
   void OnDraw(CPDFSDK_PageView* pPageView,
               CPDFSDK_Annot* pAnnot,
               CFX_RenderDevice* pDevice,
-              CFX_Matrix* pUser2Device,
+              const CFX_Matrix& mtUser2Device,
               bool bDrawAnnots) override;
   void OnLoad(CPDFSDK_Annot* pAnnot) override;
 
   void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
                     uint32_t nFlag) override;
   void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlag) override;
   bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
                      uint32_t nFlags,
                      const CFX_PointF& point) override;
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot::ObservedPtr* pAnnot,
+                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
                        uint32_t nFlags,
                        const CFX_PointF& point) override;
   bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
                     uint32_t nFlags,
                     short zDelta,
                     const CFX_PointF& point) override;
   bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
                      uint32_t nFlags,
                      const CFX_PointF& point) override;
   bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot::ObservedPtr* pAnnot,
+                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
                        uint32_t nFlags,
                        const CFX_PointF& point) override;
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
   bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
   bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag) override;
-  bool OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag) override;
-#ifdef PDF_ENABLE_XFA
-  bool OnXFAChangedFocus(CPDFSDK_Annot::ObservedPtr* pOldAnnot,
-                         CPDFSDK_Annot::ObservedPtr* pNewAnnot) override;
-#endif  // PDF_ENABLE_XFA
-
-  CFFL_InteractiveFormFiller* GetFormFiller() const {
-    return m_pFormFiller.Get();
-  }
+  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
+  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
+  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                        int index,
+                        bool selected) override;
+  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index) override;
 
  private:
-  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-  UnownedPtr<CFFL_InteractiveFormFiller> const m_pFormFiller;
+  UnownedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  UnownedPtr<CFFL_InteractiveFormFiller> m_pFormFiller;
 };
 
 #endif  // FPDFSDK_CPDFSDK_WIDGETHANDLER_H_
diff --git a/fpdfsdk/cpdfsdk_xfawidget.cpp b/fpdfsdk/cpdfsdk_xfawidget.cpp
deleted file mode 100644
index af08023..0000000
--- a/fpdfsdk/cpdfsdk_xfawidget.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_xfawidget.h"
-
-#include "fpdfsdk/ipdfsdk_annothandler.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-
-CPDFSDK_XFAWidget::CPDFSDK_XFAWidget(CXFA_FFWidget* pAnnot,
-                                     CPDFSDK_PageView* pPageView,
-                                     CPDFSDK_InterForm* pInterForm)
-    : CPDFSDK_Annot(pPageView),
-      m_pInterForm(pInterForm),
-      m_hXFAWidget(pAnnot) {}
-
-CPDFSDK_XFAWidget::~CPDFSDK_XFAWidget() {}
-
-bool CPDFSDK_XFAWidget::IsXFAField() {
-  return true;
-}
-
-CXFA_FFWidget* CPDFSDK_XFAWidget::GetXFAWidget() const {
-  return m_hXFAWidget.Get();
-}
-
-CPDF_Annot::Subtype CPDFSDK_XFAWidget::GetAnnotSubtype() const {
-  return CPDF_Annot::Subtype::XFAWIDGET;
-}
-
-CFX_FloatRect CPDFSDK_XFAWidget::GetRect() const {
-  CFX_RectF rcBBox = GetXFAWidget()->GetRect(false);
-  return CFX_FloatRect(rcBBox.left, rcBBox.top, rcBBox.left + rcBBox.width,
-                       rcBBox.top + rcBBox.height);
-}
diff --git a/fpdfsdk/cpdfsdk_xfawidget.h b/fpdfsdk/cpdfsdk_xfawidget.h
deleted file mode 100644
index d2635eb..0000000
--- a/fpdfsdk/cpdfsdk_xfawidget.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_XFAWIDGET_H_
-#define FPDFSDK_CPDFSDK_XFAWIDGET_H_
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-
-class CPDFSDK_InterForm;
-class CPDFSDK_PageView;
-class CXFA_FFWidget;
-
-class CPDFSDK_XFAWidget : public CPDFSDK_Annot {
- public:
-  CPDFSDK_XFAWidget(CXFA_FFWidget* pAnnot,
-                    CPDFSDK_PageView* pPageView,
-                    CPDFSDK_InterForm* pInterForm);
-  ~CPDFSDK_XFAWidget() override;
-
-  // CPDFSDK_Annot:
-  bool IsXFAField() override;
-  CXFA_FFWidget* GetXFAWidget() const override;
-  CPDF_Annot::Subtype GetAnnotSubtype() const override;
-  CFX_FloatRect GetRect() const override;
-
-  CPDFSDK_InterForm* GetInterForm() const { return m_pInterForm.Get(); }
-
- private:
-  UnownedPtr<CPDFSDK_InterForm> m_pInterForm;
-  UnownedPtr<CXFA_FFWidget> m_hXFAWidget;
-};
-
-#endif  // FPDFSDK_CPDFSDK_XFAWIDGET_H_
diff --git a/fpdfsdk/cpdfsdk_xfawidgethandler.cpp b/fpdfsdk/cpdfsdk_xfawidgethandler.cpp
deleted file mode 100644
index 897e16d..0000000
--- a/fpdfsdk/cpdfsdk_xfawidgethandler.cpp
+++ /dev/null
@@ -1,375 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/cpdfsdk_xfawidgethandler.h"
-
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_xfawidget.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "xfa/fwl/cfwl_app.h"
-#include "xfa/fwl/fwl_widgethit.h"
-#include "xfa/fxfa/cxfa_ffdocview.h"
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#include "xfa/fxfa/fxfa_basic.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
-
-CPDFSDK_XFAWidgetHandler::CPDFSDK_XFAWidgetHandler(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv) {}
-
-CPDFSDK_XFAWidgetHandler::~CPDFSDK_XFAWidgetHandler() {}
-
-bool CPDFSDK_XFAWidgetHandler::CanAnswer(CPDFSDK_Annot* pAnnot) {
-  return !!pAnnot->GetXFAWidget();
-}
-
-CPDFSDK_Annot* CPDFSDK_XFAWidgetHandler::NewAnnot(CPDF_Annot* pAnnot,
-                                                  CPDFSDK_PageView* pPage) {
-  return nullptr;
-}
-
-CPDFSDK_Annot* CPDFSDK_XFAWidgetHandler::NewAnnot(CXFA_FFWidget* pAnnot,
-                                                  CPDFSDK_PageView* pPage) {
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDFSDK_XFAWidget* pWidget = new CPDFSDK_XFAWidget(pAnnot, pPage, pInterForm);
-  pInterForm->AddXFAMap(pAnnot, pWidget);
-  return pWidget;
-}
-
-void CPDFSDK_XFAWidgetHandler::OnDraw(CPDFSDK_PageView* pPageView,
-                                      CPDFSDK_Annot* pAnnot,
-                                      CFX_RenderDevice* pDevice,
-                                      CFX_Matrix* pUser2Device,
-                                      bool bDrawAnnots) {
-  ASSERT(pPageView);
-  ASSERT(pAnnot);
-
-  CXFA_Graphics gs(pDevice);
-
-  CFX_Matrix mt = *pUser2Device;
-  bool bIsHighlight = false;
-  if (pPageView->GetFormFillEnv()->GetFocusAnnot() != pAnnot)
-    bIsHighlight = true;
-
-  GetXFAWidgetHandler(pAnnot)->RenderWidget(pAnnot->GetXFAWidget(), &gs, mt,
-                                            bIsHighlight);
-
-  // to do highlight and shadow
-}
-
-void CPDFSDK_XFAWidgetHandler::OnLoad(CPDFSDK_Annot* pAnnot) {}
-
-void CPDFSDK_XFAWidgetHandler::ReleaseAnnot(CPDFSDK_Annot* pAnnot) {
-  CPDFSDK_XFAWidget* pWidget = reinterpret_cast<CPDFSDK_XFAWidget*>(pAnnot);
-  CPDFSDK_InterForm* pInterForm = pWidget->GetInterForm();
-  pInterForm->RemoveXFAMap(pWidget->GetXFAWidget());
-
-  delete pWidget;
-}
-
-CFX_FloatRect CPDFSDK_XFAWidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView,
-                                                    CPDFSDK_Annot* pAnnot) {
-  ASSERT(pAnnot);
-
-  CFX_RectF rcBBox;
-  XFA_Element eType =
-      pAnnot->GetXFAWidget()->GetNode()->GetWidgetAcc()->GetUIType();
-  if (eType == XFA_Element::Signature)
-    rcBBox = pAnnot->GetXFAWidget()->GetBBox(XFA_WidgetStatus_Visible, true);
-  else
-    rcBBox = pAnnot->GetXFAWidget()->GetBBox(XFA_WidgetStatus_None);
-
-  CFX_FloatRect rcWidget(rcBBox.left, rcBBox.top, rcBBox.left + rcBBox.width,
-                         rcBBox.top + rcBBox.height);
-  rcWidget.left -= 1.0f;
-  rcWidget.right += 1.0f;
-  rcWidget.bottom -= 1.0f;
-  rcWidget.top += 1.0f;
-
-  return rcWidget;
-}
-
-WideString CPDFSDK_XFAWidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot)
-    return WideString();
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot);
-  return pWidgetHandler->GetSelectedText(pAnnot->GetXFAWidget());
-}
-
-void CPDFSDK_XFAWidgetHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                                const WideString& text) {
-  if (!pAnnot)
-    return;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot);
-  return pWidgetHandler->PasteText(pAnnot->GetXFAWidget(), text);
-}
-
-bool CPDFSDK_XFAWidgetHandler::HitTest(CPDFSDK_PageView* pPageView,
-                                       CPDFSDK_Annot* pAnnot,
-                                       const CFX_PointF& point) {
-  if (!pPageView || !pAnnot)
-    return false;
-
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return false;
-
-  CPDFXFA_Context* pContext = pFormFillEnv->GetXFAContext();
-  if (!pContext)
-    return false;
-
-  CXFA_FFDocView* pDocView = pContext->GetXFADocView();
-  if (!pDocView)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
-  if (!pWidgetHandler)
-    return false;
-
-  FWL_WidgetHit dwHitTest =
-      pWidgetHandler->OnHitTest(pAnnot->GetXFAWidget(), point);
-  return dwHitTest != FWL_WidgetHit::Unknown;
-}
-
-void CPDFSDK_XFAWidgetHandler::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                                            CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                            uint32_t nFlag) {
-  if (!pPageView || !(*pAnnot))
-    return;
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  pWidgetHandler->OnMouseEnter((*pAnnot)->GetXFAWidget());
-}
-
-void CPDFSDK_XFAWidgetHandler::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                           CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                           uint32_t nFlag) {
-  if (!pPageView || !(*pAnnot))
-    return;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  pWidgetHandler->OnMouseExit((*pAnnot)->GetXFAWidget());
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnLButtonDown(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                             uint32_t nFlags,
-                                             const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnLButtonDown((*pAnnot)->GetXFAWidget(),
-                                       GetFWLFlags(nFlags), point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                           CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                           uint32_t nFlags,
-                                           const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnLButtonUp((*pAnnot)->GetXFAWidget(),
-                                     GetFWLFlags(nFlags), point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnLButtonDblClk(
-    CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnLButtonDblClk((*pAnnot)->GetXFAWidget(),
-                                         GetFWLFlags(nFlags), point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                           CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                           uint32_t nFlags,
-                                           const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnMouseMove((*pAnnot)->GetXFAWidget(),
-                                     GetFWLFlags(nFlags), point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                            CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                            uint32_t nFlags,
-                                            short zDelta,
-                                            const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnMouseWheel((*pAnnot)->GetXFAWidget(),
-                                      GetFWLFlags(nFlags), zDelta, point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                             uint32_t nFlags,
-                                             const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnRButtonDown((*pAnnot)->GetXFAWidget(),
-                                       GetFWLFlags(nFlags), point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                           CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                           uint32_t nFlags,
-                                           const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnRButtonUp((*pAnnot)->GetXFAWidget(),
-                                     GetFWLFlags(nFlags), point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnRButtonDblClk(
-    CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
-    uint32_t nFlags,
-    const CFX_PointF& point) {
-  if (!pPageView || !(*pAnnot))
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot->Get());
-  return pWidgetHandler->OnRButtonDblClk((*pAnnot)->GetXFAWidget(),
-                                         GetFWLFlags(nFlags), point);
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnChar(CPDFSDK_Annot* pAnnot,
-                                      uint32_t nChar,
-                                      uint32_t nFlags) {
-  if (!pAnnot)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot);
-  return pWidgetHandler->OnChar(pAnnot->GetXFAWidget(), nChar,
-                                GetFWLFlags(nFlags));
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                         int nKeyCode,
-                                         int nFlag) {
-  if (!pAnnot)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot);
-  return pWidgetHandler->OnKeyDown(pAnnot->GetXFAWidget(), nKeyCode,
-                                   GetFWLFlags(nFlag));
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnKeyUp(CPDFSDK_Annot* pAnnot,
-                                       int nKeyCode,
-                                       int nFlag) {
-  if (!pAnnot)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAWidgetHandler(pAnnot);
-  return pWidgetHandler->OnKeyUp(pAnnot->GetXFAWidget(), nKeyCode,
-                                 GetFWLFlags(nFlag));
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                          uint32_t nFlag) {
-  return true;
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
-                                           uint32_t nFlag) {
-  return true;
-}
-
-bool CPDFSDK_XFAWidgetHandler::OnXFAChangedFocus(
-    CPDFSDK_Annot::ObservedPtr* pOldAnnot,
-    CPDFSDK_Annot::ObservedPtr* pNewAnnot) {
-  CXFA_FFWidgetHandler* pWidgetHandler = nullptr;
-  if (*pOldAnnot)
-    pWidgetHandler = GetXFAWidgetHandler(pOldAnnot->Get());
-  else if (*pNewAnnot)
-    pWidgetHandler = GetXFAWidgetHandler(pNewAnnot->Get());
-
-  if (!pWidgetHandler)
-    return true;
-
-  CXFA_FFWidget* hWidget = *pNewAnnot ? (*pNewAnnot)->GetXFAWidget() : nullptr;
-  if (!hWidget)
-    return true;
-
-  CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
-  if (!pXFAPageView)
-    return true;
-
-  bool bRet = pXFAPageView->GetDocView()->SetFocus(hWidget);
-  if (pXFAPageView->GetDocView()->GetFocusWidget() == hWidget)
-    bRet = true;
-
-  return bRet;
-}
-
-CXFA_FFWidgetHandler* CPDFSDK_XFAWidgetHandler::GetXFAWidgetHandler(
-    CPDFSDK_Annot* pAnnot) {
-  if (!pAnnot)
-    return nullptr;
-
-  CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
-  if (!pPageView)
-    return nullptr;
-
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return nullptr;
-
-  CPDFXFA_Context* pDoc = pFormFillEnv->GetXFAContext();
-  if (!pDoc)
-    return nullptr;
-
-  CXFA_FFDocView* pDocView = pDoc->GetXFADocView();
-  if (!pDocView)
-    return nullptr;
-
-  return pDocView->GetWidgetHandler();
-}
-
-uint32_t CPDFSDK_XFAWidgetHandler::GetFWLFlags(uint32_t dwFlag) {
-  uint32_t dwFWLFlag = 0;
-
-  if (dwFlag & FWL_EVENTFLAG_ControlKey)
-    dwFWLFlag |= FWL_KEYFLAG_Ctrl;
-  if (dwFlag & FWL_EVENTFLAG_LeftButtonDown)
-    dwFWLFlag |= FWL_KEYFLAG_LButton;
-  if (dwFlag & FWL_EVENTFLAG_MiddleButtonDown)
-    dwFWLFlag |= FWL_KEYFLAG_MButton;
-  if (dwFlag & FWL_EVENTFLAG_RightButtonDown)
-    dwFWLFlag |= FWL_KEYFLAG_RButton;
-  if (dwFlag & FWL_EVENTFLAG_ShiftKey)
-    dwFWLFlag |= FWL_KEYFLAG_Shift;
-  if (dwFlag & FWL_EVENTFLAG_AltKey)
-    dwFWLFlag |= FWL_KEYFLAG_Alt;
-
-  return dwFWLFlag;
-}
diff --git a/fpdfsdk/cpdfsdk_xfawidgethandler.h b/fpdfsdk/cpdfsdk_xfawidgethandler.h
deleted file mode 100644
index e0dccbe..0000000
--- a/fpdfsdk/cpdfsdk_xfawidgethandler.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_CPDFSDK_XFAWIDGETHANDLER_H_
-#define FPDFSDK_CPDFSDK_XFAWIDGETHANDLER_H_
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/ipdfsdk_annothandler.h"
-
-class CFX_Matrix;
-class CFX_RenderDevice;
-class CPDF_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDFSDK_Annot;
-class CPDFSDK_PageView;
-class CXFA_FFWidget;
-class CXFA_FFWidgetHandler;
-
-class CPDFSDK_XFAWidgetHandler : public IPDFSDK_AnnotHandler {
- public:
-  explicit CPDFSDK_XFAWidgetHandler(CPDFSDK_FormFillEnvironment* pApp);
-  ~CPDFSDK_XFAWidgetHandler() override;
-
-  bool CanAnswer(CPDFSDK_Annot* pAnnot) override;
-  CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override;
-  CPDFSDK_Annot* NewAnnot(CXFA_FFWidget* pAnnot,
-                          CPDFSDK_PageView* pPage) override;
-  void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override;
-  CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
-                            CPDFSDK_Annot* pAnnot) override;
-  WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
-  void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override;
-  bool HitTest(CPDFSDK_PageView* pPageView,
-               CPDFSDK_Annot* pAnnot,
-               const CFX_PointF& point) override;
-  void OnDraw(CPDFSDK_PageView* pPageView,
-              CPDFSDK_Annot* pAnnot,
-              CFX_RenderDevice* pDevice,
-              CFX_Matrix* pUser2Device,
-              bool bDrawAnnots) override;
-  void OnLoad(CPDFSDK_Annot* pAnnot) override;
-  void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
-                    uint32_t nFlag) override;
-  void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
-                   uint32_t nFlag) override;
-  bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot::ObservedPtr* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
-                    uint32_t nFlags,
-                    short zDelta,
-                    const CFX_PointF& point) override;
-  bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
-                     uint32_t nFlags,
-                     const CFX_PointF& point) override;
-  bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
-                   uint32_t nFlags,
-                   const CFX_PointF& point) override;
-  bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot::ObservedPtr* pAnnot,
-                       uint32_t nFlags,
-                       const CFX_PointF& point) override;
-  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
-  bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
-  bool OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag) override;
-  bool OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag) override;
-  bool OnXFAChangedFocus(CPDFSDK_Annot::ObservedPtr* pOldAnnot,
-                         CPDFSDK_Annot::ObservedPtr* pNewAnnot) override;
-
- private:
-  CXFA_FFWidgetHandler* GetXFAWidgetHandler(CPDFSDK_Annot* pAnnot);
-  uint32_t GetFWLFlags(uint32_t dwFlag);
-
-  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-};
-
-#endif  // FPDFSDK_CPDFSDK_XFAWIDGETHANDLER_H_
diff --git a/fpdfsdk/formfiller/Android.bp b/fpdfsdk/formfiller/Android.bp
new file mode 100644
index 0000000..5dc4ba6
--- /dev/null
+++ b/fpdfsdk/formfiller/Android.bp
@@ -0,0 +1,22 @@
+cc_library_static {
+    name: "libpdfium-formfiller",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-page",
+        "libpdfium-fpdfdoc",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+        "libpdfium-pwl",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+}
diff --git a/fpdfsdk/formfiller/BUILD.gn b/fpdfsdk/formfiller/BUILD.gn
new file mode 100644
index 0000000..e612ef4
--- /dev/null
+++ b/fpdfsdk/formfiller/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+
+source_set("formfiller") {
+  sources = [
+    "cffl_button.cpp",
+    "cffl_button.h",
+    "cffl_checkbox.cpp",
+    "cffl_checkbox.h",
+    "cffl_combobox.cpp",
+    "cffl_combobox.h",
+    "cffl_formfiller.cpp",
+    "cffl_formfiller.h",
+    "cffl_interactiveformfiller.cpp",
+    "cffl_interactiveformfiller.h",
+    "cffl_listbox.cpp",
+    "cffl_listbox.h",
+    "cffl_pushbutton.cpp",
+    "cffl_pushbutton.h",
+    "cffl_radiobutton.cpp",
+    "cffl_radiobutton.h",
+    "cffl_textfield.cpp",
+    "cffl_textfield.h",
+    "cffl_textobject.cpp",
+    "cffl_textobject.h",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  deps = [
+    "../../:pdfium_public_headers",
+    "../../constants",
+    "../../core/fpdfapi/page",
+    "../../core/fpdfdoc",
+    "../../core/fxcrt",
+    "../../core/fxge",
+    "../pwl",
+  ]
+  visibility = [ "../../*" ]
+}
diff --git a/fpdfsdk/formfiller/cba_fontmap.cpp b/fpdfsdk/formfiller/cba_fontmap.cpp
deleted file mode 100644
index d675676..0000000
--- a/fpdfsdk/formfiller/cba_fontmap.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/formfiller/cba_fontmap.h"
-
-#include <utility>
-
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_simple_parser.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fxge/cfx_substfont.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-
-CBA_FontMap::CBA_FontMap(CPDFSDK_Annot* pAnnot,
-                         CFX_SystemHandler* pSystemHandler)
-    : CPWL_FontMap(pSystemHandler),
-      m_pDocument(nullptr),
-      m_pAnnotDict(nullptr),
-      m_pDefaultFont(nullptr),
-      m_sAPType("N") {
-  CPDF_Page* pPage = pAnnot->GetPDFPage();
-
-  m_pDocument = pPage->m_pDocument.Get();
-  m_pAnnotDict = pAnnot->GetPDFAnnot()->GetAnnotDict();
-  Initialize();
-}
-
-CBA_FontMap::~CBA_FontMap() {}
-
-void CBA_FontMap::Reset() {
-  Empty();
-  m_pDefaultFont = nullptr;
-  m_sDefaultFontName.clear();
-}
-
-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.Get(), m_sDefaultFontName, nCharset);
-      AddFontToAnnotDict(m_pDefaultFont.Get(), m_sDefaultFontName);
-    }
-  }
-
-  if (nCharset != FX_CHARSET_ANSI)
-    CPWL_FontMap::Initialize();
-}
-
-void CBA_FontMap::SetDefaultFont(CPDF_Font* pFont,
-                                 const ByteString& sFontName) {
-  ASSERT(pFont);
-
-  if (m_pDefaultFont)
-    return;
-
-  m_pDefaultFont = pFont;
-  m_sDefaultFontName = sFontName;
-
-  int32_t nCharset = FX_CHARSET_Default;
-  if (const CFX_SubstFont* pSubstFont = m_pDefaultFont->GetSubstFont())
-    nCharset = pSubstFont->m_Charset;
-  AddFontData(m_pDefaultFont.Get(), m_sDefaultFontName, nCharset);
-}
-
-CPDF_Font* CBA_FontMap::FindFontSameCharset(ByteString* sFontAlias,
-                                            int32_t nCharset) {
-  if (m_pAnnotDict->GetStringFor("Subtype") != "Widget")
-    return nullptr;
-
-  CPDF_Document* pDocument = GetDocument();
-  const CPDF_Dictionary* pRootDict = pDocument->GetRoot();
-  if (!pRootDict)
-    return nullptr;
-
-  CPDF_Dictionary* pAcroFormDict = pRootDict->GetDictFor("AcroForm");
-  if (!pAcroFormDict)
-    return nullptr;
-
-  CPDF_Dictionary* pDRDict = pAcroFormDict->GetDictFor("DR");
-  if (!pDRDict)
-    return nullptr;
-
-  return FindResFontSameCharset(pDRDict, sFontAlias, nCharset);
-}
-
-CPDF_Document* CBA_FontMap::GetDocument() {
-  return m_pDocument.Get();
-}
-
-CPDF_Font* CBA_FontMap::FindResFontSameCharset(CPDF_Dictionary* pResDict,
-                                               ByteString* sFontAlias,
-                                               int32_t nCharset) {
-  if (!pResDict)
-    return nullptr;
-
-  CPDF_Dictionary* pFonts = pResDict->GetDictFor("Font");
-  if (!pFonts)
-    return nullptr;
-
-  CPDF_Document* pDocument = GetDocument();
-  CPDF_Font* pFind = nullptr;
-  for (const auto& it : *pFonts) {
-    const ByteString& csKey = it.first;
-    if (!it.second)
-      continue;
-
-    CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
-    if (!pElement)
-      continue;
-    if (pElement->GetStringFor("Type") != "Font")
-      continue;
-
-    CPDF_Font* pFont = pDocument->LoadFont(pElement);
-    if (!pFont)
-      continue;
-    const CFX_SubstFont* pSubst = pFont->GetSubstFont();
-    if (!pSubst)
-      continue;
-    if (pSubst->m_Charset == nCharset) {
-      *sFontAlias = csKey;
-      pFind = pFont;
-    }
-  }
-  return pFind;
-}
-
-void CBA_FontMap::AddedFont(CPDF_Font* pFont, const ByteString& sFontAlias) {
-  AddFontToAnnotDict(pFont, sFontAlias);
-}
-
-void CBA_FontMap::AddFontToAnnotDict(CPDF_Font* pFont,
-                                     const ByteString& sAlias) {
-  if (!pFont)
-    return;
-
-  CPDF_Dictionary* pAPDict = m_pAnnotDict->GetDictFor("AP");
-  if (!pAPDict)
-    pAPDict = m_pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
-
-  // to avoid checkbox and radiobutton
-  CPDF_Object* pObject = pAPDict->GetObjectFor(m_sAPType);
-  if (ToDictionary(pObject))
-    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 =
-        pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
-    pStreamDict = pOwnedDict.get();
-    pStream->InitStream(nullptr, 0, 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)) {
-    pStreamResFontList->SetNewFor<CPDF_Reference>(
-        sAlias, m_pDocument.Get(), pFont->GetFontDict()->GetObjNum());
-  }
-}
-
-CPDF_Font* CBA_FontMap::GetAnnotDefaultFont(ByteString* sAlias) {
-  CPDF_Dictionary* pAcroFormDict = nullptr;
-  const bool bWidget = (m_pAnnotDict->GetStringFor("Subtype") == "Widget");
-  if (bWidget) {
-    const CPDF_Dictionary* pRootDict = m_pDocument->GetRoot();
-    if (pRootDict)
-      pAcroFormDict = pRootDict->GetDictFor("AcroForm");
-  }
-
-  ByteString sDA;
-  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pAnnotDict.Get(), "DA");
-  if (pObj)
-    sDA = pObj->GetString();
-
-  if (bWidget) {
-    if (sDA.IsEmpty()) {
-      pObj = FPDF_GetFieldAttr(pAcroFormDict, "DA");
-      sDA = pObj ? pObj->GetString() : ByteString();
-    }
-  }
-  if (sDA.IsEmpty())
-    return nullptr;
-
-  CPDF_SimpleParser syntax(sDA.AsStringView());
-  syntax.FindTagParamFromStart("Tf", 2);
-
-  ByteString sFontName(syntax.GetWord());
-  ByteString sDecodedFontName = PDF_NameDecode(sFontName);
-  *sAlias = sDecodedFontName.Right(sDecodedFontName.GetLength() - 1);
-
-  CPDF_Dictionary* pFontDict = nullptr;
-  if (CPDF_Dictionary* pAPDict = m_pAnnotDict->GetDictFor("AP")) {
-    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);
-    }
-  }
-  return pFontDict ? m_pDocument->LoadFont(pFontDict) : nullptr;
-}
-
-void CBA_FontMap::SetAPType(const ByteString& sAPType) {
-  m_sAPType = sAPType;
-
-  Reset();
-  Initialize();
-}
diff --git a/fpdfsdk/formfiller/cba_fontmap.h b/fpdfsdk/formfiller/cba_fontmap.h
deleted file mode 100644
index 45df8c8..0000000
--- a/fpdfsdk/formfiller/cba_fontmap.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FORMFILLER_CBA_FONTMAP_H_
-#define FPDFSDK_FORMFILLER_CBA_FONTMAP_H_
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/pwl/cpwl_font_map.h"
-
-class CPDF_Dictionary;
-class CPDFSDK_Annot;
-
-class CBA_FontMap : public CPWL_FontMap {
- public:
-  CBA_FontMap(CPDFSDK_Annot* pAnnot, CFX_SystemHandler* pSystemHandler);
-  ~CBA_FontMap() override;
-
-  void Reset();
-  void SetDefaultFont(CPDF_Font* pFont, const ByteString& sFontName);
-  void SetAPType(const ByteString& sAPType);
-
- private:
-  // CPWL_FontMap:
-  void Initialize() override;
-  CPDF_Document* GetDocument() override;
-  CPDF_Font* FindFontSameCharset(ByteString* sFontAlias,
-                                 int32_t nCharset) override;
-  void AddedFont(CPDF_Font* pFont, const ByteString& sFontAlias) override;
-
-  CPDF_Font* FindResFontSameCharset(CPDF_Dictionary* pResDict,
-                                    ByteString* sFontAlias,
-                                    int32_t nCharset);
-  CPDF_Font* GetAnnotDefaultFont(ByteString* csNameTag);
-  void AddFontToAnnotDict(CPDF_Font* pFont, const ByteString& sAlias);
-
-  UnownedPtr<CPDF_Document> m_pDocument;
-  UnownedPtr<CPDF_Dictionary> m_pAnnotDict;
-  UnownedPtr<CPDF_Font> m_pDefaultFont;
-  ByteString m_sDefaultFontName;
-  ByteString m_sAPType;
-};
-
-#endif  // FPDFSDK_FORMFILLER_CBA_FONTMAP_H_
diff --git a/fpdfsdk/formfiller/cffl_button.cpp b/fpdfsdk/formfiller/cffl_button.cpp
index dab0045..3520927 100644
--- a/fpdfsdk/formfiller/cffl_button.cpp
+++ b/fpdfsdk/formfiller/cffl_button.cpp
@@ -6,23 +6,25 @@
 
 #include "fpdfsdk/formfiller/cffl_button.h"
 
-CFFL_Button::CFFL_Button(CPDFSDK_FormFillEnvironment* pApp,
+#include "core/fpdfdoc/cpdf_formcontrol.h"
+
+CFFL_Button::CFFL_Button(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                          CPDFSDK_Widget* pWidget)
-    : CFFL_FormFiller(pApp, pWidget), m_bMouseIn(false), m_bMouseDown(false) {}
+    : CFFL_FormFiller(pFormFillEnv, pWidget),
+      m_bMouseIn(false),
+      m_bMouseDown(false) {}
 
 CFFL_Button::~CFFL_Button() {}
 
-void CFFL_Button::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                               CPDFSDK_Annot* pAnnot) {
+void CFFL_Button::OnMouseEnter(CPDFSDK_PageView* pPageView) {
   m_bMouseIn = true;
-  InvalidateRect(GetViewBBox(pPageView, pAnnot));
+  InvalidateRect(GetViewBBox(pPageView));
 }
 
-void CFFL_Button::OnMouseExit(CPDFSDK_PageView* pPageView,
-                              CPDFSDK_Annot* pAnnot) {
+void CFFL_Button::OnMouseExit(CPDFSDK_PageView* pPageView) {
   m_bMouseIn = false;
-  InvalidateRect(GetViewBBox(pPageView, pAnnot));
-  EndTimer();
+  InvalidateRect(GetViewBBox(pPageView));
+  m_pTimer.reset();
   ASSERT(m_pWidget);
 }
 
@@ -35,7 +37,7 @@
 
   m_bMouseDown = true;
   m_bValid = true;
-  InvalidateRect(GetViewBBox(pPageView, pAnnot));
+  InvalidateRect(GetViewBBox(pPageView));
   return true;
 }
 
@@ -48,12 +50,11 @@
 
   m_bMouseDown = false;
   m_pWidget->GetPDFPage();
-  InvalidateRect(GetViewBBox(pPageView, pAnnot));
+  InvalidateRect(GetViewBBox(pPageView));
   return true;
 }
 
 bool CFFL_Button::OnMouseMove(CPDFSDK_PageView* pPageView,
-                              CPDFSDK_Annot* pAnnot,
                               uint32_t nFlags,
                               const CFX_PointF& point) {
   return true;
@@ -64,7 +65,7 @@
                          CFX_RenderDevice* pDevice,
                          const CFX_Matrix& mtUser2Device) {
   ASSERT(pPageView);
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot);
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
   CPDF_FormControl* pCtrl = pWidget->GetFormControl();
   if (pCtrl->GetHighlightingMode() != CPDF_FormControl::Push) {
     pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal,
diff --git a/fpdfsdk/formfiller/cffl_button.h b/fpdfsdk/formfiller/cffl_button.h
index ad2eb5b..768c144 100644
--- a/fpdfsdk/formfiller/cffl_button.h
+++ b/fpdfsdk/formfiller/cffl_button.h
@@ -24,9 +24,8 @@
   ~CFFL_Button() override;
 
   // CFFL_FormFiller
-  void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot* pAnnot) override;
-  void OnMouseExit(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot) override;
+  void OnMouseEnter(CPDFSDK_PageView* pPageView) override;
+  void OnMouseExit(CPDFSDK_PageView* pPageView) override;
   bool OnLButtonDown(CPDFSDK_PageView* pPageView,
                      CPDFSDK_Annot* pAnnot,
                      uint32_t nFlags,
@@ -36,7 +35,6 @@
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point) override;
   void OnDraw(CPDFSDK_PageView* pPageView,
diff --git a/fpdfsdk/formfiller/cffl_checkbox.cpp b/fpdfsdk/formfiller/cffl_checkbox.cpp
index e9c72ef..b0e60e2 100644
--- a/fpdfsdk/formfiller/cffl_checkbox.cpp
+++ b/fpdfsdk/formfiller/cffl_checkbox.cpp
@@ -6,11 +6,15 @@
 
 #include "fpdfsdk/formfiller/cffl_checkbox.h"
 
+#include <utility>
+
+#include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
 #include "fpdfsdk/pwl/cpwl_special_button.h"
 #include "public/fpdf_fwlevent.h"
+#include "third_party/base/ptr_util.h"
 
 CFFL_CheckBox::CFFL_CheckBox(CPDFSDK_FormFillEnvironment* pApp,
                              CPDFSDK_Widget* pWidget)
@@ -18,22 +22,22 @@
 
 CFFL_CheckBox::~CFFL_CheckBox() {}
 
-CPWL_Wnd* CFFL_CheckBox::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) {
-  auto* pWnd = new CPWL_CheckBox();
-  pWnd->Create(cp);
+std::unique_ptr<CPWL_Wnd> CFFL_CheckBox::NewPWLWindow(
+    const CPWL_Wnd::CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
+  auto pWnd = pdfium::MakeUnique<CPWL_CheckBox>(cp, std::move(pAttachedData));
+  pWnd->Realize();
   pWnd->SetCheck(m_pWidget->IsChecked());
-  return pWnd;
+  return std::move(pWnd);
 }
 
-bool CFFL_CheckBox::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                              uint32_t nKeyCode,
-                              uint32_t nFlags) {
+bool CFFL_CheckBox::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) {
   switch (nKeyCode) {
     case FWL_VKEY_Return:
     case FWL_VKEY_Space:
       return true;
     default:
-      return CFFL_FormFiller::OnKeyDown(pAnnot, nKeyCode, nFlags);
+      return CFFL_FormFiller::OnKeyDown(nKeyCode, nFlags);
   }
 }
 bool CFFL_CheckBox::OnChar(CPDFSDK_Annot* pAnnot,
@@ -45,7 +49,7 @@
       CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
       ASSERT(pPageView);
 
-      CPDFSDK_Annot::ObservedPtr pObserved(m_pWidget.Get());
+      ObservedPtr<CPDFSDK_Annot> pObserved(m_pWidget.Get());
       if (m_pFormFillEnv->GetInteractiveFormFiller()->OnButtonUp(
               &pObserved, pPageView, nFlags)) {
         if (!pObserved)
@@ -61,7 +65,7 @@
 
       CPWL_CheckBox* pWnd = GetCheckBox(pPageView, true);
       if (pWnd) {
-        CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot);
+        CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
         pWnd->SetCheck(!pWidget->IsChecked());
       }
 
@@ -83,7 +87,7 @@
 
   CPWL_CheckBox* pWnd = GetCheckBox(pPageView, true);
   if (pWnd) {
-    CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot);
+    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
     pWnd->SetCheck(!pWidget->IsChecked());
   }
 
@@ -111,19 +115,20 @@
       }
     }
   }
-  CPDFSDK_Widget::ObservedPtr observed_widget(m_pWidget.Get());
-  CFFL_CheckBox::ObservedPtr observed_this(this);
-
-  m_pWidget->SetCheck(bNewChecked, false);
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CFFL_CheckBox> observed_this(this);
+  m_pWidget->SetCheck(bNewChecked, NotificationOption::kDoNotNotify);
   if (!observed_widget)
     return;
+
   m_pWidget->UpdateField();
   if (!observed_widget || !observed_this)
     return;
+
   SetChangeMark();
 }
 
 CPWL_CheckBox* CFFL_CheckBox::GetCheckBox(CPDFSDK_PageView* pPageView,
                                           bool bNew) {
-  return static_cast<CPWL_CheckBox*>(GetPDFWindow(pPageView, bNew));
+  return static_cast<CPWL_CheckBox*>(GetPWLWindow(pPageView, bNew));
 }
diff --git a/fpdfsdk/formfiller/cffl_checkbox.h b/fpdfsdk/formfiller/cffl_checkbox.h
index 8ef3de6..aff2762 100644
--- a/fpdfsdk/formfiller/cffl_checkbox.h
+++ b/fpdfsdk/formfiller/cffl_checkbox.h
@@ -7,20 +7,23 @@
 #ifndef FPDFSDK_FORMFILLER_CFFL_CHECKBOX_H_
 #define FPDFSDK_FORMFILLER_CFFL_CHECKBOX_H_
 
+#include <memory>
+
 #include "fpdfsdk/formfiller/cffl_button.h"
 
 class CPWL_CheckBox;
 
-class CFFL_CheckBox : public CFFL_Button {
+class CFFL_CheckBox final : public CFFL_Button {
  public:
   CFFL_CheckBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
   ~CFFL_CheckBox() override;
 
   // CFFL_Button:
-  CPWL_Wnd* NewPDFWindow(const CPWL_Wnd::CreateParams& cp) override;
-  bool OnKeyDown(CPDFSDK_Annot* pAnnot,
-                 uint32_t nKeyCode,
-                 uint32_t nFlags) override;
+  std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+      override;
+  bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) override;
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
                    CPDFSDK_Annot* pAnnot,
diff --git a/fpdfsdk/formfiller/cffl_combobox.cpp b/fpdfsdk/formfiller/cffl_combobox.cpp
index d9b12f5..588c50c 100644
--- a/fpdfsdk/formfiller/cffl_combobox.cpp
+++ b/fpdfsdk/formfiller/cffl_combobox.cpp
@@ -6,20 +6,19 @@
 
 #include "fpdfsdk/formfiller/cffl_combobox.h"
 
+#include <utility>
+
+#include "constants/form_flags.h"
+#include "core/fpdfdoc/cba_fontmap.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cba_fontmap.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
-#include "fpdfsdk/fsdk_common.h"
 #include "fpdfsdk/pwl/cpwl_combo_box.h"
 #include "third_party/base/ptr_util.h"
 
 CFFL_ComboBox::CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp,
                              CPDFSDK_Widget* pWidget)
     : CFFL_TextObject(pApp, pWidget) {
-  m_State.nIndex = 0;
-  m_State.nStart = 0;
-  m_State.nEnd = 0;
 }
 
 CFFL_ComboBox::~CFFL_ComboBox() {
@@ -34,7 +33,7 @@
 
 CPWL_Wnd::CreateParams CFFL_ComboBox::GetCreateParam() {
   CPWL_Wnd::CreateParams cp = CFFL_TextObject::GetCreateParam();
-  if (m_pWidget->GetFieldFlags() & FIELDFLAG_EDIT)
+  if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit)
     cp.dwFlags |= PCBS_ALLOWCUSTOMTEXT;
 
   cp.pFontMap = MaybeCreateFontMap();
@@ -42,10 +41,12 @@
   return cp;
 }
 
-CPWL_Wnd* CFFL_ComboBox::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) {
-  auto* pWnd = new CPWL_ComboBox();
+std::unique_ptr<CPWL_Wnd> CFFL_ComboBox::NewPWLWindow(
+    const CPWL_Wnd::CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
+  auto pWnd = pdfium::MakeUnique<CPWL_ComboBox>(cp, std::move(pAttachedData));
   pWnd->AttachFFLData(this);
-  pWnd->Create(cp);
+  pWnd->Realize();
 
   CFFL_InteractiveFormFiller* pFormFiller =
       m_pFormFillEnv->GetInteractiveFormFiller();
@@ -58,13 +59,12 @@
   else
     swText = m_pWidget->GetOptionLabel(nCurSel);
 
-  for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++) {
+  for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++)
     pWnd->AddString(m_pWidget->GetOptionLabel(i));
-  }
 
   pWnd->SetSelect(nCurSel);
   pWnd->SetText(swText);
-  return pWnd;
+  return std::move(pWnd);
 }
 
 bool CFFL_ComboBox::OnChar(CPDFSDK_Annot* pAnnot,
@@ -74,12 +74,12 @@
 }
 
 bool CFFL_ComboBox::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  CPWL_ComboBox* pWnd = (CPWL_ComboBox*)GetPDFWindow(pPageView, false);
+  auto* pWnd = GetComboBox(pPageView, false);
   if (!pWnd)
     return false;
 
   int32_t nCurSel = pWnd->GetSelect();
-  if (!(m_pWidget->GetFieldFlags() & FIELDFLAG_EDIT))
+  if (!(m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit))
     return nCurSel != m_pWidget->GetSelectedIndex(0);
 
   if (nCurSel >= 0)
@@ -89,45 +89,43 @@
 }
 
 void CFFL_ComboBox::SaveData(CPDFSDK_PageView* pPageView) {
-  CPWL_ComboBox* pWnd =
-      static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false));
+  CPWL_ComboBox* pWnd = GetComboBox(pPageView, false);
   if (!pWnd)
     return;
 
   WideString swText = pWnd->GetText();
   int32_t nCurSel = pWnd->GetSelect();
-
   bool bSetValue = false;
-
-  if (m_pWidget->GetFieldFlags() & FIELDFLAG_EDIT)
+  if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit)
     bSetValue = (nCurSel < 0) || (swText != m_pWidget->GetOptionLabel(nCurSel));
 
   if (bSetValue) {
-    m_pWidget->SetValue(swText, false);
+    m_pWidget->SetValue(swText, NotificationOption::kDoNotNotify);
   } else {
     m_pWidget->GetSelectedIndex(0);
-    m_pWidget->SetOptionSelection(nCurSel, true, false);
+    m_pWidget->SetOptionSelection(nCurSel, true,
+                                  NotificationOption::kDoNotNotify);
   }
-  CPDFSDK_Widget::ObservedPtr observed_widget(m_pWidget.Get());
-  CFFL_ComboBox::ObservedPtr observed_this(this);
-
-  m_pWidget->ResetFieldAppearance(true);
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CFFL_ComboBox> observed_this(this);
+  m_pWidget->ResetFieldAppearance();
   if (!observed_widget)
     return;
+
   m_pWidget->UpdateField();
   if (!observed_widget || !observed_this)
     return;
+
   SetChangeMark();
   m_pWidget->GetPDFPage();
 }
 
 void CFFL_ComboBox::GetActionData(CPDFSDK_PageView* pPageView,
                                   CPDF_AAction::AActionType type,
-                                  PDFSDK_FieldAction& fa) {
+                                  CPDFSDK_FieldAction& fa) {
   switch (type) {
-    case CPDF_AAction::KeyStroke:
-      if (CPWL_ComboBox* pComboBox =
-              static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
+    case CPDF_AAction::kKeyStroke:
+      if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
           fa.bFieldFull = pEdit->IsTextFull();
           int nSelStart = 0;
@@ -139,22 +137,21 @@
           fa.sChangeEx = GetSelectExportText();
 
           if (fa.bFieldFull) {
-            fa.sChange = L"";
-            fa.sChangeEx = L"";
+            fa.sChange.clear();
+            fa.sChangeEx.clear();
           }
         }
       }
       break;
-    case CPDF_AAction::Validate:
-      if (CPWL_ComboBox* pComboBox =
-              static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
+    case CPDF_AAction::kValidate:
+      if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
           fa.sValue = pEdit->GetText();
         }
       }
       break;
-    case CPDF_AAction::LoseFocus:
-    case CPDF_AAction::GetFocus:
+    case CPDF_AAction::kLoseFocus:
+    case CPDF_AAction::kGetFocus:
       fa.sValue = m_pWidget->GetValue();
       break;
     default:
@@ -164,14 +161,13 @@
 
 void CFFL_ComboBox::SetActionData(CPDFSDK_PageView* pPageView,
                                   CPDF_AAction::AActionType type,
-                                  const PDFSDK_FieldAction& fa) {
+                                  const CPDFSDK_FieldAction& fa) {
   switch (type) {
-    case CPDF_AAction::KeyStroke:
-      if (CPWL_ComboBox* pComboBox =
-              static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
+    case CPDF_AAction::kKeyStroke:
+      if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
           pEdit->SetSelection(fa.nSelStart, fa.nSelEnd);
-          pEdit->ReplaceSel(fa.sChange);
+          pEdit->ReplaceSelection(fa.sChange);
         }
       }
       break;
@@ -180,59 +176,73 @@
   }
 }
 
-bool CFFL_ComboBox::IsActionDataChanged(CPDF_AAction::AActionType type,
-                                        const PDFSDK_FieldAction& faOld,
-                                        const PDFSDK_FieldAction& faNew) {
-  switch (type) {
-    case CPDF_AAction::KeyStroke:
-      return (!faOld.bFieldFull && faOld.nSelEnd != faNew.nSelEnd) ||
-             faOld.nSelStart != faNew.nSelStart ||
-             faOld.sChange != faNew.sChange;
-    default:
-      break;
-  }
-
-  return false;
-}
-
 void CFFL_ComboBox::SaveState(CPDFSDK_PageView* pPageView) {
-  ASSERT(pPageView);
+  CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false);
+  if (!pComboBox)
+    return;
 
-  if (CPWL_ComboBox* pComboBox =
-          static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
-    m_State.nIndex = pComboBox->GetSelect();
+  m_State.nIndex = pComboBox->GetSelect();
 
-    if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
-      pEdit->GetSelection(m_State.nStart, m_State.nEnd);
-      m_State.sValue = pEdit->GetText();
-    }
-  }
+  CPWL_Edit* pEdit = pComboBox->GetEdit();
+  if (!pEdit)
+    return;
+
+  pEdit->GetSelection(m_State.nStart, m_State.nEnd);
+  m_State.sValue = pEdit->GetText();
 }
 
 void CFFL_ComboBox::RestoreState(CPDFSDK_PageView* pPageView) {
-  ASSERT(pPageView);
+  CPWL_ComboBox* pComboBox = GetComboBox(pPageView, true);
+  if (!pComboBox)
+    return;
 
-  if (CPWL_ComboBox* pComboBox =
-          static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, true))) {
-    if (m_State.nIndex >= 0) {
-      pComboBox->SetSelect(m_State.nIndex);
-    } else {
-      if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
-        pEdit->SetText(m_State.sValue);
-        pEdit->SetSelection(m_State.nStart, m_State.nEnd);
-      }
-    }
+  if (m_State.nIndex >= 0) {
+    pComboBox->SetSelect(m_State.nIndex);
+    return;
   }
+
+  CPWL_Edit* pEdit = pComboBox->GetEdit();
+  if (!pEdit)
+    return;
+
+  pEdit->SetText(m_State.sValue);
+  pEdit->SetSelection(m_State.nStart, m_State.nEnd);
+}
+
+bool CFFL_ComboBox::SetIndexSelected(int index, bool selected) {
+  if (!IsValid() || !selected)
+    return false;
+
+  if (index < 0 || index >= m_pWidget->CountOptions())
+    return false;
+
+  CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false);
+  if (!pWnd)
+    return false;
+
+  pWnd->SetSelect(index);
+  return true;
+}
+
+bool CFFL_ComboBox::IsIndexSelected(int index) {
+  if (!IsValid())
+    return false;
+
+  if (index < 0 || index >= m_pWidget->CountOptions())
+    return false;
+
+  CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false);
+  return pWnd && index == pWnd->GetSelect();
 }
 
 #ifdef PDF_ENABLE_XFA
 bool CFFL_ComboBox::IsFieldFull(CPDFSDK_PageView* pPageView) {
-  if (CPWL_ComboBox* pComboBox =
-          static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
-    if (CPWL_Edit* pEdit = pComboBox->GetEdit())
-      return pEdit->IsTextFull();
-  }
-  return false;
+  CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false);
+  if (!pComboBox)
+    return false;
+
+  CPWL_Edit* pEdit = pComboBox->GetEdit();
+  return pEdit && pEdit->IsTextFull();
 }
 #endif  // PDF_ENABLE_XFA
 
@@ -242,7 +252,7 @@
 
   WideString wsText = pEdit->GetText();
   int nCharacters = wsText.GetLength();
-  ByteString bsUTFText = wsText.UTF16LE_Encode();
+  ByteString bsUTFText = wsText.ToUTF16LE();
   auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str());
   m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true);
 }
@@ -250,12 +260,8 @@
 WideString CFFL_ComboBox::GetSelectExportText() {
   WideString swRet;
 
-  int nExport = -1;
-  CPDFSDK_PageView* pPageView = GetCurPageView(true);
-  if (CPWL_ComboBox* pComboBox =
-          (CPWL_ComboBox*)GetPDFWindow(pPageView, false)) {
-    nExport = pComboBox->GetSelect();
-  }
+  CPWL_ComboBox* pComboBox = GetComboBox(GetCurPageView(true), false);
+  int nExport = pComboBox ? pComboBox->GetSelect() : -1;
 
   if (nExport >= 0) {
     if (CPDF_FormField* pFormField = m_pWidget->GetFormField()) {
@@ -267,3 +273,8 @@
 
   return swRet;
 }
+
+CPWL_ComboBox* CFFL_ComboBox::GetComboBox(CPDFSDK_PageView* pPageView,
+                                          bool bNew) {
+  return static_cast<CPWL_ComboBox*>(GetPWLWindow(pPageView, bNew));
+}
diff --git a/fpdfsdk/formfiller/cffl_combobox.h b/fpdfsdk/formfiller/cffl_combobox.h
index bdc0934..250dba7 100644
--- a/fpdfsdk/formfiller/cffl_combobox.h
+++ b/fpdfsdk/formfiller/cffl_combobox.h
@@ -7,41 +7,45 @@
 #ifndef FPDFSDK_FORMFILLER_CFFL_COMBOBOX_H_
 #define FPDFSDK_FORMFILLER_CFFL_COMBOBOX_H_
 
+#include <memory>
+
 #include "core/fxcrt/fx_string.h"
 #include "fpdfsdk/formfiller/cffl_textobject.h"
 
-class CBA_FontMap;
+class CPWL_ComboBox;
 
 struct FFL_ComboBoxState {
-  int nIndex;
-  int nStart;
-  int nEnd;
+  int nIndex = 0;
+  int nStart = 0;
+  int nEnd = 0;
   WideString sValue;
 };
 
-class CFFL_ComboBox : public CFFL_TextObject,
-                      public CPWL_Wnd::FocusHandlerIface {
+class CFFL_ComboBox final : public CFFL_TextObject,
+                            public CPWL_Wnd::FocusHandlerIface {
  public:
   CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
   ~CFFL_ComboBox() override;
 
   // CFFL_TextObject:
   CPWL_Wnd::CreateParams GetCreateParam() override;
-  CPWL_Wnd* NewPDFWindow(const CPWL_Wnd::CreateParams& cp) override;
+  std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+      override;
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
   bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
   void SaveData(CPDFSDK_PageView* pPageView) override;
   void GetActionData(CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     PDFSDK_FieldAction& fa) override;
+                     CPDFSDK_FieldAction& fa) override;
   void SetActionData(CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     const PDFSDK_FieldAction& fa) override;
-  bool IsActionDataChanged(CPDF_AAction::AActionType type,
-                           const PDFSDK_FieldAction& faOld,
-                           const PDFSDK_FieldAction& faNew) override;
+                     const CPDFSDK_FieldAction& fa) override;
   void SaveState(CPDFSDK_PageView* pPageView) override;
   void RestoreState(CPDFSDK_PageView* pPageView) override;
+  bool SetIndexSelected(int index, bool selected) override;
+  bool IsIndexSelected(int index) override;
 #ifdef PDF_ENABLE_XFA
   bool IsFieldFull(CPDFSDK_PageView* pPageView) override;
 #endif
@@ -51,6 +55,7 @@
 
  private:
   WideString GetSelectExportText();
+  CPWL_ComboBox* GetComboBox(CPDFSDK_PageView* pPageView, bool bNew);
 
   FFL_ComboBoxState m_State;
 };
diff --git a/fpdfsdk/formfiller/cffl_formfiller.cpp b/fpdfsdk/formfiller/cffl_formfiller.cpp
index d3aa37e..a214920 100644
--- a/fpdfsdk/formfiller/cffl_formfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_formfiller.cpp
@@ -8,25 +8,16 @@
 
 #include <utility>
 
+#include "constants/form_flags.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cba_fontmap.h"
-#include "fpdfsdk/fsdk_common.h"
-
-namespace {
-
-CPDFSDK_Widget* CPDFSDKAnnotToWidget(CPDFSDK_Annot* annot) {
-  return static_cast<CPDFSDK_Widget*>(annot);
-}
-
-}  // namespace
 
 CFFL_FormFiller::CFFL_FormFiller(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                                  CPDFSDK_Widget* pWidget)
-    : m_pFormFillEnv(pFormFillEnv), m_pWidget(pWidget), m_bValid(false) {
+    : m_pFormFillEnv(pFormFillEnv), m_pWidget(pWidget) {
   ASSERT(m_pFormFillEnv);
 }
 
@@ -35,35 +26,29 @@
 }
 
 void CFFL_FormFiller::DestroyWindows() {
-  for (const auto& it : m_Maps) {
-    CPWL_Wnd* pWnd = it.second;
-    auto* pData = static_cast<CFFL_PrivateData*>(pWnd->GetAttachedData());
+  while (!m_Maps.empty()) {
+    auto it = m_Maps.begin();
+    std::unique_ptr<CPWL_Wnd> pWnd = std::move(it->second);
+    m_Maps.erase(it);
     pWnd->InvalidateProvider(this);
     pWnd->Destroy();
-    delete pWnd;
-    delete pData;
   }
-  m_Maps.clear();
 }
 
-FX_RECT CFFL_FormFiller::GetViewBBox(CPDFSDK_PageView* pPageView,
-                                     CPDFSDK_Annot* pAnnot) {
-  ASSERT(pPageView);
-  ASSERT(pAnnot);
-
-  CFX_FloatRect rcAnnot = m_pWidget->GetRect();
-  if (CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false))
-    rcAnnot = PWLtoFFL(pWnd->GetWindowRect());
+FX_RECT CFFL_FormFiller::GetViewBBox(CPDFSDK_PageView* pPageView) {
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
+  CFX_FloatRect rcAnnot =
+      pWnd ? PWLtoFFL(pWnd->GetWindowRect()) : m_pWidget->GetRect();
+  CFX_FloatRect rcFocus = GetFocusBox(pPageView);
 
   CFX_FloatRect rcWin = rcAnnot;
-  CFX_FloatRect rcFocus = GetFocusBox(pPageView);
   if (!rcFocus.IsEmpty())
     rcWin.Union(rcFocus);
-
   if (!rcWin.IsEmpty()) {
     rcWin.Inflate(1, 1);
     rcWin.Normalize();
   }
+
   return rcWin.GetOuterRect();
 }
 
@@ -71,16 +56,13 @@
                              CPDFSDK_Annot* pAnnot,
                              CFX_RenderDevice* pDevice,
                              const CFX_Matrix& mtUser2Device) {
-  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-
-  if (CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false)) {
-    CFX_Matrix mt = GetCurMatrix();
-    mt.Concat(mtUser2Device);
-    pWnd->DrawAppearance(pDevice, mt);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
+  if (pWnd) {
+    pWnd->DrawAppearance(pDevice, GetCurMatrix() * mtUser2Device);
     return;
   }
 
-  CPDFSDK_Widget* pWidget = CPDFSDKAnnotToWidget(pAnnot);
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
   if (!CFFL_InteractiveFormFiller::IsVisible(pWidget))
     return;
 
@@ -91,16 +73,14 @@
                                      CPDFSDK_Annot* pAnnot,
                                      CFX_RenderDevice* pDevice,
                                      const CFX_Matrix& mtUser2Device) {
-  CPDFSDKAnnotToWidget(pAnnot)->DrawAppearance(pDevice, mtUser2Device,
-                                               CPDF_Annot::Normal, nullptr);
+  ToCPDFSDKWidget(pAnnot)->DrawAppearance(pDevice, mtUser2Device,
+                                          CPDF_Annot::Normal, nullptr);
 }
 
-void CFFL_FormFiller::OnMouseEnter(CPDFSDK_PageView* pPageView,
-                                   CPDFSDK_Annot* pAnnot) {}
+void CFFL_FormFiller::OnMouseEnter(CPDFSDK_PageView* pPageView) {}
 
-void CFFL_FormFiller::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                  CPDFSDK_Annot* pAnnot) {
-  EndTimer();
+void CFFL_FormFiller::OnMouseExit(CPDFSDK_PageView* pPageView) {
+  m_pTimer.reset();
   ASSERT(m_pWidget);
 }
 
@@ -108,101 +88,83 @@
                                     CPDFSDK_Annot* pAnnot,
                                     uint32_t nFlags,
                                     const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, true);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true);
   if (!pWnd)
     return false;
 
   m_bValid = true;
-  FX_RECT rect = GetViewBBox(pPageView, pAnnot);
+  FX_RECT rect = GetViewBBox(pPageView);
   InvalidateRect(rect);
   if (!rect.Contains(static_cast<int>(point.x), static_cast<int>(point.y)))
     return false;
-  return pWnd->OnLButtonDown(WndtoPWL(pPageView, point), nFlags);
+  return pWnd->OnLButtonDown(WndtoPWL(point), nFlags);
 }
 
 bool CFFL_FormFiller::OnLButtonUp(CPDFSDK_PageView* pPageView,
                                   CPDFSDK_Annot* pAnnot,
                                   uint32_t nFlags,
                                   const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
   if (!pWnd)
     return false;
 
-  InvalidateRect(GetViewBBox(pPageView, pAnnot));
-  pWnd->OnLButtonUp(WndtoPWL(pPageView, point), nFlags);
+  InvalidateRect(GetViewBBox(pPageView));
+  pWnd->OnLButtonUp(WndtoPWL(point), nFlags);
   return true;
 }
 
 bool CFFL_FormFiller::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                                      CPDFSDK_Annot* pAnnot,
                                       uint32_t nFlags,
                                       const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
   if (!pWnd)
     return false;
 
-  pWnd->OnLButtonDblClk(WndtoPWL(pPageView, point), nFlags);
+  pWnd->OnLButtonDblClk(WndtoPWL(point), nFlags);
   return true;
 }
 
 bool CFFL_FormFiller::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                  CPDFSDK_Annot* pAnnot,
                                   uint32_t nFlags,
                                   const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
   if (!pWnd)
     return false;
 
-  pWnd->OnMouseMove(WndtoPWL(pPageView, point), nFlags);
+  pWnd->OnMouseMove(WndtoPWL(point), nFlags);
   return true;
 }
 
 bool CFFL_FormFiller::OnMouseWheel(CPDFSDK_PageView* pPageView,
-                                   CPDFSDK_Annot* pAnnot,
                                    uint32_t nFlags,
                                    short zDelta,
                                    const CFX_PointF& point) {
   if (!IsValid())
     return false;
 
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, true);
-  return pWnd && pWnd->OnMouseWheel(zDelta, WndtoPWL(pPageView, point), nFlags);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true);
+  return pWnd && pWnd->OnMouseWheel(zDelta, WndtoPWL(point), nFlags);
 }
 
 bool CFFL_FormFiller::OnRButtonDown(CPDFSDK_PageView* pPageView,
-                                    CPDFSDK_Annot* pAnnot,
                                     uint32_t nFlags,
                                     const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, true);
-  if (!pWnd)
-    return false;
-
-  pWnd->OnRButtonDown(WndtoPWL(pPageView, point), nFlags);
-  return true;
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true);
+  return pWnd && pWnd->OnRButtonDown(WndtoPWL(point), nFlags);
 }
 
 bool CFFL_FormFiller::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                  CPDFSDK_Annot* pAnnot,
                                   uint32_t nFlags,
                                   const CFX_PointF& point) {
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
-  if (!pWnd)
-    return false;
-
-  pWnd->OnRButtonUp(WndtoPWL(pPageView, point), nFlags);
-  return true;
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
+  return pWnd && pWnd->OnRButtonUp(WndtoPWL(point), nFlags);
 }
 
-bool CFFL_FormFiller::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                uint32_t nKeyCode,
-                                uint32_t nFlags) {
+bool CFFL_FormFiller::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) {
   if (!IsValid())
     return false;
 
-  CPDFSDK_PageView* pPageView = GetCurPageView(true);
-  ASSERT(pPageView);
-
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
   return pWnd && pWnd->OnKeyDown(nKeyCode, nFlags);
 }
 
@@ -212,72 +174,110 @@
   if (!IsValid())
     return false;
 
-  CPDFSDK_PageView* pPageView = GetCurPageView(true);
-  ASSERT(pPageView);
-
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
   return pWnd && pWnd->OnChar(nChar, nFlags);
 }
 
-WideString CFFL_FormFiller::GetSelectedText(CPDFSDK_Annot* pAnnot) {
+bool CFFL_FormFiller::SetIndexSelected(int index, bool selected) {
+  return false;
+}
+
+bool CFFL_FormFiller::IsIndexSelected(int index) {
+  return false;
+}
+
+WideString CFFL_FormFiller::GetText() {
   if (!IsValid())
     return WideString();
 
-  CPDFSDK_PageView* pPageView = GetCurPageView(true);
-  ASSERT(pPageView);
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
+  return pWnd ? pWnd->GetText() : WideString();
+}
 
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+WideString CFFL_FormFiller::GetSelectedText() {
+  if (!IsValid())
+    return WideString();
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
   return pWnd ? pWnd->GetSelectedText() : WideString();
 }
 
-void CFFL_FormFiller::ReplaceSelection(CPDFSDK_Annot* pAnnot,
-                                       const WideString& text) {
+void CFFL_FormFiller::ReplaceSelection(const WideString& text) {
   if (!IsValid())
     return;
 
-  CPDFSDK_PageView* pPageView = GetCurPageView(true);
-  ASSERT(pPageView);
-
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
   if (!pWnd)
     return;
 
   pWnd->ReplaceSelection(text);
 }
 
+bool CFFL_FormFiller::CanUndo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
+  return pWnd && pWnd->CanUndo();
+}
+
+bool CFFL_FormFiller::CanRedo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
+  return pWnd && pWnd->CanRedo();
+}
+
+bool CFFL_FormFiller::Undo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
+  return pWnd && pWnd->Undo();
+}
+
+bool CFFL_FormFiller::Redo() {
+  if (!IsValid())
+    return false;
+
+  CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false);
+  return pWnd && pWnd->Redo();
+}
+
 void CFFL_FormFiller::SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag) {
-  CPDFSDK_Widget* pWidget = (CPDFSDK_Widget*)pAnnot;
-  UnderlyingPageType* pPage = pWidget->GetUnderlyingPage();
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
+  IPDF_Page* pPage = pWidget->GetPage();
   CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage, true);
-  if (CPWL_Wnd* pWnd = GetPDFWindow(pPageView, true))
+  if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true))
     pWnd->SetFocus();
 
   m_bValid = true;
-  InvalidateRect(GetViewBBox(pPageView, pAnnot));
+  InvalidateRect(GetViewBBox(pPageView));
 }
 
-void CFFL_FormFiller::KillFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag) {
+void CFFL_FormFiller::KillFocusForAnnot(uint32_t nFlag) {
   if (!IsValid())
     return;
 
   CPDFSDK_PageView* pPageView = GetCurPageView(false);
   if (!pPageView || !CommitData(pPageView, nFlag))
     return;
-  if (CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false))
+  if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false))
     pWnd->KillFocus();
 
-  bool bDestroyPDFWindow;
+  bool bDestroyPWLWindow;
   switch (m_pWidget->GetFieldType()) {
     case FormFieldType::kPushButton:
     case FormFieldType::kCheckBox:
     case FormFieldType::kRadioButton:
-      bDestroyPDFWindow = true;
+      bDestroyPWLWindow = true;
       break;
     default:
-      bDestroyPDFWindow = false;
+      bDestroyPWLWindow = false;
       break;
   }
-  EscapeFiller(pPageView, bDestroyPDFWindow);
+  EscapeFiller(pPageView, bDestroyPWLWindow);
 }
 
 bool CFFL_FormFiller::IsValid() const {
@@ -286,25 +286,26 @@
 
 CPWL_Wnd::CreateParams CFFL_FormFiller::GetCreateParam() {
   CPWL_Wnd::CreateParams cp;
-  cp.pParentWnd = nullptr;
   cp.pProvider.Reset(this);
-  cp.rcRectWnd = GetPDFWindowRect();
+  cp.rcRectWnd = GetPDFAnnotRect();
 
   uint32_t dwCreateFlags = PWS_BORDER | PWS_BACKGROUND | PWS_VISIBLE;
   uint32_t dwFieldFlag = m_pWidget->GetFieldFlags();
-  if (dwFieldFlag & FIELDFLAG_READONLY)
+  if (dwFieldFlag & pdfium::form_flags::kReadOnly)
     dwCreateFlags |= PWS_READONLY;
 
-  FX_COLORREF color;
-  if (m_pWidget->GetFillColor(color))
-    cp.sBackgroundColor = CFX_Color(color);
-  if (m_pWidget->GetBorderColor(color))
-    cp.sBorderColor = CFX_Color(color);
+  Optional<FX_COLORREF> color = m_pWidget->GetFillColor();
+  if (color.has_value())
+    cp.sBackgroundColor = CFX_Color(color.value());
+  color = m_pWidget->GetBorderColor();
+  if (color.has_value())
+    cp.sBorderColor = CFX_Color(color.value());
 
   cp.sTextColor = CFX_Color(CFX_Color::kGray, 0);
 
-  if (m_pWidget->GetTextColor(color))
-    cp.sTextColor = CFX_Color(color);
+  color = m_pWidget->GetTextColor();
+  if (color.has_value())
+    cp.sTextColor = CFX_Color(color.value());
 
   cp.fFontSize = m_pWidget->GetFontSize();
   cp.dwBorderWidth = m_pWidget->GetBorderWidth();
@@ -326,67 +327,59 @@
     dwCreateFlags |= PWS_AUTOFONTSIZE;
 
   cp.dwFlags = dwCreateFlags;
+  cp.pTimerHandler = m_pFormFillEnv->GetTimerHandler();
   cp.pSystemHandler = m_pFormFillEnv->GetSysHandler();
   return cp;
 }
 
-CPWL_Wnd* CFFL_FormFiller::GetPDFWindow(CPDFSDK_PageView* pPageView,
+CPWL_Wnd* CFFL_FormFiller::GetPWLWindow(CPDFSDK_PageView* pPageView,
                                         bool bNew) {
   ASSERT(pPageView);
-
   auto it = m_Maps.find(pPageView);
-  const bool found = it != m_Maps.end();
-  CPWL_Wnd* pWnd = found ? it->second : nullptr;
-  if (!bNew)
-    return pWnd;
+  if (it == m_Maps.end()) {
+    if (!bNew)
+      return nullptr;
 
-  if (!found) {
     CPWL_Wnd::CreateParams cp = GetCreateParam();
-    cp.pAttachedWidget.Reset(m_pWidget.Get());
-
-    auto* pPrivateData = new CFFL_PrivateData;
-    pPrivateData->pWidget = m_pWidget.Get();
+    auto pPrivateData = pdfium::MakeUnique<CFFL_PrivateData>();
+    pPrivateData->pWidget.Reset(m_pWidget.Get());
     pPrivateData->pPageView = pPageView;
     pPrivateData->nWidgetAppearanceAge = m_pWidget->GetAppearanceAge();
     pPrivateData->nWidgetValueAge = 0;
-    cp.pAttachedData = pPrivateData;
-    CPWL_Wnd* pNewWnd = NewPDFWindow(cp);
-    m_Maps[pPageView] = pNewWnd;
-    return pNewWnd;
+    m_Maps[pPageView] = NewPWLWindow(cp, std::move(pPrivateData));
+    return m_Maps[pPageView].get();
   }
 
-  auto* pPrivateData = static_cast<CFFL_PrivateData*>(pWnd->GetAttachedData());
+  CPWL_Wnd* pWnd = it->second.get();
+  if (!bNew)
+    return pWnd;
+
+  const auto* pPrivateData =
+      static_cast<const CFFL_PrivateData*>(pWnd->GetAttachedData());
   if (pPrivateData->nWidgetAppearanceAge == m_pWidget->GetAppearanceAge())
     return pWnd;
 
-  return ResetPDFWindow(
+  return ResetPWLWindow(
       pPageView, pPrivateData->nWidgetValueAge == m_pWidget->GetValueAge());
 }
 
-void CFFL_FormFiller::DestroyPDFWindow(CPDFSDK_PageView* pPageView) {
+void CFFL_FormFiller::DestroyPWLWindow(CPDFSDK_PageView* pPageView) {
   auto it = m_Maps.find(pPageView);
   if (it == m_Maps.end())
     return;
 
-  CPWL_Wnd* pWnd = it->second;
-  auto* pData = static_cast<CFFL_PrivateData*>(pWnd->GetAttachedData());
-  pWnd->Destroy();
-  delete pWnd;
-  delete pData;
+  std::unique_ptr<CPWL_Wnd> pWnd = std::move(it->second);
   m_Maps.erase(it);
+  pWnd->Destroy();
 }
 
-CFX_Matrix CFFL_FormFiller::GetWindowMatrix(CPWL_Wnd::PrivateData* pAttached) {
-  CFX_Matrix mt;
-  auto* pPrivateData = static_cast<CFFL_PrivateData*>(pAttached);
+CFX_Matrix CFFL_FormFiller::GetWindowMatrix(
+    const IPWL_SystemHandler::PerWindowData* pAttached) {
+  const auto* pPrivateData = static_cast<const CFFL_PrivateData*>(pAttached);
   if (!pPrivateData || !pPrivateData->pPageView)
-    return mt;
+    return CFX_Matrix();
 
-  CFX_Matrix mtPageView;
-  pPrivateData->pPageView->GetCurrentMatrix(mtPageView);
-  mt = GetCurMatrix();
-  mt.Concat(mtPageView);
-  return mt;
+  return GetCurMatrix() * pPrivateData->pPageView->GetCurrentMatrix();
 }
 
 CFX_Matrix CFFL_FormFiller::GetCurMatrix() {
@@ -413,30 +406,29 @@
   return mt;
 }
 
-CFX_FloatRect CFFL_FormFiller::GetPDFWindowRect() const {
+CFX_FloatRect CFFL_FormFiller::GetPDFAnnotRect() const {
   CFX_FloatRect rectAnnot = m_pWidget->GetPDFAnnot()->GetRect();
 
-  float fWidth = rectAnnot.right - rectAnnot.left;
-  float fHeight = rectAnnot.top - rectAnnot.bottom;
+  float fWidth = rectAnnot.Width();
+  float fHeight = rectAnnot.Height();
   if ((m_pWidget->GetRotate() / 90) & 0x01)
     std::swap(fWidth, fHeight);
   return CFX_FloatRect(0, 0, fWidth, fHeight);
 }
 
 CPDFSDK_PageView* CFFL_FormFiller::GetCurPageView(bool renew) {
-  UnderlyingPageType* pPage = m_pWidget->GetUnderlyingPage();
+  IPDF_Page* pPage = m_pWidget->GetPage();
   return m_pFormFillEnv->GetPageView(pPage, renew);
 }
 
 CFX_FloatRect CFFL_FormFiller::GetFocusBox(CPDFSDK_PageView* pPageView) {
-  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false);
   if (!pWnd)
     return CFX_FloatRect();
 
-  CFX_FloatRect rcFocus = FFLtoWnd(pPageView, PWLtoFFL(pWnd->GetFocusRect()));
-  return pPageView->GetPDFPage()->GetPageBBox().Contains(rcFocus)
-             ? rcFocus
-             : CFX_FloatRect();
+  CFX_FloatRect rcFocus = FFLtoWnd(PWLtoFFL(pWnd->GetFocusRect()));
+  return pPageView->GetPDFPage()->GetBBox().Contains(rcFocus) ? rcFocus
+                                                              : CFX_FloatRect();
 }
 
 CFX_FloatRect CFFL_FormFiller::FFLtoPWL(const CFX_FloatRect& rect) {
@@ -455,13 +447,11 @@
   return GetCurMatrix().Transform(point);
 }
 
-CFX_PointF CFFL_FormFiller::WndtoPWL(CPDFSDK_PageView* pPageView,
-                                     const CFX_PointF& pt) {
+CFX_PointF CFFL_FormFiller::WndtoPWL(const CFX_PointF& pt) {
   return FFLtoPWL(pt);
 }
 
-CFX_FloatRect CFFL_FormFiller::FFLtoWnd(CPDFSDK_PageView* pPageView,
-                                        const CFX_FloatRect& rect) {
+CFX_FloatRect CFFL_FormFiller::FFLtoWnd(const CFX_FloatRect& rect) {
   return rect;
 }
 
@@ -471,12 +461,12 @@
 
   CFFL_InteractiveFormFiller* pFormFiller =
       m_pFormFillEnv->GetInteractiveFormFiller();
-  CPDFSDK_Annot::ObservedPtr pObserved(m_pWidget.Get());
+  ObservedPtr<CPDFSDK_Annot> pObserved(m_pWidget.Get());
 
   if (!pFormFiller->OnKeyStrokeCommit(&pObserved, pPageView, nFlag)) {
     if (!pObserved)
       return false;
-    ResetPDFWindow(pPageView, false);
+    ResetPWLWindow(pPageView, false);
     return true;
   }
   if (!pObserved)
@@ -485,13 +475,13 @@
   if (!pFormFiller->OnValidate(&pObserved, pPageView, nFlag)) {
     if (!pObserved)
       return false;
-    ResetPDFWindow(pPageView, false);
+    ResetPWLWindow(pPageView, false);
     return true;
   }
   if (!pObserved)
     return false;
 
-  SaveData(pPageView); // may invoking JS to delete this widget.
+  SaveData(pPageView);  // may invoking JS to delete this widget.
   if (!pObserved)
     return false;
 
@@ -524,44 +514,34 @@
 
 void CFFL_FormFiller::GetActionData(CPDFSDK_PageView* pPageView,
                                     CPDF_AAction::AActionType type,
-                                    PDFSDK_FieldAction& fa) {
+                                    CPDFSDK_FieldAction& fa) {
   fa.sValue = m_pWidget->GetValue();
 }
 
 void CFFL_FormFiller::SetActionData(CPDFSDK_PageView* pPageView,
                                     CPDF_AAction::AActionType type,
-                                    const PDFSDK_FieldAction& fa) {}
-
-bool CFFL_FormFiller::IsActionDataChanged(CPDF_AAction::AActionType type,
-                                          const PDFSDK_FieldAction& faOld,
-                                          const PDFSDK_FieldAction& faNew) {
-  return false;
-}
+                                    const CPDFSDK_FieldAction& fa) {}
 
 void CFFL_FormFiller::SaveState(CPDFSDK_PageView* pPageView) {}
 
 void CFFL_FormFiller::RestoreState(CPDFSDK_PageView* pPageView) {}
 
-CPWL_Wnd* CFFL_FormFiller::ResetPDFWindow(CPDFSDK_PageView* pPageView,
+CPWL_Wnd* CFFL_FormFiller::ResetPWLWindow(CPDFSDK_PageView* pPageView,
                                           bool bRestoreValue) {
-  return GetPDFWindow(pPageView, false);
+  return GetPWLWindow(pPageView, false);
 }
 
-void CFFL_FormFiller::TimerProc() {}
-
-CFX_SystemHandler* CFFL_FormFiller::GetSystemHandler() const {
-  return m_pFormFillEnv->GetSysHandler();
-}
+void CFFL_FormFiller::OnTimerFired() {}
 
 void CFFL_FormFiller::EscapeFiller(CPDFSDK_PageView* pPageView,
-                                   bool bDestroyPDFWindow) {
+                                   bool bDestroyPWLWindow) {
   m_bValid = false;
 
-  InvalidateRect(GetViewBBox(pPageView, m_pWidget.Get()));
-  if (bDestroyPDFWindow)
-    DestroyPDFWindow(pPageView);
+  InvalidateRect(GetViewBBox(pPageView));
+  if (bDestroyPWLWindow)
+    DestroyPWLWindow(pPageView);
 }
 
 void CFFL_FormFiller::InvalidateRect(const FX_RECT& rect) {
-  m_pFormFillEnv->Invalidate(m_pWidget->GetUnderlyingPage(), rect);
+  m_pFormFillEnv->Invalidate(m_pWidget->GetPage(), rect);
 }
diff --git a/fpdfsdk/formfiller/cffl_formfiller.h b/fpdfsdk/formfiller/cffl_formfiller.h
index 11a2f8c..05bacaf 100644
--- a/fpdfsdk/formfiller/cffl_formfiller.h
+++ b/fpdfsdk/formfiller/cffl_formfiller.h
@@ -8,26 +8,27 @@
 #define FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_
 
 #include <map>
+#include <memory>
 
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/formfiller/cba_fontmap.h"
+#include "fpdfsdk/cpdfsdk_fieldaction.h"
+#include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
-#include "fpdfsdk/pdfsdk_fieldaction.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_systemhandler.h"
 
 class CPDFSDK_Annot;
 class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_PageView;
-class CPDFSDK_Widget;
 
 class CFFL_FormFiller : public CPWL_Wnd::ProviderIface,
-                        public CPWL_TimerHandler {
+                        public CFX_Timer::CallbackIface {
  public:
   CFFL_FormFiller(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                   CPDFSDK_Widget* pWidget);
   ~CFFL_FormFiller() override;
 
-  virtual FX_RECT GetViewBBox(CPDFSDK_PageView* pPageView,
-                              CPDFSDK_Annot* pAnnot);
   virtual void OnDraw(CPDFSDK_PageView* pPageView,
                       CPDFSDK_Annot* pAnnot,
                       CFX_RenderDevice* pDevice,
@@ -37,8 +38,8 @@
                               CFX_RenderDevice* pDevice,
                               const CFX_Matrix& mtUser2Device);
 
-  virtual void OnMouseEnter(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot);
-  virtual void OnMouseExit(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot);
+  virtual void OnMouseEnter(CPDFSDK_PageView* pPageView);
+  virtual void OnMouseExit(CPDFSDK_PageView* pPageView);
 
   virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView,
                              CPDFSDK_Annot* pAnnot,
@@ -49,71 +50,71 @@
                            uint32_t nFlags,
                            const CFX_PointF& point);
   virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                               CPDFSDK_Annot* pAnnot,
                                uint32_t nFlags,
                                const CFX_PointF& point);
   virtual bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot* pAnnot,
                            uint32_t nFlags,
                            const CFX_PointF& point);
   virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                            CPDFSDK_Annot* pAnnot,
                             uint32_t nFlags,
                             short zDelta,
                             const CFX_PointF& point);
   virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                             CPDFSDK_Annot* pAnnot,
                              uint32_t nFlags,
                              const CFX_PointF& point);
   virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot* pAnnot,
                            uint32_t nFlags,
                            const CFX_PointF& point);
 
-  virtual bool OnKeyDown(CPDFSDK_Annot* pAnnot,
-                         uint32_t nKeyCode,
-                         uint32_t nFlags);
+  virtual bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags);
   virtual bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags);
+  virtual bool SetIndexSelected(int index, bool selected);
+  virtual bool IsIndexSelected(int index);
 
-  WideString GetSelectedText(CPDFSDK_Annot* pAnnot);
-  void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text);
+  FX_RECT GetViewBBox(CPDFSDK_PageView* pPageView);
+
+  WideString GetText();
+  WideString GetSelectedText();
+  void ReplaceSelection(const WideString& text);
+
+  bool CanUndo();
+  bool CanRedo();
+  bool Undo();
+  bool Redo();
 
   void SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag);
-  void KillFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag);
+  void KillFocusForAnnot(uint32_t nFlag);
 
-  // CPWL_TimerHandler
-  void TimerProc() override;
-  CFX_SystemHandler* GetSystemHandler() const override;
+  // CFX_Timer::CallbackIface:
+  void OnTimerFired() override;
 
   // CPWL_Wnd::ProviderIface:
-  CFX_Matrix GetWindowMatrix(CPWL_Wnd::PrivateData* pAttached) override;
+  CFX_Matrix GetWindowMatrix(
+      const IPWL_SystemHandler::PerWindowData* pAttached) override;
 
   virtual void GetActionData(CPDFSDK_PageView* pPageView,
                              CPDF_AAction::AActionType type,
-                             PDFSDK_FieldAction& fa);
+                             CPDFSDK_FieldAction& fa);
   virtual void SetActionData(CPDFSDK_PageView* pPageView,
                              CPDF_AAction::AActionType type,
-                             const PDFSDK_FieldAction& fa);
-  virtual bool IsActionDataChanged(CPDF_AAction::AActionType type,
-                                   const PDFSDK_FieldAction& faOld,
-                                   const PDFSDK_FieldAction& faNew);
-
+                             const CPDFSDK_FieldAction& fa);
+  virtual CPWL_Wnd::CreateParams GetCreateParam();
+  virtual std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) = 0;
+  virtual CPWL_Wnd* ResetPWLWindow(CPDFSDK_PageView* pPageView,
+                                   bool bRestoreValue);
   virtual void SaveState(CPDFSDK_PageView* pPageView);
   virtual void RestoreState(CPDFSDK_PageView* pPageView);
 
-  virtual CPWL_Wnd* ResetPDFWindow(CPDFSDK_PageView* pPageView,
-                                   bool bRestoreValue);
-
   CFX_Matrix GetCurMatrix();
-
+  CFX_FloatRect GetFocusBox(CPDFSDK_PageView* pPageView);
   CFX_FloatRect FFLtoPWL(const CFX_FloatRect& rect);
   CFX_FloatRect PWLtoFFL(const CFX_FloatRect& rect);
   CFX_PointF FFLtoPWL(const CFX_PointF& point);
   CFX_PointF PWLtoFFL(const CFX_PointF& point);
-
-  CFX_PointF WndtoPWL(CPDFSDK_PageView* pPageView, const CFX_PointF& pt);
-  CFX_FloatRect FFLtoWnd(CPDFSDK_PageView* pPageView,
-                         const CFX_FloatRect& rect);
+  CFX_PointF WndtoPWL(const CFX_PointF& pt);
+  CFX_FloatRect FFLtoWnd(const CFX_FloatRect& rect);
 
   bool CommitData(CPDFSDK_PageView* pPageView, uint32_t nFlag);
   virtual bool IsDataChanged(CPDFSDK_PageView* pPageView);
@@ -123,25 +124,19 @@
   virtual bool IsFieldFull(CPDFSDK_PageView* pPageView);
 #endif  // PDF_ENABLE_XFA
 
-  CPWL_Wnd* GetPDFWindow(CPDFSDK_PageView* pPageView, bool bNew);
-  void DestroyPDFWindow(CPDFSDK_PageView* pPageView);
-  void EscapeFiller(CPDFSDK_PageView* pPageView, bool bDestroyPDFWindow);
-
-  virtual CPWL_Wnd::CreateParams GetCreateParam();
-  virtual CPWL_Wnd* NewPDFWindow(const CPWL_Wnd::CreateParams& cp) = 0;
-  virtual CFX_FloatRect GetFocusBox(CPDFSDK_PageView* pPageView);
+  CPWL_Wnd* GetPWLWindow(CPDFSDK_PageView* pPageView, bool bNew);
+  void DestroyPWLWindow(CPDFSDK_PageView* pPageView);
+  void EscapeFiller(CPDFSDK_PageView* pPageView, bool bDestroyPWLWindow);
 
   bool IsValid() const;
-  CFX_FloatRect GetPDFWindowRect() const;
+  CFX_FloatRect GetPDFAnnotRect() const;
 
   CPDFSDK_PageView* GetCurPageView(bool renew);
   void SetChangeMark();
 
-  CPDFSDK_Annot* GetSDKAnnot() { return m_pWidget.Get(); }
+  CPDFSDK_Annot* GetSDKAnnot() const { return m_pWidget.Get(); }
 
  protected:
-  using CFFL_PageView2PDFWindow = std::map<CPDFSDK_PageView*, CPWL_Wnd*>;
-
   // If the inheriting widget has its own fontmap and a PWL_Edit widget that
   // access that fontmap then you have to call DestroyWindows before destroying
   // the font map in order to not get a use-after-free.
@@ -152,10 +147,11 @@
 
   void InvalidateRect(const FX_RECT& rect);
 
+  bool m_bValid = false;
   UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
   UnownedPtr<CPDFSDK_Widget> m_pWidget;
-  bool m_bValid;
-  CFFL_PageView2PDFWindow m_Maps;
+  std::unique_ptr<CFX_Timer> m_pTimer;
+  std::map<CPDFSDK_PageView*, std::unique_ptr<CPWL_Wnd>> m_Maps;
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
index e74f902..6766e1c 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
@@ -6,14 +6,14 @@
 
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
 
+#include "constants/form_flags.h"
 #include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fxcrt/autorestorer.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_checkbox.h"
@@ -23,13 +23,15 @@
 #include "fpdfsdk/formfiller/cffl_pushbutton.h"
 #include "fpdfsdk/formfiller/cffl_radiobutton.h"
 #include "fpdfsdk/formfiller/cffl_textfield.h"
+#include "public/fpdf_fwlevent.h"
+#include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
 CFFL_InteractiveFormFiller::CFFL_InteractiveFormFiller(
     CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv), m_bNotifying(false) {}
+    : m_pFormFillEnv(pFormFillEnv) {}
 
-CFFL_InteractiveFormFiller::~CFFL_InteractiveFormFiller() {}
+CFFL_InteractiveFormFiller::~CFFL_InteractiveFormFiller() = default;
 
 bool CFFL_InteractiveFormFiller::Annot_HitTest(CPDFSDK_PageView* pPageView,
                                                CPDFSDK_Annot* pAnnot,
@@ -39,8 +41,8 @@
 
 FX_RECT CFFL_InteractiveFormFiller::GetViewBBox(CPDFSDK_PageView* pPageView,
                                                 CPDFSDK_Annot* pAnnot) {
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false))
-    return pFormFiller->GetViewBBox(pPageView, pAnnot);
+  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot))
+    return pFormFiller->GetViewBBox(pPageView);
 
   ASSERT(pPageView);
 
@@ -56,17 +58,16 @@
 void CFFL_InteractiveFormFiller::OnDraw(CPDFSDK_PageView* pPageView,
                                         CPDFSDK_Annot* pAnnot,
                                         CFX_RenderDevice* pDevice,
-                                        CFX_Matrix* pUser2Device) {
+                                        const CFX_Matrix& mtUser2Device) {
   ASSERT(pPageView);
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot);
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
   if (!IsVisible(pWidget))
     return;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
   if (pFormFiller && pFormFiller->IsValid()) {
-    pFormFiller->OnDraw(pPageView, pAnnot, pDevice, *pUser2Device);
+    pFormFiller->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device);
     pAnnot->GetPDFPage();
-
     if (m_pFormFillEnv->GetFocusAnnot() != pAnnot)
       return;
 
@@ -87,20 +88,18 @@
                      false);
 
     CFX_GraphStateData gsd;
-    gsd.SetDashCount(1);
-    gsd.m_DashArray[0] = 1.0f;
+    gsd.m_DashArray = {1.0f};
     gsd.m_DashPhase = 0;
     gsd.m_LineWidth = 1.0f;
-    pDevice->DrawPath(&path, pUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0),
+    pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0),
                       FXFILL_ALTERNATE);
     return;
   }
 
-  pFormFiller = GetFormFiller(pAnnot, false);
   if (pFormFiller) {
-    pFormFiller->OnDrawDeactive(pPageView, pAnnot, pDevice, *pUser2Device);
+    pFormFiller->OnDrawDeactive(pPageView, pAnnot, pDevice, mtUser2Device);
   } else {
-    pWidget->DrawAppearance(pDevice, *pUser2Device, CPDF_Annot::Normal,
+    pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal,
                             nullptr);
   }
 
@@ -114,117 +113,117 @@
 
 void CFFL_InteractiveFormFiller::OnMouseEnter(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlag) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
-    if (pWidget->GetAAction(CPDF_AAction::CursorEnter).GetDict()) {
+    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
+    if (pWidget->GetAAction(CPDF_AAction::kCursorEnter).GetDict()) {
       m_bNotifying = true;
 
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
       ASSERT(pPageView);
 
-      PDFSDK_FieldAction fa;
-      fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-      fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
-      pWidget->OnAAction(CPDF_AAction::CursorEnter, fa, pPageView);
+      CPDFSDK_FieldAction fa;
+      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+      pWidget->OnAAction(CPDF_AAction::kCursorEnter, &fa, pPageView);
       m_bNotifying = false;
-      if (!(*pAnnot))
+      if (!pAnnot->HasObservable())
         return;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false)) {
-          pFormFiller->ResetPDFWindow(pPageView,
+        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) {
+          pFormFiller->ResetPWLWindow(pPageView,
                                       pWidget->GetValueAge() == nValueAge);
         }
       }
     }
   }
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), true))
-    pFormFiller->OnMouseEnter(pPageView, pAnnot->Get());
+  if (CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get()))
+    pFormFiller->OnMouseEnter(pPageView);
 }
 
 void CFFL_InteractiveFormFiller::OnMouseExit(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              uint32_t nFlag) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
-    if (pWidget->GetAAction(CPDF_AAction::CursorExit).GetDict()) {
+    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
+    if (pWidget->GetAAction(CPDF_AAction::kCursorExit).GetDict()) {
       m_bNotifying = true;
 
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
       ASSERT(pPageView);
 
-      PDFSDK_FieldAction fa;
-      fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-      fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
-      pWidget->OnAAction(CPDF_AAction::CursorExit, fa, pPageView);
+      CPDFSDK_FieldAction fa;
+      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+      pWidget->OnAAction(CPDF_AAction::kCursorExit, &fa, pPageView);
       m_bNotifying = false;
-      if (!(*pAnnot))
+      if (!pAnnot->HasObservable())
         return;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false)) {
-          pFormFiller->ResetPDFWindow(pPageView,
+        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) {
+          pFormFiller->ResetPWLWindow(pPageView,
                                       nValueAge == pWidget->GetValueAge());
         }
       }
     }
   }
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false))
-    pFormFiller->OnMouseExit(pPageView, pAnnot->Get());
+  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()))
+    pFormFiller->OnMouseExit(pPageView);
 }
 
 bool CFFL_InteractiveFormFiller::OnLButtonDown(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
     if (Annot_HitTest(pPageView, pAnnot->Get(), point) &&
-        pWidget->GetAAction(CPDF_AAction::ButtonDown).GetDict()) {
+        pWidget->GetAAction(CPDF_AAction::kButtonDown).GetDict()) {
       m_bNotifying = true;
 
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
       ASSERT(pPageView);
 
-      PDFSDK_FieldAction fa;
-      fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlags);
-      fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlags);
-      pWidget->OnAAction(CPDF_AAction::ButtonDown, fa, pPageView);
+      CPDFSDK_FieldAction fa;
+      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlags);
+      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlags);
+      pWidget->OnAAction(CPDF_AAction::kButtonDown, &fa, pPageView);
       m_bNotifying = false;
-      if (!(*pAnnot))
+      if (!pAnnot->HasObservable())
         return true;
 
       if (!IsValidAnnot(pPageView, pAnnot->Get()))
         return true;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false)) {
-          pFormFiller->ResetPDFWindow(pPageView,
+        if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) {
+          pFormFiller->ResetPWLWindow(pPageView,
                                       nValueAge == pWidget->GetValueAge());
         }
       }
     }
   }
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
   return pFormFiller &&
          pFormFiller->OnLButtonDown(pPageView, pAnnot->Get(), nFlags, point);
 }
 
 bool CFFL_InteractiveFormFiller::OnLButtonUp(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              uint32_t nFlags,
                                              const CFX_PointF& point) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
 
   bool bSetFocus;
   switch (pWidget->GetFieldType()) {
@@ -243,7 +242,7 @@
   if (bSetFocus)
     m_pFormFillEnv->SetFocusAnnot(pAnnot);
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
   bool bRet = pFormFiller &&
               pFormFiller->OnLButtonUp(pPageView, pAnnot->Get(), nFlags, point);
   if (m_pFormFillEnv->GetFocusAnnot() != pAnnot->Get())
@@ -257,14 +256,14 @@
   return bRet;
 }
 
-bool CFFL_InteractiveFormFiller::OnButtonUp(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnButtonUp(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                             CPDFSDK_PageView* pPageView,
                                             uint32_t nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::ButtonUp).GetDict())
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
+  if (!pWidget->GetAAction(CPDF_AAction::kButtonUp).GetDict())
     return false;
 
   m_bNotifying = true;
@@ -273,75 +272,89 @@
   uint32_t nValueAge = pWidget->GetValueAge();
   ASSERT(pPageView);
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
-  pWidget->OnAAction(CPDF_AAction::ButtonUp, fa, pPageView);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+  pWidget->OnAAction(CPDF_AAction::kButtonUp, &fa, pPageView);
   m_bNotifying = false;
-  if (!(*pAnnot) || !IsValidAnnot(pPageView, pWidget))
+  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget);
   if (pFormFiller)
-    pFormFiller->ResetPDFWindow(pPageView, nValueAge == pWidget->GetValueAge());
+    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
   return true;
 }
 
+bool CFFL_InteractiveFormFiller::SetIndexSelected(
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+    int index,
+    bool selected) {
+  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
+  return pFormFiller && pFormFiller->SetIndexSelected(index, selected);
+}
+
+bool CFFL_InteractiveFormFiller::IsIndexSelected(
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+    int index) {
+  ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
+  return pFormFiller && pFormFiller->IsIndexSelected(index);
+}
+
 bool CFFL_InteractiveFormFiller::OnLButtonDblClk(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false);
-  return pFormFiller &&
-         pFormFiller->OnLButtonDblClk(pPageView, pAnnot->Get(), nFlags, point);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
+  return pFormFiller && pFormFiller->OnLButtonDblClk(pPageView, nFlags, point);
 }
 
 bool CFFL_InteractiveFormFiller::OnMouseMove(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              uint32_t nFlags,
                                              const CFX_PointF& point) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), true);
-  return pFormFiller &&
-         pFormFiller->OnMouseMove(pPageView, pAnnot->Get(), nFlags, point);
+  CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get());
+  return pFormFiller && pFormFiller->OnMouseMove(pPageView, nFlags, point);
 }
 
 bool CFFL_InteractiveFormFiller::OnMouseWheel(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     short zDelta,
     const CFX_PointF& point) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
   return pFormFiller &&
-         pFormFiller->OnMouseWheel(pPageView, pAnnot->Get(), nFlags, zDelta,
-                                   point);
+         pFormFiller->OnMouseWheel(pPageView, nFlags, zDelta, point);
 }
 
 bool CFFL_InteractiveFormFiller::OnRButtonDown(
     CPDFSDK_PageView* pPageView,
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     uint32_t nFlags,
     const CFX_PointF& point) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false);
-  return pFormFiller &&
-         pFormFiller->OnRButtonDown(pPageView, pAnnot->Get(), nFlags, point);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
+  return pFormFiller && pFormFiller->OnRButtonDown(pPageView, nFlags, point);
 }
 
 bool CFFL_InteractiveFormFiller::OnRButtonUp(CPDFSDK_PageView* pPageView,
-                                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              uint32_t nFlags,
                                              const CFX_PointF& point) {
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false);
-  return pFormFiller &&
-         pFormFiller->OnRButtonUp(pPageView, pAnnot->Get(), nFlags, point);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
+  return pFormFiller && pFormFiller->OnRButtonUp(pPageView, nFlags, point);
 }
 
 bool CFFL_InteractiveFormFiller::OnKeyDown(CPDFSDK_Annot* pAnnot,
@@ -349,8 +362,8 @@
                                            uint32_t nFlags) {
   ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false);
-  return pFormFiller && pFormFiller->OnKeyDown(pAnnot, nKeyCode, nFlags);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
+  return pFormFiller && pFormFiller->OnKeyDown(nKeyCode, nFlags);
 }
 
 bool CFFL_InteractiveFormFiller::OnChar(CPDFSDK_Annot* pAnnot,
@@ -360,74 +373,74 @@
   if (nChar == FWL_VKEY_Tab)
     return true;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
   return pFormFiller && pFormFiller->OnChar(pAnnot, nChar, nFlags);
 }
 
-bool CFFL_InteractiveFormFiller::OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                             uint32_t nFlag) {
-  if (!(*pAnnot))
+  if (!pAnnot->HasObservable())
     return false;
 
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
   if (!m_bNotifying) {
-    CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
-    if (pWidget->GetAAction(CPDF_AAction::GetFocus).GetDict()) {
+    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
+    if (pWidget->GetAAction(CPDF_AAction::kGetFocus).GetDict()) {
       m_bNotifying = true;
 
       uint32_t nValueAge = pWidget->GetValueAge();
       pWidget->ClearAppModified();
 
-      CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, true);
+      CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pWidget);
       if (!pFormFiller)
         return false;
 
       CPDFSDK_PageView* pPageView = (*pAnnot)->GetPageView();
       ASSERT(pPageView);
 
-      PDFSDK_FieldAction fa;
-      fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-      fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
-      pFormFiller->GetActionData(pPageView, CPDF_AAction::GetFocus, fa);
-      pWidget->OnAAction(CPDF_AAction::GetFocus, fa, pPageView);
+      CPDFSDK_FieldAction fa;
+      fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+      fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+      pFormFiller->GetActionData(pPageView, CPDF_AAction::kGetFocus, fa);
+      pWidget->OnAAction(CPDF_AAction::kGetFocus, &fa, pPageView);
       m_bNotifying = false;
-      if (!(*pAnnot))
+      if (!pAnnot->HasObservable())
         return false;
 
       if (pWidget->IsAppModified()) {
-        if (CFFL_FormFiller* pFiller = GetFormFiller(pWidget, false)) {
-          pFiller->ResetPDFWindow(pPageView,
+        if (CFFL_FormFiller* pFiller = GetFormFiller(pWidget)) {
+          pFiller->ResetPWLWindow(pPageView,
                                   nValueAge == pWidget->GetValueAge());
         }
       }
     }
   }
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), true))
+  if (CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get()))
     pFormFiller->SetFocusForAnnot(pAnnot->Get(), nFlag);
 
   return true;
 }
 
-bool CFFL_InteractiveFormFiller::OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              uint32_t nFlag) {
-  if (!(*pAnnot))
+  if (!pAnnot->HasObservable())
     return false;
 
   ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get(), false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get());
   if (!pFormFiller)
     return true;
 
-  pFormFiller->KillFocusForAnnot(pAnnot->Get(), nFlag);
-  if (!(*pAnnot))
+  pFormFiller->KillFocusForAnnot(nFlag);
+  if (!pAnnot->HasObservable())
     return false;
 
   if (m_bNotifying)
     return true;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::LoseFocus).GetDict())
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
+  if (!pWidget->GetAAction(CPDF_AAction::kLoseFocus).GetDict())
     return true;
 
   m_bNotifying = true;
@@ -436,13 +449,13 @@
   CPDFSDK_PageView* pPageView = pWidget->GetPageView();
   ASSERT(pPageView);
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
-  pFormFiller->GetActionData(pPageView, CPDF_AAction::LoseFocus, fa);
-  pWidget->OnAAction(CPDF_AAction::LoseFocus, fa, pPageView);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
+  pFormFiller->GetActionData(pPageView, CPDF_AAction::kLoseFocus, fa);
+  pWidget->OnAAction(CPDF_AAction::kLoseFocus, &fa, pPageView);
   m_bNotifying = false;
-  return !!(*pAnnot);
+  return pAnnot->HasObservable();
 }
 
 bool CFFL_InteractiveFormFiller::IsVisible(CPDFSDK_Widget* pWidget) {
@@ -451,98 +464,130 @@
 
 bool CFFL_InteractiveFormFiller::IsReadOnly(CPDFSDK_Widget* pWidget) {
   int nFieldFlags = pWidget->GetFieldFlags();
-  return (nFieldFlags & FIELDFLAG_READONLY) == FIELDFLAG_READONLY;
+  return !!(nFieldFlags & pdfium::form_flags::kReadOnly);
 }
 
-bool CFFL_InteractiveFormFiller::IsFillingAllowed(CPDFSDK_Widget* pWidget) {
+bool CFFL_InteractiveFormFiller::IsFillingAllowed(
+    CPDFSDK_Widget* pWidget) const {
   if (pWidget->GetFieldType() == FormFieldType::kPushButton)
     return false;
 
-  CPDF_Page* pPage = pWidget->GetPDFPage();
-  uint32_t dwPermissions = pPage->m_pDocument->GetUserPermissions();
-  return (dwPermissions & FPDFPERM_FILL_FORM) ||
-         (dwPermissions & FPDFPERM_ANNOT_FORM) ||
-         (dwPermissions & FPDFPERM_MODIFY);
+  return m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM) ||
+         m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
+         m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY);
 }
 
 CFFL_FormFiller* CFFL_InteractiveFormFiller::GetFormFiller(
-    CPDFSDK_Annot* pAnnot,
-    bool bRegister) {
-  auto it = m_Maps.find(pAnnot);
-  if (it != m_Maps.end())
-    return it->second.get();
+    CPDFSDK_Annot* pAnnot) {
+  auto it = m_Map.find(pAnnot);
+  return it != m_Map.end() ? it->second.get() : nullptr;
+}
 
-  if (!bRegister)
-    return nullptr;
+CFFL_FormFiller* CFFL_InteractiveFormFiller::GetOrCreateFormFiller(
+    CPDFSDK_Annot* pAnnot) {
+  CFFL_FormFiller* result = GetFormFiller(pAnnot);
+  if (result)
+    return result;
 
-  // TODO(thestig): How do we know |pAnnot| is a CPDFSDK_Widget?
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot);
-  FormFieldType fieldType = pWidget->GetFieldType();
-  CFFL_FormFiller* pFormFiller;
-  switch (fieldType) {
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
+  std::unique_ptr<CFFL_FormFiller> pFormFiller;
+  switch (pWidget->GetFieldType()) {
     case FormFieldType::kPushButton:
-      pFormFiller = new CFFL_PushButton(m_pFormFillEnv.Get(), pWidget);
+      pFormFiller =
+          pdfium::MakeUnique<CFFL_PushButton>(m_pFormFillEnv.Get(), pWidget);
       break;
     case FormFieldType::kCheckBox:
-      pFormFiller = new CFFL_CheckBox(m_pFormFillEnv.Get(), pWidget);
+      pFormFiller =
+          pdfium::MakeUnique<CFFL_CheckBox>(m_pFormFillEnv.Get(), pWidget);
       break;
     case FormFieldType::kRadioButton:
-      pFormFiller = new CFFL_RadioButton(m_pFormFillEnv.Get(), pWidget);
+      pFormFiller =
+          pdfium::MakeUnique<CFFL_RadioButton>(m_pFormFillEnv.Get(), pWidget);
       break;
     case FormFieldType::kTextField:
-      pFormFiller = new CFFL_TextField(m_pFormFillEnv.Get(), pWidget);
+      pFormFiller =
+          pdfium::MakeUnique<CFFL_TextField>(m_pFormFillEnv.Get(), pWidget);
       break;
     case FormFieldType::kListBox:
-      pFormFiller = new CFFL_ListBox(m_pFormFillEnv.Get(), pWidget);
+      pFormFiller =
+          pdfium::MakeUnique<CFFL_ListBox>(m_pFormFillEnv.Get(), pWidget);
       break;
     case FormFieldType::kComboBox:
-      pFormFiller = new CFFL_ComboBox(m_pFormFillEnv.Get(), pWidget);
+      pFormFiller =
+          pdfium::MakeUnique<CFFL_ComboBox>(m_pFormFillEnv.Get(), pWidget);
       break;
     case FormFieldType::kUnknown:
     default:
-      pFormFiller = nullptr;
-      break;
+      return nullptr;
   }
 
-  if (!pFormFiller)
-    return nullptr;
+  result = pFormFiller.get();
+  m_Map[pAnnot] = std::move(pFormFiller);
+  return result;
+}
 
-  m_Maps[pAnnot].reset(pFormFiller);
-  return pFormFiller;
+WideString CFFL_InteractiveFormFiller::GetText(CPDFSDK_Annot* pAnnot) {
+  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
+  return pFormFiller ? pFormFiller->GetText() : WideString();
 }
 
 WideString CFFL_InteractiveFormFiller::GetSelectedText(CPDFSDK_Annot* pAnnot) {
   ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false);
-  return pFormFiller ? pFormFiller->GetSelectedText(pAnnot) : WideString();
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
+  return pFormFiller ? pFormFiller->GetSelectedText() : WideString();
 }
 
 void CFFL_InteractiveFormFiller::ReplaceSelection(CPDFSDK_Annot* pAnnot,
                                                   const WideString& text) {
   ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
   if (!pFormFiller)
     return;
 
-  pFormFiller->ReplaceSelection(pAnnot, text);
+  pFormFiller->ReplaceSelection(text);
+}
+
+bool CFFL_InteractiveFormFiller::CanUndo(CPDFSDK_Annot* pAnnot) {
+  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
+  return pFormFiller && pFormFiller->CanUndo();
+}
+
+bool CFFL_InteractiveFormFiller::CanRedo(CPDFSDK_Annot* pAnnot) {
+  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
+  return pFormFiller && pFormFiller->CanRedo();
+}
+
+bool CFFL_InteractiveFormFiller::Undo(CPDFSDK_Annot* pAnnot) {
+  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
+  return pFormFiller && pFormFiller->Undo();
+}
+
+bool CFFL_InteractiveFormFiller::Redo(CPDFSDK_Annot* pAnnot) {
+  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot);
+  return pFormFiller && pFormFiller->Redo();
 }
 
 void CFFL_InteractiveFormFiller::UnRegisterFormFiller(CPDFSDK_Annot* pAnnot) {
-  auto it = m_Maps.find(pAnnot);
-  if (it == m_Maps.end())
+  auto it = m_Map.find(pAnnot);
+  if (it == m_Map.end())
     return;
 
-  m_Maps.erase(it);
+  m_Map.erase(it);
 }
 
 void CFFL_InteractiveFormFiller::QueryWherePopup(
-    CPWL_Wnd::PrivateData* pAttached,
+    const IPWL_SystemHandler::PerWindowData* pAttached,
     float fPopupMin,
     float fPopupMax,
     bool* bBottom,
     float* fPopupRet) {
-  auto* pData = static_cast<CFFL_PrivateData*>(pAttached);
-  CPDFSDK_Widget* pWidget = pData->pWidget;
+  auto* pData = static_cast<const CFFL_PrivateData*>(pAttached);
+  CPDFSDK_Widget* pWidget = pData->pWidget.Get();
   CPDF_Page* pPage = pWidget->GetPDFPage();
 
   CFX_FloatRect rcPageView(0, pPage->GetPageHeight(), pPage->GetPageWidth(), 0);
@@ -597,114 +642,115 @@
 }
 
 bool CFFL_InteractiveFormFiller::OnKeyStrokeCommit(
-    CPDFSDK_Annot::ObservedPtr* pAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pAnnot,
     CPDFSDK_PageView* pPageView,
     uint32_t nFlag) {
   if (m_bNotifying)
     return true;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::KeyStroke).GetDict())
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
+  if (!pWidget->GetAAction(CPDF_AAction::kKeyStroke).GetDict())
     return true;
 
   ASSERT(pPageView);
   m_bNotifying = true;
   pWidget->ClearAppModified();
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
   fa.bWillCommit = true;
   fa.bKeyDown = true;
   fa.bRC = true;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false);
-  pFormFiller->GetActionData(pPageView, CPDF_AAction::KeyStroke, fa);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget);
+  pFormFiller->GetActionData(pPageView, CPDF_AAction::kKeyStroke, fa);
   pFormFiller->SaveState(pPageView);
-  pWidget->OnAAction(CPDF_AAction::KeyStroke, fa, pPageView);
-  if (!(*pAnnot))
+  pWidget->OnAAction(CPDF_AAction::kKeyStroke, &fa, pPageView);
+  if (!pAnnot->HasObservable())
     return true;
 
   m_bNotifying = false;
   return fa.bRC;
 }
 
-bool CFFL_InteractiveFormFiller::OnValidate(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnValidate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                             CPDFSDK_PageView* pPageView,
                                             uint32_t nFlag) {
   if (m_bNotifying)
     return true;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
-  if (!pWidget->GetAAction(CPDF_AAction::Validate).GetDict())
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
+  if (!pWidget->GetAAction(CPDF_AAction::kValidate).GetDict())
     return true;
 
   ASSERT(pPageView);
   m_bNotifying = true;
   pWidget->ClearAppModified();
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
   fa.bKeyDown = true;
   fa.bRC = true;
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false);
-  pFormFiller->GetActionData(pPageView, CPDF_AAction::Validate, fa);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget);
+  pFormFiller->GetActionData(pPageView, CPDF_AAction::kValidate, fa);
   pFormFiller->SaveState(pPageView);
-  pWidget->OnAAction(CPDF_AAction::Validate, fa, pPageView);
-  if (!(*pAnnot))
+  pWidget->OnAAction(CPDF_AAction::kValidate, &fa, pPageView);
+  if (!pAnnot->HasObservable())
     return true;
 
   m_bNotifying = false;
   return fa.bRC;
 }
 
-void CFFL_InteractiveFormFiller::OnCalculate(CPDFSDK_Annot::ObservedPtr* pAnnot,
+void CFFL_InteractiveFormFiller::OnCalculate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                              CPDFSDK_PageView* pPageView,
                                              uint32_t nFlag) {
   if (m_bNotifying)
     return;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (pWidget) {
-    CPDFSDK_InterForm* pInterForm = pPageView->GetFormFillEnv()->GetInterForm();
-    pInterForm->OnCalculate(pWidget->GetFormField());
+    CPDFSDK_InteractiveForm* pForm =
+        pPageView->GetFormFillEnv()->GetInteractiveForm();
+    pForm->OnCalculate(pWidget->GetFormField());
   }
   m_bNotifying = false;
 }
 
-void CFFL_InteractiveFormFiller::OnFormat(CPDFSDK_Annot::ObservedPtr* pAnnot,
+void CFFL_InteractiveFormFiller::OnFormat(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                           CPDFSDK_PageView* pPageView,
                                           uint32_t nFlag) {
   if (m_bNotifying)
     return;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   ASSERT(pWidget);
-  CPDFSDK_InterForm* pInterForm = pPageView->GetFormFillEnv()->GetInterForm();
+  CPDFSDK_InteractiveForm* pForm =
+      pPageView->GetFormFillEnv()->GetInteractiveForm();
 
-  bool bFormatted = false;
-  WideString sValue = pInterForm->OnFormat(pWidget->GetFormField(), bFormatted);
-  if (!(*pAnnot))
+  Optional<WideString> sValue = pForm->OnFormat(pWidget->GetFormField());
+  if (!pAnnot->HasObservable())
     return;
 
-  if (bFormatted) {
-    pInterForm->ResetFieldAppearance(pWidget->GetFormField(), &sValue, true);
-    pInterForm->UpdateField(pWidget->GetFormField());
+  if (sValue.has_value()) {
+    pForm->ResetFieldAppearance(pWidget->GetFormField(), sValue);
+    pForm->UpdateField(pWidget->GetFormField());
   }
 
   m_bNotifying = false;
 }
 
 #ifdef PDF_ENABLE_XFA
-bool CFFL_InteractiveFormFiller::OnClick(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnClick(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                          CPDFSDK_PageView* pPageView,
                                          uint32_t nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_Click))
     return false;
 
@@ -712,29 +758,29 @@
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
 
-  pWidget->OnXFAAAction(PDFSDK_XFA_Click, fa, pPageView);
+  pWidget->OnXFAAAction(PDFSDK_XFA_Click, &fa, pPageView);
   m_bNotifying = false;
-  if (!(*pAnnot) || !IsValidAnnot(pPageView, pWidget))
+  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false))
-    pFormFiller->ResetPDFWindow(pPageView, nValueAge == pWidget->GetValueAge());
+  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
+    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
   return false;
 }
 
-bool CFFL_InteractiveFormFiller::OnFull(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnFull(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                         CPDFSDK_PageView* pPageView,
                                         uint32_t nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_Full))
     return false;
 
@@ -742,52 +788,30 @@
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
 
-  pWidget->OnXFAAAction(PDFSDK_XFA_Full, fa, pPageView);
+  pWidget->OnXFAAAction(PDFSDK_XFA_Full, &fa, pPageView);
   m_bNotifying = false;
-  if (!(*pAnnot) || !IsValidAnnot(pPageView, pWidget))
+  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false))
-    pFormFiller->ResetPDFWindow(pPageView, nValueAge == pWidget->GetValueAge());
+  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
+    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
 
   return true;
 }
 
-bool CFFL_InteractiveFormFiller::OnPopupPreOpen(
-    CPWL_Wnd::PrivateData* pAttached,
-    uint32_t nFlag) {
-  auto* pData = static_cast<CFFL_PrivateData*>(pAttached);
-  ASSERT(pData);
-  ASSERT(pData->pWidget);
-
-  CPDFSDK_Annot::ObservedPtr pObserved(pData->pWidget);
-  return OnPreOpen(&pObserved, pData->pPageView, nFlag) || !pObserved;
-}
-
-bool CFFL_InteractiveFormFiller::OnPopupPostOpen(
-    CPWL_Wnd::PrivateData* pAttached,
-    uint32_t nFlag) {
-  auto* pData = static_cast<CFFL_PrivateData*>(pAttached);
-  ASSERT(pData);
-  ASSERT(pData->pWidget);
-
-  CPDFSDK_Annot::ObservedPtr pObserved(pData->pWidget);
-  return OnPostOpen(&pObserved, pData->pPageView, nFlag) || !pObserved;
-}
-
-bool CFFL_InteractiveFormFiller::OnPreOpen(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnPreOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                            CPDFSDK_PageView* pPageView,
                                            uint32_t nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_PreOpen))
     return false;
 
@@ -795,30 +819,30 @@
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
 
-  pWidget->OnXFAAAction(PDFSDK_XFA_PreOpen, fa, pPageView);
+  pWidget->OnXFAAAction(PDFSDK_XFA_PreOpen, &fa, pPageView);
   m_bNotifying = false;
-  if (!(*pAnnot) || !IsValidAnnot(pPageView, pWidget))
+  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false))
-    pFormFiller->ResetPDFWindow(pPageView, nValueAge == pWidget->GetValueAge());
+  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
+    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
 
   return true;
 }
 
-bool CFFL_InteractiveFormFiller::OnPostOpen(CPDFSDK_Annot::ObservedPtr* pAnnot,
+bool CFFL_InteractiveFormFiller::OnPostOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                             CPDFSDK_PageView* pPageView,
                                             uint32_t nFlag) {
   if (m_bNotifying)
     return false;
 
-  CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot->Get());
+  CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get());
   if (!pWidget->HasXFAAAction(PDFSDK_XFA_PostOpen))
     return false;
 
@@ -826,19 +850,19 @@
   uint32_t nAge = pWidget->GetAppearanceAge();
   uint32_t nValueAge = pWidget->GetValueAge();
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
 
-  pWidget->OnXFAAAction(PDFSDK_XFA_PostOpen, fa, pPageView);
+  pWidget->OnXFAAAction(PDFSDK_XFA_PostOpen, &fa, pPageView);
   m_bNotifying = false;
-  if (!(*pAnnot) || !IsValidAnnot(pPageView, pWidget))
+  if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget))
     return true;
   if (nAge == pWidget->GetAppearanceAge())
     return false;
 
-  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget, false))
-    pFormFiller->ResetPDFWindow(pPageView, nValueAge == pWidget->GetValueAge());
+  if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget))
+    pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge());
 
   return true;
 }
@@ -850,7 +874,7 @@
 }
 
 std::pair<bool, bool> CFFL_InteractiveFormFiller::OnBeforeKeyStroke(
-    CPWL_Wnd::PrivateData* pAttached,
+    const IPWL_SystemHandler::PerWindowData* pAttached,
     WideString& strChange,
     const WideString& strChangeEx,
     int nSelStart,
@@ -858,21 +882,22 @@
     bool bKeyDown,
     uint32_t nFlag) {
   // Copy the private data since the window owning it may not survive.
-  CFFL_PrivateData privateData = *static_cast<CFFL_PrivateData*>(pAttached);
+  CFFL_PrivateData privateData =
+      *static_cast<const CFFL_PrivateData*>(pAttached);
   ASSERT(privateData.pWidget);
 
-  CFFL_FormFiller* pFormFiller = GetFormFiller(privateData.pWidget, false);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(privateData.GetWidget());
 
 #ifdef PDF_ENABLE_XFA
   if (pFormFiller->IsFieldFull(privateData.pPageView)) {
-    CPDFSDK_Annot::ObservedPtr pObserved(privateData.pWidget);
+    ObservedPtr<CPDFSDK_Annot> pObserved(privateData.GetWidget());
     if (OnFull(&pObserved, privateData.pPageView, nFlag) || !pObserved)
       return {true, true};
   }
 #endif  // PDF_ENABLE_XFA
 
   if (m_bNotifying ||
-      !privateData.pWidget->GetAAction(CPDF_AAction::KeyStroke).GetDict()) {
+      !privateData.pWidget->GetAAction(CPDF_AAction::kKeyStroke).GetDict()) {
     return {true, false};
   }
 
@@ -884,9 +909,9 @@
   CPDFSDK_FormFillEnvironment* pFormFillEnv =
       privateData.pPageView->GetFormFillEnv();
 
-  PDFSDK_FieldAction fa;
-  fa.bModifier = CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
-  fa.bShift = CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+  CPDFSDK_FieldAction fa;
+  fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag);
+  fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag);
   fa.sChange = strChange;
   fa.sChangeEx = strChangeEx;
   fa.bKeyDown = bKeyDown;
@@ -894,38 +919,79 @@
   fa.bRC = true;
   fa.nSelStart = nSelStart;
   fa.nSelEnd = nSelEnd;
-  pFormFiller->GetActionData(privateData.pPageView, CPDF_AAction::KeyStroke,
+  pFormFiller->GetActionData(privateData.pPageView, CPDF_AAction::kKeyStroke,
                              fa);
   pFormFiller->SaveState(privateData.pPageView);
 
-  CPDFSDK_Annot::ObservedPtr pObserved(privateData.pWidget);
+  ObservedPtr<CPDFSDK_Annot> pObserved(privateData.GetWidget());
   bool action_status = privateData.pWidget->OnAAction(
-      CPDF_AAction::KeyStroke, fa, privateData.pPageView);
+      CPDF_AAction::kKeyStroke, &fa, privateData.pPageView);
 
-  if (!pObserved || !IsValidAnnot(privateData.pPageView, privateData.pWidget))
+  if (!pObserved ||
+      !IsValidAnnot(privateData.pPageView, privateData.GetWidget())) {
     return {true, true};
-
+  }
   if (!action_status)
     return {true, false};
 
   bool bExit = false;
   if (nAge != privateData.pWidget->GetAppearanceAge()) {
-    CPWL_Wnd* pWnd = pFormFiller->ResetPDFWindow(
+    CPWL_Wnd* pWnd = pFormFiller->ResetPWLWindow(
         privateData.pPageView, nValueAge == privateData.pWidget->GetValueAge());
     if (!pWnd)
       return {true, true};
-    privateData = *static_cast<CFFL_PrivateData*>(pWnd->GetAttachedData());
+    privateData =
+        *static_cast<const CFFL_PrivateData*>(pWnd->GetAttachedData());
     bExit = true;
   }
   if (fa.bRC) {
-    pFormFiller->SetActionData(privateData.pPageView, CPDF_AAction::KeyStroke,
+    pFormFiller->SetActionData(privateData.pPageView, CPDF_AAction::kKeyStroke,
                                fa);
   } else {
     pFormFiller->RestoreState(privateData.pPageView);
   }
-  if (pFormFillEnv->GetFocusAnnot() == privateData.pWidget)
+  if (pFormFillEnv->GetFocusAnnot() == privateData.GetWidget())
     return {false, bExit};
 
   pFormFiller->CommitData(privateData.pPageView, nFlag);
   return {false, true};
 }
+
+bool CFFL_InteractiveFormFiller::OnPopupPreOpen(
+    const IPWL_SystemHandler::PerWindowData* pAttached,
+    uint32_t nFlag) {
+#ifdef PDF_ENABLE_XFA
+  auto* pData = static_cast<const CFFL_PrivateData*>(pAttached);
+  ASSERT(pData->pWidget);
+
+  ObservedPtr<CPDFSDK_Annot> pObserved(pData->GetWidget());
+  return OnPreOpen(&pObserved, pData->pPageView, nFlag) || !pObserved;
+#else
+  return false;
+#endif
+}
+
+bool CFFL_InteractiveFormFiller::OnPopupPostOpen(
+    const IPWL_SystemHandler::PerWindowData* pAttached,
+    uint32_t nFlag) {
+#ifdef PDF_ENABLE_XFA
+  auto* pData = static_cast<const CFFL_PrivateData*>(pAttached);
+  ASSERT(pData->pWidget);
+
+  ObservedPtr<CPDFSDK_Annot> pObserved(pData->GetWidget());
+  return OnPostOpen(&pObserved, pData->pPageView, nFlag) || !pObserved;
+#else
+  return false;
+#endif
+}
+
+CFFL_PrivateData::CFFL_PrivateData() = default;
+
+CFFL_PrivateData::CFFL_PrivateData(const CFFL_PrivateData& that) = default;
+
+CFFL_PrivateData::~CFFL_PrivateData() = default;
+
+std::unique_ptr<IPWL_SystemHandler::PerWindowData> CFFL_PrivateData::Clone()
+    const {
+  return pdfium::MakeUnique<CFFL_PrivateData>(*this);
+}
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.h b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
index 0192752..3adfe4e 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.h
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
@@ -11,17 +11,19 @@
 #include <memory>
 #include <utility>
 
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/fsdk_define.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
+#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_systemhandler.h"
 
 class CFFL_FormFiller;
 class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_PageView;
 class CPDFSDK_Widget;
 
-class CFFL_InteractiveFormFiller : public IPWL_Filler_Notify {
+class CFFL_InteractiveFormFiller final : public IPWL_Filler_Notify {
  public:
   explicit CFFL_InteractiveFormFiller(
       CPDFSDK_FormFillEnvironment* pFormFillEnv);
@@ -34,130 +36,155 @@
   void OnDraw(CPDFSDK_PageView* pPageView,
               CPDFSDK_Annot* pAnnot,
               CFX_RenderDevice* pDevice,
-              CFX_Matrix* pUser2Device);
+              const CFX_Matrix& mtUser2Device);
 
   void OnDelete(CPDFSDK_Annot* pAnnot);
 
   void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
                     uint32_t nFlag);
   void OnMouseExit(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlag);
   bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
                      uint32_t nFlags,
                      const CFX_PointF& point);
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point);
   bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                       CPDFSDK_Annot::ObservedPtr* pAnnot,
+                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
                        uint32_t nFlags,
                        const CFX_PointF& point);
   bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point);
   bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                    CPDFSDK_Annot::ObservedPtr* pAnnot,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
                     uint32_t nFlags,
                     short zDelta,
                     const CFX_PointF& point);
   bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                     CPDFSDK_Annot::ObservedPtr* pAnnot,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
                      uint32_t nFlags,
                      const CFX_PointF& point);
   bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                   CPDFSDK_Annot::ObservedPtr* pAnnot,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    uint32_t nFlags,
                    const CFX_PointF& point);
 
   bool OnKeyDown(CPDFSDK_Annot* pAnnot, uint32_t nKeyCode, uint32_t nFlags);
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags);
 
-  bool OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag);
-  bool OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot, uint32_t nFlag);
+  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
+  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag);
 
-  CFFL_FormFiller* GetFormFiller(CPDFSDK_Annot* pAnnot, bool bRegister);
+  CFFL_FormFiller* GetFormFillerForTesting(CPDFSDK_Annot* pAnnot) {
+    return GetFormFiller(pAnnot);
+  }
 
+  WideString GetText(CPDFSDK_Annot* pAnnot);
   WideString GetSelectedText(CPDFSDK_Annot* pAnnot);
   void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text);
 
+  bool CanUndo(CPDFSDK_Annot* pAnnot);
+  bool CanRedo(CPDFSDK_Annot* pAnnot);
+  bool Undo(CPDFSDK_Annot* pAnnot);
+  bool Redo(CPDFSDK_Annot* pAnnot);
+
   static bool IsVisible(CPDFSDK_Widget* pWidget);
   static bool IsReadOnly(CPDFSDK_Widget* pWidget);
-  static bool IsFillingAllowed(CPDFSDK_Widget* pWidget);
   static bool IsValidAnnot(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot);
 
-  bool OnKeyStrokeCommit(CPDFSDK_Annot::ObservedPtr* pWidget,
+  bool OnKeyStrokeCommit(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                          CPDFSDK_PageView* pPageView,
                          uint32_t nFlag);
-  bool OnValidate(CPDFSDK_Annot::ObservedPtr* pAnnot,
+  bool OnValidate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                   CPDFSDK_PageView* pPageView,
                   uint32_t nFlag);
-  void OnCalculate(CPDFSDK_Annot::ObservedPtr* pAnnot,
+  void OnCalculate(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                    CPDFSDK_PageView* pPageView,
                    uint32_t nFlag);
-  void OnFormat(CPDFSDK_Annot::ObservedPtr* pAnnot,
+  void OnFormat(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                 CPDFSDK_PageView* pPageView,
                 uint32_t nFlag);
-  bool OnButtonUp(CPDFSDK_Annot::ObservedPtr* pAnnot,
+  bool OnButtonUp(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                   CPDFSDK_PageView* pPageView,
                   uint32_t nFlag);
-#ifdef PDF_ENABLE_XFA
-  bool OnClick(CPDFSDK_Annot::ObservedPtr* pAnnot,
-               CPDFSDK_PageView* pPageView,
-               uint32_t nFlag);
-  bool OnFull(CPDFSDK_Annot::ObservedPtr* pAnnot,
-              CPDFSDK_PageView* pPageView,
-              uint32_t nFlag);
-  bool OnPreOpen(CPDFSDK_Annot::ObservedPtr* pAnnot,
-                 CPDFSDK_PageView* pPageView,
-                 uint32_t nFlag);
-  bool OnPostOpen(CPDFSDK_Annot::ObservedPtr* pAnnot,
-                  CPDFSDK_PageView* pPageView,
-                  uint32_t nFlag);
-#endif  // PDF_ENABLE_XFA
+
+  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                        int index,
+                        bool selected);
+  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index);
 
  private:
-  using CFFL_Widget2Filler =
+  using WidgetToFormFillerMap =
       std::map<CPDFSDK_Annot*, std::unique_ptr<CFFL_FormFiller>>;
 
   // IPWL_Filler_Notify:
-  void QueryWherePopup(CPWL_Wnd::PrivateData* pAttached,
+  void QueryWherePopup(const IPWL_SystemHandler::PerWindowData* pAttached,
                        float fPopupMin,
                        float fPopupMax,
                        bool* bBottom,
                        float* fPopupRet) override;
   // Returns {bRC, bExit}.
-  std::pair<bool, bool> OnBeforeKeyStroke(CPWL_Wnd::PrivateData* pAttached,
-                                          WideString& strChange,
-                                          const WideString& strChangeEx,
-                                          int nSelStart,
-                                          int nSelEnd,
-                                          bool bKeyDown,
-                                          uint32_t nFlag) override;
-#ifdef PDF_ENABLE_XFA
-  bool OnPopupPreOpen(CPWL_Wnd::PrivateData* pAttached,
+  std::pair<bool, bool> OnBeforeKeyStroke(
+      const IPWL_SystemHandler::PerWindowData* pAttached,
+      WideString& strChange,
+      const WideString& strChangeEx,
+      int nSelStart,
+      int nSelEnd,
+      bool bKeyDown,
+      uint32_t nFlag) override;
+  bool OnPopupPreOpen(const IPWL_SystemHandler::PerWindowData* pAttached,
                       uint32_t nFlag) override;
-  bool OnPopupPostOpen(CPWL_Wnd::PrivateData* pAttached,
+  bool OnPopupPostOpen(const IPWL_SystemHandler::PerWindowData* pAttached,
                        uint32_t nFlag) override;
+
+#ifdef PDF_ENABLE_XFA
   void SetFocusAnnotTab(CPDFSDK_Annot* pWidget, bool bSameField, bool bNext);
+  bool OnClick(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+               CPDFSDK_PageView* pPageView,
+               uint32_t nFlag);
+  bool OnFull(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+              CPDFSDK_PageView* pPageView,
+              uint32_t nFlag);
+  bool OnPreOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                 CPDFSDK_PageView* pPageView,
+                 uint32_t nFlag);
+  bool OnPostOpen(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                  CPDFSDK_PageView* pPageView,
+                  uint32_t nFlag);
 #endif  // PDF_ENABLE_XFA
+
+  bool IsFillingAllowed(CPDFSDK_Widget* pWidget) const;
+  CFFL_FormFiller* GetFormFiller(CPDFSDK_Annot* pAnnot);
+  CFFL_FormFiller* GetOrCreateFormFiller(CPDFSDK_Annot* pAnnot);
   void UnRegisterFormFiller(CPDFSDK_Annot* pAnnot);
 
   UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-  CFFL_Widget2Filler m_Maps;
-  bool m_bNotifying;
+  WidgetToFormFillerMap m_Map;
+  bool m_bNotifying = false;
 };
 
-class CFFL_PrivateData : public CPWL_Wnd::PrivateData {
+class CFFL_PrivateData final : public IPWL_SystemHandler::PerWindowData {
  public:
-  CPDFSDK_Widget* pWidget;
-  CPDFSDK_PageView* pPageView;
-  uint32_t nWidgetAppearanceAge;
-  uint32_t nWidgetValueAge;
+  CFFL_PrivateData();
+  CFFL_PrivateData(const CFFL_PrivateData& that);
+  ~CFFL_PrivateData() override;
+
+  // CPWL_Wnd::PrivateData:
+  std::unique_ptr<IPWL_SystemHandler::PerWindowData> Clone() const override;
+
+  CPDFSDK_Widget* GetWidget() const { return pWidget.Get(); }
+
+  ObservedPtr<CPDFSDK_Widget> pWidget;
+  CPDFSDK_PageView* pPageView = nullptr;
+  uint32_t nWidgetAppearanceAge = 0;
+  uint32_t nWidgetValueAge = 0;
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_INTERACTIVEFORMFILLER_H_
diff --git a/fpdfsdk/formfiller/cffl_listbox.cpp b/fpdfsdk/formfiller/cffl_listbox.cpp
index e628e59..3d80255 100644
--- a/fpdfsdk/formfiller/cffl_listbox.cpp
+++ b/fpdfsdk/formfiller/cffl_listbox.cpp
@@ -6,41 +6,45 @@
 
 #include "fpdfsdk/formfiller/cffl_listbox.h"
 
+#include <utility>
+
+#include "constants/form_flags.h"
+#include "core/fpdfdoc/cba_fontmap.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cba_fontmap.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
-#include "fpdfsdk/fsdk_common.h"
 #include "fpdfsdk/pwl/cpwl_list_box.h"
 #include "third_party/base/ptr_util.h"
 
-#define FFL_DEFAULTLISTBOXFONTSIZE 12.0f
-
 CFFL_ListBox::CFFL_ListBox(CPDFSDK_FormFillEnvironment* pApp,
                            CPDFSDK_Widget* pWidget)
     : CFFL_TextObject(pApp, pWidget) {}
 
-CFFL_ListBox::~CFFL_ListBox() {}
+CFFL_ListBox::~CFFL_ListBox() = default;
 
 CPWL_Wnd::CreateParams CFFL_ListBox::GetCreateParam() {
   CPWL_Wnd::CreateParams cp = CFFL_TextObject::GetCreateParam();
   uint32_t dwFieldFlag = m_pWidget->GetFieldFlags();
-  if (dwFieldFlag & FIELDFLAG_MULTISELECT)
+  if (dwFieldFlag & pdfium::form_flags::kChoiceMultiSelect)
     cp.dwFlags |= PLBS_MULTIPLESEL;
 
   cp.dwFlags |= PWS_VSCROLL;
 
-  if (cp.dwFlags & PWS_AUTOFONTSIZE)
-    cp.fFontSize = FFL_DEFAULTLISTBOXFONTSIZE;
+  if (cp.dwFlags & PWS_AUTOFONTSIZE) {
+    constexpr float kDefaultListBoxFontSize = 12.0f;
+    cp.fFontSize = kDefaultListBoxFontSize;
+  }
 
   cp.pFontMap = MaybeCreateFontMap();
   return cp;
 }
 
-CPWL_Wnd* CFFL_ListBox::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) {
-  auto* pWnd = new CPWL_ListBox();
+std::unique_ptr<CPWL_Wnd> CFFL_ListBox::NewPWLWindow(
+    const CPWL_Wnd::CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
+  auto pWnd = pdfium::MakeUnique<CPWL_ListBox>(cp, std::move(pAttachedData));
   pWnd->AttachFFLData(this);
-  pWnd->Create(cp);
+  pWnd->Realize();
   pWnd->SetFillerNotify(m_pFormFillEnv->GetInteractiveFormFiller());
 
   for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++)
@@ -70,8 +74,7 @@
   }
 
   pWnd->SetTopVisibleIndex(m_pWidget->GetTopVisibleIndex());
-
-  return pWnd;
+  return std::move(pWnd);
 }
 
 bool CFFL_ListBox::OnChar(CPDFSDK_Annot* pAnnot,
@@ -81,11 +84,11 @@
 }
 
 bool CFFL_ListBox::IsDataChanged(CPDFSDK_PageView* pPageView) {
-  CPWL_ListBox* pListBox = (CPWL_ListBox*)GetPDFWindow(pPageView, false);
+  CPWL_ListBox* pListBox = GetListBox(pPageView);
   if (!pListBox)
     return false;
 
-  if (m_pWidget->GetFieldFlags() & FIELDFLAG_MULTISELECT) {
+  if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) {
     size_t nSelCount = 0;
     for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; ++i) {
       if (pListBox->IsItemSelected(i)) {
@@ -102,56 +105,60 @@
 }
 
 void CFFL_ListBox::SaveData(CPDFSDK_PageView* pPageView) {
-  CPWL_ListBox* pListBox =
-      static_cast<CPWL_ListBox*>(GetPDFWindow(pPageView, false));
+  CPWL_ListBox* pListBox = GetListBox(pPageView);
   if (!pListBox)
     return;
 
   int32_t nNewTopIndex = pListBox->GetTopVisibleIndex();
-  m_pWidget->ClearSelection(false);
-  if (m_pWidget->GetFieldFlags() & FIELDFLAG_MULTISELECT) {
+  m_pWidget->ClearSelection(NotificationOption::kDoNotNotify);
+  if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) {
     for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; i++) {
-      if (pListBox->IsItemSelected(i))
-        m_pWidget->SetOptionSelection(i, true, false);
+      if (pListBox->IsItemSelected(i)) {
+        m_pWidget->SetOptionSelection(i, true,
+                                      NotificationOption::kDoNotNotify);
+      }
     }
   } else {
-    m_pWidget->SetOptionSelection(pListBox->GetCurSel(), true, false);
+    m_pWidget->SetOptionSelection(pListBox->GetCurSel(), true,
+                                  NotificationOption::kDoNotNotify);
   }
-  CPDFSDK_Widget::ObservedPtr observed_widget(m_pWidget.Get());
-  CFFL_ListBox::ObservedPtr observed_this(this);
-
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CFFL_ListBox> observed_this(this);
   m_pWidget->SetTopVisibleIndex(nNewTopIndex);
   if (!observed_widget)
     return;
-  m_pWidget->ResetFieldAppearance(true);
+
+  m_pWidget->ResetFieldAppearance();
   if (!observed_widget)
     return;
+
   m_pWidget->UpdateField();
   if (!observed_widget || !observed_this)
     return;
+
   SetChangeMark();
 }
 
 void CFFL_ListBox::GetActionData(CPDFSDK_PageView* pPageView,
                                  CPDF_AAction::AActionType type,
-                                 PDFSDK_FieldAction& fa) {
+                                 CPDFSDK_FieldAction& fa) {
   switch (type) {
-    case CPDF_AAction::Validate:
-      if (m_pWidget->GetFieldFlags() & FIELDFLAG_MULTISELECT) {
-        fa.sValue = L"";
+    case CPDF_AAction::kValidate:
+      if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) {
+        fa.sValue.clear();
       } else {
-        if (CPWL_ListBox* pListBox =
-                (CPWL_ListBox*)GetPDFWindow(pPageView, false)) {
+        CPWL_ListBox* pListBox = GetListBox(pPageView);
+        if (pListBox) {
           int32_t nCurSel = pListBox->GetCurSel();
           if (nCurSel >= 0)
             fa.sValue = m_pWidget->GetOptionLabel(nCurSel);
         }
       }
       break;
-    case CPDF_AAction::LoseFocus:
-    case CPDF_AAction::GetFocus:
-      if (m_pWidget->GetFieldFlags() & FIELDFLAG_MULTISELECT) {
-        fa.sValue = L"";
+    case CPDF_AAction::kLoseFocus:
+    case CPDF_AAction::kGetFocus:
+      if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) {
+        fa.sValue.clear();
       } else {
         int32_t nCurSel = m_pWidget->GetSelectedIndex(0);
         if (nCurSel >= 0)
@@ -164,10 +171,7 @@
 }
 
 void CFFL_ListBox::SaveState(CPDFSDK_PageView* pPageView) {
-  ASSERT(pPageView);
-
-  CPWL_ListBox* pListBox =
-      static_cast<CPWL_ListBox*>(GetPDFWindow(pPageView, false));
+  CPWL_ListBox* pListBox = GetListBox(pPageView);
   if (!pListBox)
     return;
 
@@ -178,11 +182,47 @@
 }
 
 void CFFL_ListBox::RestoreState(CPDFSDK_PageView* pPageView) {
-  CPWL_ListBox* pListBox =
-      static_cast<CPWL_ListBox*>(GetPDFWindow(pPageView, false));
+  CPWL_ListBox* pListBox = GetListBox(pPageView);
   if (!pListBox)
     return;
 
   for (const auto& item : m_State)
     pListBox->Select(item);
 }
+
+bool CFFL_ListBox::SetIndexSelected(int index, bool selected) {
+  if (!IsValid())
+    return false;
+
+  if (index < 0 || index >= m_pWidget->CountOptions())
+    return false;
+
+  CPWL_ListBox* pListBox = GetListBox(GetCurPageView(true));
+  if (!pListBox)
+    return false;
+
+  if (selected) {
+    pListBox->Select(index);
+    pListBox->SetCaret(index);
+  } else {
+    pListBox->Deselect(index);
+    pListBox->SetCaret(index);
+  }
+
+  return true;
+}
+
+bool CFFL_ListBox::IsIndexSelected(int index) {
+  if (!IsValid())
+    return false;
+
+  if (index < 0 || index >= m_pWidget->CountOptions())
+    return false;
+
+  CPWL_ListBox* pListBox = GetListBox(GetCurPageView(true));
+  return pListBox && pListBox->IsItemSelected(index);
+}
+
+CPWL_ListBox* CFFL_ListBox::GetListBox(CPDFSDK_PageView* pPageView) {
+  return static_cast<CPWL_ListBox*>(GetPWLWindow(pPageView, /*bNew=*/false));
+}
diff --git a/fpdfsdk/formfiller/cffl_listbox.h b/fpdfsdk/formfiller/cffl_listbox.h
index 014865d..7f39f5a 100644
--- a/fpdfsdk/formfiller/cffl_listbox.h
+++ b/fpdfsdk/formfiller/cffl_listbox.h
@@ -7,31 +7,39 @@
 #ifndef FPDFSDK_FORMFILLER_CFFL_LISTBOX_H_
 #define FPDFSDK_FORMFILLER_CFFL_LISTBOX_H_
 
+#include <memory>
 #include <set>
 #include <vector>
 
 #include "fpdfsdk/formfiller/cffl_textobject.h"
 
-class CBA_FontMap;
+class CPWL_ListBox;
 
-class CFFL_ListBox : public CFFL_TextObject {
+class CFFL_ListBox final : public CFFL_TextObject {
  public:
   CFFL_ListBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
   ~CFFL_ListBox() override;
 
   // CFFL_TextObject:
   CPWL_Wnd::CreateParams GetCreateParam() override;
-  CPWL_Wnd* NewPDFWindow(const CPWL_Wnd::CreateParams& cp) override;
+  std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+      override;
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
   bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
   void SaveData(CPDFSDK_PageView* pPageView) override;
   void GetActionData(CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     PDFSDK_FieldAction& fa) override;
+                     CPDFSDK_FieldAction& fa) override;
   void SaveState(CPDFSDK_PageView* pPageView) override;
   void RestoreState(CPDFSDK_PageView* pPageView) override;
+  bool SetIndexSelected(int index, bool selected) override;
+  bool IsIndexSelected(int index) override;
 
  private:
+  CPWL_ListBox* GetListBox(CPDFSDK_PageView* pPageView);
+
   std::set<int> m_OriginSelections;
   std::vector<int> m_State;
 };
diff --git a/fpdfsdk/formfiller/cffl_pushbutton.cpp b/fpdfsdk/formfiller/cffl_pushbutton.cpp
index 7310da6..e9872f6 100644
--- a/fpdfsdk/formfiller/cffl_pushbutton.cpp
+++ b/fpdfsdk/formfiller/cffl_pushbutton.cpp
@@ -6,17 +6,22 @@
 
 #include "fpdfsdk/formfiller/cffl_pushbutton.h"
 
+#include <utility>
+
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
 #include "fpdfsdk/pwl/cpwl_special_button.h"
+#include "third_party/base/ptr_util.h"
 
 CFFL_PushButton::CFFL_PushButton(CPDFSDK_FormFillEnvironment* pApp,
                                  CPDFSDK_Widget* pWidget)
     : CFFL_Button(pApp, pWidget) {}
 
-CFFL_PushButton::~CFFL_PushButton() {}
+CFFL_PushButton::~CFFL_PushButton() = default;
 
-CPWL_Wnd* CFFL_PushButton::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) {
-  auto* pWnd = new CPWL_PushButton();
-  pWnd->Create(cp);
-  return pWnd;
+std::unique_ptr<CPWL_Wnd> CFFL_PushButton::NewPWLWindow(
+    const CPWL_Wnd::CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
+  auto pWnd = pdfium::MakeUnique<CPWL_PushButton>(cp, std::move(pAttachedData));
+  pWnd->Realize();
+  return std::move(pWnd);
 }
diff --git a/fpdfsdk/formfiller/cffl_pushbutton.h b/fpdfsdk/formfiller/cffl_pushbutton.h
index 7ee0767..9ebaf60 100644
--- a/fpdfsdk/formfiller/cffl_pushbutton.h
+++ b/fpdfsdk/formfiller/cffl_pushbutton.h
@@ -7,15 +7,20 @@
 #ifndef FPDFSDK_FORMFILLER_CFFL_PUSHBUTTON_H_
 #define FPDFSDK_FORMFILLER_CFFL_PUSHBUTTON_H_
 
+#include <memory>
+
 #include "fpdfsdk/formfiller/cffl_button.h"
 
-class CFFL_PushButton : public CFFL_Button {
+class CFFL_PushButton final : public CFFL_Button {
  public:
   CFFL_PushButton(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
   ~CFFL_PushButton() override;
 
   // CFFL_Button:
-  CPWL_Wnd* NewPDFWindow(const CPWL_Wnd::CreateParams& cp) override;
+  std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+      override;
 };
 
 #endif  // FPDFSDK_FORMFILLER_CFFL_PUSHBUTTON_H_
diff --git a/fpdfsdk/formfiller/cffl_radiobutton.cpp b/fpdfsdk/formfiller/cffl_radiobutton.cpp
index 73ac44d..caa6349 100644
--- a/fpdfsdk/formfiller/cffl_radiobutton.cpp
+++ b/fpdfsdk/formfiller/cffl_radiobutton.cpp
@@ -6,6 +6,9 @@
 
 #include "fpdfsdk/formfiller/cffl_radiobutton.h"
 
+#include <utility>
+
+#include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
@@ -18,22 +21,23 @@
 
 CFFL_RadioButton::~CFFL_RadioButton() {}
 
-CPWL_Wnd* CFFL_RadioButton::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) {
-  auto* pWnd = new CPWL_RadioButton();
-  pWnd->Create(cp);
+std::unique_ptr<CPWL_Wnd> CFFL_RadioButton::NewPWLWindow(
+    const CPWL_Wnd::CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
+  auto pWnd =
+      pdfium::MakeUnique<CPWL_RadioButton>(cp, std::move(pAttachedData));
+  pWnd->Realize();
   pWnd->SetCheck(m_pWidget->IsChecked());
-  return pWnd;
+  return std::move(pWnd);
 }
 
-bool CFFL_RadioButton::OnKeyDown(CPDFSDK_Annot* pAnnot,
-                                 uint32_t nKeyCode,
-                                 uint32_t nFlags) {
+bool CFFL_RadioButton::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) {
   switch (nKeyCode) {
     case FWL_VKEY_Return:
     case FWL_VKEY_Space:
       return true;
     default:
-      return CFFL_FormFiller::OnKeyDown(pAnnot, nKeyCode, nFlags);
+      return CFFL_FormFiller::OnKeyDown(nKeyCode, nFlags);
   }
 }
 
@@ -46,7 +50,7 @@
       CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
       ASSERT(pPageView);
 
-      CPDFSDK_Annot::ObservedPtr pObserved(m_pWidget.Get());
+      ObservedPtr<CPDFSDK_Annot> pObserved(m_pWidget.Get());
       if (m_pFormFillEnv->GetInteractiveFormFiller()->OnButtonUp(
               &pObserved, pPageView, nFlags) ||
           !pObserved) {
@@ -91,30 +95,29 @@
     return;
 
   bool bNewChecked = pWnd->IsChecked();
-
   if (bNewChecked) {
     CPDF_FormField* pField = m_pWidget->GetFormField();
     for (int32_t i = 0, sz = pField->CountControls(); i < sz; i++) {
       if (CPDF_FormControl* pCtrl = pField->GetControl(i)) {
-        if (pCtrl->IsChecked()) {
+        if (pCtrl->IsChecked())
           break;
-        }
       }
     }
   }
-  CPDFSDK_Widget::ObservedPtr observed_widget(m_pWidget.Get());
-  CFFL_RadioButton::ObservedPtr observed_this(this);
-
-  m_pWidget->SetCheck(bNewChecked, false);
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CFFL_RadioButton> observed_this(this);
+  m_pWidget->SetCheck(bNewChecked, NotificationOption::kDoNotNotify);
   if (!observed_widget)
     return;
+
   m_pWidget->UpdateField();
   if (!observed_widget || !observed_this)
     return;
+
   SetChangeMark();
 }
 
 CPWL_RadioButton* CFFL_RadioButton::GetRadioButton(CPDFSDK_PageView* pPageView,
                                                    bool bNew) {
-  return static_cast<CPWL_RadioButton*>(GetPDFWindow(pPageView, bNew));
+  return static_cast<CPWL_RadioButton*>(GetPWLWindow(pPageView, bNew));
 }
diff --git a/fpdfsdk/formfiller/cffl_radiobutton.h b/fpdfsdk/formfiller/cffl_radiobutton.h
index 2838254..8ba2f28 100644
--- a/fpdfsdk/formfiller/cffl_radiobutton.h
+++ b/fpdfsdk/formfiller/cffl_radiobutton.h
@@ -7,20 +7,23 @@
 #ifndef FPDFSDK_FORMFILLER_CFFL_RADIOBUTTON_H_
 #define FPDFSDK_FORMFILLER_CFFL_RADIOBUTTON_H_
 
+#include <memory>
+
 #include "fpdfsdk/formfiller/cffl_button.h"
 
 class CPWL_RadioButton;
 
-class CFFL_RadioButton : public CFFL_Button {
+class CFFL_RadioButton final : public CFFL_Button {
  public:
   CFFL_RadioButton(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
   ~CFFL_RadioButton() override;
 
   // CFFL_Button:
-  CPWL_Wnd* NewPDFWindow(const CPWL_Wnd::CreateParams& cp) override;
-  bool OnKeyDown(CPDFSDK_Annot* pAnnot,
-                 uint32_t nKeyCode,
-                 uint32_t nFlags) override;
+  std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+      override;
+  bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) override;
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
   bool OnLButtonUp(CPDFSDK_PageView* pPageView,
                    CPDFSDK_Annot* pAnnot,
diff --git a/fpdfsdk/formfiller/cffl_textfield.cpp b/fpdfsdk/formfiller/cffl_textfield.cpp
index ad8d27c..c3cd365 100644
--- a/fpdfsdk/formfiller/cffl_textfield.cpp
+++ b/fpdfsdk/formfiller/cffl_textfield.cpp
@@ -6,12 +6,26 @@
 
 #include "fpdfsdk/formfiller/cffl_textfield.h"
 
+#include <utility>
+
+#include "constants/form_flags.h"
+#include "core/fpdfdoc/cba_fontmap.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cba_fontmap.h"
-#include "fpdfsdk/fsdk_common.h"
+#include "public/fpdf_fwlevent.h"
 #include "third_party/base/ptr_util.h"
 
+namespace {
+
+// PDF 1.7 spec, Table 8.25
+enum Alignment {
+  kLeft = 0,
+  kCenter = 1,
+  kRight = 2,
+};
+
+}  // namespace
+
 CFFL_TextField::CFFL_TextField(CPDFSDK_FormFillEnvironment* pApp,
                                CPDFSDK_Widget* pWidget)
     : CFFL_TextObject(pApp, pWidget) {}
@@ -29,36 +43,36 @@
 CPWL_Wnd::CreateParams CFFL_TextField::GetCreateParam() {
   CPWL_Wnd::CreateParams cp = CFFL_TextObject::GetCreateParam();
   int nFlags = m_pWidget->GetFieldFlags();
-  if (nFlags & FIELDFLAG_PASSWORD)
+  if (nFlags & pdfium::form_flags::kTextPassword)
     cp.dwFlags |= PES_PASSWORD;
 
-  if (nFlags & FIELDFLAG_MULTILINE) {
+  if (nFlags & pdfium::form_flags::kTextMultiline) {
     cp.dwFlags |= PES_MULTILINE | PES_AUTORETURN | PES_TOP;
-    if (!(nFlags & FIELDFLAG_DONOTSCROLL))
+    if (!(nFlags & pdfium::form_flags::kTextDoNotScroll))
       cp.dwFlags |= PWS_VSCROLL | PES_AUTOSCROLL;
   } else {
     cp.dwFlags |= PES_CENTER;
-    if (!(nFlags & FIELDFLAG_DONOTSCROLL))
+    if (!(nFlags & pdfium::form_flags::kTextDoNotScroll))
       cp.dwFlags |= PES_AUTOSCROLL;
   }
 
-  if (nFlags & FIELDFLAG_COMB)
+  if (nFlags & pdfium::form_flags::kTextComb)
     cp.dwFlags |= PES_CHARARRAY;
 
-  if (nFlags & FIELDFLAG_RICHTEXT)
+  if (nFlags & pdfium::form_flags::kTextRichText)
     cp.dwFlags |= PES_RICH;
 
   cp.dwFlags |= PES_UNDO;
 
   switch (m_pWidget->GetAlignment()) {
     default:
-    case BF_ALIGN_LEFT:
+    case kLeft:
       cp.dwFlags |= PES_LEFT;
       break;
-    case BF_ALIGN_MIDDLE:
+    case kCenter:
       cp.dwFlags |= PES_MIDDLE;
       break;
-    case BF_ALIGN_RIGHT:
+    case kRight:
       cp.dwFlags |= PES_RIGHT;
       break;
   }
@@ -67,26 +81,26 @@
   return cp;
 }
 
-CPWL_Wnd* CFFL_TextField::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) {
-  auto* pWnd = new CPWL_Edit();
+std::unique_ptr<CPWL_Wnd> CFFL_TextField::NewPWLWindow(
+    const CPWL_Wnd::CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
+  auto pWnd = pdfium::MakeUnique<CPWL_Edit>(cp, std::move(pAttachedData));
   pWnd->AttachFFLData(this);
-  pWnd->Create(cp);
+  pWnd->Realize();
   pWnd->SetFillerNotify(m_pFormFillEnv->GetInteractiveFormFiller());
 
   int32_t nMaxLen = m_pWidget->GetMaxLen();
   WideString swValue = m_pWidget->GetValue();
-
   if (nMaxLen > 0) {
     if (pWnd->HasFlag(PES_CHARARRAY)) {
       pWnd->SetCharArray(nMaxLen);
-      pWnd->SetAlignFormatV(PEAV_CENTER);
+      pWnd->SetAlignFormatVerticalCenter();
     } else {
       pWnd->SetLimitChar(nMaxLen);
     }
   }
-
   pWnd->SetText(swValue);
-  return pWnd;
+  return std::move(pWnd);
 }
 
 bool CFFL_TextField::OnChar(CPDFSDK_Annot* pAnnot,
@@ -94,17 +108,17 @@
                             uint32_t nFlags) {
   switch (nChar) {
     case FWL_VKEY_Return: {
-      if (m_pWidget->GetFieldFlags() & FIELDFLAG_MULTILINE)
+      if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kTextMultiline)
         break;
 
       CPDFSDK_PageView* pPageView = GetCurPageView(true);
       ASSERT(pPageView);
       m_bValid = !m_bValid;
-      m_pFormFillEnv->Invalidate(pAnnot->GetUnderlyingPage(),
+      m_pFormFillEnv->Invalidate(pAnnot->GetPage(),
                                  pAnnot->GetRect().GetOuterRect());
 
       if (m_bValid) {
-        if (CPWL_Wnd* pWnd = GetPDFWindow(pPageView, true))
+        if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true))
           pWnd->SetFocus();
         break;
       }
@@ -112,7 +126,7 @@
       if (!CommitData(pPageView, nFlags))
         return false;
 
-      DestroyPDFWindow(pPageView);
+      DestroyPWLWindow(pPageView);
       return true;
     }
     case FWL_VKEY_Escape: {
@@ -138,45 +152,46 @@
 
   WideString sOldValue = m_pWidget->GetValue();
   WideString sNewValue = pWnd->GetText();
-
-  CPDFSDK_Widget::ObservedPtr observed_widget(m_pWidget.Get());
-  CFFL_TextField::ObservedPtr observed_this(this);
-
-  m_pWidget->SetValue(sNewValue, false);
+  ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
+  ObservedPtr<CFFL_TextField> observed_this(this);
+  m_pWidget->SetValue(sNewValue, NotificationOption::kDoNotNotify);
   if (!observed_widget)
     return;
-  m_pWidget->ResetFieldAppearance(true);
+
+  m_pWidget->ResetFieldAppearance();
   if (!observed_widget)
     return;
+
   m_pWidget->UpdateField();
   if (!observed_widget || !observed_this)
     return;
+
   SetChangeMark();
 }
 
 void CFFL_TextField::GetActionData(CPDFSDK_PageView* pPageView,
                                    CPDF_AAction::AActionType type,
-                                   PDFSDK_FieldAction& fa) {
+                                   CPDFSDK_FieldAction& fa) {
   switch (type) {
-    case CPDF_AAction::KeyStroke:
+    case CPDF_AAction::kKeyStroke:
       if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) {
         fa.bFieldFull = pWnd->IsTextFull();
 
         fa.sValue = pWnd->GetText();
 
         if (fa.bFieldFull) {
-          fa.sChange = L"";
-          fa.sChangeEx = L"";
+          fa.sChange.clear();
+          fa.sChangeEx.clear();
         }
       }
       break;
-    case CPDF_AAction::Validate:
+    case CPDF_AAction::kValidate:
       if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) {
         fa.sValue = pWnd->GetText();
       }
       break;
-    case CPDF_AAction::LoseFocus:
-    case CPDF_AAction::GetFocus:
+    case CPDF_AAction::kLoseFocus:
+    case CPDF_AAction::kGetFocus:
       fa.sValue = m_pWidget->GetValue();
       break;
     default:
@@ -186,13 +201,13 @@
 
 void CFFL_TextField::SetActionData(CPDFSDK_PageView* pPageView,
                                    CPDF_AAction::AActionType type,
-                                   const PDFSDK_FieldAction& fa) {
+                                   const CPDFSDK_FieldAction& fa) {
   switch (type) {
-    case CPDF_AAction::KeyStroke:
+    case CPDF_AAction::kKeyStroke:
       if (CPWL_Edit* pEdit = GetEdit(pPageView, false)) {
         pEdit->SetFocus();
         pEdit->SetSelection(fa.nSelStart, fa.nSelEnd);
-        pEdit->ReplaceSel(fa.sChange);
+        pEdit->ReplaceSelection(fa.sChange);
       }
       break;
     default:
@@ -200,24 +215,7 @@
   }
 }
 
-bool CFFL_TextField::IsActionDataChanged(CPDF_AAction::AActionType type,
-                                         const PDFSDK_FieldAction& faOld,
-                                         const PDFSDK_FieldAction& faNew) {
-  switch (type) {
-    case CPDF_AAction::KeyStroke:
-      return (!faOld.bFieldFull && faOld.nSelEnd != faNew.nSelEnd) ||
-             faOld.nSelStart != faNew.nSelStart ||
-             faOld.sChange != faNew.sChange;
-    default:
-      break;
-  }
-
-  return false;
-}
-
 void CFFL_TextField::SaveState(CPDFSDK_PageView* pPageView) {
-  ASSERT(pPageView);
-
   CPWL_Edit* pWnd = GetEdit(pPageView, false);
   if (!pWnd)
     return;
@@ -227,8 +225,6 @@
 }
 
 void CFFL_TextField::RestoreState(CPDFSDK_PageView* pPageView) {
-  ASSERT(pPageView);
-
   CPWL_Edit* pWnd = GetEdit(pPageView, true);
   if (!pWnd)
     return;
@@ -250,11 +246,11 @@
 
   WideString wsText = pEdit->GetText();
   int nCharacters = wsText.GetLength();
-  ByteString bsUTFText = wsText.UTF16LE_Encode();
+  ByteString bsUTFText = wsText.ToUTF16LE();
   auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str());
   m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true);
 }
 
 CPWL_Edit* CFFL_TextField::GetEdit(CPDFSDK_PageView* pPageView, bool bNew) {
-  return static_cast<CPWL_Edit*>(GetPDFWindow(pPageView, bNew));
+  return static_cast<CPWL_Edit*>(GetPWLWindow(pPageView, bNew));
 }
diff --git a/fpdfsdk/formfiller/cffl_textfield.h b/fpdfsdk/formfiller/cffl_textfield.h
index 49ffc0f..5941b42 100644
--- a/fpdfsdk/formfiller/cffl_textfield.h
+++ b/fpdfsdk/formfiller/cffl_textfield.h
@@ -7,44 +7,39 @@
 #ifndef FPDFSDK_FORMFILLER_CFFL_TEXTFIELD_H_
 #define FPDFSDK_FORMFILLER_CFFL_TEXTFIELD_H_
 
+#include <memory>
+
 #include "fpdfsdk/formfiller/cffl_textobject.h"
 
-#define BF_ALIGN_LEFT 0
-#define BF_ALIGN_MIDDLE 1
-#define BF_ALIGN_RIGHT 2
-
-class CBA_FontMap;
 class CPWL_Edit;
 
 struct FFL_TextFieldState {
-  FFL_TextFieldState() : nStart(0), nEnd(0) {}
-
-  int nStart;
-  int nEnd;
+  int nStart = 0;
+  int nEnd = 0;
   WideString sValue;
 };
 
-class CFFL_TextField : public CFFL_TextObject,
-                       public CPWL_Wnd::FocusHandlerIface {
+class CFFL_TextField final : public CFFL_TextObject,
+                             public CPWL_Wnd::FocusHandlerIface {
  public:
   CFFL_TextField(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget);
   ~CFFL_TextField() override;
 
   // CFFL_TextObject:
   CPWL_Wnd::CreateParams GetCreateParam() override;
-  CPWL_Wnd* NewPDFWindow(const CPWL_Wnd::CreateParams& cp) override;
+  std::unique_ptr<CPWL_Wnd> NewPWLWindow(
+      const CPWL_Wnd::CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+      override;
   bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
   bool IsDataChanged(CPDFSDK_PageView* pPageView) override;
   void SaveData(CPDFSDK_PageView* pPageView) override;
   void GetActionData(CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     PDFSDK_FieldAction& fa) override;
+                     CPDFSDK_FieldAction& fa) override;
   void SetActionData(CPDFSDK_PageView* pPageView,
                      CPDF_AAction::AActionType type,
-                     const PDFSDK_FieldAction& fa) override;
-  bool IsActionDataChanged(CPDF_AAction::AActionType type,
-                           const PDFSDK_FieldAction& faOld,
-                           const PDFSDK_FieldAction& faNew) override;
+                     const CPDFSDK_FieldAction& fa) override;
   void SaveState(CPDFSDK_PageView* pPageView) override;
   void RestoreState(CPDFSDK_PageView* pPageView) override;
 #ifdef PDF_ENABLE_XFA
diff --git a/fpdfsdk/formfiller/cffl_textobject.cpp b/fpdfsdk/formfiller/cffl_textobject.cpp
index df2f5e5..6d84960 100644
--- a/fpdfsdk/formfiller/cffl_textobject.cpp
+++ b/fpdfsdk/formfiller/cffl_textobject.cpp
@@ -6,16 +6,20 @@
 
 #include "fpdfsdk/formfiller/cffl_textobject.h"
 
-CPWL_Wnd* CFFL_TextObject::ResetPDFWindow(CPDFSDK_PageView* pPageView,
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfdoc/cba_fontmap.h"
+#include "third_party/base/ptr_util.h"
+
+CPWL_Wnd* CFFL_TextObject::ResetPWLWindow(CPDFSDK_PageView* pPageView,
                                           bool bRestoreValue) {
   if (bRestoreValue)
     SaveState(pPageView);
 
-  DestroyPDFWindow(pPageView);
+  DestroyPWLWindow(pPageView);
   if (bRestoreValue)
     RestoreState(pPageView);
 
-  CPWL_Wnd::ObservedPtr pRet(GetPDFWindow(pPageView, !bRestoreValue));
+  ObservedPtr<CPWL_Wnd> pRet(GetPWLWindow(pPageView, !bRestoreValue));
   m_pWidget->UpdateField();  // May invoke JS, invalidating |pRet|.
   return pRet.Get();
 }
@@ -32,8 +36,9 @@
 
 CBA_FontMap* CFFL_TextObject::MaybeCreateFontMap() {
   if (!m_pFontMap) {
-    m_pFontMap =
-        pdfium::MakeUnique<CBA_FontMap>(m_pWidget.Get(), GetSystemHandler());
+    m_pFontMap = pdfium::MakeUnique<CBA_FontMap>(
+        m_pWidget->GetPDFPage()->GetDocument(),
+        m_pWidget->GetPDFAnnot()->GetAnnotDict());
   }
   return m_pFontMap.get();
 }
diff --git a/fpdfsdk/formfiller/cffl_textobject.h b/fpdfsdk/formfiller/cffl_textobject.h
index fcb3c1b..a2381af 100644
--- a/fpdfsdk/formfiller/cffl_textobject.h
+++ b/fpdfsdk/formfiller/cffl_textobject.h
@@ -11,12 +11,14 @@
 
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
 
+class CBA_FontMap;
+
 // Class to implement common functionality for CFFL_FormFiller sub-classes with
 // text fields.
 class CFFL_TextObject : public CFFL_FormFiller {
  public:
   // CFFL_FormFiller:
-  CPWL_Wnd* ResetPDFWindow(CPDFSDK_PageView* pPageView,
+  CPWL_Wnd* ResetPWLWindow(CPDFSDK_PageView* pPageView,
                            bool bRestoreValue) override;
 
  protected:
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
new file mode 100644
index 0000000..c08ba5a
--- /dev/null
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -0,0 +1,1067 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_annot.h"
+
+#include <memory>
+#include <utility>
+
+#include "constants/annotation_common.h"
+#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
+#include "core/fpdfapi/page/cpdf_annotcontext.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/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/fpdfdoc/cpdf_annot.h"
+#include "core/fpdfdoc/cpdf_color_utils.h"
+#include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "core/fpdfdoc/cpvt_generateap.h"
+#include "core/fxge/cfx_color.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+// These checks ensure the consistency of annotation subtype values across core/
+// and public.
+static_assert(static_cast<int>(CPDF_Annot::Subtype::UNKNOWN) ==
+                  FPDF_ANNOT_UNKNOWN,
+              "CPDF_Annot::UNKNOWN value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::TEXT) == FPDF_ANNOT_TEXT,
+              "CPDF_Annot::TEXT value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::LINK) == FPDF_ANNOT_LINK,
+              "CPDF_Annot::LINK value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::FREETEXT) ==
+                  FPDF_ANNOT_FREETEXT,
+              "CPDF_Annot::FREETEXT value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::LINE) == FPDF_ANNOT_LINE,
+              "CPDF_Annot::LINE value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUARE) ==
+                  FPDF_ANNOT_SQUARE,
+              "CPDF_Annot::SQUARE value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::CIRCLE) ==
+                  FPDF_ANNOT_CIRCLE,
+              "CPDF_Annot::CIRCLE value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYGON) ==
+                  FPDF_ANNOT_POLYGON,
+              "CPDF_Annot::POLYGON value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYLINE) ==
+                  FPDF_ANNOT_POLYLINE,
+              "CPDF_Annot::POLYLINE value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::HIGHLIGHT) ==
+                  FPDF_ANNOT_HIGHLIGHT,
+              "CPDF_Annot::HIGHLIGHT value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::UNDERLINE) ==
+                  FPDF_ANNOT_UNDERLINE,
+              "CPDF_Annot::UNDERLINE value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUIGGLY) ==
+                  FPDF_ANNOT_SQUIGGLY,
+              "CPDF_Annot::SQUIGGLY value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::STRIKEOUT) ==
+                  FPDF_ANNOT_STRIKEOUT,
+              "CPDF_Annot::STRIKEOUT value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::STAMP) == FPDF_ANNOT_STAMP,
+              "CPDF_Annot::STAMP value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::CARET) == FPDF_ANNOT_CARET,
+              "CPDF_Annot::CARET value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::INK) == FPDF_ANNOT_INK,
+              "CPDF_Annot::INK value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::POPUP) == FPDF_ANNOT_POPUP,
+              "CPDF_Annot::POPUP value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::FILEATTACHMENT) ==
+                  FPDF_ANNOT_FILEATTACHMENT,
+              "CPDF_Annot::FILEATTACHMENT value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::SOUND) == FPDF_ANNOT_SOUND,
+              "CPDF_Annot::SOUND value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::MOVIE) == FPDF_ANNOT_MOVIE,
+              "CPDF_Annot::MOVIE value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::WIDGET) ==
+                  FPDF_ANNOT_WIDGET,
+              "CPDF_Annot::WIDGET value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::SCREEN) ==
+                  FPDF_ANNOT_SCREEN,
+              "CPDF_Annot::SCREEN value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::PRINTERMARK) ==
+                  FPDF_ANNOT_PRINTERMARK,
+              "CPDF_Annot::PRINTERMARK value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::TRAPNET) ==
+                  FPDF_ANNOT_TRAPNET,
+              "CPDF_Annot::TRAPNET value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::WATERMARK) ==
+                  FPDF_ANNOT_WATERMARK,
+              "CPDF_Annot::WATERMARK value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::THREED) ==
+                  FPDF_ANNOT_THREED,
+              "CPDF_Annot::THREED value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::RICHMEDIA) ==
+                  FPDF_ANNOT_RICHMEDIA,
+              "CPDF_Annot::RICHMEDIA value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::Subtype::XFAWIDGET) ==
+                  FPDF_ANNOT_XFAWIDGET,
+              "CPDF_Annot::XFAWIDGET value mismatch");
+
+// These checks ensure the consistency of annotation appearance mode values
+// across core/ and public.
+static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Normal) ==
+                  FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+              "CPDF_Annot::AppearanceMode::Normal value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Rollover) ==
+                  FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+              "CPDF_Annot::AppearanceMode::Rollover value mismatch");
+static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Down) ==
+                  FPDF_ANNOT_APPEARANCEMODE_DOWN,
+              "CPDF_Annot::AppearanceMode::Down value mismatch");
+
+// These checks ensure the consistency of dictionary value types across core/
+// and public/.
+static_assert(static_cast<int>(CPDF_Object::Type::kBoolean) ==
+                  FPDF_OBJECT_BOOLEAN,
+              "CPDF_Object::kBoolean value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kNumber) ==
+                  FPDF_OBJECT_NUMBER,
+              "CPDF_Object::kNumber value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kString) ==
+                  FPDF_OBJECT_STRING,
+              "CPDF_Object::kString value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kName) == FPDF_OBJECT_NAME,
+              "CPDF_Object::kName value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kArray) == FPDF_OBJECT_ARRAY,
+              "CPDF_Object::kArray value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kDictionary) ==
+                  FPDF_OBJECT_DICTIONARY,
+              "CPDF_Object::kDictionary value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kStream) ==
+                  FPDF_OBJECT_STREAM,
+              "CPDF_Object::kStream value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kNullobj) ==
+                  FPDF_OBJECT_NULLOBJ,
+              "CPDF_Object::kNullobj value mismatch");
+static_assert(static_cast<int>(CPDF_Object::Type::kReference) ==
+                  FPDF_OBJECT_REFERENCE,
+              "CPDF_Object::kReference value mismatch");
+
+bool HasAPStream(CPDF_Dictionary* pAnnotDict) {
+  return !!GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+}
+
+void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) {
+  ASSERT(pForm);
+  ASSERT(pStream);
+
+  CPDF_PageContentGenerator generator(pForm);
+  std::ostringstream buf;
+  generator.ProcessPageObjects(&buf);
+  pStream->SetDataFromStringstreamAndRemoveFilter(&buf);
+}
+
+void SetQuadPointsAtIndex(CPDF_Array* array,
+                          size_t quad_index,
+                          const FS_QUADPOINTSF* quad_points) {
+  ASSERT(array);
+  ASSERT(quad_points);
+  ASSERT(IsValidQuadPointsIndex(array, quad_index));
+
+  size_t nIndex = quad_index * 8;
+  array->SetNewAt<CPDF_Number>(nIndex, quad_points->x1);
+  array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y1);
+  array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x2);
+  array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y2);
+  array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x3);
+  array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y3);
+  array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x4);
+  array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y4);
+}
+
+void AppendQuadPoints(CPDF_Array* array, const FS_QUADPOINTSF* quad_points) {
+  ASSERT(quad_points);
+  ASSERT(array);
+
+  array->AddNew<CPDF_Number>(quad_points->x1);
+  array->AddNew<CPDF_Number>(quad_points->y1);
+  array->AddNew<CPDF_Number>(quad_points->x2);
+  array->AddNew<CPDF_Number>(quad_points->y2);
+  array->AddNew<CPDF_Number>(quad_points->x3);
+  array->AddNew<CPDF_Number>(quad_points->y3);
+  array->AddNew<CPDF_Number>(quad_points->x4);
+  array->AddNew<CPDF_Number>(quad_points->y4);
+}
+
+void UpdateBBox(CPDF_Dictionary* annot_dict) {
+  ASSERT(annot_dict);
+  // Update BBox entry in appearance stream based on the bounding rectangle
+  // of the annotation's quadpoints.
+  CPDF_Stream* pStream =
+      GetAnnotAP(annot_dict, CPDF_Annot::AppearanceMode::Normal);
+  if (pStream) {
+    CFX_FloatRect boundingRect =
+        CPDF_Annot::BoundingRectFromQuadPoints(annot_dict);
+    if (boundingRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
+      pStream->GetDict()->SetRectFor("BBox", boundingRect);
+  }
+}
+
+CPDF_Dictionary* GetAnnotDictFromFPDFAnnotation(FPDF_ANNOTATION annot) {
+  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot);
+  return context ? context->GetAnnotDict() : nullptr;
+}
+
+RetainPtr<CPDF_Dictionary> SetExtGStateInResourceDict(
+    CPDF_Document* pDoc,
+    const CPDF_Dictionary* pAnnotDict,
+    const ByteString& sBlendMode) {
+  auto pGSDict =
+      pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict->GetByteStringPool());
+
+  // ExtGState represents a graphics state parameter dictionary.
+  pGSDict->SetNewFor<CPDF_Name>("Type", "ExtGState");
+
+  // CA respresents current stroking alpha specifying constant opacity
+  // value that should be used in transparent imaging model.
+  float fOpacity = pAnnotDict->GetNumberFor("CA");
+
+  pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
+
+  // ca represents fill color alpha specifying constant opacity
+  // value that should be used in transparent imaging model.
+  pGSDict->SetNewFor<CPDF_Number>("ca", fOpacity);
+
+  // AIS represents alpha source flag specifying whether current alpha
+  // constant shall be interpreted as shape value (true) or opacity value
+  // (false).
+  pGSDict->SetNewFor<CPDF_Boolean>("AIS", false);
+
+  // BM represents Blend Mode
+  pGSDict->SetNewFor<CPDF_Name>("BM", sBlendMode);
+
+  auto pExtGStateDict =
+      pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict->GetByteStringPool());
+
+  pExtGStateDict->SetFor("GS", pGSDict);
+
+  auto pResourceDict = pDoc->New<CPDF_Dictionary>();
+  pResourceDict->SetFor("ExtGState", pExtGStateDict);
+  return pResourceDict;
+}
+
+CPDF_FormField* GetFormField(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return nullptr;
+
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return nullptr;
+
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  return pPDFForm->GetFieldByDict(pAnnotDict);
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
+  // The supported subtypes must also be communicated in the user doc.
+  return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_FREETEXT ||
+         subtype == FPDF_ANNOT_HIGHLIGHT || subtype == FPDF_ANNOT_INK ||
+         subtype == FPDF_ANNOT_POPUP || subtype == FPDF_ANNOT_SQUARE ||
+         subtype == FPDF_ANNOT_SQUIGGLY || subtype == FPDF_ANNOT_STAMP ||
+         subtype == FPDF_ANNOT_STRIKEOUT || subtype == FPDF_ANNOT_TEXT ||
+         subtype == FPDF_ANNOT_UNDERLINE;
+}
+
+FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
+FPDFPage_CreateAnnot(FPDF_PAGE page, FPDF_ANNOTATION_SUBTYPE subtype) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage || !FPDFAnnot_IsSupportedSubtype(subtype))
+    return nullptr;
+
+  auto pDict = pPage->GetDocument()->New<CPDF_Dictionary>();
+  pDict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "Annot");
+  pDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype,
+                              CPDF_Annot::AnnotSubtypeToString(
+                                  static_cast<CPDF_Annot::Subtype>(subtype)));
+  auto pNewAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(pDict.Get(), pPage);
+
+  CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots");
+  if (!pAnnotList)
+    pAnnotList = pPage->GetDict()->SetNewFor<CPDF_Array>("Annots");
+  pAnnotList->Add(pDict);
+
+  // Caller takes ownership.
+  return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotCount(FPDF_PAGE page) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return 0;
+
+  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  return pAnnots ? pAnnots->size() : 0;
+}
+
+FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page,
+                                                            int index) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage || index < 0)
+    return nullptr;
+
+  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  if (!pAnnots || static_cast<size_t>(index) >= pAnnots->size())
+    return nullptr;
+
+  CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(index));
+  if (!pDict)
+    return nullptr;
+
+  auto pNewAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(pDict, pPage);
+
+  // Caller takes ownership.
+  return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotIndex(FPDF_PAGE page,
+                                                     FPDF_ANNOTATION annot) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return -1;
+
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return -1;
+
+  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  if (!pAnnots)
+    return -1;
+
+  CPDF_ArrayLocker locker(pAnnots);
+  auto it = std::find_if(locker.begin(), locker.end(),
+                         [pAnnotDict](const RetainPtr<CPDF_Object>& candidate) {
+                           return candidate->GetDirect() == pAnnotDict;
+                         });
+
+  if (it == locker.end())
+    return -1;
+
+  return it - locker.begin();
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_CloseAnnot(FPDF_ANNOTATION annot) {
+  delete CPDFAnnotContextFromFPDFAnnotation(annot);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_RemoveAnnot(FPDF_PAGE page,
+                                                         int index) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage || index < 0)
+    return false;
+
+  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  if (!pAnnots || static_cast<size_t>(index) >= pAnnots->size())
+    return false;
+
+  pAnnots->RemoveAt(index);
+  return true;
+}
+
+FPDF_EXPORT FPDF_ANNOTATION_SUBTYPE FPDF_CALLCONV
+FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return FPDF_ANNOT_UNKNOWN;
+
+  return static_cast<FPDF_ANNOTATION_SUBTYPE>(CPDF_Annot::StringToAnnotSubtype(
+      pAnnotDict->GetStringFor(pdfium::annotation::kSubtype)));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_IsObjectSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
+  // The supported subtypes must also be communicated in the user doc.
+  return subtype == FPDF_ANNOT_INK || subtype == FPDF_ANNOT_STAMP;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
+  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
+  if (!pAnnot || !pAnnot->HasForm() || !pObj)
+    return false;
+
+  // Check that the annotation type is supported by this method.
+  if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
+    return false;
+
+  // Check that the annotation already has an appearance stream, since an
+  // existing object is to be updated.
+  CPDF_Stream* pStream =
+      GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+  if (!pStream)
+    return false;
+
+  // Check that the object is already in this annotation's object list.
+  CPDF_Form* pForm = pAnnot->GetForm();
+  auto it =
+      std::find_if(pForm->begin(), pForm->end(),
+                   [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
+                     return candidate.get() == pObj;
+                   });
+  if (it == pForm->end())
+    return false;
+
+  // Update the content stream data in the annotation's AP stream.
+  UpdateContentStream(pForm, pStream);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_AppendObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
+  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
+  if (!pAnnot || !pObj)
+    return false;
+
+  // Check that the annotation type is supported by this method.
+  if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
+    return false;
+
+  // If the annotation does not have an AP stream yet, generate and set it.
+  CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
+  CPDF_Stream* pStream =
+      GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+  if (!pStream) {
+    CPVT_GenerateAP::GenerateEmptyAP(pAnnot->GetPage()->GetDocument(),
+                                     pAnnotDict);
+    pStream = GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+    if (!pStream)
+      return false;
+  }
+
+  // Get the annotation's corresponding form object for parsing its AP stream.
+  if (!pAnnot->HasForm())
+    pAnnot->SetForm(pStream);
+
+  // Check that the object did not come from the same annotation. If this check
+  // succeeds, then it is assumed that the object came from
+  // FPDFPageObj_CreateNew{Path|Rect}() or FPDFPageObj_New{Text|Image}Obj().
+  // Note that an object that came from a different annotation must not be
+  // passed here, since an object cannot belong to more than one annotation.
+  CPDF_Form* pForm = pAnnot->GetForm();
+  auto it =
+      std::find_if(pForm->begin(), pForm->end(),
+                   [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
+                     return candidate.get() == pObj;
+                   });
+  if (it != pForm->end())
+    return false;
+
+  // Append the object to the object list.
+  pForm->AppendPageObject(pdfium::WrapUnique(pObj));
+
+  // Set the content stream data in the annotation's AP stream.
+  UpdateContentStream(pForm, pStream);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetObjectCount(FPDF_ANNOTATION annot) {
+  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  if (!pAnnot)
+    return 0;
+
+  if (!pAnnot->HasForm()) {
+    CPDF_Stream* pStream =
+        GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+    if (!pStream)
+      return 0;
+
+    pAnnot->SetForm(pStream);
+  }
+  return pAnnot->GetForm()->GetPageObjectCount();
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDFAnnot_GetObject(FPDF_ANNOTATION annot, int index) {
+  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  if (!pAnnot || index < 0)
+    return nullptr;
+
+  if (!pAnnot->HasForm()) {
+    CPDF_Stream* pStream =
+        GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+    if (!pStream)
+      return nullptr;
+
+    pAnnot->SetForm(pStream);
+  }
+
+  return FPDFPageObjectFromCPDFPageObject(
+      pAnnot->GetForm()->GetPageObjectByIndex(index));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_RemoveObject(FPDF_ANNOTATION annot, int index) {
+  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  if (!pAnnot || !pAnnot->HasForm() || index < 0)
+    return false;
+
+  // Check that the annotation type is supported by this method.
+  if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
+    return false;
+
+  // Check that the annotation already has an appearance stream, since an
+  // existing object is to be deleted.
+  CPDF_Stream* pStream =
+      GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
+  if (!pStream)
+    return false;
+
+  if (!pAnnot->GetForm()->ErasePageObjectAtIndex(index))
+    return false;
+
+  UpdateContentStream(pAnnot->GetForm(), pStream);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetColor(FPDF_ANNOTATION annot,
+                                                       FPDFANNOT_COLORTYPE type,
+                                                       unsigned int R,
+                                                       unsigned int G,
+                                                       unsigned int B,
+                                                       unsigned int A) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict || R > 255 || G > 255 || B > 255 || A > 255)
+    return false;
+
+  // For annotations with their appearance streams already defined, the path
+  // stream's own color definitions take priority over the annotation color
+  // definitions set by this method, hence this method will simply fail.
+  if (HasAPStream(pAnnotDict))
+    return false;
+
+  // Set the opacity of the annotation.
+  pAnnotDict->SetNewFor<CPDF_Number>("CA", A / 255.f);
+
+  // Set the color of the annotation.
+  ByteString key = type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C";
+  CPDF_Array* pColor = pAnnotDict->GetArrayFor(key);
+  if (pColor)
+    pColor->Clear();
+  else
+    pColor = pAnnotDict->SetNewFor<CPDF_Array>(key);
+
+  pColor->AddNew<CPDF_Number>(R / 255.f);
+  pColor->AddNew<CPDF_Number>(G / 255.f);
+  pColor->AddNew<CPDF_Number>(B / 255.f);
+
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetColor(FPDF_ANNOTATION annot,
+                                                       FPDFANNOT_COLORTYPE type,
+                                                       unsigned int* R,
+                                                       unsigned int* G,
+                                                       unsigned int* B,
+                                                       unsigned int* A) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict || !R || !G || !B || !A)
+    return false;
+
+  // For annotations with their appearance streams already defined, the path
+  // stream's own color definitions take priority over the annotation color
+  // definitions retrieved by this method, hence this method will simply fail.
+  if (HasAPStream(pAnnotDict))
+    return false;
+
+  CPDF_Array* pColor = pAnnotDict->GetArrayFor(
+      type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C");
+  *A =
+      (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetNumberFor("CA") : 1) * 255.f;
+  if (!pColor) {
+    // Use default color. The default colors must be consistent with the ones
+    // used to generate AP. See calls to GetColorStringWithDefault() in
+    // CPVT_GenerateAP::Generate*AP().
+    if (pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) == "Highlight") {
+      *R = 255;
+      *G = 255;
+      *B = 0;
+    } else {
+      *R = 0;
+      *G = 0;
+      *B = 0;
+    }
+    return true;
+  }
+
+  CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor);
+  switch (color.nColorType) {
+    case CFX_Color::kRGB:
+      *R = color.fColor1 * 255.f;
+      *G = color.fColor2 * 255.f;
+      *B = color.fColor3 * 255.f;
+      break;
+    case CFX_Color::kGray:
+      *R = 255.f * color.fColor1;
+      *G = 255.f * color.fColor1;
+      *B = 255.f * color.fColor1;
+      break;
+    case CFX_Color::kCMYK:
+      *R = 255.f * (1 - color.fColor1) * (1 - color.fColor4);
+      *G = 255.f * (1 - color.fColor2) * (1 - color.fColor4);
+      *B = 255.f * (1 - color.fColor3) * (1 - color.fColor4);
+      break;
+    case CFX_Color::kTransparent:
+      *R = 0;
+      *G = 0;
+      *B = 0;
+      break;
+  }
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot) {
+  if (!annot)
+    return false;
+
+  FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
+  return subtype == FPDF_ANNOT_LINK || subtype == FPDF_ANNOT_HIGHLIGHT ||
+         subtype == FPDF_ANNOT_UNDERLINE || subtype == FPDF_ANNOT_SQUIGGLY ||
+         subtype == FPDF_ANNOT_STRIKEOUT;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,
+                              size_t quad_index,
+                              const FS_QUADPOINTSF* quad_points) {
+  if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
+    return false;
+
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
+  CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+  if (!IsValidQuadPointsIndex(pQuadPointsArray, quad_index))
+    return false;
+
+  SetQuadPointsAtIndex(pQuadPointsArray, quad_index, quad_points);
+  UpdateBBox(pAnnotDict);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_AppendAttachmentPoints(FPDF_ANNOTATION annot,
+                                 const FS_QUADPOINTSF* quad_points) {
+  if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
+    return false;
+
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
+  CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+  if (!pQuadPointsArray)
+    pQuadPointsArray = AddQuadPointsArrayToDictionary(pAnnotDict);
+  AppendQuadPoints(pQuadPointsArray, quad_points);
+  UpdateBBox(pAnnotDict);
+  return true;
+}
+
+FPDF_EXPORT size_t FPDF_CALLCONV
+FPDFAnnot_CountAttachmentPoints(FPDF_ANNOTATION annot) {
+  if (!FPDFAnnot_HasAttachmentPoints(annot))
+    return 0;
+
+  const CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
+  const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+  return pArray ? pArray->size() / 8 : 0;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,
+                              size_t quad_index,
+                              FS_QUADPOINTSF* quad_points) {
+  if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
+    return false;
+
+  const CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
+  const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+  if (!pArray)
+    return false;
+
+  return GetQuadPointsAtIndex(pArray, quad_index, quad_points);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot,
+                                                      const FS_RECTF* rect) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict || !rect)
+    return false;
+
+  CFX_FloatRect newRect = CFXFloatRectFromFSRectF(*rect);
+
+  // Update the "Rect" entry in the annotation dictionary.
+  pAnnotDict->SetRectFor(pdfium::annotation::kRect, newRect);
+
+  // If the annotation's appearance stream is defined, the annotation is of a
+  // type that does not have quadpoints, and the new rectangle is bigger than
+  // the current bounding box, then update the "BBox" entry in the AP
+  // dictionary too, since its "BBox" entry comes from annotation dictionary's
+  // "Rect" entry.
+  if (FPDFAnnot_HasAttachmentPoints(annot))
+    return true;
+
+  CPDF_Stream* pStream =
+      GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
+  if (pStream && newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
+    pStream->GetDict()->SetRectFor("BBox", newRect);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot,
+                                                      FS_RECTF* rect) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict || !rect)
+    return false;
+
+  *rect = FSRectFFromCFXFloatRect(
+      pAnnotDict->GetRectFor(pdfium::annotation::kRect));
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot,
+                                                     FPDF_BYTESTRING key) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  return pAnnotDict && pAnnotDict->KeyExist(key);
+}
+
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDFAnnot_GetValueType(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
+  if (!FPDFAnnot_HasKey(annot, key))
+    return FPDF_OBJECT_UNKNOWN;
+
+  auto* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  CPDF_Object* pObj = pAnnot->GetAnnotDict()->GetObjectFor(key);
+  return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_SetStringValue(FPDF_ANNOTATION annot,
+                         FPDF_BYTESTRING key,
+                         FPDF_WIDESTRING value) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return false;
+
+  pAnnotDict->SetNewFor<CPDF_String>(key, WideStringFromFPDFWideString(value));
+  return true;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetStringValue(FPDF_ANNOTATION annot,
+                         FPDF_BYTESTRING key,
+                         FPDF_WCHAR* buffer,
+                         unsigned long buflen) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return 0;
+
+  return Utf16EncodeMaybeCopyAndReturnLength(pAnnotDict->GetUnicodeTextFor(key),
+                                             buffer, buflen);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetNumberValue(FPDF_ANNOTATION annot,
+                         FPDF_BYTESTRING key,
+                         float* value) {
+  if (!value)
+    return false;
+
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return false;
+
+  const CPDF_Object* p = pAnnotDict->GetObjectFor(key);
+  if (!p || p->GetType() != FPDF_OBJECT_NUMBER)
+    return false;
+
+  *value = p->GetNumber();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_SetAP(FPDF_ANNOTATION annot,
+                FPDF_ANNOT_APPEARANCEMODE appearanceMode,
+                FPDF_WIDESTRING value) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return false;
+
+  if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
+    return false;
+
+  static constexpr const char* kModeKeyForMode[] = {"N", "R", "D"};
+  static_assert(
+      FX_ArraySize(kModeKeyForMode) == FPDF_ANNOT_APPEARANCEMODE_COUNT,
+      "length of kModeKeyForMode should be equal to "
+      "FPDF_ANNOT_APPEARANCEMODE_COUNT");
+  const char* modeKey = kModeKeyForMode[appearanceMode];
+
+  CPDF_Dictionary* pApDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
+
+  // If value is null, we're in remove mode. Otherwise, we're in add/update
+  // mode.
+  if (value) {
+    // Annotation object's non-empty bounding rect will be used as the /BBox
+    // for the associated /XObject object
+    CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
+    constexpr float kMinSize = 0.000001f;
+    if (rect.Width() < kMinSize || rect.Height() < kMinSize)
+      return false;
+
+    CPDF_AnnotContext* pAnnotContext =
+        CPDFAnnotContextFromFPDFAnnotation(annot);
+
+    CPDF_Document* pDoc = pAnnotContext->GetPage()->GetDocument();
+    if (!pDoc)
+      return false;
+
+    CPDF_Stream* pNewIndirectStream = pDoc->NewIndirect<CPDF_Stream>();
+
+    ByteString newAPStream =
+        PDF_EncodeText(WideStringFromFPDFWideString(value));
+    pNewIndirectStream->SetData(newAPStream.raw_span());
+
+    CPDF_Dictionary* pStreamDict = pNewIndirectStream->GetDict();
+    pStreamDict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "XObject");
+    pStreamDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Form");
+    pStreamDict->SetRectFor("BBox", rect);
+    // Transparency values are specified in range [0.0f, 1.0f]. We are strictly
+    // checking for value < 1 and not <= 1 so that the output PDF size does not
+    // unnecessarily bloat up by creating a new dictionary in case of solid
+    // color.
+    if (pAnnotDict->KeyExist("CA") && pAnnotDict->GetNumberFor("CA") < 1.0f) {
+      RetainPtr<CPDF_Dictionary> pResourceDict =
+          SetExtGStateInResourceDict(pDoc, pAnnotDict, "Normal");
+      pStreamDict->SetFor("Resources", pResourceDict);
+    }
+
+    // Storing reference to indirect object in annotation's AP
+    if (!pApDict)
+      pApDict = pAnnotDict->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
+
+    pApDict->SetNewFor<CPDF_Reference>(modeKey, pDoc,
+                                       pNewIndirectStream->GetObjNum());
+  } else {
+    if (pApDict) {
+      if (appearanceMode == FPDF_ANNOT_APPEARANCEMODE_NORMAL)
+        pAnnotDict->RemoveFor(pdfium::annotation::kAP);
+      else
+        pApDict->RemoveFor(modeKey);
+    }
+  }
+
+  return true;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetAP(FPDF_ANNOTATION annot,
+                FPDF_ANNOT_APPEARANCEMODE appearanceMode,
+                FPDF_WCHAR* buffer,
+                unsigned long buflen) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return 0;
+
+  if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
+    return 0;
+
+  CPDF_Annot::AppearanceMode mode =
+      static_cast<CPDF_Annot::AppearanceMode>(appearanceMode);
+
+  CPDF_Stream* pStream = GetAnnotAPNoFallback(pAnnotDict, mode);
+  return Utf16EncodeMaybeCopyAndReturnLength(
+      pStream ? pStream->GetUnicodeText() : WideString(), buffer, buflen);
+}
+
+FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
+FPDFAnnot_GetLinkedAnnot(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
+  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
+  if (!pAnnot)
+    return nullptr;
+
+  CPDF_Dictionary* pLinkedDict = pAnnot->GetAnnotDict()->GetDictFor(key);
+  if (!pLinkedDict || pLinkedDict->GetStringFor("Type") != "Annot")
+    return nullptr;
+
+  auto pLinkedAnnot =
+      pdfium::MakeUnique<CPDF_AnnotContext>(pLinkedDict, pAnnot->GetPage());
+
+  // Caller takes ownership.
+  return FPDFAnnotationFromCPDFAnnotContext(pLinkedAnnot.release());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetFlags(FPDF_ANNOTATION annot) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  return pAnnotDict ? pAnnotDict->GetIntegerFor(pdfium::annotation::kF)
+                    : FPDF_ANNOT_FLAG_NONE;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetFlags(FPDF_ANNOTATION annot,
+                                                       int flags) {
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return false;
+
+  pAnnotDict->SetNewFor<CPDF_Number>(pdfium::annotation::kF, flags);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFormFieldFlags(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
+  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  return pFormField ? pFormField->GetFieldFlags() : FPDF_FORMFLAG_NONE;
+}
+
+FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
+FPDFAnnot_GetFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
+                              FPDF_PAGE page,
+                              const FS_POINTF* point) {
+  if (!point)
+    return nullptr;
+
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return nullptr;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return nullptr;
+
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  int annot_index = -1;
+  CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
+      pPage, CFXPointFFromFSPointF(*point), &annot_index);
+  if (!pFormCtrl || annot_index == -1)
+    return nullptr;
+  return FPDFPage_GetAnnot(page, annot_index);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldName(FPDF_FORMHANDLE hHandle,
+                           FPDF_ANNOTATION annot,
+                           FPDF_WCHAR* buffer,
+                           unsigned long buflen) {
+  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  if (!pFormField)
+    return 0;
+  return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetFullName(), buffer,
+                                             buflen);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFormFieldType(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
+  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle,
+                            FPDF_ANNOTATION annot,
+                            FPDF_WCHAR* buffer,
+                            unsigned long buflen) {
+  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  if (!pFormField)
+    return 0;
+  return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetValue(), buffer,
+                                             buflen);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetOptionCount(FPDF_FORMHANDLE hHandle,
+                                                       FPDF_ANNOTATION annot) {
+  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  return pFormField ? pFormField->CountOptions() : -1;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetOptionLabel(FPDF_FORMHANDLE hHandle,
+                         FPDF_ANNOTATION annot,
+                         int index,
+                         FPDF_WCHAR* buffer,
+                         unsigned long buflen) {
+  if (index < 0)
+    return 0;
+
+  CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+  if (!pFormField || index >= pFormField->CountOptions())
+    return 0;
+
+  WideString ws = pFormField->GetOptionLabel(index);
+  return Utf16EncodeMaybeCopyAndReturnLength(ws, buffer, buflen);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,
+                      FPDF_ANNOTATION annot,
+                      float* value) {
+  if (!value)
+    return false;
+
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return false;
+
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return false;
+
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict);
+  if (!pFormControl)
+    return false;
+
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
+  if (!pWidget)
+    return false;
+
+  *value = pWidget->GetFontSize();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle,
+                                                        FPDF_ANNOTATION annot) {
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return false;
+
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return false;
+
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  CPDF_FormField* pFormField = pPDFForm->GetFieldByDict(pAnnotDict);
+  if (!pFormField)
+    return false;
+
+  if (pFormField->GetType() != CPDF_FormField::kCheckBox &&
+      pFormField->GetType() != CPDF_FormField::kRadioButton) {
+    return false;
+  }
+
+  CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict);
+  if (!pFormControl)
+    return false;
+
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
+  return pWidget && pWidget->IsChecked();
+}
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
new file mode 100644
index 0000000..1e0ebd8
--- /dev/null
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -0,0 +1,2387 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cwchar>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+#include "constants/annotation_common.h"
+#include "core/fxcrt/fx_system.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_annot.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdf_formfill.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/hash.h"
+
+class FPDFAnnotEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFAnnotEmbedderTest, BadParams) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_EQ(0, FPDFPage_GetAnnotCount(nullptr));
+
+  EXPECT_FALSE(FPDFPage_GetAnnot(nullptr, 0));
+  EXPECT_FALSE(FPDFPage_GetAnnot(nullptr, -1));
+  EXPECT_FALSE(FPDFPage_GetAnnot(nullptr, 1));
+  EXPECT_FALSE(FPDFPage_GetAnnot(page, -1));
+  EXPECT_FALSE(FPDFPage_GetAnnot(page, 1));
+
+  EXPECT_EQ(FPDF_ANNOT_UNKNOWN, FPDFAnnot_GetSubtype(nullptr));
+
+  EXPECT_EQ(0, FPDFAnnot_GetObjectCount(nullptr));
+
+  EXPECT_FALSE(FPDFAnnot_GetObject(nullptr, 0));
+  EXPECT_FALSE(FPDFAnnot_GetObject(nullptr, -1));
+  EXPECT_FALSE(FPDFAnnot_GetObject(nullptr, 1));
+
+  EXPECT_FALSE(FPDFAnnot_HasKey(nullptr, "foo"));
+
+  static const wchar_t kContents[] = L"Bar";
+  ScopedFPDFWideString text = GetFPDFWideString(kContents);
+  EXPECT_FALSE(FPDFAnnot_SetStringValue(nullptr, "foo", text.get()));
+
+  FPDF_WCHAR buffer[64];
+  EXPECT_EQ(0u, FPDFAnnot_GetStringValue(nullptr, "foo", nullptr, 0));
+  EXPECT_EQ(0u, FPDFAnnot_GetStringValue(nullptr, "foo", buffer, 0));
+  EXPECT_EQ(0u,
+            FPDFAnnot_GetStringValue(nullptr, "foo", buffer, sizeof(buffer)));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, BadAnnotsEntry) {
+  ASSERT_TRUE(OpenDocument("bad_annots_entry.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+  EXPECT_FALSE(FPDFPage_GetAnnot(page, 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, RenderAnnotWithOnlyRolloverAP) {
+  // Open a file with one annotation and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // This annotation has a malformed appearance stream, which does not have its
+  // normal appearance defined, only its rollover appearance. In this case, its
+  // normal appearance should be generated, allowing the highlight annotation to
+  // still display.
+  ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+  CompareBitmap(bitmap.get(), 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RenderMultilineMarkupAnnotWithoutAP \
+  DISABLED_RenderMultilineMarkupAnnotWithoutAP
+#else
+#define MAYBE_RenderMultilineMarkupAnnotWithoutAP \
+  RenderMultilineMarkupAnnotWithoutAP
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_RenderMultilineMarkupAnnotWithoutAP) {
+  static const char kMd5[] = "76512832d88017668d9acc7aacd13dae";
+  // Open a file with multiline markup annotations.
+  ASSERT_TRUE(OpenDocument("annotation_markup_multiline_no_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+  CompareBitmap(bitmap.get(), 595, 842, kMd5);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, ExtractHighlightLongContent) {
+  // Open a file with one annotation and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
+  FPDF_PAGE page = LoadPageNoEvents(0);
+  ASSERT_TRUE(page);
+
+  // Check that there is a total of 1 annotation on its first page.
+  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+  // Check that the annotation is of type "highlight".
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
+
+    // Check that the annotation color is yellow.
+    unsigned int R;
+    unsigned int G;
+    unsigned int B;
+    unsigned int A;
+    ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
+                                   &G, &B, &A));
+    EXPECT_EQ(255u, R);
+    EXPECT_EQ(255u, G);
+    EXPECT_EQ(0u, B);
+    EXPECT_EQ(255u, A);
+
+    // Check that the author is correct.
+    static const char kAuthorKey[] = "T";
+    EXPECT_EQ(FPDF_OBJECT_STRING,
+              FPDFAnnot_GetValueType(annot.get(), kAuthorKey));
+    unsigned long length_bytes =
+        FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, nullptr, 0);
+    ASSERT_EQ(28u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(28u, FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, buf.data(),
+                                            length_bytes));
+    EXPECT_EQ(L"Jae Hyun Park", GetPlatformWString(buf.data()));
+
+    // Check that the content is correct.
+    EXPECT_EQ(
+        FPDF_OBJECT_STRING,
+        FPDFAnnot_GetValueType(annot.get(), pdfium::annotation::kContents));
+    length_bytes = FPDFAnnot_GetStringValue(
+        annot.get(), pdfium::annotation::kContents, nullptr, 0);
+    ASSERT_EQ(2690u, length_bytes);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(2690u, FPDFAnnot_GetStringValue(annot.get(),
+                                              pdfium::annotation::kContents,
+                                              buf.data(), length_bytes));
+    static const wchar_t kContents[] =
+        L"This is a note for that highlight annotation. Very long highlight "
+        "annotation. Long long long Long long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long longLong long longLong long longLong long longLong long "
+        "longLong long long. END";
+    EXPECT_EQ(kContents, GetPlatformWString(buf.data()));
+
+    // Check that the quadpoints are correct.
+    FS_QUADPOINTSF quadpoints;
+    ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
+    EXPECT_EQ(115.802643f, quadpoints.x1);
+    EXPECT_EQ(718.913940f, quadpoints.y1);
+    EXPECT_EQ(157.211182f, quadpoints.x4);
+    EXPECT_EQ(706.264465f, quadpoints.y4);
+  }
+  UnloadPageNoEvents(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_ExtractInkMultiple DISABLED_ExtractInkMultiple
+#else
+#define MAYBE_ExtractInkMultiple ExtractInkMultiple
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_ExtractInkMultiple) {
+  // Open a file with three annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPageNoEvents(0);
+  ASSERT_TRUE(page);
+
+  // Check that there is a total of 3 annotation on its first page.
+  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
+
+  {
+    // Check that the third annotation is of type "ink".
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot.get()));
+
+    // Check that the annotation color is blue with opacity.
+    unsigned int R;
+    unsigned int G;
+    unsigned int B;
+    unsigned int A;
+    ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
+                                   &G, &B, &A));
+    EXPECT_EQ(0u, R);
+    EXPECT_EQ(0u, G);
+    EXPECT_EQ(255u, B);
+    EXPECT_EQ(76u, A);
+
+    // Check that there is no content.
+    EXPECT_EQ(2u, FPDFAnnot_GetStringValue(
+                      annot.get(), pdfium::annotation::kContents, nullptr, 0));
+
+    // Check that the rectangle coordinates are correct.
+    // Note that upon rendering, the rectangle coordinates will be adjusted.
+    FS_RECTF rect;
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_EQ(351.820404f, rect.left);
+    EXPECT_EQ(583.830688f, rect.bottom);
+    EXPECT_EQ(475.336090f, rect.right);
+    EXPECT_EQ(681.535034f, rect.top);
+  }
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, "354002e1c4386d38fdde29ef8d61074a");
+  }
+  UnloadPageNoEvents(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, AddIllegalSubtypeAnnotation) {
+  // Open a file with one annotation and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Add an annotation with an illegal subtype.
+  ASSERT_FALSE(FPDFPage_CreateAnnot(page, -1));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, AddFirstTextAnnotation) {
+  // Open a file with no annotation and load its first page.
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
+
+  {
+    // Add a text annotation to the page.
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_TEXT));
+    ASSERT_TRUE(annot);
+
+    // Check that there is now 1 annotations on this page.
+    EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+    // Check that the subtype of the annotation is correct.
+    EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
+
+    // Set the color of the annotation.
+    ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, 51,
+                                   102, 153, 204));
+    // Check that the color has been set correctly.
+    unsigned int R;
+    unsigned int G;
+    unsigned int B;
+    unsigned int A;
+    ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
+                                   &G, &B, &A));
+    EXPECT_EQ(51u, R);
+    EXPECT_EQ(102u, G);
+    EXPECT_EQ(153u, B);
+    EXPECT_EQ(204u, A);
+
+    // Change the color of the annotation.
+    ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, 204,
+                                   153, 102, 51));
+    // Check that the color has been set correctly.
+    ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
+                                   &G, &B, &A));
+    EXPECT_EQ(204u, R);
+    EXPECT_EQ(153u, G);
+    EXPECT_EQ(102u, B);
+    EXPECT_EQ(51u, A);
+
+    // Set the annotation rectangle.
+    FS_RECTF rect;
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_EQ(0.f, rect.left);
+    EXPECT_EQ(0.f, rect.right);
+    rect.left = 35;
+    rect.bottom = 150;
+    rect.right = 53;
+    rect.top = 165;
+    ASSERT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
+    // Check that the annotation rectangle has been set correctly.
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_EQ(35.f, rect.left);
+    EXPECT_EQ(150.f, rect.bottom);
+    EXPECT_EQ(53.f, rect.right);
+    EXPECT_EQ(165.f, rect.top);
+
+    // Set the content of the annotation.
+    static const wchar_t kContents[] = L"Hello! This is a customized content.";
+    ScopedFPDFWideString text = GetFPDFWideString(kContents);
+    ASSERT_TRUE(FPDFAnnot_SetStringValue(
+        annot.get(), pdfium::annotation::kContents, text.get()));
+    // Check that the content has been set correctly.
+    unsigned long length_bytes = FPDFAnnot_GetStringValue(
+        annot.get(), pdfium::annotation::kContents, nullptr, 0);
+    ASSERT_EQ(74u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(74u, FPDFAnnot_GetStringValue(annot.get(),
+                                            pdfium::annotation::kContents,
+                                            buf.data(), length_bytes));
+    EXPECT_EQ(kContents, GetPlatformWString(buf.data()));
+  }
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddAndSaveUnderlineAnnotation \
+  DISABLED_AddAndSaveUnderlineAnnotation
+#else
+#define MAYBE_AddAndSaveUnderlineAnnotation AddAndSaveUnderlineAnnotation
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndSaveUnderlineAnnotation) {
+  // Open a file with one annotation and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Check that there is a total of one annotation on its first page, and verify
+  // its quadpoints.
+  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+  FS_QUADPOINTSF quadpoints;
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
+    EXPECT_EQ(115.802643f, quadpoints.x1);
+    EXPECT_EQ(718.913940f, quadpoints.y1);
+    EXPECT_EQ(157.211182f, quadpoints.x4);
+    EXPECT_EQ(706.264465f, quadpoints.y4);
+  }
+
+  // Add an underline annotation to the page and set its quadpoints.
+  {
+    ScopedFPDFAnnotation annot(
+        FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE));
+    ASSERT_TRUE(annot);
+    quadpoints.x1 = 140.802643f;
+    quadpoints.x3 = 140.802643f;
+    ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot.get(), &quadpoints));
+  }
+
+  // Save the document, closing the page and document.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Open the saved document.
+  static const char kMd5[] = "dba153419f67b7c0c0e3d22d3e8910d5";
+
+  ASSERT_TRUE(OpenSavedDocument());
+  page = LoadSavedPage(0);
+  VerifySavedRendering(page, 612, 792, kMd5);
+
+  // Check that the saved document has 2 annotations on the first page
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  {
+    // Check that the second annotation is an underline annotation and verify
+    // its quadpoints.
+    ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(new_annot);
+    EXPECT_EQ(FPDF_ANNOT_UNDERLINE, FPDFAnnot_GetSubtype(new_annot.get()));
+    FS_QUADPOINTSF new_quadpoints;
+    ASSERT_TRUE(
+        FPDFAnnot_GetAttachmentPoints(new_annot.get(), 0, &new_quadpoints));
+    EXPECT_NEAR(quadpoints.x1, new_quadpoints.x1, 0.001f);
+    EXPECT_NEAR(quadpoints.y1, new_quadpoints.y1, 0.001f);
+    EXPECT_NEAR(quadpoints.x4, new_quadpoints.x4, 0.001f);
+    EXPECT_NEAR(quadpoints.y4, new_quadpoints.y4, 0.001f);
+  }
+
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetAndSetQuadPoints) {
+  // Open a file with four annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(4, FPDFPage_GetAnnotCount(page));
+
+  // Retrieve the highlight annotation.
+  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
+  ASSERT_TRUE(annot);
+  ASSERT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
+
+  FS_QUADPOINTSF quadpoints;
+  ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 0, &quadpoints));
+
+  {
+    // Verify the current one set of quadpoints.
+    ASSERT_EQ(1u, FPDFAnnot_CountAttachmentPoints(annot));
+
+    EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
+    EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
+    EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
+    EXPECT_NEAR(704.796f, quadpoints.y4, 0.001f);
+  }
+
+  {
+    // Update the quadpoints.
+    FS_QUADPOINTSF new_quadpoints = quadpoints;
+    new_quadpoints.y1 -= 20.f;
+    new_quadpoints.y2 -= 20.f;
+    new_quadpoints.y3 -= 20.f;
+    new_quadpoints.y4 -= 20.f;
+    ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, 0, &new_quadpoints));
+
+    // Verify added quadpoint set
+    ASSERT_EQ(1u, FPDFAnnot_CountAttachmentPoints(annot));
+    ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 0, &quadpoints));
+    EXPECT_NEAR(new_quadpoints.x1, quadpoints.x1, 0.001f);
+    EXPECT_NEAR(new_quadpoints.y1, quadpoints.y1, 0.001f);
+    EXPECT_NEAR(new_quadpoints.x4, quadpoints.x4, 0.001f);
+    EXPECT_NEAR(new_quadpoints.y4, quadpoints.y4, 0.001f);
+  }
+
+  {
+    // Append a new set of quadpoints.
+    FS_QUADPOINTSF new_quadpoints = quadpoints;
+    new_quadpoints.y1 += 20.f;
+    new_quadpoints.y2 += 20.f;
+    new_quadpoints.y3 += 20.f;
+    new_quadpoints.y4 += 20.f;
+    ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot, &new_quadpoints));
+
+    // Verify added quadpoint set
+    ASSERT_EQ(2u, FPDFAnnot_CountAttachmentPoints(annot));
+    ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 1, &quadpoints));
+    EXPECT_NEAR(new_quadpoints.x1, quadpoints.x1, 0.001f);
+    EXPECT_NEAR(new_quadpoints.y1, quadpoints.y1, 0.001f);
+    EXPECT_NEAR(new_quadpoints.x4, quadpoints.x4, 0.001f);
+    EXPECT_NEAR(new_quadpoints.y4, quadpoints.y4, 0.001f);
+  }
+
+  {
+    // Setting and getting quadpoints at out-of-bound index should fail
+    EXPECT_FALSE(FPDFAnnot_SetAttachmentPoints(annot, 300000, &quadpoints));
+    EXPECT_FALSE(FPDFAnnot_GetAttachmentPoints(annot, 300000, &quadpoints));
+  }
+
+  FPDFPage_CloseAnnot(annot);
+
+  // Retrieve the square annotation
+  FPDF_ANNOTATION squareAnnot = FPDFPage_GetAnnot(page, 2);
+
+  {
+    // Check that attempting to set its quadpoints would fail
+    ASSERT_TRUE(squareAnnot);
+    EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(squareAnnot));
+    EXPECT_EQ(0u, FPDFAnnot_CountAttachmentPoints(squareAnnot));
+    EXPECT_FALSE(FPDFAnnot_SetAttachmentPoints(squareAnnot, 0, &quadpoints));
+  }
+
+  FPDFPage_CloseAnnot(squareAnnot);
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_ModifyRectQuadpointsWithAP DISABLED_ModifyRectQuadpointsWithAP
+#else
+#define MAYBE_ModifyRectQuadpointsWithAP ModifyRectQuadpointsWithAP
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_ModifyRectQuadpointsWithAP) {
+#if defined(OS_MACOSX)
+  static const char kMd5Original[] = "63af8432fab95a67cdebb7cd0e514941";
+  static const char kMd5ModifiedHighlight[] =
+      "aec26075011349dec9bace891856b5f2";
+  static const char kMd5ModifiedSquare[] = "057f57a32be95975775e5ec513fdcb56";
+#elif defined(OS_WIN)
+  static const char kMd5Original[] = "0e27376094f11490f74c65f3dc3a42c5";
+  static const char kMd5ModifiedHighlight[] =
+      "66f3caef3a7d488a4fa1ad37fc06310e";
+  static const char kMd5ModifiedSquare[] = "a456dad0bc6801ee2d6408a4394af563";
+#else
+  static const char kMd5Original[] = "0e27376094f11490f74c65f3dc3a42c5";
+  static const char kMd5ModifiedHighlight[] =
+      "66f3caef3a7d488a4fa1ad37fc06310e";
+  static const char kMd5ModifiedSquare[] = "a456dad0bc6801ee2d6408a4394af563";
+#endif
+
+  // Open a file with four annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(4, FPDFPage_GetAnnotCount(page));
+
+  // Check that the original file renders correctly.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, kMd5Original);
+  }
+
+  FS_RECTF rect;
+  FS_RECTF new_rect;
+
+  // Retrieve the highlight annotation which has its AP stream already defined.
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
+
+    // Check that color cannot be set when an AP stream is defined already.
+    EXPECT_FALSE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, 51,
+                                    102, 153, 204));
+
+    // Verify its attachment points.
+    FS_QUADPOINTSF quadpoints;
+    ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
+    EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
+    EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
+    EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
+    EXPECT_NEAR(704.796f, quadpoints.y4, 0.001f);
+
+    // Check that updating the attachment points would succeed.
+    quadpoints.x1 -= 50.f;
+    quadpoints.x2 -= 50.f;
+    quadpoints.x3 -= 50.f;
+    quadpoints.x4 -= 50.f;
+    ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot.get(), 0, &quadpoints));
+    FS_QUADPOINTSF new_quadpoints;
+    ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &new_quadpoints));
+    EXPECT_EQ(quadpoints.x1, new_quadpoints.x1);
+    EXPECT_EQ(quadpoints.y1, new_quadpoints.y1);
+    EXPECT_EQ(quadpoints.x4, new_quadpoints.x4);
+    EXPECT_EQ(quadpoints.y4, new_quadpoints.y4);
+
+    // Check that updating quadpoints does not change the annotation's position.
+    {
+      ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+      CompareBitmap(bitmap.get(), 612, 792, kMd5Original);
+    }
+
+    // Verify its annotation rectangle.
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_NEAR(67.7299f, rect.left, 0.001f);
+    EXPECT_NEAR(704.296f, rect.bottom, 0.001f);
+    EXPECT_NEAR(136.325f, rect.right, 0.001f);
+    EXPECT_NEAR(721.292f, rect.top, 0.001f);
+
+    // Check that updating the rectangle would succeed.
+    rect.left -= 60.f;
+    rect.right -= 60.f;
+    ASSERT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
+    EXPECT_EQ(rect.right, new_rect.right);
+  }
+
+  // Check that updating the rectangle changes the annotation's position.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, kMd5ModifiedHighlight);
+  }
+
+  {
+    // Retrieve the square annotation which has its AP stream already defined.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot.get()));
+
+    // Check that updating the rectangle would succeed.
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    rect.left += 70.f;
+    rect.right += 70.f;
+    ASSERT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
+    EXPECT_EQ(rect.right, new_rect.right);
+
+    // Check that updating the rectangle changes the square annotation's
+    // position.
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, kMd5ModifiedSquare);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, CountAttachmentPoints) {
+  // Open a file with multiline markup annotations.
+  ASSERT_TRUE(OpenDocument("annotation_markup_multiline_no_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // This is a three line annotation.
+    EXPECT_EQ(3u, FPDFAnnot_CountAttachmentPoints(annot.get()));
+  }
+  UnloadPage(page);
+
+  // null annotation should return 0
+  EXPECT_EQ(0u, FPDFAnnot_CountAttachmentPoints(nullptr));
+}
+
+TEST_F(FPDFAnnotEmbedderTest, RemoveAnnotation) {
+  // Open a file with 3 annotations on its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPageNoEvents(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
+
+  FS_RECTF rect;
+
+  // Check that the annotations have the expected rectangle coordinates.
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_NEAR(86.1971f, rect.left, 0.001f);
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_NEAR(149.8127f, rect.left, 0.001f);
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_NEAR(351.8204f, rect.left, 0.001f);
+  }
+
+  // Check that nothing happens when attempting to remove an annotation with an
+  // out-of-bound index.
+  EXPECT_FALSE(FPDFPage_RemoveAnnot(page, 4));
+  EXPECT_FALSE(FPDFPage_RemoveAnnot(page, -1));
+  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
+
+  // Remove the second annotation.
+  EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1));
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+  EXPECT_FALSE(FPDFPage_GetAnnot(page, 2));
+
+  // Save the document, closing the page and document.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPageNoEvents(page);
+
+  // TODO(npm): VerifySavedRendering changes annot rect dimensions by 1??
+  // Open the saved document.
+  std::string new_file = GetString();
+  FPDF_FILEACCESS file_access;
+  memset(&file_access, 0, sizeof(file_access));
+  file_access.m_FileLen = new_file.size();
+  file_access.m_GetBlock = GetBlockFromString;
+  file_access.m_Param = &new_file;
+  FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
+  ASSERT_TRUE(new_doc);
+  FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
+  ASSERT_TRUE(new_page);
+
+  // Check that the saved document has 2 annotations on the first page.
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(new_page));
+
+  // Check that the remaining 2 annotations are the original 1st and 3rd ones
+  // by verifying their rectangle coordinates.
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(new_page, 0));
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_NEAR(86.1971f, rect.left, 0.001f);
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(new_page, 1));
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
+    EXPECT_NEAR(351.8204f, rect.left, 0.001f);
+  }
+  FPDF_ClosePage(new_page);
+  FPDF_CloseDocument(new_doc);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddAndModifyPath DISABLED_AddAndModifyPath
+#else
+#define MAYBE_AddAndModifyPath AddAndModifyPath
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) {
+#if defined(OS_MACOSX)
+  static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2";
+  static const char kMd5ModifiedPath[] = "9059723a045e17478753d2f0eb33bc03";
+  static const char kMd5TwoPaths[] = "7eed0cfba780f1d4dd8068f717d3a6bf";
+  static const char kMd5NewAnnot[] = "1de8212d43b7066a6df042095c2aca61";
+#elif defined(OS_WIN)
+  static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4";
+  static const char kMd5ModifiedPath[] = "a7a8d675a6ddbcbdfecee65a33ba19e1";
+  static const char kMd5TwoPaths[] = "7c0bdd4552329704c47a7cce47edbbd6";
+  static const char kMd5NewAnnot[] = "3c48d492b4f62941fed0fb62f729f31e";
+#else
+  static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5";
+  static const char kMd5ModifiedPath[] = "6ff77d6d1fec4ea571fabe0c7a19b517";
+  static const char kMd5TwoPaths[] = "ca37ad549e74ac5b359a055708f3e7b6";
+  static const char kMd5NewAnnot[] = "0d7a0e33fbf41ff7fa5d732ab2c5edff";
+#endif
+
+  // Open a file with two annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  // Check that the page renders correctly.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+  }
+
+  {
+    // Retrieve the stamp annotation which has its AP stream already defined.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check that this annotation has one path object and retrieve it.
+    EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
+    ASSERT_EQ(32, FPDFPage_CountObjects(page));
+    FPDF_PAGEOBJECT path = FPDFAnnot_GetObject(annot.get(), 1);
+    EXPECT_FALSE(path);
+    path = FPDFAnnot_GetObject(annot.get(), 0);
+    EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
+    EXPECT_TRUE(path);
+
+    // Modify the color of the path object.
+    EXPECT_TRUE(FPDFPageObj_SetStrokeColor(path, 0, 0, 0, 255));
+    EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), path));
+
+    // Check that the page with the modified annotation renders correctly.
+    {
+      ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+      CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedPath);
+    }
+
+    // Add a second path object to the same annotation.
+    FPDF_PAGEOBJECT dot = FPDFPageObj_CreateNewPath(7, 84);
+    EXPECT_TRUE(FPDFPath_BezierTo(dot, 9, 86, 10, 87, 11, 88));
+    EXPECT_TRUE(FPDFPageObj_SetStrokeColor(dot, 255, 0, 0, 100));
+    EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(dot, 14));
+    EXPECT_TRUE(FPDFPath_SetDrawMode(dot, 0, 1));
+    EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), dot));
+    EXPECT_EQ(2, FPDFAnnot_GetObjectCount(annot.get()));
+
+    // The object is in the annontation, not in the page, so the page object
+    // array should not change.
+    ASSERT_EQ(32, FPDFPage_CountObjects(page));
+
+    // Check that the page with an annotation with two paths renders correctly.
+    {
+      ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+      CompareBitmap(bitmap.get(), 595, 842, kMd5TwoPaths);
+    }
+
+    // Delete the newly added path object.
+    EXPECT_TRUE(FPDFAnnot_RemoveObject(annot.get(), 1));
+    EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
+    ASSERT_EQ(32, FPDFPage_CountObjects(page));
+  }
+
+  // Check that the page renders the same as before.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedPath);
+  }
+
+  FS_RECTF rect;
+
+  {
+    // Create another stamp annotation and set its annotation rectangle.
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
+    ASSERT_TRUE(annot);
+    rect.left = 200.f;
+    rect.bottom = 400.f;
+    rect.right = 500.f;
+    rect.top = 600.f;
+    EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
+
+    // Add a new path to the annotation.
+    FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(200, 500);
+    EXPECT_TRUE(FPDFPath_LineTo(check, 300, 400));
+    EXPECT_TRUE(FPDFPath_LineTo(check, 500, 600));
+    EXPECT_TRUE(FPDFPath_MoveTo(check, 350, 550));
+    EXPECT_TRUE(FPDFPath_LineTo(check, 450, 450));
+    EXPECT_TRUE(FPDFPageObj_SetStrokeColor(check, 0, 255, 255, 180));
+    EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(check, 8.35f));
+    EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
+    EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), check));
+    EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
+
+    // Check that the annotation's bounding box came from its rectangle.
+    FS_RECTF new_rect;
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
+    EXPECT_EQ(rect.left, new_rect.left);
+    EXPECT_EQ(rect.bottom, new_rect.bottom);
+    EXPECT_EQ(rect.right, new_rect.right);
+    EXPECT_EQ(rect.top, new_rect.top);
+  }
+
+  // Save the document, closing the page and document.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Open the saved document.
+  ASSERT_TRUE(OpenSavedDocument());
+  page = LoadSavedPage(0);
+  VerifySavedRendering(page, 595, 842, kMd5NewAnnot);
+
+  // Check that the document has a correct count of annotations and objects.
+  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
+
+    // Check that the new annotation's rectangle is as defined.
+    FS_RECTF new_rect;
+    ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
+    EXPECT_EQ(rect.left, new_rect.left);
+    EXPECT_EQ(rect.bottom, new_rect.bottom);
+    EXPECT_EQ(rect.right, new_rect.right);
+    EXPECT_EQ(rect.top, new_rect.top);
+  }
+
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFAnnotEmbedderTest, ModifyAnnotationFlags) {
+  // Open a file with an annotation and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Check that the page renders correctly.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
+  }
+
+  {
+    // Retrieve the annotation.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check that the original flag values are as expected.
+    int flags = FPDFAnnot_GetFlags(annot.get());
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
+    EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
+
+    // Set the HIDDEN flag.
+    flags |= FPDF_ANNOT_FLAG_HIDDEN;
+    EXPECT_TRUE(FPDFAnnot_SetFlags(annot.get(), flags));
+    flags = FPDFAnnot_GetFlags(annot.get());
+    EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_HIDDEN);
+    EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
+
+    // Check that the page renders correctly without rendering the annotation.
+    {
+      ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+      CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
+    }
+
+    // Unset the HIDDEN flag.
+    EXPECT_TRUE(FPDFAnnot_SetFlags(annot.get(), FPDF_ANNOT_FLAG_NONE));
+    EXPECT_FALSE(FPDFAnnot_GetFlags(annot.get()));
+    flags &= ~FPDF_ANNOT_FLAG_HIDDEN;
+    EXPECT_TRUE(FPDFAnnot_SetFlags(annot.get(), flags));
+    flags = FPDFAnnot_GetFlags(annot.get());
+    EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
+    EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
+
+    // Check that the page renders correctly as before.
+    {
+      ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+      CompareBitmap(bitmap.get(), 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
+    }
+  }
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddAndModifyImage DISABLED_AddAndModifyImage
+#else
+#define MAYBE_AddAndModifyImage AddAndModifyImage
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyImage) {
+#if defined(OS_MACOSX)
+  static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2";
+  static const char kMd5NewImage[] = "ff012f5697436dfcaec25b32d1333596";
+  static const char kMd5ModifiedImage[] = "86cf8cb2755a7a2046a543e66d9c1e61";
+#elif defined(OS_WIN)
+  static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4";
+  static const char kMd5NewImage[] = "3d77d06a971bcb9fb54db082f1082c8b";
+  static const char kMd5ModifiedImage[] = "dc4f4afc26c345418330d31c065020e1";
+#else
+  static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5";
+  static const char kMd5NewImage[] = "528e6243dc29d54f36b61e0d3287d935";
+  static const char kMd5ModifiedImage[] = "6d9e59f3e57a1ff82fb258356b7eb731";
+#endif
+
+  // Open a file with two annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  // Check that the page renders correctly.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+  }
+
+  constexpr int kBitmapSize = 200;
+  FPDF_BITMAP image_bitmap;
+
+  {
+    // Create a stamp annotation and set its annotation rectangle.
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
+    ASSERT_TRUE(annot);
+    FS_RECTF rect;
+    rect.left = 200.f;
+    rect.bottom = 600.f;
+    rect.right = 400.f;
+    rect.top = 800.f;
+    EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
+
+    // Add a solid-color translucent image object to the new annotation.
+    image_bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 1);
+    FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize,
+                        0xeeeecccc);
+    EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(image_bitmap));
+    EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(image_bitmap));
+    FPDF_PAGEOBJECT image_object = FPDFPageObj_NewImageObj(document());
+    ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
+    ASSERT_TRUE(FPDFImageObj_SetMatrix(image_object, kBitmapSize, 0, 0,
+                                       kBitmapSize, 0, 0));
+    FPDFPageObj_Transform(image_object, 1, 0, 0, 1, 200, 600);
+    EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), image_object));
+  }
+
+  // Check that the page renders correctly with the new image object.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5NewImage);
+  }
+
+  {
+    // Retrieve the newly added stamp annotation and its image object.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
+    FPDF_PAGEOBJECT image_object = FPDFAnnot_GetObject(annot.get(), 0);
+    EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(image_object));
+
+    // Modify the image in the new annotation.
+    FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize,
+                        0xff000000);
+    ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
+    EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), image_object));
+  }
+
+  // Save the document, closing the page and document.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+  FPDFBitmap_Destroy(image_bitmap);
+
+  // Test that the saved document renders the modified image object correctly.
+  VerifySavedDocument(595, 842, kMd5ModifiedImage);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddAndModifyText DISABLED_AddAndModifyText
+#else
+#define MAYBE_AddAndModifyText AddAndModifyText
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyText) {
+#if defined(OS_MACOSX)
+  static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2";
+  static const char kMd5NewText[] = "60031c1b0330cf1e1575f7d46687d429";
+  static const char kMd5ModifiedText[] = "79f5cfb0b07caaf936f65f6a7a57ce77";
+#elif defined(OS_WIN)
+  static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4";
+  static const char kMd5NewText[] = "204cc01749a70b8afc246a4ca33c7eb6";
+  static const char kMd5ModifiedText[] = "641261a45e8dfd68c89b80bfd237660d";
+#else
+  static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5";
+  static const char kMd5NewText[] = "00197ad6206f763febad5719e5935306";
+  static const char kMd5ModifiedText[] = "85853bc0aaa5a4e3af04e58b9cbfff23";
+#endif
+
+  // Open a file with two annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  // Check that the page renders correctly.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+  }
+
+  {
+    // Create a stamp annotation and set its annotation rectangle.
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
+    ASSERT_TRUE(annot);
+    FS_RECTF rect;
+    rect.left = 200.f;
+    rect.bottom = 550.f;
+    rect.right = 450.f;
+    rect.top = 650.f;
+    EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
+
+    // Add a translucent text object to the new annotation.
+    FPDF_PAGEOBJECT text_object =
+        FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
+    EXPECT_TRUE(text_object);
+    ScopedFPDFWideString text =
+        GetFPDFWideString(L"I'm a translucent text laying on other text.");
+    EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
+    EXPECT_TRUE(FPDFPageObj_SetFillColor(text_object, 0, 0, 255, 150));
+    FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 600);
+    EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), text_object));
+  }
+
+  // Check that the page renders correctly with the new text object.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5NewText);
+  }
+
+  {
+    // Retrieve the newly added stamp annotation and its text object.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
+    FPDF_PAGEOBJECT text_object = FPDFAnnot_GetObject(annot.get(), 0);
+    EXPECT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
+
+    // Modify the text in the new annotation.
+    ScopedFPDFWideString new_text = GetFPDFWideString(L"New text!");
+    EXPECT_TRUE(FPDFText_SetText(text_object, new_text.get()));
+    EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), text_object));
+  }
+
+  // Check that the page renders correctly with the modified text object.
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedText);
+  }
+
+  // Remove the new annotation, and check that the page renders as before.
+  EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 2));
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 595, 842, kMd5Original);
+  }
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_GetSetStringValue DISABLED_GetSetStringValue
+#else
+#define MAYBE_GetSetStringValue GetSetStringValue
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_GetSetStringValue) {
+  // Open a file with four annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  static const wchar_t kNewDate[] = L"D:201706282359Z00'00'";
+
+  {
+    // Retrieve the first annotation.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check that a non-existent key does not exist.
+    EXPECT_FALSE(FPDFAnnot_HasKey(annot.get(), "none"));
+
+    // Check that the string value of a non-string dictionary entry is empty.
+    EXPECT_TRUE(FPDFAnnot_HasKey(annot.get(), pdfium::annotation::kAP));
+    EXPECT_EQ(FPDF_OBJECT_REFERENCE,
+              FPDFAnnot_GetValueType(annot.get(), pdfium::annotation::kAP));
+    EXPECT_EQ(2u, FPDFAnnot_GetStringValue(annot.get(), pdfium::annotation::kAP,
+                                           nullptr, 0));
+
+    // Check that the string value of the hash is correct.
+    static const char kHashKey[] = "AAPL:Hash";
+    EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot.get(), kHashKey));
+    unsigned long length_bytes =
+        FPDFAnnot_GetStringValue(annot.get(), kHashKey, nullptr, 0);
+    ASSERT_EQ(66u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(66u, FPDFAnnot_GetStringValue(annot.get(), kHashKey, buf.data(),
+                                            length_bytes));
+    EXPECT_EQ(L"395fbcb98d558681742f30683a62a2ad",
+              GetPlatformWString(buf.data()));
+
+    // Check that the string value of the modified date is correct.
+    EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot.get(), kHashKey));
+    length_bytes = FPDFAnnot_GetStringValue(annot.get(), pdfium::annotation::kM,
+                                            nullptr, 0);
+    ASSERT_EQ(44u, length_bytes);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(44u, FPDFAnnot_GetStringValue(annot.get(), pdfium::annotation::kM,
+                                            buf.data(), length_bytes));
+    EXPECT_EQ(L"D:201706071721Z00'00'", GetPlatformWString(buf.data()));
+
+    // Update the date entry for the annotation.
+    ScopedFPDFWideString text = GetFPDFWideString(kNewDate);
+    EXPECT_TRUE(FPDFAnnot_SetStringValue(annot.get(), pdfium::annotation::kM,
+                                         text.get()));
+  }
+
+  // Save the document, closing the page and document.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+#if defined(OS_MACOSX)
+  static const char kMd5[] = "4d64e61c9c0f8c60ab3cc3234bb73b1c";
+#elif defined(OS_WIN)
+  static const char kMd5[] = "20b612ebd46babcb44c48c903e2c5a48";
+#else
+  static const char kMd5[] = "1d7bea2042c6fea0558ff2aef05811b5";
+#endif
+
+  // Open the saved annotation.
+  ASSERT_TRUE(OpenSavedDocument());
+  page = LoadSavedPage(0);
+  VerifySavedRendering(page, 595, 842, kMd5);
+  {
+    ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0));
+
+    // Check that the string value of the modified date is the newly-set value.
+    EXPECT_EQ(FPDF_OBJECT_STRING,
+              FPDFAnnot_GetValueType(new_annot.get(), pdfium::annotation::kM));
+    unsigned long length_bytes = FPDFAnnot_GetStringValue(
+        new_annot.get(), pdfium::annotation::kM, nullptr, 0);
+    ASSERT_EQ(44u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(44u,
+              FPDFAnnot_GetStringValue(new_annot.get(), pdfium::annotation::kM,
+                                       buf.data(), length_bytes));
+    EXPECT_EQ(kNewDate, GetPlatformWString(buf.data()));
+  }
+
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetNumberValue) {
+  // Open a file with four text annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  {
+    // First two annotations do not have "MaxLen" attribute.
+    for (int i = 0; i < 2; i++) {
+      ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
+      ASSERT_TRUE(annot);
+
+      // Verify that no "MaxLen" key present.
+      EXPECT_FALSE(FPDFAnnot_HasKey(annot.get(), "MaxLen"));
+
+      float value;
+      EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), "MaxLen", &value));
+    }
+
+    // Annotation in index 2 has "MaxLen" of 10.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+
+    // Verify that "MaxLen" key present.
+    EXPECT_TRUE(FPDFAnnot_HasKey(annot.get(), "MaxLen"));
+
+    float value;
+    EXPECT_TRUE(FPDFAnnot_GetNumberValue(annot.get(), "MaxLen", &value));
+    EXPECT_FLOAT_EQ(10.0f, value);
+
+    // Check bad inputs.
+    EXPECT_FALSE(FPDFAnnot_GetNumberValue(nullptr, "MaxLen", &value));
+    EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), nullptr, &value));
+    EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), "MaxLen", nullptr));
+    // Ask for key that exists but is not a number.
+    EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), "V", &value));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetSetAP) {
+  // Open a file with four annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    static const char kMd5NormalAP[] = "be903df0343fd774fadab9c8900cdf4a";
+    static constexpr size_t kExpectNormalAPLength = 73970;
+
+    // Retrieve the first annotation.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check that the string value of an AP returns the expected length.
+    unsigned long normal_length_bytes = FPDFAnnot_GetAP(
+        annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
+    ASSERT_EQ(kExpectNormalAPLength, normal_length_bytes);
+
+    // Check that the string value of an AP is not returned if the buffer is too
+    // small. The result buffer should be overwritten with an empty string.
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(normal_length_bytes);
+    // Write in the buffer to verify it's not overwritten.
+    memcpy(buf.data(), "abcdefgh", 8);
+    EXPECT_EQ(kExpectNormalAPLength,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              buf.data(), normal_length_bytes - 1));
+    EXPECT_EQ(0, memcmp(buf.data(), "abcdefgh", 8));
+
+    // Check that the string value of an AP is returned through a buffer that is
+    // the right size.
+    EXPECT_EQ(kExpectNormalAPLength,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              buf.data(), normal_length_bytes));
+    EXPECT_EQ(kMd5NormalAP,
+              GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()),
+                                normal_length_bytes));
+
+    // Check that the string value of an AP is returned through a buffer that is
+    // larger than necessary.
+    buf = GetFPDFWideStringBuffer(normal_length_bytes + 2);
+    EXPECT_EQ(kExpectNormalAPLength,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              buf.data(), normal_length_bytes + 2));
+    EXPECT_EQ(kMd5NormalAP,
+              GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()),
+                                normal_length_bytes));
+
+    // Check that getting an AP for a mode that does not have an AP returns an
+    // empty string.
+    unsigned long rollover_length_bytes = FPDFAnnot_GetAP(
+        annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
+    ASSERT_EQ(2u, rollover_length_bytes);
+
+    buf = GetFPDFWideStringBuffer(1000);
+    EXPECT_EQ(2u,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+                              buf.data(), 1000));
+    EXPECT_EQ(L"", GetPlatformWString(buf.data()));
+
+    // Check that setting the AP for an invalid appearance mode fails.
+    ScopedFPDFWideString ap_text = GetFPDFWideString(L"new test ap");
+    EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), -1, ap_text.get()));
+    EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_COUNT,
+                                 ap_text.get()));
+    EXPECT_FALSE(FPDFAnnot_SetAP(
+        annot.get(), FPDF_ANNOT_APPEARANCEMODE_COUNT + 1, ap_text.get()));
+
+    // Set the AP correctly now.
+    EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+                                ap_text.get()));
+
+    // Check that the new annotation value is equal to the value we just set.
+    rollover_length_bytes = FPDFAnnot_GetAP(
+        annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
+    ASSERT_EQ(24u, rollover_length_bytes);
+    buf = GetFPDFWideStringBuffer(rollover_length_bytes);
+    EXPECT_EQ(24u,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+                              buf.data(), rollover_length_bytes));
+    EXPECT_EQ(L"new test ap", GetPlatformWString(buf.data()));
+
+    // Check that the Normal AP was not touched when the Rollover AP was set.
+    buf = GetFPDFWideStringBuffer(normal_length_bytes);
+    EXPECT_EQ(kExpectNormalAPLength,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              buf.data(), normal_length_bytes));
+    EXPECT_EQ(kMd5NormalAP,
+              GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()),
+                                normal_length_bytes));
+  }
+
+  // Save the modified document, then reopen it.
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  ASSERT_TRUE(OpenSavedDocument());
+  page = LoadSavedPage(0);
+  {
+    ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0));
+
+    // Check that the new annotation value is equal to the value we set before
+    // saving.
+    unsigned long rollover_length_bytes = FPDFAnnot_GetAP(
+        new_annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
+    ASSERT_EQ(24u, rollover_length_bytes);
+    std::vector<FPDF_WCHAR> buf =
+        GetFPDFWideStringBuffer(rollover_length_bytes);
+    EXPECT_EQ(24u, FPDFAnnot_GetAP(new_annot.get(),
+                                   FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+                                   buf.data(), rollover_length_bytes));
+    EXPECT_EQ(L"new test ap", GetPlatformWString(buf.data()));
+  }
+
+  // Close saved document.
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFAnnotEmbedderTest, RemoveOptionalAP) {
+  // Open a file with four annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Retrieve the first annotation.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Set Down AP. Normal AP is already set.
+    ScopedFPDFWideString ap_text = GetFPDFWideString(L"new test ap");
+    EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
+                                ap_text.get()));
+    EXPECT_EQ(73970u,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              nullptr, 0));
+    EXPECT_EQ(24u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
+                                   nullptr, 0));
+
+    // Check that setting the Down AP to null removes the Down entry but keeps
+    // Normal intact.
+    EXPECT_TRUE(
+        FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr));
+    EXPECT_EQ(73970u,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              nullptr, 0));
+    EXPECT_EQ(2u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
+                                  nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, RemoveRequiredAP) {
+  // Open a file with four annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Retrieve the first annotation.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Set Down AP. Normal AP is already set.
+    ScopedFPDFWideString ap_text = GetFPDFWideString(L"new test ap");
+    EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
+                                ap_text.get()));
+    EXPECT_EQ(73970u,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              nullptr, 0));
+    EXPECT_EQ(24u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
+                                   nullptr, 0));
+
+    // Check that setting the Normal AP to null removes the whole AP dictionary.
+    EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                                nullptr));
+    EXPECT_EQ(2u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                                  nullptr, 0));
+    EXPECT_EQ(2u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
+                                  nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, ExtractLinkedAnnotations) {
+  // Open a file with annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(-1, FPDFPage_GetAnnotIndex(page, nullptr));
+
+  {
+    // Retrieve the highlight annotation which has its popup defined.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
+    EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot.get()));
+    static const char kPopupKey[] = "Popup";
+    ASSERT_TRUE(FPDFAnnot_HasKey(annot.get(), kPopupKey));
+    ASSERT_EQ(FPDF_OBJECT_REFERENCE,
+              FPDFAnnot_GetValueType(annot.get(), kPopupKey));
+
+    // Retrieve and verify the popup of the highlight annotation.
+    ScopedFPDFAnnotation popup(
+        FPDFAnnot_GetLinkedAnnot(annot.get(), kPopupKey));
+    ASSERT_TRUE(popup);
+    EXPECT_EQ(FPDF_ANNOT_POPUP, FPDFAnnot_GetSubtype(popup.get()));
+    EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, popup.get()));
+    FS_RECTF rect;
+    ASSERT_TRUE(FPDFAnnot_GetRect(popup.get(), &rect));
+    EXPECT_NEAR(612.0f, rect.left, 0.001f);
+    EXPECT_NEAR(578.792, rect.bottom, 0.001f);
+
+    // Attempting to retrieve |annot|'s "IRT"-linked annotation would fail,
+    // since "IRT" is not a key in |annot|'s dictionary.
+    static const char kIRTKey[] = "IRT";
+    ASSERT_FALSE(FPDFAnnot_HasKey(annot.get(), kIRTKey));
+    EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot.get(), kIRTKey));
+
+    // Attempting to retrieve |annot|'s parent dictionary as an annotation
+    // would fail, since its parent is not an annotation.
+    ASSERT_TRUE(FPDFAnnot_HasKey(annot.get(), pdfium::annotation::kP));
+    EXPECT_EQ(FPDF_OBJECT_REFERENCE,
+              FPDFAnnot_GetValueType(annot.get(), pdfium::annotation::kP));
+    EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot.get(), pdfium::annotation::kP));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsTextField) {
+  // Open file with form text fields.
+  ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Retrieve the first annotation: user-editable text field.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check that the flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
+  }
+
+  {
+    // Retrieve the second annotation: read-only text field.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Check that the flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
+  }
+
+  {
+    // Retrieve the fourth annotation: user-editable password text field.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+
+    // Check that the flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsComboBox) {
+  // Open file with form text fields.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Retrieve the first annotation: user-editable combobox.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check that the flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+  }
+
+  {
+    // Retrieve the second annotation: regular combobox.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Check that the flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+  }
+
+  {
+    // Retrieve the third annotation: read-only combobox.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+
+    // Check that the flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotNull) {
+  // Open file with form text fields.
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Attempt to get an annotation where no annotation exists on page.
+  static const FS_POINTF kOriginPoint = {0.0f, 0.0f};
+  EXPECT_FALSE(
+      FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kOriginPoint));
+
+  static const FS_POINTF kValidPoint = {120.0f, 120.0f};
+  {
+    // Verify there is an annotation.
+    ScopedFPDFAnnotation annot(
+        FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kValidPoint));
+    EXPECT_TRUE(annot);
+  }
+
+  // Try other bad inputs at a valid location.
+  EXPECT_FALSE(FPDFAnnot_GetFormFieldAtPoint(nullptr, nullptr, &kValidPoint));
+  EXPECT_FALSE(FPDFAnnot_GetFormFieldAtPoint(nullptr, page, &kValidPoint));
+  EXPECT_FALSE(
+      FPDFAnnot_GetFormFieldAtPoint(form_handle(), nullptr, &kValidPoint));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsTextField) {
+  // Open file with form text fields.
+  EXPECT_TRUE(OpenDocument("text_form_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Retrieve user-editable text field annotation.
+    static const FS_POINTF kPoint = {105.0f, 118.0f};
+    ScopedFPDFAnnotation annot(
+        FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
+    ASSERT_TRUE(annot);
+
+    // Check that interactive form annotation flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+  }
+
+  {
+    // Retrieve read-only text field annotation.
+    static const FS_POINTF kPoint = {105.0f, 202.0f};
+    ScopedFPDFAnnotation annot(
+        FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
+    ASSERT_TRUE(annot);
+
+    // Check that interactive form annotation flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) {
+  // Open file with form comboboxes.
+  EXPECT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Retrieve user-editable combobox annotation.
+    static const FS_POINTF kPoint = {102.0f, 363.0f};
+    ScopedFPDFAnnotation annot(
+        FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
+    ASSERT_TRUE(annot);
+
+    // Check that interactive form annotation flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+  }
+
+  {
+    // Retrieve regular combobox annotation.
+    static const FS_POINTF kPoint = {102.0f, 413.0f};
+    ScopedFPDFAnnotation annot(
+        FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
+    ASSERT_TRUE(annot);
+
+    // Check that interactive form annotation flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+  }
+
+  {
+    // Retrieve read-only combobox annotation.
+    static const FS_POINTF kPoint = {102.0f, 513.0f};
+    ScopedFPDFAnnotation annot(
+        FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
+    ASSERT_TRUE(annot);
+
+    // Check that interactive form annotation flag values are as expected.
+    int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
+    EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
+    EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
+  }
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_BUG_1206 DISABLED_BUG_1206
+#else
+#define MAYBE_BUG_1206 BUG_1206
+#endif
+TEST_F(FPDFAnnotEmbedderTest, MAYBE_BUG_1206) {
+  static constexpr size_t kExpectedSize = 1609;
+  static const char kExpectedBitmap[] = "0d9fc05c6762fd788bd23fd87a4967bc";
+
+  ASSERT_TRUE(OpenDocument("bug_1206.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_EQ(kExpectedSize, GetString().size());
+  ClearString();
+
+  for (size_t i = 0; i < 10; ++i) {
+    ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+    CompareBitmap(bitmap.get(), 612, 792, kExpectedBitmap);
+
+    ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    // TODO(https://crbug.com/pdfium/1206): This is wrong. The size should be
+    // equal, not bigger.
+    EXPECT_LT(kExpectedSize, GetString().size());
+    ClearString();
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, BUG_1212) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
+
+  static const char kTestKey[] = "test";
+  static const wchar_t kData[] = L"\xf6\xe4";
+  static const size_t kBufSize = 12;
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(kBufSize);
+
+  {
+    // Add a text annotation to the page.
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_TEXT));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+    EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
+
+    // Make sure there is no test key, add set a value there, and read it back.
+    std::fill(buf.begin(), buf.end(), 'x');
+    ASSERT_EQ(2u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
+                                           kBufSize));
+    EXPECT_EQ(L"", GetPlatformWString(buf.data()));
+
+    ScopedFPDFWideString text = GetFPDFWideString(kData);
+    EXPECT_TRUE(FPDFAnnot_SetStringValue(annot.get(), kTestKey, text.get()));
+
+    std::fill(buf.begin(), buf.end(), 'x');
+    ASSERT_EQ(6u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
+                                           kBufSize));
+    EXPECT_EQ(kData, GetPlatformWString(buf.data()));
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
+    ASSERT_TRUE(annot);
+    const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
+    EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
+    EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+    EXPECT_EQ(FPDF_ANNOT_STAMP, FPDFAnnot_GetSubtype(annot.get()));
+    // Also do the same test for its appearance string.
+    std::fill(buf.begin(), buf.end(), 'x');
+    ASSERT_EQ(2u,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+                              buf.data(), kBufSize));
+    EXPECT_EQ(L"", GetPlatformWString(buf.data()));
+
+    ScopedFPDFWideString text = GetFPDFWideString(kData);
+    EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+                                text.get()));
+
+    std::fill(buf.begin(), buf.end(), 'x');
+    ASSERT_EQ(6u,
+              FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
+                              buf.data(), kBufSize));
+    EXPECT_EQ(kData, GetPlatformWString(buf.data()));
+  }
+
+  UnloadPage(page);
+
+  {
+    // Save a copy, open the copy, and check the annotation again.
+    // Note that it renders the rotation.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    EXPECT_EQ(2, FPDFPage_GetAnnotCount(saved_page));
+    {
+      ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(saved_page, 0));
+      ASSERT_TRUE(annot);
+      EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
+
+      std::fill(buf.begin(), buf.end(), 'x');
+      ASSERT_EQ(6u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
+                                             kBufSize));
+      EXPECT_EQ(kData, GetPlatformWString(buf.data()));
+    }
+
+    {
+      ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(saved_page, 0));
+      ASSERT_TRUE(annot);
+      // TODO(thestig): This return FPDF_ANNOT_UNKNOWN for some reason.
+      // EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
+
+      std::fill(buf.begin(), buf.end(), 'x');
+      ASSERT_EQ(6u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
+                                             kBufSize));
+      EXPECT_EQ(kData, GetPlatformWString(buf.data()));
+    }
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetOptionCountCombobox) {
+  // Open a file with combobox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(3, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(26, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
+
+    // Check bad form handle / annot.
+    EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(nullptr, nullptr));
+    EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(form_handle(), nullptr));
+    EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(nullptr, annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetOptionCountListbox) {
+  // Open a file with listbox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(3, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(26, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetOptionCountInvalidAnnotations) {
+  // Open a file with ink annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // annotations do not have "Opt" array and will return -1
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetOptionLabelCombobox) {
+  // Open a file with combobox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    int index = 0;
+    unsigned long length_bytes =
+        FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
+    ASSERT_EQ(8u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(8u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
+                                           buf.data(), length_bytes));
+    EXPECT_EQ(L"Foo", GetPlatformWString(buf.data()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    index = 0;
+    length_bytes =
+        FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
+    ASSERT_EQ(12u, length_bytes);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(12u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
+                                            buf.data(), length_bytes));
+    EXPECT_EQ(L"Apple", GetPlatformWString(buf.data()));
+
+    index = 25;
+    length_bytes =
+        FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(18u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
+                                            buf.data(), length_bytes));
+    EXPECT_EQ(L"Zucchini", GetPlatformWString(buf.data()));
+
+    // Indices out of range
+    EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), -1,
+                                           nullptr, 0));
+    EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 26,
+                                           nullptr, 0));
+
+    // Check bad form handle / annot.
+    EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(nullptr, nullptr, 0, nullptr, 0));
+    EXPECT_EQ(0u,
+              FPDFAnnot_GetOptionLabel(nullptr, annot.get(), 0, nullptr, 0));
+    EXPECT_EQ(0u,
+              FPDFAnnot_GetOptionLabel(form_handle(), nullptr, 0, nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetOptionLabelListbox) {
+  // Open a file with listbox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    int index = 0;
+    unsigned long length_bytes =
+        FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
+    ASSERT_EQ(8u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(8u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
+                                           buf.data(), length_bytes));
+    EXPECT_EQ(L"Foo", GetPlatformWString(buf.data()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    index = 0;
+    length_bytes =
+        FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
+    ASSERT_EQ(12u, length_bytes);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(12u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
+                                            buf.data(), length_bytes));
+    EXPECT_EQ(L"Apple", GetPlatformWString(buf.data()));
+
+    index = 25;
+    length_bytes =
+        FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
+    ASSERT_EQ(18u, length_bytes);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(18u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
+                                            buf.data(), length_bytes));
+    EXPECT_EQ(L"Zucchini", GetPlatformWString(buf.data()));
+
+    // indices out of range
+    EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), -1,
+                                           nullptr, 0));
+    EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 26,
+                                           nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetOptionLabelInvalidAnnotations) {
+  // Open a file with ink annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // annotations do not have "Opt" array and will return 0
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 0,
+                                           nullptr, 0));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 0,
+                                           nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeCombobox) {
+  // Open a file with combobox annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // All 3 widgets have Tf font size 12.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+    EXPECT_EQ(12.0, value);
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    float value_two;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_two));
+    EXPECT_EQ(12.0, value_two);
+
+    annot.reset(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+
+    float value_three;
+    ASSERT_TRUE(
+        FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_three));
+    EXPECT_EQ(12.0, value_three);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeTextField) {
+  // Open a file with textfield annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // All 4 widgets have Tf font size 12.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+    EXPECT_EQ(12.0, value);
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    float value_two;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_two));
+    EXPECT_EQ(12.0, value_two);
+
+    annot.reset(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+
+    float value_three;
+    ASSERT_TRUE(
+        FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_three));
+    EXPECT_EQ(12.0, value_three);
+
+    float value_four;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_four));
+    EXPECT_EQ(12.0, value_four);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeInvalidAnnotationTypes) {
+  // Open a file with ink annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Annotations that do not have variable text and will return -1.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeInvalidArguments) {
+  // Open a file with combobox annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check bad form handle / annot.
+    float value;
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(nullptr, annot.get(), &value));
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), nullptr, &value));
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(nullptr, nullptr, &value));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeNegative) {
+  // Open a file with textfield annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("text_form_negative_fontsize.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Obtain the first annotation, a text field with negative font size, -12.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+    EXPECT_EQ(-12.0, value);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsCheckedCheckbox) {
+  // Open a file with checkbox and radiobuttons widget annotations and load its
+  // first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+    ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsCheckedCheckboxReadOnly) {
+  // Open a file with checkbox and radiobutton widget annotations and load its
+  // first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    ASSERT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsCheckedRadioButton) {
+  // Open a file with checkbox and radiobutton widget annotations and load its
+  // first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 5));
+    ASSERT_TRUE(annot);
+    ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 6));
+    ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 7));
+    ASSERT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsCheckedRadioButtonReadOnly) {
+  // Open a file with checkbox and radiobutton widget annotations and load its
+  // first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+    ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 3));
+    ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+
+    annot.reset(FPDFPage_GetAnnot(page, 4));
+    ASSERT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsCheckedInvalidArguments) {
+  // Open a file with checkbox and radiobuttons widget annotations and load its
+  // first page.
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    ASSERT_FALSE(FPDFAnnot_IsChecked(nullptr, annot.get()));
+    ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), nullptr));
+    ASSERT_FALSE(FPDFAnnot_IsChecked(nullptr, nullptr));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsCheckedInvalidWidgetType) {
+  // Open a file with text widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldTypeTextField) {
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(form_handle(), nullptr));
+
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(nullptr, annot.get()));
+
+    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
+              FPDFAnnot_GetFormFieldType(form_handle(), annot.get()));
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldTypeComboBox) {
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(FPDF_FORMFIELD_COMBOBOX,
+              FPDFAnnot_GetFormFieldType(form_handle(), annot.get()));
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldValueTextField) {
+  ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    EXPECT_EQ(0u,
+              FPDFAnnot_GetFormFieldValue(form_handle(), nullptr, nullptr, 0));
+
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(0u,
+              FPDFAnnot_GetFormFieldValue(nullptr, annot.get(), nullptr, 0));
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
+    ASSERT_EQ(2u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(2u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
+                                              buf.data(), length_bytes));
+    EXPECT_EQ(L"", GetPlatformWString(buf.data()));
+  }
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
+    ASSERT_EQ(18u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(18u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
+                                               buf.data(), length_bytes));
+    EXPECT_EQ(L"Elephant", GetPlatformWString(buf.data()));
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldValueComboBox) {
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
+    ASSERT_EQ(2u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(2u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
+                                              buf.data(), length_bytes));
+    EXPECT_EQ(L"", GetPlatformWString(buf.data()));
+  }
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
+    ASSERT_EQ(14u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(14u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
+                                               buf.data(), length_bytes));
+    EXPECT_EQ(L"Banana", GetPlatformWString(buf.data()));
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldNameTextField) {
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    EXPECT_EQ(0u,
+              FPDFAnnot_GetFormFieldName(form_handle(), nullptr, nullptr, 0));
+
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    EXPECT_EQ(0u, FPDFAnnot_GetFormFieldName(nullptr, annot.get(), nullptr, 0));
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldName(form_handle(), annot.get(), nullptr, 0);
+    ASSERT_EQ(18u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(18u, FPDFAnnot_GetFormFieldName(form_handle(), annot.get(),
+                                              buf.data(), length_bytes));
+    EXPECT_EQ(L"Text Box", GetPlatformWString(buf.data()));
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFormFieldNameComboBox) {
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    unsigned long length_bytes =
+        FPDFAnnot_GetFormFieldName(form_handle(), annot.get(), nullptr, 0);
+    ASSERT_EQ(30u, length_bytes);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    EXPECT_EQ(30u, FPDFAnnot_GetFormFieldName(form_handle(), annot.get(),
+                                              buf.data(), length_bytes));
+    EXPECT_EQ(L"Combo_Editable", GetPlatformWString(buf.data()));
+  }
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_annot_unittest.cpp b/fpdfsdk/fpdf_annot_unittest.cpp
new file mode 100644
index 0000000..a5f619e
--- /dev/null
+++ b/fpdfsdk/fpdf_annot_unittest.cpp
@@ -0,0 +1,137 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_annot.h"
+
+#include <vector>
+
+#include "constants/annotation_common.h"
+#include "core/fpdfapi/page/cpdf_annotcontext.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_edit.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const wchar_t kStreamData[] =
+    L"/GS gs 0.0 0.0 0.0 RG 4 w 211.8 747.6 m 211.8 744.8 "
+    L"212.6 743.0 214.2 740.8 "
+    L"c 215.4 739.0 216.8 737.1 218.9 736.1 c 220.8 735.1 221.4 733.0 "
+    L"223.7 732.4 c 232.6 729.9 242.0 730.8 251.2 730.8 c 257.5 730.8 "
+    L"263.0 732.9 269.0 734.4 c S";
+
+}  // namespace
+
+class PDFAnnotTest : public testing::Test {
+ protected:
+  PDFAnnotTest() = default;
+  ~PDFAnnotTest() override = default;
+
+  void SetUp() override { CPDF_PageModule::Create(); }
+  void TearDown() override { CPDF_PageModule::Destroy(); }
+};
+
+TEST_F(PDFAnnotTest, SetAP) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(doc);
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ASSERT_TRUE(page);
+  ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
+  ASSERT_TRUE(ap_stream);
+
+  ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
+  ASSERT_TRUE(annot);
+
+  // Negative case: FPDFAnnot_SetAP() should fail if bounding rect is not yet
+  // set on the annotation.
+  EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                               ap_stream.get()));
+
+  const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
+  EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
+
+  ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
+                                 /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/255));
+
+  EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              ap_stream.get()));
+
+  // Verify that appearance stream is created as form XObject
+  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
+  ASSERT_TRUE(context);
+  CPDF_Dictionary* annot_dict = context->GetAnnotDict();
+  ASSERT_TRUE(annot_dict);
+  CPDF_Dictionary* ap_dict = annot_dict->GetDictFor(pdfium::annotation::kAP);
+  ASSERT_TRUE(ap_dict);
+  CPDF_Dictionary* stream_dict = ap_dict->GetDictFor("N");
+  ASSERT_TRUE(stream_dict);
+  // Check for non-existence of resources dictionary in case of opaque color
+  CPDF_Dictionary* resources_dict = stream_dict->GetDictFor("Resources");
+  ASSERT_FALSE(resources_dict);
+  ByteString type = stream_dict->GetStringFor(pdfium::annotation::kType);
+  EXPECT_EQ("XObject", type);
+  ByteString sub_type = stream_dict->GetStringFor(pdfium::annotation::kSubtype);
+  EXPECT_EQ("Form", sub_type);
+
+  // Check that the appearance stream is same as we just set.
+  const uint32_t kStreamDataSize =
+      FX_ArraySize(kStreamData) * sizeof(FPDF_WCHAR);
+  unsigned long normal_length_bytes = FPDFAnnot_GetAP(
+      annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
+  ASSERT_EQ(kStreamDataSize, normal_length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(normal_length_bytes);
+  EXPECT_EQ(kStreamDataSize,
+            FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                            buf.data(), normal_length_bytes));
+  EXPECT_EQ(kStreamData, GetPlatformWString(buf.data()));
+}
+
+TEST_F(PDFAnnotTest, SetAPWithOpacity) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ASSERT_TRUE(doc);
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ASSERT_TRUE(page);
+  ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
+  ASSERT_TRUE(ap_stream);
+
+  ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
+  ASSERT_TRUE(annot);
+
+  ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
+                                 /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/102));
+
+  const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
+  EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
+
+  EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
+                              ap_stream.get()));
+
+  CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
+  ASSERT_TRUE(context);
+  CPDF_Dictionary* annot_dict = context->GetAnnotDict();
+  ASSERT_TRUE(annot_dict);
+  CPDF_Dictionary* ap_dict = annot_dict->GetDictFor(pdfium::annotation::kAP);
+  ASSERT_TRUE(ap_dict);
+  CPDF_Dictionary* stream_dict = ap_dict->GetDictFor("N");
+  ASSERT_TRUE(stream_dict);
+  CPDF_Dictionary* resources_dict = stream_dict->GetDictFor("Resources");
+  ASSERT_TRUE(stream_dict);
+  CPDF_Dictionary* extGState_dict = resources_dict->GetDictFor("ExtGState");
+  ASSERT_TRUE(extGState_dict);
+  CPDF_Dictionary* gs_dict = extGState_dict->GetDictFor("GS");
+  ASSERT_TRUE(gs_dict);
+  ByteString type = gs_dict->GetStringFor(pdfium::annotation::kType);
+  EXPECT_EQ("ExtGState", type);
+  float opacity = gs_dict->GetNumberFor("CA");
+  // Opacity value of 102 is represented as 0.4f (=104/255) in /CA entry.
+  EXPECT_FLOAT_EQ(0.4f, opacity);
+  ByteString blend_mode = gs_dict->GetStringFor("BM");
+  EXPECT_EQ("Normal", blend_mode);
+  bool alpha_source_flag = gs_dict->GetBooleanFor("AIS", true);
+  EXPECT_FALSE(alpha_source_flag);
+}
diff --git a/fpdfsdk/fpdf_attachment.cpp b/fpdfsdk/fpdf_attachment.cpp
new file mode 100644
index 0000000..7f55691
--- /dev/null
+++ b/fpdfsdk/fpdf_attachment.cpp
@@ -0,0 +1,277 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_attachment.h"
+
+#include <memory>
+#include <utility>
+
+#include "constants/stream_dict_common.h"
+#include "core/fdrm/fx_crypt.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfdoc/cpdf_filespec.h"
+#include "core/fpdfdoc/cpdf_nametree.h"
+#include "core/fxcrt/cfx_datetime.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+constexpr char kChecksumKey[] = "CheckSum";
+
+ByteString CFXByteStringHexDecode(const ByteString& bsHex) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> result;
+  uint32_t size = 0;
+  HexDecode(bsHex.raw_span(), &result, &size);
+  return ByteString(result.get(), size);
+}
+
+ByteString GenerateMD5Base16(const void* contents, const unsigned long len) {
+  uint8_t digest[16];
+  CRYPT_MD5Generate({static_cast<const uint8_t*>(contents), len}, digest);
+  char buf[32];
+  for (int i = 0; i < 16; ++i)
+    FXSYS_IntToTwoHexChars(digest[i], &buf[i * 2]);
+
+  return ByteString(buf, 32);
+}
+
+}  // namespace
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  return CPDF_NameTree(pDoc, "EmbeddedFiles").GetCount();
+}
+
+FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
+FPDFDoc_AddAttachment(FPDF_DOCUMENT document, FPDF_WIDESTRING name) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  CPDF_Dictionary* pRoot = pDoc->GetRoot();
+  if (!pRoot)
+    return nullptr;
+
+  WideString wsName = WideStringFromFPDFWideString(name);
+  if (wsName.IsEmpty())
+    return nullptr;
+
+  // Retrieve the document's Names dictionary; create it if missing.
+  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
+  if (!pNames) {
+    pNames = pDoc->NewIndirect<CPDF_Dictionary>();
+    pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
+  }
+
+  // Create the EmbeddedFiles dictionary if missing.
+  if (!pNames->GetDictFor("EmbeddedFiles")) {
+    CPDF_Dictionary* pFiles = pDoc->NewIndirect<CPDF_Dictionary>();
+    pFiles->SetNewFor<CPDF_Array>("Names");
+    pNames->SetNewFor<CPDF_Reference>("EmbeddedFiles", pDoc,
+                                      pFiles->GetObjNum());
+  }
+
+  // Set up the basic entries in the filespec dictionary.
+  CPDF_Dictionary* pFile = pDoc->NewIndirect<CPDF_Dictionary>();
+  pFile->SetNewFor<CPDF_Name>("Type", "Filespec");
+  pFile->SetNewFor<CPDF_String>("UF", wsName);
+  pFile->SetNewFor<CPDF_String>(pdfium::stream::kF, wsName);
+
+  // Add the new attachment name and filespec into the document's EmbeddedFiles.
+  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
+  if (!nameTree.AddValueAndName(pFile->MakeReference(pDoc), wsName)) {
+    return nullptr;
+  }
+
+  return FPDFAttachmentFromCPDFObject(pFile);
+}
+
+FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
+FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc || index < 0)
+    return nullptr;
+
+  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
+  if (static_cast<size_t>(index) >= nameTree.GetCount())
+    return nullptr;
+
+  WideString csName;
+  return FPDFAttachmentFromCPDFObject(
+      nameTree.LookupValueAndName(index, &csName));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document, int index) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc || index < 0)
+    return false;
+
+  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
+  if (static_cast<size_t>(index) >= nameTree.GetCount())
+    return false;
+
+  return nameTree.DeleteValueAndName(index);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,
+                       FPDF_WCHAR* buffer,
+                       unsigned long buflen) {
+  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
+  if (!pFile)
+    return 0;
+
+  return Utf16EncodeMaybeCopyAndReturnLength(CPDF_FileSpec(pFile).GetFileName(),
+                                             buffer, buflen);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
+  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
+  if (!pFile)
+    return 0;
+
+  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
+  return pParamsDict ? pParamsDict->KeyExist(key) : 0;
+}
+
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
+  if (!FPDFAttachment_HasKey(attachment, key))
+    return FPDF_OBJECT_UNKNOWN;
+
+  CPDF_FileSpec spec(CPDFObjectFromFPDFAttachment(attachment));
+  CPDF_Object* pObj = spec.GetParamsDict()->GetObjectFor(key);
+  return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment,
+                              FPDF_BYTESTRING key,
+                              FPDF_WIDESTRING value) {
+  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
+  if (!pFile)
+    return false;
+
+  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
+  if (!pParamsDict)
+    return false;
+
+  ByteString bsKey = key;
+  ByteString bsValue = ByteStringFromFPDFWideString(value);
+  bool bEncodedAsHex = bsKey == kChecksumKey;
+  if (bEncodedAsHex)
+    bsValue = CFXByteStringHexDecode(bsValue);
+
+  pParamsDict->SetNewFor<CPDF_String>(bsKey, bsValue, bEncodedAsHex);
+  return true;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,
+                              FPDF_BYTESTRING key,
+                              FPDF_WCHAR* buffer,
+                              unsigned long buflen) {
+  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
+  if (!pFile)
+    return 0;
+
+  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
+  if (!pParamsDict)
+    return 0;
+
+  ByteString bsKey = key;
+  WideString value = pParamsDict->GetUnicodeTextFor(bsKey);
+  if (bsKey == kChecksumKey && !value.IsEmpty()) {
+    CPDF_String* stringValue = pParamsDict->GetObjectFor(bsKey)->AsString();
+    if (stringValue->IsHex()) {
+      ByteString encoded = PDF_EncodeString(stringValue->GetString(), true);
+      value = pdfium::MakeRetain<CPDF_String>(nullptr, encoded, false)
+                  ->GetUnicodeText();
+    }
+  }
+
+  return Utf16EncodeMaybeCopyAndReturnLength(value, buffer, buflen);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
+                       FPDF_DOCUMENT document,
+                       const void* contents,
+                       const unsigned long len) {
+  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pFile || !pFile->IsDictionary() || !pDoc || len > INT_MAX)
+    return false;
+
+  // An empty content must have a zero length.
+  if (!contents && len != 0)
+    return false;
+
+  // Create a dictionary for the new embedded file stream.
+  auto pFileStreamDict = pdfium::MakeRetain<CPDF_Dictionary>();
+  CPDF_Dictionary* pParamsDict =
+      pFileStreamDict->SetNewFor<CPDF_Dictionary>("Params");
+
+  // Set the size of the new file in the dictionary.
+  pFileStreamDict->SetNewFor<CPDF_Number>(pdfium::stream::kDL,
+                                          static_cast<int>(len));
+  pParamsDict->SetNewFor<CPDF_Number>("Size", static_cast<int>(len));
+
+  // Set the creation date of the new attachment in the dictionary.
+  CFX_DateTime dateTime = CFX_DateTime::Now();
+  pParamsDict->SetNewFor<CPDF_String>(
+      "CreationDate",
+      ByteString::Format("D:%d%02d%02d%02d%02d%02d", dateTime.GetYear(),
+                         dateTime.GetMonth(), dateTime.GetDay(),
+                         dateTime.GetHour(), dateTime.GetMinute(),
+                         dateTime.GetSecond()),
+      false);
+
+  // Set the checksum of the new attachment in the dictionary.
+  pParamsDict->SetNewFor<CPDF_String>(
+      kChecksumKey, CFXByteStringHexDecode(GenerateMD5Base16(contents, len)),
+      true);
+
+  // Create the file stream and have the filespec dictionary link to it.
+  std::unique_ptr<uint8_t, FxFreeDeleter> stream(FX_Alloc(uint8_t, len));
+  memcpy(stream.get(), contents, len);
+  CPDF_Stream* pFileStream = pDoc->NewIndirect<CPDF_Stream>(
+      std::move(stream), len, std::move(pFileStreamDict));
+  CPDF_Dictionary* pEFDict =
+      pFile->AsDictionary()->SetNewFor<CPDF_Dictionary>("EF");
+  pEFDict->SetNewFor<CPDF_Reference>("F", pDoc, pFileStream->GetObjNum());
+  return true;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
+                       void* buffer,
+                       unsigned long buflen) {
+  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
+  if (!pFile)
+    return 0;
+
+  CPDF_Stream* pFileStream = CPDF_FileSpec(pFile).GetFileStream();
+  if (!pFileStream)
+    return 0;
+
+  return DecodeStreamMaybeCopyAndReturnLength(pFileStream, buffer, buflen);
+}
diff --git a/fpdfsdk/fpdf_attachment_embeddertest.cpp b/fpdfsdk/fpdf_attachment_embeddertest.cpp
new file mode 100644
index 0000000..7ce6003
--- /dev/null
+++ b/fpdfsdk/fpdf_attachment_embeddertest.cpp
@@ -0,0 +1,250 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "public/fpdf_attachment.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/utils/hash.h"
+
+static constexpr char kDateKey[] = "CreationDate";
+static constexpr char kChecksumKey[] = "CheckSum";
+
+class FPDFAttachmentEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFAttachmentEmbedderTest, ExtractAttachments) {
+  // Open a file with two attachments.
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
+
+  // Retrieve the first attachment.
+  FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
+  ASSERT_TRUE(attachment);
+
+  // Check that the name of the first attachment is correct.
+  unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data()));
+
+  // Check that the content of the first attachment is correct.
+  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  std::vector<char> content_buf(length_bytes);
+  ASSERT_EQ(
+      4u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
+  EXPECT_EQ(std::string("test"), std::string(content_buf.data(), 4));
+
+  // Check that a non-existent key does not exist.
+  EXPECT_FALSE(FPDFAttachment_HasKey(attachment, "none"));
+
+  // Check that the string value of a non-string dictionary entry is empty.
+  static constexpr char kSizeKey[] = "Size";
+  EXPECT_EQ(FPDF_OBJECT_NUMBER,
+            FPDFAttachment_GetValueType(attachment, kSizeKey));
+  EXPECT_EQ(2u,
+            FPDFAttachment_GetStringValue(attachment, kSizeKey, nullptr, 0));
+
+  // Check that the creation date of the first attachment is correct.
+  length_bytes =
+      FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
+  ASSERT_EQ(48u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
+                                               length_bytes));
+  EXPECT_EQ(L"D:20170712214438-07'00'", GetPlatformWString(buf.data()));
+
+  // Retrieve the second attachment.
+  attachment = FPDFDoc_GetAttachment(document(), 1);
+  ASSERT_TRUE(attachment);
+
+  // Retrieve the second attachment file.
+  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  content_buf.clear();
+  content_buf.resize(length_bytes);
+  ASSERT_EQ(5869u, FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                          length_bytes));
+
+  // Check that the calculated checksum of the file data matches expectation.
+  const char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18";
+  const wchar_t kCheckSumW[] = L"<72AFCDDEDF554DDA63C0C88E06F1CE18>";
+  const std::string generated_checksum = GenerateMD5Base16(
+      reinterpret_cast<uint8_t*>(content_buf.data()), length_bytes);
+  EXPECT_EQ(kCheckSum, generated_checksum);
+
+  // Check that the stored checksum matches expectation.
+  length_bytes =
+      FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
+  ASSERT_EQ(70u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
+                                               buf.data(), length_bytes));
+  EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data()));
+}
+
+TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) {
+  // Open a file with two attachments.
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
+
+  // Check that adding an attachment with an empty name would fail.
+  EXPECT_FALSE(FPDFDoc_AddAttachment(document(), nullptr));
+
+  // Add an attachment to the beginning of the embedded file list.
+  ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt");
+  FPDF_ATTACHMENT attachment =
+      FPDFDoc_AddAttachment(document(), file_name.get());
+
+  // Check that writing to a file with nullptr but non-zero bytes would fail.
+  EXPECT_FALSE(FPDFAttachment_SetFile(attachment, document(), nullptr, 10));
+
+  // Set the new attachment's file.
+  constexpr char kContents1[] = "Hello!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
+                                     strlen(kContents1)));
+
+  // Verify the name of the new attachment (i.e. the first attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 0);
+  ASSERT_TRUE(attachment);
+  unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data()));
+
+  // Verify the content of the new attachment (i.e. the first attachment).
+  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  std::vector<char> content_buf(length_bytes);
+  ASSERT_EQ(
+      6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
+  EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6));
+
+  // Add an attachment to the end of the embedded file list and set its file.
+  file_name = GetFPDFWideString(L"z.txt");
+  attachment = FPDFDoc_AddAttachment(document(), file_name.get());
+  constexpr char kContents2[] = "World!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
+                                     strlen(kContents2)));
+  EXPECT_EQ(4, FPDFDoc_GetAttachmentCount(document()));
+
+  // Verify the name of the new attachment (i.e. the fourth attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 3);
+  ASSERT_TRUE(attachment);
+  length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data()));
+
+  // Verify the content of the new attachment (i.e. the fourth attachment).
+  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  content_buf.clear();
+  content_buf.resize(length_bytes);
+  ASSERT_EQ(
+      6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes));
+  EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6));
+}
+
+TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsWithParams) {
+  // Open a file with two attachments.
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
+
+  // Add an attachment to the embedded file list.
+  ScopedFPDFWideString file_name = GetFPDFWideString(L"5.txt");
+  FPDF_ATTACHMENT attachment =
+      FPDFDoc_AddAttachment(document(), file_name.get());
+  constexpr char kContents[] = "Hello World!";
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents,
+                                     strlen(kContents)));
+
+  // Set the date to be an arbitrary value.
+  constexpr wchar_t kDateW[] = L"D:20170720161527-04'00'";
+  ScopedFPDFWideString ws_date = GetFPDFWideString(kDateW);
+  EXPECT_TRUE(
+      FPDFAttachment_SetStringValue(attachment, kDateKey, ws_date.get()));
+
+  // Set the checksum to be an arbitrary value.
+  constexpr wchar_t kCheckSumW[] = L"<ABCDEF01234567899876543210FEDCBA>";
+  ScopedFPDFWideString ws_checksum = GetFPDFWideString(kCheckSumW);
+  EXPECT_TRUE(FPDFAttachment_SetStringValue(attachment, kChecksumKey,
+                                            ws_checksum.get()));
+
+  // Verify the name of the new attachment (i.e. the second attachment).
+  attachment = FPDFDoc_GetAttachment(document(), 1);
+  ASSERT_TRUE(attachment);
+  unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"5.txt", GetPlatformWString(buf.data()));
+
+  // Verify the content of the new attachment.
+  length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+  std::vector<char> content_buf(length_bytes);
+  ASSERT_EQ(12u, FPDFAttachment_GetFile(attachment, content_buf.data(),
+                                        length_bytes));
+  EXPECT_EQ(std::string(kContents), std::string(content_buf.data(), 12));
+
+  // Verify the creation date of the new attachment.
+  length_bytes =
+      FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
+  ASSERT_EQ(48u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
+                                               length_bytes));
+  EXPECT_EQ(kDateW, GetPlatformWString(buf.data()));
+
+  // Verify the checksum of the new attachment.
+  length_bytes =
+      FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
+  ASSERT_EQ(70u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
+                                               buf.data(), length_bytes));
+  EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data()));
+
+  // Overwrite the existing file with empty content, and check that the checksum
+  // gets updated to the correct value.
+  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), nullptr, 0));
+  EXPECT_EQ(0u, FPDFAttachment_GetFile(attachment, nullptr, 0));
+  length_bytes =
+      FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
+  ASSERT_EQ(70u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
+                                               buf.data(), length_bytes));
+  EXPECT_EQ(L"<D41D8CD98F00B204E9800998ECF8427E>",
+            GetPlatformWString(buf.data()));
+}
+
+TEST_F(FPDFAttachmentEmbedderTest, DeleteAttachment) {
+  // Open a file with two attachments.
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
+  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
+
+  // Verify the name of the first attachment.
+  FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
+  unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(12u, length_bytes);
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data()));
+
+  // Delete the first attachment.
+  EXPECT_TRUE(FPDFDoc_DeleteAttachment(document(), 0));
+  EXPECT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
+
+  // Verify the name of the new first attachment.
+  attachment = FPDFDoc_GetAttachment(document(), 0);
+  length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+  ASSERT_EQ(26u, length_bytes);
+  buf = GetFPDFWideStringBuffer(length_bytes);
+  EXPECT_EQ(26u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes));
+  EXPECT_EQ(L"attached.pdf", GetPlatformWString(buf.data()));
+}
diff --git a/fpdfsdk/fpdf_catalog.cpp b/fpdfsdk/fpdf_catalog.cpp
new file mode 100644
index 0000000..97bddde
--- /dev/null
+++ b/fpdfsdk/fpdf_catalog.cpp
@@ -0,0 +1,23 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_catalog.h"
+
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFCatalog_IsTagged(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return false;
+
+  const CPDF_Dictionary* pCatalog = pDoc->GetRoot();
+  if (!pCatalog)
+    return false;
+
+  const CPDF_Dictionary* pMarkInfo = pCatalog->GetDictFor("MarkInfo");
+  return pMarkInfo && pMarkInfo->GetIntegerFor("Marked") != 0;
+}
diff --git a/fpdfsdk/fpdf_catalog_unittest.cpp b/fpdfsdk/fpdf_catalog_unittest.cpp
new file mode 100644
index 0000000..68655da
--- /dev/null
+++ b/fpdfsdk/fpdf_catalog_unittest.cpp
@@ -0,0 +1,81 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_catalog.h"
+
+#include <memory>
+
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#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_number.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+class CPDF_TestDocument final : public CPDF_Document {
+ public:
+  CPDF_TestDocument()
+      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
+                      pdfium::MakeUnique<CPDF_DocPageData>()) {}
+
+  void SetRoot(CPDF_Dictionary* root) { m_pRootDict.Reset(root); }
+};
+
+class PDFCatalogTest : public testing::Test {
+ public:
+  void SetUp() override {
+    CPDF_PageModule::Create();
+    auto pTestDoc = pdfium::MakeUnique<CPDF_TestDocument>();
+    m_pDoc.reset(FPDFDocumentFromCPDFDocument(pTestDoc.release()));
+    m_pRootObj = pdfium::MakeRetain<CPDF_Dictionary>();
+  }
+
+  void TearDown() override {
+    m_pDoc.reset();
+    CPDF_PageModule::Destroy();
+  }
+
+ protected:
+  ScopedFPDFDocument m_pDoc;
+  RetainPtr<CPDF_Dictionary> m_pRootObj;
+};
+
+TEST_F(PDFCatalogTest, IsTagged) {
+  // Null doc
+  EXPECT_FALSE(FPDFCatalog_IsTagged(nullptr));
+
+  CPDF_TestDocument* pTestDoc = static_cast<CPDF_TestDocument*>(
+      CPDFDocumentFromFPDFDocument(m_pDoc.get()));
+
+  // No root
+  pTestDoc->SetRoot(nullptr);
+  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
+
+  // Empty root
+  pTestDoc->SetRoot(m_pRootObj.Get());
+  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
+
+  // Root with other key
+  m_pRootObj->SetNewFor<CPDF_String>("OTHER_KEY", "other value", false);
+  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
+
+  // Root with empty MarkInfo
+  CPDF_Dictionary* markInfoDict =
+      m_pRootObj->SetNewFor<CPDF_Dictionary>("MarkInfo");
+  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
+
+  // MarkInfo present but Marked is 0
+  markInfoDict->SetNewFor<CPDF_Number>("Marked", 0);
+  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
+
+  // MarkInfo present and Marked is 1, PDF is considered tagged.
+  markInfoDict->SetNewFor<CPDF_Number>("Marked", 1);
+  EXPECT_TRUE(FPDFCatalog_IsTagged(m_pDoc.get()));
+}
diff --git a/fpdfsdk/fpdf_dataavail.cpp b/fpdfsdk/fpdf_dataavail.cpp
index b1a134d..11abff3 100644
--- a/fpdfsdk/fpdf_dataavail.cpp
+++ b/fpdfsdk/fpdf_dataavail.cpp
@@ -9,14 +9,21 @@
 #include <memory>
 #include <utility>
 
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/parser/cpdf_data_avail.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "fpdfsdk/fsdk_define.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/fpdf_formfill.h"
 #include "third_party/base/ptr_util.h"
 
+#ifdef PDF_ENABLE_XFA
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#endif  // PDF_ENABLE_XFA
+
 // These checks are here because core/ and public/ cannot depend on each other.
 static_assert(CPDF_DataAvail::DataError == PDF_DATA_ERROR,
               "CPDF_DataAvail::DataError value mismatch");
@@ -43,46 +50,52 @@
 
 namespace {
 
-class FPDF_FileAvailContext : public CPDF_DataAvail::FileAvail {
+class FPDF_FileAvailContext final : public CPDF_DataAvail::FileAvail {
  public:
-  FPDF_FileAvailContext() : m_pfileAvail(nullptr) {}
-  ~FPDF_FileAvailContext() override {}
-
-  void Set(FX_FILEAVAIL* pfileAvail) { m_pfileAvail = pfileAvail; }
+  explicit FPDF_FileAvailContext(FX_FILEAVAIL* avail) : avail_(avail) {}
+  ~FPDF_FileAvailContext() override = default;
 
   // CPDF_DataAvail::FileAvail:
   bool IsDataAvail(FX_FILESIZE offset, size_t size) override {
-    return !!m_pfileAvail->IsDataAvail(m_pfileAvail, offset, size);
+    return !!avail_->IsDataAvail(avail_, offset, size);
   }
 
  private:
-  FX_FILEAVAIL* m_pfileAvail;
+  FX_FILEAVAIL* const avail_;
 };
 
-class FPDF_FileAccessContext : public IFX_SeekableReadStream {
+class FPDF_FileAccessContext final : public IFX_SeekableReadStream {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
-  ~FPDF_FileAccessContext() override {}
+  // IFX_SeekableReadStream:
+  FX_FILESIZE GetSize() override { return file_->m_FileLen; }
 
-  void Set(FPDF_FILEACCESS* pFile) { m_pFileAccess = pFile; }
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override {
+    if (!buffer || offset < 0 || !size)
+      return false;
 
-  // IFX_SeekableReadStream
-  FX_FILESIZE GetSize() override { return m_pFileAccess->m_FileLen; }
+    if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(size))
+      return false;
 
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override {
-    return !!m_pFileAccess->m_GetBlock(m_pFileAccess->m_Param, offset,
-                                       (uint8_t*)buffer, size);
+    FX_SAFE_FILESIZE new_pos = size;
+    new_pos += offset;
+    return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() &&
+           file_->m_GetBlock(file_->m_Param, offset,
+                             static_cast<uint8_t*>(buffer), size);
   }
 
  private:
-  FPDF_FileAccessContext() : m_pFileAccess(nullptr) {}
+  explicit FPDF_FileAccessContext(FPDF_FILEACCESS* file) : file_(file) {}
+  ~FPDF_FileAccessContext() override = default;
 
-  FPDF_FILEACCESS* m_pFileAccess;
+  FPDF_FILEACCESS* const file_;
 };
 
-class FPDF_DownloadHintsContext : public CPDF_DataAvail::DownloadHints {
+class FPDF_DownloadHintsContext final : public CPDF_DataAvail::DownloadHints {
  public:
   explicit FPDF_DownloadHintsContext(FX_DOWNLOADHINTS* pDownloadHints) {
     m_pDownloadHints = pDownloadHints;
@@ -102,14 +115,20 @@
 
 class FPDF_AvailContext {
  public:
-  FPDF_AvailContext()
-      : m_FileAvail(pdfium::MakeUnique<FPDF_FileAvailContext>()),
-        m_FileRead(pdfium::MakeRetain<FPDF_FileAccessContext>()) {}
-  ~FPDF_AvailContext() {}
+  FPDF_AvailContext(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file)
+      : file_avail_(pdfium::MakeUnique<FPDF_FileAvailContext>(file_avail)),
+        file_read_(pdfium::MakeRetain<FPDF_FileAccessContext>(file)),
+        data_avail_(pdfium::MakeUnique<CPDF_DataAvail>(file_avail_.get(),
+                                                       file_read_,
+                                                       true)) {}
+  ~FPDF_AvailContext() = default;
 
-  std::unique_ptr<FPDF_FileAvailContext> m_FileAvail;
-  RetainPtr<FPDF_FileAccessContext> m_FileRead;
-  std::unique_ptr<CPDF_DataAvail> m_pDataAvail;
+  CPDF_DataAvail* data_avail() { return data_avail_.get(); }
+
+ private:
+  std::unique_ptr<FPDF_FileAvailContext> const file_avail_;
+  RetainPtr<FPDF_FileAccessContext> const file_read_;
+  std::unique_ptr<CPDF_DataAvail> const data_avail_;
 };
 
 FPDF_AvailContext* FPDFAvailContextFromFPDFAvail(FPDF_AVAIL avail) {
@@ -120,11 +139,7 @@
 
 FPDF_EXPORT FPDF_AVAIL FPDF_CALLCONV FPDFAvail_Create(FX_FILEAVAIL* file_avail,
                                                       FPDF_FILEACCESS* file) {
-  auto pAvail = pdfium::MakeUnique<FPDF_AvailContext>();
-  pAvail->m_FileAvail->Set(file_avail);
-  pAvail->m_FileRead->Set(file);
-  pAvail->m_pDataAvail = pdfium::MakeUnique<CPDF_DataAvail>(
-      pAvail->m_FileAvail.get(), pAvail->m_FileRead, true);
+  auto pAvail = pdfium::MakeUnique<FPDF_AvailContext>(file_avail, file);
   return pAvail.release();  // Caller takes ownership.
 }
 
@@ -135,26 +150,33 @@
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsDocAvail(FPDF_AVAIL avail,
                                                    FX_DOWNLOADHINTS* hints) {
-  if (!avail)
+  auto* avail_context = FPDFAvailContextFromFPDFAvail(avail);
+  if (!avail_context)
     return PDF_DATA_ERROR;
   FPDF_DownloadHintsContext hints_context(hints);
-  return FPDFAvailContextFromFPDFAvail(avail)->m_pDataAvail->IsDocAvail(
-      &hints_context);
+  return avail_context->data_avail()->IsDocAvail(&hints_context);
 }
 
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDFAvail_GetDocument(FPDF_AVAIL avail, FPDF_BYTESTRING password) {
-  auto* pDataAvail = FPDFAvailContextFromFPDFAvail(avail);
-  if (!pDataAvail)
+  auto* avail_context = FPDFAvailContextFromFPDFAvail(avail);
+  if (!avail_context)
     return nullptr;
   CPDF_Parser::Error error;
   std::unique_ptr<CPDF_Document> document;
-  std::tie(error, document) = pDataAvail->m_pDataAvail->ParseDocument(password);
+  std::tie(error, document) = avail_context->data_avail()->ParseDocument(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>(), password);
   if (error != CPDF_Parser::SUCCESS) {
     ProcessParseError(error);
     return nullptr;
   }
-  CheckUnSupportError(document.get(), FPDF_ERR_SUCCESS);
+
+#ifdef PDF_ENABLE_XFA
+  document->SetExtension(pdfium::MakeUnique<CPDFXFA_Context>(document.get()));
+#endif  // PDF_ENABLE_XFA
+
+  ReportUnsupportedFeatures(document.get());
   return FPDFDocumentFromCPDFDocument(document.release());
 }
 
@@ -166,26 +188,27 @@
 FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsPageAvail(FPDF_AVAIL avail,
                                                     int page_index,
                                                     FX_DOWNLOADHINTS* hints) {
-  if (!avail)
+  auto* avail_context = FPDFAvailContextFromFPDFAvail(avail);
+  if (!avail_context)
     return PDF_DATA_ERROR;
   if (page_index < 0)
     return PDF_DATA_NOTAVAIL;
   FPDF_DownloadHintsContext hints_context(hints);
-  return FPDFAvailContextFromFPDFAvail(avail)->m_pDataAvail->IsPageAvail(
-      page_index, &hints_context);
+  return avail_context->data_avail()->IsPageAvail(page_index, &hints_context);
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsFormAvail(FPDF_AVAIL avail,
                                                     FX_DOWNLOADHINTS* hints) {
-  if (!avail)
+  auto* avail_context = FPDFAvailContextFromFPDFAvail(avail);
+  if (!avail_context)
     return PDF_FORM_ERROR;
   FPDF_DownloadHintsContext hints_context(hints);
-  return FPDFAvailContextFromFPDFAvail(avail)->m_pDataAvail->IsFormAvail(
-      &hints_context);
+  return avail_context->data_avail()->IsFormAvail(&hints_context);
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsLinearized(FPDF_AVAIL avail) {
-  if (!avail)
+  auto* avail_context = FPDFAvailContextFromFPDFAvail(avail);
+  if (!avail_context)
     return PDF_LINEARIZATION_UNKNOWN;
-  return FPDFAvailContextFromFPDFAvail(avail)->m_pDataAvail->IsLinearizedPDF();
+  return avail_context->data_avail()->IsLinearizedPDF();
 }
diff --git a/fpdfsdk/fpdf_dataavail_embeddertest.cpp b/fpdfsdk/fpdf_dataavail_embeddertest.cpp
index 2084153..02b481c 100644
--- a/fpdfsdk/fpdf_dataavail_embeddertest.cpp
+++ b/fpdfsdk/fpdf_dataavail_embeddertest.cpp
@@ -4,21 +4,22 @@
 
 #include <algorithm>
 #include <memory>
-#include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/widestring.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/range_set.h"
-#include "testing/test_support.h"
+#include "testing/utils/file_util.h"
 #include "testing/utils/path_service.h"
 
 namespace {
 
-class MockDownloadHints : public FX_DOWNLOADHINTS {
+class MockDownloadHints final : public FX_DOWNLOADHINTS {
  public:
   static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
   }
@@ -28,10 +29,10 @@
     FX_DOWNLOADHINTS::AddSegment = SAddSegment;
   }
 
-  ~MockDownloadHints() {}
+  ~MockDownloadHints() = default;
 };
 
-class TestAsyncLoader : public FX_DOWNLOADHINTS, FX_FILEAVAIL {
+class TestAsyncLoader final : public FX_DOWNLOADHINTS, FX_FILEAVAIL {
  public:
   explicit TestAsyncLoader(const std::string& file_name) {
     std::string file_path;
@@ -87,6 +88,9 @@
     ClearRequestedSegments();
   }
 
+  char* file_contents() { return file_contents_.get(); }
+  size_t file_length() const { return file_length_; }
+
  private:
   void SetDataAvailable(size_t start, size_t size) {
     available_ranges_.Union(RangeSet::Range(start, start + size));
@@ -143,7 +147,7 @@
   FPDF_FILEACCESS file_access_;
 
   std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
-  size_t file_length_;
+  size_t file_length_ = 0;
   std::vector<std::pair<size_t, size_t>> requested_segments_;
   size_t max_requested_bound_ = 0;
   bool is_new_data_available_ = true;
@@ -153,23 +157,23 @@
 
 }  // namespace
 
-class FPDFDataAvailEmbeddertest : public EmbedderTest {};
+class FPDFDataAvailEmbedderTest : public EmbedderTest {};
 
-TEST_F(FPDFDataAvailEmbeddertest, TrailerUnterminated) {
+TEST_F(FPDFDataAvailEmbedderTest, TrailerUnterminated) {
   // Document must load without crashing but is too malformed to be available.
   EXPECT_FALSE(OpenDocument("trailer_unterminated.pdf"));
   MockDownloadHints hints;
   EXPECT_FALSE(FPDFAvail_IsDocAvail(avail_, &hints));
 }
 
-TEST_F(FPDFDataAvailEmbeddertest, TrailerAsHexstring) {
+TEST_F(FPDFDataAvailEmbedderTest, TrailerAsHexstring) {
   // Document must load without crashing but is too malformed to be available.
   EXPECT_FALSE(OpenDocument("trailer_as_hexstring.pdf"));
   MockDownloadHints hints;
   EXPECT_FALSE(FPDFAvail_IsDocAvail(avail_, &hints));
 }
 
-TEST_F(FPDFDataAvailEmbeddertest, LoadUsingHintTables) {
+TEST_F(FPDFDataAvailEmbedderTest, LoadUsingHintTables) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
   avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
@@ -179,20 +183,19 @@
 
   // No new data available, to prevent load "Pages" node.
   loader.set_is_new_data_available(false);
-  FPDF_PAGE page = FPDF_LoadPage(document(), 1);
+  ScopedFPDFPage page(FPDF_LoadPage(document(), 1));
   EXPECT_TRUE(page);
-  FPDF_ClosePage(page);
 }
 
-TEST_F(FPDFDataAvailEmbeddertest, CheckFormAvailIfLinearized) {
+TEST_F(FPDFDataAvailEmbedderTest, CheckFormAvailIfLinearized) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
   avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
   document_ = FPDFAvail_GetDocument(avail_, nullptr);
   ASSERT_TRUE(document_);
 
-  // Prevent access to non requested data to coerce the parser to send new
-  // request for non available (non requested before) data.
+  // Prevent access to non-requested data to coerce the parser to send new
+  // request for non available (non-requested before) data.
   loader.set_is_new_data_available(false);
   loader.ClearRequestedSegments();
 
@@ -204,7 +207,7 @@
   EXPECT_NE(PDF_FORM_ERROR, status);
 }
 
-TEST_F(FPDFDataAvailEmbeddertest,
+TEST_F(FPDFDataAvailEmbedderTest,
        DoNotLoadMainCrossRefForFirstPageIfLinearized) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
   avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
@@ -218,8 +221,8 @@
   EXPECT_GT(loader.file_access()->m_FileLen,
             loader.max_already_available_bound());
 
-  // Prevent access to non requested data to coerce the parser to send new
-  // request for non available (non requested before) data.
+  // Prevent access to non-requested data to coerce the parser to send new
+  // request for non available (non-requested before) data.
   loader.set_is_new_data_available(false);
   FPDFAvail_IsPageAvail(avail_, first_page_num, loader.hints());
 
@@ -239,12 +242,11 @@
 
   // Prevent loading data, while page loading.
   loader.set_is_new_data_available(false);
-  FPDF_PAGE page = FPDF_LoadPage(document(), first_page_num);
+  ScopedFPDFPage page(FPDF_LoadPage(document(), first_page_num));
   EXPECT_TRUE(page);
-  FPDF_ClosePage(page);
 }
 
-TEST_F(FPDFDataAvailEmbeddertest, LoadSecondPageIfLinearizedWithHints) {
+TEST_F(FPDFDataAvailEmbedderTest, LoadSecondPageIfLinearizedWithHints) {
   TestAsyncLoader loader("feature_linearized_loading.pdf");
   avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
@@ -253,8 +255,8 @@
 
   static constexpr uint32_t kSecondPageNum = 1;
 
-  // Prevent access to non requested data to coerce the parser to send new
-  // request for non available (non requested before) data.
+  // Prevent access to non-requested data to coerce the parser to send new
+  // request for non available (non-requested before) data.
   loader.set_is_new_data_available(false);
   loader.ClearRequestedSegments();
 
@@ -267,7 +269,125 @@
 
   // Prevent loading data, while page loading.
   loader.set_is_new_data_available(false);
-  FPDF_PAGE page = FPDF_LoadPage(document(), kSecondPageNum);
+  ScopedFPDFPage page(FPDF_LoadPage(document(), kSecondPageNum));
   EXPECT_TRUE(page);
-  FPDF_ClosePage(page);
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingWholeDocument) {
+  TestAsyncLoader loader("linearized.pdf");
+  loader.set_is_new_data_available(false);
+  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+    loader.FlushRequestedData();
+  }
+
+  document_ = FPDFAvail_GetDocument(avail_, nullptr);
+  ASSERT_TRUE(document_);
+
+  // The "info" dictionary should still be unavailable.
+  EXPECT_FALSE(FPDF_GetMetaText(document_, "CreationDate", nullptr, 0));
+
+  // Simulate receiving whole file.
+  loader.set_is_new_data_available(true);
+  // Load second page, to parse additional crossref sections.
+  EXPECT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 1, loader.hints()));
+
+  EXPECT_TRUE(FPDF_GetMetaText(document_, "CreationDate", nullptr, 0));
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingFirstPage) {
+  TestAsyncLoader loader("linearized.pdf");
+  // Map "Info" to an object within the first section without breaking
+  // linearization.
+  ByteString data(loader.file_contents(), loader.file_length());
+  Optional<size_t> index = data.Find("/Info 27 0 R");
+  ASSERT_TRUE(index);
+  memcpy(loader.file_contents() + *index, "/Info 29 0 R", 12);
+
+  loader.set_is_new_data_available(false);
+  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+    loader.FlushRequestedData();
+  }
+
+  document_ = FPDFAvail_GetDocument(avail_, nullptr);
+  ASSERT_TRUE(document_);
+
+  // The "Info" dictionary should be available for the linearized document, if
+  // it is located in the first page section.
+  // Info was remapped to a dictionary with Type "Catalog"
+  unsigned short buffer[100] = {0};
+  EXPECT_TRUE(FPDF_GetMetaText(document_, "Type", buffer, sizeof(buffer)));
+  constexpr wchar_t kExpectedValue[] = L"Catalog";
+  EXPECT_EQ(WideString(kExpectedValue),
+            WideString::FromUTF16LE(buffer, FXSYS_len(kExpectedValue)));
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, TryLoadInvalidInfo) {
+  TestAsyncLoader loader("linearized.pdf");
+  // Map "Info" to an invalid object without breaking linearization.
+  ByteString data(loader.file_contents(), loader.file_length());
+  Optional<size_t> index = data.Find("/Info 27 0 R");
+  ASSERT_TRUE(index);
+  memcpy(loader.file_contents() + *index, "/Info 99 0 R", 12);
+
+  loader.set_is_new_data_available(false);
+  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+    loader.FlushRequestedData();
+  }
+
+  document_ = FPDFAvail_GetDocument(avail_, nullptr);
+  ASSERT_TRUE(document_);
+
+  // Set all data available.
+  loader.set_is_new_data_available(true);
+  // Check second page, to load additional crossrefs.
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 0, loader.hints()));
+
+  // Test that api is robust enough to handle the bad case.
+  EXPECT_FALSE(FPDF_GetMetaText(document_, "Type", nullptr, 0));
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, TryLoadNonExistsInfo) {
+  TestAsyncLoader loader("linearized.pdf");
+  // Break the "Info" parameter without breaking linearization.
+  ByteString data(loader.file_contents(), loader.file_length());
+  Optional<size_t> index = data.Find("/Info 27 0 R");
+  ASSERT_TRUE(index);
+  memcpy(loader.file_contents() + *index, "/I_fo 27 0 R", 12);
+
+  loader.set_is_new_data_available(false);
+  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
+  while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) {
+    loader.FlushRequestedData();
+  }
+
+  document_ = FPDFAvail_GetDocument(avail_, nullptr);
+  ASSERT_TRUE(document_);
+
+  // Set all data available.
+  loader.set_is_new_data_available(true);
+  // Check second page, to load additional crossrefs.
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 0, loader.hints()));
+
+  // Test that api is robust enough to handle the bad case.
+  EXPECT_FALSE(FPDF_GetMetaText(document_, "Type", nullptr, 0));
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, BadInputsToAPIs) {
+  EXPECT_EQ(PDF_DATA_ERROR, FPDFAvail_IsDocAvail(nullptr, nullptr));
+  EXPECT_FALSE(FPDFAvail_GetDocument(nullptr, nullptr));
+  EXPECT_EQ(0, FPDFAvail_GetFirstPageNum(nullptr));
+  EXPECT_EQ(PDF_DATA_ERROR, FPDFAvail_IsPageAvail(nullptr, 0, nullptr));
+  EXPECT_EQ(PDF_FORM_ERROR, FPDFAvail_IsFormAvail(nullptr, nullptr));
+  EXPECT_EQ(PDF_LINEARIZATION_UNKNOWN, FPDFAvail_IsLinearized(nullptr));
+}
+
+TEST_F(FPDFDataAvailEmbedderTest, NegativePageIndex) {
+  TestAsyncLoader loader("linearized.pdf");
+  avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access());
+  ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints()));
+  EXPECT_EQ(PDF_DATA_NOTAVAIL,
+            FPDFAvail_IsPageAvail(avail_, -1, loader.hints()));
 }
diff --git a/fpdfsdk/fpdf_doc.cpp b/fpdfsdk/fpdf_doc.cpp
new file mode 100644
index 0000000..b0a2324
--- /dev/null
+++ b/fpdfsdk/fpdf_doc.cpp
@@ -0,0 +1,428 @@
+// 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 "public/fpdf_doc.h"
+
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfdoc/cpdf_bookmark.h"
+#include "core/fpdfdoc/cpdf_bookmarktree.h"
+#include "core/fpdfdoc/cpdf_dest.h"
+#include "core/fpdfdoc/cpdf_linklist.h"
+#include "core/fpdfdoc/cpdf_pagelabel.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+CPDF_Bookmark FindBookmark(const CPDF_BookmarkTree& tree,
+                           CPDF_Bookmark bookmark,
+                           const WideString& title,
+                           std::set<const CPDF_Dictionary*>* visited) {
+  // Return if already checked to avoid circular calling.
+  if (pdfium::ContainsKey(*visited, bookmark.GetDict()))
+    return CPDF_Bookmark();
+  visited->insert(bookmark.GetDict());
+
+  if (bookmark.GetDict() &&
+      bookmark.GetTitle().CompareNoCase(title.c_str()) == 0) {
+    // First check this item.
+    return bookmark;
+  }
+
+  // Go into children items.
+  CPDF_Bookmark child = tree.GetFirstChild(&bookmark);
+  while (child.GetDict() && !pdfium::ContainsKey(*visited, child.GetDict())) {
+    // Check this item and its children.
+    CPDF_Bookmark found = FindBookmark(tree, child, title, visited);
+    if (found.GetDict())
+      return found;
+    child = tree.GetNextSibling(&child);
+  }
+  return CPDF_Bookmark();
+}
+
+CPDF_LinkList* GetLinkList(CPDF_Page* page) {
+  CPDF_Document* pDoc = page->GetDocument();
+  auto* pList = static_cast<CPDF_LinkList*>(pDoc->GetLinksContext());
+  if (pList)
+    return pList;
+
+  auto pNewList = pdfium::MakeUnique<CPDF_LinkList>();
+  pList = pNewList.get();
+  pDoc->SetLinksContext(std::move(pNewList));
+  return pList;
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
+FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+  CPDF_BookmarkTree tree(pDoc);
+  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  return FPDFBookmarkFromCPDFDictionary(
+      tree.GetFirstChild(&cBookmark).GetDict());
+}
+
+FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
+FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  if (!bookmark)
+    return nullptr;
+
+  CPDF_BookmarkTree tree(pDoc);
+  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  return FPDFBookmarkFromCPDFDictionary(
+      tree.GetNextSibling(&cBookmark).GetDict());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFBookmark_GetTitle(FPDF_BOOKMARK bookmark,
+                      void* buffer,
+                      unsigned long buflen) {
+  if (!bookmark)
+    return 0;
+  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  WideString title = cBookmark.GetTitle();
+  return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen);
+}
+
+FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
+FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  WideString encodedTitle = WideStringFromFPDFWideString(title);
+  if (encodedTitle.IsEmpty())
+    return nullptr;
+
+  CPDF_BookmarkTree tree(pDoc);
+  std::set<const CPDF_Dictionary*> visited;
+  return FPDFBookmarkFromCPDFDictionary(
+      FindBookmark(tree, CPDF_Bookmark(), encodedTitle, &visited).GetDict());
+}
+
+FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
+FPDFBookmark_GetDest(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  if (!bookmark)
+    return nullptr;
+
+  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  CPDF_Dest dest = cBookmark.GetDest(pDoc);
+  if (dest.GetArray())
+    return FPDFDestFromCPDFArray(dest.GetArray());
+  // If this bookmark is not directly associated with a dest, we try to get
+  // action
+  CPDF_Action action = cBookmark.GetAction();
+  if (!action.GetDict())
+    return nullptr;
+  return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray());
+}
+
+FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
+FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark) {
+  if (!bookmark)
+    return nullptr;
+
+  CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark));
+  return FPDFActionFromCPDFDictionary(cBookmark.GetAction().GetDict());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAction_GetType(FPDF_ACTION action) {
+  if (!action)
+    return PDFACTION_UNSUPPORTED;
+
+  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
+  CPDF_Action::ActionType type = cAction.GetType();
+  switch (type) {
+    case CPDF_Action::GoTo:
+      return PDFACTION_GOTO;
+    case CPDF_Action::GoToR:
+      return PDFACTION_REMOTEGOTO;
+    case CPDF_Action::URI:
+      return PDFACTION_URI;
+    case CPDF_Action::Launch:
+      return PDFACTION_LAUNCH;
+    default:
+      return PDFACTION_UNSUPPORTED;
+  }
+}
+
+FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document,
+                                                       FPDF_ACTION action) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  unsigned long type = FPDFAction_GetType(action);
+  if (type != PDFACTION_GOTO && type != PDFACTION_REMOTEGOTO)
+    return nullptr;
+
+  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
+  return FPDFDestFromCPDFArray(cAction.GetDest(pDoc).GetArray());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAction_GetFilePath(FPDF_ACTION action, void* buffer, unsigned long buflen) {
+  unsigned long type = FPDFAction_GetType(action);
+  if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_LAUNCH)
+    return 0;
+
+  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
+  ByteString path = cAction.GetFilePath().ToUTF8();
+  unsigned long len = path.GetLength() + 1;
+  if (buffer && len <= buflen)
+    memcpy(buffer, path.c_str(), len);
+  return len;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAction_GetURIPath(FPDF_DOCUMENT document,
+                      FPDF_ACTION action,
+                      void* buffer,
+                      unsigned long buflen) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  unsigned long type = FPDFAction_GetType(action);
+  if (type != PDFACTION_URI)
+    return 0;
+
+  CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
+  ByteString path = cAction.GetURI(pDoc);
+  unsigned long len = path.GetLength() + 1;
+  if (buffer && len <= buflen)
+    memcpy(buffer, path.c_str(), len);
+  return len;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document,
+                                                        FPDF_DEST dest) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return -1;
+
+  if (!dest)
+    return -1;
+
+  CPDF_Dest destination(CPDFArrayFromFPDFDest(dest));
+  return destination.GetDestPageIndex(pDoc);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFDest_GetView(FPDF_DEST dest, unsigned long* pNumParams, FS_FLOAT* pParams) {
+  if (!dest) {
+    *pNumParams = 0;
+    return 0;
+  }
+
+  CPDF_Dest destination(CPDFArrayFromFPDFDest(dest));
+  unsigned long nParams = destination.GetNumParams();
+  ASSERT(nParams <= 4);
+  *pNumParams = nParams;
+  for (unsigned long i = 0; i < nParams; ++i)
+    pParams[i] = destination.GetParam(i);
+  return destination.GetZoomMode();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFDest_GetLocationInPage(FPDF_DEST dest,
+                           FPDF_BOOL* hasXVal,
+                           FPDF_BOOL* hasYVal,
+                           FPDF_BOOL* hasZoomVal,
+                           FS_FLOAT* x,
+                           FS_FLOAT* y,
+                           FS_FLOAT* zoom) {
+  if (!dest)
+    return false;
+
+  auto destination = pdfium::MakeUnique<CPDF_Dest>(CPDFArrayFromFPDFDest(dest));
+
+  // FPDF_BOOL is an int, GetXYZ expects bools.
+  bool bHasX;
+  bool bHasY;
+  bool bHasZoom;
+  if (!destination->GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom))
+    return false;
+
+  *hasXVal = bHasX;
+  *hasYVal = bHasY;
+  *hasZoomVal = bHasZoom;
+  return true;
+}
+
+FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page,
+                                                            double x,
+                                                            double y) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return nullptr;
+
+  CPDF_LinkList* pLinkList = GetLinkList(pPage);
+  if (!pLinkList)
+    return nullptr;
+
+  CPDF_Link link = pLinkList->GetLinkAtPoint(
+      pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)), nullptr);
+
+  return FPDFLinkFromCPDFDictionary(link.GetDict());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
+                                                            double x,
+                                                            double y) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return -1;
+
+  CPDF_LinkList* pLinkList = GetLinkList(pPage);
+  if (!pLinkList)
+    return -1;
+
+  int z_order = -1;
+  pLinkList->GetLinkAtPoint(
+      pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
+      &z_order);
+  return z_order;
+}
+
+FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document,
+                                                     FPDF_LINK link) {
+  if (!link)
+    return nullptr;
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+  CPDF_Link cLink(CPDFDictionaryFromFPDFLink(link));
+  FPDF_DEST dest = FPDFDestFromCPDFArray(cLink.GetDest(pDoc).GetArray());
+  if (dest)
+    return dest;
+  // If this link is not directly associated with a dest, we try to get action
+  CPDF_Action action = cLink.GetAction();
+  if (!action.GetDict())
+    return nullptr;
+  return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray());
+}
+
+FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK link) {
+  if (!link)
+    return nullptr;
+
+  CPDF_Link cLink(CPDFDictionaryFromFPDFLink(link));
+  return FPDFActionFromCPDFDictionary(cLink.GetAction().GetDict());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page,
+                                                       int* start_pos,
+                                                       FPDF_LINK* link_annot) {
+  if (!start_pos || !link_annot)
+    return false;
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return false;
+  CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
+  if (!pAnnots)
+    return false;
+  for (size_t i = *start_pos; i < pAnnots->size(); i++) {
+    CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(i));
+    if (!pDict)
+      continue;
+    if (pDict->GetStringFor("Subtype") == "Link") {
+      *start_pos = static_cast<int>(i + 1);
+      *link_annot = FPDFLinkFromCPDFDictionary(pDict);
+      return true;
+    }
+  }
+  return false;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK link_annot,
+                                                          FS_RECTF* rect) {
+  if (!link_annot || !rect)
+    return false;
+
+  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFLink(link_annot);
+  *rect = FSRectFFromCFXFloatRect(pAnnotDict->GetRectFor("Rect"));
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK link_annot) {
+  const CPDF_Array* pArray =
+      GetQuadPointsArrayFromDictionary(CPDFDictionaryFromFPDFLink(link_annot));
+  return pArray ? static_cast<int>(pArray->size() / 8) : 0;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFLink_GetQuadPoints(FPDF_LINK link_annot,
+                       int quad_index,
+                       FS_QUADPOINTSF* quad_points) {
+  if (!quad_points || quad_index < 0)
+    return false;
+
+  const CPDF_Dictionary* pLinkDict = CPDFDictionaryFromFPDFLink(link_annot);
+  if (!pLinkDict)
+    return false;
+
+  const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pLinkDict);
+  if (!pArray)
+    return false;
+
+  return GetQuadPointsAtIndex(pArray, static_cast<size_t>(quad_index),
+                              quad_points);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
+                                                         FPDF_BYTESTRING tag,
+                                                         void* buffer,
+                                                         unsigned long buflen) {
+  if (!tag)
+    return 0;
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  const CPDF_Dictionary* pInfo = pDoc->GetInfo();
+  if (!pInfo)
+    return 0;
+  WideString text = pInfo->GetUnicodeTextFor(tag);
+  return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetPageLabel(FPDF_DOCUMENT document,
+                  int page_index,
+                  void* buffer,
+                  unsigned long buflen) {
+  if (page_index < 0)
+    return 0;
+
+  // CPDF_PageLabel can deal with NULL |document|.
+  CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document));
+  Optional<WideString> str = label.GetLabel(page_index);
+  return str.has_value()
+             ? Utf16EncodeMaybeCopyAndReturnLength(str.value(), buffer, buflen)
+             : 0;
+}
diff --git a/fpdfsdk/fpdf_doc_embeddertest.cpp b/fpdfsdk/fpdf_doc_embeddertest.cpp
new file mode 100644
index 0000000..84712ed
--- /dev/null
+++ b/fpdfsdk/fpdf_doc_embeddertest.cpp
@@ -0,0 +1,593 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/fx_string.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_doc.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFDocEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFDocEmbedderTest, MultipleSamePage) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document());
+
+  std::set<FPDF_PAGE> unique_pages;
+  std::vector<ScopedFPDFPage> owned_pages(4);
+  for (auto& ref : owned_pages) {
+    ref.reset(FPDF_LoadPage(document(), 0));
+    unique_pages.insert(ref.get());
+  }
+#ifdef PDF_ENABLE_XFA
+  EXPECT_EQ(1u, unique_pages.size());
+  EXPECT_EQ(1u, pDoc->GetParsedPageCountForTesting());
+#else   // PDF_ENABLE_XFA
+  EXPECT_EQ(4u, unique_pages.size());
+  EXPECT_EQ(4u, pDoc->GetParsedPageCountForTesting());
+#endif  // PDF_ENABLE_XFA
+}
+
+TEST_F(FPDFDocEmbedderTest, DestGetPageIndex) {
+  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+
+  // NULL argument cases.
+  EXPECT_EQ(-1, FPDFDest_GetDestPageIndex(nullptr, nullptr));
+  EXPECT_EQ(-1, FPDFDest_GetDestPageIndex(document(), nullptr));
+
+  // Page number directly in item from Dests NameTree.
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(1, FPDFDest_GetDestPageIndex(document(), dest));
+
+  // Page number via object reference in item from Dests NameTree.
+  dest = FPDF_GetNamedDestByName(document(), "Next");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(1, FPDFDest_GetDestPageIndex(document(), dest));
+
+  // Page number directly in item from Dests dictionary.
+  dest = FPDF_GetNamedDestByName(document(), "FirstAlternate");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(11, FPDFDest_GetDestPageIndex(document(), dest));
+
+  // Invalid object reference in item from Dests NameTree.
+  dest = FPDF_GetNamedDestByName(document(), "LastAlternate");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(-1, FPDFDest_GetDestPageIndex(document(), dest));
+}
+
+TEST_F(FPDFDocEmbedderTest, DestGetView) {
+  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+
+  unsigned long numParams;
+  FS_FLOAT params[4];
+
+  numParams = 42;
+  std::fill_n(params, 4, 42.4242f);
+  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_UNKNOWN_MODE),
+            FPDFDest_GetView(nullptr, &numParams, params));
+  EXPECT_EQ(0U, numParams);
+  EXPECT_FLOAT_EQ(42.4242f, params[0]);
+
+  numParams = 42;
+  std::fill_n(params, 4, 42.4242f);
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_XYZ),
+            FPDFDest_GetView(dest, &numParams, params));
+  EXPECT_EQ(3U, numParams);
+  EXPECT_FLOAT_EQ(0, params[0]);
+  EXPECT_FLOAT_EQ(0, params[1]);
+  EXPECT_FLOAT_EQ(1, params[2]);
+  EXPECT_FLOAT_EQ(42.4242f, params[3]);
+
+  numParams = 42;
+  std::fill_n(params, 4, 42.4242f);
+  dest = FPDF_GetNamedDestByName(document(), "Next");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_FIT),
+            FPDFDest_GetView(dest, &numParams, params));
+  EXPECT_EQ(0U, numParams);
+  EXPECT_FLOAT_EQ(42.4242f, params[0]);
+
+  numParams = 42;
+  std::fill_n(params, 4, 42.4242f);
+  dest = FPDF_GetNamedDestByName(document(), "FirstAlternate");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_XYZ),
+            FPDFDest_GetView(dest, &numParams, params));
+  EXPECT_EQ(3U, numParams);
+  EXPECT_FLOAT_EQ(200, params[0]);
+  EXPECT_FLOAT_EQ(400, params[1]);
+  EXPECT_FLOAT_EQ(800, params[2]);
+  EXPECT_FLOAT_EQ(42.4242f, params[3]);
+
+  numParams = 42;
+  std::fill_n(params, 4, 42.4242f);
+  dest = FPDF_GetNamedDestByName(document(), "LastAlternate");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_XYZ),
+            FPDFDest_GetView(dest, &numParams, params));
+  EXPECT_EQ(3U, numParams);
+  EXPECT_FLOAT_EQ(0, params[0]);
+  EXPECT_FLOAT_EQ(0, params[1]);
+  EXPECT_FLOAT_EQ(-200, params[2]);
+  EXPECT_FLOAT_EQ(42.4242f, params[3]);
+}
+
+TEST_F(FPDFDocEmbedderTest, DestGetLocationInPage) {
+  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
+  EXPECT_TRUE(dest);
+
+  FPDF_BOOL hasX = 0;
+  FPDF_BOOL hasY = 0;
+  FPDF_BOOL hasZoom = 0;
+  FS_FLOAT x = -1.0f;
+  FS_FLOAT y = -1.0f;
+  FS_FLOAT zoom = -1.0f;
+
+  // NULL argument case
+  EXPECT_FALSE(FPDFDest_GetLocationInPage(nullptr, &hasX, &hasY, &hasZoom, &x,
+                                          &y, &zoom));
+
+  // Actual argument case.
+  EXPECT_TRUE(
+      FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom));
+  EXPECT_TRUE(hasX);
+  EXPECT_TRUE(hasY);
+  EXPECT_TRUE(hasZoom);
+  EXPECT_EQ(0, x);
+  EXPECT_EQ(0, y);
+  EXPECT_EQ(1, zoom);
+}
+
+TEST_F(FPDFDocEmbedderTest, BUG_680376) {
+  EXPECT_TRUE(OpenDocument("bug_680376.pdf"));
+
+  // Page number directly in item from Dests NameTree.
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
+  EXPECT_TRUE(dest);
+  EXPECT_EQ(-1, FPDFDest_GetDestPageIndex(document(), dest));
+}
+
+TEST_F(FPDFDocEmbedderTest, BUG_821454) {
+  EXPECT_TRUE(OpenDocument("bug_821454.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Cover some NULL arg cases while we're at it.
+  EXPECT_FALSE(FPDFLink_GetLinkAtPoint(nullptr, 150, 360));
+  EXPECT_EQ(-1, FPDFLink_GetLinkZOrderAtPoint(nullptr, 150, 360));
+
+  FPDF_LINK link1 = FPDFLink_GetLinkAtPoint(page, 150, 360);
+  ASSERT_TRUE(link1);
+  FPDF_LINK link2 = FPDFLink_GetLinkAtPoint(page, 150, 420);
+  ASSERT_TRUE(link2);
+
+  EXPECT_EQ(0, FPDFLink_GetLinkZOrderAtPoint(page, 150, 360));
+  EXPECT_EQ(1, FPDFLink_GetLinkZOrderAtPoint(page, 150, 420));
+
+  FPDF_DEST dest1 = FPDFLink_GetDest(document(), link1);
+  ASSERT_TRUE(dest1);
+  FPDF_DEST dest2 = FPDFLink_GetDest(document(), link2);
+  ASSERT_TRUE(dest2);
+
+  EXPECT_EQ(0, FPDFDest_GetDestPageIndex(document(), dest1));
+  EXPECT_EQ(0, FPDFDest_GetDestPageIndex(document(), dest2));
+
+  {
+    FPDF_BOOL has_x_coord;
+    FPDF_BOOL has_y_coord;
+    FPDF_BOOL has_zoom;
+    FS_FLOAT x;
+    FS_FLOAT y;
+    FS_FLOAT zoom;
+    FPDF_BOOL success = FPDFDest_GetLocationInPage(
+        dest1, &has_x_coord, &has_y_coord, &has_zoom, &x, &y, &zoom);
+    ASSERT_TRUE(success);
+    EXPECT_TRUE(has_x_coord);
+    EXPECT_TRUE(has_y_coord);
+    EXPECT_FALSE(has_zoom);
+    EXPECT_FLOAT_EQ(100.0f, x);
+    EXPECT_FLOAT_EQ(200.0f, y);
+  }
+  {
+    FPDF_BOOL has_x_coord;
+    FPDF_BOOL has_y_coord;
+    FPDF_BOOL has_zoom;
+    FS_FLOAT x;
+    FS_FLOAT y;
+    FS_FLOAT zoom;
+    FPDF_BOOL success = FPDFDest_GetLocationInPage(
+        dest2, &has_x_coord, &has_y_coord, &has_zoom, &x, &y, &zoom);
+    ASSERT_TRUE(success);
+    EXPECT_TRUE(has_x_coord);
+    EXPECT_TRUE(has_y_coord);
+    EXPECT_FALSE(has_zoom);
+    EXPECT_FLOAT_EQ(150.0f, x);
+    EXPECT_FLOAT_EQ(250.0f, y);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFDocEmbedderTest, ActionBadArguments) {
+  EXPECT_TRUE(OpenDocument("launch_action.pdf"));
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_UNSUPPORTED),
+            FPDFAction_GetType(nullptr));
+
+  EXPECT_EQ(nullptr, FPDFAction_GetDest(nullptr, nullptr));
+  EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), nullptr));
+  EXPECT_EQ(0u, FPDFAction_GetFilePath(nullptr, nullptr, 0));
+  EXPECT_EQ(0u, FPDFAction_GetURIPath(nullptr, nullptr, nullptr, 0));
+  EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), nullptr, nullptr, 0));
+}
+
+TEST_F(FPDFDocEmbedderTest, ActionLaunch) {
+  EXPECT_TRUE(OpenDocument("launch_action.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // The target action is nearly the size of the whole page.
+  FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100);
+  ASSERT_TRUE(link);
+
+  FPDF_ACTION action = FPDFLink_GetAction(link);
+  ASSERT_TRUE(action);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_LAUNCH),
+            FPDFAction_GetType(action));
+
+  const char kExpectedResult[] = "test.pdf";
+  const unsigned long kExpectedLength = sizeof(kExpectedResult);
+  unsigned long bufsize = FPDFAction_GetFilePath(action, nullptr, 0);
+  EXPECT_EQ(kExpectedLength, bufsize);
+
+  char buf[1024];
+  EXPECT_EQ(bufsize, FPDFAction_GetFilePath(action, buf, bufsize));
+  EXPECT_STREQ(kExpectedResult, buf);
+
+  // Other public methods are not appropriate for launch actions.
+  EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), action));
+  EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), action, buf, sizeof(buf)));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFDocEmbedderTest, ActionURI) {
+  EXPECT_TRUE(OpenDocument("uri_action.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // The target action is nearly the size of the whole page.
+  FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100);
+  ASSERT_TRUE(link);
+
+  FPDF_ACTION action = FPDFLink_GetAction(link);
+  ASSERT_TRUE(action);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_URI),
+            FPDFAction_GetType(action));
+
+  const char kExpectedResult[] = "https://example.com/page.html";
+  const unsigned long kExpectedLength = sizeof(kExpectedResult);
+  unsigned long bufsize = FPDFAction_GetURIPath(document(), action, nullptr, 0);
+  ASSERT_EQ(kExpectedLength, bufsize);
+
+  char buf[1024];
+  EXPECT_EQ(bufsize, FPDFAction_GetURIPath(document(), action, buf, bufsize));
+  EXPECT_STREQ(kExpectedResult, buf);
+
+  // Other public methods are not appropriate for URI actions
+  EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), action));
+  EXPECT_EQ(0u, FPDFAction_GetFilePath(action, buf, sizeof(buf)));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFDocEmbedderTest, ActionGoto) {
+  EXPECT_TRUE(OpenDocument("goto_action.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // The target action is nearly the size of the whole page.
+  FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100);
+  ASSERT_TRUE(link);
+
+  FPDF_ACTION action = FPDFLink_GetAction(link);
+  ASSERT_TRUE(action);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_GOTO),
+            FPDFAction_GetType(action));
+
+  EXPECT_TRUE(FPDFAction_GetDest(document(), action));
+
+  // Other public methods are not appropriate for GoTo actions.
+  char buf[1024];
+  EXPECT_EQ(0u, FPDFAction_GetFilePath(action, buf, sizeof(buf)));
+  EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), action, buf, sizeof(buf)));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFDocEmbedderTest, ActionNonesuch) {
+  EXPECT_TRUE(OpenDocument("nonesuch_action.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // The target action is nearly the size of the whole page.
+  FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100);
+  ASSERT_TRUE(link);
+
+  FPDF_ACTION action = FPDFLink_GetAction(link);
+  ASSERT_TRUE(action);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_UNSUPPORTED),
+            FPDFAction_GetType(action));
+
+  // No public methods are appropriate for unsupported actions.
+  char buf[1024];
+  EXPECT_FALSE(FPDFAction_GetDest(document(), action));
+  EXPECT_EQ(0u, FPDFAction_GetFilePath(action, buf, sizeof(buf)));
+  EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), action, buf, sizeof(buf)));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFDocEmbedderTest, NoBookmarks) {
+  unsigned short buf[128];
+
+  // Open a file with no bookmarks.
+  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+
+  // NULL argument cases.
+  EXPECT_EQ(0u, FPDFBookmark_GetTitle(nullptr, buf, sizeof(buf)));
+  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(nullptr, nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(document(), nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(nullptr, nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_Find(nullptr, nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_GetDest(nullptr, nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_GetDest(document(), nullptr));
+  EXPECT_EQ(nullptr, FPDFBookmark_GetAction(nullptr));
+}
+
+TEST_F(FPDFDocEmbedderTest, Bookmarks) {
+  unsigned short buf[128];
+
+  // Open a file with two bookmarks.
+  EXPECT_TRUE(OpenDocument("bookmarks.pdf"));
+
+  FPDF_BOOKMARK child = FPDFBookmark_GetFirstChild(document(), nullptr);
+  EXPECT_TRUE(child);
+  EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16));
+
+  FPDF_DEST dest = FPDFBookmark_GetDest(document(), child);
+  EXPECT_FALSE(dest);  // TODO(tsepez): put real dest into bookmarks.pdf
+
+  FPDF_ACTION action = FPDFBookmark_GetAction(child);
+  EXPECT_FALSE(action);  // TODO(tsepez): put real action into bookmarks.pdf
+
+  FPDF_BOOKMARK grand_child = FPDFBookmark_GetFirstChild(document(), child);
+  EXPECT_FALSE(grand_child);
+
+  FPDF_BOOKMARK sibling = FPDFBookmark_GetNextSibling(document(), child);
+  EXPECT_TRUE(sibling);
+  EXPECT_EQ(28u, FPDFBookmark_GetTitle(sibling, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(L"A Good Ending"), WideString::FromUTF16LE(buf, 13));
+
+  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), sibling));
+}
+
+TEST_F(FPDFDocEmbedderTest, FindBookmarks) {
+  unsigned short buf[128];
+
+  // Open a file with two bookmarks.
+  EXPECT_TRUE(OpenDocument("bookmarks.pdf"));
+
+  // Find the first one, based on its known title.
+  ScopedFPDFWideString title = GetFPDFWideString(L"A Good Beginning");
+  FPDF_BOOKMARK child = FPDFBookmark_Find(document(), title.get());
+  EXPECT_TRUE(child);
+
+  // Check that the string matches.
+  EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16));
+
+  // Check that it is them same as the one returned by GetFirstChild.
+  EXPECT_EQ(child, FPDFBookmark_GetFirstChild(document(), nullptr));
+
+  // Try to find one using a non-existent title.
+  ScopedFPDFWideString bad_title = GetFPDFWideString(L"A BAD Beginning");
+  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), bad_title.get()));
+}
+
+// Check circular bookmarks will not cause infinite loop.
+TEST_F(FPDFDocEmbedderTest, FindBookmarks_bug420) {
+  // Open a file with circular bookmarks.
+  EXPECT_TRUE(OpenDocument("bookmarks_circular.pdf"));
+
+  // Try to find a title.
+  ScopedFPDFWideString title = GetFPDFWideString(L"anything");
+  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), title.get()));
+}
+
+TEST_F(FPDFDocEmbedderTest, DeletePage) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDFPage_Delete(nullptr, 0);
+  EXPECT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDFPage_Delete(document(), -1);
+  EXPECT_EQ(1, FPDF_GetPageCount(document()));
+  FPDFPage_Delete(document(), 1);
+  EXPECT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDFPage_Delete(document(), 0);
+  EXPECT_EQ(0, FPDF_GetPageCount(document()));
+}
+
+TEST_F(FPDFDocEmbedderTest, GetMetaText) {
+  ASSERT_TRUE(OpenDocument("bug_601362.pdf"));
+
+  // Invalid document / tag results in 0.
+  unsigned short buf[128];
+  EXPECT_EQ(0u, FPDF_GetMetaText(document(), nullptr, buf, sizeof(buf)));
+  EXPECT_EQ(0u, FPDF_GetMetaText(nullptr, "", buf, sizeof(buf)));
+
+  // Tags that do not eixst results in an empty wide string.
+  EXPECT_EQ(2u, FPDF_GetMetaText(document(), "", buf, sizeof(buf)));
+  EXPECT_EQ(2u, FPDF_GetMetaText(document(), "foo", buf, sizeof(buf)));
+  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Title", buf, sizeof(buf)));
+  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Author", buf, sizeof(buf)));
+  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Subject", buf, sizeof(buf)));
+  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Keywords", buf, sizeof(buf)));
+  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Producer", buf, sizeof(buf)));
+
+  constexpr wchar_t kExpectedCreator[] = L"Microsoft Word";
+  ASSERT_EQ(30u, FPDF_GetMetaText(document(), "Creator", buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedCreator),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreator)));
+
+  constexpr wchar_t kExpectedCreationDate[] = L"D:20160411190039+00'00'";
+  ASSERT_EQ(48u,
+            FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedCreationDate),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreationDate)));
+
+  constexpr wchar_t kExpectedModDate[] = L"D:20160411190039+00'00'";
+  ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedModDate),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
+}
+
+TEST_F(FPDFDocEmbedderTest, Bug_182) {
+  ASSERT_TRUE(OpenDocument("bug_182.pdf"));
+
+  unsigned short buf[128];
+  constexpr wchar_t kExpectedTitle[] = L"Super Visual Formade 印刷";
+
+  ASSERT_EQ(48u, FPDF_GetMetaText(document(), "Title", buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedTitle),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedTitle)));
+}
+
+TEST_F(FPDFDocEmbedderTest, GetMetaTextSameObjectNumber) {
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+
+  // The PDF has been edited. It has two %%EOF markers, and 2 objects numbered
+  // (1 0). Both objects are /Info dictionaries, but contain different data.
+  // Make sure ModDate is the date of the last modification.
+  unsigned short buf[128];
+  constexpr wchar_t kExpectedModDate[] = L"D:20170612232940-04'00'";
+  ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedModDate),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
+}
+
+TEST_F(FPDFDocEmbedderTest, GetMetaTextInAttachmentFile) {
+  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
+
+  // Make sure this is the date from the PDF itself and not the attached PDF.
+  unsigned short buf[128];
+  constexpr wchar_t kExpectedModDate[] = L"D:20170712214448-07'00'";
+  ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedModDate),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
+}
+
+TEST_F(FPDFDocEmbedderTest, GetMetaTextFromNewDocument) {
+  FPDF_DOCUMENT empty_doc = FPDF_CreateNewDocument();
+  unsigned short buf[128];
+  EXPECT_EQ(2u, FPDF_GetMetaText(empty_doc, "Title", buf, sizeof(buf)));
+  FPDF_CloseDocument(empty_doc);
+}
+
+TEST_F(FPDFDocEmbedderTest, NoPageLabels) {
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  EXPECT_EQ(1, FPDF_GetPageCount(document()));
+
+  ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 0, nullptr, 0));
+}
+
+TEST_F(FPDFDocEmbedderTest, GetPageLabels) {
+  EXPECT_TRUE(OpenDocument("page_labels.pdf"));
+  EXPECT_EQ(7, FPDF_GetPageCount(document()));
+
+  // We do not request labels, when use FPDFAvail_IsXXXAvail.
+  // Flush all data, to allow read labels.
+  SetWholeFileAvailable();
+
+  unsigned short buf[128];
+  EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -2, buf, sizeof(buf)));
+  EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -1, buf, sizeof(buf)));
+
+  const wchar_t kExpectedPageLabel0[] = L"i";
+  ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 0, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedPageLabel0),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel0)));
+
+  const wchar_t kExpectedPageLabel1[] = L"ii";
+  ASSERT_EQ(6u, FPDF_GetPageLabel(document(), 1, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedPageLabel1),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel1)));
+
+  const wchar_t kExpectedPageLabel2[] = L"1";
+  ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 2, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedPageLabel2),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel2)));
+
+  const wchar_t kExpectedPageLabel3[] = L"2";
+  ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 3, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedPageLabel3),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel3)));
+
+  const wchar_t kExpectedPageLabel4[] = L"zzA";
+  ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 4, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedPageLabel4),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel4)));
+
+  const wchar_t kExpectedPageLabel5[] = L"zzB";
+  ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 5, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedPageLabel5),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel5)));
+
+  const wchar_t kExpectedPageLabel6[] = L"";
+  ASSERT_EQ(2u, FPDF_GetPageLabel(document(), 6, buf, sizeof(buf)));
+  EXPECT_EQ(WideString(kExpectedPageLabel6),
+            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel6)));
+
+  ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 7, buf, sizeof(buf)));
+  ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 8, buf, sizeof(buf)));
+}
+
+#ifdef PDF_ENABLE_XFA
+TEST_F(FPDFDocEmbedderTest, GetXFALinks) {
+  EXPECT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  ScopedFPDFPage page(FPDF_LoadPage(document(), 0));
+  ASSERT_TRUE(page);
+
+  FPDFLink_GetLinkAtPoint(page.get(), 150, 360);
+  FPDFLink_GetLinkAtPoint(page.get(), 150, 420);
+
+  // Test passes if it doesn't crash. See https://crbug.com/840922
+}
+#endif  // PDF_ENABLE_XFA
diff --git a/fpdfsdk/fpdf_doc_unittest.cpp b/fpdfsdk/fpdf_doc_unittest.cpp
new file mode 100644
index 0000000..2beadd4
--- /dev/null
+++ b/fpdfsdk/fpdf_doc_unittest.cpp
@@ -0,0 +1,257 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_doc.h"
+
+#include <memory>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_null.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#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/fpdfdoc/cpdf_dest.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+class CPDF_TestDocument final : public CPDF_Document {
+ public:
+  CPDF_TestDocument()
+      : CPDF_Document(pdfium::MakeUnique<CPDF_DocRenderData>(),
+                      pdfium::MakeUnique<CPDF_DocPageData>()) {}
+
+  void SetRoot(CPDF_Dictionary* root) { m_pRootDict.Reset(root); }
+  CPDF_IndirectObjectHolder* GetHolder() { return this; }
+};
+
+class PDFDocTest : public testing::Test {
+ public:
+  struct DictObjInfo {
+    uint32_t num;
+    CPDF_Dictionary* obj;
+  };
+
+  void SetUp() override {
+    CPDF_PageModule::Create();
+    auto pTestDoc = pdfium::MakeUnique<CPDF_TestDocument>();
+    m_pIndirectObjs = pTestDoc->GetHolder();
+    m_pRootObj.Reset(m_pIndirectObjs->NewIndirect<CPDF_Dictionary>());
+    pTestDoc->SetRoot(m_pRootObj.Get());
+    m_pDoc.reset(FPDFDocumentFromCPDFDocument(pTestDoc.release()));
+  }
+
+  void TearDown() override {
+    m_pRootObj = nullptr;
+    m_pIndirectObjs = nullptr;
+    m_pDoc.reset();
+    CPDF_PageModule::Destroy();
+  }
+
+  std::vector<DictObjInfo> CreateDictObjs(int num) {
+    std::vector<DictObjInfo> info;
+    for (int i = 0; i < num; ++i) {
+      // Objects created will be released by the document.
+      CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
+      info.push_back({obj->GetObjNum(), obj});
+    }
+    return info;
+  }
+
+ protected:
+  ScopedFPDFDocument m_pDoc;
+  UnownedPtr<CPDF_IndirectObjectHolder> m_pIndirectObjs;
+  RetainPtr<CPDF_Dictionary> m_pRootObj;
+};
+
+TEST_F(PDFDocTest, FindBookmark) {
+  {
+    // No bookmark information.
+    ScopedFPDFWideString title = GetFPDFWideString(L"");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+
+    title = GetFPDFWideString(L"Preface");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+  }
+  {
+    // Empty bookmark tree.
+    m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
+    ScopedFPDFWideString title = GetFPDFWideString(L"");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+
+    title = GetFPDFWideString(L"Preface");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+  }
+  {
+    // Check on a regular bookmark tree.
+    auto bookmarks = CreateDictObjs(3);
+
+    bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+                                                bookmarks[0].num);
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+                                                bookmarks[2].num);
+
+    bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+                                                bookmarks[0].num);
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs.Get(),
+                                                bookmarks[1].num);
+
+    bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
+    bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+                                                bookmarks[1].num);
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
+                                                bookmarks[2].num);
+
+    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
+                                          bookmarks[0].num);
+
+    // Title with no match.
+    ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+
+    // Title with partial match only.
+    title = GetFPDFWideString(L"Chapter");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+
+    // Title with a match.
+    title = GetFPDFWideString(L"Chapter 2");
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj),
+              FPDFBookmark_Find(m_pDoc.get(), title.get()));
+
+    // Title match is case insensitive.
+    title = GetFPDFWideString(L"cHaPter 2");
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj),
+              FPDFBookmark_Find(m_pDoc.get(), title.get()));
+  }
+  {
+    // Circular bookmarks in depth.
+    auto bookmarks = CreateDictObjs(3);
+
+    bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+                                                bookmarks[0].num);
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+                                                bookmarks[2].num);
+
+    bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+                                                bookmarks[1].num);
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+                                                bookmarks[1].num);
+
+    bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
+    bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+                                                bookmarks[1].num);
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
+                                                bookmarks[2].num);
+
+    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
+                                          bookmarks[0].num);
+
+    // Title with no match.
+    ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+
+    // Title with a match.
+    title = GetFPDFWideString(L"Chapter 2");
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj),
+              FPDFBookmark_Find(m_pDoc.get(), title.get()));
+  }
+  {
+    // Circular bookmarks in breadth.
+    auto bookmarks = CreateDictObjs(4);
+
+    bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+                                                bookmarks[0].num);
+    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+                                                bookmarks[2].num);
+
+    bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+                                                bookmarks[0].num);
+    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+                                                bookmarks[3].num);
+
+    bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
+    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
+                                                bookmarks[0].num);
+    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
+                                                bookmarks[1].num);
+
+    bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
+    bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
+                                                bookmarks[1].num);
+    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
+                                                bookmarks[2].num);
+
+    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
+                                          bookmarks[0].num);
+
+    // Title with no match.
+    ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 8");
+    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
+
+    // Title with a match.
+    title = GetFPDFWideString(L"Chapter 3");
+    EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[3].obj),
+              FPDFBookmark_Find(m_pDoc.get(), title.get()));
+  }
+}
+
+TEST_F(PDFDocTest, GetLocationInPage) {
+  auto array = pdfium::MakeRetain<CPDF_Array>();
+  array->AddNew<CPDF_Number>(0);  // Page Index.
+  array->AddNew<CPDF_Name>("XYZ");
+  array->AddNew<CPDF_Number>(4);  // X
+  array->AddNew<CPDF_Number>(5);  // Y
+  array->AddNew<CPDF_Number>(6);  // Zoom.
+
+  FPDF_BOOL hasX;
+  FPDF_BOOL hasY;
+  FPDF_BOOL hasZoom;
+  FS_FLOAT x;
+  FS_FLOAT y;
+  FS_FLOAT zoom;
+
+  EXPECT_TRUE(FPDFDest_GetLocationInPage(FPDFDestFromCPDFArray(array.Get()),
+                                         &hasX, &hasY, &hasZoom, &x, &y,
+                                         &zoom));
+  EXPECT_TRUE(hasX);
+  EXPECT_TRUE(hasY);
+  EXPECT_TRUE(hasZoom);
+  EXPECT_EQ(4, x);
+  EXPECT_EQ(5, y);
+  EXPECT_EQ(6, zoom);
+
+  array->SetNewAt<CPDF_Null>(2);
+  array->SetNewAt<CPDF_Null>(3);
+  array->SetNewAt<CPDF_Null>(4);
+  EXPECT_TRUE(FPDFDest_GetLocationInPage(FPDFDestFromCPDFArray(array.Get()),
+                                         &hasX, &hasY, &hasZoom, &x, &y,
+                                         &zoom));
+  EXPECT_FALSE(hasX);
+  EXPECT_FALSE(hasY);
+  EXPECT_FALSE(hasZoom);
+
+  array = pdfium::MakeRetain<CPDF_Array>();
+  EXPECT_FALSE(FPDFDest_GetLocationInPage(FPDFDestFromCPDFArray(array.Get()),
+                                          &hasX, &hasY, &hasZoom, &x, &y,
+                                          &zoom));
+}
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
new file mode 100644
index 0000000..b6cc84c
--- /dev/null
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -0,0 +1,3494 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/cpdf_formobject.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageobject.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.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/fxcrt/fx_system.h"
+#include "core/fxge/fx_font.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_annot.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/hash.h"
+
+class FPDFEditEmbedderTest : public EmbedderTest {
+ protected:
+  FPDF_DOCUMENT CreateNewDocument() {
+    document_ = FPDF_CreateNewDocument();
+    cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_);
+    return document_;
+  }
+
+  void CheckFontDescriptor(const CPDF_Dictionary* font_dict,
+                           int font_type,
+                           bool bold,
+                           bool italic,
+                           pdfium::span<const uint8_t> span) {
+    const CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor");
+    ASSERT_TRUE(font_desc);
+    EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type"));
+    EXPECT_EQ(font_dict->GetStringFor("BaseFont"),
+              font_desc->GetStringFor("FontName"));
+
+    // Check that the font descriptor has the required keys according to spec
+    // 1.7 Table 5.19
+    ASSERT_TRUE(font_desc->KeyExist("Flags"));
+
+    int font_flags = font_desc->GetIntegerFor("Flags");
+    EXPECT_EQ(bold, FontStyleIsForceBold(font_flags));
+    EXPECT_EQ(italic, FontStyleIsItalic(font_flags));
+    EXPECT_TRUE(FontStyleIsNonSymbolic(font_flags));
+    ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
+
+    const CPDF_Array* fontBBox = font_desc->GetArrayFor("FontBBox");
+    ASSERT_TRUE(fontBBox);
+    EXPECT_EQ(4u, fontBBox->size());
+    // Check that the coordinates are in the preferred order according to spec
+    // 1.7 Section 3.8.4
+    EXPECT_TRUE(fontBBox->GetIntegerAt(0) < fontBBox->GetIntegerAt(2));
+    EXPECT_TRUE(fontBBox->GetIntegerAt(1) < fontBBox->GetIntegerAt(3));
+
+    EXPECT_TRUE(font_desc->KeyExist("ItalicAngle"));
+    EXPECT_TRUE(font_desc->KeyExist("Ascent"));
+    EXPECT_TRUE(font_desc->KeyExist("Descent"));
+    EXPECT_TRUE(font_desc->KeyExist("CapHeight"));
+    EXPECT_TRUE(font_desc->KeyExist("StemV"));
+    ByteString present("FontFile");
+    ByteString absent("FontFile2");
+    if (font_type == FPDF_FONT_TRUETYPE)
+      std::swap(present, absent);
+    EXPECT_TRUE(font_desc->KeyExist(present));
+    EXPECT_FALSE(font_desc->KeyExist(absent));
+
+    auto streamAcc =
+        pdfium::MakeRetain<CPDF_StreamAcc>(font_desc->GetStreamFor(present));
+    streamAcc->LoadAllDataRaw();
+
+    // Check that the font stream is the one that was provided
+    ASSERT_EQ(span.size(), streamAcc->GetSize());
+    if (font_type == FPDF_FONT_TRUETYPE) {
+      ASSERT_EQ(static_cast<int>(span.size()),
+                streamAcc->GetDict()->GetIntegerFor("Length1"));
+    }
+
+    const uint8_t* stream_data = streamAcc->GetData();
+    for (size_t j = 0; j < span.size(); j++)
+      EXPECT_EQ(span[j], stream_data[j]) << " at byte " << j;
+  }
+
+  void CheckCompositeFontWidths(const CPDF_Array* widths_array,
+                                CPDF_Font* typed_font) {
+    // Check that W array is in a format that conforms to PDF spec 1.7 section
+    // "Glyph Metrics in CIDFonts" (these checks are not
+    // implementation-specific).
+    EXPECT_GT(widths_array->size(), 1u);
+    int num_cids_checked = 0;
+    int cur_cid = 0;
+    for (size_t idx = 0; idx < widths_array->size(); idx++) {
+      int cid = widths_array->GetNumberAt(idx);
+      EXPECT_GE(cid, cur_cid);
+      ASSERT_FALSE(++idx == widths_array->size());
+      const CPDF_Object* next = widths_array->GetObjectAt(idx);
+      if (next->IsArray()) {
+        // We are in the c [w1 w2 ...] case
+        const CPDF_Array* arr = next->AsArray();
+        int cnt = static_cast<int>(arr->size());
+        size_t inner_idx = 0;
+        for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
+          uint32_t width = arr->GetNumberAt(inner_idx++);
+          EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
+              << " at cid " << cur_cid;
+        }
+        num_cids_checked += cnt;
+        continue;
+      }
+      // Otherwise, are in the c_first c_last w case.
+      ASSERT_TRUE(next->IsNumber());
+      int last_cid = next->AsNumber()->GetInteger();
+      ASSERT_FALSE(++idx == widths_array->size());
+      uint32_t width = widths_array->GetNumberAt(idx);
+      for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
+        EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
+            << " at cid " << cur_cid;
+      }
+      num_cids_checked += last_cid - cid + 1;
+    }
+    // Make sure we have a good amount of cids described
+    EXPECT_GT(num_cids_checked, 900);
+  }
+  CPDF_Document* cpdf_doc() { return cpdf_doc_; }
+
+ private:
+  CPDF_Document* cpdf_doc_;
+};
+
+namespace {
+
+const char kExpectedPDF[] =
+    "%PDF-1.7\r\n"
+    "%\xA1\xB3\xC5\xD7\r\n"
+    "1 0 obj\r\n"
+    "<</Pages 2 0 R /Type/Catalog>>\r\n"
+    "endobj\r\n"
+    "2 0 obj\r\n"
+    "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
+    "endobj\r\n"
+    "3 0 obj\r\n"
+    "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
+    "endobj\r\n"
+    "4 0 obj\r\n"
+    "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
+    "/Resources<</ExtGState<</FXE1 5 0 R >>>>"
+    "/Rotate 0/Type/Page"
+    ">>\r\n"
+    "endobj\r\n"
+    "5 0 obj\r\n"
+    "<</BM/Normal/CA 1/ca 1>>\r\n"
+    "endobj\r\n"
+    "xref\r\n"
+    "0 6\r\n"
+    "0000000000 65535 f\r\n"
+    "0000000017 00000 n\r\n"
+    "0000000066 00000 n\r\n"
+    "0000000122 00000 n\r\n"
+    "0000000192 00000 n\r\n"
+    "0000000311 00000 n\r\n"
+    "trailer\r\n"
+    "<<\r\n"
+    "/Root 1 0 R\r\n"
+    "/Info 3 0 R\r\n"
+    "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
+    "startxref\r\n"
+    "354\r\n"
+    "%%EOF\r\n";
+
+}  // namespace
+
+TEST_F(FPDFEditEmbedderTest, EmptyCreation) {
+  EXPECT_TRUE(CreateEmptyDocument());
+  FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
+  EXPECT_NE(nullptr, page);
+  // The FPDFPage_GenerateContent call should do nothing.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  EXPECT_THAT(GetString(), testing::MatchesRegex(std::string(
+                               kExpectedPDF, sizeof(kExpectedPDF))));
+  FPDF_ClosePage(page);
+}
+
+// Regression test for https://crbug.com/667012
+TEST_F(FPDFEditEmbedderTest, RasterizePDF) {
+  const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
+
+  // Get the bitmap for the original document.
+  ScopedFPDFBitmap orig_bitmap;
+  {
+    EXPECT_TRUE(OpenDocument("black.pdf"));
+    FPDF_PAGE orig_page = LoadPage(0);
+    ASSERT_TRUE(orig_page);
+    orig_bitmap = RenderLoadedPage(orig_page);
+    CompareBitmap(orig_bitmap.get(), 612, 792, kAllBlackMd5sum);
+    UnloadPage(orig_page);
+  }
+
+  // Create a new document from |orig_bitmap| and save it.
+  {
+    FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
+    FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
+
+    // Add the bitmap to an image object and add the image object to the output
+    // page.
+    FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
+    EXPECT_TRUE(
+        FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap.get()));
+    EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
+    FPDFPage_InsertObject(temp_page, temp_img);
+    EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
+    EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
+    FPDF_ClosePage(temp_page);
+    FPDF_CloseDocument(temp_doc);
+  }
+
+  // Get the generated content. Make sure it is at least as big as the original
+  // PDF.
+  EXPECT_GT(GetString().size(), 923u);
+  VerifySavedDocument(612, 792, kAllBlackMd5sum);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddPaths DISABLED_AddPaths
+#else
+#define MAYBE_AddPaths AddPaths
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) {
+  // Start with a blank page
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+  ASSERT_TRUE(page);
+
+  // We will first add a red rectangle
+  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
+  ASSERT_TRUE(red_rect);
+  // Expect false when trying to set colors out of range
+  EXPECT_FALSE(FPDFPageObj_SetStrokeColor(red_rect, 100, 100, 100, 300));
+  EXPECT_FALSE(FPDFPageObj_SetFillColor(red_rect, 200, 256, 200, 0));
+
+  // Fill rectangle with red and insert to the page
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
+
+  int fillmode = FPDF_FILLMODE_NONE;
+  FPDF_BOOL stroke = true;
+  EXPECT_TRUE(FPDFPath_GetDrawMode(red_rect, &fillmode, &stroke));
+  EXPECT_EQ(FPDF_FILLMODE_ALTERNATE, fillmode);
+  EXPECT_FALSE(stroke);
+
+  static const FS_MATRIX kMatrix = {1, 2, 3, 4, 5, 6};
+  EXPECT_FALSE(FPDFPath_SetMatrix(nullptr, &kMatrix));
+  EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &kMatrix));
+
+  FS_MATRIX matrix;
+  EXPECT_FALSE(FPDFPath_GetMatrix(nullptr, &matrix));
+  EXPECT_TRUE(FPDFPath_GetMatrix(red_rect, &matrix));
+  EXPECT_FLOAT_EQ(1.0f, matrix.a);
+  EXPECT_FLOAT_EQ(2.0f, matrix.b);
+  EXPECT_FLOAT_EQ(3.0f, matrix.c);
+  EXPECT_FLOAT_EQ(4.0f, matrix.d);
+  EXPECT_FLOAT_EQ(5.0f, matrix.e);
+  EXPECT_FLOAT_EQ(6.0f, matrix.f);
+
+  // Set back the identity matrix.
+  matrix = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
+  EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &matrix));
+
+  FPDFPage_InsertObject(page, red_rect);
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "66d02eaa6181e2c069ce2ea99beda497");
+  }
+
+  // Now add to that a green rectangle with some medium alpha
+  FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect, 0, 255, 0, 128));
+
+  // Make sure the type of the rectangle is a path.
+  EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(green_rect));
+
+  // Make sure we get back the same color we set previously.
+  unsigned int R;
+  unsigned int G;
+  unsigned int B;
+  unsigned int A;
+  EXPECT_TRUE(FPDFPageObj_GetFillColor(green_rect, &R, &G, &B, &A));
+  EXPECT_EQ(0u, R);
+  EXPECT_EQ(255u, G);
+  EXPECT_EQ(0u, B);
+  EXPECT_EQ(128u, A);
+
+  // Make sure the path has 5 points (1 FXPT_TYPE::MoveTo and 4
+  // FXPT_TYPE::LineTo).
+  ASSERT_EQ(5, FPDFPath_CountSegments(green_rect));
+  // Verify actual coordinates.
+  FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0);
+  float x;
+  float y;
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(100, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 1);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(100, x);
+  EXPECT_EQ(140, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 2);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(140, x);
+  EXPECT_EQ(140, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 3);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(140, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 4);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(100, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
+
+  EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
+  FPDFPage_InsertObject(page, green_rect);
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "7b0b87604594e773add528fae567a558");
+  }
+
+  // Add a black triangle.
+  FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(black_path, 0, 0, 0, 200));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
+  EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
+  EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
+  EXPECT_TRUE(FPDFPath_Close(black_path));
+
+  // Make sure the path has 3 points (1 FXPT_TYPE::MoveTo and 2
+  // FXPT_TYPE::LineTo).
+  ASSERT_EQ(3, FPDFPath_CountSegments(black_path));
+  // Verify actual coordinates.
+  segment = FPDFPath_GetPathSegment(black_path, 0);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(400, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(black_path, 1);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(400, x);
+  EXPECT_EQ(200, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(black_path, 2);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(300, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
+  // Make sure out of bounds index access fails properly.
+  EXPECT_EQ(nullptr, FPDFPath_GetPathSegment(black_path, 3));
+
+  FPDFPage_InsertObject(page, black_path);
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "eadc8020a14dfcf091da2688733d8806");
+  }
+
+  // Now add a more complex blue path.
+  FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(blue_path, 0, 0, 255, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
+  EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
+  EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
+  EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
+  EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
+  EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
+  EXPECT_TRUE(FPDFPath_Close(blue_path));
+  FPDFPage_InsertObject(page, blue_path);
+  const char kLastMD5[] = "9823e1a21bd9b72b6a442ba4f12af946";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792, kLastMD5);
+  }
+
+  // Now save the result, closing the page and document
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  FPDF_ClosePage(page);
+
+  // Render the saved result
+  VerifySavedDocument(612, 792, kLastMD5);
+}
+
+TEST_F(FPDFEditEmbedderTest, ClipPath) {
+  // Load document with a clipped rectangle.
+  EXPECT_TRUE(OpenDocument("clip_path.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT triangle = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(triangle);
+
+  // Test that we got the expected triangle.
+  ASSERT_EQ(4, FPDFPath_CountSegments(triangle));
+
+  FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(triangle, 0);
+  float x;
+  float y;
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(10, x);
+  EXPECT_EQ(10, y);
+  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  segment = FPDFPath_GetPathSegment(triangle, 1);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(25, x);
+  EXPECT_EQ(40, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  segment = FPDFPath_GetPathSegment(triangle, 2);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(40, x);
+  EXPECT_EQ(10, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  segment = FPDFPath_GetPathSegment(triangle, 3);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
+
+  // Test FPDFPageObj_GetClipPath().
+  ASSERT_EQ(nullptr, FPDFPageObj_GetClipPath(nullptr));
+
+  FPDF_CLIPPATH clip_path = FPDFPageObj_GetClipPath(triangle);
+  ASSERT_TRUE(clip_path);
+
+  // Test FPDFClipPath_CountPaths().
+  ASSERT_EQ(-1, FPDFClipPath_CountPaths(nullptr));
+  ASSERT_EQ(1, FPDFClipPath_CountPaths(clip_path));
+
+  // Test FPDFClipPath_CountPathSegments().
+  ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(nullptr, 0));
+  ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, -1));
+  ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, 1));
+  ASSERT_EQ(4, FPDFClipPath_CountPathSegments(clip_path, 0));
+
+  // FPDFClipPath_GetPathSegment() negative testing.
+  ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(nullptr, 0, 0));
+  ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, -1, 0));
+  ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 1, 0));
+  ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, -1));
+  ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, 4));
+
+  // FPDFClipPath_GetPathSegment() positive testing.
+  segment = FPDFClipPath_GetPathSegment(clip_path, 0, 0);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(10, x);
+  EXPECT_EQ(15, y);
+  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  segment = FPDFClipPath_GetPathSegment(clip_path, 0, 1);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(40, x);
+  EXPECT_EQ(15, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  segment = FPDFClipPath_GetPathSegment(clip_path, 0, 2);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(40, x);
+  EXPECT_EQ(35, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  segment = FPDFClipPath_GetPathSegment(clip_path, 0, 3);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(10, x);
+  EXPECT_EQ(35, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, BUG_1399) {
+  // Load document with a clipped rectangle.
+  EXPECT_TRUE(OpenDocument("bug_1399.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_EQ(7, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(obj);
+
+  ASSERT_EQ(2, FPDFPath_CountSegments(obj));
+
+  FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(obj, 0);
+  float x;
+  float y;
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_FLOAT_EQ(107.718f, x);
+  EXPECT_FLOAT_EQ(719.922f, y);
+  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  segment = FPDFPath_GetPathSegment(obj, 1);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_FLOAT_EQ(394.718f, x);
+  EXPECT_FLOAT_EQ(719.922f, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+  FPDF_CLIPPATH clip_path = FPDFPageObj_GetClipPath(obj);
+  ASSERT_TRUE(clip_path);
+
+  EXPECT_EQ(-1, FPDFClipPath_CountPaths(clip_path));
+  EXPECT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, 0));
+  EXPECT_FALSE(FPDFClipPath_GetPathSegment(clip_path, 0, 0));
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_SetText DISABLED_SetText
+#else
+#define MAYBE_SetText SetText
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_SetText) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Get the "Hello, world!" text object and change it.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  ScopedFPDFWideString text1 = GetFPDFWideString(L"Changed for SetText test");
+  EXPECT_TRUE(FPDFText_SetText(page_object, text1.get()));
+
+  // Verify the "Hello, world!" text is gone and "Changed for SetText test" is
+  // now displayed.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+#if defined(OS_MACOSX)
+  const char kChangedMD5[] = "94c1e7a5af7dd9d77dc2223b1091acb7";
+#elif defined(OS_WIN)
+  const char kChangedMD5[] = "3137fdb27962671f5c3963a5e965eff5";
+#else
+  const char kChangedMD5[] = "a0c4ea6620772991f66bf7130379b08a";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5);
+  }
+
+  // Now save the result.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  UnloadPage(page);
+
+  // Re-open the file and check the changes were kept in the saved .pdf.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemovePageObject DISABLED_RemovePageObject
+#else
+#define MAYBE_RemovePageObject RemovePageObject
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_RemovePageObject) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Show what the original file looks like.
+  {
+#if defined(OS_MACOSX)
+    const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
+#elif defined(OS_WIN)
+    const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
+#else
+    const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
+#endif
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+  }
+
+  // Get the "Hello, world!" text object and remove it.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+
+  // Verify the "Hello, world!" text is gone.
+  {
+#if defined(OS_MACOSX)
+    const char kRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa";
+#elif defined(OS_WIN)
+    const char kRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb";
+#else
+    const char kRemovedMD5[] = "b76df015fe88009c3c342395df96abf1";
+#endif
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kRemovedMD5);
+  }
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+  UnloadPage(page);
+  FPDFPageObj_Destroy(page_object);
+}
+
+void CheckMarkCounts(FPDF_PAGE page,
+                     int start_from,
+                     int expected_object_count,
+                     size_t expected_prime_count,
+                     size_t expected_square_count,
+                     size_t expected_greater_than_ten_count,
+                     size_t expected_bounds_count) {
+  int object_count = FPDFPage_CountObjects(page);
+  ASSERT_EQ(expected_object_count, object_count);
+
+  size_t prime_count = 0;
+  size_t square_count = 0;
+  size_t greater_than_ten_count = 0;
+  size_t bounds_count = 0;
+  for (int i = 0; i < object_count; ++i) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+
+    int mark_count = FPDFPageObj_CountMarks(page_object);
+    for (int j = 0; j < mark_count; ++j) {
+      FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
+
+      char buffer[256];
+      unsigned long name_len = 999u;
+      ASSERT_TRUE(
+          FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+      EXPECT_GT(name_len, 0u);
+      EXPECT_NE(999u, name_len);
+      std::wstring name =
+          GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+      if (name == L"Prime") {
+        prime_count++;
+      } else if (name == L"Square") {
+        square_count++;
+        int expected_square = start_from + i;
+        EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
+
+        unsigned long get_param_key_return = 999u;
+        ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer),
+                                                &get_param_key_return));
+        EXPECT_EQ((6u + 1u) * 2u, get_param_key_return);
+        std::wstring key =
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+        EXPECT_EQ(L"Factor", key);
+
+        EXPECT_EQ(FPDF_OBJECT_NUMBER,
+                  FPDFPageObjMark_GetParamValueType(mark, "Factor"));
+        int square_root;
+        EXPECT_TRUE(
+            FPDFPageObjMark_GetParamIntValue(mark, "Factor", &square_root));
+        EXPECT_EQ(expected_square, square_root * square_root);
+      } else if (name == L"GreaterThanTen") {
+        greater_than_ten_count++;
+      } else if (name == L"Bounds") {
+        bounds_count++;
+        EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
+
+        unsigned long get_param_key_return = 999u;
+        ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer),
+                                                &get_param_key_return));
+        EXPECT_EQ((8u + 1u) * 2u, get_param_key_return);
+        std::wstring key =
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+        EXPECT_EQ(L"Position", key);
+
+        EXPECT_EQ(FPDF_OBJECT_STRING,
+                  FPDFPageObjMark_GetParamValueType(mark, "Position"));
+        unsigned long length;
+        EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
+            mark, "Position", buffer, sizeof(buffer), &length));
+        ASSERT_GT(length, 0u);
+        std::wstring value =
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+
+        // "Position" can be "First", "Last", or "End".
+        if (i == 0) {
+          EXPECT_EQ((5u + 1u) * 2u, length);
+          EXPECT_EQ(L"First", value);
+        } else if (i == object_count - 1) {
+          if (length == (4u + 1u) * 2u) {
+            EXPECT_EQ(L"Last", value);
+          } else if (length == (3u + 1u) * 2u) {
+            EXPECT_EQ(L"End", value);
+          } else {
+            FAIL();
+          }
+        } else {
+          FAIL();
+        }
+      } else {
+        FAIL();
+      }
+    }
+  }
+
+  // Expect certain number of tagged objects. The test file contains strings
+  // from 1 to 19.
+  EXPECT_EQ(expected_prime_count, prime_count);
+  EXPECT_EQ(expected_square_count, square_count);
+  EXPECT_EQ(expected_greater_than_ten_count, greater_than_ten_count);
+  EXPECT_EQ(expected_bounds_count, bounds_count);
+}
+
+TEST_F(FPDFEditEmbedderTest, ReadMarkedObjectsIndirectDict) {
+  // Load document with some text marked with an indirect property.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemoveMarkedObjectsPrime DISABLED_RemoveMarkedObjectsPrime
+#else
+#define MAYBE_RemoveMarkedObjectsPrime RemoveMarkedObjectsPrime
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveMarkedObjectsPrime) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Show what the original file looks like.
+  {
+#if defined(OS_MACOSX)
+    const char kOriginalMD5[] = "5a5eb63cb21cc15084fea1f14284b8df";
+#elif defined(OS_WIN)
+    const char kOriginalMD5[] = "00542ee435b37749c4453be63bf7bdb6";
+#else
+    const char kOriginalMD5[] = "41647268d5911d049801803b15c2dfb0";
+#endif
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+  }
+
+  constexpr int expected_object_count = 19;
+  CheckMarkCounts(page, 1, expected_object_count, 8, 4, 9, 1);
+
+  // Get all objects marked with "Prime"
+  std::vector<FPDF_PAGEOBJECT> primes;
+  for (int i = 0; i < expected_object_count; ++i) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+
+    int mark_count = FPDFPageObj_CountMarks(page_object);
+    for (int j = 0; j < mark_count; ++j) {
+      FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
+
+      char buffer[256];
+      unsigned long name_len = 999u;
+      ASSERT_TRUE(
+          FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+      EXPECT_GT(name_len, 0u);
+      EXPECT_NE(999u, name_len);
+      std::wstring name =
+          GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+      if (name == L"Prime") {
+        primes.push_back(page_object);
+      }
+    }
+  }
+
+  // Remove all objects marked with "Prime".
+  for (FPDF_PAGEOBJECT page_object : primes) {
+    EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+    FPDFPageObj_Destroy(page_object);
+  }
+
+  EXPECT_EQ(11, FPDFPage_CountObjects(page));
+
+#if defined(OS_MACOSX)
+  const char kNonPrimesMD5[] = "57e76dc7375d896704f0fd6d6d1b9e65";
+  const char kNonPrimesAfterSaveMD5[] = "6304512d0150bbd5578e8e22d3121103";
+#elif defined(OS_WIN)
+  const char kNonPrimesMD5[] = "86e371fdae30c2471f476631f3f93413";
+  const char kNonPrimesAfterSaveMD5[] = "86e371fdae30c2471f476631f3f93413";
+#else
+  const char kNonPrimesMD5[] = "67ab13115d0cc34e99a1003c28047b40";
+  const char kNonPrimesAfterSaveMD5[] = "67ab13115d0cc34e99a1003c28047b40";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesMD5);
+  }
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the prime marks are not there anymore.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  EXPECT_EQ(11, FPDFPage_CountObjects(saved_page));
+
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesAfterSaveMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, RemoveMarks) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr int kExpectedObjectCount = 19;
+  CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
+
+  // Remove all "Prime" content marks.
+  for (int i = 0; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+
+    int mark_count = FPDFPageObj_CountMarks(page_object);
+    for (int j = mark_count - 1; j >= 0; --j) {
+      FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
+
+      char buffer[256];
+      unsigned long name_len = 999u;
+      ASSERT_TRUE(
+          FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+      EXPECT_GT(name_len, 0u);
+      EXPECT_NE(999u, name_len);
+      std::wstring name =
+          GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+      if (name == L"Prime") {
+        // Remove mark.
+        EXPECT_TRUE(FPDFPageObj_RemoveMark(page_object, mark));
+
+        // Verify there is now one fewer mark in the page object.
+        EXPECT_EQ(mark_count - 1, FPDFPageObj_CountMarks(page_object));
+      }
+    }
+  }
+
+  // Verify there are 0 "Prime" content marks now.
+  CheckMarkCounts(page, 1, kExpectedObjectCount, 0, 4, 9, 1);
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the prime marks are not there anymore.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 0, 4, 9, 1);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, RemoveMarkParam) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr int kExpectedObjectCount = 19;
+  CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
+
+  // Remove all "Square" content marks parameters.
+  for (int i = 0; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+
+    int mark_count = FPDFPageObj_CountMarks(page_object);
+    for (int j = 0; j < mark_count; ++j) {
+      FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
+
+      char buffer[256];
+      unsigned long name_len = 999u;
+      ASSERT_TRUE(
+          FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+      EXPECT_GT(name_len, 0u);
+      EXPECT_NE(999u, name_len);
+      std::wstring name =
+          GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+      if (name == L"Square") {
+        // Show the mark has a "Factor" parameter.
+        int out_value;
+        EXPECT_TRUE(
+            FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
+
+        // Remove parameter.
+        EXPECT_TRUE(FPDFPageObjMark_RemoveParam(page_object, mark, "Factor"));
+
+        // Verify the "Factor" parameter is gone.
+        EXPECT_FALSE(
+            FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
+      }
+    }
+  }
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the "Factor" parameters are still gone.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  size_t square_count = 0;
+  for (int i = 0; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, i);
+
+    int mark_count = FPDFPageObj_CountMarks(page_object);
+    for (int j = 0; j < mark_count; ++j) {
+      FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
+
+      char buffer[256];
+      unsigned long name_len = 999u;
+      ASSERT_TRUE(
+          FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+      EXPECT_GT(name_len, 0u);
+      EXPECT_NE(999u, name_len);
+      std::wstring name =
+          GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+      if (name == L"Square") {
+        // Verify the "Factor" parameter is still gone.
+        int out_value;
+        EXPECT_FALSE(
+            FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
+
+        ++square_count;
+      }
+    }
+  }
+
+  // Verify the parameters are gone, but the marks are not.
+  EXPECT_EQ(4u, square_count);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, MaintainMarkedObjects) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Iterate over all objects, counting the number of times each content mark
+  // name appears.
+  CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
+
+  // Remove first page object.
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+  FPDFPageObj_Destroy(page_object);
+
+  CheckMarkCounts(page, 2, 18, 8, 3, 9, 1);
+
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  UnloadPage(page);
+
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, MaintainIndirectMarkedObjects) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Iterate over all objects, counting the number of times each content mark
+  // name appears.
+  CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
+
+  // Remove first page object.
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+  FPDFPageObj_Destroy(page_object);
+
+  CheckMarkCounts(page, 2, 18, 8, 3, 9, 1);
+
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  UnloadPage(page);
+
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObject) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Get the "Hello, world!" text object and remove it.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+
+  // Verify the "Hello, world!" text is gone.
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+  // Save the file
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+  FPDFPageObj_Destroy(page_object);
+
+  // Re-open the file and check the page object count is still 1.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \
+  DISABLED_RemoveExistingPageObjectSplitStreamsNotLonely
+#else
+#define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \
+  RemoveExistingPageObjectSplitStreamsNotLonely
+#endif
+TEST_F(FPDFEditEmbedderTest,
+       MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Get the "Hello, world!" text object and remove it. There is another object
+  // in the same stream that says "Goodbye, world!"
+  ASSERT_EQ(3, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+
+  // Verify the "Hello, world!" text is gone.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+#if defined(OS_MACOSX)
+  const char kHelloRemovedMD5[] = "e07a62d412728fc4d6e3ff42f2dd0e11";
+#elif defined(OS_WIN)
+  const char kHelloRemovedMD5[] = "a97d4c72c969ba373c2dce675d277e65";
+#else
+  const char kHelloRemovedMD5[] = "95b92950647a2190e1230911e7a1a0e9";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5);
+  }
+
+  // Save the file
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+  FPDFPageObj_Destroy(page_object);
+
+  // Re-open the file and check the page object count is still 2.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \
+  DISABLED_RemoveExistingPageObjectSplitStreamsLonely
+#else
+#define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \
+  RemoveExistingPageObjectSplitStreamsLonely
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveExistingPageObjectSplitStreamsLonely) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Get the "Greetings, world!" text object and remove it. This is the only
+  // object in the stream.
+  ASSERT_EQ(3, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 2);
+  ASSERT_TRUE(page_object);
+  EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+
+  // Verify the "Greetings, world!" text is gone.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+#if defined(OS_MACOSX)
+  const char kGreetingsRemovedMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
+#elif defined(OS_WIN)
+  const char kGreetingsRemovedMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
+#else
+  const char kGreetingsRemovedMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5);
+  }
+
+  // Save the file
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+  FPDFPageObj_Destroy(page_object);
+
+  // Re-open the file and check the page object count is still 2.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, GetContentStream) {
+  // Load document with some text split across streams.
+  EXPECT_TRUE(OpenDocument("split_streams.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Content stream 0: page objects 0-14.
+  // Content stream 1: page objects 15-17.
+  // Content stream 2: page object 18.
+  ASSERT_EQ(19, FPDFPage_CountObjects(page));
+  for (int i = 0; i < 19; i++) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(page_object);
+    CPDF_PageObject* cpdf_page_object =
+        CPDFPageObjectFromFPDFPageObject(page_object);
+    if (i < 15)
+      EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
+    else if (i < 18)
+      EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
+    else
+      EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i;
+  }
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemoveAllFromStream DISABLED_RemoveAllFromStream
+#else
+#define MAYBE_RemoveAllFromStream RemoveAllFromStream
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) {
+  // Load document with some text split across streams.
+  EXPECT_TRUE(OpenDocument("split_streams.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Content stream 0: page objects 0-14.
+  // Content stream 1: page objects 15-17.
+  // Content stream 2: page object 18.
+  ASSERT_EQ(19, FPDFPage_CountObjects(page));
+
+  // Loop backwards because objects will being removed, which shifts the indexes
+  // after the removed position.
+  for (int i = 18; i >= 0; i--) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(page_object);
+    CPDF_PageObject* cpdf_page_object =
+        CPDFPageObjectFromFPDFPageObject(page_object);
+
+    // Empty content stream 1.
+    if (cpdf_page_object->GetContentStream() == 1) {
+      EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
+      FPDFPageObj_Destroy(page_object);
+    }
+  }
+
+  // Content stream 0: page objects 0-14.
+  // Content stream 2: page object 15.
+  ASSERT_EQ(16, FPDFPage_CountObjects(page));
+  for (int i = 0; i < 16; i++) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(page_object);
+    CPDF_PageObject* cpdf_page_object =
+        CPDFPageObjectFromFPDFPageObject(page_object);
+    if (i < 15)
+      EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
+    else
+      EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i;
+  }
+
+  // Generate contents should remove the empty stream and update the page
+  // objects' contents stream indexes.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  // Content stream 0: page objects 0-14.
+  // Content stream 1: page object 15.
+  ASSERT_EQ(16, FPDFPage_CountObjects(page));
+  for (int i = 0; i < 16; i++) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(page_object);
+    CPDF_PageObject* cpdf_page_object =
+        CPDFPageObjectFromFPDFPageObject(page_object);
+    if (i < 15)
+      EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
+    else
+      EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
+  }
+
+#if defined(OS_MACOSX)
+  const char kStream1RemovedMD5[] = "d2e21fbd5a6de563f619feeeb6163331";
+#elif defined(OS_WIN)
+  const char kStream1RemovedMD5[] = "b4140f203523e38793283a5943d8075b";
+#else
+  const char kStream1RemovedMD5[] = "e86a3efc160ede6cfcb1f59bcacf1105";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5);
+  }
+
+  // Save the file
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the page object count is still 16, and that
+  // content stream 1 was removed.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  // Content stream 0: page objects 0-14.
+  // Content stream 1: page object 15.
+  EXPECT_EQ(16, FPDFPage_CountObjects(saved_page));
+  for (int i = 0; i < 16; i++) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, i);
+    ASSERT_TRUE(page_object);
+    CPDF_PageObject* cpdf_page_object =
+        CPDFPageObjectFromFPDFPageObject(page_object);
+    if (i < 15)
+      EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
+    else
+      EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
+  }
+
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, RemoveAllFromSingleStream) {
+  // Load document with a single stream.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Content stream 0: page objects 0-1.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+
+  // Loop backwards because objects will being removed, which shifts the indexes
+  // after the removed position.
+  for (int i = 1; i >= 0; i--) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(page_object);
+    CPDF_PageObject* cpdf_page_object =
+        CPDFPageObjectFromFPDFPageObject(page_object);
+    ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+    ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
+    FPDFPageObj_Destroy(page_object);
+  }
+
+  // No more objects in the stream
+  ASSERT_EQ(0, FPDFPage_CountObjects(page));
+
+  // Generate contents should remove the empty stream and update the page
+  // objects' contents stream indexes.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  ASSERT_EQ(0, FPDFPage_CountObjects(page));
+
+  const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+  }
+
+  // Save the file
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the page object count is still 0.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemoveFirstFromSingleStream DISABLED_RemoveFirstFromSingleStream
+#else
+#define MAYBE_RemoveFirstFromSingleStream RemoveFirstFromSingleStream
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveFirstFromSingleStream) {
+  // Load document with a single stream.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Content stream 0: page objects 0-1.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+
+  // Remove first object.
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  CPDF_PageObject* cpdf_page_object =
+      CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+  ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
+  FPDFPageObj_Destroy(page_object);
+
+  // One object left in the stream.
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+  page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  // Still one object left in the stream.
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+  page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+
+#if defined(OS_MACOSX)
+  const char kFirstRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa";
+#elif defined(OS_WIN)
+  const char kFirstRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb";
+#else
+  const char kFirstRemovedMD5[] = "b76df015fe88009c3c342395df96abf1";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5);
+  }
+
+  // Save the file
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the page object count is still 0.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
+  page_object = FPDFPage_GetObject(saved_page, 0);
+  ASSERT_TRUE(page_object);
+  cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemoveLastFromSingleStream DISABLED_RemoveLastFromSingleStream
+#else
+#define MAYBE_RemoveLastFromSingleStream RemoveLastFromSingleStream
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) {
+  // Load document with a single stream.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Content stream 0: page objects 0-1.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+
+  // Remove last object
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 1);
+  ASSERT_TRUE(page_object);
+  CPDF_PageObject* cpdf_page_object =
+      CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+  ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
+  FPDFPageObj_Destroy(page_object);
+
+  // One object left in the stream.
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+  page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  // Still one object left in the stream.
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+  page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+
+#if defined(OS_MACOSX)
+  const char kLastRemovedMD5[] = "f8fbd14a048b9e2ea8e5f059f22a910e";
+#elif defined(OS_WIN)
+  const char kLastRemovedMD5[] = "93db13099042bafefb3c22a165bad684";
+#else
+  const char kLastRemovedMD5[] = "93dcc09055f87a2792c8e3065af99a1b";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5);
+  }
+
+  // Save the file
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the page object count is still 0.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
+  page_object = FPDFPage_GetObject(saved_page, 0);
+  ASSERT_TRUE(page_object);
+  cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  ASSERT_EQ(0, cpdf_page_object->GetContentStream());
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Content stream 0: page objects 0-1.
+  // Content stream 1: page object 2.
+  ASSERT_EQ(3, FPDFPage_CountObjects(page));
+
+  // Loop backwards because objects will being removed, which shifts the indexes
+  // after the removed position.
+  for (int i = 2; i >= 0; i--) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(page_object);
+    ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
+    FPDFPageObj_Destroy(page_object);
+  }
+
+  // No more objects in the page.
+  ASSERT_EQ(0, FPDFPage_CountObjects(page));
+
+  // Generate contents should remove the empty streams and update the page
+  // objects' contents stream indexes.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  ASSERT_EQ(0, FPDFPage_CountObjects(page));
+
+  const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+  }
+
+  // Save the file
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the page object count is still 0.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, InsertPageObjectAndSave) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Add a red rectangle.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(page, red_rect);
+
+  // Verify the red rectangle was added.
+  ASSERT_EQ(3, FPDFPage_CountObjects(page));
+
+  // Save the file
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the page object count is still 3.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, InsertPageObjectEditAndSave) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Add a red rectangle.
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 100, 100, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(page, red_rect);
+
+  // Verify the red rectangle was added.
+  ASSERT_EQ(3, FPDFPage_CountObjects(page));
+
+  // Generate content but change it again
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
+
+  // Save the file
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the page object count is still 3.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_InsertAndRemoveLargeFile DISABLED_InsertAndRemoveLargeFile
+#else
+#define MAYBE_InsertAndRemoveLargeFile InsertAndRemoveLargeFile
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) {
+  const int kOriginalObjectCount = 600;
+
+  // Load document with many objects.
+  EXPECT_TRUE(OpenDocument("many_rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  const char kOriginalMD5[] = "b0170c575b65ecb93ebafada0ff0f038";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
+  }
+
+  // Add a black rectangle.
+  ASSERT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT black_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(black_rect, 0, 0, 0, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(black_rect, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(page, black_rect);
+
+  // Verify the black rectangle was added.
+  ASSERT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(page));
+  const char kPlusRectangleMD5[] = "6b9396ab570754b32b04ca629e902f77";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5);
+  }
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the rectangle added is still there.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  EXPECT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(saved_page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5);
+  }
+
+  // Remove the added rectangle.
+  FPDF_PAGEOBJECT added_object =
+      FPDFPage_GetObject(saved_page, kOriginalObjectCount);
+  EXPECT_TRUE(FPDFPage_RemoveObject(saved_page, added_object));
+  FPDFPageObj_Destroy(added_object);
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
+  }
+  EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
+
+  // Save the file again.
+  EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0));
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+
+  // Re-open the file (again) and check the black rectangle was removed and the
+  // rest is intact.
+  ASSERT_TRUE(OpenSavedDocument());
+  saved_page = LoadSavedPage(0);
+  EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
+  }
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, AddAndRemovePaths) {
+  // Start with a blank page.
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+  ASSERT_TRUE(page);
+
+  // Render the blank page and verify it's a blank bitmap.
+  const char kBlankMD5[] = "1940568c9ba33bac5d0b1ee9558c76b3";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5);
+  }
+  ASSERT_EQ(0, FPDFPage_CountObjects(page));
+
+  // Add a red rectangle.
+  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
+  ASSERT_TRUE(red_rect);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(page, red_rect);
+  const char kRedRectangleMD5[] = "66d02eaa6181e2c069ce2ea99beda497";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleMD5);
+  }
+  EXPECT_EQ(1, FPDFPage_CountObjects(page));
+
+  // Remove rectangle and verify it does not render anymore and the bitmap is
+  // back to a blank one.
+  EXPECT_TRUE(FPDFPage_RemoveObject(page, red_rect));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5);
+  }
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  // Trying to remove an object not in the page should return false.
+  EXPECT_FALSE(FPDFPage_RemoveObject(page, red_rect));
+
+  FPDF_ClosePage(page);
+  FPDFPageObj_Destroy(red_rect);
+}
+
+TEST_F(FPDFEditEmbedderTest, PathsPoints) {
+  CreateNewDocument();
+  FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document_);
+  // This should fail gracefully, even if img is not a path.
+  ASSERT_EQ(-1, FPDFPath_CountSegments(img));
+
+  // This should fail gracefully, even if path is NULL.
+  ASSERT_EQ(-1, FPDFPath_CountSegments(nullptr));
+
+  // FPDFPath_GetPathSegment() with a non-path.
+  ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(img, 0));
+  // FPDFPath_GetPathSegment() with a NULL path.
+  ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(nullptr, 0));
+  float x;
+  float y;
+  // FPDFPathSegment_GetPoint() with a NULL segment.
+  EXPECT_FALSE(FPDFPathSegment_GetPoint(nullptr, &x, &y));
+
+  // FPDFPathSegment_GetType() with a NULL segment.
+  ASSERT_EQ(FPDF_SEGMENT_UNKNOWN, FPDFPathSegment_GetType(nullptr));
+
+  // FPDFPathSegment_GetClose() with a NULL segment.
+  EXPECT_FALSE(FPDFPathSegment_GetClose(nullptr));
+
+  FPDFPageObj_Destroy(img);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_PathOnTopOfText DISABLED_PathOnTopOfText
+#else
+#define MAYBE_PathOnTopOfText PathOnTopOfText
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_PathOnTopOfText) {
+  // Load document with some text
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Add an opaque rectangle on top of some of the text.
+  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(page, red_rect);
+
+  // Add a transparent triangle on top of other part of the text.
+  FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(black_path, 0, 0, 0, 100));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
+  EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
+  EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
+  EXPECT_TRUE(FPDFPath_Close(black_path));
+  FPDFPage_InsertObject(page, black_path);
+
+  // Render and check the result. Text is slightly different on Mac.
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+#if defined(OS_MACOSX)
+  const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
+#elif defined(OS_WIN)
+  const char md5[] = "74dd9c393b8b2578d2b7feb032b7daad";
+#else
+  const char md5[] = "aa71b09b93b55f467f1290e5111babee";
+#endif
+  CompareBitmap(bitmap.get(), 200, 200, md5);
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_EditOverExistingContent DISABLED_EditOverExistingContent
+#else
+#define MAYBE_EditOverExistingContent EditOverExistingContent
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_EditOverExistingContent) {
+  // Load document with existing content
+  EXPECT_TRUE(OpenDocument("bug_717.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Add a transparent rectangle on top of the existing content
+  FPDF_PAGEOBJECT red_rect2 = FPDFPageObj_CreateNewRect(90, 700, 25, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect2, 255, 0, 0, 100));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect2, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(page, red_rect2);
+
+  // Add an opaque rectangle on top of the existing content
+  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(115, 700, 25, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(page, red_rect);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  CompareBitmap(bitmap.get(), 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  // Now save the result, closing the page and document
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  VerifySavedRendering(saved_page, 612, 792,
+                       "ad04e5bd0f471a9a564fb034bd0fb073");
+
+  ClearString();
+  // Add another opaque rectangle on top of the existing content
+  FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(150, 700, 25, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect, 0, 255, 0, 255));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(saved_page, green_rect);
+
+  // Add another transparent rectangle on top of existing content
+  FPDF_PAGEOBJECT green_rect2 = FPDFPageObj_CreateNewRect(175, 700, 25, 50);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect2, 0, 255, 0, 100));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0));
+  FPDFPage_InsertObject(saved_page, green_rect2);
+  const char kLastMD5[] = "4b5b00f824620f8c9b8801ebb98e1cdd";
+  {
+    ScopedFPDFBitmap new_bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(new_bitmap.get(), 612, 792, kLastMD5);
+  }
+  EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
+
+  // Now save the result, closing the page and document
+  EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0));
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+
+  // Render the saved result
+  VerifySavedDocument(612, 792, kLastMD5);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddStrokedPaths DISABLED_AddStrokedPaths
+#else
+#define MAYBE_AddStrokedPaths AddStrokedPaths
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddStrokedPaths) {
+  // Start with a blank page
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+
+  // Add a large stroked rectangle (fill color should not affect it).
+  FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 255, 0, 0, 255));
+  EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect, 0, 255, 0, 255));
+  EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(rect, 15.0f));
+
+  float width = 0;
+  EXPECT_TRUE(FPDFPageObj_GetStrokeWidth(rect, &width));
+  EXPECT_EQ(15.0f, width);
+
+  EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
+  FPDFPage_InsertObject(page, rect);
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "64bd31f862a89e0a9e505a5af6efd506");
+  }
+
+  // Add crossed-checkmark
+  FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
+  EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
+  EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
+  EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
+  EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
+  EXPECT_TRUE(FPDFPageObj_SetStrokeColor(check, 128, 128, 128, 180));
+  EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(check, 8.35f));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
+  FPDFPage_InsertObject(page, check);
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "4b6f3b9d25c4e194821217d5016c3724");
+  }
+
+  // Add stroked and filled oval-ish path.
+  FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
+  EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
+  EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
+  EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
+  EXPECT_TRUE(FPDFPath_Close(path));
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 128, 128, 100));
+  EXPECT_TRUE(FPDFPageObj_SetStrokeColor(path, 128, 200, 128, 150));
+  EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(path, 10.5f));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
+  FPDFPage_InsertObject(page, path);
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "ff3e6a22326754944cc6e56609acd73b");
+  }
+  FPDF_ClosePage(page);
+}
+
+// Tests adding text from standard font using FPDFPageObj_NewTextObj.
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddStandardFontText DISABLED_AddStandardFontText
+#else
+#define MAYBE_AddStandardFontText AddStandardFontText
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText) {
+  // Start with a blank page
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+
+  // Add some text to the page
+  FPDF_PAGEOBJECT text_object1 =
+      FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
+  EXPECT_TRUE(text_object1);
+  ScopedFPDFWideString text1 =
+      GetFPDFWideString(L"I'm at the bottom of the page");
+  EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get()));
+  FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20);
+  FPDFPage_InsertObject(page, text_object1);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+#if defined(OS_MACOSX)
+    const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
+#elif defined(OS_WIN)
+    const char md5[] = "08d1ff3e5a42801bee6077fd366bef00";
+#else
+    const char md5[] = "eacaa24573b8ce997b3882595f096f00";
+#endif
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    VerifySavedDocument(612, 792, md5);
+  }
+
+  // Try another font
+  FPDF_PAGEOBJECT text_object2 =
+      FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
+  EXPECT_TRUE(text_object2);
+  ScopedFPDFWideString text2 =
+      GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold.");
+  EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
+  FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600);
+  FPDFPage_InsertObject(page, text_object2);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+#if defined(OS_MACOSX)
+    const char md5[] = "a5c4ace4c6f27644094813fe1441a21c";
+#elif defined(OS_WIN)
+    const char md5[] = "3755dd35abd4c605755369401ee85b2d";
+#else
+    const char md5[] = "76fcc7d08aa15445efd2e2ceb7c6cc3b";
+#endif
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    VerifySavedDocument(612, 792, md5);
+  }
+
+  // And some randomly transformed text
+  FPDF_PAGEOBJECT text_object3 =
+      FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
+  EXPECT_TRUE(text_object3);
+  ScopedFPDFWideString text3 = GetFPDFWideString(L"Can you read me? <:)>");
+  EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get()));
+  FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200);
+  FPDFPage_InsertObject(page, text_object3);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+#if defined(OS_MACOSX)
+    const char md5[] = "40b3ef04f915ff4c4208948001763544";
+#elif defined(OS_WIN)
+    const char md5[] = "5ded49fe157f89627903553771431e3d";
+#else
+    const char md5[] = "344534539aa7c5cc78404cfff4bde7fb";
+#endif
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    VerifySavedDocument(612, 792, md5);
+  }
+
+  FS_MATRIX matrix;
+  EXPECT_FALSE(FPDFTextObj_GetMatrix(nullptr, &matrix));
+  EXPECT_TRUE(FPDFTextObj_GetMatrix(text_object3, &matrix));
+  EXPECT_FLOAT_EQ(1.0f, matrix.a);
+  EXPECT_FLOAT_EQ(1.5f, matrix.b);
+  EXPECT_FLOAT_EQ(2.0f, matrix.c);
+  EXPECT_FLOAT_EQ(0.5f, matrix.d);
+  EXPECT_FLOAT_EQ(200.0f, matrix.e);
+  EXPECT_FLOAT_EQ(200.0f, matrix.f);
+
+  EXPECT_EQ(0, FPDFTextObj_GetFontSize(nullptr));
+  EXPECT_EQ(20, FPDFTextObj_GetFontSize(text_object3));
+
+  // TODO(npm): Why are there issues with text rotated by 90 degrees?
+  // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
+  FPDF_ClosePage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetTextRenderMode) {
+  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+
+  EXPECT_EQ(FPDF_TEXTRENDERMODE_UNKNOWN,
+            FPDFTextObj_GetTextRenderMode(nullptr));
+
+  FPDF_PAGEOBJECT fill = FPDFPage_GetObject(page, 0);
+  EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL, FPDFTextObj_GetTextRenderMode(fill));
+
+  FPDF_PAGEOBJECT stroke = FPDFPage_GetObject(page, 1);
+  EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE, FPDFTextObj_GetTextRenderMode(stroke));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) {
+  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(2, FPDFPage_CountObjects(page));
+
+  // Check the bitmap
+  {
+#if defined(OS_MACOSX)
+    const char md5[] = "139846b4ffbd34b1fd67e3b82cf33b7e";
+#elif defined(OS_WIN)
+    const char md5[] = "de6e86bad3e9fda753a8471a45cfbb58";
+#else
+    const char md5[] = "5a012d2920ac075c39ffa9437ea42faa";
+#endif
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 446, md5);
+  }
+
+  // Cannot set on a null object.
+  EXPECT_FALSE(
+      FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_UNKNOWN));
+  EXPECT_FALSE(
+      FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_INVISIBLE));
+
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(page_object);
+  EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL,
+            FPDFTextObj_GetTextRenderMode(page_object));
+
+  // Cannot set UNKNOWN as a render mode.
+  EXPECT_FALSE(
+      FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_UNKNOWN));
+
+  EXPECT_TRUE(
+      FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_STROKE));
+  EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE,
+            FPDFTextObj_GetTextRenderMode(page_object));
+
+  // Check that bitmap displays changed content
+  {
+    const char md5[] = "412e52e621b46bd77baf2162e1fb1a1d";
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 446, md5);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, TestGetTextFontName) {
+  EXPECT_TRUE(OpenDocument("text_font.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+  // FPDFTextObj_GetFontName() positive testing.
+  FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0);
+  unsigned long size = FPDFTextObj_GetFontName(text, nullptr, 0);
+  const char kExpectedFontName[] = "Liberation Serif";
+  ASSERT_EQ(sizeof(kExpectedFontName), size);
+  std::vector<char> font_name(size);
+  ASSERT_EQ(size, FPDFTextObj_GetFontName(text, font_name.data(), size));
+  ASSERT_STREQ(kExpectedFontName, font_name.data());
+
+  // FPDFTextObj_GetFontName() negative testing.
+  ASSERT_EQ(0U, FPDFTextObj_GetFontName(nullptr, nullptr, 0));
+
+  font_name.resize(2);
+  font_name[0] = 'x';
+  font_name[1] = '\0';
+  size = FPDFTextObj_GetFontName(text, font_name.data(), font_name.size());
+  ASSERT_EQ(sizeof(kExpectedFontName), size);
+  ASSERT_EQ(std::string("x"), std::string(font_name.data()));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, TestFormGetObjects) {
+  EXPECT_TRUE(OpenDocument("form_object.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT form = FPDFPage_GetObject(page, 0);
+  EXPECT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
+  ASSERT_EQ(-1, FPDFFormObj_CountObjects(nullptr));
+  ASSERT_EQ(2, FPDFFormObj_CountObjects(form));
+
+  // FPDFFormObj_GetObject() positive testing.
+  FPDF_PAGEOBJECT text1 = FPDFFormObj_GetObject(form, 0);
+  ASSERT_TRUE(text1);
+  float left = 0;
+  float bottom = 0;
+  float right = 0;
+  float top = 0;
+  ASSERT_TRUE(FPDFPageObj_GetBounds(text1, &left, &bottom, &right, &top));
+  ASSERT_EQ(271, static_cast<int>(top));
+
+  FPDF_PAGEOBJECT text2 = FPDFFormObj_GetObject(form, 1);
+  ASSERT_TRUE(text2);
+  ASSERT_TRUE(FPDFPageObj_GetBounds(text2, &left, &bottom, &right, &top));
+  ASSERT_EQ(221, static_cast<int>(top));
+
+  // FPDFFormObj_GetObject() negative testing.
+  ASSERT_EQ(nullptr, FPDFFormObj_GetObject(nullptr, 0));
+  ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, -1));
+  ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, 2));
+
+  // Reset the form object matrix to identity.
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(form);
+  CPDF_FormObject* pFormObj = pPageObj->AsForm();
+  pFormObj->Transform(pFormObj->form_matrix().GetInverse());
+
+  // FPDFFormObj_GetMatrix() positive testing.
+  static constexpr FS_MATRIX kMatrix = {1.0f, 1.5f, 2.0f, 2.5f, 100.0f, 200.0f};
+  pFormObj->Transform(CFXMatrixFromFSMatrix(kMatrix));
+
+  FS_MATRIX matrix;
+  EXPECT_TRUE(FPDFFormObj_GetMatrix(form, &matrix));
+  EXPECT_FLOAT_EQ(kMatrix.a, matrix.a);
+  EXPECT_FLOAT_EQ(kMatrix.b, matrix.b);
+  EXPECT_FLOAT_EQ(kMatrix.c, matrix.c);
+  EXPECT_FLOAT_EQ(kMatrix.d, matrix.d);
+  EXPECT_FLOAT_EQ(kMatrix.e, matrix.e);
+  EXPECT_FLOAT_EQ(kMatrix.f, matrix.f);
+
+  // FPDFFormObj_GetMatrix() negative testing.
+  EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, &matrix));
+  EXPECT_FALSE(FPDFFormObj_GetMatrix(form, nullptr));
+  EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, nullptr));
+
+  UnloadPage(page);
+}
+
+// Tests adding text from standard font using FPDFText_LoadStandardFont.
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddStandardFontText2 DISABLED_AddStandardFontText2
+#else
+#define MAYBE_AddStandardFontText2 AddStandardFontText2
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText2) {
+  // Start with a blank page
+  ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
+
+  // Load a standard font.
+  ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), "Helvetica"));
+  ASSERT_TRUE(font);
+
+  // Add some text to the page.
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
+  EXPECT_TRUE(text_object);
+  ScopedFPDFWideString text =
+      GetFPDFWideString(L"I'm at the bottom of the page");
+  EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
+  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20);
+  FPDFPage_InsertObject(page.get(), text_object);
+  ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
+#if defined(OS_MACOSX)
+  const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
+#elif defined(OS_WIN)
+  const char md5[] = "08d1ff3e5a42801bee6077fd366bef00";
+#else
+  const char md5[] = "eacaa24573b8ce997b3882595f096f00";
+#endif
+  CompareBitmap(page_bitmap.get(), 612, 792, md5);
+}
+
+TEST_F(FPDFEditEmbedderTest, LoadStandardFonts) {
+  CreateNewDocument();
+  static constexpr const char* kStandardFontNames[] = {
+      "Arial",
+      "Arial-Bold",
+      "Arial-BoldItalic",
+      "Arial-Italic",
+      "Courier",
+      "Courier-BoldOblique",
+      "Courier-Oblique",
+      "Courier-Bold",
+      "CourierNew",
+      "CourierNew-Bold",
+      "CourierNew-BoldItalic",
+      "CourierNew-Italic",
+      "Helvetica",
+      "Helvetica-Bold",
+      "Helvetica-BoldOblique",
+      "Helvetica-Oblique",
+      "Symbol",
+      "TimesNewRoman",
+      "TimesNewRoman-Bold",
+      "TimesNewRoman-BoldItalic",
+      "TimesNewRoman-Italic",
+      "ZapfDingbats"};
+  for (const char* font_name : kStandardFontNames) {
+    ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), font_name));
+    EXPECT_TRUE(font) << font_name << " should be considered a standard font.";
+  }
+  static constexpr const char* kNotStandardFontNames[] = {
+      "Abcdefg",      "ArialB",    "Arial-Style",
+      "Font Name",    "FontArial", "NotAStandardFontName",
+      "TestFontName", "Quack",     "Symbol-Italic",
+      "Zapf"};
+  for (const char* font_name : kNotStandardFontNames) {
+    ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), font_name));
+    EXPECT_FALSE(font) << font_name
+                       << " should not be considered a standard font.";
+  }
+}
+
+TEST_F(FPDFEditEmbedderTest, GraphicsData) {
+  // New page
+  ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
+
+  // Create a rect with nontrivial graphics
+  FPDF_PAGEOBJECT rect1 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
+  FPDFPageObj_SetBlendMode(rect1, "Color");
+  FPDFPage_InsertObject(page.get(), rect1);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+
+  // Check that the ExtGState was created
+  CPDF_Page* cpage = CPDFPageFromFPDFPage(page.get());
+  CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState");
+  ASSERT_TRUE(graphics_dict);
+  EXPECT_EQ(2u, graphics_dict->size());
+
+  // Add a text object causing no change to the graphics dictionary
+  FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
+  // Only alpha, the last component, matters for the graphics dictionary. And
+  // the default value is 255.
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(text1, 100, 100, 100, 255));
+  FPDFPage_InsertObject(page.get(), text1);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(2u, graphics_dict->size());
+
+  // Add a text object increasing the size of the graphics dictionary
+  FPDF_PAGEOBJECT text2 =
+      FPDFPageObj_NewTextObj(document(), "Times-Roman", 12.0f);
+  FPDFPage_InsertObject(page.get(), text2);
+  FPDFPageObj_SetBlendMode(text2, "Darken");
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(text2, 0, 0, 255, 150));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(3u, graphics_dict->size());
+
+  // Add a path that should reuse graphics
+  FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
+  FPDFPageObj_SetBlendMode(path, "Darken");
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 200, 100, 150));
+  FPDFPage_InsertObject(page.get(), path);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(3u, graphics_dict->size());
+
+  // Add a rect increasing the size of the graphics dictionary
+  FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
+  FPDFPageObj_SetBlendMode(rect2, "Darken");
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(rect2, 0, 0, 255, 150));
+  EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect2, 0, 0, 0, 200));
+  FPDFPage_InsertObject(page.get(), rect2);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(4u, graphics_dict->size());
+}
+
+TEST_F(FPDFEditEmbedderTest, DoubleGenerating) {
+  // Start with a blank page
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+
+  // Add a red rectangle with some non-default alpha
+  FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 255, 0, 0, 128));
+  EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
+  FPDFPage_InsertObject(page, rect);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  // Check the ExtGState
+  CPDF_Page* cpage = CPDFPageFromFPDFPage(page);
+  CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState");
+  ASSERT_TRUE(graphics_dict);
+  EXPECT_EQ(2u, graphics_dict->size());
+
+  // Check the bitmap
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "5384da3406d62360ffb5cac4476fff1c");
+  }
+
+  // Never mind, my new favorite color is blue, increase alpha
+  EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 0, 0, 255, 180));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_EQ(3u, graphics_dict->size());
+
+  // Check that bitmap displays changed content
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "2e51656f5073b0bee611d9cd086aa09c");
+  }
+
+  // And now generate, without changes
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_EQ(3u, graphics_dict->size());
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792,
+                  "2e51656f5073b0bee611d9cd086aa09c");
+  }
+
+  // Add some text to the page
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
+  ScopedFPDFWideString text =
+      GetFPDFWideString(L"Something something #text# something");
+  EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
+  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300);
+  FPDFPage_InsertObject(page, text_object);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  CPDF_Dictionary* font_dict = cpage->m_pResources->GetDictFor("Font");
+  ASSERT_TRUE(font_dict);
+  EXPECT_EQ(1u, font_dict->size());
+
+  // Generate yet again, check dicts are reasonably sized
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_EQ(3u, graphics_dict->size());
+  EXPECT_EQ(1u, font_dict->size());
+  FPDF_ClosePage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, LoadSimpleType1Font) {
+  CreateNewDocument();
+  // TODO(npm): use other fonts after disallowing loading any font as any type
+  RetainPtr<CPDF_Font> stock_font =
+      CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
+  pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
+  ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
+                                        FPDF_FONT_TYPE1, false));
+  ASSERT_TRUE(font.get());
+  CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
+  EXPECT_TRUE(typed_font->IsType1Font());
+
+  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
+  EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
+  EXPECT_EQ("TimesNewRomanPS-BoldMT", font_dict->GetStringFor("BaseFont"));
+  ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
+  ASSERT_TRUE(font_dict->KeyExist("LastChar"));
+  EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
+  EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
+
+  const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
+  ASSERT_TRUE(widths_array);
+  ASSERT_EQ(224u, widths_array->size());
+  EXPECT_EQ(250, widths_array->GetNumberAt(0));
+  EXPECT_EQ(569, widths_array->GetNumberAt(11));
+  EXPECT_EQ(500, widths_array->GetNumberAt(223));
+  CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, span);
+}
+
+TEST_F(FPDFEditEmbedderTest, LoadSimpleTrueTypeFont) {
+  CreateNewDocument();
+  RetainPtr<CPDF_Font> stock_font =
+      CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
+  pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
+  ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
+                                        FPDF_FONT_TRUETYPE, false));
+  ASSERT_TRUE(font.get());
+  CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
+  EXPECT_TRUE(typed_font->IsTrueTypeFont());
+
+  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
+  EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
+  EXPECT_EQ("CourierNewPSMT", font_dict->GetStringFor("BaseFont"));
+  ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
+  ASSERT_TRUE(font_dict->KeyExist("LastChar"));
+  EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
+  EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
+
+  const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
+  ASSERT_TRUE(widths_array);
+  ASSERT_EQ(224u, widths_array->size());
+  EXPECT_EQ(600, widths_array->GetNumberAt(33));
+  EXPECT_EQ(600, widths_array->GetNumberAt(74));
+  EXPECT_EQ(600, widths_array->GetNumberAt(223));
+  CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, span);
+}
+
+TEST_F(FPDFEditEmbedderTest, LoadCIDType0Font) {
+  CreateNewDocument();
+  RetainPtr<CPDF_Font> stock_font =
+      CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
+  pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
+  ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
+                                        FPDF_FONT_TYPE1, 1));
+  ASSERT_TRUE(font.get());
+  CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
+  EXPECT_TRUE(typed_font->IsCIDFont());
+
+  // Check font dictionary entries
+  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
+  EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
+  EXPECT_EQ("TimesNewRomanPSMT-Identity-H",
+            font_dict->GetStringFor("BaseFont"));
+  EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
+  const CPDF_Array* descendant_array =
+      font_dict->GetArrayFor("DescendantFonts");
+  ASSERT_TRUE(descendant_array);
+  EXPECT_EQ(1u, descendant_array->size());
+
+  // Check the CIDFontDict
+  const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
+  EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
+  EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
+  EXPECT_EQ("TimesNewRomanPSMT", cidfont_dict->GetStringFor("BaseFont"));
+  const CPDF_Dictionary* cidinfo_dict =
+      cidfont_dict->GetDictFor("CIDSystemInfo");
+  ASSERT_TRUE(cidinfo_dict);
+  const CPDF_Object* registry = cidinfo_dict->GetObjectFor("Registry");
+  ASSERT_TRUE(registry);
+  EXPECT_EQ(CPDF_Object::kString, registry->GetType());
+  EXPECT_EQ("Adobe", registry->GetString());
+  const CPDF_Object* ordering = cidinfo_dict->GetObjectFor("Ordering");
+  ASSERT_TRUE(ordering);
+  EXPECT_EQ(CPDF_Object::kString, ordering->GetType());
+  EXPECT_EQ("Identity", ordering->GetString());
+  EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
+  CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, span);
+
+  // Check widths
+  const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
+  ASSERT_TRUE(widths_array);
+  EXPECT_GT(widths_array->size(), 1u);
+  CheckCompositeFontWidths(widths_array, typed_font);
+}
+
+TEST_F(FPDFEditEmbedderTest, LoadCIDType2Font) {
+  CreateNewDocument();
+  RetainPtr<CPDF_Font> stock_font =
+      CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
+  pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
+  ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
+                                        FPDF_FONT_TRUETYPE, 1));
+  ASSERT_TRUE(font.get());
+  CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
+  EXPECT_TRUE(typed_font->IsCIDFont());
+
+  // Check font dictionary entries
+  const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
+  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
+  EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
+  EXPECT_EQ("Arial-ItalicMT", font_dict->GetStringFor("BaseFont"));
+  EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
+  const CPDF_Array* descendant_array =
+      font_dict->GetArrayFor("DescendantFonts");
+  ASSERT_TRUE(descendant_array);
+  EXPECT_EQ(1u, descendant_array->size());
+
+  // Check the CIDFontDict
+  const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
+  EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
+  EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
+  EXPECT_EQ("Arial-ItalicMT", cidfont_dict->GetStringFor("BaseFont"));
+  const CPDF_Dictionary* cidinfo_dict =
+      cidfont_dict->GetDictFor("CIDSystemInfo");
+  ASSERT_TRUE(cidinfo_dict);
+  EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
+  EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
+  EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
+  CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, span);
+
+  // Check widths
+  const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
+  ASSERT_TRUE(widths_array);
+  CheckCompositeFontWidths(widths_array, typed_font);
+}
+
+TEST_F(FPDFEditEmbedderTest, NormalizeNegativeRotation) {
+  // Load document with a -90 degree rotation
+  EXPECT_TRUE(OpenDocument("bug_713197.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+
+  EXPECT_EQ(3, FPDFPage_GetRotation(page));
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddTrueTypeFontText DISABLED_AddTrueTypeFontText
+#else
+#define MAYBE_AddTrueTypeFontText AddTrueTypeFontText
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddTrueTypeFontText) {
+  // Start with a blank page
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+  {
+    RetainPtr<CPDF_Font> stock_font =
+        CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
+    pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
+    ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
+                                          FPDF_FONT_TRUETYPE, 0));
+    ASSERT_TRUE(font.get());
+
+    // Add some text to the page
+    FPDF_PAGEOBJECT text_object =
+        FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
+    EXPECT_TRUE(text_object);
+    ScopedFPDFWideString text =
+        GetFPDFWideString(L"I am testing my loaded font, WEE.");
+    EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
+    FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
+    FPDFPage_InsertObject(page, text_object);
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+#if defined(OS_MACOSX)
+    const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
+#elif defined(OS_WIN)
+    const char md5[] = "d60ba39f9698e32360d99e727dd93165";
+#else
+    const char md5[] = "70592859010ffbf532a2237b8118bcc4";
+#endif
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+
+    // Add some more text, same font
+    FPDF_PAGEOBJECT text_object2 =
+        FPDFPageObj_CreateTextObj(document(), font.get(), 15.0f);
+    ScopedFPDFWideString text2 = GetFPDFWideString(L"Bigger font size");
+    EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
+    FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 200, 200);
+    FPDFPage_InsertObject(page, text_object2);
+  }
+  ScopedFPDFBitmap page_bitmap2 = RenderPage(page);
+#if defined(OS_MACOSX)
+  const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea";
+#elif defined(OS_WIN)
+  const char md5_2[] = "2199b579c49ab5f80c246a586a80ee90";
+#else
+  const char md5_2[] = "c1d10cce1761c4a998a16b2562030568";
+#endif
+  CompareBitmap(page_bitmap2.get(), 612, 792, md5_2);
+
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  FPDF_ClosePage(page);
+
+  VerifySavedDocument(612, 792, md5_2);
+}
+
+TEST_F(FPDFEditEmbedderTest, TransformAnnot) {
+  // Open a file with one annotation and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Add an underline annotation to the page without specifying its rectangle.
+    ScopedFPDFAnnotation annot(
+        FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE));
+    ASSERT_TRUE(annot);
+
+    // FPDFPage_TransformAnnots() should run without errors when modifying
+    // annotation rectangles.
+    FPDFPage_TransformAnnots(page, 1, 2, 3, 4, 5, 6);
+  }
+  UnloadPage(page);
+}
+
+// TODO(npm): Add tests using Japanese fonts in other OS.
+#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddCIDFontText DISABLED_AddCIDFontText
+#else
+#define MAYBE_AddCIDFontText AddCIDFontText
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddCIDFontText) {
+  // Start with a blank page
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+  CFX_Font CIDfont;
+  {
+    // First, get the data from the font
+    CIDfont.LoadSubst("IPAGothic", 1, 0, 400, 0, 932, 0);
+    EXPECT_EQ("IPAGothic", CIDfont.GetFaceName());
+    pdfium::span<const uint8_t> span = CIDfont.GetFontSpan();
+
+    // Load the data into a FPDF_Font.
+    ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
+                                          FPDF_FONT_TRUETYPE, 1));
+    ASSERT_TRUE(font.get());
+
+    // Add some text to the page
+    FPDF_PAGEOBJECT text_object =
+        FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
+    ASSERT_TRUE(text_object);
+    std::wstring wstr = L"ABCDEFGhijklmnop.";
+    ScopedFPDFWideString text = GetFPDFWideString(wstr);
+    EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
+    FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 200);
+    FPDFPage_InsertObject(page, text_object);
+
+    // And add some Japanese characters
+    FPDF_PAGEOBJECT text_object2 =
+        FPDFPageObj_CreateTextObj(document(), font.get(), 18.0f);
+    ASSERT_TRUE(text_object2);
+    std::wstring wstr2 =
+        L"\u3053\u3093\u306B\u3061\u306f\u4e16\u754C\u3002\u3053\u3053\u306B1"
+        L"\u756A";
+    ScopedFPDFWideString text2 = GetFPDFWideString(wstr2);
+    EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
+    FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 500);
+    FPDFPage_InsertObject(page, text_object2);
+  }
+
+  // Check that the text renders properly.
+  const char md5[] = "5159a72903fe57bf0cf645c894de8a74";
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+  }
+
+  // Save the document, close the page.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  FPDF_ClosePage(page);
+
+  VerifySavedDocument(612, 792, md5);
+}
+#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_SaveAndRender DISABLED_SaveAndRender
+#else
+#define MAYBE_SaveAndRender SaveAndRender
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_SaveAndRender) {
+  const char md5[] = "3c20472b0552c0c22b88ab1ed8c6202b";
+  {
+    EXPECT_TRUE(OpenDocument("bug_779.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_NE(nullptr, page);
+
+    // Now add a more complex blue path.
+    FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
+    EXPECT_TRUE(FPDFPageObj_SetFillColor(green_path, 0, 255, 0, 200));
+    // TODO(npm): stroking will cause the MD5s to differ.
+    EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
+    EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
+    EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
+    EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 133));
+    EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 33));
+    EXPECT_TRUE(FPDFPath_BezierTo(green_path, 38, 33, 39, 36, 40, 40));
+    EXPECT_TRUE(FPDFPath_Close(green_path));
+    FPDFPage_InsertObject(page, green_path);
+    ScopedFPDFBitmap page_bitmap = RenderLoadedPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+
+    // Now save the result, closing the page and document
+    EXPECT_TRUE(FPDFPage_GenerateContent(page));
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    UnloadPage(page);
+  }
+
+  VerifySavedDocument(612, 792, md5);
+}
+
+TEST_F(FPDFEditEmbedderTest, AddMark) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
+
+  // Add to the first page object a "Bounds" mark with "Position": "First".
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(page_object, "Bounds");
+  EXPECT_TRUE(mark);
+  EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
+                                             "Position", "First"));
+
+  CheckMarkCounts(page, 1, 19, 8, 4, 9, 2);
+
+  // Save the file
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the new mark is present.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  CheckMarkCounts(saved_page, 1, 19, 8, 4, 9, 2);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddMarkCompressedStream DISABLED_AddMarkCompressedStream
+#else
+#define MAYBE_AddMarkCompressedStream AddMarkCompressedStream
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkCompressedStream) {
+#if defined(OS_MACOSX)
+  const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
+#elif defined(OS_WIN)
+  const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
+#else
+  const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
+#endif
+
+  // Load document with some text in a compressed stream.
+  EXPECT_TRUE(OpenDocument("hello_world_compressed_stream.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Render and check there are no marks.
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+  }
+  CheckMarkCounts(page, 0, 2, 0, 0, 0, 0);
+
+  // Add to the first page object a "Bounds" mark with "Position": "First".
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(page_object, "Bounds");
+  EXPECT_TRUE(mark);
+  EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
+                                             "Position", "First"));
+
+  // Render and check there is 1 mark.
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+  }
+  CheckMarkCounts(page, 0, 2, 0, 0, 0, 1);
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the new mark is present.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
+    CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
+  }
+  CheckMarkCounts(saved_page, 0, 2, 0, 0, 0, 1);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, SetMarkParam) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr int kExpectedObjectCount = 19;
+  CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
+
+  // Check the "Bounds" mark's "Position" param is "Last".
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
+  ASSERT_TRUE(mark);
+  char buffer[256];
+  unsigned long name_len = 999u;
+  ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+  EXPECT_EQ((6u + 1u) * 2u, name_len);
+  ASSERT_EQ(L"Bounds",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
+  unsigned long out_buffer_len;
+  ASSERT_TRUE(FPDFPageObjMark_GetParamStringValue(
+      mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
+  ASSERT_EQ(L"Last",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
+
+  // Set is to "End".
+  EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
+                                             "Position", "End"));
+
+  // Verify the object passed must correspond to the mark passed.
+  FPDF_PAGEOBJECT another_page_object = FPDFPage_GetObject(page, 17);
+  EXPECT_FALSE(FPDFPageObjMark_SetStringParam(document(), another_page_object,
+                                              mark, "Position", "End"));
+
+  // Verify nothing else changed.
+  CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
+
+  // Verify "Position" now maps to "End".
+  EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
+      mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
+  EXPECT_EQ(L"End",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
+
+  // Save the file
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and cerify "Position" still maps to "End".
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 8, 4, 9, 1);
+  page_object = FPDFPage_GetObject(saved_page, 18);
+  mark = FPDFPageObj_GetMark(page_object, 1);
+  EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
+      mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
+  EXPECT_EQ(L"End",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_AddMarkedText DISABLED_AddMarkedText
+#else
+#define MAYBE_AddMarkedText AddMarkedText
+#endif
+TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) {
+  // Start with a blank page.
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+
+  RetainPtr<CPDF_Font> stock_font =
+      CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
+  pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
+  ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
+                                        FPDF_FONT_TRUETYPE, 0));
+  ASSERT_TRUE(font.get());
+
+  // Add some text to the page.
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
+
+  EXPECT_TRUE(text_object);
+  ScopedFPDFWideString text1 =
+      GetFPDFWideString(L"I am testing my loaded font, WEE.");
+  EXPECT_TRUE(FPDFText_SetText(text_object, text1.get()));
+  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
+  FPDFPage_InsertObject(page, text_object);
+
+  // Add a mark with the tag "TestMarkName" to that text.
+  EXPECT_EQ(0, FPDFPageObj_CountMarks(text_object));
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(text_object, "Test Mark Name");
+  EXPECT_TRUE(mark);
+  EXPECT_EQ(1, FPDFPageObj_CountMarks(text_object));
+  EXPECT_EQ(mark, FPDFPageObj_GetMark(text_object, 0));
+  char buffer[256];
+  unsigned long name_len = 999u;
+  ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+  EXPECT_EQ((14u + 1u) * 2, name_len);
+  std::wstring name =
+      GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+  EXPECT_EQ(L"Test Mark Name", name);
+
+  // Add parameters:
+  // - int "IntKey" : 42
+  // - string "StringKey": "StringValue"
+  // - blob "BlobKey": "\x01\x02\x03\0BlobValue1\0\0\0BlobValue2\0"
+  constexpr size_t kBlobLen = 28;
+  char block_value[kBlobLen];
+  memcpy(block_value, "\x01\x02\x03\0BlobValue1\0\0\0BlobValue2\0", kBlobLen);
+  EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark));
+  EXPECT_TRUE(
+      FPDFPageObjMark_SetIntParam(document(), text_object, mark, "IntKey", 42));
+  EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), text_object, mark,
+                                             "StringKey", "StringValue"));
+  EXPECT_TRUE(FPDFPageObjMark_SetBlobParam(document(), text_object, mark,
+                                           "BlobKey", block_value, kBlobLen));
+  EXPECT_EQ(3, FPDFPageObjMark_CountParams(mark));
+
+  // Check the two parameters can be retrieved.
+  EXPECT_EQ(FPDF_OBJECT_NUMBER,
+            FPDFPageObjMark_GetParamValueType(mark, "IntKey"));
+  int int_value;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamIntValue(mark, "IntKey", &int_value));
+  EXPECT_EQ(42, int_value);
+
+  EXPECT_EQ(FPDF_OBJECT_STRING,
+            FPDFPageObjMark_GetParamValueType(mark, "StringKey"));
+  unsigned long out_buffer_len = 999u;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
+      mark, "StringKey", buffer, sizeof(buffer), &out_buffer_len));
+  EXPECT_GT(out_buffer_len, 0u);
+  EXPECT_NE(999u, out_buffer_len);
+  name = GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+  EXPECT_EQ(L"StringValue", name);
+
+  EXPECT_EQ(FPDF_OBJECT_STRING,
+            FPDFPageObjMark_GetParamValueType(mark, "BlobKey"));
+  out_buffer_len = 0;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamBlobValue(
+      mark, "BlobKey", buffer, sizeof(buffer), &out_buffer_len));
+  EXPECT_EQ(kBlobLen, out_buffer_len);
+  EXPECT_EQ(0, memcmp(block_value, buffer, kBlobLen));
+
+// Render and check the bitmap is the expected one.
+#if defined(OS_MACOSX)
+  const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
+#elif defined(OS_WIN)
+  const char md5[] = "d60ba39f9698e32360d99e727dd93165";
+#else
+  const char md5[] = "70592859010ffbf532a2237b8118bcc4";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPage(page);
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+  }
+
+  // Now save the result.
+  EXPECT_EQ(1, FPDFPage_CountObjects(page));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  FPDF_ClosePage(page);
+
+  // Re-open the file and check the changes were kept in the saved .pdf.
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+  EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
+
+  text_object = FPDFPage_GetObject(saved_page, 0);
+  EXPECT_TRUE(text_object);
+  EXPECT_EQ(1, FPDFPageObj_CountMarks(text_object));
+  mark = FPDFPageObj_GetMark(text_object, 0);
+  EXPECT_TRUE(mark);
+
+  name_len = 999u;
+  ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
+  EXPECT_EQ((14u + 1u) * 2, name_len);
+  name = GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+  EXPECT_EQ(L"Test Mark Name", name);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
+TEST_F(FPDFEditEmbedderTest, MarkGetName) {
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
+  ASSERT_TRUE(mark);
+
+  char buffer[256];
+  unsigned long out_len;
+
+  // Show the positive cases of FPDFPageObjMark_GetName.
+  out_len = 999u;
+  EXPECT_TRUE(FPDFPageObjMark_GetName(mark, nullptr, 0, &out_len));
+  EXPECT_EQ((6u + 1u) * 2u, out_len);
+
+  out_len = 999u;
+  EXPECT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &out_len));
+  EXPECT_EQ(L"Bounds",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
+  EXPECT_EQ((6u + 1u) * 2u, out_len);
+
+  // Show the negative cases of FPDFPageObjMark_GetName.
+  out_len = 999u;
+  EXPECT_FALSE(
+      FPDFPageObjMark_GetName(nullptr, buffer, sizeof(buffer), &out_len));
+  EXPECT_EQ(999u, out_len);
+
+  EXPECT_FALSE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), nullptr));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, MarkGetParamKey) {
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
+  ASSERT_TRUE(mark);
+
+  char buffer[256];
+  unsigned long out_len;
+
+  // Show the positive cases of FPDFPageObjMark_GetParamKey.
+  out_len = 999u;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, nullptr, 0, &out_len));
+  EXPECT_EQ((8u + 1u) * 2u, out_len);
+
+  out_len = 999u;
+  EXPECT_TRUE(
+      FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer), &out_len));
+  EXPECT_EQ(L"Position",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
+  EXPECT_EQ((8u + 1u) * 2u, out_len);
+
+  // Show the negative cases of FPDFPageObjMark_GetParamKey.
+  out_len = 999u;
+  EXPECT_FALSE(FPDFPageObjMark_GetParamKey(nullptr, 0, buffer, sizeof(buffer),
+                                           &out_len));
+  EXPECT_EQ(999u, out_len);
+
+  out_len = 999u;
+  EXPECT_FALSE(
+      FPDFPageObjMark_GetParamKey(mark, 1, buffer, sizeof(buffer), &out_len));
+  EXPECT_EQ(999u, out_len);
+
+  EXPECT_FALSE(
+      FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer), nullptr));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, MarkGetIntParam) {
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 8);
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 0);
+  ASSERT_TRUE(mark);
+
+  int out_value;
+
+  // Show the positive cases of FPDFPageObjMark_GetParamIntValue.
+  out_value = 999;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
+  EXPECT_EQ(3, out_value);
+
+  // Show the negative cases of FPDFPageObjMark_GetParamIntValue.
+  out_value = 999;
+  EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(nullptr, "Factor", &out_value));
+  EXPECT_EQ(999, out_value);
+
+  out_value = 999;
+  EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "ParamThatDoesNotExist",
+                                                &out_value));
+  EXPECT_EQ(999, out_value);
+
+  EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "Factor", nullptr));
+
+  page_object = FPDFPage_GetObject(page, 18);
+  mark = FPDFPageObj_GetMark(page_object, 1);
+  out_value = 999;
+  EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "Position", &out_value));
+  EXPECT_EQ(999, out_value);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, MarkGetStringParam) {
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
+  ASSERT_TRUE(mark);
+
+  char buffer[256];
+  unsigned long out_len;
+
+  // Show the positive cases of FPDFPageObjMark_GetParamStringValue.
+  out_len = 999u;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(mark, "Position", nullptr, 0,
+                                                  &out_len));
+  EXPECT_EQ((4u + 1u) * 2u, out_len);
+
+  out_len = 999u;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(mark, "Position", buffer,
+                                                  sizeof(buffer), &out_len));
+  EXPECT_EQ(L"Last",
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
+  EXPECT_EQ((4u + 1u) * 2u, out_len);
+
+  // Show the negative cases of FPDFPageObjMark_GetParamStringValue.
+  out_len = 999u;
+  EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(nullptr, "Position", buffer,
+                                                   sizeof(buffer), &out_len));
+  EXPECT_EQ(999u, out_len);
+
+  out_len = 999u;
+  EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(
+      mark, "ParamThatDoesNotExist", buffer, sizeof(buffer), &out_len));
+  EXPECT_EQ(999u, out_len);
+
+  EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(mark, "Position", buffer,
+                                                   sizeof(buffer), nullptr));
+
+  page_object = FPDFPage_GetObject(page, 8);
+  mark = FPDFPageObj_GetMark(page_object, 0);
+  out_len = 999u;
+  EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(mark, "Factor", buffer,
+                                                   sizeof(buffer), &out_len));
+  EXPECT_EQ(999u, out_len);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, ExtractImageBitmap) {
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(39, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
+  EXPECT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_FALSE(FPDFImageObj_GetBitmap(obj));
+
+  obj = FPDFPage_GetObject(page, 33);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  FPDF_BITMAP bitmap = FPDFImageObj_GetBitmap(obj);
+  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
+  CompareBitmap(bitmap, 109, 88, "cb3637934bb3b95a6e4ae1ea9eb9e56e");
+  FPDFBitmap_Destroy(bitmap);
+
+  obj = FPDFPage_GetObject(page, 34);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  bitmap = FPDFImageObj_GetBitmap(obj);
+  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
+  CompareBitmap(bitmap, 103, 75, "c8d51fa6821ceb2a67f08446ff236c40");
+  FPDFBitmap_Destroy(bitmap);
+
+  obj = FPDFPage_GetObject(page, 35);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  bitmap = FPDFImageObj_GetBitmap(obj);
+  EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap));
+  CompareBitmap(bitmap, 92, 68, "9c6d76cb1e37ef8514f9455d759391f3");
+  FPDFBitmap_Destroy(bitmap);
+
+  obj = FPDFPage_GetObject(page, 36);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  bitmap = FPDFImageObj_GetBitmap(obj);
+  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
+  CompareBitmap(bitmap, 79, 60, "f4e72fb783a01c7b4614cdc25eaa98ac");
+  FPDFBitmap_Destroy(bitmap);
+
+  obj = FPDFPage_GetObject(page, 37);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  bitmap = FPDFImageObj_GetBitmap(obj);
+  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
+  CompareBitmap(bitmap, 126, 106, "2cf9e66414c72461f4ccbf9cdebdfa1b");
+  FPDFBitmap_Destroy(bitmap);
+
+  obj = FPDFPage_GetObject(page, 38);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  bitmap = FPDFImageObj_GetBitmap(obj);
+  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
+  CompareBitmap(bitmap, 194, 119, "a8f3a126cec274dab8242fd2ccdc1b8b");
+  FPDFBitmap_Destroy(bitmap);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, ExtractJBigImageBitmap) {
+  ASSERT_TRUE(OpenDocument("bug_631912.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  {
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    ASSERT_TRUE(bitmap);
+    EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap.get()));
+    CompareBitmap(bitmap.get(), 1152, 720, "3f6a48e2b3e91b799bf34567f55cb4de");
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetImageData) {
+  EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(39, FPDFPage_CountObjects(page));
+
+  // Retrieve an image object with flate-encoded data stream.
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  // Check that the raw image data has the correct length and hash value.
+  unsigned long len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
+  std::vector<char> buf(len);
+  EXPECT_EQ(4091u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
+  EXPECT_EQ("f73802327d2e88e890f653961bcda81a",
+            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+
+  // Check that the decoded image data has the correct length and hash value.
+  len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  EXPECT_EQ(28776u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
+  EXPECT_EQ("cb3637934bb3b95a6e4ae1ea9eb9e56e",
+            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+
+  // Retrieve an image object with DCTDecode-encoded data stream.
+  obj = FPDFPage_GetObject(page, 37);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+
+  // Check that the raw image data has the correct length and hash value.
+  len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  EXPECT_EQ(4370u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
+  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
+            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+
+  // Check that the decoded image data has the correct length and hash value,
+  // which should be the same as those of the raw data, since this image is
+  // encoded by a single DCTDecode filter and decoding is a noop.
+  len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  EXPECT_EQ(4370u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
+  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
+            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetImageMatrix) {
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(39, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT obj;
+  double a;
+  double b;
+  double c;
+  double d;
+  double e;
+  double f;
+
+  obj = FPDFPage_GetObject(page, 33);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(53.0, a);
+  EXPECT_DOUBLE_EQ(0.0, b);
+  EXPECT_DOUBLE_EQ(0.0, c);
+  EXPECT_DOUBLE_EQ(43.0, d);
+  EXPECT_DOUBLE_EQ(72.0, e);
+  EXPECT_DOUBLE_EQ(646.510009765625, f);
+
+  obj = FPDFPage_GetObject(page, 34);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(70.0, a);
+  EXPECT_DOUBLE_EQ(0.0, b);
+  EXPECT_DOUBLE_EQ(0.0, c);
+  EXPECT_DOUBLE_EQ(51.0, d);
+  EXPECT_DOUBLE_EQ(216.0, e);
+  EXPECT_DOUBLE_EQ(646.510009765625, f);
+
+  obj = FPDFPage_GetObject(page, 35);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(69.0, a);
+  EXPECT_DOUBLE_EQ(0.0, b);
+  EXPECT_DOUBLE_EQ(0.0, c);
+  EXPECT_DOUBLE_EQ(51.0, d);
+  EXPECT_DOUBLE_EQ(360.0, e);
+  EXPECT_DOUBLE_EQ(646.510009765625, f);
+
+  obj = FPDFPage_GetObject(page, 36);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(59.0, a);
+  EXPECT_DOUBLE_EQ(0.0, b);
+  EXPECT_DOUBLE_EQ(0.0, c);
+  EXPECT_DOUBLE_EQ(45.0, d);
+  EXPECT_DOUBLE_EQ(72.0, e);
+  EXPECT_DOUBLE_EQ(553.510009765625, f);
+
+  obj = FPDFPage_GetObject(page, 37);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(55.94000244140625, a);
+  EXPECT_DOUBLE_EQ(0.0, b);
+  EXPECT_DOUBLE_EQ(0.0, c);
+  EXPECT_DOUBLE_EQ(46.950000762939453, d);
+  EXPECT_DOUBLE_EQ(216.0, e);
+  EXPECT_DOUBLE_EQ(552.510009765625, f);
+
+  obj = FPDFPage_GetObject(page, 38);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(70.528999328613281, a);
+  EXPECT_DOUBLE_EQ(0.0, b);
+  EXPECT_DOUBLE_EQ(0.0, c);
+  EXPECT_DOUBLE_EQ(43.149997711181641, d);
+  EXPECT_DOUBLE_EQ(360.0, e);
+  EXPECT_DOUBLE_EQ(553.3599853515625, f);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, DestroyPageObject) {
+  FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
+  ASSERT_TRUE(rect);
+
+  // There should be no memory leaks with a call to FPDFPageObj_Destroy().
+  FPDFPageObj_Destroy(rect);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetImageFilters) {
+  EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Verify that retrieving the filter of a non-image object would fail.
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
+  ASSERT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  ASSERT_EQ(0, FPDFImageObj_GetImageFilterCount(obj));
+  EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0));
+
+  // Verify the returned filter string for an image object with a single filter.
+  obj = FPDFPage_GetObject(page, 33);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  ASSERT_EQ(1, FPDFImageObj_GetImageFilterCount(obj));
+  unsigned long len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
+  std::vector<char> buf(len);
+  static constexpr char kFlateDecode[] = "FlateDecode";
+  EXPECT_EQ(sizeof(kFlateDecode),
+            FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
+  EXPECT_STREQ(kFlateDecode, buf.data());
+  EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0));
+
+  // Verify all the filters for an image object with a list of filters.
+  obj = FPDFPage_GetObject(page, 38);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  ASSERT_EQ(2, FPDFImageObj_GetImageFilterCount(obj));
+  len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  static constexpr char kASCIIHexDecode[] = "ASCIIHexDecode";
+  EXPECT_EQ(sizeof(kASCIIHexDecode),
+            FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
+  EXPECT_STREQ(kASCIIHexDecode, buf.data());
+
+  len = FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0);
+  buf.clear();
+  buf.resize(len);
+  static constexpr char kDCTDecode[] = "DCTDecode";
+  EXPECT_EQ(sizeof(kDCTDecode),
+            FPDFImageObj_GetImageFilter(obj, 1, buf.data(), len));
+  EXPECT_STREQ(kDCTDecode, buf.data());
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GetImageMetadata) {
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Check that getting the metadata of a null object would fail.
+  FPDF_IMAGEOBJ_METADATA metadata;
+  EXPECT_FALSE(FPDFImageObj_GetImageMetadata(nullptr, page, &metadata));
+
+  // Check that receiving the metadata with a null metadata object would fail.
+  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 35);
+  EXPECT_FALSE(FPDFImageObj_GetImageMetadata(obj, page, nullptr));
+
+  // Check that when retrieving an image object's metadata without passing in
+  // |page|, all values are correct, with the last two being default values.
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, nullptr, &metadata));
+  EXPECT_EQ(7, metadata.marked_content_id);
+  EXPECT_EQ(92u, metadata.width);
+  EXPECT_EQ(68u, metadata.height);
+  EXPECT_FLOAT_EQ(96.0f, metadata.horizontal_dpi);
+  EXPECT_FLOAT_EQ(96.0f, metadata.vertical_dpi);
+  EXPECT_EQ(0u, metadata.bits_per_pixel);
+  EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace);
+
+  // Verify the metadata of a bitmap image with indexed colorspace.
+  ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
+  EXPECT_EQ(7, metadata.marked_content_id);
+  EXPECT_EQ(92u, metadata.width);
+  EXPECT_EQ(68u, metadata.height);
+  EXPECT_FLOAT_EQ(96.0f, metadata.horizontal_dpi);
+  EXPECT_FLOAT_EQ(96.0f, metadata.vertical_dpi);
+  EXPECT_EQ(1u, metadata.bits_per_pixel);
+  EXPECT_EQ(FPDF_COLORSPACE_INDEXED, metadata.colorspace);
+
+  // Verify the metadata of an image with RGB colorspace.
+  obj = FPDFPage_GetObject(page, 37);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
+  ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
+  EXPECT_EQ(9, metadata.marked_content_id);
+  EXPECT_EQ(126u, metadata.width);
+  EXPECT_EQ(106u, metadata.height);
+  EXPECT_FLOAT_EQ(162.173752f, metadata.horizontal_dpi);
+  EXPECT_FLOAT_EQ(162.555878f, metadata.vertical_dpi);
+  EXPECT_EQ(24u, metadata.bits_per_pixel);
+  EXPECT_EQ(FPDF_COLORSPACE_DEVICERGB, metadata.colorspace);
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_edit_unittest.cpp b/fpdfsdk/fpdf_edit_unittest.cpp
new file mode 100644
index 0000000..6aa24f4
--- /dev/null
+++ b/fpdfsdk/fpdf_edit_unittest.cpp
@@ -0,0 +1,67 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_edit.h"
+
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class PDFEditTest : public testing::Test {
+  void SetUp() override { CPDF_PageModule::Create(); }
+  void TearDown() override { CPDF_PageModule::Destroy(); }
+};
+
+TEST_F(PDFEditTest, LineJoin) {
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, -1));
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, FPDF_LINEJOIN_MITER));
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, FPDF_LINEJOIN_ROUND));
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, FPDF_LINEJOIN_BEVEL));
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, 3));
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, 1000));
+
+  FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(0, 0);
+  EXPECT_EQ(FPDF_LINEJOIN_MITER, FPDFPageObj_GetLineJoin(path));
+
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(path, -1));
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(path, 3));
+  EXPECT_FALSE(FPDFPageObj_SetLineJoin(path, 1000));
+
+  EXPECT_TRUE(FPDFPageObj_SetLineJoin(path, FPDF_LINEJOIN_BEVEL));
+  EXPECT_EQ(FPDF_LINEJOIN_BEVEL, FPDFPageObj_GetLineJoin(path));
+
+  EXPECT_TRUE(FPDFPageObj_SetLineJoin(path, FPDF_LINEJOIN_ROUND));
+  EXPECT_EQ(FPDF_LINEJOIN_ROUND, FPDFPageObj_GetLineJoin(path));
+
+  EXPECT_TRUE(FPDFPageObj_SetLineJoin(path, FPDF_LINEJOIN_MITER));
+  EXPECT_EQ(FPDF_LINEJOIN_MITER, FPDFPageObj_GetLineJoin(path));
+
+  FPDFPageObj_Destroy(path);
+}
+
+TEST_F(PDFEditTest, LineCap) {
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(nullptr, -1));
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(nullptr, FPDF_LINECAP_BUTT));
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(nullptr, FPDF_LINECAP_ROUND));
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(nullptr, FPDF_LINECAP_PROJECTING_SQUARE));
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(nullptr, 3));
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(nullptr, 1000));
+
+  FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(0, 0);
+  EXPECT_EQ(FPDF_LINECAP_BUTT, FPDFPageObj_GetLineCap(path));
+
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(path, -1));
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(path, 3));
+  EXPECT_FALSE(FPDFPageObj_SetLineCap(path, 1000));
+
+  EXPECT_TRUE(FPDFPageObj_SetLineCap(path, FPDF_LINECAP_PROJECTING_SQUARE));
+  EXPECT_EQ(FPDF_LINECAP_PROJECTING_SQUARE, FPDFPageObj_GetLineCap(path));
+
+  EXPECT_TRUE(FPDFPageObj_SetLineCap(path, FPDF_LINECAP_ROUND));
+  EXPECT_EQ(FPDF_LINECAP_ROUND, FPDFPageObj_GetLineCap(path));
+
+  EXPECT_TRUE(FPDFPageObj_SetLineCap(path, FPDF_LINECAP_BUTT));
+  EXPECT_EQ(FPDF_LINECAP_BUTT, FPDFPageObj_GetLineCap(path));
+
+  FPDFPageObj_Destroy(path);
+}
diff --git a/fpdfsdk/fpdf_editimg.cpp b/fpdfsdk/fpdf_editimg.cpp
new file mode 100644
index 0000000..ecd11d1
--- /dev/null
+++ b/fpdfsdk/fpdf_editimg.cpp
@@ -0,0 +1,347 @@
+// 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 "public/fpdf_edit.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_page.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_name.h"
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "fpdfsdk/cpdfsdk_customaccess.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+// These checks ensure the consistency of colorspace values across core/ and
+// public/.
+static_assert(PDFCS_DEVICEGRAY == FPDF_COLORSPACE_DEVICEGRAY,
+              "PDFCS_DEVICEGRAY value mismatch");
+static_assert(PDFCS_DEVICERGB == FPDF_COLORSPACE_DEVICERGB,
+              "PDFCS_DEVICERGB value mismatch");
+static_assert(PDFCS_DEVICECMYK == FPDF_COLORSPACE_DEVICECMYK,
+              "PDFCS_DEVICECMYK value mismatch");
+static_assert(PDFCS_CALGRAY == FPDF_COLORSPACE_CALGRAY,
+              "PDFCS_CALGRAY value mismatch");
+static_assert(PDFCS_CALRGB == FPDF_COLORSPACE_CALRGB,
+              "PDFCS_CALRGB value mismatch");
+static_assert(PDFCS_LAB == FPDF_COLORSPACE_LAB, "PDFCS_LAB value mismatch");
+static_assert(PDFCS_ICCBASED == FPDF_COLORSPACE_ICCBASED,
+              "PDFCS_ICCBASED value mismatch");
+static_assert(PDFCS_SEPARATION == FPDF_COLORSPACE_SEPARATION,
+              "PDFCS_SEPARATION value mismatch");
+static_assert(PDFCS_DEVICEN == FPDF_COLORSPACE_DEVICEN,
+              "PDFCS_DEVICEN value mismatch");
+static_assert(PDFCS_INDEXED == FPDF_COLORSPACE_INDEXED,
+              "PDFCS_INDEXED value mismatch");
+static_assert(PDFCS_PATTERN == FPDF_COLORSPACE_PATTERN,
+              "PDFCS_PATTERN value mismatch");
+
+RetainPtr<IFX_SeekableReadStream> MakeSeekableReadStream(
+    FPDF_FILEACCESS* pFileAccess) {
+  return pdfium::MakeRetain<CPDFSDK_CustomAccess>(pFileAccess);
+}
+
+CPDF_ImageObject* CPDFImageObjectFromFPDFPageObject(
+    FPDF_PAGEOBJECT image_object) {
+  CPDF_PageObject* pPageObject = CPDFPageObjectFromFPDFPageObject(image_object);
+  return pPageObject ? pPageObject->AsImage() : nullptr;
+}
+
+bool LoadJpegHelper(FPDF_PAGE* pages,
+                    int count,
+                    FPDF_PAGEOBJECT image_object,
+                    FPDF_FILEACCESS* file_access,
+                    bool inline_jpeg) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj)
+    return false;
+
+  if (!file_access)
+    return false;
+
+  if (pages) {
+    for (int index = 0; index < count; index++) {
+      CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
+      if (pPage)
+        pImgObj->GetImage()->ResetCache(pPage);
+    }
+  }
+
+  RetainPtr<IFX_SeekableReadStream> pFile = MakeSeekableReadStream(file_access);
+  if (inline_jpeg)
+    pImgObj->GetImage()->SetJpegImageInline(pFile);
+  else
+    pImgObj->GetImage()->SetJpegImage(pFile);
+  pImgObj->SetDirty(true);
+  return true;
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDFPageObj_NewImageObj(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  auto pImageObj = pdfium::MakeUnique<CPDF_ImageObject>();
+  pImageObj->SetImage(pdfium::MakeRetain<CPDF_Image>(pDoc));
+
+  // Caller takes ownership.
+  return FPDFPageObjectFromCPDFPageObject(pImageObj.release());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_LoadJpegFile(FPDF_PAGE* pages,
+                          int count,
+                          FPDF_PAGEOBJECT image_object,
+                          FPDF_FILEACCESS* file_access) {
+  return LoadJpegHelper(pages, count, image_object, file_access, false);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_LoadJpegFileInline(FPDF_PAGE* pages,
+                                int count,
+                                FPDF_PAGEOBJECT image_object,
+                                FPDF_FILEACCESS* file_access) {
+  return LoadJpegHelper(pages, count, image_object, file_access, true);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object,
+                       double* a,
+                       double* b,
+                       double* c,
+                       double* d,
+                       double* e,
+                       double* f) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj || !a || !b || !c || !d || !e || !f)
+    return false;
+
+  const CFX_Matrix& matrix = pImgObj->matrix();
+  *a = matrix.a;
+  *b = matrix.b;
+  *c = matrix.c;
+  *d = matrix.d;
+  *e = matrix.e;
+  *f = matrix.f;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object,
+                       double a,
+                       double b,
+                       double c,
+                       double d,
+                       double e,
+                       double f) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj)
+    return false;
+
+  pImgObj->set_matrix(CFX_Matrix(static_cast<float>(a), static_cast<float>(b),
+                                 static_cast<float>(c), static_cast<float>(d),
+                                 static_cast<float>(e), static_cast<float>(f)));
+  pImgObj->CalcBoundingBox();
+  pImgObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_SetBitmap(FPDF_PAGE* pages,
+                       int count,
+                       FPDF_PAGEOBJECT image_object,
+                       FPDF_BITMAP bitmap) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj)
+    return false;
+
+  if (!bitmap)
+    return false;
+
+  if (pages) {
+    for (int index = 0; index < count; index++) {
+      CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
+      if (pPage)
+        pImgObj->GetImage()->ResetCache(pPage);
+    }
+  }
+
+  RetainPtr<CFX_DIBitmap> holder(CFXDIBitmapFromFPDFBitmap(bitmap));
+  pImgObj->GetImage()->SetImage(holder);
+  pImgObj->CalcBoundingBox();
+  pImgObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
+FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj)
+    return nullptr;
+
+  RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
+  if (!pImg)
+    return nullptr;
+
+  RetainPtr<CFX_DIBBase> pSource = pImg->LoadDIBBase();
+  if (!pSource)
+    return nullptr;
+
+  RetainPtr<CFX_DIBitmap> pBitmap;
+  // If the source image has a representation of 1 bit per pixel, then convert
+  // it to a grayscale bitmap having 1 byte per pixel, since bitmaps have no
+  // concept of bits. Otherwise, convert the source image to a bitmap directly,
+  // retaining its color representation.
+  if (pSource->GetBPP() == 1)
+    pBitmap = pSource->CloneConvert(FXDIB_8bppRgb);
+  else
+    pBitmap = pSource->Clone(nullptr);
+
+  return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object,
+                                 void* buffer,
+                                 unsigned long buflen) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj)
+    return 0;
+
+  RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
+  if (!pImg)
+    return 0;
+
+  CPDF_Stream* pImgStream = pImg->GetStream();
+  if (!pImgStream)
+    return 0;
+
+  return DecodeStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFImageObj_GetImageDataRaw(FPDF_PAGEOBJECT image_object,
+                             void* buffer,
+                             unsigned long buflen) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj)
+    return 0;
+
+  RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
+  if (!pImg)
+    return 0;
+
+  CPDF_Stream* pImgStream = pImg->GetStream();
+  if (!pImgStream)
+    return 0;
+
+  return GetRawStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFImageObj_GetImageFilterCount(FPDF_PAGEOBJECT image_object) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj)
+    return 0;
+
+  RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
+  if (!pImg)
+    return 0;
+
+  CPDF_Dictionary* pDict = pImg->GetDict();
+  CPDF_Object* pFilter = pDict ? pDict->GetDirectObjectFor("Filter") : nullptr;
+  if (!pFilter)
+    return 0;
+
+  if (pFilter->IsArray())
+    return pFilter->AsArray()->size();
+  if (pFilter->IsName())
+    return 1;
+
+  return 0;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFImageObj_GetImageFilter(FPDF_PAGEOBJECT image_object,
+                            int index,
+                            void* buffer,
+                            unsigned long buflen) {
+  if (index < 0 || index >= FPDFImageObj_GetImageFilterCount(image_object))
+    return 0;
+
+  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
+  CPDF_Object* pFilter =
+      pObj->AsImage()->GetImage()->GetDict()->GetDirectObjectFor("Filter");
+  ByteString bsFilter;
+  if (pFilter->IsName())
+    bsFilter = pFilter->AsName()->GetString();
+  else
+    bsFilter = pFilter->AsArray()->GetStringAt(index);
+
+  unsigned long len = bsFilter.GetLength() + 1;
+  if (buffer && len <= buflen)
+    memcpy(buffer, bsFilter.c_str(), len);
+  return len;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object,
+                              FPDF_PAGE page,
+                              FPDF_IMAGEOBJ_METADATA* metadata) {
+  CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
+  if (!pImgObj || !metadata)
+    return false;
+
+  RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
+  if (!pImg)
+    return false;
+
+  metadata->marked_content_id = pImgObj->m_ContentMarks.GetMarkedContentID();
+
+  const int nPixelWidth = pImg->GetPixelWidth();
+  const int nPixelHeight = pImg->GetPixelHeight();
+  metadata->width = nPixelWidth;
+  metadata->height = nPixelHeight;
+
+  const float nWidth = pImgObj->GetRect().Width();
+  const float nHeight = pImgObj->GetRect().Height();
+  constexpr int nPointsPerInch = 72;
+  if (nWidth != 0 && nHeight != 0) {
+    metadata->horizontal_dpi = nPixelWidth / nWidth * nPointsPerInch;
+    metadata->vertical_dpi = nPixelHeight / nHeight * nPointsPerInch;
+  }
+
+  metadata->bits_per_pixel = 0;
+  metadata->colorspace = FPDF_COLORSPACE_UNKNOWN;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage || !pPage->GetDocument() || !pImg->GetStream())
+    return true;
+
+  auto pSource = pdfium::MakeRetain<CPDF_DIB>();
+  CPDF_DIB::LoadState ret = pSource->StartLoadDIBBase(
+      pPage->GetDocument(), pImg->GetStream(), false, nullptr,
+      pPage->m_pPageResources.Get(), false, 0, false);
+  if (ret == CPDF_DIB::LoadState::kFail)
+    return true;
+
+  metadata->bits_per_pixel = pSource->GetBPP();
+  if (pSource->GetColorSpace())
+    metadata->colorspace = pSource->GetColorSpace()->GetFamily();
+
+  return true;
+}
diff --git a/fpdfsdk/fpdf_editimg_unittest.cpp b/fpdfsdk/fpdf_editimg_unittest.cpp
new file mode 100644
index 0000000..09ec71e
--- /dev/null
+++ b/fpdfsdk/fpdf_editimg_unittest.cpp
@@ -0,0 +1,185 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_edit.h"
+
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/file_util.h"
+
+class PDFEditImgTest : public testing::Test {
+  void SetUp() override { CPDF_PageModule::Create(); }
+  void TearDown() override { CPDF_PageModule::Destroy(); }
+};
+
+TEST_F(PDFEditImgTest, InsertObjectWithInvalidPage) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDFPage_InsertObject(nullptr, nullptr);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDFPage_InsertObject(page, nullptr);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
+  FPDFPage_InsertObject(nullptr, page_image);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDF_ClosePage(page);
+  FPDF_CloseDocument(doc);
+}
+
+TEST_F(PDFEditImgTest, NewImageObj) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
+  FPDFPage_InsertObject(page, page_image);
+  EXPECT_EQ(1, FPDFPage_CountObjects(page));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  FPDF_ClosePage(page);
+  FPDF_CloseDocument(doc);
+}
+
+TEST_F(PDFEditImgTest, NewImageObjGenerateContent) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
+  EXPECT_EQ(0, FPDFPage_CountObjects(page));
+
+  constexpr int kBitmapSize = 50;
+  FPDF_BITMAP bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 0);
+  FPDFBitmap_FillRect(bitmap, 0, 0, kBitmapSize, kBitmapSize, 0x00000000);
+  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(bitmap));
+  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(bitmap));
+
+  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
+  ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, page_image, bitmap));
+  ASSERT_TRUE(
+      FPDFImageObj_SetMatrix(page_image, kBitmapSize, 0, 0, kBitmapSize, 0, 0));
+  FPDFPage_InsertObject(page, page_image);
+  EXPECT_EQ(1, FPDFPage_CountObjects(page));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+
+  FPDFBitmap_Destroy(bitmap);
+  FPDF_ClosePage(page);
+  FPDF_CloseDocument(doc);
+}
+
+TEST_F(PDFEditImgTest, NewImageObjLoadJpeg) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 200, 200));
+  ASSERT_TRUE(page);
+
+  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
+  ASSERT_TRUE(image);
+
+  FileAccessForTesting file_access("mona_lisa.jpg");
+  FPDF_PAGE temp_page = page.get();
+  EXPECT_TRUE(
+      FPDFImageObj_LoadJpegFile(&temp_page, 1, image.get(), &file_access));
+
+  ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(image.get()));
+  EXPECT_TRUE(bitmap);
+  EXPECT_EQ(120, FPDFBitmap_GetWidth(bitmap.get()));
+  EXPECT_EQ(120, FPDFBitmap_GetHeight(bitmap.get()));
+}
+
+TEST_F(PDFEditImgTest, NewImageObjLoadJpegInline) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 200, 200));
+  ASSERT_TRUE(page);
+
+  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
+  ASSERT_TRUE(image);
+
+  FileAccessForTesting file_access("mona_lisa.jpg");
+  FPDF_PAGE temp_page = page.get();
+  EXPECT_TRUE(FPDFImageObj_LoadJpegFileInline(&temp_page, 1, image.get(),
+                                              &file_access));
+
+  ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(image.get()));
+  EXPECT_TRUE(bitmap);
+  EXPECT_EQ(120, FPDFBitmap_GetWidth(bitmap.get()));
+  EXPECT_EQ(120, FPDFBitmap_GetHeight(bitmap.get()));
+}
+
+TEST_F(PDFEditImgTest, SetBitmap) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
+  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
+  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(100, 100, 0));
+
+  FPDF_PAGE page_ptr = page.get();
+  FPDF_PAGE* pages = &page_ptr;
+  EXPECT_TRUE(FPDFImageObj_SetBitmap(nullptr, 1, image.get(), bitmap.get()));
+  EXPECT_TRUE(FPDFImageObj_SetBitmap(pages, 0, image.get(), bitmap.get()));
+  EXPECT_FALSE(FPDFImageObj_SetBitmap(pages, 1, nullptr, bitmap.get()));
+  EXPECT_FALSE(FPDFImageObj_SetBitmap(pages, 1, image.get(), nullptr));
+}
+
+TEST_F(PDFEditImgTest, GetSetImageMatrix) {
+  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
+  FPDF_PAGEOBJECT image = FPDFPageObj_NewImageObj(doc);
+
+  double a;
+  double b;
+  double c;
+  double d;
+  double e;
+  double f;
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, nullptr, nullptr, nullptr,
+                                      nullptr, nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, nullptr, nullptr, nullptr,
+                                      nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, nullptr, nullptr,
+                                      nullptr, nullptr));
+  EXPECT_FALSE(
+      FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, nullptr, nullptr, nullptr));
+  EXPECT_FALSE(
+      FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, nullptr, nullptr, nullptr));
+  EXPECT_FALSE(
+      FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, &e, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, &e, &f));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, nullptr, &c, &d, &e, &f));
+
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, nullptr, nullptr, nullptr, nullptr,
+                                      nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, nullptr, nullptr, nullptr,
+                                      nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, nullptr, nullptr, nullptr,
+                                      nullptr));
+  EXPECT_FALSE(
+      FPDFImageObj_GetMatrix(image, &a, &b, &c, nullptr, nullptr, nullptr));
+  EXPECT_FALSE(
+      FPDFImageObj_GetMatrix(image, &a, &b, &c, nullptr, nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, nullptr, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, nullptr));
+  EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, nullptr, &c, &d, &e, &f));
+
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(1.0, a);
+  EXPECT_DOUBLE_EQ(0.0, b);
+  EXPECT_DOUBLE_EQ(0.0, c);
+  EXPECT_DOUBLE_EQ(1.0, d);
+  EXPECT_DOUBLE_EQ(0.0, e);
+  EXPECT_DOUBLE_EQ(0.0, f);
+
+  EXPECT_TRUE(FPDFImageObj_SetMatrix(image, 1, 2, 3, 4, 5, 6));
+  EXPECT_TRUE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, &f));
+  EXPECT_DOUBLE_EQ(1.0, a);
+  EXPECT_DOUBLE_EQ(2.0, b);
+  EXPECT_DOUBLE_EQ(3.0, c);
+  EXPECT_DOUBLE_EQ(4.0, d);
+  EXPECT_DOUBLE_EQ(5.0, e);
+  EXPECT_DOUBLE_EQ(6.0, f);
+
+  FPDFPageObj_Destroy(image);
+  FPDF_CloseDocument(doc);
+}
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
new file mode 100644
index 0000000..8e979e0
--- /dev/null
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -0,0 +1,869 @@
+// 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 "public/fpdf_edit.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "constants/page_object.h"
+#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
+#include "core/fpdfapi/page/cpdf_colorspace.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_imageobject.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageobject.h"
+#include "core/fpdfapi/page/cpdf_pathobject.h"
+#include "core/fpdfapi/page/cpdf_shadingobject.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fpdfdoc/cpdf_annotlist.h"
+#include "core/fxcrt/fx_extension.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/fpdf_formfill.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#endif  // PDF_ENABLE_XFA
+
+namespace {
+
+static_assert(FPDF_PAGEOBJ_TEXT == CPDF_PageObject::TEXT,
+              "FPDF_PAGEOBJ_TEXT/CPDF_PageObject::TEXT mismatch");
+static_assert(FPDF_PAGEOBJ_PATH == CPDF_PageObject::PATH,
+              "FPDF_PAGEOBJ_PATH/CPDF_PageObject::PATH mismatch");
+static_assert(FPDF_PAGEOBJ_IMAGE == CPDF_PageObject::IMAGE,
+              "FPDF_PAGEOBJ_IMAGE/CPDF_PageObject::IMAGE mismatch");
+static_assert(FPDF_PAGEOBJ_SHADING == CPDF_PageObject::SHADING,
+              "FPDF_PAGEOBJ_SHADING/CPDF_PageObject::SHADING mismatch");
+static_assert(FPDF_PAGEOBJ_FORM == CPDF_PageObject::FORM,
+              "FPDF_PAGEOBJ_FORM/CPDF_PageObject::FORM mismatch");
+
+bool IsPageObject(CPDF_Page* pPage) {
+  if (!pPage)
+    return false;
+
+  const CPDF_Dictionary* pFormDict = pPage->GetDict();
+  if (!pFormDict->KeyExist("Type"))
+    return false;
+
+  const CPDF_Object* pObject = pFormDict->GetObjectFor("Type")->GetDirect();
+  return pObject && !pObject->GetString().Compare("Page");
+}
+
+void CalcBoundingBox(CPDF_PageObject* pPageObj) {
+  switch (pPageObj->GetType()) {
+    case CPDF_PageObject::TEXT: {
+      break;
+    }
+    case CPDF_PageObject::PATH: {
+      CPDF_PathObject* pPathObj = pPageObj->AsPath();
+      pPathObj->CalcBoundingBox();
+      break;
+    }
+    case CPDF_PageObject::IMAGE: {
+      CPDF_ImageObject* pImageObj = pPageObj->AsImage();
+      pImageObj->CalcBoundingBox();
+      break;
+    }
+    case CPDF_PageObject::SHADING: {
+      CPDF_ShadingObject* pShadingObj = pPageObj->AsShading();
+      pShadingObj->CalcBoundingBox();
+      break;
+    }
+    case CPDF_PageObject::FORM: {
+      CPDF_FormObject* pFormObj = pPageObj->AsForm();
+      pFormObj->CalcBoundingBox();
+      break;
+    }
+    default: {
+      NOTREACHED();
+      break;
+    }
+  }
+}
+
+CPDF_Dictionary* GetMarkParamDict(FPDF_PAGEOBJECTMARK mark) {
+  CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  return pMarkItem ? pMarkItem->GetParam() : nullptr;
+}
+
+CPDF_Dictionary* GetOrCreateMarkParamsDict(FPDF_DOCUMENT document,
+                                           FPDF_PAGEOBJECTMARK mark) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  if (!pMarkItem)
+    return nullptr;
+
+  CPDF_Dictionary* pParams = pMarkItem->GetParam();
+
+  // If the Params dict does not exist, create a new one.
+  if (!pParams) {
+    auto new_dict = pDoc->New<CPDF_Dictionary>();
+    pParams = new_dict.Get();
+    pMarkItem->SetDirectDict(std::move(new_dict));
+  }
+
+  return pParams;
+}
+
+bool PageObjectContainsMark(CPDF_PageObject* pPageObj,
+                            FPDF_PAGEOBJECTMARK mark) {
+  const CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  return pMarkItem && pPageObj->m_ContentMarks.ContainsItem(pMarkItem);
+}
+
+CPDF_FormObject* CPDFFormObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  return pPageObj ? pPageObj->AsForm() : nullptr;
+}
+
+const CPDF_PageObjectHolder* CPDFPageObjHolderFromFPDFFormObject(
+    FPDF_PAGEOBJECT page_object) {
+  CPDF_FormObject* pFormObject = CPDFFormObjectFromFPDFPageObject(page_object);
+  return pFormObject ? pFormObject->form() : nullptr;
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_CreateNewDocument() {
+  auto pDoc = pdfium::MakeUnique<CPDF_Document>(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>());
+  pDoc->CreateNewDoc();
+
+  time_t currentTime;
+  ByteString DateStr;
+  if (IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS)) {
+    if (FXSYS_time(&currentTime) != -1) {
+      tm* pTM = FXSYS_localtime(&currentTime);
+      if (pTM) {
+        DateStr = ByteString::Format(
+            "D:%04d%02d%02d%02d%02d%02d", pTM->tm_year + 1900, pTM->tm_mon + 1,
+            pTM->tm_mday, pTM->tm_hour, pTM->tm_min, pTM->tm_sec);
+      }
+    }
+  }
+
+  CPDF_Dictionary* pInfoDict = pDoc->GetInfo();
+  if (pInfoDict) {
+    if (IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
+      pInfoDict->SetNewFor<CPDF_String>("CreationDate", DateStr, false);
+    pInfoDict->SetNewFor<CPDF_String>("Creator", L"PDFium");
+  }
+
+#ifdef PDF_ENABLE_XFA
+  pDoc->SetExtension(pdfium::MakeUnique<CPDFXFA_Context>(pDoc.get()));
+#endif  // PDF_ENABLE_XFA
+
+  // Caller takes ownership of pDoc.
+  return FPDFDocumentFromCPDFDocument(pDoc.release());
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_Delete(FPDF_DOCUMENT document,
+                                               int page_index) {
+  auto* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return;
+
+  CPDF_Document::Extension* pExtension = pDoc->GetExtension();
+  if (pExtension) {
+    pExtension->DeletePage(page_index);
+    return;
+  }
+
+  pDoc->DeletePage(page_index);
+}
+
+FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDFPage_New(FPDF_DOCUMENT document,
+                                                 int page_index,
+                                                 double width,
+                                                 double height) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  page_index = pdfium::clamp(page_index, 0, pDoc->GetPageCount());
+  CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(page_index);
+  if (!pPageDict)
+    return nullptr;
+
+  pPageDict->SetRectFor(pdfium::page_object::kMediaBox,
+                        CFX_FloatRect(0, 0, width, height));
+  pPageDict->SetNewFor<CPDF_Number>(pdfium::page_object::kRotate, 0);
+  pPageDict->SetNewFor<CPDF_Dictionary>(pdfium::page_object::kResources);
+
+#ifdef PDF_ENABLE_XFA
+  if (pDoc->GetExtension()) {
+    auto pXFAPage = pdfium::MakeRetain<CPDFXFA_Page>(pDoc, page_index);
+    pXFAPage->LoadPDFPageFromDict(pPageDict);
+    return FPDFPageFromIPDFPage(pXFAPage.Leak());  // Caller takes ownership.
+  }
+#endif  // PDF_ENABLE_XFA
+
+  auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pPageDict);
+  pPage->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(pPage.Get()));
+  pPage->ParseContent();
+
+  return FPDFPageFromIPDFPage(pPage.Leak());  // Caller takes ownership.
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetRotation(FPDF_PAGE page) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  return IsPageObject(pPage) ? pPage->GetPageRotation() : -1;
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertObject(FPDF_PAGE page,
+                                                     FPDF_PAGEOBJECT page_obj) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_obj);
+  if (!pPageObj)
+    return;
+
+  std::unique_ptr<CPDF_PageObject> pPageObjHolder(pPageObj);
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!IsPageObject(pPage))
+    return;
+
+  pPageObj->SetDirty(true);
+  pPage->AppendPageObject(std::move(pPageObjHolder));
+  CalcBoundingBox(pPageObj);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPage_RemoveObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_obj) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_obj);
+  if (!pPageObj)
+    return false;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!IsPageObject(pPage))
+    return false;
+
+  return pPage->RemovePageObject(pPageObj);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!IsPageObject(pPage))
+    return -1;
+
+  return pPage->GetPageObjectCount();
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPage_GetObject(FPDF_PAGE page,
+                                                             int index) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!IsPageObject(pPage))
+    return nullptr;
+
+  return FPDFPageObjectFromCPDFPageObject(pPage->GetPageObjectByIndex(index));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_HasTransparency(FPDF_PAGE page) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  return pPage && pPage->BackgroundAlphaNeeded();
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPageObj_Destroy(FPDF_PAGEOBJECT page_obj) {
+  delete CPDFPageObjectFromFPDFPageObject(page_obj);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_CountMarks(FPDF_PAGEOBJECT page_object) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return -1;
+
+  return pPageObj->m_ContentMarks.CountItems();
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
+FPDFPageObj_GetMark(FPDF_PAGEOBJECT page_object, unsigned long index) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return nullptr;
+
+  auto& mark = pPageObj->m_ContentMarks;
+  if (index >= mark.CountItems())
+    return nullptr;
+
+  return FPDFPageObjectMarkFromCPDFContentMarkItem(mark.GetItem(index));
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
+FPDFPageObj_AddMark(FPDF_PAGEOBJECT page_object, FPDF_BYTESTRING name) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return nullptr;
+
+  auto& mark = pPageObj->m_ContentMarks;
+  mark.AddMark(name);
+  unsigned long index = mark.CountItems() - 1;
+  pPageObj->SetDirty(true);
+  return FPDFPageObjectMarkFromCPDFContentMarkItem(mark.GetItem(index));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_RemoveMark(FPDF_PAGEOBJECT page_object, FPDF_PAGEOBJECTMARK mark) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  if (!pPageObj || !pMarkItem)
+    return false;
+
+  bool result = pPageObj->m_ContentMarks.RemoveMark(pMarkItem);
+  if (result)
+    pPageObj->SetDirty(true);
+
+  return result;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetName(FPDF_PAGEOBJECTMARK mark,
+                        void* buffer,
+                        unsigned long buflen,
+                        unsigned long* out_buflen) {
+  if (!mark || !out_buflen)
+    return false;
+
+  const CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+
+  *out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
+      WideString::FromUTF8(pMarkItem->GetName().AsStringView()), buffer,
+      buflen);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObjMark_CountParams(FPDF_PAGEOBJECTMARK mark) {
+  if (!mark)
+    return -1;
+
+  const CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+
+  const CPDF_Dictionary* pParams = pMarkItem->GetParam();
+  return pParams ? pParams->size() : 0;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamKey(FPDF_PAGEOBJECTMARK mark,
+                            unsigned long index,
+                            void* buffer,
+                            unsigned long buflen,
+                            unsigned long* out_buflen) {
+  if (!out_buflen)
+    return false;
+
+  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  if (!pParams)
+    return false;
+
+  CPDF_DictionaryLocker locker(pParams);
+  for (auto& it : locker) {
+    if (index == 0) {
+      *out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
+          WideString::FromUTF8(it.first.AsStringView()), buffer, buflen);
+      return true;
+    }
+    --index;
+  }
+
+  return false;
+}
+
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDFPageObjMark_GetParamValueType(FPDF_PAGEOBJECTMARK mark,
+                                  FPDF_BYTESTRING key) {
+  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  if (!pParams)
+    return FPDF_OBJECT_UNKNOWN;
+
+  const CPDF_Object* pObject = pParams->GetObjectFor(key);
+  return pObject ? pObject->GetType() : FPDF_OBJECT_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamIntValue(FPDF_PAGEOBJECTMARK mark,
+                                 FPDF_BYTESTRING key,
+                                 int* out_value) {
+  if (!out_value)
+    return false;
+
+  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  if (!pParams)
+    return false;
+
+  const CPDF_Object* pObj = pParams->GetObjectFor(key);
+  if (!pObj || !pObj->IsNumber())
+    return false;
+
+  *out_value = pObj->GetInteger();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamStringValue(FPDF_PAGEOBJECTMARK mark,
+                                    FPDF_BYTESTRING key,
+                                    void* buffer,
+                                    unsigned long buflen,
+                                    unsigned long* out_buflen) {
+  if (!out_buflen)
+    return false;
+
+  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  if (!pParams)
+    return false;
+
+  const CPDF_Object* pObj = pParams->GetObjectFor(key);
+  if (!pObj || !pObj->IsString())
+    return false;
+
+  *out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
+      WideString::FromUTF8(pObj->GetString().AsStringView()), buffer, buflen);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamBlobValue(FPDF_PAGEOBJECTMARK mark,
+                                  FPDF_BYTESTRING key,
+                                  void* buffer,
+                                  unsigned long buflen,
+                                  unsigned long* out_buflen) {
+  if (!out_buflen)
+    return false;
+
+  const CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  if (!pParams)
+    return false;
+
+  const CPDF_Object* pObj = pParams->GetObjectFor(key);
+  if (!pObj || !pObj->IsString())
+    return false;
+
+  ByteString result = pObj->GetString();
+  unsigned long len = result.GetLength();
+
+  if (buffer && len <= buflen)
+    memcpy(buffer, result.c_str(), len);
+
+  *out_buflen = len;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT page_object) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return false;
+
+  if (pPageObj->m_GeneralState.GetBlendType() != BlendMode::kNormal)
+    return true;
+
+  const CPDF_Dictionary* pSMaskDict =
+      ToDictionary(pPageObj->m_GeneralState.GetSoftMask());
+  if (pSMaskDict)
+    return true;
+
+  if (pPageObj->m_GeneralState.GetFillAlpha() != 1.0f)
+    return true;
+
+  if (pPageObj->IsPath() && pPageObj->m_GeneralState.GetStrokeAlpha() != 1.0f)
+    return true;
+
+  if (!pPageObj->IsForm())
+    return false;
+
+  const CPDF_Form* pForm = pPageObj->AsForm()->form();
+  if (!pForm)
+    return false;
+
+  const CPDF_Transparency& trans = pForm->GetTransparency();
+  return trans.IsGroup() || trans.IsIsolated();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetIntParam(FPDF_DOCUMENT document,
+                            FPDF_PAGEOBJECT page_object,
+                            FPDF_PAGEOBJECTMARK mark,
+                            FPDF_BYTESTRING key,
+                            int value) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
+    return false;
+
+  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  if (!pParams)
+    return false;
+
+  pParams->SetNewFor<CPDF_Number>(key, value);
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetStringParam(FPDF_DOCUMENT document,
+                               FPDF_PAGEOBJECT page_object,
+                               FPDF_PAGEOBJECTMARK mark,
+                               FPDF_BYTESTRING key,
+                               FPDF_BYTESTRING value) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
+    return false;
+
+  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  if (!pParams)
+    return false;
+
+  pParams->SetNewFor<CPDF_String>(key, value, false);
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetBlobParam(FPDF_DOCUMENT document,
+                             FPDF_PAGEOBJECT page_object,
+                             FPDF_PAGEOBJECTMARK mark,
+                             FPDF_BYTESTRING key,
+                             void* value,
+                             unsigned long value_len) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
+    return false;
+
+  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  if (!pParams)
+    return false;
+
+  if (!value && value_len > 0)
+    return false;
+
+  pParams->SetNewFor<CPDF_String>(
+      key, ByteString(static_cast<const char*>(value), value_len), true);
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_RemoveParam(FPDF_PAGEOBJECT page_object,
+                            FPDF_PAGEOBJECTMARK mark,
+                            FPDF_BYTESTRING key) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return false;
+
+  CPDF_Dictionary* pParams = GetMarkParamDict(mark);
+  if (!pParams)
+    return false;
+
+  auto removed = pParams->RemoveFor(key);
+  if (!removed)
+    return false;
+
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT page_object) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  return pPageObj ? pPageObj->GetType() : FPDF_PAGEOBJ_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GenerateContent(FPDF_PAGE page) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!IsPageObject(pPage))
+    return false;
+
+  CPDF_PageContentGenerator CG(pPage);
+  CG.GenerateContent();
+  return true;
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDFPageObj_Transform(FPDF_PAGEOBJECT page_object,
+                      double a,
+                      double b,
+                      double c,
+                      double d,
+                      double e,
+                      double f) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return;
+
+  CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f);
+  pPageObj->Transform(matrix);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page_object,
+                         FPDF_BYTESTRING blend_mode) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return;
+
+  pPageObj->m_GeneralState.SetBlendMode(blend_mode);
+  pPageObj->SetDirty(true);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_TransformAnnots(FPDF_PAGE page,
+                                                        double a,
+                                                        double b,
+                                                        double c,
+                                                        double d,
+                                                        double e,
+                                                        double f) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  CPDF_AnnotList AnnotList(pPage);
+  for (size_t i = 0; i < AnnotList.Count(); ++i) {
+    CPDF_Annot* pAnnot = AnnotList.GetAt(i);
+    CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e,
+                      (float)f);
+    CFX_FloatRect rect = matrix.TransformRect(pAnnot->GetRect());
+
+    CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
+    CPDF_Array* pRectArray = pAnnotDict->GetArrayFor("Rect");
+    if (pRectArray)
+      pRectArray->Clear();
+    else
+      pRectArray = pAnnotDict->SetNewFor<CPDF_Array>("Rect");
+
+    pRectArray->AddNew<CPDF_Number>(rect.left);
+    pRectArray->AddNew<CPDF_Number>(rect.bottom);
+    pRectArray->AddNew<CPDF_Number>(rect.right);
+    pRectArray->AddNew<CPDF_Number>(rect.top);
+
+    // TODO(unknown): Transform AP's rectangle
+  }
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetRotation(FPDF_PAGE page,
+                                                    int rotate) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!IsPageObject(pPage))
+    return;
+
+  rotate %= 4;
+  pPage->GetDict()->SetNewFor<CPDF_Number>(pdfium::page_object::kRotate,
+                                           rotate * 90);
+  pPage->UpdateDimensions();
+}
+
+FPDF_BOOL FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object,
+                                   unsigned int R,
+                                   unsigned int G,
+                                   unsigned int B,
+                                   unsigned int A) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || R > 255 || G > 255 || B > 255 || A > 255)
+    return false;
+
+  std::vector<float> rgb = {R / 255.f, G / 255.f, B / 255.f};
+  pPageObj->m_GeneralState.SetFillAlpha(A / 255.f);
+  pPageObj->m_ColorState.SetFillColor(
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb);
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetFillColor(FPDF_PAGEOBJECT page_object,
+                         unsigned int* R,
+                         unsigned int* G,
+                         unsigned int* B,
+                         unsigned int* A) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !R || !G || !B || !A)
+    return false;
+
+  if (!pPageObj->m_ColorState.HasRef())
+    return false;
+
+  FX_COLORREF fill_color = pPageObj->m_ColorState.GetFillColorRef();
+  *R = FXSYS_GetRValue(fill_color);
+  *G = FXSYS_GetGValue(fill_color);
+  *B = FXSYS_GetBValue(fill_color);
+  *A = FXSYS_GetUnsignedAlpha(pPageObj->m_GeneralState.GetFillAlpha());
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetBounds(FPDF_PAGEOBJECT page_object,
+                      float* left,
+                      float* bottom,
+                      float* right,
+                      float* top) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return false;
+
+  const CFX_FloatRect& bbox = pPageObj->GetRect();
+  *left = bbox.left;
+  *bottom = bbox.bottom;
+  *right = bbox.right;
+  *top = bbox.top;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetStrokeColor(FPDF_PAGEOBJECT page_object,
+                           unsigned int R,
+                           unsigned int G,
+                           unsigned int B,
+                           unsigned int A) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || R > 255 || G > 255 || B > 255 || A > 255)
+    return false;
+
+  std::vector<float> rgb = {R / 255.f, G / 255.f, B / 255.f};
+  pPageObj->m_GeneralState.SetStrokeAlpha(A / 255.f);
+  pPageObj->m_ColorState.SetStrokeColor(
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb);
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetStrokeColor(FPDF_PAGEOBJECT page_object,
+                           unsigned int* R,
+                           unsigned int* G,
+                           unsigned int* B,
+                           unsigned int* A) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !R || !G || !B || !A)
+    return false;
+
+  if (!pPageObj->m_ColorState.HasRef())
+    return false;
+
+  FX_COLORREF stroke_color = pPageObj->m_ColorState.GetStrokeColorRef();
+  *R = FXSYS_GetRValue(stroke_color);
+  *G = FXSYS_GetGValue(stroke_color);
+  *B = FXSYS_GetBValue(stroke_color);
+  *A = FXSYS_GetUnsignedAlpha(pPageObj->m_GeneralState.GetStrokeAlpha());
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetStrokeWidth(FPDF_PAGEOBJECT page_object, float width) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || width < 0.0f)
+    return false;
+
+  pPageObj->m_GraphState.SetLineWidth(width);
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetStrokeWidth(FPDF_PAGEOBJECT page_object, float* width) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !width)
+    return false;
+
+  *width = pPageObj->m_GraphState.GetLineWidth();
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_GetLineJoin(FPDF_PAGEOBJECT page_object) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  return pPageObj ? pPageObj->m_GraphState.GetLineJoin() : -1;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetLineJoin(FPDF_PAGEOBJECT page_object, int line_join) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return false;
+
+  constexpr int kLineJoinMiter =
+      static_cast<int>(CFX_GraphStateData::LineJoin::LineJoinMiter);
+  constexpr int kLineJoinBevel =
+      static_cast<int>(CFX_GraphStateData::LineJoin::LineJoinBevel);
+  if (line_join < kLineJoinMiter || line_join > kLineJoinBevel)
+    return false;
+
+  pPageObj->m_GraphState.SetLineJoin(
+      static_cast<CFX_GraphStateData::LineJoin>(line_join));
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_GetLineCap(FPDF_PAGEOBJECT page_object) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  return pPageObj ? pPageObj->m_GraphState.GetLineCap() : -1;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetLineCap(FPDF_PAGEOBJECT page_object, int line_cap) {
+  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return false;
+
+  constexpr int kLineCapButt =
+      static_cast<int>(CFX_GraphStateData::LineCap::LineCapButt);
+  constexpr int kLineCapSquare =
+      static_cast<int>(CFX_GraphStateData::LineCap::LineCapSquare);
+  if (line_cap < kLineCapButt || line_cap > kLineCapSquare)
+    return false;
+
+  pPageObj->m_GraphState.SetLineCap(
+      static_cast<CFX_GraphStateData::LineCap>(line_cap));
+  pPageObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object) {
+  const auto* pObjectList = CPDFPageObjHolderFromFPDFFormObject(form_object);
+  return pObjectList ? pObjectList->GetPageObjectCount() : -1;
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDFFormObj_GetObject(FPDF_PAGEOBJECT form_object, unsigned long index) {
+  const auto* pObjectList = CPDFPageObjHolderFromFPDFFormObject(form_object);
+  if (!pObjectList)
+    return nullptr;
+
+  return FPDFPageObjectFromCPDFPageObject(
+      pObjectList->GetPageObjectByIndex(index));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFFormObj_GetMatrix(FPDF_PAGEOBJECT form_object, FS_MATRIX* matrix) {
+  CPDF_FormObject* pFormObj = CPDFFormObjectFromFPDFPageObject(form_object);
+  if (!pFormObj || !matrix)
+    return false;
+
+  *matrix = FSMatrixFromCFXMatrix(pFormObj->form_matrix());
+  return true;
+}
diff --git a/fpdfsdk/fpdf_editpage_embeddertest.cpp b/fpdfsdk/fpdf_editpage_embeddertest.cpp
new file mode 100644
index 0000000..6650b40
--- /dev/null
+++ b/fpdfsdk/fpdf_editpage_embeddertest.cpp
@@ -0,0 +1,157 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fx_system.h"
+#include "public/fpdf_edit.h"
+#include "testing/embedder_test.h"
+
+class FPDFEditPageEmbedderTest : public EmbedderTest {};
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Rotation DISABLED_Rotation
+#else
+#define MAYBE_Rotation Rotation
+#endif
+TEST_F(FPDFEditPageEmbedderTest, MAYBE_Rotation) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kRotatedMD5[] = "d599429574ff0dcad3bc898ea8b874ca";
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      EXPECT_EQ(0, FPDFPage_GetRotation(page));
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    FPDFPage_SetRotation(page, 1);
+
+    {
+      // Render the page after rotation.
+      // Note that the change affects the rendering, as expected.
+      // It behaves just like the case below, rather than the case above.
+      EXPECT_EQ(1, FPDFPage_GetRotation(page));
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(300, page_width);
+      EXPECT_EQ(200, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kRotatedMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the rotation.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    EXPECT_EQ(1, FPDFPage_GetRotation(saved_page));
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(300, page_width);
+    EXPECT_EQ(200, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kRotatedMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
+TEST_F(FPDFEditPageEmbedderTest, HasTransparencyImage) {
+  constexpr int kExpectedObjectCount = 39;
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page));
+
+  for (int i = 0; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    EXPECT_FALSE(FPDFPageObj_HasTransparency(obj));
+
+    FPDFPageObj_SetFillColor(obj, 255, 0, 0, 127);
+    EXPECT_TRUE(FPDFPageObj_HasTransparency(obj));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, HasTransparencyInvalid) {
+  EXPECT_FALSE(FPDFPageObj_HasTransparency(nullptr));
+}
+
+TEST_F(FPDFEditPageEmbedderTest, HasTransparencyPath) {
+  constexpr int kExpectedObjectCount = 8;
+  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page));
+
+  for (int i = 0; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    EXPECT_FALSE(FPDFPageObj_HasTransparency(obj));
+
+    FPDFPageObj_SetStrokeColor(obj, 63, 63, 0, 127);
+    EXPECT_TRUE(FPDFPageObj_HasTransparency(obj));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, HasTransparencyText) {
+  constexpr int kExpectedObjectCount = 2;
+  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page));
+
+  for (int i = 0; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    EXPECT_FALSE(FPDFPageObj_HasTransparency(obj));
+
+    FPDFPageObj_SetBlendMode(obj, "Lighten");
+    EXPECT_TRUE(FPDFPageObj_HasTransparency(obj));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditPageEmbedderTest, GetFillAndStrokeForImage) {
+  constexpr int kExpectedObjectCount = 39;
+  constexpr int kImageObjectsStartIndex = 33;
+  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page));
+
+  for (int i = kImageObjectsStartIndex; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT image = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(image);
+    EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(image));
+
+    unsigned int r;
+    unsigned int g;
+    unsigned int b;
+    unsigned int a;
+    EXPECT_FALSE(FPDFPageObj_GetFillColor(image, &r, &g, &b, &a));
+    EXPECT_FALSE(FPDFPageObj_GetStrokeColor(image, &r, &g, &b, &a));
+  }
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_editpath.cpp b/fpdfsdk/fpdf_editpath.cpp
new file mode 100644
index 0000000..aaa4b72
--- /dev/null
+++ b/fpdfsdk/fpdf_editpath.cpp
@@ -0,0 +1,233 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_edit.h"
+
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_path.h"
+#include "core/fpdfapi/page/cpdf_pathobject.h"
+#include "core/fxcrt/fx_system.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+// These checks are here because core/ and public/ cannot depend on each other.
+static_assert(CFX_GraphStateData::LineCapButt == FPDF_LINECAP_BUTT,
+              "CFX_GraphStateData::LineCapButt value mismatch");
+static_assert(CFX_GraphStateData::LineCapRound == FPDF_LINECAP_ROUND,
+              "CFX_GraphStateData::LineCapRound value mismatch");
+static_assert(CFX_GraphStateData::LineCapSquare ==
+                  FPDF_LINECAP_PROJECTING_SQUARE,
+              "CFX_GraphStateData::LineCapSquare value mismatch");
+
+static_assert(CFX_GraphStateData::LineJoinMiter == FPDF_LINEJOIN_MITER,
+              "CFX_GraphStateData::LineJoinMiter value mismatch");
+static_assert(CFX_GraphStateData::LineJoinRound == FPDF_LINEJOIN_ROUND,
+              "CFX_GraphStateData::LineJoinRound value mismatch");
+static_assert(CFX_GraphStateData::LineJoinBevel == FPDF_LINEJOIN_BEVEL,
+              "CFX_GraphStateData::LineJoinBevel value mismatch");
+
+static_assert(static_cast<int>(FXPT_TYPE::LineTo) == FPDF_SEGMENT_LINETO,
+              "FXPT_TYPE::LineTo value mismatch");
+static_assert(static_cast<int>(FXPT_TYPE::BezierTo) == FPDF_SEGMENT_BEZIERTO,
+              "FXPT_TYPE::BezierTo value mismatch");
+static_assert(static_cast<int>(FXPT_TYPE::MoveTo) == FPDF_SEGMENT_MOVETO,
+              "FXPT_TYPE::MoveTo value mismatch");
+
+namespace {
+
+CPDF_PathObject* CPDFPathObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
+  auto* obj = CPDFPageObjectFromFPDFPageObject(page_object);
+  return obj ? obj->AsPath() : nullptr;
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewPath(float x,
+                                                                    float y) {
+  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false);
+  pPathObj->DefaultStates();
+
+  // Caller takes ownership.
+  return FPDFPageObjectFromCPDFPageObject(pPathObj.release());
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewRect(float x,
+                                                                    float y,
+                                                                    float w,
+                                                                    float h) {
+  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  pPathObj->path().AppendRect(x, y, x + w, y + h);
+  pPathObj->DefaultStates();
+
+  // Caller takes ownership.
+  return FPDFPageObjectFromCPDFPageObject(pPathObj.release());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFPath_CountSegments(FPDF_PAGEOBJECT path) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return -1;
+  return pdfium::CollectionSize<int>(pPathObj->path().GetPoints());
+}
+
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return nullptr;
+
+  const std::vector<FX_PATHPOINT>& points = pPathObj->path().GetPoints();
+  if (!pdfium::IndexInBounds(points, index))
+    return nullptr;
+
+  return FPDFPathSegmentFromFXPathPoint(&points[index]);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_MoveTo(FPDF_PAGEOBJECT path,
+                                                    float x,
+                                                    float y) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return false;
+
+  pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false);
+  pPathObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_LineTo(FPDF_PAGEOBJECT path,
+                                                    float x,
+                                                    float y) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return false;
+
+  pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::LineTo, false);
+  pPathObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_BezierTo(FPDF_PAGEOBJECT path,
+                                                      float x1,
+                                                      float y1,
+                                                      float x2,
+                                                      float y2,
+                                                      float x3,
+                                                      float y3) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return false;
+
+  CPDF_Path& cpath = pPathObj->path();
+  cpath.AppendPoint(CFX_PointF(x1, y1), FXPT_TYPE::BezierTo, false);
+  cpath.AppendPoint(CFX_PointF(x2, y2), FXPT_TYPE::BezierTo, false);
+  cpath.AppendPoint(CFX_PointF(x3, y3), FXPT_TYPE::BezierTo, false);
+  pPathObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_Close(FPDF_PAGEOBJECT path) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return false;
+
+  CPDF_Path& cpath = pPathObj->path();
+  if (cpath.GetPoints().empty())
+    return false;
+
+  cpath.ClosePath();
+  pPathObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetDrawMode(FPDF_PAGEOBJECT path,
+                                                         int fillmode,
+                                                         FPDF_BOOL stroke) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return false;
+
+  pPathObj->set_stroke(!!stroke);
+  if (fillmode == FPDF_FILLMODE_ALTERNATE)
+    pPathObj->set_alternate_filltype();
+  else if (fillmode == FPDF_FILLMODE_WINDING)
+    pPathObj->set_winding_filltype();
+  else
+    pPathObj->set_no_filltype();
+  pPathObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetDrawMode(FPDF_PAGEOBJECT path,
+                                                         int* fillmode,
+                                                         FPDF_BOOL* stroke) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj || !fillmode || !stroke)
+    return false;
+
+  if (pPathObj->has_alternate_filltype())
+    *fillmode = FPDF_FILLMODE_ALTERNATE;
+  else if (pPathObj->has_winding_filltype())
+    *fillmode = FPDF_FILLMODE_WINDING;
+  else
+    *fillmode = FPDF_FILLMODE_NONE;
+
+  *stroke = pPathObj->stroke();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetMatrix(FPDF_PAGEOBJECT path,
+                                                       FS_MATRIX* matrix) {
+  if (!path || !matrix)
+    return false;
+
+  CPDF_PathObject* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return false;
+
+  *matrix = FSMatrixFromCFXMatrix(pPathObj->matrix());
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPath_SetMatrix(FPDF_PAGEOBJECT path, const FS_MATRIX* matrix) {
+  if (!matrix)
+    return false;
+
+  CPDF_PathObject* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return false;
+
+  pPathObj->set_matrix(CFXMatrixFromFSMatrix(*matrix));
+  pPathObj->SetDirty(true);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y) {
+  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
+  if (!pPathPoint || !x || !y)
+    return false;
+
+  *x = pPathPoint->m_Point.x;
+  *y = pPathPoint->m_Point.y;
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPathSegment_GetType(FPDF_PATHSEGMENT segment) {
+  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
+  return pPathPoint ? static_cast<int>(pPathPoint->m_Type)
+                    : FPDF_SEGMENT_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPathSegment_GetClose(FPDF_PATHSEGMENT segment) {
+  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
+  return pPathPoint && pPathPoint->m_CloseFigure;
+}
diff --git a/fpdfsdk/fpdf_editpath_embeddertest.cpp b/fpdfsdk/fpdf_editpath_embeddertest.cpp
new file mode 100644
index 0000000..2a8fb6a
--- /dev/null
+++ b/fpdfsdk/fpdf_editpath_embeddertest.cpp
@@ -0,0 +1,63 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fx_system.h"
+#include "public/fpdf_edit.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFEditPathEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFEditPathEmbedderTest, VerifyCorrectColoursReturned) {
+  constexpr int kObjectCount = 256;
+  CreateEmptyDocument();
+  FPDF_PAGE page = FPDFPage_New(document(), 0, 612, 792);
+
+  for (size_t i = 0; i < kObjectCount; ++i) {
+    FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
+    EXPECT_TRUE(FPDFPageObj_SetFillColor(path, i, i, i, i));
+    EXPECT_TRUE(FPDFPageObj_SetStrokeColor(path, i, i, i, i));
+    EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 0));
+    EXPECT_TRUE(FPDFPath_LineTo(path, 400, 200));
+    EXPECT_TRUE(FPDFPath_LineTo(path, 300, 100));
+    EXPECT_TRUE(FPDFPath_Close(path));
+
+    FPDFPage_InsertObject(page, path);
+  }
+
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  FPDF_ClosePage(page);
+  page = nullptr;
+
+  ASSERT_TRUE(OpenSavedDocument());
+  page = LoadSavedPage(0);
+  ASSERT(page);
+
+  for (size_t i = 0; i < kObjectCount; ++i) {
+    FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, i);
+    ASSERT_TRUE(path);
+
+    EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
+
+    unsigned int r;
+    unsigned int g;
+    unsigned int b;
+    unsigned int a;
+    ASSERT_TRUE(FPDFPageObj_GetFillColor(path, &r, &g, &b, &a));
+    EXPECT_EQ(i, r);
+    EXPECT_EQ(i, g);
+    EXPECT_EQ(i, b);
+    EXPECT_EQ(i, a);
+
+    ASSERT_TRUE(FPDFPageObj_GetStrokeColor(path, &r, &g, &b, &a));
+    EXPECT_EQ(i, r);
+    EXPECT_EQ(i, g);
+    EXPECT_EQ(i, b);
+    EXPECT_EQ(i, a);
+  }
+
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
diff --git a/fpdfsdk/fpdf_edittext.cpp b/fpdfsdk/fpdf_edittext.cpp
new file mode 100644
index 0000000..a3b4208
--- /dev/null
+++ b/fpdfsdk/fpdf_edittext.cpp
@@ -0,0 +1,618 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <limits>
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/font/cpdf_type1font.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_textobject.h"
+#include "core/fpdfapi/page/cpdf_textstate.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdftext/cpdf_textpage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/fx_font.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/fpdf_edit.h"
+#include "third_party/base/ptr_util.h"
+
+// These checks are here because core/ and public/ cannot depend on each other.
+static_assert(static_cast<int>(TextRenderingMode::MODE_UNKNOWN) ==
+                  FPDF_TEXTRENDERMODE_UNKNOWN,
+              "TextRenderingMode::MODE_UNKNOWN value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_FILL) ==
+                  FPDF_TEXTRENDERMODE_FILL,
+              "TextRenderingMode::MODE_FILL value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_STROKE) ==
+                  FPDF_TEXTRENDERMODE_STROKE,
+              "TextRenderingMode::MODE_STROKE value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_FILL_STROKE) ==
+                  FPDF_TEXTRENDERMODE_FILL_STROKE,
+              "TextRenderingMode::MODE_FILL_STROKE value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_INVISIBLE) ==
+                  FPDF_TEXTRENDERMODE_INVISIBLE,
+              "TextRenderingMode::MODE_INVISIBLE value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_FILL_CLIP) ==
+                  FPDF_TEXTRENDERMODE_FILL_CLIP,
+              "TextRenderingMode::MODE_FILL_CLIP value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_STROKE_CLIP) ==
+                  FPDF_TEXTRENDERMODE_STROKE_CLIP,
+              "TextRenderingMode::MODE_STROKE_CLIP value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_FILL_STROKE_CLIP) ==
+                  FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP,
+              "TextRenderingMode::MODE_FILL_STROKE_CLIP value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_CLIP) ==
+                  FPDF_TEXTRENDERMODE_CLIP,
+              "TextRenderingMode::MODE_CLIP value mismatch");
+static_assert(static_cast<int>(TextRenderingMode::MODE_LAST) ==
+                  FPDF_TEXTRENDERMODE_LAST,
+              "TextRenderingMode::MODE_LAST value mismatch");
+
+namespace {
+
+CPDF_Dictionary* LoadFontDesc(CPDF_Document* pDoc,
+                              const ByteString& font_name,
+                              CFX_Font* pFont,
+                              pdfium::span<const uint8_t> span,
+                              int font_type) {
+  CPDF_Dictionary* pFontDesc = pDoc->NewIndirect<CPDF_Dictionary>();
+  pFontDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
+  pFontDesc->SetNewFor<CPDF_Name>("FontName", font_name);
+  int flags = 0;
+  if (FXFT_Is_Face_fixedwidth(pFont->GetFaceRec()))
+    flags |= FXFONT_FIXED_PITCH;
+  if (font_name.Contains("Serif"))
+    flags |= FXFONT_SERIF;
+  if (FXFT_Is_Face_Italic(pFont->GetFaceRec()))
+    flags |= FXFONT_ITALIC;
+  if (FXFT_Is_Face_Bold(pFont->GetFaceRec()))
+    flags |= FXFONT_FORCE_BOLD;
+
+  // TODO(npm): How do I know if a  font is symbolic, script, allcap, smallcap
+  flags |= FXFONT_NONSYMBOLIC;
+
+  pFontDesc->SetNewFor<CPDF_Number>("Flags", flags);
+  FX_RECT bbox;
+  pFont->GetBBox(&bbox);
+  pFontDesc->SetRectFor("FontBBox", CFX_FloatRect(bbox));
+
+  // TODO(npm): calculate italic angle correctly
+  pFontDesc->SetNewFor<CPDF_Number>("ItalicAngle", pFont->IsItalic() ? -12 : 0);
+
+  pFontDesc->SetNewFor<CPDF_Number>("Ascent", pFont->GetAscent());
+  pFontDesc->SetNewFor<CPDF_Number>("Descent", pFont->GetDescent());
+
+  // TODO(npm): calculate the capheight, stemV correctly
+  pFontDesc->SetNewFor<CPDF_Number>("CapHeight", pFont->GetAscent());
+  pFontDesc->SetNewFor<CPDF_Number>("StemV", pFont->IsBold() ? 120 : 70);
+
+  CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>();
+  pStream->SetData(span);
+  // TODO(npm): Lengths for Type1 fonts.
+  if (font_type == FPDF_FONT_TRUETYPE) {
+    pStream->GetDict()->SetNewFor<CPDF_Number>("Length1",
+                                               static_cast<int>(span.size()));
+  }
+  ByteString fontFile = font_type == FPDF_FONT_TYPE1 ? "FontFile" : "FontFile2";
+  pFontDesc->SetNewFor<CPDF_Reference>(fontFile, pDoc, pStream->GetObjNum());
+  return pFontDesc;
+}
+
+const char ToUnicodeStart[] =
+    "/CIDInit /ProcSet findresource begin\n"
+    "12 dict begin\n"
+    "begincmap\n"
+    "/CIDSystemInfo\n"
+    "<</Registry (Adobe)\n"
+    "/Ordering (Identity)\n"
+    "/Supplement 0\n"
+    ">> def\n"
+    "/CMapName /Adobe-Identity-H def\n"
+    "CMapType 2 def\n"
+    "1 begincodespacerange\n"
+    "<0000> <FFFFF>\n"
+    "endcodespacerange\n";
+
+const char ToUnicodeEnd[] =
+    "endcmap\n"
+    "CMapName currentdict /CMap defineresource pop\n"
+    "end\n"
+    "end\n";
+
+void AddCharcode(std::ostringstream* pBuffer, uint32_t number) {
+  ASSERT(number <= 0xFFFF);
+  *pBuffer << "<";
+  char ans[4];
+  FXSYS_IntToFourHexChars(number, ans);
+  for (size_t i = 0; i < 4; ++i)
+    *pBuffer << ans[i];
+  *pBuffer << ">";
+}
+
+// PDF spec 1.7 Section 5.9.2: "Unicode character sequences as expressed in
+// UTF-16BE encoding." See https://en.wikipedia.org/wiki/UTF-16#Description
+void AddUnicode(std::ostringstream* pBuffer, uint32_t unicode) {
+  if (unicode >= 0xD800 && unicode <= 0xDFFF)
+    unicode = 0;
+
+  char ans[8];
+  *pBuffer << "<";
+  size_t numChars = FXSYS_ToUTF16BE(unicode, ans);
+  for (size_t i = 0; i < numChars; ++i)
+    *pBuffer << ans[i];
+  *pBuffer << ">";
+}
+
+// Loads the charcode to unicode mapping into a stream
+CPDF_Stream* LoadUnicode(CPDF_Document* pDoc,
+                         const std::map<uint32_t, uint32_t>& to_unicode) {
+  // A map charcode->unicode
+  std::map<uint32_t, uint32_t> char_to_uni;
+  // A map <char_start, char_end> to vector v of unicode characters of size (end
+  // - start + 1). This abbreviates: start->v[0], start+1->v[1], etc. PDF spec
+  // 1.7 Section 5.9.2 says that only the last byte of the unicode may change.
+  std::map<std::pair<uint32_t, uint32_t>, std::vector<uint32_t>>
+      map_range_vector;
+  // A map <start, end> -> unicode
+  // This abbreviates: start->unicode, start+1->unicode+1, etc.
+  // PDF spec 1.7 Section 5.9.2 says that only the last byte of the unicode may
+  // change.
+  std::map<std::pair<uint32_t, uint32_t>, uint32_t> map_range;
+
+  // Calculate the maps
+  for (auto iter = to_unicode.begin(); iter != to_unicode.end(); ++iter) {
+    uint32_t firstCharcode = iter->first;
+    uint32_t firstUnicode = iter->second;
+    if (std::next(iter) == to_unicode.end() ||
+        firstCharcode + 1 != std::next(iter)->first) {
+      char_to_uni[firstCharcode] = firstUnicode;
+      continue;
+    }
+    ++iter;
+    uint32_t curCharcode = iter->first;
+    uint32_t curUnicode = iter->second;
+    if (curCharcode % 256 == 0) {
+      char_to_uni[firstCharcode] = firstUnicode;
+      char_to_uni[curCharcode] = curUnicode;
+      continue;
+    }
+    const size_t maxExtra = 255 - (curCharcode % 256);
+    auto next_it = std::next(iter);
+    if (firstUnicode + 1 != curUnicode) {
+      // Consecutive charcodes mapping to non-consecutive unicodes
+      std::vector<uint32_t> unicodes;
+      unicodes.push_back(firstUnicode);
+      unicodes.push_back(curUnicode);
+      for (size_t i = 0; i < maxExtra; ++i) {
+        if (next_it == to_unicode.end() || curCharcode + 1 != next_it->first)
+          break;
+        ++iter;
+        ++curCharcode;
+        unicodes.push_back(iter->second);
+        next_it = std::next(iter);
+      }
+      ASSERT(iter->first - firstCharcode + 1 == unicodes.size());
+      map_range_vector[std::make_pair(firstCharcode, iter->first)] = unicodes;
+      continue;
+    }
+    // Consecutive charcodes mapping to consecutive unicodes
+    for (size_t i = 0; i < maxExtra; ++i) {
+      if (next_it == to_unicode.end() || curCharcode + 1 != next_it->first ||
+          curUnicode + 1 != next_it->second) {
+        break;
+      }
+      ++iter;
+      ++curCharcode;
+      ++curUnicode;
+      next_it = std::next(iter);
+    }
+    map_range[std::make_pair(firstCharcode, curCharcode)] = firstUnicode;
+  }
+  std::ostringstream buffer;
+  buffer << ToUnicodeStart;
+  // Add maps to buffer
+  buffer << static_cast<uint32_t>(char_to_uni.size()) << " beginbfchar\n";
+  for (const auto& iter : char_to_uni) {
+    AddCharcode(&buffer, iter.first);
+    buffer << " ";
+    AddUnicode(&buffer, iter.second);
+    buffer << "\n";
+  }
+  buffer << "endbfchar\n"
+         << static_cast<uint32_t>(map_range_vector.size() + map_range.size())
+         << " beginbfrange\n";
+  for (const auto& iter : map_range_vector) {
+    const std::pair<uint32_t, uint32_t>& charcodeRange = iter.first;
+    AddCharcode(&buffer, charcodeRange.first);
+    buffer << " ";
+    AddCharcode(&buffer, charcodeRange.second);
+    buffer << " [";
+    const std::vector<uint32_t>& unicodes = iter.second;
+    for (size_t i = 0; i < unicodes.size(); ++i) {
+      uint32_t uni = unicodes[i];
+      AddUnicode(&buffer, uni);
+      if (i != unicodes.size() - 1)
+        buffer << " ";
+    }
+    buffer << "]\n";
+  }
+  for (const auto& iter : map_range) {
+    const std::pair<uint32_t, uint32_t>& charcodeRange = iter.first;
+    AddCharcode(&buffer, charcodeRange.first);
+    buffer << " ";
+    AddCharcode(&buffer, charcodeRange.second);
+    buffer << " ";
+    AddUnicode(&buffer, iter.second);
+    buffer << "\n";
+  }
+  buffer << "endbfrange\n";
+  buffer << ToUnicodeEnd;
+  // TODO(npm): Encrypt / Compress?
+  CPDF_Stream* stream = pDoc->NewIndirect<CPDF_Stream>();
+  stream->SetDataFromStringstream(&buffer);
+  return stream;
+}
+
+RetainPtr<CPDF_Font> LoadSimpleFont(CPDF_Document* pDoc,
+                                    std::unique_ptr<CFX_Font> pFont,
+                                    pdfium::span<const uint8_t> span,
+                                    int font_type) {
+  CPDF_Dictionary* pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+  pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
+  pFontDict->SetNewFor<CPDF_Name>(
+      "Subtype", font_type == FPDF_FONT_TYPE1 ? "Type1" : "TrueType");
+  ByteString name = pFont->GetBaseFontName(font_type == FPDF_FONT_TYPE1);
+  if (name.IsEmpty())
+    name = CFX_Font::kUntitledFontName;
+  pFontDict->SetNewFor<CPDF_Name>("BaseFont", name);
+
+  uint32_t dwGlyphIndex;
+  uint32_t dwCurrentChar =
+      FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex);
+  static constexpr uint32_t kMaxSimpleFontChar = 0xFF;
+  if (dwCurrentChar > kMaxSimpleFontChar || dwGlyphIndex == 0)
+    return nullptr;
+  pFontDict->SetNewFor<CPDF_Number>("FirstChar",
+                                    static_cast<int>(dwCurrentChar));
+  CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
+  while (true) {
+    uint32_t width =
+        std::min(pFont->GetGlyphWidth(dwGlyphIndex),
+                 static_cast<uint32_t>(std::numeric_limits<int>::max()));
+    widthsArray->AddNew<CPDF_Number>(static_cast<int>(width));
+    uint32_t nextChar =
+        FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex);
+    // Simple fonts have 1-byte charcodes only.
+    if (nextChar > kMaxSimpleFontChar || dwGlyphIndex == 0)
+      break;
+    for (uint32_t i = dwCurrentChar + 1; i < nextChar; i++)
+      widthsArray->AddNew<CPDF_Number>(0);
+    dwCurrentChar = nextChar;
+  }
+  pFontDict->SetNewFor<CPDF_Number>("LastChar",
+                                    static_cast<int>(dwCurrentChar));
+  pFontDict->SetNewFor<CPDF_Reference>("Widths", pDoc,
+                                       widthsArray->GetObjNum());
+  CPDF_Dictionary* pFontDesc =
+      LoadFontDesc(pDoc, name, pFont.get(), span, font_type);
+
+  pFontDict->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
+                                       pFontDesc->GetObjNum());
+  return CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFontDict);
+}
+
+RetainPtr<CPDF_Font> LoadCompositeFont(CPDF_Document* pDoc,
+                                       std::unique_ptr<CFX_Font> pFont,
+                                       pdfium::span<const uint8_t> span,
+                                       int font_type) {
+  CPDF_Dictionary* pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+  pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
+  pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type0");
+  // TODO(npm): Get the correct encoding, if it's not identity.
+  ByteString encoding = "Identity-H";
+  pFontDict->SetNewFor<CPDF_Name>("Encoding", encoding);
+  ByteString name = pFont->GetBaseFontName(font_type == FPDF_FONT_TYPE1);
+  if (name.IsEmpty())
+    name = CFX_Font::kUntitledFontName;
+  pFontDict->SetNewFor<CPDF_Name>(
+      "BaseFont", font_type == FPDF_FONT_TYPE1 ? name + "-" + encoding : name);
+
+  CPDF_Dictionary* pCIDFont = pDoc->NewIndirect<CPDF_Dictionary>();
+  pCIDFont->SetNewFor<CPDF_Name>("Type", "Font");
+  pCIDFont->SetNewFor<CPDF_Name>("Subtype", font_type == FPDF_FONT_TYPE1
+                                                ? "CIDFontType0"
+                                                : "CIDFontType2");
+  pCIDFont->SetNewFor<CPDF_Name>("BaseFont", name);
+
+  // TODO(npm): Maybe use FT_Get_CID_Registry_Ordering_Supplement to get the
+  // CIDSystemInfo
+  CPDF_Dictionary* pCIDSystemInfo = pDoc->NewIndirect<CPDF_Dictionary>();
+  pCIDSystemInfo->SetNewFor<CPDF_String>("Registry", "Adobe", false);
+  pCIDSystemInfo->SetNewFor<CPDF_String>("Ordering", "Identity", false);
+  pCIDSystemInfo->SetNewFor<CPDF_Number>("Supplement", 0);
+  pCIDFont->SetNewFor<CPDF_Reference>("CIDSystemInfo", pDoc,
+                                      pCIDSystemInfo->GetObjNum());
+
+  CPDF_Dictionary* pFontDesc =
+      LoadFontDesc(pDoc, name, pFont.get(), span, font_type);
+  pCIDFont->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
+                                      pFontDesc->GetObjNum());
+
+  uint32_t dwGlyphIndex;
+  uint32_t dwCurrentChar =
+      FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex);
+  static constexpr uint32_t kMaxUnicode = 0x10FFFF;
+  // If it doesn't have a single char, just fail
+  if (dwGlyphIndex == 0 || dwCurrentChar > kMaxUnicode)
+    return nullptr;
+
+  std::map<uint32_t, uint32_t> to_unicode;
+  std::map<uint32_t, uint32_t> widths;
+  while (true) {
+    if (dwCurrentChar > kMaxUnicode)
+      break;
+
+    if (!pdfium::ContainsKey(widths, dwGlyphIndex))
+      widths[dwGlyphIndex] = pFont->GetGlyphWidth(dwGlyphIndex);
+    to_unicode[dwGlyphIndex] = dwCurrentChar;
+    dwCurrentChar =
+        FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex);
+    if (dwGlyphIndex == 0)
+      break;
+  }
+  CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
+  for (auto it = widths.begin(); it != widths.end(); ++it) {
+    int ch = it->first;
+    int w = it->second;
+    if (std::next(it) == widths.end()) {
+      // Only one char left, use format c [w]
+      auto oneW = pdfium::MakeRetain<CPDF_Array>();
+      oneW->AddNew<CPDF_Number>(w);
+      widthsArray->AddNew<CPDF_Number>(ch);
+      widthsArray->Add(oneW);
+      break;
+    }
+    ++it;
+    int next_ch = it->first;
+    int next_w = it->second;
+    if (next_ch == ch + 1 && next_w == w) {
+      // The array can have a group c_first c_last w: all CIDs in the range from
+      // c_first to c_last will have width w
+      widthsArray->AddNew<CPDF_Number>(ch);
+      ch = next_ch;
+      while (true) {
+        auto next_it = std::next(it);
+        if (next_it == widths.end() || next_it->first != it->first + 1 ||
+            next_it->second != it->second) {
+          break;
+        }
+        ++it;
+        ch = it->first;
+      }
+      widthsArray->AddNew<CPDF_Number>(ch);
+      widthsArray->AddNew<CPDF_Number>(w);
+      continue;
+    }
+    // Otherwise we can have a group of the form c [w1 w2 ...]: c has width
+    // w1, c+1 has width w2, etc.
+    widthsArray->AddNew<CPDF_Number>(ch);
+    auto curWidthArray = pdfium::MakeRetain<CPDF_Array>();
+    curWidthArray->AddNew<CPDF_Number>(w);
+    curWidthArray->AddNew<CPDF_Number>(next_w);
+    while (true) {
+      auto next_it = std::next(it);
+      if (next_it == widths.end() || next_it->first != it->first + 1)
+        break;
+      ++it;
+      curWidthArray->AddNew<CPDF_Number>(static_cast<int>(it->second));
+    }
+    widthsArray->Add(curWidthArray);
+  }
+  pCIDFont->SetNewFor<CPDF_Reference>("W", pDoc, widthsArray->GetObjNum());
+
+  // TODO(npm): Support vertical writing
+
+  auto* pDescendant = pFontDict->SetNewFor<CPDF_Array>("DescendantFonts");
+  pDescendant->AddNew<CPDF_Reference>(pDoc, pCIDFont->GetObjNum());
+
+  CPDF_Stream* toUnicodeStream = LoadUnicode(pDoc, to_unicode);
+  pFontDict->SetNewFor<CPDF_Reference>("ToUnicode", pDoc,
+                                       toUnicodeStream->GetObjNum());
+  return CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFontDict);
+}
+
+CPDF_TextObject* CPDFTextObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
+  auto* obj = CPDFPageObjectFromFPDFPageObject(page_object);
+  return obj ? obj->AsText() : nullptr;
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDFPageObj_NewTextObj(FPDF_DOCUMENT document,
+                       FPDF_BYTESTRING font,
+                       float font_size) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  RetainPtr<CPDF_Font> pFont =
+      CPDF_Font::GetStockFont(pDoc, ByteStringView(font));
+  if (!pFont)
+    return nullptr;
+
+  auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
+  pTextObj->m_TextState.SetFont(pFont);
+  pTextObj->m_TextState.SetFontSize(font_size);
+  pTextObj->DefaultStates();
+
+  // Caller takes ownership.
+  return FPDFPageObjectFromCPDFPageObject(pTextObj.release());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_SetText(FPDF_PAGEOBJECT text_object, FPDF_WIDESTRING text) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text_object);
+  if (!pTextObj)
+    return false;
+
+  WideString encodedText = WideStringFromFPDFWideString(text);
+  ByteString byteText;
+  for (wchar_t wc : encodedText) {
+    pTextObj->GetFont()->AppendChar(
+        &byteText, pTextObj->GetFont()->CharCodeFromUnicode(wc));
+  }
+  pTextObj->SetText(byteText);
+  return true;
+}
+
+FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFText_LoadFont(FPDF_DOCUMENT document,
+                                                      const uint8_t* data,
+                                                      uint32_t size,
+                                                      int font_type,
+                                                      FPDF_BOOL cid) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc || !data || size == 0 ||
+      (font_type != FPDF_FONT_TYPE1 && font_type != FPDF_FONT_TRUETYPE)) {
+    return nullptr;
+  }
+
+  auto span = pdfium::make_span(data, size);
+  auto pFont = pdfium::MakeUnique<CFX_Font>();
+
+  // TODO(npm): Maybe use FT_Get_X11_Font_Format to check format? Otherwise, we
+  // are allowing giving any font that can be loaded on freetype and setting it
+  // as any font type.
+  if (!pFont->LoadEmbedded(span, false))
+    return nullptr;
+
+  // Caller takes ownership.
+  return FPDFFontFromCPDFFont(
+      cid ? LoadCompositeFont(pDoc, std::move(pFont), span, font_type).Leak()
+          : LoadSimpleFont(pDoc, std::move(pFont), span, font_type).Leak());
+}
+
+FPDF_EXPORT FPDF_FONT FPDF_CALLCONV
+FPDFText_LoadStandardFont(FPDF_DOCUMENT document, FPDF_BYTESTRING font) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  // Caller takes ownership.
+  return FPDFFontFromCPDFFont(
+      CPDF_Font::GetStockFont(pDoc, ByteStringView(font)).Leak());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFTextObj_GetMatrix(FPDF_PAGEOBJECT text,
+                                                          FS_MATRIX* matrix) {
+  if (!matrix)
+    return false;
+
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
+  if (!pTextObj)
+    return false;
+
+  *matrix = FSMatrixFromCFXMatrix(pTextObj->GetTextMatrix());
+  return true;
+}
+
+FPDF_EXPORT float FPDF_CALLCONV FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
+  return pTextObj ? pTextObj->GetFontSize() : 0.0f;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFTextObj_GetFontName(FPDF_PAGEOBJECT text,
+                        void* buffer,
+                        unsigned long length) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
+  if (!pTextObj)
+    return 0;
+
+  RetainPtr<CPDF_Font> pPdfFont = pTextObj->GetFont();
+  CFX_Font* pFont = pPdfFont->GetFont();
+  ByteString name = pFont->GetFamilyName();
+  unsigned long dwStringLen = name.GetLength() + 1;
+  if (buffer && length >= dwStringLen)
+    memcpy(buffer, name.c_str(), dwStringLen);
+
+  return dwStringLen;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object,
+                    FPDF_TEXTPAGE text_page,
+                    void* buffer,
+                    unsigned long length) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text_object);
+  if (!pTextObj)
+    return 0;
+
+  CPDF_TextPage* pTextPage = CPDFTextPageFromFPDFTextPage(text_page);
+  if (!pTextPage)
+    return 0;
+
+  WideString text = pTextPage->GetTextByObject(pTextObj);
+  return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, length);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFFont_Close(FPDF_FONT font) {
+  // Take back ownership from caller and release.
+  RetainPtr<CPDF_Font>().Unleak(CPDFFontFromFPDFFont(font));
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDFPageObj_CreateTextObj(FPDF_DOCUMENT document,
+                          FPDF_FONT font,
+                          float font_size) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  CPDF_Font* pFont = CPDFFontFromFPDFFont(font);
+  if (!pDoc || !pFont)
+    return nullptr;
+
+  auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
+  pTextObj->m_TextState.SetFont(
+      CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFont->GetFontDict()));
+  pTextObj->m_TextState.SetFontSize(font_size);
+  pTextObj->DefaultStates();
+  return FPDFPageObjectFromCPDFPageObject(pTextObj.release());
+}
+
+FPDF_EXPORT FPDF_TEXT_RENDERMODE FPDF_CALLCONV
+FPDFTextObj_GetTextRenderMode(FPDF_PAGEOBJECT text) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
+  if (!pTextObj)
+    return FPDF_TEXTRENDERMODE_UNKNOWN;
+  return static_cast<FPDF_TEXT_RENDERMODE>(pTextObj->m_TextState.GetTextMode());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFTextObj_SetTextRenderMode(FPDF_PAGEOBJECT text,
+                              FPDF_TEXT_RENDERMODE render_mode) {
+  if (render_mode <= FPDF_TEXTRENDERMODE_UNKNOWN ||
+      render_mode > FPDF_TEXTRENDERMODE_LAST) {
+    return false;
+  }
+
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
+  if (!pTextObj)
+    return false;
+
+  pTextObj->m_TextState.SetTextMode(
+      static_cast<TextRenderingMode>(render_mode));
+  return true;
+}
diff --git a/fpdfsdk/fpdf_ext.cpp b/fpdfsdk/fpdf_ext.cpp
index 87605cf..dc928f6 100644
--- a/fpdfsdk/fpdf_ext.cpp
+++ b/fpdfsdk/fpdf_ext.cpp
@@ -6,165 +6,80 @@
 
 #include "public/fpdf_ext.h"
 
-#include <memory>
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fpdfdoc/cpdf_interform.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fpdfdoc/cpdf_metadata.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/xml/cxml_content.h"
-#include "core/fxcrt/xml/cxml_element.h"
-#include "fpdfsdk/fsdk_define.h"
+#include "core/fxcrt/fx_extension.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "third_party/base/ptr_util.h"
 
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#endif  // PDF_ENABLE_XFA
-
-bool FPDF_UnSupportError(int nError) {
-  CFSDK_UnsupportInfo_Adapter* pAdapter =
-      CPDF_ModuleMgr::Get()->GetUnsupportInfoAdapter();
-  if (!pAdapter)
-    return false;
-
-  UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(pAdapter->GetUnspInfo());
-  if (info && info->FSDK_UnSupport_Handler)
-    info->FSDK_UnSupport_Handler(info, nError);
-  return true;
-}
+static_assert(static_cast<int>(UnsupportedFeature::kDocumentXFAForm) ==
+                  FPDF_UNSP_DOC_XFAFORM,
+              "UnsupportedFeature::kDocumentXFAForm value mismatch");
+static_assert(
+    static_cast<int>(UnsupportedFeature::kDocumentPortableCollection) ==
+        FPDF_UNSP_DOC_PORTABLECOLLECTION,
+    "UnsupportedFeature::kDocumentPortableCollection value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kDocumentAttachment) ==
+                  FPDF_UNSP_DOC_ATTACHMENT,
+              "UnsupportedFeature::kDocumentAttachment value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kDocumentSecurity) ==
+                  FPDF_UNSP_DOC_SECURITY,
+              "UnsupportedFeature::kDocumentSecurity value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kDocumentSharedReview) ==
+                  FPDF_UNSP_DOC_SHAREDREVIEW,
+              "UnsupportedFeature::kDocumentSharedReview value mismatch");
+static_assert(
+    static_cast<int>(UnsupportedFeature::kDocumentSharedFormAcrobat) ==
+        FPDF_UNSP_DOC_SHAREDFORM_ACROBAT,
+    "UnsupportedFeature::kDocumentSharedFormAcrobat value mismatch");
+static_assert(
+    static_cast<int>(UnsupportedFeature::kDocumentSharedFormFilesystem) ==
+        FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM,
+    "UnsupportedFeature::kDocumentSharedFormFilesystem value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kDocumentSharedFormEmail) ==
+                  FPDF_UNSP_DOC_SHAREDFORM_EMAIL,
+              "UnsupportedFeature::kDocumentSharedFormEmail value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kAnnotation3d) ==
+                  FPDF_UNSP_ANNOT_3DANNOT,
+              "UnsupportedFeature::kAnnotation3d value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kAnnotationMovie) ==
+                  FPDF_UNSP_ANNOT_MOVIE,
+              "UnsupportedFeature::kAnnotationMovie value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kAnnotationSound) ==
+                  FPDF_UNSP_ANNOT_SOUND,
+              "UnsupportedFeature::kAnnotationSound value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kAnnotationScreenMedia) ==
+                  FPDF_UNSP_ANNOT_SCREEN_MEDIA,
+              "UnsupportedFeature::kAnnotationScreenMedia value mismatch");
+static_assert(
+    static_cast<int>(UnsupportedFeature::kAnnotationScreenRichMedia) ==
+        FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA,
+    "UnsupportedFeature::kAnnotationScreenRichMedia value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kAnnotationAttachment) ==
+                  FPDF_UNSP_ANNOT_ATTACHMENT,
+              "UnsupportedFeature::kAnnotationAttachment value mismatch");
+static_assert(static_cast<int>(UnsupportedFeature::kAnnotationSignature) ==
+                  FPDF_UNSP_ANNOT_SIG,
+              "UnsupportedFeature::kAnnotationSignature value mismatch");
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FSDK_SetUnSpObjProcessHandler(UNSUPPORT_INFO* unsp_info) {
   if (!unsp_info || unsp_info->version != 1)
     return false;
 
-  CPDF_ModuleMgr::Get()->SetUnsupportInfoAdapter(
-      pdfium::MakeUnique<CFSDK_UnsupportInfo_Adapter>(unsp_info));
+  SetPDFUnsupportInfo(unsp_info);
   return true;
 }
 
-void CheckUnSupportAnnot(CPDF_Document* pDoc, const CPDF_Annot* pPDFAnnot) {
-  CPDF_Annot::Subtype nAnnotSubtype = pPDFAnnot->GetSubtype();
-  if (nAnnotSubtype == CPDF_Annot::Subtype::THREED) {
-    FPDF_UnSupportError(FPDF_UNSP_ANNOT_3DANNOT);
-  } else if (nAnnotSubtype == CPDF_Annot::Subtype::SCREEN) {
-    const CPDF_Dictionary* pAnnotDict = pPDFAnnot->GetAnnotDict();
-    ByteString cbString;
-    if (pAnnotDict->KeyExist("IT"))
-      cbString = pAnnotDict->GetStringFor("IT");
-    if (cbString.Compare("Img") != 0)
-      FPDF_UnSupportError(FPDF_UNSP_ANNOT_SCREEN_MEDIA);
-  } else if (nAnnotSubtype == CPDF_Annot::Subtype::MOVIE) {
-    FPDF_UnSupportError(FPDF_UNSP_ANNOT_MOVIE);
-  } else if (nAnnotSubtype == CPDF_Annot::Subtype::SOUND) {
-    FPDF_UnSupportError(FPDF_UNSP_ANNOT_SOUND);
-  } else if (nAnnotSubtype == CPDF_Annot::Subtype::RICHMEDIA) {
-    FPDF_UnSupportError(FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA);
-  } else if (nAnnotSubtype == CPDF_Annot::Subtype::FILEATTACHMENT) {
-    FPDF_UnSupportError(FPDF_UNSP_ANNOT_ATTACHMENT);
-  } else if (nAnnotSubtype == CPDF_Annot::Subtype::WIDGET) {
-    const CPDF_Dictionary* pAnnotDict = pPDFAnnot->GetAnnotDict();
-    ByteString cbString;
-    if (pAnnotDict->KeyExist("FT"))
-      cbString = pAnnotDict->GetStringFor("FT");
-    if (cbString.Compare("Sig") == 0)
-      FPDF_UnSupportError(FPDF_UNSP_ANNOT_SIG);
-  }
+FPDF_EXPORT void FPDF_CALLCONV FSDK_SetTimeFunction(time_t (*func)()) {
+  FXSYS_SetTimeFunction(func);
 }
 
-bool CheckSharedForm(const CXML_Element* pElement, ByteString cbName) {
-  size_t count = pElement->CountAttrs();
-  for (size_t i = 0; i < count; ++i) {
-    ByteString space;
-    ByteString name;
-    WideString value;
-    pElement->GetAttrByIndex(i, &space, &name, &value);
-    if (space == "xmlns" && name == "adhocwf" &&
-        value == L"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/") {
-      CXML_Element* pVersion =
-          pElement->GetElement("adhocwf", cbName.AsStringView(), 0);
-      if (!pVersion)
-        continue;
-      CXML_Content* pContent = ToContent(pVersion->GetChild(0));
-      if (!pContent)
-        continue;
-      switch (pContent->m_Content.GetInteger()) {
-        case 1:
-          FPDF_UnSupportError(FPDF_UNSP_DOC_SHAREDFORM_ACROBAT);
-          break;
-        case 2:
-          FPDF_UnSupportError(FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM);
-          break;
-        case 0:
-          FPDF_UnSupportError(FPDF_UNSP_DOC_SHAREDFORM_EMAIL);
-          break;
-      }
-    }
-  }
-
-  size_t nCount = pElement->CountChildren();
-  for (size_t i = 0; i < nCount; ++i) {
-    CXML_Element* pChild = ToElement(pElement->GetChild(i));
-    if (pChild && CheckSharedForm(pChild, cbName))
-      return true;
-  }
-  return false;
-}
-
-void CheckUnSupportError(CPDF_Document* pDoc, uint32_t err_code) {
-  // Security
-  if (err_code == FPDF_ERR_SECURITY) {
-    FPDF_UnSupportError(FPDF_UNSP_DOC_SECURITY);
-    return;
-  }
-  if (!pDoc)
-    return;
-
-  // Portfolios and Packages
-  const CPDF_Dictionary* pRootDict = pDoc->GetRoot();
-  if (pRootDict) {
-    ByteString cbString;
-    if (pRootDict->KeyExist("Collection")) {
-      FPDF_UnSupportError(FPDF_UNSP_DOC_PORTABLECOLLECTION);
-      return;
-    }
-    if (pRootDict->KeyExist("Names")) {
-      CPDF_Dictionary* pNameDict = pRootDict->GetDictFor("Names");
-      if (pNameDict && pNameDict->KeyExist("EmbeddedFiles")) {
-        FPDF_UnSupportError(FPDF_UNSP_DOC_ATTACHMENT);
-        return;
-      }
-      if (pNameDict && pNameDict->KeyExist("JavaScript")) {
-        CPDF_Dictionary* pJSDict = pNameDict->GetDictFor("JavaScript");
-        CPDF_Array* pArray = pJSDict ? pJSDict->GetArrayFor("Names") : nullptr;
-        if (pArray) {
-          for (size_t i = 0; i < pArray->GetCount(); i++) {
-            ByteString cbStr = pArray->GetStringAt(i);
-            if (cbStr.Compare("com.adobe.acrobat.SharedReview.Register") == 0) {
-              FPDF_UnSupportError(FPDF_UNSP_DOC_SHAREDREVIEW);
-              return;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // SharedForm
-  CPDF_Metadata metaData(pDoc);
-  const CXML_Element* pElement = metaData.GetRoot();
-  if (pElement)
-    CheckSharedForm(pElement, "workflowType");
-
-#ifndef PDF_ENABLE_XFA
-  // XFA Forms
-  CPDF_InterForm interform(pDoc);
-  if (interform.HasXFAForm())
-    FPDF_UnSupportError(FPDF_UNSP_DOC_XFAFORM);
-#endif  // PDF_ENABLE_XFA
+FPDF_EXPORT void FPDF_CALLCONV
+FSDK_SetLocaltimeFunction(struct tm* (*func)(const time_t* tp)) {
+  FXSYS_SetLocaltimeFunction(func);
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDFDoc_GetPageMode(FPDF_DOCUMENT document) {
@@ -176,7 +91,7 @@
   if (!pRoot)
     return PAGEMODE_UNKNOWN;
 
-  CPDF_Object* pName = pRoot->GetObjectFor("PageMode");
+  const CPDF_Object* pName = pRoot->GetObjectFor("PageMode");
   if (!pName)
     return PAGEMODE_USENONE;
 
diff --git a/fpdfsdk/fpdf_ext_embeddertest.cpp b/fpdfsdk/fpdf_ext_embeddertest.cpp
new file mode 100644
index 0000000..f3ae06e
--- /dev/null
+++ b/fpdfsdk/fpdf_ext_embeddertest.cpp
@@ -0,0 +1,24 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_ext.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFExtEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFExtEmbedderTest, PageModeUnknown) {
+  EXPECT_EQ(PAGEMODE_UNKNOWN, FPDFDoc_GetPageMode(nullptr));
+}
+
+TEST_F(FPDFExtEmbedderTest, PageModeUseNone) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(PAGEMODE_USENONE, FPDFDoc_GetPageMode(document()));
+}
+
+TEST_F(FPDFExtEmbedderTest, PageModeUseOutlines) {
+  EXPECT_TRUE(OpenDocument("use_outlines.pdf"));
+  EXPECT_EQ(PAGEMODE_USEOUTLINES, FPDFDoc_GetPageMode(document()));
+}
diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp
index 4d06693..424e1b4 100644
--- a/fpdfsdk/fpdf_flatten.cpp
+++ b/fpdfsdk/fpdf_flatten.cpp
@@ -11,9 +11,14 @@
 #include <utility>
 #include <vector>
 
+#include "constants/annotation_common.h"
+#include "constants/annotation_flags.h"
+#include "constants/page_object.h"
+#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 #include "core/fpdfapi/page/cpdf_page.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_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
@@ -21,8 +26,7 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfdoc/cpdf_annot.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "third_party/base/stl_util.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 
 enum FPDF_TYPE { MAX, MIN };
 enum FPDF_VALUE { TOP, LEFT, RIGHT, BOTTOM };
@@ -47,16 +51,12 @@
 void GetContentsRect(CPDF_Document* pDoc,
                      CPDF_Dictionary* pDict,
                      std::vector<CFX_FloatRect>* pRectArray) {
-  auto pPDFPage = pdfium::MakeUnique<CPDF_Page>(pDoc, pDict, false);
+  auto pPDFPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
   pPDFPage->ParseContent();
 
-  for (const auto& pPageObject : *pPDFPage->GetPageObjectList()) {
-    CFX_FloatRect rc;
-    rc.left = pPageObject->m_Left;
-    rc.right = pPageObject->m_Right;
-    rc.bottom = pPageObject->m_Bottom;
-    rc.top = pPageObject->m_Top;
-    if (IsValidRect(rc, pDict->GetRectFor("MediaBox")))
+  for (const auto& pPageObject : *pPDFPage) {
+    const CFX_FloatRect& rc = pPageObject->GetRect();
+    if (IsValidRect(rc, pDict->GetRectFor(pdfium::page_object::kMediaBox)))
       pRectArray->push_back(rc);
   }
 }
@@ -73,7 +73,7 @@
   else if (pStream->KeyExist("BBox"))
     rect = pStream->GetRectFor("BBox");
 
-  if (IsValidRect(rect, pPageDic->GetRectFor("MediaBox")))
+  if (IsValidRect(rect, pPageDic->GetRectFor(pdfium::page_object::kMediaBox)))
     pRectArray->push_back(rect);
 
   pObjectArray->push_back(pStream);
@@ -84,7 +84,7 @@
                  std::vector<CFX_FloatRect>* pRectArray,
                  std::vector<CPDF_Dictionary*>* pObjectArray,
                  int nUsage) {
-  if (!pSourceDoc || !pPageDic)
+  if (!pSourceDoc)
     return FLATTEN_FAIL;
 
   GetContentsRect(pSourceDoc, pPageDic, pRectArray);
@@ -92,26 +92,28 @@
   if (!pAnnots)
     return FLATTEN_NOTHINGTODO;
 
-  for (const auto& pAnnot : *pAnnots) {
-    CPDF_Dictionary* pAnnotDic = ToDictionary(pAnnot->GetDirect());
-    if (!pAnnotDic)
+  CPDF_ArrayLocker locker(pAnnots);
+  for (const auto& pAnnot : locker) {
+    CPDF_Dictionary* pAnnotDict = ToDictionary(pAnnot->GetDirect());
+    if (!pAnnotDict)
       continue;
 
-    ByteString sSubtype = pAnnotDic->GetStringFor("Subtype");
+    ByteString sSubtype =
+        pAnnotDict->GetStringFor(pdfium::annotation::kSubtype);
     if (sSubtype == "Popup")
       continue;
 
-    int nAnnotFlag = pAnnotDic->GetIntegerFor("F");
-    if (nAnnotFlag & ANNOTFLAG_HIDDEN)
+    int nAnnotFlag = pAnnotDict->GetIntegerFor("F");
+    if (nAnnotFlag & pdfium::annotation_flags::kHidden)
       continue;
 
     bool bParseStream;
     if (nUsage == FLAT_NORMALDISPLAY)
-      bParseStream = !(nAnnotFlag & ANNOTFLAG_INVISIBLE);
+      bParseStream = !(nAnnotFlag & pdfium::annotation_flags::kInvisible);
     else
-      bParseStream = !!(nAnnotFlag & ANNOTFLAG_PRINT);
+      bParseStream = !!(nAnnotFlag & pdfium::annotation_flags::kPrint);
     if (bParseStream)
-      ParserStream(pPageDic, pAnnotDic, pRectArray, pObjectArray);
+      ParserStream(pPageDic, pAnnotDict, pRectArray, pObjectArray);
   }
   return FLATTEN_SUCCESS;
 }
@@ -168,68 +170,78 @@
   return rcRet;
 }
 
-uint32_t NewIndirectContentsStream(const ByteString& key,
-                                   CPDF_Document* pDocument) {
+ByteString GenerateFlattenedContent(const ByteString& key) {
+  return "q 1 0 0 1 0 0 cm /" + key + " Do Q";
+}
+
+CPDF_Object* NewIndirectContentsStream(CPDF_Document* pDocument,
+                                       const ByteString& contents) {
   CPDF_Stream* pNewContents = pDocument->NewIndirect<CPDF_Stream>(
-      nullptr, 0,
-      pdfium::MakeUnique<CPDF_Dictionary>(pDocument->GetByteStringPool()));
-  ByteString sStream =
-      ByteString::Format("q 1 0 0 1 0 0 cm /%s Do Q", key.c_str());
-  pNewContents->SetData(sStream.raw_str(), sStream.GetLength());
-  return pNewContents->GetObjNum();
+      nullptr, 0, pDocument->New<CPDF_Dictionary>());
+  pNewContents->SetData(contents.raw_span());
+  return pNewContents;
 }
 
 void SetPageContents(const ByteString& key,
                      CPDF_Dictionary* pPage,
                      CPDF_Document* pDocument) {
-  CPDF_Array* pContentsArray = nullptr;
-  CPDF_Stream* pContentsStream = pPage->GetStreamFor("Contents");
-  if (!pContentsStream) {
-    pContentsArray = pPage->GetArrayFor("Contents");
-    if (!pContentsArray) {
-      if (!key.IsEmpty()) {
-        pPage->SetNewFor<CPDF_Reference>(
-            "Contents", pDocument, NewIndirectContentsStream(key, pDocument));
-      }
-      return;
+  CPDF_Array* pContentsArray =
+      pPage->GetArrayFor(pdfium::page_object::kContents);
+  CPDF_Stream* pContentsStream =
+      pPage->GetStreamFor(pdfium::page_object::kContents);
+  if (!pContentsStream && !pContentsArray) {
+    if (!key.IsEmpty()) {
+      pPage->SetFor(
+          pdfium::page_object::kContents,
+          NewIndirectContentsStream(pDocument, GenerateFlattenedContent(key))
+              ->MakeReference(pDocument));
     }
+    return;
   }
-  pPage->ConvertToIndirectObjectFor("Contents", pDocument);
-  if (!pContentsArray) {
-    pContentsArray = pDocument->NewIndirect<CPDF_Array>();
-    auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pContentsStream);
-    pAcc->LoadAllDataFiltered();
+
+  pPage->ConvertToIndirectObjectFor(pdfium::page_object::kContents, pDocument);
+  if (pContentsArray) {
+    pContentsArray->InsertAt(
+        0, NewIndirectContentsStream(pDocument, "q")->MakeReference(pDocument));
+    pContentsArray->Add(
+        NewIndirectContentsStream(pDocument, "Q")->MakeReference(pDocument));
+  } else {
     ByteString sStream = "q\n";
-    ByteString sBody = ByteString(pAcc->GetData(), pAcc->GetSize());
-    sStream = sStream + sBody + "\nQ";
-    pContentsStream->SetDataAndRemoveFilter(sStream.raw_str(),
-                                            sStream.GetLength());
+    {
+      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pContentsStream);
+      pAcc->LoadAllDataFiltered();
+      sStream += ByteString(pAcc->GetSpan());
+      sStream += "\nQ";
+    }
+    pContentsStream->SetDataAndRemoveFilter(sStream.raw_span());
+    pContentsArray = pDocument->NewIndirect<CPDF_Array>();
     pContentsArray->AddNew<CPDF_Reference>(pDocument,
                                            pContentsStream->GetObjNum());
-    pPage->SetNewFor<CPDF_Reference>("Contents", pDocument,
+    pPage->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDocument,
                                      pContentsArray->GetObjNum());
   }
   if (!key.IsEmpty()) {
-    pContentsArray->AddNew<CPDF_Reference>(
-        pDocument, NewIndirectContentsStream(key, pDocument));
+    pContentsArray->Add(
+        NewIndirectContentsStream(pDocument, GenerateFlattenedContent(key))
+            ->MakeReference(pDocument));
   }
 }
 
-CFX_Matrix GetMatrix(CFX_FloatRect rcAnnot,
-                     CFX_FloatRect rcStream,
+CFX_Matrix GetMatrix(const CFX_FloatRect& rcAnnot,
+                     const CFX_FloatRect& rcStream,
                      const CFX_Matrix& matrix) {
   if (rcStream.IsEmpty())
     return CFX_Matrix();
 
-  rcStream = matrix.TransformRect(rcStream);
-  rcStream.Normalize();
+  CFX_FloatRect rcTransformed = matrix.TransformRect(rcStream);
+  rcTransformed.Normalize();
 
-  float a = rcAnnot.Width() / rcStream.Width();
-  float d = rcAnnot.Height() / rcStream.Height();
+  float a = rcAnnot.Width() / rcTransformed.Width();
+  float d = rcAnnot.Height() / rcTransformed.Height();
 
-  float e = rcAnnot.left - rcStream.left * a;
-  float f = rcAnnot.bottom - rcStream.bottom * d;
-  return CFX_Matrix(a, 0, 0, d, e, f);
+  float e = rcAnnot.left - rcTransformed.left * a;
+  float f = rcAnnot.bottom - rcTransformed.bottom * d;
+  return CFX_Matrix(a, 0.0f, 0.0f, d, e, f);
 }
 
 }  // namespace
@@ -239,9 +251,9 @@
   if (!page)
     return FLATTEN_FAIL;
 
-  CPDF_Document* pDocument = pPage->m_pDocument.Get();
-  CPDF_Dictionary* pPageDict = pPage->m_pFormDict.Get();
-  if (!pDocument || !pPageDict)
+  CPDF_Document* pDocument = pPage->GetDocument();
+  CPDF_Dictionary* pPageDict = pPage->GetDict();
+  if (!pDocument)
     return FLATTEN_FAIL;
 
   std::vector<CPDF_Dictionary*> ObjectArray;
@@ -251,49 +263,39 @@
   if (iRet == FLATTEN_NOTHINGTODO || iRet == FLATTEN_FAIL)
     return iRet;
 
-  CFX_FloatRect rcOriginalCB;
   CFX_FloatRect rcMerger = CalculateRect(&RectArray);
-  CFX_FloatRect rcOriginalMB = pPageDict->GetRectFor("MediaBox");
-  if (pPageDict->KeyExist("CropBox"))
-    rcOriginalMB = pPageDict->GetRectFor("CropBox");
+  CFX_FloatRect rcOriginalMB =
+      pPageDict->GetRectFor(pdfium::page_object::kMediaBox);
+  if (pPageDict->KeyExist(pdfium::page_object::kCropBox))
+    rcOriginalMB = pPageDict->GetRectFor(pdfium::page_object::kCropBox);
 
   if (rcOriginalMB.IsEmpty())
     rcOriginalMB = CFX_FloatRect(0.0f, 0.0f, 612.0f, 792.0f);
 
+  CFX_FloatRect rcOriginalCB;
+  if (pPageDict->KeyExist(pdfium::page_object::kCropBox))
+    rcOriginalCB = pPageDict->GetRectFor(pdfium::page_object::kCropBox);
+  if (rcOriginalCB.IsEmpty())
+    rcOriginalCB = rcOriginalMB;
+
   rcMerger.left = std::max(rcMerger.left, rcOriginalMB.left);
   rcMerger.right = std::min(rcMerger.right, rcOriginalMB.right);
   rcMerger.bottom = std::max(rcMerger.bottom, rcOriginalMB.bottom);
   rcMerger.top = std::min(rcMerger.top, rcOriginalMB.top);
-  if (pPageDict->KeyExist("ArtBox"))
-    rcOriginalCB = pPageDict->GetRectFor("ArtBox");
-  else
-    rcOriginalCB = rcOriginalMB;
 
-  if (!rcOriginalMB.IsEmpty()) {
-    CPDF_Array* pMediaBox = pPageDict->SetNewFor<CPDF_Array>("MediaBox");
-    pMediaBox->AddNew<CPDF_Number>(rcOriginalMB.left);
-    pMediaBox->AddNew<CPDF_Number>(rcOriginalMB.bottom);
-    pMediaBox->AddNew<CPDF_Number>(rcOriginalMB.right);
-    pMediaBox->AddNew<CPDF_Number>(rcOriginalMB.top);
+  pPageDict->SetRectFor(pdfium::page_object::kMediaBox, rcOriginalMB);
+  pPageDict->SetRectFor(pdfium::page_object::kCropBox, rcOriginalCB);
+
+  CPDF_Dictionary* pRes =
+      pPageDict->GetDictFor(pdfium::page_object::kResources);
+  if (!pRes) {
+    pRes =
+        pPageDict->SetNewFor<CPDF_Dictionary>(pdfium::page_object::kResources);
   }
 
-  if (!rcOriginalCB.IsEmpty()) {
-    CPDF_Array* pCropBox = pPageDict->SetNewFor<CPDF_Array>("ArtBox");
-    pCropBox->AddNew<CPDF_Number>(rcOriginalCB.left);
-    pCropBox->AddNew<CPDF_Number>(rcOriginalCB.bottom);
-    pCropBox->AddNew<CPDF_Number>(rcOriginalCB.right);
-    pCropBox->AddNew<CPDF_Number>(rcOriginalCB.top);
-  }
-
-  CPDF_Dictionary* pRes = pPageDict->GetDictFor("Resources");
-  if (!pRes)
-    pRes = pPageDict->SetNewFor<CPDF_Dictionary>("Resources");
-
   CPDF_Stream* pNewXObject = pDocument->NewIndirect<CPDF_Stream>(
-      nullptr, 0,
-      pdfium::MakeUnique<CPDF_Dictionary>(pDocument->GetByteStringPool()));
+      nullptr, 0, pDocument->New<CPDF_Dictionary>());
 
-  uint32_t dwObjNum = pNewXObject->GetObjNum();
   CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject");
   if (!pPageXObject)
     pPageXObject = pRes->SetNewFor<CPDF_Dictionary>("XObject");
@@ -304,7 +306,7 @@
     while (i < INT_MAX) {
       ByteString sKey = ByteString::Format("FFT%d", i);
       if (!pPageXObject->KeyExist(sKey)) {
-        key = sKey;
+        key = std::move(sKey);
         break;
       }
       ++i;
@@ -315,40 +317,42 @@
 
   CPDF_Dictionary* pNewXORes = nullptr;
   if (!key.IsEmpty()) {
-    pPageXObject->SetNewFor<CPDF_Reference>(key, pDocument, dwObjNum);
+    pPageXObject->SetNewFor<CPDF_Reference>(key, pDocument,
+                                            pNewXObject->GetObjNum());
+
     CPDF_Dictionary* pNewOXbjectDic = pNewXObject->GetDict();
     pNewXORes = pNewOXbjectDic->SetNewFor<CPDF_Dictionary>("Resources");
     pNewOXbjectDic->SetNewFor<CPDF_Name>("Type", "XObject");
     pNewOXbjectDic->SetNewFor<CPDF_Name>("Subtype", "Form");
     pNewOXbjectDic->SetNewFor<CPDF_Number>("FormType", 1);
-    CFX_FloatRect rcBBox = pPageDict->GetRectFor("ArtBox");
-    pNewOXbjectDic->SetRectFor("BBox", rcBBox);
+    pNewOXbjectDic->SetRectFor("BBox", rcOriginalCB);
   }
 
   for (size_t i = 0; i < ObjectArray.size(); ++i) {
-    CPDF_Dictionary* pAnnotDic = ObjectArray[i];
-    if (!pAnnotDic)
+    CPDF_Dictionary* pAnnotDict = ObjectArray[i];
+    if (!pAnnotDict)
       continue;
 
-    CFX_FloatRect rcAnnot = pAnnotDic->GetRectFor("Rect");
+    CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
     rcAnnot.Normalize();
 
-    ByteString sAnnotState = pAnnotDic->GetStringFor("AS");
-    CPDF_Dictionary* pAnnotAP = pAnnotDic->GetDictFor("AP");
+    ByteString sAnnotState = pAnnotDict->GetStringFor("AS");
+    CPDF_Dictionary* pAnnotAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
     if (!pAnnotAP)
       continue;
 
     CPDF_Stream* pAPStream = pAnnotAP->GetStreamFor("N");
     if (!pAPStream) {
-      CPDF_Dictionary* pAPDic = pAnnotAP->GetDictFor("N");
-      if (!pAPDic)
+      CPDF_Dictionary* pAPDict = pAnnotAP->GetDictFor("N");
+      if (!pAPDict)
         continue;
 
       if (!sAnnotState.IsEmpty()) {
-        pAPStream = pAPDic->GetStreamFor(sAnnotState);
+        pAPStream = pAPDict->GetStreamFor(sAnnotState);
       } else {
-        if (pAPDic->GetCount() > 0) {
-          CPDF_Object* pFirstObj = pAPDic->begin()->second.get();
+        if (pAPDict->size() > 0) {
+          CPDF_DictionaryLocker locker(pAPDict);
+          CPDF_Object* pFirstObj = locker.begin()->second.Get();
           if (pFirstObj) {
             if (pFirstObj->IsReference())
               pFirstObj = pFirstObj->GetDirect();
@@ -362,27 +366,28 @@
     if (!pAPStream)
       continue;
 
-    CPDF_Dictionary* pAPDic = pAPStream->GetDict();
+    CPDF_Dictionary* pAPDict = pAPStream->GetDict();
     CFX_FloatRect rcStream;
-    if (pAPDic->KeyExist("Rect"))
-      rcStream = pAPDic->GetRectFor("Rect");
-    else if (pAPDic->KeyExist("BBox"))
-      rcStream = pAPDic->GetRectFor("BBox");
+    if (pAPDict->KeyExist("Rect"))
+      rcStream = pAPDict->GetRectFor("Rect");
+    else if (pAPDict->KeyExist("BBox"))
+      rcStream = pAPDict->GetRectFor("BBox");
+    rcStream.Normalize();
 
     if (rcStream.IsEmpty())
       continue;
 
     CPDF_Object* pObj = pAPStream;
     if (pObj->IsInline()) {
-      std::unique_ptr<CPDF_Object> pNew = pObj->Clone();
-      pObj = pNew.get();
+      RetainPtr<CPDF_Object> pNew = pObj->Clone();
+      pObj = pNew.Get();
       pDocument->AddIndirectObject(std::move(pNew));
     }
 
-    CPDF_Dictionary* pObjDic = pObj->GetDict();
-    if (pObjDic) {
-      pObjDic->SetNewFor<CPDF_Name>("Type", "XObject");
-      pObjDic->SetNewFor<CPDF_Name>("Subtype", "Form");
+    CPDF_Dictionary* pObjDict = pObj->GetDict();
+    if (pObjDict) {
+      pObjDict->SetNewFor<CPDF_Name>("Type", "XObject");
+      pObjDict->SetNewFor<CPDF_Name>("Subtype", "Form");
     }
 
     CPDF_Dictionary* pXObject = pNewXORes->GetDictFor("XObject");
@@ -393,14 +398,22 @@
     pXObject->SetNewFor<CPDF_Reference>(sFormName, pDocument,
                                         pObj->GetObjNum());
 
-    auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pNewXObject);
-    pAcc->LoadAllDataFiltered();
-    ByteString sStream(pAcc->GetData(), pAcc->GetSize());
-    CFX_Matrix matrix = pAPDic->GetMatrixFor("Matrix");
+    ByteString sStream;
+    {
+      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pNewXObject);
+      pAcc->LoadAllDataFiltered();
+      sStream = ByteString(pAcc->GetSpan());
+    }
+    CFX_Matrix matrix = pAPDict->GetMatrixFor("Matrix");
     CFX_Matrix m = GetMatrix(rcAnnot, rcStream, matrix);
-    sStream += ByteString::Format("q %f 0 0 %f %f %f cm /%s Do Q\n", m.a, m.d,
-                                  m.e, m.f, sFormName.c_str());
-    pNewXObject->SetDataAndRemoveFilter(sStream.raw_str(), sStream.GetLength());
+    m.b = 0;
+    m.c = 0;
+    std::ostringstream buf;
+    buf << m;
+    ByteString str(buf);
+    sStream += ByteString::Format("q %s cm /%s Do Q\n", str.c_str(),
+                                  sFormName.c_str());
+    pNewXObject->SetDataAndRemoveFilter(sStream.raw_span());
   }
   pPageDict->RemoveFor("Annots");
   return FLATTEN_SUCCESS;
diff --git a/fpdfsdk/fpdf_flatten_embeddertest.cpp b/fpdfsdk/fpdf_flatten_embeddertest.cpp
index a8915fe..2b244c4 100644
--- a/fpdfsdk/fpdf_flatten_embeddertest.cpp
+++ b/fpdfsdk/fpdf_flatten_embeddertest.cpp
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "build/build_config.h"
 #include "public/fpdf_flatten.h"
 #include "public/fpdfview.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 
 namespace {
 
-class FPDFFlattenEmbeddertest : public EmbedderTest {};
+class FPDFFlattenEmbedderTest : public EmbedderTest {};
 
 }  // namespace
 
-TEST_F(FPDFFlattenEmbeddertest, FlatNothing) {
+TEST_F(FPDFFlattenEmbedderTest, FlatNothing) {
   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
@@ -22,7 +22,7 @@
   UnloadPage(page);
 }
 
-TEST_F(FPDFFlattenEmbeddertest, FlatNormal) {
+TEST_F(FPDFFlattenEmbedderTest, FlatNormal) {
   EXPECT_TRUE(OpenDocument("annotiter.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
@@ -30,10 +30,88 @@
   UnloadPage(page);
 }
 
-TEST_F(FPDFFlattenEmbeddertest, FlatPrint) {
+TEST_F(FPDFFlattenEmbedderTest, FlatPrint) {
   EXPECT_TRUE(OpenDocument("annotiter.pdf"));
   FPDF_PAGE page = LoadPage(0);
   EXPECT_TRUE(page);
   EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
   UnloadPage(page);
 }
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_BUG_861842 DISABLED_BUG_861842
+#else
+#define MAYBE_BUG_861842 BUG_861842
+#endif
+TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_861842) {
+#if defined(OS_WIN)
+  static const char kCheckboxHash[] = "95fba3cb7bce7e0d3c94279f60984e17";
+#elif defined(OS_MACOSX)
+  static const char kCheckboxHash[] = "0a1d1d63d4452bc26a1c5c547d309655";
+#else
+  static const char kCheckboxHash[] = "594265790b81df2d93120d33b72a6ada";
+#endif
+
+  EXPECT_TRUE(OpenDocument("bug_861842.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+  CompareBitmap(bitmap.get(), 100, 120, kCheckboxHash);
+
+  EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  UnloadPage(page);
+
+  // TODO(crbug.com/861842): This should not render blank.
+  static const char kBlankPageHash[] = "48400809c3862dae64b0cd00d51057a4";
+  VerifySavedDocument(100, 120, kBlankPageHash);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_BUG_890322 DISABLED_BUG_890322
+#else
+#define MAYBE_BUG_890322 BUG_890322
+#endif
+TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_890322) {
+  static const char md5_hash[] = "6c674642154408e877d88c6c082d67e9";
+  EXPECT_TRUE(OpenDocument("bug_890322.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+  CompareBitmap(bitmap.get(), 200, 200, md5_hash);
+
+  EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  UnloadPage(page);
+
+  VerifySavedDocument(200, 200, md5_hash);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_BUG_896366 DISABLED_BUG_896366
+#else
+#define MAYBE_BUG_896366 BUG_896366
+#endif
+TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_896366) {
+  static const char md5_hash[] = "f71ab085c52c8445ae785eca3ec858b1";
+  EXPECT_TRUE(OpenDocument("bug_896366.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+  CompareBitmap(bitmap.get(), 612, 792, md5_hash);
+
+  EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  UnloadPage(page);
+
+  VerifySavedDocument(612, 792, md5_hash);
+}
diff --git a/fpdfsdk/fpdf_formfill.cpp b/fpdfsdk/fpdf_formfill.cpp
new file mode 100644
index 0000000..a3f3022
--- /dev/null
+++ b/fpdfsdk/fpdf_formfill.cpp
@@ -0,0 +1,731 @@
+// 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 "public/fpdf_formfill.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_occontext.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fpdfdoc/cpdf_formcontrol.h"
+#include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "fpdfsdk/cpdfsdk_actionhandler.h"
+#include "fpdfsdk/cpdfsdk_baannothandler.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/cpdfsdk_widgethandler.h"
+#include "public/fpdfview.h"
+#include "third_party/base/ptr_util.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h"
+
+static_assert(static_cast<int>(AlertButton::kDefault) ==
+                  JSPLATFORM_ALERT_BUTTON_DEFAULT,
+              "Default alert button types must match");
+static_assert(static_cast<int>(AlertButton::kOK) == JSPLATFORM_ALERT_BUTTON_OK,
+              "OK alert button types must match");
+static_assert(static_cast<int>(AlertButton::kOKCancel) ==
+                  JSPLATFORM_ALERT_BUTTON_OKCANCEL,
+              "OKCancel alert button types must match");
+static_assert(static_cast<int>(AlertButton::kYesNo) ==
+                  JSPLATFORM_ALERT_BUTTON_YESNO,
+              "YesNo alert button types must match");
+static_assert(static_cast<int>(AlertButton::kYesNoCancel) ==
+                  JSPLATFORM_ALERT_BUTTON_YESNOCANCEL,
+              "YesNoCancel alert button types must match");
+
+static_assert(static_cast<int>(AlertIcon::kDefault) ==
+                  JSPLATFORM_ALERT_ICON_DEFAULT,
+              "Default alert icon types must match");
+static_assert(static_cast<int>(AlertIcon::kError) ==
+                  JSPLATFORM_ALERT_ICON_ERROR,
+              "Error alert icon types must match");
+static_assert(static_cast<int>(AlertIcon::kWarning) ==
+                  JSPLATFORM_ALERT_ICON_WARNING,
+              "Warning alert icon types must match");
+static_assert(static_cast<int>(AlertIcon::kQuestion) ==
+                  JSPLATFORM_ALERT_ICON_QUESTION,
+              "Question alert icon types must match");
+static_assert(static_cast<int>(AlertIcon::kStatus) ==
+                  JSPLATFORM_ALERT_ICON_STATUS,
+              "Status alert icon types must match");
+static_assert(static_cast<int>(AlertIcon::kAsterisk) ==
+                  JSPLATFORM_ALERT_ICON_ASTERISK,
+              "Asterisk alert icon types must match");
+
+static_assert(static_cast<int>(AlertReturn::kOK) == JSPLATFORM_ALERT_RETURN_OK,
+              "OK alert return types must match");
+static_assert(static_cast<int>(AlertReturn::kCancel) ==
+                  JSPLATFORM_ALERT_RETURN_CANCEL,
+              "Cancel alert return types must match");
+static_assert(static_cast<int>(AlertReturn::kNo) == JSPLATFORM_ALERT_RETURN_NO,
+              "No alert return types must match");
+static_assert(static_cast<int>(AlertReturn::kYes) ==
+                  JSPLATFORM_ALERT_RETURN_YES,
+              "Yes alert return types must match");
+
+static_assert(static_cast<int>(FormType::kNone) == FORMTYPE_NONE,
+              "None form types must match");
+static_assert(static_cast<int>(FormType::kAcroForm) == FORMTYPE_ACRO_FORM,
+              "AcroForm form types must match");
+static_assert(static_cast<int>(FormType::kXFAFull) == FORMTYPE_XFA_FULL,
+              "XFA full form types must match");
+static_assert(static_cast<int>(FormType::kXFAForeground) ==
+                  FORMTYPE_XFA_FOREGROUND,
+              "XFA foreground form types must match");
+#endif  // PDF_ENABLE_XFA
+
+static_assert(static_cast<int>(FormFieldType::kUnknown) ==
+                  FPDF_FORMFIELD_UNKNOWN,
+              "Unknown form field types must match");
+static_assert(static_cast<int>(FormFieldType::kPushButton) ==
+                  FPDF_FORMFIELD_PUSHBUTTON,
+              "PushButton form field types must match");
+static_assert(static_cast<int>(FormFieldType::kCheckBox) ==
+                  FPDF_FORMFIELD_CHECKBOX,
+              "CheckBox form field types must match");
+static_assert(static_cast<int>(FormFieldType::kRadioButton) ==
+                  FPDF_FORMFIELD_RADIOBUTTON,
+              "RadioButton form field types must match");
+static_assert(static_cast<int>(FormFieldType::kComboBox) ==
+                  FPDF_FORMFIELD_COMBOBOX,
+              "ComboBox form field types must match");
+static_assert(static_cast<int>(FormFieldType::kListBox) ==
+                  FPDF_FORMFIELD_LISTBOX,
+              "ListBox form field types must match");
+static_assert(static_cast<int>(FormFieldType::kTextField) ==
+                  FPDF_FORMFIELD_TEXTFIELD,
+              "TextField form field types must match");
+static_assert(static_cast<int>(FormFieldType::kSignature) ==
+                  FPDF_FORMFIELD_SIGNATURE,
+              "Signature form field types must match");
+#ifdef PDF_ENABLE_XFA
+static_assert(static_cast<int>(FormFieldType::kXFA) == FPDF_FORMFIELD_XFA,
+              "XFA form field types must match");
+static_assert(static_cast<int>(FormFieldType::kXFA_CheckBox) ==
+                  FPDF_FORMFIELD_XFA_CHECKBOX,
+              "XFA CheckBox form field types must match");
+static_assert(static_cast<int>(FormFieldType::kXFA_ComboBox) ==
+                  FPDF_FORMFIELD_XFA_COMBOBOX,
+              "XFA ComboBox form field types must match");
+static_assert(static_cast<int>(FormFieldType::kXFA_ImageField) ==
+                  FPDF_FORMFIELD_XFA_IMAGEFIELD,
+              "XFA ImageField form field types must match");
+static_assert(static_cast<int>(FormFieldType::kXFA_ListBox) ==
+                  FPDF_FORMFIELD_XFA_LISTBOX,
+              "XFA ListBox form field types must match");
+static_assert(static_cast<int>(FormFieldType::kXFA_PushButton) ==
+                  FPDF_FORMFIELD_XFA_PUSHBUTTON,
+              "XFA PushButton form field types must match");
+static_assert(static_cast<int>(FormFieldType::kXFA_Signature) ==
+                  FPDF_FORMFIELD_XFA_SIGNATURE,
+              "XFA Signature form field types must match");
+static_assert(static_cast<int>(FormFieldType::kXFA_TextField) ==
+                  FPDF_FORMFIELD_XFA_TEXTFIELD,
+              "XFA TextField form field types must match");
+#endif  // PDF_ENABLE_XFA
+static_assert(kFormFieldTypeCount == FPDF_FORMFIELD_COUNT,
+              "Number of form field types must match");
+
+static_assert(static_cast<int>(CPDF_AAction::kCloseDocument) ==
+                  FPDFDOC_AACTION_WC,
+              "CloseDocument action must match");
+static_assert(static_cast<int>(CPDF_AAction::kSaveDocument) ==
+                  FPDFDOC_AACTION_WS,
+              "SaveDocument action must match");
+static_assert(static_cast<int>(CPDF_AAction::kDocumentSaved) ==
+                  FPDFDOC_AACTION_DS,
+              "DocumentSaved action must match");
+static_assert(static_cast<int>(CPDF_AAction::kPrintDocument) ==
+                  FPDFDOC_AACTION_WP,
+              "PrintDocument action must match");
+static_assert(static_cast<int>(CPDF_AAction::kDocumentPrinted) ==
+                  FPDFDOC_AACTION_DP,
+              "DocumentPrinted action must match");
+
+namespace {
+
+CPDFSDK_PageView* FormHandleToPageView(FPDF_FORMHANDLE hHandle,
+                                       FPDF_PAGE fpdf_page) {
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(fpdf_page);
+  if (!pPage)
+    return nullptr;
+
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  return pFormFillEnv ? pFormFillEnv->GetPageView(pPage, true) : nullptr;
+}
+
+void FFLCommon(FPDF_FORMHANDLE hHandle,
+               FPDF_BITMAP bitmap,
+               FPDF_RECORDER recorder,
+               FPDF_PAGE fpdf_page,
+               int start_x,
+               int start_y,
+               int size_x,
+               int size_y,
+               int rotate,
+               int flags) {
+  if (!hHandle)
+    return;
+
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(fpdf_page);
+  if (!pPage)
+    return;
+
+  CPDF_Document* pPDFDoc = pPage->GetDocument();
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, fpdf_page);
+
+  const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
+  CFX_Matrix matrix = pPage->GetDisplayMatrix(rect, rotate);
+
+  auto pDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
+#ifdef _SKIA_SUPPORT_
+  pDevice->AttachRecorder(static_cast<SkPictureRecorder*>(recorder));
+#endif
+
+  RetainPtr<CFX_DIBitmap> holder(CFXDIBitmapFromFPDFBitmap(bitmap));
+  pDevice->Attach(holder, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
+  {
+    CFX_RenderDevice::StateRestorer restorer(pDevice.get());
+    pDevice->SetClip_Rect(rect);
+
+    CPDF_RenderOptions options;
+    options.GetOptions().bClearType = !!(flags & FPDF_LCD_TEXT);
+
+    // Grayscale output
+    if (flags & FPDF_GRAYSCALE)
+      options.SetColorMode(CPDF_RenderOptions::kGray);
+
+    options.SetDrawAnnots(flags & FPDF_ANNOT);
+    options.SetOCContext(
+        pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::View));
+
+    if (pPageView)
+      pPageView->PageView_OnDraw(pDevice.get(), matrix, &options, rect);
+  }
+
+#ifdef _SKIA_SUPPORT_PATHS_
+  pDevice->Flush(true);
+  holder->UnPreMultiply();
+#endif
+}
+
+}  // namespace
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPage_HasFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
+                             FPDF_PAGE page,
+                             double page_x,
+                             double page_y) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (pPage) {
+    CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+    if (!pForm)
+      return -1;
+
+    CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+    CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
+        pPage,
+        CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
+        nullptr);
+    if (!pFormCtrl)
+      return -1;
+    CPDF_FormField* pFormField = pFormCtrl->GetField();
+    return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1;
+  }
+
+#ifdef PDF_ENABLE_XFA
+  CPDFXFA_Page* pXFAPage = ToXFAPage(IPDFPageFromFPDFPage(page));
+  if (pXFAPage) {
+    return pXFAPage->HasFormFieldAtPoint(
+        CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)));
+  }
+#endif  // PDF_ENABLE_XFA
+
+  return -1;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPage_FormFieldZOrderAtPoint(FPDF_FORMHANDLE hHandle,
+                                FPDF_PAGE page,
+                                double page_x,
+                                double page_y) {
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return -1;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return -1;
+
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  int z_order = -1;
+  pPDFForm->GetControlAtPoint(
+      pPage, CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
+      &z_order);
+  return z_order;
+}
+
+FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV
+FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document,
+                                FPDF_FORMFILLINFO* formInfo) {
+#ifdef PDF_ENABLE_XFA
+  constexpr int kRequiredVersion = 2;
+#else   // PDF_ENABLE_XFA
+  constexpr int kRequiredVersion = 1;
+#endif  // PDF_ENABLE_XFA
+  if (!formInfo || formInfo->version != kRequiredVersion)
+    return nullptr;
+
+  auto* pDocument = CPDFDocumentFromFPDFDocument(document);
+  if (!pDocument)
+    return nullptr;
+
+  std::unique_ptr<IPDFSDK_AnnotHandler> pXFAHandler;
+#ifdef PDF_ENABLE_XFA
+  CPDFXFA_Context* pContext = nullptr;
+  if (!formInfo->xfa_disabled) {
+    if (!pDocument->GetExtension()) {
+      pDocument->SetExtension(pdfium::MakeUnique<CPDFXFA_Context>(pDocument));
+    }
+
+    // If the CPDFXFA_Context has a FormFillEnvironment already then we've done
+    // this and can just return the old Env. Otherwise, we'll end up setting a
+    // new environment into the XFADocument and, that could get weird.
+    pContext = static_cast<CPDFXFA_Context*>(pDocument->GetExtension());
+    if (pContext->GetFormFillEnv()) {
+      return FPDFFormHandleFromCPDFSDKFormFillEnvironment(
+          pContext->GetFormFillEnv());
+    }
+    pXFAHandler = pdfium::MakeUnique<CPDFXFA_WidgetHandler>();
+  }
+#endif  // PDF_ENABLE_XFA
+
+  auto pFormFillEnv = pdfium::MakeUnique<CPDFSDK_FormFillEnvironment>(
+      pDocument, formInfo,
+      pdfium::MakeUnique<CPDFSDK_AnnotHandlerMgr>(
+          pdfium::MakeUnique<CPDFSDK_BAAnnotHandler>(),
+          pdfium::MakeUnique<CPDFSDK_WidgetHandler>(), std::move(pXFAHandler)));
+
+#ifdef PDF_ENABLE_XFA
+  if (pContext)
+    pContext->SetFormFillEnv(pFormFillEnv.get());
+#endif  // PDF_ENABLE_XFA
+
+  ReportUnsupportedXFA(pDocument);
+
+  return FPDFFormHandleFromCPDFSDKFormFillEnvironment(
+      pFormFillEnv.release());  // Caller takes ownership.
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDFDOC_ExitFormFillEnvironment(FPDF_FORMHANDLE hHandle) {
+  if (!hHandle)
+    return;
+
+  // Take back ownership of the form fill environment. This is the inverse of
+  // FPDFDOC_InitFormFillEnvironment() above.
+  std::unique_ptr<CPDFSDK_FormFillEnvironment> pFormFillEnv(
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle));
+
+#ifdef PDF_ENABLE_XFA
+  // Reset the focused annotations and remove the SDK document from the
+  // XFA document.
+  pFormFillEnv->ClearAllFocusedAnnots();
+  // If the document was closed first, it's possible the XFA document
+  // is now a nullptr.
+  auto* pContext =
+      static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
+  if (pContext)
+    pContext->SetFormFillEnv(nullptr);
+#endif  // PDF_ENABLE_XFA
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseMove(FPDF_FORMHANDLE hHandle,
+                                                     FPDF_PAGE page,
+                                                     int modifier,
+                                                     double page_x,
+                                                     double page_y) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->OnMouseMove(CFX_PointF(page_x, page_y), modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle,
+                                                 FPDF_PAGE page,
+                                                 int modifier,
+                                                 double page_x,
+                                                 double page_y) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->OnFocus(CFX_PointF(page_x, page_y), modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle,
+                                                       FPDF_PAGE page,
+                                                       int modifier,
+                                                       double page_x,
+                                                       double page_y) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+#ifdef PDF_ENABLE_CLICK_LOGGING
+  fprintf(stderr, "mousedown,left,%d,%d\n", static_cast<int>(round(page_x)),
+          static_cast<int>(round(page_y)));
+#endif  // PDF_ENABLE_CLICK_LOGGING
+  return pPageView->OnLButtonDown(CFX_PointF(page_x, page_y), modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle,
+                                                     FPDF_PAGE page,
+                                                     int modifier,
+                                                     double page_x,
+                                                     double page_y) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+#ifdef PDF_ENABLE_CLICK_LOGGING
+  fprintf(stderr, "mouseup,left,%d,%d\n", static_cast<int>(round(page_x)),
+          static_cast<int>(round(page_y)));
+#endif  // PDF_ENABLE_CLICK_LOGGING
+  return pPageView->OnLButtonUp(CFX_PointF(page_x, page_y), modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_OnLButtonDoubleClick(FPDF_FORMHANDLE hHandle,
+                          FPDF_PAGE page,
+                          int modifier,
+                          double page_x,
+                          double page_y) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+#ifdef PDF_ENABLE_CLICK_LOGGING
+  fprintf(stderr, "mousedown,doubleleft,%d,%d\n",
+          static_cast<int>(round(page_x)), static_cast<int>(round(page_y)));
+#endif  // PDF_ENABLE_CLICK_LOGGING
+  return pPageView->OnLButtonDblClk(CFX_PointF(page_x, page_y), modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle,
+                                                       FPDF_PAGE page,
+                                                       int modifier,
+                                                       double page_x,
+                                                       double page_y) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+#ifdef PDF_ENABLE_CLICK_LOGGING
+  fprintf(stderr, "mousedown,right,%d,%d\n", static_cast<int>(round(page_x)),
+          static_cast<int>(round(page_y)));
+#endif  // PDF_ENABLE_CLICK_LOGGING
+  return pPageView->OnRButtonDown(CFX_PointF(page_x, page_y), modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle,
+                                                     FPDF_PAGE page,
+                                                     int modifier,
+                                                     double page_x,
+                                                     double page_y) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+#ifdef PDF_ENABLE_CLICK_LOGGING
+  fprintf(stderr, "mouseup,right,%d,%d\n", static_cast<int>(round(page_x)),
+          static_cast<int>(round(page_y)));
+#endif  // PDF_ENABLE_CLICK_LOGGING
+  return pPageView->OnRButtonUp(CFX_PointF(page_x, page_y), modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle,
+                                                   FPDF_PAGE page,
+                                                   int nKeyCode,
+                                                   int modifier) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->OnKeyDown(nKeyCode, modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle,
+                                                 FPDF_PAGE page,
+                                                 int nKeyCode,
+                                                 int modifier) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->OnKeyUp(nKeyCode, modifier);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle,
+                                                FPDF_PAGE page,
+                                                int nChar,
+                                                int modifier) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->OnChar(nChar, modifier);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FORM_GetFocusedText(FPDF_FORMHANDLE hHandle,
+                    FPDF_PAGE page,
+                    void* buffer,
+                    unsigned long buflen) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return 0;
+
+  return Utf16EncodeMaybeCopyAndReturnLength(pPageView->GetFocusedFormText(),
+                                             buffer, buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FORM_GetSelectedText(FPDF_FORMHANDLE hHandle,
+                     FPDF_PAGE page,
+                     void* buffer,
+                     unsigned long buflen) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return 0;
+
+  return Utf16EncodeMaybeCopyAndReturnLength(pPageView->GetSelectedText(),
+                                             buffer, buflen);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle,
+                                                     FPDF_PAGE page,
+                                                     FPDF_WIDESTRING wsText) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return;
+
+  pPageView->ReplaceSelection(WideStringFromFPDFWideString(wsText));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanUndo(FPDF_FORMHANDLE hHandle,
+                                                 FPDF_PAGE page) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->CanUndo();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanRedo(FPDF_FORMHANDLE hHandle,
+                                                 FPDF_PAGE page) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->CanRedo();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Undo(FPDF_FORMHANDLE hHandle,
+                                              FPDF_PAGE page) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->Undo();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Redo(FPDF_FORMHANDLE hHandle,
+                                              FPDF_PAGE page) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->Redo();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (!pFormFillEnv)
+    return false;
+  return pFormFillEnv->KillFocusAnnot(0);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle,
+                                            FPDF_BITMAP bitmap,
+                                            FPDF_PAGE page,
+                                            int start_x,
+                                            int start_y,
+                                            int size_x,
+                                            int size_y,
+                                            int rotate,
+                                            int flags) {
+  FFLCommon(hHandle, bitmap, nullptr, page, start_x, start_y, size_x, size_y,
+            rotate, flags);
+}
+
+#ifdef _SKIA_SUPPORT_
+FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLRecord(FPDF_FORMHANDLE hHandle,
+                                              FPDF_RECORDER recorder,
+                                              FPDF_PAGE page,
+                                              int start_x,
+                                              int start_y,
+                                              int size_x,
+                                              int size_y,
+                                              int rotate,
+                                              int flags) {
+  FFLCommon(hHandle, nullptr, recorder, page, start_x, start_y, size_x, size_y,
+            rotate, flags);
+}
+#endif
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDF_SetFormFieldHighlightColor(FPDF_FORMHANDLE hHandle,
+                                int fieldType,
+                                unsigned long color) {
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return;
+
+  Optional<FormFieldType> cast_input =
+      CPDF_FormField::IntToFormFieldType(fieldType);
+  if (!cast_input)
+    return;
+
+  if (cast_input.value() == FormFieldType::kUnknown)
+    pForm->SetAllHighlightColors(color);
+  else
+    pForm->SetHighlightColor(color, cast_input.value());
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDF_SetFormFieldHighlightAlpha(FPDF_FORMHANDLE hHandle, unsigned char alpha) {
+  if (CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle))
+    pForm->SetHighlightAlpha(alpha);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDF_RemoveFormFieldHighlight(FPDF_FORMHANDLE hHandle) {
+  if (CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle))
+    pForm->RemoveAllHighLights();
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FORM_OnAfterLoadPage(FPDF_PAGE page,
+                                                    FPDF_FORMHANDLE hHandle) {
+  if (CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page))
+    pPageView->SetValid(true);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FORM_OnBeforeClosePage(FPDF_PAGE page,
+                                                      FPDF_FORMHANDLE hHandle) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (!pFormFillEnv)
+    return;
+
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage, false);
+  if (pPageView) {
+    pPageView->SetValid(false);
+    // RemovePageView() takes care of the delete for us.
+    pFormFillEnv->RemovePageView(pPage);
+  }
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FORM_DoDocumentJSAction(FPDF_FORMHANDLE hHandle) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (pFormFillEnv && pFormFillEnv->IsJSPlatformPresent())
+    pFormFillEnv->ProcJavascriptAction();
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FORM_DoDocumentOpenAction(FPDF_FORMHANDLE hHandle) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (pFormFillEnv && pFormFillEnv->IsJSPlatformPresent())
+    pFormFillEnv->ProcOpenAction();
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FORM_DoDocumentAAction(FPDF_FORMHANDLE hHandle,
+                                                      int aaType) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (!pFormFillEnv)
+    return;
+
+  CPDF_Document* pDoc = pFormFillEnv->GetPDFDocument();
+  CPDF_Dictionary* pDict = pDoc->GetRoot();
+  if (!pDict)
+    return;
+
+  CPDF_AAction aa(pDict->GetDictFor("AA"));
+  auto type = static_cast<CPDF_AAction::AActionType>(aaType);
+  if (aa.ActionExist(type)) {
+    CPDF_Action action = aa.GetAction(type);
+    pFormFillEnv->GetActionHandler()->DoAction_Document(action, type,
+                                                        pFormFillEnv);
+  }
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page,
+                                                  FPDF_FORMHANDLE hHandle,
+                                                  int aaType) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv =
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
+  if (!pFormFillEnv)
+    return;
+
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
+  CPDF_Page* pPDFPage = CPDFPageFromFPDFPage(page);
+  if (!pPDFPage)
+    return;
+
+  if (!pFormFillEnv->GetPageView(pPage, false))
+    return;
+
+  CPDFSDK_ActionHandler* pActionHandler = pFormFillEnv->GetActionHandler();
+  CPDF_Dictionary* pPageDict = pPDFPage->GetDict();
+  CPDF_AAction aa(pPageDict->GetDictFor("AA"));
+  CPDF_AAction::AActionType type = aaType == FPDFPAGE_AACTION_OPEN
+                                       ? CPDF_AAction::kOpenPage
+                                       : CPDF_AAction::kClosePage;
+  if (aa.ActionExist(type)) {
+    CPDF_Action action = aa.GetAction(type);
+    pActionHandler->DoAction_Page(action, type, pFormFillEnv);
+  }
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_SetIndexSelected(FPDF_FORMHANDLE hHandle,
+                      FPDF_PAGE page,
+                      int index,
+                      FPDF_BOOL selected) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->SetIndexSelected(index, selected);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_IsIndexSelected(FPDF_FORMHANDLE hHandle, FPDF_PAGE page, int index) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return false;
+  return pPageView->IsIndexSelected(index);
+}
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
new file mode 100644
index 0000000..3ae272b
--- /dev/null
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -0,0 +1,2522 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_formfill.h"
+#include "public/fpdf_fwlevent.h"
+#include "public/fpdf_progressive.h"
+#include "testing/embedder_test.h"
+#include "testing/embedder_test_mock_delegate.h"
+#include "testing/embedder_test_timer_handling_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+using FPDFFormFillEmbedderTest = EmbedderTest;
+
+// A base class for many related tests that involve clicking and typing into
+// form fields.
+class FPDFFormFillInteractiveEmbedderTest : public FPDFFormFillEmbedderTest {
+ protected:
+  FPDFFormFillInteractiveEmbedderTest() = default;
+  ~FPDFFormFillInteractiveEmbedderTest() override = default;
+
+  void SetUp() override {
+    FPDFFormFillEmbedderTest::SetUp();
+    ASSERT_TRUE(OpenDocument(GetDocumentName()));
+    page_ = LoadPage(0);
+    ASSERT_TRUE(page_);
+    FormSanityChecks();
+  }
+
+  void TearDown() override {
+    UnloadPage(page_);
+    FPDFFormFillEmbedderTest::TearDown();
+  }
+
+  // Returns the name of the PDF to use.
+  virtual const char* GetDocumentName() const = 0;
+
+  // Returns the type of field(s) in the PDF.
+  virtual int GetFormType() const = 0;
+
+  // Optionally do some sanity check on the document after loading.
+  virtual void FormSanityChecks() {}
+
+  FPDF_PAGE page() { return page_; }
+
+  int GetFormTypeAtPoint(const CFX_PointF& point) {
+    return FPDFPage_HasFormFieldAtPoint(form_handle(), page_, point.x, point.y);
+  }
+
+  void ClickOnFormFieldAtPoint(const CFX_PointF& point) {
+    // Click on the text field or combobox as specified by coordinates.
+    FORM_OnMouseMove(form_handle(), page_, 0, point.x, point.y);
+    FORM_OnLButtonDown(form_handle(), page_, 0, point.x, point.y);
+    FORM_OnLButtonUp(form_handle(), page_, 0, point.x, point.y);
+  }
+
+  void DoubleClickOnFormFieldAtPoint(const CFX_PointF& point) {
+    // Click on the text field or combobox as specified by coordinates.
+    FORM_OnMouseMove(form_handle(), page_, 0, point.x, point.y);
+    FORM_OnLButtonDoubleClick(form_handle(), page_, 0, point.x, point.y);
+  }
+
+  void TypeTextIntoTextField(int num_chars, const CFX_PointF& point) {
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(point));
+    ClickOnFormFieldAtPoint(point);
+
+    // Type text starting with 'A' to as many chars as specified by |num_chars|.
+    for (int i = 0; i < num_chars; ++i) {
+      FORM_OnChar(form_handle(), page_, 'A' + i, 0);
+    }
+  }
+
+  // Navigates to text field using the mouse and then selects text via the
+  // shift and specfied left or right arrow key.
+  void SelectTextWithKeyboard(int num_chars,
+                              int arrow_key,
+                              const CFX_PointF& point) {
+    // Navigate to starting position for selection.
+    ClickOnFormFieldAtPoint(point);
+
+    // Hold down shift (and don't release until entire text is selected).
+    FORM_OnKeyDown(form_handle(), page_, FWL_VKEY_Shift, 0);
+
+    // Select text char by char via left or right arrow key.
+    for (int i = 0; i < num_chars; ++i) {
+      FORM_OnKeyDown(form_handle(), page_, arrow_key, FWL_EVENTFLAG_ShiftKey);
+      FORM_OnKeyUp(form_handle(), page_, arrow_key, FWL_EVENTFLAG_ShiftKey);
+    }
+    FORM_OnKeyUp(form_handle(), page_, FWL_VKEY_Shift, 0);
+  }
+
+  // Uses the mouse to navigate to text field and select text.
+  void SelectTextWithMouse(const CFX_PointF& start, const CFX_PointF& end) {
+    ASSERT(start.y == end.y);
+
+    // Navigate to starting position and click mouse.
+    FORM_OnMouseMove(form_handle(), page_, 0, start.x, start.y);
+    FORM_OnLButtonDown(form_handle(), page_, 0, start.x, start.y);
+
+    // Hold down mouse until reach end of desired selection.
+    FORM_OnMouseMove(form_handle(), page_, 0, end.x, end.y);
+    FORM_OnLButtonUp(form_handle(), page_, 0, end.x, end.y);
+  }
+
+  void CheckSelection(WideStringView expected_string) {
+    unsigned long actual_len =
+        FORM_GetSelectedText(form_handle(), page_, nullptr, 0);
+    ASSERT_NE(actual_len, 0U);
+    ASSERT_LT(actual_len, 1000U);
+
+    std::vector<unsigned short> buf(actual_len);
+    ASSERT_EQ(actual_len, FORM_GetSelectedText(form_handle(), page_, buf.data(),
+                                               actual_len));
+
+    int num_chars = (actual_len / sizeof(unsigned short)) - 1;
+    EXPECT_EQ(expected_string, WideString::FromUTF16LE(buf.data(), num_chars));
+  }
+
+  void CheckFocusedFieldText(WideStringView expected_string) {
+    unsigned long actual_len =
+        FORM_GetFocusedText(form_handle(), page_, nullptr, 0);
+    ASSERT_NE(actual_len, 0U);
+    ASSERT_LT(actual_len, 1000U);
+
+    std::vector<unsigned short> buf(actual_len);
+    ASSERT_EQ(actual_len, FORM_GetFocusedText(form_handle(), page_, buf.data(),
+                                              actual_len));
+
+    int num_chars = (actual_len / sizeof(unsigned short)) - 1;
+    EXPECT_EQ(expected_string, WideString::FromUTF16LE(buf.data(), num_chars));
+  }
+
+  void CheckCanUndo(bool expected_result) {
+    EXPECT_EQ(expected_result, !!FORM_CanUndo(form_handle(), page_));
+  }
+
+  void CheckCanRedo(bool expected_result) {
+    EXPECT_EQ(expected_result, !!FORM_CanRedo(form_handle(), page_));
+  }
+
+  void PerformUndo() { EXPECT_TRUE(FORM_Undo(form_handle(), page_)); }
+
+  void PerformRedo() { EXPECT_TRUE(FORM_Redo(form_handle(), page_)); }
+
+  void SetIndexSelectedShouldSucceed(int index, bool selected) {
+    EXPECT_TRUE(FORM_SetIndexSelected(form_handle(), page_, index, selected));
+  }
+
+  void SetIndexSelectedShouldFail(int index, bool selected) {
+    EXPECT_FALSE(FORM_SetIndexSelected(form_handle(), page_, index, selected));
+  }
+
+  void CheckIsIndexSelected(int index, bool expected) {
+    EXPECT_EQ(expected, FORM_IsIndexSelected(form_handle(), page_, index));
+  }
+
+ private:
+  FPDF_PAGE page_ = nullptr;
+};
+
+class FPDFFormFillTextFormEmbedderTest
+    : public FPDFFormFillInteractiveEmbedderTest {
+ protected:
+  FPDFFormFillTextFormEmbedderTest() = default;
+  ~FPDFFormFillTextFormEmbedderTest() override = default;
+
+  const char* GetDocumentName() const override {
+    // PDF with several form text fields:
+    // - "Text Box" - Regular text box with no special attributes.
+    // - "ReadOnly" - Ff: 1.
+    // - "CharLimit" - MaxLen: 10, V: Elephant.
+    return "text_form_multiple.pdf";
+  }
+
+  int GetFormType() const override { return FPDF_FORMFIELD_TEXTFIELD; }
+
+  void FormSanityChecks() override {
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(CharLimitFormBegin()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(CharLimitFormEnd()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(RegularFormBegin()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(RegularFormEnd()));
+  }
+
+  void SelectAllCharLimitFormTextWithMouse() {
+    SelectTextWithMouse(CharLimitFormEnd(), CharLimitFormBegin());
+  }
+
+  void SelectAllRegularFormTextWithMouse() {
+    SelectTextWithMouse(RegularFormEnd(), RegularFormBegin());
+  }
+
+  const CFX_PointF& CharLimitFormBegin() const {
+    static const CFX_PointF point = CharLimitFormAtX(kFormBeginX);
+    return point;
+  }
+
+  const CFX_PointF& CharLimitFormEnd() const {
+    static const CFX_PointF point = CharLimitFormAtX(kFormEndX);
+    return point;
+  }
+
+  const CFX_PointF& RegularFormBegin() const {
+    static const CFX_PointF point = RegularFormAtX(kFormBeginX);
+    return point;
+  }
+
+  const CFX_PointF& RegularFormEnd() const {
+    static const CFX_PointF point = RegularFormAtX(kFormEndX);
+    return point;
+  }
+
+  static CFX_PointF CharLimitFormAtX(float x) {
+    ASSERT(x >= kFormBeginX);
+    ASSERT(x <= kFormEndX);
+    return CFX_PointF(x, kCharLimitFormY);
+  }
+
+  static CFX_PointF RegularFormAtX(float x) {
+    ASSERT(x >= kFormBeginX);
+    ASSERT(x <= kFormEndX);
+    return CFX_PointF(x, kRegularFormY);
+  }
+
+ private:
+  static constexpr float kFormBeginX = 102.0;
+  static constexpr float kFormEndX = 195.0;
+  static constexpr float kCharLimitFormY = 60.0;
+  static constexpr float kRegularFormY = 115.0;
+};
+
+class FPDFFormFillComboBoxFormEmbedderTest
+    : public FPDFFormFillInteractiveEmbedderTest {
+ protected:
+  FPDFFormFillComboBoxFormEmbedderTest() = default;
+  ~FPDFFormFillComboBoxFormEmbedderTest() override = default;
+
+  const char* GetDocumentName() const override {
+    // PDF with form comboboxes:
+    // - "Combo_Editable" - Ff: 393216, 3 options with pair values.
+    // - "Combo1" - Ff: 131072, 3 options with single values.
+    // - "Combo_ReadOnly" - Ff: 131073, 3 options with single values.
+    return "combobox_form.pdf";
+  }
+
+  int GetFormType() const override { return FPDF_FORMFIELD_COMBOBOX; }
+
+  void FormSanityChecks() override {
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(EditableFormBegin()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(EditableFormEnd()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(EditableFormDropDown()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(NonEditableFormBegin()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(NonEditableFormEnd()));
+    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(NonEditableFormDropDown()));
+  }
+
+  void SelectEditableFormOption(int item_index) {
+    ASSERT(item_index >= 0);
+    ASSERT(item_index < 3);
+    SelectOption(item_index, EditableFormDropDown());
+  }
+
+  void SelectNonEditableFormOption(int item_index) {
+    ASSERT(item_index >= 0);
+    ASSERT(item_index < 26);
+    SelectOption(item_index, NonEditableFormDropDown());
+  }
+
+  void SelectAllEditableFormTextWithMouse() {
+    SelectTextWithMouse(EditableFormEnd(), EditableFormBegin());
+  }
+
+  void FocusOnEditableForm() { FocusOnPoint(EditableFormDropDown()); }
+
+  void FocusOnNonEditableForm() { FocusOnPoint(NonEditableFormDropDown()); }
+
+  void FocusOnPoint(const CFX_PointF& point) {
+    EXPECT_EQ(true, FORM_OnFocus(form_handle(), page(), 0, point.x, point.y));
+  }
+
+  const CFX_PointF& EditableFormBegin() const {
+    static const CFX_PointF point = EditableFormAtX(kFormBeginX);
+    return point;
+  }
+
+  const CFX_PointF& EditableFormEnd() const {
+    static const CFX_PointF point = EditableFormAtX(kFormEndX);
+    return point;
+  }
+
+  const CFX_PointF& EditableFormDropDown() const {
+    static const CFX_PointF point(kFormDropDownX, kEditableFormY);
+    return point;
+  }
+
+  const CFX_PointF& NonEditableFormBegin() const {
+    static const CFX_PointF point = NonEditableFormAtX(kFormBeginX);
+    return point;
+  }
+
+  const CFX_PointF& NonEditableFormEnd() const {
+    static const CFX_PointF point = NonEditableFormAtX(kFormEndX);
+    return point;
+  }
+
+  const CFX_PointF& NonEditableFormDropDown() const {
+    static const CFX_PointF point(kFormDropDownX, kNonEditableFormY);
+    return point;
+  }
+
+  static CFX_PointF EditableFormAtX(float x) {
+    ASSERT(x >= kFormBeginX);
+    ASSERT(x <= kFormEndX);
+    return CFX_PointF(x, kEditableFormY);
+  }
+
+  static CFX_PointF NonEditableFormAtX(float x) {
+    ASSERT(x >= kFormBeginX);
+    ASSERT(x <= kFormEndX);
+    return CFX_PointF(x, kNonEditableFormY);
+  }
+
+ private:
+  // Selects one of the pre-selected values from a combobox with three options.
+  // Options are specified by |item_index|, which is 0-based.
+  void SelectOption(int item_index, const CFX_PointF& point) {
+    // Navigate to button for drop down and click mouse to reveal options.
+    ClickOnFormFieldAtPoint(point);
+
+    // Calculate to Y-coordinate of dropdown option to be selected.
+    constexpr double kChoiceHeight = 15;
+    CFX_PointF option_point = point;
+    option_point.y -= kChoiceHeight * (item_index + 1);
+
+    // Move left to avoid scrollbar.
+    option_point.x -= 20;
+
+    // Navigate to option and click mouse to select it.
+    ClickOnFormFieldAtPoint(option_point);
+  }
+
+  static constexpr float kFormBeginX = 102.0;
+  static constexpr float kFormEndX = 183.0;
+  static constexpr float kFormDropDownX = 192.0;
+  static constexpr float kEditableFormY = 360.0;
+  static constexpr float kNonEditableFormY = 410.0;
+};
+
+class FPDFFormFillListBoxFormEmbedderTest
+    : public FPDFFormFillInteractiveEmbedderTest {
+ protected:
+  FPDFFormFillListBoxFormEmbedderTest() = default;
+  ~FPDFFormFillListBoxFormEmbedderTest() override = default;
+
+  const char* GetDocumentName() const override {
+    // PDF with form listboxes:
+    // - "Listbox_SingleSelect" - Ff: 0, 3 options with pair values.
+    // - "Listbox_MultiSelect" - Ff: 2097152, 26 options with single values.
+    // - "Listbox_ReadOnly" - Ff: 1, 3 options with single values.
+    // - "Listbox_MultiSelectMultipleSelected" - Ff: 2097152, 5 options with
+    // single values.
+    // - "Listbox_SingleSelectLastSelected" - Ff: 0, 10 options with single
+    // values.
+    return "listbox_form.pdf";
+  }
+
+  int GetFormType() const override { return FPDF_FORMFIELD_LISTBOX; }
+
+  void FormSanityChecks() override {
+    EXPECT_EQ(GetFormType(),
+              GetFormTypeAtPoint(SingleSelectFirstVisibleOption()));
+    EXPECT_EQ(GetFormType(),
+              GetFormTypeAtPoint(SingleSelectSecondVisibleOption()));
+    EXPECT_EQ(GetFormType(),
+              GetFormTypeAtPoint(MultiSelectFirstVisibleOption()));
+    EXPECT_EQ(GetFormType(),
+              GetFormTypeAtPoint(MultiSelectSecondVisibleOption()));
+    EXPECT_EQ(
+        GetFormType(),
+        GetFormTypeAtPoint(MultiSelectMultipleSelectedFirstVisibleOption()));
+    EXPECT_EQ(
+        GetFormType(),
+        GetFormTypeAtPoint(MultiSelectMultipleSelectedSecondVisibleOption()));
+    EXPECT_EQ(GetFormType(),
+              GetFormTypeAtPoint(SingleSelectLastSelectedFirstVisibleOption()));
+    EXPECT_EQ(
+        GetFormType(),
+        GetFormTypeAtPoint(SingleSelectLastSelectedSecondVisibleOption()));
+  }
+
+  void ClickOnSingleSelectFormOption(int item_index) {
+    // Only the first two indices are visible so can only click on those
+    // without scrolling.
+    ASSERT(item_index >= 0);
+    ASSERT(item_index < 2);
+    if (item_index == 0) {
+      ClickOnFormFieldAtPoint(SingleSelectFirstVisibleOption());
+    } else {
+      ClickOnFormFieldAtPoint(SingleSelectSecondVisibleOption());
+    }
+  }
+
+  void ClickOnMultiSelectFormOption(int item_index) {
+    // Only the first two indices are visible so can only click on those
+    // without scrolling.
+    ASSERT(item_index >= 0);
+    ASSERT(item_index < 2);
+    if (item_index == 0) {
+      ClickOnFormFieldAtPoint(MultiSelectFirstVisibleOption());
+    } else {
+      ClickOnFormFieldAtPoint(MultiSelectSecondVisibleOption());
+    }
+  }
+
+  void ClickOnMultiSelectMultipleSelectedFormOption(int item_index) {
+    // Only two indices are visible so can only click on those
+    // without scrolling.
+    ASSERT(item_index >= 0);
+    ASSERT(item_index < 2);
+    if (item_index == 0) {
+      ClickOnFormFieldAtPoint(MultiSelectMultipleSelectedFirstVisibleOption());
+    } else {
+      ClickOnFormFieldAtPoint(MultiSelectMultipleSelectedSecondVisibleOption());
+    }
+  }
+
+  void ClickOnSingleSelectLastSelectedFormOption(int item_index) {
+    // Only two indices are visible so can only click on those
+    // without scrolling.
+    ASSERT(item_index >= 0);
+    ASSERT(item_index < 2);
+    if (item_index == 0) {
+      ClickOnFormFieldAtPoint(SingleSelectLastSelectedFirstVisibleOption());
+    } else {
+      ClickOnFormFieldAtPoint(SingleSelectLastSelectedSecondVisibleOption());
+    }
+  }
+
+  void FocusOnSingleSelectForm() {
+    FocusOnPoint(SingleSelectFirstVisibleOption());
+  }
+
+  void FocusOnMultiSelectForm() {
+    FocusOnPoint(MultiSelectFirstVisibleOption());
+  }
+
+  void FocusOnMultiSelectMultipleSelectedForm() {
+    FocusOnPoint(MultiSelectMultipleSelectedFirstVisibleOption());
+  }
+
+  void FocusOnSingleSelectLastSelectedForm() {
+    FocusOnPoint(SingleSelectLastSelectedFirstVisibleOption());
+  }
+
+  void FocusOnPoint(const CFX_PointF& point) {
+    EXPECT_EQ(true, FORM_OnFocus(form_handle(), page(), 0, point.x, point.y));
+  }
+
+  const CFX_PointF& SingleSelectFirstVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX, kSingleFormYFirstVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& SingleSelectSecondVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX, kSingleFormYSecondVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectFirstVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX, kMultiFormYFirstVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectSecondVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX, kMultiFormYSecondVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectMultipleSelectedFirstVisibleOption() const {
+    static const CFX_PointF point(
+        kFormBeginX, kMultiFormMultipleSelectedYFirstVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& MultiSelectMultipleSelectedSecondVisibleOption() const {
+    static const CFX_PointF point(
+        kFormBeginX, kMultiFormMultipleSelectedYSecondVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& SingleSelectLastSelectedFirstVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX,
+                                  kSingleFormLastSelectedYFirstVisibleOption);
+    return point;
+  }
+
+  const CFX_PointF& SingleSelectLastSelectedSecondVisibleOption() const {
+    static const CFX_PointF point(kFormBeginX,
+                                  kSingleFormLastSelectedYSecondVisibleOption);
+    return point;
+  }
+
+ private:
+  static constexpr float kFormBeginX = 102.0;
+  static constexpr float kSingleFormYFirstVisibleOption = 371.0;
+  static constexpr float kSingleFormYSecondVisibleOption = 358.0;
+  static constexpr float kMultiFormYFirstVisibleOption = 423.0;
+  static constexpr float kMultiFormYSecondVisibleOption = 408.0;
+  static constexpr float kMultiFormMultipleSelectedYFirstVisibleOption = 223.0;
+  static constexpr float kMultiFormMultipleSelectedYSecondVisibleOption = 208.0;
+  static constexpr float kSingleFormLastSelectedYFirstVisibleOption = 123.0;
+  static constexpr float kSingleFormLastSelectedYSecondVisibleOption = 108.0;
+};
+
+TEST_F(FPDFFormFillEmbedderTest, FirstTest) {
+  EmbedderTestMockDelegate mock;
+  EXPECT_CALL(mock, Alert(_, _, _, _)).Times(0);
+  EXPECT_CALL(mock, UnsupportedHandler(_)).Times(0);
+  EXPECT_CALL(mock, SetTimer(_, _)).Times(0);
+  EXPECT_CALL(mock, KillTimer(_)).Times(0);
+  SetDelegate(&mock);
+
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_487928) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_487928.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+  delegate.AdvanceTime(5000);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_507316) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_507316.pdf"));
+  FPDF_PAGE page = LoadPage(2);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+  delegate.AdvanceTime(4000);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_514690) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  // Test that FORM_OnMouseMove() etc. permit null HANDLES and PAGES.
+  FORM_OnMouseMove(nullptr, page, 0, 10.0, 10.0);
+  FORM_OnMouseMove(form_handle(), nullptr, 0, 10.0, 10.0);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_900552) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_900552.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  DoOpenActions();
+  delegate.AdvanceTime(4000);
+
+  // Simulate a repaint.
+  FPDF_BITMAP bitmap = FPDFBitmap_Create(512, 512, 0);
+  ASSERT_TRUE(bitmap);
+  FPDF_RenderPageBitmap_Start(bitmap, page, 0, 0, 512, 512, 0, 0, nullptr);
+  FPDFBitmap_Destroy(bitmap);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_901654) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_901654.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  DoOpenActions();
+  delegate.AdvanceTime(4000);
+
+  // Simulate a repaint.
+  {
+    ScopedFPDFBitmap bitmap(FPDFBitmap_Create(512, 512, 0));
+    FPDF_RenderPageBitmap_Start(bitmap.get(), page, 0, 0, 512, 512, 0, 0,
+                                nullptr);
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_901654_2) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_901654_2.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  DoOpenActions();
+  delegate.AdvanceTime(4000);
+
+  // Simulate a repaint.
+  {
+    ScopedFPDFBitmap bitmap(FPDFBitmap_Create(512, 512, 0));
+    FPDF_RenderPageBitmap_Start(bitmap.get(), page, 0, 0, 512, 512, 0, 0,
+                                nullptr);
+  }
+  UnloadPage(page);
+}
+
+class DoURIActionBlockedDelegate final : public EmbedderTest::Delegate {
+ public:
+  void DoURIAction(FPDF_BYTESTRING uri) override {
+    FAIL() << "Navigated to " << uri;
+  }
+};
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_851821) {
+  DoURIActionBlockedDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("redirect.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+
+  UnloadPage(page);
+}
+
+#ifdef PDF_ENABLE_V8
+TEST_F(FPDFFormFillEmbedderTest, DisableJavaScript) {
+  // Test that timers and intervals can't fire without JS.
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocumentWithoutJavaScript("bug_551248.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(0U, alerts.size());
+
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, DocumentAActions) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("document_aactions.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(0U, alerts.size());
+
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WS);
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_DS);
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WP);
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_DP);
+  UnloadPage(page);
+
+  ASSERT_EQ(4U, alerts.size());
+  EXPECT_STREQ(L"Will Save", alerts[0].message.c_str());
+  EXPECT_STREQ(L"Did Save", alerts[1].message.c_str());
+  EXPECT_STREQ(L"Will Print", alerts[2].message.c_str());
+  EXPECT_STREQ(L"Did Print", alerts[3].message.c_str());
+}
+
+TEST_F(FPDFFormFillEmbedderTest, DocumentAActionsDisableJavaScript) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocumentWithoutJavaScript("document_aactions.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(0U, alerts.size());
+
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WS);
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_DS);
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WP);
+  FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_DP);
+  UnloadPage(page);
+
+  ASSERT_EQ(0U, alerts.size());
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_551248) {
+  // Test that timers fire once and intervals fire repeatedly.
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_551248.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(0U, alerts.size());
+
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(1U, alerts.size());  // interval fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(2U, alerts.size());  // timer fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(3U, alerts.size());  // interval fired again.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(3U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(4U, alerts.size());  // interval fired again.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(4U, alerts.size());  // nothing fired.
+  UnloadPage(page);
+
+  ASSERT_EQ(4U, alerts.size());  // nothing else fired.
+
+  EXPECT_STREQ(L"interval fired", alerts[0].message.c_str());
+  EXPECT_STREQ(L"Alert", alerts[0].title.c_str());
+  EXPECT_EQ(0, alerts[0].type);
+  EXPECT_EQ(0, alerts[0].icon);
+
+  EXPECT_STREQ(L"timer fired", alerts[1].message.c_str());
+  EXPECT_STREQ(L"Alert", alerts[1].title.c_str());
+  EXPECT_EQ(0, alerts[1].type);
+  EXPECT_EQ(0, alerts[1].icon);
+
+  EXPECT_STREQ(L"interval fired", alerts[2].message.c_str());
+  EXPECT_STREQ(L"Alert", alerts[2].title.c_str());
+  EXPECT_EQ(0, alerts[2].type);
+  EXPECT_EQ(0, alerts[2].icon);
+
+  EXPECT_STREQ(L"interval fired", alerts[3].message.c_str());
+  EXPECT_STREQ(L"Alert", alerts[3].title.c_str());
+  EXPECT_EQ(0, alerts[3].type);
+  EXPECT_EQ(0, alerts[3].icon);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_620428) {
+  // Test that timers and intervals are cancelable.
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_620428.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+  delegate.AdvanceTime(5000);
+  UnloadPage(page);
+
+  const auto& alerts = delegate.GetAlerts();
+  ASSERT_EQ(1U, alerts.size());
+  EXPECT_STREQ(L"done", alerts[0].message.c_str());
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_634394) {
+  // Cancel timer inside timer callback.
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_634394.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+
+  // Timers fire at most once per AdvanceTime(), allow intervals
+  // to fire several times if possible.
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  UnloadPage(page);
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(2U, alerts.size());
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_634716) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_634716.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+
+  // Timers fire at most once per AdvanceTime(), allow intervals
+  // to fire several times if possible.
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  delegate.AdvanceTime(1000);
+  UnloadPage(page);
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(2U, alerts.size());
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_679649) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_679649.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  delegate.SetFailNextTimer();
+  DoOpenActions();
+  delegate.AdvanceTime(2000);
+  UnloadPage(page);
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(0u, alerts.size());
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_707673) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocument("bug_707673.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  DoOpenActions();
+  FORM_OnLButtonDown(form_handle(), page, 0, 140, 590);
+  FORM_OnLButtonUp(form_handle(), page, 0, 140, 590);
+  delegate.AdvanceTime(1000);
+  UnloadPage(page);
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(0u, alerts.size());
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BUG_765384) {
+  EXPECT_TRUE(OpenDocument("bug_765384.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  DoOpenActions();
+  FORM_OnLButtonDown(form_handle(), page, 0, 140, 590);
+  FORM_OnLButtonUp(form_handle(), page, 0, 140, 590);
+  UnloadPage(page);
+}
+#endif  // PDF_ENABLE_V8
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_FormText DISABLED_FormText
+#else
+#define MAYBE_FormText FormText
+#endif
+TEST_F(FPDFFormFillEmbedderTest, MAYBE_FormText) {
+#if defined(OS_MACOSX)
+  const char md5_1[] = "5f11dbe575fe197a37c3fb422559f8ff";
+  const char md5_2[] = "35b1a4b679eafc749a0b6fda750c0e8d";
+  const char md5_3[] = "65c64a7c355388f719a752aa1e23f6fe";
+#elif defined(OS_WIN)
+  const char md5_1[] = "d3204faa62b607f0bd3893c9c22cabcb";
+  const char md5_2[] = "29d1c3fd226ca6a69597f75937690320";
+  const char md5_3[] = "5e678a55912cb568fd677bf34abb8727";
+#else
+  const char md5_1[] = "b890950d4b9bc163b1a96797f3004b53";
+  const char md5_2[] = "11487d5597599a26e8912b9c1d9422cb";
+  const char md5_3[] = "bffe0ecea9a533f217047ee41d6be466";
+#endif
+  {
+    EXPECT_TRUE(OpenDocument("text_form.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+    ScopedFPDFBitmap bitmap1 = RenderLoadedPage(page);
+    CompareBitmap(bitmap1.get(), 300, 300, md5_1);
+
+    // Click on the textfield
+    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
+              FPDFPage_HasFormFieldAtPoint(form_handle(), page, 120.0, 120.0));
+    EXPECT_EQ(
+        0, FPDFPage_FormFieldZOrderAtPoint(form_handle(), page, 120.0, 120.0));
+    FORM_OnMouseMove(form_handle(), page, 0, 120.0, 120.0);
+    FORM_OnLButtonDown(form_handle(), page, 0, 120.0, 120.0);
+    FORM_OnLButtonUp(form_handle(), page, 0, 120.0, 120.0);
+
+    // Write "ABC"
+    FORM_OnChar(form_handle(), page, 65, 0);
+    FORM_OnChar(form_handle(), page, 66, 0);
+    FORM_OnChar(form_handle(), page, 67, 0);
+    ScopedFPDFBitmap bitmap2 = RenderLoadedPage(page);
+    CompareBitmap(bitmap2.get(), 300, 300, md5_2);
+
+    // Focus remains despite right clicking out of the textfield
+    FORM_OnMouseMove(form_handle(), page, 0, 15.0, 15.0);
+    FORM_OnRButtonDown(form_handle(), page, 0, 15.0, 15.0);
+    FORM_OnRButtonUp(form_handle(), page, 0, 15.0, 15.0);
+    ScopedFPDFBitmap bitmap3 = RenderLoadedPage(page);
+    CompareBitmap(bitmap3.get(), 300, 300, md5_2);
+
+    // Take out focus by clicking out of the textfield
+    FORM_OnMouseMove(form_handle(), page, 0, 15.0, 15.0);
+    FORM_OnLButtonDown(form_handle(), page, 0, 15.0, 15.0);
+    FORM_OnLButtonUp(form_handle(), page, 0, 15.0, 15.0);
+    ScopedFPDFBitmap bitmap4 = RenderLoadedPage(page);
+    CompareBitmap(bitmap4.get(), 300, 300, md5_3);
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+    // Close page
+    UnloadPage(page);
+  }
+  // Check saved document
+  VerifySavedDocument(300, 300, md5_3);
+}
+
+// Tests using FPDF_REVERSE_BYTE_ORDER with FPDF_FFLDraw(). The two rendered
+// bitmaps should be different.
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_BUG_1281 DISABLED_BUG_1281
+#else
+#define MAYBE_BUG_1281 BUG_1281
+#endif
+TEST_F(FPDFFormFillEmbedderTest, MAYBE_BUG_1281) {
+  const char kMd5Normal[] = "6c674642154408e877d88c6c082d67e9";
+  const char kMd5ReverseByteOrder[] = "24fff03d1e663b7ece5f6e69ad837124";
+
+  ASSERT_TRUE(OpenDocument("bug_890322.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap_normal = RenderLoadedPage(page);
+  CompareBitmap(bitmap_normal.get(), 200, 200, kMd5Normal);
+
+  ScopedFPDFBitmap bitmap_reverse_byte_order =
+      RenderLoadedPageWithFlags(page, FPDF_REVERSE_BYTE_ORDER);
+  CompareBitmap(bitmap_reverse_byte_order.get(), 200, 200,
+                kMd5ReverseByteOrder);
+
+  UnloadPage(page);
+}
+
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_RemoveFormFieldHighlight DISABLED_RemoveFormFieldHighlight
+#else
+#define MAYBE_RemoveFormFieldHighlight RemoveFormFieldHighlight
+#endif
+TEST_F(FPDFFormFillEmbedderTest, MAYBE_RemoveFormFieldHighlight) {
+#if defined(OS_MACOSX)
+  const char kMd5Normal[] = "5f11dbe575fe197a37c3fb422559f8ff";
+  const char kMd5NoHighlight[] = "575ec237c790950f40bfcaefb2e3923c";
+#elif defined(OS_WIN)
+  const char kMd5Normal[] = "d3204faa62b607f0bd3893c9c22cabcb";
+  const char kMd5NoHighlight[] = "3ec0938828e0a37ef23f687ee95a80e1";
+#else
+  const char kMd5Normal[] = "b890950d4b9bc163b1a96797f3004b53";
+  const char kMd5NoHighlight[] = "006010c318457810a518aa5e0b33c498";
+#endif
+
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ScopedFPDFBitmap bitmap1 = RenderLoadedPage(page);
+  CompareBitmap(bitmap1.get(), 300, 300, kMd5Normal);
+
+  // Removing the highlight changes the rendering.
+  FPDF_RemoveFormFieldHighlight(form_handle());
+  ScopedFPDFBitmap bitmap2 = RenderLoadedPage(page);
+  CompareBitmap(bitmap2.get(), 300, 300, kMd5NoHighlight);
+
+  // Restoring it gives the original rendering.
+  SetInitialFormFieldHighlight(form_handle());
+  ScopedFPDFBitmap bitmap3 = RenderLoadedPage(page);
+  CompareBitmap(bitmap3.get(), 300, 300, kMd5Normal);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, HasFormInfoNone) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(FORMTYPE_NONE, FPDF_GetFormType(document_));
+}
+
+TEST_F(FPDFFormFillEmbedderTest, HasFormInfoAcroForm) {
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  EXPECT_EQ(FORMTYPE_ACRO_FORM, FPDF_GetFormType(document_));
+}
+
+TEST_F(FPDFFormFillEmbedderTest, HasFormInfoXFAFull) {
+  EXPECT_TRUE(OpenDocument("simple_xfa.pdf"));
+  EXPECT_EQ(FORMTYPE_XFA_FULL, FPDF_GetFormType(document_));
+}
+
+TEST_F(FPDFFormFillEmbedderTest, HasFormInfoXFAForeground) {
+  EXPECT_TRUE(OpenDocument("bug_216.pdf"));
+  EXPECT_EQ(FORMTYPE_XFA_FOREGROUND, FPDF_GetFormType(document_));
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BadApiInputsText) {
+  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_FALSE(FORM_SetIndexSelected(nullptr, nullptr, 0, true));
+  EXPECT_FALSE(FORM_SetIndexSelected(nullptr, page, 0, true));
+  EXPECT_FALSE(FORM_SetIndexSelected(form_handle(), nullptr, 0, true));
+  EXPECT_FALSE(FORM_SetIndexSelected(form_handle(), page, -1, true));
+  EXPECT_FALSE(FORM_IsIndexSelected(nullptr, nullptr, 0));
+  EXPECT_FALSE(FORM_IsIndexSelected(nullptr, page, 0));
+  EXPECT_FALSE(FORM_IsIndexSelected(form_handle(), nullptr, 0));
+  EXPECT_FALSE(FORM_IsIndexSelected(form_handle(), page, -1));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BadApiInputsComboBox) {
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_FALSE(FORM_SetIndexSelected(form_handle(), page, -1, true));
+  EXPECT_FALSE(FORM_SetIndexSelected(form_handle(), page, 100, true));
+  EXPECT_FALSE(FORM_IsIndexSelected(form_handle(), page, -1));
+  EXPECT_FALSE(FORM_IsIndexSelected(form_handle(), page, 100));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, BadApiInputsListBox) {
+  ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_FALSE(FORM_SetIndexSelected(form_handle(), page, -1, true));
+  EXPECT_FALSE(FORM_SetIndexSelected(form_handle(), page, 100, true));
+  EXPECT_FALSE(FORM_IsIndexSelected(form_handle(), page, -1));
+  EXPECT_FALSE(FORM_IsIndexSelected(form_handle(), page, 100));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, GetSelectedTextEmptyAndBasicKeyboard) {
+  // Test empty selection.
+  CheckFocusedFieldText(L"");
+  CheckSelection(L"");
+
+  // Test basic selection.
+  TypeTextIntoTextField(3, RegularFormBegin());
+  CheckFocusedFieldText(L"ABC");
+  SelectTextWithKeyboard(3, FWL_VKEY_Left, RegularFormAtX(123.0));
+  CheckSelection(L"ABC");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, GetSelectedTextEmptyAndBasicMouse) {
+  // Test empty selection.
+  CheckFocusedFieldText(L"");
+  CheckSelection(L"");
+
+  // Test basic selection.
+  TypeTextIntoTextField(3, RegularFormBegin());
+  CheckFocusedFieldText(L"ABC");
+  SelectTextWithMouse(RegularFormAtX(125.0), RegularFormBegin());
+  CheckSelection(L"ABC");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, GetSelectedTextFragmentsKeyBoard) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDEFGHIJKL");
+
+  // Test selecting first character in forward direction.
+  SelectTextWithKeyboard(1, FWL_VKEY_Right, RegularFormBegin());
+  CheckSelection(L"A");
+
+  // Test selecting entire long string in backwards direction.
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"ABCDEFGHIJKL");
+
+  // Test selecting middle section in backwards direction.
+  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormAtX(170.0));
+  CheckSelection(L"DEFGHI");
+
+  // Test selecting middle selection in forward direction.
+  SelectTextWithKeyboard(6, FWL_VKEY_Right, RegularFormAtX(125.0));
+  CheckSelection(L"DEFGHI");
+
+  // Test selecting last character in backwards direction.
+  SelectTextWithKeyboard(1, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"L");
+  CheckFocusedFieldText(L"ABCDEFGHIJKL");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, GetSelectedTextFragmentsMouse) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Test selecting first character in forward direction.
+  SelectTextWithMouse(RegularFormBegin(), RegularFormAtX(106.0));
+  CheckSelection(L"A");
+
+  // Test selecting entire long string in backwards direction.
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"ABCDEFGHIJKL");
+
+  // Test selecting middle section in backwards direction.
+  SelectTextWithMouse(RegularFormAtX(170.0), RegularFormAtX(125.0));
+  CheckSelection(L"DEFGHI");
+
+  // Test selecting middle selection in forward direction.
+  SelectTextWithMouse(RegularFormAtX(125.0), RegularFormAtX(170.0));
+  CheckSelection(L"DEFGHI");
+
+  // Test selecting last character in backwards direction.
+  SelectTextWithMouse(RegularFormEnd(), RegularFormAtX(186.0));
+  CheckSelection(L"L");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       GetSelectedTextEmptyAndBasicNormalComboBox) {
+  // Test empty selection.
+  CheckSelection(L"");
+  CheckFocusedFieldText(L"");
+
+  // Non-editable comboboxes don't allow selection with keyboard.
+  SelectTextWithMouse(NonEditableFormBegin(), NonEditableFormAtX(142.0));
+  CheckFocusedFieldText(L"Banana");
+  CheckSelection(L"Banana");
+
+  // Select other another provided option.
+  SelectNonEditableFormOption(0);
+  CheckFocusedFieldText(L"Apple");
+  CheckSelection(L"Apple");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       GetSelectedTextEmptyAndBasicEditableComboBoxKeyboard) {
+  // Test empty selection.
+  CheckSelection(L"");
+  CheckFocusedFieldText(L"");
+
+  // Test basic selection of text within user editable combobox using keyboard.
+  TypeTextIntoTextField(3, EditableFormBegin());
+  CheckFocusedFieldText(L"ABC");
+  SelectTextWithKeyboard(3, FWL_VKEY_Left, EditableFormAtX(128.0));
+  CheckSelection(L"ABC");
+
+  // Select a provided option.
+  SelectEditableFormOption(1);
+  CheckSelection(L"Bar");
+  CheckFocusedFieldText(L"Bar");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       GetSelectedTextEmptyAndBasicEditableComboBoxMouse) {
+  // Test empty selection.
+  CheckSelection(L"");
+
+  // Test basic selection of text within user editable combobox using mouse.
+  TypeTextIntoTextField(3, EditableFormBegin());
+  SelectTextWithMouse(EditableFormAtX(128.0), EditableFormBegin());
+  CheckSelection(L"ABC");
+
+  // Select a provided option.
+  SelectEditableFormOption(2);
+  CheckFocusedFieldText(L"Qux");
+  CheckSelection(L"Qux");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       GetSelectedTextFragmentsNormalComboBox) {
+  CheckFocusedFieldText(L"");
+
+  // Test selecting first character in forward direction.
+  SelectTextWithMouse(NonEditableFormBegin(), NonEditableFormAtX(107.0));
+  CheckFocusedFieldText(L"Banana");
+  CheckSelection(L"B");
+
+  // Test selecting entire string in backwards direction.
+  SelectTextWithMouse(NonEditableFormAtX(142.0), NonEditableFormBegin());
+  CheckSelection(L"Banana");
+
+  // Test selecting middle section in backwards direction.
+  SelectTextWithMouse(NonEditableFormAtX(135.0), NonEditableFormAtX(117.0));
+  CheckSelection(L"nan");
+
+  // Test selecting middle section in forward direction.
+  SelectTextWithMouse(NonEditableFormAtX(117.0), NonEditableFormAtX(135.0));
+  CheckSelection(L"nan");
+
+  // Test selecting last character in backwards direction.
+  SelectTextWithMouse(NonEditableFormAtX(142.0), NonEditableFormAtX(138.0));
+  CheckSelection(L"a");
+  CheckFocusedFieldText(L"Banana");
+
+  // Select another option and then reset selection as first three chars.
+  SelectNonEditableFormOption(2);
+  CheckFocusedFieldText(L"Cherry");
+  CheckSelection(L"Cherry");
+  SelectTextWithMouse(NonEditableFormBegin(), NonEditableFormAtX(122.0));
+  CheckSelection(L"Che");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       GetSelectedTextFragmentsEditableComboBoxKeyboard) {
+  CheckFocusedFieldText(L"");
+  TypeTextIntoTextField(10, EditableFormBegin());
+  CheckFocusedFieldText(L"ABCDEFGHIJ");
+
+  // Test selecting first character in forward direction.
+  SelectTextWithKeyboard(1, FWL_VKEY_Right, EditableFormBegin());
+  CheckSelection(L"A");
+
+  // Test selecting entire long string in backwards direction.
+  SelectTextWithKeyboard(10, FWL_VKEY_Left, EditableFormEnd());
+  CheckSelection(L"ABCDEFGHIJ");
+
+  // Test selecting middle section in backwards direction.
+  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormAtX(168.0));
+  CheckSelection(L"DEFGH");
+
+  // Test selecting middle selection in forward direction.
+  SelectTextWithKeyboard(5, FWL_VKEY_Right, EditableFormAtX(127.0));
+  CheckSelection(L"DEFGH");
+
+  // Test selecting last character in backwards direction.
+  SelectTextWithKeyboard(1, FWL_VKEY_Left, EditableFormEnd());
+  CheckSelection(L"J");
+
+  // Select a provided option and then reset selection as first two chars.
+  SelectEditableFormOption(0);
+  CheckSelection(L"Foo");
+  SelectTextWithKeyboard(2, FWL_VKEY_Right, EditableFormBegin());
+  CheckSelection(L"Fo");
+  CheckFocusedFieldText(L"Foo");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       GetSelectedTextFragmentsEditableComboBoxMouse) {
+  TypeTextIntoTextField(10, EditableFormBegin());
+
+  // Test selecting first character in forward direction.
+  SelectTextWithMouse(EditableFormBegin(), EditableFormAtX(107.0));
+  CheckSelection(L"A");
+
+  // Test selecting entire long string in backwards direction.
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCDEFGHIJ");
+
+  // Test selecting middle section in backwards direction.
+  SelectTextWithMouse(EditableFormAtX(168.0), EditableFormAtX(127.0));
+  CheckSelection(L"DEFGH");
+
+  // Test selecting middle selection in forward direction.
+  SelectTextWithMouse(EditableFormAtX(127.0), EditableFormAtX(168.0));
+  CheckSelection(L"DEFGH");
+
+  // Test selecting last character in backwards direction.
+  SelectTextWithMouse(EditableFormEnd(), EditableFormAtX(174.0));
+  CheckSelection(L"J");
+  CheckFocusedFieldText(L"ABCDEFGHIJ");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       SetSelectionProgrammaticallyNonEditableField) {
+  // Focus on non-editable form field and check that the value is as expected.
+  // This is the value that is present in the field upon opening, we have not
+  // changed it by setting focus.
+  FocusOnNonEditableForm();
+  CheckFocusedFieldText(L"Banana");
+
+  // Make selections to change the value of the focused annotation
+  // programmatically.
+  SetIndexSelectedShouldSucceed(0, true);
+  CheckFocusedFieldText(L"Apple");
+
+  // Selecting an index that is already selected is success.
+  SetIndexSelectedShouldSucceed(0, true);
+  CheckFocusedFieldText(L"Apple");
+
+  SetIndexSelectedShouldSucceed(9, true);
+  CheckFocusedFieldText(L"Jackfruit");
+
+  // Cannot deselect a combobox field - value unchanged.
+  SetIndexSelectedShouldFail(9, false);
+  CheckFocusedFieldText(L"Jackfruit");
+
+  // Cannot select indices that are out of range - value unchanged.
+  SetIndexSelectedShouldFail(100, true);
+  SetIndexSelectedShouldFail(-100, true);
+  CheckFocusedFieldText(L"Jackfruit");
+
+  // Check that above actions are interchangeable with click actions, should be
+  // able to use a combination of both.
+  SelectNonEditableFormOption(1);
+  CheckFocusedFieldText(L"Banana");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       SetSelectionProgrammaticallyEditableField) {
+  // Focus on editable form field and check that the value is as expected.
+  // This is the value that is present in the field upon opening, we have not
+  // changed it by setting focus.
+  FocusOnEditableForm();
+  CheckFocusedFieldText(L"");
+
+  // Make selections to change value of the focused annotation
+  // programmatically.
+  SetIndexSelectedShouldSucceed(0, true);
+  CheckFocusedFieldText(L"Foo");
+
+  SetIndexSelectedShouldSucceed(1, true);
+  CheckFocusedFieldText(L"Bar");
+
+  // Selecting an index that is already selected is success.
+  SetIndexSelectedShouldSucceed(1, true);
+  CheckFocusedFieldText(L"Bar");
+
+  // Cannot deselect a combobox field - value unchanged.
+  SetIndexSelectedShouldFail(0, false);
+  CheckFocusedFieldText(L"Bar");
+
+  // Cannot select indices that are out of range - value unchanged.
+  SetIndexSelectedShouldFail(100, true);
+  SetIndexSelectedShouldFail(-100, true);
+  CheckFocusedFieldText(L"Bar");
+
+  // Check that above actions are interchangeable with click actions, should be
+  // able to use a combination of both.
+  SelectEditableFormOption(0);
+  CheckFocusedFieldText(L"Foo");
+
+  // Check that above actions are interchangeable with typing actions, should
+  // be able to use a combination of both. Typing text into a text field after
+  // selecting indices programmatically should be equivalent to doing so after
+  // a user selects an index via click on the dropdown.
+  SetIndexSelectedShouldSucceed(1, true);
+  CheckFocusedFieldText(L"Bar");
+  TypeTextIntoTextField(5, EditableFormBegin());
+  CheckFocusedFieldText(L"ABCDEBar");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       CheckIfIndexSelectedNonEditableField) {
+  // Non-editable field is set to 'Banana' (index 1) upon opening.
+  ClickOnFormFieldAtPoint(NonEditableFormBegin());
+  for (int i = 0; i < 26; i++) {
+    bool expected = i == 1;
+    CheckIsIndexSelected(i, expected);
+  }
+
+  SelectNonEditableFormOption(0);
+  CheckIsIndexSelected(0, true);
+  for (int i = 1; i < 26; i++) {
+    CheckIsIndexSelected(i, false);
+  }
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       CheckIfIndexSelectedEditableField) {
+  // Editable field has nothing selected upon opening.
+  ClickOnFormFieldAtPoint(EditableFormBegin());
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(2, false);
+
+  SelectEditableFormOption(0);
+  CheckIsIndexSelected(0, true);
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(2, false);
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, DeleteTextFieldEntireSelection) {
+  // Select entire contents of text field.
+  TypeTextIntoTextField(12, RegularFormBegin());
+  SelectAllRegularFormTextWithMouse();
+  CheckFocusedFieldText(L"ABCDEFGHIJKL");
+  CheckSelection(L"ABCDEFGHIJKL");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"");
+
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, DeleteTextFieldSelectionMiddle) {
+  // Select middle section of text.
+  TypeTextIntoTextField(12, RegularFormBegin());
+  SelectTextWithMouse(RegularFormAtX(170.0), RegularFormAtX(125.0));
+  CheckFocusedFieldText(L"ABCDEFGHIJKL");
+  CheckSelection(L"DEFGHI");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"ABCJKL");
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"ABCJKL");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, DeleteTextFieldSelectionLeft) {
+  // Select first few characters of text.
+  TypeTextIntoTextField(12, RegularFormBegin());
+  SelectTextWithMouse(RegularFormBegin(), RegularFormAtX(132.0));
+  CheckSelection(L"ABCD");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"EFGHIJKL");
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"EFGHIJKL");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, DeleteTextFieldSelectionRight) {
+  // Select last few characters of text.
+  TypeTextIntoTextField(12, RegularFormBegin());
+  SelectTextWithMouse(RegularFormEnd(), RegularFormAtX(165.0));
+  CheckSelection(L"IJKL");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"ABCDEFGH");
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"ABCDEFGH");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, DeleteEmptyTextFieldSelection) {
+  // Do not select text.
+  TypeTextIntoTextField(12, RegularFormBegin());
+  CheckSelection(L"");
+
+  // Test that attempt to delete empty text selection has no effect.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"ABCDEFGHIJKL");
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"ABCDEFGHIJKL");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       DeleteEditableComboBoxEntireSelection) {
+  // Select entire contents of user-editable combobox text field.
+  TypeTextIntoTextField(10, EditableFormBegin());
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCDEFGHIJ");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       DeleteEditableComboBoxSelectionMiddle) {
+  // Select middle section of text.
+  TypeTextIntoTextField(10, EditableFormBegin());
+  SelectTextWithMouse(EditableFormAtX(168.0), EditableFormAtX(127.0));
+  CheckSelection(L"DEFGH");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"ABCIJ");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCIJ");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       DeleteEditableComboBoxSelectionLeft) {
+  // Select first few characters of text.
+  TypeTextIntoTextField(10, EditableFormBegin());
+  SelectTextWithMouse(EditableFormBegin(), EditableFormAtX(132.0));
+  CheckSelection(L"ABCD");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"EFGHIJ");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       DeleteEditableComboBoxSelectionRight) {
+  // Select last few characters of text.
+  TypeTextIntoTextField(10, EditableFormBegin());
+  SelectTextWithMouse(EditableFormEnd(), EditableFormAtX(152.0));
+  CheckSelection(L"GHIJ");
+
+  // Test deleting current text selection. Select what remains after deletion to
+  // check that remaining text is as expected.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCDEF");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       DeleteEmptyEditableComboBoxSelection) {
+  // Do not select text.
+  TypeTextIntoTextField(10, EditableFormBegin());
+  CheckSelection(L"");
+
+  // Test that attempt to delete empty text selection has no effect.
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCDEFGHIJ");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, InsertTextInEmptyTextField) {
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckFocusedFieldText(L"");
+
+  // Test inserting text into empty text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"Hello");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"Hello");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, InsertTextInPopulatedTextFieldLeft) {
+  TypeTextIntoTextField(8, RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDEFGH");
+
+  // Click on the leftmost part of the text field.
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDEFGH");
+
+  // Test inserting text in front of existing text in text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"HelloABCDEFGH");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"HelloABCDEFGH");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, InsertTextInPopulatedTextFieldMiddle) {
+  TypeTextIntoTextField(8, RegularFormBegin());
+
+  // Click on the middle of the text field.
+  ClickOnFormFieldAtPoint(RegularFormAtX(134.0));
+
+  // Test inserting text in the middle of existing text in text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"ABCDHelloEFGH");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"ABCDHelloEFGH");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, InsertTextInPopulatedTextFieldRight) {
+  TypeTextIntoTextField(8, RegularFormBegin());
+
+  // Click on the rightmost part of the text field.
+  ClickOnFormFieldAtPoint(RegularFormAtX(166.0));
+
+  // Test inserting text behind existing text in text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"ABCDEFGHHello");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"ABCDEFGHHello");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedTextFieldWhole) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select entire string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"ABCDEFGHIJKL");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"Hello");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"Hello");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedTextFieldLeft) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select left portion of string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormAtX(148.0));
+  CheckSelection(L"ABCDEF");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"HelloGHIJKL");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"HelloGHIJKL");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedTextFieldMiddle) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select middle portion of string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormAtX(171.0));
+  CheckSelection(L"DEFGHI");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"ABCHelloJKL");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedTextFieldRight) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select right portion of string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormEnd());
+  CheckSelection(L"GHIJKL");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection(L"ABCDEFHello");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextInEmptyEditableComboBox) {
+  ClickOnFormFieldAtPoint(EditableFormBegin());
+  CheckFocusedFieldText(L"");
+
+  // Test inserting text into empty user-editable combobox.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"Hello");
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"Hello");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextInPopulatedEditableComboBoxLeft) {
+  TypeTextIntoTextField(6, EditableFormBegin());
+
+  // Click on the leftmost part of the user-editable combobox.
+  ClickOnFormFieldAtPoint(EditableFormBegin());
+
+  // Test inserting text in front of existing text in user-editable combobox.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"HelloABCDEF");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextInPopulatedEditableComboBoxMiddle) {
+  TypeTextIntoTextField(6, EditableFormBegin());
+
+  // Click on the middle of the user-editable combobox.
+  ClickOnFormFieldAtPoint(EditableFormAtX(126.0));
+
+  // Test inserting text in the middle of existing text in user-editable
+  // combobox.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCHelloDEF");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextInPopulatedEditableComboBoxRight) {
+  TypeTextIntoTextField(6, EditableFormBegin());
+
+  // Click on the rightmost part of the user-editable combobox.
+  ClickOnFormFieldAtPoint(EditableFormEnd());
+
+  // Test inserting text behind existing text in user-editable combobox.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCDEFHello");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxWhole) {
+  TypeTextIntoTextField(10, EditableFormBegin());
+
+  // Select entire string in user-editable combobox.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(10, FWL_VKEY_Left, EditableFormEnd());
+  CheckSelection(L"ABCDEFGHIJ");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"Hello");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxLeft) {
+  TypeTextIntoTextField(10, EditableFormBegin());
+
+  // Select left portion of string in user-editable combobox.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormAtX(142.0));
+  CheckSelection(L"ABCDE");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"HelloFGHIJ");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxMiddle) {
+  TypeTextIntoTextField(10, EditableFormBegin());
+
+  // Select middle portion of string in user-editable combobox.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormAtX(167.0));
+  CheckSelection(L"DEFGH");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCHelloIJ");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxRight) {
+  TypeTextIntoTextField(10, EditableFormBegin());
+
+  // Select right portion of string in user-editable combobox.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormEnd());
+  CheckSelection(L"FGHIJ");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of user-editable combobox text field to check that
+  // insertion worked as expected.
+  CheckSelection(L"");
+  SelectAllEditableFormTextWithMouse();
+  CheckSelection(L"ABCDEHello");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextInEmptyCharLimitTextFieldOverflow) {
+  // Click on the textfield.
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(CharLimitFormEnd());
+  CheckFocusedFieldText(L"Elephant");
+
+  // Delete pre-filled contents of text field with char limit.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"Elephant");
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText(L"");
+
+  // Test inserting text into now empty text field so text to be inserted
+  // exceeds the char limit and is cut off.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"Hippopotam");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"Hippopotam");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextInEmptyCharLimitTextFieldFit) {
+  // Click on the textfield.
+  ClickOnFormFieldAtPoint(CharLimitFormEnd());
+  CheckFocusedFieldText(L"Elephant");
+
+  // Delete pre-filled contents of text field with char limit.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"Elephant");
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+
+  // Test inserting text into now empty text field so text to be inserted
+  // exceeds the char limit and is cut off.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Zebra");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"Zebra");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"Zebra");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextInPopulatedCharLimitTextFieldLeft) {
+  // Click on the leftmost part of the text field.
+  ClickOnFormFieldAtPoint(CharLimitFormBegin());
+
+  // Test inserting text in front of existing text in text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"HiElephant");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextInPopulatedCharLimitTextFieldMiddle) {
+  CheckFocusedFieldText(L"");
+  TypeTextIntoTextField(8, RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDEFGH");
+
+  // Click on the middle of the text field.
+  ClickOnFormFieldAtPoint(CharLimitFormAtX(134.0));
+  CheckFocusedFieldText(L"Elephant");
+
+  // Test inserting text in the middle of existing text in text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"ElephHiant");
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"ElephHiant");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextInPopulatedCharLimitTextFieldRight) {
+  TypeTextIntoTextField(8, RegularFormBegin());
+
+  // Click on the rightmost part of the text field.
+  ClickOnFormFieldAtPoint(CharLimitFormAtX(166.0));
+
+  // Test inserting text behind existing text in text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"ElephantHi");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldWhole) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select entire string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(12, FWL_VKEY_Left, CharLimitFormEnd());
+  CheckSelection(L"Elephant");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"Hippopotam");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldLeft) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select left portion of string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(4, FWL_VKEY_Left, CharLimitFormAtX(122.0));
+  CheckSelection(L"Elep");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"Hippophant");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldMiddle) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select middle portion of string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(4, FWL_VKEY_Left, CharLimitFormAtX(136.0));
+  CheckSelection(L"epha");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"ElHippopnt");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest,
+       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldRight) {
+  TypeTextIntoTextField(12, RegularFormBegin());
+
+  // Select right portion of string in text field.
+  CheckSelection(L"");
+  SelectTextWithKeyboard(4, FWL_VKEY_Left, CharLimitFormAtX(152.0));
+  CheckSelection(L"hant");
+
+  // Test replacing text selection with text to be inserted.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hippopotamus");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+
+  // Select entire contents of text field to check that insertion worked
+  // as expected.
+  CheckSelection(L"");
+  SelectAllCharLimitFormTextWithMouse();
+  CheckSelection(L"ElepHippop");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, DoubleClickInTextField) {
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckFocusedFieldText(L"");
+
+  // Test inserting text into empty text field.
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello World");
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"Hello World");
+
+  // Make sure double clicking selects the entire line.
+  CheckSelection(L"");
+  DoubleClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckSelection(L"Hello World");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, FocusChanges) {
+  static const CFX_PointF kNonFormPoint(1, 1);
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(CharLimitFormEnd());
+  CheckFocusedFieldText(L"Elephant");
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckFocusedFieldText(L"");
+  TypeTextIntoTextField(3, CharLimitFormBegin());
+  CheckFocusedFieldText(L"ABElephant");
+  TypeTextIntoTextField(5, RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDE");
+  ClickOnFormFieldAtPoint(CharLimitFormEnd());
+  CheckFocusedFieldText(L"ABElephant");
+  ClickOnFormFieldAtPoint(kNonFormPoint);
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(kNonFormPoint);
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(CharLimitFormBegin());
+  CheckFocusedFieldText(L"ABElephant");
+  ClickOnFormFieldAtPoint(CharLimitFormEnd());
+  CheckFocusedFieldText(L"ABElephant");
+  ClickOnFormFieldAtPoint(RegularFormEnd());
+  CheckFocusedFieldText(L"ABCDE");
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDE");
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDE");
+  ClickOnFormFieldAtPoint(CharLimitFormBegin());
+  CheckFocusedFieldText(L"ABElephant");
+  FORM_ForceToKillFocus(form_handle());
+  CheckFocusedFieldText(L"");
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest, FocusChanges) {
+  static const CFX_PointF kNonFormPoint(1, 1);
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(NonEditableFormBegin());
+  CheckFocusedFieldText(L"Banana");
+  ClickOnFormFieldAtPoint(EditableFormBegin());
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(NonEditableFormEnd());
+  CheckFocusedFieldText(L"Banana");
+  ClickOnFormFieldAtPoint(NonEditableFormBegin());
+  CheckFocusedFieldText(L"Banana");
+  FORM_ForceToKillFocus(form_handle());
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(EditableFormBegin());
+  CheckFocusedFieldText(L"");
+  TypeTextIntoTextField(3, EditableFormBegin());
+  CheckFocusedFieldText(L"ABC");
+  ClickOnFormFieldAtPoint(kNonFormPoint);
+  CheckFocusedFieldText(L"");
+  TypeTextIntoTextField(3, EditableFormEnd());
+  CheckFocusedFieldText(L"ABCABC");
+  ClickOnFormFieldAtPoint(kNonFormPoint);
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(EditableFormDropDown());
+  CheckFocusedFieldText(L"ABCABC");
+  FORM_ForceToKillFocus(form_handle());
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(NonEditableFormDropDown());
+  CheckFocusedFieldText(L"Banana");
+  ClickOnFormFieldAtPoint(kNonFormPoint);
+  CheckFocusedFieldText(L"");
+  ClickOnFormFieldAtPoint(NonEditableFormEnd());
+  CheckFocusedFieldText(L"Banana");
+
+  // Typing into non-editable field results in selecting a different option.
+  TypeTextIntoTextField(1, NonEditableFormEnd());
+  CheckFocusedFieldText(L"Apple");
+  TypeTextIntoTextField(3, NonEditableFormEnd());
+  CheckFocusedFieldText(L"Cherry");
+  TypeTextIntoTextField(2, NonEditableFormEnd());
+  CheckFocusedFieldText(L"Banana");
+
+  SelectEditableFormOption(0);
+  CheckFocusedFieldText(L"Foo");
+  SelectEditableFormOption(1);
+  CheckFocusedFieldText(L"Bar");
+  SelectEditableFormOption(2);
+  CheckFocusedFieldText(L"Qux");
+  SelectNonEditableFormOption(1);
+  CheckFocusedFieldText(L"Banana");
+  SelectNonEditableFormOption(0);
+  CheckFocusedFieldText(L"Apple");
+  SelectNonEditableFormOption(2);
+  CheckFocusedFieldText(L"Cherry");
+
+  // Typing into an editable field changes the text in the option.
+  SelectEditableFormOption(0);
+  CheckFocusedFieldText(L"Foo");
+  TypeTextIntoTextField(5, EditableFormBegin());
+  CheckFocusedFieldText(L"ABCDEFoo");
+  SelectEditableFormOption(2);
+  CheckFocusedFieldText(L"Qux");
+  TypeTextIntoTextField(2, EditableFormEnd());
+  CheckFocusedFieldText(L"QuxAB");
+
+  // But a previously edited option is reset when selected again.
+  SelectEditableFormOption(0);
+  CheckFocusedFieldText(L"Foo");
+  TypeTextIntoTextField(1, EditableFormBegin());
+  CheckFocusedFieldText(L"AFoo");
+  SelectEditableFormOption(0);
+  CheckFocusedFieldText(L"Foo");
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, UndoRedo) {
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckFocusedFieldText(L"");
+  CheckCanUndo(false);
+  CheckCanRedo(false);
+
+  TypeTextIntoTextField(5, RegularFormBegin());
+  CheckFocusedFieldText(L"ABCDE");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+
+  PerformUndo();
+  CheckFocusedFieldText(L"ABCD");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+  PerformUndo();
+  CheckFocusedFieldText(L"ABC");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+
+  PerformRedo();
+  CheckFocusedFieldText(L"ABCD");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+  PerformRedo();
+  CheckFocusedFieldText(L"ABCDE");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+}
+
+// This action only applies to Listboxes and Comboboxes so should fail
+// gracefully for Textboxes by returning false to all operations.
+TEST_F(FPDFFormFillTextFormEmbedderTest, SetIndexSelectedShouldFailGracefully) {
+  // set focus and read text to confirm we have it
+  ClickOnFormFieldAtPoint(CharLimitFormEnd());
+  CheckFocusedFieldText(L"Elephant");
+
+  SetIndexSelectedShouldFail(0, true);
+  SetIndexSelectedShouldFail(0, false);
+  SetIndexSelectedShouldFail(1, true);
+  SetIndexSelectedShouldFail(1, false);
+  SetIndexSelectedShouldFail(-1, true);
+  SetIndexSelectedShouldFail(-1, false);
+}
+
+// This action only applies to Listboxes and Comboboxes so should fail
+// gracefully for Textboxes by returning false to all operations.
+TEST_F(FPDFFormFillTextFormEmbedderTest, IsIndexSelectedShouldFailGracefully) {
+  // set focus and read text to confirm we have it
+  ClickOnFormFieldAtPoint(CharLimitFormEnd());
+  CheckFocusedFieldText(L"Elephant");
+
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(-1, false);
+}
+
+TEST_F(FPDFFormFillComboBoxFormEmbedderTest, UndoRedo) {
+  ClickOnFormFieldAtPoint(NonEditableFormBegin());
+  CheckFocusedFieldText(L"Banana");
+  CheckCanUndo(false);
+  CheckCanRedo(false);
+
+  ClickOnFormFieldAtPoint(EditableFormBegin());
+  CheckFocusedFieldText(L"");
+  CheckCanUndo(false);
+  CheckCanRedo(false);
+
+  TypeTextIntoTextField(3, EditableFormBegin());
+  CheckFocusedFieldText(L"ABC");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+
+  PerformUndo();
+  CheckFocusedFieldText(L"AB");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+  PerformUndo();
+  CheckFocusedFieldText(L"A");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+  PerformUndo();
+  CheckFocusedFieldText(L"");
+  CheckCanUndo(false);
+  CheckCanRedo(true);
+
+  PerformRedo();
+  CheckFocusedFieldText(L"A");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest,
+       CheckIfIndexSelectedSingleSelectField) {
+  // Nothing is selected in single select field upon opening.
+  FocusOnSingleSelectForm();
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(2, false);
+
+  ClickOnSingleSelectFormOption(1);
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, true);
+  CheckIsIndexSelected(2, false);
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest,
+       CheckIfIndexSelectedMultiSelectField) {
+  // Multiselect field set to 'Banana' (index 1) upon opening.
+  FocusOnMultiSelectForm();
+  for (int i = 0; i < 26; i++) {
+    bool expected = i == 1;
+    CheckIsIndexSelected(i, expected);
+  }
+
+  // TODO(bug_1377): Behavior should be changed to the one described below.
+  // Multiselect field set to 'Cherry' (index 2), which is index 1 among the
+  // visible form options because the listbox is scrolled down to have 'Banana'
+  // (index 1) at the top.
+  ClickOnMultiSelectFormOption(1);
+  for (int i = 0; i < 26; i++) {
+    bool expected = i == 1;
+    CheckIsIndexSelected(i, expected);
+  }
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest,
+       SetSelectionProgrammaticallySingleSelectField) {
+  // Nothing is selected in single select field upon opening.
+  FocusOnSingleSelectForm();
+  CheckFocusedFieldText(L"");
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(2, false);
+
+  // Make selections to change the value of the focused annotation
+  // programmatically showing that only one value remains selected at a time.
+  SetIndexSelectedShouldSucceed(0, true);
+  CheckFocusedFieldText(L"Foo");
+  CheckIsIndexSelected(0, true);
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(2, false);
+
+  SetIndexSelectedShouldSucceed(1, true);
+  CheckFocusedFieldText(L"Bar");
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, true);
+  CheckIsIndexSelected(2, false);
+
+  // Selecting/deselecting an index that is already selected/deselected is
+  // success.
+  SetIndexSelectedShouldSucceed(1, true);
+  CheckFocusedFieldText(L"Bar");
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, true);
+  CheckIsIndexSelected(2, false);
+
+  SetIndexSelectedShouldSucceed(2, false);
+  CheckFocusedFieldText(L"Bar");
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, true);
+  CheckIsIndexSelected(2, false);
+
+  // Cannot select indices that are out of range.
+  SetIndexSelectedShouldFail(100, true);
+  SetIndexSelectedShouldFail(-100, true);
+  SetIndexSelectedShouldFail(100, false);
+  SetIndexSelectedShouldFail(-100, false);
+  // Confirm that previous values were not changed.
+  CheckFocusedFieldText(L"Bar");
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, true);
+  CheckIsIndexSelected(2, false);
+
+  // Unlike combobox, should be able to deselect all indices.
+  SetIndexSelectedShouldSucceed(1, false);
+  CheckFocusedFieldText(L"");
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, false);
+  CheckIsIndexSelected(2, false);
+
+  // Check that above actions are interchangeable with click actions, should be
+  // able to use a combination of both.
+  ClickOnSingleSelectFormOption(1);
+  CheckFocusedFieldText(L"Bar");
+  CheckIsIndexSelected(0, false);
+  CheckIsIndexSelected(1, true);
+  CheckIsIndexSelected(2, false);
+}
+
+// Re: Focus Field Text - For multiselect listboxes a caret is set on the last
+// item to be selected/deselected. The text of that item should be returned.
+TEST_F(FPDFFormFillListBoxFormEmbedderTest,
+       SetSelectionProgrammaticallyMultiSelectField) {
+  // Multiselect field set to 'Banana' (index 1) upon opening.
+  FocusOnMultiSelectForm();
+  for (int i = 0; i < 26; i++) {
+    bool expected = i == 1;
+    CheckIsIndexSelected(i, expected);
+  }
+  CheckFocusedFieldText(L"Banana");
+
+  // Select some more options.
+  SetIndexSelectedShouldSucceed(5, true);
+  SetIndexSelectedShouldSucceed(6, true);
+  SetIndexSelectedShouldSucceed(20, true);
+  for (int i = 0; i < 26; i++) {
+    bool expected = (i == 1 || i == 5 || i == 6 || i == 20);
+    CheckIsIndexSelected(i, expected);
+  }
+  CheckFocusedFieldText(L"Ugli Fruit");
+
+  // Selecting indices that are already selected is success - changes nothing.
+  SetIndexSelectedShouldSucceed(5, true);
+  SetIndexSelectedShouldSucceed(6, true);
+  SetIndexSelectedShouldSucceed(20, true);
+  for (int i = 0; i < 26; i++) {
+    bool expected = (i == 1 || i == 5 || i == 6 || i == 20);
+    CheckIsIndexSelected(i, expected);
+  }
+  CheckFocusedFieldText(L"Ugli Fruit");
+
+  // Deselect some options.
+  SetIndexSelectedShouldSucceed(20, false);
+  SetIndexSelectedShouldSucceed(1, false);
+  for (int i = 0; i < 26; i++) {
+    bool expected = (i == 5 || i == 6);
+    CheckIsIndexSelected(i, expected);
+  }
+  CheckFocusedFieldText(L"Banana");
+
+  // Deselecting indices that already aren't selected is success - does not
+  // change the selected values but moves the focus text caret to last item we
+  // executed on.
+  SetIndexSelectedShouldSucceed(1, false);
+  SetIndexSelectedShouldSucceed(3, false);
+  for (int i = 0; i < 26; i++) {
+    bool expected = (i == 5 || i == 6);
+    CheckIsIndexSelected(i, expected);
+  }
+  CheckFocusedFieldText(L"Date");
+
+  // Cannot select indices that are out of range.
+  SetIndexSelectedShouldFail(100, true);
+  SetIndexSelectedShouldFail(-100, true);
+  SetIndexSelectedShouldFail(100, false);
+  SetIndexSelectedShouldFail(-100, false);
+  // Confirm that previous values were not changed.
+  CheckFocusedFieldText(L"Date");
+  for (int i = 0; i < 26; i++) {
+    bool expected = (i == 5 || i == 6);
+    CheckIsIndexSelected(i, expected);
+  }
+
+  // Check that above actions are interchangeable with click actions, should be
+  // able to use a combination of both.
+  // TODO(bug_1377): Change to click on form option 0 instead of form option 1
+  ClickOnMultiSelectFormOption(1);
+  for (int i = 0; i < 26; i++) {
+    bool expected = i == 1;
+    CheckIsIndexSelected(i, expected);
+  }
+  CheckFocusedFieldText(L"Banana");
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelected) {
+  // Multiselect field set to 'Gamma' (index 2) and 'Epsilon' (index 4) upon
+  // opening.
+  FocusOnMultiSelectMultipleSelectedForm();
+  for (int i = 0; i < 5; i++) {
+    // TODO(bug_1377): Should be selected at index 2 and index 4.
+    bool expected = false;
+    CheckIsIndexSelected(i, expected);
+  }
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest,
+       CheckIfVerticalScrollIsAtFirstSelected) {
+  // Multiselect field set to 'Gamma' (index 2) and 'Epsilon' (index 4) upon
+  // opening.
+
+  // TODO(bug_1377): Behavior should be changed to the one described below.
+  // The top visible option is 'Gamma' (index 2), so the first selection should
+  // not change. The second selection, 'Epsilon,' should be deselected.
+  ClickOnMultiSelectMultipleSelectedFormOption(0);
+  for (int i = 0; i < 5; i++) {
+    bool expected = i == 0;
+    CheckIsIndexSelected(i, expected);
+  }
+}
+
+TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckForNoOverscroll) {
+  // Only the last option in the list, 'Saskatchewan', is selected.
+  FocusOnSingleSelectLastSelectedForm();
+  for (int i = 0; i < 10; i++) {
+    bool expected = i == 9;
+    CheckIsIndexSelected(i, expected);
+  }
+
+  // Even though the top index is specified to be at 'Saskatchewan' (index 9),
+  // the top visible option will be the one above it, 'Quebec' (index 8), to
+  // prevent overscrolling. Therefore, clicking on the first visible option of
+  // the list should select 'Quebec' instead of 'Saskatchewan.'
+  ClickOnSingleSelectLastSelectedFormOption(0);
+  for (int i = 0; i < 10; i++) {
+    bool expected = i == 8;
+    CheckIsIndexSelected(i, expected);
+  }
+}
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceSelection) {
+  ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ");
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckCanUndo(false);
+  CheckCanRedo(false);
+
+  TypeTextIntoTextField(2, RegularFormBegin());
+  CheckFocusedFieldText(L"AB");
+  CheckSelection(L"");
+  SelectTextWithKeyboard(1, FWL_VKEY_Right, RegularFormBegin());
+  CheckSelection(L"A");
+
+  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
+  CheckFocusedFieldText(L"XYZB");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+
+  PerformUndo();
+  CheckFocusedFieldText(L"AB");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+
+  PerformUndo();
+  CheckFocusedFieldText(L"A");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+
+  PerformUndo();
+  CheckFocusedFieldText(L"");
+  CheckCanUndo(false);
+  CheckCanRedo(true);
+
+  PerformRedo();
+  CheckFocusedFieldText(L"A");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+
+  PerformRedo();
+  CheckFocusedFieldText(L"AB");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+
+  PerformRedo();
+  CheckFocusedFieldText(L"XYZB");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+}
diff --git a/fpdfsdk/fpdf_javascript.cpp b/fpdfsdk/fpdf_javascript.cpp
new file mode 100644
index 0000000..c2d119b
--- /dev/null
+++ b/fpdfsdk/fpdf_javascript.cpp
@@ -0,0 +1,85 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_javascript.h"
+
+#include <memory>
+
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfdoc/cpdf_action.h"
+#include "core/fpdfdoc/cpdf_nametree.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/ptr_util.h"
+
+struct CPDF_JavaScript {
+  WideString name;
+  WideString script;
+};
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFDoc_GetJavaScriptActionCount(FPDF_DOCUMENT document) {
+  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
+  return doc ? CPDF_NameTree(doc, "JavaScript").GetCount() : -1;
+}
+
+FPDF_EXPORT FPDF_JAVASCRIPT_ACTION FPDF_CALLCONV
+FPDFDoc_GetJavaScriptAction(FPDF_DOCUMENT document, int index) {
+  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc || index < 0)
+    return nullptr;
+
+  CPDF_NameTree name_tree(doc, "JavaScript");
+  if (static_cast<size_t>(index) >= name_tree.GetCount())
+    return nullptr;
+
+  WideString name;
+  CPDF_Dictionary* obj =
+      ToDictionary(name_tree.LookupValueAndName(index, &name));
+  if (!obj)
+    return nullptr;
+
+  // Validate |obj|. Type is optional, but must be valid if present.
+  CPDF_Action action(obj);
+  if (action.GetType() != CPDF_Action::JavaScript)
+    return nullptr;
+
+  Optional<WideString> script = action.MaybeGetJavaScript();
+  if (!script.has_value())
+    return nullptr;
+
+  auto js = pdfium::MakeUnique<CPDF_JavaScript>();
+  js->name = name;
+  js->script = script.value();
+  return FPDFJavaScriptActionFromCPDFJavaScriptAction(js.release());
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDFDoc_CloseJavaScriptAction(FPDF_JAVASCRIPT_ACTION javascript) {
+  // Take object back across API and destroy it.
+  std::unique_ptr<CPDF_JavaScript>(
+      CPDFJavaScriptActionFromFPDFJavaScriptAction(javascript));
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFJavaScriptAction_GetName(FPDF_JAVASCRIPT_ACTION javascript,
+                             FPDF_WCHAR* buffer,
+                             unsigned long buflen) {
+  CPDF_JavaScript* js =
+      CPDFJavaScriptActionFromFPDFJavaScriptAction(javascript);
+  if (!js)
+    return 0;
+  return Utf16EncodeMaybeCopyAndReturnLength(js->name, buffer, buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFJavaScriptAction_GetScript(FPDF_JAVASCRIPT_ACTION javascript,
+                               FPDF_WCHAR* buffer,
+                               unsigned long buflen) {
+  CPDF_JavaScript* js =
+      CPDFJavaScriptActionFromFPDFJavaScriptAction(javascript);
+  if (!js)
+    return 0;
+  return Utf16EncodeMaybeCopyAndReturnLength(js->script, buffer, buflen);
+}
diff --git a/fpdfsdk/fpdf_javascript_embeddertest.cpp b/fpdfsdk/fpdf_javascript_embeddertest.cpp
new file mode 100644
index 0000000..35deb69
--- /dev/null
+++ b/fpdfsdk/fpdf_javascript_embeddertest.cpp
@@ -0,0 +1,130 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "core/fxcrt/fx_memory.h"
+#include "public/fpdf_javascript.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/utils/hash.h"
+
+class FPDFJavaScriptEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFJavaScriptEmbedderTest, CountJS) {
+  // Open a file with JS.
+  ASSERT_TRUE(OpenDocument("bug_679649.pdf"));
+  EXPECT_EQ(1, FPDFDoc_GetJavaScriptActionCount(document()));
+}
+
+TEST_F(FPDFJavaScriptEmbedderTest, CountNoJS) {
+  // Open a file without JS.
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(0, FPDFDoc_GetJavaScriptActionCount(document()));
+
+  // Provide no document.
+  EXPECT_EQ(-1, FPDFDoc_GetJavaScriptActionCount(nullptr));
+}
+
+TEST_F(FPDFJavaScriptEmbedderTest, GetJS) {
+  ASSERT_TRUE(OpenDocument("js.pdf"));
+  EXPECT_EQ(6, FPDFDoc_GetJavaScriptActionCount(document()));
+
+  ScopedFPDFJavaScriptAction js;
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), -1));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 6));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(nullptr, -1));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 0));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 1));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 2));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 5));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 6));
+  EXPECT_FALSE(js);
+
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 0));
+  EXPECT_TRUE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 1));
+  EXPECT_TRUE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 2));
+  EXPECT_TRUE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 3));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 4));
+  EXPECT_FALSE(js);
+  js.reset(FPDFDoc_GetJavaScriptAction(document(), 5));
+  EXPECT_FALSE(js);
+}
+
+TEST_F(FPDFJavaScriptEmbedderTest, GetJSName) {
+  ASSERT_TRUE(OpenDocument("bug_679649.pdf"));
+  ScopedFPDFJavaScriptAction js(FPDFDoc_GetJavaScriptAction(document(), 0));
+  ASSERT_TRUE(js);
+
+  {
+    FPDF_WCHAR buf[10];
+    EXPECT_EQ(0u, FPDFJavaScriptAction_GetName(nullptr, nullptr, 0));
+    EXPECT_EQ(0u, FPDFJavaScriptAction_GetName(nullptr, buf, 0));
+    EXPECT_EQ(0u, FPDFJavaScriptAction_GetName(nullptr, buf, sizeof(buf)));
+  }
+
+  constexpr size_t kExpectedLength = 22;
+  ASSERT_EQ(kExpectedLength,
+            FPDFJavaScriptAction_GetName(js.get(), nullptr, 0));
+
+  // Check that the name not returned if the buffer is too small.
+  // The result buffer should be overwritten with an empty string.
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(kExpectedLength);
+  // Write in the buffer to verify it's not overwritten.
+  memcpy(buf.data(), "abcdefgh", 8);
+  EXPECT_EQ(kExpectedLength, FPDFJavaScriptAction_GetName(js.get(), buf.data(),
+                                                          kExpectedLength - 1));
+  EXPECT_EQ(0, memcmp(buf.data(), "abcdefgh", 8));
+
+  EXPECT_EQ(kExpectedLength, FPDFJavaScriptAction_GetName(js.get(), buf.data(),
+                                                          kExpectedLength));
+  EXPECT_EQ(L"startDelay", GetPlatformWString(buf.data()));
+}
+
+TEST_F(FPDFJavaScriptEmbedderTest, GetJSScript) {
+  ASSERT_TRUE(OpenDocument("bug_679649.pdf"));
+  ScopedFPDFJavaScriptAction js(FPDFDoc_GetJavaScriptAction(document(), 0));
+  ASSERT_TRUE(js);
+
+  {
+    FPDF_WCHAR buf[10];
+    EXPECT_EQ(0u, FPDFJavaScriptAction_GetScript(nullptr, nullptr, 0));
+    EXPECT_EQ(0u, FPDFJavaScriptAction_GetScript(nullptr, buf, 0));
+    EXPECT_EQ(0u, FPDFJavaScriptAction_GetScript(nullptr, buf, sizeof(buf)));
+  }
+
+  constexpr size_t kExpectedLength = 218;
+  ASSERT_EQ(kExpectedLength,
+            FPDFJavaScriptAction_GetScript(js.get(), nullptr, 0));
+
+  // Check that the string value of an AP is not returned if the buffer is too
+  // small. The result buffer should be overwritten with an empty string.
+  std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(kExpectedLength);
+  // Write in the buffer to verify it's not overwritten.
+  memcpy(buf.data(), "abcdefgh", 8);
+  EXPECT_EQ(kExpectedLength, FPDFJavaScriptAction_GetScript(
+                                 js.get(), buf.data(), kExpectedLength - 1));
+  EXPECT_EQ(0, memcmp(buf.data(), "abcdefgh", 8));
+
+  static const wchar_t kExpectedScript[] =
+      L"function ping() {\n  app.alert(\"ping\");\n}\n"
+      L"var timer = app.setTimeOut(\"ping()\", 100);\napp.clearTimeOut(timer);";
+  EXPECT_EQ(kExpectedLength, FPDFJavaScriptAction_GetScript(
+                                 js.get(), buf.data(), kExpectedLength));
+  EXPECT_EQ(kExpectedScript, GetPlatformWString(buf.data()));
+}
diff --git a/fpdfsdk/fpdf_ppo.cpp b/fpdfsdk/fpdf_ppo.cpp
new file mode 100644
index 0000000..819ba05
--- /dev/null
+++ b/fpdfsdk/fpdf_ppo.cpp
@@ -0,0 +1,824 @@
+// 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 "public/fpdf_ppo.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "constants/page_object.h"
+#include "core/fpdfapi/page/cpdf_page.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_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
+#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/render/cpdf_pagerendercache.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+// Struct that stores sub page origin and scale information.  When importing
+// more than one pages onto the same page, most likely the pages will need to be
+// scaled down, and scale is in range of (0, 1) exclusive.
+struct NupPageSettings {
+  CFX_PointF subPageStartPoint;
+  float scale;
+};
+
+// Calculates the N-up parameters.  When importing multiple pages into one page.
+// The space of output page is evenly divided along the X axis and Y axis based
+// on the input |nPagesOnXAxis| and |nPagesOnYAxis|.
+class NupState {
+ public:
+  NupState(const CFX_SizeF& pagesize,
+           size_t nPagesOnXAxis,
+           size_t nPagesOnYAxis);
+
+  // Calculate sub page origin and scale with the source page of |pagesize| and
+  // new page of |m_subPageSize|.
+  NupPageSettings CalculateNewPagePosition(const CFX_SizeF& pagesize);
+
+ private:
+  // Helper function to get the |iSubX|, |iSubY| pair based on |m_subPageIndex|.
+  // The space of output page is evenly divided into slots along x and y axis.
+  // |iSubX| and |iSubY| are 0-based indices that indicate which allocation
+  // slot to use.
+  std::pair<size_t, size_t> ConvertPageOrder() const;
+
+  // Given the |iSubX| and |iSubY| subpage position within a page, and a source
+  // page with dimensions of |pagesize|, calculate the sub page's origin and
+  // scale.
+  NupPageSettings CalculatePageEdit(size_t iSubX,
+                                    size_t iSubY,
+                                    const CFX_SizeF& pagesize) const;
+
+  const CFX_SizeF m_destPageSize;
+  const size_t m_nPagesOnXAxis;
+  const size_t m_nPagesOnYAxis;
+  const size_t m_nPagesPerSheet;
+  CFX_SizeF m_subPageSize;
+
+  // A 0-based index, in range of [0, m_nPagesPerSheet - 1).
+  size_t m_subPageIndex = 0;
+};
+
+NupState::NupState(const CFX_SizeF& pagesize,
+                   size_t nPagesOnXAxis,
+                   size_t nPagesOnYAxis)
+    : m_destPageSize(pagesize),
+      m_nPagesOnXAxis(nPagesOnXAxis),
+      m_nPagesOnYAxis(nPagesOnYAxis),
+      m_nPagesPerSheet(nPagesOnXAxis * nPagesOnYAxis) {
+  ASSERT(m_nPagesOnXAxis > 0);
+  ASSERT(m_nPagesOnYAxis > 0);
+  ASSERT(m_destPageSize.width > 0);
+  ASSERT(m_destPageSize.height > 0);
+
+  m_subPageSize.width = m_destPageSize.width / m_nPagesOnXAxis;
+  m_subPageSize.height = m_destPageSize.height / m_nPagesOnYAxis;
+}
+
+std::pair<size_t, size_t> NupState::ConvertPageOrder() const {
+  size_t iSubX = m_subPageIndex % m_nPagesOnXAxis;
+  size_t iSubY = m_subPageIndex / m_nPagesOnXAxis;
+
+  // Y Axis, pages start from the top of the output page.
+  iSubY = m_nPagesOnYAxis - iSubY - 1;
+
+  return {iSubX, iSubY};
+}
+
+NupPageSettings NupState::CalculatePageEdit(size_t iSubX,
+                                            size_t iSubY,
+                                            const CFX_SizeF& pagesize) const {
+  NupPageSettings settings;
+  settings.subPageStartPoint.x = iSubX * m_subPageSize.width;
+  settings.subPageStartPoint.y = iSubY * m_subPageSize.height;
+
+  const float xScale = m_subPageSize.width / pagesize.width;
+  const float yScale = m_subPageSize.height / pagesize.height;
+  settings.scale = std::min(xScale, yScale);
+
+  float subWidth = pagesize.width * settings.scale;
+  float subHeight = pagesize.height * settings.scale;
+  if (xScale > yScale)
+    settings.subPageStartPoint.x += (m_subPageSize.width - subWidth) / 2;
+  else
+    settings.subPageStartPoint.y += (m_subPageSize.height - subHeight) / 2;
+  return settings;
+}
+
+NupPageSettings NupState::CalculateNewPagePosition(const CFX_SizeF& pagesize) {
+  if (m_subPageIndex >= m_nPagesPerSheet)
+    m_subPageIndex = 0;
+
+  size_t iSubX;
+  size_t iSubY;
+  std::tie(iSubX, iSubY) = ConvertPageOrder();
+  ++m_subPageIndex;
+  return CalculatePageEdit(iSubX, iSubY, pagesize);
+}
+
+const CPDF_Object* PageDictGetInheritableTag(const CPDF_Dictionary* pDict,
+                                             const ByteString& bsSrcTag) {
+  if (!pDict || bsSrcTag.IsEmpty())
+    return nullptr;
+  if (!pDict->KeyExist(pdfium::page_object::kParent) ||
+      !pDict->KeyExist(pdfium::page_object::kType)) {
+    return nullptr;
+  }
+
+  const CPDF_Object* pType =
+      pDict->GetObjectFor(pdfium::page_object::kType)->GetDirect();
+  if (!ToName(pType))
+    return nullptr;
+  if (pType->GetString().Compare("Page"))
+    return nullptr;
+
+  const CPDF_Dictionary* pp = ToDictionary(
+      pDict->GetObjectFor(pdfium::page_object::kParent)->GetDirect());
+  if (!pp)
+    return nullptr;
+
+  if (pDict->KeyExist(bsSrcTag))
+    return pDict->GetObjectFor(bsSrcTag);
+
+  while (pp) {
+    if (pp->KeyExist(bsSrcTag))
+      return pp->GetObjectFor(bsSrcTag);
+    if (!pp->KeyExist(pdfium::page_object::kParent))
+      break;
+    pp = ToDictionary(
+        pp->GetObjectFor(pdfium::page_object::kParent)->GetDirect());
+  }
+  return nullptr;
+}
+
+CFX_FloatRect GetMediaBox(const CPDF_Dictionary* pPageDict) {
+  const CPDF_Object* pMediaBox =
+      PageDictGetInheritableTag(pPageDict, pdfium::page_object::kMediaBox);
+  const CPDF_Array* pArray = ToArray(pMediaBox->GetDirect());
+  if (!pArray)
+    return CFX_FloatRect();
+  return pArray->GetRect();
+}
+
+CFX_FloatRect GetCropBox(const CPDF_Dictionary* pPageDict) {
+  if (pPageDict->KeyExist(pdfium::page_object::kCropBox))
+    return pPageDict->GetRectFor(pdfium::page_object::kCropBox);
+  return GetMediaBox(pPageDict);
+}
+
+bool CopyInheritable(CPDF_Dictionary* pDestPageDict,
+                     const CPDF_Dictionary* pSrcPageDict,
+                     const ByteString& key) {
+  if (pDestPageDict->KeyExist(key))
+    return true;
+
+  const CPDF_Object* pInheritable =
+      PageDictGetInheritableTag(pSrcPageDict, key);
+  if (!pInheritable)
+    return false;
+
+  pDestPageDict->SetFor(key, pInheritable->Clone());
+  return true;
+}
+
+bool ParsePageRangeString(const ByteString& bsPageRange,
+                          uint32_t nCount,
+                          std::vector<uint32_t>* pageArray) {
+  ByteString bsStrippedPageRange = bsPageRange;
+  bsStrippedPageRange.Remove(' ');
+  size_t nLength = bsStrippedPageRange.GetLength();
+  if (nLength == 0)
+    return true;
+
+  static const ByteString cbCompareString("0123456789-,");
+  for (size_t i = 0; i < nLength; ++i) {
+    if (!cbCompareString.Contains(bsStrippedPageRange[i]))
+      return false;
+  }
+
+  ByteString cbMidRange;
+  size_t nStringFrom = 0;
+  size_t nStringTo = 0;
+  while (nStringTo < nLength) {
+    nStringTo = bsStrippedPageRange.Find(',', nStringFrom).value_or(nLength);
+    cbMidRange =
+        bsStrippedPageRange.Substr(nStringFrom, nStringTo - nStringFrom);
+    Optional<size_t> nDashPosition = cbMidRange.Find('-');
+    if (nDashPosition) {
+      size_t nMid = nDashPosition.value();
+      uint32_t nStartPageNum = pdfium::base::checked_cast<uint32_t>(
+          atoi(cbMidRange.First(nMid).c_str()));
+      if (nStartPageNum == 0)
+        return false;
+
+      ++nMid;
+      size_t nEnd = cbMidRange.GetLength() - nMid;
+      if (nEnd == 0)
+        return false;
+
+      uint32_t nEndPageNum = pdfium::base::checked_cast<uint32_t>(
+          atoi(cbMidRange.Substr(nMid, nEnd).c_str()));
+      if (nStartPageNum < 0 || nStartPageNum > nEndPageNum ||
+          nEndPageNum > nCount) {
+        return false;
+      }
+      for (uint32_t i = nStartPageNum; i <= nEndPageNum; ++i) {
+        pageArray->push_back(i);
+      }
+    } else {
+      uint32_t nPageNum =
+          pdfium::base::checked_cast<uint32_t>(atoi(cbMidRange.c_str()));
+      if (nPageNum <= 0 || nPageNum > nCount)
+        return false;
+      pageArray->push_back(nPageNum);
+    }
+    nStringFrom = nStringTo + 1;
+  }
+  return true;
+}
+
+std::vector<uint32_t> GetPageNumbers(const CPDF_Document& doc,
+                                     const ByteString& bsPageRange) {
+  std::vector<uint32_t> page_numbers;
+  uint32_t nCount = doc.GetPageCount();
+  if (bsPageRange.IsEmpty()) {
+    for (uint32_t i = 1; i <= nCount; ++i)
+      page_numbers.push_back(i);
+  } else {
+    if (!ParsePageRangeString(bsPageRange, nCount, &page_numbers))
+      page_numbers.clear();
+  }
+  return page_numbers;
+}
+
+class CPDF_PageOrganizer {
+ protected:
+  CPDF_PageOrganizer(CPDF_Document* pDestDoc, CPDF_Document* pSrcDoc);
+  ~CPDF_PageOrganizer();
+
+  // Must be called after construction before doing anything else.
+  bool Init();
+
+  bool UpdateReference(CPDF_Object* pObj);
+
+  CPDF_Document* dest() { return m_pDestDoc.Get(); }
+  const CPDF_Document* dest() const { return m_pDestDoc.Get(); }
+
+  CPDF_Document* src() { return m_pSrcDoc.Get(); }
+  const CPDF_Document* src() const { return m_pSrcDoc.Get(); }
+
+  void AddObjectMapping(uint32_t dwOldPageObj, uint32_t dwNewPageObj) {
+    m_ObjectNumberMap[dwOldPageObj] = dwNewPageObj;
+  }
+
+  void ClearObjectNumberMap() { m_ObjectNumberMap.clear(); }
+
+ private:
+  uint32_t GetNewObjId(CPDF_Reference* pRef);
+
+  UnownedPtr<CPDF_Document> const m_pDestDoc;
+  UnownedPtr<CPDF_Document> const m_pSrcDoc;
+
+  // Mapping of source object number to destination object number.
+  std::map<uint32_t, uint32_t> m_ObjectNumberMap;
+};
+
+CPDF_PageOrganizer::CPDF_PageOrganizer(CPDF_Document* pDestDoc,
+                                       CPDF_Document* pSrcDoc)
+    : m_pDestDoc(pDestDoc), m_pSrcDoc(pSrcDoc) {}
+
+CPDF_PageOrganizer::~CPDF_PageOrganizer() = default;
+
+bool CPDF_PageOrganizer::Init() {
+  ASSERT(m_pDestDoc);
+  ASSERT(m_pSrcDoc);
+
+  CPDF_Dictionary* pNewRoot = dest()->GetRoot();
+  if (!pNewRoot)
+    return false;
+
+  CPDF_Dictionary* pDocInfoDict = dest()->GetInfo();
+  if (!pDocInfoDict)
+    return false;
+
+  pDocInfoDict->SetNewFor<CPDF_String>("Producer", "PDFium", false);
+
+  ByteString cbRootType = pNewRoot->GetStringFor("Type", ByteString());
+  if (cbRootType.IsEmpty())
+    pNewRoot->SetNewFor<CPDF_Name>("Type", "Catalog");
+
+  CPDF_Object* pElement = pNewRoot->GetObjectFor("Pages");
+  CPDF_Dictionary* pNewPages =
+      pElement ? ToDictionary(pElement->GetDirect()) : nullptr;
+  if (!pNewPages) {
+    pNewPages = dest()->NewIndirect<CPDF_Dictionary>();
+    pNewRoot->SetNewFor<CPDF_Reference>("Pages", dest(),
+                                        pNewPages->GetObjNum());
+  }
+  ByteString cbPageType = pNewPages->GetStringFor("Type", ByteString());
+  if (cbPageType.IsEmpty())
+    pNewPages->SetNewFor<CPDF_Name>("Type", "Pages");
+
+  if (!pNewPages->GetArrayFor("Kids")) {
+    auto* pNewArray = dest()->NewIndirect<CPDF_Array>();
+    pNewPages->SetNewFor<CPDF_Number>("Count", 0);
+    pNewPages->SetNewFor<CPDF_Reference>("Kids", dest(),
+                                         pNewArray->GetObjNum());
+  }
+  return true;
+}
+
+bool CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj) {
+  switch (pObj->GetType()) {
+    case CPDF_Object::kReference: {
+      CPDF_Reference* pReference = pObj->AsReference();
+      uint32_t newobjnum = GetNewObjId(pReference);
+      if (newobjnum == 0)
+        return false;
+      pReference->SetRef(dest(), newobjnum);
+      return true;
+    }
+    case CPDF_Object::kDictionary: {
+      CPDF_Dictionary* pDict = pObj->AsDictionary();
+      std::vector<ByteString> bad_keys;
+      {
+        CPDF_DictionaryLocker locker(pDict);
+        for (const auto& it : locker) {
+          const ByteString& key = it.first;
+          if (key == "Parent" || key == "Prev" || key == "First")
+            continue;
+          CPDF_Object* pNextObj = it.second.Get();
+          if (!pNextObj)
+            return false;
+          if (!UpdateReference(pNextObj))
+            bad_keys.push_back(key);
+        }
+      }
+      for (const auto& key : bad_keys)
+        pDict->RemoveFor(key);
+      return true;
+    }
+    case CPDF_Object::kArray: {
+      CPDF_Array* pArray = pObj->AsArray();
+      for (size_t i = 0; i < pArray->size(); ++i) {
+        CPDF_Object* pNextObj = pArray->GetObjectAt(i);
+        if (!pNextObj || !UpdateReference(pNextObj))
+          return false;
+      }
+      return true;
+    }
+    case CPDF_Object::kStream: {
+      CPDF_Stream* pStream = pObj->AsStream();
+      CPDF_Dictionary* pDict = pStream->GetDict();
+      return pDict && UpdateReference(pDict);
+    }
+    default:
+      return true;
+  }
+}
+
+uint32_t CPDF_PageOrganizer::GetNewObjId(CPDF_Reference* pRef) {
+  if (!pRef)
+    return 0;
+
+  uint32_t dwObjnum = pRef->GetRefObjNum();
+  uint32_t dwNewObjNum = 0;
+  const auto it = m_ObjectNumberMap.find(dwObjnum);
+  if (it != m_ObjectNumberMap.end())
+    dwNewObjNum = it->second;
+  if (dwNewObjNum)
+    return dwNewObjNum;
+
+  CPDF_Object* pDirect = pRef->GetDirect();
+  if (!pDirect)
+    return 0;
+
+  RetainPtr<CPDF_Object> pClone = pDirect->Clone();
+  if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) {
+    if (pDictClone->KeyExist("Type")) {
+      ByteString strType = pDictClone->GetStringFor("Type");
+      if (!FXSYS_stricmp(strType.c_str(), "Pages"))
+        return 4;
+      if (!FXSYS_stricmp(strType.c_str(), "Page"))
+        return 0;
+    }
+  }
+  CPDF_Object* pUnownedClone = dest()->AddIndirectObject(std::move(pClone));
+  dwNewObjNum = pUnownedClone->GetObjNum();
+  AddObjectMapping(dwObjnum, dwNewObjNum);
+  if (!UpdateReference(pUnownedClone))
+    return 0;
+
+  return dwNewObjNum;
+}
+
+// Copies pages from a source document into a destination document.
+// This class is intended to be used once via ExportPage() and then destroyed.
+class CPDF_PageExporter final : public CPDF_PageOrganizer {
+ public:
+  CPDF_PageExporter(CPDF_Document* pDestDoc, CPDF_Document* pSrcDoc);
+  ~CPDF_PageExporter();
+
+  // For the pages from the source document with |pageNums| as their page
+  // numbers, insert them into the destination document at page |nIndex|.
+  // |pageNums| is 1-based.
+  // |nIndex| is 0-based.
+  bool ExportPage(const std::vector<uint32_t>& pageNums, int nIndex);
+};
+
+CPDF_PageExporter::CPDF_PageExporter(CPDF_Document* pDestDoc,
+                                     CPDF_Document* pSrcDoc)
+    : CPDF_PageOrganizer(pDestDoc, pSrcDoc) {}
+
+CPDF_PageExporter::~CPDF_PageExporter() = default;
+
+bool CPDF_PageExporter::ExportPage(const std::vector<uint32_t>& pageNums,
+                                   int nIndex) {
+  if (!Init())
+    return false;
+
+  int curpage = nIndex;
+  for (size_t i = 0; i < pageNums.size(); ++i) {
+    CPDF_Dictionary* pDestPageDict = dest()->CreateNewPage(curpage);
+    auto* pSrcPageDict = src()->GetPageDictionary(pageNums[i] - 1);
+    if (!pSrcPageDict || !pDestPageDict)
+      return false;
+
+    // Clone the page dictionary
+    CPDF_DictionaryLocker locker(pSrcPageDict);
+    for (const auto& it : locker) {
+      const ByteString& cbSrcKeyStr = it.first;
+      if (cbSrcKeyStr == pdfium::page_object::kType ||
+          cbSrcKeyStr == pdfium::page_object::kParent) {
+        continue;
+      }
+
+      CPDF_Object* pObj = it.second.Get();
+      pDestPageDict->SetFor(cbSrcKeyStr, pObj->Clone());
+    }
+
+    // inheritable item
+    // Even though some entries are required by the PDF spec, there exist
+    // PDFs that omit them. Set some defaults in this case.
+    // 1 MediaBox - required
+    if (!CopyInheritable(pDestPageDict, pSrcPageDict,
+                         pdfium::page_object::kMediaBox)) {
+      // Search for "CropBox" in the source page dictionary.
+      // If it does not exist, use the default letter size.
+      const CPDF_Object* pInheritable = PageDictGetInheritableTag(
+          pSrcPageDict, pdfium::page_object::kCropBox);
+      if (pInheritable) {
+        pDestPageDict->SetFor(pdfium::page_object::kMediaBox,
+                              pInheritable->Clone());
+      } else {
+        // Make the default size letter size (8.5"x11")
+        static const CFX_FloatRect kDefaultLetterRect(0, 0, 612, 792);
+        pDestPageDict->SetRectFor(pdfium::page_object::kMediaBox,
+                                  kDefaultLetterRect);
+      }
+    }
+
+    // 2 Resources - required
+    if (!CopyInheritable(pDestPageDict, pSrcPageDict,
+                         pdfium::page_object::kResources)) {
+      // Use a default empty resources if it does not exist.
+      pDestPageDict->SetNewFor<CPDF_Dictionary>(
+          pdfium::page_object::kResources);
+    }
+
+    // 3 CropBox - optional
+    CopyInheritable(pDestPageDict, pSrcPageDict, pdfium::page_object::kCropBox);
+    // 4 Rotate - optional
+    CopyInheritable(pDestPageDict, pSrcPageDict, pdfium::page_object::kRotate);
+
+    // Update the reference
+    uint32_t dwOldPageObj = pSrcPageDict->GetObjNum();
+    uint32_t dwNewPageObj = pDestPageDict->GetObjNum();
+    AddObjectMapping(dwOldPageObj, dwNewPageObj);
+    UpdateReference(pDestPageDict);
+    ++curpage;
+  }
+
+  return true;
+}
+
+// Copies pages from a source document into a destination document. Creates 1
+// page in the destination document for every N source pages. This class is
+// intended to be used once via ExportNPagesToOne() and then destroyed.
+class CPDF_NPageToOneExporter final : public CPDF_PageOrganizer {
+ public:
+  CPDF_NPageToOneExporter(CPDF_Document* pDestDoc, CPDF_Document* pSrcDoc);
+  ~CPDF_NPageToOneExporter();
+
+  // For the pages from the source document with |pageNums| as their page
+  // numbers, insert them into the destination document, starting at page 0.
+  // |pageNums| is 1-based.
+  // |destPageSize| is the destination document page dimensions, measured in
+  // PDF "user space" units.
+  // |nPagesOnXAxis| and |nPagesOnXAxis| together defines how many source
+  // pages fit on one destination page.
+  bool ExportNPagesToOne(const std::vector<uint32_t>& pageNums,
+                         const CFX_SizeF& destPageSize,
+                         size_t nPagesOnXAxis,
+                         size_t nPagesOnYAxis);
+
+ private:
+  // Map page object number to XObject object name.
+  using PageXObjectMap = std::map<uint32_t, ByteString>;
+
+  // Creates an XObject from |pSrcPageDict|, or find an existing XObject that
+  // represents |pSrcPageDict|. The transformation matrix is specified in
+  // |settings|.
+  // Returns the XObject reference surrounded by the transformation matrix.
+  ByteString AddSubPage(const CPDF_Dictionary* pSrcPageDict,
+                        const NupPageSettings& settings);
+
+  // Creates an XObject from |pSrcPageDict|. Updates mapping as needed.
+  // Returns the name of the newly created XObject.
+  ByteString MakeXObjectFromPage(const CPDF_Dictionary* pSrcPageDict);
+
+  // Adds |bsContent| as the Contents key in |pDestPageDict|.
+  // Adds the objects in |m_XObjectNameToNumberMap| to the XObject dictionary in
+  // |pDestPageDict|'s Resources dictionary.
+  void FinishPage(CPDF_Dictionary* pDestPageDict, const ByteString& bsContent);
+
+  // Counter for giving new XObjects unique names.
+  uint32_t m_nObjectNumber = 0;
+
+  // Keeps track of created XObjects in the current page.
+  // Map XObject's object name to it's object number.
+  std::map<ByteString, uint32_t> m_XObjectNameToNumberMap;
+
+  // Mapping of source page object number and XObject name of the entire doc.
+  // If there are multiple source pages that reference the same object number,
+  // they can also share the same created XObject.
+  PageXObjectMap m_SrcPageXObjectMap;
+};
+
+CPDF_NPageToOneExporter::CPDF_NPageToOneExporter(CPDF_Document* pDestDoc,
+                                                 CPDF_Document* pSrcDoc)
+    : CPDF_PageOrganizer(pDestDoc, pSrcDoc) {}
+
+CPDF_NPageToOneExporter::~CPDF_NPageToOneExporter() = default;
+
+bool CPDF_NPageToOneExporter::ExportNPagesToOne(
+    const std::vector<uint32_t>& pageNums,
+    const CFX_SizeF& destPageSize,
+    size_t nPagesOnXAxis,
+    size_t nPagesOnYAxis) {
+  if (!Init())
+    return false;
+
+  FX_SAFE_SIZE_T nSafePagesPerSheet = nPagesOnXAxis;
+  nSafePagesPerSheet *= nPagesOnYAxis;
+  if (!nSafePagesPerSheet.IsValid())
+    return false;
+
+  ClearObjectNumberMap();
+  m_SrcPageXObjectMap.clear();
+  size_t nPagesPerSheet = nSafePagesPerSheet.ValueOrDie();
+  NupState nupState(destPageSize, nPagesOnXAxis, nPagesOnYAxis);
+
+  size_t curpage = 0;
+  const CFX_FloatRect destPageRect(0, 0, destPageSize.width,
+                                   destPageSize.height);
+  for (size_t iOuterPage = 0; iOuterPage < pageNums.size();
+       iOuterPage += nPagesPerSheet) {
+    m_XObjectNameToNumberMap.clear();
+
+    // Create a new page
+    CPDF_Dictionary* pDestPageDict = dest()->CreateNewPage(curpage);
+    if (!pDestPageDict)
+      return false;
+
+    pDestPageDict->SetRectFor(pdfium::page_object::kMediaBox, destPageRect);
+    ByteString bsContent;
+    size_t iInnerPageMax =
+        std::min(iOuterPage + nPagesPerSheet, pageNums.size());
+    for (size_t i = iOuterPage; i < iInnerPageMax; ++i) {
+      auto* pSrcPageDict = src()->GetPageDictionary(pageNums[i] - 1);
+      if (!pSrcPageDict)
+        return false;
+
+      auto pSrcPage = pdfium::MakeRetain<CPDF_Page>(src(), pSrcPageDict);
+      pSrcPage->SetRenderCache(
+          pdfium::MakeUnique<CPDF_PageRenderCache>(pSrcPage.Get()));
+      NupPageSettings settings =
+          nupState.CalculateNewPagePosition(pSrcPage->GetPageSize());
+      bsContent += AddSubPage(pSrcPageDict, settings);
+    }
+
+    FinishPage(pDestPageDict, bsContent);
+    ++curpage;
+  }
+
+  return true;
+}
+
+ByteString CPDF_NPageToOneExporter::AddSubPage(
+    const CPDF_Dictionary* pSrcPageDict,
+    const NupPageSettings& settings) {
+  uint32_t dwSrcPageObjnum = pSrcPageDict->GetObjNum();
+  const auto it = m_SrcPageXObjectMap.find(dwSrcPageObjnum);
+  ByteString bsXObjectName = it != m_SrcPageXObjectMap.end()
+                                 ? it->second
+                                 : MakeXObjectFromPage(pSrcPageDict);
+
+  CFX_Matrix matrix;
+  matrix.Scale(settings.scale, settings.scale);
+  matrix.Translate(settings.subPageStartPoint.x, settings.subPageStartPoint.y);
+
+  std::ostringstream contentStream;
+  contentStream << "q\n"
+                << matrix.a << " " << matrix.b << " " << matrix.c << " "
+                << matrix.d << " " << matrix.e << " " << matrix.f << " cm\n"
+                << "/" << bsXObjectName << " Do Q\n";
+  return ByteString(contentStream);
+}
+
+ByteString CPDF_NPageToOneExporter::MakeXObjectFromPage(
+    const CPDF_Dictionary* pSrcPageDict) {
+  ASSERT(pSrcPageDict);
+
+  const CPDF_Object* pSrcContentObj =
+      pSrcPageDict->GetDirectObjectFor(pdfium::page_object::kContents);
+
+  CPDF_Stream* pNewXObject = dest()->NewIndirect<CPDF_Stream>(
+      nullptr, 0, dest()->New<CPDF_Dictionary>());
+  CPDF_Dictionary* pNewXObjectDict = pNewXObject->GetDict();
+  static const char kResourceString[] = "Resources";
+  if (!CopyInheritable(pNewXObjectDict, pSrcPageDict, kResourceString)) {
+    // Use a default empty resources if it does not exist.
+    pNewXObjectDict->SetNewFor<CPDF_Dictionary>(kResourceString);
+  }
+  uint32_t dwSrcPageObj = pSrcPageDict->GetObjNum();
+  uint32_t dwNewXobjectObj = pNewXObjectDict->GetObjNum();
+  AddObjectMapping(dwSrcPageObj, dwNewXobjectObj);
+  UpdateReference(pNewXObjectDict);
+
+  pNewXObjectDict->SetNewFor<CPDF_Name>("Type", "XObject");
+  pNewXObjectDict->SetNewFor<CPDF_Name>("Subtype", "Form");
+  pNewXObjectDict->SetNewFor<CPDF_Number>("FormType", 1);
+  pNewXObjectDict->SetRectFor("BBox", GetCropBox(pSrcPageDict));
+  // TODO(xlou): add matrix field to pNewXObjectDict.
+
+  if (pSrcContentObj) {
+    ByteString bsSrcContentStream;
+    const CPDF_Array* pSrcContentArray = ToArray(pSrcContentObj);
+    if (pSrcContentArray) {
+      for (size_t i = 0; i < pSrcContentArray->size(); ++i) {
+        const CPDF_Stream* pStream = pSrcContentArray->GetStreamAt(i);
+        auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+        pAcc->LoadAllDataFiltered();
+        bsSrcContentStream += ByteString(pAcc->GetSpan());
+        bsSrcContentStream += "\n";
+      }
+    } else {
+      const CPDF_Stream* pStream = pSrcContentObj->AsStream();
+      auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
+      pAcc->LoadAllDataFiltered();
+      bsSrcContentStream = ByteString(pAcc->GetSpan());
+    }
+    pNewXObject->SetDataAndRemoveFilter(bsSrcContentStream.raw_span());
+  }
+
+  // TODO(xlou): A better name schema to avoid possible object name collision.
+  ByteString bsXObjectName = ByteString::Format("X%d", ++m_nObjectNumber);
+  m_XObjectNameToNumberMap[bsXObjectName] = pNewXObject->GetObjNum();
+  m_SrcPageXObjectMap[pSrcPageDict->GetObjNum()] = bsXObjectName;
+  return bsXObjectName;
+}
+
+void CPDF_NPageToOneExporter::FinishPage(CPDF_Dictionary* pDestPageDict,
+                                         const ByteString& bsContent) {
+  ASSERT(pDestPageDict);
+
+  CPDF_Dictionary* pRes =
+      pDestPageDict->GetDictFor(pdfium::page_object::kResources);
+  if (!pRes) {
+    pRes = pDestPageDict->SetNewFor<CPDF_Dictionary>(
+        pdfium::page_object::kResources);
+  }
+
+  CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject");
+  if (!pPageXObject)
+    pPageXObject = pRes->SetNewFor<CPDF_Dictionary>("XObject");
+
+  for (auto& it : m_XObjectNameToNumberMap)
+    pPageXObject->SetNewFor<CPDF_Reference>(it.first, dest(), it.second);
+
+  auto pDict = dest()->New<CPDF_Dictionary>();
+  CPDF_Stream* pStream =
+      dest()->NewIndirect<CPDF_Stream>(nullptr, 0, std::move(pDict));
+  pStream->SetData(bsContent.raw_span());
+  pDestPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents,
+                                           dest(), pStream->GetObjNum());
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
+                                                     FPDF_DOCUMENT src_doc,
+                                                     FPDF_BYTESTRING pagerange,
+                                                     int index) {
+  CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc);
+  if (!dest_doc)
+    return false;
+
+  CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
+  if (!pSrcDoc)
+    return false;
+
+  std::vector<uint32_t> page_numbers = GetPageNumbers(*pSrcDoc, pagerange);
+  if (page_numbers.empty())
+    return false;
+
+  CPDF_PageExporter exporter(pDestDoc, pSrcDoc);
+  return exporter.ExportPage(page_numbers, index);
+}
+
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
+FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc,
+                       float output_width,
+                       float output_height,
+                       size_t num_pages_on_x_axis,
+                       size_t num_pages_on_y_axis) {
+  CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
+  if (!pSrcDoc)
+    return nullptr;
+
+  if (output_width <= 0 || output_height <= 0 || num_pages_on_x_axis <= 0 ||
+      num_pages_on_y_axis <= 0) {
+    return nullptr;
+  }
+
+  ScopedFPDFDocument output_doc(FPDF_CreateNewDocument());
+  if (!output_doc)
+    return nullptr;
+
+  CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(output_doc.get());
+  ASSERT(pDestDoc);
+
+  std::vector<uint32_t> page_numbers = GetPageNumbers(*pSrcDoc, ByteString());
+  if (page_numbers.empty())
+    return nullptr;
+
+  if (num_pages_on_x_axis == 1 && num_pages_on_y_axis == 1) {
+    CPDF_PageExporter exporter(pDestDoc, pSrcDoc);
+    if (!exporter.ExportPage(page_numbers, 0))
+      return nullptr;
+    return output_doc.release();
+  }
+
+  CPDF_NPageToOneExporter exporter(pDestDoc, pSrcDoc);
+  if (!exporter.ExportNPagesToOne(page_numbers,
+                                  CFX_SizeF(output_width, output_height),
+                                  num_pages_on_x_axis, num_pages_on_y_axis)) {
+    return nullptr;
+  }
+  return output_doc.release();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) {
+  CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc);
+  if (!pDstDoc)
+    return false;
+
+  CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
+  if (!pSrcDoc)
+    return false;
+
+  const CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
+  pSrcDict = pSrcDict->GetDictFor("ViewerPreferences");
+  if (!pSrcDict)
+    return false;
+
+  CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
+  if (!pDstDict)
+    return false;
+
+  pDstDict->SetFor("ViewerPreferences", pSrcDict->CloneDirectObject());
+  return true;
+}
diff --git a/fpdfsdk/fpdf_ppo_embeddertest.cpp b/fpdfsdk/fpdf_ppo_embeddertest.cpp
new file mode 100644
index 0000000..5684680
--- /dev/null
+++ b/fpdfsdk/fpdf_ppo_embeddertest.cpp
@@ -0,0 +1,297 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdf_ppo.h"
+#include "public/fpdf_save.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class FPDFPPOEmbedderTest : public EmbedderTest {};
+
+int FakeBlockWriter(FPDF_FILEWRITE* pThis,
+                    const void* pData,
+                    unsigned long size) {
+  return size;
+}
+
+}  // namespace
+
+TEST_F(FPDFPPOEmbedderTest, NoViewerPreferences) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_FALSE(FPDF_CopyViewerPreferences(output_doc, document()));
+  FPDF_CloseDocument(output_doc);
+}
+
+TEST_F(FPDFPPOEmbedderTest, ViewerPreferences) {
+  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
+  FPDF_CloseDocument(output_doc);
+}
+
+TEST_F(FPDFPPOEmbedderTest, ImportPages) {
+  ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  ASSERT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0));
+  EXPECT_EQ(1, FPDF_GetPageCount(output_doc));
+  FPDF_CloseDocument(output_doc);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFPPOEmbedderTest, ImportNPages) {
+  ASSERT_TRUE(OpenDocument("rectangles_multi_pages.pdf"));
+
+  ScopedFPDFDocument output_doc_2up(
+      FPDF_ImportNPagesToOne(document(), 612, 792, 2, 1));
+  ASSERT_TRUE(output_doc_2up);
+  EXPECT_EQ(3, FPDF_GetPageCount(output_doc_2up.get()));
+  ScopedFPDFDocument output_doc_5up(
+      FPDF_ImportNPagesToOne(document(), 612, 792, 5, 1));
+  ASSERT_TRUE(output_doc_5up);
+  EXPECT_EQ(1, FPDF_GetPageCount(output_doc_5up.get()));
+  ScopedFPDFDocument output_doc_8up(
+      FPDF_ImportNPagesToOne(document(), 792, 612, 8, 1));
+  ASSERT_TRUE(output_doc_8up);
+  EXPECT_EQ(1, FPDF_GetPageCount(output_doc_8up.get()));
+  ScopedFPDFDocument output_doc_128up(
+      FPDF_ImportNPagesToOne(document(), 792, 612, 128, 1));
+  ASSERT_TRUE(output_doc_128up);
+  EXPECT_EQ(1, FPDF_GetPageCount(output_doc_128up.get()));
+}
+
+TEST_F(FPDFPPOEmbedderTest, BadNupParams) {
+  ASSERT_TRUE(OpenDocument("rectangles_multi_pages.pdf"));
+
+  FPDF_DOCUMENT output_doc_zero_row =
+      FPDF_ImportNPagesToOne(document(), 612, 792, 0, 3);
+  ASSERT_FALSE(output_doc_zero_row);
+  FPDF_DOCUMENT output_doc_zero_col =
+      FPDF_ImportNPagesToOne(document(), 612, 792, 2, 0);
+  ASSERT_FALSE(output_doc_zero_col);
+  FPDF_DOCUMENT output_doc_zero_width =
+      FPDF_ImportNPagesToOne(document(), 0, 792, 2, 1);
+  ASSERT_FALSE(output_doc_zero_width);
+  FPDF_DOCUMENT output_doc_zero_height =
+      FPDF_ImportNPagesToOne(document(), 612, 0, 7, 1);
+  ASSERT_FALSE(output_doc_zero_height);
+}
+
+// TODO(Xlou): Add more tests to check output doc content of
+// FPDF_ImportNPagesToOne()
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_NupRenderImage DISABLED_NupRenderImage
+#else
+#define MAYBE_NupRenderImage NupRenderImage
+#endif
+TEST_F(FPDFPPOEmbedderTest, MAYBE_NupRenderImage) {
+  ASSERT_TRUE(OpenDocument("rectangles_multi_pages.pdf"));
+  const int kPageCount = 2;
+  static constexpr const char* kExpectedMD5s[kPageCount] = {
+      "4d225b961da0f1bced7c83273e64c9b6", "fb18142190d770cfbc329d2b071aee4d"};
+  ScopedFPDFDocument output_doc_3up(
+      FPDF_ImportNPagesToOne(document(), 792, 612, 3, 1));
+  ASSERT_TRUE(output_doc_3up);
+  ASSERT_EQ(kPageCount, FPDF_GetPageCount(output_doc_3up.get()));
+  for (int i = 0; i < kPageCount; ++i) {
+    ScopedFPDFPage page(FPDF_LoadPage(output_doc_3up.get(), i));
+    ASSERT_TRUE(page);
+    ScopedFPDFBitmap bitmap = RenderPage(page.get());
+    EXPECT_EQ(792, FPDFBitmap_GetWidth(bitmap.get()));
+    EXPECT_EQ(612, FPDFBitmap_GetHeight(bitmap.get()));
+    EXPECT_EQ(kExpectedMD5s[i], HashBitmap(bitmap.get()));
+  }
+}
+
+TEST_F(FPDFPPOEmbedderTest, BUG_925981) {
+  ASSERT_TRUE(OpenDocument("bug_925981.pdf"));
+  ScopedFPDFDocument output_doc_2up(
+      FPDF_ImportNPagesToOne(document(), 612, 792, 2, 1));
+  EXPECT_EQ(1, FPDF_GetPageCount(output_doc_2up.get()));
+}
+
+TEST_F(FPDFPPOEmbedderTest, BadRepeatViewerPref) {
+  ASSERT_TRUE(OpenDocument("repeat_viewer_ref.pdf"));
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
+
+  FPDF_FILEWRITE writer;
+  writer.version = 1;
+  writer.WriteBlock = FakeBlockWriter;
+
+  EXPECT_TRUE(FPDF_SaveAsCopy(output_doc, &writer, 0));
+  FPDF_CloseDocument(output_doc);
+}
+
+TEST_F(FPDFPPOEmbedderTest, BadCircularViewerPref) {
+  ASSERT_TRUE(OpenDocument("circular_viewer_ref.pdf"));
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
+
+  FPDF_FILEWRITE writer;
+  writer.version = 1;
+  writer.WriteBlock = FakeBlockWriter;
+
+  EXPECT_TRUE(FPDF_SaveAsCopy(output_doc, &writer, 0));
+  FPDF_CloseDocument(output_doc);
+}
+
+TEST_F(FPDFPPOEmbedderTest, BadRanges) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "clams", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "0", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "42", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1,2", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1-2", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), ",1", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1,", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1-", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "-1", 0));
+  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "-,0,,,1-", 0));
+  FPDF_CloseDocument(output_doc);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFPPOEmbedderTest, GoodRanges) {
+  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1,1,1,1", 0));
+  EXPECT_EQ(4, FPDF_GetPageCount(output_doc));
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1-1", 0));
+  EXPECT_EQ(5, FPDF_GetPageCount(output_doc));
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "5-5", 0));
+  EXPECT_EQ(6, FPDF_GetPageCount(output_doc));
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "2-4", 0));
+  EXPECT_EQ(9, FPDF_GetPageCount(output_doc));
+  FPDF_CloseDocument(output_doc);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFPPOEmbedderTest, BUG_664284) {
+  EXPECT_TRUE(OpenDocument("bug_664284.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_NE(nullptr, page);
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0));
+  FPDF_CloseDocument(output_doc);
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_BUG_750568 DISABLED_BUG_750568
+#else
+#define MAYBE_BUG_750568 BUG_750568
+#endif
+TEST_F(FPDFPPOEmbedderTest, MAYBE_BUG_750568) {
+  const char* const kHashes[] = {
+      "64ad08132a1c5a166768298c8a578f57", "83b83e2f6bc80707d0a917c7634140b9",
+      "913cd3723a451e4e46fbc2c05702d1ee", "81fb7cfd4860f855eb468f73dfeb6d60"};
+
+  ASSERT_TRUE(OpenDocument("bug_750568.pdf"));
+  ASSERT_EQ(4, FPDF_GetPageCount(document()));
+
+  for (size_t i = 0; i < 4; ++i) {
+    FPDF_PAGE page = LoadPage(i);
+    ASSERT_TRUE(page);
+
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get()));
+    ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get()));
+    ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get()));
+
+    EXPECT_EQ(kHashes[i], HashBitmap(bitmap.get()));
+    UnloadPage(page);
+  }
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  ASSERT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1,2,3,4", 0));
+  ASSERT_EQ(4, FPDF_GetPageCount(output_doc));
+  for (size_t i = 0; i < 4; ++i) {
+    FPDF_PAGE page = FPDF_LoadPage(output_doc, i);
+    ASSERT_TRUE(page);
+
+    ScopedFPDFBitmap bitmap = RenderPage(page);
+    ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get()));
+    ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get()));
+    ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get()));
+
+    EXPECT_EQ(kHashes[i], HashBitmap(bitmap.get()));
+    FPDF_ClosePage(page);
+  }
+  FPDF_CloseDocument(output_doc);
+}
+
+TEST_F(FPDFPPOEmbedderTest, ImportWithZeroLengthStream) {
+  EXPECT_TRUE(OpenDocument("zero_length_stream.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get()));
+  ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get()));
+  ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get()));
+
+  std::string digest = HashBitmap(bitmap.get());
+  UnloadPage(page);
+
+  FPDF_DOCUMENT new_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(new_doc);
+  EXPECT_TRUE(FPDF_ImportPages(new_doc, document(), "1", 0));
+
+  EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
+  FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
+  ASSERT_NE(nullptr, new_page);
+  ScopedFPDFBitmap new_bitmap = RenderPage(new_page);
+  ASSERT_EQ(200, FPDFBitmap_GetWidth(new_bitmap.get()));
+  ASSERT_EQ(200, FPDFBitmap_GetHeight(new_bitmap.get()));
+  ASSERT_EQ(800, FPDFBitmap_GetStride(new_bitmap.get()));
+
+  EXPECT_EQ(digest, HashBitmap(new_bitmap.get()));
+  FPDF_ClosePage(new_page);
+  FPDF_CloseDocument(new_doc);
+}
diff --git a/fpdfsdk/fpdf_progressive.cpp b/fpdfsdk/fpdf_progressive.cpp
index 730ba48..99a9230 100644
--- a/fpdfsdk/fpdf_progressive.cpp
+++ b/fpdfsdk/fpdf_progressive.cpp
@@ -8,27 +8,38 @@
 
 #include <utility>
 
-#include "core/fpdfapi/cpdf_pagerendercontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/render/cpdf_pagerendercontext.h"
 #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
-#include "core/fxcrt/fx_memory.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fpdfsdk/fsdk_pauseadapter.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_pauseadapter.h"
+#include "fpdfsdk/cpdfsdk_renderpage.h"
 #include "public/fpdfview.h"
 #include "third_party/base/ptr_util.h"
 
+#ifdef _SKIA_SUPPORT_PATHS_
+#include "core/fxge/cfx_renderdevice.h"
+#endif
+
 // These checks are here because core/ and public/ cannot depend on each other.
-static_assert(CPDF_ProgressiveRenderer::Ready == FPDF_RENDER_READY,
-              "CPDF_ProgressiveRenderer::Ready value mismatch");
-static_assert(CPDF_ProgressiveRenderer::ToBeContinued ==
+static_assert(CPDF_ProgressiveRenderer::kReady == FPDF_RENDER_READY,
+              "CPDF_ProgressiveRenderer::kReady value mismatch");
+static_assert(CPDF_ProgressiveRenderer::kToBeContinued ==
                   FPDF_RENDER_TOBECONTINUED,
-              "CPDF_ProgressiveRenderer::ToBeContinued value mismatch");
-static_assert(CPDF_ProgressiveRenderer::Done == FPDF_RENDER_DONE,
-              "CPDF_ProgressiveRenderer::Done value mismatch");
-static_assert(CPDF_ProgressiveRenderer::Failed == FPDF_RENDER_FAILED,
-              "CPDF_ProgressiveRenderer::Failed value mismatch");
+              "CPDF_ProgressiveRenderer::kToBeContinued value mismatch");
+static_assert(CPDF_ProgressiveRenderer::kDone == FPDF_RENDER_DONE,
+              "CPDF_ProgressiveRenderer::kDone value mismatch");
+static_assert(CPDF_ProgressiveRenderer::kFailed == FPDF_RENDER_FAILED,
+              "CPDF_ProgressiveRenderer::kFailed value mismatch");
+
+namespace {
+
+int ToFPDFStatus(CPDF_ProgressiveRenderer::Status status) {
+  return static_cast<int>(status);
+}
+
+}  // namespace
 
 FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap,
                                                           FPDF_PAGE page,
@@ -50,25 +61,26 @@
   CPDF_PageRenderContext* pContext = pOwnedContext.get();
   pPage->SetRenderContext(std::move(pOwnedContext));
 
-  RetainPtr<CFX_DIBitmap> pBitmap(CFXBitmapFromFPDFBitmap(bitmap));
+  RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
   auto pOwnedDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
   CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
   pContext->m_pDevice = std::move(pOwnedDevice);
   pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
 
-  IFSDK_PAUSE_Adapter IPauseAdapter(pause);
-  FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
-                         rotate, flags, false, &IPauseAdapter);
+  CPDFSDK_PauseAdapter pause_adapter(pause);
+  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+                                size_y, rotate, flags,
+                                /*need_to_restore=*/false, &pause_adapter);
 
 #ifdef _SKIA_SUPPORT_PATHS_
   pDevice->Flush(false);
   pBitmap->UnPreMultiply();
 #endif
-  if (pContext->m_pRenderer) {
-    return CPDF_ProgressiveRenderer::ToFPDFStatus(
-        pContext->m_pRenderer->GetStatus());
-  }
-  return FPDF_RENDER_FAILED;
+
+  if (!pContext->m_pRenderer)
+    return FPDF_RENDER_FAILED;
+
+  return ToFPDFStatus(pContext->m_pRenderer->GetStatus());
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPage_Continue(FPDF_PAGE page,
@@ -80,26 +92,27 @@
   if (!pPage)
     return FPDF_RENDER_FAILED;
 
-  CPDF_PageRenderContext* pContext = pPage->GetRenderContext();
-  if (pContext && pContext->m_pRenderer) {
-    IFSDK_PAUSE_Adapter IPauseAdapter(pause);
-    pContext->m_pRenderer->Continue(&IPauseAdapter);
+  auto* pContext =
+      static_cast<CPDF_PageRenderContext*>(pPage->GetRenderContext());
+  if (!pContext || !pContext->m_pRenderer)
+    return FPDF_RENDER_FAILED;
+
+  CPDFSDK_PauseAdapter pause_adapter(pause);
+  pContext->m_pRenderer->Continue(&pause_adapter);
 #ifdef _SKIA_SUPPORT_PATHS_
-    CFX_RenderDevice* pDevice = pContext->m_pDevice.get();
-    pDevice->Flush(false);
-    pDevice->GetBitmap()->UnPreMultiply();
+  CFX_RenderDevice* pDevice = pContext->m_pDevice.get();
+  pDevice->Flush(false);
+  pDevice->GetBitmap()->UnPreMultiply();
 #endif
-    return CPDF_ProgressiveRenderer::ToFPDFStatus(
-        pContext->m_pRenderer->GetStatus());
-  }
-  return FPDF_RENDER_FAILED;
+  return ToFPDFStatus(pContext->m_pRenderer->GetStatus());
 }
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage_Close(FPDF_PAGE page) {
   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (pPage) {
 #ifdef _SKIA_SUPPORT_PATHS_
-    CPDF_PageRenderContext* pContext = pPage->GetRenderContext();
+    auto* pContext =
+        static_cast<CPDF_PageRenderContext*>(pPage->GetRenderContext());
     if (pContext && pContext->m_pRenderer) {
       CFX_RenderDevice* pDevice = pContext->m_pDevice.get();
       pDevice->Flush(true);
diff --git a/fpdfsdk/fpdf_save.cpp b/fpdfsdk/fpdf_save.cpp
new file mode 100644
index 0000000..78e0e58
--- /dev/null
+++ b/fpdfsdk/fpdf_save.cpp
@@ -0,0 +1,211 @@
+// 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 "public/fpdf_save.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fpdfapi/edit/cpdf_creator.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/fx_extension.h"
+#include "fpdfsdk/cpdfsdk_filewriteadapter.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/fpdf_edit.h"
+#include "third_party/base/optional.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/cfx_memorystream.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "public/fpdf_formfill.h"
+#endif
+
+#if defined(OS_ANDROID)
+#include <time.h>
+#else
+#include <ctime>
+#endif
+
+namespace {
+
+#ifdef PDF_ENABLE_XFA
+bool SaveXFADocumentData(CPDFXFA_Context* pContext,
+                         std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
+  if (!pContext)
+    return false;
+
+  if (!pContext->ContainsExtensionForm())
+    return true;
+
+  CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
+  if (!pPDFDocument)
+    return false;
+
+  CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
+  if (!pRoot)
+    return false;
+
+  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  if (!pAcroForm)
+    return false;
+
+  CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
+  if (!pXFA)
+    return true;
+
+  CPDF_Array* pArray = pXFA->AsArray();
+  if (!pArray)
+    return false;
+
+  int size = pArray->size();
+  int iFormIndex = -1;
+  int iDataSetsIndex = -1;
+  for (int i = 0; i < size - 1; i++) {
+    const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
+    if (!pPDFObj->IsString())
+      continue;
+    if (pPDFObj->GetString() == "form")
+      iFormIndex = i + 1;
+    else if (pPDFObj->GetString() == "datasets")
+      iDataSetsIndex = i + 1;
+  }
+
+  CPDF_Stream* pFormStream = nullptr;
+  if (iFormIndex != -1) {
+    // Get form CPDF_Stream
+    CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex);
+    if (pFormPDFObj->IsReference()) {
+      CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect();
+      if (pFormDirectObj && pFormDirectObj->IsStream()) {
+        pFormStream = pFormDirectObj->AsStream();
+      }
+    } else if (pFormPDFObj->IsStream()) {
+      pFormStream = pFormPDFObj->AsStream();
+    }
+  }
+
+  CPDF_Stream* pDataSetsStream = nullptr;
+  if (iDataSetsIndex != -1) {
+    // Get datasets CPDF_Stream
+    CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex);
+    if (pDataSetsPDFObj->IsReference()) {
+      CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsReference();
+      CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect();
+      if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
+        pDataSetsStream = pDataSetsDirectObj->AsStream();
+      }
+    } else if (pDataSetsPDFObj->IsStream()) {
+      pDataSetsStream = pDataSetsPDFObj->AsStream();
+    }
+  }
+  // L"datasets"
+  {
+    RetainPtr<IFX_SeekableStream> pFileWrite =
+        pdfium::MakeRetain<CFX_MemoryStream>();
+    if (pContext->SaveDatasetsPackage(pFileWrite) &&
+        pFileWrite->GetSize() > 0) {
+      auto pDataDict = pPDFDocument->New<CPDF_Dictionary>();
+      if (iDataSetsIndex != -1) {
+        if (pDataSetsStream) {
+          pDataSetsStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
+        }
+      } else {
+        CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
+        pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
+        int iLast = pArray->size() - 2;
+        pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
+        pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
+                                            pData->GetObjNum());
+      }
+      fileList->push_back(std::move(pFileWrite));
+    }
+  }
+  // L"form"
+  {
+    RetainPtr<IFX_SeekableStream> pFileWrite =
+        pdfium::MakeRetain<CFX_MemoryStream>();
+    if (pContext->SaveFormPackage(pFileWrite) && pFileWrite->GetSize() > 0) {
+      auto pDataDict = pPDFDocument->New<CPDF_Dictionary>();
+      if (iFormIndex != -1) {
+        if (pFormStream)
+          pFormStream->InitStreamFromFile(pFileWrite, std::move(pDataDict));
+      } else {
+        CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
+        pData->InitStreamFromFile(pFileWrite, std::move(pDataDict));
+        int iLast = pArray->size() - 2;
+        pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
+        pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
+                                            pData->GetObjNum());
+      }
+      fileList->push_back(std::move(pFileWrite));
+    }
+  }
+  return true;
+}
+#endif  // PDF_ENABLE_XFA
+
+bool DoDocSave(FPDF_DOCUMENT document,
+               FPDF_FILEWRITE* pFileWrite,
+               FPDF_DWORD flags,
+               Optional<int> version) {
+  CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pPDFDoc)
+    return 0;
+
+#ifdef PDF_ENABLE_XFA
+  auto* pContext = static_cast<CPDFXFA_Context*>(pPDFDoc->GetExtension());
+  if (pContext) {
+    std::vector<RetainPtr<IFX_SeekableStream>> fileList;
+    pContext->SendPreSaveToXFADoc(&fileList);
+    SaveXFADocumentData(pContext, &fileList);
+  }
+#endif  // PDF_ENABLE_XFA
+
+  if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
+    flags = 0;
+
+  CPDF_Creator fileMaker(
+      pPDFDoc, pdfium::MakeRetain<CPDFSDK_FileWriteAdapter>(pFileWrite));
+  if (version.has_value())
+    fileMaker.SetFileVersion(version.value());
+  if (flags == FPDF_REMOVE_SECURITY) {
+    flags = 0;
+    fileMaker.RemoveSecurity();
+  }
+
+  bool bRet = fileMaker.Create(flags);
+
+#ifdef PDF_ENABLE_XFA
+  if (pContext)
+    pContext->SendPostSaveToXFADoc();
+#endif  // PDF_ENABLE_XFA
+
+  return bRet;
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
+                                                    FPDF_FILEWRITE* pFileWrite,
+                                                    FPDF_DWORD flags) {
+  return DoDocSave(document, pFileWrite, flags, {});
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_SaveWithVersion(FPDF_DOCUMENT document,
+                     FPDF_FILEWRITE* pFileWrite,
+                     FPDF_DWORD flags,
+                     int fileVersion) {
+  return DoDocSave(document, pFileWrite, flags, fileVersion);
+}
diff --git a/fpdfsdk/fpdf_save_embeddertest.cpp b/fpdfsdk/fpdf_save_embeddertest.cpp
new file mode 100644
index 0000000..614b4f4
--- /dev/null
+++ b/fpdfsdk/fpdf_save_embeddertest.cpp
@@ -0,0 +1,118 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "core/fxcrt/fx_string.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdf_ppo.h"
+#include "public/fpdf_save.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFSaveEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFSaveEmbedderTest, SaveSimpleDoc) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+  EXPECT_EQ(805u, GetString().length());
+}
+
+TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithVersion) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 14));
+  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.4\r\n"));
+  EXPECT_EQ(805u, GetString().length());
+}
+TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithBadVersion) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, -1));
+  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+
+  ClearString();
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 0));
+  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+
+  ClearString();
+  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 18));
+  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+}
+
+TEST_F(FPDFSaveEmbedderTest, SaveCopiedDoc) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
+  EXPECT_TRUE(output_doc);
+  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0));
+  EXPECT_TRUE(FPDF_SaveAsCopy(output_doc, this, 0));
+  FPDF_CloseDocument(output_doc);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFSaveEmbedderTest, SaveLinearizedDoc) {
+  const int kPageCount = 3;
+  std::string original_md5[kPageCount];
+
+  EXPECT_TRUE(OpenDocument("linearized.pdf"));
+  for (int i = 0; i < kPageCount; ++i) {
+    FPDF_PAGE page = LoadPage(i);
+    ASSERT_TRUE(page);
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    EXPECT_EQ(612, FPDFBitmap_GetWidth(bitmap.get()));
+    EXPECT_EQ(792, FPDFBitmap_GetHeight(bitmap.get()));
+    original_md5[i] = HashBitmap(bitmap.get());
+    UnloadPage(page);
+  }
+
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.6\r\n"));
+  EXPECT_THAT(GetString(), testing::HasSubstr("/Root "));
+  EXPECT_THAT(GetString(), testing::HasSubstr("/Info "));
+  EXPECT_EQ(8219u, GetString().length());
+
+  // Make sure new document renders the same as the old one.
+  ASSERT_TRUE(OpenSavedDocument());
+  for (int i = 0; i < kPageCount; ++i) {
+    FPDF_PAGE page = LoadSavedPage(i);
+    ASSERT_TRUE(page);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(page);
+    EXPECT_EQ(original_md5[i], HashBitmap(bitmap.get()));
+    CloseSavedPage(page);
+  }
+  CloseSavedDocument();
+}
+
+#ifdef PDF_ENABLE_XFA
+TEST_F(FPDFSaveEmbedderTest, SaveXFADoc) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
+  ASSERT_TRUE(OpenSavedDocument());
+  // TODO(tsepez): check for XFA forms in document
+  CloseSavedDocument();
+}
+#endif  // PDF_ENABLE_XFA
+
+TEST_F(FPDFSaveEmbedderTest, BUG_342) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_THAT(GetString(), testing::HasSubstr("0000000000 65535 f\r\n"));
+  EXPECT_THAT(GetString(),
+              testing::Not(testing::HasSubstr("0000000000 65536 f\r\n")));
+}
+
+TEST_F(FPDFSaveEmbedderTest, BUG_905142) {
+  EXPECT_TRUE(OpenDocument("bug_905142.pdf"));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_THAT(GetString(), testing::HasSubstr("/Length 0"));
+}
diff --git a/fpdfsdk/fpdf_searchex.cpp b/fpdfsdk/fpdf_searchex.cpp
index 9d48ceb..dbc2d2a 100644
--- a/fpdfsdk/fpdf_searchex.cpp
+++ b/fpdfsdk/fpdf_searchex.cpp
@@ -7,19 +7,20 @@
 #include "public/fpdf_searchex.h"
 
 #include "core/fpdftext/cpdf_textpage.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFText_GetCharIndexFromTextIndex(FPDF_TEXTPAGE text_page, int nTextIndex) {
   if (!text_page)
     return -1;
-  return static_cast<CPDF_TextPage*>(text_page)
-      ->CharIndexFromTextIndex(nTextIndex);
+  return CPDFTextPageFromFPDFTextPage(text_page)->CharIndexFromTextIndex(
+      nTextIndex);
 }
 
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFText_GetTextIndexFromCharIndex(FPDF_TEXTPAGE text_page, int nCharIndex) {
   if (!text_page)
     return -1;
-  return static_cast<CPDF_TextPage*>(text_page)->TextIndexFromCharIndex(
+  return CPDFTextPageFromFPDFTextPage(text_page)->TextIndexFromCharIndex(
       nCharIndex);
 }
diff --git a/fpdfsdk/fpdf_searchex_embeddertest.cpp b/fpdfsdk/fpdf_searchex_embeddertest.cpp
new file mode 100644
index 0000000..54b4799
--- /dev/null
+++ b/fpdfsdk/fpdf_searchex_embeddertest.cpp
@@ -0,0 +1,121 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_searchex.h"
+#include "testing/embedder_test.h"
+
+class FPDFSearchExEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFSearchExEmbedderTest, GetCharIndexFromTextIndex) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+
+    EXPECT_EQ(-2, FPDFText_GetCharIndexFromTextIndex(textpage.get(), -2));
+    EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(textpage.get(), -1));
+    EXPECT_EQ(0, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 0));
+    EXPECT_EQ(1, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 1));
+    EXPECT_EQ(2, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 2));
+    EXPECT_EQ(5, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 5));
+    EXPECT_EQ(10, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 10));
+    EXPECT_EQ(29, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 29));
+    EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 30));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFSearchExEmbedderTest,
+       GetCharIndexFromTextIndexWithNonPrintableChar) {
+  ASSERT_TRUE(OpenDocument("bug_1139.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+
+    EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(textpage.get(), -2));
+    EXPECT_EQ(0, FPDFText_GetCharIndexFromTextIndex(textpage.get(), -1));
+    EXPECT_EQ(1, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 0));
+    EXPECT_EQ(2, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 1));
+    EXPECT_EQ(3, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 2));
+    EXPECT_EQ(6, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 5));
+    EXPECT_EQ(11, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 10));
+    EXPECT_EQ(30, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 29));
+    EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 30));
+    EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(textpage.get(), 31));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFSearchExEmbedderTest, GetCharIndexFromTextIndexInvalid) {
+  EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(nullptr, -2));
+  EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(nullptr, -1));
+  EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(nullptr, 0));
+  EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(nullptr, 1));
+  EXPECT_EQ(-1, FPDFText_GetCharIndexFromTextIndex(nullptr, 2));
+}
+
+TEST_F(FPDFSearchExEmbedderTest, GetTextIndexFromCharIndex) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+
+    EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), -2));
+    EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), -1));
+    EXPECT_EQ(0, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 0));
+    EXPECT_EQ(1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 1));
+    EXPECT_EQ(2, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 2));
+    EXPECT_EQ(5, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 5));
+    EXPECT_EQ(10, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 10));
+    EXPECT_EQ(29, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 29));
+    EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 30));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFSearchExEmbedderTest,
+       GetTextIndexFromCharIndexWithNonPrintableChar) {
+  ASSERT_TRUE(OpenDocument("bug_1139.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+    ASSERT_TRUE(textpage);
+
+    EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), -2));
+    EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), -1));
+    EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 0));
+    EXPECT_EQ(0, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 1));
+    EXPECT_EQ(1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 2));
+    EXPECT_EQ(4, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 5));
+    EXPECT_EQ(9, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 10));
+    EXPECT_EQ(28, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 29));
+    EXPECT_EQ(29, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 30));
+    EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(textpage.get(), 31));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFSearchExEmbedderTest, GetTextIndexFromCharIndexInvalid) {
+  EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(nullptr, -2));
+  EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(nullptr, -1));
+  EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(nullptr, 0));
+  EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(nullptr, 1));
+  EXPECT_EQ(-1, FPDFText_GetTextIndexFromCharIndex(nullptr, 2));
+}
diff --git a/fpdfsdk/fpdf_structtree.cpp b/fpdfsdk/fpdf_structtree.cpp
index 676824e..cfd57a8 100644
--- a/fpdfsdk/fpdf_structtree.cpp
+++ b/fpdfsdk/fpdf_structtree.cpp
@@ -10,25 +10,17 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfdoc/cpdf_structelement.h"
 #include "core/fpdfdoc/cpdf_structtree.h"
-#include "fpdfsdk/fsdk_define.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 
 namespace {
 
-CPDF_StructTree* ToStructTree(FPDF_STRUCTTREE struct_tree) {
-  return static_cast<CPDF_StructTree*>(struct_tree);
-}
-
-CPDF_StructElement* ToStructTreeElement(FPDF_STRUCTELEMENT struct_element) {
-  return static_cast<CPDF_StructElement*>(struct_element);
-}
-
 unsigned long WideStringToBuffer(const WideString& str,
                                  void* buffer,
                                  unsigned long buflen) {
   if (str.IsEmpty())
     return 0;
 
-  ByteString encodedStr = str.UTF16LE_Encode();
+  ByteString encodedStr = str.ToUTF16LE();
   const unsigned long len = encodedStr.GetLength();
   if (buffer && len <= buflen)
     memcpy(buffer, encodedStr.c_str(), len);
@@ -42,19 +34,22 @@
   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (!pPage)
     return nullptr;
-  return CPDF_StructTree::LoadPage(pPage->m_pDocument.Get(),
-                                   pPage->m_pFormDict.Get())
-      .release();
+
+  // Caller takes onwership.
+  return FPDFStructTreeFromCPDFStructTree(
+      CPDF_StructTree::LoadPage(pPage->GetDocument(), pPage->GetDict())
+          .release());
 }
 
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_StructTree_Close(FPDF_STRUCTTREE struct_tree) {
-  std::unique_ptr<CPDF_StructTree>(ToStructTree(struct_tree));
+  std::unique_ptr<CPDF_StructTree>(
+      CPDFStructTreeFromFPDFStructTree(struct_tree));
 }
 
 FPDF_EXPORT int FPDF_CALLCONV
 FPDF_StructTree_CountChildren(FPDF_STRUCTTREE struct_tree) {
-  CPDF_StructTree* tree = ToStructTree(struct_tree);
+  CPDF_StructTree* tree = CPDFStructTreeFromFPDFStructTree(struct_tree);
   if (!tree)
     return -1;
 
@@ -64,30 +59,29 @@
 
 FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
 FPDF_StructTree_GetChildAtIndex(FPDF_STRUCTTREE struct_tree, int index) {
-  CPDF_StructTree* tree = ToStructTree(struct_tree);
+  CPDF_StructTree* tree = CPDFStructTreeFromFPDFStructTree(struct_tree);
   if (!tree || index < 0 ||
       static_cast<size_t>(index) >= tree->CountTopElements()) {
     return nullptr;
   }
-  return tree->GetTopElement(static_cast<size_t>(index));
+  return FPDFStructElementFromCPDFStructElement(
+      tree->GetTopElement(static_cast<size_t>(index)));
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDF_StructElement_GetAltText(FPDF_STRUCTELEMENT struct_element,
                               void* buffer,
                               unsigned long buflen) {
-  CPDF_StructElement* elem = ToStructTreeElement(struct_element);
-  return (elem && elem->GetDict())
-             ? WideStringToBuffer(elem->GetDict()->GetUnicodeTextFor("Alt"),
-                                  buffer, buflen)
-             : 0;
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  return elem ? WideStringToBuffer(elem->GetAltText(), buffer, buflen) : 0;
 }
 
 FPDF_EXPORT int FPDF_CALLCONV
 FPDF_StructElement_GetMarkedContentID(FPDF_STRUCTELEMENT struct_element) {
-  CPDF_StructElement* elem = ToStructTreeElement(struct_element);
-  CPDF_Object* p =
-      (elem && elem->GetDict()) ? elem->GetDict()->GetObjectFor("K") : nullptr;
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  const CPDF_Object* p = elem ? elem->GetDict()->GetObjectFor("K") : nullptr;
   return p && p->IsNumber() ? p->GetInteger() : -1;
 }
 
@@ -95,8 +89,11 @@
 FPDF_StructElement_GetType(FPDF_STRUCTELEMENT struct_element,
                            void* buffer,
                            unsigned long buflen) {
-  CPDF_StructElement* elem = ToStructTreeElement(struct_element);
-  return elem ? WideStringToBuffer(elem->GetType().UTF8Decode(), buffer, buflen)
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  return elem ? WideStringToBuffer(
+                    WideString::FromUTF8(elem->GetType().AsStringView()),
+                    buffer, buflen)
               : 0;
 }
 
@@ -104,15 +101,15 @@
 FPDF_StructElement_GetTitle(FPDF_STRUCTELEMENT struct_element,
                             void* buffer,
                             unsigned long buflen) {
-  CPDF_StructElement* elem = ToStructTreeElement(struct_element);
-  return elem
-             ? WideStringToBuffer(elem->GetTitle().UTF8Decode(), buffer, buflen)
-             : 0;
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
+  return elem ? WideStringToBuffer(elem->GetTitle(), buffer, buflen) : 0;
 }
 
 FPDF_EXPORT int FPDF_CALLCONV
 FPDF_StructElement_CountChildren(FPDF_STRUCTELEMENT struct_element) {
-  CPDF_StructElement* elem = ToStructTreeElement(struct_element);
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
   if (!elem)
     return -1;
 
@@ -123,9 +120,11 @@
 FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
 FPDF_StructElement_GetChildAtIndex(FPDF_STRUCTELEMENT struct_element,
                                    int index) {
-  CPDF_StructElement* elem = ToStructTreeElement(struct_element);
+  CPDF_StructElement* elem =
+      CPDFStructElementFromFPDFStructElement(struct_element);
   if (!elem || index < 0 || static_cast<size_t>(index) >= elem->CountKids())
     return nullptr;
 
-  return elem->GetKidIfElement(static_cast<size_t>(index));
+  return FPDFStructElementFromCPDFStructElement(
+      elem->GetKidIfElement(static_cast<size_t>(index)));
 }
diff --git a/fpdfsdk/fpdf_structtree_embeddertest.cpp b/fpdfsdk/fpdf_structtree_embeddertest.cpp
index 7ca81f7..bbaa115 100644
--- a/fpdfsdk/fpdf_structtree_embeddertest.cpp
+++ b/fpdfsdk/fpdf_structtree_embeddertest.cpp
@@ -5,116 +5,175 @@
 #include "core/fxcrt/fx_string.h"
 #include "public/fpdf_structtree.h"
 #include "testing/embedder_test.h"
-#include "testing/test_support.h"
 #include "third_party/base/optional.h"
 
-class FPDFStructTreeEmbeddertest : public EmbedderTest {};
+class FPDFStructTreeEmbedderTest : public EmbedderTest {};
 
-TEST_F(FPDFStructTreeEmbeddertest, GetAltText) {
+TEST_F(FPDFStructTreeEmbedderTest, GetAltText) {
   ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  FPDF_STRUCTTREE struct_tree = FPDF_StructTree_GetForPage(page);
-  ASSERT_TRUE(struct_tree);
-  ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree));
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
 
-  FPDF_STRUCTELEMENT element = FPDF_StructTree_GetChildAtIndex(struct_tree, -1);
-  EXPECT_EQ(nullptr, element);
-  element = FPDF_StructTree_GetChildAtIndex(struct_tree, 1);
-  EXPECT_EQ(nullptr, element);
-  element = FPDF_StructTree_GetChildAtIndex(struct_tree, 0);
-  ASSERT_NE(nullptr, element);
-  EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(element));
-  EXPECT_EQ(0U, FPDF_StructElement_GetAltText(element, nullptr, 0));
+    FPDF_STRUCTELEMENT element =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), -1);
+    EXPECT_EQ(nullptr, element);
+    element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 1);
+    EXPECT_EQ(nullptr, element);
+    element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_NE(nullptr, element);
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(element));
+    EXPECT_EQ(0U, FPDF_StructElement_GetAltText(element, nullptr, 0));
 
-  ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
-  FPDF_STRUCTELEMENT child_element =
-      FPDF_StructElement_GetChildAtIndex(element, -1);
-  EXPECT_EQ(nullptr, child_element);
-  child_element = FPDF_StructElement_GetChildAtIndex(element, 1);
-  EXPECT_EQ(nullptr, child_element);
-  child_element = FPDF_StructElement_GetChildAtIndex(element, 0);
-  ASSERT_NE(nullptr, child_element);
-  EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(child_element));
-  EXPECT_EQ(0U, FPDF_StructElement_GetAltText(child_element, nullptr, 0));
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
+    FPDF_STRUCTELEMENT child_element =
+        FPDF_StructElement_GetChildAtIndex(element, -1);
+    EXPECT_EQ(nullptr, child_element);
+    child_element = FPDF_StructElement_GetChildAtIndex(element, 1);
+    EXPECT_EQ(nullptr, child_element);
+    child_element = FPDF_StructElement_GetChildAtIndex(element, 0);
+    ASSERT_NE(nullptr, child_element);
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(child_element));
+    EXPECT_EQ(0U, FPDF_StructElement_GetAltText(child_element, nullptr, 0));
 
-  ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element));
-  FPDF_STRUCTELEMENT gchild_element =
-      FPDF_StructElement_GetChildAtIndex(child_element, -1);
-  EXPECT_EQ(nullptr, gchild_element);
-  gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 1);
-  EXPECT_EQ(nullptr, gchild_element);
-  gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 0);
-  ASSERT_NE(nullptr, gchild_element);
-  EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
-  ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, nullptr, 0));
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element));
+    FPDF_STRUCTELEMENT gchild_element =
+        FPDF_StructElement_GetChildAtIndex(child_element, -1);
+    EXPECT_EQ(nullptr, gchild_element);
+    gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 1);
+    EXPECT_EQ(nullptr, gchild_element);
+    gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 0);
+    ASSERT_NE(nullptr, gchild_element);
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
+    ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, nullptr, 0));
 
-  unsigned short buffer[12];
-  memset(buffer, 0, sizeof(buffer));
-  // Deliberately pass in a small buffer size to make sure |buffer| remains
-  // untouched.
-  ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer, 1));
-  for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
-    EXPECT_EQ(0U, buffer[i]);
+    unsigned short buffer[12];
+    memset(buffer, 0, sizeof(buffer));
+    // Deliberately pass in a small buffer size to make sure |buffer| remains
+    // untouched.
+    ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer, 1));
+    for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
+      EXPECT_EQ(0U, buffer[i]);
 
-  EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
-  ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer,
-                                               sizeof(buffer)));
-  const wchar_t kExpected[] = L"Black Image";
-  EXPECT_EQ(WideString(kExpected),
-            WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
+    EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
+    ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer,
+                                                 sizeof(buffer)));
+    const wchar_t kExpected[] = L"Black Image";
+    EXPECT_EQ(WideString(kExpected),
+              WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
 
-  ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild_element));
-  FPDF_STRUCTELEMENT ggchild_element =
-      FPDF_StructElement_GetChildAtIndex(gchild_element, 0);
-  EXPECT_EQ(nullptr, ggchild_element);
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild_element));
+    FPDF_STRUCTELEMENT ggchild_element =
+        FPDF_StructElement_GetChildAtIndex(gchild_element, 0);
+    EXPECT_EQ(nullptr, ggchild_element);
+  }
 
-  FPDF_StructTree_Close(struct_tree);
-  FPDF_ClosePage(page);
+  UnloadPage(page);
 }
 
-TEST_F(FPDFStructTreeEmbeddertest, GetMarkedContentID) {
+TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentID) {
   ASSERT_TRUE(OpenDocument("marked_content_id.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  FPDF_STRUCTTREE struct_tree = FPDF_StructTree_GetForPage(page);
-  ASSERT_TRUE(struct_tree);
-  ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree));
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
 
-  FPDF_STRUCTELEMENT element = FPDF_StructTree_GetChildAtIndex(struct_tree, 0);
-  EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentID(element));
+    FPDF_STRUCTELEMENT element =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentID(element));
+  }
 
-  FPDF_StructTree_Close(struct_tree);
-  FPDF_ClosePage(page);
+  UnloadPage(page);
 }
 
-TEST_F(FPDFStructTreeEmbeddertest, GetType) {
+TEST_F(FPDFStructTreeEmbedderTest, GetType) {
   ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  FPDF_STRUCTTREE struct_tree = FPDF_StructTree_GetForPage(page);
-  ASSERT_TRUE(struct_tree);
-  ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree));
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
 
-  FPDF_STRUCTELEMENT element = FPDF_StructTree_GetChildAtIndex(struct_tree, 0);
-  ASSERT_NE(nullptr, element);
+    FPDF_STRUCTELEMENT element =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_NE(nullptr, element);
 
-  unsigned short buffer[12];
-  memset(buffer, 0, sizeof(buffer));
-  // Deliberately pass in a small buffer size to make sure |buffer| remains
-  // untouched.
-  ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, 1));
-  for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
-    EXPECT_EQ(0U, buffer[i]);
+    // test nullptr inputs
+    unsigned short buffer[12];
+    ASSERT_EQ(0U, FPDF_StructElement_GetType(nullptr, buffer, sizeof(buffer)));
+    ASSERT_EQ(0U, FPDF_StructElement_GetType(nullptr, nullptr, 0));
+    ASSERT_EQ(18U, FPDF_StructElement_GetType(element, nullptr, 0));
 
-  ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, sizeof(buffer)));
-  const wchar_t kExpected[] = L"Document";
-  EXPECT_EQ(WideString(kExpected),
-            WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
+    memset(buffer, 0, sizeof(buffer));
+    // Deliberately pass in a small buffer size to make sure |buffer| remains
+    // untouched.
+    ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, 1));
+    for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
+      EXPECT_EQ(0U, buffer[i]);
 
-  FPDF_StructTree_Close(struct_tree);
-  FPDF_ClosePage(page);
+    ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, sizeof(buffer)));
+    const wchar_t kExpected[] = L"Document";
+    EXPECT_EQ(WideString(kExpected),
+              WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFStructTreeEmbedderTest, GetTitle) {
+  ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+    FPDF_STRUCTELEMENT element =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+    ASSERT_NE(nullptr, element);
+
+    // test nullptr inputs
+    unsigned short buffer[13];
+    ASSERT_EQ(0U, FPDF_StructElement_GetTitle(nullptr, buffer, sizeof(buffer)));
+    ASSERT_EQ(0U, FPDF_StructElement_GetTitle(nullptr, nullptr, 0));
+    ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, nullptr, 0));
+
+    memset(buffer, 0, sizeof(buffer));
+    // Deliberately pass in a small buffer size to make sure |buffer| remains
+    // untouched.
+    ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, buffer, 1));
+    for (size_t i = 0; i < FX_ArraySize(buffer); ++i)
+      EXPECT_EQ(0U, buffer[i]);
+
+    ASSERT_EQ(20U,
+              FPDF_StructElement_GetTitle(element, buffer, sizeof(buffer)));
+
+    const wchar_t kExpected[] = L"TitleText";
+    EXPECT_EQ(WideString(kExpected),
+              WideString::FromUTF16LE(buffer, FXSYS_len(kExpected)));
+
+    ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
+    FPDF_STRUCTELEMENT child_element =
+        FPDF_StructElement_GetChildAtIndex(element, 0);
+    ASSERT_NE(nullptr, element);
+
+    ASSERT_EQ(26U, FPDF_StructElement_GetTitle(child_element, buffer,
+                                               sizeof(buffer)));
+    const wchar_t kChildExpected[] = L"symbol: 100k";
+    EXPECT_EQ(WideString(kChildExpected),
+              WideString::FromUTF16LE(buffer, FXSYS_len(kChildExpected)));
+  }
+
+  UnloadPage(page);
 }
diff --git a/fpdfsdk/fpdf_sysfontinfo.cpp b/fpdfsdk/fpdf_sysfontinfo.cpp
index 97d02e8..96ceef2 100644
--- a/fpdfsdk/fpdf_sysfontinfo.cpp
+++ b/fpdfsdk/fpdf_sysfontinfo.cpp
@@ -6,16 +6,17 @@
 
 #include "public/fpdf_sysfontinfo.h"
 
+#include <stddef.h>
+
 #include <memory>
 
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/ifx_systemfontinfo.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fpdfsdk/pwl/cpwl_font_map.h"
+#include "core/fxge/systemfontinfo_iface.h"
 #include "third_party/base/ptr_util.h"
 
 static_assert(FXFONT_ANSI_CHARSET == FX_CHARSET_ANSI, "Charset must match");
@@ -30,8 +31,23 @@
               "Charset must match");
 static_assert(FXFONT_CHINESEBIG5_CHARSET == FX_CHARSET_ChineseTraditional,
               "Charset must match");
+static_assert(FXFONT_ARABIC_CHARSET == FX_CHARSET_MSWin_Arabic,
+              "Charset must match");
+static_assert(FXFONT_CYRILLIC_CHARSET == FX_CHARSET_MSWin_Cyrillic,
+              "Charset must match");
+static_assert(FXFONT_EASTERNEUROPEAN_CHARSET ==
+                  FX_CHARSET_MSWin_EasternEuropean,
+              "Charset must match");
+static_assert(offsetof(CFX_Font::CharsetFontMap, charset) ==
+                  offsetof(FPDF_CharsetFontMap, charset),
+              "CFX_Font::CharsetFontMap must be same as FPDF_CharsetFontMap");
+static_assert(offsetof(CFX_Font::CharsetFontMap, fontname) ==
+                  offsetof(FPDF_CharsetFontMap, fontname),
+              "CFX_Font::CharsetFontMap must be same as FPDF_CharsetFontMap");
+static_assert(sizeof(CFX_Font::CharsetFontMap) == sizeof(FPDF_CharsetFontMap),
+              "CFX_Font::CharsetFontMap must be same as FPDF_CharsetFontMap");
 
-class CFX_ExternalFontInfo final : public IFX_SystemFontInfo {
+class CFX_ExternalFontInfo final : public SystemFontInfoIface {
  public:
   explicit CFX_ExternalFontInfo(FPDF_SYSFONTINFO* pInfo) : m_pInfo(pInfo) {}
   ~CFX_ExternalFontInfo() override {
@@ -68,11 +84,11 @@
 
   uint32_t GetFontData(void* hFont,
                        uint32_t table,
-                       uint8_t* buffer,
-                       uint32_t size) override {
+                       pdfium::span<uint8_t> buffer) override {
     if (!m_pInfo->GetFontData)
       return 0;
-    return m_pInfo->GetFontData(m_pInfo, hFont, table, buffer, size);
+    return m_pInfo->GetFontData(m_pInfo, hFont, table, buffer.data(),
+                                buffer.size());
   }
 
   bool GetFaceName(void* hFont, ByteString* name) override {
@@ -106,10 +122,10 @@
 };
 
 FPDF_EXPORT void FPDF_CALLCONV FPDF_AddInstalledFont(void* mapper,
-                                                     const char* name,
+                                                     const char* face,
                                                      int charset) {
   CFX_FontMapper* pMapper = static_cast<CFX_FontMapper*>(mapper);
-  pMapper->AddInstalledFont(name, charset);
+  pMapper->AddInstalledFont(face, charset);
 }
 
 FPDF_EXPORT void FPDF_CALLCONV
@@ -122,11 +138,11 @@
 }
 
 FPDF_EXPORT const FPDF_CharsetFontMap* FPDF_CALLCONV FPDF_GetDefaultTTFMap() {
-  return CPWL_FontMap::defaultTTFMap;
+  return reinterpret_cast<const FPDF_CharsetFontMap*>(CFX_Font::defaultTTFMap);
 }
 
-struct FPDF_SYSFONTINFO_DEFAULT : public FPDF_SYSFONTINFO {
-  UnownedPtr<IFX_SystemFontInfo> m_pFontInfo;
+struct FPDF_SYSFONTINFO_DEFAULT final : public FPDF_SYSFONTINFO {
+  UnownedPtr<SystemFontInfoIface> m_pFontInfo;
 };
 
 static void DefaultRelease(struct _FPDF_SYSFONTINFO* pThis) {
@@ -136,7 +152,7 @@
 
 static void DefaultEnumFonts(struct _FPDF_SYSFONTINFO* pThis, void* pMapper) {
   auto* pDefault = static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pThis);
-  pDefault->m_pFontInfo->EnumFontList((CFX_FontMapper*)pMapper);
+  pDefault->m_pFontInfo->EnumFontList(static_cast<CFX_FontMapper*>(pMapper));
 }
 
 static void* DefaultMapFont(struct _FPDF_SYSFONTINFO* pThis,
@@ -162,7 +178,7 @@
                                         unsigned char* buffer,
                                         unsigned long buf_size) {
   auto* pDefault = static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pThis);
-  return pDefault->m_pFontInfo->GetFontData(hFont, table, buffer, buf_size);
+  return pDefault->m_pFontInfo->GetFontData(hFont, table, {buffer, buf_size});
 }
 
 static unsigned long DefaultGetFaceName(struct _FPDF_SYSFONTINFO* pThis,
@@ -195,8 +211,8 @@
 }
 
 FPDF_EXPORT FPDF_SYSFONTINFO* FPDF_CALLCONV FPDF_GetDefaultSystemFontInfo() {
-  std::unique_ptr<IFX_SystemFontInfo> pFontInfo =
-      IFX_SystemFontInfo::CreateDefault(nullptr);
+  std::unique_ptr<SystemFontInfoIface> pFontInfo =
+      SystemFontInfoIface::CreateDefault(nullptr);
   if (!pFontInfo)
     return nullptr;
 
@@ -216,6 +232,6 @@
 }
 
 FPDF_EXPORT void FPDF_CALLCONV
-FPDF_FreeDefaultSystemFontInfo(FPDF_SYSFONTINFO* pDefaultFontInfo) {
-  FX_Free(static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pDefaultFontInfo));
+FPDF_FreeDefaultSystemFontInfo(FPDF_SYSFONTINFO* pFontInfo) {
+  FX_Free(static_cast<FPDF_SYSFONTINFO_DEFAULT*>(pFontInfo));
 }
diff --git a/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
new file mode 100644
index 0000000..61cec04
--- /dev/null
+++ b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp
@@ -0,0 +1,155 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_sysfontinfo.h"
+
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+extern "C" {
+
+void FakeRelease(FPDF_SYSFONTINFO* pThis) {}
+void FakeEnumFonts(FPDF_SYSFONTINFO* pThis, void* pMapper) {}
+
+void* FakeMapFont(FPDF_SYSFONTINFO* pThis,
+                  int weight,
+                  FPDF_BOOL bItalic,
+                  int charset,
+                  int pitch_family,
+                  const char* face,
+                  FPDF_BOOL* bExact) {
+  // Any non-null return will do.
+  return pThis;
+}
+
+void* FakeGetFont(FPDF_SYSFONTINFO* pThis, const char* face) {
+  // Any non-null return will do.
+  return pThis;
+}
+
+unsigned long FakeGetFontData(FPDF_SYSFONTINFO* pThis,
+                              void* hFont,
+                              unsigned int table,
+                              unsigned char* buffer,
+                              unsigned long buf_size) {
+  return 0;
+}
+
+unsigned long FakeGetFaceName(FPDF_SYSFONTINFO* pThis,
+                              void* hFont,
+                              char* buffer,
+                              unsigned long buf_size) {
+  return 0;
+}
+
+int FakeGetFontCharset(FPDF_SYSFONTINFO* pThis, void* hFont) {
+  return 1;
+}
+
+void FakeDeleteFont(FPDF_SYSFONTINFO* pThis, void* hFont) {}
+
+}  // extern "C"
+
+class FPDFUnavailableSysFontInfoEmbedderTest : public EmbedderTest {
+ public:
+  FPDFUnavailableSysFontInfoEmbedderTest() = default;
+  ~FPDFUnavailableSysFontInfoEmbedderTest() override = default;
+
+  void SetUp() override {
+    EmbedderTest::SetUp();
+    font_info_.version = 1;
+    font_info_.Release = FakeRelease;
+    font_info_.EnumFonts = FakeEnumFonts;
+    font_info_.MapFont = FakeMapFont;
+    font_info_.GetFont = FakeGetFont;
+    font_info_.GetFontData = FakeGetFontData;
+    font_info_.GetFaceName = FakeGetFaceName;
+    font_info_.GetFontCharset = FakeGetFontCharset;
+    font_info_.DeleteFont = FakeDeleteFont;
+    FPDF_SetSystemFontInfo(&font_info_);
+  }
+
+  FPDF_SYSFONTINFO font_info_;
+};
+
+class FPDFSysFontInfoEmbedderTest : public EmbedderTest {
+ public:
+  FPDFSysFontInfoEmbedderTest() = default;
+  ~FPDFSysFontInfoEmbedderTest() override = default;
+
+  void SetUp() override {
+    EmbedderTest::SetUp();
+    font_info_ = FPDF_GetDefaultSystemFontInfo();
+    ASSERT_TRUE(font_info_);
+    FPDF_SetSystemFontInfo(font_info_);
+  }
+
+  void TearDown() override {
+    EmbedderTest::TearDown();
+    FPDF_FreeDefaultSystemFontInfo(font_info_);
+  }
+
+  FPDF_SYSFONTINFO* font_info_;
+};
+
+}  // namespace
+
+TEST_F(FPDFUnavailableSysFontInfoEmbedderTest, Bug_972518) {
+  ASSERT_TRUE(OpenDocument("bug_972518.pdf"));
+  ASSERT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFSysFontInfoEmbedderTest, DefaultSystemFontInfo) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Not checking the rendering because it will depend on the fonts installed.
+    ScopedFPDFBitmap bitmap = RenderPage(page);
+    ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get()));
+    ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get()));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFSysFontInfoEmbedderTest, DefaultTTFMap) {
+  static const int kAllowedCharsets[] = {
+      FXFONT_ANSI_CHARSET,        FXFONT_DEFAULT_CHARSET,
+      FXFONT_SYMBOL_CHARSET,      FXFONT_SHIFTJIS_CHARSET,
+      FXFONT_HANGEUL_CHARSET,     FXFONT_GB2312_CHARSET,
+      FXFONT_CHINESEBIG5_CHARSET, FXFONT_ARABIC_CHARSET,
+      FXFONT_CYRILLIC_CHARSET,    FXFONT_EASTERNEUROPEAN_CHARSET,
+  };
+  std::set<int> seen_charsets;
+
+  const FPDF_CharsetFontMap* cfmap = FPDF_GetDefaultTTFMap();
+  ASSERT_TRUE(cfmap);
+
+  // Stop at either end mark.
+  while (cfmap->charset != -1 && cfmap->fontname) {
+    // Only returns values described as legitimate in public header.
+    EXPECT_TRUE(pdfium::ContainsValue(kAllowedCharsets, cfmap->charset))
+        << " for " << cfmap->charset;
+
+    // Duplicates are not allowed.
+    EXPECT_TRUE(seen_charsets.insert(cfmap->charset).second)
+        << " for " << cfmap->charset;
+    ++cfmap;
+  }
+
+  // Confirm end marks only occur as a pair.
+  EXPECT_EQ(cfmap->charset, -1);
+  EXPECT_EQ(cfmap->fontname, nullptr);
+}
diff --git a/fpdfsdk/fpdf_text.cpp b/fpdfsdk/fpdf_text.cpp
new file mode 100644
index 0000000..5edbf27
--- /dev/null
+++ b/fpdfsdk/fpdf_text.cpp
@@ -0,0 +1,575 @@
+// 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 "public/fpdf_text.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fpdfapi/font/cpdf_cidfont.h"
+#include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_textobject.h"
+#include "core/fpdfdoc/cpdf_viewerpreferences.h"
+#include "core/fpdftext/cpdf_linkextract.h"
+#include "core/fpdftext/cpdf_textpage.h"
+#include "core/fpdftext/cpdf_textpagefind.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+#if defined(OS_WIN)
+#include <tchar.h>
+#endif
+
+namespace {
+
+constexpr size_t kBytesPerCharacter = sizeof(unsigned short);
+
+CPDF_TextPage* GetTextPageForValidIndex(FPDF_TEXTPAGE text_page, int index) {
+  if (!text_page || index < 0)
+    return nullptr;
+
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  return static_cast<size_t>(index) < textpage->size() ? textpage : nullptr;
+}
+
+}  // namespace
+
+FPDF_EXPORT FPDF_TEXTPAGE FPDF_CALLCONV FPDFText_LoadPage(FPDF_PAGE page) {
+  CPDF_Page* pPDFPage = CPDFPageFromFPDFPage(page);
+  if (!pPDFPage)
+    return nullptr;
+
+  CPDF_ViewerPreferences viewRef(pPDFPage->GetDocument());
+  auto textpage =
+      pdfium::MakeUnique<CPDF_TextPage>(pPDFPage, viewRef.IsDirectionR2L());
+
+  // Caller takes ownership.
+  return FPDFTextPageFromCPDFTextPage(textpage.release());
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFText_ClosePage(FPDF_TEXTPAGE text_page) {
+  // PDFium takes ownership.
+  std::unique_ptr<CPDF_TextPage> textpage_deleter(
+      CPDFTextPageFromFPDFTextPage(text_page));
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page) {
+  if (!text_page)
+    return -1;
+
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  return textpage->CountChars();
+}
+
+FPDF_EXPORT unsigned int FPDF_CALLCONV
+FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return 0;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  return charinfo.m_Unicode;
+}
+
+FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetFontSize(FPDF_TEXTPAGE text_page,
+                                                      int index) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return 0;
+
+  return textpage->GetCharFontSize(index);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFText_GetFontInfo(FPDF_TEXTPAGE text_page,
+                     int index,
+                     void* buffer,
+                     unsigned long buflen,
+                     int* flags) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return 0;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  if (!charinfo.m_pTextObj)
+    return 0;
+
+  RetainPtr<CPDF_Font> font = charinfo.m_pTextObj->GetFont();
+  if (flags)
+    *flags = font->GetFontFlags();
+
+  ByteString basefont = font->GetBaseFontName();
+  unsigned long length = basefont.GetLength() + 1;
+  if (buffer && buflen >= length)
+    memcpy(buffer, basefont.c_str(), length);
+
+  return length;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetFontWeight(FPDF_TEXTPAGE text_page,
+                                                     int index) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return -1;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  if (!charinfo.m_pTextObj)
+    return -1;
+
+  return charinfo.m_pTextObj->GetFont()->GetFontWeight();
+}
+
+FPDF_EXPORT FPDF_TEXT_RENDERMODE FPDF_CALLCONV
+FPDFText_GetTextRenderMode(FPDF_TEXTPAGE text_page, int index) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return FPDF_TEXTRENDERMODE_UNKNOWN;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  if (!charinfo.m_pTextObj)
+    return FPDF_TEXTRENDERMODE_UNKNOWN;
+
+  return static_cast<FPDF_TEXT_RENDERMODE>(
+      charinfo.m_pTextObj->GetTextRenderMode());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetFillColor(FPDF_TEXTPAGE text_page,
+                      int index,
+                      unsigned int* R,
+                      unsigned int* G,
+                      unsigned int* B,
+                      unsigned int* A) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage || !R || !G || !B || !A)
+    return false;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  if (!charinfo.m_pTextObj)
+    return false;
+
+  FX_COLORREF fill_color = charinfo.m_pTextObj->m_ColorState.GetFillColorRef();
+  *R = FXSYS_GetRValue(fill_color);
+  *G = FXSYS_GetGValue(fill_color);
+  *B = FXSYS_GetBValue(fill_color);
+  *A = FXSYS_GetUnsignedAlpha(
+      charinfo.m_pTextObj->m_GeneralState.GetFillAlpha());
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetStrokeColor(FPDF_TEXTPAGE text_page,
+                        int index,
+                        unsigned int* R,
+                        unsigned int* G,
+                        unsigned int* B,
+                        unsigned int* A) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage || !R || !G || !B || !A)
+    return false;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  if (!charinfo.m_pTextObj)
+    return false;
+
+  FX_COLORREF stroke_color =
+      charinfo.m_pTextObj->m_ColorState.GetStrokeColorRef();
+  *R = FXSYS_GetRValue(stroke_color);
+  *G = FXSYS_GetGValue(stroke_color);
+  *B = FXSYS_GetBValue(stroke_color);
+  *A = FXSYS_GetUnsignedAlpha(
+      charinfo.m_pTextObj->m_GeneralState.GetStrokeAlpha());
+  return true;
+}
+
+FPDF_EXPORT float FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,
+                                                      int index) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return -1.0f;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  // On the left is our current Matrix and on the right a generic rotation
+  // matrix for our coordinate space.
+  // | a  b  0 |    | cos(t)  -sin(t)  0 |
+  // | c  d  0 |    | sin(t)   cos(t)  0 |
+  // | e  f  1 |    |   0        0     1 |
+  // Calculate the angle of the vector
+  float angle = atan2f(charinfo.m_Matrix.c, charinfo.m_Matrix.a);
+  if (angle < 0)
+    angle = 2 * FX_PI + angle;
+
+  return angle;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetCharBox(FPDF_TEXTPAGE text_page,
+                                                        int index,
+                                                        double* left,
+                                                        double* right,
+                                                        double* bottom,
+                                                        double* top) {
+  if (!left || !right || !bottom || !top)
+    return false;
+
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return false;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  *left = charinfo.m_CharBox.left;
+  *right = charinfo.m_CharBox.right;
+  *bottom = charinfo.m_CharBox.bottom;
+  *top = charinfo.m_CharBox.top;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetLooseCharBox(FPDF_TEXTPAGE text_page, int index, FS_RECTF* rect) {
+  if (!rect)
+    return false;
+
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return false;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  float font_size = textpage->GetCharFontSize(index);
+
+  if (charinfo.m_pTextObj && !IsFloatZero(font_size)) {
+    bool is_vert_writing = charinfo.m_pTextObj->GetFont()->IsVertWriting();
+    if (is_vert_writing && charinfo.m_pTextObj->GetFont()->IsCIDFont()) {
+      CPDF_CIDFont* pCIDFont = charinfo.m_pTextObj->GetFont()->AsCIDFont();
+      uint16_t cid = pCIDFont->CIDFromCharCode(charinfo.m_CharCode);
+
+      short vx;
+      short vy;
+      pCIDFont->GetVertOrigin(cid, vx, vy);
+      double offsetx = (vx - 500) * font_size / 1000.0;
+      double offsety = vy * font_size / 1000.0;
+      short vert_width = pCIDFont->GetVertWidth(cid);
+      double height = vert_width * font_size / 1000.0;
+
+      rect->left = charinfo.m_Origin.x + offsetx;
+      rect->right = rect->left + font_size;
+      rect->bottom = charinfo.m_Origin.y + offsety;
+      rect->top = rect->bottom + height;
+      return true;
+    }
+
+    int ascent = charinfo.m_pTextObj->GetFont()->GetTypeAscent();
+    int descent = charinfo.m_pTextObj->GetFont()->GetTypeDescent();
+    if (ascent != descent) {
+      float width = charinfo.m_pTextObj->GetCharWidth(charinfo.m_CharCode);
+      float font_scale = font_size / (ascent - descent);
+
+      rect->left = charinfo.m_Origin.x;
+      rect->right = charinfo.m_Origin.x + (is_vert_writing ? -width : width);
+      rect->bottom = charinfo.m_Origin.y + descent * font_scale;
+      rect->top = charinfo.m_Origin.y + ascent * font_scale;
+      return true;
+    }
+  }
+
+  // Fallback to the tight bounds in empty text scenarios, or bad font metrics
+  *rect = FSRectFFromCFXFloatRect(charinfo.m_CharBox);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetMatrix(FPDF_TEXTPAGE text_page,
+                                                       int index,
+                                                       FS_MATRIX* matrix) {
+  if (!matrix)
+    return false;
+
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return false;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  *matrix = FSMatrixFromCFXMatrix(charinfo.m_Matrix);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetCharOrigin(FPDF_TEXTPAGE text_page,
+                       int index,
+                       double* x,
+                       double* y) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage)
+    return false;
+
+  const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index);
+  *x = charinfo.m_Origin.x;
+  *y = charinfo.m_Origin.y;
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page,
+                           double x,
+                           double y,
+                           double xTolerance,
+                           double yTolerance) {
+  if (!text_page)
+    return -3;
+
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  return textpage->GetIndexAtPos(
+      CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
+      CFX_SizeF(static_cast<float>(xTolerance),
+                static_cast<float>(yTolerance)));
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE page,
+                                               int start_index,
+                                               int char_count,
+                                               unsigned short* result) {
+  if (!page || start_index < 0 || char_count < 0 || !result)
+    return 0;
+
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(page);
+  int char_available = textpage->CountChars() - start_index;
+  if (char_available <= 0)
+    return 0;
+
+  char_count = std::min(char_count, char_available);
+  if (char_count == 0) {
+    // Writing out "", which has a character count of 1 due to the NUL.
+    *result = '\0';
+    return 1;
+  }
+
+  WideString str = textpage->GetPageText(start_index, char_count);
+
+  if (str.GetLength() > static_cast<size_t>(char_count))
+    str = str.First(static_cast<size_t>(char_count));
+
+  // UFT16LE_Encode doesn't handle surrogate pairs properly, so it is expected
+  // the number of items to stay the same.
+  ByteString byte_str = str.ToUTF16LE();
+  size_t byte_str_len = byte_str.GetLength();
+  int ret_count = byte_str_len / kBytesPerCharacter;
+
+  ASSERT(ret_count <= char_count + 1);  // +1 to account for the NUL terminator.
+  memcpy(result, byte_str.c_str(), byte_str_len);
+  return ret_count;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page,
+                                                  int start,
+                                                  int count) {
+  if (!text_page)
+    return 0;
+
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  return textpage->CountRects(start, count);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page,
+                                                     int rect_index,
+                                                     double* left,
+                                                     double* top,
+                                                     double* right,
+                                                     double* bottom) {
+  if (!text_page)
+    return false;
+
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  CFX_FloatRect rect;
+  bool result = textpage->GetRect(rect_index, &rect);
+
+  *left = rect.left;
+  *top = rect.top;
+  *right = rect.right;
+  *bottom = rect.bottom;
+  return result;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetBoundedText(FPDF_TEXTPAGE text_page,
+                                                      double left,
+                                                      double top,
+                                                      double right,
+                                                      double bottom,
+                                                      unsigned short* buffer,
+                                                      int buflen) {
+  if (!text_page)
+    return 0;
+
+  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
+  CFX_FloatRect rect((float)left, (float)bottom, (float)right, (float)top);
+  WideString str = textpage->GetTextByRect(rect);
+
+  if (buflen <= 0 || !buffer)
+    return str.GetLength();
+
+  ByteString cbUTF16Str = str.ToUTF16LE();
+  int len = cbUTF16Str.GetLength() / sizeof(unsigned short);
+  int size = buflen > len ? len : buflen;
+  memcpy(buffer, cbUTF16Str.c_str(), size * sizeof(unsigned short));
+  cbUTF16Str.ReleaseBuffer(size * sizeof(unsigned short));
+
+  return size;
+}
+
+FPDF_EXPORT FPDF_SCHHANDLE FPDF_CALLCONV
+FPDFText_FindStart(FPDF_TEXTPAGE text_page,
+                   FPDF_WIDESTRING findwhat,
+                   unsigned long flags,
+                   int start_index) {
+  if (!text_page)
+    return nullptr;
+
+  CPDF_TextPageFind::Options options;
+  options.bMatchCase = !!(flags & FPDF_MATCHCASE);
+  options.bMatchWholeWord = !!(flags & FPDF_MATCHWHOLEWORD);
+  options.bConsecutive = !!(flags & FPDF_CONSECUTIVE);
+  auto find = CPDF_TextPageFind::Create(
+      CPDFTextPageFromFPDFTextPage(text_page),
+      WideStringFromFPDFWideString(findwhat), options,
+      start_index >= 0 ? Optional<size_t>(start_index) : pdfium::nullopt);
+
+  // Caller takes ownership.
+  return FPDFSchHandleFromCPDFTextPageFind(find.release());
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindNext(FPDF_SCHHANDLE handle) {
+  if (!handle)
+    return false;
+
+  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
+  return textpageFind->FindNext();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindPrev(FPDF_SCHHANDLE handle) {
+  if (!handle)
+    return false;
+
+  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
+  return textpageFind->FindPrev();
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFText_GetSchResultIndex(FPDF_SCHHANDLE handle) {
+  if (!handle)
+    return 0;
+
+  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
+  return textpageFind->GetCurOrder();
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetSchCount(FPDF_SCHHANDLE handle) {
+  if (!handle)
+    return 0;
+
+  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
+  return textpageFind->GetMatchedCount();
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFText_FindClose(FPDF_SCHHANDLE handle) {
+  if (!handle)
+    return;
+
+  // Take ownership back from caller and destroy.
+  std::unique_ptr<CPDF_TextPageFind> textpageFind(
+      CPDFTextPageFindFromFPDFSchHandle(handle));
+}
+
+// web link
+FPDF_EXPORT FPDF_PAGELINK FPDF_CALLCONV
+FPDFLink_LoadWebLinks(FPDF_TEXTPAGE text_page) {
+  if (!text_page)
+    return nullptr;
+
+  CPDF_TextPage* pPage = CPDFTextPageFromFPDFTextPage(text_page);
+  auto pageLink = pdfium::MakeUnique<CPDF_LinkExtract>(pPage);
+  pageLink->ExtractLinks();
+
+  // Caller takes ownership.
+  return FPDFPageLinkFromCPDFLinkExtract(pageLink.release());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountWebLinks(FPDF_PAGELINK link_page) {
+  if (!link_page)
+    return 0;
+
+  CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
+  return pdfium::base::checked_cast<int>(pageLink->CountLinks());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetURL(FPDF_PAGELINK link_page,
+                                              int link_index,
+                                              unsigned short* buffer,
+                                              int buflen) {
+  WideString wsUrl(L"");
+  if (link_page && link_index >= 0) {
+    CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
+    wsUrl = pageLink->GetURL(link_index);
+  }
+  ByteString cbUTF16URL = wsUrl.ToUTF16LE();
+  int required = cbUTF16URL.GetLength() / sizeof(unsigned short);
+  if (!buffer || buflen <= 0)
+    return required;
+
+  int size = std::min(required, buflen);
+  if (size > 0) {
+    int buf_size = size * sizeof(unsigned short);
+    memcpy(buffer, cbUTF16URL.c_str(), buf_size);
+  }
+  return size;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountRects(FPDF_PAGELINK link_page,
+                                                  int link_index) {
+  if (!link_page || link_index < 0)
+    return 0;
+
+  CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
+  return pdfium::CollectionSize<int>(pageLink->GetRects(link_index));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetRect(FPDF_PAGELINK link_page,
+                                                     int link_index,
+                                                     int rect_index,
+                                                     double* left,
+                                                     double* top,
+                                                     double* right,
+                                                     double* bottom) {
+  if (!link_page || link_index < 0 || rect_index < 0)
+    return false;
+
+  CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
+  std::vector<CFX_FloatRect> rectArray = pageLink->GetRects(link_index);
+  if (rect_index >= pdfium::CollectionSize<int>(rectArray))
+    return false;
+
+  *left = rectArray[rect_index].left;
+  *right = rectArray[rect_index].right;
+  *top = rectArray[rect_index].top;
+  *bottom = rectArray[rect_index].bottom;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFLink_GetTextRange(FPDF_PAGELINK link_page,
+                      int link_index,
+                      int* start_char_index,
+                      int* char_count) {
+  if (!link_page || link_index < 0)
+    return false;
+
+  CPDF_LinkExtract* page_link = CPDFLinkExtractFromFPDFPageLink(link_page);
+  return page_link->GetTextRange(link_index, start_char_index, char_count);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFLink_CloseWebLinks(FPDF_PAGELINK link_page) {
+  delete CPDFLinkExtractFromFPDFPageLink(link_page);
+}
diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp
new file mode 100644
index 0000000..06ff4cc
--- /dev/null
+++ b/fpdfsdk/fpdf_text_embeddertest.cpp
@@ -0,0 +1,1550 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxge/fx_font.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_text.h"
+#include "public/fpdf_transformpage.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kHelloGoodbyeText[] = "Hello, world!\r\nGoodbye, world!";
+constexpr int kHelloGoodbyeTextSize = FX_ArraySize(kHelloGoodbyeText);
+
+bool check_unsigned_shorts(const char* expected,
+                           const unsigned short* actual,
+                           size_t length) {
+  if (length > strlen(expected) + 1)
+    return false;
+
+  for (size_t i = 0; i < length; ++i) {
+    if (actual[i] != static_cast<unsigned short>(expected[i]))
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+class FPDFTextEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFTextEmbedderTest, Text) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  unsigned short buffer[128];
+  memset(buffer, 0xbd, sizeof(buffer));
+
+  // Check that edge cases are handled gracefully
+  EXPECT_EQ(0, FPDFText_GetText(textpage, 0, 128, nullptr));
+  EXPECT_EQ(0, FPDFText_GetText(textpage, -1, 128, buffer));
+  EXPECT_EQ(0, FPDFText_GetText(textpage, 0, -1, buffer));
+  EXPECT_EQ(1, FPDFText_GetText(textpage, 0, 0, buffer));
+  EXPECT_EQ(0, buffer[0]);
+
+  // Keep going and check the next case.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(2, FPDFText_GetText(textpage, 0, 1, buffer));
+  EXPECT_EQ(kHelloGoodbyeText[0], buffer[0]);
+  EXPECT_EQ(0, buffer[1]);
+
+  // Check includes the terminating NUL that is provided.
+  int num_chars = FPDFText_GetText(textpage, 0, 128, buffer);
+  ASSERT_EQ(kHelloGoodbyeTextSize, num_chars);
+  EXPECT_TRUE(
+      check_unsigned_shorts(kHelloGoodbyeText, buffer, kHelloGoodbyeTextSize));
+
+  // Count does not include the terminating NUL in the string literal.
+  EXPECT_EQ(kHelloGoodbyeTextSize - 1, FPDFText_CountChars(textpage));
+  for (size_t i = 0; i < kHelloGoodbyeTextSize - 1; ++i) {
+    EXPECT_EQ(static_cast<unsigned int>(kHelloGoodbyeText[i]),
+              FPDFText_GetUnicode(textpage, i))
+        << " at " << i;
+  }
+
+  // Extracting using a buffer that will be completely filled. Small buffer is
+  // 12 elements long, since it will need 2 locations per displayed character in
+  // the expected string, plus 2 more for the terminating character.
+  static const char kSmallExpected[] = "Hello";
+  unsigned short small_buffer[12];
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(6, FPDFText_GetText(textpage, 0, 5, small_buffer));
+  EXPECT_TRUE(check_unsigned_shorts(kSmallExpected, small_buffer,
+                                    sizeof(kSmallExpected)));
+
+  EXPECT_EQ(12.0, FPDFText_GetFontSize(textpage, 0));
+  EXPECT_EQ(16.0, FPDFText_GetFontSize(textpage, 15));
+
+  double left = 1.0;
+  double right = 2.0;
+  double bottom = 3.0;
+  double top = 4.0;
+  EXPECT_FALSE(FPDFText_GetCharBox(nullptr, 4, &left, &right, &bottom, &top));
+  EXPECT_DOUBLE_EQ(1.0, left);
+  EXPECT_DOUBLE_EQ(2.0, right);
+  EXPECT_DOUBLE_EQ(3.0, bottom);
+  EXPECT_DOUBLE_EQ(4.0, top);
+  EXPECT_FALSE(FPDFText_GetCharBox(textpage, -1, &left, &right, &bottom, &top));
+  EXPECT_DOUBLE_EQ(1.0, left);
+  EXPECT_DOUBLE_EQ(2.0, right);
+  EXPECT_DOUBLE_EQ(3.0, bottom);
+  EXPECT_DOUBLE_EQ(4.0, top);
+  EXPECT_FALSE(FPDFText_GetCharBox(textpage, 55, &left, &right, &bottom, &top));
+  EXPECT_DOUBLE_EQ(1.0, left);
+  EXPECT_DOUBLE_EQ(2.0, right);
+  EXPECT_DOUBLE_EQ(3.0, bottom);
+  EXPECT_DOUBLE_EQ(4.0, top);
+  EXPECT_FALSE(
+      FPDFText_GetCharBox(textpage, 4, nullptr, &right, &bottom, &top));
+  EXPECT_FALSE(FPDFText_GetCharBox(textpage, 4, &left, nullptr, &bottom, &top));
+  EXPECT_FALSE(FPDFText_GetCharBox(textpage, 4, &left, &right, nullptr, &top));
+  EXPECT_FALSE(
+      FPDFText_GetCharBox(textpage, 4, &left, &right, &bottom, nullptr));
+  EXPECT_FALSE(
+      FPDFText_GetCharBox(textpage, 4, nullptr, nullptr, nullptr, nullptr));
+
+  EXPECT_TRUE(FPDFText_GetCharBox(textpage, 4, &left, &right, &bottom, &top));
+  EXPECT_NEAR(41.071, left, 0.001);
+  EXPECT_NEAR(46.243, right, 0.001);
+  EXPECT_NEAR(49.844, bottom, 0.001);
+  EXPECT_NEAR(55.520, top, 0.001);
+
+  FS_RECTF rect = {4.0f, 1.0f, 3.0f, 2.0f};
+  EXPECT_FALSE(FPDFText_GetLooseCharBox(nullptr, 4, &rect));
+  EXPECT_FLOAT_EQ(4.0f, rect.left);
+  EXPECT_FLOAT_EQ(3.0f, rect.right);
+  EXPECT_FLOAT_EQ(2.0f, rect.bottom);
+  EXPECT_FLOAT_EQ(1.0f, rect.top);
+  EXPECT_FALSE(FPDFText_GetLooseCharBox(textpage, -1, &rect));
+  EXPECT_FLOAT_EQ(4.0f, rect.left);
+  EXPECT_FLOAT_EQ(3.0f, rect.right);
+  EXPECT_FLOAT_EQ(2.0f, rect.bottom);
+  EXPECT_FLOAT_EQ(1.0f, rect.top);
+  EXPECT_FALSE(FPDFText_GetLooseCharBox(textpage, 55, &rect));
+  EXPECT_FLOAT_EQ(4.0f, rect.left);
+  EXPECT_FLOAT_EQ(3.0f, rect.right);
+  EXPECT_FLOAT_EQ(2.0f, rect.bottom);
+  EXPECT_FLOAT_EQ(1.0f, rect.top);
+  EXPECT_FALSE(FPDFText_GetLooseCharBox(textpage, 4, nullptr));
+
+  EXPECT_TRUE(FPDFText_GetLooseCharBox(textpage, 4, &rect));
+  EXPECT_FLOAT_EQ(40.664001f, rect.left);
+  EXPECT_FLOAT_EQ(46.664001f, rect.right);
+  EXPECT_FLOAT_EQ(47.667271f, rect.bottom);
+  EXPECT_FLOAT_EQ(59.667271f, rect.top);
+
+  double x = 0.0;
+  double y = 0.0;
+  EXPECT_TRUE(FPDFText_GetCharOrigin(textpage, 4, &x, &y));
+  EXPECT_NEAR(40.664, x, 0.001);
+  EXPECT_NEAR(50.000, y, 0.001);
+
+  EXPECT_EQ(4, FPDFText_GetCharIndexAtPos(textpage, 42.0, 50.0, 1.0, 1.0));
+  EXPECT_EQ(-1, FPDFText_GetCharIndexAtPos(textpage, 0.0, 0.0, 1.0, 1.0));
+  EXPECT_EQ(-1, FPDFText_GetCharIndexAtPos(textpage, 199.0, 199.0, 1.0, 1.0));
+
+  // Test out of range indicies.
+  EXPECT_EQ(-1,
+            FPDFText_GetCharIndexAtPos(textpage, 42.0, 10000000.0, 1.0, 1.0));
+  EXPECT_EQ(-1, FPDFText_GetCharIndexAtPos(textpage, -1.0, 50.0, 1.0, 1.0));
+
+  // Count does not include the terminating NUL in the string literal.
+  EXPECT_EQ(2, FPDFText_CountRects(textpage, 0, kHelloGoodbyeTextSize - 1));
+
+  left = 0.0;
+  right = 0.0;
+  bottom = 0.0;
+  top = 0.0;
+  EXPECT_TRUE(FPDFText_GetRect(textpage, 1, &left, &top, &right, &bottom));
+  EXPECT_NEAR(20.847, left, 0.001);
+  EXPECT_NEAR(135.167, right, 0.001);
+  EXPECT_NEAR(96.655, bottom, 0.001);
+  EXPECT_NEAR(116.000, top, 0.001);
+
+  // Test out of range indicies set outputs to (0.0, 0.0, 0.0, 0.0).
+  left = -1.0;
+  right = -1.0;
+  bottom = -1.0;
+  top = -1.0;
+  EXPECT_FALSE(FPDFText_GetRect(textpage, -1, &left, &top, &right, &bottom));
+  EXPECT_EQ(0.0, left);
+  EXPECT_EQ(0.0, right);
+  EXPECT_EQ(0.0, bottom);
+  EXPECT_EQ(0.0, top);
+
+  left = -2.0;
+  right = -2.0;
+  bottom = -2.0;
+  top = -2.0;
+  EXPECT_FALSE(FPDFText_GetRect(textpage, 2, &left, &top, &right, &bottom));
+  EXPECT_EQ(0.0, left);
+  EXPECT_EQ(0.0, right);
+  EXPECT_EQ(0.0, bottom);
+  EXPECT_EQ(0.0, top);
+
+  EXPECT_EQ(
+      9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, nullptr, 0));
+
+  // Extract starting at character 4 as above.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(
+      1, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, buffer, 1));
+  EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 1));
+  EXPECT_EQ(0xbdbd, buffer[1]);
+
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(
+      9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, buffer, 9));
+  EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 9));
+  EXPECT_EQ(0xbdbd, buffer[9]);
+
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(10, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0,
+                                        buffer, 128));
+  EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 9));
+  EXPECT_EQ(0u, buffer[9]);
+  EXPECT_EQ(0xbdbd, buffer[10]);
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, TextVertical) {
+  ASSERT_TRUE(OpenDocument("vertical_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  EXPECT_EQ(12.0, FPDFText_GetFontSize(textpage, 0));
+
+  double x = 0.0;
+  double y = 0.0;
+  EXPECT_TRUE(FPDFText_GetCharOrigin(textpage, 1, &x, &y));
+  EXPECT_NEAR(6.664, x, 0.001);
+  EXPECT_NEAR(171.508, y, 0.001);
+
+  EXPECT_TRUE(FPDFText_GetCharOrigin(textpage, 2, &x, &y));
+  EXPECT_NEAR(8.668, x, 0.001);
+  EXPECT_NEAR(160.492, y, 0.001);
+
+  FS_RECTF rect;
+  EXPECT_TRUE(FPDFText_GetLooseCharBox(textpage, 1, &rect));
+  EXPECT_NEAR(4, rect.left, 0.001);
+  EXPECT_NEAR(16, rect.right, 0.001);
+  EXPECT_NEAR(178.984, rect.bottom, 0.001);
+  EXPECT_NEAR(170.308, rect.top, 0.001);
+
+  EXPECT_TRUE(FPDFText_GetLooseCharBox(textpage, 2, &rect));
+  EXPECT_NEAR(4, rect.left, 0.001);
+  EXPECT_NEAR(16, rect.right, 0.001);
+  EXPECT_NEAR(170.308, rect.bottom, 0.001);
+  EXPECT_NEAR(159.292, rect.top, 0.001);
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, TextSearch) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  ScopedFPDFWideString nope = GetFPDFWideString(L"nope");
+  ScopedFPDFWideString world = GetFPDFWideString(L"world");
+  ScopedFPDFWideString world_caps = GetFPDFWideString(L"WORLD");
+  ScopedFPDFWideString world_substr = GetFPDFWideString(L"orld");
+
+  {
+    // No occurrences of "nope" in test page.
+    ScopedFPDFTextFind search(FPDFText_FindStart(textpage, nope.get(), 0, 0));
+    EXPECT_TRUE(search);
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+
+    // Advancing finds nothing.
+    EXPECT_FALSE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+
+    // Retreating finds nothing.
+    EXPECT_FALSE(FPDFText_FindPrev(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+  }
+
+  {
+    // Two occurrences of "world" in test page.
+    ScopedFPDFTextFind search(FPDFText_FindStart(textpage, world.get(), 0, 2));
+    EXPECT_TRUE(search);
+
+    // Remains not found until advanced.
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+
+    // First occurrence of "world" in this test page.
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(7, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(5, FPDFText_GetSchCount(search.get()));
+
+    // Last occurrence of "world" in this test page.
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(24, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(5, FPDFText_GetSchCount(search.get()));
+
+    // Found position unchanged when fails to advance.
+    EXPECT_FALSE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(24, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(5, FPDFText_GetSchCount(search.get()));
+
+    // Back to first occurrence.
+    EXPECT_TRUE(FPDFText_FindPrev(search.get()));
+    EXPECT_EQ(7, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(5, FPDFText_GetSchCount(search.get()));
+
+    // Found position unchanged when fails to retreat.
+    EXPECT_FALSE(FPDFText_FindPrev(search.get()));
+    EXPECT_EQ(7, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(5, FPDFText_GetSchCount(search.get()));
+  }
+
+  {
+    // Exact search unaffected by case sensitiity and whole word flags.
+    ScopedFPDFTextFind search(FPDFText_FindStart(
+        textpage, world.get(), FPDF_MATCHCASE | FPDF_MATCHWHOLEWORD, 0));
+    EXPECT_TRUE(search);
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(7, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(5, FPDFText_GetSchCount(search.get()));
+  }
+
+  {
+    // Default is case-insensitive, so matching agaist caps works.
+    ScopedFPDFTextFind search(
+        FPDFText_FindStart(textpage, world_caps.get(), 0, 0));
+    EXPECT_TRUE(search);
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(7, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(5, FPDFText_GetSchCount(search.get()));
+  }
+
+  {
+    // But can be made case sensitive, in which case this fails.
+    ScopedFPDFTextFind search(
+        FPDFText_FindStart(textpage, world_caps.get(), FPDF_MATCHCASE, 0));
+    EXPECT_FALSE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+  }
+
+  {
+    // Default is match anywhere within word, so matching substring works.
+    ScopedFPDFTextFind search(
+        FPDFText_FindStart(textpage, world_substr.get(), 0, 0));
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(8, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+  }
+
+  {
+    // But can be made to mach word boundaries, in which case this fails.
+    ScopedFPDFTextFind search(FPDFText_FindStart(textpage, world_substr.get(),
+                                                 FPDF_MATCHWHOLEWORD, 0));
+    EXPECT_FALSE(FPDFText_FindNext(search.get()));
+    // TODO(tsepez): investigate strange index/count values in this state.
+  }
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, TextSearchConsecutive) {
+  ASSERT_TRUE(OpenDocument("find_text_consecutive.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  ScopedFPDFWideString aaaa = GetFPDFWideString(L"aaaa");
+
+  {
+    // Search for "aaaa" yields 2 results in "aaaaaaaaaa".
+    ScopedFPDFTextFind search(FPDFText_FindStart(textpage, aaaa.get(), 0, 0));
+    EXPECT_TRUE(search);
+
+    // Remains not found until advanced.
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+
+    // First occurrence of "aaaa" in this test page.
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+
+    // Last occurrence of "aaaa" in this test page.
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+
+    // Found position unchanged when fails to advance.
+    EXPECT_FALSE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+
+    // Back to first occurrence.
+    EXPECT_TRUE(FPDFText_FindPrev(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+
+    // Found position unchanged when fails to retreat.
+    EXPECT_FALSE(FPDFText_FindPrev(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+  }
+
+  {
+    // Search for "aaaa" yields 7 results in "aaaaaaaaaa", when searching with
+    // FPDF_CONSECUTIVE.
+    ScopedFPDFTextFind search(
+        FPDFText_FindStart(textpage, aaaa.get(), FPDF_CONSECUTIVE, 0));
+    EXPECT_TRUE(search);
+
+    // Remains not found until advanced.
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+
+    // Find consecutive occurrences of "aaaa" in this test page:
+    for (int i = 0; i < 7; ++i) {
+      EXPECT_TRUE(FPDFText_FindNext(search.get()));
+      EXPECT_EQ(i, FPDFText_GetSchResultIndex(search.get()));
+      EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+    }
+
+    // Found position unchanged when fails to advance.
+    EXPECT_FALSE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(6, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+
+    for (int i = 5; i >= 0; --i) {
+      EXPECT_TRUE(FPDFText_FindPrev(search.get()));
+      EXPECT_EQ(i, FPDFText_GetSchResultIndex(search.get()));
+      EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+    }
+
+    // Found position unchanged when fails to retreat.
+    EXPECT_FALSE(FPDFText_FindPrev(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(4, FPDFText_GetSchCount(search.get()));
+  }
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+// Fails on Windows. https://crbug.com/pdfium/1370
+#if defined(OS_WIN)
+#define MAYBE_TextSearchLatinExtended DISABLED_TextSearchLatinExtended
+#else
+#define MAYBE_TextSearchLatinExtended TextSearchLatinExtended
+#endif
+TEST_F(FPDFTextEmbedderTest, MAYBE_TextSearchLatinExtended) {
+  ASSERT_TRUE(OpenDocument("latin_extended.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  // Upper/lowercase 'a' with breve.
+  constexpr FPDF_WCHAR kNeedleUpper[] = {0x0102, 0x0000};
+  constexpr FPDF_WCHAR kNeedleLower[] = {0x0103, 0x0000};
+
+  for (const auto* needle : {kNeedleUpper, kNeedleLower}) {
+    ScopedFPDFTextFind search(FPDFText_FindStart(textpage, needle, 0, 0));
+    EXPECT_TRUE(search);
+    EXPECT_EQ(0, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(0, FPDFText_GetSchCount(search.get()));
+
+    // Should find 2 results at position 21/22, both with length 1.
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(2, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(1, FPDFText_GetSchCount(search.get()));
+    EXPECT_TRUE(FPDFText_FindNext(search.get()));
+    EXPECT_EQ(3, FPDFText_GetSchResultIndex(search.get()));
+    EXPECT_EQ(1, FPDFText_GetSchCount(search.get()));
+    // And no more than 2 results.
+    EXPECT_FALSE(FPDFText_FindNext(search.get()));
+  }
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+// Test that the page has characters despite a bad stream length.
+TEST_F(FPDFTextEmbedderTest, StreamLengthPastEndOfFile) {
+  ASSERT_TRUE(OpenDocument("bug_57.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+  EXPECT_EQ(13, FPDFText_CountChars(textpage));
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, WebLinks) {
+  ASSERT_TRUE(OpenDocument("weblinks.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  {
+    ScopedFPDFPageLink pagelink(FPDFLink_LoadWebLinks(textpage));
+    EXPECT_TRUE(pagelink);
+
+    // Page contains two HTTP-style URLs.
+    EXPECT_EQ(2, FPDFLink_CountWebLinks(pagelink.get()));
+
+    // Only a terminating NUL required for bogus links.
+    EXPECT_EQ(1, FPDFLink_GetURL(pagelink.get(), 2, nullptr, 0));
+    EXPECT_EQ(1, FPDFLink_GetURL(pagelink.get(), 1400, nullptr, 0));
+    EXPECT_EQ(1, FPDFLink_GetURL(pagelink.get(), -1, nullptr, 0));
+  }
+
+  FPDF_PAGELINK pagelink = FPDFLink_LoadWebLinks(textpage);
+  EXPECT_TRUE(pagelink);
+
+  // Query the number of characters required for each link (incl NUL).
+  EXPECT_EQ(25, FPDFLink_GetURL(pagelink, 0, nullptr, 0));
+  EXPECT_EQ(26, FPDFLink_GetURL(pagelink, 1, nullptr, 0));
+
+  static const char expected_url[] = "http://example.com?q=foo";
+  static const size_t expected_len = sizeof(expected_url);
+  unsigned short buffer[128];
+
+  // Retrieve a link with too small a buffer.  Buffer will not be
+  // NUL-terminated, but must not be modified past indicated length,
+  // so pre-fill with a pattern to check write bounds.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(1, FPDFLink_GetURL(pagelink, 0, buffer, 1));
+  EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, 1));
+  EXPECT_EQ(0xbdbd, buffer[1]);
+
+  // Check buffer that doesn't have space for a terminating NUL.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(static_cast<int>(expected_len - 1),
+            FPDFLink_GetURL(pagelink, 0, buffer, expected_len - 1));
+  EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, expected_len - 1));
+  EXPECT_EQ(0xbdbd, buffer[expected_len - 1]);
+
+  // Retreive link with exactly-sized buffer.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(static_cast<int>(expected_len),
+            FPDFLink_GetURL(pagelink, 0, buffer, expected_len));
+  EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, expected_len));
+  EXPECT_EQ(0u, buffer[expected_len - 1]);
+  EXPECT_EQ(0xbdbd, buffer[expected_len]);
+
+  // Retreive link with ample-sized-buffer.
+  memset(buffer, 0xbd, sizeof(buffer));
+  EXPECT_EQ(static_cast<int>(expected_len),
+            FPDFLink_GetURL(pagelink, 0, buffer, 128));
+  EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, expected_len));
+  EXPECT_EQ(0u, buffer[expected_len - 1]);
+  EXPECT_EQ(0xbdbd, buffer[expected_len]);
+
+  // Each link rendered in a single rect in this test page.
+  EXPECT_EQ(1, FPDFLink_CountRects(pagelink, 0));
+  EXPECT_EQ(1, FPDFLink_CountRects(pagelink, 1));
+
+  // Each link rendered in a single rect in this test page.
+  EXPECT_EQ(0, FPDFLink_CountRects(pagelink, -1));
+  EXPECT_EQ(0, FPDFLink_CountRects(pagelink, 2));
+  EXPECT_EQ(0, FPDFLink_CountRects(pagelink, 10000));
+
+  // Check boundary of valid link index with valid rect index.
+  double left = 0.0;
+  double right = 0.0;
+  double top = 0.0;
+  double bottom = 0.0;
+  EXPECT_TRUE(FPDFLink_GetRect(pagelink, 0, 0, &left, &top, &right, &bottom));
+  EXPECT_NEAR(50.791, left, 0.001);
+  EXPECT_NEAR(187.963, right, 0.001);
+  EXPECT_NEAR(97.624, bottom, 0.001);
+  EXPECT_NEAR(108.736, top, 0.001);
+
+  // Check that valid link with invalid rect index leaves parameters unchanged.
+  left = -1.0;
+  right = -1.0;
+  top = -1.0;
+  bottom = -1.0;
+  EXPECT_FALSE(FPDFLink_GetRect(pagelink, 0, 1, &left, &top, &right, &bottom));
+  EXPECT_EQ(-1.0, left);
+  EXPECT_EQ(-1.0, right);
+  EXPECT_EQ(-1.0, bottom);
+  EXPECT_EQ(-1.0, top);
+
+  // Check that invalid link index leaves parameters unchanged.
+  left = -2.0;
+  right = -2.0;
+  top = -2.0;
+  bottom = -2.0;
+  EXPECT_FALSE(FPDFLink_GetRect(pagelink, -1, 0, &left, &top, &right, &bottom));
+  EXPECT_EQ(-2.0, left);
+  EXPECT_EQ(-2.0, right);
+  EXPECT_EQ(-2.0, bottom);
+  EXPECT_EQ(-2.0, top);
+
+  FPDFLink_CloseWebLinks(pagelink);
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, WebLinksAcrossLines) {
+  ASSERT_TRUE(OpenDocument("weblinks_across_lines.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  FPDF_PAGELINK pagelink = FPDFLink_LoadWebLinks(textpage);
+  EXPECT_TRUE(pagelink);
+
+  static const char* const kExpectedUrls[] = {
+      "http://example.com",           // from "http://www.example.com?\r\nfoo"
+      "http://example.com/",          // from "http://www.example.com/\r\nfoo"
+      "http://example.com/test-foo",  // from "http://example.com/test-\r\nfoo"
+      "http://abc.com/test-foo",      // from "http://abc.com/test-\r\n\r\nfoo"
+      // Next two links from "http://www.example.com/\r\nhttp://www.abc.com/"
+      "http://example.com/",
+      "http://www.abc.com",
+  };
+  static const int kNumLinks = static_cast<int>(FX_ArraySize(kExpectedUrls));
+
+  EXPECT_EQ(kNumLinks, FPDFLink_CountWebLinks(pagelink));
+
+  unsigned short buffer[128];
+  for (int i = 0; i < kNumLinks; i++) {
+    const size_t expected_len = strlen(kExpectedUrls[i]) + 1;
+    memset(buffer, 0, sizeof(buffer));
+    EXPECT_EQ(static_cast<int>(expected_len),
+              FPDFLink_GetURL(pagelink, i, nullptr, 0));
+    EXPECT_EQ(static_cast<int>(expected_len),
+              FPDFLink_GetURL(pagelink, i, buffer, FX_ArraySize(buffer)));
+    EXPECT_TRUE(check_unsigned_shorts(kExpectedUrls[i], buffer, expected_len));
+  }
+
+  FPDFLink_CloseWebLinks(pagelink);
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, WebLinksAcrossLinesBug) {
+  ASSERT_TRUE(OpenDocument("bug_650.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  FPDF_PAGELINK pagelink = FPDFLink_LoadWebLinks(textpage);
+  EXPECT_TRUE(pagelink);
+
+  EXPECT_EQ(2, FPDFLink_CountWebLinks(pagelink));
+  unsigned short buffer[128] = {0};
+  static const char kExpectedUrl[] =
+      "http://tutorial45.com/learn-autocad-basics-day-166/";
+  static const int kUrlSize = static_cast<int>(sizeof(kExpectedUrl));
+
+  EXPECT_EQ(kUrlSize, FPDFLink_GetURL(pagelink, 1, nullptr, 0));
+  EXPECT_EQ(kUrlSize,
+            FPDFLink_GetURL(pagelink, 1, buffer, FX_ArraySize(buffer)));
+  EXPECT_TRUE(check_unsigned_shorts(kExpectedUrl, buffer, kUrlSize));
+
+  FPDFLink_CloseWebLinks(pagelink);
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, WebLinksCharRanges) {
+  ASSERT_TRUE(OpenDocument("weblinks.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  FPDF_PAGELINK page_link = FPDFLink_LoadWebLinks(text_page);
+  EXPECT_TRUE(page_link);
+
+  // Test for char indices of a valid link
+  int start_char_index;
+  int char_count;
+  ASSERT_TRUE(
+      FPDFLink_GetTextRange(page_link, 0, &start_char_index, &char_count));
+  EXPECT_EQ(35, start_char_index);
+  EXPECT_EQ(24, char_count);
+
+  // Test for char indices of an invalid link
+  start_char_index = -10;
+  char_count = -8;
+  ASSERT_FALSE(
+      FPDFLink_GetTextRange(page_link, 6, &start_char_index, &char_count));
+  EXPECT_EQ(start_char_index, -10);
+  EXPECT_EQ(char_count, -8);
+
+  // Test for pagelink = nullptr
+  start_char_index = -10;
+  char_count = -8;
+  ASSERT_FALSE(
+      FPDFLink_GetTextRange(nullptr, 0, &start_char_index, &char_count));
+  EXPECT_EQ(start_char_index, -10);
+  EXPECT_EQ(char_count, -8);
+
+  // Test for link_index < 0
+  start_char_index = -10;
+  char_count = -8;
+  ASSERT_FALSE(
+      FPDFLink_GetTextRange(page_link, -4, &start_char_index, &char_count));
+  EXPECT_EQ(start_char_index, -10);
+  EXPECT_EQ(char_count, -8);
+
+  FPDFLink_CloseWebLinks(page_link);
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, AnnotLinks) {
+  ASSERT_TRUE(OpenDocument("link_annots.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Get link count via checking annotation subtype
+  int annot_count = FPDFPage_GetAnnotCount(page);
+  ASSERT_EQ(8, annot_count);
+  int annot_subtype_link_count = 0;
+  for (int i = 0; i < annot_count; ++i) {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
+    if (FPDFAnnot_GetSubtype(annot.get()) == FPDF_ANNOT_LINK) {
+      ++annot_subtype_link_count;
+    }
+  }
+  EXPECT_EQ(4, annot_subtype_link_count);
+
+  // Validate that FPDFLink_Enumerate() returns same number of links
+  int start_pos = 0;
+  FPDF_LINK link_annot;
+  int link_count = 0;
+  while (FPDFLink_Enumerate(page, &start_pos, &link_annot)) {
+    ASSERT_TRUE(link_annot);
+    if (start_pos == 1 || start_pos == 2) {
+      // First two links point to first and second page within the document
+      // respectively
+      FPDF_DEST link_dest = FPDFLink_GetDest(document(), link_annot);
+      EXPECT_TRUE(link_dest);
+      EXPECT_EQ(start_pos - 1,
+                FPDFDest_GetDestPageIndex(document(), link_dest));
+    } else if (start_pos == 3) {  // points to PDF Spec URL
+      FS_RECTF link_rect;
+      EXPECT_TRUE(FPDFLink_GetAnnotRect(link_annot, &link_rect));
+      EXPECT_NEAR(66.0, link_rect.left, 0.001);
+      EXPECT_NEAR(544.0, link_rect.top, 0.001);
+      EXPECT_NEAR(196.0, link_rect.right, 0.001);
+      EXPECT_NEAR(529.0, link_rect.bottom, 0.001);
+    } else if (start_pos == 4) {  // this link has quad points
+      int quad_point_count = FPDFLink_CountQuadPoints(link_annot);
+      EXPECT_EQ(1, quad_point_count);
+      FS_QUADPOINTSF quad_points;
+      EXPECT_TRUE(FPDFLink_GetQuadPoints(link_annot, 0, &quad_points));
+      EXPECT_NEAR(83.0, quad_points.x1, 0.001);
+      EXPECT_NEAR(453.0, quad_points.y1, 0.001);
+      EXPECT_NEAR(178.0, quad_points.x2, 0.001);
+      EXPECT_NEAR(453.0, quad_points.y2, 0.001);
+      EXPECT_NEAR(83.0, quad_points.x3, 0.001);
+      EXPECT_NEAR(440.0, quad_points.y3, 0.001);
+      EXPECT_NEAR(178.0, quad_points.x4, 0.001);
+      EXPECT_NEAR(440.0, quad_points.y4, 0.001);
+      // AnnotRect is same as quad points for this link
+      FS_RECTF link_rect;
+      EXPECT_TRUE(FPDFLink_GetAnnotRect(link_annot, &link_rect));
+      EXPECT_NEAR(link_rect.left, quad_points.x1, 0.001);
+      EXPECT_NEAR(link_rect.top, quad_points.y1, 0.001);
+      EXPECT_NEAR(link_rect.right, quad_points.x4, 0.001);
+      EXPECT_NEAR(link_rect.bottom, quad_points.y4, 0.001);
+    }
+    ++link_count;
+  }
+  EXPECT_EQ(annot_subtype_link_count, link_count);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetFontSize) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  const double kExpectedFontsSizes[] = {12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+                                        12, 12, 12, 1,  1,  16, 16, 16, 16, 16,
+                                        16, 16, 16, 16, 16, 16, 16, 16, 16, 16};
+
+  int count = FPDFText_CountChars(textpage);
+  ASSERT_EQ(FX_ArraySize(kExpectedFontsSizes), static_cast<size_t>(count));
+  for (int i = 0; i < count; ++i)
+    EXPECT_EQ(kExpectedFontsSizes[i], FPDFText_GetFontSize(textpage, i)) << i;
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetFontInfo) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+  std::vector<char> font_name;
+  size_t num_chars1 = strlen("Hello, world!");
+  const char kExpectedFontName1[] = "Times-Roman";
+
+  for (size_t i = 0; i < num_chars1; i++) {
+    int flags = -1;
+    unsigned long length =
+        FPDFText_GetFontInfo(textpage, i, nullptr, 0, &flags);
+    static constexpr unsigned long expected_length = sizeof(kExpectedFontName1);
+    ASSERT_EQ(expected_length, length);
+    EXPECT_EQ(FXFONT_NONSYMBOLIC, flags);
+    font_name.resize(length);
+    std::fill(font_name.begin(), font_name.end(), 'a');
+    flags = -1;
+    EXPECT_EQ(expected_length,
+              FPDFText_GetFontInfo(textpage, i, font_name.data(),
+                                   font_name.size(), &flags));
+    EXPECT_STREQ(kExpectedFontName1, font_name.data());
+    EXPECT_EQ(FXFONT_NONSYMBOLIC, flags);
+  }
+  // If the size of the buffer is not large enough, the buffer should remain
+  // unchanged.
+  font_name.pop_back();
+  std::fill(font_name.begin(), font_name.end(), 'a');
+  EXPECT_EQ(sizeof(kExpectedFontName1),
+            FPDFText_GetFontInfo(textpage, 0, font_name.data(),
+                                 font_name.size(), nullptr));
+  for (char a : font_name)
+    EXPECT_EQ('a', a);
+
+  // The text is "Hello, world!\r\nGoodbye, world!", so the next two characters
+  // do not have any font information.
+  EXPECT_EQ(0u, FPDFText_GetFontInfo(textpage, num_chars1, font_name.data(),
+                                     font_name.size(), nullptr));
+  EXPECT_EQ(0u, FPDFText_GetFontInfo(textpage, num_chars1 + 1, font_name.data(),
+                                     font_name.size(), nullptr));
+
+  size_t num_chars2 = strlen("Goodbye, world!");
+  const char kExpectedFontName2[] = "Helvetica";
+  for (size_t i = num_chars1 + 2; i < num_chars1 + num_chars2 + 2; i++) {
+    int flags = -1;
+    unsigned long length =
+        FPDFText_GetFontInfo(textpage, i, nullptr, 0, &flags);
+    static constexpr unsigned long expected_length = sizeof(kExpectedFontName2);
+    ASSERT_EQ(expected_length, length);
+    EXPECT_EQ(FXFONT_NONSYMBOLIC, flags);
+    font_name.resize(length);
+    std::fill(font_name.begin(), font_name.end(), 'a');
+    flags = -1;
+    EXPECT_EQ(expected_length,
+              FPDFText_GetFontInfo(textpage, i, font_name.data(),
+                                   font_name.size(), &flags));
+    EXPECT_STREQ(kExpectedFontName2, font_name.data());
+    EXPECT_EQ(FXFONT_NONSYMBOLIC, flags);
+  }
+
+  // Now try some out of bounds indices and null pointers to make sure we do not
+  // crash.
+  // No textpage.
+  EXPECT_EQ(0u, FPDFText_GetFontInfo(nullptr, 0, font_name.data(),
+                                     font_name.size(), nullptr));
+  // No buffer.
+  EXPECT_EQ(sizeof(kExpectedFontName1),
+            FPDFText_GetFontInfo(textpage, 0, nullptr, 0, nullptr));
+  // Negative index.
+  EXPECT_EQ(0u, FPDFText_GetFontInfo(textpage, -1, font_name.data(),
+                                     font_name.size(), nullptr));
+  // Out of bounds index.
+  EXPECT_EQ(0u, FPDFText_GetFontInfo(textpage, 1000, font_name.data(),
+                                     font_name.size(), nullptr));
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, ToUnicode) {
+  ASSERT_TRUE(OpenDocument("bug_583.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  ASSERT_EQ(1, FPDFText_CountChars(textpage));
+  EXPECT_EQ(0U, FPDFText_GetUnicode(textpage, 0));
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, Bug_921) {
+  ASSERT_TRUE(OpenDocument("bug_921.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  static constexpr unsigned int kData[] = {
+      1095, 1077, 1083, 1086, 1074, 1077, 1095, 1077, 1089, 1082, 1086, 1077,
+      32,   1089, 1090, 1088, 1072, 1076, 1072, 1085, 1080, 1077, 46,   32};
+  static constexpr int kStartIndex = 238;
+
+  ASSERT_EQ(268, FPDFText_CountChars(textpage));
+  for (size_t i = 0; i < FX_ArraySize(kData); ++i)
+    EXPECT_EQ(kData[i], FPDFText_GetUnicode(textpage, kStartIndex + i));
+
+  unsigned short buffer[FX_ArraySize(kData) + 1];
+  memset(buffer, 0xbd, sizeof(buffer));
+  int count =
+      FPDFText_GetText(textpage, kStartIndex, FX_ArraySize(kData), buffer);
+  ASSERT_GT(count, 0);
+  ASSERT_EQ(FX_ArraySize(kData) + 1, static_cast<size_t>(count));
+  for (size_t i = 0; i < FX_ArraySize(kData); ++i)
+    EXPECT_EQ(kData[i], buffer[i]);
+  EXPECT_EQ(0, buffer[FX_ArraySize(kData)]);
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetTextWithHyphen) {
+  ASSERT_TRUE(OpenDocument("bug_781804.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  // Check that soft hyphens are not included
+  // Expecting 'Veritaserum', except there is a \uFFFE where the hyphen was in
+  // the original text. This is a weird thing that Adobe does, which we
+  // replicate.
+  constexpr unsigned short soft_expected[] = {
+      0x0056, 0x0065, 0x0072, 0x0069, 0x0074, 0x0061, 0xfffe,
+      0x0073, 0x0065, 0x0072, 0x0075, 0x006D, 0x0000};
+  {
+    constexpr int count = FX_ArraySize(soft_expected) - 1;
+    unsigned short buffer[FX_ArraySize(soft_expected)];
+    memset(buffer, 0, sizeof(buffer));
+
+    EXPECT_EQ(count + 1, FPDFText_GetText(textpage, 0, count, buffer));
+    for (int i = 0; i < count; i++)
+      EXPECT_EQ(soft_expected[i], buffer[i]);
+  }
+
+  // Check that hard hyphens are included
+  {
+    // There isn't the \0 in the actual doc, but there is a \r\n, so need to
+    // add 1 to get aligned.
+    constexpr size_t offset = FX_ArraySize(soft_expected) + 1;
+    // Expecting 'User-\r\ngenerated', the - is a unicode character, so cannnot
+    // store in a char[].
+    constexpr unsigned short hard_expected[] = {
+        0x0055, 0x0073, 0x0065, 0x0072, 0x2010, 0x000d, 0x000a, 0x0067, 0x0065,
+        0x006e, 0x0065, 0x0072, 0x0061, 0x0074, 0x0065, 0x0064, 0x0000};
+    constexpr int count = FX_ArraySize(hard_expected) - 1;
+    unsigned short buffer[FX_ArraySize(hard_expected)];
+
+    EXPECT_EQ(count + 1, FPDFText_GetText(textpage, offset, count, buffer));
+    for (int i = 0; i < count; i++)
+      EXPECT_EQ(hard_expected[i], buffer[i]);
+  }
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, bug_782596) {
+  // If there is a regression in this test, it will only fail under ASAN
+  ASSERT_TRUE(OpenDocument("bug_782596.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, ControlCharacters) {
+  ASSERT_TRUE(OpenDocument("control_characters.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  // Should not include the control characters in the output
+  unsigned short buffer[128];
+  memset(buffer, 0xbd, sizeof(buffer));
+  int num_chars = FPDFText_GetText(textpage, 0, 128, buffer);
+  ASSERT_EQ(kHelloGoodbyeTextSize, num_chars);
+  EXPECT_TRUE(
+      check_unsigned_shorts(kHelloGoodbyeText, buffer, kHelloGoodbyeTextSize));
+
+  // Attempting to get a chunk of text after the control characters
+  static const char expected_substring[] = "Goodbye, world!";
+  // Offset is the length of 'Hello, world!\r\n' + 2 control characters in the
+  // original stream
+  static const int offset = 17;
+  memset(buffer, 0xbd, sizeof(buffer));
+  num_chars = FPDFText_GetText(textpage, offset, 128, buffer);
+
+  ASSERT_GE(num_chars, 0);
+  EXPECT_EQ(sizeof(expected_substring), static_cast<size_t>(num_chars));
+  EXPECT_TRUE(check_unsigned_shorts(expected_substring, buffer,
+                                    sizeof(expected_substring)));
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+// Testing that hyphen makers (0x0002) are replacing hard hyphens when
+// the word contains non-ASCII characters.
+TEST_F(FPDFTextEmbedderTest, bug_1029) {
+  ASSERT_TRUE(OpenDocument("bug_1029.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  constexpr int page_range_offset = 171;
+  constexpr int page_range_length = 56;
+
+  // This text is:
+  // 'METADATA table. When the split has committed, it noti' followed
+  // by a 'soft hyphen' (0x0002) and then 'fi'.
+  //
+  // The original text has a fi ligature, but that is broken up into
+  // two characters when the PDF is processed.
+  constexpr unsigned int expected[] = {
+      0x004d, 0x0045, 0x0054, 0x0041, 0x0044, 0x0041, 0x0054, 0x0041,
+      0x0020, 0x0074, 0x0061, 0x0062, 0x006c, 0x0065, 0x002e, 0x0020,
+      0x0057, 0x0068, 0x0065, 0x006e, 0x0020, 0x0074, 0x0068, 0x0065,
+      0x0020, 0x0073, 0x0070, 0x006c, 0x0069, 0x0074, 0x0020, 0x0068,
+      0x0061, 0x0073, 0x0020, 0x0063, 0x006f, 0x006d, 0x006d, 0x0069,
+      0x0074, 0x0074, 0x0065, 0x0064, 0x002c, 0x0020, 0x0069, 0x0074,
+      0x0020, 0x006e, 0x006f, 0x0074, 0x0069, 0x0002, 0x0066, 0x0069};
+  static_assert(page_range_length == FX_ArraySize(expected),
+                "Expected should be the same size as the range being "
+                "extracted from page.");
+  EXPECT_LT(page_range_offset + page_range_length,
+            FPDFText_CountChars(textpage));
+
+  for (int i = 0; i < page_range_length; ++i) {
+    EXPECT_EQ(expected[i],
+              FPDFText_GetUnicode(textpage, page_range_offset + i));
+  }
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, CountRects) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
+  ASSERT_TRUE(textpage);
+
+  // Sanity check hello_world.pdf.
+  // |num_chars| check includes the terminating NUL that is provided.
+  {
+    unsigned short buffer[128];
+    int num_chars = FPDFText_GetText(textpage, 0, 128, buffer);
+    ASSERT_EQ(kHelloGoodbyeTextSize, num_chars);
+    EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText, buffer,
+                                      kHelloGoodbyeTextSize));
+  }
+
+  // Now test FPDFText_CountRects().
+  static const int kHelloWorldEnd = strlen("Hello, world!");
+  static const int kGoodbyeWorldStart = kHelloWorldEnd + 2;  // "\r\n"
+  for (int start = 0; start < kHelloWorldEnd; ++start) {
+    // Always grab some part of "hello world" and some part of "goodbye world"
+    // Since -1 means "all".
+    EXPECT_EQ(2, FPDFText_CountRects(textpage, start, -1));
+
+    // No characters always means 0 rects.
+    EXPECT_EQ(0, FPDFText_CountRects(textpage, start, 0));
+
+    // 1 character stays within "hello world"
+    EXPECT_EQ(1, FPDFText_CountRects(textpage, start, 1));
+
+    // When |start| is 0, Having |kGoodbyeWorldStart| char count does not reach
+    // "goodbye world".
+    int expected_value = start ? 2 : 1;
+    EXPECT_EQ(expected_value,
+              FPDFText_CountRects(textpage, start, kGoodbyeWorldStart));
+
+    // Extremely large character count will always return 2 rects because
+    // |start| starts inside "hello world".
+    EXPECT_EQ(2, FPDFText_CountRects(textpage, start, 500));
+  }
+
+  // Now test negative counts.
+  for (int start = 0; start < kHelloWorldEnd; ++start) {
+    EXPECT_EQ(2, FPDFText_CountRects(textpage, start, -100));
+    EXPECT_EQ(2, FPDFText_CountRects(textpage, start, -2));
+  }
+
+  // Now test larger start values.
+  const int kExpectedLength = strlen(kHelloGoodbyeText);
+  for (int start = kGoodbyeWorldStart + 1; start < kExpectedLength; ++start) {
+    EXPECT_EQ(1, FPDFText_CountRects(textpage, start, -1));
+    EXPECT_EQ(0, FPDFText_CountRects(textpage, start, 0));
+    EXPECT_EQ(1, FPDFText_CountRects(textpage, start, 1));
+    EXPECT_EQ(1, FPDFText_CountRects(textpage, start, 2));
+    EXPECT_EQ(1, FPDFText_CountRects(textpage, start, 500));
+  }
+
+  // Now test start values that starts beyond the end of the text.
+  for (int start = kExpectedLength; start < 100; ++start) {
+    EXPECT_EQ(0, FPDFText_CountRects(textpage, start, -1));
+    EXPECT_EQ(0, FPDFText_CountRects(textpage, start, 0));
+    EXPECT_EQ(0, FPDFText_CountRects(textpage, start, 1));
+    EXPECT_EQ(0, FPDFText_CountRects(textpage, start, 2));
+    EXPECT_EQ(0, FPDFText_CountRects(textpage, start, 500));
+  }
+
+  FPDFText_ClosePage(textpage);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetText) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  EXPECT_EQ(2, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(text_object);
+
+  // Positive testing.
+  constexpr char kHelloText[] = "Hello, world!";
+  // Return value includes the terminating NUL that is provided.
+  constexpr unsigned long kHelloUTF16Size = FX_ArraySize(kHelloText) * 2;
+  constexpr wchar_t kHelloWideText[] = L"Hello, world!";
+  unsigned long size = FPDFTextObj_GetText(text_object, text_page, nullptr, 0);
+  ASSERT_EQ(kHelloUTF16Size, size);
+
+  std::vector<unsigned short> buffer(size);
+  ASSERT_EQ(size,
+            FPDFTextObj_GetText(text_object, text_page, buffer.data(), size));
+  ASSERT_EQ(kHelloWideText, GetPlatformWString(buffer.data()));
+
+  // Negative testing.
+  ASSERT_EQ(0U, FPDFTextObj_GetText(nullptr, text_page, nullptr, 0));
+  ASSERT_EQ(0U, FPDFTextObj_GetText(text_object, nullptr, nullptr, 0));
+  ASSERT_EQ(0U, FPDFTextObj_GetText(nullptr, nullptr, nullptr, 0));
+
+  // Buffer is too small, ensure it's not modified.
+  buffer.resize(2);
+  buffer[0] = 'x';
+  buffer[1] = '\0';
+  size =
+      FPDFTextObj_GetText(text_object, text_page, buffer.data(), buffer.size());
+  ASSERT_EQ(kHelloUTF16Size, size);
+  ASSERT_EQ('x', buffer[0]);
+  ASSERT_EQ('\0', buffer[1]);
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, CroppedText) {
+  static constexpr int kPageCount = 4;
+  static constexpr FS_RECTF kBoxes[kPageCount] = {
+      {50.0f, 150.0f, 150.0f, 50.0f},
+      {50.0f, 150.0f, 150.0f, 50.0f},
+      {60.0f, 150.0f, 150.0f, 60.0f},
+      {60.0f, 150.0f, 150.0f, 60.0f},
+  };
+  static constexpr const char* kExpectedText[kPageCount] = {
+      " world!\r\ndbye, world!",
+      " world!\r\ndbye, world!",
+      "bye, world!",
+      "bye, world!",
+  };
+
+  ASSERT_TRUE(OpenDocument("cropped_text.pdf"));
+  ASSERT_EQ(kPageCount, FPDF_GetPageCount(document()));
+
+  for (int i = 0; i < kPageCount; ++i) {
+    FPDF_PAGE page = LoadPage(i);
+    ASSERT_TRUE(page);
+
+    FS_RECTF box;
+    EXPECT_TRUE(FPDF_GetPageBoundingBox(page, &box));
+    EXPECT_EQ(kBoxes[i].left, box.left);
+    EXPECT_EQ(kBoxes[i].top, box.top);
+    EXPECT_EQ(kBoxes[i].right, box.right);
+    EXPECT_EQ(kBoxes[i].bottom, box.bottom);
+
+    {
+      ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+      ASSERT_TRUE(textpage);
+
+      unsigned short buffer[128];
+      memset(buffer, 0xbd, sizeof(buffer));
+      int num_chars = FPDFText_GetText(textpage.get(), 0, 128, buffer);
+      ASSERT_EQ(kHelloGoodbyeTextSize, num_chars);
+      EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText, buffer,
+                                        kHelloGoodbyeTextSize));
+
+      int expected_char_count = strlen(kExpectedText[i]);
+      ASSERT_EQ(expected_char_count,
+                FPDFText_GetBoundedText(textpage.get(), box.left, box.top,
+                                        box.right, box.bottom, nullptr, 0));
+
+      memset(buffer, 0xbd, sizeof(buffer));
+      ASSERT_EQ(expected_char_count + 1,
+                FPDFText_GetBoundedText(textpage.get(), box.left, box.top,
+                                        box.right, box.bottom, buffer, 128));
+      EXPECT_TRUE(
+          check_unsigned_shorts(kExpectedText[i], buffer, expected_char_count));
+    }
+
+    UnloadPage(page);
+  }
+}
+
+TEST_F(FPDFTextEmbedderTest, Bug_1139) {
+  ASSERT_TRUE(OpenDocument("bug_1139.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  // -1 for CountChars not including the \0, but +1 for the extra control
+  // character.
+  EXPECT_EQ(kHelloGoodbyeTextSize, FPDFText_CountChars(text_page));
+
+  // There is an extra control character at the beginning of the string, but it
+  // should not appear in the output nor prevent extracting the text.
+  unsigned short buffer[128];
+  int num_chars = FPDFText_GetText(text_page, 0, 128, buffer);
+  ASSERT_EQ(kHelloGoodbyeTextSize, num_chars);
+  EXPECT_TRUE(
+      check_unsigned_shorts(kHelloGoodbyeText, buffer, kHelloGoodbyeTextSize));
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, Bug_642) {
+  ASSERT_TRUE(OpenDocument("bug_642.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  {
+    ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
+    ASSERT_TRUE(text_page);
+
+    constexpr char kText[] = "ABCD";
+    constexpr size_t kTextSize = FX_ArraySize(kText);
+    // -1 for CountChars not including the \0
+    EXPECT_EQ(static_cast<int>(kTextSize) - 1,
+              FPDFText_CountChars(text_page.get()));
+
+    unsigned short buffer[kTextSize];
+    int num_chars =
+        FPDFText_GetText(text_page.get(), 0, FX_ArraySize(buffer) - 1, buffer);
+    ASSERT_EQ(static_cast<int>(kTextSize), num_chars);
+    EXPECT_TRUE(check_unsigned_shorts(kText, buffer, kTextSize));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetCharAngle) {
+  ASSERT_TRUE(OpenDocument("rotated_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  static constexpr int kSubstringsSize[] = {FX_ArraySize("Hello,"),
+                                            FX_ArraySize(" world!\r\n"),
+                                            FX_ArraySize("Goodbye,")};
+
+  // -1 for CountChars not including the \0, but +1 for the extra control
+  // character.
+  EXPECT_EQ(kHelloGoodbyeTextSize, FPDFText_CountChars(text_page));
+
+  EXPECT_FLOAT_EQ(-1.0f, FPDFText_GetCharAngle(nullptr, 0));
+  EXPECT_FLOAT_EQ(-1.0f, FPDFText_GetCharAngle(text_page, -1));
+  EXPECT_FLOAT_EQ(-1.0f,
+                  FPDFText_GetCharAngle(text_page, kHelloGoodbyeTextSize + 1));
+
+  // Test GetCharAngle for every quadrant
+  EXPECT_NEAR(FX_PI / 4.0, FPDFText_GetCharAngle(text_page, 0), 0.001);
+  EXPECT_NEAR(3 * FX_PI / 4.0,
+              FPDFText_GetCharAngle(text_page, kSubstringsSize[0]), 0.001);
+  EXPECT_NEAR(
+      5 * FX_PI / 4.0,
+      FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1]),
+      0.001);
+  EXPECT_NEAR(
+      7 * FX_PI / 4.0,
+      FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1] +
+                                           kSubstringsSize[2]),
+      0.001);
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetFontWeight) {
+  ASSERT_TRUE(OpenDocument("font_weight.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  EXPECT_EQ(2, FPDFText_CountChars(text_page));
+
+  EXPECT_EQ(-1, FPDFText_GetFontWeight(nullptr, 0));
+  EXPECT_EQ(-1, FPDFText_GetFontWeight(text_page, -1));
+  EXPECT_EQ(-1, FPDFText_GetFontWeight(text_page, 314));
+
+  // The font used for this text only specifies /StemV (80); the weight value
+  // that is returned should be calculated from that (80*5 == 400).
+  EXPECT_EQ(400, FPDFText_GetFontWeight(text_page, 0));
+
+  // Using a /StemV value of 82, the estimate comes out to 410, even though
+  // /FontWeight is 400.
+  // TODO(crbug.com/pdfium/1420): Fix this the return value here.
+  EXPECT_EQ(410, FPDFText_GetFontWeight(text_page, 1));
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetTextRenderMode) {
+  EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  ASSERT_EQ(12, FPDFText_CountChars(text_page));
+
+  ASSERT_EQ(FPDF_TEXTRENDERMODE_UNKNOWN,
+            FPDFText_GetTextRenderMode(nullptr, 0));
+  ASSERT_EQ(FPDF_TEXTRENDERMODE_UNKNOWN,
+            FPDFText_GetTextRenderMode(text_page, -1));
+  ASSERT_EQ(FPDF_TEXTRENDERMODE_UNKNOWN,
+            FPDFText_GetTextRenderMode(text_page, 314));
+
+  ASSERT_EQ(FPDF_TEXTRENDERMODE_FILL, FPDFText_GetTextRenderMode(text_page, 0));
+
+  ASSERT_EQ(FPDF_TEXTRENDERMODE_STROKE,
+            FPDFText_GetTextRenderMode(text_page, 7));
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetFillColor) {
+  ASSERT_TRUE(OpenDocument("text_color.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  ASSERT_EQ(1, FPDFText_CountChars(text_page));
+
+  ASSERT_FALSE(
+      FPDFText_GetFillColor(nullptr, 0, nullptr, nullptr, nullptr, nullptr));
+  ASSERT_FALSE(
+      FPDFText_GetFillColor(text_page, -1, nullptr, nullptr, nullptr, nullptr));
+  ASSERT_FALSE(FPDFText_GetFillColor(text_page, 314, nullptr, nullptr, nullptr,
+                                     nullptr));
+  ASSERT_FALSE(
+      FPDFText_GetFillColor(text_page, 0, nullptr, nullptr, nullptr, nullptr));
+
+  unsigned int r;
+  unsigned int g;
+  unsigned int b;
+  unsigned int a;
+  ASSERT_TRUE(FPDFText_GetFillColor(text_page, 0, &r, &g, &b, &a));
+  ASSERT_EQ(0xffu, r);
+  ASSERT_EQ(0u, g);
+  ASSERT_EQ(0u, b);
+  ASSERT_EQ(0xffu, a);
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetStrokeColor) {
+  ASSERT_TRUE(OpenDocument("text_color.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  ASSERT_EQ(1, FPDFText_CountChars(text_page));
+
+  ASSERT_FALSE(
+      FPDFText_GetStrokeColor(nullptr, 0, nullptr, nullptr, nullptr, nullptr));
+  ASSERT_FALSE(FPDFText_GetStrokeColor(text_page, -1, nullptr, nullptr, nullptr,
+                                       nullptr));
+  ASSERT_FALSE(FPDFText_GetStrokeColor(text_page, 314, nullptr, nullptr,
+                                       nullptr, nullptr));
+  ASSERT_FALSE(FPDFText_GetStrokeColor(text_page, 0, nullptr, nullptr, nullptr,
+                                       nullptr));
+
+  unsigned int r;
+  unsigned int g;
+  unsigned int b;
+  unsigned int a;
+  ASSERT_TRUE(FPDFText_GetStrokeColor(text_page, 0, &r, &g, &b, &a));
+  ASSERT_EQ(0u, r);
+  ASSERT_EQ(0xffu, g);
+  ASSERT_EQ(0u, b);
+  ASSERT_EQ(0xffu, a);
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetMatrix) {
+  constexpr char kExpectedText[] = "A1\r\nA2\r\nA3";
+  constexpr size_t kExpectedTextSize = FX_ArraySize(kExpectedText);
+  constexpr FS_MATRIX kExpectedMatrices[] = {
+      {12.0f, 0.0f, 0.0f, 10.0f, 66.0f, 90.0f},
+      {12.0f, 0.0f, 0.0f, 10.0f, 66.0f, 90.0f},
+      {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
+      {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
+      {12.0f, 0.0f, 0.0f, 10.0f, 38.0f, 60.0f},
+      {12.0f, 0.0f, 0.0f, 10.0f, 38.0f, 60.0f},
+      {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
+      {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
+      {1.0f, 0.0f, 0.0f, 0.833333, 60.0f, 130.0f},
+      {1.0f, 0.0f, 0.0f, 0.833333, 60.0f, 130.0f},
+  };
+  constexpr size_t kExpectedCount = FX_ArraySize(kExpectedMatrices);
+  static_assert(kExpectedCount + 1 == kExpectedTextSize,
+                "Bad expected matrix size");
+
+  // For a size 12 letter 'A'.
+  constexpr double kExpectedCharWidth = 8.436;
+  constexpr double kExpectedCharHeight = 6.77;
+
+  ASSERT_TRUE(OpenDocument("font_matrix.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
+    ASSERT_TRUE(text_page);
+    ASSERT_EQ(static_cast<int>(kExpectedCount),
+              FPDFText_CountChars(text_page.get()));
+
+    {
+      // Check the characters.
+      unsigned short buffer[kExpectedTextSize];
+      ASSERT_EQ(static_cast<int>(kExpectedTextSize),
+                FPDFText_GetText(text_page.get(), 0, kExpectedCount, buffer));
+      EXPECT_TRUE(
+          check_unsigned_shorts(kExpectedText, buffer, kExpectedTextSize));
+    }
+
+    {
+      // Check the character box size.
+      double left;
+      double right;
+      double bottom;
+      double top;
+      ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 0, &left, &right,
+                                      &bottom, &top));
+      EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
+      EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
+      ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 4, &left, &right,
+                                      &bottom, &top));
+      EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
+      EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
+      ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 8, &left, &right,
+                                      &bottom, &top));
+      EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001);
+      EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001);
+    }
+
+    // Check the character matrix.
+    FS_MATRIX matrix;
+    for (size_t i = 0; i < kExpectedCount; ++i) {
+      ASSERT_TRUE(FPDFText_GetMatrix(text_page.get(), i, &matrix)) << i;
+      EXPECT_FLOAT_EQ(kExpectedMatrices[i].a, matrix.a) << i;
+      EXPECT_FLOAT_EQ(kExpectedMatrices[i].b, matrix.b) << i;
+      EXPECT_FLOAT_EQ(kExpectedMatrices[i].c, matrix.c) << i;
+      EXPECT_FLOAT_EQ(kExpectedMatrices[i].d, matrix.d) << i;
+      EXPECT_FLOAT_EQ(kExpectedMatrices[i].e, matrix.e) << i;
+      EXPECT_FLOAT_EQ(kExpectedMatrices[i].f, matrix.f) << i;
+    }
+
+    // Check bad parameters.
+    EXPECT_FALSE(FPDFText_GetMatrix(nullptr, 0, &matrix));
+    EXPECT_FALSE(FPDFText_GetMatrix(text_page.get(), 10, &matrix));
+    EXPECT_FALSE(FPDFText_GetMatrix(text_page.get(), -1, &matrix));
+    EXPECT_FALSE(FPDFText_GetMatrix(text_page.get(), 0, nullptr));
+  }
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_thumbnail.cpp b/fpdfsdk/fpdf_thumbnail.cpp
new file mode 100644
index 0000000..5c22c8c
--- /dev/null
+++ b/fpdfsdk/fpdf_thumbnail.cpp
@@ -0,0 +1,76 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_thumbnail.h"
+
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_dib.h"
+#include "core/fpdfapi/page/cpdf_page.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/fxge/dib/cfx_dibitmap.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/fpdfview.h"
+
+namespace {
+
+const CPDF_Stream* CPDFStreamForThumbnailFromPage(FPDF_PAGE page) {
+  const CPDF_Page* p_page = CPDFPageFromFPDFPage(page);
+  if (!p_page)
+    return nullptr;
+
+  const CPDF_Dictionary* page_dict = p_page->GetDict();
+  if (!page_dict->KeyExist("Type"))
+    return nullptr;
+
+  return page_dict->GetStreamFor("Thumb");
+}
+
+}  // namespace
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFPage_GetDecodedThumbnailData(FPDF_PAGE page,
+                                 void* buffer,
+                                 unsigned long buflen) {
+  const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page);
+  if (!thumb_stream)
+    return 0u;
+
+  return DecodeStreamMaybeCopyAndReturnLength(thumb_stream, buffer, buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFPage_GetRawThumbnailData(FPDF_PAGE page,
+                             void* buffer,
+                             unsigned long buflen) {
+  const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page);
+  if (!thumb_stream)
+    return 0u;
+
+  return GetRawStreamMaybeCopyAndReturnLength(thumb_stream, buffer, buflen);
+}
+
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
+FPDFPage_GetThumbnailAsBitmap(FPDF_PAGE page) {
+  const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page);
+  if (!thumb_stream)
+    return nullptr;
+
+  const CPDF_Page* p_page = CPDFPageFromFPDFPage(page);
+
+  auto p_source = pdfium::MakeRetain<CPDF_DIB>();
+  const CPDF_DIB::LoadState start_status = p_source->StartLoadDIBBase(
+      p_page->GetDocument(), thumb_stream, false, nullptr,
+      p_page->m_pPageResources.Get(), false, 0, false);
+  if (start_status == CPDF_DIB::LoadState::kFail)
+    return nullptr;
+
+  auto thumb_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!thumb_bitmap->Copy(p_source))
+    return nullptr;
+
+  return FPDFBitmapFromCFXDIBitmap(thumb_bitmap.Leak());
+}
diff --git a/fpdfsdk/fpdf_thumbnail_embeddertest.cpp b/fpdfsdk/fpdf_thumbnail_embeddertest.cpp
new file mode 100644
index 0000000..101e1e4
--- /dev/null
+++ b/fpdfsdk/fpdf_thumbnail_embeddertest.cpp
@@ -0,0 +1,294 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "public/fpdf_thumbnail.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/utils/hash.h"
+
+class FPDFThumbnailEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFThumbnailEmbedderTest, GetDecodedThumbnailDataFromPageWithFilters) {
+  ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf"));
+
+  {
+    const char kHashedDecodedData[] = "7902d0be831c9024960f4ebd5d7df1f7";
+    const unsigned long kExpectedSize = 1138u;
+
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    unsigned long length_bytes =
+        FPDFPage_GetDecodedThumbnailData(page, nullptr, 0);
+    ASSERT_EQ(kExpectedSize, length_bytes);
+    std::vector<uint8_t> thumb_buf(length_bytes);
+
+    EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData(
+                                 page, thumb_buf.data(), length_bytes));
+    EXPECT_EQ(kHashedDecodedData,
+              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+
+    UnloadPage(page);
+  }
+
+  {
+    const char kHashedDecodedData[] = "e81123a573378ba1ea80461d25cc41f6";
+    const unsigned long kExpectedSize = 1110u;
+
+    FPDF_PAGE page = LoadPage(1);
+    ASSERT_TRUE(page);
+
+    unsigned long length_bytes =
+        FPDFPage_GetDecodedThumbnailData(page, nullptr, 0);
+    ASSERT_EQ(kExpectedSize, length_bytes);
+    std::vector<uint8_t> thumb_buf(length_bytes);
+
+    EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData(
+                                 page, thumb_buf.data(), length_bytes));
+    EXPECT_EQ(kHashedDecodedData,
+              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+
+    UnloadPage(page);
+  }
+}
+
+TEST_F(FPDFThumbnailEmbedderTest,
+       GetDecodedThumbnailDataFromPageWithNoFilters) {
+  ASSERT_TRUE(OpenDocument("thumbnail_with_no_filters.pdf"));
+
+  const char kHashedDecodedData[] = "b5696e586382b3373741f8a1d651cab0";
+  const unsigned long kExpectedSize = 301u;
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  unsigned long length_bytes =
+      FPDFPage_GetDecodedThumbnailData(page, nullptr, 0);
+  ASSERT_EQ(kExpectedSize, length_bytes);
+  std::vector<uint8_t> thumb_buf(length_bytes);
+
+  EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData(
+                               page, thumb_buf.data(), length_bytes));
+  EXPECT_EQ(kHashedDecodedData,
+            GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest,
+       GetDecodedThumbnailDataFromPageWithNoThumbnails) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_EQ(0u, FPDFPage_GetDecodedThumbnailData(page, nullptr, 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetDecodedThumbnailDataFromPageNullPage) {
+  EXPECT_EQ(0u, FPDFPage_GetDecodedThumbnailData(nullptr, nullptr, 0));
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithFilters) {
+  ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf"));
+
+  {
+    const char kHashedRawData[] = "f6a8e8db01cccd52abb91ea433a17373";
+    const unsigned long kExpectedSize = 1851u;
+
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    unsigned long length_bytes = FPDFPage_GetRawThumbnailData(page, nullptr, 0);
+    ASSERT_EQ(kExpectedSize, length_bytes);
+    std::vector<uint8_t> thumb_buf(length_bytes);
+
+    EXPECT_EQ(kExpectedSize, FPDFPage_GetRawThumbnailData(
+                                 page, thumb_buf.data(), length_bytes));
+    EXPECT_EQ(kHashedRawData,
+              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+
+    UnloadPage(page);
+  }
+
+  {
+    const char kHashedRawData[] = "c7558a461d5ecfb1d4757218b473afc0";
+    const unsigned long kExpectedSize = 1792u;
+
+    FPDF_PAGE page = LoadPage(1);
+    ASSERT_TRUE(page);
+
+    unsigned long length_bytes = FPDFPage_GetRawThumbnailData(page, nullptr, 0);
+    ASSERT_EQ(kExpectedSize, length_bytes);
+    std::vector<uint8_t> thumb_buf(length_bytes);
+
+    EXPECT_EQ(kExpectedSize, FPDFPage_GetRawThumbnailData(
+                                 page, thumb_buf.data(), length_bytes));
+    EXPECT_EQ(kHashedRawData,
+              GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+
+    UnloadPage(page);
+  }
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithNoFilters) {
+  ASSERT_TRUE(OpenDocument("thumbnail_with_no_filters.pdf"));
+
+  const char kHashedRawData[] = "b5696e586382b3373741f8a1d651cab0";
+  const unsigned long kExpectedSize = 301u;
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  unsigned long length_bytes = FPDFPage_GetRawThumbnailData(page, nullptr, 0);
+  ASSERT_EQ(kExpectedSize, length_bytes);
+  std::vector<uint8_t> thumb_buf(length_bytes);
+
+  EXPECT_EQ(kExpectedSize,
+            FPDFPage_GetRawThumbnailData(page, thumb_buf.data(), length_bytes));
+  EXPECT_EQ(kHashedRawData, GenerateMD5Base16(thumb_buf.data(), kExpectedSize));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithNoThumbnails) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_EQ(0u, FPDFPage_GetRawThumbnailData(page, nullptr, 0));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageNullPage) {
+  EXPECT_EQ(0u, FPDFPage_GetRawThumbnailData(nullptr, nullptr, 0));
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailAsBitmapFromPage) {
+  ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf"));
+
+  {
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+
+    EXPECT_EQ(50, FPDFBitmap_GetWidth(thumb_bitmap.get()));
+    EXPECT_EQ(50, FPDFBitmap_GetHeight(thumb_bitmap.get()));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(thumb_bitmap.get()));
+    CompareBitmap(thumb_bitmap.get(), 50, 50,
+                  "52b75451e396f55e95d1cb68e6018226");
+
+    UnloadPage(page);
+  }
+
+  {
+    FPDF_PAGE page = LoadPage(1);
+    ASSERT_TRUE(page);
+
+    ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+
+    EXPECT_EQ(50, FPDFBitmap_GetWidth(thumb_bitmap.get()));
+    EXPECT_EQ(50, FPDFBitmap_GetHeight(thumb_bitmap.get()));
+    EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(thumb_bitmap.get()));
+    CompareBitmap(thumb_bitmap.get(), 50, 50,
+                  "1f448be08c6e6043ccd0bad8ecc2a351");
+
+    UnloadPage(page);
+  }
+}
+
+TEST_F(FPDFThumbnailEmbedderTest,
+       GetThumbnailAsBitmapFromPageWithoutThumbnail) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+  ASSERT_EQ(nullptr, thumb_bitmap.get());
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest,
+       GetThumbnailAsBitmapFromThumbnailWithEmptyStream) {
+  ASSERT_TRUE(OpenDocument("thumbnail_with_empty_stream.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+  ASSERT_EQ(nullptr, thumb_bitmap.get());
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest,
+       GetThumbnailAsBitmapFromThumbnailWithNoFilters) {
+  ASSERT_TRUE(OpenDocument("thumbnail_with_no_filters.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+
+  EXPECT_EQ(10, FPDFBitmap_GetWidth(thumb_bitmap.get()));
+  EXPECT_EQ(10, FPDFBitmap_GetHeight(thumb_bitmap.get()));
+  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(thumb_bitmap.get()));
+  CompareBitmap(thumb_bitmap.get(), 10, 10, "fe02583f9e6d094042a942ff686e9936");
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailDoesNotAlterPage) {
+  ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf"));
+
+  const char kHashedRawData[] = "f6a8e8db01cccd52abb91ea433a17373";
+  const unsigned long kExpectedRawSize = 1851u;
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Get the raw data
+  unsigned long raw_size = FPDFPage_GetRawThumbnailData(page, nullptr, 0);
+  ASSERT_EQ(kExpectedRawSize, raw_size);
+  std::vector<uint8_t> raw_thumb_buf(raw_size);
+
+  EXPECT_EQ(kExpectedRawSize,
+            FPDFPage_GetRawThumbnailData(page, raw_thumb_buf.data(), raw_size));
+  EXPECT_EQ(kHashedRawData,
+            GenerateMD5Base16(raw_thumb_buf.data(), kExpectedRawSize));
+
+  // Get the thumbnail
+  ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+
+  EXPECT_EQ(50, FPDFBitmap_GetWidth(thumb_bitmap.get()));
+  EXPECT_EQ(50, FPDFBitmap_GetHeight(thumb_bitmap.get()));
+  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(thumb_bitmap.get()));
+  CompareBitmap(thumb_bitmap.get(), 50, 50, "52b75451e396f55e95d1cb68e6018226");
+
+  // Get the raw data again
+  unsigned long new_raw_size = FPDFPage_GetRawThumbnailData(page, nullptr, 0);
+  ASSERT_EQ(kExpectedRawSize, new_raw_size);
+  std::vector<uint8_t> new_raw_thumb_buf(new_raw_size);
+
+  EXPECT_EQ(kExpectedRawSize,
+            FPDFPage_GetRawThumbnailData(page, new_raw_thumb_buf.data(),
+                                         new_raw_size));
+  EXPECT_EQ(kHashedRawData,
+            GenerateMD5Base16(new_raw_thumb_buf.data(), kExpectedRawSize));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailAsBitmapFromPageNullPage) {
+  EXPECT_EQ(nullptr, FPDFPage_GetThumbnailAsBitmap(nullptr));
+}
diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp
index 565d90b..cc26888 100644
--- a/fpdfsdk/fpdf_transformpage.cpp
+++ b/fpdfsdk/fpdf_transformpage.cpp
@@ -10,31 +10,33 @@
 #include <sstream>
 #include <vector>
 
+#include "constants/page_object.h"
+#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 #include "core/fpdfapi/page/cpdf_clippath.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_path.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxge/cfx_pathdata.h"
-#include "fpdfsdk/fsdk_define.h"
+#include "core/fxge/render_defines.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
 void SetBoundingBox(CPDF_Page* page,
                     const ByteString& key,
-                    float left,
-                    float bottom,
-                    float right,
-                    float top) {
-  CPDF_Array* pBoundingBoxArray = page->m_pFormDict->SetNewFor<CPDF_Array>(key);
-  pBoundingBoxArray->AddNew<CPDF_Number>(left);
-  pBoundingBoxArray->AddNew<CPDF_Number>(bottom);
-  pBoundingBoxArray->AddNew<CPDF_Number>(right);
-  pBoundingBoxArray->AddNew<CPDF_Number>(top);
+                    const CFX_FloatRect& rect) {
+  if (!page)
+    return;
+
+  page->GetDict()->SetRectFor(key, rect);
+  page->UpdateDimensions();
 }
 
 bool GetBoundingBox(CPDF_Page* page,
@@ -43,186 +45,22 @@
                     float* bottom,
                     float* right,
                     float* top) {
-  CPDF_Array* pArray = page->m_pFormDict->GetArrayFor(key);
+  if (!page || !left || !bottom || !right || !top)
+    return false;
+
+  CPDF_Array* pArray = page->GetDict()->GetArrayFor(key);
   if (!pArray)
     return false;
 
-  *left = pArray->GetFloatAt(0);
-  *bottom = pArray->GetFloatAt(1);
-  *right = pArray->GetFloatAt(2);
-  *top = pArray->GetFloatAt(3);
+  *left = pArray->GetNumberAt(0);
+  *bottom = pArray->GetNumberAt(1);
+  *right = pArray->GetNumberAt(2);
+  *top = pArray->GetNumberAt(3);
   return true;
 }
 
 CPDF_Object* GetPageContent(CPDF_Dictionary* pPageDict) {
-  return pPageDict ? pPageDict->GetDirectObjectFor("Contents") : nullptr;
-}
-
-}  // namespace
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetMediaBox(FPDF_PAGE page,
-                                                    float left,
-                                                    float bottom,
-                                                    float right,
-                                                    float top) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-  SetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetCropBox(FPDF_PAGE page,
-                                                   float left,
-                                                   float bottom,
-                                                   float right,
-                                                   float top) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-  SetBoundingBox(pPage, "CropBox", left, bottom, right, top);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetMediaBox(FPDF_PAGE page,
-                                                         float* left,
-                                                         float* bottom,
-                                                         float* right,
-                                                         float* top) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  return pPage && GetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetCropBox(FPDF_PAGE page,
-                                                        float* left,
-                                                        float* bottom,
-                                                        float* right,
-                                                        float* top) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  return pPage && GetBoundingBox(pPage, "CropBox", left, bottom, right, top);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPage_TransFormWithClip(FPDF_PAGE page,
-                           FS_MATRIX* matrix,
-                           FS_RECTF* clipRect) {
-  if (!matrix && !clipRect)
-    return false;
-
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return false;
-
-  std::ostringstream textBuf;
-  textBuf << "q ";
-
-  if (clipRect) {
-    CFX_FloatRect rect = CFXFloatRectFromFSRECTF(*clipRect);
-    rect.Normalize();
-
-    textBuf << ByteString::Format("%f %f %f %f re W* n ", rect.left,
-                                  rect.bottom, rect.Width(), rect.Height());
-  }
-  if (matrix) {
-    textBuf << ByteString::Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b,
-                                  matrix->c, matrix->d, matrix->e, matrix->f);
-  }
-
-  CPDF_Dictionary* pPageDict = pPage->m_pFormDict.Get();
-  CPDF_Object* pContentObj = GetPageContent(pPageDict);
-  if (!pContentObj)
-    return false;
-
-  CPDF_Document* pDoc = pPage->m_pDocument.Get();
-  if (!pDoc)
-    return false;
-
-  CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>(
-      nullptr, 0,
-      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool()));
-  pStream->SetData(&textBuf);
-
-  CPDF_Stream* pEndStream = pDoc->NewIndirect<CPDF_Stream>(
-      nullptr, 0,
-      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool()));
-  pEndStream->SetData((const uint8_t*)" Q", 2);
-
-  if (CPDF_Array* pContentArray = ToArray(pContentObj)) {
-    pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
-  } else if (pContentObj->IsStream() && !pContentObj->IsInline()) {
-    CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>();
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
-    pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
-    pPageDict->SetNewFor<CPDF_Reference>("Contents", pDoc,
-                                         pContentArray->GetObjNum());
-  }
-
-  // Need to transform the patterns as well.
-  CPDF_Dictionary* pRes = pPageDict->GetDictFor("Resources");
-  if (pRes) {
-    CPDF_Dictionary* pPattenDict = pRes->GetDictFor("Pattern");
-    if (pPattenDict) {
-      for (const auto& it : *pPattenDict) {
-        CPDF_Object* pObj = it.second.get();
-        if (pObj->IsReference())
-          pObj = pObj->GetDirect();
-
-        CPDF_Dictionary* pDict = nullptr;
-        if (pObj->IsDictionary())
-          pDict = pObj->AsDictionary();
-        else if (CPDF_Stream* pObjStream = pObj->AsStream())
-          pDict = pObjStream->GetDict();
-        else
-          continue;
-
-        CFX_Matrix m = pDict->GetMatrixFor("Matrix");
-        CFX_Matrix t = *(CFX_Matrix*)matrix;
-        m.Concat(t);
-        pDict->SetMatrixFor("Matrix", m);
-      }
-    }
-  }
-
-  return true;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
-                              double a,
-                              double b,
-                              double c,
-                              double d,
-                              double e,
-                              double f) {
-  CPDF_PageObject* pPageObj = (CPDF_PageObject*)page_object;
-  if (!pPageObj)
-    return;
-  CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f);
-
-  // Special treatment to shading object, because the ClipPath for shading
-  // object is already transformed.
-  if (!pPageObj->IsShading())
-    pPageObj->TransformClipPath(matrix);
-  pPageObj->TransformGeneralState(matrix);
-}
-
-FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left,
-                                                            float bottom,
-                                                            float right,
-                                                            float top) {
-  CPDF_Path Path;
-  Path.AppendRect(left, bottom, right, top);
-
-  auto pNewClipPath = pdfium::MakeUnique<CPDF_ClipPath>();
-  pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false);
-  return pNewClipPath.release();  // Caller takes ownership.
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) {
-  // Take ownership back from caller and destroy.
-  std::unique_ptr<CPDF_ClipPath>(static_cast<CPDF_ClipPath*>(clipPath));
+  return pPageDict->GetDirectObjectFor(pdfium::page_object::kContents);
 }
 
 void OutputPath(std::ostringstream& buf, CPDF_Path path) {
@@ -262,19 +100,298 @@
   }
 }
 
+}  // namespace
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetMediaBox(FPDF_PAGE page,
+                                                    float left,
+                                                    float bottom,
+                                                    float right,
+                                                    float top) {
+  SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kMediaBox,
+                 CFX_FloatRect(left, bottom, right, top));
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetCropBox(FPDF_PAGE page,
+                                                   float left,
+                                                   float bottom,
+                                                   float right,
+                                                   float top) {
+  SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kCropBox,
+                 CFX_FloatRect(left, bottom, right, top));
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetBleedBox(FPDF_PAGE page,
+                                                    float left,
+                                                    float bottom,
+                                                    float right,
+                                                    float top) {
+  SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kBleedBox,
+                 CFX_FloatRect(left, bottom, right, top));
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetTrimBox(FPDF_PAGE page,
+                                                   float left,
+                                                   float bottom,
+                                                   float right,
+                                                   float top) {
+  SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kTrimBox,
+                 CFX_FloatRect(left, bottom, right, top));
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetArtBox(FPDF_PAGE page,
+                                                  float left,
+                                                  float bottom,
+                                                  float right,
+                                                  float top) {
+  SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kArtBox,
+                 CFX_FloatRect(left, bottom, right, top));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetMediaBox(FPDF_PAGE page,
+                                                         float* left,
+                                                         float* bottom,
+                                                         float* right,
+                                                         float* top) {
+  return GetBoundingBox(CPDFPageFromFPDFPage(page),
+                        pdfium::page_object::kMediaBox, left, bottom, right,
+                        top);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetCropBox(FPDF_PAGE page,
+                                                        float* left,
+                                                        float* bottom,
+                                                        float* right,
+                                                        float* top) {
+  return GetBoundingBox(CPDFPageFromFPDFPage(page),
+                        pdfium::page_object::kCropBox, left, bottom, right,
+                        top);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetBleedBox(FPDF_PAGE page,
+                                                         float* left,
+                                                         float* bottom,
+                                                         float* right,
+                                                         float* top) {
+  return GetBoundingBox(CPDFPageFromFPDFPage(page),
+                        pdfium::page_object::kBleedBox, left, bottom, right,
+                        top);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetTrimBox(FPDF_PAGE page,
+                                                        float* left,
+                                                        float* bottom,
+                                                        float* right,
+                                                        float* top) {
+  return GetBoundingBox(CPDFPageFromFPDFPage(page),
+                        pdfium::page_object::kTrimBox, left, bottom, right,
+                        top);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetArtBox(FPDF_PAGE page,
+                                                       float* left,
+                                                       float* bottom,
+                                                       float* right,
+                                                       float* top) {
+  return GetBoundingBox(CPDFPageFromFPDFPage(page),
+                        pdfium::page_object::kArtBox, left, bottom, right, top);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPage_TransFormWithClip(FPDF_PAGE page,
+                           const FS_MATRIX* matrix,
+                           const FS_RECTF* clipRect) {
+  if (!matrix && !clipRect)
+    return false;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return false;
+
+  CPDF_Dictionary* pPageDict = pPage->GetDict();
+  CPDF_Object* pContentObj = GetPageContent(pPageDict);
+  if (!pContentObj)
+    return false;
+
+  CPDF_Document* pDoc = pPage->GetDocument();
+  if (!pDoc)
+    return false;
+
+  std::ostringstream text_buf;
+  text_buf << "q ";
+
+  if (clipRect) {
+    CFX_FloatRect rect = CFXFloatRectFromFSRectF(*clipRect);
+    rect.Normalize();
+
+    WriteFloat(text_buf, rect.left) << " ";
+    WriteFloat(text_buf, rect.bottom) << " ";
+    WriteFloat(text_buf, rect.Width()) << " ";
+    WriteFloat(text_buf, rect.Height()) << " re W* n ";
+  }
+  if (matrix) {
+    CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix);
+    text_buf << m << " cm ";
+  }
+
+  CPDF_Stream* pStream =
+      pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
+  pStream->SetDataFromStringstream(&text_buf);
+
+  CPDF_Stream* pEndStream =
+      pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
+  pEndStream->SetData(ByteStringView(" Q").raw_span());
+
+  if (CPDF_Array* pContentArray = ToArray(pContentObj)) {
+    pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
+    pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
+  } else if (pContentObj->IsStream() && !pContentObj->IsInline()) {
+    pContentArray = pDoc->NewIndirect<CPDF_Array>();
+    pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
+    pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
+    pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
+    pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc,
+                                         pContentArray->GetObjNum());
+  }
+
+  // Need to transform the patterns as well.
+  CPDF_Dictionary* pRes =
+      pPageDict->GetDictFor(pdfium::page_object::kResources);
+  if (!pRes)
+    return true;
+
+  CPDF_Dictionary* pPatternDict = pRes->GetDictFor("Pattern");
+  if (!pPatternDict)
+    return true;
+
+  CPDF_DictionaryLocker locker(pPatternDict);
+  for (const auto& it : locker) {
+    CPDF_Object* pObj = it.second.Get();
+    if (pObj->IsReference())
+      pObj = pObj->GetDirect();
+
+    CPDF_Dictionary* pDict = nullptr;
+    if (pObj->IsDictionary())
+      pDict = pObj->AsDictionary();
+    else if (CPDF_Stream* pObjStream = pObj->AsStream())
+      pDict = pObjStream->GetDict();
+    else
+      continue;
+
+    if (matrix) {
+      CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix);
+      pDict->SetMatrixFor("Matrix", pDict->GetMatrixFor("Matrix") * m);
+    }
+  }
+
+  return true;
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
+                              double a,
+                              double b,
+                              double c,
+                              double d,
+                              double e,
+                              double f) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return;
+
+  CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f);
+
+  // Special treatment to shading object, because the ClipPath for shading
+  // object is already transformed.
+  if (!pPageObj->IsShading())
+    pPageObj->TransformClipPath(matrix);
+  pPageObj->TransformGeneralState(matrix);
+}
+
+FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV
+FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj)
+    return nullptr;
+
+  return FPDFClipPathFromCPDFClipPath(&pPageObj->m_ClipPath);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path) {
+  CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
+  if (!pClipPath || !pClipPath->HasRef())
+    return -1;
+
+  return pClipPath->GetPathCount();
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index) {
+  CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
+  if (!pClipPath || !pClipPath->HasRef())
+    return -1;
+
+  if (path_index < 0 ||
+      static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) {
+    return -1;
+  }
+
+  return pdfium::CollectionSize<int>(
+      pClipPath->GetPath(path_index).GetPoints());
+}
+
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,
+                            int path_index,
+                            int segment_index) {
+  CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
+  if (!pClipPath || !pClipPath->HasRef())
+    return nullptr;
+
+  if (path_index < 0 ||
+      static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) {
+    return nullptr;
+  }
+
+  const std::vector<FX_PATHPOINT>& points =
+      pClipPath->GetPath(path_index).GetPoints();
+  if (!pdfium::IndexInBounds(points, segment_index))
+    return nullptr;
+
+  return FPDFPathSegmentFromFXPathPoint(&points[segment_index]);
+}
+
+FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left,
+                                                            float bottom,
+                                                            float right,
+                                                            float top) {
+  CPDF_Path Path;
+  Path.AppendRect(left, bottom, right, top);
+
+  auto pNewClipPath = pdfium::MakeUnique<CPDF_ClipPath>();
+  pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false);
+
+  // Caller takes ownership.
+  return FPDFClipPathFromCPDFClipPath(pNewClipPath.release());
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) {
+  // Take ownership back from caller and destroy.
+  std::unique_ptr<CPDF_ClipPath>(CPDFClipPathFromFPDFClipPath(clipPath));
+}
+
 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page,
                                                        FPDF_CLIPPATH clipPath) {
   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
   if (!pPage)
     return;
 
-  CPDF_Dictionary* pPageDict = pPage->m_pFormDict.Get();
+  CPDF_Dictionary* pPageDict = pPage->GetDict();
   CPDF_Object* pContentObj = GetPageContent(pPageDict);
   if (!pContentObj)
     return;
 
   std::ostringstream strClip;
-  CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath;
+  CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clipPath);
   for (size_t i = 0; i < pClipPath->GetPathCount(); ++i) {
     CPDF_Path path = pClipPath->GetPath(i);
     if (path.GetPoints().empty()) {
@@ -288,14 +405,13 @@
         strClip << "W* n\n";
     }
   }
-  CPDF_Document* pDoc = pPage->m_pDocument.Get();
+  CPDF_Document* pDoc = pPage->GetDocument();
   if (!pDoc)
     return;
 
-  CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>(
-      nullptr, 0,
-      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool()));
-  pStream->SetData(&strClip);
+  CPDF_Stream* pStream =
+      pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
+  pStream->SetDataFromStringstream(&strClip);
 
   if (CPDF_Array* pArray = ToArray(pContentObj)) {
     pArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
@@ -303,7 +419,7 @@
     CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>();
     pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
     pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
-    pPageDict->SetNewFor<CPDF_Reference>("Contents", pDoc,
+    pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc,
                                          pContentArray->GetObjNum());
   }
 }
diff --git a/fpdfsdk/fpdf_transformpage_embeddertest.cpp b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
new file mode 100644
index 0000000..6ebb8d2
--- /dev/null
+++ b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
@@ -0,0 +1,545 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_transformpage.h"
+
+#include "build/build_config.h"
+#include "testing/embedder_test.h"
+
+#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#include "third_party/base/test/scoped_locale.h"
+#endif
+
+class FPDFTransformEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFTransformEmbedderTest, GetBoundingBoxes) {
+  ASSERT_TRUE(OpenDocument("cropped_text.pdf"));
+  ASSERT_EQ(4, FPDF_GetPageCount(document()));
+
+  {
+    FPDF_PAGE page = LoadPage(1);
+    ASSERT_TRUE(page);
+
+    FS_RECTF mediabox;
+    EXPECT_TRUE(FPDFPage_GetMediaBox(page, &mediabox.left, &mediabox.bottom,
+                                     &mediabox.right, &mediabox.top));
+    EXPECT_EQ(-50, mediabox.left);
+    EXPECT_EQ(-50, mediabox.bottom);
+    EXPECT_EQ(200, mediabox.right);
+    EXPECT_EQ(200, mediabox.top);
+
+    FS_RECTF cropbox;
+    EXPECT_TRUE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                    &cropbox.right, &cropbox.top));
+    EXPECT_EQ(50, cropbox.left);
+    EXPECT_EQ(50, cropbox.bottom);
+    EXPECT_EQ(150, cropbox.right);
+    EXPECT_EQ(150, cropbox.top);
+
+    FS_RECTF bleedbox;
+    EXPECT_TRUE(FPDFPage_GetBleedBox(page, &bleedbox.left, &bleedbox.bottom,
+                                     &bleedbox.right, &bleedbox.top));
+    EXPECT_EQ(0, bleedbox.left);
+    EXPECT_EQ(10, bleedbox.bottom);
+    EXPECT_EQ(150, bleedbox.right);
+    EXPECT_EQ(145, bleedbox.top);
+
+    FS_RECTF trimbox;
+    EXPECT_TRUE(FPDFPage_GetTrimBox(page, &trimbox.left, &trimbox.bottom,
+                                    &trimbox.right, &trimbox.top));
+    EXPECT_EQ(25, trimbox.left);
+    EXPECT_EQ(30, trimbox.bottom);
+    EXPECT_EQ(140, trimbox.right);
+    EXPECT_EQ(145, trimbox.top);
+
+    FS_RECTF artbox;
+    EXPECT_TRUE(FPDFPage_GetArtBox(page, &artbox.left, &artbox.bottom,
+                                   &artbox.right, &artbox.top));
+    EXPECT_EQ(50, artbox.left);
+    EXPECT_EQ(60, artbox.bottom);
+    EXPECT_EQ(135, artbox.right);
+    EXPECT_EQ(140, artbox.top);
+
+    UnloadPage(page);
+  }
+
+  {
+    FPDF_PAGE page = LoadPage(3);
+    ASSERT_TRUE(page);
+
+    FS_RECTF mediabox;
+    EXPECT_TRUE(FPDFPage_GetMediaBox(page, &mediabox.left, &mediabox.bottom,
+                                     &mediabox.right, &mediabox.top));
+    EXPECT_EQ(0, mediabox.left);
+    EXPECT_EQ(0, mediabox.bottom);
+    EXPECT_EQ(200, mediabox.right);
+    EXPECT_EQ(200, mediabox.top);
+
+    FS_RECTF cropbox;
+    EXPECT_TRUE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                    &cropbox.right, &cropbox.top));
+    EXPECT_EQ(150, cropbox.left);
+    EXPECT_EQ(150, cropbox.bottom);
+    EXPECT_EQ(60, cropbox.right);
+    EXPECT_EQ(60, cropbox.top);
+
+    EXPECT_FALSE(FPDFPage_GetCropBox(page, nullptr, &cropbox.bottom,
+                                     &cropbox.right, &cropbox.top));
+    EXPECT_FALSE(FPDFPage_GetCropBox(page, &cropbox.left, nullptr,
+                                     &cropbox.right, &cropbox.top));
+    EXPECT_FALSE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                     nullptr, &cropbox.top));
+    EXPECT_FALSE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                     &cropbox.right, nullptr));
+    EXPECT_FALSE(FPDFPage_GetCropBox(page, nullptr, nullptr, nullptr, nullptr));
+
+    FS_RECTF bleedbox;
+    EXPECT_TRUE(FPDFPage_GetBleedBox(page, &bleedbox.left, &bleedbox.bottom,
+                                     &bleedbox.right, &bleedbox.top));
+    EXPECT_EQ(160, bleedbox.left);
+    EXPECT_EQ(165, bleedbox.bottom);
+    EXPECT_EQ(0, bleedbox.right);
+    EXPECT_EQ(10, bleedbox.top);
+
+    FS_RECTF trimbox;
+    EXPECT_TRUE(FPDFPage_GetTrimBox(page, &trimbox.left, &trimbox.bottom,
+                                    &trimbox.right, &trimbox.top));
+    EXPECT_EQ(155, trimbox.left);
+    EXPECT_EQ(165, trimbox.bottom);
+    EXPECT_EQ(25, trimbox.right);
+    EXPECT_EQ(30, trimbox.top);
+
+    FS_RECTF artbox;
+    EXPECT_TRUE(FPDFPage_GetArtBox(page, &artbox.left, &artbox.bottom,
+                                   &artbox.right, &artbox.top));
+    EXPECT_EQ(140, artbox.left);
+    EXPECT_EQ(145, artbox.bottom);
+    EXPECT_EQ(65, artbox.right);
+    EXPECT_EQ(70, artbox.top);
+
+    UnloadPage(page);
+  }
+}
+
+TEST_F(FPDFTransformEmbedderTest, NoCropBox) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FS_RECTF cropbox = {-1.0f, 0.0f, 3.0f, -2.0f};
+  EXPECT_FALSE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                   &cropbox.right, &cropbox.top));
+  EXPECT_EQ(-1.0f, cropbox.left);
+  EXPECT_EQ(-2.0f, cropbox.bottom);
+  EXPECT_EQ(3.0f, cropbox.right);
+  EXPECT_EQ(0.0f, cropbox.top);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTransformEmbedderTest, NoBleedBox) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FS_RECTF bleedbox = {-1.0f, 10.f, 3.0f, -1.0f};
+  EXPECT_FALSE(FPDFPage_GetBleedBox(page, &bleedbox.left, &bleedbox.bottom,
+                                    &bleedbox.right, &bleedbox.top));
+  EXPECT_EQ(-1.0f, bleedbox.left);
+  EXPECT_EQ(-1.0f, bleedbox.bottom);
+  EXPECT_EQ(3.0f, bleedbox.right);
+  EXPECT_EQ(10.0f, bleedbox.top);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTransformEmbedderTest, NoTrimBox) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FS_RECTF trimbox = {-11.0f, 0.0f, 3.0f, -10.0f};
+  EXPECT_FALSE(FPDFPage_GetTrimBox(page, &trimbox.left, &trimbox.bottom,
+                                   &trimbox.right, &trimbox.top));
+  EXPECT_EQ(-11.0f, trimbox.left);
+  EXPECT_EQ(-10.0f, trimbox.bottom);
+  EXPECT_EQ(3.0f, trimbox.right);
+  EXPECT_EQ(0.0f, trimbox.top);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTransformEmbedderTest, NoArtBox) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  ASSERT_EQ(1, FPDF_GetPageCount(document()));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FS_RECTF artbox = {-1.0f, 0.0f, 3.0f, -1.0f};
+  EXPECT_FALSE(FPDFPage_GetArtBox(page, &artbox.left, &artbox.bottom,
+                                  &artbox.right, &artbox.top));
+  EXPECT_EQ(-1.0f, artbox.left);
+  EXPECT_EQ(-1.0f, artbox.bottom);
+  EXPECT_EQ(3.0f, artbox.right);
+  EXPECT_EQ(0.0f, artbox.top);
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_SetCropBox DISABLED_SetCropBox
+#else
+#define MAYBE_SetCropBox SetCropBox
+#endif
+TEST_F(FPDFTransformEmbedderTest, MAYBE_SetCropBox) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kCroppedMD5[] = "9937883715d5144c079fb8f7e3d4f395";
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      FS_RECTF cropbox;
+      EXPECT_FALSE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                       &cropbox.right, &cropbox.top));
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    FPDFPage_SetCropBox(page, 10, 20, 100, 150);
+
+    {
+      // Render the page after setting the CropBox.
+      // Note that the change affects the rendering, as expected.
+      // It behaves just like the case below, rather than the case above.
+      FS_RECTF cropbox;
+      EXPECT_TRUE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                      &cropbox.right, &cropbox.top));
+      EXPECT_EQ(10, cropbox.left);
+      EXPECT_EQ(20, cropbox.bottom);
+      EXPECT_EQ(100, cropbox.right);
+      EXPECT_EQ(150, cropbox.top);
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(90, page_width);
+      EXPECT_EQ(130, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the rotation.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    FS_RECTF cropbox;
+    EXPECT_TRUE(FPDFPage_GetCropBox(saved_page, &cropbox.left, &cropbox.bottom,
+                                    &cropbox.right, &cropbox.top));
+    EXPECT_EQ(10, cropbox.left);
+    EXPECT_EQ(20, cropbox.bottom);
+    EXPECT_EQ(100, cropbox.right);
+    EXPECT_EQ(150, cropbox.top);
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(90, page_width);
+    EXPECT_EQ(130, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_SetMediaBox DISABLED_SetMediaBox
+#else
+#define MAYBE_SetMediaBox SetMediaBox
+#endif
+TEST_F(FPDFTransformEmbedderTest, MAYBE_SetMediaBox) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kShrunkMD5[] = "eab5958f62f7ce65d7c32de98389fee1";
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      FS_RECTF mediabox;
+      EXPECT_FALSE(FPDFPage_GetMediaBox(page, &mediabox.left, &mediabox.bottom,
+                                        &mediabox.right, &mediabox.top));
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    FPDFPage_SetMediaBox(page, 20, 30, 100, 150);
+
+    {
+      // Render the page after setting the MediaBox.
+      // Note that the change affects the rendering, as expected.
+      // It behaves just like the case below, rather than the case above.
+      FS_RECTF mediabox;
+      EXPECT_TRUE(FPDFPage_GetMediaBox(page, &mediabox.left, &mediabox.bottom,
+                                       &mediabox.right, &mediabox.top));
+      EXPECT_EQ(20, mediabox.left);
+      EXPECT_EQ(30, mediabox.bottom);
+      EXPECT_EQ(100, mediabox.right);
+      EXPECT_EQ(150, mediabox.top);
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(80, page_width);
+      EXPECT_EQ(120, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the rotation.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    FS_RECTF mediabox;
+    EXPECT_TRUE(FPDFPage_GetMediaBox(saved_page, &mediabox.left,
+                                     &mediabox.bottom, &mediabox.right,
+                                     &mediabox.top));
+    EXPECT_EQ(20, mediabox.left);
+    EXPECT_EQ(30, mediabox.bottom);
+    EXPECT_EQ(100, mediabox.right);
+    EXPECT_EQ(150, mediabox.top);
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(80, page_width);
+    EXPECT_EQ(120, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
+TEST_F(FPDFTransformEmbedderTest, ClipPath) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFClipPath clip(FPDF_CreateClipPath(10.0f, 10.0f, 90.0f, 90.0f));
+    EXPECT_TRUE(clip);
+
+    // NULL arg call is a no-op.
+    FPDFPage_InsertClipPath(nullptr, clip.get());
+
+    // Do actual work.
+    FPDFPage_InsertClipPath(page, clip.get());
+
+    // TODO(tsepez): test how inserting path affects page rendering.
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTransformEmbedderTest, TransFormWithClip) {
+  const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0};
+  const FS_RECTF clip_rect = {0.0f, 0.0f, 20.0f, 10.0f};
+
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_FALSE(FPDFPage_TransFormWithClip(nullptr, nullptr, nullptr));
+  EXPECT_FALSE(FPDFPage_TransFormWithClip(nullptr, &half_matrix, nullptr));
+  EXPECT_FALSE(FPDFPage_TransFormWithClip(nullptr, nullptr, &clip_rect));
+  EXPECT_FALSE(FPDFPage_TransFormWithClip(nullptr, &half_matrix, &clip_rect));
+  EXPECT_FALSE(FPDFPage_TransFormWithClip(page, nullptr, nullptr));
+  EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr));
+  EXPECT_TRUE(FPDFPage_TransFormWithClip(page, nullptr, &clip_rect));
+  EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, &clip_rect));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTransformEmbedderTest, TransFormWithClipWithPatterns) {
+  const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0};
+  const FS_RECTF clip_rect = {0.0f, 0.0f, 20.0f, 10.0f};
+
+  ASSERT_TRUE(OpenDocument("bug_547706.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr));
+  EXPECT_TRUE(FPDFPage_TransFormWithClip(page, nullptr, &clip_rect));
+  EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, &clip_rect));
+
+  UnloadPage(page);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_TransFormWithClipAndSave DISABLED_TransFormWithClipAndSave
+#else
+#define MAYBE_TransFormWithClipAndSave TransFormWithClipAndSave
+#endif
+TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSave) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kShrunkMD5[] = "f4136cc9209207ab60eb8381a3df2e69";
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    {
+      // Render the page after transforming.
+      // Note that the change should affect the rendering, but does not.
+      // It should behaves just like the case below, rather than the case above.
+      // TODO(bug_1328): The checksum below should be |kShrunkMD5|.
+      const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0};
+      EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr));
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the transform.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(200, page_width);
+    EXPECT_EQ(300, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
+#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_TransFormWithClipAndSaveWithLocale \
+  DISABLED_TransFormWithClipAndSaveWithLocale
+#else
+#define MAYBE_TransFormWithClipAndSaveWithLocale \
+  TransFormWithClipAndSaveWithLocale
+#endif
+TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSaveWithLocale) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kShrunkMD5[] = "f4136cc9209207ab60eb8381a3df2e69";
+
+  pdfium::base::ScopedLocale scoped_locale("da_DK.UTF-8");
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    {
+      // Render the page after transforming.
+      // Note that the change should affect the rendering, but does not.
+      // It should behaves just like the case below, rather than the case above.
+      // TODO(bug_1328): The checksum below should be |kShrunkMD5|.
+      const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0};
+      EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr));
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the transform.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(200, page_width);
+    EXPECT_EQ(300, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+#endif  // defined(OS_LINUX) || defined(OS_FUCHSIA)
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
new file mode 100644
index 0000000..da25308
--- /dev/null
+++ b/fpdfsdk/fpdf_view.cpp
@@ -0,0 +1,1151 @@
+// 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 "public/fpdfview.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/page/cpdf_occontext.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfapi/render/cpdf_docrenderdata.h"
+#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfapi/render/cpdf_pagerendercontext.h"
+#include "core/fpdfapi/render/cpdf_rendercontext.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fpdfdoc/cpdf_nametree.h"
+#include "core/fpdfdoc/cpdf_viewerpreferences.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "fpdfsdk/cpdfsdk_customaccess.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/cpdfsdk_renderpage.h"
+#include "fxjs/ijs_runtime.h"
+#include "public/fpdf_formfill.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#include "fxbarcode/BC_Library.h"
+#endif  // PDF_ENABLE_XFA
+
+#if defined(OS_WIN)
+#include "core/fpdfapi/render/cpdf_progressiverenderer.h"
+#include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
+#include "public/fpdf_edit.h"
+
+// These checks are here because core/ and public/ cannot depend on each other.
+static_assert(WindowsPrintMode::kModeEmf == FPDF_PRINTMODE_EMF,
+              "WindowsPrintMode::kModeEmf value mismatch");
+static_assert(WindowsPrintMode::kModeTextOnly == FPDF_PRINTMODE_TEXTONLY,
+              "WindowsPrintMode::kModeTextOnly value mismatch");
+static_assert(WindowsPrintMode::kModePostScript2 == FPDF_PRINTMODE_POSTSCRIPT2,
+              "WindowsPrintMode::kModePostScript2 value mismatch");
+static_assert(WindowsPrintMode::kModePostScript3 == FPDF_PRINTMODE_POSTSCRIPT3,
+              "WindowsPrintMode::kModePostScript3 value mismatch");
+static_assert(WindowsPrintMode::kModePostScript2PassThrough ==
+                  FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH,
+              "WindowsPrintMode::kModePostScript2PassThrough value mismatch");
+static_assert(WindowsPrintMode::kModePostScript3PassThrough ==
+                  FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH,
+              "WindowsPrintMode::kModePostScript3PassThrough value mismatch");
+#endif  // defined(OS_WIN)
+
+namespace {
+
+bool g_bLibraryInitialized = false;
+
+FPDF_DOCUMENT LoadDocumentImpl(
+    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
+    FPDF_BYTESTRING password) {
+  if (!pFileAccess) {
+    ProcessParseError(CPDF_Parser::FILE_ERROR);
+    return nullptr;
+  }
+
+  auto pDocument = pdfium::MakeUnique<CPDF_Document>(
+      pdfium::MakeUnique<CPDF_DocRenderData>(),
+      pdfium::MakeUnique<CPDF_DocPageData>());
+
+  CPDF_Parser::Error error = pDocument->LoadDoc(pFileAccess, password);
+  if (error != CPDF_Parser::SUCCESS) {
+    ProcessParseError(error);
+    return nullptr;
+  }
+
+  ReportUnsupportedFeatures(pDocument.get());
+  return FPDFDocumentFromCPDFDocument(pDocument.release());
+}
+
+}  // namespace
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary() {
+  FPDF_InitLibraryWithConfig(nullptr);
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG* config) {
+  if (g_bLibraryInitialized)
+    return;
+
+  FXMEM_InitializePartitionAlloc();
+  CFX_GEModule::Create(config ? config->m_pUserFontPaths : nullptr);
+  CPDF_PageModule::Create();
+
+#ifdef PDF_ENABLE_XFA
+  BC_Library_Init();
+#endif  // PDF_ENABLE_XFA
+  if (config && config->version >= 2)
+    IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate);
+
+  g_bLibraryInitialized = true;
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyLibrary() {
+  if (!g_bLibraryInitialized)
+    return;
+
+#ifdef PDF_ENABLE_XFA
+  BC_Library_Destroy();
+#endif  // PDF_ENABLE_XFA
+
+  CPDF_PageModule::Destroy();
+  CFX_GEModule::Destroy();
+  IJS_Runtime::Destroy();
+
+  g_bLibraryInitialized = false;
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetSandBoxPolicy(FPDF_DWORD policy,
+                                                     FPDF_BOOL enable) {
+  return SetPDFSandboxPolicy(policy, enable);
+}
+
+#if defined(OS_WIN)
+#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
+FPDF_EXPORT void FPDF_CALLCONV
+FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) {
+  g_pdfium_typeface_accessible_func = func;
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi) {
+  g_pdfium_print_text_with_gdi = !!use_gdi;
+}
+#endif  // PDFIUM_PRINT_TEXT_WITH_GDI
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode) {
+  if (mode < FPDF_PRINTMODE_EMF ||
+      mode > FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH) {
+    return FALSE;
+  }
+  g_pdfium_print_mode = static_cast<WindowsPrintMode>(mode);
+  return TRUE;
+}
+#endif  // defined(OS_WIN)
+
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
+FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password) {
+  // NOTE: the creation of the file needs to be by the embedder on the
+  // other side of this API.
+  return LoadDocumentImpl(IFX_SeekableReadStream::CreateFromFilename(file_path),
+                          password);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetFormType(FPDF_DOCUMENT document) {
+  const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return FORMTYPE_NONE;
+
+  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
+  if (!pRoot)
+    return FORMTYPE_NONE;
+
+  const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  if (!pAcroForm)
+    return FORMTYPE_NONE;
+
+  const CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
+  if (!pXFA)
+    return FORMTYPE_ACRO_FORM;
+
+  bool bNeedsRendering = pRoot->GetBooleanFor("NeedsRendering", false);
+  return bNeedsRendering ? FORMTYPE_XFA_FULL : FORMTYPE_XFA_FOREGROUND;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_LoadXFA(FPDF_DOCUMENT document) {
+#ifdef PDF_ENABLE_XFA
+  auto* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return false;
+
+  auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
+  if (pContext)
+    return pContext->LoadXFADoc();
+#endif  // PDF_ENABLE_XFA
+  return false;
+}
+
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
+FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password) {
+  return LoadDocumentImpl(
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+          pdfium::make_span(static_cast<const uint8_t*>(data_buf), size)),
+      password);
+}
+
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
+FPDF_LoadCustomDocument(FPDF_FILEACCESS* pFileAccess,
+                        FPDF_BYTESTRING password) {
+  if (!pFileAccess)
+    return nullptr;
+  return LoadDocumentImpl(pdfium::MakeRetain<CPDFSDK_CustomAccess>(pFileAccess),
+                          password);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetFileVersion(FPDF_DOCUMENT doc,
+                                                        int* fileVersion) {
+  if (!fileVersion)
+    return false;
+
+  *fileVersion = 0;
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(doc);
+  if (!pDoc)
+    return false;
+
+  const CPDF_Parser* pParser = pDoc->GetParser();
+  if (!pParser)
+    return false;
+
+  *fileVersion = pParser->GetFileVersion();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  return pDoc && pDoc->has_valid_cross_reference_table();
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_GetDocPermissions(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  return pDoc ? pDoc->GetUserPermissions() : 0;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc || !pDoc->GetParser())
+    return -1;
+
+  const CPDF_Dictionary* pDict = pDoc->GetParser()->GetEncryptDict();
+  return pDict ? pDict->GetIntegerFor("R") : -1;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageCount(FPDF_DOCUMENT document) {
+  auto* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  auto* pExtension = pDoc->GetExtension();
+  return pExtension ? pExtension->GetPageCount() : pDoc->GetPageCount();
+}
+
+FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDF_LoadPage(FPDF_DOCUMENT document,
+                                                  int page_index) {
+  auto* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
+    return nullptr;
+
+#ifdef PDF_ENABLE_XFA
+  auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
+  if (pContext)
+    return FPDFPageFromIPDFPage(pContext->GetXFAPage(page_index).Leak());
+#endif  // PDF_ENABLE_XFA
+
+  CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index);
+  if (!pDict)
+    return nullptr;
+
+  auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
+  pPage->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(pPage.Get()));
+  pPage->ParseContent();
+  return FPDFPageFromIPDFPage(pPage.Leak());
+}
+
+FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageWidthF(FPDF_PAGE page) {
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
+  return pPage ? pPage->GetPageWidth() : 0.0f;
+}
+
+FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageWidth(FPDF_PAGE page) {
+  return FPDF_GetPageWidthF(page);
+}
+
+FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageHeightF(FPDF_PAGE page) {
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
+  return pPage ? pPage->GetPageHeight() : 0.0f;
+}
+
+FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageHeight(FPDF_PAGE page) {
+  return FPDF_GetPageHeightF(page);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetPageBoundingBox(FPDF_PAGE page,
+                                                            FS_RECTF* rect) {
+  if (!rect)
+    return false;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return false;
+
+  *rect = FSRectFFromCFXFloatRect(pPage->GetBBox());
+  return true;
+}
+
+#if defined(OS_WIN)
+namespace {
+
+const double kEpsilonSize = 0.01f;
+
+void GetScaling(CPDF_Page* pPage,
+                int size_x,
+                int size_y,
+                int rotate,
+                double* scale_x,
+                double* scale_y) {
+  ASSERT(pPage);
+  ASSERT(scale_x);
+  ASSERT(scale_y);
+  double page_width = pPage->GetPageWidth();
+  double page_height = pPage->GetPageHeight();
+  if (page_width < kEpsilonSize || page_height < kEpsilonSize)
+    return;
+
+  if (rotate % 2 == 0) {
+    *scale_x = size_x / page_width;
+    *scale_y = size_y / page_height;
+  } else {
+    *scale_x = size_y / page_width;
+    *scale_y = size_x / page_height;
+  }
+}
+
+FX_RECT GetMaskDimensionsAndOffsets(CPDF_Page* pPage,
+                                    int start_x,
+                                    int start_y,
+                                    int size_x,
+                                    int size_y,
+                                    int rotate,
+                                    const CFX_FloatRect& mask_box) {
+  double scale_x = 0.0f;
+  double scale_y = 0.0f;
+  GetScaling(pPage, size_x, size_y, rotate, &scale_x, &scale_y);
+  if (scale_x < kEpsilonSize || scale_y < kEpsilonSize)
+    return FX_RECT();
+
+  // Compute sizes in page points. Round down to catch the entire bitmap.
+  int start_x_bm = static_cast<int>(mask_box.left * scale_x);
+  int start_y_bm = static_cast<int>(mask_box.bottom * scale_y);
+  int size_x_bm = static_cast<int>(mask_box.right * scale_x + 1.0f) -
+                  static_cast<int>(mask_box.left * scale_x);
+  int size_y_bm = static_cast<int>(mask_box.top * scale_y + 1.0f) -
+                  static_cast<int>(mask_box.bottom * scale_y);
+
+  // Get page rotation
+  int page_rotation = pPage->GetPageRotation();
+
+  // Compute offsets
+  int offset_x = 0;
+  int offset_y = 0;
+  if (size_x > size_y)
+    std::swap(size_x_bm, size_y_bm);
+
+  switch ((rotate + page_rotation) % 4) {
+    case 0:
+      offset_x = start_x_bm + start_x;
+      offset_y = start_y + size_y - size_y_bm - start_y_bm;
+      break;
+    case 1:
+      offset_x = start_y_bm + start_x;
+      offset_y = start_x_bm + start_y;
+      break;
+    case 2:
+      offset_x = start_x + size_x - size_x_bm - start_x_bm;
+      offset_y = start_y_bm + start_y;
+      break;
+    case 3:
+      offset_x = start_x + size_x - size_x_bm - start_y_bm;
+      offset_y = start_y + size_y - size_y_bm - start_x_bm;
+      break;
+  }
+  return FX_RECT(offset_x, offset_y, offset_x + size_x_bm,
+                 offset_y + size_y_bm);
+}
+
+// Get a bitmap of just the mask section defined by |mask_box| from a full page
+// bitmap |pBitmap|.
+RetainPtr<CFX_DIBitmap> GetMaskBitmap(CPDF_Page* pPage,
+                                      int start_x,
+                                      int start_y,
+                                      int size_x,
+                                      int size_y,
+                                      int rotate,
+                                      const RetainPtr<CFX_DIBitmap>& pSrc,
+                                      const CFX_FloatRect& mask_box,
+                                      FX_RECT* bitmap_area) {
+  ASSERT(bitmap_area);
+  *bitmap_area = GetMaskDimensionsAndOffsets(pPage, start_x, start_y, size_x,
+                                             size_y, rotate, mask_box);
+  if (bitmap_area->IsEmpty())
+    return nullptr;
+
+  // Create a new bitmap to transfer part of the page bitmap to.
+  RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pDst->Create(bitmap_area->Width(), bitmap_area->Height(), FXDIB_Argb))
+    return nullptr;
+
+  pDst->Clear(0x00ffffff);
+  pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc,
+                       bitmap_area->left, bitmap_area->top);
+  return pDst;
+}
+
+void RenderBitmap(CFX_RenderDevice* device,
+                  const RetainPtr<CFX_DIBitmap>& pSrc,
+                  const FX_RECT& mask_area) {
+  int size_x_bm = mask_area.Width();
+  int size_y_bm = mask_area.Height();
+  if (size_x_bm == 0 || size_y_bm == 0)
+    return;
+
+  // Create a new bitmap from the old one
+  RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pDst->Create(size_x_bm, size_y_bm, FXDIB_Rgb32))
+    return;
+
+  pDst->Clear(0xffffffff);
+  pDst->CompositeBitmap(0, 0, size_x_bm, size_y_bm, pSrc, 0, 0,
+                        BlendMode::kNormal, nullptr, false);
+
+  if (device->GetDeviceType() == DeviceType::kPrinter) {
+    device->StretchDIBits(pDst, mask_area.left, mask_area.top, size_x_bm,
+                          size_y_bm);
+  } else {
+    device->SetDIBits(pDst, mask_area.left, mask_area.top);
+  }
+}
+
+}  // namespace
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc,
+                                               FPDF_PAGE page,
+                                               int start_x,
+                                               int start_y,
+                                               int size_x,
+                                               int size_y,
+                                               int rotate,
+                                               int flags) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* pContext = pOwnedContext.get();
+  CPDF_Page::RenderContextClearer clearer(pPage);
+  pPage->SetRenderContext(std::move(pOwnedContext));
+
+  // Don't render the full page to bitmap for a mask unless there are a lot
+  // of masks. Full page bitmaps result in large spool sizes, so they should
+  // only be used when necessary. For large numbers of masks, rendering each
+  // individually is inefficient and unlikely to significantly improve spool
+  // size. TODO(rbpotter): Find out why this still breaks printing for some
+  // PDFs (see crbug.com/777837).
+  const bool bEnableImageMasks = false;
+  const bool bNewBitmap = pPage->BackgroundAlphaNeeded() ||
+                          (pPage->HasImageMask() && !bEnableImageMasks) ||
+                          pPage->GetMaskBoundingBoxes().size() > 100;
+  const bool bHasMask = pPage->HasImageMask() && !bNewBitmap;
+  if (!bNewBitmap && !bHasMask) {
+    pContext->m_pDevice = pdfium::MakeUnique<CPDF_WindowsRenderDevice>(dc);
+    CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+                                  size_y, rotate, flags,
+                                  /*need_to_restore=*/true, /*pause=*/nullptr);
+    return;
+  }
+
+  RetainPtr<CFX_DIBitmap> pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  // Create will probably work fine even if it fails here: we will just attach
+  // a zero-sized bitmap to |pDevice|.
+  pBitmap->Create(size_x, size_y, FXDIB_Argb);
+  pBitmap->Clear(0x00ffffff);
+  CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
+  pContext->m_pDevice = pdfium::WrapUnique(pDevice);
+  pDevice->Attach(pBitmap, false, nullptr, false);
+  if (bHasMask) {
+    pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
+    pContext->m_pOptions->GetOptions().bBreakForMasks = true;
+  }
+
+  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+                                size_y, rotate, flags, /*need_to_restore=*/true,
+                                /*pause=*/nullptr);
+
+  if (!bHasMask) {
+    CPDF_WindowsRenderDevice WinDC(dc);
+    bool bitsStretched = false;
+    if (WinDC.GetDeviceType() == DeviceType::kPrinter) {
+      auto pDst = pdfium::MakeRetain<CFX_DIBitmap>();
+      if (pDst->Create(size_x, size_y, FXDIB_Rgb32)) {
+        memset(pDst->GetBuffer(), -1, pBitmap->GetPitch() * size_y);
+        pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0,
+                              BlendMode::kNormal, nullptr, false);
+        WinDC.StretchDIBits(pDst, 0, 0, size_x, size_y);
+        bitsStretched = true;
+      }
+    }
+    if (!bitsStretched)
+      WinDC.SetDIBits(pBitmap, 0, 0);
+    return;
+  }
+
+  // Finish rendering the page to bitmap and copy the correct segments
+  // of the page to individual image mask bitmaps.
+  const std::vector<CFX_FloatRect>& mask_boxes = pPage->GetMaskBoundingBoxes();
+  std::vector<FX_RECT> bitmap_areas(mask_boxes.size());
+  std::vector<RetainPtr<CFX_DIBitmap>> bitmaps(mask_boxes.size());
+  for (size_t i = 0; i < mask_boxes.size(); i++) {
+    bitmaps[i] = GetMaskBitmap(pPage, start_x, start_y, size_x, size_y, rotate,
+                               pBitmap, mask_boxes[i], &bitmap_areas[i]);
+    pContext->m_pRenderer->Continue(nullptr);
+  }
+
+  // Begin rendering to the printer. Add flag to indicate the renderer should
+  // pause after each image mask.
+  pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
+  pContext = pOwnedContext.get();
+  pPage->SetRenderContext(std::move(pOwnedContext));
+  pContext->m_pDevice = pdfium::MakeUnique<CPDF_WindowsRenderDevice>(dc);
+  pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
+  pContext->m_pOptions->GetOptions().bBreakForMasks = true;
+
+  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+                                size_y, rotate, flags, /*need_to_restore=*/true,
+                                /*pause=*/nullptr);
+
+  // Render masks
+  for (size_t i = 0; i < mask_boxes.size(); i++) {
+    // Render the bitmap for the mask and free the bitmap.
+    if (bitmaps[i]) {  // will be null if mask has zero area
+      RenderBitmap(pContext->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
+    }
+    // Render the next portion of page.
+    pContext->m_pRenderer->Continue(nullptr);
+  }
+}
+#endif  // defined(OS_WIN)
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,
+                                                     FPDF_PAGE page,
+                                                     int start_x,
+                                                     int start_y,
+                                                     int size_x,
+                                                     int size_y,
+                                                     int rotate,
+                                                     int flags) {
+  if (!bitmap)
+    return;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* pContext = pOwnedContext.get();
+  CPDF_Page::RenderContextClearer clearer(pPage);
+  pPage->SetRenderContext(std::move(pOwnedContext));
+
+  auto pOwnedDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
+  CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
+  pContext->m_pDevice = std::move(pOwnedDevice);
+
+  RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
+  pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
+  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+                                size_y, rotate, flags, /*need_to_restore=*/true,
+                                /*pause=*/nullptr);
+
+#ifdef _SKIA_SUPPORT_PATHS_
+  pDevice->Flush(true);
+  pBitmap->UnPreMultiply();
+#endif
+}
+
+FPDF_EXPORT void FPDF_CALLCONV
+FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,
+                                FPDF_PAGE page,
+                                const FS_MATRIX* matrix,
+                                const FS_RECTF* clipping,
+                                int flags) {
+  if (!bitmap)
+    return;
+
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* pContext = pOwnedContext.get();
+  CPDF_Page::RenderContextClearer clearer(pPage);
+  pPage->SetRenderContext(std::move(pOwnedContext));
+
+  auto pOwnedDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
+  CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
+  pContext->m_pDevice = std::move(pOwnedDevice);
+
+  RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
+  pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
+
+  CFX_FloatRect clipping_rect;
+  if (clipping)
+    clipping_rect = CFXFloatRectFromFSRectF(*clipping);
+  FX_RECT clip_rect = clipping_rect.ToFxRect();
+
+  const FX_RECT rect(0, 0, pPage->GetPageWidth(), pPage->GetPageHeight());
+  CFX_Matrix transform_matrix = pPage->GetDisplayMatrix(rect, 0);
+  if (matrix)
+    transform_matrix *= CFXMatrixFromFSMatrix(*matrix);
+  CPDFSDK_RenderPage(pContext, pPage, transform_matrix, clip_rect, flags);
+}
+
+#ifdef _SKIA_SUPPORT_
+FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
+                                                           int size_x,
+                                                           int size_y) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return nullptr;
+
+  auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* pContext = pOwnedContext.get();
+  CPDF_Page::RenderContextClearer clearer(pPage);
+  pPage->SetRenderContext(std::move(pOwnedContext));
+
+  auto skDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
+  FPDF_RECORDER recorder = skDevice->CreateRecorder(size_x, size_y);
+  pContext->m_pDevice = std::move(skDevice);
+
+  CPDFSDK_RenderPageWithContext(pContext, pPage, 0, 0, size_x, size_y, 0, 0,
+                                /*need_to_restore=*/true, /*pause=*/nullptr);
+  return recorder;
+}
+#endif  // _SKIA_SUPPORT_
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) {
+  if (!page)
+    return;
+
+  // Take it back across the API and hold for duration of this function.
+  RetainPtr<IPDF_Page> pPage;
+  pPage.Unleak(IPDFPageFromFPDFPage(page));
+
+  if (pPage->AsXFAPage())
+    return;
+
+  CPDFSDK_PageView* pPageView =
+      static_cast<CPDFSDK_PageView*>(pPage->AsPDFPage()->GetView());
+  if (!pPageView || pPageView->IsBeingDestroyed())
+    return;
+
+  if (pPageView->IsLocked()) {
+    pPageView->TakePageOwnership();
+    return;
+  }
+
+  // This will delete the |pPageView| object. We must cleanup the PageView
+  // first because it will attempt to reset the View on the |pPage| during
+  // destruction.
+  pPageView->GetFormFillEnv()->RemovePageView(pPage.Get());
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseDocument(FPDF_DOCUMENT document) {
+  // Take it back across the API and throw it away,
+  std::unique_ptr<CPDF_Document>(CPDFDocumentFromFPDFDocument(document));
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError() {
+  return FXSYS_GetLastError();
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page,
+                                                      int start_x,
+                                                      int start_y,
+                                                      int size_x,
+                                                      int size_y,
+                                                      int rotate,
+                                                      int device_x,
+                                                      int device_y,
+                                                      double* page_x,
+                                                      double* page_y) {
+  if (!page || !page_x || !page_y)
+    return false;
+
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
+  const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
+  Optional<CFX_PointF> pos =
+      pPage->DeviceToPage(rect, rotate, CFX_PointF(device_x, device_y));
+  if (!pos)
+    return false;
+
+  *page_x = pos->x;
+  *page_y = pos->y;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page,
+                                                      int start_x,
+                                                      int start_y,
+                                                      int size_x,
+                                                      int size_y,
+                                                      int rotate,
+                                                      double page_x,
+                                                      double page_y,
+                                                      int* device_x,
+                                                      int* device_y) {
+  if (!page || !device_x || !device_y)
+    return false;
+
+  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
+  const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
+  CFX_PointF page_point(static_cast<float>(page_x), static_cast<float>(page_y));
+  Optional<CFX_PointF> pos = pPage->PageToDevice(rect, rotate, page_point);
+  if (!pos)
+    return false;
+
+  *device_x = FXSYS_roundf(pos->x);
+  *device_y = FXSYS_roundf(pos->y);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width,
+                                                        int height,
+                                                        int alpha) {
+  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pBitmap->Create(width, height, alpha ? FXDIB_Argb : FXDIB_Rgb32))
+    return nullptr;
+
+  return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
+}
+
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width,
+                                                          int height,
+                                                          int format,
+                                                          void* first_scan,
+                                                          int stride) {
+  FXDIB_Format fx_format;
+  switch (format) {
+    case FPDFBitmap_Gray:
+      fx_format = FXDIB_8bppRgb;
+      break;
+    case FPDFBitmap_BGR:
+      fx_format = FXDIB_Rgb;
+      break;
+    case FPDFBitmap_BGRx:
+      fx_format = FXDIB_Rgb32;
+      break;
+    case FPDFBitmap_BGRA:
+      fx_format = FXDIB_Argb;
+      break;
+    default:
+      return nullptr;
+  }
+
+  // Ensure external memory is good at least for the duration of this call.
+  UnownedPtr<uint8_t> pChecker(static_cast<uint8_t*>(first_scan));
+  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pBitmap->Create(width, height, fx_format, pChecker.Get(), stride))
+    return nullptr;
+
+  return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetFormat(FPDF_BITMAP bitmap) {
+  if (!bitmap)
+    return FPDFBitmap_Unknown;
+
+  FXDIB_Format format = CFXDIBitmapFromFPDFBitmap(bitmap)->GetFormat();
+  switch (format) {
+    case FXDIB_8bppRgb:
+    case FXDIB_8bppMask:
+      return FPDFBitmap_Gray;
+    case FXDIB_Rgb:
+      return FPDFBitmap_BGR;
+    case FXDIB_Rgb32:
+      return FPDFBitmap_BGRx;
+    case FXDIB_Argb:
+      return FPDFBitmap_BGRA;
+    default:
+      return FPDFBitmap_Unknown;
+  }
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_FillRect(FPDF_BITMAP bitmap,
+                                                   int left,
+                                                   int top,
+                                                   int width,
+                                                   int height,
+                                                   FPDF_DWORD color) {
+  if (!bitmap)
+    return;
+
+  CFX_DefaultRenderDevice device;
+  RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
+  device.Attach(pBitmap, false, nullptr, false);
+  if (!pBitmap->HasAlpha())
+    color |= 0xFF000000;
+  device.FillRect(FX_RECT(left, top, left + width, top + height), color);
+}
+
+FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
+  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetBuffer() : nullptr;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) {
+  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetWidth() : 0;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetHeight(FPDF_BITMAP bitmap) {
+  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetHeight() : 0;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetStride(FPDF_BITMAP bitmap) {
+  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetPitch() : 0;
+}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_Destroy(FPDF_BITMAP bitmap) {
+  RetainPtr<CFX_DIBitmap> destroyer;
+  destroyer.Unleak(CFXDIBitmapFromFPDFBitmap(bitmap));
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,
+                         int page_index,
+                         FS_SIZEF* size) {
+  if (!size)
+    return false;
+
+  auto* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return false;
+
+#ifdef PDF_ENABLE_XFA
+  if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
+    return false;
+
+  auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
+  if (pContext) {
+    RetainPtr<CPDFXFA_Page> pPage = pContext->GetXFAPage(page_index);
+    if (!pPage)
+      return false;
+
+    size->width = pPage->GetPageWidth();
+    size->height = pPage->GetPageHeight();
+    return true;
+  }
+#endif  // PDF_ENABLE_XFA
+
+  CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index);
+  if (!pDict)
+    return false;
+
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
+  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  size->width = page->GetPageWidth();
+  size->height = page->GetPageHeight();
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,
+                                                      int page_index,
+                                                      double* width,
+                                                      double* height) {
+  if (!width || !height)
+    return false;
+
+  FS_SIZEF size;
+  if (!FPDF_GetPageSizeByIndexF(document, page_index, &size))
+    return false;
+
+  *width = size.width;
+  *height = size.height;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document) {
+  const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return true;
+  CPDF_ViewerPreferences viewRef(pDoc);
+  return viewRef.PrintScaling();
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document) {
+  const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 1;
+  CPDF_ViewerPreferences viewRef(pDoc);
+  return viewRef.NumCopies();
+}
+
+FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV
+FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document) {
+  const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+  CPDF_ViewerPreferences viewRef(pDoc);
+  return FPDFPageRangeFromCPDFArray(viewRef.PrintPageRange());
+}
+
+FPDF_EXPORT size_t FPDF_CALLCONV
+FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange) {
+  const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
+  return pArray ? pArray->size() : 0;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange,
+                                        size_t index) {
+  const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
+  if (!pArray || index >= pArray->size())
+    return -1;
+  return pArray->GetIntegerAt(index);
+}
+
+FPDF_EXPORT FPDF_DUPLEXTYPE FPDF_CALLCONV
+FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document) {
+  const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return DuplexUndefined;
+  CPDF_ViewerPreferences viewRef(pDoc);
+  ByteString duplex = viewRef.Duplex();
+  if ("Simplex" == duplex)
+    return Simplex;
+  if ("DuplexFlipShortEdge" == duplex)
+    return DuplexFlipShortEdge;
+  if ("DuplexFlipLongEdge" == duplex)
+    return DuplexFlipLongEdge;
+  return DuplexUndefined;
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,
+                       FPDF_BYTESTRING key,
+                       char* buffer,
+                       unsigned long length) {
+  const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  CPDF_ViewerPreferences viewRef(pDoc);
+  Optional<ByteString> bsVal = viewRef.GenericName(key);
+  if (!bsVal)
+    return 0;
+
+  unsigned long dwStringLen = bsVal->GetLength() + 1;
+  if (buffer && length >= dwStringLen)
+    memcpy(buffer, bsVal->c_str(), dwStringLen);
+  return dwStringLen;
+}
+
+FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV
+FPDF_CountNamedDests(FPDF_DOCUMENT document) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
+  if (!pRoot)
+    return 0;
+
+  CPDF_NameTree nameTree(pDoc, "Dests");
+  pdfium::base::CheckedNumeric<FPDF_DWORD> count = nameTree.GetCount();
+  const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
+  if (pDest)
+    count += pDest->size();
+
+  if (!count.IsValid())
+    return 0;
+
+  return count.ValueOrDie();
+}
+
+FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
+FPDF_GetNamedDestByName(FPDF_DOCUMENT document, FPDF_BYTESTRING name) {
+  if (!name || name[0] == 0)
+    return nullptr;
+
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  CPDF_NameTree name_tree(pDoc, "Dests");
+  ByteStringView name_view(name);
+  return FPDFDestFromCPDFArray(
+      name_tree.LookupNamedDest(pDoc, PDF_DecodeText(name_view.raw_span())));
+}
+
+#ifdef PDF_ENABLE_V8
+FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags() {
+  // Reduce exposure since no PDF should contain web assembly.
+  // Use interpreted JS only to avoid RWX pages in our address space.
+  return "--no-expose-wasm --jitless";
+}
+#endif  // PDF_ENABLE_V8
+
+#ifdef PDF_ENABLE_XFA
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* bstr) {
+  if (!bstr)
+    return -1;
+
+  bstr->str = nullptr;
+  bstr->len = 0;
+  return 0;
+}
+
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr,
+                                                    const char* cstr,
+                                                    int length) {
+  if (!bstr || !cstr)
+    return -1;
+
+  if (length == -1)
+    length = strlen(cstr);
+
+  if (length == 0) {
+    FPDF_BStr_Clear(bstr);
+    return 0;
+  }
+
+  if (bstr->str && bstr->len < length)
+    bstr->str = FX_Realloc(char, bstr->str, length + 1);
+  else if (!bstr->str)
+    bstr->str = FX_Alloc(char, length + 1);
+
+  bstr->str[length] = 0;
+  memcpy(bstr->str, cstr, length);
+  bstr->len = length;
+  return 0;
+}
+
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* bstr) {
+  if (!bstr)
+    return -1;
+
+  if (bstr->str) {
+    FX_Free(bstr->str);
+    bstr->str = nullptr;
+  }
+  bstr->len = 0;
+  return 0;
+}
+#endif  // PDF_ENABLE_XFA
+
+FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document,
+                                                      int index,
+                                                      void* buffer,
+                                                      long* buflen) {
+  if (!buffer)
+    *buflen = 0;
+
+  if (index < 0)
+    return nullptr;
+
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
+  if (!pRoot)
+    return nullptr;
+
+  CPDF_Object* pDestObj = nullptr;
+  WideString wsName;
+  CPDF_NameTree nameTree(pDoc, "Dests");
+  int count = nameTree.GetCount();
+  if (index >= count) {
+    const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
+    if (!pDest)
+      return nullptr;
+
+    pdfium::base::CheckedNumeric<int> checked_count = count;
+    checked_count += pDest->size();
+    if (!checked_count.IsValid() || index >= checked_count.ValueOrDie())
+      return nullptr;
+
+    index -= count;
+    int i = 0;
+    ByteStringView bsName;
+    CPDF_DictionaryLocker locker(pDest);
+    for (const auto& it : locker) {
+      bsName = it.first.AsStringView();
+      pDestObj = it.second.Get();
+      if (!pDestObj)
+        continue;
+      if (i == index)
+        break;
+      i++;
+    }
+    wsName = PDF_DecodeText(bsName.raw_span());
+  } else {
+    pDestObj = nameTree.LookupValueAndName(index, &wsName);
+  }
+  if (!pDestObj)
+    return nullptr;
+  if (CPDF_Dictionary* pDict = pDestObj->AsDictionary()) {
+    pDestObj = pDict->GetArrayFor("D");
+    if (!pDestObj)
+      return nullptr;
+  }
+  if (!pDestObj->IsArray())
+    return nullptr;
+
+  ByteString utf16Name = wsName.ToUTF16LE();
+  int len = utf16Name.GetLength();
+  if (!buffer) {
+    *buflen = len;
+  } else if (len <= *buflen) {
+    memcpy(buffer, utf16Name.c_str(), len);
+    *buflen = len;
+  } else {
+    *buflen = -1;
+  }
+  return FPDFDestFromCPDFArray(pDestObj->AsArray());
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
new file mode 100644
index 0000000..94cd3ce
--- /dev/null
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -0,0 +1,454 @@
+// Copyright 2015 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.
+
+// This "C" (not "C++") file ensures that the public headers compile
+// and link for "C" (and not just "C++").
+
+#include <stdio.h>
+
+#include "fpdfsdk/fpdf_view_c_api_test.h"
+
+#include "public/fpdf_annot.h"
+#include "public/fpdf_attachment.h"
+#include "public/fpdf_catalog.h"
+#include "public/fpdf_dataavail.h"
+#include "public/fpdf_doc.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdf_ext.h"
+#include "public/fpdf_flatten.h"
+#include "public/fpdf_formfill.h"
+#include "public/fpdf_fwlevent.h"
+#include "public/fpdf_javascript.h"
+#include "public/fpdf_ppo.h"
+#include "public/fpdf_progressive.h"
+#include "public/fpdf_save.h"
+#include "public/fpdf_searchex.h"
+#include "public/fpdf_structtree.h"
+#include "public/fpdf_sysfontinfo.h"
+#include "public/fpdf_text.h"
+#include "public/fpdf_thumbnail.h"
+#include "public/fpdf_transformpage.h"
+#include "public/fpdfview.h"
+
+// Scheme for avoiding LTO out of existence, warnings, etc.
+typedef void (*fnptr)(void);  // Legal generic function type for casts.
+fnptr g_c_api_test_fnptr = NULL;  // Extern, so can't know it doesn't change.
+#define CHK(x) if ((fnptr)(x) == g_c_api_test_fnptr) return 0
+
+// Function to call from gtest harness to ensure linker resolution.
+int CheckPDFiumCApi() {
+    // fpdf_annot.h
+    CHK(FPDFAnnot_AppendAttachmentPoints);
+    CHK(FPDFAnnot_AppendObject);
+    CHK(FPDFAnnot_CountAttachmentPoints);
+    CHK(FPDFAnnot_GetAP);
+    CHK(FPDFAnnot_GetAttachmentPoints);
+    CHK(FPDFAnnot_GetColor);
+    CHK(FPDFAnnot_GetFlags);
+    CHK(FPDFAnnot_GetFontSize);
+    CHK(FPDFAnnot_GetFormFieldAtPoint);
+    CHK(FPDFAnnot_GetFormFieldFlags);
+    CHK(FPDFAnnot_GetFormFieldName);
+    CHK(FPDFAnnot_GetFormFieldType);
+    CHK(FPDFAnnot_GetFormFieldValue);
+    CHK(FPDFAnnot_GetLinkedAnnot);
+    CHK(FPDFAnnot_GetNumberValue);
+    CHK(FPDFAnnot_GetObject);
+    CHK(FPDFAnnot_GetObjectCount);
+    CHK(FPDFAnnot_GetOptionCount);
+    CHK(FPDFAnnot_GetOptionLabel);
+    CHK(FPDFAnnot_GetRect);
+    CHK(FPDFAnnot_GetStringValue);
+    CHK(FPDFAnnot_GetSubtype);
+    CHK(FPDFAnnot_GetValueType);
+    CHK(FPDFAnnot_HasAttachmentPoints);
+    CHK(FPDFAnnot_HasKey);
+    CHK(FPDFAnnot_IsChecked);
+    CHK(FPDFAnnot_IsObjectSupportedSubtype);
+    CHK(FPDFAnnot_IsSupportedSubtype);
+    CHK(FPDFAnnot_RemoveObject);
+    CHK(FPDFAnnot_SetAP);
+    CHK(FPDFAnnot_SetAttachmentPoints);
+    CHK(FPDFAnnot_SetColor);
+    CHK(FPDFAnnot_SetFlags);
+    CHK(FPDFAnnot_SetRect);
+    CHK(FPDFAnnot_SetStringValue);
+    CHK(FPDFAnnot_UpdateObject);
+    CHK(FPDFPage_CloseAnnot);
+    CHK(FPDFPage_CreateAnnot);
+    CHK(FPDFPage_GetAnnot);
+    CHK(FPDFPage_GetAnnotCount);
+    CHK(FPDFPage_GetAnnotIndex);
+    CHK(FPDFPage_RemoveAnnot);
+
+    // fpdf_attachment.h
+    CHK(FPDFAttachment_GetFile);
+    CHK(FPDFAttachment_GetName);
+    CHK(FPDFAttachment_GetStringValue);
+    CHK(FPDFAttachment_GetValueType);
+    CHK(FPDFAttachment_HasKey);
+    CHK(FPDFAttachment_SetFile);
+    CHK(FPDFAttachment_SetStringValue);
+    CHK(FPDFDoc_AddAttachment);
+    CHK(FPDFDoc_DeleteAttachment);
+    CHK(FPDFDoc_GetAttachment);
+    CHK(FPDFDoc_GetAttachmentCount);
+
+    // fpdf_catalog.h
+    CHK(FPDFCatalog_IsTagged);
+
+    // fpdf_dataavail.h
+    CHK(FPDFAvail_Create);
+    CHK(FPDFAvail_Destroy);
+    CHK(FPDFAvail_GetDocument);
+    CHK(FPDFAvail_GetFirstPageNum);
+    CHK(FPDFAvail_IsDocAvail);
+    CHK(FPDFAvail_IsFormAvail);
+    CHK(FPDFAvail_IsLinearized);
+    CHK(FPDFAvail_IsPageAvail);
+
+    // fpdf_doc.h
+    CHK(FPDFAction_GetDest);
+    CHK(FPDFAction_GetFilePath);
+    CHK(FPDFAction_GetType);
+    CHK(FPDFAction_GetURIPath);
+    CHK(FPDFBookmark_Find);
+    CHK(FPDFBookmark_GetAction);
+    CHK(FPDFBookmark_GetDest);
+    CHK(FPDFBookmark_GetFirstChild);
+    CHK(FPDFBookmark_GetNextSibling);
+    CHK(FPDFBookmark_GetTitle);
+    CHK(FPDFDest_GetDestPageIndex);
+    CHK(FPDFDest_GetLocationInPage);
+    CHK(FPDFDest_GetView);
+    CHK(FPDFLink_CountQuadPoints);
+    CHK(FPDFLink_Enumerate);
+    CHK(FPDFLink_GetAction);
+    CHK(FPDFLink_GetAnnotRect);
+    CHK(FPDFLink_GetDest);
+    CHK(FPDFLink_GetLinkAtPoint);
+    CHK(FPDFLink_GetLinkZOrderAtPoint);
+    CHK(FPDFLink_GetQuadPoints);
+    CHK(FPDF_GetMetaText);
+    CHK(FPDF_GetPageLabel);
+
+    // fpdf_edit.h
+    CHK(FPDFFont_Close);
+    CHK(FPDFFormObj_CountObjects);
+    CHK(FPDFFormObj_GetMatrix);
+    CHK(FPDFFormObj_GetObject);
+    CHK(FPDFImageObj_GetBitmap);
+    CHK(FPDFImageObj_GetImageDataDecoded);
+    CHK(FPDFImageObj_GetImageDataRaw);
+    CHK(FPDFImageObj_GetImageFilter);
+    CHK(FPDFImageObj_GetImageFilterCount);
+    CHK(FPDFImageObj_GetImageMetadata);
+    CHK(FPDFImageObj_GetMatrix);
+    CHK(FPDFImageObj_LoadJpegFile);
+    CHK(FPDFImageObj_LoadJpegFileInline);
+    CHK(FPDFImageObj_SetBitmap);
+    CHK(FPDFImageObj_SetMatrix);
+    CHK(FPDFPageObjMark_CountParams);
+    CHK(FPDFPageObjMark_GetName);
+    CHK(FPDFPageObjMark_GetParamBlobValue);
+    CHK(FPDFPageObjMark_GetParamIntValue);
+    CHK(FPDFPageObjMark_GetParamKey);
+    CHK(FPDFPageObjMark_GetParamStringValue);
+    CHK(FPDFPageObjMark_GetParamValueType);
+    CHK(FPDFPageObjMark_RemoveParam);
+    CHK(FPDFPageObjMark_SetBlobParam);
+    CHK(FPDFPageObjMark_SetIntParam);
+    CHK(FPDFPageObjMark_SetStringParam);
+    CHK(FPDFPageObj_AddMark);
+    CHK(FPDFPageObj_CountMarks);
+    CHK(FPDFPageObj_CreateNewPath);
+    CHK(FPDFPageObj_CreateNewRect);
+    CHK(FPDFPageObj_CreateTextObj);
+    CHK(FPDFPageObj_Destroy);
+    CHK(FPDFPageObj_GetBounds);
+    CHK(FPDFPageObj_GetFillColor);
+    CHK(FPDFPageObj_GetLineCap);
+    CHK(FPDFPageObj_GetLineJoin);
+    CHK(FPDFPageObj_GetMark);
+    CHK(FPDFPageObj_GetStrokeColor);
+    CHK(FPDFPageObj_GetStrokeWidth);
+    CHK(FPDFPageObj_GetType);
+    CHK(FPDFPageObj_HasTransparency);
+    CHK(FPDFPageObj_NewImageObj);
+    CHK(FPDFPageObj_NewTextObj);
+    CHK(FPDFPageObj_RemoveMark);
+    CHK(FPDFPageObj_SetBlendMode);
+    CHK(FPDFPageObj_SetFillColor);
+    CHK(FPDFPageObj_SetLineCap);
+    CHK(FPDFPageObj_SetLineJoin);
+    CHK(FPDFPageObj_SetStrokeColor);
+    CHK(FPDFPageObj_SetStrokeWidth);
+    CHK(FPDFPageObj_Transform);
+    CHK(FPDFPage_CountObjects);
+    CHK(FPDFPage_Delete);
+    CHK(FPDFPage_GenerateContent);
+    CHK(FPDFPage_GetObject);
+    CHK(FPDFPage_GetRotation);
+    CHK(FPDFPage_HasTransparency);
+    CHK(FPDFPage_InsertObject);
+    CHK(FPDFPage_New);
+    CHK(FPDFPage_RemoveObject);
+    CHK(FPDFPage_SetRotation);
+    CHK(FPDFPage_TransformAnnots);
+    CHK(FPDFPathSegment_GetClose);
+    CHK(FPDFPathSegment_GetPoint);
+    CHK(FPDFPathSegment_GetType);
+    CHK(FPDFPath_BezierTo);
+    CHK(FPDFPath_Close);
+    CHK(FPDFPath_CountSegments);
+    CHK(FPDFPath_GetDrawMode);
+    CHK(FPDFPath_GetMatrix);
+    CHK(FPDFPath_GetPathSegment);
+    CHK(FPDFPath_LineTo);
+    CHK(FPDFPath_MoveTo);
+    CHK(FPDFPath_SetDrawMode);
+    CHK(FPDFPath_SetMatrix);
+    CHK(FPDFTextObj_GetFontName);
+    CHK(FPDFTextObj_GetFontSize);
+    CHK(FPDFTextObj_GetMatrix);
+    CHK(FPDFTextObj_GetText);
+    CHK(FPDFTextObj_GetTextRenderMode);
+    CHK(FPDFTextObj_SetTextRenderMode);
+    CHK(FPDFText_LoadFont);
+    CHK(FPDFText_LoadStandardFont);
+    CHK(FPDFText_SetText);
+    CHK(FPDF_CreateNewDocument);
+
+    // fpdf_ext.h
+    CHK(FPDFDoc_GetPageMode);
+    CHK(FSDK_SetLocaltimeFunction);
+    CHK(FSDK_SetTimeFunction);
+    CHK(FSDK_SetUnSpObjProcessHandler);
+
+    // fpdf_flatten.h
+    CHK(FPDFPage_Flatten);
+
+    // fpdf_fwlevent.h - no exports.
+
+    // fpdf_formfill.h
+    CHK(FORM_CanRedo);
+    CHK(FORM_CanUndo);
+    CHK(FORM_DoDocumentAAction);
+    CHK(FORM_DoDocumentJSAction);
+    CHK(FORM_DoDocumentOpenAction);
+    CHK(FORM_DoPageAAction);
+    CHK(FORM_ForceToKillFocus);
+    CHK(FORM_GetFocusedText);
+    CHK(FORM_GetSelectedText);
+    CHK(FORM_IsIndexSelected);
+    CHK(FORM_OnAfterLoadPage);
+    CHK(FORM_OnBeforeClosePage);
+    CHK(FORM_OnChar);
+    CHK(FORM_OnFocus);
+    CHK(FORM_OnKeyDown);
+    CHK(FORM_OnKeyUp);
+    CHK(FORM_OnLButtonDoubleClick);
+    CHK(FORM_OnLButtonDown);
+    CHK(FORM_OnLButtonUp);
+    CHK(FORM_OnMouseMove);
+    CHK(FORM_OnRButtonDown);
+    CHK(FORM_OnRButtonUp);
+    CHK(FORM_Redo);
+    CHK(FORM_ReplaceSelection);
+    CHK(FORM_SetIndexSelected);
+    CHK(FORM_Undo);
+    CHK(FPDFDOC_ExitFormFillEnvironment);
+    CHK(FPDFDOC_InitFormFillEnvironment);
+    CHK(FPDFPage_FormFieldZOrderAtPoint);
+    CHK(FPDFPage_HasFormFieldAtPoint);
+    CHK(FPDF_FFLDraw);
+#ifdef _SKIA_SUPPORT_
+    CHK(FPDF_FFLRecord);
+#endif
+    CHK(FPDF_GetFormType);
+    CHK(FPDF_LoadXFA);
+    CHK(FPDF_RemoveFormFieldHighlight);
+    CHK(FPDF_SetFormFieldHighlightAlpha);
+    CHK(FPDF_SetFormFieldHighlightColor);
+
+    // fpdf_javascript.h
+    CHK(FPDFDoc_CloseJavaScriptAction);
+    CHK(FPDFDoc_GetJavaScriptAction);
+    CHK(FPDFDoc_GetJavaScriptActionCount);
+    CHK(FPDFJavaScriptAction_GetName);
+    CHK(FPDFJavaScriptAction_GetScript);
+
+    // fpdf_ppo.h
+    CHK(FPDF_CopyViewerPreferences);
+    CHK(FPDF_ImportNPagesToOne);
+    CHK(FPDF_ImportPages);
+
+    // fpdf_progressive.h
+    CHK(FPDF_RenderPageBitmap_Start);
+    CHK(FPDF_RenderPage_Close);
+    CHK(FPDF_RenderPage_Continue);
+
+    // fpdf_save.h
+    CHK(FPDF_SaveAsCopy);
+    CHK(FPDF_SaveWithVersion);
+
+    // fpdf_searchex.h
+    CHK(FPDFText_GetCharIndexFromTextIndex);
+    CHK(FPDFText_GetTextIndexFromCharIndex);
+
+    // fpdf_structtree.h
+    CHK(FPDF_StructElement_CountChildren);
+    CHK(FPDF_StructElement_GetAltText);
+    CHK(FPDF_StructElement_GetChildAtIndex);
+    CHK(FPDF_StructElement_GetMarkedContentID);
+    CHK(FPDF_StructElement_GetTitle);
+    CHK(FPDF_StructElement_GetType);
+    CHK(FPDF_StructTree_Close);
+    CHK(FPDF_StructTree_CountChildren);
+    CHK(FPDF_StructTree_GetChildAtIndex);
+    CHK(FPDF_StructTree_GetForPage);
+
+    // fpdf_sysfontinfo.h
+    CHK(FPDF_AddInstalledFont);
+    CHK(FPDF_FreeDefaultSystemFontInfo);
+    CHK(FPDF_GetDefaultSystemFontInfo);
+    CHK(FPDF_GetDefaultTTFMap);
+    CHK(FPDF_SetSystemFontInfo);
+
+    // fpdf_text.h
+    CHK(FPDFLink_CloseWebLinks);
+    CHK(FPDFLink_CountRects);
+    CHK(FPDFLink_CountWebLinks);
+    CHK(FPDFLink_GetRect);
+    CHK(FPDFLink_GetTextRange);
+    CHK(FPDFLink_GetURL);
+    CHK(FPDFLink_LoadWebLinks);
+    CHK(FPDFText_ClosePage);
+    CHK(FPDFText_CountChars);
+    CHK(FPDFText_CountRects);
+    CHK(FPDFText_FindClose);
+    CHK(FPDFText_FindNext);
+    CHK(FPDFText_FindPrev);
+    CHK(FPDFText_FindStart);
+    CHK(FPDFText_GetBoundedText);
+    CHK(FPDFText_GetCharAngle);
+    CHK(FPDFText_GetCharBox);
+    CHK(FPDFText_GetCharIndexAtPos);
+    CHK(FPDFText_GetCharOrigin);
+    CHK(FPDFText_GetFillColor);
+    CHK(FPDFText_GetFontInfo);
+    CHK(FPDFText_GetFontSize);
+    CHK(FPDFText_GetFontWeight);
+    CHK(FPDFText_GetLooseCharBox);
+    CHK(FPDFText_GetMatrix);
+    CHK(FPDFText_GetRect);
+    CHK(FPDFText_GetSchCount);
+    CHK(FPDFText_GetSchResultIndex);
+    CHK(FPDFText_GetStrokeColor);
+    CHK(FPDFText_GetText);
+    CHK(FPDFText_GetTextRenderMode);
+    CHK(FPDFText_GetUnicode);
+    CHK(FPDFText_LoadPage);
+
+    // fpdf_thumbnail.h
+    CHK(FPDFPage_GetDecodedThumbnailData);
+    CHK(FPDFPage_GetRawThumbnailData);
+    CHK(FPDFPage_GetThumbnailAsBitmap);
+
+    // fpdf_transformpage.h
+    CHK(FPDFClipPath_CountPathSegments);
+    CHK(FPDFClipPath_CountPaths);
+    CHK(FPDFClipPath_GetPathSegment);
+    CHK(FPDFPageObj_GetClipPath);
+    CHK(FPDFPageObj_TransformClipPath);
+    CHK(FPDFPage_GetArtBox);
+    CHK(FPDFPage_GetBleedBox);
+    CHK(FPDFPage_GetCropBox);
+    CHK(FPDFPage_GetMediaBox);
+    CHK(FPDFPage_GetTrimBox);
+    CHK(FPDFPage_InsertClipPath);
+    CHK(FPDFPage_SetArtBox);
+    CHK(FPDFPage_SetBleedBox);
+    CHK(FPDFPage_SetCropBox);
+    CHK(FPDFPage_SetMediaBox);
+    CHK(FPDFPage_SetTrimBox);
+    CHK(FPDFPage_TransFormWithClip);
+    CHK(FPDF_CreateClipPath);
+    CHK(FPDF_DestroyClipPath);
+
+    // fpdfview.h
+    CHK(FPDFBitmap_Create);
+    CHK(FPDFBitmap_CreateEx);
+    CHK(FPDFBitmap_Destroy);
+    CHK(FPDFBitmap_FillRect);
+    CHK(FPDFBitmap_GetBuffer);
+    CHK(FPDFBitmap_GetFormat);
+    CHK(FPDFBitmap_GetHeight);
+    CHK(FPDFBitmap_GetStride);
+    CHK(FPDFBitmap_GetWidth);
+#ifdef PDF_ENABLE_XFA
+    CHK(FPDF_BStr_Clear);
+    CHK(FPDF_BStr_Init);
+    CHK(FPDF_BStr_Set);
+#endif
+    CHK(FPDF_CloseDocument);
+    CHK(FPDF_ClosePage);
+    CHK(FPDF_CountNamedDests);
+    CHK(FPDF_DestroyLibrary);
+    CHK(FPDF_DeviceToPage);
+    CHK(FPDF_DocumentHasValidCrossReferenceTable);
+    CHK(FPDF_GetDocPermissions);
+    CHK(FPDF_GetFileVersion);
+    CHK(FPDF_GetLastError);
+    CHK(FPDF_GetNamedDest);
+    CHK(FPDF_GetNamedDestByName);
+    CHK(FPDF_GetPageBoundingBox);
+    CHK(FPDF_GetPageCount);
+    CHK(FPDF_GetPageHeight);
+    CHK(FPDF_GetPageHeightF);
+    CHK(FPDF_GetPageSizeByIndex);
+    CHK(FPDF_GetPageSizeByIndexF);
+    CHK(FPDF_GetPageWidth);
+    CHK(FPDF_GetPageWidthF);
+#ifdef PDF_ENABLE_V8
+    CHK(FPDF_GetRecommendedV8Flags);
+#endif
+    CHK(FPDF_GetSecurityHandlerRevision);
+    CHK(FPDF_InitLibrary);
+    CHK(FPDF_InitLibraryWithConfig);
+    CHK(FPDF_LoadCustomDocument);
+    CHK(FPDF_LoadDocument);
+    CHK(FPDF_LoadMemDocument);
+    CHK(FPDF_LoadPage);
+    CHK(FPDF_PageToDevice);
+#ifdef _WIN32
+    CHK(FPDF_RenderPage);
+#endif
+    CHK(FPDF_RenderPageBitmap);
+    CHK(FPDF_RenderPageBitmapWithMatrix);
+#ifdef _SKIA_SUPPORT_
+    CHK(FPDF_RenderPageSkp);
+#endif
+#if defined(_WIN32)
+    CHK(FPDF_SetPrintMode);
+#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
+    CHK(FPDF_SetPrintTextWithGDI);
+#endif
+#endif
+    CHK(FPDF_SetSandBoxPolicy);
+#if defined(_WIN32) && defined(PDFIUM_PRINT_TEXT_WITH_GDI)
+    CHK(FPDF_SetTypefaceAccessibleFunc);
+#endif
+    CHK(FPDF_VIEWERREF_GetDuplex);
+    CHK(FPDF_VIEWERREF_GetName);
+    CHK(FPDF_VIEWERREF_GetNumCopies);
+    CHK(FPDF_VIEWERREF_GetPrintPageRange);
+    CHK(FPDF_VIEWERREF_GetPrintPageRangeCount);
+    CHK(FPDF_VIEWERREF_GetPrintPageRangeElement);
+    CHK(FPDF_VIEWERREF_GetPrintScaling);
+
+    return 1;
+}
+
+#undef CHK
diff --git a/fpdfsdk/fpdf_view_c_api_test.h b/fpdfsdk/fpdf_view_c_api_test.h
new file mode 100644
index 0000000..c662f40
--- /dev/null
+++ b/fpdfsdk/fpdf_view_c_api_test.h
@@ -0,0 +1,20 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FPDFSDK_FPDF_VIEW_C_API_TEST_H_
+#define FPDFSDK_FPDF_VIEW_C_API_TEST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Function to call from gtest harness to ensure linker resolution. Returns
+// 1 on success or 0 on error.
+int CheckPDFiumCApi();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // FPDFSDK_FPDF_VIEW_C_API_TEST_H_
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
new file mode 100644
index 0000000..fe20970
--- /dev/null
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -0,0 +1,1346 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/fpdf_view_c_api_test.h"
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/file_util.h"
+#include "testing/utils/path_service.h"
+
+namespace {
+
+#if defined(OS_WIN)
+const char kExpectedRectanglePostScript[] = R"(
+save
+/im/initmatrix load def
+/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load def/h/closepath load def
+/f/fill load def/F/eofill load def/s/stroke load def/W/clip load def/W*/eoclip load def
+/rg/setrgbcolor load def/k/setcmykcolor load def
+/J/setlinecap load def/j/setlinejoin load def/w/setlinewidth load def/M/setmiterlimit load def/d/setdash load def
+/q/gsave load def/Q/grestore load def/iM/imagemask load def
+/Tj/show load def/Ff/findfont load def/Fs/scalefont load def/Sf/setfont load def
+/cm/concat load def/Cm/currentmatrix load def/mx/matrix load def/sm/setmatrix load def
+0 300 m 0 0 l 200 0 l 200 300 l 0 300 l h W n
+q
+0 300 m 0 0 l 200 0 l 200 300 l 0 300 l h W n
+q
+0 J
+[]0 d
+0 j
+1 w
+10 M
+mx Cm [1 0 0 -1 0 300]cm 0 290 m 10 290 l 10 300 l 0 300 l 0 290 l h 0 0 0 rg
+q F Q s sm
+mx Cm [1 0 0 -1 0 300]cm 10 150 m 60 150 l 60 180 l 10 180 l 10 150 l h q F Q s sm
+mx Cm [1 0 0 -1 0 300]cm 190 290 m 200 290 l 200 300 l 190 300 l 190 290 l h 0 0 1 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 70 232 m 120 232 l 120 262 l 70 262 l 70 232 l h 0 0 1 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 190 0 m 200 0 l 200 10 l 190 10 l 190 0 l h 0 1 0 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 130 150 m 180 150 l 180 180 l 130 180 l 130 150 l h 0 1 0 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 0 0 m 10 0 l 10 10 l 0 10 l 0 0 l h 1 0 0 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 70 67 m 120 67 l 120 97 l 70 97 l 70 67 l h 1 0 0 rg
+q F Q 0 0 0 rg
+s sm
+Q
+Q
+Q
+
+restore
+)";
+#endif  // defined(OS_WIN)
+
+class MockDownloadHints final : public FX_DOWNLOADHINTS {
+ public:
+  static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
+  }
+
+  MockDownloadHints() {
+    FX_DOWNLOADHINTS::version = 1;
+    FX_DOWNLOADHINTS::AddSegment = SAddSegment;
+  }
+
+  ~MockDownloadHints() {}
+};
+
+}  // namespace
+
+TEST(fpdf, CApiTest) {
+  EXPECT_TRUE(CheckPDFiumCApi());
+}
+
+class FPDFViewEmbedderTest : public EmbedderTest {
+ protected:
+  void TestRenderPageBitmapWithMatrix(FPDF_PAGE page,
+                                      int bitmap_width,
+                                      int bitmap_height,
+                                      const FS_MATRIX& matrix,
+                                      const FS_RECTF& rect,
+                                      const char* expected_md5) {
+    ScopedFPDFBitmap bitmap(FPDFBitmap_Create(bitmap_width, bitmap_height, 0));
+    FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height,
+                        0xFFFFFFFF);
+    FPDF_RenderPageBitmapWithMatrix(bitmap.get(), page, &matrix, &rect, 0);
+    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+  }
+
+  void TestRenderPageBitmapWithFlags(FPDF_PAGE page,
+                                     int flags,
+                                     const char* expected_md5) {
+    int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
+    int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
+    ScopedFPDFBitmap bitmap(FPDFBitmap_Create(bitmap_width, bitmap_height, 0));
+    FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height,
+                        0xFFFFFFFF);
+    FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height,
+                          0, flags);
+    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+  }
+
+  void TestRenderPageBitmapWithExternalMemory(FPDF_PAGE page,
+                                              int format,
+                                              const char* expected_md5) {
+    int bitmap_width = static_cast<int>(FPDF_GetPageWidth(page));
+    int bitmap_height = static_cast<int>(FPDF_GetPageHeight(page));
+    int bytes_per_pixel = BytesPerPixelForFormat(format);
+    ASSERT_NE(0, bytes_per_pixel);
+
+    int bitmap_stride = bytes_per_pixel * bitmap_width;
+    std::vector<uint8_t> external_memory(bitmap_stride * bitmap_height);
+    ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx(bitmap_width, bitmap_height,
+                                                format, external_memory.data(),
+                                                bitmap_stride));
+    FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height,
+                        0xFFFFFFFF);
+    FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height,
+                          0, 0);
+    CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5);
+  }
+};
+
+// Test for conversion of a point in device coordinates to page coordinates
+TEST_F(FPDFViewEmbedderTest, DeviceCoordinatesToPageCoordinates) {
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+
+  // Error tolerance for floating point comparison
+  const double kTolerance = 0.0001;
+
+  // Display bounds in device coordinates
+  int start_x = 0;
+  int start_y = 0;
+  int size_x = 640;
+  int size_y = 480;
+
+  // Page Orientation normal
+  int rotate = 0;
+
+  // Device coordinate to be converted
+  int device_x = 10;
+  int device_y = 10;
+
+  double page_x = 0.0;
+  double page_y = 0.0;
+  EXPECT_TRUE(FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate,
+                                device_x, device_y, &page_x, &page_y));
+  EXPECT_NEAR(9.5625, page_x, kTolerance);
+  EXPECT_NEAR(775.5, page_y, kTolerance);
+
+  // Rotate 90 degrees clockwise
+  rotate = 1;
+  EXPECT_TRUE(FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate,
+                                device_x, device_y, &page_x, &page_y));
+  EXPECT_NEAR(12.75, page_x, kTolerance);
+  EXPECT_NEAR(12.375, page_y, kTolerance);
+
+  // Rotate 180 degrees
+  rotate = 2;
+  EXPECT_TRUE(FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate,
+                                device_x, device_y, &page_x, &page_y));
+  EXPECT_NEAR(602.4374, page_x, kTolerance);
+  EXPECT_NEAR(16.5, page_y, kTolerance);
+
+  // Rotate 90 degrees counter-clockwise
+  rotate = 3;
+  EXPECT_TRUE(FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate,
+                                device_x, device_y, &page_x, &page_y));
+  EXPECT_NEAR(599.25, page_x, kTolerance);
+  EXPECT_NEAR(779.625, page_y, kTolerance);
+
+  // FPDF_DeviceToPage() converts |rotate| into legal rotation by taking
+  // modulo by 4. A value of 4 is expected to be converted into 0 (normal
+  // rotation)
+  rotate = 4;
+  EXPECT_TRUE(FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate,
+                                device_x, device_y, &page_x, &page_y));
+  EXPECT_NEAR(9.5625, page_x, kTolerance);
+  EXPECT_NEAR(775.5, page_y, kTolerance);
+
+  // FPDF_DeviceToPage returns untransformed coordinates if |rotate| % 4 is
+  // negative.
+  rotate = -1;
+  EXPECT_TRUE(FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate,
+                                device_x, device_y, &page_x, &page_y));
+  EXPECT_NEAR(device_x, page_x, kTolerance);
+  EXPECT_NEAR(device_y, page_y, kTolerance);
+
+  // Negative case - invalid page
+  page_x = 1234.0;
+  page_y = 5678.0;
+  EXPECT_FALSE(FPDF_DeviceToPage(nullptr, start_x, start_y, size_x, size_y,
+                                 rotate, device_x, device_y, &page_x, &page_y));
+  // Out parameters are expected to remain unchanged
+  EXPECT_NEAR(1234.0, page_x, kTolerance);
+  EXPECT_NEAR(5678.0, page_y, kTolerance);
+
+  // Negative case - invalid output parameters
+  EXPECT_FALSE(FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate,
+                                 device_x, device_y, nullptr, nullptr));
+
+  UnloadPage(page);
+}
+
+// Test for conversion of a point in page coordinates to device coordinates.
+TEST_F(FPDFViewEmbedderTest, PageCoordinatesToDeviceCoordinates) {
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+
+  // Display bounds in device coordinates
+  int start_x = 0;
+  int start_y = 0;
+  int size_x = 640;
+  int size_y = 480;
+
+  // Page Orientation normal
+  int rotate = 0;
+
+  // Page coordinate to be converted
+  double page_x = 9.0;
+  double page_y = 775.0;
+
+  int device_x = 0;
+  int device_y = 0;
+  EXPECT_TRUE(FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate,
+                                page_x, page_y, &device_x, &device_y));
+
+  EXPECT_EQ(9, device_x);
+  EXPECT_EQ(10, device_y);
+
+  // Rotate 90 degrees clockwise
+  rotate = 1;
+  EXPECT_TRUE(FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate,
+                                page_x, page_y, &device_x, &device_y));
+  EXPECT_EQ(626, device_x);
+  EXPECT_EQ(7, device_y);
+
+  // Rotate 180 degrees
+  rotate = 2;
+  EXPECT_TRUE(FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate,
+                                page_x, page_y, &device_x, &device_y));
+  EXPECT_EQ(631, device_x);
+  EXPECT_EQ(470, device_y);
+
+  // Rotate 90 degrees counter-clockwise
+  rotate = 3;
+  EXPECT_TRUE(FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate,
+                                page_x, page_y, &device_x, &device_y));
+  EXPECT_EQ(14, device_x);
+  EXPECT_EQ(473, device_y);
+
+  // FPDF_PageToDevice() converts |rotate| into legal rotation by taking
+  // modulo by 4. A value of 4 is expected to be converted into 0 (normal
+  // rotation)
+  rotate = 4;
+  EXPECT_TRUE(FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate,
+                                page_x, page_y, &device_x, &device_y));
+  EXPECT_EQ(9, device_x);
+  EXPECT_EQ(10, device_y);
+
+  // FPDF_PageToDevice() returns untransformed coordinates if |rotate| % 4 is
+  // negative.
+  rotate = -1;
+  EXPECT_TRUE(FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate,
+                                page_x, page_y, &device_x, &device_y));
+  EXPECT_EQ(start_x, device_x);
+  EXPECT_EQ(start_y, device_y);
+
+  // Negative case - invalid page
+  device_x = 1234;
+  device_y = 5678;
+  EXPECT_FALSE(FPDF_PageToDevice(nullptr, start_x, start_y, size_x, size_y,
+                                 rotate, page_x, page_y, &device_x, &device_y));
+  // Out parameters are expected to remain unchanged
+  EXPECT_EQ(1234, device_x);
+  EXPECT_EQ(5678, device_y);
+
+  // Negative case - invalid output parameters
+  EXPECT_FALSE(FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate,
+                                 page_x, page_y, nullptr, nullptr));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFViewEmbedderTest, MultipleInitDestroy) {
+  FPDF_InitLibrary();  // Redundant given call in SetUp(), but safe.
+  FPDF_InitLibrary();  // Doubly-redundant even, but safe.
+
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  CloseDocument();
+  CloseDocument();  // Redundant given above, but safe.
+  CloseDocument();  // Doubly-redundant even, but safe.
+
+  FPDF_DestroyLibrary();  // Doubly-redundant even, but safe.
+  FPDF_DestroyLibrary();  // Redundant given call in TearDown(), but safe.
+}
+
+TEST_F(FPDFViewEmbedderTest, Document) {
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  EXPECT_EQ(1, GetPageCount());
+  EXPECT_EQ(0, GetFirstPageNum());
+
+  int version;
+  EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
+  EXPECT_EQ(14, version);
+
+  EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
+  EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
+}
+
+TEST_F(FPDFViewEmbedderTest, LoadNonexistentDocument) {
+  FPDF_DOCUMENT doc = FPDF_LoadDocument("nonexistent_document.pdf", "");
+  ASSERT_FALSE(doc);
+  EXPECT_EQ(static_cast<int>(FPDF_GetLastError()), FPDF_ERR_FILE);
+}
+
+// See https://crbug.com/pdfium/465
+TEST_F(FPDFViewEmbedderTest, EmptyDocument) {
+  EXPECT_TRUE(CreateEmptyDocument());
+  {
+    int version = 42;
+    EXPECT_FALSE(FPDF_GetFileVersion(document(), &version));
+    EXPECT_EQ(0, version);
+  }
+  {
+#ifdef PDF_ENABLE_XFA
+    const unsigned long kExpected = static_cast<uint32_t>(-1);
+#else   // PDF_ENABLE_XFA
+    const unsigned long kExpected = 0;
+#endif  // PDF_ENABLE_XFA
+    EXPECT_EQ(kExpected, FPDF_GetDocPermissions(document()));
+  }
+  EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
+  EXPECT_EQ(0, FPDF_GetPageCount(document()));
+  EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
+  EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document()));
+  EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
+
+  char buf[100];
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
+  EXPECT_EQ(0u, FPDF_CountNamedDests(document()));
+}
+
+TEST_F(FPDFViewEmbedderTest, SandboxDocument) {
+  uint16_t buf[200];
+  unsigned long len;
+
+  ASSERT_TRUE(CreateEmptyDocument());
+  len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
+  EXPECT_GT(len, 2u);  // Not just "double NUL" end-of-string indicator.
+  EXPECT_NE(0u, buf[0]);
+  CloseDocument();
+
+  FPDF_SetSandBoxPolicy(FPDF_POLICY_MACHINETIME_ACCESS, false);
+  ASSERT_TRUE(CreateEmptyDocument());
+  len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
+  EXPECT_EQ(2u, len);  // Only a "double NUL" end-of-string indicator.
+  EXPECT_EQ(0u, buf[0]);
+  CloseDocument();
+
+  constexpr unsigned long kNoSuchPolicy = 102;
+  FPDF_SetSandBoxPolicy(kNoSuchPolicy, true);
+  ASSERT_TRUE(CreateEmptyDocument());
+  len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
+  EXPECT_EQ(2u, len);  // Only a "double NUL" end-of-string indicator.
+  EXPECT_EQ(0u, buf[0]);
+  CloseDocument();
+
+  FPDF_SetSandBoxPolicy(FPDF_POLICY_MACHINETIME_ACCESS, true);
+  ASSERT_TRUE(CreateEmptyDocument());
+  len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf));
+  EXPECT_GT(len, 2u);  // Not just "double NUL" end-of-string indicator.
+  EXPECT_NE(0u, buf[0]);
+  CloseDocument();
+}
+
+TEST_F(FPDFViewEmbedderTest, LinearizedDocument) {
+  EXPECT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf"));
+  int version;
+  EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
+  EXPECT_EQ(16, version);
+}
+
+TEST_F(FPDFViewEmbedderTest, LoadCustomDocumentWithoutFileAccess) {
+  EXPECT_FALSE(FPDF_LoadCustomDocument(nullptr, ""));
+}
+
+// See https://crbug.com/pdfium/1261
+TEST_F(FPDFViewEmbedderTest, LoadCustomDocumentWithShortLivedFileAccess) {
+  std::string file_contents_string;  // Must outlive |doc|.
+  ScopedFPDFDocument doc;
+  {
+    // Read a PDF, and copy it into |file_contents_string|.
+    std::string pdf_path;
+    size_t pdf_length;
+    ASSERT_TRUE(PathService::GetTestFilePath("rectangles.pdf", &pdf_path));
+    auto file_contents = GetFileContents(pdf_path.c_str(), &pdf_length);
+    ASSERT_TRUE(file_contents);
+    for (size_t i = 0; i < pdf_length; ++i)
+      file_contents_string.push_back(file_contents.get()[i]);
+
+    // Define a FPDF_FILEACCESS object that will go out of scope, while the
+    // loaded document in |doc| remains valid.
+    FPDF_FILEACCESS file_access = {};
+    file_access.m_FileLen = pdf_length;
+    file_access.m_GetBlock = GetBlockFromString;
+    file_access.m_Param = &file_contents_string;
+    doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
+    ASSERT_TRUE(doc);
+  }
+
+  // Now try to access |doc| and make sure it still works.
+  ScopedFPDFPage page(FPDF_LoadPage(doc.get(), 0));
+  ASSERT_TRUE(page);
+  EXPECT_FLOAT_EQ(200.0f, FPDF_GetPageWidthF(page.get()));
+  EXPECT_FLOAT_EQ(300.0f, FPDF_GetPageHeightF(page.get()));
+}
+
+TEST_F(FPDFViewEmbedderTest, Page) {
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  EXPECT_FLOAT_EQ(612.0f, FPDF_GetPageWidthF(page));
+  EXPECT_FLOAT_EQ(792.0f, FPDF_GetPageHeightF(page));
+
+  FS_RECTF rect;
+  EXPECT_TRUE(FPDF_GetPageBoundingBox(page, &rect));
+  EXPECT_EQ(0.0, rect.left);
+  EXPECT_EQ(0.0, rect.bottom);
+  EXPECT_EQ(612.0, rect.right);
+  EXPECT_EQ(792.0, rect.top);
+
+  // Null arguments return errors rather than crashing,
+  EXPECT_EQ(0.0, FPDF_GetPageWidth(nullptr));
+  EXPECT_EQ(0.0, FPDF_GetPageHeight(nullptr));
+  EXPECT_FALSE(FPDF_GetPageBoundingBox(nullptr, &rect));
+  EXPECT_FALSE(FPDF_GetPageBoundingBox(page, nullptr));
+
+  UnloadPage(page);
+  EXPECT_FALSE(LoadPage(1));
+}
+
+TEST_F(FPDFViewEmbedderTest, ViewerRefDummy) {
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
+  EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document()));
+  EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
+
+  char buf[100];
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
+
+  FPDF_PAGERANGE page_range = FPDF_VIEWERREF_GetPrintPageRange(document());
+  EXPECT_FALSE(page_range);
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetPrintPageRangeCount(page_range));
+  EXPECT_EQ(-1, FPDF_VIEWERREF_GetPrintPageRangeElement(page_range, 0));
+  EXPECT_EQ(-1, FPDF_VIEWERREF_GetPrintPageRangeElement(page_range, 1));
+}
+
+TEST_F(FPDFViewEmbedderTest, ViewerRef) {
+  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
+  EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
+  EXPECT_EQ(5, FPDF_VIEWERREF_GetNumCopies(document()));
+  EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
+
+  // Test some corner cases.
+  char buf[100];
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "", buf, sizeof(buf)));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
+
+  // Make sure |buf| does not get written into when it appears to be too small.
+  // NOLINTNEXTLINE(runtime/printf)
+  strcpy(buf, "ABCD");
+  EXPECT_EQ(4U, FPDF_VIEWERREF_GetName(document(), "Foo", buf, 1));
+  EXPECT_STREQ("ABCD", buf);
+
+  // Note "Foo" is a different key from "foo".
+  EXPECT_EQ(4U,
+            FPDF_VIEWERREF_GetName(document(), "Foo", nullptr, sizeof(buf)));
+  ASSERT_EQ(4U, FPDF_VIEWERREF_GetName(document(), "Foo", buf, sizeof(buf)));
+  EXPECT_STREQ("foo", buf);
+
+  // Try to retrieve a boolean and an integer.
+  EXPECT_EQ(
+      0U, FPDF_VIEWERREF_GetName(document(), "HideToolbar", buf, sizeof(buf)));
+  EXPECT_EQ(0U,
+            FPDF_VIEWERREF_GetName(document(), "NumCopies", buf, sizeof(buf)));
+
+  // Try more valid cases.
+  ASSERT_EQ(4U,
+            FPDF_VIEWERREF_GetName(document(), "Direction", buf, sizeof(buf)));
+  EXPECT_STREQ("R2L", buf);
+  ASSERT_EQ(8U,
+            FPDF_VIEWERREF_GetName(document(), "ViewArea", buf, sizeof(buf)));
+  EXPECT_STREQ("CropBox", buf);
+
+  FPDF_PAGERANGE page_range = FPDF_VIEWERREF_GetPrintPageRange(document());
+  EXPECT_TRUE(page_range);
+  EXPECT_EQ(4U, FPDF_VIEWERREF_GetPrintPageRangeCount(page_range));
+  EXPECT_EQ(0, FPDF_VIEWERREF_GetPrintPageRangeElement(page_range, 0));
+  EXPECT_EQ(2, FPDF_VIEWERREF_GetPrintPageRangeElement(page_range, 1));
+  EXPECT_EQ(4, FPDF_VIEWERREF_GetPrintPageRangeElement(page_range, 2));
+  EXPECT_EQ(4, FPDF_VIEWERREF_GetPrintPageRangeElement(page_range, 3));
+  EXPECT_EQ(-1, FPDF_VIEWERREF_GetPrintPageRangeElement(page_range, 4));
+}
+
+TEST_F(FPDFViewEmbedderTest, NamedDests) {
+  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+  long buffer_size;
+  char fixed_buffer[512];
+  FPDF_DEST dest;
+
+  // Query the size of the first item.
+  buffer_size = 2000000;  // Absurdly large, check not used for this case.
+  dest = FPDF_GetNamedDest(document(), 0, nullptr, &buffer_size);
+  EXPECT_NE(nullptr, dest);
+  EXPECT_EQ(12, buffer_size);
+
+  // Try to retrieve the first item with too small a buffer.
+  buffer_size = 10;
+  dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size);
+  EXPECT_NE(nullptr, dest);
+  EXPECT_EQ(-1, buffer_size);
+
+  // Try to retrieve the first item with correctly sized buffer. Item is
+  // taken from Dests NameTree in named_dests.pdf.
+  buffer_size = 12;
+  dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size);
+  EXPECT_NE(nullptr, dest);
+  EXPECT_EQ(12, buffer_size);
+  EXPECT_EQ(std::string("F\0i\0r\0s\0t\0\0\0", 12),
+            std::string(fixed_buffer, buffer_size));
+
+  // Try to retrieve the second item with ample buffer. Item is taken
+  // from Dests NameTree but has a sub-dictionary in named_dests.pdf.
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), 1, fixed_buffer, &buffer_size);
+  EXPECT_NE(nullptr, dest);
+  EXPECT_EQ(10, buffer_size);
+  EXPECT_EQ(std::string("N\0e\0x\0t\0\0\0", 10),
+            std::string(fixed_buffer, buffer_size));
+
+  // Try to retrieve third item with ample buffer. Item is taken
+  // from Dests NameTree but has a bad sub-dictionary in named_dests.pdf.
+  // in named_dests.pdf).
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), 2, fixed_buffer, &buffer_size);
+  EXPECT_EQ(nullptr, dest);
+  EXPECT_EQ(sizeof(fixed_buffer),
+            static_cast<size_t>(buffer_size));  // unmodified.
+
+  // Try to retrieve the forth item with ample buffer. Item is taken
+  // from Dests NameTree but has a vale of the wrong type in named_dests.pdf.
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), 3, fixed_buffer, &buffer_size);
+  EXPECT_EQ(nullptr, dest);
+  EXPECT_EQ(sizeof(fixed_buffer),
+            static_cast<size_t>(buffer_size));  // unmodified.
+
+  // Try to retrieve fifth item with ample buffer. Item taken from the
+  // old-style Dests dictionary object in named_dests.pdf.
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), 4, fixed_buffer, &buffer_size);
+  EXPECT_NE(nullptr, dest);
+  EXPECT_EQ(30, buffer_size);
+  EXPECT_EQ(std::string("F\0i\0r\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 30),
+            std::string(fixed_buffer, buffer_size));
+
+  // Try to retrieve sixth item with ample buffer. Item istaken from the
+  // old-style Dests dictionary object but has a sub-dictionary in
+  // named_dests.pdf.
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), 5, fixed_buffer, &buffer_size);
+  EXPECT_NE(nullptr, dest);
+  EXPECT_EQ(28, buffer_size);
+  EXPECT_EQ(std::string("L\0a\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 28),
+            std::string(fixed_buffer, buffer_size));
+
+  // Try to retrieve non-existent item with ample buffer.
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), 6, fixed_buffer, &buffer_size);
+  EXPECT_EQ(nullptr, dest);
+  EXPECT_EQ(sizeof(fixed_buffer),
+            static_cast<size_t>(buffer_size));  // unmodified.
+
+  // Try to underflow/overflow the integer index.
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), std::numeric_limits<int>::max(),
+                           fixed_buffer, &buffer_size);
+  EXPECT_EQ(nullptr, dest);
+  EXPECT_EQ(sizeof(fixed_buffer),
+            static_cast<size_t>(buffer_size));  // unmodified.
+
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), std::numeric_limits<int>::min(),
+                           fixed_buffer, &buffer_size);
+  EXPECT_EQ(nullptr, dest);
+  EXPECT_EQ(sizeof(fixed_buffer),
+            static_cast<size_t>(buffer_size));  // unmodified.
+
+  buffer_size = sizeof(fixed_buffer);
+  dest = FPDF_GetNamedDest(document(), -1, fixed_buffer, &buffer_size);
+  EXPECT_EQ(nullptr, dest);
+  EXPECT_EQ(sizeof(fixed_buffer),
+            static_cast<size_t>(buffer_size));  // unmodified.
+}
+
+TEST_F(FPDFViewEmbedderTest, NamedDestsByName) {
+  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
+
+  // Null pointer returns nullptr.
+  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), nullptr);
+  EXPECT_EQ(nullptr, dest);
+
+  // Empty string returns nullptr.
+  dest = FPDF_GetNamedDestByName(document(), "");
+  EXPECT_EQ(nullptr, dest);
+
+  // Item from Dests NameTree.
+  dest = FPDF_GetNamedDestByName(document(), "First");
+  EXPECT_NE(nullptr, dest);
+
+  long ignore_len = 0;
+  FPDF_DEST dest_by_index =
+      FPDF_GetNamedDest(document(), 0, nullptr, &ignore_len);
+  EXPECT_EQ(dest_by_index, dest);
+
+  // Item from Dests dictionary.
+  dest = FPDF_GetNamedDestByName(document(), "FirstAlternate");
+  EXPECT_NE(nullptr, dest);
+
+  ignore_len = 0;
+  dest_by_index = FPDF_GetNamedDest(document(), 4, nullptr, &ignore_len);
+  EXPECT_EQ(dest_by_index, dest);
+
+  // Bad value type for item from Dests NameTree array.
+  dest = FPDF_GetNamedDestByName(document(), "WrongType");
+  EXPECT_EQ(nullptr, dest);
+
+  // No such destination in either Dest NameTree or dictionary.
+  dest = FPDF_GetNamedDestByName(document(), "Bogus");
+  EXPECT_EQ(nullptr, dest);
+}
+
+// The following tests pass if the document opens without crashing.
+TEST_F(FPDFViewEmbedderTest, Crasher_113) {
+  EXPECT_TRUE(OpenDocument("bug_113.pdf"));
+}
+
+TEST_F(FPDFViewEmbedderTest, Crasher_451830) {
+  // Document is damaged and can't be opened.
+  EXPECT_FALSE(OpenDocument("bug_451830.pdf"));
+}
+
+TEST_F(FPDFViewEmbedderTest, Crasher_452455) {
+  EXPECT_TRUE(OpenDocument("bug_452455.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFViewEmbedderTest, Crasher_454695) {
+  // Document is damaged and can't be opened.
+  EXPECT_FALSE(OpenDocument("bug_454695.pdf"));
+}
+
+TEST_F(FPDFViewEmbedderTest, Crasher_572871) {
+  EXPECT_TRUE(OpenDocument("bug_572871.pdf"));
+}
+
+// It tests that document can still be loaded even the trailer has no 'Size'
+// field if other information is right.
+TEST_F(FPDFViewEmbedderTest, Failed_213) {
+  EXPECT_TRUE(OpenDocument("bug_213.pdf"));
+}
+
+// The following tests pass if the document opens without infinite looping.
+TEST_F(FPDFViewEmbedderTest, Hang_298) {
+  EXPECT_FALSE(OpenDocument("bug_298.pdf"));
+}
+
+TEST_F(FPDFViewEmbedderTest, Crasher_773229) {
+  EXPECT_TRUE(OpenDocument("bug_773229.pdf"));
+}
+
+// Test if the document opens without infinite looping.
+// Previously this test will hang in a loop inside LoadAllCrossRefV4. After
+// the fix, LoadAllCrossRefV4 will return false after detecting a cross
+// reference loop. Cross references will be rebuilt successfully.
+TEST_F(FPDFViewEmbedderTest, CrossRefV4Loop) {
+  EXPECT_TRUE(OpenDocument("bug_xrefv4_loop.pdf"));
+  MockDownloadHints hints;
+
+  // Make sure calling FPDFAvail_IsDocAvail() on this file does not infinite
+  // loop either. See bug 875.
+  int ret = PDF_DATA_NOTAVAIL;
+  while (ret == PDF_DATA_NOTAVAIL)
+    ret = FPDFAvail_IsDocAvail(avail_, &hints);
+  EXPECT_EQ(PDF_DATA_AVAIL, ret);
+}
+
+// The test should pass when circular references to ParseIndirectObject will not
+// cause infinite loop.
+TEST_F(FPDFViewEmbedderTest, Hang_343) {
+  EXPECT_FALSE(OpenDocument("bug_343.pdf"));
+}
+
+// The test should pass when the absence of 'Contents' field in a signature
+// dictionary will not cause an infinite loop in CPDF_SyntaxParser::GetObject().
+TEST_F(FPDFViewEmbedderTest, Hang_344) {
+  EXPECT_FALSE(OpenDocument("bug_344.pdf"));
+}
+
+// The test should pass when there is no infinite recursion in
+// CPDF_SyntaxParser::GetString().
+TEST_F(FPDFViewEmbedderTest, Hang_355) {
+  EXPECT_FALSE(OpenDocument("bug_355.pdf"));
+}
+// The test should pass even when the file has circular references to pages.
+TEST_F(FPDFViewEmbedderTest, Hang_360) {
+  EXPECT_FALSE(OpenDocument("bug_360.pdf"));
+}
+
+// Deliberately damaged version of linearized.pdf with bad data in the shared
+// object hint table.
+TEST_F(FPDFViewEmbedderTest, Hang_1055) {
+  EXPECT_TRUE(OpenDocumentLinearized("linearized_bug_1055.pdf"));
+  int version;
+  EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
+  EXPECT_EQ(16, version);
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_FPDF_RenderPageBitmapWithMatrix \
+  DISABLED_FPDF_RenderPageBitmapWithMatrix
+#else
+#define MAYBE_FPDF_RenderPageBitmapWithMatrix FPDF_RenderPageBitmapWithMatrix
+#endif
+TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kClippedMD5[] = "a84cab93c102b9b9290fba3047ba702c";
+  const char kTopLeftQuarterMD5[] = "f11a11137c8834389e31cf555a4a6979";
+  const char kHoriStretchedMD5[] = "48ef9205941ed19691ccfa00d717187e";
+  const char kRotated90ClockwiseMD5[] = "d8da2c7bf77521550d0f2752b9cf3482";
+  const char kRotated180ClockwiseMD5[] = "0113386bb0bd45125bacc6dee78bfe78";
+  const char kRotated270ClockwiseMD5[] = "a287e0f74ce203699cda89f9cc97a240";
+  const char kMirrorHoriMD5[] = "6e8d7a6fde39d8e720fb9e620102918c";
+  const char kMirrorVertMD5[] = "8f3a555ef9c0d5031831ae3715273707";
+  const char kLargerTopLeftQuarterMD5[] = "172a2f4adafbadbe98017b1c025b9e27";
+  const char kLargerMD5[] = "c806145641c3e6fc4e022c7065343749";
+  const char kLargerClippedMD5[] = "091d3b1c7933c8f6945eb2cb41e588e9";
+  const char kLargerRotatedMD5[] = "115f13353ebfc82ddb392d1f0059eb12";
+  const char kLargerRotatedLandscapeMD5[] = "c901239d17d84ac84cb6f2124da71b0d";
+  const char kLargerRotatedDiagonalMD5[] = "3d62417468bdaff0eb14391a0c30a3b1";
+  const char kTileMD5[] = "0a190003c97220bf8877684c8d7e89cf";
+
+  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  const int page_width = static_cast<int>(FPDF_GetPageWidthF(page));
+  const int page_height = static_cast<int>(FPDF_GetPageHeightF(page));
+  EXPECT_EQ(200, page_width);
+  EXPECT_EQ(300, page_height);
+
+  ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+  CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+
+  FS_RECTF page_rect{0, 0, page_width, page_height};
+
+  // Try rendering with an identity matrix. The output should be the same as
+  // the RenderLoadedPage() output.
+  FS_MATRIX identity_matrix{1, 0, 0, 1, 0, 0};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix,
+                                 page_rect, kOriginalMD5);
+
+  // Again render with an identity matrix but with a smaller clipping rect.
+  FS_RECTF middle_of_page_rect{page_width / 4, page_height / 4,
+                               page_width * 3 / 4, page_height * 3 / 4};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix,
+                                 middle_of_page_rect, kClippedMD5);
+
+  // Now render again with the image scaled smaller.
+  FS_MATRIX half_scale_matrix{0.5, 0, 0, 0.5, 0, 0};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
+                                 half_scale_matrix, page_rect,
+                                 kTopLeftQuarterMD5);
+
+  // Now render again with the image scaled larger horizontally (the right half
+  // will be clipped).
+  FS_MATRIX stretch_x_matrix{2, 0, 0, 1, 0, 0};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
+                                 stretch_x_matrix, page_rect,
+                                 kHoriStretchedMD5);
+
+  // Try a 90 degree rotation clockwise but with the same bitmap size, so part
+  // will be clipped.
+  FS_MATRIX rotate_90_matrix{0, 1, -1, 0, page_width, 0};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
+                                 rotate_90_matrix, page_rect,
+                                 kRotated90ClockwiseMD5);
+
+  // 180 degree rotation clockwise.
+  FS_MATRIX rotate_180_matrix{-1, 0, 0, -1, page_width, page_height};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
+                                 rotate_180_matrix, page_rect,
+                                 kRotated180ClockwiseMD5);
+
+  // 270 degree rotation clockwise.
+  FS_MATRIX rotate_270_matrix{0, -1, 1, 0, 0, page_width};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
+                                 rotate_270_matrix, page_rect,
+                                 kRotated270ClockwiseMD5);
+
+  // Mirror horizontally.
+  FS_MATRIX mirror_hori_matrix{-1, 0, 0, 1, page_width, 0};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
+                                 mirror_hori_matrix, page_rect, kMirrorHoriMD5);
+
+  // Mirror vertically.
+  FS_MATRIX mirror_vert_matrix{1, 0, 0, -1, 0, page_height};
+  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
+                                 mirror_vert_matrix, page_rect, kMirrorVertMD5);
+
+  // Tests rendering to a larger bitmap
+  const int bitmap_width = page_width * 2;
+  const int bitmap_height = page_height * 2;
+
+  // Render using an identity matrix and the whole bitmap area as clipping rect.
+  FS_RECTF bitmap_rect{0, 0, bitmap_width, bitmap_height};
+  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
+                                 identity_matrix, bitmap_rect,
+                                 kLargerTopLeftQuarterMD5);
+
+  // Render using a scaling matrix to fill the larger bitmap.
+  FS_MATRIX double_scale_matrix{2, 0, 0, 2, 0, 0};
+  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
+                                 double_scale_matrix, bitmap_rect, kLargerMD5);
+
+  // Render the larger image again but with clipping.
+  FS_RECTF middle_of_bitmap_rect{bitmap_width / 4, bitmap_height / 4,
+                                 bitmap_width * 3 / 4, bitmap_height * 3 / 4};
+  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
+                                 double_scale_matrix, middle_of_bitmap_rect,
+                                 kLargerClippedMD5);
+
+  // On the larger bitmap, try a 90 degree rotation but with the same bitmap
+  // size, so part will be clipped.
+  FS_MATRIX rotate_90_scale_2_matrix{0, 2, -2, 0, bitmap_width, 0};
+  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
+                                 rotate_90_scale_2_matrix, bitmap_rect,
+                                 kLargerRotatedMD5);
+
+  // On the larger bitmap, apply 90 degree rotation to a bitmap with the
+  // appropriate dimensions.
+  const int landscape_bitmap_width = bitmap_height;
+  const int landscape_bitmap_height = bitmap_width;
+  FS_RECTF landscape_bitmap_rect{0, 0, landscape_bitmap_width,
+                                 landscape_bitmap_height};
+  FS_MATRIX landscape_rotate_90_scale_2_matrix{
+      0, 2, -2, 0, landscape_bitmap_width, 0};
+  TestRenderPageBitmapWithMatrix(
+      page, landscape_bitmap_width, landscape_bitmap_height,
+      landscape_rotate_90_scale_2_matrix, landscape_bitmap_rect,
+      kLargerRotatedLandscapeMD5);
+
+  // On the larger bitmap, apply 45 degree rotation to a bitmap with the
+  // appropriate dimensions.
+  const float sqrt2 = 1.41421356f;
+  const int diagonal_bitmap_size = ceil((bitmap_width + bitmap_height) / sqrt2);
+  FS_RECTF diagonal_bitmap_rect{0, 0, diagonal_bitmap_size,
+                                diagonal_bitmap_size};
+  FS_MATRIX rotate_45_scale_2_matrix{
+      sqrt2, sqrt2, -sqrt2, sqrt2, bitmap_height / sqrt2, 0};
+  TestRenderPageBitmapWithMatrix(page, diagonal_bitmap_size,
+                                 diagonal_bitmap_size, rotate_45_scale_2_matrix,
+                                 diagonal_bitmap_rect,
+                                 kLargerRotatedDiagonalMD5);
+
+  // Render the (2, 1) tile of the page (third column, second row) when the page
+  // is divided in 50x50 pixel tiles. The tile is scaled by a factor of 7.
+  const float scale = 7.0;
+  const int tile_size = 50;
+  const int tile_x = 2;
+  const int tile_y = 1;
+  int tile_bitmap_size = scale * tile_size;
+  FS_RECTF tile_bitmap_rect{0, 0, tile_bitmap_size, tile_bitmap_size};
+  FS_MATRIX tile_2_1_matrix{scale,
+                            0,
+                            0,
+                            scale,
+                            -tile_x * tile_bitmap_size,
+                            -tile_y * tile_bitmap_size};
+  TestRenderPageBitmapWithMatrix(page, tile_bitmap_size, tile_bitmap_size,
+                                 tile_2_1_matrix, tile_bitmap_rect, kTileMD5);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndexF) {
+  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FS_SIZEF size;
+  EXPECT_FALSE(FPDF_GetPageSizeByIndexF(nullptr, 0, &size));
+  EXPECT_FALSE(FPDF_GetPageSizeByIndexF(document(), 0, nullptr));
+
+  // Page -1 doesn't exist.
+  EXPECT_FALSE(FPDF_GetPageSizeByIndexF(document(), -1, &size));
+
+  // Page 1 doesn't exist.
+  EXPECT_FALSE(FPDF_GetPageSizeByIndexF(document(), 1, &size));
+
+  // Page 0 exists.
+  EXPECT_TRUE(FPDF_GetPageSizeByIndexF(document(), 0, &size));
+  EXPECT_FLOAT_EQ(200.0f, size.width);
+  EXPECT_FLOAT_EQ(300.0f, size.height);
+
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document());
+#ifdef PDF_ENABLE_XFA
+  // TODO(tsepez): XFA must obtain this size without parsing.
+  EXPECT_EQ(1u, pDoc->GetParsedPageCountForTesting());
+#else   // PDF_ENABLE_XFA
+  EXPECT_EQ(0u, pDoc->GetParsedPageCountForTesting());
+#endif  // PDF_ENABLE_XFA
+
+  // Double-check against values from when page is actually parsed.
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_FLOAT_EQ(size.width, FPDF_GetPageWidthF(page));
+  EXPECT_FLOAT_EQ(size.height, FPDF_GetPageHeightF(page));
+  EXPECT_EQ(1u, pDoc->GetParsedPageCountForTesting());
+  UnloadPage(page);
+}
+
+TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndex) {
+  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
+
+  double width = 0;
+  double height = 0;
+
+  EXPECT_FALSE(FPDF_GetPageSizeByIndex(nullptr, 0, &width, &height));
+  EXPECT_FALSE(FPDF_GetPageSizeByIndex(document(), 0, nullptr, &height));
+  EXPECT_FALSE(FPDF_GetPageSizeByIndex(document(), 0, &width, nullptr));
+
+  // Page -1 doesn't exist.
+  EXPECT_FALSE(FPDF_GetPageSizeByIndex(document(), -1, &width, &height));
+
+  // Page 1 doesn't exist.
+  EXPECT_FALSE(FPDF_GetPageSizeByIndex(document(), 1, &width, &height));
+
+  // Page 0 exists.
+  EXPECT_TRUE(FPDF_GetPageSizeByIndex(document(), 0, &width, &height));
+  EXPECT_EQ(200.0, width);
+  EXPECT_EQ(300.0, height);
+
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document());
+#ifdef PDF_ENABLE_XFA
+  // TODO(tsepez): XFA must obtain this size without parsing.
+  EXPECT_EQ(1u, pDoc->GetParsedPageCountForTesting());
+#else   // PDF_ENABLE_XFA
+  EXPECT_EQ(0u, pDoc->GetParsedPageCountForTesting());
+#endif  // PDF_ENABLE_XFA
+
+  // Double-check against values from when page is actually parsed.
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(width, FPDF_GetPageWidth(page));
+  EXPECT_EQ(height, FPDF_GetPageHeight(page));
+  EXPECT_EQ(1u, pDoc->GetParsedPageCountForTesting());
+  UnloadPage(page);
+}
+
+class RecordUnsupportedErrorDelegate final : public EmbedderTest::Delegate {
+ public:
+  RecordUnsupportedErrorDelegate() = default;
+  ~RecordUnsupportedErrorDelegate() override = default;
+
+  void UnsupportedHandler(int type) override { type_ = type; }
+
+  int type_ = -1;
+};
+
+TEST_F(FPDFViewEmbedderTest, UnSupportedOperations_NotFound) {
+  RecordUnsupportedErrorDelegate delegate;
+  SetDelegate(&delegate);
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(delegate.type_, -1);
+  SetDelegate(nullptr);
+}
+
+TEST_F(FPDFViewEmbedderTest, UnSupportedOperations_LoadCustomDocument) {
+  RecordUnsupportedErrorDelegate delegate;
+  SetDelegate(&delegate);
+  ASSERT_TRUE(OpenDocument("unsupported_feature.pdf"));
+  EXPECT_EQ(FPDF_UNSP_DOC_PORTABLECOLLECTION, delegate.type_);
+  SetDelegate(nullptr);
+}
+
+TEST_F(FPDFViewEmbedderTest, UnSupportedOperations_LoadDocument) {
+  std::string file_path;
+  ASSERT_TRUE(
+      PathService::GetTestFilePath("unsupported_feature.pdf", &file_path));
+
+  RecordUnsupportedErrorDelegate delegate;
+  SetDelegate(&delegate);
+  FPDF_DOCUMENT doc = FPDF_LoadDocument(file_path.c_str(), "");
+  EXPECT_TRUE(doc != nullptr);
+  EXPECT_EQ(FPDF_UNSP_DOC_PORTABLECOLLECTION, delegate.type_);
+  FPDF_CloseDocument(doc);
+  SetDelegate(nullptr);
+}
+
+TEST_F(FPDFViewEmbedderTest, DocumentHasValidCrossReferenceTable) {
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_TRUE(FPDF_DocumentHasValidCrossReferenceTable(document()));
+}
+
+TEST_F(FPDFViewEmbedderTest, DocumentHasInvalidCrossReferenceTable) {
+  EXPECT_FALSE(FPDF_DocumentHasValidCrossReferenceTable(nullptr));
+
+  ASSERT_TRUE(OpenDocument("bug_664284.pdf"));
+  EXPECT_FALSE(FPDF_DocumentHasValidCrossReferenceTable(document()));
+}
+
+// Related to https://crbug.com/pdfium/1197
+TEST_F(FPDFViewEmbedderTest, LoadDocumentWithEmptyXRefConsistently) {
+  ASSERT_TRUE(OpenDocument("empty_xref.pdf"));
+  EXPECT_TRUE(FPDF_DocumentHasValidCrossReferenceTable(document()));
+
+  std::string file_path;
+  ASSERT_TRUE(PathService::GetTestFilePath("empty_xref.pdf", &file_path));
+  {
+    ScopedFPDFDocument doc(FPDF_LoadDocument(file_path.c_str(), ""));
+    ASSERT_TRUE(doc);
+    EXPECT_TRUE(FPDF_DocumentHasValidCrossReferenceTable(doc.get()));
+  }
+  {
+    size_t file_length = 0;
+    std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
+        GetFileContents(file_path.c_str(), &file_length);
+    ASSERT(file_contents);
+    ScopedFPDFDocument doc(
+        FPDF_LoadMemDocument(file_contents.get(), file_length, ""));
+    ASSERT_TRUE(doc);
+    EXPECT_TRUE(FPDF_DocumentHasValidCrossReferenceTable(doc.get()));
+  }
+}
+
+TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithFlags) {
+  static const char kNormalMD5[] = "b0170c575b65ecb93ebafada0ff0f038";
+  static const char kGrayscaleMD5[] = "7b553f1052069a9c61237a05db0955d6";
+  static const char kNoSmoothpathMD5[] = "ff6e5c509d1f6984bcdfd18b26a4203a";
+
+  ASSERT_TRUE(OpenDocument("many_rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  TestRenderPageBitmapWithFlags(page, 0, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kGrayscaleMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE,
+                                kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH,
+                                kNoSmoothpathMD5);
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithExternalMemory) {
+  static const char kNormalMD5[] = "b0170c575b65ecb93ebafada0ff0f038";
+  static const char kGrayMD5[] = "b561c11edc44dc3972125a9b8744fa2f";
+  static const char kBgrMD5[] = "ab6312e04c0d3f4e46fb302a45173d05";
+
+  ASSERT_TRUE(OpenDocument("many_rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_Gray, kGrayMD5);
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGR, kBgrMD5);
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRx, kNormalMD5);
+  TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRA, kNormalMD5);
+  UnloadPage(page);
+}
+
+#if defined(OS_LINUX)
+TEST_F(FPDFViewEmbedderTest, RenderHelloWorldWithFlags) {
+  static const char kNormalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
+  static const char kLcdTextMD5[] = "825e881f39e48254e64e2808987a6b8c";
+  static const char kNoSmoothtextMD5[] = "3d01e234120b783a3fffb27273ea1ea8";
+
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  TestRenderPageBitmapWithFlags(page, 0, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kLcdTextMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE,
+                                kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT,
+                                kNoSmoothtextMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, kNormalMD5);
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH, kNormalMD5);
+
+  UnloadPage(page);
+}
+#endif  // defined(OS_LINUX)
+
+#if defined(OS_WIN)
+TEST_F(FPDFViewEmbedderTest, FPDFRenderPageEmf) {
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  std::vector<uint8_t> emf_normal = RenderPageWithFlagsToEmf(page, 0);
+  EXPECT_EQ(3772u, emf_normal.size());
+
+  // FPDF_REVERSE_BYTE_ORDER is ignored since EMFs are always BGR.
+  std::vector<uint8_t> emf_reverse_byte_order =
+      RenderPageWithFlagsToEmf(page, FPDF_REVERSE_BYTE_ORDER);
+  EXPECT_EQ(emf_normal, emf_reverse_byte_order);
+
+  UnloadPage(page);
+}
+
+class PostScriptRenderEmbedderTestBase : public FPDFViewEmbedderTest {
+ protected:
+  ~PostScriptRenderEmbedderTestBase() override = default;
+
+  // FPDFViewEmbedderTest:
+  void TearDown() override {
+    FPDF_SetPrintMode(FPDF_PRINTMODE_EMF);
+    FPDFViewEmbedderTest::TearDown();
+  }
+};
+
+class PostScriptLevel2EmbedderTest : public PostScriptRenderEmbedderTestBase {
+ public:
+  PostScriptLevel2EmbedderTest() = default;
+  ~PostScriptLevel2EmbedderTest() override = default;
+
+ protected:
+  // FPDFViewEmbedderTest:
+  void SetUp() override {
+    FPDFViewEmbedderTest::SetUp();
+    FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
+  }
+};
+
+class PostScriptLevel3EmbedderTest : public PostScriptRenderEmbedderTestBase {
+ public:
+  PostScriptLevel3EmbedderTest() = default;
+  ~PostScriptLevel3EmbedderTest() override = default;
+
+ protected:
+  // FPDFViewEmbedderTest:
+  void SetUp() override {
+    FPDFViewEmbedderTest::SetUp();
+    FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
+  }
+};
+
+TEST_F(PostScriptLevel2EmbedderTest, Rectangles) {
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  std::vector<uint8_t> emf_normal = RenderPageWithFlagsToEmf(page, 0);
+  std::string ps_data = GetPostScriptFromEmf(emf_normal);
+  EXPECT_STREQ(kExpectedRectanglePostScript, ps_data.c_str());
+
+  // FPDF_REVERSE_BYTE_ORDER is ignored since PostScript is not bitmap-based.
+  std::vector<uint8_t> emf_reverse_byte_order =
+      RenderPageWithFlagsToEmf(page, FPDF_REVERSE_BYTE_ORDER);
+  EXPECT_EQ(emf_normal, emf_reverse_byte_order);
+
+  UnloadPage(page);
+}
+
+TEST_F(PostScriptLevel3EmbedderTest, Rectangles) {
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  std::vector<uint8_t> emf_normal = RenderPageWithFlagsToEmf(page, 0);
+  std::string ps_data = GetPostScriptFromEmf(emf_normal);
+  EXPECT_STREQ(kExpectedRectanglePostScript, ps_data.c_str());
+
+  // FPDF_REVERSE_BYTE_ORDER is ignored since PostScript is not bitmap-based.
+  std::vector<uint8_t> emf_reverse_byte_order =
+      RenderPageWithFlagsToEmf(page, FPDF_REVERSE_BYTE_ORDER);
+  EXPECT_EQ(emf_normal, emf_reverse_byte_order);
+
+  UnloadPage(page);
+}
+
+TEST_F(PostScriptLevel2EmbedderTest, Image) {
+  const char kExpected[] =
+      "\n"
+      "save\n"
+      "/im/initmatrix load def\n"
+      "/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load "
+      "def/h/closepath load def\n"
+      "/f/fill load def/F/eofill load def/s/stroke load def/W/clip load "
+      "def/W*/eoclip load def\n"
+      "/rg/setrgbcolor load def/k/setcmykcolor load def\n"
+      "/J/setlinecap load def/j/setlinejoin load def/w/setlinewidth load "
+      "def/M/setmiterlimit load def/d/setdash load def\n"
+      "/q/gsave load def/Q/grestore load def/iM/imagemask load def\n"
+      "/Tj/show load def/Ff/findfont load def/Fs/scalefont load def/Sf/setfont "
+      "load def\n"
+      "/cm/concat load def/Cm/currentmatrix load def/mx/matrix load "
+      "def/sm/setmatrix load def\n"
+      "0 792 m 0 0 l 612 0 l 612 792 l 0 792 l h W n\n"
+      "q\n"
+      "0 792 m 0 0 l 612 0 l 612 792 l 0 792 l h W n\n"
+      "q\n"
+      "Q\n"
+      "q\n"
+      "281 106.7 m 331 106.7 l 331 56.7 l 281 56.7 l 281 106.7 l h W* n\n"
+      "q\n"
+      "[49.9 0 0 -50 281.1 106.6]cm 50 50 8[50 0 0 -50 0 "
+      "50]currentfile/ASCII85Decode filter /DCTDecode filter false 3 "
+      "colorimage\n"
+      "s4IA0!\"_al8O`[\\!<<*#!!*'\"s4[N@!!ic5#6k>;#6tJ?#m^kH'FbHY$Odmc'+Yct)"
+      "BU\"@)B9_>\r\n"
+      ",VCGe+tOrY*%3`p/2/e81c-:%3B]>W4>&EH1B6)/"
+      "6NIK\"#n.1M(_$ok1*IV\\1,:U?1,:U?1,:U?\r\n"
+      "1,:U?1,:U?1,:U?1,:U?1,:U?1,:U?1,:U?1,:U?1,:U?1,AmF!\"fJ:1&s'3!?qLF&HMtG!"
+      "WU(<\r\n"
+      "*rl9A\"T\\W)!<E3$z!!!!\"!WrQ/\"pYD?$4HmP!4<@<!W`B*!X&T/"
+      "\"U\"r.!!.KK!WrE*&Hrdj0gQ!W\r\n"
+      ";.0\\RE>10ZOeE%*6F\"?A;UOtZ1LbBV#mqFa(`=5<-7:2j.Ps\"@2`NfY6UX@47n?3D;"
+      "cHat='/U/\r\n"
+      "@q9._B4u!oF*)PJGBeCZK7nr5LPUeEP*;,qQC!u,R\\HRQV5C/"
+      "hWN*81['d?O\\@K2f_o0O6a2lBF\r\n"
+      "daQ^rf%8R-g>V&OjQ5OekiqC&o(2MHp@n@XqZ\"J6*ru?D!<E3%!<E3%!<<*\"!!!!\"!"
+      "WrQ/\"pYD?\r\n"
+      "$4HmP!4<C=!W`?*\"9Sc3\"U\"r.!<RHF!<N?8\"9fr'\"qj4!#@VTc+u4]T'LIqUZ,$_"
+      "k1K*]W@WKj'\r\n"
+      "(*k`q-1Mcg)&ahL-n-W'2E*TU3^Z;(7Rp!@8lJ\\h<``C+>%;)SAnPdkC3+K>G'A1VH@gd&"
+      "KnbA=\r\n"
+      "M2II[Pa.Q$R$jD;USO``Vl6SpZEppG[^WcW]#)A'`Q#s>ai`&\\eCE.%f\\,!<j5f="
+      "akNM0qo(2MH\r\n"
+      "p@n@XqZ#7L$j-M1!YGMH!'^JZre`+s!fAD!!fAD!!fAD!!fAD!!fAD!!fAD!!fAD!!fAD!!"
+      "fAD!\r\n"
+      "!fAD!!fAD!!fAD!!fAD!!fAD!!fAD!&-(;~>\n"
+      "Q\n"
+      "Q\n"
+      "q\n"
+      "q\n"
+      "Q\n"
+      "Q\n"
+      "Q\n"
+      "Q\n"
+      "\n"
+      "restore\n";
+
+  ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  std::vector<uint8_t> emf = RenderPageWithFlagsToEmf(page, 0);
+  std::string ps_data = GetPostScriptFromEmf(emf);
+  EXPECT_STREQ(kExpected, ps_data.c_str());
+
+  UnloadPage(page);
+}
+
+TEST_F(PostScriptLevel3EmbedderTest, Image) {
+  const char kExpected[] = R"(
+save
+/im/initmatrix load def
+/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load def/h/closepath load def
+/f/fill load def/F/eofill load def/s/stroke load def/W/clip load def/W*/eoclip load def
+/rg/setrgbcolor load def/k/setcmykcolor load def
+/J/setlinecap load def/j/setlinejoin load def/w/setlinewidth load def/M/setmiterlimit load def/d/setdash load def
+/q/gsave load def/Q/grestore load def/iM/imagemask load def
+/Tj/show load def/Ff/findfont load def/Fs/scalefont load def/Sf/setfont load def
+/cm/concat load def/Cm/currentmatrix load def/mx/matrix load def/sm/setmatrix load def
+0 792 m 0 0 l 612 0 l 612 792 l 0 792 l h W n
+q
+0 792 m 0 0 l 612 0 l 612 792 l 0 792 l h W n
+q
+Q
+q
+281 106.7 m 331 106.7 l 331 56.7 l 281 56.7 l 281 106.7 l h W* n
+q
+[49.9 0 0 -50 281.1 106.6]cm 50 50 8[50 0 0 -50 0 50]currentfile/ASCII85Decode filter /FlateDecode filter false 3 colorimage
+Gb"0;0`_7S!5bE%:[N')TE"rlzGQSs[!!*~>
+Q
+Q
+q
+q
+Q
+Q
+Q
+Q
+
+restore
+)";
+
+  ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  std::vector<uint8_t> emf = RenderPageWithFlagsToEmf(page, 0);
+  std::string ps_data = GetPostScriptFromEmf(emf);
+  EXPECT_STREQ(kExpected, ps_data.c_str());
+
+  UnloadPage(page);
+}
+#endif  // defined(OS_WIN)
diff --git a/fpdfsdk/fpdf_view_unittest.cpp b/fpdfsdk/fpdf_view_unittest.cpp
new file mode 100644
index 0000000..c08c49b
--- /dev/null
+++ b/fpdfsdk/fpdf_view_unittest.cpp
@@ -0,0 +1,56 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdfview.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#ifdef PDF_ENABLE_XFA
+TEST(FPDFView, BstrBadArgs) {
+  EXPECT_EQ(-1, FPDF_BStr_Init(nullptr));
+  EXPECT_EQ(-1, FPDF_BStr_Set(nullptr, "clams", -1));
+  EXPECT_EQ(-1, FPDF_BStr_Clear(nullptr));
+}
+
+TEST(FPDFView, BstrEmpty) {
+  FPDF_BSTR bst;
+  EXPECT_EQ(0, FPDF_BStr_Init(&bst));
+  EXPECT_FALSE(bst.str);
+  EXPECT_FALSE(bst.len);
+  EXPECT_EQ(0, FPDF_BStr_Clear(&bst));
+}
+
+TEST(FPDFView, BstrNormal) {
+  FPDF_BSTR bst;
+  EXPECT_EQ(0, FPDF_BStr_Init(&bst));
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", -1));
+  EXPECT_STREQ("clams", bst.str);
+  EXPECT_EQ(5, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Clear(&bst));
+  EXPECT_FALSE(bst.str);
+  EXPECT_FALSE(bst.len);
+}
+
+TEST(FPDFView, BstrReassign) {
+  FPDF_BSTR bst;
+  EXPECT_EQ(0, FPDF_BStr_Init(&bst));
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 3));
+  EXPECT_STREQ("cla", bst.str);
+  EXPECT_EQ(3, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 5));
+  EXPECT_STREQ("clams", bst.str);
+  EXPECT_EQ(5, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 1));
+  EXPECT_STREQ("c", bst.str);
+  EXPECT_EQ(1, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 0));
+  EXPECT_FALSE(bst.str);
+  EXPECT_EQ(0, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Clear(&bst));
+}
+#endif  // PDF_ENABLE_XFA
diff --git a/fpdfsdk/fpdfannot.cpp b/fpdfsdk/fpdfannot.cpp
deleted file mode 100644
index 9b0cb39..0000000
--- a/fpdfsdk/fpdfannot.cpp
+++ /dev/null
@@ -1,890 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_annot.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.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/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_name.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "core/fpdfdoc/cpvt_generateap.h"
-#include "core/fxge/cfx_color.h"
-#include "fpdfsdk/fsdk_define.h"
-
-namespace {
-
-// These checks ensure the consistency of annotation subtype values across core/
-// and public.
-static_assert(static_cast<int>(CPDF_Annot::Subtype::UNKNOWN) ==
-                  FPDF_ANNOT_UNKNOWN,
-              "CPDF_Annot::UNKNOWN value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::TEXT) == FPDF_ANNOT_TEXT,
-              "CPDF_Annot::TEXT value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::LINK) == FPDF_ANNOT_LINK,
-              "CPDF_Annot::LINK value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::FREETEXT) ==
-                  FPDF_ANNOT_FREETEXT,
-              "CPDF_Annot::FREETEXT value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::LINE) == FPDF_ANNOT_LINE,
-              "CPDF_Annot::LINE value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUARE) ==
-                  FPDF_ANNOT_SQUARE,
-              "CPDF_Annot::SQUARE value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::CIRCLE) ==
-                  FPDF_ANNOT_CIRCLE,
-              "CPDF_Annot::CIRCLE value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYGON) ==
-                  FPDF_ANNOT_POLYGON,
-              "CPDF_Annot::POLYGON value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYLINE) ==
-                  FPDF_ANNOT_POLYLINE,
-              "CPDF_Annot::POLYLINE value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::HIGHLIGHT) ==
-                  FPDF_ANNOT_HIGHLIGHT,
-              "CPDF_Annot::HIGHLIGHT value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::UNDERLINE) ==
-                  FPDF_ANNOT_UNDERLINE,
-              "CPDF_Annot::UNDERLINE value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUIGGLY) ==
-                  FPDF_ANNOT_SQUIGGLY,
-              "CPDF_Annot::SQUIGGLY value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::STRIKEOUT) ==
-                  FPDF_ANNOT_STRIKEOUT,
-              "CPDF_Annot::STRIKEOUT value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::STAMP) == FPDF_ANNOT_STAMP,
-              "CPDF_Annot::STAMP value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::CARET) == FPDF_ANNOT_CARET,
-              "CPDF_Annot::CARET value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::INK) == FPDF_ANNOT_INK,
-              "CPDF_Annot::INK value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::POPUP) == FPDF_ANNOT_POPUP,
-              "CPDF_Annot::POPUP value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::FILEATTACHMENT) ==
-                  FPDF_ANNOT_FILEATTACHMENT,
-              "CPDF_Annot::FILEATTACHMENT value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::SOUND) == FPDF_ANNOT_SOUND,
-              "CPDF_Annot::SOUND value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::MOVIE) == FPDF_ANNOT_MOVIE,
-              "CPDF_Annot::MOVIE value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::WIDGET) ==
-                  FPDF_ANNOT_WIDGET,
-              "CPDF_Annot::WIDGET value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::SCREEN) ==
-                  FPDF_ANNOT_SCREEN,
-              "CPDF_Annot::SCREEN value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::PRINTERMARK) ==
-                  FPDF_ANNOT_PRINTERMARK,
-              "CPDF_Annot::PRINTERMARK value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::TRAPNET) ==
-                  FPDF_ANNOT_TRAPNET,
-              "CPDF_Annot::TRAPNET value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::WATERMARK) ==
-                  FPDF_ANNOT_WATERMARK,
-              "CPDF_Annot::WATERMARK value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::THREED) ==
-                  FPDF_ANNOT_THREED,
-              "CPDF_Annot::THREED value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::RICHMEDIA) ==
-                  FPDF_ANNOT_RICHMEDIA,
-              "CPDF_Annot::RICHMEDIA value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::Subtype::XFAWIDGET) ==
-                  FPDF_ANNOT_XFAWIDGET,
-              "CPDF_Annot::XFAWIDGET value mismatch");
-
-// These checks ensure the consistency of annotation appearance mode values
-// across core/ and public.
-static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Normal) ==
-                  FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-              "CPDF_Annot::AppearanceMode::Normal value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Rollover) ==
-                  FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
-              "CPDF_Annot::AppearanceMode::Rollover value mismatch");
-static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Down) ==
-                  FPDF_ANNOT_APPEARANCEMODE_DOWN,
-              "CPDF_Annot::AppearanceMode::Down value mismatch");
-
-// These checks ensure the consistency of dictionary value types across core/
-// and public/.
-static_assert(static_cast<int>(CPDF_Object::Type::BOOLEAN) ==
-                  FPDF_OBJECT_BOOLEAN,
-              "CPDF_Object::BOOLEAN value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::NUMBER) == FPDF_OBJECT_NUMBER,
-              "CPDF_Object::NUMBER value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::STRING) == FPDF_OBJECT_STRING,
-              "CPDF_Object::STRING value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::NAME) == FPDF_OBJECT_NAME,
-              "CPDF_Object::NAME value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::ARRAY) == FPDF_OBJECT_ARRAY,
-              "CPDF_Object::ARRAY value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::DICTIONARY) ==
-                  FPDF_OBJECT_DICTIONARY,
-              "CPDF_Object::DICTIONARY value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::STREAM) == FPDF_OBJECT_STREAM,
-              "CPDF_Object::STREAM value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::NULLOBJ) ==
-                  FPDF_OBJECT_NULLOBJ,
-              "CPDF_Object::NULLOBJ value mismatch");
-static_assert(static_cast<int>(CPDF_Object::Type::REFERENCE) ==
-                  FPDF_OBJECT_REFERENCE,
-              "CPDF_Object::REFERENCE value mismatch");
-
-class CPDF_AnnotContext {
- public:
-  CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict,
-                    CPDF_Page* pPage,
-                    CPDF_Stream* pStream)
-      : m_pAnnotDict(pAnnotDict), m_pPage(pPage) {
-    SetForm(pStream);
-  }
-  ~CPDF_AnnotContext() {}
-
-  bool HasForm() const { return !!m_pAnnotForm; }
-
-  void SetForm(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());
-
-    m_pAnnotForm = pdfium::MakeUnique<CPDF_Form>(
-        m_pPage->m_pDocument.Get(), m_pPage->m_pResources.Get(), pStream);
-    m_pAnnotForm->ParseContent();
-  }
-
-  CPDF_Form* GetForm() const { return m_pAnnotForm.get(); }
-  CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
-  CPDF_Page* GetPage() const { return m_pPage.Get(); }
-
- private:
-  std::unique_ptr<CPDF_Form> m_pAnnotForm;
-  UnownedPtr<CPDF_Dictionary> m_pAnnotDict;
-  UnownedPtr<CPDF_Page> m_pPage;
-};
-
-CPDF_AnnotContext* CPDFAnnotContextFromFPDFAnnotation(FPDF_ANNOTATION annot) {
-  return static_cast<CPDF_AnnotContext*>(annot);
-}
-
-bool HasAPStream(const CPDF_Dictionary* pAnnotDict) {
-  return !!FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
-}
-
-void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) {
-  ASSERT(pForm);
-  ASSERT(pStream);
-
-  CPDF_PageContentGenerator generator(pForm);
-  std::ostringstream buf;
-  generator.ProcessPageObjects(&buf);
-  pStream->SetDataAndRemoveFilter(&buf);
-}
-
-}  // namespace
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
-  // The supported subtypes must also be communicated in the user doc.
-  return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_HIGHLIGHT ||
-         subtype == FPDF_ANNOT_INK || subtype == FPDF_ANNOT_POPUP ||
-         subtype == FPDF_ANNOT_SQUARE || subtype == FPDF_ANNOT_SQUIGGLY ||
-         subtype == FPDF_ANNOT_STAMP || subtype == FPDF_ANNOT_STRIKEOUT ||
-         subtype == FPDF_ANNOT_TEXT || subtype == FPDF_ANNOT_UNDERLINE;
-}
-
-FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
-FPDFPage_CreateAnnot(FPDF_PAGE page, FPDF_ANNOTATION_SUBTYPE subtype) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage || !FPDFAnnot_IsSupportedSubtype(subtype))
-    return nullptr;
-
-  auto pDict = pdfium::MakeUnique<CPDF_Dictionary>(
-      pPage->m_pDocument->GetByteStringPool());
-  pDict->SetNewFor<CPDF_Name>("Type", "Annot");
-  pDict->SetNewFor<CPDF_Name>("Subtype",
-                              CPDF_Annot::AnnotSubtypeToString(
-                                  static_cast<CPDF_Annot::Subtype>(subtype)));
-  auto pNewAnnot =
-      pdfium::MakeUnique<CPDF_AnnotContext>(pDict.get(), pPage, nullptr);
-
-  CPDF_Array* pAnnotList = pPage->m_pFormDict->GetArrayFor("Annots");
-  if (!pAnnotList)
-    pAnnotList = pPage->m_pFormDict->SetNewFor<CPDF_Array>("Annots");
-
-  pAnnotList->Add(std::move(pDict));
-  return pNewAnnot.release();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotCount(FPDF_PAGE page) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage || !pPage->m_pFormDict)
-    return 0;
-
-  CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
-  return pAnnots ? pAnnots->GetCount() : 0;
-}
-
-FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page,
-                                                            int index) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage || !pPage->m_pFormDict || index < 0)
-    return nullptr;
-
-  CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
-  if (!pAnnots || static_cast<size_t>(index) >= pAnnots->GetCount())
-    return nullptr;
-
-  CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(index));
-  auto pNewAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(pDict, pPage, nullptr);
-  return pNewAnnot.release();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotIndex(FPDF_PAGE page,
-                                                     FPDF_ANNOTATION annot) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  if (!pPage || !pPage->m_pFormDict || !pAnnot || !pAnnot->GetAnnotDict())
-    return -1;
-
-  CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
-  if (!pAnnots)
-    return -1;
-
-  CPDF_Dictionary* pDict = pAnnot->GetAnnotDict();
-  auto it =
-      std::find_if(pAnnots->begin(), pAnnots->end(),
-                   [pDict](const std::unique_ptr<CPDF_Object>& candidate) {
-                     return candidate->GetDirect() == pDict;
-                   });
-
-  if (it == pAnnots->end())
-    return -1;
-
-  return it - pAnnots->begin();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPage_CloseAnnot(FPDF_ANNOTATION annot) {
-  delete CPDFAnnotContextFromFPDFAnnotation(annot);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_RemoveAnnot(FPDF_PAGE page,
-                                                         int index) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage || !pPage->m_pFormDict || index < 0)
-    return false;
-
-  CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
-  if (!pAnnots || static_cast<size_t>(index) >= pAnnots->GetCount())
-    return false;
-
-  pAnnots->RemoveAt(index);
-  return true;
-}
-
-FPDF_EXPORT FPDF_ANNOTATION_SUBTYPE FPDF_CALLCONV
-FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) {
-  if (!annot)
-    return FPDF_ANNOT_UNKNOWN;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return FPDF_ANNOT_UNKNOWN;
-
-  return static_cast<FPDF_ANNOTATION_SUBTYPE>(
-      CPDF_Annot::StringToAnnotSubtype(pAnnotDict->GetStringFor("Subtype")));
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_IsObjectSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
-  // The supported subtypes must also be communicated in the user doc.
-  return subtype == FPDF_ANNOT_INK || subtype == FPDF_ANNOT_STAMP;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
-  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
-  if (!pAnnot || !pAnnot->GetAnnotDict() || !pAnnot->HasForm() || !pObj)
-    return false;
-
-  // Check that the annotation type is supported by this method.
-  if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
-    return false;
-
-  // Check that the annotation already has an appearance stream, since an
-  // existing object is to be updated.
-  CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(),
-                                            CPDF_Annot::AppearanceMode::Normal);
-  if (!pStream)
-    return false;
-
-  // Check that the object is already in this annotation's object list.
-  CPDF_Form* pForm = pAnnot->GetForm();
-  CPDF_PageObjectList* pObjList = pForm->GetPageObjectList();
-  auto it =
-      std::find_if(pObjList->begin(), pObjList->end(),
-                   [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
-                     return candidate.get() == pObj;
-                   });
-  if (it == pObjList->end())
-    return false;
-
-  // Update the content stream data in the annotation's AP stream.
-  UpdateContentStream(pForm, pStream);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_AppendObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
-  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
-  if (!pAnnot || !pObj)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-  CPDF_Page* pPage = pAnnot->GetPage();
-  if (!pAnnotDict || !pPage)
-    return false;
-
-  // Check that the annotation type is supported by this method.
-  if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
-    return false;
-
-  // If the annotation does not have an AP stream yet, generate and set it.
-  CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(),
-                                            CPDF_Annot::AppearanceMode::Normal);
-  if (!pStream) {
-    CPVT_GenerateAP::GenerateEmptyAP(pPage->m_pDocument.Get(), pAnnotDict);
-    pStream =
-        FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
-    if (!pStream)
-      return false;
-  }
-
-  // Get the annotation's corresponding form object for parsing its AP stream.
-  if (!pAnnot->HasForm())
-    pAnnot->SetForm(pStream);
-
-  // Check that the object did not come from the same annotation. If this check
-  // succeeds, then it is assumed that the object came from
-  // FPDFPageObj_CreateNew{Path|Rect}() or FPDFPageObj_New{Text|Image}Obj().
-  // Note that an object that came from a different annotation must not be
-  // passed here, since an object cannot belong to more than one annotation.
-  CPDF_Form* pForm = pAnnot->GetForm();
-  CPDF_PageObjectList* pObjList = pForm->GetPageObjectList();
-  auto it =
-      std::find_if(pObjList->begin(), pObjList->end(),
-                   [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
-                     return candidate.get() == pObj;
-                   });
-  if (it != pObjList->end())
-    return false;
-
-  // Append the object to the object list.
-  std::unique_ptr<CPDF_PageObject> pPageObjHolder(pObj);
-  pObjList->push_back(std::move(pPageObjHolder));
-
-  // Set the content stream data in the annotation's AP stream.
-  UpdateContentStream(pForm, pStream);
-  return true;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetObjectCount(FPDF_ANNOTATION annot) {
-  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  if (!pAnnot || !pAnnot->GetAnnotDict())
-    return 0;
-
-  if (!pAnnot->HasForm()) {
-    CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(
-        pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
-    if (!pStream)
-      return 0;
-
-    pAnnot->SetForm(pStream);
-  }
-  return pdfium::CollectionSize<int>(*pAnnot->GetForm()->GetPageObjectList());
-}
-
-FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
-FPDFAnnot_GetObject(FPDF_ANNOTATION annot, int index) {
-  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  if (!pAnnot || !pAnnot->GetAnnotDict() || index < 0)
-    return nullptr;
-
-  if (!pAnnot->HasForm()) {
-    CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(
-        pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
-    if (!pStream)
-      return nullptr;
-
-    pAnnot->SetForm(pStream);
-  }
-
-  return pAnnot->GetForm()->GetPageObjectList()->GetPageObjectByIndex(index);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_RemoveObject(FPDF_ANNOTATION annot, int index) {
-  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  if (!pAnnot || !pAnnot->GetAnnotDict() || !pAnnot->HasForm() || index < 0)
-    return false;
-
-  // Check that the annotation type is supported by this method.
-  if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
-    return false;
-
-  // Check that the annotation already has an appearance stream, since an
-  // existing object is to be deleted.
-  CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(),
-                                            CPDF_Annot::AppearanceMode::Normal);
-  if (!pStream)
-    return false;
-
-  CPDF_PageObjectList* pObjList = pAnnot->GetForm()->GetPageObjectList();
-  if (static_cast<size_t>(index) >= pObjList->size())
-    return false;
-
-  pObjList->erase(pObjList->begin() + index);
-  UpdateContentStream(pAnnot->GetForm(), pStream);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetColor(FPDF_ANNOTATION annot,
-                                                       FPDFANNOT_COLORTYPE type,
-                                                       unsigned int R,
-                                                       unsigned int G,
-                                                       unsigned int B,
-                                                       unsigned int A) {
-  if (!annot || R > 255 || G > 255 || B > 255 || A > 255)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  // For annotations with their appearance streams already defined, the path
-  // stream's own color definitions take priority over the annotation color
-  // definitions set by this method, hence this method will simply fail.
-  if (HasAPStream(pAnnotDict))
-    return false;
-
-  // Set the opacity of the annotation.
-  pAnnotDict->SetNewFor<CPDF_Number>("CA", A / 255.f);
-
-  // Set the color of the annotation.
-  ByteString key = type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C";
-  CPDF_Array* pColor = pAnnotDict->GetArrayFor(key);
-  if (pColor)
-    pColor->Clear();
-  else
-    pColor = pAnnotDict->SetNewFor<CPDF_Array>(key);
-
-  pColor->AddNew<CPDF_Number>(R / 255.f);
-  pColor->AddNew<CPDF_Number>(G / 255.f);
-  pColor->AddNew<CPDF_Number>(B / 255.f);
-
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetColor(FPDF_ANNOTATION annot,
-                                                       FPDFANNOT_COLORTYPE type,
-                                                       unsigned int* R,
-                                                       unsigned int* G,
-                                                       unsigned int* B,
-                                                       unsigned int* A) {
-  if (!annot || !R || !G || !B || !A)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  // For annotations with their appearance streams already defined, the path
-  // stream's own color definitions take priority over the annotation color
-  // definitions retrieved by this method, hence this method will simply fail.
-  if (HasAPStream(pAnnotDict))
-    return false;
-
-  CPDF_Array* pColor = pAnnotDict->GetArrayFor(
-      type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C");
-  *A =
-      (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetNumberFor("CA") : 1) * 255.f;
-  if (!pColor) {
-    // Use default color. The default colors must be consistent with the ones
-    // used to generate AP. See calls to GetColorStringWithDefault() in
-    // CPVT_GenerateAP::Generate*AP().
-    if (pAnnotDict->GetStringFor("Subtype") == "Highlight") {
-      *R = 255;
-      *G = 255;
-      *B = 0;
-    } else {
-      *R = 0;
-      *G = 0;
-      *B = 0;
-    }
-    return true;
-  }
-
-  CFX_Color color = CFX_Color::ParseColor(*pColor);
-  switch (color.nColorType) {
-    case CFX_Color::kRGB:
-      *R = color.fColor1 * 255.f;
-      *G = color.fColor2 * 255.f;
-      *B = color.fColor3 * 255.f;
-      break;
-    case CFX_Color::kGray:
-      *R = 255.f * color.fColor1;
-      *G = 255.f * color.fColor1;
-      *B = 255.f * color.fColor1;
-      break;
-    case CFX_Color::kCMYK:
-      *R = 255.f * (1 - color.fColor1) * (1 - color.fColor4);
-      *G = 255.f * (1 - color.fColor2) * (1 - color.fColor4);
-      *B = 255.f * (1 - color.fColor3) * (1 - color.fColor4);
-      break;
-    case CFX_Color::kTransparent:
-      *R = 0;
-      *G = 0;
-      *B = 0;
-      break;
-  }
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot) {
-  if (!annot)
-    return false;
-
-  FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
-  return subtype == FPDF_ANNOT_LINK || subtype == FPDF_ANNOT_HIGHLIGHT ||
-         subtype == FPDF_ANNOT_UNDERLINE || subtype == FPDF_ANNOT_SQUIGGLY ||
-         subtype == FPDF_ANNOT_STRIKEOUT;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,
-                              const FS_QUADPOINTSF* quadPoints) {
-  if (!annot || !quadPoints || !FPDFAnnot_HasAttachmentPoints(annot))
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  // Update the "QuadPoints" entry in the annotation dictionary.
-  CPDF_Array* pQuadPoints = pAnnotDict->GetArrayFor("QuadPoints");
-  if (pQuadPoints)
-    pQuadPoints->Clear();
-  else
-    pQuadPoints = pAnnotDict->SetNewFor<CPDF_Array>("QuadPoints");
-
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->x1);
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->y1);
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->x2);
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->y2);
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->x3);
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->y3);
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->x4);
-  pQuadPoints->AddNew<CPDF_Number>(quadPoints->y4);
-
-  // If the annotation's appearance stream is defined, and the new quadpoints
-  // defines a bigger bounding box than the appearance stream currently
-  // specifies, then update the "BBox" entry in the AP dictionary too, since it
-  // comes from annotation dictionary's "QuadPoints" entry.
-  CPDF_Stream* pStream =
-      FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
-  if (pStream) {
-    CFX_FloatRect newRect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
-    if (newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
-      pStream->GetDict()->SetRectFor("BBox", newRect);
-  }
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,
-                              FS_QUADPOINTSF* quadPoints) {
-  if (!annot || !FPDFAnnot_HasAttachmentPoints(annot) || !quadPoints)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (!pArray)
-    return false;
-
-  quadPoints->x1 = pArray->GetNumberAt(0);
-  quadPoints->y1 = pArray->GetNumberAt(1);
-  quadPoints->x2 = pArray->GetNumberAt(2);
-  quadPoints->y2 = pArray->GetNumberAt(3);
-  quadPoints->x3 = pArray->GetNumberAt(4);
-  quadPoints->y3 = pArray->GetNumberAt(5);
-  quadPoints->x4 = pArray->GetNumberAt(6);
-  quadPoints->y4 = pArray->GetNumberAt(7);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot,
-                                                      const FS_RECTF* rect) {
-  if (!annot || !rect)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  CFX_FloatRect newRect = CFXFloatRectFromFSRECTF(*rect);
-
-  // Update the "Rect" entry in the annotation dictionary.
-  pAnnotDict->SetRectFor("Rect", newRect);
-
-  // If the annotation's appearance stream is defined, the annotation is of a
-  // type that does not have quadpoints, and the new rectangle is bigger than
-  // the current bounding box, then update the "BBox" entry in the AP
-  // dictionary too, since its "BBox" entry comes from annotation dictionary's
-  // "Rect" entry.
-  if (FPDFAnnot_HasAttachmentPoints(annot))
-    return true;
-
-  CPDF_Stream* pStream =
-      FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
-  if (pStream && newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
-    pStream->GetDict()->SetRectFor("BBox", newRect);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot,
-                                                      FS_RECTF* rect) {
-  if (!annot || !rect)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  FSRECTFFromCFXFloatRect(pAnnotDict->GetRectFor("Rect"), rect);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot,
-                                                     FPDF_BYTESTRING key) {
-  if (!annot)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  return pAnnotDict->KeyExist(key);
-}
-
-FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
-FPDFAnnot_GetValueType(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
-  if (!FPDFAnnot_HasKey(annot, key))
-    return FPDF_OBJECT_UNKNOWN;
-
-  auto* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  CPDF_Object* pObj = pAnnot->GetAnnotDict()->GetObjectFor(key);
-  return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_SetStringValue(FPDF_ANNOTATION annot,
-                         FPDF_BYTESTRING key,
-                         FPDF_WIDESTRING value) {
-  if (!annot)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  pAnnotDict->SetNewFor<CPDF_String>(
-      key, CFXByteStringFromFPDFWideString(value), false);
-  return true;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFAnnot_GetStringValue(FPDF_ANNOTATION annot,
-                         FPDF_BYTESTRING key,
-                         void* buffer,
-                         unsigned long buflen) {
-  if (!annot)
-    return 0;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return 0;
-
-  return Utf16EncodeMaybeCopyAndReturnLength(pAnnotDict->GetUnicodeTextFor(key),
-                                             buffer, buflen);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAnnot_SetAP(FPDF_ANNOTATION annot,
-                FPDF_ANNOT_APPEARANCEMODE appearanceMode,
-                FPDF_WIDESTRING value) {
-  if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
-    return false;
-
-  if (!annot)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  constexpr const char* modeKeyForMode[] = {"N", "R", "D"};
-  static_assert(FX_ArraySize(modeKeyForMode) == FPDF_ANNOT_APPEARANCEMODE_COUNT,
-                "length of modeKeyForMode should be equal to "
-                "FPDF_ANNOT_APPEARANCEMODE_COUNT");
-  const char* modeKey = modeKeyForMode[appearanceMode];
-
-  CPDF_Dictionary* pApDict = pAnnotDict->GetDictFor("AP");
-
-  // If value is null, we're in remove mode. Otherwise, we're in add/update
-  // mode.
-  if (value) {
-    if (!pApDict)
-      pApDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
-
-    ByteString newValue = CFXByteStringFromFPDFWideString(value);
-    auto pNewApStream = pdfium::MakeUnique<CPDF_Stream>();
-    pNewApStream->SetData(newValue.raw_str(), newValue.GetLength());
-    pApDict->SetFor(modeKey, std::move(pNewApStream));
-  } else {
-    if (pApDict) {
-      if (appearanceMode == FPDF_ANNOT_APPEARANCEMODE_NORMAL)
-        pAnnotDict->RemoveFor("AP");
-      else
-        pApDict->RemoveFor(modeKey);
-    }
-  }
-
-  return true;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFAnnot_GetAP(FPDF_ANNOTATION annot,
-                FPDF_ANNOT_APPEARANCEMODE appearanceMode,
-                void* buffer,
-                unsigned long buflen) {
-  if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
-    return 0;
-
-  if (!annot)
-    return 0;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return 0;
-
-  CPDF_Annot::AppearanceMode mode =
-      static_cast<CPDF_Annot::AppearanceMode>(appearanceMode);
-
-  CPDF_Stream* pStream = FPDFDOC_GetAnnotAPNoFallback(pAnnotDict, mode);
-  return Utf16EncodeMaybeCopyAndReturnLength(
-      pStream ? pStream->GetUnicodeText() : L"", buffer, buflen);
-}
-
-FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
-FPDFAnnot_GetLinkedAnnot(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
-  CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
-  if (!pAnnot || !pAnnot->GetAnnotDict())
-    return nullptr;
-
-  CPDF_Dictionary* pLinkedDict = pAnnot->GetAnnotDict()->GetDictFor(key);
-  if (!pLinkedDict || pLinkedDict->GetStringFor("Type") != "Annot")
-    return nullptr;
-
-  auto pLinkedAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(
-      pLinkedDict, pAnnot->GetPage(), nullptr);
-  return pLinkedAnnot.release();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetFlags(FPDF_ANNOTATION annot) {
-  if (!annot)
-    return FPDF_ANNOT_FLAG_NONE;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  return pAnnotDict ? pAnnotDict->GetIntegerFor("F") : FPDF_ANNOT_FLAG_NONE;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetFlags(FPDF_ANNOTATION annot,
-                                                       int flags) {
-  if (!annot)
-    return false;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return false;
-
-  pAnnotDict->SetNewFor<CPDF_Number>("F", flags);
-  return true;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFAnnot_GetFormFieldFlags(FPDF_PAGE page, FPDF_ANNOTATION annot) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage || !annot)
-    return FPDF_FORMFLAG_NONE;
-
-  CPDF_Dictionary* pAnnotDict =
-      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
-  if (!pAnnotDict)
-    return FPDF_FORMFLAG_NONE;
-
-  CPDF_InterForm interform(pPage->m_pDocument.Get());
-  CPDF_FormField* pFormField = interform.GetFieldByDict(pAnnotDict);
-  return pFormField ? pFormField->GetFieldFlags() : FPDF_FORMFLAG_NONE;
-}
-
-FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
-FPDFAnnot_GetFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
-                              FPDF_PAGE page,
-                              double page_x,
-                              double page_y) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!hHandle || !pPage)
-    return nullptr;
-
-  CPDF_InterForm interform(pPage->m_pDocument.Get());
-  int annot_index = -1;
-  CPDF_FormControl* pFormCtrl = interform.GetControlAtPoint(
-      pPage, CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
-      &annot_index);
-  if (!pFormCtrl || annot_index == -1)
-    return nullptr;
-  return FPDFPage_GetAnnot(page, annot_index);
-}
diff --git a/fpdfsdk/fpdfannot_embeddertest.cpp b/fpdfsdk/fpdfannot_embeddertest.cpp
deleted file mode 100644
index b38bd87..0000000
--- a/fpdfsdk/fpdfannot_embeddertest.cpp
+++ /dev/null
@@ -1,1267 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cwchar>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-#include "public/fpdf_annot.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gmock/include/gmock/gmock-matchers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-static constexpr char kContentsKey[] = "Contents";
-
-class FPDFAnnotEmbeddertest : public EmbedderTest {};
-
-std::wstring BufferToWString(const std::vector<char>& buf) {
-  return GetPlatformWString(reinterpret_cast<FPDF_WIDESTRING>(buf.data()));
-}
-
-std::string BufferToString(const std::vector<char>& buf) {
-  return GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(buf.data()));
-}
-
-TEST_F(FPDFAnnotEmbeddertest, RenderAnnotWithOnlyRolloverAP) {
-  // Open a file with one annotation and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // This annotation has a malformed appearance stream, which does not have its
-  // normal appearance defined, only its rollover appearance. In this case, its
-  // normal appearance should be generated, allowing the highlight annotation to
-  // still display.
-  FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle(), FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
-  FPDFBitmap_Destroy(bitmap);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, ExtractHighlightLongContent) {
-  // Open a file with one annotation and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Check that there is a total of 1 annotation on its first page.
-  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
-
-  // Check that the annotation is of type "highlight".
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
-
-  // Check that the annotation color is yellow.
-  unsigned int R;
-  unsigned int G;
-  unsigned int B;
-  unsigned int A;
-  EXPECT_TRUE(
-      FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
-  EXPECT_EQ(255u, R);
-  EXPECT_EQ(255u, G);
-  EXPECT_EQ(0u, B);
-  EXPECT_EQ(255u, A);
-
-  // Check that the author is correct.
-  static constexpr char kAuthorKey[] = "T";
-  EXPECT_EQ(FPDF_OBJECT_STRING, FPDFAnnot_GetValueType(annot, kAuthorKey));
-  unsigned long len = FPDFAnnot_GetStringValue(annot, kAuthorKey, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(28u, FPDFAnnot_GetStringValue(annot, kAuthorKey, buf.data(), len));
-  EXPECT_STREQ(L"Jae Hyun Park", BufferToWString(buf).c_str());
-
-  // Check that the content is correct.
-  EXPECT_EQ(FPDF_OBJECT_STRING, FPDFAnnot_GetValueType(annot, kContentsKey));
-  len = FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(2690u,
-            FPDFAnnot_GetStringValue(annot, kContentsKey, buf.data(), len));
-  const wchar_t contents[] =
-      L"This is a note for that highlight annotation. Very long highlight "
-      "annotation. Long long long Long long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long longLong long longLong long longLong long longLong long "
-      "longLong long long. END";
-  EXPECT_STREQ(contents, BufferToWString(buf).c_str());
-
-  // Check that the quadpoints are correct.
-  FS_QUADPOINTSF quadpoints;
-  ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &quadpoints));
-  EXPECT_EQ(115.802643f, quadpoints.x1);
-  EXPECT_EQ(718.913940f, quadpoints.y1);
-  EXPECT_EQ(157.211182f, quadpoints.x4);
-  EXPECT_EQ(706.264465f, quadpoints.y4);
-
-  FPDFPage_CloseAnnot(annot);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, ExtractInkMultiple) {
-  // Open a file with three annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Check that there is a total of 3 annotation on its first page.
-  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
-
-  // Check that the third annotation is of type "ink".
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 2);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot));
-
-  // Check that the annotation color is blue with opacity.
-  unsigned int R;
-  unsigned int G;
-  unsigned int B;
-  unsigned int A;
-  EXPECT_TRUE(
-      FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
-  EXPECT_EQ(0u, R);
-  EXPECT_EQ(0u, G);
-  EXPECT_EQ(255u, B);
-  EXPECT_EQ(76u, A);
-
-  // Check that there is no content.
-  EXPECT_EQ(2u, FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0));
-
-  // Check that the rectange coordinates are correct.
-  // Note that upon rendering, the rectangle coordinates will be adjusted.
-  FS_RECTF rect;
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_EQ(351.820404f, rect.left);
-  EXPECT_EQ(583.830688f, rect.bottom);
-  EXPECT_EQ(475.336090f, rect.right);
-  EXPECT_EQ(681.535034f, rect.top);
-
-  FPDFPage_CloseAnnot(annot);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, AddIllegalSubtypeAnnotation) {
-  // Open a file with one annotation and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Add an annotation with an illegal subtype.
-  ASSERT_FALSE(FPDFPage_CreateAnnot(page, -1));
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, AddFirstTextAnnotation) {
-  // Open a file with no annotation and load its first page.
-  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
-
-  // Add a text annotation to the page.
-  FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_TEXT);
-  ASSERT_TRUE(annot);
-
-  // Check that there is now 1 annotations on this page.
-  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
-
-  // Check that the subtype of the annotation is correct.
-  EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot));
-  FPDFPage_CloseAnnot(annot);
-
-  annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot));
-
-  // Set the color of the annotation.
-  ASSERT_TRUE(
-      FPDFAnnot_SetColor(annot, FPDFANNOT_COLORTYPE_Color, 51, 102, 153, 204));
-  // Check that the color has been set correctly.
-  unsigned int R;
-  unsigned int G;
-  unsigned int B;
-  unsigned int A;
-  EXPECT_TRUE(
-      FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
-  EXPECT_EQ(51u, R);
-  EXPECT_EQ(102u, G);
-  EXPECT_EQ(153u, B);
-  EXPECT_EQ(204u, A);
-
-  // Change the color of the annotation.
-  ASSERT_TRUE(
-      FPDFAnnot_SetColor(annot, FPDFANNOT_COLORTYPE_Color, 204, 153, 102, 51));
-  // Check that the color has been set correctly.
-  EXPECT_TRUE(
-      FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
-  EXPECT_EQ(204u, R);
-  EXPECT_EQ(153u, G);
-  EXPECT_EQ(102u, B);
-  EXPECT_EQ(51u, A);
-
-  // Set the annotation rectangle.
-  FS_RECTF rect;
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_EQ(0.f, rect.left);
-  EXPECT_EQ(0.f, rect.right);
-  rect.left = 35;
-  rect.bottom = 150;
-  rect.right = 53;
-  rect.top = 165;
-  ASSERT_TRUE(FPDFAnnot_SetRect(annot, &rect));
-  // Check that the annotation rectangle has been set correctly.
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_EQ(35.f, rect.left);
-  EXPECT_EQ(150.f, rect.bottom);
-  EXPECT_EQ(53.f, rect.right);
-  EXPECT_EQ(165.f, rect.top);
-
-  // Set the content of the annotation.
-  static constexpr wchar_t contents[] = L"Hello! This is a customized content.";
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
-      GetFPDFWideString(contents);
-  ASSERT_TRUE(FPDFAnnot_SetStringValue(annot, kContentsKey, text.get()));
-  // Check that the content has been set correctly.
-  unsigned long len = FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(74u,
-            FPDFAnnot_GetStringValue(annot, kContentsKey, buf.data(), len));
-  EXPECT_STREQ(contents, BufferToWString(buf).c_str());
-
-  FPDFPage_CloseAnnot(annot);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, AddAndSaveUnderlineAnnotation) {
-  // Open a file with one annotation and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Check that there is a total of one annotation on its first page, and verify
-  // its quadpoints.
-  EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-  FS_QUADPOINTSF quadpoints;
-  ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &quadpoints));
-  EXPECT_EQ(115.802643f, quadpoints.x1);
-  EXPECT_EQ(718.913940f, quadpoints.y1);
-  EXPECT_EQ(157.211182f, quadpoints.x4);
-  EXPECT_EQ(706.264465f, quadpoints.y4);
-  FPDFPage_CloseAnnot(annot);
-
-  // Add an underline annotation to the page and set its quadpoints.
-  annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE);
-  ASSERT_TRUE(annot);
-  quadpoints.x1 = 140.802643f;
-  quadpoints.x3 = 140.802643f;
-  ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, &quadpoints));
-  FPDFPage_CloseAnnot(annot);
-
-  // Save the document, closing the page and document.
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  // Open the saved document.
-  const char md5[] = "dba153419f67b7c0c0e3d22d3e8910d5";
-
-  OpenSavedDocument();
-  page = LoadSavedPage(0);
-  VerifySavedRendering(page, 612, 792, md5);
-
-  // Check that the saved document has 2 annotations on the first page
-  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
-
-  // Check that the second annotation is an underline annotation and verify
-  // its quadpoints.
-  FPDF_ANNOTATION new_annot = FPDFPage_GetAnnot(page, 1);
-  ASSERT_TRUE(new_annot);
-  EXPECT_EQ(FPDF_ANNOT_UNDERLINE, FPDFAnnot_GetSubtype(new_annot));
-  FS_QUADPOINTSF new_quadpoints;
-  ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(new_annot, &new_quadpoints));
-  EXPECT_NEAR(quadpoints.x1, new_quadpoints.x1, 0.001f);
-  EXPECT_NEAR(quadpoints.y1, new_quadpoints.y1, 0.001f);
-  EXPECT_NEAR(quadpoints.x4, new_quadpoints.x4, 0.001f);
-  EXPECT_NEAR(quadpoints.y4, new_quadpoints.y4, 0.001f);
-
-  FPDFPage_CloseAnnot(new_annot);
-
-  CloseSavedPage(page);
-  CloseSavedDocument();
-}
-
-TEST_F(FPDFAnnotEmbeddertest, ModifyRectQuadpointsWithAP) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_original[] = "63af8432fab95a67cdebb7cd0e514941";
-  const char md5_modified_highlight[] = "aec26075011349dec9bace891856b5f2";
-  const char md5_modified_square[] = "057f57a32be95975775e5ec513fdcb56";
-#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  const char md5_original[] = "0e27376094f11490f74c65f3dc3a42c5";
-  const char md5_modified_highlight[] = "66f3caef3a7d488a4fa1ad37fc06310e";
-  const char md5_modified_square[] = "a456dad0bc6801ee2d6408a4394af563";
-#else
-  const char md5_original[] = "0e27376094f11490f74c65f3dc3a42c5";
-  const char md5_modified_highlight[] = "66f3caef3a7d488a4fa1ad37fc06310e";
-  const char md5_modified_square[] = "a456dad0bc6801ee2d6408a4394af563";
-#endif
-
-  // Open a file with four annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(4, FPDFPage_GetAnnotCount(page));
-
-  // Check that the original file renders correctly.
-  FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, md5_original);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Retrieve the highlight annotation which has its AP stream already defined.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
-
-  // Check that color cannot be set when an AP stream is defined already.
-  EXPECT_FALSE(
-      FPDFAnnot_SetColor(annot, FPDFANNOT_COLORTYPE_Color, 51, 102, 153, 204));
-
-  // Verify its attachment points.
-  FS_QUADPOINTSF quadpoints;
-  ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &quadpoints));
-  EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
-  EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
-  EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
-  EXPECT_NEAR(704.796f, quadpoints.y4, 0.001f);
-
-  // Check that updating the attachment points would succeed.
-  quadpoints.x1 -= 50.f;
-  quadpoints.x2 -= 50.f;
-  quadpoints.x3 -= 50.f;
-  quadpoints.x4 -= 50.f;
-  ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, &quadpoints));
-  FS_QUADPOINTSF new_quadpoints;
-  ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &new_quadpoints));
-  EXPECT_EQ(quadpoints.x1, new_quadpoints.x1);
-  EXPECT_EQ(quadpoints.y1, new_quadpoints.y1);
-  EXPECT_EQ(quadpoints.x4, new_quadpoints.x4);
-  EXPECT_EQ(quadpoints.y4, new_quadpoints.y4);
-
-  // Check that updating quadpoints does not change the annotation's position.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, md5_original);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Verify its annotation rectangle.
-  FS_RECTF rect;
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_NEAR(67.7299f, rect.left, 0.001f);
-  EXPECT_NEAR(704.296f, rect.bottom, 0.001f);
-  EXPECT_NEAR(136.325f, rect.right, 0.001f);
-  EXPECT_NEAR(721.292f, rect.top, 0.001f);
-
-  // Check that updating the rectangle would succeed.
-  rect.left -= 60.f;
-  rect.right -= 60.f;
-  ASSERT_TRUE(FPDFAnnot_SetRect(annot, &rect));
-  FS_RECTF new_rect;
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
-  EXPECT_EQ(rect.right, new_rect.right);
-  FPDFPage_CloseAnnot(annot);
-
-  // Check that updating the rectangle changes the annotation's position.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, md5_modified_highlight);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Retrieve the square annotation which has its AP stream already defined.
-  annot = FPDFPage_GetAnnot(page, 2);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot));
-
-  // Check that updating the rectangle would succeed.
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  rect.left += 70.f;
-  rect.right += 70.f;
-  ASSERT_TRUE(FPDFAnnot_SetRect(annot, &rect));
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
-  EXPECT_EQ(rect.right, new_rect.right);
-
-  // Check that updating the rectangle changes the square annotation's position.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, md5_modified_square);
-  FPDFBitmap_Destroy(bitmap);
-
-  FPDFPage_CloseAnnot(annot);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, RemoveAnnotation) {
-  // Open a file with 3 annotations on its first page.
-  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
-
-  // Check that the annotations have the expected rectangle coordinates.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  FS_RECTF rect;
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_NEAR(86.1971f, rect.left, 0.001f);
-  FPDFPage_CloseAnnot(annot);
-
-  annot = FPDFPage_GetAnnot(page, 1);
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_NEAR(149.8127f, rect.left, 0.001f);
-  FPDFPage_CloseAnnot(annot);
-
-  annot = FPDFPage_GetAnnot(page, 2);
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_NEAR(351.8204f, rect.left, 0.001f);
-  FPDFPage_CloseAnnot(annot);
-
-  // Check that nothing happens when attempting to remove an annotation with an
-  // out-of-bound index.
-  EXPECT_FALSE(FPDFPage_RemoveAnnot(page, 4));
-  EXPECT_FALSE(FPDFPage_RemoveAnnot(page, -1));
-  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
-
-  // Remove the second annotation.
-  EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1));
-  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
-  EXPECT_FALSE(FPDFPage_GetAnnot(page, 2));
-
-  // Save the document, closing the page and document.
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  // TODO(npm): VerifySavedRendering changes annot rect dimensions by 1??
-  // Open the saved document.
-  std::string new_file = GetString();
-  FPDF_FILEACCESS file_access;
-  memset(&file_access, 0, sizeof(file_access));
-  file_access.m_FileLen = new_file.size();
-  file_access.m_GetBlock = GetBlockFromString;
-  file_access.m_Param = &new_file;
-  FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
-  ASSERT_TRUE(new_doc);
-  FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
-  ASSERT_TRUE(new_page);
-
-  // Check that the saved document has 2 annotations on the first page.
-  EXPECT_EQ(2, FPDFPage_GetAnnotCount(new_page));
-
-  // Check that the remaining 2 annotations are the original 1st and 3rd ones by
-  // verifying their rectangle coordinates.
-  annot = FPDFPage_GetAnnot(new_page, 0);
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_NEAR(86.1971f, rect.left, 0.001f);
-  FPDFPage_CloseAnnot(annot);
-
-  annot = FPDFPage_GetAnnot(new_page, 1);
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
-  EXPECT_NEAR(351.8204f, rect.left, 0.001f);
-  FPDFPage_CloseAnnot(annot);
-  FPDF_ClosePage(new_page);
-  FPDF_CloseDocument(new_doc);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, AddAndModifyPath) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_original[] = "c35408717759562d1f8bf33d317483d2";
-  const char md5_modified_path[] = "cf3cea74bd46497520ff6c4d1ea228c8";
-  const char md5_two_paths[] = "e8994452fc4385337bae5522354e10ff";
-  const char md5_new_annot[] = "ee5372b31fede117fc83b9384598aa25";
-#else
-  const char md5_original[] = "964f89bbe8911e540a465cf1a64b7f7e";
-  const char md5_modified_path[] = "3f77b88ce6048e08e636c9a03921b2e5";
-  const char md5_two_paths[] = "bffbf5ecd15862b9fe553c795400ff8e";
-  const char md5_new_annot[] = "e020534c7eeea76be537c70d6e359a40";
-#endif
-
-  // Open a file with two annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
-
-  // Check that the page renders correctly.
-  FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_original);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Retrieve the stamp annotation which has its AP stream already defined.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Check that this annotation has one path object and retrieve it.
-  EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
-  FPDF_PAGEOBJECT path = FPDFAnnot_GetObject(annot, 1);
-  EXPECT_FALSE(path);
-  path = FPDFAnnot_GetObject(annot, 0);
-  EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
-  EXPECT_TRUE(path);
-
-  // Modify the color of the path object.
-  EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 0, 0, 0, 255));
-  EXPECT_TRUE(FPDFAnnot_UpdateObject(annot, path));
-
-  // Check that the page with the modified annotation renders correctly.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_modified_path);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Add a second path object to the same annotation.
-  FPDF_PAGEOBJECT dot = FPDFPageObj_CreateNewPath(7, 84);
-  EXPECT_TRUE(FPDFPath_BezierTo(dot, 9, 86, 10, 87, 11, 88));
-  EXPECT_TRUE(FPDFPath_SetStrokeColor(dot, 255, 0, 0, 100));
-  EXPECT_TRUE(FPDFPath_SetStrokeWidth(dot, 14));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(dot, 0, 1));
-  EXPECT_TRUE(FPDFAnnot_AppendObject(annot, dot));
-  EXPECT_EQ(2, FPDFAnnot_GetObjectCount(annot));
-
-  // Check that the page with an annotation with two paths renders correctly.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_two_paths);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Delete the newly added path object.
-  EXPECT_TRUE(FPDFAnnot_RemoveObject(annot, 1));
-  EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
-  FPDFPage_CloseAnnot(annot);
-
-  // Check that the page renders the same as before.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_modified_path);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Create another stamp annotation and set its annotation rectangle.
-  annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP);
-  ASSERT_TRUE(annot);
-  FS_RECTF rect;
-  rect.left = 200.f;
-  rect.bottom = 400.f;
-  rect.right = 500.f;
-  rect.top = 600.f;
-  EXPECT_TRUE(FPDFAnnot_SetRect(annot, &rect));
-
-  // Add a new path to the annotation.
-  FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(200, 500);
-  EXPECT_TRUE(FPDFPath_LineTo(check, 300, 400));
-  EXPECT_TRUE(FPDFPath_LineTo(check, 500, 600));
-  EXPECT_TRUE(FPDFPath_MoveTo(check, 350, 550));
-  EXPECT_TRUE(FPDFPath_LineTo(check, 450, 450));
-  EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 0, 255, 255, 180));
-  EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
-  EXPECT_TRUE(FPDFAnnot_AppendObject(annot, check));
-  EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
-
-  // Check that the annotation's bounding box came from its rectangle.
-  FS_RECTF new_rect;
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
-  EXPECT_EQ(rect.left, new_rect.left);
-  EXPECT_EQ(rect.bottom, new_rect.bottom);
-  EXPECT_EQ(rect.right, new_rect.right);
-  EXPECT_EQ(rect.top, new_rect.top);
-
-  // Save the document, closing the page and document.
-  FPDFPage_CloseAnnot(annot);
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  // Open the saved document.
-  OpenSavedDocument();
-  page = LoadSavedPage(0);
-  VerifySavedRendering(page, 595, 842, md5_new_annot);
-
-  // Check that the document has a correct count of annotations and objects.
-  EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
-  annot = FPDFPage_GetAnnot(page, 2);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
-
-  // Check that the new annotation's rectangle is as defined.
-  ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
-  EXPECT_EQ(rect.left, new_rect.left);
-  EXPECT_EQ(rect.bottom, new_rect.bottom);
-  EXPECT_EQ(rect.right, new_rect.right);
-  EXPECT_EQ(rect.top, new_rect.top);
-
-  FPDFPage_CloseAnnot(annot);
-  CloseSavedPage(page);
-  CloseSavedDocument();
-}
-
-TEST_F(FPDFAnnotEmbeddertest, ModifyAnnotationFlags) {
-  // Open a file with an annotation and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Check that the page renders correctly.
-  FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
-  FPDFBitmap_Destroy(bitmap);
-
-  // Retrieve the annotation.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Check that the original flag values are as expected.
-  int flags = FPDFAnnot_GetFlags(annot);
-  EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
-  EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
-
-  // Set the HIDDEN flag.
-  flags |= FPDF_ANNOT_FLAG_HIDDEN;
-  EXPECT_TRUE(FPDFAnnot_SetFlags(annot, flags));
-  flags = FPDFAnnot_GetFlags(annot);
-  EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_HIDDEN);
-  EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
-
-  // Check that the page renders correctly without rendering the annotation.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
-  FPDFBitmap_Destroy(bitmap);
-
-  // Unset the HIDDEN flag.
-  EXPECT_TRUE(FPDFAnnot_SetFlags(annot, FPDF_ANNOT_FLAG_NONE));
-  EXPECT_FALSE(FPDFAnnot_GetFlags(annot));
-  flags &= ~FPDF_ANNOT_FLAG_HIDDEN;
-  EXPECT_TRUE(FPDFAnnot_SetFlags(annot, flags));
-  flags = FPDFAnnot_GetFlags(annot);
-  EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
-  EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
-
-  // Check that the page renders correctly as before.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
-  FPDFBitmap_Destroy(bitmap);
-
-  FPDFPage_CloseAnnot(annot);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, AddAndModifyImage) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_original[] = "c35408717759562d1f8bf33d317483d2";
-  const char md5_new_image[] = "ff012f5697436dfcaec25b32d1333596";
-  const char md5_modified_image[] = "86cf8cb2755a7a2046a543e66d9c1e61";
-#else
-  const char md5_original[] = "964f89bbe8911e540a465cf1a64b7f7e";
-  const char md5_new_image[] = "9ea8732dc9d579f68853f16892856208";
-  const char md5_modified_image[] = "74239d2a8c55c9de1dbb9cd8781895aa";
-#endif
-
-  // Open a file with two annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
-
-  // Check that the page renders correctly.
-  FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_original);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Create a stamp annotation and set its annotation rectangle.
-  FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP);
-  ASSERT_TRUE(annot);
-  FS_RECTF rect;
-  rect.left = 200.f;
-  rect.bottom = 600.f;
-  rect.right = 400.f;
-  rect.top = 800.f;
-  EXPECT_TRUE(FPDFAnnot_SetRect(annot, &rect));
-
-  // Add a solid-color translucent image object to the new annotation.
-  constexpr int kBitmapSize = 200;
-  FPDF_BITMAP image_bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 1);
-  FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize, 0xeeeecccc);
-  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(image_bitmap));
-  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(image_bitmap));
-  FPDF_PAGEOBJECT image_object = FPDFPageObj_NewImageObj(document());
-  ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
-  ASSERT_TRUE(FPDFImageObj_SetMatrix(image_object, kBitmapSize, 0, 0,
-                                     kBitmapSize, 0, 0));
-  FPDFPageObj_Transform(image_object, 1, 0, 0, 1, 200, 600);
-  EXPECT_TRUE(FPDFAnnot_AppendObject(annot, image_object));
-  FPDFPage_CloseAnnot(annot);
-
-  // Check that the page renders correctly with the new image object.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_new_image);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Retrieve the newly added stamp annotation and its image object.
-  annot = FPDFPage_GetAnnot(page, 2);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
-  image_object = FPDFAnnot_GetObject(annot, 0);
-  EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(image_object));
-
-  // Modify the image in the new annotation.
-  FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize, 0xff000000);
-  ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
-  EXPECT_TRUE(FPDFAnnot_UpdateObject(annot, image_object));
-  FPDFPage_CloseAnnot(annot);
-
-  // Save the document, closing the page and document.
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-  FPDFBitmap_Destroy(image_bitmap);
-
-  // Test that the saved document renders the modified image object correctly.
-  VerifySavedDocument(595, 842, md5_modified_image);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, AddAndModifyText) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_original[] = "c35408717759562d1f8bf33d317483d2";
-  const char md5_new_text[] = "e5680ed048c2cfd9a1d27212cdf41286";
-  const char md5_modified_text[] = "79f5cfb0b07caaf936f65f6a7a57ce77";
-#else
-  const char md5_original[] = "964f89bbe8911e540a465cf1a64b7f7e";
-  const char md5_new_text[] = "00b14fa2dc1c90d1b0d034e1608efef5";
-  const char md5_modified_text[] = "076c8f24a09ddc0e49f7e758edead6f0";
-#endif
-
-  // Open a file with two annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
-
-  // Check that the page renders correctly.
-  FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_original);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Create a stamp annotation and set its annotation rectangle.
-  FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP);
-  ASSERT_TRUE(annot);
-  FS_RECTF rect;
-  rect.left = 200.f;
-  rect.bottom = 550.f;
-  rect.right = 450.f;
-  rect.top = 650.f;
-  EXPECT_TRUE(FPDFAnnot_SetRect(annot, &rect));
-
-  // Add a translucent text object to the new annotation.
-  FPDF_PAGEOBJECT text_object =
-      FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
-  EXPECT_TRUE(text_object);
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
-      GetFPDFWideString(L"I'm a translucent text laying on other text.");
-  EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
-  EXPECT_TRUE(FPDFText_SetFillColor(text_object, 0, 0, 255, 150));
-  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 600);
-  EXPECT_TRUE(FPDFAnnot_AppendObject(annot, text_object));
-  FPDFPage_CloseAnnot(annot);
-
-  // Check that the page renders correctly with the new text object.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_new_text);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Retrieve the newly added stamp annotation and its text object.
-  annot = FPDFPage_GetAnnot(page, 2);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
-  text_object = FPDFAnnot_GetObject(annot, 0);
-  EXPECT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
-
-  // Modify the text in the new annotation.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> new_text =
-      GetFPDFWideString(L"New text!");
-  EXPECT_TRUE(FPDFText_SetText(text_object, new_text.get()));
-  EXPECT_TRUE(FPDFAnnot_UpdateObject(annot, text_object));
-  FPDFPage_CloseAnnot(annot);
-
-  // Check that the page renders correctly with the modified text object.
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_modified_text);
-  FPDFBitmap_Destroy(bitmap);
-
-  // Remove the new annotation, and check that the page renders as before.
-  EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 2));
-  bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
-  CompareBitmap(bitmap, 595, 842, md5_original);
-  FPDFBitmap_Destroy(bitmap);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, GetSetStringValue) {
-  // Open a file with four annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Retrieve the first annotation.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Check that a non-existent key does not exist.
-  EXPECT_FALSE(FPDFAnnot_HasKey(annot, "none"));
-
-  // Check that the string value of a non-string dictionary entry is empty.
-  static constexpr char kApKey[] = "AP";
-  EXPECT_TRUE(FPDFAnnot_HasKey(annot, kApKey));
-  EXPECT_EQ(FPDF_OBJECT_REFERENCE, FPDFAnnot_GetValueType(annot, kApKey));
-  EXPECT_EQ(2u, FPDFAnnot_GetStringValue(annot, kApKey, nullptr, 0));
-
-  // Check that the string value of the hash is correct.
-  static constexpr char kHashKey[] = "AAPL:Hash";
-  EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot, kHashKey));
-  unsigned long len = FPDFAnnot_GetStringValue(annot, kHashKey, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(66u, FPDFAnnot_GetStringValue(annot, kHashKey, buf.data(), len));
-  EXPECT_STREQ(L"395fbcb98d558681742f30683a62a2ad",
-               BufferToWString(buf).c_str());
-
-  // Check that the string value of the modified date is correct.
-  static constexpr char kDateKey[] = "M";
-  EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot, kHashKey));
-  len = FPDFAnnot_GetStringValue(annot, kDateKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(44u, FPDFAnnot_GetStringValue(annot, kDateKey, buf.data(), len));
-  EXPECT_STREQ(L"D:201706071721Z00'00'", BufferToWString(buf).c_str());
-
-  // Update the date entry for the annotation.
-  const wchar_t new_date[] = L"D:201706282359Z00'00'";
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
-      GetFPDFWideString(new_date);
-  EXPECT_TRUE(FPDFAnnot_SetStringValue(annot, kDateKey, text.get()));
-
-  // Save the document, closing the page and document.
-  FPDFPage_CloseAnnot(annot);
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  // Open the saved annotation.
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5[] = "4d64e61c9c0f8c60ab3cc3234bb73b1c";
-#else
-  const char md5[] = "c96ee1f316d7f5a1b154de9f9d467f01";
-#endif
-  OpenSavedDocument();
-  page = LoadSavedPage(0);
-  VerifySavedRendering(page, 595, 842, md5);
-  FPDF_ANNOTATION new_annot = FPDFPage_GetAnnot(page, 0);
-
-  // Check that the string value of the modified date is the newly-set value.
-  EXPECT_EQ(FPDF_OBJECT_STRING, FPDFAnnot_GetValueType(new_annot, kDateKey));
-  len = FPDFAnnot_GetStringValue(new_annot, kDateKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(44u,
-            FPDFAnnot_GetStringValue(new_annot, kDateKey, buf.data(), len));
-  EXPECT_STREQ(new_date, BufferToWString(buf).c_str());
-
-  FPDFPage_CloseAnnot(new_annot);
-  CloseSavedPage(page);
-  CloseSavedDocument();
-}
-
-TEST_F(FPDFAnnotEmbeddertest, GetSetAP) {
-  // Open a file with four annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Retrieve the first annotation.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Check that the string value of an AP returns the expected length.
-  unsigned long normal_len =
-      FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
-  EXPECT_EQ(73970u, normal_len);
-
-  // Check that the string value of an AP is not returned if the buffer is too
-  // small. The result buffer should be overwritten with an empty string.
-  std::vector<char> buf(normal_len - 1);
-  // Write L"z" in the buffer to verify it's not overwritten.
-  wcscpy(reinterpret_cast<wchar_t*>(buf.data()), L"z");
-  EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                                    buf.data(), buf.size()));
-  std::string ap = BufferToString(buf);
-  EXPECT_STREQ("z", ap.c_str());
-
-  // Check that the string value of an AP is returned through a buffer that is
-  // the right size.
-  buf.clear();
-  buf.resize(normal_len);
-  EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                                    buf.data(), buf.size()));
-  ap = BufferToString(buf);
-  EXPECT_THAT(ap, testing::StartsWith("q Q q 7.442786 w 2 J"));
-  EXPECT_THAT(ap, testing::EndsWith("c 716.5381 327.7156 l S Q Q"));
-
-  // Check that the string value of an AP is returned through a buffer that is
-  // larger than necessary.
-  buf.clear();
-  buf.resize(normal_len + 1);
-  EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                                    buf.data(), buf.size()));
-  ap = BufferToString(buf);
-  EXPECT_THAT(ap, testing::StartsWith("q Q q 7.442786 w 2 J"));
-  EXPECT_THAT(ap, testing::EndsWith("c 716.5381 327.7156 l S Q Q"));
-
-  // Check that getting an AP for a mode that does not have an AP returns an
-  // empty string.
-  unsigned long rollover_len =
-      FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
-  EXPECT_EQ(2u, rollover_len);
-
-  buf.clear();
-  buf.resize(1000);
-  EXPECT_EQ(2u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
-                                buf.data(), buf.size()));
-  EXPECT_STREQ("", BufferToString(buf).c_str());
-
-  // Check that setting the AP for an invalid appearance mode fails.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> apText =
-      GetFPDFWideString(L"new test ap");
-  EXPECT_FALSE(FPDFAnnot_SetAP(annot, -1, apText.get()));
-  EXPECT_FALSE(
-      FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_COUNT, apText.get()));
-  EXPECT_FALSE(FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_COUNT + 1,
-                               apText.get()));
-
-  // Set the AP correctly now.
-  EXPECT_TRUE(
-      FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, apText.get()));
-
-  // Check that the new annotation value is equal to the value we just set.
-  rollover_len =
-      FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
-  EXPECT_EQ(24u, rollover_len);
-  buf.clear();
-  buf.resize(rollover_len);
-  EXPECT_EQ(24u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
-                                 buf.data(), buf.size()));
-  EXPECT_STREQ(L"new test ap", BufferToWString(buf).c_str());
-
-  // Check that the Normal AP was not touched when the Rollover AP was set.
-  buf.clear();
-  buf.resize(normal_len);
-  EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                                    buf.data(), buf.size()));
-  ap = BufferToString(buf);
-  EXPECT_THAT(ap, testing::StartsWith("q Q q 7.442786 w 2 J"));
-  EXPECT_THAT(ap, testing::EndsWith("c 716.5381 327.7156 l S Q Q"));
-
-  // Save the modified document, then reopen it.
-  FPDFPage_CloseAnnot(annot);
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  OpenSavedDocument();
-  page = LoadSavedPage(0);
-  FPDF_ANNOTATION new_annot = FPDFPage_GetAnnot(page, 0);
-
-  // Check that the new annotation value is equal to the value we set before
-  // saving.
-  rollover_len = FPDFAnnot_GetAP(new_annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
-                                 nullptr, 0);
-  EXPECT_EQ(24u, rollover_len);
-  buf.clear();
-  buf.resize(rollover_len);
-  EXPECT_EQ(24u, FPDFAnnot_GetAP(new_annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
-                                 buf.data(), buf.size()));
-  EXPECT_STREQ(L"new test ap", BufferToWString(buf).c_str());
-
-  // Close saved document.
-  FPDFPage_CloseAnnot(new_annot);
-  CloseSavedPage(page);
-  CloseSavedDocument();
-}
-
-TEST_F(FPDFAnnotEmbeddertest, RemoveOptionalAP) {
-  // Open a file with four annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Retrieve the first annotation.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Set Down AP. Normal AP is already set.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> apText =
-      GetFPDFWideString(L"new test ap");
-  EXPECT_TRUE(
-      FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, apText.get()));
-  EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                                    nullptr, 0));
-  EXPECT_EQ(24u,
-            FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
-
-  // Check that setting the Down AP to null removes the Down entry but keeps
-  // Normal intact.
-  EXPECT_TRUE(FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr));
-  EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                                    nullptr, 0));
-  EXPECT_EQ(2u,
-            FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
-
-  FPDFPage_CloseAnnot(annot);
-  FPDF_ClosePage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, RemoveRequiredAP) {
-  // Open a file with four annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Retrieve the first annotation.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Set Down AP. Normal AP is already set.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> apText =
-      GetFPDFWideString(L"new test ap");
-  EXPECT_TRUE(
-      FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, apText.get()));
-  EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
-                                    nullptr, 0));
-  EXPECT_EQ(24u,
-            FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
-
-  // Check that setting the Normal AP to null removes the whole AP dictionary.
-  EXPECT_TRUE(
-      FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr));
-  EXPECT_EQ(
-      2u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0));
-  EXPECT_EQ(2u,
-            FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
-
-  FPDFPage_CloseAnnot(annot);
-  FPDF_ClosePage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, ExtractLinkedAnnotations) {
-  // Open a file with annotations and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(-1, FPDFPage_GetAnnotIndex(page, nullptr));
-
-  // Retrieve the highlight annotation which has its popup defined.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-  EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
-  EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot));
-  static constexpr char kPopupKey[] = "Popup";
-  ASSERT_TRUE(FPDFAnnot_HasKey(annot, kPopupKey));
-  ASSERT_EQ(FPDF_OBJECT_REFERENCE, FPDFAnnot_GetValueType(annot, kPopupKey));
-
-  // Retrieve and verify the popup of the highlight annotation.
-  FPDF_ANNOTATION popup = FPDFAnnot_GetLinkedAnnot(annot, kPopupKey);
-  ASSERT_TRUE(popup);
-  EXPECT_EQ(FPDF_ANNOT_POPUP, FPDFAnnot_GetSubtype(popup));
-  EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, popup));
-  FS_RECTF rect;
-  ASSERT_TRUE(FPDFAnnot_GetRect(popup, &rect));
-  EXPECT_NEAR(612.0f, rect.left, 0.001f);
-  EXPECT_NEAR(578.792, rect.bottom, 0.001f);
-
-  // Attempting to retrieve |annot|'s "IRT"-linked annotation would fail, since
-  // "IRT" is not a key in |annot|'s dictionary.
-  static constexpr char kIRTKey[] = "IRT";
-  ASSERT_FALSE(FPDFAnnot_HasKey(annot, kIRTKey));
-  EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot, kIRTKey));
-
-  // Attempting to retrieve |annot|'s parent dictionary as an annotation would
-  // fail, since its parent is not an annotation.
-  static constexpr char kPKey[] = "P";
-  ASSERT_TRUE(FPDFAnnot_HasKey(annot, kPKey));
-  EXPECT_EQ(FPDF_OBJECT_REFERENCE, FPDFAnnot_GetValueType(annot, kPKey));
-  EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot, kPKey));
-
-  FPDFPage_CloseAnnot(popup);
-  FPDFPage_CloseAnnot(annot);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, GetFormFieldFlagsTextField) {
-  // Open file with form text fields.
-  ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Retrieve the first annotation: user-editable text field.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Check that the flag values are as expected.
-  int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
-  FPDFPage_CloseAnnot(annot);
-
-  // Retrieve the second annotation: read-only text field.
-  annot = FPDFPage_GetAnnot(page, 1);
-  ASSERT_TRUE(annot);
-
-  // Check that the flag values are as expected.
-  flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
-  FPDFPage_CloseAnnot(annot);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, GetFormFieldFlagsComboBox) {
-  // Open file with form text fields.
-  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Retrieve the first annotation: user-editable combobox.
-  FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
-  ASSERT_TRUE(annot);
-
-  // Check that the flag values are as expected.
-  int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
-  FPDFPage_CloseAnnot(annot);
-
-  // Retrieve the second annotation: regular combobox.
-  annot = FPDFPage_GetAnnot(page, 1);
-  ASSERT_TRUE(annot);
-
-  // Check that the flag values are as expected.
-  flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
-  FPDFPage_CloseAnnot(annot);
-
-  // Retrieve the third annotation: read-only combobox.
-  annot = FPDFPage_GetAnnot(page, 2);
-  ASSERT_TRUE(annot);
-
-  // Check that the flag values are as expected.
-  flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
-  FPDFPage_CloseAnnot(annot);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, GetFormAnnotNull) {
-  // Open file with form text fields.
-  EXPECT_TRUE(OpenDocument("text_form.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-
-  // Attempt to get an annotation where no annotation exists on page.
-  FPDF_ANNOTATION annot =
-      FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 0, 0);
-  EXPECT_FALSE(annot);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, GetFormAnnotAndCheckFlagsTextField) {
-  // Open file with form text fields.
-  EXPECT_TRUE(OpenDocument("text_form_multiple.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-
-  // Retrieve user-editable text field annotation.
-  FPDF_ANNOTATION annot =
-      FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 105, 118);
-  ASSERT_TRUE(annot);
-
-  // Check that interactive form annotation flag values are as expected.
-  int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
-  FPDFPage_CloseAnnot(annot);
-
-  // Retrieve read-only text field annotation.
-  annot = FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 105, 202);
-  ASSERT_TRUE(annot);
-
-  // Check that interactive form annotation flag values are as expected.
-  flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
-  FPDFPage_CloseAnnot(annot);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbeddertest, GetFormAnnotAndCheckFlagsComboBox) {
-  // Open file with form comboboxes.
-  EXPECT_TRUE(OpenDocument("combobox_form.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-
-  // Retrieve user-editable combobox annotation.
-  FPDF_ANNOTATION annot =
-      FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 102, 63);
-  ASSERT_TRUE(annot);
-
-  // Check that interactive form annotation flag values are as expected.
-  int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
-  FPDFPage_CloseAnnot(annot);
-
-  // Retrieve regular combobox annotation.
-  annot = FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 102, 113);
-  ASSERT_TRUE(annot);
-
-  // Check that interactive form annotation flag values are as expected.
-  flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
-  FPDFPage_CloseAnnot(annot);
-
-  // Retrieve read-only combobox annotation.
-  annot = FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 102, 213);
-  ASSERT_TRUE(annot);
-
-  // Check that interactive form annotation flag values are as expected.
-  flags = FPDFAnnot_GetFormFieldFlags(page, annot);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
-  EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
-  EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
-  FPDFPage_CloseAnnot(annot);
-
-  UnloadPage(page);
-}
diff --git a/fpdfsdk/fpdfattachment.cpp b/fpdfsdk/fpdfattachment.cpp
deleted file mode 100644
index eb83534..0000000
--- a/fpdfsdk/fpdfattachment.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_attachment.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fdrm/crypto/fx_crypt.h"
-#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/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
-#include "core/fpdfdoc/cpdf_filespec.h"
-#include "core/fpdfdoc/cpdf_nametree.h"
-#include "core/fxcrt/cfx_datetime.h"
-#include "core/fxcrt/fx_extension.h"
-#include "fpdfsdk/fsdk_define.h"
-
-namespace {
-
-constexpr char kChecksumKey[] = "CheckSum";
-
-ByteString CFXByteStringHexDecode(const ByteString& bsHex) {
-  uint8_t* result = nullptr;
-  uint32_t size = 0;
-  HexDecode(bsHex.raw_str(), bsHex.GetLength(), &result, &size);
-  ByteString bsDecoded(result, size);
-  FX_Free(result);
-  return bsDecoded;
-}
-
-ByteString GenerateMD5Base16(const void* contents, const unsigned long len) {
-  uint8_t digest[16];
-  CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(contents), len, digest);
-  char buf[32];
-  for (int i = 0; i < 16; ++i)
-    FXSYS_IntToTwoHexChars(digest[i], &buf[i * 2]);
-
-  return ByteString(buf, 32);
-}
-
-}  // namespace
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return 0;
-
-  return CPDF_NameTree(pDoc, "EmbeddedFiles").GetCount();
-}
-
-FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
-FPDFDoc_AddAttachment(FPDF_DOCUMENT document, FPDF_WIDESTRING name) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  WideString wsName =
-      WideString::FromUTF16LE(name, WideString::WStringLength(name));
-  if (!pDoc || wsName.IsEmpty())
-    return nullptr;
-
-  CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return nullptr;
-
-  // Retrieve the document's Names dictionary; create it if missing.
-  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
-  if (!pNames) {
-    pNames = pDoc->NewIndirect<CPDF_Dictionary>();
-    pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
-  }
-
-  // Create the EmbeddedFiles dictionary if missing.
-  if (!pNames->GetDictFor("EmbeddedFiles")) {
-    CPDF_Dictionary* pFiles = pDoc->NewIndirect<CPDF_Dictionary>();
-    pFiles->SetNewFor<CPDF_Array>("Names");
-    pNames->SetNewFor<CPDF_Reference>("EmbeddedFiles", pDoc,
-                                      pFiles->GetObjNum());
-  }
-
-  // Set up the basic entries in the filespec dictionary.
-  CPDF_Dictionary* pFile = pDoc->NewIndirect<CPDF_Dictionary>();
-  pFile->SetNewFor<CPDF_Name>("Type", "Filespec");
-  pFile->SetNewFor<CPDF_String>("UF", wsName);
-  pFile->SetNewFor<CPDF_String>("F", wsName);
-
-  // Add the new attachment name and filespec into the document's EmbeddedFiles.
-  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
-  if (!nameTree.AddValueAndName(
-          pdfium::MakeUnique<CPDF_Reference>(pDoc, pFile->GetObjNum()),
-          wsName)) {
-    return nullptr;
-  }
-
-  return pFile;
-}
-
-FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
-FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc || index < 0)
-    return nullptr;
-
-  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
-  if (static_cast<size_t>(index) >= nameTree.GetCount())
-    return nullptr;
-
-  WideString csName;
-  return nameTree.LookupValueAndName(index, &csName);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document, int index) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc || index < 0)
-    return false;
-
-  CPDF_NameTree nameTree(pDoc, "EmbeddedFiles");
-  if (static_cast<size_t>(index) >= nameTree.GetCount())
-    return false;
-
-  return nameTree.DeleteValueAndName(index);
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,
-                       void* buffer,
-                       unsigned long buflen) {
-  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
-  if (!pFile)
-    return 0;
-
-  return Utf16EncodeMaybeCopyAndReturnLength(CPDF_FileSpec(pFile).GetFileName(),
-                                             buffer, buflen);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
-  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
-  if (!pFile)
-    return 0;
-
-  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
-  return pParamsDict ? pParamsDict->KeyExist(key) : 0;
-}
-
-FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
-FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) {
-  if (!FPDFAttachment_HasKey(attachment, key))
-    return FPDF_OBJECT_UNKNOWN;
-
-  CPDF_FileSpec spec(CPDFObjectFromFPDFAttachment(attachment));
-  CPDF_Object* pObj = spec.GetParamsDict()->GetObjectFor(key);
-  return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment,
-                              FPDF_BYTESTRING key,
-                              FPDF_WIDESTRING value) {
-  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
-  if (!pFile)
-    return false;
-
-  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
-  if (!pParamsDict)
-    return false;
-
-  ByteString bsKey = key;
-  ByteString bsValue = CFXByteStringFromFPDFWideString(value);
-  bool bEncodedAsHex = bsKey == kChecksumKey;
-  if (bEncodedAsHex)
-    bsValue = CFXByteStringHexDecode(bsValue);
-
-  pParamsDict->SetNewFor<CPDF_String>(bsKey, bsValue, bEncodedAsHex);
-  return true;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,
-                              FPDF_BYTESTRING key,
-                              void* buffer,
-                              unsigned long buflen) {
-  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
-  if (!pFile)
-    return 0;
-
-  CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict();
-  if (!pParamsDict)
-    return 0;
-
-  ByteString bsKey = key;
-  WideString value = pParamsDict->GetUnicodeTextFor(bsKey);
-  if (bsKey == kChecksumKey && !value.IsEmpty()) {
-    CPDF_String* stringValue = pParamsDict->GetObjectFor(bsKey)->AsString();
-    if (stringValue->IsHex()) {
-      ByteString encoded = PDF_EncodeString(stringValue->GetString(), true);
-      value = CPDF_String(nullptr, encoded, false).GetUnicodeText();
-    }
-  }
-
-  return Utf16EncodeMaybeCopyAndReturnLength(value, buffer, buflen);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment,
-                       FPDF_DOCUMENT document,
-                       const void* contents,
-                       const unsigned long len) {
-  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pFile || !pFile->IsDictionary() || !pDoc || len > INT_MAX)
-    return false;
-
-  // An empty content must have a zero length.
-  if (!contents && len != 0)
-    return false;
-
-  // Create a dictionary for the new embedded file stream.
-  auto pFileStreamDict = pdfium::MakeUnique<CPDF_Dictionary>();
-  CPDF_Dictionary* pParamsDict =
-      pFileStreamDict->SetNewFor<CPDF_Dictionary>("Params");
-
-  // Set the size of the new file in the dictionary.
-  pFileStreamDict->SetNewFor<CPDF_Number>("DL", static_cast<int>(len));
-  pParamsDict->SetNewFor<CPDF_Number>("Size", static_cast<int>(len));
-
-  // Set the creation date of the new attachment in the dictionary.
-  CFX_DateTime dateTime = CFX_DateTime::Now();
-  pParamsDict->SetNewFor<CPDF_String>(
-      "CreationDate",
-      ByteString::Format("D:%d%02d%02d%02d%02d%02d", dateTime.GetYear(),
-                         dateTime.GetMonth(), dateTime.GetDay(),
-                         dateTime.GetHour(), dateTime.GetMinute(),
-                         dateTime.GetSecond()),
-      false);
-
-  // Set the checksum of the new attachment in the dictionary.
-  pParamsDict->SetNewFor<CPDF_String>(
-      kChecksumKey, CFXByteStringHexDecode(GenerateMD5Base16(contents, len)),
-      true);
-
-  // Create the file stream and have the filespec dictionary link to it.
-  std::unique_ptr<uint8_t, FxFreeDeleter> stream(FX_Alloc(uint8_t, len));
-  memcpy(stream.get(), contents, len);
-  CPDF_Stream* pFileStream = pDoc->NewIndirect<CPDF_Stream>(
-      std::move(stream), len, std::move(pFileStreamDict));
-  CPDF_Dictionary* pEFDict =
-      pFile->AsDictionary()->SetNewFor<CPDF_Dictionary>("EF");
-  pEFDict->SetNewFor<CPDF_Reference>("F", pDoc, pFileStream->GetObjNum());
-  return true;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment,
-                       void* buffer,
-                       unsigned long buflen) {
-  CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment);
-  if (!pFile)
-    return 0;
-
-  CPDF_Stream* pFileStream = CPDF_FileSpec(pFile).GetFileStream();
-  if (!pFileStream)
-    return 0;
-
-  return DecodeStreamMaybeCopyAndReturnLength(pFileStream, buffer, buflen);
-}
diff --git a/fpdfsdk/fpdfattachment_embeddertest.cpp b/fpdfsdk/fpdfattachment_embeddertest.cpp
deleted file mode 100644
index dd9b5ae..0000000
--- a/fpdfsdk/fpdfattachment_embeddertest.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "public/fpdf_attachment.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-
-static constexpr char kDateKey[] = "CreationDate";
-static constexpr char kChecksumKey[] = "CheckSum";
-
-class FPDFAttachmentEmbeddertest : public EmbedderTest {};
-
-TEST_F(FPDFAttachmentEmbeddertest, ExtractAttachments) {
-  // Open a file with two attachments.
-  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
-  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
-
-  // Retrieve the first attachment.
-  FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
-  ASSERT_TRUE(attachment);
-
-  // Check that the name of the first attachment is correct.
-  unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
-  EXPECT_STREQ(L"1.txt",
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Check that the content of the first attachment is correct.
-  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  ASSERT_EQ(4u, FPDFAttachment_GetFile(attachment, buf.data(), len));
-  EXPECT_EQ(std::string("test"), std::string(buf.data(), 4));
-
-  // Check that a non-existent key does not exist.
-  EXPECT_FALSE(FPDFAttachment_HasKey(attachment, "none"));
-
-  // Check that the string value of a non-string dictionary entry is empty.
-  static constexpr char kSizeKey[] = "Size";
-  EXPECT_EQ(FPDF_OBJECT_NUMBER,
-            FPDFAttachment_GetValueType(attachment, kSizeKey));
-  EXPECT_EQ(2u,
-            FPDFAttachment_GetStringValue(attachment, kSizeKey, nullptr, 0));
-
-  // Check that the creation date of the first attachment is correct.
-  len = FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
-                                               len));
-  EXPECT_STREQ(L"D:20170712214438-07'00'",
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Retrieve the second attachment.
-  attachment = FPDFDoc_GetAttachment(document(), 1);
-  ASSERT_TRUE(attachment);
-
-  // Retrieve the second attachment file.
-  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(5869u, FPDFAttachment_GetFile(attachment, buf.data(), len));
-
-  // Check that the calculated checksum of the file data matches expectation.
-  const char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18";
-  const wchar_t kCheckSumW[] = L"<72AFCDDEDF554DDA63C0C88E06F1CE18>";
-  const std::string generated_checksum =
-      GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len);
-  EXPECT_EQ(kCheckSum, generated_checksum);
-
-  // Check that the stored checksum matches expectation.
-  len = FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
-                                               buf.data(), len));
-  EXPECT_EQ(kCheckSumW,
-            GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data())));
-}
-
-TEST_F(FPDFAttachmentEmbeddertest, AddAttachments) {
-  // Open a file with two attachments.
-  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
-  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
-
-  // Check that adding an attachment with an empty name would fail.
-  EXPECT_FALSE(FPDFDoc_AddAttachment(document(), nullptr));
-
-  // Add an attachment to the beginning of the embedded file list.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> file_name =
-      GetFPDFWideString(L"0.txt");
-  FPDF_ATTACHMENT attachment =
-      FPDFDoc_AddAttachment(document(), file_name.get());
-
-  // Check that writing to a file with nullptr but non-zero bytes would fail.
-  EXPECT_FALSE(FPDFAttachment_SetFile(attachment, document(), nullptr, 10));
-
-  // Set the new attachment's file.
-  constexpr char kContents1[] = "Hello!";
-  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1,
-                                     strlen(kContents1)));
-
-  // Verify the name of the new attachment (i.e. the first attachment).
-  attachment = FPDFDoc_GetAttachment(document(), 0);
-  ASSERT_TRUE(attachment);
-  unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
-  EXPECT_STREQ(L"0.txt",
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Verify the content of the new attachment (i.e. the first attachment).
-  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  ASSERT_EQ(6u, FPDFAttachment_GetFile(attachment, buf.data(), len));
-  EXPECT_EQ(std::string(kContents1), std::string(buf.data(), 6));
-
-  // Add an attachment to the end of the embedded file list and set its file.
-  file_name = GetFPDFWideString(L"z.txt");
-  attachment = FPDFDoc_AddAttachment(document(), file_name.get());
-  constexpr char kContents2[] = "World!";
-  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2,
-                                     strlen(kContents2)));
-  EXPECT_EQ(4, FPDFDoc_GetAttachmentCount(document()));
-
-  // Verify the name of the new attachment (i.e. the fourth attachment).
-  attachment = FPDFDoc_GetAttachment(document(), 3);
-  ASSERT_TRUE(attachment);
-  len = FPDFAttachment_GetName(attachment, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
-  EXPECT_STREQ(L"z.txt",
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Verify the content of the new attachment (i.e. the fourth attachment).
-  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  ASSERT_EQ(6u, FPDFAttachment_GetFile(attachment, buf.data(), len));
-  EXPECT_EQ(std::string(kContents2), std::string(buf.data(), 6));
-}
-
-TEST_F(FPDFAttachmentEmbeddertest, AddAttachmentsWithParams) {
-  // Open a file with two attachments.
-  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
-  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
-
-  // Add an attachment to the embedded file list.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> file_name =
-      GetFPDFWideString(L"5.txt");
-  FPDF_ATTACHMENT attachment =
-      FPDFDoc_AddAttachment(document(), file_name.get());
-  constexpr char kContents[] = "Hello World!";
-  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents,
-                                     strlen(kContents)));
-
-  // Set the date to be an arbitrary value.
-  constexpr wchar_t kDateW[] = L"D:20170720161527-04'00'";
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> ws_date =
-      GetFPDFWideString(kDateW);
-  EXPECT_TRUE(
-      FPDFAttachment_SetStringValue(attachment, kDateKey, ws_date.get()));
-
-  // Set the checksum to be an arbitrary value.
-  constexpr wchar_t kCheckSumW[] = L"<ABCDEF01234567899876543210FEDCBA>";
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> ws_checksum =
-      GetFPDFWideString(kCheckSumW);
-  EXPECT_TRUE(FPDFAttachment_SetStringValue(attachment, kChecksumKey,
-                                            ws_checksum.get()));
-
-  // Verify the name of the new attachment (i.e. the second attachment).
-  attachment = FPDFDoc_GetAttachment(document(), 1);
-  ASSERT_TRUE(attachment);
-  unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
-  EXPECT_STREQ(L"5.txt",
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Verify the content of the new attachment.
-  len = FPDFAttachment_GetFile(attachment, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  ASSERT_EQ(12u, FPDFAttachment_GetFile(attachment, buf.data(), len));
-  EXPECT_EQ(std::string(kContents), std::string(buf.data(), 12));
-
-  // Verify the creation date of the new attachment.
-  len = FPDFAttachment_GetStringValue(attachment, kDateKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(48u, FPDFAttachment_GetStringValue(attachment, kDateKey, buf.data(),
-                                               len));
-  EXPECT_STREQ(kDateW,
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Verify the checksum of the new attachment.
-  len = FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
-                                               buf.data(), len));
-  EXPECT_STREQ(kCheckSumW,
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Overwrite the existing file with empty content, and check that the checksum
-  // gets updated to the correct value.
-  EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), nullptr, 0));
-  EXPECT_EQ(0u, FPDFAttachment_GetFile(attachment, nullptr, 0));
-  len = FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(70u, FPDFAttachment_GetStringValue(attachment, kChecksumKey,
-                                               buf.data(), len));
-  EXPECT_EQ(L"<D41D8CD98F00B204E9800998ECF8427E>",
-            GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data())));
-}
-
-TEST_F(FPDFAttachmentEmbeddertest, DeleteAttachment) {
-  // Open a file with two attachments.
-  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
-  EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document()));
-
-  // Verify the name of the first attachment.
-  FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0);
-  unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), len));
-  EXPECT_STREQ(L"1.txt",
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-
-  // Delete the first attachment.
-  EXPECT_TRUE(FPDFDoc_DeleteAttachment(document(), 0));
-  EXPECT_EQ(1, FPDFDoc_GetAttachmentCount(document()));
-
-  // Verify the name of the new first attachment.
-  attachment = FPDFDoc_GetAttachment(document(), 0);
-  len = FPDFAttachment_GetName(attachment, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(26u, FPDFAttachment_GetName(attachment, buf.data(), len));
-  EXPECT_STREQ(L"attached.pdf",
-               GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                   .c_str());
-}
diff --git a/fpdfsdk/fpdfcatalog.cpp b/fpdfsdk/fpdfcatalog.cpp
deleted file mode 100644
index 493e5ae..0000000
--- a/fpdfsdk/fpdfcatalog.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_catalog.h"
-
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "fpdfsdk/fsdk_define.h"
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFCatalog_IsTagged(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return false;
-
-  const CPDF_Dictionary* pCatalog = pDoc->GetRoot();
-  if (!pCatalog)
-    return false;
-
-  const CPDF_Dictionary* pMarkInfo = pCatalog->GetDictFor("MarkInfo");
-  return pMarkInfo && pMarkInfo->GetIntegerFor("Marked") != 0;
-}
diff --git a/fpdfsdk/fpdfcatalog_unittest.cpp b/fpdfsdk/fpdfcatalog_unittest.cpp
deleted file mode 100644
index 13c0634..0000000
--- a/fpdfsdk/fpdfcatalog_unittest.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_catalog.h"
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#endif  // PDF_ENABLE_XFA
-
-class CPDF_TestDocument : public CPDF_Document {
- public:
-  CPDF_TestDocument() : CPDF_Document(nullptr) {}
-
-  void SetRoot(CPDF_Dictionary* root) {
-    m_pRootDict = root;
-    GetRoot();
-  }
-};
-
-#ifdef PDF_ENABLE_XFA
-class CPDF_TestXFAContext : public CPDFXFA_Context {
- public:
-  CPDF_TestXFAContext()
-      : CPDFXFA_Context(pdfium::MakeUnique<CPDF_TestDocument>()) {}
-
-  void SetRoot(CPDF_Dictionary* root) {
-    reinterpret_cast<CPDF_TestDocument*>(GetPDFDoc())->SetRoot(root);
-  }
-
-  CPDF_IndirectObjectHolder* GetHolder() { return GetPDFDoc(); }
-};
-using CPDF_TestPdfDocument = CPDF_TestXFAContext;
-#else   // PDF_ENABLE_XFA
-using CPDF_TestPdfDocument = CPDF_TestDocument;
-#endif  // PDF_ENABLE_XFA
-
-class PDFCatalogTest : public testing::Test {
- public:
-  void SetUp() override {
-    CPDF_ModuleMgr::Get()->Init();
-
-    m_pDoc = pdfium::MakeUnique<CPDF_TestPdfDocument>();
-
-    // Setup the root directory.
-    m_pRootObj = pdfium::MakeUnique<CPDF_Dictionary>();
-  }
-
-  void TearDown() override {
-    m_pDoc.reset();
-    CPDF_ModuleMgr::Destroy();
-  }
-
- protected:
-  std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
-  std::unique_ptr<CPDF_Dictionary> m_pRootObj;
-};
-
-TEST_F(PDFCatalogTest, IsTagged) {
-  // Null doc
-  EXPECT_FALSE(FPDFCatalog_IsTagged(nullptr));
-
-  // No root
-  m_pDoc->SetRoot(nullptr);
-  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
-
-  // Empty root
-  m_pDoc->SetRoot(m_pRootObj.get());
-  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
-
-  // Root with other key
-  m_pRootObj->SetNewFor<CPDF_String>("OTHER_KEY", "other value", false);
-  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
-
-  // Root with empty MarkInfo
-  CPDF_Dictionary* markInfoDict =
-      m_pRootObj->SetNewFor<CPDF_Dictionary>("MarkInfo");
-  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
-
-  // MarkInfo present but Marked is 0
-  markInfoDict->SetNewFor<CPDF_Number>("Marked", 0);
-  EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get()));
-
-  // MarkInfo present and Marked is 1, PDF is considered tagged.
-  markInfoDict->SetNewFor<CPDF_Number>("Marked", 1);
-  EXPECT_TRUE(FPDFCatalog_IsTagged(m_pDoc.get()));
-}
diff --git a/fpdfsdk/fpdfdoc.cpp b/fpdfsdk/fpdfdoc.cpp
deleted file mode 100644
index 47ecf42..0000000
--- a/fpdfsdk/fpdfdoc.cpp
+++ /dev/null
@@ -1,425 +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 "public/fpdf_doc.h"
-
-#include <memory>
-#include <set>
-
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfdoc/cpdf_bookmark.h"
-#include "core/fpdfdoc/cpdf_bookmarktree.h"
-#include "core/fpdfdoc/cpdf_dest.h"
-#include "core/fpdfdoc/cpdf_pagelabel.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-CPDF_Bookmark FindBookmark(const CPDF_BookmarkTree& tree,
-                           CPDF_Bookmark bookmark,
-                           const WideString& title,
-                           std::set<CPDF_Dictionary*>* visited) {
-  // Return if already checked to avoid circular calling.
-  if (pdfium::ContainsKey(*visited, bookmark.GetDict()))
-    return CPDF_Bookmark();
-  visited->insert(bookmark.GetDict());
-
-  if (bookmark.GetDict() &&
-      bookmark.GetTitle().CompareNoCase(title.c_str()) == 0) {
-    // First check this item.
-    return bookmark;
-  }
-
-  // Go into children items.
-  CPDF_Bookmark child = tree.GetFirstChild(bookmark);
-  while (child.GetDict() && !pdfium::ContainsKey(*visited, child.GetDict())) {
-    // Check this item and its children.
-    CPDF_Bookmark found = FindBookmark(tree, child, title, visited);
-    if (found.GetDict())
-      return found;
-    child = tree.GetNextSibling(child);
-  }
-  return CPDF_Bookmark();
-}
-
-CPDF_LinkList* GetLinkList(CPDF_Page* page) {
-  if (!page)
-    return nullptr;
-
-  CPDF_Document* pDoc = page->m_pDocument.Get();
-  std::unique_ptr<CPDF_LinkList>* pHolder = pDoc->LinksContext();
-  if (!pHolder->get())
-    *pHolder = pdfium::MakeUnique<CPDF_LinkList>();
-  return pHolder->get();
-}
-
-}  // namespace
-
-FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
-FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK pDict) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-  CPDF_BookmarkTree tree(pDoc);
-  CPDF_Bookmark bookmark =
-      CPDF_Bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  return tree.GetFirstChild(bookmark).GetDict();
-}
-
-FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
-FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK pDict) {
-  if (!pDict)
-    return nullptr;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-  CPDF_BookmarkTree tree(pDoc);
-  CPDF_Bookmark bookmark =
-      CPDF_Bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  return tree.GetNextSibling(bookmark).GetDict();
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFBookmark_GetTitle(FPDF_BOOKMARK pDict, void* buffer, unsigned long buflen) {
-  if (!pDict)
-    return 0;
-  CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  WideString title = bookmark.GetTitle();
-  return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen);
-}
-
-FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
-FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title) {
-  if (!title || title[0] == 0)
-    return nullptr;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-  CPDF_BookmarkTree tree(pDoc);
-  size_t len = WideString::WStringLength(title);
-  WideString encodedTitle = WideString::FromUTF16LE(title, len);
-  std::set<CPDF_Dictionary*> visited;
-  return FindBookmark(tree, CPDF_Bookmark(), encodedTitle, &visited).GetDict();
-}
-
-FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFBookmark_GetDest(FPDF_DOCUMENT document,
-                                                         FPDF_BOOKMARK pDict) {
-  if (!pDict)
-    return nullptr;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-  CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  CPDF_Dest dest = bookmark.GetDest(pDoc);
-  if (dest.GetObject())
-    return dest.GetObject();
-  // If this bookmark is not directly associated with a dest, we try to get
-  // action
-  CPDF_Action action = bookmark.GetAction();
-  if (!action.GetDict())
-    return nullptr;
-  return action.GetDest(pDoc).GetObject();
-}
-
-FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
-FPDFBookmark_GetAction(FPDF_BOOKMARK pDict) {
-  if (!pDict)
-    return nullptr;
-  CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  return bookmark.GetAction().GetDict();
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAction_GetType(FPDF_ACTION pDict) {
-  if (!pDict)
-    return PDFACTION_UNSUPPORTED;
-
-  CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  CPDF_Action::ActionType type = action.GetType();
-  switch (type) {
-    case CPDF_Action::GoTo:
-      return PDFACTION_GOTO;
-    case CPDF_Action::GoToR:
-      return PDFACTION_REMOTEGOTO;
-    case CPDF_Action::URI:
-      return PDFACTION_URI;
-    case CPDF_Action::Launch:
-      return PDFACTION_LAUNCH;
-    default:
-      return PDFACTION_UNSUPPORTED;
-  }
-}
-
-FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document,
-                                                       FPDF_ACTION pDict) {
-  if (!pDict)
-    return nullptr;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-  CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  return action.GetDest(pDoc).GetObject();
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFAction_GetFilePath(FPDF_ACTION pDict, void* buffer, unsigned long buflen) {
-  unsigned long type = FPDFAction_GetType(pDict);
-  if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_LAUNCH)
-    return 0;
-
-  CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  ByteString path = action.GetFilePath().UTF8Encode();
-  unsigned long len = path.GetLength() + 1;
-  if (buffer && len <= buflen)
-    memcpy(buffer, path.c_str(), len);
-  return len;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFAction_GetURIPath(FPDF_DOCUMENT document,
-                      FPDF_ACTION pDict,
-                      void* buffer,
-                      unsigned long buflen) {
-  if (!pDict)
-    return 0;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return 0;
-  CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  ByteString path = action.GetURI(pDoc);
-  unsigned long len = path.GetLength() + 1;
-  if (buffer && len <= buflen)
-    memcpy(buffer, path.c_str(), len);
-  return len;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFDest_GetPageIndex(FPDF_DOCUMENT document, FPDF_DEST pDict) {
-  if (!pDict)
-    return 0;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return 0;
-  CPDF_Dest dest(static_cast<CPDF_Array*>(pDict));
-  return dest.GetPageIndex(pDoc);
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFDest_GetView(FPDF_DEST pDict,
-                 unsigned long* pNumParams,
-                 FS_FLOAT* pParams) {
-  if (!pDict) {
-    *pNumParams = 0;
-    return 0;
-  }
-
-  CPDF_Dest dest(static_cast<CPDF_Array*>(pDict));
-  unsigned long nParams = dest.GetNumParams();
-  ASSERT(nParams <= 4);
-  *pNumParams = nParams;
-  for (unsigned long i = 0; i < nParams; ++i)
-    pParams[i] = dest.GetParam(i);
-  return dest.GetZoomMode();
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFDest_GetLocationInPage(FPDF_DEST pDict,
-                           FPDF_BOOL* hasXVal,
-                           FPDF_BOOL* hasYVal,
-                           FPDF_BOOL* hasZoomVal,
-                           FS_FLOAT* x,
-                           FS_FLOAT* y,
-                           FS_FLOAT* zoom) {
-  if (!pDict)
-    return false;
-
-  auto dest = pdfium::MakeUnique<CPDF_Dest>(static_cast<CPDF_Object*>(pDict));
-
-  // FPDF_BOOL is an int, GetXYZ expects bools.
-  bool bHasX;
-  bool bHasY;
-  bool bHasZoom;
-  if (!dest->GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom))
-    return false;
-
-  *hasXVal = bHasX;
-  *hasYVal = bHasY;
-  *hasZoomVal = bHasZoom;
-  return true;
-}
-
-FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page,
-                                                            double x,
-                                                            double y) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return nullptr;
-
-  CPDF_LinkList* pLinkList = GetLinkList(pPage);
-  if (!pLinkList)
-    return nullptr;
-
-  return pLinkList
-      ->GetLinkAtPoint(pPage,
-                       CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
-                       nullptr)
-      .GetDict();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
-                                                            double x,
-                                                            double y) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return -1;
-
-  CPDF_LinkList* pLinkList = GetLinkList(pPage);
-  if (!pLinkList)
-    return -1;
-
-  int z_order = -1;
-  pLinkList->GetLinkAtPoint(
-      pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
-      &z_order);
-  return z_order;
-}
-
-FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document,
-                                                     FPDF_LINK pDict) {
-  if (!pDict)
-    return nullptr;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-  CPDF_Link link(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  FPDF_DEST dest = link.GetDest(pDoc).GetObject();
-  if (dest)
-    return dest;
-  // If this link is not directly associated with a dest, we try to get action
-  CPDF_Action action = link.GetAction();
-  if (!action.GetDict())
-    return nullptr;
-  return action.GetDest(pDoc).GetObject();
-}
-
-FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK pDict) {
-  if (!pDict)
-    return nullptr;
-
-  CPDF_Link link(ToDictionary(static_cast<CPDF_Object*>(pDict)));
-  return link.GetAction().GetDict();
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page,
-                                                       int* startPos,
-                                                       FPDF_LINK* linkAnnot) {
-  if (!startPos || !linkAnnot)
-    return false;
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage || !pPage->m_pFormDict)
-    return false;
-  CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
-  if (!pAnnots)
-    return false;
-  for (size_t i = *startPos; i < pAnnots->GetCount(); i++) {
-    CPDF_Dictionary* pDict =
-        ToDictionary(static_cast<CPDF_Object*>(pAnnots->GetDirectObjectAt(i)));
-    if (!pDict)
-      continue;
-    if (pDict->GetStringFor("Subtype") == "Link") {
-      *startPos = static_cast<int>(i + 1);
-      *linkAnnot = static_cast<FPDF_LINK>(pDict);
-      return true;
-    }
-  }
-  return false;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK linkAnnot,
-                                                          FS_RECTF* rect) {
-  if (!linkAnnot || !rect)
-    return false;
-  CPDF_Dictionary* pAnnotDict =
-      ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
-  FSRECTFFromCFXFloatRect(pAnnotDict->GetRectFor("Rect"), rect);
-  return true;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK linkAnnot) {
-  if (!linkAnnot)
-    return 0;
-  CPDF_Dictionary* pAnnotDict =
-      ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (!pArray)
-    return 0;
-  return static_cast<int>(pArray->GetCount() / 8);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFLink_GetQuadPoints(FPDF_LINK linkAnnot,
-                       int quadIndex,
-                       FS_QUADPOINTSF* quadPoints) {
-  if (!linkAnnot || !quadPoints)
-    return false;
-  CPDF_Dictionary* pAnnotDict =
-      ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
-  CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (!pArray)
-    return false;
-
-  if (quadIndex < 0 ||
-      static_cast<size_t>(quadIndex) >= pArray->GetCount() / 8 ||
-      (static_cast<size_t>(quadIndex * 8 + 7) >= pArray->GetCount())) {
-    return false;
-  }
-
-  quadPoints->x1 = pArray->GetNumberAt(quadIndex * 8);
-  quadPoints->y1 = pArray->GetNumberAt(quadIndex * 8 + 1);
-  quadPoints->x2 = pArray->GetNumberAt(quadIndex * 8 + 2);
-  quadPoints->y2 = pArray->GetNumberAt(quadIndex * 8 + 3);
-  quadPoints->x3 = pArray->GetNumberAt(quadIndex * 8 + 4);
-  quadPoints->y3 = pArray->GetNumberAt(quadIndex * 8 + 5);
-  quadPoints->x4 = pArray->GetNumberAt(quadIndex * 8 + 6);
-  quadPoints->y4 = pArray->GetNumberAt(quadIndex * 8 + 7);
-  return true;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
-                                                         FPDF_BYTESTRING tag,
-                                                         void* buffer,
-                                                         unsigned long buflen) {
-  if (!tag)
-    return 0;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return 0;
-  pDoc->LoadDocumentInfo();
-  const CPDF_Dictionary* pInfo = pDoc->GetInfo();
-  if (!pInfo)
-    return 0;
-  WideString text = pInfo->GetUnicodeTextFor(tag);
-  return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen);
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDF_GetPageLabel(FPDF_DOCUMENT document,
-                  int page_index,
-                  void* buffer,
-                  unsigned long buflen) {
-  if (page_index < 0)
-    return 0;
-
-  // CPDF_PageLabel can deal with NULL |document|.
-  CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document));
-  Optional<WideString> str = label.GetLabel(page_index);
-  return str.has_value()
-             ? Utf16EncodeMaybeCopyAndReturnLength(str.value(), buffer, buflen)
-             : 0;
-}
diff --git a/fpdfsdk/fpdfdoc_embeddertest.cpp b/fpdfsdk/fpdfdoc_embeddertest.cpp
deleted file mode 100644
index d346330..0000000
--- a/fpdfsdk/fpdfdoc_embeddertest.cpp
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-#include <string>
-
-#include "core/fxcrt/fx_string.h"
-#include "public/fpdf_doc.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-class FPDFDocEmbeddertest : public EmbedderTest {};
-
-TEST_F(FPDFDocEmbeddertest, DestGetPageIndex) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
-
-  // NULL FPDF_DEST case.
-  EXPECT_EQ(0U, FPDFDest_GetPageIndex(document(), nullptr));
-
-  // Page number directly in item from Dests NameTree.
-  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(1U, FPDFDest_GetPageIndex(document(), dest));
-
-  // Page number via object reference in item from Dests NameTree.
-  dest = FPDF_GetNamedDestByName(document(), "Next");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(1U, FPDFDest_GetPageIndex(document(), dest));
-
-  // Page number directly in item from Dests dictionary.
-  dest = FPDF_GetNamedDestByName(document(), "FirstAlternate");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(11U, FPDFDest_GetPageIndex(document(), dest));
-
-  // Invalid object reference in item from Dests NameTree.
-  dest = FPDF_GetNamedDestByName(document(), "LastAlternate");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(0U, FPDFDest_GetPageIndex(document(), dest));
-}
-
-TEST_F(FPDFDocEmbeddertest, DestGetView) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
-
-  unsigned long numParams;
-  FS_FLOAT params[4];
-
-  numParams = 42;
-  std::fill_n(params, 4, 42.4242f);
-  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_UNKNOWN_MODE),
-            FPDFDest_GetView(nullptr, &numParams, params));
-  EXPECT_EQ(0U, numParams);
-  EXPECT_FLOAT_EQ(42.4242f, params[0]);
-
-  numParams = 42;
-  std::fill_n(params, 4, 42.4242f);
-  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_XYZ),
-            FPDFDest_GetView(dest, &numParams, params));
-  EXPECT_EQ(3U, numParams);
-  EXPECT_FLOAT_EQ(0, params[0]);
-  EXPECT_FLOAT_EQ(0, params[1]);
-  EXPECT_FLOAT_EQ(1, params[2]);
-  EXPECT_FLOAT_EQ(42.4242f, params[3]);
-
-  numParams = 42;
-  std::fill_n(params, 4, 42.4242f);
-  dest = FPDF_GetNamedDestByName(document(), "Next");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_FIT),
-            FPDFDest_GetView(dest, &numParams, params));
-  EXPECT_EQ(0U, numParams);
-  EXPECT_FLOAT_EQ(42.4242f, params[0]);
-
-  numParams = 42;
-  std::fill_n(params, 4, 42.4242f);
-  dest = FPDF_GetNamedDestByName(document(), "FirstAlternate");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_XYZ),
-            FPDFDest_GetView(dest, &numParams, params));
-  EXPECT_EQ(3U, numParams);
-  EXPECT_FLOAT_EQ(200, params[0]);
-  EXPECT_FLOAT_EQ(400, params[1]);
-  EXPECT_FLOAT_EQ(800, params[2]);
-  EXPECT_FLOAT_EQ(42.4242f, params[3]);
-
-  numParams = 42;
-  std::fill_n(params, 4, 42.4242f);
-  dest = FPDF_GetNamedDestByName(document(), "LastAlternate");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(static_cast<unsigned long>(PDFDEST_VIEW_XYZ),
-            FPDFDest_GetView(dest, &numParams, params));
-  EXPECT_EQ(3U, numParams);
-  EXPECT_FLOAT_EQ(0, params[0]);
-  EXPECT_FLOAT_EQ(0, params[1]);
-  EXPECT_FLOAT_EQ(-200, params[2]);
-  EXPECT_FLOAT_EQ(42.4242f, params[3]);
-}
-
-TEST_F(FPDFDocEmbeddertest, DestGetLocationInPage) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
-
-  // NULL FPDF_DEST case.
-  EXPECT_EQ(0U, FPDFDest_GetPageIndex(document(), nullptr));
-
-  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
-  EXPECT_TRUE(dest);
-
-  FPDF_BOOL hasX;
-  FPDF_BOOL hasY;
-  FPDF_BOOL hasZoom;
-  FS_FLOAT x;
-  FS_FLOAT y;
-  FS_FLOAT zoom;
-  EXPECT_TRUE(
-      FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom));
-  EXPECT_TRUE(hasX);
-  EXPECT_TRUE(hasY);
-  EXPECT_TRUE(hasZoom);
-  EXPECT_EQ(0, x);
-  EXPECT_EQ(0, y);
-  EXPECT_EQ(1, zoom);
-}
-
-TEST_F(FPDFDocEmbeddertest, BUG_680376) {
-  EXPECT_TRUE(OpenDocument("bug_680376.pdf"));
-
-  // Page number directly in item from Dests NameTree.
-  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First");
-  EXPECT_TRUE(dest);
-  EXPECT_EQ(static_cast<unsigned long>(-1),
-            FPDFDest_GetPageIndex(document(), dest));
-}
-
-TEST_F(FPDFDocEmbeddertest, ActionGetFilePath) {
-  EXPECT_TRUE(OpenDocument("launch_action.pdf"));
-
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // The target action is nearly the size of the whole page.
-  FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100);
-  ASSERT_TRUE(link);
-
-  FPDF_ACTION action = FPDFLink_GetAction(link);
-  ASSERT_TRUE(action);
-
-  const char kExpectedResult[] = "test.pdf";
-  const unsigned long kExpectedLength = sizeof(kExpectedResult);
-  unsigned long bufsize = FPDFAction_GetFilePath(action, nullptr, 0);
-  ASSERT_EQ(kExpectedLength, bufsize);
-
-  char buf[kExpectedLength];
-  EXPECT_EQ(bufsize, FPDFAction_GetFilePath(action, buf, bufsize));
-  EXPECT_EQ(std::string(kExpectedResult), std::string(buf));
-
-  FPDF_ClosePage(page);
-}
-
-TEST_F(FPDFDocEmbeddertest, NoBookmarks) {
-  // Open a file with no bookmarks.
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
-
-  // The non-existent top-level bookmark has no title.
-  unsigned short buf[128];
-  EXPECT_EQ(0u, FPDFBookmark_GetTitle(nullptr, buf, sizeof(buf)));
-
-  // The non-existent top-level bookmark has no children.
-  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(document(), nullptr));
-}
-
-TEST_F(FPDFDocEmbeddertest, Bookmarks) {
-  // Open a file with two bookmarks.
-  EXPECT_TRUE(OpenDocument("bookmarks.pdf"));
-
-  // The existent top-level bookmark has no title.
-  unsigned short buf[128];
-  EXPECT_EQ(0u, FPDFBookmark_GetTitle(nullptr, buf, sizeof(buf)));
-
-  FPDF_BOOKMARK child = FPDFBookmark_GetFirstChild(document(), nullptr);
-  EXPECT_TRUE(child);
-  EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16));
-
-  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(document(), child));
-
-  FPDF_BOOKMARK sibling = FPDFBookmark_GetNextSibling(document(), child);
-  EXPECT_TRUE(sibling);
-  EXPECT_EQ(28u, FPDFBookmark_GetTitle(sibling, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(L"A Good Ending"), WideString::FromUTF16LE(buf, 13));
-
-  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), sibling));
-}
-
-TEST_F(FPDFDocEmbeddertest, FindBookmarks) {
-  // Open a file with two bookmarks.
-  EXPECT_TRUE(OpenDocument("bookmarks.pdf"));
-
-  // Find the first one, based on its known title.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
-      GetFPDFWideString(L"A Good Beginning");
-  FPDF_BOOKMARK child = FPDFBookmark_Find(document(), title.get());
-  EXPECT_TRUE(child);
-
-  // Check that the string matches.
-  unsigned short buf[128];
-  EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16));
-
-  // Check that it is them same as the one returned by GetFirstChild.
-  EXPECT_EQ(child, FPDFBookmark_GetFirstChild(document(), nullptr));
-
-  // Try to find one using a non-existent title.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> bad_title =
-      GetFPDFWideString(L"A BAD Beginning");
-  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), bad_title.get()));
-}
-
-// Check circular bookmarks will not cause infinite loop.
-TEST_F(FPDFDocEmbeddertest, FindBookmarks_bug420) {
-  // Open a file with circular bookmarks.
-  EXPECT_TRUE(OpenDocument("bookmarks_circular.pdf"));
-
-  // Try to find a title.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
-      GetFPDFWideString(L"anything");
-  EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), title.get()));
-}
-
-TEST_F(FPDFDocEmbeddertest, DeletePage) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_EQ(1, FPDF_GetPageCount(document()));
-  FPDFPage_Delete(document(), 0);
-  EXPECT_EQ(0, FPDF_GetPageCount(document()));
-}
-
-TEST_F(FPDFDocEmbeddertest, GetMetaText) {
-  ASSERT_TRUE(OpenDocument("bug_601362.pdf"));
-
-  // Invalid document / tag results in 0.
-  unsigned short buf[128];
-  EXPECT_EQ(0u, FPDF_GetMetaText(document(), nullptr, buf, sizeof(buf)));
-  EXPECT_EQ(0u, FPDF_GetMetaText(nullptr, "", buf, sizeof(buf)));
-
-  // Tags that do not eixst results in an empty wide string.
-  EXPECT_EQ(2u, FPDF_GetMetaText(document(), "", buf, sizeof(buf)));
-  EXPECT_EQ(2u, FPDF_GetMetaText(document(), "foo", buf, sizeof(buf)));
-  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Title", buf, sizeof(buf)));
-  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Author", buf, sizeof(buf)));
-  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Subject", buf, sizeof(buf)));
-  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Keywords", buf, sizeof(buf)));
-  ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Producer", buf, sizeof(buf)));
-
-  constexpr wchar_t kExpectedCreator[] = L"Microsoft Word";
-  ASSERT_EQ(30u, FPDF_GetMetaText(document(), "Creator", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedCreator),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreator)));
-
-  constexpr wchar_t kExpectedCreationDate[] = L"D:20160411190039+00'00'";
-  ASSERT_EQ(48u,
-            FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedCreationDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreationDate)));
-
-  constexpr wchar_t kExpectedModDate[] = L"D:20160411190039+00'00'";
-  ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedModDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
-}
-
-TEST_F(FPDFDocEmbeddertest, GetMetaTextSameObjectNumber) {
-  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
-
-  // The PDF has been edited. It has two %%EOF markers, and 2 objects numbered
-  // (1 0). Both objects are /Info dictionaries, but contain different data.
-  // Make sure ModDate is the date of the last modification.
-  unsigned short buf[128];
-  constexpr wchar_t kExpectedModDate[] = L"D:20170612232940-04'00'";
-  ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedModDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
-}
-
-TEST_F(FPDFDocEmbeddertest, GetMetaTextInAttachmentFile) {
-  ASSERT_TRUE(OpenDocument("embedded_attachments.pdf"));
-
-  // Make sure this is the date from the PDF itself and not the attached PDF.
-  unsigned short buf[128];
-  constexpr wchar_t kExpectedModDate[] = L"D:20170712214448-07'00'";
-  ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedModDate),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate)));
-}
-
-TEST_F(FPDFDocEmbeddertest, GetMetaTextFromNewDocument) {
-  FPDF_DOCUMENT empty_doc = FPDF_CreateNewDocument();
-  unsigned short buf[128];
-  EXPECT_EQ(2u, FPDF_GetMetaText(empty_doc, "Title", buf, sizeof(buf)));
-  FPDF_CloseDocument(empty_doc);
-}
-
-TEST_F(FPDFDocEmbeddertest, NoPageLabels) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
-  EXPECT_EQ(1, FPDF_GetPageCount(document()));
-
-  ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 0, nullptr, 0));
-}
-
-TEST_F(FPDFDocEmbeddertest, GetPageLabels) {
-  EXPECT_TRUE(OpenDocument("page_labels.pdf"));
-  EXPECT_EQ(7, FPDF_GetPageCount(document()));
-
-  // We do not request labels, when use FPDFAvail_IsXXXAvail.
-  // Flush all data, to allow read labels.
-  SetWholeFileAvailable();
-
-  unsigned short buf[128];
-  EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -2, buf, sizeof(buf)));
-  EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -1, buf, sizeof(buf)));
-
-  const wchar_t kExpectedPageLabel0[] = L"i";
-  ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 0, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel0),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel0)));
-
-  const wchar_t kExpectedPageLabel1[] = L"ii";
-  ASSERT_EQ(6u, FPDF_GetPageLabel(document(), 1, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel1),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel1)));
-
-  const wchar_t kExpectedPageLabel2[] = L"1";
-  ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 2, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel2),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel2)));
-
-  const wchar_t kExpectedPageLabel3[] = L"2";
-  ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 3, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel3),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel3)));
-
-  const wchar_t kExpectedPageLabel4[] = L"zzA";
-  ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 4, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel4),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel4)));
-
-  const wchar_t kExpectedPageLabel5[] = L"zzB";
-  ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 5, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel5),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel5)));
-
-  const wchar_t kExpectedPageLabel6[] = L"";
-  ASSERT_EQ(2u, FPDF_GetPageLabel(document(), 6, buf, sizeof(buf)));
-  EXPECT_EQ(WideString(kExpectedPageLabel6),
-            WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel6)));
-
-  ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 7, buf, sizeof(buf)));
-  ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 8, buf, sizeof(buf)));
-}
diff --git a/fpdfsdk/fpdfdoc_unittest.cpp b/fpdfsdk/fpdfdoc_unittest.cpp
deleted file mode 100644
index b52cccf..0000000
--- a/fpdfsdk/fpdfdoc_unittest.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_doc.h"
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#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_null.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_dest.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#endif  // PDF_ENABLE_XFA
-
-class CPDF_TestDocument : public CPDF_Document {
- public:
-  CPDF_TestDocument() : CPDF_Document(nullptr) {}
-
-  void SetRoot(CPDF_Dictionary* root) { m_pRootDict = root; }
-  CPDF_IndirectObjectHolder* GetHolder() { return this; }
-};
-
-#ifdef PDF_ENABLE_XFA
-class CPDF_TestXFAContext : public CPDFXFA_Context {
- public:
-  CPDF_TestXFAContext()
-      : CPDFXFA_Context(pdfium::MakeUnique<CPDF_TestDocument>()) {}
-
-  void SetRoot(CPDF_Dictionary* root) {
-    reinterpret_cast<CPDF_TestDocument*>(GetPDFDoc())->SetRoot(root);
-  }
-
-  CPDF_IndirectObjectHolder* GetHolder() { return GetPDFDoc(); }
-};
-using CPDF_TestPdfDocument = CPDF_TestXFAContext;
-#else   // PDF_ENABLE_XFA
-using CPDF_TestPdfDocument = CPDF_TestDocument;
-#endif  // PDF_ENABLE_XFA
-
-class PDFDocTest : public testing::Test {
- public:
-  struct DictObjInfo {
-    uint32_t num;
-    CPDF_Dictionary* obj;
-  };
-
-  void SetUp() override {
-    CPDF_ModuleMgr::Get()->Init();
-
-    m_pDoc = pdfium::MakeUnique<CPDF_TestPdfDocument>();
-    m_pIndirectObjs = m_pDoc->GetHolder();
-
-    // Setup the root directory.
-    m_pRootObj = pdfium::MakeUnique<CPDF_Dictionary>();
-    m_pDoc->SetRoot(m_pRootObj.get());
-  }
-
-  void TearDown() override {
-    m_pRootObj.reset();
-    m_pIndirectObjs = nullptr;
-    m_pDoc.reset();
-    CPDF_ModuleMgr::Destroy();
-  }
-
-  std::vector<DictObjInfo> CreateDictObjs(int num) {
-    std::vector<DictObjInfo> info;
-    for (int i = 0; i < num; ++i) {
-      // Objects created will be released by the document.
-      CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
-      info.push_back({obj->GetObjNum(), obj});
-    }
-    return info;
-  }
-
- protected:
-  std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
-  UnownedPtr<CPDF_IndirectObjectHolder> m_pIndirectObjs;
-  std::unique_ptr<CPDF_Dictionary> m_pRootObj;
-};
-
-TEST_F(PDFDocTest, FindBookmark) {
-  {
-    // No bookmark information.
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
-        GetFPDFWideString(L"");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-
-    title = GetFPDFWideString(L"Preface");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-  }
-  {
-    // Empty bookmark tree.
-    m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
-        GetFPDFWideString(L"");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-
-    title = GetFPDFWideString(L"Preface");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-  }
-  {
-    // Check on a regular bookmark tree.
-    auto bookmarks = CreateDictObjs(3);
-
-    bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
-                                                bookmarks[0].num);
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
-                                                bookmarks[2].num);
-
-    bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
-                                                bookmarks[0].num);
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs.Get(),
-                                                bookmarks[1].num);
-
-    bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
-    bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
-                                                bookmarks[1].num);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
-                                                bookmarks[2].num);
-
-    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
-                                          bookmarks[0].num);
-
-    // Title with no match.
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
-        GetFPDFWideString(L"Chapter 3");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-
-    // Title with partial match only.
-    title = GetFPDFWideString(L"Chapter");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-
-    // Title with a match.
-    title = GetFPDFWideString(L"Chapter 2");
-    EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-
-    // Title match is case insensitive.
-    title = GetFPDFWideString(L"cHaPter 2");
-    EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-  }
-  {
-    // Circular bookmarks in depth.
-    auto bookmarks = CreateDictObjs(3);
-
-    bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
-                                                bookmarks[0].num);
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
-                                                bookmarks[2].num);
-
-    bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
-                                                bookmarks[1].num);
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
-                                                bookmarks[1].num);
-
-    bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
-    bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
-                                                bookmarks[1].num);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
-                                                bookmarks[2].num);
-
-    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
-                                          bookmarks[0].num);
-
-    // Title with no match.
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
-        GetFPDFWideString(L"Chapter 3");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-
-    // Title with a match.
-    title = GetFPDFWideString(L"Chapter 2");
-    EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-  }
-  {
-    // Circular bookmarks in breadth.
-    auto bookmarks = CreateDictObjs(4);
-
-    bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
-                                                bookmarks[0].num);
-    bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
-                                                bookmarks[2].num);
-
-    bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
-                                                bookmarks[0].num);
-    bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
-                                                bookmarks[3].num);
-
-    bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
-    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
-                                                bookmarks[0].num);
-    bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
-                                                bookmarks[1].num);
-
-    bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
-    bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
-                                                bookmarks[1].num);
-    bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
-                                                bookmarks[2].num);
-
-    m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
-                                          bookmarks[0].num);
-
-    // Title with no match.
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
-        GetFPDFWideString(L"Chapter 8");
-    EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-
-    // Title with a match.
-    title = GetFPDFWideString(L"Chapter 3");
-    EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
-  }
-}
-
-TEST_F(PDFDocTest, GetLocationInPage) {
-  auto array = pdfium::MakeUnique<CPDF_Array>();
-  array->AddNew<CPDF_Number>(0);  // Page Index.
-  array->AddNew<CPDF_Name>("XYZ");
-  array->AddNew<CPDF_Number>(4);  // X
-  array->AddNew<CPDF_Number>(5);  // Y
-  array->AddNew<CPDF_Number>(6);  // Zoom.
-
-  FPDF_BOOL hasX;
-  FPDF_BOOL hasY;
-  FPDF_BOOL hasZoom;
-  FS_FLOAT x;
-  FS_FLOAT y;
-  FS_FLOAT zoom;
-
-  EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
-                                         &x, &y, &zoom));
-  EXPECT_TRUE(hasX);
-  EXPECT_TRUE(hasY);
-  EXPECT_TRUE(hasZoom);
-  EXPECT_EQ(4, x);
-  EXPECT_EQ(5, y);
-  EXPECT_EQ(6, zoom);
-
-  array->SetNewAt<CPDF_Null>(2);
-  array->SetNewAt<CPDF_Null>(3);
-  array->SetNewAt<CPDF_Null>(4);
-  EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
-                                         &x, &y, &zoom));
-  EXPECT_FALSE(hasX);
-  EXPECT_FALSE(hasY);
-  EXPECT_FALSE(hasZoom);
-
-  array = pdfium::MakeUnique<CPDF_Array>();
-  EXPECT_FALSE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
-                                          &x, &y, &zoom));
-}
diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp
deleted file mode 100644
index ee2fc7e..0000000
--- a/fpdfsdk/fpdfedit_embeddertest.cpp
+++ /dev/null
@@ -1,1239 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxcrt/fx_system.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "public/cpp/fpdf_deleters.h"
-#include "public/fpdf_annot.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gmock/include/gmock/gmock-matchers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-class FPDFEditEmbeddertest : public EmbedderTest {
- protected:
-  FPDF_DOCUMENT CreateNewDocument() {
-    document_ = FPDF_CreateNewDocument();
-    cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_);
-    return document_;
-  }
-
-  void CheckFontDescriptor(CPDF_Dictionary* font_dict,
-                           int font_type,
-                           bool bold,
-                           bool italic,
-                           uint32_t size,
-                           const uint8_t* data) {
-    CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor");
-    ASSERT_TRUE(font_desc);
-    EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type"));
-    EXPECT_EQ(font_dict->GetStringFor("BaseFont"),
-              font_desc->GetStringFor("FontName"));
-
-    // Check that the font descriptor has the required keys according to spec
-    // 1.7 Table 5.19
-    ASSERT_TRUE(font_desc->KeyExist("Flags"));
-
-    int font_flags = font_desc->GetIntegerFor("Flags");
-    EXPECT_EQ(bold, FontStyleIsBold(font_flags));
-    EXPECT_EQ(italic, FontStyleIsItalic(font_flags));
-    EXPECT_TRUE(FontStyleIsNonSymbolic(font_flags));
-    ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
-
-    CPDF_Array* fontBBox = font_desc->GetArrayFor("FontBBox");
-    ASSERT_TRUE(fontBBox);
-    EXPECT_EQ(4U, fontBBox->GetCount());
-    // Check that the coordinates are in the preferred order according to spec
-    // 1.7 Section 3.8.4
-    EXPECT_TRUE(fontBBox->GetIntegerAt(0) < fontBBox->GetIntegerAt(2));
-    EXPECT_TRUE(fontBBox->GetIntegerAt(1) < fontBBox->GetIntegerAt(3));
-
-    EXPECT_TRUE(font_desc->KeyExist("ItalicAngle"));
-    EXPECT_TRUE(font_desc->KeyExist("Ascent"));
-    EXPECT_TRUE(font_desc->KeyExist("Descent"));
-    EXPECT_TRUE(font_desc->KeyExist("CapHeight"));
-    EXPECT_TRUE(font_desc->KeyExist("StemV"));
-    ByteString present("FontFile");
-    ByteString absent("FontFile2");
-    if (font_type == FPDF_FONT_TRUETYPE)
-      std::swap(present, absent);
-    EXPECT_TRUE(font_desc->KeyExist(present));
-    EXPECT_FALSE(font_desc->KeyExist(absent));
-
-    // Check that the font stream is the one that was provided
-    CPDF_Stream* font_stream = font_desc->GetStreamFor(present);
-    ASSERT_EQ(size, font_stream->GetRawSize());
-    if (font_type == FPDF_FONT_TRUETYPE) {
-      ASSERT_EQ(static_cast<int>(size),
-                font_stream->GetDict()->GetIntegerFor("Length1"));
-    }
-    uint8_t* stream_data = font_stream->GetRawData();
-    for (size_t j = 0; j < size; j++)
-      EXPECT_EQ(data[j], stream_data[j]) << " at byte " << j;
-  }
-
-  void CheckCompositeFontWidths(CPDF_Array* widths_array,
-                                CPDF_Font* typed_font) {
-    // Check that W array is in a format that conforms to PDF spec 1.7 section
-    // "Glyph Metrics in CIDFonts" (these checks are not
-    // implementation-specific).
-    EXPECT_GT(widths_array->GetCount(), 1U);
-    int num_cids_checked = 0;
-    int cur_cid = 0;
-    for (size_t idx = 0; idx < widths_array->GetCount(); idx++) {
-      int cid = widths_array->GetNumberAt(idx);
-      EXPECT_GE(cid, cur_cid);
-      ASSERT_FALSE(++idx == widths_array->GetCount());
-      CPDF_Object* next = widths_array->GetObjectAt(idx);
-      if (next->IsArray()) {
-        // We are in the c [w1 w2 ...] case
-        CPDF_Array* arr = next->AsArray();
-        int cnt = static_cast<int>(arr->GetCount());
-        size_t inner_idx = 0;
-        for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
-          int width = arr->GetNumberAt(inner_idx++);
-          EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
-                                                               << cur_cid;
-        }
-        num_cids_checked += cnt;
-        continue;
-      }
-      // Otherwise, are in the c_first c_last w case.
-      ASSERT_TRUE(next->IsNumber());
-      int last_cid = next->AsNumber()->GetInteger();
-      ASSERT_FALSE(++idx == widths_array->GetCount());
-      int width = widths_array->GetNumberAt(idx);
-      for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
-        EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
-                                                             << cur_cid;
-      }
-      num_cids_checked += last_cid - cid + 1;
-    }
-    // Make sure we have a good amount of cids described
-    EXPECT_GT(num_cids_checked, 900);
-  }
-  CPDF_Document* cpdf_doc() { return cpdf_doc_; }
-
- private:
-  CPDF_Document* cpdf_doc_;
-};
-
-namespace {
-
-const char kExpectedPDF[] =
-    "%PDF-1.7\r\n"
-    "%\xA1\xB3\xC5\xD7\r\n"
-    "1 0 obj\r\n"
-    "<</Pages 2 0 R /Type/Catalog>>\r\n"
-    "endobj\r\n"
-    "2 0 obj\r\n"
-    "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
-    "endobj\r\n"
-    "3 0 obj\r\n"
-    "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
-    "endobj\r\n"
-    "4 0 obj\r\n"
-    "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
-    "/Resources<</ExtGState<</FXE1 5 0 R >>>>"
-    "/Rotate 0/Type/Page"
-    ">>\r\n"
-    "endobj\r\n"
-    "5 0 obj\r\n"
-    "<</BM/Normal/CA 1/ca 1>>\r\n"
-    "endobj\r\n"
-    "xref\r\n"
-    "0 6\r\n"
-    "0000000000 65535 f\r\n"
-    "0000000017 00000 n\r\n"
-    "0000000066 00000 n\r\n"
-    "0000000122 00000 n\r\n"
-    "0000000192 00000 n\r\n"
-    "0000000311 00000 n\r\n"
-    "trailer\r\n"
-    "<<\r\n"
-    "/Root 1 0 R\r\n"
-    "/Info 3 0 R\r\n"
-    "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
-    "startxref\r\n"
-    "354\r\n"
-    "%%EOF\r\n";
-
-}  // namespace
-
-TEST_F(FPDFEditEmbeddertest, EmptyCreation) {
-  EXPECT_TRUE(CreateEmptyDocument());
-  FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
-  EXPECT_NE(nullptr, page);
-  // The FPDFPage_GenerateContent call should do nothing.
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-
-  EXPECT_THAT(GetString(), testing::MatchesRegex(std::string(
-                               kExpectedPDF, sizeof(kExpectedPDF))));
-  FPDF_ClosePage(page);
-}
-
-// Regression test for https://crbug.com/667012
-TEST_F(FPDFEditEmbeddertest, RasterizePDF) {
-  const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
-
-  // Get the bitmap for the original document/
-  FPDF_BITMAP orig_bitmap;
-  {
-    EXPECT_TRUE(OpenDocument("black.pdf"));
-    FPDF_PAGE orig_page = LoadPage(0);
-    EXPECT_NE(nullptr, orig_page);
-    orig_bitmap = RenderPage(orig_page);
-    CompareBitmap(orig_bitmap, 612, 792, kAllBlackMd5sum);
-    UnloadPage(orig_page);
-  }
-
-  // Create a new document from |orig_bitmap| and save it.
-  {
-    FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
-    FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
-
-    // Add the bitmap to an image object and add the image object to the output
-    // page.
-    FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
-    EXPECT_TRUE(FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap));
-    EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
-    FPDFPage_InsertObject(temp_page, temp_img);
-    EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
-    EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
-    FPDF_ClosePage(temp_page);
-    FPDF_CloseDocument(temp_doc);
-  }
-  FPDFBitmap_Destroy(orig_bitmap);
-
-  // Get the generated content. Make sure it is at least as big as the original
-  // PDF.
-  EXPECT_GT(GetString().size(), 923U);
-  VerifySavedDocument(612, 792, kAllBlackMd5sum);
-}
-
-TEST_F(FPDFEditEmbeddertest, AddPaths) {
-  // Start with a blank page
-  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
-
-  // We will first add a red rectangle
-  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
-  ASSERT_NE(nullptr, red_rect);
-  // Expect false when trying to set colors out of range
-  EXPECT_FALSE(FPDFPath_SetStrokeColor(red_rect, 100, 100, 100, 300));
-  EXPECT_FALSE(FPDFPath_SetFillColor(red_rect, 200, 256, 200, 0));
-
-  // Fill rectangle with red and insert to the page
-  EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
-  FPDFPage_InsertObject(page, red_rect);
-  FPDF_BITMAP page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "66d02eaa6181e2c069ce2ea99beda497");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Now add to that a green rectangle with some medium alpha
-  FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
-  EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 128));
-
-  // Make sure the type of the rectangle is a path.
-  EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(green_rect));
-
-  // Make sure we get back the same color we set previously.
-  unsigned int R;
-  unsigned int G;
-  unsigned int B;
-  unsigned int A;
-  EXPECT_TRUE(FPDFPath_GetFillColor(green_rect, &R, &G, &B, &A));
-  EXPECT_EQ(0U, R);
-  EXPECT_EQ(255U, G);
-  EXPECT_EQ(0U, B);
-  EXPECT_EQ(128U, A);
-
-  // Make sure the path has 5 points (1 FXPT_TYPE::MoveTo and 4
-  // FXPT_TYPE::LineTo).
-  ASSERT_EQ(5, FPDFPath_CountSegments(green_rect));
-  // Verify actual coordinates.
-  FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0);
-  float x;
-  float y;
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(100, x);
-  EXPECT_EQ(100, y);
-  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
-  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
-  segment = FPDFPath_GetPathSegment(green_rect, 1);
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(100, x);
-  EXPECT_EQ(140, y);
-  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
-  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
-  segment = FPDFPath_GetPathSegment(green_rect, 2);
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(140, x);
-  EXPECT_EQ(140, y);
-  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
-  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
-  segment = FPDFPath_GetPathSegment(green_rect, 3);
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(140, x);
-  EXPECT_EQ(100, y);
-  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
-  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
-  segment = FPDFPath_GetPathSegment(green_rect, 4);
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(100, x);
-  EXPECT_EQ(100, y);
-  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
-  EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
-
-  EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
-  FPDFPage_InsertObject(page, green_rect);
-  page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "7b0b87604594e773add528fae567a558");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Add a black triangle.
-  FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
-  EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 200));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
-  EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
-  EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
-  EXPECT_TRUE(FPDFPath_Close(black_path));
-
-  // Make sure the path has 3 points (1 FXPT_TYPE::MoveTo and 2
-  // FXPT_TYPE::LineTo).
-  ASSERT_EQ(3, FPDFPath_CountSegments(black_path));
-  // Verify actual coordinates.
-  segment = FPDFPath_GetPathSegment(black_path, 0);
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(400, x);
-  EXPECT_EQ(100, y);
-  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
-  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
-  segment = FPDFPath_GetPathSegment(black_path, 1);
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(400, x);
-  EXPECT_EQ(200, y);
-  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
-  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
-  segment = FPDFPath_GetPathSegment(black_path, 2);
-  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
-  EXPECT_EQ(300, x);
-  EXPECT_EQ(100, y);
-  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
-  EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
-  // Make sure out of bounds index access fails properly.
-  EXPECT_EQ(nullptr, FPDFPath_GetPathSegment(black_path, 3));
-
-  FPDFPage_InsertObject(page, black_path);
-  page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "eadc8020a14dfcf091da2688733d8806");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Now add a more complex blue path.
-  FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
-  EXPECT_TRUE(FPDFPath_SetFillColor(blue_path, 0, 0, 255, 255));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
-  EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
-  EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
-  EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
-  EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
-  EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
-  EXPECT_TRUE(FPDFPath_Close(blue_path));
-  FPDFPage_InsertObject(page, blue_path);
-  page_bitmap = RenderPage(page);
-  const char last_md5[] = "9823e1a21bd9b72b6a442ba4f12af946";
-  CompareBitmap(page_bitmap, 612, 792, last_md5);
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Now save the result, closing the page and document
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  // Render the saved result
-  VerifySavedDocument(612, 792, last_md5);
-}
-
-TEST_F(FPDFEditEmbeddertest, PathsPoints) {
-  CreateNewDocument();
-  FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document_);
-  // This should fail gracefully, even if img is not a path.
-  ASSERT_EQ(-1, FPDFPath_CountSegments(img));
-
-  // This should fail gracefully, even if path is NULL.
-  ASSERT_EQ(-1, FPDFPath_CountSegments(nullptr));
-
-  // FPDFPath_GetPathSegment() with a non-path.
-  ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(img, 0));
-  // FPDFPath_GetPathSegment() with a NULL path.
-  ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(nullptr, 0));
-  float x;
-  float y;
-  // FPDFPathSegment_GetPoint() with a NULL segment.
-  EXPECT_FALSE(FPDFPathSegment_GetPoint(nullptr, &x, &y));
-
-  // FPDFPathSegment_GetType() with a NULL segment.
-  ASSERT_EQ(FPDF_SEGMENT_UNKNOWN, FPDFPathSegment_GetType(nullptr));
-
-  // FPDFPathSegment_GetClose() with a NULL segment.
-  EXPECT_FALSE(FPDFPathSegment_GetClose(nullptr));
-
-  FPDFPageObj_Destroy(img);
-}
-
-TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) {
-  // Load document with some text
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-
-  // Add an opaque rectangle on top of some of the text.
-  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
-  EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
-  FPDFPage_InsertObject(page, red_rect);
-
-  // Add a transparent triangle on top of other part of the text.
-  FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
-  EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 100));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
-  EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
-  EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
-  EXPECT_TRUE(FPDFPath_Close(black_path));
-  FPDFPage_InsertObject(page, black_path);
-
-  // Render and check the result. Text is slightly different on Mac.
-  FPDF_BITMAP bitmap = RenderPage(page);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
-#else
-  const char md5[] = "aa71b09b93b55f467f1290e5111babee";
-#endif
-  CompareBitmap(bitmap, 200, 200, md5);
-  FPDFBitmap_Destroy(bitmap);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, EditOverExistingContent) {
-  // Load document with existing content
-  EXPECT_TRUE(OpenDocument("bug_717.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-
-  // Add a transparent rectangle on top of the existing content
-  FPDF_PAGEOBJECT red_rect2 = FPDFPageObj_CreateNewRect(90, 700, 25, 50);
-  EXPECT_TRUE(FPDFPath_SetFillColor(red_rect2, 255, 0, 0, 100));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect2, FPDF_FILLMODE_ALTERNATE, 0));
-  FPDFPage_InsertObject(page, red_rect2);
-
-  // Add an opaque rectangle on top of the existing content
-  FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(115, 700, 25, 50);
-  EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
-  FPDFPage_InsertObject(page, red_rect);
-
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
-  FPDFBitmap_Destroy(bitmap);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-
-  // Now save the result, closing the page and document
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  UnloadPage(page);
-
-  OpenSavedDocument();
-  page = LoadSavedPage(0);
-  VerifySavedRendering(page, 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
-
-  ClearString();
-  // Add another opaque rectangle on top of the existing content
-  FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(150, 700, 25, 50);
-  EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 255));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_ALTERNATE, 0));
-  FPDFPage_InsertObject(page, green_rect);
-
-  // Add another transparent rectangle on top of existing content
-  FPDF_PAGEOBJECT green_rect2 = FPDFPageObj_CreateNewRect(175, 700, 25, 50);
-  EXPECT_TRUE(FPDFPath_SetFillColor(green_rect2, 0, 255, 0, 100));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0));
-  FPDFPage_InsertObject(page, green_rect2);
-  FPDF_BITMAP new_bitmap = RenderPageWithFlags(page, m_SavedForm, 0);
-  const char last_md5[] = "4b5b00f824620f8c9b8801ebb98e1cdd";
-  CompareBitmap(new_bitmap, 612, 792, last_md5);
-  FPDFBitmap_Destroy(new_bitmap);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-
-  // Now save the result, closing the page and document
-  EXPECT_TRUE(FPDF_SaveAsCopy(m_SavedDocument, this, 0));
-
-  CloseSavedPage(page);
-  CloseSavedDocument();
-
-  // Render the saved result
-  VerifySavedDocument(612, 792, last_md5);
-}
-
-TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) {
-  // Start with a blank page
-  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
-
-  // Add a large stroked rectangle (fill color should not affect it).
-  FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
-  EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 255));
-  EXPECT_TRUE(FPDFPath_SetStrokeColor(rect, 0, 255, 0, 255));
-  EXPECT_TRUE(FPDFPath_SetStrokeWidth(rect, 15.0f));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
-  FPDFPage_InsertObject(page, rect);
-  FPDF_BITMAP page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "64bd31f862a89e0a9e505a5af6efd506");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Add crossed-checkmark
-  FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
-  EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
-  EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
-  EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
-  EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
-  EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 128, 128, 128, 180));
-  EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
-  FPDFPage_InsertObject(page, check);
-  page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "4b6f3b9d25c4e194821217d5016c3724");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Add stroked and filled oval-ish path.
-  FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
-  EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
-  EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
-  EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
-  EXPECT_TRUE(FPDFPath_Close(path));
-  EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 128, 128, 100));
-  EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 128, 200, 128, 150));
-  EXPECT_TRUE(FPDFPath_SetStrokeWidth(path, 10.5f));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
-  FPDFPage_InsertObject(page, path);
-  page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b");
-  FPDFBitmap_Destroy(page_bitmap);
-  FPDF_ClosePage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, AddStandardFontText) {
-  // Start with a blank page
-  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
-
-  // Add some text to the page
-  FPDF_PAGEOBJECT text_object1 =
-      FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
-  EXPECT_TRUE(text_object1);
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text1 =
-      GetFPDFWideString(L"I'm at the bottom of the page");
-  EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get()));
-  FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20);
-  FPDFPage_InsertObject(page, text_object1);
-  FPDF_BITMAP page_bitmap = RenderPage(page);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
-#else
-  const char md5[] = "eacaa24573b8ce997b3882595f096f00";
-#endif
-  CompareBitmap(page_bitmap, 612, 792, md5);
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Try another font
-  FPDF_PAGEOBJECT text_object2 =
-      FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
-  EXPECT_TRUE(text_object2);
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
-      GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold.");
-  EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
-  FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600);
-  FPDFPage_InsertObject(page, text_object2);
-  page_bitmap = RenderPage(page);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_2[] = "a5c4ace4c6f27644094813fe1441a21c";
-#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  const char md5_2[] = "2587eac9a787e97a37636d54d11bd28d";
-#else
-  const char md5_2[] = "76fcc7d08aa15445efd2e2ceb7c6cc3b";
-#endif
-  CompareBitmap(page_bitmap, 612, 792, md5_2);
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // And some randomly transformed text
-  FPDF_PAGEOBJECT text_object3 =
-      FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
-  EXPECT_TRUE(text_object3);
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text3 =
-      GetFPDFWideString(L"Can you read me? <:)>");
-  EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get()));
-  FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200);
-  FPDFPage_InsertObject(page, text_object3);
-  page_bitmap = RenderPage(page);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_3[] = "40b3ef04f915ff4c4208948001763544";
-#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  const char md5_3[] = "7cb61ec112cf400b489360d443ffc9d2";
-#else
-  const char md5_3[] = "b8a21668f1dab625af7c072e07fcefc4";
-#endif
-  CompareBitmap(page_bitmap, 612, 792, md5_3);
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // TODO(npm): Why are there issues with text rotated by 90 degrees?
-  // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
-  FPDF_ClosePage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, GraphicsData) {
-  // New page
-  std::unique_ptr<void, FPDFPageDeleter> page(
-      FPDFPage_New(CreateNewDocument(), 0, 612, 792));
-
-  // Create a rect with nontrivial graphics
-  FPDF_PAGEOBJECT rect1 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
-  FPDFPageObj_SetBlendMode(rect1, "Color");
-  FPDFPage_InsertObject(page.get(), rect1);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-
-  // Check that the ExtGState was created
-  CPDF_Page* the_page = CPDFPageFromFPDFPage(page.get());
-  CPDF_Dictionary* graphics_dict =
-      the_page->m_pResources->GetDictFor("ExtGState");
-  ASSERT_TRUE(graphics_dict);
-  EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
-
-  // Add a text object causing no change to the graphics dictionary
-  FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
-  // Only alpha, the last component, matters for the graphics dictionary. And
-  // the default value is 255.
-  EXPECT_TRUE(FPDFText_SetFillColor(text1, 100, 100, 100, 255));
-  FPDFPage_InsertObject(page.get(), text1);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
-
-  // Add a text object increasing the size of the graphics dictionary
-  FPDF_PAGEOBJECT text2 =
-      FPDFPageObj_NewTextObj(document(), "Times-Roman", 12.0f);
-  FPDFPage_InsertObject(page.get(), text2);
-  FPDFPageObj_SetBlendMode(text2, "Darken");
-  EXPECT_TRUE(FPDFText_SetFillColor(text2, 0, 0, 255, 150));
-  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
-
-  // Add a path that should reuse graphics
-  FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
-  FPDFPageObj_SetBlendMode(path, "Darken");
-  EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 200, 100, 150));
-  FPDFPage_InsertObject(page.get(), path);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
-
-  // Add a rect increasing the size of the graphics dictionary
-  FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
-  FPDFPageObj_SetBlendMode(rect2, "Darken");
-  EXPECT_TRUE(FPDFPath_SetFillColor(rect2, 0, 0, 255, 150));
-  EXPECT_TRUE(FPDFPath_SetStrokeColor(rect2, 0, 0, 0, 200));
-  FPDFPage_InsertObject(page.get(), rect2);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
-  EXPECT_EQ(4, static_cast<int>(graphics_dict->GetCount()));
-}
-
-TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
-  // Start with a blank page
-  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
-
-  // Add a red rectangle with some non-default alpha
-  FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
-  EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 128));
-  EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
-  FPDFPage_InsertObject(page, rect);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-
-  // Check the ExtGState
-  CPDF_Page* the_page = CPDFPageFromFPDFPage(page);
-  CPDF_Dictionary* graphics_dict =
-      the_page->m_pResources->GetDictFor("ExtGState");
-  ASSERT_TRUE(graphics_dict);
-  EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
-
-  // Check the bitmap
-  FPDF_BITMAP page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "5384da3406d62360ffb5cac4476fff1c");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Never mind, my new favorite color is blue, increase alpha
-  EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
-
-  // Check that bitmap displays changed content
-  page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // And now generate, without changes
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
-  page_bitmap = RenderPage(page);
-  CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Add some text to the page
-  FPDF_PAGEOBJECT text_object =
-      FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
-      GetFPDFWideString(L"Something something #text# something");
-  EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
-  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300);
-  FPDFPage_InsertObject(page, text_object);
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font");
-  ASSERT_TRUE(font_dict);
-  EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
-
-  // Generate yet again, check dicts are reasonably sized
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
-  EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
-  FPDF_ClosePage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) {
-  CreateNewDocument();
-  // TODO(npm): use other fonts after disallowing loading any font as any type
-  const CPDF_Font* stock_font =
-      CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
-  const uint8_t* data = stock_font->GetFont()->GetFontData();
-  const uint32_t size = stock_font->GetFont()->GetSize();
-  std::unique_ptr<void, FPDFFontDeleter> font(
-      FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false));
-  ASSERT_TRUE(font.get());
-  CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
-  EXPECT_TRUE(typed_font->IsType1Font());
-
-  CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont"));
-  ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
-  ASSERT_TRUE(font_dict->KeyExist("LastChar"));
-  EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
-  EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
-
-  CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
-  ASSERT_TRUE(widths_array);
-  ASSERT_EQ(224U, widths_array->GetCount());
-  EXPECT_EQ(250, widths_array->GetNumberAt(0));
-  EXPECT_EQ(569, widths_array->GetNumberAt(11));
-  EXPECT_EQ(500, widths_array->GetNumberAt(223));
-  CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data);
-}
-
-TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) {
-  CreateNewDocument();
-  const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
-  const uint8_t* data = stock_font->GetFont()->GetFontData();
-  const uint32_t size = stock_font->GetFont()->GetSize();
-  std::unique_ptr<void, FPDFFontDeleter> font(
-      FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false));
-  ASSERT_TRUE(font.get());
-  CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
-  EXPECT_TRUE(typed_font->IsTrueTypeFont());
-
-  CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont"));
-  ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
-  ASSERT_TRUE(font_dict->KeyExist("LastChar"));
-  EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
-  EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
-
-  CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
-  ASSERT_TRUE(widths_array);
-  ASSERT_EQ(224U, widths_array->GetCount());
-  EXPECT_EQ(600, widths_array->GetNumberAt(33));
-  EXPECT_EQ(600, widths_array->GetNumberAt(74));
-  EXPECT_EQ(600, widths_array->GetNumberAt(223));
-  CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data);
-}
-
-TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) {
-  CreateNewDocument();
-  const CPDF_Font* stock_font =
-      CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
-  const uint8_t* data = stock_font->GetFont()->GetFontData();
-  const uint32_t size = stock_font->GetFont()->GetSize();
-  std::unique_ptr<void, FPDFFontDeleter> font(
-      FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1));
-  ASSERT_TRUE(font.get());
-  CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
-  EXPECT_TRUE(typed_font->IsCIDFont());
-
-  // Check font dictionary entries
-  CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont"));
-  EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
-  CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
-  ASSERT_TRUE(descendant_array);
-  EXPECT_EQ(1U, descendant_array->GetCount());
-
-  // Check the CIDFontDict
-  CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
-  EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
-  EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont"));
-  CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
-  ASSERT_TRUE(cidinfo_dict);
-  EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
-  EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
-  EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
-  CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data);
-
-  // Check widths
-  CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
-  ASSERT_TRUE(widths_array);
-  EXPECT_GT(widths_array->GetCount(), 1U);
-  CheckCompositeFontWidths(widths_array, typed_font);
-}
-
-TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) {
-  CreateNewDocument();
-  const CPDF_Font* stock_font =
-      CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
-  const uint8_t* data = stock_font->GetFont()->GetFontData();
-  const uint32_t size = stock_font->GetFont()->GetSize();
-
-  std::unique_ptr<void, FPDFFontDeleter> font(
-      FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1));
-  ASSERT_TRUE(font.get());
-  CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
-  EXPECT_TRUE(typed_font->IsCIDFont());
-
-  // Check font dictionary entries
-  CPDF_Dictionary* font_dict = typed_font->GetFontDict();
-  EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
-  EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont"));
-  EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
-  CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
-  ASSERT_TRUE(descendant_array);
-  EXPECT_EQ(1U, descendant_array->GetCount());
-
-  // Check the CIDFontDict
-  CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
-  EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
-  EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
-  EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont"));
-  CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
-  ASSERT_TRUE(cidinfo_dict);
-  EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
-  EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
-  EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
-  CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size,
-                      data);
-
-  // Check widths
-  CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
-  ASSERT_TRUE(widths_array);
-  CheckCompositeFontWidths(widths_array, typed_font);
-}
-
-TEST_F(FPDFEditEmbeddertest, NormalizeNegativeRotation) {
-  // Load document with a -90 degree rotation
-  EXPECT_TRUE(OpenDocument("bug_713197.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-
-  EXPECT_EQ(3, FPDFPage_GetRotation(page));
-  UnloadPage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, AddTrueTypeFontText) {
-  // Start with a blank page
-  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
-  {
-    const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
-    const uint8_t* data = stock_font->GetFont()->GetFontData();
-    const uint32_t size = stock_font->GetFont()->GetSize();
-    std::unique_ptr<void, FPDFFontDeleter> font(
-        FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 0));
-    ASSERT_TRUE(font.get());
-
-    // Add some text to the page
-    FPDF_PAGEOBJECT text_object =
-        FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
-    EXPECT_TRUE(text_object);
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
-        GetFPDFWideString(L"I am testing my loaded font, WEE.");
-    EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
-    FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
-    FPDFPage_InsertObject(page, text_object);
-    FPDF_BITMAP page_bitmap = RenderPage(page);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-    const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
-#else
-    const char md5[] = "70592859010ffbf532a2237b8118bcc4";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-    CompareBitmap(page_bitmap, 612, 792, md5);
-    FPDFBitmap_Destroy(page_bitmap);
-
-    // Add some more text, same font
-    FPDF_PAGEOBJECT text_object2 =
-        FPDFPageObj_CreateTextObj(document(), font.get(), 15.0f);
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
-        GetFPDFWideString(L"Bigger font size");
-    EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
-    FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 200, 200);
-    FPDFPage_InsertObject(page, text_object2);
-  }
-  FPDF_BITMAP page_bitmap2 = RenderPage(page);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea";
-#else
-  const char md5_2[] = "c1d10cce1761c4a998a16b2562030568";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  CompareBitmap(page_bitmap2, 612, 792, md5_2);
-  FPDFBitmap_Destroy(page_bitmap2);
-
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  VerifySavedDocument(612, 792, md5_2);
-}
-
-TEST_F(FPDFEditEmbeddertest, TransformAnnot) {
-  // Open a file with one annotation and load its first page.
-  ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
-  FPDF_PAGE page = FPDF_LoadPage(document(), 0);
-  ASSERT_TRUE(page);
-
-  // Add an underline annotation to the page without specifying its rectangle.
-  FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE);
-  ASSERT_TRUE(annot);
-
-  // FPDFPage_TransformAnnots() should run without errors when modifying
-  // annotation rectangles.
-  FPDFPage_TransformAnnots(page, 1, 2, 3, 4, 5, 6);
-
-  FPDFPage_CloseAnnot(annot);
-  UnloadPage(page);
-}
-
-// TODO(npm): Add tests using Japanese fonts in other OS.
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-TEST_F(FPDFEditEmbeddertest, AddCIDFontText) {
-  // Start with a blank page
-  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
-  CFX_Font CIDfont;
-  {
-    // First, get the data from the font
-    CIDfont.LoadSubst("IPAGothic", 1, 0, 400, 0, 932, 0);
-    EXPECT_EQ("IPAGothic", CIDfont.GetFaceName());
-    const uint8_t* data = CIDfont.GetFontData();
-    const uint32_t size = CIDfont.GetSize();
-
-    // Load the data into a FPDF_Font.
-    std::unique_ptr<void, FPDFFontDeleter> font(
-        FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1));
-    ASSERT_TRUE(font.get());
-
-    // Add some text to the page
-    FPDF_PAGEOBJECT text_object =
-        FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
-    ASSERT_TRUE(text_object);
-    std::wstring wstr = L"ABCDEFGhijklmnop.";
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
-        GetFPDFWideString(wstr);
-    EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
-    FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 200);
-    FPDFPage_InsertObject(page, text_object);
-
-    // And add some Japanese characters
-    FPDF_PAGEOBJECT text_object2 =
-        FPDFPageObj_CreateTextObj(document(), font.get(), 18.0f);
-    ASSERT_TRUE(text_object2);
-    std::wstring wstr2 =
-        L"\u3053\u3093\u306B\u3061\u306f\u4e16\u754C\u3002\u3053\u3053\u306B1"
-        L"\u756A";
-    std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
-        GetFPDFWideString(wstr2);
-    EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
-    FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 500);
-    FPDFPage_InsertObject(page, text_object2);
-  }
-
-  // Check that the text renders properly.
-  FPDF_BITMAP page_bitmap = RenderPage(page);
-  const char md5[] = "c68cd79aa72bf83a7b25271370d46b21";
-  CompareBitmap(page_bitmap, 612, 792, md5);
-  FPDFBitmap_Destroy(page_bitmap);
-
-  // Save the document, close the page.
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-
-  VerifySavedDocument(612, 792, md5);
-}
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-
-TEST_F(FPDFEditEmbeddertest, SaveAndRender) {
-  const char md5[] = "3c20472b0552c0c22b88ab1ed8c6202b";
-  {
-    EXPECT_TRUE(OpenDocument("bug_779.pdf"));
-    FPDF_PAGE page = LoadPage(0);
-    ASSERT_NE(nullptr, page);
-
-    // Now add a more complex blue path.
-    FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
-    EXPECT_TRUE(FPDFPath_SetFillColor(green_path, 0, 255, 0, 200));
-    // TODO(npm): stroking will cause the MD5s to differ.
-    EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
-    EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
-    EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
-    EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 133));
-    EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 33));
-    EXPECT_TRUE(FPDFPath_BezierTo(green_path, 38, 33, 39, 36, 40, 40));
-    EXPECT_TRUE(FPDFPath_Close(green_path));
-    FPDFPage_InsertObject(page, green_path);
-    FPDF_BITMAP page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap, 612, 792, md5);
-    FPDFBitmap_Destroy(page_bitmap);
-
-    // Now save the result, closing the page and document
-    EXPECT_TRUE(FPDFPage_GenerateContent(page));
-    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-    UnloadPage(page);
-  }
-
-  VerifySavedDocument(612, 792, md5);
-}
-
-TEST_F(FPDFEditEmbeddertest, ExtractImageBitmap) {
-  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-  ASSERT_EQ(39, FPDFPage_CountObjects(page));
-
-  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
-  EXPECT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  EXPECT_FALSE(FPDFImageObj_GetBitmap(obj));
-
-  obj = FPDFPage_GetObject(page, 33);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  FPDF_BITMAP bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 109, 88, "d65e98d968d196abf13f78aec655ffae");
-  FPDFBitmap_Destroy(bitmap);
-
-  obj = FPDFPage_GetObject(page, 34);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 103, 75, "1287711c84dbef767c435d11697661d6");
-  FPDFBitmap_Destroy(bitmap);
-
-  obj = FPDFPage_GetObject(page, 35);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 92, 68, "9c6d76cb1e37ef8514f9455d759391f3");
-  FPDFBitmap_Destroy(bitmap);
-
-  obj = FPDFPage_GetObject(page, 36);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 79, 60, "15cb6a49a2e354ed0e9f45dd34e3da1a");
-  FPDFBitmap_Destroy(bitmap);
-
-  obj = FPDFPage_GetObject(page, 37);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 126, 106, "be5a64ba7890d2657522af6524118534");
-  FPDFBitmap_Destroy(bitmap);
-
-  obj = FPDFPage_GetObject(page, 38);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  bitmap = FPDFImageObj_GetBitmap(obj);
-  EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
-  CompareBitmap(bitmap, 194, 119, "f9e24207ee1bc0db6c543d33a5f12ec5");
-  FPDFBitmap_Destroy(bitmap);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, GetImageData) {
-  EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-  ASSERT_EQ(39, FPDFPage_CountObjects(page));
-
-  // Retrieve an image object with flate-encoded data stream.
-  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-
-  // Check that the raw image data has the correct length and hash value.
-  unsigned long len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
-  std::vector<char> buf(len);
-  EXPECT_EQ(4091u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
-  EXPECT_EQ("f73802327d2e88e890f653961bcda81a",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
-
-  // Check that the decoded image data has the correct length and hash value.
-  len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(28776u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
-  EXPECT_EQ("cb3637934bb3b95a6e4ae1ea9eb9e56e",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
-
-  // Retrieve an image obejct with DCTDecode-encoded data stream.
-  obj = FPDFPage_GetObject(page, 37);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-
-  // Check that the raw image data has the correct length and hash value.
-  len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(4370u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
-  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
-
-  // Check that the decoded image data has the correct length and hash value,
-  // which should be the same as those of the raw data, since this image is
-  // encoded by a single DCTDecode filter and decoding is a noop.
-  len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  EXPECT_EQ(4370u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
-  EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
-            GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, DestroyPageObject) {
-  FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
-  ASSERT_TRUE(rect);
-
-  // There should be no memory leaks with a call to FPDFPageObj_Destroy().
-  FPDFPageObj_Destroy(rect);
-}
-
-TEST_F(FPDFEditEmbeddertest, GetImageFilters) {
-  EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-
-  // Verify that retrieving the filter of a non-image object would fail.
-  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
-  ASSERT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  ASSERT_EQ(0, FPDFImageObj_GetImageFilterCount(obj));
-  EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0));
-
-  // Verify the returned filter string for an image object with a single filter.
-  obj = FPDFPage_GetObject(page, 33);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  ASSERT_EQ(1, FPDFImageObj_GetImageFilterCount(obj));
-  unsigned long len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
-  std::vector<char> buf(len);
-  static constexpr char kFlateDecode[] = "FlateDecode";
-  EXPECT_EQ(sizeof(kFlateDecode),
-            FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
-  EXPECT_STREQ(kFlateDecode, buf.data());
-  EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0));
-
-  // Verify all the filters for an image object with a list of filters.
-  obj = FPDFPage_GetObject(page, 38);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  ASSERT_EQ(2, FPDFImageObj_GetImageFilterCount(obj));
-  len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  static constexpr char kASCIIHexDecode[] = "ASCIIHexDecode";
-  EXPECT_EQ(sizeof(kASCIIHexDecode),
-            FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
-  EXPECT_STREQ(kASCIIHexDecode, buf.data());
-
-  len = FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0);
-  buf.clear();
-  buf.resize(len);
-  static constexpr char kDCTDecode[] = "DCTDecode";
-  EXPECT_EQ(sizeof(kDCTDecode),
-            FPDFImageObj_GetImageFilter(obj, 1, buf.data(), len));
-  EXPECT_STREQ(kDCTDecode, buf.data());
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFEditEmbeddertest, GetImageMetadata) {
-  ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-
-  // Check that getting the metadata of a null object would fail.
-  FPDF_IMAGEOBJ_METADATA metadata;
-  EXPECT_FALSE(FPDFImageObj_GetImageMetadata(nullptr, page, &metadata));
-
-  // Check that receiving the metadata with a null metadata object would fail.
-  FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 35);
-  EXPECT_FALSE(FPDFImageObj_GetImageMetadata(obj, page, nullptr));
-
-  // Check that when retrieving an image object's metadata without passing in
-  // |page|, all values are correct, with the last two being default values.
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, nullptr, &metadata));
-  EXPECT_EQ(7, metadata.marked_content_id);
-  EXPECT_EQ(92u, metadata.width);
-  EXPECT_EQ(68u, metadata.height);
-  EXPECT_NEAR(96.000000, metadata.horizontal_dpi, 0.001);
-  EXPECT_NEAR(96.000000, metadata.vertical_dpi, 0.001);
-  EXPECT_EQ(0u, metadata.bits_per_pixel);
-  EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace);
-
-  // Verify the metadata of a bitmap image with indexed colorspace.
-  ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
-  EXPECT_EQ(7, metadata.marked_content_id);
-  EXPECT_EQ(92u, metadata.width);
-  EXPECT_EQ(68u, metadata.height);
-  EXPECT_NEAR(96.000000, metadata.horizontal_dpi, 0.001);
-  EXPECT_NEAR(96.000000, metadata.vertical_dpi, 0.001);
-  EXPECT_EQ(1u, metadata.bits_per_pixel);
-  EXPECT_EQ(FPDF_COLORSPACE_INDEXED, metadata.colorspace);
-
-  // Verify the metadata of an image with RGB colorspace.
-  obj = FPDFPage_GetObject(page, 37);
-  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
-  ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
-  EXPECT_EQ(9, metadata.marked_content_id);
-  EXPECT_EQ(126u, metadata.width);
-  EXPECT_EQ(106u, metadata.height);
-  EXPECT_NEAR(162.173752, metadata.horizontal_dpi, 0.001);
-  EXPECT_NEAR(162.555878, metadata.vertical_dpi, 0.001);
-  EXPECT_EQ(24u, metadata.bits_per_pixel);
-  EXPECT_EQ(FPDF_COLORSPACE_DEVICERGB, metadata.colorspace);
-
-  UnloadPage(page);
-}
diff --git a/fpdfsdk/fpdfeditimg.cpp b/fpdfsdk/fpdfeditimg.cpp
deleted file mode 100644
index 0d7ba56..0000000
--- a/fpdfsdk/fpdfeditimg.cpp
+++ /dev/null
@@ -1,304 +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 "public/fpdf_edit.h"
-
-#include "core/fpdfapi/cpdf_modulemgr.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_pageobject.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_name.h"
-#include "core/fpdfapi/render/cpdf_dibsource.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-// These checks ensure the consistency of colorspace values across core/ and
-// public/.
-static_assert(PDFCS_DEVICEGRAY == FPDF_COLORSPACE_DEVICEGRAY,
-              "PDFCS_DEVICEGRAY value mismatch");
-static_assert(PDFCS_DEVICERGB == FPDF_COLORSPACE_DEVICERGB,
-              "PDFCS_DEVICERGB value mismatch");
-static_assert(PDFCS_DEVICECMYK == FPDF_COLORSPACE_DEVICECMYK,
-              "PDFCS_DEVICECMYK value mismatch");
-static_assert(PDFCS_CALGRAY == FPDF_COLORSPACE_CALGRAY,
-              "PDFCS_CALGRAY value mismatch");
-static_assert(PDFCS_CALRGB == FPDF_COLORSPACE_CALRGB,
-              "PDFCS_CALRGB value mismatch");
-static_assert(PDFCS_LAB == FPDF_COLORSPACE_LAB, "PDFCS_LAB value mismatch");
-static_assert(PDFCS_ICCBASED == FPDF_COLORSPACE_ICCBASED,
-              "PDFCS_ICCBASED value mismatch");
-static_assert(PDFCS_SEPARATION == FPDF_COLORSPACE_SEPARATION,
-              "PDFCS_SEPARATION value mismatch");
-static_assert(PDFCS_DEVICEN == FPDF_COLORSPACE_DEVICEN,
-              "PDFCS_DEVICEN value mismatch");
-static_assert(PDFCS_INDEXED == FPDF_COLORSPACE_INDEXED,
-              "PDFCS_INDEXED value mismatch");
-static_assert(PDFCS_PATTERN == FPDF_COLORSPACE_PATTERN,
-              "PDFCS_PATTERN value mismatch");
-
-bool LoadJpegHelper(FPDF_PAGE* pages,
-                    int nCount,
-                    FPDF_PAGEOBJECT image_object,
-                    FPDF_FILEACCESS* fileAccess,
-                    bool inlineJpeg) {
-  if (!image_object || !fileAccess)
-    return false;
-
-  RetainPtr<IFX_SeekableReadStream> pFile = MakeSeekableReadStream(fileAccess);
-  CPDF_ImageObject* pImgObj = static_cast<CPDF_ImageObject*>(image_object);
-
-  if (pages) {
-    for (int index = 0; index < nCount; index++) {
-      CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
-      if (pPage)
-        pImgObj->GetImage()->ResetCache(pPage, nullptr);
-    }
-  }
-
-  if (inlineJpeg)
-    pImgObj->GetImage()->SetJpegImageInline(pFile);
-  else
-    pImgObj->GetImage()->SetJpegImage(pFile);
-  pImgObj->SetDirty(true);
-  return true;
-}
-
-}  // namespace
-
-FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
-FPDFPageObj_NewImageObj(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-
-  auto pImageObj = pdfium::MakeUnique<CPDF_ImageObject>();
-  pImageObj->SetImage(pdfium::MakeRetain<CPDF_Image>(pDoc));
-  return pImageObj.release();
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_LoadJpegFile(FPDF_PAGE* pages,
-                          int nCount,
-                          FPDF_PAGEOBJECT image_object,
-                          FPDF_FILEACCESS* fileAccess) {
-  return LoadJpegHelper(pages, nCount, image_object, fileAccess, false);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_LoadJpegFileInline(FPDF_PAGE* pages,
-                                int nCount,
-                                FPDF_PAGEOBJECT image_object,
-                                FPDF_FILEACCESS* fileAccess) {
-  return LoadJpegHelper(pages, nCount, image_object, fileAccess, true);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object,
-                       double a,
-                       double b,
-                       double c,
-                       double d,
-                       double e,
-                       double f) {
-  if (!image_object)
-    return false;
-
-  CPDF_ImageObject* pImgObj = static_cast<CPDF_ImageObject*>(image_object);
-  pImgObj->set_matrix(CFX_Matrix(static_cast<float>(a), static_cast<float>(b),
-                                 static_cast<float>(c), static_cast<float>(d),
-                                 static_cast<float>(e), static_cast<float>(f)));
-  pImgObj->CalcBoundingBox();
-  pImgObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_SetBitmap(FPDF_PAGE* pages,
-                       int nCount,
-                       FPDF_PAGEOBJECT image_object,
-                       FPDF_BITMAP bitmap) {
-  if (!image_object || !bitmap || !pages)
-    return false;
-
-  CPDF_ImageObject* pImgObj = static_cast<CPDF_ImageObject*>(image_object);
-  for (int index = 0; index < nCount; index++) {
-    CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
-    if (pPage)
-      pImgObj->GetImage()->ResetCache(pPage, nullptr);
-  }
-  RetainPtr<CFX_DIBitmap> holder(CFXBitmapFromFPDFBitmap(bitmap));
-  pImgObj->GetImage()->SetImage(holder);
-  pImgObj->CalcBoundingBox();
-  pImgObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
-FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object) {
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
-  if (!pObj || !pObj->IsImage())
-    return nullptr;
-
-  RetainPtr<CPDF_Image> pImg = pObj->AsImage()->GetImage();
-  if (!pImg)
-    return nullptr;
-
-  RetainPtr<CFX_DIBSource> pSource = pImg->LoadDIBSource();
-  if (!pSource)
-    return nullptr;
-
-  RetainPtr<CFX_DIBitmap> pBitmap;
-  // If the source image has a representation of 1 bit per pixel, then convert
-  // it to a grayscale bitmap having 1 byte per pixel, since bitmaps have no
-  // concept of bits. Otherwise, convert the source image to a bitmap directly,
-  // retaining its color representation.
-  if (pSource->GetBPP() == 1)
-    pBitmap = pSource->CloneConvert(FXDIB_8bppRgb);
-  else
-    pBitmap = pSource->Clone(nullptr);
-
-  return pBitmap.Leak();
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object,
-                                 void* buffer,
-                                 unsigned long buflen) {
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
-  if (!pObj || !pObj->IsImage())
-    return 0;
-
-  RetainPtr<CPDF_Image> pImg = pObj->AsImage()->GetImage();
-  if (!pImg)
-    return 0;
-
-  CPDF_Stream* pImgStream = pImg->GetStream();
-  if (!pImgStream)
-    return 0;
-
-  return DecodeStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen);
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFImageObj_GetImageDataRaw(FPDF_PAGEOBJECT image_object,
-                             void* buffer,
-                             unsigned long buflen) {
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
-  if (!pObj || !pObj->IsImage())
-    return 0;
-
-  RetainPtr<CPDF_Image> pImg = pObj->AsImage()->GetImage();
-  if (!pImg)
-    return 0;
-
-  CPDF_Stream* pImgStream = pImg->GetStream();
-  if (!pImgStream)
-    return 0;
-
-  uint32_t len = pImgStream->GetRawSize();
-  if (buffer && buflen >= len)
-    memcpy(buffer, pImgStream->GetRawData(), len);
-
-  return len;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFImageObj_GetImageFilterCount(FPDF_PAGEOBJECT image_object) {
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
-  if (!pObj || !pObj->IsImage())
-    return 0;
-
-  RetainPtr<CPDF_Image> pImg = pObj->AsImage()->GetImage();
-  if (!pImg)
-    return 0;
-
-  CPDF_Dictionary* pDict = pImg->GetDict();
-  CPDF_Object* pFilter = pDict ? pDict->GetDirectObjectFor("Filter") : nullptr;
-  if (!pFilter)
-    return 0;
-
-  if (pFilter->IsArray())
-    return pFilter->AsArray()->GetCount();
-  if (pFilter->IsName())
-    return 1;
-
-  return 0;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFImageObj_GetImageFilter(FPDF_PAGEOBJECT image_object,
-                            int index,
-                            void* buffer,
-                            unsigned long buflen) {
-  if (index < 0 || index >= FPDFImageObj_GetImageFilterCount(image_object))
-    return 0;
-
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
-  CPDF_Object* pFilter =
-      pObj->AsImage()->GetImage()->GetDict()->GetDirectObjectFor("Filter");
-  ByteString bsFilter;
-  if (pFilter->IsName())
-    bsFilter = pFilter->AsName()->GetString();
-  else
-    bsFilter = pFilter->AsArray()->GetStringAt(index);
-
-  unsigned long len = bsFilter.GetLength() + 1;
-  if (buffer && len <= buflen)
-    memcpy(buffer, bsFilter.c_str(), len);
-  return len;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object,
-                              FPDF_PAGE page,
-                              FPDF_IMAGEOBJ_METADATA* metadata) {
-  CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
-  if (!pObj || !pObj->IsImage() || !metadata)
-    return false;
-
-  RetainPtr<CPDF_Image> pImg = pObj->AsImage()->GetImage();
-  if (!pImg)
-    return false;
-
-  metadata->marked_content_id = pObj->m_ContentMark.GetMarkedContentID();
-
-  const int nPixelWidth = pImg->GetPixelWidth();
-  const int nPixelHeight = pImg->GetPixelHeight();
-  metadata->width = nPixelWidth;
-  metadata->height = nPixelHeight;
-
-  const float nWidth = pObj->m_Right - pObj->m_Left;
-  const float nHeight = pObj->m_Top - pObj->m_Bottom;
-  constexpr int nPointsPerInch = 72;
-  if (nWidth != 0 && nHeight != 0) {
-    metadata->horizontal_dpi = nPixelWidth / nWidth * nPointsPerInch;
-    metadata->vertical_dpi = nPixelHeight / nHeight * nPointsPerInch;
-  }
-
-  metadata->bits_per_pixel = 0;
-  metadata->colorspace = FPDF_COLORSPACE_UNKNOWN;
-
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage || !pPage->m_pDocument.Get() || !pImg->GetStream())
-    return true;
-
-  auto pSource = pdfium::MakeRetain<CPDF_DIBSource>();
-  if (!pSource->StartLoadDIBSource(pPage->m_pDocument.Get(), pImg->GetStream(),
-                                   false, nullptr,
-                                   pPage->m_pPageResources.Get())) {
-    return true;
-  }
-
-  metadata->bits_per_pixel = pSource->GetBPP();
-  if (pSource->GetColorSpace())
-    metadata->colorspace = pSource->GetColorSpace()->GetFamily();
-
-  return true;
-}
diff --git a/fpdfsdk/fpdfeditimg_unittest.cpp b/fpdfsdk/fpdfeditimg_unittest.cpp
deleted file mode 100644
index fcc081a..0000000
--- a/fpdfsdk/fpdfeditimg_unittest.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_edit.h"
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class PDFEditTest : public testing::Test {
-  void SetUp() override { CPDF_ModuleMgr::Get()->Init(); }
-
-  void TearDown() override { CPDF_ModuleMgr::Destroy(); }
-};
-
-TEST_F(PDFEditTest, InsertObjectWithInvalidPage) {
-  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
-  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDFPage_InsertObject(nullptr, nullptr);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDFPage_InsertObject(page, nullptr);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
-  FPDFPage_InsertObject(nullptr, page_image);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDF_ClosePage(page);
-  FPDF_CloseDocument(doc);
-}
-
-TEST_F(PDFEditTest, NewImageObj) {
-  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
-  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
-  FPDFPage_InsertObject(page, page_image);
-  EXPECT_EQ(1, FPDFPage_CountObjects(page));
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-
-  FPDF_ClosePage(page);
-  FPDF_CloseDocument(doc);
-}
-
-TEST_F(PDFEditTest, NewImageObjGenerateContent) {
-  FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
-  FPDF_PAGE page = FPDFPage_New(doc, 0, 100, 100);
-  EXPECT_EQ(0, FPDFPage_CountObjects(page));
-
-  constexpr int kBitmapSize = 50;
-  FPDF_BITMAP bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 0);
-  FPDFBitmap_FillRect(bitmap, 0, 0, kBitmapSize, kBitmapSize, 0x00000000);
-  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(bitmap));
-  EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(bitmap));
-
-  FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc);
-  ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, page_image, bitmap));
-  ASSERT_TRUE(
-      FPDFImageObj_SetMatrix(page_image, kBitmapSize, 0, 0, kBitmapSize, 0, 0));
-  FPDFPage_InsertObject(page, page_image);
-  EXPECT_EQ(1, FPDFPage_CountObjects(page));
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-
-  FPDFBitmap_Destroy(bitmap);
-  FPDF_ClosePage(page);
-  FPDF_CloseDocument(doc);
-}
diff --git a/fpdfsdk/fpdfeditpage.cpp b/fpdfsdk/fpdfeditpage.cpp
deleted file mode 100644
index a032bf6..0000000
--- a/fpdfsdk/fpdfeditpage.cpp
+++ /dev/null
@@ -1,369 +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 "public/fpdf_edit.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-
-#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
-#include "core/fpdfapi/page/cpdf_form.h"
-#include "core/fpdfapi/page/cpdf_formobject.h"
-#include "core/fpdfapi/page/cpdf_imageobject.h"
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/page/cpdf_pageobject.h"
-#include "core/fpdfapi/page/cpdf_pathobject.h"
-#include "core/fpdfapi/page/cpdf_shadingobject.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_annot.h"
-#include "core/fpdfdoc/cpdf_annotlist.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "public/fpdf_formfill.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#endif  // PDF_ENABLE_XFA
-
-#if _FX_OS_ == _FX_OS_ANDROID_
-#include <time.h>
-#else
-#include <ctime>
-#endif
-
-namespace {
-
-static_assert(FPDF_PAGEOBJ_TEXT == CPDF_PageObject::TEXT,
-              "FPDF_PAGEOBJ_TEXT/CPDF_PageObject::TEXT mismatch");
-static_assert(FPDF_PAGEOBJ_PATH == CPDF_PageObject::PATH,
-              "FPDF_PAGEOBJ_PATH/CPDF_PageObject::PATH mismatch");
-static_assert(FPDF_PAGEOBJ_IMAGE == CPDF_PageObject::IMAGE,
-              "FPDF_PAGEOBJ_IMAGE/CPDF_PageObject::IMAGE mismatch");
-static_assert(FPDF_PAGEOBJ_SHADING == CPDF_PageObject::SHADING,
-              "FPDF_PAGEOBJ_SHADING/CPDF_PageObject::SHADING mismatch");
-static_assert(FPDF_PAGEOBJ_FORM == CPDF_PageObject::FORM,
-              "FPDF_PAGEOBJ_FORM/CPDF_PageObject::FORM mismatch");
-
-bool IsPageObject(CPDF_Page* pPage) {
-  if (!pPage || !pPage->m_pFormDict || !pPage->m_pFormDict->KeyExist("Type"))
-    return false;
-
-  CPDF_Object* pObject = pPage->m_pFormDict->GetObjectFor("Type")->GetDirect();
-  return pObject && !pObject->GetString().Compare("Page");
-}
-
-void CalcBoundingBox(CPDF_PageObject* pPageObj) {
-  switch (pPageObj->GetType()) {
-    case CPDF_PageObject::TEXT: {
-      break;
-    }
-    case CPDF_PageObject::PATH: {
-      CPDF_PathObject* pPathObj = pPageObj->AsPath();
-      pPathObj->CalcBoundingBox();
-      break;
-    }
-    case CPDF_PageObject::IMAGE: {
-      CPDF_ImageObject* pImageObj = pPageObj->AsImage();
-      pImageObj->CalcBoundingBox();
-      break;
-    }
-    case CPDF_PageObject::SHADING: {
-      CPDF_ShadingObject* pShadingObj = pPageObj->AsShading();
-      pShadingObj->CalcBoundingBox();
-      break;
-    }
-    case CPDF_PageObject::FORM: {
-      CPDF_FormObject* pFormObj = pPageObj->AsForm();
-      pFormObj->CalcBoundingBox();
-      break;
-    }
-    default: {
-      NOTREACHED();
-      break;
-    }
-  }
-}
-
-}  // namespace
-
-FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_CreateNewDocument() {
-  auto pDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
-  pDoc->CreateNewDoc();
-
-  time_t currentTime;
-  ByteString DateStr;
-  if (FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS)) {
-    if (time(&currentTime) != -1) {
-      tm* pTM = localtime(&currentTime);
-      if (pTM) {
-        DateStr = ByteString::Format(
-            "D:%04d%02d%02d%02d%02d%02d", pTM->tm_year + 1900, pTM->tm_mon + 1,
-            pTM->tm_mday, pTM->tm_hour, pTM->tm_min, pTM->tm_sec);
-      }
-    }
-  }
-
-  CPDF_Dictionary* pInfoDict = pDoc->GetInfo();
-  if (pInfoDict) {
-    if (FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
-      pInfoDict->SetNewFor<CPDF_String>("CreationDate", DateStr, false);
-    pInfoDict->SetNewFor<CPDF_String>("Creator", L"PDFium");
-  }
-
-  // Caller takes ownership of pDoc.
-  return FPDFDocumentFromCPDFDocument(pDoc.release());
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPage_Delete(FPDF_DOCUMENT document,
-                                               int page_index) {
-  if (UnderlyingDocumentType* pDoc = UnderlyingFromFPDFDocument(document))
-    pDoc->DeletePage(page_index);
-}
-
-FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDFPage_New(FPDF_DOCUMENT document,
-                                                 int page_index,
-                                                 double width,
-                                                 double height) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-
-  page_index = pdfium::clamp(page_index, 0, pDoc->GetPageCount());
-  CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(page_index);
-  if (!pPageDict)
-    return nullptr;
-
-  CPDF_Array* pMediaBoxArray = pPageDict->SetNewFor<CPDF_Array>("MediaBox");
-  pMediaBoxArray->AddNew<CPDF_Number>(0);
-  pMediaBoxArray->AddNew<CPDF_Number>(0);
-  pMediaBoxArray->AddNew<CPDF_Number>(static_cast<float>(width));
-  pMediaBoxArray->AddNew<CPDF_Number>(static_cast<float>(height));
-  pPageDict->SetNewFor<CPDF_Number>("Rotate", 0);
-  pPageDict->SetNewFor<CPDF_Dictionary>("Resources");
-
-#ifdef PDF_ENABLE_XFA
-  auto pXFAPage = pdfium::MakeRetain<CPDFXFA_Page>(
-      static_cast<CPDFXFA_Context*>(document), page_index);
-  pXFAPage->LoadPDFPage(pPageDict);
-  return pXFAPage.Leak();  // Caller takes ownership.
-#else   // PDF_ENABLE_XFA
-  auto pPage = pdfium::MakeUnique<CPDF_Page>(pDoc, pPageDict, true);
-  pPage->ParseContent();
-  return pPage.release();  // Caller takes ownership.
-#endif  // PDF_ENABLE_XFA
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetRotation(FPDF_PAGE page) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  return IsPageObject(pPage) ? pPage->GetPageRotation() : -1;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertObject(FPDF_PAGE page,
-                                                     FPDF_PAGEOBJECT page_obj) {
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_obj);
-  if (!pPageObj)
-    return;
-
-  std::unique_ptr<CPDF_PageObject> pPageObjHolder(pPageObj);
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!IsPageObject(pPage))
-    return;
-  pPageObj->SetDirty(true);
-  pPage->GetPageObjectList()->push_back(std::move(pPageObjHolder));
-  CalcBoundingBox(pPageObj);
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObject(FPDF_PAGE page) {
-  return FPDFPage_CountObjects(page);
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!IsPageObject(pPage))
-    return -1;
-  return pdfium::CollectionSize<int>(*pPage->GetPageObjectList());
-}
-
-FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPage_GetObject(FPDF_PAGE page,
-                                                             int index) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!IsPageObject(pPage))
-    return nullptr;
-  return pPage->GetPageObjectList()->GetPageObjectByIndex(index);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_HasTransparency(FPDF_PAGE page) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  return pPage && pPage->BackgroundAlphaNeeded();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPageObj_Destroy(FPDF_PAGEOBJECT page_obj) {
-  delete CPDFPageObjectFromFPDFPageObject(page_obj);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT pageObject) {
-  if (!pageObject)
-    return false;
-
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(pageObject);
-  int blend_type = pPageObj->m_GeneralState.GetBlendType();
-  if (blend_type != FXDIB_BLEND_NORMAL)
-    return true;
-
-  CPDF_Dictionary* pSMaskDict =
-      ToDictionary(pPageObj->m_GeneralState.GetSoftMask());
-  if (pSMaskDict)
-    return true;
-
-  if (pPageObj->m_GeneralState.GetFillAlpha() != 1.0f)
-    return true;
-
-  if (pPageObj->IsPath() && pPageObj->m_GeneralState.GetStrokeAlpha() != 1.0f) {
-    return true;
-  }
-
-  if (pPageObj->IsForm()) {
-    const CPDF_Form* pForm = pPageObj->AsForm()->form();
-    if (pForm) {
-      int trans = pForm->m_iTransparency;
-      if ((trans & PDFTRANS_ISOLATED) || (trans & PDFTRANS_GROUP))
-        return true;
-    }
-  }
-
-  return false;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT pageObject) {
-  if (!pageObject)
-    return FPDF_PAGEOBJ_UNKNOWN;
-
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(pageObject);
-  return pPageObj->GetType();
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GenerateContent(FPDF_PAGE page) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!IsPageObject(pPage))
-    return false;
-
-  CPDF_PageContentGenerator CG(pPage);
-  CG.GenerateContent();
-  return true;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDFPageObj_Transform(FPDF_PAGEOBJECT page_object,
-                      double a,
-                      double b,
-                      double c,
-                      double d,
-                      double e,
-                      double f) {
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  if (!pPageObj)
-    return;
-
-  CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f);
-  pPageObj->Transform(matrix);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page_object,
-                         FPDF_BYTESTRING blend_mode) {
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  if (!pPageObj)
-    return;
-
-  pPageObj->m_GeneralState.SetBlendMode(blend_mode);
-  pPageObj->SetDirty(true);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPage_TransformAnnots(FPDF_PAGE page,
-                                                        double a,
-                                                        double b,
-                                                        double c,
-                                                        double d,
-                                                        double e,
-                                                        double f) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-  CPDF_AnnotList AnnotList(pPage);
-  for (size_t i = 0; i < AnnotList.Count(); ++i) {
-    CPDF_Annot* pAnnot = AnnotList.GetAt(i);
-    CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e,
-                      (float)f);
-    CFX_FloatRect rect = matrix.TransformRect(pAnnot->GetRect());
-
-    CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-    CPDF_Array* pRectArray = pAnnotDict->GetArrayFor("Rect");
-    if (pRectArray)
-      pRectArray->Clear();
-    else
-      pRectArray = pAnnotDict->SetNewFor<CPDF_Array>("Rect");
-
-    pRectArray->AddNew<CPDF_Number>(rect.left);
-    pRectArray->AddNew<CPDF_Number>(rect.bottom);
-    pRectArray->AddNew<CPDF_Number>(rect.right);
-    pRectArray->AddNew<CPDF_Number>(rect.top);
-
-    // TODO(unknown): Transform AP's rectangle
-  }
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetRotation(FPDF_PAGE page,
-                                                    int rotate) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!IsPageObject(pPage))
-    return;
-
-  rotate %= 4;
-  pPage->m_pFormDict->SetNewFor<CPDF_Number>("Rotate", rotate * 90);
-}
-
-FPDF_BOOL FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object,
-                                   unsigned int R,
-                                   unsigned int G,
-                                   unsigned int B,
-                                   unsigned int A) {
-  if (!page_object || R > 255 || G > 255 || B > 255 || A > 255)
-    return false;
-
-  float rgb[3] = {R / 255.f, G / 255.f, B / 255.f};
-  auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  pPageObj->m_GeneralState.SetFillAlpha(A / 255.f);
-  pPageObj->m_ColorState.SetFillColor(
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3);
-  pPageObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPageObj_GetBounds(FPDF_PAGEOBJECT pageObject,
-                      float* left,
-                      float* bottom,
-                      float* right,
-                      float* top) {
-  if (!pageObject)
-    return false;
-
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(pageObject);
-  CFX_FloatRect bbox = pPageObj->GetRect();
-  *left = bbox.left;
-  *bottom = bbox.bottom;
-  *right = bbox.right;
-  *top = bbox.top;
-  return true;
-}
diff --git a/fpdfsdk/fpdfeditpath.cpp b/fpdfsdk/fpdfeditpath.cpp
deleted file mode 100644
index a291987..0000000
--- a/fpdfsdk/fpdfeditpath.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <vector>
-
-#include "public/fpdf_edit.h"
-
-#include "core/fpdfapi/page/cpdf_path.h"
-#include "core/fpdfapi/page/cpdf_pathobject.h"
-#include "core/fxcrt/fx_system.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "third_party/base/ptr_util.h"
-
-// These checks are here because core/ and public/ cannot depend on each other.
-static_assert(CFX_GraphStateData::LineCapButt == FPDF_LINECAP_BUTT,
-              "CFX_GraphStateData::LineCapButt value mismatch");
-static_assert(CFX_GraphStateData::LineCapRound == FPDF_LINECAP_ROUND,
-              "CFX_GraphStateData::LineCapRound value mismatch");
-static_assert(CFX_GraphStateData::LineCapSquare ==
-                  FPDF_LINECAP_PROJECTING_SQUARE,
-              "CFX_GraphStateData::LineCapSquare value mismatch");
-
-static_assert(CFX_GraphStateData::LineJoinMiter == FPDF_LINEJOIN_MITER,
-              "CFX_GraphStateData::LineJoinMiter value mismatch");
-static_assert(CFX_GraphStateData::LineJoinRound == FPDF_LINEJOIN_ROUND,
-              "CFX_GraphStateData::LineJoinRound value mismatch");
-static_assert(CFX_GraphStateData::LineJoinBevel == FPDF_LINEJOIN_BEVEL,
-              "CFX_GraphStateData::LineJoinBevel value mismatch");
-
-static_assert(static_cast<int>(FXPT_TYPE::LineTo) == FPDF_SEGMENT_LINETO,
-              "FXPT_TYPE::LineTo value mismatch");
-static_assert(static_cast<int>(FXPT_TYPE::BezierTo) == FPDF_SEGMENT_BEZIERTO,
-              "FXPT_TYPE::BezierTo value mismatch");
-static_assert(static_cast<int>(FXPT_TYPE::MoveTo) == FPDF_SEGMENT_MOVETO,
-              "FXPT_TYPE::MoveTo value mismatch");
-
-FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewPath(float x,
-                                                                    float y) {
-  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->m_Path.AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false);
-  pPathObj->DefaultStates();
-  return pPathObj.release();  // Caller takes ownership.
-}
-
-FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewRect(float x,
-                                                                    float y,
-                                                                    float w,
-                                                                    float h) {
-  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
-  pPathObj->m_Path.AppendRect(x, y, x + w, y + h);
-  pPathObj->DefaultStates();
-  return pPathObj.release();  // Caller takes ownership.
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPath_SetStrokeColor(FPDF_PAGEOBJECT path,
-                        unsigned int R,
-                        unsigned int G,
-                        unsigned int B,
-                        unsigned int A) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj || R > 255 || G > 255 || B > 255 || A > 255)
-    return false;
-
-  float rgb[3] = {R / 255.f, G / 255.f, B / 255.f};
-  pPathObj->m_GeneralState.SetStrokeAlpha(A / 255.f);
-  pPathObj->m_ColorState.SetStrokeColor(
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3);
-  pPathObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPath_GetStrokeColor(FPDF_PAGEOBJECT path,
-                        unsigned int* R,
-                        unsigned int* G,
-                        unsigned int* B,
-                        unsigned int* A) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj || !R || !G || !B || !A)
-    return false;
-
-  uint32_t strokeRGB = pPathObj->m_ColorState.GetStrokeRGB();
-  *R = FXSYS_GetRValue(strokeRGB);
-  *G = FXSYS_GetGValue(strokeRGB);
-  *B = FXSYS_GetBValue(strokeRGB);
-  *A = static_cast<unsigned int>(
-      (pPathObj->m_GeneralState.GetStrokeAlpha() * 255.f) + 0.5f);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPath_SetStrokeWidth(FPDF_PAGEOBJECT path, float width) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj || width < 0.0f)
-    return false;
-
-  pPathObj->m_GraphState.SetLineWidth(width);
-  pPathObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetFillColor(FPDF_PAGEOBJECT path,
-                                                          unsigned int R,
-                                                          unsigned int G,
-                                                          unsigned int B,
-                                                          unsigned int A) {
-  return FPDFPageObj_SetFillColor(path, R, G, B, A);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetFillColor(FPDF_PAGEOBJECT path,
-                                                          unsigned int* R,
-                                                          unsigned int* G,
-                                                          unsigned int* B,
-                                                          unsigned int* A) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj || !R || !G || !B || !A)
-    return false;
-
-  uint32_t fillRGB = pPathObj->m_ColorState.GetFillRGB();
-  *R = FXSYS_GetRValue(fillRGB);
-  *G = FXSYS_GetGValue(fillRGB);
-  *B = FXSYS_GetBValue(fillRGB);
-  *A = static_cast<unsigned int>(
-      (pPathObj->m_GeneralState.GetFillAlpha() * 255.f) + 0.5f);
-  return true;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFPath_CountSegments(FPDF_PAGEOBJECT path) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return -1;
-  return pdfium::CollectionSize<int>(pPathObj->m_Path.GetPoints());
-}
-
-FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
-FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return nullptr;
-
-  const std::vector<FX_PATHPOINT>& points = pPathObj->m_Path.GetPoints();
-  return pdfium::IndexInBounds(points, index) ? &points[index] : nullptr;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_MoveTo(FPDF_PAGEOBJECT path,
-                                                    float x,
-                                                    float y) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return false;
-
-  pPathObj->m_Path.AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false);
-  pPathObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_LineTo(FPDF_PAGEOBJECT path,
-                                                    float x,
-                                                    float y) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return false;
-
-  pPathObj->m_Path.AppendPoint(CFX_PointF(x, y), FXPT_TYPE::LineTo, false);
-  pPathObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_BezierTo(FPDF_PAGEOBJECT path,
-                                                      float x1,
-                                                      float y1,
-                                                      float x2,
-                                                      float y2,
-                                                      float x3,
-                                                      float y3) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return false;
-
-  pPathObj->m_Path.AppendPoint(CFX_PointF(x1, y1), FXPT_TYPE::BezierTo, false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(x2, y2), FXPT_TYPE::BezierTo, false);
-  pPathObj->m_Path.AppendPoint(CFX_PointF(x3, y3), FXPT_TYPE::BezierTo, false);
-  pPathObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_Close(FPDF_PAGEOBJECT path) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return false;
-
-  if (pPathObj->m_Path.GetPoints().empty())
-    return false;
-
-  pPathObj->m_Path.ClosePath();
-  pPathObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetDrawMode(FPDF_PAGEOBJECT path,
-                                                         int fillmode,
-                                                         FPDF_BOOL stroke) {
-  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
-  if (!pPathObj)
-    return false;
-
-  if (fillmode == FPDF_FILLMODE_ALTERNATE)
-    pPathObj->m_FillType = FXFILL_ALTERNATE;
-  else if (fillmode == FPDF_FILLMODE_WINDING)
-    pPathObj->m_FillType = FXFILL_WINDING;
-  else
-    pPathObj->m_FillType = 0;
-  pPathObj->m_bStroke = stroke != 0;
-  pPathObj->SetDirty(true);
-  return true;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPath_SetLineJoin(FPDF_PAGEOBJECT path,
-                                                    int line_join) {
-  if (!path)
-    return;
-  if (line_join <
-          static_cast<int>(CFX_GraphStateData::LineJoin::LineJoinMiter) ||
-      line_join >
-          static_cast<int>(CFX_GraphStateData::LineJoin::LineJoinBevel)) {
-    return;
-  }
-  auto* pPathObj = CPDFPageObjectFromFPDFPageObject(path);
-  CFX_GraphStateData::LineJoin lineJoin =
-      static_cast<CFX_GraphStateData::LineJoin>(line_join);
-  pPathObj->m_GraphState.SetLineJoin(lineJoin);
-  pPathObj->SetDirty(true);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFPath_SetLineCap(FPDF_PAGEOBJECT path,
-                                                   int line_cap) {
-  if (!path)
-    return;
-  if (line_cap < static_cast<int>(CFX_GraphStateData::LineCap::LineCapButt) ||
-      line_cap > static_cast<int>(CFX_GraphStateData::LineCap::LineCapSquare)) {
-    return;
-  }
-  auto* pPathObj = CPDFPageObjectFromFPDFPageObject(path);
-  CFX_GraphStateData::LineCap lineCap =
-      static_cast<CFX_GraphStateData::LineCap>(line_cap);
-  pPathObj->m_GraphState.SetLineCap(lineCap);
-  pPathObj->SetDirty(true);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y) {
-  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
-  if (!pPathPoint || !x || !y)
-    return false;
-
-  *x = pPathPoint->m_Point.x;
-  *y = pPathPoint->m_Point.y;
-
-  return true;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFPathSegment_GetType(FPDF_PATHSEGMENT segment) {
-  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
-
-  return pPathPoint ? static_cast<int>(pPathPoint->m_Type)
-                    : FPDF_SEGMENT_UNKNOWN;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPathSegment_GetClose(FPDF_PATHSEGMENT segment) {
-  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
-
-  return pPathPoint ? pPathPoint->m_CloseFigure : false;
-}
diff --git a/fpdfsdk/fpdfeditpath_embeddertest.cpp b/fpdfsdk/fpdfeditpath_embeddertest.cpp
deleted file mode 100644
index 59e5dbb..0000000
--- a/fpdfsdk/fpdfeditpath_embeddertest.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/fxcrt/fx_system.h"
-#include "public/fpdf_edit.h"
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-class FPDFEditPathEmbedderTest : public EmbedderTest {};
-
-TEST_F(FPDFEditPathEmbedderTest, VerifyCorrectColoursReturned) {
-  CreateEmptyDocument();
-  FPDF_PAGE page = FPDFPage_New(document(), 0, 612, 792);
-
-  for (size_t i = 0; i < 256; ++i) {
-    FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
-    EXPECT_TRUE(FPDFPath_SetFillColor(path, i, i, i, i));
-    EXPECT_TRUE(FPDFPath_SetStrokeColor(path, i, i, i, i));
-    EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 0));
-    EXPECT_TRUE(FPDFPath_LineTo(path, 400, 200));
-    EXPECT_TRUE(FPDFPath_LineTo(path, 300, 100));
-    EXPECT_TRUE(FPDFPath_Close(path));
-
-    FPDFPage_InsertObject(page, path);
-  }
-
-  EXPECT_TRUE(FPDFPage_GenerateContent(page));
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  FPDF_ClosePage(page);
-  page = nullptr;
-
-  OpenSavedDocument();
-  page = LoadSavedPage(0);
-  ASSERT(page);
-
-  for (size_t i = 0; i < 256; ++i) {
-    FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, i);
-    ASSERT(path);
-
-    EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
-
-    unsigned int r;
-    unsigned int g;
-    unsigned int b;
-    unsigned int a;
-    FPDFPath_GetFillColor(path, &r, &g, &b, &a);
-    EXPECT_EQ(i, r);
-    EXPECT_EQ(i, g);
-    EXPECT_EQ(i, b);
-    EXPECT_EQ(i, a);
-
-    FPDFPath_GetStrokeColor(path, &r, &g, &b, &a);
-    EXPECT_EQ(i, r);
-    EXPECT_EQ(i, g);
-    EXPECT_EQ(i, b);
-    EXPECT_EQ(i, a);
-  }
-
-  CloseSavedPage(page);
-  CloseSavedDocument();
-}
diff --git a/fpdfsdk/fpdfedittext.cpp b/fpdfsdk/fpdfedittext.cpp
deleted file mode 100644
index 22c6266..0000000
--- a/fpdfsdk/fpdfedittext.cpp
+++ /dev/null
@@ -1,499 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <map>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/font/cpdf_type1font.h"
-#include "core/fpdfapi/page/cpdf_docpagedata.h"
-#include "core/fpdfapi/page/cpdf_textobject.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_name.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/fx_font.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "public/fpdf_edit.h"
-
-namespace {
-
-CPDF_Dictionary* LoadFontDesc(CPDF_Document* pDoc,
-                              const ByteString& font_name,
-                              CFX_Font* pFont,
-                              const uint8_t* data,
-                              uint32_t size,
-                              int font_type) {
-  CPDF_Dictionary* fontDesc = pDoc->NewIndirect<CPDF_Dictionary>();
-  fontDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
-  fontDesc->SetNewFor<CPDF_Name>("FontName", font_name);
-  int flags = 0;
-  if (FXFT_Is_Face_fixedwidth(pFont->GetFace()))
-    flags |= FXFONT_FIXED_PITCH;
-  if (font_name.Contains("Serif"))
-    flags |= FXFONT_SERIF;
-  if (FXFT_Is_Face_Italic(pFont->GetFace()))
-    flags |= FXFONT_ITALIC;
-  if (FXFT_Is_Face_Bold(pFont->GetFace()))
-    flags |= FXFONT_BOLD;
-
-  // TODO(npm): How do I know if a  font is symbolic, script, allcap, smallcap
-  flags |= FXFONT_NONSYMBOLIC;
-
-  fontDesc->SetNewFor<CPDF_Number>("Flags", flags);
-  FX_RECT bbox;
-  pFont->GetBBox(bbox);
-  auto pBBox = pdfium::MakeUnique<CPDF_Array>();
-  pBBox->AddNew<CPDF_Number>(bbox.left);
-  pBBox->AddNew<CPDF_Number>(bbox.top);
-  pBBox->AddNew<CPDF_Number>(bbox.right);
-  pBBox->AddNew<CPDF_Number>(bbox.bottom);
-  fontDesc->SetFor("FontBBox", std::move(pBBox));
-
-  // TODO(npm): calculate italic angle correctly
-  fontDesc->SetNewFor<CPDF_Number>("ItalicAngle", pFont->IsItalic() ? -12 : 0);
-
-  fontDesc->SetNewFor<CPDF_Number>("Ascent", pFont->GetAscent());
-  fontDesc->SetNewFor<CPDF_Number>("Descent", pFont->GetDescent());
-
-  // TODO(npm): calculate the capheight, stemV correctly
-  fontDesc->SetNewFor<CPDF_Number>("CapHeight", pFont->GetAscent());
-  fontDesc->SetNewFor<CPDF_Number>("StemV", pFont->IsBold() ? 120 : 70);
-
-  CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>();
-  pStream->SetData(data, size);
-  // TODO(npm): Lengths for Type1 fonts.
-  if (font_type == FPDF_FONT_TRUETYPE) {
-    pStream->GetDict()->SetNewFor<CPDF_Number>("Length1",
-                                               static_cast<int>(size));
-  }
-  ByteString fontFile = font_type == FPDF_FONT_TYPE1 ? "FontFile" : "FontFile2";
-  fontDesc->SetNewFor<CPDF_Reference>(fontFile, pDoc, pStream->GetObjNum());
-  return fontDesc;
-}
-
-const char ToUnicodeStart[] =
-    "/CIDInit /ProcSet findresource begin\n"
-    "12 dict begin\n"
-    "begincmap\n"
-    "/CIDSystemInfo\n"
-    "<</Registry (Adobe)\n"
-    "/Ordering (Identity)\n"
-    "/Supplement 0\n"
-    ">> def\n"
-    "/CMapName /Adobe-Identity-H def\n"
-    "CMapType 2 def\n"
-    "1 begincodespacerange\n"
-    "<0000> <FFFFF>\n"
-    "endcodespacerange\n";
-
-const char ToUnicodeEnd[] =
-    "endcmap\n"
-    "CMapName currentdict /CMap defineresource pop\n"
-    "end\n"
-    "end\n";
-
-void AddCharcode(std::ostringstream* pBuffer, uint32_t number) {
-  ASSERT(number <= 0xFFFF);
-  *pBuffer << "<";
-  char ans[4];
-  FXSYS_IntToFourHexChars(number, ans);
-  for (size_t i = 0; i < 4; ++i)
-    *pBuffer << ans[i];
-  *pBuffer << ">";
-}
-
-// PDF spec 1.7 Section 5.9.2: "Unicode character sequences as expressed in
-// UTF-16BE encoding." See https://en.wikipedia.org/wiki/UTF-16#Description
-void AddUnicode(std::ostringstream* pBuffer, uint32_t unicode) {
-  if (unicode >= 0xD800 && unicode <= 0xDFFF)
-    unicode = 0;
-
-  char ans[8];
-  *pBuffer << "<";
-  size_t numChars = FXSYS_ToUTF16BE(unicode, ans);
-  for (size_t i = 0; i < numChars; ++i)
-    *pBuffer << ans[i];
-  *pBuffer << ">";
-}
-
-// Loads the charcode to unicode mapping into a stream
-CPDF_Stream* LoadUnicode(CPDF_Document* pDoc,
-                         const std::map<uint32_t, uint32_t>& to_unicode) {
-  // A map charcode->unicode
-  std::map<uint32_t, uint32_t> char_to_uni;
-  // A map <char_start, char_end> to vector v of unicode characters of size (end
-  // - start + 1). This abbreviates: start->v[0], start+1->v[1], etc. PDF spec
-  // 1.7 Section 5.9.2 says that only the last byte of the unicode may change.
-  std::map<std::pair<uint32_t, uint32_t>, std::vector<uint32_t>>
-      map_range_vector;
-  // A map <start, end> -> unicode
-  // This abbreviates: start->unicode, start+1->unicode+1, etc.
-  // PDF spec 1.7 Section 5.9.2 says that only the last byte of the unicode may
-  // change.
-  std::map<std::pair<uint32_t, uint32_t>, uint32_t> map_range;
-
-  // Calculate the maps
-  for (auto iter = to_unicode.begin(); iter != to_unicode.end(); ++iter) {
-    uint32_t firstCharcode = iter->first;
-    uint32_t firstUnicode = iter->second;
-    if (std::next(iter) == to_unicode.end() ||
-        firstCharcode + 1 != std::next(iter)->first) {
-      char_to_uni[firstCharcode] = firstUnicode;
-      continue;
-    }
-    ++iter;
-    uint32_t curCharcode = iter->first;
-    uint32_t curUnicode = iter->second;
-    if (curCharcode % 256 == 0) {
-      char_to_uni[firstCharcode] = firstUnicode;
-      char_to_uni[curCharcode] = curUnicode;
-      continue;
-    }
-    const size_t maxExtra = 255 - (curCharcode % 256);
-    auto next_it = std::next(iter);
-    if (firstUnicode + 1 != curUnicode) {
-      // Consecutive charcodes mapping to non-consecutive unicodes
-      std::vector<uint32_t> unicodes;
-      unicodes.push_back(firstUnicode);
-      unicodes.push_back(curUnicode);
-      for (size_t i = 0; i < maxExtra; ++i) {
-        if (next_it == to_unicode.end() || curCharcode + 1 != next_it->first)
-          break;
-        ++iter;
-        ++curCharcode;
-        unicodes.push_back(iter->second);
-        next_it = std::next(iter);
-      }
-      ASSERT(iter->first - firstCharcode + 1 == unicodes.size());
-      map_range_vector[std::make_pair(firstCharcode, iter->first)] = unicodes;
-      continue;
-    }
-    // Consecutive charcodes mapping to consecutive unicodes
-    for (size_t i = 0; i < maxExtra; ++i) {
-      if (next_it == to_unicode.end() || curCharcode + 1 != next_it->first ||
-          curUnicode + 1 != next_it->second) {
-        break;
-      }
-      ++iter;
-      ++curCharcode;
-      ++curUnicode;
-      next_it = std::next(iter);
-    }
-    map_range[std::make_pair(firstCharcode, curCharcode)] = firstUnicode;
-  }
-  std::ostringstream buffer;
-  buffer << ToUnicodeStart;
-  // Add maps to buffer
-  buffer << static_cast<uint32_t>(char_to_uni.size()) << " beginbfchar\n";
-  for (const auto& iter : char_to_uni) {
-    AddCharcode(&buffer, iter.first);
-    buffer << " ";
-    AddUnicode(&buffer, iter.second);
-    buffer << "\n";
-  }
-  buffer << "endbfchar\n"
-         << static_cast<uint32_t>(map_range_vector.size() + map_range.size())
-         << " beginbfrange\n";
-  for (const auto& iter : map_range_vector) {
-    const std::pair<uint32_t, uint32_t>& charcodeRange = iter.first;
-    AddCharcode(&buffer, charcodeRange.first);
-    buffer << " ";
-    AddCharcode(&buffer, charcodeRange.second);
-    buffer << " [";
-    const std::vector<uint32_t>& unicodes = iter.second;
-    for (size_t i = 0; i < unicodes.size(); ++i) {
-      uint32_t uni = unicodes[i];
-      AddUnicode(&buffer, uni);
-      if (i != unicodes.size() - 1)
-        buffer << " ";
-    }
-    buffer << "]\n";
-  }
-  for (const auto& iter : map_range) {
-    const std::pair<uint32_t, uint32_t>& charcodeRange = iter.first;
-    AddCharcode(&buffer, charcodeRange.first);
-    buffer << " ";
-    AddCharcode(&buffer, charcodeRange.second);
-    buffer << " ";
-    AddUnicode(&buffer, iter.second);
-    buffer << "\n";
-  }
-  buffer << "endbfrange\n";
-  buffer << ToUnicodeEnd;
-  // TODO(npm): Encrypt / Compress?
-  CPDF_Stream* stream = pDoc->NewIndirect<CPDF_Stream>();
-  stream->SetData(&buffer);
-  return stream;
-}
-
-const uint32_t kMaxSimpleFontChar = 0xFF;
-
-void* LoadSimpleFont(CPDF_Document* pDoc,
-                     std::unique_ptr<CFX_Font> pFont,
-                     const uint8_t* data,
-                     uint32_t size,
-                     int font_type) {
-  CPDF_Dictionary* fontDict = pDoc->NewIndirect<CPDF_Dictionary>();
-  fontDict->SetNewFor<CPDF_Name>("Type", "Font");
-  fontDict->SetNewFor<CPDF_Name>(
-      "Subtype", font_type == FPDF_FONT_TYPE1 ? "Type1" : "TrueType");
-  ByteString name = pFont->GetFaceName();
-  if (name.IsEmpty())
-    name = "Unnamed";
-  fontDict->SetNewFor<CPDF_Name>("BaseFont", name);
-
-  uint32_t glyphIndex;
-  uint32_t currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex);
-  if (currentChar > kMaxSimpleFontChar || glyphIndex == 0)
-    return nullptr;
-  fontDict->SetNewFor<CPDF_Number>("FirstChar", static_cast<int>(currentChar));
-  CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
-  while (true) {
-    widthsArray->AddNew<CPDF_Number>(pFont->GetGlyphWidth(glyphIndex));
-    uint32_t nextChar =
-        FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex);
-    // Simple fonts have 1-byte charcodes only.
-    if (nextChar > kMaxSimpleFontChar || glyphIndex == 0)
-      break;
-    for (uint32_t i = currentChar + 1; i < nextChar; i++)
-      widthsArray->AddNew<CPDF_Number>(0);
-    currentChar = nextChar;
-  }
-  fontDict->SetNewFor<CPDF_Number>("LastChar", static_cast<int>(currentChar));
-  fontDict->SetNewFor<CPDF_Reference>("Widths", pDoc, widthsArray->GetObjNum());
-  CPDF_Dictionary* fontDesc =
-      LoadFontDesc(pDoc, name, pFont.get(), data, size, font_type);
-
-  fontDict->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
-                                      fontDesc->GetObjNum());
-  return pDoc->LoadFont(fontDict);
-}
-
-const uint32_t kMaxUnicode = 0x10FFFF;
-
-void* LoadCompositeFont(CPDF_Document* pDoc,
-                        std::unique_ptr<CFX_Font> pFont,
-                        const uint8_t* data,
-                        uint32_t size,
-                        int font_type) {
-  CPDF_Dictionary* fontDict = pDoc->NewIndirect<CPDF_Dictionary>();
-  fontDict->SetNewFor<CPDF_Name>("Type", "Font");
-  fontDict->SetNewFor<CPDF_Name>("Subtype", "Type0");
-  // TODO(npm): Get the correct encoding, if it's not identity.
-  ByteString encoding = "Identity-H";
-  fontDict->SetNewFor<CPDF_Name>("Encoding", encoding);
-  ByteString name = pFont->GetFaceName();
-  if (name.IsEmpty())
-    name = "Unnamed";
-  fontDict->SetNewFor<CPDF_Name>(
-      "BaseFont", font_type == FPDF_FONT_TYPE1 ? name + "-" + encoding : name);
-
-  CPDF_Dictionary* pCIDFont = pDoc->NewIndirect<CPDF_Dictionary>();
-  pCIDFont->SetNewFor<CPDF_Name>("Type", "Font");
-  pCIDFont->SetNewFor<CPDF_Name>("Subtype", font_type == FPDF_FONT_TYPE1
-                                                ? "CIDFontType0"
-                                                : "CIDFontType2");
-  pCIDFont->SetNewFor<CPDF_Name>("BaseFont", name);
-
-  // TODO(npm): Maybe use FT_Get_CID_Registry_Ordering_Supplement to get the
-  // CIDSystemInfo
-  CPDF_Dictionary* pCIDSystemInfo = pDoc->NewIndirect<CPDF_Dictionary>();
-  pCIDSystemInfo->SetNewFor<CPDF_Name>("Registry", "Adobe");
-  pCIDSystemInfo->SetNewFor<CPDF_Name>("Ordering", "Identity");
-  pCIDSystemInfo->SetNewFor<CPDF_Number>("Supplement", 0);
-  pCIDFont->SetNewFor<CPDF_Reference>("CIDSystemInfo", pDoc,
-                                      pCIDSystemInfo->GetObjNum());
-
-  CPDF_Dictionary* fontDesc =
-      LoadFontDesc(pDoc, name, pFont.get(), data, size, font_type);
-  pCIDFont->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
-                                      fontDesc->GetObjNum());
-
-  uint32_t glyphIndex;
-  uint32_t currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex);
-  // If it doesn't have a single char, just fail
-  if (glyphIndex == 0 || currentChar > kMaxUnicode)
-    return nullptr;
-
-  std::map<uint32_t, uint32_t> to_unicode;
-  std::map<uint32_t, uint32_t> widths;
-  while (true) {
-    if (currentChar > kMaxUnicode)
-      break;
-
-    widths[glyphIndex] = pFont->GetGlyphWidth(glyphIndex);
-    to_unicode[glyphIndex] = currentChar;
-    currentChar =
-        FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex);
-    if (glyphIndex == 0)
-      break;
-  }
-  CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
-  for (auto it = widths.begin(); it != widths.end(); ++it) {
-    int ch = it->first;
-    int w = it->second;
-    if (std::next(it) == widths.end()) {
-      // Only one char left, use format c [w]
-      auto oneW = pdfium::MakeUnique<CPDF_Array>();
-      oneW->AddNew<CPDF_Number>(w);
-      widthsArray->AddNew<CPDF_Number>(ch);
-      widthsArray->Add(std::move(oneW));
-      break;
-    }
-    ++it;
-    int next_ch = it->first;
-    int next_w = it->second;
-    if (next_ch == ch + 1 && next_w == w) {
-      // The array can have a group c_first c_last w: all CIDs in the range from
-      // c_first to c_last will have width w
-      widthsArray->AddNew<CPDF_Number>(ch);
-      ch = next_ch;
-      while (true) {
-        auto next_it = std::next(it);
-        if (next_it == widths.end() || next_it->first != it->first + 1 ||
-            next_it->second != it->second) {
-          break;
-        }
-        ++it;
-        ch = it->first;
-      }
-      widthsArray->AddNew<CPDF_Number>(ch);
-      widthsArray->AddNew<CPDF_Number>(w);
-      continue;
-    }
-    // Otherwise we can have a group of the form c [w1 w2 ...]: c has width
-    // w1, c+1 has width w2, etc.
-    widthsArray->AddNew<CPDF_Number>(ch);
-    auto curWidthArray = pdfium::MakeUnique<CPDF_Array>();
-    curWidthArray->AddNew<CPDF_Number>(w);
-    curWidthArray->AddNew<CPDF_Number>(next_w);
-    while (true) {
-      auto next_it = std::next(it);
-      if (next_it == widths.end() || next_it->first != it->first + 1)
-        break;
-      ++it;
-      curWidthArray->AddNew<CPDF_Number>(static_cast<int>(it->second));
-    }
-    widthsArray->Add(std::move(curWidthArray));
-  }
-  pCIDFont->SetNewFor<CPDF_Reference>("W", pDoc, widthsArray->GetObjNum());
-  // TODO(npm): Support vertical writing
-
-  auto pDescendant = pdfium::MakeUnique<CPDF_Array>();
-  pDescendant->AddNew<CPDF_Reference>(pDoc, pCIDFont->GetObjNum());
-  fontDict->SetFor("DescendantFonts", std::move(pDescendant));
-  CPDF_Stream* toUnicodeStream = LoadUnicode(pDoc, to_unicode);
-  fontDict->SetNewFor<CPDF_Reference>("ToUnicode", pDoc,
-                                      toUnicodeStream->GetObjNum());
-  return pDoc->LoadFont(fontDict);
-}
-
-}  // namespace
-
-FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
-FPDFPageObj_NewTextObj(FPDF_DOCUMENT document,
-                       FPDF_BYTESTRING font,
-                       float font_size) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-
-  CPDF_Font* pFont = CPDF_Font::GetStockFont(pDoc, ByteStringView(font));
-  if (!pFont)
-    return nullptr;
-
-  auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
-  pTextObj->m_TextState.SetFont(pFont);
-  pTextObj->m_TextState.SetFontSize(font_size);
-  pTextObj->DefaultStates();
-  return pTextObj.release();  // Caller takes ownership.
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFText_SetText(FPDF_PAGEOBJECT text_object, FPDF_WIDESTRING text) {
-  auto* pTextObj = static_cast<CPDF_TextObject*>(text_object);
-  if (!pTextObj)
-    return false;
-
-  size_t len = WideString::WStringLength(text);
-  WideString encodedText = WideString::FromUTF16LE(text, len);
-  ByteString byteText;
-  for (wchar_t wc : encodedText) {
-    pTextObj->GetFont()->AppendChar(
-        &byteText, pTextObj->GetFont()->CharCodeFromUnicode(wc));
-  }
-  pTextObj->SetText(byteText);
-  return true;
-}
-
-FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFText_LoadFont(FPDF_DOCUMENT document,
-                                                      const uint8_t* data,
-                                                      uint32_t size,
-                                                      int font_type,
-                                                      FPDF_BOOL cid) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc || !data || size == 0 ||
-      (font_type != FPDF_FONT_TYPE1 && font_type != FPDF_FONT_TRUETYPE)) {
-    return nullptr;
-  }
-
-  auto pFont = pdfium::MakeUnique<CFX_Font>();
-
-  // TODO(npm): Maybe use FT_Get_X11_Font_Format to check format? Otherwise, we
-  // are allowing giving any font that can be loaded on freetype and setting it
-  // as any font type.
-  if (!pFont->LoadEmbedded(data, size))
-    return nullptr;
-
-  return cid ? LoadCompositeFont(pDoc, std::move(pFont), data, size, font_type)
-             : LoadSimpleFont(pDoc, std::move(pFont), data, size, font_type);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFText_SetFillColor(FPDF_PAGEOBJECT text_object,
-                      unsigned int R,
-                      unsigned int G,
-                      unsigned int B,
-                      unsigned int A) {
-  return FPDFPageObj_SetFillColor(text_object, R, G, B, A);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFFont_Close(FPDF_FONT font) {
-  CPDF_Font* pFont = static_cast<CPDF_Font*>(font);
-  if (!pFont)
-    return;
-
-  CPDF_Document* pDoc = pFont->GetDocument();
-  if (!pDoc)
-    return;
-
-  CPDF_DocPageData* pPageData = pDoc->GetPageData();
-  if (!pPageData->IsForceClear())
-    pPageData->ReleaseFont(pFont->GetFontDict());
-}
-
-FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
-FPDFPageObj_CreateTextObj(FPDF_DOCUMENT document,
-                          FPDF_FONT font,
-                          float font_size) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  CPDF_Font* pFont = static_cast<CPDF_Font*>(font);
-  if (!pDoc || !pFont)
-    return nullptr;
-
-  auto pTextObj = pdfium::MakeUnique<CPDF_TextObject>();
-  pTextObj->m_TextState.SetFont(pDoc->LoadFont(pFont->GetFontDict()));
-  pTextObj->m_TextState.SetFontSize(font_size);
-  pTextObj->DefaultStates();
-  return pTextObj.release();
-}
diff --git a/fpdfsdk/fpdfext_embeddertest.cpp b/fpdfsdk/fpdfext_embeddertest.cpp
deleted file mode 100644
index 7c28c29..0000000
--- a/fpdfsdk/fpdfext_embeddertest.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_ext.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class FPDFExtEmbeddertest : public EmbedderTest {};
-
-TEST_F(FPDFExtEmbeddertest, PageModeUnknown) {
-  EXPECT_EQ(PAGEMODE_UNKNOWN, FPDFDoc_GetPageMode(nullptr));
-}
-
-TEST_F(FPDFExtEmbeddertest, PageModeUseNone) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_EQ(PAGEMODE_USENONE, FPDFDoc_GetPageMode(document()));
-}
-
-TEST_F(FPDFExtEmbeddertest, PageModeUseOutlines) {
-  EXPECT_TRUE(OpenDocument("use_outlines.pdf"));
-  EXPECT_EQ(PAGEMODE_USEOUTLINES, FPDFDoc_GetPageMode(document()));
-}
diff --git a/fpdfsdk/fpdfformfill.cpp b/fpdfsdk/fpdfformfill.cpp
deleted file mode 100644
index 10aecb3..0000000
--- a/fpdfsdk/fpdfformfill.cpp
+++ /dev/null
@@ -1,833 +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 "public/fpdf_formfill.h"
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/render/cpdf_renderoptions.h"
-#include "core/fpdfdoc/cpdf_formcontrol.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "core/fpdfdoc/cpdf_occontext.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/fsdk_actionhandler.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "public/fpdfview.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "xfa/fxfa/cxfa_ffdocview.h"
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-
-static_assert(static_cast<int>(FormType::kNone) == FORMTYPE_NONE,
-              "None form types must match");
-static_assert(static_cast<int>(FormType::kAcroForm) == FORMTYPE_ACRO_FORM,
-              "AcroForm form types must match");
-static_assert(static_cast<int>(FormType::kXFAFull) == FORMTYPE_XFA_FULL,
-              "XFA full form types must match");
-static_assert(static_cast<int>(FormType::kXFAForeground) ==
-                  FORMTYPE_XFA_FOREGROUND,
-              "XFA foreground form types must match");
-#endif  // PDF_ENABLE_XFA
-
-static_assert(static_cast<int>(FormFieldType::kUnknown) ==
-                  FPDF_FORMFIELD_UNKNOWN,
-              "Unknown form field types must match");
-static_assert(static_cast<int>(FormFieldType::kPushButton) ==
-                  FPDF_FORMFIELD_PUSHBUTTON,
-              "PushButton form field types must match");
-static_assert(static_cast<int>(FormFieldType::kCheckBox) ==
-                  FPDF_FORMFIELD_CHECKBOX,
-              "CheckBox form field types must match");
-static_assert(static_cast<int>(FormFieldType::kRadioButton) ==
-                  FPDF_FORMFIELD_RADIOBUTTON,
-              "RadioButton form field types must match");
-static_assert(static_cast<int>(FormFieldType::kComboBox) ==
-                  FPDF_FORMFIELD_COMBOBOX,
-              "ComboBox form field types must match");
-static_assert(static_cast<int>(FormFieldType::kListBox) ==
-                  FPDF_FORMFIELD_LISTBOX,
-              "ListBox form field types must match");
-static_assert(static_cast<int>(FormFieldType::kTextField) ==
-                  FPDF_FORMFIELD_TEXTFIELD,
-              "TextField form field types must match");
-static_assert(static_cast<int>(FormFieldType::kSignature) ==
-                  FPDF_FORMFIELD_SIGNATURE,
-              "Signature form field types must match");
-#ifdef PDF_ENABLE_XFA
-static_assert(static_cast<int>(FormFieldType::kXFA) == FPDF_FORMFIELD_XFA,
-              "XFA form field types must match");
-static_assert(static_cast<int>(FormFieldType::kXFA_CheckBox) ==
-                  FPDF_FORMFIELD_XFA_CHECKBOX,
-              "XFA CheckBox form field types must match");
-static_assert(static_cast<int>(FormFieldType::kXFA_ComboBox) ==
-                  FPDF_FORMFIELD_XFA_COMBOBOX,
-              "XFA ComboBox form field types must match");
-static_assert(static_cast<int>(FormFieldType::kXFA_ImageField) ==
-                  FPDF_FORMFIELD_XFA_IMAGEFIELD,
-              "XFA ImageField form field types must match");
-static_assert(static_cast<int>(FormFieldType::kXFA_ListBox) ==
-                  FPDF_FORMFIELD_XFA_LISTBOX,
-              "XFA ListBox form field types must match");
-static_assert(static_cast<int>(FormFieldType::kXFA_PushButton) ==
-                  FPDF_FORMFIELD_XFA_PUSHBUTTON,
-              "XFA PushButton form field types must match");
-static_assert(static_cast<int>(FormFieldType::kXFA_Signature) ==
-                  FPDF_FORMFIELD_XFA_SIGNATURE,
-              "XFA Signature form field types must match");
-static_assert(static_cast<int>(FormFieldType::kXFA_TextField) ==
-                  FPDF_FORMFIELD_XFA_TEXTFIELD,
-              "XFA TextField form field types must match");
-#endif  // PDF_ENABLE_XFA
-static_assert(kFormFieldTypeCount == FPDF_FORMFIELD_COUNT,
-              "Number of form field types must match");
-
-namespace {
-
-CPDFSDK_FormFillEnvironment* HandleToCPDFSDKEnvironment(
-    FPDF_FORMHANDLE handle) {
-  return static_cast<CPDFSDK_FormFillEnvironment*>(handle);
-}
-
-CPDFSDK_InterForm* FormHandleToInterForm(FPDF_FORMHANDLE hHandle) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  return pFormFillEnv ? pFormFillEnv->GetInterForm() : nullptr;
-}
-
-CPDFSDK_PageView* FormHandleToPageView(FPDF_FORMHANDLE hHandle,
-                                       FPDF_PAGE page) {
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  if (!pPage)
-    return nullptr;
-
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  return pFormFillEnv ? pFormFillEnv->GetPageView(pPage, true) : nullptr;
-}
-
-#ifdef PDF_ENABLE_XFA
-std::vector<ByteString>* FromFPDFStringHandle(FPDF_STRINGHANDLE handle) {
-  return static_cast<std::vector<ByteString>*>(handle);
-}
-
-FPDF_STRINGHANDLE ToFPDFStringHandle(std::vector<ByteString>* strings) {
-  return static_cast<FPDF_STRINGHANDLE>(strings);
-}
-#endif  // PDF_ENABLE_XFA
-
-void FFLCommon(FPDF_FORMHANDLE hHandle,
-               FPDF_BITMAP bitmap,
-               FPDF_RECORDER recorder,
-               FPDF_PAGE page,
-               int start_x,
-               int start_y,
-               int size_x,
-               int size_y,
-               int rotate,
-               int flags) {
-  if (!hHandle)
-    return;
-
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-#ifdef PDF_ENABLE_XFA
-  CPDFXFA_Context* pContext = pPage->GetContext();
-  if (!pContext)
-    return;
-  CPDF_Document* pPDFDoc = pContext->GetPDFDoc();
-  if (!pPDFDoc)
-    return;
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (!pFormFillEnv)
-    return;
-#endif  // PDF_ENABLE_XFA
-
-  CFX_Matrix matrix =
-      pPage->GetDisplayMatrix(start_x, start_y, size_x, size_y, rotate);
-  FX_RECT clip(start_x, start_y, start_x + size_x, start_y + size_y);
-
-  auto pDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
-#ifdef _SKIA_SUPPORT_
-  pDevice->AttachRecorder(static_cast<SkPictureRecorder*>(recorder));
-#endif
-  RetainPtr<CFX_DIBitmap> holder(CFXBitmapFromFPDFBitmap(bitmap));
-  pDevice->Attach(holder, false, nullptr, false);
-  {
-    CFX_RenderDevice::StateRestorer restorer(pDevice.get());
-    pDevice->SetClip_Rect(clip);
-
-    CPDF_RenderOptions options;
-    uint32_t option_flags = options.GetFlags();
-    if (flags & FPDF_LCD_TEXT)
-      option_flags |= RENDER_CLEARTYPE;
-    else
-      option_flags &= ~RENDER_CLEARTYPE;
-    options.SetFlags(option_flags);
-
-    // Grayscale output
-    if (flags & FPDF_GRAYSCALE)
-      options.SetColorMode(CPDF_RenderOptions::kGray);
-
-    options.SetDrawAnnots(flags & FPDF_ANNOT);
-
-#ifdef PDF_ENABLE_XFA
-    options.SetOCContext(
-        pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::View));
-    if (CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage, true))
-      pPageView->PageView_OnDraw(pDevice.get(), &matrix, &options, clip);
-#else   // PDF_ENABLE_XFA
-    options.SetOCContext(pdfium::MakeRetain<CPDF_OCContext>(
-        pPage->m_pDocument.Get(), CPDF_OCContext::View));
-    if (CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, pPage))
-      pPageView->PageView_OnDraw(pDevice.get(), &matrix, &options);
-#endif  // PDF_ENABLE_XFA
-  }
-#ifdef _SKIA_SUPPORT_PATHS_
-  pDevice->Flush(true);
-  holder->UnPreMultiply();
-#endif
-}
-
-}  // namespace
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFPage_HasFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
-                             FPDF_PAGE page,
-                             double page_x,
-                             double page_y) {
-  if (!hHandle)
-    return -1;
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (pPage) {
-    CPDF_InterForm interform(pPage->m_pDocument.Get());
-    CPDF_FormControl* pFormCtrl = interform.GetControlAtPoint(
-        pPage,
-        CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
-        nullptr);
-    if (!pFormCtrl)
-      return -1;
-    CPDF_FormField* pFormField = pFormCtrl->GetField();
-    return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1;
-  }
-
-#ifdef PDF_ENABLE_XFA
-  CPDFXFA_Page* pXFAPage = UnderlyingFromFPDFPage(page);
-  if (!pXFAPage)
-    return -1;
-
-  CXFA_FFPageView* pPageView = pXFAPage->GetXFAPageView();
-  if (!pPageView)
-    return -1;
-
-  CXFA_FFDocView* pDocView = pPageView->GetDocView();
-  if (!pDocView)
-    return -1;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
-  if (!pWidgetHandler)
-    return -1;
-
-  std::unique_ptr<IXFA_WidgetIterator> pWidgetIterator(
-      pPageView->CreateWidgetIterator(XFA_TRAVERSEWAY_Form,
-                                      XFA_WidgetStatus_Viewable));
-  if (!pWidgetIterator)
-    return -1;
-
-  CXFA_FFWidget* pXFAAnnot;
-  while ((pXFAAnnot = pWidgetIterator->MoveToNext()) != nullptr) {
-    CFX_RectF rcBBox = pXFAAnnot->GetBBox(0);
-    CFX_FloatRect rcWidget(rcBBox.left, rcBBox.top, rcBBox.left + rcBBox.width,
-                           rcBBox.top + rcBBox.height);
-    rcWidget.Inflate(1.0f, 1.0f);
-    if (rcWidget.Contains(CFX_PointF(static_cast<float>(page_x),
-                                     static_cast<float>(page_y)))) {
-      return static_cast<int>(pXFAAnnot->GetFormFieldType());
-    }
-  }
-#endif  // PDF_ENABLE_XFA
-  return -1;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFPage_FormFieldZOrderAtPoint(FPDF_FORMHANDLE hHandle,
-                                FPDF_PAGE page,
-                                double page_x,
-                                double page_y) {
-  if (!hHandle)
-    return -1;
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return -1;
-  CPDF_InterForm interform(pPage->m_pDocument.Get());
-  int z_order = -1;
-  (void)interform.GetControlAtPoint(
-      pPage, CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
-      &z_order);
-  return z_order;
-}
-
-FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV
-FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document,
-                                FPDF_FORMFILLINFO* formInfo) {
-#ifdef PDF_ENABLE_XFA
-  const int kRequiredVersion = 2;
-#else   // PDF_ENABLE_XFA
-  const int kRequiredVersion = 1;
-#endif  // PDF_ENABLE_XFA
-  if (!formInfo || formInfo->version != kRequiredVersion)
-    return nullptr;
-
-  UnderlyingDocumentType* pDocument = UnderlyingFromFPDFDocument(document);
-  if (!pDocument)
-    return nullptr;
-
-#ifdef PDF_ENABLE_XFA
-  // If the CPDFXFA_Context has a FormFillEnvironment already then we've done
-  // this and can just return the old Env. Otherwise, we'll end up setting a new
-  // environment into the XFADocument and, that could get weird.
-  if (pDocument->GetFormFillEnv())
-    return pDocument->GetFormFillEnv();
-#endif
-
-  auto pFormFillEnv =
-      pdfium::MakeUnique<CPDFSDK_FormFillEnvironment>(pDocument, formInfo);
-
-#ifdef PDF_ENABLE_XFA
-  pDocument->SetFormFillEnv(pFormFillEnv.get());
-#endif  // PDF_ENABLE_XFA
-
-  return pFormFillEnv.release();  // Caller takes ownership.
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDFDOC_ExitFormFillEnvironment(FPDF_FORMHANDLE hHandle) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (!pFormFillEnv)
-    return;
-
-#ifdef PDF_ENABLE_XFA
-  // Reset the focused annotations and remove the SDK document from the
-  // XFA document.
-  pFormFillEnv->ClearAllFocusedAnnots();
-  // If the document was closed first, it's possible the XFA document
-  // is now a nullptr.
-  if (pFormFillEnv->GetXFAContext())
-    pFormFillEnv->GetXFAContext()->SetFormFillEnv(nullptr);
-#endif  // PDF_ENABLE_XFA
-  delete pFormFillEnv;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseMove(FPDF_FORMHANDLE hHandle,
-                                                     FPDF_PAGE page,
-                                                     int modifier,
-                                                     double page_x,
-                                                     double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnMouseMove(CFX_PointF(page_x, page_y), modifier);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle,
-                                                 FPDF_PAGE page,
-                                                 int modifier,
-                                                 double page_x,
-                                                 double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnFocus(CFX_PointF(page_x, page_y), modifier);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle,
-                                                       FPDF_PAGE page,
-                                                       int modifier,
-                                                       double page_x,
-                                                       double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnLButtonDown(CFX_PointF(page_x, page_y), modifier);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle,
-                                                     FPDF_PAGE page,
-                                                     int modifier,
-                                                     double page_x,
-                                                     double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnLButtonUp(CFX_PointF(page_x, page_y), modifier);
-}
-
-#ifdef PDF_ENABLE_XFA
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle,
-                                                       FPDF_PAGE page,
-                                                       int modifier,
-                                                       double page_x,
-                                                       double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnRButtonDown(CFX_PointF(page_x, page_y), modifier);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle,
-                                                     FPDF_PAGE page,
-                                                     int modifier,
-                                                     double page_x,
-                                                     double page_y) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnRButtonUp(CFX_PointF(page_x, page_y), modifier);
-}
-#endif  // PDF_ENABLE_XFA
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle,
-                                                   FPDF_PAGE page,
-                                                   int nKeyCode,
-                                                   int modifier) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnKeyDown(nKeyCode, modifier);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle,
-                                                 FPDF_PAGE page,
-                                                 int nKeyCode,
-                                                 int modifier) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnKeyUp(nKeyCode, modifier);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle,
-                                                FPDF_PAGE page,
-                                                int nChar,
-                                                int modifier) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return false;
-  return pPageView->OnChar(nChar, modifier);
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FORM_GetSelectedText(FPDF_FORMHANDLE hHandle,
-                     FPDF_PAGE page,
-                     void* buffer,
-                     unsigned long buflen) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return 0;
-
-  WideString wide_str_form_text = pPageView->GetSelectedText();
-  ByteString encoded_form_text = wide_str_form_text.UTF16LE_Encode();
-  unsigned long form_text_len = encoded_form_text.GetLength();
-
-  if (buffer && buflen >= form_text_len)
-    memcpy(buffer, encoded_form_text.c_str(), form_text_len);
-
-  return form_text_len;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle,
-                                                     FPDF_PAGE page,
-                                                     FPDF_WIDESTRING wsText) {
-  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
-  if (!pPageView)
-    return;
-
-  size_t len = WideString::WStringLength(wsText);
-  WideString wide_str_text = WideString::FromUTF16LE(wsText, len);
-
-  pPageView->ReplaceSelection(wide_str_text);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (!pFormFillEnv)
-    return false;
-  return pFormFillEnv->KillFocusAnnot(0);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle,
-                                            FPDF_BITMAP bitmap,
-                                            FPDF_PAGE page,
-                                            int start_x,
-                                            int start_y,
-                                            int size_x,
-                                            int size_y,
-                                            int rotate,
-                                            int flags) {
-  FFLCommon(hHandle, bitmap, nullptr, page, start_x, start_y, size_x, size_y,
-            rotate, flags);
-}
-
-#ifdef _SKIA_SUPPORT_
-FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLRecord(FPDF_FORMHANDLE hHandle,
-                                              FPDF_RECORDER recorder,
-                                              FPDF_PAGE page,
-                                              int start_x,
-                                              int start_y,
-                                              int size_x,
-                                              int size_y,
-                                              int rotate,
-                                              int flags) {
-  FFLCommon(hHandle, nullptr, recorder, page, start_x, start_y, size_x, size_y,
-            rotate, flags);
-}
-#endif
-
-#ifdef PDF_ENABLE_XFA
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Undo(FPDF_DOCUMENT document,
-                                                FPDF_WIDGET hWidget) {
-  if (!hWidget || !document)
-    return;
-
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  static_cast<CXFA_FFWidget*>(hWidget)->Undo();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Redo(FPDF_DOCUMENT document,
-                                                FPDF_WIDGET hWidget) {
-  if (!hWidget || !document)
-    return;
-
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  static_cast<CXFA_FFWidget*>(hWidget)->Redo();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_SelectAll(FPDF_DOCUMENT document,
-                                                     FPDF_WIDGET hWidget) {
-  if (!hWidget || !document)
-    return;
-
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  static_cast<CXFA_FFWidget*>(hWidget)->SelectAll();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Copy(FPDF_DOCUMENT document,
-                                                FPDF_WIDGET hWidget,
-                                                FPDF_WIDESTRING wsText,
-                                                FPDF_DWORD* size) {
-  if (!hWidget || !document)
-    return;
-
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  WideString wsCpText =
-      static_cast<CXFA_FFWidget*>(hWidget)->Copy().value_or(WideString());
-
-  ByteString bsCpText = wsCpText.UTF16LE_Encode();
-  uint32_t len = bsCpText.GetLength() / sizeof(unsigned short);
-  if (!wsText) {
-    *size = len;
-    return;
-  }
-
-  uint32_t real_size = len < *size ? len : *size;
-  if (real_size > 0) {
-    memcpy((void*)wsText,
-           bsCpText.GetBuffer(real_size * sizeof(unsigned short)),
-           real_size * sizeof(unsigned short));
-    bsCpText.ReleaseBuffer(real_size * sizeof(unsigned short));
-  }
-  *size = real_size;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Cut(FPDF_DOCUMENT document,
-                                               FPDF_WIDGET hWidget,
-                                               FPDF_WIDESTRING wsText,
-                                               FPDF_DWORD* size) {
-  if (!hWidget || !document)
-    return;
-
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  WideString wsCpText =
-      static_cast<CXFA_FFWidget*>(hWidget)->Cut().value_or(WideString());
-
-  ByteString bsCpText = wsCpText.UTF16LE_Encode();
-  uint32_t len = bsCpText.GetLength() / sizeof(unsigned short);
-  if (!wsText) {
-    *size = len;
-    return;
-  }
-
-  uint32_t real_size = len < *size ? len : *size;
-  if (real_size > 0) {
-    memcpy((void*)wsText,
-           bsCpText.GetBuffer(real_size * sizeof(unsigned short)),
-           real_size * sizeof(unsigned short));
-    bsCpText.ReleaseBuffer(real_size * sizeof(unsigned short));
-  }
-  *size = real_size;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Paste(FPDF_DOCUMENT document,
-                                                 FPDF_WIDGET hWidget,
-                                                 FPDF_WIDESTRING wsText,
-                                                 FPDF_DWORD size) {
-  if (!hWidget || !document)
-    return;
-
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  WideString wstr = WideString::FromUTF16LE(wsText, size);
-  static_cast<CXFA_FFWidget*>(hWidget)->Paste(wstr);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_Widget_ReplaceSpellCheckWord(FPDF_DOCUMENT document,
-                                  FPDF_WIDGET hWidget,
-                                  float x,
-                                  float y,
-                                  FPDF_BYTESTRING bsText) {
-  if (!hWidget || !document)
-    return;
-
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  CFX_PointF ptPopup;
-  ptPopup.x = x;
-  ptPopup.y = y;
-  ByteStringView bs(bsText);
-  static_cast<CXFA_FFWidget*>(hWidget)->ReplaceSpellCheckWord(ptPopup, bs);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_Widget_GetSpellCheckWords(FPDF_DOCUMENT document,
-                               FPDF_WIDGET hWidget,
-                               float x,
-                               float y,
-                               FPDF_STRINGHANDLE* stringHandle) {
-  if (!hWidget || !document)
-    return;
-
-  auto* pContext = static_cast<CPDFXFA_Context*>(document);
-  if (!pContext->ContainsXFAForm())
-    return;
-
-  CFX_PointF ptPopup;
-  ptPopup.x = x;
-  ptPopup.y = y;
-  auto sSuggestWords = pdfium::MakeUnique<std::vector<ByteString>>();
-  static_cast<CXFA_FFWidget*>(hWidget)->GetSuggestWords(ptPopup,
-                                                        sSuggestWords.get());
-
-  // Caller takes ownership.
-  *stringHandle = ToFPDFStringHandle(sSuggestWords.release());
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDF_StringHandleCounts(FPDF_STRINGHANDLE sHandle) {
-  std::vector<ByteString>* sSuggestWords = FromFPDFStringHandle(sHandle);
-  return sSuggestWords ? pdfium::CollectionSize<int>(*sSuggestWords) : -1;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_StringHandleGetStringByIndex(FPDF_STRINGHANDLE sHandle,
-                                  int index,
-                                  FPDF_BYTESTRING bsText,
-                                  FPDF_DWORD* size) {
-  if (!sHandle || !size)
-    return false;
-
-  int count = FPDF_StringHandleCounts(sHandle);
-  if (index < 0 || index >= count)
-    return false;
-
-  std::vector<ByteString>* sSuggestWords = FromFPDFStringHandle(sHandle);
-  uint32_t len = (*sSuggestWords)[index].GetLength();
-  if (!bsText) {
-    *size = len;
-    return true;
-  }
-
-  uint32_t real_size = len < *size ? len : *size;
-  if (real_size > 0)
-    memcpy((void*)bsText, (*sSuggestWords)[index].c_str(), real_size);
-  *size = real_size;
-  return true;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_StringHandleRelease(FPDF_STRINGHANDLE stringHandle) {
-  delete FromFPDFStringHandle(stringHandle);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_StringHandleAddString(FPDF_STRINGHANDLE stringHandle,
-                           FPDF_BYTESTRING bsText,
-                           FPDF_DWORD size) {
-  if (!stringHandle || !bsText || size == 0)
-    return false;
-
-  FromFPDFStringHandle(stringHandle)->push_back(ByteString(bsText, size));
-  return true;
-}
-#endif  // PDF_ENABLE_XFA
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_SetFormFieldHighlightColor(FPDF_FORMHANDLE hHandle,
-                                int fieldType,
-                                unsigned long color) {
-  CPDFSDK_InterForm* interForm = FormHandleToInterForm(hHandle);
-  if (!interForm)
-    return;
-
-  Optional<FormFieldType> cast_input = IntToFormFieldType(fieldType);
-  if (!cast_input)
-    return;
-
-  if (cast_input.value() == FormFieldType::kUnknown) {
-    interForm->SetAllHighlightColors(color);
-  } else {
-    interForm->SetHighlightColor(color, cast_input.value());
-  }
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_SetFormFieldHighlightAlpha(FPDF_FORMHANDLE hHandle, unsigned char alpha) {
-  if (CPDFSDK_InterForm* pInterForm = FormHandleToInterForm(hHandle))
-    pInterForm->SetHighlightAlpha(alpha);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_RemoveFormFieldHighlight(FPDF_FORMHANDLE hHandle) {
-  if (CPDFSDK_InterForm* pInterForm = FormHandleToInterForm(hHandle))
-    pInterForm->RemoveAllHighLights();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FORM_OnAfterLoadPage(FPDF_PAGE page,
-                                                    FPDF_FORMHANDLE hHandle) {
-  if (CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page))
-    pPageView->SetValid(true);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FORM_OnBeforeClosePage(FPDF_PAGE page,
-                                                      FPDF_FORMHANDLE hHandle) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (!pFormFillEnv)
-    return;
-
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-  CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage, false);
-  if (pPageView) {
-    pPageView->SetValid(false);
-    // RemovePageView() takes care of the delete for us.
-    pFormFillEnv->RemovePageView(pPage);
-  }
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FORM_DoDocumentJSAction(FPDF_FORMHANDLE hHandle) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (pFormFillEnv && pFormFillEnv->IsJSInitiated())
-    pFormFillEnv->ProcJavascriptFun();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FORM_DoDocumentOpenAction(FPDF_FORMHANDLE hHandle) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (pFormFillEnv && pFormFillEnv->IsJSInitiated())
-    pFormFillEnv->ProcOpenAction();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FORM_DoDocumentAAction(FPDF_FORMHANDLE hHandle,
-                                                      int aaType) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (!pFormFillEnv)
-    return;
-
-  CPDF_Document* pDoc = pFormFillEnv->GetPDFDocument();
-  const CPDF_Dictionary* pDict = pDoc->GetRoot();
-  if (!pDict)
-    return;
-
-  CPDF_AAction aa(pDict->GetDictFor("AA"));
-  auto type = static_cast<CPDF_AAction::AActionType>(aaType);
-  if (aa.ActionExist(type)) {
-    CPDF_Action action = aa.GetAction(type);
-    CPDFSDK_ActionHandler* pActionHandler =
-        HandleToCPDFSDKEnvironment(hHandle)->GetActionHandler();
-    pActionHandler->DoAction_Document(action, type, pFormFillEnv);
-  }
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page,
-                                                  FPDF_FORMHANDLE hHandle,
-                                                  int aaType) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      HandleToCPDFSDKEnvironment(hHandle);
-  if (!pFormFillEnv)
-    return;
-
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  CPDF_Page* pPDFPage = CPDFPageFromFPDFPage(page);
-  if (!pPDFPage)
-    return;
-
-  if (!pFormFillEnv->GetPageView(pPage, false))
-    return;
-
-  CPDFSDK_ActionHandler* pActionHandler = pFormFillEnv->GetActionHandler();
-  CPDF_Dictionary* pPageDict = pPDFPage->m_pFormDict.Get();
-  CPDF_AAction aa(pPageDict->GetDictFor("AA"));
-  CPDF_AAction::AActionType type = aaType == FPDFPAGE_AACTION_OPEN
-                                       ? CPDF_AAction::OpenPage
-                                       : CPDF_AAction::ClosePage;
-  if (aa.ActionExist(type)) {
-    CPDF_Action action = aa.GetAction(type);
-    pActionHandler->DoAction_Page(action, type, pFormFillEnv);
-  }
-}
diff --git a/fpdfsdk/fpdfformfill_embeddertest.cpp b/fpdfsdk/fpdfformfill_embeddertest.cpp
deleted file mode 100644
index ae1c02e..0000000
--- a/fpdfsdk/fpdfformfill_embeddertest.cpp
+++ /dev/null
@@ -1,1374 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "public/cpp/fpdf_deleters.h"
-#include "public/fpdf_formfill.h"
-#include "public/fpdf_fwlevent.h"
-#include "testing/embedder_test.h"
-#include "testing/embedder_test_mock_delegate.h"
-#include "testing/embedder_test_timer_handling_delegate.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-
-using FPDFFormFillEmbeddertest = EmbedderTest;
-
-// A base class for many related tests that involve clicking and typing into
-// form fields.
-class FPDFFormFillInteractiveEmbeddertest : public FPDFFormFillEmbeddertest {
- protected:
-  FPDFFormFillInteractiveEmbeddertest() = default;
-  ~FPDFFormFillInteractiveEmbeddertest() override = default;
-
-  void SetUp() override {
-    FPDFFormFillEmbeddertest::SetUp();
-    ASSERT_TRUE(OpenDocument(GetDocumentName()));
-    page_ = LoadPage(0);
-    ASSERT_TRUE(page_);
-    FormSanityChecks();
-  }
-
-  void TearDown() override {
-    UnloadPage(page_);
-    FPDFFormFillEmbeddertest::TearDown();
-  }
-
-  // Returns the name of the PDF to use.
-  virtual const char* GetDocumentName() const = 0;
-
-  // Returns the type of field(s) in the PDF.
-  virtual int GetFormType() const = 0;
-
-  // Optionally do some sanity check on the document after loading.
-  virtual void FormSanityChecks() {}
-
-  FPDF_PAGE page() { return page_; }
-
-  int GetFormTypeAtPoint(const CFX_PointF& point) {
-    return FPDFPage_HasFormFieldAtPoint(form_handle(), page_, point.x, point.y);
-  }
-
-  void ClickOnFormFieldAtPoint(const CFX_PointF& point) {
-    // Click on the text field or combobox as specified by coordinates.
-    FORM_OnMouseMove(form_handle(), page_, 0, point.x, point.y);
-    FORM_OnLButtonDown(form_handle(), page_, 0, point.x, point.y);
-    FORM_OnLButtonUp(form_handle(), page_, 0, point.x, point.y);
-  }
-
-  void TypeTextIntoTextField(int num_chars, const CFX_PointF& point) {
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(point));
-    ClickOnFormFieldAtPoint(point);
-
-    // Type text starting with 'A' to as many chars as specified by |num_chars|.
-    for (int i = 0; i < num_chars; ++i) {
-      FORM_OnChar(form_handle(), page_, 'A' + i, 0);
-    }
-  }
-
-  // Navigates to text field using the mouse and then selects text via the
-  // shift and specfied left or right arrow key.
-  void SelectTextWithKeyboard(int num_chars,
-                              int arrow_key,
-                              const CFX_PointF& point) {
-    // Navigate to starting position for selection.
-    ClickOnFormFieldAtPoint(point);
-
-    // Hold down shift (and don't release until entire text is selected).
-    FORM_OnKeyDown(form_handle(), page_, FWL_VKEY_Shift, 0);
-
-    // Select text char by char via left or right arrow key.
-    for (int i = 0; i < num_chars; ++i) {
-      FORM_OnKeyDown(form_handle(), page_, arrow_key, FWL_EVENTFLAG_ShiftKey);
-      FORM_OnKeyUp(form_handle(), page_, arrow_key, FWL_EVENTFLAG_ShiftKey);
-    }
-    FORM_OnKeyUp(form_handle(), page_, FWL_VKEY_Shift, 0);
-  }
-
-  // Uses the mouse to navigate to text field and select text.
-  void SelectTextWithMouse(const CFX_PointF& start, const CFX_PointF& end) {
-    ASSERT(start.y == end.y);
-
-    // Navigate to starting position and click mouse.
-    FORM_OnMouseMove(form_handle(), page_, 0, start.x, start.y);
-    FORM_OnLButtonDown(form_handle(), page_, 0, start.x, start.y);
-
-    // Hold down mouse until reach end of desired selection.
-    FORM_OnMouseMove(form_handle(), page_, 0, end.x, end.y);
-    FORM_OnLButtonUp(form_handle(), page_, 0, end.x, end.y);
-  }
-
-  void CheckSelection(const WideStringView& expected_string) {
-    // Calculate expected length for selected text.
-    int num_chars = expected_string.GetLength();
-
-    // Check actual selection against expected selection.
-    const unsigned long expected_length =
-        sizeof(unsigned short) * (num_chars + 1);
-    unsigned long sel_text_len =
-        FORM_GetSelectedText(form_handle(), page_, nullptr, 0);
-    ASSERT_EQ(expected_length, sel_text_len);
-
-    std::vector<unsigned short> buf(sel_text_len);
-    EXPECT_EQ(expected_length, FORM_GetSelectedText(form_handle(), page_,
-                                                    buf.data(), sel_text_len));
-
-    EXPECT_EQ(expected_string, WideString::FromUTF16LE(buf.data(), num_chars));
-  }
-
- private:
-  FPDF_PAGE page_ = nullptr;
-};
-
-class FPDFFormFillTextFormEmbeddertest
-    : public FPDFFormFillInteractiveEmbeddertest {
- protected:
-  FPDFFormFillTextFormEmbeddertest() = default;
-  ~FPDFFormFillTextFormEmbeddertest() override = default;
-
-  const char* GetDocumentName() const override {
-    // PDF with several form text fields:
-    // - "Text Box" - Regular text box with no special attributes.
-    // - "ReadOnly" - Ff: 1.
-    // - "CharLimit" - MaxLen: 10, V: Elephant.
-    return "text_form_multiple.pdf";
-  }
-
-  int GetFormType() const override { return FPDF_FORMFIELD_TEXTFIELD; }
-
-  void FormSanityChecks() override {
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(CharLimitFormBegin()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(CharLimitFormEnd()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(RegularFormBegin()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(RegularFormEnd()));
-  }
-
-  void SelectAllCharLimitFormTextWithMouse() {
-    SelectTextWithMouse(CharLimitFormEnd(), CharLimitFormBegin());
-  }
-
-  void SelectAllRegularFormTextWithMouse() {
-    SelectTextWithMouse(RegularFormEnd(), RegularFormBegin());
-  }
-
-  const CFX_PointF& CharLimitFormBegin() const {
-    static const CFX_PointF point = CharLimitFormAtX(kFormBeginX);
-    return point;
-  }
-
-  const CFX_PointF& CharLimitFormEnd() const {
-    static const CFX_PointF point = CharLimitFormAtX(kFormEndX);
-    return point;
-  }
-
-  const CFX_PointF& RegularFormBegin() const {
-    static const CFX_PointF point = RegularFormAtX(kFormBeginX);
-    return point;
-  }
-
-  const CFX_PointF& RegularFormEnd() const {
-    static const CFX_PointF point = RegularFormAtX(kFormEndX);
-    return point;
-  }
-
-  static CFX_PointF CharLimitFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
-    return CFX_PointF(x, kCharLimitFormY);
-  }
-
-  static CFX_PointF RegularFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
-    return CFX_PointF(x, kRegularFormY);
-  }
-
- private:
-  static constexpr float kFormBeginX = 102.0;
-  static constexpr float kFormEndX = 195.0;
-  static constexpr float kCharLimitFormY = 60.0;
-  static constexpr float kRegularFormY = 115.0;
-};
-
-class FPDFFormFillComboBoxFormEmbeddertest
-    : public FPDFFormFillInteractiveEmbeddertest {
- protected:
-  FPDFFormFillComboBoxFormEmbeddertest() = default;
-  ~FPDFFormFillComboBoxFormEmbeddertest() override = default;
-
-  const char* GetDocumentName() const override {
-    // PDF with form comboboxes:
-    // - "Combo_Editable" - Ff: 393216, 3 options with pair values.
-    // - "Combo1" - Ff: 131072, 3 options with single values.
-    // - "Combo_ReadOnly" - Ff: 131073, 3 options with single values.
-    return "combobox_form.pdf";
-  }
-
-  int GetFormType() const override { return FPDF_FORMFIELD_COMBOBOX; }
-
-  void FormSanityChecks() override {
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(EditableFormBegin()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(EditableFormEnd()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(EditableFormDropDown()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(NonEditableFormBegin()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(NonEditableFormEnd()));
-    EXPECT_EQ(GetFormType(), GetFormTypeAtPoint(NonEditableFormDropDown()));
-  }
-
-  void SelectEditableFormOption(int item_index) {
-    SelectOption(item_index, EditableFormDropDown());
-  }
-
-  void SelectNonEditableFormOption(int item_index) {
-    SelectOption(item_index, NonEditableFormDropDown());
-  }
-
-  void SelectAllEditableFormTextWithMouse() {
-    SelectTextWithMouse(EditableFormEnd(), EditableFormBegin());
-  }
-
-  const CFX_PointF& EditableFormBegin() const {
-    static const CFX_PointF point = EditableFormAtX(kFormBeginX);
-    return point;
-  }
-
-  const CFX_PointF& EditableFormEnd() const {
-    static const CFX_PointF point = EditableFormAtX(kFormEndX);
-    return point;
-  }
-
-  const CFX_PointF& EditableFormDropDown() const {
-    static const CFX_PointF point(kFormDropDownX, kEditableFormY);
-    return point;
-  }
-
-  const CFX_PointF& NonEditableFormBegin() const {
-    static const CFX_PointF point = NonEditableFormAtX(kFormBeginX);
-    return point;
-  }
-
-  const CFX_PointF& NonEditableFormEnd() const {
-    static const CFX_PointF point = NonEditableFormAtX(kFormEndX);
-    return point;
-  }
-
-  const CFX_PointF& NonEditableFormDropDown() const {
-    static const CFX_PointF point(kFormDropDownX, kNonEditableFormY);
-    return point;
-  }
-
-  static CFX_PointF EditableFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
-    return CFX_PointF(x, kEditableFormY);
-  }
-
-  static CFX_PointF NonEditableFormAtX(float x) {
-    ASSERT(x >= kFormBeginX);
-    ASSERT(x <= kFormEndX);
-    return CFX_PointF(x, kNonEditableFormY);
-  }
-
- private:
-  // Selects one of the pre-selected values from a combobox with three options.
-  // Options are specified by |item_index|, which is 0-based.
-  void SelectOption(int item_index, const CFX_PointF& point) {
-    // Only relevant for comboboxes with three choices and the same dimensions
-    // as those in combobox_form.pdf.
-    ASSERT(item_index >= 0);
-    ASSERT(item_index < 3);
-
-    // Navigate to button for drop down and click mouse to reveal options.
-    ClickOnFormFieldAtPoint(point);
-
-    // Calculate to Y-coordinate of dropdown option to be selected.
-    constexpr double kChoiceHeight = 15;
-    CFX_PointF option_point = point;
-    option_point.y -= kChoiceHeight * (item_index + 1);
-
-    // Navigate to option and click mouse to select it.
-    ClickOnFormFieldAtPoint(option_point);
-  }
-
-  static constexpr float kFormBeginX = 102.0;
-  static constexpr float kFormEndX = 183.0;
-  static constexpr float kFormDropDownX = 192.0;
-  static constexpr float kEditableFormY = 60.0;
-  static constexpr float kNonEditableFormY = 110.0;
-};
-
-TEST_F(FPDFFormFillEmbeddertest, FirstTest) {
-  EmbedderTestMockDelegate mock;
-  EXPECT_CALL(mock, Alert(_, _, _, _)).Times(0);
-  EXPECT_CALL(mock, UnsupportedHandler(_)).Times(0);
-  EXPECT_CALL(mock, SetTimer(_, _)).Times(0);
-  EXPECT_CALL(mock, KillTimer(_)).Times(0);
-  SetDelegate(&mock);
-
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_487928) {
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_487928.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-  DoOpenActions();
-  delegate.AdvanceTime(5000);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_507316) {
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_507316.pdf"));
-  FPDF_PAGE page = LoadPage(2);
-  EXPECT_TRUE(page);
-  DoOpenActions();
-  delegate.AdvanceTime(4000);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_514690) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  // Test that FORM_OnMouseMove() etc. permit null HANDLES and PAGES.
-  FORM_OnMouseMove(nullptr, page, 0, 10.0, 10.0);
-  FORM_OnMouseMove(form_handle(), nullptr, 0, 10.0, 10.0);
-
-  UnloadPage(page);
-}
-
-#ifdef PDF_ENABLE_V8
-TEST_F(FPDFFormFillEmbeddertest, BUG_551248) {
-  // Test that timers fire once and intervals fire repeatedly.
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_551248.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-  DoOpenActions();
-
-  const auto& alerts = delegate.GetAlerts();
-  EXPECT_EQ(0U, alerts.size());
-
-  delegate.AdvanceTime(1000);
-  EXPECT_EQ(0U, alerts.size());  // nothing fired.
-  delegate.AdvanceTime(1000);
-  EXPECT_EQ(1U, alerts.size());  // interval fired.
-  delegate.AdvanceTime(1000);
-  EXPECT_EQ(2U, alerts.size());  // timer fired.
-  delegate.AdvanceTime(1000);
-  EXPECT_EQ(3U, alerts.size());  // interval fired again.
-  delegate.AdvanceTime(1000);
-  EXPECT_EQ(3U, alerts.size());  // nothing fired.
-  delegate.AdvanceTime(1000);
-  EXPECT_EQ(4U, alerts.size());  // interval fired again.
-  delegate.AdvanceTime(1000);
-  EXPECT_EQ(4U, alerts.size());  // nothing fired.
-  UnloadPage(page);
-
-  ASSERT_EQ(4U, alerts.size());  // nothing else fired.
-
-  EXPECT_STREQ(L"interval fired", alerts[0].message.c_str());
-  EXPECT_STREQ(L"Alert", alerts[0].title.c_str());
-  EXPECT_EQ(0, alerts[0].type);
-  EXPECT_EQ(0, alerts[0].icon);
-
-  EXPECT_STREQ(L"timer fired", alerts[1].message.c_str());
-  EXPECT_STREQ(L"Alert", alerts[1].title.c_str());
-  EXPECT_EQ(0, alerts[1].type);
-  EXPECT_EQ(0, alerts[1].icon);
-
-  EXPECT_STREQ(L"interval fired", alerts[2].message.c_str());
-  EXPECT_STREQ(L"Alert", alerts[2].title.c_str());
-  EXPECT_EQ(0, alerts[2].type);
-  EXPECT_EQ(0, alerts[2].icon);
-
-  EXPECT_STREQ(L"interval fired", alerts[3].message.c_str());
-  EXPECT_STREQ(L"Alert", alerts[3].title.c_str());
-  EXPECT_EQ(0, alerts[3].type);
-  EXPECT_EQ(0, alerts[3].icon);
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_620428) {
-  // Test that timers and intervals are cancelable.
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_620428.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-  DoOpenActions();
-  delegate.AdvanceTime(5000);
-  UnloadPage(page);
-
-  const auto& alerts = delegate.GetAlerts();
-  ASSERT_EQ(1U, alerts.size());
-  EXPECT_STREQ(L"done", alerts[0].message.c_str());
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_634394) {
-  // Cancel timer inside timer callback.
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_634394.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-  DoOpenActions();
-
-  // Timers fire at most once per AdvanceTime(), allow intervals
-  // to fire several times if possible.
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  UnloadPage(page);
-
-  const auto& alerts = delegate.GetAlerts();
-  EXPECT_EQ(2U, alerts.size());
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_634716) {
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_634716.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-  DoOpenActions();
-
-  // Timers fire at most once per AdvanceTime(), allow intervals
-  // to fire several times if possible.
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  delegate.AdvanceTime(1000);
-  UnloadPage(page);
-
-  const auto& alerts = delegate.GetAlerts();
-  EXPECT_EQ(2U, alerts.size());
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_679649) {
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_679649.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  delegate.SetFailNextTimer();
-  DoOpenActions();
-  delegate.AdvanceTime(2000);
-  UnloadPage(page);
-
-  const auto& alerts = delegate.GetAlerts();
-  EXPECT_EQ(0u, alerts.size());
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_707673) {
-  EmbedderTestTimerHandlingDelegate delegate;
-  SetDelegate(&delegate);
-
-  EXPECT_TRUE(OpenDocument("bug_707673.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  DoOpenActions();
-  FORM_OnLButtonDown(form_handle(), page, 0, 140, 590);
-  FORM_OnLButtonUp(form_handle(), page, 0, 140, 590);
-  delegate.AdvanceTime(1000);
-  UnloadPage(page);
-
-  const auto& alerts = delegate.GetAlerts();
-  EXPECT_EQ(0u, alerts.size());
-}
-
-TEST_F(FPDFFormFillEmbeddertest, BUG_765384) {
-  EXPECT_TRUE(OpenDocument("bug_765384.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  DoOpenActions();
-  FORM_OnLButtonDown(form_handle(), page, 0, 140, 590);
-  FORM_OnLButtonUp(form_handle(), page, 0, 140, 590);
-  UnloadPage(page);
-}
-
-#endif  // PDF_ENABLE_V8
-
-TEST_F(FPDFFormFillEmbeddertest, FormText) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-  const char md5_1[] = "5f11dbe575fe197a37c3fb422559f8ff";
-  const char md5_2[] = "35b1a4b679eafc749a0b6fda750c0e8d";
-  const char md5_3[] = "65c64a7c355388f719a752aa1e23f6fe";
-#else
-  const char md5_1[] = "b890950d4b9bc163b1a96797f3004b53";
-  const char md5_2[] = "11487d5597599a26e8912b9c1d9422cb";
-  const char md5_3[] = "bffe0ecea9a533f217047ee41d6be466";
-#endif
-  {
-    EXPECT_TRUE(OpenDocument("text_form.pdf"));
-    FPDF_PAGE page = LoadPage(0);
-    ASSERT_TRUE(page);
-    std::unique_ptr<void, FPDFBitmapDeleter> bitmap1(RenderPage(page));
-    CompareBitmap(bitmap1.get(), 300, 300, md5_1);
-
-    // Click on the textfield
-    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
-              FPDFPage_HasFormFieldAtPoint(form_handle(), page, 120.0, 120.0));
-    FORM_OnMouseMove(form_handle(), page, 0, 120.0, 120.0);
-    FORM_OnLButtonDown(form_handle(), page, 0, 120.0, 120.0);
-    FORM_OnLButtonUp(form_handle(), page, 0, 120.0, 120.0);
-
-    // Write "ABC"
-    FORM_OnChar(form_handle(), page, 65, 0);
-    FORM_OnChar(form_handle(), page, 66, 0);
-    FORM_OnChar(form_handle(), page, 67, 0);
-    std::unique_ptr<void, FPDFBitmapDeleter> bitmap2(RenderPage(page));
-    CompareBitmap(bitmap2.get(), 300, 300, md5_2);
-
-    // Take out focus by clicking out of the textfield
-    FORM_OnMouseMove(form_handle(), page, 0, 15.0, 15.0);
-    FORM_OnLButtonDown(form_handle(), page, 0, 15.0, 15.0);
-    FORM_OnLButtonUp(form_handle(), page, 0, 15.0, 15.0);
-    std::unique_ptr<void, FPDFBitmapDeleter> bitmap3(RenderPage(page));
-    CompareBitmap(bitmap3.get(), 300, 300, md5_3);
-
-    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-
-    // Close page
-    UnloadPage(page);
-  }
-  // Check saved document
-  VerifySavedDocument(300, 300, md5_3);
-}
-
-TEST_F(FPDFFormFillEmbeddertest, HasFormInfoNone) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_EQ(FORMTYPE_NONE, FPDF_GetFormType(document_));
-}
-
-TEST_F(FPDFFormFillEmbeddertest, HasFormInfoAcroForm) {
-  EXPECT_TRUE(OpenDocument("text_form.pdf"));
-  EXPECT_EQ(FORMTYPE_ACRO_FORM, FPDF_GetFormType(document_));
-}
-
-TEST_F(FPDFFormFillEmbeddertest, HasFormInfoXFAFull) {
-  EXPECT_TRUE(OpenDocument("simple_xfa.pdf"));
-  EXPECT_EQ(FORMTYPE_XFA_FULL, FPDF_GetFormType(document_));
-}
-
-TEST_F(FPDFFormFillEmbeddertest, HasFormInfoXFAForeground) {
-  EXPECT_TRUE(OpenDocument("bug_216.pdf"));
-  EXPECT_EQ(FORMTYPE_XFA_FOREGROUND, FPDF_GetFormType(document_));
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, GetSelectedTextEmptyAndBasicKeyboard) {
-  // Test empty selection.
-  CheckSelection(L"");
-
-  // Test basic selection.
-  TypeTextIntoTextField(3, RegularFormBegin());
-  SelectTextWithKeyboard(3, FWL_VKEY_Left, RegularFormAtX(123.0));
-  CheckSelection(L"ABC");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, GetSelectedTextEmptyAndBasicMouse) {
-  // Test empty selection.
-  CheckSelection(L"");
-
-  // Test basic selection.
-  TypeTextIntoTextField(3, RegularFormBegin());
-  SelectTextWithMouse(RegularFormAtX(125.0), RegularFormBegin());
-  CheckSelection(L"ABC");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, GetSelectedTextFragmentsKeyBoard) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Test selecting first character in forward direction.
-  SelectTextWithKeyboard(1, FWL_VKEY_Right, RegularFormBegin());
-  CheckSelection(L"A");
-
-  // Test selecting entire long string in backwards direction.
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"ABCDEFGHIJKL");
-
-  // Test selecting middle section in backwards direction.
-  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormAtX(170.0));
-  CheckSelection(L"DEFGHI");
-
-  // Test selecting middle selection in forward direction.
-  SelectTextWithKeyboard(6, FWL_VKEY_Right, RegularFormAtX(125.0));
-  CheckSelection(L"DEFGHI");
-
-  // Test selecting last character in backwards direction.
-  SelectTextWithKeyboard(1, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"L");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, GetSelectedTextFragmentsMouse) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Test selecting first character in forward direction.
-  SelectTextWithMouse(RegularFormBegin(), RegularFormAtX(106.0));
-  CheckSelection(L"A");
-
-  // Test selecting entire long string in backwards direction.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"ABCDEFGHIJKL");
-
-  // Test selecting middle section in backwards direction.
-  SelectTextWithMouse(RegularFormAtX(170.0), RegularFormAtX(125.0));
-  CheckSelection(L"DEFGHI");
-
-  // Test selecting middle selection in forward direction.
-  SelectTextWithMouse(RegularFormAtX(125.0), RegularFormAtX(170.0));
-  CheckSelection(L"DEFGHI");
-
-  // Test selecting last character in backwards direction.
-  SelectTextWithMouse(RegularFormEnd(), RegularFormAtX(186.0));
-  CheckSelection(L"L");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       GetSelectedTextEmptyAndBasicNormalComboBox) {
-  // Test empty selection.
-  CheckSelection(L"");
-
-  // Non-editable comboboxes don't allow selection with keyboard.
-  SelectTextWithMouse(NonEditableFormBegin(), NonEditableFormAtX(142.0));
-  CheckSelection(L"Banana");
-
-  // Select other another provided option.
-  SelectNonEditableFormOption(0);
-  CheckSelection(L"Apple");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       GetSelectedTextEmptyAndBasicEditableComboBoxKeyboard) {
-  // Test empty selection.
-  CheckSelection(L"");
-
-  // Test basic selection of text within user editable combobox using keyboard.
-  TypeTextIntoTextField(3, EditableFormBegin());
-  SelectTextWithKeyboard(3, FWL_VKEY_Left, EditableFormAtX(128.0));
-  CheckSelection(L"ABC");
-
-  // Select a provided option.
-  SelectEditableFormOption(1);
-  CheckSelection(L"Bar");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       GetSelectedTextEmptyAndBasicEditableComboBoxMouse) {
-  // Test empty selection.
-  CheckSelection(L"");
-
-  // Test basic selection of text within user editable combobox using mouse.
-  TypeTextIntoTextField(3, EditableFormBegin());
-  SelectTextWithMouse(EditableFormAtX(128.0), EditableFormBegin());
-  CheckSelection(L"ABC");
-
-  // Select a provided option.
-  SelectEditableFormOption(2);
-  CheckSelection(L"Qux");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       GetSelectedTextFragmentsNormalComboBox) {
-  // Test selecting first character in forward direction.
-  SelectTextWithMouse(NonEditableFormBegin(), NonEditableFormAtX(107.0));
-  CheckSelection(L"B");
-
-  // Test selecting entire string in backwards direction.
-  SelectTextWithMouse(NonEditableFormAtX(142.0), NonEditableFormBegin());
-  CheckSelection(L"Banana");
-
-  // Test selecting middle section in backwards direction.
-  SelectTextWithMouse(NonEditableFormAtX(135.0), NonEditableFormAtX(117.0));
-  CheckSelection(L"nan");
-
-  // Test selecting middle section in forward direction.
-  SelectTextWithMouse(NonEditableFormAtX(117.0), NonEditableFormAtX(135.0));
-  CheckSelection(L"nan");
-
-  // Test selecting last character in backwards direction.
-  SelectTextWithMouse(NonEditableFormAtX(142.0), NonEditableFormAtX(138.0));
-  CheckSelection(L"a");
-
-  // Select another option and then reset selection as first three chars.
-  SelectNonEditableFormOption(2);
-  CheckSelection(L"Cherry");
-  SelectTextWithMouse(NonEditableFormBegin(), NonEditableFormAtX(122.0));
-  CheckSelection(L"Che");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       GetSelectedTextFragmentsEditableComboBoxKeyboard) {
-  TypeTextIntoTextField(10, EditableFormBegin());
-
-  // Test selecting first character in forward direction.
-  SelectTextWithKeyboard(1, FWL_VKEY_Right, EditableFormBegin());
-  CheckSelection(L"A");
-
-  // Test selecting entire long string in backwards direction.
-  SelectTextWithKeyboard(10, FWL_VKEY_Left, EditableFormEnd());
-  CheckSelection(L"ABCDEFGHIJ");
-
-  // Test selecting middle section in backwards direction.
-  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormAtX(168.0));
-  CheckSelection(L"DEFGH");
-
-  // Test selecting middle selection in forward direction.
-  SelectTextWithKeyboard(5, FWL_VKEY_Right, EditableFormAtX(127.0));
-  CheckSelection(L"DEFGH");
-
-  // Test selecting last character in backwards direction.
-  SelectTextWithKeyboard(1, FWL_VKEY_Left, EditableFormEnd());
-  CheckSelection(L"J");
-
-  // Select a provided option and then reset selection as first two chars.
-  SelectEditableFormOption(0);
-  CheckSelection(L"Foo");
-  SelectTextWithKeyboard(2, FWL_VKEY_Right, EditableFormBegin());
-  CheckSelection(L"Fo");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       GetSelectedTextFragmentsEditableComboBoxMouse) {
-  TypeTextIntoTextField(10, EditableFormBegin());
-
-  // Test selecting first character in forward direction.
-  SelectTextWithMouse(EditableFormBegin(), EditableFormAtX(107.0));
-  CheckSelection(L"A");
-
-  // Test selecting entire long string in backwards direction.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCDEFGHIJ");
-
-  // Test selecting middle section in backwards direction.
-  SelectTextWithMouse(EditableFormAtX(168.0), EditableFormAtX(127.0));
-  CheckSelection(L"DEFGH");
-
-  // Test selecting middle selection in forward direction.
-  SelectTextWithMouse(EditableFormAtX(127.0), EditableFormAtX(168.0));
-  CheckSelection(L"DEFGH");
-
-  // Test selecting last character in backwards direction.
-  SelectTextWithMouse(EditableFormEnd(), EditableFormAtX(174.0));
-  CheckSelection(L"J");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, DeleteTextFieldEntireSelection) {
-  // Select entire contents of text field.
-  TypeTextIntoTextField(12, RegularFormBegin());
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"ABCDEFGHIJKL");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, DeleteTextFieldSelectionMiddle) {
-  // Select middle section of text.
-  TypeTextIntoTextField(12, RegularFormBegin());
-  SelectTextWithMouse(RegularFormAtX(170.0), RegularFormAtX(125.0));
-  CheckSelection(L"DEFGHI");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"ABCJKL");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, DeleteTextFieldSelectionLeft) {
-  // Select first few characters of text.
-  TypeTextIntoTextField(12, RegularFormBegin());
-  SelectTextWithMouse(RegularFormBegin(), RegularFormAtX(132.0));
-  CheckSelection(L"ABCD");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"EFGHIJKL");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, DeleteTextFieldSelectionRight) {
-  // Select last few characters of text.
-  TypeTextIntoTextField(12, RegularFormBegin());
-  SelectTextWithMouse(RegularFormEnd(), RegularFormAtX(165.0));
-  CheckSelection(L"IJKL");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"ABCDEFGH");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, DeleteEmptyTextFieldSelection) {
-  // Do not select text.
-  TypeTextIntoTextField(12, RegularFormBegin());
-  CheckSelection(L"");
-
-  // Test that attempt to delete empty text selection has no effect.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"ABCDEFGHIJKL");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       DeleteEditableComboBoxEntireSelection) {
-  // Select entire contents of user-editable combobox text field.
-  TypeTextIntoTextField(10, EditableFormBegin());
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCDEFGHIJ");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       DeleteEditableComboBoxSelectionMiddle) {
-  // Select middle section of text.
-  TypeTextIntoTextField(10, EditableFormBegin());
-  SelectTextWithMouse(EditableFormAtX(168.0), EditableFormAtX(127.0));
-  CheckSelection(L"DEFGH");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCIJ");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       DeleteEditableComboBoxSelectionLeft) {
-  // Select first few characters of text.
-  TypeTextIntoTextField(10, EditableFormBegin());
-  SelectTextWithMouse(EditableFormBegin(), EditableFormAtX(132.0));
-  CheckSelection(L"ABCD");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"EFGHIJ");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       DeleteEditableComboBoxSelectionRight) {
-  // Select last few characters of text.
-  TypeTextIntoTextField(10, EditableFormBegin());
-  SelectTextWithMouse(EditableFormEnd(), EditableFormAtX(152.0));
-  CheckSelection(L"GHIJ");
-
-  // Test deleting current text selection. Select what remains after deletion to
-  // check that remaining text is as expected.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCDEF");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       DeleteEmptyEditableComboBoxSelection) {
-  // Do not select text.
-  TypeTextIntoTextField(10, EditableFormBegin());
-  CheckSelection(L"");
-
-  // Test that attempt to delete empty text selection has no effect.
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCDEFGHIJ");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, InsertTextInEmptyTextField) {
-  ClickOnFormFieldAtPoint(RegularFormBegin());
-
-  // Test inserting text into empty text field.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"Hello");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, InsertTextInPopulatedTextFieldLeft) {
-  TypeTextIntoTextField(8, RegularFormBegin());
-
-  // Click on the leftmost part of the text field.
-  ClickOnFormFieldAtPoint(RegularFormBegin());
-
-  // Test inserting text in front of existing text in text field.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"HelloABCDEFGH");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, InsertTextInPopulatedTextFieldMiddle) {
-  TypeTextIntoTextField(8, RegularFormBegin());
-
-  // Click on the middle of the text field.
-  ClickOnFormFieldAtPoint(RegularFormAtX(134.0));
-
-  // Test inserting text in the middle of existing text in text field.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"ABCDHelloEFGH");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest, InsertTextInPopulatedTextFieldRight) {
-  TypeTextIntoTextField(8, RegularFormBegin());
-
-  // Click on the rightmost part of the text field.
-  ClickOnFormFieldAtPoint(RegularFormAtX(166.0));
-
-  // Test inserting text behind existing text in text field.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"ABCDEFGHHello");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedTextFieldWhole) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select entire string in text field.
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"ABCDEFGHIJKL");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"Hello");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedTextFieldLeft) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select left portion of string in text field.
-  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormAtX(148.0));
-  CheckSelection(L"ABCDEF");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"HelloGHIJKL");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedTextFieldMiddle) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select middle portion of string in text field.
-  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormAtX(171.0));
-  CheckSelection(L"DEFGHI");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"ABCHelloJKL");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedTextFieldRight) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select right portion of string in text field.
-  SelectTextWithKeyboard(6, FWL_VKEY_Left, RegularFormEnd());
-  CheckSelection(L"GHIJKL");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllRegularFormTextWithMouse();
-  CheckSelection(L"ABCDEFHello");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextInEmptyEditableComboBox) {
-  ClickOnFormFieldAtPoint(EditableFormBegin());
-
-  // Test inserting text into empty user-editable combobox.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"Hello");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextInPopulatedEditableComboBoxLeft) {
-  TypeTextIntoTextField(6, EditableFormBegin());
-
-  // Click on the leftmost part of the user-editable combobox.
-  ClickOnFormFieldAtPoint(EditableFormBegin());
-
-  // Test inserting text in front of existing text in user-editable combobox.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"HelloABCDEF");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextInPopulatedEditableComboBoxMiddle) {
-  TypeTextIntoTextField(6, EditableFormBegin());
-
-  // Click on the middle of the user-editable combobox.
-  ClickOnFormFieldAtPoint(EditableFormAtX(126.0));
-
-  // Test inserting text in the middle of existing text in user-editable
-  // combobox.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCHelloDEF");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextInPopulatedEditableComboBoxRight) {
-  TypeTextIntoTextField(6, EditableFormBegin());
-
-  // Click on the rightmost part of the user-editable combobox.
-  ClickOnFormFieldAtPoint(EditableFormEnd());
-
-  // Test inserting text behind existing text in user-editable combobox.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCDEFHello");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxWhole) {
-  TypeTextIntoTextField(10, EditableFormBegin());
-
-  // Select entire string in user-editable combobox.
-  SelectTextWithKeyboard(10, FWL_VKEY_Left, EditableFormEnd());
-  CheckSelection(L"ABCDEFGHIJ");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"Hello");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxLeft) {
-  TypeTextIntoTextField(10, EditableFormBegin());
-
-  // Select left portion of string in user-editable combobox.
-  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormAtX(142.0));
-  CheckSelection(L"ABCDE");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"HelloFGHIJ");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxMiddle) {
-  TypeTextIntoTextField(10, EditableFormBegin());
-
-  // Select middle portion of string in user-editable combobox.
-  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormAtX(167.0));
-  CheckSelection(L"DEFGH");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCHelloIJ");
-}
-
-TEST_F(FPDFFormFillComboBoxFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedEditableComboBoxRight) {
-  TypeTextIntoTextField(10, EditableFormBegin());
-
-  // Select right portion of string in user-editable combobox.
-  SelectTextWithKeyboard(5, FWL_VKEY_Left, EditableFormEnd());
-  CheckSelection(L"FGHIJ");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hello");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of user-editable combobox text field to check that
-  // insertion worked as expected.
-  SelectAllEditableFormTextWithMouse();
-  CheckSelection(L"ABCDEHello");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextInEmptyCharLimitTextFieldOverflow) {
-  // Click on the textfield.
-  ClickOnFormFieldAtPoint(CharLimitFormEnd());
-
-  // Delete pre-filled contents of text field with char limit.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"Elephant");
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-
-  // Test inserting text into now empty text field so text to be inserted
-  // exceeds the char limit and is cut off.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"Hippopotam");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextInEmptyCharLimitTextFieldFit) {
-  // Click on the textfield.
-  ClickOnFormFieldAtPoint(CharLimitFormEnd());
-
-  // Delete pre-filled contents of text field with char limit.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"Elephant");
-  FORM_ReplaceSelection(form_handle(), page(), nullptr);
-
-  // Test inserting text into now empty text field so text to be inserted
-  // exceeds the char limit and is cut off.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Zebra");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"Zebra");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextInPopulatedCharLimitTextFieldLeft) {
-  // Click on the leftmost part of the text field.
-  ClickOnFormFieldAtPoint(CharLimitFormBegin());
-
-  // Test inserting text in front of existing text in text field.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"HiElephant");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextInPopulatedCharLimitTextFieldMiddle) {
-  TypeTextIntoTextField(8, RegularFormBegin());
-
-  // Click on the middle of the text field.
-  ClickOnFormFieldAtPoint(CharLimitFormAtX(134.0));
-
-  // Test inserting text in the middle of existing text in text field.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"ElephHiant");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextInPopulatedCharLimitTextFieldRight) {
-  TypeTextIntoTextField(8, RegularFormBegin());
-
-  // Click on the rightmost part of the text field.
-  ClickOnFormFieldAtPoint(CharLimitFormAtX(166.0));
-
-  // Test inserting text behind existing text in text field.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"ElephantHi");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldWhole) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select entire string in text field.
-  SelectTextWithKeyboard(12, FWL_VKEY_Left, CharLimitFormEnd());
-  CheckSelection(L"Elephant");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"Hippopotam");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldLeft) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select left portion of string in text field.
-  SelectTextWithKeyboard(4, FWL_VKEY_Left, CharLimitFormAtX(122.0));
-  CheckSelection(L"Elep");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"Hippophant");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldMiddle) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select middle portion of string in text field.
-  SelectTextWithKeyboard(4, FWL_VKEY_Left, CharLimitFormAtX(136.0));
-  CheckSelection(L"epha");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"ElHippopnt");
-}
-
-TEST_F(FPDFFormFillTextFormEmbeddertest,
-       InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldRight) {
-  TypeTextIntoTextField(12, RegularFormBegin());
-
-  // Select right portion of string in text field.
-  SelectTextWithKeyboard(4, FWL_VKEY_Left, CharLimitFormAtX(152.0));
-  CheckSelection(L"hant");
-
-  // Test replacing text selection with text to be inserted.
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text_to_insert =
-      GetFPDFWideString(L"Hippopotamus");
-  FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get());
-
-  // Select entire contents of text field to check that insertion worked
-  // as expected.
-  SelectAllCharLimitFormTextWithMouse();
-  CheckSelection(L"ElepHippop");
-}
diff --git a/fpdfsdk/fpdfppo.cpp b/fpdfsdk/fpdfppo.cpp
deleted file mode 100644
index d88f910..0000000
--- a/fpdfsdk/fpdfppo.cpp
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "public/fpdf_ppo.h"
-
-#include <map>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#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/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-CPDF_Object* PageDictGetInheritableTag(CPDF_Dictionary* pDict,
-                                       const ByteString& bsSrcTag) {
-  if (!pDict || bsSrcTag.IsEmpty())
-    return nullptr;
-  if (!pDict->KeyExist("Parent") || !pDict->KeyExist("Type"))
-    return nullptr;
-
-  CPDF_Object* pType = pDict->GetObjectFor("Type")->GetDirect();
-  if (!ToName(pType))
-    return nullptr;
-  if (pType->GetString().Compare("Page"))
-    return nullptr;
-
-  CPDF_Dictionary* pp =
-      ToDictionary(pDict->GetObjectFor("Parent")->GetDirect());
-  if (!pp)
-    return nullptr;
-
-  if (pDict->KeyExist(bsSrcTag))
-    return pDict->GetObjectFor(bsSrcTag);
-
-  while (pp) {
-    if (pp->KeyExist(bsSrcTag))
-      return pp->GetObjectFor(bsSrcTag);
-    if (!pp->KeyExist("Parent"))
-      break;
-    pp = ToDictionary(pp->GetObjectFor("Parent")->GetDirect());
-  }
-  return nullptr;
-}
-
-bool CopyInheritable(CPDF_Dictionary* pCurPageDict,
-                     CPDF_Dictionary* pSrcPageDict,
-                     const ByteString& key) {
-  if (pCurPageDict->KeyExist(key))
-    return true;
-
-  CPDF_Object* pInheritable = PageDictGetInheritableTag(pSrcPageDict, key);
-  if (!pInheritable)
-    return false;
-
-  pCurPageDict->SetFor(key, pInheritable->Clone());
-  return true;
-}
-
-bool ParserPageRangeString(ByteString rangstring,
-                           std::vector<uint16_t>* pageArray,
-                           int nCount) {
-  if (rangstring.IsEmpty())
-    return true;
-
-  rangstring.Remove(' ');
-  size_t nLength = rangstring.GetLength();
-  ByteString cbCompareString("0123456789-,");
-  for (size_t i = 0; i < nLength; ++i) {
-    if (!cbCompareString.Contains(rangstring[i]))
-      return false;
-  }
-
-  ByteString cbMidRange;
-  size_t nStringFrom = 0;
-  Optional<size_t> nStringTo = 0;
-  while (nStringTo < nLength) {
-    nStringTo = rangstring.Find(',', nStringFrom);
-    if (!nStringTo.has_value())
-      nStringTo = nLength;
-    cbMidRange = rangstring.Mid(nStringFrom, nStringTo.value() - nStringFrom);
-    auto nMid = cbMidRange.Find('-');
-    if (!nMid.has_value()) {
-      uint16_t pageNum =
-          pdfium::base::checked_cast<uint16_t>(atoi(cbMidRange.c_str()));
-      if (pageNum <= 0 || pageNum > nCount)
-        return false;
-      pageArray->push_back(pageNum);
-    } else {
-      uint16_t nStartPageNum = pdfium::base::checked_cast<uint16_t>(
-          atoi(cbMidRange.Left(nMid.value()).c_str()));
-      if (nStartPageNum == 0)
-        return false;
-
-      nMid = nMid.value() + 1;
-      size_t nEnd = cbMidRange.GetLength() - nMid.value();
-      if (nEnd == 0)
-        return false;
-
-      uint16_t nEndPageNum = pdfium::base::checked_cast<uint16_t>(
-          atoi(cbMidRange.Mid(nMid.value(), nEnd).c_str()));
-      if (nStartPageNum < 0 || nStartPageNum > nEndPageNum ||
-          nEndPageNum > nCount) {
-        return false;
-      }
-      for (uint16_t i = nStartPageNum; i <= nEndPageNum; ++i) {
-        pageArray->push_back(i);
-      }
-    }
-    nStringFrom = nStringTo.value() + 1;
-  }
-  return true;
-}
-
-}  // namespace
-
-class CPDF_PageOrganizer {
- public:
-  CPDF_PageOrganizer(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc);
-  ~CPDF_PageOrganizer();
-
-  bool PDFDocInit();
-  bool ExportPage(const std::vector<uint16_t>& pageNums, int nIndex);
-
- private:
-  using ObjectNumberMap = std::map<uint32_t, uint32_t>;
-
-  bool UpdateReference(CPDF_Object* pObj, ObjectNumberMap* pObjNumberMap);
-  uint32_t GetNewObjId(ObjectNumberMap* pObjNumberMap, CPDF_Reference* pRef);
-
-  UnownedPtr<CPDF_Document> m_pDestPDFDoc;
-  UnownedPtr<CPDF_Document> m_pSrcPDFDoc;
-};
-
-CPDF_PageOrganizer::CPDF_PageOrganizer(CPDF_Document* pDestPDFDoc,
-                                       CPDF_Document* pSrcPDFDoc)
-    : m_pDestPDFDoc(pDestPDFDoc), m_pSrcPDFDoc(pSrcPDFDoc) {}
-
-CPDF_PageOrganizer::~CPDF_PageOrganizer() {}
-
-bool CPDF_PageOrganizer::PDFDocInit() {
-  ASSERT(m_pDestPDFDoc);
-  ASSERT(m_pSrcPDFDoc);
-
-  CPDF_Dictionary* pNewRoot = m_pDestPDFDoc->GetRoot();
-  if (!pNewRoot)
-    return false;
-
-  CPDF_Dictionary* pDocInfoDict = m_pDestPDFDoc->GetInfo();
-  if (!pDocInfoDict)
-    return false;
-
-  pDocInfoDict->SetNewFor<CPDF_String>("Producer", "PDFium", false);
-
-  ByteString cbRootType = pNewRoot->GetStringFor("Type", "");
-  if (cbRootType.IsEmpty())
-    pNewRoot->SetNewFor<CPDF_Name>("Type", "Catalog");
-
-  CPDF_Object* pElement = pNewRoot->GetObjectFor("Pages");
-  CPDF_Dictionary* pNewPages =
-      pElement ? ToDictionary(pElement->GetDirect()) : nullptr;
-  if (!pNewPages) {
-    pNewPages = m_pDestPDFDoc->NewIndirect<CPDF_Dictionary>();
-    pNewRoot->SetNewFor<CPDF_Reference>("Pages", m_pDestPDFDoc.Get(),
-                                        pNewPages->GetObjNum());
-  }
-
-  ByteString cbPageType = pNewPages->GetStringFor("Type", "");
-  if (cbPageType.IsEmpty())
-    pNewPages->SetNewFor<CPDF_Name>("Type", "Pages");
-
-  if (!pNewPages->GetArrayFor("Kids")) {
-    pNewPages->SetNewFor<CPDF_Number>("Count", 0);
-    pNewPages->SetNewFor<CPDF_Reference>(
-        "Kids", m_pDestPDFDoc.Get(),
-        m_pDestPDFDoc->NewIndirect<CPDF_Array>()->GetObjNum());
-  }
-
-  return true;
-}
-
-bool CPDF_PageOrganizer::ExportPage(const std::vector<uint16_t>& pageNums,
-                                    int nIndex) {
-  int curpage = nIndex;
-  auto pObjNumberMap = pdfium::MakeUnique<ObjectNumberMap>();
-  for (size_t i = 0; i < pageNums.size(); ++i) {
-    CPDF_Dictionary* pCurPageDict = m_pDestPDFDoc->CreateNewPage(curpage);
-    CPDF_Dictionary* pSrcPageDict = m_pSrcPDFDoc->GetPage(pageNums[i] - 1);
-    if (!pSrcPageDict || !pCurPageDict)
-      return false;
-
-    // Clone the page dictionary
-    for (const auto& it : *pSrcPageDict) {
-      const ByteString& cbSrcKeyStr = it.first;
-      if (cbSrcKeyStr == "Type" || cbSrcKeyStr == "Parent")
-        continue;
-
-      CPDF_Object* pObj = it.second.get();
-      pCurPageDict->SetFor(cbSrcKeyStr, pObj->Clone());
-    }
-
-    // inheritable item
-    // Even though some entries are required by the PDF spec, there exist
-    // PDFs that omit them. Set some defaults in this case.
-    // 1 MediaBox - required
-    if (!CopyInheritable(pCurPageDict, pSrcPageDict, "MediaBox")) {
-      // Search for "CropBox" in the source page dictionary.
-      // If it does not exist, use the default letter size.
-      CPDF_Object* pInheritable =
-          PageDictGetInheritableTag(pSrcPageDict, "CropBox");
-      if (pInheritable) {
-        pCurPageDict->SetFor("MediaBox", pInheritable->Clone());
-      } else {
-        // Make the default size letter size (8.5"x11")
-        CPDF_Array* pArray = pCurPageDict->SetNewFor<CPDF_Array>("MediaBox");
-        pArray->AddNew<CPDF_Number>(0);
-        pArray->AddNew<CPDF_Number>(0);
-        pArray->AddNew<CPDF_Number>(612);
-        pArray->AddNew<CPDF_Number>(792);
-      }
-    }
-
-    // 2 Resources - required
-    if (!CopyInheritable(pCurPageDict, pSrcPageDict, "Resources")) {
-      // Use a default empty resources if it does not exist.
-      pCurPageDict->SetNewFor<CPDF_Dictionary>("Resources");
-    }
-
-    // 3 CropBox - optional
-    CopyInheritable(pCurPageDict, pSrcPageDict, "CropBox");
-    // 4 Rotate - optional
-    CopyInheritable(pCurPageDict, pSrcPageDict, "Rotate");
-
-    // Update the reference
-    uint32_t dwOldPageObj = pSrcPageDict->GetObjNum();
-    uint32_t dwNewPageObj = pCurPageDict->GetObjNum();
-    (*pObjNumberMap)[dwOldPageObj] = dwNewPageObj;
-    UpdateReference(pCurPageDict, pObjNumberMap.get());
-    ++curpage;
-  }
-
-  return true;
-}
-
-bool CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj,
-                                         ObjectNumberMap* pObjNumberMap) {
-  switch (pObj->GetType()) {
-    case CPDF_Object::REFERENCE: {
-      CPDF_Reference* pReference = pObj->AsReference();
-      uint32_t newobjnum = GetNewObjId(pObjNumberMap, pReference);
-      if (newobjnum == 0)
-        return false;
-      pReference->SetRef(m_pDestPDFDoc.Get(), newobjnum);
-      break;
-    }
-    case CPDF_Object::DICTIONARY: {
-      CPDF_Dictionary* pDict = pObj->AsDictionary();
-      auto it = pDict->begin();
-      while (it != pDict->end()) {
-        const ByteString& key = it->first;
-        CPDF_Object* pNextObj = it->second.get();
-        ++it;
-        if (key == "Parent" || key == "Prev" || key == "First")
-          continue;
-        if (!pNextObj)
-          return false;
-        if (!UpdateReference(pNextObj, pObjNumberMap))
-          pDict->RemoveFor(key);
-      }
-      break;
-    }
-    case CPDF_Object::ARRAY: {
-      CPDF_Array* pArray = pObj->AsArray();
-      for (size_t i = 0; i < pArray->GetCount(); ++i) {
-        CPDF_Object* pNextObj = pArray->GetObjectAt(i);
-        if (!pNextObj)
-          return false;
-        if (!UpdateReference(pNextObj, pObjNumberMap))
-          return false;
-      }
-      break;
-    }
-    case CPDF_Object::STREAM: {
-      CPDF_Stream* pStream = pObj->AsStream();
-      CPDF_Dictionary* pDict = pStream->GetDict();
-      if (!pDict)
-        return false;
-      if (!UpdateReference(pDict, pObjNumberMap))
-        return false;
-      break;
-    }
-    default:
-      break;
-  }
-
-  return true;
-}
-
-uint32_t CPDF_PageOrganizer::GetNewObjId(ObjectNumberMap* pObjNumberMap,
-                                         CPDF_Reference* pRef) {
-  if (!pRef)
-    return 0;
-
-  uint32_t dwObjnum = pRef->GetRefObjNum();
-  uint32_t dwNewObjNum = 0;
-  const auto it = pObjNumberMap->find(dwObjnum);
-  if (it != pObjNumberMap->end())
-    dwNewObjNum = it->second;
-  if (dwNewObjNum)
-    return dwNewObjNum;
-
-  CPDF_Object* pDirect = pRef->GetDirect();
-  if (!pDirect)
-    return 0;
-
-  std::unique_ptr<CPDF_Object> pClone = pDirect->Clone();
-  if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) {
-    if (pDictClone->KeyExist("Type")) {
-      ByteString strType = pDictClone->GetStringFor("Type");
-      if (!FXSYS_stricmp(strType.c_str(), "Pages"))
-        return 4;
-      if (!FXSYS_stricmp(strType.c_str(), "Page"))
-        return 0;
-    }
-  }
-  CPDF_Object* pUnownedClone =
-      m_pDestPDFDoc->AddIndirectObject(std::move(pClone));
-  dwNewObjNum = pUnownedClone->GetObjNum();
-  (*pObjNumberMap)[dwObjnum] = dwNewObjNum;
-  if (!UpdateReference(pUnownedClone, pObjNumberMap))
-    return 0;
-
-  return dwNewObjNum;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
-                                                     FPDF_DOCUMENT src_doc,
-                                                     FPDF_BYTESTRING pagerange,
-                                                     int index) {
-  CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc);
-  if (!dest_doc)
-    return false;
-
-  CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
-  if (!pSrcDoc)
-    return false;
-
-  std::vector<uint16_t> pageArray;
-  int nCount = pSrcDoc->GetPageCount();
-  if (pagerange) {
-    if (!ParserPageRangeString(pagerange, &pageArray, nCount))
-      return false;
-  } else {
-    for (int i = 1; i <= nCount; ++i) {
-      pageArray.push_back(i);
-    }
-  }
-
-  CPDF_PageOrganizer pageOrg(pDestDoc, pSrcDoc);
-  return pageOrg.PDFDocInit() && pageOrg.ExportPage(pageArray, index);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) {
-  CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc);
-  if (!pDstDoc)
-    return false;
-
-  CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
-  if (!pSrcDoc)
-    return false;
-
-  CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
-  pSrcDict = pSrcDict->GetDictFor("ViewerPreferences");
-  if (!pSrcDict)
-    return false;
-
-  CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
-  if (!pDstDict)
-    return false;
-
-  pDstDict->SetFor("ViewerPreferences", pSrcDict->CloneDirectObject());
-  return true;
-}
diff --git a/fpdfsdk/fpdfppo_embeddertest.cpp b/fpdfsdk/fpdfppo_embeddertest.cpp
deleted file mode 100644
index c642b71..0000000
--- a/fpdfsdk/fpdfppo_embeddertest.cpp
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include <string>
-
-#include "public/fpdf_edit.h"
-#include "public/fpdf_ppo.h"
-#include "public/fpdf_save.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-namespace {
-
-class FPDFPPOEmbeddertest : public EmbedderTest {};
-
-int FakeBlockWriter(FPDF_FILEWRITE* pThis,
-                    const void* pData,
-                    unsigned long size) {
-  return size;
-}
-
-}  // namespace
-
-TEST_F(FPDFPPOEmbeddertest, NoViewerPreferences) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_FALSE(FPDF_CopyViewerPreferences(output_doc, document()));
-  FPDF_CloseDocument(output_doc);
-}
-
-TEST_F(FPDFPPOEmbeddertest, ViewerPreferences) {
-  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
-  FPDF_CloseDocument(output_doc);
-}
-
-TEST_F(FPDFPPOEmbeddertest, ImportPages) {
-  ASSERT_TRUE(OpenDocument("viewer_ref.pdf"));
-
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  ASSERT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0));
-  EXPECT_EQ(1, FPDF_GetPageCount(output_doc));
-  FPDF_CloseDocument(output_doc);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFPPOEmbeddertest, BadRepeatViewerPref) {
-  ASSERT_TRUE(OpenDocument("repeat_viewer_ref.pdf"));
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
-
-  FPDF_FILEWRITE writer;
-  writer.version = 1;
-  writer.WriteBlock = FakeBlockWriter;
-
-  EXPECT_TRUE(FPDF_SaveAsCopy(output_doc, &writer, 0));
-  FPDF_CloseDocument(output_doc);
-}
-
-TEST_F(FPDFPPOEmbeddertest, BadCircularViewerPref) {
-  ASSERT_TRUE(OpenDocument("circular_viewer_ref.pdf"));
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
-
-  FPDF_FILEWRITE writer;
-  writer.version = 1;
-  writer.WriteBlock = FakeBlockWriter;
-
-  EXPECT_TRUE(FPDF_SaveAsCopy(output_doc, &writer, 0));
-  FPDF_CloseDocument(output_doc);
-}
-
-TEST_F(FPDFPPOEmbeddertest, BadRanges) {
-  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
-
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "clams", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "0", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "42", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1,2", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1-2", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), ",1", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1,", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "1-", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "-1", 0));
-  EXPECT_FALSE(FPDF_ImportPages(output_doc, document(), "-,0,,,1-", 0));
-  FPDF_CloseDocument(output_doc);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFPPOEmbeddertest, GoodRanges) {
-  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
-
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc, document()));
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1,1,1,1", 0));
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1-1", 0));
-  EXPECT_EQ(5, FPDF_GetPageCount(output_doc));
-  FPDF_CloseDocument(output_doc);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFPPOEmbeddertest, BUG_664284) {
-  EXPECT_TRUE(OpenDocument("bug_664284.pdf"));
-
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_NE(nullptr, page);
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0));
-  FPDF_CloseDocument(output_doc);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFPPOEmbeddertest, BUG_750568) {
-  const char* const kHashes[] = {
-      "64ad08132a1c5a166768298c8a578f57", "83b83e2f6bc80707d0a917c7634140b9",
-      "913cd3723a451e4e46fbc2c05702d1ee", "81fb7cfd4860f855eb468f73dfeb6d60"};
-
-  ASSERT_TRUE(OpenDocument("bug_750568.pdf"));
-  ASSERT_EQ(4, FPDF_GetPageCount(document()));
-
-  for (size_t i = 0; i < 4; ++i) {
-    FPDF_PAGE page = LoadPage(i);
-    ASSERT_NE(nullptr, page);
-
-    FPDF_BITMAP bitmap = RenderPage(page);
-    ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap));
-    ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap));
-    ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap));
-
-    std::string digest = HashBitmap(bitmap);
-    FPDFBitmap_Destroy(bitmap);
-    UnloadPage(page);
-    EXPECT_EQ(kHashes[i], digest);
-  }
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  ASSERT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1,2,3,4", 0));
-  ASSERT_EQ(4, FPDF_GetPageCount(output_doc));
-  for (size_t i = 0; i < 4; ++i) {
-    FPDF_PAGE page = FPDF_LoadPage(output_doc, i);
-    ASSERT_NE(nullptr, page);
-
-    FPDF_BITMAP bitmap = RenderPage(page);
-    ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap));
-    ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap));
-    ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap));
-
-    std::string digest = HashBitmap(bitmap);
-    FPDFBitmap_Destroy(bitmap);
-    FPDF_ClosePage(page);
-    EXPECT_EQ(kHashes[i], digest);
-  }
-  FPDF_CloseDocument(output_doc);
-}
-
-TEST_F(FPDFPPOEmbeddertest, ImportWithZeroLengthStream) {
-  EXPECT_TRUE(OpenDocument("zero_length_stream.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_NE(nullptr, page);
-
-  FPDF_BITMAP bitmap = RenderPage(page);
-  ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap));
-  ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap));
-  ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap));
-
-  std::string digest = HashBitmap(bitmap);
-  FPDFBitmap_Destroy(bitmap);
-  FPDF_ClosePage(page);
-
-  FPDF_DOCUMENT new_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(new_doc);
-  EXPECT_TRUE(FPDF_ImportPages(new_doc, document(), "1", 0));
-
-  EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
-  FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
-  ASSERT_NE(nullptr, new_page);
-  FPDF_BITMAP new_bitmap = RenderPage(new_page);
-  ASSERT_EQ(200, FPDFBitmap_GetWidth(new_bitmap));
-  ASSERT_EQ(200, FPDFBitmap_GetHeight(new_bitmap));
-  ASSERT_EQ(800, FPDFBitmap_GetStride(new_bitmap));
-
-  std::string new_digest = HashBitmap(new_bitmap);
-  FPDFBitmap_Destroy(new_bitmap);
-  FPDF_ClosePage(new_page);
-  FPDF_CloseDocument(new_doc);
-
-  EXPECT_EQ(digest, new_digest);
-}
diff --git a/fpdfsdk/fpdfsave.cpp b/fpdfsdk/fpdfsave.cpp
deleted file mode 100644
index ddbc994..0000000
--- a/fpdfsdk/fpdfsave.cpp
+++ /dev/null
@@ -1,289 +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 "public/fpdf_save.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fpdfapi/edit/cpdf_creator.h"
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_reference.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fxcrt/cfx_memorystream.h"
-#include "core/fxcrt/fx_extension.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fpdfsdk/fsdk_filewriteadapter.h"
-#include "public/fpdf_edit.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "core/fxcrt/cfx_checksumcontext.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
-#include "public/fpdf_formfill.h"
-#include "xfa/fxfa/cxfa_eventparam.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
-#include "xfa/fxfa/cxfa_ffdocview.h"
-#include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#include "xfa/fxfa/cxfa_widgetacciterator.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
-#endif
-
-#if _FX_OS_ == _FX_OS_ANDROID_
-#include <time.h>
-#else
-#include <ctime>
-#endif
-
-namespace {
-
-#ifdef PDF_ENABLE_XFA
-bool SaveXFADocumentData(CPDFXFA_Context* pContext,
-                         std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
-  if (!pContext)
-    return false;
-
-  if (!pContext->ContainsXFAForm())
-    return true;
-
-  CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
-  if (!pXFADocView)
-    return true;
-
-  CPDF_Document* pPDFDocument = pContext->GetPDFDoc();
-  if (!pPDFDocument)
-    return false;
-
-  const CPDF_Dictionary* pRoot = pPDFDocument->GetRoot();
-  if (!pRoot)
-    return false;
-
-  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
-  if (!pAcroForm)
-    return false;
-
-  CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
-  if (!pXFA)
-    return true;
-
-  CPDF_Array* pArray = pXFA->AsArray();
-  if (!pArray)
-    return false;
-
-  int size = pArray->GetCount();
-  int iFormIndex = -1;
-  int iDataSetsIndex = -1;
-  int iTemplate = -1;
-  int iLast = size - 2;
-  for (int i = 0; i < size - 1; i++) {
-    CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
-    if (!pPDFObj->IsString())
-      continue;
-    if (pPDFObj->GetString() == "form")
-      iFormIndex = i + 1;
-    else if (pPDFObj->GetString() == "datasets")
-      iDataSetsIndex = i + 1;
-    else if (pPDFObj->GetString() == "template")
-      iTemplate = i + 1;
-  }
-  auto pChecksum = pdfium::MakeUnique<CFX_ChecksumContext>();
-  pChecksum->StartChecksum();
-
-  // template
-  if (iTemplate > -1) {
-    CPDF_Stream* pTemplateStream = pArray->GetStreamAt(iTemplate);
-    auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pTemplateStream);
-    pAcc->LoadAllDataFiltered();
-    RetainPtr<IFX_SeekableStream> pTemplate =
-        pdfium::MakeRetain<CFX_MemoryStream>(
-            const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize(), false);
-    pChecksum->UpdateChecksum(pTemplate);
-  }
-  CPDF_Stream* pFormStream = nullptr;
-  CPDF_Stream* pDataSetsStream = nullptr;
-  if (iFormIndex != -1) {
-    // Get form CPDF_Stream
-    CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex);
-    if (pFormPDFObj->IsReference()) {
-      CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect();
-      if (pFormDirectObj && pFormDirectObj->IsStream()) {
-        pFormStream = (CPDF_Stream*)pFormDirectObj;
-      }
-    } else if (pFormPDFObj->IsStream()) {
-      pFormStream = (CPDF_Stream*)pFormPDFObj;
-    }
-  }
-
-  if (iDataSetsIndex != -1) {
-    // Get datasets CPDF_Stream
-    CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex);
-    if (pDataSetsPDFObj->IsReference()) {
-      CPDF_Reference* pDataSetsRefObj = (CPDF_Reference*)pDataSetsPDFObj;
-      CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect();
-      if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) {
-        pDataSetsStream = (CPDF_Stream*)pDataSetsDirectObj;
-      }
-    } else if (pDataSetsPDFObj->IsStream()) {
-      pDataSetsStream = (CPDF_Stream*)pDataSetsPDFObj;
-    }
-  }
-  // L"datasets"
-  {
-    RetainPtr<IFX_SeekableStream> pDsfileWrite =
-        pdfium::MakeRetain<CFX_MemoryStream>(false);
-    CXFA_FFDoc* ffdoc = pXFADocView->GetDoc();
-    if (ffdoc->SavePackage(
-            ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
-            pDsfileWrite, nullptr) &&
-        pDsfileWrite->GetSize() > 0) {
-      // Datasets
-      pChecksum->UpdateChecksum(pDsfileWrite);
-      pChecksum->FinishChecksum();
-      auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>(
-          pPDFDocument->GetByteStringPool());
-      if (iDataSetsIndex != -1) {
-        if (pDataSetsStream) {
-          pDataSetsStream->InitStreamFromFile(pDsfileWrite,
-                                              std::move(pDataDict));
-        }
-      } else {
-        CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
-        pData->InitStreamFromFile(pDsfileWrite, std::move(pDataDict));
-        iLast = pArray->GetCount() - 2;
-        pArray->InsertNewAt<CPDF_String>(iLast, "datasets", false);
-        pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
-                                            pData->GetObjNum());
-      }
-      fileList->push_back(std::move(pDsfileWrite));
-    }
-  }
-  // L"form"
-  {
-    RetainPtr<IFX_SeekableStream> pfileWrite =
-        pdfium::MakeRetain<CFX_MemoryStream>(false);
-
-    CXFA_FFDoc* ffdoc = pXFADocView->GetDoc();
-    if (ffdoc->SavePackage(
-            ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
-            pfileWrite, pChecksum.get()) &&
-        pfileWrite->GetSize() > 0) {
-      auto pDataDict = pdfium::MakeUnique<CPDF_Dictionary>(
-          pPDFDocument->GetByteStringPool());
-      if (iFormIndex != -1) {
-        if (pFormStream)
-          pFormStream->InitStreamFromFile(pfileWrite, std::move(pDataDict));
-      } else {
-        CPDF_Stream* pData = pPDFDocument->NewIndirect<CPDF_Stream>();
-        pData->InitStreamFromFile(pfileWrite, std::move(pDataDict));
-        iLast = pArray->GetCount() - 2;
-        pArray->InsertNewAt<CPDF_String>(iLast, "form", false);
-        pArray->InsertNewAt<CPDF_Reference>(iLast + 1, pPDFDocument,
-                                            pData->GetObjNum());
-      }
-      fileList->push_back(std::move(pfileWrite));
-    }
-  }
-  return true;
-}
-
-bool SendPostSaveToXFADoc(CPDFXFA_Context* pContext) {
-  if (!pContext)
-    return false;
-
-  if (!pContext->ContainsXFAForm())
-    return true;
-
-  CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
-  if (!pXFADocView)
-    return false;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
-  std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator =
-      pXFADocView->CreateWidgetAccIterator();
-  while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) {
-    CXFA_EventParam preParam;
-    preParam.m_eType = XFA_EVENT_PostSave;
-    pWidgetHandler->ProcessEvent(pWidgetAcc, &preParam);
-  }
-  pXFADocView->UpdateDocView();
-  pContext->ClearChangeMark();
-  return true;
-}
-
-bool SendPreSaveToXFADoc(CPDFXFA_Context* pContext,
-                         std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
-  if (!pContext->ContainsXFAForm())
-    return true;
-
-  CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
-  if (!pXFADocView)
-    return true;
-
-  CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
-  std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator =
-      pXFADocView->CreateWidgetAccIterator();
-  while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext()) {
-    CXFA_EventParam preParam;
-    preParam.m_eType = XFA_EVENT_PreSave;
-    pWidgetHandler->ProcessEvent(pWidgetAcc, &preParam);
-  }
-  pXFADocView->UpdateDocView();
-  return SaveXFADocumentData(pContext, fileList);
-}
-#endif  // PDF_ENABLE_XFA
-
-bool FPDF_Doc_Save(FPDF_DOCUMENT document,
-                   FPDF_FILEWRITE* pFileWrite,
-                   FPDF_DWORD flags,
-                   FPDF_BOOL bSetVersion,
-                   int fileVerion) {
-  CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pPDFDoc)
-    return 0;
-
-#ifdef PDF_ENABLE_XFA
-  CPDFXFA_Context* pContext = static_cast<CPDFXFA_Context*>(document);
-  std::vector<RetainPtr<IFX_SeekableStream>> fileList;
-  SendPreSaveToXFADoc(pContext, &fileList);
-#endif  // PDF_ENABLE_XFA
-
-  if (flags < FPDF_INCREMENTAL || flags > FPDF_REMOVE_SECURITY)
-    flags = 0;
-
-  CPDF_Creator fileMaker(pPDFDoc,
-                         pdfium::MakeRetain<FSDK_FileWriteAdapter>(pFileWrite));
-  if (bSetVersion)
-    fileMaker.SetFileVersion(fileVerion);
-  if (flags == FPDF_REMOVE_SECURITY) {
-    flags = 0;
-    fileMaker.RemoveSecurity();
-  }
-
-  bool bRet = fileMaker.Create(flags);
-#ifdef PDF_ENABLE_XFA
-  SendPostSaveToXFADoc(pContext);
-#endif  // PDF_ENABLE_XFA
-  return bRet;
-}
-
-}  // namespace
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SaveAsCopy(FPDF_DOCUMENT document,
-                                                    FPDF_FILEWRITE* pFileWrite,
-                                                    FPDF_DWORD flags) {
-  return FPDF_Doc_Save(document, pFileWrite, flags, false, 0);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_SaveWithVersion(FPDF_DOCUMENT document,
-                     FPDF_FILEWRITE* pFileWrite,
-                     FPDF_DWORD flags,
-                     int fileVersion) {
-  return FPDF_Doc_Save(document, pFileWrite, flags, true, fileVersion);
-}
diff --git a/fpdfsdk/fpdfsave_embeddertest.cpp b/fpdfsdk/fpdfsave_embeddertest.cpp
deleted file mode 100644
index 3122be1..0000000
--- a/fpdfsdk/fpdfsave_embeddertest.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "public/fpdf_save.h"
-
-#include <string>
-
-#include "core/fxcrt/fx_string.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdf_ppo.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gmock/include/gmock/gmock-matchers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-class FPDFSaveEmbedderTest : public EmbedderTest {};
-
-TEST_F(FPDFSaveEmbedderTest, SaveSimpleDoc) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
-  EXPECT_EQ(805u, GetString().length());
-}
-
-TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithVersion) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 14));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.4\r\n"));
-  EXPECT_EQ(805u, GetString().length());
-}
-TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithBadVersion) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, -1));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
-
-  ClearString();
-  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 0));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
-
-  ClearString();
-  EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 18));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n"));
-}
-
-TEST_F(FPDFSaveEmbedderTest, SaveCopiedDoc) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
-  EXPECT_TRUE(output_doc);
-  EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0));
-  EXPECT_TRUE(FPDF_SaveAsCopy(output_doc, this, 0));
-  FPDF_CloseDocument(output_doc);
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFSaveEmbedderTest, SaveLinearizedDoc) {
-  const int kPageCount = 3;
-  std::string original_md5[kPageCount];
-
-  EXPECT_TRUE(OpenDocument("linearized.pdf"));
-  for (int i = 0; i < kPageCount; ++i) {
-    FPDF_PAGE page = LoadPage(i);
-    FPDF_BITMAP bitmap = RenderPage(page);
-    EXPECT_EQ(612, FPDFBitmap_GetWidth(bitmap));
-    EXPECT_EQ(792, FPDFBitmap_GetHeight(bitmap));
-    original_md5[i] = HashBitmap(bitmap);
-    FPDFBitmap_Destroy(bitmap);
-    UnloadPage(page);
-  }
-
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.6\r\n"));
-  EXPECT_THAT(GetString(), testing::HasSubstr("/Root "));
-  EXPECT_THAT(GetString(), testing::HasSubstr("/Info "));
-  EXPECT_EQ(8219u, GetString().length());
-
-  // Make sure new document renders the same as the old one.
-  EXPECT_TRUE(OpenSavedDocument());
-  for (int i = 0; i < kPageCount; ++i) {
-    FPDF_PAGE page = LoadSavedPage(i);
-    FPDF_BITMAP bitmap = RenderSavedPage(page);
-    EXPECT_EQ(original_md5[i], HashBitmap(bitmap));
-    FPDFBitmap_Destroy(bitmap);
-    CloseSavedPage(page);
-  }
-  CloseSavedDocument();
-}
-
-TEST_F(FPDFSaveEmbedderTest, BUG_342) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  EXPECT_THAT(GetString(), testing::HasSubstr("0000000000 65535 f\r\n"));
-  EXPECT_THAT(GetString(),
-              testing::Not(testing::HasSubstr("0000000000 65536 f\r\n")));
-}
diff --git a/fpdfsdk/fpdftext.cpp b/fpdfsdk/fpdftext.cpp
deleted file mode 100644
index 68bf4f8..0000000
--- a/fpdfsdk/fpdftext.cpp
+++ /dev/null
@@ -1,393 +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 "public/fpdf_text.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "core/fpdfapi/page/cpdf_page.h"
-#include "core/fpdfdoc/cpdf_viewerpreferences.h"
-#include "core/fpdftext/cpdf_linkextract.h"
-#include "core/fpdftext/cpdf_textpage.h"
-#include "core/fpdftext/cpdf_textpagefind.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#endif  // PDF_ENABLE_XFA
-
-#ifdef _WIN32
-#include <tchar.h>
-#endif
-
-namespace {
-
-constexpr size_t kBytesPerCharacter = sizeof(unsigned short);
-
-CPDF_TextPage* CPDFTextPageFromFPDFTextPage(FPDF_TEXTPAGE text_page) {
-  return static_cast<CPDF_TextPage*>(text_page);
-}
-
-CPDF_TextPageFind* CPDFTextPageFindFromFPDFSchHandle(FPDF_SCHHANDLE handle) {
-  return static_cast<CPDF_TextPageFind*>(handle);
-}
-
-CPDF_LinkExtract* CPDFLinkExtractFromFPDFPageLink(FPDF_PAGELINK link) {
-  return static_cast<CPDF_LinkExtract*>(link);
-}
-
-}  // namespace
-
-FPDF_EXPORT FPDF_TEXTPAGE FPDF_CALLCONV FPDFText_LoadPage(FPDF_PAGE page) {
-  CPDF_Page* pPDFPage = CPDFPageFromFPDFPage(page);
-  if (!pPDFPage)
-    return nullptr;
-
-#ifdef PDF_ENABLE_XFA
-  CPDFXFA_Page* pPage = (CPDFXFA_Page*)page;
-  CPDFXFA_Context* pContext = pPage->GetContext();
-  CPDF_ViewerPreferences viewRef(pContext->GetPDFDoc());
-#else  // PDF_ENABLE_XFA
-  CPDF_ViewerPreferences viewRef(pPDFPage->m_pDocument.Get());
-#endif  // PDF_ENABLE_XFA
-
-  CPDF_TextPage* textpage = new CPDF_TextPage(
-      pPDFPage, viewRef.IsDirectionR2L() ? FPDFText_Direction::Right
-                                         : FPDFText_Direction::Left);
-  textpage->ParseTextPage();
-  return textpage;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFText_ClosePage(FPDF_TEXTPAGE text_page) {
-  delete CPDFTextPageFromFPDFTextPage(text_page);
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page) {
-  if (!text_page)
-    return -1;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  return textpage->CountChars();
-}
-
-FPDF_EXPORT unsigned int FPDF_CALLCONV
-FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index) {
-  if (!text_page)
-    return 0;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  if (index < 0 || index >= textpage->CountChars())
-    return 0;
-
-  FPDF_CHAR_INFO charinfo;
-  textpage->GetCharInfo(index, &charinfo);
-  return charinfo.m_Unicode;
-}
-
-FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetFontSize(FPDF_TEXTPAGE text_page,
-                                                      int index) {
-  if (!text_page)
-    return 0;
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-
-  if (index < 0 || index >= textpage->CountChars())
-    return 0;
-
-  FPDF_CHAR_INFO charinfo;
-  textpage->GetCharInfo(index, &charinfo);
-  return charinfo.m_FontSize;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetCharBox(FPDF_TEXTPAGE text_page,
-                                                        int index,
-                                                        double* left,
-                                                        double* right,
-                                                        double* bottom,
-                                                        double* top) {
-  if (!text_page || index < 0)
-    return false;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  if (index >= textpage->CountChars())
-    return false;
-
-  FPDF_CHAR_INFO charinfo;
-  textpage->GetCharInfo(index, &charinfo);
-  *left = charinfo.m_CharBox.left;
-  *right = charinfo.m_CharBox.right;
-  *bottom = charinfo.m_CharBox.bottom;
-  *top = charinfo.m_CharBox.top;
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFText_GetCharOrigin(FPDF_TEXTPAGE text_page,
-                       int index,
-                       double* x,
-                       double* y) {
-  if (!text_page)
-    return false;
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-
-  if (index < 0 || index >= textpage->CountChars())
-    return false;
-  FPDF_CHAR_INFO charinfo;
-  textpage->GetCharInfo(index, &charinfo);
-  *x = charinfo.m_Origin.x;
-  *y = charinfo.m_Origin.y;
-  return true;
-}
-
-// select
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page,
-                           double x,
-                           double y,
-                           double xTolerance,
-                           double yTolerance) {
-  if (!text_page)
-    return -3;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  return textpage->GetIndexAtPos(
-      CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
-      CFX_SizeF(static_cast<float>(xTolerance),
-                static_cast<float>(yTolerance)));
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE page,
-                                               int char_start,
-                                               int char_count,
-                                               unsigned short* result) {
-  if (!page || char_start < 0 || char_count < 0 || !result)
-    return 0;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(page);
-  int char_available = textpage->CountChars() - char_start;
-  if (char_available <= 0)
-    return 0;
-
-  char_count = std::min(char_count, char_available);
-  if (char_count == 0) {
-    // Writing out "", which has a character count of 1 due to the NUL.
-    *result = '\0';
-    return 1;
-  }
-
-  WideString str = textpage->GetPageText(char_start, char_count);
-
-  if (str.GetLength() > static_cast<size_t>(char_count))
-    str = str.Left(static_cast<size_t>(char_count));
-
-  // UFT16LE_Encode doesn't handle surrogate pairs properly, so it is expected
-  // the number of items to stay the same.
-  ByteString byte_str = str.UTF16LE_Encode();
-  size_t byte_str_len = byte_str.GetLength();
-  int ret_count = byte_str_len / kBytesPerCharacter;
-
-  ASSERT(ret_count <= char_count + 1);  // +1 to account for the NUL terminator.
-  memcpy(result, byte_str.GetBuffer(byte_str_len), byte_str_len);
-  return ret_count;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page,
-                                                  int start,
-                                                  int count) {
-  if (!text_page)
-    return 0;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  return textpage->CountRects(start, count);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page,
-                                                     int rect_index,
-                                                     double* left,
-                                                     double* top,
-                                                     double* right,
-                                                     double* bottom) {
-  if (!text_page)
-    return false;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  CFX_FloatRect rect;
-  bool result = textpage->GetRect(rect_index, &rect);
-
-  *left = rect.left;
-  *top = rect.top;
-  *right = rect.right;
-  *bottom = rect.bottom;
-  return result;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetBoundedText(FPDF_TEXTPAGE text_page,
-                                                      double left,
-                                                      double top,
-                                                      double right,
-                                                      double bottom,
-                                                      unsigned short* buffer,
-                                                      int buflen) {
-  if (!text_page)
-    return 0;
-
-  CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page);
-  CFX_FloatRect rect((float)left, (float)bottom, (float)right, (float)top);
-  WideString str = textpage->GetTextByRect(rect);
-
-  if (buflen <= 0 || !buffer)
-    return str.GetLength();
-
-  ByteString cbUTF16Str = str.UTF16LE_Encode();
-  int len = cbUTF16Str.GetLength() / sizeof(unsigned short);
-  int size = buflen > len ? len : buflen;
-  memcpy(buffer, cbUTF16Str.GetBuffer(size * sizeof(unsigned short)),
-         size * sizeof(unsigned short));
-  cbUTF16Str.ReleaseBuffer(size * sizeof(unsigned short));
-
-  return size;
-}
-
-// Search
-// -1 for end
-FPDF_EXPORT FPDF_SCHHANDLE FPDF_CALLCONV
-FPDFText_FindStart(FPDF_TEXTPAGE text_page,
-                   FPDF_WIDESTRING findwhat,
-                   unsigned long flags,
-                   int start_index) {
-  if (!text_page)
-    return nullptr;
-
-  CPDF_TextPageFind* textpageFind =
-      new CPDF_TextPageFind(CPDFTextPageFromFPDFTextPage(text_page));
-  size_t len = WideString::WStringLength(findwhat);
-  textpageFind->FindFirst(
-      WideString::FromUTF16LE(findwhat, len), flags,
-      start_index >= 0 ? Optional<size_t>(start_index) : Optional<size_t>());
-  return textpageFind;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindNext(FPDF_SCHHANDLE handle) {
-  if (!handle)
-    return false;
-
-  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
-  return textpageFind->FindNext();
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_FindPrev(FPDF_SCHHANDLE handle) {
-  if (!handle)
-    return false;
-
-  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
-  return textpageFind->FindPrev();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDFText_GetSchResultIndex(FPDF_SCHHANDLE handle) {
-  if (!handle)
-    return 0;
-
-  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
-  return textpageFind->GetCurOrder();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetSchCount(FPDF_SCHHANDLE handle) {
-  if (!handle)
-    return 0;
-
-  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
-  return textpageFind->GetMatchedCount();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFText_FindClose(FPDF_SCHHANDLE handle) {
-  if (!handle)
-    return;
-
-  CPDF_TextPageFind* textpageFind = CPDFTextPageFindFromFPDFSchHandle(handle);
-  delete textpageFind;
-  handle = nullptr;
-}
-
-// web link
-FPDF_EXPORT FPDF_PAGELINK FPDF_CALLCONV
-FPDFLink_LoadWebLinks(FPDF_TEXTPAGE text_page) {
-  if (!text_page)
-    return nullptr;
-
-  CPDF_LinkExtract* pageLink =
-      new CPDF_LinkExtract(CPDFTextPageFromFPDFTextPage(text_page));
-  pageLink->ExtractLinks();
-  return pageLink;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountWebLinks(FPDF_PAGELINK link_page) {
-  if (!link_page)
-    return 0;
-
-  CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
-  return pdfium::base::checked_cast<int>(pageLink->CountLinks());
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetURL(FPDF_PAGELINK link_page,
-                                              int link_index,
-                                              unsigned short* buffer,
-                                              int buflen) {
-  WideString wsUrl(L"");
-  if (link_page && link_index >= 0) {
-    CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
-    wsUrl = pageLink->GetURL(link_index);
-  }
-  ByteString cbUTF16URL = wsUrl.UTF16LE_Encode();
-  int required = cbUTF16URL.GetLength() / sizeof(unsigned short);
-  if (!buffer || buflen <= 0)
-    return required;
-
-  int size = std::min(required, buflen);
-  if (size > 0) {
-    int buf_size = size * sizeof(unsigned short);
-    memcpy(buffer, cbUTF16URL.GetBuffer(buf_size), buf_size);
-  }
-  return size;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountRects(FPDF_PAGELINK link_page,
-                                                  int link_index) {
-  if (!link_page || link_index < 0)
-    return 0;
-
-  CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
-  return pdfium::CollectionSize<int>(pageLink->GetRects(link_index));
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetRect(FPDF_PAGELINK link_page,
-                                                     int link_index,
-                                                     int rect_index,
-                                                     double* left,
-                                                     double* top,
-                                                     double* right,
-                                                     double* bottom) {
-  if (!link_page || link_index < 0 || rect_index < 0)
-    return false;
-
-  CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page);
-  std::vector<CFX_FloatRect> rectArray = pageLink->GetRects(link_index);
-  if (rect_index >= pdfium::CollectionSize<int>(rectArray))
-    return false;
-
-  *left = rectArray[rect_index].left;
-  *right = rectArray[rect_index].right;
-  *top = rectArray[rect_index].top;
-  *bottom = rectArray[rect_index].bottom;
-  return true;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFLink_CloseWebLinks(FPDF_PAGELINK link_page) {
-  delete CPDFLinkExtractFromFPDFPageLink(link_page);
-}
diff --git a/fpdfsdk/fpdftext_embeddertest.cpp b/fpdfsdk/fpdftext_embeddertest.cpp
deleted file mode 100644
index 86d32a7..0000000
--- a/fpdfsdk/fpdftext_embeddertest.cpp
+++ /dev/null
@@ -1,643 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "core/fxcrt/fx_memory.h"
-#include "public/fpdf_text.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-namespace {
-
-bool check_unsigned_shorts(const char* expected,
-                           const unsigned short* actual,
-                           size_t length) {
-  if (length > strlen(expected) + 1)
-    return false;
-
-  for (size_t i = 0; i < length; ++i) {
-    if (actual[i] != static_cast<unsigned short>(expected[i]))
-      return false;
-  }
-  return true;
-}
-
-}  // namespace
-
-class FPDFTextEmbeddertest : public EmbedderTest {};
-
-TEST_F(FPDFTextEmbeddertest, Text) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  static const char expected[] = "Hello, world!\r\nGoodbye, world!";
-  unsigned short fixed_buffer[128];
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-
-  // Check that edge cases are handled gracefully
-  EXPECT_EQ(0, FPDFText_GetText(textpage, 0, 128, nullptr));
-  EXPECT_EQ(0, FPDFText_GetText(textpage, -1, 128, fixed_buffer));
-  EXPECT_EQ(0, FPDFText_GetText(textpage, 0, -1, fixed_buffer));
-  EXPECT_EQ(1, FPDFText_GetText(textpage, 0, 0, fixed_buffer));
-  EXPECT_EQ(0, fixed_buffer[0]);
-
-  // Keep going and check the next case.
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(2, FPDFText_GetText(textpage, 0, 1, fixed_buffer));
-  EXPECT_EQ(expected[0], fixed_buffer[0]);
-  EXPECT_EQ(0, fixed_buffer[1]);
-
-  // Check includes the terminating NUL that is provided.
-  int num_chars = FPDFText_GetText(textpage, 0, 128, fixed_buffer);
-  ASSERT_GE(num_chars, 0);
-  EXPECT_EQ(sizeof(expected), static_cast<size_t>(num_chars));
-  EXPECT_TRUE(check_unsigned_shorts(expected, fixed_buffer, sizeof(expected)));
-
-  // Count does not include the terminating NUL in the string literal.
-  EXPECT_EQ(sizeof(expected) - 1,
-            static_cast<size_t>(FPDFText_CountChars(textpage)));
-  for (size_t i = 0; i < sizeof(expected) - 1; ++i) {
-    EXPECT_EQ(static_cast<unsigned int>(expected[i]),
-              FPDFText_GetUnicode(textpage, i))
-        << " at " << i;
-  }
-
-  // Extracting using a buffer that will be completely filled. Small buffer is
-  // 12 elements long, since it will need 2 locations per displayed character in
-  // the expected string, plus 2 more for the terminating character.
-  static const char small_expected[] = "Hello";
-  unsigned short small_buffer[12];
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(6, FPDFText_GetText(textpage, 0, 5, small_buffer));
-  EXPECT_TRUE(check_unsigned_shorts(small_expected, small_buffer,
-                                    sizeof(small_expected)));
-
-  EXPECT_EQ(12.0, FPDFText_GetFontSize(textpage, 0));
-  EXPECT_EQ(16.0, FPDFText_GetFontSize(textpage, 15));
-
-  double left = 0.0;
-  double right = 0.0;
-  double bottom = 0.0;
-  double top = 0.0;
-  EXPECT_FALSE(FPDFText_GetCharBox(nullptr, 4, &left, &right, &bottom, &top));
-  EXPECT_DOUBLE_EQ(0.0, left);
-  EXPECT_DOUBLE_EQ(0.0, right);
-  EXPECT_DOUBLE_EQ(0.0, bottom);
-  EXPECT_DOUBLE_EQ(0.0, top);
-  EXPECT_FALSE(FPDFText_GetCharBox(textpage, -1, &left, &right, &bottom, &top));
-  EXPECT_DOUBLE_EQ(0.0, left);
-  EXPECT_DOUBLE_EQ(0.0, right);
-  EXPECT_DOUBLE_EQ(0.0, bottom);
-  EXPECT_DOUBLE_EQ(0.0, top);
-  EXPECT_FALSE(FPDFText_GetCharBox(textpage, 55, &left, &right, &bottom, &top));
-  EXPECT_DOUBLE_EQ(0.0, left);
-  EXPECT_DOUBLE_EQ(0.0, right);
-  EXPECT_DOUBLE_EQ(0.0, bottom);
-  EXPECT_DOUBLE_EQ(0.0, top);
-
-  EXPECT_TRUE(FPDFText_GetCharBox(textpage, 4, &left, &right, &bottom, &top));
-  EXPECT_NEAR(41.071, left, 0.001);
-  EXPECT_NEAR(46.243, right, 0.001);
-  EXPECT_NEAR(49.844, bottom, 0.001);
-  EXPECT_NEAR(55.520, top, 0.001);
-
-  double x = 0.0;
-  double y = 0.0;
-  EXPECT_TRUE(FPDFText_GetCharOrigin(textpage, 4, &x, &y));
-  EXPECT_NEAR(40.664, x, 0.001);
-  EXPECT_NEAR(50.000, y, 0.001);
-
-  EXPECT_EQ(4, FPDFText_GetCharIndexAtPos(textpage, 42.0, 50.0, 1.0, 1.0));
-  EXPECT_EQ(-1, FPDFText_GetCharIndexAtPos(textpage, 0.0, 0.0, 1.0, 1.0));
-  EXPECT_EQ(-1, FPDFText_GetCharIndexAtPos(textpage, 199.0, 199.0, 1.0, 1.0));
-
-  // Test out of range indicies.
-  EXPECT_EQ(-1,
-            FPDFText_GetCharIndexAtPos(textpage, 42.0, 10000000.0, 1.0, 1.0));
-  EXPECT_EQ(-1, FPDFText_GetCharIndexAtPos(textpage, -1.0, 50.0, 1.0, 1.0));
-
-  // Count does not include the terminating NUL in the string literal.
-  EXPECT_EQ(2, FPDFText_CountRects(textpage, 0, sizeof(expected) - 1));
-
-  left = 0.0;
-  right = 0.0;
-  bottom = 0.0;
-  top = 0.0;
-  EXPECT_TRUE(FPDFText_GetRect(textpage, 1, &left, &top, &right, &bottom));
-  EXPECT_NEAR(20.847, left, 0.001);
-  EXPECT_NEAR(135.167, right, 0.001);
-  EXPECT_NEAR(96.655, bottom, 0.001);
-  EXPECT_NEAR(116.000, top, 0.001);
-
-  // Test out of range indicies set outputs to (0.0, 0.0, 0.0, 0.0).
-  left = -1.0;
-  right = -1.0;
-  bottom = -1.0;
-  top = -1.0;
-  EXPECT_FALSE(FPDFText_GetRect(textpage, -1, &left, &top, &right, &bottom));
-  EXPECT_EQ(0.0, left);
-  EXPECT_EQ(0.0, right);
-  EXPECT_EQ(0.0, bottom);
-  EXPECT_EQ(0.0, top);
-
-  left = -2.0;
-  right = -2.0;
-  bottom = -2.0;
-  top = -2.0;
-  EXPECT_FALSE(FPDFText_GetRect(textpage, 2, &left, &top, &right, &bottom));
-  EXPECT_EQ(0.0, left);
-  EXPECT_EQ(0.0, right);
-  EXPECT_EQ(0.0, bottom);
-  EXPECT_EQ(0.0, top);
-
-  EXPECT_EQ(9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, 0, 0));
-
-  // Extract starting at character 4 as above.
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(1, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0,
-                                       fixed_buffer, 1));
-  EXPECT_TRUE(check_unsigned_shorts(expected + 4, fixed_buffer, 1));
-  EXPECT_EQ(0xbdbd, fixed_buffer[1]);
-
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0,
-                                       fixed_buffer, 9));
-  EXPECT_TRUE(check_unsigned_shorts(expected + 4, fixed_buffer, 9));
-  EXPECT_EQ(0xbdbd, fixed_buffer[9]);
-
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(10, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0,
-                                        fixed_buffer, 128));
-  EXPECT_TRUE(check_unsigned_shorts(expected + 4, fixed_buffer, 9));
-  EXPECT_EQ(0u, fixed_buffer[9]);
-  EXPECT_EQ(0xbdbd, fixed_buffer[10]);
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, TextSearch) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> nope =
-      GetFPDFWideString(L"nope");
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> world =
-      GetFPDFWideString(L"world");
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> world_caps =
-      GetFPDFWideString(L"WORLD");
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> world_substr =
-      GetFPDFWideString(L"orld");
-
-  // No occurences of "nope" in test page.
-  FPDF_SCHHANDLE search = FPDFText_FindStart(textpage, nope.get(), 0, 0);
-  EXPECT_TRUE(search);
-  EXPECT_EQ(0, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(0, FPDFText_GetSchCount(search));
-
-  // Advancing finds nothing.
-  EXPECT_FALSE(FPDFText_FindNext(search));
-  EXPECT_EQ(0, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(0, FPDFText_GetSchCount(search));
-
-  // Retreating finds nothing.
-  EXPECT_FALSE(FPDFText_FindPrev(search));
-  EXPECT_EQ(0, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(0, FPDFText_GetSchCount(search));
-  FPDFText_FindClose(search);
-
-  // Two occurences of "world" in test page.
-  search = FPDFText_FindStart(textpage, world.get(), 0, 2);
-  EXPECT_TRUE(search);
-
-  // Remains not found until advanced.
-  EXPECT_EQ(0, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(0, FPDFText_GetSchCount(search));
-
-  // First occurence of "world" in this test page.
-  EXPECT_TRUE(FPDFText_FindNext(search));
-  EXPECT_EQ(7, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(5, FPDFText_GetSchCount(search));
-
-  // Last occurence of "world" in this test page.
-  EXPECT_TRUE(FPDFText_FindNext(search));
-  EXPECT_EQ(24, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(5, FPDFText_GetSchCount(search));
-
-  // Found position unchanged when fails to advance.
-  EXPECT_FALSE(FPDFText_FindNext(search));
-  EXPECT_EQ(24, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(5, FPDFText_GetSchCount(search));
-
-  // Back to first occurence.
-  EXPECT_TRUE(FPDFText_FindPrev(search));
-  EXPECT_EQ(7, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(5, FPDFText_GetSchCount(search));
-
-  // Found position unchanged when fails to retreat.
-  EXPECT_FALSE(FPDFText_FindPrev(search));
-  EXPECT_EQ(7, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(5, FPDFText_GetSchCount(search));
-  FPDFText_FindClose(search);
-
-  // Exact search unaffected by case sensitiity and whole word flags.
-  search = FPDFText_FindStart(textpage, world.get(),
-                              FPDF_MATCHCASE | FPDF_MATCHWHOLEWORD, 0);
-  EXPECT_TRUE(search);
-  EXPECT_TRUE(FPDFText_FindNext(search));
-  EXPECT_EQ(7, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(5, FPDFText_GetSchCount(search));
-  FPDFText_FindClose(search);
-
-  // Default is case-insensitive, so matching agaist caps works.
-  search = FPDFText_FindStart(textpage, world_caps.get(), 0, 0);
-  EXPECT_TRUE(search);
-  EXPECT_TRUE(FPDFText_FindNext(search));
-  EXPECT_EQ(7, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(5, FPDFText_GetSchCount(search));
-  FPDFText_FindClose(search);
-
-  // But can be made case sensitive, in which case this fails.
-  search = FPDFText_FindStart(textpage, world_caps.get(), FPDF_MATCHCASE, 0);
-  EXPECT_FALSE(FPDFText_FindNext(search));
-  EXPECT_EQ(0, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(0, FPDFText_GetSchCount(search));
-  FPDFText_FindClose(search);
-
-  // Default is match anywhere within word, so matching substirng works.
-  search = FPDFText_FindStart(textpage, world_substr.get(), 0, 0);
-  EXPECT_TRUE(FPDFText_FindNext(search));
-  EXPECT_EQ(8, FPDFText_GetSchResultIndex(search));
-  EXPECT_EQ(4, FPDFText_GetSchCount(search));
-  FPDFText_FindClose(search);
-
-  // But can be made to mach word boundaries, in which case this fails.
-  search =
-      FPDFText_FindStart(textpage, world_substr.get(), FPDF_MATCHWHOLEWORD, 0);
-  EXPECT_FALSE(FPDFText_FindNext(search));
-  // TODO(tsepez): investigate strange index/count values in this state.
-  FPDFText_FindClose(search);
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-// Test that the page has characters despite a bad stream length.
-TEST_F(FPDFTextEmbeddertest, StreamLengthPastEndOfFile) {
-  EXPECT_TRUE(OpenDocument("bug_57.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-  EXPECT_EQ(13, FPDFText_CountChars(textpage));
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, WebLinks) {
-  EXPECT_TRUE(OpenDocument("weblinks.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  FPDF_PAGELINK pagelink = FPDFLink_LoadWebLinks(textpage);
-  EXPECT_TRUE(pagelink);
-
-  // Page contains two HTTP-style URLs.
-  EXPECT_EQ(2, FPDFLink_CountWebLinks(pagelink));
-
-  // Only a terminating NUL required for bogus links.
-  EXPECT_EQ(1, FPDFLink_GetURL(pagelink, 2, nullptr, 0));
-  EXPECT_EQ(1, FPDFLink_GetURL(pagelink, 1400, nullptr, 0));
-  EXPECT_EQ(1, FPDFLink_GetURL(pagelink, -1, nullptr, 0));
-
-  // Query the number of characters required for each link (incl NUL).
-  EXPECT_EQ(25, FPDFLink_GetURL(pagelink, 0, nullptr, 0));
-  EXPECT_EQ(26, FPDFLink_GetURL(pagelink, 1, nullptr, 0));
-
-  static const char expected_url[] = "http://example.com?q=foo";
-  static const size_t expected_len = sizeof(expected_url);
-  unsigned short fixed_buffer[128];
-
-  // Retrieve a link with too small a buffer.  Buffer will not be
-  // NUL-terminated, but must not be modified past indicated length,
-  // so pre-fill with a pattern to check write bounds.
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(1, FPDFLink_GetURL(pagelink, 0, fixed_buffer, 1));
-  EXPECT_TRUE(check_unsigned_shorts(expected_url, fixed_buffer, 1));
-  EXPECT_EQ(0xbdbd, fixed_buffer[1]);
-
-  // Check buffer that doesn't have space for a terminating NUL.
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(static_cast<int>(expected_len - 1),
-            FPDFLink_GetURL(pagelink, 0, fixed_buffer, expected_len - 1));
-  EXPECT_TRUE(
-      check_unsigned_shorts(expected_url, fixed_buffer, expected_len - 1));
-  EXPECT_EQ(0xbdbd, fixed_buffer[expected_len - 1]);
-
-  // Retreive link with exactly-sized buffer.
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(static_cast<int>(expected_len),
-            FPDFLink_GetURL(pagelink, 0, fixed_buffer, expected_len));
-  EXPECT_TRUE(check_unsigned_shorts(expected_url, fixed_buffer, expected_len));
-  EXPECT_EQ(0u, fixed_buffer[expected_len - 1]);
-  EXPECT_EQ(0xbdbd, fixed_buffer[expected_len]);
-
-  // Retreive link with ample-sized-buffer.
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  EXPECT_EQ(static_cast<int>(expected_len),
-            FPDFLink_GetURL(pagelink, 0, fixed_buffer, 128));
-  EXPECT_TRUE(check_unsigned_shorts(expected_url, fixed_buffer, expected_len));
-  EXPECT_EQ(0u, fixed_buffer[expected_len - 1]);
-  EXPECT_EQ(0xbdbd, fixed_buffer[expected_len]);
-
-  // Each link rendered in a single rect in this test page.
-  EXPECT_EQ(1, FPDFLink_CountRects(pagelink, 0));
-  EXPECT_EQ(1, FPDFLink_CountRects(pagelink, 1));
-
-  // Each link rendered in a single rect in this test page.
-  EXPECT_EQ(0, FPDFLink_CountRects(pagelink, -1));
-  EXPECT_EQ(0, FPDFLink_CountRects(pagelink, 2));
-  EXPECT_EQ(0, FPDFLink_CountRects(pagelink, 10000));
-
-  // Check boundary of valid link index with valid rect index.
-  double left = 0.0;
-  double right = 0.0;
-  double top = 0.0;
-  double bottom = 0.0;
-  EXPECT_TRUE(FPDFLink_GetRect(pagelink, 0, 0, &left, &top, &right, &bottom));
-  EXPECT_NEAR(50.791, left, 0.001);
-  EXPECT_NEAR(187.963, right, 0.001);
-  EXPECT_NEAR(97.624, bottom, 0.001);
-  EXPECT_NEAR(108.736, top, 0.001);
-
-  // Check that valid link with invalid rect index leaves parameters unchanged.
-  left = -1.0;
-  right = -1.0;
-  top = -1.0;
-  bottom = -1.0;
-  EXPECT_FALSE(FPDFLink_GetRect(pagelink, 0, 1, &left, &top, &right, &bottom));
-  EXPECT_EQ(-1.0, left);
-  EXPECT_EQ(-1.0, right);
-  EXPECT_EQ(-1.0, bottom);
-  EXPECT_EQ(-1.0, top);
-
-  // Check that invalid link index leaves parameters unchanged.
-  left = -2.0;
-  right = -2.0;
-  top = -2.0;
-  bottom = -2.0;
-  EXPECT_FALSE(FPDFLink_GetRect(pagelink, -1, 0, &left, &top, &right, &bottom));
-  EXPECT_EQ(-2.0, left);
-  EXPECT_EQ(-2.0, right);
-  EXPECT_EQ(-2.0, bottom);
-  EXPECT_EQ(-2.0, top);
-
-  FPDFLink_CloseWebLinks(pagelink);
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, WebLinksAcrossLines) {
-  EXPECT_TRUE(OpenDocument("weblinks_across_lines.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  FPDF_PAGELINK pagelink = FPDFLink_LoadWebLinks(textpage);
-  EXPECT_TRUE(pagelink);
-
-  static const char* const kExpectedUrls[] = {
-      "http://example.com",           // from "http://www.example.com?\r\nfoo"
-      "http://example.com/",          // from "http://www.example.com/\r\nfoo"
-      "http://example.com/test-foo",  // from "http://example.com/test-\r\nfoo"
-      "http://abc.com/test-foo",      // from "http://abc.com/test-\r\n\r\nfoo"
-      // Next two links from "http://www.example.com/\r\nhttp://www.abc.com/"
-      "http://example.com/", "http://www.abc.com",
-  };
-  static const int kNumLinks = static_cast<int>(FX_ArraySize(kExpectedUrls));
-
-  EXPECT_EQ(kNumLinks, FPDFLink_CountWebLinks(pagelink));
-
-  unsigned short fixed_buffer[128];
-  for (int i = 0; i < kNumLinks; i++) {
-    const size_t expected_len = strlen(kExpectedUrls[i]) + 1;
-    memset(fixed_buffer, 0, FX_ArraySize(fixed_buffer));
-    EXPECT_EQ(static_cast<int>(expected_len),
-              FPDFLink_GetURL(pagelink, i, nullptr, 0));
-    EXPECT_EQ(
-        static_cast<int>(expected_len),
-        FPDFLink_GetURL(pagelink, i, fixed_buffer, FX_ArraySize(fixed_buffer)));
-    EXPECT_TRUE(
-        check_unsigned_shorts(kExpectedUrls[i], fixed_buffer, expected_len));
-  }
-
-  FPDFLink_CloseWebLinks(pagelink);
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, WebLinksAcrossLinesBug) {
-  EXPECT_TRUE(OpenDocument("bug_650.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  FPDF_PAGELINK pagelink = FPDFLink_LoadWebLinks(textpage);
-  EXPECT_TRUE(pagelink);
-
-  EXPECT_EQ(2, FPDFLink_CountWebLinks(pagelink));
-  unsigned short fixed_buffer[128] = {0};
-  static const char kExpectedUrl[] =
-      "http://tutorial45.com/learn-autocad-basics-day-166/";
-  static const int kUrlSize = static_cast<int>(sizeof(kExpectedUrl));
-
-  EXPECT_EQ(kUrlSize, FPDFLink_GetURL(pagelink, 1, nullptr, 0));
-  EXPECT_EQ(kUrlSize, FPDFLink_GetURL(pagelink, 1, fixed_buffer,
-                                      FX_ArraySize(fixed_buffer)));
-  EXPECT_TRUE(check_unsigned_shorts(kExpectedUrl, fixed_buffer, kUrlSize));
-
-  FPDFLink_CloseWebLinks(pagelink);
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, GetFontSize) {
-  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  const double kExpectedFontsSizes[] = {12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
-                                        12, 12, 12, 1,  1,  16, 16, 16, 16, 16,
-                                        16, 16, 16, 16, 16, 16, 16, 16, 16, 16};
-
-  int count = FPDFText_CountChars(textpage);
-  ASSERT_EQ(FX_ArraySize(kExpectedFontsSizes), static_cast<size_t>(count));
-  for (int i = 0; i < count; ++i)
-    EXPECT_EQ(kExpectedFontsSizes[i], FPDFText_GetFontSize(textpage, i)) << i;
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, ToUnicode) {
-  EXPECT_TRUE(OpenDocument("bug_583.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  ASSERT_EQ(1, FPDFText_CountChars(textpage));
-  EXPECT_EQ(static_cast<unsigned int>(0), FPDFText_GetUnicode(textpage, 0));
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, Bug_921) {
-  EXPECT_TRUE(OpenDocument("bug_921.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  static constexpr unsigned int kData[] = {
-      1095, 1077, 1083, 1086, 1074, 1077, 1095, 1077, 1089, 1082, 1086, 1077,
-      32,   1089, 1090, 1088, 1072, 1076, 1072, 1085, 1080, 1077, 46,   32};
-  static constexpr int kStartIndex = 238;
-
-  ASSERT_EQ(268, FPDFText_CountChars(textpage));
-  for (size_t i = 0; i < FX_ArraySize(kData); ++i)
-    EXPECT_EQ(kData[i], FPDFText_GetUnicode(textpage, kStartIndex + i));
-
-  unsigned short buffer[FX_ArraySize(kData) + 1];
-  memset(buffer, 0xbd, sizeof(buffer));
-  int count =
-      FPDFText_GetText(textpage, kStartIndex, FX_ArraySize(kData), buffer);
-  ASSERT_GT(count, 0);
-  ASSERT_EQ(FX_ArraySize(kData) + 1, static_cast<size_t>(count));
-  for (size_t i = 0; i < FX_ArraySize(kData); ++i)
-    EXPECT_EQ(kData[i], buffer[i]);
-  EXPECT_EQ(0, buffer[FX_ArraySize(kData)]);
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, GetTextWithHyphen) {
-  EXPECT_TRUE(OpenDocument("bug_781804.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  // Check that soft hyphens are not included
-  // Expecting 'Veritaserum', except there is a \uFFFE where the hyphen was in
-  // the original text. This is a weird thing that Adobe does, which we
-  // replicate.
-  constexpr unsigned short soft_expected[] = {
-      0x0056, 0x0065, 0x0072, 0x0069, 0x0074, 0x0061, 0xfffe,
-      0x0073, 0x0065, 0x0072, 0x0075, 0x006D, 0x0000};
-  {
-    constexpr int count = FX_ArraySize(soft_expected) - 1;
-    unsigned short buffer[FX_ArraySize(soft_expected)];
-    memset(buffer, 0, sizeof(buffer));
-
-    EXPECT_EQ(count + 1, FPDFText_GetText(textpage, 0, count, buffer));
-    for (int i = 0; i < count; i++)
-      EXPECT_EQ(soft_expected[i], buffer[i]);
-  }
-
-  // Check that hard hyphens are included
-  {
-    // There isn't the \0 in the actual doc, but there is a \r\n, so need to
-    // add 1 to get aligned.
-    constexpr size_t offset = FX_ArraySize(soft_expected) + 1;
-    // Expecting 'User-\r\ngenerated', the - is a unicode character, so cannnot
-    // store in a char[].
-    constexpr unsigned short hard_expected[] = {
-        0x0055, 0x0073, 0x0065, 0x0072, 0x2010, 0x000d, 0x000a, 0x0067, 0x0065,
-        0x006e, 0x0065, 0x0072, 0x0061, 0x0074, 0x0065, 0x0064, 0x0000};
-    constexpr int count = FX_ArraySize(hard_expected) - 1;
-    unsigned short buffer[FX_ArraySize(hard_expected)];
-
-    EXPECT_EQ(count + 1, FPDFText_GetText(textpage, offset, count, buffer));
-    for (int i = 0; i < count; i++)
-      EXPECT_EQ(hard_expected[i], buffer[i]);
-  }
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, bug_782596) {
-  // If there is a regression in this test, it will only fail under ASAN
-  EXPECT_TRUE(OpenDocument("bug_782596.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFTextEmbeddertest, ControlCharacters) {
-  EXPECT_TRUE(OpenDocument("control_characters.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_TRUE(page);
-
-  FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
-  EXPECT_TRUE(textpage);
-
-  // Should not include the control characters in the output
-  static const char expected[] = "Hello, world!\r\nGoodbye, world!";
-  unsigned short fixed_buffer[128];
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  int num_chars = FPDFText_GetText(textpage, 0, 128, fixed_buffer);
-
-  ASSERT_GE(num_chars, 0);
-  EXPECT_EQ(sizeof(expected), static_cast<size_t>(num_chars));
-  EXPECT_TRUE(check_unsigned_shorts(expected, fixed_buffer, sizeof(expected)));
-
-  // Attempting to get a chunk of text after the control characters
-  static const char expected_substring[] = "Goodbye, world!";
-  // Offset is the length of 'Hello, world!\r\n' + 2 control characters in the
-  // original stream
-  static const int offset = 17;
-  memset(fixed_buffer, 0xbd, sizeof(fixed_buffer));
-  num_chars = FPDFText_GetText(textpage, offset, 128, fixed_buffer);
-
-  ASSERT_GE(num_chars, 0);
-  EXPECT_EQ(sizeof(expected_substring), static_cast<size_t>(num_chars));
-  EXPECT_TRUE(check_unsigned_shorts(expected_substring, fixed_buffer,
-                                    sizeof(expected_substring)));
-
-  FPDFText_ClosePage(textpage);
-  UnloadPage(page);
-}
diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp
deleted file mode 100644
index 97fc02a..0000000
--- a/fpdfsdk/fpdfview.cpp
+++ /dev/null
@@ -1,1531 +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 "public/fpdfview.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/cpdf_pagerendercontext.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_pageobject.h"
-#include "core/fpdfapi/page/cpdf_pathobject.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/fpdf_parser_decode.h"
-#include "core/fpdfapi/render/cpdf_progressiverenderer.h"
-#include "core/fpdfapi/render/cpdf_renderoptions.h"
-#include "core/fpdfdoc/cpdf_annotlist.h"
-#include "core/fpdfdoc/cpdf_nametree.h"
-#include "core/fpdfdoc/cpdf_occontext.h"
-#include "core/fpdfdoc/cpdf_viewerpreferences.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fpdfsdk/fsdk_pauseadapter.h"
-#include "fxjs/ijs_runtime.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdf_ext.h"
-#include "public/fpdf_formfill.h"
-#include "public/fpdf_progressive.h"
-#include "third_party/base/allocator/partition_allocator/partition_alloc.h"
-#include "third_party/base/numerics/safe_conversions_impl.h"
-#include "third_party/base/ptr_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
-#include "fxbarcode/BC_Library.h"
-#endif  // PDF_ENABLE_XFA
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#include "core/fxge/cfx_windowsrenderdevice.h"
-
-// These checks are here because core/ and public/ cannot depend on each other.
-static_assert(WindowsPrintMode::kModeEmf == FPDF_PRINTMODE_EMF,
-              "WindowsPrintMode::kModeEmf value mismatch");
-static_assert(WindowsPrintMode::kModeTextOnly == FPDF_PRINTMODE_TEXTONLY,
-              "WindowsPrintMode::kModeTextOnly value mismatch");
-static_assert(WindowsPrintMode::kModePostScript2 == FPDF_PRINTMODE_POSTSCRIPT2,
-              "WindowsPrintMode::kModePostScript2 value mismatch");
-static_assert(WindowsPrintMode::kModePostScript3 == FPDF_PRINTMODE_POSTSCRIPT3,
-              "WindowsPrintMode::kModePostScript3 value mismatch");
-#endif
-
-namespace {
-
-bool g_bLibraryInitialized = false;
-
-void RenderPageImpl(CPDF_PageRenderContext* pContext,
-                    CPDF_Page* pPage,
-                    const CFX_Matrix& matrix,
-                    const FX_RECT& clipping_rect,
-                    int flags,
-                    bool bNeedToRestore,
-                    IFSDK_PAUSE_Adapter* pause) {
-  if (!pContext->m_pOptions)
-    pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
-
-  uint32_t option_flags = pContext->m_pOptions->GetFlags();
-  if (flags & FPDF_LCD_TEXT)
-    option_flags |= RENDER_CLEARTYPE;
-  else
-    option_flags &= ~RENDER_CLEARTYPE;
-
-  if (flags & FPDF_NO_NATIVETEXT)
-    option_flags |= RENDER_NO_NATIVETEXT;
-  if (flags & FPDF_RENDER_LIMITEDIMAGECACHE)
-    option_flags |= RENDER_LIMITEDIMAGECACHE;
-  if (flags & FPDF_RENDER_FORCEHALFTONE)
-    option_flags |= RENDER_FORCE_HALFTONE;
-#ifndef PDF_ENABLE_XFA
-  if (flags & FPDF_RENDER_NO_SMOOTHTEXT)
-    option_flags |= RENDER_NOTEXTSMOOTH;
-  if (flags & FPDF_RENDER_NO_SMOOTHIMAGE)
-    option_flags |= RENDER_NOIMAGESMOOTH;
-  if (flags & FPDF_RENDER_NO_SMOOTHPATH)
-    option_flags |= RENDER_NOPATHSMOOTH;
-#endif  // PDF_ENABLE_XFA
-  pContext->m_pOptions->SetFlags(option_flags);
-
-  // Grayscale output
-  if (flags & FPDF_GRAYSCALE)
-    pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kGray);
-
-  const CPDF_OCContext::UsageType usage =
-      (flags & FPDF_PRINTING) ? CPDF_OCContext::Print : CPDF_OCContext::View;
-  pContext->m_pOptions->SetOCContext(
-      pdfium::MakeRetain<CPDF_OCContext>(pPage->m_pDocument.Get(), usage));
-
-  pContext->m_pDevice->SaveState();
-  pContext->m_pDevice->SetClip_Rect(clipping_rect);
-  pContext->m_pContext = pdfium::MakeUnique<CPDF_RenderContext>(pPage);
-  pContext->m_pContext->AppendLayer(pPage, &matrix);
-
-  if (flags & FPDF_ANNOT) {
-    pContext->m_pAnnots = pdfium::MakeUnique<CPDF_AnnotList>(pPage);
-    bool bPrinting = pContext->m_pDevice->GetDeviceClass() != FXDC_DISPLAY;
-    pContext->m_pAnnots->DisplayAnnots(pPage, pContext->m_pContext.get(),
-                                       bPrinting, &matrix, false, nullptr);
-  }
-
-  pContext->m_pRenderer = pdfium::MakeUnique<CPDF_ProgressiveRenderer>(
-      pContext->m_pContext.get(), pContext->m_pDevice.get(),
-      pContext->m_pOptions.get());
-  pContext->m_pRenderer->Start(pause);
-  if (bNeedToRestore)
-    pContext->m_pDevice->RestoreState(false);
-}
-
-class CPDF_CustomAccess final : public IFX_SeekableReadStream {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  // IFX_SeekableReadStream
-  FX_FILESIZE GetSize() override;
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override;
-
- private:
-  explicit CPDF_CustomAccess(FPDF_FILEACCESS* pFileAccess);
-
-  FPDF_FILEACCESS m_FileAccess;
-};
-
-CPDF_CustomAccess::CPDF_CustomAccess(FPDF_FILEACCESS* pFileAccess)
-    : m_FileAccess(*pFileAccess) {}
-
-FX_FILESIZE CPDF_CustomAccess::GetSize() {
-  return m_FileAccess.m_FileLen;
-}
-
-bool CPDF_CustomAccess::ReadBlock(void* buffer,
-                                  FX_FILESIZE offset,
-                                  size_t size) {
-  if (offset < 0)
-    return false;
-
-  FX_SAFE_FILESIZE newPos = pdfium::base::checked_cast<FX_FILESIZE>(size);
-  newPos += offset;
-  if (!newPos.IsValid() ||
-      newPos.ValueOrDie() > static_cast<FX_FILESIZE>(m_FileAccess.m_FileLen)) {
-    return false;
-  }
-  return !!m_FileAccess.m_GetBlock(m_FileAccess.m_Param, offset,
-                                   static_cast<uint8_t*>(buffer), size);
-}
-
-#ifdef PDF_ENABLE_XFA
-class FPDF_FileHandlerContext : public IFX_SeekableStream {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  ~FPDF_FileHandlerContext() override;
-
-  // IFX_SeekableStream:
-  FX_FILESIZE GetSize() override;
-  bool IsEOF() override;
-  FX_FILESIZE GetPosition() override;
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override;
-  size_t ReadBlock(void* buffer, size_t size) override;
-  bool WriteBlock(const void* buffer, FX_FILESIZE offset, size_t size) override;
-  bool Flush() override;
-
-  void SetPosition(FX_FILESIZE pos) { m_nCurPos = pos; }
-
- protected:
-  explicit FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS);
-
-  FPDF_FILEHANDLER* m_pFS;
-  FX_FILESIZE m_nCurPos;
-};
-
-FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS) {
-  m_pFS = pFS;
-  m_nCurPos = 0;
-}
-
-FPDF_FileHandlerContext::~FPDF_FileHandlerContext() {
-  if (m_pFS && m_pFS->Release)
-    m_pFS->Release(m_pFS->clientData);
-}
-
-FX_FILESIZE FPDF_FileHandlerContext::GetSize() {
-  if (m_pFS && m_pFS->GetSize)
-    return (FX_FILESIZE)m_pFS->GetSize(m_pFS->clientData);
-  return 0;
-}
-
-bool FPDF_FileHandlerContext::IsEOF() {
-  return m_nCurPos >= GetSize();
-}
-
-FX_FILESIZE FPDF_FileHandlerContext::GetPosition() {
-  return m_nCurPos;
-}
-
-bool FPDF_FileHandlerContext::ReadBlock(void* buffer,
-                                        FX_FILESIZE offset,
-                                        size_t size) {
-  if (!buffer || !size || !m_pFS->ReadBlock)
-    return false;
-
-  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
-                       (FPDF_DWORD)size) == 0) {
-    m_nCurPos = offset + size;
-    return true;
-  }
-  return false;
-}
-
-size_t FPDF_FileHandlerContext::ReadBlock(void* buffer, size_t size) {
-  if (!buffer || !size || !m_pFS->ReadBlock)
-    return 0;
-
-  FX_FILESIZE nSize = GetSize();
-  if (m_nCurPos >= nSize)
-    return 0;
-  FX_FILESIZE dwAvail = nSize - m_nCurPos;
-  if (dwAvail < (FX_FILESIZE)size)
-    size = static_cast<size_t>(dwAvail);
-  if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)m_nCurPos, buffer,
-                       (FPDF_DWORD)size) == 0) {
-    m_nCurPos += size;
-    return size;
-  }
-
-  return 0;
-}
-
-bool FPDF_FileHandlerContext::WriteBlock(const void* buffer,
-                                         FX_FILESIZE offset,
-                                         size_t size) {
-  if (!m_pFS || !m_pFS->WriteBlock)
-    return false;
-
-  if (m_pFS->WriteBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer,
-                        (FPDF_DWORD)size) == 0) {
-    m_nCurPos = offset + size;
-    return true;
-  }
-  return false;
-}
-
-bool FPDF_FileHandlerContext::Flush() {
-  if (!m_pFS || !m_pFS->Flush)
-    return true;
-
-  return m_pFS->Flush(m_pFS->clientData) == 0;
-}
-#endif  // PDF_ENABLE_XFA
-
-FPDF_DOCUMENT LoadDocumentImpl(
-    const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
-    FPDF_BYTESTRING password) {
-  if (!pFileAccess) {
-    ProcessParseError(CPDF_Parser::FILE_ERROR);
-    return nullptr;
-  }
-
-  auto pParser = pdfium::MakeUnique<CPDF_Parser>();
-  pParser->SetPassword(password);
-
-  auto pDocument = pdfium::MakeUnique<CPDF_Document>(std::move(pParser));
-  CPDF_Parser::Error error =
-      pDocument->GetParser()->StartParse(pFileAccess, pDocument.get());
-  if (error != CPDF_Parser::SUCCESS) {
-    ProcessParseError(error);
-    return nullptr;
-  }
-  CheckUnSupportError(pDocument.get(), error);
-  return FPDFDocumentFromCPDFDocument(pDocument.release());
-}
-
-}  // namespace
-
-UnderlyingDocumentType* UnderlyingFromFPDFDocument(FPDF_DOCUMENT doc) {
-  return static_cast<UnderlyingDocumentType*>(doc);
-}
-
-FPDF_DOCUMENT FPDFDocumentFromUnderlying(UnderlyingDocumentType* doc) {
-  return static_cast<FPDF_DOCUMENT>(doc);
-}
-
-UnderlyingPageType* UnderlyingFromFPDFPage(FPDF_PAGE page) {
-  return static_cast<UnderlyingPageType*>(page);
-}
-
-CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc) {
-#ifdef PDF_ENABLE_XFA
-  return doc ? UnderlyingFromFPDFDocument(doc)->GetPDFDoc() : nullptr;
-#else   // PDF_ENABLE_XFA
-  return UnderlyingFromFPDFDocument(doc);
-#endif  // PDF_ENABLE_XFA
-}
-
-FPDF_DOCUMENT FPDFDocumentFromCPDFDocument(CPDF_Document* doc) {
-#ifdef PDF_ENABLE_XFA
-  return doc ? FPDFDocumentFromUnderlying(
-                   new CPDFXFA_Context(pdfium::WrapUnique(doc)))
-             : nullptr;
-#else   // PDF_ENABLE_XFA
-  return FPDFDocumentFromUnderlying(doc);
-#endif  // PDF_ENABLE_XFA
-}
-
-CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page) {
-#ifdef PDF_ENABLE_XFA
-  return page ? UnderlyingFromFPDFPage(page)->GetPDFPage() : nullptr;
-#else   // PDF_ENABLE_XFA
-  return UnderlyingFromFPDFPage(page);
-#endif  // PDF_ENABLE_XFA
-}
-
-CPDF_PathObject* CPDFPathObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
-  auto* obj = CPDFPageObjectFromFPDFPageObject(page_object);
-  return obj ? obj->AsPath() : nullptr;
-}
-
-CPDF_PageObject* CPDFPageObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
-  return static_cast<CPDF_PageObject*>(page_object);
-}
-
-CPDF_Object* CPDFObjectFromFPDFAttachment(FPDF_ATTACHMENT attachment) {
-  return static_cast<CPDF_Object*>(attachment);
-}
-
-ByteString CFXByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string) {
-  return WideString::FromUTF16LE(wide_string,
-                                 WideString::WStringLength(wide_string))
-      .UTF8Encode();
-}
-
-CFX_DIBitmap* CFXBitmapFromFPDFBitmap(FPDF_BITMAP bitmap) {
-  return static_cast<CFX_DIBitmap*>(bitmap);
-}
-
-CFX_FloatRect CFXFloatRectFromFSRECTF(const FS_RECTF& rect) {
-  return CFX_FloatRect(rect.left, rect.bottom, rect.right, rect.top);
-}
-
-void FSRECTFFromCFXFloatRect(const CFX_FloatRect& rect, FS_RECTF* out_rect) {
-  out_rect->left = rect.left;
-  out_rect->top = rect.top;
-  out_rect->right = rect.right;
-  out_rect->bottom = rect.bottom;
-}
-
-const FX_PATHPOINT* FXPathPointFromFPDFPathSegment(FPDF_PATHSEGMENT segment) {
-  return static_cast<const FX_PATHPOINT*>(segment);
-}
-
-unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
-                                                  void* buffer,
-                                                  unsigned long buflen) {
-  ByteString encoded_text = text.UTF16LE_Encode();
-  unsigned long len = encoded_text.GetLength();
-  if (buffer && len <= buflen)
-    memcpy(buffer, encoded_text.c_str(), len);
-  return len;
-}
-
-unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
-                                                   void* buffer,
-                                                   unsigned long buflen) {
-  ASSERT(stream);
-  uint8_t* data = stream->GetRawData();
-  uint32_t len = stream->GetRawSize();
-  CPDF_Dictionary* dict = stream->GetDict();
-  CPDF_Object* decoder = dict ? dict->GetDirectObjectFor("Filter") : nullptr;
-  if (decoder && (decoder->IsArray() || decoder->IsName())) {
-    // Decode the stream if one or more stream filters are specified.
-    uint8_t* decoded_data = nullptr;
-    uint32_t decoded_len = 0;
-    ByteString dummy_last_decoder;
-    CPDF_Dictionary* dummy_last_param;
-    if (PDF_DataDecode(data, len, dict, dict->GetIntegerFor("DL"), false,
-                       &decoded_data, &decoded_len, &dummy_last_decoder,
-                       &dummy_last_param)) {
-      if (buffer && buflen >= decoded_len)
-        memcpy(buffer, decoded_data, decoded_len);
-
-      // Free the buffer for the decoded data if it was allocated by
-      // PDF_DataDecode(). Note that for images with a single image-specific
-      // filter, |decoded_data| is directly assigned to be |data|, so
-      // |decoded_data| does not need to be freed.
-      if (decoded_data != data)
-        FX_Free(decoded_data);
-
-      return decoded_len;
-    }
-  }
-  // Copy the raw data and return its length if there is no valid filter
-  // specified or if decoding failed.
-  if (buffer && buflen >= len)
-    memcpy(buffer, data, len);
-
-  return len;
-}
-
-RetainPtr<IFX_SeekableReadStream> MakeSeekableReadStream(
-    FPDF_FILEACCESS* pFileAccess) {
-  return pdfium::MakeRetain<CPDF_CustomAccess>(pFileAccess);
-}
-
-#ifdef PDF_ENABLE_XFA
-RetainPtr<IFX_SeekableStream> MakeSeekableStream(
-    FPDF_FILEHANDLER* pFilehandler) {
-  return pdfium::MakeRetain<FPDF_FileHandlerContext>(pFilehandler);
-}
-#endif  // PDF_ENABLE_XFA
-
-// 0 bit: FPDF_POLICY_MACHINETIME_ACCESS
-static uint32_t foxit_sandbox_policy = 0xFFFFFFFF;
-
-void FSDK_SetSandBoxPolicy(FPDF_DWORD policy, FPDF_BOOL enable) {
-  switch (policy) {
-    case FPDF_POLICY_MACHINETIME_ACCESS: {
-      if (enable)
-        foxit_sandbox_policy |= 0x01;
-      else
-        foxit_sandbox_policy &= 0xFFFFFFFE;
-    } break;
-    default:
-      break;
-  }
-}
-
-FPDF_BOOL FSDK_IsSandBoxPolicyEnabled(FPDF_DWORD policy) {
-  switch (policy) {
-    case FPDF_POLICY_MACHINETIME_ACCESS:
-      return !!(foxit_sandbox_policy & 0x01);
-    default:
-      return false;
-  }
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary() {
-  FPDF_InitLibraryWithConfig(nullptr);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG* cfg) {
-  if (g_bLibraryInitialized)
-    return;
-
-  FXMEM_InitializePartitionAlloc();
-
-  CFX_GEModule* pModule = CFX_GEModule::Get();
-  pModule->Init(cfg ? cfg->m_pUserFontPaths : nullptr);
-
-  CPDF_ModuleMgr* pModuleMgr = CPDF_ModuleMgr::Get();
-  pModuleMgr->Init();
-
-#ifdef PDF_ENABLE_XFA
-  BC_Library_Init();
-#endif  // PDF_ENABLE_XFA
-  if (cfg && cfg->version >= 2)
-    IJS_Runtime::Initialize(cfg->m_v8EmbedderSlot, cfg->m_pIsolate);
-
-  g_bLibraryInitialized = true;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyLibrary() {
-  if (!g_bLibraryInitialized)
-    return;
-
-#ifdef PDF_ENABLE_XFA
-  BC_Library_Destroy();
-#endif  // PDF_ENABLE_XFA
-
-  CPDF_ModuleMgr::Destroy();
-  CFX_GEModule::Destroy();
-
-  IJS_Runtime::Destroy();
-
-  g_bLibraryInitialized = false;
-}
-
-#ifndef _WIN32
-int g_LastError;
-void SetLastError(int err) {
-  g_LastError = err;
-}
-
-int GetLastError() {
-  return g_LastError;
-}
-#endif  // _WIN32
-
-void ProcessParseError(CPDF_Parser::Error err) {
-  uint32_t err_code = FPDF_ERR_SUCCESS;
-  // Translate FPDFAPI error code to FPDFVIEW error code
-  switch (err) {
-    case CPDF_Parser::SUCCESS:
-      err_code = FPDF_ERR_SUCCESS;
-      break;
-    case CPDF_Parser::FILE_ERROR:
-      err_code = FPDF_ERR_FILE;
-      break;
-    case CPDF_Parser::FORMAT_ERROR:
-      err_code = FPDF_ERR_FORMAT;
-      break;
-    case CPDF_Parser::PASSWORD_ERROR:
-      err_code = FPDF_ERR_PASSWORD;
-      break;
-    case CPDF_Parser::HANDLER_ERROR:
-      err_code = FPDF_ERR_SECURITY;
-      break;
-  }
-  SetLastError(err_code);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_SetSandBoxPolicy(FPDF_DWORD policy,
-                                                     FPDF_BOOL enable) {
-  return FSDK_SetSandBoxPolicy(policy, enable);
-}
-
-#if defined(_WIN32)
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) {
-  g_pdfium_typeface_accessible_func = func;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi) {
-  g_pdfium_print_text_with_gdi = !!use_gdi;
-}
-#endif  // PDFIUM_PRINT_TEXT_WITH_GDI
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_SetPrintPostscriptLevel(int postscript_level) {
-  return postscript_level != 1 && FPDF_SetPrintMode(postscript_level);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode) {
-  if (mode < FPDF_PRINTMODE_EMF || mode > FPDF_PRINTMODE_POSTSCRIPT3)
-    return FALSE;
-  g_pdfium_print_mode = mode;
-  return TRUE;
-}
-#endif  // defined(_WIN32)
-
-FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
-FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password) {
-  // NOTE: the creation of the file needs to be by the embedder on the
-  // other side of this API.
-  return LoadDocumentImpl(IFX_SeekableReadStream::CreateFromFilename(file_path),
-                          password);
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDF_GetFormType(FPDF_DOCUMENT document) {
-  if (!document)
-    return false;
-
-  const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return FORMTYPE_NONE;
-
-  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return FORMTYPE_NONE;
-
-  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
-  if (!pAcroForm)
-    return FORMTYPE_NONE;
-
-  CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
-  if (!pXFA)
-    return FORMTYPE_ACRO_FORM;
-
-  bool needsRendering = pRoot->GetBooleanFor("NeedsRendering", false);
-  return needsRendering ? FORMTYPE_XFA_FULL : FORMTYPE_XFA_FOREGROUND;
-}
-
-#ifdef PDF_ENABLE_XFA
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_LoadXFA(FPDF_DOCUMENT document) {
-  return document && static_cast<CPDFXFA_Context*>(document)->LoadXFADoc();
-}
-#endif  // PDF_ENABLE_XFA
-
-class CMemFile final : public IFX_SeekableReadStream {
- public:
-  static RetainPtr<CMemFile> Create(const uint8_t* pBuf, FX_FILESIZE size) {
-    return RetainPtr<CMemFile>(new CMemFile(pBuf, size));
-  }
-
-  FX_FILESIZE GetSize() override { return m_size; }
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override {
-    if (offset < 0)
-      return false;
-
-    FX_SAFE_FILESIZE newPos = pdfium::base::checked_cast<FX_FILESIZE>(size);
-    newPos += offset;
-    if (!newPos.IsValid() || newPos.ValueOrDie() > m_size)
-      return false;
-
-    memcpy(buffer, m_pBuf + offset, size);
-    return true;
-  }
-
- private:
-  CMemFile(const uint8_t* pBuf, FX_FILESIZE size)
-      : m_pBuf(pBuf), m_size(size) {}
-
-  const uint8_t* const m_pBuf;
-  const FX_FILESIZE m_size;
-};
-
-FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
-FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password) {
-  return LoadDocumentImpl(
-      CMemFile::Create(static_cast<const uint8_t*>(data_buf), size), password);
-}
-
-FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
-FPDF_LoadCustomDocument(FPDF_FILEACCESS* pFileAccess,
-                        FPDF_BYTESTRING password) {
-  return LoadDocumentImpl(pdfium::MakeRetain<CPDF_CustomAccess>(pFileAccess),
-                          password);
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetFileVersion(FPDF_DOCUMENT doc,
-                                                        int* fileVersion) {
-  if (!fileVersion)
-    return false;
-
-  *fileVersion = 0;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(doc);
-  if (!pDoc)
-    return false;
-
-  const CPDF_Parser* pParser = pDoc->GetParser();
-  if (!pParser)
-    return false;
-
-  *fileVersion = pParser->GetFileVersion();
-  return true;
-}
-
-// jabdelmalek: changed return type from uint32_t to build on Linux (and match
-// header).
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDF_GetDocPermissions(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  // https://bugs.chromium.org/p/pdfium/issues/detail?id=499
-  if (!pDoc) {
-#ifndef PDF_ENABLE_XFA
-    return 0;
-#else   // PDF_ENABLE_XFA
-    return 0xFFFFFFFF;
-#endif  // PDF_ENABLE_XFA
-  }
-
-  return pDoc->GetUserPermissions();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc || !pDoc->GetParser())
-    return -1;
-
-  CPDF_Dictionary* pDict = pDoc->GetParser()->GetEncryptDict();
-  return pDict ? pDict->GetIntegerFor("R") : -1;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageCount(FPDF_DOCUMENT document) {
-  UnderlyingDocumentType* pDoc = UnderlyingFromFPDFDocument(document);
-  return pDoc ? pDoc->GetPageCount() : 0;
-}
-
-FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDF_LoadPage(FPDF_DOCUMENT document,
-                                                  int page_index) {
-  UnderlyingDocumentType* pDoc = UnderlyingFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-
-  if (page_index < 0 || page_index >= pDoc->GetPageCount())
-    return nullptr;
-
-#ifdef PDF_ENABLE_XFA
-  return pDoc->GetXFAPage(page_index).Leak();
-#else   // PDF_ENABLE_XFA
-  CPDF_Dictionary* pDict = pDoc->GetPage(page_index);
-  if (!pDict)
-    return nullptr;
-
-  CPDF_Page* pPage = new CPDF_Page(pDoc, pDict, true);
-  pPage->ParseContent();
-  return pPage;
-#endif  // PDF_ENABLE_XFA
-}
-
-FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageWidth(FPDF_PAGE page) {
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  return pPage ? pPage->GetPageWidth() : 0.0;
-}
-
-FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageHeight(FPDF_PAGE page) {
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  return pPage ? pPage->GetPageHeight() : 0.0;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetPageBoundingBox(FPDF_PAGE page,
-                                                            FS_RECTF* rect) {
-  if (!rect)
-    return false;
-
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return false;
-
-  FSRECTFFromCFXFloatRect(pPage->GetPageBBox(), rect);
-  return true;
-}
-
-#if defined(_WIN32)
-namespace {
-
-const double kEpsilonSize = 0.01f;
-
-void GetScaling(CPDF_Page* pPage,
-                int size_x,
-                int size_y,
-                int rotate,
-                double* scale_x,
-                double* scale_y) {
-  ASSERT(pPage);
-  ASSERT(scale_x);
-  ASSERT(scale_y);
-  double page_width = pPage->GetPageWidth();
-  double page_height = pPage->GetPageHeight();
-  if (page_width < kEpsilonSize || page_height < kEpsilonSize)
-    return;
-
-  if (rotate % 2 == 0) {
-    *scale_x = size_x / page_width;
-    *scale_y = size_y / page_height;
-  } else {
-    *scale_x = size_y / page_width;
-    *scale_y = size_x / page_height;
-  }
-}
-
-FX_RECT GetMaskDimensionsAndOffsets(CPDF_Page* pPage,
-                                    int start_x,
-                                    int start_y,
-                                    int size_x,
-                                    int size_y,
-                                    int rotate,
-                                    const CFX_FloatRect& mask_box) {
-  double scale_x = 0.0f;
-  double scale_y = 0.0f;
-  GetScaling(pPage, size_x, size_y, rotate, &scale_x, &scale_y);
-  if (scale_x < kEpsilonSize || scale_y < kEpsilonSize)
-    return FX_RECT();
-
-  // Compute sizes in page points. Round down to catch the entire bitmap.
-  int start_x_bm = static_cast<int>(mask_box.left * scale_x);
-  int start_y_bm = static_cast<int>(mask_box.bottom * scale_y);
-  int size_x_bm = static_cast<int>(mask_box.right * scale_x + 1.0f) -
-                  static_cast<int>(mask_box.left * scale_x);
-  int size_y_bm = static_cast<int>(mask_box.top * scale_y + 1.0f) -
-                  static_cast<int>(mask_box.bottom * scale_y);
-
-  // Get page rotation
-  int page_rotation = pPage->GetPageRotation();
-
-  // Compute offsets
-  int offset_x = 0;
-  int offset_y = 0;
-  if (size_x > size_y)
-    std::swap(size_x_bm, size_y_bm);
-
-  switch ((rotate + page_rotation) % 4) {
-    case 0:
-      offset_x = start_x_bm + start_x;
-      offset_y = start_y + size_y - size_y_bm - start_y_bm;
-      break;
-    case 1:
-      offset_x = start_y_bm + start_x;
-      offset_y = start_x_bm + start_y;
-      break;
-    case 2:
-      offset_x = start_x + size_x - size_x_bm - start_x_bm;
-      offset_y = start_y_bm + start_y;
-      break;
-    case 3:
-      offset_x = start_x + size_x - size_x_bm - start_y_bm;
-      offset_y = start_y + size_y - size_y_bm - start_x_bm;
-      break;
-  }
-  return FX_RECT(offset_x, offset_y, offset_x + size_x_bm,
-                 offset_y + size_y_bm);
-}
-
-// Get a bitmap of just the mask section defined by |mask_box| from a full page
-// bitmap |pBitmap|.
-RetainPtr<CFX_DIBitmap> GetMaskBitmap(CPDF_Page* pPage,
-                                      int start_x,
-                                      int start_y,
-                                      int size_x,
-                                      int size_y,
-                                      int rotate,
-                                      const RetainPtr<CFX_DIBitmap>& pSrc,
-                                      const CFX_FloatRect& mask_box,
-                                      FX_RECT* bitmap_area) {
-  ASSERT(bitmap_area);
-  *bitmap_area = GetMaskDimensionsAndOffsets(pPage, start_x, start_y, size_x,
-                                             size_y, rotate, mask_box);
-  if (bitmap_area->IsEmpty())
-    return nullptr;
-
-  // Create a new bitmap to transfer part of the page bitmap to.
-  RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
-  pDst->Create(bitmap_area->Width(), bitmap_area->Height(), FXDIB_Argb);
-  pDst->Clear(0x00ffffff);
-  pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc,
-                       bitmap_area->left, bitmap_area->top);
-  return pDst;
-}
-
-void RenderBitmap(CFX_RenderDevice* device,
-                  const RetainPtr<CFX_DIBitmap>& pSrc,
-                  const FX_RECT& mask_area) {
-  int size_x_bm = mask_area.Width();
-  int size_y_bm = mask_area.Height();
-  if (size_x_bm == 0 || size_y_bm == 0)
-    return;
-
-  // Create a new bitmap from the old one
-  RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
-  pDst->Create(size_x_bm, size_y_bm, FXDIB_Rgb32);
-  pDst->Clear(0xffffffff);
-  pDst->CompositeBitmap(0, 0, size_x_bm, size_y_bm, pSrc, 0, 0,
-                        FXDIB_BLEND_NORMAL, nullptr, false);
-
-  if (device->GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
-    device->StretchDIBits(pDst, mask_area.left, mask_area.top, size_x_bm,
-                          size_y_bm);
-  } else {
-    device->SetDIBits(pDst, mask_area.left, mask_area.top);
-  }
-}
-
-}  // namespace
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc,
-                                               FPDF_PAGE page,
-                                               int start_x,
-                                               int start_y,
-                                               int size_x,
-                                               int size_y,
-                                               int rotate,
-                                               int flags) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return;
-  pPage->SetRenderContext(pdfium::MakeUnique<CPDF_PageRenderContext>());
-  CPDF_PageRenderContext* pContext = pPage->GetRenderContext();
-
-  RetainPtr<CFX_DIBitmap> pBitmap;
-  // Don't render the full page to bitmap for a mask unless there are a lot
-  // of masks. Full page bitmaps result in large spool sizes, so they should
-  // only be used when necessary. For large numbers of masks, rendering each
-  // individually is inefficient and unlikely to significantly improve spool
-  // size. TODO (rbpotter): Find out why this still breaks printing for some
-  // PDFs (see crbug.com/777837).
-  const bool bEnableImageMasks = false;
-  const bool bNewBitmap = pPage->BackgroundAlphaNeeded() ||
-                          (pPage->HasImageMask() && !bEnableImageMasks) ||
-                          pPage->GetMaskBoundingBoxes().size() > 100;
-  const bool bHasMask = pPage->HasImageMask() && !bNewBitmap;
-  if (bNewBitmap || bHasMask) {
-    pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    pBitmap->Create(size_x, size_y, FXDIB_Argb);
-    pBitmap->Clear(0x00ffffff);
-    CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
-    pContext->m_pDevice = pdfium::WrapUnique(pDevice);
-    pDevice->Attach(pBitmap, false, nullptr, false);
-    if (bHasMask) {
-      pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
-      uint32_t option_flags = pContext->m_pOptions->GetFlags();
-      option_flags |= RENDER_BREAKFORMASKS;
-      pContext->m_pOptions->SetFlags(option_flags);
-    }
-  } else {
-    pContext->m_pDevice = pdfium::MakeUnique<CFX_WindowsRenderDevice>(dc);
-  }
-
-  FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
-                         rotate, flags, true, nullptr);
-
-  if (bHasMask) {
-    // Finish rendering the page to bitmap and copy the correct segments
-    // of the page to individual image mask bitmaps.
-    const std::vector<CFX_FloatRect>& mask_boxes =
-        pPage->GetMaskBoundingBoxes();
-    std::vector<FX_RECT> bitmap_areas(mask_boxes.size());
-    std::vector<RetainPtr<CFX_DIBitmap>> bitmaps(mask_boxes.size());
-    for (size_t i = 0; i < mask_boxes.size(); i++) {
-      bitmaps[i] =
-          GetMaskBitmap(pPage, start_x, start_y, size_x, size_y, rotate,
-                        pBitmap, mask_boxes[i], &bitmap_areas[i]);
-      pContext->m_pRenderer->Continue(nullptr);
-    }
-
-    // Reset rendering context
-    pPage->SetRenderContext(nullptr);
-
-    // Begin rendering to the printer. Add flag to indicate the renderer should
-    // pause after each image mask.
-    pPage->SetRenderContext(pdfium::MakeUnique<CPDF_PageRenderContext>());
-    pContext = pPage->GetRenderContext();
-    pContext->m_pDevice = pdfium::MakeUnique<CFX_WindowsRenderDevice>(dc);
-    pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
-
-    uint32_t option_flags = pContext->m_pOptions->GetFlags();
-    option_flags |= RENDER_BREAKFORMASKS;
-    pContext->m_pOptions->SetFlags(option_flags);
-
-    FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
-                           rotate, flags, true, nullptr);
-
-    // Render masks
-    for (size_t i = 0; i < mask_boxes.size(); i++) {
-      // Render the bitmap for the mask and free the bitmap.
-      if (bitmaps[i]) {  // will be null if mask has zero area
-        RenderBitmap(pContext->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
-      }
-      // Render the next portion of page.
-      pContext->m_pRenderer->Continue(nullptr);
-    }
-  } else if (bNewBitmap) {
-    CFX_WindowsRenderDevice WinDC(dc);
-    if (WinDC.GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
-      auto pDst = pdfium::MakeRetain<CFX_DIBitmap>();
-      int pitch = pBitmap->GetPitch();
-      pDst->Create(size_x, size_y, FXDIB_Rgb32);
-      memset(pDst->GetBuffer(), -1, pitch * size_y);
-      pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0,
-                            FXDIB_BLEND_NORMAL, nullptr, false);
-      WinDC.StretchDIBits(pDst, 0, 0, size_x, size_y);
-    } else {
-      WinDC.SetDIBits(pBitmap, 0, 0);
-    }
-  }
-
-  pPage->SetRenderContext(nullptr);
-}
-#endif  // defined(_WIN32)
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,
-                                                     FPDF_PAGE page,
-                                                     int start_x,
-                                                     int start_y,
-                                                     int size_x,
-                                                     int size_y,
-                                                     int rotate,
-                                                     int flags) {
-  if (!bitmap)
-    return;
-
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-  CPDF_PageRenderContext* pContext = new CPDF_PageRenderContext;
-  pPage->SetRenderContext(pdfium::WrapUnique(pContext));
-
-  CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
-  pContext->m_pDevice.reset(pDevice);
-
-  RetainPtr<CFX_DIBitmap> pBitmap(CFXBitmapFromFPDFBitmap(bitmap));
-  pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
-  FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
-                         rotate, flags, true, nullptr);
-
-#ifdef _SKIA_SUPPORT_PATHS_
-  pDevice->Flush(true);
-  pBitmap->UnPreMultiply();
-#endif
-  pPage->SetRenderContext(nullptr);
-}
-
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,
-                                FPDF_PAGE page,
-                                const FS_MATRIX* matrix,
-                                const FS_RECTF* clipping,
-                                int flags) {
-  if (!bitmap)
-    return;
-
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-  CPDF_PageRenderContext* pContext = new CPDF_PageRenderContext;
-  pPage->SetRenderContext(pdfium::WrapUnique(pContext));
-
-  CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
-  pContext->m_pDevice.reset(pDevice);
-
-  RetainPtr<CFX_DIBitmap> pBitmap(CFXBitmapFromFPDFBitmap(bitmap));
-  pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
-
-  CFX_FloatRect clipping_rect;
-  if (clipping)
-    clipping_rect = CFXFloatRectFromFSRECTF(*clipping);
-  FX_RECT clip_rect = clipping_rect.ToFxRect();
-
-  CFX_Matrix transform_matrix = pPage->GetDisplayMatrix(
-      0, 0, pPage->GetPageWidth(), pPage->GetPageHeight(), 0);
-
-  if (matrix) {
-    transform_matrix.Concat(CFX_Matrix(matrix->a, matrix->b, matrix->c,
-                                       matrix->d, matrix->e, matrix->f));
-  }
-  RenderPageImpl(pContext, pPage, transform_matrix, clip_rect, flags, true,
-                 nullptr);
-
-  pPage->SetRenderContext(nullptr);
-}
-
-#ifdef _SKIA_SUPPORT_
-FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
-                                                           int size_x,
-                                                           int size_y) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return nullptr;
-
-  CPDF_PageRenderContext* pContext = new CPDF_PageRenderContext;
-  pPage->SetRenderContext(pdfium::WrapUnique(pContext));
-  CFX_DefaultRenderDevice* skDevice = new CFX_DefaultRenderDevice;
-  FPDF_RECORDER recorder = skDevice->CreateRecorder(size_x, size_y);
-  pContext->m_pDevice.reset(skDevice);
-  FPDF_RenderPage_Retail(pContext, page, 0, 0, size_x, size_y, 0, 0, true,
-                         nullptr);
-  pPage->SetRenderContext(nullptr);
-  return recorder;
-}
-#endif
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) {
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  if (!page)
-    return;
-#ifdef PDF_ENABLE_XFA
-  // Take it back across the API and throw it away.
-  RetainPtr<CPDFXFA_Page>().Unleak(pPage);
-#else   // PDF_ENABLE_XFA
-  CPDFSDK_PageView* pPageView =
-      static_cast<CPDFSDK_PageView*>(pPage->GetView());
-  if (pPageView) {
-    // We're already destroying the pageview, so bail early.
-    if (pPageView->IsBeingDestroyed())
-      return;
-
-    if (pPageView->IsLocked()) {
-      pPageView->TakePageOwnership();
-      return;
-    }
-
-    bool owned = pPageView->OwnsPage();
-    // This will delete the |pPageView| object. We must cleanup the PageView
-    // first because it will attempt to reset the View on the |pPage| during
-    // destruction.
-    pPageView->GetFormFillEnv()->RemovePageView(pPage);
-    // If the page was owned then the pageview will have deleted the page.
-    if (owned)
-      return;
-  }
-  delete pPage;
-#endif  // PDF_ENABLE_XFA
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseDocument(FPDF_DOCUMENT document) {
-  delete UnderlyingFromFPDFDocument(document);
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError() {
-  return GetLastError();
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page,
-                                                 int start_x,
-                                                 int start_y,
-                                                 int size_x,
-                                                 int size_y,
-                                                 int rotate,
-                                                 int device_x,
-                                                 int device_y,
-                                                 double* page_x,
-                                                 double* page_y) {
-  if (!page || !page_x || !page_y)
-    return;
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-#ifdef PDF_ENABLE_XFA
-  pPage->DeviceToPage(start_x, start_y, size_x, size_y, rotate, device_x,
-                      device_y, page_x, page_y);
-#else   // PDF_ENABLE_XFA
-  CFX_Matrix page2device =
-      pPage->GetDisplayMatrix(start_x, start_y, size_x, size_y, rotate);
-
-  CFX_PointF pos = page2device.GetInverse().Transform(
-      CFX_PointF(static_cast<float>(device_x), static_cast<float>(device_y)));
-
-  *page_x = pos.x;
-  *page_y = pos.y;
-#endif  // PDF_ENABLE_XFA
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page,
-                                                 int start_x,
-                                                 int start_y,
-                                                 int size_x,
-                                                 int size_y,
-                                                 int rotate,
-                                                 double page_x,
-                                                 double page_y,
-                                                 int* device_x,
-                                                 int* device_y) {
-  if (!device_x || !device_y)
-    return;
-  UnderlyingPageType* pPage = UnderlyingFromFPDFPage(page);
-  if (!pPage)
-    return;
-#ifdef PDF_ENABLE_XFA
-  pPage->PageToDevice(start_x, start_y, size_x, size_y, rotate, page_x, page_y,
-                      device_x, device_y);
-#else   // PDF_ENABLE_XFA
-  CFX_Matrix page2device =
-      pPage->GetDisplayMatrix(start_x, start_y, size_x, size_y, rotate);
-  CFX_PointF pos = page2device.Transform(
-      CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)));
-
-  *device_x = FXSYS_round(pos.x);
-  *device_y = FXSYS_round(pos.y);
-#endif  // PDF_ENABLE_XFA
-}
-
-FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width,
-                                                        int height,
-                                                        int alpha) {
-  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(width, height, alpha ? FXDIB_Argb : FXDIB_Rgb32))
-    return nullptr;
-
-  return pBitmap.Leak();
-}
-
-FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width,
-                                                          int height,
-                                                          int format,
-                                                          void* first_scan,
-                                                          int stride) {
-  FXDIB_Format fx_format;
-  switch (format) {
-    case FPDFBitmap_Gray:
-      fx_format = FXDIB_8bppRgb;
-      break;
-    case FPDFBitmap_BGR:
-      fx_format = FXDIB_Rgb;
-      break;
-    case FPDFBitmap_BGRx:
-      fx_format = FXDIB_Rgb32;
-      break;
-    case FPDFBitmap_BGRA:
-      fx_format = FXDIB_Argb;
-      break;
-    default:
-      return nullptr;
-  }
-  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  pBitmap->Create(width, height, fx_format, static_cast<uint8_t*>(first_scan),
-                  stride);
-  return pBitmap.Leak();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetFormat(FPDF_BITMAP bitmap) {
-  if (!bitmap)
-    return FPDFBitmap_Unknown;
-
-  FXDIB_Format format = CFXBitmapFromFPDFBitmap(bitmap)->GetFormat();
-  switch (format) {
-    case FXDIB_8bppRgb:
-    case FXDIB_8bppMask:
-      return FPDFBitmap_Gray;
-    case FXDIB_Rgb:
-      return FPDFBitmap_BGR;
-    case FXDIB_Rgb32:
-      return FPDFBitmap_BGRx;
-    case FXDIB_Argb:
-      return FPDFBitmap_BGRA;
-    default:
-      return FPDFBitmap_Unknown;
-  }
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_FillRect(FPDF_BITMAP bitmap,
-                                                   int left,
-                                                   int top,
-                                                   int width,
-                                                   int height,
-                                                   FPDF_DWORD color) {
-  if (!bitmap)
-    return;
-
-  CFX_DefaultRenderDevice device;
-  RetainPtr<CFX_DIBitmap> pBitmap(CFXBitmapFromFPDFBitmap(bitmap));
-  device.Attach(pBitmap, false, nullptr, false);
-  if (!pBitmap->HasAlpha())
-    color |= 0xFF000000;
-  FX_RECT rect(left, top, left + width, top + height);
-  device.FillRect(&rect, color);
-}
-
-FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
-  return bitmap ? CFXBitmapFromFPDFBitmap(bitmap)->GetBuffer() : nullptr;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) {
-  return bitmap ? CFXBitmapFromFPDFBitmap(bitmap)->GetWidth() : 0;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetHeight(FPDF_BITMAP bitmap) {
-  return bitmap ? CFXBitmapFromFPDFBitmap(bitmap)->GetHeight() : 0;
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetStride(FPDF_BITMAP bitmap) {
-  return bitmap ? CFXBitmapFromFPDFBitmap(bitmap)->GetPitch() : 0;
-}
-
-FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_Destroy(FPDF_BITMAP bitmap) {
-  RetainPtr<CFX_DIBitmap> destroyer;
-  destroyer.Unleak(CFXBitmapFromFPDFBitmap(bitmap));
-}
-
-void FPDF_RenderPage_Retail(CPDF_PageRenderContext* pContext,
-                            FPDF_PAGE page,
-                            int start_x,
-                            int start_y,
-                            int size_x,
-                            int size_y,
-                            int rotate,
-                            int flags,
-                            bool bNeedToRestore,
-                            IFSDK_PAUSE_Adapter* pause) {
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage)
-    return;
-
-  RenderPageImpl(pContext, pPage, pPage->GetDisplayMatrix(
-                                      start_x, start_y, size_x, size_y, rotate),
-                 FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y),
-                 flags, bNeedToRestore, pause);
-}
-
-FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,
-                                                      int page_index,
-                                                      double* width,
-                                                      double* height) {
-  UnderlyingDocumentType* pDoc = UnderlyingFromFPDFDocument(document);
-  if (!pDoc)
-    return false;
-
-#ifdef PDF_ENABLE_XFA
-  int count = pDoc->GetPageCount();
-  if (page_index < 0 || page_index >= count)
-    return false;
-  RetainPtr<CPDFXFA_Page> pPage = pDoc->GetXFAPage(page_index);
-  if (!pPage)
-    return false;
-  *width = pPage->GetPageWidth();
-  *height = pPage->GetPageHeight();
-#else   // PDF_ENABLE_XFA
-  CPDF_Dictionary* pDict = pDoc->GetPage(page_index);
-  if (!pDict)
-    return false;
-
-  CPDF_Page page(pDoc, pDict, true);
-  *width = page.GetPageWidth();
-  *height = page.GetPageHeight();
-#endif  // PDF_ENABLE_XFA
-
-  return true;
-}
-
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return true;
-  CPDF_ViewerPreferences viewRef(pDoc);
-  return viewRef.PrintScaling();
-}
-
-FPDF_EXPORT int FPDF_CALLCONV
-FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return 1;
-  CPDF_ViewerPreferences viewRef(pDoc);
-  return viewRef.NumCopies();
-}
-
-FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV
-FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-  CPDF_ViewerPreferences viewRef(pDoc);
-  return viewRef.PrintPageRange();
-}
-
-FPDF_EXPORT FPDF_DUPLEXTYPE FPDF_CALLCONV
-FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return DuplexUndefined;
-  CPDF_ViewerPreferences viewRef(pDoc);
-  ByteString duplex = viewRef.Duplex();
-  if ("Simplex" == duplex)
-    return Simplex;
-  if ("DuplexFlipShortEdge" == duplex)
-    return DuplexFlipShortEdge;
-  if ("DuplexFlipLongEdge" == duplex)
-    return DuplexFlipLongEdge;
-  return DuplexUndefined;
-}
-
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,
-                       FPDF_BYTESTRING key,
-                       char* buffer,
-                       unsigned long length) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return 0;
-
-  CPDF_ViewerPreferences viewRef(pDoc);
-  ByteString bsVal;
-  if (!viewRef.GenericName(key, &bsVal))
-    return 0;
-
-  unsigned long dwStringLen = bsVal.GetLength() + 1;
-  if (buffer && length >= dwStringLen)
-    memcpy(buffer, bsVal.c_str(), dwStringLen);
-  return dwStringLen;
-}
-
-FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV
-FPDF_CountNamedDests(FPDF_DOCUMENT document) {
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return 0;
-
-  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return 0;
-
-  CPDF_NameTree nameTree(pDoc, "Dests");
-  pdfium::base::CheckedNumeric<FPDF_DWORD> count = nameTree.GetCount();
-  CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
-  if (pDest)
-    count += pDest->GetCount();
-
-  if (!count.IsValid())
-    return 0;
-
-  return count.ValueOrDie();
-}
-
-FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
-FPDF_GetNamedDestByName(FPDF_DOCUMENT document, FPDF_BYTESTRING name) {
-  if (!name || name[0] == 0)
-    return nullptr;
-
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-
-  CPDF_NameTree name_tree(pDoc, "Dests");
-  return name_tree.LookupNamedDest(pDoc, PDF_DecodeText(ByteString(name)));
-}
-
-#ifdef PDF_ENABLE_XFA
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* str) {
-  if (!str)
-    return -1;
-
-  memset(str, 0, sizeof(FPDF_BSTR));
-  return 0;
-}
-
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* str,
-                                                    FPDF_LPCSTR bstr,
-                                                    int length) {
-  if (!str || !bstr || !length)
-    return -1;
-
-  if (length == -1)
-    length = strlen(bstr);
-
-  if (length == 0) {
-    FPDF_BStr_Clear(str);
-    return 0;
-  }
-
-  if (str->str && str->len < length)
-    str->str = FX_Realloc(char, str->str, length + 1);
-  else if (!str->str)
-    str->str = FX_Alloc(char, length + 1);
-
-  str->str[length] = 0;
-  memcpy(str->str, bstr, length);
-  str->len = length;
-
-  return 0;
-}
-
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* str) {
-  if (!str)
-    return -1;
-
-  if (str->str) {
-    FX_Free(str->str);
-    str->str = nullptr;
-  }
-  str->len = 0;
-  return 0;
-}
-#endif  // PDF_ENABLE_XFA
-
-FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document,
-                                                      int index,
-                                                      void* buffer,
-                                                      long* buflen) {
-  if (!buffer)
-    *buflen = 0;
-
-  if (index < 0)
-    return nullptr;
-
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
-  if (!pDoc)
-    return nullptr;
-
-  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return nullptr;
-
-  CPDF_Object* pDestObj = nullptr;
-  WideString wsName;
-  CPDF_NameTree nameTree(pDoc, "Dests");
-  int count = nameTree.GetCount();
-  if (index >= count) {
-    CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
-    if (!pDest)
-      return nullptr;
-
-    pdfium::base::CheckedNumeric<int> checked_count = count;
-    checked_count += pDest->GetCount();
-    if (!checked_count.IsValid() || index >= checked_count.ValueOrDie())
-      return nullptr;
-
-    index -= count;
-    int i = 0;
-    ByteString bsName;
-    for (const auto& it : *pDest) {
-      bsName = it.first;
-      pDestObj = it.second.get();
-      if (!pDestObj)
-        continue;
-      if (i == index)
-        break;
-      i++;
-    }
-    wsName = PDF_DecodeText(bsName);
-  } else {
-    pDestObj = nameTree.LookupValueAndName(index, &wsName);
-  }
-  if (!pDestObj)
-    return nullptr;
-  if (CPDF_Dictionary* pDict = pDestObj->AsDictionary()) {
-    pDestObj = pDict->GetArrayFor("D");
-    if (!pDestObj)
-      return nullptr;
-  }
-  if (!pDestObj->IsArray())
-    return nullptr;
-
-  ByteString utf16Name = wsName.UTF16LE_Encode();
-  int len = utf16Name.GetLength();
-  if (!buffer) {
-    *buflen = len;
-  } else if (len <= *buflen) {
-    memcpy(buffer, utf16Name.c_str(), len);
-    *buflen = len;
-  } else {
-    *buflen = -1;
-  }
-  return (FPDF_DEST)pDestObj;
-}
diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c
deleted file mode 100644
index 53d8776..0000000
--- a/fpdfsdk/fpdfview_c_api_test.c
+++ /dev/null
@@ -1,375 +0,0 @@
-// Copyright 2015 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.
-
-// This "C" (not "C++") file ensures that the public headers compile
-// and link for "C" (and not just "C++").
-
-#include <stdio.h>
-
-#include "fpdfsdk/fpdfview_c_api_test.h"
-
-#include "public/fpdf_annot.h"
-#include "public/fpdf_attachment.h"
-#include "public/fpdf_catalog.h"
-#include "public/fpdf_dataavail.h"
-#include "public/fpdf_doc.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdf_ext.h"
-#include "public/fpdf_flatten.h"
-#include "public/fpdf_formfill.h"
-#include "public/fpdf_fwlevent.h"
-#include "public/fpdf_ppo.h"
-#include "public/fpdf_progressive.h"
-#include "public/fpdf_save.h"
-#include "public/fpdf_searchex.h"
-#include "public/fpdf_structtree.h"
-#include "public/fpdf_sysfontinfo.h"
-#include "public/fpdf_text.h"
-#include "public/fpdf_transformpage.h"
-#include "public/fpdfview.h"
-
-// Scheme for avoiding LTO out of existence, warnings, etc.
-typedef void (*fnptr)(void);  // Legal generic function type for casts.
-fnptr g_c_api_test_fnptr = NULL;  // Extern, so can't know it doesn't change.
-#define CHK(x) if ((fnptr)(x) == g_c_api_test_fnptr) return 0
-
-// Function to call from gtest harness to ensure linker resolution.
-int CheckPDFiumCApi() {
-    // fpdf_annot.h
-    CHK(FPDFAnnot_IsSupportedSubtype);
-    CHK(FPDFPage_CreateAnnot);
-    CHK(FPDFPage_GetAnnotCount);
-    CHK(FPDFPage_GetAnnot);
-    CHK(FPDFPage_GetAnnotIndex);
-    CHK(FPDFPage_CloseAnnot);
-    CHK(FPDFPage_RemoveAnnot);
-    CHK(FPDFAnnot_GetSubtype);
-    CHK(FPDFAnnot_IsObjectSupportedSubtype);
-    CHK(FPDFAnnot_UpdateObject);
-    CHK(FPDFAnnot_AppendObject);
-    CHK(FPDFAnnot_GetObjectCount);
-    CHK(FPDFAnnot_GetObject);
-    CHK(FPDFAnnot_RemoveObject);
-    CHK(FPDFAnnot_SetColor);
-    CHK(FPDFAnnot_GetColor);
-    CHK(FPDFAnnot_HasAttachmentPoints);
-    CHK(FPDFAnnot_SetAttachmentPoints);
-    CHK(FPDFAnnot_GetAttachmentPoints);
-    CHK(FPDFAnnot_SetRect);
-    CHK(FPDFAnnot_GetRect);
-    CHK(FPDFAnnot_HasKey);
-    CHK(FPDFAnnot_GetValueType);
-    CHK(FPDFAnnot_SetStringValue);
-    CHK(FPDFAnnot_GetStringValue);
-    CHK(FPDFAnnot_SetAP);
-    CHK(FPDFAnnot_GetAP);
-    CHK(FPDFAnnot_GetLinkedAnnot);
-    CHK(FPDFAnnot_GetFlags);
-    CHK(FPDFAnnot_SetFlags);
-    CHK(FPDFAnnot_GetFormFieldFlags);
-    CHK(FPDFAnnot_GetFormFieldAtPoint);
-
-    // fpdf_attachment.h
-    CHK(FPDFDoc_GetAttachmentCount);
-    CHK(FPDFDoc_AddAttachment);
-    CHK(FPDFDoc_GetAttachment);
-    CHK(FPDFDoc_DeleteAttachment);
-    CHK(FPDFAttachment_GetName);
-    CHK(FPDFAttachment_HasKey);
-    CHK(FPDFAttachment_GetValueType);
-    CHK(FPDFAttachment_SetStringValue);
-    CHK(FPDFAttachment_GetStringValue);
-    CHK(FPDFAttachment_SetFile);
-    CHK(FPDFAttachment_GetFile);
-
-    // fpdf_catalog.h
-    CHK(FPDFCatalog_IsTagged);
-
-    // fpdf_dataavail.h
-    CHK(FPDFAvail_Create);
-    CHK(FPDFAvail_Destroy);
-    CHK(FPDFAvail_IsDocAvail);
-    CHK(FPDFAvail_GetDocument);
-    CHK(FPDFAvail_GetFirstPageNum);
-    CHK(FPDFAvail_IsPageAvail);
-    CHK(FPDFAvail_IsFormAvail);
-    CHK(FPDFAvail_IsLinearized);
-
-    // fpdf_doc.h
-    CHK(FPDFBookmark_GetFirstChild);
-    CHK(FPDFBookmark_GetNextSibling);
-    CHK(FPDFBookmark_GetTitle);
-    CHK(FPDFBookmark_Find);
-    CHK(FPDFBookmark_GetDest);
-    CHK(FPDFBookmark_GetAction);
-    CHK(FPDFAction_GetType);
-    CHK(FPDFAction_GetDest);
-    CHK(FPDFAction_GetFilePath);
-    CHK(FPDFAction_GetURIPath);
-    CHK(FPDFDest_GetPageIndex);
-    CHK(FPDFDest_GetLocationInPage);
-    CHK(FPDFDest_GetView);
-    CHK(FPDFLink_GetLinkAtPoint);
-    CHK(FPDFLink_GetLinkZOrderAtPoint);
-    CHK(FPDFLink_GetDest);
-    CHK(FPDFLink_GetAction);
-    CHK(FPDFLink_Enumerate);
-    CHK(FPDFLink_GetAnnotRect);
-    CHK(FPDFLink_CountQuadPoints);
-    CHK(FPDFLink_GetQuadPoints);
-    CHK(FPDF_GetMetaText);
-    CHK(FPDF_GetPageLabel);
-
-    // fpdf_edit.h
-    CHK(FPDF_CreateNewDocument);
-    CHK(FPDFPage_New);
-    CHK(FPDFPage_Delete);
-    CHK(FPDFPage_GetRotation);
-    CHK(FPDFPage_SetRotation);
-    CHK(FPDFPage_InsertObject);
-    CHK(FPDFPage_CountObject);
-    CHK(FPDFPage_CountObjects);
-    CHK(FPDFPage_GetObject);
-    CHK(FPDFPage_HasTransparency);
-    CHK(FPDFPage_GenerateContent);
-    CHK(FPDFPageObj_Destroy);
-    CHK(FPDFPageObj_HasTransparency);
-    CHK(FPDFPageObj_GetBounds);
-    CHK(FPDFPageObj_GetType);
-    CHK(FPDFPageObj_SetBlendMode);
-    CHK(FPDFPageObj_Transform);
-    CHK(FPDFPage_TransformAnnots);
-    CHK(FPDFPageObj_NewImageObj);
-    CHK(FPDFImageObj_LoadJpegFile);
-    CHK(FPDFImageObj_LoadJpegFileInline);
-    CHK(FPDFImageObj_SetMatrix);
-    CHK(FPDFImageObj_SetBitmap);
-    CHK(FPDFImageObj_GetBitmap);
-    CHK(FPDFImageObj_GetImageDataDecoded);
-    CHK(FPDFImageObj_GetImageDataRaw);
-    CHK(FPDFImageObj_GetImageFilterCount);
-    CHK(FPDFImageObj_GetImageFilter);
-    CHK(FPDFImageObj_GetImageMetadata);
-    CHK(FPDFPageObj_CreateNewPath);
-    CHK(FPDFPageObj_CreateNewRect);
-    CHK(FPDFPath_SetStrokeColor);
-    CHK(FPDFPath_GetStrokeColor);
-    CHK(FPDFPath_SetStrokeWidth);
-    CHK(FPDFPath_SetFillColor);
-    CHK(FPDFPath_GetFillColor);
-    CHK(FPDFPath_CountSegments);
-    CHK(FPDFPath_GetPathSegment);
-    CHK(FPDFPathSegment_GetPoint);
-    CHK(FPDFPathSegment_GetType);
-    CHK(FPDFPathSegment_GetClose);
-    CHK(FPDFPath_MoveTo);
-    CHK(FPDFPath_LineTo);
-    CHK(FPDFPath_BezierTo);
-    CHK(FPDFPath_Close);
-    CHK(FPDFPath_SetDrawMode);
-    CHK(FPDFPath_SetLineCap);
-    CHK(FPDFPath_SetLineJoin);
-    CHK(FPDFPageObj_NewTextObj);
-    CHK(FPDFText_SetText);
-    CHK(FPDFText_SetFillColor);
-    CHK(FPDFText_LoadFont);
-    CHK(FPDFFont_Close);
-    CHK(FPDFPageObj_CreateTextObj);
-
-    // fpdf_ext.h
-    CHK(FSDK_SetUnSpObjProcessHandler);
-    CHK(FPDFDoc_GetPageMode);
-
-    // fpdf_flatten.h
-    CHK(FPDFPage_Flatten);
-
-    // fpdf_fwlevent.h - no exports.
-
-    // fpdf_formfill.h
-    CHK(FPDFDOC_InitFormFillEnvironment);
-    CHK(FPDFDOC_ExitFormFillEnvironment);
-    CHK(FORM_OnAfterLoadPage);
-    CHK(FORM_OnBeforeClosePage);
-    CHK(FORM_DoDocumentJSAction);
-    CHK(FORM_DoDocumentOpenAction);
-    CHK(FORM_DoDocumentAAction);
-    CHK(FORM_DoPageAAction);
-    CHK(FORM_OnMouseMove);
-    CHK(FORM_OnFocus);
-    CHK(FORM_OnLButtonDown);
-    CHK(FORM_OnLButtonUp);
-#ifdef PDF_ENABLE_XFA
-    CHK(FORM_OnRButtonDown);
-    CHK(FORM_OnRButtonUp);
-#endif
-    CHK(FORM_OnKeyDown);
-    CHK(FORM_OnKeyUp);
-    CHK(FORM_OnChar);
-    CHK(FORM_GetSelectedText);
-    CHK(FORM_ReplaceSelection);
-    CHK(FORM_ForceToKillFocus);
-    CHK(FPDFPage_HasFormFieldAtPoint);
-    CHK(FPDFPage_FormFieldZOrderAtPoint);
-    CHK(FPDF_SetFormFieldHighlightColor);
-    CHK(FPDF_SetFormFieldHighlightAlpha);
-    CHK(FPDF_RemoveFormFieldHighlight);
-    CHK(FPDF_FFLDraw);
-#ifdef _SKIA_SUPPORT_
-    CHK(FPDF_FFLRecord);
-#endif
-    CHK(FPDF_GetFormType);
-#ifdef PDF_ENABLE_XFA
-    CHK(FPDF_LoadXFA);
-    CHK(FPDF_Widget_Undo);
-    CHK(FPDF_Widget_Redo);
-    CHK(FPDF_Widget_SelectAll);
-    CHK(FPDF_Widget_Copy);
-    CHK(FPDF_Widget_Cut);
-    CHK(FPDF_Widget_Paste);
-    CHK(FPDF_Widget_ReplaceSpellCheckWord);
-    CHK(FPDF_Widget_GetSpellCheckWords);
-    CHK(FPDF_StringHandleCounts);
-    CHK(FPDF_StringHandleGetStringByIndex);
-    CHK(FPDF_StringHandleRelease);
-    CHK(FPDF_StringHandleAddString);
-#endif
-
-    // fpdf_ppo.h
-    CHK(FPDF_ImportPages);
-    CHK(FPDF_CopyViewerPreferences);
-
-    // fpdf_progressive.h
-    CHK(FPDF_RenderPageBitmap_Start);
-    CHK(FPDF_RenderPage_Continue);
-    CHK(FPDF_RenderPage_Close);
-
-    // fpdf_save.h
-    CHK(FPDF_SaveAsCopy);
-    CHK(FPDF_SaveWithVersion);
-
-    // fpdf_searchex.h
-    CHK(FPDFText_GetCharIndexFromTextIndex);
-    CHK(FPDFText_GetTextIndexFromCharIndex);
-
-    // fpdf_structtree.h
-    CHK(FPDF_StructTree_GetForPage);
-    CHK(FPDF_StructTree_Close);
-    CHK(FPDF_StructTree_CountChildren);
-    CHK(FPDF_StructTree_GetChildAtIndex);
-    CHK(FPDF_StructElement_GetAltText);
-    CHK(FPDF_StructElement_GetMarkedContentID);
-    CHK(FPDF_StructElement_GetType);
-    CHK(FPDF_StructElement_GetTitle);
-    CHK(FPDF_StructElement_CountChildren);
-    CHK(FPDF_StructElement_GetChildAtIndex);
-
-    // fpdf_sysfontinfo.h
-    CHK(FPDF_GetDefaultTTFMap);
-    CHK(FPDF_AddInstalledFont);
-    CHK(FPDF_SetSystemFontInfo);
-    CHK(FPDF_GetDefaultSystemFontInfo);
-    CHK(FPDF_FreeDefaultSystemFontInfo);
-
-    // fpdf_text.h
-    CHK(FPDFText_LoadPage);
-    CHK(FPDFText_ClosePage);
-    CHK(FPDFText_CountChars);
-    CHK(FPDFText_GetUnicode);
-    CHK(FPDFText_GetFontSize);
-    CHK(FPDFText_GetCharBox);
-    CHK(FPDFText_GetCharOrigin);
-    CHK(FPDFText_GetCharIndexAtPos);
-    CHK(FPDFText_GetText);
-    CHK(FPDFText_CountRects);
-    CHK(FPDFText_GetRect);
-    CHK(FPDFText_GetBoundedText);
-    CHK(FPDFText_FindStart);
-    CHK(FPDFText_FindNext);
-    CHK(FPDFText_FindPrev);
-    CHK(FPDFText_GetSchResultIndex);
-    CHK(FPDFText_GetSchCount);
-    CHK(FPDFText_FindClose);
-    CHK(FPDFLink_LoadWebLinks);
-    CHK(FPDFLink_CountWebLinks);
-    CHK(FPDFLink_GetURL);
-    CHK(FPDFLink_CountRects);
-    CHK(FPDFLink_GetRect);
-    CHK(FPDFLink_CloseWebLinks);
-
-    // fpdf_transformpage.h
-    CHK(FPDFPage_SetMediaBox);
-    CHK(FPDFPage_SetCropBox);
-    CHK(FPDFPage_GetMediaBox);
-    CHK(FPDFPage_GetCropBox);
-    CHK(FPDFPage_TransFormWithClip);
-    CHK(FPDFPageObj_TransformClipPath);
-    CHK(FPDF_CreateClipPath);
-    CHK(FPDF_DestroyClipPath);
-    CHK(FPDFPage_InsertClipPath);
-
-    // fpdfview.h
-    CHK(FPDF_InitLibrary);
-    CHK(FPDF_InitLibraryWithConfig);
-    CHK(FPDF_DestroyLibrary);
-    CHK(FPDF_SetSandBoxPolicy);
-#if defined(_WIN32)
-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
-    CHK(FPDF_SetTypefaceAccessibleFunc);
-    CHK(FPDF_SetPrintTextWithGDI);
-#endif
-    CHK(FPDF_SetPrintPostscriptLevel);
-    CHK(FPDF_SetPrintMode);
-#endif
-    CHK(FPDF_LoadDocument);
-    CHK(FPDF_LoadMemDocument);
-    CHK(FPDF_LoadCustomDocument);
-    CHK(FPDF_GetFileVersion);
-    CHK(FPDF_GetLastError);
-    CHK(FPDF_GetDocPermissions);
-    CHK(FPDF_GetSecurityHandlerRevision);
-    CHK(FPDF_GetPageCount);
-    CHK(FPDF_LoadPage);
-    CHK(FPDF_GetPageWidth);
-    CHK(FPDF_GetPageHeight);
-    CHK(FPDF_GetPageBoundingBox);
-    CHK(FPDF_GetPageSizeByIndex);
-#ifdef _WIN32
-    CHK(FPDF_RenderPage);
-#endif
-    CHK(FPDF_RenderPageBitmap);
-    CHK(FPDF_RenderPageBitmapWithMatrix);
-#ifdef _SKIA_SUPPORT_
-    CHK(FPDF_RenderPageSkp);
-#endif
-    CHK(FPDF_ClosePage);
-    CHK(FPDF_CloseDocument);
-    CHK(FPDF_DeviceToPage);
-    CHK(FPDF_PageToDevice);
-    CHK(FPDFBitmap_Create);
-    CHK(FPDFBitmap_CreateEx);
-    CHK(FPDFBitmap_GetFormat);
-    CHK(FPDFBitmap_FillRect);
-    CHK(FPDFBitmap_GetBuffer);
-    CHK(FPDFBitmap_GetWidth);
-    CHK(FPDFBitmap_GetHeight);
-    CHK(FPDFBitmap_GetStride);
-    CHK(FPDFBitmap_Destroy);
-    CHK(FPDF_VIEWERREF_GetPrintScaling);
-    CHK(FPDF_VIEWERREF_GetNumCopies);
-    CHK(FPDF_VIEWERREF_GetPrintPageRange);
-    CHK(FPDF_VIEWERREF_GetDuplex);
-    CHK(FPDF_VIEWERREF_GetName);
-    CHK(FPDF_CountNamedDests);
-    CHK(FPDF_GetNamedDestByName);
-    CHK(FPDF_GetNamedDest);
-#ifdef PDF_ENABLE_XFA
-    CHK(FPDF_BStr_Init);
-    CHK(FPDF_BStr_Set);
-    CHK(FPDF_BStr_Clear);
-#endif
-
-    return 1;
-}
-
-#undef CHK
diff --git a/fpdfsdk/fpdfview_c_api_test.h b/fpdfsdk/fpdfview_c_api_test.h
deleted file mode 100644
index d5b84a2..0000000
--- a/fpdfsdk/fpdfview_c_api_test.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FPDFSDK_FPDFVIEW_C_API_TEST_H_
-#define FPDFSDK_FPDFVIEW_C_API_TEST_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Function to call from gtest harness to ensure linker resolution. Returns
-// 1 on success or 0 on error.
-int CheckPDFiumCApi();
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif  // FPDFSDK_FPDFVIEW_C_API_TEST_H_
diff --git a/fpdfsdk/fpdfview_embeddertest.cpp b/fpdfsdk/fpdfview_embeddertest.cpp
deleted file mode 100644
index cca77c9..0000000
--- a/fpdfsdk/fpdfview_embeddertest.cpp
+++ /dev/null
@@ -1,601 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cmath>
-#include <limits>
-#include <string>
-
-#include "fpdfsdk/fpdfview_c_api_test.h"
-#include "public/fpdfview.h"
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/utils/path_service.h"
-
-namespace {
-
-class MockDownloadHints : public FX_DOWNLOADHINTS {
- public:
-  static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
-  }
-
-  MockDownloadHints() {
-    FX_DOWNLOADHINTS::version = 1;
-    FX_DOWNLOADHINTS::AddSegment = SAddSegment;
-  }
-
-  ~MockDownloadHints() {}
-};
-
-}  // namespace
-
-TEST(fpdf, CApiTest) {
-  EXPECT_TRUE(CheckPDFiumCApi());
-}
-
-class FPDFViewEmbeddertest : public EmbedderTest {
- protected:
-  void TestRenderPageBitmapWithMatrix(FPDF_PAGE page,
-                                      const int bitmap_width,
-                                      const int bitmap_height,
-                                      const FS_MATRIX& matrix,
-                                      const FS_RECTF& rect,
-                                      const char* expected_md5);
-};
-
-TEST_F(FPDFViewEmbeddertest, Document) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
-  EXPECT_EQ(1, GetPageCount());
-  EXPECT_EQ(0, GetFirstPageNum());
-
-  int version;
-  EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
-  EXPECT_EQ(14, version);
-
-  EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document()));
-  EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
-}
-
-TEST_F(FPDFViewEmbeddertest, LoadNonexistentDocument) {
-  FPDF_DOCUMENT doc = FPDF_LoadDocument("nonexistent_document.pdf", "");
-  ASSERT_FALSE(doc);
-  EXPECT_EQ(static_cast<int>(FPDF_GetLastError()), FPDF_ERR_FILE);
-}
-
-// See bug 465.
-TEST_F(FPDFViewEmbeddertest, EmptyDocument) {
-  EXPECT_TRUE(CreateEmptyDocument());
-
-  {
-    int version = 42;
-    EXPECT_FALSE(FPDF_GetFileVersion(document(), &version));
-    EXPECT_EQ(0, version);
-  }
-
-  {
-#ifndef PDF_ENABLE_XFA
-    const unsigned long kExpected = 0;
-#else
-    const unsigned long kExpected = static_cast<uint32_t>(-1);
-#endif
-    EXPECT_EQ(kExpected, FPDF_GetDocPermissions(document()));
-  }
-
-  EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document()));
-
-  EXPECT_EQ(0, FPDF_GetPageCount(document()));
-
-  EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
-  EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document()));
-  EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
-
-  char buf[100];
-  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
-  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
-
-  EXPECT_EQ(0u, FPDF_CountNamedDests(document()));
-}
-
-TEST_F(FPDFViewEmbeddertest, LinearizedDocument) {
-  EXPECT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf"));
-  int version;
-  EXPECT_TRUE(FPDF_GetFileVersion(document(), &version));
-  EXPECT_EQ(16, version);
-}
-
-TEST_F(FPDFViewEmbeddertest, Page) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-
-  EXPECT_EQ(612.0, FPDF_GetPageWidth(page));
-  EXPECT_EQ(792.0, FPDF_GetPageHeight(page));
-
-  FS_RECTF rect;
-  EXPECT_TRUE(FPDF_GetPageBoundingBox(page, &rect));
-  EXPECT_EQ(0.0, rect.left);
-  EXPECT_EQ(0.0, rect.bottom);
-  EXPECT_EQ(612.0, rect.right);
-  EXPECT_EQ(792.0, rect.top);
-
-  UnloadPage(page);
-  EXPECT_EQ(nullptr, LoadPage(1));
-}
-
-TEST_F(FPDFViewEmbeddertest, ViewerRefDummy) {
-  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
-  EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
-  EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document()));
-  EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
-
-  char buf[100];
-  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
-  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
-}
-
-TEST_F(FPDFViewEmbeddertest, ViewerRef) {
-  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
-  EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
-  EXPECT_EQ(5, FPDF_VIEWERREF_GetNumCopies(document()));
-  EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
-
-  // Test some corner cases.
-  char buf[100];
-  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "", buf, sizeof(buf)));
-  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
-  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
-
-  // Make sure |buf| does not get written into when it appears to be too small.
-  // NOLINTNEXTLINE(runtime/printf)
-  strcpy(buf, "ABCD");
-  EXPECT_EQ(4U, FPDF_VIEWERREF_GetName(document(), "Foo", buf, 1));
-  EXPECT_STREQ("ABCD", buf);
-
-  // Note "Foo" is a different key from "foo".
-  EXPECT_EQ(4U,
-            FPDF_VIEWERREF_GetName(document(), "Foo", nullptr, sizeof(buf)));
-  ASSERT_EQ(4U, FPDF_VIEWERREF_GetName(document(), "Foo", buf, sizeof(buf)));
-  EXPECT_STREQ("foo", buf);
-
-  // Try to retrieve a boolean and an integer.
-  EXPECT_EQ(
-      0U, FPDF_VIEWERREF_GetName(document(), "HideToolbar", buf, sizeof(buf)));
-  EXPECT_EQ(0U,
-            FPDF_VIEWERREF_GetName(document(), "NumCopies", buf, sizeof(buf)));
-
-  // Try more valid cases.
-  ASSERT_EQ(4U,
-            FPDF_VIEWERREF_GetName(document(), "Direction", buf, sizeof(buf)));
-  EXPECT_STREQ("R2L", buf);
-  ASSERT_EQ(8U,
-            FPDF_VIEWERREF_GetName(document(), "ViewArea", buf, sizeof(buf)));
-  EXPECT_STREQ("CropBox", buf);
-}
-
-TEST_F(FPDFViewEmbeddertest, NamedDests) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
-  long buffer_size;
-  char fixed_buffer[512];
-  FPDF_DEST dest;
-
-  // Query the size of the first item.
-  buffer_size = 2000000;  // Absurdly large, check not used for this case.
-  dest = FPDF_GetNamedDest(document(), 0, nullptr, &buffer_size);
-  EXPECT_NE(nullptr, dest);
-  EXPECT_EQ(12, buffer_size);
-
-  // Try to retrieve the first item with too small a buffer.
-  buffer_size = 10;
-  dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
-  EXPECT_EQ(-1, buffer_size);
-
-  // Try to retrieve the first item with correctly sized buffer. Item is
-  // taken from Dests NameTree in named_dests.pdf.
-  buffer_size = 12;
-  dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
-  EXPECT_EQ(12, buffer_size);
-  EXPECT_EQ(std::string("F\0i\0r\0s\0t\0\0\0", 12),
-            std::string(fixed_buffer, buffer_size));
-
-  // Try to retrieve the second item with ample buffer. Item is taken
-  // from Dests NameTree but has a sub-dictionary in named_dests.pdf.
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), 1, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
-  EXPECT_EQ(10, buffer_size);
-  EXPECT_EQ(std::string("N\0e\0x\0t\0\0\0", 10),
-            std::string(fixed_buffer, buffer_size));
-
-  // Try to retrieve third item with ample buffer. Item is taken
-  // from Dests NameTree but has a bad sub-dictionary in named_dests.pdf.
-  // in named_dests.pdf).
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), 2, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
-  EXPECT_EQ(sizeof(fixed_buffer),
-            static_cast<size_t>(buffer_size));  // unmodified.
-
-  // Try to retrieve the forth item with ample buffer. Item is taken
-  // from Dests NameTree but has a vale of the wrong type in named_dests.pdf.
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), 3, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
-  EXPECT_EQ(sizeof(fixed_buffer),
-            static_cast<size_t>(buffer_size));  // unmodified.
-
-  // Try to retrieve fifth item with ample buffer. Item taken from the
-  // old-style Dests dictionary object in named_dests.pdf.
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), 4, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
-  EXPECT_EQ(30, buffer_size);
-  EXPECT_EQ(std::string("F\0i\0r\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 30),
-            std::string(fixed_buffer, buffer_size));
-
-  // Try to retrieve sixth item with ample buffer. Item istaken from the
-  // old-style Dests dictionary object but has a sub-dictionary in
-  // named_dests.pdf.
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), 5, fixed_buffer, &buffer_size);
-  EXPECT_NE(nullptr, dest);
-  EXPECT_EQ(28, buffer_size);
-  EXPECT_EQ(std::string("L\0a\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 28),
-            std::string(fixed_buffer, buffer_size));
-
-  // Try to retrieve non-existent item with ample buffer.
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), 6, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
-  EXPECT_EQ(sizeof(fixed_buffer),
-            static_cast<size_t>(buffer_size));  // unmodified.
-
-  // Try to underflow/overflow the integer index.
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), std::numeric_limits<int>::max(),
-                           fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
-  EXPECT_EQ(sizeof(fixed_buffer),
-            static_cast<size_t>(buffer_size));  // unmodified.
-
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), std::numeric_limits<int>::min(),
-                           fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
-  EXPECT_EQ(sizeof(fixed_buffer),
-            static_cast<size_t>(buffer_size));  // unmodified.
-
-  buffer_size = sizeof(fixed_buffer);
-  dest = FPDF_GetNamedDest(document(), -1, fixed_buffer, &buffer_size);
-  EXPECT_EQ(nullptr, dest);
-  EXPECT_EQ(sizeof(fixed_buffer),
-            static_cast<size_t>(buffer_size));  // unmodified.
-}
-
-TEST_F(FPDFViewEmbeddertest, NamedDestsByName) {
-  EXPECT_TRUE(OpenDocument("named_dests.pdf"));
-
-  // Null pointer returns nullptr.
-  FPDF_DEST dest = FPDF_GetNamedDestByName(document(), nullptr);
-  EXPECT_EQ(nullptr, dest);
-
-  // Empty string returns nullptr.
-  dest = FPDF_GetNamedDestByName(document(), "");
-  EXPECT_EQ(nullptr, dest);
-
-  // Item from Dests NameTree.
-  dest = FPDF_GetNamedDestByName(document(), "First");
-  EXPECT_NE(nullptr, dest);
-
-  long ignore_len = 0;
-  FPDF_DEST dest_by_index =
-      FPDF_GetNamedDest(document(), 0, nullptr, &ignore_len);
-  EXPECT_EQ(dest_by_index, dest);
-
-  // Item from Dests dictionary.
-  dest = FPDF_GetNamedDestByName(document(), "FirstAlternate");
-  EXPECT_NE(nullptr, dest);
-
-  ignore_len = 0;
-  dest_by_index = FPDF_GetNamedDest(document(), 4, nullptr, &ignore_len);
-  EXPECT_EQ(dest_by_index, dest);
-
-  // Bad value type for item from Dests NameTree array.
-  dest = FPDF_GetNamedDestByName(document(), "WrongType");
-  EXPECT_EQ(nullptr, dest);
-
-  // No such destination in either Dest NameTree or dictionary.
-  dest = FPDF_GetNamedDestByName(document(), "Bogus");
-  EXPECT_EQ(nullptr, dest);
-}
-
-// The following tests pass if the document opens without crashing.
-TEST_F(FPDFViewEmbeddertest, Crasher_113) {
-  EXPECT_TRUE(OpenDocument("bug_113.pdf"));
-}
-
-TEST_F(FPDFViewEmbeddertest, Crasher_451830) {
-  // Document is damaged and can't be opened.
-  EXPECT_FALSE(OpenDocument("bug_451830.pdf"));
-}
-
-TEST_F(FPDFViewEmbeddertest, Crasher_452455) {
-  EXPECT_TRUE(OpenDocument("bug_452455.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  UnloadPage(page);
-}
-
-TEST_F(FPDFViewEmbeddertest, Crasher_454695) {
-  // Document is damaged and can't be opened.
-  EXPECT_FALSE(OpenDocument("bug_454695.pdf"));
-}
-
-TEST_F(FPDFViewEmbeddertest, Crasher_572871) {
-  EXPECT_TRUE(OpenDocument("bug_572871.pdf"));
-}
-
-// It tests that document can still be loaded even the trailer has no 'Size'
-// field if other information is right.
-TEST_F(FPDFViewEmbeddertest, Failed_213) {
-  EXPECT_TRUE(OpenDocument("bug_213.pdf"));
-}
-
-// The following tests pass if the document opens without infinite looping.
-TEST_F(FPDFViewEmbeddertest, Hang_298) {
-  EXPECT_FALSE(OpenDocument("bug_298.pdf"));
-}
-
-// Test if the document opens without infinite looping.
-// Previously this test will hang in a loop inside LoadAllCrossRefV4. After
-// the fix, LoadAllCrossRefV4 will return false after detecting a cross
-// reference loop. Cross references will be rebuilt successfully.
-TEST_F(FPDFViewEmbeddertest, CrossRefV4Loop) {
-  EXPECT_TRUE(OpenDocument("bug_xrefv4_loop.pdf"));
-  MockDownloadHints hints;
-
-  // Make sure calling FPDFAvail_IsDocAvail() on this file does not infinite
-  // loop either. See bug 875.
-  int ret = PDF_DATA_NOTAVAIL;
-  while (ret == PDF_DATA_NOTAVAIL)
-    ret = FPDFAvail_IsDocAvail(avail_, &hints);
-  EXPECT_EQ(PDF_DATA_AVAIL, ret);
-}
-
-// The test should pass when circular references to ParseIndirectObject will not
-// cause infinite loop.
-TEST_F(FPDFViewEmbeddertest, Hang_343) {
-  EXPECT_FALSE(OpenDocument("bug_343.pdf"));
-}
-
-// The test should pass when the absence of 'Contents' field in a signature
-// dictionary will not cause an infinite loop in CPDF_SyntaxParser::GetObject().
-TEST_F(FPDFViewEmbeddertest, Hang_344) {
-  EXPECT_FALSE(OpenDocument("bug_344.pdf"));
-}
-
-// The test should pass when there is no infinite recursion in
-// CPDF_SyntaxParser::GetString().
-TEST_F(FPDFViewEmbeddertest, Hang_355) {
-  EXPECT_FALSE(OpenDocument("bug_355.pdf"));
-}
-// The test should pass even when the file has circular references to pages.
-TEST_F(FPDFViewEmbeddertest, Hang_360) {
-  EXPECT_FALSE(OpenDocument("bug_360.pdf"));
-}
-
-void FPDFViewEmbeddertest::TestRenderPageBitmapWithMatrix(
-    FPDF_PAGE page,
-    const int bitmap_width,
-    const int bitmap_height,
-    const FS_MATRIX& matrix,
-    const FS_RECTF& rect,
-    const char* expected_md5) {
-  FPDF_BITMAP bitmap = FPDFBitmap_Create(bitmap_width, bitmap_height, 0);
-  FPDFBitmap_FillRect(bitmap, 0, 0, bitmap_width, bitmap_height, 0xFFFFFFFF);
-  FPDF_RenderPageBitmapWithMatrix(bitmap, page, &matrix, &rect, 0);
-  CompareBitmap(bitmap, bitmap_width, bitmap_height, expected_md5);
-  FPDFBitmap_Destroy(bitmap);
-}
-
-TEST_F(FPDFViewEmbeddertest, FPDF_RenderPageBitmapWithMatrix) {
-  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
-  const char kClippedMD5[] = "a84cab93c102b9b9290fba3047ba702c";
-  const char kTopLeftQuarterMD5[] = "f11a11137c8834389e31cf555a4a6979";
-  const char kHoriStretchedMD5[] = "48ef9205941ed19691ccfa00d717187e";
-  const char kRotated90ClockwiseMD5[] = "d8da2c7bf77521550d0f2752b9cf3482";
-  const char kRotated180ClockwiseMD5[] = "0113386bb0bd45125bacc6dee78bfe78";
-  const char kRotated270ClockwiseMD5[] = "a287e0f74ce203699cda89f9cc97a240";
-  const char kMirrorHoriMD5[] = "6e8d7a6fde39d8e720fb9e620102918c";
-  const char kMirrorVertMD5[] = "8f3a555ef9c0d5031831ae3715273707";
-  const char kLargerTopLeftQuarterMD5[] = "172a2f4adafbadbe98017b1c025b9e27";
-  const char kLargerMD5[] = "c806145641c3e6fc4e022c7065343749";
-  const char kLargerClippedMD5[] = "091d3b1c7933c8f6945eb2cb41e588e9";
-  const char kLargerRotatedMD5[] = "115f13353ebfc82ddb392d1f0059eb12";
-  const char kLargerRotatedLandscapeMD5[] = "c901239d17d84ac84cb6f2124da71b0d";
-  const char kLargerRotatedDiagonalMD5[] = "3d62417468bdaff0eb14391a0c30a3b1";
-  const char kTileMD5[] = "0a190003c97220bf8877684c8d7e89cf";
-
-  EXPECT_TRUE(OpenDocument("rectangles.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
-  const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
-  EXPECT_EQ(200, page_width);
-  EXPECT_EQ(300, page_height);
-
-  FPDF_BITMAP bitmap = RenderPage(page);
-  CompareBitmap(bitmap, page_width, page_height, kOriginalMD5);
-  FPDFBitmap_Destroy(bitmap);
-
-  FS_RECTF page_rect{0, 0, page_width, page_height};
-
-  // Try rendering with an identity matrix. The output should be the same as
-  // the RenderPage() output.
-  FS_MATRIX identity_matrix{1, 0, 0, 1, 0, 0};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix,
-                                 page_rect, kOriginalMD5);
-
-  // Again render with an identity matrix but with a smaller clipping rect.
-  FS_RECTF middle_of_page_rect{page_width / 4, page_height / 4,
-                               page_width * 3 / 4, page_height * 3 / 4};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix,
-                                 middle_of_page_rect, kClippedMD5);
-
-  // Now render again with the image scaled smaller.
-  FS_MATRIX half_scale_matrix{0.5, 0, 0, 0.5, 0, 0};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 half_scale_matrix, page_rect,
-                                 kTopLeftQuarterMD5);
-
-  // Now render again with the image scaled larger horizontally (the right half
-  // will be clipped).
-  FS_MATRIX stretch_x_matrix{2, 0, 0, 1, 0, 0};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 stretch_x_matrix, page_rect,
-                                 kHoriStretchedMD5);
-
-  // Try a 90 degree rotation clockwise but with the same bitmap size, so part
-  // will be clipped.
-  FS_MATRIX rotate_90_matrix{0, 1, -1, 0, page_width, 0};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 rotate_90_matrix, page_rect,
-                                 kRotated90ClockwiseMD5);
-
-  // 180 degree rotation clockwise.
-  FS_MATRIX rotate_180_matrix{-1, 0, 0, -1, page_width, page_height};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 rotate_180_matrix, page_rect,
-                                 kRotated180ClockwiseMD5);
-
-  // 270 degree rotation clockwise.
-  FS_MATRIX rotate_270_matrix{0, -1, 1, 0, 0, page_width};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 rotate_270_matrix, page_rect,
-                                 kRotated270ClockwiseMD5);
-
-  // Mirror horizontally.
-  FS_MATRIX mirror_hori_matrix{-1, 0, 0, 1, page_width, 0};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 mirror_hori_matrix, page_rect, kMirrorHoriMD5);
-
-  // Mirror vertically.
-  FS_MATRIX mirror_vert_matrix{1, 0, 0, -1, 0, page_height};
-  TestRenderPageBitmapWithMatrix(page, page_width, page_height,
-                                 mirror_vert_matrix, page_rect, kMirrorVertMD5);
-
-  // Tests rendering to a larger bitmap
-  const int bitmap_width = page_width * 2;
-  const int bitmap_height = page_height * 2;
-
-  // Render using an identity matrix and the whole bitmap area as clipping rect.
-  FS_RECTF bitmap_rect{0, 0, bitmap_width, bitmap_height};
-  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
-                                 identity_matrix, bitmap_rect,
-                                 kLargerTopLeftQuarterMD5);
-
-  // Render using a scaling matrix to fill the larger bitmap.
-  FS_MATRIX double_scale_matrix{2, 0, 0, 2, 0, 0};
-  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
-                                 double_scale_matrix, bitmap_rect, kLargerMD5);
-
-  // Render the larger image again but with clipping.
-  FS_RECTF middle_of_bitmap_rect{bitmap_width / 4, bitmap_height / 4,
-                                 bitmap_width * 3 / 4, bitmap_height * 3 / 4};
-  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
-                                 double_scale_matrix, middle_of_bitmap_rect,
-                                 kLargerClippedMD5);
-
-  // On the larger bitmap, try a 90 degree rotation but with the same bitmap
-  // size, so part will be clipped.
-  FS_MATRIX rotate_90_scale_2_matrix{0, 2, -2, 0, bitmap_width, 0};
-  TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height,
-                                 rotate_90_scale_2_matrix, bitmap_rect,
-                                 kLargerRotatedMD5);
-
-  // On the larger bitmap, apply 90 degree rotation to a bitmap with the
-  // appropriate dimensions.
-  const int landscape_bitmap_width = bitmap_height;
-  const int landscape_bitmap_height = bitmap_width;
-  FS_RECTF landscape_bitmap_rect{0, 0, landscape_bitmap_width,
-                                 landscape_bitmap_height};
-  FS_MATRIX landscape_rotate_90_scale_2_matrix{
-      0, 2, -2, 0, landscape_bitmap_width, 0};
-  TestRenderPageBitmapWithMatrix(
-      page, landscape_bitmap_width, landscape_bitmap_height,
-      landscape_rotate_90_scale_2_matrix, landscape_bitmap_rect,
-      kLargerRotatedLandscapeMD5);
-
-  // On the larger bitmap, apply 45 degree rotation to a bitmap with the
-  // appropriate dimensions.
-  const float sqrt2 = 1.41421356f;
-  const int diagonal_bitmap_size = ceil((bitmap_width + bitmap_height) / sqrt2);
-  FS_RECTF diagonal_bitmap_rect{0, 0, diagonal_bitmap_size,
-                                diagonal_bitmap_size};
-  FS_MATRIX rotate_45_scale_2_matrix{
-      sqrt2, sqrt2, -sqrt2, sqrt2, bitmap_height / sqrt2, 0};
-  TestRenderPageBitmapWithMatrix(page, diagonal_bitmap_size,
-                                 diagonal_bitmap_size, rotate_45_scale_2_matrix,
-                                 diagonal_bitmap_rect,
-                                 kLargerRotatedDiagonalMD5);
-
-  // Render the (2, 1) tile of the page (third column, second row) when the page
-  // is divided in 50x50 pixel tiles. The tile is scaled by a factor of 7.
-  const float scale = 7.0;
-  const int tile_size = 50;
-  const int tile_x = 2;
-  const int tile_y = 1;
-  int tile_bitmap_size = scale * tile_size;
-  FS_RECTF tile_bitmap_rect{0, 0, tile_bitmap_size, tile_bitmap_size};
-  FS_MATRIX tile_2_1_matrix{scale,
-                            0,
-                            0,
-                            scale,
-                            -tile_x * tile_bitmap_size,
-                            -tile_y * tile_bitmap_size};
-  TestRenderPageBitmapWithMatrix(page, tile_bitmap_size, tile_bitmap_size,
-                                 tile_2_1_matrix, tile_bitmap_rect, kTileMD5);
-
-  UnloadPage(page);
-}
-
-class UnSupRecordDelegate : public EmbedderTest::Delegate {
- public:
-  UnSupRecordDelegate() : type_(-1) {}
-  ~UnSupRecordDelegate() override {}
-
-  void UnsupportedHandler(int type) override { type_ = type; }
-
-  int type_;
-};
-
-TEST_F(FPDFViewEmbeddertest, UnSupportedOperations_NotFound) {
-  UnSupRecordDelegate delegate;
-  SetDelegate(&delegate);
-  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
-  EXPECT_EQ(delegate.type_, -1);
-  SetDelegate(nullptr);
-}
-
-TEST_F(FPDFViewEmbeddertest, UnSupportedOperations_LoadCustomDocument) {
-  UnSupRecordDelegate delegate;
-  SetDelegate(&delegate);
-  ASSERT_TRUE(OpenDocument("unsupported_feature.pdf"));
-  EXPECT_EQ(FPDF_UNSP_DOC_PORTABLECOLLECTION, delegate.type_);
-  SetDelegate(nullptr);
-}
-
-TEST_F(FPDFViewEmbeddertest, UnSupportedOperations_LoadDocument) {
-  std::string file_path;
-  ASSERT_TRUE(
-      PathService::GetTestFilePath("unsupported_feature.pdf", &file_path));
-
-  UnSupRecordDelegate delegate;
-  SetDelegate(&delegate);
-  FPDF_DOCUMENT doc = FPDF_LoadDocument(file_path.c_str(), "");
-  EXPECT_TRUE(doc != nullptr);
-  EXPECT_EQ(FPDF_UNSP_DOC_PORTABLECOLLECTION, delegate.type_);
-  FPDF_CloseDocument(doc);
-  SetDelegate(nullptr);
-}
diff --git a/fpdfsdk/fpdfxfa/BUILD.gn b/fpdfsdk/fpdfxfa/BUILD.gn
new file mode 100644
index 0000000..4e88c4c
--- /dev/null
+++ b/fpdfsdk/fpdfxfa/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fpdfxfa") {
+  sources = [
+    "cpdfxfa_context.cpp",
+    "cpdfxfa_context.h",
+    "cpdfxfa_docenvironment.cpp",
+    "cpdfxfa_docenvironment.h",
+    "cpdfxfa_page.cpp",
+    "cpdfxfa_page.h",
+    "cpdfxfa_widget.cpp",
+    "cpdfxfa_widget.h",
+    "cpdfxfa_widgethandler.cpp",
+    "cpdfxfa_widgethandler.h",
+  ]
+  deps = [
+    "../../:pdfium_public_headers",
+    "../../core/fpdfapi/page",
+    "../../core/fpdfapi/parser",
+    "../../core/fpdfapi/render",
+    "../../core/fxcrt",
+    "../../fxjs",
+    "../../xfa/fwl",
+    "../../xfa/fxfa",
+    "../../xfa/fxfa/parser",
+    "../../xfa/fxgraphics",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "cpdfxfa_docenvironment_embeddertest.cpp" ]
+  configs = [ "//v8:external_startup_data" ]
+  deps = [ "../../fxjs" ]
+  pdfium_root_dir = "../../"
+}
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
index d05e2f6..7cb4a2c 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
@@ -9,19 +9,18 @@
 #include <algorithm>
 #include <utility>
 
+#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_seekablemultistream.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
-#include "fpdfsdk/fsdk_define.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/ijs_runtime.h"
 #include "public/fpdf_formfill.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
-#include "xfa/fgas/font/cfgas_defaultfontmanager.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
@@ -29,17 +28,62 @@
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
 #include "xfa/fxfa/cxfa_fontmgr.h"
+#include "xfa/fxfa/cxfa_readynodeiterator.h"
 
-#ifndef _WIN32
-extern void SetLastError(int err);
-extern int GetLastError();
-#endif
+namespace {
 
-CPDFXFA_Context::CPDFXFA_Context(std::unique_ptr<CPDF_Document> pPDFDoc)
-    : m_pPDFDoc(std::move(pPDFDoc)),
+bool IsValidAlertButton(int type) {
+  return type == JSPLATFORM_ALERT_BUTTON_OK ||
+         type == JSPLATFORM_ALERT_BUTTON_OKCANCEL ||
+         type == JSPLATFORM_ALERT_BUTTON_YESNO ||
+         type == JSPLATFORM_ALERT_BUTTON_YESNOCANCEL;
+}
+
+bool IsValidAlertIcon(int type) {
+  return type == JSPLATFORM_ALERT_ICON_ERROR ||
+         type == JSPLATFORM_ALERT_ICON_WARNING ||
+         type == JSPLATFORM_ALERT_ICON_QUESTION ||
+         type == JSPLATFORM_ALERT_ICON_STATUS ||
+         type == JSPLATFORM_ALERT_ICON_ASTERISK;
+}
+
+RetainPtr<CPDF_SeekableMultiStream> CreateXFAMultiStream(
+    const CPDF_Document* pPDFDoc) {
+  const CPDF_Dictionary* pRoot = pPDFDoc->GetRoot();
+  if (!pRoot)
+    return nullptr;
+
+  const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  if (!pAcroForm)
+    return nullptr;
+
+  const CPDF_Object* pElementXFA = pAcroForm->GetDirectObjectFor("XFA");
+  if (!pElementXFA)
+    return nullptr;
+
+  std::vector<const CPDF_Stream*> xfaStreams;
+  if (pElementXFA->IsArray()) {
+    const CPDF_Array* pXFAArray = pElementXFA->AsArray();
+    for (size_t i = 0; i < pXFAArray->size() / 2; i++) {
+      if (const CPDF_Stream* pStream = pXFAArray->GetStreamAt(i * 2 + 1))
+        xfaStreams.push_back(pStream);
+    }
+  } else if (pElementXFA->IsStream()) {
+    xfaStreams.push_back(pElementXFA->AsStream());
+  }
+  if (xfaStreams.empty())
+    return nullptr;
+
+  return pdfium::MakeRetain<CPDF_SeekableMultiStream>(xfaStreams);
+}
+
+}  // namespace
+
+CPDFXFA_Context::CPDFXFA_Context(CPDF_Document* pPDFDoc)
+    : m_pPDFDoc(pPDFDoc),
       m_pXFAApp(pdfium::MakeUnique<CXFA_FFApp>(this)),
       m_DocEnv(this) {
-  m_pXFAApp->SetDefaultFontMgr(pdfium::MakeUnique<CFGAS_DefaultFontManager>());
+  ASSERT(m_pPDFDoc);
 }
 
 CPDFXFA_Context::~CPDFXFA_Context() {
@@ -52,7 +96,7 @@
     m_pFormFillEnv->ClearAllFocusedAnnots();
     // Once we're deleted the FormFillEnvironment will point at a bad underlying
     // doc so we need to reset it ...
-    m_pFormFillEnv->ResetXFADocument();
+    m_pFormFillEnv->GetPDFDocument()->SetExtension(nullptr);
     m_pFormFillEnv.Reset();
   }
 
@@ -64,7 +108,6 @@
     return;
 
   m_pXFADocView = nullptr;
-  m_pXFADoc->CloseDoc();
   m_pXFADoc.reset();
 }
 
@@ -73,39 +116,44 @@
   // The layout data can have pointers back into the script context. That
   // context will be different if the form fill environment closes, so, force
   // the layout data to clear.
-  if (m_pXFADoc && m_pXFADoc->GetXFADoc())
+  if (m_pXFADoc && m_pXFADoc->GetXFADoc()) {
+    // The CPDF_XFADocView has a pointer to the CXFA_LayoutProcessor which is
+    // owned by the CXFA_Document. The Layout Processor will be freed with the
+    // ClearLayoutData() call. Make sure the doc view has already released the
+    // pointer.
+    if (m_pXFADocView)
+      m_pXFADocView->ResetLayoutProcessor();
+
     m_pXFADoc->GetXFADoc()->ClearLayoutData();
+  }
 
   m_pFormFillEnv.Reset(pFormFillEnv);
 }
 
 bool CPDFXFA_Context::LoadXFADoc() {
   m_nLoadStatus = FXFA_LOADSTATUS_LOADING;
-  if (!m_pPDFDoc)
-    return false;
-
   m_XFAPageList.clear();
 
-  CXFA_FFApp* pApp = GetXFAApp();
-  if (!pApp)
+  auto stream = CreateXFAMultiStream(m_pPDFDoc.Get());
+  if (!stream) {
+    FXSYS_SetLastError(FPDF_ERR_XFALOAD);
     return false;
+  }
 
-  m_pXFADoc = pApp->CreateDoc(&m_DocEnv, m_pPDFDoc.get());
+  m_pXFADoc = CXFA_FFDoc::CreateAndOpen(m_pXFAApp.get(), &m_DocEnv,
+                                        m_pPDFDoc.Get(), stream);
   if (!m_pXFADoc) {
-    SetLastError(FPDF_ERR_XFALOAD);
+    FXSYS_SetLastError(FPDF_ERR_XFALOAD);
     return false;
   }
 
-  m_pXFADoc->StartLoad();
-  int iStatus = m_pXFADoc->DoLoad();
-  if (iStatus != XFA_PARSESTATUS_Done) {
-    CloseXFADoc();
-    SetLastError(FPDF_ERR_XFALOAD);
+  CJS_Runtime* actual_runtime = GetCJSRuntime();  // Null if a stub.
+  if (!actual_runtime) {
+    FXSYS_SetLastError(FPDF_ERR_XFALOAD);
     return false;
   }
-  m_pXFADoc->StopLoad();
-  m_pXFADoc->GetXFADoc()->InitScriptContext(GetJSERuntime());
 
+  m_pXFADoc->GetXFADoc()->InitScriptContext(actual_runtime);
   if (m_pXFADoc->GetFormType() == FormType::kXFAFull)
     m_FormType = FormType::kXFAFull;
   else
@@ -114,31 +162,26 @@
   m_pXFADocView = m_pXFADoc->CreateDocView();
   if (m_pXFADocView->StartLayout() < 0) {
     CloseXFADoc();
-    SetLastError(FPDF_ERR_XFALAYOUT);
+    FXSYS_SetLastError(FPDF_ERR_XFALAYOUT);
     return false;
   }
 
   m_pXFADocView->DoLayout();
   m_pXFADocView->StopLayout();
   m_nLoadStatus = FXFA_LOADSTATUS_LOADED;
-
   return true;
 }
 
 int CPDFXFA_Context::GetPageCount() const {
-  if (!m_pPDFDoc && !m_pXFADoc)
-    return 0;
-
   switch (m_FormType) {
     case FormType::kNone:
     case FormType::kAcroForm:
     case FormType::kXFAForeground:
-      if (m_pPDFDoc)
-        return m_pPDFDoc->GetPageCount();
+      return m_pPDFDoc->GetPageCount();
     case FormType::kXFAFull:
-      if (m_pXFADoc)
-        return m_pXFADocView->CountPageViews();
+      return m_pXFADoc ? m_pXFADocView->CountPageViews() : 0;
   }
+  NOTREACHED();
   return 0;
 }
 
@@ -154,7 +197,7 @@
     m_XFAPageList.resize(m_nPageCount);
   }
 
-  auto pPage = pdfium::MakeRetain<CPDFXFA_Page>(this, page_index);
+  auto pPage = pdfium::MakeRetain<CPDFXFA_Page>(GetPDFDoc(), page_index);
   if (!pPage->LoadPage())
     return nullptr;
 
@@ -182,30 +225,48 @@
   return nullptr;
 }
 
+CPDF_Document* CPDFXFA_Context::GetPDFDoc() const {
+  return m_pPDFDoc.Get();
+}
+
 void CPDFXFA_Context::DeletePage(int page_index) {
   // Delete from the document first because, if GetPage was never called for
   // this |page_index| then |m_XFAPageList| may have size < |page_index| even
   // if it's a valid page in the document.
-  if (m_pPDFDoc)
-    m_pPDFDoc->DeletePage(page_index);
+  m_pPDFDoc->DeletePage(page_index);
 
   if (pdfium::IndexInBounds(m_XFAPageList, page_index))
     m_XFAPageList[page_index].Reset();
 }
 
+uint32_t CPDFXFA_Context::GetUserPermissions() const {
+  // See https://bugs.chromium.org/p/pdfium/issues/detail?id=499
+  return 0xFFFFFFFF;
+}
+
+bool CPDFXFA_Context::ContainsExtensionForm() const {
+  return m_FormType == FormType::kXFAFull ||
+         m_FormType == FormType::kXFAForeground;
+}
+
+bool CPDFXFA_Context::ContainsExtensionFullForm() const {
+  return m_FormType == FormType::kXFAFull;
+}
+
+bool CPDFXFA_Context::ContainsExtensionForegroundForm() const {
+  return m_FormType == FormType::kXFAForeground;
+}
+
 void CPDFXFA_Context::ClearChangeMark() {
   if (m_pFormFillEnv)
     m_pFormFillEnv->ClearChangeMark();
 }
 
-v8::Isolate* CPDFXFA_Context::GetJSERuntime() const {
+CJS_Runtime* CPDFXFA_Context::GetCJSRuntime() const {
   if (!m_pFormFillEnv)
     return nullptr;
 
-  // XFA requires V8, if we have V8 then we have a CJS_Runtime and not the stub.
-  CJS_Runtime* runtime =
-      static_cast<CJS_Runtime*>(m_pFormFillEnv->GetJSRuntime());
-  return runtime->GetIsolate();
+  return m_pFormFillEnv->GetIJSRuntime()->AsCJSRuntime();
 }
 
 WideString CPDFXFA_Context::GetAppTitle() const {
@@ -213,15 +274,15 @@
 }
 
 WideString CPDFXFA_Context::GetAppName() {
-  return m_pFormFillEnv ? m_pFormFillEnv->FFI_GetAppName() : L"";
+  return m_pFormFillEnv ? m_pFormFillEnv->FFI_GetAppName() : WideString();
 }
 
 WideString CPDFXFA_Context::GetLanguage() {
-  return m_pFormFillEnv ? m_pFormFillEnv->GetLanguage() : L"";
+  return m_pFormFillEnv ? m_pFormFillEnv->GetLanguage() : WideString();
 }
 
 WideString CPDFXFA_Context::GetPlatform() {
-  return m_pFormFillEnv ? m_pFormFillEnv->GetPlatform() : L"";
+  return m_pFormFillEnv ? m_pFormFillEnv->GetPlatform() : WideString();
 }
 
 void CPDFXFA_Context::Beep(uint32_t dwType) {
@@ -233,52 +294,15 @@
                                 const WideString& wsTitle,
                                 uint32_t dwIconType,
                                 uint32_t dwButtonType) {
-  if (!m_pFormFillEnv)
+  if (!m_pFormFillEnv || m_nLoadStatus != FXFA_LOADSTATUS_LOADED)
     return -1;
 
-  uint32_t iconType = 0;
-  int iButtonType = 0;
-  switch (dwIconType) {
-    case XFA_MBICON_Error:
-      iconType |= 0;
-      break;
-    case XFA_MBICON_Warning:
-      iconType |= 1;
-      break;
-    case XFA_MBICON_Question:
-      iconType |= 2;
-      break;
-    case XFA_MBICON_Status:
-      iconType |= 3;
-      break;
-  }
-  switch (dwButtonType) {
-    case XFA_MB_OK:
-      iButtonType |= 0;
-      break;
-    case XFA_MB_OKCancel:
-      iButtonType |= 1;
-      break;
-    case XFA_MB_YesNo:
-      iButtonType |= 2;
-      break;
-    case XFA_MB_YesNoCancel:
-      iButtonType |= 3;
-      break;
-  }
-  int32_t iRet = m_pFormFillEnv->JS_appAlert(wsMessage.c_str(), wsTitle.c_str(),
-                                             iButtonType, iconType);
-  switch (iRet) {
-    case 1:
-      return XFA_IDOK;
-    case 2:
-      return XFA_IDCancel;
-    case 3:
-      return XFA_IDNo;
-    case 4:
-      return XFA_IDYes;
-  }
-  return XFA_IDYes;
+  int iconType =
+      IsValidAlertIcon(dwIconType) ? dwIconType : JSPLATFORM_ALERT_ICON_DEFAULT;
+  int iButtonType = IsValidAlertButton(dwButtonType)
+                        ? dwButtonType
+                        : JSPLATFORM_ALERT_BUTTON_DEFAULT;
+  return m_pFormFillEnv->JS_appAlert(wsMessage, wsTitle, iButtonType, iconType);
 }
 
 WideString CPDFXFA_Context::Response(const WideString& wsQuestion,
@@ -290,9 +314,9 @@
 
   int nLength = 2048;
   std::vector<uint8_t> pBuff(nLength);
-  nLength = m_pFormFillEnv->JS_appResponse(wsQuestion.c_str(), wsTitle.c_str(),
-                                           wsDefaultAnswer.c_str(), nullptr,
-                                           bMark, pBuff.data(), nLength);
+  nLength = m_pFormFillEnv->JS_appResponse(wsQuestion, wsTitle, wsDefaultAnswer,
+                                           WideString(), bMark, pBuff.data(),
+                                           nLength);
   if (nLength <= 0)
     return WideString();
 
@@ -305,8 +329,7 @@
 
 RetainPtr<IFX_SeekableReadStream> CPDFXFA_Context::DownloadURL(
     const WideString& wsURL) {
-  return m_pFormFillEnv ? m_pFormFillEnv->DownloadFromURL(wsURL.c_str())
-                        : nullptr;
+  return m_pFormFillEnv ? m_pFormFillEnv->DownloadFromURL(wsURL) : nullptr;
 }
 
 bool CPDFXFA_Context::PostRequestURL(const WideString& wsURL,
@@ -318,9 +341,8 @@
   if (!m_pFormFillEnv)
     return false;
 
-  wsResponse = m_pFormFillEnv->PostRequestURL(
-      wsURL.c_str(), wsData.c_str(), wsContentType.c_str(), wsEncode.c_str(),
-      wsHeader.c_str());
+  wsResponse = m_pFormFillEnv->PostRequestURL(wsURL, wsData, wsContentType,
+                                              wsEncode, wsHeader);
   return true;
 }
 
@@ -328,13 +350,69 @@
                                     const WideString& wsData,
                                     const WideString& wsEncode) {
   return m_pFormFillEnv &&
-         m_pFormFillEnv->PutRequestURL(wsURL.c_str(), wsData.c_str(),
-                                       wsEncode.c_str());
+         m_pFormFillEnv->PutRequestURL(wsURL, wsData, wsEncode);
 }
 
-IFWL_AdapterTimerMgr* CPDFXFA_Context::GetTimerMgr() {
-  CXFA_FWLAdapterTimerMgr* pAdapter = nullptr;
-  if (m_pFormFillEnv)
-    pAdapter = new CXFA_FWLAdapterTimerMgr(m_pFormFillEnv.Get());
-  return pAdapter;
+TimerHandlerIface* CPDFXFA_Context::GetTimerHandler() const {
+  return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
+}
+
+bool CPDFXFA_Context::SaveDatasetsPackage(
+    const RetainPtr<IFX_SeekableStream>& pStream) {
+  return SavePackage(pStream, XFA_HASHCODE_Datasets);
+}
+
+bool CPDFXFA_Context::SaveFormPackage(
+    const RetainPtr<IFX_SeekableStream>& pStream) {
+  return SavePackage(pStream, XFA_HASHCODE_Form);
+}
+
+bool CPDFXFA_Context::SavePackage(const RetainPtr<IFX_SeekableStream>& pStream,
+                                  XFA_HashCode code) {
+  CXFA_FFDocView* pXFADocView = GetXFADocView();
+  if (!pXFADocView)
+    return false;
+
+  CXFA_FFDoc* ffdoc = pXFADocView->GetDoc();
+  return ffdoc->SavePackage(ToNode(ffdoc->GetXFADoc()->GetXFAObject(code)),
+                            pStream);
+}
+
+void CPDFXFA_Context::SendPostSaveToXFADoc() {
+  if (!ContainsExtensionForm())
+    return;
+
+  CXFA_FFDocView* pXFADocView = GetXFADocView();
+  if (!pXFADocView)
+    return;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
+  auto it = pXFADocView->CreateReadyNodeIterator();
+  while (CXFA_Node* pNode = it->MoveToNext()) {
+    CXFA_EventParam preParam;
+    preParam.m_eType = XFA_EVENT_PostSave;
+    pWidgetHandler->ProcessEvent(pNode, &preParam);
+  }
+  pXFADocView->UpdateDocView();
+  ClearChangeMark();
+}
+
+void CPDFXFA_Context::SendPreSaveToXFADoc(
+    std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
+  if (!ContainsExtensionForm())
+    return;
+
+  CXFA_FFDocView* pXFADocView = GetXFADocView();
+  if (!pXFADocView)
+    return;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
+  auto it = pXFADocView->CreateReadyNodeIterator();
+  while (CXFA_Node* pNode = it->MoveToNext()) {
+    CXFA_EventParam preParam;
+    preParam.m_eType = XFA_EVENT_PreSave;
+    pWidgetHandler->ProcessEvent(pNode, &preParam);
+  }
+  pXFADocView->UpdateDocView();
+  return;
 }
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.h b/fpdfsdk/fpdfxfa/cpdfxfa_context.h
index acef8f2..0f6a14b 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.h
@@ -10,15 +10,17 @@
 #include <memory>
 #include <vector>
 
+#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/observable.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/timerhandler_iface.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 
 class CJS_Runtime;
-class CPDFSDK_FormFillEnvironment;
 class CXFA_FFDocHandler;
 class IJS_EventContext;
 class IJS_Runtime;
@@ -31,40 +33,39 @@
   FXFA_LOADSTATUS_CLOSED
 };
 
-class CPDFXFA_Context : public IXFA_AppProvider {
+class CPDFXFA_Context final : public CPDF_Document::Extension,
+                              public IXFA_AppProvider {
  public:
-  explicit CPDFXFA_Context(std::unique_ptr<CPDF_Document> pPDFDoc);
+  explicit CPDFXFA_Context(CPDF_Document* pPDFDoc);
   ~CPDFXFA_Context() override;
 
   bool LoadXFADoc();
-  CPDF_Document* GetPDFDoc() { return m_pPDFDoc.get(); }
   CXFA_FFDoc* GetXFADoc() { return m_pXFADoc.get(); }
-  CXFA_FFDocView* GetXFADocView() { return m_pXFADocView.Get(); }
+  CXFA_FFDocView* GetXFADocView() const { return m_pXFADocView.Get(); }
   FormType GetFormType() const { return m_FormType; }
-  bool ContainsXFAForm() const {
-    return m_FormType == FormType::kXFAFull ||
-           m_FormType == FormType::kXFAForeground;
-  }
-  v8::Isolate* GetJSERuntime() const;
-  CXFA_FFApp* GetXFAApp() { return m_pXFAApp.get(); }
-
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
     return m_pFormFillEnv.Get();
   }
   void SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv);
 
-  int GetPageCount() const;
-  void DeletePage(int page_index);
   RetainPtr<CPDFXFA_Page> GetXFAPage(int page_index);
   RetainPtr<CPDFXFA_Page> GetXFAPage(CXFA_FFPageView* pPage) const;
   void ClearChangeMark();
 
+  // CPDF_Document::Extension:
+  CPDF_Document* GetPDFDoc() const override;
+  int GetPageCount() const override;
+  void DeletePage(int page_index) override;
+  uint32_t GetUserPermissions() const override;
+  bool ContainsExtensionForm() const override;
+  bool ContainsExtensionFullForm() const override;
+  bool ContainsExtensionForegroundForm() const override;
+
   // IFXA_AppProvider:
   WideString GetLanguage() override;
   WideString GetPlatform() override;
   WideString GetAppName() override;
   WideString GetAppTitle() const override;
-
   void Beep(uint32_t dwType) override;
   int32_t MsgBox(const WideString& wsMessage,
                  const WideString& wsTitle,
@@ -85,10 +86,15 @@
   bool PutRequestURL(const WideString& wsURL,
                      const WideString& wsData,
                      const WideString& wsEncode) override;
+  TimerHandlerIface* GetTimerHandler() const override;
 
-  IFWL_AdapterTimerMgr* GetTimerMgr() override;
+  bool SaveDatasetsPackage(const RetainPtr<IFX_SeekableStream>& pStream);
+  bool SaveFormPackage(const RetainPtr<IFX_SeekableStream>& pStream);
+  void SendPostSaveToXFADoc();
+  void SendPreSaveToXFADoc(
+      std::vector<RetainPtr<IFX_SeekableStream>>* fileList);
 
- protected:
+ private:
   friend class CPDFXFA_DocEnvironment;
 
   int GetOriginalPageCount() const { return m_nPageCount; }
@@ -102,15 +108,17 @@
     return &m_XFAPageList;
   }
 
- private:
+  CJS_Runtime* GetCJSRuntime() const;
+  bool SavePackage(const RetainPtr<IFX_SeekableStream>& pStream,
+                   XFA_HashCode code);
   void CloseXFADoc();
 
   FormType m_FormType = FormType::kNone;
-  std::unique_ptr<CPDF_Document> m_pPDFDoc;
+  UnownedPtr<CPDF_Document> const m_pPDFDoc;
   std::unique_ptr<CXFA_FFDoc> m_pXFADoc;
-  Observable<CPDFSDK_FormFillEnvironment>::ObservedPtr m_pFormFillEnv;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
   UnownedPtr<CXFA_FFDocView> m_pXFADocView;
-  std::unique_ptr<CXFA_FFApp> m_pXFAApp;
+  std::unique_ptr<CXFA_FFApp> const m_pXFAApp;
   std::unique_ptr<CJS_Runtime> m_pRuntime;
   std::vector<RetainPtr<CPDFXFA_Page>> m_XFAPageList;
   LoadStatus m_nLoadStatus = FXFA_LOADSTATUS_PRELOAD;
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp
index 4ebe8f9..11d1e49 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp
@@ -7,21 +7,25 @@
 #include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h"
 
 #include <memory>
+#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/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
-#include "fxjs/ijs_runtime.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
-#include "xfa/fxfa/cxfa_widgetacciterator.h"
+#include "xfa/fxfa/cxfa_readynodeiterator.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_submit.h"
 
 #define IDS_XFA_Validate_Input                                          \
@@ -106,7 +110,7 @@
                                          float fMinPopup,
                                          float fMaxPopup,
                                          const CFX_RectF& rtAnchor,
-                                         CFX_RectF& rtPopup) {
+                                         CFX_RectF* pPopupRect) {
   if (!hWidget)
     return false;
 
@@ -122,103 +126,117 @@
   if (!pFormFillEnv)
     return false;
 
-  FS_RECTF pageViewRect = {0.0f, 0.0f, 0.0f, 0.0f};
-  pFormFillEnv->GetPageViewRect(pPage.Get(), pageViewRect);
-
-  int t1;
-  int t2;
-  CFX_FloatRect rcAnchor = rtAnchor.ToFloatRect();
+  FS_RECTF page_view_rect = pFormFillEnv->GetPageViewRect(pPage.Get());
   int nRotate = hWidget->GetNode()->GetRotate();
+
+  int space_available_below_anchor;
+  int space_available_above_anchor;
   switch (nRotate) {
+    case 0:
+    default: {
+      space_available_below_anchor =
+          static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
+      space_available_above_anchor =
+          static_cast<int>(rtAnchor.top - page_view_rect.top);
+
+      if (rtAnchor.left < page_view_rect.left)
+        pPopupRect->left += page_view_rect.left - rtAnchor.left;
+      if (rtAnchor.right() > page_view_rect.right)
+        pPopupRect->left -= rtAnchor.right() - page_view_rect.right;
+      break;
+    }
     case 90: {
-      t1 = (int)(pageViewRect.right - rcAnchor.right);
-      t2 = (int)(rcAnchor.left - pageViewRect.left);
-      if (rcAnchor.bottom < pageViewRect.bottom)
-        rtPopup.left += rcAnchor.bottom - pageViewRect.bottom;
+      space_available_below_anchor =
+          static_cast<int>(page_view_rect.right - rtAnchor.right());
+      space_available_above_anchor =
+          static_cast<int>(rtAnchor.left - page_view_rect.left);
+
+      if (rtAnchor.bottom() > page_view_rect.bottom)
+        pPopupRect->left += rtAnchor.bottom() - page_view_rect.bottom;
+      if (rtAnchor.top < page_view_rect.top)
+        pPopupRect->left -= page_view_rect.top - rtAnchor.top;
       break;
     }
     case 180: {
-      t2 = (int)(pageViewRect.top - rcAnchor.top);
-      t1 = (int)(rcAnchor.bottom - pageViewRect.bottom);
-      if (rcAnchor.left < pageViewRect.left)
-        rtPopup.left += rcAnchor.left - pageViewRect.left;
+      space_available_below_anchor =
+          static_cast<int>(rtAnchor.top - page_view_rect.top);
+      space_available_above_anchor =
+          static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
+
+      if (rtAnchor.right() > page_view_rect.right)
+        pPopupRect->left += rtAnchor.right() - page_view_rect.right;
+      if (rtAnchor.left < page_view_rect.left)
+        pPopupRect->left -= page_view_rect.left - rtAnchor.left;
       break;
     }
     case 270: {
-      t1 = (int)(rcAnchor.left - pageViewRect.left);
-      t2 = (int)(pageViewRect.right - rcAnchor.right);
-      if (rcAnchor.top > pageViewRect.top)
-        rtPopup.left -= rcAnchor.top - pageViewRect.top;
-      break;
-    }
-    case 0:
-    default: {
-      t1 = (int)(pageViewRect.top - rcAnchor.top);
-      t2 = (int)(rcAnchor.bottom - pageViewRect.bottom);
-      if (rcAnchor.right > pageViewRect.right)
-        rtPopup.left -= rcAnchor.right - pageViewRect.right;
+      space_available_below_anchor =
+          static_cast<int>(rtAnchor.left - page_view_rect.left);
+      space_available_above_anchor =
+          static_cast<int>(page_view_rect.right - rtAnchor.right());
+
+      if (rtAnchor.top < page_view_rect.top)
+        pPopupRect->left += page_view_rect.top - rtAnchor.top;
+      if (rtAnchor.bottom() > page_view_rect.bottom)
+        pPopupRect->left -= rtAnchor.bottom() - page_view_rect.bottom;
       break;
     }
   }
 
-  int t;
-  uint32_t dwPos;
-  if (t1 <= 0 && t2 <= 0)
+  // If there is no space on either side, the popup can't be rendered.
+  if (space_available_below_anchor <= 0 && space_available_above_anchor <= 0)
     return false;
-  if (t1 <= 0) {
-    t = t2;
-    dwPos = 1;
-  } else if (t2 <= 0) {
-    t = t1;
-    dwPos = 0;
-  } else if (t1 > t2) {
-    t = t1;
-    dwPos = 0;
-  } else {
-    t = t2;
-    dwPos = 1;
-  }
 
-  float fPopupHeight;
-  if (t < fMinPopup)
-    fPopupHeight = fMinPopup;
-  else if (t > fMaxPopup)
-    fPopupHeight = fMaxPopup;
+  // Determine whether to draw above or below the anchor.
+  bool draw_below_anchor;
+  if (space_available_below_anchor <= 0)
+    draw_below_anchor = false;
+  else if (space_available_above_anchor <= 0)
+    draw_below_anchor = true;
+  else if (space_available_below_anchor > space_available_above_anchor)
+    draw_below_anchor = true;
   else
-    fPopupHeight = static_cast<float>(t);
+    draw_below_anchor = false;
+
+  int space_available = (draw_below_anchor ? space_available_below_anchor
+                                           : space_available_above_anchor);
+
+  // Set the popup height and y position according to what was decided above.
+  float popup_height;
+  if (space_available < fMinPopup)
+    popup_height = fMinPopup;
+  else if (space_available > fMaxPopup)
+    popup_height = fMaxPopup;
+  else
+    popup_height = static_cast<float>(space_available);
 
   switch (nRotate) {
     case 0:
     case 180: {
-      if (dwPos == 0) {
-        rtPopup.top = rtAnchor.height;
-        rtPopup.height = fPopupHeight;
-      } else {
-        rtPopup.top = -fPopupHeight;
-        rtPopup.height = fPopupHeight;
-      }
+      if (draw_below_anchor)
+        pPopupRect->top = rtAnchor.height;
+      else
+        pPopupRect->top = -popup_height;
       break;
     }
     case 90:
     case 270: {
-      if (dwPos == 0) {
-        rtPopup.top = rtAnchor.width;
-        rtPopup.height = fPopupHeight;
-      } else {
-        rtPopup.top = -fPopupHeight;
-        rtPopup.height = fPopupHeight;
-      }
+      if (draw_below_anchor)
+        pPopupRect->top = rtAnchor.width;
+      else
+        pPopupRect->top = -popup_height;
       break;
     }
     default:
       break;
   }
 
+  pPopupRect->height = popup_height;
   return true;
 }
 
 bool CPDFXFA_DocEnvironment::PopupMenu(CXFA_FFWidget* hWidget,
-                                       CFX_PointF ptPopup) {
+                                       const CFX_PointF& ptPopup) {
   if (!hWidget)
     return false;
 
@@ -248,7 +266,7 @@
   if (hWidget->CanSelectAll())
     menuFlag |= FXFA_MENU_SELECTALL;
 
-  return pFormFillEnv->PopupMenu(pPage.Get(), hWidget, menuFlag, ptPopup);
+  return pFormFillEnv->PopupMenu(pPage.Get(), nullptr, menuFlag, ptPopup);
 }
 
 void CPDFXFA_DocEnvironment::PageViewEvent(CXFA_FFPageView* pPageView,
@@ -277,7 +295,7 @@
       continue;
 
     m_pContext->GetFormFillEnv()->RemovePageView(pPage.Get());
-    pPage->SetXFAPageView(pXFADocView->GetPageView(iPageIter));
+    pPage->SetXFAPageViewIndex(iPageIter);
   }
 
   int flag = (nNewCount < m_pContext->GetOriginalPageCount())
@@ -288,8 +306,7 @@
   pFormFillEnv->PageEvent(count, flag);
 }
 
-void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget,
-                                           CXFA_WidgetAcc* pWidgetAcc) {
+void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget) {
   if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget)
     return;
 
@@ -306,8 +323,7 @@
       ->AddAnnot(hWidget);
 }
 
-void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget,
-                                             CXFA_WidgetAcc* pWidgetAcc) {
+void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget) {
   if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget)
     return;
 
@@ -335,20 +351,18 @@
 int32_t CPDFXFA_DocEnvironment::GetCurrentPage(CXFA_FFDoc* hDoc) {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return -1;
+
   if (m_pContext->GetFormType() != FormType::kXFAFull)
     return -1;
 
   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return -1;
-
-  return pFormFillEnv->GetCurrentPageIndex(m_pContext.Get());
+  return pFormFillEnv ? pFormFillEnv->GetCurrentPageIndex() : -1;
 }
 
 void CPDFXFA_DocEnvironment::SetCurrentPage(CXFA_FFDoc* hDoc,
                                             int32_t iCurPage) {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv() ||
-      m_pContext->GetFormType() != FormType::kXFAFull || iCurPage < 0 ||
+      !m_pContext->ContainsExtensionForm() || iCurPage < 0 ||
       iCurPage >= m_pContext->GetFormFillEnv()->GetPageCount()) {
     return;
   }
@@ -356,27 +370,23 @@
   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
   if (!pFormFillEnv)
     return;
-  pFormFillEnv->SetCurrentPage(m_pContext.Get(), iCurPage);
+
+  pFormFillEnv->SetCurrentPage(iCurPage);
 }
 
 bool CPDFXFA_DocEnvironment::IsCalculationsEnabled(CXFA_FFDoc* hDoc) {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return false;
-  if (m_pContext->GetFormFillEnv()->GetInterForm()) {
-    return m_pContext->GetFormFillEnv()
-        ->GetInterForm()
-        ->IsXfaCalculateEnabled();
-  }
-  return false;
+  auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
+  return pForm->IsXfaCalculateEnabled();
 }
 
 void CPDFXFA_DocEnvironment::SetCalculationsEnabled(CXFA_FFDoc* hDoc,
                                                     bool bEnabled) {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return;
-  if (m_pContext->GetFormFillEnv()->GetInterForm()) {
-    m_pContext->GetFormFillEnv()->GetInterForm()->XfaEnableCalculate(bEnabled);
-  }
+  m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaEnableCalculate(
+      bEnabled);
 }
 
 void CPDFXFA_DocEnvironment::GetTitle(CXFA_FFDoc* hDoc, WideString& wsTitle) {
@@ -388,8 +398,7 @@
     return;
 
   ByteString csTitle = pInfoDict->GetStringFor("Title");
-  wsTitle = wsTitle.FromLocal(csTitle.GetBuffer(csTitle.GetLength()));
-  csTitle.ReleaseBuffer(csTitle.GetLength());
+  wsTitle = WideString::FromDefANSI(csTitle.AsStringView());
 }
 
 void CPDFXFA_DocEnvironment::SetTitle(CXFA_FFDoc* hDoc,
@@ -408,7 +417,7 @@
   if (hDoc != m_pContext->GetXFADoc())
     return;
 
-  if (!m_pContext->ContainsXFAForm())
+  if (!m_pContext->ContainsExtensionForm())
     return;
 
   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
@@ -416,7 +425,7 @@
     return;
 
   int fileType = bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML;
-  ByteString bs = wsFilePath.UTF16LE_Encode();
+  ByteString bs = wsFilePath.ToUTF16LE();
   if (wsFilePath.IsEmpty()) {
     if (!pFormFillEnv->GetFormFillInfo() ||
         !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform) {
@@ -424,25 +433,19 @@
     }
 
     WideString filepath = pFormFillEnv->JS_fieldBrowse();
-    bs = filepath.UTF16LE_Encode();
+    bs = filepath.ToUTF16LE();
   }
-  int len = bs.GetLength();
-  FPDF_FILEHANDLER* pFileHandler =
-      pFormFillEnv->OpenFile(bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML,
-                             (FPDF_WIDESTRING)bs.GetBuffer(len), "wb");
-  bs.ReleaseBuffer(len);
+  FPDF_FILEHANDLER* pFileHandler = pFormFillEnv->OpenFile(
+      bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML, AsFPDFWideString(&bs), "wb");
   if (!pFileHandler)
     return;
 
   RetainPtr<IFX_SeekableStream> fileWrite = MakeSeekableStream(pFileHandler);
   if (fileType == FXFA_SAVEAS_XML) {
-    ByteString content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n";
-    fileWrite->WriteBlock(content.c_str(), fileWrite->GetSize(),
-                          content.GetLength());
+    fileWrite->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
     CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
     ffdoc->SavePackage(
-        ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)), fileWrite,
-        nullptr);
+        ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)), fileWrite);
   } else if (fileType == FXFA_SAVEAS_XDP) {
     if (!m_pContext->GetPDFDoc())
       return;
@@ -451,62 +454,59 @@
     if (!pRoot)
       return;
 
-    CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+    const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
     if (!pAcroForm)
       return;
 
-    CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
+    const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
     if (!pArray)
       return;
 
-    int size = pArray->GetCount();
-    for (int i = 1; i < size; i += 2) {
-      CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
-      CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
+    for (size_t i = 1; i < pArray->size(); i += 2) {
+      const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
+      const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
       if (!pPrePDFObj->IsString())
         continue;
       if (!pPDFObj->IsReference())
         continue;
 
-      CPDF_Stream* pStream = ToStream(pPDFObj->GetDirect());
+      const CPDF_Stream* pStream = ToStream(pPDFObj->GetDirect());
       if (!pStream)
         continue;
       if (pPrePDFObj->GetString() == "form") {
         CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
         ffdoc->SavePackage(
             ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
-            fileWrite, nullptr);
+            fileWrite);
         continue;
       }
       if (pPrePDFObj->GetString() == "datasets") {
         CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
         ffdoc->SavePackage(
             ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
-            fileWrite, nullptr);
+            fileWrite);
         continue;
       }
-      if (i == size - 1) {
+      if (i == pArray->size() - 1) {
         WideString wPath = WideString::FromUTF16LE(
             reinterpret_cast<const unsigned short*>(bs.c_str()),
             bs.GetLength() / sizeof(unsigned short));
-        ByteString bPath = wPath.UTF8Encode();
-        const char* szFormat =
+        ByteString bPath = wPath.ToUTF8();
+        static const char kFormat[] =
             "\n<pdf href=\"%s\" xmlns=\"http://ns.adobe.com/xdp/pdf/\"/>";
-        ByteString content = ByteString::Format(szFormat, bPath.c_str());
-        fileWrite->WriteBlock(content.c_str(), fileWrite->GetSize(),
-                              content.GetLength());
+        ByteString content = ByteString::Format(kFormat, bPath.c_str());
+        fileWrite->WriteString(content.AsStringView());
       }
       auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
       pAcc->LoadAllDataFiltered();
-      fileWrite->WriteBlock(pAcc->GetData(), fileWrite->GetSize(),
-                            pAcc->GetSize());
+      fileWrite->WriteBlock(pAcc->GetData(), pAcc->GetSize());
     }
   }
   fileWrite->Flush();
 }
 
 void CPDFXFA_DocEnvironment::GotoURL(CXFA_FFDoc* hDoc,
-                                     const WideString& bsURL) {
+                                     const WideString& wsURL) {
   if (hDoc != m_pContext->GetXFADoc())
     return;
 
@@ -517,29 +517,24 @@
   if (!pFormFillEnv)
     return;
 
-  WideStringView str(bsURL.c_str());
-  pFormFillEnv->GotoURL(m_pContext.Get(), str);
+  pFormFillEnv->GotoURL(wsURL);
 }
 
 bool CPDFXFA_DocEnvironment::IsValidationsEnabled(CXFA_FFDoc* hDoc) {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return false;
-  if (m_pContext->GetFormFillEnv()->GetInterForm()) {
-    return m_pContext->GetFormFillEnv()
-        ->GetInterForm()
-        ->IsXfaValidationsEnabled();
-  }
-  return true;
+
+  auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
+  return pForm->IsXfaValidationsEnabled();
 }
 
 void CPDFXFA_DocEnvironment::SetValidationsEnabled(CXFA_FFDoc* hDoc,
                                                    bool bEnabled) {
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return;
-  if (m_pContext->GetFormFillEnv()->GetInterForm()) {
-    m_pContext->GetFormFillEnv()->GetInterForm()->XfaSetValidationsEnabled(
-        bEnabled);
-  }
+
+  m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaSetValidationsEnabled(
+      bEnabled);
 }
 
 void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc,
@@ -548,7 +543,7 @@
     return;
 
   if (!hWidget) {
-    CPDFSDK_Annot::ObservedPtr pNull;
+    ObservedPtr<CPDFSDK_Annot> pNull;
     m_pContext->GetFormFillEnv()->SetFocusAnnot(&pNull);
     return;
   }
@@ -559,7 +554,7 @@
     if (!pPageView)
       continue;
 
-    CPDFSDK_Annot::ObservedPtr pAnnot(pPageView->GetAnnotByXFAWidget(hWidget));
+    ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotByXFAWidget(hWidget));
     if (pAnnot) {
       m_pContext->GetFormFillEnv()->SetFocusAnnot(&pAnnot);
       break;
@@ -593,98 +588,37 @@
   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
     return 0;
 
-  CPDFSDK_InterForm* pInterForm = m_pContext->GetFormFillEnv()->GetInterForm();
-  if (!pInterForm)
-    return 0;
-
-  return ArgbEncode(pInterForm->GetHighlightAlpha(),
-                    pInterForm->GetHighlightColor(FormFieldType::kXFA));
+  CPDFSDK_InteractiveForm* pForm =
+      m_pContext->GetFormFillEnv()->GetInteractiveForm();
+  return AlphaAndColorRefToArgb(pForm->GetHighlightAlpha(),
+                                pForm->GetHighlightColor(FormFieldType::kXFA));
 }
 
-bool CPDFXFA_DocEnvironment::NotifySubmit(bool bPrevOrPost) {
-  if (bPrevOrPost)
-    return OnBeforeNotifySubmit();
+IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(CXFA_FFDoc* hDoc) const {
+  if (hDoc != m_pContext->GetXFADoc())
+    return nullptr;
 
-  OnAfterNotifySubmit();
-  return true;
+  CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
+  return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
 }
 
-bool CPDFXFA_DocEnvironment::OnBeforeNotifySubmit() {
-  if (!m_pContext->ContainsXFAForm())
-    return true;
+RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
+    CXFA_FFDoc* hDoc,
+    const WideString& wsLink) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
+  if (!pFormFillEnv)
+    return nullptr;
 
-  CXFA_FFDocView* docView = m_pContext->GetXFADocView();
-  if (!docView)
-    return true;
+  ByteString bs = wsLink.ToUTF16LE();
+  FPDF_FILEHANDLER* pFileHandler =
+      pFormFillEnv->OpenFile(0, AsFPDFWideString(&bs), "rb");
+  if (!pFileHandler)
+    return nullptr;
 
-  CXFA_FFWidgetHandler* pWidgetHandler = docView->GetWidgetHandler();
-  if (!pWidgetHandler)
-    return true;
-
-  std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator =
-      docView->CreateWidgetAccIterator();
-  if (pWidgetAccIterator) {
-    CXFA_EventParam Param;
-    Param.m_eType = XFA_EVENT_PreSubmit;
-    while (CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext())
-      pWidgetHandler->ProcessEvent(pWidgetAcc, &Param);
-  }
-
-  pWidgetAccIterator = docView->CreateWidgetAccIterator();
-  if (!pWidgetAccIterator)
-    return true;
-
-  CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext();
-  pWidgetAcc = pWidgetAccIterator->MoveToNext();
-  while (pWidgetAcc) {
-    int fRet = pWidgetAcc->GetNode()->ProcessValidate(docView, -1);
-    if (fRet == XFA_EVENTERROR_Error) {
-      CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
-      if (!pFormFillEnv)
-        return false;
-
-      WideString ws = WideString::FromLocal(IDS_XFA_Validate_Input);
-      ByteString bs = ws.UTF16LE_Encode();
-      int len = bs.GetLength();
-      pFormFillEnv->Alert((FPDF_WIDESTRING)bs.GetBuffer(len),
-                          (FPDF_WIDESTRING)L"", 0, 1);
-      bs.ReleaseBuffer(len);
-      return false;
-    }
-    pWidgetAcc = pWidgetAccIterator->MoveToNext();
-  }
-  docView->UpdateDocView();
-
-  return true;
+  return MakeSeekableStream(pFileHandler);
 }
 
-void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() {
-  if (!m_pContext->ContainsXFAForm())
-    return;
-
-  if (!m_pContext->GetXFADocView())
-    return;
-
-  CXFA_FFWidgetHandler* pWidgetHandler =
-      m_pContext->GetXFADocView()->GetWidgetHandler();
-  if (!pWidgetHandler)
-    return;
-
-  std::unique_ptr<CXFA_WidgetAccIterator> pWidgetAccIterator =
-      m_pContext->GetXFADocView()->CreateWidgetAccIterator();
-  if (!pWidgetAccIterator)
-    return;
-
-  CXFA_EventParam Param;
-  Param.m_eType = XFA_EVENT_PostSubmit;
-  CXFA_WidgetAcc* pWidgetAcc = pWidgetAccIterator->MoveToNext();
-  while (pWidgetAcc) {
-    pWidgetHandler->ProcessEvent(pWidgetAcc, &Param);
-    pWidgetAcc = pWidgetAccIterator->MoveToNext();
-  }
-  m_pContext->GetXFADocView()->UpdateDocView();
-}
-
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
 bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) {
   if (!NotifySubmit(true) || !m_pContext->GetXFADocView())
     return false;
@@ -695,22 +629,69 @@
   return ret;
 }
 
-RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
-    CXFA_FFDoc* hDoc,
-    const WideString& wsLink) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return nullptr;
+bool CPDFXFA_DocEnvironment::MailToInfo(WideString& csURL,
+                                        WideString& csToAddress,
+                                        WideString& csCCAddress,
+                                        WideString& csBCCAddress,
+                                        WideString& csSubject,
+                                        WideString& csMsg) {
+  WideString srcURL = csURL;
+  srcURL.TrimLeft();
+  if (srcURL.Left(7).CompareNoCase(L"mailto:") != 0)
+    return false;
 
-  ByteString bs = wsLink.UTF16LE_Encode();
-  int len = bs.GetLength();
-  FPDF_FILEHANDLER* pFileHandler =
-      pFormFillEnv->OpenFile(0, (FPDF_WIDESTRING)bs.GetBuffer(len), "rb");
-  bs.ReleaseBuffer(len);
-  if (!pFileHandler)
-    return nullptr;
+  auto pos = srcURL.Find(L'?');
 
-  return MakeSeekableStream(pFileHandler);
+  {
+    WideString tmp;
+    if (!pos.has_value()) {
+      pos = srcURL.Find(L'@');
+      if (!pos.has_value())
+        return false;
+
+      tmp = srcURL.Right(csURL.GetLength() - 7);
+    } else {
+      tmp = srcURL.Left(pos.value());
+      tmp = tmp.Right(tmp.GetLength() - 7);
+    }
+    tmp.Trim();
+    csToAddress = std::move(tmp);
+  }
+
+  srcURL = srcURL.Right(srcURL.GetLength() - (pos.value() + 1));
+  while (!srcURL.IsEmpty()) {
+    srcURL.Trim();
+    pos = srcURL.Find(L'&');
+    WideString tmp = (!pos.has_value()) ? srcURL : srcURL.Left(pos.value());
+    tmp.Trim();
+    if (tmp.GetLength() >= 3 && tmp.Left(3).CompareNoCase(L"cc=") == 0) {
+      tmp = tmp.Right(tmp.GetLength() - 3);
+      if (!csCCAddress.IsEmpty())
+        csCCAddress += L';';
+      csCCAddress += tmp;
+    } else if (tmp.GetLength() >= 4 &&
+               tmp.Left(4).CompareNoCase(L"bcc=") == 0) {
+      tmp = tmp.Right(tmp.GetLength() - 4);
+      if (!csBCCAddress.IsEmpty())
+        csBCCAddress += L';';
+      csBCCAddress += tmp;
+    } else if (tmp.GetLength() >= 8 &&
+               tmp.Left(8).CompareNoCase(L"subject=") == 0) {
+      tmp = tmp.Right(tmp.GetLength() - 8);
+      csSubject += tmp;
+    } else if (tmp.GetLength() >= 5 &&
+               tmp.Left(5).CompareNoCase(L"body=") == 0) {
+      tmp = tmp.Right(tmp.GetLength() - 5);
+      csMsg += tmp;
+    }
+    srcURL = pos.has_value()
+                 ? srcURL.Right(csURL.GetLength() - (pos.value() + 1))
+                 : WideString();
+  }
+  csToAddress.Replace(L",", L";");
+  csCCAddress.Replace(L",", L";");
+  csBCCAddress.Replace(L",", L";");
+  return true;
 }
 
 bool CPDFXFA_DocEnvironment::ExportSubmitFile(FPDF_FILEHANDLER* pFileHandler,
@@ -727,13 +708,10 @@
   CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
   RetainPtr<IFX_SeekableStream> fileStream = MakeSeekableStream(pFileHandler);
   if (fileType == FXFA_SAVEAS_XML) {
-    static constexpr char kContent[] =
-        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n";
-    fileStream->WriteBlock(kContent, 0, strlen(kContent));
-
+    fileStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
     ffdoc->SavePackage(
-        ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)), fileStream,
-        nullptr);
+        ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)),
+        fileStream);
     return true;
   }
 
@@ -755,53 +733,53 @@
     return false;
   }
 
-  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
+  const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
   if (!pAcroForm) {
     fileStream->Flush();
     return false;
   }
 
-  CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
+  const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
   if (!pArray) {
     fileStream->Flush();
     return false;
   }
 
-  int size = pArray->GetCount();
-  for (int i = 1; i < size; i += 2) {
-    CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
-    CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
+  for (size_t i = 1; i < pArray->size(); i += 2) {
+    const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
+    const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
     if (!pPrePDFObj->IsString())
       continue;
     if (!pPDFObj->IsReference())
       continue;
 
-    CPDF_Object* pDirectObj = pPDFObj->GetDirect();
+    const CPDF_Object* pDirectObj = pPDFObj->GetDirect();
     if (!pDirectObj->IsStream())
       continue;
-    if (pPrePDFObj->GetString() == "config" && !(flag & FXFA_CONFIG))
+    ByteString bsType = pPrePDFObj->GetString();
+    if (bsType == "config" && !(flag & FXFA_CONFIG))
       continue;
-    if (pPrePDFObj->GetString() == "template" && !(flag & FXFA_TEMPLATE))
+    if (bsType == "template" && !(flag & FXFA_TEMPLATE))
       continue;
-    if (pPrePDFObj->GetString() == "localeSet" && !(flag & FXFA_LOCALESET))
+    if (bsType == "localeSet" && !(flag & FXFA_LOCALESET))
       continue;
-    if (pPrePDFObj->GetString() == "datasets" && !(flag & FXFA_DATASETS))
+    if (bsType == "datasets" && !(flag & FXFA_DATASETS))
       continue;
-    if (pPrePDFObj->GetString() == "xmpmeta" && !(flag & FXFA_XMPMETA))
+    if (bsType == "xmpmeta" && !(flag & FXFA_XMPMETA))
       continue;
-    if (pPrePDFObj->GetString() == "xfdf" && !(flag & FXFA_XFDF))
+    if (bsType == "xfdf" && !(flag & FXFA_XFDF))
       continue;
-    if (pPrePDFObj->GetString() == "form" && !(flag & FXFA_FORM))
+    if (bsType == "form" && !(flag & FXFA_FORM))
       continue;
 
-    if (pPrePDFObj->GetString() == "form") {
+    if (bsType == "form") {
       ffdoc->SavePackage(
           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
-          fileStream, nullptr);
+          fileStream);
     } else if (pPrePDFObj->GetString() == "datasets") {
       ffdoc->SavePackage(
           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
-          fileStream, nullptr);
+          fileStream);
     }
   }
   return true;
@@ -829,67 +807,82 @@
   }
 }
 
-bool CPDFXFA_DocEnvironment::MailToInfo(WideString& csURL,
-                                        WideString& csToAddress,
-                                        WideString& csCCAddress,
-                                        WideString& csBCCAddress,
-                                        WideString& csSubject,
-                                        WideString& csMsg) {
-  WideString srcURL = csURL;
-  srcURL.TrimLeft();
-  if (srcURL.Left(7).CompareNoCase(L"mailto:") != 0)
-    return false;
+bool CPDFXFA_DocEnvironment::OnBeforeNotifySubmit() {
+  if (!m_pContext->ContainsXFAForm())
+    return true;
 
-  auto pos = srcURL.Find(L'?');
-  WideString tmp;
-  if (!pos.has_value()) {
-    pos = srcURL.Find(L'@');
-    if (!pos.has_value())
+  CXFA_FFDocView* docView = m_pContext->GetXFADocView();
+  if (!docView)
+    return true;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = docView->GetWidgetHandler();
+  if (!pWidgetHandler)
+    return true;
+
+  auto it = docView->CreateReadyNodeIterator();
+  if (it) {
+    CXFA_EventParam Param;
+    Param.m_eType = XFA_EVENT_PreSubmit;
+    while (CXFA_Node* pNode = it->MoveToNext())
+      pWidgetHandler->ProcessEvent(pNode, &Param);
+  }
+
+  it = docView->CreateReadyNodeIterator();
+  if (!it)
+    return true;
+
+  (void)it->MoveToNext();
+  CXFA_Node* pNode = it->MoveToNext();
+
+  while (pNode) {
+    if (pNode->ProcessValidate(docView, -1) == XFA_EventError::kError) {
+      CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
+      if (!pFormFillEnv)
+        return false;
+
+      pFormFillEnv->JS_appAlert(WideString::FromDefANSI(IDS_XFA_Validate_Input),
+                                WideString(), JSPLATFORM_ALERT_BUTTON_OK,
+                                JSPLATFORM_ALERT_ICON_WARNING);
       return false;
-
-    tmp = srcURL.Right(csURL.GetLength() - 7);
-  } else {
-    tmp = srcURL.Left(pos.value());
-    tmp = tmp.Right(tmp.GetLength() - 7);
-  }
-  tmp.Trim();
-
-  csToAddress = tmp;
-
-  srcURL = srcURL.Right(srcURL.GetLength() - (pos.value() + 1));
-  while (!srcURL.IsEmpty()) {
-    srcURL.Trim();
-    pos = srcURL.Find(L'&');
-
-    tmp = (!pos.has_value()) ? srcURL : srcURL.Left(pos.value());
-    tmp.Trim();
-    if (tmp.GetLength() >= 3 && tmp.Left(3).CompareNoCase(L"cc=") == 0) {
-      tmp = tmp.Right(tmp.GetLength() - 3);
-      if (!csCCAddress.IsEmpty())
-        csCCAddress += L';';
-      csCCAddress += tmp;
-    } else if (tmp.GetLength() >= 4 &&
-               tmp.Left(4).CompareNoCase(L"bcc=") == 0) {
-      tmp = tmp.Right(tmp.GetLength() - 4);
-      if (!csBCCAddress.IsEmpty())
-        csBCCAddress += L';';
-      csBCCAddress += tmp;
-    } else if (tmp.GetLength() >= 8 &&
-               tmp.Left(8).CompareNoCase(L"subject=") == 0) {
-      tmp = tmp.Right(tmp.GetLength() - 8);
-      csSubject += tmp;
-    } else if (tmp.GetLength() >= 5 &&
-               tmp.Left(5).CompareNoCase(L"body=") == 0) {
-      tmp = tmp.Right(tmp.GetLength() - 5);
-      csMsg += tmp;
     }
-    srcURL = !pos.has_value()
-                 ? L""
-                 : srcURL.Right(csURL.GetLength() - (pos.value() + 1));
+    pNode = it->MoveToNext();
   }
-  csToAddress.Replace(L",", L";");
-  csCCAddress.Replace(L",", L";");
-  csBCCAddress.Replace(L",", L";");
+
+  docView->UpdateDocView();
+  return true;
+}
+
+void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() {
+  if (!m_pContext->ContainsXFAForm())
+    return;
+
+  if (!m_pContext->GetXFADocView())
+    return;
+
+  CXFA_FFWidgetHandler* pWidgetHandler =
+      m_pContext->GetXFADocView()->GetWidgetHandler();
+  if (!pWidgetHandler)
+    return;
+
+  auto it = m_pContext->GetXFADocView()->CreateReadyNodeIterator();
+  if (!it)
+    return;
+
+  CXFA_EventParam Param;
+  Param.m_eType = XFA_EVENT_PostSubmit;
+  CXFA_Node* pNode = it->MoveToNext();
+  while (pNode) {
+    pWidgetHandler->ProcessEvent(pNode, &Param);
+    pNode = it->MoveToNext();
+  }
+  m_pContext->GetXFADocView()->UpdateDocView();
+}
+
+bool CPDFXFA_DocEnvironment::NotifySubmit(bool bPrevOrPost) {
+  if (bPrevOrPost)
+    return OnBeforeNotifySubmit();
+
+  OnAfterNotifySubmit();
   return true;
 }
 
@@ -901,23 +894,20 @@
 
   WideString csURL = submit->GetSubmitTarget();
   if (csURL.IsEmpty()) {
-    WideString ws = WideString::FromLocal("Submit cancelled.");
-    ByteString bs = ws.UTF16LE_Encode();
-    int len = bs.GetLength();
-    pFormFillEnv->Alert(reinterpret_cast<FPDF_WIDESTRING>(bs.GetBuffer(len)),
-                        reinterpret_cast<FPDF_WIDESTRING>(L""), 0, 4);
-    bs.ReleaseBuffer(len);
+    pFormFillEnv->JS_appAlert(WideString::FromDefANSI("Submit cancelled."),
+                              WideString(), JSPLATFORM_ALERT_BUTTON_OK,
+                              JSPLATFORM_ALERT_ICON_ASTERISK);
     return false;
   }
 
   FPDF_FILEHANDLER* pFileHandler = nullptr;
   int fileFlag = -1;
   switch (submit->GetSubmitFormat()) {
-    case XFA_AttributeEnum::Xdp: {
+    case XFA_AttributeValue::Xdp: {
       WideString csContent = submit->GetSubmitXDPContent();
       csContent.Trim();
 
-      WideString space = WideString::FromLocal(" ");
+      WideString space = WideString::FromDefANSI(" ");
       csContent = space + csContent + space;
       FPDF_DWORD flag = 0;
       if (submit->IsSubmitEmbedPDF())
@@ -929,14 +919,14 @@
       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XDP, 0, flag);
       break;
     }
-    case XFA_AttributeEnum::Xml:
+    case XFA_AttributeValue::Xml:
       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
       fileFlag = FXFA_SAVEAS_XML;
       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
       break;
-    case XFA_AttributeEnum::Pdf:
+    case XFA_AttributeValue::Pdf:
       break;
-    case XFA_AttributeEnum::Urlencoded:
+    case XFA_AttributeValue::Urlencoded:
       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
       fileFlag = FXFA_SAVEAS_XML;
       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
@@ -957,63 +947,20 @@
                     csMsg)) {
       return false;
     }
-    ByteString bsTo = WideString(csToAddress).UTF16LE_Encode();
-    ByteString bsCC = WideString(csCCAddress).UTF16LE_Encode();
-    ByteString bsBcc = WideString(csBCCAddress).UTF16LE_Encode();
-    ByteString bsSubject = WideString(csSubject).UTF16LE_Encode();
-    ByteString bsMsg = WideString(csMsg).UTF16LE_Encode();
-    FPDF_WIDESTRING pTo = (FPDF_WIDESTRING)bsTo.GetBuffer(bsTo.GetLength());
-    FPDF_WIDESTRING pCC = (FPDF_WIDESTRING)bsCC.GetBuffer(bsCC.GetLength());
-    FPDF_WIDESTRING pBcc = (FPDF_WIDESTRING)bsBcc.GetBuffer(bsBcc.GetLength());
-    FPDF_WIDESTRING pSubject =
-        (FPDF_WIDESTRING)bsSubject.GetBuffer(bsSubject.GetLength());
-    FPDF_WIDESTRING pMsg = (FPDF_WIDESTRING)bsMsg.GetBuffer(bsMsg.GetLength());
-    pFormFillEnv->EmailTo(pFileHandler, pTo, pSubject, pCC, pBcc, pMsg);
-    bsTo.ReleaseBuffer(bsTo.GetStringLength());
-    bsCC.ReleaseBuffer(bsCC.GetStringLength());
-    bsBcc.ReleaseBuffer(bsBcc.GetStringLength());
-    bsSubject.ReleaseBuffer(bsSubject.GetStringLength());
-    bsMsg.ReleaseBuffer(bsMsg.GetStringLength());
-  } else {
-    // HTTP or FTP
-    WideString ws;
-    ByteString bs = csURL.UTF16LE_Encode();
-    int len = bs.GetLength();
-    pFormFillEnv->UploadTo(pFileHandler, fileFlag,
-                           (FPDF_WIDESTRING)bs.GetBuffer(len));
-    bs.ReleaseBuffer(len);
+    ByteString bsTo = WideString(csToAddress).ToUTF16LE();
+    ByteString bsCC = WideString(csCCAddress).ToUTF16LE();
+    ByteString bsBcc = WideString(csBCCAddress).ToUTF16LE();
+    ByteString bsSubject = WideString(csSubject).ToUTF16LE();
+    ByteString bsMsg = WideString(csMsg).ToUTF16LE();
+    pFormFillEnv->EmailTo(pFileHandler, AsFPDFWideString(&bsTo),
+                          AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
+                          AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg));
+    return true;
   }
+
+  // HTTP or FTP
+  ByteString bs = csURL.ToUTF16LE();
+  pFormFillEnv->UploadTo(pFileHandler, fileFlag, AsFPDFWideString(&bs));
   return true;
 }
-
-bool CPDFXFA_DocEnvironment::SetGlobalProperty(CXFA_FFDoc* hDoc,
-                                               const ByteStringView& szPropName,
-                                               CFXJSE_Value* pValue) {
-  if (hDoc != m_pContext->GetXFADoc())
-    return false;
-  if (!m_pContext->GetFormFillEnv() ||
-      !m_pContext->GetFormFillEnv()->GetJSRuntime()) {
-    return false;
-  }
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
-  IJS_EventContext* pContext = pFormFillEnv->GetJSRuntime()->NewEventContext();
-  bool bRet = pFormFillEnv->GetJSRuntime()->SetValueByName(szPropName, pValue);
-  pFormFillEnv->GetJSRuntime()->ReleaseEventContext(pContext);
-  return bRet;
-}
-
-bool CPDFXFA_DocEnvironment::GetGlobalProperty(CXFA_FFDoc* hDoc,
-                                               const ByteStringView& szPropName,
-                                               CFXJSE_Value* pValue) {
-  if (hDoc != m_pContext->GetXFADoc())
-    return false;
-  if (!m_pContext->GetFormFillEnv() ||
-      !m_pContext->GetFormFillEnv()->GetJSRuntime()) {
-    return false;
-  }
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
-  IJS_EventContext* pContext = pFormFillEnv->GetJSRuntime()->NewEventContext();
-  bool bRet = pFormFillEnv->GetJSRuntime()->GetValueByName(szPropName, pValue);
-  pFormFillEnv->GetJSRuntime()->ReleaseEventContext(pContext);
-  return bRet;
-}
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h
index 9eabf8d..ebb86c4 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h
@@ -13,37 +13,28 @@
 #include "xfa/fxfa/fxfa.h"
 
 class CPDFXFA_Context;
-class IJS_EventContext;
+class IJS_Runtime;
 
-class CPDFXFA_DocEnvironment : public IXFA_DocEnvironment {
+class CPDFXFA_DocEnvironment final : public IXFA_DocEnvironment {
  public:
   explicit CPDFXFA_DocEnvironment(CPDFXFA_Context*);
   ~CPDFXFA_DocEnvironment() override;
 
-  // IXFA_DocEnvironment
+  // IXFA_DocEnvironment:
   void SetChangeMark(CXFA_FFDoc* hDoc) override;
-  // Used in dynamic xfa.
   void InvalidateRect(CXFA_FFPageView* pPageView, const CFX_RectF& rt) override;
-  // Show or hide caret.
   void DisplayCaret(CXFA_FFWidget* hWidget,
                     bool bVisible,
                     const CFX_RectF* pRtAnchor) override;
-  // dwPos: (0:bottom 1:top)
   bool GetPopupPos(CXFA_FFWidget* hWidget,
                    float fMinPopup,
                    float fMaxPopup,
                    const CFX_RectF& rtAnchor,
-                   CFX_RectF& rtPopup) override;
-  bool PopupMenu(CXFA_FFWidget* hWidget, CFX_PointF ptPopup) override;
-
-  // dwFlags XFA_PAGEVIEWEVENT_Added, XFA_PAGEVIEWEVENT_Removing
+                   CFX_RectF* pPopupRect) override;
+  bool PopupMenu(CXFA_FFWidget* hWidget, const CFX_PointF& ptPopup) override;
   void PageViewEvent(CXFA_FFPageView* pPageView, uint32_t dwFlags) override;
-  void WidgetPostAdd(CXFA_FFWidget* hWidget,
-                     CXFA_WidgetAcc* pWidgetAcc) override;
-  void WidgetPreRemove(CXFA_FFWidget* hWidget,
-                       CXFA_WidgetAcc* pWidgetAcc) override;
-
-  // Host method
+  void WidgetPostAdd(CXFA_FFWidget* hWidget) override;
+  void WidgetPreRemove(CXFA_FFWidget* hWidget) override;
   int32_t CountPages(CXFA_FFDoc* hDoc) override;
   int32_t GetCurrentPage(CXFA_FFDoc* hDoc) override;
   void SetCurrentPage(CXFA_FFDoc* hDoc, int32_t iCurPage) override;
@@ -63,25 +54,17 @@
              int32_t nEndPage,
              uint32_t dwOptions) override;
   FX_ARGB GetHighlightColor(CXFA_FFDoc* hDoc) override;
-
-  bool Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) override;
-
-  bool GetGlobalProperty(CXFA_FFDoc* hDoc,
-                         const ByteStringView& szPropName,
-                         CFXJSE_Value* pValue) override;
-  bool SetGlobalProperty(CXFA_FFDoc* hDoc,
-                         const ByteStringView& szPropName,
-                         CFXJSE_Value* pValue) override;
-
+  IJS_Runtime* GetIJSRuntime(CXFA_FFDoc* hDoc) const override;
   RetainPtr<IFX_SeekableReadStream> OpenLinkedFile(
       CXFA_FFDoc* hDoc,
       const WideString& wsLink) override;
 
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
+  bool Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) override;
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
+
  private:
-  bool OnBeforeNotifySubmit();
-  void OnAfterNotifySubmit();
-  bool NotifySubmit(bool bPrevOrPost);
-  bool SubmitInternal(CXFA_FFDoc* hDoc, CXFA_Submit* submit);
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
   bool MailToInfo(WideString& csURL,
                   WideString& csToAddress,
                   WideString& csCCAddress,
@@ -93,6 +76,11 @@
                         FPDF_DWORD encodeType,
                         FPDF_DWORD flag);
   void ToXFAContentFlags(WideString csSrcContent, FPDF_DWORD& flag);
+  bool OnBeforeNotifySubmit();
+  void OnAfterNotifySubmit();
+  bool NotifySubmit(bool bPrevOrPost);
+  bool SubmitInternal(CXFA_FFDoc* hDoc, CXFA_Submit* submit);
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
 
   UnownedPtr<CPDFXFA_Context> const m_pContext;
 };
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp
new file mode 100644
index 0000000..1dce59c
--- /dev/null
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp
@@ -0,0 +1,15 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+
+class CPDFXFA_DocEnvironmentEmbedderTest : public XFAJSEmbedderTest {};
+
+// Should not crash.
+TEST_F(CPDFXFA_DocEnvironmentEmbedderTest, BUG_1223) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  EXPECT_FALSE(Execute("URL=y"));
+}
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp
index fbea90d..9797429 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp
@@ -6,183 +6,252 @@
 
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
 
+#include <memory>
+
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "public/fpdf_formfill.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/cxfa_ffwidgethandler.h"
+#include "xfa/fxfa/cxfa_rendercontext.h"
+#include "xfa/fxgraphics/cxfa_graphics.h"
 
-CPDFXFA_Page::CPDFXFA_Page(CPDFXFA_Context* pContext, int page_index)
-    : m_pXFAPageView(nullptr), m_pContext(pContext), m_iPageIndex(page_index) {}
+CPDFXFA_Page::CPDFXFA_Page(CPDF_Document* pDocument, int page_index)
+    : m_pDocument(pDocument), m_iPageIndex(page_index) {
+  ASSERT(m_pDocument->GetExtension());
+  ASSERT(m_iPageIndex >= 0);
+}
 
-CPDFXFA_Page::~CPDFXFA_Page() {}
+CPDFXFA_Page::~CPDFXFA_Page() = default;
+
+CPDF_Page* CPDFXFA_Page::AsPDFPage() {
+  return m_pPDFPage.Get();
+}
+
+CPDFXFA_Page* CPDFXFA_Page::AsXFAPage() {
+  return this;
+}
+
+CPDF_Document* CPDFXFA_Page::GetDocument() const {
+  return m_pDocument.Get();
+}
 
 bool CPDFXFA_Page::LoadPDFPage() {
-  if (!m_pContext)
-    return false;
-
-  CPDF_Document* pPDFDoc = m_pContext->GetPDFDoc();
-  if (!pPDFDoc)
-    return false;
-
-  CPDF_Dictionary* pDict = pPDFDoc->GetPage(m_iPageIndex);
+  CPDF_Document* pPDFDoc = GetDocument();
+  CPDF_Dictionary* pDict = pPDFDoc->GetPageDictionary(m_iPageIndex);
   if (!pDict)
     return false;
 
-  if (!m_pPDFPage || m_pPDFPage->m_pFormDict != pDict) {
-    m_pPDFPage = pdfium::MakeUnique<CPDF_Page>(pPDFDoc, pDict, true);
-    m_pPDFPage->ParseContent();
-  }
+  if (!m_pPDFPage || m_pPDFPage->GetDict() != pDict)
+    LoadPDFPageFromDict(pDict);
+
   return true;
 }
 
-bool CPDFXFA_Page::LoadXFAPageView() {
-  if (!m_pContext)
-    return false;
-
-  CXFA_FFDoc* pXFADoc = m_pContext->GetXFADoc();
-  if (!pXFADoc)
-    return false;
-
-  CXFA_FFDocView* pXFADocView = m_pContext->GetXFADocView();
-  if (!pXFADocView)
-    return false;
-
-  CXFA_FFPageView* pPageView = pXFADocView->GetPageView(m_iPageIndex);
-  if (!pPageView)
-    return false;
-
-  m_pXFAPageView = pPageView;
-  return true;
+CXFA_FFPageView* CPDFXFA_Page::GetXFAPageView() const {
+  auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
+  CXFA_FFDocView* pXFADocView = pContext->GetXFADocView();
+  return pXFADocView ? pXFADocView->GetPageView(m_iPageIndex) : nullptr;
 }
 
 bool CPDFXFA_Page::LoadPage() {
-  if (!m_pContext || m_iPageIndex < 0)
-    return false;
-
-  switch (m_pContext->GetFormType()) {
+  auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
+  switch (pContext->GetFormType()) {
     case FormType::kNone:
     case FormType::kAcroForm:
     case FormType::kXFAForeground:
       return LoadPDFPage();
     case FormType::kXFAFull:
-      return LoadXFAPageView();
+      return !!GetXFAPageView();
   }
+  NOTREACHED();
   return false;
 }
 
-bool CPDFXFA_Page::LoadPDFPage(CPDF_Dictionary* pageDict) {
-  if (!m_pContext || m_iPageIndex < 0 || !pageDict)
-    return false;
-
-  m_pPDFPage =
-      pdfium::MakeUnique<CPDF_Page>(m_pContext->GetPDFDoc(), pageDict, true);
+void CPDFXFA_Page::LoadPDFPageFromDict(CPDF_Dictionary* pPageDict) {
+  ASSERT(pPageDict);
+  m_pPDFPage = pdfium::MakeRetain<CPDF_Page>(GetDocument(), pPageDict);
+  m_pPDFPage->SetRenderCache(
+      pdfium::MakeUnique<CPDF_PageRenderCache>(m_pPDFPage.Get()));
   m_pPDFPage->ParseContent();
-  return true;
 }
 
 float CPDFXFA_Page::GetPageWidth() const {
-  if (!m_pPDFPage && !m_pXFAPageView)
+  CXFA_FFPageView* pPageView = GetXFAPageView();
+  if (!m_pPDFPage && !pPageView)
     return 0.0f;
 
-  switch (m_pContext->GetFormType()) {
+  auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
+  switch (pContext->GetFormType()) {
     case FormType::kNone:
     case FormType::kAcroForm:
     case FormType::kXFAForeground:
       if (m_pPDFPage)
         return m_pPDFPage->GetPageWidth();
+      FALLTHROUGH;
     case FormType::kXFAFull:
-      if (m_pXFAPageView)
-        return m_pXFAPageView->GetPageViewRect().width;
+      if (pPageView)
+        return pPageView->GetPageViewRect().width;
+      break;
   }
 
   return 0.0f;
 }
 
 float CPDFXFA_Page::GetPageHeight() const {
-  if (!m_pPDFPage && !m_pXFAPageView)
+  CXFA_FFPageView* pPageView = GetXFAPageView();
+  if (!m_pPDFPage && !pPageView)
     return 0.0f;
 
-  switch (m_pContext->GetFormType()) {
+  auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
+  switch (pContext->GetFormType()) {
     case FormType::kNone:
     case FormType::kAcroForm:
     case FormType::kXFAForeground:
       if (m_pPDFPage)
         return m_pPDFPage->GetPageHeight();
+      FALLTHROUGH;
     case FormType::kXFAFull:
-      if (m_pXFAPageView)
-        return m_pXFAPageView->GetPageViewRect().height;
+      if (pPageView)
+        return pPageView->GetPageViewRect().height;
+      break;
   }
 
   return 0.0f;
 }
 
-void CPDFXFA_Page::DeviceToPage(int start_x,
-                                int start_y,
-                                int size_x,
-                                int size_y,
-                                int rotate,
-                                int device_x,
-                                int device_y,
-                                double* page_x,
-                                double* page_y) {
-  if (!m_pPDFPage && !m_pXFAPageView)
-    return;
+Optional<CFX_PointF> CPDFXFA_Page::DeviceToPage(
+    const FX_RECT& rect,
+    int rotate,
+    const CFX_PointF& device_point) const {
+  CXFA_FFPageView* pPageView = GetXFAPageView();
+  if (!m_pPDFPage && !pPageView)
+    return {};
 
-  CFX_PointF pos = GetDisplayMatrix(start_x, start_y, size_x, size_y, rotate)
-                       .GetInverse()
-                       .Transform(CFX_PointF(static_cast<float>(device_x),
-                                             static_cast<float>(device_y)));
-
-  *page_x = pos.x;
-  *page_y = pos.y;
+  CFX_PointF pos =
+      GetDisplayMatrix(rect, rotate).GetInverse().Transform(device_point);
+  return pos;
 }
 
-void CPDFXFA_Page::PageToDevice(int start_x,
-                                int start_y,
-                                int size_x,
-                                int size_y,
-                                int rotate,
-                                double page_x,
-                                double page_y,
-                                int* device_x,
-                                int* device_y) {
-  if (!m_pPDFPage && !m_pXFAPageView)
-    return;
+Optional<CFX_PointF> CPDFXFA_Page::PageToDevice(
+    const FX_RECT& rect,
+    int rotate,
+    const CFX_PointF& page_point) const {
+  CXFA_FFPageView* pPageView = GetXFAPageView();
+  if (!m_pPDFPage && !pPageView)
+    return {};
 
-  CFX_Matrix page2device =
-      GetDisplayMatrix(start_x, start_y, size_x, size_y, rotate);
-
-  CFX_PointF pos = page2device.Transform(
-      CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)));
-
-  *device_x = FXSYS_round(pos.x);
-  *device_y = FXSYS_round(pos.y);
+  CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
+  return page2device.Transform(page_point);
 }
 
-CFX_Matrix CPDFXFA_Page::GetDisplayMatrix(int xPos,
-                                          int yPos,
-                                          int xSize,
-                                          int ySize,
+CFX_Matrix CPDFXFA_Page::GetDisplayMatrix(const FX_RECT& rect,
                                           int iRotate) const {
-  if (!m_pPDFPage && !m_pXFAPageView)
+  CXFA_FFPageView* pPageView = GetXFAPageView();
+  if (!m_pPDFPage && !pPageView)
     return CFX_Matrix();
 
-  switch (m_pContext->GetFormType()) {
+  auto* pContext = static_cast<CPDFXFA_Context*>(m_pDocument->GetExtension());
+  switch (pContext->GetFormType()) {
     case FormType::kNone:
     case FormType::kAcroForm:
     case FormType::kXFAForeground:
       if (m_pPDFPage)
-        return m_pPDFPage->GetDisplayMatrix(xPos, yPos, xSize, ySize, iRotate);
+        return m_pPDFPage->GetDisplayMatrix(rect, iRotate);
+      FALLTHROUGH;
     case FormType::kXFAFull:
-      if (m_pXFAPageView)
-        return m_pXFAPageView->GetDisplayMatrix(
-            CFX_Rect(xPos, yPos, xSize, ySize), iRotate);
+      if (pPageView)
+        return pPageView->GetDisplayMatrix(rect, iRotate);
+      break;
   }
 
   return CFX_Matrix();
 }
+
+CPDFSDK_Annot* CPDFXFA_Page::GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot,
+                                             bool bNext) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pSDKAnnot);
+  if (!pXFAWidget)
+    return nullptr;
+
+  ObservedPtr<CPDFSDK_Annot> pObservedAnnot(pSDKAnnot);
+  CPDFSDK_PageView* pPageView = pSDKAnnot->GetPageView();
+  std::unique_ptr<IXFA_WidgetIterator> pWidgetIterator =
+      GetXFAPageView()->CreateTraverseWidgetIterator(XFA_WidgetStatus_Visible |
+                                                     XFA_WidgetStatus_Viewable |
+                                                     XFA_WidgetStatus_Focused);
+
+  // Check |pSDKAnnot| again because JS may have destroyed it
+  if (!pObservedAnnot)
+    return nullptr;
+
+  if (pWidgetIterator->GetCurrentWidget() != pXFAWidget->GetXFAFFWidget())
+    pWidgetIterator->SetCurrentWidget(pXFAWidget->GetXFAFFWidget());
+
+  CXFA_FFWidget* hNextFocus =
+      bNext ? pWidgetIterator->MoveToNext() : pWidgetIterator->MoveToPrevious();
+  if (!hNextFocus && pSDKAnnot)
+    hNextFocus = pWidgetIterator->MoveToFirst();
+
+  return pPageView->GetAnnotByXFAWidget(hNextFocus);
+}
+
+int CPDFXFA_Page::HasFormFieldAtPoint(const CFX_PointF& point) const {
+  CXFA_FFPageView* pPageView = GetXFAPageView();
+  if (!pPageView)
+    return -1;
+
+  CXFA_FFDocView* pDocView = pPageView->GetDocView();
+  if (!pDocView)
+    return -1;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
+  if (!pWidgetHandler)
+    return -1;
+
+  std::unique_ptr<IXFA_WidgetIterator> pWidgetIterator =
+      pPageView->CreateFormWidgetIterator(XFA_WidgetStatus_Viewable);
+
+  CXFA_FFWidget* pXFAAnnot;
+  while ((pXFAAnnot = pWidgetIterator->MoveToNext()) != nullptr) {
+    if (pXFAAnnot->GetFormFieldType() == FormFieldType::kXFA)
+      continue;
+
+    CFX_FloatRect rcWidget = pXFAAnnot->GetWidgetRect().ToFloatRect();
+    rcWidget.Inflate(1.0f, 1.0f);
+    if (rcWidget.Contains(point))
+      return static_cast<int>(pXFAAnnot->GetFormFieldType());
+  }
+
+  return -1;
+}
+
+void CPDFXFA_Page::DrawFocusAnnot(CFX_RenderDevice* pDevice,
+                                  CPDFSDK_Annot* pAnnot,
+                                  const CFX_Matrix& mtUser2Device,
+                                  const FX_RECT& rtClip) {
+  CFX_RectF rectClip(rtClip);
+  CXFA_Graphics gs(pDevice);
+  gs.SetClipRect(rectClip);
+
+  CXFA_FFPageView* xfaView = GetXFAPageView();
+  CXFA_RenderContext renderContext(xfaView, rectClip, mtUser2Device);
+  renderContext.DoRender(&gs);
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return;
+
+  CXFA_FFDocView* docView = xfaView->GetDocView();
+  if (!docView)
+    return;
+
+  docView->GetWidgetHandler()->RenderWidget(pXFAWidget->GetXFAFFWidget(), &gs,
+                                            mtUser2Device, false);
+}
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_page.h b/fpdfsdk/fpdfxfa/cpdfxfa_page.h
index f64d66b..73d5421 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_page.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_page.h
@@ -7,75 +7,63 @@
 #ifndef FPDFSDK_FPDFXFA_CPDFXFA_PAGE_H_
 #define FPDFSDK_FPDFXFA_CPDFXFA_PAGE_H_
 
-#include <memory>
-
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/ipdf_page.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
 
-class CFX_Matrix;
-class CPDFXFA_Context;
+class CFX_RenderDevice;
 class CPDF_Dictionary;
-class CPDF_Page;
+class CPDF_Document;
+class CPDFSDK_Annot;
 class CXFA_FFPageView;
 
-class CPDFXFA_Page : public Retainable {
+class CPDFXFA_Page final : public IPDF_Page {
  public:
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
+  // IPDF_Page:
+  CPDF_Page* AsPDFPage() override;
+  CPDFXFA_Page* AsXFAPage() override;
+  CPDF_Document* GetDocument() const override;
+  float GetPageWidth() const override;
+  float GetPageHeight() const override;
+  CFX_Matrix GetDisplayMatrix(const FX_RECT& rect, int iRotate) const override;
+  Optional<CFX_PointF> DeviceToPage(
+      const FX_RECT& rect,
+      int rotate,
+      const CFX_PointF& device_point) const override;
+  Optional<CFX_PointF> PageToDevice(
+      const FX_RECT& rect,
+      int rotate,
+      const CFX_PointF& page_point) const override;
+
   bool LoadPage();
-  bool LoadPDFPage(CPDF_Dictionary* pageDict);
-  CPDFXFA_Context* GetContext() const { return m_pContext.Get(); }
+  void LoadPDFPageFromDict(CPDF_Dictionary* pPageDict);
   int GetPageIndex() const { return m_iPageIndex; }
-  CPDF_Page* GetPDFPage() const { return m_pPDFPage.get(); }
-  CXFA_FFPageView* GetXFAPageView() const { return m_pXFAPageView; }
+  void SetXFAPageViewIndex(int index) { m_iPageIndex = index; }
+  CXFA_FFPageView* GetXFAPageView() const;
+  CPDFSDK_Annot* GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot, bool bNext);
+  int HasFormFieldAtPoint(const CFX_PointF& point) const;
+  void DrawFocusAnnot(CFX_RenderDevice* pDevice,
+                      CPDFSDK_Annot* pAnnot,
+                      const CFX_Matrix& mtUser2Device,
+                      const FX_RECT& rtClip);
 
-  void SetXFAPageView(CXFA_FFPageView* pPageView) {
-    m_pXFAPageView = pPageView;
-  }
-
-  float GetPageWidth() const;
-  float GetPageHeight() const;
-
-  void DeviceToPage(int start_x,
-                    int start_y,
-                    int size_x,
-                    int size_y,
-                    int rotate,
-                    int device_x,
-                    int device_y,
-                    double* page_x,
-                    double* page_y);
-  void PageToDevice(int start_x,
-                    int start_y,
-                    int size_x,
-                    int size_y,
-                    int rotate,
-                    double page_x,
-                    double page_y,
-                    int* device_x,
-                    int* device_y);
-
-  CFX_Matrix GetDisplayMatrix(int xPos,
-                              int yPos,
-                              int xSize,
-                              int ySize,
-                              int iRotate) const;
-
- protected:
+ private:
   // Refcounted class.
-  CPDFXFA_Page(CPDFXFA_Context* pContext, int page_index);
+  CPDFXFA_Page(CPDF_Document* pDocument, int page_index);
   ~CPDFXFA_Page() override;
 
   bool LoadPDFPage();
-  bool LoadXFAPageView();
 
- private:
-  std::unique_ptr<CPDF_Page> m_pPDFPage;
-  CXFA_FFPageView* m_pXFAPageView;
-  UnownedPtr<CPDFXFA_Context> const m_pContext;
-  const int m_iPageIndex;
+  RetainPtr<CPDF_Page> m_pPDFPage;  // Backing page, if any.
+  UnownedPtr<CPDF_Document> const m_pDocument;
+  int m_iPageIndex;
 };
 
 #endif  // FPDFSDK_FPDFXFA_CPDFXFA_PAGE_H_
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
new file mode 100644
index 0000000..f4c9934
--- /dev/null
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp
@@ -0,0 +1,31 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
+
+#include "fpdfsdk/ipdfsdk_annothandler.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+
+CPDFXFA_Widget::CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget,
+                               CPDFSDK_PageView* pPageView,
+                               CPDFSDK_InteractiveForm* pInteractiveForm)
+    : CPDFSDK_Annot(pPageView),
+      m_pInteractiveForm(pInteractiveForm),
+      m_pXFAFFWidget(pXFAFFWidget) {}
+
+CPDFXFA_Widget::~CPDFXFA_Widget() = default;
+
+CPDFXFA_Widget* CPDFXFA_Widget::AsXFAWidget() {
+  return this;
+}
+
+CPDF_Annot::Subtype CPDFXFA_Widget::GetAnnotSubtype() const {
+  return CPDF_Annot::Subtype::XFAWIDGET;
+}
+
+CFX_FloatRect CPDFXFA_Widget::GetRect() const {
+  return GetXFAFFWidget()->GetLayoutItem()->GetRect(false).ToFloatRect();
+}
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.h b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
new file mode 100644
index 0000000..76d9878
--- /dev/null
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h
@@ -0,0 +1,42 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_FPDFXFA_CPDFXFA_WIDGET_H_
+#define FPDFSDK_FPDFXFA_CPDFXFA_WIDGET_H_
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/cpdfsdk_annot.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+
+class CPDFSDK_InteractiveForm;
+class CPDFSDK_PageView;
+
+class CPDFXFA_Widget final : public CPDFSDK_Annot {
+ public:
+  CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget,
+                 CPDFSDK_PageView* pPageView,
+                 CPDFSDK_InteractiveForm* pInteractiveForm);
+  ~CPDFXFA_Widget() override;
+
+  // CPDFSDK_Annot:
+  CPDFXFA_Widget* AsXFAWidget() override;
+  CPDF_Annot::Subtype GetAnnotSubtype() const override;
+  CFX_FloatRect GetRect() const override;
+
+  CXFA_FFWidget* GetXFAFFWidget() const { return m_pXFAFFWidget.Get(); }
+  CPDFSDK_InteractiveForm* GetInteractiveForm() const {
+    return m_pInteractiveForm.Get();
+  }
+
+ private:
+  UnownedPtr<CPDFSDK_InteractiveForm> const m_pInteractiveForm;
+  ObservedPtr<CXFA_FFWidget> const m_pXFAFFWidget;
+};
+
+#endif  // FPDFSDK_FPDFXFA_CPDFXFA_WIDGET_H_
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp
new file mode 100644
index 0000000..3012b0d
--- /dev/null
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp
@@ -0,0 +1,667 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h"
+
+#include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
+#include "fpdfsdk/cpdfsdk_pageview.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h"
+#include "public/fpdf_fwlevent.h"
+#include "xfa/fwl/cfwl_app.h"
+#include "xfa/fwl/fwl_widgetdef.h"
+#include "xfa/fwl/fwl_widgethit.h"
+#include "xfa/fxfa/cxfa_ffdocview.h"
+#include "xfa/fxfa/cxfa_ffpageview.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/cxfa_ffwidgethandler.h"
+#include "xfa/fxfa/fxfa_basic.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxgraphics/cxfa_graphics.h"
+
+#define CHECK_FWL_VKEY_ENUM____(name)                                   \
+  static_assert(static_cast<int>(name) == static_cast<int>(XFA_##name), \
+                "FWL_VKEYCODE enum mismatch")
+
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Back);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Tab);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NewLine);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Clear);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Return);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Shift);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Control);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Menu);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Pause);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Capital);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kana);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hangul);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Junja);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Final);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hanja);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kanji);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Escape);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Convert);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NonConvert);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Accept);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ModeChange);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Space);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Prior);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Next);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_End);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Home);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Left);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Up);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Right);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Down);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Select);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Print);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Execute);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Snapshot);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Insert);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Delete);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Help);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_0);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_9);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_A);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_B);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_C);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_D);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_E);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_G);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_H);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_I);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_J);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_K);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_L);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_M);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_N);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_O);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_P);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Q);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_R);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_S);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_T);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_U);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_V);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_W);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_X);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Y);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Z);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LWin);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Command);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RWin);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Apps);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Sleep);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad0);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad9);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Multiply);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Add);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Separator);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Subtract);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Decimal);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Divide);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F9);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F10);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F11);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F12);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F13);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F14);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F15);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F16);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F17);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F18);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F19);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F20);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F21);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F22);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F23);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F24);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NunLock);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Scroll);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LShift);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RShift);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LControl);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RControl);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LMenu);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RMenu);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Back);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Forward);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Refresh);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Stop);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Search);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Favorites);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Home);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Mute);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Down);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Up);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_NEXT_Track);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PREV_Track);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_Stop);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PLAY_Pause);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_Mail);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Plus);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Comma);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Minus);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Period);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_2);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_3);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_4);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_5);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_6);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_7);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_8);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_102);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ProcessKey);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Packet);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Attn);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Crsel);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Exsel);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Ereof);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Play);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Zoom);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NoName);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_PA1);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Clear);
+CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Unknown);
+
+#undef CHECK_FWL_VKEY_ENUM____
+
+CPDFXFA_WidgetHandler::CPDFXFA_WidgetHandler() = default;
+
+CPDFXFA_WidgetHandler::~CPDFXFA_WidgetHandler() = default;
+
+void CPDFXFA_WidgetHandler::SetFormFillEnvironment(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  m_pFormFillEnv = pFormFillEnv;
+}
+
+bool CPDFXFA_WidgetHandler::CanAnswer(CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pWidget = ToXFAWidget(pAnnot);
+  return pWidget && pWidget->GetXFAFFWidget();
+}
+
+CPDFSDK_Annot* CPDFXFA_WidgetHandler::NewAnnot(CPDF_Annot* pAnnot,
+                                               CPDFSDK_PageView* pPage) {
+  return nullptr;
+}
+
+std::unique_ptr<CPDFSDK_Annot> CPDFXFA_WidgetHandler::NewAnnotForXFA(
+    CXFA_FFWidget* pAnnot,
+    CPDFSDK_PageView* pPage) {
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
+  return pdfium::MakeUnique<CPDFXFA_Widget>(pAnnot, pPage, pForm);
+}
+
+void CPDFXFA_WidgetHandler::OnDraw(CPDFSDK_PageView* pPageView,
+                                   CPDFSDK_Annot* pAnnot,
+                                   CFX_RenderDevice* pDevice,
+                                   const CFX_Matrix& mtUser2Device,
+                                   bool bDrawAnnots) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  ASSERT(pXFAWidget);
+
+  bool bIsHighlight = false;
+  if (pPageView->GetFormFillEnv()->GetFocusAnnot() != pAnnot)
+    bIsHighlight = true;
+
+  CXFA_Graphics gs(pDevice);
+  GetXFAFFWidgetHandler(pXFAWidget)
+      ->RenderWidget(pXFAWidget->GetXFAFFWidget(), &gs, mtUser2Device,
+                     bIsHighlight);
+
+  // to do highlight and shadow
+}
+
+void CPDFXFA_WidgetHandler::OnLoad(CPDFSDK_Annot* pAnnot) {}
+
+void CPDFXFA_WidgetHandler::ReleaseAnnot(
+    std::unique_ptr<CPDFSDK_Annot> pAnnot) {}
+
+CFX_FloatRect CPDFXFA_WidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView,
+                                                 CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  CXFA_Node* node = pXFAWidget->GetXFAFFWidget()->GetNode();
+  ASSERT(node->IsWidgetReady());
+
+  CFX_RectF rcBBox = pXFAWidget->GetXFAFFWidget()->GetBBox(
+      node->GetFFWidgetType() == XFA_FFWidgetType::kSignature
+          ? CXFA_FFWidget::kDrawFocus
+          : CXFA_FFWidget::kDoNotDrawFocus);
+
+  CFX_FloatRect rcWidget = rcBBox.ToFloatRect();
+  rcWidget.Inflate(1.0f, 1.0f);
+  return rcWidget;
+}
+
+WideString CPDFXFA_WidgetHandler::GetText(CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return WideString();
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->GetText(pXFAWidget->GetXFAFFWidget());
+}
+
+WideString CPDFXFA_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return WideString();
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->GetSelectedText(pXFAWidget->GetXFAFFWidget());
+}
+
+void CPDFXFA_WidgetHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot,
+                                             const WideString& text) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->PasteText(pXFAWidget->GetXFAFFWidget(), text);
+}
+
+bool CPDFXFA_WidgetHandler::CanUndo(CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->CanUndo(pXFAWidget->GetXFAFFWidget());
+}
+
+bool CPDFXFA_WidgetHandler::CanRedo(CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->CanRedo(pXFAWidget->GetXFAFFWidget());
+}
+
+bool CPDFXFA_WidgetHandler::Undo(CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->Undo(pXFAWidget->GetXFAFFWidget());
+}
+
+bool CPDFXFA_WidgetHandler::Redo(CPDFSDK_Annot* pAnnot) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->Redo(pXFAWidget->GetXFAFFWidget());
+}
+
+bool CPDFXFA_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView,
+                                    CPDFSDK_Annot* pAnnot,
+                                    const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
+  if (!pFormFillEnv)
+    return false;
+
+  auto* pContext =
+      static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
+  if (!pContext)
+    return false;
+
+  CXFA_FFDocView* pDocView = pContext->GetXFADocView();
+  if (!pDocView)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler();
+  return pWidgetHandler &&
+         pWidgetHandler->HitTest(pXFAWidget->GetXFAFFWidget(), point) !=
+             FWL_WidgetHit::Unknown;
+}
+
+void CPDFXFA_WidgetHandler::OnMouseEnter(CPDFSDK_PageView* pPageView,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                         uint32_t nFlag) {
+  if (!pPageView)
+    return;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  pWidgetHandler->OnMouseEnter(pXFAWidget->GetXFAFFWidget());
+}
+
+void CPDFXFA_WidgetHandler::OnMouseExit(CPDFSDK_PageView* pPageView,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                        uint32_t nFlag) {
+  if (!pPageView)
+    return;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  pWidgetHandler->OnMouseExit(pXFAWidget->GetXFAFFWidget());
+}
+
+bool CPDFXFA_WidgetHandler::OnLButtonDown(CPDFSDK_PageView* pPageView,
+                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                          uint32_t nFlags,
+                                          const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnLButtonDown(pXFAWidget->GetXFAFFWidget(),
+                                       GetFWLFlags(nFlags), point);
+}
+
+bool CPDFXFA_WidgetHandler::OnLButtonUp(CPDFSDK_PageView* pPageView,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                        uint32_t nFlags,
+                                        const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnLButtonUp(pXFAWidget->GetXFAFFWidget(),
+                                     GetFWLFlags(nFlags), point);
+}
+
+bool CPDFXFA_WidgetHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView,
+                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                            uint32_t nFlags,
+                                            const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnLButtonDblClk(pXFAWidget->GetXFAFFWidget(),
+                                         GetFWLFlags(nFlags), point);
+}
+
+bool CPDFXFA_WidgetHandler::OnMouseMove(CPDFSDK_PageView* pPageView,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                        uint32_t nFlags,
+                                        const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnMouseMove(pXFAWidget->GetXFAFFWidget(),
+                                     GetFWLFlags(nFlags), point);
+}
+
+bool CPDFXFA_WidgetHandler::OnMouseWheel(CPDFSDK_PageView* pPageView,
+                                         ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                         uint32_t nFlags,
+                                         short zDelta,
+                                         const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnMouseWheel(pXFAWidget->GetXFAFFWidget(),
+                                      GetFWLFlags(nFlags), zDelta, point);
+}
+
+bool CPDFXFA_WidgetHandler::OnRButtonDown(CPDFSDK_PageView* pPageView,
+                                          ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                          uint32_t nFlags,
+                                          const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnRButtonDown(pXFAWidget->GetXFAFFWidget(),
+                                       GetFWLFlags(nFlags), point);
+}
+
+bool CPDFXFA_WidgetHandler::OnRButtonUp(CPDFSDK_PageView* pPageView,
+                                        ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                        uint32_t nFlags,
+                                        const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnRButtonUp(pXFAWidget->GetXFAFFWidget(),
+                                     GetFWLFlags(nFlags), point);
+}
+
+bool CPDFXFA_WidgetHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView,
+                                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                            uint32_t nFlags,
+                                            const CFX_PointF& point) {
+  if (!pPageView)
+    return false;
+
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnRButtonDblClk(pXFAWidget->GetXFAFFWidget(),
+                                         GetFWLFlags(nFlags), point);
+}
+
+bool CPDFXFA_WidgetHandler::OnChar(CPDFSDK_Annot* pAnnot,
+                                   uint32_t nChar,
+                                   uint32_t nFlags) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnChar(pXFAWidget->GetXFAFFWidget(), nChar,
+                                GetFWLFlags(nFlags));
+}
+
+bool CPDFXFA_WidgetHandler::OnKeyDown(CPDFSDK_Annot* pAnnot,
+                                      int nKeyCode,
+                                      int nFlag) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnKeyDown(pXFAWidget->GetXFAFFWidget(), nKeyCode,
+                                   GetFWLFlags(nFlag));
+}
+
+bool CPDFXFA_WidgetHandler::OnKeyUp(CPDFSDK_Annot* pAnnot,
+                                    int nKeyCode,
+                                    int nFlag) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot);
+  if (!pXFAWidget)
+    return false;
+
+  CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget);
+  return pWidgetHandler->OnKeyUp(pXFAWidget->GetXFAFFWidget(), nKeyCode,
+                                 GetFWLFlags(nFlag));
+}
+
+bool CPDFXFA_WidgetHandler::OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                       uint32_t nFlag) {
+  return true;
+}
+
+bool CPDFXFA_WidgetHandler::OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                        uint32_t nFlag) {
+  CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get());
+  if (!pXFAWidget)
+    return true;
+
+  CXFA_FFWidget* hWidget = pXFAWidget->GetXFAFFWidget();
+  if (!hWidget)
+    return true;
+
+  CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
+  if (!pXFAPageView)
+    return true;
+
+  pXFAPageView->GetDocView()->SetFocus(nullptr);
+  return true;
+}
+
+bool CPDFXFA_WidgetHandler::OnXFAChangedFocus(
+    ObservedPtr<CPDFSDK_Annot>* pOldAnnot,
+    ObservedPtr<CPDFSDK_Annot>* pNewAnnot) {
+  CXFA_FFWidgetHandler* pWidgetHandler = nullptr;
+  if (pOldAnnot->HasObservable())
+    pWidgetHandler = GetXFAFFWidgetHandler(pOldAnnot->Get());
+  else if (pNewAnnot->HasObservable())
+    pWidgetHandler = GetXFAFFWidgetHandler(pNewAnnot->Get());
+
+  if (!pWidgetHandler)
+    return true;
+
+  CPDFXFA_Widget* pNewXFAWidget = ToXFAWidget(pNewAnnot->Get());
+  if (!pNewXFAWidget)
+    return true;
+
+  CXFA_FFWidget* hWidget = pNewXFAWidget->GetXFAFFWidget();
+  if (!hWidget)
+    return true;
+
+  CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
+  if (!pXFAPageView)
+    return true;
+
+  ObservedPtr<CXFA_FFPageView> pObservedXFAPageView(pXFAPageView);
+  bool bRet = pXFAPageView->GetDocView()->SetFocus(hWidget);
+
+  // Check |pXFAPageView| again because |SetFocus| can trigger JS to destroy it.
+  if (pObservedXFAPageView &&
+      pXFAPageView->GetDocView()->GetFocusWidget() == hWidget) {
+    bRet = true;
+  }
+
+  return bRet;
+}
+
+bool CPDFXFA_WidgetHandler::SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                             int index,
+                                             bool selected) {
+  return false;
+}
+
+bool CPDFXFA_WidgetHandler::IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                            int index) {
+  return false;
+}
+
+CXFA_FFWidgetHandler* CPDFXFA_WidgetHandler::GetXFAFFWidgetHandler(
+    CPDFSDK_Annot* pAnnot) {
+  if (!pAnnot)
+    return nullptr;
+
+  CPDFSDK_PageView* pPageView = pAnnot->GetPageView();
+  if (!pPageView)
+    return nullptr;
+
+  CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv();
+  if (!pFormFillEnv)
+    return nullptr;
+
+  auto* pDoc = static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
+  if (!pDoc)
+    return nullptr;
+
+  CXFA_FFDocView* pDocView = pDoc->GetXFADocView();
+  if (!pDocView)
+    return nullptr;
+
+  return pDocView->GetWidgetHandler();
+}
+
+uint32_t CPDFXFA_WidgetHandler::GetFWLFlags(uint32_t dwFlag) {
+  uint32_t dwFWLFlag = 0;
+
+  if (dwFlag & FWL_EVENTFLAG_ControlKey)
+    dwFWLFlag |= FWL_KEYFLAG_Ctrl;
+  if (dwFlag & FWL_EVENTFLAG_LeftButtonDown)
+    dwFWLFlag |= FWL_KEYFLAG_LButton;
+  if (dwFlag & FWL_EVENTFLAG_MiddleButtonDown)
+    dwFWLFlag |= FWL_KEYFLAG_MButton;
+  if (dwFlag & FWL_EVENTFLAG_RightButtonDown)
+    dwFWLFlag |= FWL_KEYFLAG_RButton;
+  if (dwFlag & FWL_EVENTFLAG_ShiftKey)
+    dwFWLFlag |= FWL_KEYFLAG_Shift;
+  if (dwFlag & FWL_EVENTFLAG_AltKey)
+    dwFWLFlag |= FWL_KEYFLAG_Alt;
+
+  return dwFWLFlag;
+}
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h
new file mode 100644
index 0000000..e18e0ca
--- /dev/null
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h
@@ -0,0 +1,115 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_
+#define FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/ipdfsdk_annothandler.h"
+
+class CFX_Matrix;
+class CFX_RenderDevice;
+class CPDF_Annot;
+class CPDFSDK_FormFillEnvironment;
+class CPDFSDK_Annot;
+class CPDFSDK_PageView;
+class CXFA_FFWidget;
+class CXFA_FFWidgetHandler;
+
+class CPDFXFA_WidgetHandler final : public IPDFSDK_AnnotHandler {
+ public:
+  CPDFXFA_WidgetHandler();
+  ~CPDFXFA_WidgetHandler() override;
+
+  // IPDFSDK_AnnotHandler:
+  void SetFormFillEnvironment(
+      CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
+  bool CanAnswer(CPDFSDK_Annot* pAnnot) override;
+  CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override;
+  void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) override;
+  CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
+                            CPDFSDK_Annot* pAnnot) override;
+  WideString GetText(CPDFSDK_Annot* pAnnot) override;
+  WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
+  void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override;
+  bool CanUndo(CPDFSDK_Annot* pAnnot) override;
+  bool CanRedo(CPDFSDK_Annot* pAnnot) override;
+  bool Undo(CPDFSDK_Annot* pAnnot) override;
+  bool Redo(CPDFSDK_Annot* pAnnot) override;
+  bool HitTest(CPDFSDK_PageView* pPageView,
+               CPDFSDK_Annot* pAnnot,
+               const CFX_PointF& point) override;
+  void OnDraw(CPDFSDK_PageView* pPageView,
+              CPDFSDK_Annot* pAnnot,
+              CFX_RenderDevice* pDevice,
+              const CFX_Matrix& mtUser2Device,
+              bool bDrawAnnots) override;
+  void OnLoad(CPDFSDK_Annot* pAnnot) override;
+  void OnMouseEnter(CPDFSDK_PageView* pPageView,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                    uint32_t nFlag) override;
+  void OnMouseExit(CPDFSDK_PageView* pPageView,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                   uint32_t nFlag) override;
+  bool OnLButtonDown(CPDFSDK_PageView* pPageView,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                     uint32_t nFlags,
+                     const CFX_PointF& point) override;
+  bool OnLButtonUp(CPDFSDK_PageView* pPageView,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                   uint32_t nFlags,
+                   const CFX_PointF& point) override;
+  bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
+                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                       uint32_t nFlags,
+                       const CFX_PointF& point) override;
+  bool OnMouseMove(CPDFSDK_PageView* pPageView,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                   uint32_t nFlags,
+                   const CFX_PointF& point) override;
+  bool OnMouseWheel(CPDFSDK_PageView* pPageView,
+                    ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                    uint32_t nFlags,
+                    short zDelta,
+                    const CFX_PointF& point) override;
+  bool OnRButtonDown(CPDFSDK_PageView* pPageView,
+                     ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                     uint32_t nFlags,
+                     const CFX_PointF& point) override;
+  bool OnRButtonUp(CPDFSDK_PageView* pPageView,
+                   ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                   uint32_t nFlags,
+                   const CFX_PointF& point) override;
+  bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
+                       ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                       uint32_t nFlags,
+                       const CFX_PointF& point) override;
+  bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override;
+  bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
+  bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override;
+  bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
+  bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot, uint32_t nFlag) override;
+  bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                        int index,
+                        bool selected) override;
+  bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot, int index) override;
+
+  std::unique_ptr<CPDFSDK_Annot> NewAnnotForXFA(CXFA_FFWidget* pAnnot,
+                                                CPDFSDK_PageView* pPage);
+  bool OnXFAChangedFocus(ObservedPtr<CPDFSDK_Annot>* pOldAnnot,
+                         ObservedPtr<CPDFSDK_Annot>* pNewAnnot);
+
+ private:
+  CXFA_FFWidgetHandler* GetXFAFFWidgetHandler(CPDFSDK_Annot* pAnnot);
+  uint32_t GetFWLFlags(uint32_t dwFlag);
+
+  UnownedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+};
+
+#endif  // FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_
diff --git a/fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.cpp b/fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.cpp
deleted file mode 100644
index c7201c3..0000000
--- a/fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
-
-#include <utility>
-#include <vector>
-
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/fsdk_define.h"
-
-namespace {
-
-class CFWL_FWLAdapterTimerInfo : public CFWL_TimerInfo {
- public:
-  CFWL_FWLAdapterTimerInfo(IFWL_AdapterTimerMgr* mgr,
-                           int32_t event,
-                           CFWL_Timer* timer)
-      : CFWL_TimerInfo(mgr), idEvent(event), pTimer(timer) {}
-
-  int32_t idEvent;
-  CFWL_Timer* pTimer;
-};
-
-}  // namespace
-
-std::vector<CFWL_TimerInfo*>* CXFA_FWLAdapterTimerMgr::s_TimerArray = nullptr;
-
-CXFA_FWLAdapterTimerMgr::CXFA_FWLAdapterTimerMgr(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv) {}
-
-CXFA_FWLAdapterTimerMgr::~CXFA_FWLAdapterTimerMgr() {}
-
-void CXFA_FWLAdapterTimerMgr::Start(CFWL_Timer* pTimer,
-                                    uint32_t dwElapse,
-                                    bool bImmediately,
-                                    CFWL_TimerInfo** pTimerInfo) {
-  if (!m_pFormFillEnv)
-    return;
-
-  int32_t id_event = m_pFormFillEnv->SetTimer(dwElapse, TimerProc);
-  if (!s_TimerArray)
-    s_TimerArray = new std::vector<CFWL_TimerInfo*>;
-
-  *pTimerInfo = new CFWL_FWLAdapterTimerInfo(this, id_event, pTimer);
-  s_TimerArray->push_back(*pTimerInfo);
-}
-
-void CXFA_FWLAdapterTimerMgr::Stop(CFWL_TimerInfo* pTimerInfo) {
-  if (!pTimerInfo || !m_pFormFillEnv)
-    return;
-
-  CFWL_FWLAdapterTimerInfo* pInfo =
-      static_cast<CFWL_FWLAdapterTimerInfo*>(pTimerInfo);
-  m_pFormFillEnv->KillTimer(pInfo->idEvent);
-  if (!s_TimerArray)
-    return;
-
-  auto it = std::find(s_TimerArray->begin(), s_TimerArray->end(), pInfo);
-  if (it != s_TimerArray->end()) {
-    s_TimerArray->erase(it);
-    delete pInfo;
-  }
-}
-
-// static
-void CXFA_FWLAdapterTimerMgr::TimerProc(int32_t idEvent) {
-  if (!s_TimerArray)
-    return;
-
-  for (auto* info : *s_TimerArray) {
-    CFWL_FWLAdapterTimerInfo* pInfo =
-        static_cast<CFWL_FWLAdapterTimerInfo*>(info);
-    if (pInfo->idEvent == idEvent) {
-      pInfo->pTimer->Run(pInfo);
-      break;
-    }
-  }
-}
diff --git a/fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h b/fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h
deleted file mode 100644
index d763f02..0000000
--- a/fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FPDFXFA_CXFA_FWLADAPTERTIMERMGR_H_
-#define FPDFSDK_FPDFXFA_CXFA_FWLADAPTERTIMERMGR_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "xfa/fwl/cfwl_timerinfo.h"
-#include "xfa/fwl/ifwl_adaptertimermgr.h"
-
-class CXFA_FWLAdapterTimerMgr : public IFWL_AdapterTimerMgr {
- public:
-  explicit CXFA_FWLAdapterTimerMgr(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  ~CXFA_FWLAdapterTimerMgr();
-
-  void Start(CFWL_Timer* pTimer,
-             uint32_t dwElapse,
-             bool bImmediately,
-             CFWL_TimerInfo** pTimerInfo) override;
-  void Stop(CFWL_TimerInfo* pTimerInfo) override;
-
- protected:
-  static void TimerProc(int32_t idEvent);
-
-  static std::vector<CFWL_TimerInfo*>* s_TimerArray;
-  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-};
-
-#endif  // FPDFSDK_FPDFXFA_CXFA_FWLADAPTERTIMERMGR_H_
diff --git a/fpdfsdk/fsdk_actionhandler.cpp b/fpdfsdk/fsdk_actionhandler.cpp
deleted file mode 100644
index de5c931..0000000
--- a/fpdfsdk/fsdk_actionhandler.cpp
+++ /dev/null
@@ -1,619 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/fsdk_actionhandler.h"
-
-#include <set>
-#include <vector>
-
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fxjs/ijs_event_context.h"
-#include "fxjs/ijs_runtime.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
-
-bool CPDFSDK_ActionHandler::DoAction_DocOpen(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<CPDF_Dictionary*> visited;
-  return ExecuteDocumentOpenAction(action, pFormFillEnv, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_JavaScript(
-    const CPDF_Action& JsAction,
-    WideString csJSName,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  if (JsAction.GetType() == CPDF_Action::JavaScript) {
-    WideString swJS = JsAction.GetJavaScript();
-    if (!swJS.IsEmpty()) {
-      RunDocumentOpenJavaScript(pFormFillEnv, csJSName, swJS);
-      return true;
-    }
-  }
-
-  return false;
-}
-
-bool CPDFSDK_ActionHandler::DoAction_FieldJavaScript(
-    const CPDF_Action& JsAction,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    PDFSDK_FieldAction& data) {
-  ASSERT(pFormFillEnv);
-  if (pFormFillEnv->IsJSInitiated() &&
-      JsAction.GetType() == CPDF_Action::JavaScript) {
-    WideString swJS = JsAction.GetJavaScript();
-    if (!swJS.IsEmpty()) {
-      RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS);
-      return true;
-    }
-  }
-  return false;
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Page(
-    const CPDF_Action& action,
-    enum CPDF_AAction::AActionType eType,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<CPDF_Dictionary*> visited;
-  return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Document(
-    const CPDF_Action& action,
-    enum CPDF_AAction::AActionType eType,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<CPDF_Dictionary*> visited;
-  return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_BookMark(
-    CPDF_Bookmark* pBookMark,
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<CPDF_Dictionary*> visited;
-  return ExecuteBookMark(action, pFormFillEnv, pBookMark, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Screen(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDFSDK_Annot* pScreen) {
-  std::set<CPDF_Dictionary*> visited;
-  return ExecuteScreenAction(action, type, pFormFillEnv, pScreen, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Link(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  std::set<CPDF_Dictionary*> visited;
-  return ExecuteLinkAction(action, pFormFillEnv, &visited);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Field(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    PDFSDK_FieldAction& data) {
-  std::set<CPDF_Dictionary*> visited;
-  return ExecuteFieldAction(action, type, pFormFillEnv, pFormField, data,
-                            &visited);
-}
-
-bool CPDFSDK_ActionHandler::ExecuteDocumentOpenAction(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    std::set<CPDF_Dictionary*>* visited) {
-  CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSInitiated()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty()) {
-        RunDocumentOpenJavaScript(pFormFillEnv, L"", swJS);
-      }
-    }
-  } else {
-    DoAction_NoJs(action, pFormFillEnv);
-  }
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteDocumentOpenAction(subaction, pFormFillEnv, visited))
-      return false;
-  }
-
-  return true;
-}
-
-bool CPDFSDK_ActionHandler::ExecuteLinkAction(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    std::set<CPDF_Dictionary*>* visited) {
-  CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSInitiated()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty()) {
-        IJS_Runtime* pRuntime = pFormFillEnv->GetJSRuntime();
-        IJS_EventContext* pContext = pRuntime->NewEventContext();
-        pContext->OnLink_MouseUp(pFormFillEnv);
-
-        WideString csInfo;
-        bool bRet = pContext->RunScript(swJS, &csInfo);
-        pRuntime->ReleaseEventContext(pContext);
-        if (!bRet) {
-          // FIXME: return error.
-        }
-      }
-    }
-  } else {
-    DoAction_NoJs(action, pFormFillEnv);
-  }
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteLinkAction(subaction, pFormFillEnv, visited))
-      return false;
-  }
-
-  return true;
-}
-
-bool CPDFSDK_ActionHandler::ExecuteDocumentPageAction(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    std::set<CPDF_Dictionary*>* visited) {
-  CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSInitiated()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty()) {
-        RunDocumentPageJavaScript(pFormFillEnv, type, swJS);
-      }
-    }
-  } else {
-    DoAction_NoJs(action, pFormFillEnv);
-  }
-
-  if (!IsValidDocView(pFormFillEnv))
-    return false;
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteDocumentPageAction(subaction, type, pFormFillEnv, visited))
-      return false;
-  }
-
-  return true;
-}
-
-bool CPDFSDK_ActionHandler::IsValidField(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_Dictionary* pFieldDict) {
-  ASSERT(pFieldDict);
-
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pPDFInterForm = pInterForm->GetInterForm();
-  return !!pPDFInterForm->GetFieldByDict(pFieldDict);
-}
-
-bool CPDFSDK_ActionHandler::ExecuteFieldAction(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    PDFSDK_FieldAction& data,
-    std::set<CPDF_Dictionary*>* visited) {
-  CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSInitiated()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty()) {
-        RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS);
-        if (!IsValidField(pFormFillEnv, pFormField->GetFieldDict()))
-          return false;
-      }
-    }
-  } else {
-    DoAction_NoJs(action, pFormFillEnv);
-  }
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteFieldAction(subaction, type, pFormFillEnv, pFormField, data,
-                            visited))
-      return false;
-  }
-
-  return true;
-}
-
-bool CPDFSDK_ActionHandler::ExecuteScreenAction(
-    const CPDF_Action& action,
-    CPDF_AAction::AActionType type,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDFSDK_Annot* pScreen,
-    std::set<CPDF_Dictionary*>* visited) {
-  CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSInitiated()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty()) {
-        IJS_Runtime* pRuntime = pFormFillEnv->GetJSRuntime();
-        IJS_EventContext* pContext = pRuntime->NewEventContext();
-        WideString csInfo;
-        bool bRet = pContext->RunScript(swJS, &csInfo);
-        pRuntime->ReleaseEventContext(pContext);
-        if (!bRet) {
-          // FIXME: return error.
-        }
-      }
-    }
-  } else {
-    DoAction_NoJs(action, pFormFillEnv);
-  }
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteScreenAction(subaction, type, pFormFillEnv, pScreen, visited))
-      return false;
-  }
-
-  return true;
-}
-
-bool CPDFSDK_ActionHandler::ExecuteBookMark(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_Bookmark* pBookmark,
-    std::set<CPDF_Dictionary*>* visited) {
-  CPDF_Dictionary* pDict = action.GetDict();
-  if (pdfium::ContainsKey(*visited, pDict))
-    return false;
-
-  visited->insert(pDict);
-
-  ASSERT(pFormFillEnv);
-  if (action.GetType() == CPDF_Action::JavaScript) {
-    if (pFormFillEnv->IsJSInitiated()) {
-      WideString swJS = action.GetJavaScript();
-      if (!swJS.IsEmpty()) {
-        IJS_Runtime* pRuntime = pFormFillEnv->GetJSRuntime();
-        IJS_EventContext* pContext = pRuntime->NewEventContext();
-        pContext->OnBookmark_MouseUp(pBookmark);
-
-        WideString csInfo;
-        bool bRet = pContext->RunScript(swJS, &csInfo);
-        pRuntime->ReleaseEventContext(pContext);
-        if (!bRet) {
-          // FIXME: return error.
-        }
-      }
-    }
-  } else {
-    DoAction_NoJs(action, pFormFillEnv);
-  }
-
-  for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) {
-    CPDF_Action subaction = action.GetSubAction(i);
-    if (!ExecuteBookMark(subaction, pFormFillEnv, pBookmark, visited))
-      return false;
-  }
-
-  return true;
-}
-
-void CPDFSDK_ActionHandler::DoAction_NoJs(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  ASSERT(pFormFillEnv);
-
-  switch (action.GetType()) {
-    case CPDF_Action::GoTo:
-      DoAction_GoTo(pFormFillEnv, action);
-      break;
-    case CPDF_Action::GoToR:
-      DoAction_GoToR(pFormFillEnv, action);
-      break;
-    case CPDF_Action::GoToE:
-      break;
-    case CPDF_Action::Launch:
-      DoAction_Launch(pFormFillEnv, action);
-      break;
-    case CPDF_Action::Thread:
-      break;
-    case CPDF_Action::URI:
-      DoAction_URI(pFormFillEnv, action);
-      break;
-    case CPDF_Action::Sound:
-      break;
-    case CPDF_Action::Movie:
-      break;
-    case CPDF_Action::Hide:
-      DoAction_Hide(action, pFormFillEnv);
-      break;
-    case CPDF_Action::Named:
-      DoAction_Named(pFormFillEnv, action);
-      break;
-    case CPDF_Action::SubmitForm:
-      DoAction_SubmitForm(action, pFormFillEnv);
-      break;
-    case CPDF_Action::ResetForm:
-      DoAction_ResetForm(action, pFormFillEnv);
-      break;
-    case CPDF_Action::ImportData:
-      DoAction_ImportData(action, pFormFillEnv);
-      break;
-    case CPDF_Action::JavaScript:
-      NOTREACHED();
-      break;
-    case CPDF_Action::SetOCGState:
-      DoAction_SetOCGState(pFormFillEnv, action);
-      break;
-    case CPDF_Action::Rendition:
-      break;
-    case CPDF_Action::Trans:
-      break;
-    case CPDF_Action::GoTo3DView:
-      break;
-    default:
-      break;
-  }
-}
-
-bool CPDFSDK_ActionHandler::IsValidDocView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  ASSERT(pFormFillEnv);
-  return true;
-}
-
-void CPDFSDK_ActionHandler::DoAction_GoTo(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  CPDF_Document* pPDFDocument = pFormFillEnv->GetPDFDocument();
-  ASSERT(pPDFDocument);
-
-  CPDF_Dest MyDest = action.GetDest(pPDFDocument);
-  int nPageIndex = MyDest.GetPageIndex(pPDFDocument);
-  int nFitType = MyDest.GetZoomMode();
-  const CPDF_Array* pMyArray = ToArray(MyDest.GetObject());
-  std::vector<float> posArray;
-  if (pMyArray) {
-    for (size_t i = 2; i < pMyArray->GetCount(); i++)
-      posArray.push_back(pMyArray->GetFloatAt(i));
-  }
-  pFormFillEnv->DoGoToAction(nPageIndex, nFitType, posArray.data(),
-                             posArray.size());
-}
-
-void CPDFSDK_ActionHandler::DoAction_GoToR(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {}
-
-void CPDFSDK_ActionHandler::DoAction_Launch(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {}
-
-void CPDFSDK_ActionHandler::DoAction_URI(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  ByteString sURI = action.GetURI(pFormFillEnv->GetPDFDocument());
-  pFormFillEnv->DoURIAction(sURI.c_str());
-}
-
-void CPDFSDK_ActionHandler::DoAction_Named(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {
-  ASSERT(action.GetDict());
-
-  ByteString csName = action.GetNamedAction();
-  pFormFillEnv->ExecuteNamedAction(csName.c_str());
-}
-
-void CPDFSDK_ActionHandler::DoAction_SetOCGState(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const CPDF_Action& action) {}
-
-void CPDFSDK_ActionHandler::RunFieldJavaScript(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_FormField* pFormField,
-    CPDF_AAction::AActionType type,
-    PDFSDK_FieldAction& data,
-    const WideString& script) {
-  ASSERT(type != CPDF_AAction::Calculate);
-  ASSERT(type != CPDF_AAction::Format);
-
-  IJS_Runtime* pRuntime = pFormFillEnv->GetJSRuntime();
-  IJS_EventContext* pContext = pRuntime->NewEventContext();
-  switch (type) {
-    case CPDF_AAction::CursorEnter:
-      pContext->OnField_MouseEnter(data.bModifier, data.bShift, pFormField);
-      break;
-    case CPDF_AAction::CursorExit:
-      pContext->OnField_MouseExit(data.bModifier, data.bShift, pFormField);
-      break;
-    case CPDF_AAction::ButtonDown:
-      pContext->OnField_MouseDown(data.bModifier, data.bShift, pFormField);
-      break;
-    case CPDF_AAction::ButtonUp:
-      pContext->OnField_MouseUp(data.bModifier, data.bShift, pFormField);
-      break;
-    case CPDF_AAction::GetFocus:
-      pContext->OnField_Focus(data.bModifier, data.bShift, pFormField,
-                              data.sValue);
-      break;
-    case CPDF_AAction::LoseFocus:
-      pContext->OnField_Blur(data.bModifier, data.bShift, pFormField,
-                             data.sValue);
-      break;
-    case CPDF_AAction::KeyStroke:
-      pContext->OnField_Keystroke(data.sChange, data.sChangeEx, data.bKeyDown,
-                                  data.bModifier, data.nSelEnd, data.nSelStart,
-                                  data.bShift, pFormField, data.sValue,
-                                  data.bWillCommit, data.bFieldFull, data.bRC);
-      break;
-    case CPDF_AAction::Validate:
-      pContext->OnField_Validate(data.sChange, data.sChangeEx, data.bKeyDown,
-                                 data.bModifier, data.bShift, pFormField,
-                                 data.sValue, data.bRC);
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-
-  WideString csInfo;
-  bool bRet = pContext->RunScript(script, &csInfo);
-  pRuntime->ReleaseEventContext(pContext);
-  if (!bRet) {
-    // FIXME: return error.
-  }
-}
-
-void CPDFSDK_ActionHandler::RunDocumentOpenJavaScript(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const WideString& sScriptName,
-    const WideString& script) {
-  IJS_Runtime* pRuntime = pFormFillEnv->GetJSRuntime();
-  IJS_EventContext* pContext = pRuntime->NewEventContext();
-  pContext->OnDoc_Open(pFormFillEnv, sScriptName);
-
-  WideString csInfo;
-  bool bRet = pContext->RunScript(script, &csInfo);
-  pRuntime->ReleaseEventContext(pContext);
-  if (!bRet) {
-    // FIXME: return error.
-  }
-}
-
-void CPDFSDK_ActionHandler::RunDocumentPageJavaScript(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    CPDF_AAction::AActionType type,
-    const WideString& script) {
-  IJS_Runtime* pRuntime = pFormFillEnv->GetJSRuntime();
-  IJS_EventContext* pContext = pRuntime->NewEventContext();
-  switch (type) {
-    case CPDF_AAction::OpenPage:
-      pContext->OnPage_Open(pFormFillEnv);
-      break;
-    case CPDF_AAction::ClosePage:
-      pContext->OnPage_Close(pFormFillEnv);
-      break;
-    case CPDF_AAction::CloseDocument:
-      pContext->OnDoc_WillClose(pFormFillEnv);
-      break;
-    case CPDF_AAction::SaveDocument:
-      pContext->OnDoc_WillSave(pFormFillEnv);
-      break;
-    case CPDF_AAction::DocumentSaved:
-      pContext->OnDoc_DidSave(pFormFillEnv);
-      break;
-    case CPDF_AAction::PrintDocument:
-      pContext->OnDoc_WillPrint(pFormFillEnv);
-      break;
-    case CPDF_AAction::DocumentPrinted:
-      pContext->OnDoc_DidPrint(pFormFillEnv);
-      break;
-    case CPDF_AAction::PageVisible:
-      pContext->OnPage_InView(pFormFillEnv);
-      break;
-    case CPDF_AAction::PageInvisible:
-      pContext->OnPage_OutView(pFormFillEnv);
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-
-  WideString csInfo;
-  bool bRet = pContext->RunScript(script, &csInfo);
-  pRuntime->ReleaseEventContext(pContext);
-  if (!bRet) {
-    // FIXME: return error.
-  }
-}
-
-bool CPDFSDK_ActionHandler::DoAction_Hide(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  if (pInterForm->DoAction_Hide(action)) {
-    pFormFillEnv->SetChangeMark();
-    return true;
-  }
-
-  return false;
-}
-
-bool CPDFSDK_ActionHandler::DoAction_SubmitForm(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  return pInterForm->DoAction_SubmitForm(action);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_ResetForm(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  return pInterForm->DoAction_ResetForm(action);
-}
-
-bool CPDFSDK_ActionHandler::DoAction_ImportData(
-    const CPDF_Action& action,
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  if (pInterForm->DoAction_ImportData(action)) {
-    pFormFillEnv->SetChangeMark();
-    return true;
-  }
-
-  return false;
-}
diff --git a/fpdfsdk/fsdk_actionhandler.h b/fpdfsdk/fsdk_actionhandler.h
deleted file mode 100644
index 7457b4e..0000000
--- a/fpdfsdk/fsdk_actionhandler.h
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FSDK_ACTIONHANDLER_H_
-#define FPDFSDK_FSDK_ACTIONHANDLER_H_
-
-#include <memory>
-#include <set>
-
-#include "core/fpdfdoc/cpdf_aaction.h"
-#include "core/fpdfdoc/cpdf_action.h"
-#include "core/fxcrt/fx_string.h"
-#include "fpdfsdk/pdfsdk_fieldaction.h"
-
-class CPDFSDK_Annot;
-class CPDFSDK_FormFillEnvironment;
-class CPDF_Bookmark;
-class CPDF_Dictionary;
-class CPDF_FormField;
-
-class CPDFSDK_ActionHandler {
- public:
-  bool DoAction_DocOpen(const CPDF_Action& action,
-                        CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_JavaScript(const CPDF_Action& JsAction,
-                           WideString csJSName,
-                           CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_Page(const CPDF_Action& action,
-                     enum CPDF_AAction::AActionType eType,
-                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_Document(const CPDF_Action& action,
-                         enum CPDF_AAction::AActionType eType,
-                         CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_BookMark(CPDF_Bookmark* pBookMark,
-                         const CPDF_Action& action,
-                         CPDF_AAction::AActionType type,
-                         CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_Screen(const CPDF_Action& action,
-                       CPDF_AAction::AActionType type,
-                       CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                       CPDFSDK_Annot* pScreen);
-  bool DoAction_Link(const CPDF_Action& action,
-                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_Field(const CPDF_Action& action,
-                      CPDF_AAction::AActionType type,
-                      CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                      CPDF_FormField* pFormField,
-                      PDFSDK_FieldAction& data);
-  bool DoAction_FieldJavaScript(const CPDF_Action& JsAction,
-                                CPDF_AAction::AActionType type,
-                                CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                CPDF_FormField* pFormField,
-                                PDFSDK_FieldAction& data);
-
- private:
-  bool ExecuteDocumentOpenAction(const CPDF_Action& action,
-                                 CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 std::set<CPDF_Dictionary*>* visited);
-  bool ExecuteDocumentPageAction(const CPDF_Action& action,
-                                 CPDF_AAction::AActionType type,
-                                 CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 std::set<CPDF_Dictionary*>* visited);
-  bool ExecuteFieldAction(const CPDF_Action& action,
-                          CPDF_AAction::AActionType type,
-                          CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                          CPDF_FormField* pFormField,
-                          PDFSDK_FieldAction& data,
-                          std::set<CPDF_Dictionary*>* visited);
-  bool ExecuteScreenAction(const CPDF_Action& action,
-                           CPDF_AAction::AActionType type,
-                           CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                           CPDFSDK_Annot* pScreen,
-                           std::set<CPDF_Dictionary*>* visited);
-  bool ExecuteBookMark(const CPDF_Action& action,
-                       CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                       CPDF_Bookmark* pBookmark,
-                       std::set<CPDF_Dictionary*>* visited);
-  bool ExecuteLinkAction(const CPDF_Action& action,
-                         CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                         std::set<CPDF_Dictionary*>* visited);
-
-  void DoAction_NoJs(const CPDF_Action& action,
-                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void RunDocumentPageJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 CPDF_AAction::AActionType type,
-                                 const WideString& script);
-  void RunDocumentOpenJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 const WideString& sScriptName,
-                                 const WideString& script);
-  void RunFieldJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                          CPDF_FormField* pFormField,
-                          CPDF_AAction::AActionType type,
-                          PDFSDK_FieldAction& data,
-                          const WideString& script);
-
-  bool IsValidField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                    CPDF_Dictionary* pFieldDict);
-  bool IsValidDocView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void DoAction_GoTo(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                     const CPDF_Action& action);
-  void DoAction_GoToR(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                      const CPDF_Action& action);
-  void DoAction_Launch(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                       const CPDF_Action& action);
-  void DoAction_URI(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                    const CPDF_Action& action);
-  void DoAction_Named(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                      const CPDF_Action& action);
-  void DoAction_SetOCGState(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                            const CPDF_Action& action);
-
-  bool DoAction_Hide(const CPDF_Action& action,
-                     CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_SubmitForm(const CPDF_Action& action,
-                           CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_ResetForm(const CPDF_Action& action,
-                          CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  bool DoAction_ImportData(const CPDF_Action& action,
-                           CPDFSDK_FormFillEnvironment* pFormFillEnv);
-};
-
-#endif  // FPDFSDK_FSDK_ACTIONHANDLER_H_
diff --git a/fpdfsdk/fsdk_baseform_embeddertest.cpp b/fpdfsdk/fsdk_baseform_embeddertest.cpp
deleted file mode 100644
index 13efe86..0000000
--- a/fpdfsdk/fsdk_baseform_embeddertest.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fpdfsdk/cba_annotiterator.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "testing/embedder_test.h"
-#include "testing/embedder_test_mock_delegate.h"
-#include "testing/embedder_test_timer_handling_delegate.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-void CheckRect(const CFX_FloatRect& actual, const CFX_FloatRect& expected) {
-  EXPECT_EQ(expected.left, actual.left);
-  EXPECT_EQ(expected.bottom, actual.bottom);
-  EXPECT_EQ(expected.right, actual.right);
-  EXPECT_EQ(expected.top, actual.top);
-}
-
-}  // namespace
-
-class FSDKBaseFormEmbeddertest : public EmbedderTest {};
-
-TEST_F(FSDKBaseFormEmbeddertest, CBA_AnnotIterator) {
-  EXPECT_TRUE(OpenDocument("annotiter.pdf"));
-  FPDF_PAGE page0 = LoadPage(0);
-  FPDF_PAGE page1 = LoadPage(1);
-  FPDF_PAGE page2 = LoadPage(2);
-  EXPECT_TRUE(page0);
-  EXPECT_TRUE(page1);
-  EXPECT_TRUE(page2);
-
-  CFX_FloatRect LeftBottom(200, 200, 220, 220);
-  CFX_FloatRect RightBottom(400, 201, 420, 221);
-  CFX_FloatRect LeftTop(201, 400, 221, 420);
-  CFX_FloatRect RightTop(401, 401, 421, 421);
-
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      static_cast<CPDFSDK_FormFillEnvironment*>(form_handle());
-
-  {
-    // Page 0 specifies "row order".
-    CBA_AnnotIterator iter(pFormFillEnv->GetPageView(0),
-                           CPDF_Annot::Subtype::WIDGET);
-    CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
-    CheckRect(pAnnot->GetRect(), RightTop);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftTop);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightBottom);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftBottom);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
-
-    pAnnot = iter.GetLastAnnot();
-    CheckRect(pAnnot->GetRect(), LeftBottom);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightBottom);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftTop);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightTop);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
-  }
-  {
-    // Page 1 specifies "column order"
-    CBA_AnnotIterator iter(pFormFillEnv->GetPageView(1),
-                           CPDF_Annot::Subtype::WIDGET);
-    CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
-    CheckRect(pAnnot->GetRect(), RightTop);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightBottom);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftTop);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftBottom);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
-
-    pAnnot = iter.GetLastAnnot();
-    CheckRect(pAnnot->GetRect(), LeftBottom);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftTop);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightBottom);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightTop);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
-  }
-  {
-    // Page 2 specifies "struct order"
-    CBA_AnnotIterator iter(pFormFillEnv->GetPageView(2),
-                           CPDF_Annot::Subtype::WIDGET);
-    CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot();
-    CheckRect(pAnnot->GetRect(), LeftBottom);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightTop);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftTop);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightBottom);
-    pAnnot = iter.GetNextAnnot(pAnnot);
-    EXPECT_EQ(iter.GetFirstAnnot(), pAnnot);
-
-    pAnnot = iter.GetLastAnnot();
-    CheckRect(pAnnot->GetRect(), RightBottom);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftTop);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), RightTop);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    CheckRect(pAnnot->GetRect(), LeftBottom);
-    pAnnot = iter.GetPrevAnnot(pAnnot);
-    EXPECT_EQ(iter.GetLastAnnot(), pAnnot);
-  }
-  UnloadPage(page2);
-  UnloadPage(page1);
-  UnloadPage(page0);
-}
diff --git a/fpdfsdk/fsdk_common.h b/fpdfsdk/fsdk_common.h
deleted file mode 100644
index 78ecd98..0000000
--- a/fpdfsdk/fsdk_common.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FSDK_COMMON_H_
-#define FPDFSDK_FSDK_COMMON_H_
-
-// for all fields
-#define FIELDFLAG_READONLY 1
-#define FIELDFLAG_REQUIRED 2
-// for text fields
-#define FIELDFLAG_MULTILINE (1 << 12)
-#define FIELDFLAG_PASSWORD (1 << 13)
-#define FIELDFLAG_FILESELECT (1 << 20)
-#define FIELDFLAG_DONOTSPELLCHECK (1 << 22)
-#define FIELDFLAG_DONOTSCROLL (1 << 23)
-#define FIELDFLAG_COMB (1 << 24)
-#define FIELDFLAG_RICHTEXT (1 << 25)
-// for button fileds
-#define FIELDFLAG_RADIOSINUNISON (1 << 27)
-// for choice fields
-#define FIELDFLAG_EDIT (1 << 18)
-#define FIELDFLAG_MULTISELECT (1 << 21)
-#define FIELDFLAG_COMMITONSELCHANGE (1 << 26)
-
-#endif  // FPDFSDK_FSDK_COMMON_H_
diff --git a/fpdfsdk/fsdk_define.h b/fpdfsdk/fsdk_define.h
deleted file mode 100644
index 77c2315..0000000
--- a/fpdfsdk/fsdk_define.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FSDK_DEFINE_H_
-#define FPDFSDK_FSDK_DEFINE_H_
-
-#include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "public/fpdfview.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#endif  // PDF_ENABLE_XFA
-
-#ifdef _WIN32
-#include <math.h>
-#include <tchar.h>
-#endif
-
-class CPDF_Annot;
-class CPDF_Page;
-class CPDF_PageObject;
-class CPDF_PageRenderContext;
-class CPDF_PathObject;
-class CPDF_Stream;
-class IFSDK_PAUSE_Adapter;
-class FX_PATHPOINT;
-
-// Layering prevents fxcrt from knowing about FPDF_FILEACCESS, so this can't
-// be a static method of IFX_SeekableReadStream.
-RetainPtr<IFX_SeekableReadStream> MakeSeekableReadStream(
-    FPDF_FILEACCESS* pFileAccess);
-
-#ifdef PDF_ENABLE_XFA
-// Layering prevents fxcrt from knowing about FPDF_FILEHANDLER, so this can't
-// be a static method of IFX_SeekableStream.
-RetainPtr<IFX_SeekableStream> MakeSeekableStream(
-    FPDF_FILEHANDLER* pFileHandler);
-#endif  // PDF_ENABLE_XFA
-
-// Object types for public FPDF_ types; these correspond to next layer down
-// from fpdfsdk. For master, these are CPDF_ types, but for XFA, these are
-// CPDFXFA_ types.
-#ifndef PDF_ENABLE_XFA
-using UnderlyingDocumentType = CPDF_Document;
-using UnderlyingPageType = CPDF_Page;
-#else   // PDF_ENABLE_XFA
-using UnderlyingDocumentType = CPDFXFA_Context;
-using UnderlyingPageType = CPDFXFA_Page;
-#endif  // PDF_ENABLE_XFA
-
-// Conversions to/from underlying types.
-UnderlyingDocumentType* UnderlyingFromFPDFDocument(FPDF_DOCUMENT doc);
-FPDF_DOCUMENT FPDFDocumentFromUnderlying(UnderlyingDocumentType* doc);
-
-UnderlyingPageType* UnderlyingFromFPDFPage(FPDF_PAGE page);
-
-// Conversions to/from FPDF_ types.
-CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc);
-FPDF_DOCUMENT FPDFDocumentFromCPDFDocument(CPDF_Document* doc);
-
-CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page);
-
-CPDF_PathObject* CPDFPathObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object);
-
-CPDF_PageObject* CPDFPageObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object);
-
-CPDF_Object* CPDFObjectFromFPDFAttachment(FPDF_ATTACHMENT attachment);
-
-ByteString CFXByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string);
-
-CFX_DIBitmap* CFXBitmapFromFPDFBitmap(FPDF_BITMAP bitmap);
-
-CFX_FloatRect CFXFloatRectFromFSRECTF(const FS_RECTF& rect);
-void FSRECTFFromCFXFloatRect(const CFX_FloatRect& rect, FS_RECTF* out_rect);
-
-const FX_PATHPOINT* FXPathPointFromFPDFPathSegment(FPDF_PATHSEGMENT segment);
-
-unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
-                                                  void* buffer,
-                                                  unsigned long buflen);
-
-unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream,
-                                                   void* buffer,
-                                                   unsigned long buflen);
-
-void FSDK_SetSandBoxPolicy(FPDF_DWORD policy, FPDF_BOOL enable);
-FPDF_BOOL FSDK_IsSandBoxPolicyEnabled(FPDF_DWORD policy);
-void FPDF_RenderPage_Retail(CPDF_PageRenderContext* pContext,
-                            FPDF_PAGE page,
-                            int start_x,
-                            int start_y,
-                            int size_x,
-                            int size_y,
-                            int rotate,
-                            int flags,
-                            bool bNeedToRestore,
-                            IFSDK_PAUSE_Adapter* pause);
-
-void CheckUnSupportError(CPDF_Document* pDoc, uint32_t err_code);
-void CheckUnSupportAnnot(CPDF_Document* pDoc, const CPDF_Annot* pPDFAnnot);
-void ProcessParseError(CPDF_Parser::Error err);
-FPDF_BOOL FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object,
-                                   unsigned int R,
-                                   unsigned int G,
-                                   unsigned int B,
-                                   unsigned int A);
-
-#endif  // FPDFSDK_FSDK_DEFINE_H_
diff --git a/fpdfsdk/fsdk_filewriteadapter.cpp b/fpdfsdk/fsdk_filewriteadapter.cpp
deleted file mode 100644
index fad058f..0000000
--- a/fpdfsdk/fsdk_filewriteadapter.cpp
+++ /dev/null
@@ -1,22 +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 "fpdfsdk/fsdk_filewriteadapter.h"
-
-FSDK_FileWriteAdapter::FSDK_FileWriteAdapter(FPDF_FILEWRITE* fileWriteStruct)
-    : fileWriteStruct_(fileWriteStruct) {
-  ASSERT(fileWriteStruct_);
-}
-
-FSDK_FileWriteAdapter::~FSDK_FileWriteAdapter() {}
-
-bool FSDK_FileWriteAdapter::WriteBlock(const void* data, size_t size) {
-  return fileWriteStruct_->WriteBlock(fileWriteStruct_, data, size) != 0;
-}
-
-bool FSDK_FileWriteAdapter::WriteString(const ByteStringView& str) {
-  return WriteBlock(str.unterminated_c_str(), str.GetLength());
-}
diff --git a/fpdfsdk/fsdk_filewriteadapter.h b/fpdfsdk/fsdk_filewriteadapter.h
deleted file mode 100644
index 6bba1da..0000000
--- a/fpdfsdk/fsdk_filewriteadapter.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_FSDK_FILEWRITEADAPTER_H_
-#define FPDFSDK_FSDK_FILEWRITEADAPTER_H_
-
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "public/fpdf_save.h"
-
-class FSDK_FileWriteAdapter : public IFX_WriteStream {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  bool WriteBlock(const void* data, size_t size) override;
-  bool WriteString(const ByteStringView& str) override;
-
- private:
-  explicit FSDK_FileWriteAdapter(FPDF_FILEWRITE* fileWriteStruct);
-  ~FSDK_FileWriteAdapter() override;
-
-  FPDF_FILEWRITE* fileWriteStruct_;
-};
-
-#endif  // FPDFSDK_FSDK_FILEWRITEADAPTER_H_
diff --git a/fpdfsdk/fsdk_pauseadapter.cpp b/fpdfsdk/fsdk_pauseadapter.cpp
deleted file mode 100644
index cf99253..0000000
--- a/fpdfsdk/fsdk_pauseadapter.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/fsdk_pauseadapter.h"
-
-IFSDK_PAUSE_Adapter::IFSDK_PAUSE_Adapter(IFSDK_PAUSE* IPause)
-    : m_IPause(IPause) {}
-
-IFSDK_PAUSE_Adapter::~IFSDK_PAUSE_Adapter() {}
-
-bool IFSDK_PAUSE_Adapter::NeedToPauseNow() {
-  return m_IPause->NeedToPauseNow && m_IPause->NeedToPauseNow(m_IPause.Get());
-}
diff --git a/fpdfsdk/fsdk_pauseadapter.h b/fpdfsdk/fsdk_pauseadapter.h
deleted file mode 100644
index a0d0098..0000000
--- a/fpdfsdk/fsdk_pauseadapter.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 FPDFSDK_FSDK_PAUSEADAPTER_H_
-#define FPDFSDK_FSDK_PAUSEADAPTER_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/ifx_pauseindicator.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "public/fpdf_progressive.h"
-
-class IFSDK_PAUSE_Adapter : public IFX_PauseIndicator {
- public:
-  explicit IFSDK_PAUSE_Adapter(IFSDK_PAUSE* IPause);
-  ~IFSDK_PAUSE_Adapter() override;
-
-  bool NeedToPauseNow() override;
-
- private:
-  UnownedPtr<IFSDK_PAUSE> const m_IPause;
-};
-
-#endif  // FPDFSDK_FSDK_PAUSEADAPTER_H_
diff --git a/fpdfsdk/ipdfsdk_annothandler.h b/fpdfsdk/ipdfsdk_annothandler.h
index bffeac6..24b6ff0 100644
--- a/fpdfsdk/ipdfsdk_annothandler.h
+++ b/fpdfsdk/ipdfsdk_annothandler.h
@@ -7,84 +7,83 @@
 #ifndef FPDFSDK_IPDFSDK_ANNOTHANDLER_H_
 #define FPDFSDK_IPDFSDK_ANNOTHANDLER_H_
 
+#include <memory>
+
 #include "core/fxcrt/fx_coordinates.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 
 class CFX_Matrix;
 class CFX_RenderDevice;
 class CPDF_Annot;
+class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_PageView;
 
-#ifdef PDF_ENABLE_XFA
-class CXFA_FFWidget;
-#endif  // PDF_ENABLE_XFA
-
 class IPDFSDK_AnnotHandler {
  public:
-  virtual ~IPDFSDK_AnnotHandler() {}
+  virtual ~IPDFSDK_AnnotHandler() = default;
 
+  virtual void SetFormFillEnvironment(
+      CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
   virtual bool CanAnswer(CPDFSDK_Annot* pAnnot) = 0;
   virtual CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot,
                                   CPDFSDK_PageView* pPage) = 0;
-
-#ifdef PDF_ENABLE_XFA
-  virtual CPDFSDK_Annot* NewAnnot(CXFA_FFWidget* hWidget,
-                                  CPDFSDK_PageView* pPage) = 0;
-#endif  // PDF_ENABLE_XFA
-
-  virtual void ReleaseAnnot(CPDFSDK_Annot* pAnnot) = 0;
+  virtual void ReleaseAnnot(std::unique_ptr<CPDFSDK_Annot> pAnnot) = 0;
   virtual CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
                                     CPDFSDK_Annot* pAnnot) = 0;
+  virtual WideString GetText(CPDFSDK_Annot* pAnnot) = 0;
   virtual WideString GetSelectedText(CPDFSDK_Annot* pAnnot) = 0;
   virtual void ReplaceSelection(CPDFSDK_Annot* pAnnot,
                                 const WideString& text) = 0;
+  virtual bool CanUndo(CPDFSDK_Annot* pAnnot) = 0;
+  virtual bool CanRedo(CPDFSDK_Annot* pAnnot) = 0;
+  virtual bool Undo(CPDFSDK_Annot* pAnnot) = 0;
+  virtual bool Redo(CPDFSDK_Annot* pAnnot) = 0;
   virtual bool HitTest(CPDFSDK_PageView* pPageView,
                        CPDFSDK_Annot* pAnnot,
                        const CFX_PointF& point) = 0;
   virtual void OnDraw(CPDFSDK_PageView* pPageView,
                       CPDFSDK_Annot* pAnnot,
                       CFX_RenderDevice* pDevice,
-                      CFX_Matrix* pUser2Device,
+                      const CFX_Matrix& mtUser2Device,
                       bool bDrawAnnots) = 0;
   virtual void OnLoad(CPDFSDK_Annot* pAnnot) = 0;
-
   virtual void OnMouseEnter(CPDFSDK_PageView* pPageView,
-                            CPDFSDK_Annot::ObservedPtr* pAnnot,
+                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
                             uint32_t nFlag) = 0;
   virtual void OnMouseExit(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                            uint32_t nFlag) = 0;
   virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView,
-                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                              uint32_t nFlags,
                              const CFX_PointF& point) = 0;
   virtual bool OnLButtonUp(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                            uint32_t nFlags,
                            const CFX_PointF& point) = 0;
   virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView,
-                               CPDFSDK_Annot::ObservedPtr* pAnnot,
+                               ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                uint32_t nFlags,
                                const CFX_PointF& point) = 0;
   virtual bool OnMouseMove(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                            uint32_t nFlags,
                            const CFX_PointF& point) = 0;
   virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView,
-                            CPDFSDK_Annot::ObservedPtr* pAnnot,
+                            ObservedPtr<CPDFSDK_Annot>* pAnnot,
                             uint32_t nFlags,
                             short zDelta,
                             const CFX_PointF& point) = 0;
   virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView,
-                             CPDFSDK_Annot::ObservedPtr* pAnnot,
+                             ObservedPtr<CPDFSDK_Annot>* pAnnot,
                              uint32_t nFlags,
                              const CFX_PointF& point) = 0;
   virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView,
-                           CPDFSDK_Annot::ObservedPtr* pAnnot,
+                           ObservedPtr<CPDFSDK_Annot>* pAnnot,
                            uint32_t nFlags,
                            const CFX_PointF& point) = 0;
   virtual bool OnRButtonDblClk(CPDFSDK_PageView* pPageView,
-                               CPDFSDK_Annot::ObservedPtr* pAnnot,
+                               ObservedPtr<CPDFSDK_Annot>* pAnnot,
                                uint32_t nFlags,
                                const CFX_PointF& point) = 0;
   virtual bool OnChar(CPDFSDK_Annot* pAnnot,
@@ -92,14 +91,15 @@
                       uint32_t nFlags) = 0;
   virtual bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) = 0;
   virtual bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) = 0;
-  virtual bool OnSetFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+  virtual bool OnSetFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                           uint32_t nFlag) = 0;
-  virtual bool OnKillFocus(CPDFSDK_Annot::ObservedPtr* pAnnot,
+  virtual bool OnKillFocus(ObservedPtr<CPDFSDK_Annot>* pAnnot,
                            uint32_t nFlag) = 0;
-#ifdef PDF_ENABLE_XFA
-  virtual bool OnXFAChangedFocus(CPDFSDK_Annot::ObservedPtr* pOldAnnot,
-                                 CPDFSDK_Annot::ObservedPtr* pNewAnnot) = 0;
-#endif  // PDF_ENABLE_XFA
+  virtual bool SetIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                                int index,
+                                bool selected) = 0;
+  virtual bool IsIndexSelected(ObservedPtr<CPDFSDK_Annot>* pAnnot,
+                               int index) = 0;
 };
 
 #endif  // FPDFSDK_IPDFSDK_ANNOTHANDLER_H_
diff --git a/fpdfsdk/pdfsdk_fieldaction.cpp b/fpdfsdk/pdfsdk_fieldaction.cpp
deleted file mode 100644
index 61228ea..0000000
--- a/fpdfsdk/pdfsdk_fieldaction.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/pdfsdk_fieldaction.h"
-
-PDFSDK_FieldAction::PDFSDK_FieldAction()
-    : bModifier(false),
-      bShift(false),
-      nCommitKey(0),
-      bKeyDown(false),
-      nSelEnd(0),
-      nSelStart(0),
-      bWillCommit(false),
-      bFieldFull(false),
-      bRC(true) {}
diff --git a/fpdfsdk/pdfsdk_fieldaction.h b/fpdfsdk/pdfsdk_fieldaction.h
deleted file mode 100644
index aec5dca..0000000
--- a/fpdfsdk/pdfsdk_fieldaction.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_PDFSDK_FIELDACTION_H_
-#define FPDFSDK_PDFSDK_FIELDACTION_H_
-
-#include "core/fxcrt/fx_string.h"
-
-#ifdef PDF_ENABLE_XFA
-typedef enum {
-  PDFSDK_XFA_Click = 0,
-  PDFSDK_XFA_Full,
-  PDFSDK_XFA_PreOpen,
-  PDFSDK_XFA_PostOpen
-} PDFSDK_XFAAActionType;
-#endif  // PDF_ENABLE_XFA
-
-struct PDFSDK_FieldAction {
-  PDFSDK_FieldAction();
-  PDFSDK_FieldAction(const PDFSDK_FieldAction& other) = delete;
-
-  bool bModifier;
-  bool bShift;
-  int nCommitKey;
-  WideString sChange;
-  WideString sChangeEx;
-  bool bKeyDown;
-  int nSelEnd;
-  int nSelStart;
-  WideString sValue;
-  bool bWillCommit;
-  bool bFieldFull;
-  bool bRC;
-};
-
-#endif  // FPDFSDK_PDFSDK_FIELDACTION_H_
diff --git a/fpdfsdk/pwl/Android.bp b/fpdfsdk/pwl/Android.bp
new file mode 100644
index 0000000..d57a01f
--- /dev/null
+++ b/fpdfsdk/pwl/Android.bp
@@ -0,0 +1,27 @@
+cc_library_static {
+    name: "libpdfium-pwl",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-font",
+        "libpdfium-render",
+        "libpdfium-fpdfdoc",
+        "libpdfium-fxcrt",
+        "libpdfium-fxge",
+    ],
+
+    srcs: [
+        "*.cpp",
+    ],
+
+    include_dirs: [
+        "external/freetype/include",
+        "external/freetype/include/freetype",
+    ],
+}
diff --git a/fpdfsdk/pwl/BUILD.gn b/fpdfsdk/pwl/BUILD.gn
new file mode 100644
index 0000000..ea26c13
--- /dev/null
+++ b/fpdfsdk/pwl/BUILD.gn
@@ -0,0 +1,60 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+source_set("pwl") {
+  sources = [
+    "cpwl_button.cpp",
+    "cpwl_button.h",
+    "cpwl_caret.cpp",
+    "cpwl_caret.h",
+    "cpwl_combo_box.cpp",
+    "cpwl_combo_box.h",
+    "cpwl_edit.cpp",
+    "cpwl_edit.h",
+    "cpwl_edit_ctrl.cpp",
+    "cpwl_edit_ctrl.h",
+    "cpwl_edit_impl.cpp",
+    "cpwl_edit_impl.h",
+    "cpwl_icon.cpp",
+    "cpwl_icon.h",
+    "cpwl_list_box.cpp",
+    "cpwl_list_box.h",
+    "cpwl_list_impl.cpp",
+    "cpwl_list_impl.h",
+    "cpwl_scroll_bar.cpp",
+    "cpwl_scroll_bar.h",
+    "cpwl_special_button.cpp",
+    "cpwl_special_button.h",
+    "cpwl_wnd.cpp",
+    "cpwl_wnd.h",
+    "ipwl_systemhandler.h",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  deps = [
+    "../../:pdfium_public_headers",
+    "../../constants",
+    "../../core/fpdfapi/font",
+    "../../core/fpdfapi/render",
+    "../../core/fpdfdoc",
+    "../../core/fxcrt",
+    "../../core/fxge",
+  ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [
+    "cpwl_combo_box_embeddertest.cpp",
+    "cpwl_edit_embeddertest.cpp",
+  ]
+  deps = [
+    ":pwl",
+    "../:fpdfsdk",
+    "../formfiller",
+  ]
+  pdfium_root_dir = "../../"
+}
diff --git a/fpdfsdk/pwl/README.md b/fpdfsdk/pwl/README.md
index 1a73250..d9be3b1 100644
--- a/fpdfsdk/pwl/README.md
+++ b/fpdfsdk/pwl/README.md
@@ -17,5 +17,5 @@
     * CPWL_ScrollBar
 
 Widgets are rendered to Appearance Streams, with the case all centralized in
-CPWL_AppStream.
+CPDFSDK_AppStream.
 
diff --git a/fpdfsdk/pwl/cpwl_appstream.cpp b/fpdfsdk/pwl/cpwl_appstream.cpp
deleted file mode 100644
index 203a634..0000000
--- a/fpdfsdk/pwl/cpwl_appstream.cpp
+++ /dev/null
@@ -1,1993 +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 "fpdfsdk/pwl/cpwl_appstream.h"
-
-#include <utility>
-
-#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/fpdfdoc/cpvt_word.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
-#include "fpdfsdk/cpdfsdk_pageview.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/formfiller/cba_fontmap.h"
-#include "fpdfsdk/pwl/cpwl_edit.h"
-#include "fpdfsdk/pwl/cpwl_edit_impl.h"
-#include "fpdfsdk/pwl/cpwl_icon.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-
-namespace {
-
-// Checkbox & radiobutton styles.
-enum class CheckStyle { kCheck = 0, kCircle, kCross, kDiamond, kSquare, kStar };
-
-// Pushbutton layout styles.
-enum class ButtonStyle {
-  kLabel = 0,
-  kIcon,
-  kIconTopLabelBottom,
-  kIconBottomLabelTop,
-  kIconLeftLabelRight,
-  kIconRightLabelLeft,
-  kLabelOverIcon
-};
-
-const char kAppendRectOperator[] = "re";
-const char kConcatMatrixOperator[] = "cm";
-const char kCurveToOperator[] = "c";
-const char kEndPathNoFillOrStrokeOperator[] = "n";
-const char kFillOperator[] = "f";
-const char kFillEvenOddOperator[] = "f*";
-const char kInvokeNamedXObjectOperator[] = "Do";
-const char kLineToOperator[] = "l";
-const char kMarkedSequenceBeginOperator[] = "BMC";
-const char kMarkedSequenceEndOperator[] = "EMC";
-const char kMoveTextPositionOperator[] = "Td";
-const char kMoveToOperator[] = "m";
-const char kSetCharacterSpacingOperator[] = "Tc";
-const char kSetCMYKOperator[] = "k";
-const char kSetCMKYStrokedOperator[] = "K";
-const char kSetDashOperator[] = "d";
-const char kSetGrayOperator[] = "g";
-const char kSetGrayStrokedOperator[] = "G";
-const char kSetLineCapStyleOperator[] = "J";
-const char kSetLineJoinStyleOperator[] = "j";
-const char kSetLineWidthOperator[] = "w";
-const char kSetNonZeroWindingClipOperator[] = "W";
-const char kSetRGBOperator[] = "rg";
-const char kSetRGBStrokedOperator[] = "RG";
-const char kSetTextFontAndSizeOperator[] = "Tf";
-const char kSetTextScaleHorizontalOperator[] = "Tz";
-const char kShowTextOperator[] = "Tj";
-const char kStateRestoreOperator[] = "Q";
-const char kStateSaveOperator[] = "q";
-const char kStrokeOperator[] = "S";
-const char kTextBeginOperator[] = "BT";
-const char kTextEndOperator[] = "ET";
-
-class AutoClosedCommand {
- public:
-  AutoClosedCommand(std::ostringstream* stream,
-                    ByteString open,
-                    ByteString close)
-      : stream_(stream), close_(close) {
-    *stream_ << open << "\n";
-  }
-
-  virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; }
-
- private:
-  std::ostringstream* stream_;
-  ByteString close_;
-};
-
-class AutoClosedQCommand : public AutoClosedCommand {
- public:
-  explicit AutoClosedQCommand(std::ostringstream* stream)
-      : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {}
-  ~AutoClosedQCommand() override {}
-};
-
-ByteString GetColorAppStream(const CFX_Color& color,
-                             const bool& bFillOrStroke) {
-  std::ostringstream sColorStream;
-
-  switch (color.nColorType) {
-    case CFX_Color::kRGB:
-      sColorStream << color.fColor1 << " " << color.fColor2 << " "
-                   << color.fColor3 << " "
-                   << (bFillOrStroke ? kSetRGBOperator : kSetRGBStrokedOperator)
-                   << "\n";
-      break;
-    case CFX_Color::kGray:
-      sColorStream << color.fColor1 << " "
-                   << (bFillOrStroke ? kSetGrayOperator
-                                     : kSetGrayStrokedOperator)
-                   << "\n";
-      break;
-    case CFX_Color::kCMYK:
-      sColorStream << color.fColor1 << " " << color.fColor2 << " "
-                   << color.fColor3 << " " << color.fColor4 << " "
-                   << (bFillOrStroke ? kSetCMYKOperator
-                                     : kSetCMKYStrokedOperator)
-                   << "\n";
-      break;
-  }
-
-  return ByteString(sColorStream);
-}
-
-ByteString GetAP_Check(const CFX_FloatRect& crBBox) {
-  const float fWidth = crBBox.right - crBBox.left;
-  const float fHeight = crBBox.top - crBBox.bottom;
-
-  CFX_PointF pts[8][3] = {{CFX_PointF(0.28f, 0.52f), CFX_PointF(0.27f, 0.48f),
-                           CFX_PointF(0.29f, 0.40f)},
-                          {CFX_PointF(0.30f, 0.33f), CFX_PointF(0.31f, 0.29f),
-                           CFX_PointF(0.31f, 0.28f)},
-                          {CFX_PointF(0.39f, 0.28f), CFX_PointF(0.49f, 0.29f),
-                           CFX_PointF(0.77f, 0.67f)},
-                          {CFX_PointF(0.76f, 0.68f), CFX_PointF(0.78f, 0.69f),
-                           CFX_PointF(0.76f, 0.75f)},
-                          {CFX_PointF(0.76f, 0.75f), CFX_PointF(0.73f, 0.80f),
-                           CFX_PointF(0.68f, 0.75f)},
-                          {CFX_PointF(0.68f, 0.74f), CFX_PointF(0.68f, 0.74f),
-                           CFX_PointF(0.44f, 0.47f)},
-                          {CFX_PointF(0.43f, 0.47f), CFX_PointF(0.40f, 0.47f),
-                           CFX_PointF(0.41f, 0.58f)},
-                          {CFX_PointF(0.40f, 0.60f), CFX_PointF(0.28f, 0.66f),
-                           CFX_PointF(0.30f, 0.56f)}};
-
-  for (size_t i = 0; i < FX_ArraySize(pts); ++i) {
-    for (size_t j = 0; j < FX_ArraySize(pts[0]); ++j) {
-      pts[i][j].x = pts[i][j].x * fWidth + crBBox.left;
-      pts[i][j].y *= pts[i][j].y * fHeight + crBBox.bottom;
-    }
-  }
-
-  std::ostringstream csAP;
-  csAP << pts[0][0].x << " " << pts[0][0].y << " " << kMoveToOperator << "\n";
-
-  for (size_t i = 0; i < FX_ArraySize(pts); ++i) {
-    size_t nNext = i < FX_ArraySize(pts) - 1 ? i + 1 : 0;
-
-    float px1 = pts[i][1].x - pts[i][0].x;
-    float py1 = pts[i][1].y - pts[i][0].y;
-    float px2 = pts[i][2].x - pts[nNext][0].x;
-    float py2 = pts[i][2].y - pts[nNext][0].y;
-
-    csAP << pts[i][0].x + px1 * FX_BEZIER << " "
-         << pts[i][0].y + py1 * FX_BEZIER << " "
-         << pts[nNext][0].x + px2 * FX_BEZIER << " "
-         << pts[nNext][0].y + py2 * FX_BEZIER << " " << pts[nNext][0].x << " "
-         << pts[nNext][0].y << " " << kCurveToOperator << "\n";
-  }
-
-  return ByteString(csAP);
-}
-
-ByteString GetAP_Circle(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
-
-  float fWidth = crBBox.right - crBBox.left;
-  float fHeight = crBBox.top - crBBox.bottom;
-
-  CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2);
-  CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top);
-  CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2);
-  CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom);
-
-  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
-
-  float px = pt2.x - pt1.x;
-  float py = pt2.y - pt1.y;
-
-  csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " "
-       << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y
-       << " " << kCurveToOperator << "\n";
-
-  px = pt3.x - pt2.x;
-  py = pt2.y - pt3.y;
-
-  csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " "
-       << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " "
-       << kCurveToOperator << "\n";
-
-  px = pt3.x - pt4.x;
-  py = pt3.y - pt4.y;
-
-  csAP << pt3.x << " " << pt3.y - py * FX_BEZIER << " "
-       << pt4.x + px * FX_BEZIER << " " << pt4.y << " " << pt4.x << " " << pt4.y
-       << " " << kCurveToOperator << "\n";
-
-  px = pt4.x - pt1.x;
-  py = pt1.y - pt4.y;
-
-  csAP << pt4.x - px * FX_BEZIER << " " << pt4.y << " " << pt1.x << " "
-       << pt1.y - py * FX_BEZIER << " " << pt1.x << " " << pt1.y << " "
-       << kCurveToOperator << "\n";
-
-  return ByteString(csAP);
-}
-
-ByteString GetAP_Cross(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
-
-  csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator
-       << "\n";
-  csAP << crBBox.left << " " << crBBox.bottom << " " << kMoveToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n";
-
-  return ByteString(csAP);
-}
-
-ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
-
-  float fWidth = crBBox.right - crBBox.left;
-  float fHeight = crBBox.top - crBBox.bottom;
-
-  CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2);
-  CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top);
-  CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2);
-  CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom);
-
-  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
-  csAP << pt2.x << " " << pt2.y << " " << kLineToOperator << "\n";
-  csAP << pt3.x << " " << pt3.y << " " << kLineToOperator << "\n";
-  csAP << pt4.x << " " << pt4.y << " " << kLineToOperator << "\n";
-  csAP << pt1.x << " " << pt1.y << " " << kLineToOperator << "\n";
-
-  return ByteString(csAP);
-}
-
-ByteString GetAP_Square(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
-
-  csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n";
-  csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator
-       << "\n";
-  csAP << crBBox.left << " " << crBBox.bottom << " " << kLineToOperator << "\n";
-  csAP << crBBox.left << " " << crBBox.top << " " << kLineToOperator << "\n";
-
-  return ByteString(csAP);
-}
-
-ByteString GetAP_Star(const CFX_FloatRect& crBBox) {
-  std::ostringstream csAP;
-
-  float fRadius = (crBBox.top - crBBox.bottom) / (1 + (float)cos(FX_PI / 5.0f));
-  CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f,
-                                   (crBBox.top + crBBox.bottom) / 2.0f);
-
-  float px[5];
-  float py[5];
-  float fAngel = FX_PI / 10.0f;
-  for (int32_t i = 0; i < 5; i++) {
-    px[i] = ptCenter.x + fRadius * (float)cos(fAngel);
-    py[i] = ptCenter.y + fRadius * (float)sin(fAngel);
-    fAngel += FX_PI * 2 / 5.0f;
-  }
-
-  csAP << px[0] << " " << py[0] << " " << kMoveToOperator << "\n";
-
-  int32_t nNext = 0;
-  for (int32_t j = 0; j < 5; j++) {
-    nNext += 2;
-    if (nNext >= 5)
-      nNext -= 5;
-    csAP << px[nNext] << " " << py[nNext] << " " << kLineToOperator << "\n";
-  }
-
-  return ByteString(csAP);
-}
-
-ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) {
-  std::ostringstream csAP;
-
-  float fWidth = crBBox.right - crBBox.left;
-  float fHeight = crBBox.top - crBBox.bottom;
-
-  CFX_PointF pt1(-fWidth / 2, 0);
-  CFX_PointF pt2(0, fHeight / 2);
-  CFX_PointF pt3(fWidth / 2, 0);
-
-  float px;
-  float py;
-
-  csAP << cos(fRotate) << " " << sin(fRotate) << " " << -sin(fRotate) << " "
-       << cos(fRotate) << " " << crBBox.left + fWidth / 2 << " "
-       << crBBox.bottom + fHeight / 2 << " " << kConcatMatrixOperator << "\n";
-
-  csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n";
-
-  px = pt2.x - pt1.x;
-  py = pt2.y - pt1.y;
-
-  csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " "
-       << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y
-       << " " << kCurveToOperator << "\n";
-
-  px = pt3.x - pt2.x;
-  py = pt2.y - pt3.y;
-
-  csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " "
-       << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " "
-       << kCurveToOperator << "\n";
-
-  return ByteString(csAP);
-}
-
-ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox,
-                              const CFX_Color& crText) {
-  std::ostringstream sAP;
-  {
-    AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Check(rcBBox)
-        << kFillOperator << "\n";
-  }
-  return ByteString(sAP);
-}
-
-ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox,
-                               const CFX_Color& crText) {
-  std::ostringstream sAP;
-  {
-    AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Circle(rcBBox)
-        << kFillOperator << "\n";
-  }
-  return ByteString(sAP);
-}
-
-ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox,
-                              const CFX_Color& crText) {
-  std::ostringstream sAP;
-  {
-    AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, false) << GetAP_Cross(rcBBox)
-        << kStrokeOperator << "\n";
-  }
-  return ByteString(sAP);
-}
-
-ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox,
-                                const CFX_Color& crText) {
-  std::ostringstream sAP;
-  {
-    AutoClosedQCommand q(&sAP);
-    sAP << "1 " << kSetLineWidthOperator << "\n"
-        << GetColorAppStream(crText, true) << GetAP_Diamond(rcBBox)
-        << kFillOperator << "\n";
-  }
-  return ByteString(sAP);
-}
-
-ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox,
-                               const CFX_Color& crText) {
-  std::ostringstream sAP;
-  {
-    AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Square(rcBBox)
-        << kFillOperator << "\n";
-  }
-  return ByteString(sAP);
-}
-
-ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox,
-                             const CFX_Color& crText) {
-  std::ostringstream sAP;
-  {
-    AutoClosedQCommand q(&sAP);
-    sAP << GetColorAppStream(crText, true) << GetAP_Star(rcBBox)
-        << kFillOperator << "\n";
-  }
-  return ByteString(sAP);
-}
-
-ByteString GetCircleFillAppStream(const CFX_FloatRect& rect,
-                                  const CFX_Color& color) {
-  std::ostringstream sAppStream;
-  ByteString sColor = GetColorAppStream(color, true);
-  if (sColor.GetLength() > 0) {
-    AutoClosedQCommand q(&sAppStream);
-    sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n";
-  }
-  return ByteString(sAppStream);
-}
-
-ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect,
-                                    float fWidth,
-                                    const CFX_Color& color,
-                                    const CFX_Color& crLeftTop,
-                                    const CFX_Color& crRightBottom,
-                                    BorderStyle nStyle,
-                                    const CPWL_Dash& dash) {
-  std::ostringstream sAppStream;
-  ByteString sColor;
-
-  if (fWidth > 0.0f) {
-    AutoClosedQCommand q(&sAppStream);
-
-    float fHalfWidth = fWidth / 2.0f;
-    CFX_FloatRect rect_by_2 = rect.GetDeflated(fHalfWidth, fHalfWidth);
-
-    float div = fHalfWidth * 0.75f;
-    CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div);
-    switch (nStyle) {
-      default:
-      case BorderStyle::SOLID:
-      case BorderStyle::UNDERLINE: {
-        sColor = GetColorAppStream(color, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_Circle(rect_by_2) << " "
-                     << kStrokeOperator << "\n";
-        }
-      } break;
-      case BorderStyle::DASH: {
-        sColor = GetColorAppStream(color, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
-                     << "[" << dash.nDash << " " << dash.nGap << "] "
-                     << dash.nPhase << " " << kSetDashOperator << "\n"
-                     << sColor << GetAP_Circle(rect_by_2) << " "
-                     << kStrokeOperator << "\n";
-        }
-      } break;
-      case BorderStyle::BEVELED: {
-        sColor = GetColorAppStream(color, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
-                     << "\n";
-        }
-
-        sColor = GetColorAppStream(crLeftTop, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f)
-                     << " " << kStrokeOperator << "\n";
-        }
-
-        sColor = GetColorAppStream(crRightBottom, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f)
-                     << " " << kStrokeOperator << "\n";
-        }
-      } break;
-      case BorderStyle::INSET: {
-        sColor = GetColorAppStream(color, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
-                     << "\n";
-        }
-
-        sColor = GetColorAppStream(crLeftTop, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f)
-                     << " " << kStrokeOperator << "\n";
-        }
-
-        sColor = GetColorAppStream(crRightBottom, false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q2(&sAppStream);
-          sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
-                     << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f)
-                     << " " << kStrokeOperator << "\n";
-        }
-      } break;
-    }
-  }
-  return ByteString(sAppStream);
-}
-
-ByteString GetCheckBoxAppStream(const CFX_FloatRect& rcBBox,
-                                CheckStyle nStyle,
-                                const CFX_Color& crText) {
-  CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
-  switch (nStyle) {
-    default:
-    case CheckStyle::kCheck:
-      return GetAppStream_Check(rcCenter, crText);
-    case CheckStyle::kCircle:
-      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
-      return GetAppStream_Circle(rcCenter, crText);
-    case CheckStyle::kCross:
-      return GetAppStream_Cross(rcCenter, crText);
-    case CheckStyle::kDiamond:
-      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
-      return GetAppStream_Diamond(rcCenter, crText);
-    case CheckStyle::kSquare:
-      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
-      return GetAppStream_Square(rcCenter, crText);
-    case CheckStyle::kStar:
-      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
-      return GetAppStream_Star(rcCenter, crText);
-  }
-}
-
-ByteString GetRadioButtonAppStream(const CFX_FloatRect& rcBBox,
-                                   CheckStyle nStyle,
-                                   const CFX_Color& crText) {
-  CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
-  switch (nStyle) {
-    default:
-    case CheckStyle::kCheck:
-      return GetAppStream_Check(rcCenter, crText);
-    case CheckStyle::kCircle:
-      rcCenter.ScaleFromCenterPoint(1.0f / 2.0f);
-      return GetAppStream_Circle(rcCenter, crText);
-    case CheckStyle::kCross:
-      return GetAppStream_Cross(rcCenter, crText);
-    case CheckStyle::kDiamond:
-      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
-      return GetAppStream_Diamond(rcCenter, crText);
-    case CheckStyle::kSquare:
-      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
-      return GetAppStream_Square(rcCenter, crText);
-    case CheckStyle::kStar:
-      rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
-      return GetAppStream_Star(rcCenter, crText);
-  }
-}
-
-ByteString GetFontSetString(IPVT_FontMap* pFontMap,
-                            int32_t nFontIndex,
-                            float fFontSize) {
-  if (!pFontMap)
-    return ByteString();
-
-  ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
-  if (sFontAlias.GetLength() <= 0 || fFontSize <= 0)
-    return ByteString();
-
-  std::ostringstream sRet;
-  sRet << "/" << sFontAlias << " " << fFontSize << " "
-       << kSetTextFontAndSizeOperator << "\n";
-  return ByteString(sRet);
-}
-
-ByteString GetWordRenderString(const ByteString& strWords) {
-  if (strWords.GetLength() > 0) {
-    return PDF_EncodeString(strWords, false) + " " + kShowTextOperator + "\n";
-  }
-  return ByteString();
-}
-
-ByteString GetEditAppStream(CPWL_EditImpl* pEdit,
-                            const CFX_PointF& ptOffset,
-                            bool bContinuous,
-                            uint16_t SubWord) {
-  CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator();
-  pIterator->SetAt(0);
-
-  std::ostringstream sEditStream;
-  std::ostringstream sWords;
-  int32_t nCurFontIndex = -1;
-  CFX_PointF ptOld;
-  CFX_PointF ptNew;
-  CPVT_WordPlace oldplace;
-
-  while (pIterator->NextWord()) {
-    CPVT_WordPlace place = pIterator->GetAt();
-    if (bContinuous) {
-      if (place.LineCmp(oldplace) != 0) {
-        if (sWords.tellp() > 0) {
-          sEditStream << GetWordRenderString(ByteString(sWords));
-          sWords.str("");
-        }
-
-        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.x != ptOld.x || ptNew.y != ptOld.y) {
-          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " "
-                      << kMoveTextPositionOperator << "\n";
-
-          ptOld = ptNew;
-        }
-      }
-
-      CPVT_Word word;
-      if (pIterator->GetWord(word)) {
-        if (word.nFontIndex != nCurFontIndex) {
-          if (sWords.tellp() > 0) {
-            sEditStream << GetWordRenderString(ByteString(sWords));
-            sWords.str("");
-          }
-          sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
-                                          word.fFontSize);
-          nCurFontIndex = word.nFontIndex;
-        }
-
-        sWords << pEdit->GetPDFWordString(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.x != ptOld.x || ptNew.y != ptOld.y) {
-          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " "
-                      << kMoveTextPositionOperator << "\n";
-          ptOld = ptNew;
-        }
-
-        if (word.nFontIndex != nCurFontIndex) {
-          sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
-                                          word.fFontSize);
-          nCurFontIndex = word.nFontIndex;
-        }
-
-        sEditStream << GetWordRenderString(
-            pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord));
-      }
-    }
-  }
-
-  if (sWords.tellp() > 0) {
-    sEditStream << GetWordRenderString(ByteString(sWords));
-    sWords.str("");
-  }
-
-  std::ostringstream sAppStream;
-  if (sEditStream.tellp() > 0) {
-    int32_t nHorzScale = pEdit->GetHorzScale();
-    if (nHorzScale != 100) {
-      sAppStream << nHorzScale << " " << kSetTextScaleHorizontalOperator
-                 << "\n";
-    }
-
-    float fCharSpace = pEdit->GetCharSpace();
-    if (!IsFloatZero(fCharSpace)) {
-      sAppStream << fCharSpace << " " << kSetCharacterSpacingOperator << "\n";
-    }
-
-    sAppStream << sEditStream.str();
-  }
-
-  return ByteString(sAppStream);
-}
-
-ByteString GenerateIconAppStream(CPDF_IconFit& fit,
-                                 CPDF_Stream* pIconStream,
-                                 const CFX_FloatRect& rcIcon) {
-  if (rcIcon.IsEmpty() || !pIconStream)
-    return ByteString();
-
-  CPWL_Icon icon;
-  CPWL_Wnd::CreateParams cp;
-  cp.dwFlags = PWS_VISIBLE;
-  icon.Create(cp);
-  icon.SetIconFit(&fit);
-  icon.SetPDFStream(pIconStream);
-  if (!icon.Move(rcIcon, false, false))
-    return ByteString();
-
-  ByteString sAlias = icon.GetImageAlias();
-  if (sAlias.GetLength() <= 0)
-    return ByteString();
-
-  CFX_FloatRect rcPlate = icon.GetClientRect();
-  CFX_Matrix mt = icon.GetImageMatrix().GetInverse();
-
-  float fHScale;
-  float fVScale;
-  std::tie(fHScale, fVScale) = icon.GetScale();
-
-  float fx;
-  float fy;
-  std::tie(fx, fy) = icon.GetImageOffset();
-
-  std::ostringstream str;
-  {
-    AutoClosedQCommand q(&str);
-    str << rcPlate.left << " " << rcPlate.bottom << " "
-        << rcPlate.right - rcPlate.left << " " << rcPlate.top - rcPlate.bottom
-        << " " << kAppendRectOperator << " " << kSetNonZeroWindingClipOperator
-        << " " << kEndPathNoFillOrStrokeOperator << "\n";
-
-    str << fHScale << " 0 0 " << fVScale << " " << rcPlate.left + fx << " "
-        << rcPlate.bottom + fy << " " << kConcatMatrixOperator << "\n";
-    str << mt.a << " " << mt.b << " " << mt.c << " " << mt.d << " " << mt.e
-        << " " << mt.f << " " << kConcatMatrixOperator << "\n";
-
-    str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 "
-        << kSetLineWidthOperator << " /" << sAlias << " "
-        << kInvokeNamedXObjectOperator << "\n";
-  }
-  icon.Destroy();
-
-  return ByteString(str);
-}
-
-ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox,
-                                  IPVT_FontMap* pFontMap,
-                                  CPDF_Stream* pIconStream,
-                                  CPDF_IconFit& IconFit,
-                                  const WideString& sLabel,
-                                  const CFX_Color& crText,
-                                  float fFontSize,
-                                  ButtonStyle nLayOut) {
-  const float fAutoFontScale = 1.0f / 3.0f;
-
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
-  pEdit->SetFontMap(pFontMap);
-  pEdit->SetAlignmentH(1, true);
-  pEdit->SetAlignmentV(1, true);
-  pEdit->SetMultiLine(false, true);
-  pEdit->SetAutoReturn(false, true);
-  if (IsFloatZero(fFontSize))
-    pEdit->SetAutoFontSize(true, true);
-  else
-    pEdit->SetFontSize(fFontSize);
-
-  pEdit->Initialize();
-  pEdit->SetText(sLabel);
-
-  CFX_FloatRect rcLabelContent = pEdit->GetContentRect();
-  CFX_FloatRect rcLabel;
-  CFX_FloatRect rcIcon;
-  float fWidth = 0.0f;
-  float fHeight = 0.0f;
-
-  switch (nLayOut) {
-    case ButtonStyle::kLabel:
-      rcLabel = rcBBox;
-      break;
-    case ButtonStyle::kIcon:
-      rcIcon = rcBBox;
-      break;
-    case ButtonStyle::kIconTopLabelBottom:
-      if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
-          fHeight = rcBBox.top - rcBBox.bottom;
-          rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
-                                  rcBBox.bottom + fHeight * fAutoFontScale);
-          rcIcon =
-              CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, rcBBox.top);
-        } else {
-          fHeight = rcLabelContent.Height();
-
-          if (rcBBox.bottom + fHeight > rcBBox.top) {
-            rcLabel = rcBBox;
-          } else {
-            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
-                                    rcBBox.bottom + fHeight);
-            rcIcon = CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right,
-                                   rcBBox.top);
-          }
-        }
-      } else {
-        rcLabel = rcBBox;
-      }
-      break;
-    case ButtonStyle::kIconBottomLabelTop:
-      if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
-          fHeight = rcBBox.top - rcBBox.bottom;
-          rcLabel =
-              CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale,
-                            rcBBox.right, rcBBox.top);
-          rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
-                                 rcLabel.bottom);
-        } else {
-          fHeight = rcLabelContent.Height();
-
-          if (rcBBox.bottom + fHeight > rcBBox.top) {
-            rcLabel = rcBBox;
-          } else {
-            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight,
-                                    rcBBox.right, rcBBox.top);
-            rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
-                                   rcLabel.bottom);
-          }
-        }
-      } else {
-        rcLabel = rcBBox;
-      }
-      break;
-    case ButtonStyle::kIconLeftLabelRight:
-      if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
-          fWidth = rcBBox.right - rcBBox.left;
-          if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
-            rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale,
-                                    rcBBox.bottom, rcBBox.right, rcBBox.top);
-            rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
-                                   rcBBox.top);
-          } else {
-            if (rcLabelContent.Width() < fWidth) {
-              rcLabel = CFX_FloatRect(rcBBox.right - rcLabelContent.Width(),
-                                      rcBBox.bottom, rcBBox.right, rcBBox.top);
-              rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
-                                     rcBBox.top);
-            } else {
-              rcLabel = rcBBox;
-            }
-          }
-        } else {
-          fWidth = rcLabelContent.Width();
-          if (rcBBox.left + fWidth > rcBBox.right) {
-            rcLabel = rcBBox;
-          } else {
-            rcLabel = CFX_FloatRect(rcBBox.right - fWidth, rcBBox.bottom,
-                                    rcBBox.right, rcBBox.top);
-            rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
-                                   rcBBox.top);
-          }
-        }
-      } else {
-        rcLabel = rcBBox;
-      }
-      break;
-    case ButtonStyle::kIconRightLabelLeft:
-      if (pIconStream) {
-        if (IsFloatZero(fFontSize)) {
-          fWidth = rcBBox.right - rcBBox.left;
-          if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
-            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
-                                    rcBBox.left + fWidth * fAutoFontScale,
-                                    rcBBox.top);
-            rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
-                                   rcBBox.top);
-          } else {
-            if (rcLabelContent.Width() < fWidth) {
-              rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
-                                      rcBBox.left + rcLabelContent.Width(),
-                                      rcBBox.top);
-              rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
-                                     rcBBox.top);
-            } else {
-              rcLabel = rcBBox;
-            }
-          }
-        } else {
-          fWidth = rcLabelContent.Width();
-          if (rcBBox.left + fWidth > rcBBox.right) {
-            rcLabel = rcBBox;
-          } else {
-            rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
-                                    rcBBox.left + fWidth, rcBBox.top);
-            rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
-                                   rcBBox.top);
-          }
-        }
-      } else {
-        rcLabel = rcBBox;
-      }
-      break;
-    case ButtonStyle::kLabelOverIcon:
-      rcLabel = rcBBox;
-      rcIcon = rcBBox;
-      break;
-  }
-
-  std::ostringstream sTemp;
-  sTemp << GenerateIconAppStream(IconFit, pIconStream, rcIcon);
-
-  if (!rcLabel.IsEmpty()) {
-    pEdit->SetPlateRect(rcLabel);
-    ByteString sEdit =
-        GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0);
-    if (sEdit.GetLength() > 0) {
-      AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator);
-      sTemp << GetColorAppStream(crText, true) << sEdit;
-    }
-  }
-
-  if (sTemp.tellp() <= 0)
-    return ByteString();
-
-  std::ostringstream sAppStream;
-  {
-    AutoClosedQCommand q(&sAppStream);
-    sAppStream << rcBBox.left << " " << rcBBox.bottom << " "
-               << rcBBox.right - rcBBox.left << " "
-               << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator
-               << " " << kSetNonZeroWindingClipOperator << " "
-               << kEndPathNoFillOrStrokeOperator << "\n";
-    sAppStream << sTemp.str().c_str();
-  }
-  return ByteString(sAppStream);
-}
-
-ByteString GetBorderAppStreamInternal(const CFX_FloatRect& rect,
-                                      float fWidth,
-                                      const CFX_Color& color,
-                                      const CFX_Color& crLeftTop,
-                                      const CFX_Color& crRightBottom,
-                                      BorderStyle nStyle,
-                                      const CPWL_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;
-    AutoClosedQCommand q(&sAppStream);
-
-    switch (nStyle) {
-      default:
-      case BorderStyle::SOLID:
-        sColor = GetColorAppStream(color, true);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
-                     << fTop - fBottom << " " << kAppendRectOperator << "\n";
-          sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " "
-                     << fRight - fLeft - fWidth * 2 << " "
-                     << fTop - fBottom - fWidth * 2 << " "
-                     << kAppendRectOperator << "\n";
-          sAppStream << kFillEvenOddOperator << "\n";
-        }
-        break;
-      case BorderStyle::DASH:
-        sColor = GetColorAppStream(color, false);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fWidth << " " << kSetLineWidthOperator << " ["
-                     << dash.nDash << " " << dash.nGap << "] " << dash.nPhase
-                     << " " << kSetDashOperator << "\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2 << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2 << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " "
-                     << kLineToOperator << " " << kStrokeOperator << "\n";
-        }
-        break;
-      case BorderStyle::BEVELED:
-      case BorderStyle::INSET:
-        sColor = GetColorAppStream(crLeftTop, true);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
-                     << " " << kFillOperator << "\n";
-        }
-
-        sColor = GetColorAppStream(crRightBottom, true);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth
-                     << " " << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
-                     << kLineToOperator << "\n";
-          sAppStream << fLeft + fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
-                     << "\n";
-          sAppStream << fRight - fHalfWidth * 2 << " "
-                     << fBottom + fHalfWidth * 2 << " " << kLineToOperator
-                     << "\n";
-          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
-                     << " " << kLineToOperator << " " << kFillOperator << "\n";
-        }
-
-        sColor = GetColorAppStream(color, true);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
-                     << fTop - fBottom << " " << kAppendRectOperator << "\n";
-          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
-                     << fRight - fLeft - fHalfWidth * 2 << " "
-                     << fTop - fBottom - fHalfWidth * 2 << " "
-                     << kAppendRectOperator << " " << kFillEvenOddOperator
-                     << "\n";
-        }
-        break;
-      case BorderStyle::UNDERLINE:
-        sColor = GetColorAppStream(color, false);
-        if (sColor.GetLength() > 0) {
-          sAppStream << sColor;
-          sAppStream << fWidth << " " << kSetLineWidthOperator << "\n";
-          sAppStream << fLeft << " " << fBottom + fWidth / 2 << " "
-                     << kMoveToOperator << "\n";
-          sAppStream << fRight << " " << fBottom + fWidth / 2 << " "
-                     << kLineToOperator << " " << kStrokeOperator << "\n";
-        }
-        break;
-    }
-  }
-
-  return ByteString(sAppStream);
-}
-
-ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) {
-  if (rcBBox.IsEmpty())
-    return ByteString();
-
-  std::ostringstream sAppStream;
-  {
-    AutoClosedQCommand q(&sAppStream);
-    sAppStream << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f,
-                                              220.0f / 255.0f, 220.0f / 255.0f),
-                                    true)
-               << rcBBox.left << " " << rcBBox.bottom << " "
-               << rcBBox.right - rcBBox.left << " "
-               << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator
-               << " " << kFillOperator << "\n";
-  }
-
-  {
-    AutoClosedQCommand q(&sAppStream);
-    sAppStream << GetBorderAppStreamInternal(
-        rcBBox, 2, CFX_Color(CFX_Color::kGray, 0),
-        CFX_Color(CFX_Color::kGray, 1), CFX_Color(CFX_Color::kGray, 0.5),
-        BorderStyle::BEVELED, CPWL_Dash(3, 0, 0));
-  }
-
-  CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2,
-                                   (rcBBox.top + rcBBox.bottom) / 2);
-  if (IsFloatBigger(rcBBox.right - rcBBox.left, 6) &&
-      IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) {
-    AutoClosedQCommand q(&sAppStream);
-    sAppStream << " 0 " << kSetGrayOperator << "\n"
-               << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " "
-               << kMoveToOperator << "\n"
-               << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " "
-               << kLineToOperator << "\n"
-               << ptCenter.x << " " << ptCenter.y - 1.5f << " "
-               << kLineToOperator << "\n"
-               << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " "
-               << kLineToOperator << " " << kFillOperator << "\n";
-  }
-
-  return ByteString(sAppStream);
-}
-
-ByteString GetRectFillAppStream(const CFX_FloatRect& rect,
-                                const CFX_Color& color) {
-  std::ostringstream sAppStream;
-  ByteString sColor = GetColorAppStream(color, true);
-  if (sColor.GetLength() > 0) {
-    AutoClosedQCommand q(&sAppStream);
-    sAppStream << sColor << rect.left << " " << rect.bottom << " "
-               << rect.right - rect.left << " " << rect.top - rect.bottom << " "
-               << kAppendRectOperator << " " << kFillOperator << "\n";
-  }
-
-  return ByteString(sAppStream);
-}
-
-}  // namespace
-
-CPWL_AppStream::CPWL_AppStream(CPDFSDK_Widget* widget, CPDF_Dictionary* dict)
-    : widget_(widget), dict_(dict) {}
-
-CPWL_AppStream::~CPWL_AppStream() {}
-
-void CPWL_AppStream::SetAsPushButton() {
-  CPDF_FormControl* pControl = widget_->GetFormControl();
-  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
-  ButtonStyle nLayout = ButtonStyle::kLabel;
-  switch (pControl->GetTextPosition()) {
-    case TEXTPOS_ICON:
-      nLayout = ButtonStyle::kIcon;
-      break;
-    case TEXTPOS_BELOW:
-      nLayout = ButtonStyle::kIconTopLabelBottom;
-      break;
-    case TEXTPOS_ABOVE:
-      nLayout = ButtonStyle::kIconBottomLabelTop;
-      break;
-    case TEXTPOS_RIGHT:
-      nLayout = ButtonStyle::kIconLeftLabelRight;
-      break;
-    case TEXTPOS_LEFT:
-      nLayout = ButtonStyle::kIconRightLabelLeft;
-      break;
-    case TEXTPOS_OVERLAID:
-      nLayout = ButtonStyle::kLabelOverIcon;
-      break;
-    default:
-      nLayout = ButtonStyle::kLabel;
-      break;
-  }
-
-  CFX_Color crBackground;
-  CFX_Color crBorder;
-  int iColorType;
-  float fc[4];
-  pControl->GetOriginalBackgroundColor(iColorType, fc);
-  if (iColorType > 0)
-    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  pControl->GetOriginalBorderColor(iColorType, fc);
-  if (iColorType > 0)
-    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
-  CPWL_Dash dsBorder(3, 0, 0);
-  CFX_Color crLeftTop;
-  CFX_Color crRightBottom;
-
-  BorderStyle nBorderStyle = widget_->GetBorderStyle();
-  switch (nBorderStyle) {
-    case BorderStyle::DASH:
-      dsBorder = CPWL_Dash(3, 3, 0);
-      break;
-    case BorderStyle::BEVELED:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
-      crRightBottom = crBackground / 2.0f;
-      break;
-    case BorderStyle::INSET:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
-      break;
-    default:
-      break;
-  }
-
-  CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
-  CFX_Color crText(CFX_Color::kGray, 0);
-  ByteString csNameTag;
-  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
-  if (da.HasColor()) {
-    da.GetColor(iColorType, fc);
-    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-  }
-  float fFontSize = 12.0f;
-  if (da.HasFont())
-    csNameTag = da.GetFont(&fFontSize);
-
-  WideString csWCaption;
-  WideString csNormalCaption;
-  WideString csRolloverCaption;
-  WideString csDownCaption;
-  if (pControl->HasMKEntry("CA"))
-    csNormalCaption = pControl->GetNormalCaption();
-
-  if (pControl->HasMKEntry("RC"))
-    csRolloverCaption = pControl->GetRolloverCaption();
-
-  if (pControl->HasMKEntry("AC"))
-    csDownCaption = pControl->GetDownCaption();
-
-  CPDF_Stream* pNormalIcon = nullptr;
-  CPDF_Stream* pRolloverIcon = nullptr;
-  CPDF_Stream* pDownIcon = nullptr;
-  if (pControl->HasMKEntry("I"))
-    pNormalIcon = pControl->GetNormalIcon();
-
-  if (pControl->HasMKEntry("RI"))
-    pRolloverIcon = pControl->GetRolloverIcon();
-
-  if (pControl->HasMKEntry("IX"))
-    pDownIcon = pControl->GetDownIcon();
-
-  if (pNormalIcon) {
-    if (CPDF_Dictionary* pImageDict = pNormalIcon->GetDict()) {
-      if (pImageDict->GetStringFor("Name").IsEmpty())
-        pImageDict->SetNewFor<CPDF_String>("Name", "ImgA", false);
-    }
-  }
-
-  if (pRolloverIcon) {
-    if (CPDF_Dictionary* pImageDict = pRolloverIcon->GetDict()) {
-      if (pImageDict->GetStringFor("Name").IsEmpty())
-        pImageDict->SetNewFor<CPDF_String>("Name", "ImgB", false);
-    }
-  }
-
-  if (pDownIcon) {
-    if (CPDF_Dictionary* pImageDict = pDownIcon->GetDict()) {
-      if (pImageDict->GetStringFor("Name").IsEmpty())
-        pImageDict->SetNewFor<CPDF_String>("Name", "ImgC", false);
-    }
-  }
-
-  CPDF_IconFit iconFit = pControl->GetIconFit();
-
-  CBA_FontMap font_map(
-      widget_.Get(),
-      widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler());
-  font_map.SetAPType("N");
-
-  ByteString csAP =
-      GetRectFillAppStream(rcWindow, crBackground) +
-      GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                 crRightBottom, nBorderStyle, dsBorder) +
-      GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
-                             &font_map, pNormalIcon, iconFit, csNormalCaption,
-                             crText, fFontSize, nLayout);
-
-  Write("N", csAP, "");
-  if (pNormalIcon)
-    AddImage("N", pNormalIcon);
-
-  CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode();
-  if (eHLM == CPDF_FormControl::Push || eHLM == CPDF_FormControl::Toggle) {
-    if (csRolloverCaption.IsEmpty() && !pRolloverIcon) {
-      csRolloverCaption = csNormalCaption;
-      pRolloverIcon = pNormalIcon;
-    }
-
-    font_map.SetAPType("R");
-
-    csAP =
-        GetRectFillAppStream(rcWindow, crBackground) +
-        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                   crRightBottom, nBorderStyle, dsBorder) +
-        GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
-                               &font_map, pRolloverIcon, iconFit,
-                               csRolloverCaption, crText, fFontSize, nLayout);
-
-    Write("R", csAP, "");
-    if (pRolloverIcon)
-      AddImage("R", pRolloverIcon);
-
-    if (csDownCaption.IsEmpty() && !pDownIcon) {
-      csDownCaption = csNormalCaption;
-      pDownIcon = pNormalIcon;
-    }
-
-    switch (nBorderStyle) {
-      case BorderStyle::BEVELED: {
-        CFX_Color crTemp = crLeftTop;
-        crLeftTop = crRightBottom;
-        crRightBottom = crTemp;
-        break;
-      }
-      case BorderStyle::INSET: {
-        crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-        crRightBottom = CFX_Color(CFX_Color::kGray, 1);
-        break;
-      }
-      default:
-        break;
-    }
-
-    font_map.SetAPType("D");
-
-    csAP =
-        GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
-        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                   crRightBottom, nBorderStyle, dsBorder) +
-        GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
-                               &font_map, pDownIcon, iconFit, csDownCaption,
-                               crText, fFontSize, nLayout);
-
-    Write("D", csAP, "");
-    if (pDownIcon)
-      AddImage("D", pDownIcon);
-  } else {
-    Remove("D");
-    Remove("R");
-  }
-}
-
-void CPWL_AppStream::SetAsCheckBox() {
-  CPDF_FormControl* pControl = widget_->GetFormControl();
-  CFX_Color crBackground, crBorder, crText;
-  int iColorType;
-  float fc[4];
-
-  pControl->GetOriginalBackgroundColor(iColorType, fc);
-  if (iColorType > 0)
-    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  pControl->GetOriginalBorderColor(iColorType, fc);
-  if (iColorType > 0)
-    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
-  CPWL_Dash dsBorder(3, 0, 0);
-  CFX_Color crLeftTop, crRightBottom;
-
-  BorderStyle nBorderStyle = widget_->GetBorderStyle();
-  switch (nBorderStyle) {
-    case BorderStyle::DASH:
-      dsBorder = CPWL_Dash(3, 3, 0);
-      break;
-    case BorderStyle::BEVELED:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
-      crRightBottom = crBackground / 2.0f;
-      break;
-    case BorderStyle::INSET:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
-      break;
-    default:
-      break;
-  }
-
-  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
-  CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
-  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
-  if (da.HasColor()) {
-    da.GetColor(iColorType, fc);
-    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-  }
-
-  CheckStyle nStyle = CheckStyle::kCheck;
-  WideString csWCaption = pControl->GetNormalCaption();
-  if (csWCaption.GetLength() > 0) {
-    switch (csWCaption[0]) {
-      case L'l':
-        nStyle = CheckStyle::kCircle;
-        break;
-      case L'8':
-        nStyle = CheckStyle::kCross;
-        break;
-      case L'u':
-        nStyle = CheckStyle::kDiamond;
-        break;
-      case L'n':
-        nStyle = CheckStyle::kSquare;
-        break;
-      case L'H':
-        nStyle = CheckStyle::kStar;
-        break;
-      case L'4':
-      default:
-        nStyle = CheckStyle::kCheck;
-    }
-  }
-
-  ByteString csAP_N_ON =
-      GetRectFillAppStream(rcWindow, crBackground) +
-      GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                 crRightBottom, nBorderStyle, dsBorder);
-
-  ByteString csAP_N_OFF = csAP_N_ON;
-
-  switch (nBorderStyle) {
-    case BorderStyle::BEVELED: {
-      CFX_Color crTemp = crLeftTop;
-      crLeftTop = crRightBottom;
-      crRightBottom = crTemp;
-      break;
-    }
-    case BorderStyle::INSET: {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
-      break;
-    }
-    default:
-      break;
-  }
-
-  ByteString csAP_D_ON =
-      GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
-      GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                 crRightBottom, nBorderStyle, dsBorder);
-
-  ByteString csAP_D_OFF = csAP_D_ON;
-
-  csAP_N_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
-  csAP_D_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
-
-  Write("N", csAP_N_ON, pControl->GetCheckedAPState());
-  Write("N", csAP_N_OFF, "Off");
-
-  Write("D", csAP_D_ON, pControl->GetCheckedAPState());
-  Write("D", csAP_D_OFF, "Off");
-
-  ByteString csAS = widget_->GetAppState();
-  if (csAS.IsEmpty())
-    widget_->SetAppState("Off");
-}
-
-void CPWL_AppStream::SetAsRadioButton() {
-  CPDF_FormControl* pControl = widget_->GetFormControl();
-  CFX_Color crBackground;
-  CFX_Color crBorder;
-  CFX_Color crText;
-  int iColorType;
-  float fc[4];
-
-  pControl->GetOriginalBackgroundColor(iColorType, fc);
-  if (iColorType > 0)
-    crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  pControl->GetOriginalBorderColor(iColorType, fc);
-  if (iColorType > 0)
-    crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-
-  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
-  CPWL_Dash dsBorder(3, 0, 0);
-  CFX_Color crLeftTop;
-  CFX_Color crRightBottom;
-  BorderStyle nBorderStyle = widget_->GetBorderStyle();
-  switch (nBorderStyle) {
-    case BorderStyle::DASH:
-      dsBorder = CPWL_Dash(3, 3, 0);
-      break;
-    case BorderStyle::BEVELED:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
-      crRightBottom = crBackground / 2.0f;
-      break;
-    case BorderStyle::INSET:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
-      break;
-    default:
-      break;
-  }
-
-  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
-  CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
-  CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
-  if (da.HasColor()) {
-    da.GetColor(iColorType, fc);
-    crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]);
-  }
-
-  CheckStyle nStyle = CheckStyle::kCircle;
-  WideString csWCaption = pControl->GetNormalCaption();
-  if (csWCaption.GetLength() > 0) {
-    switch (csWCaption[0]) {
-      case L'8':
-        nStyle = CheckStyle::kCross;
-        break;
-      case L'u':
-        nStyle = CheckStyle::kDiamond;
-        break;
-      case L'n':
-        nStyle = CheckStyle::kSquare;
-        break;
-      case L'H':
-        nStyle = CheckStyle::kStar;
-        break;
-      case L'4':
-        nStyle = CheckStyle::kCheck;
-        break;
-      case L'l':
-      default:
-        nStyle = CheckStyle::kCircle;
-    }
-  }
-
-  ByteString csAP_N_ON;
-  CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f);
-  if (nStyle == CheckStyle::kCircle) {
-    if (nBorderStyle == BorderStyle::BEVELED) {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
-      crRightBottom = crBackground - 0.25f;
-    } else if (nBorderStyle == BorderStyle::INSET) {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5f);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75f);
-    }
-
-    csAP_N_ON =
-        GetCircleFillAppStream(rcCenter, crBackground) +
-        GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
-                                 crRightBottom, nBorderStyle, dsBorder);
-  } else {
-    csAP_N_ON =
-        GetRectFillAppStream(rcWindow, crBackground) +
-        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                   crRightBottom, nBorderStyle, dsBorder);
-  }
-
-  ByteString csAP_N_OFF = csAP_N_ON;
-
-  switch (nBorderStyle) {
-    case BorderStyle::BEVELED: {
-      CFX_Color crTemp = crLeftTop;
-      crLeftTop = crRightBottom;
-      crRightBottom = crTemp;
-      break;
-    }
-    case BorderStyle::INSET: {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
-      break;
-    }
-    default:
-      break;
-  }
-
-  ByteString csAP_D_ON;
-
-  if (nStyle == CheckStyle::kCircle) {
-    CFX_Color crBK = crBackground - 0.25f;
-    if (nBorderStyle == BorderStyle::BEVELED) {
-      crLeftTop = crBackground - 0.25f;
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
-      crBK = crBackground;
-    } else if (nBorderStyle == BorderStyle::INSET) {
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 1);
-    }
-
-    csAP_D_ON =
-        GetCircleFillAppStream(rcCenter, crBK) +
-        GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
-                                 crRightBottom, nBorderStyle, dsBorder);
-  } else {
-    csAP_D_ON =
-        GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
-        GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                   crRightBottom, nBorderStyle, dsBorder);
-  }
-
-  ByteString csAP_D_OFF = csAP_D_ON;
-
-  csAP_N_ON += GetRadioButtonAppStream(rcClient, nStyle, crText);
-  csAP_D_ON += GetRadioButtonAppStream(rcClient, nStyle, crText);
-
-  Write("N", csAP_N_ON, pControl->GetCheckedAPState());
-  Write("N", csAP_N_OFF, "Off");
-
-  Write("D", csAP_D_ON, pControl->GetCheckedAPState());
-  Write("D", csAP_D_OFF, "Off");
-
-  ByteString csAS = widget_->GetAppState();
-  if (csAS.IsEmpty())
-    widget_->SetAppState("Off");
-}
-
-void CPWL_AppStream::SetAsComboBox(const WideString* sValue) {
-  CPDF_FormControl* pControl = widget_->GetFormControl();
-  CPDF_FormField* pField = pControl->GetField();
-  std::ostringstream sBody;
-
-  CFX_FloatRect rcClient = widget_->GetClientRect();
-  CFX_FloatRect rcButton = rcClient;
-  rcButton.left = rcButton.right - 13;
-  rcButton.Normalize();
-
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
-  pEdit->EnableRefresh(false);
-
-  CBA_FontMap font_map(
-      widget_.Get(),
-      widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler());
-  pEdit->SetFontMap(&font_map);
-
-  CFX_FloatRect rcEdit = rcClient;
-  rcEdit.right = rcButton.left;
-  rcEdit.Normalize();
-
-  pEdit->SetPlateRect(rcEdit);
-  pEdit->SetAlignmentV(1, true);
-
-  float fFontSize = widget_->GetFontSize();
-  if (IsFloatZero(fFontSize))
-    pEdit->SetAutoFontSize(true, true);
-  else
-    pEdit->SetFontSize(fFontSize);
-
-  pEdit->Initialize();
-
-  if (sValue) {
-    pEdit->SetText(*sValue);
-  } else {
-    int32_t nCurSel = pField->GetSelectedIndex(0);
-    if (nCurSel < 0)
-      pEdit->SetText(pField->GetValue());
-    else
-      pEdit->SetText(pField->GetOptionLabel(nCurSel));
-  }
-
-  CFX_FloatRect rcContent = pEdit->GetContentRect();
-  ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0);
-  if (sEdit.GetLength() > 0) {
-    sBody << "/Tx ";
-    AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
-                          kMarkedSequenceEndOperator);
-    AutoClosedQCommand q(&sBody);
-
-    if (rcContent.Width() > rcEdit.Width() ||
-        rcContent.Height() > rcEdit.Height()) {
-      sBody << rcEdit.left << " " << rcEdit.bottom << " " << rcEdit.Width()
-            << " " << rcEdit.Height() << " " << kAppendRectOperator << "\n"
-            << kSetNonZeroWindingClipOperator << "\n"
-            << kEndPathNoFillOrStrokeOperator << "\n";
-    }
-
-    CFX_Color crText = widget_->GetTextPWLColor();
-    AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
-    sBody << GetColorAppStream(crText, true) << sEdit;
-  }
-
-  sBody << GetDropButtonAppStream(rcButton);
-  Write("N",
-        GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
-        "");
-}
-
-void CPWL_AppStream::SetAsListBox() {
-  CPDF_FormControl* pControl = widget_->GetFormControl();
-  CPDF_FormField* pField = pControl->GetField();
-  CFX_FloatRect rcClient = widget_->GetClientRect();
-  std::ostringstream sBody;
-
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
-  pEdit->EnableRefresh(false);
-
-  CBA_FontMap font_map(
-      widget_.Get(),
-      widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler());
-  pEdit->SetFontMap(&font_map);
-  pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f));
-
-  float fFontSize = widget_->GetFontSize();
-  pEdit->SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize);
-  pEdit->Initialize();
-
-  std::ostringstream sList;
-  float fy = rcClient.top;
-
-  int32_t nTop = pField->GetTopVisibleIndex();
-  int32_t nCount = pField->CountOptions();
-  int32_t nSelCount = pField->CountSelectedItems();
-
-  for (int32_t i = nTop; i < nCount; ++i) {
-    bool bSelected = false;
-    for (int32_t j = 0; j < nSelCount; ++j) {
-      if (pField->GetSelectedIndex(j) == i) {
-        bSelected = true;
-        break;
-      }
-    }
-
-    pEdit->SetText(pField->GetOptionLabel(i));
-
-    CFX_FloatRect rcContent = pEdit->GetContentRect();
-    float fItemHeight = rcContent.Height();
-
-    if (bSelected) {
-      CFX_FloatRect rcItem =
-          CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy);
-      {
-        AutoClosedQCommand q(&sList);
-        sList << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 0, 51.0f / 255.0f,
-                                             113.0f / 255.0f),
-                                   true)
-              << rcItem.left << " " << rcItem.bottom << " " << rcItem.Width()
-              << " " << rcItem.Height() << " " << kAppendRectOperator << " "
-              << kFillOperator << "\n";
-      }
-
-      AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
-      sList << GetColorAppStream(CFX_Color(CFX_Color::kGray, 1), true)
-            << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
-    } else {
-      CFX_Color crText = widget_->GetTextPWLColor();
-
-      AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
-      sList << GetColorAppStream(crText, true)
-            << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
-    }
-
-    fy -= fItemHeight;
-  }
-
-  if (sList.tellp() > 0) {
-    sBody << "/Tx ";
-    AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
-                          kMarkedSequenceEndOperator);
-    AutoClosedQCommand q(&sBody);
-
-    sBody << rcClient.left << " " << rcClient.bottom << " " << rcClient.Width()
-          << " " << rcClient.Height() << " " << kAppendRectOperator << "\n"
-          << kSetNonZeroWindingClipOperator << "\n"
-          << kEndPathNoFillOrStrokeOperator << "\n"
-          << sList.str();
-  }
-  Write("N",
-        GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
-        "");
-}
-
-void CPWL_AppStream::SetAsTextField(const WideString* sValue) {
-  CPDF_FormControl* pControl = widget_->GetFormControl();
-  CPDF_FormField* pField = pControl->GetField();
-  std::ostringstream sBody;
-  std::ostringstream sLines;
-
-  auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>();
-  pEdit->EnableRefresh(false);
-
-  CBA_FontMap font_map(
-      widget_.Get(),
-      widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler());
-  pEdit->SetFontMap(&font_map);
-
-  CFX_FloatRect rcClient = widget_->GetClientRect();
-  pEdit->SetPlateRect(rcClient);
-  pEdit->SetAlignmentH(pControl->GetControlAlignment(), true);
-
-  uint32_t dwFieldFlags = pField->GetFieldFlags();
-  bool bMultiLine = (dwFieldFlags >> 12) & 1;
-  if (bMultiLine) {
-    pEdit->SetMultiLine(true, true);
-    pEdit->SetAutoReturn(true, true);
-  } else {
-    pEdit->SetAlignmentV(1, true);
-  }
-
-  uint16_t subWord = 0;
-  if ((dwFieldFlags >> 13) & 1) {
-    subWord = '*';
-    pEdit->SetPasswordChar(subWord, true);
-  }
-
-  int nMaxLen = pField->GetMaxLen();
-  bool bCharArray = (dwFieldFlags >> 24) & 1;
-  float fFontSize = widget_->GetFontSize();
-
-#ifdef PDF_ENABLE_XFA
-  WideString sValueTmp;
-  if (!sValue && widget_->GetMixXFAWidget()) {
-    sValueTmp = widget_->GetValue(true);
-    sValue = &sValueTmp;
-  }
-#endif  // PDF_ENABLE_XFA
-
-  if (nMaxLen > 0) {
-    if (bCharArray) {
-      pEdit->SetCharArray(nMaxLen);
-
-      if (IsFloatZero(fFontSize)) {
-        fFontSize = CPWL_Edit::GetCharArrayAutoFontSize(font_map.GetPDFFont(0),
-                                                        rcClient, nMaxLen);
-      }
-    } else {
-      if (sValue)
-        nMaxLen = sValue->GetLength();
-      pEdit->SetLimitChar(nMaxLen);
-    }
-  }
-
-  if (IsFloatZero(fFontSize))
-    pEdit->SetAutoFontSize(true, true);
-  else
-    pEdit->SetFontSize(fFontSize);
-
-  pEdit->Initialize();
-  pEdit->SetText(sValue ? *sValue : pField->GetValue());
-
-  CFX_FloatRect rcContent = pEdit->GetContentRect();
-  ByteString sEdit =
-      GetEditAppStream(pEdit.get(), CFX_PointF(), !bCharArray, subWord);
-
-  if (sEdit.GetLength() > 0) {
-    sBody << "/Tx ";
-    AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
-                          kMarkedSequenceEndOperator);
-    AutoClosedQCommand q(&sBody);
-
-    if (rcContent.Width() > rcClient.Width() ||
-        rcContent.Height() > rcClient.Height()) {
-      sBody << rcClient.left << " " << rcClient.bottom << " "
-            << rcClient.Width() << " " << rcClient.Height() << " "
-            << kAppendRectOperator << "\n"
-            << kSetNonZeroWindingClipOperator << "\n"
-            << kEndPathNoFillOrStrokeOperator << "\n";
-    }
-    CFX_Color crText = widget_->GetTextPWLColor();
-
-    AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
-    sBody << GetColorAppStream(crText, true) << sEdit;
-  }
-
-  if (bCharArray) {
-    switch (widget_->GetBorderStyle()) {
-      case BorderStyle::SOLID: {
-        ByteString sColor =
-            GetColorAppStream(widget_->GetBorderPWLColor(), false);
-        if (sColor.GetLength() > 0) {
-          AutoClosedQCommand q(&sLines);
-          sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
-                 << "\n"
-                 << GetColorAppStream(widget_->GetBorderPWLColor(), false)
-                 << " 2 " << kSetLineCapStyleOperator << " 0 "
-                 << kSetLineJoinStyleOperator << "\n";
-
-          for (int32_t i = 1; i < nMaxLen; ++i) {
-            sLines << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.bottom << " " << kMoveToOperator << "\n"
-                   << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.top << " " << kLineToOperator << " "
-                   << kStrokeOperator << "\n";
-          }
-        }
-        break;
-      }
-      case BorderStyle::DASH: {
-        ByteString sColor =
-            GetColorAppStream(widget_->GetBorderPWLColor(), false);
-        if (sColor.GetLength() > 0) {
-          CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0);
-          AutoClosedQCommand q(&sLines);
-          sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
-                 << "\n"
-                 << GetColorAppStream(widget_->GetBorderPWLColor(), false)
-                 << "[" << dsBorder.nDash << " " << dsBorder.nGap << "] "
-                 << dsBorder.nPhase << " " << kSetDashOperator << "\n";
-
-          for (int32_t i = 1; i < nMaxLen; ++i) {
-            sLines << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.bottom << " " << kMoveToOperator << "\n"
-                   << rcClient.left +
-                          ((rcClient.right - rcClient.left) / nMaxLen) * i
-                   << " " << rcClient.top << " " << kLineToOperator << " "
-                   << kStrokeOperator << "\n";
-          }
-        }
-        break;
-      }
-      default:
-        break;
-    }
-  }
-
-  Write("N",
-        GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sLines) +
-            ByteString(sBody),
-        "");
-}
-
-void CPWL_AppStream::AddImage(const ByteString& sAPType, CPDF_Stream* pImage) {
-  CPDF_Stream* pStream = dict_->GetStreamFor(sAPType);
-  CPDF_Dictionary* pStreamDict = pStream->GetDict();
-  ByteString sImageAlias = "IMG";
-
-  if (CPDF_Dictionary* pImageDict = pImage->GetDict()) {
-    sImageAlias = pImageDict->GetStringFor("Name");
-    if (sImageAlias.IsEmpty())
-      sImageAlias = "IMG";
-  }
-
-  CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
-  if (!pStreamResList)
-    pStreamResList = pStreamDict->SetNewFor<CPDF_Dictionary>("Resources");
-
-  CPDF_Dictionary* pXObject =
-      pStreamResList->SetNewFor<CPDF_Dictionary>("XObject");
-  pXObject->SetNewFor<CPDF_Reference>(sImageAlias,
-                                      widget_->GetPageView()->GetPDFDocument(),
-                                      pImage->GetObjNum());
-}
-
-void CPWL_AppStream::Write(const ByteString& sAPType,
-                           const ByteString& sContents,
-                           const ByteString& sAPState) {
-  CPDF_Stream* pStream = nullptr;
-  CPDF_Dictionary* pParentDict = nullptr;
-  if (sAPState.IsEmpty()) {
-    pParentDict = dict_.Get();
-    pStream = dict_->GetStreamFor(sAPType);
-  } else {
-    CPDF_Dictionary* pAPTypeDict = dict_->GetDictFor(sAPType);
-    if (!pAPTypeDict)
-      pAPTypeDict = dict_->SetNewFor<CPDF_Dictionary>(sAPType);
-
-    pParentDict = pAPTypeDict;
-    pStream = pAPTypeDict->GetStreamFor(sAPState);
-  }
-
-  if (!pStream) {
-    CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument();
-    pStream = doc->NewIndirect<CPDF_Stream>();
-    pParentDict->SetNewFor<CPDF_Reference>(sAPType, doc, pStream->GetObjNum());
-  }
-
-  CPDF_Dictionary* pStreamDict = pStream->GetDict();
-  if (!pStreamDict) {
-    auto pNewDict = pdfium::MakeUnique<CPDF_Dictionary>(
-        widget_->GetPDFAnnot()->GetDocument()->GetByteStringPool());
-    pStreamDict = pNewDict.get();
-    pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
-    pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
-    pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
-    pStream->InitStream(nullptr, 0, std::move(pNewDict));
-  }
-  pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix());
-  pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect());
-  pStream->SetDataAndRemoveFilter((uint8_t*)(sContents.c_str()),
-                                  sContents.GetLength());
-}
-
-void CPWL_AppStream::Remove(const ByteString& sAPType) {
-  dict_->RemoveFor(sAPType);
-}
-
-ByteString CPWL_AppStream::GetBackgroundAppStream() const {
-  CFX_Color crBackground = widget_->GetFillPWLColor();
-  if (crBackground.nColorType != CFX_Color::kTransparent)
-    return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground);
-
-  return ByteString();
-}
-
-ByteString CPWL_AppStream::GetBorderAppStream() const {
-  CFX_FloatRect rcWindow = widget_->GetRotatedRect();
-  CFX_Color crBorder = widget_->GetBorderPWLColor();
-  CFX_Color crBackground = widget_->GetFillPWLColor();
-  CFX_Color crLeftTop;
-  CFX_Color crRightBottom;
-
-  float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
-  CPWL_Dash dsBorder(3, 0, 0);
-
-  BorderStyle nBorderStyle = widget_->GetBorderStyle();
-  switch (nBorderStyle) {
-    case BorderStyle::DASH:
-      dsBorder = CPWL_Dash(3, 3, 0);
-      break;
-    case BorderStyle::BEVELED:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 1);
-      crRightBottom = crBackground / 2.0f;
-      break;
-    case BorderStyle::INSET:
-      fBorderWidth *= 2;
-      crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
-      crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
-      break;
-    default:
-      break;
-  }
-
-  return GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
-                                    crRightBottom, nBorderStyle, dsBorder);
-}
diff --git a/fpdfsdk/pwl/cpwl_appstream.h b/fpdfsdk/pwl/cpwl_appstream.h
deleted file mode 100644
index 810e79c..0000000
--- a/fpdfsdk/pwl/cpwl_appstream.h
+++ /dev/null
@@ -1,43 +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 FPDFSDK_PWL_CPWL_APPSTREAM_H_
-#define FPDFSDK_PWL_CPWL_APPSTREAM_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CPDFSDK_Widget;
-class CPDF_Dictionary;
-class CPDF_Stream;
-
-class CPWL_AppStream {
- public:
-  CPWL_AppStream(CPDFSDK_Widget* widget, CPDF_Dictionary* dict);
-  ~CPWL_AppStream();
-
-  void SetAsPushButton();
-  void SetAsCheckBox();
-  void SetAsRadioButton();
-  void SetAsComboBox(const WideString* sValue);
-  void SetAsListBox();
-  void SetAsTextField(const WideString* sValue);
-
- private:
-  void AddImage(const ByteString& sAPType, CPDF_Stream* pImage);
-  void Write(const ByteString& sAPType,
-             const ByteString& sContents,
-             const ByteString& sAPState);
-  void Remove(const ByteString& sAPType);
-
-  ByteString GetBackgroundAppStream() const;
-  ByteString GetBorderAppStream() const;
-
-  UnownedPtr<CPDFSDK_Widget> widget_;
-  UnownedPtr<CPDF_Dictionary> dict_;
-};
-
-#endif  // FPDFSDK_PWL_CPWL_APPSTREAM_H_
diff --git a/fpdfsdk/pwl/cpwl_button.cpp b/fpdfsdk/pwl/cpwl_button.cpp
index 5c06a98..b81afae 100644
--- a/fpdfsdk/pwl/cpwl_button.cpp
+++ b/fpdfsdk/pwl/cpwl_button.cpp
@@ -5,19 +5,17 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "fpdfsdk/pwl/cpwl_button.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
 
-CPWL_Button::CPWL_Button() : m_bMouseDown(false) {}
+#include <utility>
 
-CPWL_Button::~CPWL_Button() {}
-
-ByteString CPWL_Button::GetClassName() const {
-  return "CPWL_Button";
+CPWL_Button::CPWL_Button(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)) {
+  GetCreationParams()->eCursorType = FXCT_HAND;
 }
 
-void CPWL_Button::OnCreate(CreateParams* pParamsToAdjust) {
-  pParamsToAdjust->eCursorType = FXCT_HAND;
-}
+CPWL_Button::~CPWL_Button() = default;
 
 bool CPWL_Button::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
   CPWL_Wnd::OnLButtonDown(point, nFlag);
diff --git a/fpdfsdk/pwl/cpwl_button.h b/fpdfsdk/pwl/cpwl_button.h
index 70f5ef1..e7760dd 100644
--- a/fpdfsdk/pwl/cpwl_button.h
+++ b/fpdfsdk/pwl/cpwl_button.h
@@ -7,21 +7,23 @@
 #ifndef FPDFSDK_PWL_CPWL_BUTTON_H_
 #define FPDFSDK_PWL_CPWL_BUTTON_H_
 
+#include <memory>
+
 #include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "fpdfsdk/pwl/ipwl_systemhandler.h"
 
 class CPWL_Button : public CPWL_Wnd {
  public:
-  CPWL_Button();
+  CPWL_Button(const CreateParams& cp,
+              std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_Button() override;
 
   // CPWL_Wnd
-  ByteString GetClassName() const override;
-  void OnCreate(CreateParams* pParamsToAdjust) override;
   bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
 
  protected:
-  bool m_bMouseDown;
+  bool m_bMouseDown = false;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_BUTTON_H_
diff --git a/fpdfsdk/pwl/cpwl_caret.cpp b/fpdfsdk/pwl/cpwl_caret.cpp
index 77a768a..739fd37 100644
--- a/fpdfsdk/pwl/cpwl_caret.cpp
+++ b/fpdfsdk/pwl/cpwl_caret.cpp
@@ -7,21 +7,19 @@
 #include "fpdfsdk/pwl/cpwl_caret.h"
 
 #include <sstream>
+#include <utility>
 
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "third_party/base/ptr_util.h"
 
-#define PWL_CARET_FLASHINTERVAL 500
+CPWL_Caret::CPWL_Caret(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)) {}
 
-CPWL_Caret::CPWL_Caret() : m_bFlash(false), m_fWidth(0.4f), m_nDelay(0) {}
-
-CPWL_Caret::~CPWL_Caret() {}
-
-ByteString CPWL_Caret::GetClassName() const {
-  return "CPWL_Caret";
-}
+CPWL_Caret::~CPWL_Caret() = default;
 
 void CPWL_Caret::DrawThisAppearance(CFX_RenderDevice* pDevice,
                                     const CFX_Matrix& mtUser2Device) {
@@ -53,12 +51,7 @@
                     FXFILL_ALTERNATE);
 }
 
-void CPWL_Caret::TimerProc() {
-  if (m_nDelay > 0) {
-    --m_nDelay;
-    return;
-  }
-
+void CPWL_Caret::OnTimerFired() {
   m_bFlash = !m_bFlash;
   InvalidateRect(nullptr);
   // Note, |this| may no longer be viable at this point. If more work needs
@@ -80,7 +73,7 @@
     if (!IsVisible())
       return;
 
-    EndTimer();
+    m_pTimer.reset();
     CPWL_Wnd::SetVisible(false);
     // Note, |this| may no longer be viable at this point. If more work needs
     // to be done, check the return value of SetVisible().
@@ -88,10 +81,12 @@
   }
 
   if (!IsVisible()) {
+    static constexpr int32_t kCaretFlashIntervalMs = 500;
+
     m_ptHead = ptHead;
     m_ptFoot = ptFoot;
-    EndTimer();
-    BeginTimer(PWL_CARET_FLASHINTERVAL);
+    m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this,
+                                             kCaretFlashIntervalMs);
 
     if (!CPWL_Wnd::SetVisible(true))
       return;
diff --git a/fpdfsdk/pwl/cpwl_caret.h b/fpdfsdk/pwl/cpwl_caret.h
index d60a964..71788ef 100644
--- a/fpdfsdk/pwl/cpwl_caret.h
+++ b/fpdfsdk/pwl/cpwl_caret.h
@@ -7,35 +7,40 @@
 #ifndef FPDFSDK_PWL_CPWL_CARET_H_
 #define FPDFSDK_PWL_CPWL_CARET_H_
 
+#include <memory>
+
+#include "core/fxcrt/cfx_timer.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-class CPWL_Caret : public CPWL_Wnd {
+class CPWL_Caret final : public CPWL_Wnd, public CFX_Timer::CallbackIface {
  public:
-  CPWL_Caret();
+  CPWL_Caret(const CreateParams& cp,
+             std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_Caret() override;
 
-  // CPWL_Wnd
-  ByteString GetClassName() const override;
+  // CPWL_Wnd:
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
   bool InvalidateRect(CFX_FloatRect* pRect) override;
   bool SetVisible(bool bVisible) override;
-  void TimerProc() override;
+
+  // CFX_Timer::CallbackIface:
+  void OnTimerFired() override;
 
   void SetCaret(bool bVisible,
                 const CFX_PointF& ptHead,
                 const CFX_PointF& ptFoot);
-  void SetInvalidRect(CFX_FloatRect rc) { m_rcInvalid = rc; }
+  void SetInvalidRect(const CFX_FloatRect& rc) { m_rcInvalid = rc; }
 
  private:
   CFX_FloatRect GetCaretRect() const;
 
-  bool m_bFlash;
+  bool m_bFlash = false;
   CFX_PointF m_ptHead;
   CFX_PointF m_ptFoot;
-  float m_fWidth;
-  int32_t m_nDelay;
+  float m_fWidth = 0.4f;
   CFX_FloatRect m_rcInvalid;
+  std::unique_ptr<CFX_Timer> m_pTimer;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_CARET_H_
diff --git a/fpdfsdk/pwl/cpwl_combo_box.cpp b/fpdfsdk/pwl/cpwl_combo_box.cpp
index 3adbebc..8192da2 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.cpp
+++ b/fpdfsdk/pwl/cpwl_combo_box.cpp
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 #include <sstream>
+#include <utility>
 
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
@@ -17,6 +18,7 @@
 #include "fpdfsdk/pwl/cpwl_list_impl.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 #include "public/fpdf_fwlevent.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -26,6 +28,13 @@
 
 }  // namespace
 
+CPWL_CBListBox::CPWL_CBListBox(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_ListBox(cp, std::move(pAttachedData)) {}
+
+CPWL_CBListBox::~CPWL_CBListBox() = default;
+
 bool CPWL_CBListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
   CPWL_Wnd::OnLButtonUp(point, nFlag);
 
@@ -88,18 +97,24 @@
 }
 
 bool CPWL_CBListBox::OnCharNotify(uint16_t nChar, uint32_t nFlag) {
-  if (CPWL_ComboBox* pComboBox = (CPWL_ComboBox*)GetParentWindow())
+  if (auto* pComboBox = static_cast<CPWL_ComboBox*>(GetParentWindow()))
     pComboBox->SetSelectText();
 
   return OnNotifySelectionChanged(true, nFlag);
 }
 
+CPWL_CBButton::CPWL_CBButton(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)) {}
+
+CPWL_CBButton::~CPWL_CBButton() = default;
+
 void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
                                        const CFX_Matrix& mtUser2Device) {
   CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
 
   CFX_FloatRect rectWnd = CPWL_Wnd::GetWindowRect();
-
   if (!IsVisible() || rectWnd.IsEmpty())
     return;
 
@@ -133,7 +148,6 @@
   CPWL_Wnd::OnLButtonDown(point, nFlag);
 
   SetCapture();
-
   if (CPWL_Wnd* pParent = GetParentWindow())
     pParent->NotifyLButtonDown(this, point);
 
@@ -144,22 +158,18 @@
   CPWL_Wnd::OnLButtonUp(point, nFlag);
 
   ReleaseCapture();
-
   return true;
 }
 
-CPWL_ComboBox::CPWL_ComboBox() {}
-
-CPWL_ComboBox::~CPWL_ComboBox() {}
-
-ByteString CPWL_ComboBox::GetClassName() const {
-  return "CPWL_ComboBox";
+CPWL_ComboBox::CPWL_ComboBox(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)) {
+  GetCreationParams()->dwFlags &= ~PWS_HSCROLL;
+  GetCreationParams()->dwFlags &= ~PWS_VSCROLL;
 }
 
-void CPWL_ComboBox::OnCreate(CreateParams* pParamsToAdjust) {
-  pParamsToAdjust->dwFlags &= ~PWS_HSCROLL;
-  pParamsToAdjust->dwFlags &= ~PWS_VSCROLL;
-}
+CPWL_ComboBox::~CPWL_ComboBox() = default;
 
 void CPWL_ComboBox::OnDestroy() {
   // Until cleanup takes place in the virtual destructor for CPWL_Wnd
@@ -196,11 +206,24 @@
     m_pEdit->ReplaceSelection(text);
 }
 
-WideString CPWL_ComboBox::GetText() const {
-  if (m_pEdit) {
-    return m_pEdit->GetText();
-  }
-  return WideString();
+bool CPWL_ComboBox::CanUndo() {
+  return m_pEdit && m_pEdit->CanUndo();
+}
+
+bool CPWL_ComboBox::CanRedo() {
+  return m_pEdit && m_pEdit->CanRedo();
+}
+
+bool CPWL_ComboBox::Undo() {
+  return m_pEdit && m_pEdit->Undo();
+}
+
+bool CPWL_ComboBox::Redo() {
+  return m_pEdit && m_pEdit->Redo();
+}
+
+WideString CPWL_ComboBox::GetText() {
+  return m_pEdit ? m_pEdit->GetText() : WideString();
 }
 
 void CPWL_ComboBox::SetText(const WideString& text) {
@@ -254,11 +277,7 @@
   if (m_pEdit)
     return;
 
-  m_pEdit = new CPWL_Edit();
-  m_pEdit->AttachFFLData(m_pFormFiller.Get());
-
   CreateParams ecp = cp;
-  ecp.pParentWnd = this;
   ecp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PES_CENTER |
                 PES_AUTOSCROLL | PES_UNDO;
 
@@ -271,17 +290,19 @@
   ecp.rcRectWnd = CFX_FloatRect();
   ecp.dwBorderWidth = 0;
   ecp.nBorderStyle = BorderStyle::SOLID;
-  m_pEdit->Create(ecp);
+
+  auto pEdit = pdfium::MakeUnique<CPWL_Edit>(ecp, CloneAttachedData());
+  m_pEdit = pEdit.get();
+  m_pEdit->AttachFFLData(m_pFormFiller.Get());
+  AddChild(std::move(pEdit));
+  m_pEdit->Realize();
 }
 
 void CPWL_ComboBox::CreateButton(const CreateParams& cp) {
   if (m_pButton)
     return;
 
-  m_pButton = new CPWL_CBButton;
-
   CreateParams bcp = cp;
-  bcp.pParentWnd = this;
   bcp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND;
   bcp.sBackgroundColor = CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f,
                                    220.0f / 255.0f, 220.0f / 255.0f);
@@ -289,18 +310,18 @@
   bcp.dwBorderWidth = 2;
   bcp.nBorderStyle = BorderStyle::BEVELED;
   bcp.eCursorType = FXCT_ARROW;
-  m_pButton->Create(bcp);
+
+  auto pButton = pdfium::MakeUnique<CPWL_CBButton>(bcp, CloneAttachedData());
+  m_pButton = pButton.get();
+  AddChild(std::move(pButton));
+  m_pButton->Realize();
 }
 
 void CPWL_ComboBox::CreateListBox(const CreateParams& cp) {
   if (m_pList)
     return;
 
-  m_pList = new CPWL_CBListBox();
-  m_pList->AttachFFLData(m_pFormFiller.Get());
-
   CreateParams lcp = cp;
-  lcp.pParentWnd = this;
   lcp.dwFlags =
       PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL;
   lcp.nBorderStyle = BorderStyle::SOLID;
@@ -317,12 +338,15 @@
   if (cp.sBackgroundColor.nColorType == CFX_Color::kTransparent)
     lcp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR;
 
-  m_pList->Create(lcp);
+  auto pList = pdfium::MakeUnique<CPWL_CBListBox>(lcp, CloneAttachedData());
+  m_pList = pList.get();
+  m_pList->AttachFFLData(m_pFormFiller.Get());
+  AddChild(std::move(pList));
+  m_pList->Realize();
 }
 
 bool CPWL_ComboBox::RePosChildWnd() {
-  ObservedPtr thisObserved(this);
-
+  ObservedPtr<CPWL_ComboBox> thisObserved(this);
   const CFX_FloatRect rcClient = GetClientRect();
   if (m_bPopup) {
     const float fOldWindowHeight = m_rcOldWindow.Height();
@@ -423,14 +447,11 @@
   if (!m_pFillerNotify)
     return true;
 
-  ObservedPtr thisObserved(this);
-
-#ifdef PDF_ENABLE_XFA
+  ObservedPtr<CPWL_ComboBox> thisObserved(this);
   if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), 0))
     return !!thisObserved;
   if (!thisObserved)
     return false;
-#endif  // PDF_ENABLE_XFA
 
   float fBorderWidth = m_pList->GetBorderWidth() * 2;
   float fPopupMin = 0.0f;
@@ -458,12 +479,7 @@
   if (!Move(rcWindow, true, true))
     return false;
 
-#ifdef PDF_ENABLE_XFA
   m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), 0);
-  if (!thisObserved)
-    return false;
-#endif  // PDF_ENABLE_XFA
-
   return !!thisObserved;
 }
 
@@ -478,14 +494,12 @@
   switch (nChar) {
     case FWL_VKEY_Up:
       if (m_pList->GetCurSel() > 0) {
-#ifdef PDF_ENABLE_XFA
         if (m_pFillerNotify) {
           if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
             return false;
           if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
             return false;
         }
-#endif  // PDF_ENABLE_XFA
         if (m_pList->IsMovementKey(nChar)) {
           if (m_pList->OnMovementKeyDown(nChar, nFlag))
             return false;
@@ -495,14 +509,12 @@
       return true;
     case FWL_VKEY_Down:
       if (m_pList->GetCurSel() < m_pList->GetCount() - 1) {
-#ifdef PDF_ENABLE_XFA
         if (m_pFillerNotify) {
           if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
             return false;
           if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
             return false;
         }
-#endif  // PDF_ENABLE_XFA
         if (m_pList->IsMovementKey(nChar)) {
           if (m_pList->OnMovementKeyDown(nChar, nFlag))
             return false;
@@ -529,14 +541,12 @@
   if (HasFlag(PCBS_ALLOWCUSTOMTEXT))
     return m_pEdit->OnChar(nChar, nFlag);
 
-#ifdef PDF_ENABLE_XFA
   if (m_pFillerNotify) {
     if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
       return false;
     if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
       return false;
   }
-#endif  // PDF_ENABLE_XFA
   if (!m_pList->IsChar(nChar, nFlag))
     return false;
   return m_pList->OnCharNotify(nChar, nFlag);
@@ -568,7 +578,7 @@
 
 void CPWL_ComboBox::SetSelectText() {
   m_pEdit->SelectAll();
-  m_pEdit->ReplaceSel(m_pList->GetText());
+  m_pEdit->ReplaceSelection(m_pList->GetText());
   m_pEdit->SelectAll();
   m_nSelectItem = m_pList->GetCurSel();
 }
diff --git a/fpdfsdk/pwl/cpwl_combo_box.h b/fpdfsdk/pwl/cpwl_combo_box.h
index 6db7213..8b9bf12 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.h
+++ b/fpdfsdk/pwl/cpwl_combo_box.h
@@ -14,10 +14,12 @@
 #include "fpdfsdk/pwl/cpwl_list_box.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-class CPWL_CBListBox : public CPWL_ListBox {
+class CPWL_CBListBox final : public CPWL_ListBox {
  public:
-  CPWL_CBListBox() {}
-  ~CPWL_CBListBox() override {}
+  CPWL_CBListBox(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+  ~CPWL_CBListBox() override;
 
   // CPWL_ListBox
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
@@ -28,10 +30,12 @@
   bool OnCharNotify(uint16_t nChar, uint32_t nFlag);
 };
 
-class CPWL_CBButton : public CPWL_Wnd {
+class CPWL_CBButton final : public CPWL_Wnd {
  public:
-  CPWL_CBButton() {}
-  ~CPWL_CBButton() override {}
+  CPWL_CBButton(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+  ~CPWL_CBButton() override;
 
   // CPWL_Wnd
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
@@ -40,16 +44,16 @@
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
 };
 
-class CPWL_ComboBox : public CPWL_Wnd {
+class CPWL_ComboBox final : public CPWL_Wnd {
  public:
-  CPWL_ComboBox();
+  CPWL_ComboBox(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_ComboBox() override;
 
   CPWL_Edit* GetEdit() const { return m_pEdit.Get(); }
 
   // CPWL_Wnd:
-  ByteString GetClassName() const override;
-  void OnCreate(CreateParams* pParamsToAdjust) override;
   void OnDestroy() override;
   bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override;
   bool OnChar(uint16_t nChar, uint32_t nFlag) override;
@@ -60,12 +64,16 @@
   CFX_FloatRect GetFocusRect() const override;
   void SetFocus() override;
   void KillFocus() override;
+  WideString GetText() override;
   WideString GetSelectedText() override;
   void ReplaceSelection(const WideString& text) override;
+  bool CanUndo() override;
+  bool CanRedo() override;
+  bool Undo() override;
+  bool Redo() override;
 
   void SetFillerNotify(IPWL_Filler_Notify* pNotify);
 
-  WideString GetText() const;
   void SetText(const WideString& text);
   void AddString(const WideString& str);
   int32_t GetSelect() const;
@@ -76,9 +84,7 @@
   void ClearSelection();
   void SelectAll();
   bool IsPopup() const;
-
   void SetSelectText();
-
   void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; }
 
  private:
diff --git a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp
index 9fd8eec..7d3dd4d 100644
--- a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp
+++ b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp
@@ -2,17 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fpdfsdk/cba_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
 #include "fpdfsdk/pwl/cpwl_combo_box.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "public/fpdf_fwlevent.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CPWLComboBoxEditEmbeddertest : public EmbedderTest {
+class CPWLComboBoxEditEmbedderTest : public EmbedderTest {
  protected:
   void SetUp() override {
     EmbedderTest::SetUp();
@@ -29,9 +31,10 @@
     m_page = LoadPage(0);
     ASSERT_TRUE(m_page);
 
-    m_pFormFillEnv = static_cast<CPDFSDK_FormFillEnvironment*>(form_handle());
-    CBA_AnnotIterator iter(m_pFormFillEnv->GetPageView(0),
-                           CPDF_Annot::Subtype::WIDGET);
+    m_pFormFillEnv =
+        CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
+    CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageView(0),
+                               CPDF_Annot::Subtype::WIDGET);
 
     // User editable combobox.
     m_pAnnotEditable = iter.GetFirstAnnot();
@@ -53,18 +56,17 @@
     CFFL_InteractiveFormFiller* pInteractiveFormFiller =
         m_pFormFillEnv->GetInteractiveFormFiller();
     {
-      CPDFSDK_Annot::ObservedPtr pObserved(pAnnotCombobox);
+      ObservedPtr<CPDFSDK_Annot> pObserved(pAnnotCombobox);
       EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0));
     }
 
     m_pFormFiller =
-        pInteractiveFormFiller->GetFormFiller(pAnnotCombobox, false);
+        pInteractiveFormFiller->GetFormFillerForTesting(pAnnotCombobox);
     ASSERT_TRUE(m_pFormFiller);
 
     CPWL_Wnd* pWindow =
-        m_pFormFiller->GetPDFWindow(m_pFormFillEnv->GetPageView(0), false);
+        m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageView(0), false);
     ASSERT_TRUE(pWindow);
-    ASSERT_EQ("CPWL_ComboBox", pWindow->GetClassName());
     m_pComboBox = static_cast<CPWL_ComboBox*>(pWindow);
   }
 
@@ -96,7 +98,7 @@
   CPDFSDK_FormFillEnvironment* m_pFormFillEnv;
 };
 
-TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextEmptyAndBasicNormal) {
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicNormal) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
 
   // Automatically pre-filled with "Banana".
@@ -118,7 +120,7 @@
   EXPECT_FALSE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotNormal(), 'a', 0));
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextFragmentsNormal) {
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsNormal) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal());
   EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str());
 
@@ -144,7 +146,7 @@
   EXPECT_STREQ(L"a", GetCPWLComboBox()->GetSelectedText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextEmptyAndBasicEditable) {
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicEditable) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty());
 
@@ -173,7 +175,7 @@
   EXPECT_STREQ(L"Baabc", GetCPWLComboBox()->GetSelectedText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextFragmentsEditable) {
+TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsEditable) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(50);
 
@@ -203,7 +205,7 @@
   EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, DeleteEntireTextSelection) {
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEntireTextSelection) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(50);
 
@@ -215,7 +217,7 @@
   EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, DeleteTextSelectionMiddle) {
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(50);
 
@@ -227,7 +229,7 @@
                GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, DeleteTextSelectionLeft) {
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(50);
 
@@ -239,7 +241,7 @@
                GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, DeleteTextSelectionRight) {
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(50);
 
@@ -251,7 +253,7 @@
                GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, DeleteEmptyTextSelection) {
+TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEmptyTextSelection) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(50);
 
@@ -260,41 +262,39 @@
                GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest, InsertTextInEmptyEditableComboBox) {
+TEST_F(CPWLComboBoxEditEmbedderTest, InsertTextInEmptyEditableComboBox) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   GetCPWLComboBox()->ReplaceSelection(L"Hello");
   EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest,
+TEST_F(CPWLComboBoxEditEmbedderTest,
        InsertTextInPopulatedEditableComboBoxLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(10);
 
   // Move cursor to beginning of user-editable combobox text field.
-  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(GetCPDFSDKAnnotUserEditable(),
-                                             FWL_VKEY_Home, 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, 0));
 
   GetCPWLComboBox()->ReplaceSelection(L"Hello");
   EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest,
+TEST_F(CPWLComboBoxEditEmbedderTest,
        InsertTextInPopulatedEditableComboBoxMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(10);
 
   // Move cursor to middle of user-editable combobox text field.
   for (int i = 0; i < 5; ++i) {
-    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(GetCPDFSDKAnnotUserEditable(),
-                                               FWL_VKEY_Left, 0));
+    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, 0));
   }
 
   GetCPWLComboBox()->ReplaceSelection(L"Hello");
   EXPECT_STREQ(L"ABCDEHelloFGHIJ", GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest,
+TEST_F(CPWLComboBoxEditEmbedderTest,
        InsertTextInPopulatedEditableComboBoxRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(10);
@@ -303,7 +303,7 @@
   EXPECT_STREQ(L"ABCDEFGHIJHello", GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest,
+TEST_F(CPWLComboBoxEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedEditableComboBoxWhole) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(10);
@@ -314,7 +314,7 @@
   EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest,
+TEST_F(CPWLComboBoxEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedEditableComboBoxLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(10);
@@ -325,7 +325,7 @@
   EXPECT_STREQ(L"HelloFGHIJ", GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest,
+TEST_F(CPWLComboBoxEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedEditableComboBoxMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(10);
@@ -336,7 +336,7 @@
   EXPECT_STREQ(L"ABHelloHIJ", GetCPWLComboBox()->GetText().c_str());
 }
 
-TEST_F(CPWLComboBoxEditEmbeddertest,
+TEST_F(CPWLComboBoxEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedEditableComboBoxRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable());
   TypeTextIntoTextField(10);
diff --git a/fpdfsdk/pwl/cpwl_edit.cpp b/fpdfsdk/pwl/cpwl_edit.cpp
index 6c867ca..77491da 100644
--- a/fpdfsdk/pwl/cpwl_edit.cpp
+++ b/fpdfsdk/pwl/cpwl_edit.cpp
@@ -9,13 +9,13 @@
 #include <algorithm>
 #include <memory>
 #include <sstream>
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfdoc/cpvt_word.h"
+#include "core/fpdfdoc/ipvt_fontmap.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/xml/cxml_content.h"
-#include "core/fxcrt/xml/cxml_element.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
@@ -23,60 +23,21 @@
 #include "fpdfsdk/pwl/cpwl_caret.h"
 #include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
-#include "fpdfsdk/pwl/cpwl_font_map.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 #include "public/fpdf_fwlevent.h"
-#include "third_party/base/stl_util.h"
 
-CPWL_Edit::CPWL_Edit() : m_bFocus(false) {}
+CPWL_Edit::CPWL_Edit(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_EditCtrl(cp, std::move(pAttachedData)) {}
 
 CPWL_Edit::~CPWL_Edit() {
   ASSERT(!m_bFocus);
 }
 
-ByteString CPWL_Edit::GetClassName() const {
-  return PWL_CLASSNAME_EDIT;
-}
-
 void CPWL_Edit::SetText(const WideString& csText) {
-  WideString swText = csText;
-  if (!HasFlag(PES_RICH)) {
-    m_pEdit->SetText(swText);
-    return;
-  }
-
-  ByteString sValue = ByteString::FromUnicode(swText);
-  std::unique_ptr<CXML_Element> pXML(
-      CXML_Element::Parse(sValue.c_str(), sValue.GetLength()));
-  if (!pXML) {
-    m_pEdit->SetText(swText);
-    return;
-  }
-  swText.clear();
-
-  bool bFirst = true;
-  size_t nCount = pXML->CountChildren();
-  for (size_t i = 0; i < nCount; ++i) {
-    CXML_Element* pSubElement = ToElement(pXML->GetChild(i));
-    if (!pSubElement || !pSubElement->GetTagName().EqualNoCase("p"))
-      continue;
-
-    WideString swSection;
-    size_t nSubChild = pSubElement->CountChildren();
-    for (size_t j = 0; j < nSubChild; ++j) {
-      CXML_Content* pSubContent = ToContent(pSubElement->GetChild(j));
-      if (pSubContent)
-        swSection += pSubContent->m_Content;
-    }
-    if (bFirst)
-      bFirst = false;
-    else
-      swText += FWL_VKEY_Return;
-    swText += swSection;
-  }
-
-  m_pEdit->SetText(swText);
+  m_pEdit->SetText(csText);
 }
 
 bool CPWL_Edit::RePosChildWnd() {
@@ -86,8 +47,7 @@
         CFX_FloatRect(rcWindow.right, rcWindow.bottom,
                       rcWindow.right + PWL_SCROLLBAR_WIDTH, rcWindow.top);
 
-    ObservedPtr thisObserved(this);
-
+    ObservedPtr<CPWL_Edit> thisObserved(this);
     pVSB->Move(rcVScroll, true, false);
     if (!thisObserved)
       return false;
@@ -118,8 +78,8 @@
   return rcClient;
 }
 
-void CPWL_Edit::SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat, bool bPaint) {
-  m_pEdit->SetAlignmentV((int32_t)nFormat, bPaint);
+void CPWL_Edit::SetAlignFormatVerticalCenter() {
+  m_pEdit->SetAlignmentV(static_cast<int32_t>(PEAV_CENTER), true);
 }
 
 bool CPWL_Edit::CanSelectAll() const {
@@ -213,7 +173,7 @@
     switch (GetBorderStyle()) {
       case BorderStyle::SOLID: {
         CFX_GraphStateData gsd;
-        gsd.m_LineWidth = (float)GetBorderWidth();
+        gsd.m_LineWidth = GetBorderWidth();
 
         CFX_PathData path;
 
@@ -239,12 +199,10 @@
       }
       case BorderStyle::DASH: {
         CFX_GraphStateData gsd;
-        gsd.m_LineWidth = (float)GetBorderWidth();
-
-        gsd.SetDashCount(2);
-        gsd.m_DashArray[0] = (float)GetBorderDash().nDash;
-        gsd.m_DashArray[1] = (float)GetBorderDash().nGap;
-        gsd.m_DashPhase = (float)GetBorderDash().nPhase;
+        gsd.m_LineWidth = static_cast<float>(GetBorderWidth());
+        gsd.m_DashArray = {static_cast<float>(GetBorderDash().nDash),
+                           static_cast<float>(GetBorderDash().nGap)};
+        gsd.m_DashPhase = static_cast<float>(GetBorderDash().nPhase);
 
         CFX_PathData path;
         for (int32_t i = 0; i < nCharArray - 1; i++) {
@@ -280,10 +238,9 @@
     pRange = &wrRange;
   }
 
-  CFX_SystemHandler* pSysHandler = GetSystemHandler();
   CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pEdit.get(),
                           GetTextColor().ToFXColor(GetTransparency()), rcClip,
-                          CFX_PointF(), pRange, pSysHandler,
+                          CFX_PointF(), pRange, GetSystemHandler(),
                           m_pFormFiller.Get());
 }
 
@@ -322,17 +279,13 @@
   if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point))
     return true;
 
-  CFX_SystemHandler* pSH = GetSystemHandler();
-  if (!pSH)
-    return false;
-
   SetFocus();
 
   return false;
 }
 
 void CPWL_Edit::OnSetFocus() {
-  ObservedPtr observed_ptr(this);
+  ObservedPtr<CPWL_Edit> observed_ptr(this);
   SetEditCaret(true);
   if (!observed_ptr)
     return;
@@ -348,8 +301,7 @@
 }
 
 void CPWL_Edit::OnKillFocus() {
-  ObservedPtr observed_ptr(this);
-
+  ObservedPtr<CPWL_Edit> observed_ptr(this);
   CPWL_ScrollBar* pScroll = GetVScrollBar();
   if (pScroll && pScroll->IsVisible()) {
     pScroll->SetVisible(false);
@@ -407,14 +359,13 @@
   return m_pEdit->IsTextFull();
 }
 
-float CPWL_Edit::GetCharArrayAutoFontSize(CPDF_Font* pFont,
+float CPWL_Edit::GetCharArrayAutoFontSize(const CPDF_Font* pFont,
                                           const CFX_FloatRect& rcPlate,
                                           int32_t nCharArray) {
   if (!pFont || pFont->IsStandardFont())
     return 0.0f;
 
-  FX_RECT rcBBox;
-  pFont->GetFontBBox(rcBBox);
+  const FX_RECT& rcBBox = pFont->GetFontBBox();
 
   CFX_FloatRect rcCell = rcPlate;
   float xdiv = rcCell.Width() / nCharArray * 1000.0f / rcBBox.Width();
@@ -437,7 +388,7 @@
   if (!pFontMap)
     return;
 
-  float fFontSize = GetCharArrayAutoFontSize(pFontMap->GetPDFFont(0),
+  float fFontSize = GetCharArrayAutoFontSize(pFontMap->GetPDFFont(0).Get(),
                                              GetClientRect(), nCharArray);
   if (fFontSize <= 0.0f)
     return;
@@ -450,11 +401,6 @@
   m_pEdit->SetLimitChar(nLimitChar);
 }
 
-void CPWL_Edit::ReplaceSel(const WideString& wsText) {
-  m_pEdit->ClearSelection();
-  m_pEdit->InsertText(wsText, FX_CHARSET_Default);
-}
-
 CFX_FloatRect CPWL_Edit::GetFocusRect() const {
   return CFX_FloatRect();
 }
@@ -480,7 +426,7 @@
       if (nSelStart == nSelEnd)
         nSelEnd = nSelStart + 1;
 
-      CPWL_Wnd::ObservedPtr thisObserved(this);
+      ObservedPtr<CPWL_Wnd> thisObserved(this);
 
       bool bRC;
       bool bExit;
@@ -563,7 +509,7 @@
           break;
       }
 
-      CPWL_Wnd::ObservedPtr thisObserved(this);
+      ObservedPtr<CPWL_Wnd> thisObserved(this);
 
       WideString strChangeEx;
       std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke(
@@ -676,8 +622,6 @@
 CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place,
                                             bool bLatin,
                                             bool bArabic) const {
-  CPVT_WordRange range;
-
   CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
   CPVT_Word wordinfo;
   CPVT_WordPlace wpStart(place), wpEnd(place);
@@ -721,6 +665,5 @@
     } while (pIterator->PrevWord());
   }
 
-  range.Set(wpStart, wpEnd);
-  return range;
+  return CPVT_WordRange(wpStart, wpEnd);
 }
diff --git a/fpdfsdk/pwl/cpwl_edit.h b/fpdfsdk/pwl/cpwl_edit.h
index 3f9cd43..1de382e 100644
--- a/fpdfsdk/pwl/cpwl_edit.h
+++ b/fpdfsdk/pwl/cpwl_edit.h
@@ -7,47 +7,53 @@
 #ifndef FPDFSDK_PWL_CPWL_EDIT_H_
 #define FPDFSDK_PWL_CPWL_EDIT_H_
 
+#include <memory>
 #include <utility>
 
 #include "core/fpdfdoc/cpvt_wordrange.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
+#include "fpdfsdk/pwl/ipwl_systemhandler.h"
 
-#define PWL_CLASSNAME_EDIT "CPWL_Edit"
+class CPDF_Font;
 
 class IPWL_Filler_Notify {
  public:
-  virtual ~IPWL_Filler_Notify() {}
+  virtual ~IPWL_Filler_Notify() = default;
 
   // Must write to |bBottom| and |fPopupRet|.
-  virtual void QueryWherePopup(CPWL_Wnd::PrivateData* pAttached,
-                               float fPopupMin,
-                               float fPopupMax,
-                               bool* bBottom,
-                               float* fPopupRet) = 0;
+  virtual void QueryWherePopup(
+      const IPWL_SystemHandler::PerWindowData* pAttached,
+      float fPopupMin,
+      float fPopupMax,
+      bool* bBottom,
+      float* fPopupRet) = 0;
+
   virtual std::pair<bool, bool> OnBeforeKeyStroke(
-      CPWL_Wnd::PrivateData* pAttached,
+      const IPWL_SystemHandler::PerWindowData* pAttached,
       WideString& strChange,
       const WideString& strChangeEx,
       int nSelStart,
       int nSelEnd,
       bool bKeyDown,
       uint32_t nFlag) = 0;
-#ifdef PDF_ENABLE_XFA
-  virtual bool OnPopupPreOpen(CPWL_Wnd::PrivateData* pAttached,
-                              uint32_t nFlag) = 0;
-  virtual bool OnPopupPostOpen(CPWL_Wnd::PrivateData* pAttached,
-                               uint32_t nFlag) = 0;
-#endif  // PDF_ENABLE_XFA
+
+  virtual bool OnPopupPreOpen(
+      const IPWL_SystemHandler::PerWindowData* pAttached,
+      uint32_t nFlag) = 0;
+
+  virtual bool OnPopupPostOpen(
+      const IPWL_SystemHandler::PerWindowData* pAttached,
+      uint32_t nFlag) = 0;
 };
 
-class CPWL_Edit : public CPWL_EditCtrl {
+class CPWL_Edit final : public CPWL_EditCtrl {
  public:
-  CPWL_Edit();
+  CPWL_Edit(const CreateParams& cp,
+            std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_Edit() override;
 
   // CPWL_EditCtrl
-  ByteString GetClassName() const override;
   void OnCreated() override;
   bool RePosChildWnd() override;
   CFX_FloatRect GetClientRect() const override;
@@ -65,12 +71,9 @@
   void OnSetFocus() override;
   void OnKillFocus() override;
 
-  void SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat = PEAV_TOP,
-                       bool bPaint = true);  // 0:top 1:bottom 2:center
-
+  void SetAlignFormatVerticalCenter();
   void SetCharArray(int32_t nCharArray);
   void SetLimitChar(int32_t nLimitChar);
-
   void SetCharSpace(float fCharSpace);
 
   bool CanSelectAll() const;
@@ -80,11 +83,10 @@
   void CutText();
 
   void SetText(const WideString& csText);
-  void ReplaceSel(const WideString& csText);
 
   bool IsTextFull() const;
 
-  static float GetCharArrayAutoFontSize(CPDF_Font* pFont,
+  static float GetCharArrayAutoFontSize(const CPDF_Font* pFont,
                                         const CFX_FloatRect& rcPlate,
                                         int32_t nCharArray);
 
@@ -114,7 +116,6 @@
   bool IsVScrollBarVisible() const;
   void SetParamByFlag();
 
-  float GetCharArrayAutoFontSize(int32_t nCharArray);
   CFX_PointF GetWordRightBottomPoint(const CPVT_WordPlace& wpWord);
 
   CPVT_WordRange CombineWordRange(const CPVT_WordRange& wr1,
@@ -125,7 +126,7 @@
                                    bool bLatin,
                                    bool bArabic) const;
 
-  bool m_bFocus;
+  bool m_bFocus = false;
   CFX_FloatRect m_rcOldWindow;
   UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify;
   UnownedPtr<CFFL_FormFiller> m_pFormFiller;
diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.cpp b/fpdfsdk/pwl/cpwl_edit_ctrl.cpp
index 06b5ded..54fe91a 100644
--- a/fpdfsdk/pwl/cpwl_edit_ctrl.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_ctrl.cpp
@@ -6,64 +6,50 @@
 
 #include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
 
+#include <utility>
+
 #include "core/fpdfdoc/cpvt_word.h"
 #include "core/fxge/fx_font.h"
 #include "fpdfsdk/pwl/cpwl_caret.h"
 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
-#include "fpdfsdk/pwl/cpwl_font_map.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 #include "public/fpdf_fwlevent.h"
+#include "third_party/base/ptr_util.h"
 
-CPWL_EditCtrl::CPWL_EditCtrl()
-    : m_pEdit(new CPWL_EditImpl),
-      m_pEditCaret(nullptr),
-      m_bMouseDown(false),
-      m_nCharSet(FX_CHARSET_Default) {}
-
-CPWL_EditCtrl::~CPWL_EditCtrl() {}
-
-void CPWL_EditCtrl::OnCreate(CreateParams* pParamsToAdjust) {
-  pParamsToAdjust->eCursorType = FXCT_VBEAM;
+CPWL_EditCtrl::CPWL_EditCtrl(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)),
+      m_pEdit(pdfium::MakeUnique<CPWL_EditImpl>()) {
+  GetCreationParams()->eCursorType = FXCT_VBEAM;
 }
 
-void CPWL_EditCtrl::OnCreated() {
-  SetFontSize(GetCreationParams().fFontSize);
+CPWL_EditCtrl::~CPWL_EditCtrl() = default;
 
+void CPWL_EditCtrl::OnCreated() {
+  SetFontSize(GetCreationParams()->fFontSize);
   m_pEdit->SetFontMap(GetFontMap());
   m_pEdit->SetNotify(this);
   m_pEdit->Initialize();
 }
 
-bool CPWL_EditCtrl::IsWndHorV() {
+bool CPWL_EditCtrl::IsWndHorV() const {
   CFX_Matrix mt = GetWindowMatrix();
   return mt.Transform(CFX_PointF(1, 1)).y == mt.Transform(CFX_PointF(0, 1)).y;
 }
 
 void CPWL_EditCtrl::SetCursor() {
-  if (IsValid()) {
-    if (CFX_SystemHandler* pSH = GetSystemHandler()) {
-      if (IsWndHorV())
-        pSH->SetCursor(FXCT_VBEAM);
-      else
-        pSH->SetCursor(FXCT_HBEAM);
-    }
-  }
+  if (IsValid())
+    GetSystemHandler()->SetCursor(IsWndHorV() ? FXCT_VBEAM : FXCT_HBEAM);
 }
 
 WideString CPWL_EditCtrl::GetSelectedText() {
-  if (m_pEdit)
-    return m_pEdit->GetSelectedText();
-
-  return WideString();
+  return m_pEdit->GetSelectedText();
 }
 
 void CPWL_EditCtrl::ReplaceSelection(const WideString& text) {
-  if (!m_pEdit)
-    return;
-
-  m_pEdit->ClearSelection();
-  m_pEdit->InsertText(text, FX_CHARSET_Default);
+  m_pEdit->ReplaceSelection(text);
 }
 
 bool CPWL_EditCtrl::RePosChildWnd() {
@@ -94,17 +80,17 @@
   if (m_pEditCaret)
     return;
 
-  m_pEditCaret = new CPWL_Caret;
-  m_pEditCaret->SetInvalidRect(GetClientRect());
-
   CreateParams ecp = cp;
-  ecp.pParentWnd = this;
   ecp.dwFlags = PWS_CHILD | PWS_NOREFRESHCLIP;
   ecp.dwBorderWidth = 0;
   ecp.nBorderStyle = BorderStyle::SOLID;
   ecp.rcRectWnd = CFX_FloatRect();
 
-  m_pEditCaret->Create(ecp);
+  auto pCaret = pdfium::MakeUnique<CPWL_Caret>(ecp, CloneAttachedData());
+  m_pEditCaret = pCaret.get();
+  m_pEditCaret->SetInvalidRect(GetClientRect());
+  AddChild(std::move(pCaret));
+  m_pEditCaret->Realize();
 }
 
 void CPWL_EditCtrl::SetFontSize(float fFontSize) {
@@ -338,7 +324,7 @@
   if (!IsFocused() || m_pEdit->IsSelected())
     bVisible = false;
 
-  ObservedPtr thisObserved(this);
+  ObservedPtr<CPWL_EditCtrl> thisObserved(this);
   m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot);
   if (!thisObserved)
     return false;
@@ -346,7 +332,7 @@
   return true;
 }
 
-WideString CPWL_EditCtrl::GetText() const {
+WideString CPWL_EditCtrl::GetText() {
   return m_pEdit->GetText();
 }
 
@@ -401,22 +387,20 @@
     m_pEdit->Backspace();
 }
 
-bool CPWL_EditCtrl::CanUndo() const {
+bool CPWL_EditCtrl::CanUndo() {
   return !IsReadOnly() && m_pEdit->CanUndo();
 }
 
-bool CPWL_EditCtrl::CanRedo() const {
+bool CPWL_EditCtrl::CanRedo() {
   return !IsReadOnly() && m_pEdit->CanRedo();
 }
 
-void CPWL_EditCtrl::Redo() {
-  if (CanRedo())
-    m_pEdit->Redo();
+bool CPWL_EditCtrl::Undo() {
+  return CanUndo() && m_pEdit->Undo();
 }
 
-void CPWL_EditCtrl::Undo() {
-  if (CanUndo())
-    m_pEdit->Undo();
+bool CPWL_EditCtrl::Redo() {
+  return CanRedo() && m_pEdit->Redo();
 }
 
 int32_t CPWL_EditCtrl::GetCharSet() const {
diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.h b/fpdfsdk/pwl/cpwl_edit_ctrl.h
index 31140e3..9cd92bf 100644
--- a/fpdfsdk/pwl/cpwl_edit_ctrl.h
+++ b/fpdfsdk/pwl/cpwl_edit_ctrl.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_string.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
@@ -22,10 +23,11 @@
 
 class CPWL_EditCtrl : public CPWL_Wnd {
  public:
-  CPWL_EditCtrl();
+  CPWL_EditCtrl(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_EditCtrl() override;
 
-  WideString GetText() const;
   void SetSelection(int32_t nStartChar, int32_t nEndChar);
   void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const;
   void ClearSelection();
@@ -37,15 +39,14 @@
   void SetCharSet(uint8_t nCharSet) { m_nCharSet = nCharSet; }
   int32_t GetCharSet() const;
 
-  bool CanUndo() const;
-  bool CanRedo() const;
-  void Redo();
-  void Undo();
+  bool CanUndo() override;
+  bool CanRedo() override;
+  bool Undo() override;
+  bool Redo() override;
 
   void SetReadyToInput();
 
   // CPWL_Wnd:
-  void OnCreate(CreateParams* pParamsToAdjust) override;
   void OnCreated() override;
   bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override;
   bool OnChar(uint16_t nChar, uint32_t nFlag) override;
@@ -60,6 +61,7 @@
   void SetFontSize(float fFontSize) override;
   float GetFontSize() const override;
   void SetCursor() override;
+  WideString GetText() override;
   WideString GetSelectedText() override;
   void ReplaceSelection(const WideString& text) override;
 
@@ -73,24 +75,20 @@
   void CutText();
   void InsertWord(uint16_t word, int32_t nCharset);
   void InsertReturn();
-
-  bool IsWndHorV();
-
+  bool IsWndHorV() const;
   void Delete();
   void Backspace();
-
   void GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const;
-
   void SetEditCaret(bool bVisible);
 
-  std::unique_ptr<CPWL_EditImpl> m_pEdit;
-  CPWL_Caret* m_pEditCaret;
-  bool m_bMouseDown;
+  std::unique_ptr<CPWL_EditImpl> const m_pEdit;
+  CPWL_Caret* m_pEditCaret = nullptr;
+  bool m_bMouseDown = false;
 
  private:
   void CreateEditCaret(const CreateParams& cp);
 
-  int32_t m_nCharSet;
+  int32_t m_nCharSet = FX_CHARSET_Default;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_EDIT_CTRL_H_
diff --git a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
index b3f0d5d..8b68afa 100644
--- a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp
@@ -2,16 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fpdfsdk/cba_annotiterator.h"
+#include "fpdfsdk/pwl/cpwl_edit.h"
+
 #include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_annotiterator.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/formfiller/cffl_formfiller.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "public/fpdf_fwlevent.h"
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CPWLEditEmbeddertest : public EmbedderTest {
+class CPWLEditEmbedderTest : public EmbedderTest {
  protected:
   void SetUp() override {
     EmbedderTest::SetUp();
@@ -28,9 +31,10 @@
     m_page = LoadPage(0);
     ASSERT_TRUE(m_page);
 
-    m_pFormFillEnv = static_cast<CPDFSDK_FormFillEnvironment*>(form_handle());
-    CBA_AnnotIterator iter(m_pFormFillEnv->GetPageView(0),
-                           CPDF_Annot::Subtype::WIDGET);
+    m_pFormFillEnv =
+        CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle());
+    CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageView(0),
+                               CPDF_Annot::Subtype::WIDGET);
     // Normal text field.
     m_pAnnot = iter.GetFirstAnnot();
     ASSERT_TRUE(m_pAnnot);
@@ -44,27 +48,31 @@
     ASSERT_TRUE(m_pAnnotCharLimit);
     ASSERT_EQ(CPDF_Annot::Subtype::WIDGET,
               m_pAnnotCharLimit->GetAnnotSubtype());
+
+    // Password text field.
+    CPDFSDK_Annot* password_annot = iter.GetNextAnnot(m_pAnnotCharLimit);
+    ASSERT_TRUE(password_annot);
+    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, password_annot->GetAnnotSubtype());
+
     CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot();
-    ASSERT_EQ(m_pAnnotCharLimit, pLastAnnot);
+    ASSERT_EQ(password_annot, pLastAnnot);
   }
 
   void FormFillerAndWindowSetup(CPDFSDK_Annot* pAnnotTextField) {
     CFFL_InteractiveFormFiller* pInteractiveFormFiller =
         m_pFormFillEnv->GetInteractiveFormFiller();
     {
-      CPDFSDK_Annot::ObservedPtr pObserved(pAnnotTextField);
+      ObservedPtr<CPDFSDK_Annot> pObserved(pAnnotTextField);
       EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0));
     }
 
     m_pFormFiller =
-        pInteractiveFormFiller->GetFormFiller(pAnnotTextField, false);
+        pInteractiveFormFiller->GetFormFillerForTesting(pAnnotTextField);
     ASSERT_TRUE(m_pFormFiller);
 
     CPWL_Wnd* pWindow =
-        m_pFormFiller->GetPDFWindow(m_pFormFillEnv->GetPageView(0), false);
+        m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageView(0), false);
     ASSERT_TRUE(pWindow);
-    ASSERT_EQ(PWL_CLASSNAME_EDIT, pWindow->GetClassName());
-
     m_pEdit = static_cast<CPWL_Edit*>(pWindow);
   }
 
@@ -90,7 +98,7 @@
   CPDFSDK_FormFillEnvironment* m_pFormFillEnv;
 };
 
-TEST_F(CPWLEditEmbeddertest, TypeText) {
+TEST_F(CPWLEditEmbedderTest, TypeText) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty());
   EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0));
@@ -100,7 +108,7 @@
   EXPECT_STREQ(L"abc", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, GetSelectedTextEmptyAndBasic) {
+TEST_F(CPWLEditEmbedderTest, GetSelectedTextEmptyAndBasic) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   // Attempt to set selection before text has been typed to test that
   // selection is identified as empty.
@@ -117,7 +125,7 @@
   EXPECT_STREQ(L"ab", GetCPWLEdit()->GetSelectedText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, GetSelectedTextFragments) {
+TEST_F(CPWLEditEmbedderTest, GetSelectedTextFragments) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(50);
 
@@ -147,7 +155,7 @@
   EXPECT_STREQ(L"r", GetCPWLEdit()->GetSelectedText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, DeleteEntireTextSelection) {
+TEST_F(CPWLEditEmbedderTest, DeleteEntireTextSelection) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(50);
 
@@ -159,7 +167,7 @@
   EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty());
 }
 
-TEST_F(CPWLEditEmbeddertest, DeleteTextSelectionMiddle) {
+TEST_F(CPWLEditEmbedderTest, DeleteTextSelectionMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(50);
 
@@ -171,7 +179,7 @@
                GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, DeleteTextSelectionLeft) {
+TEST_F(CPWLEditEmbedderTest, DeleteTextSelectionLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(50);
 
@@ -183,7 +191,7 @@
                GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, DeleteTextSelectionRight) {
+TEST_F(CPWLEditEmbedderTest, DeleteTextSelectionRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(50);
 
@@ -195,7 +203,7 @@
                GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, DeleteEmptyTextSelection) {
+TEST_F(CPWLEditEmbedderTest, DeleteEmptyTextSelection) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(50);
 
@@ -204,39 +212,37 @@
                GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInEmptyTextField) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInEmptyTextField) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->ReplaceSelection(L"Hello");
   EXPECT_STREQ(L"Hello", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInPopulatedTextFieldLeft) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedTextFieldLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(10);
 
   // Move cursor to beginning of text field.
-  EXPECT_TRUE(
-      GetCFFLFormFiller()->OnKeyDown(GetCPDFSDKAnnot(), FWL_VKEY_Home, 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, 0));
 
   GetCPWLEdit()->ReplaceSelection(L"Hello");
   EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInPopulatedTextFieldMiddle) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedTextFieldMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(10);
 
   // Move cursor to middle of text field.
   for (int i = 0; i < 5; ++i) {
-    EXPECT_TRUE(
-        GetCFFLFormFiller()->OnKeyDown(GetCPDFSDKAnnot(), FWL_VKEY_Left, 0));
+    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, 0));
   }
 
   GetCPWLEdit()->ReplaceSelection(L"Hello");
   EXPECT_STREQ(L"ABCDEHelloFGHIJ", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInPopulatedTextFieldRight) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedTextFieldRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(10);
 
@@ -244,7 +250,7 @@
   EXPECT_STREQ(L"ABCDEFGHIJHello", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedTextFieldWhole) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(10);
@@ -255,7 +261,7 @@
   EXPECT_STREQ(L"Hello", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedTextFieldLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(10);
@@ -266,7 +272,7 @@
   EXPECT_STREQ(L"HelloFGHIJ", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedTextFieldMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(10);
@@ -277,7 +283,7 @@
   EXPECT_STREQ(L"ABHelloHIJ", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedTextFieldRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   TypeTextIntoTextField(10);
@@ -288,7 +294,7 @@
   EXPECT_STREQ(L"ABCDEHello", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInEmptyCharLimitTextFieldOverflow) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInEmptyCharLimitTextFieldOverflow) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   GetCPWLEdit()->SetSelection(0, -1);
   EXPECT_STREQ(L"Elephant", GetCPWLEdit()->GetSelectedText().c_str());
@@ -298,7 +304,7 @@
   EXPECT_STREQ(L"Hippopotam", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInEmptyCharLimitTextFieldFit) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInEmptyCharLimitTextFieldFit) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   GetCPWLEdit()->SetSelection(0, -1);
   EXPECT_STREQ(L"Elephant", GetCPWLEdit()->GetSelectedText().c_str());
@@ -308,35 +314,33 @@
   EXPECT_STREQ(L"Zebra", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInPopulatedCharLimitTextFieldLeft) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedCharLimitTextFieldLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   GetCPWLEdit()->ReplaceSelection(L"Hippopotamus");
   EXPECT_STREQ(L"HiElephant", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInPopulatedCharLimitTextFieldMiddle) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedCharLimitTextFieldMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   // Move cursor to middle of text field.
   for (int i = 0; i < 5; ++i) {
-    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(GetCPDFSDKAnnotCharLimit(),
-                                               FWL_VKEY_Right, 0));
+    EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Right, 0));
   }
 
   GetCPWLEdit()->ReplaceSelection(L"Hippopotamus");
   EXPECT_STREQ(L"ElephHiant", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, InsertTextInPopulatedCharLimitTextFieldRight) {
+TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedCharLimitTextFieldRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   // Move cursor to end of text field.
-  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(GetCPDFSDKAnnotCharLimit(),
-                                             FWL_VKEY_End, 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_End, 0));
 
   GetCPWLEdit()->ReplaceSelection(L"Hippopotamus");
   EXPECT_STREQ(L"ElephantHi", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldWhole) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   GetCPWLEdit()->SetSelection(0, -1);
@@ -345,7 +349,7 @@
   EXPECT_STREQ(L"Hippopotam", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldLeft) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   GetCPWLEdit()->SetSelection(0, 4);
@@ -354,7 +358,7 @@
   EXPECT_STREQ(L"Hippophant", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldMiddle) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   GetCPWLEdit()->SetSelection(2, 6);
@@ -363,7 +367,7 @@
   EXPECT_STREQ(L"ElHippopnt", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest,
+TEST_F(CPWLEditEmbedderTest,
        InsertTextAndReplaceSelectionInPopulatedCharLimitTextFieldRight) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit());
   GetCPWLEdit()->SetSelection(4, 8);
@@ -372,49 +376,49 @@
   EXPECT_STREQ(L"ElepHippop", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithEndCarriageFeed) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithEndCarriageFeed) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\r");
   EXPECT_STREQ(L"Foo", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithEndNewline) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithEndNewline) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\n");
   EXPECT_STREQ(L"Foo", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithEndCarriageFeedAndNewLine) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithEndCarriageFeedAndNewLine) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\r\n");
   EXPECT_STREQ(L"Foo", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithEndNewLineAndCarriageFeed) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithEndNewLineAndCarriageFeed) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\n\r");
   EXPECT_STREQ(L"Foo", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithBodyCarriageFeed) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithBodyCarriageFeed) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\rBar");
   EXPECT_STREQ(L"FooBar", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithBodyNewline) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithBodyNewline) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\nBar");
   EXPECT_STREQ(L"FooBar", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithBodyCarriageFeedAndNewLine) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithBodyCarriageFeedAndNewLine) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\r\nBar");
   EXPECT_STREQ(L"FooBar", GetCPWLEdit()->GetText().c_str());
 }
 
-TEST_F(CPWLEditEmbeddertest, SetTextWithBodyNewLineAndCarriageFeed) {
+TEST_F(CPWLEditEmbedderTest, SetTextWithBodyNewLineAndCarriageFeed) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnot());
   GetCPWLEdit()->SetText(L"Foo\n\rBar");
   EXPECT_STREQ(L"FooBar", GetCPWLEdit()->GetText().c_str());
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.cpp b/fpdfsdk/pwl/cpwl_edit_impl.cpp
index a2a7b1d..a9ee19e 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_impl.cpp
@@ -12,11 +12,6 @@
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/page/cpdf_pageobject.h"
-#include "core/fpdfapi/page/cpdf_pageobjectholder.h"
-#include "core/fpdfapi/page/cpdf_pathobject.h"
-#include "core/fpdfapi/page/cpdf_textobject.h"
-#include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_textrenderer.h"
 #include "core/fpdfdoc/cpvt_word.h"
@@ -26,12 +21,12 @@
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/cfx_systemhandler.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
 #include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
+#include "fpdfsdk/pwl/ipwl_systemhandler.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -43,26 +38,16 @@
                     float fFontSize,
                     const CFX_Matrix& mtUser2Device,
                     const ByteString& str,
-                    FX_ARGB crTextFill,
-                    int32_t nHorzScale) {
+                    FX_ARGB crTextFill) {
   if (!pFont)
     return;
 
   CFX_PointF pos = mtUser2Device.Transform(pt);
-  CFX_Matrix mt;
-  if (nHorzScale == 100) {
-    mt = mtUser2Device;
-  } else {
-    mt = CFX_Matrix(nHorzScale / 100.0f, 0, 0, 1, 0, 0);
-    mt.Concat(mtUser2Device);
-  }
-
   CPDF_RenderOptions ro;
-  ro.SetFlags(RENDER_CLEARTYPE);
-
+  ASSERT(ro.GetOptions().bClearType);
   ro.SetColorMode(CPDF_RenderOptions::kNormal);
   CPDF_TextRenderer::DrawTextString(pDevice, pos.x, pos.y, pFont, fFontSize,
-                                    &mt, str, crTextFill, nullptr, &ro);
+                                    mtUser2Device, str, crTextFill, ro);
 }
 
 }  // namespace
@@ -125,35 +110,30 @@
   return m_pFontMap;
 }
 
-int32_t CPWL_EditImpl_Provider::GetCharWidth(int32_t nFontIndex,
-                                             uint16_t word) {
-  if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) {
-    uint32_t charcode = word;
+uint32_t CPWL_EditImpl_Provider::GetCharWidth(int32_t nFontIndex,
+                                              uint16_t word) {
+  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+  if (!pPDFFont)
+    return 0;
 
-    if (pPDFFont->IsUnicodeCompatible())
-      charcode = pPDFFont->CharCodeFromUnicode(word);
-    else
-      charcode = m_pFontMap->CharCodeFromUnicode(nFontIndex, word);
+  uint32_t charcode = pPDFFont->IsUnicodeCompatible()
+                          ? pPDFFont->CharCodeFromUnicode(word)
+                          : m_pFontMap->CharCodeFromUnicode(nFontIndex, word);
 
-    if (charcode != CPDF_Font::kInvalidCharCode)
-      return pPDFFont->GetCharWidthF(charcode);
-  }
+  if (charcode == CPDF_Font::kInvalidCharCode)
+    return 0;
 
-  return 0;
+  return pPDFFont->GetCharWidthF(charcode);
 }
 
 int32_t CPWL_EditImpl_Provider::GetTypeAscent(int32_t nFontIndex) {
-  if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex))
-    return pPDFFont->GetTypeAscent();
-
-  return 0;
+  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+  return pPDFFont ? pPDFFont->GetTypeAscent() : 0;
 }
 
 int32_t CPWL_EditImpl_Provider::GetTypeDescent(int32_t nFontIndex) {
-  if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex))
-    return pPDFFont->GetTypeDescent();
-
-  return 0;
+  RetainPtr<CPDF_Font> pPDFFont = m_pFontMap->GetPDFFont(nFontIndex);
+  return pPDFFont ? pPDFFont->GetTypeDescent() : 0;
 }
 
 int32_t CPWL_EditImpl_Provider::GetWordFontIndex(uint16_t word,
@@ -175,8 +155,9 @@
 CPWL_EditImpl_Refresh::~CPWL_EditImpl_Refresh() {}
 
 void CPWL_EditImpl_Refresh::BeginRefresh() {
-  m_RefreshRects.clear();
   m_OldLineRects = std::move(m_NewLineRects);
+  m_NewLineRects.clear();
+  m_RefreshRects.clear();
 }
 
 void CPWL_EditImpl_Refresh::Push(const CPVT_WordRange& linerange,
@@ -219,11 +200,16 @@
 }
 
 void CPWL_EditImpl_Undo::Undo() {
+  ASSERT(!m_bWorking);
   m_bWorking = true;
-  if (CanUndo()) {
-    m_UndoItemStack[m_nCurUndoPos - 1]->Undo();
+  int nUndoRemain = 1;
+  while (CanUndo() && nUndoRemain > 0) {
+    nUndoRemain += m_UndoItemStack[m_nCurUndoPos - 1]->Undo();
     m_nCurUndoPos--;
+    nUndoRemain--;
   }
+  ASSERT(nUndoRemain == 0);
+  ASSERT(m_bWorking);
   m_bWorking = false;
 }
 
@@ -232,11 +218,16 @@
 }
 
 void CPWL_EditImpl_Undo::Redo() {
+  ASSERT(!m_bWorking);
   m_bWorking = true;
-  if (CanRedo()) {
-    m_UndoItemStack[m_nCurUndoPos]->Redo();
+  int nRedoRemain = 1;
+  while (CanRedo() && nRedoRemain > 0) {
+    nRedoRemain += m_UndoItemStack[m_nCurUndoPos]->Redo();
     m_nCurUndoPos++;
+    nRedoRemain--;
   }
+  ASSERT(nRedoRemain == 0);
+  ASSERT(m_bWorking);
   m_bWorking = false;
 }
 
@@ -278,16 +269,18 @@
 
 CFXEU_InsertWord::~CFXEU_InsertWord() {}
 
-void CFXEU_InsertWord::Redo() {
+int CFXEU_InsertWord::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
   m_pEdit->InsertWord(m_Word, m_nCharset, false, true);
+  return 0;
 }
 
-void CFXEU_InsertWord::Undo() {
+int CFXEU_InsertWord::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
   m_pEdit->Backspace(false, true);
+  return 0;
 }
 
 CFXEU_InsertReturn::CFXEU_InsertReturn(CPWL_EditImpl* pEdit,
@@ -299,16 +292,43 @@
 
 CFXEU_InsertReturn::~CFXEU_InsertReturn() {}
 
-void CFXEU_InsertReturn::Redo() {
+int CFXEU_InsertReturn::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
   m_pEdit->InsertReturn(false, true);
+  return 0;
 }
 
-void CFXEU_InsertReturn::Undo() {
+int CFXEU_InsertReturn::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
   m_pEdit->Backspace(false, true);
+  return 0;
+}
+
+CFXEU_ReplaceSelection::CFXEU_ReplaceSelection(CPWL_EditImpl* pEdit,
+                                               bool bIsEnd)
+    : m_pEdit(pEdit), m_bEnd(bIsEnd) {
+  ASSERT(m_pEdit);
+}
+
+CFXEU_ReplaceSelection::~CFXEU_ReplaceSelection() {}
+
+int CFXEU_ReplaceSelection::Redo() {
+  m_pEdit->SelectNone();
+  if (IsEnd())
+    return 0;
+  // Redo ClearSelection, InsertText and ReplaceSelection's end marker
+  return 3;
+}
+
+int CFXEU_ReplaceSelection::Undo() {
+  m_pEdit->SelectNone();
+  if (!IsEnd())
+    return 0;
+  // Undo InsertText, ClearSelection and ReplaceSelection's beginning
+  // marker
+  return 3;
 }
 
 CFXEU_Backspace::CFXEU_Backspace(CPWL_EditImpl* pEdit,
@@ -326,19 +346,21 @@
 
 CFXEU_Backspace::~CFXEU_Backspace() {}
 
-void CFXEU_Backspace::Redo() {
+int CFXEU_Backspace::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
   m_pEdit->Backspace(false, true);
+  return 0;
 }
 
-void CFXEU_Backspace::Undo() {
+int CFXEU_Backspace::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
   if (m_wpNew.nSecIndex != m_wpOld.nSecIndex)
     m_pEdit->InsertReturn(false, true);
   else
     m_pEdit->InsertWord(m_Word, m_nCharset, false, true);
+  return 0;
 }
 
 CFXEU_Delete::CFXEU_Delete(CPWL_EditImpl* pEdit,
@@ -358,19 +380,21 @@
 
 CFXEU_Delete::~CFXEU_Delete() {}
 
-void CFXEU_Delete::Redo() {
+int CFXEU_Delete::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
   m_pEdit->Delete(false, true);
+  return 0;
 }
 
-void CFXEU_Delete::Undo() {
+int CFXEU_Delete::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpNew);
   if (m_bSecEnd)
     m_pEdit->InsertReturn(false, true);
   else
     m_pEdit->InsertWord(m_Word, m_nCharset, false, true);
+  return 0;
 }
 
 CFXEU_Clear::CFXEU_Clear(CPWL_EditImpl* pEdit,
@@ -382,17 +406,19 @@
 
 CFXEU_Clear::~CFXEU_Clear() {}
 
-void CFXEU_Clear::Redo() {
+int CFXEU_Clear::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos);
   m_pEdit->Clear(false, true);
+  return 0;
 }
 
-void CFXEU_Clear::Undo() {
+int CFXEU_Clear::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wrSel.BeginPos);
   m_pEdit->InsertText(m_swText, FX_CHARSET_Default, false, true);
   m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos);
+  return 0;
 }
 
 CFXEU_InsertText::CFXEU_InsertText(CPWL_EditImpl* pEdit,
@@ -410,16 +436,18 @@
 
 CFXEU_InsertText::~CFXEU_InsertText() {}
 
-void CFXEU_InsertText::Redo() {
+int CFXEU_InsertText::Redo() {
   m_pEdit->SelectNone();
   m_pEdit->SetCaret(m_wpOld);
   m_pEdit->InsertText(m_swText, m_nCharset, false, true);
+  return 0;
 }
 
-void CFXEU_InsertText::Undo() {
+int CFXEU_InsertText::Undo() {
   m_pEdit->SelectNone();
   m_pEdit->SetSelection(m_wpOld, m_wpNew);
   m_pEdit->Clear(false, true);
+  return 0;
 }
 
 // static
@@ -430,21 +458,18 @@
                              const CFX_FloatRect& rcClip,
                              const CFX_PointF& ptOffset,
                              const CPVT_WordRange* pRange,
-                             CFX_SystemHandler* pSystemHandler,
+                             IPWL_SystemHandler* pSystemHandler,
                              CFFL_FormFiller* pFFLData) {
   const bool bContinuous =
       pEdit->GetCharArray() == 0 && pEdit->GetCharSpace() <= 0.0f;
   uint16_t SubWord = pEdit->GetPasswordChar();
   float fFontSize = pEdit->GetFontSize();
   CPVT_WordRange wrSelect = pEdit->GetSelectWordRange();
-  int32_t nHorzScale = pEdit->GetHorzScale();
-
   FX_COLORREF crCurFill = crTextFill;
   FX_COLORREF crOldFill = crCurFill;
-
   bool bSelect = false;
-  static const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255);
-  static const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113);
+  const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255);
+  const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113);
 
   std::ostringstream sTextBuf;
   int32_t nFontIndex = -1;
@@ -473,7 +498,7 @@
       bSelect = place > wrSelect.BeginPos && place <= wrSelect.EndPos;
       crCurFill = bSelect ? crWhite : crTextFill;
     }
-    if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) {
+    if (pSystemHandler->IsSelectionImplemented()) {
       crCurFill = crTextFill;
       crOldFill = crCurFill;
     }
@@ -483,7 +508,7 @@
         CPVT_Line line;
         pIterator->GetLine(line);
 
-        if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) {
+        if (pSystemHandler->IsSelectionImplemented()) {
           CFX_FloatRect rc(word.ptWord.x, line.ptLine.y + line.fLineDescent,
                            word.ptWord.x + word.fWidth,
                            line.ptLine.y + line.fLineAscent);
@@ -504,10 +529,10 @@
         if (place.LineCmp(oldplace) != 0 || word.nFontIndex != nFontIndex ||
             crOldFill != crCurFill) {
           if (sTextBuf.tellp() > 0) {
-            DrawTextString(
-                pDevice, CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
-                pFontMap->GetPDFFont(nFontIndex), fFontSize, mtUser2Device,
-                ByteString(sTextBuf), crOldFill, nHorzScale);
+            DrawTextString(pDevice,
+                           CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
+                           pFontMap->GetPDFFont(nFontIndex).Get(), fFontSize,
+                           mtUser2Device, ByteString(sTextBuf), crOldFill);
 
             sTextBuf.str("");
           }
@@ -522,9 +547,10 @@
         DrawTextString(
             pDevice,
             CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y),
-            pFontMap->GetPDFFont(word.nFontIndex), fFontSize, mtUser2Device,
+            pFontMap->GetPDFFont(word.nFontIndex).Get(), fFontSize,
+            mtUser2Device,
             pEdit->GetPDFWordString(word.nFontIndex, word.Word, SubWord),
-            crCurFill, nHorzScale);
+            crCurFill);
       }
       oldplace = place;
     }
@@ -533,8 +559,8 @@
   if (sTextBuf.tellp() > 0) {
     DrawTextString(pDevice,
                    CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
-                   pFontMap->GetPDFFont(nFontIndex), fFontSize, mtUser2Device,
-                   ByteString(sTextBuf), crOldFill, nHorzScale);
+                   pFontMap->GetPDFFont(nFontIndex).Get(), fFontSize,
+                   mtUser2Device, ByteString(sTextBuf), crOldFill);
   }
 }
 
@@ -784,7 +810,7 @@
 }
 
 void CPWL_EditImpl::SetText(const WideString& sText) {
-  Empty();
+  Clear();
   DoInsertText(CPVT_WordPlace(0, 0, -1), sText, FX_CHARSET_Default);
   Paint();
 }
@@ -829,10 +855,6 @@
   return VTToEdit(m_pVT->GetContentRect());
 }
 
-int32_t CPWL_EditImpl::GetHorzScale() const {
-  return m_pVT->GetHorzScale();
-}
-
 float CPWL_EditImpl::GetCharSpace() const {
   return m_pVT->GetCharSpace();
 }
@@ -1576,13 +1598,8 @@
     return false;
 
   if (bAddUndo && m_bEnableUndo) {
-    if (m_wpCaret.nSecIndex != m_wpOldCaret.nSecIndex) {
-      AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>(
-          this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset));
-    } else {
-      AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>(
-          this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset));
-    }
+    AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>(
+        this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset));
   }
   if (bPaint) {
     RearrangePart(CPVT_WordRange(m_wpCaret, m_wpOldCaret));
@@ -1633,7 +1650,7 @@
   return true;
 }
 
-bool CPWL_EditImpl::Empty() {
+bool CPWL_EditImpl::Clear() {
   if (m_pVT->IsValid()) {
     m_pVT->DeleteWords(GetWholeWordRange());
     SetCaret(m_pVT->GetBeginWordPlace());
@@ -1707,6 +1724,13 @@
   }
 }
 
+void CPWL_EditImpl::ReplaceSelection(const WideString& text) {
+  AddEditUndoItem(pdfium::MakeUnique<CFXEU_ReplaceSelection>(this, false));
+  ClearSelection();
+  InsertText(text, FX_CHARSET_Default);
+  AddEditUndoItem(pdfium::MakeUnique<CFXEU_ReplaceSelection>(this, true));
+}
+
 bool CPWL_EditImpl::Redo() {
   if (m_bEnableUndo) {
     if (m_Undo.CanRedo()) {
@@ -1812,16 +1836,17 @@
     for (int32_t i = 0, sz = sText.GetLength(); i < sz; i++) {
       uint16_t word = sText[i];
       switch (word) {
-        case 0x0D:
+        case '\r':
           wp = m_pVT->InsertSection(wp);
-          if (i + 1 < sz && sText[i + 1] == 0x0A)
+          if (i + 1 < sz && sText[i + 1] == '\n')
             i++;
           break;
-        case 0x0A:
+        case '\n':
           wp = m_pVT->InsertSection(wp);
           break;
-        case 0x09:
-          word = 0x20;
+        case '\t':
+          word = ' ';
+          FALLTHROUGH;
         default:
           wp =
               m_pVT->InsertWord(wp, word, GetCharSetFromUnicode(word, charset));
@@ -1849,7 +1874,7 @@
                                            uint16_t Word,
                                            uint16_t SubWord) {
   IPVT_FontMap* pFontMap = GetFontMap();
-  CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex);
+  RetainPtr<CPDF_Font> pPDFFont = pFontMap->GetPDFFont(nFontIndex);
   if (!pPDFFont)
     return ByteString();
 
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.h b/fpdfsdk/pwl/cpwl_edit_impl.h
index 38477db..e9ac7de 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.h
+++ b/fpdfsdk/pwl/cpwl_edit_impl.h
@@ -25,9 +25,9 @@
 class CPWL_EditImpl_Iterator;
 class CPWL_EditImpl_Provider;
 class CFX_RenderDevice;
-class CFX_SystemHandler;
 class CPWL_Edit;
 class CPWL_EditCtrl;
+class IPWL_SystemHandler;
 class IFX_Edit_UndoItem;
 
 struct CPWL_EditImpl_LineRect {
@@ -96,13 +96,21 @@
 
 class IFX_Edit_UndoItem {
  public:
-  virtual ~IFX_Edit_UndoItem() {}
+  virtual ~IFX_Edit_UndoItem() = default;
 
-  virtual void Undo() = 0;
-  virtual void Redo() = 0;
+  // Undo/Redo the current undo item and returns the number of additional items
+  // to be processed in |m_UndoItemStack| to fully undo/redo the action. (An
+  // example is CFXEU_ReplaceSelection::Undo(), if CFXEU_ReplaceSelection marks
+  // the end of a replace action, CFXEU_ReplaceSelection::Undo() returns 3
+  // because 3 more undo items need to be processed to revert the replace
+  // action: insert text, clear selection and the CFXEU_ReplaceSelection which
+  // marks the beginning of replace action.) Implementations should return 0 by
+  // default.
+  virtual int Undo() = 0;
+  virtual int Redo() = 0;
 };
 
-class CFXEU_InsertWord : public IFX_Edit_UndoItem {
+class CFXEU_InsertWord final : public IFX_Edit_UndoItem {
  public:
   CFXEU_InsertWord(CPWL_EditImpl* pEdit,
                    const CPVT_WordPlace& wpOldPlace,
@@ -112,8 +120,8 @@
   ~CFXEU_InsertWord() override;
 
   // IFX_Edit_UndoItem:
-  void Redo() override;
-  void Undo() override;
+  int Redo() override;
+  int Undo() override;
 
  private:
   UnownedPtr<CPWL_EditImpl> m_pEdit;
@@ -124,7 +132,7 @@
   int32_t m_nCharset;
 };
 
-class CFXEU_InsertReturn : public IFX_Edit_UndoItem {
+class CFXEU_InsertReturn final : public IFX_Edit_UndoItem {
  public:
   CFXEU_InsertReturn(CPWL_EditImpl* pEdit,
                      const CPVT_WordPlace& wpOldPlace,
@@ -132,8 +140,8 @@
   ~CFXEU_InsertReturn() override;
 
   // IFX_Edit_UndoItem:
-  void Redo() override;
-  void Undo() override;
+  int Redo() override;
+  int Undo() override;
 
  private:
   UnownedPtr<CPWL_EditImpl> m_pEdit;
@@ -142,7 +150,23 @@
   CPVT_WordPlace m_wpNew;
 };
 
-class CFXEU_Backspace : public IFX_Edit_UndoItem {
+class CFXEU_ReplaceSelection final : public IFX_Edit_UndoItem {
+ public:
+  CFXEU_ReplaceSelection(CPWL_EditImpl* pEdit, bool bIsEnd);
+  ~CFXEU_ReplaceSelection() override;
+
+  // IFX_Edit_UndoItem:
+  int Redo() override;
+  int Undo() override;
+
+ private:
+  bool IsEnd() const { return m_bEnd; }
+
+  UnownedPtr<CPWL_EditImpl> m_pEdit;
+  const bool m_bEnd;  // indicate whether this is the end of replace action
+};
+
+class CFXEU_Backspace final : public IFX_Edit_UndoItem {
  public:
   CFXEU_Backspace(CPWL_EditImpl* pEdit,
                   const CPVT_WordPlace& wpOldPlace,
@@ -152,8 +176,8 @@
   ~CFXEU_Backspace() override;
 
   // IFX_Edit_UndoItem:
-  void Redo() override;
-  void Undo() override;
+  int Redo() override;
+  int Undo() override;
 
  private:
   UnownedPtr<CPWL_EditImpl> m_pEdit;
@@ -164,7 +188,7 @@
   int32_t m_nCharset;
 };
 
-class CFXEU_Delete : public IFX_Edit_UndoItem {
+class CFXEU_Delete final : public IFX_Edit_UndoItem {
  public:
   CFXEU_Delete(CPWL_EditImpl* pEdit,
                const CPVT_WordPlace& wpOldPlace,
@@ -175,8 +199,8 @@
   ~CFXEU_Delete() override;
 
   // IFX_Edit_UndoItem:
-  void Redo() override;
-  void Undo() override;
+  int Redo() override;
+  int Undo() override;
 
  private:
   UnownedPtr<CPWL_EditImpl> m_pEdit;
@@ -188,7 +212,7 @@
   bool m_bSecEnd;
 };
 
-class CFXEU_Clear : public IFX_Edit_UndoItem {
+class CFXEU_Clear final : public IFX_Edit_UndoItem {
  public:
   CFXEU_Clear(CPWL_EditImpl* pEdit,
               const CPVT_WordRange& wrSel,
@@ -196,8 +220,8 @@
   ~CFXEU_Clear() override;
 
   // IFX_Edit_UndoItem:
-  void Redo() override;
-  void Undo() override;
+  int Redo() override;
+  int Undo() override;
 
  private:
   UnownedPtr<CPWL_EditImpl> m_pEdit;
@@ -206,7 +230,7 @@
   WideString m_swText;
 };
 
-class CFXEU_InsertText : public IFX_Edit_UndoItem {
+class CFXEU_InsertText final : public IFX_Edit_UndoItem {
  public:
   CFXEU_InsertText(CPWL_EditImpl* pEdit,
                    const CPVT_WordPlace& wpOldPlace,
@@ -216,8 +240,8 @@
   ~CFXEU_InsertText() override;
 
   // IFX_Edit_UndoItem:
-  void Redo() override;
-  void Undo() override;
+  int Redo() override;
+  int Undo() override;
 
  private:
   UnownedPtr<CPWL_EditImpl> m_pEdit;
@@ -237,7 +261,7 @@
                        const CFX_FloatRect& rcClip,
                        const CFX_PointF& ptOffset,
                        const CPVT_WordRange* pRange,
-                       CFX_SystemHandler* pSystemHandler,
+                       IPWL_SystemHandler* pSystemHandler,
                        CFFL_FormFiller* pFFLData);
 
   CPWL_EditImpl();
@@ -289,6 +313,7 @@
   bool Delete();
   bool ClearSelection();
   bool InsertText(const WideString& sText, int32_t charset);
+  void ReplaceSelection(const WideString& text);
   bool Redo();
   bool Undo();
   CPVT_WordPlace WordIndexToWordPlace(int32_t index) const;
@@ -303,7 +328,6 @@
   int32_t GetCharArray() const;
   CFX_FloatRect GetContentRect() const;
   WideString GetRangeText(const CPVT_WordRange& range) const;
-  int32_t GetHorzScale() const;
   float GetCharSpace() const;
   void SetSelection(int32_t nStartChar, int32_t nEndChar);
   void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const;
@@ -322,7 +346,7 @@
   bool CanRedo() const;
   CPVT_WordRange GetVisibleWordRange() const;
 
-  bool Empty();
+  bool Clear();
 
   CPVT_WordPlace DoInsertText(const CPVT_WordPlace& place,
                               const WideString& sText,
@@ -374,10 +398,10 @@
 
   void AddEditUndoItem(std::unique_ptr<IFX_Edit_UndoItem> pEditUndoItem);
 
-  std::unique_ptr<CPDF_VariableText> m_pVT;
+  std::unique_ptr<CPWL_EditImpl_Provider> m_pVTProvider;
+  std::unique_ptr<CPDF_VariableText> m_pVT;  // Must outlive |m_pVTProvider|.
   UnownedPtr<CPWL_EditCtrl> m_pNotify;
   UnownedPtr<CPWL_Edit> m_pOperationNotify;
-  std::unique_ptr<CPWL_EditImpl_Provider> m_pVTProvider;
   CPVT_WordPlace m_wpCaret;
   CPVT_WordPlace m_wpOldCaret;
   CPWL_EditImpl_Select m_SelState;
@@ -415,7 +439,7 @@
   CPDF_VariableText::Iterator* m_pVTIterator;
 };
 
-class CPWL_EditImpl_Provider : public CPDF_VariableText::Provider {
+class CPWL_EditImpl_Provider final : public CPDF_VariableText::Provider {
  public:
   explicit CPWL_EditImpl_Provider(IPVT_FontMap* pFontMap);
   ~CPWL_EditImpl_Provider() override;
@@ -423,7 +447,7 @@
   IPVT_FontMap* GetFontMap() const;
 
   // CPDF_VariableText::Provider:
-  int32_t GetCharWidth(int32_t nFontIndex, uint16_t word) override;
+  uint32_t GetCharWidth(int32_t nFontIndex, uint16_t word) override;
   int32_t GetTypeAscent(int32_t nFontIndex) override;
   int32_t GetTypeDescent(int32_t nFontIndex) override;
   int32_t GetWordFontIndex(uint16_t word,
diff --git a/fpdfsdk/pwl/cpwl_font_map.cpp b/fpdfsdk/pwl/cpwl_font_map.cpp
deleted file mode 100644
index 5e5556e..0000000
--- a/fpdfsdk/pwl/cpwl_font_map.cpp
+++ /dev/null
@@ -1,408 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fpdfsdk/pwl/cpwl_font_map.h"
-
-#include <utility>
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/font/cpdf_fontencoding.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
-#include "core/fpdfapi/parser/cpdf_parser.h"
-#include "core/fpdfdoc/ipvt_fontmap.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-namespace {
-
-const char kDefaultFontName[] = "Helvetica";
-
-const char* const g_sDEStandardFontName[] = {"Courier",
-                                             "Courier-Bold",
-                                             "Courier-BoldOblique",
-                                             "Courier-Oblique",
-                                             "Helvetica",
-                                             "Helvetica-Bold",
-                                             "Helvetica-BoldOblique",
-                                             "Helvetica-Oblique",
-                                             "Times-Roman",
-                                             "Times-Bold",
-                                             "Times-Italic",
-                                             "Times-BoldItalic",
-                                             "Symbol",
-                                             "ZapfDingbats"};
-
-}  // namespace
-
-CPWL_FontMap::CPWL_FontMap(CFX_SystemHandler* pSystemHandler)
-    : m_pSystemHandler(pSystemHandler) {
-  ASSERT(m_pSystemHandler);
-}
-
-CPWL_FontMap::~CPWL_FontMap() {
-  Empty();
-}
-
-CPDF_Document* CPWL_FontMap::GetDocument() {
-  if (!m_pPDFDoc) {
-    if (CPDF_ModuleMgr::Get()) {
-      m_pPDFDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
-      m_pPDFDoc->CreateNewDoc();
-    }
-  }
-  return m_pPDFDoc.get();
-}
-
-CPDF_Font* CPWL_FontMap::GetPDFFont(int32_t nFontIndex) {
-  if (pdfium::IndexInBounds(m_Data, nFontIndex) && m_Data[nFontIndex])
-    return m_Data[nFontIndex]->pFont;
-
-  return nullptr;
-}
-
-ByteString CPWL_FontMap::GetPDFFontAlias(int32_t nFontIndex) {
-  if (pdfium::IndexInBounds(m_Data, nFontIndex) && m_Data[nFontIndex])
-    return m_Data[nFontIndex]->sFontName;
-
-  return ByteString();
-}
-
-bool CPWL_FontMap::KnowWord(int32_t nFontIndex, uint16_t word) {
-  return pdfium::IndexInBounds(m_Data, nFontIndex) && m_Data[nFontIndex] &&
-         CharCodeFromUnicode(nFontIndex, word) >= 0;
-}
-
-int32_t CPWL_FontMap::GetWordFontIndex(uint16_t word,
-                                       int32_t nCharset,
-                                       int32_t nFontIndex) {
-  if (nFontIndex > 0) {
-    if (KnowWord(nFontIndex, word))
-      return nFontIndex;
-  } else {
-    if (const CPWL_FontMap_Data* pData = GetFontMapData(0)) {
-      if (nCharset == FX_CHARSET_Default ||
-          pData->nCharset == FX_CHARSET_Symbol || nCharset == pData->nCharset) {
-        if (KnowWord(0, word))
-          return 0;
-      }
-    }
-  }
-
-  int32_t nNewFontIndex =
-      GetFontIndex(GetNativeFontName(nCharset), nCharset, true);
-  if (nNewFontIndex >= 0) {
-    if (KnowWord(nNewFontIndex, word))
-      return nNewFontIndex;
-  }
-  nNewFontIndex = GetFontIndex("Arial Unicode MS", FX_CHARSET_Default, false);
-  if (nNewFontIndex >= 0) {
-    if (KnowWord(nNewFontIndex, word))
-      return nNewFontIndex;
-  }
-  return -1;
-}
-
-int32_t CPWL_FontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) {
-  if (!pdfium::IndexInBounds(m_Data, nFontIndex))
-    return -1;
-
-  CPWL_FontMap_Data* pData = m_Data[nFontIndex].get();
-  if (!pData || !pData->pFont)
-    return -1;
-
-  if (pData->pFont->IsUnicodeCompatible())
-    return pData->pFont->CharCodeFromUnicode(word);
-
-  return word < 0xFF ? word : -1;
-}
-
-ByteString CPWL_FontMap::GetNativeFontName(int32_t nCharset) {
-  for (const auto& pData : m_NativeFont) {
-    if (pData && pData->nCharset == nCharset)
-      return pData->sFontName;
-  }
-
-  ByteString sNew = GetNativeFont(nCharset);
-  if (sNew.IsEmpty())
-    return ByteString();
-
-  auto pNewData = pdfium::MakeUnique<CPWL_FontMap_Native>();
-  pNewData->nCharset = nCharset;
-  pNewData->sFontName = sNew;
-  m_NativeFont.push_back(std::move(pNewData));
-  return sNew;
-}
-
-void CPWL_FontMap::Empty() {
-  m_Data.clear();
-  m_NativeFont.clear();
-}
-
-void CPWL_FontMap::Initialize() {
-  GetFontIndex(kDefaultFontName, FX_CHARSET_ANSI, false);
-}
-
-bool CPWL_FontMap::IsStandardFont(const ByteString& sFontName) {
-  for (const char* name : g_sDEStandardFontName) {
-    if (sFontName == name)
-      return true;
-  }
-
-  return false;
-}
-
-int32_t CPWL_FontMap::FindFont(const ByteString& sFontName, int32_t nCharset) {
-  int32_t i = 0;
-  for (const auto& pData : m_Data) {
-    if (pData &&
-        (nCharset == FX_CHARSET_Default || nCharset == pData->nCharset) &&
-        (sFontName.IsEmpty() || pData->sFontName == sFontName)) {
-      return i;
-    }
-    ++i;
-  }
-  return -1;
-}
-
-int32_t CPWL_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;
-  CPDF_Font* pFont = bFind ? FindFontSameCharset(&sAlias, nCharset) : nullptr;
-  if (!pFont) {
-    ByteString sTemp = sFontName;
-    pFont = AddFontToDocument(GetDocument(), sTemp, nCharset);
-    sAlias = EncodeFontAlias(sTemp, nCharset);
-  }
-  AddedFont(pFont, sAlias);
-  return AddFontData(pFont, sAlias, nCharset);
-}
-
-CPDF_Font* CPWL_FontMap::FindFontSameCharset(ByteString* sFontAlias,
-                                             int32_t nCharset) {
-  return nullptr;
-}
-
-int32_t CPWL_FontMap::AddFontData(CPDF_Font* pFont,
-                                  const ByteString& sFontAlias,
-                                  int32_t nCharset) {
-  auto pNewData = pdfium::MakeUnique<CPWL_FontMap_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;
-}
-
-void CPWL_FontMap::AddedFont(CPDF_Font* pFont, const ByteString& sFontAlias) {}
-
-ByteString CPWL_FontMap::GetNativeFont(int32_t nCharset) {
-  if (nCharset == FX_CHARSET_Default)
-    nCharset = GetNativeCharset();
-
-  ByteString sFontName = GetDefaultFontByCharset(nCharset);
-  if (!m_pSystemHandler->FindNativeTrueTypeFont(sFontName))
-    return ByteString();
-
-  return sFontName;
-}
-
-CPDF_Font* CPWL_FontMap::AddFontToDocument(CPDF_Document* pDoc,
-                                           ByteString& sFontName,
-                                           uint8_t nCharset) {
-  if (IsStandardFont(sFontName))
-    return AddStandardFont(pDoc, sFontName);
-
-  return AddSystemFont(pDoc, sFontName, nCharset);
-}
-
-CPDF_Font* CPWL_FontMap::AddStandardFont(CPDF_Document* pDoc,
-                                         ByteString& sFontName) {
-  if (!pDoc)
-    return nullptr;
-
-  CPDF_Font* pFont = nullptr;
-
-  if (sFontName == "ZapfDingbats") {
-    pFont = pDoc->AddStandardFont(sFontName.c_str(), nullptr);
-  } else {
-    CPDF_FontEncoding fe(PDFFONT_ENCODING_WINANSI);
-    pFont = pDoc->AddStandardFont(sFontName.c_str(), &fe);
-  }
-
-  return pFont;
-}
-
-CPDF_Font* CPWL_FontMap::AddSystemFont(CPDF_Document* pDoc,
-                                       ByteString& sFontName,
-                                       uint8_t nCharset) {
-  if (!pDoc)
-    return nullptr;
-
-  if (sFontName.IsEmpty())
-    sFontName = GetNativeFont(nCharset);
-  if (nCharset == FX_CHARSET_Default)
-    nCharset = GetNativeCharset();
-
-  return m_pSystemHandler->AddNativeTrueTypeFontToPDF(pDoc, sFontName,
-                                                      nCharset);
-}
-
-ByteString CPWL_FontMap::EncodeFontAlias(const ByteString& sFontName,
-                                         int32_t nCharset) {
-  return EncodeFontAlias(sFontName) + ByteString::Format("_%02X", nCharset);
-}
-
-ByteString CPWL_FontMap::EncodeFontAlias(const ByteString& sFontName) {
-  ByteString sRet = sFontName;
-  sRet.Remove(' ');
-  return sRet;
-}
-
-const CPWL_FontMap_Data* CPWL_FontMap::GetFontMapData(int32_t nIndex) const {
-  return pdfium::IndexInBounds(m_Data, nIndex) ? m_Data[nIndex].get() : nullptr;
-}
-
-int32_t CPWL_FontMap::GetNativeCharset() {
-  uint8_t nCharset = FX_CHARSET_ANSI;
-  int32_t iCodePage = FXSYS_GetACP();
-  switch (iCodePage) {
-    case FX_CODEPAGE_ShiftJIS:
-      nCharset = FX_CHARSET_ShiftJIS;
-      break;
-    case FX_CODEPAGE_ChineseSimplified:
-      nCharset = FX_CHARSET_ChineseSimplified;
-      break;
-    case FX_CODEPAGE_ChineseTraditional:
-      nCharset = FX_CHARSET_ChineseTraditional;
-      break;
-    case FX_CODEPAGE_MSWin_WesternEuropean:
-      nCharset = FX_CHARSET_ANSI;
-      break;
-    case FX_CODEPAGE_MSDOS_Thai:
-      nCharset = FX_CHARSET_Thai;
-      break;
-    case FX_CODEPAGE_Hangul:
-      nCharset = FX_CHARSET_Hangul;
-      break;
-    case FX_CODEPAGE_UTF16LE:
-      nCharset = FX_CHARSET_ANSI;
-      break;
-    case FX_CODEPAGE_MSWin_EasternEuropean:
-      nCharset = FX_CHARSET_MSWin_EasternEuropean;
-      break;
-    case FX_CODEPAGE_MSWin_Cyrillic:
-      nCharset = FX_CHARSET_MSWin_Cyrillic;
-      break;
-    case FX_CODEPAGE_MSWin_Greek:
-      nCharset = FX_CHARSET_MSWin_Greek;
-      break;
-    case FX_CODEPAGE_MSWin_Turkish:
-      nCharset = FX_CHARSET_MSWin_Turkish;
-      break;
-    case FX_CODEPAGE_MSWin_Hebrew:
-      nCharset = FX_CHARSET_MSWin_Hebrew;
-      break;
-    case FX_CODEPAGE_MSWin_Arabic:
-      nCharset = FX_CHARSET_MSWin_Arabic;
-      break;
-    case FX_CODEPAGE_MSWin_Baltic:
-      nCharset = FX_CHARSET_MSWin_Baltic;
-      break;
-    case FX_CODEPAGE_MSWin_Vietnamese:
-      nCharset = FX_CHARSET_MSWin_Vietnamese;
-      break;
-    case FX_CODEPAGE_Johab:
-      nCharset = FX_CHARSET_Johab;
-      break;
-  }
-  return nCharset;
-}
-
-const FPDF_CharsetFontMap CPWL_FontMap::defaultTTFMap[] = {
-    {FX_CHARSET_ANSI, "Helvetica"},
-    {FX_CHARSET_ChineseSimplified, "SimSun"},
-    {FX_CHARSET_ChineseTraditional, "MingLiU"},
-    {FX_CHARSET_ShiftJIS, "MS Gothic"},
-    {FX_CHARSET_Hangul, "Batang"},
-    {FX_CHARSET_MSWin_Cyrillic, "Arial"},
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ || _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-    {FX_CHARSET_MSWin_EasternEuropean, "Arial"},
-#else
-    {FX_CHARSET_MSWin_EasternEuropean, "Tahoma"},
-#endif
-    {FX_CHARSET_MSWin_Arabic, "Arial"},
-    {-1, nullptr}};
-
-ByteString CPWL_FontMap::GetDefaultFontByCharset(int32_t nCharset) {
-  int i = 0;
-  while (defaultTTFMap[i].charset != -1) {
-    if (nCharset == defaultTTFMap[i].charset)
-      return defaultTTFMap[i].fontname;
-    ++i;
-  }
-  return "";
-}
-
-int32_t CPWL_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;
-
-  // find new charset
-  if ((word >= 0x4E00 && word <= 0x9FA5) ||
-      (word >= 0xE7C7 && word <= 0xE7F3) ||
-      (word >= 0x3000 && word <= 0x303F) ||
-      (word >= 0x2000 && word <= 0x206F)) {
-    return FX_CHARSET_ChineseSimplified;
-  }
-
-  if (((word >= 0x3040) && (word <= 0x309F)) ||
-      ((word >= 0x30A0) && (word <= 0x30FF)) ||
-      ((word >= 0x31F0) && (word <= 0x31FF)) ||
-      ((word >= 0xFF00) && (word <= 0xFFEF))) {
-    return FX_CHARSET_ShiftJIS;
-  }
-
-  if (((word >= 0xAC00) && (word <= 0xD7AF)) ||
-      ((word >= 0x1100) && (word <= 0x11FF)) ||
-      ((word >= 0x3130) && (word <= 0x318F))) {
-    return FX_CHARSET_Hangul;
-  }
-
-  if (word >= 0x0E00 && word <= 0x0E7F)
-    return FX_CHARSET_Thai;
-
-  if ((word >= 0x0370 && word <= 0x03FF) || (word >= 0x1F00 && word <= 0x1FFF))
-    return FX_CHARSET_MSWin_Greek;
-
-  if ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC))
-    return FX_CHARSET_MSWin_Arabic;
-
-  if (word >= 0x0590 && word <= 0x05FF)
-    return FX_CHARSET_MSWin_Hebrew;
-
-  if (word >= 0x0400 && word <= 0x04FF)
-    return FX_CHARSET_MSWin_Cyrillic;
-
-  if (word >= 0x0100 && word <= 0x024F)
-    return FX_CHARSET_MSWin_EasternEuropean;
-
-  if (word >= 0x1E00 && word <= 0x1EFF)
-    return FX_CHARSET_MSWin_Vietnamese;
-
-  return FX_CHARSET_ANSI;
-}
diff --git a/fpdfsdk/pwl/cpwl_font_map.h b/fpdfsdk/pwl/cpwl_font_map.h
deleted file mode 100644
index 592c3fa..0000000
--- a/fpdfsdk/pwl/cpwl_font_map.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_PWL_CPWL_FONT_MAP_H_
-#define FPDFSDK_PWL_CPWL_FONT_MAP_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fpdfdoc/ipvt_fontmap.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "public/fpdf_sysfontinfo.h"
-
-class CPDF_Document;
-class CFX_SystemHandler;
-
-struct CPWL_FontMap_Data {
-  CPDF_Font* pFont;
-  int32_t nCharset;
-  ByteString sFontName;
-};
-
-struct CPWL_FontMap_Native {
-  int32_t nCharset;
-  ByteString sFontName;
-};
-
-class CPWL_FontMap : public IPVT_FontMap {
- public:
-  explicit CPWL_FontMap(CFX_SystemHandler* pSystemHandler);
-  ~CPWL_FontMap() override;
-
-  // IPVT_FontMap
-  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;
-
-  const CPWL_FontMap_Data* GetFontMapData(int32_t nIndex) const;
-  static int32_t GetNativeCharset();
-  ByteString GetNativeFontName(int32_t nCharset);
-
-  static ByteString GetDefaultFontByCharset(int32_t nCharset);
-  static const FPDF_CharsetFontMap defaultTTFMap[];
-
- protected:
-  virtual void Initialize();
-  virtual CPDF_Document* GetDocument();
-  virtual CPDF_Font* FindFontSameCharset(ByteString* sFontAlias,
-                                         int32_t nCharset);
-  virtual void AddedFont(CPDF_Font* pFont, const ByteString& sFontAlias);
-
-  bool KnowWord(int32_t nFontIndex, uint16_t word);
-
-  void Empty();
-  int32_t GetFontIndex(const ByteString& sFontName,
-                       int32_t nCharset,
-                       bool bFind);
-  int32_t AddFontData(CPDF_Font* pFont,
-                      const ByteString& sFontAlias,
-                      int32_t nCharset = FX_CHARSET_Default);
-
-  ByteString EncodeFontAlias(const ByteString& sFontName, int32_t nCharset);
-  ByteString EncodeFontAlias(const ByteString& sFontName);
-
-  std::vector<std::unique_ptr<CPWL_FontMap_Data>> m_Data;
-  std::vector<std::unique_ptr<CPWL_FontMap_Native>> m_NativeFont;
-
- private:
-  int32_t FindFont(const ByteString& sFontName,
-                   int32_t nCharset = FX_CHARSET_Default);
-
-  ByteString GetNativeFont(int32_t nCharset);
-  CPDF_Font* AddFontToDocument(CPDF_Document* pDoc,
-                               ByteString& sFontName,
-                               uint8_t nCharset);
-  bool IsStandardFont(const ByteString& sFontName);
-  CPDF_Font* AddStandardFont(CPDF_Document* pDoc, ByteString& sFontName);
-  CPDF_Font* AddSystemFont(CPDF_Document* pDoc,
-                           ByteString& sFontName,
-                           uint8_t nCharset);
-
-  std::unique_ptr<CPDF_Document> m_pPDFDoc;
-  UnownedPtr<CFX_SystemHandler> const m_pSystemHandler;
-};
-
-#endif  // FPDFSDK_PWL_CPWL_FONT_MAP_H_
diff --git a/fpdfsdk/pwl/cpwl_icon.cpp b/fpdfsdk/pwl/cpwl_icon.cpp
index e7669b3..67df09e 100644
--- a/fpdfsdk/pwl/cpwl_icon.cpp
+++ b/fpdfsdk/pwl/cpwl_icon.cpp
@@ -8,72 +8,51 @@
 
 #include <algorithm>
 #include <sstream>
+#include <utility>
 
-#include "core/fpdfapi/parser/cpdf_array.h"
-#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfdoc/cpdf_icon.h"
+#include "core/fpdfdoc/cpdf_iconfit.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-CPWL_Icon::CPWL_Icon() : m_pPDFStream(nullptr), m_pIconFit(nullptr) {}
+CPWL_Icon::CPWL_Icon(const CreateParams& cp,
+                     std::unique_ptr<CPDF_Icon> pIcon,
+                     CPDF_IconFit* pFit)
+    : CPWL_Wnd(cp, nullptr), m_pIcon(std::move(pIcon)), m_pIconFit(pFit) {
+  ASSERT(m_pIcon);
+}
 
-CPWL_Icon::~CPWL_Icon() {}
+CPWL_Icon::~CPWL_Icon() = default;
 
-std::pair<float, float> CPWL_Icon::GetImageSize() {
-  if (!m_pPDFStream)
-    return {0.0f, 0.0f};
-
-  CPDF_Dictionary* pDict = m_pPDFStream->GetDict();
-  if (!pDict)
-    return {0.0f, 0.0f};
-
-  CFX_FloatRect rect = pDict->GetRectFor("BBox");
-  return {rect.right - rect.left, rect.top - rect.bottom};
+CFX_SizeF CPWL_Icon::GetImageSize() {
+  return m_pIcon->GetImageSize();
 }
 
 CFX_Matrix CPWL_Icon::GetImageMatrix() {
-  if (!m_pPDFStream)
-    return CFX_Matrix();
-  if (CPDF_Dictionary* pDict = m_pPDFStream->GetDict())
-    return pDict->GetMatrixFor("Matrix");
-  return CFX_Matrix();
+  return m_pIcon->GetImageMatrix();
 }
 
 ByteString CPWL_Icon::GetImageAlias() {
-  if (!m_pPDFStream)
-    return ByteString();
-  if (CPDF_Dictionary* pDict = m_pPDFStream->GetDict())
-    return pDict->GetStringFor("Name");
-  return ByteString();
+  return m_pIcon->GetImageAlias();
 }
 
-std::pair<float, float> CPWL_Icon::GetIconPosition() {
+CFX_PointF CPWL_Icon::GetIconPosition() {
   if (!m_pIconFit)
-    return {0.0f, 0.0f};
+    return CFX_PointF();
 
-  CPDF_Array* pA =
-      m_pIconFit->GetDict() ? m_pIconFit->GetDict()->GetArrayFor("A") : nullptr;
-  if (!pA)
-    return {0.0f, 0.0f};
-
-  size_t dwCount = pA->GetCount();
-  return {dwCount > 0 ? pA->GetNumberAt(0) : 0.0f,
-          dwCount > 1 ? pA->GetNumberAt(1) : 0.0f};
+  return m_pIconFit->GetIconPosition();
 }
 
 std::pair<float, float> CPWL_Icon::GetScale() {
   float fHScale = 1.0f;
   float fVScale = 1.0f;
 
-  if (!m_pPDFStream)
-    return {fHScale, fVScale};
-
   CFX_FloatRect rcPlate = GetClientRect();
-  float fPlateWidth = rcPlate.right - rcPlate.left;
-  float fPlateHeight = rcPlate.top - rcPlate.bottom;
+  float fPlateWidth = rcPlate.Width();
+  float fPlateHeight = rcPlate.Height();
 
-  float fImageWidth;
-  float fImageHeight;
-  std::tie(fImageWidth, fImageHeight) = GetImageSize();
-
+  CFX_SizeF image_size = GetImageSize();
+  float fImageWidth = image_size.width;
+  float fImageHeight = image_size.height;
   int32_t nScaleMethod = m_pIconFit ? m_pIconFit->GetScaleMethod() : 0;
 
   switch (nScaleMethod) {
@@ -108,13 +87,13 @@
 }
 
 std::pair<float, float> CPWL_Icon::GetImageOffset() {
-  float fLeft;
-  float fBottom;
-  std::tie(fLeft, fBottom) = GetIconPosition();
+  CFX_PointF icon_position = GetIconPosition();
+  float fLeft = icon_position.x;
+  float fBottom = icon_position.y;
 
-  float fImageWidth;
-  float fImageHeight;
-  std::tie(fImageWidth, fImageHeight) = GetImageSize();
+  CFX_SizeF image_size = GetImageSize();
+  float fImageWidth = image_size.width;
+  float fImageHeight = image_size.height;
 
   float fHScale, fVScale;
   std::tie(fHScale, fVScale) = GetScale();
@@ -123,8 +102,8 @@
   float fImageFactHeight = fImageHeight * fVScale;
 
   CFX_FloatRect rcPlate = GetClientRect();
-  float fPlateWidth = rcPlate.right - rcPlate.left;
-  float fPlateHeight = rcPlate.top - rcPlate.bottom;
+  float fPlateWidth = rcPlate.Width();
+  float fPlateHeight = rcPlate.Height();
 
   return {(fPlateWidth - fImageFactWidth) * fLeft,
           (fPlateHeight - fImageFactHeight) * fBottom};
diff --git a/fpdfsdk/pwl/cpwl_icon.h b/fpdfsdk/pwl/cpwl_icon.h
index df88465..aa9b3d7 100644
--- a/fpdfsdk/pwl/cpwl_icon.h
+++ b/fpdfsdk/pwl/cpwl_icon.h
@@ -7,19 +7,22 @@
 #ifndef FPDFSDK_PWL_CPWL_ICON_H_
 #define FPDFSDK_PWL_CPWL_ICON_H_
 
+#include <memory>
 #include <utility>
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-class CPWL_Icon : public CPWL_Wnd {
- public:
-  CPWL_Icon();
-  ~CPWL_Icon() override;
+class CPDF_Icon;
+class CPDF_IconFit;
 
-  void SetIconFit(CPDF_IconFit* pIconFit) { m_pIconFit = pIconFit; }
-  void SetPDFStream(CPDF_Stream* pStream) { m_pPDFStream = pStream; }
+class CPWL_Icon final : public CPWL_Wnd {
+ public:
+  CPWL_Icon(const CreateParams& cp,
+            std::unique_ptr<CPDF_Icon> pIcon,
+            CPDF_IconFit* pFit);
+  ~CPWL_Icon() override;
 
   // horizontal scale, vertical scale
   std::pair<float, float> GetScale();
@@ -31,14 +34,11 @@
   ByteString GetImageAlias();
 
  private:
-  // left, bottom
-  std::pair<float, float> GetIconPosition();
+  CFX_PointF GetIconPosition();  // left, bottom coordinates.
+  CFX_SizeF GetImageSize();
 
-  // width, height
-  std::pair<float, float> GetImageSize();
-
-  UnownedPtr<CPDF_Stream> m_pPDFStream;
-  UnownedPtr<CPDF_IconFit> m_pIconFit;
+  std::unique_ptr<CPDF_Icon> const m_pIcon;
+  UnownedPtr<CPDF_IconFit> const m_pIconFit;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_ICON_H_
diff --git a/fpdfsdk/pwl/cpwl_list_box.cpp b/fpdfsdk/pwl/cpwl_list_box.cpp
index a7c02e4..a0e2039 100644
--- a/fpdfsdk/pwl/cpwl_list_box.cpp
+++ b/fpdfsdk/pwl/cpwl_list_box.cpp
@@ -7,6 +7,7 @@
 #include "fpdfsdk/pwl/cpwl_list_box.h"
 
 #include <sstream>
+#include <utility>
 
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/pwl/cpwl_edit.h"
@@ -64,17 +65,13 @@
   m_pList->InvalidateRect(pRect);
 }
 
-CPWL_ListBox::CPWL_ListBox()
-    : m_pList(new CPWL_ListCtrl),
-      m_bMouseDown(false),
-      m_bHoverSel(false),
-      m_pFillerNotify(nullptr) {}
+CPWL_ListBox::CPWL_ListBox(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Wnd(cp, std::move(pAttachedData)),
+      m_pList(pdfium::MakeUnique<CPWL_ListCtrl>()) {}
 
-CPWL_ListBox::~CPWL_ListBox() {}
-
-ByteString CPWL_ListBox::GetClassName() const {
-  return "CPWL_ListBox";
-}
+CPWL_ListBox::~CPWL_ListBox() = default;
 
 void CPWL_ListBox::OnCreated() {
   m_pList->SetFontMap(GetFontMap());
@@ -83,7 +80,7 @@
 
   SetHoverSel(HasFlag(PLBS_HOVERSEL));
   m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL));
-  m_pList->SetFontSize(GetCreationParams().fFontSize);
+  m_pList->SetFontSize(GetCreationParams()->fFontSize);
 
   m_bHoverSel = HasFlag(PLBS_HOVERSEL);
 }
@@ -111,15 +108,13 @@
     CFX_PointF ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
     if (CPWL_EditImpl* pEdit = m_pList->GetItemEdit(i)) {
       CFX_FloatRect rcContent = pEdit->GetContentRect();
-      if (rcContent.Width() > rcClient.Width())
-        rcItem.Intersect(rcList);
-      else
-        rcItem.Intersect(rcClient);
+      rcItem.Intersect(rcContent.Width() > rcClient.Width() ? rcList
+                                                            : rcClient);
     }
 
+    IPWL_SystemHandler* pSysHandler = GetSystemHandler();
     if (m_pList->IsItemSelected(i)) {
-      CFX_SystemHandler* pSysHandler = GetSystemHandler();
-      if (pSysHandler && pSysHandler->IsSelectionImplemented()) {
+      if (pSysHandler->IsSelectionImplemented()) {
         CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
                                 GetTextColor().ToFXColor(255), rcList, ptOffset,
                                 nullptr, pSysHandler, m_pFormFiller.Get());
@@ -133,7 +128,6 @@
                                 m_pFormFiller.Get());
       }
     } else {
-      CFX_SystemHandler* pSysHandler = GetSystemHandler();
       CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
                               GetTextColor().ToFXColor(255), rcList, ptOffset,
                               nullptr, pSysHandler, nullptr);
@@ -246,10 +240,6 @@
   m_pList->SetScrollPos(CFX_PointF(0, pos));
 }
 
-void CPWL_ListBox::KillFocus() {
-  CPWL_Wnd::KillFocus();
-}
-
 bool CPWL_ListBox::RePosChildWnd() {
   if (!CPWL_Wnd::RePosChildWnd())
     return false;
@@ -262,7 +252,7 @@
   if (!m_pFillerNotify)
     return false;
 
-  CPWL_Wnd::ObservedPtr thisObserved(this);
+  ObservedPtr<CPWL_Wnd> thisObserved(this);
 
   WideString swChange = GetText();
   WideString strChangeEx;
@@ -294,7 +284,7 @@
   m_pList->AddString(str);
 }
 
-WideString CPWL_ListBox::GetText() const {
+WideString CPWL_ListBox::GetText() {
   return m_pList->GetText();
 }
 
@@ -310,6 +300,10 @@
   m_pList->Select(nItemIndex);
 }
 
+void CPWL_ListBox::Deselect(int32_t nItemIndex) {
+  m_pList->Deselect(nItemIndex);
+}
+
 void CPWL_ListBox::SetCaret(int32_t nItemIndex) {
   m_pList->SetCaret(nItemIndex);
 }
@@ -323,7 +317,7 @@
 }
 
 void CPWL_ListBox::ResetContent() {
-  m_pList->Empty();
+  m_pList->Clear();
 }
 
 void CPWL_ListBox::Reset() {
diff --git a/fpdfsdk/pwl/cpwl_list_box.h b/fpdfsdk/pwl/cpwl_list_box.h
index 9a725d1..ba3a653 100644
--- a/fpdfsdk/pwl/cpwl_list_box.h
+++ b/fpdfsdk/pwl/cpwl_list_box.h
@@ -38,11 +38,12 @@
 
 class CPWL_ListBox : public CPWL_Wnd {
  public:
-  CPWL_ListBox();
+  CPWL_ListBox(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_ListBox() override;
 
   // CPWL_Wnd
-  ByteString GetClassName() const override;
   void OnCreated() override;
   void OnDestroy() override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
@@ -55,7 +56,7 @@
   bool OnMouseWheel(short zDelta,
                     const CFX_PointF& point,
                     uint32_t nFlag) override;
-  void KillFocus() override;
+  WideString GetText() override;
   void SetScrollInfo(const PWL_SCROLL_INFO& info) override;
   void SetScrollPosition(float pos) override;
   void ScrollWindowVertically(float pos) override;
@@ -64,8 +65,6 @@
   void SetFontSize(float fFontSize) override;
   float GetFontSize() const override;
 
-  virtual WideString GetText() const;
-
   bool OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag);
 
   void AddString(const WideString& str);
@@ -74,6 +73,7 @@
   void ResetContent();
   void Reset();
   void Select(int32_t nItemIndex);
+  void Deselect(int32_t nItemIndex);
   void SetCaret(int32_t nItemIndex);
   void SetHoverSel(bool bHoverSel);
 
@@ -95,10 +95,10 @@
   void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; }
 
  protected:
+  bool m_bMouseDown = false;
+  bool m_bHoverSel = false;
   std::unique_ptr<CPWL_ListCtrl> m_pList;
   std::unique_ptr<CPWL_List_Notify> m_pListNotify;
-  bool m_bMouseDown;
-  bool m_bHoverSel;
   UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify;
 
  private:
diff --git a/fpdfsdk/pwl/cpwl_list_impl.cpp b/fpdfsdk/pwl/cpwl_list_impl.cpp
index da455d0..a7ceeb9 100644
--- a/fpdfsdk/pwl/cpwl_list_impl.cpp
+++ b/fpdfsdk/pwl/cpwl_list_impl.cpp
@@ -13,17 +13,15 @@
 #include "core/fxcrt/fx_extension.h"
 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
 #include "fpdfsdk/pwl/cpwl_list_box.h"
+#include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
-CPWL_ListCtrl::Item::Item()
-    : m_pEdit(new CPWL_EditImpl),
-      m_bSelected(false),
-      m_rcListItem(0.0f, 0.0f, 0.0f, 0.0f) {
+CPWL_ListCtrl::Item::Item() : m_pEdit(pdfium::MakeUnique<CPWL_EditImpl>()) {
   m_pEdit->SetAlignmentV(1, true);
   m_pEdit->Initialize();
 }
 
-CPWL_ListCtrl::Item::~Item() {}
+CPWL_ListCtrl::Item::~Item() = default;
 
 void CPWL_ListCtrl::Item::SetFontMap(IPVT_FontMap* pFontMap) {
   m_pEdit->SetFontMap(pFontMap);
@@ -110,7 +108,7 @@
       m_bMultiple(false) {}
 
 CPWL_ListCtrl::~CPWL_ListCtrl() {
-  Empty();
+  Clear();
 }
 
 CFX_PointF CPWL_ListCtrl::InToOut(const CFX_PointF& point) const {
@@ -301,7 +299,7 @@
 }
 
 CFX_FloatRect CPWL_ListCtrl::GetItemRectInternal(int32_t nIndex) const {
-  if (!pdfium::IndexInBounds(m_ListItems, nIndex) || !m_ListItems[nIndex])
+  if (!IsValid(nIndex))
     return CFX_FloatRect();
 
   CFX_FloatRect rcItem = m_ListItems[nIndex]->GetRect();
@@ -406,6 +404,16 @@
   }
 }
 
+void CPWL_ListCtrl::Deselect(int32_t nItemIndex) {
+  if (!IsItemSelected(nItemIndex))
+    return;
+
+  SetMultipleSelect(nItemIndex, false);
+
+  if (!IsMultipleSel())
+    m_nSelItem = -1;
+}
+
 bool CPWL_ListCtrl::IsItemVisible(int32_t nItemIndex) const {
   CFX_FloatRect rcPlate = m_rcPlate;
   CFX_FloatRect rcItem = GetItemRect(nItemIndex);
@@ -489,17 +497,14 @@
 
 void CPWL_ListCtrl::ReArrange(int32_t nItemIndex) {
   float fPosY = 0.0f;
-  if (pdfium::IndexInBounds(m_ListItems, nItemIndex - 1) &&
-      m_ListItems[nItemIndex - 1]) {
+  if (IsValid(nItemIndex - 1))
     fPosY = m_ListItems[nItemIndex - 1]->GetRect().bottom;
-  }
+
   for (const auto& pListItem : m_ListItems) {
-    if (pListItem) {
-      float fListItemHeight = pListItem->GetItemHeight();
-      pListItem->SetRect(
-          CFX_FloatRect(0.0f, fPosY + fListItemHeight, 0.0f, fPosY));
-      fPosY += fListItemHeight;
-    }
+    float fListItemHeight = pListItem->GetItemHeight();
+    pListItem->SetRect(
+        CFX_FloatRect(0.0f, fPosY + fListItemHeight, 0.0f, fPosY));
+    fPosY += fListItemHeight;
   }
   SetContentRect(CFX_FloatRect(0.0f, fPosY, 0.0f, 0.0f));
   SetScrollInfo();
@@ -520,7 +525,7 @@
   return nItemIndex;
 }
 
-void CPWL_ListCtrl::Empty() {
+void CPWL_ListCtrl::Clear() {
   m_ListItems.clear();
   InvalidateItem(-1);
 }
@@ -534,8 +539,6 @@
   bool bFirst = true;
   bool bLast = true;
   for (const auto& pListItem : m_ListItems) {
-    if (!pListItem)
-      continue;
     CFX_FloatRect rcListItem = pListItem->GetRect();
     if (IsFloatBigger(pt.y, rcListItem.top))
       bFirst = false;
@@ -547,7 +550,7 @@
   if (bFirst)
     return 0;
   if (bLast)
-    return pdfium::CollectionSize<int32_t>(m_ListItems) - 1;
+    return GetCount() - 1;
   return -1;
 }
 
@@ -566,7 +569,7 @@
 }
 
 CPWL_EditImpl* CPWL_ListCtrl::GetItemEdit(int32_t nIndex) const {
-  if (!pdfium::IndexInBounds(m_ListItems, nIndex) || !m_ListItems[nIndex])
+  if (!IsValid(nIndex))
     return nullptr;
   return m_ListItems[nIndex]->GetEdit();
 }
@@ -576,7 +579,7 @@
 }
 
 float CPWL_ListCtrl::GetFirstHeight() const {
-  if (m_ListItems.empty() || !m_ListItems.front())
+  if (m_ListItems.empty())
     return 1.0f;
   return m_ListItems.front()->GetItemHeight();
 }
@@ -584,7 +587,7 @@
 int32_t CPWL_ListCtrl::GetFirstSelected() const {
   int32_t i = 0;
   for (const auto& pListItem : m_ListItems) {
-    if (pListItem && pListItem->IsSelected())
+    if (pListItem->IsSelected())
       return i;
     ++i;
   }
@@ -593,7 +596,7 @@
 
 int32_t CPWL_ListCtrl::GetLastSelected() const {
   for (auto iter = m_ListItems.rbegin(); iter != m_ListItems.rend(); ++iter) {
-    if (*iter && (*iter)->IsSelected())
+    if ((*iter)->IsSelected())
       return &*iter - &m_ListItems.front();
   }
   return -1;
@@ -601,14 +604,14 @@
 
 int32_t CPWL_ListCtrl::FindNext(int32_t nIndex, wchar_t nChar) const {
   int32_t nCircleIndex = nIndex;
-  int32_t sz = pdfium::CollectionSize<int32_t>(m_ListItems);
+  int32_t sz = GetCount();
   for (int32_t i = 0; i < sz; i++) {
     nCircleIndex++;
     if (nCircleIndex >= sz)
       nCircleIndex = 0;
 
     if (Item* pListItem = m_ListItems[nCircleIndex].get()) {
-      if (FXSYS_toupper(pListItem->GetFirstChar()) == FXSYS_toupper(nChar))
+      if (FXSYS_towupper(pListItem->GetFirstChar()) == FXSYS_towupper(nChar))
         return nCircleIndex;
     }
   }
@@ -617,12 +620,11 @@
 }
 
 bool CPWL_ListCtrl::IsItemSelected(int32_t nIndex) const {
-  return pdfium::IndexInBounds(m_ListItems, nIndex) && m_ListItems[nIndex] &&
-         m_ListItems[nIndex]->IsSelected();
+  return IsValid(nIndex) && m_ListItems[nIndex]->IsSelected();
 }
 
 void CPWL_ListCtrl::SetItemSelect(int32_t nIndex, bool bSelected) {
-  if (pdfium::IndexInBounds(m_ListItems, nIndex) && m_ListItems[nIndex])
+  if (IsValid(nIndex))
     m_ListItems[nIndex]->SetSelect(bSelected);
 }
 
@@ -631,7 +633,7 @@
 }
 
 WideString CPWL_ListCtrl::GetItemText(int32_t nIndex) const {
-  if (pdfium::IndexInBounds(m_ListItems, nIndex) && m_ListItems[nIndex])
+  if (IsValid(nIndex))
     return m_ListItems[nIndex]->GetText();
-  return L"";
+  return WideString();
 }
diff --git a/fpdfsdk/pwl/cpwl_list_impl.h b/fpdfsdk/pwl/cpwl_list_impl.h
index e39a9c8..c7a86ab 100644
--- a/fpdfsdk/pwl/cpwl_list_impl.h
+++ b/fpdfsdk/pwl/cpwl_list_impl.h
@@ -20,13 +20,13 @@
 class CPWL_List_Notify;
 class IPVT_FontMap;
 
-class CPLST_Select {
+class CPLST_Select final {
  public:
   enum State { DESELECTING = -1, NORMAL = 0, SELECTING = 1 };
   using const_iterator = std::map<int32_t, State>::const_iterator;
 
   CPLST_Select();
-  virtual ~CPLST_Select();
+  ~CPLST_Select();
 
   void Add(int32_t nItemIndex);
   void Add(int32_t nBeginIndex, int32_t nEndIndex);
@@ -71,8 +71,9 @@
   void AddString(const WideString& str);
   void SetTopItem(int32_t nIndex);
   void Select(int32_t nItemIndex);
+  void Deselect(int32_t nItemIndex);
   void SetCaret(int32_t nItemIndex);
-  void Empty();
+  void Clear();
   void Cancel();
   WideString GetText() const;
 
@@ -114,9 +115,9 @@
    private:
     CPWL_EditImpl_Iterator* GetIterator() const;
 
-    std::unique_ptr<CPWL_EditImpl> m_pEdit;
-    bool m_bSelected;
+    bool m_bSelected = false;
     CFX_FloatRect m_rcListItem;
+    std::unique_ptr<CPWL_EditImpl> const m_pEdit;
   };
 
   CFX_PointF InToOut(const CFX_PointF& point) const;
@@ -144,7 +145,7 @@
   void SetScrollPosY(float fy);
   void AddItem(const WideString& str);
   WideString GetItemText(int32_t nIndex) const;
-  void SetItemSelect(int32_t nItemIndex, bool bSelected);
+  void SetItemSelect(int32_t nIndex, bool bSelected);
   int32_t GetLastSelected() const;
   CFX_PointF GetBTPoint() const {
     return CFX_PointF(m_rcPlate.left, m_rcPlate.top);
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.cpp b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
index 65a51e3..bf4d1da 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.cpp
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
@@ -8,11 +8,13 @@
 
 #include <algorithm>
 #include <sstream>
+#include <utility>
 #include <vector>
 
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -24,27 +26,14 @@
 
 #define PWL_DEFAULT_HEAVYGRAYCOLOR CFX_Color(CFX_Color::kGray, 0.50)
 
-PWL_FLOATRANGE::PWL_FLOATRANGE() {
-  Default();
-}
-
-PWL_FLOATRANGE::PWL_FLOATRANGE(float min, float max) {
-  Set(min, max);
-}
-
-void PWL_FLOATRANGE::Default() {
-  fMin = 0;
-  fMax = 0;
+void PWL_FLOATRANGE::Reset() {
+  fMin = 0.0f;
+  fMax = 0.0f;
 }
 
 void PWL_FLOATRANGE::Set(float min, float max) {
-  if (min > max) {
-    fMin = max;
-    fMax = min;
-  } else {
-    fMin = min;
-    fMax = max;
-  }
+  fMin = std::min(min, max);
+  fMax = std::max(min, max);
 }
 
 bool PWL_FLOATRANGE::In(float x) const {
@@ -61,7 +50,7 @@
 }
 
 void PWL_SCROLL_PRIVATEDATA::Default() {
-  ScrollRange.Default();
+  ScrollRange.Reset();
   fScrollPos = ScrollRange.fMin;
   fClientWidth = 0;
   fBigStep = 10;
@@ -117,23 +106,18 @@
     SetPos(ScrollRange.fMin);
 }
 
-CPWL_SBButton::CPWL_SBButton(PWL_SCROLLBAR_TYPE eScrollBarType,
-                             PWL_SBBUTTON_TYPE eButtonType) {
-  m_eScrollBarType = eScrollBarType;
-  m_eSBButtonType = eButtonType;
-
-  m_bMouseDown = false;
+CPWL_SBButton::CPWL_SBButton(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
+    PWL_SCROLLBAR_TYPE eScrollBarType,
+    PWL_SBBUTTON_TYPE eButtonType)
+    : CPWL_Wnd(cp, std::move(pAttachedData)),
+      m_eScrollBarType(eScrollBarType),
+      m_eSBButtonType(eButtonType) {
+  GetCreationParams()->eCursorType = FXCT_ARROW;
 }
 
-CPWL_SBButton::~CPWL_SBButton() {}
-
-ByteString CPWL_SBButton::GetClassName() const {
-  return "CPWL_SBButton";
-}
-
-void CPWL_SBButton::OnCreate(CreateParams* pParamsToAdjust) {
-  pParamsToAdjust->eCursorType = FXCT_ARROW;
-}
+CPWL_SBButton::~CPWL_SBButton() = default;
 
 void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
                                        const CFX_Matrix& mtUser2Device) {
@@ -187,15 +171,15 @@
   }
 
   // draw border
-  pDevice->DrawStrokeRect(&mtUser2Device, rectWnd,
+  pDevice->DrawStrokeRect(mtUser2Device, rectWnd,
                           ArgbEncode(nTransparency, 100, 100, 100), 0.0f);
-  pDevice->DrawStrokeRect(&mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
+  pDevice->DrawStrokeRect(mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
                           ArgbEncode(nTransparency, 255, 255, 255), 1.0f);
 
   if (m_eSBButtonType != PSBT_POS) {
     // draw background
     if (IsEnabled()) {
-      pDevice->DrawShadow(&mtUser2Device, true, false,
+      pDevice->DrawShadow(mtUser2Device, true, false,
                           rectWnd.GetDeflated(1.0f, 1.0f), nTransparency, 80,
                           220);
     } else {
@@ -208,24 +192,21 @@
       float fX = rectWnd.left + 1.5f;
       float fY = rectWnd.bottom;
       std::vector<CFX_PointF> pts;
-      if (m_eSBButtonType == PSBT_MIN) {
-        pts.push_back(CFX_PointF(fX + 2.5f, fY + 4.0f));
-        pts.push_back(CFX_PointF(fX + 2.5f, fY + 3.0f));
-        pts.push_back(CFX_PointF(fX + 4.5f, fY + 5.0f));
-        pts.push_back(CFX_PointF(fX + 6.5f, fY + 3.0f));
-        pts.push_back(CFX_PointF(fX + 6.5f, fY + 4.0f));
-        pts.push_back(CFX_PointF(fX + 4.5f, fY + 6.0f));
-        pts.push_back(CFX_PointF(fX + 2.5f, fY + 4.0f));
-      } else {
-        pts.push_back(CFX_PointF(fX + 2.5f, fY + 5.0f));
-        pts.push_back(CFX_PointF(fX + 2.5f, fY + 6.0f));
-        pts.push_back(CFX_PointF(fX + 4.5f, fY + 4.0f));
-        pts.push_back(CFX_PointF(fX + 6.5f, fY + 6.0f));
-        pts.push_back(CFX_PointF(fX + 6.5f, fY + 5.0f));
-        pts.push_back(CFX_PointF(fX + 4.5f, fY + 3.0f));
-        pts.push_back(CFX_PointF(fX + 2.5f, fY + 5.0f));
-      }
-      pDevice->DrawFillArea(&mtUser2Device, pts.data(), 7,
+      static constexpr float kOffsetsX[] = {2.5f, 2.5f, 4.5f, 6.5f,
+                                            6.5f, 4.5f, 2.5f};
+      static constexpr float kOffsetsY[] = {5.0f, 6.0f, 4.0f, 6.0f,
+                                            5.0f, 3.0f, 5.0f};
+      static constexpr float kOffsetsMinY[] = {4.0f, 3.0f, 5.0f, 3.0f,
+                                               4.0f, 6.0f, 4.0f};
+      static_assert(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsY),
+                    "Wrong offset count");
+      static_assert(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsMinY),
+                    "Wrong offset count");
+      const float* pOffsetsY =
+          m_eSBButtonType == PSBT_MIN ? kOffsetsMinY : kOffsetsY;
+      for (size_t i = 0; i < FX_ArraySize(kOffsetsX); ++i)
+        pts.push_back(CFX_PointF(fX + kOffsetsX[i], fY + pOffsetsY[i]));
+      pDevice->DrawFillArea(mtUser2Device, pts,
                             IsEnabled()
                                 ? ArgbEncode(nTransparency, 255, 255, 255)
                                 : PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255));
@@ -318,24 +299,15 @@
   return true;
 }
 
-CPWL_ScrollBar::CPWL_ScrollBar(PWL_SCROLLBAR_TYPE sbType)
-    : m_sbType(sbType),
-      m_pMinButton(nullptr),
-      m_pMaxButton(nullptr),
-      m_pPosButton(nullptr),
-      m_bMouseDown(false),
-      m_bMinOrMax(false),
-      m_bNotifyForever(true) {}
-
-CPWL_ScrollBar::~CPWL_ScrollBar() {}
-
-ByteString CPWL_ScrollBar::GetClassName() const {
-  return "CPWL_ScrollBar";
+CPWL_ScrollBar::CPWL_ScrollBar(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
+    PWL_SCROLLBAR_TYPE sbType)
+    : CPWL_Wnd(cp, std::move(pAttachedData)), m_sbType(sbType) {
+  GetCreationParams()->eCursorType = FXCT_ARROW;
 }
 
-void CPWL_ScrollBar::OnCreate(CreateParams* pParamsToAdjust) {
-  pParamsToAdjust->eCursorType = FXCT_ARROW;
-}
+CPWL_ScrollBar::~CPWL_ScrollBar() = default;
 
 void CPWL_ScrollBar::OnDestroy() {
   // Until cleanup takes place in the virtual destructor for CPWL_Wnd
@@ -401,20 +373,17 @@
       break;
   }
 
-  ObservedPtr thisObserved(this);
-
+  ObservedPtr<CPWL_ScrollBar> thisObserved(this);
   if (m_pMinButton) {
     m_pMinButton->Move(rcMinButton, true, false);
     if (!thisObserved)
       return false;
   }
-
   if (m_pMaxButton) {
     m_pMaxButton->Move(rcMaxButton, true, false);
     if (!thisObserved)
       return false;
   }
-
   if (!MovePosButton(false))
     return false;
 
@@ -506,9 +475,8 @@
     }
   }
 
-  EndTimer();
+  m_pTimer.reset();
   m_bMouseDown = false;
-
   return true;
 }
 
@@ -564,30 +532,36 @@
 
 void CPWL_ScrollBar::CreateButtons(const CreateParams& cp) {
   CreateParams scp = cp;
-  scp.pParentWnd = this;
   scp.dwBorderWidth = 2;
   scp.nBorderStyle = BorderStyle::BEVELED;
-
   scp.dwFlags =
       PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP;
 
   if (!m_pMinButton) {
-    m_pMinButton = new CPWL_SBButton(m_sbType, PSBT_MIN);
-    m_pMinButton->Create(scp);
+    auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
+                                                     m_sbType, PSBT_MIN);
+    m_pMinButton = pButton.get();
+    AddChild(std::move(pButton));
+    m_pMinButton->Realize();
   }
 
   if (!m_pMaxButton) {
-    m_pMaxButton = new CPWL_SBButton(m_sbType, PSBT_MAX);
-    m_pMaxButton->Create(scp);
+    auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
+                                                     m_sbType, PSBT_MAX);
+    m_pMaxButton = pButton.get();
+    AddChild(std::move(pButton));
+    m_pMaxButton->Realize();
   }
 
   if (!m_pPosButton) {
-    m_pPosButton = new CPWL_SBButton(m_sbType, PSBT_POS);
-
-    ObservedPtr thisObserved(this);
-    if (!m_pPosButton->SetVisible(false) || !thisObserved)
-      return;
-    m_pPosButton->Create(scp);
+    auto pButton = pdfium::MakeUnique<CPWL_SBButton>(scp, CloneAttachedData(),
+                                                     m_sbType, PSBT_POS);
+    m_pPosButton = pButton.get();
+    ObservedPtr<CPWL_ScrollBar> thisObserved(this);
+    if (m_pPosButton->SetVisible(false) && thisObserved) {
+      AddChild(std::move(pButton));
+      m_pPosButton->Realize();
+    }
   }
 }
 
@@ -604,11 +578,10 @@
   if (!m_pPosButton)
     return;
 
+  ObservedPtr<CPWL_ScrollBar> thisObserved(this);
   m_sData.SetScrollRange(fMin, fMax);
   m_sData.SetClientWidth(fClientWidth);
 
-  ObservedPtr thisObserved(this);
-
   if (IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) {
     m_pPosButton->SetVisible(false);
     // Note, |this| may no longer be viable at this point. If more work needs
@@ -687,8 +660,7 @@
         break;
     }
 
-    ObservedPtr thisObserved(this);
-
+    ObservedPtr<CPWL_ScrollBar> thisObserved(this);
     m_pPosButton->Move(rcPosButton, true, bRefresh);
     if (!thisObserved)
       return false;
@@ -701,12 +673,10 @@
   m_sData.SubSmall();
   if (!MovePosButton(true))
     return;
+
   NotifyScrollWindow();
-
   m_bMinOrMax = true;
-
-  EndTimer();
-  BeginTimer(100);
+  m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
 }
 
 void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {}
@@ -717,12 +687,10 @@
   m_sData.AddSmall();
   if (!MovePosButton(true))
     return;
+
   NotifyScrollWindow();
-
   m_bMinOrMax = false;
-
-  EndTimer();
-  BeginTimer(100);
+  m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
 }
 
 void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {}
@@ -833,10 +801,10 @@
   CFX_FloatRect rcMin = m_pMinButton->GetWindowRect();
   CFX_FloatRect rcMax = m_pMaxButton->GetWindowRect();
 
-  float fMinWidth = rcMin.right - rcMin.left;
-  float fMinHeight = rcMin.top - rcMin.bottom;
-  float fMaxWidth = rcMax.right - rcMax.left;
-  float fMaxHeight = rcMax.top - rcMax.bottom;
+  float fMinWidth = rcMin.Width();
+  float fMinHeight = rcMin.Height();
+  float fMaxWidth = rcMax.Width();
+  float fMaxHeight = rcMax.Height();
 
   switch (m_sbType) {
     case SBT_HSCROLL:
@@ -915,16 +883,18 @@
   CreateButtons(cp);
 }
 
-void CPWL_ScrollBar::TimerProc() {
+void CPWL_ScrollBar::OnTimerFired() {
   PWL_SCROLL_PRIVATEDATA sTemp = m_sData;
   if (m_bMinOrMax)
     m_sData.SubSmall();
   else
     m_sData.AddSmall();
 
-  if (sTemp != m_sData) {
-    if (!MovePosButton(true))
-      return;
-    NotifyScrollWindow();
-  }
+  if (sTemp == m_sData)
+    return;
+
+  if (!MovePosButton(true))
+    return;
+
+  NotifyScrollWindow();
 }
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.h b/fpdfsdk/pwl/cpwl_scroll_bar.h
index 6921732..f6bb2b9 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.h
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.h
@@ -7,12 +7,12 @@
 #ifndef FPDFSDK_PWL_CPWL_SCROLL_BAR_H_
 #define FPDFSDK_PWL_CPWL_SCROLL_BAR_H_
 
+#include <memory>
+
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-class CPWL_SBButton;
-class CPWL_ScrollBar;
-
 struct PWL_SCROLL_INFO {
  public:
   PWL_SCROLL_INFO()
@@ -42,45 +42,44 @@
 
 enum PWL_SBBUTTON_TYPE { PSBT_MIN, PSBT_MAX, PSBT_POS };
 
-class CPWL_SBButton : public CPWL_Wnd {
+class CPWL_SBButton final : public CPWL_Wnd {
  public:
-  CPWL_SBButton(PWL_SCROLLBAR_TYPE eScrollBarType,
-                PWL_SBBUTTON_TYPE eButtonType);
+  CPWL_SBButton(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
+      PWL_SCROLLBAR_TYPE eScrollBarType,
+      PWL_SBBUTTON_TYPE eButtonType);
   ~CPWL_SBButton() override;
 
   // CPWL_Wnd
-  ByteString GetClassName() const override;
-  void OnCreate(CreateParams* pParamsToAdjust) override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
   bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override;
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
   bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override;
 
- protected:
+ private:
   PWL_SCROLLBAR_TYPE m_eScrollBarType;
   PWL_SBBUTTON_TYPE m_eSBButtonType;
-
-  bool m_bMouseDown;
+  bool m_bMouseDown = false;
 };
 
 struct PWL_FLOATRANGE {
  public:
-  PWL_FLOATRANGE();
-  PWL_FLOATRANGE(float min, float max);
+  PWL_FLOATRANGE() = default;
 
   bool operator==(const PWL_FLOATRANGE& that) const {
     return fMin == that.fMin && fMax == that.fMax;
   }
   bool operator!=(const PWL_FLOATRANGE& that) const { return !(*this == that); }
 
-  void Default();
+  void Reset();
   void Set(float min, float max);
   bool In(float x) const;
   float GetWidth() const;
 
-  float fMin;
-  float fMax;
+  float fMin = 0.0f;
+  float fMax = 0.0f;
 };
 
 struct PWL_SCROLL_PRIVATEDATA {
@@ -115,14 +114,15 @@
   float fSmallStep;
 };
 
-class CPWL_ScrollBar : public CPWL_Wnd {
+class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface {
  public:
-  explicit CPWL_ScrollBar(PWL_SCROLLBAR_TYPE sbType = SBT_HSCROLL);
+  CPWL_ScrollBar(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData,
+      PWL_SCROLLBAR_TYPE sbType);
   ~CPWL_ScrollBar() override;
 
   // CPWL_Wnd:
-  ByteString GetClassName() const override;
-  void OnCreate(CreateParams* pParamsToAdjust) override;
   void OnDestroy() override;
   bool RePosChildWnd() override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
@@ -135,14 +135,16 @@
   void NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) override;
   void NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) override;
   void CreateChildWnd(const CreateParams& cp) override;
-  void TimerProc() override;
+
+  // CFX_Timer::CallbackIface:
+  void OnTimerFired() override;
 
   float GetScrollBarWidth() const;
   PWL_SCROLLBAR_TYPE GetScrollBarType() const { return m_sbType; }
 
   void SetNotifyForever(bool bForever) { m_bNotifyForever = bForever; }
 
- protected:
+ private:
   void SetScrollRange(float fMin, float fMax, float fClientWidth);
   void SetScrollPos(float fPos);
 
@@ -152,7 +154,6 @@
   void NotifyScrollWindow();
   CFX_FloatRect GetScrollArea() const;
 
- private:
   void CreateButtons(const CreateParams& cp);
 
   void OnMinButtonLBDown(const CFX_PointF& point);
@@ -175,12 +176,13 @@
   UnownedPtr<CPWL_SBButton> m_pMinButton;
   UnownedPtr<CPWL_SBButton> m_pMaxButton;
   UnownedPtr<CPWL_SBButton> m_pPosButton;
+  std::unique_ptr<CFX_Timer> m_pTimer;
   PWL_SCROLL_PRIVATEDATA m_sData;
-  bool m_bMouseDown;
-  bool m_bMinOrMax;
-  bool m_bNotifyForever;
-  float m_nOldPos;
-  float m_fOldPosButton;
+  bool m_bMouseDown = false;
+  bool m_bMinOrMax = false;
+  bool m_bNotifyForever = true;
+  float m_nOldPos = 0.0f;
+  float m_fOldPosButton = 0.0f;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_SCROLL_BAR_H_
diff --git a/fpdfsdk/pwl/cpwl_special_button.cpp b/fpdfsdk/pwl/cpwl_special_button.cpp
index ddca67e..3fbf919 100644
--- a/fpdfsdk/pwl/cpwl_special_button.cpp
+++ b/fpdfsdk/pwl/cpwl_special_button.cpp
@@ -5,37 +5,30 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "fpdfsdk/pwl/cpwl_special_button.h"
+
+#include <utility>
+
 #include "fpdfsdk/pwl/cpwl_button.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-CPWL_PushButton::CPWL_PushButton() {}
+CPWL_PushButton::CPWL_PushButton(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Button(cp, std::move(pAttachedData)) {}
 
-CPWL_PushButton::~CPWL_PushButton() {}
-
-ByteString CPWL_PushButton::GetClassName() const {
-  return "CPWL_PushButton";
-}
+CPWL_PushButton::~CPWL_PushButton() = default;
 
 CFX_FloatRect CPWL_PushButton::GetFocusRect() const {
   return GetWindowRect().GetDeflated(static_cast<float>(GetBorderWidth()),
                                      static_cast<float>(GetBorderWidth()));
 }
 
-CPWL_CheckBox::CPWL_CheckBox() : m_bChecked(false) {}
+CPWL_CheckBox::CPWL_CheckBox(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Button(cp, std::move(pAttachedData)) {}
 
-CPWL_CheckBox::~CPWL_CheckBox() {}
-
-ByteString CPWL_CheckBox::GetClassName() const {
-  return "CPWL_CheckBox";
-}
-
-void CPWL_CheckBox::SetCheck(bool bCheck) {
-  m_bChecked = bCheck;
-}
-
-bool CPWL_CheckBox::IsChecked() const {
-  return m_bChecked;
-}
+CPWL_CheckBox::~CPWL_CheckBox() = default;
 
 bool CPWL_CheckBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
   if (IsReadOnly())
@@ -50,13 +43,12 @@
   return true;
 }
 
-CPWL_RadioButton::CPWL_RadioButton() : m_bChecked(false) {}
+CPWL_RadioButton::CPWL_RadioButton(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : CPWL_Button(cp, std::move(pAttachedData)) {}
 
-CPWL_RadioButton::~CPWL_RadioButton() {}
-
-ByteString CPWL_RadioButton::GetClassName() const {
-  return "CPWL_RadioButton";
-}
+CPWL_RadioButton::~CPWL_RadioButton() = default;
 
 bool CPWL_RadioButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
   if (IsReadOnly())
@@ -66,14 +58,6 @@
   return true;
 }
 
-void CPWL_RadioButton::SetCheck(bool bCheck) {
-  m_bChecked = bCheck;
-}
-
-bool CPWL_RadioButton::IsChecked() const {
-  return m_bChecked;
-}
-
 bool CPWL_RadioButton::OnChar(uint16_t nChar, uint32_t nFlag) {
   SetCheck(true);
   return true;
diff --git a/fpdfsdk/pwl/cpwl_special_button.h b/fpdfsdk/pwl/cpwl_special_button.h
index 076b529..3d9fa25 100644
--- a/fpdfsdk/pwl/cpwl_special_button.h
+++ b/fpdfsdk/pwl/cpwl_special_button.h
@@ -7,50 +7,55 @@
 #ifndef FPDFSDK_PWL_CPWL_SPECIAL_BUTTON_H_
 #define FPDFSDK_PWL_CPWL_SPECIAL_BUTTON_H_
 
+#include <memory>
+
 #include "fpdfsdk/pwl/cpwl_button.h"
 
-class CPWL_PushButton : public CPWL_Button {
+class CPWL_PushButton final : public CPWL_Button {
  public:
-  CPWL_PushButton();
+  CPWL_PushButton(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_PushButton() override;
 
-  // CPWL_Button
-  ByteString GetClassName() const override;
+  // CPWL_Button:
   CFX_FloatRect GetFocusRect() const override;
 };
 
-class CPWL_CheckBox : public CPWL_Button {
+class CPWL_CheckBox final : public CPWL_Button {
  public:
-  CPWL_CheckBox();
+  CPWL_CheckBox(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_CheckBox() override;
 
-  // CPWL_Button
-  ByteString GetClassName() const override;
+  // CPWL_Button:
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
   bool OnChar(uint16_t nChar, uint32_t nFlag) override;
 
-  void SetCheck(bool bCheck);
-  bool IsChecked() const;
+  bool IsChecked() const { return m_bChecked; }
+  void SetCheck(bool bCheck) { m_bChecked = bCheck; }
 
  private:
-  bool m_bChecked;
+  bool m_bChecked = false;
 };
 
-class CPWL_RadioButton : public CPWL_Button {
+class CPWL_RadioButton final : public CPWL_Button {
  public:
-  CPWL_RadioButton();
+  CPWL_RadioButton(
+      const CreateParams& cp,
+      std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
   ~CPWL_RadioButton() override;
 
   // CPWL_Button
-  ByteString GetClassName() const override;
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override;
   bool OnChar(uint16_t nChar, uint32_t nFlag) override;
 
-  void SetCheck(bool bCheck);
-  bool IsChecked() const;
+  bool IsChecked() const { return m_bChecked; }
+  void SetCheck(bool bCheck) { m_bChecked = bCheck; }
 
  private:
-  bool m_bChecked;
+  bool m_bChecked = false;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_SPECIAL_BUTTON_H_
diff --git a/fpdfsdk/pwl/cpwl_timer.cpp b/fpdfsdk/pwl/cpwl_timer.cpp
deleted file mode 100644
index 34f81fc..0000000
--- a/fpdfsdk/pwl/cpwl_timer.cpp
+++ /dev/null
@@ -1,62 +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 "fpdfsdk/pwl/cpwl_timer.h"
-
-#include <map>
-
-#include "fpdfsdk/cfx_systemhandler.h"
-#include "fpdfsdk/pwl/cpwl_timer_handler.h"
-
-namespace {
-
-std::map<int32_t, CPWL_Timer*>& GetPWLTimeMap() {
-  // Leak the object at shutdown.
-  static auto* timeMap = new std::map<int32_t, CPWL_Timer*>;
-  return *timeMap;
-}
-
-}  // namespace
-
-CPWL_Timer::CPWL_Timer(CPWL_TimerHandler* pAttached,
-                       CFX_SystemHandler* pSystemHandler)
-    : m_nTimerID(0), m_pAttached(pAttached), m_pSystemHandler(pSystemHandler) {
-  ASSERT(m_pAttached);
-  ASSERT(m_pSystemHandler);
-}
-
-CPWL_Timer::~CPWL_Timer() {
-  KillPWLTimer();
-}
-
-int32_t CPWL_Timer::SetPWLTimer(int32_t nElapse) {
-  if (m_nTimerID != 0)
-    KillPWLTimer();
-  m_nTimerID = m_pSystemHandler->SetTimer(nElapse, TimerProc);
-
-  GetPWLTimeMap()[m_nTimerID] = this;
-  return m_nTimerID;
-}
-
-void CPWL_Timer::KillPWLTimer() {
-  if (m_nTimerID == 0)
-    return;
-
-  m_pSystemHandler->KillTimer(m_nTimerID);
-  GetPWLTimeMap().erase(m_nTimerID);
-  m_nTimerID = 0;
-}
-
-// static
-void CPWL_Timer::TimerProc(int32_t idEvent) {
-  auto it = GetPWLTimeMap().find(idEvent);
-  if (it == GetPWLTimeMap().end())
-    return;
-
-  CPWL_Timer* pTimer = it->second;
-  if (pTimer->m_pAttached)
-    pTimer->m_pAttached->TimerProc();
-}
diff --git a/fpdfsdk/pwl/cpwl_timer.h b/fpdfsdk/pwl/cpwl_timer.h
deleted file mode 100644
index 6e082ab..0000000
--- a/fpdfsdk/pwl/cpwl_timer.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FPDFSDK_PWL_CPWL_TIMER_H_
-#define FPDFSDK_PWL_CPWL_TIMER_H_
-
-#include "core/fxcrt/unowned_ptr.h"
-
-class CFX_SystemHandler;
-class CPWL_TimerHandler;
-
-class CPWL_Timer {
- public:
-  CPWL_Timer(CPWL_TimerHandler* pAttached, CFX_SystemHandler* pSystemHandler);
-  virtual ~CPWL_Timer();
-
-  static void TimerProc(int32_t idEvent);
-
-  int32_t SetPWLTimer(int32_t nElapse);
-  void KillPWLTimer();
-
- private:
-  int32_t m_nTimerID;
-  UnownedPtr<CPWL_TimerHandler> m_pAttached;
-  UnownedPtr<CFX_SystemHandler> m_pSystemHandler;
-};
-
-#endif  // FPDFSDK_PWL_CPWL_TIMER_H_
diff --git a/fpdfsdk/pwl/cpwl_timer_handler.cpp b/fpdfsdk/pwl/cpwl_timer_handler.cpp
deleted file mode 100644
index 33af306..0000000
--- a/fpdfsdk/pwl/cpwl_timer_handler.cpp
+++ /dev/null
@@ -1,27 +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 "fpdfsdk/pwl/cpwl_timer_handler.h"
-
-#include "fpdfsdk/pwl/cpwl_timer.h"
-#include "third_party/base/ptr_util.h"
-
-CPWL_TimerHandler::CPWL_TimerHandler() {}
-
-CPWL_TimerHandler::~CPWL_TimerHandler() {}
-
-void CPWL_TimerHandler::BeginTimer(int32_t nElapse) {
-  if (!m_pTimer)
-    m_pTimer = pdfium::MakeUnique<CPWL_Timer>(this, GetSystemHandler());
-  m_pTimer->SetPWLTimer(nElapse);
-}
-
-void CPWL_TimerHandler::EndTimer() {
-  if (m_pTimer)
-    m_pTimer->KillPWLTimer();
-}
-
-void CPWL_TimerHandler::TimerProc() {}
diff --git a/fpdfsdk/pwl/cpwl_timer_handler.h b/fpdfsdk/pwl/cpwl_timer_handler.h
deleted file mode 100644
index f208e1c..0000000
--- a/fpdfsdk/pwl/cpwl_timer_handler.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 FPDFSDK_PWL_CPWL_TIMER_HANDLER_H_
-#define FPDFSDK_PWL_CPWL_TIMER_HANDLER_H_
-
-#include <memory>
-
-class CFX_SystemHandler;
-class CPWL_Timer;
-
-class CPWL_TimerHandler {
- public:
-  CPWL_TimerHandler();
-  virtual ~CPWL_TimerHandler();
-
-  virtual void TimerProc();
-  virtual CFX_SystemHandler* GetSystemHandler() const = 0;
-
-  void BeginTimer(int32_t nElapse);
-  void EndTimer();
-
- private:
-  std::unique_ptr<CPWL_Timer> m_pTimer;
-};
-
-#endif  // FPDFSDK_PWL_CPWL_TIMER_HANDLER_H_
diff --git a/fpdfsdk/pwl/cpwl_wnd.cpp b/fpdfsdk/pwl/cpwl_wnd.cpp
index 368db74..0ba0e56 100644
--- a/fpdfsdk/pwl/cpwl_wnd.cpp
+++ b/fpdfsdk/pwl/cpwl_wnd.cpp
@@ -8,10 +8,12 @@
 
 #include <map>
 #include <sstream>
+#include <utility>
 #include <vector>
 
 #include "core/fxge/cfx_renderdevice.h"
 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
+#include "public/fpdf_fwlevent.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
@@ -22,31 +24,13 @@
 }  // namespace
 
 CPWL_Wnd::CreateParams::CreateParams()
-    : rcRectWnd(0, 0, 0, 0),
-      pSystemHandler(nullptr),
-      pFontMap(nullptr),
-      pProvider(nullptr),
-      pFocusHandler(nullptr),
-      dwFlags(0),
-      sBackgroundColor(),
-      pAttachedWidget(nullptr),
-      nBorderStyle(BorderStyle::SOLID),
-      dwBorderWidth(1),
-      sBorderColor(),
-      sTextColor(),
-      nTransparency(255),
-      fFontSize(kDefaultFontSize),
-      sDash(3, 0, 0),
-      pAttachedData(nullptr),
-      pParentWnd(nullptr),
-      pMsgControl(nullptr),
-      eCursorType(FXCT_ARROW) {}
+    : fFontSize(kDefaultFontSize), sDash(3, 0, 0) {}
 
 CPWL_Wnd::CreateParams::CreateParams(const CreateParams& other) = default;
 
 CPWL_Wnd::CreateParams::~CreateParams() = default;
 
-class CPWL_MsgControl : public Observable<CPWL_MsgControl> {
+class CPWL_MsgControl final : public Observable {
  public:
   explicit CPWL_MsgControl(CPWL_Wnd* pWnd) : m_pCreatedWnd(pWnd) {}
   ~CPWL_MsgControl() {}
@@ -83,7 +67,7 @@
   }
 
   void KillFocus() {
-    ObservedPtr observed_ptr(this);
+    ObservedPtr<CPWL_MsgControl> observed_ptr(this);
     if (!m_aKeyboardPath.empty())
       if (CPWL_Wnd* pWnd = m_aKeyboardPath[0])
         pWnd->OnKillFocus();
@@ -105,9 +89,7 @@
     }
   }
 
-  void ReleaseCapture() {
-    m_aMousePath.clear();
-  }
+  void ReleaseCapture() { m_aMousePath.clear(); }
 
   CPWL_Wnd* GetFocusedWindow() const { return m_pMainKeyboardWnd.Get(); }
 
@@ -118,28 +100,33 @@
   UnownedPtr<CPWL_Wnd> m_pMainKeyboardWnd;
 };
 
-CPWL_Wnd::CPWL_Wnd()
-    : m_rcWindow(),
-      m_rcClip(),
-      m_bCreated(false),
-      m_bVisible(false),
-      m_bNotifying(false),
-      m_bEnabled(true) {}
+// static
+bool CPWL_Wnd::IsSHIFTKeyDown(uint32_t nFlag) {
+  return !!(nFlag & FWL_EVENTFLAG_ShiftKey);
+}
+
+// static
+bool CPWL_Wnd::IsCTRLKeyDown(uint32_t nFlag) {
+  return !!(nFlag & FWL_EVENTFLAG_ControlKey);
+}
+
+// static
+bool CPWL_Wnd::IsALTKeyDown(uint32_t nFlag) {
+  return !!(nFlag & FWL_EVENTFLAG_AltKey);
+}
+
+CPWL_Wnd::CPWL_Wnd(
+    const CreateParams& cp,
+    std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)
+    : m_CreationParams(cp), m_pAttachedData(std::move(pAttachedData)) {}
 
 CPWL_Wnd::~CPWL_Wnd() {
   ASSERT(!m_bCreated);
 }
 
-ByteString CPWL_Wnd::GetClassName() const {
-  return "CPWL_Wnd";
-}
+void CPWL_Wnd::Realize() {
+  ASSERT(!m_bCreated);
 
-void CPWL_Wnd::Create(const CreateParams& cp) {
-  if (IsValid())
-    return;
-
-  m_CreationParams = cp;
-  OnCreate(&m_CreationParams);
   m_CreationParams.rcRectWnd.Normalize();
   m_rcWindow = m_CreationParams.rcRectWnd;
   m_rcClip = m_rcWindow;
@@ -148,8 +135,6 @@
     m_rcClip.Normalize();
   }
   CreateMsgControl();
-  if (m_CreationParams.pParentWnd)
-    m_CreationParams.pParentWnd->AddChild(this);
 
   CreateParams ccp = m_CreationParams;
   ccp.dwFlags &= 0xFFFF0000L;  // remove sub styles
@@ -163,8 +148,6 @@
   m_bCreated = true;
 }
 
-void CPWL_Wnd::OnCreate(CreateParams* pParamsToAdjust) {}
-
 void CPWL_Wnd::OnCreated() {}
 
 void CPWL_Wnd::OnDestroy() {}
@@ -184,21 +167,16 @@
   OnDestroy();
   if (m_bCreated) {
     m_pVScrollBar = nullptr;
-    for (auto it = m_Children.rbegin(); it != m_Children.rend(); ++it) {
-      CPWL_Wnd* pChild = *it;
-      if (pChild) {
-        *it = nullptr;
-        pChild->Destroy();
-        delete pChild;
-      }
+    while (!m_Children.empty()) {
+      std::unique_ptr<CPWL_Wnd> pChild = std::move(m_Children.back());
+      m_Children.pop_back();
+      pChild->Destroy();
     }
-    if (m_CreationParams.pParentWnd)
-      m_CreationParams.pParentWnd->RemoveChild(this);
-
+    if (m_pParent)
+      m_pParent->RemoveChild(this);
     m_bCreated = false;
   }
   DestroyMsgControl();
-  m_Children.clear();
 }
 
 bool CPWL_Wnd::Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh) {
@@ -262,48 +240,34 @@
 
 void CPWL_Wnd::DrawChildAppearance(CFX_RenderDevice* pDevice,
                                    const CFX_Matrix& mtUser2Device) {
-  for (CPWL_Wnd* pChild : m_Children) {
-    if (!pChild)
-      continue;
-
+  for (const auto& pChild : m_Children) {
     CFX_Matrix mt = pChild->GetChildMatrix();
     if (mt.IsIdentity()) {
       pChild->DrawAppearance(pDevice, mtUser2Device);
-    } else {
-      mt.Concat(mtUser2Device);
-      pChild->DrawAppearance(pDevice, mt);
+      continue;
     }
+    mt.Concat(mtUser2Device);
+    pChild->DrawAppearance(pDevice, mt);
   }
 }
 
 bool CPWL_Wnd::InvalidateRect(CFX_FloatRect* pRect) {
-  ObservedPtr thisObserved(this);
   if (!IsValid())
     return true;
 
+  ObservedPtr<CPWL_Wnd> thisObserved(this);
   CFX_FloatRect rcRefresh = pRect ? *pRect : GetWindowRect();
-
   if (!HasFlag(PWS_NOREFRESHCLIP)) {
     CFX_FloatRect rcClip = GetClipRect();
-    if (!rcClip.IsEmpty()) {
+    if (!rcClip.IsEmpty())
       rcRefresh.Intersect(rcClip);
-    }
   }
 
   CFX_FloatRect rcWin = PWLtoWnd(rcRefresh);
   rcWin.Inflate(1, 1);
   rcWin.Normalize();
-
-  if (CFX_SystemHandler* pSH = GetSystemHandler()) {
-    if (CPDFSDK_Widget* widget = static_cast<CPDFSDK_Widget*>(
-            m_CreationParams.pAttachedWidget.Get())) {
-      pSH->InvalidateRect(widget, rcWin);
-      if (!thisObserved)
-        return false;
-    }
-  }
-
-  return true;
+  GetSystemHandler()->InvalidateRect(m_pAttachedData.get(), rcWin);
+  return !!thisObserved;
 }
 
 #define PWL_IMPLEMENT_KEY_METHOD(key_method_name)                  \
@@ -312,8 +276,8 @@
       return false;                                                \
     if (!IsWndCaptureKeyboard(this))                               \
       return false;                                                \
-    for (auto* pChild : m_Children) {                              \
-      if (pChild && IsWndCaptureKeyboard(pChild))                  \
+    for (const auto& pChild : m_Children) {                        \
+      if (IsWndCaptureKeyboard(pChild.get()))                      \
         return pChild->key_method_name(nChar, nFlag);              \
     }                                                              \
     return false;                                                  \
@@ -328,8 +292,8 @@
     if (!IsValid() || !IsVisible() || !IsEnabled())                            \
       return false;                                                            \
     if (IsWndCaptureMouse(this)) {                                             \
-      for (auto* pChild : m_Children) {                                        \
-        if (pChild && IsWndCaptureMouse(pChild)) {                             \
+      for (const auto& pChild : m_Children) {                                  \
+        if (IsWndCaptureMouse(pChild.get())) {                                 \
           return pChild->mouse_method_name(pChild->ParentToChild(point),       \
                                            nFlag);                             \
         }                                                                      \
@@ -337,8 +301,8 @@
       SetCursor();                                                             \
       return false;                                                            \
     }                                                                          \
-    for (auto* pChild : m_Children) {                                          \
-      if (pChild && pChild->WndHitTest(pChild->ParentToChild(point))) {        \
+    for (const auto& pChild : m_Children) {                                    \
+      if (pChild->WndHitTest(pChild->ParentToChild(point))) {                  \
         return pChild->mouse_method_name(pChild->ParentToChild(point), nFlag); \
       }                                                                        \
     }                                                                          \
@@ -350,17 +314,44 @@
 PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDblClk)
 PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDown)
 PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonUp)
-PWL_IMPLEMENT_MOUSE_METHOD(OnRButtonDown)
-PWL_IMPLEMENT_MOUSE_METHOD(OnRButtonUp)
 PWL_IMPLEMENT_MOUSE_METHOD(OnMouseMove)
 #undef PWL_IMPLEMENT_MOUSE_METHOD
 
+// Unlike their FWL counterparts, PWL windows don't handle right clicks.
+bool CPWL_Wnd::OnRButtonDown(const CFX_PointF& point, uint32_t nFlag) {
+  return false;
+}
+
+bool CPWL_Wnd::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) {
+  return false;
+}
+
+WideString CPWL_Wnd::GetText() {
+  return WideString();
+}
+
 WideString CPWL_Wnd::GetSelectedText() {
   return WideString();
 }
 
 void CPWL_Wnd::ReplaceSelection(const WideString& text) {}
 
+bool CPWL_Wnd::CanUndo() {
+  return false;
+}
+
+bool CPWL_Wnd::CanRedo() {
+  return false;
+}
+
+bool CPWL_Wnd::Undo() {
+  return false;
+}
+
+bool CPWL_Wnd::Redo() {
+  return false;
+}
+
 bool CPWL_Wnd::OnMouseWheel(short zDelta,
                             const CFX_PointF& point,
                             uint32_t nFlag) {
@@ -371,24 +362,29 @@
   if (!IsWndCaptureKeyboard(this))
     return false;
 
-  for (auto* pChild : m_Children) {
-    if (pChild && IsWndCaptureKeyboard(pChild))
+  for (const auto& pChild : m_Children) {
+    if (IsWndCaptureKeyboard(pChild.get()))
       return pChild->OnMouseWheel(zDelta, pChild->ParentToChild(point), nFlag);
   }
   return false;
 }
 
-void CPWL_Wnd::AddChild(CPWL_Wnd* pWnd) {
-  m_Children.push_back(pWnd);
+void CPWL_Wnd::AddChild(std::unique_ptr<CPWL_Wnd> pWnd) {
+  ASSERT(!pWnd->m_pParent);
+  pWnd->m_pParent = this;
+  m_Children.push_back(std::move(pWnd));
 }
 
 void CPWL_Wnd::RemoveChild(CPWL_Wnd* pWnd) {
-  for (auto it = m_Children.rbegin(); it != m_Children.rend(); ++it) {
-    if (*it && *it == pWnd) {
-      m_Children.erase(std::next(it).base());
-      break;
-    }
-  }
+  ASSERT(pWnd->m_pParent == this);
+  auto it = std::find(m_Children.begin(), m_Children.end(),
+                      pdfium::FakeUniquePtr<CPWL_Wnd>(pWnd));
+  if (it == m_Children.end())
+    return;
+
+  // TODO(tsepez): murky ownership.
+  it->release();
+  m_Children.erase(it);
 }
 
 void CPWL_Wnd::SetScrollInfo(const PWL_SCROLL_INFO& info) {}
@@ -403,10 +399,6 @@
 
 void CPWL_Wnd::NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) {}
 
-CPWL_Wnd* CPWL_Wnd::GetParentWindow() const {
-  return m_CreationParams.pParentWnd;
-}
-
 CFX_FloatRect CPWL_Wnd::GetWindowRect() const {
   return m_rcWindow;
 }
@@ -478,10 +470,6 @@
   return m_CreationParams.sDash;
 }
 
-CPWL_Wnd::PrivateData* CPWL_Wnd::GetAttachedData() const {
-  return m_CreationParams.pAttachedData.Get();
-}
-
 CPWL_ScrollBar* CPWL_Wnd::GetVScrollBar() const {
   return HasFlag(PWS_VSCROLL) ? m_pVScrollBar.Get() : nullptr;
 }
@@ -495,18 +483,17 @@
     return;
 
   CreateParams scp = cp;
-
-  // flags
   scp.dwFlags =
       PWS_CHILD | PWS_BACKGROUND | PWS_AUTOTRANSPARENT | PWS_NOREFRESHCLIP;
-
-  scp.pParentWnd = this;
   scp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR;
   scp.eCursorType = FXCT_ARROW;
   scp.nTransparency = PWL_SCROLLBAR_TRANSPARENCY;
 
-  m_pVScrollBar = new CPWL_ScrollBar(SBT_VSCROLL);
-  m_pVScrollBar->Create(scp);
+  auto pBar =
+      pdfium::MakeUnique<CPWL_ScrollBar>(scp, CloneAttachedData(), SBT_VSCROLL);
+  m_pVScrollBar = pBar.get();
+  AddChild(std::move(pBar));
+  m_pVScrollBar->Realize();
 }
 
 void CPWL_Wnd::SetCapture() {
@@ -515,10 +502,9 @@
 }
 
 void CPWL_Wnd::ReleaseCapture() {
-  for (auto* pChild : m_Children) {
-    if (pChild)
-      pChild->ReleaseCapture();
-  }
+  for (const auto& pChild : m_Children)
+    pChild->ReleaseCapture();
+
   if (CPWL_MsgControl* pMsgCtrl = GetMsgControl())
     pMsgCtrl->ReleaseCapture();
 }
@@ -542,6 +528,11 @@
 
 void CPWL_Wnd::OnKillFocus() {}
 
+std::unique_ptr<IPWL_SystemHandler::PerWindowData> CPWL_Wnd::CloneAttachedData()
+    const {
+  return m_pAttachedData ? m_pAttachedData->Clone() : nullptr;
+}
+
 bool CPWL_Wnd::WndHitTest(const CFX_PointF& point) const {
   return IsValid() && IsVisible() && GetWindowRect().Contains(point);
 }
@@ -550,23 +541,15 @@
   return IsValid() && IsVisible() && GetClientRect().Contains(point);
 }
 
-const CPWL_Wnd* CPWL_Wnd::GetRootWnd() const {
-  auto* pParent = m_CreationParams.pParentWnd;
-  return pParent ? pParent->GetRootWnd() : this;
-}
-
 bool CPWL_Wnd::SetVisible(bool bVisible) {
   if (!IsValid())
     return true;
 
-  ObservedPtr thisObserved(this);
-
-  for (auto* pChild : m_Children) {
-    if (pChild) {
-      pChild->SetVisible(bVisible);
-      if (!thisObserved)
-        return false;
-    }
+  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  for (const auto& pChild : m_Children) {
+    pChild->SetVisible(bVisible);
+    if (!thisObserved)
+      return false;
   }
 
   if (bVisible != m_bVisible) {
@@ -608,8 +591,7 @@
       CFX_FloatRect(rcContent.right - PWL_SCROLLBAR_WIDTH, rcContent.bottom,
                     rcContent.right - 1.0f, rcContent.top);
 
-  ObservedPtr thisObserved(this);
-
+  ObservedPtr<CPWL_Wnd> thisObserved(this);
   pVSB->Move(rcVScroll, true, false);
   if (!thisObserved)
     return false;
@@ -620,12 +602,8 @@
 void CPWL_Wnd::CreateChildWnd(const CreateParams& cp) {}
 
 void CPWL_Wnd::SetCursor() {
-  if (IsValid()) {
-    if (CFX_SystemHandler* pSH = GetSystemHandler()) {
-      int32_t nCursorType = GetCreationParams().eCursorType;
-      pSH->SetCursor(nCursorType);
-    }
-  }
+  if (IsValid())
+    GetSystemHandler()->SetCursor(GetCreationParams()->eCursorType);
 }
 
 void CPWL_Wnd::CreateMsgControl() {
@@ -649,17 +627,17 @@
 
 bool CPWL_Wnd::IsWndCaptureMouse(const CPWL_Wnd* pWnd) const {
   CPWL_MsgControl* pCtrl = GetMsgControl();
-  return pCtrl ? pCtrl->IsWndCaptureMouse(pWnd) : false;
+  return pCtrl && pCtrl->IsWndCaptureMouse(pWnd);
 }
 
 bool CPWL_Wnd::IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const {
   CPWL_MsgControl* pCtrl = GetMsgControl();
-  return pCtrl ? pCtrl->IsWndCaptureKeyboard(pWnd) : false;
+  return pCtrl && pCtrl->IsWndCaptureKeyboard(pWnd);
 }
 
 bool CPWL_Wnd::IsFocused() const {
   CPWL_MsgControl* pCtrl = GetMsgControl();
-  return pCtrl ? pCtrl->IsMainCaptureKeyboard(this) : false;
+  return pCtrl && pCtrl->IsMainCaptureKeyboard(this);
 }
 
 CFX_FloatRect CPWL_Wnd::GetFocusRect() const {
@@ -679,22 +657,6 @@
   m_CreationParams.fFontSize = fFontSize;
 }
 
-CFX_SystemHandler* CPWL_Wnd::GetSystemHandler() const {
-  return m_CreationParams.pSystemHandler;
-}
-
-CPWL_Wnd::FocusHandlerIface* CPWL_Wnd::GetFocusHandler() const {
-  return m_CreationParams.pFocusHandler.Get();
-}
-
-CPWL_Wnd::ProviderIface* CPWL_Wnd::GetProvider() const {
-  return m_CreationParams.pProvider.Get();
-}
-
-IPVT_FontMap* CPWL_Wnd::GetFontMap() const {
-  return m_CreationParams.pFontMap;
-}
-
 CFX_Color CPWL_Wnd::GetBorderLeftTopColor(BorderStyle nBorderStyle) const {
   switch (nBorderStyle) {
     case BorderStyle::BEVELED:
@@ -722,10 +684,9 @@
 }
 
 void CPWL_Wnd::SetTransparency(int32_t nTransparency) {
-  for (auto* pChild : m_Children) {
-    if (pChild)
-      pChild->SetTransparency(nTransparency);
-  }
+  for (const auto& pChild : m_Children)
+    pChild->SetTransparency(nTransparency);
+
   m_CreationParams.nTransparency = nTransparency;
 }
 
@@ -793,9 +754,8 @@
   if (m_bEnabled == bEnable)
     return;
 
-  for (auto* pChild : m_Children) {
-    if (pChild)
-      pChild->EnableWindow(bEnable);
-  }
+  for (const auto& pChild : m_Children)
+    pChild->EnableWindow(bEnable);
+
   m_bEnabled = bEnable;
 }
diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h
index ba52420..6e8f073 100644
--- a/fpdfsdk/pwl/cpwl_wnd.h
+++ b/fpdfsdk/pwl/cpwl_wnd.h
@@ -10,19 +10,16 @@
 #include <memory>
 #include <vector>
 
-#include "core/fpdfdoc/cpdf_formcontrol.h"
-#include "core/fxcrt/observable.h"
+#include "core/fxcrt/cfx_timer.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_color.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_widget.h"
-#include "fpdfsdk/pwl/cpwl_timer.h"
-#include "fpdfsdk/pwl/cpwl_timer_handler.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "fpdfsdk/pwl/ipwl_systemhandler.h"
 
 class CPWL_Edit;
 class CPWL_MsgControl;
 class CPWL_ScrollBar;
-class CFX_SystemHandler;
 class IPVT_FontMap;
 struct PWL_SCROLL_INFO;
 
@@ -63,6 +60,14 @@
 // combobox styles
 #define PCBS_ALLOWCUSTOMTEXT 0x0001L
 
+// Cursor style. These must match the values in public/fpdf_formfill.h
+#define FXCT_ARROW 0
+#define FXCT_NESW 1
+#define FXCT_NWSE 2
+#define FXCT_VBEAM 3
+#define FXCT_HBEAM 4
+#define FXCT_HAND 5
+
 struct CPWL_Dash {
   CPWL_Dash() : nDash(0), nGap(0), nPhase(0) {}
   CPWL_Dash(int32_t dash, int32_t gap, int32_t phase)
@@ -79,43 +84,25 @@
   int32_t nPhase;
 };
 
-inline bool operator==(const CFX_Color& c1, const CFX_Color& c2) {
-  return c1.nColorType == c2.nColorType && c1.fColor1 - c2.fColor1 < 0.0001 &&
-         c1.fColor1 - c2.fColor1 > -0.0001 &&
-         c1.fColor2 - c2.fColor2 < 0.0001 &&
-         c1.fColor2 - c2.fColor2 > -0.0001 &&
-         c1.fColor3 - c2.fColor3 < 0.0001 &&
-         c1.fColor3 - c2.fColor3 > -0.0001 &&
-         c1.fColor4 - c2.fColor4 < 0.0001 && c1.fColor4 - c2.fColor4 > -0.0001;
-}
-
-inline bool operator!=(const CFX_Color& c1, const CFX_Color& c2) {
-  return !(c1 == c2);
-}
-
 #define PWL_SCROLLBAR_WIDTH 12.0f
 #define PWL_SCROLLBAR_TRANSPARENCY 150
 #define PWL_DEFAULT_BLACKCOLOR CFX_Color(CFX_Color::kGray, 0)
 #define PWL_DEFAULT_WHITECOLOR CFX_Color(CFX_Color::kGray, 1)
 
-class CPWL_Wnd : public CPWL_TimerHandler, public Observable<CPWL_Wnd> {
+class CPWL_Wnd : public Observable {
  public:
-  class PrivateData {
-   protected:
-    ~PrivateData() {}
-  };
-
-  class ProviderIface : public Observable<ProviderIface> {
+  class ProviderIface : public Observable {
    public:
-    virtual ~ProviderIface() {}
+    virtual ~ProviderIface() = default;
 
     // get a matrix which map user space to CWnd client space
-    virtual CFX_Matrix GetWindowMatrix(PrivateData* pAttached) = 0;
+    virtual CFX_Matrix GetWindowMatrix(
+        const IPWL_SystemHandler::PerWindowData* pAttached) = 0;
   };
 
   class FocusHandlerIface {
    public:
-    virtual ~FocusHandlerIface() {}
+    virtual ~FocusHandlerIface() = default;
     virtual void OnSetFocus(CPWL_Edit* pEdit) = 0;
   };
 
@@ -125,32 +112,33 @@
     CreateParams(const CreateParams& other);
     ~CreateParams();
 
-    CFX_FloatRect rcRectWnd;                          // required
-    CFX_SystemHandler* pSystemHandler;                // required
-    IPVT_FontMap* pFontMap;                           // required
-    ProviderIface::ObservedPtr pProvider;             // required
-    UnownedPtr<FocusHandlerIface> pFocusHandler;      // optional
-    uint32_t dwFlags;                                 // optional
-    CFX_Color sBackgroundColor;                       // optional
-    CPDFSDK_Widget::ObservedPtr pAttachedWidget;      // required
-    BorderStyle nBorderStyle;                         // optional
-    int32_t dwBorderWidth;                            // optional
-    CFX_Color sBorderColor;                           // optional
-    CFX_Color sTextColor;                             // optional
-    int32_t nTransparency;                            // optional
-    float fFontSize;                                  // optional
-    CPWL_Dash sDash;                                  // optional
-    UnownedPtr<PrivateData> pAttachedData;            // optional
-    CPWL_Wnd* pParentWnd;                             // ignore
-    CPWL_MsgControl* pMsgControl;                     // ignore
-    int32_t eCursorType;                              // ignore
-    CFX_Matrix mtChild;                               // ignore
+    CFX_FloatRect rcRectWnd;                        // required
+    UnownedPtr<TimerHandlerIface> pTimerHandler;    // required
+    UnownedPtr<IPWL_SystemHandler> pSystemHandler;  // required
+    UnownedPtr<IPVT_FontMap> pFontMap;              // required
+    ObservedPtr<ProviderIface> pProvider;           // required
+    UnownedPtr<FocusHandlerIface> pFocusHandler;    // optional
+    uint32_t dwFlags = 0;                           // optional
+    CFX_Color sBackgroundColor;                     // optional
+    BorderStyle nBorderStyle = BorderStyle::SOLID;  // optional
+    int32_t dwBorderWidth = 1;                      // optional
+    CFX_Color sBorderColor;                         // optional
+    CFX_Color sTextColor;                           // optional
+    int32_t nTransparency = 255;                    // optional
+    float fFontSize;                                // optional
+    CPWL_Dash sDash;                                // optional
+    CPWL_MsgControl* pMsgControl = nullptr;         // ignore
+    int32_t eCursorType = FXCT_ARROW;               // ignore
+    CFX_Matrix mtChild;                             // ignore
   };
 
-  CPWL_Wnd();
-  ~CPWL_Wnd() override;
+  static bool IsSHIFTKeyDown(uint32_t nFlag);
+  static bool IsCTRLKeyDown(uint32_t nFlag);
+  static bool IsALTKeyDown(uint32_t nFlag);
 
-  virtual ByteString GetClassName() const;
+  CPWL_Wnd(const CreateParams& cp,
+           std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
+  virtual ~CPWL_Wnd();
 
   // Returns |true| iff this instance is still allocated.
   virtual bool InvalidateRect(CFX_FloatRect* pRect);
@@ -181,20 +169,28 @@
   virtual void SetFontSize(float fFontSize);
   virtual float GetFontSize() const;
 
+  virtual WideString GetText();
   virtual WideString GetSelectedText();
   virtual void ReplaceSelection(const WideString& text);
+
+  virtual bool CanUndo();
+  virtual bool CanRedo();
+  virtual bool Undo();
+  virtual bool Redo();
+
   virtual CFX_FloatRect GetFocusRect() const;
   virtual CFX_FloatRect GetClientRect() const;
 
-  void InvalidateFocusHandler(FocusHandlerIface* handler);
-  void InvalidateProvider(ProviderIface* provider);
-  void Create(const CreateParams& cp);
+  void AddChild(std::unique_ptr<CPWL_Wnd> pWnd);
+  void RemoveChild(CPWL_Wnd* pWnd);
+  void Realize();
   void Destroy();
   bool Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh);
 
+  void InvalidateFocusHandler(FocusHandlerIface* handler);
+  void InvalidateProvider(ProviderIface* provider);
   void SetCapture();
   void ReleaseCapture();
-
   void DrawAppearance(CFX_RenderDevice* pDevice,
                       const CFX_Matrix& mtUser2Device);
 
@@ -206,7 +202,7 @@
   CFX_Color GetBorderLeftTopColor(BorderStyle nBorderStyle) const;
   CFX_Color GetBorderRightBottomColor(BorderStyle nBorderStyle) const;
 
-  void SetBorderStyle(BorderStyle eBorderStyle);
+  void SetBorderStyle(BorderStyle nBorderStyle);
   BorderStyle GetBorderStyle() const;
   const CPWL_Dash& GetBorderDash() const;
 
@@ -223,8 +219,11 @@
   void SetClipRect(const CFX_FloatRect& rect);
   const CFX_FloatRect& GetClipRect() const;
 
-  CPWL_Wnd* GetParentWindow() const;
-  PrivateData* GetAttachedData() const;
+  CPWL_Wnd* GetParentWindow() const { return m_pParent.Get(); }
+  const IPWL_SystemHandler::PerWindowData* GetAttachedData() const {
+    return m_pAttachedData.get();
+  }
+  std::unique_ptr<IPWL_SystemHandler::PerWindowData> CloneAttachedData() const;
 
   bool WndHitTest(const CFX_PointF& point) const;
   bool ClientHitTest(const CFX_PointF& point) const;
@@ -237,9 +236,13 @@
   bool IsReadOnly() const;
   CPWL_ScrollBar* GetVScrollBar() const;
 
-  IPVT_FontMap* GetFontMap() const;
-  ProviderIface* GetProvider() const;
-  FocusHandlerIface* GetFocusHandler() const;
+  IPVT_FontMap* GetFontMap() const { return m_CreationParams.pFontMap.Get(); }
+  ProviderIface* GetProvider() const {
+    return m_CreationParams.pProvider.Get();
+  }
+  FocusHandlerIface* GetFocusHandler() const {
+    return m_CreationParams.pFocusHandler.Get();
+  }
 
   int32_t GetTransparency();
   void SetTransparency(int32_t nTransparency);
@@ -253,9 +256,6 @@
   virtual void OnKillFocus();
 
  protected:
-  // CPWL_TimerHandler
-  CFX_SystemHandler* GetSystemHandler() const override;
-
   virtual void CreateChildWnd(const CreateParams& cp);
 
   // Returns |true| iff this instance is still allocated.
@@ -264,14 +264,18 @@
   virtual void DrawThisAppearance(CFX_RenderDevice* pDevice,
                                   const CFX_Matrix& mtUser2Device);
 
-  virtual void OnCreate(CreateParams* pParamsToAdjust);
   virtual void OnCreated();
   virtual void OnDestroy();
 
-  void SetNotifyFlag(bool bNotifying = true) { m_bNotifying = bNotifying; }
   bool IsNotifying() const { return m_bNotifying; }
   bool IsValid() const { return m_bCreated; }
-  const CreateParams& GetCreationParams() const { return m_CreationParams; }
+  CreateParams* GetCreationParams() { return &m_CreationParams; }
+  TimerHandlerIface* GetTimerHandler() const {
+    return m_CreationParams.pTimerHandler.Get();
+  }
+  IPWL_SystemHandler* GetSystemHandler() const {
+    return m_CreationParams.pSystemHandler.Get();
+  }
 
   // Returns |true| iff this instance is still allocated.
   bool InvalidateRectMove(const CFX_FloatRect& rcOld,
@@ -279,16 +283,15 @@
 
   bool IsWndCaptureMouse(const CPWL_Wnd* pWnd) const;
   bool IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const;
-  const CPWL_Wnd* GetRootWnd() const;
 
   static bool IsCTRLpressed(uint32_t nFlag) {
-    return CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag);
+    return CPWL_Wnd::IsCTRLKeyDown(nFlag);
   }
   static bool IsSHIFTpressed(uint32_t nFlag) {
-    return CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag);
+    return CPWL_Wnd::IsSHIFTKeyDown(nFlag);
   }
   static bool IsALTpressed(uint32_t nFlag) {
-    return CPDFSDK_FormFillEnvironment::IsALTKeyDown(nFlag);
+    return CPWL_Wnd::IsALTKeyDown(nFlag);
   }
 
  private:
@@ -300,9 +303,6 @@
 
   CFX_FloatRect PWLtoWnd(const CFX_FloatRect& rect) const;
 
-  void AddChild(CPWL_Wnd* pWnd);
-  void RemoveChild(CPWL_Wnd* pWnd);
-
   void CreateScrollBar(const CreateParams& cp);
   void CreateVScrollBar(const CreateParams& cp);
 
@@ -313,14 +313,16 @@
   CPWL_MsgControl* GetMsgControl() const;
 
   CreateParams m_CreationParams;
-  std::vector<CPWL_Wnd*> m_Children;
+  std::unique_ptr<IPWL_SystemHandler::PerWindowData> m_pAttachedData;
+  UnownedPtr<CPWL_Wnd> m_pParent;
+  std::vector<std::unique_ptr<CPWL_Wnd>> m_Children;
   UnownedPtr<CPWL_ScrollBar> m_pVScrollBar;
   CFX_FloatRect m_rcWindow;
   CFX_FloatRect m_rcClip;
-  bool m_bCreated;
-  bool m_bVisible;
-  bool m_bNotifying;
-  bool m_bEnabled;
+  bool m_bCreated = false;
+  bool m_bVisible = false;
+  bool m_bNotifying = false;
+  bool m_bEnabled = true;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_WND_H_
diff --git a/fpdfsdk/pwl/ipwl_systemhandler.h b/fpdfsdk/pwl/ipwl_systemhandler.h
new file mode 100644
index 0000000..8a14d8e
--- /dev/null
+++ b/fpdfsdk/pwl/ipwl_systemhandler.h
@@ -0,0 +1,35 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_
+#define FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_system.h"
+
+class CFFL_FormFiller;
+
+class IPWL_SystemHandler {
+ public:
+  class PerWindowData {
+   public:
+    virtual ~PerWindowData() = default;
+    virtual std::unique_ptr<PerWindowData> Clone() const = 0;
+  };
+
+  virtual ~IPWL_SystemHandler() = default;
+
+  virtual void InvalidateRect(PerWindowData* pWidgetData,
+                              const CFX_FloatRect& rect) = 0;
+  virtual void OutputSelectedRect(CFFL_FormFiller* pFormFiller,
+                                  const CFX_FloatRect& rect) = 0;
+  virtual bool IsSelectionImplemented() const = 0;
+  virtual void SetCursor(int32_t nCursorType) = 0;
+};
+
+#endif  // FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_
diff --git a/fxbarcode/BC_Library.cpp b/fxbarcode/BC_Library.cpp
index 5d658ef..393f329 100644
--- a/fxbarcode/BC_Library.cpp
+++ b/fxbarcode/BC_Library.cpp
@@ -8,10 +8,8 @@
 
 #include <stdint.h>
 
-#include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
-#include "fxbarcode/datamatrix/BC_ErrorCorrection.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h"
+#include "fxbarcode/qrcode/BC_QRCoderEncoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
 #include "fxbarcode/qrcode/BC_QRCoderMode.h"
 #include "fxbarcode/qrcode/BC_QRCoderVersion.h"
@@ -20,18 +18,14 @@
   CBC_QRCoderErrorCorrectionLevel::Initialize();
   CBC_QRCoderMode::Initialize();
   CBC_QRCoderVersion::Initialize();
-  CBC_ReedSolomonGF256::Initialize();
+  CBC_QRCoderEncoder::Initialize();
   CBC_SymbolInfo::Initialize();
-  CBC_ErrorCorrection::Initialize();
-  CBC_PDF417HighLevelEncoder::Initialize();
 }
 
 void BC_Library_Destroy() {
   CBC_QRCoderErrorCorrectionLevel::Finalize();
   CBC_QRCoderMode::Finalize();
   CBC_QRCoderVersion::Finalize();
-  CBC_ReedSolomonGF256::Finalize();
+  CBC_QRCoderEncoder::Finalize();
   CBC_SymbolInfo::Finalize();
-  CBC_ErrorCorrection::Finalize();
-  CBC_PDF417HighLevelEncoder::Finalize();
 }
diff --git a/fxbarcode/BC_Library.h b/fxbarcode/BC_Library.h
index 4e98743..5f1af4d 100644
--- a/fxbarcode/BC_Library.h
+++ b/fxbarcode/BC_Library.h
@@ -7,7 +7,9 @@
 #ifndef FXBARCODE_BC_LIBRARY_H_
 #define FXBARCODE_BC_LIBRARY_H_
 
-enum BC_TEXT_LOC {
+#include <stdint.h>
+
+enum BC_TEXT_LOC : uint8_t {
   BC_TEXT_LOC_NONE = 0,
   BC_TEXT_LOC_ABOVE,
   BC_TEXT_LOC_BELOW,
@@ -15,9 +17,12 @@
   BC_TEXT_LOC_BELOWEMBED
 };
 
-enum BC_CHAR_ENCODING { CHAR_ENCODING_UTF8 = 0, CHAR_ENCODING_UNICODE };
+enum BC_CHAR_ENCODING : uint8_t {
+  CHAR_ENCODING_UTF8 = 0,
+  CHAR_ENCODING_UNICODE
+};
 
-enum BC_TYPE {
+enum BC_TYPE : int8_t {
   BC_UNKNOWN = -1,
   BC_CODE39 = 0,
   BC_CODABAR,
@@ -29,7 +34,8 @@
   BC_EAN13,
   BC_QR_CODE,
   BC_PDF417,
-  BC_DATAMATRIX
+  BC_DATAMATRIX,
+  BC_LAST = BC_DATAMATRIX
 };
 
 void BC_Library_Init();
diff --git a/fxbarcode/BC_TwoDimWriter.cpp b/fxbarcode/BC_TwoDimWriter.cpp
index 6852fee..b88574c 100644
--- a/fxbarcode/BC_TwoDimWriter.cpp
+++ b/fxbarcode/BC_TwoDimWriter.cpp
@@ -4,109 +4,124 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include "fxbarcode/BC_TwoDimWriter.h"
+
 #include <algorithm>
 
+#include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "fxbarcode/BC_TwoDimWriter.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "third_party/base/numerics/safe_math.h"
 #include "third_party/base/ptr_util.h"
 
-CBC_TwoDimWriter::CBC_TwoDimWriter() : m_iCorrectLevel(1), m_bFixedSize(true) {}
+CBC_TwoDimWriter::CBC_TwoDimWriter(bool bFixedSize)
+    : m_bFixedSize(bFixedSize) {}
 
-CBC_TwoDimWriter::~CBC_TwoDimWriter() {}
+CBC_TwoDimWriter::~CBC_TwoDimWriter() = default;
 
-void CBC_TwoDimWriter::RenderDeviceResult(CFX_RenderDevice* device,
-                                          const CFX_Matrix* matrix) {
-  CFX_GraphStateData stateData;
-  CFX_PathData path;
-  path.AppendRect(0, 0, (float)m_Width, (float)m_Height);
-  device->DrawPath(&path, matrix, &stateData, m_backgroundColor,
-                   m_backgroundColor, FXFILL_ALTERNATE);
-  int32_t leftPos = 0;
-  int32_t topPos = 0;
-  if (m_bFixedSize) {
-    leftPos = (m_Width - m_output->GetWidth()) / 2;
-    topPos = (m_Height - m_output->GetHeight()) / 2;
-  }
-  CFX_Matrix matri = *matrix;
-  if (m_Width < m_output->GetWidth() && m_Height < m_output->GetHeight()) {
-    CFX_Matrix matriScale((float)m_Width / (float)m_output->GetWidth(), 0.0,
-                          0.0, (float)m_Height / (float)m_output->GetHeight(),
-                          0.0, 0.0);
-    matriScale.Concat(*matrix);
-    matri = matriScale;
-  }
-  for (int32_t x = 0; x < m_output->GetWidth(); x++) {
-    for (int32_t y = 0; y < m_output->GetHeight(); y++) {
-      CFX_PathData rect;
-      rect.AppendRect((float)leftPos + x, (float)topPos + y,
-                      (float)(leftPos + x + 1), (float)(topPos + y + 1));
-      if (m_output->Get(x, y)) {
-        CFX_GraphStateData data;
-        device->DrawPath(&rect, &matri, &data, m_barColor, 0, FXFILL_WINDING);
-      }
-    }
-  }
-}
-
-int32_t CBC_TwoDimWriter::GetErrorCorrectionLevel() const {
-  return m_iCorrectLevel;
-}
-
-bool CBC_TwoDimWriter::RenderResult(uint8_t* code,
+bool CBC_TwoDimWriter::RenderResult(pdfium::span<const uint8_t> code,
                                     int32_t codeWidth,
                                     int32_t codeHeight) {
-  int32_t inputWidth = codeWidth;
-  int32_t inputHeight = codeHeight;
-  int32_t tempWidth = inputWidth + 2;
-  int32_t tempHeight = inputHeight + 2;
+  if (code.empty())
+    return false;
+
+  m_inputWidth = codeWidth;
+  m_inputHeight = codeHeight;
+  int32_t tempWidth = m_inputWidth + 2;
+  int32_t tempHeight = m_inputHeight + 2;
   float moduleHSize = std::min(m_ModuleWidth, m_ModuleHeight);
   moduleHSize = std::min(moduleHSize, 8.0f);
   moduleHSize = std::max(moduleHSize, 1.0f);
-  pdfium::base::CheckedNumeric<int32_t> scaledWidth = tempWidth;
-  pdfium::base::CheckedNumeric<int32_t> scaledHeight = tempHeight;
+  FX_SAFE_INT32 scaledWidth = tempWidth;
+  FX_SAFE_INT32 scaledHeight = tempHeight;
   scaledWidth *= moduleHSize;
   scaledHeight *= moduleHSize;
+  m_outputWidth = scaledWidth.ValueOrDie();
+  m_outputHeight = scaledHeight.ValueOrDie();
 
-  int32_t outputWidth = scaledWidth.ValueOrDie();
-  int32_t outputHeight = scaledHeight.ValueOrDie();
   if (m_bFixedSize) {
-    if (m_Width < outputWidth || m_Height < outputHeight) {
+    if (m_Width < m_outputWidth || m_Height < m_outputHeight) {
       return false;
     }
   } else {
-    if (m_Width > outputWidth || m_Height > outputHeight) {
-      outputWidth =
-          (int32_t)(outputWidth * ceil((float)m_Width / (float)outputWidth));
-      outputHeight =
-          (int32_t)(outputHeight * ceil((float)m_Height / (float)outputHeight));
+    if (m_Width > m_outputWidth || m_Height > m_outputHeight) {
+      int32_t width_factor = static_cast<int32_t>(
+          floor(static_cast<float>(m_Width) / m_outputWidth));
+      int32_t height_factor = static_cast<int32_t>(
+          floor(static_cast<float>(m_Height) / m_outputHeight));
+      width_factor = std::max(width_factor, 1);
+      height_factor = std::max(height_factor, 1);
+
+      m_outputWidth *= width_factor;
+      m_outputHeight *= height_factor;
     }
   }
-  int32_t multiX = (int32_t)ceil((float)outputWidth / (float)tempWidth);
-  int32_t multiY = (int32_t)ceil((float)outputHeight / (float)tempHeight);
+  m_multiX =
+      static_cast<int32_t>(ceil(static_cast<float>(m_outputWidth) / tempWidth));
+  m_multiY = static_cast<int32_t>(
+      ceil(static_cast<float>(m_outputHeight) / tempHeight));
   if (m_bFixedSize) {
-    multiX = std::min(multiX, multiY);
-    multiY = multiX;
+    m_multiX = std::min(m_multiX, m_multiY);
+    m_multiY = m_multiX;
   }
-  int32_t leftPadding = std::max((outputWidth - (inputWidth * multiX)) / 2, 0);
-  int32_t topPadding = std::max((outputHeight - (inputHeight * multiY)) / 2, 0);
+
+  m_leftPadding = std::max((m_Width - m_outputWidth) / 2, 0);
+  m_topPadding = std::max((m_Height - m_outputHeight) / 2, 0);
+
   m_output = pdfium::MakeUnique<CBC_CommonBitMatrix>();
-  m_output->Init(outputWidth, outputHeight);
-  for (int32_t inputY = 0, outputY = topPadding;
-       (inputY < inputHeight) && (outputY < outputHeight - multiY);
-       inputY++, outputY += multiY) {
-    for (int32_t inputX = 0, outputX = leftPadding;
-         (inputX < inputWidth) && (outputX < outputWidth - multiX);
-         inputX++, outputX += multiX) {
-      if (code[inputX + inputY * inputWidth] == 1 &&
-          !m_output->SetRegion(outputX, outputY, multiX, multiY)) {
-        return false;
-      }
+  m_output->Init(m_inputWidth, m_inputHeight);
+
+  for (int32_t y = 0; y < m_inputHeight; ++y) {
+    for (int32_t x = 0; x < m_inputWidth; ++x) {
+      if (code[x + y * m_inputWidth] == 1)
+        m_output->Set(x, y);
     }
   }
   return true;
 }
+
+void CBC_TwoDimWriter::RenderDeviceResult(CFX_RenderDevice* device,
+                                          const CFX_Matrix* matrix) {
+  ASSERT(m_output);
+
+  CFX_GraphStateData stateData;
+  CFX_PathData path;
+  path.AppendRect(0, 0, m_Width, m_Height);
+  device->DrawPath(&path, matrix, &stateData, kBackgroundColor,
+                   kBackgroundColor, FXFILL_ALTERNATE);
+  int32_t leftPos = m_leftPadding;
+  int32_t topPos = m_topPadding;
+
+  CFX_Matrix matri = *matrix;
+  if (m_Width < m_outputWidth && m_Height < m_outputHeight) {
+    CFX_Matrix matriScale(static_cast<float>(m_Width) / m_outputWidth, 0.0, 0.0,
+                          static_cast<float>(m_Height) / m_outputHeight, 0.0,
+                          0.0);
+    matriScale.Concat(*matrix);
+    matri = matriScale;
+  }
+
+  CFX_GraphStateData data;
+  for (int32_t x = 0; x < m_inputWidth; x++) {
+    for (int32_t y = 0; y < m_inputHeight; y++) {
+      if (m_output->Get(x, y)) {
+        // In the output, each module is shifted by 1 due to the one module
+        // padding added to create quiet areas.
+        int start_x_output = x + 1;
+        int end_x_output = x + 2;
+        int start_y_output = y + 1;
+        int end_y_output = y + 2;
+
+        CFX_PathData rect;
+        rect.AppendRect(leftPos + start_x_output * m_multiX,
+                        topPos + start_y_output * m_multiY,
+                        leftPos + end_x_output * m_multiX,
+                        topPos + end_y_output * m_multiY);
+        device->DrawPath(&rect, &matri, &data, kBarColor, 0, FXFILL_WINDING);
+      }
+    }
+  }
+}
diff --git a/fxbarcode/BC_TwoDimWriter.h b/fxbarcode/BC_TwoDimWriter.h
index e554805..a887ded 100644
--- a/fxbarcode/BC_TwoDimWriter.h
+++ b/fxbarcode/BC_TwoDimWriter.h
@@ -11,27 +11,38 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "fxbarcode/BC_Writer.h"
+#include "third_party/base/span.h"
 
 class CBC_CommonBitMatrix;
 class CFX_RenderDevice;
 
 class CBC_TwoDimWriter : public CBC_Writer {
  public:
-  CBC_TwoDimWriter();
+  explicit CBC_TwoDimWriter(bool bFixedSize);
   ~CBC_TwoDimWriter() override;
 
-  virtual bool RenderResult(uint8_t* code,
-                            int32_t codeWidth,
-                            int32_t codeHeight);
-  virtual void RenderDeviceResult(CFX_RenderDevice* device,
-                                  const CFX_Matrix* matrix);
-  virtual bool SetErrorCorrectionLevel(int32_t level) = 0;
-  int32_t GetErrorCorrectionLevel() const;
+  bool RenderResult(pdfium::span<const uint8_t> code,
+                    int32_t codeWidth,
+                    int32_t codeHeight);
+  void RenderDeviceResult(CFX_RenderDevice* device, const CFX_Matrix* matrix);
+
+  int32_t error_correction_level() const { return m_iCorrectionLevel; }
 
  protected:
-  int32_t m_iCorrectLevel;
-  bool m_bFixedSize;
+  void set_error_correction_level(int32_t level) { m_iCorrectionLevel = level; }
+
+ private:
   std::unique_ptr<CBC_CommonBitMatrix> m_output;
+  int32_t m_multiX;
+  int32_t m_multiY;
+  int32_t m_leftPadding;
+  int32_t m_topPadding;
+  int32_t m_inputWidth;
+  int32_t m_inputHeight;
+  int32_t m_outputWidth;
+  int32_t m_outputHeight;
+  int32_t m_iCorrectionLevel = 1;
+  const bool m_bFixedSize;
 };
 
 #endif  // FXBARCODE_BC_TWODIMWRITER_H_
diff --git a/fxbarcode/BC_UtilCodingConvert.cpp b/fxbarcode/BC_UtilCodingConvert.cpp
deleted file mode 100644
index 77c52f2..0000000
--- a/fxbarcode/BC_UtilCodingConvert.cpp
+++ /dev/null
@@ -1,51 +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 "fxbarcode/BC_UtilCodingConvert.h"
-
-CBC_UtilCodingConvert::CBC_UtilCodingConvert() {}
-
-CBC_UtilCodingConvert::~CBC_UtilCodingConvert() {}
-
-void CBC_UtilCodingConvert::UnicodeToLocale(const WideString& src,
-                                            ByteString& dst) {
-  dst = ByteString::FromUnicode(src);
-}
-
-void CBC_UtilCodingConvert::LocaleToUtf8(const ByteString& src,
-                                         ByteString& dst) {
-  WideString unicode = WideString::FromLocal(src.AsStringView());
-  dst = unicode.UTF8Encode();
-}
-
-void CBC_UtilCodingConvert::LocaleToUtf8(const ByteString& src,
-                                         std::vector<uint8_t>& dst) {
-  WideString unicode = WideString::FromLocal(src.AsStringView());
-  ByteString utf8 = unicode.UTF8Encode();
-  dst = std::vector<uint8_t>(utf8.begin(), utf8.end());
-}
-
-void CBC_UtilCodingConvert::Utf8ToLocale(const std::vector<uint8_t>& src,
-                                         ByteString& dst) {
-  ByteString utf8;
-  for (uint8_t value : src)
-    utf8 += value;
-
-  WideString unicode = WideString::FromUTF8(utf8.AsStringView());
-  dst = ByteString::FromUnicode(unicode);
-}
-
-void CBC_UtilCodingConvert::Utf8ToLocale(const uint8_t* src,
-                                         int32_t count,
-                                         ByteString& dst) {
-  WideString unicode = WideString::FromUTF8(ByteStringView(src, count));
-  dst = ByteString::FromUnicode(unicode);
-}
-
-void CBC_UtilCodingConvert::UnicodeToUTF8(const WideString& src,
-                                          ByteString& dst) {
-  dst = src.UTF8Encode();
-}
diff --git a/fxbarcode/BC_UtilCodingConvert.h b/fxbarcode/BC_UtilCodingConvert.h
deleted file mode 100644
index 8600cd1..0000000
--- a/fxbarcode/BC_UtilCodingConvert.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXBARCODE_BC_UTILCODINGCONVERT_H_
-#define FXBARCODE_BC_UTILCODINGCONVERT_H_
-
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-
-class CBC_UtilCodingConvert {
- public:
-  CBC_UtilCodingConvert();
-  virtual ~CBC_UtilCodingConvert();
-  static void UnicodeToLocale(const WideString& source, ByteString& result);
-  static void LocaleToUtf8(const ByteString& source, ByteString& result);
-  static void LocaleToUtf8(const ByteString& source,
-                           std::vector<uint8_t>& result);
-  static void Utf8ToLocale(const std::vector<uint8_t>& source,
-                           ByteString& result);
-  static void Utf8ToLocale(const uint8_t* source,
-                           int32_t count,
-                           ByteString& result);
-  static void UnicodeToUTF8(const WideString& source, ByteString& result);
-};
-
-#endif  // FXBARCODE_BC_UTILCODINGCONVERT_H_
diff --git a/fxbarcode/BC_Utils.cpp b/fxbarcode/BC_Utils.cpp
deleted file mode 100644
index 1d09e24..0000000
--- a/fxbarcode/BC_Utils.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include <vector>
-
-#include "fxbarcode/utils.h"
-
-bool BC_FX_ByteString_Replace(ByteString& dst,
-                              uint32_t first,
-                              uint32_t last,
-                              int32_t count,
-                              char c) {
-  if (first > last || count <= 0) {
-    return false;
-  }
-  dst.Delete(first, last - first);
-  for (int32_t i = 0; i < count; i++) {
-    dst.InsertAtFront(c);
-  }
-  return true;
-}
-
-void BC_FX_ByteString_Append(ByteString& dst, int32_t count, char c) {
-  for (int32_t i = 0; i < count; i++)
-    dst += c;
-}
-void BC_FX_ByteString_Append(ByteString& dst, const std::vector<uint8_t>& ba) {
-  for (uint8_t value : ba)
-    dst += value;
-}
diff --git a/fxbarcode/BC_Writer.cpp b/fxbarcode/BC_Writer.cpp
index 5c8e0cf..9f77ffb 100644
--- a/fxbarcode/BC_Writer.cpp
+++ b/fxbarcode/BC_Writer.cpp
@@ -6,53 +6,57 @@
 
 #include "fxbarcode/BC_Writer.h"
 
-CBC_Writer::CBC_Writer() {
-  m_CharEncoding = 0;
-  m_ModuleHeight = 1;
-  m_ModuleWidth = 1;
-  m_Height = 320;
-  m_Width = 640;
-  m_colorSpace = FXDIB_Argb;
-  m_barColor = 0xff000000;
-  m_backgroundColor = 0xffffffff;
-}
-CBC_Writer::~CBC_Writer() {}
+CBC_Writer::CBC_Writer() = default;
+
+CBC_Writer::~CBC_Writer() = default;
+
 bool CBC_Writer::SetCharEncoding(int32_t encoding) {
   m_CharEncoding = encoding;
   return true;
 }
+
 bool CBC_Writer::SetModuleHeight(int32_t moduleHeight) {
-  if (moduleHeight > 10 || moduleHeight < 1) {
+  if (moduleHeight > 10 || moduleHeight < 1)
     return false;
-  }
+
   m_ModuleHeight = moduleHeight;
   return true;
 }
+
 bool CBC_Writer::SetModuleWidth(int32_t moduleWidth) {
-  if (moduleWidth > 10 || moduleWidth < 1) {
+  if (moduleWidth > 10 || moduleWidth < 1)
     return false;
-  }
+
   m_ModuleWidth = moduleWidth;
   return true;
 }
+
 bool CBC_Writer::SetHeight(int32_t height) {
   m_Height = height;
   return true;
 }
+
 bool CBC_Writer::SetWidth(int32_t width) {
   m_Width = width;
   return true;
 }
-void CBC_Writer::SetBackgroundColor(FX_ARGB backgroundColor) {
-  m_backgroundColor = backgroundColor;
-}
-void CBC_Writer::SetBarcodeColor(FX_ARGB foregroundColor) {
-  m_barColor = foregroundColor;
+
+bool CBC_Writer::SetTextLocation(BC_TEXT_LOC location) {
+  return false;
 }
 
-RetainPtr<CFX_DIBitmap> CBC_Writer::CreateDIBitmap(int32_t width,
-                                                   int32_t height) {
-  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  pDIBitmap->Create(width, height, m_colorSpace);
-  return pDIBitmap;
+bool CBC_Writer::SetWideNarrowRatio(int8_t ratio) {
+  return false;
+}
+
+bool CBC_Writer::SetStartChar(char start) {
+  return false;
+}
+
+bool CBC_Writer::SetEndChar(char end) {
+  return false;
+}
+
+bool CBC_Writer::SetErrorCorrectionLevel(int32_t level) {
+  return false;
 }
diff --git a/fxbarcode/BC_Writer.h b/fxbarcode/BC_Writer.h
index 565c03f..cf7f43b 100644
--- a/fxbarcode/BC_Writer.h
+++ b/fxbarcode/BC_Writer.h
@@ -7,10 +7,8 @@
 #ifndef FXBARCODE_BC_WRITER_H_
 #define FXBARCODE_BC_WRITER_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_dib.h"
-#include "fxbarcode/utils.h"
+#include "fxbarcode/BC_Library.h"
 
 class CBC_Writer {
  public:
@@ -21,19 +19,22 @@
   virtual bool SetModuleWidth(int32_t moduleWidth);
   virtual bool SetHeight(int32_t height);
   virtual bool SetWidth(int32_t width);
-  virtual void SetBackgroundColor(FX_ARGB backgroundColor);
-  virtual void SetBarcodeColor(FX_ARGB foregroundColor);
+  virtual bool SetTextLocation(BC_TEXT_LOC location);
+  virtual bool SetWideNarrowRatio(int8_t ratio);
+  virtual bool SetStartChar(char start);
+  virtual bool SetEndChar(char end);
+  virtual bool SetErrorCorrectionLevel(int32_t level);
 
  protected:
-  RetainPtr<CFX_DIBitmap> CreateDIBitmap(int32_t width, int32_t height);
-  int32_t m_CharEncoding;
-  int32_t m_ModuleHeight;
-  int32_t m_ModuleWidth;
-  int32_t m_Height;
-  int32_t m_Width;
-  FXDIB_Format m_colorSpace;
-  FX_ARGB m_barColor;
-  FX_ARGB m_backgroundColor;
+  static const FX_ARGB kBarColor = 0xff000000;
+  static const FX_ARGB kBackgroundColor = 0xffffffff;
+
+  int32_t m_CharEncoding = 0;
+  int32_t m_ModuleHeight = 1;
+  int32_t m_ModuleWidth = 1;
+  int32_t m_Height = 320;
+  int32_t m_Width = 640;
+  FXDIB_Format m_colorSpace = FXDIB_Argb;
 };
 
 #endif  // FXBARCODE_BC_WRITER_H_
diff --git a/fxbarcode/BUILD.gn b/fxbarcode/BUILD.gn
new file mode 100644
index 0000000..2b10e95
--- /dev/null
+++ b/fxbarcode/BUILD.gn
@@ -0,0 +1,165 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../pdfium.gni")
+import("../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fxbarcode") {
+  sources = [
+    "BC_Library.cpp",
+    "BC_Library.h",
+    "BC_TwoDimWriter.cpp",
+    "BC_TwoDimWriter.h",
+    "BC_Writer.cpp",
+    "BC_Writer.h",
+    "cbc_codabar.cpp",
+    "cbc_codabar.h",
+    "cbc_code128.cpp",
+    "cbc_code128.h",
+    "cbc_code39.cpp",
+    "cbc_code39.h",
+    "cbc_codebase.cpp",
+    "cbc_codebase.h",
+    "cbc_datamatrix.cpp",
+    "cbc_datamatrix.h",
+    "cbc_ean13.cpp",
+    "cbc_ean13.h",
+    "cbc_ean8.cpp",
+    "cbc_ean8.h",
+    "cbc_eancode.cpp",
+    "cbc_eancode.h",
+    "cbc_onecode.cpp",
+    "cbc_onecode.h",
+    "cbc_pdf417i.cpp",
+    "cbc_pdf417i.h",
+    "cbc_qrcode.cpp",
+    "cbc_qrcode.h",
+    "cbc_upca.cpp",
+    "cbc_upca.h",
+    "cfx_barcode.cpp",
+    "cfx_barcode.h",
+    "common/BC_CommonBitMatrix.cpp",
+    "common/BC_CommonBitMatrix.h",
+    "common/BC_CommonByteMatrix.cpp",
+    "common/BC_CommonByteMatrix.h",
+    "common/reedsolomon/BC_ReedSolomon.cpp",
+    "common/reedsolomon/BC_ReedSolomon.h",
+    "common/reedsolomon/BC_ReedSolomonGF256.cpp",
+    "common/reedsolomon/BC_ReedSolomonGF256.h",
+    "common/reedsolomon/BC_ReedSolomonGF256Poly.cpp",
+    "common/reedsolomon/BC_ReedSolomonGF256Poly.h",
+    "datamatrix/BC_ASCIIEncoder.cpp",
+    "datamatrix/BC_ASCIIEncoder.h",
+    "datamatrix/BC_Base256Encoder.cpp",
+    "datamatrix/BC_Base256Encoder.h",
+    "datamatrix/BC_C40Encoder.cpp",
+    "datamatrix/BC_C40Encoder.h",
+    "datamatrix/BC_DataMatrixSymbolInfo144.cpp",
+    "datamatrix/BC_DataMatrixSymbolInfo144.h",
+    "datamatrix/BC_DataMatrixWriter.cpp",
+    "datamatrix/BC_DataMatrixWriter.h",
+    "datamatrix/BC_DefaultPlacement.cpp",
+    "datamatrix/BC_DefaultPlacement.h",
+    "datamatrix/BC_EdifactEncoder.cpp",
+    "datamatrix/BC_EdifactEncoder.h",
+    "datamatrix/BC_Encoder.cpp",
+    "datamatrix/BC_Encoder.h",
+    "datamatrix/BC_EncoderContext.cpp",
+    "datamatrix/BC_EncoderContext.h",
+    "datamatrix/BC_ErrorCorrection.cpp",
+    "datamatrix/BC_ErrorCorrection.h",
+    "datamatrix/BC_HighLevelEncoder.cpp",
+    "datamatrix/BC_HighLevelEncoder.h",
+    "datamatrix/BC_SymbolInfo.cpp",
+    "datamatrix/BC_SymbolInfo.h",
+    "datamatrix/BC_TextEncoder.cpp",
+    "datamatrix/BC_TextEncoder.h",
+    "datamatrix/BC_X12Encoder.cpp",
+    "datamatrix/BC_X12Encoder.h",
+    "oned/BC_OneDimWriter.cpp",
+    "oned/BC_OneDimWriter.h",
+    "oned/BC_OnedCodaBarWriter.cpp",
+    "oned/BC_OnedCodaBarWriter.h",
+    "oned/BC_OnedCode128Writer.cpp",
+    "oned/BC_OnedCode128Writer.h",
+    "oned/BC_OnedCode39Writer.cpp",
+    "oned/BC_OnedCode39Writer.h",
+    "oned/BC_OnedEAN13Writer.cpp",
+    "oned/BC_OnedEAN13Writer.h",
+    "oned/BC_OnedEAN8Writer.cpp",
+    "oned/BC_OnedEAN8Writer.h",
+    "oned/BC_OnedEANChecksum.cpp",
+    "oned/BC_OnedEANChecksum.h",
+    "oned/BC_OnedEANWriter.cpp",
+    "oned/BC_OnedEANWriter.h",
+    "oned/BC_OnedUPCAWriter.cpp",
+    "oned/BC_OnedUPCAWriter.h",
+    "pdf417/BC_PDF417.cpp",
+    "pdf417/BC_PDF417.h",
+    "pdf417/BC_PDF417BarcodeMatrix.cpp",
+    "pdf417/BC_PDF417BarcodeMatrix.h",
+    "pdf417/BC_PDF417BarcodeRow.cpp",
+    "pdf417/BC_PDF417BarcodeRow.h",
+    "pdf417/BC_PDF417ErrorCorrection.cpp",
+    "pdf417/BC_PDF417ErrorCorrection.h",
+    "pdf417/BC_PDF417HighLevelEncoder.cpp",
+    "pdf417/BC_PDF417HighLevelEncoder.h",
+    "pdf417/BC_PDF417Writer.cpp",
+    "pdf417/BC_PDF417Writer.h",
+    "qrcode/BC_QRCodeWriter.cpp",
+    "qrcode/BC_QRCodeWriter.h",
+    "qrcode/BC_QRCoder.cpp",
+    "qrcode/BC_QRCoder.h",
+    "qrcode/BC_QRCoderBitVector.cpp",
+    "qrcode/BC_QRCoderBitVector.h",
+    "qrcode/BC_QRCoderECBlocks.cpp",
+    "qrcode/BC_QRCoderECBlocks.h",
+    "qrcode/BC_QRCoderECBlocksData.cpp",
+    "qrcode/BC_QRCoderECBlocksData.h",
+    "qrcode/BC_QRCoderEncoder.cpp",
+    "qrcode/BC_QRCoderEncoder.h",
+    "qrcode/BC_QRCoderErrorCorrectionLevel.cpp",
+    "qrcode/BC_QRCoderErrorCorrectionLevel.h",
+    "qrcode/BC_QRCoderMaskUtil.cpp",
+    "qrcode/BC_QRCoderMaskUtil.h",
+    "qrcode/BC_QRCoderMatrixUtil.cpp",
+    "qrcode/BC_QRCoderMatrixUtil.h",
+    "qrcode/BC_QRCoderMode.cpp",
+    "qrcode/BC_QRCoderMode.h",
+    "qrcode/BC_QRCoderVersion.cpp",
+    "qrcode/BC_QRCoderVersion.h",
+    "utils.h",
+  ]
+  deps = [
+    "../core/fxcrt",
+    "../core/fxge",
+    "../third_party:bigint",
+  ]
+  configs += [ "../:pdfium_core_config" ]
+  visibility = [ "../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cbc_pdf417i_unittest.cpp",
+    "cfx_barcode_unittest.cpp",
+    "datamatrix/BC_DataMatrixWriter_unittest.cpp",
+    "oned/BC_OnedCodaBarWriter_unittest.cpp",
+    "oned/BC_OnedCode128Writer_unittest.cpp",
+    "oned/BC_OnedCode39Writer_unittest.cpp",
+    "oned/BC_OnedEAN13Writer_unittest.cpp",
+    "oned/BC_OnedEAN8Writer_unittest.cpp",
+    "oned/BC_OnedUPCAWriter_unittest.cpp",
+    "pdf417/BC_PDF417HighLevelEncoder_unittest.cpp",
+    "pdf417/BC_PDF417Writer_unittest.cpp",
+    "qrcode/BC_QRCodeWriter_unittest.cpp",
+  ]
+  deps = [
+    ":fxbarcode",
+    "../core/fxge",
+  ]
+  pdfium_root_dir = "../"
+}
diff --git a/fxbarcode/DEPS b/fxbarcode/DEPS
index 35a265a..296c87f 100644
--- a/fxbarcode/DEPS
+++ b/fxbarcode/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  '+core/fxcodec',
   '+core/fxcrt',
   '+core/fxge',
   '+third_party/bigint',
diff --git a/fxbarcode/cbc_codabar.cpp b/fxbarcode/cbc_codabar.cpp
index 0795ff2..ad4d21c 100644
--- a/fxbarcode/cbc_codabar.cpp
+++ b/fxbarcode/cbc_codabar.cpp
@@ -23,6 +23,7 @@
 
 #include <memory>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "fxbarcode/oned/BC_OnedCodaBarWriter.h"
 #include "third_party/base/ptr_util.h"
 
@@ -31,40 +32,20 @@
 
 CBC_Codabar::~CBC_Codabar() {}
 
-bool CBC_Codabar::SetStartChar(char start) {
-  return GetOnedCodaBarWriter()->SetStartChar(start);
-}
-
-bool CBC_Codabar::SetEndChar(char end) {
-  return GetOnedCodaBarWriter()->SetEndChar(end);
-}
-
-bool CBC_Codabar::SetTextLocation(BC_TEXT_LOC location) {
-  return GetOnedCodaBarWriter()->SetTextLocation(location);
-}
-
-bool CBC_Codabar::SetWideNarrowRatio(int8_t ratio) {
-  return GetOnedCodaBarWriter()->SetWideNarrowRatio(ratio);
-}
-
-bool CBC_Codabar::Encode(const WideStringView& contents) {
-  if (contents.IsEmpty())
+bool CBC_Codabar::Encode(WideStringView contents) {
+  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
     return false;
 
   BCFORMAT format = BCFORMAT_CODABAR;
   int32_t outWidth = 0;
   int32_t outHeight = 0;
-  WideString filtercontents = GetOnedCodaBarWriter()->FilterContents(contents);
-  ByteString byteString = filtercontents.UTF8Encode();
-  m_renderContents = filtercontents;
+  m_renderContents = GetOnedCodaBarWriter()->FilterContents(contents);
+  ByteString byteString = m_renderContents.ToUTF8();
   auto* pWriter = GetOnedCodaBarWriter();
   std::unique_ptr<uint8_t, FxFreeDeleter> data(
       pWriter->Encode(byteString, format, outWidth, outHeight));
-  if (!data)
-    return false;
-
-  return pWriter->RenderResult(filtercontents.AsStringView(), data.get(),
-                               outWidth);
+  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
+                                       data.get(), outWidth);
 }
 
 bool CBC_Codabar::RenderDevice(CFX_RenderDevice* device,
diff --git a/fxbarcode/cbc_codabar.h b/fxbarcode/cbc_codabar.h
index 827fc28..a3e1df4 100644
--- a/fxbarcode/cbc_codabar.h
+++ b/fxbarcode/cbc_codabar.h
@@ -9,26 +9,20 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
 #include "fxbarcode/cbc_onecode.h"
 
 class CBC_OnedCodaBarWriter;
 
-class CBC_Codabar : public CBC_OneCode {
+class CBC_Codabar final : public CBC_OneCode {
  public:
   CBC_Codabar();
   ~CBC_Codabar() override;
 
   // CBC_OneCode:
-  bool Encode(const WideStringView& contents) override;
+  BC_TYPE GetType() override;
+  bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
                     const CFX_Matrix* matrix) override;
-  BC_TYPE GetType() override;
-
-  bool SetStartChar(char start);
-  bool SetEndChar(char end);
-  bool SetTextLocation(BC_TEXT_LOC location);
-  bool SetWideNarrowRatio(int8_t ratio);
 
  private:
   CBC_OnedCodaBarWriter* GetOnedCodaBarWriter();
diff --git a/fxbarcode/cbc_code128.cpp b/fxbarcode/cbc_code128.cpp
index 7ba6235..c363c94 100644
--- a/fxbarcode/cbc_code128.cpp
+++ b/fxbarcode/cbc_code128.cpp
@@ -23,6 +23,7 @@
 
 #include <memory>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "fxbarcode/oned/BC_OnedCode128Writer.h"
 #include "third_party/base/ptr_util.h"
 
@@ -31,12 +32,8 @@
 
 CBC_Code128::~CBC_Code128() {}
 
-bool CBC_Code128::SetTextLocation(BC_TEXT_LOC location) {
-  return GetOnedCode128Writer()->SetTextLocation(location);
-}
-
-bool CBC_Code128::Encode(const WideStringView& contents) {
-  if (contents.IsEmpty())
+bool CBC_Code128::Encode(WideStringView contents) {
+  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
     return false;
 
   BCFORMAT format = BCFORMAT_CODE_128;
@@ -47,15 +44,12 @@
   if (contents.GetLength() % 2 && pWriter->GetType() == BC_CODE128_C)
     content += '0';
 
-  WideString encodeContents = pWriter->FilterContents(content.AsStringView());
-  m_renderContents = encodeContents;
-  ByteString byteString = encodeContents.UTF8Encode();
+  m_renderContents = pWriter->FilterContents(content.AsStringView());
+  ByteString byteString = m_renderContents.ToUTF8();
   std::unique_ptr<uint8_t, FxFreeDeleter> data(
       pWriter->Encode(byteString, format, outWidth, outHeight));
-  if (!data)
-    return false;
-  return pWriter->RenderResult(encodeContents.AsStringView(), data.get(),
-                               outWidth);
+  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
+                                       data.get(), outWidth);
 }
 
 bool CBC_Code128::RenderDevice(CFX_RenderDevice* device,
diff --git a/fxbarcode/cbc_code128.h b/fxbarcode/cbc_code128.h
index b057aff..2ccf07d 100644
--- a/fxbarcode/cbc_code128.h
+++ b/fxbarcode/cbc_code128.h
@@ -9,23 +9,20 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
 #include "fxbarcode/cbc_onecode.h"
 
 class CBC_OnedCode128Writer;
 
-class CBC_Code128 : public CBC_OneCode {
+class CBC_Code128 final : public CBC_OneCode {
  public:
   explicit CBC_Code128(BC_TYPE type);
   ~CBC_Code128() override;
 
   // CBC_OneCode:
-  bool Encode(const WideStringView& contents) override;
+  BC_TYPE GetType() override;
+  bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
                     const CFX_Matrix* matrix) override;
-  BC_TYPE GetType() override;
-
-  bool SetTextLocation(BC_TEXT_LOC loction);
 
  private:
   CBC_OnedCode128Writer* GetOnedCode128Writer();
diff --git a/fxbarcode/cbc_code39.cpp b/fxbarcode/cbc_code39.cpp
index 9715c25..2138d18 100644
--- a/fxbarcode/cbc_code39.cpp
+++ b/fxbarcode/cbc_code39.cpp
@@ -23,6 +23,7 @@
 
 #include <memory>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "fxbarcode/oned/BC_OnedCode39Writer.h"
 #include "third_party/base/ptr_util.h"
 
@@ -31,8 +32,8 @@
 
 CBC_Code39::~CBC_Code39() {}
 
-bool CBC_Code39::Encode(const WideStringView& contents) {
-  if (contents.IsEmpty())
+bool CBC_Code39::Encode(WideStringView contents) {
+  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
     return false;
 
   BCFORMAT format = BCFORMAT_CODE_39;
@@ -40,15 +41,12 @@
   int32_t outHeight = 0;
   auto* pWriter = GetOnedCode39Writer();
   WideString filtercontents = pWriter->FilterContents(contents);
-  WideString renderContents = pWriter->RenderTextContents(contents);
-  m_renderContents = renderContents;
-  ByteString byteString = filtercontents.UTF8Encode();
+  m_renderContents = pWriter->RenderTextContents(contents);
+  ByteString byteString = filtercontents.ToUTF8();
   std::unique_ptr<uint8_t, FxFreeDeleter> data(
       pWriter->Encode(byteString, format, outWidth, outHeight));
-  if (!data)
-    return false;
-  return pWriter->RenderResult(renderContents.AsStringView(), data.get(),
-                               outWidth);
+  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
+                                       data.get(), outWidth);
 }
 
 bool CBC_Code39::RenderDevice(CFX_RenderDevice* device,
@@ -64,14 +62,6 @@
   return BC_CODE39;
 }
 
-bool CBC_Code39::SetTextLocation(BC_TEXT_LOC location) {
-  return GetOnedCode39Writer()->SetTextLocation(location);
-}
-
-bool CBC_Code39::SetWideNarrowRatio(int8_t ratio) {
-  return GetOnedCode39Writer()->SetWideNarrowRatio(ratio);
-}
-
 CBC_OnedCode39Writer* CBC_Code39::GetOnedCode39Writer() {
   return static_cast<CBC_OnedCode39Writer*>(m_pBCWriter.get());
 }
diff --git a/fxbarcode/cbc_code39.h b/fxbarcode/cbc_code39.h
index 937673c..ab48292 100644
--- a/fxbarcode/cbc_code39.h
+++ b/fxbarcode/cbc_code39.h
@@ -10,24 +10,20 @@
 #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 "fxbarcode/cbc_onecode.h"
 
 class CBC_OnedCode39Writer;
 
-class CBC_Code39 : public CBC_OneCode {
+class CBC_Code39 final : public CBC_OneCode {
  public:
   CBC_Code39();
   ~CBC_Code39() override;
 
   // CBC_OneCode:
-  bool Encode(const WideStringView& contents) override;
+  BC_TYPE GetType() override;
+  bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
                     const CFX_Matrix* matrix) override;
-  BC_TYPE GetType() override;
-
-  bool SetTextLocation(BC_TEXT_LOC location);
-  bool SetWideNarrowRatio(int8_t ratio);
 
  private:
   CBC_OnedCode39Writer* GetOnedCode39Writer();
diff --git a/fxbarcode/cbc_codebase.cpp b/fxbarcode/cbc_codebase.cpp
index 2278f93..305d09b 100644
--- a/fxbarcode/cbc_codebase.cpp
+++ b/fxbarcode/cbc_codebase.cpp
@@ -30,6 +30,26 @@
 
 CBC_CodeBase::~CBC_CodeBase() {}
 
+bool CBC_CodeBase::SetTextLocation(BC_TEXT_LOC location) {
+  return m_pBCWriter->SetTextLocation(location);
+}
+
+bool CBC_CodeBase::SetWideNarrowRatio(int8_t ratio) {
+  return m_pBCWriter->SetWideNarrowRatio(ratio);
+}
+
+bool CBC_CodeBase::SetStartChar(char start) {
+  return m_pBCWriter->SetStartChar(start);
+}
+
+bool CBC_CodeBase::SetEndChar(char end) {
+  return m_pBCWriter->SetEndChar(end);
+}
+
+bool CBC_CodeBase::SetErrorCorrectionLevel(int32_t level) {
+  return m_pBCWriter->SetErrorCorrectionLevel(level);
+}
+
 bool CBC_CodeBase::SetCharEncoding(int32_t encoding) {
   return m_pBCWriter->SetCharEncoding(encoding);
 }
@@ -49,11 +69,3 @@
 bool CBC_CodeBase::SetWidth(int32_t width) {
   return m_pBCWriter->SetWidth(width);
 }
-
-void CBC_CodeBase::SetBackgroundColor(FX_ARGB backgroundColor) {
-  m_pBCWriter->SetBackgroundColor(backgroundColor);
-}
-
-void CBC_CodeBase::SetBarcodeColor(FX_ARGB foregroundColor) {
-  m_pBCWriter->SetBarcodeColor(foregroundColor);
-}
diff --git a/fxbarcode/cbc_codebase.h b/fxbarcode/cbc_codebase.h
index aab98da..d3e21dc 100644
--- a/fxbarcode/cbc_codebase.h
+++ b/fxbarcode/cbc_codebase.h
@@ -26,17 +26,20 @@
   virtual ~CBC_CodeBase();
 
   virtual BC_TYPE GetType() = 0;
-  virtual bool Encode(const WideStringView& contents) = 0;
+  virtual bool Encode(WideStringView contents) = 0;
   virtual bool RenderDevice(CFX_RenderDevice* device,
                             const CFX_Matrix* matrix) = 0;
 
+  bool SetTextLocation(BC_TEXT_LOC location);
+  bool SetWideNarrowRatio(int8_t ratio);
+  bool SetStartChar(char start);
+  bool SetEndChar(char end);
+  bool SetErrorCorrectionLevel(int32_t level);
   bool SetCharEncoding(int32_t encoding);
   bool SetModuleHeight(int32_t moduleHeight);
   bool SetModuleWidth(int32_t moduleWidth);
   bool SetHeight(int32_t height);
   bool SetWidth(int32_t width);
-  void SetBackgroundColor(FX_ARGB backgroundColor);
-  void SetBarcodeColor(FX_ARGB foregroundColor);
 
  protected:
   std::unique_ptr<CBC_Writer> m_pBCWriter;
diff --git a/fxbarcode/cbc_datamatrix.cpp b/fxbarcode/cbc_datamatrix.cpp
index c9e23fa..e8695d2 100644
--- a/fxbarcode/cbc_datamatrix.cpp
+++ b/fxbarcode/cbc_datamatrix.cpp
@@ -21,7 +21,7 @@
 
 #include "fxbarcode/cbc_datamatrix.h"
 
-#include <memory>
+#include <vector>
 
 #include "fxbarcode/datamatrix/BC_DataMatrixWriter.h"
 #include "third_party/base/ptr_util.h"
@@ -29,17 +29,15 @@
 CBC_DataMatrix::CBC_DataMatrix()
     : CBC_CodeBase(pdfium::MakeUnique<CBC_DataMatrixWriter>()) {}
 
-CBC_DataMatrix::~CBC_DataMatrix() {}
+CBC_DataMatrix::~CBC_DataMatrix() = default;
 
-bool CBC_DataMatrix::Encode(const WideStringView& contents) {
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
+bool CBC_DataMatrix::Encode(WideStringView contents) {
+  int32_t width;
+  int32_t height;
   auto* pWriter = GetDataMatrixWriter();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(WideString(contents), outWidth, outHeight));
-  if (!data)
-    return false;
-  return pWriter->RenderResult(data.get(), outWidth, outHeight);
+  std::vector<uint8_t> data =
+      pWriter->Encode(WideString(contents), &width, &height);
+  return pWriter->RenderResult(data, width, height);
 }
 
 bool CBC_DataMatrix::RenderDevice(CFX_RenderDevice* device,
diff --git a/fxbarcode/cbc_datamatrix.h b/fxbarcode/cbc_datamatrix.h
index 3fe1c09..c504c95 100644
--- a/fxbarcode/cbc_datamatrix.h
+++ b/fxbarcode/cbc_datamatrix.h
@@ -9,18 +9,17 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
 #include "fxbarcode/cbc_codebase.h"
 
 class CBC_DataMatrixWriter;
 
-class CBC_DataMatrix : public CBC_CodeBase {
+class CBC_DataMatrix final : public CBC_CodeBase {
  public:
   CBC_DataMatrix();
   ~CBC_DataMatrix() override;
 
   // CBC_OneCode:
-  bool Encode(const WideStringView& contents) override;
+  bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
                     const CFX_Matrix* matrix) override;
   BC_TYPE GetType() override;
diff --git a/fxbarcode/cbc_ean13.cpp b/fxbarcode/cbc_ean13.cpp
index 1672532..934e61c 100644
--- a/fxbarcode/cbc_ean13.cpp
+++ b/fxbarcode/cbc_ean13.cpp
@@ -27,58 +27,18 @@
 #include "third_party/base/ptr_util.h"
 
 CBC_EAN13::CBC_EAN13()
-    : CBC_OneCode(pdfium::MakeUnique<CBC_OnedEAN13Writer>()) {}
+    : CBC_EANCode(pdfium::MakeUnique<CBC_OnedEAN13Writer>()) {}
 
-CBC_EAN13::~CBC_EAN13() {}
-
-WideString CBC_EAN13::Preprocess(const WideStringView& contents) {
-  auto* pWriter = GetOnedEAN13Writer();
-  WideString encodeContents = pWriter->FilterContents(contents);
-  int32_t length = encodeContents.GetLength();
-  if (length <= 12) {
-    for (int32_t i = 0; i < 12 - length; i++)
-      encodeContents = wchar_t('0') + encodeContents;
-
-    ByteString byteString = encodeContents.UTF8Encode();
-    int32_t checksum = pWriter->CalcChecksum(byteString);
-    byteString += checksum - 0 + '0';
-    encodeContents = byteString.UTF8Decode();
-  }
-  if (length > 13)
-    encodeContents = encodeContents.Left(13);
-
-  return encodeContents;
-}
-
-bool CBC_EAN13::Encode(const WideStringView& contents) {
-  if (contents.IsEmpty())
-    return false;
-
-  BCFORMAT format = BCFORMAT_EAN_13;
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
-  WideString encodeContents = Preprocess(contents);
-  ByteString byteString = encodeContents.UTF8Encode();
-  m_renderContents = encodeContents;
-  auto* pWriter = GetOnedEAN13Writer();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(byteString, format, outWidth, outHeight));
-  if (!data)
-    return false;
-  return pWriter->RenderResult(encodeContents.AsStringView(), data.get(),
-                               outWidth);
-}
-
-bool CBC_EAN13::RenderDevice(CFX_RenderDevice* device,
-                             const CFX_Matrix* matrix) {
-  return GetOnedEAN13Writer()->RenderDeviceResult(
-      device, matrix, m_renderContents.AsStringView());
-}
+CBC_EAN13::~CBC_EAN13() = default;
 
 BC_TYPE CBC_EAN13::GetType() {
   return BC_EAN13;
 }
 
-CBC_OnedEAN13Writer* CBC_EAN13::GetOnedEAN13Writer() {
-  return static_cast<CBC_OnedEAN13Writer*>(m_pBCWriter.get());
+BCFORMAT CBC_EAN13::GetFormat() const {
+  return BCFORMAT_EAN_13;
+}
+
+size_t CBC_EAN13::GetMaxLength() const {
+  return 12;
 }
diff --git a/fxbarcode/cbc_ean13.h b/fxbarcode/cbc_ean13.h
index 609f8b0..a82a03f 100644
--- a/fxbarcode/cbc_ean13.h
+++ b/fxbarcode/cbc_ean13.h
@@ -7,30 +7,21 @@
 #ifndef FXBARCODE_CBC_EAN13_H_
 #define FXBARCODE_CBC_EAN13_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 "fxbarcode/cbc_onecode.h"
+#include "fxbarcode/cbc_eancode.h"
 
 class CBC_OnedEAN13Writer;
 
-class CBC_EAN13 : public CBC_OneCode {
+class CBC_EAN13 final : public CBC_EANCode {
  public:
   CBC_EAN13();
   ~CBC_EAN13() override;
 
-  // CBC_OneCode:
-  bool Encode(const WideStringView& contents) override;
-  bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+  // CBC_EANCode:
   BC_TYPE GetType() override;
-
- private:
-  CBC_OnedEAN13Writer* GetOnedEAN13Writer();
-  WideString Preprocess(const WideStringView& contents);
-
-  WideString m_renderContents;
+  BCFORMAT GetFormat() const override;
+  size_t GetMaxLength() const override;
 };
 
 #endif  // FXBARCODE_CBC_EAN13_H_
diff --git a/fxbarcode/cbc_ean8.cpp b/fxbarcode/cbc_ean8.cpp
index 796f3a5..84ed672 100644
--- a/fxbarcode/cbc_ean8.cpp
+++ b/fxbarcode/cbc_ean8.cpp
@@ -26,57 +26,18 @@
 #include "fxbarcode/oned/BC_OnedEAN8Writer.h"
 #include "third_party/base/ptr_util.h"
 
-CBC_EAN8::CBC_EAN8() : CBC_OneCode(pdfium::MakeUnique<CBC_OnedEAN8Writer>()) {}
+CBC_EAN8::CBC_EAN8() : CBC_EANCode(pdfium::MakeUnique<CBC_OnedEAN8Writer>()) {}
 
-CBC_EAN8::~CBC_EAN8() {}
-
-WideString CBC_EAN8::Preprocess(const WideStringView& contents) {
-  auto* pWriter = GetOnedEAN8Writer();
-  WideString encodeContents = pWriter->FilterContents(contents);
-  int32_t length = encodeContents.GetLength();
-  if (length <= 7) {
-    for (int32_t i = 0; i < 7 - length; i++)
-      encodeContents = wchar_t('0') + encodeContents;
-
-    ByteString byteString = encodeContents.UTF8Encode();
-    int32_t checksum = pWriter->CalcChecksum(byteString);
-    encodeContents += wchar_t(checksum - 0 + '0');
-  }
-  if (length > 8)
-    encodeContents = encodeContents.Left(8);
-
-  return encodeContents;
-}
-
-bool CBC_EAN8::Encode(const WideStringView& contents) {
-  if (contents.IsEmpty())
-    return false;
-
-  BCFORMAT format = BCFORMAT_EAN_8;
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
-  WideString encodeContents = Preprocess(contents);
-  ByteString byteString = encodeContents.UTF8Encode();
-  m_renderContents = encodeContents;
-  auto* pWriter = GetOnedEAN8Writer();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(byteString, format, outWidth, outHeight));
-  if (!data)
-    return false;
-  return pWriter->RenderResult(encodeContents.AsStringView(), data.get(),
-                               outWidth);
-}
-
-bool CBC_EAN8::RenderDevice(CFX_RenderDevice* device,
-                            const CFX_Matrix* matrix) {
-  return GetOnedEAN8Writer()->RenderDeviceResult(
-      device, matrix, m_renderContents.AsStringView());
-}
+CBC_EAN8::~CBC_EAN8() = default;
 
 BC_TYPE CBC_EAN8::GetType() {
   return BC_EAN8;
 }
 
-CBC_OnedEAN8Writer* CBC_EAN8::GetOnedEAN8Writer() {
-  return static_cast<CBC_OnedEAN8Writer*>(m_pBCWriter.get());
+BCFORMAT CBC_EAN8::GetFormat() const {
+  return BCFORMAT_EAN_8;
+}
+
+size_t CBC_EAN8::GetMaxLength() const {
+  return 7;
 }
diff --git a/fxbarcode/cbc_ean8.h b/fxbarcode/cbc_ean8.h
index 4127650..57c06fa 100644
--- a/fxbarcode/cbc_ean8.h
+++ b/fxbarcode/cbc_ean8.h
@@ -9,26 +9,19 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
-#include "fxbarcode/cbc_onecode.h"
+#include "fxbarcode/cbc_eancode.h"
 
 class CBC_OnedEAN8Writer;
 
-class CBC_EAN8 : public CBC_OneCode {
+class CBC_EAN8 final : public CBC_EANCode {
  public:
   CBC_EAN8();
   ~CBC_EAN8() override;
 
-  // CBC_OneCode:
-  bool Encode(const WideStringView& contents) override;
-  bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+  // CBC_EANCode:
   BC_TYPE GetType() override;
-
- private:
-  CBC_OnedEAN8Writer* GetOnedEAN8Writer();
-  WideString Preprocess(const WideStringView& contents);
-  WideString m_renderContents;
+  BCFORMAT GetFormat() const override;
+  size_t GetMaxLength() const override;
 };
 
 #endif  // FXBARCODE_CBC_EAN8_H_
diff --git a/fxbarcode/cbc_eancode.cpp b/fxbarcode/cbc_eancode.cpp
new file mode 100644
index 0000000..6e44f81
--- /dev/null
+++ b/fxbarcode/cbc_eancode.cpp
@@ -0,0 +1,64 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxbarcode/cbc_eancode.h"
+
+#include <utility>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "fxbarcode/oned/BC_OnedEANWriter.h"
+
+CBC_EANCode::CBC_EANCode(std::unique_ptr<CBC_OneDimEANWriter> pWriter)
+    : CBC_OneCode(std::move(pWriter)) {}
+
+CBC_EANCode::~CBC_EANCode() = default;
+
+CBC_OneDimEANWriter* CBC_EANCode::GetOneDimEANWriter() {
+  return static_cast<CBC_OneDimEANWriter*>(m_pBCWriter.get());
+}
+
+bool CBC_EANCode::Encode(WideStringView contents) {
+  if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes)
+    return false;
+
+  BCFORMAT format = GetFormat();
+  int32_t out_width = 0;
+  int32_t out_height = 0;
+  m_renderContents = Preprocess(contents);
+  ByteString str = m_renderContents.ToUTF8();
+  auto* pWriter = GetOneDimEANWriter();
+  pWriter->InitEANWriter();
+  std::unique_ptr<uint8_t, FxFreeDeleter> data(
+      pWriter->Encode(str, format, out_width, out_height));
+  return data && pWriter->RenderResult(m_renderContents.AsStringView(),
+                                       data.get(), out_width);
+}
+
+bool CBC_EANCode::RenderDevice(CFX_RenderDevice* device,
+                               const CFX_Matrix* matrix) {
+  return GetOneDimEANWriter()->RenderDeviceResult(
+      device, matrix, m_renderContents.AsStringView());
+}
+
+WideString CBC_EANCode::Preprocess(WideStringView contents) {
+  auto* pWriter = GetOneDimEANWriter();
+  WideString encoded_contents = pWriter->FilterContents(contents);
+  size_t length = encoded_contents.GetLength();
+  size_t max_length = GetMaxLength();
+  if (length <= max_length) {
+    for (size_t i = 0; i < max_length - length; i++)
+      encoded_contents.InsertAtFront(L'0');
+
+    ByteString str = encoded_contents.ToUTF8();
+    int32_t checksum = pWriter->CalcChecksum(str);
+    str += '0' + checksum;
+    encoded_contents = WideString::FromUTF8(str.AsStringView());
+  } else {
+    encoded_contents = encoded_contents.First(max_length + 1);
+  }
+
+  return encoded_contents;
+}
diff --git a/fxbarcode/cbc_eancode.h b/fxbarcode/cbc_eancode.h
new file mode 100644
index 0000000..572bb42
--- /dev/null
+++ b/fxbarcode/cbc_eancode.h
@@ -0,0 +1,38 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXBARCODE_CBC_EANCODE_H_
+#define FXBARCODE_CBC_EANCODE_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_string.h"
+#include "fxbarcode/cbc_onecode.h"
+#include "fxbarcode/utils.h"
+
+class CBC_OneDimEANWriter;
+
+class CBC_EANCode : public CBC_OneCode {
+ public:
+  explicit CBC_EANCode(std::unique_ptr<CBC_OneDimEANWriter> pWriter);
+  ~CBC_EANCode() override;
+
+  virtual BCFORMAT GetFormat() const = 0;
+  virtual size_t GetMaxLength() const = 0;
+
+  // CBC_EANCode:
+  bool Encode(WideStringView contents) override;
+  bool RenderDevice(CFX_RenderDevice* device,
+                    const CFX_Matrix* matrix) override;
+
+ protected:
+  CBC_OneDimEANWriter* GetOneDimEANWriter();
+  WideString Preprocess(WideStringView contents);
+
+  WideString m_renderContents;
+};
+
+#endif  // FXBARCODE_CBC_EANCODE_H_
diff --git a/fxbarcode/cbc_onecode.cpp b/fxbarcode/cbc_onecode.cpp
index fb8cb05..05501bd 100644
--- a/fxbarcode/cbc_onecode.cpp
+++ b/fxbarcode/cbc_onecode.cpp
@@ -30,11 +30,11 @@
 
 CBC_OneCode::~CBC_OneCode() {}
 
-bool CBC_OneCode::CheckContentValidity(const WideStringView& contents) {
+bool CBC_OneCode::CheckContentValidity(WideStringView contents) {
   return GetOneDimWriter()->CheckContentValidity(contents);
 }
 
-WideString CBC_OneCode::FilterContents(const WideStringView& contents) {
+WideString CBC_OneCode::FilterContents(WideStringView contents) {
   return GetOneDimWriter()->FilterContents(contents);
 }
 
diff --git a/fxbarcode/cbc_onecode.h b/fxbarcode/cbc_onecode.h
index eae618e..26aa943 100644
--- a/fxbarcode/cbc_onecode.h
+++ b/fxbarcode/cbc_onecode.h
@@ -18,19 +18,23 @@
 
 class CBC_OneCode : public CBC_CodeBase {
  public:
+  // Limit the size of 1D barcodes. Typical 1D barcodes are short so this should
+  // be sufficient for most use cases.
+  static constexpr size_t kMaxInputLengthBytes = 8192;
+
   explicit CBC_OneCode(std::unique_ptr<CBC_Writer> pWriter);
   ~CBC_OneCode() override;
 
-  virtual bool CheckContentValidity(const WideStringView& contents);
-  virtual WideString FilterContents(const WideStringView& contents);
+  virtual bool CheckContentValidity(WideStringView contents);
+  virtual WideString FilterContents(WideStringView contents);
 
-  virtual void SetPrintChecksum(bool checksum);
-  virtual void SetDataLength(int32_t length);
-  virtual void SetCalChecksum(bool calc);
-  virtual bool SetFont(CFX_Font* cFont);
-  virtual void SetFontSize(float size);
-  virtual void SetFontStyle(int32_t style);
-  virtual void SetFontColor(FX_ARGB color);
+  void SetPrintChecksum(bool checksum);
+  void SetDataLength(int32_t length);
+  void SetCalChecksum(bool calc);
+  bool SetFont(CFX_Font* cFont);
+  void SetFontSize(float size);
+  void SetFontStyle(int32_t style);
+  void SetFontColor(FX_ARGB color);
 
  private:
   CBC_OneDimWriter* GetOneDimWriter();
diff --git a/fxbarcode/cbc_pdf417i.cpp b/fxbarcode/cbc_pdf417i.cpp
index 4e7e063..f91f5c6 100644
--- a/fxbarcode/cbc_pdf417i.cpp
+++ b/fxbarcode/cbc_pdf417i.cpp
@@ -21,34 +21,33 @@
 
 #include "fxbarcode/cbc_pdf417i.h"
 
-#include <memory>
+#include <vector>
 
 #include "fxbarcode/pdf417/BC_PDF417Writer.h"
 #include "third_party/base/ptr_util.h"
 
+namespace {
+
+// Multiple source say PDF417 can encode about 1100 bytes, 1800 ASCII characters
+// or 2710 numerical digits.
+constexpr size_t kMaxPDF417InputLengthBytes = 2710;
+
+}  // namespace
+
 CBC_PDF417I::CBC_PDF417I()
     : CBC_CodeBase(pdfium::MakeUnique<CBC_PDF417Writer>()) {}
 
 CBC_PDF417I::~CBC_PDF417I() {}
 
-bool CBC_PDF417I::SetErrorCorrectionLevel(int32_t level) {
-  GetPDF417Writer()->SetErrorCorrectionLevel(level);
-  return true;
-}
-
-void CBC_PDF417I::SetTruncated(bool truncated) {
-  GetPDF417Writer()->SetTruncated(truncated);
-}
-
-bool CBC_PDF417I::Encode(const WideStringView& contents) {
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
-  auto* pWriter = GetPDF417Writer();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(WideString(contents), outWidth, outHeight));
-  if (!data)
+bool CBC_PDF417I::Encode(WideStringView contents) {
+  if (contents.GetLength() > kMaxPDF417InputLengthBytes)
     return false;
-  return pWriter->RenderResult(data.get(), outWidth, outHeight);
+
+  int32_t width;
+  int32_t height;
+  auto* pWriter = GetPDF417Writer();
+  std::vector<uint8_t> data = pWriter->Encode(contents, &width, &height);
+  return pWriter->RenderResult(data, width, height);
 }
 
 bool CBC_PDF417I::RenderDevice(CFX_RenderDevice* device,
diff --git a/fxbarcode/cbc_pdf417i.h b/fxbarcode/cbc_pdf417i.h
index 5bd7a26..ab773f9 100644
--- a/fxbarcode/cbc_pdf417i.h
+++ b/fxbarcode/cbc_pdf417i.h
@@ -9,25 +9,21 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
 #include "fxbarcode/cbc_codebase.h"
 
 class CBC_PDF417Writer;
 
-class CBC_PDF417I : public CBC_CodeBase {
+class CBC_PDF417I final : public CBC_CodeBase {
  public:
   CBC_PDF417I();
   ~CBC_PDF417I() override;
 
   // CBC_CodeBase:
-  bool Encode(const WideStringView& contents) override;
+  bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
                     const CFX_Matrix* matrix) override;
   BC_TYPE GetType() override;
 
-  bool SetErrorCorrectionLevel(int32_t level);
-  void SetTruncated(bool truncated);
-
  private:
   CBC_PDF417Writer* GetPDF417Writer();
 };
diff --git a/fxbarcode/cbc_pdf417i_unittest.cpp b/fxbarcode/cbc_pdf417i_unittest.cpp
new file mode 100644
index 0000000..94e69fb
--- /dev/null
+++ b/fxbarcode/cbc_pdf417i_unittest.cpp
@@ -0,0 +1,20 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxbarcode/cbc_pdf417i.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CBC_PDF417ITest, Normal) {
+  CBC_PDF417I encoder;
+  EXPECT_TRUE(encoder.Encode(L"Foo"));
+}
+
+TEST(CBC_PDF417ITest, MaxLength) {
+  std::vector<wchar_t> input(2711, L'1');
+  CBC_PDF417I encoder;
+  EXPECT_FALSE(encoder.Encode(WideStringView(input.data(), input.size())));
+}
diff --git a/fxbarcode/cbc_qrcode.cpp b/fxbarcode/cbc_qrcode.cpp
index 0f3a65e..5e60fa3 100644
--- a/fxbarcode/cbc_qrcode.cpp
+++ b/fxbarcode/cbc_qrcode.cpp
@@ -21,7 +21,7 @@
 
 #include "fxbarcode/cbc_qrcode.h"
 
-#include <memory>
+#include <vector>
 
 #include "fxbarcode/qrcode/BC_QRCodeWriter.h"
 #include "third_party/base/ptr_util.h"
@@ -29,24 +29,15 @@
 CBC_QRCode::CBC_QRCode()
     : CBC_CodeBase(pdfium::MakeUnique<CBC_QRCodeWriter>()) {}
 
-CBC_QRCode::~CBC_QRCode() {}
+CBC_QRCode::~CBC_QRCode() = default;
 
-bool CBC_QRCode::SetErrorCorrectionLevel(int32_t level) {
-  if (level < 0 || level > 3)
-    return false;
-  return GetQRCodeWriter()->SetErrorCorrectionLevel(level);
-}
-
-bool CBC_QRCode::Encode(const WideStringView& contents) {
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
+bool CBC_QRCode::Encode(WideStringView contents) {
+  int32_t width;
+  int32_t height;
   CBC_QRCodeWriter* pWriter = GetQRCodeWriter();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(WideString(contents), pWriter->GetErrorCorrectionLevel(),
-                      outWidth, outHeight));
-  if (!data)
-    return false;
-  return pWriter->RenderResult(data.get(), outWidth, outHeight);
+  std::vector<uint8_t> data = pWriter->Encode(
+      contents, pWriter->error_correction_level(), &width, &height);
+  return pWriter->RenderResult(data, width, height);
 }
 
 bool CBC_QRCode::RenderDevice(CFX_RenderDevice* device,
diff --git a/fxbarcode/cbc_qrcode.h b/fxbarcode/cbc_qrcode.h
index 509de8a..c541b7e 100644
--- a/fxbarcode/cbc_qrcode.h
+++ b/fxbarcode/cbc_qrcode.h
@@ -9,24 +9,21 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
 #include "fxbarcode/cbc_codebase.h"
 
 class CBC_QRCodeWriter;
 
-class CBC_QRCode : public CBC_CodeBase {
+class CBC_QRCode final : public CBC_CodeBase {
  public:
   CBC_QRCode();
   ~CBC_QRCode() override;
 
   // CBC_CodeBase:
-  bool Encode(const WideStringView& contents) override;
+  bool Encode(WideStringView contents) override;
   bool RenderDevice(CFX_RenderDevice* device,
                     const CFX_Matrix* matrix) override;
   BC_TYPE GetType() override;
 
-  bool SetErrorCorrectionLevel(int32_t level);
-
  private:
   CBC_QRCodeWriter* GetQRCodeWriter();
 };
diff --git a/fxbarcode/cbc_upca.cpp b/fxbarcode/cbc_upca.cpp
index 69cedce..3a3e87f 100644
--- a/fxbarcode/cbc_upca.cpp
+++ b/fxbarcode/cbc_upca.cpp
@@ -26,60 +26,18 @@
 #include "fxbarcode/oned/BC_OnedUPCAWriter.h"
 #include "third_party/base/ptr_util.h"
 
-CBC_UPCA::CBC_UPCA() : CBC_OneCode(pdfium::MakeUnique<CBC_OnedUPCAWriter>()) {}
+CBC_UPCA::CBC_UPCA() : CBC_EANCode(pdfium::MakeUnique<CBC_OnedUPCAWriter>()) {}
 
-CBC_UPCA::~CBC_UPCA() {}
-
-WideString CBC_UPCA::Preprocess(const WideStringView& contents) {
-  CBC_OnedUPCAWriter* pWriter = GetOnedUPCAWriter();
-  WideString encodeContents = pWriter->FilterContents(contents);
-  int32_t length = encodeContents.GetLength();
-  if (length <= 11) {
-    for (int32_t i = 0; i < 11 - length; i++)
-      encodeContents = wchar_t('0') + encodeContents;
-
-    ByteString byteString = encodeContents.UTF8Encode();
-    int32_t checksum = pWriter->CalcChecksum(byteString);
-    byteString += checksum - 0 + '0';
-    encodeContents = byteString.UTF8Decode();
-  }
-  if (length > 12)
-    encodeContents = encodeContents.Left(12);
-
-  return encodeContents;
-}
-
-bool CBC_UPCA::Encode(const WideStringView& contents) {
-  if (contents.IsEmpty())
-    return false;
-
-  BCFORMAT format = BCFORMAT_UPC_A;
-  int32_t outWidth = 0;
-  int32_t outHeight = 0;
-  WideString encodeContents = Preprocess(contents);
-  ByteString byteString = encodeContents.UTF8Encode();
-  m_renderContents = encodeContents;
-
-  CBC_OnedUPCAWriter* pWriter = GetOnedUPCAWriter();
-  pWriter->Init();
-  std::unique_ptr<uint8_t, FxFreeDeleter> data(
-      pWriter->Encode(byteString, format, outWidth, outHeight));
-  if (!data)
-    return false;
-  return pWriter->RenderResult(encodeContents.AsStringView(), data.get(),
-                               outWidth);
-}
-
-bool CBC_UPCA::RenderDevice(CFX_RenderDevice* device,
-                            const CFX_Matrix* matrix) {
-  return GetOnedUPCAWriter()->RenderDeviceResult(
-      device, matrix, m_renderContents.AsStringView());
-}
+CBC_UPCA::~CBC_UPCA() = default;
 
 BC_TYPE CBC_UPCA::GetType() {
   return BC_UPCA;
 }
 
-CBC_OnedUPCAWriter* CBC_UPCA::GetOnedUPCAWriter() {
-  return static_cast<CBC_OnedUPCAWriter*>(m_pBCWriter.get());
+BCFORMAT CBC_UPCA::GetFormat() const {
+  return BCFORMAT_UPC_A;
+}
+
+size_t CBC_UPCA::GetMaxLength() const {
+  return 11;
 }
diff --git a/fxbarcode/cbc_upca.h b/fxbarcode/cbc_upca.h
index 29c4d7d..bf71fc9 100644
--- a/fxbarcode/cbc_upca.h
+++ b/fxbarcode/cbc_upca.h
@@ -9,26 +9,19 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
-#include "fxbarcode/cbc_onecode.h"
+#include "fxbarcode/cbc_eancode.h"
 
 class CBC_OnedUPCAWriter;
 
-class CBC_UPCA : public CBC_OneCode {
+class CBC_UPCA final : public CBC_EANCode {
  public:
   CBC_UPCA();
   ~CBC_UPCA() override;
 
-  // CBC_CodeBase:
-  bool Encode(const WideStringView& contents) override;
-  bool RenderDevice(CFX_RenderDevice* device,
-                    const CFX_Matrix* matrix) override;
+  // CBC_EANCode:
   BC_TYPE GetType() override;
-
- private:
-  CBC_OnedUPCAWriter* GetOnedUPCAWriter();
-  WideString Preprocess(const WideStringView& contents);
-  WideString m_renderContents;
+  BCFORMAT GetFormat() const override;
+  size_t GetMaxLength() const override;
 };
 
 #endif  // FXBARCODE_CBC_UPCA_H_
diff --git a/fxbarcode/cfx_barcode.cpp b/fxbarcode/cfx_barcode.cpp
new file mode 100644
index 0000000..54addad
--- /dev/null
+++ b/fxbarcode/cfx_barcode.cpp
@@ -0,0 +1,232 @@
+// 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 "fxbarcode/cfx_barcode.h"
+
+#include <memory>
+
+#include "fxbarcode/cbc_codabar.h"
+#include "fxbarcode/cbc_code128.h"
+#include "fxbarcode/cbc_code39.h"
+#include "fxbarcode/cbc_codebase.h"
+#include "fxbarcode/cbc_datamatrix.h"
+#include "fxbarcode/cbc_ean13.h"
+#include "fxbarcode/cbc_ean8.h"
+#include "fxbarcode/cbc_pdf417i.h"
+#include "fxbarcode/cbc_qrcode.h"
+#include "fxbarcode/cbc_upca.h"
+#include "fxbarcode/utils.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+std::unique_ptr<CBC_CodeBase> CreateBarCodeEngineObject(BC_TYPE type) {
+  switch (type) {
+    case BC_CODE39:
+      return pdfium::MakeUnique<CBC_Code39>();
+    case BC_CODABAR:
+      return pdfium::MakeUnique<CBC_Codabar>();
+    case BC_CODE128:
+      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_B);
+    case BC_CODE128_B:
+      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_B);
+    case BC_CODE128_C:
+      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_C);
+    case BC_EAN8:
+      return pdfium::MakeUnique<CBC_EAN8>();
+    case BC_UPCA:
+      return pdfium::MakeUnique<CBC_UPCA>();
+    case BC_EAN13:
+      return pdfium::MakeUnique<CBC_EAN13>();
+    case BC_QR_CODE:
+      return pdfium::MakeUnique<CBC_QRCode>();
+    case BC_PDF417:
+      return pdfium::MakeUnique<CBC_PDF417I>();
+    case BC_DATAMATRIX:
+      return pdfium::MakeUnique<CBC_DataMatrix>();
+    case BC_UNKNOWN:
+    default:
+      return nullptr;
+  }
+}
+
+}  // namespace
+
+CFX_Barcode::CFX_Barcode() {}
+
+CFX_Barcode::~CFX_Barcode() {}
+
+std::unique_ptr<CFX_Barcode> CFX_Barcode::Create(BC_TYPE type) {
+  auto barcode = pdfium::WrapUnique(new CFX_Barcode());  // Private ctor.
+  barcode->m_pBCEngine = CreateBarCodeEngineObject(type);
+  return barcode;
+}
+
+BC_TYPE CFX_Barcode::GetType() {
+  return m_pBCEngine ? m_pBCEngine->GetType() : BC_UNKNOWN;
+}
+
+bool CFX_Barcode::SetCharEncoding(BC_CHAR_ENCODING encoding) {
+  return m_pBCEngine && m_pBCEngine->SetCharEncoding(encoding);
+}
+
+bool CFX_Barcode::SetModuleHeight(int32_t moduleHeight) {
+  return m_pBCEngine && m_pBCEngine->SetModuleHeight(moduleHeight);
+}
+
+bool CFX_Barcode::SetModuleWidth(int32_t moduleWidth) {
+  return m_pBCEngine && m_pBCEngine->SetModuleWidth(moduleWidth);
+}
+
+bool CFX_Barcode::SetHeight(int32_t height) {
+  return m_pBCEngine && m_pBCEngine->SetHeight(height);
+}
+
+bool CFX_Barcode::SetWidth(int32_t width) {
+  return m_pBCEngine && m_pBCEngine->SetWidth(width);
+}
+
+bool CFX_Barcode::SetPrintChecksum(bool checksum) {
+  switch (GetType()) {
+    case BC_CODE39:
+    case BC_CODABAR:
+    case BC_CODE128:
+    case BC_CODE128_B:
+    case BC_CODE128_C:
+    case BC_EAN8:
+    case BC_EAN13:
+    case BC_UPCA:
+      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
+                                ->SetPrintChecksum(checksum),
+                            true)
+                         : false;
+    default:
+      return false;
+  }
+}
+
+bool CFX_Barcode::SetDataLength(int32_t length) {
+  switch (GetType()) {
+    case BC_CODE39:
+    case BC_CODABAR:
+    case BC_CODE128:
+    case BC_CODE128_B:
+    case BC_CODE128_C:
+    case BC_EAN8:
+    case BC_EAN13:
+    case BC_UPCA:
+      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
+                                ->SetDataLength(length),
+                            true)
+                         : false;
+    default:
+      return false;
+  }
+}
+
+bool CFX_Barcode::SetCalChecksum(bool state) {
+  switch (GetType()) {
+    case BC_CODE39:
+    case BC_CODABAR:
+    case BC_CODE128:
+    case BC_CODE128_B:
+    case BC_CODE128_C:
+    case BC_EAN8:
+    case BC_EAN13:
+    case BC_UPCA:
+      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
+                                ->SetCalChecksum(state),
+                            true)
+                         : false;
+    default:
+      return false;
+  }
+}
+
+bool CFX_Barcode::SetFont(CFX_Font* pFont) {
+  switch (GetType()) {
+    case BC_CODE39:
+    case BC_CODABAR:
+    case BC_CODE128:
+    case BC_CODE128_B:
+    case BC_CODE128_C:
+    case BC_EAN8:
+    case BC_EAN13:
+    case BC_UPCA:
+      return m_pBCEngine
+                 ? static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetFont(pFont)
+                 : false;
+    default:
+      return false;
+  }
+}
+
+bool CFX_Barcode::SetFontSize(float size) {
+  switch (GetType()) {
+    case BC_CODE39:
+    case BC_CODABAR:
+    case BC_CODE128:
+    case BC_CODE128_B:
+    case BC_CODE128_C:
+    case BC_EAN8:
+    case BC_EAN13:
+    case BC_UPCA:
+      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
+                                ->SetFontSize(size),
+                            true)
+                         : false;
+    default:
+      return false;
+  }
+}
+
+bool CFX_Barcode::SetFontColor(FX_ARGB color) {
+  switch (GetType()) {
+    case BC_CODE39:
+    case BC_CODABAR:
+    case BC_CODE128:
+    case BC_CODE128_B:
+    case BC_CODE128_C:
+    case BC_EAN8:
+    case BC_EAN13:
+    case BC_UPCA:
+      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
+                                ->SetFontColor(color),
+                            true)
+                         : false;
+    default:
+      return false;
+  }
+}
+
+bool CFX_Barcode::SetTextLocation(BC_TEXT_LOC location) {
+  return m_pBCEngine && m_pBCEngine->SetTextLocation(location);
+}
+
+bool CFX_Barcode::SetWideNarrowRatio(int8_t ratio) {
+  return m_pBCEngine && m_pBCEngine->SetWideNarrowRatio(ratio);
+}
+
+bool CFX_Barcode::SetStartChar(char start) {
+  return m_pBCEngine && m_pBCEngine->SetStartChar(start);
+}
+
+bool CFX_Barcode::SetEndChar(char end) {
+  return m_pBCEngine && m_pBCEngine->SetEndChar(end);
+}
+
+bool CFX_Barcode::SetErrorCorrectionLevel(int32_t level) {
+  return m_pBCEngine && m_pBCEngine->SetErrorCorrectionLevel(level);
+}
+
+bool CFX_Barcode::Encode(WideStringView contents) {
+  return m_pBCEngine && m_pBCEngine->Encode(contents);
+}
+
+bool CFX_Barcode::RenderDevice(CFX_RenderDevice* device,
+                               const CFX_Matrix* matrix) {
+  return m_pBCEngine && m_pBCEngine->RenderDevice(device, matrix);
+}
diff --git a/fxbarcode/cfx_barcode.h b/fxbarcode/cfx_barcode.h
new file mode 100644
index 0000000..14204ea
--- /dev/null
+++ b/fxbarcode/cfx_barcode.h
@@ -0,0 +1,62 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXBARCODE_CFX_BARCODE_H_
+#define FXBARCODE_CFX_BARCODE_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxge/fx_dib.h"
+#include "fxbarcode/BC_Library.h"
+
+class CBC_CodeBase;
+class CFX_Font;
+class CFX_RenderDevice;
+class CFX_Matrix;
+
+class CFX_Barcode {
+ public:
+  ~CFX_Barcode();
+
+  static std::unique_ptr<CFX_Barcode> Create(BC_TYPE type);
+  BC_TYPE GetType();
+  bool Encode(WideStringView contents);
+
+  bool RenderDevice(CFX_RenderDevice* device, const CFX_Matrix* matrix);
+
+  bool SetCharEncoding(BC_CHAR_ENCODING encoding);
+
+  bool SetModuleHeight(int32_t moduleHeight);
+  bool SetModuleWidth(int32_t moduleWidth);
+
+  bool SetHeight(int32_t height);
+  bool SetWidth(int32_t width);
+
+  bool SetPrintChecksum(bool checksum);
+  bool SetDataLength(int32_t length);
+  bool SetCalChecksum(bool state);
+
+  bool SetFont(CFX_Font* pFont);
+  bool SetFontSize(float size);
+  bool SetFontColor(FX_ARGB color);
+
+  bool SetTextLocation(BC_TEXT_LOC location);
+
+  bool SetWideNarrowRatio(int8_t ratio);
+  bool SetStartChar(char start);
+  bool SetEndChar(char end);
+  bool SetErrorCorrectionLevel(int32_t level);
+
+ private:
+  CFX_Barcode();
+
+  std::unique_ptr<CBC_CodeBase> m_pBCEngine;
+};
+
+#endif  // FXBARCODE_CFX_BARCODE_H_
diff --git a/fxbarcode/cfx_barcode_unittest.cpp b/fxbarcode/cfx_barcode_unittest.cpp
new file mode 100644
index 0000000..7569bf2
--- /dev/null
+++ b/fxbarcode/cfx_barcode_unittest.cpp
@@ -0,0 +1,237 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxbarcode/cfx_barcode.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/utils/bitmap_saver.h"
+#include "testing/utils/hash.h"
+#include "third_party/base/ptr_util.h"
+
+class BarcodeTest : public testing::Test {
+ public:
+  void SetUp() override {
+    BC_Library_Init();
+
+    auto device = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
+    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+    if (bitmap->Create(640, 480, FXDIB_Rgb32))
+      bitmap_ = bitmap;
+    ASSERT_TRUE(bitmap_);
+    ASSERT_TRUE(device->Attach(bitmap_, false, nullptr, false));
+    device_ = std::move(device);
+  }
+
+  void TearDown() override {
+    bitmap_.Reset();
+    device_.reset();
+    barcode_.reset();
+    BC_Library_Destroy();
+  }
+
+  CFX_Barcode* barcode() const { return barcode_.get(); }
+
+  void Create(BC_TYPE type) {
+    barcode_ = CFX_Barcode::Create(type);
+    barcode_->SetHeight(298);
+    barcode_->SetWidth(418);
+  }
+
+  bool RenderDevice() {
+    return barcode_->RenderDevice(device_.get(), &matrix_);
+  }
+
+  std::string BitmapChecksum() {
+    return GenerateMD5Base16(bitmap_->GetBuffer(),
+                             bitmap_->GetPitch() * bitmap_->GetHeight());
+  }
+
+  // Manually insert calls to this as needed for debugging.
+  void SaveBitmap(const std::string& filename) {
+    BitmapSaver::WriteBitmapToPng(bitmap_.Get(), filename);
+  }
+
+ protected:
+  CFX_Matrix matrix_;
+  std::unique_ptr<CFX_Barcode> barcode_;
+  std::unique_ptr<CFX_RenderDevice> device_;
+  RetainPtr<CFX_DIBitmap> bitmap_;
+};
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Code39 DISABLED_Code39
+#else
+#define MAYBE_Code39 Code39
+#endif
+TEST_F(BarcodeTest, MAYBE_Code39) {
+  Create(BC_CODE39);
+  EXPECT_TRUE(barcode()->Encode(L"CLAMS"));
+  RenderDevice();
+  EXPECT_EQ("cd4cd3f36da38ff58d9f621827018903", BitmapChecksum());
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_CodaBar DISABLED_CodaBar
+#else
+#define MAYBE_CodaBar CodaBar
+#endif
+TEST_F(BarcodeTest, MAYBE_CodaBar) {
+  Create(BC_CODABAR);
+  EXPECT_TRUE(barcode()->Encode(L"$123-456"));
+  RenderDevice();
+  EXPECT_EQ("5fad4fc19f099001a0fe83c89430c977", BitmapChecksum());
+}
+
+TEST_F(BarcodeTest, DISABLED_CodaBarLetters) {
+  Create(BC_CODABAR);
+  EXPECT_FALSE(barcode()->Encode(L"clams"));
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Code128 DISABLED_Code128
+#else
+#define MAYBE_Code128 Code128
+#endif
+TEST_F(BarcodeTest, MAYBE_Code128) {
+  Create(BC_CODE128);
+  EXPECT_TRUE(barcode()->Encode(L"Clams"));
+  RenderDevice();
+  EXPECT_EQ("6351f0f6e997050e4658bbb4777aef74", BitmapChecksum());
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Code128B DISABLED_Code128B
+#else
+#define MAYBE_Code128B Code128B
+#endif
+TEST_F(BarcodeTest, MAYBE_Code128B) {
+  Create(BC_CODE128_B);
+  EXPECT_TRUE(barcode()->Encode(L"Clams"));
+  RenderDevice();
+  EXPECT_EQ("6351f0f6e997050e4658bbb4777aef74", BitmapChecksum());
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Code128C DISABLED_Code128C
+#else
+#define MAYBE_Code128C Code128C
+#endif
+TEST_F(BarcodeTest, MAYBE_Code128C) {
+  Create(BC_CODE128_C);
+  EXPECT_TRUE(barcode()->Encode(L"123456"));
+  RenderDevice();
+  EXPECT_EQ("fba730a807ba6363f9bd2bc7f8c56d1f", BitmapChecksum());
+}
+
+TEST_F(BarcodeTest, DISABLED_Code128CLetters) {
+  Create(BC_CODE128_C);
+  EXPECT_FALSE(barcode()->Encode(L"clams"));
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Ean8 DISABLED_Ean8
+#else
+#define MAYBE_Ean8 Ean8
+#endif
+TEST_F(BarcodeTest, MAYBE_Ean8) {
+  Create(BC_EAN8);
+  EXPECT_TRUE(barcode()->Encode(L"123456"));
+  RenderDevice();
+  EXPECT_EQ("aff88491ac46ca6217d780d185300cde", BitmapChecksum());
+}
+
+TEST_F(BarcodeTest, DISABLED_Ean8Letters) {
+  Create(BC_EAN8);
+  EXPECT_FALSE(barcode()->Encode(L"clams"));
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_UPCA DISABLED_UPCA
+#else
+#define MAYBE_UPCA UPCA
+#endif
+TEST_F(BarcodeTest, MAYBE_UPCA) {
+  Create(BC_UPCA);
+  EXPECT_TRUE(barcode()->Encode(L"123456"));
+  RenderDevice();
+  EXPECT_EQ("fe26a5714cff7ffe3f9b02183efc435b", BitmapChecksum());
+}
+
+TEST_F(BarcodeTest, DISABLED_UPCALetters) {
+  Create(BC_UPCA);
+  EXPECT_FALSE(barcode()->Encode(L"clams"));
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Ean13 DISABLED_Ean13
+#else
+#define MAYBE_Ean13 Ean13
+#endif
+TEST_F(BarcodeTest, MAYBE_Ean13) {
+  Create(BC_EAN13);
+  EXPECT_TRUE(barcode()->Encode(L"123456"));
+  RenderDevice();
+  EXPECT_EQ("72d2190b98d635c32834bf67552e561e", BitmapChecksum());
+}
+
+TEST_F(BarcodeTest, DISABLED_Ean13Letters) {
+  Create(BC_EAN13);
+  EXPECT_FALSE(barcode()->Encode(L"clams"));
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_Pdf417 DISABLED_Pdf417
+#else
+#define MAYBE_Pdf417 Pdf417
+#endif
+TEST_F(BarcodeTest, MAYBE_Pdf417) {
+  Create(BC_PDF417);
+  EXPECT_TRUE(barcode()->Encode(L"clams"));
+  RenderDevice();
+  EXPECT_EQ("191e35d11613901b7d5d51033689aa89", BitmapChecksum());
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_DataMatrix DISABLED_DataMatrix
+#else
+#define MAYBE_DataMatrix DataMatrix
+#endif
+TEST_F(BarcodeTest, MAYBE_DataMatrix) {
+  Create(BC_DATAMATRIX);
+  EXPECT_TRUE(barcode()->Encode(L"clams"));
+  RenderDevice();
+  EXPECT_EQ("5e5cd9a680b86fcd4ffd53ed36e3c980", BitmapChecksum());
+}
+
+// https://crbug.com/pdfium/738
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_QrCode DISABLED_QrCode
+#else
+#define MAYBE_QrCode QrCode
+#endif
+TEST_F(BarcodeTest, MAYBE_QrCode) {
+  Create(BC_QR_CODE);
+  EXPECT_TRUE(barcode()->Encode(L"clams"));
+  RenderDevice();
+  EXPECT_EQ("4751c6e0f67749fabe24f787128decee", BitmapChecksum());
+}
diff --git a/fxbarcode/common/BC_CommonBitArray.cpp b/fxbarcode/common/BC_CommonBitArray.cpp
deleted file mode 100644
index 10da9be..0000000
--- a/fxbarcode/common/BC_CommonBitArray.cpp
+++ /dev/null
@@ -1,90 +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
-// Original code is licensed as follows:
-/*
- * Copyright 2007 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "fxbarcode/common/BC_CommonBitArray.h"
-
-#include <utility>
-
-#include "fxbarcode/utils.h"
-
-CBC_CommonBitArray::CBC_CommonBitArray(CBC_CommonBitArray* array) {
-  m_size = array->GetSize();
-  m_bits = array->GetBits();
-}
-
-CBC_CommonBitArray::CBC_CommonBitArray() {
-  m_bits.resize(1);
-  m_size = 0;
-}
-
-CBC_CommonBitArray::CBC_CommonBitArray(int32_t size) {
-  m_bits.resize((size + 31) >> 5);
-  m_size = size;
-}
-
-CBC_CommonBitArray::~CBC_CommonBitArray() {}
-
-size_t CBC_CommonBitArray::GetSize() {
-  return m_size;
-}
-
-std::vector<int32_t>& CBC_CommonBitArray::GetBits() {
-  return m_bits;
-}
-
-size_t CBC_CommonBitArray::GetSizeInBytes() {
-  return (m_size + 7) >> 3;
-}
-
-bool CBC_CommonBitArray::Get(size_t i) {
-  return (m_bits[i >> 5] & (1 << (i & 0x1f))) != 0;
-}
-
-void CBC_CommonBitArray::Set(size_t i) {
-  m_bits[i >> 5] |= 1 << (i & 0x1F);
-}
-
-void CBC_CommonBitArray::Flip(size_t i) {
-  m_bits[i >> 5] ^= 1 << (i & 0x1F);
-}
-
-void CBC_CommonBitArray::SetBulk(size_t i, int32_t newBits) {
-  m_bits[i >> 5] = newBits;
-}
-
-void CBC_CommonBitArray::Clear() {
-  for (auto& value : m_bits)
-    value = 0;
-}
-
-int32_t* CBC_CommonBitArray::GetBitArray() {
-  return m_bits.data();
-}
-
-void CBC_CommonBitArray::Reverse() {
-  std::vector<int32_t> newBits(m_bits.size());
-  for (size_t i = 0; i < m_size; i++) {
-    if (Get(m_size - i - 1))
-      newBits[i >> 5] |= 1 << (i & 0x1F);
-  }
-  m_bits = std::move(newBits);
-}
diff --git a/fxbarcode/common/BC_CommonBitArray.h b/fxbarcode/common/BC_CommonBitArray.h
deleted file mode 100644
index 163db86..0000000
--- a/fxbarcode/common/BC_CommonBitArray.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXBARCODE_COMMON_BC_COMMONBITARRAY_H_
-#define FXBARCODE_COMMON_BC_COMMONBITARRAY_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-class CBC_CommonBitArray {
- public:
-  explicit CBC_CommonBitArray(CBC_CommonBitArray* array);
-  explicit CBC_CommonBitArray(int32_t size);
-  CBC_CommonBitArray();
-  virtual ~CBC_CommonBitArray();
-
-  size_t GetSize();
-  size_t GetSizeInBytes();
-  std::vector<int32_t>& GetBits();
-  int32_t* GetBitArray();
-  bool Get(size_t i);
-  void Set(size_t i);
-  void Flip(size_t i);
-  void SetBulk(size_t i, int32_t newBits);
-  void Reverse();
-  void Clear();
-
- private:
-  size_t m_size;
-  std::vector<int32_t> m_bits;
-};
-
-#endif  // FXBARCODE_COMMON_BC_COMMONBITARRAY_H_
diff --git a/fxbarcode/common/BC_CommonBitMatrix.cpp b/fxbarcode/common/BC_CommonBitMatrix.cpp
index 6fe447d..4cf07ed 100644
--- a/fxbarcode/common/BC_CommonBitMatrix.cpp
+++ b/fxbarcode/common/BC_CommonBitMatrix.cpp
@@ -22,33 +22,21 @@
 
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 
-#include "fxbarcode/common/BC_CommonBitArray.h"
-#include "fxbarcode/utils.h"
-#include "third_party/base/ptr_util.h"
+#include <algorithm>
+#include <iterator>
+
+#include "third_party/base/stl_util.h"
 
 CBC_CommonBitMatrix::CBC_CommonBitMatrix() {}
 
-void CBC_CommonBitMatrix::Init(int32_t dimension) {
-  m_width = dimension;
-  m_height = dimension;
-  int32_t rowSize = (m_height + 31) >> 5;
-  m_rowSize = rowSize;
-  m_bits = FX_Alloc2D(int32_t, m_rowSize, m_height);
-  memset(m_bits, 0, m_rowSize * m_height * sizeof(int32_t));
-}
-
 void CBC_CommonBitMatrix::Init(int32_t width, int32_t height) {
   m_width = width;
   m_height = height;
-  int32_t rowSize = (width + 31) >> 5;
-  m_rowSize = rowSize;
-  m_bits = FX_Alloc2D(int32_t, m_rowSize, m_height);
-  memset(m_bits, 0, m_rowSize * m_height * sizeof(int32_t));
+  m_rowSize = (width + 31) >> 5;
+  m_bits = pdfium::Vector2D<int32_t>(m_rowSize, m_height);
 }
 
-CBC_CommonBitMatrix::~CBC_CommonBitMatrix() {
-  FX_Free(m_bits);
-}
+CBC_CommonBitMatrix::~CBC_CommonBitMatrix() = default;
 
 bool CBC_CommonBitMatrix::Get(int32_t x, int32_t y) const {
   int32_t offset = y * m_rowSize + (x >> 5);
@@ -59,49 +47,7 @@
 
 void CBC_CommonBitMatrix::Set(int32_t x, int32_t y) {
   int32_t offset = y * m_rowSize + (x >> 5);
-  if (offset >= m_rowSize * m_height || offset < 0)
-    return;
+  ASSERT(offset >= 0);
+  ASSERT(offset < m_rowSize * m_height);
   m_bits[offset] |= 1 << (x & 0x1f);
 }
-
-void CBC_CommonBitMatrix::Flip(int32_t x, int32_t y) {
-  int32_t offset = y * m_rowSize + (x >> 5);
-  m_bits[offset] ^= 1 << (x & 0x1f);
-}
-
-void CBC_CommonBitMatrix::Clear() {
-  memset(m_bits, 0, m_rowSize * m_height * sizeof(int32_t));
-}
-
-bool CBC_CommonBitMatrix::SetRegion(int32_t left,
-                                    int32_t top,
-                                    int32_t width,
-                                    int32_t height) {
-  if (top < 0 || left < 0 || height < 1 || width < 1)
-    return false;
-
-  int32_t right = left + width;
-  int32_t bottom = top + height;
-  if (m_height < bottom || m_width < right)
-    return false;
-
-  for (int32_t y = top; y < bottom; y++) {
-    int32_t offset = y * m_rowSize;
-    for (int32_t x = left; x < right; x++)
-      m_bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
-  }
-  return true;
-}
-
-void CBC_CommonBitMatrix::SetRow(int32_t y, CBC_CommonBitArray* row) {
-  int32_t l = y * m_rowSize;
-  for (int32_t i = 0; i < m_rowSize; i++) {
-    m_bits[l] = row->GetBitArray()[i];
-    l++;
-  }
-}
-
-void CBC_CommonBitMatrix::SetCol(int32_t y, CBC_CommonBitArray* col) {
-  for (size_t i = 0; i < col->GetBits().size(); ++i)
-    m_bits[i * m_rowSize + y] = col->GetBitArray()[i];
-}
diff --git a/fxbarcode/common/BC_CommonBitMatrix.h b/fxbarcode/common/BC_CommonBitMatrix.h
index 67c0f88..ce40ade 100644
--- a/fxbarcode/common/BC_CommonBitMatrix.h
+++ b/fxbarcode/common/BC_CommonBitMatrix.h
@@ -7,37 +7,27 @@
 #ifndef FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_
 #define FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_
 
-#include <memory>
+#include <vector>
 
 #include "core/fxcrt/fx_system.h"
 
-class CBC_CommonBitArray;
-
 class CBC_CommonBitMatrix {
  public:
   CBC_CommonBitMatrix();
   ~CBC_CommonBitMatrix();
 
-  void Init(int32_t dimension);
   void Init(int32_t width, int32_t height);
 
   bool Get(int32_t x, int32_t y) const;
   void Set(int32_t x, int32_t y);
-  void Flip(int32_t x, int32_t y);
-  void Clear();
-  bool SetRegion(int32_t left, int32_t top, int32_t width, int32_t height);
-  void SetRow(int32_t y, CBC_CommonBitArray* row);
-  CBC_CommonBitArray* GetCol(int32_t y, CBC_CommonBitArray* row);
-  void SetCol(int32_t y, CBC_CommonBitArray* col);
   int32_t GetWidth() const { return m_width; }
   int32_t GetHeight() const { return m_height; }
-  int32_t* GetBits() const { return m_bits; }
 
  private:
   int32_t m_width = 0;
   int32_t m_height = 0;
   int32_t m_rowSize = 0;
-  int32_t* m_bits = nullptr;
+  std::vector<int32_t> m_bits;
 };
 
 #endif  // FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_
diff --git a/fxbarcode/common/BC_CommonByteArray.cpp b/fxbarcode/common/BC_CommonByteArray.cpp
deleted file mode 100644
index d66568c..0000000
--- a/fxbarcode/common/BC_CommonByteArray.cpp
+++ /dev/null
@@ -1,102 +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
-// Original code is licensed as follows:
-/*
- * Copyright 2008 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "fxbarcode/common/BC_CommonByteArray.h"
-
-#include <algorithm>
-
-#include "core/fxcrt/fx_memory.h"
-
-CBC_CommonByteArray::CBC_CommonByteArray() {
-  m_bytes = nullptr;
-  m_size = 0;
-  m_index = 0;
-}
-CBC_CommonByteArray::CBC_CommonByteArray(int32_t size) {
-  m_size = size;
-  m_bytes = FX_Alloc(uint8_t, size);
-  memset(m_bytes, 0, size);
-  m_index = 0;
-}
-CBC_CommonByteArray::CBC_CommonByteArray(uint8_t* byteArray, int32_t size) {
-  m_size = size;
-  m_bytes = FX_Alloc(uint8_t, size);
-  memcpy(m_bytes, byteArray, size);
-  m_index = size;
-}
-CBC_CommonByteArray::~CBC_CommonByteArray() {
-  FX_Free(m_bytes);
-}
-int32_t CBC_CommonByteArray::At(int32_t index) const {
-  return m_bytes[index] & 0xff;
-}
-void CBC_CommonByteArray::Set(int32_t index, int32_t value) {
-  m_bytes[index] = (uint8_t)value;
-}
-int32_t CBC_CommonByteArray::Size() const {
-  return m_size;
-}
-bool CBC_CommonByteArray::IsEmpty() const {
-  return m_size == 0;
-}
-void CBC_CommonByteArray::AppendByte(int32_t value) {
-  if (m_size == 0 || m_index >= m_size) {
-    int32_t newSize = std::max(32, m_size << 1);
-    Reserve(newSize);
-  }
-  m_bytes[m_index] = (uint8_t)value;
-  m_index++;
-}
-void CBC_CommonByteArray::Reserve(int32_t capacity) {
-  if (!m_bytes || m_size < capacity) {
-    uint8_t* newArray = FX_Alloc(uint8_t, capacity);
-    if (m_bytes) {
-      memcpy(newArray, m_bytes, m_size);
-      memset(newArray + m_size, 0, capacity - m_size);
-    } else {
-      memset(newArray, 0, capacity);
-    }
-    FX_Free(m_bytes);
-    m_bytes = newArray;
-    m_size = capacity;
-  }
-}
-void CBC_CommonByteArray::Set(const uint8_t* source,
-                              int32_t offset,
-                              int32_t count) {
-  FX_Free(m_bytes);
-  m_bytes = FX_Alloc(uint8_t, count);
-  m_size = count;
-  memcpy(m_bytes, source + offset, count);
-  m_index = count;
-}
-void CBC_CommonByteArray::Set(std::vector<uint8_t>* source,
-                              int32_t offset,
-                              int32_t count) {
-  FX_Free(m_bytes);
-  m_bytes = FX_Alloc(uint8_t, count);
-  m_size = count;
-  int32_t i;
-  for (i = 0; i < count; i++) {
-    m_bytes[i] = source->operator[](i + offset);
-  }
-  m_index = m_size;
-}
diff --git a/fxbarcode/common/BC_CommonByteArray.h b/fxbarcode/common/BC_CommonByteArray.h
deleted file mode 100644
index 285a567..0000000
--- a/fxbarcode/common/BC_CommonByteArray.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXBARCODE_COMMON_BC_COMMONBYTEARRAY_H_
-#define FXBARCODE_COMMON_BC_COMMONBYTEARRAY_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-// TODO(weili): The usage of this class should be replaced by
-// std::vector<uint8_t>.
-class CBC_CommonByteArray {
- public:
-  CBC_CommonByteArray();
-  explicit CBC_CommonByteArray(int32_t size);
-  CBC_CommonByteArray(uint8_t* byteArray, int32_t size);
-  virtual ~CBC_CommonByteArray();
-
-  int32_t At(int32_t index) const;
-  int32_t Size() const;
-  bool IsEmpty() const;
-  void Set(int32_t index, int32_t value);
-  void AppendByte(int32_t value);
-  void Reserve(int32_t capacity);
-  void Set(const uint8_t* source, int32_t offset, int32_t count);
-  void Set(std::vector<uint8_t>* source, int32_t offset, int32_t count);
-
- private:
-  int32_t m_size;
-  int32_t m_index;
-  uint8_t* m_bytes;
-};
-
-#endif  // FXBARCODE_COMMON_BC_COMMONBYTEARRAY_H_
diff --git a/fxbarcode/common/BC_CommonByteMatrix.cpp b/fxbarcode/common/BC_CommonByteMatrix.cpp
index 2ab1e9a..a05fde0 100644
--- a/fxbarcode/common/BC_CommonByteMatrix.cpp
+++ b/fxbarcode/common/BC_CommonByteMatrix.cpp
@@ -19,46 +19,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#include "core/fxcrt/fx_memory.h"
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 
-CBC_CommonByteMatrix::CBC_CommonByteMatrix(int32_t width, int32_t height) {
-  m_height = height;
-  m_width = width;
-  m_bytes = nullptr;
+#include <algorithm>
+#include <iterator>
+#include "third_party/base/stl_util.h"
+
+CBC_CommonByteMatrix::CBC_CommonByteMatrix(int32_t width, int32_t height)
+    : m_width(width), m_height(height) {
+  m_bytes = pdfium::Vector2D<uint8_t>(m_height, m_width);
+  clear(0xff);
 }
-void CBC_CommonByteMatrix::Init() {
-  m_bytes = FX_Alloc2D(uint8_t, m_height, m_width);
-  memset(m_bytes, 0xff, m_height * m_width);
-}
-CBC_CommonByteMatrix::~CBC_CommonByteMatrix() {
-  FX_Free(m_bytes);
-}
-int32_t CBC_CommonByteMatrix::GetHeight() {
-  return m_height;
-}
-int32_t CBC_CommonByteMatrix::GetWidth() {
-  return m_width;
-}
-uint8_t CBC_CommonByteMatrix::Get(int32_t x, int32_t y) {
+
+CBC_CommonByteMatrix::~CBC_CommonByteMatrix() = default;
+
+uint8_t CBC_CommonByteMatrix::Get(int32_t x, int32_t y) const {
   return m_bytes[y * m_width + x];
 }
+
 void CBC_CommonByteMatrix::Set(int32_t x, int32_t y, int32_t value) {
   m_bytes[y * m_width + x] = (uint8_t)value;
 }
+
 void CBC_CommonByteMatrix::Set(int32_t x, int32_t y, uint8_t value) {
   m_bytes[y * m_width + x] = value;
 }
+
 void CBC_CommonByteMatrix::clear(uint8_t value) {
-  int32_t y;
-  for (y = 0; y < m_height; y++) {
-    int32_t x;
-    for (x = 0; x < m_width; x++) {
-      m_bytes[y * m_width + x] = value;
-    }
-  }
-}
-uint8_t* CBC_CommonByteMatrix::GetArray() {
-  return m_bytes;
+  std::fill(std::begin(m_bytes), std::end(m_bytes), value);
 }
diff --git a/fxbarcode/common/BC_CommonByteMatrix.h b/fxbarcode/common/BC_CommonByteMatrix.h
index 9f13a37..c5a66a3 100644
--- a/fxbarcode/common/BC_CommonByteMatrix.h
+++ b/fxbarcode/common/BC_CommonByteMatrix.h
@@ -9,27 +9,29 @@
 
 #include <stdint.h>
 
-#include "core/fxcrt/fx_system.h"
+#include <vector>
 
-class CBC_CommonByteMatrix {
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/span.h"
+
+class CBC_CommonByteMatrix final {
  public:
   CBC_CommonByteMatrix(int32_t width, int32_t height);
-  virtual ~CBC_CommonByteMatrix();
+  ~CBC_CommonByteMatrix();
 
-  int32_t GetHeight();
-  int32_t GetWidth();
-  uint8_t Get(int32_t x, int32_t y);
-  uint8_t* GetArray();
+  int32_t GetWidth() const { return m_width; }
+  int32_t GetHeight() const { return m_height; }
+  pdfium::span<const uint8_t> GetArray() const { return m_bytes; }
+  uint8_t Get(int32_t x, int32_t y) const;
 
   void Set(int32_t x, int32_t y, int32_t value);
   void Set(int32_t x, int32_t y, uint8_t value);
   void clear(uint8_t value);
-  virtual void Init();
 
  private:
-  uint8_t* m_bytes;
   int32_t m_width;
   int32_t m_height;
+  std::vector<uint8_t> m_bytes;
 };
 
 #endif  // FXBARCODE_COMMON_BC_COMMONBYTEMATRIX_H_
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp
index 2be74c7..f19886e 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp
@@ -30,25 +30,20 @@
 #include "third_party/base/ptr_util.h"
 
 CBC_ReedSolomonEncoder::CBC_ReedSolomonEncoder(CBC_ReedSolomonGF256* field)
-    : m_field(field) {}
+    : m_field(field) {
+  m_cachedGenerators.push_back(pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(
+      m_field.Get(), std::vector<int32_t>{1}));
+}
 
 CBC_ReedSolomonEncoder::~CBC_ReedSolomonEncoder() {}
 
-void CBC_ReedSolomonEncoder::Init() {
-  m_cachedGenerators.push_back(
-      pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(), 1));
-}
-
 CBC_ReedSolomonGF256Poly* CBC_ReedSolomonEncoder::BuildGenerator(
     size_t degree) {
   if (degree >= m_cachedGenerators.size()) {
     CBC_ReedSolomonGF256Poly* lastGenerator = m_cachedGenerators.back().get();
     for (size_t d = m_cachedGenerators.size(); d <= degree; ++d) {
-      std::vector<int32_t> temp = {1, m_field->Exp(d - 1)};
-      CBC_ReedSolomonGF256Poly temp_poly;
-      if (!temp_poly.Init(m_field.Get(), &temp))
-        return nullptr;
-
+      CBC_ReedSolomonGF256Poly temp_poly(m_field.Get(),
+                                         {1, m_field->Exp(d - 1)});
       auto nextGenerator = lastGenerator->Multiply(&temp_poly);
       if (!nextGenerator)
         return nullptr;
@@ -77,10 +72,7 @@
   for (size_t x = 0; x < dataBytes; x++)
     infoCoefficients[x] = (*toEncode)[x];
 
-  CBC_ReedSolomonGF256Poly info;
-  if (!info.Init(m_field.Get(), &infoCoefficients))
-    return false;
-
+  CBC_ReedSolomonGF256Poly info(m_field.Get(), infoCoefficients);
   auto infoTemp = info.MultiplyByMonomial(ecBytes, 1);
   if (!infoTemp)
     return false;
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomon.h b/fxbarcode/common/reedsolomon/BC_ReedSolomon.h
index e45c8a4..772ca3c 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomon.h
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomon.h
@@ -20,7 +20,6 @@
   explicit CBC_ReedSolomonEncoder(CBC_ReedSolomonGF256* field);
   ~CBC_ReedSolomonEncoder();
 
-  void Init();
   bool Encode(std::vector<int32_t>* toEncode, size_t ecBytes);
 
  private:
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp
index 3f76105..f2d86b3 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp
@@ -27,23 +27,6 @@
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h"
 #include "third_party/base/ptr_util.h"
 
-CBC_ReedSolomonGF256* CBC_ReedSolomonGF256::QRCodeField = nullptr;
-CBC_ReedSolomonGF256* CBC_ReedSolomonGF256::DataMatrixField = nullptr;
-
-void CBC_ReedSolomonGF256::Initialize() {
-  QRCodeField = new CBC_ReedSolomonGF256(0x011D);
-  QRCodeField->Init();
-  DataMatrixField = new CBC_ReedSolomonGF256(0x012D);
-  DataMatrixField->Init();
-}
-
-void CBC_ReedSolomonGF256::Finalize() {
-  delete QRCodeField;
-  QRCodeField = nullptr;
-  delete DataMatrixField;
-  DataMatrixField = nullptr;
-}
-
 CBC_ReedSolomonGF256::CBC_ReedSolomonGF256(int32_t primitive) {
   int32_t x = 1;
   for (int32_t j = 0; j < 256; j++) {
@@ -60,44 +43,29 @@
 }
 
 void CBC_ReedSolomonGF256::Init() {
-  m_zero = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(this, 0);
-  m_one = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(this, 1);
+  m_zero = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(
+      this, std::vector<int32_t>{0});
+  m_one = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(this,
+                                                       std::vector<int32_t>{1});
 }
 
 CBC_ReedSolomonGF256::~CBC_ReedSolomonGF256() {}
 
-CBC_ReedSolomonGF256Poly* CBC_ReedSolomonGF256::GetZero() const {
-  return m_zero.get();
-}
-
-CBC_ReedSolomonGF256Poly* CBC_ReedSolomonGF256::GetOne() const {
-  return m_one.get();
-}
-
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256::BuildMonomial(
     int32_t degree,
-    int32_t coefficient,
-    int32_t& e) {
-  if (degree < 0) {
-    e = BCExceptionDegreeIsNegative;
+    int32_t coefficient) {
+  if (degree < 0)
     return nullptr;
-  }
-  if (coefficient == 0) {
-    auto temp = m_zero->Clone();
-    if (!temp)
-      e = BCExceptionGeneric;
-    return temp;
-  }
+
+  if (coefficient == 0)
+    return m_zero->Clone();
+
   std::vector<int32_t> coefficients(degree + 1);
   coefficients[0] = coefficient;
-  auto temp = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>();
-  if (!temp->Init(this, &coefficients)) {
-    e = BCExceptionGeneric;
-    return nullptr;
-  }
-  return temp;
+  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(this, coefficients);
 }
 
+// static
 int32_t CBC_ReedSolomonGF256::AddOrSubtract(int32_t a, int32_t b) {
   return a ^ b;
 }
@@ -106,19 +74,9 @@
   return m_expTable[a];
 }
 
-int32_t CBC_ReedSolomonGF256::Log(int32_t a, int32_t& e) {
-  if (a == 0) {
-    e = BCExceptionAIsZero;
-    return 0;
-  }
-  return m_logTable[a];
-}
-
-int32_t CBC_ReedSolomonGF256::Inverse(int32_t a, int32_t& e) {
-  if (a == 0) {
-    e = BCExceptionAIsZero;
-    return 0;
-  }
+Optional<int32_t> CBC_ReedSolomonGF256::Inverse(int32_t a) {
+  if (a == 0)
+    return {};
   return m_expTable[255 - m_logTable[a]];
 }
 
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h
index 693d943..30f5524 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h
@@ -9,7 +9,7 @@
 
 #include <memory>
 
-#include "fxbarcode/utils.h"
+#include "third_party/base/optional.h"
 
 class CBC_ReedSolomonGF256Poly;
 
@@ -18,29 +18,22 @@
   explicit CBC_ReedSolomonGF256(int32_t primitive);
   ~CBC_ReedSolomonGF256();
 
-  static void Initialize();
-  static void Finalize();
+  CBC_ReedSolomonGF256Poly* GetZero() const { return m_zero.get(); }
+  CBC_ReedSolomonGF256Poly* GetOne() const { return m_one.get(); }
 
-  CBC_ReedSolomonGF256Poly* GetZero() const;
-  CBC_ReedSolomonGF256Poly* GetOne() const;
   std::unique_ptr<CBC_ReedSolomonGF256Poly> BuildMonomial(int32_t degree,
-                                                          int32_t coefficient,
-                                                          int32_t& e);
+                                                          int32_t coefficient);
   static int32_t AddOrSubtract(int32_t a, int32_t b);
   int32_t Exp(int32_t a);
-  int32_t Log(int32_t a, int32_t& e);
-  int32_t Inverse(int32_t a, int32_t& e);
+  Optional<int32_t> Inverse(int32_t a);
   int32_t Multiply(int32_t a, int32_t b);
   void Init();
 
-  static CBC_ReedSolomonGF256* QRCodeField;
-  static CBC_ReedSolomonGF256* DataMatrixField;
-
  private:
-  int32_t m_expTable[256];
-  int32_t m_logTable[256];
   std::unique_ptr<CBC_ReedSolomonGF256Poly> m_zero;
   std::unique_ptr<CBC_ReedSolomonGF256Poly> m_one;
+  int32_t m_expTable[256];
+  int32_t m_logTable[256];
 };
 
 #endif  // FXBARCODE_COMMON_REEDSOLOMON_BC_REEDSOLOMONGF256_H_
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp
index ef83691..4339502 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp
@@ -25,49 +25,38 @@
 #include <memory>
 #include <utility>
 
+#include "core/fxcrt/fx_system.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
-CBC_ReedSolomonGF256Poly::CBC_ReedSolomonGF256Poly(CBC_ReedSolomonGF256* field,
-                                                   int32_t coefficients) {
-  if (!field)
+CBC_ReedSolomonGF256Poly::CBC_ReedSolomonGF256Poly(
+    CBC_ReedSolomonGF256* field,
+    const std::vector<int32_t>& coefficients)
+    : m_field(field) {
+  ASSERT(m_field);
+  ASSERT(!coefficients.empty());
+  if (coefficients.size() == 1 || coefficients.front() != 0) {
+    m_coefficients = coefficients;
     return;
-
-  m_field = field;
-  m_coefficients.push_back(coefficients);
-}
-
-CBC_ReedSolomonGF256Poly::CBC_ReedSolomonGF256Poly() {
-  m_field = nullptr;
-}
-
-bool CBC_ReedSolomonGF256Poly::Init(CBC_ReedSolomonGF256* field,
-                                    const std::vector<int32_t>* coefficients) {
-  if (!coefficients || coefficients->empty())
-    return false;
-
-  m_field = field;
-  size_t coefficientsLength = coefficients->size();
-  if (coefficientsLength > 1 && coefficients->front() == 0) {
-    size_t firstNonZero = 1;
-    while (firstNonZero < coefficientsLength &&
-           (*coefficients)[firstNonZero] == 0) {
-      firstNonZero++;
-    }
-    if (firstNonZero == coefficientsLength) {
-      m_coefficients = m_field->GetZero()->GetCoefficients();
-    } else {
-      m_coefficients.resize(coefficientsLength - firstNonZero);
-      for (size_t i = firstNonZero, j = 0; i < coefficientsLength; i++, j++)
-        m_coefficients[j] = (*coefficients)[i];
-    }
-  } else {
-    m_coefficients = *coefficients;
   }
-  return true;
+
+  size_t firstNonZero = 1;
+  while (firstNonZero < coefficients.size() &&
+         coefficients[firstNonZero] == 0) {
+    firstNonZero++;
+  }
+  if (firstNonZero == coefficients.size()) {
+    m_coefficients = m_field->GetZero()->GetCoefficients();
+  } else {
+    m_coefficients.resize(coefficients.size() - firstNonZero);
+    for (size_t i = firstNonZero, j = 0; i < coefficients.size(); i++, j++)
+      m_coefficients[j] = coefficients[i];
+  }
 }
 
+CBC_ReedSolomonGF256Poly::~CBC_ReedSolomonGF256Poly() = default;
+
 const std::vector<int32_t>& CBC_ReedSolomonGF256Poly::GetCoefficients() const {
   return m_coefficients;
 }
@@ -84,31 +73,10 @@
   return m_coefficients[m_coefficients.size() - 1 - degree];
 }
 
-int32_t CBC_ReedSolomonGF256Poly::EvaluateAt(int32_t a) {
-  if (a == 0)
-    return GetCoefficients(0);
-
-  size_t size = m_coefficients.size();
-  if (a == 1) {
-    int32_t result = 0;
-    for (size_t i = 0; i < size; i++)
-      result = CBC_ReedSolomonGF256::AddOrSubtract(result, m_coefficients[i]);
-    return result;
-  }
-  int32_t result = m_coefficients[0];
-  for (size_t j = 1; j < size; j++) {
-    result = CBC_ReedSolomonGF256::AddOrSubtract(m_field->Multiply(a, result),
-                                                 m_coefficients[j]);
-  }
-  return result;
-}
-
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256Poly::Clone()
     const {
-  auto temp = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>();
-  if (!temp->Init(m_field.Get(), &m_coefficients))
-    return nullptr;
-  return temp;
+  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(),
+                                                      m_coefficients);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly>
@@ -125,17 +93,14 @@
 
   std::vector<int32_t> sumDiff(largerCoefficients.size());
   size_t lengthDiff = largerCoefficients.size() - smallerCoefficients.size();
-  for (size_t i = 0; i < lengthDiff; i++)
+  for (size_t i = 0; i < lengthDiff; ++i)
     sumDiff[i] = largerCoefficients[i];
 
-  for (size_t j = lengthDiff; j < largerCoefficients.size(); j++) {
-    sumDiff[j] = CBC_ReedSolomonGF256::AddOrSubtract(
-        smallerCoefficients[j - lengthDiff], largerCoefficients[j]);
+  for (size_t i = lengthDiff; i < largerCoefficients.size(); ++i) {
+    sumDiff[i] = CBC_ReedSolomonGF256::AddOrSubtract(
+        smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
   }
-  auto temp = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>();
-  if (!temp->Init(m_field.Get(), &sumDiff))
-    return nullptr;
-  return temp;
+  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(), sumDiff);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256Poly::Multiply(
@@ -155,28 +120,7 @@
           product[i + j], m_field->Multiply(aCoeff, bCoefficients[j]));
     }
   }
-  auto temp = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>();
-  if (!temp->Init(m_field.Get(), &product))
-    return nullptr;
-  return temp;
-}
-
-std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256Poly::Multiply(
-    int32_t scalar) {
-  if (scalar == 0)
-    return m_field->GetZero()->Clone();
-  if (scalar == 1)
-    return Clone();
-
-  size_t size = m_coefficients.size();
-  std::vector<int32_t> product(size);
-  for (size_t i = 0; i < size; i++)
-    product[i] = m_field->Multiply(m_coefficients[i], scalar);
-
-  auto temp = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>();
-  if (!temp->Init(m_field.Get(), &product))
-    return nullptr;
-  return temp;
+  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(), product);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly>
@@ -192,10 +136,7 @@
   for (size_t i = 0; i < size; i++)
     product[i] = m_field->Multiply(m_coefficients[i], coefficient);
 
-  auto temp = pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>();
-  if (!temp->Init(m_field.Get(), &product))
-    return nullptr;
-  return temp;
+  return pdfium::MakeUnique<CBC_ReedSolomonGF256Poly>(m_field.Get(), product);
 }
 
 std::unique_ptr<CBC_ReedSolomonGF256Poly> CBC_ReedSolomonGF256Poly::Divide(
@@ -210,22 +151,22 @@
   if (!remainder)
     return nullptr;
 
-  int e = BCExceptionNO;
   int32_t denominatorLeadingTerm = other->GetCoefficients(other->GetDegree());
-  int32_t inverseDenominatorLeadingTeam =
-      m_field->Inverse(denominatorLeadingTerm, e);
-  if (e != BCExceptionNO)
+  Optional<int32_t> inverseDenominatorLeadingTeam =
+      m_field->Inverse(denominatorLeadingTerm);
+  if (!inverseDenominatorLeadingTeam.has_value())
     return nullptr;
+
   while (remainder->GetDegree() >= other->GetDegree() && !remainder->IsZero()) {
     int32_t degreeDifference = remainder->GetDegree() - other->GetDegree();
     int32_t scale =
         m_field->Multiply(remainder->GetCoefficients((remainder->GetDegree())),
-                          inverseDenominatorLeadingTeam);
+                          inverseDenominatorLeadingTeam.value());
     auto term = other->MultiplyByMonomial(degreeDifference, scale);
     if (!term)
       return nullptr;
-    auto iteratorQuotient = m_field->BuildMonomial(degreeDifference, scale, e);
-    if (e != BCExceptionNO)
+    auto iteratorQuotient = m_field->BuildMonomial(degreeDifference, scale);
+    if (!iteratorQuotient)
       return nullptr;
     quotient = quotient->AddOrSubtract(iteratorQuotient.get());
     if (!quotient)
@@ -236,5 +177,3 @@
   }
   return remainder;
 }
-
-CBC_ReedSolomonGF256Poly::~CBC_ReedSolomonGF256Poly() {}
diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h
index 810b478..7c3c7fc 100644
--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h
+++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h
@@ -16,22 +16,18 @@
 
 class CBC_ReedSolomonGF256Poly final {
  public:
-  CBC_ReedSolomonGF256Poly(CBC_ReedSolomonGF256* field, int32_t coefficients);
-  CBC_ReedSolomonGF256Poly();
+  CBC_ReedSolomonGF256Poly(CBC_ReedSolomonGF256* field,
+                           const std::vector<int32_t>& coefficients);
   ~CBC_ReedSolomonGF256Poly();
-  bool Init(CBC_ReedSolomonGF256* field,
-            const std::vector<int32_t>* coefficients);
 
   int32_t GetCoefficients(int32_t degree) const;
   const std::vector<int32_t>& GetCoefficients() const;
   int32_t GetDegree() const;
   bool IsZero() const;
-  int32_t EvaluateAt(int32_t a);
   std::unique_ptr<CBC_ReedSolomonGF256Poly> AddOrSubtract(
       const CBC_ReedSolomonGF256Poly* other);
   std::unique_ptr<CBC_ReedSolomonGF256Poly> Multiply(
       const CBC_ReedSolomonGF256Poly* other);
-  std::unique_ptr<CBC_ReedSolomonGF256Poly> Multiply(int32_t scalar);
   std::unique_ptr<CBC_ReedSolomonGF256Poly> MultiplyByMonomial(
       int32_t degree,
       int32_t coefficient) const;
@@ -40,7 +36,7 @@
   std::unique_ptr<CBC_ReedSolomonGF256Poly> Clone() const;
 
  private:
-  UnownedPtr<CBC_ReedSolomonGF256> m_field;
+  UnownedPtr<CBC_ReedSolomonGF256> const m_field;
   std::vector<int32_t> m_coefficients;
 };
 
diff --git a/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp b/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp
index e6b7a2b..ed51f81 100644
--- a/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp
@@ -22,76 +22,94 @@
 
 #include "fxbarcode/datamatrix/BC_ASCIIEncoder.h"
 
+#include "core/fxcrt/fx_extension.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
+#include "third_party/base/optional.h"
 
-CBC_ASCIIEncoder::CBC_ASCIIEncoder() {}
-CBC_ASCIIEncoder::~CBC_ASCIIEncoder() {}
-int32_t CBC_ASCIIEncoder::getEncodingMode() {
-  return ASCII_ENCODATION;
+namespace {
+
+Optional<wchar_t> EncodeASCIIDigits(wchar_t digit1, wchar_t digit2) {
+  if (!FXSYS_IsDecimalDigit(digit1) || !FXSYS_IsDecimalDigit(digit2)) {
+    // This could potentially return 0 as a sentinel value. Then this function
+    // can just return wchar_t instead of Optional<wchar_t>.
+    return {};
+  }
+  return static_cast<wchar_t>((digit1 - 48) * 10 + (digit2 - 48) + 130);
 }
-void CBC_ASCIIEncoder::Encode(CBC_EncoderContext& context, int32_t& e) {
-  int32_t n = CBC_HighLevelEncoder::determineConsecutiveDigitCount(
-      context.m_msg, context.m_pos);
+
+size_t DetermineConsecutiveDigitCount(const WideString& msg, size_t startpos) {
+  // This is faster in debug builds and helpful for fuzzers.
+  // Access |data| with care.
+  size_t count = 0;
+  const size_t size = msg.GetLength();
+  const wchar_t* data = msg.c_str();
+  for (size_t i = startpos; i < size; ++i) {
+    if (!FXSYS_IsDecimalDigit(data[i]))
+      break;
+    ++count;
+  }
+  return count;
+}
+
+}  // namespace
+
+CBC_ASCIIEncoder::CBC_ASCIIEncoder() = default;
+
+CBC_ASCIIEncoder::~CBC_ASCIIEncoder() = default;
+
+CBC_HighLevelEncoder::Encoding CBC_ASCIIEncoder::GetEncodingMode() {
+  return CBC_HighLevelEncoder::Encoding::ASCII;
+}
+
+bool CBC_ASCIIEncoder::Encode(CBC_EncoderContext* context) {
+  size_t n = DetermineConsecutiveDigitCount(context->m_msg, context->m_pos);
   if (n >= 2) {
-    wchar_t code = encodeASCIIDigits(context.m_msg[context.m_pos],
-                                     context.m_msg[context.m_pos + 1], e);
-    if (e != BCExceptionNO) {
-      return;
+    Optional<wchar_t> code = EncodeASCIIDigits(
+        context->m_msg[context->m_pos], context->m_msg[context->m_pos + 1]);
+    if (!code)
+      return false;
+
+    context->writeCodeword(*code);
+    context->m_pos += 2;
+    return true;
+  }
+
+  wchar_t c = context->getCurrentChar();
+  CBC_HighLevelEncoder::Encoding newMode = CBC_HighLevelEncoder::LookAheadTest(
+      context->m_msg, context->m_pos, GetEncodingMode());
+  if (newMode != GetEncodingMode()) {
+    switch (newMode) {
+      case CBC_HighLevelEncoder::Encoding::BASE256:
+        context->writeCodeword(CBC_HighLevelEncoder::LATCH_TO_BASE256);
+        break;
+      case CBC_HighLevelEncoder::Encoding::C40:
+        context->writeCodeword(CBC_HighLevelEncoder::LATCH_TO_C40);
+        break;
+      case CBC_HighLevelEncoder::Encoding::X12:
+        context->writeCodeword(CBC_HighLevelEncoder::LATCH_TO_ANSIX12);
+        break;
+      case CBC_HighLevelEncoder::Encoding::TEXT:
+        context->writeCodeword(CBC_HighLevelEncoder::LATCH_TO_TEXT);
+        break;
+      case CBC_HighLevelEncoder::Encoding::EDIFACT:
+        context->writeCodeword(CBC_HighLevelEncoder::LATCH_TO_EDIFACT);
+        break;
+      default:
+        return false;
     }
-    context.writeCodeword(code);
-    context.m_pos += 2;
+    context->SignalEncoderChange(newMode);
+    return true;
+  }
+
+  if (CBC_HighLevelEncoder::IsExtendedASCII(c)) {
+    context->writeCodeword(CBC_HighLevelEncoder::UPPER_SHIFT);
+    context->writeCodeword(static_cast<wchar_t>(c - 128 + 1));
   } else {
-    wchar_t c = context.getCurrentChar();
-    int32_t newMode = CBC_HighLevelEncoder::lookAheadTest(
-        context.m_msg, context.m_pos, getEncodingMode());
-    if (newMode != getEncodingMode()) {
-      switch (newMode) {
-        case BASE256_ENCODATION:
-          context.writeCodeword(CBC_HighLevelEncoder::LATCH_TO_BASE256);
-          context.signalEncoderChange(BASE256_ENCODATION);
-          return;
-        case C40_ENCODATION:
-          context.writeCodeword(CBC_HighLevelEncoder::LATCH_TO_C40);
-          context.signalEncoderChange(C40_ENCODATION);
-          return;
-        case X12_ENCODATION:
-          context.writeCodeword(CBC_HighLevelEncoder::LATCH_TO_ANSIX12);
-          context.signalEncoderChange(X12_ENCODATION);
-          break;
-        case TEXT_ENCODATION:
-          context.writeCodeword(CBC_HighLevelEncoder::LATCH_TO_TEXT);
-          context.signalEncoderChange(TEXT_ENCODATION);
-          break;
-        case EDIFACT_ENCODATION:
-          context.writeCodeword(CBC_HighLevelEncoder::LATCH_TO_EDIFACT);
-          context.signalEncoderChange(EDIFACT_ENCODATION);
-          break;
-        default:
-          e = BCExceptionIllegalStateIllegalMode;
-          return;
-      }
-    } else if (CBC_HighLevelEncoder::isExtendedASCII(c)) {
-      context.writeCodeword(CBC_HighLevelEncoder::UPPER_SHIFT);
-      context.writeCodeword((wchar_t)(c - 128 + 1));
-      context.m_pos++;
-    } else {
-      context.writeCodeword((wchar_t)(c + 1));
-      context.m_pos++;
-    }
+    context->writeCodeword(static_cast<wchar_t>(c + 1));
   }
-}
-wchar_t CBC_ASCIIEncoder::encodeASCIIDigits(wchar_t digit1,
-                                            wchar_t digit2,
-                                            int32_t& e) {
-  if (CBC_HighLevelEncoder::isDigit(digit1) &&
-      CBC_HighLevelEncoder::isDigit(digit2)) {
-    int32_t num = (digit1 - 48) * 10 + (digit2 - 48);
-    return (wchar_t)(num + 130);
-  }
-  e = BCExceptionIllegalArgumentNotGigits;
-  return 0;
+  context->m_pos++;
+  return true;
 }
diff --git a/fxbarcode/datamatrix/BC_ASCIIEncoder.h b/fxbarcode/datamatrix/BC_ASCIIEncoder.h
index 6eb3a50..8b3c2de 100644
--- a/fxbarcode/datamatrix/BC_ASCIIEncoder.h
+++ b/fxbarcode/datamatrix/BC_ASCIIEncoder.h
@@ -11,17 +11,14 @@
 
 class CBC_EncoderContext;
 
-class CBC_ASCIIEncoder : public CBC_Encoder {
+class CBC_ASCIIEncoder final : public CBC_Encoder {
  public:
   CBC_ASCIIEncoder();
   ~CBC_ASCIIEncoder() override;
 
   // CBC_Encoder
-  int32_t getEncodingMode() override;
-  void Encode(CBC_EncoderContext& context, int32_t& e) override;
-
- private:
-  static wchar_t encodeASCIIDigits(wchar_t digit1, wchar_t digit2, int32_t& e);
+  CBC_HighLevelEncoder::Encoding GetEncodingMode() override;
+  bool Encode(CBC_EncoderContext* context) override;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_ASCIIENCODER_H_
diff --git a/fxbarcode/datamatrix/BC_Base256Encoder.cpp b/fxbarcode/datamatrix/BC_Base256Encoder.cpp
index b7dd69c..ea70f68 100644
--- a/fxbarcode/datamatrix/BC_Base256Encoder.cpp
+++ b/fxbarcode/datamatrix/BC_Base256Encoder.cpp
@@ -26,61 +26,67 @@
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
 
-CBC_Base256Encoder::CBC_Base256Encoder() {}
-CBC_Base256Encoder::~CBC_Base256Encoder() {}
-int32_t CBC_Base256Encoder::getEncodingMode() {
-  return BASE256_ENCODATION;
+namespace {
+
+wchar_t Randomize255State(wchar_t ch, int32_t position) {
+  int32_t pseudoRandom = ((149 * position) % 255) + 1;
+  int32_t tempVariable = ch + pseudoRandom;
+  if (tempVariable <= 255)
+    return static_cast<wchar_t>(tempVariable);
+  return static_cast<wchar_t>(tempVariable - 256);
 }
-void CBC_Base256Encoder::Encode(CBC_EncoderContext& context, int32_t& e) {
+
+}  // namespace
+
+CBC_Base256Encoder::CBC_Base256Encoder() = default;
+
+CBC_Base256Encoder::~CBC_Base256Encoder() = default;
+
+CBC_HighLevelEncoder::Encoding CBC_Base256Encoder::GetEncodingMode() {
+  return CBC_HighLevelEncoder::Encoding::BASE256;
+}
+
+bool CBC_Base256Encoder::Encode(CBC_EncoderContext* context) {
   WideString buffer;
+  buffer.Reserve(context->getRemainingCharacters() + 1);
   buffer += L'\0';
-  while (context.hasMoreCharacters()) {
-    wchar_t c = context.getCurrentChar();
+  while (context->hasMoreCharacters()) {
+    wchar_t c = context->getCurrentChar();
     buffer += c;
-    context.m_pos++;
-    int32_t newMode = CBC_HighLevelEncoder::lookAheadTest(
-        context.m_msg, context.m_pos, getEncodingMode());
-    if (newMode != getEncodingMode()) {
-      context.signalEncoderChange(newMode);
+    context->m_pos++;
+    CBC_HighLevelEncoder::Encoding newMode =
+        CBC_HighLevelEncoder::LookAheadTest(context->m_msg, context->m_pos,
+                                            GetEncodingMode());
+    if (newMode != GetEncodingMode()) {
+      context->SignalEncoderChange(newMode);
       break;
     }
   }
-  int32_t dataCount = buffer.GetLength() - 1;
+  size_t dataCount = buffer.GetLength() - 1;
   char buf[128];
   FXSYS_itoa(dataCount, buf, 10);
   buffer.SetAt(0, static_cast<wchar_t>(*buf) - '0');
   int32_t lengthFieldSize = 1;
   int32_t currentSize =
-      context.getCodewordCount() + dataCount + lengthFieldSize;
-  context.updateSymbolInfo(currentSize, e);
-  if (e != BCExceptionNO) {
-    return;
-  }
-  bool mustPad = (context.m_symbolInfo->dataCapacity() - currentSize) > 0;
-  if (context.hasMoreCharacters() || mustPad) {
+      context->getCodewordCount() + dataCount + lengthFieldSize;
+  if (!context->UpdateSymbolInfo(currentSize))
+    return false;
+
+  bool mustPad = (context->m_symbolInfo->dataCapacity() - currentSize) > 0;
+  if (context->hasMoreCharacters() || mustPad) {
     if (dataCount <= 249) {
       buffer.SetAt(0, static_cast<wchar_t>(dataCount));
     } else if (dataCount > 249 && dataCount <= 1555) {
       buffer.SetAt(0, static_cast<wchar_t>((dataCount / 250) + 249));
       buffer.Insert(1, static_cast<wchar_t>(dataCount % 250));
     } else {
-      e = BCExceptionIllegalStateMessageLengthInvalid;
-      return;
+      return false;
     }
   }
   for (const auto& c : buffer) {
-    context.writeCodeword(randomize255State(c, context.getCodewordCount() + 1));
+    context->writeCodeword(
+        Randomize255State(c, context->getCodewordCount() + 1));
   }
-}
-wchar_t CBC_Base256Encoder::randomize255State(wchar_t ch,
-                                              int32_t codewordPosition) {
-  int32_t pseudoRandom = ((149 * codewordPosition) % 255) + 1;
-  int32_t tempVariable = ch + pseudoRandom;
-  if (tempVariable <= 255) {
-    return static_cast<wchar_t>(tempVariable);
-  } else {
-    return static_cast<wchar_t>(tempVariable - 256);
-  }
+  return true;
 }
diff --git a/fxbarcode/datamatrix/BC_Base256Encoder.h b/fxbarcode/datamatrix/BC_Base256Encoder.h
index 65abf59..4897cc5 100644
--- a/fxbarcode/datamatrix/BC_Base256Encoder.h
+++ b/fxbarcode/datamatrix/BC_Base256Encoder.h
@@ -9,17 +9,14 @@
 
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 
-class CBC_Base256Encoder : public CBC_Encoder {
+class CBC_Base256Encoder final : public CBC_Encoder {
  public:
   CBC_Base256Encoder();
   ~CBC_Base256Encoder() override;
 
   // CBC_Encoder
-  int32_t getEncodingMode() override;
-  void Encode(CBC_EncoderContext& context, int32_t& e) override;
-
- private:
-  static wchar_t randomize255State(wchar_t ch, int32_t codewordPosition);
+  CBC_HighLevelEncoder::Encoding GetEncodingMode() override;
+  bool Encode(CBC_EncoderContext* context) override;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_BASE256ENCODER_H_
diff --git a/fxbarcode/datamatrix/BC_C40Encoder.cpp b/fxbarcode/datamatrix/BC_C40Encoder.cpp
index bb8b11c..9f20a35 100644
--- a/fxbarcode/datamatrix/BC_C40Encoder.cpp
+++ b/fxbarcode/datamatrix/BC_C40Encoder.cpp
@@ -22,19 +22,19 @@
 
 #include "fxbarcode/datamatrix/BC_C40Encoder.h"
 
+#include "core/fxcrt/fx_extension.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
 
 namespace {
 
-WideString EncodeToC40Codewords(const WideString& sb, int32_t startPos) {
-  wchar_t c1 = sb[startPos];
-  wchar_t c2 = sb[startPos + 1];
-  wchar_t c3 = sb[startPos + 2];
+WideString EncodeToC40Codewords(const WideString& sb) {
+  wchar_t c1 = sb[0];
+  wchar_t c2 = sb[1];
+  wchar_t c3 = sb[2];
   int32_t v = (1600 * c1) + (40 * c2) + c3 + 1;
   wchar_t cw[2];
   cw[0] = static_cast<wchar_t>(v / 256);
@@ -44,168 +44,169 @@
 
 }  // namespace
 
-CBC_C40Encoder::CBC_C40Encoder() {}
-CBC_C40Encoder::~CBC_C40Encoder() {}
-int32_t CBC_C40Encoder::getEncodingMode() {
-  return C40_ENCODATION;
+CBC_C40Encoder::CBC_C40Encoder() = default;
+
+CBC_C40Encoder::~CBC_C40Encoder() = default;
+
+CBC_HighLevelEncoder::Encoding CBC_C40Encoder::GetEncodingMode() {
+  return CBC_HighLevelEncoder::Encoding::C40;
 }
-void CBC_C40Encoder::Encode(CBC_EncoderContext& context, int32_t& e) {
+
+bool CBC_C40Encoder::Encode(CBC_EncoderContext* context) {
   WideString buffer;
-  while (context.hasMoreCharacters()) {
-    wchar_t c = context.getCurrentChar();
-    context.m_pos++;
-    int32_t lastCharSize = encodeChar(c, buffer, e);
-    if (e != BCExceptionNO) {
-      return;
-    }
-    int32_t unwritten = (buffer.GetLength() / 3) * 2;
-    int32_t curCodewordCount = context.getCodewordCount() + unwritten;
-    context.updateSymbolInfo(curCodewordCount, e);
-    if (e != BCExceptionNO) {
-      return;
-    }
-    int32_t available = context.m_symbolInfo->dataCapacity() - curCodewordCount;
-    if (!context.hasMoreCharacters()) {
+  while (context->hasMoreCharacters()) {
+    wchar_t c = context->getCurrentChar();
+    context->m_pos++;
+    int32_t lastCharSize = EncodeChar(c, &buffer);
+    if (lastCharSize <= 0)
+      return false;
+
+    size_t unwritten = (buffer.GetLength() / 3) * 2;
+    int32_t curCodewordCount = context->getCodewordCount() + unwritten;
+    if (!context->UpdateSymbolInfo(curCodewordCount))
+      return false;
+
+    int32_t available =
+        context->m_symbolInfo->dataCapacity() - curCodewordCount;
+    if (!context->hasMoreCharacters()) {
       if ((buffer.GetLength() % 3) == 2) {
         if (available < 2 || available > 2) {
-          lastCharSize = BacktrackOneCharacter(&context, &buffer, lastCharSize);
-          if (lastCharSize < 0) {
-            e = BCExceptionGeneric;
-            return;
-          }
+          lastCharSize = BacktrackOneCharacter(context, &buffer, lastCharSize);
+          if (lastCharSize < 0)
+            return false;
         }
       }
       while ((buffer.GetLength() % 3) == 1 &&
              ((lastCharSize <= 3 && available != 1) || lastCharSize > 3)) {
-        lastCharSize = BacktrackOneCharacter(&context, &buffer, lastCharSize);
-        if (lastCharSize < 0) {
-          e = BCExceptionGeneric;
-          return;
-        }
+        lastCharSize = BacktrackOneCharacter(context, &buffer, lastCharSize);
+        if (lastCharSize < 0)
+          return false;
       }
       break;
     }
-    int32_t count = buffer.GetLength();
+    size_t count = buffer.GetLength();
     if ((count % 3) == 0) {
-      int32_t newMode = CBC_HighLevelEncoder::lookAheadTest(
-          context.m_msg, context.m_pos, getEncodingMode());
-      if (newMode != getEncodingMode()) {
-        context.signalEncoderChange(newMode);
+      CBC_HighLevelEncoder::Encoding newMode =
+          CBC_HighLevelEncoder::LookAheadTest(context->m_msg, context->m_pos,
+                                              GetEncodingMode());
+      if (newMode != GetEncodingMode()) {
+        context->SignalEncoderChange(newMode);
         break;
       }
     }
   }
-  handleEOD(context, buffer, e);
+  return HandleEOD(context, &buffer);
 }
-void CBC_C40Encoder::writeNextTriplet(CBC_EncoderContext& context,
-                                      WideString& buffer) {
-  context.writeCodewords(EncodeToC40Codewords(buffer, 0));
-  buffer.Delete(0, 3);
+
+void CBC_C40Encoder::WriteNextTriplet(CBC_EncoderContext* context,
+                                      WideString* buffer) {
+  context->writeCodewords(EncodeToC40Codewords(*buffer));
+  buffer->Delete(0, 3);
 }
-void CBC_C40Encoder::handleEOD(CBC_EncoderContext& context,
-                               WideString& buffer,
-                               int32_t& e) {
-  int32_t unwritten = (buffer.GetLength() / 3) * 2;
-  int32_t rest = buffer.GetLength() % 3;
-  int32_t curCodewordCount = context.getCodewordCount() + unwritten;
-  context.updateSymbolInfo(curCodewordCount, e);
-  if (e != BCExceptionNO) {
-    return;
-  }
-  int32_t available = context.m_symbolInfo->dataCapacity() - curCodewordCount;
+
+bool CBC_C40Encoder::HandleEOD(CBC_EncoderContext* context,
+                               WideString* buffer) {
+  size_t unwritten = (buffer->GetLength() / 3) * 2;
+  size_t rest = buffer->GetLength() % 3;
+  int32_t curCodewordCount = context->getCodewordCount() + unwritten;
+  if (!context->UpdateSymbolInfo(curCodewordCount))
+    return false;
+
+  int32_t available = context->m_symbolInfo->dataCapacity() - curCodewordCount;
   if (rest == 2) {
-    buffer += (wchar_t)'\0';
-    while (buffer.GetLength() >= 3) {
-      writeNextTriplet(context, buffer);
-    }
-    if (context.hasMoreCharacters()) {
-      context.writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
+    *buffer += (wchar_t)'\0';
+    while (buffer->GetLength() >= 3)
+      WriteNextTriplet(context, buffer);
+    if (context->hasMoreCharacters()) {
+      context->writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
     }
   } else if (available == 1 && rest == 1) {
-    while (buffer.GetLength() >= 3) {
-      writeNextTriplet(context, buffer);
+    while (buffer->GetLength() >= 3)
+      WriteNextTriplet(context, buffer);
+    if (context->hasMoreCharacters()) {
+      context->writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
     }
-    if (context.hasMoreCharacters()) {
-      context.writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
-    }
-    context.m_pos--;
+    context->m_pos--;
   } else if (rest == 0) {
-    while (buffer.GetLength() >= 3) {
-      writeNextTriplet(context, buffer);
-    }
-    if (available > 0 || context.hasMoreCharacters()) {
-      context.writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
+    while (buffer->GetLength() >= 3)
+      WriteNextTriplet(context, buffer);
+    if (available > 0 || context->hasMoreCharacters()) {
+      context->writeCodeword(CBC_HighLevelEncoder::C40_UNLATCH);
     }
   } else {
-    e = BCExceptionIllegalStateUnexpectedCase;
-    return;
+    return false;
   }
-  context.signalEncoderChange(ASCII_ENCODATION);
+  context->SignalEncoderChange(CBC_HighLevelEncoder::Encoding::ASCII);
+  return true;
 }
-int32_t CBC_C40Encoder::encodeChar(wchar_t c, WideString& sb, int32_t& e) {
+
+int32_t CBC_C40Encoder::EncodeChar(wchar_t c, WideString* sb) {
   if (c == ' ') {
-    sb += (wchar_t)'\3';
+    *sb += (wchar_t)'\3';
     return 1;
-  } else if ((c >= '0') && (c <= '9')) {
-    sb += (wchar_t)(c - 48 + 4);
-    return 1;
-  } else if ((c >= 'A') && (c <= 'Z')) {
-    sb += (wchar_t)(c - 65 + 14);
-    return 1;
-  } else if (c <= 0x1f) {
-    sb += (wchar_t)'\0';
-    sb += c;
-    return 2;
-  } else if ((c >= '!') && (c <= '/')) {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)(c - 33);
-    return 2;
-  } else if ((c >= ':') && (c <= '@')) {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)(c - 58 + 15);
-    return 2;
-  } else if ((c >= '[') && (c <= '_')) {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)(c - 91 + 22);
-    return 2;
-  } else if ((c >= 60) && (c <= 0x7f)) {
-    sb += (wchar_t)'\2';
-    sb += (wchar_t)(c - 96);
-    return 2;
-  } else if (c >= 80) {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)0x001e;
-    int32_t len = 2;
-    len += encodeChar((c - 128), sb, e);
-    if (e != BCExceptionNO)
-      return 0;
-    return len;
-  } else {
-    e = BCExceptionIllegalArgument;
-    return 0;
   }
+  if (FXSYS_IsDecimalDigit(c)) {
+    *sb += (wchar_t)(c - 48 + 4);
+    return 1;
+  }
+  if ((c >= 'A') && (c <= 'Z')) {
+    *sb += (wchar_t)(c - 65 + 14);
+    return 1;
+  }
+  if (c <= 0x1f) {
+    *sb += (wchar_t)'\0';
+    *sb += c;
+    return 2;
+  }
+  if ((c >= '!') && (c <= '/')) {
+    *sb += (wchar_t)'\1';
+    *sb += (wchar_t)(c - 33);
+    return 2;
+  }
+  if ((c >= ':') && (c <= '@')) {
+    *sb += (wchar_t)'\1';
+    *sb += (wchar_t)(c - 58 + 15);
+    return 2;
+  }
+  if ((c >= '[') && (c <= '_')) {
+    *sb += (wchar_t)'\1';
+    *sb += (wchar_t)(c - 91 + 22);
+    return 2;
+  }
+  if ((c >= 60) && (c <= 0x7f)) {
+    *sb += (wchar_t)'\2';
+    *sb += (wchar_t)(c - 96);
+    return 2;
+  }
+  if (c >= 80) {
+    *sb += (wchar_t)'\1';
+    *sb += (wchar_t)0x001e;
+    int32_t encode_result = EncodeChar(c - 128, sb);
+    return encode_result > 0 ? encode_result + 2 : 0;
+  }
+  return 0;
 }
 
 int32_t CBC_C40Encoder::BacktrackOneCharacter(CBC_EncoderContext* context,
                                               WideString* buffer,
                                               int32_t lastCharSize) {
+  ASSERT(lastCharSize >= 0);
+
   if (context->m_pos < 1)
     return -1;
 
-  int32_t count = buffer->GetLength();
-  if (count < lastCharSize)
+  size_t count = buffer->GetLength();
+  if (count < static_cast<size_t>(lastCharSize))
     return -1;
 
   buffer->Delete(count - lastCharSize, lastCharSize);
   context->m_pos--;
   wchar_t c = context->getCurrentChar();
-  int32_t e = BCExceptionNO;
   WideString removed;
-  int32_t len = encodeChar(c, removed, e);
-  if (e != BCExceptionNO)
+  int32_t len = EncodeChar(c, &removed);
+  if (len <= 0)
     return -1;
 
-  ASSERT(len > 0);
   context->resetSymbolInfo();
   return len;
 }
diff --git a/fxbarcode/datamatrix/BC_C40Encoder.h b/fxbarcode/datamatrix/BC_C40Encoder.h
index 5ddad92..8bea47b 100644
--- a/fxbarcode/datamatrix/BC_C40Encoder.h
+++ b/fxbarcode/datamatrix/BC_C40Encoder.h
@@ -16,15 +16,15 @@
   ~CBC_C40Encoder() override;
 
   // CBC_Encoder
-  int32_t getEncodingMode() override;
-  void Encode(CBC_EncoderContext& context, int32_t& e) override;
+  CBC_HighLevelEncoder::Encoding GetEncodingMode() override;
+  bool Encode(CBC_EncoderContext* context) override;
 
-  static void writeNextTriplet(CBC_EncoderContext& context, WideString& buffer);
+  static void WriteNextTriplet(CBC_EncoderContext* context, WideString* buffer);
 
-  virtual void handleEOD(CBC_EncoderContext& context,
-                         WideString& buffer,
-                         int32_t& e);
-  virtual int32_t encodeChar(wchar_t c, WideString& sb, int32_t& e);
+  virtual bool HandleEOD(CBC_EncoderContext* context, WideString* buffer);
+
+  // Returns the number of characters appended to |sb|, or 0 on failure.
+  virtual int32_t EncodeChar(wchar_t c, WideString* sb);
 
  private:
   // Moves back by 1 position in |context| and adjusts |buffer| accordingly
diff --git a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp
index 7a18d03..682d095 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp
+++ b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp
@@ -29,11 +29,6 @@
 
 CBC_DataMatrixSymbolInfo144::~CBC_DataMatrixSymbolInfo144() {}
 
-int32_t CBC_DataMatrixSymbolInfo144::getInterleavedBlockCount() const {
+size_t CBC_DataMatrixSymbolInfo144::getInterleavedBlockCount() const {
   return 10;
 }
-
-int32_t CBC_DataMatrixSymbolInfo144getDataLengthForInterleavedBlock(
-    int32_t index) {
-  return index <= 8 ? 156 : 155;
-}
diff --git a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h
index 0a1c529..2f44557 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h
+++ b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h
@@ -9,12 +9,13 @@
 
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
 
-class CBC_DataMatrixSymbolInfo144 : public CBC_SymbolInfo {
+class CBC_DataMatrixSymbolInfo144 final : public CBC_SymbolInfo {
  public:
   CBC_DataMatrixSymbolInfo144();
   ~CBC_DataMatrixSymbolInfo144() override;
 
-  int32_t getInterleavedBlockCount() const override;
+  // CBC_SymbolInfo:
+  size_t getInterleavedBlockCount() const override;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_DATAMATRIXSYMBOLINFO144_H_
diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp b/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp
index d4f9ad3..1d2b39e 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp
+++ b/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp
@@ -25,7 +25,6 @@
 #include <memory>
 
 #include "fxbarcode/BC_TwoDimWriter.h"
-#include "fxbarcode/BC_UtilCodingConvert.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
@@ -43,12 +42,13 @@
 #include "fxbarcode/datamatrix/BC_TextEncoder.h"
 #include "fxbarcode/datamatrix/BC_X12Encoder.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 namespace {
 
 std::unique_ptr<CBC_CommonByteMatrix> encodeLowLevel(
     CBC_DefaultPlacement* placement,
-    CBC_SymbolInfo* symbolInfo) {
+    const CBC_SymbolInfo* symbolInfo) {
   int32_t symbolWidth = symbolInfo->getSymbolDataWidth();
   ASSERT(symbolWidth);
   int32_t symbolHeight = symbolInfo->getSymbolDataHeight();
@@ -59,7 +59,6 @@
   ASSERT(height);
 
   auto matrix = pdfium::MakeUnique<CBC_CommonByteMatrix>(width, height);
-  matrix->Init();
   int32_t matrixY = 0;
   for (int32_t y = 0; y < symbolHeight; y++) {
     int32_t matrixX;
@@ -99,51 +98,49 @@
 
 }  // namespace
 
-CBC_DataMatrixWriter::CBC_DataMatrixWriter() {}
+CBC_DataMatrixWriter::CBC_DataMatrixWriter() : CBC_TwoDimWriter(true) {}
 
-CBC_DataMatrixWriter::~CBC_DataMatrixWriter() {}
+CBC_DataMatrixWriter::~CBC_DataMatrixWriter() = default;
 
 bool CBC_DataMatrixWriter::SetErrorCorrectionLevel(int32_t level) {
-  m_iCorrectLevel = level;
+  set_error_correction_level(level);
   return true;
 }
 
-uint8_t* CBC_DataMatrixWriter::Encode(const WideString& contents,
-                                      int32_t& outWidth,
-                                      int32_t& outHeight) {
-  if (outWidth < 0 || outHeight < 0)
-    return nullptr;
+std::vector<uint8_t> CBC_DataMatrixWriter::Encode(const WideString& contents,
+                                                  int32_t* pOutWidth,
+                                                  int32_t* pOutHeight) {
+  std::vector<uint8_t> results;
+  WideString encoded = CBC_HighLevelEncoder::EncodeHighLevel(contents);
+  if (encoded.IsEmpty())
+    return results;
 
-  WideString ecLevel;
-  int32_t e = BCExceptionNO;
-  WideString encoded =
-      CBC_HighLevelEncoder::encodeHighLevel(contents, ecLevel, false, e);
-  if (e != BCExceptionNO)
-    return nullptr;
-  CBC_SymbolInfo* symbolInfo =
-      CBC_SymbolInfo::lookup(encoded.GetLength(), false, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  const CBC_SymbolInfo* pSymbolInfo =
+      CBC_SymbolInfo::Lookup(encoded.GetLength(), false);
+  if (!pSymbolInfo)
+    return results;
+
   WideString codewords =
-      CBC_ErrorCorrection::encodeECC200(encoded, symbolInfo, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+      CBC_ErrorCorrection::EncodeECC200(encoded, pSymbolInfo);
+  if (codewords.IsEmpty())
+    return results;
 
-  int32_t width = symbolInfo->getSymbolDataWidth();
+  int32_t width = pSymbolInfo->getSymbolDataWidth();
   ASSERT(width);
-  int32_t height = symbolInfo->getSymbolDataHeight();
+  int32_t height = pSymbolInfo->getSymbolDataHeight();
   ASSERT(height);
 
   auto placement =
       pdfium::MakeUnique<CBC_DefaultPlacement>(codewords, width, height);
   placement->place();
-  auto bytematrix = encodeLowLevel(placement.get(), symbolInfo);
+  auto bytematrix = encodeLowLevel(placement.get(), pSymbolInfo);
   if (!bytematrix)
-    return nullptr;
+    return results;
 
-  outWidth = bytematrix->GetWidth();
-  outHeight = bytematrix->GetHeight();
-  uint8_t* result = FX_Alloc2D(uint8_t, outWidth, outHeight);
-  memcpy(result, bytematrix->GetArray(), outWidth * outHeight);
-  return result;
+  *pOutWidth = bytematrix->GetWidth();
+  *pOutHeight = bytematrix->GetHeight();
+  results = pdfium::Vector2D<uint8_t>(*pOutWidth, *pOutHeight);
+  memcpy(results.data(), bytematrix->GetArray().data(),
+         *pOutWidth * *pOutHeight);
+  return results;
 }
diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter.h b/fxbarcode/datamatrix/BC_DataMatrixWriter.h
index ba2c547..f95612e 100644
--- a/fxbarcode/datamatrix/BC_DataMatrixWriter.h
+++ b/fxbarcode/datamatrix/BC_DataMatrixWriter.h
@@ -7,26 +7,21 @@
 #ifndef FXBARCODE_DATAMATRIX_BC_DATAMATRIXWRITER_H_
 #define FXBARCODE_DATAMATRIX_BC_DATAMATRIXWRITER_H_
 
+#include <vector>
+
 #include "fxbarcode/BC_TwoDimWriter.h"
 
-class CBC_CommonByteMatrix;
-class CBC_DefaultPlacement;
-class CBC_SymbolInfo;
-
-class CBC_DataMatrixWriter : public CBC_TwoDimWriter {
+class CBC_DataMatrixWriter final : public CBC_TwoDimWriter {
  public:
   CBC_DataMatrixWriter();
   ~CBC_DataMatrixWriter() override;
 
-  uint8_t* Encode(const WideString& contents,
-                  int32_t& outWidth,
-                  int32_t& outHeight);
+  std::vector<uint8_t> Encode(const WideString& contents,
+                              int32_t* pOutWidth,
+                              int32_t* pOutHeight);
 
   // CBC_TwoDimWriter
   bool SetErrorCorrectionLevel(int32_t level) override;
-
- private:
-  int32_t m_iCorrectLevel;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_DATAMATRIXWRITER_H_
diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp b/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp
new file mode 100644
index 0000000..fa5d26c
--- /dev/null
+++ b/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp
@@ -0,0 +1,196 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxbarcode/datamatrix/BC_DataMatrixWriter.h"
+
+#include <vector>
+
+#include "core/fxcrt/fx_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CBC_DataMatrixWriterTest : public testing::Test {
+ public:
+  CBC_DataMatrixWriterTest() = default;
+  ~CBC_DataMatrixWriterTest() override = default;
+
+  // testing::Test:
+  void SetUp() override { BC_Library_Init(); }
+  void TearDown() override { BC_Library_Destroy(); }
+};
+
+TEST_F(CBC_DataMatrixWriterTest, Encode) {
+  CBC_DataMatrixWriter writer;
+  int32_t width = -1;
+  int32_t height = -1;
+
+  {
+    static constexpr int kExpectedDimension = 10;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 1, 0, 1, 1, 0, 1, 0, 0, 1,
+        1, 1, 0, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 0, 0, 0, 1, 0, 1,
+        1, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+        1, 1, 1, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 1, 1, 0, 1, 0,
+        1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
+        1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+    };
+    // clang-format on
+    std::vector<uint8_t> data = writer.Encode(L"", &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 14;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0,
+        1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1,
+        1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0,
+        1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+    };
+    // clang-format on
+    std::vector<uint8_t> data = writer.Encode(L"helloworld", &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 10;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 1, 0, 1, 1, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 0, 1, 0, 1, 1, 0,
+        1, 1, 0, 0, 1, 1, 0, 1, 0, 1,
+        1, 1, 0, 0, 1, 1, 1, 0, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 1, 1, 0,
+        1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+    };
+    // clang-format on
+    std::vector<uint8_t> data = writer.Encode(L"12345", &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 18;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1,
+        1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0,
+        1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+        1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0,
+        1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0,
+        1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0,
+        1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1,
+        1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0,
+        1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0,
+        1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+    };
+    // clang-format on
+    std::vector<uint8_t> data =
+        writer.Encode(L"abcdefghijklmnopqrst", &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    std::vector<uint8_t> data = writer.Encode(L"hello world", &width, &height);
+    ASSERT_TRUE(data.empty());
+  }
+}
+
+TEST_F(CBC_DataMatrixWriterTest, EncodeLimitAlphaNumeric) {
+  CBC_DataMatrixWriter writer;
+  int32_t width = -1;
+  int32_t height = -1;
+
+  static constexpr int kMaxInputLength = 2335;  // Per spec.
+  WideString input;
+  for (size_t i = 0; i < kMaxInputLength; ++i)
+    input.InsertAtBack(L'a');
+
+  {
+    static constexpr int kExpectedDimension = 144;
+    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    EXPECT_EQ(20736u, data.size());
+    EXPECT_EQ(kExpectedDimension, width);
+    EXPECT_EQ(kExpectedDimension, height);
+  }
+
+  // Go over the limit.
+  input.InsertAtBack(L'a');
+  {
+    width = -1;
+    height = -1;
+    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    EXPECT_EQ(0u, data.size());
+    EXPECT_EQ(-1, width);
+    EXPECT_EQ(-1, height);
+  }
+}
+
+TEST_F(CBC_DataMatrixWriterTest, EncodeLimitNumbers) {
+  CBC_DataMatrixWriter writer;
+  int32_t width = -1;
+  int32_t height = -1;
+
+  static constexpr int kMaxInputLength = 3116;  // Per spec.
+  WideString input;
+  for (size_t i = 0; i < kMaxInputLength; ++i)
+    input.InsertAtBack(L'1');
+
+  {
+    static constexpr int kExpectedDimension = 144;
+    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    EXPECT_EQ(20736u, data.size());
+    EXPECT_EQ(kExpectedDimension, width);
+    EXPECT_EQ(kExpectedDimension, height);
+  }
+
+  // Go over the limit.
+  input.InsertAtBack(L'1');
+  {
+    width = -1;
+    height = -1;
+    std::vector<uint8_t> data = writer.Encode(input.c_str(), &width, &height);
+    EXPECT_EQ(0u, data.size());
+    EXPECT_EQ(-1, width);
+    EXPECT_EQ(-1, height);
+  }
+}
diff --git a/fxbarcode/datamatrix/BC_DefaultPlacement.cpp b/fxbarcode/datamatrix/BC_DefaultPlacement.cpp
index 3f1b358..24d81f1 100644
--- a/fxbarcode/datamatrix/BC_DefaultPlacement.cpp
+++ b/fxbarcode/datamatrix/BC_DefaultPlacement.cpp
@@ -21,19 +21,23 @@
  */
 
 #include "fxbarcode/datamatrix/BC_DefaultPlacement.h"
+
+#include <utility>
+
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 
 CBC_DefaultPlacement::CBC_DefaultPlacement(WideString codewords,
                                            int32_t numcols,
-                                           int32_t numrows) {
-  m_codewords = codewords;
-  m_numcols = numcols;
-  m_numrows = numrows;
+                                           int32_t numrows)
+    : m_codewords(std::move(codewords)),
+      m_numrows(numrows),
+      m_numcols(numcols) {
   m_bits.resize(numcols * numrows);
   for (int32_t i = 0; i < numcols * numrows; i++) {
     m_bits[i] = (uint8_t)2;
   }
 }
+
 CBC_DefaultPlacement::~CBC_DefaultPlacement() {}
 
 int32_t CBC_DefaultPlacement::getNumrows() {
diff --git a/fxbarcode/datamatrix/BC_DefaultPlacement.h b/fxbarcode/datamatrix/BC_DefaultPlacement.h
index 12ea852..823d587 100644
--- a/fxbarcode/datamatrix/BC_DefaultPlacement.h
+++ b/fxbarcode/datamatrix/BC_DefaultPlacement.h
@@ -11,10 +11,10 @@
 
 #include "core/fxcrt/fx_string.h"
 
-class CBC_DefaultPlacement {
+class CBC_DefaultPlacement final {
  public:
   CBC_DefaultPlacement(WideString codewords, int32_t numcols, int32_t numrows);
-  virtual ~CBC_DefaultPlacement();
+  ~CBC_DefaultPlacement();
 
   int32_t getNumrows();
   int32_t getNumcols();
diff --git a/fxbarcode/datamatrix/BC_EdifactEncoder.cpp b/fxbarcode/datamatrix/BC_EdifactEncoder.cpp
index 41180dc..bd86a95 100644
--- a/fxbarcode/datamatrix/BC_EdifactEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_EdifactEncoder.cpp
@@ -29,7 +29,6 @@
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
 
 namespace {
 
@@ -52,16 +51,14 @@
 }
 
 bool HandleEOD(CBC_EncoderContext* context, const WideString& buffer) {
-  int32_t count = buffer.GetLength();
+  size_t count = buffer.GetLength();
   if (count == 0)
     return true;
   if (count > 4)
     return false;
 
   if (count == 1) {
-    int32_t e = BCExceptionNO;
-    context->updateSymbolInfo(e);
-    if (e != BCExceptionNO)
+    if (!context->UpdateSymbolInfo())
       return false;
 
     int32_t available =
@@ -79,19 +76,17 @@
   bool endOfSymbolReached = !context->hasMoreCharacters();
   bool restInAscii = endOfSymbolReached && restChars <= 2;
   if (restChars <= 2) {
-    int32_t e = BCExceptionNO;
-    context->updateSymbolInfo(context->getCodewordCount() + restChars, e);
-    if (e != BCExceptionNO)
+    if (!context->UpdateSymbolInfo(context->getCodewordCount() + restChars))
       return false;
 
     int32_t available =
         context->m_symbolInfo->dataCapacity() - context->getCodewordCount();
     if (available >= 3) {
       restInAscii = false;
-      context->updateSymbolInfo(
-          context->getCodewordCount() + encoded.GetLength(), e);
-      if (e != BCExceptionNO)
+      if (!context->UpdateSymbolInfo(context->getCodewordCount() +
+                                     encoded.GetLength())) {
         return false;
+      }
     }
   }
 
@@ -101,57 +96,59 @@
   } else {
     context->writeCodewords(encoded);
   }
-  context->signalEncoderChange(ASCII_ENCODATION);
+  context->SignalEncoderChange(CBC_HighLevelEncoder::Encoding::ASCII);
   return true;
 }
 
-void encodeChar(wchar_t c, WideString* sb, int32_t& e) {
+bool AppendEncodedChar(wchar_t c, WideString* sb) {
   if (c >= ' ' && c <= '?') {
     *sb += c;
-  } else if (c >= '@' && c <= '^') {
-    *sb += (wchar_t)(c - 64);
-  } else {
-    e = BCExceptionIllegalArgument;
+    return true;
   }
+
+  if (c >= '@' && c <= '^') {
+    *sb += (c - 64);
+    return true;
+  }
+
+  return false;
 }
 
 }  // namespace
 
-CBC_EdifactEncoder::CBC_EdifactEncoder() {}
+CBC_EdifactEncoder::CBC_EdifactEncoder() = default;
 
-CBC_EdifactEncoder::~CBC_EdifactEncoder() {}
+CBC_EdifactEncoder::~CBC_EdifactEncoder() = default;
 
-int32_t CBC_EdifactEncoder::getEncodingMode() {
-  return EDIFACT_ENCODATION;
+CBC_HighLevelEncoder::Encoding CBC_EdifactEncoder::GetEncodingMode() {
+  return CBC_HighLevelEncoder::Encoding::EDIFACT;
 }
 
-void CBC_EdifactEncoder::Encode(CBC_EncoderContext& context, int32_t& e) {
+bool CBC_EdifactEncoder::Encode(CBC_EncoderContext* context) {
   WideString buffer;
-  while (context.hasMoreCharacters()) {
-    wchar_t c = context.getCurrentChar();
-    encodeChar(c, &buffer, e);
-    if (e != BCExceptionNO) {
-      return;
-    }
-    context.m_pos++;
-    int32_t count = buffer.GetLength();
+  while (context->hasMoreCharacters()) {
+    wchar_t c = context->getCurrentChar();
+    if (!AppendEncodedChar(c, &buffer))
+      return false;
+
+    context->m_pos++;
+    size_t count = buffer.GetLength();
     if (count >= 4) {
       WideString encoded = EncodeToEdifactCodewords(buffer, 0);
-      if (encoded.IsEmpty()) {
-        e = BCExceptionGeneric;
-        return;
-      }
-      context.writeCodewords(encoded);
+      if (encoded.IsEmpty())
+        return false;
+
+      context->writeCodewords(encoded);
       buffer.Delete(0, 4);
-      int32_t newMode = CBC_HighLevelEncoder::lookAheadTest(
-          context.m_msg, context.m_pos, getEncodingMode());
-      if (newMode != getEncodingMode()) {
-        context.signalEncoderChange(ASCII_ENCODATION);
+      CBC_HighLevelEncoder::Encoding newMode =
+          CBC_HighLevelEncoder::LookAheadTest(context->m_msg, context->m_pos,
+                                              GetEncodingMode());
+      if (newMode != GetEncodingMode()) {
+        context->SignalEncoderChange(CBC_HighLevelEncoder::Encoding::ASCII);
         break;
       }
     }
   }
   buffer += static_cast<wchar_t>(31);
-  if (!HandleEOD(&context, buffer))
-    e = BCExceptionGeneric;
+  return HandleEOD(context, buffer);
 }
diff --git a/fxbarcode/datamatrix/BC_EdifactEncoder.h b/fxbarcode/datamatrix/BC_EdifactEncoder.h
index eb59222..0bd8e57 100644
--- a/fxbarcode/datamatrix/BC_EdifactEncoder.h
+++ b/fxbarcode/datamatrix/BC_EdifactEncoder.h
@@ -9,14 +9,14 @@
 
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 
-class CBC_EdifactEncoder : public CBC_Encoder {
+class CBC_EdifactEncoder final : public CBC_Encoder {
  public:
   CBC_EdifactEncoder();
   ~CBC_EdifactEncoder() override;
 
   // CBC_Encoder
-  int32_t getEncodingMode() override;
-  void Encode(CBC_EncoderContext& context, int32_t& e) override;
+  CBC_HighLevelEncoder::Encoding GetEncodingMode() override;
+  bool Encode(CBC_EncoderContext* context) override;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_EDIFACTENCODER_H_
diff --git a/fxbarcode/datamatrix/BC_Encoder.h b/fxbarcode/datamatrix/BC_Encoder.h
index be1730f..0ea22ab 100644
--- a/fxbarcode/datamatrix/BC_Encoder.h
+++ b/fxbarcode/datamatrix/BC_Encoder.h
@@ -7,7 +7,7 @@
 #ifndef FXBARCODE_DATAMATRIX_BC_ENCODER_H_
 #define FXBARCODE_DATAMATRIX_BC_ENCODER_H_
 
-#include <stdint.h>
+#include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 
 class CBC_EncoderContext;
 
@@ -16,8 +16,8 @@
   CBC_Encoder();
   virtual ~CBC_Encoder();
 
-  virtual int32_t getEncodingMode() = 0;
-  virtual void Encode(CBC_EncoderContext& context, int32_t& e) = 0;
+  virtual CBC_HighLevelEncoder::Encoding GetEncodingMode() = 0;
+  virtual bool Encode(CBC_EncoderContext* context) = 0;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_ENCODER_H_
diff --git a/fxbarcode/datamatrix/BC_EncoderContext.cpp b/fxbarcode/datamatrix/BC_EncoderContext.cpp
index fe0a497..3fbfee8 100644
--- a/fxbarcode/datamatrix/BC_EncoderContext.cpp
+++ b/fxbarcode/datamatrix/BC_EncoderContext.cpp
@@ -22,39 +22,29 @@
 
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 
-#include "fxbarcode/BC_UtilCodingConvert.h"
+#include <utility>
+
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
 
-CBC_EncoderContext::CBC_EncoderContext(const WideString& msg,
-                                       const WideString& ecLevel,
-                                       int32_t& e) {
-  ByteString dststr;
-  CBC_UtilCodingConvert::UnicodeToUTF8(msg, dststr);
-  WideString sb;
+CBC_EncoderContext::CBC_EncoderContext(const WideString& msg) {
+  ByteString dststr = msg.ToUTF8();
   size_t c = dststr.GetLength();
+  WideString sb;
+  sb.Reserve(c);
   for (size_t i = 0; i < c; i++) {
     wchar_t ch = static_cast<wchar_t>(dststr[i] & 0xff);
     if (ch == '?' && dststr[i] != '?') {
-      e = BCExceptionCharactersOutsideISO88591Encoding;
+      m_bHasCharactersOutsideISO88591Encoding = true;
     }
     sb += ch;
   }
-  m_msg = sb;
-  m_allowRectangular = true;
-  m_newEncoding = -1;
-  m_pos = 0;
-  m_symbolInfo = nullptr;
-  m_skipAtEnd = 0;
+  m_msg = std::move(sb);
+  m_codewords.Reserve(m_msg.GetLength());
 }
 
-CBC_EncoderContext::~CBC_EncoderContext() {}
-
-void CBC_EncoderContext::setAllowRectangular(bool allow) {
-  m_allowRectangular = allow;
-}
+CBC_EncoderContext::~CBC_EncoderContext() = default;
 
 void CBC_EncoderContext::setSkipAtEnd(int32_t count) {
   m_skipAtEnd = count;
@@ -73,34 +63,43 @@
 void CBC_EncoderContext::writeCodeword(wchar_t codeword) {
   m_codewords += codeword;
 }
+
 size_t CBC_EncoderContext::getCodewordCount() {
   return m_codewords.GetLength();
 }
-void CBC_EncoderContext::signalEncoderChange(int32_t encoding) {
+
+void CBC_EncoderContext::SignalEncoderChange(
+    CBC_HighLevelEncoder::Encoding encoding) {
   m_newEncoding = encoding;
 }
-void CBC_EncoderContext::resetEncoderSignal() {
-  m_newEncoding = -1;
+
+void CBC_EncoderContext::ResetEncoderSignal() {
+  m_newEncoding = CBC_HighLevelEncoder::Encoding::UNKNOWN;
 }
+
 bool CBC_EncoderContext::hasMoreCharacters() {
   return m_pos < getTotalMessageCharCount();
 }
+
 size_t CBC_EncoderContext::getRemainingCharacters() {
   return getTotalMessageCharCount() - m_pos;
 }
-void CBC_EncoderContext::updateSymbolInfo(int32_t& e) {
-  updateSymbolInfo(getCodewordCount(), e);
+
+bool CBC_EncoderContext::UpdateSymbolInfo() {
+  return UpdateSymbolInfo(getCodewordCount());
 }
-void CBC_EncoderContext::updateSymbolInfo(int32_t len, int32_t& e) {
+
+bool CBC_EncoderContext::UpdateSymbolInfo(size_t len) {
   if (!m_symbolInfo || len > m_symbolInfo->dataCapacity()) {
-    m_symbolInfo = CBC_SymbolInfo::lookup(len, m_allowRectangular, e);
-    if (e != BCExceptionNO)
-      return;
+    m_symbolInfo = CBC_SymbolInfo::Lookup(len, m_bAllowRectangular);
+    if (!m_symbolInfo)
+      return false;
   }
+  return true;
 }
 
 void CBC_EncoderContext::resetSymbolInfo() {
-  m_allowRectangular = true;
+  m_bAllowRectangular = true;
 }
 
 size_t CBC_EncoderContext::getTotalMessageCharCount() {
diff --git a/fxbarcode/datamatrix/BC_EncoderContext.h b/fxbarcode/datamatrix/BC_EncoderContext.h
index c0ddb20..e09b63a 100644
--- a/fxbarcode/datamatrix/BC_EncoderContext.h
+++ b/fxbarcode/datamatrix/BC_EncoderContext.h
@@ -9,42 +9,46 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/widestring.h"
+#include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 
 class CBC_SymbolInfo;
 
 class CBC_EncoderContext {
  public:
-  CBC_EncoderContext(const WideString& msg,
-                     const WideString& ecLevel,
-                     int32_t& e);
+  explicit CBC_EncoderContext(const WideString& msg);
   ~CBC_EncoderContext();
 
-  void setAllowRectangular(bool allow);
   void setSkipAtEnd(int32_t count);
   wchar_t getCurrentChar();
   wchar_t getCurrent();
   void writeCodewords(const WideString& codewords);
   void writeCodeword(wchar_t codeword);
   size_t getCodewordCount();
-  void signalEncoderChange(int32_t encoding);
-  void resetEncoderSignal();
+  void SignalEncoderChange(CBC_HighLevelEncoder::Encoding encoding);
+  void ResetEncoderSignal();
   bool hasMoreCharacters();
   size_t getRemainingCharacters();
-  void updateSymbolInfo(int32_t& e);
-  void updateSymbolInfo(int32_t len, int32_t& e);
+  bool UpdateSymbolInfo();
+  bool UpdateSymbolInfo(size_t len);
   void resetSymbolInfo();
 
+  bool HasCharactersOutsideISO88591Encoding() const {
+    return m_bHasCharactersOutsideISO88591Encoding;
+  }
+
   WideString m_msg;
   WideString m_codewords;
-  size_t m_pos;
-  int32_t m_newEncoding;
-  UnownedPtr<CBC_SymbolInfo> m_symbolInfo;
+  size_t m_pos = 0;
+  CBC_HighLevelEncoder::Encoding m_newEncoding =
+      CBC_HighLevelEncoder::Encoding::UNKNOWN;
+  UnownedPtr<const CBC_SymbolInfo> m_symbolInfo;
 
  private:
   size_t getTotalMessageCharCount();
 
-  bool m_allowRectangular;  // Force square when false.
-  size_t m_skipAtEnd;
+  bool m_bAllowRectangular = false;  // Force square when false.
+  bool m_bHasCharactersOutsideISO88591Encoding = false;
+  size_t m_skipAtEnd = 0;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_ENCODERCONTEXT_H_
diff --git a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
index 35077ee..9c6ff7d 100644
--- a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
+++ b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
@@ -22,11 +22,11 @@
 
 #include "fxbarcode/datamatrix/BC_ErrorCorrection.h"
 
+#include <algorithm>
 #include <vector>
 
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
 
 namespace {
 
@@ -100,111 +100,132 @@
     FACTORS_6,  FACTORS_7,  FACTORS_8,  FACTORS_9, FACTORS_10, FACTORS_11,
     FACTORS_12, FACTORS_13, FACTORS_14, FACTORS_15};
 
-}  // namespace
+constexpr uint8_t LOG[256] = {
+    0,   0,   1,   240, 2,   225, 241, 53,  3,   38,  226, 133, 242, 43,  54,
+    210, 4,   195, 39,  114, 227, 106, 134, 28,  243, 140, 44,  23,  55,  118,
+    211, 234, 5,   219, 196, 96,  40,  222, 115, 103, 228, 78,  107, 125, 135,
+    8,   29,  162, 244, 186, 141, 180, 45,  99,  24,  49,  56,  13,  119, 153,
+    212, 199, 235, 91,  6,   76,  220, 217, 197, 11,  97,  184, 41,  36,  223,
+    253, 116, 138, 104, 193, 229, 86,  79,  171, 108, 165, 126, 145, 136, 34,
+    9,   74,  30,  32,  163, 84,  245, 173, 187, 204, 142, 81,  181, 190, 46,
+    88,  100, 159, 25,  231, 50,  207, 57,  147, 14,  67,  120, 128, 154, 248,
+    213, 167, 200, 63,  236, 110, 92,  176, 7,   161, 77,  124, 221, 102, 218,
+    95,  198, 90,  12,  152, 98,  48,  185, 179, 42,  209, 37,  132, 224, 52,
+    254, 239, 117, 233, 139, 22,  105, 27,  194, 113, 230, 206, 87,  158, 80,
+    189, 172, 203, 109, 175, 166, 62,  127, 247, 146, 66,  137, 192, 35,  252,
+    10,  183, 75,  216, 31,  83,  33,  73,  164, 144, 85,  170, 246, 65,  174,
+    61,  188, 202, 205, 157, 143, 169, 82,  72,  182, 215, 191, 251, 47,  178,
+    89,  151, 101, 94,  160, 123, 26,  112, 232, 21,  51,  238, 208, 131, 58,
+    69,  148, 18,  15,  16,  68,  17,  121, 149, 129, 19,  155, 59,  249, 70,
+    214, 250, 168, 71,  201, 156, 64,  60,  237, 130, 111, 20,  93,  122, 177,
+    150};
 
-int32_t CBC_ErrorCorrection::MODULO_VALUE = 0x12D;
-int32_t CBC_ErrorCorrection::LOG[256] = {0};
-int32_t CBC_ErrorCorrection::ALOG[256] = {0};
-void CBC_ErrorCorrection::Initialize() {
-  int32_t p = 1;
-  for (int32_t i = 0; i < 255; i++) {
-    ALOG[i] = p;
-    LOG[p] = i;
-    p <<= 1;
-    if (p >= 256) {
-      p ^= MODULO_VALUE;
-    }
-  }
-}
-void CBC_ErrorCorrection::Finalize() {}
-CBC_ErrorCorrection::CBC_ErrorCorrection() {}
-CBC_ErrorCorrection::~CBC_ErrorCorrection() {}
-WideString CBC_ErrorCorrection::encodeECC200(WideString codewords,
-                                             CBC_SymbolInfo* symbolInfo,
-                                             int32_t& e) {
-  if (pdfium::base::checked_cast<int32_t>(codewords.GetLength()) !=
-      symbolInfo->dataCapacity()) {
-    e = BCExceptionIllegalArgument;
-    return WideString();
-  }
-  WideString sb;
-  sb += codewords;
-  int32_t blockCount = symbolInfo->getInterleavedBlockCount();
-  if (blockCount == 1) {
-    WideString ecc = createECCBlock(codewords, symbolInfo->errorCodewords(), e);
-    if (e != BCExceptionNO)
-      return WideString();
-    sb += ecc;
-  } else {
-    std::vector<int32_t> dataSizes(blockCount);
-    std::vector<int32_t> errorSizes(blockCount);
-    std::vector<int32_t> startPos(blockCount);
-    for (int32_t i = 0; i < blockCount; i++) {
-      dataSizes[i] = symbolInfo->getDataLengthForInterleavedBlock(i + 1);
-      errorSizes[i] = symbolInfo->getErrorLengthForInterleavedBlock(i + 1);
-      startPos[i] = 0;
-      if (i > 0) {
-        startPos[i] = startPos[i - 1] + dataSizes[i];
-      }
-    }
-    for (int32_t block = 0; block < blockCount; block++) {
-      WideString temp;
-      for (int32_t d = block; d < symbolInfo->dataCapacity(); d += blockCount) {
-        temp += (wchar_t)codewords[d];
-      }
-      WideString ecc = createECCBlock(temp, errorSizes[block], e);
-      if (e != BCExceptionNO)
-        return WideString();
-      int32_t pos = 0;
-      for (int32_t l = block; l < errorSizes[block] * blockCount;
-           l += blockCount) {
-        sb.SetAt(symbolInfo->dataCapacity() + l, ecc[pos++]);
-      }
-    }
-  }
-  return sb;
-}
-WideString CBC_ErrorCorrection::createECCBlock(WideString codewords,
-                                               int32_t numECWords,
-                                               int32_t& e) {
-  return createECCBlock(codewords, 0, codewords.GetLength(), numECWords, e);
-}
-WideString CBC_ErrorCorrection::createECCBlock(WideString codewords,
-                                               int32_t start,
-                                               int32_t len,
-                                               int32_t numECWords,
-                                               int32_t& e) {
-  static const size_t kFactorTableNum = sizeof(FACTOR_SETS) / sizeof(int32_t);
+constexpr uint8_t ALOG[256] = {
+    1,   2,   4,   8,   16,  32,  64,  128, 45,  90,  180, 69,  138, 57,  114,
+    228, 229, 231, 227, 235, 251, 219, 155, 27,  54,  108, 216, 157, 23,  46,
+    92,  184, 93,  186, 89,  178, 73,  146, 9,   18,  36,  72,  144, 13,  26,
+    52,  104, 208, 141, 55,  110, 220, 149, 7,   14,  28,  56,  112, 224, 237,
+    247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239, 243, 203, 187, 91,
+    182, 65,  130, 41,  82,  164, 101, 202, 185, 95,  190, 81,  162, 105, 210,
+    137, 63,  126, 252, 213, 135, 35,  70,  140, 53,  106, 212, 133, 39,  78,
+    156, 21,  42,  84,  168, 125, 250, 217, 159, 19,  38,  76,  152, 29,  58,
+    116, 232, 253, 215, 131, 43,  86,  172, 117, 234, 249, 223, 147, 11,  22,
+    44,  88,  176, 77,  154, 25,  50,  100, 200, 189, 87,  174, 113, 226, 233,
+    255, 211, 139, 59,  118, 236, 245, 199, 163, 107, 214, 129, 47,  94,  188,
+    85,  170, 121, 242, 201, 191, 83,  166, 97,  194, 169, 127, 254, 209, 143,
+    51,  102, 204, 181, 71,  142, 49,  98,  196, 165, 103, 206, 177, 79,  158,
+    17,  34,  68,  136, 61,  122, 244, 197, 167, 99,  198, 161, 111, 222, 145,
+    15,  30,  60,  120, 240, 205, 183, 67,  134, 33,  66,  132, 37,  74,  148,
+    5,   10,  20,  40,  80,  160, 109, 218, 153, 31,  62,  124, 248, 221, 151,
+    3,   6,   12,  24,  48,  96,  192, 173, 119, 238, 241, 207, 179, 75,  150,
+    0};
+
+WideString CreateECCBlock(const WideString& codewords, size_t numECWords) {
+  ASSERT(numECWords > 0);
+
+  const size_t len = codewords.GetLength();
+  static constexpr size_t kFactorTableNum = FX_ArraySize(FACTOR_SETS);
   size_t table = 0;
   while (table < kFactorTableNum && FACTOR_SETS[table] != numECWords)
-    table++;
+    ++table;
 
-  if (table >= kFactorTableNum) {
-    e = BCExceptionIllegalArgument;
+  if (table >= kFactorTableNum)
     return WideString();
-  }
-  uint16_t* ecc = FX_Alloc(uint16_t, numECWords);
-  memset(ecc, 0, numECWords * sizeof(uint16_t));
-  for (int32_t l = start; l < start + len; l++) {
-    uint16_t m = ecc[numECWords - 1] ^ codewords[l];
-    for (int32_t k = numECWords - 1; k > 0; k--) {
-      if (m != 0 && FACTORS[table][k] != 0) {
-        ecc[k] = (uint16_t)(ecc[k - 1] ^
-                            ALOG[(LOG[m] + LOG[FACTORS[table][k]]) % 255]);
+
+  std::vector<uint16_t> ecc(numECWords);
+  for (size_t i = 0; i < len; ++i) {
+    uint16_t m = ecc[numECWords - 1] ^ codewords[i];
+    for (int32_t j = numECWords - 1; j > 0; --j) {
+      if (m != 0 && FACTORS[table][j] != 0) {
+        ecc[j] = static_cast<uint16_t>(
+            ecc[j - 1] ^ ALOG[(LOG[m] + LOG[FACTORS[table][j]]) % 255]);
       } else {
-        ecc[k] = ecc[k - 1];
+        ecc[j] = ecc[j - 1];
       }
     }
     if (m != 0 && FACTORS[table][0] != 0) {
-      ecc[0] = (uint16_t)ALOG[(LOG[m] + LOG[FACTORS[table][0]]) % 255];
+      ecc[0] =
+          static_cast<uint16_t>(ALOG[(LOG[m] + LOG[FACTORS[table][0]]) % 255]);
     } else {
       ecc[0] = 0;
     }
   }
   WideString strecc;
-  for (int32_t j = 0; j < numECWords; j++) {
-    strecc += (wchar_t)ecc[numECWords - j - 1];
-  }
-  FX_Free(ecc);
+  strecc.Reserve(numECWords);
+  for (size_t i = 0; i < numECWords; ++i)
+    strecc.InsertAtBack(static_cast<wchar_t>(ecc[numECWords - i - 1]));
+
+  ASSERT(!strecc.IsEmpty());
   return strecc;
 }
+
+}  // namespace
+
+WideString CBC_ErrorCorrection::EncodeECC200(const WideString& codewords,
+                                             const CBC_SymbolInfo* symbolInfo) {
+  if (codewords.GetLength() != symbolInfo->dataCapacity())
+    return WideString();
+
+  WideString sb = codewords;
+  size_t blockCount = symbolInfo->getInterleavedBlockCount();
+  if (blockCount == 1) {
+    WideString ecc = CreateECCBlock(codewords, symbolInfo->errorCodewords());
+    if (ecc.IsEmpty())
+      return WideString();
+    sb += ecc;
+  } else {
+    std::vector<size_t> dataSizes(blockCount);
+    std::vector<size_t> errorSizes(blockCount);
+    std::vector<size_t> startPos(blockCount);
+    for (size_t i = 0; i < blockCount; ++i) {
+      dataSizes[i] = symbolInfo->getDataLengthForInterleavedBlock();
+      errorSizes[i] = symbolInfo->getErrorLengthForInterleavedBlock();
+      startPos[i] = i > 0 ? startPos[i - 1] + dataSizes[i] : 0;
+    }
+
+    size_t max_error_sizes =
+        *std::max_element(errorSizes.begin(), errorSizes.end()) * blockCount;
+    sb.Reserve(sb.GetLength() + max_error_sizes);
+    for (size_t i = 0; i < max_error_sizes; ++i)
+      sb.InsertAtBack(0);
+
+    for (size_t block = 0; block < blockCount; ++block) {
+      WideString temp;
+      if (symbolInfo->dataCapacity() > block)
+        temp.Reserve((symbolInfo->dataCapacity() - block / blockCount) + 1);
+      for (size_t d = block; d < symbolInfo->dataCapacity(); d += blockCount)
+        temp.InsertAtBack(static_cast<wchar_t>(codewords[d]));
+
+      WideString ecc = CreateECCBlock(temp, errorSizes[block]);
+      if (ecc.IsEmpty())
+        return WideString();
+
+      for (size_t pos = 0, i = block; i < errorSizes[block] * blockCount;
+           ++pos, i += blockCount) {
+        sb.SetAt(symbolInfo->dataCapacity() + i, ecc[pos]);
+      }
+    }
+  }
+  ASSERT(!sb.IsEmpty());
+  return sb;
+}
diff --git a/fxbarcode/datamatrix/BC_ErrorCorrection.h b/fxbarcode/datamatrix/BC_ErrorCorrection.h
index 361b205..676093f 100644
--- a/fxbarcode/datamatrix/BC_ErrorCorrection.h
+++ b/fxbarcode/datamatrix/BC_ErrorCorrection.h
@@ -13,29 +13,12 @@
 
 class CBC_ErrorCorrection {
  public:
-  CBC_ErrorCorrection();
-  virtual ~CBC_ErrorCorrection();
+  CBC_ErrorCorrection() = delete;
+  ~CBC_ErrorCorrection() = delete;
 
-  static void Initialize();
-  static void Finalize();
-  static WideString encodeECC200(WideString codewords,
-                                 CBC_SymbolInfo* symbolInfo,
-                                 int32_t& e);
-
- private:
-  static int32_t MODULO_VALUE;
-  static int32_t LOG[256];
-  static int32_t ALOG[256];
-
- private:
-  static WideString createECCBlock(WideString codewords,
-                                   int32_t numECWords,
-                                   int32_t& e);
-  static WideString createECCBlock(WideString codewords,
-                                   int32_t start,
-                                   int32_t len,
-                                   int32_t numECWords,
-                                   int32_t& e);
+  // Returns an empty string on failure.
+  static WideString EncodeECC200(const WideString& codewords,
+                                 const CBC_SymbolInfo* symbolInfo);
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_ERRORCORRECTION_H_
diff --git a/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp b/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp
index 56a8847..fbc4262 100644
--- a/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp
@@ -22,11 +22,13 @@
 
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 
+#include <algorithm>
+#include <array>
 #include <limits>
 #include <memory>
 #include <vector>
 
-#include "fxbarcode/BC_UtilCodingConvert.h"
+#include "core/fxcrt/fx_extension.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_ASCIIEncoder.h"
 #include "fxbarcode/datamatrix/BC_Base256Encoder.h"
@@ -37,53 +39,109 @@
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
 #include "fxbarcode/datamatrix/BC_TextEncoder.h"
 #include "fxbarcode/datamatrix/BC_X12Encoder.h"
-#include "fxbarcode/utils.h"
 #include "third_party/base/ptr_util.h"
 
-const wchar_t CBC_HighLevelEncoder::LATCH_TO_C40 = 230;
-const wchar_t CBC_HighLevelEncoder::LATCH_TO_BASE256 = 231;
-const wchar_t CBC_HighLevelEncoder::UPPER_SHIFT = 235;
-const wchar_t CBC_HighLevelEncoder::LATCH_TO_ANSIX12 = 238;
-const wchar_t CBC_HighLevelEncoder::LATCH_TO_TEXT = 239;
-const wchar_t CBC_HighLevelEncoder::LATCH_TO_EDIFACT = 240;
-const wchar_t CBC_HighLevelEncoder::C40_UNLATCH = 254;
-const wchar_t CBC_HighLevelEncoder::X12_UNLATCH = 254;
-const wchar_t CBC_HighLevelEncoder::PAD = 129;
-const wchar_t CBC_HighLevelEncoder::MACRO_05 = 236;
-const wchar_t CBC_HighLevelEncoder::MACRO_06 = 237;
-const wchar_t CBC_HighLevelEncoder::MACRO_05_HEADER[] = L"[)>05";
-const wchar_t CBC_HighLevelEncoder::MACRO_06_HEADER[] = L"[)>06";
-const wchar_t CBC_HighLevelEncoder::MACRO_TRAILER = 0x0004;
+namespace {
 
-CBC_HighLevelEncoder::CBC_HighLevelEncoder() {}
-CBC_HighLevelEncoder::~CBC_HighLevelEncoder() {}
+const wchar_t kPad = 129;
+const wchar_t kMacro05 = 236;
+const wchar_t kMacro06 = 237;
+const wchar_t kMacro05Header[] =
+    L"[)>\036"
+    L"05";
+const wchar_t kMacro06Header[] =
+    L"[)>\036"
+    L"06";
+const wchar_t kMacroTrailer = 0x0004;
 
-std::vector<uint8_t>& CBC_HighLevelEncoder::getBytesForMessage(WideString msg) {
-  ByteString bytestr;
-  CBC_UtilCodingConvert::UnicodeToUTF8(msg, bytestr);
-  m_bytearray.insert(m_bytearray.end(), bytestr.begin(), bytestr.end());
-  return m_bytearray;
+constexpr size_t kEncoderCount =
+    static_cast<size_t>(CBC_HighLevelEncoder::Encoding::LAST) + 1;
+static_assert(kEncoderCount == 6, "Bad encoder count");
+
+wchar_t Randomize253State(wchar_t ch, int32_t codewordPosition) {
+  int32_t pseudoRandom = ((149 * codewordPosition) % 253) + 1;
+  int32_t tempVariable = ch + pseudoRandom;
+  return tempVariable <= 254 ? static_cast<wchar_t>(tempVariable)
+                             : static_cast<wchar_t>(tempVariable - 254);
 }
 
+int32_t FindMinimums(const std::array<float, kEncoderCount>& charCounts,
+                     std::array<int32_t, kEncoderCount>* intCharCounts,
+                     std::array<uint8_t, kEncoderCount>* mins) {
+  int32_t min = std::numeric_limits<int32_t>::max();
+  for (size_t i = 0; i < kEncoderCount; ++i) {
+    int32_t current = static_cast<int32_t>(ceil(charCounts[i]));
+    (*intCharCounts)[i] = current;
+    if (min > current) {
+      min = current;
+      for (auto& m : *mins)
+        m = 0;
+    }
+    if (min == current)
+      (*mins)[i]++;
+  }
+  return min;
+}
+
+int32_t GetMinimumCount(const std::array<uint8_t, kEncoderCount>& mins) {
+  int32_t count = 0;
+  for (const auto& m : mins)
+    count += m;
+  return count;
+}
+
+bool IsNativeC40(wchar_t ch) {
+  return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
+}
+
+bool IsNativeText(wchar_t ch) {
+  return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');
+}
+
+bool IsX12TermSep(wchar_t ch) {
+  return (ch == '\r') || (ch == '*') || (ch == '>');
+}
+
+bool IsNativeX12(wchar_t ch) {
+  return IsX12TermSep(ch) || (ch == ' ') || (ch >= '0' && ch <= '9') ||
+         (ch >= 'A' && ch <= 'Z');
+}
+
+bool IsNativeEDIFACT(wchar_t ch) {
+  return ch >= ' ' && ch <= '^';
+}
+
+size_t EncoderIndex(CBC_HighLevelEncoder::Encoding encoding) {
+  ASSERT(encoding != CBC_HighLevelEncoder::Encoding::UNKNOWN);
+  return static_cast<size_t>(encoding);
+}
+
+}  // namespace
+
 // static
-WideString CBC_HighLevelEncoder::encodeHighLevel(WideString msg,
-                                                 WideString ecLevel,
-                                                 bool allowRectangular,
-                                                 int32_t& e) {
-  CBC_EncoderContext context(msg, ecLevel, e);
-  if (e != BCExceptionNO)
+WideString CBC_HighLevelEncoder::EncodeHighLevel(const WideString& msg) {
+  // Per spec. Alpha numeric input is even shorter.
+  static constexpr size_t kMaxNumericInputLength = 3116;
+
+  // Exit early if the input is too long. It will fail no matter what.
+  if (msg.GetLength() > kMaxNumericInputLength)
     return WideString();
 
-  context.setAllowRectangular(allowRectangular);
-  if ((msg.Left(6) == MACRO_05_HEADER) && (msg.Last() == MACRO_TRAILER)) {
-    context.writeCodeword(MACRO_05);
-    context.setSkipAtEnd(2);
-    context.m_pos += 6;
-  } else if ((msg.Left(6) == MACRO_06_HEADER) &&
-             (msg.Last() == MACRO_TRAILER)) {
-    context.writeCodeword(MACRO_06);
-    context.setSkipAtEnd(2);
-    context.m_pos += 6;
+  CBC_EncoderContext context(msg);
+  if (context.HasCharactersOutsideISO88591Encoding())
+    return WideString();
+
+  if (msg.Back() == kMacroTrailer) {
+    WideString left = msg.First(6);
+    if (left == kMacro05Header) {
+      context.writeCodeword(kMacro05);
+      context.setSkipAtEnd(2);
+      context.m_pos += 6;
+    } else if (left == kMacro06Header) {
+      context.writeCodeword(kMacro06);
+      context.setSkipAtEnd(2);
+      context.m_pos += 6;
+    }
   }
 
   std::vector<std::unique_ptr<CBC_Encoder>> encoders;
@@ -93,256 +151,180 @@
   encoders.push_back(pdfium::MakeUnique<CBC_X12Encoder>());
   encoders.push_back(pdfium::MakeUnique<CBC_EdifactEncoder>());
   encoders.push_back(pdfium::MakeUnique<CBC_Base256Encoder>());
-  int32_t encodingMode = ASCII_ENCODATION;
+  Encoding encodingMode = Encoding::ASCII;
   while (context.hasMoreCharacters()) {
-    encoders[encodingMode]->Encode(context, e);
-    if (e != BCExceptionNO)
-      return L"";
+    if (!encoders[EncoderIndex(encodingMode)]->Encode(&context))
+      return WideString();
 
-    if (context.m_newEncoding >= 0) {
+    if (context.m_newEncoding != Encoding::UNKNOWN) {
       encodingMode = context.m_newEncoding;
-      context.resetEncoderSignal();
+      context.ResetEncoderSignal();
     }
   }
-  int32_t len = context.m_codewords.GetLength();
-  context.updateSymbolInfo(e);
-  if (e != BCExceptionNO)
-    return L"";
+  size_t len = context.m_codewords.GetLength();
+  if (!context.UpdateSymbolInfo())
+    return WideString();
 
-  int32_t capacity = context.m_symbolInfo->dataCapacity();
+  size_t capacity = context.m_symbolInfo->dataCapacity();
   if (len < capacity) {
-    if (encodingMode != ASCII_ENCODATION &&
-        encodingMode != BASE256_ENCODATION) {
+    if (encodingMode != Encoding::ASCII && encodingMode != Encoding::BASE256)
       context.writeCodeword(0x00fe);
-    }
   }
   WideString codewords = context.m_codewords;
-  if (pdfium::base::checked_cast<int32_t>(codewords.GetLength()) < capacity) {
-    codewords += PAD;
-  }
-  while (pdfium::base::checked_cast<int32_t>(codewords.GetLength()) <
-         capacity) {
-    codewords += (randomize253State(
-        PAD, pdfium::base::checked_cast<int32_t>(codewords.GetLength()) + 1));
-  }
+  if (codewords.GetLength() < capacity)
+    codewords += kPad;
+
+  while (codewords.GetLength() < capacity)
+    codewords += Randomize253State(kPad, codewords.GetLength() + 1);
+
+  ASSERT(!codewords.IsEmpty());
   return codewords;
 }
-int32_t CBC_HighLevelEncoder::lookAheadTest(WideString msg,
-                                            int32_t startpos,
-                                            int32_t currentMode) {
-  if (startpos >= pdfium::base::checked_cast<int32_t>(msg.GetLength())) {
+
+// static
+CBC_HighLevelEncoder::Encoding CBC_HighLevelEncoder::LookAheadTest(
+    const WideString& msg,
+    size_t startpos,
+    CBC_HighLevelEncoder::Encoding currentMode) {
+  if (startpos >= msg.GetLength())
     return currentMode;
-  }
-  std::vector<float> charCounts;
-  if (currentMode == ASCII_ENCODATION) {
-    charCounts.push_back(0);
-    charCounts.push_back(1);
-    charCounts.push_back(1);
-    charCounts.push_back(1);
-    charCounts.push_back(1);
-    charCounts.push_back(1.25f);
+
+  std::array<float, kEncoderCount> charCounts;
+  if (currentMode == Encoding::ASCII) {
+    charCounts = {0, 1, 1, 1, 1, 1.25f};
   } else {
-    charCounts.push_back(1);
-    charCounts.push_back(2);
-    charCounts.push_back(2);
-    charCounts.push_back(2);
-    charCounts.push_back(2);
-    charCounts.push_back(2.25f);
-    charCounts[currentMode] = 0;
+    charCounts = {1, 2, 2, 2, 2, 2.25f};
+    charCounts[EncoderIndex(currentMode)] = 0;
   }
-  int32_t charsProcessed = 0;
+
+  size_t charsProcessed = 0;
   while (true) {
-    if ((startpos + charsProcessed) ==
-        pdfium::base::checked_cast<int32_t>(msg.GetLength())) {
-      int32_t min = std::numeric_limits<int32_t>::max();
-      std::vector<uint8_t> mins(6);
-      std::vector<int32_t> intCharCounts(6);
-      min = findMinimums(charCounts, intCharCounts, min, mins);
-      int32_t minCount = getMinimumCount(mins);
-      if (intCharCounts[ASCII_ENCODATION] == min) {
-        return ASCII_ENCODATION;
+    if ((startpos + charsProcessed) == msg.GetLength()) {
+      std::array<int32_t, kEncoderCount> intCharCounts;
+      std::array<uint8_t, kEncoderCount> mins;
+      int32_t min = FindMinimums(charCounts, &intCharCounts, &mins);
+      if (intCharCounts[EncoderIndex(Encoding::ASCII)] == min)
+        return Encoding::ASCII;
+      const int32_t minCount = GetMinimumCount(mins);
+      if (minCount == 1) {
+        if (mins[EncoderIndex(Encoding::BASE256)] > 0)
+          return Encoding::BASE256;
+        if (mins[EncoderIndex(Encoding::EDIFACT)] > 0)
+          return Encoding::EDIFACT;
+        if (mins[EncoderIndex(Encoding::TEXT)] > 0)
+          return Encoding::TEXT;
+        if (mins[EncoderIndex(Encoding::X12)] > 0)
+          return Encoding::X12;
       }
-      if (minCount == 1 && mins[BASE256_ENCODATION] > 0) {
-        return BASE256_ENCODATION;
-      }
-      if (minCount == 1 && mins[EDIFACT_ENCODATION] > 0) {
-        return EDIFACT_ENCODATION;
-      }
-      if (minCount == 1 && mins[TEXT_ENCODATION] > 0) {
-        return TEXT_ENCODATION;
-      }
-      if (minCount == 1 && mins[X12_ENCODATION] > 0) {
-        return X12_ENCODATION;
-      }
-      return C40_ENCODATION;
+      return Encoding::C40;
     }
+
     wchar_t c = msg[startpos + charsProcessed];
     charsProcessed++;
-    if (isDigit(c)) {
-      charCounts[ASCII_ENCODATION] += 0.5;
-    } else if (isExtendedASCII(c)) {
-      charCounts[ASCII_ENCODATION] = (float)ceil(charCounts[ASCII_ENCODATION]);
-      charCounts[ASCII_ENCODATION] += 2;
-    } else {
-      charCounts[ASCII_ENCODATION] = (float)ceil(charCounts[ASCII_ENCODATION]);
-      charCounts[ASCII_ENCODATION]++;
+    {
+      auto& count = charCounts[EncoderIndex(Encoding::ASCII)];
+      if (FXSYS_IsDecimalDigit(c))
+        count += 0.5;
+      else if (IsExtendedASCII(c))
+        count = ceilf(count) + 2;
+      else
+        count = ceilf(count) + 1;
     }
-    if (isNativeC40(c)) {
-      charCounts[C40_ENCODATION] += 2.0f / 3.0f;
-    } else if (isExtendedASCII(c)) {
-      charCounts[C40_ENCODATION] += 8.0f / 3.0f;
-    } else {
-      charCounts[C40_ENCODATION] += 4.0f / 3.0f;
+
+    {
+      auto& count = charCounts[EncoderIndex(Encoding::C40)];
+      if (IsNativeC40(c))
+        count += 2.0f / 3.0f;
+      else if (IsExtendedASCII(c))
+        count += 8.0f / 3.0f;
+      else
+        count += 4.0f / 3.0f;
     }
-    if (isNativeText(c)) {
-      charCounts[TEXT_ENCODATION] += 2.0f / 3.0f;
-    } else if (isExtendedASCII(c)) {
-      charCounts[TEXT_ENCODATION] += 8.0f / 3.0f;
-    } else {
-      charCounts[TEXT_ENCODATION] += 4.0f / 3.0f;
+
+    {
+      auto& count = charCounts[EncoderIndex(Encoding::TEXT)];
+      if (IsNativeText(c))
+        count += 2.0f / 3.0f;
+      else if (IsExtendedASCII(c))
+        count += 8.0f / 3.0f;
+      else
+        count += 4.0f / 3.0f;
     }
-    if (isNativeX12(c)) {
-      charCounts[X12_ENCODATION] += 2.0f / 3.0f;
-    } else if (isExtendedASCII(c)) {
-      charCounts[X12_ENCODATION] += 13.0f / 3.0f;
-    } else {
-      charCounts[X12_ENCODATION] += 10.0f / 3.0f;
+
+    {
+      auto& count = charCounts[EncoderIndex(Encoding::X12)];
+      if (IsNativeX12(c))
+        count += 2.0f / 3.0f;
+      else if (IsExtendedASCII(c))
+        count += 13.0f / 3.0f;
+      else
+        count += 10.0f / 3.0f;
     }
-    if (isNativeEDIFACT(c)) {
-      charCounts[EDIFACT_ENCODATION] += 3.0f / 4.0f;
-    } else if (isExtendedASCII(c)) {
-      charCounts[EDIFACT_ENCODATION] += 17.0f / 4.0f;
-    } else {
-      charCounts[EDIFACT_ENCODATION] += 13.0f / 4.0f;
+
+    {
+      auto& count = charCounts[EncoderIndex(Encoding::EDIFACT)];
+      if (IsNativeEDIFACT(c))
+        count += 3.0f / 4.0f;
+      else if (IsExtendedASCII(c))
+        count += 17.0f / 4.0f;
+      else
+        count += 13.0f / 4.0f;
     }
-    charCounts[BASE256_ENCODATION]++;
-    if (charsProcessed >= 4) {
-      std::vector<int32_t> intCharCounts(6);
-      std::vector<uint8_t> mins(6);
-      findMinimums(charCounts, intCharCounts,
-                   std::numeric_limits<int32_t>::max(), mins);
-      int32_t minCount = getMinimumCount(mins);
-      if (intCharCounts[ASCII_ENCODATION] < intCharCounts[BASE256_ENCODATION] &&
-          intCharCounts[ASCII_ENCODATION] < intCharCounts[C40_ENCODATION] &&
-          intCharCounts[ASCII_ENCODATION] < intCharCounts[TEXT_ENCODATION] &&
-          intCharCounts[ASCII_ENCODATION] < intCharCounts[X12_ENCODATION] &&
-          intCharCounts[ASCII_ENCODATION] < intCharCounts[EDIFACT_ENCODATION]) {
-        return ASCII_ENCODATION;
-      }
-      if (intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION] ||
-          (mins[C40_ENCODATION] + mins[TEXT_ENCODATION] + mins[X12_ENCODATION] +
-           mins[EDIFACT_ENCODATION]) == 0) {
-        return BASE256_ENCODATION;
-      }
-      if (minCount == 1 && mins[EDIFACT_ENCODATION] > 0) {
-        return EDIFACT_ENCODATION;
-      }
-      if (minCount == 1 && mins[TEXT_ENCODATION] > 0) {
-        return TEXT_ENCODATION;
-      }
-      if (minCount == 1 && mins[X12_ENCODATION] > 0) {
-        return X12_ENCODATION;
-      }
-      if (intCharCounts[C40_ENCODATION] + 1 < intCharCounts[ASCII_ENCODATION] &&
-          intCharCounts[C40_ENCODATION] + 1 <
-              intCharCounts[BASE256_ENCODATION] &&
-          intCharCounts[C40_ENCODATION] + 1 <
-              intCharCounts[EDIFACT_ENCODATION] &&
-          intCharCounts[C40_ENCODATION] + 1 < intCharCounts[TEXT_ENCODATION]) {
-        if (intCharCounts[C40_ENCODATION] < intCharCounts[X12_ENCODATION]) {
-          return C40_ENCODATION;
+
+    charCounts[EncoderIndex(Encoding::BASE256)]++;
+    if (charsProcessed < 4)
+      continue;
+
+    std::array<int32_t, kEncoderCount> intCharCounts;
+    std::array<uint8_t, kEncoderCount> mins;
+    FindMinimums(charCounts, &intCharCounts, &mins);
+    int32_t minCount = GetMinimumCount(mins);
+    int32_t ascii_count = intCharCounts[EncoderIndex(Encoding::ASCII)];
+    int32_t c40_count = intCharCounts[EncoderIndex(Encoding::C40)];
+    int32_t text_count = intCharCounts[EncoderIndex(Encoding::TEXT)];
+    int32_t x12_count = intCharCounts[EncoderIndex(Encoding::X12)];
+    int32_t editfact_count = intCharCounts[EncoderIndex(Encoding::EDIFACT)];
+    int32_t base256_count = intCharCounts[EncoderIndex(Encoding::BASE256)];
+    int32_t bet_min = std::min({base256_count, editfact_count, text_count});
+    if (ascii_count < bet_min && ascii_count < c40_count &&
+        ascii_count < x12_count) {
+      return Encoding::ASCII;
+    }
+    if (base256_count < ascii_count ||
+        (mins[EncoderIndex(Encoding::C40)] +
+         mins[EncoderIndex(Encoding::TEXT)] +
+         mins[EncoderIndex(Encoding::X12)] +
+         mins[EncoderIndex(Encoding::EDIFACT)]) == 0) {
+      return Encoding::BASE256;
+    }
+    if (minCount == 1) {
+      if (mins[EncoderIndex(Encoding::EDIFACT)] > 0)
+        return Encoding::EDIFACT;
+      if (mins[EncoderIndex(Encoding::TEXT)] > 0)
+        return Encoding::TEXT;
+      if (mins[EncoderIndex(Encoding::X12)] > 0)
+        return Encoding::X12;
+    }
+    if (c40_count + 1 < ascii_count && c40_count + 1 < bet_min) {
+      if (c40_count < x12_count)
+        return Encoding::C40;
+      if (c40_count == x12_count) {
+        size_t p = startpos + charsProcessed + 1;
+        while (p < msg.GetLength()) {
+          wchar_t tc = msg[p];
+          if (IsX12TermSep(tc))
+            return Encoding::X12;
+          if (!IsNativeX12(tc))
+            break;
+          p++;
         }
-        if (intCharCounts[C40_ENCODATION] == intCharCounts[X12_ENCODATION]) {
-          int32_t p = startpos + charsProcessed + 1;
-          int32_t checked_length =
-              pdfium::base::checked_cast<int32_t>(msg.GetLength());
-          while (p < checked_length) {
-            wchar_t tc = msg[p];
-            if (isX12TermSep(tc)) {
-              return X12_ENCODATION;
-            }
-            if (!isNativeX12(tc)) {
-              break;
-            }
-            p++;
-          }
-          return C40_ENCODATION;
-        }
+        return Encoding::C40;
       }
     }
   }
 }
-bool CBC_HighLevelEncoder::isDigit(wchar_t ch) {
-  return ch >= '0' && ch <= '9';
-}
-bool CBC_HighLevelEncoder::isExtendedASCII(wchar_t ch) {
+
+// static
+bool CBC_HighLevelEncoder::IsExtendedASCII(wchar_t ch) {
   return ch >= 128 && ch <= 255;
 }
-int32_t CBC_HighLevelEncoder::determineConsecutiveDigitCount(WideString msg,
-                                                             int32_t startpos) {
-  int32_t count = 0;
-  int32_t len = msg.GetLength();
-  int32_t idx = startpos;
-  if (idx < len) {
-    wchar_t ch = msg[idx];
-    while (isDigit(ch) && idx < len) {
-      count++;
-      idx++;
-      if (idx < len) {
-        ch = msg[idx];
-      }
-    }
-  }
-  return count;
-}
-
-wchar_t CBC_HighLevelEncoder::randomize253State(wchar_t ch,
-                                                int32_t codewordPosition) {
-  int32_t pseudoRandom = ((149 * codewordPosition) % 253) + 1;
-  int32_t tempVariable = ch + pseudoRandom;
-  return tempVariable <= 254 ? (wchar_t)tempVariable
-                             : (wchar_t)(tempVariable - 254);
-}
-int32_t CBC_HighLevelEncoder::findMinimums(std::vector<float>& charCounts,
-                                           std::vector<int32_t>& intCharCounts,
-                                           int32_t min,
-                                           std::vector<uint8_t>& mins) {
-  for (size_t l = 0; l < mins.size(); l++)
-    mins[l] = 0;
-
-  for (size_t i = 0; i < 6; i++) {
-    intCharCounts[i] = static_cast<int32_t>(ceil(charCounts[i]));
-    int32_t current = intCharCounts[i];
-    if (min > current) {
-      min = current;
-      for (size_t j = 0; j < mins.size(); j++)
-        mins[j] = 0;
-    }
-    if (min == current)
-      mins[i]++;
-  }
-  return min;
-}
-int32_t CBC_HighLevelEncoder::getMinimumCount(std::vector<uint8_t>& mins) {
-  int32_t minCount = 0;
-  for (int32_t i = 0; i < 6; i++) {
-    minCount += mins[i];
-  }
-  return minCount;
-}
-bool CBC_HighLevelEncoder::isNativeC40(wchar_t ch) {
-  return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
-}
-bool CBC_HighLevelEncoder::isNativeText(wchar_t ch) {
-  return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');
-}
-bool CBC_HighLevelEncoder::isNativeX12(wchar_t ch) {
-  return isX12TermSep(ch) || (ch == ' ') || (ch >= '0' && ch <= '9') ||
-         (ch >= 'A' && ch <= 'Z');
-}
-bool CBC_HighLevelEncoder::isX12TermSep(wchar_t ch) {
-  return (ch == '\r') || (ch == '*') || (ch == '>');
-}
-bool CBC_HighLevelEncoder::isNativeEDIFACT(wchar_t ch) {
-  return ch >= ' ' && ch <= '^';
-}
diff --git a/fxbarcode/datamatrix/BC_HighLevelEncoder.h b/fxbarcode/datamatrix/BC_HighLevelEncoder.h
index 50cfc8c..14f64e2 100644
--- a/fxbarcode/datamatrix/BC_HighLevelEncoder.h
+++ b/fxbarcode/datamatrix/BC_HighLevelEncoder.h
@@ -11,62 +11,38 @@
 
 #include "core/fxcrt/widestring.h"
 
-#define ASCII_ENCODATION 0
-#define C40_ENCODATION 1
-#define TEXT_ENCODATION 2
-#define X12_ENCODATION 3
-#define EDIFACT_ENCODATION 4
-#define BASE256_ENCODATION 5
-
 class CBC_HighLevelEncoder {
  public:
-  CBC_HighLevelEncoder();
-  ~CBC_HighLevelEncoder();
+  enum class Encoding : int8_t {
+    UNKNOWN = -1,
+    ASCII = 0,
+    C40,
+    TEXT,
+    X12,
+    EDIFACT,
+    BASE256,
+    LAST = BASE256,
+  };
 
-  std::vector<uint8_t>& getBytesForMessage(WideString msg);
+  CBC_HighLevelEncoder() = delete;
+  ~CBC_HighLevelEncoder() = delete;
 
-  static WideString encodeHighLevel(WideString msg,
-                                    WideString ecLevel,
-                                    bool allowRectangular,
-                                    int32_t& e);
-  static int32_t lookAheadTest(WideString msg,
-                               int32_t startpos,
-                               int32_t currentMode);
-  static bool isDigit(wchar_t ch);
-  static bool isExtendedASCII(wchar_t ch);
-  static int32_t determineConsecutiveDigitCount(WideString msg,
-                                                int32_t startpos);
+  // Returns an empty string on failure.
+  static WideString EncodeHighLevel(const WideString& msg);
 
-  static const wchar_t LATCH_TO_C40;
-  static const wchar_t LATCH_TO_BASE256;
-  static const wchar_t UPPER_SHIFT;
-  static const wchar_t LATCH_TO_ANSIX12;
-  static const wchar_t LATCH_TO_TEXT;
-  static const wchar_t LATCH_TO_EDIFACT;
-  static const wchar_t C40_UNLATCH;
-  static const wchar_t X12_UNLATCH;
+  static Encoding LookAheadTest(const WideString& msg,
+                                size_t startpos,
+                                Encoding currentMode);
+  static bool IsExtendedASCII(wchar_t ch);
 
- private:
-  static wchar_t randomize253State(wchar_t ch, int32_t codewordPosition);
-  static int32_t findMinimums(std::vector<float>& charCounts,
-                              std::vector<int32_t>& intCharCounts,
-                              int32_t min,
-                              std::vector<uint8_t>& mins);
-  static int32_t getMinimumCount(std::vector<uint8_t>& mins);
-  static bool isNativeC40(wchar_t ch);
-  static bool isNativeText(wchar_t ch);
-  static bool isNativeX12(wchar_t ch);
-  static bool isX12TermSep(wchar_t ch);
-  static bool isNativeEDIFACT(wchar_t ch);
-
-  static const wchar_t PAD;
-  static const wchar_t MACRO_05;
-  static const wchar_t MACRO_06;
-  static const wchar_t MACRO_05_HEADER[];
-  static const wchar_t MACRO_06_HEADER[];
-  static const wchar_t MACRO_TRAILER;
-
-  std::vector<uint8_t> m_bytearray;
+  static const wchar_t LATCH_TO_C40 = 230;
+  static const wchar_t LATCH_TO_BASE256 = 231;
+  static const wchar_t UPPER_SHIFT = 235;
+  static const wchar_t LATCH_TO_ANSIX12 = 238;
+  static const wchar_t LATCH_TO_TEXT = 239;
+  static const wchar_t LATCH_TO_EDIFACT = 240;
+  static const wchar_t C40_UNLATCH = 254;
+  static const wchar_t X12_UNLATCH = 254;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_HIGHLEVELENCODER_H_
diff --git a/fxbarcode/datamatrix/BC_SymbolInfo.cpp b/fxbarcode/datamatrix/BC_SymbolInfo.cpp
index f4d99d2..d80afc2 100644
--- a/fxbarcode/datamatrix/BC_SymbolInfo.cpp
+++ b/fxbarcode/datamatrix/BC_SymbolInfo.cpp
@@ -25,11 +25,10 @@
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
-#include "fxbarcode/utils.h"
 
 namespace {
 
-const size_t kSymbolsCount = 30;
+constexpr size_t kSymbolsCount = 30;
 
 CBC_SymbolInfo* g_symbols[kSymbolsCount] = {
     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
@@ -79,8 +78,8 @@
   }
 }
 
-CBC_SymbolInfo::CBC_SymbolInfo(int32_t dataCapacity,
-                               int32_t errorCodewords,
+CBC_SymbolInfo::CBC_SymbolInfo(size_t dataCapacity,
+                               size_t errorCodewords,
                                int32_t matrixWidth,
                                int32_t matrixHeight,
                                int32_t dataRegions)
@@ -92,13 +91,13 @@
                      dataCapacity,
                      errorCodewords) {}
 
-CBC_SymbolInfo::CBC_SymbolInfo(int32_t dataCapacity,
-                               int32_t errorCodewords,
+CBC_SymbolInfo::CBC_SymbolInfo(size_t dataCapacity,
+                               size_t errorCodewords,
                                int32_t matrixWidth,
                                int32_t matrixHeight,
                                int32_t dataRegions,
-                               int32_t rsBlockData,
-                               int32_t rsBlockError)
+                               size_t rsBlockData,
+                               size_t rsBlockError)
     : m_rectangular(matrixWidth != matrixHeight),
       m_dataCapacity(dataCapacity),
       m_errorCodewords(errorCodewords),
@@ -108,20 +107,18 @@
       m_rsBlockData(rsBlockData),
       m_rsBlockError(rsBlockError) {}
 
-CBC_SymbolInfo::~CBC_SymbolInfo() {}
+CBC_SymbolInfo::~CBC_SymbolInfo() = default;
 
-CBC_SymbolInfo* CBC_SymbolInfo::lookup(int32_t dataCodewords,
-                                       bool allowRectangular,
-                                       int32_t& e) {
+const CBC_SymbolInfo* CBC_SymbolInfo::Lookup(size_t iDataCodewords,
+                                             bool bAllowRectangular) {
   for (size_t i = 0; i < kSymbolsCount; i++) {
     CBC_SymbolInfo* symbol = g_symbols[i];
-    if (symbol->m_rectangular && !allowRectangular)
+    if (symbol->m_rectangular && !bAllowRectangular)
       continue;
 
-    if (dataCodewords <= symbol->dataCapacity())
+    if (iDataCodewords <= symbol->dataCapacity())
       return symbol;
   }
-  e = BCExceptionIllegalDataCodewords;
   return nullptr;
 }
 
@@ -177,18 +174,18 @@
   return getSymbolDataHeight() + (getVerticalDataRegions() * 2);
 }
 
-int32_t CBC_SymbolInfo::getCodewordCount() const {
+size_t CBC_SymbolInfo::getCodewordCount() const {
   return m_dataCapacity + m_errorCodewords;
 }
 
-int32_t CBC_SymbolInfo::getInterleavedBlockCount() const {
+size_t CBC_SymbolInfo::getInterleavedBlockCount() const {
   return m_dataCapacity / m_rsBlockData;
 }
 
-int32_t CBC_SymbolInfo::getDataLengthForInterleavedBlock(int32_t index) const {
+size_t CBC_SymbolInfo::getDataLengthForInterleavedBlock() const {
   return m_rsBlockData;
 }
 
-int32_t CBC_SymbolInfo::getErrorLengthForInterleavedBlock(int32_t index) const {
+size_t CBC_SymbolInfo::getErrorLengthForInterleavedBlock() const {
   return m_rsBlockError;
 }
diff --git a/fxbarcode/datamatrix/BC_SymbolInfo.h b/fxbarcode/datamatrix/BC_SymbolInfo.h
index fe3e24c..07e563d 100644
--- a/fxbarcode/datamatrix/BC_SymbolInfo.h
+++ b/fxbarcode/datamatrix/BC_SymbolInfo.h
@@ -12,8 +12,8 @@
 
 class CBC_SymbolInfo {
  public:
-  CBC_SymbolInfo(int32_t dataCapacity,
-                 int32_t errorCodewords,
+  CBC_SymbolInfo(size_t dataCapacity,
+                 size_t errorCodewords,
                  int32_t matrixWidth,
                  int32_t matrixHeight,
                  int32_t dataRegions);
@@ -22,45 +22,44 @@
   static void Initialize();
   static void Finalize();
   static void overrideSymbolSet(CBC_SymbolInfo* override);
-  static CBC_SymbolInfo* lookup(int32_t dataCodewords,
-                                bool allowRectangular,
-                                int32_t& e);
+  static const CBC_SymbolInfo* Lookup(size_t iDataCodewords,
+                                      bool bAllowRectangular);
 
   int32_t getSymbolDataWidth() const;
   int32_t getSymbolDataHeight() const;
   int32_t getSymbolWidth() const;
   int32_t getSymbolHeight() const;
-  int32_t getCodewordCount() const;
-  virtual int32_t getInterleavedBlockCount() const;
-  int32_t getDataLengthForInterleavedBlock(int32_t index) const;
-  int32_t getErrorLengthForInterleavedBlock(int32_t index) const;
+  size_t getCodewordCount() const;
+  virtual size_t getInterleavedBlockCount() const;
+  size_t getDataLengthForInterleavedBlock() const;
+  size_t getErrorLengthForInterleavedBlock() const;
 
-  int32_t dataCapacity() const { return m_dataCapacity; }
-  int32_t errorCodewords() const { return m_errorCodewords; }
+  size_t dataCapacity() const { return m_dataCapacity; }
+  size_t errorCodewords() const { return m_errorCodewords; }
   int32_t matrixWidth() const { return m_matrixWidth; }
   int32_t matrixHeight() const { return m_matrixHeight; }
 
  protected:
-  CBC_SymbolInfo(int32_t dataCapacity,
-                 int32_t errorCodewords,
+  CBC_SymbolInfo(size_t dataCapacity,
+                 size_t errorCodewords,
                  int32_t matrixWidth,
                  int32_t matrixHeight,
                  int32_t dataRegions,
-                 int32_t rsBlockData,
-                 int32_t rsBlockError);
+                 size_t rsBlockData,
+                 size_t rsBlockError);
 
  private:
   int32_t getHorizontalDataRegions() const;
   int32_t getVerticalDataRegions() const;
 
   const bool m_rectangular;
-  const int32_t m_dataCapacity;
-  const int32_t m_errorCodewords;
+  const size_t m_dataCapacity;
+  const size_t m_errorCodewords;
   const int32_t m_matrixWidth;
   const int32_t m_matrixHeight;
   const int32_t m_dataRegions;
-  const int32_t m_rsBlockData;
-  const int32_t m_rsBlockError;
+  const size_t m_rsBlockData;
+  const size_t m_rsBlockError;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_SYMBOLINFO_H_
diff --git a/fxbarcode/datamatrix/BC_TextEncoder.cpp b/fxbarcode/datamatrix/BC_TextEncoder.cpp
index a78310e..bf87afa 100644
--- a/fxbarcode/datamatrix/BC_TextEncoder.cpp
+++ b/fxbarcode/datamatrix/BC_TextEncoder.cpp
@@ -1,3 +1,5 @@
+// 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
@@ -20,76 +22,75 @@
 
 #include "fxbarcode/datamatrix/BC_TextEncoder.h"
 
+#include "core/fxcrt/fx_extension.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_C40Encoder.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
 
-CBC_TextEncoder::CBC_TextEncoder() {}
-CBC_TextEncoder::~CBC_TextEncoder() {}
-int32_t CBC_TextEncoder::getEncodingMode() {
-  return TEXT_ENCODATION;
+CBC_TextEncoder::CBC_TextEncoder() = default;
+
+CBC_TextEncoder::~CBC_TextEncoder() = default;
+
+CBC_HighLevelEncoder::Encoding CBC_TextEncoder::GetEncodingMode() {
+  return CBC_HighLevelEncoder::Encoding::TEXT;
 }
-int32_t CBC_TextEncoder::encodeChar(wchar_t c, WideString& sb, int32_t& e) {
+
+int32_t CBC_TextEncoder::EncodeChar(wchar_t c, WideString* sb) {
   if (c == ' ') {
-    sb += (wchar_t)'\3';
+    *sb += (wchar_t)'\3';
     return 1;
   }
-  if (c >= '0' && c <= '9') {
-    sb += (wchar_t)(c - 48 + 4);
+  if (FXSYS_IsDecimalDigit(c)) {
+    *sb += (wchar_t)(c - 48 + 4);
     return 1;
   }
   if (c >= 'a' && c <= 'z') {
-    sb += (wchar_t)(c - 97 + 14);
+    *sb += (wchar_t)(c - 97 + 14);
     return 1;
   }
   if (c <= 0x1f) {
-    sb += (wchar_t)'\0';
-    sb += c;
+    *sb += (wchar_t)'\0';
+    *sb += c;
     return 2;
   }
   if (c >= '!' && c <= '/') {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)(c - 33);
+    *sb += (wchar_t)'\1';
+    *sb += (wchar_t)(c - 33);
     return 2;
   }
   if (c >= ':' && c <= '@') {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)(c - 58 + 15);
+    *sb += (wchar_t)'\1';
+    *sb += (wchar_t)(c - 58 + 15);
     return 2;
   }
   if (c >= '[' && c <= '_') {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)(c - 91 + 22);
+    *sb += (wchar_t)'\1';
+    *sb += (wchar_t)(c - 91 + 22);
     return 2;
   }
   if (c == 0x0060) {
-    sb += (wchar_t)'\2';
-    sb += (wchar_t)(c - 96);
+    *sb += (wchar_t)'\2';
+    *sb += (wchar_t)(c - 96);
     return 2;
   }
   if (c >= 'A' && c <= 'Z') {
-    sb += (wchar_t)'\2';
-    sb += (wchar_t)(c - 65 + 1);
+    *sb += (wchar_t)'\2';
+    *sb += (wchar_t)(c - 65 + 1);
     return 2;
   }
   if (c >= '{' && c <= 0x007f) {
-    sb += (wchar_t)'\2';
-    sb += (wchar_t)(c - 123 + 27);
+    *sb += (wchar_t)'\2';
+    *sb += (wchar_t)(c - 123 + 27);
     return 2;
   }
-  if (c >= 0x0080) {
-    sb += (wchar_t)'\1';
-    sb += (wchar_t)0x001e;
-    int32_t len = 2;
-    len += encodeChar((wchar_t)(c - 128), sb, e);
-    if (e != BCExceptionNO)
-      return -1;
-    return len;
-  }
-  e = BCExceptionIllegalArgument;
-  return -1;
+  if (c < 0x0080)
+    return 0;
+
+  *sb += (wchar_t)'\1';
+  *sb += (wchar_t)0x001e;
+  int32_t encode_result = EncodeChar(c - 128, sb);
+  return encode_result > 0 ? encode_result + 2 : 0;
 }
diff --git a/fxbarcode/datamatrix/BC_TextEncoder.h b/fxbarcode/datamatrix/BC_TextEncoder.h
index 9d4cdaf..40914b3 100644
--- a/fxbarcode/datamatrix/BC_TextEncoder.h
+++ b/fxbarcode/datamatrix/BC_TextEncoder.h
@@ -9,14 +9,14 @@
 
 #include "fxbarcode/datamatrix/BC_C40Encoder.h"
 
-class CBC_TextEncoder : public CBC_C40Encoder {
+class CBC_TextEncoder final : public CBC_C40Encoder {
  public:
   CBC_TextEncoder();
   ~CBC_TextEncoder() override;
 
   // CBC_C40Encoder
-  int32_t getEncodingMode() override;
-  int32_t encodeChar(wchar_t c, WideString& sb, int32_t& e) override;
+  CBC_HighLevelEncoder::Encoding GetEncodingMode() override;
+  int32_t EncodeChar(wchar_t c, WideString* sb) override;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_TEXTENCODER_H_
diff --git a/fxbarcode/datamatrix/BC_X12Encoder.cpp b/fxbarcode/datamatrix/BC_X12Encoder.cpp
index a080a71..37180ea 100644
--- a/fxbarcode/datamatrix/BC_X12Encoder.cpp
+++ b/fxbarcode/datamatrix/BC_X12Encoder.cpp
@@ -22,79 +22,81 @@
 
 #include "fxbarcode/datamatrix/BC_X12Encoder.h"
 
+#include "core/fxcrt/fx_extension.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/datamatrix/BC_C40Encoder.h"
 #include "fxbarcode/datamatrix/BC_Encoder.h"
 #include "fxbarcode/datamatrix/BC_EncoderContext.h"
 #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h"
 #include "fxbarcode/datamatrix/BC_SymbolInfo.h"
-#include "fxbarcode/utils.h"
 
-CBC_X12Encoder::CBC_X12Encoder() {}
-CBC_X12Encoder::~CBC_X12Encoder() {}
-int32_t CBC_X12Encoder::getEncodingMode() {
-  return X12_ENCODATION;
+CBC_X12Encoder::CBC_X12Encoder() = default;
+
+CBC_X12Encoder::~CBC_X12Encoder() = default;
+
+CBC_HighLevelEncoder::Encoding CBC_X12Encoder::GetEncodingMode() {
+  return CBC_HighLevelEncoder::Encoding::X12;
 }
-void CBC_X12Encoder::Encode(CBC_EncoderContext& context, int32_t& e) {
+
+bool CBC_X12Encoder::Encode(CBC_EncoderContext* context) {
   WideString buffer;
-  while (context.hasMoreCharacters()) {
-    wchar_t c = context.getCurrentChar();
-    context.m_pos++;
-    encodeChar(c, buffer, e);
-    if (e != BCExceptionNO) {
-      return;
-    }
-    int32_t count = buffer.GetLength();
+  while (context->hasMoreCharacters()) {
+    wchar_t c = context->getCurrentChar();
+    context->m_pos++;
+    if (EncodeChar(c, &buffer) <= 0)
+      return false;
+
+    size_t count = buffer.GetLength();
     if ((count % 3) == 0) {
-      writeNextTriplet(context, buffer);
-      int32_t newMode = CBC_HighLevelEncoder::lookAheadTest(
-          context.m_msg, context.m_pos, getEncodingMode());
-      if (newMode != getEncodingMode()) {
-        context.signalEncoderChange(newMode);
+      WriteNextTriplet(context, &buffer);
+      CBC_HighLevelEncoder::Encoding newMode =
+          CBC_HighLevelEncoder::LookAheadTest(context->m_msg, context->m_pos,
+                                              GetEncodingMode());
+      if (newMode != GetEncodingMode()) {
+        context->SignalEncoderChange(newMode);
         break;
       }
     }
   }
-  handleEOD(context, buffer, e);
+  return HandleEOD(context, &buffer);
 }
-void CBC_X12Encoder::handleEOD(CBC_EncoderContext& context,
-                               WideString& buffer,
-                               int32_t& e) {
-  context.updateSymbolInfo(e);
-  if (e != BCExceptionNO) {
-    return;
-  }
+
+bool CBC_X12Encoder::HandleEOD(CBC_EncoderContext* context,
+                               WideString* buffer) {
+  if (!context->UpdateSymbolInfo())
+    return false;
+
   int32_t available =
-      context.m_symbolInfo->dataCapacity() - context.getCodewordCount();
-  int32_t count = buffer.GetLength();
+      context->m_symbolInfo->dataCapacity() - context->getCodewordCount();
+  size_t count = buffer->GetLength();
   if (count == 2) {
-    context.writeCodeword(CBC_HighLevelEncoder::X12_UNLATCH);
-    context.m_pos -= 2;
-    context.signalEncoderChange(ASCII_ENCODATION);
+    context->writeCodeword(CBC_HighLevelEncoder::X12_UNLATCH);
+    context->m_pos -= 2;
+    context->SignalEncoderChange(CBC_HighLevelEncoder::Encoding::ASCII);
   } else if (count == 1) {
-    context.m_pos--;
+    context->m_pos--;
     if (available > 1) {
-      context.writeCodeword(CBC_HighLevelEncoder::X12_UNLATCH);
+      context->writeCodeword(CBC_HighLevelEncoder::X12_UNLATCH);
     }
-    context.signalEncoderChange(ASCII_ENCODATION);
+    context->SignalEncoderChange(CBC_HighLevelEncoder::Encoding::ASCII);
   }
+  return true;
 }
-int32_t CBC_X12Encoder::encodeChar(wchar_t c, WideString& sb, int32_t& e) {
-  if (c == '\r') {
-    sb += (wchar_t)'\0';
-  } else if (c == '*') {
-    sb += (wchar_t)'\1';
-  } else if (c == '>') {
-    sb += (wchar_t)'\2';
-  } else if (c == ' ') {
-    sb += (wchar_t)'\3';
-  } else if (c >= '0' && c <= '9') {
-    sb += (wchar_t)(c - 48 + 4);
-  } else if (c >= 'A' && c <= 'Z') {
-    sb += (wchar_t)(c - 65 + 14);
-  } else {
-    e = BCExceptionIllegalArgument;
-    return -1;
-  }
+
+int32_t CBC_X12Encoder::EncodeChar(wchar_t c, WideString* sb) {
+  if (c == '\r')
+    *sb += (wchar_t)'\0';
+  else if (c == '*')
+    *sb += (wchar_t)'\1';
+  else if (c == '>')
+    *sb += (wchar_t)'\2';
+  else if (c == ' ')
+    *sb += (wchar_t)'\3';
+  else if (FXSYS_IsDecimalDigit(c))
+    *sb += (wchar_t)(c - 48 + 4);
+  else if (c >= 'A' && c <= 'Z')
+    *sb += (wchar_t)(c - 65 + 14);
+  else
+    return 0;
   return 1;
 }
diff --git a/fxbarcode/datamatrix/BC_X12Encoder.h b/fxbarcode/datamatrix/BC_X12Encoder.h
index 324b78b..aa258a9 100644
--- a/fxbarcode/datamatrix/BC_X12Encoder.h
+++ b/fxbarcode/datamatrix/BC_X12Encoder.h
@@ -9,18 +9,16 @@
 
 #include "fxbarcode/datamatrix/BC_C40Encoder.h"
 
-class CBC_X12Encoder : public CBC_C40Encoder {
+class CBC_X12Encoder final : public CBC_C40Encoder {
  public:
   CBC_X12Encoder();
   ~CBC_X12Encoder() override;
 
   // CBC_C40Encoder
-  int32_t getEncodingMode() override;
-  void Encode(CBC_EncoderContext& context, int32_t& e) override;
-  void handleEOD(CBC_EncoderContext& context,
-                 WideString& buffer,
-                 int32_t& e) override;
-  int32_t encodeChar(wchar_t c, WideString& sb, int32_t& e) override;
+  CBC_HighLevelEncoder::Encoding GetEncodingMode() override;
+  bool Encode(CBC_EncoderContext* context) override;
+  bool HandleEOD(CBC_EncoderContext* context, WideString* buffer) override;
+  int32_t EncodeChar(wchar_t c, WideString* sb) override;
 };
 
 #endif  // FXBARCODE_DATAMATRIX_BC_X12ENCODER_H_
diff --git a/fxbarcode/oned/BC_OneDimWriter.cpp b/fxbarcode/oned/BC_OneDimWriter.cpp
index 0fa23bb..5a02cdf 100644
--- a/fxbarcode/oned/BC_OneDimWriter.cpp
+++ b/fxbarcode/oned/BC_OneDimWriter.cpp
@@ -26,30 +26,19 @@
 #include <memory>
 #include <vector>
 
+#include "build/build_config.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/cfx_unicodeencodingex.h"
+#include "core/fxge/text_char_pos.h"
 #include "fxbarcode/BC_Writer.h"
-#include "third_party/base/ptr_util.h"
 
-CBC_OneDimWriter::CBC_OneDimWriter() {
-  m_locTextLoc = BC_TEXT_LOC_BELOWEMBED;
-  m_bPrintChecksum = true;
-  m_iDataLenth = 0;
-  m_bCalcChecksum = false;
-  m_pFont = nullptr;
-  m_fFontSize = 10;
-  m_iFontStyle = 0;
-  m_fontColor = 0xff000000;
-  m_iContentLen = 0;
-  m_bLeftPadding = false;
-  m_bRightPadding = false;
-}
+CBC_OneDimWriter::CBC_OneDimWriter() = default;
 
-CBC_OneDimWriter::~CBC_OneDimWriter() {}
+CBC_OneDimWriter::~CBC_OneDimWriter() = default;
 
 void CBC_OneDimWriter::SetPrintChecksum(bool checksum) {
   m_bPrintChecksum = checksum;
@@ -83,13 +72,6 @@
   m_fontColor = color;
 }
 
-wchar_t CBC_OneDimWriter::Upper(wchar_t ch) {
-  if (ch >= 'a' && ch <= 'z') {
-    ch = ch - ('a' - 'A');
-  }
-  return ch;
-}
-
 uint8_t* CBC_OneDimWriter::EncodeWithHint(const ByteString& contents,
                                           BCFORMAT format,
                                           int32_t& outWidth,
@@ -110,41 +92,35 @@
                                         int32_t pos,
                                         const int8_t* pattern,
                                         int32_t patternLength,
-                                        int32_t startColor,
-                                        int32_t& e) {
-  if (startColor != 0 && startColor != 1) {
-    e = BCExceptionValueMustBeEither0or1;
-    return 0;
-  }
-  uint8_t color = (uint8_t)startColor;
+                                        bool startColor) {
+  bool color = startColor;
   int32_t numAdded = 0;
   for (int32_t i = 0; i < patternLength; i++) {
-    for (int32_t j = 0; j < pattern[i]; j++) {
-      target[pos++] = color;
-      numAdded += 1;
-    }
-    color ^= 1;
+    for (int32_t j = 0; j < pattern[i]; j++)
+      target[pos++] = color ? 1 : 0;
+    numAdded += pattern[i];
+    color = !color;
   }
   return numAdded;
 }
 
 void CBC_OneDimWriter::CalcTextInfo(const ByteString& text,
-                                    FXTEXT_CHARPOS* charPos,
+                                    TextCharPos* charPos,
                                     CFX_Font* cFont,
                                     float geWidth,
                                     int32_t fontSize,
                                     float& charsLen) {
   std::unique_ptr<CFX_UnicodeEncodingEx> encoding =
-      FX_CreateFontEncodingEx(cFont, FXFM_ENCODING_NONE);
+      FX_CreateFontEncodingEx(cFont);
 
-  size_t length = text.GetLength();
-  uint32_t* pCharCode = FX_Alloc(uint32_t, text.GetLength());
+  const size_t length = text.GetLength();
+  std::vector<uint32_t> charcodes(length);
   float charWidth = 0;
-  for (size_t j = 0; j < length; j++) {
-    pCharCode[j] = encoding->CharCodeFromUnicode(text[j]);
-    int32_t glyp_code = encoding->GlyphFromCharCode(pCharCode[j]);
-    int32_t glyp_value = cFont->GetGlyphWidth(glyp_code);
-    float temp = (float)((glyp_value)*fontSize / 1000.0);
+  for (size_t i = 0; i < length; ++i) {
+    charcodes[i] = encoding->CharCodeFromUnicode(text[i]);
+    int32_t glyph_code = encoding->GlyphFromCharCode(charcodes[i]);
+    uint32_t glyph_value = cFont->GetGlyphWidth(glyph_code);
+    float temp = glyph_value * fontSize / 1000.0;
     charWidth += temp;
   }
   charsLen = charWidth;
@@ -157,29 +133,28 @@
   float left = leftPositon;
   float top = 0.0;
   charPos[0].m_Origin = CFX_PointF(penX + left, penY + top);
-  charPos[0].m_GlyphIndex = encoding->GlyphFromCharCode(pCharCode[0]);
+  charPos[0].m_GlyphIndex = encoding->GlyphFromCharCode(charcodes[0]);
   charPos[0].m_FontCharWidth = cFont->GetGlyphWidth(charPos[0].m_GlyphIndex);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
   charPos[0].m_ExtGID = charPos[0].m_GlyphIndex;
 #endif
   penX += (float)(charPos[0].m_FontCharWidth) * (float)fontSize / 1000.0f;
   for (size_t i = 1; i < length; i++) {
     charPos[i].m_Origin = CFX_PointF(penX + left, penY + top);
-    charPos[i].m_GlyphIndex = encoding->GlyphFromCharCode(pCharCode[i]);
+    charPos[i].m_GlyphIndex = encoding->GlyphFromCharCode(charcodes[i]);
     charPos[i].m_FontCharWidth = cFont->GetGlyphWidth(charPos[i].m_GlyphIndex);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
     charPos[i].m_ExtGID = charPos[i].m_GlyphIndex;
 #endif
     penX += (float)(charPos[i].m_FontCharWidth) * (float)fontSize / 1000.0f;
   }
-  FX_Free(pCharCode);
 }
 
 void CBC_OneDimWriter::ShowDeviceChars(CFX_RenderDevice* device,
                                        const CFX_Matrix* matrix,
                                        const ByteString str,
                                        float geWidth,
-                                       FXTEXT_CHARPOS* pCharPos,
+                                       TextCharPos* pCharPos,
                                        float locX,
                                        float locY,
                                        int32_t barWidth) {
@@ -191,18 +166,18 @@
     rect.right -= 1;
   }
   FX_RECT re = matrix->TransformRect(rect).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   CFX_Matrix affine_matrix(1.0, 0.0, 0.0, -1.0, (float)locX,
                            (float)(locY + iFontSize));
   if (matrix) {
     affine_matrix.Concat(*matrix);
   }
   device->DrawNormalText(str.GetLength(), pCharPos, m_pFont.Get(),
-                         static_cast<float>(iFontSize), &affine_matrix,
+                         static_cast<float>(iFontSize), affine_matrix,
                          m_fontColor, FXTEXT_CLEARTYPE);
 }
 
-bool CBC_OneDimWriter::ShowChars(const WideStringView& contents,
+bool CBC_OneDimWriter::ShowChars(WideStringView contents,
                                  CFX_RenderDevice* device,
                                  const CFX_Matrix* matrix,
                                  int32_t barWidth,
@@ -211,8 +186,7 @@
     return false;
 
   ByteString str = FX_UTF8Encode(contents);
-  int32_t iLen = str.GetLength();
-  std::vector<FXTEXT_CHARPOS> charpos(iLen);
+  std::vector<TextCharPos> charpos(str.GetLength());
   float charsLen = 0;
   float geWidth = 0;
   if (m_locTextLoc == BC_TEXT_LOC_ABOVEEMBED ||
@@ -261,7 +235,7 @@
 
 bool CBC_OneDimWriter::RenderDeviceResult(CFX_RenderDevice* device,
                                           const CFX_Matrix* matrix,
-                                          const WideStringView& contents) {
+                                          WideStringView contents) {
   if (m_output.empty())
     return false;
 
@@ -269,22 +243,21 @@
   CFX_PathData path;
   path.AppendRect(0, 0, static_cast<float>(m_Width),
                   static_cast<float>(m_Height));
-  device->DrawPath(&path, matrix, &stateData, m_backgroundColor,
-                   m_backgroundColor, FXFILL_ALTERNATE);
+  device->DrawPath(&path, matrix, &stateData, kBackgroundColor,
+                   kBackgroundColor, FXFILL_ALTERNATE);
   CFX_Matrix scaledMatrix(m_outputHScale, 0.0, 0.0,
                           static_cast<float>(m_Height), 0.0, 0.0);
   scaledMatrix.Concat(*matrix);
-  for (auto& rect : m_output) {
+  for (const auto& rect : m_output) {
     CFX_GraphStateData data;
-    device->DrawPath(&rect, &scaledMatrix, &data, m_barColor, 0,
-                     FXFILL_WINDING);
+    device->DrawPath(&rect, &scaledMatrix, &data, kBarColor, 0, FXFILL_WINDING);
   }
 
   return m_locTextLoc == BC_TEXT_LOC_NONE || !contents.Contains(' ') ||
          ShowChars(contents, device, matrix, m_barWidth, m_multiple);
 }
 
-bool CBC_OneDimWriter::RenderResult(const WideStringView& contents,
+bool CBC_OneDimWriter::RenderResult(WideStringView contents,
                                     uint8_t* code,
                                     int32_t codeLength) {
   if (codeLength < 1)
@@ -300,11 +273,11 @@
       m_Width > 0 ? static_cast<float>(m_Width) / static_cast<float>(codeLength)
                   : 1.0;
   m_multiple = 1;
-  const int32_t outputHeight = 1;
   const int32_t outputWidth = codeLength;
   m_barWidth = m_Width;
 
   m_output.clear();
+  m_output.reserve(codeOldLength * m_multiple);
   for (int32_t inputX = 0, outputX = leftPadding * m_multiple;
        inputX < codeOldLength; ++inputX, outputX += m_multiple) {
     if (code[inputX] != 1)
@@ -314,27 +287,19 @@
       return true;
 
     if (outputX + m_multiple > outputWidth && outputWidth - outputX > 0) {
-      RenderVerticalBars(outputX, outputWidth - outputX, outputHeight);
+      RenderVerticalBars(outputX, outputWidth - outputX);
       return true;
     }
 
-    RenderVerticalBars(outputX, m_multiple, outputHeight);
+    RenderVerticalBars(outputX, m_multiple);
   }
   return true;
 }
 
-void CBC_OneDimWriter::RenderVerticalBars(int32_t outputX,
-                                          int32_t width,
-                                          int32_t height) {
+void CBC_OneDimWriter::RenderVerticalBars(int32_t outputX, int32_t width) {
   for (int i = 0; i < width; ++i) {
     float x = outputX + i;
-    CFX_PathData rect;
-    rect.AppendRect(x, 0.0f, x + 1, static_cast<float>(height));
-    m_output.push_back(rect);
+    m_output.emplace_back();
+    m_output.back().AppendRect(x, 0.0f, x + 1, 1.0f);
   }
 }
-
-WideString CBC_OneDimWriter::RenderTextContents(
-    const WideStringView& contents) {
-  return WideString();
-}
diff --git a/fxbarcode/oned/BC_OneDimWriter.h b/fxbarcode/oned/BC_OneDimWriter.h
index a597d86..3e0ccc9 100644
--- a/fxbarcode/oned/BC_OneDimWriter.h
+++ b/fxbarcode/oned/BC_OneDimWriter.h
@@ -7,29 +7,29 @@
 #ifndef FXBARCODE_ONED_BC_ONEDIMWRITER_H_
 #define FXBARCODE_ONED_BC_ONEDIMWRITER_H_
 
-#include <memory>
 #include <vector>
 
+#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "core/fxge/cfx_renderdevice.h"
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/BC_Writer.h"
+#include "fxbarcode/utils.h"
 
 class CFX_Font;
 class CFX_PathData;
 class CFX_RenderDevice;
+class TextCharPos;
 
 class CBC_OneDimWriter : public CBC_Writer {
  public:
   CBC_OneDimWriter();
   ~CBC_OneDimWriter() override;
 
-  virtual bool RenderResult(const WideStringView& contents,
+  virtual bool RenderResult(WideStringView contents,
                             uint8_t* code,
                             int32_t codeLength);
-  virtual bool CheckContentValidity(const WideStringView& contents) = 0;
-  virtual WideString FilterContents(const WideStringView& contents) = 0;
-  virtual WideString RenderTextContents(const WideStringView& contents);
+  virtual bool CheckContentValidity(WideStringView contents) = 0;
+  virtual WideString FilterContents(WideStringView contents) = 0;
   virtual void SetPrintChecksum(bool checksum);
   virtual void SetDataLength(int32_t length);
   virtual void SetCalcChecksum(bool state);
@@ -43,7 +43,7 @@
                   int32_t& outHeight);
   bool RenderDeviceResult(CFX_RenderDevice* device,
                           const CFX_Matrix* matrix,
-                          const WideStringView& contents);
+                          WideStringView contents);
   bool SetFont(CFX_Font* cFont);
 
  protected:
@@ -54,46 +54,47 @@
                                   int32_t hints);
   virtual uint8_t* EncodeImpl(const ByteString& contents,
                               int32_t& outLength) = 0;
-  virtual void CalcTextInfo(const ByteString& text,
-                            FXTEXT_CHARPOS* charPos,
-                            CFX_Font* cFont,
-                            float geWidth,
-                            int32_t fontSize,
-                            float& charsLen);
-  virtual bool ShowChars(const WideStringView& contents,
+  virtual bool ShowChars(WideStringView contents,
                          CFX_RenderDevice* device,
                          const CFX_Matrix* matrix,
                          int32_t barWidth,
                          int32_t multiple);
-  virtual void ShowDeviceChars(CFX_RenderDevice* device,
-                               const CFX_Matrix* matrix,
-                               const ByteString str,
-                               float geWidth,
-                               FXTEXT_CHARPOS* pCharPos,
-                               float locX,
-                               float locY,
-                               int32_t barWidth);
-  virtual int32_t AppendPattern(uint8_t* target,
-                                int32_t pos,
-                                const int8_t* pattern,
-                                int32_t patternLength,
-                                int32_t startColor,
-                                int32_t& e);
+  void ShowDeviceChars(CFX_RenderDevice* device,
+                       const CFX_Matrix* matrix,
+                       const ByteString str,
+                       float geWidth,
+                       TextCharPos* pCharPos,
+                       float locX,
+                       float locY,
+                       int32_t barWidth);
+  void CalcTextInfo(const ByteString& text,
+                    TextCharPos* charPos,
+                    CFX_Font* cFont,
+                    float geWidth,
+                    int32_t fontSize,
+                    float& charsLen);
+  int32_t AppendPattern(uint8_t* target,
+                        int32_t pos,
+                        const int8_t* pattern,
+                        int32_t patternLength,
+                        bool startColor);
 
-  wchar_t Upper(wchar_t ch);
-  void RenderVerticalBars(int32_t outputX, int32_t width, int32_t height);
+  void RenderVerticalBars(int32_t outputX, int32_t width);
 
-  bool m_bPrintChecksum;
-  int32_t m_iDataLenth;
-  bool m_bCalcChecksum;
+  bool m_bPrintChecksum = true;
+  bool m_bCalcChecksum = false;
+  bool m_bLeftPadding = false;
+  bool m_bRightPadding = false;
+
   UnownedPtr<CFX_Font> m_pFont;
-  float m_fFontSize;
-  int32_t m_iFontStyle;
-  uint32_t m_fontColor;
-  BC_TEXT_LOC m_locTextLoc;
-  size_t m_iContentLen;
-  bool m_bLeftPadding;
-  bool m_bRightPadding;
+  float m_fFontSize = 10.0f;
+  int32_t m_iFontStyle = 0;
+  uint32_t m_fontColor = 0xff000000;
+  BC_TEXT_LOC m_locTextLoc = BC_TEXT_LOC_BELOWEMBED;
+
+  int32_t m_iDataLenth = 0;
+  size_t m_iContentLen = 0;
+
   std::vector<CFX_PathData> m_output;
   int32_t m_barWidth;
   int32_t m_multiple;
diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter.cpp b/fxbarcode/oned/BC_OnedCodaBarWriter.cpp
index 42990f7..c4334c6 100644
--- a/fxbarcode/oned/BC_OnedCodaBarWriter.cpp
+++ b/fxbarcode/oned/BC_OnedCodaBarWriter.cpp
@@ -23,7 +23,6 @@
 #include "fxbarcode/oned/BC_OnedCodaBarWriter.h"
 
 #include "fxbarcode/BC_Writer.h"
-#include "fxbarcode/common/BC_CommonBitArray.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 #include "third_party/base/stl_util.h"
@@ -49,10 +48,9 @@
 
 }  // namespace
 
-CBC_OnedCodaBarWriter::CBC_OnedCodaBarWriter()
-    : m_chStart('A'), m_chEnd('B'), m_iWideNarrRatio(2) {}
+CBC_OnedCodaBarWriter::CBC_OnedCodaBarWriter() = default;
 
-CBC_OnedCodaBarWriter::~CBC_OnedCodaBarWriter() {}
+CBC_OnedCodaBarWriter::~CBC_OnedCodaBarWriter() = default;
 
 bool CBC_OnedCodaBarWriter::SetStartChar(char start) {
   if (!pdfium::ContainsValue(kStartEndChars, start))
@@ -99,16 +97,15 @@
          (isContent && pdfium::ContainsValue(kStartEndChars, narrow_ch));
 }
 
-bool CBC_OnedCodaBarWriter::CheckContentValidity(
-    const WideStringView& contents) {
+bool CBC_OnedCodaBarWriter::CheckContentValidity(WideStringView contents) {
   return std::all_of(
       contents.begin(), contents.end(),
       [this](const wchar_t& ch) { return this->FindChar(ch, false); });
 }
 
-WideString CBC_OnedCodaBarWriter::FilterContents(
-    const WideStringView& contents) {
+WideString CBC_OnedCodaBarWriter::FilterContents(WideStringView contents) {
   WideString filtercontents;
+  filtercontents.Reserve(contents.GetLength());
   wchar_t ch;
   for (size_t index = 0; index < contents.GetLength(); index++) {
     ch = contents[index];
@@ -192,14 +189,13 @@
   return result;
 }
 
-WideString CBC_OnedCodaBarWriter::encodedContents(
-    const WideStringView& contents) {
+WideString CBC_OnedCodaBarWriter::encodedContents(WideStringView contents) {
   WideString strStart(static_cast<wchar_t>(m_chStart));
   WideString strEnd(static_cast<wchar_t>(m_chEnd));
   return strStart + contents + strEnd;
 }
 
-bool CBC_OnedCodaBarWriter::RenderResult(const WideStringView& contents,
+bool CBC_OnedCodaBarWriter::RenderResult(WideStringView contents,
                                          uint8_t* code,
                                          int32_t codeLength) {
   return CBC_OneDimWriter::RenderResult(
diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter.h b/fxbarcode/oned/BC_OnedCodaBarWriter.h
index ab354ef..6feb380 100644
--- a/fxbarcode/oned/BC_OnedCodaBarWriter.h
+++ b/fxbarcode/oned/BC_OnedCodaBarWriter.h
@@ -12,7 +12,7 @@
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
-class CBC_OnedCodaBarWriter : public CBC_OneDimWriter {
+class CBC_OnedCodaBarWriter final : public CBC_OneDimWriter {
  public:
   CBC_OnedCodaBarWriter();
   ~CBC_OnedCodaBarWriter() override;
@@ -24,25 +24,25 @@
                           int32_t& outWidth,
                           int32_t& outHeight,
                           int32_t hints) override;
-  bool RenderResult(const WideStringView& contents,
+  bool RenderResult(WideStringView contents,
                     uint8_t* code,
                     int32_t codeLength) override;
-  bool CheckContentValidity(const WideStringView& contents) override;
-  WideString FilterContents(const WideStringView& contents) override;
+  bool CheckContentValidity(WideStringView contents) override;
+  WideString FilterContents(WideStringView contents) override;
   void SetDataLength(int32_t length) override;
+  bool SetTextLocation(BC_TEXT_LOC location) override;
+  bool SetWideNarrowRatio(int8_t ratio) override;
+  bool SetStartChar(char start) override;
+  bool SetEndChar(char end) override;
 
-  virtual bool SetStartChar(char start);
-  virtual bool SetEndChar(char end);
-  virtual bool SetTextLocation(BC_TEXT_LOC location);
-  virtual bool SetWideNarrowRatio(int8_t ratio);
   virtual bool FindChar(wchar_t ch, bool isContent);
 
-  WideString encodedContents(const WideStringView& contents);
+  WideString encodedContents(WideStringView contents);
 
  private:
-  char m_chStart;
-  char m_chEnd;
-  int8_t m_iWideNarrRatio;
+  char m_chStart = 'A';
+  char m_chEnd = 'B';
+  int8_t m_iWideNarrRatio = 2;
 };
 
 #endif  // FXBARCODE_ONED_BC_ONEDCODABARWRITER_H_
diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp b/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp
index f498449..89b0ac8 100644
--- a/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp
@@ -3,18 +3,19 @@
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedCodaBarWriter.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
 // 3 wide and 4 narrow modules per delimiter. One space between them.
-const int kModulesForDelimiters = (3 * 2 + 4 * 1) * 2 + 1;
+constexpr int kModulesForDelimiters = (3 * 2 + 4) * 2 + 1;
 
 // 2 wide and 5 narrow modules per number, '_' or '$'. 1 space between chars.
-const int kModulesPerNumber = 2 * 2 + 5 * 1 + 1;
+constexpr int kModulesPerNumber = 2 * 2 + 5 + 1;
 
 // 3 wide and 4 narrow modules per number, '_' or '$'. 1 space between chars.
-const int kModulesPerPunctuation = 3 * 2 + 4 * 1 + 1;
+constexpr int kModulesPerPunctuation = 3 * 2 + 4 + 1;
 
 TEST(OnedCodaBarWriterTest, Encode) {
   CBC_OnedCodaBarWriter writer;
diff --git a/fxbarcode/oned/BC_OnedCode128Writer.cpp b/fxbarcode/oned/BC_OnedCode128Writer.cpp
index 50d3fb6..b01abb5 100644
--- a/fxbarcode/oned/BC_OnedCode128Writer.cpp
+++ b/fxbarcode/oned/BC_OnedCode128Writer.cpp
@@ -25,6 +25,7 @@
 #include <cctype>
 #include <memory>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
@@ -78,13 +79,12 @@
 
 CBC_OnedCode128Writer::CBC_OnedCode128Writer(BC_TYPE type)
     : m_codeFormat(type) {
-  assert(m_codeFormat == BC_CODE128_B || m_codeFormat == BC_CODE128_C);
+  ASSERT(m_codeFormat == BC_CODE128_B || m_codeFormat == BC_CODE128_C);
 }
 
-CBC_OnedCode128Writer::~CBC_OnedCode128Writer() {}
+CBC_OnedCode128Writer::~CBC_OnedCode128Writer() = default;
 
-bool CBC_OnedCode128Writer::CheckContentValidity(
-    const WideStringView& contents) {
+bool CBC_OnedCode128Writer::CheckContentValidity(WideStringView contents) {
   for (const auto& ch : contents) {
     int32_t patternIndex = static_cast<int32_t>(ch);
     if (patternIndex < 32 || patternIndex > 126 || patternIndex == 34)
@@ -93,24 +93,21 @@
   return true;
 }
 
-WideString CBC_OnedCode128Writer::FilterContents(
-    const WideStringView& contents) {
-  WideString filterChineseChar;
+WideString CBC_OnedCode128Writer::FilterContents(WideStringView contents) {
+  const wchar_t limit = m_codeFormat == BC_CODE128_B ? 126 : 106;
+
+  WideString filtered;
+  filtered.Reserve(contents.GetLength());
   for (size_t i = 0; i < contents.GetLength(); i++) {
     wchar_t ch = contents[i];
     if (ch > 175) {
       i++;
       continue;
     }
-    filterChineseChar += ch;
-  }
-  const wchar_t limit = m_codeFormat == BC_CODE128_B ? 126 : 106;
-  WideString filtercontents;
-  for (const auto& ch : filterChineseChar) {
     if (ch >= 32 && ch <= limit)
-      filtercontents += ch;
+      filtered += ch;
   }
-  return filtercontents;
+  return filtered;
 }
 
 bool CBC_OnedCode128Writer::SetTextLocation(BC_TEXT_LOC location) {
@@ -159,10 +156,7 @@
   int32_t pos = 0;
   for (size_t i = 0; i < patterns.size(); ++i) {
     const int8_t* pattern = CODE_PATTERNS[patterns[i]];
-    int32_t e = BCExceptionNO;
-    pos += AppendPattern(result.get(), pos, pattern, kPatternSize, 1, e);
-    if (e != BCExceptionNO)
-      return nullptr;
+    pos += AppendPattern(result.get(), pos, pattern, kPatternSize, true);
   }
   return result.release();
 }
@@ -193,7 +187,7 @@
     char ch = contents[position];
     if (std::isdigit(ch)) {
       patternIndex = FXSYS_atoi(
-          contents.Mid(position, contents.IsValidIndex(position + 1) ? 2 : 1)
+          contents.Substr(position, contents.IsValidIndex(position + 1) ? 2 : 1)
               .c_str());
       ++position;
       if (position < contents.GetLength() && std::isdigit(contents[position]))
diff --git a/fxbarcode/oned/BC_OnedCode128Writer.h b/fxbarcode/oned/BC_OnedCode128Writer.h
index 3edb65a..370c025 100644
--- a/fxbarcode/oned/BC_OnedCode128Writer.h
+++ b/fxbarcode/oned/BC_OnedCode128Writer.h
@@ -13,7 +13,7 @@
 #include "core/fxcrt/fx_system.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
-class CBC_OnedCode128Writer : public CBC_OneDimWriter {
+class CBC_OnedCode128Writer final : public CBC_OneDimWriter {
  public:
   explicit CBC_OnedCode128Writer(BC_TYPE type);
   ~CBC_OnedCode128Writer() override;
@@ -31,10 +31,9 @@
                           int32_t& outHeight,
                           int32_t hints) override;
   uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
-  bool CheckContentValidity(const WideStringView& contents) override;
-  WideString FilterContents(const WideStringView& contents) override;
-
-  bool SetTextLocation(BC_TEXT_LOC location);
+  bool CheckContentValidity(WideStringView contents) override;
+  WideString FilterContents(WideStringView contents) override;
+  bool SetTextLocation(BC_TEXT_LOC location) override;
 
   BC_TYPE GetType() const { return m_codeFormat; }
 
diff --git a/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp b/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp
index fbff804..19d91f9 100644
--- a/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedCode128Writer.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -16,7 +17,7 @@
 
 TEST(OnedCode128WriterTest, Encode128B) {
   char buf[100];
-  TestCase kTestCases[] = {
+  static const TestCase kTestCases[] = {
       {"", 104, {104}, 1},
       {"a", 169, {104, 65}, 2},
       {"1", 121, {104, 17}, 2},
@@ -49,7 +50,7 @@
 
 TEST(OnedCode128WriterTest, Encode128C) {
   char buf[100];
-  TestCase kTestCases[] = {
+  static const TestCase kTestCases[] = {
       {"", 105, {105}, 1},
       {"a", 202, {105, 97}, 2},
       {"1", 106, {105, 1}, 2},
diff --git a/fxbarcode/oned/BC_OnedCode39Writer.cpp b/fxbarcode/oned/BC_OnedCode39Writer.cpp
index f04dc19..97ee881 100644
--- a/fxbarcode/oned/BC_OnedCode39Writer.cpp
+++ b/fxbarcode/oned/BC_OnedCode39Writer.cpp
@@ -22,8 +22,11 @@
 
 #include "fxbarcode/oned/BC_OnedCode39Writer.h"
 
+#include <algorithm>
 #include <memory>
 
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
@@ -50,29 +53,25 @@
     0x0085, 0x0184, 0x00C4, 0x0094, 0x00A8, 0x00A2, 0x008A, 0x002A};
 static_assert(FX_ArraySize(kOnedCode39CharacterEncoding) == 44, "Wrong size");
 
-}  // namespace
-
-CBC_OnedCode39Writer::CBC_OnedCode39Writer() : m_iWideNarrRatio(3) {}
-
-CBC_OnedCode39Writer::~CBC_OnedCode39Writer() {}
-
-bool CBC_OnedCode39Writer::CheckContentValidity(
-    const WideStringView& contents) {
-  for (size_t i = 0; i < contents.GetLength(); i++) {
-    wchar_t ch = contents[i];
-    if ((ch >= L'0' && ch <= L'9') || (ch >= L'A' && ch <= L'Z') ||
-        ch == L'-' || ch == L'.' || ch == L' ' || ch == L'*' || ch == L'$' ||
-        ch == L'/' || ch == L'+' || ch == L'%') {
-      continue;
-    }
-    return false;
-  }
-  return true;
+bool IsInOnedCode39Alphabet(wchar_t ch) {
+  return FXSYS_IsDecimalDigit(ch) || (ch >= L'A' && ch <= L'Z') || ch == L'-' ||
+         ch == L'.' || ch == L' ' || ch == L'*' || ch == L'$' || ch == L'/' ||
+         ch == L'+' || ch == L'%';
 }
 
-WideString CBC_OnedCode39Writer::FilterContents(
-    const WideStringView& contents) {
+}  // namespace
+
+CBC_OnedCode39Writer::CBC_OnedCode39Writer() = default;
+
+CBC_OnedCode39Writer::~CBC_OnedCode39Writer() = default;
+
+bool CBC_OnedCode39Writer::CheckContentValidity(WideStringView contents) {
+  return std::all_of(contents.begin(), contents.end(), IsInOnedCode39Alphabet);
+}
+
+WideString CBC_OnedCode39Writer::FilterContents(WideStringView contents) {
   WideString filtercontents;
+  filtercontents.Reserve(contents.GetLength());
   for (size_t i = 0; i < contents.GetLength(); i++) {
     wchar_t ch = contents[i];
     if (ch == L'*' && (i == 0 || i == contents.GetLength() - 1)) {
@@ -82,18 +81,14 @@
       i++;
       continue;
     }
-    ch = Upper(ch);
-    if ((ch >= L'0' && ch <= L'9') || (ch >= L'A' && ch <= L'Z') ||
-        ch == L'-' || ch == L'.' || ch == L' ' || ch == L'*' || ch == L'$' ||
-        ch == L'/' || ch == L'+' || ch == L'%') {
+    ch = FXSYS_ToUpperASCII(ch);
+    if (IsInOnedCode39Alphabet(ch))
       filtercontents += ch;
-    }
   }
   return filtercontents;
 }
 
-WideString CBC_OnedCode39Writer::RenderTextContents(
-    const WideStringView& contents) {
+WideString CBC_OnedCode39Writer::RenderTextContents(WideStringView contents) {
   WideString renderContents;
   for (size_t i = 0; i < contents.GetLength(); i++) {
     wchar_t ch = contents[i];
@@ -104,11 +99,8 @@
       i++;
       continue;
     }
-    if ((ch >= L'0' && ch <= L'9') || (ch >= L'A' && ch <= L'Z') ||
-        (ch >= L'a' && ch <= L'z') || ch == L'-' || ch == L'.' || ch == L' ' ||
-        ch == L'*' || ch == L'$' || ch == L'/' || ch == L'+' || ch == L'%') {
+    if (IsInOnedCode39Alphabet(FXSYS_ToUpperASCII(ch)))
       renderContents += ch;
-    }
   }
   return renderContents;
 }
@@ -193,15 +185,10 @@
   outlength = codeWidth;
   std::unique_ptr<uint8_t, FxFreeDeleter> result(FX_Alloc(uint8_t, codeWidth));
   ToIntArray(kOnedCode39CharacterEncoding[39], widths);
-  int32_t e = BCExceptionNO;
-  int32_t pos = AppendPattern(result.get(), 0, widths, 9, 1, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  int32_t pos = AppendPattern(result.get(), 0, widths, 9, true);
 
   int8_t narrowWhite[] = {1};
-  pos += AppendPattern(result.get(), pos, narrowWhite, 1, 0, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, narrowWhite, 1, false);
 
   for (int32_t l = m_iContentLen - 1; l >= 0; l--) {
     for (size_t i = 0; i < kOnedCode39AlphabetLen; i++) {
@@ -209,18 +196,12 @@
         continue;
 
       ToIntArray(kOnedCode39CharacterEncoding[i], widths);
-      pos += AppendPattern(result.get(), pos, widths, 9, 1, e);
-      if (e != BCExceptionNO)
-        return nullptr;
+      pos += AppendPattern(result.get(), pos, widths, 9, true);
     }
-    pos += AppendPattern(result.get(), pos, narrowWhite, 1, 0, e);
-    if (e != BCExceptionNO)
-      return nullptr;
+    pos += AppendPattern(result.get(), pos, narrowWhite, 1, false);
   }
   ToIntArray(kOnedCode39CharacterEncoding[39], widths);
-  pos += AppendPattern(result.get(), pos, widths, 9, 1, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, widths, 9, true);
 
   auto* result_ptr = result.get();
   for (int32_t i = 0; i < codeWidth / 2; i++) {
@@ -231,12 +212,12 @@
   return result.release();
 }
 
-bool CBC_OnedCode39Writer::encodedContents(const WideStringView& contents,
+bool CBC_OnedCode39Writer::encodedContents(WideStringView contents,
                                            WideString* result) {
   *result = WideString(contents);
   if (m_bCalcChecksum && m_bPrintChecksum) {
     WideString checksumContent = FilterContents(contents);
-    ByteString str = checksumContent.UTF8Encode();
+    ByteString str = checksumContent.ToUTF8();
     char checksum;
     checksum = CalcCheckSum(str);
     if (checksum == '*')
@@ -247,7 +228,7 @@
   return true;
 }
 
-bool CBC_OnedCode39Writer::RenderResult(const WideStringView& contents,
+bool CBC_OnedCode39Writer::RenderResult(WideStringView contents,
                                         uint8_t* code,
                                         int32_t codeLength) {
   WideString encodedCon;
diff --git a/fxbarcode/oned/BC_OnedCode39Writer.h b/fxbarcode/oned/BC_OnedCode39Writer.h
index 90611c0..525573a 100644
--- a/fxbarcode/oned/BC_OnedCode39Writer.h
+++ b/fxbarcode/oned/BC_OnedCode39Writer.h
@@ -10,7 +10,7 @@
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 
-class CBC_OnedCode39Writer : public CBC_OneDimWriter {
+class CBC_OnedCode39Writer final : public CBC_OneDimWriter {
  public:
   CBC_OnedCode39Writer();
   ~CBC_OnedCode39Writer() override;
@@ -22,23 +22,22 @@
                           int32_t& outHeight,
                           int32_t hints) override;
   uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
-  bool RenderResult(const WideStringView& contents,
+  bool RenderResult(WideStringView contents,
                     uint8_t* code,
                     int32_t codeLength) override;
-  bool CheckContentValidity(const WideStringView& contents) override;
-  WideString FilterContents(const WideStringView& contents) override;
-  WideString RenderTextContents(const WideStringView& contents) override;
+  bool CheckContentValidity(WideStringView contents) override;
+  WideString FilterContents(WideStringView contents) override;
+  bool SetTextLocation(BC_TEXT_LOC location) override;
+  bool SetWideNarrowRatio(int8_t ratio) override;
 
-  virtual bool SetTextLocation(BC_TEXT_LOC loction);
-  virtual bool SetWideNarrowRatio(int8_t ratio);
-
-  bool encodedContents(const WideStringView& contents, WideString* result);
+  WideString RenderTextContents(WideStringView contents);
+  bool encodedContents(WideStringView contents, WideString* result);
 
  private:
   void ToIntArray(int16_t a, int8_t* toReturn);
   char CalcCheckSum(const ByteString& contents);
 
-  int8_t m_iWideNarrRatio;
+  int8_t m_iWideNarrRatio = 3;
 };
 
 #endif  // FXBARCODE_ONED_BC_ONEDCODE39WRITER_H_
diff --git a/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp b/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp
index 27a0410..e263d59 100644
--- a/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "fxbarcode/oned/BC_OnedCode39Writer.h"
+
 #include <cstring>
 
-#include "fxbarcode/oned/BC_OnedCode39Writer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
 // 3 wide and 6 narrow modules per char. 1 space between chars.
-const int MODULES_PER_CHAR = 3 * 3 + 6 * 1 + 1;
+constexpr int MODULES_PER_CHAR = 3 * 3 + 6 + 1;
 
 // '*' is added as the first and last char.
 const int DELIMITER_CHARS = 2;
@@ -49,6 +50,8 @@
       "###   # # # ### "  // U
       "### ### # #   # "  // M
       "#   # ### ### #";  // * End
+  for (size_t i = 0; i < strlen(expected); i++)
+    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
   FX_Free(encoded);
 
   writer.SetWideNarrowRatio(2);
@@ -63,6 +66,8 @@
       "##  # # # ## "  // U
       "## ## # #  # "  // M
       "#  # ## ## #";  // * End
+  for (size_t i = 0; i < strlen(expected); i++)
+    EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
   FX_Free(encoded);
 }
 
@@ -79,9 +84,8 @@
   expected =
       "#   # ### ### # "  // * Start
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++) {
+  for (size_t i = 0; i < strlen(expected); i++)
     EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
   FX_Free(encoded);
 
   encoded = writer.Encode("123", BCFORMAT_CODE_39, width, height);
@@ -93,9 +97,8 @@
       "# ###   # # ### "  // 2
       "### ###   # # # "  // 3
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++) {
+  for (size_t i = 0; i < strlen(expected); i++)
     EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
   FX_Free(encoded);
 
   encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height);
@@ -110,9 +113,8 @@
       "###   # # # ### "  // U
       "### ### # #   # "  // M
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++) {
+  for (size_t i = 0; i < strlen(expected); i++)
     EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
   FX_Free(encoded);
 
   encoded = writer.Encode("A -$%./+Z", BCFORMAT_CODE_39, width, height);
@@ -130,9 +132,8 @@
       "#   # #   #   # "  // +
       "#   ### ### # # "  // Z
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++) {
+  for (size_t i = 0; i < strlen(expected); i++)
     EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
   FX_Free(encoded);
 }
 
@@ -156,9 +157,8 @@
       "### ###   # # # "  // 3 (3)
       "# ###   ### # # "  // 6 (6 = (1 + 2 + 3) % 43)
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++) {
+  for (size_t i = 0; i < strlen(expected); i++)
     EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
   FX_Free(encoded);
 
   encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height);
@@ -175,9 +175,8 @@
       "### ### # #   # "  // M (22)
       "###   # # ### # "  // . (37 = (25 + 13 + 15 + 18 + 30 + 22) % 43)
       "#   # ### ### #";  // * End
-  for (size_t i = 0; i < strlen(expected); i++) {
+  for (size_t i = 0; i < strlen(expected); i++)
     EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i;
-  }
   FX_Free(encoded);
 }
 
diff --git a/fxbarcode/oned/BC_OnedEAN13Writer.cpp b/fxbarcode/oned/BC_OnedEAN13Writer.cpp
index a6315bc..f2e88dd 100644
--- a/fxbarcode/oned/BC_OnedEAN13Writer.cpp
+++ b/fxbarcode/oned/BC_OnedEAN13Writer.cpp
@@ -28,7 +28,9 @@
 #include <vector>
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/text_char_pos.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 #include "fxbarcode/oned/BC_OnedEANChecksum.h"
@@ -56,12 +58,14 @@
 }
 CBC_OnedEAN13Writer::~CBC_OnedEAN13Writer() {}
 
-bool CBC_OnedEAN13Writer::CheckContentValidity(const WideStringView& contents) {
-  return std::all_of(contents.begin(), contents.end(), std::iswdigit);
+bool CBC_OnedEAN13Writer::CheckContentValidity(WideStringView contents) {
+  return std::all_of(contents.begin(), contents.end(),
+                     [](wchar_t c) { return FXSYS_IsDecimalDigit(c); });
 }
 
-WideString CBC_OnedEAN13Writer::FilterContents(const WideStringView& contents) {
+WideString CBC_OnedEAN13Writer::FilterContents(WideStringView contents) {
   WideString filtercontents;
+  filtercontents.Reserve(contents.GetLength());
   wchar_t ch;
   for (size_t i = 0; i < contents.GetLength(); i++) {
     ch = contents[i];
@@ -69,9 +73,8 @@
       i++;
       continue;
     }
-    if (ch >= '0' && ch <= '9') {
+    if (FXSYS_IsDecimalDigit(ch))
       filtercontents += ch;
-    }
   }
   return filtercontents;
 }
@@ -97,16 +100,13 @@
     return nullptr;
 
   m_iDataLenth = 13;
-  int32_t firstDigit = FXSYS_DecimalCharToInt(contents.First());
+  int32_t firstDigit = FXSYS_DecimalCharToInt(contents.Front());
   int32_t parities = kFirstDigitEncodings[firstDigit];
   outLength = m_codeWidth;
   std::unique_ptr<uint8_t, FxFreeDeleter> result(
       FX_Alloc(uint8_t, m_codeWidth));
   int32_t pos = 0;
-  int32_t e = BCExceptionNO;
-  pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, 1, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, true);
 
   int32_t i = 0;
   for (i = 1; i <= 6; i++) {
@@ -114,27 +114,19 @@
     if ((parities >> (6 - i) & 1) == 1) {
       digit += 10;
     }
-    pos += AppendPattern(result.get(), pos, L_AND_G_PATTERNS[digit], 4, 0, e);
-    if (e != BCExceptionNO)
-      return nullptr;
+    pos += AppendPattern(result.get(), pos, L_AND_G_PATTERNS[digit], 4, false);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN13MiddlePattern, 5, 0, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, kOnedEAN13MiddlePattern, 5, false);
 
   for (i = 7; i <= 12; i++) {
     int32_t digit = FXSYS_DecimalCharToInt(contents[i]);
-    pos += AppendPattern(result.get(), pos, kOnedEAN13LPattern[digit], 4, 1, e);
-    if (e != BCExceptionNO)
-      return nullptr;
+    pos += AppendPattern(result.get(), pos, kOnedEAN13LPattern[digit], 4, true);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, 1, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, true);
   return result.release();
 }
 
-bool CBC_OnedEAN13Writer::ShowChars(const WideStringView& contents,
+bool CBC_OnedEAN13Writer::ShowChars(WideStringView contents,
                                     CFX_RenderDevice* device,
                                     const CFX_Matrix* matrix,
                                     int32_t barWidth,
@@ -145,11 +137,11 @@
   int32_t leftPadding = 7 * multiple;
   int32_t leftPosition = 3 * multiple + leftPadding;
   ByteString str = FX_UTF8Encode(contents);
-  int32_t iLen = str.GetLength();
-  std::vector<FXTEXT_CHARPOS> charpos(iLen);
+  size_t length = str.GetLength();
+  std::vector<TextCharPos> charpos(length);
   int32_t iFontSize = (int32_t)fabs(m_fFontSize);
   int32_t iTextHeight = iFontSize + 1;
-  ByteString tempStr = str.Mid(1, 6);
+  ByteString tempStr = str.Substr(1, 6);
   int32_t strWidth = multiple * 42;
 
   CFX_Matrix matr(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
@@ -157,24 +149,24 @@
                      (float)(leftPosition + strWidth - 0.5), (float)m_Height);
   matr.Concat(*matrix);
   FX_RECT re = matr.TransformRect(rect).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   CFX_FloatRect rect1(
       (float)(leftPosition + 47 * multiple), (float)(m_Height - iTextHeight),
       (float)(leftPosition + 47 * multiple + strWidth - 0.5), (float)m_Height);
   CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
   matr1.Concat(*matrix);
   re = matr1.TransformRect(rect1).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   int32_t strWidth1 = multiple * 7;
   CFX_Matrix matr2(m_outputHScale, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
   CFX_FloatRect rect2(0.0f, (float)(m_Height - iTextHeight),
                       (float)strWidth1 - 0.5f, (float)m_Height);
   matr2.Concat(*matrix);
   re = matr2.TransformRect(rect2).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
 
   float blank = 0.0;
-  iLen = tempStr.GetLength();
+  length = tempStr.GetLength();
   strWidth = (int32_t)(strWidth * m_outputHScale);
 
   CalcTextInfo(tempStr, &charpos[1], m_pFont.Get(), (float)strWidth, iFontSize,
@@ -185,12 +177,12 @@
                               (float)(m_Height - iTextHeight) + iFontSize);
     if (matrix)
       affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, &charpos[1], m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+    device->DrawNormalText(length, &charpos[1], m_pFont.Get(),
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
-  tempStr = str.Mid(7, 6);
-  iLen = tempStr.GetLength();
+  tempStr = str.Substr(7, 6);
+  length = tempStr.GetLength();
   CalcTextInfo(tempStr, &charpos[7], m_pFont.Get(), (float)strWidth, iFontSize,
                blank);
   {
@@ -200,12 +192,12 @@
         (float)(m_Height - iTextHeight + iFontSize));
     if (matrix)
       affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, &charpos[7], m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+    device->DrawNormalText(length, &charpos[7], m_pFont.Get(),
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
-  tempStr = str.Left(1);
-  iLen = tempStr.GetLength();
+  tempStr = str.First(1);
+  length = tempStr.GetLength();
   strWidth = multiple * 7;
   strWidth = (int32_t)(strWidth * m_outputHScale);
 
@@ -216,8 +208,8 @@
                               (float)(m_Height - iTextHeight + iFontSize));
     if (matrix)
       affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, charpos.data(), m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+    device->DrawNormalText(length, charpos.data(), m_pFont.Get(),
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
   return true;
diff --git a/fxbarcode/oned/BC_OnedEAN13Writer.h b/fxbarcode/oned/BC_OnedEAN13Writer.h
index c874610..88fa9cb 100644
--- a/fxbarcode/oned/BC_OnedEAN13Writer.h
+++ b/fxbarcode/oned/BC_OnedEAN13Writer.h
@@ -9,36 +9,34 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "fxbarcode/oned/BC_OneDimWriter.h"
+#include "fxbarcode/oned/BC_OnedEANWriter.h"
 
 class CFX_DIBitmap;
 class CFX_RenderDevice;
 
-class CBC_OnedEAN13Writer : public CBC_OneDimWriter {
+class CBC_OnedEAN13Writer final : public CBC_OneDimEANWriter {
  public:
   CBC_OnedEAN13Writer();
   ~CBC_OnedEAN13Writer() override;
 
-  // CBC_OneDimWriter
+  // CBC_OneDimEANWriter:
   uint8_t* EncodeWithHint(const ByteString& contents,
                           BCFORMAT format,
                           int32_t& outWidth,
                           int32_t& outHeight,
                           int32_t hints) override;
   uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
-  bool CheckContentValidity(const WideStringView& contents) override;
-  WideString FilterContents(const WideStringView& contents) override;
+  bool CheckContentValidity(WideStringView contents) override;
+  WideString FilterContents(WideStringView contents) override;
+  int32_t CalcChecksum(const ByteString& contents) override;
 
-  int32_t CalcChecksum(const ByteString& contents);
-
- protected:
-  bool ShowChars(const WideStringView& contents,
+ private:
+  bool ShowChars(WideStringView contents,
                  CFX_RenderDevice* device,
                  const CFX_Matrix* matrix,
                  int32_t barWidth,
                  int32_t multiple) override;
 
- private:
   int32_t m_codeWidth;
 };
 
diff --git a/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp b/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp
index 932e1f4..55746ba 100644
--- a/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedEAN13Writer.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
 TEST(OnedEAN13WriterTest, Encode) {
   CBC_OnedEAN13Writer writer;
+  writer.InitEANWriter();
   int32_t width;
   int32_t height;
   uint8_t* encoded;
@@ -89,6 +91,7 @@
 
 TEST(OnedEAN13WriterTest, Checksum) {
   CBC_OnedEAN13Writer writer;
+  writer.InitEANWriter();
   EXPECT_EQ(0, writer.CalcChecksum(""));
   EXPECT_EQ(6, writer.CalcChecksum("123"));
   EXPECT_EQ(8, writer.CalcChecksum("123456789012"));
diff --git a/fxbarcode/oned/BC_OnedEAN8Writer.cpp b/fxbarcode/oned/BC_OnedEAN8Writer.cpp
index 988528e..62c760c 100644
--- a/fxbarcode/oned/BC_OnedEAN8Writer.cpp
+++ b/fxbarcode/oned/BC_OnedEAN8Writer.cpp
@@ -28,7 +28,9 @@
 #include <vector>
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/text_char_pos.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
@@ -46,10 +48,9 @@
 
 CBC_OnedEAN8Writer::CBC_OnedEAN8Writer() {
   m_iDataLenth = 8;
-  m_codeWidth = 3 + (7 * 4) + 5 + (7 * 4) + 3;
 }
 
-CBC_OnedEAN8Writer::~CBC_OnedEAN8Writer() {}
+CBC_OnedEAN8Writer::~CBC_OnedEAN8Writer() = default;
 
 void CBC_OnedEAN8Writer::SetDataLength(int32_t length) {
   m_iDataLenth = 8;
@@ -63,12 +64,14 @@
   return false;
 }
 
-bool CBC_OnedEAN8Writer::CheckContentValidity(const WideStringView& contents) {
-  return std::all_of(contents.begin(), contents.end(), std::iswdigit);
+bool CBC_OnedEAN8Writer::CheckContentValidity(WideStringView contents) {
+  return std::all_of(contents.begin(), contents.end(),
+                     [](wchar_t c) { return FXSYS_IsDecimalDigit(c); });
 }
 
-WideString CBC_OnedEAN8Writer::FilterContents(const WideStringView& contents) {
+WideString CBC_OnedEAN8Writer::FilterContents(WideStringView contents) {
   WideString filtercontents;
+  filtercontents.Reserve(contents.GetLength());
   wchar_t ch;
   for (size_t i = 0; i < contents.GetLength(); i++) {
     ch = contents[i];
@@ -76,9 +79,8 @@
       i++;
       continue;
     }
-    if (ch >= '0' && ch <= '9') {
+    if (FXSYS_IsDecimalDigit(ch))
       filtercontents += ch;
-    }
   }
   return filtercontents;
 }
@@ -107,35 +109,24 @@
   std::unique_ptr<uint8_t, FxFreeDeleter> result(
       FX_Alloc(uint8_t, m_codeWidth));
   int32_t pos = 0;
-  int32_t e = BCExceptionNO;
-  pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, 1, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, true);
 
   int32_t i = 0;
   for (i = 0; i <= 3; i++) {
     int32_t digit = FXSYS_DecimalCharToInt(contents[i]);
-    pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, 0, e);
-    if (e != BCExceptionNO)
-      return nullptr;
+    pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, false);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN8MiddlePattern, 5, 0, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, kOnedEAN8MiddlePattern, 5, false);
 
   for (i = 4; i <= 7; i++) {
     int32_t digit = FXSYS_DecimalCharToInt(contents[i]);
-    pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, 1, e);
-    if (e != BCExceptionNO)
-      return nullptr;
+    pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, true);
   }
-  pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, 1, e);
-  if (e != BCExceptionNO)
-    return nullptr;
+  pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, true);
   return result.release();
 }
 
-bool CBC_OnedEAN8Writer::ShowChars(const WideStringView& contents,
+bool CBC_OnedEAN8Writer::ShowChars(WideStringView contents,
                                    CFX_RenderDevice* device,
                                    const CFX_Matrix* matrix,
                                    int32_t barWidth,
@@ -146,8 +137,8 @@
   int32_t leftPosition = 3 * multiple;
   ByteString str = FX_UTF8Encode(contents);
   size_t iLength = str.GetLength();
-  std::vector<FXTEXT_CHARPOS> charpos(iLength);
-  ByteString tempStr = str.Left(4);
+  std::vector<TextCharPos> charpos(iLength);
+  ByteString tempStr = str.First(4);
   size_t iLen = tempStr.GetLength();
   int32_t strWidth = 7 * multiple * 4;
   float blank = 0.0;
@@ -160,14 +151,14 @@
                      (float)(leftPosition + strWidth - 0.5), (float)m_Height);
   matr.Concat(*matrix);
   FX_RECT re = matr.TransformRect(rect).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
   CFX_FloatRect rect1(
       (float)(leftPosition + 33 * multiple), (float)(m_Height - iTextHeight),
       (float)(leftPosition + 33 * multiple + strWidth - 0.5), (float)m_Height);
   matr1.Concat(*matrix);
   re = matr1.TransformRect(rect1).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   strWidth = (int32_t)(strWidth * m_outputHScale);
 
   CalcTextInfo(tempStr, charpos.data(), m_pFont.Get(), (float)strWidth,
@@ -178,10 +169,10 @@
                               (float)(m_Height - iTextHeight + iFontSize));
     affine_matrix1.Concat(*matrix);
     device->DrawNormalText(iLen, charpos.data(), m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
-  tempStr = str.Mid(4, 4);
+  tempStr = str.Substr(4, 4);
   iLen = tempStr.GetLength();
   CalcTextInfo(tempStr, &charpos[4], m_pFont.Get(), (float)strWidth, iFontSize,
                blank);
@@ -193,7 +184,7 @@
     if (matrix)
       affine_matrix1.Concat(*matrix);
     device->DrawNormalText(iLen, &charpos[4], m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
   return true;
diff --git a/fxbarcode/oned/BC_OnedEAN8Writer.h b/fxbarcode/oned/BC_OnedEAN8Writer.h
index 77a3602..b71dbb9 100644
--- a/fxbarcode/oned/BC_OnedEAN8Writer.h
+++ b/fxbarcode/oned/BC_OnedEAN8Writer.h
@@ -10,39 +10,38 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 #include "fxbarcode/BC_Library.h"
-#include "fxbarcode/oned/BC_OneDimWriter.h"
+#include "fxbarcode/oned/BC_OnedEANWriter.h"
 
 class CFX_DIBitmap;
 class CFX_RenderDevice;
 
-class CBC_OnedEAN8Writer : public CBC_OneDimWriter {
+class CBC_OnedEAN8Writer final : public CBC_OneDimEANWriter {
  public:
   CBC_OnedEAN8Writer();
   ~CBC_OnedEAN8Writer() override;
 
-  // CBC_OneDimWriter
+  // CBC_OneDimEANWriter:
   uint8_t* EncodeWithHint(const ByteString& contents,
                           BCFORMAT format,
                           int32_t& outWidth,
                           int32_t& outHeight,
                           int32_t hints) override;
   uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
-  bool CheckContentValidity(const WideStringView& contents) override;
-  WideString FilterContents(const WideStringView& contents) override;
+  bool CheckContentValidity(WideStringView contents) override;
+  WideString FilterContents(WideStringView contents) override;
   void SetDataLength(int32_t length) override;
+  bool SetTextLocation(BC_TEXT_LOC location) override;
+  int32_t CalcChecksum(const ByteString& contents) override;
 
-  bool SetTextLocation(BC_TEXT_LOC location);
-  int32_t CalcChecksum(const ByteString& contents);
-
- protected:
-  bool ShowChars(const WideStringView& contents,
+ private:
+  bool ShowChars(WideStringView contents,
                  CFX_RenderDevice* device,
                  const CFX_Matrix* matrix,
                  int32_t barWidth,
                  int32_t multiple) override;
 
- private:
-  int32_t m_codeWidth;
+  static constexpr int32_t kDefaultCodeWidth = 3 + (7 * 4) + 5 + (7 * 4) + 3;
+  int32_t m_codeWidth = kDefaultCodeWidth;
 };
 
 #endif  // FXBARCODE_ONED_BC_ONEDEAN8WRITER_H_
diff --git a/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp b/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp
index 16a1df6..ae1b823 100644
--- a/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedEAN8Writer.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
 TEST(OnedEAN8WriterTest, Encode) {
   CBC_OnedEAN8Writer writer;
+  writer.InitEANWriter();
   int32_t width;
   int32_t height;
   uint8_t* encoded;
@@ -79,6 +81,7 @@
 
 TEST(OnedEAN8WriterTest, Checksum) {
   CBC_OnedEAN8Writer writer;
+  writer.InitEANWriter();
   EXPECT_EQ(0, writer.CalcChecksum(""));
   EXPECT_EQ(6, writer.CalcChecksum("123"));
   EXPECT_EQ(0, writer.CalcChecksum("1234567"));
diff --git a/fxbarcode/oned/BC_OnedEANWriter.cpp b/fxbarcode/oned/BC_OnedEANWriter.cpp
new file mode 100644
index 0000000..f43b9ad
--- /dev/null
+++ b/fxbarcode/oned/BC_OnedEANWriter.cpp
@@ -0,0 +1,11 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxbarcode/oned/BC_OnedEANWriter.h"
+
+CBC_OneDimEANWriter::CBC_OneDimEANWriter() = default;
+
+CBC_OneDimEANWriter::~CBC_OneDimEANWriter() = default;
+
+void CBC_OneDimEANWriter::InitEANWriter() {}
diff --git a/fxbarcode/oned/BC_OnedEANWriter.h b/fxbarcode/oned/BC_OnedEANWriter.h
new file mode 100644
index 0000000..5d80167
--- /dev/null
+++ b/fxbarcode/oned/BC_OnedEANWriter.h
@@ -0,0 +1,19 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXBARCODE_ONED_BC_ONEDEANWRITER_H_
+#define FXBARCODE_ONED_BC_ONEDEANWRITER_H_
+
+#include "fxbarcode/oned/BC_OneDimWriter.h"
+
+class CBC_OneDimEANWriter : public CBC_OneDimWriter {
+ public:
+  CBC_OneDimEANWriter();
+  ~CBC_OneDimEANWriter() override;
+
+  virtual void InitEANWriter();
+  virtual int32_t CalcChecksum(const ByteString& contents) = 0;
+};
+
+#endif  // FXBARCODE_ONED_BC_ONEDEANWRITER_H_
diff --git a/fxbarcode/oned/BC_OnedUPCAWriter.cpp b/fxbarcode/oned/BC_OnedUPCAWriter.cpp
index 025f851..666b037 100644
--- a/fxbarcode/oned/BC_OnedUPCAWriter.cpp
+++ b/fxbarcode/oned/BC_OnedUPCAWriter.cpp
@@ -26,6 +26,7 @@
 
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/text_char_pos.h"
 #include "fxbarcode/BC_Writer.h"
 #include "fxbarcode/oned/BC_OneDimWriter.h"
 #include "fxbarcode/oned/BC_OnedEAN13Writer.h"
@@ -36,13 +37,9 @@
   m_bRightPadding = true;
 }
 
-void CBC_OnedUPCAWriter::Init() {
-  m_subWriter = pdfium::MakeUnique<CBC_OnedEAN13Writer>();
-}
-
 CBC_OnedUPCAWriter::~CBC_OnedUPCAWriter() {}
 
-bool CBC_OnedUPCAWriter::CheckContentValidity(const WideStringView& contents) {
+bool CBC_OnedUPCAWriter::CheckContentValidity(WideStringView contents) {
   for (size_t i = 0; i < contents.GetLength(); ++i) {
     if (contents[i] < '0' || contents[i] > '9')
       return false;
@@ -50,8 +47,9 @@
   return true;
 }
 
-WideString CBC_OnedUPCAWriter::FilterContents(const WideStringView& contents) {
+WideString CBC_OnedUPCAWriter::FilterContents(WideStringView contents) {
   WideString filtercontents;
+  filtercontents.Reserve(contents.GetLength());
   wchar_t ch;
   for (size_t i = 0; i < contents.GetLength(); i++) {
     ch = contents[i];
@@ -59,13 +57,16 @@
       i++;
       continue;
     }
-    if (ch >= '0' && ch <= '9') {
+    if (FXSYS_IsDecimalDigit(ch))
       filtercontents += ch;
-    }
   }
   return filtercontents;
 }
 
+void CBC_OnedUPCAWriter::InitEANWriter() {
+  m_subWriter = pdfium::MakeUnique<CBC_OnedEAN13Writer>();
+}
+
 int32_t CBC_OnedUPCAWriter::CalcChecksum(const ByteString& contents) {
   int32_t odd = 0;
   int32_t even = 0;
@@ -102,7 +103,7 @@
   return nullptr;
 }
 
-bool CBC_OnedUPCAWriter::ShowChars(const WideStringView& contents,
+bool CBC_OnedUPCAWriter::ShowChars(WideStringView contents,
                                    CFX_RenderDevice* device,
                                    const CFX_Matrix* matrix,
                                    int32_t barWidth,
@@ -113,13 +114,13 @@
   int32_t leftPadding = 7 * multiple;
   int32_t leftPosition = 10 * multiple + leftPadding;
   ByteString str = FX_UTF8Encode(contents);
-  int32_t iLen = str.GetLength();
-  std::vector<FXTEXT_CHARPOS> charpos(iLen);
-  ByteString tempStr = str.Mid(1, 5);
+  size_t length = str.GetLength();
+  std::vector<TextCharPos> charpos(length);
+  ByteString tempStr = str.Substr(1, 5);
   float strWidth = (float)35 * multiple;
   float blank = 0.0;
 
-  iLen = tempStr.GetLength();
+  length = tempStr.GetLength();
   int32_t iFontSize = (int32_t)fabs(m_fFontSize);
   int32_t iTextHeight = iFontSize + 1;
 
@@ -128,7 +129,7 @@
                      (float)(leftPosition + strWidth - 0.5), (float)m_Height);
   matr.Concat(*matrix);
   FX_RECT re = matr.TransformRect(rect).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
   CFX_FloatRect rect1((float)(leftPosition + 40 * multiple),
                       (float)(m_Height - iTextHeight),
@@ -136,14 +137,14 @@
                       (float)m_Height);
   matr1.Concat(*matrix);
   re = matr1.TransformRect(rect1).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   float strWidth1 = (float)multiple * 7;
   CFX_Matrix matr2(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
   CFX_FloatRect rect2(0.0, (float)(m_Height - iTextHeight),
                       (float)strWidth1 - 1, (float)m_Height);
   matr2.Concat(*matrix);
   re = matr2.TransformRect(rect2).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   CFX_Matrix matr3(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0);
   CFX_FloatRect rect3((float)(leftPosition + 85 * multiple),
                       (float)(m_Height - iTextHeight),
@@ -151,7 +152,7 @@
                       (float)m_Height);
   matr3.Concat(*matrix);
   re = matr3.TransformRect(rect3).GetOuterRect();
-  device->FillRect(&re, m_backgroundColor);
+  device->FillRect(re, kBackgroundColor);
   strWidth = strWidth * m_outputHScale;
 
   CalcTextInfo(tempStr, &charpos[1], m_pFont.Get(), strWidth, iFontSize, blank);
@@ -161,12 +162,12 @@
                               (float)(m_Height - iTextHeight + iFontSize));
     if (matrix)
       affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, &charpos[1], m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+    device->DrawNormalText(length, &charpos[1], m_pFont.Get(),
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
-  tempStr = str.Mid(6, 5);
-  iLen = tempStr.GetLength();
+  tempStr = str.Substr(6, 5);
+  length = tempStr.GetLength();
   CalcTextInfo(tempStr, &charpos[6], m_pFont.Get(), strWidth, iFontSize, blank);
   {
     CFX_Matrix affine_matrix1(
@@ -175,12 +176,12 @@
         (float)(m_Height - iTextHeight + iFontSize));
     if (matrix)
       affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, &charpos[6], m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+    device->DrawNormalText(length, &charpos[6], m_pFont.Get(),
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
-  tempStr = str.Left(1);
-  iLen = tempStr.GetLength();
+  tempStr = str.First(1);
+  length = tempStr.GetLength();
   strWidth = (float)multiple * 7;
   strWidth = strWidth * m_outputHScale;
 
@@ -191,12 +192,12 @@
                               (float)(m_Height - iTextHeight + iFontSize));
     if (matrix)
       affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, charpos.data(), m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+    device->DrawNormalText(length, charpos.data(), m_pFont.Get(),
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
-  tempStr = str.Mid(11, 1);
-  iLen = tempStr.GetLength();
+  tempStr = str.Substr(11, 1);
+  length = tempStr.GetLength();
   CalcTextInfo(tempStr, &charpos[11], m_pFont.Get(), strWidth, iFontSize,
                blank);
   {
@@ -206,8 +207,8 @@
         (float)(m_Height - iTextHeight + iFontSize));
     if (matrix)
       affine_matrix1.Concat(*matrix);
-    device->DrawNormalText(iLen, &charpos[11], m_pFont.Get(),
-                           static_cast<float>(iFontSize), &affine_matrix1,
+    device->DrawNormalText(length, &charpos[11], m_pFont.Get(),
+                           static_cast<float>(iFontSize), affine_matrix1,
                            m_fontColor, FXTEXT_CLEARTYPE);
   }
   return true;
diff --git a/fxbarcode/oned/BC_OnedUPCAWriter.h b/fxbarcode/oned/BC_OnedUPCAWriter.h
index a854ef4..991dfe6 100644
--- a/fxbarcode/oned/BC_OnedUPCAWriter.h
+++ b/fxbarcode/oned/BC_OnedUPCAWriter.h
@@ -11,39 +11,37 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "fxbarcode/oned/BC_OneDimWriter.h"
+#include "fxbarcode/oned/BC_OnedEANWriter.h"
 
 class CBC_OnedEAN13Writer;
 class CFX_DIBitmap;
 class CFX_Matrix;
 class CFX_RenderDevice;
 
-class CBC_OnedUPCAWriter : public CBC_OneDimWriter {
+class CBC_OnedUPCAWriter final : public CBC_OneDimEANWriter {
  public:
   CBC_OnedUPCAWriter();
   ~CBC_OnedUPCAWriter() override;
 
-  // CBC_OneDimWriter
+  // CBC_OneDimEANWriter:
   uint8_t* EncodeWithHint(const ByteString& contents,
                           BCFORMAT format,
                           int32_t& outWidth,
                           int32_t& outHeight,
                           int32_t hints) override;
   uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override;
-  bool CheckContentValidity(const WideStringView& contents) override;
-  WideString FilterContents(const WideStringView& contents) override;
+  bool CheckContentValidity(WideStringView contents) override;
+  WideString FilterContents(WideStringView contents) override;
+  void InitEANWriter() override;
+  int32_t CalcChecksum(const ByteString& contents) override;
 
-  void Init();
-  int32_t CalcChecksum(const ByteString& contents);
-
- protected:
-  bool ShowChars(const WideStringView& contents,
+ private:
+  bool ShowChars(WideStringView contents,
                  CFX_RenderDevice* device,
                  const CFX_Matrix* matrix,
                  int32_t barWidth,
                  int32_t multiple) override;
 
- private:
   std::unique_ptr<CBC_OnedEAN13Writer> m_subWriter;
 };
 
diff --git a/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp b/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp
index 3d9f6c5..c5acd23 100644
--- a/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp
+++ b/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp
@@ -3,19 +3,17 @@
 // found in the LICENSE file.
 
 #include "fxbarcode/oned/BC_OnedUPCAWriter.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
 TEST(OnedUPCAWriterTest, Encode) {
   CBC_OnedUPCAWriter writer;
+  writer.InitEANWriter();
   int32_t width;
   int32_t height;
 
-  // TODO(hnakashima): CBC_OnedUPCAWriter is unique in that it needs to be
-  // Init()'d. Get rid of this call.
-  writer.Init();
-
   // UPCA barcodes encode 12-digit numbers into 95 modules in a unidimensional
   // disposition.
   uint8_t* encoded = writer.Encode("", BCFORMAT_UPC_A, width, height);
@@ -87,6 +85,7 @@
 
 TEST(OnedUPCAWriterTest, Checksum) {
   CBC_OnedUPCAWriter writer;
+  writer.InitEANWriter();
   EXPECT_EQ(0, writer.CalcChecksum(""));
   EXPECT_EQ(6, writer.CalcChecksum("123"));
   EXPECT_EQ(2, writer.CalcChecksum("12345678901"));
diff --git a/fxbarcode/pdf417/BC_PDF417.cpp b/fxbarcode/pdf417/BC_PDF417.cpp
index 9b73b49..b9ba390 100644
--- a/fxbarcode/pdf417/BC_PDF417.cpp
+++ b/fxbarcode/pdf417/BC_PDF417.cpp
@@ -24,395 +24,353 @@
 
 #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h"
 #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h"
-#include "fxbarcode/pdf417/BC_PDF417Compaction.h"
 #include "fxbarcode/pdf417/BC_PDF417ErrorCorrection.h"
 #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h"
-#include "fxbarcode/utils.h"
 #include "third_party/base/ptr_util.h"
 
-const int32_t CBC_PDF417::CODEWORD_TABLE[][929] = {
-    {0x1d5c0, 0x1eaf0, 0x1f57c, 0x1d4e0, 0x1ea78, 0x1f53e, 0x1a8c0, 0x1d470,
-     0x1a860, 0x15040, 0x1a830, 0x15020, 0x1adc0, 0x1d6f0, 0x1eb7c, 0x1ace0,
-     0x1d678, 0x1eb3e, 0x158c0, 0x1ac70, 0x15860, 0x15dc0, 0x1aef0, 0x1d77c,
-     0x15ce0, 0x1ae78, 0x1d73e, 0x15c70, 0x1ae3c, 0x15ef0, 0x1af7c, 0x15e78,
-     0x1af3e, 0x15f7c, 0x1f5fa, 0x1d2e0, 0x1e978, 0x1f4be, 0x1a4c0, 0x1d270,
-     0x1e93c, 0x1a460, 0x1d238, 0x14840, 0x1a430, 0x1d21c, 0x14820, 0x1a418,
-     0x14810, 0x1a6e0, 0x1d378, 0x1e9be, 0x14cc0, 0x1a670, 0x1d33c, 0x14c60,
-     0x1a638, 0x1d31e, 0x14c30, 0x1a61c, 0x14ee0, 0x1a778, 0x1d3be, 0x14e70,
-     0x1a73c, 0x14e38, 0x1a71e, 0x14f78, 0x1a7be, 0x14f3c, 0x14f1e, 0x1a2c0,
-     0x1d170, 0x1e8bc, 0x1a260, 0x1d138, 0x1e89e, 0x14440, 0x1a230, 0x1d11c,
-     0x14420, 0x1a218, 0x14410, 0x14408, 0x146c0, 0x1a370, 0x1d1bc, 0x14660,
-     0x1a338, 0x1d19e, 0x14630, 0x1a31c, 0x14618, 0x1460c, 0x14770, 0x1a3bc,
-     0x14738, 0x1a39e, 0x1471c, 0x147bc, 0x1a160, 0x1d0b8, 0x1e85e, 0x14240,
-     0x1a130, 0x1d09c, 0x14220, 0x1a118, 0x1d08e, 0x14210, 0x1a10c, 0x14208,
-     0x1a106, 0x14360, 0x1a1b8, 0x1d0de, 0x14330, 0x1a19c, 0x14318, 0x1a18e,
-     0x1430c, 0x14306, 0x1a1de, 0x1438e, 0x14140, 0x1a0b0, 0x1d05c, 0x14120,
-     0x1a098, 0x1d04e, 0x14110, 0x1a08c, 0x14108, 0x1a086, 0x14104, 0x141b0,
-     0x14198, 0x1418c, 0x140a0, 0x1d02e, 0x1a04c, 0x1a046, 0x14082, 0x1cae0,
-     0x1e578, 0x1f2be, 0x194c0, 0x1ca70, 0x1e53c, 0x19460, 0x1ca38, 0x1e51e,
-     0x12840, 0x19430, 0x12820, 0x196e0, 0x1cb78, 0x1e5be, 0x12cc0, 0x19670,
-     0x1cb3c, 0x12c60, 0x19638, 0x12c30, 0x12c18, 0x12ee0, 0x19778, 0x1cbbe,
-     0x12e70, 0x1973c, 0x12e38, 0x12e1c, 0x12f78, 0x197be, 0x12f3c, 0x12fbe,
-     0x1dac0, 0x1ed70, 0x1f6bc, 0x1da60, 0x1ed38, 0x1f69e, 0x1b440, 0x1da30,
-     0x1ed1c, 0x1b420, 0x1da18, 0x1ed0e, 0x1b410, 0x1da0c, 0x192c0, 0x1c970,
-     0x1e4bc, 0x1b6c0, 0x19260, 0x1c938, 0x1e49e, 0x1b660, 0x1db38, 0x1ed9e,
-     0x16c40, 0x12420, 0x19218, 0x1c90e, 0x16c20, 0x1b618, 0x16c10, 0x126c0,
-     0x19370, 0x1c9bc, 0x16ec0, 0x12660, 0x19338, 0x1c99e, 0x16e60, 0x1b738,
-     0x1db9e, 0x16e30, 0x12618, 0x16e18, 0x12770, 0x193bc, 0x16f70, 0x12738,
-     0x1939e, 0x16f38, 0x1b79e, 0x16f1c, 0x127bc, 0x16fbc, 0x1279e, 0x16f9e,
-     0x1d960, 0x1ecb8, 0x1f65e, 0x1b240, 0x1d930, 0x1ec9c, 0x1b220, 0x1d918,
-     0x1ec8e, 0x1b210, 0x1d90c, 0x1b208, 0x1b204, 0x19160, 0x1c8b8, 0x1e45e,
-     0x1b360, 0x19130, 0x1c89c, 0x16640, 0x12220, 0x1d99c, 0x1c88e, 0x16620,
-     0x12210, 0x1910c, 0x16610, 0x1b30c, 0x19106, 0x12204, 0x12360, 0x191b8,
-     0x1c8de, 0x16760, 0x12330, 0x1919c, 0x16730, 0x1b39c, 0x1918e, 0x16718,
-     0x1230c, 0x12306, 0x123b8, 0x191de, 0x167b8, 0x1239c, 0x1679c, 0x1238e,
-     0x1678e, 0x167de, 0x1b140, 0x1d8b0, 0x1ec5c, 0x1b120, 0x1d898, 0x1ec4e,
-     0x1b110, 0x1d88c, 0x1b108, 0x1d886, 0x1b104, 0x1b102, 0x12140, 0x190b0,
-     0x1c85c, 0x16340, 0x12120, 0x19098, 0x1c84e, 0x16320, 0x1b198, 0x1d8ce,
-     0x16310, 0x12108, 0x19086, 0x16308, 0x1b186, 0x16304, 0x121b0, 0x190dc,
-     0x163b0, 0x12198, 0x190ce, 0x16398, 0x1b1ce, 0x1638c, 0x12186, 0x16386,
-     0x163dc, 0x163ce, 0x1b0a0, 0x1d858, 0x1ec2e, 0x1b090, 0x1d84c, 0x1b088,
-     0x1d846, 0x1b084, 0x1b082, 0x120a0, 0x19058, 0x1c82e, 0x161a0, 0x12090,
-     0x1904c, 0x16190, 0x1b0cc, 0x19046, 0x16188, 0x12084, 0x16184, 0x12082,
-     0x120d8, 0x161d8, 0x161cc, 0x161c6, 0x1d82c, 0x1d826, 0x1b042, 0x1902c,
-     0x12048, 0x160c8, 0x160c4, 0x160c2, 0x18ac0, 0x1c570, 0x1e2bc, 0x18a60,
-     0x1c538, 0x11440, 0x18a30, 0x1c51c, 0x11420, 0x18a18, 0x11410, 0x11408,
-     0x116c0, 0x18b70, 0x1c5bc, 0x11660, 0x18b38, 0x1c59e, 0x11630, 0x18b1c,
-     0x11618, 0x1160c, 0x11770, 0x18bbc, 0x11738, 0x18b9e, 0x1171c, 0x117bc,
-     0x1179e, 0x1cd60, 0x1e6b8, 0x1f35e, 0x19a40, 0x1cd30, 0x1e69c, 0x19a20,
-     0x1cd18, 0x1e68e, 0x19a10, 0x1cd0c, 0x19a08, 0x1cd06, 0x18960, 0x1c4b8,
-     0x1e25e, 0x19b60, 0x18930, 0x1c49c, 0x13640, 0x11220, 0x1cd9c, 0x1c48e,
-     0x13620, 0x19b18, 0x1890c, 0x13610, 0x11208, 0x13608, 0x11360, 0x189b8,
-     0x1c4de, 0x13760, 0x11330, 0x1cdde, 0x13730, 0x19b9c, 0x1898e, 0x13718,
-     0x1130c, 0x1370c, 0x113b8, 0x189de, 0x137b8, 0x1139c, 0x1379c, 0x1138e,
-     0x113de, 0x137de, 0x1dd40, 0x1eeb0, 0x1f75c, 0x1dd20, 0x1ee98, 0x1f74e,
-     0x1dd10, 0x1ee8c, 0x1dd08, 0x1ee86, 0x1dd04, 0x19940, 0x1ccb0, 0x1e65c,
-     0x1bb40, 0x19920, 0x1eedc, 0x1e64e, 0x1bb20, 0x1dd98, 0x1eece, 0x1bb10,
-     0x19908, 0x1cc86, 0x1bb08, 0x1dd86, 0x19902, 0x11140, 0x188b0, 0x1c45c,
-     0x13340, 0x11120, 0x18898, 0x1c44e, 0x17740, 0x13320, 0x19998, 0x1ccce,
-     0x17720, 0x1bb98, 0x1ddce, 0x18886, 0x17710, 0x13308, 0x19986, 0x17708,
-     0x11102, 0x111b0, 0x188dc, 0x133b0, 0x11198, 0x188ce, 0x177b0, 0x13398,
-     0x199ce, 0x17798, 0x1bbce, 0x11186, 0x13386, 0x111dc, 0x133dc, 0x111ce,
-     0x177dc, 0x133ce, 0x1dca0, 0x1ee58, 0x1f72e, 0x1dc90, 0x1ee4c, 0x1dc88,
-     0x1ee46, 0x1dc84, 0x1dc82, 0x198a0, 0x1cc58, 0x1e62e, 0x1b9a0, 0x19890,
-     0x1ee6e, 0x1b990, 0x1dccc, 0x1cc46, 0x1b988, 0x19884, 0x1b984, 0x19882,
-     0x1b982, 0x110a0, 0x18858, 0x1c42e, 0x131a0, 0x11090, 0x1884c, 0x173a0,
-     0x13190, 0x198cc, 0x18846, 0x17390, 0x1b9cc, 0x11084, 0x17388, 0x13184,
-     0x11082, 0x13182, 0x110d8, 0x1886e, 0x131d8, 0x110cc, 0x173d8, 0x131cc,
-     0x110c6, 0x173cc, 0x131c6, 0x110ee, 0x173ee, 0x1dc50, 0x1ee2c, 0x1dc48,
-     0x1ee26, 0x1dc44, 0x1dc42, 0x19850, 0x1cc2c, 0x1b8d0, 0x19848, 0x1cc26,
-     0x1b8c8, 0x1dc66, 0x1b8c4, 0x19842, 0x1b8c2, 0x11050, 0x1882c, 0x130d0,
-     0x11048, 0x18826, 0x171d0, 0x130c8, 0x19866, 0x171c8, 0x1b8e6, 0x11042,
-     0x171c4, 0x130c2, 0x171c2, 0x130ec, 0x171ec, 0x171e6, 0x1ee16, 0x1dc22,
-     0x1cc16, 0x19824, 0x19822, 0x11028, 0x13068, 0x170e8, 0x11022, 0x13062,
-     0x18560, 0x10a40, 0x18530, 0x10a20, 0x18518, 0x1c28e, 0x10a10, 0x1850c,
-     0x10a08, 0x18506, 0x10b60, 0x185b8, 0x1c2de, 0x10b30, 0x1859c, 0x10b18,
-     0x1858e, 0x10b0c, 0x10b06, 0x10bb8, 0x185de, 0x10b9c, 0x10b8e, 0x10bde,
-     0x18d40, 0x1c6b0, 0x1e35c, 0x18d20, 0x1c698, 0x18d10, 0x1c68c, 0x18d08,
-     0x1c686, 0x18d04, 0x10940, 0x184b0, 0x1c25c, 0x11b40, 0x10920, 0x1c6dc,
-     0x1c24e, 0x11b20, 0x18d98, 0x1c6ce, 0x11b10, 0x10908, 0x18486, 0x11b08,
-     0x18d86, 0x10902, 0x109b0, 0x184dc, 0x11bb0, 0x10998, 0x184ce, 0x11b98,
-     0x18dce, 0x11b8c, 0x10986, 0x109dc, 0x11bdc, 0x109ce, 0x11bce, 0x1cea0,
-     0x1e758, 0x1f3ae, 0x1ce90, 0x1e74c, 0x1ce88, 0x1e746, 0x1ce84, 0x1ce82,
-     0x18ca0, 0x1c658, 0x19da0, 0x18c90, 0x1c64c, 0x19d90, 0x1cecc, 0x1c646,
-     0x19d88, 0x18c84, 0x19d84, 0x18c82, 0x19d82, 0x108a0, 0x18458, 0x119a0,
-     0x10890, 0x1c66e, 0x13ba0, 0x11990, 0x18ccc, 0x18446, 0x13b90, 0x19dcc,
-     0x10884, 0x13b88, 0x11984, 0x10882, 0x11982, 0x108d8, 0x1846e, 0x119d8,
-     0x108cc, 0x13bd8, 0x119cc, 0x108c6, 0x13bcc, 0x119c6, 0x108ee, 0x119ee,
-     0x13bee, 0x1ef50, 0x1f7ac, 0x1ef48, 0x1f7a6, 0x1ef44, 0x1ef42, 0x1ce50,
-     0x1e72c, 0x1ded0, 0x1ef6c, 0x1e726, 0x1dec8, 0x1ef66, 0x1dec4, 0x1ce42,
-     0x1dec2, 0x18c50, 0x1c62c, 0x19cd0, 0x18c48, 0x1c626, 0x1bdd0, 0x19cc8,
-     0x1ce66, 0x1bdc8, 0x1dee6, 0x18c42, 0x1bdc4, 0x19cc2, 0x1bdc2, 0x10850,
-     0x1842c, 0x118d0, 0x10848, 0x18426, 0x139d0, 0x118c8, 0x18c66, 0x17bd0,
-     0x139c8, 0x19ce6, 0x10842, 0x17bc8, 0x1bde6, 0x118c2, 0x17bc4, 0x1086c,
-     0x118ec, 0x10866, 0x139ec, 0x118e6, 0x17bec, 0x139e6, 0x17be6, 0x1ef28,
-     0x1f796, 0x1ef24, 0x1ef22, 0x1ce28, 0x1e716, 0x1de68, 0x1ef36, 0x1de64,
-     0x1ce22, 0x1de62, 0x18c28, 0x1c616, 0x19c68, 0x18c24, 0x1bce8, 0x19c64,
-     0x18c22, 0x1bce4, 0x19c62, 0x1bce2, 0x10828, 0x18416, 0x11868, 0x18c36,
-     0x138e8, 0x11864, 0x10822, 0x179e8, 0x138e4, 0x11862, 0x179e4, 0x138e2,
-     0x179e2, 0x11876, 0x179f6, 0x1ef12, 0x1de34, 0x1de32, 0x19c34, 0x1bc74,
-     0x1bc72, 0x11834, 0x13874, 0x178f4, 0x178f2, 0x10540, 0x10520, 0x18298,
-     0x10510, 0x10508, 0x10504, 0x105b0, 0x10598, 0x1058c, 0x10586, 0x105dc,
-     0x105ce, 0x186a0, 0x18690, 0x1c34c, 0x18688, 0x1c346, 0x18684, 0x18682,
-     0x104a0, 0x18258, 0x10da0, 0x186d8, 0x1824c, 0x10d90, 0x186cc, 0x10d88,
-     0x186c6, 0x10d84, 0x10482, 0x10d82, 0x104d8, 0x1826e, 0x10dd8, 0x186ee,
-     0x10dcc, 0x104c6, 0x10dc6, 0x104ee, 0x10dee, 0x1c750, 0x1c748, 0x1c744,
-     0x1c742, 0x18650, 0x18ed0, 0x1c76c, 0x1c326, 0x18ec8, 0x1c766, 0x18ec4,
-     0x18642, 0x18ec2, 0x10450, 0x10cd0, 0x10448, 0x18226, 0x11dd0, 0x10cc8,
-     0x10444, 0x11dc8, 0x10cc4, 0x10442, 0x11dc4, 0x10cc2, 0x1046c, 0x10cec,
-     0x10466, 0x11dec, 0x10ce6, 0x11de6, 0x1e7a8, 0x1e7a4, 0x1e7a2, 0x1c728,
-     0x1cf68, 0x1e7b6, 0x1cf64, 0x1c722, 0x1cf62, 0x18628, 0x1c316, 0x18e68,
-     0x1c736, 0x19ee8, 0x18e64, 0x18622, 0x19ee4, 0x18e62, 0x19ee2, 0x10428,
-     0x18216, 0x10c68, 0x18636, 0x11ce8, 0x10c64, 0x10422, 0x13de8, 0x11ce4,
-     0x10c62, 0x13de4, 0x11ce2, 0x10436, 0x10c76, 0x11cf6, 0x13df6, 0x1f7d4,
-     0x1f7d2, 0x1e794, 0x1efb4, 0x1e792, 0x1efb2, 0x1c714, 0x1cf34, 0x1c712,
-     0x1df74, 0x1cf32, 0x1df72, 0x18614, 0x18e34, 0x18612, 0x19e74, 0x18e32,
-     0x1bef4},
-    {0x1f560, 0x1fab8, 0x1ea40, 0x1f530, 0x1fa9c, 0x1ea20, 0x1f518, 0x1fa8e,
-     0x1ea10, 0x1f50c, 0x1ea08, 0x1f506, 0x1ea04, 0x1eb60, 0x1f5b8, 0x1fade,
-     0x1d640, 0x1eb30, 0x1f59c, 0x1d620, 0x1eb18, 0x1f58e, 0x1d610, 0x1eb0c,
-     0x1d608, 0x1eb06, 0x1d604, 0x1d760, 0x1ebb8, 0x1f5de, 0x1ae40, 0x1d730,
-     0x1eb9c, 0x1ae20, 0x1d718, 0x1eb8e, 0x1ae10, 0x1d70c, 0x1ae08, 0x1d706,
-     0x1ae04, 0x1af60, 0x1d7b8, 0x1ebde, 0x15e40, 0x1af30, 0x1d79c, 0x15e20,
-     0x1af18, 0x1d78e, 0x15e10, 0x1af0c, 0x15e08, 0x1af06, 0x15f60, 0x1afb8,
-     0x1d7de, 0x15f30, 0x1af9c, 0x15f18, 0x1af8e, 0x15f0c, 0x15fb8, 0x1afde,
-     0x15f9c, 0x15f8e, 0x1e940, 0x1f4b0, 0x1fa5c, 0x1e920, 0x1f498, 0x1fa4e,
-     0x1e910, 0x1f48c, 0x1e908, 0x1f486, 0x1e904, 0x1e902, 0x1d340, 0x1e9b0,
-     0x1f4dc, 0x1d320, 0x1e998, 0x1f4ce, 0x1d310, 0x1e98c, 0x1d308, 0x1e986,
-     0x1d304, 0x1d302, 0x1a740, 0x1d3b0, 0x1e9dc, 0x1a720, 0x1d398, 0x1e9ce,
-     0x1a710, 0x1d38c, 0x1a708, 0x1d386, 0x1a704, 0x1a702, 0x14f40, 0x1a7b0,
-     0x1d3dc, 0x14f20, 0x1a798, 0x1d3ce, 0x14f10, 0x1a78c, 0x14f08, 0x1a786,
-     0x14f04, 0x14fb0, 0x1a7dc, 0x14f98, 0x1a7ce, 0x14f8c, 0x14f86, 0x14fdc,
-     0x14fce, 0x1e8a0, 0x1f458, 0x1fa2e, 0x1e890, 0x1f44c, 0x1e888, 0x1f446,
-     0x1e884, 0x1e882, 0x1d1a0, 0x1e8d8, 0x1f46e, 0x1d190, 0x1e8cc, 0x1d188,
-     0x1e8c6, 0x1d184, 0x1d182, 0x1a3a0, 0x1d1d8, 0x1e8ee, 0x1a390, 0x1d1cc,
-     0x1a388, 0x1d1c6, 0x1a384, 0x1a382, 0x147a0, 0x1a3d8, 0x1d1ee, 0x14790,
-     0x1a3cc, 0x14788, 0x1a3c6, 0x14784, 0x14782, 0x147d8, 0x1a3ee, 0x147cc,
-     0x147c6, 0x147ee, 0x1e850, 0x1f42c, 0x1e848, 0x1f426, 0x1e844, 0x1e842,
-     0x1d0d0, 0x1e86c, 0x1d0c8, 0x1e866, 0x1d0c4, 0x1d0c2, 0x1a1d0, 0x1d0ec,
-     0x1a1c8, 0x1d0e6, 0x1a1c4, 0x1a1c2, 0x143d0, 0x1a1ec, 0x143c8, 0x1a1e6,
-     0x143c4, 0x143c2, 0x143ec, 0x143e6, 0x1e828, 0x1f416, 0x1e824, 0x1e822,
-     0x1d068, 0x1e836, 0x1d064, 0x1d062, 0x1a0e8, 0x1d076, 0x1a0e4, 0x1a0e2,
-     0x141e8, 0x1a0f6, 0x141e4, 0x141e2, 0x1e814, 0x1e812, 0x1d034, 0x1d032,
-     0x1a074, 0x1a072, 0x1e540, 0x1f2b0, 0x1f95c, 0x1e520, 0x1f298, 0x1f94e,
-     0x1e510, 0x1f28c, 0x1e508, 0x1f286, 0x1e504, 0x1e502, 0x1cb40, 0x1e5b0,
-     0x1f2dc, 0x1cb20, 0x1e598, 0x1f2ce, 0x1cb10, 0x1e58c, 0x1cb08, 0x1e586,
-     0x1cb04, 0x1cb02, 0x19740, 0x1cbb0, 0x1e5dc, 0x19720, 0x1cb98, 0x1e5ce,
-     0x19710, 0x1cb8c, 0x19708, 0x1cb86, 0x19704, 0x19702, 0x12f40, 0x197b0,
-     0x1cbdc, 0x12f20, 0x19798, 0x1cbce, 0x12f10, 0x1978c, 0x12f08, 0x19786,
-     0x12f04, 0x12fb0, 0x197dc, 0x12f98, 0x197ce, 0x12f8c, 0x12f86, 0x12fdc,
-     0x12fce, 0x1f6a0, 0x1fb58, 0x16bf0, 0x1f690, 0x1fb4c, 0x169f8, 0x1f688,
-     0x1fb46, 0x168fc, 0x1f684, 0x1f682, 0x1e4a0, 0x1f258, 0x1f92e, 0x1eda0,
-     0x1e490, 0x1fb6e, 0x1ed90, 0x1f6cc, 0x1f246, 0x1ed88, 0x1e484, 0x1ed84,
-     0x1e482, 0x1ed82, 0x1c9a0, 0x1e4d8, 0x1f26e, 0x1dba0, 0x1c990, 0x1e4cc,
-     0x1db90, 0x1edcc, 0x1e4c6, 0x1db88, 0x1c984, 0x1db84, 0x1c982, 0x1db82,
-     0x193a0, 0x1c9d8, 0x1e4ee, 0x1b7a0, 0x19390, 0x1c9cc, 0x1b790, 0x1dbcc,
-     0x1c9c6, 0x1b788, 0x19384, 0x1b784, 0x19382, 0x1b782, 0x127a0, 0x193d8,
-     0x1c9ee, 0x16fa0, 0x12790, 0x193cc, 0x16f90, 0x1b7cc, 0x193c6, 0x16f88,
-     0x12784, 0x16f84, 0x12782, 0x127d8, 0x193ee, 0x16fd8, 0x127cc, 0x16fcc,
-     0x127c6, 0x16fc6, 0x127ee, 0x1f650, 0x1fb2c, 0x165f8, 0x1f648, 0x1fb26,
-     0x164fc, 0x1f644, 0x1647e, 0x1f642, 0x1e450, 0x1f22c, 0x1ecd0, 0x1e448,
-     0x1f226, 0x1ecc8, 0x1f666, 0x1ecc4, 0x1e442, 0x1ecc2, 0x1c8d0, 0x1e46c,
-     0x1d9d0, 0x1c8c8, 0x1e466, 0x1d9c8, 0x1ece6, 0x1d9c4, 0x1c8c2, 0x1d9c2,
-     0x191d0, 0x1c8ec, 0x1b3d0, 0x191c8, 0x1c8e6, 0x1b3c8, 0x1d9e6, 0x1b3c4,
-     0x191c2, 0x1b3c2, 0x123d0, 0x191ec, 0x167d0, 0x123c8, 0x191e6, 0x167c8,
-     0x1b3e6, 0x167c4, 0x123c2, 0x167c2, 0x123ec, 0x167ec, 0x123e6, 0x167e6,
-     0x1f628, 0x1fb16, 0x162fc, 0x1f624, 0x1627e, 0x1f622, 0x1e428, 0x1f216,
-     0x1ec68, 0x1f636, 0x1ec64, 0x1e422, 0x1ec62, 0x1c868, 0x1e436, 0x1d8e8,
-     0x1c864, 0x1d8e4, 0x1c862, 0x1d8e2, 0x190e8, 0x1c876, 0x1b1e8, 0x1d8f6,
-     0x1b1e4, 0x190e2, 0x1b1e2, 0x121e8, 0x190f6, 0x163e8, 0x121e4, 0x163e4,
-     0x121e2, 0x163e2, 0x121f6, 0x163f6, 0x1f614, 0x1617e, 0x1f612, 0x1e414,
-     0x1ec34, 0x1e412, 0x1ec32, 0x1c834, 0x1d874, 0x1c832, 0x1d872, 0x19074,
-     0x1b0f4, 0x19072, 0x1b0f2, 0x120f4, 0x161f4, 0x120f2, 0x161f2, 0x1f60a,
-     0x1e40a, 0x1ec1a, 0x1c81a, 0x1d83a, 0x1903a, 0x1b07a, 0x1e2a0, 0x1f158,
-     0x1f8ae, 0x1e290, 0x1f14c, 0x1e288, 0x1f146, 0x1e284, 0x1e282, 0x1c5a0,
-     0x1e2d8, 0x1f16e, 0x1c590, 0x1e2cc, 0x1c588, 0x1e2c6, 0x1c584, 0x1c582,
-     0x18ba0, 0x1c5d8, 0x1e2ee, 0x18b90, 0x1c5cc, 0x18b88, 0x1c5c6, 0x18b84,
-     0x18b82, 0x117a0, 0x18bd8, 0x1c5ee, 0x11790, 0x18bcc, 0x11788, 0x18bc6,
-     0x11784, 0x11782, 0x117d8, 0x18bee, 0x117cc, 0x117c6, 0x117ee, 0x1f350,
-     0x1f9ac, 0x135f8, 0x1f348, 0x1f9a6, 0x134fc, 0x1f344, 0x1347e, 0x1f342,
-     0x1e250, 0x1f12c, 0x1e6d0, 0x1e248, 0x1f126, 0x1e6c8, 0x1f366, 0x1e6c4,
-     0x1e242, 0x1e6c2, 0x1c4d0, 0x1e26c, 0x1cdd0, 0x1c4c8, 0x1e266, 0x1cdc8,
-     0x1e6e6, 0x1cdc4, 0x1c4c2, 0x1cdc2, 0x189d0, 0x1c4ec, 0x19bd0, 0x189c8,
-     0x1c4e6, 0x19bc8, 0x1cde6, 0x19bc4, 0x189c2, 0x19bc2, 0x113d0, 0x189ec,
-     0x137d0, 0x113c8, 0x189e6, 0x137c8, 0x19be6, 0x137c4, 0x113c2, 0x137c2,
-     0x113ec, 0x137ec, 0x113e6, 0x137e6, 0x1fba8, 0x175f0, 0x1bafc, 0x1fba4,
-     0x174f8, 0x1ba7e, 0x1fba2, 0x1747c, 0x1743e, 0x1f328, 0x1f996, 0x132fc,
-     0x1f768, 0x1fbb6, 0x176fc, 0x1327e, 0x1f764, 0x1f322, 0x1767e, 0x1f762,
-     0x1e228, 0x1f116, 0x1e668, 0x1e224, 0x1eee8, 0x1f776, 0x1e222, 0x1eee4,
-     0x1e662, 0x1eee2, 0x1c468, 0x1e236, 0x1cce8, 0x1c464, 0x1dde8, 0x1cce4,
-     0x1c462, 0x1dde4, 0x1cce2, 0x1dde2, 0x188e8, 0x1c476, 0x199e8, 0x188e4,
-     0x1bbe8, 0x199e4, 0x188e2, 0x1bbe4, 0x199e2, 0x1bbe2, 0x111e8, 0x188f6,
-     0x133e8, 0x111e4, 0x177e8, 0x133e4, 0x111e2, 0x177e4, 0x133e2, 0x177e2,
-     0x111f6, 0x133f6, 0x1fb94, 0x172f8, 0x1b97e, 0x1fb92, 0x1727c, 0x1723e,
-     0x1f314, 0x1317e, 0x1f734, 0x1f312, 0x1737e, 0x1f732, 0x1e214, 0x1e634,
-     0x1e212, 0x1ee74, 0x1e632, 0x1ee72, 0x1c434, 0x1cc74, 0x1c432, 0x1dcf4,
-     0x1cc72, 0x1dcf2, 0x18874, 0x198f4, 0x18872, 0x1b9f4, 0x198f2, 0x1b9f2,
-     0x110f4, 0x131f4, 0x110f2, 0x173f4, 0x131f2, 0x173f2, 0x1fb8a, 0x1717c,
-     0x1713e, 0x1f30a, 0x1f71a, 0x1e20a, 0x1e61a, 0x1ee3a, 0x1c41a, 0x1cc3a,
-     0x1dc7a, 0x1883a, 0x1987a, 0x1b8fa, 0x1107a, 0x130fa, 0x171fa, 0x170be,
-     0x1e150, 0x1f0ac, 0x1e148, 0x1f0a6, 0x1e144, 0x1e142, 0x1c2d0, 0x1e16c,
-     0x1c2c8, 0x1e166, 0x1c2c4, 0x1c2c2, 0x185d0, 0x1c2ec, 0x185c8, 0x1c2e6,
-     0x185c4, 0x185c2, 0x10bd0, 0x185ec, 0x10bc8, 0x185e6, 0x10bc4, 0x10bc2,
-     0x10bec, 0x10be6, 0x1f1a8, 0x1f8d6, 0x11afc, 0x1f1a4, 0x11a7e, 0x1f1a2,
-     0x1e128, 0x1f096, 0x1e368, 0x1e124, 0x1e364, 0x1e122, 0x1e362, 0x1c268,
-     0x1e136, 0x1c6e8, 0x1c264, 0x1c6e4, 0x1c262, 0x1c6e2, 0x184e8, 0x1c276,
-     0x18de8, 0x184e4, 0x18de4, 0x184e2, 0x18de2, 0x109e8, 0x184f6, 0x11be8,
-     0x109e4, 0x11be4, 0x109e2, 0x11be2, 0x109f6, 0x11bf6, 0x1f9d4, 0x13af8,
-     0x19d7e, 0x1f9d2, 0x13a7c, 0x13a3e, 0x1f194, 0x1197e, 0x1f3b4, 0x1f192,
-     0x13b7e, 0x1f3b2, 0x1e114, 0x1e334, 0x1e112, 0x1e774, 0x1e332, 0x1e772,
-     0x1c234, 0x1c674, 0x1c232, 0x1cef4, 0x1c672, 0x1cef2, 0x18474, 0x18cf4,
-     0x18472, 0x19df4, 0x18cf2, 0x19df2, 0x108f4, 0x119f4, 0x108f2, 0x13bf4,
-     0x119f2, 0x13bf2, 0x17af0, 0x1bd7c, 0x17a78, 0x1bd3e, 0x17a3c, 0x17a1e,
-     0x1f9ca, 0x1397c, 0x1fbda, 0x17b7c, 0x1393e, 0x17b3e, 0x1f18a, 0x1f39a,
-     0x1f7ba, 0x1e10a, 0x1e31a, 0x1e73a, 0x1ef7a, 0x1c21a, 0x1c63a, 0x1ce7a,
-     0x1defa, 0x1843a, 0x18c7a, 0x19cfa, 0x1bdfa, 0x1087a, 0x118fa, 0x139fa,
-     0x17978, 0x1bcbe, 0x1793c, 0x1791e, 0x138be, 0x179be, 0x178bc, 0x1789e,
-     0x1785e, 0x1e0a8, 0x1e0a4, 0x1e0a2, 0x1c168, 0x1e0b6, 0x1c164, 0x1c162,
-     0x182e8, 0x1c176, 0x182e4, 0x182e2, 0x105e8, 0x182f6, 0x105e4, 0x105e2,
-     0x105f6, 0x1f0d4, 0x10d7e, 0x1f0d2, 0x1e094, 0x1e1b4, 0x1e092, 0x1e1b2,
-     0x1c134, 0x1c374, 0x1c132, 0x1c372, 0x18274, 0x186f4, 0x18272, 0x186f2,
-     0x104f4, 0x10df4, 0x104f2, 0x10df2, 0x1f8ea, 0x11d7c, 0x11d3e, 0x1f0ca,
-     0x1f1da, 0x1e08a, 0x1e19a, 0x1e3ba, 0x1c11a, 0x1c33a, 0x1c77a, 0x1823a,
-     0x1867a, 0x18efa, 0x1047a, 0x10cfa, 0x11dfa, 0x13d78, 0x19ebe, 0x13d3c,
-     0x13d1e, 0x11cbe, 0x13dbe, 0x17d70, 0x1bebc, 0x17d38, 0x1be9e, 0x17d1c,
-     0x17d0e, 0x13cbc, 0x17dbc, 0x13c9e, 0x17d9e, 0x17cb8, 0x1be5e, 0x17c9c,
-     0x17c8e, 0x13c5e, 0x17cde, 0x17c5c, 0x17c4e, 0x17c2e, 0x1c0b4, 0x1c0b2,
-     0x18174, 0x18172, 0x102f4, 0x102f2, 0x1e0da, 0x1c09a, 0x1c1ba, 0x1813a,
-     0x1837a, 0x1027a, 0x106fa, 0x10ebe, 0x11ebc, 0x11e9e, 0x13eb8, 0x19f5e,
-     0x13e9c, 0x13e8e, 0x11e5e, 0x13ede, 0x17eb0, 0x1bf5c, 0x17e98, 0x1bf4e,
-     0x17e8c, 0x17e86, 0x13e5c, 0x17edc, 0x13e4e, 0x17ece, 0x17e58, 0x1bf2e,
-     0x17e4c, 0x17e46, 0x13e2e, 0x17e6e, 0x17e2c, 0x17e26, 0x10f5e, 0x11f5c,
-     0x11f4e, 0x13f58, 0x19fae, 0x13f4c, 0x13f46, 0x11f2e, 0x13f6e, 0x13f2c,
-     0x13f26},
-    {0x1abe0, 0x1d5f8, 0x153c0, 0x1a9f0, 0x1d4fc, 0x151e0, 0x1a8f8, 0x1d47e,
-     0x150f0, 0x1a87c, 0x15078, 0x1fad0, 0x15be0, 0x1adf8, 0x1fac8, 0x159f0,
-     0x1acfc, 0x1fac4, 0x158f8, 0x1ac7e, 0x1fac2, 0x1587c, 0x1f5d0, 0x1faec,
-     0x15df8, 0x1f5c8, 0x1fae6, 0x15cfc, 0x1f5c4, 0x15c7e, 0x1f5c2, 0x1ebd0,
-     0x1f5ec, 0x1ebc8, 0x1f5e6, 0x1ebc4, 0x1ebc2, 0x1d7d0, 0x1ebec, 0x1d7c8,
-     0x1ebe6, 0x1d7c4, 0x1d7c2, 0x1afd0, 0x1d7ec, 0x1afc8, 0x1d7e6, 0x1afc4,
-     0x14bc0, 0x1a5f0, 0x1d2fc, 0x149e0, 0x1a4f8, 0x1d27e, 0x148f0, 0x1a47c,
-     0x14878, 0x1a43e, 0x1483c, 0x1fa68, 0x14df0, 0x1a6fc, 0x1fa64, 0x14cf8,
-     0x1a67e, 0x1fa62, 0x14c7c, 0x14c3e, 0x1f4e8, 0x1fa76, 0x14efc, 0x1f4e4,
-     0x14e7e, 0x1f4e2, 0x1e9e8, 0x1f4f6, 0x1e9e4, 0x1e9e2, 0x1d3e8, 0x1e9f6,
-     0x1d3e4, 0x1d3e2, 0x1a7e8, 0x1d3f6, 0x1a7e4, 0x1a7e2, 0x145e0, 0x1a2f8,
-     0x1d17e, 0x144f0, 0x1a27c, 0x14478, 0x1a23e, 0x1443c, 0x1441e, 0x1fa34,
-     0x146f8, 0x1a37e, 0x1fa32, 0x1467c, 0x1463e, 0x1f474, 0x1477e, 0x1f472,
-     0x1e8f4, 0x1e8f2, 0x1d1f4, 0x1d1f2, 0x1a3f4, 0x1a3f2, 0x142f0, 0x1a17c,
-     0x14278, 0x1a13e, 0x1423c, 0x1421e, 0x1fa1a, 0x1437c, 0x1433e, 0x1f43a,
-     0x1e87a, 0x1d0fa, 0x14178, 0x1a0be, 0x1413c, 0x1411e, 0x141be, 0x140bc,
-     0x1409e, 0x12bc0, 0x195f0, 0x1cafc, 0x129e0, 0x194f8, 0x1ca7e, 0x128f0,
-     0x1947c, 0x12878, 0x1943e, 0x1283c, 0x1f968, 0x12df0, 0x196fc, 0x1f964,
-     0x12cf8, 0x1967e, 0x1f962, 0x12c7c, 0x12c3e, 0x1f2e8, 0x1f976, 0x12efc,
-     0x1f2e4, 0x12e7e, 0x1f2e2, 0x1e5e8, 0x1f2f6, 0x1e5e4, 0x1e5e2, 0x1cbe8,
-     0x1e5f6, 0x1cbe4, 0x1cbe2, 0x197e8, 0x1cbf6, 0x197e4, 0x197e2, 0x1b5e0,
-     0x1daf8, 0x1ed7e, 0x169c0, 0x1b4f0, 0x1da7c, 0x168e0, 0x1b478, 0x1da3e,
-     0x16870, 0x1b43c, 0x16838, 0x1b41e, 0x1681c, 0x125e0, 0x192f8, 0x1c97e,
-     0x16de0, 0x124f0, 0x1927c, 0x16cf0, 0x1b67c, 0x1923e, 0x16c78, 0x1243c,
-     0x16c3c, 0x1241e, 0x16c1e, 0x1f934, 0x126f8, 0x1937e, 0x1fb74, 0x1f932,
-     0x16ef8, 0x1267c, 0x1fb72, 0x16e7c, 0x1263e, 0x16e3e, 0x1f274, 0x1277e,
-     0x1f6f4, 0x1f272, 0x16f7e, 0x1f6f2, 0x1e4f4, 0x1edf4, 0x1e4f2, 0x1edf2,
-     0x1c9f4, 0x1dbf4, 0x1c9f2, 0x1dbf2, 0x193f4, 0x193f2, 0x165c0, 0x1b2f0,
-     0x1d97c, 0x164e0, 0x1b278, 0x1d93e, 0x16470, 0x1b23c, 0x16438, 0x1b21e,
-     0x1641c, 0x1640e, 0x122f0, 0x1917c, 0x166f0, 0x12278, 0x1913e, 0x16678,
-     0x1b33e, 0x1663c, 0x1221e, 0x1661e, 0x1f91a, 0x1237c, 0x1fb3a, 0x1677c,
-     0x1233e, 0x1673e, 0x1f23a, 0x1f67a, 0x1e47a, 0x1ecfa, 0x1c8fa, 0x1d9fa,
-     0x191fa, 0x162e0, 0x1b178, 0x1d8be, 0x16270, 0x1b13c, 0x16238, 0x1b11e,
-     0x1621c, 0x1620e, 0x12178, 0x190be, 0x16378, 0x1213c, 0x1633c, 0x1211e,
-     0x1631e, 0x121be, 0x163be, 0x16170, 0x1b0bc, 0x16138, 0x1b09e, 0x1611c,
-     0x1610e, 0x120bc, 0x161bc, 0x1209e, 0x1619e, 0x160b8, 0x1b05e, 0x1609c,
-     0x1608e, 0x1205e, 0x160de, 0x1605c, 0x1604e, 0x115e0, 0x18af8, 0x1c57e,
-     0x114f0, 0x18a7c, 0x11478, 0x18a3e, 0x1143c, 0x1141e, 0x1f8b4, 0x116f8,
-     0x18b7e, 0x1f8b2, 0x1167c, 0x1163e, 0x1f174, 0x1177e, 0x1f172, 0x1e2f4,
-     0x1e2f2, 0x1c5f4, 0x1c5f2, 0x18bf4, 0x18bf2, 0x135c0, 0x19af0, 0x1cd7c,
-     0x134e0, 0x19a78, 0x1cd3e, 0x13470, 0x19a3c, 0x13438, 0x19a1e, 0x1341c,
-     0x1340e, 0x112f0, 0x1897c, 0x136f0, 0x11278, 0x1893e, 0x13678, 0x19b3e,
-     0x1363c, 0x1121e, 0x1361e, 0x1f89a, 0x1137c, 0x1f9ba, 0x1377c, 0x1133e,
-     0x1373e, 0x1f13a, 0x1f37a, 0x1e27a, 0x1e6fa, 0x1c4fa, 0x1cdfa, 0x189fa,
-     0x1bae0, 0x1dd78, 0x1eebe, 0x174c0, 0x1ba70, 0x1dd3c, 0x17460, 0x1ba38,
-     0x1dd1e, 0x17430, 0x1ba1c, 0x17418, 0x1ba0e, 0x1740c, 0x132e0, 0x19978,
-     0x1ccbe, 0x176e0, 0x13270, 0x1993c, 0x17670, 0x1bb3c, 0x1991e, 0x17638,
-     0x1321c, 0x1761c, 0x1320e, 0x1760e, 0x11178, 0x188be, 0x13378, 0x1113c,
-     0x17778, 0x1333c, 0x1111e, 0x1773c, 0x1331e, 0x1771e, 0x111be, 0x133be,
-     0x177be, 0x172c0, 0x1b970, 0x1dcbc, 0x17260, 0x1b938, 0x1dc9e, 0x17230,
-     0x1b91c, 0x17218, 0x1b90e, 0x1720c, 0x17206, 0x13170, 0x198bc, 0x17370,
-     0x13138, 0x1989e, 0x17338, 0x1b99e, 0x1731c, 0x1310e, 0x1730e, 0x110bc,
-     0x131bc, 0x1109e, 0x173bc, 0x1319e, 0x1739e, 0x17160, 0x1b8b8, 0x1dc5e,
-     0x17130, 0x1b89c, 0x17118, 0x1b88e, 0x1710c, 0x17106, 0x130b8, 0x1985e,
-     0x171b8, 0x1309c, 0x1719c, 0x1308e, 0x1718e, 0x1105e, 0x130de, 0x171de,
-     0x170b0, 0x1b85c, 0x17098, 0x1b84e, 0x1708c, 0x17086, 0x1305c, 0x170dc,
-     0x1304e, 0x170ce, 0x17058, 0x1b82e, 0x1704c, 0x17046, 0x1302e, 0x1706e,
-     0x1702c, 0x17026, 0x10af0, 0x1857c, 0x10a78, 0x1853e, 0x10a3c, 0x10a1e,
-     0x10b7c, 0x10b3e, 0x1f0ba, 0x1e17a, 0x1c2fa, 0x185fa, 0x11ae0, 0x18d78,
-     0x1c6be, 0x11a70, 0x18d3c, 0x11a38, 0x18d1e, 0x11a1c, 0x11a0e, 0x10978,
-     0x184be, 0x11b78, 0x1093c, 0x11b3c, 0x1091e, 0x11b1e, 0x109be, 0x11bbe,
-     0x13ac0, 0x19d70, 0x1cebc, 0x13a60, 0x19d38, 0x1ce9e, 0x13a30, 0x19d1c,
-     0x13a18, 0x19d0e, 0x13a0c, 0x13a06, 0x11970, 0x18cbc, 0x13b70, 0x11938,
-     0x18c9e, 0x13b38, 0x1191c, 0x13b1c, 0x1190e, 0x13b0e, 0x108bc, 0x119bc,
-     0x1089e, 0x13bbc, 0x1199e, 0x13b9e, 0x1bd60, 0x1deb8, 0x1ef5e, 0x17a40,
-     0x1bd30, 0x1de9c, 0x17a20, 0x1bd18, 0x1de8e, 0x17a10, 0x1bd0c, 0x17a08,
-     0x1bd06, 0x17a04, 0x13960, 0x19cb8, 0x1ce5e, 0x17b60, 0x13930, 0x19c9c,
-     0x17b30, 0x1bd9c, 0x19c8e, 0x17b18, 0x1390c, 0x17b0c, 0x13906, 0x17b06,
-     0x118b8, 0x18c5e, 0x139b8, 0x1189c, 0x17bb8, 0x1399c, 0x1188e, 0x17b9c,
-     0x1398e, 0x17b8e, 0x1085e, 0x118de, 0x139de, 0x17bde, 0x17940, 0x1bcb0,
-     0x1de5c, 0x17920, 0x1bc98, 0x1de4e, 0x17910, 0x1bc8c, 0x17908, 0x1bc86,
-     0x17904, 0x17902, 0x138b0, 0x19c5c, 0x179b0, 0x13898, 0x19c4e, 0x17998,
-     0x1bcce, 0x1798c, 0x13886, 0x17986, 0x1185c, 0x138dc, 0x1184e, 0x179dc,
-     0x138ce, 0x179ce, 0x178a0, 0x1bc58, 0x1de2e, 0x17890, 0x1bc4c, 0x17888,
-     0x1bc46, 0x17884, 0x17882, 0x13858, 0x19c2e, 0x178d8, 0x1384c, 0x178cc,
-     0x13846, 0x178c6, 0x1182e, 0x1386e, 0x178ee, 0x17850, 0x1bc2c, 0x17848,
-     0x1bc26, 0x17844, 0x17842, 0x1382c, 0x1786c, 0x13826, 0x17866, 0x17828,
-     0x1bc16, 0x17824, 0x17822, 0x13816, 0x17836, 0x10578, 0x182be, 0x1053c,
-     0x1051e, 0x105be, 0x10d70, 0x186bc, 0x10d38, 0x1869e, 0x10d1c, 0x10d0e,
-     0x104bc, 0x10dbc, 0x1049e, 0x10d9e, 0x11d60, 0x18eb8, 0x1c75e, 0x11d30,
-     0x18e9c, 0x11d18, 0x18e8e, 0x11d0c, 0x11d06, 0x10cb8, 0x1865e, 0x11db8,
-     0x10c9c, 0x11d9c, 0x10c8e, 0x11d8e, 0x1045e, 0x10cde, 0x11dde, 0x13d40,
-     0x19eb0, 0x1cf5c, 0x13d20, 0x19e98, 0x1cf4e, 0x13d10, 0x19e8c, 0x13d08,
-     0x19e86, 0x13d04, 0x13d02, 0x11cb0, 0x18e5c, 0x13db0, 0x11c98, 0x18e4e,
-     0x13d98, 0x19ece, 0x13d8c, 0x11c86, 0x13d86, 0x10c5c, 0x11cdc, 0x10c4e,
-     0x13ddc, 0x11cce, 0x13dce, 0x1bea0, 0x1df58, 0x1efae, 0x1be90, 0x1df4c,
-     0x1be88, 0x1df46, 0x1be84, 0x1be82, 0x13ca0, 0x19e58, 0x1cf2e, 0x17da0,
-     0x13c90, 0x19e4c, 0x17d90, 0x1becc, 0x19e46, 0x17d88, 0x13c84, 0x17d84,
-     0x13c82, 0x17d82, 0x11c58, 0x18e2e, 0x13cd8, 0x11c4c, 0x17dd8, 0x13ccc,
-     0x11c46, 0x17dcc, 0x13cc6, 0x17dc6, 0x10c2e, 0x11c6e, 0x13cee, 0x17dee,
-     0x1be50, 0x1df2c, 0x1be48, 0x1df26, 0x1be44, 0x1be42, 0x13c50, 0x19e2c,
-     0x17cd0, 0x13c48, 0x19e26, 0x17cc8, 0x1be66, 0x17cc4, 0x13c42, 0x17cc2,
-     0x11c2c, 0x13c6c, 0x11c26, 0x17cec, 0x13c66, 0x17ce6, 0x1be28, 0x1df16,
-     0x1be24, 0x1be22, 0x13c28, 0x19e16, 0x17c68, 0x13c24, 0x17c64, 0x13c22,
-     0x17c62, 0x11c16, 0x13c36, 0x17c76, 0x1be14, 0x1be12, 0x13c14, 0x17c34,
-     0x13c12, 0x17c32, 0x102bc, 0x1029e, 0x106b8, 0x1835e, 0x1069c, 0x1068e,
-     0x1025e, 0x106de, 0x10eb0, 0x1875c, 0x10e98, 0x1874e, 0x10e8c, 0x10e86,
-     0x1065c, 0x10edc, 0x1064e, 0x10ece, 0x11ea0, 0x18f58, 0x1c7ae, 0x11e90,
-     0x18f4c, 0x11e88, 0x18f46, 0x11e84, 0x11e82, 0x10e58, 0x1872e, 0x11ed8,
-     0x18f6e, 0x11ecc, 0x10e46, 0x11ec6, 0x1062e, 0x10e6e, 0x11eee, 0x19f50,
-     0x1cfac, 0x19f48, 0x1cfa6, 0x19f44, 0x19f42, 0x11e50, 0x18f2c, 0x13ed0,
-     0x19f6c, 0x18f26, 0x13ec8, 0x11e44, 0x13ec4, 0x11e42, 0x13ec2, 0x10e2c,
-     0x11e6c, 0x10e26, 0x13eec, 0x11e66, 0x13ee6, 0x1dfa8, 0x1efd6, 0x1dfa4,
-     0x1dfa2, 0x19f28, 0x1cf96, 0x1bf68, 0x19f24, 0x1bf64, 0x19f22, 0x1bf62,
-     0x11e28, 0x18f16, 0x13e68, 0x11e24, 0x17ee8, 0x13e64, 0x11e22, 0x17ee4,
-     0x13e62, 0x17ee2, 0x10e16, 0x11e36, 0x13e76, 0x17ef6, 0x1df94, 0x1df92,
-     0x19f14, 0x1bf34, 0x19f12, 0x1bf32, 0x11e14, 0x13e34, 0x11e12, 0x17e74,
-     0x13e32, 0x17e72, 0x1df8a, 0x19f0a, 0x1bf1a, 0x11e0a, 0x13e1a, 0x17e3a,
-     0x1035c, 0x1034e, 0x10758, 0x183ae, 0x1074c, 0x10746, 0x1032e, 0x1076e,
-     0x10f50, 0x187ac, 0x10f48, 0x187a6, 0x10f44, 0x10f42, 0x1072c, 0x10f6c,
-     0x10726, 0x10f66, 0x18fa8, 0x1c7d6, 0x18fa4, 0x18fa2, 0x10f28, 0x18796,
-     0x11f68, 0x18fb6, 0x11f64, 0x10f22, 0x11f62, 0x10716, 0x10f36, 0x11f76,
-     0x1cfd4, 0x1cfd2, 0x18f94, 0x19fb4, 0x18f92, 0x19fb2, 0x10f14, 0x11f34,
-     0x10f12, 0x13f74, 0x11f32, 0x13f72, 0x1cfca, 0x18f8a, 0x19f9a, 0x10f0a,
-     0x11f1a, 0x13f3a, 0x103ac, 0x103a6, 0x107a8, 0x183d6, 0x107a4, 0x107a2,
-     0x10396, 0x107b6, 0x187d4, 0x187d2, 0x10794, 0x10fb4, 0x10792, 0x10fb2,
-     0x1c7ea}};
+namespace {
 
-CBC_PDF417::CBC_PDF417() : CBC_PDF417(false) {}
+const uint16_t g_CodewordTable[3][929] = {
+    {0xd5c0, 0xeaf0, 0xf57c, 0xd4e0, 0xea78, 0xf53e, 0xa8c0, 0xd470, 0xa860,
+     0x5040, 0xa830, 0x5020, 0xadc0, 0xd6f0, 0xeb7c, 0xace0, 0xd678, 0xeb3e,
+     0x58c0, 0xac70, 0x5860, 0x5dc0, 0xaef0, 0xd77c, 0x5ce0, 0xae78, 0xd73e,
+     0x5c70, 0xae3c, 0x5ef0, 0xaf7c, 0x5e78, 0xaf3e, 0x5f7c, 0xf5fa, 0xd2e0,
+     0xe978, 0xf4be, 0xa4c0, 0xd270, 0xe93c, 0xa460, 0xd238, 0x4840, 0xa430,
+     0xd21c, 0x4820, 0xa418, 0x4810, 0xa6e0, 0xd378, 0xe9be, 0x4cc0, 0xa670,
+     0xd33c, 0x4c60, 0xa638, 0xd31e, 0x4c30, 0xa61c, 0x4ee0, 0xa778, 0xd3be,
+     0x4e70, 0xa73c, 0x4e38, 0xa71e, 0x4f78, 0xa7be, 0x4f3c, 0x4f1e, 0xa2c0,
+     0xd170, 0xe8bc, 0xa260, 0xd138, 0xe89e, 0x4440, 0xa230, 0xd11c, 0x4420,
+     0xa218, 0x4410, 0x4408, 0x46c0, 0xa370, 0xd1bc, 0x4660, 0xa338, 0xd19e,
+     0x4630, 0xa31c, 0x4618, 0x460c, 0x4770, 0xa3bc, 0x4738, 0xa39e, 0x471c,
+     0x47bc, 0xa160, 0xd0b8, 0xe85e, 0x4240, 0xa130, 0xd09c, 0x4220, 0xa118,
+     0xd08e, 0x4210, 0xa10c, 0x4208, 0xa106, 0x4360, 0xa1b8, 0xd0de, 0x4330,
+     0xa19c, 0x4318, 0xa18e, 0x430c, 0x4306, 0xa1de, 0x438e, 0x4140, 0xa0b0,
+     0xd05c, 0x4120, 0xa098, 0xd04e, 0x4110, 0xa08c, 0x4108, 0xa086, 0x4104,
+     0x41b0, 0x4198, 0x418c, 0x40a0, 0xd02e, 0xa04c, 0xa046, 0x4082, 0xcae0,
+     0xe578, 0xf2be, 0x94c0, 0xca70, 0xe53c, 0x9460, 0xca38, 0xe51e, 0x2840,
+     0x9430, 0x2820, 0x96e0, 0xcb78, 0xe5be, 0x2cc0, 0x9670, 0xcb3c, 0x2c60,
+     0x9638, 0x2c30, 0x2c18, 0x2ee0, 0x9778, 0xcbbe, 0x2e70, 0x973c, 0x2e38,
+     0x2e1c, 0x2f78, 0x97be, 0x2f3c, 0x2fbe, 0xdac0, 0xed70, 0xf6bc, 0xda60,
+     0xed38, 0xf69e, 0xb440, 0xda30, 0xed1c, 0xb420, 0xda18, 0xed0e, 0xb410,
+     0xda0c, 0x92c0, 0xc970, 0xe4bc, 0xb6c0, 0x9260, 0xc938, 0xe49e, 0xb660,
+     0xdb38, 0xed9e, 0x6c40, 0x2420, 0x9218, 0xc90e, 0x6c20, 0xb618, 0x6c10,
+     0x26c0, 0x9370, 0xc9bc, 0x6ec0, 0x2660, 0x9338, 0xc99e, 0x6e60, 0xb738,
+     0xdb9e, 0x6e30, 0x2618, 0x6e18, 0x2770, 0x93bc, 0x6f70, 0x2738, 0x939e,
+     0x6f38, 0xb79e, 0x6f1c, 0x27bc, 0x6fbc, 0x279e, 0x6f9e, 0xd960, 0xecb8,
+     0xf65e, 0xb240, 0xd930, 0xec9c, 0xb220, 0xd918, 0xec8e, 0xb210, 0xd90c,
+     0xb208, 0xb204, 0x9160, 0xc8b8, 0xe45e, 0xb360, 0x9130, 0xc89c, 0x6640,
+     0x2220, 0xd99c, 0xc88e, 0x6620, 0x2210, 0x910c, 0x6610, 0xb30c, 0x9106,
+     0x2204, 0x2360, 0x91b8, 0xc8de, 0x6760, 0x2330, 0x919c, 0x6730, 0xb39c,
+     0x918e, 0x6718, 0x230c, 0x2306, 0x23b8, 0x91de, 0x67b8, 0x239c, 0x679c,
+     0x238e, 0x678e, 0x67de, 0xb140, 0xd8b0, 0xec5c, 0xb120, 0xd898, 0xec4e,
+     0xb110, 0xd88c, 0xb108, 0xd886, 0xb104, 0xb102, 0x2140, 0x90b0, 0xc85c,
+     0x6340, 0x2120, 0x9098, 0xc84e, 0x6320, 0xb198, 0xd8ce, 0x6310, 0x2108,
+     0x9086, 0x6308, 0xb186, 0x6304, 0x21b0, 0x90dc, 0x63b0, 0x2198, 0x90ce,
+     0x6398, 0xb1ce, 0x638c, 0x2186, 0x6386, 0x63dc, 0x63ce, 0xb0a0, 0xd858,
+     0xec2e, 0xb090, 0xd84c, 0xb088, 0xd846, 0xb084, 0xb082, 0x20a0, 0x9058,
+     0xc82e, 0x61a0, 0x2090, 0x904c, 0x6190, 0xb0cc, 0x9046, 0x6188, 0x2084,
+     0x6184, 0x2082, 0x20d8, 0x61d8, 0x61cc, 0x61c6, 0xd82c, 0xd826, 0xb042,
+     0x902c, 0x2048, 0x60c8, 0x60c4, 0x60c2, 0x8ac0, 0xc570, 0xe2bc, 0x8a60,
+     0xc538, 0x1440, 0x8a30, 0xc51c, 0x1420, 0x8a18, 0x1410, 0x1408, 0x16c0,
+     0x8b70, 0xc5bc, 0x1660, 0x8b38, 0xc59e, 0x1630, 0x8b1c, 0x1618, 0x160c,
+     0x1770, 0x8bbc, 0x1738, 0x8b9e, 0x171c, 0x17bc, 0x179e, 0xcd60, 0xe6b8,
+     0xf35e, 0x9a40, 0xcd30, 0xe69c, 0x9a20, 0xcd18, 0xe68e, 0x9a10, 0xcd0c,
+     0x9a08, 0xcd06, 0x8960, 0xc4b8, 0xe25e, 0x9b60, 0x8930, 0xc49c, 0x3640,
+     0x1220, 0xcd9c, 0xc48e, 0x3620, 0x9b18, 0x890c, 0x3610, 0x1208, 0x3608,
+     0x1360, 0x89b8, 0xc4de, 0x3760, 0x1330, 0xcdde, 0x3730, 0x9b9c, 0x898e,
+     0x3718, 0x130c, 0x370c, 0x13b8, 0x89de, 0x37b8, 0x139c, 0x379c, 0x138e,
+     0x13de, 0x37de, 0xdd40, 0xeeb0, 0xf75c, 0xdd20, 0xee98, 0xf74e, 0xdd10,
+     0xee8c, 0xdd08, 0xee86, 0xdd04, 0x9940, 0xccb0, 0xe65c, 0xbb40, 0x9920,
+     0xeedc, 0xe64e, 0xbb20, 0xdd98, 0xeece, 0xbb10, 0x9908, 0xcc86, 0xbb08,
+     0xdd86, 0x9902, 0x1140, 0x88b0, 0xc45c, 0x3340, 0x1120, 0x8898, 0xc44e,
+     0x7740, 0x3320, 0x9998, 0xccce, 0x7720, 0xbb98, 0xddce, 0x8886, 0x7710,
+     0x3308, 0x9986, 0x7708, 0x1102, 0x11b0, 0x88dc, 0x33b0, 0x1198, 0x88ce,
+     0x77b0, 0x3398, 0x99ce, 0x7798, 0xbbce, 0x1186, 0x3386, 0x11dc, 0x33dc,
+     0x11ce, 0x77dc, 0x33ce, 0xdca0, 0xee58, 0xf72e, 0xdc90, 0xee4c, 0xdc88,
+     0xee46, 0xdc84, 0xdc82, 0x98a0, 0xcc58, 0xe62e, 0xb9a0, 0x9890, 0xee6e,
+     0xb990, 0xdccc, 0xcc46, 0xb988, 0x9884, 0xb984, 0x9882, 0xb982, 0x10a0,
+     0x8858, 0xc42e, 0x31a0, 0x1090, 0x884c, 0x73a0, 0x3190, 0x98cc, 0x8846,
+     0x7390, 0xb9cc, 0x1084, 0x7388, 0x3184, 0x1082, 0x3182, 0x10d8, 0x886e,
+     0x31d8, 0x10cc, 0x73d8, 0x31cc, 0x10c6, 0x73cc, 0x31c6, 0x10ee, 0x73ee,
+     0xdc50, 0xee2c, 0xdc48, 0xee26, 0xdc44, 0xdc42, 0x9850, 0xcc2c, 0xb8d0,
+     0x9848, 0xcc26, 0xb8c8, 0xdc66, 0xb8c4, 0x9842, 0xb8c2, 0x1050, 0x882c,
+     0x30d0, 0x1048, 0x8826, 0x71d0, 0x30c8, 0x9866, 0x71c8, 0xb8e6, 0x1042,
+     0x71c4, 0x30c2, 0x71c2, 0x30ec, 0x71ec, 0x71e6, 0xee16, 0xdc22, 0xcc16,
+     0x9824, 0x9822, 0x1028, 0x3068, 0x70e8, 0x1022, 0x3062, 0x8560, 0x0a40,
+     0x8530, 0x0a20, 0x8518, 0xc28e, 0x0a10, 0x850c, 0x0a08, 0x8506, 0x0b60,
+     0x85b8, 0xc2de, 0x0b30, 0x859c, 0x0b18, 0x858e, 0x0b0c, 0x0b06, 0x0bb8,
+     0x85de, 0x0b9c, 0x0b8e, 0x0bde, 0x8d40, 0xc6b0, 0xe35c, 0x8d20, 0xc698,
+     0x8d10, 0xc68c, 0x8d08, 0xc686, 0x8d04, 0x0940, 0x84b0, 0xc25c, 0x1b40,
+     0x0920, 0xc6dc, 0xc24e, 0x1b20, 0x8d98, 0xc6ce, 0x1b10, 0x0908, 0x8486,
+     0x1b08, 0x8d86, 0x0902, 0x09b0, 0x84dc, 0x1bb0, 0x0998, 0x84ce, 0x1b98,
+     0x8dce, 0x1b8c, 0x0986, 0x09dc, 0x1bdc, 0x09ce, 0x1bce, 0xcea0, 0xe758,
+     0xf3ae, 0xce90, 0xe74c, 0xce88, 0xe746, 0xce84, 0xce82, 0x8ca0, 0xc658,
+     0x9da0, 0x8c90, 0xc64c, 0x9d90, 0xcecc, 0xc646, 0x9d88, 0x8c84, 0x9d84,
+     0x8c82, 0x9d82, 0x08a0, 0x8458, 0x19a0, 0x0890, 0xc66e, 0x3ba0, 0x1990,
+     0x8ccc, 0x8446, 0x3b90, 0x9dcc, 0x0884, 0x3b88, 0x1984, 0x0882, 0x1982,
+     0x08d8, 0x846e, 0x19d8, 0x08cc, 0x3bd8, 0x19cc, 0x08c6, 0x3bcc, 0x19c6,
+     0x08ee, 0x19ee, 0x3bee, 0xef50, 0xf7ac, 0xef48, 0xf7a6, 0xef44, 0xef42,
+     0xce50, 0xe72c, 0xded0, 0xef6c, 0xe726, 0xdec8, 0xef66, 0xdec4, 0xce42,
+     0xdec2, 0x8c50, 0xc62c, 0x9cd0, 0x8c48, 0xc626, 0xbdd0, 0x9cc8, 0xce66,
+     0xbdc8, 0xdee6, 0x8c42, 0xbdc4, 0x9cc2, 0xbdc2, 0x0850, 0x842c, 0x18d0,
+     0x0848, 0x8426, 0x39d0, 0x18c8, 0x8c66, 0x7bd0, 0x39c8, 0x9ce6, 0x0842,
+     0x7bc8, 0xbde6, 0x18c2, 0x7bc4, 0x086c, 0x18ec, 0x0866, 0x39ec, 0x18e6,
+     0x7bec, 0x39e6, 0x7be6, 0xef28, 0xf796, 0xef24, 0xef22, 0xce28, 0xe716,
+     0xde68, 0xef36, 0xde64, 0xce22, 0xde62, 0x8c28, 0xc616, 0x9c68, 0x8c24,
+     0xbce8, 0x9c64, 0x8c22, 0xbce4, 0x9c62, 0xbce2, 0x0828, 0x8416, 0x1868,
+     0x8c36, 0x38e8, 0x1864, 0x0822, 0x79e8, 0x38e4, 0x1862, 0x79e4, 0x38e2,
+     0x79e2, 0x1876, 0x79f6, 0xef12, 0xde34, 0xde32, 0x9c34, 0xbc74, 0xbc72,
+     0x1834, 0x3874, 0x78f4, 0x78f2, 0x0540, 0x0520, 0x8298, 0x0510, 0x0508,
+     0x0504, 0x05b0, 0x0598, 0x058c, 0x0586, 0x05dc, 0x05ce, 0x86a0, 0x8690,
+     0xc34c, 0x8688, 0xc346, 0x8684, 0x8682, 0x04a0, 0x8258, 0x0da0, 0x86d8,
+     0x824c, 0x0d90, 0x86cc, 0x0d88, 0x86c6, 0x0d84, 0x0482, 0x0d82, 0x04d8,
+     0x826e, 0x0dd8, 0x86ee, 0x0dcc, 0x04c6, 0x0dc6, 0x04ee, 0x0dee, 0xc750,
+     0xc748, 0xc744, 0xc742, 0x8650, 0x8ed0, 0xc76c, 0xc326, 0x8ec8, 0xc766,
+     0x8ec4, 0x8642, 0x8ec2, 0x0450, 0x0cd0, 0x0448, 0x8226, 0x1dd0, 0x0cc8,
+     0x0444, 0x1dc8, 0x0cc4, 0x0442, 0x1dc4, 0x0cc2, 0x046c, 0x0cec, 0x0466,
+     0x1dec, 0x0ce6, 0x1de6, 0xe7a8, 0xe7a4, 0xe7a2, 0xc728, 0xcf68, 0xe7b6,
+     0xcf64, 0xc722, 0xcf62, 0x8628, 0xc316, 0x8e68, 0xc736, 0x9ee8, 0x8e64,
+     0x8622, 0x9ee4, 0x8e62, 0x9ee2, 0x0428, 0x8216, 0x0c68, 0x8636, 0x1ce8,
+     0x0c64, 0x0422, 0x3de8, 0x1ce4, 0x0c62, 0x3de4, 0x1ce2, 0x0436, 0x0c76,
+     0x1cf6, 0x3df6, 0xf7d4, 0xf7d2, 0xe794, 0xefb4, 0xe792, 0xefb2, 0xc714,
+     0xcf34, 0xc712, 0xdf74, 0xcf32, 0xdf72, 0x8614, 0x8e34, 0x8612, 0x9e74,
+     0x8e32, 0xbef4},
+    {0xf560, 0xfab8, 0xea40, 0xf530, 0xfa9c, 0xea20, 0xf518, 0xfa8e, 0xea10,
+     0xf50c, 0xea08, 0xf506, 0xea04, 0xeb60, 0xf5b8, 0xfade, 0xd640, 0xeb30,
+     0xf59c, 0xd620, 0xeb18, 0xf58e, 0xd610, 0xeb0c, 0xd608, 0xeb06, 0xd604,
+     0xd760, 0xebb8, 0xf5de, 0xae40, 0xd730, 0xeb9c, 0xae20, 0xd718, 0xeb8e,
+     0xae10, 0xd70c, 0xae08, 0xd706, 0xae04, 0xaf60, 0xd7b8, 0xebde, 0x5e40,
+     0xaf30, 0xd79c, 0x5e20, 0xaf18, 0xd78e, 0x5e10, 0xaf0c, 0x5e08, 0xaf06,
+     0x5f60, 0xafb8, 0xd7de, 0x5f30, 0xaf9c, 0x5f18, 0xaf8e, 0x5f0c, 0x5fb8,
+     0xafde, 0x5f9c, 0x5f8e, 0xe940, 0xf4b0, 0xfa5c, 0xe920, 0xf498, 0xfa4e,
+     0xe910, 0xf48c, 0xe908, 0xf486, 0xe904, 0xe902, 0xd340, 0xe9b0, 0xf4dc,
+     0xd320, 0xe998, 0xf4ce, 0xd310, 0xe98c, 0xd308, 0xe986, 0xd304, 0xd302,
+     0xa740, 0xd3b0, 0xe9dc, 0xa720, 0xd398, 0xe9ce, 0xa710, 0xd38c, 0xa708,
+     0xd386, 0xa704, 0xa702, 0x4f40, 0xa7b0, 0xd3dc, 0x4f20, 0xa798, 0xd3ce,
+     0x4f10, 0xa78c, 0x4f08, 0xa786, 0x4f04, 0x4fb0, 0xa7dc, 0x4f98, 0xa7ce,
+     0x4f8c, 0x4f86, 0x4fdc, 0x4fce, 0xe8a0, 0xf458, 0xfa2e, 0xe890, 0xf44c,
+     0xe888, 0xf446, 0xe884, 0xe882, 0xd1a0, 0xe8d8, 0xf46e, 0xd190, 0xe8cc,
+     0xd188, 0xe8c6, 0xd184, 0xd182, 0xa3a0, 0xd1d8, 0xe8ee, 0xa390, 0xd1cc,
+     0xa388, 0xd1c6, 0xa384, 0xa382, 0x47a0, 0xa3d8, 0xd1ee, 0x4790, 0xa3cc,
+     0x4788, 0xa3c6, 0x4784, 0x4782, 0x47d8, 0xa3ee, 0x47cc, 0x47c6, 0x47ee,
+     0xe850, 0xf42c, 0xe848, 0xf426, 0xe844, 0xe842, 0xd0d0, 0xe86c, 0xd0c8,
+     0xe866, 0xd0c4, 0xd0c2, 0xa1d0, 0xd0ec, 0xa1c8, 0xd0e6, 0xa1c4, 0xa1c2,
+     0x43d0, 0xa1ec, 0x43c8, 0xa1e6, 0x43c4, 0x43c2, 0x43ec, 0x43e6, 0xe828,
+     0xf416, 0xe824, 0xe822, 0xd068, 0xe836, 0xd064, 0xd062, 0xa0e8, 0xd076,
+     0xa0e4, 0xa0e2, 0x41e8, 0xa0f6, 0x41e4, 0x41e2, 0xe814, 0xe812, 0xd034,
+     0xd032, 0xa074, 0xa072, 0xe540, 0xf2b0, 0xf95c, 0xe520, 0xf298, 0xf94e,
+     0xe510, 0xf28c, 0xe508, 0xf286, 0xe504, 0xe502, 0xcb40, 0xe5b0, 0xf2dc,
+     0xcb20, 0xe598, 0xf2ce, 0xcb10, 0xe58c, 0xcb08, 0xe586, 0xcb04, 0xcb02,
+     0x9740, 0xcbb0, 0xe5dc, 0x9720, 0xcb98, 0xe5ce, 0x9710, 0xcb8c, 0x9708,
+     0xcb86, 0x9704, 0x9702, 0x2f40, 0x97b0, 0xcbdc, 0x2f20, 0x9798, 0xcbce,
+     0x2f10, 0x978c, 0x2f08, 0x9786, 0x2f04, 0x2fb0, 0x97dc, 0x2f98, 0x97ce,
+     0x2f8c, 0x2f86, 0x2fdc, 0x2fce, 0xf6a0, 0xfb58, 0x6bf0, 0xf690, 0xfb4c,
+     0x69f8, 0xf688, 0xfb46, 0x68fc, 0xf684, 0xf682, 0xe4a0, 0xf258, 0xf92e,
+     0xeda0, 0xe490, 0xfb6e, 0xed90, 0xf6cc, 0xf246, 0xed88, 0xe484, 0xed84,
+     0xe482, 0xed82, 0xc9a0, 0xe4d8, 0xf26e, 0xdba0, 0xc990, 0xe4cc, 0xdb90,
+     0xedcc, 0xe4c6, 0xdb88, 0xc984, 0xdb84, 0xc982, 0xdb82, 0x93a0, 0xc9d8,
+     0xe4ee, 0xb7a0, 0x9390, 0xc9cc, 0xb790, 0xdbcc, 0xc9c6, 0xb788, 0x9384,
+     0xb784, 0x9382, 0xb782, 0x27a0, 0x93d8, 0xc9ee, 0x6fa0, 0x2790, 0x93cc,
+     0x6f90, 0xb7cc, 0x93c6, 0x6f88, 0x2784, 0x6f84, 0x2782, 0x27d8, 0x93ee,
+     0x6fd8, 0x27cc, 0x6fcc, 0x27c6, 0x6fc6, 0x27ee, 0xf650, 0xfb2c, 0x65f8,
+     0xf648, 0xfb26, 0x64fc, 0xf644, 0x647e, 0xf642, 0xe450, 0xf22c, 0xecd0,
+     0xe448, 0xf226, 0xecc8, 0xf666, 0xecc4, 0xe442, 0xecc2, 0xc8d0, 0xe46c,
+     0xd9d0, 0xc8c8, 0xe466, 0xd9c8, 0xece6, 0xd9c4, 0xc8c2, 0xd9c2, 0x91d0,
+     0xc8ec, 0xb3d0, 0x91c8, 0xc8e6, 0xb3c8, 0xd9e6, 0xb3c4, 0x91c2, 0xb3c2,
+     0x23d0, 0x91ec, 0x67d0, 0x23c8, 0x91e6, 0x67c8, 0xb3e6, 0x67c4, 0x23c2,
+     0x67c2, 0x23ec, 0x67ec, 0x23e6, 0x67e6, 0xf628, 0xfb16, 0x62fc, 0xf624,
+     0x627e, 0xf622, 0xe428, 0xf216, 0xec68, 0xf636, 0xec64, 0xe422, 0xec62,
+     0xc868, 0xe436, 0xd8e8, 0xc864, 0xd8e4, 0xc862, 0xd8e2, 0x90e8, 0xc876,
+     0xb1e8, 0xd8f6, 0xb1e4, 0x90e2, 0xb1e2, 0x21e8, 0x90f6, 0x63e8, 0x21e4,
+     0x63e4, 0x21e2, 0x63e2, 0x21f6, 0x63f6, 0xf614, 0x617e, 0xf612, 0xe414,
+     0xec34, 0xe412, 0xec32, 0xc834, 0xd874, 0xc832, 0xd872, 0x9074, 0xb0f4,
+     0x9072, 0xb0f2, 0x20f4, 0x61f4, 0x20f2, 0x61f2, 0xf60a, 0xe40a, 0xec1a,
+     0xc81a, 0xd83a, 0x903a, 0xb07a, 0xe2a0, 0xf158, 0xf8ae, 0xe290, 0xf14c,
+     0xe288, 0xf146, 0xe284, 0xe282, 0xc5a0, 0xe2d8, 0xf16e, 0xc590, 0xe2cc,
+     0xc588, 0xe2c6, 0xc584, 0xc582, 0x8ba0, 0xc5d8, 0xe2ee, 0x8b90, 0xc5cc,
+     0x8b88, 0xc5c6, 0x8b84, 0x8b82, 0x17a0, 0x8bd8, 0xc5ee, 0x1790, 0x8bcc,
+     0x1788, 0x8bc6, 0x1784, 0x1782, 0x17d8, 0x8bee, 0x17cc, 0x17c6, 0x17ee,
+     0xf350, 0xf9ac, 0x35f8, 0xf348, 0xf9a6, 0x34fc, 0xf344, 0x347e, 0xf342,
+     0xe250, 0xf12c, 0xe6d0, 0xe248, 0xf126, 0xe6c8, 0xf366, 0xe6c4, 0xe242,
+     0xe6c2, 0xc4d0, 0xe26c, 0xcdd0, 0xc4c8, 0xe266, 0xcdc8, 0xe6e6, 0xcdc4,
+     0xc4c2, 0xcdc2, 0x89d0, 0xc4ec, 0x9bd0, 0x89c8, 0xc4e6, 0x9bc8, 0xcde6,
+     0x9bc4, 0x89c2, 0x9bc2, 0x13d0, 0x89ec, 0x37d0, 0x13c8, 0x89e6, 0x37c8,
+     0x9be6, 0x37c4, 0x13c2, 0x37c2, 0x13ec, 0x37ec, 0x13e6, 0x37e6, 0xfba8,
+     0x75f0, 0xbafc, 0xfba4, 0x74f8, 0xba7e, 0xfba2, 0x747c, 0x743e, 0xf328,
+     0xf996, 0x32fc, 0xf768, 0xfbb6, 0x76fc, 0x327e, 0xf764, 0xf322, 0x767e,
+     0xf762, 0xe228, 0xf116, 0xe668, 0xe224, 0xeee8, 0xf776, 0xe222, 0xeee4,
+     0xe662, 0xeee2, 0xc468, 0xe236, 0xcce8, 0xc464, 0xdde8, 0xcce4, 0xc462,
+     0xdde4, 0xcce2, 0xdde2, 0x88e8, 0xc476, 0x99e8, 0x88e4, 0xbbe8, 0x99e4,
+     0x88e2, 0xbbe4, 0x99e2, 0xbbe2, 0x11e8, 0x88f6, 0x33e8, 0x11e4, 0x77e8,
+     0x33e4, 0x11e2, 0x77e4, 0x33e2, 0x77e2, 0x11f6, 0x33f6, 0xfb94, 0x72f8,
+     0xb97e, 0xfb92, 0x727c, 0x723e, 0xf314, 0x317e, 0xf734, 0xf312, 0x737e,
+     0xf732, 0xe214, 0xe634, 0xe212, 0xee74, 0xe632, 0xee72, 0xc434, 0xcc74,
+     0xc432, 0xdcf4, 0xcc72, 0xdcf2, 0x8874, 0x98f4, 0x8872, 0xb9f4, 0x98f2,
+     0xb9f2, 0x10f4, 0x31f4, 0x10f2, 0x73f4, 0x31f2, 0x73f2, 0xfb8a, 0x717c,
+     0x713e, 0xf30a, 0xf71a, 0xe20a, 0xe61a, 0xee3a, 0xc41a, 0xcc3a, 0xdc7a,
+     0x883a, 0x987a, 0xb8fa, 0x107a, 0x30fa, 0x71fa, 0x70be, 0xe150, 0xf0ac,
+     0xe148, 0xf0a6, 0xe144, 0xe142, 0xc2d0, 0xe16c, 0xc2c8, 0xe166, 0xc2c4,
+     0xc2c2, 0x85d0, 0xc2ec, 0x85c8, 0xc2e6, 0x85c4, 0x85c2, 0x0bd0, 0x85ec,
+     0x0bc8, 0x85e6, 0x0bc4, 0x0bc2, 0x0bec, 0x0be6, 0xf1a8, 0xf8d6, 0x1afc,
+     0xf1a4, 0x1a7e, 0xf1a2, 0xe128, 0xf096, 0xe368, 0xe124, 0xe364, 0xe122,
+     0xe362, 0xc268, 0xe136, 0xc6e8, 0xc264, 0xc6e4, 0xc262, 0xc6e2, 0x84e8,
+     0xc276, 0x8de8, 0x84e4, 0x8de4, 0x84e2, 0x8de2, 0x09e8, 0x84f6, 0x1be8,
+     0x09e4, 0x1be4, 0x09e2, 0x1be2, 0x09f6, 0x1bf6, 0xf9d4, 0x3af8, 0x9d7e,
+     0xf9d2, 0x3a7c, 0x3a3e, 0xf194, 0x197e, 0xf3b4, 0xf192, 0x3b7e, 0xf3b2,
+     0xe114, 0xe334, 0xe112, 0xe774, 0xe332, 0xe772, 0xc234, 0xc674, 0xc232,
+     0xcef4, 0xc672, 0xcef2, 0x8474, 0x8cf4, 0x8472, 0x9df4, 0x8cf2, 0x9df2,
+     0x08f4, 0x19f4, 0x08f2, 0x3bf4, 0x19f2, 0x3bf2, 0x7af0, 0xbd7c, 0x7a78,
+     0xbd3e, 0x7a3c, 0x7a1e, 0xf9ca, 0x397c, 0xfbda, 0x7b7c, 0x393e, 0x7b3e,
+     0xf18a, 0xf39a, 0xf7ba, 0xe10a, 0xe31a, 0xe73a, 0xef7a, 0xc21a, 0xc63a,
+     0xce7a, 0xdefa, 0x843a, 0x8c7a, 0x9cfa, 0xbdfa, 0x087a, 0x18fa, 0x39fa,
+     0x7978, 0xbcbe, 0x793c, 0x791e, 0x38be, 0x79be, 0x78bc, 0x789e, 0x785e,
+     0xe0a8, 0xe0a4, 0xe0a2, 0xc168, 0xe0b6, 0xc164, 0xc162, 0x82e8, 0xc176,
+     0x82e4, 0x82e2, 0x05e8, 0x82f6, 0x05e4, 0x05e2, 0x05f6, 0xf0d4, 0x0d7e,
+     0xf0d2, 0xe094, 0xe1b4, 0xe092, 0xe1b2, 0xc134, 0xc374, 0xc132, 0xc372,
+     0x8274, 0x86f4, 0x8272, 0x86f2, 0x04f4, 0x0df4, 0x04f2, 0x0df2, 0xf8ea,
+     0x1d7c, 0x1d3e, 0xf0ca, 0xf1da, 0xe08a, 0xe19a, 0xe3ba, 0xc11a, 0xc33a,
+     0xc77a, 0x823a, 0x867a, 0x8efa, 0x047a, 0x0cfa, 0x1dfa, 0x3d78, 0x9ebe,
+     0x3d3c, 0x3d1e, 0x1cbe, 0x3dbe, 0x7d70, 0xbebc, 0x7d38, 0xbe9e, 0x7d1c,
+     0x7d0e, 0x3cbc, 0x7dbc, 0x3c9e, 0x7d9e, 0x7cb8, 0xbe5e, 0x7c9c, 0x7c8e,
+     0x3c5e, 0x7cde, 0x7c5c, 0x7c4e, 0x7c2e, 0xc0b4, 0xc0b2, 0x8174, 0x8172,
+     0x02f4, 0x02f2, 0xe0da, 0xc09a, 0xc1ba, 0x813a, 0x837a, 0x027a, 0x06fa,
+     0x0ebe, 0x1ebc, 0x1e9e, 0x3eb8, 0x9f5e, 0x3e9c, 0x3e8e, 0x1e5e, 0x3ede,
+     0x7eb0, 0xbf5c, 0x7e98, 0xbf4e, 0x7e8c, 0x7e86, 0x3e5c, 0x7edc, 0x3e4e,
+     0x7ece, 0x7e58, 0xbf2e, 0x7e4c, 0x7e46, 0x3e2e, 0x7e6e, 0x7e2c, 0x7e26,
+     0x0f5e, 0x1f5c, 0x1f4e, 0x3f58, 0x9fae, 0x3f4c, 0x3f46, 0x1f2e, 0x3f6e,
+     0x3f2c, 0x3f26},
+    {0xabe0, 0xd5f8, 0x53c0, 0xa9f0, 0xd4fc, 0x51e0, 0xa8f8, 0xd47e, 0x50f0,
+     0xa87c, 0x5078, 0xfad0, 0x5be0, 0xadf8, 0xfac8, 0x59f0, 0xacfc, 0xfac4,
+     0x58f8, 0xac7e, 0xfac2, 0x587c, 0xf5d0, 0xfaec, 0x5df8, 0xf5c8, 0xfae6,
+     0x5cfc, 0xf5c4, 0x5c7e, 0xf5c2, 0xebd0, 0xf5ec, 0xebc8, 0xf5e6, 0xebc4,
+     0xebc2, 0xd7d0, 0xebec, 0xd7c8, 0xebe6, 0xd7c4, 0xd7c2, 0xafd0, 0xd7ec,
+     0xafc8, 0xd7e6, 0xafc4, 0x4bc0, 0xa5f0, 0xd2fc, 0x49e0, 0xa4f8, 0xd27e,
+     0x48f0, 0xa47c, 0x4878, 0xa43e, 0x483c, 0xfa68, 0x4df0, 0xa6fc, 0xfa64,
+     0x4cf8, 0xa67e, 0xfa62, 0x4c7c, 0x4c3e, 0xf4e8, 0xfa76, 0x4efc, 0xf4e4,
+     0x4e7e, 0xf4e2, 0xe9e8, 0xf4f6, 0xe9e4, 0xe9e2, 0xd3e8, 0xe9f6, 0xd3e4,
+     0xd3e2, 0xa7e8, 0xd3f6, 0xa7e4, 0xa7e2, 0x45e0, 0xa2f8, 0xd17e, 0x44f0,
+     0xa27c, 0x4478, 0xa23e, 0x443c, 0x441e, 0xfa34, 0x46f8, 0xa37e, 0xfa32,
+     0x467c, 0x463e, 0xf474, 0x477e, 0xf472, 0xe8f4, 0xe8f2, 0xd1f4, 0xd1f2,
+     0xa3f4, 0xa3f2, 0x42f0, 0xa17c, 0x4278, 0xa13e, 0x423c, 0x421e, 0xfa1a,
+     0x437c, 0x433e, 0xf43a, 0xe87a, 0xd0fa, 0x4178, 0xa0be, 0x413c, 0x411e,
+     0x41be, 0x40bc, 0x409e, 0x2bc0, 0x95f0, 0xcafc, 0x29e0, 0x94f8, 0xca7e,
+     0x28f0, 0x947c, 0x2878, 0x943e, 0x283c, 0xf968, 0x2df0, 0x96fc, 0xf964,
+     0x2cf8, 0x967e, 0xf962, 0x2c7c, 0x2c3e, 0xf2e8, 0xf976, 0x2efc, 0xf2e4,
+     0x2e7e, 0xf2e2, 0xe5e8, 0xf2f6, 0xe5e4, 0xe5e2, 0xcbe8, 0xe5f6, 0xcbe4,
+     0xcbe2, 0x97e8, 0xcbf6, 0x97e4, 0x97e2, 0xb5e0, 0xdaf8, 0xed7e, 0x69c0,
+     0xb4f0, 0xda7c, 0x68e0, 0xb478, 0xda3e, 0x6870, 0xb43c, 0x6838, 0xb41e,
+     0x681c, 0x25e0, 0x92f8, 0xc97e, 0x6de0, 0x24f0, 0x927c, 0x6cf0, 0xb67c,
+     0x923e, 0x6c78, 0x243c, 0x6c3c, 0x241e, 0x6c1e, 0xf934, 0x26f8, 0x937e,
+     0xfb74, 0xf932, 0x6ef8, 0x267c, 0xfb72, 0x6e7c, 0x263e, 0x6e3e, 0xf274,
+     0x277e, 0xf6f4, 0xf272, 0x6f7e, 0xf6f2, 0xe4f4, 0xedf4, 0xe4f2, 0xedf2,
+     0xc9f4, 0xdbf4, 0xc9f2, 0xdbf2, 0x93f4, 0x93f2, 0x65c0, 0xb2f0, 0xd97c,
+     0x64e0, 0xb278, 0xd93e, 0x6470, 0xb23c, 0x6438, 0xb21e, 0x641c, 0x640e,
+     0x22f0, 0x917c, 0x66f0, 0x2278, 0x913e, 0x6678, 0xb33e, 0x663c, 0x221e,
+     0x661e, 0xf91a, 0x237c, 0xfb3a, 0x677c, 0x233e, 0x673e, 0xf23a, 0xf67a,
+     0xe47a, 0xecfa, 0xc8fa, 0xd9fa, 0x91fa, 0x62e0, 0xb178, 0xd8be, 0x6270,
+     0xb13c, 0x6238, 0xb11e, 0x621c, 0x620e, 0x2178, 0x90be, 0x6378, 0x213c,
+     0x633c, 0x211e, 0x631e, 0x21be, 0x63be, 0x6170, 0xb0bc, 0x6138, 0xb09e,
+     0x611c, 0x610e, 0x20bc, 0x61bc, 0x209e, 0x619e, 0x60b8, 0xb05e, 0x609c,
+     0x608e, 0x205e, 0x60de, 0x605c, 0x604e, 0x15e0, 0x8af8, 0xc57e, 0x14f0,
+     0x8a7c, 0x1478, 0x8a3e, 0x143c, 0x141e, 0xf8b4, 0x16f8, 0x8b7e, 0xf8b2,
+     0x167c, 0x163e, 0xf174, 0x177e, 0xf172, 0xe2f4, 0xe2f2, 0xc5f4, 0xc5f2,
+     0x8bf4, 0x8bf2, 0x35c0, 0x9af0, 0xcd7c, 0x34e0, 0x9a78, 0xcd3e, 0x3470,
+     0x9a3c, 0x3438, 0x9a1e, 0x341c, 0x340e, 0x12f0, 0x897c, 0x36f0, 0x1278,
+     0x893e, 0x3678, 0x9b3e, 0x363c, 0x121e, 0x361e, 0xf89a, 0x137c, 0xf9ba,
+     0x377c, 0x133e, 0x373e, 0xf13a, 0xf37a, 0xe27a, 0xe6fa, 0xc4fa, 0xcdfa,
+     0x89fa, 0xbae0, 0xdd78, 0xeebe, 0x74c0, 0xba70, 0xdd3c, 0x7460, 0xba38,
+     0xdd1e, 0x7430, 0xba1c, 0x7418, 0xba0e, 0x740c, 0x32e0, 0x9978, 0xccbe,
+     0x76e0, 0x3270, 0x993c, 0x7670, 0xbb3c, 0x991e, 0x7638, 0x321c, 0x761c,
+     0x320e, 0x760e, 0x1178, 0x88be, 0x3378, 0x113c, 0x7778, 0x333c, 0x111e,
+     0x773c, 0x331e, 0x771e, 0x11be, 0x33be, 0x77be, 0x72c0, 0xb970, 0xdcbc,
+     0x7260, 0xb938, 0xdc9e, 0x7230, 0xb91c, 0x7218, 0xb90e, 0x720c, 0x7206,
+     0x3170, 0x98bc, 0x7370, 0x3138, 0x989e, 0x7338, 0xb99e, 0x731c, 0x310e,
+     0x730e, 0x10bc, 0x31bc, 0x109e, 0x73bc, 0x319e, 0x739e, 0x7160, 0xb8b8,
+     0xdc5e, 0x7130, 0xb89c, 0x7118, 0xb88e, 0x710c, 0x7106, 0x30b8, 0x985e,
+     0x71b8, 0x309c, 0x719c, 0x308e, 0x718e, 0x105e, 0x30de, 0x71de, 0x70b0,
+     0xb85c, 0x7098, 0xb84e, 0x708c, 0x7086, 0x305c, 0x70dc, 0x304e, 0x70ce,
+     0x7058, 0xb82e, 0x704c, 0x7046, 0x302e, 0x706e, 0x702c, 0x7026, 0x0af0,
+     0x857c, 0x0a78, 0x853e, 0x0a3c, 0x0a1e, 0x0b7c, 0x0b3e, 0xf0ba, 0xe17a,
+     0xc2fa, 0x85fa, 0x1ae0, 0x8d78, 0xc6be, 0x1a70, 0x8d3c, 0x1a38, 0x8d1e,
+     0x1a1c, 0x1a0e, 0x0978, 0x84be, 0x1b78, 0x093c, 0x1b3c, 0x091e, 0x1b1e,
+     0x09be, 0x1bbe, 0x3ac0, 0x9d70, 0xcebc, 0x3a60, 0x9d38, 0xce9e, 0x3a30,
+     0x9d1c, 0x3a18, 0x9d0e, 0x3a0c, 0x3a06, 0x1970, 0x8cbc, 0x3b70, 0x1938,
+     0x8c9e, 0x3b38, 0x191c, 0x3b1c, 0x190e, 0x3b0e, 0x08bc, 0x19bc, 0x089e,
+     0x3bbc, 0x199e, 0x3b9e, 0xbd60, 0xdeb8, 0xef5e, 0x7a40, 0xbd30, 0xde9c,
+     0x7a20, 0xbd18, 0xde8e, 0x7a10, 0xbd0c, 0x7a08, 0xbd06, 0x7a04, 0x3960,
+     0x9cb8, 0xce5e, 0x7b60, 0x3930, 0x9c9c, 0x7b30, 0xbd9c, 0x9c8e, 0x7b18,
+     0x390c, 0x7b0c, 0x3906, 0x7b06, 0x18b8, 0x8c5e, 0x39b8, 0x189c, 0x7bb8,
+     0x399c, 0x188e, 0x7b9c, 0x398e, 0x7b8e, 0x085e, 0x18de, 0x39de, 0x7bde,
+     0x7940, 0xbcb0, 0xde5c, 0x7920, 0xbc98, 0xde4e, 0x7910, 0xbc8c, 0x7908,
+     0xbc86, 0x7904, 0x7902, 0x38b0, 0x9c5c, 0x79b0, 0x3898, 0x9c4e, 0x7998,
+     0xbcce, 0x798c, 0x3886, 0x7986, 0x185c, 0x38dc, 0x184e, 0x79dc, 0x38ce,
+     0x79ce, 0x78a0, 0xbc58, 0xde2e, 0x7890, 0xbc4c, 0x7888, 0xbc46, 0x7884,
+     0x7882, 0x3858, 0x9c2e, 0x78d8, 0x384c, 0x78cc, 0x3846, 0x78c6, 0x182e,
+     0x386e, 0x78ee, 0x7850, 0xbc2c, 0x7848, 0xbc26, 0x7844, 0x7842, 0x382c,
+     0x786c, 0x3826, 0x7866, 0x7828, 0xbc16, 0x7824, 0x7822, 0x3816, 0x7836,
+     0x0578, 0x82be, 0x053c, 0x051e, 0x05be, 0x0d70, 0x86bc, 0x0d38, 0x869e,
+     0x0d1c, 0x0d0e, 0x04bc, 0x0dbc, 0x049e, 0x0d9e, 0x1d60, 0x8eb8, 0xc75e,
+     0x1d30, 0x8e9c, 0x1d18, 0x8e8e, 0x1d0c, 0x1d06, 0x0cb8, 0x865e, 0x1db8,
+     0x0c9c, 0x1d9c, 0x0c8e, 0x1d8e, 0x045e, 0x0cde, 0x1dde, 0x3d40, 0x9eb0,
+     0xcf5c, 0x3d20, 0x9e98, 0xcf4e, 0x3d10, 0x9e8c, 0x3d08, 0x9e86, 0x3d04,
+     0x3d02, 0x1cb0, 0x8e5c, 0x3db0, 0x1c98, 0x8e4e, 0x3d98, 0x9ece, 0x3d8c,
+     0x1c86, 0x3d86, 0x0c5c, 0x1cdc, 0x0c4e, 0x3ddc, 0x1cce, 0x3dce, 0xbea0,
+     0xdf58, 0xefae, 0xbe90, 0xdf4c, 0xbe88, 0xdf46, 0xbe84, 0xbe82, 0x3ca0,
+     0x9e58, 0xcf2e, 0x7da0, 0x3c90, 0x9e4c, 0x7d90, 0xbecc, 0x9e46, 0x7d88,
+     0x3c84, 0x7d84, 0x3c82, 0x7d82, 0x1c58, 0x8e2e, 0x3cd8, 0x1c4c, 0x7dd8,
+     0x3ccc, 0x1c46, 0x7dcc, 0x3cc6, 0x7dc6, 0x0c2e, 0x1c6e, 0x3cee, 0x7dee,
+     0xbe50, 0xdf2c, 0xbe48, 0xdf26, 0xbe44, 0xbe42, 0x3c50, 0x9e2c, 0x7cd0,
+     0x3c48, 0x9e26, 0x7cc8, 0xbe66, 0x7cc4, 0x3c42, 0x7cc2, 0x1c2c, 0x3c6c,
+     0x1c26, 0x7cec, 0x3c66, 0x7ce6, 0xbe28, 0xdf16, 0xbe24, 0xbe22, 0x3c28,
+     0x9e16, 0x7c68, 0x3c24, 0x7c64, 0x3c22, 0x7c62, 0x1c16, 0x3c36, 0x7c76,
+     0xbe14, 0xbe12, 0x3c14, 0x7c34, 0x3c12, 0x7c32, 0x02bc, 0x029e, 0x06b8,
+     0x835e, 0x069c, 0x068e, 0x025e, 0x06de, 0x0eb0, 0x875c, 0x0e98, 0x874e,
+     0x0e8c, 0x0e86, 0x065c, 0x0edc, 0x064e, 0x0ece, 0x1ea0, 0x8f58, 0xc7ae,
+     0x1e90, 0x8f4c, 0x1e88, 0x8f46, 0x1e84, 0x1e82, 0x0e58, 0x872e, 0x1ed8,
+     0x8f6e, 0x1ecc, 0x0e46, 0x1ec6, 0x062e, 0x0e6e, 0x1eee, 0x9f50, 0xcfac,
+     0x9f48, 0xcfa6, 0x9f44, 0x9f42, 0x1e50, 0x8f2c, 0x3ed0, 0x9f6c, 0x8f26,
+     0x3ec8, 0x1e44, 0x3ec4, 0x1e42, 0x3ec2, 0x0e2c, 0x1e6c, 0x0e26, 0x3eec,
+     0x1e66, 0x3ee6, 0xdfa8, 0xefd6, 0xdfa4, 0xdfa2, 0x9f28, 0xcf96, 0xbf68,
+     0x9f24, 0xbf64, 0x9f22, 0xbf62, 0x1e28, 0x8f16, 0x3e68, 0x1e24, 0x7ee8,
+     0x3e64, 0x1e22, 0x7ee4, 0x3e62, 0x7ee2, 0x0e16, 0x1e36, 0x3e76, 0x7ef6,
+     0xdf94, 0xdf92, 0x9f14, 0xbf34, 0x9f12, 0xbf32, 0x1e14, 0x3e34, 0x1e12,
+     0x7e74, 0x3e32, 0x7e72, 0xdf8a, 0x9f0a, 0xbf1a, 0x1e0a, 0x3e1a, 0x7e3a,
+     0x035c, 0x034e, 0x0758, 0x83ae, 0x074c, 0x0746, 0x032e, 0x076e, 0x0f50,
+     0x87ac, 0x0f48, 0x87a6, 0x0f44, 0x0f42, 0x072c, 0x0f6c, 0x0726, 0x0f66,
+     0x8fa8, 0xc7d6, 0x8fa4, 0x8fa2, 0x0f28, 0x8796, 0x1f68, 0x8fb6, 0x1f64,
+     0x0f22, 0x1f62, 0x0716, 0x0f36, 0x1f76, 0xcfd4, 0xcfd2, 0x8f94, 0x9fb4,
+     0x8f92, 0x9fb2, 0x0f14, 0x1f34, 0x0f12, 0x3f74, 0x1f32, 0x3f72, 0xcfca,
+     0x8f8a, 0x9f9a, 0x0f0a, 0x1f1a, 0x3f3a, 0x03ac, 0x03a6, 0x07a8, 0x83d6,
+     0x07a4, 0x07a2, 0x0396, 0x07b6, 0x87d4, 0x87d2, 0x0794, 0x0fb4, 0x0792,
+     0x0fb2, 0xc7ea}};
 
-CBC_PDF417::CBC_PDF417(bool compact)
-    : m_compact(compact),
-      m_compaction(AUTO),
-      m_minCols(1),
-      m_maxCols(30),
-      m_maxRows(90),
-      m_minRows(3) {}
+int32_t Get17BitCodeword(int i, int j) {
+  return (0x10000 | g_CodewordTable[i][j]);
+}
 
-CBC_PDF417::~CBC_PDF417() {}
+}  // namespace
+CBC_PDF417::CBC_PDF417() = default;
+
+CBC_PDF417::~CBC_PDF417() = default;
 
 CBC_BarcodeMatrix* CBC_PDF417::getBarcodeMatrix() {
   return m_barcodeMatrix.get();
 }
 
-bool CBC_PDF417::generateBarcodeLogic(WideString msg,
+bool CBC_PDF417::GenerateBarcodeLogic(WideStringView msg,
                                       int32_t errorCorrectionLevel) {
   int32_t errorCorrectionCodeWords =
-      CBC_PDF417ErrorCorrection::getErrorCorrectionCodewordCount(
+      CBC_PDF417ErrorCorrection::GetErrorCorrectionCodewordCount(
           errorCorrectionLevel);
   if (errorCorrectionCodeWords < 0)
     return false;
 
-  int32_t e = BCExceptionNO;
-  WideString highLevel =
-      CBC_PDF417HighLevelEncoder::encodeHighLevel(msg, m_compaction, e);
-  if (e != BCExceptionNO)
+  Optional<WideString> high_level =
+      CBC_PDF417HighLevelEncoder::EncodeHighLevel(msg);
+  if (!high_level.has_value())
     return false;
-  int32_t sourceCodeWords = highLevel.GetLength();
+
+  size_t sourceCodeWords = high_level.value().GetLength();
   std::vector<int32_t> dimensions =
       determineDimensions(sourceCodeWords, errorCorrectionCodeWords);
   if (dimensions.size() != 2)
@@ -427,18 +385,18 @@
   int32_t n = sourceCodeWords + pad + 1;
   WideString sb;
   sb += (wchar_t)n;
-  sb += highLevel;
+  sb += high_level.value();
   for (int32_t i = 0; i < pad; i++)
     sb += (wchar_t)900;
 
   WideString dataCodewords(sb);
-  WideString ec;
-  if (!CBC_PDF417ErrorCorrection::generateErrorCorrection(
-          dataCodewords, errorCorrectionLevel, &ec)) {
+  Optional<WideString> ec = CBC_PDF417ErrorCorrection::GenerateErrorCorrection(
+      dataCodewords, errorCorrectionLevel);
+  if (!ec.has_value())
     return false;
-  }
-  WideString fullCodewords = dataCodewords + ec;
-  m_barcodeMatrix = pdfium::MakeUnique<CBC_BarcodeMatrix>(rows, cols);
+
+  WideString fullCodewords = dataCodewords + ec.value();
+  m_barcodeMatrix = pdfium::MakeUnique<CBC_BarcodeMatrix>(cols, rows);
   encodeLowLevel(fullCodewords, cols, rows, errorCorrectionLevel,
                  m_barcodeMatrix.get());
   return true;
@@ -454,14 +412,6 @@
   m_minRows = minRows;
 }
 
-void CBC_PDF417::setCompaction(Compaction compaction) {
-  m_compaction = compaction;
-}
-
-void CBC_PDF417::setCompact(bool compact) {
-  m_compact = compact;
-}
-
 int32_t CBC_PDF417::calculateNumberOfRows(int32_t m, int32_t k, int32_t c) {
   int32_t r = ((m + 1 + k) / c) + 1;
   if (c * r >= (m + 1 + k + c)) {
@@ -505,9 +455,9 @@
                                 CBC_BarcodeMatrix* logic) {
   int32_t idx = 0;
   for (int32_t y = 0; y < r; y++) {
+    CBC_BarcodeRow* logicRow = logic->getRow(y);
     int32_t cluster = y % 3;
-    logic->startRow();
-    encodeChar(START_PATTERN, 17, logic->getCurrentRow());
+    encodeChar(START_PATTERN, 17, logicRow);
     int32_t left;
     int32_t right;
     if (cluster == 0) {
@@ -520,25 +470,21 @@
       left = (30 * (y / 3)) + (c - 1);
       right = (30 * (y / 3)) + (errorCorrectionLevel * 3) + ((r - 1) % 3);
     }
-    int32_t pattern = CODEWORD_TABLE[cluster][left];
-    encodeChar(pattern, 17, logic->getCurrentRow());
+    int32_t pattern = Get17BitCodeword(cluster, left);
+    encodeChar(pattern, 17, logicRow);
     for (int32_t x = 0; x < c; x++) {
-      pattern = CODEWORD_TABLE[cluster][fullCodewords[idx]];
-      encodeChar(pattern, 17, logic->getCurrentRow());
+      pattern = Get17BitCodeword(cluster, fullCodewords[idx]);
+      encodeChar(pattern, 17, logicRow);
       idx++;
     }
-    if (m_compact) {
-      encodeChar(STOP_PATTERN, 1, logic->getCurrentRow());
-    } else {
-      pattern = CODEWORD_TABLE[cluster][right];
-      encodeChar(pattern, 17, logic->getCurrentRow());
-      encodeChar(STOP_PATTERN, 18, logic->getCurrentRow());
-    }
+    pattern = Get17BitCodeword(cluster, right);
+    encodeChar(pattern, 17, logicRow);
+    encodeChar(STOP_PATTERN, 18, logicRow);
   }
 }
 
 std::vector<int32_t> CBC_PDF417::determineDimensions(
-    int32_t sourceCodeWords,
+    size_t sourceCodeWords,
     int32_t errorCorrectionCodeWords) const {
   std::vector<int32_t> dimensions;
   float ratio = 0.0f;
@@ -562,14 +508,14 @@
   }
   if (dimensions.empty()) {
     int32_t rows = calculateNumberOfRows(sourceCodeWords,
-                                         errorCorrectionCodeWords, m_minCols);
+                                         errorCorrectionCodeWords, m_maxCols);
     if (rows < m_minRows) {
       dimensions.resize(2);
-      dimensions[0] = m_minCols;
+      dimensions[0] = m_maxCols;
       dimensions[1] = m_minRows;
     } else if (rows >= 3 && rows <= 90) {
       dimensions.resize(2);
-      dimensions[0] = m_minCols;
+      dimensions[0] = m_maxCols;
       dimensions[1] = rows;
     }
   }
diff --git a/fxbarcode/pdf417/BC_PDF417.h b/fxbarcode/pdf417/BC_PDF417.h
index 6014df0..b445ede 100644
--- a/fxbarcode/pdf417/BC_PDF417.h
+++ b/fxbarcode/pdf417/BC_PDF417.h
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
-#include "fxbarcode/pdf417/BC_PDF417Compaction.h"
 
 class CBC_BarcodeRow;
 class CBC_BarcodeMatrix;
@@ -19,22 +18,18 @@
 class CBC_PDF417 {
  public:
   CBC_PDF417();
-  explicit CBC_PDF417(bool compact);
-  virtual ~CBC_PDF417();
+  ~CBC_PDF417();
 
   CBC_BarcodeMatrix* getBarcodeMatrix();
-  bool generateBarcodeLogic(WideString msg, int32_t errorCorrectionLevel);
+  bool GenerateBarcodeLogic(WideStringView msg, int32_t errorCorrectionLevel);
   void setDimensions(int32_t maxCols,
                      int32_t minCols,
                      int32_t maxRows,
                      int32_t minRows);
-  void setCompaction(Compaction compaction);
-  void setCompact(bool compact);
 
  private:
   static const int32_t START_PATTERN = 0x1fea8;
   static const int32_t STOP_PATTERN = 0x3fa29;
-  static const int32_t CODEWORD_TABLE[][929];
   static constexpr float PREFERRED_RATIO = 3.0f;
   static constexpr float DEFAULT_MODULE_WIDTH = 0.357f;
   static constexpr float HEIGHT = 2.0f;
@@ -51,16 +46,14 @@
                       int32_t errorCorrectionLevel,
                       CBC_BarcodeMatrix* logic);
   std::vector<int32_t> determineDimensions(
-      int32_t sourceCodeWords,
+      size_t sourceCodeWords,
       int32_t errorCorrectionCodeWords) const;
 
   std::unique_ptr<CBC_BarcodeMatrix> m_barcodeMatrix;
-  bool m_compact;
-  Compaction m_compaction;
-  int32_t m_minCols;
-  int32_t m_maxCols;
-  int32_t m_maxRows;
-  int32_t m_minRows;
+  int32_t m_minCols = 1;
+  int32_t m_maxCols = 30;
+  int32_t m_minRows = 3;
+  int32_t m_maxRows = 90;
 };
 
 #endif  // FXBARCODE_PDF417_BC_PDF417_H_
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp
index ca492b3..70e7cd9 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp
@@ -24,50 +24,21 @@
 #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h"
 #include "third_party/base/ptr_util.h"
 
-CBC_BarcodeMatrix::CBC_BarcodeMatrix(int32_t height, int32_t width) {
-  m_matrix.resize(height + 2);
-  for (size_t i = 0; i < m_matrix.size(); ++i)
-    m_matrix[i] = pdfium::MakeUnique<CBC_BarcodeRow>((width + 4) * 17 + 1);
-
-  m_width = width * 17;
-  m_height = height + 2;
-  m_currentRow = 0;
-  m_outHeight = 0;
-  m_outWidth = 0;
+CBC_BarcodeMatrix::CBC_BarcodeMatrix(size_t width, size_t height)
+    : m_width((width + 4) * 17 + 1), m_height(height) {
+  m_matrix.resize(m_height);
+  for (size_t i = 0; i < m_height; ++i)
+    m_matrix[i] = pdfium::MakeUnique<CBC_BarcodeRow>(m_width);
 }
 
 CBC_BarcodeMatrix::~CBC_BarcodeMatrix() {}
 
-void CBC_BarcodeMatrix::set(int32_t x, int32_t y, uint8_t value) {
-  m_matrix[y]->set(x, value);
-}
-void CBC_BarcodeMatrix::setMatrix(int32_t x, int32_t y, bool black) {
-  set(x, y, (uint8_t)(black ? 1 : 0));
-}
-void CBC_BarcodeMatrix::startRow() {
-  ++m_currentRow;
-}
-std::vector<uint8_t>& CBC_BarcodeMatrix::getMatrix() {
-  return getScaledMatrix(1, 1);
-}
-std::vector<uint8_t>& CBC_BarcodeMatrix::getScaledMatrix(int32_t scale) {
-  return getScaledMatrix(scale, scale);
-}
-std::vector<uint8_t>& CBC_BarcodeMatrix::getScaledMatrix(int32_t xScale,
-                                                         int32_t yScale) {
-  size_t yMax = m_height * yScale;
-  std::vector<uint8_t> bytearray = m_matrix[0]->getScaledRow(xScale);
-  size_t xMax = bytearray.size();
-  m_matrixOut.resize(xMax * yMax);
-  m_outWidth = xMax;
-  m_outHeight = yMax;
-  int32_t k = 0;
-  for (size_t i = 0; i < yMax; i++) {
-    if (i != 0)
-      bytearray = m_matrix[i / yScale]->getScaledRow(xScale);
-    k = i * xMax;
-    for (size_t l = 0; l < xMax; l++)
-      m_matrixOut[k + l] = bytearray[l];
+std::vector<uint8_t> CBC_BarcodeMatrix::toBitArray() {
+  std::vector<uint8_t> bitArray(m_width * m_height);
+  for (size_t i = 0; i < m_height; ++i) {
+    std::vector<uint8_t>& bytearray = m_matrix[i]->getRow();
+    for (size_t j = 0; j < m_width; ++j)
+      bitArray[i * m_width + j] = bytearray[j];
   }
-  return m_matrixOut;
+  return bitArray;
 }
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h
index 71ada98..d059cee 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h
@@ -12,30 +12,20 @@
 
 class CBC_BarcodeRow;
 
-class CBC_BarcodeMatrix {
+class CBC_BarcodeMatrix final {
  public:
-  CBC_BarcodeMatrix();
-  CBC_BarcodeMatrix(int32_t height, int32_t width);
-  virtual ~CBC_BarcodeMatrix();
+  CBC_BarcodeMatrix(size_t width, size_t height);
+  ~CBC_BarcodeMatrix();
 
-  CBC_BarcodeRow* getCurrentRow() const { return m_matrix[m_currentRow].get(); }
-  int32_t getWidth() const { return m_outWidth; }
-  int32_t getHeight() const { return m_outHeight; }
-  void set(int32_t x, int32_t y, uint8_t value);
-  void setMatrix(int32_t x, int32_t y, bool black);
-  void startRow();
-  std::vector<uint8_t>& getMatrix();
-  std::vector<uint8_t>& getScaledMatrix(int32_t scale);
-  std::vector<uint8_t>& getScaledMatrix(int32_t xScale, int32_t yScale);
+  CBC_BarcodeRow* getRow(size_t row) const { return m_matrix[row].get(); }
+  size_t getWidth() const { return m_width; }
+  size_t getHeight() const { return m_height; }
+  std::vector<uint8_t> toBitArray();
 
  private:
   std::vector<std::unique_ptr<CBC_BarcodeRow>> m_matrix;
-  std::vector<uint8_t> m_matrixOut;
-  int32_t m_currentRow;
-  int32_t m_height;
-  int32_t m_width;
-  int32_t m_outWidth;
-  int32_t m_outHeight;
+  size_t m_width;
+  size_t m_height;
 };
 
 #endif  // FXBARCODE_PDF417_BC_PDF417BARCODEMATRIX_H_
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
index c0c251f..65c27de 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
@@ -22,31 +22,18 @@
 
 #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h"
 
+#include <algorithm>
+
 CBC_BarcodeRow::CBC_BarcodeRow(size_t width)
     : m_row(width), m_currentLocation(0) {}
 
 CBC_BarcodeRow::~CBC_BarcodeRow() {}
 
-void CBC_BarcodeRow::set(int32_t x, uint8_t value) {
-  m_row[x] = value;
-}
-
-void CBC_BarcodeRow::set(int32_t x, bool black) {
-  m_row[x] = black ? 1 : 0;
-}
-
 void CBC_BarcodeRow::addBar(bool black, int32_t width) {
-  for (int32_t ii = 0; ii < width; ii++)
-    set(m_currentLocation++, black);
+  std::fill_n(m_row.begin() + m_currentLocation, width, black ? 1 : 0);
+  m_currentLocation += width;
 }
 
 std::vector<uint8_t>& CBC_BarcodeRow::getRow() {
   return m_row;
 }
-
-std::vector<uint8_t>& CBC_BarcodeRow::getScaledRow(int32_t scale) {
-  m_output.resize(m_row.size() * scale);
-  for (size_t i = 0; i < m_output.size(); i++)
-    m_output[i] = m_row[i / scale];
-  return m_output;
-}
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
index d49d0eb..07ebbd7 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
@@ -11,20 +11,16 @@
 
 #include <vector>
 
-class CBC_BarcodeRow {
+class CBC_BarcodeRow final {
  public:
   explicit CBC_BarcodeRow(size_t width);
-  virtual ~CBC_BarcodeRow();
+  ~CBC_BarcodeRow();
 
-  void set(int32_t x, uint8_t value);
-  void set(int32_t x, bool black);
   void addBar(bool black, int32_t width);
   std::vector<uint8_t>& getRow();
-  std::vector<uint8_t>& getScaledRow(int32_t scale);
 
  private:
   std::vector<uint8_t> m_row;
-  std::vector<uint8_t> m_output;
   int32_t m_currentLocation;
 };
 
diff --git a/fxbarcode/pdf417/BC_PDF417Compaction.cpp b/fxbarcode/pdf417/BC_PDF417Compaction.cpp
deleted file mode 100644
index d0ed878..0000000
--- a/fxbarcode/pdf417/BC_PDF417Compaction.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-// Original code is licensed as follows:
-/*
- * Copyright 2011 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "fxbarcode/pdf417/BC_PDF417Compaction.h"
-
-CBC_Compaction::CBC_Compaction() {}
-CBC_Compaction::~CBC_Compaction() {}
diff --git a/fxbarcode/pdf417/BC_PDF417Compaction.h b/fxbarcode/pdf417/BC_PDF417Compaction.h
deleted file mode 100644
index 7f4795a..0000000
--- a/fxbarcode/pdf417/BC_PDF417Compaction.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXBARCODE_PDF417_BC_PDF417COMPACTION_H_
-#define FXBARCODE_PDF417_BC_PDF417COMPACTION_H_
-
-class CBC_Compaction;
-enum Compaction { AUTO, TEXT, BYTES, NUMERIC };
-class CBC_Compaction {
- public:
-  CBC_Compaction();
-  virtual ~CBC_Compaction();
-};
-
-#endif  // FXBARCODE_PDF417_BC_PDF417COMPACTION_H_
diff --git a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp
index ade3eb5..f910d4a 100644
--- a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp
+++ b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp
@@ -24,6 +24,8 @@
 
 #include <vector>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
+
 namespace {
 
 const uint16_t EC_LEVEL_0_COEFFICIENTS[2] = {27, 917};
@@ -122,29 +124,25 @@
 
 }  // namespace
 
-CBC_PDF417ErrorCorrection::CBC_PDF417ErrorCorrection() {}
-CBC_PDF417ErrorCorrection::~CBC_PDF417ErrorCorrection() {}
-int32_t CBC_PDF417ErrorCorrection::getErrorCorrectionCodewordCount(
+// static
+int32_t CBC_PDF417ErrorCorrection::GetErrorCorrectionCodewordCount(
     int32_t errorCorrectionLevel) {
   if (errorCorrectionLevel < 0 || errorCorrectionLevel > 8)
     return -1;
   return 1 << (errorCorrectionLevel + 1);
 }
 
-bool CBC_PDF417ErrorCorrection::generateErrorCorrection(
+// static
+Optional<WideString> CBC_PDF417ErrorCorrection::GenerateErrorCorrection(
     const WideString& dataCodewords,
-    int32_t errorCorrectionLevel,
-    WideString* result) {
-  assert(result);
-  assert(result->IsEmpty());
-
-  int32_t k = getErrorCorrectionCodewordCount(errorCorrectionLevel);
+    int32_t errorCorrectionLevel) {
+  int32_t k = GetErrorCorrectionCodewordCount(errorCorrectionLevel);
   if (k < 0)
-    return false;
+    return {};
 
-  std::vector<wchar_t> ech(k);
-  int32_t sld = dataCodewords.GetLength();
-  for (int32_t i = 0; i < sld; i++) {
+  std::vector<wchar_t, FxAllocAllocator<wchar_t>> ech(k);
+  size_t sld = dataCodewords.GetLength();
+  for (size_t i = 0; i < sld; i++) {
     int32_t t1 = (dataCodewords[i] + ech[k - 1]) % 929;
     int32_t t2;
     int32_t t3;
@@ -157,11 +155,12 @@
     t3 = 929 - t2;
     ech[0] = (wchar_t)(t3 % 929);
   }
-  result->Reserve(k);
+  WideString result;
+  result.Reserve(k);
   for (int32_t j = k - 1; j >= 0; j--) {
     if (ech[j] != 0)
       ech[j] = static_cast<wchar_t>(929) - ech[j];
-    *result += ech[j];
+    result += ech[j];
   }
-  return true;
+  return result;
 }
diff --git a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h
index 3e28162..f5aa898 100644
--- a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h
+++ b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h
@@ -10,16 +10,17 @@
 #include <stdint.h>
 
 #include "core/fxcrt/fx_string.h"
+#include "third_party/base/optional.h"
 
 class CBC_PDF417ErrorCorrection {
  public:
-  CBC_PDF417ErrorCorrection();
-  virtual ~CBC_PDF417ErrorCorrection();
+  CBC_PDF417ErrorCorrection() = delete;
+  ~CBC_PDF417ErrorCorrection() = delete;
 
-  static int32_t getErrorCorrectionCodewordCount(int32_t errorCorrectionLevel);
-  static bool generateErrorCorrection(const WideString& dataCodewords,
-                                      int32_t errorCorrectionLevel,
-                                      WideString* result);
+  static int32_t GetErrorCorrectionCodewordCount(int32_t errorCorrectionLevel);
+  static Optional<WideString> GenerateErrorCorrection(
+      const WideString& dataCodewords,
+      int32_t errorCorrectionLevel);
 };
 
 #endif  // FXBARCODE_PDF417_BC_PDF417ERRORCORRECTION_H_
diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp
index 059287a..09cac1b 100644
--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp
+++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp
@@ -22,402 +22,368 @@
 
 #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h"
 
-#include "fxbarcode/BC_UtilCodingConvert.h"
-#include "fxbarcode/pdf417/BC_PDF417Compaction.h"
-#include "fxbarcode/utils.h"
+#include "core/fxcrt/fx_extension.h"
 #include "third_party/bigint/BigIntegerLibrary.hh"
 
-#define SUBMODE_ALPHA 0
-#define SUBMODE_LOWER 1
-#define SUBMODE_MIXED 2
+namespace {
 
-int32_t CBC_PDF417HighLevelEncoder::TEXT_COMPACTION = 0;
-int32_t CBC_PDF417HighLevelEncoder::BYTE_COMPACTION = 1;
-int32_t CBC_PDF417HighLevelEncoder::NUMERIC_COMPACTION = 2;
-int32_t CBC_PDF417HighLevelEncoder::SUBMODE_PUNCTUATION = 3;
-int32_t CBC_PDF417HighLevelEncoder::LATCH_TO_TEXT = 900;
-int32_t CBC_PDF417HighLevelEncoder::LATCH_TO_BYTE_PADDED = 901;
-int32_t CBC_PDF417HighLevelEncoder::LATCH_TO_NUMERIC = 902;
-int32_t CBC_PDF417HighLevelEncoder::SHIFT_TO_BYTE = 913;
-int32_t CBC_PDF417HighLevelEncoder::LATCH_TO_BYTE = 924;
-uint8_t CBC_PDF417HighLevelEncoder::TEXT_MIXED_RAW[] = {
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,
-    35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0,  32, 0, 0,  0};
-uint8_t CBC_PDF417HighLevelEncoder::TEXT_PUNCTUATION_RAW[] = {
-    59, 60, 62, 64, 91, 92, 93,  95, 96, 126, 33, 13,  9,   44, 58,
-    10, 45, 46, 36, 47, 34, 124, 42, 40, 41,  63, 123, 125, 39, 0};
-int32_t CBC_PDF417HighLevelEncoder::MIXED[128] = {0};
-int32_t CBC_PDF417HighLevelEncoder::PUNCTUATION[128] = {0};
+constexpr int16_t kLatchToText = 900;
+constexpr int16_t kLatchToBytePadded = 901;
+constexpr int16_t kLatchToNumeric = 902;
+constexpr int16_t kShiftToByte = 913;
+constexpr int16_t kLatchToByte = 924;
 
-void CBC_PDF417HighLevelEncoder::Initialize() {
-  Inverse();
+constexpr int8_t kMixed[128] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, -1, 11, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, -1, 15, 18, 21,
+    10, -1, -1, -1, 22, 20, 13, 16, 17, 19, 0,  1,  2,  3,  4,  5,  6,  7,  8,
+    9,  14, -1, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+
+constexpr int8_t kPunctuation[128] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 15, -1, -1, 11, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 20, -1, 18, -1,
+    -1, 28, 23, 24, 22, -1, 13, 16, 17, 19, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, 14, 0,  1,  -1, 2,  25, 3,  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,  5,  6,  -1,
+    7,  8,  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 21, 27, 9,  -1};
+
+bool IsAlphaUpperOrSpace(wchar_t ch) {
+  return ch == ' ' || (ch >= 'A' && ch <= 'Z');
 }
 
-void CBC_PDF417HighLevelEncoder::Finalize() {}
+bool IsAlphaLowerOrSpace(wchar_t ch) {
+  return ch == ' ' || (ch >= 'a' && ch <= 'z');
+}
 
-WideString CBC_PDF417HighLevelEncoder::encodeHighLevel(WideString wideMsg,
-                                                       Compaction compaction,
-                                                       int32_t& e) {
-  ByteString bytes;
-  CBC_UtilCodingConvert::UnicodeToUTF8(wideMsg, bytes);
-  WideString msg;
-  int32_t len = bytes.GetLength();
-  for (int32_t i = 0; i < len; i++) {
-    wchar_t ch = (wchar_t)(bytes[i] & 0xff);
-    if (ch == '?' && bytes[i] != '?') {
-      e = BCExceptionCharactersOutsideISO88591Encoding;
-      return WideString();
-    }
-    msg += ch;
+bool IsMixed(wchar_t ch) {
+  // Bounds check avoiding sign mismatch error given questionable signedness.
+  return !((ch & ~0x7F) || kMixed[ch] == -1);
+}
+
+bool IsPunctuation(wchar_t ch) {
+  // Bounds check avoiding sign mismatch error given questionable signedness.
+  return !((ch & ~0x7F) || kPunctuation[ch] == -1);
+}
+
+bool IsText(wchar_t ch) {
+  return (ch >= 32 && ch <= 126) || ch == '\t' || ch == '\n' || ch == '\r';
+}
+
+}  // namespace
+
+// static
+Optional<WideString> CBC_PDF417HighLevelEncoder::EncodeHighLevel(
+    WideStringView msg) {
+  ByteString bytes = FX_UTF8Encode(msg);
+  size_t len = bytes.GetLength();
+  WideString result;
+  result.Reserve(len);
+  for (size_t i = 0; i < len; i++) {
+    wchar_t ch = bytes[i] & 0xff;
+    if (ch == '?' && bytes[i] != '?')
+      return {};
+
+    result += ch;
   }
   std::vector<uint8_t> byteArr(bytes.begin(), bytes.end());
+  len = result.GetLength();
   WideString sb;
-  len = msg.GetLength();
-  int32_t p = 0;
-  int32_t textSubMode = SUBMODE_ALPHA;
-  if (compaction == TEXT) {
-    encodeText(msg, p, len, sb, textSubMode);
-  } else if (compaction == BYTES) {
-    encodeBinary(&byteArr, p, byteArr.size(), BYTE_COMPACTION, sb);
-  } else if (compaction == NUMERIC) {
-    sb += (wchar_t)LATCH_TO_NUMERIC;
-    encodeNumeric(msg, p, len, sb);
-  } else {
-    int32_t encodingMode = LATCH_TO_TEXT;
-    while (p < len) {
-      int32_t n = determineConsecutiveDigitCount(msg, p);
-      if (n >= 13) {
-        sb += (wchar_t)LATCH_TO_NUMERIC;
-        encodingMode = NUMERIC_COMPACTION;
-        textSubMode = SUBMODE_ALPHA;
-        encodeNumeric(msg, p, n, sb);
-        p += n;
-      } else {
-        int32_t t = determineConsecutiveTextCount(msg, p);
-        if (t >= 5 || n == len) {
-          if (encodingMode != TEXT_COMPACTION) {
-            sb += (wchar_t)LATCH_TO_TEXT;
-            encodingMode = TEXT_COMPACTION;
-            textSubMode = SUBMODE_ALPHA;
-          }
-          textSubMode = encodeText(msg, p, t, sb, textSubMode);
-          p += t;
-        } else {
-          int32_t b = determineConsecutiveBinaryCount(msg, &byteArr, p, e);
-          if (e != BCExceptionNO)
-            return L" ";
-          if (b == 0) {
-            b = 1;
-          }
-          if (b == 1 && encodingMode == TEXT_COMPACTION) {
-            encodeBinary(&byteArr, p, 1, TEXT_COMPACTION, sb);
-          } else {
-            encodeBinary(&byteArr, p, b, encodingMode, sb);
-            encodingMode = BYTE_COMPACTION;
-            textSubMode = SUBMODE_ALPHA;
-          }
-          p += b;
+  sb.Reserve(len);
+  size_t p = 0;
+  SubMode textSubMode = SubMode::kAlpha;
+  EncodingMode encodingMode = EncodingMode::kUnknown;
+  while (p < len) {
+    size_t n = DetermineConsecutiveDigitCount(result, p);
+    if (n >= 13) {
+      sb += kLatchToNumeric;
+      encodingMode = EncodingMode::kNumeric;
+      textSubMode = SubMode::kAlpha;
+      EncodeNumeric(result, p, n, &sb);
+      p += n;
+    } else {
+      size_t t = DetermineConsecutiveTextCount(result, p);
+      if (t >= 5 || n == len) {
+        if (encodingMode != EncodingMode::kText) {
+          sb += kLatchToText;
+          encodingMode = EncodingMode::kText;
+          textSubMode = SubMode::kAlpha;
         }
+        textSubMode = EncodeText(result, p, t, textSubMode, &sb);
+        p += t;
+      } else {
+        Optional<size_t> b =
+            DetermineConsecutiveBinaryCount(result, &byteArr, p);
+        if (!b)
+          return {};
+
+        size_t b_value = b.value();
+        if (b_value == 0)
+          b_value = 1;
+        if (b_value == 1 && encodingMode == EncodingMode::kText) {
+          EncodeBinary(byteArr, p, 1, EncodingMode::kText, &sb);
+        } else {
+          EncodeBinary(byteArr, p, b_value, encodingMode, &sb);
+          encodingMode = EncodingMode::kByte;
+          textSubMode = SubMode::kAlpha;
+        }
+        p += b_value;
       }
     }
   }
   return sb;
 }
 
-void CBC_PDF417HighLevelEncoder::Inverse() {
-  for (size_t l = 0; l < FX_ArraySize(MIXED); ++l)
-    MIXED[l] = -1;
-
-  for (uint8_t i = 0; i < FX_ArraySize(TEXT_MIXED_RAW); ++i) {
-    uint8_t b = TEXT_MIXED_RAW[i];
-    if (b != 0)
-      MIXED[b] = i;
-  }
-
-  for (size_t l = 0; l < FX_ArraySize(PUNCTUATION); ++l)
-    PUNCTUATION[l] = -1;
-
-  for (uint8_t i = 0; i < FX_ArraySize(TEXT_PUNCTUATION_RAW); ++i) {
-    uint8_t b = TEXT_PUNCTUATION_RAW[i];
-    if (b != 0)
-      PUNCTUATION[b] = i;
-  }
-}
-
-int32_t CBC_PDF417HighLevelEncoder::encodeText(WideString msg,
-                                               int32_t startpos,
-                                               int32_t count,
-                                               WideString& sb,
-                                               int32_t initialSubmode) {
+CBC_PDF417HighLevelEncoder::SubMode CBC_PDF417HighLevelEncoder::EncodeText(
+    const WideString& msg,
+    size_t startpos,
+    size_t count,
+    SubMode initialSubmode,
+    WideString* sb) {
   WideString tmp;
-  int32_t submode = initialSubmode;
-  int32_t idx = 0;
-  while (true) {
+  tmp.Reserve(count);
+  SubMode submode = initialSubmode;
+  size_t idx = 0;
+  while (idx < count) {
     wchar_t ch = msg[startpos + idx];
     switch (submode) {
-      case SUBMODE_ALPHA:
-        if (isAlphaUpper(ch)) {
+      case SubMode::kAlpha:
+        if (IsAlphaUpperOrSpace(ch)) {
           if (ch == ' ')
-            tmp += (wchar_t)26;
+            tmp += 26;
           else
-            tmp += (wchar_t)(ch - 65);
+            tmp += ch - 65;
           break;
         }
-        if (isAlphaLower(ch)) {
-          submode = SUBMODE_LOWER;
-          tmp += (wchar_t)27;
+        if (IsAlphaLowerOrSpace(ch)) {
+          submode = SubMode::kLower;
+          tmp += 27;
           continue;
         }
-        if (isMixed(ch)) {
-          submode = SUBMODE_MIXED;
-          tmp += (wchar_t)28;
+        if (IsMixed(ch)) {
+          submode = SubMode::kMixed;
+          tmp += 28;
           continue;
         }
-        tmp += (wchar_t)29;
-        tmp += PUNCTUATION[ch];
+        if (IsPunctuation(ch)) {
+          tmp += 29;
+          tmp += kPunctuation[ch];
+        }
         break;
-      case SUBMODE_LOWER:
-        if (isAlphaLower(ch)) {
-          if (ch == ' ') {
-            tmp += (wchar_t)26;
-          } else {
-            tmp += (wchar_t)(ch - 97);
-          }
+      case SubMode::kLower:
+        if (IsAlphaLowerOrSpace(ch)) {
+          if (ch == ' ')
+            tmp += 26;
+          else
+            tmp += ch - 97;
           break;
         }
-        if (isAlphaUpper(ch)) {
-          tmp += (wchar_t)27;
-          tmp += (wchar_t)(ch - 65);
+        if (IsAlphaUpperOrSpace(ch)) {
+          tmp += 27;
+          tmp += ch - 65;
           break;
         }
-        if (isMixed(ch)) {
-          submode = SUBMODE_MIXED;
-          tmp += (wchar_t)28;
+        if (IsMixed(ch)) {
+          submode = SubMode::kMixed;
+          tmp += 28;
           continue;
         }
-
-        tmp += (wchar_t)29;
-        tmp += PUNCTUATION[ch];
+        if (IsPunctuation(ch)) {
+          tmp += 29;
+          tmp += kPunctuation[ch];
+        }
         break;
-      case SUBMODE_MIXED:
-        if (isMixed(ch)) {
-          tmp += MIXED[ch];
+      case SubMode::kMixed:
+        if (IsMixed(ch)) {
+          tmp += kMixed[ch];
           break;
         }
-        if (isAlphaUpper(ch)) {
-          submode = SUBMODE_ALPHA;
-          tmp += (wchar_t)28;
+        if (IsAlphaUpperOrSpace(ch)) {
+          submode = SubMode::kAlpha;
+          tmp += 28;
           continue;
         }
-        if (isAlphaLower(ch)) {
-          submode = SUBMODE_LOWER;
-          tmp += (wchar_t)27;
+        if (IsAlphaLowerOrSpace(ch)) {
+          submode = SubMode::kLower;
+          tmp += 27;
           continue;
         }
         if (startpos + idx + 1 < count) {
           wchar_t next = msg[startpos + idx + 1];
-          if (isPunctuation(next)) {
-            submode = SUBMODE_PUNCTUATION;
-            tmp += (wchar_t)25;
+          if (IsPunctuation(next)) {
+            submode = SubMode::kPunctuation;
+            tmp += 25;
             continue;
           }
         }
-        tmp += (wchar_t)29;
-        tmp += PUNCTUATION[ch];
+        if (IsPunctuation(ch)) {
+          tmp += 29;
+          tmp += kPunctuation[ch];
+        }
         break;
       default:
-        if (isPunctuation(ch)) {
-          tmp += PUNCTUATION[ch];
+        if (IsPunctuation(ch)) {
+          tmp += kPunctuation[ch];
           break;
         }
-        submode = SUBMODE_ALPHA;
-        tmp += (wchar_t)29;
+        submode = SubMode::kAlpha;
+        tmp += 29;
         continue;
     }
-    idx++;
-    if (idx >= count) {
-      break;
-    }
+    ++idx;
   }
   wchar_t h = 0;
-  int32_t len = tmp.GetLength();
-  for (int32_t i = 0; i < len; i++) {
+  size_t len = tmp.GetLength();
+  for (size_t i = 0; i < len; i++) {
     bool odd = (i % 2) != 0;
     if (odd) {
-      h = (wchar_t)((h * 30) + tmp[i]);
-      sb += h;
+      h = (h * 30) + tmp[i];
+      *sb += h;
     } else {
       h = tmp[i];
     }
   }
-  if ((len % 2) != 0) {
-    sb += (wchar_t)((h * 30) + 29);
-  }
+  if ((len % 2) != 0)
+    *sb += (h * 30) + 29;
   return submode;
 }
-void CBC_PDF417HighLevelEncoder::encodeBinary(std::vector<uint8_t>* bytes,
-                                              int32_t startpos,
-                                              int32_t count,
-                                              int32_t startmode,
-                                              WideString& sb) {
-  if (count == 1 && startmode == TEXT_COMPACTION) {
-    sb += (wchar_t)SHIFT_TO_BYTE;
-  }
-  int32_t idx = startpos;
-  int32_t i = 0;
+
+void CBC_PDF417HighLevelEncoder::EncodeBinary(pdfium::span<const uint8_t> bytes,
+                                              size_t startpos,
+                                              size_t count,
+                                              EncodingMode startmode,
+                                              WideString* sb) {
+  if (count == 1 && startmode == EncodingMode::kText)
+    *sb += kShiftToByte;
+
+  size_t idx = startpos;
   if (count >= 6) {
-    sb += (wchar_t)LATCH_TO_BYTE;
+    *sb += kLatchToByte;
     wchar_t chars[5];
     while ((startpos + count - idx) >= 6) {
       int64_t t = 0;
-      for (i = 0; i < 6; i++) {
+      for (size_t i = 0; i < 6; i++) {
         t <<= 8;
-        t += (*bytes)[idx + i] & 0xff;
+        t += bytes[idx + i] & 0xff;
       }
-      for (i = 0; i < 5; i++) {
-        chars[i] = (wchar_t)(t % 900);
+      for (size_t i = 0; i < 5; i++) {
+        chars[i] = (t % 900);
         t /= 900;
       }
-      for (i = 4; i >= 0; i--) {
-        sb += (chars[i]);
-      }
+      for (size_t i = 5; i >= 1; i--)
+        *sb += (chars[i - 1]);
       idx += 6;
     }
   }
-  if (idx < startpos + count) {
-    sb += (wchar_t)LATCH_TO_BYTE_PADDED;
-  }
-  for (i = idx; i < startpos + count; i++) {
-    int32_t ch = (*bytes)[i] & 0xff;
-    sb += (wchar_t)ch;
+  if (idx < startpos + count)
+    *sb += kLatchToBytePadded;
+  for (size_t i = idx; i < startpos + count; i++) {
+    int32_t ch = bytes[i] & 0xff;
+    *sb += ch;
   }
 }
-void CBC_PDF417HighLevelEncoder::encodeNumeric(WideString msg,
-                                               int32_t startpos,
-                                               int32_t count,
-                                               WideString& sb) {
-  int32_t idx = 0;
+
+void CBC_PDF417HighLevelEncoder::EncodeNumeric(const WideString& msg,
+                                               size_t startpos,
+                                               size_t count,
+                                               WideString* sb) {
+  size_t idx = 0;
   BigInteger num900 = 900;
   while (idx < count) {
     WideString tmp;
-    int32_t len = 44 < count - idx ? 44 : count - idx;
-    ByteString part =
-        ((wchar_t)'1' + msg.Mid(startpos + idx, len)).UTF8Encode();
+    size_t len = 44 < count - idx ? 44 : count - idx;
+    ByteString part = (L'1' + msg.Substr(startpos + idx, len)).ToUTF8();
     BigInteger bigint = stringToBigInteger(part.c_str());
     do {
       int32_t c = (bigint % num900).toInt();
-      tmp += (wchar_t)(c);
+      tmp += c;
       bigint = bigint / num900;
     } while (!bigint.isZero());
-    for (int32_t i = tmp.GetLength() - 1; i >= 0; i--) {
-      sb += tmp[i];
-    }
+    for (size_t i = tmp.GetLength(); i >= 1; i--)
+      *sb += tmp[i - 1];
     idx += len;
   }
 }
-bool CBC_PDF417HighLevelEncoder::isDigit(wchar_t ch) {
-  return ch >= '0' && ch <= '9';
-}
-bool CBC_PDF417HighLevelEncoder::isAlphaUpper(wchar_t ch) {
-  return ch == ' ' || (ch >= 'A' && ch <= 'Z');
-}
-bool CBC_PDF417HighLevelEncoder::isAlphaLower(wchar_t ch) {
-  return ch == ' ' || (ch >= 'a' && ch <= 'z');
-}
-bool CBC_PDF417HighLevelEncoder::isMixed(wchar_t ch) {
-  return MIXED[ch] != -1;
-}
-bool CBC_PDF417HighLevelEncoder::isPunctuation(wchar_t ch) {
-  return PUNCTUATION[ch] != -1;
-}
-bool CBC_PDF417HighLevelEncoder::isText(wchar_t ch) {
-  return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126);
-}
-int32_t CBC_PDF417HighLevelEncoder::determineConsecutiveDigitCount(
+
+size_t CBC_PDF417HighLevelEncoder::DetermineConsecutiveDigitCount(
     WideString msg,
-    int32_t startpos) {
-  int32_t count = 0;
-  int32_t len = msg.GetLength();
-  int32_t idx = startpos;
+    size_t startpos) {
+  size_t count = 0;
+  size_t len = msg.GetLength();
+  size_t idx = startpos;
   if (idx < len) {
     wchar_t ch = msg[idx];
-    while (isDigit(ch) && idx < len) {
+    while (FXSYS_IsDecimalDigit(ch) && idx < len) {
       count++;
       idx++;
-      if (idx < len) {
+      if (idx < len)
         ch = msg[idx];
-      }
     }
   }
   return count;
 }
-int32_t CBC_PDF417HighLevelEncoder::determineConsecutiveTextCount(
+
+size_t CBC_PDF417HighLevelEncoder::DetermineConsecutiveTextCount(
     WideString msg,
-    int32_t startpos) {
-  int32_t len = msg.GetLength();
-  int32_t idx = startpos;
+    size_t startpos) {
+  size_t len = msg.GetLength();
+  size_t idx = startpos;
   while (idx < len) {
     wchar_t ch = msg[idx];
-    int32_t numericCount = 0;
-    while (numericCount < 13 && isDigit(ch) && idx < len) {
+    size_t numericCount = 0;
+    while (numericCount < 13 && FXSYS_IsDecimalDigit(ch) && idx < len) {
       numericCount++;
       idx++;
-      if (idx < len) {
+      if (idx < len)
         ch = msg[idx];
-      }
     }
-    if (numericCount >= 13) {
+    if (numericCount >= 13)
       return idx - startpos - numericCount;
-    }
-    if (numericCount > 0) {
+    if (numericCount > 0)
       continue;
-    }
     ch = msg[idx];
-    if (!isText(ch)) {
+    if (!IsText(ch))
       break;
-    }
     idx++;
   }
   return idx - startpos;
 }
-int32_t CBC_PDF417HighLevelEncoder::determineConsecutiveBinaryCount(
+
+Optional<size_t> CBC_PDF417HighLevelEncoder::DetermineConsecutiveBinaryCount(
     WideString msg,
     std::vector<uint8_t>* bytes,
-    int32_t startpos,
-    int32_t& e) {
-  int32_t len = msg.GetLength();
-  int32_t idx = startpos;
+    size_t startpos) {
+  size_t len = msg.GetLength();
+  size_t idx = startpos;
   while (idx < len) {
     wchar_t ch = msg[idx];
-    int32_t numericCount = 0;
-    while (numericCount < 13 && isDigit(ch)) {
+    size_t numericCount = 0;
+    while (numericCount < 13 && FXSYS_IsDecimalDigit(ch)) {
       numericCount++;
-      int32_t i = idx + numericCount;
-      if (i >= len) {
+      size_t i = idx + numericCount;
+      if (i >= len)
         break;
-      }
       ch = msg[i];
     }
-    if (numericCount >= 13) {
+    if (numericCount >= 13)
       return idx - startpos;
-    }
-    int32_t textCount = 0;
-    while (textCount < 5 && isText(ch)) {
+
+    size_t textCount = 0;
+    while (textCount < 5 && IsText(ch)) {
       textCount++;
-      int32_t i = idx + textCount;
-      if (i >= len) {
+      size_t i = idx + textCount;
+      if (i >= len)
         break;
-      }
       ch = msg[i];
     }
-    if (textCount >= 5) {
+    if (textCount >= 5)
       return idx - startpos;
-    }
     ch = msg[idx];
-    if ((*bytes)[idx] == 63 && ch != '?') {
-      e = BCExceptionNonEncodableCharacterDetected;
-      return -1;
-    }
+    if ((*bytes)[idx] == 63 && ch != '?')
+      return {};
     idx++;
   }
   return idx - startpos;
diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
index 2701c81..df1cd54 100644
--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
+++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
@@ -10,66 +10,50 @@
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
-#include "fxbarcode/pdf417/BC_PDF417Compaction.h"
+#include "fxbarcode/pdf417/BC_PDF417.h"
+#include "third_party/base/optional.h"
+#include "third_party/base/span.h"
 
 class CBC_PDF417HighLevelEncoder {
  public:
-  static WideString encodeHighLevel(WideString msg,
-                                    Compaction compaction,
-                                    int32_t& e);
-  static void Inverse();
-  static void Initialize();
-  static void Finalize();
+  CBC_PDF417HighLevelEncoder() = delete;
+  ~CBC_PDF417HighLevelEncoder() = delete;
+
+  static Optional<WideString> EncodeHighLevel(WideStringView msg);
 
  private:
-  static int32_t TEXT_COMPACTION;
-  static int32_t BYTE_COMPACTION;
-  static int32_t NUMERIC_COMPACTION;
-  static int32_t SUBMODE_PUNCTUATION;
-  static int32_t LATCH_TO_TEXT;
-  static int32_t LATCH_TO_BYTE_PADDED;
-  static int32_t LATCH_TO_NUMERIC;
-  static int32_t SHIFT_TO_BYTE;
-  static int32_t LATCH_TO_BYTE;
-  static uint8_t TEXT_MIXED_RAW[];
-  static uint8_t TEXT_PUNCTUATION_RAW[];
-  static int32_t MIXED[128];
-  static int32_t PUNCTUATION[128];
-  static int32_t encodeText(WideString msg,
-                            int32_t startpos,
-                            int32_t count,
-                            WideString& sb,
-                            int32_t initialSubmode);
-  static void encodeBinary(std::vector<uint8_t>* bytes,
-                           int32_t startpos,
-                           int32_t count,
-                           int32_t startmode,
-                           WideString& sb);
-  static void encodeNumeric(WideString msg,
-                            int32_t startpos,
-                            int32_t count,
-                            WideString& sb);
-  static bool isDigit(wchar_t ch);
-  static bool isAlphaUpper(wchar_t ch);
-  static bool isAlphaLower(wchar_t ch);
-  static bool isMixed(wchar_t ch);
-  static bool isPunctuation(wchar_t ch);
-  static bool isText(wchar_t ch);
-  static int32_t determineConsecutiveDigitCount(WideString msg,
-                                                int32_t startpos);
-  static int32_t determineConsecutiveTextCount(WideString msg,
-                                               int32_t startpos);
-  static int32_t determineConsecutiveBinaryCount(WideString msg,
-                                                 std::vector<uint8_t>* bytes,
-                                                 int32_t startpos,
-                                                 int32_t& e);
+  enum class EncodingMode { kUnknown = 0, kText, kByte, kNumeric };
 
-  friend class PDF417HighLevelEncoder_EncodeNumeric_Test;
-  friend class PDF417HighLevelEncoder_EncodeBinary_Test;
-  friend class PDF417HighLevelEncoder_EncodeText_Test;
-  friend class PDF417HighLevelEncoder_ConsecutiveDigitCount_Test;
-  friend class PDF417HighLevelEncoder_ConsecutiveTextCount_Test;
-  friend class PDF417HighLevelEncoder_ConsecutiveBinaryCount_Test;
+  enum class SubMode { kAlpha = 0, kLower, kMixed, kPunctuation };
+
+  static SubMode EncodeText(const WideString& msg,
+                            size_t startpos,
+                            size_t count,
+                            SubMode initialSubmode,
+                            WideString* sb);
+  static void EncodeBinary(pdfium::span<const uint8_t> bytes,
+                           size_t startpos,
+                           size_t count,
+                           EncodingMode startmode,
+                           WideString* sb);
+  static void EncodeNumeric(const WideString& msg,
+                            size_t startpos,
+                            size_t count,
+                            WideString* sb);
+  static size_t DetermineConsecutiveDigitCount(WideString msg, size_t startpos);
+  static size_t DetermineConsecutiveTextCount(WideString msg, size_t startpos);
+  static Optional<size_t> DetermineConsecutiveBinaryCount(
+      WideString msg,
+      std::vector<uint8_t>* bytes,
+      size_t startpos);
+
+  friend class PDF417HighLevelEncoderTest_ConsecutiveBinaryCount_Test;
+  friend class PDF417HighLevelEncoderTest_ConsecutiveDigitCount_Test;
+  friend class PDF417HighLevelEncoderTest_ConsecutiveTextCount_Test;
+  friend class PDF417HighLevelEncoderTest_EncodeBinary_Test;
+  friend class PDF417HighLevelEncoderTest_EncodeHighLevel_Test;
+  friend class PDF417HighLevelEncoderTest_EncodeNumeric_Test;
+  friend class PDF417HighLevelEncoderTest_EncodeText_Test;
 };
 
 #endif  // FXBARCODE_PDF417_BC_PDF417HIGHLEVELENCODER_H_
diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp
index ad4a59a..4ca5c8b 100644
--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp
+++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp
@@ -3,67 +3,109 @@
 // found in the LICENSE file.
 
 #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(PDF417HighLevelEncoder, EncodeHighLevel) {
+TEST(PDF417HighLevelEncoderTest, EncodeHighLevel) {
+  static constexpr struct EncodeHighLevelCase {
+    const wchar_t* input;
+    const wchar_t* expected;
+    int expected_length;
+  } kEncodeHighLevelCases[] = {
+      // Empty string encodes as empty string.
+      {L"", L"", 0},
+
+      // Binary mode with digit.
+      {L"\x000b\x000b\x0030", L"\x0385\x000b\x000b\x0030", 4},
+
+      // Text mode.
+      {L"xxxxxxXx", L"\x0384\x0341\x02c9\x02c9\x02cd\x02c9", 6},
+
+      // Text mode with punctuation.
+      {L"xxxxxx!x", L"\x0384\x0341\x02c9\x02c9\x02cf\x0143", 6},
+
+      // Text mode with mixed submode.
+      {L"xxxxxx0x", L"\x0384\x0341\x02c9\x02c9\x02ce\x001b\x02cf", 7},
+
+      // Text mode with mixed submode, and space in alpha submode.
+      {L"xxxxxx0X ", L"\x0384\x0341\x02c9\x02c9\x02ce\x001c\x02cc", 7},
+
+      // Text mode to binary mode.
+      {L"xxxxxx\x0b", L"\x0384\x0341\x02c9\x02c9\x02cf\x0391\x0385\x000b", 8},
+
+      // 13 consecutive digits triggers numeric encoding.
+      {L"0000000000000", L"\x0386\x000f\x00d9\x017b\x000b\x0064", 6},
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(kEncodeHighLevelCases); ++i) {
+    const EncodeHighLevelCase& testcase = kEncodeHighLevelCases[i];
+    WideStringView input(testcase.input);
+    WideString expected(testcase.expected, testcase.expected_length);
+    Optional<WideString> result =
+        CBC_PDF417HighLevelEncoder::EncodeHighLevel(input);
+    ASSERT_TRUE(result.has_value());
+    EXPECT_EQ(expected, result.value()) << " for case number " << i;
+  }
+}
+
+TEST(PDF417HighLevelEncoderTest, EncodeText) {
   // TODO(tsepez): implement test cases.
 }
 
-TEST(PDF417HighLevelEncoder, EncodeText) {
-  // TODO(tsepez): implement test cases.
-}
-
-TEST(PDF417HighLevelEncoder, EncodeBinary) {
-  struct EncodeBinaryCase {
+TEST(PDF417HighLevelEncoderTest, EncodeBinary) {
+  static constexpr struct EncodeBinaryCase {
     const char* input;
     int offset;
     int count;
-    int startmode;
+    CBC_PDF417HighLevelEncoder::EncodingMode startmode;
     const wchar_t* expected;
     int expected_length;
-  } encode_binary_cases[] = {
+  } kEncodeBinaryCases[] = {
       // Empty string encodes as empty string.
-      {"", 0, 0, CBC_PDF417HighLevelEncoder::TEXT_COMPACTION, L"", 0},
+      {"", 0, 0, CBC_PDF417HighLevelEncoder::EncodingMode::kText, L"", 0},
+
+      // Single digit encodes with a shift-to byte.
+      {"x", 0, 1, CBC_PDF417HighLevelEncoder::EncodingMode::kText,
+       L"\x0391\x0385x", 3},
 
       // Fewer than 6 characters encodes as prefix without compaction.
-      {"xxxxx", 0, 5, CBC_PDF417HighLevelEncoder::TEXT_COMPACTION,
+      {"xxxxx", 0, 5, CBC_PDF417HighLevelEncoder::EncodingMode::kText,
        L"\x0385xxxxx", 6},
 
-      // 6 charcters triggerst text encoding compaction.
-      {"xxxxxx", 0, 6, CBC_PDF417HighLevelEncoder::TEXT_COMPACTION,
+      // 6 charcters triggers text encoding compaction.
+      {"xxxxxx", 0, 6, CBC_PDF417HighLevelEncoder::EncodingMode::kText,
        L"\u039c\u00c9\u031f\u012a\u00d2\u02d0", 6},
 
       // Same result if initially in numeric compaction mode.
-      {"xxxxxx", 0, 6, CBC_PDF417HighLevelEncoder::NUMERIC_COMPACTION,
+      {"xxxxxx", 0, 6, CBC_PDF417HighLevelEncoder::EncodingMode::kNumeric,
        L"\u039c\u00c9\u031f\u012a\u00d2\u02d0", 6},
   };
 
-  CBC_PDF417HighLevelEncoder::Initialize();
-  for (size_t i = 0; i < FX_ArraySize(encode_binary_cases); ++i) {
-    EncodeBinaryCase* ptr = &encode_binary_cases[i];
+  for (size_t i = 0; i < FX_ArraySize(kEncodeBinaryCases); ++i) {
+    const EncodeBinaryCase& testcase = kEncodeBinaryCases[i];
     std::vector<uint8_t> input_array;
-    size_t input_length = strlen(ptr->input);
+    size_t input_length = strlen(testcase.input);
     input_array.resize(input_length);
     for (size_t j = 0; j < input_length; ++j) {
-      input_array[j] = ptr->input[j];
+      input_array[j] = testcase.input[j];
     }
-    WideString expected(ptr->expected, ptr->expected_length);
+    WideString expected(testcase.expected, testcase.expected_length);
     WideString result;
-    CBC_PDF417HighLevelEncoder::encodeBinary(
-        &input_array, ptr->offset, ptr->count, ptr->startmode, result);
+    CBC_PDF417HighLevelEncoder::EncodeBinary(input_array, testcase.offset,
+                                             testcase.count, testcase.startmode,
+                                             &result);
     EXPECT_EQ(expected, result) << " for case number " << i;
   }
-  CBC_PDF417HighLevelEncoder::Finalize();
 }
 
-TEST(PDF417HighLevelEncoder, EncodeNumeric) {
-  struct EncodeNumericCase {
+TEST(PDF417HighLevelEncoderTest, EncodeNumeric) {
+  static constexpr struct EncodeNumericCase {
     const wchar_t* input;
     int offset;
     int count;
     const wchar_t* expected;
     int expected_length;
-  } encode_numeric_cases[] = {
+  } kEncodeNumericCases[] = {
       // Empty string encodes as empty string.
       {L"", 0, 0, L"", 0},
 
@@ -106,26 +148,24 @@
        18},
   };
 
-  CBC_PDF417HighLevelEncoder::Initialize();
-  for (size_t i = 0; i < FX_ArraySize(encode_numeric_cases); ++i) {
-    EncodeNumericCase* ptr = &encode_numeric_cases[i];
-    WideString input(ptr->input);
-    WideString expected(ptr->expected, ptr->expected_length);
+  for (size_t i = 0; i < FX_ArraySize(kEncodeNumericCases); ++i) {
+    const EncodeNumericCase& testcase = kEncodeNumericCases[i];
+    WideString input(testcase.input);
+    WideString expected(testcase.expected, testcase.expected_length);
     WideString result;
-    CBC_PDF417HighLevelEncoder::encodeNumeric(input, ptr->offset, ptr->count,
-                                              result);
+    CBC_PDF417HighLevelEncoder::EncodeNumeric(input, testcase.offset,
+                                              testcase.count, &result);
     EXPECT_EQ(expected, result) << " for case number " << i;
   }
-  CBC_PDF417HighLevelEncoder::Finalize();
 }
 
-TEST(PDF417HighLevelEncoder, ConsecutiveDigitCount) {
-  struct ConsecutiveDigitCase {
+TEST(PDF417HighLevelEncoderTest, ConsecutiveDigitCount) {
+  static constexpr struct ConsecutiveDigitCase {
     const wchar_t* input;
     int offset;
     int expected_count;
-  } consecutive_digit_cases[] = {
-      // Empty string contains 0 consecuitve digits.
+  } kConsecutiveDigitCases[] = {
+      // Empty string contains 0 consecutive digits.
       {L"", 0, 0},
 
       // Single non-digit character contains 0 consecutive digits.
@@ -153,24 +193,23 @@
       {L"123FOO45678", 6, 5},
   };
 
-  CBC_PDF417HighLevelEncoder::Initialize();
-  for (size_t i = 0; i < FX_ArraySize(consecutive_digit_cases); ++i) {
-    ConsecutiveDigitCase* ptr = &consecutive_digit_cases[i];
-    WideString input(ptr->input);
+  for (size_t i = 0; i < FX_ArraySize(kConsecutiveDigitCases); ++i) {
+    const ConsecutiveDigitCase& testcase = kConsecutiveDigitCases[i];
+    WideString input(testcase.input);
     int actual_count =
-        CBC_PDF417HighLevelEncoder::determineConsecutiveDigitCount(input,
-                                                                   ptr->offset);
-    EXPECT_EQ(ptr->expected_count, actual_count) << " for case number " << i;
+        CBC_PDF417HighLevelEncoder::DetermineConsecutiveDigitCount(
+            input, testcase.offset);
+    EXPECT_EQ(testcase.expected_count, actual_count)
+        << " for case number " << i;
   }
-  CBC_PDF417HighLevelEncoder::Finalize();
 }
 
-TEST(PDF417HighLevelEncoder, ConsecutiveTextCount) {
-  struct ConsecutiveTextCase {
+TEST(PDF417HighLevelEncoderTest, ConsecutiveTextCount) {
+  static constexpr struct ConsecutiveTextCase {
     const wchar_t* input;
     int offset;
     int expected_count;
-  } consecutive_text_cases[] = {
+  } kConsecutiveTextCases[] = {
       // Empty string contains 0 consecutive text characters.
       {L"", 0, 0},
 
@@ -214,16 +253,17 @@
       {L"XXX121XXX12345678901234", 0, 9},
   };
 
-  CBC_PDF417HighLevelEncoder::Initialize();
-  for (size_t i = 0; i < FX_ArraySize(consecutive_text_cases); ++i) {
-    ConsecutiveTextCase* ptr = &consecutive_text_cases[i];
-    WideString input(ptr->input);
+  for (size_t i = 0; i < FX_ArraySize(kConsecutiveTextCases); ++i) {
+    const ConsecutiveTextCase& testcase = kConsecutiveTextCases[i];
+    WideString input(testcase.input);
     int actual_count =
-        CBC_PDF417HighLevelEncoder::determineConsecutiveTextCount(input,
-                                                                  ptr->offset);
-    EXPECT_EQ(ptr->expected_count, actual_count) << " for case number " << i;
+        CBC_PDF417HighLevelEncoder::DetermineConsecutiveTextCount(
+            input, testcase.offset);
+    EXPECT_EQ(testcase.expected_count, actual_count)
+        << " for case number " << i;
   }
-  CBC_PDF417HighLevelEncoder::Finalize();
 }
 
-TEST(PDF417HighLevelEncoder, ConsecutiveBinaryCount) {}
+TEST(PDF417HighLevelEncoderTest, ConsecutiveBinaryCount) {
+  // TODO(tsepez): implement test cases.
+}
diff --git a/fxbarcode/pdf417/BC_PDF417Writer.cpp b/fxbarcode/pdf417/BC_PDF417Writer.cpp
index 4fec974..f9f3624 100644
--- a/fxbarcode/pdf417/BC_PDF417Writer.cpp
+++ b/fxbarcode/pdf417/BC_PDF417Writer.cpp
@@ -23,89 +23,66 @@
 #include "fxbarcode/pdf417/BC_PDF417Writer.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "fxbarcode/BC_TwoDimWriter.h"
-#include "fxbarcode/common/BC_CommonBitArray.h"
 #include "fxbarcode/common/BC_CommonBitMatrix.h"
 #include "fxbarcode/pdf417/BC_PDF417.h"
 #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h"
-#include "fxbarcode/pdf417/BC_PDF417Compaction.h"
+#include "third_party/base/stl_util.h"
 
-CBC_PDF417Writer::CBC_PDF417Writer() {
-  m_bFixedSize = false;
-}
-CBC_PDF417Writer::~CBC_PDF417Writer() {
-  m_bTruncated = true;
-}
+CBC_PDF417Writer::CBC_PDF417Writer() : CBC_TwoDimWriter(false) {}
+
+CBC_PDF417Writer::~CBC_PDF417Writer() = default;
+
 bool CBC_PDF417Writer::SetErrorCorrectionLevel(int32_t level) {
   if (level < 0 || level > 8) {
     return false;
   }
-  m_iCorrectLevel = level;
+  set_error_correction_level(level);
   return true;
 }
-void CBC_PDF417Writer::SetTruncated(bool truncated) {
-  m_bTruncated = truncated;
-}
 
-uint8_t* CBC_PDF417Writer::Encode(const WideString& contents,
-                                  int32_t& outWidth,
-                                  int32_t& outHeight) {
+std::vector<uint8_t> CBC_PDF417Writer::Encode(WideStringView contents,
+                                              int32_t* pOutWidth,
+                                              int32_t* pOutHeight) {
+  std::vector<uint8_t> results;
   CBC_PDF417 encoder;
   int32_t col = (m_Width / m_ModuleWidth - 69) / 17;
   int32_t row = m_Height / (m_ModuleWidth * 20);
   if (row >= 3 && row <= 90 && col >= 1 && col <= 30)
-    encoder.setDimensions(col, col, row, row);
+    encoder.setDimensions(col, 1, row, 3);
   else if (col >= 1 && col <= 30)
     encoder.setDimensions(col, col, 90, 3);
   else if (row >= 3 && row <= 90)
     encoder.setDimensions(30, 1, row, row);
-  if (!encoder.generateBarcodeLogic(contents, m_iCorrectLevel))
-    return nullptr;
+  if (!encoder.GenerateBarcodeLogic(contents, error_correction_level()))
+    return results;
 
-  int32_t lineThickness = 2;
-  int32_t aspectRatio = 4;
   CBC_BarcodeMatrix* barcodeMatrix = encoder.getBarcodeMatrix();
-  std::vector<uint8_t> originalScale = barcodeMatrix->getScaledMatrix(
-      lineThickness, aspectRatio * lineThickness);
-  int32_t width = outWidth;
-  int32_t height = outHeight;
-  outWidth = barcodeMatrix->getWidth();
-  outHeight = barcodeMatrix->getHeight();
-  bool rotated = false;
-  if ((height > width) ^ (outWidth < outHeight)) {
-    rotateArray(originalScale, outHeight, outWidth);
-    rotated = true;
-    int32_t temp = outHeight;
-    outHeight = outWidth;
-    outWidth = temp;
+  std::vector<uint8_t> matrixData = barcodeMatrix->toBitArray();
+  int32_t matrixWidth = barcodeMatrix->getWidth();
+  int32_t matrixHeight = barcodeMatrix->getHeight();
+
+  if (matrixWidth < matrixHeight) {
+    RotateArray(&matrixData, matrixHeight, matrixWidth);
+    std::swap(matrixWidth, matrixHeight);
   }
-  int32_t scaleX = width / outWidth;
-  int32_t scaleY = height / outHeight;
-  int32_t scale = std::min(scaleX, scaleY);
-  if (scale > 1) {
-    originalScale = barcodeMatrix->getScaledMatrix(
-        scale * lineThickness, scale * aspectRatio * lineThickness);
-    if (rotated) {
-      rotateArray(originalScale, outHeight, outWidth);
-      int32_t temp = outHeight;
-      outHeight = outWidth;
-      outWidth = temp;
-    }
-  }
-  uint8_t* result = FX_Alloc2D(uint8_t, outHeight, outWidth);
-  memcpy(result, originalScale.data(), outHeight * outWidth);
-  return result;
+  *pOutWidth = matrixWidth;
+  *pOutHeight = matrixHeight;
+  results = pdfium::Vector2D<uint8_t>(*pOutWidth, *pOutHeight);
+  memcpy(results.data(), matrixData.data(), *pOutWidth * *pOutHeight);
+  return results;
 }
 
-void CBC_PDF417Writer::rotateArray(std::vector<uint8_t>& bitarray,
+void CBC_PDF417Writer::RotateArray(std::vector<uint8_t>* bitarray,
                                    int32_t height,
                                    int32_t width) {
-  std::vector<uint8_t> temp = bitarray;
+  std::vector<uint8_t> temp = *bitarray;
   for (int32_t ii = 0; ii < height; ii++) {
     int32_t inverseii = height - ii - 1;
     for (int32_t jj = 0; jj < width; jj++) {
-      bitarray[jj * height + inverseii] = temp[ii * width + jj];
+      (*bitarray)[jj * height + inverseii] = temp[ii * width + jj];
     }
   }
 }
diff --git a/fxbarcode/pdf417/BC_PDF417Writer.h b/fxbarcode/pdf417/BC_PDF417Writer.h
index ef59616..10f069a 100644
--- a/fxbarcode/pdf417/BC_PDF417Writer.h
+++ b/fxbarcode/pdf417/BC_PDF417Writer.h
@@ -13,25 +13,22 @@
 #include "core/fxcrt/fx_system.h"
 #include "fxbarcode/BC_TwoDimWriter.h"
 
-class CBC_PDF417Writer : public CBC_TwoDimWriter {
+class CBC_PDF417Writer final : public CBC_TwoDimWriter {
  public:
   CBC_PDF417Writer();
   ~CBC_PDF417Writer() override;
 
-  uint8_t* Encode(const WideString& contents,
-                  int32_t& outWidth,
-                  int32_t& outHeight);
+  std::vector<uint8_t> Encode(WideStringView contents,
+                              int32_t* pOutWidth,
+                              int32_t* pOutHeight);
 
   // CBC_TwoDimWriter
   bool SetErrorCorrectionLevel(int32_t level) override;
 
-  void SetTruncated(bool truncated);
-
  private:
-  void rotateArray(std::vector<uint8_t>& bitarray,
+  void RotateArray(std::vector<uint8_t>* bitarray,
                    int32_t width,
                    int32_t height);
-  bool m_bTruncated;
 };
 
 #endif  // FXBARCODE_PDF417_BC_PDF417WRITER_H_
diff --git a/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp b/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp
new file mode 100644
index 0000000..1c09d78
--- /dev/null
+++ b/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp
@@ -0,0 +1,820 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxbarcode/pdf417/BC_PDF417Writer.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CBC_PDF417WriterTest : public testing::Test {
+ public:
+  CBC_PDF417WriterTest() = default;
+  ~CBC_PDF417WriterTest() override = default;
+
+  // testing::Test:
+  void SetUp() override { BC_Library_Init(); }
+  void TearDown() override { BC_Library_Destroy(); }
+};
+
+TEST_F(CBC_PDF417WriterTest, Encode) {
+  CBC_PDF417Writer writer;
+  int32_t width;
+  int32_t height;
+
+  {
+    static constexpr int kExpectedWidth = 579;
+    static constexpr int kExpectedHeight = 16;
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+        0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
+        0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1,
+        0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+        1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
+        1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1,
+        0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
+        0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1,
+        1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
+        1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1,
+        0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1,
+        0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1,
+        0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
+        1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1,
+        1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
+        1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0,
+        1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1,
+        1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1,
+        1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1};
+    std::vector<uint8_t> data = writer.Encode(L"", &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedWidth, width);
+    ASSERT_EQ(kExpectedHeight, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedWidth = 579;
+    static constexpr int kExpectedHeight = 16;
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+        0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0,
+        0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1,
+        0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
+        0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1,
+        0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+        1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
+        1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1,
+        0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
+        0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1,
+        1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
+        1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1,
+        0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1,
+        0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1,
+        0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
+        1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1,
+        1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
+        1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
+        1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
+        1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+        0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+        0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0,
+        1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+        0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+        1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
+        0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
+        1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+        1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1,
+        1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
+        0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,
+        1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
+        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+        0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+        1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+        0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+        1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0,
+        1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1,
+        1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0,
+        0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1};
+    std::vector<uint8_t> data = writer.Encode(L"hello world", &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedWidth, width);
+    ASSERT_EQ(kExpectedHeight, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+}
diff --git a/fxbarcode/qrcode/BC_QRCodeWriter.cpp b/fxbarcode/qrcode/BC_QRCodeWriter.cpp
index eb35af3..1eead62 100644
--- a/fxbarcode/qrcode/BC_QRCodeWriter.cpp
+++ b/fxbarcode/qrcode/BC_QRCodeWriter.cpp
@@ -20,45 +20,34 @@
  * limitations under the License.
  */
 
-#include "fxbarcode/BC_TwoDimWriter.h"
+#include "fxbarcode/qrcode/BC_QRCodeWriter.h"
+
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
-#include "fxbarcode/qrcode/BC_QRCodeWriter.h"
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderEncoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
 #include "fxbarcode/qrcode/BC_QRCoderMode.h"
 #include "fxbarcode/qrcode/BC_QRCoderVersion.h"
+#include "third_party/base/stl_util.h"
 
-CBC_QRCodeWriter::CBC_QRCodeWriter() {
-  m_bFixedSize = true;
-  m_iCorrectLevel = 1;
-}
+CBC_QRCodeWriter::CBC_QRCodeWriter() : CBC_TwoDimWriter(true) {}
 
-CBC_QRCodeWriter::~CBC_QRCodeWriter() {}
-
-void CBC_QRCodeWriter::ReleaseAll() {
-  delete CBC_ReedSolomonGF256::QRCodeField;
-  CBC_ReedSolomonGF256::QRCodeField = nullptr;
-  delete CBC_ReedSolomonGF256::DataMatrixField;
-  CBC_ReedSolomonGF256::DataMatrixField = nullptr;
-  CBC_QRCoderMode::Destroy();
-  CBC_QRCoderErrorCorrectionLevel::Destroy();
-  CBC_QRCoderVersion::Destroy();
-}
+CBC_QRCodeWriter::~CBC_QRCodeWriter() = default;
 
 bool CBC_QRCodeWriter::SetErrorCorrectionLevel(int32_t level) {
   if (level < 0 || level > 3) {
     return false;
   }
-  m_iCorrectLevel = level;
+  set_error_correction_level(level);
   return true;
 }
 
-uint8_t* CBC_QRCodeWriter::Encode(const WideString& contents,
-                                  int32_t ecLevel,
-                                  int32_t& outWidth,
-                                  int32_t& outHeight) {
+std::vector<uint8_t> CBC_QRCodeWriter::Encode(WideStringView contents,
+                                              int32_t ecLevel,
+                                              int32_t* pOutWidth,
+                                              int32_t* pOutHeight) {
+  std::vector<uint8_t> results;
   CBC_QRCoderErrorCorrectionLevel* ec = nullptr;
   switch (ecLevel) {
     case 0:
@@ -74,15 +63,16 @@
       ec = CBC_QRCoderErrorCorrectionLevel::H;
       break;
     default:
-      return nullptr;
+      return results;
   }
   CBC_QRCoder qr;
   if (!CBC_QRCoderEncoder::Encode(contents, ec, &qr))
-    return nullptr;
+    return results;
 
-  outWidth = qr.GetMatrixWidth();
-  outHeight = qr.GetMatrixWidth();
-  uint8_t* result = FX_Alloc2D(uint8_t, outWidth, outHeight);
-  memcpy(result, qr.GetMatrix()->GetArray(), outWidth * outHeight);
-  return result;
+  *pOutWidth = qr.GetMatrixWidth();
+  *pOutHeight = qr.GetMatrixWidth();
+  results = pdfium::Vector2D<uint8_t>(*pOutWidth, *pOutHeight);
+  memcpy(results.data(), qr.GetMatrix()->GetArray().data(),
+         *pOutWidth * *pOutHeight);
+  return results;
 }
diff --git a/fxbarcode/qrcode/BC_QRCodeWriter.h b/fxbarcode/qrcode/BC_QRCodeWriter.h
index 3b3efc9..3f2cca0 100644
--- a/fxbarcode/qrcode/BC_QRCodeWriter.h
+++ b/fxbarcode/qrcode/BC_QRCodeWriter.h
@@ -7,20 +7,19 @@
 #ifndef FXBARCODE_QRCODE_BC_QRCODEWRITER_H_
 #define FXBARCODE_QRCODE_BC_QRCODEWRITER_H_
 
+#include <vector>
+
 #include "fxbarcode/BC_TwoDimWriter.h"
 
-class CBC_TwoDimWriter;
-class CBC_QRCodeWriter : public CBC_TwoDimWriter {
+class CBC_QRCodeWriter final : public CBC_TwoDimWriter {
  public:
   CBC_QRCodeWriter();
   ~CBC_QRCodeWriter() override;
 
-  static void ReleaseAll();
-
-  uint8_t* Encode(const WideString& contents,
-                  int32_t ecLevel,
-                  int32_t& outWidth,
-                  int32_t& outHeight);
+  std::vector<uint8_t> Encode(WideStringView contents,
+                              int32_t ecLevel,
+                              int32_t* pOutWidth,
+                              int32_t* pOutHeight);
 
   // CBC_TwoDimWriter
   bool SetErrorCorrectionLevel(int32_t level) override;
diff --git a/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp b/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp
new file mode 100644
index 0000000..709d497
--- /dev/null
+++ b/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp
@@ -0,0 +1,309 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxbarcode/qrcode/BC_QRCodeWriter.h"
+
+#include <vector>
+
+#include "core/fxcrt/fx_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CBC_QRCodeWriterTest : public testing::Test {
+ public:
+  CBC_QRCodeWriterTest() = default;
+  ~CBC_QRCodeWriterTest() override = default;
+
+  // testing::Test:
+  void SetUp() override { BC_Library_Init(); }
+  void TearDown() override { BC_Library_Destroy(); }
+};
+
+TEST_F(CBC_QRCodeWriterTest, Encode) {
+  CBC_QRCodeWriter writer;
+  int32_t width;
+  int32_t height;
+
+  {
+    static constexpr int kExpectedDimension = 21;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0,
+        1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
+        0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1,
+        1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1
+    };
+    // clang-format on
+    std::vector<uint8_t> data = writer.Encode(L"", 0, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 21;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0,
+        0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
+        1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1,
+        1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1
+    };
+    // clang-format on
+    std::vector<uint8_t> data = writer.Encode(L"", 1, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 21;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0,
+        1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1,
+        1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+        0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
+        1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0
+    };
+    // clang-format on
+    std::vector<uint8_t> data = writer.Encode(L"", 2, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 21;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1,
+        0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0,
+        0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0,
+        0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0,
+        1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0
+    };
+    // clang-format on
+    std::vector<uint8_t> data = writer.Encode(L"", 3, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 21;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
+        0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1,
+        1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1,
+        0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0
+    };
+    // clang-format on
+    std::vector<uint8_t> data =
+        writer.Encode(L"hello world", 0, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 21;
+    // clang-format off
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1,
+        0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
+        0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1,
+        0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0,
+        0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+        1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
+        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0,
+        1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+        1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0
+    };
+    // clang-format on
+    std::vector<uint8_t> data =
+        writer.Encode(L"hello world", 1, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 25;
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0,
+        0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1,
+        1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1,
+        1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0,
+        1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1,
+        0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
+        0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0,
+        0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+        0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0,
+        0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
+        1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1,
+        1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0,
+        1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0,
+        0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+        1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0,
+        0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1,
+        1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1,
+        1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0,
+        1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1,
+        0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1,
+        1};
+    std::vector<uint8_t> data =
+        writer.Encode(L"hello world", 2, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+  {
+    static constexpr int kExpectedDimension = 25;
+    static constexpr uint8_t kExpectedData[] = {
+        1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
+        1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0,
+        0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1,
+        1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1,
+        1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0,
+        1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1,
+        0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,
+        1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
+        1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+        1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1,
+        1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0,
+        1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
+        0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0,
+        0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1,
+        0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+        1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+        0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1,
+        1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1,
+        1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0,
+        1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1,
+        0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1,
+        1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
+        1};
+    std::vector<uint8_t> data =
+        writer.Encode(L"hello world", 3, &width, &height);
+    ASSERT_EQ(FX_ArraySize(kExpectedData), data.size());
+    ASSERT_EQ(kExpectedDimension, width);
+    ASSERT_EQ(kExpectedDimension, height);
+    for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i)
+      EXPECT_EQ(kExpectedData[i], data[i]) << i;
+  }
+}
diff --git a/fxbarcode/qrcode/BC_QRCoder.cpp b/fxbarcode/qrcode/BC_QRCoder.cpp
index b0a24fa..46f80fc 100644
--- a/fxbarcode/qrcode/BC_QRCoder.cpp
+++ b/fxbarcode/qrcode/BC_QRCoder.cpp
@@ -26,24 +26,10 @@
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
 #include "fxbarcode/qrcode/BC_QRCoderMode.h"
-#include "fxbarcode/utils.h"
 
-CBC_QRCoder::CBC_QRCoder()
-    : m_mode(nullptr),
-      m_ecLevel(nullptr),
-      m_version(-1),
-      m_matrixWidth(-1),
-      m_maskPattern(-1),
-      m_numTotalBytes(-1),
-      m_numDataBytes(-1),
-      m_numECBytes(-1),
-      m_numRSBlocks(-1) {}
+CBC_QRCoder::CBC_QRCoder() = default;
 
-CBC_QRCoder::~CBC_QRCoder() {}
-
-CBC_QRCoderMode* CBC_QRCoder::GetMode() const {
-  return m_mode.Get();
-}
+CBC_QRCoder::~CBC_QRCoder() = default;
 
 const CBC_QRCoderErrorCorrectionLevel* CBC_QRCoder::GetECLevel() const {
   return m_ecLevel.Get();
@@ -69,29 +55,16 @@
   return m_numDataBytes;
 }
 
-int32_t CBC_QRCoder::GetNumECBytes() const {
-  return m_numECBytes;
-}
-
 int32_t CBC_QRCoder::GetNumRSBlocks() const {
   return m_numRSBlocks;
 }
 
-CBC_CommonByteMatrix* CBC_QRCoder::GetMatrix() const {
+const CBC_CommonByteMatrix* CBC_QRCoder::GetMatrix() const {
   return m_matrix.get();
 }
 
-int32_t CBC_QRCoder::At(int32_t x, int32_t y, int32_t& e) {
-  int32_t value = m_matrix->Get(x, y);
-  if (!(value == 0 || value == 1)) {
-    e = BCExceptionValueMustBeEither0or1;
-    return 0;
-  }
-  return value;
-}
-
-bool CBC_QRCoder::IsValid() {
-  return m_mode && m_ecLevel && m_version != -1 && m_matrixWidth != -1 &&
+bool CBC_QRCoder::IsValid() const {
+  return m_ecLevel && m_version != -1 && m_matrixWidth != -1 &&
          m_maskPattern != -1 && m_numTotalBytes != -1 && m_numDataBytes != -1 &&
          m_numECBytes != -1 && m_numRSBlocks != -1 &&
          IsValidMaskPattern(m_maskPattern) &&
@@ -100,10 +73,6 @@
          m_matrix->GetWidth() == m_matrix->GetHeight();
 }
 
-void CBC_QRCoder::SetMode(CBC_QRCoderMode* value) {
-  m_mode = value;
-}
-
 void CBC_QRCoder::SetECLevel(const CBC_QRCoderErrorCorrectionLevel* ecLevel) {
   m_ecLevel = ecLevel;
 }
diff --git a/fxbarcode/qrcode/BC_QRCoder.h b/fxbarcode/qrcode/BC_QRCoder.h
index af53ded..ec9bb09 100644
--- a/fxbarcode/qrcode/BC_QRCoder.h
+++ b/fxbarcode/qrcode/BC_QRCoder.h
@@ -15,30 +15,26 @@
 class CBC_QRCoderMode;
 class CBC_CommonByteMatrix;
 
-class CBC_QRCoder {
+class CBC_QRCoder final {
  public:
   static constexpr int32_t kNumMaskPatterns = 8;
 
   CBC_QRCoder();
-  virtual ~CBC_QRCoder();
+  ~CBC_QRCoder();
 
   static bool IsValidMaskPattern(int32_t maskPattern);
 
-  CBC_QRCoderMode* GetMode() const;
   const CBC_QRCoderErrorCorrectionLevel* GetECLevel() const;
   int32_t GetVersion() const;
   int32_t GetMatrixWidth() const;
   int32_t GetMaskPattern() const;
   int32_t GetNumTotalBytes() const;
   int32_t GetNumDataBytes() const;
-  int32_t GetNumECBytes() const;
   int32_t GetNumRSBlocks() const;
-  CBC_CommonByteMatrix* GetMatrix() const;
+  const CBC_CommonByteMatrix* GetMatrix() const;
 
-  int32_t At(int32_t x, int32_t y, int32_t& e);
-  bool IsValid();
+  bool IsValid() const;
 
-  void SetMode(CBC_QRCoderMode* value);
   void SetECLevel(const CBC_QRCoderErrorCorrectionLevel* ecLevel);
   void SetVersion(int32_t version);
   void SetMatrixWidth(int32_t width);
@@ -50,15 +46,14 @@
   void SetMatrix(std::unique_ptr<CBC_CommonByteMatrix> pMatrix);
 
  private:
-  UnownedPtr<CBC_QRCoderMode> m_mode;
   UnownedPtr<const CBC_QRCoderErrorCorrectionLevel> m_ecLevel;
-  int32_t m_version;
-  int32_t m_matrixWidth;
-  int32_t m_maskPattern;
-  int32_t m_numTotalBytes;
-  int32_t m_numDataBytes;
-  int32_t m_numECBytes;
-  int32_t m_numRSBlocks;
+  int32_t m_version = -1;
+  int32_t m_matrixWidth = -1;
+  int32_t m_maskPattern = -1;
+  int32_t m_numTotalBytes = -1;
+  int32_t m_numDataBytes = -1;
+  int32_t m_numECBytes = -1;
+  int32_t m_numRSBlocks = -1;
   std::unique_ptr<CBC_CommonByteMatrix> m_matrix;
 };
 
diff --git a/fxbarcode/qrcode/BC_QRCoderBitVector.cpp b/fxbarcode/qrcode/BC_QRCoderBitVector.cpp
index 7a41ad0..121fa92 100644
--- a/fxbarcode/qrcode/BC_QRCoderBitVector.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderBitVector.cpp
@@ -22,18 +22,15 @@
 
 #include "fxbarcode/qrcode/BC_QRCoderBitVector.h"
 
-#include "core/fxcrt/fx_memory.h"
-#include "fxbarcode/utils.h"
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/logging.h"
 
-CBC_QRCoderBitVector::CBC_QRCoderBitVector() {}
+CBC_QRCoderBitVector::CBC_QRCoderBitVector() = default;
 
-CBC_QRCoderBitVector::~CBC_QRCoderBitVector() {}
+CBC_QRCoderBitVector::~CBC_QRCoderBitVector() = default;
 
-int32_t CBC_QRCoderBitVector::At(size_t index, int32_t& e) const {
-  if (index >= m_sizeInBits) {
-    e = BCExceptionBadIndexException;
-    return 0;
-  }
+int32_t CBC_QRCoderBitVector::At(size_t index) const {
+  CHECK(index < m_sizeInBits);
   int32_t value = m_array[index >> 3] & 0xff;
   return (value >> (7 - (index & 0x7))) & 1;
 }
@@ -73,14 +70,9 @@
   }
 }
 
-void CBC_QRCoderBitVector::AppendBitVector(CBC_QRCoderBitVector* bits) {
-  int32_t size = bits->Size();
-  for (int32_t i = 0; i < size; i++) {
-    int e = BCExceptionNO;
-    int32_t num = bits->At(i, e);
-    ASSERT(e == BCExceptionNO);
-    AppendBit(num);
-  }
+void CBC_QRCoderBitVector::AppendBitVector(const CBC_QRCoderBitVector* bits) {
+  for (size_t i = 0; i < bits->Size(); i++)
+    AppendBit(bits->At(i));
 }
 
 bool CBC_QRCoderBitVector::XOR(const CBC_QRCoderBitVector* other) {
diff --git a/fxbarcode/qrcode/BC_QRCoderBitVector.h b/fxbarcode/qrcode/BC_QRCoderBitVector.h
index 0ebeb60..9b8af81 100644
--- a/fxbarcode/qrcode/BC_QRCoderBitVector.h
+++ b/fxbarcode/qrcode/BC_QRCoderBitVector.h
@@ -18,13 +18,13 @@
   ~CBC_QRCoderBitVector();
 
   const uint8_t* GetArray() const;
-  int32_t At(size_t index, int32_t& e) const;
+  int32_t At(size_t index) const;
   size_t Size() const;
   size_t sizeInBytes() const;
 
   void AppendBit(int32_t bit);
   void AppendBits(int32_t value, int32_t numBits);
-  void AppendBitVector(CBC_QRCoderBitVector* bits);
+  void AppendBitVector(const CBC_QRCoderBitVector* bits);
   bool XOR(const CBC_QRCoderBitVector* other);
 
  private:
diff --git a/fxbarcode/qrcode/BC_QRCoderBlockPair.cpp b/fxbarcode/qrcode/BC_QRCoderBlockPair.cpp
deleted file mode 100644
index 9ac93e9..0000000
--- a/fxbarcode/qrcode/BC_QRCoderBlockPair.cpp
+++ /dev/null
@@ -1,47 +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
-// Original code is licensed as follows:
-/*
- * Copyright 2008 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "fxbarcode/qrcode/BC_QRCoderBlockPair.h"
-
-#include <utility>
-
-#include "fxbarcode/common/BC_CommonByteArray.h"
-
-CBC_QRCoderBlockPair::CBC_QRCoderBlockPair() {}
-
-CBC_QRCoderBlockPair::~CBC_QRCoderBlockPair() {}
-
-const CBC_CommonByteArray* CBC_QRCoderBlockPair::GetDataBytes() const {
-  return m_dataBytes.get();
-}
-
-const CBC_CommonByteArray* CBC_QRCoderBlockPair::GetErrorCorrectionBytes()
-    const {
-  return m_errorCorrectionBytes.get();
-}
-
-void CBC_QRCoderBlockPair::SetData(
-    std::unique_ptr<CBC_CommonByteArray> data,
-    std::unique_ptr<CBC_CommonByteArray> errorCorrection) {
-  m_dataBytes = std::move(data);
-  m_errorCorrectionBytes = std::move(errorCorrection);
-}
diff --git a/fxbarcode/qrcode/BC_QRCoderBlockPair.h b/fxbarcode/qrcode/BC_QRCoderBlockPair.h
deleted file mode 100644
index 9d571f3..0000000
--- a/fxbarcode/qrcode/BC_QRCoderBlockPair.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXBARCODE_QRCODE_BC_QRCODERBLOCKPAIR_H_
-#define FXBARCODE_QRCODE_BC_QRCODERBLOCKPAIR_H_
-
-#include <memory>
-
-class CBC_CommonByteArray;
-
-class CBC_QRCoderBlockPair {
- public:
-  CBC_QRCoderBlockPair();
-  ~CBC_QRCoderBlockPair();
-
-  const CBC_CommonByteArray* GetDataBytes() const;
-  const CBC_CommonByteArray* GetErrorCorrectionBytes() const;
-  void SetData(std::unique_ptr<CBC_CommonByteArray> data,
-               std::unique_ptr<CBC_CommonByteArray> errorCorrection);
-
- private:
-  std::unique_ptr<CBC_CommonByteArray> m_dataBytes;
-  std::unique_ptr<CBC_CommonByteArray> m_errorCorrectionBytes;
-};
-
-#endif  // FXBARCODE_QRCODE_BC_QRCODERBLOCKPAIR_H_
diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocks.h b/fxbarcode/qrcode/BC_QRCoderECBlocks.h
index 6c4f1cd..2583b12 100644
--- a/fxbarcode/qrcode/BC_QRCoderECBlocks.h
+++ b/fxbarcode/qrcode/BC_QRCoderECBlocks.h
@@ -9,8 +9,6 @@
 
 #include <stdint.h>
 
-#include <vector>
-
 struct CBC_QRCoderECBlockData;
 
 class CBC_QRCoderECBlocks {
diff --git a/fxbarcode/qrcode/BC_QRCoderEncoder.cpp b/fxbarcode/qrcode/BC_QRCoderEncoder.cpp
index a44dfc3..be02f5b 100644
--- a/fxbarcode/qrcode/BC_QRCoderEncoder.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderEncoder.cpp
@@ -25,26 +25,32 @@
 #include <algorithm>
 #include <memory>
 #include <utility>
+#include <vector>
 
-#include "fxbarcode/BC_UtilCodingConvert.h"
-#include "fxbarcode/common/BC_CommonByteArray.h"
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomon.h"
 #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h"
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderBitVector.h"
-#include "fxbarcode/qrcode/BC_QRCoderBlockPair.h"
 #include "fxbarcode/qrcode/BC_QRCoderECBlocks.h"
 #include "fxbarcode/qrcode/BC_QRCoderMaskUtil.h"
 #include "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h"
 #include "fxbarcode/qrcode/BC_QRCoderMode.h"
 #include "fxbarcode/qrcode/BC_QRCoderVersion.h"
+#include "third_party/base/optional.h"
 #include "third_party/base/ptr_util.h"
 
 using ModeStringPair = std::pair<CBC_QRCoderMode*, ByteString>;
 
 namespace {
 
+CBC_ReedSolomonGF256* g_QRCodeField = nullptr;
+
+struct QRCoderBlockPair {
+  std::vector<uint8_t> data;
+  std::vector<uint8_t> ecc;
+};
+
 // This is a mapping for an ASCII table, starting at an index of 32.
 const int8_t g_alphaNumericTable[] = {
     36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,  // 32-47
@@ -61,11 +67,9 @@
   return g_alphaNumericTable[code_index];
 }
 
-void AppendNumericBytes(const ByteString& content,
-                        CBC_QRCoderBitVector* bits,
-                        int32_t& e) {
-  int32_t length = content.GetLength();
-  int32_t i = 0;
+bool AppendNumericBytes(const ByteString& content, CBC_QRCoderBitVector* bits) {
+  size_t length = content.GetLength();
+  size_t i = 0;
   while (i < length) {
     int32_t num1 = content[i] - '0';
     if (i + 2 < length) {
@@ -82,25 +86,23 @@
       i++;
     }
   }
+  return true;
 }
 
-void AppendAlphaNumericBytes(const ByteString& content,
-                             CBC_QRCoderBitVector* bits,
-                             int32_t& e) {
-  int32_t length = content.GetLength();
-  int32_t i = 0;
+bool AppendAlphaNumericBytes(const ByteString& content,
+                             CBC_QRCoderBitVector* bits) {
+  size_t length = content.GetLength();
+  size_t i = 0;
   while (i < length) {
     int32_t code1 = GetAlphaNumericCode(content[i]);
-    if (code1 == -1) {
-      e = BCExceptionInvalidateCharacter;
-      return;
-    }
+    if (code1 == -1)
+      return false;
+
     if (i + 1 < length) {
       int32_t code2 = GetAlphaNumericCode(content[i + 1]);
-      if (code2 == -1) {
-        e = BCExceptionInvalidateCharacter;
-        return;
-      }
+      if (code2 == -1)
+        return false;
+
       bits->AppendBits(code1 * 45 + code2, 11);
       i += 2;
     } else {
@@ -108,54 +110,52 @@
       i++;
     }
   }
+  return true;
 }
 
-void AppendGBKBytes(const ByteString& content,
-                    CBC_QRCoderBitVector* bits,
-                    int32_t& e) {
-  int32_t length = content.GetLength();
+bool AppendGBKBytes(const ByteString& content, CBC_QRCoderBitVector* bits) {
+  size_t length = content.GetLength();
   uint32_t value = 0;
-  for (int32_t i = 0; i < length; i += 2) {
+  for (size_t i = 0; i < length; i += 2) {
     value = (uint32_t)(content[i] << 8 | content[i + 1]);
-    if (value <= 0xAAFE && value >= 0xA1A1) {
+    if (value <= 0xAAFE && value >= 0xA1A1)
       value -= 0xA1A1;
-    } else if (value <= 0xFAFE && value >= 0xB0A1) {
+    else if (value <= 0xFAFE && value >= 0xB0A1)
       value -= 0xA6A1;
-    } else {
-      e = BCExceptionInvalidateCharacter;
-      return;
-    }
+    else
+      return false;
+
     value = (uint32_t)((value >> 8) * 0x60) + (uint32_t)(value & 0xff);
     bits->AppendBits(value, 13);
   }
+  return true;
 }
 
-void Append8BitBytes(const ByteString& content,
+bool Append8BitBytes(const ByteString& content,
                      CBC_QRCoderBitVector* bits,
-                     ByteString encoding,
-                     int32_t& e) {
+                     ByteString encoding) {
   for (size_t i = 0; i < content.GetLength(); i++)
     bits->AppendBits(content[i], 8);
+  return true;
 }
 
-void AppendKanjiBytes(const ByteString& content,
-                      CBC_QRCoderBitVector* bits,
-                      int32_t& e) {
+bool AppendKanjiBytes(const ByteString& content, CBC_QRCoderBitVector* bits) {
   std::vector<uint8_t> bytes;
   uint32_t value = 0;
+  // TODO(thestig): This is wrong, as |bytes| is empty.
   for (size_t i = 0; i < bytes.size(); i += 2) {
     value = (uint32_t)((content[i] << 8) | content[i + 1]);
-    if (value <= 0x9ffc && value >= 0x8140) {
+    if (value <= 0x9ffc && value >= 0x8140)
       value -= 0x8140;
-    } else if (value <= 0xebbf && value >= 0xe040) {
+    else if (value <= 0xebbf && value >= 0xe040)
       value -= 0xc140;
-    } else {
-      e = BCExceptionInvalidateCharacter;
-      return;
-    }
+    else
+      return false;
+
     value = (uint32_t)((value >> 8) * 0xc0) + (uint32_t)(value & 0xff);
     bits->AppendBits(value, 13);
   }
+  return true;
 }
 
 void AppendModeInfo(CBC_QRCoderMode* mode, CBC_QRCoderBitVector* bits) {
@@ -168,12 +168,11 @@
                       int32_t version,
                       CBC_QRCoderMode* mode,
                       CBC_QRCoderBitVector* bits) {
-  int32_t e = BCExceptionNO;
   const auto* qcv = CBC_QRCoderVersion::GetVersionForNumber(version);
   if (!qcv)
     return false;
-  int32_t numBits = mode->GetCharacterCountBits(qcv->GetVersionNumber(), e);
-  if (e != BCExceptionNO)
+  int32_t numBits = mode->GetCharacterCountBits(qcv->GetVersionNumber());
+  if (numBits == 0)
     return false;
   if (numBits > ((1 << numBits) - 1))
     return true;
@@ -184,31 +183,27 @@
   return true;
 }
 
-void AppendBytes(const ByteString& content,
+bool AppendBytes(const ByteString& content,
                  CBC_QRCoderMode* mode,
                  CBC_QRCoderBitVector* bits,
-                 ByteString encoding,
-                 int32_t& e) {
+                 ByteString encoding) {
   if (mode == CBC_QRCoderMode::sNUMERIC)
-    AppendNumericBytes(content, bits, e);
-  else if (mode == CBC_QRCoderMode::sALPHANUMERIC)
-    AppendAlphaNumericBytes(content, bits, e);
-  else if (mode == CBC_QRCoderMode::sBYTE)
-    Append8BitBytes(content, bits, encoding, e);
-  else if (mode == CBC_QRCoderMode::sKANJI)
-    AppendKanjiBytes(content, bits, e);
-  else if (mode == CBC_QRCoderMode::sGBK)
-    AppendGBKBytes(content, bits, e);
-  else
-    e = BCExceptionUnsupportedMode;
+    return AppendNumericBytes(content, bits);
+  if (mode == CBC_QRCoderMode::sALPHANUMERIC)
+    return AppendAlphaNumericBytes(content, bits);
+  if (mode == CBC_QRCoderMode::sBYTE)
+    return Append8BitBytes(content, bits, encoding);
+  if (mode == CBC_QRCoderMode::sKANJI)
+    return AppendKanjiBytes(content, bits);
+  if (mode == CBC_QRCoderMode::sGBK)
+    return AppendGBKBytes(content, bits);
+  return false;
 }
 
 bool InitQRCode(int32_t numInputBytes,
                 const CBC_QRCoderErrorCorrectionLevel* ecLevel,
-                CBC_QRCoderMode* mode,
                 CBC_QRCoder* qrCode) {
   qrCode->SetECLevel(ecLevel);
-  qrCode->SetMode(mode);
   for (int32_t i = 1; i <= CBC_QRCoderVersion::kMaxVersion; ++i) {
     const auto* version = CBC_QRCoderVersion::GetVersionForNumber(i);
     int32_t numBytes = version->GetTotalCodeWords();
@@ -229,66 +224,23 @@
   return false;
 }
 
-std::unique_ptr<CBC_CommonByteArray> GenerateECBytes(
-    CBC_CommonByteArray* dataBytes,
-    int32_t numEcBytesInBlock) {
-  int32_t numDataBytes = dataBytes->Size();
-  std::vector<int32_t> toEncode(numDataBytes + numEcBytesInBlock);
-  for (int32_t i = 0; i < numDataBytes; ++i)
-    toEncode[i] = dataBytes->At(i);
-  CBC_ReedSolomonEncoder encode(CBC_ReedSolomonGF256::QRCodeField);
-  encode.Init();
-  if (!encode.Encode(&toEncode, numEcBytesInBlock))
-    return nullptr;
-  auto ecBytes = pdfium::MakeUnique<CBC_CommonByteArray>(numEcBytesInBlock);
-  for (int32_t i = 0; i < numEcBytesInBlock; ++i)
-    ecBytes->Set(i, toEncode[numDataBytes + i]);
+std::vector<uint8_t> GenerateECBytes(pdfium::span<const uint8_t> dataBytes,
+                                     size_t numEcBytesInBlock) {
+  // If |numEcBytesInBlock| is 0, the encoder will fail anyway.
+  ASSERT(numEcBytesInBlock > 0);
+  std::vector<int32_t> toEncode(dataBytes.size() + numEcBytesInBlock);
+  std::copy(dataBytes.begin(), dataBytes.end(), toEncode.begin());
+
+  std::vector<uint8_t> ecBytes;
+  CBC_ReedSolomonEncoder encoder(g_QRCodeField);
+  if (encoder.Encode(&toEncode, numEcBytesInBlock)) {
+    ecBytes = std::vector<uint8_t>(toEncode.begin() + dataBytes.size(),
+                                   toEncode.end());
+    ASSERT(ecBytes.size() == static_cast<size_t>(numEcBytesInBlock));
+  }
   return ecBytes;
 }
 
-int32_t GetSpanByVersion(CBC_QRCoderMode* modeFirst,
-                         CBC_QRCoderMode* modeSecond,
-                         int32_t versionNum,
-                         int32_t& e) {
-  if (versionNum == 0)
-    return 0;
-
-  if (modeFirst == CBC_QRCoderMode::sALPHANUMERIC &&
-      modeSecond == CBC_QRCoderMode::sBYTE) {
-    if (versionNum >= 1 && versionNum <= 9)
-      return 11;
-    if (versionNum >= 10 && versionNum <= 26)
-      return 15;
-    if (versionNum >= 27 && versionNum <= CBC_QRCoderVersion::kMaxVersion)
-      return 16;
-    e = BCExceptionNoSuchVersion;
-    return 0;
-  }
-  if (modeSecond == CBC_QRCoderMode::sALPHANUMERIC &&
-      modeFirst == CBC_QRCoderMode::sNUMERIC) {
-    if (versionNum >= 1 && versionNum <= 9)
-      return 13;
-    if (versionNum >= 10 && versionNum <= 26)
-      return 15;
-    if (versionNum >= 27 && versionNum <= CBC_QRCoderVersion::kMaxVersion)
-      return 17;
-    e = BCExceptionNoSuchVersion;
-    return 0;
-  }
-  if (modeSecond == CBC_QRCoderMode::sBYTE &&
-      modeFirst == CBC_QRCoderMode::sNUMERIC) {
-    if (versionNum >= 1 && versionNum <= 9)
-      return 6;
-    if (versionNum >= 10 && versionNum <= 26)
-      return 8;
-    if (versionNum >= 27 && versionNum <= CBC_QRCoderVersion::kMaxVersion)
-      return 9;
-    e = BCExceptionNoSuchVersion;
-    return 0;
-  }
-  return -1;
-}
-
 int32_t CalculateMaskPenalty(CBC_CommonByteMatrix* matrix) {
   return CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule1(matrix) +
          CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule2(matrix) +
@@ -296,19 +248,19 @@
          CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule4(matrix);
 }
 
-int32_t ChooseMaskPattern(CBC_QRCoderBitVector* bits,
-                          const CBC_QRCoderErrorCorrectionLevel* ecLevel,
-                          int32_t version,
-                          CBC_CommonByteMatrix* matrix,
-                          int32_t& e) {
+Optional<int32_t> ChooseMaskPattern(
+    CBC_QRCoderBitVector* bits,
+    const CBC_QRCoderErrorCorrectionLevel* ecLevel,
+    int32_t version,
+    CBC_CommonByteMatrix* matrix) {
   int32_t minPenalty = 65535;
   int32_t bestMaskPattern = -1;
   for (int32_t maskPattern = 0; maskPattern < CBC_QRCoder::kNumMaskPatterns;
        maskPattern++) {
-    CBC_QRCoderMatrixUtil::BuildMatrix(bits, ecLevel, version, maskPattern,
-                                       matrix, e);
-    if (e != BCExceptionNO)
-      return 0;
+    if (!CBC_QRCoderMatrixUtil::BuildMatrix(bits, ecLevel, version, maskPattern,
+                                            matrix)) {
+      return {};
+    }
     int32_t penalty = CalculateMaskPenalty(matrix);
     if (penalty < minPenalty) {
       minPenalty = penalty;
@@ -322,8 +274,8 @@
                                             int32_t numDataBytes,
                                             int32_t numRSBlocks,
                                             int32_t blockID,
-                                            int32_t& numDataBytesInBlock,
-                                            int32_t& numECBytesInBlock) {
+                                            int32_t* numDataBytesInBlock,
+                                            int32_t* numECBytesInBlock) {
   if (blockID >= numRSBlocks)
     return;
 
@@ -336,11 +288,11 @@
   int32_t numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
   int32_t numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
   if (blockID < numRsBlocksInGroup1) {
-    numDataBytesInBlock = numDataBytesInGroup1;
-    numECBytesInBlock = numEcBytesInGroup1;
+    *numDataBytesInBlock = numDataBytesInGroup1;
+    *numECBytesInBlock = numEcBytesInGroup1;
   } else {
-    numDataBytesInBlock = numDataBytesInGroup2;
-    numECBytesInBlock = numEcBytesInGroup2;
+    *numDataBytesInBlock = numDataBytesInGroup2;
+    *numECBytesInBlock = numEcBytesInGroup2;
   }
 }
 
@@ -368,122 +320,6 @@
   return bits->Size() == capacity;
 }
 
-void MergeString(std::vector<ModeStringPair>* result,
-                 int32_t versionNum,
-                 int32_t& e) {
-  size_t mergeNum = 0;
-  for (size_t i = 0; i + 1 < result->size(); i++) {
-    auto& element1 = (*result)[i];
-    auto& element2 = (*result)[i + 1];
-    if (element1.first == CBC_QRCoderMode::sALPHANUMERIC) {
-      int32_t tmp = GetSpanByVersion(CBC_QRCoderMode::sALPHANUMERIC,
-                                     CBC_QRCoderMode::sBYTE, versionNum, e);
-      if (e != BCExceptionNO)
-        return;
-      if (element2.first == CBC_QRCoderMode::sBYTE && tmp >= 0 &&
-          element1.second.GetLength() < static_cast<size_t>(tmp)) {
-        element2.second = element1.second + element2.second;
-        result->erase(result->begin() + i);
-        i--;
-        mergeNum++;
-      }
-    } else if (element1.first == CBC_QRCoderMode::sBYTE) {
-      if (element2.first == CBC_QRCoderMode::sBYTE) {
-        element1.second += element2.second;
-        result->erase(result->begin() + i + 1);
-        i--;
-        mergeNum++;
-      }
-    } else if (element1.first == CBC_QRCoderMode::sNUMERIC) {
-      int32_t tmp = GetSpanByVersion(CBC_QRCoderMode::sNUMERIC,
-                                     CBC_QRCoderMode::sBYTE, versionNum, e);
-      if (e != BCExceptionNO)
-        return;
-      if (element2.first == CBC_QRCoderMode::sBYTE && tmp >= 0 &&
-          element1.second.GetLength() < static_cast<size_t>(tmp)) {
-        element2.second = element1.second + element2.second;
-        result->erase(result->begin() + i);
-        i--;
-        mergeNum++;
-      }
-      tmp = GetSpanByVersion(CBC_QRCoderMode::sNUMERIC,
-                             CBC_QRCoderMode::sALPHANUMERIC, versionNum, e);
-      if (e != BCExceptionNO)
-        return;
-      if (element2.first == CBC_QRCoderMode::sALPHANUMERIC && tmp >= 0 &&
-          element1.second.GetLength() < static_cast<size_t>(tmp)) {
-        element2.second = element1.second + element2.second;
-        result->erase(result->begin() + i);
-        i--;
-        mergeNum++;
-      }
-    }
-  }
-  if (mergeNum != 0)
-    MergeString(result, versionNum, e);
-}
-
-void SplitString(const ByteString& content,
-                 std::vector<ModeStringPair>* result) {
-  size_t index = 0;
-  while (index < content.GetLength()) {
-    uint8_t c = static_cast<uint8_t>(content[index]);
-    if (!((c >= 0xA1 && c <= 0xAA) || (c >= 0xB0 && c <= 0xFA)))
-      break;
-    index += 2;
-  }
-  if (index)
-    result->push_back({CBC_QRCoderMode::sGBK, content.Left(index)});
-  if (index >= content.GetLength())
-    return;
-
-  size_t flag = index;
-  while (GetAlphaNumericCode(content[index]) == -1 &&
-         index < content.GetLength()) {
-    uint8_t c = static_cast<uint8_t>(content[index]);
-    if (((c >= 0xA1 && c <= 0xAA) || (c >= 0xB0 && c <= 0xFA)))
-      break;
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-    bool high = !!IsDBCSLeadByte(content[index]);
-#else
-    bool high = content[index] > 127;
-#endif
-    ++index;
-    if (high)
-      ++index;
-  }
-  if (index != flag) {
-    result->push_back(
-        {CBC_QRCoderMode::sBYTE, content.Mid(flag, index - flag)});
-  }
-  flag = index;
-  if (index >= content.GetLength())
-    return;
-
-  while (index < content.GetLength() && isdigit(content[index]))
-    ++index;
-
-  if (index != flag) {
-    result->push_back(
-        {CBC_QRCoderMode::sNUMERIC, content.Mid(flag, index - flag)});
-  }
-  flag = index;
-  if (index >= content.GetLength())
-    return;
-
-  while (index < content.GetLength() &&
-         GetAlphaNumericCode(content[index]) != -1) {
-    ++index;
-  }
-  if (index != flag) {
-    result->push_back(
-        {CBC_QRCoderMode::sALPHANUMERIC, content.Mid(flag, index - flag)});
-  }
-  flag = index;
-  if (index < content.GetLength())
-    SplitString(content.Right(content.GetLength() - index), result);
-}
-
 CBC_QRCoderMode* ChooseMode(const ByteString& content, ByteString encoding) {
   if (encoding.Compare("SHIFT_JIS") == 0)
     return CBC_QRCoderMode::sKANJI;
@@ -517,42 +353,47 @@
     return false;
 
   int32_t dataBytesOffset = 0;
-  int32_t maxNumDataBytes = 0;
-  int32_t maxNumEcBytes = 0;
-  std::vector<CBC_QRCoderBlockPair> blocks(numRSBlocks);
+  size_t maxNumDataBytes = 0;
+  size_t maxNumEcBytes = 0;
+  std::vector<QRCoderBlockPair> blocks(numRSBlocks);
   for (int32_t i = 0; i < numRSBlocks; i++) {
     int32_t numDataBytesInBlock;
-    int32_t numEcBytesInBlosk;
+    int32_t numEcBytesInBlock;
     GetNumDataBytesAndNumECBytesForBlockID(numTotalBytes, numDataBytes,
-                                           numRSBlocks, i, numDataBytesInBlock,
-                                           numEcBytesInBlosk);
-    auto dataBytes = pdfium::MakeUnique<CBC_CommonByteArray>();
-    dataBytes->Set(bits->GetArray(), dataBytesOffset, numDataBytesInBlock);
-    std::unique_ptr<CBC_CommonByteArray> ecBytes =
-        GenerateECBytes(dataBytes.get(), numEcBytesInBlosk);
-    if (!ecBytes)
+                                           numRSBlocks, i, &numDataBytesInBlock,
+                                           &numEcBytesInBlock);
+    if (numDataBytesInBlock < 0 || numEcBytesInBlock <= 0)
       return false;
 
-    maxNumDataBytes = std::max(maxNumDataBytes, dataBytes->Size());
-    maxNumEcBytes = std::max(maxNumEcBytes, ecBytes->Size());
-    blocks[i].SetData(std::move(dataBytes), std::move(ecBytes));
+    std::vector<uint8_t> dataBytes(numDataBytesInBlock);
+    memcpy(dataBytes.data(), bits->GetArray() + dataBytesOffset,
+           numDataBytesInBlock);
+    std::vector<uint8_t> ecBytes =
+        GenerateECBytes(dataBytes, numEcBytesInBlock);
+    if (ecBytes.empty())
+      return false;
+
+    maxNumDataBytes = std::max(maxNumDataBytes, dataBytes.size());
+    maxNumEcBytes = std::max(maxNumEcBytes, ecBytes.size());
+    blocks[i].data = std::move(dataBytes);
+    blocks[i].ecc = std::move(ecBytes);
     dataBytesOffset += numDataBytesInBlock;
   }
   if (numDataBytes != dataBytesOffset)
     return false;
 
-  for (int32_t x = 0; x < maxNumDataBytes; x++) {
+  for (size_t x = 0; x < maxNumDataBytes; x++) {
     for (size_t j = 0; j < blocks.size(); j++) {
-      const CBC_CommonByteArray* dataBytes = blocks[j].GetDataBytes();
-      if (x < dataBytes->Size())
-        result->AppendBits(dataBytes->At(x), 8);
+      const std::vector<uint8_t>& dataBytes = blocks[j].data;
+      if (x < dataBytes.size())
+        result->AppendBits(dataBytes[x], 8);
     }
   }
-  for (int32_t y = 0; y < maxNumEcBytes; y++) {
+  for (size_t y = 0; y < maxNumEcBytes; y++) {
     for (size_t l = 0; l < blocks.size(); l++) {
-      const CBC_CommonByteArray* ecBytes = blocks[l].GetErrorCorrectionBytes();
-      if (y < ecBytes->Size())
-        result->AppendBits(ecBytes->At(y), 8);
+      const std::vector<uint8_t>& ecBytes = blocks[l].ecc;
+      if (y < ecBytes.size())
+        result->AppendBits(ecBytes[y], 8);
     }
   }
   return static_cast<size_t>(numTotalBytes) == result->sizeInBytes();
@@ -560,25 +401,30 @@
 
 }  // namespace
 
-CBC_QRCoderEncoder::CBC_QRCoderEncoder() {}
-
-CBC_QRCoderEncoder::~CBC_QRCoderEncoder() {}
+// static
+void CBC_QRCoderEncoder::Initialize() {
+  g_QRCodeField = new CBC_ReedSolomonGF256(0x011D);
+  g_QRCodeField->Init();
+}
 
 // static
-bool CBC_QRCoderEncoder::Encode(const WideString& content,
+void CBC_QRCoderEncoder::Finalize() {
+  delete g_QRCodeField;
+  g_QRCodeField = nullptr;
+}
+
+// static
+bool CBC_QRCoderEncoder::Encode(WideStringView content,
                                 const CBC_QRCoderErrorCorrectionLevel* ecLevel,
                                 CBC_QRCoder* qrCode) {
   ByteString encoding = "utf8";
-  ByteString utf8Data;
-  CBC_UtilCodingConvert::UnicodeToUTF8(content, utf8Data);
+  ByteString utf8Data = FX_UTF8Encode(content);
   CBC_QRCoderMode* mode = ChooseMode(utf8Data, encoding);
   CBC_QRCoderBitVector dataBits;
-  int32_t e = BCExceptionNO;
-  AppendBytes(utf8Data, mode, &dataBits, encoding, e);
-  if (e != BCExceptionNO)
+  if (!AppendBytes(utf8Data, mode, &dataBits, encoding))
     return false;
   int32_t numInputBytes = dataBits.sizeInBytes();
-  if (!InitQRCode(numInputBytes, ecLevel, mode, qrCode))
+  if (!InitQRCode(numInputBytes, ecLevel, qrCode))
     return false;
   CBC_QRCoderBitVector headerAndDataBits;
   AppendModeInfo(mode, &headerAndDataBits);
@@ -600,18 +446,17 @@
 
   auto matrix = pdfium::MakeUnique<CBC_CommonByteMatrix>(
       qrCode->GetMatrixWidth(), qrCode->GetMatrixWidth());
-  matrix->Init();
-  int32_t maskPattern = ChooseMaskPattern(
-      &finalBits, qrCode->GetECLevel(), qrCode->GetVersion(), matrix.get(), e);
-  if (e != BCExceptionNO)
+  Optional<int32_t> maskPattern = ChooseMaskPattern(
+      &finalBits, qrCode->GetECLevel(), qrCode->GetVersion(), matrix.get());
+  if (!maskPattern)
     return false;
 
-  qrCode->SetMaskPattern(maskPattern);
-  CBC_QRCoderMatrixUtil::BuildMatrix(&finalBits, qrCode->GetECLevel(),
-                                     qrCode->GetVersion(),
-                                     qrCode->GetMaskPattern(), matrix.get(), e);
-  if (e != BCExceptionNO)
+  qrCode->SetMaskPattern(*maskPattern);
+  if (!CBC_QRCoderMatrixUtil::BuildMatrix(
+          &finalBits, qrCode->GetECLevel(), qrCode->GetVersion(),
+          qrCode->GetMaskPattern(), matrix.get())) {
     return false;
+  }
 
   qrCode->SetMatrix(std::move(matrix));
   return qrCode->IsValid();
diff --git a/fxbarcode/qrcode/BC_QRCoderEncoder.h b/fxbarcode/qrcode/BC_QRCoderEncoder.h
index 3acbd1b..a3af1fc 100644
--- a/fxbarcode/qrcode/BC_QRCoderEncoder.h
+++ b/fxbarcode/qrcode/BC_QRCoderEncoder.h
@@ -7,9 +7,6 @@
 #ifndef FXBARCODE_QRCODE_BC_QRCODERENCODER_H_
 #define FXBARCODE_QRCODE_BC_QRCODERENCODER_H_
 
-#include <utility>
-#include <vector>
-
 #include "core/fxcrt/fx_string.h"
 
 class CBC_QRCoder;
@@ -17,10 +14,12 @@
 
 class CBC_QRCoderEncoder {
  public:
-  CBC_QRCoderEncoder();
-  ~CBC_QRCoderEncoder();
+  CBC_QRCoderEncoder() = delete;
+  ~CBC_QRCoderEncoder() = delete;
 
-  static bool Encode(const WideString& content,
+  static void Initialize();
+  static void Finalize();
+  static bool Encode(WideStringView content,
                      const CBC_QRCoderErrorCorrectionLevel* ecLevel,
                      CBC_QRCoder* qrCode);
 };
diff --git a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp
index 6fb3233..a0e5c2b 100644
--- a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp
@@ -29,56 +29,25 @@
 
 CBC_QRCoderErrorCorrectionLevel::CBC_QRCoderErrorCorrectionLevel(
     int32_t ordinal,
-    int32_t bits,
-    const char* name)
-    : m_ordinal(ordinal), m_bits(bits), m_name(name) {}
+    int32_t bits)
+    : m_ordinal(ordinal), m_bits(bits) {}
 
-CBC_QRCoderErrorCorrectionLevel::~CBC_QRCoderErrorCorrectionLevel() {}
+CBC_QRCoderErrorCorrectionLevel::~CBC_QRCoderErrorCorrectionLevel() = default;
 
 void CBC_QRCoderErrorCorrectionLevel::Initialize() {
-  L = new CBC_QRCoderErrorCorrectionLevel(0, 0x01, "L");
-  M = new CBC_QRCoderErrorCorrectionLevel(1, 0x00, "M");
-  Q = new CBC_QRCoderErrorCorrectionLevel(2, 0x03, "Q");
-  H = new CBC_QRCoderErrorCorrectionLevel(3, 0x02, "H");
+  L = new CBC_QRCoderErrorCorrectionLevel(0, 0x01);
+  M = new CBC_QRCoderErrorCorrectionLevel(1, 0x00);
+  Q = new CBC_QRCoderErrorCorrectionLevel(2, 0x03);
+  H = new CBC_QRCoderErrorCorrectionLevel(3, 0x02);
 }
 
 void CBC_QRCoderErrorCorrectionLevel::Finalize() {
   delete L;
+  L = nullptr;
   delete M;
+  M = nullptr;
   delete Q;
+  Q = nullptr;
   delete H;
-}
-
-CBC_QRCoderErrorCorrectionLevel* CBC_QRCoderErrorCorrectionLevel::ForBits(
-    int32_t bits) {
-  switch (bits) {
-    case 0x00:
-      return M;
-    case 0x01:
-      return L;
-    case 0x02:
-      return H;
-    case 0x03:
-      return Q;
-    default:
-      return nullptr;
-  }
-}
-void CBC_QRCoderErrorCorrectionLevel::Destroy() {
-  if (L) {
-    delete CBC_QRCoderErrorCorrectionLevel::L;
-    L = nullptr;
-  }
-  if (M) {
-    delete CBC_QRCoderErrorCorrectionLevel::M;
-    M = nullptr;
-  }
-  if (H) {
-    delete CBC_QRCoderErrorCorrectionLevel::H;
-    H = nullptr;
-  }
-  if (Q) {
-    delete CBC_QRCoderErrorCorrectionLevel::Q;
-    Q = nullptr;
-  }
+  H = nullptr;
 }
diff --git a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h
index ff7563c..9a572a5 100644
--- a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h
+++ b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h
@@ -7,7 +7,7 @@
 #ifndef FXBARCODE_QRCODE_BC_QRCODERERRORCORRECTIONLEVEL_H_
 #define FXBARCODE_QRCODE_BC_QRCODERERRORCORRECTIONLEVEL_H_
 
-#include "core/fxcrt/fx_string.h"
+#include <stdint.h>
 
 class CBC_QRCoderErrorCorrectionLevel {
  public:
@@ -18,24 +18,17 @@
 
   static void Initialize();
   static void Finalize();
-  static void Destroy();
-  static CBC_QRCoderErrorCorrectionLevel* ForBits(int32_t bits);
 
   ~CBC_QRCoderErrorCorrectionLevel();
 
   int32_t Ordinal() const { return m_ordinal; }
   int32_t GetBits() const { return m_bits; }
-  ByteString GetName() const { return m_name; }
 
  private:
-  CBC_QRCoderErrorCorrectionLevel(int32_t ordinal,
-                                  int32_t bits,
-                                  const char* name);
-  CBC_QRCoderErrorCorrectionLevel();
+  CBC_QRCoderErrorCorrectionLevel(int32_t ordinal, int32_t bits);
 
   int32_t m_ordinal;
   int32_t m_bits;
-  ByteString m_name;
 };
 
 #endif  // FXBARCODE_QRCODE_BC_QRCODERERRORCORRECTIONLEVEL_H_
diff --git a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
index cdf1e4c..9776ddc 100644
--- a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
@@ -20,24 +20,58 @@
  * limitations under the License.
  */
 
+#include "fxbarcode/qrcode/BC_QRCoderMaskUtil.h"
+
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
-#include "fxbarcode/qrcode/BC_QRCoderMaskUtil.h"
-#include "fxbarcode/utils.h"
 
-CBC_QRCoderMaskUtil::CBC_QRCoderMaskUtil() {}
-CBC_QRCoderMaskUtil::~CBC_QRCoderMaskUtil() {}
+namespace {
+
+int32_t ApplyMaskPenaltyRule1Internal(CBC_CommonByteMatrix* matrix,
+                                      bool isHorizontal) {
+  int32_t penalty = 0;
+  int32_t numSameBitCells = 0;
+  int32_t prevBit = -1;
+  int32_t width = matrix->GetWidth();
+  int32_t height = matrix->GetHeight();
+  int32_t iLimit = isHorizontal ? height : width;
+  int32_t jLimit = isHorizontal ? width : height;
+  pdfium::span<const uint8_t> array = matrix->GetArray();
+  for (int32_t i = 0; i < iLimit; ++i) {
+    for (int32_t j = 0; j < jLimit; ++j) {
+      int32_t bit = isHorizontal ? array[i * width + j] : array[j * width + i];
+      if (bit == prevBit) {
+        numSameBitCells += 1;
+        if (numSameBitCells == 5) {
+          penalty += 3;
+        } else if (numSameBitCells > 5) {
+          penalty += 1;
+        }
+      } else {
+        numSameBitCells = 1;
+        prevBit = bit;
+      }
+    }
+    numSameBitCells = 0;
+  }
+  return penalty;
+}
+
+}  // namespace
+
+// static
 int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule1(
     CBC_CommonByteMatrix* matrix) {
   return ApplyMaskPenaltyRule1Internal(matrix, true) +
          ApplyMaskPenaltyRule1Internal(matrix, false);
 }
 
+// static
 int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule2(
     CBC_CommonByteMatrix* matrix) {
   int32_t penalty = 0;
-  uint8_t* array = matrix->GetArray();
+  pdfium::span<const uint8_t> array = matrix->GetArray();
   int32_t width = matrix->GetWidth();
   int32_t height = matrix->GetHeight();
   for (int32_t y = 0; y < height - 1; y++) {
@@ -53,10 +87,11 @@
   return 3 * penalty;
 }
 
+// static
 int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule3(
     CBC_CommonByteMatrix* matrix) {
   int32_t penalty = 0;
-  uint8_t* array = matrix->GetArray();
+  pdfium::span<const uint8_t> array = matrix->GetArray();
   int32_t width = matrix->GetWidth();
   int32_t height = matrix->GetHeight();
   for (int32_t y = 0; y < height; ++y) {
@@ -105,31 +140,31 @@
   }
   return penalty;
 }
+
+// static
 int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule4(
     CBC_CommonByteMatrix* matrix) {
   int32_t numDarkCells = 0;
-  uint8_t* array = matrix->GetArray();
+  pdfium::span<const uint8_t> array = matrix->GetArray();
   int32_t width = matrix->GetWidth();
   int32_t height = matrix->GetHeight();
   for (int32_t y = 0; y < height; ++y) {
     for (int32_t x = 0; x < width; ++x) {
-      if (array[y * width + x] == 1) {
+      if (array[y * width + x] == 1)
         numDarkCells += 1;
-      }
     }
   }
   int32_t numTotalCells = matrix->GetHeight() * matrix->GetWidth();
-  double darkRatio = (double)numDarkCells / numTotalCells;
-  return abs((int32_t)(darkRatio * 100 - 50) / 5) * 5 * 10;
+  double darkRatio = static_cast<double>(numDarkCells) / numTotalCells;
+  return abs(static_cast<int32_t>(darkRatio * 100 - 50) / 5) * 5 * 10;
 }
+
+// static
 bool CBC_QRCoderMaskUtil::GetDataMaskBit(int32_t maskPattern,
                                          int32_t x,
-                                         int32_t y,
-                                         int32_t& e) {
-  if (!CBC_QRCoder::IsValidMaskPattern(maskPattern)) {
-    e = (BCExceptionInvalidateMaskPattern);
-    return false;
-  }
+                                         int32_t y) {
+  ASSERT(CBC_QRCoder::IsValidMaskPattern(maskPattern));
+
   int32_t intermediate = 0, temp = 0;
   switch (maskPattern) {
     case 0:
@@ -159,40 +194,9 @@
       temp = y * x;
       intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);
       break;
-    default: {
-      e = BCExceptionInvalidateMaskPattern;
+    default:
+      NOTREACHED();
       return false;
-    }
   }
   return intermediate == 0;
 }
-int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule1Internal(
-    CBC_CommonByteMatrix* matrix,
-    bool isHorizontal) {
-  int32_t penalty = 0;
-  int32_t numSameBitCells = 0;
-  int32_t prevBit = -1;
-  int32_t width = matrix->GetWidth();
-  int32_t height = matrix->GetHeight();
-  int32_t iLimit = isHorizontal ? height : width;
-  int32_t jLimit = isHorizontal ? width : height;
-  uint8_t* array = matrix->GetArray();
-  for (int32_t i = 0; i < iLimit; ++i) {
-    for (int32_t j = 0; j < jLimit; ++j) {
-      int32_t bit = isHorizontal ? array[i * width + j] : array[j * width + i];
-      if (bit == prevBit) {
-        numSameBitCells += 1;
-        if (numSameBitCells == 5) {
-          penalty += 3;
-        } else if (numSameBitCells > 5) {
-          penalty += 1;
-        }
-      } else {
-        numSameBitCells = 1;
-        prevBit = bit;
-      }
-    }
-    numSameBitCells = 0;
-  }
-  return penalty;
-}
diff --git a/fxbarcode/qrcode/BC_QRCoderMaskUtil.h b/fxbarcode/qrcode/BC_QRCoderMaskUtil.h
index 63fccbb..b2da2a8 100644
--- a/fxbarcode/qrcode/BC_QRCoderMaskUtil.h
+++ b/fxbarcode/qrcode/BC_QRCoderMaskUtil.h
@@ -6,22 +6,22 @@
 
 #ifndef FXBARCODE_QRCODE_BC_QRCODERMASKUTIL_H_
 #define FXBARCODE_QRCODE_BC_QRCODERMASKUTIL_H_
+
+#include <stdint.h>
+
 class CBC_CommonByteMatrix;
+
 class CBC_QRCoderMaskUtil {
  public:
-  CBC_QRCoderMaskUtil();
-  virtual ~CBC_QRCoderMaskUtil();
-  static bool GetDataMaskBit(int32_t maskPattern,
-                             int32_t x,
-                             int32_t y,
-                             int32_t& e);
+  CBC_QRCoderMaskUtil() = delete;
+  ~CBC_QRCoderMaskUtil() = delete;
+
+  static bool GetDataMaskBit(int32_t maskPattern, int32_t x, int32_t y);
 
   static int32_t ApplyMaskPenaltyRule1(CBC_CommonByteMatrix* matrix);
   static int32_t ApplyMaskPenaltyRule2(CBC_CommonByteMatrix* matrix);
   static int32_t ApplyMaskPenaltyRule3(CBC_CommonByteMatrix* matrix);
   static int32_t ApplyMaskPenaltyRule4(CBC_CommonByteMatrix* matrix);
-  static int32_t ApplyMaskPenaltyRule1Internal(CBC_CommonByteMatrix* matrix,
-                                               bool isHorizontal);
 };
 
 #endif  // FXBARCODE_QRCODE_BC_QRCODERMASKUTIL_H_
diff --git a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp
index d5b7bcd..2c6b051 100644
--- a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp
@@ -20,135 +20,165 @@
  * limitations under the License.
  */
 
+#include "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h"
+
+#include "core/fxcrt/fx_memory.h"
 #include "fxbarcode/common/BC_CommonByteMatrix.h"
 #include "fxbarcode/qrcode/BC_QRCoder.h"
 #include "fxbarcode/qrcode/BC_QRCoderBitVector.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
 #include "fxbarcode/qrcode/BC_QRCoderMaskUtil.h"
-#include "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h"
-#include "fxbarcode/utils.h"
 
-const int32_t CBC_QRCoderMatrixUtil::POSITION_DETECTION_PATTERN[7][7] = {
+namespace {
+
+constexpr uint8_t POSITION_DETECTION_PATTERN[7][7] = {
     {1, 1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0, 1}, {1, 0, 1, 1, 1, 0, 1},
     {1, 0, 1, 1, 1, 0, 1}, {1, 0, 1, 1, 1, 0, 1}, {1, 0, 0, 0, 0, 0, 1},
     {1, 1, 1, 1, 1, 1, 1}};
-const int32_t CBC_QRCoderMatrixUtil::HORIZONTAL_SEPARATION_PATTERN[1][8] = {
-    {0, 0, 0, 0, 0, 0, 0, 0}};
-const int32_t CBC_QRCoderMatrixUtil::VERTICAL_SEPARATION_PATTERN[7][1] = {
-    {0}, {0}, {0}, {0}, {0}, {0}, {0}};
-const int32_t CBC_QRCoderMatrixUtil::POSITION_ADJUSTMENT_PATTERN[5][5] = {
-    {1, 1, 1, 1, 1},
-    {1, 0, 0, 0, 1},
-    {1, 0, 1, 0, 1},
-    {1, 0, 0, 0, 1},
-    {1, 1, 1, 1, 1}};
-const int32_t
-    CBC_QRCoderMatrixUtil::POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[40][7] =
-        // NOLINTNEXTLINE
-    {
-        {-1, -1, -1, -1, -1, -1, -1},   {6, 18, -1, -1, -1, -1, -1},
-        {6, 22, -1, -1, -1, -1, -1},    {6, 26, -1, -1, -1, -1, -1},
-        {6, 30, -1, -1, -1, -1, -1},    {6, 34, -1, -1, -1, -1, -1},
-        {6, 22, 38, -1, -1, -1, -1},    {6, 24, 42, -1, -1, -1, -1},
-        {6, 26, 46, -1, -1, -1, -1},    {6, 28, 50, -1, -1, -1, -1},
-        {6, 30, 54, -1, -1, -1, -1},    {6, 32, 58, -1, -1, -1, -1},
-        {6, 34, 62, -1, -1, -1, -1},    {6, 26, 46, 66, -1, -1, -1},
-        {6, 26, 48, 70, -1, -1, -1},    {6, 26, 50, 74, -1, -1, -1},
-        {6, 30, 54, 78, -1, -1, -1},    {6, 30, 56, 82, -1, -1, -1},
-        {6, 30, 58, 86, -1, -1, -1},    {6, 34, 62, 90, -1, -1, -1},
-        {6, 28, 50, 72, 94, -1, -1},    {6, 26, 50, 74, 98, -1, -1},
-        {6, 30, 54, 78, 102, -1, -1},   {6, 28, 54, 80, 106, -1, -1},
-        {6, 32, 58, 84, 110, -1, -1},   {6, 30, 58, 86, 114, -1, -1},
-        {6, 34, 62, 90, 118, -1, -1},   {6, 26, 50, 74, 98, 122, -1},
-        {6, 30, 54, 78, 102, 126, -1},  {6, 26, 52, 78, 104, 130, -1},
-        {6, 30, 56, 82, 108, 134, -1},  {6, 34, 60, 86, 112, 138, -1},
-        {6, 30, 58, 86, 114, 142, -1},  {6, 34, 62, 90, 118, 146, -1},
-        {6, 30, 54, 78, 102, 126, 150}, {6, 24, 50, 76, 102, 128, 154},
-        {6, 28, 54, 80, 106, 132, 158}, {6, 32, 58, 84, 110, 136, 162},
-        {6, 26, 54, 82, 110, 138, 166}, {6, 30, 58, 86, 114, 142, 170},
+
+constexpr uint8_t POSITION_ADJUSTMENT_PATTERN[5][5] = {{1, 1, 1, 1, 1},
+                                                       {1, 0, 0, 0, 1},
+                                                       {1, 0, 1, 0, 1},
+                                                       {1, 0, 0, 0, 1},
+                                                       {1, 1, 1, 1, 1}};
+
+constexpr size_t kNumCoordinate = 7;
+constexpr uint8_t kPositionAdjustmentPatternCoordinates[39][kNumCoordinate] = {
+    {6, 18, 0, 0, 0, 0, 0},         {6, 22, 0, 0, 0, 0, 0},
+    {6, 26, 0, 0, 0, 0, 0},         {6, 30, 0, 0, 0, 0, 0},
+    {6, 34, 0, 0, 0, 0, 0},         {6, 22, 38, 0, 0, 0, 0},
+    {6, 24, 42, 0, 0, 0, 0},        {6, 26, 46, 0, 0, 0, 0},
+    {6, 28, 50, 0, 0, 0, 0},        {6, 30, 54, 0, 0, 0, 0},
+    {6, 32, 58, 0, 0, 0, 0},        {6, 34, 62, 0, 0, 0, 0},
+    {6, 26, 46, 66, 0, 0, 0},       {6, 26, 48, 70, 0, 0, 0},
+    {6, 26, 50, 74, 0, 0, 0},       {6, 30, 54, 78, 0, 0, 0},
+    {6, 30, 56, 82, 0, 0, 0},       {6, 30, 58, 86, 0, 0, 0},
+    {6, 34, 62, 90, 0, 0, 0},       {6, 28, 50, 72, 94, 0, 0},
+    {6, 26, 50, 74, 98, 0, 0},      {6, 30, 54, 78, 102, 0, 0},
+    {6, 28, 54, 80, 106, 0, 0},     {6, 32, 58, 84, 110, 0, 0},
+    {6, 30, 58, 86, 114, 0, 0},     {6, 34, 62, 90, 118, 0, 0},
+    {6, 26, 50, 74, 98, 122, 0},    {6, 30, 54, 78, 102, 126, 0},
+    {6, 26, 52, 78, 104, 130, 0},   {6, 30, 56, 82, 108, 134, 0},
+    {6, 34, 60, 86, 112, 138, 0},   {6, 30, 58, 86, 114, 142, 0},
+    {6, 34, 62, 90, 118, 146, 0},   {6, 30, 54, 78, 102, 126, 150},
+    {6, 24, 50, 76, 102, 128, 154}, {6, 28, 54, 80, 106, 132, 158},
+    {6, 32, 58, 84, 110, 136, 162}, {6, 26, 54, 82, 110, 138, 166},
+    {6, 30, 58, 86, 114, 142, 170},
 };
-const int32_t CBC_QRCoderMatrixUtil::TYPE_INFO_COORDINATES[15][2] = {
+
+const uint8_t TYPE_INFO_COORDINATES[15][2] = {
     {8, 0}, {8, 1}, {8, 2}, {8, 3}, {8, 4}, {8, 5}, {8, 7}, {8, 8},
     {7, 8}, {5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, {0, 8},
 };
-const int32_t CBC_QRCoderMatrixUtil::VERSION_INFO_POLY = 0x1f25;
-const int32_t CBC_QRCoderMatrixUtil::TYPE_INFO_POLY = 0x0537;
-const int32_t CBC_QRCoderMatrixUtil::TYPE_INFO_MASK_PATTERN = 0x5412;
 
-void CBC_QRCoderMatrixUtil::ClearMatrix(CBC_CommonByteMatrix* matrix,
-                                        int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-  matrix->clear((uint8_t)-1);
-}
-void CBC_QRCoderMatrixUtil::BuildMatrix(
-    CBC_QRCoderBitVector* dataBits,
-    const CBC_QRCoderErrorCorrectionLevel* ecLevel,
-    int32_t version,
-    int32_t maskPattern,
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-  ClearMatrix(matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedBasicPatterns(version, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedTypeInfo(ecLevel, maskPattern, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  MaybeEmbedVersionInfo(version, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedDataBits(dataBits, maskPattern, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-}
-void CBC_QRCoderMatrixUtil::EmbedBasicPatterns(int32_t version,
-                                               CBC_CommonByteMatrix* matrix,
-                                               int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-  EmbedPositionDetectionPatternsAndSeparators(matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedDarkDotAtLeftBottomCorner(matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  MaybeEmbedPositionAdjustmentPatterns(version, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedTimingPatterns(matrix, e);
-  if (e != BCExceptionNO)
-    return;
+const int32_t VERSION_INFO_POLY = 0x1f25;
+const int32_t TYPE_INFO_POLY = 0x0537;
+const int32_t TYPE_INFO_MASK_PATTERN = 0x5412;
+
+bool IsEmpty(int32_t value) {
+  return (uint8_t)value == 0xff;
 }
 
-void CBC_QRCoderMatrixUtil::EmbedTypeInfo(
-    const CBC_QRCoderErrorCorrectionLevel* ecLevel,
-    int32_t maskPattern,
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
+bool IsValidValue(int32_t value) {
+  return ((uint8_t)value == 0xff || (uint8_t)value == 0x00 ||
+          (uint8_t)value == 0x01);
+}
+
+int32_t FindMSBSet(int32_t value) {
+  int32_t numDigits = 0;
+  while (value != 0) {
+    value >>= 1;
+    ++numDigits;
   }
+  return numDigits;
+}
+
+bool EmbedDataBits(CBC_QRCoderBitVector* dataBits,
+                   int32_t maskPattern,
+                   CBC_CommonByteMatrix* matrix) {
+  size_t szBitIndex = 0;
+  int32_t direction = -1;
+  int32_t x = matrix->GetWidth() - 1;
+  int32_t y = matrix->GetHeight() - 1;
+  while (x > 0) {
+    if (x == 6)
+      x -= 1;
+
+    while (y >= 0 && y < matrix->GetHeight()) {
+      if (y == 6) {
+        y += direction;
+        continue;
+      }
+      for (int32_t i = 0; i < 2; i++) {
+        int32_t xx = x - i;
+        if (!IsEmpty(matrix->Get(xx, y))) {
+          continue;
+        }
+        int32_t bit;
+        if (szBitIndex < dataBits->Size()) {
+          bit = dataBits->At(szBitIndex);
+          szBitIndex++;
+        } else {
+          bit = 0;
+        }
+        ASSERT(CBC_QRCoder::IsValidMaskPattern(maskPattern));
+        if (CBC_QRCoderMaskUtil::GetDataMaskBit(maskPattern, xx, y))
+          bit ^= 0x01;
+        matrix->Set(xx, y, bit);
+      }
+      y += direction;
+    }
+    direction = -direction;
+    y += direction;
+    x -= 2;
+  }
+  return szBitIndex == dataBits->Size();
+}
+
+int32_t CalculateBCHCode(int32_t value, int32_t poly) {
+  int32_t msbSetInPoly = FindMSBSet(poly);
+  value <<= msbSetInPoly - 1;
+  while (FindMSBSet(value) >= msbSetInPoly) {
+    value ^= poly << (FindMSBSet(value) - msbSetInPoly);
+  }
+  return value;
+}
+
+bool MakeTypeInfoBits(const CBC_QRCoderErrorCorrectionLevel* ecLevel,
+                      int32_t maskPattern,
+                      CBC_QRCoderBitVector* bits) {
+  if (!CBC_QRCoder::IsValidMaskPattern(maskPattern))
+    return false;
+
+  int32_t typeInfo = (ecLevel->GetBits() << 3) | maskPattern;
+  bits->AppendBits(typeInfo, 5);
+  int32_t bchCode = CalculateBCHCode(typeInfo, TYPE_INFO_POLY);
+  bits->AppendBits(bchCode, 10);
+  CBC_QRCoderBitVector maskBits;
+  maskBits.AppendBits(TYPE_INFO_MASK_PATTERN, 15);
+  if (!bits->XOR(&maskBits))
+    return false;
+
+  ASSERT(bits->Size() == 15);
+  return true;
+}
+
+void MakeVersionInfoBits(int32_t version, CBC_QRCoderBitVector* bits) {
+  bits->AppendBits(version, 6);
+  int32_t bchCode = CalculateBCHCode(version, VERSION_INFO_POLY);
+  bits->AppendBits(bchCode, 12);
+  ASSERT(bits->Size() == 18);
+}
+
+bool EmbedTypeInfo(const CBC_QRCoderErrorCorrectionLevel* ecLevel,
+                   int32_t maskPattern,
+                   CBC_CommonByteMatrix* matrix) {
   CBC_QRCoderBitVector typeInfoBits;
-  MakeTypeInfoBits(ecLevel, maskPattern, &typeInfoBits, e);
-  if (e != BCExceptionNO)
-    return;
+  if (!MakeTypeInfoBits(ecLevel, maskPattern, &typeInfoBits))
+    return false;
 
   for (size_t i = 0; i < typeInfoBits.Size(); i++) {
-    int32_t bit = typeInfoBits.At(typeInfoBits.Size() - 1 - i, e);
-    if (e != BCExceptionNO)
-      return;
+    int32_t bit = typeInfoBits.At(typeInfoBits.Size() - 1 - i);
     int32_t x1 = TYPE_INFO_COORDINATES[i][0];
     int32_t y1 = TYPE_INFO_COORDINATES[i][1];
     matrix->Set(x1, y1, bit);
@@ -162,333 +192,197 @@
       matrix->Set(x2, y2, bit);
     }
   }
+  return true;
 }
 
-void CBC_QRCoderMatrixUtil::MaybeEmbedVersionInfo(int32_t version,
-                                                  CBC_CommonByteMatrix* matrix,
-                                                  int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
+void MaybeEmbedVersionInfo(int32_t version, CBC_CommonByteMatrix* matrix) {
+  if (version < 7)
     return;
-  }
-  if (version < 7) {
-    return;
-  }
+
   CBC_QRCoderBitVector versionInfoBits;
-  MakeVersionInfoBits(version, &versionInfoBits, e);
-  if (e != BCExceptionNO)
-    return;
+  MakeVersionInfoBits(version, &versionInfoBits);
   int32_t bitIndex = 6 * 3 - 1;
   for (int32_t i = 0; i < 6; i++) {
     for (int32_t j = 0; j < 3; j++) {
-      int32_t bit = versionInfoBits.At(bitIndex, e);
-      if (e != BCExceptionNO)
-        return;
+      int32_t bit = versionInfoBits.At(bitIndex);
       bitIndex--;
       matrix->Set(i, matrix->GetHeight() - 11 + j, bit);
       matrix->Set(matrix->GetHeight() - 11 + j, i, bit);
     }
   }
 }
-void CBC_QRCoderMatrixUtil::EmbedDataBits(CBC_QRCoderBitVector* dataBits,
-                                          int32_t maskPattern,
-                                          CBC_CommonByteMatrix* matrix,
-                                          int32_t& e) {
-  if (!matrix || !dataBits) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-  size_t bitIndex = 0;
-  int32_t direction = -1;
-  int32_t x = matrix->GetWidth() - 1;
-  int32_t y = matrix->GetHeight() - 1;
-  while (x > 0) {
-    if (x == 6) {
-      x -= 1;
-    }
-    while (y >= 0 && y < matrix->GetHeight()) {
-      if (y == 6) {
-        y += direction;
-        continue;
-      }
-      for (int32_t i = 0; i < 2; i++) {
-        int32_t xx = x - i;
-        if (!IsEmpty(matrix->Get(xx, y))) {
-          continue;
-        }
-        int32_t bit;
-        if (bitIndex < dataBits->Size()) {
-          bit = dataBits->At(bitIndex, e);
-          if (e != BCExceptionNO)
-            return;
-          bitIndex++;
-        } else {
-          bit = 0;
-        }
-        if (maskPattern != -1) {
-          bool bol = CBC_QRCoderMaskUtil::GetDataMaskBit(maskPattern, xx, y, e);
-          if (e != BCExceptionNO)
-            return;
-          if (bol) {
-            bit ^= 0x01;
-          }
-        }
-        matrix->Set(xx, y, bit);
-      }
-      y += direction;
-    }
-    direction = -direction;
-    y += direction;
-    x -= 2;
-  }
-  if (bitIndex != dataBits->Size()) {
-    return;
-  }
-}
-int32_t CBC_QRCoderMatrixUtil::CalculateBCHCode(int32_t value, int32_t poly) {
-  int32_t msbSetInPoly = FindMSBSet(poly);
-  value <<= msbSetInPoly - 1;
-  while (FindMSBSet(value) >= msbSetInPoly) {
-    value ^= poly << (FindMSBSet(value) - msbSetInPoly);
-  }
-  return value;
-}
-void CBC_QRCoderMatrixUtil::MakeTypeInfoBits(
-    const CBC_QRCoderErrorCorrectionLevel* ecLevel,
-    int32_t maskPattern,
-    CBC_QRCoderBitVector* bits,
-    int32_t& e) {
-  if (!bits) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-  if (!CBC_QRCoder::IsValidMaskPattern(maskPattern)) {
-    e = BCExceptionBadMask;
-    return;
-  }
-  int32_t typeInfo = (ecLevel->GetBits() << 3) | maskPattern;
-  bits->AppendBits(typeInfo, 5);
-  int32_t bchCode = CalculateBCHCode(typeInfo, TYPE_INFO_POLY);
-  bits->AppendBits(bchCode, 10);
-  CBC_QRCoderBitVector maskBits;
-  maskBits.AppendBits(TYPE_INFO_MASK_PATTERN, 15);
-  if (!bits->XOR(&maskBits)) {
-    e = BCExceptionGeneric;
-    return;
-  }
-  ASSERT(bits->Size() == 15);
-}
 
-void CBC_QRCoderMatrixUtil::MakeVersionInfoBits(int32_t version,
-                                                CBC_QRCoderBitVector* bits,
-                                                int32_t& e) {
-  if (!bits) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-
-  bits->AppendBits(version, 6);
-  int32_t bchCode = CalculateBCHCode(version, VERSION_INFO_POLY);
-  bits->AppendBits(bchCode, 12);
-  ASSERT(bits->Size() == 18);
-}
-
-bool CBC_QRCoderMatrixUtil::IsEmpty(int32_t value) {
-  return (uint8_t)value == 0xff;
-}
-bool CBC_QRCoderMatrixUtil::IsValidValue(int32_t value) {
-  return ((uint8_t)value == 0xff || (uint8_t)value == 0x00 ||
-          (uint8_t)value == 0x01);
-}
-
-void CBC_QRCoderMatrixUtil::EmbedTimingPatterns(CBC_CommonByteMatrix* matrix,
-                                                int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
+bool EmbedTimingPatterns(CBC_CommonByteMatrix* matrix) {
   for (int32_t i = 8; i < matrix->GetWidth() - 8; i++) {
     int32_t bit = (i + 1) % 2;
-    if (!IsValidValue(matrix->Get(i, 6))) {
-      e = BCExceptionInvalidateImageData;
-      return;
-    }
-    if (IsEmpty(matrix->Get(i, 6))) {
+    if (!IsValidValue(matrix->Get(i, 6)))
+      return false;
+
+    if (IsEmpty(matrix->Get(i, 6)))
       matrix->Set(i, 6, bit);
-    }
-    if (!IsValidValue(matrix->Get(6, i))) {
-      e = BCExceptionInvalidateImageData;
-      return;
-    }
-    if (IsEmpty(matrix->Get(6, i))) {
+
+    if (!IsValidValue(matrix->Get(6, i)))
+      return false;
+
+    if (IsEmpty(matrix->Get(6, i)))
       matrix->Set(6, i, bit);
-    }
   }
+  return true;
 }
-void CBC_QRCoderMatrixUtil::EmbedDarkDotAtLeftBottomCorner(
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-  if (matrix->Get(8, matrix->GetHeight() - 8) == 0) {
-    e = BCExceptionHeight_8BeZero;
-    return;
-  }
+
+bool EmbedDarkDotAtLeftBottomCorner(CBC_CommonByteMatrix* matrix) {
+  if (matrix->Get(8, matrix->GetHeight() - 8) == 0)
+    return false;
+
   matrix->Set(8, matrix->GetHeight() - 8, 1);
+  return true;
 }
-void CBC_QRCoderMatrixUtil::EmbedHorizontalSeparationPattern(
-    int32_t xStart,
-    int32_t yStart,
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
+
+bool EmbedHorizontalSeparationPattern(int32_t xStart,
+                                      int32_t yStart,
+                                      CBC_CommonByteMatrix* matrix) {
   for (int32_t x = 0; x < 8; x++) {
-    if (!IsEmpty(matrix->Get(xStart + x, yStart))) {
-      e = BCExceptionInvalidateData;
-      return;
-    }
-    matrix->Set(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);
+    if (!IsEmpty(matrix->Get(xStart + x, yStart)))
+      return false;
+
+    matrix->Set(xStart + x, yStart, 0);
   }
+  return true;
 }
-void CBC_QRCoderMatrixUtil::EmbedVerticalSeparationPattern(
-    int32_t xStart,
-    int32_t yStart,
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
+
+bool EmbedVerticalSeparationPattern(int32_t xStart,
+                                    int32_t yStart,
+                                    CBC_CommonByteMatrix* matrix) {
   for (int32_t y = 0; y < 7; y++) {
-    if (!IsEmpty(matrix->Get(xStart, yStart + y))) {
-      e = BCExceptionInvalidateData;
-      return;
-    }
-    matrix->Set(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);
+    if (!IsEmpty(matrix->Get(xStart, yStart + y)))
+      return false;
+
+    matrix->Set(xStart, yStart + y, 0);
   }
+  return true;
 }
-void CBC_QRCoderMatrixUtil::EmbedPositionAdjustmentPattern(
-    int32_t xStart,
-    int32_t yStart,
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    if (e != BCExceptionNO)
-      return;
-  }
+
+bool EmbedPositionAdjustmentPattern(int32_t xStart,
+                                    int32_t yStart,
+                                    CBC_CommonByteMatrix* matrix) {
   for (int32_t y = 0; y < 5; y++) {
     for (int32_t x = 0; x < 5; x++) {
-      if (!IsEmpty(matrix->Get(xStart + x, y + yStart))) {
-        e = BCExceptionInvalidateData;
-        return;
-      }
+      if (!IsEmpty(matrix->Get(xStart + x, y + yStart)))
+        return false;
+
       matrix->Set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
     }
   }
+  return true;
 }
-void CBC_QRCoderMatrixUtil::EmbedPositionDetectionPattern(
-    int32_t xStart,
-    int32_t yStart,
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
+
+bool EmbedPositionDetectionPattern(int32_t xStart,
+                                   int32_t yStart,
+                                   CBC_CommonByteMatrix* matrix) {
   for (int32_t y = 0; y < 7; y++) {
     for (int32_t x = 0; x < 7; x++) {
-      if (!IsEmpty(matrix->Get(xStart + x, yStart + y))) {
-        e = BCExceptionInvalidateData;
-        return;
-      }
+      if (!IsEmpty(matrix->Get(xStart + x, yStart + y)))
+        return false;
+
       matrix->Set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
     }
   }
+  return true;
 }
-void CBC_QRCoderMatrixUtil::EmbedPositionDetectionPatternsAndSeparators(
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
+
+bool EmbedPositionDetectionPatternsAndSeparators(CBC_CommonByteMatrix* matrix) {
+  constexpr int32_t pdpWidth = 7;
+  if (!EmbedPositionDetectionPattern(0, 0, matrix))
+    return false;
+  if (!EmbedPositionDetectionPattern(matrix->GetWidth() - pdpWidth, 0, matrix))
+    return false;
+  if (!EmbedPositionDetectionPattern(0, matrix->GetWidth() - pdpWidth, matrix))
+    return false;
+
+  constexpr int32_t hspWidth = 8;
+  if (!EmbedHorizontalSeparationPattern(0, hspWidth - 1, matrix))
+    return false;
+  if (!EmbedHorizontalSeparationPattern(matrix->GetWidth() - hspWidth,
+                                        hspWidth - 1, matrix)) {
+    return false;
   }
-  int32_t pdpWidth = 7;
-  EmbedPositionDetectionPattern(0, 0, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedPositionDetectionPattern(matrix->GetWidth() - pdpWidth, 0, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedPositionDetectionPattern(0, matrix->GetWidth() - pdpWidth, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  int32_t hspWidth = 8;
-  EmbedHorizontalSeparationPattern(0, hspWidth - 1, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedHorizontalSeparationPattern(matrix->GetWidth() - hspWidth, hspWidth - 1,
-                                   matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedHorizontalSeparationPattern(0, matrix->GetWidth() - hspWidth, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  int32_t vspSize = 7;
-  EmbedVerticalSeparationPattern(vspSize, 0, matrix, e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedVerticalSeparationPattern(matrix->GetHeight() - vspSize - 1, 0, matrix,
-                                 e);
-  if (e != BCExceptionNO)
-    return;
-  EmbedVerticalSeparationPattern(vspSize, matrix->GetHeight() - vspSize, matrix,
-                                 e);
-  if (e != BCExceptionNO)
-    return;
+  if (!EmbedHorizontalSeparationPattern(0, matrix->GetWidth() - hspWidth,
+                                        matrix)) {
+    return false;
+  }
+
+  constexpr int32_t vspSize = 7;
+  if (!EmbedVerticalSeparationPattern(vspSize, 0, matrix))
+    return false;
+  if (!EmbedVerticalSeparationPattern(matrix->GetHeight() - vspSize - 1, 0,
+                                      matrix)) {
+    return false;
+  }
+  if (!EmbedVerticalSeparationPattern(vspSize, matrix->GetHeight() - vspSize,
+                                      matrix)) {
+    return false;
+  }
+  return true;
 }
-void CBC_QRCoderMatrixUtil::MaybeEmbedPositionAdjustmentPatterns(
-    int32_t version,
-    CBC_CommonByteMatrix* matrix,
-    int32_t& e) {
-  if (!matrix) {
-    e = BCExceptionNullPointer;
-    return;
-  }
-  if (version < 2) {
-    return;
-  }
-  int32_t index = version - 1;
-  int32_t const* coordinates =
-      &(POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index][0]);
-  int32_t numCoordinate = 7;
-  for (int32_t i = 0; i < numCoordinate; i++) {
-    for (int32_t j = 0; j < numCoordinate; j++) {
-      int32_t y = coordinates[i];
-      int32_t x = coordinates[j];
-      if (x == -1 || y == -1) {
-        continue;
-      }
+
+bool MaybeEmbedPositionAdjustmentPatterns(int32_t version,
+                                          CBC_CommonByteMatrix* matrix) {
+  if (version < 2)
+    return true;
+
+  const size_t index = version - 2;
+  if (index >= FX_ArraySize(kPositionAdjustmentPatternCoordinates))
+    return false;
+
+  const auto* coordinates = &kPositionAdjustmentPatternCoordinates[index][0];
+  for (size_t i = 0; i < kNumCoordinate; i++) {
+    const int32_t y = coordinates[i];
+    if (y == 0)
+      break;
+    for (size_t j = 0; j < kNumCoordinate; j++) {
+      const int32_t x = coordinates[j];
+      if (x == 0)
+        break;
+
       if (IsEmpty(matrix->Get(x, y))) {
-        EmbedPositionAdjustmentPattern(x - 2, y - 2, matrix, e);
-        if (e != BCExceptionNO)
-          return;
+        if (!EmbedPositionAdjustmentPattern(x - 2, y - 2, matrix))
+          return false;
       }
     }
   }
+  return true;
 }
-int32_t CBC_QRCoderMatrixUtil::FindMSBSet(int32_t value) {
-  int32_t numDigits = 0;
-  while (value != 0) {
-    value >>= 1;
-    ++numDigits;
-  }
-  return numDigits;
+
+bool EmbedBasicPatterns(int32_t version, CBC_CommonByteMatrix* matrix) {
+  if (!EmbedPositionDetectionPatternsAndSeparators(matrix))
+    return false;
+  if (!EmbedDarkDotAtLeftBottomCorner(matrix))
+    return false;
+  if (!MaybeEmbedPositionAdjustmentPatterns(version, matrix))
+    return false;
+  if (!EmbedTimingPatterns(matrix))
+    return false;
+  return true;
 }
-CBC_QRCoderMatrixUtil::CBC_QRCoderMatrixUtil() {}
-CBC_QRCoderMatrixUtil::~CBC_QRCoderMatrixUtil() {}
+
+}  // namespace
+
+bool CBC_QRCoderMatrixUtil::BuildMatrix(
+    CBC_QRCoderBitVector* dataBits,
+    const CBC_QRCoderErrorCorrectionLevel* ecLevel,
+    int32_t version,
+    int32_t maskPattern,
+    CBC_CommonByteMatrix* matrix) {
+  if (!dataBits || !matrix)
+    return false;
+
+  matrix->clear(0xff);
+
+  if (!EmbedBasicPatterns(version, matrix))
+    return false;
+  if (!EmbedTypeInfo(ecLevel, maskPattern, matrix))
+    return false;
+
+  MaybeEmbedVersionInfo(version, matrix);
+  return EmbedDataBits(dataBits, maskPattern, matrix);
+}
diff --git a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h
index 23fe0df..4bef5e1 100644
--- a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h
+++ b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h
@@ -7,82 +7,22 @@
 #ifndef FXBARCODE_QRCODE_BC_QRCODERMATRIXUTIL_H_
 #define FXBARCODE_QRCODE_BC_QRCODERMATRIXUTIL_H_
 
+#include <stdint.h>
+
 class CBC_CommonByteMatrix;
 class CBC_QRCoderErrorCorrectionLevel;
 class CBC_QRCoderBitVector;
 
 class CBC_QRCoderMatrixUtil {
  public:
-  CBC_QRCoderMatrixUtil();
-  virtual ~CBC_QRCoderMatrixUtil();
-  static void ClearMatrix(CBC_CommonByteMatrix* matrix, int32_t& e);
-  static void BuildMatrix(CBC_QRCoderBitVector* dataBits,
+  CBC_QRCoderMatrixUtil() = delete;
+  ~CBC_QRCoderMatrixUtil() = delete;
+
+  static bool BuildMatrix(CBC_QRCoderBitVector* dataBits,
                           const CBC_QRCoderErrorCorrectionLevel* ecLevel,
                           int32_t version,
                           int32_t maskPattern,
-                          CBC_CommonByteMatrix* matrix,
-                          int32_t& e);
-  static void EmbedBasicPatterns(int32_t version,
-                                 CBC_CommonByteMatrix* matrix,
-                                 int32_t& e);
-  static void EmbedTypeInfo(const CBC_QRCoderErrorCorrectionLevel* ecLevel,
-                            int32_t maskPattern,
-                            CBC_CommonByteMatrix* matrix,
-                            int32_t& e);
-  static void EmbedDataBits(CBC_QRCoderBitVector* dataBits,
-                            int32_t maskPattern,
-                            CBC_CommonByteMatrix* matrix,
-                            int32_t& e);
-  static void MaybeEmbedVersionInfo(int32_t version,
-                                    CBC_CommonByteMatrix* matrix,
-                                    int32_t& e);
-  static int32_t FindMSBSet(int32_t value);
-  static int32_t CalculateBCHCode(int32_t code, int32_t poly);
-  static void MakeTypeInfoBits(const CBC_QRCoderErrorCorrectionLevel* ecLevel,
-                               int32_t maskPattern,
-                               CBC_QRCoderBitVector* bits,
-                               int32_t& e);
-  static void MakeVersionInfoBits(int32_t version,
-                                  CBC_QRCoderBitVector* bits,
-                                  int32_t& e);
-  static bool IsEmpty(int32_t value);
-  static bool IsValidValue(int32_t value);
-  static void EmbedTimingPatterns(CBC_CommonByteMatrix* matrix, int32_t& e);
-  static void EmbedDarkDotAtLeftBottomCorner(CBC_CommonByteMatrix* matrix,
-                                             int32_t& e);
-  static void EmbedHorizontalSeparationPattern(int32_t xStart,
-                                               int32_t yStart,
-                                               CBC_CommonByteMatrix* matrix,
-                                               int32_t& e);
-  static void EmbedVerticalSeparationPattern(int32_t xStart,
-                                             int32_t yStart,
-                                             CBC_CommonByteMatrix* matrix,
-                                             int32_t& e);
-  static void EmbedPositionAdjustmentPattern(int32_t xStart,
-                                             int32_t yStart,
-                                             CBC_CommonByteMatrix* matrix,
-                                             int32_t& e);
-  static void EmbedPositionDetectionPattern(int32_t xStart,
-                                            int32_t yStart,
-                                            CBC_CommonByteMatrix* matrix,
-                                            int32_t& e);
-  static void EmbedPositionDetectionPatternsAndSeparators(
-      CBC_CommonByteMatrix* matrix,
-      int32_t& e);
-  static void MaybeEmbedPositionAdjustmentPatterns(int32_t version,
-                                                   CBC_CommonByteMatrix* matrix,
-                                                   int32_t& e);
-
- private:
-  static const int32_t POSITION_DETECTION_PATTERN[7][7];
-  static const int32_t VERTICAL_SEPARATION_PATTERN[7][1];
-  static const int32_t HORIZONTAL_SEPARATION_PATTERN[1][8];
-  static const int32_t POSITION_ADJUSTMENT_PATTERN[5][5];
-  static const int32_t POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[40][7];
-  static const int32_t TYPE_INFO_COORDINATES[15][2];
-  static const int32_t VERSION_INFO_POLY;
-  static const int32_t TYPE_INFO_POLY;
-  static const int32_t TYPE_INFO_MASK_PATTERN;
+                          CBC_CommonByteMatrix* matrix);
 };
 
 #endif  // FXBARCODE_QRCODE_BC_QRCODERMATRIXUTIL_H_
diff --git a/fxbarcode/qrcode/BC_QRCoderMode.cpp b/fxbarcode/qrcode/BC_QRCoderMode.cpp
index 0f8c227..8648f8c 100644
--- a/fxbarcode/qrcode/BC_QRCoderMode.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderMode.cpp
@@ -24,7 +24,7 @@
 
 #include <utility>
 
-#include "fxbarcode/utils.h"
+#include "core/fxcrt/fx_system.h"
 
 CBC_QRCoderMode* CBC_QRCoderMode::sBYTE = nullptr;
 CBC_QRCoderMode* CBC_QRCoderMode::sNUMERIC = nullptr;
@@ -38,135 +38,64 @@
 CBC_QRCoderMode* CBC_QRCoderMode::sSTRUCTURED_APPEND = nullptr;
 
 CBC_QRCoderMode::CBC_QRCoderMode(std::vector<int32_t> charCountBits,
-                                 int32_t bits,
-                                 ByteString name)
-    : m_characterCountBitsForVersions(std::move(charCountBits)),
-      m_bits(bits),
-      m_name(name) {}
+                                 int32_t bits)
+    : m_characterCountBitsForVersions(std::move(charCountBits)), m_bits(bits) {}
 
-CBC_QRCoderMode::~CBC_QRCoderMode() {}
+CBC_QRCoderMode::~CBC_QRCoderMode() = default;
 
 void CBC_QRCoderMode::Initialize() {
-  sBYTE = new CBC_QRCoderMode({8, 16, 16}, 0x4, "BYTE");
-  sALPHANUMERIC = new CBC_QRCoderMode({9, 11, 13}, 0x2, "ALPHANUMERIC");
-  sECI = new CBC_QRCoderMode(std::vector<int32_t>(), 0x7, "ECI");
-  sKANJI = new CBC_QRCoderMode({8, 10, 12}, 0x8, "KANJI");
-  sNUMERIC = new CBC_QRCoderMode({10, 12, 14}, 0x1, "NUMERIC");
-  sGBK = new CBC_QRCoderMode({8, 10, 12}, 0x0D, "GBK");
-  sTERMINATOR = new CBC_QRCoderMode(std::vector<int32_t>(), 0x00, "TERMINATOR");
-  sFNC1_FIRST_POSITION =
-      new CBC_QRCoderMode(std::vector<int32_t>(), 0x05, "FNC1_FIRST_POSITION");
-  sFNC1_SECOND_POSITION =
-      new CBC_QRCoderMode(std::vector<int32_t>(), 0x09, "FNC1_SECOND_POSITION");
-  sSTRUCTURED_APPEND =
-      new CBC_QRCoderMode(std::vector<int32_t>(), 0x03, "STRUCTURED_APPEND");
+  sBYTE = new CBC_QRCoderMode({8, 16, 16}, 0x4);
+  sALPHANUMERIC = new CBC_QRCoderMode({9, 11, 13}, 0x2);
+  sECI = new CBC_QRCoderMode(std::vector<int32_t>(), 0x7);
+  sKANJI = new CBC_QRCoderMode({8, 10, 12}, 0x8);
+  sNUMERIC = new CBC_QRCoderMode({10, 12, 14}, 0x1);
+  sGBK = new CBC_QRCoderMode({8, 10, 12}, 0x0D);
+  sTERMINATOR = new CBC_QRCoderMode(std::vector<int32_t>(), 0x00);
+  sFNC1_FIRST_POSITION = new CBC_QRCoderMode(std::vector<int32_t>(), 0x05);
+  sFNC1_SECOND_POSITION = new CBC_QRCoderMode(std::vector<int32_t>(), 0x09);
+  sSTRUCTURED_APPEND = new CBC_QRCoderMode(std::vector<int32_t>(), 0x03);
 }
 
 void CBC_QRCoderMode::Finalize() {
   delete sBYTE;
+  sBYTE = nullptr;
   delete sALPHANUMERIC;
+  sALPHANUMERIC = nullptr;
   delete sECI;
+  sECI = nullptr;
   delete sKANJI;
+  sKANJI = nullptr;
   delete sNUMERIC;
+  sNUMERIC = nullptr;
   delete sGBK;
+  sGBK = nullptr;
   delete sTERMINATOR;
+  sTERMINATOR = nullptr;
   delete sFNC1_FIRST_POSITION;
+  sFNC1_FIRST_POSITION = nullptr;
   delete sFNC1_SECOND_POSITION;
+  sFNC1_SECOND_POSITION = nullptr;
   delete sSTRUCTURED_APPEND;
-}
-
-CBC_QRCoderMode* CBC_QRCoderMode::ForBits(int32_t bits, int32_t& e) {
-  switch (bits) {
-    case 0x0:
-      return sTERMINATOR;
-    case 0x1:
-      return sNUMERIC;
-    case 0x2:
-      return sALPHANUMERIC;
-    case 0x3:
-      return sSTRUCTURED_APPEND;
-    case 0x4:
-      return sBYTE;
-    case 0x5:
-      return sFNC1_FIRST_POSITION;
-    case 0x7:
-      return sECI;
-    case 0x8:
-      return sKANJI;
-    case 0x9:
-      return sFNC1_SECOND_POSITION;
-    case 0x0D:
-      return sGBK;
-    default:
-      e = BCExceptionUnsupportedMode;
-      return nullptr;
-  }
+  sSTRUCTURED_APPEND = nullptr;
 }
 
 int32_t CBC_QRCoderMode::GetBits() const {
   return m_bits;
 }
 
-ByteString CBC_QRCoderMode::GetName() const {
-  return m_name;
-}
-
-int32_t CBC_QRCoderMode::GetCharacterCountBits(int32_t number,
-                                               int32_t& e) const {
-  if (m_characterCountBitsForVersions.empty()) {
-    e = BCExceptionCharacterNotThisMode;
+int32_t CBC_QRCoderMode::GetCharacterCountBits(int32_t number) const {
+  if (m_characterCountBitsForVersions.empty())
     return 0;
-  }
-  int32_t offset;
-  if (number <= 9) {
-    offset = 0;
-  } else if (number <= 26) {
-    offset = 1;
-  } else {
-    offset = 2;
-  }
-  return m_characterCountBitsForVersions[offset];
-}
 
-void CBC_QRCoderMode::Destroy() {
-  if (sBYTE) {
-    delete CBC_QRCoderMode::sBYTE;
-    sBYTE = nullptr;
-  }
-  if (sNUMERIC) {
-    delete CBC_QRCoderMode::sNUMERIC;
-    sNUMERIC = nullptr;
-  }
-  if (sALPHANUMERIC) {
-    delete CBC_QRCoderMode::sALPHANUMERIC;
-    sALPHANUMERIC = nullptr;
-  }
-  if (sKANJI) {
-    delete CBC_QRCoderMode::sKANJI;
-    sKANJI = nullptr;
-  }
-  if (sECI) {
-    delete CBC_QRCoderMode::sECI;
-    sECI = nullptr;
-  }
-  if (sGBK) {
-    delete CBC_QRCoderMode::sGBK;
-    sGBK = nullptr;
-  }
-  if (sTERMINATOR) {
-    delete CBC_QRCoderMode::sTERMINATOR;
-    sTERMINATOR = nullptr;
-  }
-  if (sFNC1_FIRST_POSITION) {
-    delete CBC_QRCoderMode::sFNC1_FIRST_POSITION;
-    sFNC1_FIRST_POSITION = nullptr;
-  }
-  if (sFNC1_SECOND_POSITION) {
-    delete CBC_QRCoderMode::sFNC1_SECOND_POSITION;
-    sFNC1_SECOND_POSITION = nullptr;
-  }
-  if (sSTRUCTURED_APPEND) {
-    delete CBC_QRCoderMode::sSTRUCTURED_APPEND;
-    sSTRUCTURED_APPEND = nullptr;
-  }
+  int32_t offset;
+  if (number <= 9)
+    offset = 0;
+  else if (number <= 26)
+    offset = 1;
+  else
+    offset = 2;
+
+  int32_t result = m_characterCountBitsForVersions[offset];
+  ASSERT(result != 0);
+  return result;
 }
diff --git a/fxbarcode/qrcode/BC_QRCoderMode.h b/fxbarcode/qrcode/BC_QRCoderMode.h
index 9f2844d..b61d751 100644
--- a/fxbarcode/qrcode/BC_QRCoderMode.h
+++ b/fxbarcode/qrcode/BC_QRCoderMode.h
@@ -11,20 +11,15 @@
 
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
-
-class CBC_QRCoderMode {
+class CBC_QRCoderMode final {
  public:
-  virtual ~CBC_QRCoderMode();
+  ~CBC_QRCoderMode();
 
   static void Initialize();
   static void Finalize();
-  static CBC_QRCoderMode* ForBits(int32_t bits, int32_t& e);
-  static void Destroy();
 
-  int32_t GetCharacterCountBits(int32_t number, int32_t& e) const;
+  int32_t GetCharacterCountBits(int32_t number) const;
   int32_t GetBits() const;
-  ByteString GetName() const;
 
   static CBC_QRCoderMode* sBYTE;
   static CBC_QRCoderMode* sNUMERIC;
@@ -38,14 +33,10 @@
   static CBC_QRCoderMode* sSTRUCTURED_APPEND;
 
  private:
-  CBC_QRCoderMode();
-  CBC_QRCoderMode(std::vector<int32_t> charCountBits,
-                  int32_t bits,
-                  ByteString name);
+  CBC_QRCoderMode(std::vector<int32_t> charCountBits, int32_t bits);
 
   std::vector<int32_t> m_characterCountBitsForVersions;
   const int32_t m_bits;
-  const ByteString m_name;
 };
 
 #endif  // FXBARCODE_QRCODE_BC_QRCODERMODE_H_
diff --git a/fxbarcode/qrcode/BC_QRCoderVersion.cpp b/fxbarcode/qrcode/BC_QRCoderVersion.cpp
index 7da6033..9263ee7 100644
--- a/fxbarcode/qrcode/BC_QRCoderVersion.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderVersion.cpp
@@ -29,7 +29,6 @@
 #include "fxbarcode/qrcode/BC_QRCoderBitVector.h"
 #include "fxbarcode/qrcode/BC_QRCoderECBlocksData.h"
 #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h"
-#include "fxbarcode/utils.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
@@ -48,8 +47,7 @@
   m_totalCodeWords = m_ecBlocksArray[0]->GetTotalDataCodeWords();
 }
 
-CBC_QRCoderVersion::~CBC_QRCoderVersion() {
-}
+CBC_QRCoderVersion::~CBC_QRCoderVersion() = default;
 
 // static
 void CBC_QRCoderVersion::Initialize() {
@@ -76,11 +74,6 @@
   return (*g_VERSION)[versionNumber - 1].get();
 }
 
-// static
-void CBC_QRCoderVersion::Destroy() {
-  g_VERSION->clear();
-}
-
 int32_t CBC_QRCoderVersion::GetVersionNumber() const {
   return m_versionNumber;
 }
diff --git a/fxbarcode/qrcode/BC_QRCoderVersion.h b/fxbarcode/qrcode/BC_QRCoderVersion.h
index bfa213c..b94a9e7 100644
--- a/fxbarcode/qrcode/BC_QRCoderVersion.h
+++ b/fxbarcode/qrcode/BC_QRCoderVersion.h
@@ -8,7 +8,6 @@
 #define FXBARCODE_QRCODE_BC_QRCODERVERSION_H_
 
 #include <memory>
-#include <vector>
 
 #include "fxbarcode/qrcode/BC_QRCoderECBlocks.h"
 
@@ -26,7 +25,6 @@
   static void Finalize();
 
   static const CBC_QRCoderVersion* GetVersionForNumber(int32_t versionNumber);
-  static void Destroy();
 
   int32_t GetVersionNumber() const;
   int32_t GetTotalCodeWords() const;
diff --git a/fxbarcode/utils.h b/fxbarcode/utils.h
index 2303af4..fe3caa6 100644
--- a/fxbarcode/utils.h
+++ b/fxbarcode/utils.h
@@ -7,26 +7,6 @@
 #ifndef FXBARCODE_UTILS_H_
 #define FXBARCODE_UTILS_H_
 
-#include <ctype.h>
-
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-
-bool BC_FX_ByteString_Replace(ByteString& dst,
-                              uint32_t first,
-                              uint32_t last,
-                              int32_t count,
-                              char c);
-void BC_FX_ByteString_Append(ByteString& dst, int32_t count, char c);
-void BC_FX_ByteString_Append(ByteString& dst, const std::vector<uint8_t>& ba);
-
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-#include <limits>
-#elif _FX_OS_ == _FX_OS_MACOSX_ || _FX_OS_ == _FX_OS_LINUX_
-#include <limits.h>
-#endif
-
 enum BCFORMAT {
   BCFORMAT_UNSPECIFY = -1,
   BCFORMAT_CODABAR,
@@ -42,29 +22,4 @@
   BCFORMAT_QR_CODE
 };
 
-#define BCExceptionNO 0
-#define BCExceptionIllegalArgument 16
-#define BCExceptionDegreeIsNegative 31
-#define BCExceptionAIsZero 37
-#define BCExceptionValueMustBeEither0or1 50
-#define BCExceptionBadIndexException 52
-#define BCExceptionNoSuchVersion 58
-#define BCExceptionUnsupportedMode 64
-#define BCExceptionInvalidateCharacter 65
-#define BCExceptionInvalidateMaskPattern 68
-#define BCExceptionNullPointer 69
-#define BCExceptionBadMask 70
-#define BCExceptionInvalidateImageData 73
-#define BCExceptionHeight_8BeZero 74
-#define BCExceptionCharacterNotThisMode 75
-#define BCExceptionInvalidateData 77
-#define BCExceptionCharactersOutsideISO88591Encoding 87
-#define BCExceptionIllegalDataCodewords 88
-#define BCExceptionIllegalStateUnexpectedCase 90
-#define BCExceptionIllegalStateMessageLengthInvalid 92
-#define BCExceptionIllegalArgumentNotGigits 93
-#define BCExceptionIllegalStateIllegalMode 94
-#define BCExceptionNonEncodableCharacterDetected 96
-#define BCExceptionGeneric 107
-
 #endif  // FXBARCODE_UTILS_H_
diff --git a/fxjs/Android.bp b/fxjs/Android.bp
new file mode 100644
index 0000000..9616816
--- /dev/null
+++ b/fxjs/Android.bp
@@ -0,0 +1,30 @@
+cc_library_static {
+    name: "libpdfium-fxjs",
+    defaults: ["pdfium-core"],
+
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    header_libs: [
+        "libpdfium-constants",
+    ],
+
+    static_libs: [
+        "libpdfium-fdrm",
+        "libpdfium-page",
+        "libpdfium-parser",
+        "libpdfium-render",
+        "libpdfium-fpdfdoc",
+        "libpdfium-fxge",
+    ],
+
+    srcs: [
+        "cjs_event_context_stub.cpp",
+        "cjs_runtimestub.cpp",
+        "ijs_runtime.cpp",
+    ],
+
+    include_dirs: [
+        "external/freetype/include",
+        "external/freetype/include/freetype",
+    ],
+}
diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn
new file mode 100644
index 0000000..2fb819c
--- /dev/null
+++ b/fxjs/BUILD.gn
@@ -0,0 +1,252 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../pdfium.gni")
+import("../testing/test.gni")
+
+source_set("fxjs") {
+  sources = [
+    "cjs_event_context_stub.cpp",
+    "cjs_event_context_stub.h",
+    "cjs_runtimestub.cpp",
+    "cjs_runtimestub.h",
+    "ijs_event_context.h",
+    "ijs_runtime.cpp",
+    "ijs_runtime.h",
+  ]
+  configs += [ "../:pdfium_core_config" ]
+  deps = [ "../core/fxcrt" ]
+  visibility = [ "../*" ]
+
+  if (pdf_enable_v8) {
+    sources += [
+      "cfx_globaldata.cpp",
+      "cfx_globaldata.h",
+      "cfx_keyvalue.cpp",
+      "cfx_keyvalue.h",
+      "cfx_v8.cpp",
+      "cfx_v8.h",
+      "cfxjs_engine.cpp",
+      "cfxjs_engine.h",
+      "cjs_annot.cpp",
+      "cjs_annot.h",
+      "cjs_app.cpp",
+      "cjs_app.h",
+      "cjs_border.cpp",
+      "cjs_border.h",
+      "cjs_color.cpp",
+      "cjs_color.h",
+      "cjs_console.cpp",
+      "cjs_console.h",
+      "cjs_delaydata.cpp",
+      "cjs_delaydata.h",
+      "cjs_display.cpp",
+      "cjs_display.h",
+      "cjs_document.cpp",
+      "cjs_document.h",
+      "cjs_event.cpp",
+      "cjs_event.h",
+      "cjs_event_context.cpp",
+      "cjs_event_context.h",
+      "cjs_eventrecorder.cpp",
+      "cjs_eventrecorder.h",
+      "cjs_field.cpp",
+      "cjs_field.h",
+      "cjs_font.cpp",
+      "cjs_font.h",
+      "cjs_global.cpp",
+      "cjs_global.h",
+      "cjs_globalarrays.cpp",
+      "cjs_globalarrays.h",
+      "cjs_globalconsts.cpp",
+      "cjs_globalconsts.h",
+      "cjs_highlight.cpp",
+      "cjs_highlight.h",
+      "cjs_icon.cpp",
+      "cjs_icon.h",
+      "cjs_object.cpp",
+      "cjs_object.h",
+      "cjs_position.cpp",
+      "cjs_position.h",
+      "cjs_publicmethods.cpp",
+      "cjs_publicmethods.h",
+      "cjs_result.cpp",
+      "cjs_result.h",
+      "cjs_runtime.cpp",
+      "cjs_runtime.h",
+      "cjs_scalehow.cpp",
+      "cjs_scalehow.h",
+      "cjs_scalewhen.cpp",
+      "cjs_scalewhen.h",
+      "cjs_style.cpp",
+      "cjs_style.h",
+      "cjs_timerobj.cpp",
+      "cjs_timerobj.h",
+      "cjs_util.cpp",
+      "cjs_util.h",
+      "cjs_zoomtype.cpp",
+      "cjs_zoomtype.h",
+      "fx_date_helpers.cpp",
+      "fx_date_helpers.h",
+      "global_timer.cpp",
+      "global_timer.h",
+      "js_define.cpp",
+      "js_define.h",
+      "js_resources.cpp",
+      "js_resources.h",
+    ]
+    deps += [
+      "../constants",
+      "../core/fdrm",
+      "../core/fpdfapi/page",
+      "../core/fpdfapi/parser",
+      "../core/fpdfapi/render",
+      "../core/fpdfdoc",
+      "../core/fxge",
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+    configs += [ "//v8:external_startup_data" ]
+    public_deps = [ "//v8" ]
+
+    if (pdf_enable_xfa) {
+      sources += [
+        "xfa/cfxjse_arguments.cpp",
+        "xfa/cfxjse_arguments.h",
+        "xfa/cfxjse_class.cpp",
+        "xfa/cfxjse_class.h",
+        "xfa/cfxjse_context.cpp",
+        "xfa/cfxjse_context.h",
+        "xfa/cfxjse_engine.cpp",
+        "xfa/cfxjse_engine.h",
+        "xfa/cfxjse_formcalc_context.cpp",
+        "xfa/cfxjse_formcalc_context.h",
+        "xfa/cfxjse_isolatetracker.cpp",
+        "xfa/cfxjse_isolatetracker.h",
+        "xfa/cfxjse_resolveprocessor.cpp",
+        "xfa/cfxjse_resolveprocessor.h",
+        "xfa/cfxjse_runtimedata.cpp",
+        "xfa/cfxjse_runtimedata.h",
+        "xfa/cfxjse_value.cpp",
+        "xfa/cfxjse_value.h",
+        "xfa/cjx_boolean.cpp",
+        "xfa/cjx_boolean.h",
+        "xfa/cjx_container.cpp",
+        "xfa/cjx_container.h",
+        "xfa/cjx_datawindow.cpp",
+        "xfa/cjx_datawindow.h",
+        "xfa/cjx_delta.cpp",
+        "xfa/cjx_delta.h",
+        "xfa/cjx_desc.cpp",
+        "xfa/cjx_desc.h",
+        "xfa/cjx_draw.cpp",
+        "xfa/cjx_draw.h",
+        "xfa/cjx_encrypt.cpp",
+        "xfa/cjx_encrypt.h",
+        "xfa/cjx_eventpseudomodel.cpp",
+        "xfa/cjx_eventpseudomodel.h",
+        "xfa/cjx_exclgroup.cpp",
+        "xfa/cjx_exclgroup.h",
+        "xfa/cjx_extras.cpp",
+        "xfa/cjx_extras.h",
+        "xfa/cjx_field.cpp",
+        "xfa/cjx_field.h",
+        "xfa/cjx_form.cpp",
+        "xfa/cjx_form.h",
+        "xfa/cjx_handler.cpp",
+        "xfa/cjx_handler.h",
+        "xfa/cjx_hostpseudomodel.cpp",
+        "xfa/cjx_hostpseudomodel.h",
+        "xfa/cjx_instancemanager.cpp",
+        "xfa/cjx_instancemanager.h",
+        "xfa/cjx_layoutpseudomodel.cpp",
+        "xfa/cjx_layoutpseudomodel.h",
+        "xfa/cjx_list.cpp",
+        "xfa/cjx_list.h",
+        "xfa/cjx_logpseudomodel.cpp",
+        "xfa/cjx_logpseudomodel.h",
+        "xfa/cjx_manifest.cpp",
+        "xfa/cjx_manifest.h",
+        "xfa/cjx_model.cpp",
+        "xfa/cjx_model.h",
+        "xfa/cjx_node.cpp",
+        "xfa/cjx_node.h",
+        "xfa/cjx_object.cpp",
+        "xfa/cjx_object.h",
+        "xfa/cjx_occur.cpp",
+        "xfa/cjx_occur.h",
+        "xfa/cjx_packet.cpp",
+        "xfa/cjx_packet.h",
+        "xfa/cjx_script.cpp",
+        "xfa/cjx_script.h",
+        "xfa/cjx_signaturepseudomodel.cpp",
+        "xfa/cjx_signaturepseudomodel.h",
+        "xfa/cjx_source.cpp",
+        "xfa/cjx_source.h",
+        "xfa/cjx_subform.cpp",
+        "xfa/cjx_subform.h",
+        "xfa/cjx_template.cpp",
+        "xfa/cjx_template.h",
+        "xfa/cjx_textnode.cpp",
+        "xfa/cjx_textnode.h",
+        "xfa/cjx_tree.cpp",
+        "xfa/cjx_tree.h",
+        "xfa/cjx_treelist.cpp",
+        "xfa/cjx_treelist.h",
+        "xfa/cjx_wsdlconnection.cpp",
+        "xfa/cjx_wsdlconnection.h",
+        "xfa/cjx_xfa.cpp",
+        "xfa/cjx_xfa.h",
+        "xfa/fxjse.cpp",
+        "xfa/fxjse.h",
+        "xfa/jse_define.h",
+      ]
+      deps += [
+        "../xfa/fgas",
+        "../xfa/fxfa/fm2js",
+      ]
+    }
+  }
+}
+
+if (pdf_enable_v8) {
+  pdfium_unittest_source_set("unittests") {
+    sources = [
+      "cfx_globaldata_unittest.cpp",
+      "cfx_v8_unittest.cpp",
+      "cfx_v8_unittest.h",
+      "cfxjs_engine_unittest.cpp",
+      "cjs_publicmethods_unittest.cpp",
+      "cjs_util_unittest.cpp",
+      "fx_date_helpers_unittest.cpp",
+    ]
+    configs = [ "//v8:external_startup_data" ]
+    deps = [ ":fxjs" ]
+    pdfium_root_dir = "../"
+  }
+
+  pdfium_embeddertest_source_set("embeddertests") {
+    sources = [
+      "cfxjs_engine_embeddertest.cpp",
+      "cjs_publicmethods_embeddertest.cpp",
+    ]
+    configs = [ "//v8:external_startup_data" ]
+    deps = [
+      ":fxjs",
+      "../fpdfsdk",
+    ]
+    pdfium_root_dir = "../"
+
+    if (pdf_enable_xfa) {
+      sources += [
+        "xfa/cfxjse_app_embeddertest.cpp",
+        "xfa/cfxjse_formcalc_context_embeddertest.cpp",
+        "xfa/cfxjse_value_embeddertest.cpp",
+        "xfa/cjx_hostpseudomodel_embeddertest.cpp",
+        "xfa/cjx_list_embeddertest.cpp",
+      ]
+      deps += [ "../xfa/fxfa" ]
+    }
+  }
+}
diff --git a/fxjs/CJX_Define.h b/fxjs/CJX_Define.h
deleted file mode 100644
index 4a26fd4..0000000
--- a/fxjs/CJX_Define.h
+++ /dev/null
@@ -1,37 +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 FXJS_CJX_DEFINE_H_
-#define FXJS_CJX_DEFINE_H_
-
-#include <vector>
-
-#include "fxjs/cjs_return.h"
-#include "fxjs/cjs_v8.h"
-
-template <class C,
-          CJS_Return (C::*M)(CJS_V8* runtime,
-                             const std::vector<v8::Local<v8::Value>>& params)>
-CJS_Return JSMethod(C* node,
-                    CJS_V8* runtime,
-                    const std::vector<v8::Local<v8::Value>>& params) {
-  return (node->*M)(runtime, params);
-}
-
-#define JS_METHOD(method_name, class_name)                 \
-  static CJS_Return method_name##_static(                  \
-      CJX_Object* node, CJS_V8* runtime,                   \
-      const std::vector<v8::Local<v8::Value>>& params) {   \
-    return JSMethod<class_name, &class_name::method_name>( \
-        static_cast<class_name*>(node), runtime, params);  \
-  }                                                        \
-  CJS_Return method_name(CJS_V8* runtime,                  \
-                         const std::vector<v8::Local<v8::Value>>& params)
-
-#define JS_PROP(prop_name) \
-  void prop_name(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute)
-
-#endif  // FXJS_CJX_DEFINE_H_
diff --git a/fxjs/DEPS b/fxjs/DEPS
index 4cc93f9..bea61c1 100644
--- a/fxjs/DEPS
+++ b/fxjs/DEPS
@@ -7,5 +7,4 @@
   '+public',
   '+fpdfsdk',
   '+v8/include',
-  '+xfa/fxfa'
 ]
diff --git a/fxjs/JS_Define.cpp b/fxjs/JS_Define.cpp
deleted file mode 100644
index 90f7557..0000000
--- a/fxjs/JS_Define.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/JS_Define.h"
-
-#include <time.h>
-
-#include <algorithm>
-#include <cmath>
-#include <limits>
-#include <vector>
-
-#include "fxjs/cjs_document.h"
-#include "fxjs/cjs_object.h"
-
-namespace {
-
-double GetLocalTZA() {
-  if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
-    return 0;
-  time_t t = 0;
-  time(&t);
-  localtime(&t);
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  // In gcc 'timezone' is a global variable declared in time.h. In VC++, that
-  // variable was removed in VC++ 2015, with _get_timezone replacing it.
-  long timezone = 0;
-  _get_timezone(&timezone);
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  return (double)(-(timezone * 1000));
-}
-
-int GetDaylightSavingTA(double d) {
-  if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
-    return 0;
-  time_t t = (time_t)(d / 1000);
-  struct tm* tmp = localtime(&t);
-  if (!tmp)
-    return 0;
-  if (tmp->tm_isdst > 0)
-    // One hour.
-    return (int)60 * 60 * 1000;
-  return 0;
-}
-
-double Mod(double x, double y) {
-  double r = fmod(x, y);
-  if (r < 0)
-    r += y;
-  return r;
-}
-
-bool IsLeapYear(int year) {
-  return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 != 0));
-}
-
-int DayFromYear(int y) {
-  return (int)(365 * (y - 1970.0) + floor((y - 1969.0) / 4) -
-               floor((y - 1901.0) / 100) + floor((y - 1601.0) / 400));
-}
-
-double TimeFromYear(int y) {
-  return 86400000.0 * DayFromYear(y);
-}
-
-static const uint16_t daysMonth[12] = {0,   31,  59,  90,  120, 151,
-                                       181, 212, 243, 273, 304, 334};
-static const uint16_t leapDaysMonth[12] = {0,   31,  60,  91,  121, 152,
-                                           182, 213, 244, 274, 305, 335};
-
-double TimeFromYearMonth(int y, int m) {
-  const uint16_t* pMonth = IsLeapYear(y) ? leapDaysMonth : daysMonth;
-  return TimeFromYear(y) + ((double)pMonth[m]) * 86400000;
-}
-
-int Day(double t) {
-  return static_cast<int>(floor(t / 86400000.0));
-}
-
-int YearFromTime(double t) {
-  // estimate the time.
-  int y = 1970 + static_cast<int>(t / (365.2425 * 86400000.0));
-  if (TimeFromYear(y) <= t) {
-    while (TimeFromYear(y + 1) <= t)
-      y++;
-  } else {
-    while (TimeFromYear(y) > t)
-      y--;
-  }
-  return y;
-}
-
-int DayWithinYear(double t) {
-  int year = YearFromTime(t);
-  int day = Day(t);
-  return day - DayFromYear(year);
-}
-
-int MonthFromTime(double t) {
-  int day = DayWithinYear(t);
-  int year = YearFromTime(t);
-  if (0 <= day && day < 31)
-    return 0;
-  if (31 <= day && day < 59 + IsLeapYear(year))
-    return 1;
-  if ((59 + IsLeapYear(year)) <= day && day < (90 + IsLeapYear(year)))
-    return 2;
-  if ((90 + IsLeapYear(year)) <= day && day < (120 + IsLeapYear(year)))
-    return 3;
-  if ((120 + IsLeapYear(year)) <= day && day < (151 + IsLeapYear(year)))
-    return 4;
-  if ((151 + IsLeapYear(year)) <= day && day < (181 + IsLeapYear(year)))
-    return 5;
-  if ((181 + IsLeapYear(year)) <= day && day < (212 + IsLeapYear(year)))
-    return 6;
-  if ((212 + IsLeapYear(year)) <= day && day < (243 + IsLeapYear(year)))
-    return 7;
-  if ((243 + IsLeapYear(year)) <= day && day < (273 + IsLeapYear(year)))
-    return 8;
-  if ((273 + IsLeapYear(year)) <= day && day < (304 + IsLeapYear(year)))
-    return 9;
-  if ((304 + IsLeapYear(year)) <= day && day < (334 + IsLeapYear(year)))
-    return 10;
-  if ((334 + IsLeapYear(year)) <= day && day < (365 + IsLeapYear(year)))
-    return 11;
-
-  return -1;
-}
-
-int DateFromTime(double t) {
-  int day = DayWithinYear(t);
-  int year = YearFromTime(t);
-  int leap = IsLeapYear(year);
-  int month = MonthFromTime(t);
-  switch (month) {
-    case 0:
-      return day + 1;
-    case 1:
-      return day - 30;
-    case 2:
-      return day - 58 - leap;
-    case 3:
-      return day - 89 - leap;
-    case 4:
-      return day - 119 - leap;
-    case 5:
-      return day - 150 - leap;
-    case 6:
-      return day - 180 - leap;
-    case 7:
-      return day - 211 - leap;
-    case 8:
-      return day - 242 - leap;
-    case 9:
-      return day - 272 - leap;
-    case 10:
-      return day - 303 - leap;
-    case 11:
-      return day - 333 - leap;
-    default:
-      return 0;
-  }
-}
-
-}  // namespace
-
-double JS_GetDateTime() {
-  if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
-    return 0;
-  time_t t = time(nullptr);
-  struct tm* pTm = localtime(&t);
-
-  int year = pTm->tm_year + 1900;
-  double t1 = TimeFromYear(year);
-
-  return t1 + pTm->tm_yday * 86400000.0 + pTm->tm_hour * 3600000.0 +
-         pTm->tm_min * 60000.0 + pTm->tm_sec * 1000.0;
-}
-
-int JS_GetYearFromTime(double dt) {
-  return YearFromTime(dt);
-}
-
-int JS_GetMonthFromTime(double dt) {
-  return MonthFromTime(dt);
-}
-
-int JS_GetDayFromTime(double dt) {
-  return DateFromTime(dt);
-}
-
-int JS_GetHourFromTime(double dt) {
-  return (int)Mod(floor(dt / (60 * 60 * 1000)), 24);
-}
-
-int JS_GetMinFromTime(double dt) {
-  return (int)Mod(floor(dt / (60 * 1000)), 60);
-}
-
-int JS_GetSecFromTime(double dt) {
-  return (int)Mod(floor(dt / 1000), 60);
-}
-
-double JS_LocalTime(double d) {
-  return d + GetLocalTZA() + GetDaylightSavingTA(d);
-}
-
-double JS_DateParse(const WideString& str) {
-  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
-  v8::Isolate::Scope isolate_scope(pIsolate);
-  v8::HandleScope scope(pIsolate);
-
-  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
-
-  // Use the built-in object method.
-  v8::Local<v8::Value> v =
-      context->Global()
-          ->Get(context, v8::String::NewFromUtf8(pIsolate, "Date",
-                                                 v8::NewStringType::kNormal)
-                             .ToLocalChecked())
-          .ToLocalChecked();
-  if (v->IsObject()) {
-    v8::Local<v8::Object> o = v->ToObject(context).ToLocalChecked();
-    v = o->Get(context, v8::String::NewFromUtf8(pIsolate, "parse",
-                                                v8::NewStringType::kNormal)
-                            .ToLocalChecked())
-            .ToLocalChecked();
-    if (v->IsFunction()) {
-      v8::Local<v8::Function> funC = v8::Local<v8::Function>::Cast(v);
-      const int argc = 1;
-      v8::Local<v8::Value> timeStr =
-          CJS_Runtime::CurrentRuntimeFromIsolate(pIsolate)->NewString(
-              str.AsStringView());
-      v8::Local<v8::Value> argv[argc] = {timeStr};
-      v = funC->Call(context, context->Global(), argc, argv).ToLocalChecked();
-      if (v->IsNumber()) {
-        double date = v->ToNumber(context).ToLocalChecked()->Value();
-        if (!std::isfinite(date))
-          return date;
-        return JS_LocalTime(date);
-      }
-    }
-  }
-  return 0;
-}
-
-double JS_MakeDay(int nYear, int nMonth, int nDate) {
-  double y = static_cast<double>(nYear);
-  double m = static_cast<double>(nMonth);
-  double dt = static_cast<double>(nDate);
-  double ym = y + floor(m / 12);
-  double mn = Mod(m, 12);
-  double t = TimeFromYearMonth(static_cast<int>(ym), static_cast<int>(mn));
-  if (YearFromTime(t) != ym || MonthFromTime(t) != mn || DateFromTime(t) != 1)
-    return std::nan("");
-
-  return Day(t) + dt - 1;
-}
-
-double JS_MakeTime(int nHour, int nMin, int nSec, int nMs) {
-  double h = static_cast<double>(nHour);
-  double m = static_cast<double>(nMin);
-  double s = static_cast<double>(nSec);
-  double milli = static_cast<double>(nMs);
-  return h * 3600000 + m * 60000 + s * 1000 + milli;
-}
-
-double JS_MakeDate(double day, double time) {
-  if (!std::isfinite(day) || !std::isfinite(time))
-    return std::nan("");
-
-  return day * 86400000 + time;
-}
-
-std::vector<v8::Local<v8::Value>> ExpandKeywordParams(
-    CJS_Runtime* pRuntime,
-    const std::vector<v8::Local<v8::Value>>& originals,
-    size_t nKeywords,
-    ...) {
-  ASSERT(nKeywords);
-
-  std::vector<v8::Local<v8::Value>> result(nKeywords, v8::Local<v8::Value>());
-  size_t size = std::min(originals.size(), nKeywords);
-  for (size_t i = 0; i < size; ++i)
-    result[i] = originals[i];
-
-  if (originals.size() != 1 || !originals[0]->IsObject() ||
-      originals[0]->IsArray()) {
-    return result;
-  }
-  result[0] = v8::Local<v8::Value>();  // Make unknown.
-
-  v8::Local<v8::Object> pObj = pRuntime->ToObject(originals[0]);
-  va_list ap;
-  va_start(ap, nKeywords);
-  for (size_t i = 0; i < nKeywords; ++i) {
-    const wchar_t* property = va_arg(ap, const wchar_t*);
-    v8::Local<v8::Value> v8Value = pRuntime->GetObjectProperty(pObj, property);
-    if (!v8Value->IsUndefined())
-      result[i] = v8Value;
-  }
-  va_end(ap);
-
-  return result;
-}
diff --git a/fxjs/JS_Define.h b/fxjs/JS_Define.h
deleted file mode 100644
index 6de56f4..0000000
--- a/fxjs/JS_Define.h
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_JS_DEFINE_H_
-#define FXJS_JS_DEFINE_H_
-
-#include <vector>
-
-#include "fxjs/cjs_object.h"
-#include "fxjs/cjs_return.h"
-#include "fxjs/fxjs_v8.h"
-#include "fxjs/js_resources.h"
-
-double JS_GetDateTime();
-int JS_GetYearFromTime(double dt);
-int JS_GetMonthFromTime(double dt);
-int JS_GetDayFromTime(double dt);
-int JS_GetHourFromTime(double dt);
-int JS_GetMinFromTime(double dt);
-int JS_GetSecFromTime(double dt);
-double JS_LocalTime(double d);
-double JS_DateParse(const WideString& str);
-double JS_MakeDay(int nYear, int nMonth, int nDay);
-double JS_MakeTime(int nHour, int nMin, int nSec, int nMs);
-double JS_MakeDate(double day, double time);
-
-// Some JS methods have the bizarre convention that they may also be called
-// with a single argument which is an object containing the actual arguments
-// as its properties. The varying arguments to this method are the property
-// names as wchar_t string literals corresponding to each positional argument.
-// The result will always contain |nKeywords| value, with unspecified ones
-// being set to type VT_unknown.
-std::vector<v8::Local<v8::Value>> ExpandKeywordParams(
-    CJS_Runtime* pRuntime,
-    const std::vector<v8::Local<v8::Value>>& originals,
-    size_t nKeywords,
-    ...);
-
-// All JS classes have a name, an object defintion ID, and the ability to
-// register themselves with FXJS_V8. We never make a BASE class on its own
-// because it can't really do anything.
-
-// Rich JS classes provide constants, methods, properties, and the ability
-// to construct native object state.
-
-template <class T, class A>
-static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new T(obj);
-  pObj->SetEmbedObject(new A(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-template <class T>
-static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<T*>(pEngine->GetObjectPrivate(obj));
-}
-
-template <class C, CJS_Return (C::*M)(CJS_Runtime*)>
-void JSPropGetter(const char* prop_name_string,
-                  const char* class_name_string,
-                  v8::Local<v8::String> property,
-                  const v8::PropertyCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
-  CJS_Return result = (pObj->*M)(pRuntime);
-  if (result.HasError()) {
-    pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
-                                        result.Error()));
-    return;
-  }
-
-  if (result.HasReturn())
-    info.GetReturnValue().Set(result.Return());
-}
-
-template <class C, CJS_Return (C::*M)(CJS_Runtime*, v8::Local<v8::Value>)>
-void JSPropSetter(const char* prop_name_string,
-                  const char* class_name_string,
-                  v8::Local<v8::String> property,
-                  v8::Local<v8::Value> value,
-                  const v8::PropertyCallbackInfo<void>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
-  CJS_Return result = (pObj->*M)(pRuntime, value);
-  if (result.HasError()) {
-    pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
-                                        result.Error()));
-  }
-}
-
-template <class C,
-          CJS_Return (C::*M)(CJS_Runtime*,
-                             const std::vector<v8::Local<v8::Value>>&)>
-void JSMethod(const char* method_name_string,
-              const char* class_name_string,
-              const v8::FunctionCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  std::vector<v8::Local<v8::Value>> parameters;
-  for (unsigned int i = 0; i < (unsigned int)info.Length(); i++)
-    parameters.push_back(info[i]);
-
-  C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
-  CJS_Return result = (pObj->*M)(pRuntime, parameters);
-  if (result.HasError()) {
-    pRuntime->Error(JSFormatErrorString(class_name_string, method_name_string,
-                                        result.Error()));
-    return;
-  }
-
-  if (result.HasReturn())
-    info.GetReturnValue().Set(result.Return());
-}
-
-#define JS_STATIC_PROP(err_name, prop_name, class_name)           \
-  static void get_##prop_name##_static(                           \
-      v8::Local<v8::String> property,                             \
-      const v8::PropertyCallbackInfo<v8::Value>& info) {          \
-    JSPropGetter<class_name, &class_name::get_##prop_name>(       \
-        #err_name, #class_name, property, info);                  \
-  }                                                               \
-  static void set_##prop_name##_static(                           \
-      v8::Local<v8::String> property, v8::Local<v8::Value> value, \
-      const v8::PropertyCallbackInfo<void>& info) {               \
-    JSPropSetter<class_name, &class_name::set_##prop_name>(       \
-        #err_name, #class_name, property, value, info);           \
-  }
-
-#define JS_STATIC_METHOD(method_name, class_name)                             \
-  static void method_name##_static(                                           \
-      const v8::FunctionCallbackInfo<v8::Value>& info) {                      \
-    JSMethod<class_name, &class_name::method_name>(#method_name, #class_name, \
-                                                   info);                     \
-  }
-
-#endif  // FXJS_JS_DEFINE_H_
diff --git a/fxjs/README b/fxjs/README
index adc5827..a1cfe32 100644
--- a/fxjs/README
+++ b/fxjs/README
@@ -20,7 +20,8 @@
 objects, regardless of the FXJS/FXJSE distinction.  Slot 0 is the
 tag and contains either:
   kPerObjectDataTag for FXJS objects, or
-  g_FXJSETagString for FXJSE Host objects, or
+  g_FXJSEHostObjectTag for FXJSE Host objects, or
+  g_FXJSEProxyObjectTag for a global proxy object under FXJSE, or
   One of 4 specific FXJSE_CLASS_DESCRIPTOR globals for FXJSE classes:
     GlobalClassDescriptor
     NormalClassDescriptor
@@ -28,6 +29,13 @@
     formcalc_fm2js_descriptor
 
 Slot 1's contents are determined by these tags:
-  kPerObjectDataTag means to expect a CFXJS_PerObjectData.
-  g_FXJSETagString means to expect a CFXJSE_HostObject.
-  A FXJSE_CLASS_DESCRIPTOR pointer means to expect a v8 function.
+  kPerObjectDataTag means an aligned pointer to CFXJS_PerObjectData.
+  g_FXJSEHostObjectTag means an aligned pointer to CFXJSE_HostObject.
+  g_FXJSEProxyObjectTag means nullptr, and to check the prototype instead.
+  A FXJSE_CLASS_DESCRIPTOR pointer means to expect an actual v8 function
+  object (or a string naming that function),  and not an aligned pointer.
+
+Because PDFium uses V8 for various unrelated purposes, there may be up to
+four v8::Contexts (JS Global Objects) associated with each document. One is
+used by FXJS and holds objects as described by the js_api_reference.pdf
+specification. The others are used by FXJSE.
diff --git a/fxjs/cfx_globaldata.cpp b/fxjs/cfx_globaldata.cpp
new file mode 100644
index 0000000..9131f02
--- /dev/null
+++ b/fxjs/cfx_globaldata.cpp
@@ -0,0 +1,399 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/cfx_globaldata.h"
+
+#include <utility>
+
+#include "core/fdrm/fx_crypt.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+constexpr size_t kMinGlobalDataBytes = 12;
+constexpr size_t kMaxGlobalDataBytes = 4 * 1024 - 8;
+constexpr uint16_t kMagic = ('X' << 8) | 'F';
+constexpr uint16_t kMaxVersion = 2;
+
+const uint8_t kRC4KEY[] = {
+    0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d,
+    0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51,
+    0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16,
+    0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22,
+    0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b,
+    0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a,
+    0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00,
+    0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0,
+    0xf8, 0x77, 0xd5, 0xa3};
+
+CFX_GlobalData* g_pInstance = nullptr;
+
+// Returns true if non-empty, setting sPropName
+bool TrimPropName(ByteString* sPropName) {
+  sPropName->Trim();
+  return sPropName->GetLength() != 0;
+}
+
+void MakeNameTypeString(const ByteString& name,
+                        CFX_Value::DataType eType,
+                        CFX_BinaryBuf* result) {
+  uint32_t dwNameLen = (uint32_t)name.GetLength();
+  result->AppendBlock(&dwNameLen, sizeof(uint32_t));
+  result->AppendString(name);
+
+  uint16_t wType = static_cast<uint16_t>(eType);
+  result->AppendBlock(&wType, sizeof(uint16_t));
+}
+
+bool MakeByteString(const ByteString& name,
+                    const CFX_KeyValue& pData,
+                    CFX_BinaryBuf* result) {
+  switch (pData.nType) {
+    case CFX_Value::DataType::NUMBER: {
+      MakeNameTypeString(name, pData.nType, result);
+      double dData = pData.dData;
+      result->AppendBlock(&dData, sizeof(double));
+      return true;
+    }
+    case CFX_Value::DataType::BOOLEAN: {
+      MakeNameTypeString(name, pData.nType, result);
+      uint16_t wData = static_cast<uint16_t>(pData.bData);
+      result->AppendBlock(&wData, sizeof(uint16_t));
+      return true;
+    }
+    case CFX_Value::DataType::STRING: {
+      MakeNameTypeString(name, pData.nType, result);
+      uint32_t dwDataLen = (uint32_t)pData.sData.GetLength();
+      result->AppendBlock(&dwDataLen, sizeof(uint32_t));
+      result->AppendString(pData.sData);
+      return true;
+    }
+    case CFX_Value::DataType::NULLOBJ: {
+      MakeNameTypeString(name, pData.nType, result);
+      return true;
+    }
+    // Arrays don't get persisted per JS spec page 484.
+    case CFX_Value::DataType::OBJECT:
+    default:
+      break;
+  }
+  return false;
+}
+
+}  // namespace
+
+// static
+CFX_GlobalData* CFX_GlobalData::GetRetainedInstance(Delegate* pDelegate) {
+  if (!g_pInstance) {
+    g_pInstance = new CFX_GlobalData(pDelegate);
+  }
+  ++g_pInstance->m_RefCount;
+  return g_pInstance;
+}
+
+bool CFX_GlobalData::Release() {
+  if (--m_RefCount)
+    return false;
+
+  delete g_pInstance;
+  g_pInstance = nullptr;
+  return true;
+}
+
+CFX_GlobalData::CFX_GlobalData(Delegate* pDelegate) : m_pDelegate(pDelegate) {
+  LoadGlobalPersistentVariables();
+}
+
+CFX_GlobalData::~CFX_GlobalData() {
+  SaveGlobalPersisitentVariables();
+}
+
+CFX_GlobalData::iterator CFX_GlobalData::FindGlobalVariable(
+    const ByteString& propname) {
+  for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
+       ++it) {
+    if ((*it)->data.sKey == propname)
+      return it;
+  }
+  return m_arrayGlobalData.end();
+}
+
+CFX_GlobalData::Element* CFX_GlobalData::GetGlobalVariable(
+    const ByteString& propname) {
+  auto iter = FindGlobalVariable(propname);
+  return iter != m_arrayGlobalData.end() ? iter->get() : nullptr;
+}
+
+void CFX_GlobalData::SetGlobalVariableNumber(ByteString sPropName,
+                                             double dData) {
+  if (!TrimPropName(&sPropName))
+    return;
+
+  CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
+  if (pData) {
+    pData->data.nType = CFX_Value::DataType::NUMBER;
+    pData->data.dData = dData;
+    return;
+  }
+  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  pNewData->data.sKey = std::move(sPropName);
+  pNewData->data.nType = CFX_Value::DataType::NUMBER;
+  pNewData->data.dData = dData;
+  m_arrayGlobalData.push_back(std::move(pNewData));
+}
+
+void CFX_GlobalData::SetGlobalVariableBoolean(ByteString sPropName,
+                                              bool bData) {
+  if (!TrimPropName(&sPropName))
+    return;
+
+  CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
+  if (pData) {
+    pData->data.nType = CFX_Value::DataType::BOOLEAN;
+    pData->data.bData = bData;
+    return;
+  }
+  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  pNewData->data.sKey = std::move(sPropName);
+  pNewData->data.nType = CFX_Value::DataType::BOOLEAN;
+  pNewData->data.bData = bData;
+  m_arrayGlobalData.push_back(std::move(pNewData));
+}
+
+void CFX_GlobalData::SetGlobalVariableString(ByteString sPropName,
+                                             const ByteString& sData) {
+  if (!TrimPropName(&sPropName))
+    return;
+
+  CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
+  if (pData) {
+    pData->data.nType = CFX_Value::DataType::STRING;
+    pData->data.sData = sData;
+    return;
+  }
+  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  pNewData->data.sKey = std::move(sPropName);
+  pNewData->data.nType = CFX_Value::DataType::STRING;
+  pNewData->data.sData = sData;
+  m_arrayGlobalData.push_back(std::move(pNewData));
+}
+
+void CFX_GlobalData::SetGlobalVariableObject(
+    ByteString sPropName,
+    std::vector<std::unique_ptr<CFX_KeyValue>> array) {
+  if (!TrimPropName(&sPropName))
+    return;
+
+  CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
+  if (pData) {
+    pData->data.nType = CFX_Value::DataType::OBJECT;
+    pData->data.objData = std::move(array);
+    return;
+  }
+  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  pNewData->data.sKey = std::move(sPropName);
+  pNewData->data.nType = CFX_Value::DataType::OBJECT;
+  pNewData->data.objData = std::move(array);
+  m_arrayGlobalData.push_back(std::move(pNewData));
+}
+
+void CFX_GlobalData::SetGlobalVariableNull(ByteString sPropName) {
+  if (!TrimPropName(&sPropName))
+    return;
+
+  CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
+  if (pData) {
+    pData->data.nType = CFX_Value::DataType::NULLOBJ;
+    return;
+  }
+  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  pNewData->data.sKey = std::move(sPropName);
+  pNewData->data.nType = CFX_Value::DataType::NULLOBJ;
+  m_arrayGlobalData.push_back(std::move(pNewData));
+}
+
+bool CFX_GlobalData::SetGlobalVariablePersistent(ByteString sPropName,
+                                                 bool bPersistent) {
+  if (!TrimPropName(&sPropName))
+    return false;
+
+  CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
+  if (!pData)
+    return false;
+
+  pData->bPersistent = bPersistent;
+  return true;
+}
+
+bool CFX_GlobalData::DeleteGlobalVariable(ByteString sPropName) {
+  if (!TrimPropName(&sPropName))
+    return false;
+
+  auto iter = FindGlobalVariable(sPropName);
+  if (iter == m_arrayGlobalData.end())
+    return false;
+
+  m_arrayGlobalData.erase(iter);
+  return true;
+}
+
+int32_t CFX_GlobalData::GetSize() const {
+  return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
+}
+
+CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) {
+  if (index < 0 || index >= GetSize())
+    return nullptr;
+  return m_arrayGlobalData[index].get();
+}
+
+bool CFX_GlobalData::LoadGlobalPersistentVariables() {
+  if (!m_pDelegate)
+    return false;
+
+  bool ret;
+  {
+    // Span can't outlive call to BufferDone().
+    Optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
+    if (!buffer.has_value() || buffer.value().empty())
+      return false;
+
+    ret = LoadGlobalPersistentVariablesFromBuffer(buffer.value());
+  }
+  m_pDelegate->BufferDone();
+  return ret;
+}
+
+bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer(
+    pdfium::span<uint8_t> buffer) {
+  if (buffer.size() < kMinGlobalDataBytes)
+    return false;
+
+  CRYPT_ArcFourCryptBlock(buffer, kRC4KEY);
+
+  uint8_t* p = buffer.data();
+  uint16_t wType = *((uint16_t*)p);
+  p += sizeof(uint16_t);
+  if (wType != kMagic)
+    return false;
+
+  uint16_t wVersion = *((uint16_t*)p);
+  p += sizeof(uint16_t);
+  if (wVersion > kMaxVersion)
+    return false;
+
+  uint32_t dwCount = *((uint32_t*)p);
+  p += sizeof(uint32_t);
+
+  uint32_t dwSize = *((uint32_t*)p);
+  p += sizeof(uint32_t);
+
+  if (dwSize != buffer.size() - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2)
+    return false;
+
+  for (int32_t i = 0, sz = dwCount; i < sz; i++) {
+    if (p > buffer.end())
+      break;
+
+    uint32_t dwNameLen = *((uint32_t*)p);
+    p += sizeof(uint32_t);
+    if (p + dwNameLen > buffer.end())
+      break;
+
+    ByteString sEntry = ByteString(p, dwNameLen);
+    p += sizeof(char) * dwNameLen;
+
+    CFX_Value::DataType wDataType =
+        static_cast<CFX_Value::DataType>(*((uint16_t*)p));
+    p += sizeof(uint16_t);
+
+    switch (wDataType) {
+      case CFX_Value::DataType::NUMBER: {
+        double dData = 0;
+        switch (wVersion) {
+          case 1: {
+            uint32_t dwData = *((uint32_t*)p);
+            p += sizeof(uint32_t);
+            dData = dwData;
+          } break;
+          case 2: {
+            dData = *((double*)p);
+            p += sizeof(double);
+          } break;
+        }
+        SetGlobalVariableNumber(sEntry, dData);
+        SetGlobalVariablePersistent(sEntry, true);
+      } break;
+      case CFX_Value::DataType::BOOLEAN: {
+        uint16_t wData = *((uint16_t*)p);
+        p += sizeof(uint16_t);
+        SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
+        SetGlobalVariablePersistent(sEntry, true);
+      } break;
+      case CFX_Value::DataType::STRING: {
+        uint32_t dwLength = *((uint32_t*)p);
+        p += sizeof(uint32_t);
+        if (p + dwLength > buffer.end())
+          break;
+
+        SetGlobalVariableString(sEntry, ByteString(p, dwLength));
+        SetGlobalVariablePersistent(sEntry, true);
+        p += sizeof(char) * dwLength;
+      } break;
+      case CFX_Value::DataType::NULLOBJ: {
+        SetGlobalVariableNull(sEntry);
+        SetGlobalVariablePersistent(sEntry, true);
+      } break;
+      case CFX_Value::DataType::OBJECT:
+      default:
+        // Arrays aren't allowed in these buffers, nor are unrecoginzed tags.
+        return false;
+    }
+  }
+  return true;
+}
+
+bool CFX_GlobalData::SaveGlobalPersisitentVariables() {
+  if (!m_pDelegate)
+    return false;
+
+  uint32_t nCount = 0;
+  CFX_BinaryBuf sData;
+  for (const auto& pElement : m_arrayGlobalData) {
+    if (!pElement->bPersistent)
+      continue;
+
+    CFX_BinaryBuf sElement;
+    if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement))
+      continue;
+
+    if (sData.GetSize() + sElement.GetSize() > kMaxGlobalDataBytes)
+      break;
+
+    sData.AppendSpan(sElement.GetSpan());
+    nCount++;
+  }
+
+  CFX_BinaryBuf sFile;
+  uint16_t wType = kMagic;
+  uint16_t wVersion = 2;
+  sFile.AppendBlock(&wType, sizeof(uint16_t));
+  sFile.AppendBlock(&wVersion, sizeof(uint16_t));
+  sFile.AppendBlock(&nCount, sizeof(uint32_t));
+
+  uint32_t dwSize = sData.GetSize();
+  sFile.AppendBlock(&dwSize, sizeof(uint32_t));
+  sFile.AppendSpan(sData.GetSpan());
+
+  CRYPT_ArcFourCryptBlock(sFile.GetSpan(), kRC4KEY);
+
+  return m_pDelegate->StoreBuffer({sFile.GetBuffer(), sFile.GetSize()});
+}
+
+CFX_GlobalData::Element::Element() = default;
+
+CFX_GlobalData::Element::~Element() = default;
diff --git a/fxjs/cfx_globaldata.h b/fxjs/cfx_globaldata.h
new file mode 100644
index 0000000..421a3b4
--- /dev/null
+++ b/fxjs/cfx_globaldata.h
@@ -0,0 +1,84 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_CFX_GLOBALDATA_H_
+#define FXJS_CFX_GLOBALDATA_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/cfx_binarybuf.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/cfx_keyvalue.h"
+#include "third_party/base/optional.h"
+#include "third_party/base/span.h"
+
+class CPDFSDK_FormFillEnvironment;
+
+class CFX_GlobalData {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    virtual bool StoreBuffer(pdfium::span<const uint8_t> pBuffer) = 0;
+    virtual Optional<pdfium::span<uint8_t>> LoadBuffer() = 0;
+    virtual void BufferDone() = 0;
+  };
+
+  class Element {
+   public:
+    Element();
+    ~Element();
+
+    CFX_KeyValue data;
+    bool bPersistent;
+  };
+
+  static CFX_GlobalData* GetRetainedInstance(Delegate* pDelegate);
+  bool Release();
+
+  void SetGlobalVariableNumber(ByteString propname, double dData);
+  void SetGlobalVariableBoolean(ByteString propname, bool bData);
+  void SetGlobalVariableString(ByteString propname, const ByteString& sData);
+  void SetGlobalVariableObject(
+      ByteString propname,
+      std::vector<std::unique_ptr<CFX_KeyValue>> array);
+  void SetGlobalVariableNull(ByteString propname);
+  bool SetGlobalVariablePersistent(ByteString propname, bool bPersistent);
+  bool DeleteGlobalVariable(ByteString propname);
+
+  int32_t GetSize() const;
+  Element* GetAt(int index);
+
+  // Exposed for testing.
+  Element* GetGlobalVariable(const ByteString& sPropname);
+
+ private:
+  using iterator = std::vector<std::unique_ptr<Element>>::iterator;
+
+  explicit CFX_GlobalData(Delegate* pDelegate);
+  ~CFX_GlobalData();
+
+  bool LoadGlobalPersistentVariables();
+  bool LoadGlobalPersistentVariablesFromBuffer(pdfium::span<uint8_t> buffer);
+  bool SaveGlobalPersisitentVariables();
+
+  iterator FindGlobalVariable(const ByteString& sPropname);
+
+  void LoadFileBuffer(const wchar_t* sFilePath,
+                      uint8_t*& pBuffer,
+                      int32_t& nLength);
+  void WriteFileBuffer(const wchar_t* sFilePath,
+                       const char* pBuffer,
+                       int32_t nLength);
+
+  size_t m_RefCount = 0;
+  UnownedPtr<Delegate> const m_pDelegate;
+  std::vector<std::unique_ptr<Element>> m_arrayGlobalData;
+};
+
+#endif  // FXJS_CFX_GLOBALDATA_H_
diff --git a/fxjs/cfx_globaldata_unittest.cpp b/fxjs/cfx_globaldata_unittest.cpp
new file mode 100644
index 0000000..3eb0ac4
--- /dev/null
+++ b/fxjs/cfx_globaldata_unittest.cpp
@@ -0,0 +1,163 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/cfx_globaldata.h"
+
+#include <utility>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestDelegate : public CFX_GlobalData::Delegate {
+ public:
+  TestDelegate() = default;
+  ~TestDelegate() override {}
+
+  bool StoreBuffer(pdfium::span<const uint8_t> buffer) override {
+    last_buffer_ = std::vector<uint8_t>(buffer.begin(), buffer.end());
+    return true;
+  }
+  Optional<pdfium::span<uint8_t>> LoadBuffer() override {
+    return pdfium::span<uint8_t>(last_buffer_);
+  }
+  void BufferDone() override {
+    last_buffer_ = std::vector<uint8_t>();  // Catch misuse after done.
+  }
+
+  std::vector<uint8_t> last_buffer_;
+};
+
+}  // namespace
+
+TEST(CFXGlobalData, GetSafety) {
+  CFX_GlobalData* pInstance = CFX_GlobalData::GetRetainedInstance(nullptr);
+  EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch"));
+  EXPECT_EQ(nullptr, pInstance->GetAt(-1));
+  EXPECT_EQ(nullptr, pInstance->GetAt(0));
+  EXPECT_EQ(nullptr, pInstance->GetAt(1));
+
+  pInstance->SetGlobalVariableNumber("double", 2.0);
+  pInstance->SetGlobalVariableString("string", "clams");
+
+  EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch"));
+  EXPECT_EQ(nullptr, pInstance->GetAt(-1));
+  EXPECT_EQ(pInstance->GetGlobalVariable("double"), pInstance->GetAt(0));
+  EXPECT_EQ(pInstance->GetGlobalVariable("string"), pInstance->GetAt(1));
+  EXPECT_EQ(nullptr, pInstance->GetAt(2));
+
+  ASSERT_TRUE(pInstance->Release());
+}
+
+TEST(CFXGlobalData, StoreReload) {
+  TestDelegate delegate;
+  std::vector<std::unique_ptr<CFX_KeyValue>> array;
+  CFX_GlobalData* pInstance = CFX_GlobalData::GetRetainedInstance(&delegate);
+  pInstance->SetGlobalVariableNumber("double", 2.0);
+  pInstance->SetGlobalVariableString("string", "clams");
+  pInstance->SetGlobalVariableBoolean("boolean", true);
+  pInstance->SetGlobalVariableNull("null");
+  pInstance->SetGlobalVariableObject("array", std::move(array));
+  pInstance->SetGlobalVariablePersistent("double", true);
+  pInstance->SetGlobalVariablePersistent("string", true);
+  pInstance->SetGlobalVariablePersistent("boolean", true);
+  pInstance->SetGlobalVariablePersistent("null", true);
+  pInstance->SetGlobalVariablePersistent("array", true);
+  ASSERT_TRUE(pInstance->Release());
+
+  pInstance = CFX_GlobalData::GetRetainedInstance(&delegate);
+  auto* element = pInstance->GetAt(0);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("double", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType);
+  EXPECT_EQ(2.0, element->data.dData);
+
+  element = pInstance->GetAt(1);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("string", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType);
+  EXPECT_EQ("clams", element->data.sData);
+
+  element = pInstance->GetAt(2);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("boolean", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType);
+  EXPECT_EQ(true, element->data.bData);
+
+  element = pInstance->GetAt(3);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("null", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType);
+
+  // Arrays don't get persisted.
+  element = pInstance->GetAt(4);
+  ASSERT_FALSE(element);
+
+  ASSERT_TRUE(pInstance->Release());
+}
+
+TEST(CFXGlobalData, ResetValues) {
+  CFX_GlobalData* pInstance = CFX_GlobalData::GetRetainedInstance(nullptr);
+  pInstance->SetGlobalVariableString("double", "bogus!!!");
+  pInstance->SetGlobalVariableString("string", "bogus!!!");
+  pInstance->SetGlobalVariableString("boolean", "bogus!!!");
+  pInstance->SetGlobalVariableString("null", "bogus!!!");
+
+  pInstance->SetGlobalVariableNumber("double", 2.0);
+  pInstance->SetGlobalVariableString("string", "clams");
+  pInstance->SetGlobalVariableBoolean("boolean", true);
+  pInstance->SetGlobalVariableNull("null");
+
+  auto* element = pInstance->GetAt(0);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("double", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType);
+  EXPECT_EQ(2.0, element->data.dData);
+
+  element = pInstance->GetAt(1);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("string", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType);
+  EXPECT_EQ("clams", element->data.sData);
+
+  element = pInstance->GetAt(2);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("boolean", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType);
+  EXPECT_EQ(true, element->data.bData);
+
+  element = pInstance->GetAt(3);
+  ASSERT_TRUE(element);
+  EXPECT_EQ("null", element->data.sKey);
+  EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType);
+
+  ASSERT_TRUE(pInstance->Release());
+}
+
+TEST(CFXGlobalData, DeleteValues) {
+  CFX_GlobalData* pInstance = CFX_GlobalData::GetRetainedInstance(nullptr);
+  pInstance->SetGlobalVariableNumber("double", 2.0);
+  pInstance->SetGlobalVariableString("string", "clams");
+  pInstance->SetGlobalVariableBoolean("boolean", true);
+  pInstance->SetGlobalVariableNull("null");
+  EXPECT_EQ(4, pInstance->GetSize());
+
+  pInstance->DeleteGlobalVariable("nonesuch");
+  EXPECT_EQ(4, pInstance->GetSize());
+
+  pInstance->DeleteGlobalVariable("boolean");
+  EXPECT_EQ(3, pInstance->GetSize());
+
+  pInstance->DeleteGlobalVariable("string");
+  EXPECT_EQ(2, pInstance->GetSize());
+
+  pInstance->DeleteGlobalVariable("double");
+  EXPECT_EQ(1, pInstance->GetSize());
+
+  pInstance->DeleteGlobalVariable("null");
+  EXPECT_EQ(0, pInstance->GetSize());
+
+  ASSERT_TRUE(pInstance->Release());
+}
diff --git a/fxjs/cfx_keyvalue.cpp b/fxjs/cfx_keyvalue.cpp
new file mode 100644
index 0000000..d1c7fae
--- /dev/null
+++ b/fxjs/cfx_keyvalue.cpp
@@ -0,0 +1,15 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/cfx_keyvalue.h"
+
+CFX_Value::CFX_Value() = default;
+
+CFX_Value::~CFX_Value() = default;
+
+CFX_KeyValue::CFX_KeyValue() = default;
+
+CFX_KeyValue::~CFX_KeyValue() = default;
diff --git a/fxjs/cfx_keyvalue.h b/fxjs/cfx_keyvalue.h
new file mode 100644
index 0000000..b66dc12
--- /dev/null
+++ b/fxjs/cfx_keyvalue.h
@@ -0,0 +1,45 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_CFX_KEYVALUE_H_
+#define FXJS_CFX_KEYVALUE_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+
+class CFX_KeyValue;
+
+class CFX_Value {
+ public:
+  enum class DataType : uint8_t {
+    NUMBER = 0,
+    BOOLEAN,
+    STRING,
+    OBJECT,
+    NULLOBJ
+  };
+
+  CFX_Value();
+  ~CFX_Value();
+
+  DataType nType = DataType::NULLOBJ;
+  bool bData;
+  double dData;
+  ByteString sData;
+  std::vector<std::unique_ptr<CFX_KeyValue>> objData;
+};
+
+class CFX_KeyValue : public CFX_Value {
+ public:
+  CFX_KeyValue();
+  ~CFX_KeyValue();
+
+  ByteString sKey;
+};
+
+#endif  // FXJS_CFX_KEYVALUE_H_
diff --git a/fxjs/cfx_v8.cpp b/fxjs/cfx_v8.cpp
new file mode 100644
index 0000000..72bf361
--- /dev/null
+++ b/fxjs/cfx_v8.cpp
@@ -0,0 +1,217 @@
+// 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 "fxjs/cfx_v8.h"
+
+#include "core/fxcrt/fx_memory.h"
+#include "third_party/base/allocator/partition_allocator/partition_alloc.h"
+
+CFX_V8::CFX_V8(v8::Isolate* isolate) : m_pIsolate(isolate) {}
+
+CFX_V8::~CFX_V8() = default;
+
+v8::Local<v8::Value> CFX_V8::GetObjectProperty(
+    v8::Local<v8::Object> pObj,
+    ByteStringView bsUTF8PropertyName) {
+  if (pObj.IsEmpty())
+    return v8::Local<v8::Value>();
+  v8::Local<v8::Value> val;
+  if (!pObj->Get(m_pIsolate->GetCurrentContext(), NewString(bsUTF8PropertyName))
+           .ToLocal(&val))
+    return v8::Local<v8::Value>();
+  return val;
+}
+
+std::vector<WideString> CFX_V8::GetObjectPropertyNames(
+    v8::Local<v8::Object> pObj) {
+  if (pObj.IsEmpty())
+    return std::vector<WideString>();
+
+  v8::Local<v8::Array> val;
+  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
+  if (!pObj->GetPropertyNames(context).ToLocal(&val))
+    return std::vector<WideString>();
+
+  std::vector<WideString> result;
+  for (uint32_t i = 0; i < val->Length(); ++i) {
+    result.push_back(ToWideString(val->Get(context, i).ToLocalChecked()));
+  }
+
+  return result;
+}
+
+bool CFX_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
+                               ByteStringView bsUTF8PropertyName,
+                               v8::Local<v8::Value> pPut) {
+  ASSERT(!pPut.IsEmpty());
+  if (pObj.IsEmpty())
+    return false;
+
+  v8::Local<v8::String> name = NewString(bsUTF8PropertyName);
+  return pObj->Set(m_pIsolate->GetCurrentContext(), name, pPut).IsJust();
+}
+
+void CFX_V8::DisposeIsolate() {
+  if (m_pIsolate)
+    m_pIsolate.Release()->Dispose();
+}
+
+v8::Local<v8::Array> CFX_V8::NewArray() {
+  return v8::Array::New(GetIsolate());
+}
+
+v8::Local<v8::Object> CFX_V8::NewObject() {
+  return v8::Object::New(GetIsolate());
+}
+
+bool CFX_V8::PutArrayElement(v8::Local<v8::Array> pArray,
+                             unsigned index,
+                             v8::Local<v8::Value> pValue) {
+  ASSERT(!pValue.IsEmpty());
+  if (pArray.IsEmpty())
+    return false;
+  return pArray->Set(m_pIsolate->GetCurrentContext(), index, pValue).IsJust();
+}
+
+v8::Local<v8::Value> CFX_V8::GetArrayElement(v8::Local<v8::Array> pArray,
+                                             unsigned index) {
+  if (pArray.IsEmpty())
+    return v8::Local<v8::Value>();
+  v8::Local<v8::Value> val;
+  if (!pArray->Get(m_pIsolate->GetCurrentContext(), index).ToLocal(&val))
+    return v8::Local<v8::Value>();
+  return val;
+}
+
+unsigned CFX_V8::GetArrayLength(v8::Local<v8::Array> pArray) {
+  if (pArray.IsEmpty())
+    return 0;
+  return pArray->Length();
+}
+
+v8::Local<v8::Number> CFX_V8::NewNumber(int number) {
+  return v8::Int32::New(GetIsolate(), number);
+}
+
+v8::Local<v8::Number> CFX_V8::NewNumber(double number) {
+  return v8::Number::New(GetIsolate(), number);
+}
+
+v8::Local<v8::Number> CFX_V8::NewNumber(float number) {
+  return v8::Number::New(GetIsolate(), number);
+}
+
+v8::Local<v8::Boolean> CFX_V8::NewBoolean(bool b) {
+  return v8::Boolean::New(GetIsolate(), b);
+}
+
+v8::Local<v8::String> CFX_V8::NewString(ByteStringView str) {
+  v8::Isolate* pIsolate = m_pIsolate ? GetIsolate() : v8::Isolate::GetCurrent();
+  return v8::String::NewFromUtf8(pIsolate, str.unterminated_c_str(),
+                                 v8::NewStringType::kNormal, str.GetLength())
+      .ToLocalChecked();
+}
+
+v8::Local<v8::String> CFX_V8::NewString(WideStringView str) {
+  // Conversion from pdfium's wchar_t wide-strings to v8's uint16_t
+  // wide-strings isn't handled by v8, so use UTF8 as a common
+  // intermediate format.
+  return NewString(FX_UTF8Encode(str).AsStringView());
+}
+
+v8::Local<v8::Value> CFX_V8::NewNull() {
+  return v8::Null(GetIsolate());
+}
+
+v8::Local<v8::Value> CFX_V8::NewUndefined() {
+  return v8::Undefined(GetIsolate());
+}
+
+v8::Local<v8::Date> CFX_V8::NewDate(double d) {
+  return v8::Date::New(m_pIsolate->GetCurrentContext(), d)
+      .ToLocalChecked()
+      .As<v8::Date>();
+}
+
+int CFX_V8::ToInt32(v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return 0;
+  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
+  v8::MaybeLocal<v8::Int32> maybe_int32 = pValue->ToInt32(context);
+  if (maybe_int32.IsEmpty())
+    return 0;
+  return maybe_int32.ToLocalChecked()->Value();
+}
+
+bool CFX_V8::ToBoolean(v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return false;
+  return pValue->BooleanValue(m_pIsolate.Get());
+}
+
+double CFX_V8::ToDouble(v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return 0.0;
+  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
+  v8::MaybeLocal<v8::Number> maybe_number = pValue->ToNumber(context);
+  if (maybe_number.IsEmpty())
+    return 0.0;
+  return maybe_number.ToLocalChecked()->Value();
+}
+
+WideString CFX_V8::ToWideString(v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return WideString();
+  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
+  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
+  if (maybe_string.IsEmpty())
+    return WideString();
+  v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked());
+  return WideString::FromUTF8(ByteStringView(*s, s.length()));
+}
+
+ByteString CFX_V8::ToByteString(v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return ByteString();
+  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
+  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
+  if (maybe_string.IsEmpty())
+    return ByteString();
+  v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked());
+  return ByteString(*s);
+}
+
+v8::Local<v8::Object> CFX_V8::ToObject(v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty() || !pValue->IsObject())
+    return v8::Local<v8::Object>();
+  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
+  return pValue->ToObject(context).ToLocalChecked();
+}
+
+v8::Local<v8::Array> CFX_V8::ToArray(v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty() || !pValue->IsArray())
+    return v8::Local<v8::Array>();
+  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
+  return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
+}
+
+void* CFX_V8ArrayBufferAllocator::Allocate(size_t length) {
+  if (length > kMaxAllowedBytes)
+    return nullptr;
+  return GetArrayBufferPartitionAllocator().root()->AllocFlags(
+      pdfium::base::PartitionAllocZeroFill, length, "CFX_V8ArrayBuffer");
+}
+
+void* CFX_V8ArrayBufferAllocator::AllocateUninitialized(size_t length) {
+  if (length > kMaxAllowedBytes)
+    return nullptr;
+  return GetArrayBufferPartitionAllocator().root()->Alloc(length,
+                                                          "CFX_V8ArrayBuffer");
+}
+
+void CFX_V8ArrayBufferAllocator::Free(void* data, size_t length) {
+  GetArrayBufferPartitionAllocator().root()->Free(data);
+}
diff --git a/fxjs/cfx_v8.h b/fxjs/cfx_v8.h
new file mode 100644
index 0000000..cb152ac
--- /dev/null
+++ b/fxjs/cfx_v8.h
@@ -0,0 +1,79 @@
+// 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 FXJS_CFX_V8_H_
+#define FXJS_CFX_V8_H_
+
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/v8.h"
+
+class CFX_V8 {
+ public:
+  explicit CFX_V8(v8::Isolate* pIsolate);
+  virtual ~CFX_V8();
+
+  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+
+  v8::Local<v8::Value> NewNull();
+  v8::Local<v8::Value> NewUndefined();
+  v8::Local<v8::Array> NewArray();
+  v8::Local<v8::Object> NewObject();
+  v8::Local<v8::Number> NewNumber(int number);
+  v8::Local<v8::Number> NewNumber(double number);
+  v8::Local<v8::Number> NewNumber(float number);
+  v8::Local<v8::Boolean> NewBoolean(bool b);
+  v8::Local<v8::String> NewString(ByteStringView str);
+  v8::Local<v8::String> NewString(WideStringView str);
+  v8::Local<v8::Date> NewDate(double d);
+
+  int ToInt32(v8::Local<v8::Value> pValue);
+  bool ToBoolean(v8::Local<v8::Value> pValue);
+  double ToDouble(v8::Local<v8::Value> pValue);
+  WideString ToWideString(v8::Local<v8::Value> pValue);
+  ByteString ToByteString(v8::Local<v8::Value> pValue);
+  v8::Local<v8::Object> ToObject(v8::Local<v8::Value> pValue);
+  v8::Local<v8::Array> ToArray(v8::Local<v8::Value> pValue);
+
+  // Arrays.
+  unsigned GetArrayLength(v8::Local<v8::Array> pArray);
+  v8::Local<v8::Value> GetArrayElement(v8::Local<v8::Array> pArray,
+                                       unsigned index);
+  bool PutArrayElement(v8::Local<v8::Array> pArray,
+                       unsigned index,
+                       v8::Local<v8::Value> pValue);
+
+  // Objects.
+  std::vector<WideString> GetObjectPropertyNames(v8::Local<v8::Object> pObj);
+  v8::Local<v8::Value> GetObjectProperty(v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName);
+  bool PutObjectProperty(v8::Local<v8::Object> pObj,
+                         ByteStringView bsUTF8PropertyName,
+                         v8::Local<v8::Value> pValue);
+
+ protected:
+  void SetIsolate(v8::Isolate* pIsolate) { m_pIsolate = pIsolate; }
+  void DisposeIsolate();
+
+ private:
+  UnownedPtr<v8::Isolate> m_pIsolate;
+};
+
+class CFX_V8ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
+  static const size_t kMaxAllowedBytes = 0x10000000;
+  void* Allocate(size_t length) override;
+  void* AllocateUninitialized(size_t length) override;
+  void Free(void* data, size_t length) override;
+};
+
+// Use with std::unique_ptr<v8::Isolate> to dispose of isolates correctly.
+struct CFX_V8IsolateDeleter {
+  inline void operator()(v8::Isolate* ptr) { ptr->Dispose(); }
+};
+
+#endif  // FXJS_CFX_V8_H_
diff --git a/fxjs/cfx_v8_unittest.cpp b/fxjs/cfx_v8_unittest.cpp
new file mode 100644
index 0000000..7b9ecb5
--- /dev/null
+++ b/fxjs/cfx_v8_unittest.cpp
@@ -0,0 +1,269 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/cfx_v8_unittest.h"
+
+#include <memory>
+
+#include "fxjs/cfx_v8.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+bool getter_sentinel = false;
+bool setter_sentinel = false;
+}  // namespace
+
+void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const {
+  ptr->Dispose();
+}
+
+FXV8UnitTest::FXV8UnitTest() = default;
+
+FXV8UnitTest::~FXV8UnitTest() = default;
+
+void FXV8UnitTest::SetUp() {
+  array_buffer_allocator_ = pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>();
+
+  v8::Isolate::CreateParams params;
+  params.array_buffer_allocator = array_buffer_allocator_.get();
+  isolate_.reset(v8::Isolate::New(params));
+
+  cfx_v8_ = pdfium::MakeUnique<CFX_V8>(isolate_.get());
+}
+
+TEST_F(FXV8UnitTest, EmptyLocal) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  v8::Local<v8::Value> empty;
+  EXPECT_FALSE(cfx_v8()->ToBoolean(empty));
+  EXPECT_EQ(0, cfx_v8()->ToInt32(empty));
+  EXPECT_EQ(0.0, cfx_v8()->ToDouble(empty));
+  EXPECT_EQ("", cfx_v8()->ToByteString(empty));
+  EXPECT_EQ(L"", cfx_v8()->ToWideString(empty));
+  EXPECT_TRUE(cfx_v8()->ToObject(empty).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(empty).IsEmpty());
+
+  // Can't set properties on empty objects, but does not fault.
+  v8::Local<v8::Value> marker = cfx_v8()->NewNumber(2);
+  v8::Local<v8::Object> empty_object;
+  EXPECT_FALSE(cfx_v8()->PutObjectProperty(empty_object, "clams", marker));
+  EXPECT_TRUE(cfx_v8()->GetObjectProperty(empty_object, "clams").IsEmpty());
+  EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(empty_object).size());
+
+  // Can't set elements in empty arrays, but does not fault.
+  v8::Local<v8::Array> empty_array;
+  EXPECT_FALSE(cfx_v8()->PutArrayElement(empty_array, 0, marker));
+  EXPECT_TRUE(cfx_v8()->GetArrayElement(empty_array, 0).IsEmpty());
+  EXPECT_EQ(0u, cfx_v8()->GetArrayLength(empty_array));
+}
+
+TEST_F(FXV8UnitTest, NewNull) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto nullz = cfx_v8()->NewNull();
+  EXPECT_FALSE(cfx_v8()->ToBoolean(nullz));
+  EXPECT_EQ(0, cfx_v8()->ToInt32(nullz));
+  EXPECT_EQ(0.0, cfx_v8()->ToDouble(nullz));
+  EXPECT_EQ("null", cfx_v8()->ToByteString(nullz));
+  EXPECT_EQ(L"null", cfx_v8()->ToWideString(nullz));
+  EXPECT_TRUE(cfx_v8()->ToObject(nullz).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(nullz).IsEmpty());
+}
+
+TEST_F(FXV8UnitTest, NewUndefined) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto undef = cfx_v8()->NewUndefined();
+  EXPECT_FALSE(cfx_v8()->ToBoolean(undef));
+  EXPECT_EQ(0, cfx_v8()->ToInt32(undef));
+  EXPECT_TRUE(std::isnan(cfx_v8()->ToDouble(undef)));
+  EXPECT_EQ("undefined", cfx_v8()->ToByteString(undef));
+  EXPECT_EQ(L"undefined", cfx_v8()->ToWideString(undef));
+  EXPECT_TRUE(cfx_v8()->ToObject(undef).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(undef).IsEmpty());
+}
+
+TEST_F(FXV8UnitTest, NewBoolean) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto boolz = cfx_v8()->NewBoolean(true);
+  EXPECT_TRUE(cfx_v8()->ToBoolean(boolz));
+  EXPECT_EQ(1, cfx_v8()->ToInt32(boolz));
+  EXPECT_EQ(1.0, cfx_v8()->ToDouble(boolz));
+  EXPECT_EQ("true", cfx_v8()->ToByteString(boolz));
+  EXPECT_EQ(L"true", cfx_v8()->ToWideString(boolz));
+  EXPECT_TRUE(cfx_v8()->ToObject(boolz).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(boolz).IsEmpty());
+
+  boolz = cfx_v8()->NewBoolean(false);
+  EXPECT_FALSE(cfx_v8()->ToBoolean(boolz));
+  EXPECT_EQ(0, cfx_v8()->ToInt32(boolz));
+  EXPECT_EQ(0.0, cfx_v8()->ToDouble(boolz));
+  EXPECT_EQ("false", cfx_v8()->ToByteString(boolz));
+  EXPECT_EQ(L"false", cfx_v8()->ToWideString(boolz));
+  EXPECT_TRUE(cfx_v8()->ToObject(boolz).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(boolz).IsEmpty());
+}
+
+TEST_F(FXV8UnitTest, NewNumber) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto num = cfx_v8()->NewNumber(42.1);
+  EXPECT_TRUE(cfx_v8()->ToBoolean(num));
+  EXPECT_EQ(42, cfx_v8()->ToInt32(num));
+  EXPECT_EQ(42.1, cfx_v8()->ToDouble(num));
+  EXPECT_EQ("42.1", cfx_v8()->ToByteString(num));
+  EXPECT_EQ(L"42.1", cfx_v8()->ToWideString(num));
+  EXPECT_TRUE(cfx_v8()->ToObject(num).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(num).IsEmpty());
+}
+
+TEST_F(FXV8UnitTest, NewString) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto str = cfx_v8()->NewString("123");
+  EXPECT_TRUE(cfx_v8()->ToBoolean(str));
+  EXPECT_EQ(123, cfx_v8()->ToInt32(str));
+  EXPECT_EQ(123, cfx_v8()->ToDouble(str));
+  EXPECT_EQ("123", cfx_v8()->ToByteString(str));
+  EXPECT_EQ(L"123", cfx_v8()->ToWideString(str));
+  EXPECT_TRUE(cfx_v8()->ToObject(str).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(str).IsEmpty());
+
+  auto str2 = cfx_v8()->NewString(L"123");
+  EXPECT_TRUE(cfx_v8()->ToBoolean(str2));
+  EXPECT_EQ(123, cfx_v8()->ToInt32(str2));
+  EXPECT_EQ(123, cfx_v8()->ToDouble(str2));
+  EXPECT_EQ("123", cfx_v8()->ToByteString(str2));
+  EXPECT_EQ(L"123", cfx_v8()->ToWideString(str2));
+  EXPECT_TRUE(cfx_v8()->ToObject(str2).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->ToArray(str2).IsEmpty());
+}
+
+TEST_F(FXV8UnitTest, NewDate) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto date = cfx_v8()->NewDate(1111111111);
+  EXPECT_TRUE(cfx_v8()->ToBoolean(date));
+  EXPECT_EQ(1111111111, cfx_v8()->ToInt32(date));
+  EXPECT_EQ(1111111111.0, cfx_v8()->ToDouble(date));
+  EXPECT_NE("", cfx_v8()->ToByteString(date));   // exact format varies.
+  EXPECT_NE(L"", cfx_v8()->ToWideString(date));  // exact format varies.
+  EXPECT_TRUE(cfx_v8()->ToObject(date)->IsObject());
+  EXPECT_TRUE(cfx_v8()->ToArray(date).IsEmpty());
+}
+
+TEST_F(FXV8UnitTest, NewArray) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto array = cfx_v8()->NewArray();
+  EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array));
+  EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
+  EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array));
+
+  EXPECT_TRUE(cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12)));
+  EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
+  EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 3).IsEmpty());
+  EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 3)->IsNumber());
+  EXPECT_EQ(4u, cfx_v8()->GetArrayLength(array));
+
+  EXPECT_TRUE(cfx_v8()->ToBoolean(array));
+  EXPECT_EQ(0, cfx_v8()->ToInt32(array));
+  double d = cfx_v8()->ToDouble(array);
+  EXPECT_NE(d, d);  // i.e. NaN.
+  EXPECT_EQ(L",,,12", cfx_v8()->ToWideString(array));
+  EXPECT_TRUE(cfx_v8()->ToObject(array)->IsObject());
+  EXPECT_TRUE(cfx_v8()->ToArray(array)->IsArray());
+}
+
+TEST_F(FXV8UnitTest, NewObject) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(v8::Context::New(isolate()));
+
+  auto object = cfx_v8()->NewObject();
+  ASSERT_FALSE(object.IsEmpty());
+  EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size());
+  EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
+  EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsUndefined());
+  EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size());
+
+  EXPECT_TRUE(
+      cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12)));
+  EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
+  EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsNumber());
+  EXPECT_EQ(1u, cfx_v8()->GetObjectPropertyNames(object).size());
+  EXPECT_EQ(L"clams", cfx_v8()->GetObjectPropertyNames(object)[0]);
+
+  EXPECT_TRUE(cfx_v8()->ToBoolean(object));
+  EXPECT_EQ(0, cfx_v8()->ToInt32(object));
+  double d = cfx_v8()->ToDouble(object);
+  EXPECT_NE(d, d);  // i.e. NaN.
+  EXPECT_EQ(L"[object Object]", cfx_v8()->ToWideString(object));
+  EXPECT_TRUE(cfx_v8()->ToObject(object)->IsObject());
+  EXPECT_TRUE(cfx_v8()->ToArray(object).IsEmpty());
+}
+
+TEST_F(FXV8UnitTest, ThrowFromGetter) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = v8::Context::New(isolate());
+  v8::Context::Scope context_scope(context);
+
+  v8::Local<v8::Object> object = cfx_v8()->NewObject();
+  v8::Local<v8::String> name = cfx_v8()->NewString("clams");
+  EXPECT_TRUE(
+      object
+          ->SetAccessor(context, name,
+                        [](v8::Local<v8::Name> property,
+                           const v8::PropertyCallbackInfo<v8::Value>& info) {
+                          getter_sentinel = true;
+                          info.GetIsolate()->ThrowException(property);
+                        })
+          .FromJust());
+  getter_sentinel = false;
+  EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
+  EXPECT_TRUE(getter_sentinel);
+}
+
+TEST_F(FXV8UnitTest, ThrowFromSetter) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = v8::Context::New(isolate());
+  v8::Context::Scope context_scope(context);
+
+  v8::Local<v8::Object> object = cfx_v8()->NewObject();
+  v8::Local<v8::String> name = cfx_v8()->NewString("clams");
+  EXPECT_TRUE(object
+                  ->SetAccessor(context, name, nullptr,
+                                [](v8::Local<v8::Name> property,
+                                   v8::Local<v8::Value> value,
+                                   const v8::PropertyCallbackInfo<void>& info) {
+                                  setter_sentinel = true;
+                                  info.GetIsolate()->ThrowException(property);
+                                })
+                  .FromJust());
+  setter_sentinel = false;
+  EXPECT_FALSE(cfx_v8()->PutObjectProperty(object, "clams", name));
+  EXPECT_TRUE(setter_sentinel);
+}
diff --git a/fxjs/cfx_v8_unittest.h b/fxjs/cfx_v8_unittest.h
new file mode 100644
index 0000000..e5d4e3f
--- /dev/null
+++ b/fxjs/cfx_v8_unittest.h
@@ -0,0 +1,39 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_CFX_V8_UNITTEST_H_
+#define FXJS_CFX_V8_UNITTEST_H_
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CFX_V8;
+class CFX_V8ArrayBufferAllocator;
+
+namespace v8 {
+class Isolate;
+}  // namespace v8
+
+class FXV8UnitTest : public ::testing::Test {
+ public:
+  struct V8IsolateDeleter {
+    void operator()(v8::Isolate* ptr) const;
+  };
+
+  FXV8UnitTest();
+  ~FXV8UnitTest() override;
+
+  void SetUp() override;
+
+  v8::Isolate* isolate() const { return isolate_.get(); }
+  CFX_V8* cfx_v8() const { return cfx_v8_.get(); }
+
+ protected:
+  std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
+  std::unique_ptr<v8::Isolate, V8IsolateDeleter> isolate_;
+  std::unique_ptr<CFX_V8> cfx_v8_;
+};
+
+#endif  // FXJS_CFX_V8_UNITTEST_H_
diff --git a/fxjs/cfxjs_engine.cpp b/fxjs/cfxjs_engine.cpp
new file mode 100644
index 0000000..c4d46a2
--- /dev/null
+++ b/fxjs/cfxjs_engine.cpp
@@ -0,0 +1,667 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/cfxjs_engine.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/xfa/cfxjse_runtimedata.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "v8/include/v8-util.h"
+
+class CFXJS_PerObjectData;
+
+namespace {
+
+unsigned int g_embedderDataSlot = 1u;
+v8::Isolate* g_isolate = nullptr;
+size_t g_isolate_ref_count = 0;
+CFX_V8ArrayBufferAllocator* g_arrayBufferAllocator = nullptr;
+v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr;
+const wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData";
+
+void* GetAlignedPointerForPerObjectDataTag() {
+  return const_cast<void*>(static_cast<const void*>(kPerObjectDataTag));
+}
+
+std::pair<int, int> GetLineAndColumnFromError(v8::Local<v8::Message> message,
+                                              v8::Local<v8::Context> context) {
+  if (message.IsEmpty())
+    return std::make_pair(-1, -1);
+  return std::make_pair(message->GetLineNumber(context).FromMaybe(-1),
+                        message->GetStartColumn());
+}
+
+}  // namespace
+
+// Global weak map to save dynamic objects.
+class V8TemplateMapTraits final
+    : public v8::StdMapTraits<CFXJS_PerObjectData*, v8::Object> {
+ public:
+  using WeakCallbackDataType = CFXJS_PerObjectData;
+  using MapType = v8::
+      GlobalValueMap<WeakCallbackDataType*, v8::Object, V8TemplateMapTraits>;
+
+  static const v8::PersistentContainerCallbackType kCallbackType =
+      v8::kWeakWithInternalFields;
+
+  static WeakCallbackDataType* WeakCallbackParameter(
+      MapType* map,
+      WeakCallbackDataType* key,
+      v8::Local<v8::Object> value) {
+    return key;
+  }
+  static MapType* MapFromWeakCallbackInfo(
+      const v8::WeakCallbackInfo<WeakCallbackDataType>&);
+  static WeakCallbackDataType* KeyFromWeakCallbackInfo(
+      const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+    return data.GetParameter();
+  }
+  static void OnWeakCallback(
+      const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {}
+  static void DisposeWeak(
+      const v8::WeakCallbackInfo<WeakCallbackDataType>& data);
+  static void Dispose(v8::Isolate* isolate,
+                      v8::Global<v8::Object> value,
+                      WeakCallbackDataType* key);
+  static void DisposeCallbackData(WeakCallbackDataType* callbackData) {}
+};
+
+class V8TemplateMap {
+ public:
+  using WeakCallbackDataType = CFXJS_PerObjectData;
+  using MapType = v8::
+      GlobalValueMap<WeakCallbackDataType*, v8::Object, V8TemplateMapTraits>;
+
+  explicit V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
+  ~V8TemplateMap() = default;
+
+  void SetAndMakeWeak(WeakCallbackDataType* key, v8::Local<v8::Object> handle) {
+    ASSERT(!m_map.Contains(key));
+
+    // Inserting an object into a GlobalValueMap with the appropriate traits
+    // has the side-effect of making the object weak deep in the guts of V8,
+    // and arranges for it to be cleaned up by the methods in the traits.
+    m_map.Set(key, handle);
+  }
+
+  friend class V8TemplateMapTraits;
+
+ private:
+  MapType m_map;
+};
+
+class CFXJS_PerObjectData {
+ public:
+  explicit CFXJS_PerObjectData(int nObjDefID) : m_ObjDefID(nObjDefID) {}
+
+  ~CFXJS_PerObjectData() = default;
+
+  static void SetInObject(CFXJS_PerObjectData* pData,
+                          v8::Local<v8::Object> pObj) {
+    if (pObj->InternalFieldCount() == 2) {
+      pObj->SetAlignedPointerInInternalField(
+          0, GetAlignedPointerForPerObjectDataTag());
+      pObj->SetAlignedPointerInInternalField(1, pData);
+    }
+  }
+
+  static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) {
+    if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 ||
+        pObj->GetAlignedPointerFromInternalField(0) !=
+            GetAlignedPointerForPerObjectDataTag()) {
+      return nullptr;
+    }
+    return static_cast<CFXJS_PerObjectData*>(
+        pObj->GetAlignedPointerFromInternalField(1));
+  }
+
+  const int m_ObjDefID;
+  std::unique_ptr<CJS_Object> m_pPrivate;
+};
+
+class CFXJS_ObjDefinition {
+ public:
+  CFXJS_ObjDefinition(v8::Isolate* isolate,
+                      const char* sObjName,
+                      FXJSOBJTYPE eObjType,
+                      CFXJS_Engine::Constructor pConstructor,
+                      CFXJS_Engine::Destructor pDestructor)
+      : m_ObjName(sObjName),
+        m_ObjType(eObjType),
+        m_pConstructor(pConstructor),
+        m_pDestructor(pDestructor),
+        m_pIsolate(isolate) {
+    v8::Isolate::Scope isolate_scope(isolate);
+    v8::HandleScope handle_scope(isolate);
+    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
+    fun->InstanceTemplate()->SetInternalFieldCount(2);
+    fun->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType));
+    if (eObjType == FXJSOBJTYPE_GLOBAL) {
+      fun->InstanceTemplate()->Set(
+          v8::Symbol::GetToStringTag(isolate),
+          v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal)
+              .ToLocalChecked());
+    }
+    m_FunctionTemplate.Reset(isolate, fun);
+    m_Signature.Reset(isolate, v8::Signature::New(isolate, fun));
+  }
+
+  static void CallHandler(const v8::FunctionCallbackInfo<v8::Value>& info) {
+    v8::Isolate* isolate = info.GetIsolate();
+    if (!info.IsConstructCall()) {
+      isolate->ThrowException(
+          v8::String::NewFromUtf8(isolate, "illegal constructor",
+                                  v8::NewStringType::kNormal)
+              .ToLocalChecked());
+      return;
+    }
+    if (info.Data().As<v8::Int32>()->Value() != FXJSOBJTYPE_DYNAMIC) {
+      isolate->ThrowException(
+          v8::String::NewFromUtf8(isolate, "not a dynamic object",
+                                  v8::NewStringType::kNormal)
+              .ToLocalChecked());
+      return;
+    }
+    v8::Local<v8::Object> holder = info.Holder();
+    ASSERT(holder->InternalFieldCount() == 2);
+    holder->SetAlignedPointerInInternalField(0, nullptr);
+    holder->SetAlignedPointerInInternalField(1, nullptr);
+  }
+
+  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+
+  void DefineConst(const char* sConstName, v8::Local<v8::Value> pDefault) {
+    GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault);
+  }
+
+  void DefineProperty(v8::Local<v8::String> sPropName,
+                      v8::AccessorGetterCallback pPropGet,
+                      v8::AccessorSetterCallback pPropPut) {
+    GetInstanceTemplate()->SetAccessor(sPropName, pPropGet, pPropPut);
+  }
+
+  void DefineMethod(v8::Local<v8::String> sMethodName,
+                    v8::FunctionCallback pMethodCall) {
+    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
+        GetIsolate(), pMethodCall, v8::Local<v8::Value>(), GetSignature());
+    fun->RemovePrototype();
+    GetInstanceTemplate()->Set(sMethodName, fun, v8::ReadOnly);
+  }
+
+  void DefineAllProperties(v8::GenericNamedPropertyQueryCallback pPropQurey,
+                           v8::GenericNamedPropertyGetterCallback pPropGet,
+                           v8::GenericNamedPropertySetterCallback pPropPut,
+                           v8::GenericNamedPropertyDeleterCallback pPropDel) {
+    GetInstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
+        pPropGet, pPropPut, pPropQurey, pPropDel, nullptr,
+        v8::Local<v8::Value>(),
+        v8::PropertyHandlerFlags::kOnlyInterceptStrings));
+  }
+
+  v8::Local<v8::ObjectTemplate> GetInstanceTemplate() {
+    v8::EscapableHandleScope scope(GetIsolate());
+    v8::Local<v8::FunctionTemplate> function =
+        m_FunctionTemplate.Get(GetIsolate());
+    return scope.Escape(function->InstanceTemplate());
+  }
+
+  v8::Local<v8::Signature> GetSignature() {
+    v8::EscapableHandleScope scope(GetIsolate());
+    return scope.Escape(m_Signature.Get(GetIsolate()));
+  }
+
+  const char* const m_ObjName;
+  const FXJSOBJTYPE m_ObjType;
+  const CFXJS_Engine::Constructor m_pConstructor;
+  const CFXJS_Engine::Destructor m_pDestructor;
+  UnownedPtr<v8::Isolate> m_pIsolate;
+  v8::Global<v8::FunctionTemplate> m_FunctionTemplate;
+  v8::Global<v8::Signature> m_Signature;
+};
+
+static v8::Local<v8::ObjectTemplate> GetGlobalObjectTemplate(
+    v8::Isolate* pIsolate) {
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(pIsolate);
+  for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) {
+    CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
+    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL)
+      return pObjDef->GetInstanceTemplate();
+  }
+  if (!g_DefaultGlobalObjectTemplate) {
+    v8::Local<v8::ObjectTemplate> hGlobalTemplate =
+        v8::ObjectTemplate::New(pIsolate);
+    hGlobalTemplate->Set(
+        v8::Symbol::GetToStringTag(pIsolate),
+        v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
+            .ToLocalChecked());
+    g_DefaultGlobalObjectTemplate =
+        new v8::Global<v8::ObjectTemplate>(pIsolate, hGlobalTemplate);
+  }
+  return g_DefaultGlobalObjectTemplate->Get(pIsolate);
+}
+
+void V8TemplateMapTraits::Dispose(v8::Isolate* isolate,
+                                  v8::Global<v8::Object> value,
+                                  WeakCallbackDataType* key) {
+  v8::Local<v8::Object> obj = value.Get(isolate);
+  if (obj.IsEmpty())
+    return;
+  int id = CFXJS_Engine::GetObjDefnID(obj);
+  if (id == -1)
+    return;
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(isolate);
+  CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(id);
+  if (!pObjDef)
+    return;
+  if (pObjDef->m_pDestructor)
+    pObjDef->m_pDestructor(obj);
+  CFXJS_Engine::FreeObjectPrivate(obj);
+}
+
+void V8TemplateMapTraits::DisposeWeak(
+    const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+  // TODO(tsepez): this is expected be called during GC.
+}
+
+V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo(
+    const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+  V8TemplateMap* pMap =
+      FXJS_PerIsolateData::Get(data.GetIsolate())->m_pDynamicObjsMap.get();
+  return pMap ? &pMap->m_map : nullptr;
+}
+
+void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) {
+  if (g_isolate) {
+    ASSERT(g_embedderDataSlot == embedderDataSlot);
+    ASSERT(g_isolate == pIsolate);
+    return;
+  }
+  g_embedderDataSlot = embedderDataSlot;
+  g_isolate = pIsolate;
+}
+
+void FXJS_Release() {
+  ASSERT(!g_isolate || g_isolate_ref_count == 0);
+  delete g_DefaultGlobalObjectTemplate;
+  g_DefaultGlobalObjectTemplate = nullptr;
+  g_isolate = nullptr;
+
+  delete g_arrayBufferAllocator;
+  g_arrayBufferAllocator = nullptr;
+}
+
+bool FXJS_GetIsolate(v8::Isolate** pResultIsolate) {
+  if (g_isolate) {
+    *pResultIsolate = g_isolate;
+    return false;
+  }
+  // Provide backwards compatibility when no external isolate.
+  if (!g_arrayBufferAllocator)
+    g_arrayBufferAllocator = new CFX_V8ArrayBufferAllocator();
+  v8::Isolate::CreateParams params;
+  params.array_buffer_allocator = g_arrayBufferAllocator;
+  *pResultIsolate = v8::Isolate::New(params);
+  return true;
+}
+
+size_t FXJS_GlobalIsolateRefCount() {
+  return g_isolate_ref_count;
+}
+
+FXJS_PerIsolateData::~FXJS_PerIsolateData() {}
+
+// static
+void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) {
+  if (!pIsolate->GetData(g_embedderDataSlot))
+    pIsolate->SetData(g_embedderDataSlot, new FXJS_PerIsolateData(pIsolate));
+}
+
+// static
+FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
+  return static_cast<FXJS_PerIsolateData*>(
+      pIsolate->GetData(g_embedderDataSlot));
+}
+
+int FXJS_PerIsolateData::MaxObjDefinitionID() const {
+  return pdfium::CollectionSize<int>(m_ObjectDefnArray);
+}
+
+FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate)
+    : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {}
+
+CFXJS_ObjDefinition* FXJS_PerIsolateData::ObjDefinitionForID(int id) const {
+  return (id >= 0 && id < MaxObjDefinitionID()) ? m_ObjectDefnArray[id].get()
+                                                : nullptr;
+}
+
+int FXJS_PerIsolateData::AssignIDForObjDefinition(
+    std::unique_ptr<CFXJS_ObjDefinition> pDefn) {
+  m_ObjectDefnArray.push_back(std::move(pDefn));
+  return m_ObjectDefnArray.size() - 1;
+}
+
+CFXJS_Engine::CFXJS_Engine() : CFX_V8(nullptr) {}
+
+CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : CFX_V8(pIsolate) {}
+
+CFXJS_Engine::~CFXJS_Engine() = default;
+
+// static
+int CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
+  CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
+  return pData ? pData->m_ObjDefID : -1;
+}
+
+// static
+void CFXJS_Engine::SetObjectPrivate(v8::Local<v8::Object> pObj,
+                                    std::unique_ptr<CJS_Object> p) {
+  CFXJS_PerObjectData* pPerObjectData =
+      CFXJS_PerObjectData::GetFromObject(pObj);
+  if (!pPerObjectData)
+    return;
+  pPerObjectData->m_pPrivate = std::move(p);
+}
+
+// static
+void CFXJS_Engine::FreeObjectPrivate(v8::Local<v8::Object> pObj) {
+  CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
+  pObj->SetAlignedPointerInInternalField(0, nullptr);
+  pObj->SetAlignedPointerInInternalField(1, nullptr);
+  delete pData;
+}
+
+int CFXJS_Engine::DefineObj(const char* sObjName,
+                            FXJSOBJTYPE eObjType,
+                            CFXJS_Engine::Constructor pConstructor,
+                            CFXJS_Engine::Destructor pDestructor) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  FXJS_PerIsolateData::SetUp(GetIsolate());
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+  return pIsolateData->AssignIDForObjDefinition(
+      pdfium::MakeUnique<CFXJS_ObjDefinition>(GetIsolate(), sObjName, eObjType,
+                                              pConstructor, pDestructor));
+}
+
+void CFXJS_Engine::DefineObjMethod(int nObjDefnID,
+                                   const char* sMethodName,
+                                   v8::FunctionCallback pMethodCall) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+  CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
+  pObjDef->DefineMethod(NewString(sMethodName), pMethodCall);
+}
+
+void CFXJS_Engine::DefineObjProperty(int nObjDefnID,
+                                     const char* sPropName,
+                                     v8::AccessorGetterCallback pPropGet,
+                                     v8::AccessorSetterCallback pPropPut) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+  CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
+  pObjDef->DefineProperty(NewString(sPropName), pPropGet, pPropPut);
+}
+
+void CFXJS_Engine::DefineObjAllProperties(
+    int nObjDefnID,
+    v8::GenericNamedPropertyQueryCallback pPropQurey,
+    v8::GenericNamedPropertyGetterCallback pPropGet,
+    v8::GenericNamedPropertySetterCallback pPropPut,
+    v8::GenericNamedPropertyDeleterCallback pPropDel) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+  CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
+  pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel);
+}
+
+void CFXJS_Engine::DefineObjConst(int nObjDefnID,
+                                  const char* sConstName,
+                                  v8::Local<v8::Value> pDefault) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+  CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
+  pObjDef->DefineConst(sConstName, pDefault);
+}
+
+void CFXJS_Engine::DefineGlobalMethod(const char* sMethodName,
+                                      v8::FunctionCallback pMethodCall) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  v8::Local<v8::FunctionTemplate> fun =
+      v8::FunctionTemplate::New(GetIsolate(), pMethodCall);
+  fun->RemovePrototype();
+  GetGlobalObjectTemplate(GetIsolate())
+      ->Set(NewString(sMethodName), fun, v8::ReadOnly);
+}
+
+void CFXJS_Engine::DefineGlobalConst(const wchar_t* sConstName,
+                                     v8::FunctionCallback pConstGetter) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  v8::Local<v8::FunctionTemplate> fun =
+      v8::FunctionTemplate::New(GetIsolate(), pConstGetter);
+  fun->RemovePrototype();
+  GetGlobalObjectTemplate(GetIsolate())
+      ->SetAccessorProperty(NewString(sConstName), fun);
+}
+
+void CFXJS_Engine::InitializeEngine() {
+  if (GetIsolate() == g_isolate)
+    ++g_isolate_ref_count;
+
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+
+  // This has to happen before we call GetGlobalObjectTemplate because that
+  // method gets the PerIsolateData from GetIsolate().
+  FXJS_PerIsolateData::SetUp(GetIsolate());
+
+  v8::Local<v8::Context> v8Context = v8::Context::New(
+      GetIsolate(), nullptr, GetGlobalObjectTemplate(GetIsolate()));
+
+  // May not have the internal fields when called from tests.
+  v8::Local<v8::Object> pThisProxy = v8Context->Global();
+  if (pThisProxy->InternalFieldCount() == 2) {
+    pThisProxy->SetAlignedPointerInInternalField(0, nullptr);
+    pThisProxy->SetAlignedPointerInInternalField(1, nullptr);
+  }
+  v8::Local<v8::Object> pThis = pThisProxy->GetPrototype().As<v8::Object>();
+  if (pThis->InternalFieldCount() == 2) {
+    pThis->SetAlignedPointerInInternalField(0, nullptr);
+    pThis->SetAlignedPointerInInternalField(1, nullptr);
+  }
+
+  v8::Context::Scope context_scope(v8Context);
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+  int maxID = pIsolateData->MaxObjDefinitionID();
+  m_StaticObjects.resize(maxID + 1);
+  for (int i = 0; i < maxID; ++i) {
+    CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
+    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
+      CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i),
+                                       v8Context->Global()
+                                           ->GetPrototype()
+                                           ->ToObject(v8Context)
+                                           .ToLocalChecked());
+      if (pObjDef->m_pConstructor) {
+        pObjDef->m_pConstructor(this, v8Context->Global()
+                                          ->GetPrototype()
+                                          ->ToObject(v8Context)
+                                          .ToLocalChecked());
+      }
+    } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) {
+      v8::Local<v8::String> pObjName = NewString(pObjDef->m_ObjName);
+      v8::Local<v8::Object> obj = NewFXJSBoundObject(i, FXJSOBJTYPE_STATIC);
+      if (!obj.IsEmpty()) {
+        v8Context->Global()->Set(v8Context, pObjName, obj).FromJust();
+        m_StaticObjects[i] = v8::Global<v8::Object>(GetIsolate(), obj);
+      }
+    }
+  }
+  m_V8Context.Reset(GetIsolate(), v8Context);
+}
+
+void CFXJS_Engine::ReleaseEngine() {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
+  v8::Local<v8::Context> context = GetV8Context();
+  v8::Context::Scope context_scope(context);
+  FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+  if (!pIsolateData)
+    return;
+
+  m_ConstArrays.clear();
+
+  for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) {
+    CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
+    v8::Local<v8::Object> pObj;
+    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
+      pObj =
+          context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
+    } else if (!m_StaticObjects[i].IsEmpty()) {
+      pObj = v8::Local<v8::Object>::New(GetIsolate(), m_StaticObjects[i]);
+      m_StaticObjects[i].Reset();
+    }
+    if (!pObj.IsEmpty()) {
+      if (pObjDef->m_pDestructor)
+        pObjDef->m_pDestructor(pObj);
+      FreeObjectPrivate(pObj);
+    }
+  }
+
+  m_V8Context.Reset();
+
+  if (GetIsolate() == g_isolate && --g_isolate_ref_count > 0)
+    return;
+
+  delete pIsolateData;
+  GetIsolate()->SetData(g_embedderDataSlot, nullptr);
+}
+
+Optional<IJS_Runtime::JS_Error> CFXJS_Engine::Execute(
+    const WideString& script) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::TryCatch try_catch(GetIsolate());
+  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
+  v8::Local<v8::Script> compiled_script;
+  if (!v8::Script::Compile(context, NewString(script.AsStringView()))
+           .ToLocal(&compiled_script)) {
+    v8::String::Utf8Value error(GetIsolate(), try_catch.Exception());
+    v8::Local<v8::Message> msg = try_catch.Message();
+    int line = -1;
+    int column = -1;
+    std::tie(line, column) = GetLineAndColumnFromError(msg, context);
+    return IJS_Runtime::JS_Error(line, column, WideString::FromUTF8(*error));
+  }
+
+  v8::Local<v8::Value> result;
+  if (!compiled_script->Run(context).ToLocal(&result)) {
+    v8::String::Utf8Value error(GetIsolate(), try_catch.Exception());
+    auto msg = try_catch.Message();
+    int line = -1;
+    int column = -1;
+    std::tie(line, column) = GetLineAndColumnFromError(msg, context);
+    return IJS_Runtime::JS_Error(line, column, WideString::FromUTF8(*error));
+  }
+  return pdfium::nullopt;
+}
+
+v8::Local<v8::Object> CFXJS_Engine::NewFXJSBoundObject(int nObjDefnID,
+                                                       FXJSOBJTYPE type) {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
+  FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(GetIsolate());
+  if (!pData)
+    return v8::Local<v8::Object>();
+
+  CFXJS_ObjDefinition* pObjDef = pData->ObjDefinitionForID(nObjDefnID);
+  if (!pObjDef)
+    return v8::Local<v8::Object>();
+
+  v8::Local<v8::Object> obj;
+  if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj))
+    return v8::Local<v8::Object>();
+
+  CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID);
+  CFXJS_PerObjectData::SetInObject(pObjData, obj);
+  if (pObjDef->m_pConstructor)
+    pObjDef->m_pConstructor(this, obj);
+
+  if (type == FXJSOBJTYPE_DYNAMIC) {
+    auto* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
+    if (pIsolateData->m_pDynamicObjsMap)
+      pIsolateData->m_pDynamicObjsMap->SetAndMakeWeak(pObjData, obj);
+  }
+  return obj;
+}
+
+v8::Local<v8::Object> CFXJS_Engine::GetThisObj() {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  if (!FXJS_PerIsolateData::Get(GetIsolate()))
+    return v8::Local<v8::Object>();
+
+  // Return the global object.
+  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
+  return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
+}
+
+void CFXJS_Engine::Error(const WideString& message) {
+  GetIsolate()->ThrowException(NewString(message.AsStringView()));
+}
+
+v8::Local<v8::Context> CFXJS_Engine::GetV8Context() {
+  return v8::Local<v8::Context>::New(GetIsolate(), m_V8Context);
+}
+
+// static
+CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Local<v8::Object> pObj) {
+  auto* pData = CFXJS_PerObjectData::GetFromObject(pObj);
+  if (pData)
+    return pData->m_pPrivate.get();
+
+  if (pObj.IsEmpty())
+    return nullptr;
+
+  // It could be a global proxy object, in which case the prototype holds
+  // the actual bound object.
+  v8::Local<v8::Value> val = pObj->GetPrototype();
+  if (!val->IsObject())
+    return nullptr;
+
+  auto* pProtoData = CFXJS_PerObjectData::GetFromObject(val.As<v8::Object>());
+  if (!pProtoData)
+    return nullptr;
+
+  auto* pIsolateData = FXJS_PerIsolateData::Get(v8::Isolate::GetCurrent());
+  if (!pIsolateData)
+    return nullptr;
+
+  CFXJS_ObjDefinition* pObjDef =
+      pIsolateData->ObjDefinitionForID(pProtoData->m_ObjDefID);
+  if (!pObjDef || pObjDef->m_ObjType != FXJSOBJTYPE_GLOBAL)
+    return nullptr;
+
+  return pProtoData->m_pPrivate.get();
+}
+
+v8::Local<v8::Array> CFXJS_Engine::GetConstArray(const WideString& name) {
+  return v8::Local<v8::Array>::New(GetIsolate(), m_ConstArrays[name]);
+}
+
+void CFXJS_Engine::SetConstArray(const WideString& name,
+                                 v8::Local<v8::Array> array) {
+  m_ConstArrays[name] = v8::Global<v8::Array>(GetIsolate(), array);
+}
diff --git a/fxjs/cfxjs_engine.h b/fxjs/cfxjs_engine.h
new file mode 100644
index 0000000..933c250
--- /dev/null
+++ b/fxjs/cfxjs_engine.h
@@ -0,0 +1,143 @@
+// 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
+
+// CFXJS_ENGINE is a layer that makes it easier to define native objects in V8,
+// but has no knowledge of PDF-specific native objects. It could in theory be
+// used to implement other sets of native objects.
+
+// PDFium code should include this file rather than including V8 headers
+// directly.
+
+#ifndef FXJS_CFXJS_ENGINE_H_
+#define FXJS_CFXJS_ENGINE_H_
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+#include "fxjs/cfx_v8.h"
+#include "fxjs/ijs_runtime.h"
+#include "v8/include/v8.h"
+
+class CFXJS_ObjDefinition;
+class CJS_Object;
+class V8TemplateMap;
+
+// CFXJS_ENGINE places no restrictions on this class; it merely passes it
+// on to caller-provided methods.
+class IJS_EventContext;  // A description of the event that caused JS execution.
+
+enum FXJSOBJTYPE {
+  FXJSOBJTYPE_DYNAMIC = 0,  // Created by native method and returned to JS.
+  FXJSOBJTYPE_STATIC,       // Created by init and hung off of global object.
+  FXJSOBJTYPE_GLOBAL,       // The global object itself (may only appear once).
+};
+
+class FXJS_PerIsolateData {
+ public:
+  // Hook for XFA's data, when present.
+  class ExtensionIface {
+   public:
+    virtual ~ExtensionIface() = default;
+  };
+
+  ~FXJS_PerIsolateData();
+
+  static void SetUp(v8::Isolate* pIsolate);
+  static FXJS_PerIsolateData* Get(v8::Isolate* pIsolate);
+
+  int MaxObjDefinitionID() const;
+  CFXJS_ObjDefinition* ObjDefinitionForID(int id) const;
+  int AssignIDForObjDefinition(std::unique_ptr<CFXJS_ObjDefinition> pDefn);
+
+  std::vector<std::unique_ptr<CFXJS_ObjDefinition>> m_ObjectDefnArray;
+  std::unique_ptr<V8TemplateMap> m_pDynamicObjsMap;
+  std::unique_ptr<ExtensionIface> m_pFXJSERuntimeData;
+
+ protected:
+  explicit FXJS_PerIsolateData(v8::Isolate* pIsolate);
+};
+
+void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate);
+void FXJS_Release();
+
+// Gets the global isolate set by FXJS_Initialize(), or makes a new one each
+// time if there is no such isolate. Returns true if a new isolate had to be
+// created.
+bool FXJS_GetIsolate(v8::Isolate** pResultIsolate);
+
+// Get the global isolate's ref count.
+size_t FXJS_GlobalIsolateRefCount();
+
+class CFXJS_Engine : public CFX_V8 {
+ public:
+  explicit CFXJS_Engine(v8::Isolate* pIsolate);
+  ~CFXJS_Engine() override;
+
+  using Constructor =
+      std::function<void(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj)>;
+  using Destructor = std::function<void(v8::Local<v8::Object> obj)>;
+
+  static int GetObjDefnID(v8::Local<v8::Object> pObj);
+  static CJS_Object* GetObjectPrivate(v8::Local<v8::Object> pObj);
+  static void SetObjectPrivate(v8::Local<v8::Object> pObj,
+                               std::unique_ptr<CJS_Object> p);
+  static void FreeObjectPrivate(v8::Local<v8::Object> pObj);
+
+  // Always returns a valid, newly-created objDefnID.
+  int DefineObj(const char* sObjName,
+                FXJSOBJTYPE eObjType,
+                Constructor pConstructor,
+                Destructor pDestructor);
+
+  void DefineObjMethod(int nObjDefnID,
+                       const char* sMethodName,
+                       v8::FunctionCallback pMethodCall);
+  void DefineObjProperty(int nObjDefnID,
+                         const char* sPropName,
+                         v8::AccessorGetterCallback pPropGet,
+                         v8::AccessorSetterCallback pPropPut);
+  void DefineObjAllProperties(int nObjDefnID,
+                              v8::GenericNamedPropertyQueryCallback pPropQurey,
+                              v8::GenericNamedPropertyGetterCallback pPropGet,
+                              v8::GenericNamedPropertySetterCallback pPropPut,
+                              v8::GenericNamedPropertyDeleterCallback pPropDel);
+  void DefineObjConst(int nObjDefnID,
+                      const char* sConstName,
+                      v8::Local<v8::Value> pDefault);
+  void DefineGlobalMethod(const char* sMethodName,
+                          v8::FunctionCallback pMethodCall);
+  void DefineGlobalConst(const wchar_t* sConstName,
+                         v8::FunctionCallback pConstGetter);
+
+  // Called after FXJS_Define* calls made.
+  void InitializeEngine();
+  void ReleaseEngine();
+
+  // Called after FXJS_InitializeEngine call made.
+  Optional<IJS_Runtime::JS_Error> Execute(const WideString& script);
+
+  v8::Local<v8::Object> GetThisObj();
+  v8::Local<v8::Object> NewFXJSBoundObject(int nObjDefnID, FXJSOBJTYPE type);
+  void Error(const WideString& message);
+
+  v8::Local<v8::Context> GetV8Context();
+
+  v8::Local<v8::Array> GetConstArray(const WideString& name);
+  void SetConstArray(const WideString& name, v8::Local<v8::Array> array);
+
+ protected:
+  CFXJS_Engine();
+
+ private:
+  v8::Global<v8::Context> m_V8Context;
+  std::vector<v8::Global<v8::Object>> m_StaticObjects;
+  std::map<WideString, v8::Global<v8::Array>> m_ConstArrays;
+};
+
+#endif  // FXJS_CFXJS_ENGINE_H_
diff --git a/fxjs/cfxjs_engine_embeddertest.cpp b/fxjs/cfxjs_engine_embeddertest.cpp
new file mode 100644
index 0000000..c18171e
--- /dev/null
+++ b/fxjs/cfxjs_engine_embeddertest.cpp
@@ -0,0 +1,106 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/cfxjs_engine.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/js_embedder_test.h"
+
+namespace {
+
+const double kExpected0 = 6.0;
+const double kExpected1 = 7.0;
+const double kExpected2 = 8.0;
+
+const wchar_t kScript0[] = L"fred = 6";
+const wchar_t kScript1[] = L"fred = 7";
+const wchar_t kScript2[] = L"fred = 8";
+
+}  // namespace
+
+using CFXJSEngineEmbedderTest = JSEmbedderTest;
+
+void CheckAssignmentInEngineContext(CFXJS_Engine* current_engine,
+                                    double expected) {
+  v8::Context::Scope context_scope(current_engine->GetV8Context());
+  v8::Local<v8::Object> This = current_engine->GetThisObj();
+  v8::Local<v8::Value> fred = current_engine->GetObjectProperty(This, "fred");
+  EXPECT_TRUE(fred->IsNumber());
+  EXPECT_EQ(expected, current_engine->ToDouble(fred));
+}
+
+TEST_F(CFXJSEngineEmbedderTest, Getters) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(GetV8Context());
+
+  Optional<IJS_Runtime::JS_Error> err = engine()->Execute(WideString(kScript1));
+  EXPECT_FALSE(err);
+  CheckAssignmentInEngineContext(engine(), kExpected1);
+}
+
+TEST_F(CFXJSEngineEmbedderTest, MultipleEngines) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+
+  CFXJS_Engine engine1(isolate());
+  engine1.InitializeEngine();
+
+  CFXJS_Engine engine2(isolate());
+  engine2.InitializeEngine();
+
+  v8::Context::Scope context_scope(GetV8Context());
+  {
+    Optional<IJS_Runtime::JS_Error> err =
+        engine()->Execute(WideString(kScript0));
+    EXPECT_FALSE(err);
+    CheckAssignmentInEngineContext(engine(), kExpected0);
+  }
+  {
+    // engine1 executing in engine1's context doesn't affect main.
+    v8::Context::Scope context_scope1(engine1.GetV8Context());
+    Optional<IJS_Runtime::JS_Error> err = engine1.Execute(WideString(kScript1));
+    EXPECT_FALSE(err);
+    CheckAssignmentInEngineContext(engine(), kExpected0);
+    CheckAssignmentInEngineContext(&engine1, kExpected1);
+  }
+  {
+    // engine1 executing in engine2's context doesn't affect engine1.
+    v8::Context::Scope context_scope2(engine2.GetV8Context());
+    Optional<IJS_Runtime::JS_Error> err = engine1.Execute(WideString(kScript2));
+    EXPECT_FALSE(err);
+    CheckAssignmentInEngineContext(engine(), kExpected0);
+    CheckAssignmentInEngineContext(&engine1, kExpected1);
+    CheckAssignmentInEngineContext(&engine2, kExpected2);
+  }
+  engine1.ReleaseEngine();
+  engine2.ReleaseEngine();
+}
+
+TEST_F(CFXJSEngineEmbedderTest, JSCompileError) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(GetV8Context());
+
+  Optional<IJS_Runtime::JS_Error> err =
+      engine()->Execute(L"functoon(x) { return x+1; }");
+  EXPECT_TRUE(err);
+  EXPECT_STREQ(L"SyntaxError: Unexpected token '{'", err->exception.c_str());
+  EXPECT_EQ(1, err->line);
+  EXPECT_EQ(12, err->column);
+}
+
+TEST_F(CFXJSEngineEmbedderTest, JSRuntimeError) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(GetV8Context());
+
+  Optional<IJS_Runtime::JS_Error> err =
+      engine()->Execute(L"let a = 3;\nundefined.colour");
+  EXPECT_TRUE(err);
+  EXPECT_EQ(L"TypeError: Cannot read property 'colour' of undefined",
+            err->exception);
+  EXPECT_EQ(2, err->line);
+  EXPECT_EQ(10, err->column);
+}
diff --git a/fxjs/cfxjs_engine_unittest.cpp b/fxjs/cfxjs_engine_unittest.cpp
new file mode 100644
index 0000000..e0f7301
--- /dev/null
+++ b/fxjs/cfxjs_engine_unittest.cpp
@@ -0,0 +1,100 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/cfxjs_engine.h"
+
+#include <memory>
+
+#include "fxjs/cfx_v8_unittest.h"
+#include "fxjs/cjs_object.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+class FXJSEngineUnitTest : public FXV8UnitTest {
+ public:
+  FXJSEngineUnitTest() = default;
+  ~FXJSEngineUnitTest() override = default;
+
+  void SetUp() override {
+    FXV8UnitTest::SetUp();
+    FXJS_Initialize(1, isolate());
+    engine_ = pdfium::MakeUnique<CFXJS_Engine>(isolate());
+  }
+
+  void TearDown() override { FXJS_Release(); }
+
+  CFXJS_Engine* engine() const { return engine_.get(); }
+
+ private:
+  std::unique_ptr<CFXJS_Engine> engine_;
+};
+
+static bool perm_created = false;
+static bool perm_destroyed = false;
+static bool temp_created = false;
+static bool temp_destroyed = false;
+
+TEST_F(FXJSEngineUnitTest, GC) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+
+  // Object: 0
+  engine()->DefineObj(
+      "perm", FXJSOBJTYPE_DYNAMIC,
+      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+        pEngine->SetObjectPrivate(obj,
+                                  pdfium::MakeUnique<CJS_Object>(obj, nullptr));
+        perm_created = true;
+      },
+      [](v8::Local<v8::Object> obj) {
+        perm_destroyed = true;
+        CFXJS_Engine::SetObjectPrivate(obj, nullptr);
+      });
+
+  // Object: 1
+  engine()->DefineObj(
+      "temp", FXJSOBJTYPE_DYNAMIC,
+      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+        pEngine->SetObjectPrivate(obj,
+                                  pdfium::MakeUnique<CJS_Object>(obj, nullptr));
+        temp_created = true;
+      },
+      [](v8::Local<v8::Object> obj) {
+        temp_destroyed = true;
+        CFXJS_Engine::SetObjectPrivate(obj, nullptr);
+      });
+
+  engine()->InitializeEngine();
+
+  v8::Context::Scope context_scope(engine()->GetV8Context());
+  v8::Local<v8::Object> perm =
+      engine()->NewFXJSBoundObject(0, FXJSOBJTYPE_DYNAMIC);
+  EXPECT_FALSE(perm.IsEmpty());
+  EXPECT_TRUE(perm_created);
+  EXPECT_FALSE(perm_destroyed);
+
+  {
+    v8::HandleScope inner_handle_scope(isolate());
+    v8::Local<v8::Object> temp =
+        engine()->NewFXJSBoundObject(1, FXJSOBJTYPE_DYNAMIC);
+    EXPECT_FALSE(temp.IsEmpty());
+    EXPECT_TRUE(temp_created);
+    EXPECT_FALSE(temp_destroyed);
+  }
+
+  Optional<IJS_Runtime::JS_Error> err = engine()->Execute(L"gc();");
+  EXPECT_FALSE(err);
+
+  EXPECT_TRUE(perm_created);
+  EXPECT_FALSE(perm_destroyed);
+  EXPECT_TRUE(temp_created);
+  // TODO(tsepez): temp_destroyed should be true, but it isnt.
+  // EXPECT_TRUE(temp_destroyed);
+
+  engine()->ReleaseEngine();
+  EXPECT_TRUE(perm_created);
+  EXPECT_TRUE(perm_destroyed);
+  EXPECT_TRUE(temp_created);
+  EXPECT_TRUE(temp_destroyed);
+}
diff --git a/fxjs/cfxjse_arguments.cpp b/fxjs/cfxjse_arguments.cpp
deleted file mode 100644
index 663996c..0000000
--- a/fxjs/cfxjse_arguments.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_arguments.h"
-
-#include "fxjs/cfxjse_context.h"
-#include "fxjs/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-
-CFXJSE_Arguments::CFXJSE_Arguments(
-    const v8::FunctionCallbackInfo<v8::Value>* pInfo,
-    CFXJSE_Value* pRetValue)
-    : m_pInfo(pInfo), m_pRetValue(pRetValue) {}
-
-CFXJSE_Arguments::~CFXJSE_Arguments() {}
-
-int32_t CFXJSE_Arguments::GetLength() const {
-  return m_pInfo->Length();
-}
-
-std::unique_ptr<CFXJSE_Value> CFXJSE_Arguments::GetValue(int32_t index) const {
-  auto pArgValue = pdfium::MakeUnique<CFXJSE_Value>(v8::Isolate::GetCurrent());
-  pArgValue->ForceSetValue((*m_pInfo)[index]);
-  return pArgValue;
-}
-
-bool CFXJSE_Arguments::GetBoolean(int32_t index) const {
-  return (*m_pInfo)[index]->BooleanValue();
-}
-
-int32_t CFXJSE_Arguments::GetInt32(int32_t index) const {
-  return static_cast<int32_t>((*m_pInfo)[index]->NumberValue());
-}
-
-float CFXJSE_Arguments::GetFloat(int32_t index) const {
-  return static_cast<float>((*m_pInfo)[index]->NumberValue());
-}
-
-ByteString CFXJSE_Arguments::GetUTF8String(int32_t index) const {
-  v8::Local<v8::String> hString = (*m_pInfo)[index]->ToString();
-  v8::String::Utf8Value szStringVal(m_pInfo->GetIsolate(), hString);
-  return ByteString(*szStringVal);
-}
-
-CFXJSE_HostObject* CFXJSE_Arguments::GetObject(int32_t index,
-                                               CFXJSE_Class* pClass) const {
-  v8::Local<v8::Value> hValue = (*m_pInfo)[index];
-  ASSERT(!hValue.IsEmpty());
-  if (!hValue->IsObject())
-    return nullptr;
-  return FXJSE_RetrieveObjectBinding(hValue.As<v8::Object>(), pClass);
-}
-
-CFXJSE_Value* CFXJSE_Arguments::GetReturnValue() const {
-  return m_pRetValue.Get();
-}
diff --git a/fxjs/cfxjse_arguments.h b/fxjs/cfxjse_arguments.h
deleted file mode 100644
index 92a36c8..0000000
--- a/fxjs/cfxjse_arguments.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_ARGUMENTS_H_
-#define FXJS_CFXJSE_ARGUMENTS_H_
-
-#include <memory>
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "fxjs/cfxjse_class.h"
-#include "fxjs/fxjse.h"
-
-class CFXJSE_Arguments {
- public:
-  CFXJSE_Arguments(const v8::FunctionCallbackInfo<v8::Value>* pInfo,
-                   CFXJSE_Value* pRetValue);
-  ~CFXJSE_Arguments();
-
-  int32_t GetLength() const;
-  std::unique_ptr<CFXJSE_Value> GetValue(int32_t index) const;
-  bool GetBoolean(int32_t index) const;
-  int32_t GetInt32(int32_t index) const;
-  float GetFloat(int32_t index) const;
-  ByteString GetUTF8String(int32_t index) const;
-  CFXJSE_HostObject* GetObject(int32_t index,
-                               CFXJSE_Class* pClass = nullptr) const;
-  CFXJSE_Value* GetReturnValue() const;
-
- private:
-  const v8::FunctionCallbackInfo<v8::Value>* m_pInfo;
-  UnownedPtr<CFXJSE_Value> m_pRetValue;
-};
-
-#endif  // FXJS_CFXJSE_ARGUMENTS_H_
diff --git a/fxjs/cfxjse_class.cpp b/fxjs/cfxjse_class.cpp
deleted file mode 100644
index dd2181b..0000000
--- a/fxjs/cfxjse_class.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_class.h"
-
-#include <memory>
-#include <utility>
-
-#include "fxjs/cfxjse_arguments.h"
-#include "fxjs/cfxjse_context.h"
-#include "fxjs/cfxjse_value.h"
-#include "fxjs/cjs_return.h"
-#include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-void V8FunctionCallback_Wrapper(
-    const v8::FunctionCallbackInfo<v8::Value>& info) {
-  const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
-      static_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
-          info.Data().As<v8::External>()->Value());
-  if (!lpFunctionInfo)
-    return;
-
-  ByteStringView szFunctionName(lpFunctionInfo->name);
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(info.Holder());
-  auto lpRetValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  CFXJSE_Arguments impl(&info, lpRetValue.get());
-  lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl);
-  if (!lpRetValue->DirectGetValue().IsEmpty())
-    info.GetReturnValue().Set(lpRetValue->DirectGetValue());
-}
-
-void V8ConstructorCallback_Wrapper(
-    const v8::FunctionCallbackInfo<v8::Value>& info) {
-  if (!info.IsConstructCall())
-    return;
-
-  const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
-      static_cast<FXJSE_CLASS_DESCRIPTOR*>(
-          info.Data().As<v8::External>()->Value());
-  if (!lpClassDefinition)
-    return;
-
-  ASSERT(info.Holder()->InternalFieldCount());
-  info.Holder()->SetAlignedPointerInInternalField(0, nullptr);
-}
-
-void Context_GlobalObjToString(
-    const v8::FunctionCallbackInfo<v8::Value>& info) {
-  const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
-      info.Data().As<v8::External>()->Value());
-  if (!lpClass)
-    return;
-
-  if (info.This() == info.Holder() && lpClass->name) {
-    ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name);
-    info.GetReturnValue().Set(v8::String::NewFromUtf8(
-        info.GetIsolate(), szStringVal.c_str(), v8::String::kNormalString,
-        szStringVal.GetLength()));
-    return;
-  }
-  v8::Local<v8::String> local_str =
-      info.Holder()
-          ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext())
-          .FromMaybe(v8::Local<v8::String>());
-  info.GetReturnValue().Set(local_str);
-}
-
-void DynPropGetterAdapter_MethodCallback(
-    const v8::FunctionCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::Object> hCallBackInfo = info.Data().As<v8::Object>();
-  FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
-      hCallBackInfo->GetAlignedPointerFromInternalField(0));
-  v8::Local<v8::String> hPropName =
-      hCallBackInfo->GetInternalField(1).As<v8::String>();
-  ASSERT(lpClass && !hPropName.IsEmpty());
-
-  v8::String::Utf8Value szPropName(info.GetIsolate(), hPropName);
-  WideString szFxPropName = WideString::FromUTF8(*szPropName);
-
-  CJS_Return result = lpClass->dynMethodCall(info, szFxPropName);
-  if (result.HasError()) {
-    WideString err = JSFormatErrorString(*szPropName, "", result.Error());
-    v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(
-        info.GetIsolate(), ByteString::FromUnicode(err).c_str(),
-        v8::NewStringType::kNormal);
-    info.GetIsolate()->ThrowException(str.ToLocalChecked());
-    return;
-  }
-  if (result.HasReturn())
-    info.GetReturnValue().Set(result.Return());
-}
-
-void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                          CFXJSE_Value* pObject,
-                          const ByteStringView& szPropName,
-                          CFXJSE_Value* pValue) {
-  ASSERT(lpClass);
-
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
-  if (nPropType == FXJSE_ClassPropType_Property) {
-    if (lpClass->dynPropGetter)
-      lpClass->dynPropGetter(pObject, szPropName, pValue);
-  } else if (nPropType == FXJSE_ClassPropType_Method) {
-    if (lpClass->dynMethodCall && pValue) {
-      v8::Isolate* pIsolate = pValue->GetIsolate();
-      v8::HandleScope hscope(pIsolate);
-      v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
-          v8::ObjectTemplate::New(pIsolate);
-      hCallBackInfoTemplate->SetInternalFieldCount(2);
-      v8::Local<v8::Object> hCallBackInfo =
-          hCallBackInfoTemplate->NewInstance();
-      hCallBackInfo->SetAlignedPointerInInternalField(
-          0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
-      hCallBackInfo->SetInternalField(
-          1, v8::String::NewFromUtf8(
-                 pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()),
-                 v8::String::kNormalString, szPropName.GetLength()));
-      pValue->ForceSetValue(
-          v8::Function::New(pValue->GetIsolate()->GetCurrentContext(),
-                            DynPropGetterAdapter_MethodCallback, hCallBackInfo,
-                            0, v8::ConstructorBehavior::kThrow)
-              .ToLocalChecked());
-    }
-  }
-}
-
-void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                          CFXJSE_Value* pObject,
-                          const ByteStringView& szPropName,
-                          CFXJSE_Value* pValue) {
-  ASSERT(lpClass);
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
-  if (nPropType != FXJSE_ClassPropType_Method) {
-    if (lpClass->dynPropSetter)
-      lpClass->dynPropSetter(pObject, szPropName, pValue);
-  }
-}
-
-bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                         CFXJSE_Value* pObject,
-                         const ByteStringView& szPropName) {
-  ASSERT(lpClass);
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, true);
-  return nPropType != FXJSE_ClassPropType_None;
-}
-
-void NamedPropertyQueryCallback(
-    v8::Local<v8::Name> property,
-    const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
-      info.Data().As<v8::External>()->Value());
-  v8::Isolate* pIsolate = info.GetIsolate();
-  v8::HandleScope scope(pIsolate);
-  v8::String::Utf8Value szPropName(pIsolate, property);
-  ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) {
-    info.GetReturnValue().Set(v8::DontDelete);
-    return;
-  }
-  const int32_t iV8Absent = 64;
-  info.GetReturnValue().Set(iV8Absent);
-}
-
-void NamedPropertyGetterCallback(
-    v8::Local<v8::Name> property,
-    const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
-      info.Data().As<v8::External>()->Value());
-  v8::String::Utf8Value szPropName(info.GetIsolate(), property);
-  ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
-                       lpNewValue.get());
-  info.GetReturnValue().Set(lpNewValue->DirectGetValue());
-}
-
-void NamedPropertySetterCallback(
-    v8::Local<v8::Name> property,
-    v8::Local<v8::Value> value,
-    const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
-      info.Data().As<v8::External>()->Value());
-  v8::String::Utf8Value szPropName(info.GetIsolate(), property);
-  ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-
-  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpNewValue->ForceSetValue(value);
-  DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
-                       lpNewValue.get());
-  info.GetReturnValue().Set(value);
-}
-
-void NamedPropertyEnumeratorCallback(
-    const v8::PropertyCallbackInfo<v8::Array>& info) {
-  info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
-}
-
-}  // namespace
-
-// static
-CFXJSE_Class* CFXJSE_Class::Create(
-    CFXJSE_Context* lpContext,
-    const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
-    bool bIsJSGlobal) {
-  if (!lpContext || !lpClassDefinition)
-    return nullptr;
-
-  CFXJSE_Class* pExistingClass =
-      lpContext->GetClassByName(lpClassDefinition->name);
-  if (pExistingClass)
-    return pExistingClass;
-
-  v8::Isolate* pIsolate = lpContext->GetIsolate();
-  auto pClass = pdfium::MakeUnique<CFXJSE_Class>(lpContext);
-  pClass->m_szClassName = lpClassDefinition->name;
-  pClass->m_lpClassDefinition = lpClassDefinition;
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
-  v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
-      pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
-      v8::External::New(
-          pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
-  hFunctionTemplate->SetClassName(
-      v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name));
-  hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2);
-  v8::Local<v8::ObjectTemplate> hObjectTemplate =
-      hFunctionTemplate->InstanceTemplate();
-  SetUpNamedPropHandler(pIsolate, hObjectTemplate, lpClassDefinition);
-
-  if (lpClassDefinition->methNum) {
-    for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
-      v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
-          pIsolate, V8FunctionCallback_Wrapper,
-          v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
-                                          lpClassDefinition->methods + i)));
-      fun->RemovePrototype();
-      hObjectTemplate->Set(
-          v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name),
-          fun,
-          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
-    }
-  }
-
-  if (bIsJSGlobal) {
-    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
-        pIsolate, Context_GlobalObjToString,
-        v8::External::New(
-            pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
-    fun->RemovePrototype();
-    hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString"), fun);
-  }
-  pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate);
-  CFXJSE_Class* pResult = pClass.get();
-  lpContext->AddClass(std::move(pClass));
-  return pResult;
-}
-
-// static
-void CFXJSE_Class::SetUpNamedPropHandler(
-    v8::Isolate* pIsolate,
-    v8::Local<v8::ObjectTemplate>& hObjectTemplate,
-    const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
-  v8::NamedPropertyHandlerConfiguration configuration(
-      lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : 0,
-      lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : 0,
-      lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback : 0, 0,
-      NamedPropertyEnumeratorCallback,
-      v8::External::New(pIsolate,
-                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
-      v8::PropertyHandlerFlags::kNonMasking);
-  hObjectTemplate->SetHandler(configuration);
-}
-
-CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext)
-    : m_lpClassDefinition(nullptr), m_pContext(lpContext) {}
-
-CFXJSE_Class::~CFXJSE_Class() {}
diff --git a/fxjs/cfxjse_class.h b/fxjs/cfxjse_class.h
deleted file mode 100644
index 056525f..0000000
--- a/fxjs/cfxjse_class.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_CLASS_H_
-#define FXJS_CFXJSE_CLASS_H_
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "fxjs/fxjse.h"
-#include "v8/include/v8.h"
-
-class CFXJSE_Context;
-class CFXJSE_Value;
-
-class CFXJSE_Class {
- public:
-  static CFXJSE_Class* Create(CFXJSE_Context* pContext,
-                              const FXJSE_CLASS_DESCRIPTOR* lpClassDefintion,
-                              bool bIsJSGlobal);
-
-  static void SetUpNamedPropHandler(
-      v8::Isolate* pIsolate,
-      v8::Local<v8::ObjectTemplate>& hObjectTemplate,
-      const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition);
-
-  explicit CFXJSE_Class(CFXJSE_Context* lpContext);
-  ~CFXJSE_Class();
-
-  CFXJSE_Context* GetContext() const { return m_pContext.Get(); }
-  v8::Global<v8::FunctionTemplate>& GetTemplate() { return m_hTemplate; }
-
- protected:
-  ByteString m_szClassName;
-  UnownedPtr<const FXJSE_CLASS_DESCRIPTOR> m_lpClassDefinition;
-  UnownedPtr<CFXJSE_Context> m_pContext;
-  v8::Global<v8::FunctionTemplate> m_hTemplate;
-  friend class CFXJSE_Context;
-  friend class CFXJSE_Value;
-};
-
-#endif  // FXJS_CFXJSE_CLASS_H_
diff --git a/fxjs/cfxjse_context.cpp b/fxjs/cfxjse_context.cpp
deleted file mode 100644
index bf9bee8..0000000
--- a/fxjs/cfxjse_context.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_context.h"
-
-#include <utility>
-
-#include "fxjs/cfxjse_class.h"
-#include "fxjs/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-const char szCompatibleModeScript[] =
-    "(function(global, list) {\n"
-    "  'use strict';\n"
-    "  var objname;\n"
-    "  for (objname in list) {\n"
-    "    var globalobj = global[objname];\n"
-    "    if (globalobj) {\n"
-    "      list[objname].forEach(function(name) {\n"
-    "        if (!globalobj[name]) {\n"
-    "          Object.defineProperty(globalobj, name, {\n"
-    "            writable: true,\n"
-    "            enumerable: false,\n"
-    "            value: (function(obj) {\n"
-    "              if (arguments.length === 0) {\n"
-    "                throw new TypeError('missing argument 0 when calling "
-    "                    function ' + objname + '.' + name);\n"
-    "              }\n"
-    "              return globalobj.prototype[name].apply(obj, "
-    "                  Array.prototype.slice.call(arguments, 1));\n"
-    "            })\n"
-    "          });\n"
-    "        }\n"
-    "      });\n"
-    "    }\n"
-    "  }\n"
-    "}(this, {String: ['substr', 'toUpperCase']}));";
-
-wchar_t g_FXJSETagString[] = L"FXJSE_HostObject";
-
-v8::Local<v8::Object> CreateReturnValue(v8::Isolate* pIsolate,
-                                        v8::TryCatch& trycatch) {
-  v8::Local<v8::Object> hReturnValue = v8::Object::New(pIsolate);
-  if (trycatch.HasCaught()) {
-    v8::Local<v8::Value> hException = trycatch.Exception();
-    v8::Local<v8::Message> hMessage = trycatch.Message();
-    if (hException->IsObject()) {
-      v8::Local<v8::Value> hValue;
-      hValue = hException.As<v8::Object>()->Get(
-          v8::String::NewFromUtf8(pIsolate, "name"));
-      if (hValue->IsString() || hValue->IsStringObject())
-        hReturnValue->Set(0, hValue);
-      else
-        hReturnValue->Set(0, v8::String::NewFromUtf8(pIsolate, "Error"));
-
-      hValue = hException.As<v8::Object>()->Get(
-          v8::String::NewFromUtf8(pIsolate, "message"));
-      if (hValue->IsString() || hValue->IsStringObject())
-        hReturnValue->Set(1, hValue);
-      else
-        hReturnValue->Set(1, hMessage->Get());
-    } else {
-      hReturnValue->Set(0, v8::String::NewFromUtf8(pIsolate, "Error"));
-      hReturnValue->Set(1, hMessage->Get());
-    }
-    hReturnValue->Set(2, hException);
-    hReturnValue->Set(3, v8::Integer::New(pIsolate, hMessage->GetLineNumber()));
-    hReturnValue->Set(4, hMessage->GetSourceLine());
-    v8::Maybe<int32_t> maybe_int =
-        hMessage->GetStartColumn(pIsolate->GetCurrentContext());
-    hReturnValue->Set(5, v8::Integer::New(pIsolate, maybe_int.FromMaybe(0)));
-    maybe_int = hMessage->GetEndColumn(pIsolate->GetCurrentContext());
-    hReturnValue->Set(6, v8::Integer::New(pIsolate, maybe_int.FromMaybe(0)));
-  }
-  return hReturnValue;
-}
-
-v8::Local<v8::Object> GetGlobalObjectFromContext(
-    v8::Local<v8::Context> hContext) {
-  return hContext->Global()->GetPrototype().As<v8::Object>();
-}
-
-}  // namespace
-
-// Note, not in the anonymous namespace due to the friend call
-// in cfxjse_context.h
-// TODO(dsinclair): Remove the friending, use public methods.
-class CFXJSE_ScopeUtil_IsolateHandleContext {
- public:
-  explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext)
-      : m_context(pContext),
-        m_parent(pContext->m_pIsolate),
-        m_cscope(v8::Local<v8::Context>::New(pContext->m_pIsolate,
-                                             pContext->m_hContext)) {}
-  v8::Isolate* GetIsolate() { return m_context->m_pIsolate; }
-  v8::Local<v8::Context> GetLocalContext() {
-    return v8::Local<v8::Context>::New(m_context->m_pIsolate,
-                                       m_context->m_hContext);
-  }
-
- private:
-  CFXJSE_ScopeUtil_IsolateHandleContext(
-      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
-  void operator=(const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  UnownedPtr<CFXJSE_Context> m_context;
-  CFXJSE_ScopeUtil_IsolateHandle m_parent;
-  v8::Context::Scope m_cscope;
-};
-
-void FXJSE_UpdateObjectBinding(v8::Local<v8::Object>& hObject,
-                               CFXJSE_HostObject* lpNewBinding) {
-  ASSERT(!hObject.IsEmpty());
-  ASSERT(hObject->InternalFieldCount() == 2);
-  hObject->SetAlignedPointerInInternalField(0, g_FXJSETagString);
-  hObject->SetAlignedPointerInInternalField(1, lpNewBinding);
-}
-
-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Object> hJSObject,
-                                               CFXJSE_Class* lpClass) {
-  ASSERT(!hJSObject.IsEmpty());
-  if (!hJSObject->IsObject())
-    return nullptr;
-
-  v8::Local<v8::Object> hObject = hJSObject;
-  if (hObject->InternalFieldCount() != 2) {
-    v8::Local<v8::Value> hProtoObject = hObject->GetPrototype();
-    if (hProtoObject.IsEmpty() || !hProtoObject->IsObject())
-      return nullptr;
-
-    hObject = hProtoObject.As<v8::Object>();
-    if (hObject->InternalFieldCount() != 2)
-      return nullptr;
-  }
-  if (hObject->GetAlignedPointerFromInternalField(0) != g_FXJSETagString)
-    return nullptr;
-  if (lpClass) {
-    v8::Local<v8::FunctionTemplate> hClass =
-        v8::Local<v8::FunctionTemplate>::New(
-            lpClass->GetContext()->GetIsolate(), lpClass->GetTemplate());
-    if (!hClass->HasInstance(hObject))
-      return nullptr;
-  }
-  return static_cast<CFXJSE_HostObject*>(
-      hObject->GetAlignedPointerFromInternalField(1));
-}
-
-// static
-std::unique_ptr<CFXJSE_Context> CFXJSE_Context::Create(
-    v8::Isolate* pIsolate,
-    const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
-    CFXJSE_HostObject* pGlobalObject) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
-  auto pContext = pdfium::MakeUnique<CFXJSE_Context>(pIsolate);
-  v8::Local<v8::ObjectTemplate> hObjectTemplate;
-  if (pGlobalClass) {
-    CFXJSE_Class* pGlobalClassObj =
-        CFXJSE_Class::Create(pContext.get(), pGlobalClass, true);
-    ASSERT(pGlobalClassObj);
-    v8::Local<v8::FunctionTemplate> hFunctionTemplate =
-        v8::Local<v8::FunctionTemplate>::New(pIsolate,
-                                             pGlobalClassObj->m_hTemplate);
-    hObjectTemplate = hFunctionTemplate->InstanceTemplate();
-  } else {
-    hObjectTemplate = v8::ObjectTemplate::New(pIsolate);
-    hObjectTemplate->SetInternalFieldCount(2);
-  }
-
-  hObjectTemplate->Set(
-      v8::Symbol::GetToStringTag(pIsolate),
-      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-          .ToLocalChecked());
-  v8::Local<v8::Context> hNewContext =
-      v8::Context::New(pIsolate, nullptr, hObjectTemplate);
-  v8::Local<v8::Context> hRootContext = v8::Local<v8::Context>::New(
-      pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext);
-  hNewContext->SetSecurityToken(hRootContext->GetSecurityToken());
-  v8::Local<v8::Object> hGlobalObject = GetGlobalObjectFromContext(hNewContext);
-  FXJSE_UpdateObjectBinding(hGlobalObject, pGlobalObject);
-  pContext->m_hContext.Reset(pIsolate, hNewContext);
-  return pContext;
-}
-
-CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
-
-CFXJSE_Context::~CFXJSE_Context() {}
-
-std::unique_ptr<CFXJSE_Value> CFXJSE_Context::GetGlobalObject() {
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(m_pIsolate);
-  CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
-  v8::Local<v8::Context> hContext =
-      v8::Local<v8::Context>::New(m_pIsolate, m_hContext);
-  v8::Local<v8::Object> hGlobalObject = GetGlobalObjectFromContext(hContext);
-  pValue->ForceSetValue(hGlobalObject);
-  return pValue;
-}
-
-v8::Local<v8::Context> CFXJSE_Context::GetContext() {
-  return v8::Local<v8::Context>::New(m_pIsolate, m_hContext);
-}
-
-void CFXJSE_Context::AddClass(std::unique_ptr<CFXJSE_Class> pClass) {
-  m_rgClasses.push_back(std::move(pClass));
-}
-
-CFXJSE_Class* CFXJSE_Context::GetClassByName(
-    const ByteStringView& szName) const {
-  auto pClass =
-      std::find_if(m_rgClasses.begin(), m_rgClasses.end(),
-                   [szName](const std::unique_ptr<CFXJSE_Class>& item) {
-                     return szName == item->m_szClassName;
-                   });
-  return pClass != m_rgClasses.end() ? pClass->get() : nullptr;
-}
-
-void CFXJSE_Context::EnableCompatibleMode() {
-  ExecuteScript(szCompatibleModeScript, nullptr, nullptr);
-}
-
-bool CFXJSE_Context::ExecuteScript(const char* szScript,
-                                   CFXJSE_Value* lpRetValue,
-                                   CFXJSE_Value* lpNewThisObject) {
-  CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
-  v8::TryCatch trycatch(m_pIsolate);
-  v8::Local<v8::String> hScriptString =
-      v8::String::NewFromUtf8(m_pIsolate, szScript);
-  if (!lpNewThisObject) {
-    v8::Local<v8::Script> hScript = v8::Script::Compile(hScriptString);
-    if (!trycatch.HasCaught()) {
-      v8::Local<v8::Value> hValue = hScript->Run();
-      if (!trycatch.HasCaught()) {
-        if (lpRetValue)
-          lpRetValue->m_hValue.Reset(m_pIsolate, hValue);
-        return true;
-      }
-    }
-    if (lpRetValue) {
-      lpRetValue->m_hValue.Reset(m_pIsolate,
-                                 CreateReturnValue(m_pIsolate, trycatch));
-    }
-    return false;
-  }
-
-  v8::Local<v8::Value> hNewThis =
-      v8::Local<v8::Value>::New(m_pIsolate, lpNewThisObject->m_hValue);
-  ASSERT(!hNewThis.IsEmpty());
-  v8::Local<v8::Script> hWrapper = v8::Script::Compile(v8::String::NewFromUtf8(
-      m_pIsolate, "(function () { return eval(arguments[0]); })"));
-  v8::Local<v8::Value> hWrapperValue = hWrapper->Run();
-  ASSERT(hWrapperValue->IsFunction());
-  v8::Local<v8::Function> hWrapperFn = hWrapperValue.As<v8::Function>();
-  if (!trycatch.HasCaught()) {
-    v8::Local<v8::Value> rgArgs[] = {hScriptString};
-    v8::Local<v8::Value> hValue =
-        hWrapperFn->Call(hNewThis.As<v8::Object>(), 1, rgArgs);
-    if (!trycatch.HasCaught()) {
-      if (lpRetValue)
-        lpRetValue->m_hValue.Reset(m_pIsolate, hValue);
-      return true;
-    }
-  }
-  if (lpRetValue) {
-    lpRetValue->m_hValue.Reset(m_pIsolate,
-                               CreateReturnValue(m_pIsolate, trycatch));
-  }
-  return false;
-}
diff --git a/fxjs/cfxjse_context.h b/fxjs/cfxjse_context.h
deleted file mode 100644
index 48816eb..0000000
--- a/fxjs/cfxjse_context.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_CONTEXT_H_
-#define FXJS_CFXJSE_CONTEXT_H_
-
-#include <memory>
-#include <vector>
-
-#include "fxjs/fxjse.h"
-#include "v8/include/v8.h"
-
-class CFXJSE_Class;
-class CFXJSE_Value;
-struct FXJSE_CLASS_DESCRIPTOR;
-
-class CFXJSE_Context {
- public:
-  static std::unique_ptr<CFXJSE_Context> Create(
-      v8::Isolate* pIsolate,
-      const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
-      CFXJSE_HostObject* pGlobalObject);
-
-  explicit CFXJSE_Context(v8::Isolate* pIsolate);
-  ~CFXJSE_Context();
-
-  v8::Isolate* GetIsolate() const { return m_pIsolate; }
-  v8::Local<v8::Context> GetContext();
-  std::unique_ptr<CFXJSE_Value> GetGlobalObject();
-  void AddClass(std::unique_ptr<CFXJSE_Class> pClass);
-  CFXJSE_Class* GetClassByName(const ByteStringView& szName) const;
-  void EnableCompatibleMode();
-  bool ExecuteScript(const char* szScript,
-                     CFXJSE_Value* lpRetValue,
-                     CFXJSE_Value* lpNewThisObject = nullptr);
-
- protected:
-  friend class CFXJSE_ScopeUtil_IsolateHandleContext;
-
-  CFXJSE_Context();
-  CFXJSE_Context(const CFXJSE_Context&);
-  CFXJSE_Context& operator=(const CFXJSE_Context&);
-
-  v8::Global<v8::Context> m_hContext;
-  v8::Isolate* m_pIsolate;
-  std::vector<std::unique_ptr<CFXJSE_Class>> m_rgClasses;
-};
-
-void FXJSE_UpdateObjectBinding(v8::Local<v8::Object>& hObject,
-                               CFXJSE_HostObject* lpNewBinding = nullptr);
-
-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Object> hJSObject,
-                                               CFXJSE_Class* lpClass = nullptr);
-
-#endif  // FXJS_CFXJSE_CONTEXT_H_
diff --git a/fxjs/cfxjse_engine.cpp b/fxjs/cfxjse_engine.cpp
deleted file mode 100644
index 325895f..0000000
--- a/fxjs/cfxjse_engine.cpp
+++ /dev/null
@@ -1,755 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_engine.h"
-
-#include <utility>
-
-#include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_class.h"
-#include "fxjs/cfxjse_resolveprocessor.h"
-#include "fxjs/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fxfa/cxfa_eventparam.h"
-#include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodehelper.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
-#include "xfa/fxfa/parser/cxfa_thisproxy.h"
-#include "xfa/fxfa/parser/cxfa_treelist.h"
-#include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-namespace {
-
-const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = {
-    "Root",   // name
-    nullptr,  // methods
-    0,        // method count
-    CFXJSE_Engine::GlobalPropTypeGetter,
-    CFXJSE_Engine::GlobalPropertyGetter,
-    CFXJSE_Engine::GlobalPropertySetter,
-    CFXJSE_Engine::NormalMethodCall,
-};
-
-const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = {
-    "XFAObject",  // name
-    nullptr,      // methods
-    0,            // method count
-    CFXJSE_Engine::NormalPropTypeGetter,
-    CFXJSE_Engine::NormalPropertyGetter,
-    CFXJSE_Engine::NormalPropertySetter,
-    CFXJSE_Engine::NormalMethodCall,
-};
-
-const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor = {
-    "XFAScriptObject",  // name
-    nullptr,            // methods
-    0,                  // method count
-    CFXJSE_Engine::NormalPropTypeGetter,
-    CFXJSE_Engine::GlobalPropertyGetter,
-    CFXJSE_Engine::GlobalPropertySetter,
-    CFXJSE_Engine::NormalMethodCall,
-};
-
-const char kFormCalcRuntime[] = "pfm_rt";
-
-CXFA_ThisProxy* ToThisProxy(CFXJSE_Value* pValue, CFXJSE_Class* pClass) {
-  return static_cast<CXFA_ThisProxy*>(pValue->ToHostObject(pClass));
-}
-
-}  // namespace
-
-// static
-CXFA_Object* CFXJSE_Engine::ToObject(
-    const v8::FunctionCallbackInfo<v8::Value>& info) {
-  if (!info.Holder()->IsObject())
-    return nullptr;
-
-  CFXJSE_HostObject* hostObj =
-      FXJSE_RetrieveObjectBinding(info.Holder().As<v8::Object>(), nullptr);
-  if (!hostObj || hostObj->type() != CFXJSE_HostObject::kXFA)
-    return nullptr;
-  return static_cast<CXFA_Object*>(hostObj);
-}
-
-// static.
-CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_Value* pValue,
-                                     CFXJSE_Class* pClass) {
-  CFXJSE_HostObject* pHostObj = pValue->ToHostObject(pClass);
-  if (!pHostObj || pHostObj->type() != CFXJSE_HostObject::kXFA)
-    return nullptr;
-  return static_cast<CXFA_Object*>(pHostObj);
-}
-
-CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument, v8::Isolate* pIsolate)
-    : CJS_V8(pIsolate),
-      m_pDocument(pDocument),
-      m_JsContext(CFXJSE_Context::Create(pIsolate,
-                                         &GlobalClassDescriptor,
-                                         pDocument->GetRoot())),
-      m_pJsClass(nullptr),
-      m_eScriptType(CXFA_Script::Type::Unknown),
-      m_pScriptNodeArray(nullptr),
-      m_ResolveProcessor(pdfium::MakeUnique<CFXJSE_ResolveProcessor>()),
-      m_pThisObject(nullptr),
-      m_eRunAtType(XFA_AttributeEnum::Client) {
-  RemoveBuiltInObjs(m_JsContext.get());
-  m_JsContext->EnableCompatibleMode();
-
-  // Don't know if this can happen before we remove the builtin objs and set
-  // compatibility mode.
-  m_pJsClass =
-      CFXJSE_Class::Create(m_JsContext.get(), &NormalClassDescriptor, false);
-}
-
-CFXJSE_Engine::~CFXJSE_Engine() {
-  for (const auto& pair : m_mapVariableToContext)
-    delete ToThisProxy(pair.second->GetGlobalObject().get(), nullptr);
-}
-
-bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType,
-                              const WideStringView& wsScript,
-                              CFXJSE_Value* hRetValue,
-                              CXFA_Object* pThisObject) {
-  ByteString btScript;
-  AutoRestorer<CXFA_Script::Type> typeRestorer(&m_eScriptType);
-  m_eScriptType = eScriptType;
-  if (eScriptType == CXFA_Script::Type::Formcalc) {
-    if (!m_FM2JSContext) {
-      m_FM2JSContext = pdfium::MakeUnique<CFXJSE_FormCalcContext>(
-          GetIsolate(), m_JsContext.get(), m_pDocument.Get());
-    }
-    CFX_WideTextBuf wsJavaScript;
-    if (!CFXJSE_FormCalcContext::Translate(wsScript, &wsJavaScript)) {
-      hRetValue->SetUndefined();
-      return false;
-    }
-    btScript = FX_UTF8Encode(wsJavaScript.AsStringView());
-  } else {
-    btScript = FX_UTF8Encode(wsScript);
-  }
-  AutoRestorer<CXFA_Object*> nodeRestorer(&m_pThisObject);
-  m_pThisObject = pThisObject;
-  CFXJSE_Value* pValue = pThisObject ? GetJSValueFromMap(pThisObject) : nullptr;
-  return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pValue);
-}
-
-bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode,
-                                    const WideStringView& propname,
-                                    CFXJSE_Value* pValue,
-                                    uint32_t dwFlag,
-                                    bool bSetting) {
-  if (!refNode)
-    return false;
-
-  XFA_RESOLVENODE_RS resolveRs;
-  if (!ResolveObjects(refNode, propname, &resolveRs, dwFlag, nullptr))
-    return false;
-  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    pValue->Assign(GetJSValueFromMap(resolveRs.objects.front()));
-    return true;
-  }
-  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Attribute) {
-    const XFA_SCRIPTATTRIBUTEINFO* lpAttributeInfo = resolveRs.pScriptAttribute;
-    if (lpAttributeInfo) {
-      CJX_Object* jsObject = resolveRs.objects.front()->JSObject();
-      (jsObject->*(lpAttributeInfo->callback))(pValue, bSetting,
-                                               lpAttributeInfo->attribute);
-    }
-  }
-  return true;
-}
-
-void CFXJSE_Engine::GlobalPropertySetter(CFXJSE_Value* pObject,
-                                         const ByteStringView& szPropName,
-                                         CFXJSE_Value* pValue) {
-  CXFA_Object* lpOrginalNode = ToObject(pObject, nullptr);
-  CXFA_Document* pDoc = lpOrginalNode->GetDocument();
-  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
-  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
-  if (lpOrginalNode->IsVariablesThis())
-    pRefNode = ToNode(lpScriptContext->GetVariablesThis(lpOrginalNode));
-
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (lpScriptContext->QueryNodeByFlag(
-          pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings |
-              XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes,
-          true)) {
-    return;
-  }
-  if (lpOrginalNode->IsVariablesThis()) {
-    if (pValue && pValue->IsUndefined()) {
-      pObject->SetObjectOwnProperty(szPropName, pValue);
-      return;
-    }
-  }
-  CXFA_FFNotify* pNotify = pDoc->GetNotify();
-  if (!pNotify)
-    return;
-
-  pNotify->GetDocEnvironment()->SetGlobalProperty(pNotify->GetHDOC(),
-                                                  szPropName, pValue);
-}
-
-void CFXJSE_Engine::GlobalPropertyGetter(CFXJSE_Value* pObject,
-                                         const ByteStringView& szPropName,
-                                         CFXJSE_Value* pValue) {
-  CXFA_Object* pOriginalObject = ToObject(pObject, nullptr);
-  CXFA_Document* pDoc = pOriginalObject->GetDocument();
-  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (lpScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
-    if (szPropName == kFormCalcRuntime) {
-      lpScriptContext->m_FM2JSContext->GlobalPropertyGetter(pValue);
-      return;
-    }
-    XFA_HashCode uHashCode = static_cast<XFA_HashCode>(
-        FX_HashCode_GetW(wsPropName.AsStringView(), false));
-    if (uHashCode != XFA_HASHCODE_Layout) {
-      CXFA_Object* pObj =
-          lpScriptContext->GetDocument()->GetXFAObject(uHashCode);
-      if (pObj) {
-        pValue->Assign(lpScriptContext->GetJSValueFromMap(pObj));
-        return;
-      }
-    }
-  }
-
-  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
-  if (pOriginalObject->IsVariablesThis())
-    pRefNode = ToNode(lpScriptContext->GetVariablesThis(pOriginalObject));
-
-  if (lpScriptContext->QueryNodeByFlag(
-          pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes,
-          false)) {
-    return;
-  }
-
-  if (lpScriptContext->QueryNodeByFlag(
-          pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false)) {
-    return;
-  }
-
-  CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
-  if (pScriptObject && lpScriptContext->QueryVariableValue(
-                           pScriptObject->AsNode(), szPropName, pValue, true)) {
-    return;
-  }
-
-  CXFA_FFNotify* pNotify = pDoc->GetNotify();
-  if (!pNotify)
-    return;
-
-  pNotify->GetDocEnvironment()->GetGlobalProperty(pNotify->GetHDOC(),
-                                                  szPropName, pValue);
-}
-
-int32_t CFXJSE_Engine::GlobalPropTypeGetter(CFXJSE_Value* pOriginalValue,
-                                            const ByteStringView& szPropName,
-                                            bool bQueryIn) {
-  CXFA_Object* pObject = ToObject(pOriginalValue, nullptr);
-  if (!pObject)
-    return FXJSE_ClassPropType_None;
-
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject);
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (pObject->JSObject()->HasMethod(wsPropName))
-    return FXJSE_ClassPropType_Method;
-
-  return FXJSE_ClassPropType_Property;
-}
-
-void CFXJSE_Engine::NormalPropertyGetter(CFXJSE_Value* pOriginalValue,
-                                         const ByteStringView& szPropName,
-                                         CFXJSE_Value* pReturnValue) {
-  CXFA_Object* pOriginalObject = ToObject(pOriginalValue, nullptr);
-  if (!pOriginalObject) {
-    pReturnValue->SetUndefined();
-    return;
-  }
-
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  CFXJSE_Engine* lpScriptContext =
-      pOriginalObject->GetDocument()->GetScriptContext();
-  CXFA_Object* pObject = lpScriptContext->GetVariablesThis(pOriginalObject);
-  if (wsPropName == L"xfa") {
-    CFXJSE_Value* pValue = lpScriptContext->GetJSValueFromMap(
-        lpScriptContext->GetDocument()->GetRoot());
-    pReturnValue->Assign(pValue);
-    return;
-  }
-
-  bool bRet = lpScriptContext->QueryNodeByFlag(
-      ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
-      XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-          XFA_RESOLVENODE_Attributes,
-      false);
-  if (bRet)
-    return;
-
-  if (pObject == lpScriptContext->GetThisObject() ||
-      (lpScriptContext->GetType() == CXFA_Script::Type::Javascript &&
-       !lpScriptContext->IsStrictScopeInJavaScript())) {
-    bRet = lpScriptContext->QueryNodeByFlag(
-        ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
-        XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false);
-  }
-  if (bRet)
-    return;
-
-  CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
-  if (pScriptObject) {
-    bRet = lpScriptContext->QueryVariableValue(ToNode(pScriptObject),
-                                               szPropName, pReturnValue, true);
-  }
-  if (!bRet)
-    pReturnValue->SetUndefined();
-}
-
-void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue,
-                                         const ByteStringView& szPropName,
-                                         CFXJSE_Value* pReturnValue) {
-  CXFA_Object* pOriginalObject = ToObject(pOriginalValue, nullptr);
-  if (!pOriginalObject)
-    return;
-
-  CFXJSE_Engine* lpScriptContext =
-      pOriginalObject->GetDocument()->GetScriptContext();
-  CXFA_Object* pObject = lpScriptContext->GetVariablesThis(pOriginalObject);
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  const XFA_SCRIPTATTRIBUTEINFO* lpAttributeInfo = XFA_GetScriptAttributeByName(
-      pObject->GetElementType(), wsPropName.AsStringView());
-  if (lpAttributeInfo) {
-    CJX_Object* jsObject = pObject->JSObject();
-    (jsObject->*(lpAttributeInfo->callback))(pReturnValue, true,
-                                             lpAttributeInfo->attribute);
-    return;
-  }
-
-  if (pObject->IsNode()) {
-    if (wsPropName[0] == '#')
-      wsPropName = wsPropName.Right(wsPropName.GetLength() - 1);
-
-    CXFA_Node* pNode = ToNode(pObject);
-    CXFA_Node* pPropOrChild = nullptr;
-    XFA_Element eType = CXFA_Node::NameToElement(wsPropName);
-    if (eType != XFA_Element::Unknown) {
-      pPropOrChild =
-          pNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eType);
-    } else {
-      pPropOrChild = pNode->GetFirstChildByName(wsPropName.AsStringView());
-    }
-
-    if (pPropOrChild) {
-      const XFA_SCRIPTATTRIBUTEINFO* lpAttrInfo = XFA_GetScriptAttributeByName(
-          pPropOrChild->GetElementType(), L"{default}");
-      if (lpAttrInfo) {
-        pPropOrChild->JSObject()->Script_Som_DefaultValue(
-            pReturnValue, true, XFA_Attribute::Unknown);
-        return;
-      }
-    }
-  }
-
-  CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
-  if (pScriptObject) {
-    lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
-                                        pReturnValue, false);
-  }
-}
-
-int32_t CFXJSE_Engine::NormalPropTypeGetter(CFXJSE_Value* pOriginalValue,
-                                            const ByteStringView& szPropName,
-                                            bool bQueryIn) {
-  CXFA_Object* pObject = ToObject(pOriginalValue, nullptr);
-  if (!pObject)
-    return FXJSE_ClassPropType_None;
-
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject);
-  XFA_Element eType = pObject->GetElementType();
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (pObject->JSObject()->HasMethod(wsPropName))
-    return FXJSE_ClassPropType_Method;
-
-  if (bQueryIn &&
-      !XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView())) {
-    return FXJSE_ClassPropType_None;
-  }
-  return FXJSE_ClassPropType_Property;
-}
-
-CJS_Return CFXJSE_Engine::NormalMethodCall(
-    const v8::FunctionCallbackInfo<v8::Value>& info,
-    const WideString& functionName) {
-  CXFA_Object* pObject = ToObject(info);
-  if (!pObject)
-    return CJS_Return(false);
-
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject);
-
-  std::vector<v8::Local<v8::Value>> parameters;
-  for (unsigned int i = 0; i < (unsigned int)info.Length(); i++)
-    parameters.push_back(info[i]);
-
-  return pObject->JSObject()->RunMethod(functionName, parameters);
-}
-
-bool CFXJSE_Engine::IsStrictScopeInJavaScript() {
-  return m_pDocument->HasFlag(XFA_DOCFLAG_StrictScoping);
-}
-
-CXFA_Script::Type CFXJSE_Engine::GetType() {
-  return m_eScriptType;
-}
-
-CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Node* pScriptNode,
-                                                      CXFA_Node* pSubform) {
-  if (!pScriptNode || !pSubform)
-    return nullptr;
-
-  auto pNewContext =
-      CFXJSE_Context::Create(GetIsolate(), &VariablesClassDescriptor,
-                             new CXFA_ThisProxy(pSubform, pScriptNode));
-  RemoveBuiltInObjs(pNewContext.get());
-  pNewContext->EnableCompatibleMode();
-  CFXJSE_Context* pResult = pNewContext.get();
-  m_mapVariableToContext[pScriptNode] = std::move(pNewContext);
-  return pResult;
-}
-
-CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject,
-                                             bool bScriptNode) {
-  if (!pObject->IsVariablesThis())
-    return pObject;
-
-  CXFA_ThisProxy* pProxy = static_cast<CXFA_ThisProxy*>(pObject);
-  return bScriptNode ? pProxy->GetScriptNode() : pProxy->GetThisNode();
-}
-
-bool CFXJSE_Engine::RunVariablesScript(CXFA_Node* pScriptNode) {
-  if (!pScriptNode)
-    return false;
-
-  if (pScriptNode->GetElementType() != XFA_Element::Script)
-    return true;
-
-  CXFA_Node* pParent = pScriptNode->GetParent();
-  if (!pParent || pParent->GetElementType() != XFA_Element::Variables)
-    return false;
-
-  auto it = m_mapVariableToContext.find(pScriptNode);
-  if (it != m_mapVariableToContext.end() && it->second)
-    return true;
-
-  CXFA_Node* pTextNode = pScriptNode->GetFirstChild();
-  if (!pTextNode)
-    return false;
-
-  Optional<WideString> wsScript =
-      pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true);
-  if (!wsScript)
-    return false;
-
-  ByteString btScript = wsScript->UTF8Encode();
-  auto hRetValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  CXFA_Node* pThisObject = pParent->GetParent();
-  CFXJSE_Context* pVariablesContext =
-      CreateVariablesContext(pScriptNode, pThisObject);
-  AutoRestorer<CXFA_Object*> nodeRestorer(&m_pThisObject);
-  m_pThisObject = pThisObject;
-  return pVariablesContext->ExecuteScript(btScript.c_str(), hRetValue.get());
-}
-
-bool CFXJSE_Engine::QueryVariableValue(CXFA_Node* pScriptNode,
-                                       const ByteStringView& szPropName,
-                                       CFXJSE_Value* pValue,
-                                       bool bGetter) {
-  if (!pScriptNode || pScriptNode->GetElementType() != XFA_Element::Script)
-    return false;
-
-  CXFA_Node* variablesNode = pScriptNode->GetParent();
-  if (!variablesNode ||
-      variablesNode->GetElementType() != XFA_Element::Variables)
-    return false;
-
-  auto it = m_mapVariableToContext.find(pScriptNode);
-  if (it == m_mapVariableToContext.end() || !it->second)
-    return false;
-
-  CFXJSE_Context* pVariableContext = it->second.get();
-  std::unique_ptr<CFXJSE_Value> pObject = pVariableContext->GetGlobalObject();
-  auto hVariableValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  if (!bGetter) {
-    pObject->SetObjectOwnProperty(szPropName, pValue);
-    return true;
-  }
-  if (pObject->HasObjectOwnProperty(szPropName, false)) {
-    pObject->GetObjectProperty(szPropName, hVariableValue.get());
-    if (hVariableValue->IsFunction())
-      pValue->SetFunctionBind(hVariableValue.get(), pObject.get());
-    else if (bGetter)
-      pValue->Assign(hVariableValue.get());
-    else
-      hVariableValue.get()->Assign(pValue);
-    return true;
-  }
-  return false;
-}
-
-void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) const {
-  static const ByteStringView OBJ_NAME[2] = {"Number", "Date"};
-  std::unique_ptr<CFXJSE_Value> pObject = pContext->GetGlobalObject();
-  auto hProp = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  for (int i = 0; i < 2; ++i) {
-    if (pObject->GetObjectProperty(OBJ_NAME[i], hProp.get()))
-      pObject->DeleteObjectProperty(OBJ_NAME[i]);
-  }
-}
-
-CFXJSE_Class* CFXJSE_Engine::GetJseNormalClass() {
-  return m_pJsClass;
-}
-
-bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject,
-                                   const WideStringView& wsExpression,
-                                   XFA_RESOLVENODE_RS* resolveNodeRS,
-                                   uint32_t dwStyles,
-                                   CXFA_Node* bindNode) {
-  if (wsExpression.IsEmpty())
-    return false;
-
-  if (m_eScriptType != CXFA_Script::Type::Formcalc ||
-      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
-    m_upObjectArray.clear();
-  }
-  if (refObject && refObject->IsNode() &&
-      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
-    m_upObjectArray.push_back(refObject->AsNode());
-  }
-
-  bool bNextCreate = false;
-  if (dwStyles & XFA_RESOLVENODE_CreateNode)
-    m_ResolveProcessor->GetNodeHelper()->SetCreateNodeType(bindNode);
-
-  m_ResolveProcessor->GetNodeHelper()->m_pCreateParent = nullptr;
-  m_ResolveProcessor->GetNodeHelper()->m_iCurAllStart = -1;
-
-  CFXJSE_ResolveNodeData rndFind(this);
-  int32_t nStart = 0;
-  int32_t nLevel = 0;
-
-  std::vector<CXFA_Object*> findObjects;
-  findObjects.push_back(refObject ? refObject : m_pDocument->GetRoot());
-  int32_t nNodes = 0;
-  while (true) {
-    nNodes = pdfium::CollectionSize<int32_t>(findObjects);
-    int32_t i = 0;
-    rndFind.m_dwStyles = dwStyles;
-    m_ResolveProcessor->SetCurStart(nStart);
-    nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind);
-    if (nStart < 1) {
-      if ((dwStyles & XFA_RESOLVENODE_CreateNode) && !bNextCreate) {
-        CXFA_Node* pDataNode = nullptr;
-        nStart = m_ResolveProcessor->GetNodeHelper()->m_iCurAllStart;
-        if (nStart != -1) {
-          pDataNode = m_pDocument->GetNotBindNode(findObjects);
-          if (pDataNode) {
-            findObjects.clear();
-            findObjects.push_back(pDataNode);
-            break;
-          }
-        } else {
-          pDataNode = findObjects.front()->AsNode();
-          findObjects.clear();
-          findObjects.push_back(pDataNode);
-          break;
-        }
-        dwStyles |= XFA_RESOLVENODE_Bind;
-        findObjects.clear();
-        findObjects.push_back(
-            m_ResolveProcessor->GetNodeHelper()->m_pAllStartParent);
-        continue;
-      }
-      break;
-    }
-    if (bNextCreate) {
-      bool bCreate =
-          m_ResolveProcessor->GetNodeHelper()->ResolveNodes_CreateNode(
-              rndFind.m_wsName, rndFind.m_wsCondition,
-              nStart ==
-                  pdfium::base::checked_cast<int32_t>(wsExpression.GetLength()),
-              this);
-      if (bCreate)
-        continue;
-
-      break;
-    }
-
-    std::vector<CXFA_Object*> retObjects;
-    while (i < nNodes) {
-      bool bDataBind = false;
-      if (((dwStyles & XFA_RESOLVENODE_Bind) ||
-           (dwStyles & XFA_RESOLVENODE_CreateNode)) &&
-          nNodes > 1) {
-        CFXJSE_ResolveNodeData rndBind(nullptr);
-        m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind);
-        m_ResolveProcessor->SetIndexDataBind(rndBind.m_wsCondition, i, nNodes);
-        bDataBind = true;
-      }
-      rndFind.m_CurObject = findObjects[i++];
-      rndFind.m_nLevel = nLevel;
-      rndFind.m_dwFlag = XFA_ResolveNode_RSType_Nodes;
-      if (!m_ResolveProcessor->Resolve(rndFind))
-        continue;
-
-      if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute &&
-          rndFind.m_pScriptAttribute &&
-          nStart <
-              pdfium::base::checked_cast<int32_t>(wsExpression.GetLength())) {
-        auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-        CJX_Object* jsObject = rndFind.m_Objects.front()->JSObject();
-        (jsObject->*(rndFind.m_pScriptAttribute->callback))(
-            pValue.get(), false, rndFind.m_pScriptAttribute->attribute);
-        rndFind.m_Objects.front() = ToObject(pValue.get(), nullptr);
-      }
-      if (!m_upObjectArray.empty())
-        m_upObjectArray.pop_back();
-      retObjects.insert(retObjects.end(), rndFind.m_Objects.begin(),
-                        rndFind.m_Objects.end());
-      rndFind.m_Objects.clear();
-      if (bDataBind)
-        break;
-    }
-    findObjects.clear();
-
-    nNodes = pdfium::CollectionSize<int32_t>(retObjects);
-    if (nNodes < 1) {
-      if (dwStyles & XFA_RESOLVENODE_CreateNode) {
-        bNextCreate = true;
-        if (!m_ResolveProcessor->GetNodeHelper()->m_pCreateParent) {
-          m_ResolveProcessor->GetNodeHelper()->m_pCreateParent =
-              ToNode(rndFind.m_CurObject);
-          m_ResolveProcessor->GetNodeHelper()->m_iCreateCount = 1;
-        }
-        bool bCreate =
-            m_ResolveProcessor->GetNodeHelper()->ResolveNodes_CreateNode(
-                rndFind.m_wsName, rndFind.m_wsCondition,
-                nStart == pdfium::base::checked_cast<int32_t>(
-                              wsExpression.GetLength()),
-                this);
-        if (bCreate)
-          continue;
-      }
-      break;
-    }
-
-    findObjects =
-        std::vector<CXFA_Object*>(retObjects.begin(), retObjects.end());
-    rndFind.m_Objects.clear();
-    if (nLevel == 0)
-      dwStyles &= ~(XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
-
-    nLevel++;
-  }
-
-  if (!bNextCreate) {
-    resolveNodeRS->dwFlags = rndFind.m_dwFlag;
-    if (nNodes > 0) {
-      resolveNodeRS->objects.insert(resolveNodeRS->objects.end(),
-                                    findObjects.begin(), findObjects.end());
-    }
-    if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute) {
-      resolveNodeRS->pScriptAttribute = rndFind.m_pScriptAttribute;
-      return 1;
-    }
-  }
-  if (dwStyles & (XFA_RESOLVENODE_CreateNode | XFA_RESOLVENODE_Bind |
-                  XFA_RESOLVENODE_BindNew)) {
-    CXFA_NodeHelper* helper = m_ResolveProcessor->GetNodeHelper();
-    if (helper->m_pCreateParent)
-      resolveNodeRS->objects.push_back(helper->m_pCreateParent);
-    else
-      helper->CreateNode_ForCondition(rndFind.m_wsCondition);
-
-    resolveNodeRS->dwFlags = helper->m_iCreateFlag;
-    if (resolveNodeRS->dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
-      if (helper->m_iCurAllStart != -1)
-        resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_CreateNodeMidAll;
-    }
-
-    if (!bNextCreate && (dwStyles & XFA_RESOLVENODE_CreateNode))
-      resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_ExistNodes;
-
-    return !resolveNodeRS->objects.empty();
-  }
-  return nNodes > 0;
-}
-
-void CFXJSE_Engine::AddToCacheList(std::unique_ptr<CXFA_List> pList) {
-  m_CacheList.push_back(std::move(pList));
-}
-
-CFXJSE_Value* CFXJSE_Engine::GetJSValueFromMap(CXFA_Object* pObject) {
-  if (!pObject)
-    return nullptr;
-  if (pObject->IsNode())
-    RunVariablesScript(pObject->AsNode());
-
-  auto iter = m_mapObjectToValue.find(pObject);
-  if (iter != m_mapObjectToValue.end())
-    return iter->second.get();
-
-  auto jsValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  jsValue->SetObject(pObject, m_pJsClass);
-
-  CFXJSE_Value* pValue = jsValue.get();
-  m_mapObjectToValue.insert(std::make_pair(pObject, std::move(jsValue)));
-  return pValue;
-}
-
-int32_t CFXJSE_Engine::GetIndexByName(CXFA_Node* refNode) {
-  CXFA_NodeHelper* lpNodeHelper = m_ResolveProcessor->GetNodeHelper();
-  return lpNodeHelper->GetIndex(refNode, XFA_LOGIC_Transparent,
-                                lpNodeHelper->NodeIsProperty(refNode), false);
-}
-
-int32_t CFXJSE_Engine::GetIndexByClassName(CXFA_Node* refNode) {
-  CXFA_NodeHelper* lpNodeHelper = m_ResolveProcessor->GetNodeHelper();
-  return lpNodeHelper->GetIndex(refNode, XFA_LOGIC_Transparent,
-                                lpNodeHelper->NodeIsProperty(refNode), true);
-}
-
-WideString CFXJSE_Engine::GetSomExpression(CXFA_Node* refNode) {
-  CXFA_NodeHelper* lpNodeHelper = m_ResolveProcessor->GetNodeHelper();
-  return lpNodeHelper->GetNameExpression(refNode, true, XFA_LOGIC_Transparent);
-}
-
-void CFXJSE_Engine::SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray) {
-  m_pScriptNodeArray = pArray;
-}
-
-void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) {
-  if (m_pScriptNodeArray && !pdfium::ContainsValue(*m_pScriptNodeArray, pNode))
-    m_pScriptNodeArray->push_back(pNode);
-}
diff --git a/fxjs/cfxjse_engine.h b/fxjs/cfxjse_engine.h
deleted file mode 100644
index 0b360bd..0000000
--- a/fxjs/cfxjse_engine.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_ENGINE_H_
-#define FXJS_CFXJSE_ENGINE_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "fxjs/cfxjse_formcalc_context.h"
-#include "fxjs/cjs_v8.h"
-#include "xfa/fxfa/cxfa_eventparam.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_script.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-
-#define XFA_RESOLVENODE_TagName 0x0002
-
-class CXFA_List;
-class CFXJSE_ResolveProcessor;
-
-class CFXJSE_Engine : public CJS_V8 {
- public:
-  static CXFA_Object* ToObject(const v8::FunctionCallbackInfo<v8::Value>& info);
-  static CXFA_Object* ToObject(CFXJSE_Value* pValue, CFXJSE_Class* pClass);
-  static void GlobalPropertyGetter(CFXJSE_Value* pObject,
-                                   const ByteStringView& szPropName,
-                                   CFXJSE_Value* pValue);
-  static void GlobalPropertySetter(CFXJSE_Value* pObject,
-                                   const ByteStringView& szPropName,
-                                   CFXJSE_Value* pValue);
-  static void NormalPropertyGetter(CFXJSE_Value* pObject,
-                                   const ByteStringView& szPropName,
-                                   CFXJSE_Value* pValue);
-  static void NormalPropertySetter(CFXJSE_Value* pObject,
-                                   const ByteStringView& szPropName,
-                                   CFXJSE_Value* pValue);
-  static CJS_Return NormalMethodCall(
-      const v8::FunctionCallbackInfo<v8::Value>& info,
-      const WideString& functionName);
-  static int32_t NormalPropTypeGetter(CFXJSE_Value* pObject,
-                                      const ByteStringView& szPropName,
-                                      bool bQueryIn);
-  static int32_t GlobalPropTypeGetter(CFXJSE_Value* pObject,
-                                      const ByteStringView& szPropName,
-                                      bool bQueryIn);
-
-  explicit CFXJSE_Engine(CXFA_Document* pDocument, v8::Isolate* pIsolate);
-  ~CFXJSE_Engine() override;
-
-  void SetEventParam(CXFA_EventParam param) { m_eventParam = param; }
-  CXFA_EventParam* GetEventParam() { return &m_eventParam; }
-  bool RunScript(CXFA_Script::Type eScriptType,
-                 const WideStringView& wsScript,
-                 CFXJSE_Value* pRetValue,
-                 CXFA_Object* pThisObject);
-
-  bool ResolveObjects(CXFA_Object* refObject,
-                      const WideStringView& wsExpression,
-                      XFA_RESOLVENODE_RS* resolveNodeRS,
-                      uint32_t dwStyles,
-                      CXFA_Node* bindNode);
-  CFXJSE_Value* GetJSValueFromMap(CXFA_Object* pObject);
-  void AddToCacheList(std::unique_ptr<CXFA_List> pList);
-  CXFA_Object* GetThisObject() const { return m_pThisObject; }
-
-  int32_t GetIndexByName(CXFA_Node* refNode);
-  int32_t GetIndexByClassName(CXFA_Node* refNode);
-  WideString GetSomExpression(CXFA_Node* refNode);
-
-  void SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray);
-  void AddNodesOfRunScript(CXFA_Node* pNode);
-  CFXJSE_Class* GetJseNormalClass();
-
-  void SetRunAtType(XFA_AttributeEnum eRunAt) { m_eRunAtType = eRunAt; }
-  bool IsRunAtClient() { return m_eRunAtType != XFA_AttributeEnum::Server; }
-
-  CXFA_Script::Type GetType();
-  std::vector<CXFA_Node*>* GetUpObjectArray() { return &m_upObjectArray; }
-  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
-
- private:
-  CFXJSE_Context* CreateVariablesContext(CXFA_Node* pScriptNode,
-                                         CXFA_Node* pSubform);
-  void RemoveBuiltInObjs(CFXJSE_Context* pContext) const;
-  bool QueryNodeByFlag(CXFA_Node* refNode,
-                       const WideStringView& propname,
-                       CFXJSE_Value* pValue,
-                       uint32_t dwFlag,
-                       bool bSetting);
-  bool IsStrictScopeInJavaScript();
-  CXFA_Object* GetVariablesThis(CXFA_Object* pObject, bool bScriptNode = false);
-  bool QueryVariableValue(CXFA_Node* pScriptNode,
-                          const ByteStringView& szPropName,
-                          CFXJSE_Value* pValue,
-                          bool bGetter);
-  bool RunVariablesScript(CXFA_Node* pScriptNode);
-
-  UnownedPtr<CXFA_Document> const m_pDocument;
-  std::unique_ptr<CFXJSE_Context> m_JsContext;
-  CFXJSE_Class* m_pJsClass;
-  CXFA_Script::Type m_eScriptType;
-  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Value>> m_mapObjectToValue;
-  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Context>>
-      m_mapVariableToContext;
-  CXFA_EventParam m_eventParam;
-  std::vector<CXFA_Node*> m_upObjectArray;
-  // CacheList holds the List items so we can clean them up when we're done.
-  std::vector<std::unique_ptr<CXFA_List>> m_CacheList;
-  std::vector<CXFA_Node*>* m_pScriptNodeArray;
-  std::unique_ptr<CFXJSE_ResolveProcessor> m_ResolveProcessor;
-  std::unique_ptr<CFXJSE_FormCalcContext> m_FM2JSContext;
-  CXFA_Object* m_pThisObject;
-  XFA_AttributeEnum m_eRunAtType;
-};
-
-#endif  //  FXJS_CFXJSE_ENGINE_H_
diff --git a/fxjs/cfxjse_formcalc_context.cpp b/fxjs/cfxjse_formcalc_context.cpp
deleted file mode 100644
index c46b9a6..0000000
--- a/fxjs/cfxjse_formcalc_context.cpp
+++ /dev/null
@@ -1,6254 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_formcalc_context.h"
-
-#include <time.h>
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "core/fxcrt/cfx_decimal.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_random.h"
-#include "fxjs/cfxjse_class.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_localevalue.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-namespace {
-
-const double kFinancialPrecision = 0.00000001;
-
-struct XFA_FMHtmlReserveCode {
-  uint32_t m_uCode;
-  const wchar_t* m_htmlReserve;
-};
-
-// Sorted by m_htmlReserve
-XFA_FMHtmlReserveCode reservesForDecode[] = {
-    {198, L"AElig"},   {193, L"Aacute"},   {194, L"Acirc"},
-    {192, L"Agrave"},  {913, L"Alpha"},    {197, L"Aring"},
-    {195, L"Atilde"},  {196, L"Auml"},     {914, L"Beta"},
-    {199, L"Ccedil"},  {935, L"Chi"},      {8225, L"Dagger"},
-    {916, L"Delta"},   {208, L"ETH"},      {201, L"Eacute"},
-    {202, L"Ecirc"},   {200, L"Egrave"},   {917, L"Epsilon"},
-    {919, L"Eta"},     {203, L"Euml"},     {915, L"Gamma"},
-    {922, L"Kappa"},   {923, L"Lambda"},   {924, L"Mu"},
-    {209, L"Ntilde"},  {925, L"Nu"},       {338, L"OElig"},
-    {211, L"Oacute"},  {212, L"Ocirc"},    {210, L"Ograve"},
-    {937, L"Omega"},   {927, L"Omicron"},  {216, L"Oslash"},
-    {213, L"Otilde"},  {214, L"Ouml"},     {934, L"Phi"},
-    {928, L"Pi"},      {936, L"Psi"},      {929, L"Rho"},
-    {352, L"Scaron"},  {931, L"Sigma"},    {222, L"THORN"},
-    {932, L"Tau"},     {920, L"Theta"},    {218, L"Uacute"},
-    {219, L"Ucirc"},   {217, L"Ugrave"},   {933, L"Upsilon"},
-    {220, L"Uuml"},    {926, L"Xi"},       {221, L"Yacute"},
-    {376, L"Yuml"},    {918, L"Zeta"},     {225, L"aacute"},
-    {226, L"acirc"},   {180, L"acute"},    {230, L"aelig"},
-    {224, L"agrave"},  {8501, L"alefsym"}, {945, L"alpha"},
-    {38, L"amp"},      {8743, L"and"},     {8736, L"ang"},
-    {39, L"apos"},     {229, L"aring"},    {8776, L"asymp"},
-    {227, L"atilde"},  {228, L"auml"},     {8222, L"bdquo"},
-    {946, L"beta"},    {166, L"brvbar"},   {8226, L"bull"},
-    {8745, L"cap"},    {231, L"ccedil"},   {184, L"cedil"},
-    {162, L"cent"},    {967, L"chi"},      {710, L"circ"},
-    {9827, L"clubs"},  {8773, L"cong"},    {169, L"copy"},
-    {8629, L"crarr"},  {8746, L"cup"},     {164, L"current"},
-    {8659, L"dArr"},   {8224, L"dagger"},  {8595, L"darr"},
-    {176, L"deg"},     {948, L"delta"},    {9830, L"diams"},
-    {247, L"divide"},  {233, L"eacute"},   {234, L"ecirc"},
-    {232, L"egrave"},  {8709, L"empty"},   {8195, L"emsp"},
-    {8194, L"ensp"},   {949, L"epsilon"},  {8801, L"equiv"},
-    {951, L"eta"},     {240, L"eth"},      {235, L"euml"},
-    {8364, L"euro"},   {8707, L"exist"},   {402, L"fnof"},
-    {8704, L"forall"}, {189, L"frac12"},   {188, L"frac14"},
-    {190, L"frac34"},  {8260, L"frasl"},   {947, L"gamma"},
-    {8805, L"ge"},     {62, L"gt"},        {8660, L"hArr"},
-    {8596, L"harr"},   {9829, L"hearts"},  {8230, L"hellip"},
-    {237, L"iacute"},  {238, L"icirc"},    {161, L"iexcl"},
-    {236, L"igrave"},  {8465, L"image"},   {8734, L"infin"},
-    {8747, L"int"},    {953, L"iota"},     {191, L"iquest"},
-    {8712, L"isin"},   {239, L"iuml"},     {954, L"kappa"},
-    {8656, L"lArr"},   {205, L"lacute"},   {955, L"lambda"},
-    {9001, L"lang"},   {171, L"laquo"},    {8592, L"larr"},
-    {8968, L"lceil"},  {206, L"lcirc"},    {8220, L"ldquo"},
-    {8804, L"le"},     {8970, L"lfloor"},  {204, L"lgrave"},
-    {921, L"lota"},    {8727, L"lowast"},  {9674, L"loz"},
-    {8206, L"lrm"},    {8249, L"lsaquo"},  {8216, L"lsquo"},
-    {60, L"lt"},       {207, L"luml"},     {175, L"macr"},
-    {8212, L"mdash"},  {181, L"micro"},    {183, L"middot"},
-    {8722, L"minus"},  {956, L"mu"},       {8711, L"nabla"},
-    {160, L"nbsp"},    {8211, L"ndash"},   {8800, L"ne"},
-    {8715, L"ni"},     {172, L"not"},      {8713, L"notin"},
-    {8836, L"nsub"},   {241, L"ntilde"},   {957, L"nu"},
-    {243, L"oacute"},  {244, L"ocirc"},    {339, L"oelig"},
-    {242, L"ograve"},  {8254, L"oline"},   {969, L"omega"},
-    {959, L"omicron"}, {8853, L"oplus"},   {8744, L"or"},
-    {170, L"ordf"},    {186, L"ordm"},     {248, L"oslash"},
-    {245, L"otilde"},  {8855, L"otimes"},  {246, L"ouml"},
-    {182, L"para"},    {8706, L"part"},    {8240, L"permil"},
-    {8869, L"perp"},   {966, L"phi"},      {960, L"pi"},
-    {982, L"piv"},     {177, L"plusmn"},   {8242, L"prime"},
-    {8719, L"prod"},   {8733, L"prop"},    {968, L"psi"},
-    {163, L"pund"},    {34, L"quot"},      {8658, L"rArr"},
-    {8730, L"radic"},  {9002, L"rang"},    {187, L"raquo"},
-    {8594, L"rarr"},   {8969, L"rceil"},   {8476, L"real"},
-    {174, L"reg"},     {8971, L"rfloor"},  {961, L"rho"},
-    {8207, L"rlm"},    {8250, L"rsaquo"},  {8217, L"rsquo"},
-    {353, L"saron"},   {8218, L"sbquo"},   {8901, L"sdot"},
-    {167, L"sect"},    {173, L"shy"},      {963, L"sigma"},
-    {962, L"sigmaf"},  {8764, L"sim"},     {9824, L"spades"},
-    {8834, L"sub"},    {8838, L"sube"},    {8721, L"sum"},
-    {8835, L"sup"},    {185, L"sup1"},     {178, L"sup2"},
-    {179, L"sup3"},    {8839, L"supe"},    {223, L"szlig"},
-    {964, L"tau"},     {8221, L"tdquo"},   {8756, L"there4"},
-    {952, L"theta"},   {977, L"thetasym"}, {8201, L"thinsp"},
-    {254, L"thorn"},   {732, L"tilde"},    {215, L"times"},
-    {8482, L"trade"},  {8657, L"uArr"},    {250, L"uacute"},
-    {8593, L"uarr"},   {251, L"ucirc"},    {249, L"ugrave"},
-    {168, L"uml"},     {978, L"upsih"},    {965, L"upsilon"},
-    {252, L"uuml"},    {8472, L"weierp"},  {958, L"xi"},
-    {253, L"yacute"},  {165, L"yen"},      {255, L"yuml"},
-    {950, L"zeta"},    {8205, L"zwj"},     {8204, L"zwnj"},
-};
-
-// Sorted by m_uCode
-const XFA_FMHtmlReserveCode reservesForEncode[] = {
-    {34, L"quot"},     {38, L"amp"},      {39, L"apos"},
-    {60, L"lt"},       {62, L"gt"},       {160, L"nbsp"},
-    {161, L"iexcl"},   {162, L"cent"},    {163, L"pund"},
-    {164, L"current"}, {165, L"yen"},     {166, L"brvbar"},
-    {167, L"sect"},    {168, L"uml"},     {169, L"copy"},
-    {170, L"ordf"},    {171, L"laquo"},   {172, L"not"},
-    {173, L"shy"},     {174, L"reg"},     {175, L"macr"},
-    {176, L"deg"},     {177, L"plusmn"},  {178, L"sup2"},
-    {179, L"sup3"},    {180, L"acute"},   {181, L"micro"},
-    {182, L"para"},    {183, L"middot"},  {184, L"cedil"},
-    {185, L"sup1"},    {186, L"ordm"},    {187, L"raquo"},
-    {188, L"frac14"},  {189, L"frac12"},  {190, L"frac34"},
-    {191, L"iquest"},  {192, L"Agrave"},  {193, L"Aacute"},
-    {194, L"Acirc"},   {195, L"Atilde"},  {196, L"Auml"},
-    {197, L"Aring"},   {198, L"AElig"},   {199, L"Ccedil"},
-    {200, L"Egrave"},  {201, L"Eacute"},  {202, L"Ecirc"},
-    {203, L"Euml"},    {204, L"lgrave"},  {205, L"lacute"},
-    {206, L"lcirc"},   {207, L"luml"},    {208, L"ETH"},
-    {209, L"Ntilde"},  {210, L"Ograve"},  {211, L"Oacute"},
-    {212, L"Ocirc"},   {213, L"Otilde"},  {214, L"Ouml"},
-    {215, L"times"},   {216, L"Oslash"},  {217, L"Ugrave"},
-    {218, L"Uacute"},  {219, L"Ucirc"},   {220, L"Uuml"},
-    {221, L"Yacute"},  {222, L"THORN"},   {223, L"szlig"},
-    {224, L"agrave"},  {225, L"aacute"},  {226, L"acirc"},
-    {227, L"atilde"},  {228, L"auml"},    {229, L"aring"},
-    {230, L"aelig"},   {231, L"ccedil"},  {232, L"egrave"},
-    {233, L"eacute"},  {234, L"ecirc"},   {235, L"euml"},
-    {236, L"igrave"},  {237, L"iacute"},  {238, L"icirc"},
-    {239, L"iuml"},    {240, L"eth"},     {241, L"ntilde"},
-    {242, L"ograve"},  {243, L"oacute"},  {244, L"ocirc"},
-    {245, L"otilde"},  {246, L"ouml"},    {247, L"divide"},
-    {248, L"oslash"},  {249, L"ugrave"},  {250, L"uacute"},
-    {251, L"ucirc"},   {252, L"uuml"},    {253, L"yacute"},
-    {254, L"thorn"},   {255, L"yuml"},    {338, L"OElig"},
-    {339, L"oelig"},   {352, L"Scaron"},  {353, L"saron"},
-    {376, L"Yuml"},    {402, L"fnof"},    {710, L"circ"},
-    {732, L"tilde"},   {913, L"Alpha"},   {914, L"Beta"},
-    {915, L"Gamma"},   {916, L"Delta"},   {917, L"Epsilon"},
-    {918, L"Zeta"},    {919, L"Eta"},     {920, L"Theta"},
-    {921, L"lota"},    {922, L"Kappa"},   {923, L"Lambda"},
-    {924, L"Mu"},      {925, L"Nu"},      {926, L"Xi"},
-    {927, L"Omicron"}, {928, L"Pi"},      {929, L"Rho"},
-    {931, L"Sigma"},   {932, L"Tau"},     {933, L"Upsilon"},
-    {934, L"Phi"},     {935, L"Chi"},     {936, L"Psi"},
-    {937, L"Omega"},   {945, L"alpha"},   {946, L"beta"},
-    {947, L"gamma"},   {948, L"delta"},   {949, L"epsilon"},
-    {950, L"zeta"},    {951, L"eta"},     {952, L"theta"},
-    {953, L"iota"},    {954, L"kappa"},   {955, L"lambda"},
-    {956, L"mu"},      {957, L"nu"},      {958, L"xi"},
-    {959, L"omicron"}, {960, L"pi"},      {961, L"rho"},
-    {962, L"sigmaf"},  {963, L"sigma"},   {964, L"tau"},
-    {965, L"upsilon"}, {966, L"phi"},     {967, L"chi"},
-    {968, L"psi"},     {969, L"omega"},   {977, L"thetasym"},
-    {978, L"upsih"},   {982, L"piv"},     {8194, L"ensp"},
-    {8195, L"emsp"},   {8201, L"thinsp"}, {8204, L"zwnj"},
-    {8205, L"zwj"},    {8206, L"lrm"},    {8207, L"rlm"},
-    {8211, L"ndash"},  {8212, L"mdash"},  {8216, L"lsquo"},
-    {8217, L"rsquo"},  {8218, L"sbquo"},  {8220, L"ldquo"},
-    {8221, L"tdquo"},  {8222, L"bdquo"},  {8224, L"dagger"},
-    {8225, L"Dagger"}, {8226, L"bull"},   {8230, L"hellip"},
-    {8240, L"permil"}, {8242, L"prime"},  {8249, L"lsaquo"},
-    {8250, L"rsaquo"}, {8254, L"oline"},  {8260, L"frasl"},
-    {8364, L"euro"},   {8465, L"image"},  {8472, L"weierp"},
-    {8476, L"real"},   {8482, L"trade"},  {8501, L"alefsym"},
-    {8592, L"larr"},   {8593, L"uarr"},   {8594, L"rarr"},
-    {8595, L"darr"},   {8596, L"harr"},   {8629, L"crarr"},
-    {8656, L"lArr"},   {8657, L"uArr"},   {8658, L"rArr"},
-    {8659, L"dArr"},   {8660, L"hArr"},   {8704, L"forall"},
-    {8706, L"part"},   {8707, L"exist"},  {8709, L"empty"},
-    {8711, L"nabla"},  {8712, L"isin"},   {8713, L"notin"},
-    {8715, L"ni"},     {8719, L"prod"},   {8721, L"sum"},
-    {8722, L"minus"},  {8727, L"lowast"}, {8730, L"radic"},
-    {8733, L"prop"},   {8734, L"infin"},  {8736, L"ang"},
-    {8743, L"and"},    {8744, L"or"},     {8745, L"cap"},
-    {8746, L"cup"},    {8747, L"int"},    {8756, L"there4"},
-    {8764, L"sim"},    {8773, L"cong"},   {8776, L"asymp"},
-    {8800, L"ne"},     {8801, L"equiv"},  {8804, L"le"},
-    {8805, L"ge"},     {8834, L"sub"},    {8835, L"sup"},
-    {8836, L"nsub"},   {8838, L"sube"},   {8839, L"supe"},
-    {8853, L"oplus"},  {8855, L"otimes"}, {8869, L"perp"},
-    {8901, L"sdot"},   {8968, L"lceil"},  {8969, L"rceil"},
-    {8970, L"lfloor"}, {8971, L"rfloor"}, {9001, L"lang"},
-    {9002, L"rang"},   {9674, L"loz"},    {9824, L"spades"},
-    {9827, L"clubs"},  {9829, L"hearts"}, {9830, L"diams"},
-};
-
-const FXJSE_FUNCTION_DESCRIPTOR formcalc_fm2js_functions[] = {
-    {"Abs", CFXJSE_FormCalcContext::Abs},
-    {"Avg", CFXJSE_FormCalcContext::Avg},
-    {"Ceil", CFXJSE_FormCalcContext::Ceil},
-    {"Count", CFXJSE_FormCalcContext::Count},
-    {"Floor", CFXJSE_FormCalcContext::Floor},
-    {"Max", CFXJSE_FormCalcContext::Max},
-    {"Min", CFXJSE_FormCalcContext::Min},
-    {"Mod", CFXJSE_FormCalcContext::Mod},
-    {"Round", CFXJSE_FormCalcContext::Round},
-    {"Sum", CFXJSE_FormCalcContext::Sum},
-    {"Date", CFXJSE_FormCalcContext::Date},
-    {"Date2Num", CFXJSE_FormCalcContext::Date2Num},
-    {"DateFmt", CFXJSE_FormCalcContext::DateFmt},
-    {"IsoDate2Num", CFXJSE_FormCalcContext::IsoDate2Num},
-    {"IsoTime2Num", CFXJSE_FormCalcContext::IsoTime2Num},
-    {"LocalDateFmt", CFXJSE_FormCalcContext::LocalDateFmt},
-    {"LocalTimeFmt", CFXJSE_FormCalcContext::LocalTimeFmt},
-    {"Num2Date", CFXJSE_FormCalcContext::Num2Date},
-    {"Num2GMTime", CFXJSE_FormCalcContext::Num2GMTime},
-    {"Num2Time", CFXJSE_FormCalcContext::Num2Time},
-    {"Time", CFXJSE_FormCalcContext::Time},
-    {"Time2Num", CFXJSE_FormCalcContext::Time2Num},
-    {"TimeFmt", CFXJSE_FormCalcContext::TimeFmt},
-    {"Apr", CFXJSE_FormCalcContext::Apr},
-    {"Cterm", CFXJSE_FormCalcContext::CTerm},
-    {"FV", CFXJSE_FormCalcContext::FV},
-    {"Ipmt", CFXJSE_FormCalcContext::IPmt},
-    {"NPV", CFXJSE_FormCalcContext::NPV},
-    {"Pmt", CFXJSE_FormCalcContext::Pmt},
-    {"PPmt", CFXJSE_FormCalcContext::PPmt},
-    {"PV", CFXJSE_FormCalcContext::PV},
-    {"Rate", CFXJSE_FormCalcContext::Rate},
-    {"Term", CFXJSE_FormCalcContext::Term},
-    {"Choose", CFXJSE_FormCalcContext::Choose},
-    {"Exists", CFXJSE_FormCalcContext::Exists},
-    {"HasValue", CFXJSE_FormCalcContext::HasValue},
-    {"Oneof", CFXJSE_FormCalcContext::Oneof},
-    {"Within", CFXJSE_FormCalcContext::Within},
-    {"If", CFXJSE_FormCalcContext::If},
-    {"Eval", CFXJSE_FormCalcContext::Eval},
-    {"Translate", CFXJSE_FormCalcContext::eval_translation},
-    {"Ref", CFXJSE_FormCalcContext::Ref},
-    {"UnitType", CFXJSE_FormCalcContext::UnitType},
-    {"UnitValue", CFXJSE_FormCalcContext::UnitValue},
-    {"At", CFXJSE_FormCalcContext::At},
-    {"Concat", CFXJSE_FormCalcContext::Concat},
-    {"Decode", CFXJSE_FormCalcContext::Decode},
-    {"Encode", CFXJSE_FormCalcContext::Encode},
-    {"Format", CFXJSE_FormCalcContext::Format},
-    {"Left", CFXJSE_FormCalcContext::Left},
-    {"Len", CFXJSE_FormCalcContext::Len},
-    {"Lower", CFXJSE_FormCalcContext::Lower},
-    {"Ltrim", CFXJSE_FormCalcContext::Ltrim},
-    {"Parse", CFXJSE_FormCalcContext::Parse},
-    {"Replace", CFXJSE_FormCalcContext::Replace},
-    {"Right", CFXJSE_FormCalcContext::Right},
-    {"Rtrim", CFXJSE_FormCalcContext::Rtrim},
-    {"Space", CFXJSE_FormCalcContext::Space},
-    {"Str", CFXJSE_FormCalcContext::Str},
-    {"Stuff", CFXJSE_FormCalcContext::Stuff},
-    {"Substr", CFXJSE_FormCalcContext::Substr},
-    {"Uuid", CFXJSE_FormCalcContext::Uuid},
-    {"Upper", CFXJSE_FormCalcContext::Upper},
-    {"WordNum", CFXJSE_FormCalcContext::WordNum},
-    {"Get", CFXJSE_FormCalcContext::Get},
-    {"Post", CFXJSE_FormCalcContext::Post},
-    {"Put", CFXJSE_FormCalcContext::Put},
-    {"pos_op", CFXJSE_FormCalcContext::positive_operator},
-    {"neg_op", CFXJSE_FormCalcContext::negative_operator},
-    {"log_or_op", CFXJSE_FormCalcContext::logical_or_operator},
-    {"log_and_op", CFXJSE_FormCalcContext::logical_and_operator},
-    {"log_not_op", CFXJSE_FormCalcContext::logical_not_operator},
-    {"eq_op", CFXJSE_FormCalcContext::equality_operator},
-    {"neq_op", CFXJSE_FormCalcContext::notequality_operator},
-    {"lt_op", CFXJSE_FormCalcContext::less_operator},
-    {"le_op", CFXJSE_FormCalcContext::lessequal_operator},
-    {"gt_op", CFXJSE_FormCalcContext::greater_operator},
-    {"ge_op", CFXJSE_FormCalcContext::greaterequal_operator},
-    {"plus_op", CFXJSE_FormCalcContext::plus_operator},
-    {"minus_op", CFXJSE_FormCalcContext::minus_operator},
-    {"mul_op", CFXJSE_FormCalcContext::multiple_operator},
-    {"div_op", CFXJSE_FormCalcContext::divide_operator},
-    {"asgn_val_op", CFXJSE_FormCalcContext::assign_value_operator},
-    {"dot_acc", CFXJSE_FormCalcContext::dot_accessor},
-    {"dotdot_acc", CFXJSE_FormCalcContext::dotdot_accessor},
-    {"concat_obj", CFXJSE_FormCalcContext::concat_fm_object},
-    {"is_obj", CFXJSE_FormCalcContext::is_fm_object},
-    {"is_ary", CFXJSE_FormCalcContext::is_fm_array},
-    {"get_val", CFXJSE_FormCalcContext::get_fm_value},
-    {"get_jsobj", CFXJSE_FormCalcContext::get_fm_jsobj},
-    {"var_filter", CFXJSE_FormCalcContext::fm_var_filter},
-};
-
-const FXJSE_CLASS_DESCRIPTOR formcalc_fm2js_descriptor = {
-    "XFA_FM2JS_FormCalcClass",               // name
-    formcalc_fm2js_functions,                // methods
-    FX_ArraySize(formcalc_fm2js_functions),  // number of methods
-    nullptr,                                 // dynamic prop type
-    nullptr,                                 // dynamic prop getter
-    nullptr,                                 // dynamic prop setter
-    nullptr,                                 // dynamic prop method call
-};
-
-const uint8_t g_sAltTable_Date[] = {
-    255, 255, 255, 3,   9,   255, 255, 255, 255, 255, 255,
-    255, 2,   255, 255, 255, 255, 255, 255, 255, 255, 255,
-    255, 255, 1,   255, 255, 255, 255, 255, 255, 255, 255,
-};
-static_assert(FX_ArraySize(g_sAltTable_Date) == L'a' - L'A' + 1,
-              "Invalid g_sAltTable_Date size.");
-
-const uint8_t g_sAltTable_Time[] = {
-    14,  255, 255, 3,   9,   255, 255, 15,  255, 255, 255,
-    255, 6,   255, 255, 255, 255, 255, 7,   255, 255, 255,
-    255, 255, 1,   17,  255, 255, 255, 255, 255, 255, 255,
-};
-static_assert(FX_ArraySize(g_sAltTable_Time) == L'a' - L'A' + 1,
-              "Invalid g_sAltTable_Time size.");
-
-void AlternateDateTimeSymbols(WideString& wsPattern,
-                              const WideString& wsAltSymbols,
-                              const uint8_t* pAltTable) {
-  int32_t nLength = wsPattern.GetLength();
-  bool bInConstRange = false;
-  bool bEscape = false;
-  int32_t i = 0;
-  while (i < nLength) {
-    wchar_t wc = wsPattern[i];
-    if (wc == L'\'') {
-      bInConstRange = !bInConstRange;
-      if (bEscape) {
-        i++;
-      } else {
-        wsPattern.Delete(i);
-        nLength--;
-      }
-      bEscape = !bEscape;
-      continue;
-    }
-    if (!bInConstRange && wc >= L'A' && wc <= L'a') {
-      uint8_t nAlt = pAltTable[wc - L'A'];
-      if (nAlt != 255)
-        wsPattern.SetAt(i, wsAltSymbols[nAlt]);
-    }
-    i++;
-    bEscape = false;
-  }
-}
-
-bool PatternStringType(const ByteStringView& szPattern, uint32_t& patternType) {
-  WideString wsPattern = WideString::FromUTF8(szPattern);
-  if (L"datetime" == wsPattern.Left(8)) {
-    patternType = XFA_VT_DATETIME;
-    return true;
-  }
-  if (L"date" == wsPattern.Left(4)) {
-    auto pos = wsPattern.Find(L"time");
-    patternType =
-        pos.has_value() && pos.value() != 0 ? XFA_VT_DATETIME : XFA_VT_DATE;
-    return true;
-  }
-  if (L"time" == wsPattern.Left(4)) {
-    patternType = XFA_VT_TIME;
-    return true;
-  }
-  if (L"text" == wsPattern.Left(4)) {
-    patternType = XFA_VT_TEXT;
-    return true;
-  }
-  if (L"num" == wsPattern.Left(3)) {
-    if (L"integer" == wsPattern.Mid(4, 7)) {
-      patternType = XFA_VT_INTEGER;
-    } else if (L"decimal" == wsPattern.Mid(4, 7)) {
-      patternType = XFA_VT_DECIMAL;
-    } else if (L"currency" == wsPattern.Mid(4, 8)) {
-      patternType = XFA_VT_FLOAT;
-    } else if (L"percent" == wsPattern.Mid(4, 7)) {
-      patternType = XFA_VT_FLOAT;
-    } else {
-      patternType = XFA_VT_FLOAT;
-    }
-    return true;
-  }
-
-  patternType = XFA_VT_NULL;
-  wsPattern.MakeLower();
-  const wchar_t* pData = wsPattern.c_str();
-  int32_t iLength = wsPattern.GetLength();
-  int32_t iIndex = 0;
-  bool bSingleQuotation = false;
-  wchar_t patternChar;
-  while (iIndex < iLength) {
-    patternChar = pData[iIndex];
-    if (patternChar == 0x27) {
-      bSingleQuotation = !bSingleQuotation;
-    } else if (!bSingleQuotation &&
-               (patternChar == 'y' || patternChar == 'j')) {
-      patternType = XFA_VT_DATE;
-      iIndex++;
-      wchar_t timePatternChar;
-      while (iIndex < iLength) {
-        timePatternChar = pData[iIndex];
-        if (timePatternChar == 0x27) {
-          bSingleQuotation = !bSingleQuotation;
-        } else if (!bSingleQuotation && timePatternChar == 't') {
-          patternType = XFA_VT_DATETIME;
-          break;
-        }
-        iIndex++;
-      }
-      break;
-    } else if (!bSingleQuotation &&
-               (patternChar == 'h' || patternChar == 'k')) {
-      patternType = XFA_VT_TIME;
-      break;
-    } else if (!bSingleQuotation &&
-               (patternChar == 'a' || patternChar == 'x' ||
-                patternChar == 'o' || patternChar == '0')) {
-      patternType = XFA_VT_TEXT;
-      if (patternChar == 'x' || patternChar == 'o' || patternChar == '0') {
-        break;
-      }
-    } else if (!bSingleQuotation &&
-               (patternChar == 'z' || patternChar == 's' ||
-                patternChar == 'e' || patternChar == 'v' ||
-                patternChar == '8' || patternChar == ',' ||
-                patternChar == '.' || patternChar == '$')) {
-      patternType = XFA_VT_FLOAT;
-      if (patternChar == 'v' || patternChar == '8' || patternChar == '$') {
-        break;
-      }
-    }
-    iIndex++;
-  }
-  if (patternType == XFA_VT_NULL) {
-    patternType = XFA_VT_TEXT | XFA_VT_FLOAT;
-  }
-  return false;
-}
-
-CFXJSE_FormCalcContext* ToJSContext(CFXJSE_Value* pValue,
-                                    CFXJSE_Class* pClass) {
-  CFXJSE_HostObject* pHostObj = pValue->ToHostObject(pClass);
-  if (!pHostObj || pHostObj->type() != CFXJSE_HostObject::kFM2JS)
-    return nullptr;
-  return static_cast<CFXJSE_FormCalcContext*>(pHostObj);
-}
-
-bool IsWhitespace(char c) {
-  return c == 0x20 || c == 0x09 || c == 0x0B || c == 0x0C || c == 0x0A ||
-         c == 0x0D;
-}
-
-IFX_Locale* LocaleFromString(CXFA_Document* pDoc,
-                             CXFA_LocaleMgr* pMgr,
-                             const ByteStringView& szLocale) {
-  if (!szLocale.IsEmpty())
-    return pMgr->GetLocaleByName(WideString::FromUTF8(szLocale));
-
-  CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-  ASSERT(pThisNode);
-  return pThisNode->GetLocale();
-}
-
-WideString FormatFromString(IFX_Locale* pLocale,
-                            const ByteStringView& szFormat) {
-  if (!szFormat.IsEmpty())
-    return WideString::FromUTF8(szFormat);
-
-  return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
-}
-
-FX_LOCALEDATETIMESUBCATEGORY SubCategoryFromInt(int32_t iStyle) {
-  switch (iStyle) {
-    case 1:
-      return FX_LOCALEDATETIMESUBCATEGORY_Short;
-    case 3:
-      return FX_LOCALEDATETIMESUBCATEGORY_Long;
-    case 4:
-      return FX_LOCALEDATETIMESUBCATEGORY_Full;
-    case 0:
-    case 2:
-    default:
-      return FX_LOCALEDATETIMESUBCATEGORY_Medium;
-  }
-}
-
-bool IsPartOfNumber(char ch) {
-  return std::isdigit(ch) || ch == '-' || ch == '.';
-}
-
-bool IsPartOfNumberW(wchar_t ch) {
-  return std::iswdigit(ch) || ch == L'-' || ch == L'.';
-}
-
-ByteString GUIDString(bool bSeparator) {
-  uint8_t data[16];
-  FX_Random_GenerateMT(reinterpret_cast<uint32_t*>(data), 4);
-  data[6] = (data[6] & 0x0F) | 0x40;
-
-  ByteString bsStr;
-  char* pBuf = bsStr.GetBuffer(40);
-  for (int32_t i = 0; i < 16; ++i, pBuf += 2) {
-    if (bSeparator && (i == 4 || i == 6 || i == 8 || i == 10))
-      *pBuf++ = L'-';
-
-    FXSYS_IntToTwoHexChars(data[i], pBuf);
-  }
-  bsStr.ReleaseBuffer(bSeparator ? 36 : 32);
-  return bsStr;
-}
-
-double ByteStringToDouble(const ByteStringView& szStringVal) {
-  WideString wsValue = WideString::FromUTF8(szStringVal);
-  wsValue.Trim();
-
-  int32_t cc = 0;
-  bool bNegative = false;
-
-  const wchar_t* str = wsValue.c_str();
-  int32_t len = wsValue.GetLength();
-  if (str[0] == '+') {
-    cc++;
-  } else if (str[0] == '-') {
-    bNegative = true;
-    cc++;
-  }
-
-  int32_t nIntegralLen = 0;
-  int64_t nIntegral = 0;
-  while (cc < len) {
-    if (str[cc] == '.' || str[cc] == 'E' || str[cc] == 'e' ||
-        nIntegralLen > 17) {
-      break;
-    }
-    if (!FXSYS_isDecimalDigit(str[cc])) {
-      return 0;
-    }
-    nIntegral = nIntegral * 10 + str[cc] - '0';
-    cc++;
-    nIntegralLen++;
-  }
-  nIntegral = bNegative ? -nIntegral : nIntegral;
-
-  int32_t scale = 0;
-  double fraction = 0.0;
-  uint32_t dwFractional = 0;
-  if (cc < len && str[cc] == '.') {
-    cc++;
-    while (cc < len) {
-      fraction += XFA_GetFractionalScale(scale) * (str[cc] - '0');
-      scale++;
-      cc++;
-      if (cc == len)
-        break;
-      if (scale == XFA_GetMaxFractionalScale() || str[cc] == 'E' ||
-          str[cc] == 'e') {
-        break;
-      }
-      if (!FXSYS_isDecimalDigit(str[cc]))
-        return 0;
-    }
-    dwFractional = static_cast<uint32_t>(fraction * 4294967296.0);
-  }
-
-  int32_t nExponent = 0;
-  bool bExpSign = false;
-  if (cc < len && (str[cc] == 'E' || str[cc] == 'e')) {
-    cc++;
-    if (cc < len) {
-      if (str[cc] == '+') {
-        cc++;
-      } else if (str[cc] == '-') {
-        bExpSign = true;
-        cc++;
-      }
-    }
-    while (cc < len) {
-      if (str[cc] == '.' || !FXSYS_isDecimalDigit(str[cc]))
-        return 0;
-
-      nExponent = nExponent * 10 + str[cc] - '0';
-      cc++;
-    }
-    nExponent = bExpSign ? -nExponent : nExponent;
-  }
-
-  double dValue = dwFractional / 4294967296.0;
-  dValue = nIntegral + (nIntegral >= 0 ? dValue : -dValue);
-  if (nExponent != 0)
-    dValue *= FXSYS_pow(10, static_cast<float>(nExponent));
-
-  return dValue;
-}
-
-}  // namespace
-
-// static
-void CFXJSE_FormCalcContext::Abs(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Abs");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double dValue = ValueToDouble(pThis, argOne.get());
-  if (dValue < 0)
-    dValue = -dValue;
-
-  args.GetReturnValue()->SetDouble(dValue);
-}
-
-// static
-void CFXJSE_FormCalcContext::Avg(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  uint32_t uCount = 0;
-  double dSum = 0.0;
-  for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
-
-    if (!argValue->IsArray()) {
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-      continue;
-    }
-
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argValue->GetObjectProperty("length", lengthValue.get());
-    int32_t iLength = lengthValue->ToInteger();
-
-    if (iLength > 2) {
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          auto defaultPropValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-          GetObjectDefaultValue(jsObjectValue.get(), defaultPropValue.get());
-          if (defaultPropValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, defaultPropValue.get());
-          uCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, newPropertyValue.get());
-          uCount++;
-        }
-      }
-    }
-  }
-  if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  args.GetReturnValue()->SetDouble(dSum / uCount);
-}
-
-// static
-void CFXJSE_FormCalcContext::Ceil(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Ceil");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argValue.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  args.GetReturnValue()->SetFloat(ceil(ValueToFloat(pThis, argValue.get())));
-}
-
-// static
-void CFXJSE_FormCalcContext::Count(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  int32_t iCount = 0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
-
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (!newPropertyValue->IsNull())
-            iCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          iCount += newPropertyValue->IsNull() ? 0 : 1;
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (!newPropertyValue->IsNull())
-        iCount++;
-    } else {
-      iCount++;
-    }
-  }
-  args.GetReturnValue()->SetInteger(iCount);
-}
-
-// static
-void CFXJSE_FormCalcContext::Floor(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Floor");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argValue.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  args.GetReturnValue()->SetFloat(floor(ValueToFloat(pThis, argValue.get())));
-}
-
-// static
-void CFXJSE_FormCalcContext::Max(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  uint32_t uCount = 0;
-  double dMaxValue = 0.0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
-
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      uCount++;
-      double dValue = ValueToDouble(pThis, newPropertyValue.get());
-      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-    } else {
-      uCount++;
-      double dValue = ValueToDouble(pThis, argValue.get());
-      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-    }
-  }
-  if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  args.GetReturnValue()->SetDouble(dMaxValue);
-}
-
-// static
-void CFXJSE_FormCalcContext::Min(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  uint32_t uCount = 0;
-  double dMinValue = 0.0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
-
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      uCount++;
-      double dValue = ValueToDouble(pThis, newPropertyValue.get());
-      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-    } else {
-      uCount++;
-      double dValue = ValueToDouble(pThis, argValue.get());
-      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-    }
-  }
-  if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  args.GetReturnValue()->SetDouble(dMinValue);
-}
-
-// static
-void CFXJSE_FormCalcContext::Mod(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 2) {
-    pContext->ThrowParamCountMismatchException(L"Mod");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
-  if (argOne->IsNull() || argTwo->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  bool argOneResult;
-  double dDividend = ExtractDouble(pThis, argOne.get(), &argOneResult);
-  bool argTwoResult;
-  double dDivisor = ExtractDouble(pThis, argTwo.get(), &argTwoResult);
-  if (!argOneResult || !argTwoResult) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  if (dDivisor == 0.0) {
-    pContext->ThrowDivideByZeroException();
-    return;
-  }
-
-  args.GetReturnValue()->SetDouble(dDividend -
-                                   dDivisor * (int32_t)(dDividend / dDivisor));
-}
-
-// static
-void CFXJSE_FormCalcContext::Round(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 2) {
-    pContext->ThrowParamCountMismatchException(L"Round");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  bool dValueRet;
-  double dValue = ExtractDouble(pThis, argOne.get(), &dValueRet);
-  if (!dValueRet) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  uint8_t uPrecision = 0;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
-    if (argTwo->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-
-    bool dPrecisionRet;
-    double dPrecision = ExtractDouble(pThis, argTwo.get(), &dPrecisionRet);
-    if (!dPrecisionRet) {
-      pContext->ThrowArgumentMismatchException();
-      return;
-    }
-
-    uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
-  }
-
-  CFX_Decimal decimalValue(static_cast<float>(dValue), uPrecision);
-  args.GetReturnValue()->SetDouble(decimalValue);
-}
-
-// static
-void CFXJSE_FormCalcContext::Sum(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc == 0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  uint32_t uCount = 0;
-  double dSum = 0.0;
-  for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
-
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, jsObjectValue.get());
-          uCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, newPropertyValue.get());
-          uCount++;
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-    } else {
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-    }
-  }
-  if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  args.GetReturnValue()->SetDouble(dSum);
-}
-
-// static
-void CFXJSE_FormCalcContext::Date(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 0) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Date");
-    return;
-  }
-
-  time_t currentTime;
-  time(&currentTime);
-  struct tm* pTmStruct = gmtime(&currentTime);
-
-  args.GetReturnValue()->SetInteger(DateString2Num(
-      ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900,
-                         pTmStruct->tm_mon + 1, pTmStruct->tm_mday)
-          .AsStringView()));
-}
-
-// static
-void CFXJSE_FormCalcContext::Date2Num(CFXJSE_Value* pThis,
-                                      const ByteStringView& szFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Date2Num");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, dateValue.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString dateString = ValueToUTF8String(dateValue.get());
-  ByteString formatString;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    formatString = ValueToUTF8String(formatValue.get());
-  }
-
-  ByteString localString;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localValue.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    localString = ValueToUTF8String(localValue.get());
-  }
-
-  ByteString szIsoDateString =
-      Local2IsoDate(pThis, dateString.AsStringView(),
-                    formatString.AsStringView(), localString.AsStringView());
-  args.GetReturnValue()->SetInteger(
-      DateString2Num(szIsoDateString.AsStringView()));
-}
-
-// static
-void CFXJSE_FormCalcContext::DateFmt(CFXJSE_Value* pThis,
-                                     const ByteStringView& szFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc > 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Date2Num");
-    return;
-  }
-
-  int32_t iStyle = 0;
-  if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
-    if (iStyle < 0 || iStyle > 4)
-      iStyle = 0;
-  }
-
-  ByteString szLocal;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
-    if (argLocal->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    szLocal = ValueToUTF8String(argLocal.get());
-  }
-
-  ByteString formatStr =
-      GetStandardDateFormat(pThis, iStyle, szLocal.AsStringView());
-  args.GetReturnValue()->SetString(formatStr.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::IsoDate2Num(CFXJSE_Value* pThis,
-                                         const ByteStringView& szFuncName,
-                                         CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)
-        ->ThrowParamCountMismatchException(L"IsoDate2Num");
-    return;
-  }
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  ByteString szArgString = ValueToUTF8String(argOne.get());
-  args.GetReturnValue()->SetInteger(DateString2Num(szArgString.AsStringView()));
-}
-
-// static
-void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis,
-                                         const ByteStringView& szFuncName,
-                                         CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"IsoTime2Num");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  CXFA_Document* pDoc = pContext->GetDocument();
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  ByteString szArgString = ValueToUTF8String(argOne.get());
-  auto pos = szArgString.Find('T', 0);
-  if (!pos.has_value() || pos.value() == szArgString.GetLength() - 1) {
-    args.GetReturnValue()->SetInteger(0);
-    return;
-  }
-  szArgString = szArgString.Right(szArgString.GetLength() - (pos.value() + 1));
-
-  CXFA_LocaleValue timeValue(
-      XFA_VT_TIME, WideString::FromUTF8(szArgString.AsStringView()), pMgr);
-  if (!timeValue.IsValid()) {
-    args.GetReturnValue()->SetInteger(0);
-    return;
-  }
-
-  CFX_DateTime uniTime = timeValue.GetTime();
-  int32_t hour = uniTime.GetHour();
-  int32_t min = uniTime.GetMinute();
-  int32_t second = uniTime.GetSecond();
-  int32_t milSecond = uniTime.GetMillisecond();
-
-  // TODO(dsinclair): See if there is other time conversion code in pdfium and
-  //   consolidate.
-  int32_t mins = hour * 60 + min;
-  mins -= (pMgr->GetDefLocale()->GetTimeZone().tzHour * 60);
-  while (mins > 1440)
-    mins -= 1440;
-  while (mins < 0)
-    mins += 1440;
-  hour = mins / 60;
-  min = mins % 60;
-
-  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
-                                    second * 1000 + milSecond + 1);
-}
-
-// static
-void CFXJSE_FormCalcContext::LocalDateFmt(CFXJSE_Value* pThis,
-                                          const ByteStringView& szFuncName,
-                                          CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc > 2) {
-    ToJSContext(pThis, nullptr)
-        ->ThrowParamCountMismatchException(L"LocalDateFmt");
-    return;
-  }
-
-  int32_t iStyle = 0;
-  if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
-    if (iStyle > 4 || iStyle < 0)
-      iStyle = 0;
-  }
-
-  ByteString szLocal;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
-    if (argLocal->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    szLocal = ValueToUTF8String(argLocal.get());
-  }
-
-  ByteString formatStr =
-      GetLocalDateFormat(pThis, iStyle, szLocal.AsStringView(), false);
-  args.GetReturnValue()->SetString(formatStr.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::LocalTimeFmt(CFXJSE_Value* pThis,
-                                          const ByteStringView& szFuncName,
-                                          CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc > 2) {
-    ToJSContext(pThis, nullptr)
-        ->ThrowParamCountMismatchException(L"LocalTimeFmt");
-    return;
-  }
-
-  int32_t iStyle = 0;
-  if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
-    if (iStyle > 4 || iStyle < 0)
-      iStyle = 0;
-  }
-
-  ByteString szLocal;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
-    if (argLocal->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    szLocal = ValueToUTF8String(argLocal.get());
-  }
-
-  ByteString formatStr =
-      GetLocalTimeFormat(pThis, iStyle, szLocal.AsStringView(), false);
-  args.GetReturnValue()->SetString(formatStr.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Num2Date(CFXJSE_Value* pThis,
-                                      const ByteStringView& szFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Num2Date");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, dateValue.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  int32_t dDate = (int32_t)ValueToFloat(pThis, dateValue.get());
-  if (dDate < 1) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString formatString;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    formatString = ValueToUTF8String(formatValue.get());
-  }
-
-  ByteString localString;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localValue.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    localString = ValueToUTF8String(localValue.get());
-  }
-
-  int32_t iYear = 1900;
-  int32_t iMonth = 1;
-  int32_t iDay = 1;
-  int32_t i = 0;
-  while (dDate > 0) {
-    if (iMonth == 2) {
-      if ((!((iYear + i) % 4) && ((iYear + i) % 100)) || !((iYear + i) % 400)) {
-        if (dDate > 29) {
-          ++iMonth;
-          if (iMonth > 12) {
-            iMonth = 1;
-            ++i;
-          }
-          iDay = 1;
-          dDate -= 29;
-        } else {
-          iDay += static_cast<int32_t>(dDate) - 1;
-          dDate = 0;
-        }
-      } else {
-        if (dDate > 28) {
-          ++iMonth;
-          if (iMonth > 12) {
-            iMonth = 1;
-            ++i;
-          }
-          iDay = 1;
-          dDate -= 28;
-        } else {
-          iDay += static_cast<int32_t>(dDate) - 1;
-          dDate = 0;
-        }
-      }
-    } else if (iMonth < 8) {
-      if ((iMonth % 2 == 0)) {
-        if (dDate > 30) {
-          ++iMonth;
-          if (iMonth > 12) {
-            iMonth = 1;
-            ++i;
-          }
-          iDay = 1;
-          dDate -= 30;
-        } else {
-          iDay += static_cast<int32_t>(dDate) - 1;
-          dDate = 0;
-        }
-      } else {
-        if (dDate > 31) {
-          ++iMonth;
-          if (iMonth > 12) {
-            iMonth = 1;
-            ++i;
-          }
-          iDay = 1;
-          dDate -= 31;
-        } else {
-          iDay += static_cast<int32_t>(dDate) - 1;
-          dDate = 0;
-        }
-      }
-    } else {
-      if (iMonth % 2 != 0) {
-        if (dDate > 30) {
-          ++iMonth;
-          if (iMonth > 12) {
-            iMonth = 1;
-            ++i;
-          }
-          iDay = 1;
-          dDate -= 30;
-        } else {
-          iDay += static_cast<int32_t>(dDate) - 1;
-          dDate = 0;
-        }
-      } else {
-        if (dDate > 31) {
-          ++iMonth;
-          if (iMonth > 12) {
-            iMonth = 1;
-            ++i;
-          }
-          iDay = 1;
-          dDate -= 31;
-        } else {
-          iDay += static_cast<int32_t>(dDate) - 1;
-          dDate = 0;
-        }
-      }
-    }
-  }
-
-  ByteString szLocalDateString = IsoDate2Local(
-      pThis,
-      ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(),
-      formatString.AsStringView(), localString.AsStringView());
-  args.GetReturnValue()->SetString(szLocalDateString.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Num2GMTime(CFXJSE_Value* pThis,
-                                        const ByteStringView& szFuncName,
-                                        CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 3) {
-    ToJSContext(pThis, nullptr)
-        ->ThrowParamCountMismatchException(L"Num2GMTime");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (timeValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  int32_t iTime = (int32_t)ValueToFloat(pThis, timeValue.get());
-  if (abs(iTime) < 1.0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString formatString;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (formatValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    formatString = ValueToUTF8String(formatValue.get());
-  }
-
-  ByteString localString;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
-    if (localValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    localString = ValueToUTF8String(localValue.get());
-  }
-
-  ByteString szGMTTimeString =
-      Num2AllTime(pThis, iTime, formatString.AsStringView(),
-                  localString.AsStringView(), true);
-  args.GetReturnValue()->SetString(szGMTTimeString.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Num2Time(CFXJSE_Value* pThis,
-                                      const ByteStringView& szFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Num2Time");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (timeValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  float fTime = ValueToFloat(pThis, timeValue.get());
-  if (fabs(fTime) < 1.0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString formatString;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (formatValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    formatString = ValueToUTF8String(formatValue.get());
-  }
-
-  ByteString localString;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
-    if (localValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    localString = ValueToUTF8String(localValue.get());
-  }
-
-  ByteString szLocalTimeString = Num2AllTime(pThis, static_cast<int32_t>(fTime),
-                                             formatString.AsStringView(),
-                                             localString.AsStringView(), false);
-  args.GetReturnValue()->SetString(szLocalTimeString.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Time(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 0) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Time");
-    return;
-  }
-
-  time_t now;
-  time(&now);
-
-  struct tm* pGmt = gmtime(&now);
-  args.GetReturnValue()->SetInteger(
-      (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000);
-}
-
-// static
-void CFXJSE_FormCalcContext::Time2Num(CFXJSE_Value* pThis,
-                                      const ByteStringView& szFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Time2Num");
-    return;
-  }
-
-  ByteString timeString;
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, timeValue.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  timeString = ValueToUTF8String(timeValue.get());
-
-  ByteString formatString;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    formatString = ValueToUTF8String(formatValue.get());
-  }
-
-  ByteString localString;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localValue.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    localString = ValueToUTF8String(localValue.get());
-  }
-
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  IFX_Locale* pLocale = nullptr;
-  if (localString.IsEmpty()) {
-    CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-    ASSERT(pThisNode);
-    pLocale = pThisNode->GetLocale();
-  } else {
-    pLocale =
-        pMgr->GetLocaleByName(WideString::FromUTF8(localString.AsStringView()));
-  }
-
-  WideString wsFormat;
-  if (formatString.IsEmpty())
-    wsFormat = pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
-  else
-    wsFormat = WideString::FromUTF8(formatString.AsStringView());
-
-  wsFormat = L"time{" + wsFormat + L"}";
-  CXFA_LocaleValue localeValue(XFA_VT_TIME,
-                               WideString::FromUTF8(timeString.AsStringView()),
-                               wsFormat, pLocale, pMgr);
-  if (!localeValue.IsValid()) {
-    args.GetReturnValue()->SetInteger(0);
-    return;
-  }
-
-  CFX_DateTime uniTime = localeValue.GetTime();
-  int32_t hour = uniTime.GetHour();
-  int32_t min = uniTime.GetMinute();
-  int32_t second = uniTime.GetSecond();
-  int32_t milSecond = uniTime.GetMillisecond();
-  int32_t mins = hour * 60 + min;
-
-  mins -= (CXFA_TimeZoneProvider().GetTimeZone().tzHour * 60);
-  while (mins > 1440)
-    mins -= 1440;
-
-  while (mins < 0)
-    mins += 1440;
-
-  hour = mins / 60;
-  min = mins % 60;
-  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
-                                    second * 1000 + milSecond + 1);
-}
-
-// static
-void CFXJSE_FormCalcContext::TimeFmt(CFXJSE_Value* pThis,
-                                     const ByteStringView& szFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc > 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"TimeFmt");
-    return;
-  }
-
-  int32_t iStyle = 0;
-  if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
-    if (iStyle > 4 || iStyle < 0)
-      iStyle = 0;
-  }
-
-  ByteString szLocal;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
-    if (argLocal->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    szLocal = ValueToUTF8String(argLocal.get());
-  }
-
-  ByteString formatStr =
-      GetStandardTimeFormat(pThis, iStyle, szLocal.AsStringView());
-  args.GetReturnValue()->SetString(formatStr.AsStringView());
-}
-
-// static
-bool CFXJSE_FormCalcContext::IsIsoDateFormat(const char* pData,
-                                             int32_t iLength,
-                                             int32_t& iStyle,
-                                             int32_t& iYear,
-                                             int32_t& iMonth,
-                                             int32_t& iDay) {
-  iYear = 0;
-  iMonth = 1;
-  iDay = 1;
-
-  if (iLength < 4)
-    return false;
-
-  char strYear[5];
-  strYear[4] = '\0';
-  for (int32_t i = 0; i < 4; ++i) {
-    if (!std::isdigit(pData[i]))
-      return false;
-
-    strYear[i] = pData[i];
-  }
-  iYear = FXSYS_atoi(strYear);
-  iStyle = 0;
-  if (iLength == 4)
-    return true;
-
-  iStyle = pData[4] == '-' ? 1 : 0;
-
-  char strTemp[3];
-  strTemp[2] = '\0';
-  int32_t iPosOff = iStyle == 0 ? 4 : 5;
-  if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
-    return false;
-
-  strTemp[0] = pData[iPosOff];
-  strTemp[1] = pData[iPosOff + 1];
-  iMonth = FXSYS_atoi(strTemp);
-  if (iMonth > 12 || iMonth < 1)
-    return false;
-
-  if (iStyle == 0) {
-    iPosOff += 2;
-    if (iLength == 6)
-      return true;
-  } else {
-    iPosOff += 3;
-    if (iLength == 7)
-      return true;
-  }
-  if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
-    return false;
-
-  strTemp[0] = pData[iPosOff];
-  strTemp[1] = pData[iPosOff + 1];
-  iDay = FXSYS_atoi(strTemp);
-  if (iPosOff + 2 < iLength)
-    return false;
-
-  if ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) {
-    if (iMonth == 2 && iDay > 29)
-      return false;
-  } else {
-    if (iMonth == 2 && iDay > 28)
-      return false;
-  }
-  if (iMonth != 2) {
-    if (iMonth < 8) {
-      if (iDay > (iMonth % 2 == 0 ? 30 : 31))
-        return false;
-    } else if (iDay > (iMonth % 2 == 0 ? 31 : 30)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-// static
-bool CFXJSE_FormCalcContext::IsIsoTimeFormat(const char* pData,
-                                             int32_t iLength,
-                                             int32_t& iHour,
-                                             int32_t& iMinute,
-                                             int32_t& iSecond,
-                                             int32_t& iMilliSecond,
-                                             int32_t& iZoneHour,
-                                             int32_t& iZoneMinute) {
-  iHour = 0;
-  iMinute = 0;
-  iSecond = 0;
-  iMilliSecond = 0;
-  iZoneHour = 0;
-  iZoneMinute = 0;
-  if (!pData)
-    return false;
-
-  char strTemp[3];
-  strTemp[2] = '\0';
-  int32_t iZone = 0;
-  int32_t i = 0;
-  while (i < iLength) {
-    if (!std::isdigit(pData[i]) && pData[i] != ':') {
-      iZone = i;
-      break;
-    }
-    ++i;
-  }
-  if (i == iLength)
-    iZone = iLength;
-
-  int32_t iPos = 0;
-  int32_t iIndex = 0;
-  while (iIndex < iZone) {
-    if (!std::isdigit(pData[iIndex]))
-      return false;
-
-    strTemp[0] = pData[iIndex];
-    if (!std::isdigit(pData[iIndex + 1]))
-      return false;
-
-    strTemp[1] = pData[iIndex + 1];
-    if (FXSYS_atoi(strTemp) > 60)
-      return false;
-
-    if (pData[2] == ':') {
-      if (iPos == 0) {
-        iHour = FXSYS_atoi(strTemp);
-        ++iPos;
-      } else if (iPos == 1) {
-        iMinute = FXSYS_atoi(strTemp);
-        ++iPos;
-      } else {
-        iSecond = FXSYS_atoi(strTemp);
-      }
-      iIndex += 3;
-    } else {
-      if (iPos == 0) {
-        iHour = FXSYS_atoi(strTemp);
-        ++iPos;
-      } else if (iPos == 1) {
-        iMinute = FXSYS_atoi(strTemp);
-        ++iPos;
-      } else if (iPos == 2) {
-        iSecond = FXSYS_atoi(strTemp);
-        ++iPos;
-      }
-      iIndex += 2;
-    }
-  }
-
-  if (iIndex < iLength && pData[iIndex] == '.') {
-    constexpr int kSubSecondLength = 3;
-    if (iIndex + kSubSecondLength >= iLength)
-      return false;
-
-    ++iIndex;
-    char strSec[kSubSecondLength + 1];
-    for (int i = 0; i < kSubSecondLength; ++i) {
-      char c = pData[iIndex + i];
-      if (!std::isdigit(c))
-        return false;
-      strSec[i] = c;
-    }
-    strSec[kSubSecondLength] = '\0';
-
-    iMilliSecond = FXSYS_atoi(strSec);
-    if (iMilliSecond > 100) {
-      iMilliSecond = 0;
-      return false;
-    }
-    iIndex += kSubSecondLength;
-  }
-
-  if (iIndex < iLength && FXSYS_tolower(pData[iIndex]) == 'z')
-    return true;
-
-  int32_t iSign = 1;
-  if (iIndex < iLength) {
-    if (pData[iIndex] == '+') {
-      ++iIndex;
-    } else if (pData[iIndex] == '-') {
-      iSign = -1;
-      ++iIndex;
-    }
-  }
-  iPos = 0;
-  while (iIndex < iLength) {
-    if (!std::isdigit(pData[iIndex]))
-      return false;
-
-    strTemp[0] = pData[iIndex];
-    if (!std::isdigit(pData[iIndex + 1]))
-      return false;
-
-    strTemp[1] = pData[iIndex + 1];
-    if (FXSYS_atoi(strTemp) > 60)
-      return false;
-
-    if (pData[2] == ':') {
-      if (iPos == 0) {
-        iZoneHour = FXSYS_atoi(strTemp);
-      } else if (iPos == 1) {
-        iZoneMinute = FXSYS_atoi(strTemp);
-      }
-      iIndex += 3;
-    } else {
-      if (!iPos) {
-        iZoneHour = FXSYS_atoi(strTemp);
-        ++iPos;
-      } else if (iPos == 1) {
-        iZoneMinute = FXSYS_atoi(strTemp);
-        ++iPos;
-      }
-      iIndex += 2;
-    }
-  }
-  if (iIndex < iLength)
-    return false;
-
-  iZoneHour *= iSign;
-  return true;
-}
-
-// static
-bool CFXJSE_FormCalcContext::IsIsoDateTimeFormat(const char* pData,
-                                                 int32_t iLength,
-                                                 int32_t& iYear,
-                                                 int32_t& iMonth,
-                                                 int32_t& iDay,
-                                                 int32_t& iHour,
-                                                 int32_t& iMinute,
-                                                 int32_t& iSecond,
-                                                 int32_t& iMillionSecond,
-                                                 int32_t& iZoneHour,
-                                                 int32_t& iZoneMinute) {
-  iYear = 0;
-  iMonth = 0;
-  iDay = 0;
-  iHour = 0;
-  iMinute = 0;
-  iSecond = 0;
-  if (!pData)
-    return false;
-
-  int32_t iIndex = 0;
-  while (pData[iIndex] != 'T' && pData[iIndex] != 't') {
-    if (iIndex >= iLength)
-      return false;
-    ++iIndex;
-  }
-  if (iIndex != 8 && iIndex != 10)
-    return false;
-
-  int32_t iStyle = -1;
-  if (!IsIsoDateFormat(pData, iIndex, iStyle, iYear, iMonth, iDay))
-    return false;
-  if (pData[iIndex] != 'T' && pData[iIndex] != 't')
-    return true;
-
-  ++iIndex;
-  if (((iLength - iIndex > 13) && (iLength - iIndex < 6)) &&
-      (iLength - iIndex != 15)) {
-    return true;
-  }
-  return IsIsoTimeFormat(pData + iIndex, iLength - iIndex, iHour, iMinute,
-                         iSecond, iMillionSecond, iZoneHour, iZoneMinute);
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::Local2IsoDate(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szDate,
-    const ByteStringView& szFormat,
-    const ByteStringView& szLocale) {
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  if (!pDoc)
-    return ByteString();
-
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
-  if (!pLocale)
-    return ByteString();
-
-  WideString wsFormat = FormatFromString(pLocale, szFormat);
-  CFX_DateTime dt = CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(szDate),
-                                     wsFormat, pLocale, pMgr)
-                        .GetDate();
-
-  return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(),
-                            dt.GetDay());
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::IsoDate2Local(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szDate,
-    const ByteStringView& szFormat,
-    const ByteStringView& szLocale) {
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  if (!pDoc)
-    return ByteString();
-
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
-  if (!pLocale)
-    return ByteString();
-
-  WideString wsFormat = FormatFromString(pLocale, szFormat);
-  WideString wsRet;
-  CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(szDate), pMgr)
-      .FormatPatterns(wsRet, wsFormat, pLocale, XFA_VALUEPICTURE_Display);
-  return wsRet.UTF8Encode();
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::IsoTime2Local(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szTime,
-    const ByteStringView& szFormat,
-    const ByteStringView& szLocale) {
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  if (!pDoc)
-    return ByteString();
-
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
-  if (!pLocale)
-    return ByteString();
-
-  WideString wsFormat = {
-      L"time{", FormatFromString(pLocale, szFormat).AsStringView(), L"}"};
-  CXFA_LocaleValue widgetValue(XFA_VT_TIME, WideString::FromUTF8(szTime), pMgr);
-  WideString wsRet;
-  widgetValue.FormatPatterns(wsRet, wsFormat, pLocale,
-                             XFA_VALUEPICTURE_Display);
-  return wsRet.UTF8Encode();
-}
-
-// static
-int32_t CFXJSE_FormCalcContext::DateString2Num(
-    const ByteStringView& szDateString) {
-  int32_t iLength = szDateString.GetLength();
-  int32_t iYear = 0;
-  int32_t iMonth = 0;
-  int32_t iDay = 0;
-  if (iLength <= 10) {
-    int32_t iStyle = -1;
-    if (!IsIsoDateFormat(szDateString.unterminated_c_str(), iLength, iStyle,
-                         iYear, iMonth, iDay)) {
-      return 0;
-    }
-  } else {
-    int32_t iHour = 0;
-    int32_t iMinute = 0;
-    int32_t iSecond = 0;
-    int32_t iMilliSecond = 0;
-    int32_t iZoneHour = 0;
-    int32_t iZoneMinute = 0;
-    if (!IsIsoDateTimeFormat(szDateString.unterminated_c_str(), iLength, iYear,
-                             iMonth, iDay, iHour, iMinute, iSecond,
-                             iMilliSecond, iZoneHour, iZoneMinute)) {
-      return 0;
-    }
-  }
-
-  float dDays = 0;
-  int32_t i = 1;
-  if (iYear < 1900)
-    return 0;
-
-  while (iYear - i >= 1900) {
-    dDays +=
-        ((!((iYear - i) % 4) && ((iYear - i) % 100)) || !((iYear - i) % 400))
-            ? 366
-            : 365;
-    ++i;
-  }
-  i = 1;
-  while (i < iMonth) {
-    if (i == 2)
-      dDays += ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) ? 29 : 28;
-    else if (i <= 7)
-      dDays += (i % 2 == 0) ? 30 : 31;
-    else
-      dDays += (i % 2 == 0) ? 31 : 30;
-
-    ++i;
-  }
-  i = 0;
-  while (iDay - i > 0) {
-    dDays += 1;
-    ++i;
-  }
-  return (int32_t)dDays;
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(
-    CFXJSE_Value* pThis,
-    int32_t iStyle,
-    const ByteStringView& szLocale,
-    bool bStandard) {
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  if (!pDoc)
-    return ByteString();
-
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
-  if (!pLocale)
-    return ByteString();
-
-  WideString strRet = pLocale->GetDatePattern(SubCategoryFromInt(iStyle));
-  if (!bStandard) {
-    AlternateDateTimeSymbols(strRet, pLocale->GetDateTimeSymbols(),
-                             g_sAltTable_Date);
-  }
-  return strRet.UTF8Encode();
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(
-    CFXJSE_Value* pThis,
-    int32_t iStyle,
-    const ByteStringView& szLocale,
-    bool bStandard) {
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  if (!pDoc)
-    return ByteString();
-
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
-  if (!pLocale)
-    return ByteString();
-
-  WideString strRet = pLocale->GetTimePattern(SubCategoryFromInt(iStyle));
-  if (!bStandard) {
-    AlternateDateTimeSymbols(strRet, pLocale->GetDateTimeSymbols(),
-                             g_sAltTable_Time);
-  }
-  return strRet.UTF8Encode();
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::GetStandardDateFormat(
-    CFXJSE_Value* pThis,
-    int32_t iStyle,
-    const ByteStringView& szLocalStr) {
-  return GetLocalDateFormat(pThis, iStyle, szLocalStr, true);
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat(
-    CFXJSE_Value* pThis,
-    int32_t iStyle,
-    const ByteStringView& szLocalStr) {
-  return GetLocalTimeFormat(pThis, iStyle, szLocalStr, true);
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_Value* pThis,
-                                               int32_t iTime,
-                                               const ByteStringView& szFormat,
-                                               const ByteStringView& szLocale,
-                                               bool bGM) {
-  int32_t iHour = 0;
-  int32_t iMin = 0;
-  int32_t iSec = 0;
-  iHour = static_cast<int>(iTime) / 3600000;
-  iMin = (static_cast<int>(iTime) - iHour * 3600000) / 60000;
-  iSec = (static_cast<int>(iTime) - iHour * 3600000 - iMin * 60000) / 1000;
-
-  if (!bGM) {
-    int32_t iZoneHour = 0;
-    int32_t iZoneMin = 0;
-    int32_t iZoneSec = 0;
-    GetLocalTimeZone(iZoneHour, iZoneMin, iZoneSec);
-    iHour += iZoneHour;
-    iMin += iZoneMin;
-    iSec += iZoneSec;
-  }
-
-  return IsoTime2Local(
-      pThis,
-      ByteString::Format("%02d:%02d:%02d", iHour, iMin, iSec).AsStringView(),
-      szFormat, szLocale);
-}
-
-// static
-void CFXJSE_FormCalcContext::GetLocalTimeZone(int32_t& iHour,
-                                              int32_t& iMin,
-                                              int32_t& iSec) {
-  time_t now;
-  time(&now);
-
-  struct tm* pGmt = gmtime(&now);
-  struct tm* pLocal = localtime(&now);
-  iHour = pLocal->tm_hour - pGmt->tm_hour;
-  iMin = pLocal->tm_min - pGmt->tm_min;
-  iSec = pLocal->tm_sec - pGmt->tm_sec;
-}
-
-// static
-void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Apr");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double nPrincipal = ValueToDouble(pThis, argOne.get());
-  double nPayment = ValueToDouble(pThis, argTwo.get());
-  double nPeriods = ValueToDouble(pThis, argThree.get());
-  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods <= 0) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  double r = 2 * (nPeriods * nPayment - nPrincipal) / (nPeriods * nPrincipal);
-  double nTemp = 1;
-  for (int32_t i = 0; i < nPeriods; ++i)
-    nTemp *= (1 + r);
-
-  double nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
-  while (fabs(nRet) > kFinancialPrecision) {
-    double nDerivative =
-        ((nTemp + r * nPeriods * (nTemp / (1 + r))) * (nTemp - 1) -
-         (r * nTemp * nPeriods * (nTemp / (1 + r)))) /
-        ((nTemp - 1) * (nTemp - 1));
-    if (nDerivative == 0) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-
-    r = r - nRet / nDerivative;
-    nTemp = 1;
-    for (int32_t i = 0; i < nPeriods; ++i) {
-      nTemp *= (1 + r);
-    }
-    nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
-  }
-  args.GetReturnValue()->SetDouble(r * 12);
-}
-
-// static
-void CFXJSE_FormCalcContext::CTerm(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"CTerm");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float nRate = ValueToFloat(pThis, argOne.get());
-  float nFutureValue = ValueToFloat(pThis, argTwo.get());
-  float nInitAmount = ValueToFloat(pThis, argThree.get());
-  if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  args.GetReturnValue()->SetFloat(log((float)(nFutureValue / nInitAmount)) /
-                                  log((float)(1 + nRate)));
-}
-
-// static
-void CFXJSE_FormCalcContext::FV(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"FV");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double nAmount = ValueToDouble(pThis, argOne.get());
-  double nRate = ValueToDouble(pThis, argTwo.get());
-  double nPeriod = ValueToDouble(pThis, argThree.get());
-  if ((nRate < 0) || (nPeriod <= 0) || (nAmount <= 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  double dResult = 0;
-  if (nRate) {
-    double nTemp = 1;
-    for (int i = 0; i < nPeriod; ++i) {
-      nTemp *= 1 + nRate;
-    }
-    dResult = nAmount * (nTemp - 1) / nRate;
-  } else {
-    dResult = nAmount * nPeriod;
-  }
-
-  args.GetReturnValue()->SetDouble(dResult);
-}
-
-// static
-void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 5) {
-    pContext->ThrowParamCountMismatchException(L"IPmt");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
-      ValueIsNull(pThis, argFive.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPayment = ValueToFloat(pThis, argThree.get());
-  float nFirstMonth = ValueToFloat(pThis, argFour.get());
-  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
-  if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
-      (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  float nRateOfMonth = nRate / 12;
-  int32_t iNums =
-      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
-                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
-                log10((float)(1 + nRateOfMonth)));
-  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
-
-  if (nPayment < nPrincipalAmount * nRateOfMonth) {
-    args.GetReturnValue()->SetFloat(0);
-    return;
-  }
-
-  int32_t i = 0;
-  for (i = 0; i < nFirstMonth - 1; ++i)
-    nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
-
-  float nSum = 0;
-  for (; i < iEnd; ++i) {
-    nSum += nPrincipalAmount * nRateOfMonth;
-    nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
-  }
-  args.GetReturnValue()->SetFloat(nSum);
-}
-
-// static
-void CFXJSE_FormCalcContext::NPV(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  int32_t argc = args.GetLength();
-  if (argc < 3) {
-    pContext->ThrowParamCountMismatchException(L"NPV");
-    return;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
-  for (int32_t i = 0; i < argc; i++) {
-    argValues.push_back(GetSimpleValue(pThis, args, i));
-    if (ValueIsNull(pThis, argValues[i].get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-  }
-
-  double nRate = ValueToDouble(pThis, argValues[0].get());
-  if (nRate <= 0) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  std::vector<double> data(argc - 1);
-  for (int32_t i = 1; i < argc; i++)
-    data.push_back(ValueToDouble(pThis, argValues[i].get()));
-
-  double nSum = 0;
-  int32_t iIndex = 0;
-  for (int32_t i = 0; i < argc - 1; i++) {
-    double nTemp = 1;
-    for (int32_t j = 0; j <= i; j++)
-      nTemp *= 1 + nRate;
-
-    double nNum = data[iIndex++];
-    nSum += nNum / nTemp;
-  }
-  args.GetReturnValue()->SetDouble(nSum);
-}
-
-// static
-void CFXJSE_FormCalcContext::Pmt(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Pmt");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float nPrincipal = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPeriods = ValueToFloat(pThis, argThree.get());
-  if ((nPrincipal <= 0) || (nRate <= 0) || (nPeriods <= 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  float nTmp = 1 + nRate;
-  float nSum = nTmp;
-  for (int32_t i = 0; i < nPeriods - 1; ++i)
-    nSum *= nTmp;
-
-  args.GetReturnValue()->SetFloat((nPrincipal * nRate * nSum) / (nSum - 1));
-}
-
-// static
-void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 5) {
-    pContext->ThrowParamCountMismatchException(L"PPmt");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
-      ValueIsNull(pThis, argFive.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPayment = ValueToFloat(pThis, argThree.get());
-  float nFirstMonth = ValueToFloat(pThis, argFour.get());
-  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
-  if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
-      (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  float nRateOfMonth = nRate / 12;
-  int32_t iNums =
-      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
-                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
-                log10((float)(1 + nRateOfMonth)));
-  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
-  if (nPayment < nPrincipalAmount * nRateOfMonth) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  int32_t i = 0;
-  for (i = 0; i < nFirstMonth - 1; ++i)
-    nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
-
-  float nTemp = 0;
-  float nSum = 0;
-  for (; i < iEnd; ++i) {
-    nTemp = nPayment - nPrincipalAmount * nRateOfMonth;
-    nSum += nTemp;
-    nPrincipalAmount -= nTemp;
-  }
-  args.GetReturnValue()->SetFloat(nSum);
-}
-
-// static
-void CFXJSE_FormCalcContext::PV(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"PV");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double nAmount = ValueToDouble(pThis, argOne.get());
-  double nRate = ValueToDouble(pThis, argTwo.get());
-  double nPeriod = ValueToDouble(pThis, argThree.get());
-  if ((nAmount <= 0) || (nRate < 0) || (nPeriod <= 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  double nTemp = 1;
-  for (int32_t i = 0; i < nPeriod; ++i)
-    nTemp *= 1 + nRate;
-
-  nTemp = 1 / nTemp;
-  args.GetReturnValue()->SetDouble(nAmount * ((1 - nTemp) / nRate));
-}
-
-// static
-void CFXJSE_FormCalcContext::Rate(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Rate");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float nFuture = ValueToFloat(pThis, argOne.get());
-  float nPresent = ValueToFloat(pThis, argTwo.get());
-  float nTotalNumber = ValueToFloat(pThis, argThree.get());
-  if ((nFuture <= 0) || (nPresent < 0) || (nTotalNumber <= 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  args.GetReturnValue()->SetFloat(
-      FXSYS_pow((float)(nFuture / nPresent), (float)(1 / nTotalNumber)) - 1);
-}
-
-// static
-void CFXJSE_FormCalcContext::Term(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Term");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float nMount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nFuture = ValueToFloat(pThis, argThree.get());
-  if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  args.GetReturnValue()->SetFloat(log((float)(nFuture / nMount * nRate) + 1) /
-                                  log((float)(1 + nRate)));
-}
-
-// static
-void CFXJSE_FormCalcContext::Choose(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  int32_t argc = args.GetLength();
-  if (argc < 2) {
-    pContext->ThrowParamCountMismatchException(L"Choose");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  int32_t iIndex = (int32_t)ValueToFloat(pThis, argOne.get());
-  if (iIndex < 1) {
-    args.GetReturnValue()->SetString("");
-    return;
-  }
-
-  bool bFound = false;
-  bool bStopCounterFlags = false;
-  int32_t iArgIndex = 1;
-  int32_t iValueIndex = 0;
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) {
-    std::unique_ptr<CFXJSE_Value> argIndexValue = args.GetValue(iArgIndex);
-    if (argIndexValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argIndexValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength > 3)
-        bStopCounterFlags = true;
-
-      iValueIndex += (iLength - 2);
-      if (iValueIndex >= iIndex) {
-        auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        argIndexValue->GetObjectPropertyByIdx(1, propertyValue.get());
-        argIndexValue->GetObjectPropertyByIdx(
-            (iLength - 1) - (iValueIndex - iIndex), jsObjectValue.get());
-        if (propertyValue->IsNull()) {
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-        } else {
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-        }
-        ByteString bsChosen = ValueToUTF8String(newPropertyValue.get());
-        args.GetReturnValue()->SetString(bsChosen.AsStringView());
-        bFound = true;
-      }
-    } else {
-      iValueIndex++;
-      if (iValueIndex == iIndex) {
-        ByteString bsChosen = ValueToUTF8String(argIndexValue.get());
-        args.GetReturnValue()->SetString(bsChosen.AsStringView());
-        bFound = true;
-      }
-    }
-    iArgIndex++;
-  }
-  if (!bFound)
-    args.GetReturnValue()->SetString("");
-}
-
-// static
-void CFXJSE_FormCalcContext::Exists(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Exists");
-    return;
-  }
-  args.GetReturnValue()->SetInteger(args.GetValue(0)->IsObject());
-}
-
-// static
-void CFXJSE_FormCalcContext::HasValue(CFXJSE_Value* pThis,
-                                      const ByteStringView& szFuncName,
-                                      CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"HasValue");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (!argOne->IsString()) {
-    args.GetReturnValue()->SetInteger(argOne->IsNumber() ||
-                                      argOne->IsBoolean());
-    return;
-  }
-
-  ByteString valueStr = argOne->ToString();
-  valueStr.TrimLeft();
-  args.GetReturnValue()->SetInteger(!valueStr.IsEmpty());
-}
-
-// static
-void CFXJSE_FormCalcContext::Oneof(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() < 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Oneof");
-    return;
-  }
-
-  bool bFlags = false;
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::vector<std::unique_ptr<CFXJSE_Value>> parameterValues;
-  unfoldArgs(pThis, args, &parameterValues, 1);
-  for (const auto& value : parameterValues) {
-    if (simpleValueCompare(pThis, argOne.get(), value.get())) {
-      bFlags = true;
-      break;
-    }
-  }
-
-  args.GetReturnValue()->SetInteger(bFlags);
-}
-
-// static
-void CFXJSE_FormCalcContext::Within(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Within");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetUndefined();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argLow = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argHigh = GetSimpleValue(pThis, args, 2);
-  if (argOne->IsNumber()) {
-    float oneNumber = ValueToFloat(pThis, argOne.get());
-    float lowNumber = ValueToFloat(pThis, argLow.get());
-    float heightNumber = ValueToFloat(pThis, argHigh.get());
-    args.GetReturnValue()->SetInteger((oneNumber >= lowNumber) &&
-                                      (oneNumber <= heightNumber));
-    return;
-  }
-
-  ByteString oneString = ValueToUTF8String(argOne.get());
-  ByteString lowString = ValueToUTF8String(argLow.get());
-  ByteString heightString = ValueToUTF8String(argHigh.get());
-  args.GetReturnValue()->SetInteger(
-      (oneString.Compare(lowString.AsStringView()) >= 0) &&
-      (oneString.Compare(heightString.AsStringView()) <= 0));
-}
-
-// static
-void CFXJSE_FormCalcContext::If(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"If");
-    return;
-  }
-
-  args.GetReturnValue()->Assign(GetSimpleValue(pThis, args, 0)->ToBoolean()
-                                    ? GetSimpleValue(pThis, args, 1).get()
-                                    : GetSimpleValue(pThis, args, 2).get());
-}
-
-// static
-void CFXJSE_FormCalcContext::Eval(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Eval");
-    return;
-  }
-
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  std::unique_ptr<CFXJSE_Value> scriptValue = GetSimpleValue(pThis, args, 0);
-  ByteString utf8ScriptString = ValueToUTF8String(scriptValue.get());
-  if (utf8ScriptString.IsEmpty()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  CFX_WideTextBuf wsJavaScriptBuf;
-  if (!CFXJSE_FormCalcContext::Translate(
-          WideString::FromUTF8(utf8ScriptString.AsStringView()).AsStringView(),
-          &wsJavaScriptBuf)) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Context> pNewContext(
-      CFXJSE_Context::Create(pIsolate, nullptr, nullptr));
-
-  auto returnValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  pNewContext->ExecuteScript(
-      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).c_str(), returnValue.get());
-
-  args.GetReturnValue()->Assign(returnValue.get());
-}
-
-// static
-void CFXJSE_FormCalcContext::Ref(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Ref");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray() && !argOne->IsObject() && !argOne->IsBoolean() &&
-      !argOne->IsString() && !argOne->IsNull() && !argOne->IsNumber()) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  if (argOne->IsBoolean() || argOne->IsString() || argOne->IsNumber()) {
-    args.GetReturnValue()->Assign(argOne.get());
-    return;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> values;
-  for (int32_t i = 0; i < 3; i++)
-    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int intVal = 3;
-  if (argOne->IsNull()) {
-    // TODO(dsinclair): Why is this 4 when the others are all 3?
-    intVal = 4;
-    values[2]->SetNull();
-  } else if (argOne->IsArray()) {
-#ifndef NDEBUG
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectProperty("length", lengthValue.get());
-    ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
-    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (!propertyValue->IsNull() || jsObjectValue->IsNull()) {
-      pContext->ThrowArgumentMismatchException();
-      return;
-    }
-
-    values[2]->Assign(jsObjectValue.get());
-  } else if (argOne->IsObject()) {
-    values[2]->Assign(argOne.get());
-  }
-
-  values[0]->SetInteger(intVal);
-  values[1]->SetNull();
-  args.GetReturnValue()->SetArray(values);
-}
-
-// static
-void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis,
-                                      const ByteStringView& szFuncName,
-                                      CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"UnitType");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
-  if (unitspanValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString unitspanString = ValueToUTF8String(unitspanValue.get());
-  if (unitspanString.IsEmpty()) {
-    args.GetReturnValue()->SetString("in");
-    return;
-  }
-
-  enum XFA_FM2JS_VALUETYPE_ParserStatus {
-    VALUETYPE_START,
-    VALUETYPE_HAVEINVALIDCHAR,
-    VALUETYPE_HAVEDIGIT,
-    VALUETYPE_HAVEDIGITWHITE,
-    VALUETYPE_ISCM,
-    VALUETYPE_ISMM,
-    VALUETYPE_ISPT,
-    VALUETYPE_ISMP,
-    VALUETYPE_ISIN,
-  };
-  unitspanString.MakeLower();
-  WideString wsTypeString = WideString::FromUTF8(unitspanString.AsStringView());
-  const wchar_t* pData = wsTypeString.c_str();
-  int32_t u = 0;
-  int32_t uLen = wsTypeString.GetLength();
-  while (IsWhitespace(pData[u]))
-    u++;
-
-  XFA_FM2JS_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
-  wchar_t typeChar;
-  // TODO(dsinclair): Cleanup this parser, figure out what the various checks
-  //    are for.
-  while (u < uLen) {
-    typeChar = pData[u];
-    if (IsWhitespace(typeChar)) {
-      if (eParserStatus != VALUETYPE_HAVEDIGIT &&
-          eParserStatus != VALUETYPE_HAVEDIGITWHITE) {
-        eParserStatus = VALUETYPE_ISIN;
-        break;
-      }
-      eParserStatus = VALUETYPE_HAVEDIGITWHITE;
-    } else if (IsPartOfNumberW(typeChar)) {
-      if (eParserStatus == VALUETYPE_HAVEDIGITWHITE) {
-        eParserStatus = VALUETYPE_ISIN;
-        break;
-      }
-      eParserStatus = VALUETYPE_HAVEDIGIT;
-    } else if ((typeChar == 'c' || typeChar == 'p') && (u + 1 < uLen)) {
-      wchar_t nextChar = pData[u + 1];
-      if ((eParserStatus == VALUETYPE_START ||
-           eParserStatus == VALUETYPE_HAVEDIGIT ||
-           eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
-          !IsPartOfNumberW(nextChar)) {
-        eParserStatus = (typeChar == 'c') ? VALUETYPE_ISCM : VALUETYPE_ISPT;
-        break;
-      }
-      eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
-    } else if (typeChar == 'm' && (u + 1 < uLen)) {
-      wchar_t nextChar = pData[u + 1];
-      if ((eParserStatus == VALUETYPE_START ||
-           eParserStatus == VALUETYPE_HAVEDIGIT ||
-           eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
-          !IsPartOfNumberW(nextChar)) {
-        eParserStatus = VALUETYPE_ISMM;
-        if (nextChar == 'p' || ((u + 5 < uLen) && pData[u + 1] == 'i' &&
-                                pData[u + 2] == 'l' && pData[u + 3] == 'l' &&
-                                pData[u + 4] == 'i' && pData[u + 5] == 'p')) {
-          eParserStatus = VALUETYPE_ISMP;
-        }
-        break;
-      }
-    } else {
-      eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
-    }
-    u++;
-  }
-  switch (eParserStatus) {
-    case VALUETYPE_ISCM:
-      args.GetReturnValue()->SetString("cm");
-      break;
-    case VALUETYPE_ISMM:
-      args.GetReturnValue()->SetString("mm");
-      break;
-    case VALUETYPE_ISPT:
-      args.GetReturnValue()->SetString("pt");
-      break;
-    case VALUETYPE_ISMP:
-      args.GetReturnValue()->SetString("mp");
-      break;
-    default:
-      args.GetReturnValue()->SetString("in");
-      break;
-  }
-}
-
-// static
-void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis,
-                                       const ByteStringView& szFuncName,
-                                       CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"UnitValue");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
-  if (unitspanValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString unitspanString = ValueToUTF8String(unitspanValue.get());
-  const char* pData = unitspanString.c_str();
-  if (!pData) {
-    args.GetReturnValue()->SetInteger(0);
-    return;
-  }
-
-  size_t u = 0;
-  while (IsWhitespace(pData[u]))
-    ++u;
-
-  while (u < unitspanString.GetLength()) {
-    if (!IsPartOfNumber(pData[u]))
-      break;
-    ++u;
-  }
-
-  char* pTemp = nullptr;
-  double dFirstNumber = strtod(pData, &pTemp);
-  while (IsWhitespace(pData[u]))
-    ++u;
-
-  size_t uLen = unitspanString.GetLength();
-  ByteString strFirstUnit;
-  while (u < uLen) {
-    if (pData[u] == ' ')
-      break;
-
-    strFirstUnit += pData[u];
-    ++u;
-  }
-  strFirstUnit.MakeLower();
-
-  ByteString strUnit;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> unitValue = GetSimpleValue(pThis, args, 1);
-    ByteString unitTempString = ValueToUTF8String(unitValue.get());
-    const char* pChar = unitTempString.c_str();
-    size_t uVal = 0;
-    while (IsWhitespace(pChar[uVal]))
-      ++uVal;
-
-    while (uVal < unitTempString.GetLength()) {
-      if (!std::isdigit(pChar[uVal]) && pChar[uVal] != '.')
-        break;
-      ++uVal;
-    }
-    while (IsWhitespace(pChar[uVal]))
-      ++uVal;
-
-    size_t uValLen = unitTempString.GetLength();
-    while (uVal < uValLen) {
-      if (pChar[uVal] == ' ')
-        break;
-
-      strUnit += pChar[uVal];
-      ++uVal;
-    }
-    strUnit.MakeLower();
-  } else {
-    strUnit = strFirstUnit;
-  }
-
-  double dResult = 0;
-  if (strFirstUnit == "in" || strFirstUnit == "inches") {
-    if (strUnit == "mm" || strUnit == "millimeters")
-      dResult = dFirstNumber * 25.4;
-    else if (strUnit == "cm" || strUnit == "centimeters")
-      dResult = dFirstNumber * 2.54;
-    else if (strUnit == "pt" || strUnit == "points")
-      dResult = dFirstNumber / 72;
-    else if (strUnit == "mp" || strUnit == "millipoints")
-      dResult = dFirstNumber / 72000;
-    else
-      dResult = dFirstNumber;
-  } else if (strFirstUnit == "mm" || strFirstUnit == "millimeters") {
-    if (strUnit == "mm" || strUnit == "millimeters")
-      dResult = dFirstNumber;
-    else if (strUnit == "cm" || strUnit == "centimeters")
-      dResult = dFirstNumber / 10;
-    else if (strUnit == "pt" || strUnit == "points")
-      dResult = dFirstNumber / 25.4 / 72;
-    else if (strUnit == "mp" || strUnit == "millipoints")
-      dResult = dFirstNumber / 25.4 / 72000;
-    else
-      dResult = dFirstNumber / 25.4;
-  } else if (strFirstUnit == "cm" || strFirstUnit == "centimeters") {
-    if (strUnit == "mm" || strUnit == "millimeters")
-      dResult = dFirstNumber * 10;
-    else if (strUnit == "cm" || strUnit == "centimeters")
-      dResult = dFirstNumber;
-    else if (strUnit == "pt" || strUnit == "points")
-      dResult = dFirstNumber / 2.54 / 72;
-    else if (strUnit == "mp" || strUnit == "millipoints")
-      dResult = dFirstNumber / 2.54 / 72000;
-    else
-      dResult = dFirstNumber / 2.54;
-  } else if (strFirstUnit == "pt" || strFirstUnit == "points") {
-    if (strUnit == "mm" || strUnit == "millimeters")
-      dResult = dFirstNumber / 72 * 25.4;
-    else if (strUnit == "cm" || strUnit == "centimeters")
-      dResult = dFirstNumber / 72 * 2.54;
-    else if (strUnit == "pt" || strUnit == "points")
-      dResult = dFirstNumber;
-    else if (strUnit == "mp" || strUnit == "millipoints")
-      dResult = dFirstNumber * 1000;
-    else
-      dResult = dFirstNumber / 72;
-  } else if (strFirstUnit == "mp" || strFirstUnit == "millipoints") {
-    if (strUnit == "mm" || strUnit == "millimeters")
-      dResult = dFirstNumber / 72000 * 25.4;
-    else if (strUnit == "cm" || strUnit == "centimeters")
-      dResult = dFirstNumber / 72000 * 2.54;
-    else if (strUnit == "pt" || strUnit == "points")
-      dResult = dFirstNumber / 1000;
-    else if (strUnit == "mp" || strUnit == "millipoints")
-      dResult = dFirstNumber;
-    else
-      dResult = dFirstNumber / 72000;
-  }
-  args.GetReturnValue()->SetDouble(dResult);
-}
-
-// static
-void CFXJSE_FormCalcContext::At(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"At");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString stringTwo = ValueToUTF8String(argTwo.get());
-  if (stringTwo.IsEmpty()) {
-    args.GetReturnValue()->SetInteger(1);
-    return;
-  }
-
-  ByteString stringOne = ValueToUTF8String(argOne.get());
-  auto pos = stringOne.Find(stringTwo.AsStringView());
-  args.GetReturnValue()->SetInteger(pos.has_value() ? pos.value() + 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::Concat(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Concat");
-    return;
-  }
-
-  ByteString resultString;
-  bool bAllNull = true;
-  for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> value = GetSimpleValue(pThis, args, i);
-    if (ValueIsNull(pThis, value.get()))
-      continue;
-
-    bAllNull = false;
-    resultString += ValueToUTF8String(value.get());
-  }
-
-  if (bAllNull) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  args.GetReturnValue()->SetString(resultString.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Decode(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Decode");
-    return;
-  }
-
-  if (argc == 1) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    if (ValueIsNull(pThis, argOne.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-
-    WideString decoded = DecodeURL(
-        WideString::FromUTF8(ValueToUTF8String(argOne.get()).AsStringView()));
-
-    args.GetReturnValue()->SetString(
-        FX_UTF8Encode(decoded.AsStringView()).AsStringView());
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString toDecodeString = ValueToUTF8String(argOne.get());
-  ByteString identifyString = ValueToUTF8String(argTwo.get());
-  WideString decoded;
-
-  WideString toDecodeWideString =
-      WideString::FromUTF8(toDecodeString.AsStringView());
-
-  if (identifyString.EqualNoCase("html"))
-    decoded = DecodeHTML(toDecodeWideString);
-  else if (identifyString.EqualNoCase("xml"))
-    decoded = DecodeXML(toDecodeWideString);
-  else
-    decoded = DecodeURL(toDecodeWideString);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(decoded.AsStringView()).AsStringView());
-}
-
-// static
-WideString CFXJSE_FormCalcContext::DecodeURL(const WideString& wsURLString) {
-  const wchar_t* pData = wsURLString.c_str();
-  size_t i = 0;
-  CFX_WideTextBuf wsResultBuf;
-  while (i < wsURLString.GetLength()) {
-    wchar_t ch = pData[i];
-    if ('%' != ch) {
-      wsResultBuf.AppendChar(ch);
-      ++i;
-      continue;
-    }
-
-    wchar_t chTemp = 0;
-    int32_t iCount = 0;
-    while (iCount < 2) {
-      ++i;
-      ch = pData[i];
-      if (ch <= '9' && ch >= '0') {
-        // TODO(dsinclair): Premultiply and add rather then scale.
-        chTemp += (ch - '0') * (!iCount ? 16 : 1);
-      } else if (ch <= 'F' && ch >= 'A') {
-        chTemp += (ch - 'A' + 10) * (!iCount ? 16 : 1);
-      } else if (ch <= 'f' && ch >= 'a') {
-        chTemp += (ch - 'a' + 10) * (!iCount ? 16 : 1);
-      } else {
-        return WideString();
-      }
-      ++iCount;
-    }
-    wsResultBuf.AppendChar(chTemp);
-    ++i;
-  }
-  wsResultBuf.AppendChar(0);
-  return wsResultBuf.MakeString();
-}
-
-// static
-WideString CFXJSE_FormCalcContext::DecodeHTML(const WideString& wsHTMLString) {
-  wchar_t strString[9];
-  size_t iStrIndex = 0;
-  size_t iLen = wsHTMLString.GetLength();
-  size_t i = 0;
-  int32_t iCode = 0;
-  const wchar_t* pData = wsHTMLString.c_str();
-  CFX_WideTextBuf wsResultBuf;
-  while (i < iLen) {
-    wchar_t ch = pData[i];
-    if (ch != '&') {
-      wsResultBuf.AppendChar(ch);
-      ++i;
-      continue;
-    }
-
-    ++i;
-    ch = pData[i];
-    if (ch == '#') {
-      ++i;
-      ch = pData[i];
-      if (ch != 'x' && ch != 'X') {
-        return WideString();
-      }
-
-      ++i;
-      ch = pData[i];
-      if ((ch >= '0' && ch <= '9') || (ch <= 'f' && ch >= 'a') ||
-          (ch <= 'F' && ch >= 'A')) {
-        while (ch != ';' && i < iLen) {
-          if (ch >= '0' && ch <= '9') {
-            iCode += ch - '0';
-          } else if (ch <= 'f' && ch >= 'a') {
-            iCode += ch - 'a' + 10;
-          } else if (ch <= 'F' && ch >= 'A') {
-            iCode += ch - 'A' + 10;
-          } else {
-            return WideString();
-          }
-          ++i;
-          // TODO(dsinclair): Postmultiply seems wrong, start at zero
-          //   and pre-multiply then can remove the post divide.
-          iCode *= 16;
-          ch = pData[i];
-        }
-        iCode /= 16;
-      }
-    } else {
-      while (ch != ';' && i < iLen) {
-        strString[iStrIndex++] = ch;
-        ++i;
-        ch = pData[i];
-      }
-      strString[iStrIndex] = 0;
-    }
-    uint32_t iData = 0;
-    if (HTMLSTR2Code(strString, &iData)) {
-      wsResultBuf.AppendChar((wchar_t)iData);
-    } else {
-      wsResultBuf.AppendChar(iCode);
-    }
-    iStrIndex = 0;
-    strString[iStrIndex] = 0;
-    ++i;
-  }
-  wsResultBuf.AppendChar(0);
-
-  return wsResultBuf.MakeString();
-}
-
-// static
-WideString CFXJSE_FormCalcContext::DecodeXML(const WideString& wsXMLString) {
-  wchar_t strString[9];
-  int32_t iStrIndex = 0;
-  int32_t iLen = wsXMLString.GetLength();
-  int32_t i = 0;
-  int32_t iCode = 0;
-  wchar_t ch = 0;
-  const wchar_t* pData = wsXMLString.c_str();
-  CFX_WideTextBuf wsResultBuf;
-  while (i < iLen) {
-    ch = pData[i];
-    if (ch != '&') {
-      wsResultBuf.AppendChar(ch);
-      ++i;
-      continue;
-    }
-
-    // TODO(dsinclair): This is very similar to DecodeHTML, can they be
-    //   combined?
-    ++i;
-    ch = pData[i];
-    if (ch == '#') {
-      ++i;
-      ch = pData[i];
-      if (ch != 'x' && ch != 'X') {
-        return WideString();
-      }
-
-      ++i;
-      ch = pData[i];
-      if ((ch >= '0' && ch <= '9') || (ch <= 'f' && ch >= 'a') ||
-          (ch <= 'F' && ch >= 'A')) {
-        while (ch != ';') {
-          if (ch >= '0' && ch <= '9') {
-            iCode += ch - '0';
-          } else if (ch <= 'f' && ch >= 'a') {
-            iCode += ch - 'a' + 10;
-          } else if (ch <= 'F' && ch >= 'A') {
-            iCode += ch - 'A' + 10;
-          } else {
-            return WideString();
-          }
-          ++i;
-          iCode *= 16;
-          ch = pData[i];
-        }
-        iCode /= 16;
-      }
-    } else {
-      while (ch != ';' && i < iLen) {
-        strString[iStrIndex++] = ch;
-        ++i;
-        ch = pData[i];
-      }
-      strString[iStrIndex] = 0;
-    }
-
-    const wchar_t* const strName[] = {L"quot", L"amp", L"apos", L"lt", L"gt"};
-    int32_t iIndex = 0;
-    while (iIndex < 5) {
-      if (memcmp(strString, strName[iIndex], wcslen(strName[iIndex])) == 0) {
-        break;
-      }
-      ++iIndex;
-    }
-    switch (iIndex) {
-      case 0:
-        wsResultBuf.AppendChar('"');
-        break;
-      case 1:
-        wsResultBuf.AppendChar('&');
-        break;
-      case 2:
-        wsResultBuf.AppendChar('\'');
-        break;
-      case 3:
-        wsResultBuf.AppendChar('<');
-        break;
-      case 4:
-        wsResultBuf.AppendChar('>');
-        break;
-      default:
-        wsResultBuf.AppendChar(iCode);
-        break;
-    }
-    iStrIndex = 0;
-    strString[iStrIndex] = 0;
-    ++i;
-    iCode = 0;
-  }
-  wsResultBuf.AppendChar(0);
-  return wsResultBuf.MakeString();
-}
-
-// static
-void CFXJSE_FormCalcContext::Encode(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Encode");
-    return;
-  }
-
-  if (argc == 1) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    if (ValueIsNull(pThis, argOne.get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-
-    WideString encoded = EncodeURL(ValueToUTF8String(argOne.get()));
-    args.GetReturnValue()->SetString(
-        FX_UTF8Encode(encoded.AsStringView()).AsStringView());
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString toEncodeString = ValueToUTF8String(argOne.get());
-  ByteString identifyString = ValueToUTF8String(argTwo.get());
-  WideString encoded;
-  if (identifyString.EqualNoCase("html"))
-    encoded = EncodeHTML(toEncodeString);
-  else if (identifyString.EqualNoCase("xml"))
-    encoded = EncodeXML(toEncodeString);
-  else
-    encoded = EncodeURL(toEncodeString);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(encoded.AsStringView()).AsStringView());
-}
-
-// static
-WideString CFXJSE_FormCalcContext::EncodeURL(const ByteString& szURLString) {
-  WideString wsURLString = WideString::FromUTF8(szURLString.AsStringView());
-  CFX_WideTextBuf wsResultBuf;
-  wchar_t strEncode[4];
-  strEncode[0] = '%';
-  strEncode[3] = 0;
-  wchar_t strUnsafe[] = {' ', '<',  '>', '"', '#', '%', '{', '}',
-                         '|', '\\', '^', '~', '[', ']', '`'};
-  wchar_t strReserved[] = {';', '/', '?', ':', '@', '=', '&'};
-  wchar_t strSpecial[] = {'$', '-', '+', '!', '*', '\'', '(', ')', ','};
-  const wchar_t* strCode = L"0123456789abcdef";
-  for (auto ch : wsURLString) {
-    int32_t i = 0;
-    int32_t iCount = FX_ArraySize(strUnsafe);
-    while (i < iCount) {
-      if (ch == strUnsafe[i]) {
-        int32_t iIndex = ch / 16;
-        strEncode[1] = strCode[iIndex];
-        strEncode[2] = strCode[ch - iIndex * 16];
-        wsResultBuf << strEncode;
-        break;
-      }
-      ++i;
-    }
-    if (i < iCount)
-      continue;
-
-    i = 0;
-    iCount = FX_ArraySize(strReserved);
-    while (i < iCount) {
-      if (ch == strReserved[i]) {
-        int32_t iIndex = ch / 16;
-        strEncode[1] = strCode[iIndex];
-        strEncode[2] = strCode[ch - iIndex * 16];
-        wsResultBuf << strEncode;
-        break;
-      }
-      ++i;
-    }
-    if (i < iCount)
-      continue;
-
-    i = 0;
-    iCount = FX_ArraySize(strSpecial);
-    while (i < iCount) {
-      if (ch == strSpecial[i]) {
-        wsResultBuf.AppendChar(ch);
-        break;
-      }
-      ++i;
-    }
-    if (i < iCount)
-      continue;
-
-    if ((ch >= 0x80 && ch <= 0xff) || ch <= 0x1f || ch == 0x7f) {
-      int32_t iIndex = ch / 16;
-      strEncode[1] = strCode[iIndex];
-      strEncode[2] = strCode[ch - iIndex * 16];
-      wsResultBuf << strEncode;
-    } else if (ch >= 0x20 && ch <= 0x7e) {
-      wsResultBuf.AppendChar(ch);
-    } else {
-      const wchar_t iRadix = 16;
-      WideString strTmp;
-      while (ch >= iRadix) {
-        wchar_t tmp = strCode[ch % iRadix];
-        ch /= iRadix;
-        strTmp += tmp;
-      }
-      strTmp += strCode[ch];
-      int32_t iLen = strTmp.GetLength();
-      if (iLen < 2)
-        break;
-
-      int32_t iIndex = 0;
-      if (iLen % 2 != 0) {
-        strEncode[1] = '0';
-        strEncode[2] = strTmp[iLen - 1];
-        iIndex = iLen - 2;
-      } else {
-        strEncode[1] = strTmp[iLen - 1];
-        strEncode[2] = strTmp[iLen - 2];
-        iIndex = iLen - 3;
-      }
-      wsResultBuf << strEncode;
-      while (iIndex > 0) {
-        strEncode[1] = strTmp[iIndex];
-        strEncode[2] = strTmp[iIndex - 1];
-        iIndex -= 2;
-        wsResultBuf << strEncode;
-      }
-    }
-  }
-  wsResultBuf.AppendChar(0);
-  return wsResultBuf.MakeString();
-}
-
-// static
-WideString CFXJSE_FormCalcContext::EncodeHTML(const ByteString& szHTMLString) {
-  WideString wsHTMLString = WideString::FromUTF8(szHTMLString.AsStringView());
-  const wchar_t* strCode = L"0123456789abcdef";
-  wchar_t strEncode[9];
-  strEncode[0] = '&';
-  strEncode[1] = '#';
-  strEncode[2] = 'x';
-  strEncode[5] = ';';
-  strEncode[6] = 0;
-  strEncode[7] = ';';
-  strEncode[8] = 0;
-  CFX_WideTextBuf wsResultBuf;
-  int32_t iLen = wsHTMLString.GetLength();
-  int32_t i = 0;
-  const wchar_t* pData = wsHTMLString.c_str();
-  while (i < iLen) {
-    uint32_t ch = pData[i];
-    WideString htmlReserve;
-    if (HTMLCode2STR(ch, &htmlReserve)) {
-      wsResultBuf.AppendChar(L'&');
-      wsResultBuf << htmlReserve;
-      wsResultBuf.AppendChar(L';');
-    } else if (ch >= 32 && ch <= 126) {
-      wsResultBuf.AppendChar((wchar_t)ch);
-    } else if (ch < 256) {
-      int32_t iIndex = ch / 16;
-      strEncode[3] = strCode[iIndex];
-      strEncode[4] = strCode[ch - iIndex * 16];
-      strEncode[5] = ';';
-      strEncode[6] = 0;
-      wsResultBuf << strEncode;
-    } else {
-      int32_t iBigByte = ch / 256;
-      int32_t iLittleByte = ch % 256;
-      strEncode[3] = strCode[iBigByte / 16];
-      strEncode[4] = strCode[iBigByte % 16];
-      strEncode[5] = strCode[iLittleByte / 16];
-      strEncode[6] = strCode[iLittleByte % 16];
-      wsResultBuf << strEncode;
-    }
-    ++i;
-  }
-  wsResultBuf.AppendChar(0);
-  return wsResultBuf.MakeString();
-}
-
-// static
-WideString CFXJSE_FormCalcContext::EncodeXML(const ByteString& szXMLString) {
-  WideString wsXMLString = WideString::FromUTF8(szXMLString.AsStringView());
-  CFX_WideTextBuf wsResultBuf;
-  wchar_t strEncode[9];
-  strEncode[0] = '&';
-  strEncode[1] = '#';
-  strEncode[2] = 'x';
-  strEncode[5] = ';';
-  strEncode[6] = 0;
-  strEncode[7] = ';';
-  strEncode[8] = 0;
-  const wchar_t* strCode = L"0123456789abcdef";
-  for (const auto& ch : wsXMLString) {
-    switch (ch) {
-      case '"':
-        wsResultBuf.AppendChar('&');
-        wsResultBuf << WideStringView(L"quot");
-        wsResultBuf.AppendChar(';');
-        break;
-      case '&':
-        wsResultBuf.AppendChar('&');
-        wsResultBuf << WideStringView(L"amp");
-        wsResultBuf.AppendChar(';');
-        break;
-      case '\'':
-        wsResultBuf.AppendChar('&');
-        wsResultBuf << WideStringView(L"apos");
-        wsResultBuf.AppendChar(';');
-        break;
-      case '<':
-        wsResultBuf.AppendChar('&');
-        wsResultBuf << WideStringView(L"lt");
-        wsResultBuf.AppendChar(';');
-        break;
-      case '>':
-        wsResultBuf.AppendChar('&');
-        wsResultBuf << WideStringView(L"gt");
-        wsResultBuf.AppendChar(';');
-        break;
-      default: {
-        if (ch >= 32 && ch <= 126) {
-          wsResultBuf.AppendChar(ch);
-        } else if (ch < 256) {
-          int32_t iIndex = ch / 16;
-          strEncode[3] = strCode[iIndex];
-          strEncode[4] = strCode[ch - iIndex * 16];
-          strEncode[5] = ';';
-          strEncode[6] = 0;
-          wsResultBuf << strEncode;
-        } else {
-          int32_t iBigByte = ch / 256;
-          int32_t iLittleByte = ch % 256;
-          strEncode[3] = strCode[iBigByte / 16];
-          strEncode[4] = strCode[iBigByte % 16];
-          strEncode[5] = strCode[iLittleByte / 16];
-          strEncode[6] = strCode[iLittleByte % 16];
-          wsResultBuf << strEncode;
-        }
-        break;
-      }
-    }
-  }
-  wsResultBuf.AppendChar(0);
-  return wsResultBuf.MakeString();
-}
-
-// static
-bool CFXJSE_FormCalcContext::HTMLSTR2Code(const WideStringView& pData,
-                                          uint32_t* iCode) {
-  auto cmpFunc = [](const XFA_FMHtmlReserveCode& iter,
-                    const WideStringView& val) {
-    // TODO(tsepez): check usage of c_str() below.
-    return wcscmp(val.unterminated_c_str(), iter.m_htmlReserve) > 0;
-  };
-  const XFA_FMHtmlReserveCode* result =
-      std::lower_bound(std::begin(reservesForDecode),
-                       std::end(reservesForDecode), pData, cmpFunc);
-  if (result != std::end(reservesForEncode) &&
-      !wcscmp(pData.unterminated_c_str(), result->m_htmlReserve)) {
-    *iCode = result->m_uCode;
-    return true;
-  }
-  return false;
-}
-
-// static
-bool CFXJSE_FormCalcContext::HTMLCode2STR(uint32_t iCode,
-                                          WideString* wsHTMLReserve) {
-  auto cmpFunc = [](const XFA_FMHtmlReserveCode iter, const uint32_t& val) {
-    return iter.m_uCode < val;
-  };
-  const XFA_FMHtmlReserveCode* result =
-      std::lower_bound(std::begin(reservesForEncode),
-                       std::end(reservesForEncode), iCode, cmpFunc);
-  if (result != std::end(reservesForEncode) && result->m_uCode == iCode) {
-    *wsHTMLReserve = result->m_htmlReserve;
-    return true;
-  }
-  return false;
-}
-
-// static
-void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() < 2) {
-    pContext->ThrowParamCountMismatchException(L"Format");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString szPattern = ValueToUTF8String(argOne.get());
-
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString szValue = ValueToUTF8String(argTwo.get());
-
-  CXFA_Document* pDoc = pContext->GetDocument();
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-  ASSERT(pThisNode);
-
-  IFX_Locale* pLocale = pThisNode->GetLocale();
-  uint32_t patternType;
-  WideString wsPattern = WideString::FromUTF8(szPattern.AsStringView());
-  WideString wsValue = WideString::FromUTF8(szValue.AsStringView());
-  if (!PatternStringType(szPattern.AsStringView(), patternType)) {
-    switch (patternType) {
-      case XFA_VT_DATETIME: {
-        auto iTChar = wsPattern.Find(L'T');
-        if (!iTChar.has_value()) {
-          args.GetReturnValue()->SetString("");
-          return;
-        }
-        WideString wsDatePattern(L"date{");
-        wsDatePattern += wsPattern.Left(iTChar.value()) + L"} ";
-
-        WideString wsTimePattern(L"time{");
-        wsTimePattern +=
-            wsPattern.Right(wsPattern.GetLength() - (iTChar.value() + 1)) +
-            L"}";
-        wsPattern = wsDatePattern + wsTimePattern;
-      } break;
-      case XFA_VT_DATE: {
-        wsPattern = L"date{" + wsPattern + L"}";
-      } break;
-      case XFA_VT_TIME: {
-        wsPattern = L"time{" + wsPattern + L"}";
-      } break;
-      case XFA_VT_TEXT: {
-        wsPattern = L"text{" + wsPattern + L"}";
-      } break;
-      case XFA_VT_FLOAT: {
-        wsPattern = L"num{" + wsPattern + L"}";
-      } break;
-      default: {
-        WideString wsTestPattern;
-        wsTestPattern = L"num{" + wsPattern + L"}";
-        CXFA_LocaleValue tempLocaleValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
-                                         pLocale, pMgr);
-        if (tempLocaleValue.IsValid()) {
-          wsPattern = wsTestPattern;
-          patternType = XFA_VT_FLOAT;
-        } else {
-          wsTestPattern = L"text{" + wsPattern + L"}";
-          wsPattern = wsTestPattern;
-          patternType = XFA_VT_TEXT;
-        }
-      } break;
-    }
-  }
-  CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale, pMgr);
-  WideString wsRet;
-  if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale,
-                                  XFA_VALUEPICTURE_Display)) {
-    args.GetReturnValue()->SetString("");
-    return;
-  }
-
-  args.GetReturnValue()->SetString(wsRet.UTF8Encode().AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Left(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Left");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if ((ValueIsNull(pThis, argOne.get())) ||
-      (ValueIsNull(pThis, argTwo.get()))) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString sourceString = ValueToUTF8String(argOne.get());
-  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
-  args.GetReturnValue()->SetString(sourceString.Left(count).AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Len(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Len");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString sourceString = ValueToUTF8String(argOne.get());
-  args.GetReturnValue()->SetInteger(sourceString.GetLength());
-}
-
-// static
-void CFXJSE_FormCalcContext::Lower(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Lower");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  CFX_WideTextBuf lowStringBuf;
-  ByteString argString = ValueToUTF8String(argOne.get());
-  WideString wsArgString = WideString::FromUTF8(argString.AsStringView());
-  const wchar_t* pData = wsArgString.c_str();
-  size_t i = 0;
-  while (i < argString.GetLength()) {
-    int32_t ch = pData[i];
-    if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE))
-      ch += 32;
-    else if (ch == 0x100 || ch == 0x102 || ch == 0x104)
-      ch += 1;
-
-    lowStringBuf.AppendChar(ch);
-    ++i;
-  }
-  lowStringBuf.AppendChar(0);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(lowStringBuf.AsStringView()).AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Ltrim(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Ltrim");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString sourceString = ValueToUTF8String(argOne.get());
-  sourceString.TrimLeft();
-  args.GetReturnValue()->SetString(sourceString.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Parse(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 2) {
-    pContext->ThrowParamCountMismatchException(L"Parse");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString szPattern = ValueToUTF8String(argOne.get());
-  ByteString szValue = ValueToUTF8String(argTwo.get());
-  CXFA_Document* pDoc = pContext->GetDocument();
-  CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
-  CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-  ASSERT(pThisNode);
-
-  IFX_Locale* pLocale = pThisNode->GetLocale();
-  WideString wsPattern = WideString::FromUTF8(szPattern.AsStringView());
-  WideString wsValue = WideString::FromUTF8(szValue.AsStringView());
-  uint32_t patternType;
-  if (PatternStringType(szPattern.AsStringView(), patternType)) {
-    CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
-                                 pMgr);
-    if (!localeValue.IsValid()) {
-      args.GetReturnValue()->SetString("");
-      return;
-    }
-    args.GetReturnValue()->SetString(
-        localeValue.GetValue().UTF8Encode().AsStringView());
-    return;
-  }
-
-  switch (patternType) {
-    case XFA_VT_DATETIME: {
-      auto iTChar = wsPattern.Find(L'T');
-      if (!iTChar.has_value()) {
-        args.GetReturnValue()->SetString("");
-        return;
-      }
-      WideString wsDatePattern(L"date{" + wsPattern.Left(iTChar.value()) +
-                               L"} ");
-      WideString wsTimePattern(
-          L"time{" +
-          wsPattern.Right(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}");
-      wsPattern = wsDatePattern + wsTimePattern;
-      CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
-                                   pMgr);
-      if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
-        return;
-      }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().UTF8Encode().AsStringView());
-      return;
-    }
-    case XFA_VT_DATE: {
-      wsPattern = L"date{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
-                                   pMgr);
-      if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
-        return;
-      }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().UTF8Encode().AsStringView());
-      return;
-    }
-    case XFA_VT_TIME: {
-      wsPattern = L"time{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
-                                   pMgr);
-      if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
-        return;
-      }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().UTF8Encode().AsStringView());
-      return;
-    }
-    case XFA_VT_TEXT: {
-      wsPattern = L"text{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsPattern, pLocale,
-                                   pMgr);
-      if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
-        return;
-      }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().UTF8Encode().AsStringView());
-      return;
-    }
-    case XFA_VT_FLOAT: {
-      wsPattern = L"num{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsPattern, pLocale,
-                                   pMgr);
-      if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
-        return;
-      }
-      args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
-      return;
-    }
-    default: {
-      WideString wsTestPattern;
-      wsTestPattern = L"num{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
-                                   pLocale, pMgr);
-      if (localeValue.IsValid()) {
-        args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
-        return;
-      }
-
-      wsTestPattern = L"text{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue2(XFA_VT_TEXT, wsValue, wsTestPattern,
-                                    pLocale, pMgr);
-      if (!localeValue2.IsValid()) {
-        args.GetReturnValue()->SetString("");
-        return;
-      }
-      args.GetReturnValue()->SetString(
-          localeValue2.GetValue().UTF8Encode().AsStringView());
-      return;
-    }
-  }
-}
-
-// static
-void CFXJSE_FormCalcContext::Replace(CFXJSE_Value* pThis,
-                                     const ByteStringView& szFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 2 || argc > 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Replace");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString oneString;
-  ByteString twoString;
-  if (!ValueIsNull(pThis, argOne.get()) && !ValueIsNull(pThis, argTwo.get())) {
-    oneString = ValueToUTF8String(argOne.get());
-    twoString = ValueToUTF8String(argTwo.get());
-  }
-
-  ByteString threeString;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    threeString = ValueToUTF8String(argThree.get());
-  }
-
-  size_t iFindLen = twoString.GetLength();
-  std::ostringstream resultString;
-  size_t iFindIndex = 0;
-  for (size_t u = 0; u < oneString.GetLength(); ++u) {
-    char ch = static_cast<char>(oneString[u]);
-    if (ch != static_cast<char>(twoString[iFindIndex])) {
-      resultString << ch;
-      continue;
-    }
-
-    size_t iTemp = u + 1;
-    ++iFindIndex;
-    while (iFindIndex < iFindLen) {
-      uint8_t chTemp = oneString[iTemp];
-      if (chTemp != twoString[iFindIndex]) {
-        iFindIndex = 0;
-        break;
-      }
-
-      ++iTemp;
-      ++iFindIndex;
-    }
-    if (iFindIndex == iFindLen) {
-      resultString << threeString;
-      u += iFindLen - 1;
-      iFindIndex = 0;
-    } else {
-      resultString << ch;
-    }
-  }
-  resultString << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(resultString.str().c_str()));
-}
-
-// static
-void CFXJSE_FormCalcContext::Right(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Right");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if ((ValueIsNull(pThis, argOne.get())) ||
-      (ValueIsNull(pThis, argTwo.get()))) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString sourceString = ValueToUTF8String(argOne.get());
-  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
-  args.GetReturnValue()->SetString(sourceString.Right(count).AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Rtrim(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Rtrim");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  ByteString sourceString = ValueToUTF8String(argOne.get());
-  sourceString.TrimRight();
-  args.GetReturnValue()->SetString(sourceString.AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Space(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Space");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  int32_t count = std::max(0, ValueToInteger(pThis, argOne.get()));
-  std::ostringstream spaceString;
-  int32_t index = 0;
-  while (index < count) {
-    spaceString << ' ';
-    index++;
-  }
-  spaceString << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(spaceString.str().c_str()));
-}
-
-// static
-void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Str");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
-  if (numberValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  float fNumber = ValueToFloat(pThis, numberValue.get());
-
-  int32_t iWidth = 10;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> widthValue = GetSimpleValue(pThis, args, 1);
-    iWidth = static_cast<int32_t>(ValueToFloat(pThis, widthValue.get()));
-  }
-
-  int32_t iPrecision = 0;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> precisionValue =
-        GetSimpleValue(pThis, args, 2);
-    iPrecision = std::max(
-        0, static_cast<int32_t>(ValueToFloat(pThis, precisionValue.get())));
-  }
-
-  ByteString formatStr = "%";
-  if (iPrecision) {
-    formatStr += ".";
-    formatStr += ByteString::FormatInteger(iPrecision);
-  }
-  formatStr += "f";
-  ByteString numberString = ByteString::Format(formatStr.c_str(), fNumber);
-
-  const char* pData = numberString.c_str();
-  int32_t iLength = numberString.GetLength();
-  int32_t u = 0;
-  while (u < iLength) {
-    if (pData[u] == '.')
-      break;
-
-    ++u;
-  }
-
-  std::ostringstream resultBuf;
-  if (u > iWidth || (iPrecision + u) >= iWidth) {
-    int32_t i = 0;
-    while (i < iWidth) {
-      resultBuf << '*';
-      ++i;
-    }
-    resultBuf << '\0';
-    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
-    return;
-  }
-
-  if (u == iLength) {
-    if (iLength > iWidth) {
-      int32_t i = 0;
-      while (i < iWidth) {
-        resultBuf << '*';
-        ++i;
-      }
-    } else {
-      int32_t i = 0;
-      while (i < iWidth - iLength) {
-        resultBuf << ' ';
-        ++i;
-      }
-      resultBuf << pData;
-    }
-    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
-    return;
-  }
-
-  int32_t iLeavingSpace = iWidth - u - iPrecision;
-  if (iPrecision != 0)
-    iLeavingSpace--;
-
-  int32_t i = 0;
-  while (i < iLeavingSpace) {
-    resultBuf << ' ';
-    ++i;
-  }
-  i = 0;
-  while (i < u) {
-    resultBuf << pData[i];
-    ++i;
-  }
-  if (iPrecision != 0)
-    resultBuf << '.';
-
-  u++;
-  i = 0;
-  while (u < iLength) {
-    if (i >= iPrecision)
-      break;
-
-    resultBuf << pData[u];
-    ++i;
-    ++u;
-  }
-  while (i < iPrecision) {
-    resultBuf << '0';
-    ++i;
-  }
-  resultBuf << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
-}
-
-// static
-void CFXJSE_FormCalcContext::Stuff(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 3 || argc > 4) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Stuff");
-    return;
-  }
-
-  ByteString sourceString;
-  ByteString insertString;
-  int32_t iLength = 0;
-  int32_t iStart = 0;
-  int32_t iDelete = 0;
-  std::unique_ptr<CFXJSE_Value> sourceValue = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> deleteValue = GetSimpleValue(pThis, args, 2);
-  if (!sourceValue->IsNull() && !startValue->IsNull() &&
-      !deleteValue->IsNull()) {
-    sourceString = ValueToUTF8String(sourceValue.get());
-    iLength = sourceString.GetLength();
-    iStart = pdfium::clamp(
-        static_cast<int32_t>(ValueToFloat(pThis, startValue.get())), 1,
-        iLength);
-    iDelete = std::max(
-        0, static_cast<int32_t>(ValueToFloat(pThis, deleteValue.get())));
-  }
-
-  if (argc > 3) {
-    std::unique_ptr<CFXJSE_Value> insertValue = GetSimpleValue(pThis, args, 3);
-    insertString = ValueToUTF8String(insertValue.get());
-  }
-
-  iStart -= 1;
-  std::ostringstream resultString;
-  int32_t i = 0;
-  while (i < iStart) {
-    resultString << static_cast<char>(sourceString[i]);
-    ++i;
-  }
-  resultString << insertString.AsStringView();
-  i = iStart + iDelete;
-  while (i < iLength) {
-    resultString << static_cast<char>(sourceString[i]);
-    ++i;
-  }
-  resultString << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(resultString.str().c_str()));
-}
-
-// static
-void CFXJSE_FormCalcContext::Substr(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Substr");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> stringValue = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> endValue = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, stringValue.get()) ||
-      (ValueIsNull(pThis, startValue.get())) ||
-      (ValueIsNull(pThis, endValue.get()))) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  int32_t iStart = 0;
-  int32_t iCount = 0;
-  ByteString szSourceStr = ValueToUTF8String(stringValue.get());
-  int32_t iLength = szSourceStr.GetLength();
-  if (iLength == 0) {
-    args.GetReturnValue()->SetString("");
-    return;
-  }
-
-  iStart = pdfium::clamp(
-      iLength, 1, static_cast<int32_t>(ValueToFloat(pThis, startValue.get())));
-  iCount =
-      std::max(0, static_cast<int32_t>(ValueToFloat(pThis, endValue.get())));
-
-  iStart -= 1;
-  args.GetReturnValue()->SetString(
-      szSourceStr.Mid(iStart, iCount).AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Uuid(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 0 || argc > 1) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Uuid");
-    return;
-  }
-
-  int32_t iNum = 0;
-  if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    iNum = static_cast<int32_t>(ValueToFloat(pThis, argOne.get()));
-  }
-  args.GetReturnValue()->SetString(GUIDString(!!iNum).AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Upper(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 2) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Upper");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  CFX_WideTextBuf upperStringBuf;
-  ByteString argString = ValueToUTF8String(argOne.get());
-  WideString wsArgString = WideString::FromUTF8(argString.AsStringView());
-  const wchar_t* pData = wsArgString.c_str();
-  size_t i = 0;
-  while (i < wsArgString.GetLength()) {
-    int32_t ch = pData[i];
-    if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE))
-      ch -= 32;
-    else if (ch == 0x101 || ch == 0x103 || ch == 0x105)
-      ch -= 1;
-
-    upperStringBuf.AppendChar(ch);
-    ++i;
-  }
-  upperStringBuf.AppendChar(0);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::WordNum(CFXJSE_Value* pThis,
-                                     const ByteStringView& szFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1 || argc > 3) {
-    ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"WordNum");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
-  if (numberValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  float fNumber = ValueToFloat(pThis, numberValue.get());
-
-  int32_t iIdentifier = 0;
-  if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> identifierValue =
-        GetSimpleValue(pThis, args, 1);
-    if (identifierValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    iIdentifier =
-        static_cast<int32_t>(ValueToFloat(pThis, identifierValue.get()));
-  }
-
-  ByteString localeString;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (localeValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
-    localeString = ValueToUTF8String(localeValue.get());
-  }
-
-  if (fNumber < 0.0f || fNumber > 922337203685477550.0f) {
-    args.GetReturnValue()->SetString("*");
-    return;
-  }
-
-  args.GetReturnValue()->SetString(
-      WordUS(ByteString::Format("%.2f", fNumber), iIdentifier).AsStringView());
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::TrillionUS(const ByteStringView& szData) {
-  std::ostringstream strBuf;
-  ByteStringView pUnits[] = {"zero", "one", "two",   "three", "four",
-                             "five", "six", "seven", "eight", "nine"};
-  ByteStringView pCapUnits[] = {"Zero", "One", "Two",   "Three", "Four",
-                                "Five", "Six", "Seven", "Eight", "Nine"};
-  ByteStringView pTens[] = {"Ten",      "Eleven",  "Twelve",  "Thirteen",
-                            "Fourteen", "Fifteen", "Sixteen", "Seventeen",
-                            "Eighteen", "Nineteen"};
-  ByteStringView pLastTens[] = {"Twenty", "Thirty",  "Forty",  "Fifty",
-                                "Sixty",  "Seventy", "Eighty", "Ninety"};
-  ByteStringView pComm[] = {" Hundred ", " Thousand ", " Million ", " Billion ",
-                            "Trillion"};
-  const char* pData = szData.unterminated_c_str();
-  int32_t iLength = szData.GetLength();
-  int32_t iComm = 0;
-  if (iLength > 12)
-    iComm = 4;
-  else if (iLength > 9)
-    iComm = 3;
-  else if (iLength > 6)
-    iComm = 2;
-  else if (iLength > 3)
-    iComm = 1;
-
-  int32_t iFirstCount = iLength % 3;
-  if (iFirstCount == 0)
-    iFirstCount = 3;
-
-  int32_t iIndex = 0;
-  if (iFirstCount == 3) {
-    if (pData[iIndex] != '0') {
-      strBuf << pCapUnits[pData[iIndex] - '0'];
-      strBuf << pComm[0];
-    }
-    if (pData[iIndex + 1] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
-    } else {
-      if (pData[iIndex + 1] > '1') {
-        strBuf << pLastTens[pData[iIndex + 1] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 2] - '0'];
-      } else if (pData[iIndex + 1] == '1') {
-        strBuf << pTens[pData[iIndex + 2] - '0'];
-      } else if (pData[iIndex + 1] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
-      }
-    }
-    iIndex += 3;
-  } else if (iFirstCount == 2) {
-    if (pData[iIndex] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 1] - '0'];
-    } else {
-      if (pData[iIndex] > '1') {
-        strBuf << pLastTens[pData[iIndex] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 1] - '0'];
-      } else if (pData[iIndex] == '1') {
-        strBuf << pTens[pData[iIndex + 1] - '0'];
-      } else if (pData[iIndex] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 1] - '0'];
-      }
-    }
-    iIndex += 2;
-  } else if (iFirstCount == 1) {
-    strBuf << pCapUnits[pData[iIndex] - '0'];
-    iIndex += 1;
-  }
-  if (iLength > 3 && iFirstCount > 0) {
-    strBuf << pComm[iComm];
-    --iComm;
-  }
-  while (iIndex < iLength) {
-    if (pData[iIndex] != '0') {
-      strBuf << pCapUnits[pData[iIndex] - '0'];
-      strBuf << pComm[0];
-    }
-    if (pData[iIndex + 1] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
-    } else {
-      if (pData[iIndex + 1] > '1') {
-        strBuf << pLastTens[pData[iIndex + 1] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 2] - '0'];
-      } else if (pData[iIndex + 1] == '1') {
-        strBuf << pTens[pData[iIndex + 2] - '0'];
-      } else if (pData[iIndex + 1] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
-      }
-    }
-    if (iIndex < iLength - 3) {
-      strBuf << pComm[iComm];
-      --iComm;
-    }
-    iIndex += 3;
-  }
-  return ByteString(strBuf);
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::WordUS(const ByteString& szData,
-                                          int32_t iStyle) {
-  const char* pData = szData.c_str();
-  int32_t iLength = szData.GetLength();
-  if (iStyle < 0 || iStyle > 2) {
-    return ByteString();
-  }
-
-  std::ostringstream strBuf;
-
-  int32_t iIndex = 0;
-  while (iIndex < iLength) {
-    if (pData[iIndex] == '.')
-      break;
-    ++iIndex;
-  }
-  int32_t iInteger = iIndex;
-  iIndex = 0;
-  while (iIndex < iInteger) {
-    int32_t iCount = (iInteger - iIndex) % 12;
-    if (!iCount && iInteger - iIndex > 0)
-      iCount = 12;
-
-    strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
-    iIndex += iCount;
-    if (iIndex < iInteger)
-      strBuf << " Trillion ";
-  }
-
-  if (iStyle > 0)
-    strBuf << " Dollars";
-
-  if (iStyle > 1 && iInteger < iLength) {
-    strBuf << " And ";
-    iIndex = iInteger + 1;
-    while (iIndex < iLength) {
-      int32_t iCount = (iLength - iIndex) % 12;
-      if (!iCount && iLength - iIndex > 0)
-        iCount = 12;
-
-      strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
-      iIndex += iCount;
-      if (iIndex < iLength)
-        strBuf << " Trillion ";
-    }
-    strBuf << " Cents";
-  }
-  return ByteString(strBuf);
-}
-
-// static
-void CFXJSE_FormCalcContext::Get(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Get");
-    return;
-  }
-
-  CXFA_Document* pDoc = pContext->GetDocument();
-  if (!pDoc)
-    return;
-
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
-  if (!pAppProvider)
-    return;
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString urlString = ValueToUTF8String(argOne.get());
-  RetainPtr<IFX_SeekableReadStream> pFile =
-      pAppProvider->DownloadURL(WideString::FromUTF8(urlString.AsStringView()));
-  if (!pFile)
-    return;
-
-  int32_t size = pFile->GetSize();
-  std::vector<uint8_t> dataBuf(size);
-  pFile->ReadBlock(dataBuf.data(), size);
-  args.GetReturnValue()->SetString(ByteStringView(dataBuf));
-}
-
-// static
-void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  int32_t argc = args.GetLength();
-  if (argc < 2 || argc > 5) {
-    pContext->ThrowParamCountMismatchException(L"Post");
-    return;
-  }
-
-  CXFA_Document* pDoc = pContext->GetDocument();
-  if (!pDoc)
-    return;
-
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
-  if (!pAppProvider)
-    return;
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsURL = ValueToUTF8String(argOne.get());
-
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsData = ValueToUTF8String(argTwo.get());
-
-  ByteString bsContentType;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsContentType = ValueToUTF8String(argThree.get());
-  }
-
-  ByteString bsEncode;
-  if (argc > 3) {
-    std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-    bsEncode = ValueToUTF8String(argFour.get());
-  }
-
-  ByteString bsHeader;
-  if (argc > 4) {
-    std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-    bsHeader = ValueToUTF8String(argFive.get());
-  }
-
-  WideString decodedResponse;
-  if (!pAppProvider->PostRequestURL(
-          WideString::FromUTF8(bsURL.AsStringView()),
-          WideString::FromUTF8(bsData.AsStringView()),
-          WideString::FromUTF8(bsContentType.AsStringView()),
-          WideString::FromUTF8(bsEncode.AsStringView()),
-          WideString::FromUTF8(bsHeader.AsStringView()), decodedResponse)) {
-    pContext->ThrowServerDeniedException();
-    return;
-  }
-  args.GetReturnValue()->SetString(decodedResponse.UTF8Encode().AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  int32_t argc = args.GetLength();
-  if (argc < 2 || argc > 3) {
-    pContext->ThrowParamCountMismatchException(L"Put");
-    return;
-  }
-
-  CXFA_Document* pDoc = pContext->GetDocument();
-  if (!pDoc)
-    return;
-
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
-  if (!pAppProvider)
-    return;
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsURL = ValueToUTF8String(argOne.get());
-
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsData = ValueToUTF8String(argTwo.get());
-
-  ByteString bsEncode;
-  if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsEncode = ValueToUTF8String(argThree.get());
-  }
-
-  if (!pAppProvider->PutRequestURL(
-          WideString::FromUTF8(bsURL.AsStringView()),
-          WideString::FromUTF8(bsData.AsStringView()),
-          WideString::FromUTF8(bsEncode.AsStringView()))) {
-    pContext->ThrowServerDeniedException();
-    return;
-  }
-
-  args.GetReturnValue()->SetString("");
-}
-
-// static
-void CFXJSE_FormCalcContext::assign_value_operator(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szFuncName,
-    CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 2) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> lValue = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> rValue = GetSimpleValue(pThis, args, 1);
-  if (lValue->IsArray()) {
-    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-    auto leftLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    lValue->GetObjectProperty("length", leftLengthValue.get());
-    int32_t iLeftLength = leftLengthValue->ToInteger();
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    lValue->GetObjectPropertyByIdx(1, propertyValue.get());
-    if (propertyValue->IsNull()) {
-      for (int32_t i = 2; i < iLeftLength; i++) {
-        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
-        if (!SetObjectDefaultValue(jsObjectValue.get(), rValue.get())) {
-          pContext->ThrowNoDefaultPropertyException(szFuncName);
-          return;
-        }
-      }
-    } else {
-      for (int32_t i = 2; i < iLeftLength; i++) {
-        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
-        jsObjectValue->SetObjectProperty(
-            propertyValue->ToString().AsStringView(), rValue.get());
-      }
-    }
-  } else if (lValue->IsObject()) {
-    if (!SetObjectDefaultValue(lValue.get(), rValue.get())) {
-      pContext->ThrowNoDefaultPropertyException(szFuncName);
-      return;
-    }
-  }
-  args.GetReturnValue()->Assign(rValue.get());
-}
-
-// static
-void CFXJSE_FormCalcContext::logical_or_operator(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szFuncName,
-    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float first = ValueToFloat(pThis, argFirst.get());
-  float second = ValueToFloat(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first || second) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::logical_and_operator(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szFuncName,
-    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  float first = ValueToFloat(pThis, argFirst.get());
-  float second = ValueToFloat(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first && second) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::equality_operator(CFXJSE_Value* pThis,
-                                               const ByteStringView& szFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  if (fm_ref_equal(pThis, args)) {
-    args.GetReturnValue()->SetInteger(1);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
-    return;
-  }
-
-  if (argFirst->IsString() && argSecond->IsString()) {
-    args.GetReturnValue()->SetInteger(argFirst->ToString() ==
-                                      argSecond->ToString());
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first == second) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::notequality_operator(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szFuncName,
-    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  if (fm_ref_equal(pThis, args)) {
-    args.GetReturnValue()->SetInteger(0);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 0 : 1);
-    return;
-  }
-
-  if (argFirst->IsString() && argSecond->IsString()) {
-    args.GetReturnValue()->SetInteger(argFirst->ToString() !=
-                                      argSecond->ToString());
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger(first != second);
-}
-
-// static
-bool CFXJSE_FormCalcContext::fm_ref_equal(CFXJSE_Value* pThis,
-                                          CFXJSE_Arguments& args) {
-  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
-  if (!argFirst->IsArray() || !argSecond->IsArray())
-    return false;
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  auto firstFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto secondFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argFirst->GetObjectPropertyByIdx(0, firstFlagValue.get());
-  argSecond->GetObjectPropertyByIdx(0, secondFlagValue.get());
-  if (firstFlagValue->ToInteger() != 3 || secondFlagValue->ToInteger() != 3)
-    return false;
-
-  auto firstJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto secondJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argFirst->GetObjectPropertyByIdx(2, firstJSObject.get());
-  argSecond->GetObjectPropertyByIdx(2, secondJSObject.get());
-  if (firstJSObject->IsNull() || secondJSObject->IsNull())
-    return false;
-
-  return (firstJSObject->ToHostObject(nullptr) ==
-          secondJSObject->ToHostObject(nullptr));
-}
-
-// static
-void CFXJSE_FormCalcContext::less_operator(CFXJSE_Value* pThis,
-                                           const ByteStringView& szFuncName,
-                                           CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(0);
-    return;
-  }
-
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) < 0;
-    args.GetReturnValue()->SetInteger(result);
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first < second) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::lessequal_operator(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szFuncName,
-    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
-    return;
-  }
-
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) <= 0;
-    args.GetReturnValue()->SetInteger(result);
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first <= second) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::greater_operator(CFXJSE_Value* pThis,
-                                              const ByteStringView& szFuncName,
-                                              CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(0);
-    return;
-  }
-
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) > 0;
-    args.GetReturnValue()->SetInteger(result);
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first > second) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::greaterequal_operator(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szFuncName,
-    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
-    return;
-  }
-
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) >= 0;
-    args.GetReturnValue()->SetInteger(result);
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first >= second) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::plus_operator(CFXJSE_Value* pThis,
-                                           const ByteStringView& szFuncName,
-                                           CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
-  if (ValueIsNull(pThis, argFirst.get()) &&
-      ValueIsNull(pThis, argSecond.get())) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first + second);
-}
-
-// static
-void CFXJSE_FormCalcContext::minus_operator(CFXJSE_Value* pThis,
-                                            const ByteStringView& szFuncName,
-                                            CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first - second);
-}
-
-// static
-void CFXJSE_FormCalcContext::multiple_operator(CFXJSE_Value* pThis,
-                                               const ByteStringView& szFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first * second);
-}
-
-// static
-void CFXJSE_FormCalcContext::divide_operator(CFXJSE_Value* pThis,
-                                             const ByteStringView& szFuncName,
-                                             CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 2) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double second = ValueToDouble(pThis, argSecond.get());
-  if (second == 0.0) {
-    pContext->ThrowDivideByZeroException();
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argFirst.get());
-  args.GetReturnValue()->SetDouble(first / second);
-}
-
-// static
-void CFXJSE_FormCalcContext::positive_operator(CFXJSE_Value* pThis,
-                                               const ByteStringView& szFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  args.GetReturnValue()->SetDouble(0.0 + ValueToDouble(pThis, argOne.get()));
-}
-
-// static
-void CFXJSE_FormCalcContext::negative_operator(CFXJSE_Value* pThis,
-                                               const ByteStringView& szFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-  args.GetReturnValue()->SetDouble(0.0 - ValueToDouble(pThis, argOne.get()));
-}
-
-// static
-void CFXJSE_FormCalcContext::logical_not_operator(
-    CFXJSE_Value* pThis,
-    const ByteStringView& szFuncName,
-    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  double first = ValueToDouble(pThis, argOne.get());
-  args.GetReturnValue()->SetInteger((first == 0.0) ? 1 : 0);
-}
-
-// static
-void CFXJSE_FormCalcContext::dot_accessor(CFXJSE_Value* pThis,
-                                          const ByteStringView& szFuncName,
-                                          CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  int32_t argc = args.GetLength();
-  if (argc < 4 || argc > 5) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  bool bIsStar = true;
-  int32_t iIndexValue = 0;
-  if (argc > 4) {
-    bIsStar = false;
-    iIndexValue = ValueToInteger(pThis, args.GetValue(4).get());
-  }
-
-  ByteString szName = args.GetUTF8String(2);
-  ByteString szSomExp = GenerateSomExpression(
-      szName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar);
-
-  std::unique_ptr<CFXJSE_Value> argAccessor = args.GetValue(0);
-  if (argAccessor->IsArray()) {
-    auto pLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argAccessor->GetObjectProperty("length", pLengthValue.get());
-    int32_t iLength = pLengthValue->ToInteger();
-    if (iLength < 3) {
-      pContext->ThrowArgumentMismatchException();
-      return;
-    }
-
-    auto hJSObjValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    std::vector<std::vector<std::unique_ptr<CFXJSE_Value>>> resolveValues(
-        iLength - 2);
-    bool bAttribute = false;
-    int32_t iCounter = 0;
-    for (int32_t i = 2; i < iLength; i++) {
-      argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get());
-
-      XFA_RESOLVENODE_RS resolveNodeRS;
-      if (ResolveObjects(pThis, hJSObjValue.get(), szSomExp.AsStringView(),
-                         &resolveNodeRS, true, szName.IsEmpty())) {
-        ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(),
-                           &resolveValues[i - 2], &bAttribute);
-        iCounter += resolveValues[i - 2].size();
-      }
-    }
-    if (iCounter < 1) {
-      pContext->ThrowPropertyNotInObjectException(
-          WideString::FromUTF8(szName.AsStringView()),
-          WideString::FromUTF8(szSomExp.AsStringView()));
-      return;
-    }
-
-    std::vector<std::unique_ptr<CFXJSE_Value>> values;
-    for (int32_t i = 0; i < iCounter + 2; i++)
-      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-    values[0]->SetInteger(1);
-    if (bAttribute)
-      values[1]->SetString(szName.AsStringView());
-    else
-      values[1]->SetNull();
-
-    int32_t iIndex = 2;
-    for (int32_t i = 0; i < iLength - 2; i++) {
-      for (size_t j = 0; j < resolveValues[i].size(); j++) {
-        values[iIndex]->Assign(resolveValues[i][j].get());
-        iIndex++;
-      }
-    }
-    args.GetReturnValue()->SetArray(values);
-    return;
-  }
-
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  bool iRet = false;
-  ByteString bsAccessorName = args.GetUTF8String(1);
-  if (argAccessor->IsObject() ||
-      (argAccessor->IsNull() && bsAccessorName.IsEmpty())) {
-    iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
-                          &resolveNodeRS, true, szName.IsEmpty());
-  } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() &&
-             GetObjectForName(pThis, argAccessor.get(),
-                              bsAccessorName.AsStringView())) {
-    iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
-                          &resolveNodeRS, true, szName.IsEmpty());
-  }
-  if (!iRet) {
-    pContext->ThrowPropertyNotInObjectException(
-        WideString::FromUTF8(szName.AsStringView()),
-        WideString::FromUTF8(szSomExp.AsStringView()));
-    return;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> resolveValues;
-  bool bAttribute = false;
-  ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues,
-                     &bAttribute);
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> values;
-  for (size_t i = 0; i < resolveValues.size() + 2; i++)
-    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  values[0]->SetInteger(1);
-  if (bAttribute)
-    values[1]->SetString(szName.AsStringView());
-  else
-    values[1]->SetNull();
-
-  for (size_t i = 0; i < resolveValues.size(); i++)
-    values[i + 2]->Assign(resolveValues[i].get());
-
-  args.GetReturnValue()->SetArray(values);
-}
-
-// static
-void CFXJSE_FormCalcContext::dotdot_accessor(CFXJSE_Value* pThis,
-                                             const ByteStringView& szFuncName,
-                                             CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  int32_t argc = args.GetLength();
-  if (argc < 4 || argc > 5) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  bool bIsStar = true;
-  int32_t iIndexValue = 0;
-  if (argc > 4) {
-    bIsStar = false;
-    iIndexValue = ValueToInteger(pThis, args.GetValue(4).get());
-  }
-
-  ByteString szName = args.GetUTF8String(2);
-  ByteString szSomExp = GenerateSomExpression(
-      szName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar);
-
-  std::unique_ptr<CFXJSE_Value> argAccessor = args.GetValue(0);
-  if (argAccessor->IsArray()) {
-    auto pLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argAccessor->GetObjectProperty("length", pLengthValue.get());
-    int32_t iLength = pLengthValue->ToInteger();
-    if (iLength < 3) {
-      pContext->ThrowArgumentMismatchException();
-      return;
-    }
-
-    int32_t iCounter = 0;
-
-    std::vector<std::vector<std::unique_ptr<CFXJSE_Value>>> resolveValues(
-        iLength - 2);
-    auto hJSObjValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    bool bAttribute = false;
-    for (int32_t i = 2; i < iLength; i++) {
-      argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get());
-      XFA_RESOLVENODE_RS resolveNodeRS;
-      if (ResolveObjects(pThis, hJSObjValue.get(), szSomExp.AsStringView(),
-                         &resolveNodeRS, false, false)) {
-        ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(),
-                           &resolveValues[i - 2], &bAttribute);
-        iCounter += resolveValues[i - 2].size();
-      }
-    }
-    if (iCounter < 1) {
-      pContext->ThrowPropertyNotInObjectException(
-          WideString::FromUTF8(szName.AsStringView()),
-          WideString::FromUTF8(szSomExp.AsStringView()));
-      return;
-    }
-
-    std::vector<std::unique_ptr<CFXJSE_Value>> values;
-    for (int32_t i = 0; i < iCounter + 2; i++)
-      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-    values[0]->SetInteger(1);
-    if (bAttribute)
-      values[1]->SetString(szName.AsStringView());
-    else
-      values[1]->SetNull();
-
-    int32_t iIndex = 2;
-    for (int32_t i = 0; i < iLength - 2; i++) {
-      for (size_t j = 0; j < resolveValues[i].size(); j++) {
-        values[iIndex]->Assign(resolveValues[i][j].get());
-        iIndex++;
-      }
-    }
-    args.GetReturnValue()->SetArray(values);
-    return;
-  }
-
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  bool iRet = false;
-  ByteString bsAccessorName = args.GetUTF8String(1);
-  if (argAccessor->IsObject() ||
-      (argAccessor->IsNull() && bsAccessorName.IsEmpty())) {
-    iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
-                          &resolveNodeRS, false, false);
-  } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() &&
-             GetObjectForName(pThis, argAccessor.get(),
-                              bsAccessorName.AsStringView())) {
-    iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
-                          &resolveNodeRS, false, false);
-  }
-  if (!iRet) {
-    pContext->ThrowPropertyNotInObjectException(
-        WideString::FromUTF8(szName.AsStringView()),
-        WideString::FromUTF8(szSomExp.AsStringView()));
-    return;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> resolveValues;
-  bool bAttribute = false;
-  ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues,
-                     &bAttribute);
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> values;
-  for (size_t i = 0; i < resolveValues.size() + 2; i++)
-    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  values[0]->SetInteger(1);
-  if (bAttribute)
-    values[1]->SetString(szName.AsStringView());
-  else
-    values[1]->SetNull();
-
-  for (size_t i = 0; i < resolveValues.size(); i++)
-    values[i + 2]->Assign(resolveValues[i].get());
-
-  args.GetReturnValue()->SetArray(values);
-}
-
-// static
-void CFXJSE_FormCalcContext::eval_translation(CFXJSE_Value* pThis,
-                                              const ByteStringView& szFuncName,
-                                              CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Eval");
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString argString = ValueToUTF8String(argOne.get());
-  if (argString.IsEmpty()) {
-    pContext->ThrowArgumentMismatchException();
-    return;
-  }
-
-  WideString scriptString = WideString::FromUTF8(argString.AsStringView());
-  CFX_WideTextBuf wsJavaScriptBuf;
-  if (!CFXJSE_FormCalcContext::Translate(scriptString.AsStringView(),
-                                         &wsJavaScriptBuf)) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).AsStringView());
-}
-
-// static
-void CFXJSE_FormCalcContext::is_fm_object(CFXJSE_Value* pThis,
-                                          const ByteStringView& szFuncName,
-                                          CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    args.GetReturnValue()->SetBoolean(false);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  args.GetReturnValue()->SetBoolean(argOne->IsObject());
-}
-
-// static
-void CFXJSE_FormCalcContext::is_fm_array(CFXJSE_Value* pThis,
-                                         const ByteStringView& szFuncName,
-                                         CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    args.GetReturnValue()->SetBoolean(false);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  args.GetReturnValue()->SetBoolean(argOne->IsArray());
-}
-
-// static
-void CFXJSE_FormCalcContext::get_fm_value(CFXJSE_Value* pThis,
-                                          const ByteStringView& szFuncName,
-                                          CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 1) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (argOne->IsArray()) {
-    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
-    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), args.GetReturnValue());
-      return;
-    }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     args.GetReturnValue());
-    return;
-  }
-
-  if (argOne->IsObject()) {
-    GetObjectDefaultValue(argOne.get(), args.GetReturnValue());
-    return;
-  }
-
-  args.GetReturnValue()->Assign(argOne.get());
-}
-
-// static
-void CFXJSE_FormCalcContext::get_fm_jsobj(CFXJSE_Value* pThis,
-                                          const ByteStringView& szFuncName,
-                                          CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray()) {
-    args.GetReturnValue()->Assign(argOne.get());
-    return;
-  }
-
-#ifndef NDEBUG
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectProperty("length", lengthValue.get());
-  ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-  argOne->GetObjectPropertyByIdx(2, args.GetReturnValue());
-}
-
-// static
-void CFXJSE_FormCalcContext::fm_var_filter(CFXJSE_Value* pThis,
-                                           const ByteStringView& szFuncName,
-                                           CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  if (args.GetLength() != 1) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray()) {
-    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
-    args.GetReturnValue()->Assign(simpleValue.get());
-    return;
-  }
-
-#ifndef NDEBUG
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectProperty("length", lengthValue.get());
-  ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-  auto flagsValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectPropertyByIdx(0, flagsValue.get());
-  int32_t iFlags = flagsValue->ToInteger();
-  if (iFlags != 3 && iFlags != 4) {
-    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
-    args.GetReturnValue()->Assign(simpleValue.get());
-    return;
-  }
-
-  if (iFlags == 4) {
-    std::vector<std::unique_ptr<CFXJSE_Value>> values;
-    for (int32_t i = 0; i < 3; i++)
-      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-    values[0]->SetInteger(3);
-    values[1]->SetNull();
-    values[2]->SetNull();
-    args.GetReturnValue()->SetArray(values);
-    return;
-  }
-
-  auto objectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectPropertyByIdx(2, objectValue.get());
-  if (objectValue->IsNull()) {
-    pContext->ThrowCompilerErrorException();
-    return;
-  }
-  args.GetReturnValue()->Assign(argOne.get());
-}
-
-// static
-void CFXJSE_FormCalcContext::concat_fm_object(CFXJSE_Value* pThis,
-                                              const ByteStringView& szFuncName,
-                                              CFXJSE_Arguments& args) {
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  uint32_t iLength = 0;
-  int32_t argc = args.GetLength();
-  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
-  for (int32_t i = 0; i < argc; i++) {
-    argValues.push_back(args.GetValue(i));
-    if (argValues[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValues[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t length = lengthValue->ToInteger();
-      iLength = iLength + ((length > 2) ? (length - 2) : 0);
-    }
-    iLength += 1;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> returnValues;
-  for (int32_t i = 0; i < (int32_t)iLength; i++)
-    returnValues.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int32_t index = 0;
-  for (int32_t i = 0; i < argc; i++) {
-    if (argValues[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValues[i]->GetObjectProperty("length", lengthValue.get());
-
-      int32_t length = lengthValue->ToInteger();
-      for (int32_t j = 2; j < length; j++) {
-        argValues[i]->GetObjectPropertyByIdx(j, returnValues[index].get());
-        index++;
-      }
-    }
-    returnValues[index]->Assign(argValues[i].get());
-    index++;
-  }
-  args.GetReturnValue()->SetArray(returnValues);
-}
-
-// static
-std::unique_ptr<CFXJSE_Value> CFXJSE_FormCalcContext::GetSimpleValue(
-    CFXJSE_Value* pThis,
-    CFXJSE_Arguments& args,
-    uint32_t index) {
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  ASSERT(index < (uint32_t)args.GetLength());
-
-  std::unique_ptr<CFXJSE_Value> argIndex = args.GetValue(index);
-  if (!argIndex->IsArray() && !argIndex->IsObject())
-    return argIndex;
-
-  if (argIndex->IsArray()) {
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argIndex->GetObjectProperty("length", lengthValue.get());
-    int32_t iLength = lengthValue->ToInteger();
-    auto simpleValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    if (iLength < 3) {
-      simpleValue.get()->SetUndefined();
-      return simpleValue;
-    }
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argIndex->GetObjectPropertyByIdx(1, propertyValue.get());
-    argIndex->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), simpleValue.get());
-      return simpleValue;
-    }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     simpleValue.get());
-    return simpleValue;
-  }
-
-  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  GetObjectDefaultValue(argIndex.get(), defaultValue.get());
-  return defaultValue;
-}
-
-// static
-bool CFXJSE_FormCalcContext::ValueIsNull(CFXJSE_Value* pThis,
-                                         CFXJSE_Value* arg) {
-  if (!arg || arg->IsNull())
-    return true;
-
-  if (!arg->IsArray() && !arg->IsObject())
-    return false;
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    int32_t iLength = hvalue_get_array_length(pThis, arg);
-    if (iLength < 3)
-      return true;
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(jsObjectValue.get(), defaultValue.get());
-      return defaultValue->IsNull();
-    }
-
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return newPropertyValue->IsNull();
-  }
-
-  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  GetObjectDefaultValue(arg, defaultValue.get());
-  return defaultValue->IsNull();
-}
-
-// static
-int32_t CFXJSE_FormCalcContext::hvalue_get_array_length(CFXJSE_Value* pThis,
-                                                        CFXJSE_Value* arg) {
-  if (!arg || !arg->IsArray())
-    return 0;
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  arg->GetObjectProperty("length", lengthValue.get());
-  return lengthValue->ToInteger();
-}
-
-// static
-bool CFXJSE_FormCalcContext::simpleValueCompare(CFXJSE_Value* pThis,
-                                                CFXJSE_Value* firstValue,
-                                                CFXJSE_Value* secondValue) {
-  if (!firstValue)
-    return false;
-
-  if (firstValue->IsString()) {
-    ByteString firstString = ValueToUTF8String(firstValue);
-    ByteString secondString = ValueToUTF8String(secondValue);
-    return firstString == secondString;
-  }
-  if (firstValue->IsNumber()) {
-    float first = ValueToFloat(pThis, firstValue);
-    float second = ValueToFloat(pThis, secondValue);
-    return first == second;
-  }
-  if (firstValue->IsBoolean())
-    return firstValue->ToBoolean() == secondValue->ToBoolean();
-
-  return firstValue->IsNull() && secondValue && secondValue->IsNull();
-}
-
-// static
-void CFXJSE_FormCalcContext::unfoldArgs(
-    CFXJSE_Value* pThis,
-    CFXJSE_Arguments& args,
-    std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-    int32_t iStart) {
-  resultValues->clear();
-
-  int32_t iCount = 0;
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  int32_t argc = args.GetLength();
-  std::vector<std::unique_ptr<CFXJSE_Value>> argsValue;
-  for (int32_t i = 0; i < argc - iStart; i++) {
-    argsValue.push_back(args.GetValue(i + iStart));
-    if (argsValue[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      iCount += ((iLength > 2) ? (iLength - 2) : 0);
-    } else {
-      iCount += 1;
-    }
-  }
-
-  for (int32_t i = 0; i < iCount; i++)
-    resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int32_t index = 0;
-  for (int32_t i = 0; i < argc - iStart; i++) {
-    if (argsValue[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength < 3)
-        continue;
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectPropertyByIdx(1, propertyValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(),
-                                (*resultValues)[index].get());
-          index++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(),
-              (*resultValues)[index].get());
-          index++;
-        }
-      }
-    } else if (argsValue[i]->IsObject()) {
-      GetObjectDefaultValue(argsValue[i].get(), (*resultValues)[index].get());
-      index++;
-    } else {
-      (*resultValues)[index]->Assign(argsValue[i].get());
-      index++;
-    }
-  }
-}
-
-// static
-void CFXJSE_FormCalcContext::GetObjectDefaultValue(
-    CFXJSE_Value* pValue,
-    CFXJSE_Value* pDefaultValue) {
-  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue, nullptr));
-  if (!pNode) {
-    pDefaultValue->SetNull();
-    return;
-  }
-  pNode->JSObject()->Script_Som_DefaultValue(pDefaultValue, false,
-                                             XFA_Attribute::Unknown);
-}
-
-// static
-bool CFXJSE_FormCalcContext::SetObjectDefaultValue(CFXJSE_Value* pValue,
-                                                   CFXJSE_Value* hNewValue) {
-  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue, nullptr));
-  if (!pNode)
-    return false;
-
-  pNode->JSObject()->Script_Som_DefaultValue(hNewValue, true,
-                                             XFA_Attribute::Unknown);
-  return true;
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::GenerateSomExpression(
-    const ByteStringView& szName,
-    int32_t iIndexFlags,
-    int32_t iIndexValue,
-    bool bIsStar) {
-  if (bIsStar)
-    return ByteString(szName, "[*]");
-
-  if (iIndexFlags == 0)
-    return ByteString(szName);
-
-  if (iIndexFlags == 1 || iIndexValue == 0) {
-    return ByteString(szName, "[") + ByteString::FormatInteger(iIndexValue) +
-           "]";
-  }
-  ByteString szSomExp;
-  if (iIndexFlags == 2) {
-    szSomExp = (iIndexValue < 0) ? (szName + "[-") : (szName + "[+");
-    iIndexValue = (iIndexValue < 0) ? (0 - iIndexValue) : iIndexValue;
-    szSomExp += ByteString::FormatInteger(iIndexValue);
-    szSomExp += "]";
-  } else {
-    szSomExp = (iIndexValue < 0) ? (szName + "[") : (szName + "[-");
-    iIndexValue = (iIndexValue < 0) ? (0 - iIndexValue) : iIndexValue;
-    szSomExp += ByteString::FormatInteger(iIndexValue);
-    szSomExp += "]";
-  }
-  return szSomExp;
-}
-
-// static
-bool CFXJSE_FormCalcContext::GetObjectForName(
-    CFXJSE_Value* pThis,
-    CFXJSE_Value* accessorValue,
-    const ByteStringView& szAccessorName) {
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  if (!pDoc)
-    return false;
-
-  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  uint32_t dwFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-                     XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
-  bool iRet = pScriptContext->ResolveObjects(
-      pScriptContext->GetThisObject(),
-      WideString::FromUTF8(szAccessorName).AsStringView(), &resolveNodeRS,
-      dwFlags, nullptr);
-  if (iRet && resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    accessorValue->Assign(
-        pScriptContext->GetJSValueFromMap(resolveNodeRS.objects.front()));
-    return true;
-  }
-  return false;
-}
-
-// static
-bool CFXJSE_FormCalcContext::ResolveObjects(CFXJSE_Value* pThis,
-                                            CFXJSE_Value* pRefValue,
-                                            const ByteStringView& bsSomExp,
-                                            XFA_RESOLVENODE_RS* resolveNodeRS,
-                                            bool bdotAccessor,
-                                            bool bHasNoResolveName) {
-  CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
-  if (!pDoc)
-    return false;
-
-  WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
-  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
-  CXFA_Object* pNode = nullptr;
-  uint32_t dFlags = 0UL;
-  if (bdotAccessor) {
-    if (pRefValue && pRefValue->IsNull()) {
-      pNode = pScriptContext->GetThisObject();
-      dFlags = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
-    } else {
-      pNode = CFXJSE_Engine::ToObject(pRefValue, nullptr);
-      ASSERT(pNode);
-      if (bHasNoResolveName) {
-        WideString wsName;
-        if (CXFA_Node* pXFANode = pNode->AsNode()) {
-          Optional<WideString> ret =
-              pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
-          if (ret)
-            wsName = *ret;
-        }
-        if (wsName.IsEmpty())
-          wsName = L"#" + pNode->GetClassName();
-
-        wsSomExpression = wsName + wsSomExpression;
-        dFlags = XFA_RESOLVENODE_Siblings;
-      } else {
-        dFlags = (bsSomExp == "*")
-                     ? (XFA_RESOLVENODE_Children)
-                     : (XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                        XFA_RESOLVENODE_Properties);
-      }
-    }
-  } else {
-    pNode = CFXJSE_Engine::ToObject(pRefValue, nullptr);
-    dFlags = XFA_RESOLVENODE_AnyChild;
-  }
-  return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
-                                        resolveNodeRS, dFlags, nullptr);
-}
-
-// static
-void CFXJSE_FormCalcContext::ParseResolveResult(
-    CFXJSE_Value* pThis,
-    const XFA_RESOLVENODE_RS& resolveNodeRS,
-    CFXJSE_Value* pParentValue,
-    std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-    bool* bAttribute) {
-  ASSERT(bAttribute);
-
-  resultValues->clear();
-
-  CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    *bAttribute = false;
-    CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
-    for (CXFA_Object* pObject : resolveNodeRS.objects) {
-      resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-      resultValues->back()->Assign(pScriptContext->GetJSValueFromMap(pObject));
-    }
-    return;
-  }
-
-  *bAttribute = true;
-  if (resolveNodeRS.pScriptAttribute &&
-      resolveNodeRS.pScriptAttribute->eValueType == XFA_ScriptType::Object) {
-    for (CXFA_Object* pObject : resolveNodeRS.objects) {
-      auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      CJX_Object* jsObject = pObject->JSObject();
-      (jsObject->*(resolveNodeRS.pScriptAttribute->callback))(
-          pValue.get(), false, resolveNodeRS.pScriptAttribute->attribute);
-
-      resultValues->push_back(std::move(pValue));
-      *bAttribute = false;
-    }
-  }
-  if (!*bAttribute)
-    return;
-  if (!pParentValue || !pParentValue->IsObject())
-    return;
-
-  resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-  resultValues->back()->Assign(pParentValue);
-}
-
-// static
-int32_t CFXJSE_FormCalcContext::ValueToInteger(CFXJSE_Value* pThis,
-                                               CFXJSE_Value* pValue) {
-  if (!pValue)
-    return 0;
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  if (pValue->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    pValue->GetObjectPropertyByIdx(1, propertyValue.get());
-    pValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToInteger(pThis, newPropertyValue.get());
-    }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToInteger(pThis, newPropertyValue.get());
-  }
-  if (pValue->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(pValue, newPropertyValue.get());
-    return ValueToInteger(pThis, newPropertyValue.get());
-  }
-  if (pValue->IsString())
-    return FXSYS_atoi(pValue->ToString().c_str());
-  return pValue->ToInteger();
-}
-
-// static
-float CFXJSE_FormCalcContext::ValueToFloat(CFXJSE_Value* pThis,
-                                           CFXJSE_Value* arg) {
-  if (!arg)
-    return 0.0f;
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToFloat(pThis, newPropertyValue.get());
-    }
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToFloat(pThis, newPropertyValue.get());
-  }
-  if (arg->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(arg, newPropertyValue.get());
-    return ValueToFloat(pThis, newPropertyValue.get());
-  }
-  if (arg->IsString()) {
-    return static_cast<float>(
-        ByteStringToDouble(arg->ToString().AsStringView()));
-  }
-  if (arg->IsUndefined())
-    return 0;
-
-  return arg->ToFloat();
-}
-
-// static
-double CFXJSE_FormCalcContext::ValueToDouble(CFXJSE_Value* pThis,
-                                             CFXJSE_Value* arg) {
-  if (!arg)
-    return 0;
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToDouble(pThis, newPropertyValue.get());
-    }
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToDouble(pThis, newPropertyValue.get());
-  }
-  if (arg->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(arg, newPropertyValue.get());
-    return ValueToDouble(pThis, newPropertyValue.get());
-  }
-  if (arg->IsString())
-    return ByteStringToDouble(arg->ToString().AsStringView());
-  if (arg->IsUndefined())
-    return 0;
-  return arg->ToDouble();
-}
-
-// static.
-double CFXJSE_FormCalcContext::ExtractDouble(CFXJSE_Value* pThis,
-                                             CFXJSE_Value* src,
-                                             bool* ret) {
-  ASSERT(ret);
-  *ret = true;
-
-  if (!src)
-    return 0;
-
-  if (!src->IsArray())
-    return ValueToDouble(pThis, src);
-
-  v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  src->GetObjectProperty("length", lengthValue.get());
-  int32_t iLength = lengthValue->ToInteger();
-  if (iLength <= 2) {
-    *ret = false;
-    return 0.0;
-  }
-
-  auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  src->GetObjectPropertyByIdx(1, propertyValue.get());
-  src->GetObjectPropertyByIdx(2, jsObjectValue.get());
-  if (propertyValue->IsNull())
-    return ValueToDouble(pThis, jsObjectValue.get());
-
-  auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                   newPropertyValue.get());
-  return ValueToDouble(pThis, newPropertyValue.get());
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::ValueToUTF8String(CFXJSE_Value* arg) {
-  if (!arg || arg->IsNull() || arg->IsUndefined())
-    return ByteString();
-  if (arg->IsBoolean())
-    return arg->ToBoolean() ? "1" : "0";
-  return arg->ToString();
-}
-
-// static.
-bool CFXJSE_FormCalcContext::Translate(const WideStringView& wsFormcalc,
-                                       CFX_WideTextBuf* wsJavascript) {
-  if (wsFormcalc.IsEmpty()) {
-    wsJavascript->Clear();
-    return true;
-  }
-
-  CXFA_FMParser parser(wsFormcalc);
-  std::unique_ptr<CXFA_FMFunctionDefinition> func = parser.Parse();
-  if (!func || parser.HasError())
-    return false;
-
-  CXFA_FMToJavaScriptDepth::Reset();
-  if (!func->ToJavaScript(*wsJavascript))
-    return false;
-
-  wsJavascript->AppendChar(0);
-
-  return !CXFA_IsTooBig(*wsJavascript);
-}
-
-CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
-                                               CFXJSE_Context* pScriptContext,
-                                               CXFA_Document* pDoc)
-    : CFXJSE_HostObject(kFM2JS),
-      m_pIsolate(pScriptIsolate),
-      m_pFMClass(CFXJSE_Class::Create(pScriptContext,
-                                      &formcalc_fm2js_descriptor,
-                                      false)),
-      m_pValue(pdfium::MakeUnique<CFXJSE_Value>(pScriptIsolate)),
-      m_pDocument(pDoc) {
-  m_pValue.get()->SetObject(this, m_pFMClass);
-}
-
-CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() {}
-
-void CFXJSE_FormCalcContext::GlobalPropertyGetter(CFXJSE_Value* pValue) {
-  pValue->Assign(m_pValue.get());
-}
-
-void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException(
-    const ByteStringView& name) const {
-  // TODO(tsepez): check usage of c_str() below.
-  ThrowException(L"%.16S doesn't have a default property.",
-                 name.unterminated_c_str());
-}
-
-void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const {
-  ThrowException(L"Compiler error.");
-}
-
-void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const {
-  ThrowException(L"Divide by zero.");
-}
-
-void CFXJSE_FormCalcContext::ThrowServerDeniedException() const {
-  ThrowException(L"Server does not permit operation.");
-}
-
-void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException(
-    const WideString& name,
-    const WideString& exp) const {
-  ThrowException(
-      L"An attempt was made to reference property '%.16s' of a non-object "
-      L"in SOM expression %.16s.",
-      name.c_str(), exp.c_str());
-}
-
-void CFXJSE_FormCalcContext::ThrowParamCountMismatchException(
-    const WideString& method) const {
-  ThrowException(L"Incorrect number of parameters calling method '%.16s'.",
-                 method.c_str());
-}
-
-void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const {
-  ThrowException(L"Argument mismatch in property or function argument.");
-}
-
-void CFXJSE_FormCalcContext::ThrowException(const wchar_t* str, ...) const {
-  va_list arg_ptr;
-  va_start(arg_ptr, str);
-  WideString wsMessage = WideString::FormatV(str, arg_ptr);
-  va_end(arg_ptr);
-
-  ASSERT(!wsMessage.IsEmpty());
-  FXJSE_ThrowMessage(wsMessage.UTF8Encode().AsStringView());
-}
diff --git a/fxjs/cfxjse_formcalc_context.h b/fxjs/cfxjse_formcalc_context.h
deleted file mode 100644
index 51e3178..0000000
--- a/fxjs/cfxjse_formcalc_context.h
+++ /dev/null
@@ -1,445 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_FORMCALC_CONTEXT_H_
-#define FXJS_CFXJSE_FORMCALC_CONTEXT_H_
-
-#include <memory>
-#include <vector>
-
-#include "fxjs/cfxjse_arguments.h"
-#include "fxjs/cfxjse_context.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-
-class CFX_WideTextBuf;
-class CXFA_Document;
-
-class CFXJSE_FormCalcContext : public CFXJSE_HostObject {
- public:
-  CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
-                         CFXJSE_Context* pScriptContext,
-                         CXFA_Document* pDoc);
-  ~CFXJSE_FormCalcContext() override;
-
-  static void Abs(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Avg(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Ceil(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Count(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Floor(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Max(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Min(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Mod(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Round(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Sum(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Date(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Date2Num(CFXJSE_Value* pThis,
-                       const ByteStringView& szFuncName,
-                       CFXJSE_Arguments& args);
-  static void DateFmt(CFXJSE_Value* pThis,
-                      const ByteStringView& szFuncName,
-                      CFXJSE_Arguments& args);
-  static void IsoDate2Num(CFXJSE_Value* pThis,
-                          const ByteStringView& szFuncName,
-                          CFXJSE_Arguments& args);
-  static void IsoTime2Num(CFXJSE_Value* pThis,
-                          const ByteStringView& szFuncName,
-                          CFXJSE_Arguments& args);
-  static void LocalDateFmt(CFXJSE_Value* pThis,
-                           const ByteStringView& szFuncName,
-                           CFXJSE_Arguments& args);
-  static void LocalTimeFmt(CFXJSE_Value* pThis,
-                           const ByteStringView& szFuncName,
-                           CFXJSE_Arguments& args);
-  static void Num2Date(CFXJSE_Value* pThis,
-                       const ByteStringView& szFuncName,
-                       CFXJSE_Arguments& args);
-  static void Num2GMTime(CFXJSE_Value* pThis,
-                         const ByteStringView& szFuncName,
-                         CFXJSE_Arguments& args);
-  static void Num2Time(CFXJSE_Value* pThis,
-                       const ByteStringView& szFuncName,
-                       CFXJSE_Arguments& args);
-  static void Time(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Time2Num(CFXJSE_Value* pThis,
-                       const ByteStringView& szFuncName,
-                       CFXJSE_Arguments& args);
-  static void TimeFmt(CFXJSE_Value* pThis,
-                      const ByteStringView& szFuncName,
-                      CFXJSE_Arguments& args);
-
-  static bool IsIsoDateFormat(const char* pData,
-                              int32_t iLength,
-                              int32_t& iStyle,
-                              int32_t& iYear,
-                              int32_t& iMonth,
-                              int32_t& iDay);
-  static bool IsIsoTimeFormat(const char* pData,
-                              int32_t iLength,
-                              int32_t& iHour,
-                              int32_t& iMinute,
-                              int32_t& iSecond,
-                              int32_t& iMilliSecond,
-                              int32_t& iZoneHour,
-                              int32_t& iZoneMinute);
-  static bool IsIsoDateTimeFormat(const char* pData,
-                                  int32_t iLength,
-                                  int32_t& iYear,
-                                  int32_t& iMonth,
-                                  int32_t& iDay,
-                                  int32_t& iHour,
-                                  int32_t& iMinute,
-                                  int32_t& iSecond,
-                                  int32_t& iMillionSecond,
-                                  int32_t& iZoneHour,
-                                  int32_t& iZoneMinute);
-  static ByteString Local2IsoDate(CFXJSE_Value* pThis,
-                                  const ByteStringView& szDate,
-                                  const ByteStringView& szFormat,
-                                  const ByteStringView& szLocale);
-  static ByteString IsoDate2Local(CFXJSE_Value* pThis,
-                                  const ByteStringView& szDate,
-                                  const ByteStringView& szFormat,
-                                  const ByteStringView& szLocale);
-  static ByteString IsoTime2Local(CFXJSE_Value* pThis,
-                                  const ByteStringView& szTime,
-                                  const ByteStringView& szFormat,
-                                  const ByteStringView& szLocale);
-  static int32_t DateString2Num(const ByteStringView& szDateString);
-  static ByteString GetLocalDateFormat(CFXJSE_Value* pThis,
-                                       int32_t iStyle,
-                                       const ByteStringView& szLocalStr,
-                                       bool bStandard);
-  static ByteString GetLocalTimeFormat(CFXJSE_Value* pThis,
-                                       int32_t iStyle,
-                                       const ByteStringView& szLocalStr,
-                                       bool bStandard);
-  static ByteString GetStandardDateFormat(CFXJSE_Value* pThis,
-                                          int32_t iStyle,
-                                          const ByteStringView& szLocalStr);
-  static ByteString GetStandardTimeFormat(CFXJSE_Value* pThis,
-                                          int32_t iStyle,
-                                          const ByteStringView& szLocalStr);
-  static ByteString Num2AllTime(CFXJSE_Value* pThis,
-                                int32_t iTime,
-                                const ByteStringView& szFormat,
-                                const ByteStringView& szLocale,
-                                bool bGM);
-  static void GetLocalTimeZone(int32_t& iHour, int32_t& iMin, int32_t& iSec);
-
-  static void Apr(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void CTerm(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void FV(CFXJSE_Value* pThis,
-                 const ByteStringView& szFuncName,
-                 CFXJSE_Arguments& args);
-  static void IPmt(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void NPV(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Pmt(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void PPmt(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void PV(CFXJSE_Value* pThis,
-                 const ByteStringView& szFuncName,
-                 CFXJSE_Arguments& args);
-  static void Rate(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Term(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Choose(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static void Exists(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static void HasValue(CFXJSE_Value* pThis,
-                       const ByteStringView& szFuncName,
-                       CFXJSE_Arguments& args);
-  static void Oneof(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Within(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static void If(CFXJSE_Value* pThis,
-                 const ByteStringView& szFuncName,
-                 CFXJSE_Arguments& args);
-  static void Eval(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Ref(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void UnitType(CFXJSE_Value* pThis,
-                       const ByteStringView& szFuncName,
-                       CFXJSE_Arguments& args);
-  static void UnitValue(CFXJSE_Value* pThis,
-                        const ByteStringView& szFuncName,
-                        CFXJSE_Arguments& args);
-
-  static void At(CFXJSE_Value* pThis,
-                 const ByteStringView& szFuncName,
-                 CFXJSE_Arguments& args);
-  static void Concat(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static void Decode(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static WideString DecodeURL(const WideString& wsURLString);
-  static WideString DecodeHTML(const WideString& wsHTMLString);
-  static WideString DecodeXML(const WideString& wsXMLString);
-  static void Encode(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static WideString EncodeURL(const ByteString& szURLString);
-  static WideString EncodeHTML(const ByteString& szHTMLString);
-  static WideString EncodeXML(const ByteString& szXMLString);
-  static bool HTMLSTR2Code(const WideStringView& pData, uint32_t* iCode);
-  static bool HTMLCode2STR(uint32_t iCode, WideString* wsHTMLReserve);
-  static void Format(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static void Left(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Len(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Lower(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Ltrim(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Parse(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Replace(CFXJSE_Value* pThis,
-                      const ByteStringView& szFuncName,
-                      CFXJSE_Arguments& args);
-  static void Right(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Rtrim(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Space(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Str(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Stuff(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void Substr(CFXJSE_Value* pThis,
-                     const ByteStringView& szFuncName,
-                     CFXJSE_Arguments& args);
-  static void Uuid(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Upper(CFXJSE_Value* pThis,
-                    const ByteStringView& szFuncName,
-                    CFXJSE_Arguments& args);
-  static void WordNum(CFXJSE_Value* pThis,
-                      const ByteStringView& szFuncName,
-                      CFXJSE_Arguments& args);
-  static ByteString TrillionUS(const ByteStringView& szData);
-  static ByteString WordUS(const ByteString& szData, int32_t iStyle);
-
-  static void Get(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void Post(CFXJSE_Value* pThis,
-                   const ByteStringView& szFuncName,
-                   CFXJSE_Arguments& args);
-  static void Put(CFXJSE_Value* pThis,
-                  const ByteStringView& szFuncName,
-                  CFXJSE_Arguments& args);
-  static void assign_value_operator(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args);
-  static void logical_or_operator(CFXJSE_Value* pThis,
-                                  const ByteStringView& szFuncName,
-                                  CFXJSE_Arguments& args);
-  static void logical_and_operator(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args);
-  static void equality_operator(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args);
-  static void notequality_operator(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args);
-  static bool fm_ref_equal(CFXJSE_Value* pThis, CFXJSE_Arguments& args);
-  static void less_operator(CFXJSE_Value* pThis,
-                            const ByteStringView& szFuncName,
-                            CFXJSE_Arguments& args);
-  static void lessequal_operator(CFXJSE_Value* pThis,
-                                 const ByteStringView& szFuncName,
-                                 CFXJSE_Arguments& args);
-  static void greater_operator(CFXJSE_Value* pThis,
-                               const ByteStringView& szFuncName,
-                               CFXJSE_Arguments& args);
-  static void greaterequal_operator(CFXJSE_Value* pThis,
-                                    const ByteStringView& szFuncName,
-                                    CFXJSE_Arguments& args);
-  static void plus_operator(CFXJSE_Value* pThis,
-                            const ByteStringView& szFuncName,
-                            CFXJSE_Arguments& args);
-  static void minus_operator(CFXJSE_Value* pThis,
-                             const ByteStringView& szFuncName,
-                             CFXJSE_Arguments& args);
-  static void multiple_operator(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args);
-  static void divide_operator(CFXJSE_Value* pThis,
-                              const ByteStringView& szFuncName,
-                              CFXJSE_Arguments& args);
-  static void positive_operator(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args);
-  static void negative_operator(CFXJSE_Value* pThis,
-                                const ByteStringView& szFuncName,
-                                CFXJSE_Arguments& args);
-  static void logical_not_operator(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args);
-  static void dot_accessor(CFXJSE_Value* pThis,
-                           const ByteStringView& szFuncName,
-                           CFXJSE_Arguments& args);
-  static void dotdot_accessor(CFXJSE_Value* pThis,
-                              const ByteStringView& szFuncName,
-                              CFXJSE_Arguments& args);
-  static void eval_translation(CFXJSE_Value* pThis,
-                               const ByteStringView& szFuncName,
-                               CFXJSE_Arguments& args);
-  static void is_fm_object(CFXJSE_Value* pThis,
-                           const ByteStringView& szFuncName,
-                           CFXJSE_Arguments& args);
-  static void is_fm_array(CFXJSE_Value* pThis,
-                          const ByteStringView& szFuncName,
-                          CFXJSE_Arguments& args);
-  static void get_fm_value(CFXJSE_Value* pThis,
-                           const ByteStringView& szFuncName,
-                           CFXJSE_Arguments& args);
-  static void get_fm_jsobj(CFXJSE_Value* pThis,
-                           const ByteStringView& szFuncName,
-                           CFXJSE_Arguments& args);
-  static void fm_var_filter(CFXJSE_Value* pThis,
-                            const ByteStringView& szFuncName,
-                            CFXJSE_Arguments& args);
-  static void concat_fm_object(CFXJSE_Value* pThis,
-                               const ByteStringView& szFuncName,
-                               CFXJSE_Arguments& args);
-
-  static int32_t hvalue_get_array_length(CFXJSE_Value* pThis,
-                                         CFXJSE_Value* arg);
-  static bool simpleValueCompare(CFXJSE_Value* pThis,
-                                 CFXJSE_Value* firstValue,
-                                 CFXJSE_Value* secondValue);
-  static void unfoldArgs(
-      CFXJSE_Value* pThis,
-      CFXJSE_Arguments& args,
-      std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-      int32_t iStart = 0);
-  static void GetObjectDefaultValue(CFXJSE_Value* pObjectValue,
-                                    CFXJSE_Value* pDefaultValue);
-  static bool SetObjectDefaultValue(CFXJSE_Value* pObjectValue,
-                                    CFXJSE_Value* pNewValue);
-  static ByteString GenerateSomExpression(const ByteStringView& szName,
-                                          int32_t iIndexFlags,
-                                          int32_t iIndexValue,
-                                          bool bIsStar);
-  static bool GetObjectForName(CFXJSE_Value* pThis,
-                               CFXJSE_Value* accessorValue,
-                               const ByteStringView& szAccessorName);
-  static bool ResolveObjects(CFXJSE_Value* pThis,
-                             CFXJSE_Value* pParentValue,
-                             const ByteStringView& bsSomExp,
-                             XFA_RESOLVENODE_RS* resolveNodeRS,
-                             bool bdotAccessor,
-                             bool bHasNoResolveName);
-  static void ParseResolveResult(
-      CFXJSE_Value* pThis,
-      const XFA_RESOLVENODE_RS& resolveNodeRS,
-      CFXJSE_Value* pParentValue,
-      std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-      bool* bAttribute);
-
-  static std::unique_ptr<CFXJSE_Value> GetSimpleValue(CFXJSE_Value* pThis,
-                                                      CFXJSE_Arguments& args,
-                                                      uint32_t index);
-  static bool ValueIsNull(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static int32_t ValueToInteger(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static float ValueToFloat(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static double ValueToDouble(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static ByteString ValueToUTF8String(CFXJSE_Value* pValue);
-  static double ExtractDouble(CFXJSE_Value* pThis,
-                              CFXJSE_Value* src,
-                              bool* ret);
-
-  static bool Translate(const WideStringView& wsFormcalc,
-                        CFX_WideTextBuf* wsJavascript);
-
-  void GlobalPropertyGetter(CFXJSE_Value* pValue);
-
- private:
-  v8::Isolate* GetScriptRuntime() const { return m_pIsolate; }
-  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
-
-  void ThrowNoDefaultPropertyException(const ByteStringView& name) const;
-  void ThrowCompilerErrorException() const;
-  void ThrowDivideByZeroException() const;
-  void ThrowServerDeniedException() const;
-  void ThrowPropertyNotInObjectException(const WideString& name,
-                                         const WideString& exp) const;
-  void ThrowArgumentMismatchException() const;
-  void ThrowParamCountMismatchException(const WideString& method) const;
-  void ThrowException(const wchar_t* str, ...) const;
-
-  v8::Isolate* m_pIsolate;
-  CFXJSE_Class* m_pFMClass;
-  std::unique_ptr<CFXJSE_Value> m_pValue;
-  UnownedPtr<CXFA_Document> const m_pDocument;
-};
-
-#endif  // FXJS_CFXJSE_FORMCALC_CONTEXT_H_
diff --git a/fxjs/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/cfxjse_formcalc_context_embeddertest.cpp
deleted file mode 100644
index d48d5cc..0000000
--- a/fxjs/cfxjse_formcalc_context_embeddertest.cpp
+++ /dev/null
@@ -1,1446 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/xfa_js_embedder_test.h"
-
-class CFXJSE_FormCalcContextEmbedderTest : public XFAJSEmbedderTest {};
-
-// TODO(dsinclair): Comment out tests are broken and need to be fixed.
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateEmpty) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  const char input[] = "";
-  EXPECT_TRUE(Execute(input));
-  // TODO(dsinclair): This should probably throw as a blank formcalc script
-  // is invalid.
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateNumber) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  const char input[] = "123";
-  EXPECT_TRUE(Execute(input));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_EQ(123, value->ToInteger()) << "Program: " << input;
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Numeric) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"123 + 456", 579},
-               {"2 - 3 * 10 / 2 + 7", -6},
-               {"10 * 3 + 5 * 4", 50},
-               {"(5 - \"abc\") * 3", 15},
-               {"\"100\" / 10e1", 1},
-               {"5 + null + 3", 8},
-               // {"if (\"abc\") then\n"
-               //  "  10\n"
-               //  "else\n"
-               //  "  20\n"
-               //  "endif",
-               //  20},
-               // {"3 / 0 + 1", 0},
-               {"-(17)", -17},
-               {"-(-17)", 17},
-               {"+(17)", 17},
-               {"+(-17)", -17},
-               {"if (1 < 2) then\n1\nendif", 1},
-               {"if (\"abc\" > \"def\") then\n"
-                "  1 and 0\n"
-                "else\n"
-                "  0\n"
-                "endif",
-                0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Strings) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"\"abc\"", "abc"},
-      {"concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")",
-       "The total is 2 dollars and 57 cents."}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Booleans) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"0 and 1 or 2 > 1", true},
-               {"2 < 3 not 1 == 1", false},
-               {"\"abc\" | 2", true},
-               {"1 or 0", true},
-               {"0 | 0", false},
-               {"0 or 1 | 0 or 0", true},
-               {"1 and 0", false},
-               // {"0 & 0", true},  // TODO(dsinclair) Confirm with Reader.
-               {"0 and 1 & 0 and 0", false},
-               {"not(\"true\")", true},
-               {"not(1)", false},
-               {"3 == 3", true},
-               {"3 <> 4", true},
-               {"\"abc\" eq \"def\"", false},
-               {"\"def\" ne \"abc\"", true},
-               {"5 + 5 == 10", true},
-               {"5 + 5 <> \"10\"", false},
-               {"3 < 3", false},
-               {"3 > 4", false},
-               {"\"abc\" <= \"def\"", true},
-               {"\"def\" > \"abc\"", true},
-               {"12 >= 12", true},
-               {"\"true\" < \"false\"", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Abs) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Abs(1.03)", 1.03f}, {"Abs(-1.03)", 1.03f}, {"Abs(0)", 0.0f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Avg) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Avg(0, 32, 16)", 16.0f}, {"Avg(2.5, 17, null)", 9.75f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ceil) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Ceil(2.5875)", 3}, {"Ceil(-5.9)", -5}, {"Ceil(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Count) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Count(\"Tony\", \"Blue\", 41)", 3}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Floor) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Floor(21.3409873)", 21},
-               {"Floor(5.999965342)", 5},
-               {"Floor(3.2 * 15)", 48}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Max) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Max(234, 15, 107)", 234},
-               {"Max(\"abc\", 15, \"Tony Blue\")", 15},
-               {"Max(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Min) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Min(234, 15, 107)", 15},
-               // TODO(dsinclair): Verify with Reader; I believe this should
-               // have a return of 0.
-               // {"Min(\"abc\", 15, \"Tony Blue\")", 15},
-               {"Min(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Mod) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Mod(64, -3)", 1}, {"Mod(-13, 3)", -1}, {"Mod(\"abc\", 2)", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Round) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Round(12.389764537, 4)", 12.3898f},
-               {"Round(20/3, 2)", 6.67f},
-               {"Round(8.9897, \"abc\")", 9.0f},
-               {"Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Sum) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Sum(2, 4, 6, 8)", 20},
-               {"Sum(-2, 4, -6, 8)", 4},
-               {"Sum(4, 16, \"abc\", 19)", 39}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-// TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Date) {
-//   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-//
-//   TODO(dsinclair): Make compatible with windows.
-//   time_t seconds = time(nullptr);
-//   int days = seconds / (60 * 60 * 24);
-
-//   EXPECT_TRUE(Execute("Date()"));
-
-//   CFXJSE_Value* value = GetValue();
-//   EXPECT_TRUE(value->IsNumber());
-//   EXPECT_EQ(days, value->ToInteger());
-// }
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Date2Num) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      // {"Date2Num(\"Mar 15, 1996\")", 35138},
-      {"Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1},
-      {"Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138},
-      // {"Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277},
-      {"Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296},
-      {"Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")",
-       29}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DateFmt) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"DateFmt(1)", "M/D/YY"},
-      // {"DateFmt(2, \"fr_CA\")", "YY-MM-DD"},
-      {"DateFmt(3, \"de_DE\")", "D. MMMM YYYY"},
-      // {"DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, IsoDate2Num) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"IsoDate2Num(\"1900\")", 1},
-               {"IsoDate2Num(\"1900-01\")", 1},
-               {"IsoDate2Num(\"1900-01-01\")", 1},
-               {"IsoDate2Num(\"19960315T20:20:20\")", 35138},
-               {"IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")", 29}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_IsoTime2Num) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"IsoTime2Num(\"00:00:00Z\")", 1}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, LocalDateFmt) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {// {"LocalDateFmt(1, \"de_DE\")", "tt.MM.uu"},
-               // {"LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj"},
-               {"LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj"},
-               {"LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_LocalTimeFmt) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"LocalTimeFmt(1, \"de_DE\")", "HH:mm"},
-               {"LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss"},
-               {"LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z"},
-               {"LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Num2Date) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900"},
-      {"Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")", "16-Mrz-1996"},
-      // {"Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", "
-      //  "\"YY-MM-DD\", \"fr_CA\"))",
-      //  "Jan 1, 1902"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString()) << "Program: " << tests[i].program;
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2GMTime) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {// Broken on Windows only.
-               {"Num2GMTime(1, \"HH:MM:SS\")", "00:00:00"},
-               // Below broken on other platforms.
-               {"Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT"},
-               {"Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")",
-                "12.13 Uhr GMT"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-// TODO(dsinclair): Broken on Mac ...
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2Time) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Num2Time(1, \"HH:MM:SS\")", "00:00:00"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-// TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Time) {
-//   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-//   TODO(dsinclair): Make compatible with windows.
-//   struct timeval tp;
-//   gettimeofday(&tp, nullptr);
-
-//   EXPECT_TRUE(Execute("Time()"));
-
-//   CFXJSE_Value* value = GetValue();
-//   EXPECT_TRUE(value->IsInteger());
-//   EXPECT_EQ(tp.tv_sec * 1000L + tp.tv_usec / 1000, value->ToInteger())
-//       << "Program: Time()";
-// }
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2Num) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      // {"Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1},
-      {"Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")", 47593001}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, TimeFmt) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"TimeFmt(1)", "h::MM A"},
-      {"TimeFmt(2, \"fr_CA\")", "HH:MM:SS"},
-      {"TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z"},
-      // {"TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Apr) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Apr(35000, 269.50, 360)", 0.08515404566f},
-               {"Apr(210000 * 0.75, 850 + 110, 25 * 26)", 0.07161332404f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, CTerm) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      // {"CTerm(0.02, 1000, 100)", 116.2767474515f},
-      {"CTerm(0.10, 500000, 12000)", 39.13224648502f},
-      // {"CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)", 176.02226044975f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, FV) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f},
-               {"FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, IPmt) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f},
-               {"IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f},
-               {"IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_NPV) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"NPV(0.065, 5000)", 4694.83568075117f},
-               {"NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f},
-               {"NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Pmt) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {// {"Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f},
-               {"Pmt(25000, 0.085, 12)", 3403.82145169876f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, PPmt) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f},
-      {"PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f},
-      // {"PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, PV) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f},
-      // {"PV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Rate) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Rate(12000, 8000, 5)", 0.0844717712f},
-               {"Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Term) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {// {"Term(475, .05, 1500)", 3.00477517728f},
-               {"Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Choose) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")", "Person"},
-      {"Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9"},
-      {"Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")",
-       "F"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Exists) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  EXPECT_TRUE(Execute("Exists(\"hello world\")"));
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_FALSE(value->ToBoolean());
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, HasValue) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"HasValue(2)", true}, {"HasValue(\" \")", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Oneof) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {
-      {"Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true},
-      {"Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")",
-       true},
-      {"Oneof(3, 1, 25)", false},
-      {"Oneof(3, 3, null)", true},
-      {"Oneof(3, null, null)", false},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Within) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"Within(\"C\", \"A\", \"D\")", true},
-               {"Within(1.5, 0, 2)", true},
-               {"Within(-1, 0, 2)", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Eval) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"eval(\"10*3+5*4\")", 50}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Null) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Null()", "null"},
-               {"Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-
-  EXPECT_TRUE(Execute("Null() + 5"));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_EQ(5, value->ToInteger());
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ref) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Ref(\"10*3+5*4\")", "10*3+5*4"}, {"Ref(\"hello\")", "hello"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitType) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"UnitType(\"36 in\")", "in"},
-               {"UnitType(\"2.54centimeters\")", "cm"},
-               {"UnitType(\"picas\")", "pt"},
-               {"UnitType(\"2.cm\")", "cm"},
-               {"UnitType(\"2.zero cm\")", "in"},
-               {"UnitType(\"kilometers\")", "in"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitValue) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"UnitValue(\"2in\")", 2.0f}, {"UnitValue(\"2in\", \"cm\")", 5.08f},
-      // {"UnitValue(\"6\", \"pt\")", 432f},
-      // {"UnitType(\"A\", \"cm\")", 0.0f},
-      // {"UnitType(\"5.08cm\", \"kilograms\")", 2.0f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, At) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"At(\"ABCDEFGH\", \"AB\")", 1},
-               {"At(\"ABCDEFGH\", \"F\")", 6},
-               {"At(23412931298471, 29)", 5}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Concat) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Concat(\"ABC\", \"DEF\")", "ABCDEF"},
-               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"},
-               {"Concat(\"You owe \", WordNum(1154.67, 2), \".\")",
-                "You owe One Thousand One Hundred Fifty-four Dollars And "
-                "Sixty-seven Cents."}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Decode) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Decode(\"&AElig;&Aacute;&Acirc;&Aacute;&Acirc;\", \"html\")", "ÆÁÂÁÂ"},
-      // {"Decode(\"~!@#$%%^&amp;*()_+|`{&quot;}[]&lt;&gt;?,./;&apos;:\", "
-      //  "\"xml\")",
-      //  "~!@#$%%^&*()_+|`{"
-      //  "}[]<>?,./;':"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Encode) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Encode(\"\"\"hello, world!\"\"\", \"url\")",
-       "%%22hello,%%20world!%%22"},
-      {"Encode(\"ÁÂÃÄÅÆ\", \"html\")", "&#xc1;&#Xc2;&#Xc3;&#xc4;&#xc5;&#xc6;"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Format) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002"},
-               {"Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Left) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Left(\"ABCDEFGH\", 3)", "ABC"},
-               {"Left(\"Tony Blue\", 5)", "Tony "}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Len) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      {"Len(\"ABCDEFGH\")", 8}, {"Len(4)", 1}, {"Len(Str(4.532, 6, 4))", 6}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Lower) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Lower(\"ABC\")", "abc"},
-               {"Lower(\"21 Main St.\")", "21 main st."},
-               {"Lower(15)", "15"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ltrim) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Ltrim(\"   ABCD\")", "ABCD"},
-               {"Ltrim(Rtrim(\"    Tony Blue    \"))", "Tony Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Parse) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-
-  EXPECT_TRUE(Execute("Parse(\"$9,999,999.99\", \"$1,234,567.89\")"));
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsNumber());
-  EXPECT_FLOAT_EQ(1234567.89f, value->ToFloat());
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Replace) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Replace(\"Tony Blue\", \"Tony\", \"Chris\")", "Chris Blue"},
-               {"Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH"},
-               {"Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Right) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Right(\"ABCDEFGH\", 3)", "FGH"},
-               {"Right(\"Tony Blue\", 5)", " Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rtrim) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Rtrim(\"ABCD   \")", "ABCD"},
-               {"Rtrim(\"Tony Blue      \t\")", "Tony Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Space) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Space(5)", "     "},
-               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Str) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Str(2.456)", "         2"},
-               {"Str(4.532, 6, 4)", "4.5320"},
-               {"Str(234.458, 4)", " 234"},
-               {"Str(31.2345, 4, 2)", "****"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Stuff) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue"},
-               {"Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH"},
-               {"Stuff(\"members-list@myweb.com\", 0, 0, \"cc:\")",
-                "cc:members-list@myweb.com"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Substr) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Substr(\"ABCDEFG\", 3, 4)", "CDEF"},
-               {"Substr(3214, 2, 1)", "2"},
-               {"Substr(\"ABCDEFG\", 5, 0)", ""},
-               {"Substr(\"21 Waterloo St.\", 4, 5)", "Water"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Uuid) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  EXPECT_TRUE(Execute("Uuid()"));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString());
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Upper) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Upper(\"abc\")", "ABC"},
-               {"Upper(\"21 Main St.\")", "21 MAIN ST."},
-               {"Upper(15)", "15"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, WordNum) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"WordNum(123.45)",
-      //  "One Hundred and Twenty-three"},  // This looks like it's wrong in the
-      //                                    // Formcalc document.
-      // {"WordNum(123.45, 1)", "One Hundred and Twenty-three Dollars"},
-      {"WordNum(1154.67, 2)",
-       "One Thousand One Hundred Fifty-four Dollars And Sixty-seven Cents"},
-      {"WordNum(43, 2)", "Forty-three Dollars And Zero Cents"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '"
-        << value->ToString().c_str() << "'";
-  }
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Get) {
-  // TODO(dsinclair): Is this supported?
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Post) {
-  // TODO(dsinclair): Is this supported?
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, Put) {
-  // TODO(dsinclair): Is this supported?
-}
-
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, InvalidFunctions) {
-  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  const char* const tests[] = {
-      "F()", "()", "()()()", "Round(2.0)()",
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_FALSE(ExecuteSilenceFailure(tests[i]));
-  }
-}
diff --git a/fxjs/cfxjse_isolatetracker.h b/fxjs/cfxjse_isolatetracker.h
deleted file mode 100644
index 2bb16ca..0000000
--- a/fxjs/cfxjse_isolatetracker.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_ISOLATETRACKER_H_
-#define FXJS_CFXJSE_ISOLATETRACKER_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "v8/include/v8.h"
-
-#include "fxjs/cfxjse_runtimedata.h"
-
-class CFXJSE_ScopeUtil_IsolateHandle {
- public:
-  explicit CFXJSE_ScopeUtil_IsolateHandle(v8::Isolate* pIsolate)
-      : m_isolate(pIsolate), m_iscope(pIsolate), m_hscope(pIsolate) {}
-  v8::Isolate* GetIsolate() { return m_isolate; }
-
- private:
-  CFXJSE_ScopeUtil_IsolateHandle(const CFXJSE_ScopeUtil_IsolateHandle&) =
-      delete;
-  void operator=(const CFXJSE_ScopeUtil_IsolateHandle&) = delete;
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  v8::Isolate* m_isolate;
-  v8::Isolate::Scope m_iscope;
-  v8::HandleScope m_hscope;
-};
-
-class CFXJSE_ScopeUtil_IsolateHandleRootContext {
- public:
-  explicit CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate)
-      : m_parent(pIsolate),
-        m_cscope(v8::Local<v8::Context>::New(
-            pIsolate,
-            CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext)) {}
-
- private:
-  CFXJSE_ScopeUtil_IsolateHandleRootContext(
-      const CFXJSE_ScopeUtil_IsolateHandleRootContext&) = delete;
-  void operator=(const CFXJSE_ScopeUtil_IsolateHandleRootContext&) = delete;
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  CFXJSE_ScopeUtil_IsolateHandle m_parent;
-  v8::Context::Scope m_cscope;
-};
-
-#endif  // FXJS_CFXJSE_ISOLATETRACKER_H_
diff --git a/fxjs/cfxjse_resolveprocessor.cpp b/fxjs/cfxjse_resolveprocessor.cpp
deleted file mode 100644
index 2ca0838..0000000
--- a/fxjs/cfxjse_resolveprocessor.cpp
+++ /dev/null
@@ -1,746 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_resolveprocessor.h"
-
-#include <algorithm>
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodehelper.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
-#include "xfa/fxfa/parser/cxfa_occur.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor()
-    : m_iCurStart(0), m_pNodeHelper(pdfium::MakeUnique<CXFA_NodeHelper>()) {}
-
-CFXJSE_ResolveProcessor::~CFXJSE_ResolveProcessor() {}
-
-bool CFXJSE_ResolveProcessor::Resolve(CFXJSE_ResolveNodeData& rnd) {
-  if (!rnd.m_CurObject)
-    return false;
-
-  if (!rnd.m_CurObject->IsNode()) {
-    if (rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) {
-      return ResolveForAttributeRs(rnd.m_CurObject, rnd,
-                                   rnd.m_wsName.AsStringView());
-    }
-    return false;
-  }
-  if (rnd.m_dwStyles & XFA_RESOLVENODE_AnyChild)
-    return ResolveAnyChild(rnd);
-
-  if (rnd.m_wsName.GetLength()) {
-    wchar_t wch = rnd.m_wsName[0];
-    switch (wch) {
-      case '$':
-        return ResolveDollar(rnd);
-      case '!':
-        return ResolveExcalmatory(rnd);
-      case '#':
-        return ResolveNumberSign(rnd);
-      case '*':
-        return ResolveAsterisk(rnd);
-      // TODO(dsinclair): We could probably remove this.
-      case '.':
-        return ResolveAnyChild(rnd);
-      default:
-        break;
-    }
-  }
-  if (rnd.m_uHashName == XFA_HASHCODE_This && rnd.m_nLevel == 0) {
-    rnd.m_Objects.push_back(rnd.m_pSC->GetThisObject());
-    return true;
-  }
-  if (rnd.m_CurObject->GetElementType() == XFA_Element::Xfa) {
-    CXFA_Object* pObjNode =
-        rnd.m_pSC->GetDocument()->GetXFAObject(rnd.m_uHashName);
-    if (pObjNode) {
-      rnd.m_Objects.push_back(pObjNode);
-    } else if (rnd.m_uHashName == XFA_HASHCODE_Xfa) {
-      rnd.m_Objects.push_back(rnd.m_CurObject);
-    } else if ((rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) &&
-               ResolveForAttributeRs(rnd.m_CurObject, rnd,
-                                     rnd.m_wsName.AsStringView())) {
-      return true;
-    }
-    if (!rnd.m_Objects.empty())
-      FilterCondition(rnd, rnd.m_wsCondition);
-
-    return !rnd.m_Objects.empty();
-  }
-  if (!ResolveNormal(rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa)
-    rnd.m_Objects.push_back(rnd.m_pSC->GetDocument()->GetRoot());
-
-  return !rnd.m_Objects.empty();
-}
-
-bool CFXJSE_ResolveProcessor::ResolveAnyChild(CFXJSE_ResolveNodeData& rnd) {
-  WideString wsName = rnd.m_wsName;
-  WideString wsCondition = rnd.m_wsCondition;
-  CXFA_Node* findNode = nullptr;
-  bool bClassName = false;
-  if (wsName.GetLength() && wsName[0] == '#') {
-    bClassName = true;
-    wsName = wsName.Right(wsName.GetLength() - 1);
-  }
-  findNode = m_pNodeHelper->ResolveNodes_GetOneChild(
-      ToNode(rnd.m_CurObject), wsName.c_str(), bClassName);
-  if (!findNode)
-    return false;
-
-  if (wsCondition.IsEmpty()) {
-    rnd.m_Objects.push_back(findNode);
-    return !rnd.m_Objects.empty();
-  }
-
-  std::vector<CXFA_Node*> tempNodes;
-  for (auto* pObject : rnd.m_Objects)
-    tempNodes.push_back(pObject->AsNode());
-  m_pNodeHelper->CountSiblings(findNode, XFA_LOGIC_Transparent, &tempNodes,
-                               bClassName);
-  rnd.m_Objects = std::vector<CXFA_Object*>(tempNodes.begin(), tempNodes.end());
-  FilterCondition(rnd, wsCondition);
-  return !rnd.m_Objects.empty();
-}
-
-bool CFXJSE_ResolveProcessor::ResolveDollar(CFXJSE_ResolveNodeData& rnd) {
-  WideString wsName = rnd.m_wsName;
-  WideString wsCondition = rnd.m_wsCondition;
-  int32_t iNameLen = wsName.GetLength();
-  if (iNameLen == 1) {
-    rnd.m_Objects.push_back(rnd.m_CurObject);
-    return true;
-  }
-  if (rnd.m_nLevel > 0)
-    return false;
-
-  XFA_HashCode dwNameHash = static_cast<XFA_HashCode>(FX_HashCode_GetW(
-      WideStringView(wsName.c_str() + 1, iNameLen - 1), false));
-  if (dwNameHash == XFA_HASHCODE_Xfa) {
-    rnd.m_Objects.push_back(rnd.m_pSC->GetDocument()->GetRoot());
-  } else {
-    CXFA_Object* pObjNode = rnd.m_pSC->GetDocument()->GetXFAObject(dwNameHash);
-    if (pObjNode)
-      rnd.m_Objects.push_back(pObjNode);
-  }
-  if (!rnd.m_Objects.empty())
-    FilterCondition(rnd, wsCondition);
-
-  return !rnd.m_Objects.empty();
-}
-
-bool CFXJSE_ResolveProcessor::ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd) {
-  if (rnd.m_nLevel > 0)
-    return false;
-
-  CXFA_Node* datasets =
-      ToNode(rnd.m_pSC->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
-  if (!datasets)
-    return false;
-
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC);
-  rndFind.m_CurObject = datasets;
-  rndFind.m_wsName = rnd.m_wsName.Right(rnd.m_wsName.GetLength() - 1);
-  rndFind.m_uHashName = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
-  rndFind.m_nLevel = rnd.m_nLevel + 1;
-  rndFind.m_dwStyles = XFA_RESOLVENODE_Children;
-  rndFind.m_wsCondition = rnd.m_wsCondition;
-  Resolve(rndFind);
-
-  rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                       rndFind.m_Objects.end());
-  return !rnd.m_Objects.empty();
-}
-
-bool CFXJSE_ResolveProcessor::ResolveNumberSign(CFXJSE_ResolveNodeData& rnd) {
-  WideString wsName = rnd.m_wsName.Right(rnd.m_wsName.GetLength() - 1);
-  WideString wsCondition = rnd.m_wsCondition;
-  CXFA_Node* curNode = ToNode(rnd.m_CurObject);
-  if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
-    return true;
-
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC);
-  rndFind.m_nLevel = rnd.m_nLevel + 1;
-  rndFind.m_dwStyles = rnd.m_dwStyles;
-  rndFind.m_dwStyles |= XFA_RESOLVENODE_TagName;
-  rndFind.m_dwStyles &= ~XFA_RESOLVENODE_Attributes;
-  rndFind.m_wsName = wsName;
-  rndFind.m_uHashName = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
-  rndFind.m_wsCondition = wsCondition;
-  rndFind.m_CurObject = curNode;
-  ResolveNormal(rndFind);
-  if (rndFind.m_Objects.empty())
-    return false;
-
-  if (wsCondition.GetLength() == 0 &&
-      pdfium::ContainsValue(rndFind.m_Objects, curNode)) {
-    rnd.m_Objects.push_back(curNode);
-  } else {
-    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                         rndFind.m_Objects.end());
-  }
-  return !rnd.m_Objects.empty();
-}
-
-bool CFXJSE_ResolveProcessor::ResolveForAttributeRs(
-    CXFA_Object* curNode,
-    CFXJSE_ResolveNodeData& rnd,
-    const WideStringView& strAttr) {
-  const XFA_SCRIPTATTRIBUTEINFO* lpScriptAttribute =
-      XFA_GetScriptAttributeByName(curNode->GetElementType(), strAttr);
-  if (!lpScriptAttribute)
-    return false;
-
-  rnd.m_pScriptAttribute = lpScriptAttribute;
-  rnd.m_Objects.push_back(curNode);
-  rnd.m_dwFlag = XFA_ResolveNode_RSType_Attribute;
-  return true;
-}
-
-bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) {
-  if (rnd.m_nLevel > 32 || !rnd.m_CurObject->IsNode())
-    return false;
-
-  CXFA_Node* curNode = rnd.m_CurObject->AsNode();
-  size_t nNum = rnd.m_Objects.size();
-  uint32_t dwStyles = rnd.m_dwStyles;
-  WideString& wsName = rnd.m_wsName;
-  XFA_HashCode uNameHash = rnd.m_uHashName;
-  WideString& wsCondition = rnd.m_wsCondition;
-
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC);
-  rndFind.m_wsName = rnd.m_wsName;
-  rndFind.m_wsCondition = rnd.m_wsCondition;
-  rndFind.m_nLevel = rnd.m_nLevel + 1;
-  rndFind.m_uHashName = uNameHash;
-
-  std::vector<CXFA_Node*> children;
-  std::vector<CXFA_Node*> properties;
-  CXFA_Node* pVariablesNode = nullptr;
-  CXFA_Node* pPageSetNode = nullptr;
-  for (CXFA_Node* pChild = curNode->GetFirstChild(); pChild;
-       pChild = pChild->GetNextSibling()) {
-    if (pChild->GetElementType() == XFA_Element::Variables) {
-      pVariablesNode = pChild;
-      continue;
-    }
-    if (pChild->GetElementType() == XFA_Element::PageSet) {
-      pPageSetNode = pChild;
-      continue;
-    }
-    if (curNode->HasProperty(pChild->GetElementType()))
-      properties.push_back(pChild);
-    else
-      children.push_back(pChild);
-  }
-  if ((dwStyles & XFA_RESOLVENODE_Properties) && pVariablesNode) {
-    if (pVariablesNode->GetClassHashCode() == uNameHash) {
-      rnd.m_Objects.push_back(pVariablesNode);
-    } else {
-      rndFind.m_CurObject = pVariablesNode;
-      SetStylesForChild(dwStyles, rndFind);
-      WideString wsSaveCondition = rndFind.m_wsCondition;
-      rndFind.m_wsCondition.clear();
-      ResolveNormal(rndFind);
-      rndFind.m_wsCondition = wsSaveCondition;
-      rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                           rndFind.m_Objects.end());
-      rndFind.m_Objects.clear();
-    }
-    if (rnd.m_Objects.size() > nNum) {
-      FilterCondition(rnd, wsCondition);
-      return !rnd.m_Objects.empty();
-    }
-  }
-
-  if (dwStyles & XFA_RESOLVENODE_Children) {
-    bool bSetFlag = false;
-    if (pPageSetNode && (dwStyles & XFA_RESOLVENODE_Properties))
-      children.push_back(pPageSetNode);
-
-    for (CXFA_Node* child : children) {
-      if (dwStyles & XFA_RESOLVENODE_TagName) {
-        if (child->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.push_back(child);
-      } else if (child->GetNameHash() == uNameHash) {
-        rnd.m_Objects.push_back(child);
-      }
-
-      if (m_pNodeHelper->NodeIsTransparent(child) &&
-          child->GetElementType() != XFA_Element::PageSet) {
-        if (!bSetFlag) {
-          SetStylesForChild(dwStyles, rndFind);
-          bSetFlag = true;
-        }
-        rndFind.m_CurObject = child;
-
-        WideString wsSaveCondition = rndFind.m_wsCondition;
-        rndFind.m_wsCondition.clear();
-        ResolveNormal(rndFind);
-
-        rndFind.m_wsCondition = wsSaveCondition;
-        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                             rndFind.m_Objects.end());
-        rndFind.m_Objects.clear();
-      }
-    }
-    if (rnd.m_Objects.size() > nNum) {
-      if (!(dwStyles & XFA_RESOLVENODE_ALL)) {
-        std::vector<CXFA_Node*> upArrayNodes;
-        if (m_pNodeHelper->NodeIsTransparent(ToNode(curNode))) {
-          m_pNodeHelper->CountSiblings(ToNode(rnd.m_Objects[0]),
-                                       XFA_LOGIC_Transparent, &upArrayNodes,
-                                       !!(dwStyles & XFA_RESOLVENODE_TagName));
-        }
-        if (upArrayNodes.size() > rnd.m_Objects.size()) {
-          CXFA_Object* pSaveObject = rnd.m_Objects.front();
-          rnd.m_Objects = std::vector<CXFA_Object*>(upArrayNodes.begin(),
-                                                    upArrayNodes.end());
-          rnd.m_Objects.front() = pSaveObject;
-        }
-      }
-      FilterCondition(rnd, wsCondition);
-      return !rnd.m_Objects.empty();
-    }
-  }
-  if (dwStyles & XFA_RESOLVENODE_Attributes) {
-    if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
-      return 1;
-  }
-  if (dwStyles & XFA_RESOLVENODE_Properties) {
-    for (CXFA_Node* pChildProperty : properties) {
-      if (pChildProperty->IsUnnamed()) {
-        if (pChildProperty->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.push_back(pChildProperty);
-        continue;
-      }
-      if (pChildProperty->GetNameHash() == uNameHash &&
-          pChildProperty->GetElementType() != XFA_Element::Extras &&
-          pChildProperty->GetElementType() != XFA_Element::Items) {
-        rnd.m_Objects.push_back(pChildProperty);
-      }
-    }
-    if (rnd.m_Objects.size() > nNum) {
-      FilterCondition(rnd, wsCondition);
-      return !rnd.m_Objects.empty();
-    }
-
-    CXFA_Node* pProp = nullptr;
-    if (XFA_Element::Subform == curNode->GetElementType() &&
-        XFA_HASHCODE_Occur == uNameHash) {
-      CXFA_Node* pInstanceManager =
-          curNode->AsNode()->GetInstanceMgrOfSubform();
-      if (pInstanceManager) {
-        pProp = pInstanceManager->JSObject()->GetOrCreateProperty<CXFA_Occur>(
-            0, XFA_Element::Occur);
-      }
-    } else {
-      XFA_Element eType = CXFA_Node::NameToElement(wsName);
-      if (eType == XFA_Element::PageSet) {
-        pProp = curNode->AsNode()->JSObject()->GetProperty<CXFA_Node>(0, eType);
-      } else if (eType != XFA_Element::Unknown) {
-        pProp = curNode->AsNode()->JSObject()->GetOrCreateProperty<CXFA_Node>(
-            0, eType);
-      }
-    }
-    if (pProp) {
-      rnd.m_Objects.push_back(pProp);
-      return !rnd.m_Objects.empty();
-    }
-  }
-
-  CXFA_Node* parentNode = m_pNodeHelper->ResolveNodes_GetParent(
-      curNode->AsNode(), XFA_LOGIC_NoTransparent);
-  uint32_t uCurClassHash = curNode->GetClassHashCode();
-  if (!parentNode) {
-    if (uCurClassHash == uNameHash) {
-      rnd.m_Objects.push_back(curNode->AsNode());
-      FilterCondition(rnd, wsCondition);
-      if (!rnd.m_Objects.empty())
-        return true;
-    }
-    return false;
-  }
-
-  if (dwStyles & XFA_RESOLVENODE_Siblings) {
-    CXFA_Node* child = parentNode->GetFirstChild();
-    uint32_t dwSubStyles =
-        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties;
-    if (dwStyles & XFA_RESOLVENODE_TagName)
-      dwSubStyles |= XFA_RESOLVENODE_TagName;
-    if (dwStyles & XFA_RESOLVENODE_ALL)
-      dwSubStyles |= XFA_RESOLVENODE_ALL;
-
-    rndFind.m_dwStyles = dwSubStyles;
-    while (child) {
-      if (child == curNode) {
-        if (dwStyles & XFA_RESOLVENODE_TagName) {
-          if (uCurClassHash == uNameHash)
-            rnd.m_Objects.push_back(curNode);
-        } else {
-          if (child->GetNameHash() == uNameHash) {
-            rnd.m_Objects.push_back(curNode);
-            if (rnd.m_nLevel == 0 && wsCondition.GetLength() == 0) {
-              rnd.m_Objects.clear();
-              rnd.m_Objects.push_back(curNode);
-              return true;
-            }
-          }
-        }
-        child = child->GetNextSibling();
-        continue;
-      }
-
-      if (dwStyles & XFA_RESOLVENODE_TagName) {
-        if (child->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.push_back(child);
-      } else if (child->GetNameHash() == uNameHash) {
-        rnd.m_Objects.push_back(child);
-      }
-
-      bool bInnerSearch = false;
-      if (parentNode->HasProperty(child->GetElementType())) {
-        if ((child->GetElementType() == XFA_Element::Variables ||
-             child->GetElementType() == XFA_Element::PageSet)) {
-          bInnerSearch = true;
-        }
-      } else if (m_pNodeHelper->NodeIsTransparent(child)) {
-        bInnerSearch = true;
-      }
-      if (bInnerSearch) {
-        rndFind.m_CurObject = child;
-        WideString wsOriginCondition = rndFind.m_wsCondition;
-        rndFind.m_wsCondition.clear();
-
-        uint32_t dwOriginStyle = rndFind.m_dwStyles;
-        rndFind.m_dwStyles = dwOriginStyle | XFA_RESOLVENODE_ALL;
-        ResolveNormal(rndFind);
-
-        rndFind.m_dwStyles = dwOriginStyle;
-        rndFind.m_wsCondition = wsOriginCondition;
-        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                             rndFind.m_Objects.end());
-        rndFind.m_Objects.clear();
-      }
-      child = child->GetNextSibling();
-    }
-    if (rnd.m_Objects.size() > nNum) {
-      if (m_pNodeHelper->NodeIsTransparent(parentNode)) {
-        std::vector<CXFA_Node*> upArrayNodes;
-        m_pNodeHelper->CountSiblings(ToNode(rnd.m_Objects.front()),
-                                     XFA_LOGIC_Transparent, &upArrayNodes,
-                                     !!(dwStyles & XFA_RESOLVENODE_TagName));
-        if (upArrayNodes.size() > rnd.m_Objects.size()) {
-          CXFA_Object* pSaveObject = rnd.m_Objects.front();
-          rnd.m_Objects = std::vector<CXFA_Object*>(upArrayNodes.begin(),
-                                                    upArrayNodes.end());
-          rnd.m_Objects.front() = pSaveObject;
-        }
-      }
-      FilterCondition(rnd, wsCondition);
-      return !rnd.m_Objects.empty();
-    }
-  }
-
-  if (dwStyles & XFA_RESOLVENODE_Parent) {
-    uint32_t dwSubStyles = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent |
-                           XFA_RESOLVENODE_Properties;
-    if (dwStyles & XFA_RESOLVENODE_TagName)
-      dwSubStyles |= XFA_RESOLVENODE_TagName;
-    if (dwStyles & XFA_RESOLVENODE_ALL)
-      dwSubStyles |= XFA_RESOLVENODE_ALL;
-
-    rndFind.m_dwStyles = dwSubStyles;
-    rndFind.m_CurObject = parentNode;
-    rnd.m_pSC->GetUpObjectArray()->push_back(parentNode);
-    ResolveNormal(rndFind);
-    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                         rndFind.m_Objects.end());
-    rndFind.m_Objects.clear();
-    if (rnd.m_Objects.size() > nNum)
-      return true;
-  }
-  return false;
-}
-
-bool CFXJSE_ResolveProcessor::ResolveAsterisk(CFXJSE_ResolveNodeData& rnd) {
-  CXFA_Node* curNode = ToNode(rnd.m_CurObject);
-  std::vector<CXFA_Node*> array =
-      curNode->GetNodeList(XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties,
-                           XFA_Element::Unknown);
-  rnd.m_Objects.insert(rnd.m_Objects.end(), array.begin(), array.end());
-  return !rnd.m_Objects.empty();
-}
-
-int32_t CFXJSE_ResolveProcessor::GetFilter(const WideStringView& wsExpression,
-                                           int32_t nStart,
-                                           CFXJSE_ResolveNodeData& rnd) {
-  ASSERT(nStart > -1);
-
-  int32_t iLength = wsExpression.GetLength();
-  if (nStart >= iLength)
-    return 0;
-
-  WideString& wsName = rnd.m_wsName;
-  WideString& wsCondition = rnd.m_wsCondition;
-  wchar_t* pNameBuf = wsName.GetBuffer(iLength - nStart);
-  wchar_t* pConditionBuf = wsCondition.GetBuffer(iLength - nStart);
-  int32_t nNameCount = 0;
-  int32_t nConditionCount = 0;
-  std::vector<int32_t> stack;
-  int32_t nType = -1;
-  const wchar_t* pSrc = wsExpression.unterminated_c_str();
-  wchar_t wPrev = 0;
-  wchar_t wCur;
-  bool bIsCondition = false;
-  while (nStart < iLength) {
-    wCur = pSrc[nStart++];
-    if (wCur == '.') {
-      if (wPrev == '\\') {
-        pNameBuf[nNameCount - 1] = wPrev = '.';
-        continue;
-      }
-      if (nNameCount == 0) {
-        rnd.m_dwStyles |= XFA_RESOLVENODE_AnyChild;
-        continue;
-      }
-
-      wchar_t wLookahead = nStart < iLength ? pSrc[nStart] : 0;
-      if (wLookahead != '[' && wLookahead != '(' && nType < 0)
-        break;
-    }
-    if (wCur == '[' || wCur == '(') {
-      bIsCondition = true;
-    } else if (wCur == '.' && nStart < iLength &&
-               (pSrc[nStart] == '[' || pSrc[nStart] == '(')) {
-      bIsCondition = true;
-    }
-    if (bIsCondition)
-      pConditionBuf[nConditionCount++] = wCur;
-    else
-      pNameBuf[nNameCount++] = wCur;
-
-    if ((nType == 0 && wCur == ']') || (nType == 1 && wCur == ')') ||
-        (nType == 2 && wCur == '"')) {
-      nType = stack.empty() ? -1 : stack.back();
-      if (!stack.empty())
-        stack.pop_back();
-    } else if (wCur == '[') {
-      stack.push_back(nType);
-      nType = 0;
-    } else if (wCur == '(') {
-      stack.push_back(nType);
-      nType = 1;
-    } else if (wCur == '"') {
-      stack.push_back(nType);
-      nType = 2;
-    }
-    wPrev = wCur;
-  }
-  if (!stack.empty())
-    return -1;
-
-  wsName.ReleaseBuffer(nNameCount);
-  wsName.Trim();
-  wsCondition.ReleaseBuffer(nConditionCount);
-  wsCondition.Trim();
-  rnd.m_uHashName =
-      static_cast<XFA_HashCode>(FX_HashCode_GetW(wsName.AsStringView(), false));
-  return nStart;
-}
-
-void CFXJSE_ResolveProcessor::ConditionArray(int32_t iCurIndex,
-                                             WideString wsCondition,
-                                             int32_t iFoundCount,
-                                             CFXJSE_ResolveNodeData& rnd) {
-  int32_t iLen = wsCondition.GetLength();
-  bool bRelative = false;
-  bool bAll = false;
-  int32_t i = 1;
-  for (; i < iLen; ++i) {
-    wchar_t ch = wsCondition[i];
-    if (ch == ' ')
-      continue;
-    if (ch == '+' || ch == '-')
-      bRelative = true;
-    else if (ch == '*')
-      bAll = true;
-
-    break;
-  }
-  if (bAll) {
-    if (rnd.m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-      if (rnd.m_dwStyles & XFA_RESOLVENODE_Bind) {
-        m_pNodeHelper->m_pCreateParent = ToNode(rnd.m_CurObject);
-        m_pNodeHelper->m_iCreateCount = 1;
-        rnd.m_Objects.clear();
-        m_pNodeHelper->m_iCurAllStart = -1;
-        m_pNodeHelper->m_pAllStartParent = nullptr;
-      } else if (m_pNodeHelper->m_iCurAllStart == -1) {
-        m_pNodeHelper->m_iCurAllStart = m_iCurStart;
-        m_pNodeHelper->m_pAllStartParent = ToNode(rnd.m_CurObject);
-      }
-    } else if (rnd.m_dwStyles & XFA_RESOLVENODE_BindNew) {
-      if (m_pNodeHelper->m_iCurAllStart == -1)
-        m_pNodeHelper->m_iCurAllStart = m_iCurStart;
-    }
-    return;
-  }
-  if (iFoundCount == 1 && !iLen)
-    return;
-
-  int32_t iIndex = wsCondition.Mid(i, iLen - 1 - i).GetInteger();
-  if (bRelative)
-    iIndex += iCurIndex;
-
-  if (iFoundCount <= iIndex || iIndex < 0) {
-    if (rnd.m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-      m_pNodeHelper->m_pCreateParent = ToNode(rnd.m_CurObject);
-      m_pNodeHelper->m_iCreateCount = iIndex - iFoundCount + 1;
-    }
-    rnd.m_Objects.clear();
-  } else {
-    CXFA_Object* ret = rnd.m_Objects[iIndex];
-    rnd.m_Objects.clear();
-    rnd.m_Objects.push_back(ret);
-  }
-}
-
-void CFXJSE_ResolveProcessor::DoPredicateFilter(int32_t iCurIndex,
-                                                WideString wsCondition,
-                                                int32_t iFoundCount,
-                                                CFXJSE_ResolveNodeData& rnd) {
-  ASSERT(iFoundCount == pdfium::CollectionSize<int32_t>(rnd.m_Objects));
-  WideString wsExpression;
-  CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown;
-  if (wsCondition.Left(2) == L".[" && wsCondition.Last() == L']')
-    eLangType = CXFA_Script::Type::Formcalc;
-  else if (wsCondition.Left(2) == L".(" && wsCondition.Last() == L')')
-    eLangType = CXFA_Script::Type::Javascript;
-  else
-    return;
-
-  CFXJSE_Engine* pContext = rnd.m_pSC;
-  wsExpression = wsCondition.Mid(2, wsCondition.GetLength() - 3);
-  for (int32_t i = iFoundCount - 1; i >= 0; i--) {
-    auto pRetValue = pdfium::MakeUnique<CFXJSE_Value>(rnd.m_pSC->GetIsolate());
-    bool bRet = pContext->RunScript(eLangType, wsExpression.AsStringView(),
-                                    pRetValue.get(), rnd.m_Objects[i]);
-    if (!bRet || !pRetValue->ToBoolean())
-      rnd.m_Objects.erase(rnd.m_Objects.begin() + i);
-  }
-}
-
-void CFXJSE_ResolveProcessor::FilterCondition(CFXJSE_ResolveNodeData& rnd,
-                                              WideString wsCondition) {
-  int32_t iCurrIndex = 0;
-  const std::vector<CXFA_Node*>* pArray = rnd.m_pSC->GetUpObjectArray();
-  if (!pArray->empty()) {
-    CXFA_Node* curNode = pArray->back();
-    bool bIsProperty = m_pNodeHelper->NodeIsProperty(curNode);
-    if (curNode->IsUnnamed() ||
-        (bIsProperty && curNode->GetElementType() != XFA_Element::PageSet)) {
-      iCurrIndex = m_pNodeHelper->GetIndex(curNode, XFA_LOGIC_Transparent,
-                                           bIsProperty, true);
-    } else {
-      iCurrIndex = m_pNodeHelper->GetIndex(curNode, XFA_LOGIC_Transparent,
-                                           bIsProperty, false);
-    }
-  }
-
-  int32_t iFoundCount = pdfium::CollectionSize<int32_t>(rnd.m_Objects);
-  wsCondition.Trim();
-
-  int32_t iLen = wsCondition.GetLength();
-  if (!iLen) {
-    if (rnd.m_dwStyles & XFA_RESOLVENODE_ALL)
-      return;
-    if (iFoundCount == 1)
-      return;
-
-    if (iFoundCount <= iCurrIndex) {
-      if (rnd.m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-        m_pNodeHelper->m_pCreateParent = ToNode(rnd.m_CurObject);
-        m_pNodeHelper->m_iCreateCount = iCurrIndex - iFoundCount + 1;
-      }
-      rnd.m_Objects.clear();
-      return;
-    }
-
-    CXFA_Object* ret = rnd.m_Objects[iCurrIndex];
-    rnd.m_Objects.clear();
-    rnd.m_Objects.push_back(ret);
-    return;
-  }
-
-  wchar_t wTypeChar = wsCondition[0];
-  switch (wTypeChar) {
-    case '[':
-      ConditionArray(iCurrIndex, wsCondition, iFoundCount, rnd);
-      return;
-    case '.':
-      if (iLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '('))
-        DoPredicateFilter(iCurrIndex, wsCondition, iFoundCount, rnd);
-      return;
-    case '(':
-    case '"':
-    default:
-      return;
-  }
-}
-void CFXJSE_ResolveProcessor::SetStylesForChild(uint32_t dwParentStyles,
-                                                CFXJSE_ResolveNodeData& rnd) {
-  uint32_t dwSubStyles = XFA_RESOLVENODE_Children;
-  if (dwParentStyles & XFA_RESOLVENODE_TagName)
-    dwSubStyles |= XFA_RESOLVENODE_TagName;
-
-  dwSubStyles &= ~XFA_RESOLVENODE_Parent;
-  dwSubStyles &= ~XFA_RESOLVENODE_Siblings;
-  dwSubStyles &= ~XFA_RESOLVENODE_Properties;
-  dwSubStyles |= XFA_RESOLVENODE_ALL;
-  rnd.m_dwStyles = dwSubStyles;
-}
-
-void CFXJSE_ResolveProcessor::SetIndexDataBind(WideString& wsNextCondition,
-                                               int32_t& iIndex,
-                                               int32_t iCount) {
-  if (m_pNodeHelper->CreateNode_ForCondition(wsNextCondition)) {
-    if (m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) {
-      iIndex = 0;
-    } else {
-      iIndex = iCount - 1;
-    }
-  } else {
-    iIndex = iCount - 1;
-  }
-}
-
-CFXJSE_ResolveNodeData::CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC)
-    : m_pSC(pSC),
-      m_CurObject(nullptr),
-      m_wsName(),
-      m_uHashName(XFA_HASHCODE_None),
-      m_wsCondition(),
-      m_nLevel(0),
-      m_Objects(),
-      m_dwStyles(XFA_RESOLVENODE_Children),
-      m_pScriptAttribute(nullptr),
-      m_dwFlag(XFA_ResolveNode_RSType_Nodes) {}
-
-CFXJSE_ResolveNodeData::~CFXJSE_ResolveNodeData() {}
diff --git a/fxjs/cfxjse_resolveprocessor.h b/fxjs/cfxjse_resolveprocessor.h
deleted file mode 100644
index 71aba6e..0000000
--- a/fxjs/cfxjse_resolveprocessor.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_RESOLVEPROCESSOR_H_
-#define FXJS_CFXJSE_RESOLVEPROCESSOR_H_
-
-#include <memory>
-#include <vector>
-
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-
-class CXFA_NodeHelper;
-class CFXJSE_Engine;
-
-class CFXJSE_ResolveNodeData {
- public:
-  explicit CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC);
-  ~CFXJSE_ResolveNodeData();
-
-  CFXJSE_Engine* m_pSC;
-  CXFA_Object* m_CurObject;
-  WideString m_wsName;
-  XFA_HashCode m_uHashName;
-  WideString m_wsCondition;
-  int32_t m_nLevel;
-  std::vector<CXFA_Object*> m_Objects;  // Not owned.
-  uint32_t m_dwStyles;
-  const XFA_SCRIPTATTRIBUTEINFO* m_pScriptAttribute;
-  XFA_ResolveNode_RSType m_dwFlag;
-};
-
-class CFXJSE_ResolveProcessor {
- public:
-  CFXJSE_ResolveProcessor();
-  ~CFXJSE_ResolveProcessor();
-
-  bool Resolve(CFXJSE_ResolveNodeData& rnd);
-  int32_t GetFilter(const WideStringView& wsExpression,
-                    int32_t nStart,
-                    CFXJSE_ResolveNodeData& rnd);
-  void SetIndexDataBind(WideString& wsNextCondition,
-                        int32_t& iIndex,
-                        int32_t iCount);
-  void SetCurStart(int32_t start) { m_iCurStart = start; }
-
-  CXFA_NodeHelper* GetNodeHelper() const { return m_pNodeHelper.get(); }
-
- private:
-  bool ResolveForAttributeRs(CXFA_Object* curNode,
-                             CFXJSE_ResolveNodeData& rnd,
-                             const WideStringView& strAttr);
-  bool ResolveAnyChild(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveDollar(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveNumberSign(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveAsterisk(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveNormal(CFXJSE_ResolveNodeData& rnd);
-  void SetStylesForChild(uint32_t dwParentStyles, CFXJSE_ResolveNodeData& rnd);
-
-  void ConditionArray(int32_t iCurIndex,
-                      WideString wsCondition,
-                      int32_t iFoundCount,
-                      CFXJSE_ResolveNodeData& rnd);
-  void DoPredicateFilter(int32_t iCurIndex,
-                         WideString wsCondition,
-                         int32_t iFoundCount,
-                         CFXJSE_ResolveNodeData& rnd);
-  void FilterCondition(CFXJSE_ResolveNodeData& rnd, WideString wsCondition);
-
-  int32_t m_iCurStart;
-  std::unique_ptr<CXFA_NodeHelper> m_pNodeHelper;
-};
-
-#endif  // FXJS_CFXJSE_RESOLVEPROCESSOR_H_
diff --git a/fxjs/cfxjse_runtimedata.cpp b/fxjs/cfxjse_runtimedata.cpp
deleted file mode 100644
index b5c2de9..0000000
--- a/fxjs/cfxjse_runtimedata.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_runtimedata.h"
-
-#include <utility>
-
-#include "fxjs/cfxjse_isolatetracker.h"
-#include "fxjs/fxjs_v8.h"
-
-CFXJSE_RuntimeData::CFXJSE_RuntimeData(v8::Isolate* pIsolate)
-    : m_pIsolate(pIsolate) {}
-
-CFXJSE_RuntimeData::~CFXJSE_RuntimeData() {}
-
-std::unique_ptr<CFXJSE_RuntimeData> CFXJSE_RuntimeData::Create(
-    v8::Isolate* pIsolate) {
-  std::unique_ptr<CFXJSE_RuntimeData> pRuntimeData(
-      new CFXJSE_RuntimeData(pIsolate));
-  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
-
-  v8::Local<v8::FunctionTemplate> hFuncTemplate =
-      v8::FunctionTemplate::New(pIsolate);
-  v8::Local<v8::ObjectTemplate> hGlobalTemplate =
-      hFuncTemplate->InstanceTemplate();
-  hGlobalTemplate->Set(
-      v8::Symbol::GetToStringTag(pIsolate),
-      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-          .ToLocalChecked());
-
-  v8::Local<v8::Context> hContext =
-      v8::Context::New(pIsolate, 0, hGlobalTemplate);
-  hContext->SetSecurityToken(v8::External::New(pIsolate, pIsolate));
-
-  pRuntimeData->m_hRootContextGlobalTemplate.Reset(pIsolate, hFuncTemplate);
-  pRuntimeData->m_hRootContext.Reset(pIsolate, hContext);
-  return pRuntimeData;
-}
-
-CFXJSE_RuntimeData* CFXJSE_RuntimeData::Get(v8::Isolate* pIsolate) {
-  FXJS_PerIsolateData::SetUp(pIsolate);
-
-  FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(pIsolate);
-  if (!pData->m_pFXJSERuntimeData)
-    pData->m_pFXJSERuntimeData = CFXJSE_RuntimeData::Create(pIsolate);
-  return pData->m_pFXJSERuntimeData.get();
-}
diff --git a/fxjs/cfxjse_runtimedata.h b/fxjs/cfxjse_runtimedata.h
deleted file mode 100644
index 292fe26..0000000
--- a/fxjs/cfxjse_runtimedata.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_RUNTIMEDATA_H_
-#define FXJS_CFXJSE_RUNTIMEDATA_H_
-
-#include <memory>
-
-#include "v8/include/v8.h"
-
-class CFXJSE_RuntimeList;
-
-class CFXJSE_RuntimeData {
- public:
-  ~CFXJSE_RuntimeData();
-
-  static CFXJSE_RuntimeData* Get(v8::Isolate* pIsolate);
-
-  v8::Isolate* m_pIsolate;
-  v8::Global<v8::FunctionTemplate> m_hRootContextGlobalTemplate;
-  v8::Global<v8::Context> m_hRootContext;
-
- protected:
-  explicit CFXJSE_RuntimeData(v8::Isolate* pIsolate);
-
-  static std::unique_ptr<CFXJSE_RuntimeData> Create(v8::Isolate* pIsolate);
-
- private:
-  CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete;
-  CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete;
-};
-
-#endif  // FXJS_CFXJSE_RUNTIMEDATA_H_
diff --git a/fxjs/cfxjse_value.cpp b/fxjs/cfxjse_value.cpp
deleted file mode 100644
index d07b356..0000000
--- a/fxjs/cfxjse_value.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cfxjse_value.h"
-
-#include <math.h>
-
-#include "fxjs/cfxjse_class.h"
-#include "fxjs/cfxjse_context.h"
-
-namespace {
-
-double ftod(float fNumber) {
-  static_assert(sizeof(float) == 4, "float of incorrect size");
-
-  uint32_t nFloatBits = (uint32_t&)fNumber;
-  uint8_t nExponent = (uint8_t)(nFloatBits >> 23);
-  if (nExponent == 0 || nExponent == 255)
-    return fNumber;
-
-  int8_t nErrExp = nExponent - 150;
-  if (nErrExp >= 0)
-    return fNumber;
-
-  double dwError = pow(2.0, nErrExp), dwErrorHalf = dwError / 2;
-  double dNumber = fNumber, dNumberAbs = fabs(fNumber);
-  double dNumberAbsMin = dNumberAbs - dwErrorHalf,
-         dNumberAbsMax = dNumberAbs + dwErrorHalf;
-  int32_t iErrPos = 0;
-  if (floor(dNumberAbsMin) == floor(dNumberAbsMax)) {
-    dNumberAbsMin = fmod(dNumberAbsMin, 1.0);
-    dNumberAbsMax = fmod(dNumberAbsMax, 1.0);
-    int32_t iErrPosMin = 1, iErrPosMax = 38;
-    do {
-      int32_t iMid = (iErrPosMin + iErrPosMax) / 2;
-      double dPow = pow(10.0, iMid);
-      if (floor(dNumberAbsMin * dPow) == floor(dNumberAbsMax * dPow)) {
-        iErrPosMin = iMid + 1;
-      } else {
-        iErrPosMax = iMid;
-      }
-    } while (iErrPosMin < iErrPosMax);
-    iErrPos = iErrPosMax;
-  }
-  double dPow = pow(10.0, iErrPos);
-  return fNumber < 0 ? ceil(dNumber * dPow - 0.5) / dPow
-                     : floor(dNumber * dPow + 0.5) / dPow;
-}
-
-}  // namespace
-
-void FXJSE_ThrowMessage(const ByteStringView& utf8Message) {
-  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
-  ASSERT(pIsolate);
-
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
-  v8::Local<v8::String> hMessage = v8::String::NewFromUtf8(
-      pIsolate, utf8Message.unterminated_c_str(), v8::String::kNormalString,
-      utf8Message.GetLength());
-  v8::Local<v8::Value> hError = v8::Exception::Error(hMessage);
-  pIsolate->ThrowException(hError);
-}
-
-CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
-
-CFXJSE_Value::~CFXJSE_Value() {}
-
-CFXJSE_HostObject* CFXJSE_Value::ToHostObject(CFXJSE_Class* lpClass) const {
-  ASSERT(!m_hValue.IsEmpty());
-
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> pValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  ASSERT(!pValue.IsEmpty());
-
-  if (!pValue->IsObject())
-    return nullptr;
-
-  return FXJSE_RetrieveObjectBinding(pValue.As<v8::Object>(), lpClass);
-}
-
-void CFXJSE_Value::SetObject(CFXJSE_HostObject* lpObject,
-                             CFXJSE_Class* pClass) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::FunctionTemplate> hClass =
-      v8::Local<v8::FunctionTemplate>::New(m_pIsolate, pClass->m_hTemplate);
-  v8::Local<v8::Object> hObject = hClass->InstanceTemplate()->NewInstance();
-  FXJSE_UpdateObjectBinding(hObject, lpObject);
-  m_hValue.Reset(m_pIsolate, hObject);
-}
-
-void CFXJSE_Value::SetArray(
-    const std::vector<std::unique_ptr<CFXJSE_Value>>& values) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Array> hArrayObject = v8::Array::New(m_pIsolate, values.size());
-  uint32_t count = 0;
-  for (auto& v : values) {
-    hArrayObject->Set(count++, v8::Local<v8::Value>::New(
-                                   m_pIsolate, v.get()->DirectGetValue()));
-  }
-  m_hValue.Reset(m_pIsolate, hArrayObject);
-}
-
-void CFXJSE_Value::SetDate(double dDouble) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hDate = v8::Date::New(m_pIsolate, dDouble);
-  m_hValue.Reset(m_pIsolate, hDate);
-}
-
-void CFXJSE_Value::SetFloat(float fFloat) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> pValue = v8::Number::New(m_pIsolate, ftod(fFloat));
-  m_hValue.Reset(m_pIsolate, pValue);
-}
-
-bool CFXJSE_Value::SetObjectProperty(const ByteStringView& szPropName,
-                                     CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::Value> hPropValue =
-      v8::Local<v8::Value>::New(m_pIsolate, lpPropValue->DirectGetValue());
-  return (bool)hObject.As<v8::Object>()->Set(
-      v8::String::NewFromUtf8(m_pIsolate, szPropName.unterminated_c_str(),
-                              v8::String::kNormalString,
-                              szPropName.GetLength()),
-      hPropValue);
-}
-
-bool CFXJSE_Value::GetObjectProperty(const ByteStringView& szPropName,
-                                     CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::Value> hPropValue =
-      hObject.As<v8::Object>()->Get(v8::String::NewFromUtf8(
-          m_pIsolate, szPropName.unterminated_c_str(),
-          v8::String::kNormalString, szPropName.GetLength()));
-  lpPropValue->ForceSetValue(hPropValue);
-  return true;
-}
-
-bool CFXJSE_Value::SetObjectProperty(uint32_t uPropIdx,
-                                     CFXJSE_Value* lpPropValue) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::Value> hPropValue =
-      v8::Local<v8::Value>::New(m_pIsolate, lpPropValue->DirectGetValue());
-  return (bool)hObject.As<v8::Object>()->Set(uPropIdx, hPropValue);
-}
-
-bool CFXJSE_Value::GetObjectPropertyByIdx(uint32_t uPropIdx,
-                                          CFXJSE_Value* lpPropValue) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::Value> hPropValue = hObject.As<v8::Object>()->Get(uPropIdx);
-  lpPropValue->ForceSetValue(hPropValue);
-  return true;
-}
-
-bool CFXJSE_Value::DeleteObjectProperty(const ByteStringView& szPropName) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  hObject.As<v8::Object>()->Delete(v8::String::NewFromUtf8(
-      m_pIsolate, szPropName.unterminated_c_str(), v8::String::kNormalString,
-      szPropName.GetLength()));
-  return true;
-}
-
-bool CFXJSE_Value::HasObjectOwnProperty(const ByteStringView& szPropName,
-                                        bool bUseTypeGetter) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::String> hKey = v8::String::NewFromUtf8(
-      m_pIsolate, szPropName.unterminated_c_str(), v8::String::kNormalString,
-      szPropName.GetLength());
-  return hObject.As<v8::Object>()->HasRealNamedProperty(hKey) ||
-         (bUseTypeGetter &&
-          hObject.As<v8::Object>()
-              ->HasOwnProperty(m_pIsolate->GetCurrentContext(), hKey)
-              .FromMaybe(false));
-}
-
-bool CFXJSE_Value::SetObjectOwnProperty(const ByteStringView& szPropName,
-                                        CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::Value> pValue =
-      v8::Local<v8::Value>::New(m_pIsolate, lpPropValue->m_hValue);
-  return hObject.As<v8::Object>()
-      ->DefineOwnProperty(
-          m_pIsolate->GetCurrentContext(),
-          v8::String::NewFromUtf8(m_pIsolate, szPropName.unterminated_c_str(),
-                                  v8::String::kNormalString,
-                                  szPropName.GetLength()),
-          pValue)
-      .FromMaybe(false);
-}
-
-bool CFXJSE_Value::SetFunctionBind(CFXJSE_Value* lpOldFunction,
-                                   CFXJSE_Value* lpNewThis) {
-  ASSERT(lpOldFunction && lpNewThis);
-
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> rgArgs[2];
-  v8::Local<v8::Value> hOldFunction =
-      v8::Local<v8::Value>::New(m_pIsolate, lpOldFunction->DirectGetValue());
-  if (hOldFunction.IsEmpty() || !hOldFunction->IsFunction())
-    return false;
-
-  rgArgs[0] = hOldFunction;
-  v8::Local<v8::Value> hNewThis =
-      v8::Local<v8::Value>::New(m_pIsolate, lpNewThis->DirectGetValue());
-  if (hNewThis.IsEmpty())
-    return false;
-
-  rgArgs[1] = hNewThis;
-  v8::Local<v8::String> hBinderFuncSource =
-      v8::String::NewFromUtf8(m_pIsolate,
-                              "(function (oldfunction, newthis) { return "
-                              "oldfunction.bind(newthis); })");
-  v8::Local<v8::Function> hBinderFunc =
-      v8::Script::Compile(hBinderFuncSource)->Run().As<v8::Function>();
-  v8::Local<v8::Value> hBoundFunction =
-      hBinderFunc->Call(m_pIsolate->GetCurrentContext()->Global(), 2, rgArgs);
-  if (hBoundFunction.IsEmpty() || !hBoundFunction->IsFunction())
-    return false;
-
-  m_hValue.Reset(m_pIsolate, hBoundFunction);
-  return true;
-}
-
-bool CFXJSE_Value::IsUndefined() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsUndefined();
-}
-
-bool CFXJSE_Value::IsNull() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsNull();
-}
-
-bool CFXJSE_Value::IsBoolean() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsBoolean();
-}
-
-bool CFXJSE_Value::IsString() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsString();
-}
-
-bool CFXJSE_Value::IsNumber() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsNumber();
-}
-
-bool CFXJSE_Value::IsInteger() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsInt32();
-}
-
-bool CFXJSE_Value::IsObject() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsObject();
-}
-
-bool CFXJSE_Value::IsArray() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsArray();
-}
-
-bool CFXJSE_Value::IsFunction() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsFunction();
-}
-
-bool CFXJSE_Value::IsDate() const {
-  if (m_hValue.IsEmpty())
-    return false;
-
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return hValue->IsDate();
-}
-
-bool CFXJSE_Value::ToBoolean() const {
-  ASSERT(!m_hValue.IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return static_cast<bool>(hValue->BooleanValue());
-}
-
-float CFXJSE_Value::ToFloat() const {
-  ASSERT(!m_hValue.IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return static_cast<float>(hValue->NumberValue());
-}
-
-double CFXJSE_Value::ToDouble() const {
-  ASSERT(!m_hValue.IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return static_cast<double>(hValue->NumberValue());
-}
-
-int32_t CFXJSE_Value::ToInteger() const {
-  ASSERT(!m_hValue.IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  return static_cast<int32_t>(hValue->NumberValue());
-}
-
-ByteString CFXJSE_Value::ToString() const {
-  ASSERT(!m_hValue.IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(m_pIsolate, m_hValue);
-  v8::Local<v8::String> hString = hValue->ToString();
-  v8::String::Utf8Value hStringVal(m_pIsolate, hString);
-  return ByteString(*hStringVal);
-}
-
-void CFXJSE_Value::SetUndefined() {
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Undefined(m_pIsolate);
-  m_hValue.Reset(m_pIsolate, hValue);
-}
-
-void CFXJSE_Value::SetNull() {
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Null(m_pIsolate);
-  m_hValue.Reset(m_pIsolate, hValue);
-}
-
-void CFXJSE_Value::SetBoolean(bool bBoolean) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Boolean::New(m_pIsolate, bBoolean != false);
-  m_hValue.Reset(m_pIsolate, hValue);
-}
-
-void CFXJSE_Value::SetInteger(int32_t nInteger) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Integer::New(m_pIsolate, nInteger);
-  m_hValue.Reset(m_pIsolate, hValue);
-}
-
-void CFXJSE_Value::SetDouble(double dDouble) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::Number::New(m_pIsolate, dDouble);
-  m_hValue.Reset(m_pIsolate, hValue);
-}
-
-void CFXJSE_Value::SetString(const ByteStringView& szString) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(m_pIsolate);
-  v8::Local<v8::Value> hValue = v8::String::NewFromUtf8(
-      m_pIsolate, reinterpret_cast<const char*>(szString.raw_str()),
-      v8::String::kNormalString, szString.GetLength());
-  m_hValue.Reset(m_pIsolate, hValue);
-}
-
diff --git a/fxjs/cfxjse_value.h b/fxjs/cfxjse_value.h
deleted file mode 100644
index 52bb036..0000000
--- a/fxjs/cfxjse_value.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CFXJSE_VALUE_H_
-#define FXJS_CFXJSE_VALUE_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "fxjs/cfxjse_isolatetracker.h"
-#include "fxjs/cfxjse_runtimedata.h"
-#include "v8/include/v8.h"
-
-class CFXJSE_Class;
-class CFXJSE_HostObject;
-
-class CFXJSE_Value {
- public:
-  explicit CFXJSE_Value(v8::Isolate* pIsolate);
-  ~CFXJSE_Value();
-
-  bool IsUndefined() const;
-  bool IsNull() const;
-  bool IsBoolean() const;
-  bool IsString() const;
-  bool IsNumber() const;
-  bool IsInteger() const;
-  bool IsObject() const;
-  bool IsArray() const;
-  bool IsFunction() const;
-  bool IsDate() const;
-  bool ToBoolean() const;
-  float ToFloat() const;
-  double ToDouble() const;
-  int32_t ToInteger() const;
-  ByteString ToString() const;
-  WideString ToWideString() const {
-    return WideString::FromUTF8(ToString().AsStringView());
-  }
-  CFXJSE_HostObject* ToHostObject(CFXJSE_Class* lpClass) const;
-
-  void SetUndefined();
-  void SetNull();
-  void SetBoolean(bool bBoolean);
-  void SetInteger(int32_t nInteger);
-  void SetDouble(double dDouble);
-  void SetString(const ByteStringView& szString);
-  void SetFloat(float fFloat);
-
-  void SetObject(CFXJSE_HostObject* lpObject, CFXJSE_Class* pClass);
-  void SetArray(const std::vector<std::unique_ptr<CFXJSE_Value>>& values);
-  void SetDate(double dDouble);
-
-  bool GetObjectProperty(const ByteStringView& szPropName,
-                         CFXJSE_Value* lpPropValue);
-  bool SetObjectProperty(const ByteStringView& szPropName,
-                         CFXJSE_Value* lpPropValue);
-  bool GetObjectPropertyByIdx(uint32_t uPropIdx, CFXJSE_Value* lpPropValue);
-  bool SetObjectProperty(uint32_t uPropIdx, CFXJSE_Value* lpPropValue);
-  bool DeleteObjectProperty(const ByteStringView& szPropName);
-  bool HasObjectOwnProperty(const ByteStringView& szPropName,
-                            bool bUseTypeGetter);
-  bool SetObjectOwnProperty(const ByteStringView& szPropName,
-                            CFXJSE_Value* lpPropValue);
-  bool SetFunctionBind(CFXJSE_Value* lpOldFunction, CFXJSE_Value* lpNewThis);
-
-  v8::Isolate* GetIsolate() const { return m_pIsolate; }
-  const v8::Global<v8::Value>& DirectGetValue() const { return m_hValue; }
-  void ForceSetValue(v8::Local<v8::Value> hValue) {
-    m_hValue.Reset(m_pIsolate, hValue);
-  }
-  void Assign(const CFXJSE_Value* lpValue) {
-    ASSERT(lpValue);
-    if (lpValue) {
-      m_hValue.Reset(m_pIsolate, lpValue->m_hValue);
-    } else {
-      m_hValue.Reset();
-    }
-  }
-
- private:
-  friend class CFXJSE_Class;
-  friend class CFXJSE_Context;
-
-  CFXJSE_Value();
-  CFXJSE_Value(const CFXJSE_Value&);
-  CFXJSE_Value& operator=(const CFXJSE_Value&);
-
-  v8::Isolate* m_pIsolate;
-  v8::Global<v8::Value> m_hValue;
-};
-
-#endif  // FXJS_CFXJSE_VALUE_H_
diff --git a/fxjs/cjs_annot.cpp b/fxjs/cjs_annot.cpp
index 69eccef..292e586 100644
--- a/fxjs/cjs_annot.cpp
+++ b/fxjs/cjs_annot.cpp
@@ -6,19 +6,13 @@
 
 #include "fxjs/cjs_annot.h"
 
-#include "fxjs/JS_Define.h"
+#include "constants/annotation_flags.h"
+#include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fxjs/cjs_event_context.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
 
-namespace {
-
-CPDFSDK_BAAnnot* ToBAAnnot(CPDFSDK_Annot* annot) {
-  return static_cast<CPDFSDK_BAAnnot*>(annot);
-}
-
-}  // namespace
-
 const JSPropertySpec CJS_Annot::PropertySpecs[] = {
     {"hidden", get_hidden_static, set_hidden_static},
     {"name", get_name_static, set_name_static},
@@ -26,6 +20,8 @@
 
 int CJS_Annot::ObjDefnID = -1;
 
+const char CJS_Annot::kName[] = "Annot";
+
 // static
 int CJS_Annot::GetObjDefnID() {
   return ObjDefnID;
@@ -33,79 +29,86 @@
 
 // static
 void CJS_Annot::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID = pEngine->DefineObj("Annot", FXJSOBJTYPE_DYNAMIC,
-                                 JSConstructor<CJS_Annot, Annot>,
-                                 JSDestructor<CJS_Annot>);
-  DefineProps(pEngine, ObjDefnID, PropertySpecs, FX_ArraySize(PropertySpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_Annot::kName, FXJSOBJTYPE_DYNAMIC,
+                                 JSConstructor<CJS_Annot>, JSDestructor);
+  DefineProps(pEngine, ObjDefnID, PropertySpecs);
 }
 
-Annot::Annot(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
+CJS_Annot::CJS_Annot(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-Annot::~Annot() {}
+CJS_Annot::~CJS_Annot() = default;
 
-CJS_Return Annot::get_hidden(CJS_Runtime* pRuntime) {
+void CJS_Annot::SetSDKAnnot(CPDFSDK_BAAnnot* annot) {
+  m_pAnnot.Reset(annot);
+}
+
+CJS_Result CJS_Annot::get_hidden(CJS_Runtime* pRuntime) {
   if (!m_pAnnot)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Annot* pPDFAnnot = ToBAAnnot(m_pAnnot.Get())->GetPDFAnnot();
-  return CJS_Return(pRuntime->NewBoolean(
-      CPDF_Annot::IsAnnotationHidden(pPDFAnnot->GetAnnotDict())));
+  CPDF_Annot* pPDFAnnot = m_pAnnot->AsBAAnnot()->GetPDFAnnot();
+  return CJS_Result::Success(pRuntime->NewBoolean(pPDFAnnot->IsHidden()));
 }
 
-CJS_Return Annot::set_hidden(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Annot::set_hidden(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
   // May invalidate m_pAnnot.
   bool bHidden = pRuntime->ToBoolean(vp);
-  if (!m_pAnnot)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
 
-  uint32_t flags = ToBAAnnot(m_pAnnot.Get())->GetFlags();
+  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  if (!pBAAnnot)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  uint32_t flags = pBAAnnot->GetFlags();
   if (bHidden) {
-    flags |= ANNOTFLAG_HIDDEN;
-    flags |= ANNOTFLAG_INVISIBLE;
-    flags |= ANNOTFLAG_NOVIEW;
-    flags &= ~ANNOTFLAG_PRINT;
+    flags |= pdfium::annotation_flags::kHidden;
+    flags |= pdfium::annotation_flags::kInvisible;
+    flags |= pdfium::annotation_flags::kNoView;
+    flags &= ~pdfium::annotation_flags::kPrint;
   } else {
-    flags &= ~ANNOTFLAG_HIDDEN;
-    flags &= ~ANNOTFLAG_INVISIBLE;
-    flags &= ~ANNOTFLAG_NOVIEW;
-    flags |= ANNOTFLAG_PRINT;
+    flags &= ~pdfium::annotation_flags::kHidden;
+    flags &= ~pdfium::annotation_flags::kInvisible;
+    flags &= ~pdfium::annotation_flags::kNoView;
+    flags |= pdfium::annotation_flags::kPrint;
   }
-  ToBAAnnot(m_pAnnot.Get())->SetFlags(flags);
-
-  return CJS_Return(true);
+  pBAAnnot->SetFlags(flags);
+  return CJS_Result::Success();
 }
 
-CJS_Return Annot::get_name(CJS_Runtime* pRuntime) {
-  if (!m_pAnnot)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(
-      pRuntime->NewString(ToBAAnnot(m_pAnnot.Get())->GetAnnotName().c_str()));
+CJS_Result CJS_Annot::get_name(CJS_Runtime* pRuntime) {
+  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  if (!pBAAnnot)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  return CJS_Result::Success(
+      pRuntime->NewString(pBAAnnot->GetAnnotName().AsStringView()));
 }
 
-CJS_Return Annot::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Annot::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   // May invalidate m_pAnnot.
   WideString annotName = pRuntime->ToWideString(vp);
-  if (!m_pAnnot)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
 
-  ToBAAnnot(m_pAnnot.Get())->SetAnnotName(annotName);
-  return CJS_Return(true);
+  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  if (!pBAAnnot)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  pBAAnnot->SetAnnotName(annotName);
+  return CJS_Result::Success();
 }
 
-CJS_Return Annot::get_type(CJS_Runtime* pRuntime) {
-  if (!m_pAnnot)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(pRuntime->NewString(
-      WideString::FromLocal(CPDF_Annot::AnnotSubtypeToString(
-                                ToBAAnnot(m_pAnnot.Get())->GetAnnotSubtype())
-                                .c_str())
-          .c_str()));
+CJS_Result CJS_Annot::get_type(CJS_Runtime* pRuntime) {
+  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  if (!pBAAnnot)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  return CJS_Result::Success(pRuntime->NewString(
+      WideString::FromDefANSI(
+          CPDF_Annot::AnnotSubtypeToString(pBAAnnot->GetAnnotSubtype())
+              .AsStringView())
+          .AsStringView()));
 }
 
-CJS_Return Annot::set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
-}
-
-void Annot::SetSDKAnnot(CPDFSDK_BAAnnot* annot) {
-  m_pAnnot.Reset(annot);
+CJS_Result CJS_Annot::set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
diff --git a/fxjs/cjs_annot.h b/fxjs/cjs_annot.h
index 18124f6..ceb2615 100644
--- a/fxjs/cjs_annot.h
+++ b/fxjs/cjs_annot.h
@@ -7,44 +7,40 @@
 #ifndef FXJS_CJS_ANNOT_H_
 #define FXJS_CJS_ANNOT_H_
 
-#include "fpdfsdk/cpdfsdk_baannot.h"
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
-class Annot : public CJS_EmbedObj {
- public:
-  explicit Annot(CJS_Object* pJSObject);
-  ~Annot() override;
+class CPDFSDK_BAAnnot;
 
-  CJS_Return get_hidden(CJS_Runtime* pRuntime);
-  CJS_Return set_hidden(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_name(CJS_Runtime* pRuntime);
-  CJS_Return set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_type(CJS_Runtime* pRuntime);
-  CJS_Return set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  void SetSDKAnnot(CPDFSDK_BAAnnot* annot);
-
- private:
-  CPDFSDK_Annot::ObservedPtr m_pAnnot;
-};
-
-class CJS_Annot : public CJS_Object {
+class CJS_Annot final : public CJS_Object {
  public:
   static int GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Annot(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Annot() override {}
+  CJS_Annot(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Annot() override;
 
-  JS_STATIC_PROP(hidden, hidden, Annot);
-  JS_STATIC_PROP(name, name, Annot);
-  JS_STATIC_PROP(type, type, Annot);
+  void SetSDKAnnot(CPDFSDK_BAAnnot* annot);
+
+  JS_STATIC_PROP(hidden, hidden, CJS_Annot)
+  JS_STATIC_PROP(name, name, CJS_Annot)
+  JS_STATIC_PROP(type, type, CJS_Annot)
 
  private:
   static int ObjDefnID;
+  static const char kName[];
   static const JSPropertySpec PropertySpecs[];
+
+  CJS_Result get_hidden(CJS_Runtime* pRuntime);
+  CJS_Result set_hidden(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_name(CJS_Runtime* pRuntime);
+  CJS_Result set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_type(CJS_Runtime* pRuntime);
+  CJS_Result set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  ObservedPtr<CPDFSDK_Annot> m_pAnnot;
 };
 
 #endif  // FXJS_CJS_ANNOT_H_
diff --git a/fxjs/cjs_app.cpp b/fxjs/cjs_app.cpp
index 32cac22..992fd98 100644
--- a/fxjs/cjs_app.cpp
+++ b/fxjs/cjs_app.cpp
@@ -6,32 +6,23 @@
 
 #include "fxjs/cjs_app.h"
 
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include <utility>
+
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_timerobj.h"
 #include "fxjs/global_timer.h"
 #include "fxjs/ijs_event_context.h"
 #include "fxjs/js_resources.h"
-
-namespace {
-
-bool IsTypeKnown(v8::Local<v8::Value> value) {
-  return !value.IsEmpty() &&
-         (value->IsString() || value->IsNumber() || value->IsBoolean() ||
-          value->IsDate() || value->IsObject() || value->IsNull() ||
-          value->IsUndefined());
-}
-
-}  // namespace
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 #define JS_STR_VIEWERTYPE L"pdfium"
 #define JS_STR_VIEWERVARIATION L"Full"
 #define JS_STR_PLATFORM L"WIN"
 #define JS_STR_LANGUAGE L"ENU"
 #define JS_NUM_VIEWERVERSION 8
-#ifdef PDF_ENABLE_XFA
 #define JS_NUM_VIEWERVERSION_XFA 11
-#endif  // PDF_ENABLE_XFA
 #define JS_NUM_FORMSVERSION 7
 
 const JSPropertySpec CJS_App::PropertySpecs[] = {
@@ -75,133 +66,134 @@
 
 int CJS_App::ObjDefnID = -1;
 
+const char CJS_App::kName[] = "app";
+
+// static
+int CJS_App::GetObjDefnID() {
+  return ObjDefnID;
+}
+
 // static
 void CJS_App::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID =
-      pEngine->DefineObj("app", FXJSOBJTYPE_STATIC, JSConstructor<CJS_App, app>,
-                         JSDestructor<CJS_App>);
-  DefineProps(pEngine, ObjDefnID, PropertySpecs, FX_ArraySize(PropertySpecs));
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_App::kName, FXJSOBJTYPE_STATIC,
+                                 JSConstructor<CJS_App>, JSDestructor);
+  DefineProps(pEngine, ObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, ObjDefnID, MethodSpecs);
 }
 
-app::app(CJS_Object* pJSObject)
-    : CJS_EmbedObj(pJSObject), m_bCalculate(true), m_bRuntimeHighLight(false) {}
+CJS_App::CJS_App(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-app::~app() {}
+CJS_App::~CJS_App() = default;
 
-CJS_Return app::get_active_docs(CJS_Runtime* pRuntime) {
-  CJS_Document* pJSDocument = nullptr;
+CJS_Result CJS_App::get_active_docs(CJS_Runtime* pRuntime) {
   v8::Local<v8::Object> pObj = pRuntime->GetThisObj();
-  if (CFXJS_Engine::GetObjDefnID(pObj) == CJS_Document::GetObjDefnID())
-    pJSDocument = static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pObj));
-
+  auto pJSDocument = JSGetObject<CJS_Document>(pObj);
+  if (!pJSDocument)
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
   v8::Local<v8::Array> aDocs = pRuntime->NewArray();
-  pRuntime->PutArrayElement(
-      aDocs, 0,
-      pJSDocument ? v8::Local<v8::Value>(pJSDocument->ToV8Object())
-                  : v8::Local<v8::Value>());
+  pRuntime->PutArrayElement(aDocs, 0, pJSDocument->ToV8Object());
   if (pRuntime->GetArrayLength(aDocs) > 0)
-    return CJS_Return(aDocs);
-  return CJS_Return(pRuntime->NewUndefined());
+    return CJS_Result::Success(aDocs);
+
+  return CJS_Result::Success(pRuntime->NewUndefined());
 }
 
-CJS_Return app::set_active_docs(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_active_docs(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::get_calculate(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewBoolean(m_bCalculate));
+CJS_Result CJS_App::get_calculate(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewBoolean(m_bCalculate));
 }
 
-CJS_Return app::set_calculate(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  m_bCalculate = pRuntime->ToBoolean(vp);
-  pRuntime->GetFormFillEnv()->GetInterForm()->EnableCalculate(m_bCalculate);
-  return CJS_Return(true);
-}
-
-CJS_Return app::get_forms_version(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewNumber(JS_NUM_FORMSVERSION));
-}
-
-CJS_Return app::set_forms_version(CJS_Runtime* pRuntime,
+CJS_Result CJS_App::set_calculate(CJS_Runtime* pRuntime,
                                   v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+  m_bCalculate = pRuntime->ToBoolean(vp);
+  pRuntime->GetFormFillEnv()->GetInteractiveForm()->EnableCalculate(
+      m_bCalculate);
+  return CJS_Result::Success();
 }
 
-CJS_Return app::get_viewer_type(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewString(JS_STR_VIEWERTYPE));
+CJS_Result CJS_App::get_forms_version(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewNumber(JS_NUM_FORMSVERSION));
 }
 
-CJS_Return app::set_viewer_type(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_forms_version(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::get_viewer_variation(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewString(JS_STR_VIEWERVARIATION));
+CJS_Result CJS_App::get_viewer_type(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERTYPE));
 }
 
-CJS_Return app::set_viewer_variation(CJS_Runtime* pRuntime,
-                                     v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_viewer_type(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::get_viewer_version(CJS_Runtime* pRuntime) {
-#ifdef PDF_ENABLE_XFA
-  CPDFXFA_Context* pXFAContext = pRuntime->GetFormFillEnv()->GetXFAContext();
-  if (pXFAContext->ContainsXFAForm())
-    return CJS_Return(pRuntime->NewNumber(JS_NUM_VIEWERVERSION_XFA));
-#endif  // PDF_ENABLE_XFA
-  return CJS_Return(pRuntime->NewNumber(JS_NUM_VIEWERVERSION));
+CJS_Result CJS_App::get_viewer_variation(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERVARIATION));
 }
 
-CJS_Return app::set_viewer_version(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_viewer_variation(CJS_Runtime* pRuntime,
+                                         v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::get_platform(CJS_Runtime* pRuntime) {
-#ifdef PDF_ENABLE_XFA
+CJS_Result CJS_App::get_viewer_version(CJS_Runtime* pRuntime) {
+  CPDF_Document::Extension* pContext =
+      pRuntime->GetFormFillEnv()->GetDocExtension();
+  int version = pContext && pContext->ContainsExtensionForm()
+                    ? JS_NUM_VIEWERVERSION_XFA
+                    : JS_NUM_VIEWERVERSION;
+  return CJS_Result::Success(pRuntime->NewNumber(version));
+}
+
+CJS_Result CJS_App::set_viewer_version(CJS_Runtime* pRuntime,
+                                       v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_App::get_platform(CJS_Runtime* pRuntime) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pRuntime->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return CJS_Return(false);
-
-  WideString platfrom = pFormFillEnv->GetPlatform();
-  if (!platfrom.IsEmpty())
-    return CJS_Return(pRuntime->NewString(platfrom.c_str()));
-#endif
-  return CJS_Return(pRuntime->NewString(JS_STR_PLATFORM));
+  if (pFormFillEnv) {
+    WideString platform = pFormFillEnv->GetPlatform();
+    if (!platform.IsEmpty())
+      return CJS_Result::Success(pRuntime->NewString(platform.AsStringView()));
+  }
+  return CJS_Result::Success(pRuntime->NewString(JS_STR_PLATFORM));
 }
 
-CJS_Return app::set_platform(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_platform(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::get_language(CJS_Runtime* pRuntime) {
-#ifdef PDF_ENABLE_XFA
+CJS_Result CJS_App::get_language(CJS_Runtime* pRuntime) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pRuntime->GetFormFillEnv();
-  if (!pFormFillEnv)
-    return CJS_Return(false);
-
-  WideString language = pFormFillEnv->GetLanguage();
-  if (!language.IsEmpty())
-    return CJS_Return(pRuntime->NewString(language.c_str()));
-#endif
-  return CJS_Return(pRuntime->NewString(JS_STR_LANGUAGE));
+  if (pFormFillEnv) {
+    WideString language = pFormFillEnv->GetLanguage();
+    if (!language.IsEmpty())
+      return CJS_Result::Success(pRuntime->NewString(language.AsStringView()));
+  }
+  return CJS_Result::Success(pRuntime->NewString(JS_STR_LANGUAGE));
 }
 
-CJS_Return app::set_language(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_language(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
 // creates a new fdf object that contains no data
 // comment: need reader support
 // note:
 // CFDF_Document * CPDFSDK_FormFillEnvironment::NewFDF();
-CJS_Return app::newFDF(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_App::newFDF(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
 // opens a specified pdf document and returns its document object
@@ -211,22 +203,22 @@
 // CFDF_Document * CPDFSDK_FormFillEnvironment::OpenFDF(string strPath,bool
 // bUserConv);
 
-CJS_Return app::openFDF(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_App::openFDF(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return app::alert(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_App::alert(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params) {
   std::vector<v8::Local<v8::Value>> newParams = ExpandKeywordParams(
-      pRuntime, params, 4, L"cMsg", L"nIcon", L"nType", L"cTitle");
+      pRuntime, params, 4, "cMsg", "nIcon", "nType", "cTitle");
 
-  if (!IsTypeKnown(newParams[0]))
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  if (!IsExpandedParamKnown(newParams[0]))
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pRuntime->GetFormFillEnv();
   if (!pFormFillEnv)
-    return CJS_Return(pRuntime->NewNumber(0));
+    return CJS_Result::Success(pRuntime->NewNumber(0));
 
   WideString swMsg;
   if (newParams[0]->IsArray()) {
@@ -243,267 +235,268 @@
     swMsg = pRuntime->ToWideString(newParams[0]);
   }
 
-  int iIcon = 0;
-  if (IsTypeKnown(newParams[1]))
+  int iIcon = JSPLATFORM_ALERT_ICON_DEFAULT;
+  if (IsExpandedParamKnown(newParams[1]))
     iIcon = pRuntime->ToInt32(newParams[1]);
 
-  int iType = 0;
-  if (IsTypeKnown(newParams[2]))
+  int iType = JSPLATFORM_ALERT_BUTTON_DEFAULT;
+  if (IsExpandedParamKnown(newParams[2]))
     iType = pRuntime->ToInt32(newParams[2]);
 
   WideString swTitle;
-  if (IsTypeKnown(newParams[3]))
+  if (IsExpandedParamKnown(newParams[3]))
     swTitle = pRuntime->ToWideString(newParams[3]);
   else
     swTitle = JSGetStringFromID(JSMessage::kAlert);
 
   pRuntime->BeginBlock();
   pFormFillEnv->KillFocusAnnot(0);
-
   v8::Local<v8::Value> ret = pRuntime->NewNumber(
-      pFormFillEnv->JS_appAlert(swMsg.c_str(), swTitle.c_str(), iType, iIcon));
+      pFormFillEnv->JS_appAlert(swMsg, swTitle, iType, iIcon));
   pRuntime->EndBlock();
 
-  return CJS_Return(ret);
+  return CJS_Result::Success(ret);
 }
 
-CJS_Return app::beep(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params) {
-  if (params.size() == 1) {
-    pRuntime->GetFormFillEnv()->JS_appBeep(pRuntime->ToInt32(params[0]));
-    return CJS_Return(true);
-  }
-  return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+CJS_Result CJS_App::beep(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  int type = JSPLATFORM_BEEP_DEFAULT;
+  if (IsExpandedParamKnown(params[0]))
+    type = pRuntime->ToInt32(params[0]);
+
+  pRuntime->GetFormFillEnv()->JS_appBeep(type);
+  return CJS_Result::Success();
 }
 
-CJS_Return app::findComponent(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_App::findComponent(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return app::popUpMenuEx(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+CJS_Result CJS_App::popUpMenuEx(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::get_fs(CJS_Runtime* pRuntime) {
-  return CJS_Return(false);
+CJS_Result CJS_App::get_fs(CJS_Runtime* pRuntime) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::set_fs(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_fs(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::setInterval(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
-  if (params.size() > 2 || params.size() == 0)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-
-  WideString script =
-      params.size() > 0 ? pRuntime->ToWideString(params[0]) : L"";
-  if (script.IsEmpty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError));
-
-  uint32_t dwInterval = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000;
-  GlobalTimer* timerRef = new GlobalTimer(this, pRuntime->GetFormFillEnv(),
-                                          pRuntime, 0, script, dwInterval, 0);
-  m_Timers.insert(std::unique_ptr<GlobalTimer>(timerRef));
-
-  v8::Local<v8::Object> pRetObj =
-      pRuntime->NewFxDynamicObj(CJS_TimerObj::GetObjDefnID());
-  if (pRetObj.IsEmpty())
-    return CJS_Return(false);
-
-  CJS_TimerObj* pJS_TimerObj =
-      static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj));
-  TimerObj* pTimerObj = static_cast<TimerObj*>(pJS_TimerObj->GetEmbedObject());
-  pTimerObj->SetTimer(timerRef);
-
-  return CJS_Return(pRetObj);
-}
-
-CJS_Return app::setTimeOut(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params) {
-  if (params.size() > 2 || params.size() == 0)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+CJS_Result CJS_App::setInterval(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() == 0 || params.size() > 2)
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString script = pRuntime->ToWideString(params[0]);
   if (script.IsEmpty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError));
+    return CJS_Result::Failure(JSMessage::kInvalidInputError);
+
+  uint32_t dwInterval = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000;
+  auto timerRef = pdfium::MakeUnique<GlobalTimer>(
+      this, pRuntime, GlobalTimer::Type::kRepeating, script, dwInterval, 0);
+  GlobalTimer* pTimerRef = timerRef.get();
+  m_Timers.insert(std::move(timerRef));
+
+  v8::Local<v8::Object> pRetObj = pRuntime->NewFXJSBoundObject(
+      CJS_TimerObj::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
+  if (pRetObj.IsEmpty())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  auto* pJS_TimerObj =
+      static_cast<CJS_TimerObj*>(CFXJS_Engine::GetObjectPrivate(pRetObj));
+
+  pJS_TimerObj->SetTimer(pTimerRef);
+  return CJS_Result::Success(pRetObj);
+}
+
+CJS_Result CJS_App::setTimeOut(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() == 0 || params.size() > 2)
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  WideString script = pRuntime->ToWideString(params[0]);
+  if (script.IsEmpty())
+    return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   uint32_t dwTimeOut = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000;
-  GlobalTimer* timerRef =
-      new GlobalTimer(this, pRuntime->GetFormFillEnv(), pRuntime, 1, script,
-                      dwTimeOut, dwTimeOut);
-  m_Timers.insert(std::unique_ptr<GlobalTimer>(timerRef));
+  auto timerRef = pdfium::MakeUnique<GlobalTimer>(this, pRuntime,
+                                                  GlobalTimer::Type::kOneShot,
+                                                  script, dwTimeOut, dwTimeOut);
+  GlobalTimer* pTimerRef = timerRef.get();
+  m_Timers.insert(std::move(timerRef));
 
-  v8::Local<v8::Object> pRetObj =
-      pRuntime->NewFxDynamicObj(CJS_TimerObj::GetObjDefnID());
+  v8::Local<v8::Object> pRetObj = pRuntime->NewFXJSBoundObject(
+      CJS_TimerObj::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
   if (pRetObj.IsEmpty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_TimerObj* pJS_TimerObj =
-      static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj));
-  TimerObj* pTimerObj = static_cast<TimerObj*>(pJS_TimerObj->GetEmbedObject());
-  pTimerObj->SetTimer(timerRef);
+  auto* pJS_TimerObj =
+      static_cast<CJS_TimerObj*>(CFXJS_Engine::GetObjectPrivate(pRetObj));
 
-  return CJS_Return(pRetObj);
+  pJS_TimerObj->SetTimer(pTimerRef);
+  return CJS_Result::Success(pRetObj);
 }
 
-CJS_Return app::clearTimeOut(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_App::clearTimeOut(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  app::ClearTimerCommon(pRuntime, params[0]);
-  return CJS_Return(true);
+  CJS_App::ClearTimerCommon(pRuntime, params[0]);
+  return CJS_Result::Success();
 }
 
-CJS_Return app::clearInterval(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_App::clearInterval(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  app::ClearTimerCommon(pRuntime, params[0]);
-  return CJS_Return(true);
+  CJS_App::ClearTimerCommon(pRuntime, params[0]);
+  return CJS_Result::Success();
 }
 
-void app::ClearTimerCommon(CJS_Runtime* pRuntime, v8::Local<v8::Value> param) {
+void CJS_App::ClearTimerCommon(CJS_Runtime* pRuntime,
+                               v8::Local<v8::Value> param) {
   if (!param->IsObject())
     return;
 
   v8::Local<v8::Object> pObj = pRuntime->ToObject(param);
-  if (CFXJS_Engine::GetObjDefnID(pObj) != CJS_TimerObj::GetObjDefnID())
+  auto pTimer = JSGetObject<CJS_TimerObj>(pObj);
+  if (!pTimer)
     return;
 
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(pObj));
-  if (!pJSObj)
-    return;
-
-  TimerObj* pTimerObj = static_cast<TimerObj*>(pJSObj->GetEmbedObject());
-  if (!pTimerObj)
-    return;
-
-  GlobalTimer::Cancel(pTimerObj->GetTimerID());
+  GlobalTimer::Cancel(pTimer->GetTimerID());
 }
 
-CJS_Return app::execMenuItem(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+CJS_Result CJS_App::execMenuItem(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-void app::TimerProc(GlobalTimer* pTimer) {
+void CJS_App::TimerProc(GlobalTimer* pTimer) {
   CJS_Runtime* pRuntime = pTimer->GetRuntime();
   if (pRuntime && (!pTimer->IsOneShot() || pTimer->GetTimeOut() > 0))
     RunJsScript(pRuntime, pTimer->GetJScript());
 }
 
-void app::CancelProc(GlobalTimer* pTimer) {
+void CJS_App::CancelProc(GlobalTimer* pTimer) {
   m_Timers.erase(pdfium::FakeUniquePtr<GlobalTimer>(pTimer));
 }
 
-void app::RunJsScript(CJS_Runtime* pRuntime, const WideString& wsScript) {
-  if (!pRuntime->IsBlocking()) {
-    IJS_EventContext* pContext = pRuntime->NewEventContext();
-    pContext->OnExternal_Exec();
-    WideString wtInfo;
-    pContext->RunScript(wsScript, &wtInfo);
-    pRuntime->ReleaseEventContext(pContext);
-  }
+void CJS_App::RunJsScript(CJS_Runtime* pRuntime, const WideString& wsScript) {
+  if (pRuntime->IsBlocking())
+    return;
+
+  IJS_Runtime::ScopedEventContext pContext(pRuntime);
+  pContext->OnExternal_Exec();
+  pContext->RunScript(wsScript);
 }
 
-CJS_Return app::goBack(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params) {
-  // Not supported.
-  return CJS_Return(true);
+CJS_Result CJS_App::goBack(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params) {
+  // Not supported, but do not return error.
+  return CJS_Result::Success();
 }
 
-CJS_Return app::goForward(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params) {
-  // Not supported.
-  return CJS_Return(true);
+CJS_Result CJS_App::goForward(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  // Not supported, but do not return error.
+  return CJS_Result::Success();
 }
 
-CJS_Return app::mailMsg(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
-  std::vector<v8::Local<v8::Value>> newParams =
-      ExpandKeywordParams(pRuntime, params, 6, L"bUI", L"cTo", L"cCc", L"cBcc",
-                          L"cSubject", L"cMsg");
+CJS_Result CJS_App::mailMsg(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params) {
+  std::vector<v8::Local<v8::Value>> newParams = ExpandKeywordParams(
+      pRuntime, params, 6, "bUI", "cTo", "cCc", "cBcc", "cSubject", "cMsg");
 
-  if (!IsTypeKnown(newParams[0]))
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  if (!IsExpandedParamKnown(newParams[0]))
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   bool bUI = pRuntime->ToBoolean(newParams[0]);
   WideString cTo;
-  if (IsTypeKnown(newParams[1])) {
+  if (IsExpandedParamKnown(newParams[1])) {
     cTo = pRuntime->ToWideString(newParams[1]);
   } else {
     // cTo parameter required when UI not invoked.
     if (!bUI)
-      return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+      return CJS_Result::Failure(JSMessage::kParamError);
   }
 
   WideString cCc;
-  if (IsTypeKnown(newParams[2]))
+  if (IsExpandedParamKnown(newParams[2]))
     cCc = pRuntime->ToWideString(newParams[2]);
 
   WideString cBcc;
-  if (IsTypeKnown(newParams[3]))
+  if (IsExpandedParamKnown(newParams[3]))
     cBcc = pRuntime->ToWideString(newParams[3]);
 
   WideString cSubject;
-  if (IsTypeKnown(newParams[4]))
+  if (IsExpandedParamKnown(newParams[4]))
     cSubject = pRuntime->ToWideString(newParams[4]);
 
   WideString cMsg;
-  if (IsTypeKnown(newParams[5]))
+  if (IsExpandedParamKnown(newParams[5]))
     cMsg = pRuntime->ToWideString(newParams[5]);
 
   pRuntime->BeginBlock();
-  pRuntime->GetFormFillEnv()->JS_docmailForm(nullptr, 0, bUI, cTo.c_str(),
-                                             cSubject.c_str(), cCc.c_str(),
-                                             cBcc.c_str(), cMsg.c_str());
+  pRuntime->GetFormFillEnv()->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject,
+                                             cCc, cBcc, cMsg);
   pRuntime->EndBlock();
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return app::launchURL(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params) {
-  // Unsafe, not supported.
-  return CJS_Return(true);
+CJS_Result CJS_App::launchURL(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  // Unsafe, not supported, but do not return error.
+  return CJS_Result::Success();
 }
 
-CJS_Return app::get_runtime_highlight(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewBoolean(m_bRuntimeHighLight));
+CJS_Result CJS_App::get_runtime_highlight(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewBoolean(m_bRuntimeHighLight));
 }
 
-CJS_Return app::set_runtime_highlight(CJS_Runtime* pRuntime,
-                                      v8::Local<v8::Value> vp) {
+CJS_Result CJS_App::set_runtime_highlight(CJS_Runtime* pRuntime,
+                                          v8::Local<v8::Value> vp) {
   m_bRuntimeHighLight = pRuntime->ToBoolean(vp);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return app::get_fullscreen(CJS_Runtime* pRuntime) {
-  return CJS_Return(false);
+CJS_Result CJS_App::get_fullscreen(CJS_Runtime* pRuntime) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::set_fullscreen(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_fullscreen(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::popUpMenu(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+CJS_Result CJS_App::popUpMenu(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::browseForDoc(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
-  // Unsafe, not supported.
-  return CJS_Return(true);
+CJS_Result CJS_App::browseForDoc(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  // Unsafe, not supported, but do not return an error.
+  return CJS_Result::Success();
 }
 
-WideString app::SysPathToPDFPath(const WideString& sOldPath) {
+WideString CJS_App::SysPathToPDFPath(const WideString& sOldPath) {
   WideString sRet = L"/";
   for (const wchar_t& c : sOldPath) {
     if (c != L':')
@@ -512,66 +505,67 @@
   return sRet;
 }
 
-CJS_Return app::newDoc(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+CJS_Result CJS_App::newDoc(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::openDoc(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+CJS_Result CJS_App::openDoc(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::response(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_App::response(CJS_Runtime* pRuntime,
+                             const std::vector<v8::Local<v8::Value>>& params) {
   std::vector<v8::Local<v8::Value>> newParams =
-      ExpandKeywordParams(pRuntime, params, 5, L"cQuestion", L"cTitle",
-                          L"cDefault", L"bPassword", L"cLabel");
+      ExpandKeywordParams(pRuntime, params, 5, "cQuestion", "cTitle",
+                          "cDefault", "bPassword", "cLabel");
 
-  if (!IsTypeKnown(newParams[0]))
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  if (!IsExpandedParamKnown(newParams[0]))
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString swQuestion = pRuntime->ToWideString(newParams[0]);
   WideString swTitle = L"PDF";
-  if (IsTypeKnown(newParams[1]))
+  if (IsExpandedParamKnown(newParams[1]))
     swTitle = pRuntime->ToWideString(newParams[1]);
 
   WideString swDefault;
-  if (IsTypeKnown(newParams[2]))
+  if (IsExpandedParamKnown(newParams[2]))
     swDefault = pRuntime->ToWideString(newParams[2]);
 
   bool bPassword = false;
-  if (IsTypeKnown(newParams[3]))
+  if (IsExpandedParamKnown(newParams[3]))
     bPassword = pRuntime->ToBoolean(newParams[3]);
 
   WideString swLabel;
-  if (IsTypeKnown(newParams[4]))
+  if (IsExpandedParamKnown(newParams[4]))
     swLabel = pRuntime->ToWideString(newParams[4]);
 
   const int MAX_INPUT_BYTES = 2048;
   std::vector<uint8_t> pBuff(MAX_INPUT_BYTES + 2);
   int nLengthBytes = pRuntime->GetFormFillEnv()->JS_appResponse(
-      swQuestion.c_str(), swTitle.c_str(), swDefault.c_str(), swLabel.c_str(),
-      bPassword, pBuff.data(), MAX_INPUT_BYTES);
+      swQuestion, swTitle, swDefault, swLabel, bPassword, pBuff.data(),
+      MAX_INPUT_BYTES);
 
   if (nLengthBytes < 0 || nLengthBytes > MAX_INPUT_BYTES)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamTooLongError));
+    return CJS_Result::Failure(JSMessage::kParamTooLongError);
 
-  return CJS_Return(pRuntime->NewString(
+  return CJS_Result::Success(pRuntime->NewString(
       WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
                               nLengthBytes / sizeof(uint16_t))
-          .c_str()));
+          .AsStringView()));
 }
 
-CJS_Return app::get_media(CJS_Runtime* pRuntime) {
-  return CJS_Return(false);
+CJS_Result CJS_App::get_media(CJS_Runtime* pRuntime) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::set_media(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_App::set_media(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return app::execDialog(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_App::execDialog(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
diff --git a/fxjs/cjs_app.h b/fxjs/cjs_app.h
index 703c705..524a7f0 100644
--- a/fxjs/cjs_app.h
+++ b/fxjs/cjs_app.h
@@ -11,159 +11,153 @@
 #include <set>
 #include <vector>
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
 class CJS_Runtime;
 class GlobalTimer;
 
-class app : public CJS_EmbedObj {
+class CJS_App final : public CJS_Object {
  public:
-  explicit app(CJS_Object* pJSObject);
-  ~app() override;
+  static int GetObjDefnID();
+  static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  CJS_Return get_active_docs(CJS_Runtime* pRuntime);
-  CJS_Return set_active_docs(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_calculate(CJS_Runtime* pRuntime);
-  CJS_Return set_calculate(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_forms_version(CJS_Runtime* pRuntime);
-  CJS_Return set_forms_version(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_fs(CJS_Runtime* pRuntime);
-  CJS_Return set_fs(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_fullscreen(CJS_Runtime* pRuntime);
-  CJS_Return set_fullscreen(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_language(CJS_Runtime* pRuntime);
-  CJS_Return set_language(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_media(CJS_Runtime* pRuntime);
-  CJS_Return set_media(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_platform(CJS_Runtime* pRuntime);
-  CJS_Return set_platform(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_runtime_highlight(CJS_Runtime* pRuntime);
-  CJS_Return set_runtime_highlight(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp);
-
-  CJS_Return get_viewer_type(CJS_Runtime* pRuntime);
-  CJS_Return set_viewer_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_viewer_variation(CJS_Runtime* pRuntime);
-  CJS_Return set_viewer_variation(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp);
-
-  CJS_Return get_viewer_version(CJS_Runtime* pRuntime);
-  CJS_Return set_viewer_version(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return alert(CJS_Runtime* pRuntime,
-                   const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return beep(CJS_Runtime* pRuntime,
-                  const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return browseForDoc(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return clearInterval(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return clearTimeOut(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return execDialog(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return execMenuItem(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return findComponent(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return goBack(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return goForward(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return launchURL(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return mailMsg(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return newFDF(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return newDoc(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return openDoc(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return openFDF(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return popUpMenuEx(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return popUpMenu(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return response(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return setInterval(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return setTimeOut(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
+  CJS_App(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_App() override;
 
   void TimerProc(GlobalTimer* pTimer);
   void CancelProc(GlobalTimer* pTimer);
 
   static WideString SysPathToPDFPath(const WideString& sOldPath);
 
- private:
-  // CJS_EmbedObj
-  void RunJsScript(CJS_Runtime* pRuntime, const WideString& wsScript);
+  JS_STATIC_PROP(activeDocs, active_docs, CJS_App)
+  JS_STATIC_PROP(calculate, calculate, CJS_App)
+  JS_STATIC_PROP(formsVersion, forms_version, CJS_App)
+  JS_STATIC_PROP(fs, fs, CJS_App)
+  JS_STATIC_PROP(fullscreen, fullscreen, CJS_App)
+  JS_STATIC_PROP(language, language, CJS_App)
+  JS_STATIC_PROP(media, media, CJS_App)
+  JS_STATIC_PROP(platform, platform, CJS_App)
+  JS_STATIC_PROP(runtimeHighlight, runtime_highlight, CJS_App)
+  JS_STATIC_PROP(viewerType, viewer_type, CJS_App)
+  JS_STATIC_PROP(viewerVariation, viewer_variation, CJS_App)
+  JS_STATIC_PROP(viewerVersion, viewer_version, CJS_App)
 
-  void ClearTimerCommon(CJS_Runtime* pRuntime, v8::Local<v8::Value> param);
-
-  bool m_bCalculate;
-  bool m_bRuntimeHighLight;
-  std::set<std::unique_ptr<GlobalTimer>> m_Timers;
-};
-
-class CJS_App : public CJS_Object {
- public:
-  static void DefineJSObjects(CFXJS_Engine* pEngine);
-
-  explicit CJS_App(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_App() override {}
-
-  JS_STATIC_PROP(activeDocs, active_docs, app);
-  JS_STATIC_PROP(calculate, calculate, app);
-  JS_STATIC_PROP(formsVersion, forms_version, app);
-  JS_STATIC_PROP(fs, fs, app);
-  JS_STATIC_PROP(fullscreen, fullscreen, app);
-  JS_STATIC_PROP(language, language, app);
-  JS_STATIC_PROP(media, media, app);
-  JS_STATIC_PROP(platform, platform, app);
-  JS_STATIC_PROP(runtimeHighlight, runtime_highlight, app);
-  JS_STATIC_PROP(viewerType, viewer_type, app);
-  JS_STATIC_PROP(viewerVariation, viewer_variation, app);
-  JS_STATIC_PROP(viewerVersion, viewer_version, app);
-
-  JS_STATIC_METHOD(alert, app);
-  JS_STATIC_METHOD(beep, app);
-  JS_STATIC_METHOD(browseForDoc, app);
-  JS_STATIC_METHOD(clearInterval, app);
-  JS_STATIC_METHOD(clearTimeOut, app);
-  JS_STATIC_METHOD(execDialog, app);
-  JS_STATIC_METHOD(execMenuItem, app);
-  JS_STATIC_METHOD(findComponent, app);
-  JS_STATIC_METHOD(goBack, app);
-  JS_STATIC_METHOD(goForward, app);
-  JS_STATIC_METHOD(launchURL, app);
-  JS_STATIC_METHOD(mailMsg, app);
-  JS_STATIC_METHOD(newFDF, app);
-  JS_STATIC_METHOD(newDoc, app);
-  JS_STATIC_METHOD(openDoc, app);
-  JS_STATIC_METHOD(openFDF, app);
-  JS_STATIC_METHOD(popUpMenuEx, app);
-  JS_STATIC_METHOD(popUpMenu, app);
-  JS_STATIC_METHOD(response, app);
-  JS_STATIC_METHOD(setInterval, app);
-  JS_STATIC_METHOD(setTimeOut, app);
+  JS_STATIC_METHOD(alert, CJS_App)
+  JS_STATIC_METHOD(beep, CJS_App)
+  JS_STATIC_METHOD(browseForDoc, CJS_App)
+  JS_STATIC_METHOD(clearInterval, CJS_App)
+  JS_STATIC_METHOD(clearTimeOut, CJS_App)
+  JS_STATIC_METHOD(execDialog, CJS_App)
+  JS_STATIC_METHOD(execMenuItem, CJS_App)
+  JS_STATIC_METHOD(findComponent, CJS_App)
+  JS_STATIC_METHOD(goBack, CJS_App)
+  JS_STATIC_METHOD(goForward, CJS_App)
+  JS_STATIC_METHOD(launchURL, CJS_App)
+  JS_STATIC_METHOD(mailMsg, CJS_App)
+  JS_STATIC_METHOD(newFDF, CJS_App)
+  JS_STATIC_METHOD(newDoc, CJS_App)
+  JS_STATIC_METHOD(openDoc, CJS_App)
+  JS_STATIC_METHOD(openFDF, CJS_App)
+  JS_STATIC_METHOD(popUpMenuEx, CJS_App)
+  JS_STATIC_METHOD(popUpMenu, CJS_App)
+  JS_STATIC_METHOD(response, CJS_App)
+  JS_STATIC_METHOD(setInterval, CJS_App)
+  JS_STATIC_METHOD(setTimeOut, CJS_App)
 
  private:
   static int ObjDefnID;
+  static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
+
+  CJS_Result get_active_docs(CJS_Runtime* pRuntime);
+  CJS_Result set_active_docs(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_calculate(CJS_Runtime* pRuntime);
+  CJS_Result set_calculate(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_forms_version(CJS_Runtime* pRuntime);
+  CJS_Result set_forms_version(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_fs(CJS_Runtime* pRuntime);
+  CJS_Result set_fs(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_fullscreen(CJS_Runtime* pRuntime);
+  CJS_Result set_fullscreen(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_language(CJS_Runtime* pRuntime);
+  CJS_Result set_language(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_media(CJS_Runtime* pRuntime);
+  CJS_Result set_media(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_platform(CJS_Runtime* pRuntime);
+  CJS_Result set_platform(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_runtime_highlight(CJS_Runtime* pRuntime);
+  CJS_Result set_runtime_highlight(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp);
+
+  CJS_Result get_viewer_type(CJS_Runtime* pRuntime);
+  CJS_Result set_viewer_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_viewer_variation(CJS_Runtime* pRuntime);
+  CJS_Result set_viewer_variation(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp);
+
+  CJS_Result get_viewer_version(CJS_Runtime* pRuntime);
+  CJS_Result set_viewer_version(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result alert(CJS_Runtime* pRuntime,
+                   const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result beep(CJS_Runtime* pRuntime,
+                  const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result browseForDoc(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result clearInterval(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result clearTimeOut(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result execDialog(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result execMenuItem(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result findComponent(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result goBack(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result goForward(CJS_Runtime* pRuntime,
+                       const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result launchURL(CJS_Runtime* pRuntime,
+                       const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result mailMsg(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result newFDF(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result newDoc(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result openDoc(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result openFDF(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result popUpMenuEx(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result popUpMenu(CJS_Runtime* pRuntime,
+                       const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result response(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result setInterval(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result setTimeOut(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
+
+  void RunJsScript(CJS_Runtime* pRuntime, const WideString& wsScript);
+  void ClearTimerCommon(CJS_Runtime* pRuntime, v8::Local<v8::Value> param);
+
+  bool m_bCalculate = true;
+  bool m_bRuntimeHighLight = false;
+  std::set<std::unique_ptr<GlobalTimer>> m_Timers;
 };
 
 #endif  // FXJS_CJS_APP_H_
diff --git a/fxjs/cjs_border.cpp b/fxjs/cjs_border.cpp
index 95693f4..35204d7 100644
--- a/fxjs/cjs_border.cpp
+++ b/fxjs/cjs_border.cpp
@@ -19,5 +19,5 @@
 void CJS_Border::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID =
       pEngine->DefineObj("border", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_border.h b/fxjs/cjs_border.h
index b3fcef7..0a306f7 100644
--- a/fxjs/cjs_border.h
+++ b/fxjs/cjs_border.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_BORDER_H_
 #define FXJS_CJS_BORDER_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_Border : public CJS_Object {
+class CJS_Border final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Border(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Border() override {}
+  CJS_Border() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_color.cpp b/fxjs/cjs_color.cpp
index 58a98dd..2b75e75 100644
--- a/fxjs/cjs_color.cpp
+++ b/fxjs/cjs_color.cpp
@@ -6,13 +6,15 @@
 
 #include "fxjs/cjs_color.h"
 
+#include <algorithm>
 #include <vector>
 
-#include "fxjs/JS_Define.h"
+#include "core/fxge/cfx_color.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventhandler.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_runtime.h"
+#include "fxjs/js_define.h"
 
 const JSPropertySpec CJS_Color::PropertySpecs[] = {
     {"black", get_black_static, set_black_static},
@@ -32,40 +34,45 @@
                                                {"equal", equal_static}};
 
 int CJS_Color::ObjDefnID = -1;
+const char CJS_Color::kName[] = "color";
 
 // static
-void CJS_Color::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID = pEngine->DefineObj("color", FXJSOBJTYPE_STATIC,
-                                 JSConstructor<CJS_Color, color>,
-                                 JSDestructor<CJS_Color>);
-  DefineProps(pEngine, ObjDefnID, PropertySpecs, FX_ArraySize(PropertySpecs));
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
+int CJS_Color::GetObjDefnID() {
+  return ObjDefnID;
 }
 
 // static
-v8::Local<v8::Array> color::ConvertPWLColorToArray(CJS_Runtime* pRuntime,
-                                                   const CFX_Color& color) {
+void CJS_Color::DefineJSObjects(CFXJS_Engine* pEngine) {
+  ObjDefnID = pEngine->DefineObj(CJS_Color::kName, FXJSOBJTYPE_STATIC,
+                                 JSConstructor<CJS_Color>, JSDestructor);
+  DefineProps(pEngine, ObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, ObjDefnID, MethodSpecs);
+}
+
+// static
+v8::Local<v8::Array> CJS_Color::ConvertPWLColorToArray(CJS_Runtime* pRuntime,
+                                                       const CFX_Color& color) {
   v8::Local<v8::Array> array;
   switch (color.nColorType) {
     case CFX_Color::kTransparent:
       array = pRuntime->NewArray();
-      pRuntime->PutArrayElement(array, 0, pRuntime->NewString(L"T"));
+      pRuntime->PutArrayElement(array, 0, pRuntime->NewString("T"));
       break;
     case CFX_Color::kGray:
       array = pRuntime->NewArray();
-      pRuntime->PutArrayElement(array, 0, pRuntime->NewString(L"G"));
+      pRuntime->PutArrayElement(array, 0, pRuntime->NewString("G"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
       break;
     case CFX_Color::kRGB:
       array = pRuntime->NewArray();
-      pRuntime->PutArrayElement(array, 0, pRuntime->NewString(L"RGB"));
+      pRuntime->PutArrayElement(array, 0, pRuntime->NewString("RGB"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
       pRuntime->PutArrayElement(array, 2, pRuntime->NewNumber(color.fColor2));
       pRuntime->PutArrayElement(array, 3, pRuntime->NewNumber(color.fColor3));
       break;
     case CFX_Color::kCMYK:
       array = pRuntime->NewArray();
-      pRuntime->PutArrayElement(array, 0, pRuntime->NewString(L"CMYK"));
+      pRuntime->PutArrayElement(array, 0, pRuntime->NewString("CMYK"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
       pRuntime->PutArrayElement(array, 2, pRuntime->NewNumber(color.fColor2));
       pRuntime->PutArrayElement(array, 3, pRuntime->NewNumber(color.fColor3));
@@ -76,15 +83,15 @@
 }
 
 // static
-CFX_Color color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime,
-                                        v8::Local<v8::Array> array) {
+CFX_Color CJS_Color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime,
+                                            v8::Local<v8::Array> array) {
   int nArrayLen = pRuntime->GetArrayLength(array);
   if (nArrayLen < 1)
     return CFX_Color();
 
   WideString sSpace =
       pRuntime->ToWideString(pRuntime->GetArrayElement(array, 0));
-  if (sSpace == L"T")
+  if (sSpace.EqualsASCII("T"))
     return CFX_Color(CFX_Color::kTransparent);
 
   float d1 = 0;
@@ -92,8 +99,7 @@
     d1 = static_cast<float>(
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 1)));
   }
-
-  if (sSpace == L"G")
+  if (sSpace.EqualsASCII("G"))
     return CFX_Color(CFX_Color::kGray, d1);
 
   float d2 = 0;
@@ -106,8 +112,7 @@
     d3 = static_cast<float>(
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 3)));
   }
-
-  if (sSpace == L"RGB")
+  if (sSpace.EqualsASCII("RGB"))
     return CFX_Color(CFX_Color::kRGB, d1, d2, d3);
 
   float d4 = 0;
@@ -115,181 +120,191 @@
     d4 = static_cast<float>(
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 4)));
   }
-  if (sSpace == L"CMYK")
+  if (sSpace.EqualsASCII("CMYK"))
     return CFX_Color(CFX_Color::kCMYK, d1, d2, d3, d4);
 
   return CFX_Color();
 }
 
-color::color(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {
-  m_crTransparent = CFX_Color(CFX_Color::kTransparent);
-  m_crBlack = CFX_Color(CFX_Color::kGray, 0);
-  m_crWhite = CFX_Color(CFX_Color::kGray, 1);
-  m_crRed = CFX_Color(CFX_Color::kRGB, 1, 0, 0);
-  m_crGreen = CFX_Color(CFX_Color::kRGB, 0, 1, 0);
-  m_crBlue = CFX_Color(CFX_Color::kRGB, 0, 0, 1);
-  m_crCyan = CFX_Color(CFX_Color::kCMYK, 1, 0, 0, 0);
-  m_crMagenta = CFX_Color(CFX_Color::kCMYK, 0, 1, 0, 0);
-  m_crYellow = CFX_Color(CFX_Color::kCMYK, 0, 0, 1, 0);
-  m_crDKGray = CFX_Color(CFX_Color::kGray, 0.25);
-  m_crGray = CFX_Color(CFX_Color::kGray, 0.5);
-  m_crLTGray = CFX_Color(CFX_Color::kGray, 0.75);
-}
+CJS_Color::CJS_Color(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime),
+      m_crTransparent(CFX_Color::kTransparent),
+      m_crBlack(CFX_Color::kGray, 0),
+      m_crWhite(CFX_Color::kGray, 1),
+      m_crRed(CFX_Color::kRGB, 1, 0, 0),
+      m_crGreen(CFX_Color::kRGB, 0, 1, 0),
+      m_crBlue(CFX_Color::kRGB, 0, 0, 1),
+      m_crCyan(CFX_Color::kCMYK, 1, 0, 0, 0),
+      m_crMagenta(CFX_Color::kCMYK, 0, 1, 0, 0),
+      m_crYellow(CFX_Color::kCMYK, 0, 0, 1, 0),
+      m_crDKGray(CFX_Color::kGray, 0.25),
+      m_crGray(CFX_Color::kGray, 0.5),
+      m_crLTGray(CFX_Color::kGray, 0.75) {}
 
-color::~color() {}
+CJS_Color::~CJS_Color() = default;
 
-CJS_Return color::get_transparent(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_transparent(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crTransparent);
 }
 
-CJS_Return color::set_transparent(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_transparent(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crTransparent);
 }
 
-CJS_Return color::get_black(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_black(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crBlack);
 }
 
-CJS_Return color::set_black(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_black(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crBlack);
 }
 
-CJS_Return color::get_white(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_white(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crWhite);
 }
 
-CJS_Return color::set_white(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_white(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crWhite);
 }
 
-CJS_Return color::get_red(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_red(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crRed);
 }
 
-CJS_Return color::set_red(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_red(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crRed);
 }
 
-CJS_Return color::get_green(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_green(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crGreen);
 }
 
-CJS_Return color::set_green(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_green(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crGreen);
 }
 
-CJS_Return color::get_blue(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_blue(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crBlue);
 }
 
-CJS_Return color::set_blue(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_blue(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crBlue);
 }
 
-CJS_Return color::get_cyan(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_cyan(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crCyan);
 }
 
-CJS_Return color::set_cyan(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_cyan(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crCyan);
 }
 
-CJS_Return color::get_magenta(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_magenta(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crMagenta);
 }
 
-CJS_Return color::set_magenta(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_magenta(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crMagenta);
 }
 
-CJS_Return color::get_yellow(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_yellow(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crYellow);
 }
 
-CJS_Return color::set_yellow(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_yellow(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crYellow);
 }
 
-CJS_Return color::get_dark_gray(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_dark_gray(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crDKGray);
 }
 
-CJS_Return color::set_dark_gray(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_dark_gray(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crDKGray);
 }
 
-CJS_Return color::get_gray(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_gray(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crGray);
 }
 
-CJS_Return color::set_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crGray);
 }
 
-CJS_Return color::get_light_gray(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Color::get_light_gray(CJS_Runtime* pRuntime) {
   return GetPropertyHelper(pRuntime, &m_crLTGray);
 }
 
-CJS_Return color::set_light_gray(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
+CJS_Result CJS_Color::set_light_gray(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
   return SetPropertyHelper(pRuntime, vp, &m_crLTGray);
 }
 
-CJS_Return color::GetPropertyHelper(CJS_Runtime* pRuntime, CFX_Color* var) {
+CJS_Result CJS_Color::GetPropertyHelper(CJS_Runtime* pRuntime, CFX_Color* var) {
   v8::Local<v8::Value> array = ConvertPWLColorToArray(pRuntime, *var);
   if (array.IsEmpty())
-    return CJS_Return(pRuntime->NewArray());
-  return CJS_Return(array);
+    return CJS_Result::Success(pRuntime->NewArray());
+
+  return CJS_Result::Success(array);
 }
 
-CJS_Return color::SetPropertyHelper(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp,
-                                    CFX_Color* var) {
-  if (vp.IsEmpty() || !vp->IsArray())
-    return CJS_Return(false);
+CJS_Result CJS_Color::SetPropertyHelper(CJS_Runtime* pRuntime,
+                                        v8::Local<v8::Value> vp,
+                                        CFX_Color* var) {
+  if (vp.IsEmpty())
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  if (!vp->IsArray())
+    return CJS_Result::Failure(JSMessage::kTypeError);
 
   *var = ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(vp));
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return color::convert(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params) {
-  int iSize = params.size();
-  if (iSize < 2)
-    return CJS_Return(false);
+CJS_Result CJS_Color::convert(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() < 2)
+    return CJS_Result::Failure(JSMessage::kParamError);
+
   if (params[0].IsEmpty() || !params[0]->IsArray())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kTypeError);
 
   WideString sDestSpace = pRuntime->ToWideString(params[1]);
   int nColorType = CFX_Color::kTransparent;
-  if (sDestSpace == L"T")
+  if (sDestSpace.EqualsASCII("T"))
     nColorType = CFX_Color::kTransparent;
-  else if (sDestSpace == L"G")
+  else if (sDestSpace.EqualsASCII("G"))
     nColorType = CFX_Color::kGray;
-  else if (sDestSpace == L"RGB")
+  else if (sDestSpace.EqualsASCII("RGB"))
     nColorType = CFX_Color::kRGB;
-  else if (sDestSpace == L"CMYK")
+  else if (sDestSpace.EqualsASCII("CMYK"))
     nColorType = CFX_Color::kCMYK;
 
   CFX_Color color =
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[0]));
-
   v8::Local<v8::Value> array =
       ConvertPWLColorToArray(pRuntime, color.ConvertColorType(nColorType));
   if (array.IsEmpty())
-    return CJS_Return(pRuntime->NewArray());
-  return CJS_Return(array);
+    return CJS_Result::Success(pRuntime->NewArray());
+
+  return CJS_Result::Success(array);
 }
 
-CJS_Return color::equal(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Color::equal(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 2)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
   if (params[0].IsEmpty() || !params[0]->IsArray() || params[1].IsEmpty() ||
       !params[1]->IsArray()) {
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kTypeError);
   }
 
   CFX_Color color1 =
@@ -297,6 +312,8 @@
   CFX_Color color2 =
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[1]));
 
-  color1 = color1.ConvertColorType(color2.nColorType);
-  return CJS_Return(pRuntime->NewBoolean(color1 == color2));
+  // Relies on higher values having more components.
+  int32_t best = std::max(color1.nColorType, color2.nColorType);
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      color1.ConvertColorType(best) == color2.ConvertColorType(best)));
 }
diff --git a/fxjs/cjs_color.h b/fxjs/cjs_color.h
index 5f7c1e5..03aa735 100644
--- a/fxjs/cjs_color.h
+++ b/fxjs/cjs_color.h
@@ -9,63 +9,86 @@
 
 #include <vector>
 
-#include "fpdfsdk/pwl/cpwl_wnd.h"
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
-class color : public CJS_EmbedObj {
+class CJS_Color final : public CJS_Object {
  public:
+  static int GetObjDefnID();
+  static void DefineJSObjects(CFXJS_Engine* pEngine);
   static v8::Local<v8::Array> ConvertPWLColorToArray(CJS_Runtime* pRuntime,
                                                      const CFX_Color& color);
   static CFX_Color ConvertArrayToPWLColor(CJS_Runtime* pRuntime,
                                           v8::Local<v8::Array> array);
 
-  explicit color(CJS_Object* pJSObject);
-  ~color() override;
+  CJS_Color(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Color() override;
 
-  CJS_Return get_black(CJS_Runtime* pRuntime);
-  CJS_Return set_black(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+  JS_STATIC_PROP(black, black, CJS_Color)
+  JS_STATIC_PROP(blue, blue, CJS_Color)
+  JS_STATIC_PROP(cyan, cyan, CJS_Color)
+  JS_STATIC_PROP(dkGray, dark_gray, CJS_Color)
+  JS_STATIC_PROP(gray, gray, CJS_Color)
+  JS_STATIC_PROP(green, green, CJS_Color)
+  JS_STATIC_PROP(ltGray, light_gray, CJS_Color)
+  JS_STATIC_PROP(magenta, magenta, CJS_Color)
+  JS_STATIC_PROP(red, red, CJS_Color)
+  JS_STATIC_PROP(transparent, transparent, CJS_Color)
+  JS_STATIC_PROP(white, white, CJS_Color)
+  JS_STATIC_PROP(yellow, yellow, CJS_Color)
 
-  CJS_Return get_blue(CJS_Runtime* pRuntime);
-  CJS_Return set_blue(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_cyan(CJS_Runtime* pRuntime);
-  CJS_Return set_cyan(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_dark_gray(CJS_Runtime* pRuntime);
-  CJS_Return set_dark_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_gray(CJS_Runtime* pRuntime);
-  CJS_Return set_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_green(CJS_Runtime* pRuntime);
-  CJS_Return set_green(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_light_gray(CJS_Runtime* pRuntime);
-  CJS_Return set_light_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_magenta(CJS_Runtime* pRuntime);
-  CJS_Return set_magenta(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_red(CJS_Runtime* pRuntime);
-  CJS_Return set_red(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_transparent(CJS_Runtime* pRuntime);
-  CJS_Return set_transparent(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_white(CJS_Runtime* pRuntime);
-  CJS_Return set_white(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_yellow(CJS_Runtime* pRuntime);
-  CJS_Return set_yellow(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return convert(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return equal(CJS_Runtime* pRuntime,
-                   const std::vector<v8::Local<v8::Value>>& params);
+  JS_STATIC_METHOD(convert, CJS_Color)
+  JS_STATIC_METHOD(equal, CJS_Color)
 
  private:
-  CJS_Return GetPropertyHelper(CJS_Runtime* pRuntime, CFX_Color* val);
-  CJS_Return SetPropertyHelper(CJS_Runtime* pRuntime,
+  static int ObjDefnID;
+  static const char kName[];
+  static const JSPropertySpec PropertySpecs[];
+  static const JSMethodSpec MethodSpecs[];
+
+  CJS_Result get_black(CJS_Runtime* pRuntime);
+  CJS_Result set_black(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_blue(CJS_Runtime* pRuntime);
+  CJS_Result set_blue(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_cyan(CJS_Runtime* pRuntime);
+  CJS_Result set_cyan(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_dark_gray(CJS_Runtime* pRuntime);
+  CJS_Result set_dark_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_gray(CJS_Runtime* pRuntime);
+  CJS_Result set_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_green(CJS_Runtime* pRuntime);
+  CJS_Result set_green(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_light_gray(CJS_Runtime* pRuntime);
+  CJS_Result set_light_gray(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_magenta(CJS_Runtime* pRuntime);
+  CJS_Result set_magenta(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_red(CJS_Runtime* pRuntime);
+  CJS_Result set_red(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_transparent(CJS_Runtime* pRuntime);
+  CJS_Result set_transparent(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_white(CJS_Runtime* pRuntime);
+  CJS_Result set_white(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_yellow(CJS_Runtime* pRuntime);
+  CJS_Result set_yellow(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result convert(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result equal(CJS_Runtime* pRuntime,
+                   const std::vector<v8::Local<v8::Value>>& params);
+
+  CJS_Result GetPropertyHelper(CJS_Runtime* pRuntime, CFX_Color* val);
+  CJS_Result SetPropertyHelper(CJS_Runtime* pRuntime,
                                v8::Local<v8::Value> vp,
                                CFX_Color* val);
 
@@ -83,33 +106,4 @@
   CFX_Color m_crLTGray;
 };
 
-class CJS_Color : public CJS_Object {
- public:
-  static void DefineJSObjects(CFXJS_Engine* pEngine);
-
-  explicit CJS_Color(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Color() override {}
-
-  JS_STATIC_PROP(black, black, color);
-  JS_STATIC_PROP(blue, blue, color);
-  JS_STATIC_PROP(cyan, cyan, color);
-  JS_STATIC_PROP(dkGray, dark_gray, color);
-  JS_STATIC_PROP(gray, gray, color);
-  JS_STATIC_PROP(green, green, color);
-  JS_STATIC_PROP(ltGray, light_gray, color);
-  JS_STATIC_PROP(magenta, magenta, color);
-  JS_STATIC_PROP(red, red, color);
-  JS_STATIC_PROP(transparent, transparent, color);
-  JS_STATIC_PROP(white, white, color);
-  JS_STATIC_PROP(yellow, yellow, color);
-
-  JS_STATIC_METHOD(convert, color);
-  JS_STATIC_METHOD(equal, color);
-
- private:
-  static int ObjDefnID;
-  static const JSPropertySpec PropertySpecs[];
-  static const JSMethodSpec MethodSpecs[];
-};
-
 #endif  // FXJS_CJS_COLOR_H_
diff --git a/fxjs/cjs_console.cpp b/fxjs/cjs_console.cpp
index 1921cd2..c696bbd 100644
--- a/fxjs/cjs_console.cpp
+++ b/fxjs/cjs_console.cpp
@@ -8,10 +8,10 @@
 
 #include <vector>
 
-#include "fxjs/JS_Define.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventhandler.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
 const JSMethodSpec CJS_Console::MethodSpecs[] = {{"clear", clear_static},
                                                  {"hide", hide_static},
@@ -19,35 +19,42 @@
                                                  {"show", show_static}};
 
 int CJS_Console::ObjDefnID = -1;
+const char CJS_Console::kName[] = "console";
+
+// static
+int CJS_Console::GetObjDefnID() {
+  return ObjDefnID;
+}
 
 // static
 void CJS_Console::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID = pEngine->DefineObj("console", FXJSOBJTYPE_STATIC,
-                                 JSConstructor<CJS_Console, console>,
-                                 JSDestructor<CJS_Console>);
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_Console::kName, FXJSOBJTYPE_STATIC,
+                                 JSConstructor<CJS_Console>, JSDestructor);
+  DefineMethods(pEngine, ObjDefnID, MethodSpecs);
 }
 
-console::console(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
+CJS_Console::CJS_Console(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-console::~console() {}
+CJS_Console::~CJS_Console() = default;
 
-CJS_Return console::clear(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_Console::clear(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return console::hide(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_Console::hide(CJS_Runtime* pRuntime,
+                             const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return console::println(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(params.size() > 0);
+CJS_Result CJS_Console::println(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return console::show(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_Console::show(CJS_Runtime* pRuntime,
+                             const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
diff --git a/fxjs/cjs_console.h b/fxjs/cjs_console.h
index 43a55bc..2bc118a 100644
--- a/fxjs/cjs_console.h
+++ b/fxjs/cjs_console.h
@@ -9,39 +9,35 @@
 
 #include <vector>
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
-class console : public CJS_EmbedObj {
+class CJS_Console final : public CJS_Object {
  public:
-  explicit console(CJS_Object* pJSObject);
-  ~console() override;
-
- public:
-  CJS_Return clear(CJS_Runtime* pRuntime,
-                   const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return hide(CJS_Runtime* pRuntime,
-                  const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return println(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return show(CJS_Runtime* pRuntime,
-                  const std::vector<v8::Local<v8::Value>>& params);
-};
-
-class CJS_Console : public CJS_Object {
- public:
+  static int GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Console(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Console() override {}
+  CJS_Console(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Console() override;
 
-  JS_STATIC_METHOD(clear, console);
-  JS_STATIC_METHOD(hide, console);
-  JS_STATIC_METHOD(println, console);
-  JS_STATIC_METHOD(show, console);
+  JS_STATIC_METHOD(clear, CJS_Console)
+  JS_STATIC_METHOD(hide, CJS_Console)
+  JS_STATIC_METHOD(println, CJS_Console)
+  JS_STATIC_METHOD(show, CJS_Console)
 
  private:
   static int ObjDefnID;
+  static const char kName[];
   static const JSMethodSpec MethodSpecs[];
+
+  CJS_Result clear(CJS_Runtime* pRuntime,
+                   const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result hide(CJS_Runtime* pRuntime,
+                  const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result println(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result show(CJS_Runtime* pRuntime,
+                  const std::vector<v8::Local<v8::Value>>& params);
 };
 
 #endif  // FXJS_CJS_CONSOLE_H_
diff --git a/fxjs/cjs_delaydata.h b/fxjs/cjs_delaydata.h
index deeb65d..f4c7820 100644
--- a/fxjs/cjs_delaydata.h
+++ b/fxjs/cjs_delaydata.h
@@ -7,7 +7,6 @@
 #ifndef FXJS_CJS_DELAYDATA_H_
 #define FXJS_CJS_DELAYDATA_H_
 
-#include <string>
 #include <vector>
 
 #include "core/fxcrt/fx_coordinates.h"
@@ -24,7 +23,7 @@
   WideString sFieldName;
   int32_t num;
   bool b;
-  ByteString string;
+  ByteString bytestring;
   WideString widestring;
   CFX_FloatRect rect;
   CFX_Color color;
diff --git a/fxjs/cjs_display.cpp b/fxjs/cjs_display.cpp
index 6970bfc..71d6c02 100644
--- a/fxjs/cjs_display.cpp
+++ b/fxjs/cjs_display.cpp
@@ -18,5 +18,5 @@
 void CJS_Display::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID =
       pEngine->DefineObj("display", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_display.h b/fxjs/cjs_display.h
index 7b13c38..59b6e9c 100644
--- a/fxjs/cjs_display.h
+++ b/fxjs/cjs_display.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_DISPLAY_H_
 #define FXJS_CJS_DISPLAY_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_Display : public CJS_Object {
+class CJS_Display final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Display(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Display() override {}
+  CJS_Display() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp
index d4eca2f..9294f17 100644
--- a/fxjs/cjs_document.cpp
+++ b/fxjs/cjs_document.cpp
@@ -8,23 +8,24 @@
 
 #include <utility>
 
-#include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfdoc/cpdf_interform.h"
+#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 #include "fpdfsdk/cpdfsdk_annotiteration.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fxjs/cjs_annot.h"
 #include "fxjs/cjs_app.h"
 #include "fxjs/cjs_delaydata.h"
+#include "fxjs/cjs_event_context.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_icon.h"
-#include "fxjs/cjs_printparamsobj.h"
 #include "fxjs/js_resources.h"
 
 const JSPropertySpec CJS_Document::PropertySpecs[] = {
@@ -96,6 +97,7 @@
     {"importAnXFDF", importAnXFDF_static},
     {"importTextData", importTextData_static},
     {"insertPages", insertPages_static},
+    {"mailDoc", mailDoc_static},
     {"mailForm", mailForm_static},
     {"print", print_static},
     {"removeField", removeField_static},
@@ -104,10 +106,10 @@
     {"removeIcon", removeIcon_static},
     {"saveAs", saveAs_static},
     {"submitForm", submitForm_static},
-    {"syncAnnotScan", syncAnnotScan_static},
-    {"mailDoc", mailDoc_static}};
+    {"syncAnnotScan", syncAnnotScan_static}};
 
 int CJS_Document::ObjDefnID = -1;
+const char CJS_Document::kName[] = "Document";
 
 // static
 int CJS_Document::GetObjDefnID() {
@@ -116,80 +118,76 @@
 
 // static
 void CJS_Document::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID = pEngine->DefineObj("Document", FXJSOBJTYPE_GLOBAL,
-                                 JSConstructor<CJS_Document, Document>,
-                                 JSDestructor<CJS_Document>);
-  DefineProps(pEngine, ObjDefnID, PropertySpecs, FX_ArraySize(PropertySpecs));
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_Document::kName, FXJSOBJTYPE_GLOBAL,
+                                 JSConstructor<CJS_Document>, JSDestructor);
+  DefineProps(pEngine, ObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, ObjDefnID, MethodSpecs);
 }
 
-void CJS_Document::InitInstance(IJS_Runtime* pIRuntime) {
-  CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
-  Document* pDoc = static_cast<Document*>(GetEmbedObject());
-  pDoc->SetFormFillEnv(pRuntime->GetFormFillEnv());
+CJS_Document::CJS_Document(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {
+  SetFormFillEnv(GetRuntime()->GetFormFillEnv());
 }
 
-Document::Document(CJS_Object* pJSObject)
-    : CJS_EmbedObj(pJSObject),
-      m_pFormFillEnv(nullptr),
-      m_cwBaseURL(L""),
-      m_bDelay(false) {}
-
-Document::~Document() {}
+CJS_Document::~CJS_Document() = default;
 
 // The total number of fields in document.
-CJS_Return Document::get_num_fields(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_num_fields(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pPDFForm = pInterForm->GetInterForm();
-  return CJS_Return(pRuntime->NewNumber(
+  CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm();
+  return CJS_Result::Success(pRuntime->NewNumber(
       static_cast<int>(pPDFForm->CountFields(WideString()))));
 }
 
-CJS_Return Document::set_num_fields(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_num_fields(CJS_Runtime* pRuntime,
+                                        v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::get_dirty(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_dirty(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(pRuntime->NewBoolean(!!m_pFormFillEnv->GetChangeMark()));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  return CJS_Result::Success(
+      pRuntime->NewBoolean(!!m_pFormFillEnv->GetChangeMark()));
 }
 
-CJS_Return Document::set_dirty(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_dirty(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   pRuntime->ToBoolean(vp) ? m_pFormFillEnv->SetChangeMark()
                           : m_pFormFillEnv->ClearChangeMark();
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_ADBE(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewUndefined());
+CJS_Result CJS_Document::get_ADBE(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewUndefined());
 }
 
-CJS_Return Document::set_ADBE(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_ADBE(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_page_num(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_page_num(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetCurrentView();
   if (!pPageView)
-    return CJS_Return(pRuntime->NewUndefined());
-  return CJS_Return(pRuntime->NewNumber(pPageView->GetPageIndex()));
+    return CJS_Result::Success(pRuntime->NewUndefined());
+
+  return CJS_Result::Success(pRuntime->NewNumber(pPageView->GetPageIndex()));
 }
 
-CJS_Return Document::set_page_num(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_page_num(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   int iPageCount = m_pFormFillEnv->GetPageCount();
   int iPageNum = pRuntime->ToInt32(vp);
@@ -200,287 +198,325 @@
   else if (iPageNum < 0)
     m_pFormFillEnv->JS_docgotoPage(0);
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::addAnnot(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
-  // Not supported.
-  return CJS_Return(true);
-}
-
-CJS_Return Document::addField(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
-  // Not supported.
-  return CJS_Return(true);
-}
-
-CJS_Return Document::exportAsText(
+CJS_Result CJS_Document::addAnnot(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  // Unsafe, not supported.
-  return CJS_Return(true);
+  // Not supported, but do not return an error.
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::exportAsFDF(
+CJS_Result CJS_Document::addField(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  // Unsafe, not supported.
-  return CJS_Return(true);
+  // Not supported, but do not return an error.
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::exportAsXFDF(
+CJS_Result CJS_Document::exportAsText(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  // Unsafe, not supported.
-  return CJS_Return(true);
+  // Unsafe, not supported, but do not return an error.
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::getField(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
-  if (params.size() < 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+CJS_Result CJS_Document::exportAsFDF(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  // Unsafe, not supported, but do not return an error.
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Document::exportAsXFDF(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  // Unsafe, not supported, but do not return an error.
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Document::getField(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty())
+    return CJS_Result::Failure(JSMessage::kParamError);
+
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   WideString wideName = pRuntime->ToWideString(params[0]);
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pPDFForm = pInterForm->GetInterForm();
+  CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm();
   if (pPDFForm->CountFields(wideName) <= 0)
-    return CJS_Return(pRuntime->NewUndefined());
+    return CJS_Result::Success(pRuntime->NewUndefined());
 
-  v8::Local<v8::Object> pFieldObj =
-      pRuntime->NewFxDynamicObj(CJS_Field::GetObjDefnID());
+  v8::Local<v8::Object> pFieldObj = pRuntime->NewFXJSBoundObject(
+      CJS_Field::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
   if (pFieldObj.IsEmpty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_Field* pJSField =
-      static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
-  Field* pField = static_cast<Field*>(pJSField->GetEmbedObject());
-  pField->AttachField(this, wideName);
+  auto* pJSField =
+      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
   if (!pJSField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(pJSField->ToV8Object());
+  pJSField->AttachField(this, wideName);
+  return CJS_Result::Success(pJSField->ToV8Object());
 }
 
 // Gets the name of the nth field in the document
-CJS_Return Document::getNthFieldName(
+CJS_Result CJS_Document::getNthFieldName(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   int nIndex = pRuntime->ToInt32(params[0]);
   if (nIndex < 0)
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pPDFForm = pInterForm->GetInterForm();
+  CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm();
   CPDF_FormField* pField = pPDFForm->GetField(nIndex, WideString());
   if (!pField)
-    return CJS_Return(false);
-  return CJS_Return(pRuntime->NewString(pField->GetFullName().c_str()));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(
+      pRuntime->NewString(pField->GetFullName().AsStringView()));
 }
 
-CJS_Return Document::importAnFDF(
+CJS_Result CJS_Document::importAnFDF(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::importAnXFDF(
+CJS_Result CJS_Document::importAnXFDF(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::importTextData(
+CJS_Result CJS_Document::importTextData(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Document::mailDoc(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!m_pFormFillEnv)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  std::vector<v8::Local<v8::Value>> newParams = ExpandKeywordParams(
+      pRuntime, params, 6, "bUI", "cTo", "cCc", "cBcc", "cSubject", "cMsg");
+
+  bool bUI = true;
+  if (IsExpandedParamKnown(newParams[0]))
+    bUI = pRuntime->ToBoolean(newParams[0]);
+
+  WideString cTo;
+  if (IsExpandedParamKnown(newParams[1]))
+    cTo = pRuntime->ToWideString(newParams[1]);
+
+  WideString cCc;
+  if (IsExpandedParamKnown(newParams[2]))
+    cCc = pRuntime->ToWideString(newParams[2]);
+
+  WideString cBcc;
+  if (IsExpandedParamKnown(newParams[3]))
+    cBcc = pRuntime->ToWideString(newParams[3]);
+
+  WideString cSubject;
+  if (IsExpandedParamKnown(newParams[4]))
+    cSubject = pRuntime->ToWideString(newParams[4]);
+
+  WideString cMsg;
+  if (IsExpandedParamKnown(newParams[5]))
+    cMsg = pRuntime->ToWideString(newParams[5]);
+
+  pRuntime->BeginBlock();
+  m_pFormFillEnv->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject, cCc, cBcc,
+                                 cMsg);
+  pRuntime->EndBlock();
+  return CJS_Result::Success();
 }
 
 // exports the form data and mails the resulting fdf file as an attachment to
 // all recipients.
 // comment: need reader supports
-CJS_Return Document::mailForm(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Document::mailForm(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+    return CJS_Result::Failure(JSMessage::kPermissionError);
 
-  int iLength = params.size();
-  bool bUI = iLength > 0 ? pRuntime->ToBoolean(params[0]) : true;
-  WideString cTo = iLength > 1 ? pRuntime->ToWideString(params[1]) : L"";
-  WideString cCc = iLength > 2 ? pRuntime->ToWideString(params[2]) : L"";
-  WideString cBcc = iLength > 3 ? pRuntime->ToWideString(params[3]) : L"";
-  WideString cSubject = iLength > 4 ? pRuntime->ToWideString(params[4]) : L"";
-  WideString cMsg = iLength > 5 ? pRuntime->ToWideString(params[5]) : L"";
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  ByteString sTextBuf = pInterForm->ExportFormToFDFTextBuf();
-  if (sTextBuf.GetLength() == 0)
-    return CJS_Return(false);
+  CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
+  ByteString sTextBuf = pInteractiveForm->ExportFormToFDFTextBuf();
+  if (sTextBuf.IsEmpty())
+    return CJS_Result::Failure(L"Bad FDF format.");
 
-  size_t nBufSize = sTextBuf.GetLength();
-  char* pMutableBuf = FX_Alloc(char, nBufSize);
-  memcpy(pMutableBuf, sTextBuf.c_str(), nBufSize);
-
-  pRuntime->BeginBlock();
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = pRuntime->GetFormFillEnv();
-  pFormFillEnv->JS_docmailForm(pMutableBuf, nBufSize, bUI, cTo.c_str(),
-                               cSubject.c_str(), cCc.c_str(), cBcc.c_str(),
-                               cMsg.c_str());
-  pRuntime->EndBlock();
-  FX_Free(pMutableBuf);
-  return CJS_Return(true);
-}
-
-CJS_Return Document::print(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params) {
-  if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+  std::vector<v8::Local<v8::Value>> newParams = ExpandKeywordParams(
+      pRuntime, params, 6, "bUI", "cTo", "cCc", "cBcc", "cSubject", "cMsg");
 
   bool bUI = true;
+  if (IsExpandedParamKnown(newParams[0]))
+    bUI = pRuntime->ToBoolean(newParams[0]);
+
+  WideString cTo;
+  if (IsExpandedParamKnown(newParams[1]))
+    cTo = pRuntime->ToWideString(newParams[1]);
+
+  WideString cCc;
+  if (IsExpandedParamKnown(newParams[2]))
+    cCc = pRuntime->ToWideString(newParams[2]);
+
+  WideString cBcc;
+  if (IsExpandedParamKnown(newParams[3]))
+    cBcc = pRuntime->ToWideString(newParams[3]);
+
+  WideString cSubject;
+  if (IsExpandedParamKnown(newParams[4]))
+    cSubject = pRuntime->ToWideString(newParams[4]);
+
+  WideString cMsg;
+  if (IsExpandedParamKnown(newParams[5]))
+    cMsg = pRuntime->ToWideString(newParams[5]);
+
+  std::vector<char> mutable_buf(sTextBuf.begin(), sTextBuf.end());
+  pRuntime->BeginBlock();
+  m_pFormFillEnv->JS_docmailForm(mutable_buf.data(), mutable_buf.size(), bUI,
+                                 cTo, cSubject, cCc, cBcc, cMsg);
+  pRuntime->EndBlock();
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Document::print(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  std::vector<v8::Local<v8::Value>> newParams = ExpandKeywordParams(
+      pRuntime, params, 8, "bUI", "nStart", "nEnd", "bSilent", "bShrinkToFit",
+      "bPrintAsImage", "bReverse", "bAnnotations");
+
+  bool bUI = true;
+  if (IsExpandedParamKnown(newParams[0]))
+    bUI = pRuntime->ToBoolean(newParams[0]);
+
   int nStart = 0;
+  if (IsExpandedParamKnown(newParams[1]))
+    nStart = pRuntime->ToInt32(newParams[1]);
+
   int nEnd = 0;
+  if (IsExpandedParamKnown(newParams[2]))
+    nEnd = pRuntime->ToInt32(newParams[2]);
+
   bool bSilent = false;
+  if (IsExpandedParamKnown(newParams[3]))
+    bSilent = pRuntime->ToBoolean(newParams[3]);
+
   bool bShrinkToFit = false;
+  if (IsExpandedParamKnown(newParams[4]))
+    bShrinkToFit = pRuntime->ToBoolean(newParams[4]);
+
   bool bPrintAsImage = false;
+  if (IsExpandedParamKnown(newParams[5]))
+    bPrintAsImage = pRuntime->ToBoolean(newParams[5]);
+
   bool bReverse = false;
+  if (IsExpandedParamKnown(newParams[6]))
+    bReverse = pRuntime->ToBoolean(newParams[6]);
+
   bool bAnnotations = false;
-  int nlength = params.size();
-  if (nlength == 9) {
-    if (params[8]->IsObject()) {
-      v8::Local<v8::Object> pObj = pRuntime->ToObject(params[8]);
-      if (CFXJS_Engine::GetObjDefnID(pObj) ==
-          CJS_PrintParamsObj::GetObjDefnID()) {
-        v8::Local<v8::Object> pObj = pRuntime->ToObject(params[8]);
-        CJS_Object* pJSObj =
-            static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(pObj));
-        if (pJSObj) {
-          if (PrintParamsObj* pprintparamsObj =
-                  static_cast<PrintParamsObj*>(pJSObj->GetEmbedObject())) {
-            bUI = pprintparamsObj->bUI;
-            nStart = pprintparamsObj->nStart;
-            nEnd = pprintparamsObj->nEnd;
-            bSilent = pprintparamsObj->bSilent;
-            bShrinkToFit = pprintparamsObj->bShrinkToFit;
-            bPrintAsImage = pprintparamsObj->bPrintAsImage;
-            bReverse = pprintparamsObj->bReverse;
-            bAnnotations = pprintparamsObj->bAnnotations;
-          }
-        }
-      }
-    }
-  } else {
-    if (nlength >= 1)
-      bUI = pRuntime->ToBoolean(params[0]);
-    if (nlength >= 2)
-      nStart = pRuntime->ToInt32(params[1]);
-    if (nlength >= 3)
-      nEnd = pRuntime->ToInt32(params[2]);
-    if (nlength >= 4)
-      bSilent = pRuntime->ToBoolean(params[3]);
-    if (nlength >= 5)
-      bShrinkToFit = pRuntime->ToBoolean(params[4]);
-    if (nlength >= 6)
-      bPrintAsImage = pRuntime->ToBoolean(params[5]);
-    if (nlength >= 7)
-      bReverse = pRuntime->ToBoolean(params[6]);
-    if (nlength >= 8)
-      bAnnotations = pRuntime->ToBoolean(params[7]);
-  }
+  if (IsExpandedParamKnown(newParams[7]))
+    bAnnotations = pRuntime->ToBoolean(newParams[7]);
 
   if (!m_pFormFillEnv)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  CJS_EventRecorder* pHandler =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (!pHandler->IsUserGesture())
+    return CJS_Result::Failure(JSMessage::kUserGestureRequiredError);
 
   m_pFormFillEnv->JS_docprint(bUI, nStart, nEnd, bSilent, bShrinkToFit,
                               bPrintAsImage, bReverse, bAnnotations);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
 // removes the specified field from the document.
 // comment:
 // note: if the filed name is not rational, adobe is dumb for it.
-
-CJS_Return Document::removeField(
+CJS_Result CJS_Document::removeField(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM)))
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM))) {
+    return CJS_Result::Failure(JSMessage::kPermissionError);
+  }
 
   WideString sFieldName = pRuntime->ToWideString(params[0]);
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  std::vector<CPDFSDK_Annot::ObservedPtr> widgets;
-  pInterForm->GetWidgets(sFieldName, &widgets);
+  CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
+  std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+  pInteractiveForm->GetWidgets(sFieldName, &widgets);
   if (widgets.empty())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   for (const auto& pAnnot : widgets) {
-    CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pAnnot.Get());
+    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get());
     if (!pWidget)
       continue;
 
-    CFX_FloatRect rcAnnot = pWidget->GetRect();
-    --rcAnnot.left;
-    --rcAnnot.bottom;
-    ++rcAnnot.right;
-    ++rcAnnot.top;
-
-    std::vector<CFX_FloatRect> aRefresh(1, rcAnnot);
-    UnderlyingPageType* pPage = pWidget->GetUnderlyingPage();
+    IPDF_Page* pPage = pWidget->GetPage();
     ASSERT(pPage);
 
     // If there is currently no pageview associated with the page being used
     // do not create one. We may be in the process of tearing down the document
     // and creating a new pageview at this point will cause bad things.
     CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage, false);
-    if (pPageView) {
-#if PDF_ENABLE_XFA
-      pPageView->DeleteAnnot(pWidget);
-#endif  // PDF_ENABLE_XFA
-      pPageView->UpdateRects(aRefresh);
-    }
+    if (!pPageView)
+      continue;
+
+    CFX_FloatRect rcAnnot = pWidget->GetRect();
+    rcAnnot.Inflate(1.0f, 1.0f, 1.0f, 1.0f);
+
+    std::vector<CFX_FloatRect> aRefresh(1, rcAnnot);
+    pPageView->UpdateRects(aRefresh);
   }
   m_pFormFillEnv->SetChangeMark();
-
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
 // reset filed values within a document.
 // comment:
 // note: if the fields names r not rational, aodbe is dumb for it.
 
-CJS_Return Document::resetForm(
+CJS_Result CJS_Document::resetForm(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
         m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
         m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) {
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+    return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pPDFForm = pInterForm->GetInterForm();
+  CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm();
   if (params.empty()) {
-    pPDFForm->ResetForm(true);
+    pPDFForm->ResetForm(NotificationOption::kNotify);
     m_pFormFillEnv->SetChangeMark();
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   v8::Local<v8::Array> array;
@@ -500,33 +536,39 @@
   }
 
   if (!aFields.empty()) {
-    pPDFForm->ResetForm(aFields, true, true);
+    pPDFForm->ResetForm(aFields, true, NotificationOption::kNotify);
     m_pFormFillEnv->SetChangeMark();
   }
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::saveAs(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Document::saveAs(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::syncAnnotScan(
+CJS_Result CJS_Document::syncAnnotScan(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::submitForm(
+CJS_Result CJS_Document::submitForm(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  int nSize = params.size();
+  size_t nSize = params.size();
   if (nSize < 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  CJS_EventRecorder* pHandler =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (!pHandler->IsUserGesture())
+    return CJS_Result::Failure(JSMessage::kUserGestureRequiredError);
 
   v8::Local<v8::Array> aFields;
   WideString strURL;
@@ -542,32 +584,29 @@
       aFields = pRuntime->ToArray(params[3]);
   } else if (params[0]->IsObject()) {
     v8::Local<v8::Object> pObj = pRuntime->ToObject(params[0]);
-    v8::Local<v8::Value> pValue = pRuntime->GetObjectProperty(pObj, L"cURL");
+    v8::Local<v8::Value> pValue = pRuntime->GetObjectProperty(pObj, "cURL");
     if (!pValue.IsEmpty())
       strURL = pRuntime->ToWideString(pValue);
 
-    bFDF = pRuntime->ToBoolean(pRuntime->GetObjectProperty(pObj, L"bFDF"));
-    bEmpty = pRuntime->ToBoolean(pRuntime->GetObjectProperty(pObj, L"bEmpty"));
-    aFields = pRuntime->ToArray(pRuntime->GetObjectProperty(pObj, L"aFields"));
+    bFDF = pRuntime->ToBoolean(pRuntime->GetObjectProperty(pObj, "bFDF"));
+    bEmpty = pRuntime->ToBoolean(pRuntime->GetObjectProperty(pObj, "bEmpty"));
+    aFields = pRuntime->ToArray(pRuntime->GetObjectProperty(pObj, "aFields"));
   }
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pPDFInterForm = pInterForm->GetInterForm();
-
+  CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm();
   if (pRuntime->GetArrayLength(aFields) == 0 && bEmpty) {
-    if (pPDFInterForm->CheckRequiredFields(nullptr, true)) {
+    if (pPDFForm->CheckRequiredFields(nullptr, true)) {
       pRuntime->BeginBlock();
-      pInterForm->SubmitForm(strURL, false);
+      GetSDKInteractiveForm()->SubmitForm(strURL, false);
       pRuntime->EndBlock();
     }
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   std::vector<CPDF_FormField*> fieldObjects;
   for (size_t i = 0; i < pRuntime->GetArrayLength(aFields); ++i) {
     WideString sName =
         pRuntime->ToWideString(pRuntime->GetArrayElement(aFields, i));
-    CPDF_InterForm* pPDFForm = pInterForm->GetInterForm();
     for (int j = 0, jsz = pPDFForm->CountFields(sName); j < jsz; ++j) {
       CPDF_FormField* pField = pPDFForm->GetField(j, sName);
       if (!bEmpty && pField->GetValue().IsEmpty())
@@ -577,85 +616,43 @@
     }
   }
 
-  if (pPDFInterForm->CheckRequiredFields(&fieldObjects, true)) {
+  if (pPDFForm->CheckRequiredFields(&fieldObjects, true)) {
     pRuntime->BeginBlock();
-    pInterForm->SubmitFields(strURL, fieldObjects, true, !bFDF);
+    GetSDKInteractiveForm()->SubmitFields(strURL, fieldObjects, true, !bFDF);
     pRuntime->EndBlock();
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Document::SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+void CJS_Document::SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
   m_pFormFillEnv.Reset(pFormFillEnv);
 }
 
-CJS_Return Document::get_bookmark_root(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_bookmark_root(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_bookmark_root(CJS_Runtime* pRuntime,
-                                       v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_bookmark_root(CJS_Runtime* pRuntime,
+                                           v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::mailDoc(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
-  // TODO(tsepez): Check maximum number of allowed params.
-  bool bUI = true;
-  WideString cTo = L"";
-  WideString cCc = L"";
-  WideString cBcc = L"";
-  WideString cSubject = L"";
-  WideString cMsg = L"";
-
-  if (params.size() >= 1)
-    bUI = pRuntime->ToBoolean(params[0]);
-  if (params.size() >= 2)
-    cTo = pRuntime->ToWideString(params[1]);
-  if (params.size() >= 3)
-    cCc = pRuntime->ToWideString(params[2]);
-  if (params.size() >= 4)
-    cBcc = pRuntime->ToWideString(params[3]);
-  if (params.size() >= 5)
-    cSubject = pRuntime->ToWideString(params[4]);
-  if (params.size() >= 6)
-    cMsg = pRuntime->ToWideString(params[5]);
-
-  if (params.size() >= 1 && params[0]->IsObject()) {
-    v8::Local<v8::Object> pObj = pRuntime->ToObject(params[0]);
-    bUI = pRuntime->ToBoolean(pRuntime->GetObjectProperty(pObj, L"bUI"));
-    cTo = pRuntime->ToWideString(pRuntime->GetObjectProperty(pObj, L"cTo"));
-    cCc = pRuntime->ToWideString(pRuntime->GetObjectProperty(pObj, L"cCc"));
-    cBcc = pRuntime->ToWideString(pRuntime->GetObjectProperty(pObj, L"cBcc"));
-    cSubject =
-        pRuntime->ToWideString(pRuntime->GetObjectProperty(pObj, L"cSubject"));
-    cMsg = pRuntime->ToWideString(pRuntime->GetObjectProperty(pObj, L"cMsg"));
-  }
-
-  pRuntime->BeginBlock();
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = pRuntime->GetFormFillEnv();
-  pFormFillEnv->JS_docmailForm(nullptr, 0, bUI, cTo.c_str(), cSubject.c_str(),
-                               cCc.c_str(), cBcc.c_str(), cMsg.c_str());
-  pRuntime->EndBlock();
-  return CJS_Return(true);
-}
-
-CJS_Return Document::get_author(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_author(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "Author");
 }
 
-CJS_Return Document::set_author(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_author(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   return setPropertyInternal(pRuntime, vp, "Author");
 }
 
-CJS_Return Document::get_info(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_info(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   const auto* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
   if (!pDictionary)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   WideString cwAuthor = pDictionary->GetUnicodeTextFor("Author");
   WideString cwTitle = pDictionary->GetUnicodeTextFor("Title");
@@ -667,265 +664,270 @@
   WideString cwModDate = pDictionary->GetUnicodeTextFor("ModDate");
   WideString cwTrapped = pDictionary->GetUnicodeTextFor("Trapped");
 
-  v8::Local<v8::Object> pObj = pRuntime->NewFxDynamicObj(-1);
-  pRuntime->PutObjectProperty(pObj, L"Author",
+  v8::Local<v8::Object> pObj = pRuntime->NewObject();
+  pRuntime->PutObjectProperty(pObj, "Author",
                               pRuntime->NewString(cwAuthor.AsStringView()));
-  pRuntime->PutObjectProperty(pObj, L"Title",
+  pRuntime->PutObjectProperty(pObj, "Title",
                               pRuntime->NewString(cwTitle.AsStringView()));
-  pRuntime->PutObjectProperty(pObj, L"Subject",
+  pRuntime->PutObjectProperty(pObj, "Subject",
                               pRuntime->NewString(cwSubject.AsStringView()));
-  pRuntime->PutObjectProperty(pObj, L"Keywords",
+  pRuntime->PutObjectProperty(pObj, "Keywords",
                               pRuntime->NewString(cwKeywords.AsStringView()));
-  pRuntime->PutObjectProperty(pObj, L"Creator",
+  pRuntime->PutObjectProperty(pObj, "Creator",
                               pRuntime->NewString(cwCreator.AsStringView()));
-  pRuntime->PutObjectProperty(pObj, L"Producer",
+  pRuntime->PutObjectProperty(pObj, "Producer",
                               pRuntime->NewString(cwProducer.AsStringView()));
   pRuntime->PutObjectProperty(
-      pObj, L"CreationDate",
-      pRuntime->NewString(cwCreationDate.AsStringView()));
-  pRuntime->PutObjectProperty(pObj, L"ModDate",
+      pObj, "CreationDate", pRuntime->NewString(cwCreationDate.AsStringView()));
+  pRuntime->PutObjectProperty(pObj, "ModDate",
                               pRuntime->NewString(cwModDate.AsStringView()));
-  pRuntime->PutObjectProperty(pObj, L"Trapped",
+  pRuntime->PutObjectProperty(pObj, "Trapped",
                               pRuntime->NewString(cwTrapped.AsStringView()));
 
-  // It's to be compatible to non-standard info dictionary.
-  for (const auto& it : *pDictionary) {
+  // PutObjectProperty() calls below may re-enter JS and change info dict.
+  auto pCopy = pDictionary->Clone();
+  CPDF_DictionaryLocker locker(ToDictionary(pCopy.Get()));
+  for (const auto& it : locker) {
     const ByteString& bsKey = it.first;
-    CPDF_Object* pValueObj = it.second.get();
-    WideString wsKey = WideString::FromUTF8(bsKey.AsStringView());
+    CPDF_Object* pValueObj = it.second.Get();
     if (pValueObj->IsString() || pValueObj->IsName()) {
       pRuntime->PutObjectProperty(
-          pObj, wsKey,
+          pObj, bsKey.AsStringView(),
           pRuntime->NewString(pValueObj->GetUnicodeText().AsStringView()));
     } else if (pValueObj->IsNumber()) {
-      pRuntime->PutObjectProperty(pObj, wsKey,
+      pRuntime->PutObjectProperty(pObj, bsKey.AsStringView(),
                                   pRuntime->NewNumber(pValueObj->GetNumber()));
     } else if (pValueObj->IsBoolean()) {
       pRuntime->PutObjectProperty(
-          pObj, wsKey, pRuntime->NewBoolean(!!pValueObj->GetInteger()));
+          pObj, bsKey.AsStringView(),
+          pRuntime->NewBoolean(!!pValueObj->GetInteger()));
     }
   }
-  return CJS_Return(pObj);
+  return CJS_Result::Success(pObj);
 }
 
-CJS_Return Document::set_info(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_info(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::getPropertyInternal(CJS_Runtime* pRuntime,
-                                         const ByteString& propName) {
+CJS_Result CJS_Document::getPropertyInternal(CJS_Runtime* pRuntime,
+                                             const ByteString& propName) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
   if (!pDictionary)
-    return CJS_Return(false);
-  return CJS_Return(
-      pRuntime->NewString(pDictionary->GetUnicodeTextFor(propName).c_str()));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(pRuntime->NewString(
+      pDictionary->GetUnicodeTextFor(propName).AsStringView()));
 }
 
-CJS_Return Document::setPropertyInternal(CJS_Runtime* pRuntime,
-                                         v8::Local<v8::Value> vp,
-                                         const ByteString& propName) {
+CJS_Result CJS_Document::setPropertyInternal(CJS_Runtime* pRuntime,
+                                             v8::Local<v8::Value> vp,
+                                             const ByteString& propName) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
   if (!pDictionary)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY))
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+    return CJS_Result::Failure(JSMessage::kPermissionError);
 
-  WideString csProperty = pRuntime->ToWideString(vp);
-  pDictionary->SetNewFor<CPDF_String>(propName, PDF_EncodeText(csProperty),
-                                      false);
+  pDictionary->SetNewFor<CPDF_String>(propName, pRuntime->ToWideString(vp));
   m_pFormFillEnv->SetChangeMark();
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_creation_date(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_creation_date(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "CreationDate");
 }
 
-CJS_Return Document::set_creation_date(CJS_Runtime* pRuntime,
-                                       v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_creation_date(CJS_Runtime* pRuntime,
+                                           v8::Local<v8::Value> vp) {
   return setPropertyInternal(pRuntime, vp, "CreationDate");
 }
 
-CJS_Return Document::get_creator(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_creator(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "Creator");
 }
 
-CJS_Return Document::set_creator(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_creator(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
   return setPropertyInternal(pRuntime, vp, "Creator");
 }
 
-CJS_Return Document::get_delay(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_delay(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(pRuntime->NewBoolean(m_bDelay));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(pRuntime->NewBoolean(m_bDelay));
 }
 
-CJS_Return Document::set_delay(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_delay(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY))
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+    return CJS_Result::Failure(JSMessage::kPermissionError);
 
   m_bDelay = pRuntime->ToBoolean(vp);
   if (m_bDelay) {
     m_DelayData.clear();
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   std::list<std::unique_ptr<CJS_DelayData>> DelayDataToProcess;
   DelayDataToProcess.swap(m_DelayData);
   for (const auto& pData : DelayDataToProcess)
-    Field::DoDelay(m_pFormFillEnv.Get(), pData.get());
+    CJS_Field::DoDelay(m_pFormFillEnv.Get(), pData.get());
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_keywords(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_keywords(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "Keywords");
 }
 
-CJS_Return Document::set_keywords(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_keywords(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
   return setPropertyInternal(pRuntime, vp, "Keywords");
 }
 
-CJS_Return Document::get_mod_date(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_mod_date(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "ModDate");
 }
 
-CJS_Return Document::set_mod_date(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_mod_date(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
   return setPropertyInternal(pRuntime, vp, "ModDate");
 }
 
-CJS_Return Document::get_producer(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_producer(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "Producer");
 }
 
-CJS_Return Document::set_producer(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_producer(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
   return setPropertyInternal(pRuntime, vp, "Producer");
 }
 
-CJS_Return Document::get_subject(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_subject(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "Subject");
 }
 
-CJS_Return Document::set_subject(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_subject(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
   return setPropertyInternal(pRuntime, vp, "Subject");
 }
 
-CJS_Return Document::get_title(CJS_Runtime* pRuntime) {
-  if (!m_pFormFillEnv || !m_pFormFillEnv->GetUnderlyingDocument())
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+CJS_Result CJS_Document::get_title(CJS_Runtime* pRuntime) {
+  if (!m_pFormFillEnv)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   return getPropertyInternal(pRuntime, "Title");
 }
 
-CJS_Return Document::set_title(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  if (!m_pFormFillEnv || !m_pFormFillEnv->GetUnderlyingDocument())
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+CJS_Result CJS_Document::set_title(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  if (!m_pFormFillEnv)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   return setPropertyInternal(pRuntime, vp, "Title");
 }
 
-CJS_Return Document::get_num_pages(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_num_pages(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(pRuntime->NewNumber(m_pFormFillEnv->GetPageCount()));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(
+      pRuntime->NewNumber(m_pFormFillEnv->GetPageCount()));
 }
 
-CJS_Return Document::set_num_pages(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_num_pages(CJS_Runtime* pRuntime,
+                                       v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::get_external(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_external(CJS_Runtime* pRuntime) {
   // In Chrome case, should always return true.
-  return CJS_Return(pRuntime->NewBoolean(true));
+  return CJS_Result::Success(pRuntime->NewBoolean(true));
 }
 
-CJS_Return Document::set_external(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_external(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_filesize(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewNumber(0));
+CJS_Result CJS_Document::get_filesize(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewNumber(0));
 }
 
-CJS_Return Document::set_filesize(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_filesize(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::get_mouse_x(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_mouse_x(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_mouse_x(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_mouse_x(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_mouse_y(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_mouse_y(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_mouse_y(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_mouse_y(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_URL(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_URL(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(
-      pRuntime->NewString(m_pFormFillEnv->JS_docGetFilePath().c_str()));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(
+      pRuntime->NewString(m_pFormFillEnv->JS_docGetFilePath().AsStringView()));
 }
 
-CJS_Return Document::set_URL(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_URL(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::get_base_URL(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewString(m_cwBaseURL.c_str()));
+CJS_Result CJS_Document::get_base_URL(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewString(m_cwBaseURL.AsStringView()));
 }
 
-CJS_Return Document::set_base_URL(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_base_URL(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
   m_cwBaseURL = pRuntime->ToWideString(vp);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_calculate(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_calculate(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  return CJS_Return(pRuntime->NewBoolean(!!pInterForm->IsCalculateEnabled()));
+  CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
+  return CJS_Result::Success(
+      pRuntime->NewBoolean(!!pInteractiveForm->IsCalculateEnabled()));
 }
 
-CJS_Return Document::set_calculate(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp) {
+CJS_Result CJS_Document::set_calculate(CJS_Runtime* pRuntime,
+                                       v8::Local<v8::Value> vp) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  pInterForm->EnableCalculate(pRuntime->ToBoolean(vp));
-  return CJS_Return(true);
+  CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
+  pInteractiveForm->EnableCalculate(pRuntime->ToBoolean(vp));
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_document_file_name(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_document_file_name(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   WideString wsFilePath = m_pFormFillEnv->JS_docGetFilePath();
   size_t i = wsFilePath.GetLength();
@@ -933,111 +935,111 @@
     if (wsFilePath[i - 1] == L'\\' || wsFilePath[i - 1] == L'/')
       break;
   }
+  if (i > 0 && i < wsFilePath.GetLength())
+    return CJS_Result::Success(pRuntime->NewString(wsFilePath.c_str() + i));
 
-  if (i > 0 && i < wsFilePath.GetLength()) {
-    return CJS_Return(
-        pRuntime->NewString(wsFilePath.GetBuffer(wsFilePath.GetLength()) + i));
-  }
-  return CJS_Return(pRuntime->NewString(L""));
+  return CJS_Result::Success(pRuntime->NewString(""));
 }
 
-CJS_Return Document::set_document_file_name(CJS_Runtime* pRuntime,
-                                            v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_document_file_name(CJS_Runtime* pRuntime,
+                                                v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::get_path(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_path(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(pRuntime->NewString(
-      app::SysPathToPDFPath(m_pFormFillEnv->JS_docGetFilePath()).c_str()));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(pRuntime->NewString(
+      CJS_App::SysPathToPDFPath(m_pFormFillEnv->JS_docGetFilePath())
+          .AsStringView()));
 }
 
-CJS_Return Document::set_path(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_path(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::get_page_window_rect(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_page_window_rect(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_page_window_rect(CJS_Runtime* pRuntime,
-                                          v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_page_window_rect(CJS_Runtime* pRuntime,
+                                              v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_layout(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_layout(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_layout(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_layout(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::addLink(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
-}
-
-CJS_Return Document::closeDoc(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
-}
-
-CJS_Return Document::getPageBox(
+CJS_Result CJS_Document::addLink(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::getAnnot(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Document::closeDoc(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Document::getPageBox(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Document::getAnnot(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   int nPageNo = pRuntime->ToInt32(params[0]);
   WideString swAnnotName = pRuntime->ToWideString(params[1]);
   CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(nPageNo);
   if (!pPageView)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDFSDK_AnnotIteration annotIteration(pPageView, false);
   CPDFSDK_BAAnnot* pSDKBAAnnot = nullptr;
   for (const auto& pSDKAnnotCur : annotIteration) {
-    CPDFSDK_BAAnnot* pBAAnnot =
-        static_cast<CPDFSDK_BAAnnot*>(pSDKAnnotCur.Get());
+    auto* pBAAnnot = pSDKAnnotCur->AsBAAnnot();
     if (pBAAnnot && pBAAnnot->GetAnnotName() == swAnnotName) {
       pSDKBAAnnot = pBAAnnot;
       break;
     }
   }
   if (!pSDKBAAnnot)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  v8::Local<v8::Object> pObj =
-      pRuntime->NewFxDynamicObj(CJS_Annot::GetObjDefnID());
+  v8::Local<v8::Object> pObj = pRuntime->NewFXJSBoundObject(
+      CJS_Annot::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
   if (pObj.IsEmpty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_Annot* pJS_Annot =
-      static_cast<CJS_Annot*>(pRuntime->GetObjectPrivate(pObj));
+  auto* pJS_Annot =
+      static_cast<CJS_Annot*>(CFXJS_Engine::GetObjectPrivate(pObj));
   if (!pJS_Annot)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  Annot* pAnnot = static_cast<Annot*>(pJS_Annot->GetEmbedObject());
-  pAnnot->SetSDKAnnot(pSDKBAAnnot);
-
-  return CJS_Return(pJS_Annot->ToV8Object());
+  pJS_Annot->SetSDKAnnot(pSDKBAAnnot);
+  return CJS_Result::Success(pJS_Annot->ToV8Object());
 }
 
-CJS_Return Document::getAnnots(
+CJS_Result CJS_Document::getAnnots(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   // TODO(tonikitoo): Add support supported parameters as per
   // the PDF spec.
@@ -1047,186 +1049,181 @@
   for (int i = 0; i < nPageNo; ++i) {
     CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(i);
     if (!pPageView)
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
 
     CPDFSDK_AnnotIteration annotIteration(pPageView, false);
     for (const auto& pSDKAnnotCur : annotIteration) {
       if (!pSDKAnnotCur)
-        return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+        return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-      v8::Local<v8::Object> pObj =
-          pRuntime->NewFxDynamicObj(CJS_Annot::GetObjDefnID());
+      v8::Local<v8::Object> pObj = pRuntime->NewFXJSBoundObject(
+          CJS_Annot::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
       if (pObj.IsEmpty())
-        return CJS_Return(false);
+        return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-      CJS_Annot* pJS_Annot =
-          static_cast<CJS_Annot*>(pRuntime->GetObjectPrivate(pObj));
-      Annot* pAnnot = static_cast<Annot*>(pJS_Annot->GetEmbedObject());
-      pAnnot->SetSDKAnnot(static_cast<CPDFSDK_BAAnnot*>(pSDKAnnotCur.Get()));
+      auto* pJS_Annot =
+          static_cast<CJS_Annot*>(CFXJS_Engine::GetObjectPrivate(pObj));
+      pJS_Annot->SetSDKAnnot(pSDKAnnotCur->AsBAAnnot());
       pRuntime->PutArrayElement(
           annots, i,
           pJS_Annot ? v8::Local<v8::Value>(pJS_Annot->ToV8Object())
                     : v8::Local<v8::Value>());
     }
   }
-  return CJS_Return(annots);
+  return CJS_Result::Success(annots);
 }
 
-CJS_Return Document::getAnnot3D(
+CJS_Result CJS_Document::getAnnot3D(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(pRuntime->NewUndefined());
+  return CJS_Result::Success(pRuntime->NewUndefined());
 }
 
-CJS_Return Document::getAnnots3D(
+CJS_Result CJS_Document::getAnnots3D(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::getOCGs(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::getOCGs(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::getLinks(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::getLinks(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-bool Document::IsEnclosedInRect(CFX_FloatRect rect, CFX_FloatRect LinkRect) {
-  return (rect.left <= LinkRect.left && rect.top <= LinkRect.top &&
-          rect.right >= LinkRect.right && rect.bottom >= LinkRect.bottom);
-}
-
-CJS_Return Document::addIcon(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Document::addIcon(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  WideString swIconName = pRuntime->ToWideString(params[0]);
   if (!params[1]->IsObject())
-    return CJS_Return(JSGetStringFromID(JSMessage::kTypeError));
-
-  v8::Local<v8::Object> pJSIcon = pRuntime->ToObject(params[1]);
-  if (CFXJS_Engine::GetObjDefnID(pJSIcon) != CJS_Icon::GetObjDefnID())
-    return CJS_Return(JSGetStringFromID(JSMessage::kTypeError));
+    return CJS_Result::Failure(JSMessage::kTypeError);
 
   v8::Local<v8::Object> pObj = pRuntime->ToObject(params[1]);
-  CJS_Object* obj = static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(pObj));
-  if (!obj->GetEmbedObject())
-    return CJS_Return(JSGetStringFromID(JSMessage::kTypeError));
+  if (!JSGetObject<CJS_Icon>(pObj))
+    return CJS_Result::Failure(JSMessage::kTypeError);
 
+  WideString swIconName = pRuntime->ToWideString(params[0]);
   m_IconNames.push_back(swIconName);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_icons(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Document::get_icons(CJS_Runtime* pRuntime) {
+  // TODO(tsepez): Maybe make consistent with Acrobat Reader behavior which
+  // is to throw an exception under the default security settings.
   if (m_IconNames.empty())
-    return CJS_Return(pRuntime->NewUndefined());
+    return CJS_Result::Success(pRuntime->NewUndefined());
 
   v8::Local<v8::Array> Icons = pRuntime->NewArray();
   int i = 0;
   for (const auto& name : m_IconNames) {
-    v8::Local<v8::Object> pObj =
-        pRuntime->NewFxDynamicObj(CJS_Icon::GetObjDefnID());
+    v8::Local<v8::Object> pObj = pRuntime->NewFXJSBoundObject(
+        CJS_Icon::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
     if (pObj.IsEmpty())
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    CJS_Icon* pJS_Icon =
-        static_cast<CJS_Icon*>(pRuntime->GetObjectPrivate(pObj));
-    Icon* pIcon = static_cast<Icon*>(pJS_Icon->GetEmbedObject());
-    pIcon->SetIconName(name);
+    auto* pJS_Icon =
+        static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+    pJS_Icon->SetIconName(name);
     pRuntime->PutArrayElement(Icons, i++,
                               pJS_Icon
                                   ? v8::Local<v8::Value>(pJS_Icon->ToV8Object())
                                   : v8::Local<v8::Value>());
   }
-  return CJS_Return(Icons);
+  return CJS_Result::Success(Icons);
 }
 
-CJS_Return Document::set_icons(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Document::set_icons(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Document::getIcon(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Document::getIcon(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString swIconName = pRuntime->ToWideString(params[0]);
   auto it = std::find(m_IconNames.begin(), m_IconNames.end(), swIconName);
   if (it == m_IconNames.end())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  v8::Local<v8::Object> pObj =
-      pRuntime->NewFxDynamicObj(CJS_Icon::GetObjDefnID());
+  v8::Local<v8::Object> pObj = pRuntime->NewFXJSBoundObject(
+      CJS_Icon::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
   if (pObj.IsEmpty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_Icon* pJS_Icon = static_cast<CJS_Icon*>(pRuntime->GetObjectPrivate(pObj));
-  if (!pJS_Icon)
-    return CJS_Return(false);
+  auto* pJSIcon = static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  if (!pJSIcon)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  Icon* pIcon = static_cast<Icon*>(pJS_Icon->GetEmbedObject());
-  pIcon->SetIconName(*it);
-  return CJS_Return(pJS_Icon->ToV8Object());
+  pJSIcon->SetIconName(*it);
+  return CJS_Result::Success(pJSIcon->ToV8Object());
 }
 
-CJS_Return Document::removeIcon(
+CJS_Result CJS_Document::removeIcon(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, no supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::createDataObject(
+CJS_Result CJS_Document::createDataObject(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not implemented.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_media(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_media(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_media(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_media(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::calculateNow(
+CJS_Result CJS_Document::calculateNow(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
         m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
         m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) {
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+    return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
-  m_pFormFillEnv->GetInterForm()->OnCalculate();
-  return CJS_Return(true);
+  GetSDKInteractiveForm()->OnCalculate(nullptr);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_collab(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_collab(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_collab(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_collab(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::getPageNthWord(
+CJS_Result CJS_Document::getPageNthWord(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+    return CJS_Result::Failure(JSMessage::kPermissionError);
 
   // TODO(tsepez): check maximum allowable params.
 
@@ -1235,27 +1232,25 @@
   bool bStrip = params.size() > 2 ? pRuntime->ToBoolean(params[2]) : true;
 
   CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
-  if (!pDocument)
-    return CJS_Return(false);
-
   if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount())
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  CPDF_Dictionary* pPageDict = pDocument->GetPage(nPageNo);
+  CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo);
   if (!pPageDict)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Page page(pDocument, pPageDict, true);
-  page.ParseContent();
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, pPageDict);
+  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  page->ParseContent();
 
   int nWords = 0;
   WideString swRet;
-  for (auto& pPageObj : *page.GetPageObjectList()) {
+  for (auto& pPageObj : *page) {
     if (pPageObj->IsText()) {
       CPDF_TextObject* pTextObj = pPageObj->AsText();
-      int nObjWords = CountWords(pTextObj);
+      int nObjWords = pTextObj->CountWords();
       if (nWords + nObjWords >= nWordNo) {
-        swRet = GetObjWordStr(pTextObj, nWordNo - nWords);
+        swRet = pTextObj->GetWordString(nWordNo - nWords);
         break;
       }
       nWords += nObjWords;
@@ -1264,221 +1259,144 @@
 
   if (bStrip)
     swRet.Trim();
-  return CJS_Return(pRuntime->NewString(swRet.c_str()));
+  return CJS_Result::Success(pRuntime->NewString(swRet.AsStringView()));
 }
 
-CJS_Return Document::getPageNthWordQuads(
+CJS_Result CJS_Document::getPageNthWordQuads(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
-  return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Document::getPageNumWords(
+CJS_Result CJS_Document::getPageNumWords(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
-    return CJS_Return(JSGetStringFromID(JSMessage::kPermissionError));
+    return CJS_Result::Failure(JSMessage::kPermissionError);
 
   int nPageNo = params.size() > 0 ? pRuntime->ToInt32(params[0]) : 0;
   CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
   if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount())
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  CPDF_Dictionary* pPageDict = pDocument->GetPage(nPageNo);
+  CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo);
   if (!pPageDict)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Page page(pDocument, pPageDict, true);
-  page.ParseContent();
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, pPageDict);
+  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  page->ParseContent();
 
   int nWords = 0;
-  for (auto& pPageObj : *page.GetPageObjectList()) {
+  for (auto& pPageObj : *page) {
     if (pPageObj->IsText())
-      nWords += CountWords(pPageObj->AsText());
+      nWords += pPageObj->AsText()->CountWords();
   }
-
-  return CJS_Return(pRuntime->NewNumber(nWords));
+  return CJS_Result::Success(pRuntime->NewNumber(nWords));
 }
 
-CJS_Return Document::getPrintParams(
+CJS_Result CJS_Document::getPrintParams(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  v8::Local<v8::Object> pRetObj =
-      pRuntime->NewFxDynamicObj(CJS_PrintParamsObj::GetObjDefnID());
-  if (pRetObj.IsEmpty())
-    return CJS_Return(false);
-
-  // Not implemented yet.
-
-  return CJS_Return(pRetObj);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-#define ISLATINWORD(u) (u != 0x20 && u <= 0x28FF)
-
-int Document::CountWords(CPDF_TextObject* pTextObj) {
-  if (!pTextObj)
-    return 0;
-
-  int nWords = 0;
-
-  CPDF_Font* pFont = pTextObj->GetFont();
-  if (!pFont)
-    return 0;
-
-  bool bIsLatin = false;
-
-  for (size_t i = 0, sz = pTextObj->CountChars(); i < sz; ++i) {
-    uint32_t charcode = CPDF_Font::kInvalidCharCode;
-    float kerning;
-
-    pTextObj->GetCharInfo(i, &charcode, &kerning);
-    WideString swUnicode = pFont->UnicodeFromCharCode(charcode);
-
-    uint16_t unicode = 0;
-    if (swUnicode.GetLength() > 0)
-      unicode = swUnicode[0];
-
-    if (ISLATINWORD(unicode) && bIsLatin)
-      continue;
-
-    bIsLatin = ISLATINWORD(unicode);
-    if (unicode != 0x20)
-      nWords++;
-  }
-
-  return nWords;
+CJS_Result CJS_Document::get_zoom(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-WideString Document::GetObjWordStr(CPDF_TextObject* pTextObj, int nWordIndex) {
-  WideString swRet;
-
-  CPDF_Font* pFont = pTextObj->GetFont();
-  if (!pFont)
-    return L"";
-
-  int nWords = 0;
-  bool bIsLatin = false;
-
-  for (size_t i = 0, sz = pTextObj->CountChars(); i < sz; ++i) {
-    uint32_t charcode = CPDF_Font::kInvalidCharCode;
-    float kerning;
-
-    pTextObj->GetCharInfo(i, &charcode, &kerning);
-    WideString swUnicode = pFont->UnicodeFromCharCode(charcode);
-
-    uint16_t unicode = 0;
-    if (swUnicode.GetLength() > 0)
-      unicode = swUnicode[0];
-
-    if (ISLATINWORD(unicode) && bIsLatin) {
-    } else {
-      bIsLatin = ISLATINWORD(unicode);
-      if (unicode != 0x20)
-        nWords++;
-    }
-
-    if (nWords - 1 == nWordIndex)
-      swRet += unicode;
-  }
-
-  return swRet;
+CJS_Result CJS_Document::set_zoom(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_zoom(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::get_zoom_type(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::set_zoom(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Document::set_zoom_type(CJS_Runtime* pRuntime,
+                                       v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::get_zoom_type(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
-}
-
-CJS_Return Document::set_zoom_type(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
-}
-
-CJS_Return Document::deletePages(
+CJS_Result CJS_Document::deletePages(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::extractPages(
+CJS_Result CJS_Document::extractPages(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::insertPages(
+CJS_Result CJS_Document::insertPages(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::replacePages(
+CJS_Result CJS_Document::replacePages(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::getURL(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Document::getURL(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   // Unsafe, not supported.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Document::gotoNamedDest(
+CJS_Result CJS_Document::gotoNamedDest(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
   if (!m_pFormFillEnv)
-    return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  WideString wideName = pRuntime->ToWideString(params[0]);
   CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
-  if (!pDocument)
-    return CJS_Return(false);
-
   CPDF_NameTree nameTree(pDocument, "Dests");
-  CPDF_Array* destArray = nameTree.LookupNamedDest(pDocument, wideName);
+  CPDF_Array* destArray =
+      nameTree.LookupNamedDest(pDocument, pRuntime->ToWideString(params[0]));
   if (!destArray)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_Dest dest(destArray);
-  const CPDF_Array* arrayObject = ToArray(dest.GetObject());
+  const CPDF_Array* arrayObject = dest.GetArray();
   std::vector<float> scrollPositionArray;
   if (arrayObject) {
-    for (size_t i = 2; i < arrayObject->GetCount(); i++)
-      scrollPositionArray.push_back(arrayObject->GetFloatAt(i));
+    for (size_t i = 2; i < arrayObject->size(); i++)
+      scrollPositionArray.push_back(arrayObject->GetNumberAt(i));
   }
   pRuntime->BeginBlock();
-  m_pFormFillEnv->DoGoToAction(dest.GetPageIndex(pDocument), dest.GetZoomMode(),
-                               scrollPositionArray.data(),
+  m_pFormFillEnv->DoGoToAction(dest.GetDestPageIndex(pDocument),
+                               dest.GetZoomMode(), scrollPositionArray.data(),
                                scrollPositionArray.size());
   pRuntime->EndBlock();
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Document::AddDelayData(CJS_DelayData* pData) {
-  m_DelayData.push_back(std::unique_ptr<CJS_DelayData>(pData));
+void CJS_Document::AddDelayData(std::unique_ptr<CJS_DelayData> pData) {
+  m_DelayData.push_back(std::move(pData));
 }
 
-void Document::DoFieldDelay(const WideString& sFieldName, int nControlIndex) {
+void CJS_Document::DoFieldDelay(const WideString& sFieldName,
+                                int nControlIndex) {
   std::vector<std::unique_ptr<CJS_DelayData>> delayed_data;
   auto iter = m_DelayData.begin();
   while (iter != m_DelayData.end()) {
@@ -1491,9 +1409,13 @@
   }
 
   for (const auto& pData : delayed_data)
-    Field::DoDelay(m_pFormFillEnv.Get(), pData.get());
+    CJS_Field::DoDelay(m_pFormFillEnv.Get(), pData.get());
 }
 
-CJS_Document* Document::GetCJSDoc() const {
-  return static_cast<CJS_Document*>(m_pJSObject.Get());
+CPDF_InteractiveForm* CJS_Document::GetCoreInteractiveForm() {
+  return GetSDKInteractiveForm()->GetInteractiveForm();
+}
+
+CPDFSDK_InteractiveForm* CJS_Document::GetSDKInteractiveForm() {
+  return m_pFormFillEnv->GetInteractiveForm();
 }
diff --git a/fxjs/cjs_document.h b/fxjs/cjs_document.h
index edfaf96..49448fc 100644
--- a/fxjs/cjs_document.h
+++ b/fxjs/cjs_document.h
@@ -11,320 +11,311 @@
 #include <memory>
 #include <vector>
 
-#include "fxjs/JS_Define.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
-class CJS_Document;
+class CPDFSDK_InteractiveForm;
+class CPDF_InteractiveForm;
 class CPDF_TextObject;
-
 struct CJS_DelayData;
 
-class Document : public CJS_EmbedObj {
+class CJS_Document final : public CJS_Object, public Observable {
  public:
-  explicit Document(CJS_Object* pJSObject);
-  ~Document() override;
+  static int GetObjDefnID();
+  static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  CJS_Return get_ADBE(CJS_Runtime* pRuntime);
-  CJS_Return set_ADBE(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_author(CJS_Runtime* pRuntime);
-  CJS_Return set_author(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_base_URL(CJS_Runtime* pRuntime);
-  CJS_Return set_base_URL(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_bookmark_root(CJS_Runtime* pRuntime);
-  CJS_Return set_bookmark_root(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_calculate(CJS_Runtime* pRuntime);
-  CJS_Return set_calculate(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_collab(CJS_Runtime* pRuntime);
-  CJS_Return set_collab(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_creation_date(CJS_Runtime* pRuntime);
-  CJS_Return set_creation_date(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_creator(CJS_Runtime* pRuntime);
-  CJS_Return set_creator(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_delay(CJS_Runtime* pRuntime);
-  CJS_Return set_delay(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_dirty(CJS_Runtime* pRuntime);
-  CJS_Return set_dirty(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_document_file_name(CJS_Runtime* pRuntime);
-  CJS_Return set_document_file_name(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp);
-
-  CJS_Return get_external(CJS_Runtime* pRuntime);
-  CJS_Return set_external(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_filesize(CJS_Runtime* pRuntime);
-  CJS_Return set_filesize(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_icons(CJS_Runtime* pRuntime);
-  CJS_Return set_icons(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_info(CJS_Runtime* pRuntime);
-  CJS_Return set_info(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_keywords(CJS_Runtime* pRuntime);
-  CJS_Return set_keywords(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_layout(CJS_Runtime* pRuntime);
-  CJS_Return set_layout(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_media(CJS_Runtime* pRuntime);
-  CJS_Return set_media(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_mod_date(CJS_Runtime* pRuntime);
-  CJS_Return set_mod_date(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_mouse_x(CJS_Runtime* pRuntime);
-  CJS_Return set_mouse_x(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_mouse_y(CJS_Runtime* pRuntime);
-  CJS_Return set_mouse_y(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_num_fields(CJS_Runtime* pRuntime);
-  CJS_Return set_num_fields(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_num_pages(CJS_Runtime* pRuntime);
-  CJS_Return set_num_pages(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_page_num(CJS_Runtime* pRuntime);
-  CJS_Return set_page_num(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_page_window_rect(CJS_Runtime* pRuntime);
-  CJS_Return set_page_window_rect(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp);
-
-  CJS_Return get_path(CJS_Runtime* pRuntime);
-  CJS_Return set_path(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_producer(CJS_Runtime* pRuntime);
-  CJS_Return set_producer(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_subject(CJS_Runtime* pRuntime);
-  CJS_Return set_subject(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_title(CJS_Runtime* pRuntime);
-  CJS_Return set_title(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_zoom(CJS_Runtime* pRuntime);
-  CJS_Return set_zoom(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_zoom_type(CJS_Runtime* pRuntime);
-  CJS_Return set_zoom_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_URL(CJS_Runtime* pRuntime);
-  CJS_Return set_URL(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return addAnnot(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return addField(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return addLink(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return addIcon(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return calculateNow(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return closeDoc(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return createDataObject(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return deletePages(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return exportAsText(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return exportAsFDF(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return exportAsXFDF(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return extractPages(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getAnnot(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getAnnots(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getAnnot3D(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getAnnots3D(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getField(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getIcon(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getLinks(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getNthFieldName(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getOCGs(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getPageBox(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getPageNthWord(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getPageNthWordQuads(
-      CJS_Runtime* pRuntime,
-      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getPageNumWords(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getPrintParams(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getURL(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return gotoNamedDest(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return importAnFDF(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return importAnXFDF(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return importTextData(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return insertPages(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return mailForm(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return print(CJS_Runtime* pRuntime,
-                   const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return removeField(CJS_Runtime* pRuntime,
-                         const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return replacePages(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return resetForm(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return saveAs(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return submitForm(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return syncAnnotScan(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return mailDoc(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return removeIcon(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Document(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Document() override;
 
   void SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv);
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
     return m_pFormFillEnv.Get();
   }
-  void AddDelayData(CJS_DelayData* pData);
+  void AddDelayData(std::unique_ptr<CJS_DelayData> pData);
   void DoFieldDelay(const WideString& sFieldName, int nControlIndex);
-  CJS_Document* GetCJSDoc() const;
 
- private:
-  bool IsEnclosedInRect(CFX_FloatRect rect, CFX_FloatRect LinkRect);
-  int CountWords(CPDF_TextObject* pTextObj);
-  WideString GetObjWordStr(CPDF_TextObject* pTextObj, int nWordIndex);
+  JS_STATIC_PROP(ADBE, ADBE, CJS_Document)
+  JS_STATIC_PROP(author, author, CJS_Document)
+  JS_STATIC_PROP(baseURL, base_URL, CJS_Document)
+  JS_STATIC_PROP(bookmarkRoot, bookmark_root, CJS_Document)
+  JS_STATIC_PROP(calculate, calculate, CJS_Document)
+  JS_STATIC_PROP(Collab, collab, CJS_Document)
+  JS_STATIC_PROP(creationDate, creation_date, CJS_Document)
+  JS_STATIC_PROP(creator, creator, CJS_Document)
+  JS_STATIC_PROP(delay, delay, CJS_Document)
+  JS_STATIC_PROP(dirty, dirty, CJS_Document)
+  JS_STATIC_PROP(documentFileName, document_file_name, CJS_Document)
+  JS_STATIC_PROP(external, external, CJS_Document)
+  JS_STATIC_PROP(filesize, filesize, CJS_Document)
+  JS_STATIC_PROP(icons, icons, CJS_Document)
+  JS_STATIC_PROP(info, info, CJS_Document)
+  JS_STATIC_PROP(keywords, keywords, CJS_Document)
+  JS_STATIC_PROP(layout, layout, CJS_Document)
+  JS_STATIC_PROP(media, media, CJS_Document)
+  JS_STATIC_PROP(modDate, mod_date, CJS_Document)
+  JS_STATIC_PROP(mouseX, mouse_x, CJS_Document)
+  JS_STATIC_PROP(mouseY, mouse_y, CJS_Document)
+  JS_STATIC_PROP(numFields, num_fields, CJS_Document)
+  JS_STATIC_PROP(numPages, num_pages, CJS_Document)
+  JS_STATIC_PROP(pageNum, page_num, CJS_Document)
+  JS_STATIC_PROP(pageWindowRect, page_window_rect, CJS_Document)
+  JS_STATIC_PROP(path, path, CJS_Document)
+  JS_STATIC_PROP(producer, producer, CJS_Document)
+  JS_STATIC_PROP(subject, subject, CJS_Document)
+  JS_STATIC_PROP(title, title, CJS_Document)
+  JS_STATIC_PROP(URL, URL, CJS_Document)
+  JS_STATIC_PROP(zoom, zoom, CJS_Document)
+  JS_STATIC_PROP(zoomType, zoom_type, CJS_Document)
 
-  CJS_Return getPropertyInternal(CJS_Runtime* pRuntime,
-                                 const ByteString& propName);
-  CJS_Return setPropertyInternal(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp,
-                                 const ByteString& propName);
-
-  CPDFSDK_FormFillEnvironment::ObservedPtr m_pFormFillEnv;
-  WideString m_cwBaseURL;
-  std::list<std::unique_ptr<CJS_DelayData>> m_DelayData;
-  // Needs to be a std::list for iterator stability.
-  std::list<WideString> m_IconNames;
-  bool m_bDelay;
-};
-
-class CJS_Document : public CJS_Object {
- public:
-  static int GetObjDefnID();
-  static void DefineJSObjects(CFXJS_Engine* pEngine);
-
-  explicit CJS_Document(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Document() override {}
-
-  // CJS_Object
-  void InitInstance(IJS_Runtime* pIRuntime) override;
-
-  JS_STATIC_PROP(ADBE, ADBE, Document);
-  JS_STATIC_PROP(author, author, Document);
-  JS_STATIC_PROP(baseURL, base_URL, Document);
-  JS_STATIC_PROP(bookmarkRoot, bookmark_root, Document);
-  JS_STATIC_PROP(calculate, calculate, Document);
-  JS_STATIC_PROP(Collab, collab, Document);
-  JS_STATIC_PROP(creationDate, creation_date, Document);
-  JS_STATIC_PROP(creator, creator, Document);
-  JS_STATIC_PROP(delay, delay, Document);
-  JS_STATIC_PROP(dirty, dirty, Document);
-  JS_STATIC_PROP(documentFileName, document_file_name, Document);
-  JS_STATIC_PROP(external, external, Document);
-  JS_STATIC_PROP(filesize, filesize, Document);
-  JS_STATIC_PROP(icons, icons, Document);
-  JS_STATIC_PROP(info, info, Document);
-  JS_STATIC_PROP(keywords, keywords, Document);
-  JS_STATIC_PROP(layout, layout, Document);
-  JS_STATIC_PROP(media, media, Document);
-  JS_STATIC_PROP(modDate, mod_date, Document);
-  JS_STATIC_PROP(mouseX, mouse_x, Document);
-  JS_STATIC_PROP(mouseY, mouse_y, Document);
-  JS_STATIC_PROP(numFields, num_fields, Document);
-  JS_STATIC_PROP(numPages, num_pages, Document);
-  JS_STATIC_PROP(pageNum, page_num, Document);
-  JS_STATIC_PROP(pageWindowRect, page_window_rect, Document);
-  JS_STATIC_PROP(path, path, Document);
-  JS_STATIC_PROP(producer, producer, Document);
-  JS_STATIC_PROP(subject, subject, Document);
-  JS_STATIC_PROP(title, title, Document);
-  JS_STATIC_PROP(URL, URL, Document);
-  JS_STATIC_PROP(zoom, zoom, Document);
-  JS_STATIC_PROP(zoomType, zoom_type, Document);
-
-  JS_STATIC_METHOD(addAnnot, Document);
-  JS_STATIC_METHOD(addField, Document);
-  JS_STATIC_METHOD(addLink, Document);
-  JS_STATIC_METHOD(addIcon, Document);
-  JS_STATIC_METHOD(calculateNow, Document);
-  JS_STATIC_METHOD(closeDoc, Document);
-  JS_STATIC_METHOD(createDataObject, Document);
-  JS_STATIC_METHOD(deletePages, Document);
-  JS_STATIC_METHOD(exportAsText, Document);
-  JS_STATIC_METHOD(exportAsFDF, Document);
-  JS_STATIC_METHOD(exportAsXFDF, Document);
-  JS_STATIC_METHOD(extractPages, Document);
-  JS_STATIC_METHOD(getAnnot, Document);
-  JS_STATIC_METHOD(getAnnots, Document);
-  JS_STATIC_METHOD(getAnnot3D, Document);
-  JS_STATIC_METHOD(getAnnots3D, Document);
-  JS_STATIC_METHOD(getField, Document);
-  JS_STATIC_METHOD(getIcon, Document);
-  JS_STATIC_METHOD(getLinks, Document);
-  JS_STATIC_METHOD(getNthFieldName, Document);
-  JS_STATIC_METHOD(getOCGs, Document);
-  JS_STATIC_METHOD(getPageBox, Document);
-  JS_STATIC_METHOD(getPageNthWord, Document);
-  JS_STATIC_METHOD(getPageNthWordQuads, Document);
-  JS_STATIC_METHOD(getPageNumWords, Document);
-  JS_STATIC_METHOD(getPrintParams, Document);
-  JS_STATIC_METHOD(getURL, Document);
-  JS_STATIC_METHOD(gotoNamedDest, Document);
-  JS_STATIC_METHOD(importAnFDF, Document);
-  JS_STATIC_METHOD(importAnXFDF, Document);
-  JS_STATIC_METHOD(importTextData, Document);
-  JS_STATIC_METHOD(insertPages, Document);
-  JS_STATIC_METHOD(mailForm, Document);
-  JS_STATIC_METHOD(print, Document);
-  JS_STATIC_METHOD(removeField, Document);
-  JS_STATIC_METHOD(replacePages, Document);
-  JS_STATIC_METHOD(removeIcon, Document);
-  JS_STATIC_METHOD(resetForm, Document);
-  JS_STATIC_METHOD(saveAs, Document);
-  JS_STATIC_METHOD(submitForm, Document);
-  JS_STATIC_METHOD(syncAnnotScan, Document);
-  JS_STATIC_METHOD(mailDoc, Document);
+  JS_STATIC_METHOD(addAnnot, CJS_Document)
+  JS_STATIC_METHOD(addField, CJS_Document)
+  JS_STATIC_METHOD(addLink, CJS_Document)
+  JS_STATIC_METHOD(addIcon, CJS_Document)
+  JS_STATIC_METHOD(calculateNow, CJS_Document)
+  JS_STATIC_METHOD(closeDoc, CJS_Document)
+  JS_STATIC_METHOD(createDataObject, CJS_Document)
+  JS_STATIC_METHOD(deletePages, CJS_Document)
+  JS_STATIC_METHOD(exportAsText, CJS_Document)
+  JS_STATIC_METHOD(exportAsFDF, CJS_Document)
+  JS_STATIC_METHOD(exportAsXFDF, CJS_Document)
+  JS_STATIC_METHOD(extractPages, CJS_Document)
+  JS_STATIC_METHOD(getAnnot, CJS_Document)
+  JS_STATIC_METHOD(getAnnots, CJS_Document)
+  JS_STATIC_METHOD(getAnnot3D, CJS_Document)
+  JS_STATIC_METHOD(getAnnots3D, CJS_Document)
+  JS_STATIC_METHOD(getField, CJS_Document)
+  JS_STATIC_METHOD(getIcon, CJS_Document)
+  JS_STATIC_METHOD(getLinks, CJS_Document)
+  JS_STATIC_METHOD(getNthFieldName, CJS_Document)
+  JS_STATIC_METHOD(getOCGs, CJS_Document)
+  JS_STATIC_METHOD(getPageBox, CJS_Document)
+  JS_STATIC_METHOD(getPageNthWord, CJS_Document)
+  JS_STATIC_METHOD(getPageNthWordQuads, CJS_Document)
+  JS_STATIC_METHOD(getPageNumWords, CJS_Document)
+  JS_STATIC_METHOD(getPrintParams, CJS_Document)
+  JS_STATIC_METHOD(getURL, CJS_Document)
+  JS_STATIC_METHOD(gotoNamedDest, CJS_Document)
+  JS_STATIC_METHOD(importAnFDF, CJS_Document)
+  JS_STATIC_METHOD(importAnXFDF, CJS_Document)
+  JS_STATIC_METHOD(importTextData, CJS_Document)
+  JS_STATIC_METHOD(insertPages, CJS_Document)
+  JS_STATIC_METHOD(mailDoc, CJS_Document)
+  JS_STATIC_METHOD(mailForm, CJS_Document)
+  JS_STATIC_METHOD(print, CJS_Document)
+  JS_STATIC_METHOD(removeField, CJS_Document)
+  JS_STATIC_METHOD(replacePages, CJS_Document)
+  JS_STATIC_METHOD(removeIcon, CJS_Document)
+  JS_STATIC_METHOD(resetForm, CJS_Document)
+  JS_STATIC_METHOD(saveAs, CJS_Document)
+  JS_STATIC_METHOD(submitForm, CJS_Document)
+  JS_STATIC_METHOD(syncAnnotScan, CJS_Document)
 
  private:
   static int ObjDefnID;
+  static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
+
+  CJS_Result get_ADBE(CJS_Runtime* pRuntime);
+  CJS_Result set_ADBE(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_author(CJS_Runtime* pRuntime);
+  CJS_Result set_author(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_base_URL(CJS_Runtime* pRuntime);
+  CJS_Result set_base_URL(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_bookmark_root(CJS_Runtime* pRuntime);
+  CJS_Result set_bookmark_root(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_calculate(CJS_Runtime* pRuntime);
+  CJS_Result set_calculate(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_collab(CJS_Runtime* pRuntime);
+  CJS_Result set_collab(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_creation_date(CJS_Runtime* pRuntime);
+  CJS_Result set_creation_date(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_creator(CJS_Runtime* pRuntime);
+  CJS_Result set_creator(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_delay(CJS_Runtime* pRuntime);
+  CJS_Result set_delay(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_dirty(CJS_Runtime* pRuntime);
+  CJS_Result set_dirty(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_document_file_name(CJS_Runtime* pRuntime);
+  CJS_Result set_document_file_name(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp);
+
+  CJS_Result get_external(CJS_Runtime* pRuntime);
+  CJS_Result set_external(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_filesize(CJS_Runtime* pRuntime);
+  CJS_Result set_filesize(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_icons(CJS_Runtime* pRuntime);
+  CJS_Result set_icons(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_info(CJS_Runtime* pRuntime);
+  CJS_Result set_info(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_keywords(CJS_Runtime* pRuntime);
+  CJS_Result set_keywords(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_layout(CJS_Runtime* pRuntime);
+  CJS_Result set_layout(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_media(CJS_Runtime* pRuntime);
+  CJS_Result set_media(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_mod_date(CJS_Runtime* pRuntime);
+  CJS_Result set_mod_date(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_mouse_x(CJS_Runtime* pRuntime);
+  CJS_Result set_mouse_x(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_mouse_y(CJS_Runtime* pRuntime);
+  CJS_Result set_mouse_y(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_num_fields(CJS_Runtime* pRuntime);
+  CJS_Result set_num_fields(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_num_pages(CJS_Runtime* pRuntime);
+  CJS_Result set_num_pages(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_page_num(CJS_Runtime* pRuntime);
+  CJS_Result set_page_num(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_page_window_rect(CJS_Runtime* pRuntime);
+  CJS_Result set_page_window_rect(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp);
+
+  CJS_Result get_path(CJS_Runtime* pRuntime);
+  CJS_Result set_path(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_producer(CJS_Runtime* pRuntime);
+  CJS_Result set_producer(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_subject(CJS_Runtime* pRuntime);
+  CJS_Result set_subject(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_title(CJS_Runtime* pRuntime);
+  CJS_Result set_title(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_zoom(CJS_Runtime* pRuntime);
+  CJS_Result set_zoom(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_zoom_type(CJS_Runtime* pRuntime);
+  CJS_Result set_zoom_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_URL(CJS_Runtime* pRuntime);
+  CJS_Result set_URL(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result addAnnot(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result addField(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result addLink(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result addIcon(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result calculateNow(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result closeDoc(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result createDataObject(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result deletePages(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result exportAsText(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result exportAsFDF(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result exportAsXFDF(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result extractPages(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getAnnot(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getAnnots(CJS_Runtime* pRuntime,
+                       const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getAnnot3D(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getAnnots3D(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getField(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getIcon(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getLinks(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getNthFieldName(CJS_Runtime* pRuntime,
+                             const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getOCGs(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getPageBox(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getPageNthWord(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getPageNthWordQuads(
+      CJS_Runtime* pRuntime,
+      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getPageNumWords(CJS_Runtime* pRuntime,
+                             const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getPrintParams(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getURL(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result gotoNamedDest(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result importAnFDF(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result importAnXFDF(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result importTextData(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result insertPages(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result mailForm(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result print(CJS_Runtime* pRuntime,
+                   const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result removeField(CJS_Runtime* pRuntime,
+                         const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result replacePages(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result resetForm(CJS_Runtime* pRuntime,
+                       const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result saveAs(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result submitForm(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result syncAnnotScan(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result mailDoc(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result removeIcon(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
+
+  CJS_Result getPropertyInternal(CJS_Runtime* pRuntime,
+                                 const ByteString& propName);
+  CJS_Result setPropertyInternal(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp,
+                                 const ByteString& propName);
+
+  CPDF_InteractiveForm* GetCoreInteractiveForm();
+  CPDFSDK_InteractiveForm* GetSDKInteractiveForm();
+
+  WideString m_cwBaseURL;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  std::list<std::unique_ptr<CJS_DelayData>> m_DelayData;
+  // Needs to be a std::list for iterator stability.
+  std::list<WideString> m_IconNames;
+  bool m_bDelay = false;
 };
 
 #endif  // FXJS_CJS_DOCUMENT_H_
diff --git a/fxjs/cjs_embedobj.cpp b/fxjs/cjs_embedobj.cpp
deleted file mode 100644
index cbebd63..0000000
--- a/fxjs/cjs_embedobj.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_embedobj.h"
-
-#include "fxjs/cjs_object.h"
-
-CJS_EmbedObj::CJS_EmbedObj(CJS_Object* pJSObject) : m_pJSObject(pJSObject) {}
-
-CJS_EmbedObj::~CJS_EmbedObj() {}
diff --git a/fxjs/cjs_embedobj.h b/fxjs/cjs_embedobj.h
deleted file mode 100644
index 82e549e..0000000
--- a/fxjs/cjs_embedobj.h
+++ /dev/null
@@ -1,25 +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 FXJS_CJS_EMBEDOBJ_H_
-#define FXJS_CJS_EMBEDOBJ_H_
-
-#include "core/fxcrt/unowned_ptr.h"
-
-class CJS_Object;
-
-class CJS_EmbedObj {
- public:
-  explicit CJS_EmbedObj(CJS_Object* pJSObject);
-  virtual ~CJS_EmbedObj();
-
-  CJS_Object* GetJSObject() const { return m_pJSObject.Get(); }
-
- protected:
-  UnownedPtr<CJS_Object> const m_pJSObject;
-};
-
-#endif  // FXJS_CJS_EMBEDOBJ_H_
diff --git a/fxjs/cjs_event.cpp b/fxjs/cjs_event.cpp
index 4fb988f..a16ee6c 100644
--- a/fxjs/cjs_event.cpp
+++ b/fxjs/cjs_event.cpp
@@ -6,11 +6,11 @@
 
 #include "fxjs/cjs_event.h"
 
-#include "fxjs/JS_Define.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventhandler.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
 const JSPropertySpec CJS_Event::PropertySpecs[] = {
     {"change", get_change_static, set_change_static},
@@ -35,273 +35,289 @@
     {"willCommit", get_will_commit_static, set_will_commit_static}};
 
 int CJS_Event::ObjDefnID = -1;
+const char CJS_Event::kName[] = "event";
+
+// static
+int CJS_Event::GetObjDefnID() {
+  return ObjDefnID;
+}
 
 // static
 void CJS_Event::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID = pEngine->DefineObj("event", FXJSOBJTYPE_STATIC,
-                                 JSConstructor<CJS_Event, event>,
-                                 JSDestructor<CJS_Event>);
-  DefineProps(pEngine, ObjDefnID, PropertySpecs, FX_ArraySize(PropertySpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_Event::kName, FXJSOBJTYPE_STATIC,
+                                 JSConstructor<CJS_Event>, JSDestructor);
+  DefineProps(pEngine, ObjDefnID, PropertySpecs);
 }
 
-event::event(CJS_Object* pJsObject) : CJS_EmbedObj(pJsObject) {}
+CJS_Event::CJS_Event(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-event::~event() {}
+CJS_Event::~CJS_Event() = default;
 
-CJS_Return event::get_change(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewString(pEvent->Change().c_str()));
+CJS_Result CJS_Event::get_change(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(
+      pRuntime->NewString(pEvent->Change().AsStringView()));
 }
 
-CJS_Return event::set_change(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
+CJS_Result CJS_Event::set_change(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
 
   if (vp->IsString()) {
     WideString& wChange = pEvent->Change();
     wChange = pRuntime->ToWideString(vp);
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return event::get_change_ex(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  return CJS_Return(pRuntime->NewString(pEvent->ChangeEx().c_str()));
+CJS_Result CJS_Event::get_change_ex(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(
+      pRuntime->NewString(pEvent->ChangeEx().AsStringView()));
 }
 
-CJS_Return event::set_change_ex(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Event::set_change_ex(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return event::get_commit_key(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  return CJS_Return(pRuntime->NewNumber(pEvent->CommitKey()));
+CJS_Result CJS_Event::get_commit_key(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(pRuntime->NewNumber(pEvent->CommitKey()));
 }
 
-CJS_Return event::set_commit_key(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return event::get_field_full(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  if (wcscmp((const wchar_t*)pEvent->Name(), L"Keystroke") != 0)
-    return CJS_Return(false);
-
-  return CJS_Return(pRuntime->NewBoolean(pEvent->FieldFull()));
-}
-
-CJS_Return event::set_field_full(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return event::get_key_down(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewBoolean(pEvent->KeyDown()));
-}
-
-CJS_Return event::set_key_down(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return event::get_modifier(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewBoolean(pEvent->Modifier()));
-}
-
-CJS_Return event::set_modifier(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return event::get_name(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewString(pEvent->Name()));
-}
-
-CJS_Return event::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return event::get_rc(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewBoolean(pEvent->Rc()));
-}
-
-CJS_Return event::set_rc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  pEvent->Rc() = pRuntime->ToBoolean(vp);
-  return CJS_Return(true);
-}
-
-CJS_Return event::get_rich_change(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
-}
-
-CJS_Return event::set_rich_change(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
-}
-
-CJS_Return event::get_rich_change_ex(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
-}
-
-CJS_Return event::set_rich_change_ex(CJS_Runtime* pRuntime,
+CJS_Result CJS_Event::set_commit_key(CJS_Runtime* pRuntime,
                                      v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return event::get_rich_value(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Event::get_field_full(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (pEvent->Name() != "Keystroke")
+    return CJS_Result::Failure(L"unrecognized event");
+
+  return CJS_Result::Success(pRuntime->NewBoolean(pEvent->FieldFull()));
 }
 
-CJS_Return event::set_rich_value(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Event::set_field_full(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return event::get_sel_end(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  if (wcscmp((const wchar_t*)pEvent->Name(), L"Keystroke") != 0)
-    return CJS_Return(true);
-
-  return CJS_Return(pRuntime->NewNumber(pEvent->SelEnd()));
+CJS_Result CJS_Event::get_key_down(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(pRuntime->NewBoolean(pEvent->KeyDown()));
 }
 
-CJS_Return event::set_sel_end(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  if (wcscmp((const wchar_t*)pEvent->Name(), L"Keystroke") != 0)
-    return CJS_Return(true);
-
-  pEvent->SetSelEnd(pRuntime->ToInt32(vp));
-  return CJS_Return(true);
+CJS_Result CJS_Event::set_key_down(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return event::get_sel_start(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  if (wcscmp((const wchar_t*)pEvent->Name(), L"Keystroke") != 0)
-    return CJS_Return(true);
-
-  return CJS_Return(pRuntime->NewNumber(pEvent->SelStart()));
+CJS_Result CJS_Event::get_modifier(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Modifier()));
 }
 
-CJS_Return event::set_sel_start(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  if (wcscmp((const wchar_t*)pEvent->Name(), L"Keystroke") != 0)
-    return CJS_Return(true);
-
-  pEvent->SetSelStart(pRuntime->ToInt32(vp));
-  return CJS_Return(true);
+CJS_Result CJS_Event::set_modifier(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return event::get_shift(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewBoolean(pEvent->Shift()));
+CJS_Result CJS_Event::get_name(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(pRuntime->NewString(pEvent->Name()));
 }
 
-CJS_Return event::set_shift(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Event::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return event::get_source(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pEvent->Source()->GetJSObject()->ToV8Object());
+CJS_Result CJS_Event::get_rc(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Rc()));
 }
 
-CJS_Return event::set_source(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Event::set_rc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  pEvent->Rc() = pRuntime->ToBoolean(vp);
+  return CJS_Result::Success();
 }
 
-CJS_Return event::get_target(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pEvent->Target_Field()->GetJSObject()->ToV8Object());
+CJS_Result CJS_Event::get_rich_change(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return event::set_target(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Event::set_rich_change(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return event::get_target_name(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewString(pEvent->TargetName().c_str()));
+CJS_Result CJS_Event::get_rich_change_ex(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return event::set_target_name(CJS_Runtime* pRuntime,
+CJS_Result CJS_Event::set_rich_change_ex(CJS_Runtime* pRuntime,
+                                         v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Event::get_rich_value(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Event::set_rich_value(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Event::get_sel_end(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (pEvent->Name() != "Keystroke")
+    return CJS_Result::Success();
+
+  return CJS_Result::Success(pRuntime->NewNumber(pEvent->SelEnd()));
+}
+
+CJS_Result CJS_Event::set_sel_end(CJS_Runtime* pRuntime,
                                   v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (pEvent->Name() == "Keystroke")
+    pEvent->SetSelEnd(pRuntime->ToInt32(vp));
+
+  return CJS_Result::Success();
 }
 
-CJS_Return event::get_type(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewString(pEvent->Type()));
+CJS_Result CJS_Event::get_sel_start(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (pEvent->Name() != "Keystroke")
+    return CJS_Result::Success();
+
+  return CJS_Result::Success(pRuntime->NewNumber(pEvent->SelStart()));
 }
 
-CJS_Return event::set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Event::set_sel_start(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (pEvent->Name() == "Keystroke")
+    pEvent->SetSelStart(pRuntime->ToInt32(vp));
+
+  return CJS_Result::Success();
 }
 
-CJS_Return event::get_value(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-
-  if (wcscmp((const wchar_t*)pEvent->Type(), L"Field") != 0)
-    return CJS_Return(false);
-
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
-
-  return CJS_Return(pRuntime->NewString(pEvent->Value().c_str()));
+CJS_Result CJS_Event::get_shift(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Shift()));
 }
 
-CJS_Return event::set_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
+CJS_Result CJS_Event::set_shift(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
 
-  if (wcscmp((const wchar_t*)pEvent->Type(), L"Field") != 0)
-    return CJS_Return(false);
+CJS_Result CJS_Event::get_source(CJS_Runtime* pRuntime) {
+  CJS_Field* pField = pRuntime->GetCurrentEventContext()->SourceField();
+  if (!pField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(pField->ToV8Object());
+}
 
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+CJS_Result CJS_Event::set_source(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_Event::get_target(CJS_Runtime* pRuntime) {
+  CJS_Field* pField = pRuntime->GetCurrentEventContext()->TargetField();
+  if (!pField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success(pField->ToV8Object());
+}
+
+CJS_Result CJS_Event::set_target(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_Event::get_target_name(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(
+      pRuntime->NewString(pEvent->TargetName().AsStringView()));
+}
+
+CJS_Result CJS_Event::set_target_name(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_Event::get_type(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  return CJS_Result::Success(pRuntime->NewString(pEvent->Type()));
+}
+
+CJS_Result CJS_Event::set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_Event::get_value(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (pEvent->Type() != "Field")
+    return CJS_Result::Failure(L"Bad event type.");
+
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  return CJS_Result::Success(
+      pRuntime->NewString(pEvent->Value().AsStringView()));
+}
+
+CJS_Result CJS_Event::set_value(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (pEvent->Type() != "Field")
+    return CJS_Result::Failure(L"Bad event type.");
+
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  if (vp.IsEmpty())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  if (vp->IsNullOrUndefined() || vp->IsBoolean())
+    return CJS_Result::Failure(JSMessage::kInvalidSetError);
 
   pEvent->Value() = pRuntime->ToWideString(vp);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return event::get_will_commit(CJS_Runtime* pRuntime) {
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  return CJS_Return(pRuntime->NewBoolean(pEvent->WillCommit()));
+CJS_Result CJS_Event::get_will_commit(CJS_Runtime* pRuntime) {
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+
+  return CJS_Result::Success(pRuntime->NewBoolean(pEvent->WillCommit()));
 }
 
-CJS_Return event::set_will_commit(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Event::set_will_commit(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
diff --git a/fxjs/cjs_event.h b/fxjs/cjs_event.h
index 4ae5fa2..b2fa4dc 100644
--- a/fxjs/cjs_event.h
+++ b/fxjs/cjs_event.h
@@ -7,105 +7,102 @@
 #ifndef FXJS_CJS_EVENT_H_
 #define FXJS_CJS_EVENT_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
-class event : public CJS_EmbedObj {
+class CJS_Event final : public CJS_Object {
  public:
-  explicit event(CJS_Object* pJSObject);
-  ~event() override;
-
-  CJS_Return get_change(CJS_Runtime* pRuntime);
-  CJS_Return set_change(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_change_ex(CJS_Runtime* pRuntime);
-  CJS_Return set_change_ex(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_commit_key(CJS_Runtime* pRuntime);
-  CJS_Return set_commit_key(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_field_full(CJS_Runtime* pRuntime);
-  CJS_Return set_field_full(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_key_down(CJS_Runtime* pRuntime);
-  CJS_Return set_key_down(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_modifier(CJS_Runtime* pRuntime);
-  CJS_Return set_modifier(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_name(CJS_Runtime* pRuntime);
-  CJS_Return set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rc(CJS_Runtime* pRuntime);
-  CJS_Return set_rc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rich_change(CJS_Runtime* pRuntime);
-  CJS_Return set_rich_change(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rich_change_ex(CJS_Runtime* pRuntime);
-  CJS_Return set_rich_change_ex(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rich_value(CJS_Runtime* pRuntime);
-  CJS_Return set_rich_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_sel_end(CJS_Runtime* pRuntime);
-  CJS_Return set_sel_end(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_sel_start(CJS_Runtime* pRuntime);
-  CJS_Return set_sel_start(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_shift(CJS_Runtime* pRuntime);
-  CJS_Return set_shift(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_source(CJS_Runtime* pRuntime);
-  CJS_Return set_source(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_target(CJS_Runtime* pRuntime);
-  CJS_Return set_target(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_target_name(CJS_Runtime* pRuntime);
-  CJS_Return set_target_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_type(CJS_Runtime* pRuntime);
-  CJS_Return set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_value(CJS_Runtime* pRuntime);
-  CJS_Return set_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_will_commit(CJS_Runtime* pRuntime);
-  CJS_Return set_will_commit(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-};
-
-class CJS_Event : public CJS_Object {
- public:
+  static int GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Event(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Event() override {}
+  CJS_Event(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Event() override;
 
-  JS_STATIC_PROP(change, change, event);
-  JS_STATIC_PROP(changeEx, change_ex, event);
-  JS_STATIC_PROP(commitKey, commit_key, event);
-  JS_STATIC_PROP(fieldFull, field_full, event);
-  JS_STATIC_PROP(keyDown, key_down, event);
-  JS_STATIC_PROP(modifier, modifier, event);
-  JS_STATIC_PROP(name, name, event);
-  JS_STATIC_PROP(rc, rc, event);
-  JS_STATIC_PROP(richChange, rich_change, event);
-  JS_STATIC_PROP(richChangeEx, rich_change_ex, event);
-  JS_STATIC_PROP(richValue, rich_value, event);
-  JS_STATIC_PROP(selEnd, sel_end, event);
-  JS_STATIC_PROP(selStart, sel_start, event);
-  JS_STATIC_PROP(shift, shift, event);
-  JS_STATIC_PROP(source, source, event);
-  JS_STATIC_PROP(target, target, event);
-  JS_STATIC_PROP(targetName, target_name, event);
-  JS_STATIC_PROP(type, type, event);
-  JS_STATIC_PROP(value, value, event);
-  JS_STATIC_PROP(willCommit, will_commit, event);
+  JS_STATIC_PROP(change, change, CJS_Event)
+  JS_STATIC_PROP(changeEx, change_ex, CJS_Event)
+  JS_STATIC_PROP(commitKey, commit_key, CJS_Event)
+  JS_STATIC_PROP(fieldFull, field_full, CJS_Event)
+  JS_STATIC_PROP(keyDown, key_down, CJS_Event)
+  JS_STATIC_PROP(modifier, modifier, CJS_Event)
+  JS_STATIC_PROP(name, name, CJS_Event)
+  JS_STATIC_PROP(rc, rc, CJS_Event)
+  JS_STATIC_PROP(richChange, rich_change, CJS_Event)
+  JS_STATIC_PROP(richChangeEx, rich_change_ex, CJS_Event)
+  JS_STATIC_PROP(richValue, rich_value, CJS_Event)
+  JS_STATIC_PROP(selEnd, sel_end, CJS_Event)
+  JS_STATIC_PROP(selStart, sel_start, CJS_Event)
+  JS_STATIC_PROP(shift, shift, CJS_Event)
+  JS_STATIC_PROP(source, source, CJS_Event)
+  JS_STATIC_PROP(target, target, CJS_Event)
+  JS_STATIC_PROP(targetName, target_name, CJS_Event)
+  JS_STATIC_PROP(type, type, CJS_Event)
+  JS_STATIC_PROP(value, value, CJS_Event)
+  JS_STATIC_PROP(willCommit, will_commit, CJS_Event)
 
  private:
   static int ObjDefnID;
+  static const char kName[];
   static const JSPropertySpec PropertySpecs[];
+
+  CJS_Result get_change(CJS_Runtime* pRuntime);
+  CJS_Result set_change(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_change_ex(CJS_Runtime* pRuntime);
+  CJS_Result set_change_ex(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_commit_key(CJS_Runtime* pRuntime);
+  CJS_Result set_commit_key(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_field_full(CJS_Runtime* pRuntime);
+  CJS_Result set_field_full(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_key_down(CJS_Runtime* pRuntime);
+  CJS_Result set_key_down(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_modifier(CJS_Runtime* pRuntime);
+  CJS_Result set_modifier(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_name(CJS_Runtime* pRuntime);
+  CJS_Result set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rc(CJS_Runtime* pRuntime);
+  CJS_Result set_rc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rich_change(CJS_Runtime* pRuntime);
+  CJS_Result set_rich_change(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rich_change_ex(CJS_Runtime* pRuntime);
+  CJS_Result set_rich_change_ex(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rich_value(CJS_Runtime* pRuntime);
+  CJS_Result set_rich_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_sel_end(CJS_Runtime* pRuntime);
+  CJS_Result set_sel_end(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_sel_start(CJS_Runtime* pRuntime);
+  CJS_Result set_sel_start(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_shift(CJS_Runtime* pRuntime);
+  CJS_Result set_shift(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_source(CJS_Runtime* pRuntime);
+  CJS_Result set_source(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_target(CJS_Runtime* pRuntime);
+  CJS_Result set_target(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_target_name(CJS_Runtime* pRuntime);
+  CJS_Result set_target_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_type(CJS_Runtime* pRuntime);
+  CJS_Result set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_value(CJS_Runtime* pRuntime);
+  CJS_Result set_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_will_commit(CJS_Runtime* pRuntime);
+  CJS_Result set_will_commit(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
 };
 
 #endif  // FXJS_CJS_EVENT_H_
diff --git a/fxjs/cjs_event_context.cpp b/fxjs/cjs_event_context.cpp
index d2f270b..27e8120 100644
--- a/fxjs/cjs_event_context.cpp
+++ b/fxjs/cjs_event_context.cpp
@@ -7,275 +7,320 @@
 #include "fxjs/cjs_event_context.h"
 
 #include "core/fxcrt/autorestorer.h"
-#include "fxjs/JS_Define.h"
-#include "fxjs/cjs_eventhandler.h"
+#include "fxjs/cjs_eventrecorder.h"
+#include "fxjs/cjs_field.h"
 #include "fxjs/cjs_runtime.h"
+#include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/ptr_util.h"
 
 CJS_EventContext::CJS_EventContext(CJS_Runtime* pRuntime)
     : m_pRuntime(pRuntime),
-      m_pEventHandler(new CJS_EventHandler(this)),
-      m_bBusy(false) {
+      m_pEventRecorder(pdfium::MakeUnique<CJS_EventRecorder>()) {
   ASSERT(pRuntime);
 }
 
-CJS_EventContext::~CJS_EventContext() {}
+CJS_EventContext::~CJS_EventContext() = default;
 
 CPDFSDK_FormFillEnvironment* CJS_EventContext::GetFormFillEnv() {
   return m_pRuntime->GetFormFillEnv();
 }
 
-bool CJS_EventContext::RunScript(const WideString& script, WideString* info) {
+Optional<IJS_Runtime::JS_Error> CJS_EventContext::RunScript(
+    const WideString& script) {
   v8::Isolate::Scope isolate_scope(m_pRuntime->GetIsolate());
   v8::HandleScope handle_scope(m_pRuntime->GetIsolate());
-  v8::Local<v8::Context> context = m_pRuntime->NewLocalContext();
+  v8::Local<v8::Context> context = m_pRuntime->GetV8Context();
   v8::Context::Scope context_scope(context);
 
   if (m_bBusy) {
-    *info = JSGetStringFromID(JSMessage::kBusyError);
-    return false;
+    return IJS_Runtime::JS_Error(1, 1,
+                                 JSGetStringFromID(JSMessage::kBusyError));
   }
 
   AutoRestorer<bool> restorer(&m_bBusy);
   m_bBusy = true;
 
-  ASSERT(m_pEventHandler->IsValid());
-  CJS_Runtime::FieldEvent event(m_pEventHandler->TargetName(),
-                                m_pEventHandler->EventType());
+  ASSERT(m_pEventRecorder->IsValid());
+  CJS_Runtime::FieldEvent event(m_pEventRecorder->TargetName(),
+                                m_pEventRecorder->EventType());
   if (!m_pRuntime->AddEventToSet(event)) {
-    *info = JSGetStringFromID(JSMessage::kDuplicateEventError);
-    return false;
+    return IJS_Runtime::JS_Error(
+        1, 1, JSGetStringFromID(JSMessage::kDuplicateEventError));
   }
 
-  WideString sErrorMessage;
-  int nRet = 0;
+  Optional<IJS_Runtime::JS_Error> err;
   if (script.GetLength() > 0)
-    nRet = m_pRuntime->ExecuteScript(script.c_str(), &sErrorMessage);
-
-  if (nRet < 0)
-    *info += sErrorMessage;
-  else
-    *info = JSGetStringFromID(JSMessage::kRunSuccess);
+    err = m_pRuntime->ExecuteScript(script);
 
   m_pRuntime->RemoveEventFromSet(event);
-  m_pEventHandler->Destroy();
-  return nRet >= 0;
+  m_pEventRecorder->Destroy();
+  return err;
+}
+
+CJS_Field* CJS_EventContext::SourceField() {
+  v8::Local<v8::Object> pDocObj = m_pRuntime->NewFXJSBoundObject(
+      CJS_Document::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
+  if (pDocObj.IsEmpty())
+    return nullptr;
+
+  v8::Local<v8::Object> pFieldObj = m_pRuntime->NewFXJSBoundObject(
+      CJS_Field::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
+  if (pFieldObj.IsEmpty())
+    return nullptr;
+
+  auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment();
+  if (!pFormFillEnv)
+    pFormFillEnv = GetFormFillEnv();
+
+  auto* pJSDocument =
+      static_cast<CJS_Document*>(CFXJS_Engine::GetObjectPrivate(pDocObj));
+  pJSDocument->SetFormFillEnv(pFormFillEnv);
+
+  auto* pJSField =
+      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
+  pJSField->AttachField(pJSDocument, m_pEventRecorder->SourceName());
+  return pJSField;
+}
+
+CJS_Field* CJS_EventContext::TargetField() {
+  v8::Local<v8::Object> pDocObj = m_pRuntime->NewFXJSBoundObject(
+      CJS_Document::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
+  if (pDocObj.IsEmpty())
+    return nullptr;
+
+  v8::Local<v8::Object> pFieldObj = m_pRuntime->NewFXJSBoundObject(
+      CJS_Field::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
+  if (pFieldObj.IsEmpty())
+    return nullptr;
+
+  auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment();
+  if (!pFormFillEnv)
+    pFormFillEnv = GetFormFillEnv();
+
+  auto* pJSDocument =
+      static_cast<CJS_Document*>(CFXJS_Engine::GetObjectPrivate(pDocObj));
+  pJSDocument->SetFormFillEnv(pFormFillEnv);
+
+  auto* pJSField =
+      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
+  pJSField->AttachField(pJSDocument, m_pEventRecorder->TargetName());
+  return pJSField;
 }
 
 void CJS_EventContext::OnApp_Init() {
-  m_pEventHandler->OnApp_Init();
+  m_pEventRecorder->OnApp_Init();
 }
 
 void CJS_EventContext::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                                   const WideString& strTargetName) {
-  m_pEventHandler->OnDoc_Open(pFormFillEnv, strTargetName);
+  m_pEventRecorder->OnDoc_Open(pFormFillEnv, strTargetName);
 }
 
 void CJS_EventContext::OnDoc_WillPrint(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnDoc_WillPrint(pFormFillEnv);
+  m_pEventRecorder->OnDoc_WillPrint(pFormFillEnv);
 }
 
 void CJS_EventContext::OnDoc_DidPrint(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnDoc_DidPrint(pFormFillEnv);
+  m_pEventRecorder->OnDoc_DidPrint(pFormFillEnv);
 }
 
 void CJS_EventContext::OnDoc_WillSave(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnDoc_WillSave(pFormFillEnv);
+  m_pEventRecorder->OnDoc_WillSave(pFormFillEnv);
 }
 
 void CJS_EventContext::OnDoc_DidSave(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnDoc_DidSave(pFormFillEnv);
+  m_pEventRecorder->OnDoc_DidSave(pFormFillEnv);
 }
 
 void CJS_EventContext::OnDoc_WillClose(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnDoc_WillClose(pFormFillEnv);
+  m_pEventRecorder->OnDoc_WillClose(pFormFillEnv);
 }
 
 void CJS_EventContext::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnPage_Open(pFormFillEnv);
+  m_pEventRecorder->OnPage_Open(pFormFillEnv);
 }
 
 void CJS_EventContext::OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnPage_Close(pFormFillEnv);
+  m_pEventRecorder->OnPage_Close(pFormFillEnv);
 }
 
 void CJS_EventContext::OnPage_InView(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnPage_InView(pFormFillEnv);
+  m_pEventRecorder->OnPage_InView(pFormFillEnv);
 }
 
 void CJS_EventContext::OnPage_OutView(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnPage_OutView(pFormFillEnv);
+  m_pEventRecorder->OnPage_OutView(pFormFillEnv);
 }
 
 void CJS_EventContext::OnField_MouseDown(bool bModifier,
                                          bool bShift,
                                          CPDF_FormField* pTarget) {
-  m_pEventHandler->OnField_MouseDown(bModifier, bShift, pTarget);
+  m_pEventRecorder->OnField_MouseDown(bModifier, bShift, pTarget);
 }
 
 void CJS_EventContext::OnField_MouseEnter(bool bModifier,
                                           bool bShift,
                                           CPDF_FormField* pTarget) {
-  m_pEventHandler->OnField_MouseEnter(bModifier, bShift, pTarget);
+  m_pEventRecorder->OnField_MouseEnter(bModifier, bShift, pTarget);
 }
 
 void CJS_EventContext::OnField_MouseExit(bool bModifier,
                                          bool bShift,
                                          CPDF_FormField* pTarget) {
-  m_pEventHandler->OnField_MouseExit(bModifier, bShift, pTarget);
+  m_pEventRecorder->OnField_MouseExit(bModifier, bShift, pTarget);
 }
 
 void CJS_EventContext::OnField_MouseUp(bool bModifier,
                                        bool bShift,
                                        CPDF_FormField* pTarget) {
-  m_pEventHandler->OnField_MouseUp(bModifier, bShift, pTarget);
+  m_pEventRecorder->OnField_MouseUp(bModifier, bShift, pTarget);
 }
 
 void CJS_EventContext::OnField_Focus(bool bModifier,
                                      bool bShift,
                                      CPDF_FormField* pTarget,
-                                     const WideString& Value) {
-  m_pEventHandler->OnField_Focus(bModifier, bShift, pTarget, Value);
+                                     WideString* Value) {
+  m_pEventRecorder->OnField_Focus(bModifier, bShift, pTarget, Value);
 }
 
 void CJS_EventContext::OnField_Blur(bool bModifier,
                                     bool bShift,
                                     CPDF_FormField* pTarget,
-                                    const WideString& Value) {
-  m_pEventHandler->OnField_Blur(bModifier, bShift, pTarget, Value);
+                                    WideString* Value) {
+  m_pEventRecorder->OnField_Blur(bModifier, bShift, pTarget, Value);
 }
 
 void CJS_EventContext::OnField_Calculate(CPDF_FormField* pSource,
                                          CPDF_FormField* pTarget,
-                                         WideString& Value,
-                                         bool& bRc) {
-  m_pEventHandler->OnField_Calculate(pSource, pTarget, Value, bRc);
+                                         WideString* pValue,
+                                         bool* pRc) {
+  m_pEventRecorder->OnField_Calculate(pSource, pTarget, pValue, pRc);
 }
 
 void CJS_EventContext::OnField_Format(CPDF_FormField* pTarget,
-                                      WideString& Value,
-                                      bool bWillCommit) {
-  m_pEventHandler->OnField_Format(pTarget, Value, bWillCommit);
+                                      WideString* Value) {
+  m_pEventRecorder->OnField_Format(pTarget, Value);
 }
 
-void CJS_EventContext::OnField_Keystroke(WideString& strChange,
+void CJS_EventContext::OnField_Keystroke(WideString* strChange,
                                          const WideString& strChangeEx,
                                          bool bKeyDown,
                                          bool bModifier,
-                                         int& nSelEnd,
-                                         int& nSelStart,
+                                         int* nSelEnd,
+                                         int* nSelStart,
                                          bool bShift,
                                          CPDF_FormField* pTarget,
-                                         WideString& Value,
+                                         WideString* Value,
                                          bool bWillCommit,
                                          bool bFieldFull,
-                                         bool& bRc) {
-  m_pEventHandler->OnField_Keystroke(
+                                         bool* bRc) {
+  m_pEventRecorder->OnField_Keystroke(
       strChange, strChangeEx, bKeyDown, bModifier, nSelEnd, nSelStart, bShift,
       pTarget, Value, bWillCommit, bFieldFull, bRc);
 }
 
-void CJS_EventContext::OnField_Validate(WideString& strChange,
+void CJS_EventContext::OnField_Validate(WideString* strChange,
                                         const WideString& strChangeEx,
                                         bool bKeyDown,
                                         bool bModifier,
                                         bool bShift,
                                         CPDF_FormField* pTarget,
-                                        WideString& Value,
-                                        bool& bRc) {
-  m_pEventHandler->OnField_Validate(strChange, strChangeEx, bKeyDown, bModifier,
-                                    bShift, pTarget, Value, bRc);
+                                        WideString* Value,
+                                        bool* bRc) {
+  m_pEventRecorder->OnField_Validate(strChange, strChangeEx, bKeyDown,
+                                     bModifier, bShift, pTarget, Value, bRc);
 }
 
 void CJS_EventContext::OnScreen_Focus(bool bModifier,
                                       bool bShift,
                                       CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_Focus(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_Focus(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_Blur(bool bModifier,
                                      bool bShift,
                                      CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_Blur(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_Blur(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_Open(bool bModifier,
                                      bool bShift,
                                      CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_Open(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_Open(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_Close(bool bModifier,
                                       bool bShift,
                                       CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_Close(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_Close(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_MouseDown(bool bModifier,
                                           bool bShift,
                                           CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_MouseDown(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_MouseDown(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_MouseUp(bool bModifier,
                                         bool bShift,
                                         CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_MouseUp(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_MouseUp(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_MouseEnter(bool bModifier,
                                            bool bShift,
                                            CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_MouseEnter(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_MouseEnter(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_MouseExit(bool bModifier,
                                           bool bShift,
                                           CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_MouseExit(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_MouseExit(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_InView(bool bModifier,
                                        bool bShift,
                                        CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_InView(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_InView(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnScreen_OutView(bool bModifier,
                                         bool bShift,
                                         CPDFSDK_Annot* pScreen) {
-  m_pEventHandler->OnScreen_OutView(bModifier, bShift, pScreen);
+  m_pEventRecorder->OnScreen_OutView(bModifier, bShift, pScreen);
 }
 
 void CJS_EventContext::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) {
-  m_pEventHandler->OnBookmark_MouseUp(pBookMark);
+  m_pEventRecorder->OnBookmark_MouseUp(pBookMark);
 }
 
 void CJS_EventContext::OnLink_MouseUp(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnLink_MouseUp(pFormFillEnv);
+  m_pEventRecorder->OnLink_MouseUp(pFormFillEnv);
 }
 
 void CJS_EventContext::OnConsole_Exec() {
-  m_pEventHandler->OnConsole_Exec();
+  m_pEventRecorder->OnConsole_Exec();
 }
 
 void CJS_EventContext::OnExternal_Exec() {
-  m_pEventHandler->OnExternal_Exec();
+  m_pEventRecorder->OnExternal_Exec();
 }
 
 void CJS_EventContext::OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventHandler->OnBatchExec(pFormFillEnv);
+  m_pEventRecorder->OnBatchExec(pFormFillEnv);
 }
 
 void CJS_EventContext::OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                                    const WideString& strTargetName) {
-  m_pEventHandler->OnMenu_Exec(pFormFillEnv, strTargetName);
+  m_pEventRecorder->OnMenu_Exec(pFormFillEnv, strTargetName);
 }
diff --git a/fxjs/cjs_event_context.h b/fxjs/cjs_event_context.h
index f957290..6a953e6 100644
--- a/fxjs/cjs_event_context.h
+++ b/fxjs/cjs_event_context.h
@@ -14,17 +14,18 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/ijs_event_context.h"
 
-class CJS_EventHandler;
+class CJS_EventRecorder;
+class CJS_Field;
 class CJS_Runtime;
 class CPDFSDK_FormFillEnvironment;
 
-class CJS_EventContext : public IJS_EventContext {
+class CJS_EventContext final : public IJS_EventContext {
  public:
   explicit CJS_EventContext(CJS_Runtime* pRuntime);
   ~CJS_EventContext() override;
 
   // IJS_EventContext
-  bool RunScript(const WideString& script, WideString* info) override;
+  Optional<IJS_Runtime::JS_Error> RunScript(const WideString& script) override;
   void OnApp_Init() override;
   void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                   const WideString& strTargetName) override;
@@ -52,38 +53,36 @@
   void OnField_Focus(bool bModifier,
                      bool bShift,
                      CPDF_FormField* pTarget,
-                     const WideString& Value) override;
+                     WideString* Value) override;
   void OnField_Blur(bool bModifier,
                     bool bShift,
                     CPDF_FormField* pTarget,
-                    const WideString& Value) override;
+                    WideString* Value) override;
   void OnField_Calculate(CPDF_FormField* pSource,
                          CPDF_FormField* pTarget,
-                         WideString& Value,
-                         bool& bRc) override;
-  void OnField_Format(CPDF_FormField* pTarget,
-                      WideString& Value,
-                      bool bWillCommit) override;
-  void OnField_Keystroke(WideString& strChange,
+                         WideString* pValue,
+                         bool* pRc) override;
+  void OnField_Format(CPDF_FormField* pTarget, WideString* Value) override;
+  void OnField_Keystroke(WideString* strChange,
                          const WideString& strChangeEx,
                          bool bKeyDown,
                          bool bModifier,
-                         int& nSelEnd,
-                         int& nSelStart,
+                         int* nSelEnd,
+                         int* nSelStart,
                          bool bShift,
                          CPDF_FormField* pTarget,
-                         WideString& Value,
+                         WideString* Value,
                          bool bWillCommit,
                          bool bFieldFull,
-                         bool& bRc) override;
-  void OnField_Validate(WideString& strChange,
+                         bool* bRc) override;
+  void OnField_Validate(WideString* strChange,
                         const WideString& strChangeEx,
                         bool bKeyDown,
                         bool bModifier,
                         bool bShift,
                         CPDF_FormField* pTarget,
-                        WideString& Value,
-                        bool& bRc) override;
+                        WideString* Value,
+                        bool* bRc) override;
   void OnScreen_Focus(bool bModifier,
                       bool bShift,
                       CPDFSDK_Annot* pScreen) override;
@@ -123,14 +122,15 @@
   void OnExternal_Exec() override;
 
   CJS_Runtime* GetJSRuntime() const { return m_pRuntime.Get(); }
-  CJS_EventHandler* GetEventHandler() const { return m_pEventHandler.get(); }
-
+  CJS_EventRecorder* GetEventRecorder() const { return m_pEventRecorder.get(); }
   CPDFSDK_FormFillEnvironment* GetFormFillEnv();
+  CJS_Field* SourceField();
+  CJS_Field* TargetField();
 
  private:
   UnownedPtr<CJS_Runtime> const m_pRuntime;
-  std::unique_ptr<CJS_EventHandler> m_pEventHandler;
-  bool m_bBusy;
+  std::unique_ptr<CJS_EventRecorder> m_pEventRecorder;
+  bool m_bBusy = false;
 };
 
 #endif  // FXJS_CJS_EVENT_CONTEXT_H_
diff --git a/fxjs/cjs_event_context_stub.cpp b/fxjs/cjs_event_context_stub.cpp
index 0517ab2..82530e4 100644
--- a/fxjs/cjs_event_context_stub.cpp
+++ b/fxjs/cjs_event_context_stub.cpp
@@ -6,7 +6,7 @@
 
 #include "fxjs/cjs_event_context_stub.h"
 
-bool CJS_EventContextStub::RunScript(const WideString& script,
-                                     WideString* info) {
-  return false;
+Optional<IJS_Runtime::JS_Error> CJS_EventContextStub::RunScript(
+    const WideString& script) {
+  return IJS_Runtime::JS_Error(1, 1, L"JavaScript support not present");
 }
diff --git a/fxjs/cjs_event_context_stub.h b/fxjs/cjs_event_context_stub.h
index bc85369..06d0184 100644
--- a/fxjs/cjs_event_context_stub.h
+++ b/fxjs/cjs_event_context_stub.h
@@ -15,7 +15,7 @@
   ~CJS_EventContextStub() override {}
 
   // IJS_EventContext:
-  bool RunScript(const WideString& script, WideString* info) override;
+  Optional<IJS_Runtime::JS_Error> RunScript(const WideString& script) override;
 
   void OnApp_Init() override {}
   void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
@@ -44,38 +44,36 @@
   void OnField_Focus(bool bModifier,
                      bool bShift,
                      CPDF_FormField* pTarget,
-                     const WideString& Value) override {}
+                     WideString* Value) override {}
   void OnField_Blur(bool bModifier,
                     bool bShift,
                     CPDF_FormField* pTarget,
-                    const WideString& Value) override {}
+                    WideString* Value) override {}
   void OnField_Calculate(CPDF_FormField* pSource,
                          CPDF_FormField* pTarget,
-                         WideString& Value,
-                         bool& bRc) override {}
-  void OnField_Format(CPDF_FormField* pTarget,
-                      WideString& Value,
-                      bool bWillCommit) override {}
-  void OnField_Keystroke(WideString& strChange,
+                         WideString* pValue,
+                         bool* pRc) override {}
+  void OnField_Format(CPDF_FormField* pTarget, WideString* Value) override {}
+  void OnField_Keystroke(WideString* strChange,
                          const WideString& strChangeEx,
                          bool KeyDown,
                          bool bModifier,
-                         int& nSelEnd,
-                         int& nSelStart,
+                         int* nSelEnd,
+                         int* nSelStart,
                          bool bShift,
                          CPDF_FormField* pTarget,
-                         WideString& Value,
+                         WideString* Value,
                          bool bWillCommit,
                          bool bFieldFull,
-                         bool& bRc) override {}
-  void OnField_Validate(WideString& strChange,
+                         bool* bRc) override {}
+  void OnField_Validate(WideString* strChange,
                         const WideString& strChangeEx,
                         bool bKeyDown,
                         bool bModifier,
                         bool bShift,
                         CPDF_FormField* pTarget,
-                        WideString& Value,
-                        bool& bRc) override {}
+                        WideString* Value,
+                        bool* bRc) override {}
   void OnScreen_Focus(bool bModifier,
                       bool bShift,
                       CPDFSDK_Annot* pScreen) override {}
diff --git a/fxjs/cjs_eventhandler.cpp b/fxjs/cjs_eventhandler.cpp
deleted file mode 100644
index eb150b2..0000000
--- a/fxjs/cjs_eventhandler.cpp
+++ /dev/null
@@ -1,654 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_eventhandler.h"
-
-#include "core/fpdfdoc/cpdf_formfield.h"
-#include "fxjs/JS_Define.h"
-#include "fxjs/cjs_document.h"
-#include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_field.h"
-#include "fxjs/cjs_object.h"
-#include "fxjs/cjs_runtime.h"
-
-CJS_EventHandler::CJS_EventHandler(CJS_EventContext* pContext)
-    : m_pJSEventContext(pContext),
-      m_eEventType(JET_UNKNOWN),
-      m_bValid(false),
-      m_pWideStrChange(nullptr),
-      m_nCommitKey(-1),
-      m_bKeyDown(false),
-      m_bModifier(false),
-      m_bShift(false),
-      m_pISelEnd(nullptr),
-      m_nSelEndDu(0),
-      m_pISelStart(nullptr),
-      m_nSelStartDu(0),
-      m_bWillCommit(false),
-      m_pValue(nullptr),
-      m_bFieldFull(false),
-      m_pbRc(nullptr),
-      m_bRcDu(false),
-      m_pTargetBookMark(nullptr),
-      m_pTargetFormFillEnv(nullptr),
-      m_pTargetAnnot(nullptr) {}
-
-CJS_EventHandler::~CJS_EventHandler() {}
-
-void CJS_EventHandler::OnApp_Init() {
-  Initial(JET_APP_INIT);
-}
-
-void CJS_EventHandler::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                  const WideString& strTargetName) {
-  Initial(JET_DOC_OPEN);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-  m_strTargetName = strTargetName;
-}
-
-void CJS_EventHandler::OnDoc_WillPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_DOC_WILLPRINT);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnDoc_DidPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_DOC_DIDPRINT);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnDoc_WillSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_DOC_WILLSAVE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnDoc_DidSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_DOC_DIDSAVE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnDoc_WillClose(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_DOC_WILLCLOSE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_PAGE_OPEN);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_PAGE_CLOSE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnPage_InView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_PAGE_INVIEW);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnPage_OutView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initial(JET_PAGE_OUTVIEW);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventHandler::OnField_MouseEnter(bool bModifier,
-                                          bool bShift,
-                                          CPDF_FormField* pTarget) {
-  Initial(JET_FIELD_MOUSEENTER);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventHandler::OnField_MouseExit(bool bModifier,
-                                         bool bShift,
-                                         CPDF_FormField* pTarget) {
-  Initial(JET_FIELD_MOUSEEXIT);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventHandler::OnField_MouseDown(bool bModifier,
-                                         bool bShift,
-                                         CPDF_FormField* pTarget) {
-  Initial(JET_FIELD_MOUSEDOWN);
-  m_eEventType = JET_FIELD_MOUSEDOWN;
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventHandler::OnField_MouseUp(bool bModifier,
-                                       bool bShift,
-                                       CPDF_FormField* pTarget) {
-  Initial(JET_FIELD_MOUSEUP);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventHandler::OnField_Focus(bool bModifier,
-                                     bool bShift,
-                                     CPDF_FormField* pTarget,
-                                     const WideString& Value) {
-  Initial(JET_FIELD_FOCUS);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = (WideString*)&Value;
-}
-
-void CJS_EventHandler::OnField_Blur(bool bModifier,
-                                    bool bShift,
-                                    CPDF_FormField* pTarget,
-                                    const WideString& Value) {
-  Initial(JET_FIELD_BLUR);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = (WideString*)&Value;
-}
-
-void CJS_EventHandler::OnField_Keystroke(WideString& strChange,
-                                         const WideString& strChangeEx,
-                                         bool KeyDown,
-                                         bool bModifier,
-                                         int& nSelEnd,
-                                         int& nSelStart,
-                                         bool bShift,
-                                         CPDF_FormField* pTarget,
-                                         WideString& Value,
-                                         bool bWillCommit,
-                                         bool bFieldFull,
-                                         bool& bRc) {
-  Initial(JET_FIELD_KEYSTROKE);
-
-  m_nCommitKey = 0;
-  m_pWideStrChange = &strChange;
-  m_WideStrChangeEx = strChangeEx;
-  m_bKeyDown = KeyDown;
-  m_bModifier = bModifier;
-  m_pISelEnd = &nSelEnd;
-  m_pISelStart = &nSelStart;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = &Value;
-  m_bWillCommit = bWillCommit;
-  m_pbRc = &bRc;
-  m_bFieldFull = bFieldFull;
-}
-
-void CJS_EventHandler::OnField_Validate(WideString& strChange,
-                                        const WideString& strChangeEx,
-                                        bool bKeyDown,
-                                        bool bModifier,
-                                        bool bShift,
-                                        CPDF_FormField* pTarget,
-                                        WideString& Value,
-                                        bool& bRc) {
-  Initial(JET_FIELD_VALIDATE);
-
-  m_pWideStrChange = &strChange;
-  m_WideStrChangeEx = strChangeEx;
-  m_bKeyDown = bKeyDown;
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = &Value;
-  m_pbRc = &bRc;
-}
-
-void CJS_EventHandler::OnField_Calculate(CPDF_FormField* pSource,
-                                         CPDF_FormField* pTarget,
-                                         WideString& Value,
-                                         bool& bRc) {
-  Initial(JET_FIELD_CALCULATE);
-
-  if (pSource)
-    m_strSourceName = pSource->GetFullName();
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = &Value;
-  m_pbRc = &bRc;
-}
-
-void CJS_EventHandler::OnField_Format(CPDF_FormField* pTarget,
-                                      WideString& Value,
-                                      bool bWillCommit) {
-  Initial(JET_FIELD_FORMAT);
-
-  m_nCommitKey = 0;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = &Value;
-  m_bWillCommit = bWillCommit;
-}
-
-void CJS_EventHandler::OnScreen_Focus(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_FOCUS);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_Blur(bool bModifier,
-                                     bool bShift,
-                                     CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_BLUR);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_Open(bool bModifier,
-                                     bool bShift,
-                                     CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_OPEN);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_Close(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_CLOSE);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_MouseDown(bool bModifier,
-                                          bool bShift,
-                                          CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_MOUSEDOWN);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_MouseUp(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_MOUSEUP);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_MouseEnter(bool bModifier,
-                                           bool bShift,
-                                           CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_MOUSEENTER);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_MouseExit(bool bModifier,
-                                          bool bShift,
-                                          CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_MOUSEEXIT);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_InView(bool bModifier,
-                                       bool bShift,
-                                       CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_INVIEW);
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnScreen_OutView(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  Initial(JET_SCREEN_OUTVIEW);
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventHandler::OnLink_MouseUp(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
-  Initial(JET_LINK_MOUSEUP);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-}
-
-void CJS_EventHandler::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) {
-  Initial(JET_BOOKMARK_MOUSEUP);
-  m_pTargetBookMark = pBookMark;
-}
-
-void CJS_EventHandler::OnMenu_Exec(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv,
-    const WideString& strTargetName) {
-  Initial(JET_MENU_EXEC);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-  m_strTargetName = strTargetName;
-}
-
-void CJS_EventHandler::OnExternal_Exec() {
-  Initial(JET_EXTERNAL_EXEC);
-}
-
-void CJS_EventHandler::OnBatchExec(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
-  Initial(JET_BATCH_EXEC);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-}
-
-void CJS_EventHandler::OnConsole_Exec() {
-  Initial(JET_CONSOLE_EXEC);
-}
-
-void CJS_EventHandler::Initial(JS_EVENT_T type) {
-  m_eEventType = type;
-
-  m_strTargetName = L"";
-  m_strSourceName = L"";
-  m_pWideStrChange = nullptr;
-  m_WideStrChangeDu = L"";
-  m_WideStrChangeEx = L"";
-  m_nCommitKey = -1;
-  m_bKeyDown = false;
-  m_bModifier = false;
-  m_bShift = false;
-  m_pISelEnd = nullptr;
-  m_nSelEndDu = 0;
-  m_pISelStart = nullptr;
-  m_nSelStartDu = 0;
-  m_bWillCommit = false;
-  m_pValue = nullptr;
-  m_bFieldFull = false;
-  m_pbRc = nullptr;
-  m_bRcDu = false;
-
-  m_pTargetBookMark = nullptr;
-  m_pTargetFormFillEnv.Reset();
-  m_pTargetAnnot.Reset();
-
-  m_bValid = true;
-}
-
-void CJS_EventHandler::Destroy() {
-  m_bValid = false;
-}
-
-bool CJS_EventHandler::IsValid() const {
-  return m_bValid;
-}
-
-WideString& CJS_EventHandler::Change() {
-  if (m_pWideStrChange) {
-    return *m_pWideStrChange;
-  }
-  return m_WideStrChangeDu;
-}
-
-const WideString& CJS_EventHandler::ChangeEx() {
-  return m_WideStrChangeEx;
-}
-
-int CJS_EventHandler::CommitKey() const {
-  return m_nCommitKey;
-}
-
-bool CJS_EventHandler::FieldFull() const {
-  return m_bFieldFull;
-}
-
-bool CJS_EventHandler::KeyDown() const {
-  return m_bKeyDown;
-}
-
-bool CJS_EventHandler::Modifier() const {
-  return m_bModifier;
-}
-
-const wchar_t* CJS_EventHandler::Name() const {
-  switch (m_eEventType) {
-    case JET_APP_INIT:
-      return L"Init";
-    case JET_BATCH_EXEC:
-      return L"Exec";
-    case JET_BOOKMARK_MOUSEUP:
-      return L"Mouse Up";
-    case JET_CONSOLE_EXEC:
-      return L"Exec";
-    case JET_DOC_DIDPRINT:
-      return L"DidPrint";
-    case JET_DOC_DIDSAVE:
-      return L"DidSave";
-    case JET_DOC_OPEN:
-      return L"Open";
-    case JET_DOC_WILLCLOSE:
-      return L"WillClose";
-    case JET_DOC_WILLPRINT:
-      return L"WillPrint";
-    case JET_DOC_WILLSAVE:
-      return L"WillSave";
-    case JET_EXTERNAL_EXEC:
-      return L"Exec";
-    case JET_FIELD_FOCUS:
-    case JET_SCREEN_FOCUS:
-      return L"Focus";
-    case JET_FIELD_BLUR:
-    case JET_SCREEN_BLUR:
-      return L"Blur";
-    case JET_FIELD_MOUSEDOWN:
-    case JET_SCREEN_MOUSEDOWN:
-      return L"Mouse Down";
-    case JET_FIELD_MOUSEUP:
-    case JET_SCREEN_MOUSEUP:
-      return L"Mouse Up";
-    case JET_FIELD_MOUSEENTER:
-    case JET_SCREEN_MOUSEENTER:
-      return L"Mouse Enter";
-    case JET_FIELD_MOUSEEXIT:
-    case JET_SCREEN_MOUSEEXIT:
-      return L"Mouse Exit";
-    case JET_FIELD_CALCULATE:
-      return L"Calculate";
-    case JET_FIELD_FORMAT:
-      return L"Format";
-    case JET_FIELD_KEYSTROKE:
-      return L"Keystroke";
-    case JET_FIELD_VALIDATE:
-      return L"Validate";
-    case JET_LINK_MOUSEUP:
-      return L"Mouse Up";
-    case JET_MENU_EXEC:
-      return L"Exec";
-    case JET_PAGE_OPEN:
-    case JET_SCREEN_OPEN:
-      return L"Open";
-    case JET_PAGE_CLOSE:
-    case JET_SCREEN_CLOSE:
-      return L"Close";
-    case JET_SCREEN_INVIEW:
-    case JET_PAGE_INVIEW:
-      return L"InView";
-    case JET_PAGE_OUTVIEW:
-    case JET_SCREEN_OUTVIEW:
-      return L"OutView";
-    default:
-      return L"";
-  }
-}
-
-const wchar_t* CJS_EventHandler::Type() const {
-  switch (m_eEventType) {
-    case JET_APP_INIT:
-      return L"App";
-    case JET_BATCH_EXEC:
-      return L"Batch";
-    case JET_BOOKMARK_MOUSEUP:
-      return L"BookMark";
-    case JET_CONSOLE_EXEC:
-      return L"Console";
-    case JET_DOC_DIDPRINT:
-    case JET_DOC_DIDSAVE:
-    case JET_DOC_OPEN:
-    case JET_DOC_WILLCLOSE:
-    case JET_DOC_WILLPRINT:
-    case JET_DOC_WILLSAVE:
-      return L"Doc";
-    case JET_EXTERNAL_EXEC:
-      return L"External";
-    case JET_FIELD_BLUR:
-    case JET_FIELD_FOCUS:
-    case JET_FIELD_MOUSEDOWN:
-    case JET_FIELD_MOUSEENTER:
-    case JET_FIELD_MOUSEEXIT:
-    case JET_FIELD_MOUSEUP:
-    case JET_FIELD_CALCULATE:
-    case JET_FIELD_FORMAT:
-    case JET_FIELD_KEYSTROKE:
-    case JET_FIELD_VALIDATE:
-      return L"Field";
-    case JET_SCREEN_FOCUS:
-    case JET_SCREEN_BLUR:
-    case JET_SCREEN_OPEN:
-    case JET_SCREEN_CLOSE:
-    case JET_SCREEN_MOUSEDOWN:
-    case JET_SCREEN_MOUSEUP:
-    case JET_SCREEN_MOUSEENTER:
-    case JET_SCREEN_MOUSEEXIT:
-    case JET_SCREEN_INVIEW:
-    case JET_SCREEN_OUTVIEW:
-      return L"Screen";
-    case JET_LINK_MOUSEUP:
-      return L"Link";
-    case JET_MENU_EXEC:
-      return L"Menu";
-    case JET_PAGE_OPEN:
-    case JET_PAGE_CLOSE:
-    case JET_PAGE_INVIEW:
-    case JET_PAGE_OUTVIEW:
-      return L"Page";
-    default:
-      return L"";
-  }
-}
-
-bool& CJS_EventHandler::Rc() {
-  return m_pbRc ? *m_pbRc : m_bRcDu;
-}
-
-int CJS_EventHandler::SelEnd() const {
-  return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
-}
-
-int CJS_EventHandler::SelStart() const {
-  return m_pISelStart ? *m_pISelStart : m_nSelStartDu;
-}
-
-void CJS_EventHandler::SetSelEnd(int value) {
-  int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
-  target = value;
-}
-
-void CJS_EventHandler::SetSelStart(int value) {
-  int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu;
-  target = value;
-}
-
-bool CJS_EventHandler::Shift() const {
-  return m_bShift;
-}
-
-Field* CJS_EventHandler::Source() {
-  CJS_Runtime* pRuntime = m_pJSEventContext->GetJSRuntime();
-  v8::Local<v8::Object> pDocObj =
-      pRuntime->NewFxDynamicObj(CJS_Document::GetObjDefnID());
-  if (pDocObj.IsEmpty())
-    return nullptr;
-
-  v8::Local<v8::Object> pFieldObj =
-      pRuntime->NewFxDynamicObj(CJS_Field::GetObjDefnID());
-  if (pFieldObj.IsEmpty())
-    return nullptr;
-
-  CJS_Document* pJSDocument =
-      static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pDocObj));
-  CJS_Field* pJSField =
-      static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
-
-  Document* pDocument = static_cast<Document*>(pJSDocument->GetEmbedObject());
-  pDocument->SetFormFillEnv(m_pTargetFormFillEnv
-                                ? m_pTargetFormFillEnv.Get()
-                                : m_pJSEventContext->GetFormFillEnv());
-
-  Field* pField = static_cast<Field*>(pJSField->GetEmbedObject());
-  pField->AttachField(pDocument, m_strSourceName);
-  return pField;
-}
-
-Field* CJS_EventHandler::Target_Field() {
-  CJS_Runtime* pRuntime = m_pJSEventContext->GetJSRuntime();
-  v8::Local<v8::Object> pDocObj =
-      pRuntime->NewFxDynamicObj(CJS_Document::GetObjDefnID());
-  if (pDocObj.IsEmpty())
-    return nullptr;
-
-  v8::Local<v8::Object> pFieldObj =
-      pRuntime->NewFxDynamicObj(CJS_Field::GetObjDefnID());
-  if (pFieldObj.IsEmpty())
-    return nullptr;
-
-  CJS_Document* pJSDocument =
-      static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pDocObj));
-  CJS_Field* pJSField =
-      static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
-
-  Document* pDocument = static_cast<Document*>(pJSDocument->GetEmbedObject());
-  pDocument->SetFormFillEnv(m_pTargetFormFillEnv
-                                ? m_pTargetFormFillEnv.Get()
-                                : m_pJSEventContext->GetFormFillEnv());
-
-  Field* pField = static_cast<Field*>(pJSField->GetEmbedObject());
-  pField->AttachField(pDocument, m_strTargetName);
-  return pField;
-}
-
-WideString& CJS_EventHandler::Value() {
-  return *m_pValue;
-}
-
-bool CJS_EventHandler::WillCommit() const {
-  return m_bWillCommit;
-}
-
-const WideString& CJS_EventHandler::TargetName() const {
-  return m_strTargetName;
-}
diff --git a/fxjs/cjs_eventhandler.h b/fxjs/cjs_eventhandler.h
deleted file mode 100644
index 87ce1dc..0000000
--- a/fxjs/cjs_eventhandler.h
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_EVENTHANDLER_H_
-#define FXJS_CJS_EVENTHANDLER_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-
-class CJS_EventContext;
-class CPDFSDK_Annot;
-class CPDF_Bookmark;
-class CPDF_FormField;
-class Field;
-
-enum JS_EVENT_T {
-  JET_UNKNOWN,
-  JET_APP_INIT,
-  JET_DOC_OPEN,
-  JET_DOC_WILLPRINT,
-  JET_DOC_DIDPRINT,
-  JET_DOC_WILLSAVE,
-  JET_DOC_DIDSAVE,
-  JET_DOC_WILLCLOSE,
-  JET_PAGE_OPEN,
-  JET_PAGE_CLOSE,
-  JET_PAGE_INVIEW,
-  JET_PAGE_OUTVIEW,
-  JET_FIELD_MOUSEDOWN,
-  JET_FIELD_MOUSEUP,
-  JET_FIELD_MOUSEENTER,
-  JET_FIELD_MOUSEEXIT,
-  JET_FIELD_FOCUS,
-  JET_FIELD_BLUR,
-  JET_FIELD_KEYSTROKE,
-  JET_FIELD_VALIDATE,
-  JET_FIELD_CALCULATE,
-  JET_FIELD_FORMAT,
-  JET_SCREEN_FOCUS,
-  JET_SCREEN_BLUR,
-  JET_SCREEN_OPEN,
-  JET_SCREEN_CLOSE,
-  JET_SCREEN_MOUSEDOWN,
-  JET_SCREEN_MOUSEUP,
-  JET_SCREEN_MOUSEENTER,
-  JET_SCREEN_MOUSEEXIT,
-  JET_SCREEN_INVIEW,
-  JET_SCREEN_OUTVIEW,
-  JET_BATCH_EXEC,
-  JET_MENU_EXEC,
-  JET_CONSOLE_EXEC,
-  JET_EXTERNAL_EXEC,
-  JET_BOOKMARK_MOUSEUP,
-  JET_LINK_MOUSEUP
-};
-
-class CJS_EventHandler {
- public:
-  explicit CJS_EventHandler(CJS_EventContext* pContext);
-  virtual ~CJS_EventHandler();
-
-  void OnApp_Init();
-
-  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  const WideString& strTargetName);
-  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnField_Calculate(CPDF_FormField* pSource,
-                         CPDF_FormField* pTarget,
-                         WideString& Value,
-                         bool& bRc);
-  void OnField_Format(CPDF_FormField* pTarget,
-                      WideString& Value,
-                      bool bWillCommit);
-  void OnField_Keystroke(WideString& strChange,
-                         const WideString& strChangeEx,
-                         bool KeyDown,
-                         bool bModifier,
-                         int& nSelEnd,
-                         int& nSelStart,
-                         bool bShift,
-                         CPDF_FormField* pTarget,
-                         WideString& Value,
-                         bool bWillCommit,
-                         bool bFieldFull,
-                         bool& bRc);
-  void OnField_Validate(WideString& strChange,
-                        const WideString& strChangeEx,
-                        bool bKeyDown,
-                        bool bModifier,
-                        bool bShift,
-                        CPDF_FormField* pTarget,
-                        WideString& Value,
-                        bool& bRc);
-
-  void OnField_MouseDown(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseEnter(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseExit(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseUp(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_Blur(bool bModifier,
-                    bool bShift,
-                    CPDF_FormField* pTarget,
-                    const WideString& Value);
-  void OnField_Focus(bool bModifier,
-                     bool bShift,
-                     CPDF_FormField* pTarget,
-                     const WideString& Value);
-
-  void OnScreen_Focus(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Blur(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Open(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Close(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseDown(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseUp(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseEnter(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseExit(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_InView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_OutView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-
-  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark);
-  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   const WideString& strTargetName);
-  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnConsole_Exec();
-  void OnExternal_Exec();
-
-  void Initial(JS_EVENT_T type);
-  void Destroy();
-  bool IsValid() const;
-
-  WideString& Change();
-  const WideString& ChangeEx();
-  int CommitKey() const;
-  bool FieldFull() const;
-  bool KeyDown() const;
-  bool Modifier() const;
-  const wchar_t* Name() const;
-  const wchar_t* Type() const;
-  bool& Rc();
-  int SelEnd() const;
-  int SelStart() const;
-  void SetSelEnd(int value);
-  void SetSelStart(int value);
-  bool Shift() const;
-  Field* Source();
-  Field* Target_Field();
-  WideString& Value();
-  bool WillCommit() const;
-  const WideString& TargetName() const;
-
-  JS_EVENT_T EventType() const { return m_eEventType; }
-
-  UnownedPtr<CJS_EventContext> const m_pJSEventContext;
-  JS_EVENT_T m_eEventType;
-  bool m_bValid;
-
-  WideString m_strTargetName;
-  WideString m_strSourceName;
-  UnownedPtr<WideString> m_pWideStrChange;
-  WideString m_WideStrChangeDu;
-  WideString m_WideStrChangeEx;
-  int m_nCommitKey;
-  bool m_bKeyDown;
-  bool m_bModifier;
-  bool m_bShift;
-  int* m_pISelEnd;
-  int m_nSelEndDu;
-  int* m_pISelStart;
-  int m_nSelStartDu;
-  bool m_bWillCommit;
-  UnownedPtr<WideString> m_pValue;
-  bool m_bFieldFull;
-  bool* m_pbRc;
-  bool m_bRcDu;
-
-  UnownedPtr<CPDF_Bookmark> m_pTargetBookMark;
-  CPDFSDK_FormFillEnvironment::ObservedPtr m_pTargetFormFillEnv;
-  CPDFSDK_Annot::ObservedPtr m_pTargetAnnot;
-};
-
-#endif  // FXJS_CJS_EVENTHANDLER_H_
diff --git a/fxjs/cjs_eventrecorder.cpp b/fxjs/cjs_eventrecorder.cpp
new file mode 100644
index 0000000..59febf0
--- /dev/null
+++ b/fxjs/cjs_eventrecorder.cpp
@@ -0,0 +1,558 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/cjs_eventrecorder.h"
+
+#include "core/fpdfdoc/cpdf_bookmark.h"
+#include "core/fpdfdoc/cpdf_formfield.h"
+
+CJS_EventRecorder::CJS_EventRecorder() = default;
+
+CJS_EventRecorder::~CJS_EventRecorder() = default;
+
+void CJS_EventRecorder::OnApp_Init() {
+  Initialize(JET_APP_INIT);
+}
+
+void CJS_EventRecorder::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                                   const WideString& strTargetName) {
+  Initialize(JET_DOC_OPEN);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+  m_strTargetName = strTargetName;
+}
+
+void CJS_EventRecorder::OnDoc_WillPrint(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_DOC_WILLPRINT);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnDoc_DidPrint(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_DOC_DIDPRINT);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnDoc_WillSave(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_DOC_WILLSAVE);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnDoc_DidSave(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_DOC_DIDSAVE);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnDoc_WillClose(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_DOC_WILLCLOSE);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_PAGE_OPEN);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnPage_Close(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_PAGE_CLOSE);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnPage_InView(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_PAGE_INVIEW);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnPage_OutView(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+  Initialize(JET_PAGE_OUTVIEW);
+  m_pTargetFormFillEnv.Reset(pFormFillEnv);
+}
+
+void CJS_EventRecorder::OnField_MouseEnter(bool bModifier,
+                                           bool bShift,
+                                           CPDF_FormField* pTarget) {
+  Initialize(JET_FIELD_MOUSEENTER);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+
+  m_strTargetName = pTarget->GetFullName();
+}
+
+void CJS_EventRecorder::OnField_MouseExit(bool bModifier,
+                                          bool bShift,
+                                          CPDF_FormField* pTarget) {
+  Initialize(JET_FIELD_MOUSEEXIT);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+}
+
+void CJS_EventRecorder::OnField_MouseDown(bool bModifier,
+                                          bool bShift,
+                                          CPDF_FormField* pTarget) {
+  Initialize(JET_FIELD_MOUSEDOWN);
+  m_eEventType = JET_FIELD_MOUSEDOWN;
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+}
+
+void CJS_EventRecorder::OnField_MouseUp(bool bModifier,
+                                        bool bShift,
+                                        CPDF_FormField* pTarget) {
+  Initialize(JET_FIELD_MOUSEUP);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+}
+
+void CJS_EventRecorder::OnField_Focus(bool bModifier,
+                                      bool bShift,
+                                      CPDF_FormField* pTarget,
+                                      WideString* pValue) {
+  ASSERT(pValue);
+  Initialize(JET_FIELD_FOCUS);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+}
+
+void CJS_EventRecorder::OnField_Blur(bool bModifier,
+                                     bool bShift,
+                                     CPDF_FormField* pTarget,
+                                     WideString* pValue) {
+  ASSERT(pValue);
+  Initialize(JET_FIELD_BLUR);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+}
+
+void CJS_EventRecorder::OnField_Keystroke(WideString* strChange,
+                                          const WideString& strChangeEx,
+                                          bool KeyDown,
+                                          bool bModifier,
+                                          int* pSelEnd,
+                                          int* pSelStart,
+                                          bool bShift,
+                                          CPDF_FormField* pTarget,
+                                          WideString* pValue,
+                                          bool bWillCommit,
+                                          bool bFieldFull,
+                                          bool* pbRc) {
+  ASSERT(pValue);
+  ASSERT(pbRc);
+  ASSERT(pSelStart);
+  ASSERT(pSelEnd);
+
+  Initialize(JET_FIELD_KEYSTROKE);
+
+  m_nCommitKey = 0;
+  m_pWideStrChange = strChange;
+  m_WideStrChangeEx = strChangeEx;
+  m_bKeyDown = KeyDown;
+  m_bModifier = bModifier;
+  m_pISelEnd = pSelEnd;
+  m_pISelStart = pSelStart;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_bWillCommit = bWillCommit;
+  m_pbRc = pbRc;
+  m_bFieldFull = bFieldFull;
+}
+
+void CJS_EventRecorder::OnField_Validate(WideString* strChange,
+                                         const WideString& strChangeEx,
+                                         bool bKeyDown,
+                                         bool bModifier,
+                                         bool bShift,
+                                         CPDF_FormField* pTarget,
+                                         WideString* pValue,
+                                         bool* pbRc) {
+  ASSERT(pValue);
+  ASSERT(pbRc);
+
+  Initialize(JET_FIELD_VALIDATE);
+
+  m_pWideStrChange = strChange;
+  m_WideStrChangeEx = strChangeEx;
+  m_bKeyDown = bKeyDown;
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_pbRc = pbRc;
+}
+
+void CJS_EventRecorder::OnField_Calculate(CPDF_FormField* pSource,
+                                          CPDF_FormField* pTarget,
+                                          WideString* pValue,
+                                          bool* pRc) {
+  ASSERT(pValue);
+  ASSERT(pRc);
+
+  Initialize(JET_FIELD_CALCULATE);
+
+  if (pSource)
+    m_strSourceName = pSource->GetFullName();
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_pbRc = pRc;
+}
+
+void CJS_EventRecorder::OnField_Format(CPDF_FormField* pTarget,
+                                       WideString* pValue) {
+  ASSERT(pValue);
+  Initialize(JET_FIELD_FORMAT);
+
+  m_nCommitKey = 0;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_bWillCommit = true;
+}
+
+void CJS_EventRecorder::OnScreen_Focus(bool bModifier,
+                                       bool bShift,
+                                       CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_FOCUS);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_Blur(bool bModifier,
+                                      bool bShift,
+                                      CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_BLUR);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_Open(bool bModifier,
+                                      bool bShift,
+                                      CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_OPEN);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_Close(bool bModifier,
+                                       bool bShift,
+                                       CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_CLOSE);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_MouseDown(bool bModifier,
+                                           bool bShift,
+                                           CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_MOUSEDOWN);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_MouseUp(bool bModifier,
+                                         bool bShift,
+                                         CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_MOUSEUP);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_MouseEnter(bool bModifier,
+                                            bool bShift,
+                                            CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_MOUSEENTER);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_MouseExit(bool bModifier,
+                                           bool bShift,
+                                           CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_MOUSEEXIT);
+
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_InView(bool bModifier,
+                                        bool bShift,
+                                        CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_INVIEW);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnScreen_OutView(bool bModifier,
+                                         bool bShift,
+                                         CPDFSDK_Annot* pScreen) {
+  Initialize(JET_SCREEN_OUTVIEW);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_pTargetAnnot.Reset(pScreen);
+}
+
+void CJS_EventRecorder::OnLink_MouseUp(
+    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
+  Initialize(JET_LINK_MOUSEUP);
+  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
+}
+
+void CJS_EventRecorder::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) {
+  Initialize(JET_BOOKMARK_MOUSEUP);
+  m_pTargetBookMark = pBookMark;
+}
+
+void CJS_EventRecorder::OnMenu_Exec(
+    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv,
+    const WideString& strTargetName) {
+  Initialize(JET_MENU_EXEC);
+  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
+  m_strTargetName = strTargetName;
+}
+
+void CJS_EventRecorder::OnExternal_Exec() {
+  Initialize(JET_EXTERNAL_EXEC);
+}
+
+void CJS_EventRecorder::OnBatchExec(
+    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
+  Initialize(JET_BATCH_EXEC);
+  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
+}
+
+void CJS_EventRecorder::OnConsole_Exec() {
+  Initialize(JET_CONSOLE_EXEC);
+}
+
+void CJS_EventRecorder::Initialize(JS_EVENT_T type) {
+  m_eEventType = type;
+  m_strTargetName.clear();
+  m_strSourceName.clear();
+  m_pWideStrChange = nullptr;
+  m_WideStrChangeDu.clear();
+  m_WideStrChangeEx.clear();
+  m_nCommitKey = -1;
+  m_bKeyDown = false;
+  m_bModifier = false;
+  m_bShift = false;
+  m_pISelEnd = nullptr;
+  m_nSelEndDu = 0;
+  m_pISelStart = nullptr;
+  m_nSelStartDu = 0;
+  m_bWillCommit = false;
+  m_pValue = nullptr;
+  m_bFieldFull = false;
+  m_pbRc = nullptr;
+  m_bRcDu = false;
+  m_pTargetBookMark = nullptr;
+  m_pTargetFormFillEnv.Reset();
+  m_pTargetAnnot.Reset();
+  m_bValid = true;
+}
+
+void CJS_EventRecorder::Destroy() {
+  m_bValid = false;
+}
+
+bool CJS_EventRecorder::IsUserGesture() const {
+  switch (m_eEventType) {
+    case JET_FIELD_MOUSEDOWN:
+    case JET_FIELD_MOUSEUP:
+    case JET_SCREEN_MOUSEDOWN:
+    case JET_SCREEN_MOUSEUP:
+    case JET_BOOKMARK_MOUSEUP:
+    case JET_LINK_MOUSEUP:
+    case JET_FIELD_KEYSTROKE:
+      return true;
+    default:
+      return false;
+  }
+}
+
+WideString& CJS_EventRecorder::Change() {
+  return m_pWideStrChange ? *m_pWideStrChange : m_WideStrChangeDu;
+}
+
+ByteStringView CJS_EventRecorder::Name() const {
+  switch (m_eEventType) {
+    case JET_APP_INIT:
+      return "Init";
+    case JET_BATCH_EXEC:
+      return "Exec";
+    case JET_BOOKMARK_MOUSEUP:
+      return "Mouse Up";
+    case JET_CONSOLE_EXEC:
+      return "Exec";
+    case JET_DOC_DIDPRINT:
+      return "DidPrint";
+    case JET_DOC_DIDSAVE:
+      return "DidSave";
+    case JET_DOC_OPEN:
+      return "Open";
+    case JET_DOC_WILLCLOSE:
+      return "WillClose";
+    case JET_DOC_WILLPRINT:
+      return "WillPrint";
+    case JET_DOC_WILLSAVE:
+      return "WillSave";
+    case JET_EXTERNAL_EXEC:
+      return "Exec";
+    case JET_FIELD_FOCUS:
+    case JET_SCREEN_FOCUS:
+      return "Focus";
+    case JET_FIELD_BLUR:
+    case JET_SCREEN_BLUR:
+      return "Blur";
+    case JET_FIELD_MOUSEDOWN:
+    case JET_SCREEN_MOUSEDOWN:
+      return "Mouse Down";
+    case JET_FIELD_MOUSEUP:
+    case JET_SCREEN_MOUSEUP:
+      return "Mouse Up";
+    case JET_FIELD_MOUSEENTER:
+    case JET_SCREEN_MOUSEENTER:
+      return "Mouse Enter";
+    case JET_FIELD_MOUSEEXIT:
+    case JET_SCREEN_MOUSEEXIT:
+      return "Mouse Exit";
+    case JET_FIELD_CALCULATE:
+      return "Calculate";
+    case JET_FIELD_FORMAT:
+      return "Format";
+    case JET_FIELD_KEYSTROKE:
+      return "Keystroke";
+    case JET_FIELD_VALIDATE:
+      return "Validate";
+    case JET_LINK_MOUSEUP:
+      return "Mouse Up";
+    case JET_MENU_EXEC:
+      return "Exec";
+    case JET_PAGE_OPEN:
+    case JET_SCREEN_OPEN:
+      return "Open";
+    case JET_PAGE_CLOSE:
+    case JET_SCREEN_CLOSE:
+      return "Close";
+    case JET_SCREEN_INVIEW:
+    case JET_PAGE_INVIEW:
+      return "InView";
+    case JET_PAGE_OUTVIEW:
+    case JET_SCREEN_OUTVIEW:
+      return "OutView";
+    default:
+      return "";
+  }
+}
+
+ByteStringView CJS_EventRecorder::Type() const {
+  switch (m_eEventType) {
+    case JET_APP_INIT:
+      return "App";
+    case JET_BATCH_EXEC:
+      return "Batch";
+    case JET_BOOKMARK_MOUSEUP:
+      return "BookMark";
+    case JET_CONSOLE_EXEC:
+      return "Console";
+    case JET_DOC_DIDPRINT:
+    case JET_DOC_DIDSAVE:
+    case JET_DOC_OPEN:
+    case JET_DOC_WILLCLOSE:
+    case JET_DOC_WILLPRINT:
+    case JET_DOC_WILLSAVE:
+      return "Doc";
+    case JET_EXTERNAL_EXEC:
+      return "External";
+    case JET_FIELD_BLUR:
+    case JET_FIELD_FOCUS:
+    case JET_FIELD_MOUSEDOWN:
+    case JET_FIELD_MOUSEENTER:
+    case JET_FIELD_MOUSEEXIT:
+    case JET_FIELD_MOUSEUP:
+    case JET_FIELD_CALCULATE:
+    case JET_FIELD_FORMAT:
+    case JET_FIELD_KEYSTROKE:
+    case JET_FIELD_VALIDATE:
+      return "Field";
+    case JET_SCREEN_FOCUS:
+    case JET_SCREEN_BLUR:
+    case JET_SCREEN_OPEN:
+    case JET_SCREEN_CLOSE:
+    case JET_SCREEN_MOUSEDOWN:
+    case JET_SCREEN_MOUSEUP:
+    case JET_SCREEN_MOUSEENTER:
+    case JET_SCREEN_MOUSEEXIT:
+    case JET_SCREEN_INVIEW:
+    case JET_SCREEN_OUTVIEW:
+      return "Screen";
+    case JET_LINK_MOUSEUP:
+      return "Link";
+    case JET_MENU_EXEC:
+      return "Menu";
+    case JET_PAGE_OPEN:
+    case JET_PAGE_CLOSE:
+    case JET_PAGE_INVIEW:
+    case JET_PAGE_OUTVIEW:
+      return "Page";
+    default:
+      return "";
+  }
+}
+
+bool& CJS_EventRecorder::Rc() {
+  return m_pbRc ? *m_pbRc : m_bRcDu;
+}
+
+int CJS_EventRecorder::SelEnd() const {
+  return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
+}
+
+int CJS_EventRecorder::SelStart() const {
+  return m_pISelStart ? *m_pISelStart : m_nSelStartDu;
+}
+
+void CJS_EventRecorder::SetSelEnd(int value) {
+  int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
+  target = value;
+}
+
+void CJS_EventRecorder::SetSelStart(int value) {
+  int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu;
+  target = value;
+}
diff --git a/fxjs/cjs_eventrecorder.h b/fxjs/cjs_eventrecorder.h
new file mode 100644
index 0000000..acf9856
--- /dev/null
+++ b/fxjs/cjs_eventrecorder.h
@@ -0,0 +1,201 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_CJS_EVENTRECORDER_H_
+#define FXJS_CJS_EVENTRECORDER_H_
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/cpdfsdk_annot.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
+
+class CPDF_Bookmark;
+class CPDF_FormField;
+
+enum JS_EVENT_T {
+  JET_UNKNOWN,
+  JET_APP_INIT,
+  JET_DOC_OPEN,
+  JET_DOC_WILLPRINT,
+  JET_DOC_DIDPRINT,
+  JET_DOC_WILLSAVE,
+  JET_DOC_DIDSAVE,
+  JET_DOC_WILLCLOSE,
+  JET_PAGE_OPEN,
+  JET_PAGE_CLOSE,
+  JET_PAGE_INVIEW,
+  JET_PAGE_OUTVIEW,
+  JET_FIELD_MOUSEDOWN,
+  JET_FIELD_MOUSEUP,
+  JET_FIELD_MOUSEENTER,
+  JET_FIELD_MOUSEEXIT,
+  JET_FIELD_FOCUS,
+  JET_FIELD_BLUR,
+  JET_FIELD_KEYSTROKE,
+  JET_FIELD_VALIDATE,
+  JET_FIELD_CALCULATE,
+  JET_FIELD_FORMAT,
+  JET_SCREEN_FOCUS,
+  JET_SCREEN_BLUR,
+  JET_SCREEN_OPEN,
+  JET_SCREEN_CLOSE,
+  JET_SCREEN_MOUSEDOWN,
+  JET_SCREEN_MOUSEUP,
+  JET_SCREEN_MOUSEENTER,
+  JET_SCREEN_MOUSEEXIT,
+  JET_SCREEN_INVIEW,
+  JET_SCREEN_OUTVIEW,
+  JET_BATCH_EXEC,
+  JET_MENU_EXEC,
+  JET_CONSOLE_EXEC,
+  JET_EXTERNAL_EXEC,
+  JET_BOOKMARK_MOUSEUP,
+  JET_LINK_MOUSEUP
+};
+
+class CJS_EventRecorder {
+ public:
+  CJS_EventRecorder();
+  ~CJS_EventRecorder();
+
+  void OnApp_Init();
+
+  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                  const WideString& strTargetName);
+  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+
+  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+
+  void OnField_Calculate(CPDF_FormField* pSource,
+                         CPDF_FormField* pTarget,
+                         WideString* Value,
+                         bool* pbRc);
+  void OnField_Format(CPDF_FormField* pTarget, WideString* Value);
+  void OnField_Keystroke(WideString* strChange,
+                         const WideString& strChangeEx,
+                         bool KeyDown,
+                         bool bModifier,
+                         int* nSelEnd,
+                         int* nSelStart,
+                         bool bShift,
+                         CPDF_FormField* pTarget,
+                         WideString* Value,
+                         bool bWillCommit,
+                         bool bFieldFull,
+                         bool* bRc);
+  void OnField_Validate(WideString* strChange,
+                        const WideString& strChangeEx,
+                        bool bKeyDown,
+                        bool bModifier,
+                        bool bShift,
+                        CPDF_FormField* pTarget,
+                        WideString* Value,
+                        bool* bRc);
+  void OnField_MouseDown(bool bModifier, bool bShift, CPDF_FormField* pTarget);
+  void OnField_MouseEnter(bool bModifier, bool bShift, CPDF_FormField* pTarget);
+  void OnField_MouseExit(bool bModifier, bool bShift, CPDF_FormField* pTarget);
+  void OnField_MouseUp(bool bModifier, bool bShift, CPDF_FormField* pTarget);
+  void OnField_Blur(bool bModifier,
+                    bool bShift,
+                    CPDF_FormField* pTarget,
+                    WideString* Value);
+  void OnField_Focus(bool bModifier,
+                     bool bShift,
+                     CPDF_FormField* pTarget,
+                     WideString* Value);
+
+  void OnScreen_Focus(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_Blur(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_Open(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_Close(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_MouseDown(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_MouseUp(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_MouseEnter(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_MouseExit(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_InView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+  void OnScreen_OutView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
+
+  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark);
+  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+
+  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                   const WideString& strTargetName);
+  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  void OnConsole_Exec();
+  void OnExternal_Exec();
+
+  void Destroy();
+
+  JS_EVENT_T EventType() const { return m_eEventType; }
+  bool IsValid() const { return m_bValid; }
+  bool IsUserGesture() const;
+  WideString& Change();
+  WideString ChangeEx() const { return m_WideStrChangeEx; }
+  WideString SourceName() const { return m_strSourceName; }
+  WideString TargetName() const { return m_strTargetName; }
+  int CommitKey() const { return m_nCommitKey; }
+  bool FieldFull() const { return m_bFieldFull; }
+  bool KeyDown() const { return m_bKeyDown; }
+  bool Modifier() const { return m_bModifier; }
+  ByteStringView Name() const;
+  ByteStringView Type() const;
+  bool& Rc();
+  int SelEnd() const;
+  int SelStart() const;
+  void SetSelEnd(int value);
+  void SetSelStart(int value);
+  bool Shift() const { return m_bShift; }
+  bool HasValue() const { return !!m_pValue; }
+  WideString& Value() { return *m_pValue; }
+  bool WillCommit() const { return m_bWillCommit; }
+  CPDFSDK_FormFillEnvironment* GetFormFillEnvironment() const {
+    return m_pTargetFormFillEnv.Get();
+  }
+
+  void SetValueForTest(WideString* pStr) { m_pValue = pStr; }
+  void SetRCForTest(bool* pRC) { m_pbRc = pRC; }
+  void SetStrChangeForTest(WideString* pStrChange) {
+    m_pWideStrChange = pStrChange;
+  }
+  void ResetWillCommitForTest() { m_bWillCommit = false; }
+
+ private:
+  void Initialize(JS_EVENT_T type);
+
+  JS_EVENT_T m_eEventType = JET_UNKNOWN;
+  bool m_bValid = false;
+  UnownedPtr<WideString> m_pValue;
+  WideString m_strSourceName;
+  WideString m_strTargetName;
+  WideString m_WideStrChangeDu;
+  WideString m_WideStrChangeEx;
+  UnownedPtr<WideString> m_pWideStrChange;
+  int m_nCommitKey = -1;
+  bool m_bKeyDown = false;
+  bool m_bModifier = false;
+  bool m_bShift = false;
+  int m_nSelEndDu = 0;
+  int m_nSelStartDu = 0;
+  UnownedPtr<int> m_pISelEnd;
+  UnownedPtr<int> m_pISelStart;
+  bool m_bWillCommit = false;
+  bool m_bFieldFull = false;
+  bool m_bRcDu = false;
+  UnownedPtr<bool> m_pbRc;
+  UnownedPtr<CPDF_Bookmark> m_pTargetBookMark;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pTargetFormFillEnv;
+  ObservedPtr<CPDFSDK_Annot> m_pTargetAnnot;
+};
+
+#endif  // FXJS_CJS_EVENTRECORDER_H_
diff --git a/fxjs/cjs_field.cpp b/fxjs/cjs_field.cpp
index 0f06be0..b6aa9bb 100644
--- a/fxjs/cjs_field.cpp
+++ b/fxjs/cjs_field.cpp
@@ -8,11 +8,15 @@
 
 #include <algorithm>
 #include <memory>
+#include <utility>
 
-#include "core/fpdfapi/font/cpdf_font.h"
+#include "constants/annotation_flags.h"
+#include "constants/form_flags.h"
+#include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
-#include "core/fpdfdoc/cpdf_interform.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fxjs/cjs_color.h"
@@ -20,9 +24,157 @@
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_icon.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
+bool IsCheckBoxOrRadioButton(const CPDF_FormField* pFormField) {
+  return pFormField->GetFieldType() == FormFieldType::kCheckBox ||
+         pFormField->GetFieldType() == FormFieldType::kRadioButton;
+}
+
+bool IsComboBoxOrListBox(const CPDF_FormField* pFormField) {
+  return pFormField->GetFieldType() == FormFieldType::kComboBox ||
+         pFormField->GetFieldType() == FormFieldType::kListBox;
+}
+
+bool IsComboBoxOrTextField(const CPDF_FormField* pFormField) {
+  return pFormField->GetFieldType() == FormFieldType::kComboBox ||
+         pFormField->GetFieldType() == FormFieldType::kTextField;
+}
+
+void UpdateFormField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                     CPDF_FormField* pFormField,
+                     bool bChangeMark,
+                     bool bResetAP,
+                     bool bRefresh) {
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+
+  if (bResetAP) {
+    std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+    pForm->GetWidgets(pFormField, &widgets);
+
+    if (IsComboBoxOrTextField(pFormField)) {
+      for (auto& pObserved : widgets) {
+        if (pObserved) {
+          Optional<WideString> sValue =
+              ToCPDFSDKWidget(pObserved.Get())->OnFormat();
+          if (pObserved) {  // Not redundant, may be clobbered by OnFormat.
+            ToCPDFSDKWidget(pObserved.Get())->ResetAppearance(sValue, false);
+          }
+        }
+      }
+    } else {
+      for (auto& pObserved : widgets) {
+        if (pObserved)
+          ToCPDFSDKWidget(pObserved.Get())->ResetAppearance({}, false);
+      }
+    }
+  }
+
+  if (bRefresh) {
+    // Refresh the widget list. The calls in |bResetAP| may have caused widgets
+    // to be removed from the list. We need to call |GetWidgets| again to be
+    // sure none of the widgets have been deleted.
+    std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+    pForm->GetWidgets(pFormField, &widgets);
+
+    // TODO(dsinclair): Determine if all widgets share the same
+    // CPDFSDK_InteractiveForm. If that's the case, we can move the code to
+    // |GetFormFillEnv| out of the loop.
+    for (auto& pObserved : widgets) {
+      if (pObserved) {
+        CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pObserved.Get());
+        pWidget->GetInteractiveForm()->GetFormFillEnv()->UpdateAllViews(
+            nullptr, pWidget);
+      }
+    }
+  }
+
+  if (bChangeMark)
+    pFormFillEnv->SetChangeMark();
+}
+
+void UpdateFormControl(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                       CPDF_FormControl* pFormControl,
+                       bool bChangeMark,
+                       bool bResetAP,
+                       bool bRefresh) {
+  ASSERT(pFormControl);
+
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
+
+  if (pWidget) {
+    ObservedPtr<CPDFSDK_Widget> observed_widget(pWidget);
+    if (bResetAP) {
+      FormFieldType fieldType = pWidget->GetFieldType();
+      if (fieldType == FormFieldType::kComboBox ||
+          fieldType == FormFieldType::kTextField) {
+        Optional<WideString> sValue = pWidget->OnFormat();
+        if (!observed_widget)
+          return;
+        pWidget->ResetAppearance(sValue, false);
+      } else {
+        pWidget->ResetAppearance({}, false);
+      }
+      if (!observed_widget)
+        return;
+    }
+
+    if (bRefresh) {
+      CPDFSDK_InteractiveForm* pWidgetForm = pWidget->GetInteractiveForm();
+      pWidgetForm->GetFormFillEnv()->UpdateAllViews(nullptr, pWidget);
+    }
+  }
+
+  if (bChangeMark)
+    pFormFillEnv->SetChangeMark();
+}
+
+// note: iControlNo = -1, means not a widget.
+void ParseFieldName(const WideString& strFieldNameParsed,
+                    WideString& strFieldName,
+                    int& iControlNo) {
+  auto reverse_it = strFieldNameParsed.rbegin();
+  while (reverse_it != strFieldNameParsed.rend()) {
+    if (*reverse_it == L'.')
+      break;
+    ++reverse_it;
+  }
+  if (reverse_it == strFieldNameParsed.rend()) {
+    strFieldName = strFieldNameParsed;
+    iControlNo = -1;
+    return;
+  }
+  WideString suffixal =
+      strFieldNameParsed.Last(reverse_it - strFieldNameParsed.rbegin());
+  iControlNo = FXSYS_wtoi(suffixal.c_str());
+  if (iControlNo == 0) {
+    suffixal.TrimRight(L' ');
+    if (suffixal != L"0") {
+      strFieldName = strFieldNameParsed;
+      iControlNo = -1;
+      return;
+    }
+  }
+  strFieldName =
+      strFieldNameParsed.First(strFieldNameParsed.rend() - reverse_it - 1);
+}
+
+std::vector<CPDF_FormField*> GetFormFieldsForName(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv,
+    const WideString& csFieldName) {
+  std::vector<CPDF_FormField*> fields;
+  CPDFSDK_InteractiveForm* pReaderForm = pFormFillEnv->GetInteractiveForm();
+  CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm();
+  for (int i = 0, sz = pForm->CountFields(csFieldName); i < sz; ++i) {
+    if (CPDF_FormField* pFormField = pForm->GetField(i, csFieldName))
+      fields.push_back(pFormField);
+  }
+  return fields;
+}
+
 bool SetWidgetDisplayStatus(CPDFSDK_Widget* pWidget, int value) {
   if (!pWidget)
     return false;
@@ -30,26 +182,27 @@
   uint32_t dwFlag = pWidget->GetFlags();
   switch (value) {
     case 0:
-      dwFlag &= ~ANNOTFLAG_INVISIBLE;
-      dwFlag &= ~ANNOTFLAG_HIDDEN;
-      dwFlag &= ~ANNOTFLAG_NOVIEW;
-      dwFlag |= ANNOTFLAG_PRINT;
+      dwFlag &= ~pdfium::annotation_flags::kInvisible;
+      dwFlag &= ~pdfium::annotation_flags::kHidden;
+      dwFlag &= ~pdfium::annotation_flags::kNoView;
+      dwFlag |= pdfium::annotation_flags::kPrint;
       break;
     case 1:
-      dwFlag &= ~ANNOTFLAG_INVISIBLE;
-      dwFlag &= ~ANNOTFLAG_NOVIEW;
-      dwFlag |= (ANNOTFLAG_HIDDEN | ANNOTFLAG_PRINT);
+      dwFlag &= ~pdfium::annotation_flags::kInvisible;
+      dwFlag &= ~pdfium::annotation_flags::kNoView;
+      dwFlag |= (pdfium::annotation_flags::kHidden |
+                 pdfium::annotation_flags::kPrint);
       break;
     case 2:
-      dwFlag &= ~ANNOTFLAG_INVISIBLE;
-      dwFlag &= ~ANNOTFLAG_PRINT;
-      dwFlag &= ~ANNOTFLAG_HIDDEN;
-      dwFlag &= ~ANNOTFLAG_NOVIEW;
+      dwFlag &= ~pdfium::annotation_flags::kInvisible;
+      dwFlag &= ~pdfium::annotation_flags::kPrint;
+      dwFlag &= ~pdfium::annotation_flags::kHidden;
+      dwFlag &= ~pdfium::annotation_flags::kNoView;
       break;
     case 3:
-      dwFlag |= ANNOTFLAG_NOVIEW;
-      dwFlag |= ANNOTFLAG_PRINT;
-      dwFlag &= ~ANNOTFLAG_HIDDEN;
+      dwFlag |= pdfium::annotation_flags::kNoView;
+      dwFlag |= pdfium::annotation_flags::kPrint;
+      dwFlag &= ~pdfium::annotation_flags::kHidden;
       break;
   }
 
@@ -61,6 +214,284 @@
   return false;
 }
 
+void SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                    const WideString& swFieldName,
+                    int nControlIndex,
+                    const ByteString& string) {
+  ASSERT(pFormFillEnv);
+
+  BorderStyle nBorderStyle = BorderStyle::SOLID;
+  if (string == "solid")
+    nBorderStyle = BorderStyle::SOLID;
+  else if (string == "beveled")
+    nBorderStyle = BorderStyle::BEVELED;
+  else if (string == "dashed")
+    nBorderStyle = BorderStyle::DASH;
+  else if (string == "inset")
+    nBorderStyle = BorderStyle::INSET;
+  else if (string == "underline")
+    nBorderStyle = BorderStyle::UNDERLINE;
+  else
+    return;
+
+  std::vector<CPDF_FormField*> FieldArray =
+      GetFormFieldsForName(pFormFillEnv, swFieldName);
+  auto* pForm = pFormFillEnv->GetInteractiveForm();
+  for (CPDF_FormField* pFormField : FieldArray) {
+    if (nControlIndex < 0) {
+      bool bSet = false;
+      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
+        CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormField->GetControl(i));
+        if (pWidget) {
+          if (pWidget->GetBorderStyle() != nBorderStyle) {
+            pWidget->SetBorderStyle(nBorderStyle);
+            bSet = true;
+          }
+        }
+      }
+      if (bSet)
+        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+    } else {
+      if (nControlIndex >= pFormField->CountControls())
+        return;
+      if (CPDF_FormControl* pFormControl =
+              pFormField->GetControl(nControlIndex)) {
+        CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
+        if (pWidget) {
+          if (pWidget->GetBorderStyle() != nBorderStyle) {
+            pWidget->SetBorderStyle(nBorderStyle);
+            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+          }
+        }
+      }
+    }
+  }
+}
+
+void SetCurrentValueIndices(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                            const WideString& swFieldName,
+                            int nControlIndex,
+                            const std::vector<uint32_t>& array) {
+  ASSERT(pFormFillEnv);
+  std::vector<CPDF_FormField*> FieldArray =
+      GetFormFieldsForName(pFormFillEnv, swFieldName);
+
+  for (CPDF_FormField* pFormField : FieldArray) {
+    if (!IsComboBoxOrListBox(pFormField))
+      continue;
+
+    uint32_t dwFieldFlags = pFormField->GetFieldFlags();
+    pFormField->ClearSelection(NotificationOption::kNotify);
+    for (size_t i = 0; i < array.size(); ++i) {
+      if (i != 0 && !(dwFieldFlags & pdfium::form_flags::kChoiceMultiSelect))
+        break;
+      if (array[i] < static_cast<uint32_t>(pFormField->CountOptions()) &&
+          !pFormField->IsItemSelected(array[i])) {
+        pFormField->SetItemSelection(array[i], true,
+                                     NotificationOption::kDoNotNotify);
+      }
+    }
+    UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+  }
+}
+
+void SetDisplay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                const WideString& swFieldName,
+                int nControlIndex,
+                int number) {
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  std::vector<CPDF_FormField*> FieldArray =
+      GetFormFieldsForName(pFormFillEnv, swFieldName);
+  for (CPDF_FormField* pFormField : FieldArray) {
+    if (nControlIndex < 0) {
+      bool bAnySet = false;
+      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
+        CPDF_FormControl* pFormControl = pFormField->GetControl(i);
+        ASSERT(pFormControl);
+
+        CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
+        if (SetWidgetDisplayStatus(pWidget, number))
+          bAnySet = true;
+      }
+
+      if (bAnySet)
+        UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+    } else {
+      if (nControlIndex >= pFormField->CountControls())
+        return;
+
+      CPDF_FormControl* pFormControl = pFormField->GetControl(nControlIndex);
+      if (!pFormControl)
+        return;
+
+      CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
+      if (SetWidgetDisplayStatus(pWidget, number))
+        UpdateFormControl(pFormFillEnv, pFormControl, true, false, true);
+    }
+  }
+}
+
+void SetHidden(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+               const WideString& swFieldName,
+               int nControlIndex,
+               bool b) {
+  int display = b ? 1 /*Hidden*/ : 0 /*Visible*/;
+  SetDisplay(pFormFillEnv, swFieldName, nControlIndex, display);
+}
+
+void SetLineWidth(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                  const WideString& swFieldName,
+                  int nControlIndex,
+                  int number) {
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  std::vector<CPDF_FormField*> FieldArray =
+      GetFormFieldsForName(pFormFillEnv, swFieldName);
+  for (CPDF_FormField* pFormField : FieldArray) {
+    if (nControlIndex < 0) {
+      bool bSet = false;
+      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
+        CPDF_FormControl* pFormControl = pFormField->GetControl(i);
+        ASSERT(pFormControl);
+
+        if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
+          if (number != pWidget->GetBorderWidth()) {
+            pWidget->SetBorderWidth(number);
+            bSet = true;
+          }
+        }
+      }
+      if (bSet)
+        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+    } else {
+      if (nControlIndex >= pFormField->CountControls())
+        return;
+      if (CPDF_FormControl* pFormControl =
+              pFormField->GetControl(nControlIndex)) {
+        if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
+          if (number != pWidget->GetBorderWidth()) {
+            pWidget->SetBorderWidth(number);
+            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+          }
+        }
+      }
+    }
+  }
+}
+
+void SetRect(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+             const WideString& swFieldName,
+             int nControlIndex,
+             const CFX_FloatRect& rect) {
+  CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
+  std::vector<CPDF_FormField*> FieldArray =
+      GetFormFieldsForName(pFormFillEnv, swFieldName);
+  for (CPDF_FormField* pFormField : FieldArray) {
+    if (nControlIndex < 0) {
+      bool bSet = false;
+      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
+        CPDF_FormControl* pFormControl = pFormField->GetControl(i);
+        ASSERT(pFormControl);
+
+        if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
+          CFX_FloatRect crRect = rect;
+
+          CPDF_Page* pPDFPage = pWidget->GetPDFPage();
+          crRect.Intersect(pPDFPage->GetBBox());
+
+          if (!crRect.IsEmpty()) {
+            CFX_FloatRect rcOld = pWidget->GetRect();
+            if (crRect.left != rcOld.left || crRect.right != rcOld.right ||
+                crRect.top != rcOld.top || crRect.bottom != rcOld.bottom) {
+              pWidget->SetRect(crRect);
+              bSet = true;
+            }
+          }
+        }
+      }
+
+      if (bSet)
+        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+
+      continue;
+    }
+
+    if (nControlIndex >= pFormField->CountControls())
+      return;
+    if (CPDF_FormControl* pFormControl =
+            pFormField->GetControl(nControlIndex)) {
+      if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
+        CFX_FloatRect crRect = rect;
+        CPDF_Page* pPDFPage = pWidget->GetPDFPage();
+        crRect.Intersect(pPDFPage->GetBBox());
+        if (!crRect.IsEmpty()) {
+          CFX_FloatRect rcOld = pWidget->GetRect();
+          if (crRect.left != rcOld.left || crRect.right != rcOld.right ||
+              crRect.top != rcOld.top || crRect.bottom != rcOld.bottom) {
+            pWidget->SetRect(crRect);
+            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+          }
+        }
+      }
+    }
+  }
+}
+
+void SetFieldValue(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                   const WideString& swFieldName,
+                   int nControlIndex,
+                   const std::vector<WideString>& strArray) {
+  ASSERT(pFormFillEnv);
+  if (strArray.empty())
+    return;
+
+  std::vector<CPDF_FormField*> FieldArray =
+      GetFormFieldsForName(pFormFillEnv, swFieldName);
+
+  for (CPDF_FormField* pFormField : FieldArray) {
+    if (pFormField->GetFullName().Compare(swFieldName) != 0)
+      continue;
+
+    switch (pFormField->GetFieldType()) {
+      case FormFieldType::kTextField:
+      case FormFieldType::kComboBox:
+        if (pFormField->GetValue() != strArray[0]) {
+          pFormField->SetValue(strArray[0], NotificationOption::kNotify);
+          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+        }
+        break;
+      case FormFieldType::kCheckBox:
+      case FormFieldType::kRadioButton:
+        if (pFormField->GetValue() != strArray[0]) {
+          pFormField->SetValue(strArray[0], NotificationOption::kNotify);
+          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+        }
+        break;
+      case FormFieldType::kListBox: {
+        bool bModified = false;
+        for (const auto& str : strArray) {
+          if (!pFormField->IsItemSelected(pFormField->FindOption(str))) {
+            bModified = true;
+            break;
+          }
+        }
+        if (bModified) {
+          pFormField->ClearSelection(NotificationOption::kNotify);
+          for (const auto& str : strArray) {
+            int index = pFormField->FindOption(str);
+            if (!pFormField->IsItemSelected(index))
+              pFormField->SetItemSelection(index, true,
+                                           NotificationOption::kNotify);
+          }
+          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+}
+
 }  // namespace
 
 const JSPropertySpec CJS_Field::PropertySpecs[] = {
@@ -114,6 +545,7 @@
     {"richText", get_rich_text_static, set_rich_text_static},
     {"richValue", get_rich_value_static, set_rich_value_static},
     {"rotation", get_rotation_static, set_rotation_static},
+    {"source", get_source_static, set_source_static},
     {"strokeColor", get_stroke_color_static, set_stroke_color_static},
     {"style", get_style_static, set_style_static},
     {"submitName", get_submit_name_static, set_submit_name_static},
@@ -123,8 +555,7 @@
     {"type", get_type_static, set_type_static},
     {"userName", get_user_name_static, set_user_name_static},
     {"value", get_value_static, set_value_static},
-    {"valueAsString", get_value_as_string_static, set_value_as_string_static},
-    {"source", get_source_static, set_source_static}};
+    {"valueAsString", get_value_as_string_static, set_value_as_string_static}};
 
 const JSMethodSpec CJS_Field::MethodSpecs[] = {
     {"browseForFileToSubmit", browseForFileToSubmit_static},
@@ -155,6 +586,7 @@
     {"signatureValidate", signatureValidate_static}};
 
 int CJS_Field::ObjDefnID = -1;
+const char CJS_Field::kName[] = "Field";
 
 // static
 int CJS_Field::GetObjDefnID() {
@@ -163,72 +595,38 @@
 
 // static
 void CJS_Field::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID = pEngine->DefineObj("Field", FXJSOBJTYPE_DYNAMIC,
-                                 JSConstructor<CJS_Field, Field>,
-                                 JSDestructor<CJS_Field>);
-  DefineProps(pEngine, ObjDefnID, PropertySpecs, FX_ArraySize(PropertySpecs));
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_Field::kName, FXJSOBJTYPE_DYNAMIC,
+                                 JSConstructor<CJS_Field>, JSDestructor);
+  DefineProps(pEngine, ObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, ObjDefnID, MethodSpecs);
 }
 
-void CJS_Field::InitInstance(IJS_Runtime* pIRuntime) {}
+CJS_Field::CJS_Field(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-Field::Field(CJS_Object* pJSObject)
-    : CJS_EmbedObj(pJSObject),
-      m_pJSDoc(nullptr),
-      m_pFormFillEnv(nullptr),
-      m_nFormControlIndex(-1),
-      m_bCanSet(false),
-      m_bDelay(false) {}
+CJS_Field::~CJS_Field() = default;
 
-Field::~Field() {}
-
-// note: iControlNo = -1, means not a widget.
-void Field::ParseFieldName(const std::wstring& strFieldNameParsed,
-                           std::wstring& strFieldName,
-                           int& iControlNo) {
-  int iStart = strFieldNameParsed.find_last_of(L'.');
-  if (iStart == -1) {
-    strFieldName = strFieldNameParsed;
-    iControlNo = -1;
-    return;
-  }
-  std::wstring suffixal = strFieldNameParsed.substr(iStart + 1);
-  iControlNo = FXSYS_wtoi(suffixal.c_str());
-  if (iControlNo == 0) {
-    int iSpaceStart;
-    while ((iSpaceStart = suffixal.find_last_of(L" ")) != -1) {
-      suffixal.erase(iSpaceStart, 1);
-    }
-
-    if (suffixal.compare(L"0") != 0) {
-      strFieldName = strFieldNameParsed;
-      iControlNo = -1;
-      return;
-    }
-  }
-  strFieldName = strFieldNameParsed.substr(0, iStart);
-}
-
-bool Field::AttachField(Document* pDocument, const WideString& csFieldName) {
-  m_pJSDoc = pDocument;
+bool CJS_Field::AttachField(CJS_Document* pDocument,
+                            const WideString& csFieldName) {
+  m_pJSDoc.Reset(pDocument);
   m_pFormFillEnv.Reset(pDocument->GetFormFillEnv());
   m_bCanSet = m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM) ||
               m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
               m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY);
 
-  CPDFSDK_InterForm* pRDInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pInterForm = pRDInterForm->GetInterForm();
+  CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm();
+  CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm();
   WideString swFieldNameTemp = csFieldName;
   swFieldNameTemp.Replace(L"..", L".");
 
-  if (pInterForm->CountFields(swFieldNameTemp) <= 0) {
-    std::wstring strFieldName;
+  if (pForm->CountFields(swFieldNameTemp) <= 0) {
+    WideString strFieldName;
     int iControlNo = -1;
-    ParseFieldName(swFieldNameTemp.c_str(), strFieldName, iControlNo);
+    ParseFieldName(swFieldNameTemp, strFieldName, iControlNo);
     if (iControlNo == -1)
       return false;
 
-    m_FieldName = strFieldName.c_str();
+    m_FieldName = strFieldName;
     m_nFormControlIndex = iControlNo;
     return true;
   }
@@ -239,137 +637,16 @@
   return true;
 }
 
-std::vector<CPDF_FormField*> Field::GetFormFields(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv,
-    const WideString& csFieldName) {
-  std::vector<CPDF_FormField*> fields;
-  CPDFSDK_InterForm* pReaderInterForm = pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pInterForm = pReaderInterForm->GetInterForm();
-  for (int i = 0, sz = pInterForm->CountFields(csFieldName); i < sz; ++i) {
-    if (CPDF_FormField* pFormField = pInterForm->GetField(i, csFieldName))
-      fields.push_back(pFormField);
-  }
-  return fields;
+std::vector<CPDF_FormField*> CJS_Field::GetFormFields() const {
+  return GetFormFieldsForName(m_pFormFillEnv.Get(), m_FieldName);
 }
 
-std::vector<CPDF_FormField*> Field::GetFormFields(
-    const WideString& csFieldName) const {
-  return Field::GetFormFields(m_pFormFillEnv.Get(), csFieldName);
+CPDF_FormField* CJS_Field::GetFirstFormField() const {
+  std::vector<CPDF_FormField*> fields = GetFormFields();
+  return fields.empty() ? nullptr : fields[0];
 }
 
-void Field::UpdateFormField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                            CPDF_FormField* pFormField,
-                            bool bChangeMark,
-                            bool bResetAP,
-                            bool bRefresh) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-
-  if (bResetAP) {
-    std::vector<CPDFSDK_Annot::ObservedPtr> widgets;
-    pInterForm->GetWidgets(pFormField, &widgets);
-
-    FormFieldType fieldType = pFormField->GetFieldType();
-    if (fieldType == FormFieldType::kComboBox ||
-        fieldType == FormFieldType::kTextField) {
-      for (auto& pObserved : widgets) {
-        if (pObserved) {
-          bool bFormatted = false;
-          WideString sValue = static_cast<CPDFSDK_Widget*>(pObserved.Get())
-                                  ->OnFormat(bFormatted);
-          if (pObserved) {  // Not redundant, may be clobbered by OnFormat.
-            static_cast<CPDFSDK_Widget*>(pObserved.Get())
-                ->ResetAppearance(bFormatted ? &sValue : nullptr, false);
-          }
-        }
-      }
-    } else {
-      for (auto& pObserved : widgets) {
-        if (pObserved) {
-          static_cast<CPDFSDK_Widget*>(pObserved.Get())
-              ->ResetAppearance(nullptr, false);
-        }
-      }
-    }
-  }
-
-  if (bRefresh) {
-    // Refresh the widget list. The calls in |bResetAP| may have caused widgets
-    // to be removed from the list. We need to call |GetWidgets| again to be
-    // sure none of the widgets have been deleted.
-    std::vector<CPDFSDK_Annot::ObservedPtr> widgets;
-    pInterForm->GetWidgets(pFormField, &widgets);
-
-    // TODO(dsinclair): Determine if all widgets share the same
-    // CPDFSDK_InterForm. If that's the case, we can move the code to
-    // |GetFormFillEnv| out of the loop.
-    for (auto& pObserved : widgets) {
-      if (pObserved) {
-        CPDFSDK_Widget* pWidget = static_cast<CPDFSDK_Widget*>(pObserved.Get());
-        pWidget->GetInterForm()->GetFormFillEnv()->UpdateAllViews(nullptr,
-                                                                  pWidget);
-      }
-    }
-  }
-
-  if (bChangeMark)
-    pFormFillEnv->SetChangeMark();
-}
-
-void Field::UpdateFormControl(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                              CPDF_FormControl* pFormControl,
-                              bool bChangeMark,
-                              bool bResetAP,
-                              bool bRefresh) {
-  ASSERT(pFormControl);
-
-  CPDFSDK_InterForm* pForm = pFormFillEnv->GetInterForm();
-  CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
-
-  if (pWidget) {
-    CPDFSDK_Widget::ObservedPtr observed_widget(pWidget);
-    if (bResetAP) {
-      FormFieldType fieldType = pWidget->GetFieldType();
-      if (fieldType == FormFieldType::kComboBox ||
-          fieldType == FormFieldType::kTextField) {
-        bool bFormatted = false;
-        WideString sValue = pWidget->OnFormat(bFormatted);
-        if (!observed_widget)
-          return;
-        pWidget->ResetAppearance(bFormatted ? &sValue : nullptr, false);
-      } else {
-        pWidget->ResetAppearance(nullptr, false);
-      }
-      if (!observed_widget)
-        return;
-    }
-
-    if (bRefresh) {
-      CPDFSDK_InterForm* pInterForm = pWidget->GetInterForm();
-      pInterForm->GetFormFillEnv()->UpdateAllViews(nullptr, pWidget);
-    }
-  }
-
-  if (bChangeMark)
-    pFormFillEnv->SetChangeMark();
-}
-
-CPDFSDK_Widget* Field::GetWidget(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                 CPDF_FormControl* pFormControl) {
-  CPDFSDK_InterForm* pInterForm =
-      static_cast<CPDFSDK_InterForm*>(pFormFillEnv->GetInterForm());
-  return pInterForm ? pInterForm->GetWidget(pFormControl) : nullptr;
-}
-
-bool Field::ValueIsOccur(CPDF_FormField* pFormField, WideString csOptLabel) {
-  for (int i = 0, sz = pFormField->CountOptions(); i < sz; i++) {
-    if (csOptLabel.Compare(pFormField->GetOptionLabel(i)) == 0)
-      return true;
-  }
-
-  return false;
-}
-
-CPDF_FormControl* Field::GetSmartFieldControl(CPDF_FormField* pFormField) {
+CPDF_FormControl* CJS_Field::GetSmartFieldControl(CPDF_FormField* pFormField) {
   if (!pFormField->CountControls() ||
       m_nFormControlIndex >= pFormField->CountControls())
     return nullptr;
@@ -378,416 +655,359 @@
   return pFormField->GetControl(m_nFormControlIndex);
 }
 
-CJS_Return Field::get_alignment(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_alignment(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   switch (pFormControl->GetControlAlignment()) {
     case 0:
-      return CJS_Return(pRuntime->NewString(L"left"));
+      return CJS_Result::Success(pRuntime->NewString("left"));
     case 1:
-      return CJS_Return(pRuntime->NewString(L"center"));
+      return CJS_Result::Success(pRuntime->NewString("center"));
     case 2:
-      return CJS_Return(pRuntime->NewString(L"right"));
+      return CJS_Result::Success(pRuntime->NewString("right"));
   }
-  return CJS_Return(pRuntime->NewString(L""));
+  return CJS_Result::Success(pRuntime->NewString(""));
 }
 
-CJS_Return Field::set_alignment(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_alignment(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_border_style(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_border_style(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
+  CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_Widget* pWidget =
-      GetWidget(m_pFormFillEnv.Get(), GetSmartFieldControl(pFormField));
+  CPDFSDK_Widget* pWidget = m_pFormFillEnv->GetInteractiveForm()->GetWidget(
+      GetSmartFieldControl(pFormField));
   if (!pWidget)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   switch (pWidget->GetBorderStyle()) {
     case BorderStyle::SOLID:
-      return CJS_Return(pRuntime->NewString(L"solid"));
+      return CJS_Result::Success(pRuntime->NewString("solid"));
     case BorderStyle::DASH:
-      return CJS_Return(pRuntime->NewString(L"dashed"));
+      return CJS_Result::Success(pRuntime->NewString("dashed"));
     case BorderStyle::BEVELED:
-      return CJS_Return(pRuntime->NewString(L"beveled"));
+      return CJS_Result::Success(pRuntime->NewString("beveled"));
     case BorderStyle::INSET:
-      return CJS_Return(pRuntime->NewString(L"inset"));
+      return CJS_Result::Success(pRuntime->NewString("inset"));
     case BorderStyle::UNDERLINE:
-      return CJS_Return(pRuntime->NewString(L"underline"));
+      return CJS_Result::Success(pRuntime->NewString("underline"));
   }
-  return CJS_Return(pRuntime->NewString(L""));
+  return CJS_Result::Success(pRuntime->NewString(""));
 }
 
-CJS_Return Field::set_border_style(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_border_style(CJS_Runtime* pRuntime,
+                                       v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
-  ByteString byte_str = ByteString::FromUnicode(pRuntime->ToWideString(vp));
+  ByteString byte_str = pRuntime->ToWideString(vp).ToDefANSI();
   if (m_bDelay) {
     AddDelay_String(FP_BORDERSTYLE, byte_str);
   } else {
-    Field::SetBorderStyle(m_pFormFillEnv.Get(), m_FieldName,
-                          m_nFormControlIndex, byte_str);
+    SetBorderStyle(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
+                   byte_str);
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Field::SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                           const WideString& swFieldName,
-                           int nControlIndex,
-                           const ByteString& string) {
-  ASSERT(pFormFillEnv);
-
-  BorderStyle nBorderStyle = BorderStyle::SOLID;
-  if (string == "solid")
-    nBorderStyle = BorderStyle::SOLID;
-  else if (string == "beveled")
-    nBorderStyle = BorderStyle::BEVELED;
-  else if (string == "dashed")
-    nBorderStyle = BorderStyle::DASH;
-  else if (string == "inset")
-    nBorderStyle = BorderStyle::INSET;
-  else if (string == "underline")
-    nBorderStyle = BorderStyle::UNDERLINE;
-  else
-    return;
-
-  std::vector<CPDF_FormField*> FieldArray =
-      GetFormFields(pFormFillEnv, swFieldName);
-  for (CPDF_FormField* pFormField : FieldArray) {
-    if (nControlIndex < 0) {
-      bool bSet = false;
-      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
-        if (CPDFSDK_Widget* pWidget =
-                GetWidget(pFormFillEnv, pFormField->GetControl(i))) {
-          if (pWidget->GetBorderStyle() != nBorderStyle) {
-            pWidget->SetBorderStyle(nBorderStyle);
-            bSet = true;
-          }
-        }
-      }
-      if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
-    } else {
-      if (nControlIndex >= pFormField->CountControls())
-        return;
-      if (CPDF_FormControl* pFormControl =
-              pFormField->GetControl(nControlIndex)) {
-        if (CPDFSDK_Widget* pWidget = GetWidget(pFormFillEnv, pFormControl)) {
-          if (pWidget->GetBorderStyle() != nBorderStyle) {
-            pWidget->SetBorderStyle(nBorderStyle);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
-          }
-        }
-      }
-    }
-  }
-}
-
-CJS_Return Field::get_button_align_x(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_button_align_x(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_IconFit IconFit = pFormControl->GetIconFit();
-
-  float fLeft;
-  float fBottom;
-  IconFit.GetIconPosition(fLeft, fBottom);
-
-  return CJS_Return(pRuntime->NewNumber(static_cast<int32_t>(fLeft)));
+  CFX_PointF pos = IconFit.GetIconBottomLeftPosition();
+  return CJS_Result::Success(pRuntime->NewNumber(static_cast<int32_t>(pos.x)));
 }
 
-CJS_Return Field::set_button_align_x(CJS_Runtime* pRuntime,
-                                     v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_button_align_x(CJS_Runtime* pRuntime,
+                                         v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_button_align_y(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_button_align_y(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_IconFit IconFit = pFormControl->GetIconFit();
-
-  float fLeft;
-  float fBottom;
-  IconFit.GetIconPosition(fLeft, fBottom);
-
-  return CJS_Return(pRuntime->NewNumber(static_cast<int32_t>(fBottom)));
+  CFX_PointF pos = IconFit.GetIconBottomLeftPosition();
+  return CJS_Result::Success(pRuntime->NewNumber(static_cast<int32_t>(pos.y)));
 }
 
-CJS_Return Field::set_button_align_y(CJS_Runtime* pRuntime,
-                                     v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_button_align_y(CJS_Runtime* pRuntime,
+                                         v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_button_fit_bounds(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_button_fit_bounds(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(
+  return CJS_Result::Success(
       pRuntime->NewBoolean(pFormControl->GetIconFit().GetFittingBounds()));
 }
 
-CJS_Return Field::set_button_fit_bounds(CJS_Runtime* pRuntime,
-                                        v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_button_fit_bounds(CJS_Runtime* pRuntime,
+                                            v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_button_position(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_button_position(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(pRuntime->NewNumber(pFormControl->GetTextPosition()));
+  return CJS_Result::Success(
+      pRuntime->NewNumber(pFormControl->GetTextPosition()));
 }
 
-CJS_Return Field::set_button_position(CJS_Runtime* pRuntime,
-                                      v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_button_position(CJS_Runtime* pRuntime,
+                                          v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_button_scale_how(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_button_scale_how(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(pRuntime->NewBoolean(
+  return CJS_Result::Success(pRuntime->NewBoolean(
       pFormControl->GetIconFit().IsProportionalScale() ? 0 : 1));
 }
 
-CJS_Return Field::set_button_scale_how(CJS_Runtime* pRuntime,
-                                       v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_button_scale_how(CJS_Runtime* pRuntime,
+                                           v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_button_scale_when(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_button_scale_when(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_IconFit IconFit = pFormControl->GetIconFit();
   int ScaleM = IconFit.GetScaleMethod();
   switch (ScaleM) {
     case CPDF_IconFit::Always:
-      return CJS_Return(
+      return CJS_Result::Success(
           pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Always)));
     case CPDF_IconFit::Bigger:
-      return CJS_Return(
+      return CJS_Result::Success(
           pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Bigger)));
     case CPDF_IconFit::Never:
-      return CJS_Return(
+      return CJS_Result::Success(
           pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Never)));
     case CPDF_IconFit::Smaller:
-      return CJS_Return(
+      return CJS_Result::Success(
           pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Smaller)));
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::set_button_scale_when(CJS_Runtime* pRuntime,
-                                        v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_button_scale_when(CJS_Runtime* pRuntime,
+                                            v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_calc_order_index(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_calc_order_index(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kComboBox &&
-      pFormField->GetFieldType() != FormFieldType::kTextField) {
-    return CJS_Return(false);
-  }
+  if (!IsComboBoxOrTextField(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  CPDFSDK_InterForm* pRDInterForm = m_pFormFillEnv->GetInterForm();
-  CPDF_InterForm* pInterForm = pRDInterForm->GetInterForm();
-  return CJS_Return(pRuntime->NewNumber(static_cast<int32_t>(
-      pInterForm->FindFieldInCalculationOrder(pFormField))));
+  CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm();
+  CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm();
+  return CJS_Result::Success(pRuntime->NewNumber(
+      static_cast<int32_t>(pForm->FindFieldInCalculationOrder(pFormField))));
 }
 
-CJS_Return Field::set_calc_order_index(CJS_Runtime* pRuntime,
-                                       v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_calc_order_index(CJS_Runtime* pRuntime,
+                                           v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_char_limit(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_char_limit(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
-  return CJS_Return(
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
+  return CJS_Result::Success(
       pRuntime->NewNumber(static_cast<int32_t>(pFormField->GetMaxLen())));
 }
 
-CJS_Return Field::set_char_limit(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_char_limit(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_comb(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_comb(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(
-      pRuntime->NewBoolean(!!(pFormField->GetFieldFlags() & FIELDFLAG_COMB)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kTextComb)));
 }
 
-CJS_Return Field::set_comb(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_comb(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_commit_on_sel_change(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_commit_on_sel_change(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kComboBox &&
-      pFormField->GetFieldType() != FormFieldType::kListBox) {
-    return CJS_Return(false);
-  }
+  if (!IsComboBoxOrListBox(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_COMMITONSELCHANGE)));
+  uint32_t dwFieldFlags = pFormField->GetFieldFlags();
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(dwFieldFlags & pdfium::form_flags::kChoiceCommitOnSelChange)));
 }
 
-CJS_Return Field::set_commit_on_sel_change(CJS_Runtime* pRuntime,
-                                           v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_commit_on_sel_change(CJS_Runtime* pRuntime,
+                                               v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_current_value_indices(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_current_value_indices(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kComboBox &&
-      pFormField->GetFieldType() != FormFieldType::kListBox) {
-    return CJS_Return(false);
-  }
+  if (!IsComboBoxOrListBox(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   int count = pFormField->CountSelectedItems();
   if (count <= 0)
-    return CJS_Return(pRuntime->NewNumber(-1));
+    return CJS_Result::Success(pRuntime->NewNumber(-1));
   if (count == 1)
-    return CJS_Return(pRuntime->NewNumber(pFormField->GetSelectedIndex(0)));
+    return CJS_Result::Success(
+        pRuntime->NewNumber(pFormField->GetSelectedIndex(0)));
 
   v8::Local<v8::Array> SelArray = pRuntime->NewArray();
   for (int i = 0; i < count; i++) {
@@ -795,14 +1015,14 @@
         SelArray, i, pRuntime->NewNumber(pFormField->GetSelectedIndex(i)));
   }
   if (SelArray.IsEmpty())
-    return CJS_Return(pRuntime->NewArray());
-  return CJS_Return(SelArray);
+    return CJS_Result::Success(pRuntime->NewArray());
+  return CJS_Result::Success(SelArray);
 }
 
-CJS_Return Field::set_current_value_indices(CJS_Runtime* pRuntime,
-                                            v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_current_value_indices(CJS_Runtime* pRuntime,
+                                                v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   std::vector<uint32_t> array;
   if (vp->IsNumber()) {
@@ -818,115 +1038,91 @@
   if (m_bDelay) {
     AddDelay_WordArray(FP_CURRENTVALUEINDICES, array);
   } else {
-    Field::SetCurrentValueIndices(m_pFormFillEnv.Get(), m_FieldName,
-                                  m_nFormControlIndex, array);
+    SetCurrentValueIndices(m_pFormFillEnv.Get(), m_FieldName,
+                           m_nFormControlIndex, array);
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Field::SetCurrentValueIndices(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                   const WideString& swFieldName,
-                                   int nControlIndex,
-                                   const std::vector<uint32_t>& array) {
-  ASSERT(pFormFillEnv);
-  std::vector<CPDF_FormField*> FieldArray =
-      GetFormFields(pFormFillEnv, swFieldName);
-
-  for (CPDF_FormField* pFormField : FieldArray) {
-    FormFieldType fieldType = pFormField->GetFieldType();
-    if (fieldType == FormFieldType::kComboBox ||
-        fieldType == FormFieldType::kListBox) {
-      uint32_t dwFieldFlags = pFormField->GetFieldFlags();
-      pFormField->ClearSelection(true);
-      for (size_t i = 0; i < array.size(); ++i) {
-        if (i != 0 && !(dwFieldFlags & (1 << 21)))
-          break;
-        if (array[i] < static_cast<uint32_t>(pFormField->CountOptions()) &&
-            !pFormField->IsItemSelected(array[i])) {
-          pFormField->SetItemSelection(array[i], true);
-        }
-      }
-      UpdateFormField(pFormFillEnv, pFormField, true, true, true);
-    }
-  }
+CJS_Result CJS_Field::get_default_style(CJS_Runtime* pRuntime) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::get_default_style(CJS_Runtime* pRuntime) {
-  return CJS_Return(false);
+CJS_Result CJS_Field::set_default_style(CJS_Runtime* pRuntime,
+                                        v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::set_default_style(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return Field::get_default_value(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_default_value(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() == FormFieldType::kPushButton ||
       pFormField->GetFieldType() == FormFieldType::kSignature) {
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
 
-  return CJS_Return(pRuntime->NewString(pFormField->GetDefaultValue().c_str()));
+  return CJS_Result::Success(
+      pRuntime->NewString(pFormField->GetDefaultValue().AsStringView()));
 }
 
-CJS_Return Field::set_default_value(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_default_value(CJS_Runtime* pRuntime,
+                                        v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_do_not_scroll(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_do_not_scroll(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_DONOTSCROLL)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kTextDoNotScroll)));
 }
 
-CJS_Return Field::set_do_not_scroll(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_do_not_scroll(CJS_Runtime* pRuntime,
+                                        v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_do_not_spell_check(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_do_not_spell_check(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kTextField &&
-      pFormField->GetFieldType() != FormFieldType::kComboBox) {
-    return CJS_Return(false);
-  }
+  if (!IsComboBoxOrTextField(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_DONOTSPELLCHECK)));
+  uint32_t dwFieldFlags = pFormField->GetFieldFlags();
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(dwFieldFlags & pdfium::form_flags::kTextDoNotSpellCheck)));
 }
 
-CJS_Return Field::set_do_not_spell_check(CJS_Runtime* pRuntime,
-                                         v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_do_not_spell_check(CJS_Runtime* pRuntime,
+                                             v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-void Field::SetDelay(bool bDelay) {
+void CJS_Field::SetDelay(bool bDelay) {
   m_bDelay = bDelay;
 
   if (m_bDelay)
@@ -935,128 +1131,91 @@
     m_pJSDoc->DoFieldDelay(m_FieldName, m_nFormControlIndex);
 }
 
-CJS_Return Field::get_delay(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewBoolean(m_bDelay));
+CJS_Result CJS_Field::get_delay(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewBoolean(m_bDelay));
 }
 
-CJS_Return Field::set_delay(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_delay(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   SetDelay(pRuntime->ToBoolean(vp));
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_display(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_display(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  ASSERT(pFormField);
-
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDFSDK_Widget* pWidget =
-      pInterForm->GetWidget(GetSmartFieldControl(pFormField));
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(GetSmartFieldControl(pFormField));
   if (!pWidget)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   uint32_t dwFlag = pWidget->GetFlags();
-  if (ANNOTFLAG_INVISIBLE & dwFlag || ANNOTFLAG_HIDDEN & dwFlag)
-    return CJS_Return(pRuntime->NewNumber(1));
-
-  if (ANNOTFLAG_PRINT & dwFlag) {
-    if (ANNOTFLAG_NOVIEW & dwFlag)
-      return CJS_Return(pRuntime->NewNumber(3));
-    return CJS_Return(pRuntime->NewNumber(0));
+  if (pdfium::annotation_flags::kInvisible & dwFlag ||
+      pdfium::annotation_flags::kHidden & dwFlag) {
+    return CJS_Result::Success(pRuntime->NewNumber(1));
   }
-  return CJS_Return(pRuntime->NewNumber(2));
+
+  if (pdfium::annotation_flags::kPrint & dwFlag) {
+    if (pdfium::annotation_flags::kNoView & dwFlag)
+      return CJS_Result::Success(pRuntime->NewNumber(3));
+    return CJS_Result::Success(pRuntime->NewNumber(0));
+  }
+  return CJS_Result::Success(pRuntime->NewNumber(2));
 }
 
-CJS_Return Field::set_display(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_display(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   if (m_bDelay) {
     AddDelay_Int(FP_DISPLAY, pRuntime->ToInt32(vp));
   } else {
-    Field::SetDisplay(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
-                      pRuntime->ToInt32(vp));
+    SetDisplay(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
+               pRuntime->ToInt32(vp));
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Field::SetDisplay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                       const WideString& swFieldName,
-                       int nControlIndex,
-                       int number) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  std::vector<CPDF_FormField*> FieldArray =
-      GetFormFields(pFormFillEnv, swFieldName);
-  for (CPDF_FormField* pFormField : FieldArray) {
-    if (nControlIndex < 0) {
-      bool bAnySet = false;
-      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
-        CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
-
-        CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormControl);
-        if (SetWidgetDisplayStatus(pWidget, number))
-          bAnySet = true;
-      }
-
-      if (bAnySet)
-        UpdateFormField(pFormFillEnv, pFormField, true, false, true);
-    } else {
-      if (nControlIndex >= pFormField->CountControls())
-        return;
-
-      CPDF_FormControl* pFormControl = pFormField->GetControl(nControlIndex);
-      if (!pFormControl)
-        return;
-
-      CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormControl);
-      if (SetWidgetDisplayStatus(pWidget, number))
-        UpdateFormControl(pFormFillEnv, pFormControl, true, false, true);
-    }
-  }
+CJS_Result CJS_Field::get_doc(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(m_pJSDoc->ToV8Object());
 }
 
-CJS_Return Field::get_doc(CJS_Runtime* pRuntime) {
-  return CJS_Return(m_pJSDoc->GetCJSDoc()->ToV8Object());
+CJS_Result CJS_Field::set_doc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::set_doc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
+CJS_Result CJS_Field::get_editable(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-CJS_Return Field::get_editable(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kComboBox)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(
-      pRuntime->NewBoolean(!!(pFormField->GetFieldFlags() & FIELDFLAG_EDIT)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kChoiceEdit)));
 }
 
-CJS_Return Field::set_editable(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(m_bCanSet);
+CJS_Result CJS_Field::set_editable(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_export_values(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_export_values(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kCheckBox &&
-      pFormField->GetFieldType() != FormFieldType::kRadioButton) {
-    return CJS_Return(false);
-  }
+  if (!IsCheckBoxOrRadioButton(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   v8::Local<v8::Array> ExportValuesArray = pRuntime->NewArray();
   if (m_nFormControlIndex < 0) {
@@ -1064,74 +1223,77 @@
       CPDF_FormControl* pFormControl = pFormField->GetControl(i);
       pRuntime->PutArrayElement(
           ExportValuesArray, i,
-          pRuntime->NewString(pFormControl->GetExportValue().c_str()));
+          pRuntime->NewString(pFormControl->GetExportValue().AsStringView()));
     }
   } else {
     if (m_nFormControlIndex >= pFormField->CountControls())
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kValueError);
 
     CPDF_FormControl* pFormControl =
         pFormField->GetControl(m_nFormControlIndex);
     if (!pFormControl)
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
 
     pRuntime->PutArrayElement(
         ExportValuesArray, 0,
-        pRuntime->NewString(pFormControl->GetExportValue().c_str()));
+        pRuntime->NewString(pFormControl->GetExportValue().AsStringView()));
   }
-  return CJS_Return(ExportValuesArray);
+  return CJS_Result::Success(ExportValuesArray);
 }
 
-CJS_Return Field::set_export_values(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::set_export_values(CJS_Runtime* pRuntime,
+                                        v8::Local<v8::Value> vp) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kCheckBox &&
-      pFormField->GetFieldType() != FormFieldType::kRadioButton) {
-    return CJS_Return(false);
-  }
+  if (!IsCheckBoxOrRadioButton(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(m_bCanSet && !vp.IsEmpty() && vp->IsArray());
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+
+  if (vp.IsEmpty() || !vp->IsArray())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_file_select(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_file_select(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_FILESELECT)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kTextFileSelect)));
 }
 
-CJS_Return Field::set_file_select(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::set_file_select(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
-  return CJS_Return(m_bCanSet);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
+
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_fill_color(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_fill_color(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  ASSERT(pFormField);
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   int iColorType;
   pFormControl->GetBackgroundColor(iColorType);
@@ -1154,345 +1316,292 @@
                   pFormControl->GetOriginalBackgroundColor(2),
                   pFormControl->GetOriginalBackgroundColor(3));
   } else {
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kValueError);
   }
 
-  v8::Local<v8::Value> array = color::ConvertPWLColorToArray(pRuntime, color);
+  v8::Local<v8::Value> array =
+      CJS_Color::ConvertPWLColorToArray(pRuntime, color);
   if (array.IsEmpty())
-    return CJS_Return(pRuntime->NewArray());
-  return CJS_Return(array);
+    return CJS_Result::Success(pRuntime->NewArray());
+  return CJS_Result::Success(array);
 }
 
-CJS_Return Field::set_fill_color(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
+CJS_Result CJS_Field::set_fill_color(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
+  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
   if (FieldArray.empty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
   if (vp.IsEmpty() || !vp->IsArray())
-    return CJS_Return(false);
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_hidden(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_hidden(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  ASSERT(pFormField);
-
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDFSDK_Widget* pWidget =
-      pInterForm->GetWidget(GetSmartFieldControl(pFormField));
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(GetSmartFieldControl(pFormField));
   if (!pWidget)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   uint32_t dwFlags = pWidget->GetFlags();
-  return CJS_Return(pRuntime->NewBoolean(ANNOTFLAG_INVISIBLE & dwFlags ||
-                                         ANNOTFLAG_HIDDEN & dwFlags));
+  return CJS_Result::Success(
+      pRuntime->NewBoolean(pdfium::annotation_flags::kInvisible & dwFlags ||
+                           pdfium::annotation_flags::kHidden & dwFlags));
 }
 
-CJS_Return Field::set_hidden(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_hidden(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   if (m_bDelay) {
     AddDelay_Bool(FP_HIDDEN, pRuntime->ToBoolean(vp));
   } else {
-    Field::SetHidden(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
-                     pRuntime->ToBoolean(vp));
+    SetHidden(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
+              pRuntime->ToBoolean(vp));
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Field::SetHidden(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                      const WideString& swFieldName,
-                      int nControlIndex,
-                      bool b) {
-  int display = b ? 1 /*Hidden*/ : 0 /*Visible*/;
-  SetDisplay(pFormFillEnv, swFieldName, nControlIndex, display);
-}
-
-CJS_Return Field::get_highlight(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_highlight(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   int eHM = pFormControl->GetHighlightingMode();
   switch (eHM) {
     case CPDF_FormControl::None:
-      return CJS_Return(pRuntime->NewString(L"none"));
+      return CJS_Result::Success(pRuntime->NewString("none"));
     case CPDF_FormControl::Push:
-      return CJS_Return(pRuntime->NewString(L"push"));
+      return CJS_Result::Success(pRuntime->NewString("push"));
     case CPDF_FormControl::Invert:
-      return CJS_Return(pRuntime->NewString(L"invert"));
+      return CJS_Result::Success(pRuntime->NewString("invert"));
     case CPDF_FormControl::Outline:
-      return CJS_Return(pRuntime->NewString(L"outline"));
+      return CJS_Result::Success(pRuntime->NewString("outline"));
     case CPDF_FormControl::Toggle:
-      return CJS_Return(pRuntime->NewString(L"toggle"));
+      return CJS_Result::Success(pRuntime->NewString("toggle"));
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::set_highlight(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_highlight(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_line_width(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
-  ASSERT(pFormField);
+CJS_Result CJS_Field::get_line_width(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
   if (!pFormField->CountControls())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormField->GetControl(0));
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormField->GetControl(0));
   if (!pWidget)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(pRuntime->NewNumber(pWidget->GetBorderWidth()));
+  return CJS_Result::Success(pRuntime->NewNumber(pWidget->GetBorderWidth()));
 }
 
-CJS_Return Field::set_line_width(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_line_width(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   if (m_bDelay) {
     AddDelay_Int(FP_LINEWIDTH, pRuntime->ToInt32(vp));
   } else {
-    Field::SetLineWidth(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
-                        pRuntime->ToInt32(vp));
+    SetLineWidth(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
+                 pRuntime->ToInt32(vp));
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Field::SetLineWidth(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                         const WideString& swFieldName,
-                         int nControlIndex,
-                         int number) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  std::vector<CPDF_FormField*> FieldArray =
-      GetFormFields(pFormFillEnv, swFieldName);
-  for (CPDF_FormField* pFormField : FieldArray) {
-    if (nControlIndex < 0) {
-      bool bSet = false;
-      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
-        CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
-
-        if (CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormControl)) {
-          if (number != pWidget->GetBorderWidth()) {
-            pWidget->SetBorderWidth(number);
-            bSet = true;
-          }
-        }
-      }
-      if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
-    } else {
-      if (nControlIndex >= pFormField->CountControls())
-        return;
-      if (CPDF_FormControl* pFormControl =
-              pFormField->GetControl(nControlIndex)) {
-        if (CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormControl)) {
-          if (number != pWidget->GetBorderWidth()) {
-            pWidget->SetBorderWidth(number);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
-          }
-        }
-      }
-    }
-  }
-}
-
-CJS_Return Field::get_multiline(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_multiline(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
-
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_MULTILINE)));
-}
-
-CJS_Return Field::set_multiline(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
-}
-
-CJS_Return Field::get_multiple_selection(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kListBox)
-    return CJS_Return(false);
-
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_MULTISELECT)));
-}
-
-CJS_Return Field::set_multiple_selection(CJS_Runtime* pRuntime,
-                                         v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
-}
-
-CJS_Return Field::get_name(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  return CJS_Return(pRuntime->NewString(m_FieldName.c_str()));
-}
-
-CJS_Return Field::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return Field::get_num_items(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kComboBox &&
-      pFormField->GetFieldType() != FormFieldType::kListBox) {
-    return CJS_Return(false);
-  }
-
-  return CJS_Return(pRuntime->NewNumber(pFormField->CountOptions()));
-}
-
-CJS_Return Field::set_num_items(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
-}
-
-CJS_Return Field::get_page(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
+  CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  std::vector<CPDFSDK_Annot::ObservedPtr> widgets;
-  m_pFormFillEnv->GetInterForm()->GetWidgets(pFormField, &widgets);
+  if (pFormField->GetFieldType() != FormFieldType::kTextField)
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
+
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kTextMultiline)));
+}
+
+CJS_Result CJS_Field::set_multiline(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  ASSERT(m_pFormFillEnv);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Field::get_multiple_selection(CJS_Runtime* pRuntime) {
+  ASSERT(m_pFormFillEnv);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  if (pFormField->GetFieldType() != FormFieldType::kListBox)
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
+
+  uint32_t dwFieldFlags = pFormField->GetFieldFlags();
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(dwFieldFlags & pdfium::form_flags::kChoiceMultiSelect)));
+}
+
+CJS_Result CJS_Field::set_multiple_selection(CJS_Runtime* pRuntime,
+                                             v8::Local<v8::Value> vp) {
+  ASSERT(m_pFormFillEnv);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Field::get_name(CJS_Runtime* pRuntime) {
+  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
+  if (FieldArray.empty())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  return CJS_Result::Success(pRuntime->NewString(m_FieldName.AsStringView()));
+}
+
+CJS_Result CJS_Field::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_Field::get_num_items(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  if (!IsComboBoxOrListBox(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
+
+  return CJS_Result::Success(pRuntime->NewNumber(pFormField->CountOptions()));
+}
+
+CJS_Result CJS_Field::set_num_items(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_Field::get_page(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+  m_pFormFillEnv->GetInteractiveForm()->GetWidgets(pFormField, &widgets);
   if (widgets.empty())
-    return CJS_Return(pRuntime->NewNumber(-1));
+    return CJS_Result::Success(pRuntime->NewNumber(-1));
 
   v8::Local<v8::Array> PageArray = pRuntime->NewArray();
   int i = 0;
   for (const auto& pObserved : widgets) {
     if (!pObserved)
-      return CJS_Return(JSGetStringFromID(JSMessage::kBadObjectError));
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    auto* pWidget = static_cast<CPDFSDK_Widget*>(pObserved.Get());
+    auto* pWidget = ToCPDFSDKWidget(pObserved.Get());
     CPDFSDK_PageView* pPageView = pWidget->GetPageView();
     if (!pPageView)
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
 
     pRuntime->PutArrayElement(
         PageArray, i,
         pRuntime->NewNumber(static_cast<int32_t>(pPageView->GetPageIndex())));
     ++i;
   }
-  return CJS_Return(PageArray);
+  return CJS_Result::Success(PageArray);
 }
 
-CJS_Return Field::set_page(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(JSGetStringFromID(JSMessage::kReadOnlyError));
+CJS_Result CJS_Field::set_page(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
 
-CJS_Return Field::get_password(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_password(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_PASSWORD)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kTextPassword)));
 }
 
-CJS_Return Field::set_password(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_password(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_print(CJS_Runtime* pRuntime) {
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_print(CJS_Runtime* pRuntime) {
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  CPDFSDK_Widget* pWidget =
-      pInterForm->GetWidget(GetSmartFieldControl(pFormField));
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(GetSmartFieldControl(pFormField));
   if (!pWidget)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(
-      pRuntime->NewBoolean(!!(pWidget->GetFlags() & ANNOTFLAG_PRINT)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pWidget->GetFlags() & pdfium::annotation_flags::kPrint)));
 }
 
-CJS_Return Field::set_print(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
+CJS_Result CJS_Field::set_print(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
+  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
   if (FieldArray.empty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   for (CPDF_FormField* pFormField : FieldArray) {
     if (m_nFormControlIndex < 0) {
       bool bSet = false;
       for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
         if (CPDFSDK_Widget* pWidget =
-                pInterForm->GetWidget(pFormField->GetControl(i))) {
+                pForm->GetWidget(pFormField->GetControl(i))) {
           uint32_t dwFlags = pWidget->GetFlags();
           if (pRuntime->ToBoolean(vp))
-            dwFlags |= ANNOTFLAG_PRINT;
+            dwFlags |= pdfium::annotation_flags::kPrint;
           else
-            dwFlags &= ~ANNOTFLAG_PRINT;
+            dwFlags &= ~pdfium::annotation_flags::kPrint;
 
           if (dwFlags != pWidget->GetFlags()) {
             pWidget->SetFlags(dwFlags);
@@ -1508,16 +1617,16 @@
     }
 
     if (m_nFormControlIndex >= pFormField->CountControls())
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kValueError);
 
     if (CPDF_FormControl* pFormControl =
             pFormField->GetControl(m_nFormControlIndex)) {
-      if (CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormControl)) {
+      if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
         uint32_t dwFlags = pWidget->GetFlags();
         if (pRuntime->ToBoolean(vp))
-          dwFlags |= ANNOTFLAG_PRINT;
+          dwFlags |= pdfium::annotation_flags::kPrint;
         else
-          dwFlags &= ~ANNOTFLAG_PRINT;
+          dwFlags &= ~pdfium::annotation_flags::kPrint;
 
         if (dwFlags != pWidget->GetFlags()) {
           pWidget->SetFlags(dwFlags);
@@ -1528,57 +1637,60 @@
       }
     }
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_radios_in_unison(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_radios_in_unison(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kRadioButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_RADIOSINUNISON)));
+  uint32_t dwFieldFlags = pFormField->GetFieldFlags();
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(dwFieldFlags & pdfium::form_flags::kButtonRadiosInUnison)));
 }
 
-CJS_Return Field::set_radios_in_unison(CJS_Runtime* pRuntime,
-                                       v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
+CJS_Result CJS_Field::set_radios_in_unison(CJS_Runtime* pRuntime,
+                                           v8::Local<v8::Value> vp) {
+  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
   if (FieldArray.empty())
-    return CJS_Return(false);
-  return CJS_Return(m_bCanSet);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_readonly(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_readonly(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(FieldArray[0]->GetFieldFlags() & FIELDFLAG_READONLY)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kReadOnly)));
 }
 
-CJS_Return Field::set_readonly(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
+CJS_Result CJS_Field::set_readonly(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
   if (FieldArray.empty())
-    return CJS_Return(false);
-  return CJS_Return(m_bCanSet);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_rect(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_rect(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
-  CPDFSDK_Widget* pWidget =
-      pInterForm->GetWidget(GetSmartFieldControl(pFormField));
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(GetSmartFieldControl(pFormField));
   if (!pWidget)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CFX_FloatRect crRect = pWidget->GetRect();
   v8::Local<v8::Array> rcArray = pRuntime->NewArray();
@@ -1591,18 +1703,18 @@
   pRuntime->PutArrayElement(
       rcArray, 3, pRuntime->NewNumber(static_cast<int32_t>(crRect.bottom)));
 
-  return CJS_Return(rcArray);
+  return CJS_Result::Success(rcArray);
 }
 
-CJS_Return Field::set_rect(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_rect(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
   if (vp.IsEmpty() || !vp->IsArray())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kValueError);
 
   v8::Local<v8::Array> rcArray = pRuntime->ToArray(vp);
   if (pRuntime->GetArrayLength(rcArray) < 4)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kValueError);
 
   float pArray[4];
   pArray[0] = static_cast<float>(
@@ -1618,151 +1730,103 @@
   if (m_bDelay) {
     AddDelay_Rect(FP_RECT, crRect);
   } else {
-    Field::SetRect(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
-                   crRect);
+    SetRect(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex, crRect);
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Field::SetRect(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                    const WideString& swFieldName,
-                    int nControlIndex,
-                    const CFX_FloatRect& rect) {
-  CPDFSDK_InterForm* pInterForm = pFormFillEnv->GetInterForm();
-  std::vector<CPDF_FormField*> FieldArray =
-      GetFormFields(pFormFillEnv, swFieldName);
-  for (CPDF_FormField* pFormField : FieldArray) {
-    if (nControlIndex < 0) {
-      bool bSet = false;
-      for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
-        CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
+CJS_Result CJS_Field::get_required(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-        if (CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormControl)) {
-          CFX_FloatRect crRect = rect;
-
-          CPDF_Page* pPDFPage = pWidget->GetPDFPage();
-          crRect.Intersect(pPDFPage->GetPageBBox());
-
-          if (!crRect.IsEmpty()) {
-            CFX_FloatRect rcOld = pWidget->GetRect();
-            if (crRect.left != rcOld.left || crRect.right != rcOld.right ||
-                crRect.top != rcOld.top || crRect.bottom != rcOld.bottom) {
-              pWidget->SetRect(crRect);
-              bSet = true;
-            }
-          }
-        }
-      }
-
-      if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
-
-      continue;
-    }
-
-    if (nControlIndex >= pFormField->CountControls())
-      return;
-    if (CPDF_FormControl* pFormControl =
-            pFormField->GetControl(nControlIndex)) {
-      if (CPDFSDK_Widget* pWidget = pInterForm->GetWidget(pFormControl)) {
-        CFX_FloatRect crRect = rect;
-
-        CPDF_Page* pPDFPage = pWidget->GetPDFPage();
-        crRect.Intersect(pPDFPage->GetPageBBox());
-
-        if (!crRect.IsEmpty()) {
-          CFX_FloatRect rcOld = pWidget->GetRect();
-          if (crRect.left != rcOld.left || crRect.right != rcOld.right ||
-              crRect.top != rcOld.top || crRect.bottom != rcOld.bottom) {
-            pWidget->SetRect(crRect);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
-          }
-        }
-      }
-    }
-  }
-}
-
-CJS_Return Field::get_required(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() == FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_REQUIRED)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kRequired)));
 }
 
-CJS_Return Field::set_required(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
+CJS_Result CJS_Field::set_required(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
   if (FieldArray.empty())
-    return CJS_Return(false);
-  return CJS_Return(m_bCanSet);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_rich_text(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_rich_text(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kTextField)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      !!(pFormField->GetFieldFlags() & FIELDFLAG_RICHTEXT)));
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      !!(pFormField->GetFieldFlags() & pdfium::form_flags::kTextRichText)));
 }
 
-CJS_Return Field::set_rich_text(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_rich_text(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_rich_value(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Field::get_rich_value(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::set_rich_value(CJS_Runtime* pRuntime,
+CJS_Result CJS_Field::set_rich_value(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Field::get_rotation(CJS_Runtime* pRuntime) {
+  ASSERT(m_pFormFillEnv);
+
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
+  if (!pFormControl)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+
+  return CJS_Result::Success(pRuntime->NewNumber(pFormControl->GetRotation()));
+}
+
+CJS_Result CJS_Field::set_rotation(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp) {
+  ASSERT(m_pFormFillEnv);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Field::get_source(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJS_Field::set_source(CJS_Runtime* pRuntime,
                                  v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_rotation(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+CJS_Result CJS_Field::get_stroke_color(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
-
-  return CJS_Return(pRuntime->NewNumber(pFormControl->GetRotation()));
-}
-
-CJS_Return Field::set_rotation(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
-}
-
-CJS_Return Field::get_stroke_color(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
-  CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
-  if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   int iColorType;
   pFormControl->GetBorderColor(iColorType);
@@ -1783,45 +1847,44 @@
                       pFormControl->GetOriginalBorderColor(2),
                       pFormControl->GetOriginalBorderColor(3));
   } else {
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
 
-  v8::Local<v8::Value> array = color::ConvertPWLColorToArray(pRuntime, color);
+  v8::Local<v8::Value> array =
+      CJS_Color::ConvertPWLColorToArray(pRuntime, color);
   if (array.IsEmpty())
-    return CJS_Return(pRuntime->NewArray());
-  return CJS_Return(array);
+    return CJS_Result::Success(pRuntime->NewArray());
+  return CJS_Result::Success(array);
 }
 
-CJS_Return Field::set_stroke_color(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_stroke_color(CJS_Runtime* pRuntime,
+                                       v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
   if (vp.IsEmpty() || !vp->IsArray())
-    return CJS_Return(false);
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_style(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_style(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kRadioButton &&
-      pFormField->GetFieldType() != FormFieldType::kCheckBox) {
-    return CJS_Return(false);
-  }
+  if (!IsCheckBoxOrRadioButton(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   WideString csWCaption = pFormControl->GetNormalCaption();
-  ByteString csBCaption;
+  wchar_t selector = !csWCaption.IsEmpty() ? csWCaption[0] : L'4';
 
-  switch (csWCaption[0]) {
+  ByteString csBCaption;
+  switch (selector) {
     case L'l':
       csBCaption = "circle";
       break;
@@ -1841,193 +1904,194 @@
       csBCaption = "check";
       break;
   }
-  return CJS_Return(
-      pRuntime->NewString(WideString::FromLocal(csBCaption.c_str()).c_str()));
+  return CJS_Result::Success(pRuntime->NewString(
+      WideString::FromDefANSI(csBCaption.AsStringView()).AsStringView()));
 }
 
-CJS_Return Field::set_style(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_style(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_submit_name(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Field::get_submit_name(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::set_submit_name(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
+CJS_Result CJS_Field::set_submit_name(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_text_color(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_text_color(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  int iColorType;
+  Optional<CFX_Color::Type> iColorType;
   FX_ARGB color;
   CPDF_DefaultAppearance FieldAppearance = pFormControl->GetDefaultAppearance();
-  FieldAppearance.GetColor(color, iColorType);
+  std::tie(iColorType, color) = FieldAppearance.GetColor();
 
-  int32_t a;
-  int32_t r;
-  int32_t g;
-  int32_t b;
-  std::tie(a, r, g, b) = ArgbDecode(color);
-
-  CFX_Color crRet =
-      CFX_Color(CFX_Color::kRGB, r / 255.0f, g / 255.0f, b / 255.0f);
-
-  if (iColorType == CFX_Color::kTransparent)
+  CFX_Color crRet;
+  if (!iColorType || *iColorType == CFX_Color::kTransparent) {
     crRet = CFX_Color(CFX_Color::kTransparent);
+  } else {
+    int32_t a;
+    int32_t r;
+    int32_t g;
+    int32_t b;
+    std::tie(a, r, g, b) = ArgbDecode(color);
+    crRet = CFX_Color(CFX_Color::kRGB, r / 255.0f, g / 255.0f, b / 255.0f);
+  }
 
-  v8::Local<v8::Value> array = color::ConvertPWLColorToArray(pRuntime, crRet);
+  v8::Local<v8::Value> array =
+      CJS_Color::ConvertPWLColorToArray(pRuntime, crRet);
   if (array.IsEmpty())
-    return CJS_Return(pRuntime->NewArray());
-  return CJS_Return(array);
+    return CJS_Result::Success(pRuntime->NewArray());
+  return CJS_Result::Success(array);
 }
 
-CJS_Return Field::set_text_color(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_text_color(CJS_Runtime* pRuntime,
+                                     v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
   if (vp.IsEmpty() || !vp->IsArray())
-    return CJS_Return(false);
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_text_font(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_text_font(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  ASSERT(pFormField);
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   FormFieldType fieldType = pFormField->GetFieldType();
   if (fieldType != FormFieldType::kPushButton &&
       fieldType != FormFieldType::kComboBox &&
       fieldType != FormFieldType::kListBox &&
       fieldType != FormFieldType::kTextField) {
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
 
-  CPDF_Font* pFont = pFormControl->GetDefaultControlFont();
-  if (!pFont)
-    return CJS_Return(false);
+  Optional<WideString> wsFontName = pFormControl->GetDefaultControlFontName();
+  if (!wsFontName.has_value())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(pRuntime->NewString(
-      WideString::FromLocal(pFont->GetBaseFont().c_str()).c_str()));
+  return CJS_Result::Success(
+      pRuntime->NewString(wsFontName.value().AsStringView()));
 }
 
-CJS_Return Field::set_text_font(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_text_font(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
 
   if (!m_bCanSet)
-    return CJS_Return(false);
-  return CJS_Return(
-      !ByteString::FromUnicode(pRuntime->ToWideString(vp)).IsEmpty());
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  if (pRuntime->ToWideString(vp).ToDefANSI().IsEmpty())
+    return CJS_Result::Failure(JSMessage::kValueError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_text_size(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_text_size(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  ASSERT(pFormField);
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   float fFontSize;
   CPDF_DefaultAppearance FieldAppearance = pFormControl->GetDefaultAppearance();
   FieldAppearance.GetFont(&fFontSize);
-  return CJS_Return(pRuntime->NewNumber(static_cast<int>(fFontSize)));
+  return CJS_Result::Success(pRuntime->NewNumber(static_cast<int>(fFontSize)));
 }
 
-CJS_Return Field::set_text_size(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_text_size(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_type(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_type(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   switch (pFormField->GetFieldType()) {
     case FormFieldType::kUnknown:
-      return CJS_Return(pRuntime->NewString(L"unknown"));
+      return CJS_Result::Success(pRuntime->NewString("unknown"));
     case FormFieldType::kPushButton:
-      return CJS_Return(pRuntime->NewString(L"button"));
+      return CJS_Result::Success(pRuntime->NewString("button"));
     case FormFieldType::kCheckBox:
-      return CJS_Return(pRuntime->NewString(L"checkbox"));
+      return CJS_Result::Success(pRuntime->NewString("checkbox"));
     case FormFieldType::kRadioButton:
-      return CJS_Return(pRuntime->NewString(L"radiobutton"));
+      return CJS_Result::Success(pRuntime->NewString("radiobutton"));
     case FormFieldType::kComboBox:
-      return CJS_Return(pRuntime->NewString(L"combobox"));
+      return CJS_Result::Success(pRuntime->NewString("combobox"));
     case FormFieldType::kListBox:
-      return CJS_Return(pRuntime->NewString(L"listbox"));
+      return CJS_Result::Success(pRuntime->NewString("listbox"));
     case FormFieldType::kTextField:
-      return CJS_Return(pRuntime->NewString(L"text"));
+      return CJS_Result::Success(pRuntime->NewString("text"));
     case FormFieldType::kSignature:
-      return CJS_Return(pRuntime->NewString(L"signature"));
+      return CJS_Result::Success(pRuntime->NewString("signature"));
     default:
-      return CJS_Return(pRuntime->NewString(L"unknown"));
+      return CJS_Result::Success(pRuntime->NewString("unknown"));
   }
 }
 
-CJS_Return Field::set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Field::set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::get_user_name(CJS_Runtime* pRuntime) {
+CJS_Result CJS_Field::get_user_name(CJS_Runtime* pRuntime) {
   ASSERT(m_pFormFillEnv);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Return(
-      pRuntime->NewString(FieldArray[0]->GetAlternateName().c_str()));
+  return CJS_Result::Success(
+      pRuntime->NewString(pFormField->GetAlternateName().AsStringView()));
 }
 
-CJS_Return Field::set_user_name(CJS_Runtime* pRuntime,
-                                v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_user_name(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp) {
   ASSERT(m_pFormFillEnv);
-  return CJS_Return(m_bCanSet);
+  if (!m_bCanSet)
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::get_value(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::get_value(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   v8::Local<v8::Value> ret;
-
-  CPDF_FormField* pFormField = FieldArray[0];
   switch (pFormField->GetFieldType()) {
     case FormFieldType::kPushButton:
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kObjectTypeError);
     case FormFieldType::kComboBox:
     case FormFieldType::kTextField:
-      ret = pRuntime->NewString(pFormField->GetValue().c_str());
+      ret = pRuntime->NewString(pFormField->GetValue().AsStringView());
       break;
     case FormFieldType::kListBox: {
       if (pFormField->CountSelectedItems() > 1) {
@@ -2036,17 +2100,17 @@
         int iIndex;
         for (int i = 0, sz = pFormField->CountSelectedItems(); i < sz; i++) {
           iIndex = pFormField->GetSelectedIndex(i);
-          ElementValue =
-              pRuntime->NewString(pFormField->GetOptionValue(iIndex).c_str());
+          ElementValue = pRuntime->NewString(
+              pFormField->GetOptionValue(iIndex).AsStringView());
           if (wcslen(pRuntime->ToWideString(ElementValue).c_str()) == 0) {
-            ElementValue =
-                pRuntime->NewString(pFormField->GetOptionLabel(iIndex).c_str());
+            ElementValue = pRuntime->NewString(
+                pFormField->GetOptionLabel(iIndex).AsStringView());
           }
           pRuntime->PutArrayElement(ValueArray, i, ElementValue);
         }
         ret = ValueArray;
       } else {
-        ret = pRuntime->NewString(pFormField->GetValue().c_str());
+        ret = pRuntime->NewString(pFormField->GetValue().AsStringView());
       }
       break;
     }
@@ -2056,26 +2120,27 @@
       for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
         if (pFormField->GetControl(i)->IsChecked()) {
           ret = pRuntime->NewString(
-              pFormField->GetControl(i)->GetExportValue().c_str());
+              pFormField->GetControl(i)->GetExportValue().AsStringView());
           bFind = true;
           break;
         }
       }
       if (!bFind)
-        ret = pRuntime->NewString(L"Off");
+        ret = pRuntime->NewString("Off");
 
       break;
     }
     default:
-      ret = pRuntime->NewString(pFormField->GetValue().c_str());
+      ret = pRuntime->NewString(pFormField->GetValue().AsStringView());
       break;
   }
-  return CJS_Return(pRuntime->MaybeCoerceToNumber(ret));
+  return CJS_Result::Success(pRuntime->MaybeCoerceToNumber(ret));
 }
 
-CJS_Return Field::set_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+CJS_Result CJS_Field::set_value(CJS_Runtime* pRuntime,
+                                v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   std::vector<WideString> strArray;
   if (!vp.IsEmpty() && vp->IsArray()) {
@@ -2091,127 +2156,72 @@
   if (m_bDelay) {
     AddDelay_WideStringArray(FP_VALUE, strArray);
   } else {
-    Field::SetValue(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
-                    strArray);
+    SetFieldValue(m_pFormFillEnv.Get(), m_FieldName, m_nFormControlIndex,
+                  strArray);
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void Field::SetValue(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                     const WideString& swFieldName,
-                     int nControlIndex,
-                     const std::vector<WideString>& strArray) {
-  ASSERT(pFormFillEnv);
-  if (strArray.empty())
-    return;
+CJS_Result CJS_Field::get_value_as_string(CJS_Runtime* pRuntime) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  std::vector<CPDF_FormField*> FieldArray =
-      GetFormFields(pFormFillEnv, swFieldName);
-
-  for (CPDF_FormField* pFormField : FieldArray) {
-    if (pFormField->GetFullName().Compare(swFieldName) != 0)
-      continue;
-
-    switch (pFormField->GetFieldType()) {
-      case FormFieldType::kTextField:
-      case FormFieldType::kComboBox:
-        if (pFormField->GetValue() != strArray[0]) {
-          pFormField->SetValue(strArray[0], true);
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
-        }
-        break;
-      case FormFieldType::kCheckBox:
-      case FormFieldType::kRadioButton:
-        if (pFormField->GetValue() != strArray[0]) {
-          pFormField->SetValue(strArray[0], true);
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
-        }
-        break;
-      case FormFieldType::kListBox: {
-        bool bModified = false;
-        for (const auto& str : strArray) {
-          if (!pFormField->IsItemSelected(pFormField->FindOption(str))) {
-            bModified = true;
-            break;
-          }
-        }
-        if (bModified) {
-          pFormField->ClearSelection(true);
-          for (const auto& str : strArray) {
-            int index = pFormField->FindOption(str);
-            if (!pFormField->IsItemSelected(index))
-              pFormField->SetItemSelection(index, true, true);
-          }
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
-        }
-        break;
-      }
-      default:
-        break;
-    }
-  }
-}
-
-CJS_Return Field::get_value_as_string(CJS_Runtime* pRuntime) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() == FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   if (pFormField->GetFieldType() == FormFieldType::kCheckBox) {
     if (!pFormField->CountControls())
-      return CJS_Return(false);
-    return CJS_Return(pRuntime->NewString(
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
+    return CJS_Result::Success(pRuntime->NewString(
         pFormField->GetControl(0)->IsChecked() ? L"Yes" : L"Off"));
   }
 
   if (pFormField->GetFieldType() == FormFieldType::kRadioButton &&
-      !(pFormField->GetFieldFlags() & FIELDFLAG_RADIOSINUNISON)) {
+      !(pFormField->GetFieldFlags() &
+        pdfium::form_flags::kButtonRadiosInUnison)) {
     for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) {
       if (pFormField->GetControl(i)->IsChecked()) {
-        return CJS_Return(pRuntime->NewString(
-            pFormField->GetControl(i)->GetExportValue().c_str()));
+        return CJS_Result::Success(pRuntime->NewString(
+            pFormField->GetControl(i)->GetExportValue().AsStringView()));
       }
     }
-    return CJS_Return(pRuntime->NewString(L"Off"));
+    return CJS_Result::Success(pRuntime->NewString("Off"));
   }
 
   if (pFormField->GetFieldType() == FormFieldType::kListBox &&
       (pFormField->CountSelectedItems() > 1)) {
-    return CJS_Return(pRuntime->NewString(L""));
+    return CJS_Result::Success(pRuntime->NewString(""));
   }
-  return CJS_Return(pRuntime->NewString(pFormField->GetValue().c_str()));
+  return CJS_Result::Success(
+      pRuntime->NewString(pFormField->GetValue().AsStringView()));
 }
 
-CJS_Return Field::set_value_as_string(CJS_Runtime* pRuntime,
-                                      v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Field::set_value_as_string(CJS_Runtime* pRuntime,
+                                          v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::browseForFileToSubmit(
+CJS_Result CJS_Field::browseForFileToSubmit(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if ((pFormField->GetFieldFlags() & FIELDFLAG_FILESELECT) &&
+  if ((pFormField->GetFieldFlags() & pdfium::form_flags::kTextFileSelect) &&
       (pFormField->GetFieldType() == FormFieldType::kTextField)) {
     WideString wsFileName = m_pFormFillEnv->JS_fieldBrowse();
     if (!wsFileName.IsEmpty()) {
-      pFormField->SetValue(wsFileName);
+      pFormField->SetValue(wsFileName, NotificationOption::kDoNotNotify);
       UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true);
     }
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kObjectTypeError);
 }
 
-CJS_Return Field::buttonGetCaption(
+CJS_Result CJS_Field::buttonGetCaption(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   int nface = 0;
@@ -2219,163 +2229,156 @@
   if (iSize >= 1)
     nface = pRuntime->ToInt32(params[0]);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (nface == 0) {
-    return CJS_Return(
-        pRuntime->NewString(pFormControl->GetNormalCaption().c_str()));
-  } else if (nface == 1) {
-    return CJS_Return(
-        pRuntime->NewString(pFormControl->GetDownCaption().c_str()));
-  } else if (nface == 2) {
-    return CJS_Return(
-        pRuntime->NewString(pFormControl->GetRolloverCaption().c_str()));
+    return CJS_Result::Success(
+        pRuntime->NewString(pFormControl->GetNormalCaption().AsStringView()));
   }
-  return CJS_Return(false);
+  if (nface == 1) {
+    return CJS_Result::Success(
+        pRuntime->NewString(pFormControl->GetDownCaption().AsStringView()));
+  }
+  if (nface == 2) {
+    return CJS_Result::Success(
+        pRuntime->NewString(pFormControl->GetRolloverCaption().AsStringView()));
+  }
+  return CJS_Result::Failure(JSMessage::kValueError);
 }
 
-CJS_Return Field::buttonGetIcon(
+CJS_Result CJS_Field::buttonGetIcon(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() >= 1) {
     int nFace = pRuntime->ToInt32(params[0]);
     if (nFace < 0 || nFace > 2)
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kValueError);
   }
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (pFormField->GetFieldType() != FormFieldType::kPushButton)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
 
   CPDF_FormControl* pFormControl = GetSmartFieldControl(pFormField);
   if (!pFormControl)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  v8::Local<v8::Object> pObj =
-      pRuntime->NewFxDynamicObj(CJS_Icon::GetObjDefnID());
+  v8::Local<v8::Object> pObj = pRuntime->NewFXJSBoundObject(
+      CJS_Icon::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
   if (pObj.IsEmpty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_Icon* pJS_Icon = static_cast<CJS_Icon*>(pRuntime->GetObjectPrivate(pObj));
-  if (!pJS_Icon)
-    return CJS_Return(false);
-  return CJS_Return(pJS_Icon->ToV8Object());
+  auto* pJS_Icon = static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  return pJS_Icon ? CJS_Result::Success(pJS_Icon->ToV8Object())
+                  : CJS_Result::Failure(JSMessage::kBadObjectError);
 }
 
-CJS_Return Field::buttonImportIcon(
+CJS_Result CJS_Field::buttonImportIcon(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::buttonSetCaption(
+CJS_Result CJS_Field::buttonSetCaption(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::buttonSetIcon(
+CJS_Result CJS_Field::buttonSetIcon(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::checkThisBox(
+CJS_Result CJS_Field::checkThisBox(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   int iSize = params.size();
   if (iSize < 1)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   int nWidget = pRuntime->ToInt32(params[0]);
   bool bCheckit = true;
   if (iSize >= 2)
     bCheckit = pRuntime->ToBoolean(params[1]);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if (pFormField->GetFieldType() != FormFieldType::kCheckBox &&
-      pFormField->GetFieldType() != FormFieldType::kRadioButton) {
-    return CJS_Return(false);
-  }
+  if (!IsCheckBoxOrRadioButton(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
+
   if (nWidget < 0 || nWidget >= pFormField->CountControls())
-    return CJS_Return(false);
-  // TODO(weili): Check whether anything special needed for radio button,
-  // otherwise merge these branches.
-  if (pFormField->GetFieldType() == FormFieldType::kRadioButton)
-    pFormField->CheckControl(nWidget, bCheckit, true);
-  else
-    pFormField->CheckControl(nWidget, bCheckit, true);
+    return CJS_Result::Failure(JSMessage::kValueError);
 
+  // TODO(weili): Check whether anything special needed for radio button.
+  // (When pFormField->GetFieldType() == FormFieldType::kRadioButton.)
+  pFormField->CheckControl(nWidget, bCheckit, NotificationOption::kNotify);
   UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::clearItems(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_Field::clearItems(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::defaultIsChecked(
+CJS_Result CJS_Field::defaultIsChecked(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_bCanSet)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   int iSize = params.size();
   if (iSize < 1)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   int nWidget = pRuntime->ToInt32(params[0]);
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
-
-  CPDF_FormField* pFormField = FieldArray[0];
   if (nWidget < 0 || nWidget >= pFormField->CountControls())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      pFormField->GetFieldType() == FormFieldType::kCheckBox ||
-      pFormField->GetFieldType() == FormFieldType::kRadioButton));
+  return CJS_Result::Success(
+      pRuntime->NewBoolean(IsCheckBoxOrRadioButton(pFormField)));
 }
 
-CJS_Return Field::deleteItemAt(
+CJS_Result CJS_Field::deleteItemAt(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::getArray(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
+CJS_Result CJS_Field::getArray(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
   if (FieldArray.empty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   std::vector<std::unique_ptr<WideString>> swSort;
   for (CPDF_FormField* pFormField : FieldArray) {
-    swSort.push_back(
-        std::unique_ptr<WideString>(new WideString(pFormField->GetFullName())));
+    swSort.push_back(pdfium::MakeUnique<WideString>(pFormField->GetFullName()));
   }
 
   std::sort(swSort.begin(), swSort.end(),
@@ -2385,25 +2388,25 @@
   v8::Local<v8::Array> FormFieldArray = pRuntime->NewArray();
   int j = 0;
   for (const auto& pStr : swSort) {
-    v8::Local<v8::Object> pObj =
-        pRuntime->NewFxDynamicObj(CJS_Field::GetObjDefnID());
+    v8::Local<v8::Object> pObj = pRuntime->NewFXJSBoundObject(
+        CJS_Field::GetObjDefnID(), FXJSOBJTYPE_DYNAMIC);
     if (pObj.IsEmpty())
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    CJS_Field* pJSField =
-        static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pObj));
-    Field* pField = static_cast<Field*>(pJSField->GetEmbedObject());
-    pField->AttachField(m_pJSDoc, *pStr);
+    auto* pJSField =
+        static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pObj));
+    pJSField->AttachField(m_pJSDoc.Get(), *pStr);
     pRuntime->PutArrayElement(FormFieldArray, j++,
                               pJSField
                                   ? v8::Local<v8::Value>(pJSField->ToV8Object())
                                   : v8::Local<v8::Value>());
   }
-  return CJS_Return(FormFieldArray);
+  return CJS_Result::Success(FormFieldArray);
 }
 
-CJS_Return Field::getItemAt(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Field::getItemAt(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   int iSize = params.size();
   int nIdx = -1;
   if (iSize >= 1)
@@ -2413,113 +2416,107 @@
   if (iSize >= 2)
     bExport = pRuntime->ToBoolean(params[1]);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
-  if ((pFormField->GetFieldType() == FormFieldType::kListBox) ||
-      (pFormField->GetFieldType() == FormFieldType::kComboBox)) {
-    if (nIdx == -1 || nIdx > pFormField->CountOptions())
-      nIdx = pFormField->CountOptions() - 1;
-    if (bExport) {
-      WideString strval = pFormField->GetOptionValue(nIdx);
-      if (strval.IsEmpty()) {
-        return CJS_Return(
-            pRuntime->NewString(pFormField->GetOptionLabel(nIdx).c_str()));
-      }
-      return CJS_Return(pRuntime->NewString(strval.c_str()));
-    }
-    return CJS_Return(
-        pRuntime->NewString(pFormField->GetOptionLabel(nIdx).c_str()));
+  if (!IsComboBoxOrListBox(pFormField))
+    return CJS_Result::Failure(JSMessage::kObjectTypeError);
+
+  if (nIdx == -1 || nIdx > pFormField->CountOptions())
+    nIdx = pFormField->CountOptions() - 1;
+  if (!bExport) {
+    return CJS_Result::Success(
+        pRuntime->NewString(pFormField->GetOptionLabel(nIdx).AsStringView()));
   }
-  return CJS_Return(false);
+
+  WideString strval = pFormField->GetOptionValue(nIdx);
+  if (strval.IsEmpty()) {
+    return CJS_Result::Success(
+        pRuntime->NewString(pFormField->GetOptionLabel(nIdx).AsStringView()));
+  }
+  return CJS_Result::Success(pRuntime->NewString(strval.AsStringView()));
 }
 
-CJS_Return Field::getLock(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+CJS_Result CJS_Field::getLock(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::insertItemAt(
+CJS_Result CJS_Field::insertItemAt(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::isBoxChecked(
+CJS_Result CJS_Field::isBoxChecked(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   int nIndex = -1;
   if (params.size() >= 1)
     nIndex = pRuntime->ToInt32(params[0]);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (nIndex < 0 || nIndex >= pFormField->CountControls())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      ((pFormField->GetFieldType() == FormFieldType::kCheckBox ||
-        pFormField->GetFieldType() == FormFieldType::kRadioButton) &&
-       pFormField->GetControl(nIndex)->IsChecked() != 0)));
+  return CJS_Result::Success(
+      pRuntime->NewBoolean((IsCheckBoxOrRadioButton(pFormField) &&
+                            pFormField->GetControl(nIndex)->IsChecked() != 0)));
 }
 
-CJS_Return Field::isDefaultChecked(
+CJS_Result CJS_Field::isDefaultChecked(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   int nIndex = -1;
   if (params.size() >= 1)
     nIndex = pRuntime->ToInt32(params[0]);
 
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   if (nIndex < 0 || nIndex >= pFormField->CountControls())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  return CJS_Return(pRuntime->NewBoolean(
-      ((pFormField->GetFieldType() == FormFieldType::kCheckBox ||
-        pFormField->GetFieldType() == FormFieldType::kRadioButton) &&
+  return CJS_Result::Success(pRuntime->NewBoolean(
+      (IsCheckBoxOrRadioButton(pFormField) &&
        pFormField->GetControl(nIndex)->IsDefaultChecked() != 0)));
 }
 
-CJS_Return Field::setAction(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+CJS_Result CJS_Field::setAction(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::setFocus(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields(m_FieldName);
-  if (FieldArray.empty())
-    return CJS_Return(false);
+CJS_Result CJS_Field::setFocus(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_FormField* pFormField = FieldArray[0];
   int32_t nCount = pFormField->CountControls();
   if (nCount < 1)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_InterForm* pInterForm = m_pFormFillEnv->GetInterForm();
+  CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm();
   CPDFSDK_Widget* pWidget = nullptr;
   if (nCount == 1) {
-    pWidget = pInterForm->GetWidget(pFormField->GetControl(0));
+    pWidget = pForm->GetWidget(pFormField->GetControl(0));
   } else {
-    UnderlyingPageType* pPage =
-        UnderlyingFromFPDFPage(m_pFormFillEnv->GetCurrentPage(
-            m_pFormFillEnv->GetUnderlyingDocument()));
+    IPDF_Page* pPage = IPDFPageFromFPDFPage(m_pFormFillEnv->GetCurrentPage());
     if (!pPage)
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kBadObjectError);
     if (CPDFSDK_PageView* pCurPageView =
             m_pFormFillEnv->GetPageView(pPage, true)) {
       for (int32_t i = 0; i < nCount; i++) {
         if (CPDFSDK_Widget* pTempWidget =
-                pInterForm->GetWidget(pFormField->GetControl(i))) {
+                pForm->GetWidget(pFormField->GetControl(i))) {
           if (pTempWidget->GetPDFPage() == pCurPageView->GetPDFPage()) {
             pWidget = pTempWidget;
             break;
@@ -2530,142 +2527,135 @@
   }
 
   if (pWidget) {
-    CPDFSDK_Annot::ObservedPtr pObserved(pWidget);
+    ObservedPtr<CPDFSDK_Annot> pObserved(pWidget);
     m_pFormFillEnv->SetFocusAnnot(&pObserved);
   }
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::setItems(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
-}
-
-CJS_Return Field::setLock(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
-}
-
-CJS_Return Field::signatureGetModifications(
+CJS_Result CJS_Field::setItems(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Success();
 }
 
-CJS_Return Field::signatureGetSeedValue(
+CJS_Result CJS_Field::setLock(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
+}
+
+CJS_Result CJS_Field::signatureGetModifications(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::signatureInfo(
+CJS_Result CJS_Field::signatureGetSeedValue(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::signatureSetSeedValue(
+CJS_Result CJS_Field::signatureInfo(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::signatureSign(
+CJS_Result CJS_Field::signatureSetSeedValue(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::signatureValidate(
+CJS_Result CJS_Field::signatureSign(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::get_source(CJS_Runtime* pRuntime) {
-  return CJS_Return(true);
+CJS_Result CJS_Field::signatureValidate(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
-CJS_Return Field::set_source(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(true);
-}
-
-void Field::AddDelay_Int(FIELD_PROP prop, int32_t n) {
-  CJS_DelayData* pNewData =
-      new CJS_DelayData(prop, m_nFormControlIndex, m_FieldName);
+void CJS_Field::AddDelay_Int(FIELD_PROP prop, int32_t n) {
+  auto pNewData =
+      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->num = n;
-  m_pJSDoc->AddDelayData(pNewData);
+  m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
-void Field::AddDelay_Bool(FIELD_PROP prop, bool b) {
-  CJS_DelayData* pNewData =
-      new CJS_DelayData(prop, m_nFormControlIndex, m_FieldName);
+void CJS_Field::AddDelay_Bool(FIELD_PROP prop, bool b) {
+  auto pNewData =
+      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->b = b;
-  m_pJSDoc->AddDelayData(pNewData);
+  m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
-void Field::AddDelay_String(FIELD_PROP prop, const ByteString& string) {
-  CJS_DelayData* pNewData =
-      new CJS_DelayData(prop, m_nFormControlIndex, m_FieldName);
-  pNewData->string = string;
-  m_pJSDoc->AddDelayData(pNewData);
+void CJS_Field::AddDelay_String(FIELD_PROP prop, const ByteString& str) {
+  auto pNewData =
+      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+  pNewData->bytestring = str;
+  m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
-void Field::AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect) {
-  CJS_DelayData* pNewData =
-      new CJS_DelayData(prop, m_nFormControlIndex, m_FieldName);
+void CJS_Field::AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect) {
+  auto pNewData =
+      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->rect = rect;
-  m_pJSDoc->AddDelayData(pNewData);
+  m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
-void Field::AddDelay_WordArray(FIELD_PROP prop,
-                               const std::vector<uint32_t>& array) {
-  CJS_DelayData* pNewData =
-      new CJS_DelayData(prop, m_nFormControlIndex, m_FieldName);
+void CJS_Field::AddDelay_WordArray(FIELD_PROP prop,
+                                   const std::vector<uint32_t>& array) {
+  auto pNewData =
+      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->wordarray = array;
-  m_pJSDoc->AddDelayData(pNewData);
+  m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
-void Field::AddDelay_WideStringArray(FIELD_PROP prop,
-                                     const std::vector<WideString>& array) {
-  CJS_DelayData* pNewData =
-      new CJS_DelayData(prop, m_nFormControlIndex, m_FieldName);
+void CJS_Field::AddDelay_WideStringArray(FIELD_PROP prop,
+                                         const std::vector<WideString>& array) {
+  auto pNewData =
+      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->widestringarray = array;
-  m_pJSDoc->AddDelayData(pNewData);
+  m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
-void Field::DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                    CJS_DelayData* pData) {
+void CJS_Field::DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
+                        CJS_DelayData* pData) {
   ASSERT(pFormFillEnv);
   switch (pData->eProp) {
     case FP_BORDERSTYLE:
-      Field::SetBorderStyle(pFormFillEnv, pData->sFieldName,
-                            pData->nControlIndex, pData->string);
+      SetBorderStyle(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
+                     pData->bytestring);
       break;
     case FP_CURRENTVALUEINDICES:
-      Field::SetCurrentValueIndices(pFormFillEnv, pData->sFieldName,
-                                    pData->nControlIndex, pData->wordarray);
+      SetCurrentValueIndices(pFormFillEnv, pData->sFieldName,
+                             pData->nControlIndex, pData->wordarray);
       break;
     case FP_DISPLAY:
-      Field::SetDisplay(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
-                        pData->num);
+      SetDisplay(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
+                 pData->num);
       break;
     case FP_HIDDEN:
-      Field::SetHidden(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
-                       pData->b);
+      SetHidden(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
+                pData->b);
       break;
     case FP_LINEWIDTH:
-      Field::SetLineWidth(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
-                          pData->num);
+      SetLineWidth(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
+                   pData->num);
       break;
     case FP_RECT:
-      Field::SetRect(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
-                     pData->rect);
+      SetRect(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
+              pData->rect);
       break;
     case FP_VALUE:
-      Field::SetValue(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
-                      pData->widestringarray);
+      SetFieldValue(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
+                    pData->widestringarray);
       break;
     default:
       NOTREACHED();
diff --git a/fxjs/cjs_field.h b/fxjs/cjs_field.h
index 8116e07..d772e66 100644
--- a/fxjs/cjs_field.h
+++ b/fxjs/cjs_field.h
@@ -7,14 +7,14 @@
 #ifndef FXJS_CJS_FIELD_H_
 #define FXJS_CJS_FIELD_H_
 
-#include <string>
 #include <vector>
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_document.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
 class CPDF_FormControl;
 class CPDFSDK_Widget;
-class Document;
 struct CJS_DelayData;
 
 enum FIELD_PROP {
@@ -27,303 +27,336 @@
   FP_VALUE
 };
 
-class Field : public CJS_EmbedObj {
+class CJS_Field final : public CJS_Object {
  public:
+  static int GetObjDefnID();
+  static void DefineJSObjects(CFXJS_Engine* pEngine);
   static void DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                       CJS_DelayData* pData);
 
-  explicit Field(CJS_Object* pJSObject);
-  ~Field() override;
+  CJS_Field(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Field() override;
 
-  CJS_Return get_alignment(CJS_Runtime* pRuntime);
-  CJS_Return set_alignment(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+  bool AttachField(CJS_Document* pDocument, const WideString& csFieldName);
 
-  CJS_Return get_border_style(CJS_Runtime* pRuntime);
-  CJS_Return set_border_style(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+  JS_STATIC_PROP(alignment, alignment, CJS_Field)
+  JS_STATIC_PROP(borderStyle, border_style, CJS_Field)
+  JS_STATIC_PROP(buttonAlignX, button_align_x, CJS_Field)
+  JS_STATIC_PROP(buttonAlignY, button_align_y, CJS_Field)
+  JS_STATIC_PROP(buttonFitBounds, button_fit_bounds, CJS_Field)
+  JS_STATIC_PROP(buttonPosition, button_position, CJS_Field)
+  JS_STATIC_PROP(buttonScaleHow, button_scale_how, CJS_Field)
+  JS_STATIC_PROP(ButtonScaleWhen, button_scale_when, CJS_Field)
+  JS_STATIC_PROP(calcOrderIndex, calc_order_index, CJS_Field)
+  JS_STATIC_PROP(charLimit, char_limit, CJS_Field)
+  JS_STATIC_PROP(comb, comb, CJS_Field)
+  JS_STATIC_PROP(commitOnSelChange, commit_on_sel_change, CJS_Field)
+  JS_STATIC_PROP(currentValueIndices, current_value_indices, CJS_Field)
+  JS_STATIC_PROP(defaultStyle, default_style, CJS_Field)
+  JS_STATIC_PROP(defaultValue, default_value, CJS_Field)
+  JS_STATIC_PROP(doNotScroll, do_not_scroll, CJS_Field)
+  JS_STATIC_PROP(doNotSpellCheck, do_not_spell_check, CJS_Field)
+  JS_STATIC_PROP(delay, delay, CJS_Field)
+  JS_STATIC_PROP(display, display, CJS_Field)
+  JS_STATIC_PROP(doc, doc, CJS_Field)
+  JS_STATIC_PROP(editable, editable, CJS_Field)
+  JS_STATIC_PROP(exportValues, export_values, CJS_Field)
+  JS_STATIC_PROP(fileSelect, file_select, CJS_Field)
+  JS_STATIC_PROP(fillColor, fill_color, CJS_Field)
+  JS_STATIC_PROP(hidden, hidden, CJS_Field)
+  JS_STATIC_PROP(highlight, highlight, CJS_Field)
+  JS_STATIC_PROP(lineWidth, line_width, CJS_Field)
+  JS_STATIC_PROP(multiline, multiline, CJS_Field)
+  JS_STATIC_PROP(multipleSelection, multiple_selection, CJS_Field)
+  JS_STATIC_PROP(name, name, CJS_Field)
+  JS_STATIC_PROP(numItems, num_items, CJS_Field)
+  JS_STATIC_PROP(page, page, CJS_Field)
+  JS_STATIC_PROP(password, password, CJS_Field)
+  JS_STATIC_PROP(print, print, CJS_Field)
+  JS_STATIC_PROP(radiosInUnison, radios_in_unison, CJS_Field)
+  JS_STATIC_PROP(readonly, readonly, CJS_Field)
+  JS_STATIC_PROP(rect, rect, CJS_Field)
+  JS_STATIC_PROP(required, required, CJS_Field)
+  JS_STATIC_PROP(richText, rich_text, CJS_Field)
+  JS_STATIC_PROP(richValue, rich_value, CJS_Field)
+  JS_STATIC_PROP(rotation, rotation, CJS_Field)
+  JS_STATIC_PROP(source, source, CJS_Field)
+  JS_STATIC_PROP(strokeColor, stroke_color, CJS_Field)
+  JS_STATIC_PROP(style, style, CJS_Field)
+  JS_STATIC_PROP(submitName, submit_name, CJS_Field)
+  JS_STATIC_PROP(textColor, text_color, CJS_Field)
+  JS_STATIC_PROP(textFont, text_font, CJS_Field)
+  JS_STATIC_PROP(textSize, text_size, CJS_Field)
+  JS_STATIC_PROP(type, type, CJS_Field)
+  JS_STATIC_PROP(userName, user_name, CJS_Field)
+  JS_STATIC_PROP(value, value, CJS_Field)
+  JS_STATIC_PROP(valueAsString, value_as_string, CJS_Field)
 
-  CJS_Return get_button_align_x(CJS_Runtime* pRuntime);
-  CJS_Return set_button_align_x(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+  JS_STATIC_METHOD(browseForFileToSubmit, CJS_Field)
+  JS_STATIC_METHOD(buttonGetCaption, CJS_Field)
+  JS_STATIC_METHOD(buttonGetIcon, CJS_Field)
+  JS_STATIC_METHOD(buttonImportIcon, CJS_Field)
+  JS_STATIC_METHOD(buttonSetCaption, CJS_Field)
+  JS_STATIC_METHOD(buttonSetIcon, CJS_Field)
+  JS_STATIC_METHOD(checkThisBox, CJS_Field)
+  JS_STATIC_METHOD(clearItems, CJS_Field)
+  JS_STATIC_METHOD(defaultIsChecked, CJS_Field)
+  JS_STATIC_METHOD(deleteItemAt, CJS_Field)
+  JS_STATIC_METHOD(getArray, CJS_Field)
+  JS_STATIC_METHOD(getItemAt, CJS_Field)
+  JS_STATIC_METHOD(getLock, CJS_Field)
+  JS_STATIC_METHOD(insertItemAt, CJS_Field)
+  JS_STATIC_METHOD(isBoxChecked, CJS_Field)
+  JS_STATIC_METHOD(isDefaultChecked, CJS_Field)
+  JS_STATIC_METHOD(setAction, CJS_Field)
+  JS_STATIC_METHOD(setFocus, CJS_Field)
+  JS_STATIC_METHOD(setItems, CJS_Field)
+  JS_STATIC_METHOD(setLock, CJS_Field)
+  JS_STATIC_METHOD(signatureGetModifications, CJS_Field)
+  JS_STATIC_METHOD(signatureGetSeedValue, CJS_Field)
+  JS_STATIC_METHOD(signatureInfo, CJS_Field)
+  JS_STATIC_METHOD(signatureSetSeedValue, CJS_Field)
+  JS_STATIC_METHOD(signatureSign, CJS_Field)
+  JS_STATIC_METHOD(signatureValidate, CJS_Field)
 
-  CJS_Return get_button_align_y(CJS_Runtime* pRuntime);
-  CJS_Return set_button_align_y(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_button_fit_bounds(CJS_Runtime* pRuntime);
-  CJS_Return set_button_fit_bounds(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp);
-
-  CJS_Return get_button_position(CJS_Runtime* pRuntime);
-  CJS_Return set_button_position(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp);
-
-  CJS_Return get_button_scale_how(CJS_Runtime* pRuntime);
-  CJS_Return set_button_scale_how(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp);
-
-  CJS_Return get_button_scale_when(CJS_Runtime* pRuntime);
-  CJS_Return set_button_scale_when(CJS_Runtime* pRuntime,
-                                   v8::Local<v8::Value> vp);
-
-  CJS_Return get_calc_order_index(CJS_Runtime* pRuntime);
-  CJS_Return set_calc_order_index(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp);
-
-  CJS_Return get_char_limit(CJS_Runtime* pRuntime);
-  CJS_Return set_char_limit(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_comb(CJS_Runtime* pRuntime);
-  CJS_Return set_comb(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_commit_on_sel_change(CJS_Runtime* pRuntime);
-  CJS_Return set_commit_on_sel_change(CJS_Runtime* pRuntime,
-                                      v8::Local<v8::Value> vp);
-
-  CJS_Return get_current_value_indices(CJS_Runtime* pRuntime);
-  CJS_Return set_current_value_indices(CJS_Runtime* pRuntime,
-                                       v8::Local<v8::Value> vp);
-
-  CJS_Return get_default_style(CJS_Runtime* pRuntime);
-  CJS_Return set_default_style(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_default_value(CJS_Runtime* pRuntime);
-  CJS_Return set_default_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_do_not_scroll(CJS_Runtime* pRuntime);
-  CJS_Return set_do_not_scroll(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_do_not_spell_check(CJS_Runtime* pRuntime);
-  CJS_Return set_do_not_spell_check(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp);
-
-  CJS_Return get_delay(CJS_Runtime* pRuntime);
-  CJS_Return set_delay(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_display(CJS_Runtime* pRuntime);
-  CJS_Return set_display(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_doc(CJS_Runtime* pRuntime);
-  CJS_Return set_doc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_editable(CJS_Runtime* pRuntime);
-  CJS_Return set_editable(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_export_values(CJS_Runtime* pRuntime);
-  CJS_Return set_export_values(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_file_select(CJS_Runtime* pRuntime);
-  CJS_Return set_file_select(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_fill_color(CJS_Runtime* pRuntime);
-  CJS_Return set_fill_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_hidden(CJS_Runtime* pRuntime);
-  CJS_Return set_hidden(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_highlight(CJS_Runtime* pRuntime);
-  CJS_Return set_highlight(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_line_width(CJS_Runtime* pRuntime);
-  CJS_Return set_line_width(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_multiline(CJS_Runtime* pRuntime);
-  CJS_Return set_multiline(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_multiple_selection(CJS_Runtime* pRuntime);
-  CJS_Return set_multiple_selection(CJS_Runtime* pRuntime,
-                                    v8::Local<v8::Value> vp);
-
-  CJS_Return get_name(CJS_Runtime* pRuntime);
-  CJS_Return set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_num_items(CJS_Runtime* pRuntime);
-  CJS_Return set_num_items(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_page(CJS_Runtime* pRuntime);
-  CJS_Return set_page(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_password(CJS_Runtime* pRuntime);
-  CJS_Return set_password(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_print(CJS_Runtime* pRuntime);
-  CJS_Return set_print(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_radios_in_unison(CJS_Runtime* pRuntime);
-  CJS_Return set_radios_in_unison(CJS_Runtime* pRuntime,
-                                  v8::Local<v8::Value> vp);
-
-  CJS_Return get_readonly(CJS_Runtime* pRuntime);
-  CJS_Return set_readonly(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rect(CJS_Runtime* pRuntime);
-  CJS_Return set_rect(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_required(CJS_Runtime* pRuntime);
-  CJS_Return set_required(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rich_text(CJS_Runtime* pRuntime);
-  CJS_Return set_rich_text(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rich_value(CJS_Runtime* pRuntime);
-  CJS_Return set_rich_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_rotation(CJS_Runtime* pRuntime);
-  CJS_Return set_rotation(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_stroke_color(CJS_Runtime* pRuntime);
-  CJS_Return set_stroke_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_style(CJS_Runtime* pRuntime);
-  CJS_Return set_style(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_submit_name(CJS_Runtime* pRuntime);
-  CJS_Return set_submit_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_text_color(CJS_Runtime* pRuntime);
-  CJS_Return set_text_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_text_font(CJS_Runtime* pRuntime);
-  CJS_Return set_text_font(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_text_size(CJS_Runtime* pRuntime);
-  CJS_Return set_text_size(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_type(CJS_Runtime* pRuntime);
-  CJS_Return set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_user_name(CJS_Runtime* pRuntime);
-  CJS_Return set_user_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_value(CJS_Runtime* pRuntime);
-  CJS_Return set_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return get_value_as_string(CJS_Runtime* pRuntime);
-  CJS_Return set_value_as_string(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp);
-
-  CJS_Return get_source(CJS_Runtime* pRuntime);
-  CJS_Return set_source(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  CJS_Return browseForFileToSubmit(
-      CJS_Runtime* pRuntime,
-      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return buttonGetCaption(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return buttonGetIcon(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return buttonImportIcon(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return buttonSetCaption(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return buttonSetIcon(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return checkThisBox(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return clearItems(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return defaultIsChecked(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return deleteItemAt(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getArray(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getItemAt(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return getLock(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return insertItemAt(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return isBoxChecked(CJS_Runtime* pRuntime,
-                          const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return isDefaultChecked(CJS_Runtime* pRuntime,
-                              const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return setAction(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return setFocus(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return setItems(CJS_Runtime* pRuntime,
-                      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return setLock(CJS_Runtime* pRuntime,
-                     const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return signatureGetModifications(
-      CJS_Runtime* pRuntime,
-      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return signatureGetSeedValue(
-      CJS_Runtime* pRuntime,
-      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return signatureInfo(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return signatureSetSeedValue(
-      CJS_Runtime* pRuntime,
-      const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return signatureSign(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return signatureValidate(CJS_Runtime* pRuntime,
-                               const std::vector<v8::Local<v8::Value>>& params);
-
-  bool AttachField(Document* pDocument, const WideString& csFieldName);
+  CJS_Result get_text_color(CJS_Runtime* pRuntime);
+  CJS_Result set_text_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
 
  private:
-  static void SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                             const WideString& swFieldName,
-                             int nControlIndex,
-                             const ByteString& string);
-  static void SetCurrentValueIndices(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                     const WideString& swFieldName,
-                                     int nControlIndex,
-                                     const std::vector<uint32_t>& array);
-  static void SetDisplay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                         const WideString& swFieldName,
-                         int nControlIndex,
-                         int number);
-  static void SetHidden(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                        const WideString& swFieldName,
-                        int nControlIndex,
-                        bool b);
-  static void SetLineWidth(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                           const WideString& swFieldName,
-                           int nControlIndex,
-                           int number);
-  static void SetMultiline(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                           const WideString& swFieldName,
-                           int nControlIndex,
-                           bool b);
-  static void SetRect(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                      const WideString& swFieldName,
-                      int nControlIndex,
-                      const CFX_FloatRect& rect);
-  static void SetValue(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                       const WideString& swFieldName,
-                       int nControlIndex,
-                       const std::vector<WideString>& strArray);
+  static int ObjDefnID;
+  static const char kName[];
+  static const JSPropertySpec PropertySpecs[];
+  static const JSMethodSpec MethodSpecs[];
 
-  static void UpdateFormField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                              CPDF_FormField* pFormField,
-                              bool bChangeMark,
-                              bool bResetAP,
-                              bool bRefresh);
-  static void UpdateFormControl(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                CPDF_FormControl* pFormControl,
-                                bool bChangeMark,
-                                bool bResetAP,
-                                bool bRefresh);
+  CJS_Result get_alignment(CJS_Runtime* pRuntime);
+  CJS_Result set_alignment(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
 
-  static CPDFSDK_Widget* GetWidget(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                   CPDF_FormControl* pFormControl);
-  static std::vector<CPDF_FormField*> GetFormFields(
-      CPDFSDK_FormFillEnvironment* pFormFillEnv,
-      const WideString& csFieldName);
+  CJS_Result get_border_style(CJS_Runtime* pRuntime);
+  CJS_Result set_border_style(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_button_align_x(CJS_Runtime* pRuntime);
+  CJS_Result set_button_align_x(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_button_align_y(CJS_Runtime* pRuntime);
+  CJS_Result set_button_align_y(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_button_fit_bounds(CJS_Runtime* pRuntime);
+  CJS_Result set_button_fit_bounds(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp);
+
+  CJS_Result get_button_position(CJS_Runtime* pRuntime);
+  CJS_Result set_button_position(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp);
+
+  CJS_Result get_button_scale_how(CJS_Runtime* pRuntime);
+  CJS_Result set_button_scale_how(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp);
+
+  CJS_Result get_button_scale_when(CJS_Runtime* pRuntime);
+  CJS_Result set_button_scale_when(CJS_Runtime* pRuntime,
+                                   v8::Local<v8::Value> vp);
+
+  CJS_Result get_calc_order_index(CJS_Runtime* pRuntime);
+  CJS_Result set_calc_order_index(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp);
+
+  CJS_Result get_char_limit(CJS_Runtime* pRuntime);
+  CJS_Result set_char_limit(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_comb(CJS_Runtime* pRuntime);
+  CJS_Result set_comb(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_commit_on_sel_change(CJS_Runtime* pRuntime);
+  CJS_Result set_commit_on_sel_change(CJS_Runtime* pRuntime,
+                                      v8::Local<v8::Value> vp);
+
+  CJS_Result get_current_value_indices(CJS_Runtime* pRuntime);
+  CJS_Result set_current_value_indices(CJS_Runtime* pRuntime,
+                                       v8::Local<v8::Value> vp);
+
+  CJS_Result get_default_style(CJS_Runtime* pRuntime);
+  CJS_Result set_default_style(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_default_value(CJS_Runtime* pRuntime);
+  CJS_Result set_default_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_do_not_scroll(CJS_Runtime* pRuntime);
+  CJS_Result set_do_not_scroll(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_do_not_spell_check(CJS_Runtime* pRuntime);
+  CJS_Result set_do_not_spell_check(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp);
+
+  CJS_Result get_delay(CJS_Runtime* pRuntime);
+  CJS_Result set_delay(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_display(CJS_Runtime* pRuntime);
+  CJS_Result set_display(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_doc(CJS_Runtime* pRuntime);
+  CJS_Result set_doc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_editable(CJS_Runtime* pRuntime);
+  CJS_Result set_editable(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_export_values(CJS_Runtime* pRuntime);
+  CJS_Result set_export_values(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_file_select(CJS_Runtime* pRuntime);
+  CJS_Result set_file_select(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_fill_color(CJS_Runtime* pRuntime);
+  CJS_Result set_fill_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_hidden(CJS_Runtime* pRuntime);
+  CJS_Result set_hidden(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_highlight(CJS_Runtime* pRuntime);
+  CJS_Result set_highlight(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_line_width(CJS_Runtime* pRuntime);
+  CJS_Result set_line_width(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_multiline(CJS_Runtime* pRuntime);
+  CJS_Result set_multiline(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_multiple_selection(CJS_Runtime* pRuntime);
+  CJS_Result set_multiple_selection(CJS_Runtime* pRuntime,
+                                    v8::Local<v8::Value> vp);
+
+  CJS_Result get_name(CJS_Runtime* pRuntime);
+  CJS_Result set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_num_items(CJS_Runtime* pRuntime);
+  CJS_Result set_num_items(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_page(CJS_Runtime* pRuntime);
+  CJS_Result set_page(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_password(CJS_Runtime* pRuntime);
+  CJS_Result set_password(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_print(CJS_Runtime* pRuntime);
+  CJS_Result set_print(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_radios_in_unison(CJS_Runtime* pRuntime);
+  CJS_Result set_radios_in_unison(CJS_Runtime* pRuntime,
+                                  v8::Local<v8::Value> vp);
+
+  CJS_Result get_readonly(CJS_Runtime* pRuntime);
+  CJS_Result set_readonly(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rect(CJS_Runtime* pRuntime);
+  CJS_Result set_rect(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_required(CJS_Runtime* pRuntime);
+  CJS_Result set_required(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rich_text(CJS_Runtime* pRuntime);
+  CJS_Result set_rich_text(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rich_value(CJS_Runtime* pRuntime);
+  CJS_Result set_rich_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_rotation(CJS_Runtime* pRuntime);
+  CJS_Result set_rotation(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_stroke_color(CJS_Runtime* pRuntime);
+  CJS_Result set_stroke_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_style(CJS_Runtime* pRuntime);
+  CJS_Result set_style(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_submit_name(CJS_Runtime* pRuntime);
+  CJS_Result set_submit_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_text_font(CJS_Runtime* pRuntime);
+  CJS_Result set_text_font(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_text_size(CJS_Runtime* pRuntime);
+  CJS_Result set_text_size(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_type(CJS_Runtime* pRuntime);
+  CJS_Result set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_user_name(CJS_Runtime* pRuntime);
+  CJS_Result set_user_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_value(CJS_Runtime* pRuntime);
+  CJS_Result set_value(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result get_value_as_string(CJS_Runtime* pRuntime);
+  CJS_Result set_value_as_string(CJS_Runtime* pRuntime,
+                                 v8::Local<v8::Value> vp);
+
+  CJS_Result get_source(CJS_Runtime* pRuntime);
+  CJS_Result set_source(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  CJS_Result browseForFileToSubmit(
+      CJS_Runtime* pRuntime,
+      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result buttonGetCaption(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result buttonGetIcon(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result buttonImportIcon(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result buttonSetCaption(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result buttonSetIcon(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result checkThisBox(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result clearItems(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result defaultIsChecked(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result deleteItemAt(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getArray(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getItemAt(CJS_Runtime* pRuntime,
+                       const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result getLock(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result insertItemAt(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result isBoxChecked(CJS_Runtime* pRuntime,
+                          const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result isDefaultChecked(CJS_Runtime* pRuntime,
+                              const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result setAction(CJS_Runtime* pRuntime,
+                       const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result setFocus(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result setItems(CJS_Runtime* pRuntime,
+                      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result setLock(CJS_Runtime* pRuntime,
+                     const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result signatureGetModifications(
+      CJS_Runtime* pRuntime,
+      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result signatureGetSeedValue(
+      CJS_Runtime* pRuntime,
+      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result signatureInfo(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result signatureSetSeedValue(
+      CJS_Runtime* pRuntime,
+      const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result signatureSign(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result signatureValidate(CJS_Runtime* pRuntime,
+                               const std::vector<v8::Local<v8::Value>>& params);
 
   void SetDelay(bool bDelay);
-  void ParseFieldName(const std::wstring& strFieldNameParsed,
-                      std::wstring& strFieldName,
-                      int& iControlNo);
-  std::vector<CPDF_FormField*> GetFormFields(
-      const WideString& csFieldName) const;
+  std::vector<CPDF_FormField*> GetFormFields() const;
+  CPDF_FormField* GetFirstFormField() const;
   CPDF_FormControl* GetSmartFieldControl(CPDF_FormField* pFormField);
-  bool ValueIsOccur(CPDF_FormField* pFormField, WideString csOptLabel);
 
   void AddDelay_Int(FIELD_PROP prop, int32_t n);
   void AddDelay_Bool(FIELD_PROP prop, bool b);
-  void AddDelay_String(FIELD_PROP prop, const ByteString& string);
+  void AddDelay_String(FIELD_PROP prop, const ByteString& str);
   void AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect);
   void AddDelay_WordArray(FIELD_PROP prop, const std::vector<uint32_t>& array);
   void AddDelay_WideStringArray(FIELD_PROP prop,
@@ -331,108 +364,12 @@
 
   void DoDelay();
 
-  Document* m_pJSDoc;
-  CPDFSDK_FormFillEnvironment::ObservedPtr m_pFormFillEnv;
+  ObservedPtr<CJS_Document> m_pJSDoc;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
   WideString m_FieldName;
-  int m_nFormControlIndex;
-  bool m_bCanSet;
-  bool m_bDelay;
-};
-
-class CJS_Field : public CJS_Object {
- public:
-  static int GetObjDefnID();
-  static void DefineJSObjects(CFXJS_Engine* pEngine);
-
-  explicit CJS_Field(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Field() override {}
-
-  void InitInstance(IJS_Runtime* pIRuntime) override;
-
-  JS_STATIC_PROP(alignment, alignment, Field);
-  JS_STATIC_PROP(borderStyle, border_style, Field);
-  JS_STATIC_PROP(buttonAlignX, button_align_x, Field);
-  JS_STATIC_PROP(buttonAlignY, button_align_y, Field);
-  JS_STATIC_PROP(buttonFitBounds, button_fit_bounds, Field);
-  JS_STATIC_PROP(buttonPosition, button_position, Field);
-  JS_STATIC_PROP(buttonScaleHow, button_scale_how, Field);
-  JS_STATIC_PROP(ButtonScaleWhen, button_scale_when, Field);
-  JS_STATIC_PROP(calcOrderIndex, calc_order_index, Field);
-  JS_STATIC_PROP(charLimit, char_limit, Field);
-  JS_STATIC_PROP(comb, comb, Field);
-  JS_STATIC_PROP(commitOnSelChange, commit_on_sel_change, Field);
-  JS_STATIC_PROP(currentValueIndices, current_value_indices, Field);
-  JS_STATIC_PROP(defaultStyle, default_style, Field);
-  JS_STATIC_PROP(defaultValue, default_value, Field);
-  JS_STATIC_PROP(doNotScroll, do_not_scroll, Field);
-  JS_STATIC_PROP(doNotSpellCheck, do_not_spell_check, Field);
-  JS_STATIC_PROP(delay, delay, Field);
-  JS_STATIC_PROP(display, display, Field);
-  JS_STATIC_PROP(doc, doc, Field);
-  JS_STATIC_PROP(editable, editable, Field);
-  JS_STATIC_PROP(exportValues, export_values, Field);
-  JS_STATIC_PROP(fileSelect, file_select, Field);
-  JS_STATIC_PROP(fillColor, fill_color, Field);
-  JS_STATIC_PROP(hidden, hidden, Field);
-  JS_STATIC_PROP(highlight, highlight, Field);
-  JS_STATIC_PROP(lineWidth, line_width, Field);
-  JS_STATIC_PROP(multiline, multiline, Field);
-  JS_STATIC_PROP(multipleSelection, multiple_selection, Field);
-  JS_STATIC_PROP(name, name, Field);
-  JS_STATIC_PROP(numItems, num_items, Field);
-  JS_STATIC_PROP(page, page, Field);
-  JS_STATIC_PROP(password, password, Field);
-  JS_STATIC_PROP(print, print, Field);
-  JS_STATIC_PROP(radiosInUnison, radios_in_unison, Field);
-  JS_STATIC_PROP(readonly, readonly, Field);
-  JS_STATIC_PROP(rect, rect, Field);
-  JS_STATIC_PROP(required, required, Field);
-  JS_STATIC_PROP(richText, rich_text, Field);
-  JS_STATIC_PROP(richValue, rich_value, Field);
-  JS_STATIC_PROP(rotation, rotation, Field);
-  JS_STATIC_PROP(strokeColor, stroke_color, Field);
-  JS_STATIC_PROP(style, style, Field);
-  JS_STATIC_PROP(submitName, submit_name, Field);
-  JS_STATIC_PROP(textColor, text_color, Field);
-  JS_STATIC_PROP(textFont, text_font, Field);
-  JS_STATIC_PROP(textSize, text_size, Field);
-  JS_STATIC_PROP(type, type, Field);
-  JS_STATIC_PROP(userName, user_name, Field);
-  JS_STATIC_PROP(value, value, Field);
-  JS_STATIC_PROP(valueAsString, value_as_string, Field);
-  JS_STATIC_PROP(source, source, Field);
-
-  JS_STATIC_METHOD(browseForFileToSubmit, Field);
-  JS_STATIC_METHOD(buttonGetCaption, Field);
-  JS_STATIC_METHOD(buttonGetIcon, Field);
-  JS_STATIC_METHOD(buttonImportIcon, Field);
-  JS_STATIC_METHOD(buttonSetCaption, Field);
-  JS_STATIC_METHOD(buttonSetIcon, Field);
-  JS_STATIC_METHOD(checkThisBox, Field);
-  JS_STATIC_METHOD(clearItems, Field);
-  JS_STATIC_METHOD(defaultIsChecked, Field);
-  JS_STATIC_METHOD(deleteItemAt, Field);
-  JS_STATIC_METHOD(getArray, Field);
-  JS_STATIC_METHOD(getItemAt, Field);
-  JS_STATIC_METHOD(getLock, Field);
-  JS_STATIC_METHOD(insertItemAt, Field);
-  JS_STATIC_METHOD(isBoxChecked, Field);
-  JS_STATIC_METHOD(isDefaultChecked, Field);
-  JS_STATIC_METHOD(setAction, Field);
-  JS_STATIC_METHOD(setFocus, Field);
-  JS_STATIC_METHOD(setItems, Field);
-  JS_STATIC_METHOD(setLock, Field);
-  JS_STATIC_METHOD(signatureGetModifications, Field);
-  JS_STATIC_METHOD(signatureGetSeedValue, Field);
-  JS_STATIC_METHOD(signatureInfo, Field);
-  JS_STATIC_METHOD(signatureSetSeedValue, Field);
-  JS_STATIC_METHOD(signatureSign, Field);
-  JS_STATIC_METHOD(signatureValidate, Field);
-
- private:
-  static int ObjDefnID;
-  static const JSPropertySpec PropertySpecs[];
-  static const JSMethodSpec MethodSpecs[];
+  int m_nFormControlIndex = -1;
+  bool m_bCanSet = false;
+  bool m_bDelay = false;
 };
 
 #endif  // FXJS_CJS_FIELD_H_
diff --git a/fxjs/cjs_font.cpp b/fxjs/cjs_font.cpp
index 54b392f..0e6e94b 100644
--- a/fxjs/cjs_font.cpp
+++ b/fxjs/cjs_font.cpp
@@ -27,5 +27,5 @@
 // static
 void CJS_Font::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID = pEngine->DefineObj("font", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_font.h b/fxjs/cjs_font.h
index 31edf06..d3cf159 100644
--- a/fxjs/cjs_font.h
+++ b/fxjs/cjs_font.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_FONT_H_
 #define FXJS_CJS_FONT_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_Font : public CJS_Object {
+class CJS_Font final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Font(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Font() override {}
+  CJS_Font() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_global.cpp b/fxjs/cjs_global.cpp
index eb66488..8ac3181 100644
--- a/fxjs/cjs_global.cpp
+++ b/fxjs/cjs_global.cpp
@@ -12,13 +12,14 @@
 #include <vector>
 
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/JS_Define.h"
+#include "fxjs/cfx_globaldata.h"
+#include "fxjs/cfx_keyvalue.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventhandler.h"
-#include "fxjs/cjs_globaldata.h"
-#include "fxjs/cjs_keyvalue.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -32,19 +33,17 @@
 void JSSpecialPropQuery(const char*,
                         v8::Local<v8::String> property,
                         const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  auto pObj = JSGetObject<Alt>(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
   if (!pRuntime)
     return;
 
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  CJS_Return result =
+  CJS_Result result =
       pObj->QueryProperty(PropFromV8Prop(info.GetIsolate(), property).c_str());
+
   info.GetReturnValue().Set(!result.HasError() ? 4 : 0);
 }
 
@@ -52,25 +51,22 @@
 void JSSpecialPropGet(const char* class_name,
                       v8::Local<v8::String> property,
                       const v8::PropertyCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  auto pObj = JSGetObject<Alt>(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
   if (!pRuntime)
     return;
 
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  CJS_Return result = pObj->GetProperty(
+  CJS_Result result = pObj->GetProperty(
       pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str());
+
   if (result.HasError()) {
     pRuntime->Error(
         JSFormatErrorString(class_name, "GetProperty", result.Error()));
     return;
   }
-
   if (result.HasReturn())
     info.GetReturnValue().Set(result.Return());
 }
@@ -80,19 +76,17 @@
                       v8::Local<v8::String> property,
                       v8::Local<v8::Value> value,
                       const v8::PropertyCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  auto pObj = JSGetObject<Alt>(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
   if (!pRuntime)
     return;
 
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  CJS_Return result = pObj->SetProperty(
+  CJS_Result result = pObj->SetProperty(
       pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str(), value);
+
   if (result.HasError()) {
     pRuntime->Error(
         JSFormatErrorString(class_name, "PutProperty", result.Error()));
@@ -103,18 +97,15 @@
 void JSSpecialPropDel(const char* class_name,
                       v8::Local<v8::String> property,
                       const v8::PropertyCallbackInfo<v8::Boolean>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  auto pObj = JSGetObject<Alt>(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
   if (!pRuntime)
     return;
 
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  CJS_Return result = pObj->DelProperty(
+  CJS_Result result = pObj->DelProperty(
       pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str());
   if (result.HasError()) {
     // TODO(dsinclair): Should this set the pRuntime->Error result?
@@ -123,58 +114,19 @@
   }
 }
 
-struct JSGlobalData {
-  JSGlobalData();
-  ~JSGlobalData();
-
-  JS_GlobalDataType nType;
-  double dData;
-  bool bData;
-  ByteString sData;
-  v8::Global<v8::Object> pData;
-  bool bPersistent;
-  bool bDeleted;
-};
-
-class JSGlobalAlternate : public CJS_EmbedObj {
- public:
-  explicit JSGlobalAlternate(CJS_Object* pJSObject);
-  ~JSGlobalAlternate() override;
-
-  CJS_Return setPersistent(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return QueryProperty(const wchar_t* propname);
-  CJS_Return GetProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
-  CJS_Return SetProperty(CJS_Runtime* pRuntime,
-                         const wchar_t* propname,
-                         v8::Local<v8::Value> vp);
-  CJS_Return DelProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
-  void Initial(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
- private:
-  void UpdateGlobalPersistentVariables();
-  void CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime);
-  void DestroyGlobalPersisitentVariables();
-  CJS_Return SetGlobalVariables(const ByteString& propname,
-                                JS_GlobalDataType nType,
-                                double dData,
-                                bool bData,
-                                const ByteString& sData,
-                                v8::Local<v8::Object> pData,
-                                bool bDefaultPersistent);
-  void ObjectToArray(CJS_Runtime* pRuntime,
-                     v8::Local<v8::Object> pObj,
-                     CJS_GlobalVariableArray& array);
-  void PutObjectProperty(v8::Local<v8::Object> obj, CJS_KeyValue* pData);
-
-  std::map<ByteString, std::unique_ptr<JSGlobalData>> m_MapGlobal;
-  WideString m_sFilePath;
-  CJS_GlobalData* m_pGlobalData;
-  CPDFSDK_FormFillEnvironment::ObservedPtr m_pFormFillEnv;
-};
+template <class T>
+v8::Local<v8::String> GetV8StringFromProperty(v8::Local<v8::Name> property,
+                                              const T& info) {
+  return property->ToString(info.GetIsolate()->GetCurrentContext())
+      .ToLocalChecked();
+}
 
 }  // namespace
 
+CJS_Global::JSGlobalData::JSGlobalData() = default;
+
+CJS_Global::JSGlobalData::~JSGlobalData() = default;
+
 const JSMethodSpec CJS_Global::MethodSpecs[] = {
     {"setPersistent", setPersistent_static}};
 
@@ -183,37 +135,57 @@
 // static
 void CJS_Global::setPersistent_static(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  JSMethod<JSGlobalAlternate, &JSGlobalAlternate::setPersistent>(
-      "setPersistent", "global", info);
+  JSMethod<CJS_Global, &CJS_Global::setPersistent>("setPersistent", "global",
+                                                   info);
 }
 
 // static
 void CJS_Global::queryprop_static(
-    v8::Local<v8::String> property,
+    v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  JSSpecialPropQuery<JSGlobalAlternate>("global", property, info);
+  ASSERT(property->IsString());
+  JSSpecialPropQuery<CJS_Global>(
+      "global",
+      v8::Local<v8::String>::New(info.GetIsolate(),
+                                 GetV8StringFromProperty(property, info)),
+      info);
 }
 
 // static
 void CJS_Global::getprop_static(
-    v8::Local<v8::String> property,
+    v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  JSSpecialPropGet<JSGlobalAlternate>("global", property, info);
+  ASSERT(property->IsString());
+  JSSpecialPropGet<CJS_Global>(
+      "global",
+      v8::Local<v8::String>::New(info.GetIsolate(),
+                                 GetV8StringFromProperty(property, info)),
+      info);
 }
 
 // static
 void CJS_Global::putprop_static(
-    v8::Local<v8::String> property,
+    v8::Local<v8::Name> property,
     v8::Local<v8::Value> value,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  JSSpecialPropPut<JSGlobalAlternate>("global", property, value, info);
+  ASSERT(property->IsString());
+  JSSpecialPropPut<CJS_Global>(
+      "global",
+      v8::Local<v8::String>::New(info.GetIsolate(),
+                                 GetV8StringFromProperty(property, info)),
+      value, info);
 }
 
 // static
 void CJS_Global::delprop_static(
-    v8::Local<v8::String> property,
+    v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Boolean>& info) {
-  JSSpecialPropDel<JSGlobalAlternate>("global", property, info);
+  ASSERT(property->IsString());
+  JSSpecialPropDel<CJS_Global>(
+      "global",
+      v8::Local<v8::String>::New(info.GetIsolate(),
+                                 GetV8StringFromProperty(property, info)),
+      info);
 }
 
 // static
@@ -224,196 +196,182 @@
 }
 
 // static
+int CJS_Global::GetObjDefnID() {
+  return ObjDefnID;
+}
+
+// static
 void CJS_Global::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID = pEngine->DefineObj("global", FXJSOBJTYPE_STATIC,
-                                 JSConstructor<CJS_Global, JSGlobalAlternate>,
-                                 JSDestructor<CJS_Global>);
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
+                                 JSConstructor<CJS_Global>, JSDestructor);
+  DefineMethods(pEngine, ObjDefnID, MethodSpecs);
   DefineAllProperties(pEngine);
 }
 
-void CJS_Global::InitInstance(IJS_Runtime* pIRuntime) {
-  CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
-  JSGlobalAlternate* pGlobal =
-      static_cast<JSGlobalAlternate*>(GetEmbedObject());
-  pGlobal->Initial(pRuntime->GetFormFillEnv());
+CJS_Global::CJS_Global(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {
+  CPDFSDK_FormFillEnvironment* pFormFillEnv = GetRuntime()->GetFormFillEnv();
+  m_pFormFillEnv.Reset(pFormFillEnv);
+  m_pGlobalData = CFX_GlobalData::GetRetainedInstance(nullptr);
+  UpdateGlobalPersistentVariables();
 }
 
-JSGlobalData::JSGlobalData()
-    : nType(JS_GlobalDataType::NUMBER),
-      dData(0),
-      bData(false),
-      sData(""),
-      bPersistent(false),
-      bDeleted(false) {}
-
-JSGlobalData::~JSGlobalData() {
-  pData.Reset();
-}
-
-JSGlobalAlternate::JSGlobalAlternate(CJS_Object* pJSObject)
-    : CJS_EmbedObj(pJSObject), m_pFormFillEnv(nullptr) {}
-
-JSGlobalAlternate::~JSGlobalAlternate() {
+CJS_Global::~CJS_Global() {
   DestroyGlobalPersisitentVariables();
   m_pGlobalData->Release();
 }
 
-void JSGlobalAlternate::Initial(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pFormFillEnv.Reset(pFormFillEnv);
-  m_pGlobalData = CJS_GlobalData::GetRetainedInstance(pFormFillEnv);
-  UpdateGlobalPersistentVariables();
+CJS_Result CJS_Global::QueryProperty(const wchar_t* propname) {
+  if (WideString(propname).EqualsASCII("setPersistent"))
+    return CJS_Result::Success();
+
+  return CJS_Result::Failure(JSMessage::kUnknownProperty);
 }
 
-CJS_Return JSGlobalAlternate::QueryProperty(const wchar_t* propname) {
-  return CJS_Return(WideString(propname) != L"setPersistent");
-}
-
-CJS_Return JSGlobalAlternate::DelProperty(CJS_Runtime* pRuntime,
-                                          const wchar_t* propname) {
-  auto it = m_MapGlobal.find(ByteString::FromUnicode(propname));
+CJS_Result CJS_Global::DelProperty(CJS_Runtime* pRuntime,
+                                   const wchar_t* propname) {
+  auto it = m_MapGlobal.find(WideString(propname).ToDefANSI());
   if (it == m_MapGlobal.end())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kUnknownProperty);
 
   it->second->bDeleted = true;
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return JSGlobalAlternate::GetProperty(CJS_Runtime* pRuntime,
-                                          const wchar_t* propname) {
-  auto it = m_MapGlobal.find(ByteString::FromUnicode(propname));
+CJS_Result CJS_Global::GetProperty(CJS_Runtime* pRuntime,
+                                   const wchar_t* propname) {
+  auto it = m_MapGlobal.find(WideString(propname).ToDefANSI());
   if (it == m_MapGlobal.end())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   JSGlobalData* pData = it->second.get();
   if (pData->bDeleted)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   switch (pData->nType) {
-    case JS_GlobalDataType::NUMBER:
-      return CJS_Return(pRuntime->NewNumber(pData->dData));
-    case JS_GlobalDataType::BOOLEAN:
-      return CJS_Return(pRuntime->NewBoolean(pData->bData));
-    case JS_GlobalDataType::STRING:
-      return CJS_Return(pRuntime->NewString(
-          WideString::FromLocal(pData->sData.c_str()).c_str()));
-    case JS_GlobalDataType::OBJECT:
-      return CJS_Return(
+    case CFX_Value::DataType::NUMBER:
+      return CJS_Result::Success(pRuntime->NewNumber(pData->dData));
+    case CFX_Value::DataType::BOOLEAN:
+      return CJS_Result::Success(pRuntime->NewBoolean(pData->bData));
+    case CFX_Value::DataType::STRING:
+      return CJS_Result::Success(pRuntime->NewString(
+          WideString::FromDefANSI(pData->sData.AsStringView()).AsStringView()));
+    case CFX_Value::DataType::OBJECT:
+      return CJS_Result::Success(
           v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData));
-    case JS_GlobalDataType::NULLOBJ:
-      return CJS_Return(pRuntime->NewNull());
+    case CFX_Value::DataType::NULLOBJ:
+      return CJS_Result::Success(pRuntime->NewNull());
     default:
       break;
   }
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kObjectTypeError);
 }
 
-CJS_Return JSGlobalAlternate::SetProperty(CJS_Runtime* pRuntime,
-                                          const wchar_t* propname,
-                                          v8::Local<v8::Value> vp) {
-  ByteString sPropName = ByteString::FromUnicode(propname);
+CJS_Result CJS_Global::SetProperty(CJS_Runtime* pRuntime,
+                                   const wchar_t* propname,
+                                   v8::Local<v8::Value> vp) {
+  ByteString sPropName = WideString(propname).ToDefANSI();
   if (vp->IsNumber()) {
-    return SetGlobalVariables(sPropName, JS_GlobalDataType::NUMBER,
-                              pRuntime->ToDouble(vp), false, "",
+    return SetGlobalVariables(sPropName, CFX_Value::DataType::NUMBER,
+                              pRuntime->ToDouble(vp), false, ByteString(),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsBoolean()) {
-    return SetGlobalVariables(sPropName, JS_GlobalDataType::BOOLEAN, 0,
-                              pRuntime->ToBoolean(vp), "",
+    return SetGlobalVariables(sPropName, CFX_Value::DataType::BOOLEAN, 0,
+                              pRuntime->ToBoolean(vp), ByteString(),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsString()) {
-    return SetGlobalVariables(
-        sPropName, JS_GlobalDataType::STRING, 0, false,
-        ByteString::FromUnicode(pRuntime->ToWideString(vp)),
-        v8::Local<v8::Object>(), false);
+    return SetGlobalVariables(sPropName, CFX_Value::DataType::STRING, 0, false,
+                              pRuntime->ToWideString(vp).ToDefANSI(),
+                              v8::Local<v8::Object>(), false);
   }
   if (vp->IsObject()) {
-    return SetGlobalVariables(sPropName, JS_GlobalDataType::OBJECT, 0, false,
-                              "", pRuntime->ToObject(vp), false);
+    return SetGlobalVariables(sPropName, CFX_Value::DataType::OBJECT, 0, false,
+                              ByteString(), pRuntime->ToObject(vp), false);
   }
   if (vp->IsNull()) {
-    return SetGlobalVariables(sPropName, JS_GlobalDataType::NULLOBJ, 0, false,
-                              "", v8::Local<v8::Object>(), false);
+    return SetGlobalVariables(sPropName, CFX_Value::DataType::NULLOBJ, 0, false,
+                              ByteString(), v8::Local<v8::Object>(), false);
   }
   if (vp->IsUndefined()) {
     DelProperty(pRuntime, propname);
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
-  return CJS_Return(false);
+  return CJS_Result::Failure(JSMessage::kObjectTypeError);
 }
 
-CJS_Return JSGlobalAlternate::setPersistent(
+CJS_Result CJS_Global::setPersistent(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto it = m_MapGlobal.find(
-      ByteString::FromUnicode(pRuntime->ToWideString(params[0])));
+  auto it = m_MapGlobal.find(pRuntime->ToWideString(params[0]).ToDefANSI());
   if (it == m_MapGlobal.end() || it->second->bDeleted)
-    return CJS_Return(JSGetStringFromID(JSMessage::kGlobalNotFoundError));
+    return CJS_Result::Failure(JSMessage::kGlobalNotFoundError);
 
   it->second->bPersistent = pRuntime->ToBoolean(params[1]);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-void JSGlobalAlternate::UpdateGlobalPersistentVariables() {
-  CJS_Runtime* pRuntime =
-      static_cast<CJS_Runtime*>(CFXJS_Engine::CurrentEngineFromIsolate(
-          m_pJSObject->ToV8Object()->GetIsolate()));
+void CJS_Global::UpdateGlobalPersistentVariables() {
+  CJS_Runtime* pRuntime = GetRuntime();
+  if (!pRuntime)
+    return;
 
   for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
-    CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i);
+    CFX_GlobalData::Element* pData = m_pGlobalData->GetAt(i);
     switch (pData->data.nType) {
-      case JS_GlobalDataType::NUMBER:
-        SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::NUMBER,
-                           pData->data.dData, false, "",
+      case CFX_Value::DataType::NUMBER:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NUMBER,
+                           pData->data.dData, false, ByteString(),
                            v8::Local<v8::Object>(), pData->bPersistent == 1);
-        pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
-                                    pData->data.sKey.UTF8Decode(),
+        pRuntime->PutObjectProperty(ToV8Object(),
+                                    pData->data.sKey.AsStringView(),
                                     pRuntime->NewNumber(pData->data.dData));
         break;
-      case JS_GlobalDataType::BOOLEAN:
-        SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::BOOLEAN, 0,
-                           pData->data.bData == 1, "", v8::Local<v8::Object>(),
-                           pData->bPersistent == 1);
+      case CFX_Value::DataType::BOOLEAN:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::BOOLEAN, 0,
+                           pData->data.bData == 1, ByteString(),
+                           v8::Local<v8::Object>(), pData->bPersistent == 1);
         pRuntime->PutObjectProperty(
-            m_pJSObject->ToV8Object(), pData->data.sKey.UTF8Decode(),
+            ToV8Object(), pData->data.sKey.AsStringView(),
             pRuntime->NewBoolean(pData->data.bData == 1));
         break;
-      case JS_GlobalDataType::STRING:
-        SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::STRING, 0,
+      case CFX_Value::DataType::STRING:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::STRING, 0,
                            false, pData->data.sData, v8::Local<v8::Object>(),
                            pData->bPersistent == 1);
         pRuntime->PutObjectProperty(
-            m_pJSObject->ToV8Object(), pData->data.sKey.UTF8Decode(),
-            pRuntime->NewString(pData->data.sData.UTF8Decode().AsStringView()));
+            ToV8Object(), pData->data.sKey.AsStringView(),
+            pRuntime->NewString(
+                WideString::FromUTF8(pData->data.sData.AsStringView())
+                    .AsStringView()));
         break;
-      case JS_GlobalDataType::OBJECT: {
-        v8::Local<v8::Object> pObj = pRuntime->NewFxDynamicObj(-1);
+      case CFX_Value::DataType::OBJECT: {
+        v8::Local<v8::Object> pObj = pRuntime->NewObject();
         if (!pObj.IsEmpty()) {
           PutObjectProperty(pObj, &pData->data);
-          SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::OBJECT, 0,
-                             false, "", pObj, pData->bPersistent == 1);
-          pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
-                                      pData->data.sKey.UTF8Decode(), pObj);
+          SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::OBJECT, 0,
+                             false, ByteString(), pObj,
+                             pData->bPersistent == 1);
+          pRuntime->PutObjectProperty(ToV8Object(),
+                                      pData->data.sKey.AsStringView(), pObj);
         }
       } break;
-      case JS_GlobalDataType::NULLOBJ:
-        SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::NULLOBJ, 0,
-                           false, "", v8::Local<v8::Object>(),
+      case CFX_Value::DataType::NULLOBJ:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NULLOBJ, 0,
+                           false, ByteString(), v8::Local<v8::Object>(),
                            pData->bPersistent == 1);
-        pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
-                                    pData->data.sKey.UTF8Decode(),
-                                    pRuntime->NewNull());
+        pRuntime->PutObjectProperty(
+            ToV8Object(), pData->data.sKey.AsStringView(), pRuntime->NewNull());
         break;
     }
   }
 }
 
-void JSGlobalAlternate::CommitGlobalPersisitentVariables(
-    CJS_Runtime* pRuntime) {
+void CJS_Global::CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime) {
   for (const auto& iter : m_MapGlobal) {
     ByteString name = iter.first;
     JSGlobalData* pData = iter.second.get();
@@ -422,27 +380,27 @@
       continue;
     }
     switch (pData->nType) {
-      case JS_GlobalDataType::NUMBER:
+      case CFX_Value::DataType::NUMBER:
         m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case JS_GlobalDataType::BOOLEAN:
+      case CFX_Value::DataType::BOOLEAN:
         m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case JS_GlobalDataType::STRING:
+      case CFX_Value::DataType::STRING:
         m_pGlobalData->SetGlobalVariableString(name, pData->sData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case JS_GlobalDataType::OBJECT: {
-        CJS_GlobalVariableArray array;
-        v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
-            GetJSObject()->GetIsolate(), pData->pData);
-        ObjectToArray(pRuntime, obj, array);
-        m_pGlobalData->SetGlobalVariableObject(name, array);
+      case CFX_Value::DataType::OBJECT: {
+        std::vector<std::unique_ptr<CFX_KeyValue>> array;
+        v8::Local<v8::Object> obj =
+            v8::Local<v8::Object>::New(GetIsolate(), pData->pData);
+        ObjectToArray(pRuntime, obj, &array);
+        m_pGlobalData->SetGlobalVariableObject(name, std::move(array));
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
       } break;
-      case JS_GlobalDataType::NULLOBJ:
+      case CFX_Value::DataType::NULLOBJ:
         m_pGlobalData->SetGlobalVariableNull(name);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
@@ -450,105 +408,110 @@
   }
 }
 
-void JSGlobalAlternate::ObjectToArray(CJS_Runtime* pRuntime,
-                                      v8::Local<v8::Object> pObj,
-                                      CJS_GlobalVariableArray& array) {
+void CJS_Global::ObjectToArray(
+    CJS_Runtime* pRuntime,
+    v8::Local<v8::Object> pObj,
+    std::vector<std::unique_ptr<CFX_KeyValue>>* pArray) {
   std::vector<WideString> pKeyList = pRuntime->GetObjectPropertyNames(pObj);
   for (const auto& ws : pKeyList) {
-    ByteString sKey = ws.UTF8Encode();
-    v8::Local<v8::Value> v = pRuntime->GetObjectProperty(pObj, ws);
+    ByteString sKey = ws.ToUTF8();
+    v8::Local<v8::Value> v =
+        pRuntime->GetObjectProperty(pObj, sKey.AsStringView());
     if (v->IsNumber()) {
-      CJS_KeyValue* pObjElement = new CJS_KeyValue;
-      pObjElement->nType = JS_GlobalDataType::NUMBER;
+      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::NUMBER;
       pObjElement->sKey = sKey;
       pObjElement->dData = pRuntime->ToDouble(v);
-      array.Add(pObjElement);
+      pArray->push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsBoolean()) {
-      CJS_KeyValue* pObjElement = new CJS_KeyValue;
-      pObjElement->nType = JS_GlobalDataType::BOOLEAN;
+      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::BOOLEAN;
       pObjElement->sKey = sKey;
       pObjElement->dData = pRuntime->ToBoolean(v);
-      array.Add(pObjElement);
+      pArray->push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsString()) {
-      ByteString sValue = ByteString::FromUnicode(pRuntime->ToWideString(v));
-      CJS_KeyValue* pObjElement = new CJS_KeyValue;
-      pObjElement->nType = JS_GlobalDataType::STRING;
+      ByteString sValue = pRuntime->ToWideString(v).ToDefANSI();
+      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::STRING;
       pObjElement->sKey = sKey;
       pObjElement->sData = sValue;
-      array.Add(pObjElement);
+      pArray->push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsObject()) {
-      CJS_KeyValue* pObjElement = new CJS_KeyValue;
-      pObjElement->nType = JS_GlobalDataType::OBJECT;
+      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::OBJECT;
       pObjElement->sKey = sKey;
-      ObjectToArray(pRuntime, pRuntime->ToObject(v), pObjElement->objData);
-      array.Add(pObjElement);
+      ObjectToArray(pRuntime, pRuntime->ToObject(v), &pObjElement->objData);
+      pArray->push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsNull()) {
-      CJS_KeyValue* pObjElement = new CJS_KeyValue;
-      pObjElement->nType = JS_GlobalDataType::NULLOBJ;
+      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::NULLOBJ;
       pObjElement->sKey = sKey;
-      array.Add(pObjElement);
+      pArray->push_back(std::move(pObjElement));
     }
   }
 }
 
-void JSGlobalAlternate::PutObjectProperty(v8::Local<v8::Object> pObj,
-                                          CJS_KeyValue* pData) {
-  CJS_Runtime* pRuntime = CJS_Runtime::CurrentRuntimeFromIsolate(
-      m_pJSObject->ToV8Object()->GetIsolate());
+void CJS_Global::PutObjectProperty(v8::Local<v8::Object> pObj,
+                                   CFX_KeyValue* pData) {
+  CJS_Runtime* pRuntime = GetRuntime();
+  if (pRuntime)
+    return;
 
-  for (int i = 0, sz = pData->objData.Count(); i < sz; i++) {
-    CJS_KeyValue* pObjData = pData->objData.GetAt(i);
+  for (size_t i = 0; i < pData->objData.size(); ++i) {
+    CFX_KeyValue* pObjData = pData->objData.at(i).get();
     switch (pObjData->nType) {
-      case JS_GlobalDataType::NUMBER:
-        pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
+      case CFX_Value::DataType::NUMBER:
+        pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewNumber(pObjData->dData));
         break;
-      case JS_GlobalDataType::BOOLEAN:
-        pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
+      case CFX_Value::DataType::BOOLEAN:
+        pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewBoolean(pObjData->bData == 1));
         break;
-      case JS_GlobalDataType::STRING:
+      case CFX_Value::DataType::STRING:
         pRuntime->PutObjectProperty(
-            pObj, pObjData->sKey.UTF8Decode(),
-            pRuntime->NewString(pObjData->sData.UTF8Decode().AsStringView()));
+            pObj, pObjData->sKey.AsStringView(),
+            pRuntime->NewString(
+                WideString::FromUTF8(pObjData->sData.AsStringView())
+                    .AsStringView()));
         break;
-      case JS_GlobalDataType::OBJECT: {
-        v8::Local<v8::Object> pNewObj = pRuntime->NewFxDynamicObj(-1);
+      case CFX_Value::DataType::OBJECT: {
+        v8::Local<v8::Object> pNewObj = pRuntime->NewObject();
         if (!pNewObj.IsEmpty()) {
           PutObjectProperty(pNewObj, pObjData);
-          pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
+          pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                       pNewObj);
         }
       } break;
-      case JS_GlobalDataType::NULLOBJ:
-        pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
+      case CFX_Value::DataType::NULLOBJ:
+        pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewNull());
         break;
     }
   }
 }
 
-void JSGlobalAlternate::DestroyGlobalPersisitentVariables() {
+void CJS_Global::DestroyGlobalPersisitentVariables() {
   m_MapGlobal.clear();
 }
 
-CJS_Return JSGlobalAlternate::SetGlobalVariables(const ByteString& propname,
-                                                 JS_GlobalDataType nType,
-                                                 double dData,
-                                                 bool bData,
-                                                 const ByteString& sData,
-                                                 v8::Local<v8::Object> pData,
-                                                 bool bDefaultPersistent) {
+CJS_Result CJS_Global::SetGlobalVariables(const ByteString& propname,
+                                          CFX_Value::DataType nType,
+                                          double dData,
+                                          bool bData,
+                                          const ByteString& sData,
+                                          v8::Local<v8::Object> pData,
+                                          bool bDefaultPersistent) {
   if (propname.IsEmpty())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kUnknownProperty);
 
   auto it = m_MapGlobal.find(propname);
   if (it != m_MapGlobal.end()) {
@@ -561,55 +524,55 @@
     }
     pTemp->bDeleted = false;
     switch (nType) {
-      case JS_GlobalDataType::NUMBER:
+      case CFX_Value::DataType::NUMBER:
         pTemp->dData = dData;
         break;
-      case JS_GlobalDataType::BOOLEAN:
+      case CFX_Value::DataType::BOOLEAN:
         pTemp->bData = bData;
         break;
-      case JS_GlobalDataType::STRING:
+      case CFX_Value::DataType::STRING:
         pTemp->sData = sData;
         break;
-      case JS_GlobalDataType::OBJECT:
+      case CFX_Value::DataType::OBJECT:
         pTemp->pData.Reset(pData->GetIsolate(), pData);
         break;
-      case JS_GlobalDataType::NULLOBJ:
+      case CFX_Value::DataType::NULLOBJ:
         break;
       default:
-        return CJS_Return(false);
+        return CJS_Result::Failure(JSMessage::kObjectTypeError);
     }
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   auto pNewData = pdfium::MakeUnique<JSGlobalData>();
   switch (nType) {
-    case JS_GlobalDataType::NUMBER:
-      pNewData->nType = JS_GlobalDataType::NUMBER;
+    case CFX_Value::DataType::NUMBER:
+      pNewData->nType = CFX_Value::DataType::NUMBER;
       pNewData->dData = dData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case JS_GlobalDataType::BOOLEAN:
-      pNewData->nType = JS_GlobalDataType::BOOLEAN;
+    case CFX_Value::DataType::BOOLEAN:
+      pNewData->nType = CFX_Value::DataType::BOOLEAN;
       pNewData->bData = bData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case JS_GlobalDataType::STRING:
-      pNewData->nType = JS_GlobalDataType::STRING;
+    case CFX_Value::DataType::STRING:
+      pNewData->nType = CFX_Value::DataType::STRING;
       pNewData->sData = sData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case JS_GlobalDataType::OBJECT:
-      pNewData->nType = JS_GlobalDataType::OBJECT;
+    case CFX_Value::DataType::OBJECT:
+      pNewData->nType = CFX_Value::DataType::OBJECT;
       pNewData->pData.Reset(pData->GetIsolate(), pData);
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case JS_GlobalDataType::NULLOBJ:
-      pNewData->nType = JS_GlobalDataType::NULLOBJ;
+    case CFX_Value::DataType::NULLOBJ:
+      pNewData->nType = CFX_Value::DataType::NULLOBJ;
       pNewData->bPersistent = bDefaultPersistent;
       break;
     default:
-      return CJS_Return(false);
+      return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
   m_MapGlobal[propname] = std::move(pNewData);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
diff --git a/fxjs/cjs_global.h b/fxjs/cjs_global.h
index 203d6e9..2c89188 100644
--- a/fxjs/cjs_global.h
+++ b/fxjs/cjs_global.h
@@ -7,36 +7,82 @@
 #ifndef FXJS_CJS_GLOBAL_H_
 #define FXJS_CJS_GLOBAL_H_
 
-#include "fxjs/JS_Define.h"
+#include <map>
+#include <memory>
+#include <vector>
 
-class CJS_Global : public CJS_Object {
+#include "fxjs/cfx_keyvalue.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/cjs_result.h"
+
+class CFX_GlobalData;
+
+class CJS_Global final : public CJS_Object {
  public:
+  static int GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
   static void DefineAllProperties(CFXJS_Engine* pEngine);
 
   static void queryprop_static(
-      v8::Local<v8::String> property,
+      v8::Local<v8::Name> property,
       const v8::PropertyCallbackInfo<v8::Integer>& info);
-  static void getprop_static(v8::Local<v8::String> property,
+  static void getprop_static(v8::Local<v8::Name> property,
                              const v8::PropertyCallbackInfo<v8::Value>& info);
-  static void putprop_static(v8::Local<v8::String> property,
+  static void putprop_static(v8::Local<v8::Name> property,
                              v8::Local<v8::Value> value,
                              const v8::PropertyCallbackInfo<v8::Value>& info);
-  static void delprop_static(v8::Local<v8::String> property,
+  static void delprop_static(v8::Local<v8::Name> property,
                              const v8::PropertyCallbackInfo<v8::Boolean>& info);
 
   static void setPersistent_static(
       const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  explicit CJS_Global(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Global() override {}
+  CJS_Global(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Global() override;
 
-  // CJS_Object
-  void InitInstance(IJS_Runtime* pIRuntime) override;
+  CJS_Result DelProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
+
+  CJS_Result setPersistent(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result QueryProperty(const wchar_t* propname);
+  CJS_Result GetProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
+  CJS_Result SetProperty(CJS_Runtime* pRuntime,
+                         const wchar_t* propname,
+                         v8::Local<v8::Value> vp);
 
  private:
+  struct JSGlobalData : public CFX_Value {
+   public:
+    JSGlobalData();
+    ~JSGlobalData();
+
+    v8::Global<v8::Object> pData;
+    bool bPersistent = false;
+    bool bDeleted = false;
+  };
+
   static int ObjDefnID;
   static const JSMethodSpec MethodSpecs[];
+
+  void UpdateGlobalPersistentVariables();
+  void CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime);
+  void DestroyGlobalPersisitentVariables();
+  CJS_Result SetGlobalVariables(const ByteString& propname,
+                                CFX_Value::DataType nType,
+                                double dData,
+                                bool bData,
+                                const ByteString& sData,
+                                v8::Local<v8::Object> pData,
+                                bool bDefaultPersistent);
+  void ObjectToArray(CJS_Runtime* pRuntime,
+                     v8::Local<v8::Object> pObj,
+                     std::vector<std::unique_ptr<CFX_KeyValue>>* pArray);
+  void PutObjectProperty(v8::Local<v8::Object> obj, CFX_KeyValue* pData);
+
+  std::map<ByteString, std::unique_ptr<JSGlobalData>> m_MapGlobal;
+  WideString m_sFilePath;
+  CFX_GlobalData* m_pGlobalData;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
 };
 
 #endif  // FXJS_CJS_GLOBAL_H_
diff --git a/fxjs/cjs_globalarrays.cpp b/fxjs/cjs_globalarrays.cpp
index 7857161..9cf540e 100644
--- a/fxjs/cjs_globalarrays.cpp
+++ b/fxjs/cjs_globalarrays.cpp
@@ -8,15 +8,16 @@
 
 #define GLOBAL_ARRAY(rt, name, ...)                                          \
   {                                                                          \
-    const wchar_t* values[] = {__VA_ARGS__};                                 \
+    static const wchar_t* const values[] = {__VA_ARGS__};                    \
     v8::Local<v8::Array> array = (rt)->NewArray();                           \
+    v8::Local<v8::Context> ctx = (rt)->GetIsolate()->GetCurrentContext();    \
     for (size_t i = 0; i < FX_ArraySize(values); ++i)                        \
-      array->Set(i, (rt)->NewString(values[i]));                             \
+      array->Set(ctx, i, (rt)->NewString(values[i])).FromJust();             \
     (rt)->SetConstArray((name), array);                                      \
     (rt)->DefineGlobalConst(                                                 \
         (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {        \
-          CJS_Runtime* pCurrentRuntime =                                     \
-              CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());     \
+          CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder());  \
+          CJS_Runtime* pCurrentRuntime = pObj->GetRuntime();                 \
           if (pCurrentRuntime)                                               \
             info.GetReturnValue().Set(pCurrentRuntime->GetConstArray(name)); \
         });                                                                  \
diff --git a/fxjs/cjs_globalarrays.h b/fxjs/cjs_globalarrays.h
index 297b97b..244db2e 100644
--- a/fxjs/cjs_globalarrays.h
+++ b/fxjs/cjs_globalarrays.h
@@ -7,9 +7,9 @@
 #ifndef FXJS_CJS_GLOBALARRAYS_H_
 #define FXJS_CJS_GLOBALARRAYS_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_GlobalArrays : public CJS_Object {
+class CJS_GlobalArrays final : public CJS_Object {
  public:
   static void DefineJSObjects(CJS_Runtime* pRuntmie);
 };
diff --git a/fxjs/cjs_globalconsts.cpp b/fxjs/cjs_globalconsts.cpp
index 5984797..cb6bd33 100644
--- a/fxjs/cjs_globalconsts.cpp
+++ b/fxjs/cjs_globalconsts.cpp
@@ -6,42 +6,44 @@
 
 #include "fxjs/cjs_globalconsts.h"
 
-#define GLOBAL_STRING(rt, name, value)                                \
-  (rt)->DefineGlobalConst(                                            \
-      (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {   \
-        info.GetReturnValue().Set(                                    \
-            CFXJS_Engine::CurrentEngineFromIsolate(info.GetIsolate()) \
-                ->NewString(value));                                  \
+#define GLOBAL_STRING(rt, name, value)                                        \
+  (rt)->DefineGlobalConst(                                                    \
+      (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {           \
+        const char* pStr = (value);                                           \
+        info.GetReturnValue().Set(                                            \
+            v8::String::NewFromUtf8(info.GetIsolate(), pStr,                  \
+                                    v8::NewStringType::kNormal, strlen(pStr)) \
+                .ToLocalChecked());                                           \
       })
 
 // static
 void CJS_GlobalConsts::DefineJSObjects(CJS_Runtime* pRuntime) {
   GLOBAL_STRING(pRuntime, L"IDS_GREATER_THAN",
-                L"Invalid value: must be greater than or equal to % s.");
+                "Invalid value: must be greater than or equal to % s.");
 
   GLOBAL_STRING(pRuntime, L"IDS_GT_AND_LT",
-                L"Invalid value: must be greater than or equal to % s "
-                L"and less than or equal to % s.");
+                "Invalid value: must be greater than or equal to % s "
+                "and less than or equal to % s.");
 
   GLOBAL_STRING(pRuntime, L"IDS_LESS_THAN",
-                L"Invalid value: must be less than or equal to % s.");
+                "Invalid value: must be less than or equal to % s.");
 
-  GLOBAL_STRING(pRuntime, L"IDS_INVALID_MONTH", L"**Invalid**");
+  GLOBAL_STRING(pRuntime, L"IDS_INVALID_MONTH", "**Invalid**");
   GLOBAL_STRING(
       pRuntime, L"IDS_INVALID_DATE",
-      L"Invalid date / time: please ensure that the date / time exists.Field");
+      "Invalid date / time: please ensure that the date / time exists.Field");
 
   GLOBAL_STRING(pRuntime, L"IDS_INVALID_VALUE",
-                L"The value entered does not match the format of the field");
+                "The value entered does not match the format of the field");
 
-  GLOBAL_STRING(pRuntime, L"IDS_AM", L"am");
-  GLOBAL_STRING(pRuntime, L"IDS_PM", L"pm");
+  GLOBAL_STRING(pRuntime, L"IDS_AM", "am");
+  GLOBAL_STRING(pRuntime, L"IDS_PM", "pm");
   GLOBAL_STRING(pRuntime, L"IDS_MONTH_INFO",
-                L"January[1] February[2] March[3] April[4] May[5] "
-                L"June[6] July[7] August[8] September[9] October[10] "
-                L"November[11] December[12] Sept[9] Jan[1] Feb[2] Mar[3] "
-                L"Apr[4] Jun[6] Jul[7] Aug[8] Sep[9] Oct[10] Nov[11] "
-                L"Dec[12]");
+                "January[1] February[2] March[3] April[4] May[5] "
+                "June[6] July[7] August[8] September[9] October[10] "
+                "November[11] December[12] Sept[9] Jan[1] Feb[2] Mar[3] "
+                "Apr[4] Jun[6] Jul[7] Aug[8] Sep[9] Oct[10] Nov[11] "
+                "Dec[12]");
 
-  GLOBAL_STRING(pRuntime, L"IDS_STARTUP_CONSOLE_MSG", L"** ^ _ ^ **");
+  GLOBAL_STRING(pRuntime, L"IDS_STARTUP_CONSOLE_MSG", "** ^ _ ^ **");
 }
diff --git a/fxjs/cjs_globalconsts.h b/fxjs/cjs_globalconsts.h
index 8c6618e..71c689b 100644
--- a/fxjs/cjs_globalconsts.h
+++ b/fxjs/cjs_globalconsts.h
@@ -7,9 +7,9 @@
 #ifndef FXJS_CJS_GLOBALCONSTS_H_
 #define FXJS_CJS_GLOBALCONSTS_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_GlobalConsts : public CJS_Object {
+class CJS_GlobalConsts final : public CJS_Object {
  public:
   static void DefineJSObjects(CJS_Runtime* pRuntime);
 };
diff --git a/fxjs/cjs_globaldata.cpp b/fxjs/cjs_globaldata.cpp
deleted file mode 100644
index a78bce5..0000000
--- a/fxjs/cjs_globaldata.cpp
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_globaldata.h"
-
-#include <utility>
-
-#include "core/fdrm/crypto/fx_crypt.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#define JS_MAXGLOBALDATA (1024 * 4 - 8)
-
-#define READER_JS_GLOBALDATA_FILENAME L"Reader_JsGlobal.Data"
-#define PHANTOM_JS_GLOBALDATA_FILENAME L"Phantom_JsGlobal.Data"
-#define SDK_JS_GLOBALDATA_FILENAME L"SDK_JsGlobal.Data"
-
-namespace {
-
-const uint8_t JS_RC4KEY[] = {
-    0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d,
-    0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51,
-    0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16,
-    0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22,
-    0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b,
-    0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a,
-    0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00,
-    0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0,
-    0xf8, 0x77, 0xd5, 0xa3};
-
-// Returns true if non-empty, setting sPropName
-bool TrimPropName(ByteString* sPropName) {
-  sPropName->Trim();
-  return sPropName->GetLength() != 0;
-}
-
-CJS_GlobalData* g_pInstance = nullptr;
-
-}  // namespace
-
-// static
-CJS_GlobalData* CJS_GlobalData::GetRetainedInstance(
-    CPDFSDK_FormFillEnvironment* pApp) {
-  if (!g_pInstance) {
-    g_pInstance = new CJS_GlobalData();
-  }
-  ++g_pInstance->m_RefCount;
-  return g_pInstance;
-}
-
-void CJS_GlobalData::Release() {
-  if (!--m_RefCount) {
-    delete g_pInstance;
-    g_pInstance = nullptr;
-  }
-}
-
-CJS_GlobalData::CJS_GlobalData()
-    : m_RefCount(0), m_sFilePath(SDK_JS_GLOBALDATA_FILENAME) {
-  LoadGlobalPersistentVariables();
-}
-
-CJS_GlobalData::~CJS_GlobalData() {
-  SaveGlobalPersisitentVariables();
-}
-
-CJS_GlobalData::iterator CJS_GlobalData::FindGlobalVariable(
-    const ByteString& propname) {
-  for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
-       ++it) {
-    if ((*it)->data.sKey == propname)
-      return it;
-  }
-  return m_arrayGlobalData.end();
-}
-
-CJS_GlobalData::const_iterator CJS_GlobalData::FindGlobalVariable(
-    const ByteString& propname) const {
-  for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
-       ++it) {
-    if ((*it)->data.sKey == propname)
-      return it;
-  }
-  return m_arrayGlobalData.end();
-}
-
-CJS_GlobalData_Element* CJS_GlobalData::GetGlobalVariable(
-    const ByteString& propname) {
-  auto iter = FindGlobalVariable(propname);
-  return iter != m_arrayGlobalData.end() ? iter->get() : nullptr;
-}
-
-void CJS_GlobalData::SetGlobalVariableNumber(const ByteString& propname,
-                                             double dData) {
-  ByteString sPropName(propname);
-  if (!TrimPropName(&sPropName))
-    return;
-
-  if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
-    pData->data.nType = JS_GlobalDataType::NUMBER;
-    pData->data.dData = dData;
-    return;
-  }
-  auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
-  pNewData->data.sKey = sPropName;
-  pNewData->data.nType = JS_GlobalDataType::NUMBER;
-  pNewData->data.dData = dData;
-  m_arrayGlobalData.push_back(std::move(pNewData));
-}
-
-void CJS_GlobalData::SetGlobalVariableBoolean(const ByteString& propname,
-                                              bool bData) {
-  ByteString sPropName(propname);
-  if (!TrimPropName(&sPropName))
-    return;
-
-  if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
-    pData->data.nType = JS_GlobalDataType::BOOLEAN;
-    pData->data.bData = bData;
-    return;
-  }
-  auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
-  pNewData->data.sKey = sPropName;
-  pNewData->data.nType = JS_GlobalDataType::BOOLEAN;
-  pNewData->data.bData = bData;
-  m_arrayGlobalData.push_back(std::move(pNewData));
-}
-
-void CJS_GlobalData::SetGlobalVariableString(const ByteString& propname,
-                                             const ByteString& sData) {
-  ByteString sPropName(propname);
-  if (!TrimPropName(&sPropName))
-    return;
-
-  if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
-    pData->data.nType = JS_GlobalDataType::STRING;
-    pData->data.sData = sData;
-    return;
-  }
-  auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
-  pNewData->data.sKey = sPropName;
-  pNewData->data.nType = JS_GlobalDataType::STRING;
-  pNewData->data.sData = sData;
-  m_arrayGlobalData.push_back(std::move(pNewData));
-}
-
-void CJS_GlobalData::SetGlobalVariableObject(
-    const ByteString& propname,
-    const CJS_GlobalVariableArray& array) {
-  ByteString sPropName(propname);
-  if (!TrimPropName(&sPropName))
-    return;
-
-  if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
-    pData->data.nType = JS_GlobalDataType::OBJECT;
-    pData->data.objData.Copy(array);
-    return;
-  }
-  auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
-  pNewData->data.sKey = sPropName;
-  pNewData->data.nType = JS_GlobalDataType::OBJECT;
-  pNewData->data.objData.Copy(array);
-  m_arrayGlobalData.push_back(std::move(pNewData));
-}
-
-void CJS_GlobalData::SetGlobalVariableNull(const ByteString& propname) {
-  ByteString sPropName(propname);
-  if (!TrimPropName(&sPropName))
-    return;
-
-  if (CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName)) {
-    pData->data.nType = JS_GlobalDataType::NULLOBJ;
-    return;
-  }
-  auto pNewData = pdfium::MakeUnique<CJS_GlobalData_Element>();
-  pNewData->data.sKey = sPropName;
-  pNewData->data.nType = JS_GlobalDataType::NULLOBJ;
-  m_arrayGlobalData.push_back(std::move(pNewData));
-}
-
-bool CJS_GlobalData::SetGlobalVariablePersistent(const ByteString& propname,
-                                                 bool bPersistent) {
-  ByteString sPropName(propname);
-  if (!TrimPropName(&sPropName))
-    return false;
-
-  CJS_GlobalData_Element* pData = GetGlobalVariable(sPropName);
-  if (!pData)
-    return false;
-
-  pData->bPersistent = bPersistent;
-  return true;
-}
-
-bool CJS_GlobalData::DeleteGlobalVariable(const ByteString& propname) {
-  ByteString sPropName(propname);
-  if (!TrimPropName(&sPropName))
-    return false;
-
-  auto iter = FindGlobalVariable(sPropName);
-  if (iter == m_arrayGlobalData.end())
-    return false;
-
-  m_arrayGlobalData.erase(iter);
-  return true;
-}
-
-int32_t CJS_GlobalData::GetSize() const {
-  return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
-}
-
-CJS_GlobalData_Element* CJS_GlobalData::GetAt(int index) const {
-  if (index < 0 || index >= GetSize())
-    return nullptr;
-  return m_arrayGlobalData[index].get();
-}
-
-void CJS_GlobalData::LoadGlobalPersistentVariables() {
-  uint8_t* pBuffer = nullptr;
-  int32_t nLength = 0;
-
-  LoadFileBuffer(m_sFilePath.c_str(), pBuffer, nLength);
-  CRYPT_ArcFourCryptBlock(pBuffer, nLength, JS_RC4KEY, sizeof(JS_RC4KEY));
-
-  if (pBuffer) {
-    uint8_t* p = pBuffer;
-    uint16_t wType = *((uint16_t*)p);
-    p += sizeof(uint16_t);
-
-    if (wType == (uint16_t)(('X' << 8) | 'F')) {
-      uint16_t wVersion = *((uint16_t*)p);
-      p += sizeof(uint16_t);
-
-      ASSERT(wVersion <= 2);
-
-      uint32_t dwCount = *((uint32_t*)p);
-      p += sizeof(uint32_t);
-
-      uint32_t dwSize = *((uint32_t*)p);
-      p += sizeof(uint32_t);
-
-      if (dwSize == nLength - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2) {
-        for (int32_t i = 0, sz = dwCount; i < sz; i++) {
-          if (p > pBuffer + nLength)
-            break;
-
-          uint32_t dwNameLen = *((uint32_t*)p);
-          p += sizeof(uint32_t);
-
-          if (p + dwNameLen > pBuffer + nLength)
-            break;
-
-          ByteString sEntry = ByteString(p, dwNameLen);
-          p += sizeof(char) * dwNameLen;
-
-          JS_GlobalDataType wDataType =
-              static_cast<JS_GlobalDataType>(*((uint16_t*)p));
-          p += sizeof(uint16_t);
-
-          switch (wDataType) {
-            case JS_GlobalDataType::NUMBER: {
-              double dData = 0;
-              switch (wVersion) {
-                case 1: {
-                  uint32_t dwData = *((uint32_t*)p);
-                  p += sizeof(uint32_t);
-                  dData = dwData;
-                } break;
-                case 2: {
-                  dData = *((double*)p);
-                  p += sizeof(double);
-                } break;
-              }
-              SetGlobalVariableNumber(sEntry, dData);
-              SetGlobalVariablePersistent(sEntry, true);
-            } break;
-            case JS_GlobalDataType::BOOLEAN: {
-              uint16_t wData = *((uint16_t*)p);
-              p += sizeof(uint16_t);
-              SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
-              SetGlobalVariablePersistent(sEntry, true);
-            } break;
-            case JS_GlobalDataType::STRING: {
-              uint32_t dwLength = *((uint32_t*)p);
-              p += sizeof(uint32_t);
-
-              if (p + dwLength > pBuffer + nLength)
-                break;
-
-              SetGlobalVariableString(sEntry, ByteString(p, dwLength));
-              SetGlobalVariablePersistent(sEntry, true);
-              p += sizeof(char) * dwLength;
-            } break;
-            case JS_GlobalDataType::NULLOBJ: {
-              SetGlobalVariableNull(sEntry);
-              SetGlobalVariablePersistent(sEntry, true);
-            }
-            case JS_GlobalDataType::OBJECT:
-              break;
-          }
-        }
-      }
-    }
-    FX_Free(pBuffer);
-  }
-}
-
-void CJS_GlobalData::SaveGlobalPersisitentVariables() {
-  uint32_t nCount = 0;
-  CFX_BinaryBuf sData;
-  for (const auto& pElement : m_arrayGlobalData) {
-    if (pElement->bPersistent) {
-      CFX_BinaryBuf sElement;
-      MakeByteString(pElement->data.sKey, &pElement->data, sElement);
-      if (sData.GetSize() + sElement.GetSize() > JS_MAXGLOBALDATA)
-        break;
-
-      sData.AppendBlock(sElement.GetBuffer(), sElement.GetSize());
-      nCount++;
-    }
-  }
-
-  CFX_BinaryBuf sFile;
-  uint16_t wType = (uint16_t)(('X' << 8) | 'F');
-  sFile.AppendBlock(&wType, sizeof(uint16_t));
-  uint16_t wVersion = 2;
-  sFile.AppendBlock(&wVersion, sizeof(uint16_t));
-  sFile.AppendBlock(&nCount, sizeof(uint32_t));
-  uint32_t dwSize = sData.GetSize();
-  sFile.AppendBlock(&dwSize, sizeof(uint32_t));
-
-  sFile.AppendBlock(sData.GetBuffer(), sData.GetSize());
-
-  CRYPT_ArcFourCryptBlock(sFile.GetBuffer(), sFile.GetSize(), JS_RC4KEY,
-                          sizeof(JS_RC4KEY));
-  WriteFileBuffer(m_sFilePath.c_str(),
-                  reinterpret_cast<char*>(sFile.GetBuffer()), sFile.GetSize());
-}
-
-void CJS_GlobalData::LoadFileBuffer(const wchar_t* sFilePath,
-                                    uint8_t*& pBuffer,
-                                    int32_t& nLength) {
-  // UnSupport.
-}
-
-void CJS_GlobalData::WriteFileBuffer(const wchar_t* sFilePath,
-                                     const char* pBuffer,
-                                     int32_t nLength) {
-  // UnSupport.
-}
-
-void CJS_GlobalData::MakeByteString(const ByteString& name,
-                                    CJS_KeyValue* pData,
-                                    CFX_BinaryBuf& sData) {
-  switch (pData->nType) {
-    case JS_GlobalDataType::NUMBER: {
-      uint32_t dwNameLen = (uint32_t)name.GetLength();
-      sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
-      sData.AppendString(name);
-      sData.AppendBlock(&pData->nType, sizeof(uint16_t));
-
-      double dData = pData->dData;
-      sData.AppendBlock(&dData, sizeof(double));
-    } break;
-    case JS_GlobalDataType::BOOLEAN: {
-      uint32_t dwNameLen = (uint32_t)name.GetLength();
-      sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
-      sData.AppendString(name);
-      sData.AppendBlock(&pData->nType, sizeof(uint16_t));
-
-      uint16_t wData = (uint16_t)pData->bData;
-      sData.AppendBlock(&wData, sizeof(uint16_t));
-    } break;
-    case JS_GlobalDataType::STRING: {
-      uint32_t dwNameLen = (uint32_t)name.GetLength();
-      sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
-      sData.AppendString(name);
-      sData.AppendBlock(&pData->nType, sizeof(uint16_t));
-
-      uint32_t dwDataLen = (uint32_t)pData->sData.GetLength();
-      sData.AppendBlock(&dwDataLen, sizeof(uint32_t));
-      sData.AppendString(pData->sData);
-    } break;
-    case JS_GlobalDataType::NULLOBJ: {
-      uint32_t dwNameLen = (uint32_t)name.GetLength();
-      sData.AppendBlock(&dwNameLen, sizeof(uint32_t));
-      sData.AppendString(name);
-      sData.AppendBlock(&pData->nType, sizeof(uint32_t));
-    } break;
-    default:
-      break;
-  }
-}
diff --git a/fxjs/cjs_globaldata.h b/fxjs/cjs_globaldata.h
deleted file mode 100644
index b9a4b21..0000000
--- a/fxjs/cjs_globaldata.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_GLOBALDATA_H_
-#define FXJS_CJS_GLOBALDATA_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/cfx_binarybuf.h"
-#include "fxjs/cjs_keyvalue.h"
-
-class CPDFSDK_FormFillEnvironment;
-
-class CJS_GlobalData_Element {
- public:
-  CJS_GlobalData_Element() {}
-  ~CJS_GlobalData_Element() {}
-
-  CJS_KeyValue data;
-  bool bPersistent;
-};
-
-class CJS_GlobalData {
- public:
-  static CJS_GlobalData* GetRetainedInstance(CPDFSDK_FormFillEnvironment* pApp);
-  void Release();
-
-  void SetGlobalVariableNumber(const ByteString& propname, double dData);
-  void SetGlobalVariableBoolean(const ByteString& propname, bool bData);
-  void SetGlobalVariableString(const ByteString& propname,
-                               const ByteString& sData);
-  void SetGlobalVariableObject(const ByteString& propname,
-                               const CJS_GlobalVariableArray& array);
-  void SetGlobalVariableNull(const ByteString& propname);
-  bool SetGlobalVariablePersistent(const ByteString& propname,
-                                   bool bPersistent);
-  bool DeleteGlobalVariable(const ByteString& propname);
-
-  int32_t GetSize() const;
-  CJS_GlobalData_Element* GetAt(int index) const;
-
- private:
-  using iterator =
-      std::vector<std::unique_ptr<CJS_GlobalData_Element>>::iterator;
-  using const_iterator =
-      std::vector<std::unique_ptr<CJS_GlobalData_Element>>::const_iterator;
-
-  CJS_GlobalData();
-  ~CJS_GlobalData();
-
-  void LoadGlobalPersistentVariables();
-  void SaveGlobalPersisitentVariables();
-
-  CJS_GlobalData_Element* GetGlobalVariable(const ByteString& sPropname);
-  iterator FindGlobalVariable(const ByteString& sPropname);
-  const_iterator FindGlobalVariable(const ByteString& sPropname) const;
-
-  void LoadFileBuffer(const wchar_t* sFilePath,
-                      uint8_t*& pBuffer,
-                      int32_t& nLength);
-  void WriteFileBuffer(const wchar_t* sFilePath,
-                       const char* pBuffer,
-                       int32_t nLength);
-  void MakeByteString(const ByteString& name,
-                      CJS_KeyValue* pData,
-                      CFX_BinaryBuf& sData);
-
-  size_t m_RefCount;
-  std::vector<std::unique_ptr<CJS_GlobalData_Element>> m_arrayGlobalData;
-  WideString m_sFilePath;
-};
-
-#endif  // FXJS_CJS_GLOBALDATA_H_
diff --git a/fxjs/cjs_globalvariablearray.cpp b/fxjs/cjs_globalvariablearray.cpp
deleted file mode 100644
index 8bf9f1b..0000000
--- a/fxjs/cjs_globalvariablearray.cpp
+++ /dev/null
@@ -1,68 +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 "fxjs/cjs_globalvariablearray.h"
-
-#include "fxjs/cjs_keyvalue.h"
-
-CJS_GlobalVariableArray::CJS_GlobalVariableArray() {}
-
-CJS_GlobalVariableArray::~CJS_GlobalVariableArray() {}
-
-void CJS_GlobalVariableArray::Copy(const CJS_GlobalVariableArray& array) {
-  m_Array.clear();
-  for (int i = 0, sz = array.Count(); i < sz; i++) {
-    CJS_KeyValue* pOldObjData = array.GetAt(i);
-    switch (pOldObjData->nType) {
-      case JS_GlobalDataType::NUMBER: {
-        CJS_KeyValue* pNewObjData = new CJS_KeyValue;
-        pNewObjData->sKey = pOldObjData->sKey;
-        pNewObjData->nType = pOldObjData->nType;
-        pNewObjData->dData = pOldObjData->dData;
-        Add(pNewObjData);
-      } break;
-      case JS_GlobalDataType::BOOLEAN: {
-        CJS_KeyValue* pNewObjData = new CJS_KeyValue;
-        pNewObjData->sKey = pOldObjData->sKey;
-        pNewObjData->nType = pOldObjData->nType;
-        pNewObjData->bData = pOldObjData->bData;
-        Add(pNewObjData);
-      } break;
-      case JS_GlobalDataType::STRING: {
-        CJS_KeyValue* pNewObjData = new CJS_KeyValue;
-        pNewObjData->sKey = pOldObjData->sKey;
-        pNewObjData->nType = pOldObjData->nType;
-        pNewObjData->sData = pOldObjData->sData;
-        Add(pNewObjData);
-      } break;
-      case JS_GlobalDataType::OBJECT: {
-        CJS_KeyValue* pNewObjData = new CJS_KeyValue;
-        pNewObjData->sKey = pOldObjData->sKey;
-        pNewObjData->nType = pOldObjData->nType;
-        pNewObjData->objData.Copy(pOldObjData->objData);
-        Add(pNewObjData);
-      } break;
-      case JS_GlobalDataType::NULLOBJ: {
-        CJS_KeyValue* pNewObjData = new CJS_KeyValue;
-        pNewObjData->sKey = pOldObjData->sKey;
-        pNewObjData->nType = pOldObjData->nType;
-        Add(pNewObjData);
-      } break;
-    }
-  }
-}
-
-void CJS_GlobalVariableArray::Add(CJS_KeyValue* p) {
-  m_Array.push_back(std::unique_ptr<CJS_KeyValue>(p));
-}
-
-int CJS_GlobalVariableArray::Count() const {
-  return m_Array.size();
-}
-
-CJS_KeyValue* CJS_GlobalVariableArray::GetAt(int index) const {
-  return m_Array.at(index).get();
-}
diff --git a/fxjs/cjs_globalvariablearray.h b/fxjs/cjs_globalvariablearray.h
deleted file mode 100644
index a249f60..0000000
--- a/fxjs/cjs_globalvariablearray.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_GLOBALVARIABLEARRAY_H_
-#define FXJS_CJS_GLOBALVARIABLEARRAY_H_
-
-#include <memory>
-#include <vector>
-
-class CJS_KeyValue;
-
-class CJS_GlobalVariableArray {
- public:
-  CJS_GlobalVariableArray();
-  ~CJS_GlobalVariableArray();
-
-  void Add(CJS_KeyValue* p);
-  int Count() const;
-  CJS_KeyValue* GetAt(int index) const;
-  void Copy(const CJS_GlobalVariableArray& array);
-
- private:
-  std::vector<std::unique_ptr<CJS_KeyValue>> m_Array;
-};
-
-#endif  // FXJS_CJS_GLOBALVARIABLEARRAY_H_
diff --git a/fxjs/cjs_highlight.cpp b/fxjs/cjs_highlight.cpp
index e60a5a8..4ada397 100644
--- a/fxjs/cjs_highlight.cpp
+++ b/fxjs/cjs_highlight.cpp
@@ -18,5 +18,5 @@
 void CJS_Highlight::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID =
       pEngine->DefineObj("highlight", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_highlight.h b/fxjs/cjs_highlight.h
index 74091d3..efec127 100644
--- a/fxjs/cjs_highlight.h
+++ b/fxjs/cjs_highlight.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_HIGHLIGHT_H_
 #define FXJS_CJS_HIGHLIGHT_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_Highlight : public CJS_Object {
+class CJS_Highlight final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Highlight(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Highlight() override {}
+  CJS_Highlight() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_icon.cpp b/fxjs/cjs_icon.cpp
index 2b56f70..80be5c8 100644
--- a/fxjs/cjs_icon.cpp
+++ b/fxjs/cjs_icon.cpp
@@ -10,6 +10,7 @@
     {"name", get_name_static, set_name_static}};
 
 int CJS_Icon::ObjDefnID = -1;
+const char CJS_Icon::kName[] = "Icon";
 
 // static
 int CJS_Icon::GetObjDefnID() {
@@ -18,21 +19,20 @@
 
 // static
 void CJS_Icon::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID =
-      pEngine->DefineObj("Icon", FXJSOBJTYPE_DYNAMIC,
-                         JSConstructor<CJS_Icon, Icon>, JSDestructor<CJS_Icon>);
-  DefineProps(pEngine, ObjDefnID, PropertySpecs, FX_ArraySize(PropertySpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_Icon::kName, FXJSOBJTYPE_DYNAMIC,
+                                 JSConstructor<CJS_Icon>, JSDestructor);
+  DefineProps(pEngine, ObjDefnID, PropertySpecs);
 }
 
-Icon::Icon(CJS_Object* pJSObject)
-    : CJS_EmbedObj(pJSObject), m_swIconName(L"") {}
+CJS_Icon::CJS_Icon(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-Icon::~Icon() {}
+CJS_Icon::~CJS_Icon() = default;
 
-CJS_Return Icon::get_name(CJS_Runtime* pRuntime) {
-  return CJS_Return(pRuntime->NewString(m_swIconName.c_str()));
+CJS_Result CJS_Icon::get_name(CJS_Runtime* pRuntime) {
+  return CJS_Result::Success(pRuntime->NewString(m_swIconName.AsStringView()));
 }
 
-CJS_Return Icon::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  return CJS_Return(false);
+CJS_Result CJS_Icon::set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
+  return CJS_Result::Failure(JSMessage::kReadOnlyError);
 }
diff --git a/fxjs/cjs_icon.h b/fxjs/cjs_icon.h
index 05b8438..321f508 100644
--- a/fxjs/cjs_icon.h
+++ b/fxjs/cjs_icon.h
@@ -7,36 +7,31 @@
 #ifndef FXJS_CJS_ICON_H_
 #define FXJS_CJS_ICON_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
-class Icon : public CJS_EmbedObj {
- public:
-  explicit Icon(CJS_Object* pJSObject);
-  ~Icon() override;
-
-  CJS_Return get_name(CJS_Runtime* pRuntime);
-  CJS_Return set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
-
-  WideString GetIconName() const { return m_swIconName; }
-  void SetIconName(WideString name) { m_swIconName = name; }
-
- private:
-  WideString m_swIconName;
-};
-
-class CJS_Icon : public CJS_Object {
+class CJS_Icon final : public CJS_Object {
  public:
   static int GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Icon(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Icon() override {}
+  CJS_Icon(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Icon() override;
 
-  JS_STATIC_PROP(name, name, Icon);
+  WideString GetIconName() const { return m_swIconName; }
+  void SetIconName(WideString name) { m_swIconName = name; }
+
+  JS_STATIC_PROP(name, name, CJS_Icon)
 
  private:
   static int ObjDefnID;
+  static const char kName[];
   static const JSPropertySpec PropertySpecs[];
+
+  CJS_Result get_name(CJS_Runtime* pRuntime);
+  CJS_Result set_name(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
+
+  WideString m_swIconName;
 };
 
 #endif  // FXJS_CJS_ICON_H_
diff --git a/fxjs/cjs_keyvalue.cpp b/fxjs/cjs_keyvalue.cpp
deleted file mode 100644
index 4fda3d6..0000000
--- a/fxjs/cjs_keyvalue.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_keyvalue.h"
-
-CJS_KeyValue::CJS_KeyValue() {}
-
-CJS_KeyValue::~CJS_KeyValue() {}
diff --git a/fxjs/cjs_keyvalue.h b/fxjs/cjs_keyvalue.h
deleted file mode 100644
index a1fd314..0000000
--- a/fxjs/cjs_keyvalue.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_KEYVALUE_H_
-#define FXJS_CJS_KEYVALUE_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "fxjs/cjs_globalvariablearray.h"
-
-enum class JS_GlobalDataType { NUMBER = 0, BOOLEAN, STRING, OBJECT, NULLOBJ };
-
-class CJS_KeyValue {
- public:
-  CJS_KeyValue();
-  ~CJS_KeyValue();
-
-  ByteString sKey;
-  JS_GlobalDataType nType;
-  double dData;
-  bool bData;
-  ByteString sData;
-  CJS_GlobalVariableArray objData;
-};
-
-#endif  // FXJS_CJS_KEYVALUE_H_
diff --git a/fxjs/cjs_object.cpp b/fxjs/cjs_object.cpp
index a1e5ce1..e13a509 100644
--- a/fxjs/cjs_object.cpp
+++ b/fxjs/cjs_object.cpp
@@ -6,45 +6,40 @@
 
 #include "fxjs/cjs_object.h"
 
+#include "fxjs/cfxjs_engine.h"
+
 // static
 void CJS_Object::DefineConsts(CFXJS_Engine* pEngine,
                               int objId,
-                              const JSConstSpec consts[],
-                              size_t count) {
-  for (size_t i = 0; i < count; ++i) {
+                              pdfium::span<const JSConstSpec> consts) {
+  for (const auto& item : consts) {
     pEngine->DefineObjConst(
-        objId, consts[i].pName,
-        consts[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(consts[i].number).As<v8::Value>()
-            : pEngine->NewString(consts[i].pStr).As<v8::Value>());
+        objId, item.pName,
+        item.eType == JSConstSpec::Number
+            ? pEngine->NewNumber(item.number).As<v8::Value>()
+            : pEngine->NewString(item.pStr).As<v8::Value>());
   }
 }
 
 // static
 void CJS_Object::DefineProps(CFXJS_Engine* pEngine,
                              int objId,
-                             const JSPropertySpec props[],
-                             size_t count) {
-  for (size_t i = 0; i < count; ++i) {
-    pEngine->DefineObjProperty(objId, props[i].pName, props[i].pPropGet,
-                               props[i].pPropPut);
-  }
+                             pdfium::span<const JSPropertySpec> props) {
+  for (const auto& item : props)
+    pEngine->DefineObjProperty(objId, item.pName, item.pPropGet, item.pPropPut);
 }
 
 // static
 void CJS_Object::DefineMethods(CFXJS_Engine* pEngine,
                                int objId,
-                               const JSMethodSpec methods[],
-                               size_t count) {
-  for (size_t i = 0; i < count; ++i)
-    pEngine->DefineObjMethod(objId, methods[i].pName, methods[i].pMethodCall);
+                               pdfium::span<const JSMethodSpec> methods) {
+  for (const auto& item : methods)
+    pEngine->DefineObjMethod(objId, item.pName, item.pMethodCall);
 }
 
-CJS_Object::CJS_Object(v8::Local<v8::Object> pObject) {
-  m_pIsolate = pObject->GetIsolate();
-  m_pV8Object.Reset(m_pIsolate, pObject);
-}
+CJS_Object::CJS_Object(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : m_pIsolate(pObject->GetIsolate()),
+      m_pV8Object(GetIsolate(), pObject),
+      m_pRuntime(pRuntime) {}
 
 CJS_Object::~CJS_Object() {}
-
-void CJS_Object::InitInstance(IJS_Runtime* pIRuntime) {}
diff --git a/fxjs/cjs_object.h b/fxjs/cjs_object.h
index d929a01..5da72ff 100644
--- a/fxjs/cjs_object.h
+++ b/fxjs/cjs_object.h
@@ -7,12 +7,11 @@
 #ifndef FXJS_CJS_OBJECT_H_
 #define FXJS_CJS_OBJECT_H_
 
-#include <memory>
-
-#include "fpdfsdk/fsdk_define.h"
-#include "fxjs/cjs_embedobj.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cjs_runtime.h"
-#include "fxjs/fxjs_v8.h"
+#include "third_party/base/span.h"
+
+class CFXJS_Engine;
 
 struct JSConstSpec {
   enum Type { Number = 0, String = 1 };
@@ -38,34 +37,25 @@
  public:
   static void DefineConsts(CFXJS_Engine* pEngine,
                            int objId,
-                           const JSConstSpec consts[],
-                           size_t count);
+                           pdfium::span<const JSConstSpec> consts);
   static void DefineProps(CFXJS_Engine* pEngine,
                           int objId,
-                          const JSPropertySpec props[],
-                          size_t count);
+                          pdfium::span<const JSPropertySpec> consts);
   static void DefineMethods(CFXJS_Engine* pEngine,
                             int objId,
-                            const JSMethodSpec methods[],
-                            size_t count);
+                            pdfium::span<const JSMethodSpec> consts);
 
-  explicit CJS_Object(v8::Local<v8::Object> pObject);
+  CJS_Object(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
   virtual ~CJS_Object();
 
-  virtual void InitInstance(IJS_Runtime* pIRuntime);
+  v8::Local<v8::Object> ToV8Object() { return m_pV8Object.Get(GetIsolate()); }
+  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  CJS_Runtime* GetRuntime() const { return m_pRuntime.Get(); }
 
-  v8::Local<v8::Object> ToV8Object() { return m_pV8Object.Get(m_pIsolate); }
-
-  // Takes ownership of |pObj|.
-  void SetEmbedObject(CJS_EmbedObj* pObj) { m_pEmbedObj.reset(pObj); }
-  CJS_EmbedObj* GetEmbedObject() const { return m_pEmbedObj.get(); }
-
-  v8::Isolate* GetIsolate() const { return m_pIsolate; }
-
- protected:
-  std::unique_ptr<CJS_EmbedObj> m_pEmbedObj;
+ private:
+  UnownedPtr<v8::Isolate> m_pIsolate;
   v8::Global<v8::Object> m_pV8Object;
-  v8::Isolate* m_pIsolate;
+  ObservedPtr<CJS_Runtime> m_pRuntime;
 };
 
 #endif  // FXJS_CJS_OBJECT_H_
diff --git a/fxjs/cjs_position.cpp b/fxjs/cjs_position.cpp
index 16e4ab0..dc9594e 100644
--- a/fxjs/cjs_position.cpp
+++ b/fxjs/cjs_position.cpp
@@ -21,5 +21,5 @@
 void CJS_Position::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID =
       pEngine->DefineObj("position", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_position.h b/fxjs/cjs_position.h
index 7557f01..ae951bf 100644
--- a/fxjs/cjs_position.h
+++ b/fxjs/cjs_position.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_POSITION_H_
 #define FXJS_CJS_POSITION_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_Position : public CJS_Object {
+class CJS_Position final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Position(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Position() override {}
+  CJS_Position() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_printparamsobj.cpp b/fxjs/cjs_printparamsobj.cpp
deleted file mode 100644
index 296c241..0000000
--- a/fxjs/cjs_printparamsobj.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_printparamsobj.h"
-
-int CJS_PrintParamsObj::ObjDefnID = -1;
-
-// static
-int CJS_PrintParamsObj::GetObjDefnID() {
-  return ObjDefnID;
-}
-
-// static
-void CJS_PrintParamsObj::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID =
-      pEngine->DefineObj("PrintParamsObj", FXJSOBJTYPE_DYNAMIC,
-                         JSConstructor<CJS_PrintParamsObj, PrintParamsObj>,
-                         JSDestructor<CJS_PrintParamsObj>);
-}
-
-PrintParamsObj::PrintParamsObj(CJS_Object* pJSObject)
-    : CJS_EmbedObj(pJSObject) {
-  bUI = true;
-  nStart = 0;
-  nEnd = 0;
-  bSilent = false;
-  bShrinkToFit = false;
-  bPrintAsImage = false;
-  bReverse = false;
-  bAnnotations = true;
-}
diff --git a/fxjs/cjs_printparamsobj.h b/fxjs/cjs_printparamsobj.h
deleted file mode 100644
index a0c91b0..0000000
--- a/fxjs/cjs_printparamsobj.h
+++ /dev/null
@@ -1,41 +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 FXJS_CJS_PRINTPARAMSOBJ_H_
-#define FXJS_CJS_PRINTPARAMSOBJ_H_
-
-#include "fxjs/JS_Define.h"
-
-class PrintParamsObj : public CJS_EmbedObj {
- public:
-  explicit PrintParamsObj(CJS_Object* pJSObject);
-  ~PrintParamsObj() override {}
-
- public:
-  bool bUI;
-  int nStart;
-  int nEnd;
-  bool bSilent;
-  bool bShrinkToFit;
-  bool bPrintAsImage;
-  bool bReverse;
-  bool bAnnotations;
-};
-
-class CJS_PrintParamsObj : public CJS_Object {
- public:
-  static int GetObjDefnID();
-  static void DefineJSObjects(CFXJS_Engine* pEngine);
-
-  explicit CJS_PrintParamsObj(v8::Local<v8::Object> pObject)
-      : CJS_Object(pObject) {}
-  ~CJS_PrintParamsObj() override {}
-
- private:
-  static int ObjDefnID;
-};
-
-#endif  // FXJS_CJS_PRINTPARAMSOBJ_H_
diff --git a/fxjs/cjs_publicmethods.cpp b/fxjs/cjs_publicmethods.cpp
index 45eae7f..8eed97d 100644
--- a/fxjs/cjs_publicmethods.cpp
+++ b/fxjs/cjs_publicmethods.cpp
@@ -10,65 +10,81 @@
 #include <cmath>
 #include <cwctype>
 #include <iomanip>
+#include <iterator>
 #include <limits>
 #include <sstream>
 #include <string>
+#include <utility>
 #include <vector>
 
-#include "core/fpdfdoc/cpdf_interform.h"
+#include "build/build_config.h"
+#include "core/fpdfdoc/cpdf_formcontrol.h"
+#include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxge/cfx_color.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fpdfsdk/cpdfsdk_interform.h"
-#include "fxjs/JS_Define.h"
+#include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fxjs/cjs_color.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventhandler.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/cjs_util.h"
+#include "fxjs/fx_date_helpers.h"
+#include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/optional.h"
 
 // static
 const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = {
+    {"AFDate_Format", AFDate_Format_static},
+    {"AFDate_FormatEx", AFDate_FormatEx_static},
+    {"AFDate_Keystroke", AFDate_Keystroke_static},
+    {"AFDate_KeystrokeEx", AFDate_KeystrokeEx_static},
+    {"AFExtractNums", AFExtractNums_static},
+    {"AFMakeNumber", AFMakeNumber_static},
+    {"AFMergeChange", AFMergeChange_static},
     {"AFNumber_Format", AFNumber_Format_static},
     {"AFNumber_Keystroke", AFNumber_Keystroke_static},
+    {"AFParseDateEx", AFParseDateEx_static},
     {"AFPercent_Format", AFPercent_Format_static},
     {"AFPercent_Keystroke", AFPercent_Keystroke_static},
-    {"AFDate_FormatEx", AFDate_FormatEx_static},
-    {"AFDate_KeystrokeEx", AFDate_KeystrokeEx_static},
-    {"AFDate_Format", AFDate_Format_static},
-    {"AFDate_Keystroke", AFDate_Keystroke_static},
-    {"AFTime_FormatEx", AFTime_FormatEx_static},
-    {"AFTime_KeystrokeEx", AFTime_KeystrokeEx_static},
-    {"AFTime_Format", AFTime_Format_static},
-    {"AFTime_Keystroke", AFTime_Keystroke_static},
+    {"AFRange_Validate", AFRange_Validate_static},
+    {"AFSimple", AFSimple_static},
+    {"AFSimple_Calculate", AFSimple_Calculate_static},
     {"AFSpecial_Format", AFSpecial_Format_static},
     {"AFSpecial_Keystroke", AFSpecial_Keystroke_static},
     {"AFSpecial_KeystrokeEx", AFSpecial_KeystrokeEx_static},
-    {"AFSimple", AFSimple_static},
-    {"AFMakeNumber", AFMakeNumber_static},
-    {"AFSimple_Calculate", AFSimple_Calculate_static},
-    {"AFRange_Validate", AFRange_Validate_static},
-    {"AFMergeChange", AFMergeChange_static},
-    {"AFParseDateEx", AFParseDateEx_static},
-    {"AFExtractNums", AFExtractNums_static},
+    {"AFTime_Format", AFTime_Format_static},
+    {"AFTime_FormatEx", AFTime_FormatEx_static},
+    {"AFTime_Keystroke", AFTime_Keystroke_static},
+    {"AFTime_KeystrokeEx", AFTime_KeystrokeEx_static},
 };
 
 namespace {
 
-#if _FX_OS_ != _FX_OS_ANDROID_
+#if !defined(OS_ANDROID)
 constexpr double kDoubleCorrect = 0.000000000000001;
 #endif
 
-const wchar_t* const kMonths[] = {L"Jan", L"Feb", L"Mar", L"Apr",
-                                  L"May", L"Jun", L"Jul", L"Aug",
-                                  L"Sep", L"Oct", L"Nov", L"Dec"};
+constexpr const wchar_t* kDateFormats[] = {L"m/d",
+                                           L"m/d/yy",
+                                           L"mm/dd/yy",
+                                           L"mm/yy",
+                                           L"d-mmm",
+                                           L"d-mmm-yy",
+                                           L"dd-mmm-yy",
+                                           L"yy-mm-dd",
+                                           L"mmm-yy",
+                                           L"mmmm-yy",
+                                           L"mmm d, yyyy",
+                                           L"mmmm d, yyyy",
+                                           L"m/d/yy h:MM tt",
+                                           L"m/d/yy HH:MM"};
 
-const wchar_t* const kFullMonths[] = {L"January", L"February", L"March",
-                                      L"April",   L"May",      L"June",
-                                      L"July",    L"August",   L"September",
-                                      L"October", L"November", L"December"};
+constexpr const wchar_t* kTimeFormats[] = {L"HH:MM", L"h:MM tt", L"HH:MM:ss",
+                                           L"h:MM:ss tt"};
 
 template <typename T>
 T StrTrim(const T& str) {
@@ -77,13 +93,14 @@
   return result;
 }
 
-void AlertIfPossible(CJS_EventContext* pContext, const wchar_t* swMsg) {
+void AlertIfPossible(CJS_EventContext* pContext, const WideString& swMsg) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pContext->GetFormFillEnv();
   if (pFormFillEnv)
-    pFormFillEnv->JS_appAlert(swMsg, nullptr, 0, 3);
+    pFormFillEnv->JS_appAlert(swMsg, WideString(), JSPLATFORM_ALERT_BUTTON_OK,
+                              JSPLATFORM_ALERT_ICON_STATUS);
 }
 
-#if _FX_OS_ != _FX_OS_ANDROID_
+#if !defined(OS_ANDROID)
 ByteString CalculateString(double dValue,
                            int iDec,
                            int* iDec2,
@@ -104,23 +121,26 @@
 }
 #endif
 
-WideString CalcMergedString(const CJS_EventHandler* event,
+WideString CalcMergedString(const CJS_EventRecorder* event,
                             const WideString& value,
                             const WideString& change) {
-  WideString prefix = value.Left(event->SelStart());
+  WideString prefix = value.First(event->SelStart());
   WideString postfix;
   int end = event->SelEnd();
   if (end >= 0 && static_cast<size_t>(end) < value.GetLength())
-    postfix = value.Right(value.GetLength() - static_cast<size_t>(end));
+    postfix = value.Last(value.GetLength() - static_cast<size_t>(end));
   return prefix + change + postfix;
 }
 
-template <CJS_Return (*F)(CJS_Runtime*,
+template <CJS_Result (*F)(CJS_Runtime*,
                           const std::vector<v8::Local<v8::Value>>&)>
 void JSGlobalFunc(const char* func_name_string,
                   const v8::FunctionCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
   if (!pRuntime)
     return;
 
@@ -128,7 +148,7 @@
   for (int i = 0; i < info.Length(); ++i)
     parameters.push_back(info[i]);
 
-  CJS_Return result = (*F)(pRuntime, parameters);
+  CJS_Result result = (*F)(pRuntime, parameters);
   if (result.HasError()) {
     pRuntime->Error(
         JSFormatErrorString(func_name_string, nullptr, result.Error()));
@@ -151,7 +171,7 @@
   return c == '.' || c == ',';
 }
 
-#if _FX_OS_ != _FX_OS_ANDROID_
+#if !defined(OS_ANDROID)
 bool IsStyleWithDigitSeparator(int style) {
   return style == 0 || style == 2;
 }
@@ -160,17 +180,21 @@
   ASSERT(IsStyleWithDigitSeparator(style));
   return style == 0 ? ',' : '.';
 }
+
+bool IsStyleWithApostropheSeparator(int style) {
+  return style >= 4;
+}
 #endif
 
 bool IsStyleWithCommaDecimalMark(int style) {
-  return style >= 2;
+  return style == 2 || style == 3;
 }
 
 char DecimalMarkForStyle(int style) {
   return IsStyleWithCommaDecimalMark(style) ? ',' : '.';
 }
 
-#if _FX_OS_ != _FX_OS_ANDROID_
+#if !defined(OS_ANDROID)
 void NormalizeDecimalMark(ByteString* str) {
   str->Replace(",", ".");
 }
@@ -180,35 +204,24 @@
   str->Replace(L",", L".");
 }
 
-bool IsValidMonth(int m) {
-  return m >= 1 && m <= 12;
-}
-
-// TODO(thestig): Should this take the month into consideration?
-bool IsValidDay(int d) {
-  return d >= 1 && d <= 31;
-}
-
-// TODO(thestig): Should 24 be allowed? Similarly, 60 for minutes and seconds.
-bool IsValid24Hour(int h) {
-  return h >= 0 && h <= 24;
-}
-
-bool IsValidMinute(int m) {
-  return m >= 0 && m <= 60;
-}
-
-bool IsValidSecond(int s) {
-  return s >= 0 && s <= 60;
+Optional<double> ApplyNamedOperation(const wchar_t* sFunction,
+                                     double dValue1,
+                                     double dValue2) {
+  if (FXSYS_wcsicmp(sFunction, L"AVG") == 0 ||
+      FXSYS_wcsicmp(sFunction, L"SUM") == 0) {
+    return dValue1 + dValue2;
+  }
+  if (FXSYS_wcsicmp(sFunction, L"PRD") == 0)
+    return dValue1 * dValue2;
+  if (FXSYS_wcsicmp(sFunction, L"MIN") == 0)
+    return std::min(dValue1, dValue2);
+  if (FXSYS_wcsicmp(sFunction, L"MAX") == 0)
+    return std::max(dValue1, dValue2);
+  return {};
 }
 
 }  // namespace
 
-CJS_PublicMethods::CJS_PublicMethods(v8::Local<v8::Object> pObject)
-    : CJS_Object(pObject) {}
-
-CJS_PublicMethods::~CJS_PublicMethods() {}
-
 // static
 void CJS_PublicMethods::DefineJSObjects(CFXJS_Engine* pEngine) {
   for (const auto& spec : GlobalFunctionSpecs)
@@ -221,28 +234,28 @@
     JSGlobalFunc<fun_name>(#fun_name, info);             \
   }
 
-JS_STATIC_GLOBAL_FUN(AFNumber_Format);
-JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke);
-JS_STATIC_GLOBAL_FUN(AFPercent_Format);
-JS_STATIC_GLOBAL_FUN(AFPercent_Keystroke);
-JS_STATIC_GLOBAL_FUN(AFDate_FormatEx);
-JS_STATIC_GLOBAL_FUN(AFDate_KeystrokeEx);
-JS_STATIC_GLOBAL_FUN(AFDate_Format);
-JS_STATIC_GLOBAL_FUN(AFDate_Keystroke);
-JS_STATIC_GLOBAL_FUN(AFTime_FormatEx);
-JS_STATIC_GLOBAL_FUN(AFTime_KeystrokeEx);
-JS_STATIC_GLOBAL_FUN(AFTime_Format);
-JS_STATIC_GLOBAL_FUN(AFTime_Keystroke);
-JS_STATIC_GLOBAL_FUN(AFSpecial_Format);
-JS_STATIC_GLOBAL_FUN(AFSpecial_Keystroke);
-JS_STATIC_GLOBAL_FUN(AFSpecial_KeystrokeEx);
-JS_STATIC_GLOBAL_FUN(AFSimple);
-JS_STATIC_GLOBAL_FUN(AFMakeNumber);
-JS_STATIC_GLOBAL_FUN(AFSimple_Calculate);
-JS_STATIC_GLOBAL_FUN(AFRange_Validate);
-JS_STATIC_GLOBAL_FUN(AFMergeChange);
-JS_STATIC_GLOBAL_FUN(AFParseDateEx);
-JS_STATIC_GLOBAL_FUN(AFExtractNums);
+JS_STATIC_GLOBAL_FUN(AFNumber_Format)
+JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke)
+JS_STATIC_GLOBAL_FUN(AFPercent_Format)
+JS_STATIC_GLOBAL_FUN(AFPercent_Keystroke)
+JS_STATIC_GLOBAL_FUN(AFDate_FormatEx)
+JS_STATIC_GLOBAL_FUN(AFDate_KeystrokeEx)
+JS_STATIC_GLOBAL_FUN(AFDate_Format)
+JS_STATIC_GLOBAL_FUN(AFDate_Keystroke)
+JS_STATIC_GLOBAL_FUN(AFTime_FormatEx)
+JS_STATIC_GLOBAL_FUN(AFTime_KeystrokeEx)
+JS_STATIC_GLOBAL_FUN(AFTime_Format)
+JS_STATIC_GLOBAL_FUN(AFTime_Keystroke)
+JS_STATIC_GLOBAL_FUN(AFSpecial_Format)
+JS_STATIC_GLOBAL_FUN(AFSpecial_Keystroke)
+JS_STATIC_GLOBAL_FUN(AFSpecial_KeystrokeEx)
+JS_STATIC_GLOBAL_FUN(AFSimple)
+JS_STATIC_GLOBAL_FUN(AFMakeNumber)
+JS_STATIC_GLOBAL_FUN(AFSimple_Calculate)
+JS_STATIC_GLOBAL_FUN(AFRange_Validate)
+JS_STATIC_GLOBAL_FUN(AFMergeChange)
+JS_STATIC_GLOBAL_FUN(AFParseDateEx)
+JS_STATIC_GLOBAL_FUN(AFExtractNums)
 
 bool CJS_PublicMethods::IsNumber(const WideString& str) {
   WideString sTrim = StrTrim(str);
@@ -269,7 +282,7 @@
       if (c != L'+' && c != L'-')
         return false;
       bKXJS = true;
-    } else if (!std::iswdigit(c)) {
+    } else if (!FXSYS_IsDecimalDigit(c)) {
       return false;
     }
     p++;
@@ -281,11 +294,11 @@
 bool CJS_PublicMethods::MaskSatisfied(wchar_t c_Change, wchar_t c_Mask) {
   switch (c_Mask) {
     case L'9':
-      return !!std::iswdigit(c_Change);
+      return !!FXSYS_IsDecimalDigit(c_Change);
     case L'A':
-      return FXSYS_iswalpha(c_Change);
+      return isascii(c_Change) && isalpha(c_Change);
     case L'O':
-      return FXSYS_iswalnum(c_Change);
+      return isascii(c_Change) && isalnum(c_Change);
     case L'X':
       return true;
     default:
@@ -297,30 +310,16 @@
   return ch == L'9' || ch == L'A' || ch == L'O' || ch == L'X';
 }
 
-double CJS_PublicMethods::AF_Simple(const wchar_t* sFuction,
-                                    double dValue1,
-                                    double dValue2) {
-  if (FXSYS_wcsicmp(sFuction, L"AVG") == 0 ||
-      FXSYS_wcsicmp(sFuction, L"SUM") == 0) {
-    return dValue1 + dValue2;
-  }
-  if (FXSYS_wcsicmp(sFuction, L"PRD") == 0)
-    return dValue1 * dValue2;
-  if (FXSYS_wcsicmp(sFuction, L"MIN") == 0)
-    return std::min(dValue1, dValue2);
-  if (FXSYS_wcsicmp(sFuction, L"MAX") == 0)
-    return std::max(dValue1, dValue2);
-  return dValue1;
-}
-
 v8::Local<v8::Array> CJS_PublicMethods::AF_MakeArrayFromList(
     CJS_Runtime* pRuntime,
     v8::Local<v8::Value> val) {
-  if (!val.IsEmpty() && val->IsArray())
+  ASSERT(!val.IsEmpty());
+  if (val->IsArray())
     return pRuntime->ToArray(val);
 
+  ASSERT(val->IsString());
   WideString wsStr = pRuntime->ToWideString(val);
-  ByteString t = ByteString::FromUnicode(wsStr);
+  ByteString t = wsStr.ToDefANSI();
   const char* p = t.c_str();
 
   int nIndex = 0;
@@ -330,13 +329,13 @@
     if (!pTemp) {
       pRuntime->PutArrayElement(
           StrArray, nIndex,
-          pRuntime->NewString(StrTrim(ByteString(p)).c_str()));
+          pRuntime->NewString(StrTrim(ByteString(p)).AsStringView()));
       break;
     }
 
     pRuntime->PutArrayElement(
         StrArray, nIndex,
-        pRuntime->NewString(StrTrim(ByteString(p, pTemp - p)).c_str()));
+        pRuntime->NewString(StrTrim(ByteString(p, pTemp - p)).AsStringView()));
 
     nIndex++;
     p = ++pTemp;
@@ -344,57 +343,15 @@
   return StrArray;
 }
 
-int CJS_PublicMethods::ParseStringInteger(const WideString& str,
-                                          size_t nStart,
-                                          size_t* pSkip,
-                                          size_t nMaxStep) {
-  int nRet = 0;
-  size_t nSkip = 0;
-  for (size_t i = nStart; i < str.GetLength(); ++i) {
-    if (i - nStart > 10)
-      break;
-
-    wchar_t c = str[i];
-    if (!std::iswdigit(c))
-      break;
-
-    nRet = nRet * 10 + FXSYS_DecimalCharToInt(c);
-    ++nSkip;
-    if (nSkip >= nMaxStep)
-      break;
-  }
-
-  *pSkip = nSkip;
-  return nRet;
-}
-
-WideString CJS_PublicMethods::ParseStringString(const WideString& str,
-                                                size_t nStart,
-                                                size_t* pSkip) {
-  WideString swRet;
-  swRet.Reserve(str.GetLength());
-  for (size_t i = nStart; i < str.GetLength(); ++i) {
-    wchar_t c = str[i];
-    if (!std::iswdigit(c))
-      break;
-
-    swRet += c;
-  }
-
-  *pSkip = swRet.GetLength();
-  return swRet;
-}
-
-double CJS_PublicMethods::ParseNormalDate(const WideString& value,
-                                          bool* bWrongFormat) {
-  double dt = JS_GetDateTime();
-
-  int nYear = JS_GetYearFromTime(dt);
-  int nMonth = JS_GetMonthFromTime(dt) + 1;
-  int nDay = JS_GetDayFromTime(dt);
-  int nHour = JS_GetHourFromTime(dt);
-  int nMin = JS_GetMinFromTime(dt);
-  int nSec = JS_GetSecFromTime(dt);
+double CJS_PublicMethods::ParseDate(const WideString& value,
+                                    bool* bWrongFormat) {
+  double dt = FX_GetDateTime();
+  int nYear = FX_GetYearFromTime(dt);
+  int nMonth = FX_GetMonthFromTime(dt) + 1;
+  int nDay = FX_GetDayFromTime(dt);
+  int nHour = FX_GetHourFromTime(dt);
+  int nMin = FX_GetMinFromTime(dt);
+  int nSec = FX_GetSecFromTime(dt);
 
   int number[3];
 
@@ -407,8 +364,8 @@
       break;
 
     wchar_t c = value[i];
-    if (std::iswdigit(c)) {
-      number[nIndex++] = ParseStringInteger(value, i, &nSkip, 4);
+    if (FXSYS_IsDecimalDigit(c)) {
+      number[nIndex++] = FX_ParseStringInteger(value, i, &nSkip, 4);
       i += nSkip;
     } else {
       i++;
@@ -419,10 +376,10 @@
     // TODO(thestig): Should the else case set |bWrongFormat| to true?
     // case2: month/day
     // case3: day/month
-    if (IsValidMonth(number[0]) && IsValidDay(number[1])) {
+    if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1])) {
       nMonth = number[0];
       nDay = number[1];
-    } else if (IsValidDay(number[0]) && IsValidMonth(number[1])) {
+    } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1])) {
       nDay = number[0];
       nMonth = number[1];
     }
@@ -434,16 +391,17 @@
     // case1: year/month/day
     // case2: month/day/year
     // case3: day/month/year
-    if (number[0] > 12 && IsValidMonth(number[1]) && IsValidDay(number[2])) {
+    if (number[0] > 12 && FX_IsValidMonth(number[1]) &&
+        FX_IsValidDay(number[2])) {
       nYear = number[0];
       nMonth = number[1];
       nDay = number[2];
-    } else if (IsValidMonth(number[0]) && IsValidDay(number[1]) &&
+    } else if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1]) &&
                number[2] > 31) {
       nMonth = number[0];
       nDay = number[1];
       nYear = number[2];
-    } else if (IsValidDay(number[0]) && IsValidMonth(number[1]) &&
+    } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1]) &&
                number[2] > 31) {
       nDay = number[0];
       nMonth = number[1];
@@ -463,280 +421,39 @@
                                          nYear, nHour, nMin, nSec));
 }
 
-double CJS_PublicMethods::MakeRegularDate(const WideString& value,
-                                          const WideString& format,
-                                          bool* bWrongFormat) {
-  double dt = JS_GetDateTime();
+double CJS_PublicMethods::ParseDateUsingFormat(const WideString& value,
+                                               const WideString& format,
+                                               bool* bWrongFormat) {
+  double dRet = std::nan("");
+  fxjs::ConversionStatus status = FX_ParseDateUsingFormat(value, format, &dRet);
+  if (status == fxjs::ConversionStatus::kSuccess)
+    return dRet;
 
-  if (format.IsEmpty() || value.IsEmpty())
-    return dt;
+  if (status == fxjs::ConversionStatus::kBadDate) {
+    dRet = JS_DateParse(value);
+    if (!std::isnan(dRet))
+      return dRet;
+  }
 
-  int nYear = JS_GetYearFromTime(dt);
-  int nMonth = JS_GetMonthFromTime(dt) + 1;
-  int nDay = JS_GetDayFromTime(dt);
-  int nHour = JS_GetHourFromTime(dt);
-  int nMin = JS_GetMinFromTime(dt);
-  int nSec = JS_GetSecFromTime(dt);
-
-  int nYearSub = 99;  // nYear - 2000;
-
-  bool bPm = false;
-  bool bExit = false;
   bool bBadFormat = false;
-
-  size_t i = 0;
-  size_t j = 0;
-
-  while (i < format.GetLength()) {
-    if (bExit)
-      break;
-
-    wchar_t c = format[i];
-    switch (c) {
-      case ':':
-      case '.':
-      case '-':
-      case '\\':
-      case '/':
-        i++;
-        j++;
-        break;
-
-      case 'y':
-      case 'm':
-      case 'd':
-      case 'H':
-      case 'h':
-      case 'M':
-      case 's':
-      case 't': {
-        size_t oldj = j;
-        size_t nSkip = 0;
-        size_t remaining = format.GetLength() - i - 1;
-
-        if (remaining == 0 || format[i + 1] != c) {
-          switch (c) {
-            case 'y':
-              i++;
-              j++;
-              break;
-            case 'm':
-              nMonth = ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'd':
-              nDay = ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'H':
-              nHour = ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'h':
-              nHour = ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 'M':
-              nMin = ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 's':
-              nSec = ParseStringInteger(value, j, &nSkip, 2);
-              i++;
-              j += nSkip;
-              break;
-            case 't':
-              bPm = (j < value.GetLength() && value[j] == 'p');
-              i++;
-              j++;
-              break;
-          }
-        } else if (remaining == 1 || format[i + 2] != c) {
-          switch (c) {
-            case 'y':
-              nYear = ParseStringInteger(value, j, &nSkip, 4);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'm':
-              nMonth = ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'd':
-              nDay = ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'H':
-              nHour = ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'h':
-              nHour = ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 'M':
-              nMin = ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 's':
-              nSec = ParseStringInteger(value, j, &nSkip, 2);
-              i += 2;
-              j += nSkip;
-              break;
-            case 't':
-              bPm = (j + 1 < value.GetLength() && value[j] == 'p' &&
-                     value[j + 1] == 'm');
-              i += 2;
-              j += 2;
-              break;
-          }
-        } else if (remaining == 2 || format[i + 3] != c) {
-          switch (c) {
-            case 'm': {
-              WideString sMonth = ParseStringString(value, j, &nSkip);
-              bool bFind = false;
-              for (int m = 0; m < 12; m++) {
-                if (sMonth.CompareNoCase(kMonths[m]) == 0) {
-                  nMonth = m + 1;
-                  i += 3;
-                  j += nSkip;
-                  bFind = true;
-                  break;
-                }
-              }
-
-              if (!bFind) {
-                nMonth = ParseStringInteger(value, j, &nSkip, 3);
-                i += 3;
-                j += nSkip;
-              }
-            } break;
-            case 'y':
-              break;
-            default:
-              i += 3;
-              j += 3;
-              break;
-          }
-        } else if (remaining == 3 || format[i + 4] != c) {
-          switch (c) {
-            case 'y':
-              nYear = ParseStringInteger(value, j, &nSkip, 4);
-              j += nSkip;
-              i += 4;
-              break;
-            case 'm': {
-              bool bFind = false;
-
-              WideString sMonth = ParseStringString(value, j, &nSkip);
-              sMonth.MakeLower();
-
-              for (int m = 0; m < 12; m++) {
-                WideString sFullMonths = kFullMonths[m];
-                sFullMonths.MakeLower();
-
-                if (sFullMonths.Contains(sMonth.c_str())) {
-                  nMonth = m + 1;
-                  i += 4;
-                  j += nSkip;
-                  bFind = true;
-                  break;
-                }
-              }
-
-              if (!bFind) {
-                nMonth = ParseStringInteger(value, j, &nSkip, 4);
-                i += 4;
-                j += nSkip;
-              }
-            } break;
-            default:
-              i += 4;
-              j += 4;
-              break;
-          }
-        } else {
-          if (j >= value.GetLength() || format[i] != value[j]) {
-            bBadFormat = true;
-            bExit = true;
-          }
-          i++;
-          j++;
-        }
-
-        if (oldj == j) {
-          bBadFormat = true;
-          bExit = true;
-        }
-        break;
-      }
-
-      default:
-        if (value.GetLength() <= j) {
-          bExit = true;
-        } else if (format[i] != value[j]) {
-          bBadFormat = true;
-          bExit = true;
-        }
-
-        i++;
-        j++;
-        break;
-    }
-  }
-
-  if (bPm)
-    nHour += 12;
-
-  if (nYear >= 0 && nYear <= nYearSub)
-    nYear += 2000;
-
-  if (!bBadFormat) {
-    bBadFormat = !IsValidMonth(nMonth) || !IsValidDay(nDay) ||
-                 !IsValid24Hour(nHour) || !IsValidMinute(nMin) ||
-                 !IsValidSecond(nSec);
-  }
-
-  double dRet;
-  if (bBadFormat) {
-    dRet = ParseNormalDate(value, &bBadFormat);
-  } else {
-    dRet = JS_MakeDate(JS_MakeDay(nYear, nMonth - 1, nDay),
-                       JS_MakeTime(nHour, nMin, nSec, 0));
-    if (std::isnan(dRet))
-      dRet = JS_DateParse(value);
-  }
-
-  if (std::isnan(dRet))
-    dRet = ParseNormalDate(value, &bBadFormat);
-
+  dRet = ParseDate(value, &bBadFormat);
   if (bWrongFormat)
     *bWrongFormat = bBadFormat;
 
   return dRet;
 }
 
-WideString CJS_PublicMethods::MakeFormatDate(double dDate,
-                                             const WideString& format) {
+WideString CJS_PublicMethods::PrintDateUsingFormat(double dDate,
+                                                   const WideString& format) {
   WideString sRet;
   WideString sPart;
 
-  int nYear = JS_GetYearFromTime(dDate);
-  int nMonth = JS_GetMonthFromTime(dDate) + 1;
-  int nDay = JS_GetDayFromTime(dDate);
-  int nHour = JS_GetHourFromTime(dDate);
-  int nMin = JS_GetMinFromTime(dDate);
-  int nSec = JS_GetSecFromTime(dDate);
+  int nYear = FX_GetYearFromTime(dDate);
+  int nMonth = FX_GetMonthFromTime(dDate) + 1;
+  int nDay = FX_GetDayFromTime(dDate);
+  int nHour = FX_GetHourFromTime(dDate);
+  int nMin = FX_GetMinFromTime(dDate);
+  int nSec = FX_GetSecFromTime(dDate);
 
   size_t i = 0;
   while (i < format.GetLength()) {
@@ -814,8 +531,8 @@
           switch (c) {
             case 'm':
               i += 3;
-              if (IsValidMonth(nMonth))
-                sPart += kMonths[nMonth - 1];
+              if (FX_IsValidMonth(nMonth))
+                sPart += fxjs::kMonths[nMonth - 1];
               break;
             default:
               i += 3;
@@ -832,8 +549,8 @@
               break;
             case 'm':
               i += 4;
-              if (IsValidMonth(nMonth))
-                sPart += kFullMonths[nMonth - 1];
+              if (FX_IsValidMonth(nMonth))
+                sPart += fxjs::kFullMonths[nMonth - 1];
               break;
             default:
               i += 4;
@@ -862,22 +579,22 @@
 
 // function AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency,
 // bCurrencyPrepend)
-CJS_Return CJS_PublicMethods::AFNumber_Format(
+CJS_Result CJS_PublicMethods::AFNumber_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-#if _FX_OS_ != _FX_OS_ANDROID_
+#if !defined(OS_ANDROID)
   if (params.size() != 6)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventContext* pEventContext = pRuntime->GetCurrentEventContext();
+  CJS_EventRecorder* pEvent = pEventContext->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(WideString::FromASCII("No event handler"));
 
   WideString& Value = pEvent->Value();
-  ByteString strValue = StrTrim(ByteString::FromUnicode(Value));
+  ByteString strValue = StrTrim(Value.ToDefANSI());
   if (strValue.IsEmpty())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   int iDec = abs(pRuntime->ToInt32(params[0]));
   int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1]));
@@ -921,7 +638,7 @@
   }
 
   // Processing currency string
-  Value = WideString::FromLocal(strValue.AsStringView());
+  Value = WideString::FromDefANSI(strValue.AsStringView());
   if (bCurrencyPrepend)
     Value = wstrCurrency + Value;
   else
@@ -936,9 +653,9 @@
       Value += L')';
     }
     if (iNegStyle == 1 || iNegStyle == 3) {
-      if (Field* fTarget = pEvent->Target_Field()) {
+      if (CJS_Field* fTarget = pEventContext->TargetField()) {
         v8::Local<v8::Array> arColor = pRuntime->NewArray();
-        pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString(L"RGB"));
+        pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString("RGB"));
         pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(1));
         pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0));
         pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0));
@@ -947,38 +664,39 @@
     }
   } else {
     if (iNegStyle == 1 || iNegStyle == 3) {
-      if (Field* fTarget = pEvent->Target_Field()) {
+      if (CJS_Field* fTarget = pEventContext->TargetField()) {
         v8::Local<v8::Array> arColor = pRuntime->NewArray();
-        pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString(L"RGB"));
+        pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString("RGB"));
         pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(0));
         pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0));
         pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0));
 
-        CJS_Return result = fTarget->get_text_color(pRuntime);
-        CFX_Color crProp = color::ConvertArrayToPWLColor(
+        CJS_Result result = fTarget->get_text_color(pRuntime);
+        CFX_Color crProp = CJS_Color::ConvertArrayToPWLColor(
             pRuntime, pRuntime->ToArray(result.Return()));
-        CFX_Color crColor = color::ConvertArrayToPWLColor(pRuntime, arColor);
+        CFX_Color crColor =
+            CJS_Color::ConvertArrayToPWLColor(pRuntime, arColor);
         if (crColor != crProp)
           fTarget->set_text_color(pRuntime, arColor);
       }
     }
   }
 #endif
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
 // function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency,
 // bCurrencyPrepend)
-CJS_Return CJS_PublicMethods::AFNumber_Keystroke(
+CJS_Result CJS_PublicMethods::AFNumber_Keystroke(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 2)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventHandler* pEvent = pContext->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   WideString& val = pEvent->Value();
   WideString& wstrChange = pEvent->Change();
@@ -987,31 +705,31 @@
   if (pEvent->WillCommit()) {
     WideString swTemp = StrTrim(wstrValue);
     if (swTemp.IsEmpty())
-      return CJS_Return(true);
+      return CJS_Result::Success();
 
     NormalizeDecimalMarkW(&swTemp);
-    if (!IsNumber(swTemp.c_str())) {
+    if (!IsNumber(swTemp)) {
       pEvent->Rc() = false;
       WideString sError = JSGetStringFromID(JSMessage::kInvalidInputError);
-      AlertIfPossible(pContext, sError.c_str());
-      return CJS_Return(sError);
+      AlertIfPossible(pContext, sError);
+      return CJS_Result::Failure(sError);
     }
     // It happens after the last keystroke and before validating,
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   WideString wstrSelected;
   if (pEvent->SelStart() != -1) {
-    wstrSelected = wstrValue.Mid(pEvent->SelStart(),
-                                 pEvent->SelEnd() - pEvent->SelStart());
+    wstrSelected = wstrValue.Substr(pEvent->SelStart(),
+                                    pEvent->SelEnd() - pEvent->SelStart());
   }
 
   bool bHasSign = wstrValue.Contains(L'-') && !wstrSelected.Contains(L'-');
   if (bHasSign) {
-    // can't insert "change" in front to sign postion.
-    if (pEvent->SelStart() == 0) {
+    // can't insert "change" in front of sign position.
+    if (!wstrSelected.IsEmpty() && pEvent->SelStart() == 0) {
       pEvent->Rc() = false;
-      return CJS_Return(true);
+      return CJS_Result::Success();
     }
   }
 
@@ -1023,7 +741,7 @@
     if (wstrChange[i] == cSep) {
       if (bHasSep) {
         pEvent->Rc() = false;
-        return CJS_Return(true);
+        return CJS_Result::Success();
       }
       bHasSep = true;
       continue;
@@ -1031,185 +749,195 @@
     if (wstrChange[i] == L'-') {
       if (bHasSign) {
         pEvent->Rc() = false;
-        return CJS_Return(true);
+        return CJS_Result::Success();
       }
       // sign's position is not correct
       if (i != 0) {
         pEvent->Rc() = false;
-        return CJS_Return(true);
+        return CJS_Result::Success();
       }
       if (pEvent->SelStart() != 0) {
         pEvent->Rc() = false;
-        return CJS_Return(true);
+        return CJS_Result::Success();
       }
       bHasSign = true;
       continue;
     }
 
-    if (!std::iswdigit(wstrChange[i])) {
+    if (!FXSYS_IsDecimalDigit(wstrChange[i])) {
       pEvent->Rc() = false;
-      return CJS_Return(true);
+      return CJS_Result::Success();
     }
   }
 
   val = CalcMergedString(pEvent, wstrValue, wstrChange);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-// function AFPercent_Format(nDec, sepStyle)
-CJS_Return CJS_PublicMethods::AFPercent_Format(
+// function AFPercent_Format(nDec, sepStyle, bPercentPrepend)
+CJS_Result CJS_PublicMethods::AFPercent_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-#if _FX_OS_ != _FX_OS_ANDROID_
-  if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+#if !defined(OS_ANDROID)
+  if (params.size() < 2)
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
+  // Acrobat will accept this. Anything larger causes it to throw an error.
+  static constexpr int kMaxSepStyle = 49;
+
+  int iDec = pRuntime->ToInt32(params[0]);
+  int iSepStyle = pRuntime->ToInt32(params[1]);
+  // TODO(thestig): How do we handle negative raw |bPercentPrepend| values?
+  bool bPercentPrepend = params.size() > 2 && pRuntime->ToBoolean(params[2]);
+  if (iDec < 0 || iSepStyle < 0 || iSepStyle > kMaxSepStyle)
+    return CJS_Result::Failure(JSMessage::kValueError);
+
+  // When the |iDec| value is too big, Acrobat will just return "%".
+  static constexpr int kDecLimit = 512;
+  // TODO(thestig): Calculate this once C++14 can be used to declare variables
+  // in constexpr functions.
+  static constexpr size_t kDigitsInDecLimit = 3;
   WideString& Value = pEvent->Value();
-  ByteString strValue = StrTrim(ByteString::FromUnicode(Value));
-  if (strValue.IsEmpty())
-    return CJS_Return(true);
+  if (iDec > kDecLimit) {
+    Value = L"%";
+    return CJS_Result::Success();
+  }
 
-  int iDec = abs(pRuntime->ToInt32(params[0]));
-  int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1]));
+  ByteString strValue = StrTrim(Value.ToDefANSI());
+  if (strValue.IsEmpty())
+    strValue = "0";
 
   // for processing decimal places
   double dValue = atof(strValue.c_str());
   dValue *= 100;
-  if (iDec > 0)
-    dValue += kDoubleCorrect;
 
-  int iDec2;
-  int iNegative = 0;
-  strValue = fcvt(dValue, iDec, &iDec2, &iNegative);
-  if (strValue.IsEmpty()) {
-    dValue = 0;
-    strValue = fcvt(dValue, iDec, &iDec2, &iNegative);
-  }
+  size_t szNewSize;
+  {
+    // Figure out the format to use with FXSYS_snprintf() below.
+    // |format| is small because |iDec| is limited in size.
+    char format[sizeof("%.f") + kDigitsInDecLimit];  // e.g. "%.512f"
+    FXSYS_snprintf(format, sizeof(format), "%%.%df", iDec);
 
-  if (iDec2 < 0) {
-    ByteString zeros;
-    char* zeros_ptr = zeros.GetBuffer(abs(iDec2));
-    memset(zeros_ptr, '0', abs(iDec2));
-    strValue = zeros + strValue;
-
-    iDec2 = 0;
-  }
-  int iMax = strValue.GetLength();
-  if (iDec2 > iMax) {
-    for (int iNum = 0; iNum <= iDec2 - iMax; iNum++)
-      strValue += '0';
-
-    iMax = iDec2 + 1;
-  }
-
-  // for processing seperator style
-  if (iDec2 < iMax) {
-    char mark = DecimalMarkForStyle(iSepStyle);
-    strValue.Insert(iDec2, mark);
-    iMax++;
-
-    if (iDec2 == 0)
-      strValue.Insert(iDec2, '0');
-  }
-  if (IsStyleWithDigitSeparator(iSepStyle)) {
-    char cSeparator = DigitSeparatorForStyle(iSepStyle);
-    for (int iDecPositive = iDec2 - 3; iDecPositive > 0; iDecPositive -= 3) {
-      strValue.Insert(iDecPositive, cSeparator);
-      iMax++;
+    // Calculate the new size for |strValue| and get a span.
+    size_t szBufferSize = iDec + 3;  // Negative sign, decimal point, and NUL.
+    double dValueCopy = fabs(dValue);
+    while (dValueCopy > 1) {
+      dValueCopy /= 10;
+      ++szBufferSize;
     }
+
+    // Write into |strValue|.
+    pdfium::span<char> span = strValue.GetBuffer(szBufferSize);
+    FXSYS_snprintf(span.data(), szBufferSize, format, dValue);
+    szNewSize = strlen(span.data());
+  }
+  strValue.ReleaseBuffer(szNewSize);
+
+  // for processing separator style
+  Optional<size_t> mark_pos = strValue.Find('.');
+  if (mark_pos.has_value()) {
+    char mark = DecimalMarkForStyle(iSepStyle);
+    if (mark != '.')
+      strValue.SetAt(mark_pos.value(), mark);
+  }
+  bool bUseDigitSeparator = IsStyleWithDigitSeparator(iSepStyle);
+  if (bUseDigitSeparator || IsStyleWithApostropheSeparator(iSepStyle)) {
+    char cSeparator =
+        bUseDigitSeparator ? DigitSeparatorForStyle(iSepStyle) : '\'';
+    int iEnd = mark_pos.value_or(strValue.GetLength());
+    int iStop = dValue < 0 ? 1 : 0;
+    for (int i = iEnd - 3; i > iStop; i -= 3)
+      strValue.Insert(i, cSeparator);
   }
 
-  // negative mark
-  if (iNegative)
-    strValue.InsertAtFront('-');
-
-  strValue += '%';
-  Value = WideString::FromLocal(strValue.AsStringView());
+  if (bPercentPrepend)
+    strValue.InsertAtFront('%');
+  else
+    strValue.InsertAtBack('%');
+  Value = WideString::FromDefANSI(strValue.AsStringView());
 #endif
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
 // AFPercent_Keystroke(nDec, sepStyle)
-CJS_Return CJS_PublicMethods::AFPercent_Keystroke(
+CJS_Result CJS_PublicMethods::AFPercent_Keystroke(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return AFNumber_Keystroke(pRuntime, params);
 }
 
 // function AFDate_FormatEx(cFormat)
-CJS_Return CJS_PublicMethods::AFDate_FormatEx(
+CJS_Result CJS_PublicMethods::AFDate_FormatEx(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventHandler* pEvent = pContext->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   WideString& val = pEvent->Value();
   WideString strValue = val;
   if (strValue.IsEmpty())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   WideString sFormat = pRuntime->ToWideString(params[0]);
   double dDate;
   if (strValue.Contains(L"GMT")) {
-    // for GMT format time
-    // such as "Tue Aug 11 14:24:16 GMT+08002009"
-    dDate = MakeInterDate(strValue);
+    // e.g. "Tue Aug 11 14:24:16 GMT+08002009"
+    dDate = ParseDateAsGMT(strValue);
   } else {
-    dDate = MakeRegularDate(strValue, sFormat, nullptr);
+    dDate = ParseDateUsingFormat(strValue, sFormat, nullptr);
   }
 
   if (std::isnan(dDate)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pContext, swMsg.c_str());
-    return CJS_Return(false);
+    AlertIfPossible(pContext, swMsg);
+    return CJS_Result::Failure(JSMessage::kParseDateError);
   }
 
-  val = MakeFormatDate(dDate, sFormat);
-  return CJS_Return(true);
+  val = PrintDateUsingFormat(dDate, sFormat);
+  return CJS_Result::Success();
 }
 
-double CJS_PublicMethods::MakeInterDate(const WideString& strValue) {
+double CJS_PublicMethods::ParseDateAsGMT(const WideString& strValue) {
   std::vector<WideString> wsArray;
   WideString sTemp;
   for (const auto& c : strValue) {
     if (c == L' ' || c == L':') {
-      wsArray.push_back(sTemp);
-      sTemp.clear();
+      wsArray.push_back(std::move(sTemp));
       continue;
     }
     sTemp += c;
   }
-  wsArray.push_back(sTemp);
+  wsArray.push_back(std::move(sTemp));
   if (wsArray.size() != 8)
     return 0;
 
   int nMonth = 1;
   sTemp = wsArray[1];
-  for (size_t i = 0; i < FX_ArraySize(kMonths); ++i) {
-    if (sTemp.Compare(kMonths[i]) == 0) {
+  for (size_t i = 0; i < FX_ArraySize(fxjs::kMonths); ++i) {
+    if (sTemp.Compare(fxjs::kMonths[i]) == 0) {
       nMonth = i + 1;
       break;
     }
   }
 
-  int nDay = FX_atof(wsArray[2].AsStringView());
-  int nHour = FX_atof(wsArray[3].AsStringView());
-  int nMin = FX_atof(wsArray[4].AsStringView());
-  int nSec = FX_atof(wsArray[5].AsStringView());
-  int nYear = FX_atof(wsArray[7].AsStringView());
-  double dRet = JS_MakeDate(JS_MakeDay(nYear, nMonth - 1, nDay),
-                            JS_MakeTime(nHour, nMin, nSec, 0));
+  int nDay = StringToFloat(wsArray[2].AsStringView());
+  int nHour = StringToFloat(wsArray[3].AsStringView());
+  int nMin = StringToFloat(wsArray[4].AsStringView());
+  int nSec = StringToFloat(wsArray[5].AsStringView());
+  int nYear = StringToFloat(wsArray[7].AsStringView());
+  double dRet = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
+                            FX_MakeTime(nHour, nMin, nSec, 0));
   if (std::isnan(dRet))
     dRet = JS_DateParse(strValue);
 
@@ -1217,152 +945,115 @@
 }
 
 // AFDate_KeystrokeEx(cFormat)
-CJS_Return CJS_PublicMethods::AFDate_KeystrokeEx(
+CJS_Result CJS_PublicMethods::AFDate_KeystrokeEx(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1) {
-    return CJS_Return(
-        WideString(L"AFDate_KeystrokeEx's parameters' size r not correct"));
+    return CJS_Result::Failure(WideString::FromASCII(
+        "AFDate_KeystrokeEx's parameter size not correct"));
   }
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventHandler* pEvent = pContext->GetEventHandler();
-  if (pEvent->WillCommit()) {
-    if (!pEvent->m_pValue)
-      return CJS_Return(false);
+  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  if (!pEvent->WillCommit())
+    return CJS_Result::Success();
 
-    const WideString& strValue = pEvent->Value();
-    if (strValue.IsEmpty())
-      return CJS_Return(true);
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    WideString sFormat = pRuntime->ToWideString(params[0]);
-    bool bWrongFormat = false;
-    double dRet = MakeRegularDate(strValue, sFormat, &bWrongFormat);
-    if (bWrongFormat || std::isnan(dRet)) {
-      WideString swMsg = WideString::Format(
-          JSGetStringFromID(JSMessage::kParseDateError).c_str(),
-          sFormat.c_str());
-      AlertIfPossible(pContext, swMsg.c_str());
-      pEvent->Rc() = false;
-      return CJS_Return(true);
-    }
+  const WideString& strValue = pEvent->Value();
+  if (strValue.IsEmpty())
+    return CJS_Result::Success();
+
+  bool bWrongFormat = false;
+  WideString sFormat = pRuntime->ToWideString(params[0]);
+  double dRet = ParseDateUsingFormat(strValue, sFormat, &bWrongFormat);
+  if (bWrongFormat || std::isnan(dRet)) {
+    WideString swMsg = WideString::Format(
+        JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
+    AlertIfPossible(pContext, swMsg);
+    pEvent->Rc() = false;
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJS_PublicMethods::AFDate_Format(
+CJS_Result CJS_PublicMethods::AFDate_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  static constexpr const wchar_t* cFormats[] = {L"m/d",
-                                                L"m/d/yy",
-                                                L"mm/dd/yy",
-                                                L"mm/yy",
-                                                L"d-mmm",
-                                                L"d-mmm-yy",
-                                                L"dd-mmm-yy",
-                                                L"yy-mm-dd",
-                                                L"mmm-yy",
-                                                L"mmmm-yy",
-                                                L"mmm d, yyyy",
-                                                L"mmmm d, yyyy",
-                                                L"m/d/yy h:MM tt",
-                                                L"m/d/yy HH:MM"};
-
-  int iIndex =
-      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(cFormats));
+  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
+                                  FX_ArraySize(kDateFormats));
   std::vector<v8::Local<v8::Value>> newParams;
-  newParams.push_back(pRuntime->NewString(cFormats[iIndex]));
+  newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
   return AFDate_FormatEx(pRuntime, newParams);
 }
 
 // AFDate_KeystrokeEx(cFormat)
-CJS_Return CJS_PublicMethods::AFDate_Keystroke(
+CJS_Result CJS_PublicMethods::AFDate_Keystroke(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  static constexpr const wchar_t* cFormats[] = {L"m/d",
-                                                L"m/d/yy",
-                                                L"mm/dd/yy",
-                                                L"mm/yy",
-                                                L"d-mmm",
-                                                L"d-mmm-yy",
-                                                L"dd-mmm-yy",
-                                                L"yy-mm-dd",
-                                                L"mmm-yy",
-                                                L"mmmm-yy",
-                                                L"mmm d, yyyy",
-                                                L"mmmm d, yyyy",
-                                                L"m/d/yy h:MM tt",
-                                                L"m/d/yy HH:MM"};
-
-  int iIndex =
-      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(cFormats));
+  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
+                                  FX_ArraySize(kDateFormats));
   std::vector<v8::Local<v8::Value>> newParams;
-  newParams.push_back(pRuntime->NewString(cFormats[iIndex]));
+  newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
   return AFDate_KeystrokeEx(pRuntime, newParams);
 }
 
 // function AFTime_Format(ptf)
-CJS_Return CJS_PublicMethods::AFTime_Format(
+CJS_Result CJS_PublicMethods::AFTime_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  static constexpr const wchar_t* cFormats[] = {L"HH:MM", L"h:MM tt",
-                                                L"HH:MM:ss", L"h:MM:ss tt"};
-
-  int iIndex =
-      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(cFormats));
+  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
+                                  FX_ArraySize(kTimeFormats));
   std::vector<v8::Local<v8::Value>> newParams;
-  newParams.push_back(pRuntime->NewString(cFormats[iIndex]));
+  newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
   return AFDate_FormatEx(pRuntime, newParams);
 }
 
-CJS_Return CJS_PublicMethods::AFTime_Keystroke(
+CJS_Result CJS_PublicMethods::AFTime_Keystroke(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  static constexpr const wchar_t* cFormats[] = {L"HH:MM", L"h:MM tt",
-                                                L"HH:MM:ss", L"h:MM:ss tt"};
-
-  int iIndex =
-      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(cFormats));
+  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
+                                  FX_ArraySize(kTimeFormats));
   std::vector<v8::Local<v8::Value>> newParams;
-  newParams.push_back(pRuntime->NewString(cFormats[iIndex]));
+  newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
   return AFDate_KeystrokeEx(pRuntime, newParams);
 }
 
-CJS_Return CJS_PublicMethods::AFTime_FormatEx(
+CJS_Result CJS_PublicMethods::AFTime_FormatEx(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return AFDate_FormatEx(pRuntime, params);
 }
 
-CJS_Return CJS_PublicMethods::AFTime_KeystrokeEx(
+CJS_Result CJS_PublicMethods::AFTime_KeystrokeEx(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return AFDate_KeystrokeEx(pRuntime, params);
 }
 
 // function AFSpecial_Format(psf)
-CJS_Return CJS_PublicMethods::AFSpecial_Format(
+CJS_Result CJS_PublicMethods::AFSpecial_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   const WideString& wsSource = pEvent->Value();
   WideString wsFormat;
@@ -1374,7 +1065,7 @@
       wsFormat = L"99999-9999";
       break;
     case 2:
-      if (util::printx(L"9999999999", wsSource).GetLength() >= 10)
+      if (CJS_Util::StringPrintx(L"9999999999", wsSource).GetLength() >= 10)
         wsFormat = L"(999) 999-9999";
       else
         wsFormat = L"999-9999";
@@ -1384,74 +1075,77 @@
       break;
   }
 
-  pEvent->Value() = util::printx(wsFormat, wsSource);
-  return CJS_Return(true);
+  pEvent->Value() = CJS_Util::StringPrintx(wsFormat, wsSource);
+  return CJS_Result::Success();
 }
 
 // function AFSpecial_KeystrokeEx(mask)
-CJS_Return CJS_PublicMethods::AFSpecial_KeystrokeEx(
+CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventHandler* pEvent = pContext->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   const WideString& valEvent = pEvent->Value();
   WideString wstrMask = pRuntime->ToWideString(params[0]);
   if (wstrMask.IsEmpty())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (pEvent->WillCommit()) {
     if (valEvent.IsEmpty())
-      return CJS_Return(true);
+      return CJS_Result::Success();
 
-    size_t iIndexMask = 0;
-    for (; iIndexMask < valEvent.GetLength(); ++iIndexMask) {
-      if (!MaskSatisfied(valEvent[iIndexMask], wstrMask[iIndexMask]))
+    if (valEvent.GetLength() > wstrMask.GetLength()) {
+      AlertIfPossible(pContext,
+                      JSGetStringFromID(JSMessage::kParamTooLongError));
+      pEvent->Rc() = false;
+      return CJS_Result::Success();
+    }
+
+    size_t iIndex = 0;
+    for (iIndex = 0; iIndex < valEvent.GetLength(); ++iIndex) {
+      if (!MaskSatisfied(valEvent[iIndex], wstrMask[iIndex]))
         break;
     }
-
-    if (iIndexMask != wstrMask.GetLength() ||
-        (iIndexMask != valEvent.GetLength() && wstrMask.GetLength() != 0)) {
+    if (iIndex != wstrMask.GetLength()) {
       AlertIfPossible(pContext,
-                      JSGetStringFromID(JSMessage::kInvalidInputError).c_str());
+                      JSGetStringFromID(JSMessage::kInvalidInputError));
       pEvent->Rc() = false;
     }
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   WideString& wideChange = pEvent->Change();
   if (wideChange.IsEmpty())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   WideString wChange = wideChange;
   size_t iIndexMask = pEvent->SelStart();
   size_t combined_len = valEvent.GetLength() + wChange.GetLength() +
                         pEvent->SelStart() - pEvent->SelEnd();
   if (combined_len > wstrMask.GetLength()) {
-    AlertIfPossible(pContext,
-                    JSGetStringFromID(JSMessage::kParamTooLongError).c_str());
+    AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError));
     pEvent->Rc() = false;
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   if (iIndexMask >= wstrMask.GetLength() && !wChange.IsEmpty()) {
-    AlertIfPossible(pContext,
-                    JSGetStringFromID(JSMessage::kParamTooLongError).c_str());
+    AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError));
     pEvent->Rc() = false;
-    return CJS_Return(true);
+    return CJS_Result::Success();
   }
 
   for (size_t i = 0; i < wChange.GetLength(); ++i) {
     if (iIndexMask >= wstrMask.GetLength()) {
       AlertIfPossible(pContext,
-                      JSGetStringFromID(JSMessage::kParamTooLongError).c_str());
+                      JSGetStringFromID(JSMessage::kParamTooLongError));
       pEvent->Rc() = false;
-      return CJS_Return(true);
+      return CJS_Result::Success();
     }
     wchar_t wMask = wstrMask[iIndexMask];
     if (!IsReservedMaskChar(wMask))
@@ -1459,25 +1153,25 @@
 
     if (!MaskSatisfied(wChange[i], wMask)) {
       pEvent->Rc() = false;
-      return CJS_Return(true);
+      return CJS_Result::Success();
     }
     iIndexMask++;
   }
-  wideChange = wChange;
-  return CJS_Return(true);
+  wideChange = std::move(wChange);
+  return CJS_Result::Success();
 }
 
 // function AFSpecial_Keystroke(psf)
-CJS_Return CJS_PublicMethods::AFSpecial_Keystroke(
+CJS_Result CJS_PublicMethods::AFSpecial_Keystroke(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventHandler* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventRecorder* pEvent =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   const char* cFormat = "";
   switch (pRuntime->ToInt32(params[0])) {
@@ -1503,97 +1197,110 @@
   return AFSpecial_KeystrokeEx(pRuntime, params2);
 }
 
-CJS_Return CJS_PublicMethods::AFMergeChange(
+CJS_Result CJS_PublicMethods::AFMergeChange(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventHandler* pEventHandler =
-      pRuntime->GetCurrentEventContext()->GetEventHandler();
+  CJS_EventRecorder* pEventRecorder =
+      pRuntime->GetCurrentEventContext()->GetEventRecorder();
 
   WideString swValue;
-  if (pEventHandler->m_pValue)
-    swValue = pEventHandler->Value();
+  if (pEventRecorder->HasValue())
+    swValue = pEventRecorder->Value();
 
-  if (pEventHandler->WillCommit())
-    return CJS_Return(pRuntime->NewString(swValue.c_str()));
+  if (pEventRecorder->WillCommit())
+    return CJS_Result::Success(pRuntime->NewString(swValue.AsStringView()));
 
-  WideString merged =
-      CalcMergedString(pEventHandler, swValue, pEventHandler->Change());
-  return CJS_Return(pRuntime->NewString(merged.c_str()));
+  return CJS_Result::Success(pRuntime->NewString(
+      CalcMergedString(pEventRecorder, swValue, pEventRecorder->Change())
+          .AsStringView()));
 }
 
-CJS_Return CJS_PublicMethods::AFParseDateEx(
+CJS_Result CJS_PublicMethods::AFParseDateEx(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString sValue = pRuntime->ToWideString(params[0]);
   WideString sFormat = pRuntime->ToWideString(params[1]);
-  double dDate = MakeRegularDate(sValue, sFormat, nullptr);
+  double dDate = ParseDateUsingFormat(sValue, sFormat, nullptr);
   if (std::isnan(dDate)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pRuntime->GetCurrentEventContext(), swMsg.c_str());
-    return CJS_Return(false);
+    AlertIfPossible(pRuntime->GetCurrentEventContext(), swMsg);
+    return CJS_Result::Failure(JSMessage::kParseDateError);
   }
-  return CJS_Return(pRuntime->NewNumber(dDate));
+  return CJS_Result::Success(pRuntime->NewNumber(dDate));
 }
 
-CJS_Return CJS_PublicMethods::AFSimple(
+CJS_Result CJS_PublicMethods::AFSimple(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 3)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  return CJS_Return(pRuntime->NewNumber(static_cast<double>(AF_Simple(
-      pRuntime->ToWideString(params[0]).c_str(), pRuntime->ToDouble(params[1]),
-      pRuntime->ToDouble(params[2])))));
+  WideString sFunction = pRuntime->ToWideString(params[0]);
+  double arg1 = pRuntime->ToDouble(params[1]);
+  double arg2 = pRuntime->ToDouble(params[2]);
+  if (std::isnan(arg1) || std::isnan(arg2))
+    return CJS_Result::Failure(JSMessage::kValueError);
+
+  Optional<double> result = ApplyNamedOperation(sFunction.c_str(), arg1, arg2);
+  if (!result.has_value())
+    return CJS_Result::Failure(JSMessage::kValueError);
+
+  double dValue = result.value();
+  if (wcscmp(sFunction.c_str(), L"AVG") == 0)
+    dValue /= 2.0;
+
+  return CJS_Result::Success(pRuntime->NewNumber(dValue));
 }
 
-CJS_Return CJS_PublicMethods::AFMakeNumber(
+CJS_Result CJS_PublicMethods::AFMakeNumber(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString ws = pRuntime->ToWideString(params[0]);
   NormalizeDecimalMarkW(&ws);
 
   v8::Local<v8::Value> val =
-      pRuntime->MaybeCoerceToNumber(pRuntime->NewString(ws.c_str()));
+      pRuntime->MaybeCoerceToNumber(pRuntime->NewString(ws.AsStringView()));
   if (!val->IsNumber())
-    return CJS_Return(pRuntime->NewNumber(0));
-  return CJS_Return(val);
+    return CJS_Result::Success(pRuntime->NewNumber(0));
+
+  return CJS_Result::Success(val);
 }
 
-CJS_Return CJS_PublicMethods::AFSimple_Calculate(
+CJS_Result CJS_PublicMethods::AFSimple_Calculate(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  if ((params[1].IsEmpty() || !params[1]->IsArray()) && !params[1]->IsString())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-
-  CPDFSDK_InterForm* pReaderInterForm =
-      pRuntime->GetFormFillEnv()->GetInterForm();
-  CPDF_InterForm* pInterForm = pReaderInterForm->GetInterForm();
+  if (params[1].IsEmpty() || (!params[1]->IsArray() && !params[1]->IsString()))
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString sFunction = pRuntime->ToWideString(params[0]);
-  double dValue = wcscmp(sFunction.c_str(), L"PRD") == 0 ? 1.0 : 0.0;
-
   v8::Local<v8::Array> FieldNameArray =
-      AF_MakeArrayFromList(pRuntime, params[0]);
+      AF_MakeArrayFromList(pRuntime, params[1]);
+
+  CPDFSDK_InteractiveForm* pReaderForm =
+      pRuntime->GetFormFillEnv()->GetInteractiveForm();
+  CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm();
+
+  double dValue = wcscmp(sFunction.c_str(), L"PRD") == 0 ? 1.0 : 0.0;
   int nFieldsCount = 0;
   for (size_t i = 0; i < pRuntime->GetArrayLength(FieldNameArray); ++i) {
     WideString wsFieldName =
         pRuntime->ToWideString(pRuntime->GetArrayElement(FieldNameArray, i));
 
-    for (size_t j = 0; j < pInterForm->CountFields(wsFieldName); ++j) {
-      CPDF_FormField* pFormField = pInterForm->GetField(j, wsFieldName);
+    for (size_t j = 0; j < pForm->CountFields(wsFieldName); ++j) {
+      CPDF_FormField* pFormField = pForm->GetField(j, wsFieldName);
       if (!pFormField)
         continue;
 
@@ -1604,7 +1311,7 @@
           WideString trimmed = pFormField->GetValue();
           trimmed.TrimRight();
           trimmed.TrimLeft();
-          dTemp = FX_atof(trimmed.AsStringView());
+          dTemp = StringToDouble(trimmed.AsStringView());
           break;
         }
         case FormFieldType::kPushButton:
@@ -1619,7 +1326,7 @@
             WideString trimmed = pFormCtrl->GetExportValue();
             trimmed.TrimRight();
             trimmed.TrimLeft();
-            dTemp = FX_atof(trimmed.AsStringView());
+            dTemp = StringToFloat(trimmed.AsStringView());
             break;
           }
           break;
@@ -1628,7 +1335,7 @@
             WideString trimmed = pFormField->GetValue();
             trimmed.TrimRight();
             trimmed.TrimLeft();
-            dTemp = FX_atof(trimmed.AsStringView());
+            dTemp = StringToFloat(trimmed.AsStringView());
           }
           break;
         default:
@@ -1640,8 +1347,12 @@
            wcscmp(sFunction.c_str(), L"MAX") == 0)) {
         dValue = dTemp;
       }
-      dValue = AF_Simple(sFunction.c_str(), dValue, dTemp);
+      Optional<double> dResult =
+          ApplyNamedOperation(sFunction.c_str(), dValue, dTemp);
+      if (!dResult.has_value())
+        return CJS_Result::Failure(JSMessage::kValueError);
 
+      dValue = dResult.value();
       nFieldsCount++;
     }
   }
@@ -1652,31 +1363,31 @@
   dValue = floor(dValue * FXSYS_pow(10, 6) + 0.49) / FXSYS_pow(10, 6);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  if (pContext->GetEventHandler()->m_pValue) {
-    pContext->GetEventHandler()->Value() =
+  if (pContext->GetEventRecorder()->HasValue()) {
+    pContext->GetEventRecorder()->Value() =
         pRuntime->ToWideString(pRuntime->NewNumber(dValue));
   }
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-/* This function validates the current event to ensure that its value is
-** within the specified range. */
-CJS_Return CJS_PublicMethods::AFRange_Validate(
+// This function validates the current event to ensure that its value is
+// within the specified range.
+CJS_Result CJS_PublicMethods::AFRange_Validate(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 4)
-    CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventHandler* pEvent = pContext->GetEventHandler();
-  if (!pEvent->m_pValue)
-    return CJS_Return(false);
+  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  if (!pEvent->HasValue())
+    return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (pEvent->Value().IsEmpty())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
-  double dEentValue = atof(ByteString::FromUnicode(pEvent->Value()).c_str());
+  double dEentValue = atof(pEvent->Value().ToDefANSI().c_str());
   bool bGreaterThan = pRuntime->ToBoolean(params[0]);
   double dGreaterThan = pRuntime->ToDouble(params[1]);
   bool bLessThan = pRuntime->ToBoolean(params[2]);
@@ -1702,17 +1413,17 @@
   }
 
   if (!swMsg.IsEmpty()) {
-    AlertIfPossible(pContext, swMsg.c_str());
+    AlertIfPossible(pContext, swMsg);
     pEvent->Rc() = false;
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJS_PublicMethods::AFExtractNums(
+CJS_Result CJS_PublicMethods::AFExtractNums(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString str = pRuntime->ToWideString(params[0]);
   if (str.GetLength() > 0 && IsDigitSeparatorOrDecimalMark(str[0]))
@@ -1722,19 +1433,21 @@
   v8::Local<v8::Array> nums = pRuntime->NewArray();
   int nIndex = 0;
   for (const auto& wc : str) {
-    if (std::iswdigit(wc)) {
+    if (FXSYS_IsDecimalDigit(wc)) {
       sPart += wc;
     } else if (sPart.GetLength() > 0) {
       pRuntime->PutArrayElement(nums, nIndex,
-                                pRuntime->NewString(sPart.c_str()));
+                                pRuntime->NewString(sPart.AsStringView()));
       sPart.clear();
       nIndex++;
     }
   }
-  if (sPart.GetLength() > 0)
-    pRuntime->PutArrayElement(nums, nIndex, pRuntime->NewString(sPart.c_str()));
-
+  if (sPart.GetLength() > 0) {
+    pRuntime->PutArrayElement(nums, nIndex,
+                              pRuntime->NewString(sPart.AsStringView()));
+  }
   if (pRuntime->GetArrayLength(nums) > 0)
-    return CJS_Return(nums);
-  return CJS_Return(pRuntime->NewUndefined());
+    return CJS_Result::Success(nums);
+
+  return CJS_Result::Success(pRuntime->NewUndefined());
 }
diff --git a/fxjs/cjs_publicmethods.h b/fxjs/cjs_publicmethods.h
index 8726996..53a4dcc 100644
--- a/fxjs/cjs_publicmethods.h
+++ b/fxjs/cjs_publicmethods.h
@@ -7,88 +7,91 @@
 #ifndef FXJS_CJS_PUBLICMETHODS_H_
 #define FXJS_CJS_PUBLICMETHODS_H_
 
-#include <string>
 #include <vector>
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/cjs_result.h"
 
-class CJS_PublicMethods : public CJS_Object {
+class CJS_PublicMethods final : public CJS_Object {
  public:
-  explicit CJS_PublicMethods(v8::Local<v8::Object> pObject);
-  ~CJS_PublicMethods() override;
+  CJS_PublicMethods() = delete;
 
   static void DefineJSObjects(CFXJS_Engine* pEngine);
-  static double MakeRegularDate(const WideString& value,
-                                const WideString& format,
-                                bool* bWrongFormat);
+
+  static double ParseDate(const WideString& value, bool* bWrongFormat);
+  static double ParseDateAsGMT(const WideString& value);
+  static double ParseDateUsingFormat(const WideString& value,
+                                     const WideString& format,
+                                     bool* bWrongFormat);
 
   // Exposed for testing.
-  static WideString MakeFormatDate(double dDate, const WideString& format);
+  static WideString PrintDateUsingFormat(double dDate,
+                                         const WideString& format);
   static bool IsNumber(const WideString& str);
 
-  static CJS_Return AFNumber_Format(
+  static CJS_Result AFNumber_Format(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFNumber_Keystroke(
+  static CJS_Result AFNumber_Keystroke(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFPercent_Format(
+  static CJS_Result AFPercent_Format(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFPercent_Keystroke(
+  static CJS_Result AFPercent_Keystroke(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFDate_FormatEx(
+  static CJS_Result AFDate_FormatEx(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFDate_KeystrokeEx(
+  static CJS_Result AFDate_KeystrokeEx(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFDate_Format(
+  static CJS_Result AFDate_Format(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFDate_Keystroke(
+  static CJS_Result AFDate_Keystroke(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFTime_FormatEx(
+  static CJS_Result AFTime_FormatEx(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFTime_KeystrokeEx(
+  static CJS_Result AFTime_KeystrokeEx(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFTime_Format(
+  static CJS_Result AFTime_Format(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFTime_Keystroke(
+  static CJS_Result AFTime_Keystroke(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFSpecial_Format(
+  static CJS_Result AFSpecial_Format(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFSpecial_Keystroke(
+  static CJS_Result AFSpecial_Keystroke(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFSpecial_KeystrokeEx(
+  static CJS_Result AFSpecial_KeystrokeEx(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFSimple(CJS_Runtime* pRuntime,
+  static CJS_Result AFSimple(CJS_Runtime* pRuntime,
                              const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFMakeNumber(
+  static CJS_Result AFMakeNumber(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFSimple_Calculate(
+  static CJS_Result AFSimple_Calculate(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFRange_Validate(
+  static CJS_Result AFRange_Validate(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFMergeChange(
+  static CJS_Result AFMergeChange(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFParseDateEx(
+  static CJS_Result AFParseDateEx(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
-  static CJS_Return AFExtractNums(
+  static CJS_Result AFExtractNums(
       CJS_Runtime* pRuntime,
       const std::vector<v8::Local<v8::Value>>& params);
 
@@ -136,25 +139,13 @@
   static void AFExtractNums_static(
       const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static const JSMethodSpec GlobalFunctionSpecs[];
-  static int ParseStringInteger(const WideString& string,
-                                size_t nStart,
-                                size_t* pSkip,
-                                size_t nMaxStep);
-  static WideString ParseStringString(const WideString& string,
-                                      size_t nStart,
-                                      size_t* pSkip);
-  static double ParseNormalDate(const WideString& value, bool* bWrongFormat);
-  static double MakeInterDate(const WideString& value);
-
   static bool MaskSatisfied(wchar_t c_Change, wchar_t c_Mask);
   static bool IsReservedMaskChar(wchar_t ch);
-
-  static double AF_Simple(const wchar_t* sFuction,
-                          double dValue1,
-                          double dValue2);
   static v8::Local<v8::Array> AF_MakeArrayFromList(CJS_Runtime* pRuntime,
                                                    v8::Local<v8::Value> val);
+
+ private:
+  static const JSMethodSpec GlobalFunctionSpecs[];
 };
 
 #endif  // FXJS_CJS_PUBLICMETHODS_H_
diff --git a/fxjs/cjs_publicmethods_embeddertest.cpp b/fxjs/cjs_publicmethods_embeddertest.cpp
index dbab081..d35f0cc 100644
--- a/fxjs/cjs_publicmethods_embeddertest.cpp
+++ b/fxjs/cjs_publicmethods_embeddertest.cpp
@@ -3,8 +3,11 @@
 // found in the LICENSE file.
 
 #include <cmath>
+#include <vector>
 
 #include "core/fxcrt/fx_string.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fxjs/cjs_event_context.h"
 #include "fxjs/cjs_publicmethods.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/js_embedder_test.h"
@@ -19,7 +22,7 @@
 
 class CJS_PublicMethodsEmbedderTest : public JSEmbedderTest {};
 
-TEST_F(CJS_PublicMethodsEmbedderTest, MakeRegularDate) {
+TEST_F(CJS_PublicMethodsEmbedderTest, ParseDateUsingFormat) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
@@ -28,135 +31,229 @@
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::MakeRegularDate(L"06/25/1968", L"mm/dd/yyyy",
-                                            &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"06/25/1968", L"mm/dd/yyyy",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::MakeRegularDate(L"25061968", L"ddmmyyyy",
-                                            &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"25061968", L"ddmmyyyy",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::MakeRegularDate(L"19680625", L"yyyymmdd",
-                                            &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"19680625", L"yyyymmdd",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1985
   bWrongFormat = false;
-  date = CJS_PublicMethods::MakeRegularDate(L"31121985", L"ddmmyyyy",
-                                            &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"31121985", L"ddmmyyyy",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(504835200000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2085, the other '85.
   bWrongFormat = false;
-  date =
-      CJS_PublicMethods::MakeRegularDate(L"311285", L"ddmmyy", &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"311285", L"ddmmyy",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(3660595200000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1995
   bWrongFormat = false;
-  date = CJS_PublicMethods::MakeRegularDate(L"01021995", L"ddmmyyyy",
-                                            &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"01021995", L"ddmmyyyy",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(791596800000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2095, the other '95.
   bWrongFormat = false;
-  date =
-      CJS_PublicMethods::MakeRegularDate(L"010295", L"ddmmyy", &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"010295", L"ddmmyy",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(3947356800000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005
   bWrongFormat = false;
-  date = CJS_PublicMethods::MakeRegularDate(L"01022005", L"ddmmyyyy",
-                                            &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"01022005", L"ddmmyyyy",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005
   bWrongFormat = false;
-  date =
-      CJS_PublicMethods::MakeRegularDate(L"010205", L"ddmmyy", &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"010205", L"ddmmyy",
+                                                 &bWrongFormat);
+  date = RoundDownDate(date);
+  EXPECT_DOUBLE_EQ(1107216000000.0, date);
+  EXPECT_FALSE(bWrongFormat);
+
+  // 2005 in a different format. https://crbug.com/436572
+  bWrongFormat = false;
+  date = CJS_PublicMethods::ParseDateUsingFormat(L"050201", L"yymmdd",
+                                                 &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
 }
 
-TEST_F(CJS_PublicMethodsEmbedderTest, MakeFormatDate) {
+TEST_F(CJS_PublicMethodsEmbedderTest, PrintDateUsingFormat) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
   WideString formatted_date;
 
   // 1968-06-25
-  formatted_date = CJS_PublicMethods::MakeFormatDate(-47952000000, L"ddmmyy");
+  formatted_date =
+      CJS_PublicMethods::PrintDateUsingFormat(-47952000000, L"ddmmyy");
   EXPECT_STREQ(L"250668", formatted_date.c_str());
-  formatted_date = CJS_PublicMethods::MakeFormatDate(-47952000000, L"yy/mm/dd");
+  formatted_date =
+      CJS_PublicMethods::PrintDateUsingFormat(-47952000000, L"yy/mm/dd");
   EXPECT_STREQ(L"68/06/25", formatted_date.c_str());
 
   // 1969-12-31
-  formatted_date = CJS_PublicMethods::MakeFormatDate(-0.0001, L"ddmmyy");
+  formatted_date = CJS_PublicMethods::PrintDateUsingFormat(-0.0001, L"ddmmyy");
   EXPECT_STREQ(L"311269", formatted_date.c_str());
-  formatted_date = CJS_PublicMethods::MakeFormatDate(-0.0001, L"yy!mmdd");
+  formatted_date = CJS_PublicMethods::PrintDateUsingFormat(-0.0001, L"yy!mmdd");
   EXPECT_STREQ(L"69!1231", formatted_date.c_str());
 
   // 1970-01-01
-  formatted_date = CJS_PublicMethods::MakeFormatDate(0, L"ddmmyy");
+  formatted_date = CJS_PublicMethods::PrintDateUsingFormat(0, L"ddmmyy");
   EXPECT_STREQ(L"010170", formatted_date.c_str());
-  formatted_date = CJS_PublicMethods::MakeFormatDate(0, L"mm-yyyy-dd");
+  formatted_date = CJS_PublicMethods::PrintDateUsingFormat(0, L"mm-yyyy-dd");
   EXPECT_STREQ(L"01-1970-01", formatted_date.c_str());
 
   // 1985-12-31
-  formatted_date = CJS_PublicMethods::MakeFormatDate(504835200000.0, L"ddmmyy");
+  formatted_date =
+      CJS_PublicMethods::PrintDateUsingFormat(504835200000.0, L"ddmmyy");
   EXPECT_STREQ(L"311285", formatted_date.c_str());
-  formatted_date = CJS_PublicMethods::MakeFormatDate(504835200000.0, L"yymmdd");
+  formatted_date =
+      CJS_PublicMethods::PrintDateUsingFormat(504835200000.0, L"yymmdd");
   EXPECT_STREQ(L"851231", formatted_date.c_str());
 
   // 1995-02-01
-  formatted_date = CJS_PublicMethods::MakeFormatDate(791596800000.0, L"ddmmyy");
+  formatted_date =
+      CJS_PublicMethods::PrintDateUsingFormat(791596800000.0, L"ddmmyy");
   EXPECT_STREQ(L"010295", formatted_date.c_str());
   formatted_date =
-      CJS_PublicMethods::MakeFormatDate(791596800000.0, L"yyyymmdd");
+      CJS_PublicMethods::PrintDateUsingFormat(791596800000.0, L"yyyymmdd");
   EXPECT_STREQ(L"19950201", formatted_date.c_str());
 
   // 2005-02-01
   formatted_date =
-      CJS_PublicMethods::MakeFormatDate(1107216000000.0, L"ddmmyy");
+      CJS_PublicMethods::PrintDateUsingFormat(1107216000000.0, L"ddmmyy");
   EXPECT_STREQ(L"010205", formatted_date.c_str());
   formatted_date =
-      CJS_PublicMethods::MakeFormatDate(1107216000000.0, L"yyyyddmm");
+      CJS_PublicMethods::PrintDateUsingFormat(1107216000000.0, L"yyyyddmm");
   EXPECT_STREQ(L"20050102", formatted_date.c_str());
 
   // 2085-12-31
   formatted_date =
-      CJS_PublicMethods::MakeFormatDate(3660595200000.0, L"ddmmyy");
+      CJS_PublicMethods::PrintDateUsingFormat(3660595200000.0, L"ddmmyy");
   EXPECT_STREQ(L"311285", formatted_date.c_str());
   formatted_date =
-      CJS_PublicMethods::MakeFormatDate(3660595200000.0, L"yyyydd");
+      CJS_PublicMethods::PrintDateUsingFormat(3660595200000.0, L"yyyydd");
   EXPECT_STREQ(L"208531", formatted_date.c_str());
 
   // 2095-02-01
   formatted_date =
-      CJS_PublicMethods::MakeFormatDate(3947356800000.0, L"ddmmyy");
+      CJS_PublicMethods::PrintDateUsingFormat(3947356800000.0, L"ddmmyy");
   EXPECT_STREQ(L"010295", formatted_date.c_str());
   formatted_date =
-      CJS_PublicMethods::MakeFormatDate(3947356800000.0, L"mmddyyyy");
+      CJS_PublicMethods::PrintDateUsingFormat(3947356800000.0, L"mmddyyyy");
   EXPECT_STREQ(L"02012095", formatted_date.c_str());
 }
+
+TEST_F(CJS_PublicMethodsEmbedderTest, AFSimple_CalculateSum) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(GetV8Context());
+
+  EXPECT_TRUE(OpenDocument("calculate.pdf"));
+  auto* page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  CJS_Runtime runtime(
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()));
+  runtime.NewEventContext();
+
+  WideString result;
+  runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest(
+      &result);
+
+  auto ary = runtime.NewArray();
+  runtime.PutArrayElement(ary, 0, runtime.NewString("Calc1_A"));
+  runtime.PutArrayElement(ary, 1, runtime.NewString("Calc1_B"));
+
+  std::vector<v8::Local<v8::Value>> params;
+  params.push_back(runtime.NewString("SUM"));
+  params.push_back(ary);
+
+  CJS_Result ret = CJS_PublicMethods::AFSimple_Calculate(&runtime, params);
+  UnloadPage(page);
+
+  runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest(
+      nullptr);
+
+  ASSERT_TRUE(!ret.HasError());
+  ASSERT_TRUE(!ret.HasReturn());
+  ASSERT_EQ(L"7", result);
+}
+
+TEST_F(CJS_PublicMethodsEmbedderTest, AFNumber_Keystroke) {
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  v8::Context::Scope context_scope(GetV8Context());
+
+  EXPECT_TRUE(OpenDocument("calculate.pdf"));
+  auto* page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  CJS_Runtime runtime(
+      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()));
+  runtime.NewEventContext();
+
+  auto* handler = runtime.GetCurrentEventContext()->GetEventRecorder();
+
+  bool valid = true;
+  WideString result = L"-10";
+  WideString change = L"";
+
+  handler->SetValueForTest(&result);
+  handler->SetRCForTest(&valid);
+  handler->SetStrChangeForTest(&change);
+
+  handler->ResetWillCommitForTest();
+  handler->SetSelStart(0);
+  handler->SetSelEnd(0);
+
+  std::vector<v8::Local<v8::Value>> params;
+  params.push_back(runtime.NewString("-10"));
+  params.push_back(runtime.NewString(""));
+
+  CJS_Result ret = CJS_PublicMethods::AFNumber_Keystroke(&runtime, params);
+  EXPECT_TRUE(valid);
+  EXPECT_TRUE(!ret.HasError());
+  EXPECT_TRUE(!ret.HasReturn());
+
+  UnloadPage(page);
+
+  // Keep the *SAN bots happy. One of these is an UnownedPtr, another seems to
+  // used during destruction. Clear them all to be safe and consistent.
+  handler->SetValueForTest(nullptr);
+  handler->SetRCForTest(nullptr);
+  handler->SetStrChangeForTest(nullptr);
+}
diff --git a/fxjs/cjs_publicmethods_unittest.cpp b/fxjs/cjs_publicmethods_unittest.cpp
index 37bf93b..1d72418 100644
--- a/fxjs/cjs_publicmethods_unittest.cpp
+++ b/fxjs/cjs_publicmethods_unittest.cpp
@@ -5,7 +5,6 @@
 #include "fxjs/cjs_publicmethods.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 
 TEST(CJS_PublicMethods, IsNumber) {
   // TODO(weili): Check whether results from case 0, 1, 10, 15 are intended.
diff --git a/fxjs/cjs_report.cpp b/fxjs/cjs_report.cpp
deleted file mode 100644
index 0788a90..0000000
--- a/fxjs/cjs_report.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_report.h"
-
-#include <vector>
-
-#include "fxjs/JS_Define.h"
-#include "fxjs/cjs_object.h"
-
-const JSMethodSpec CJS_Report::MethodSpecs[] = {
-    {"save", save_static},
-    {"writeText", writeText_static}};
-
-int CJS_Report::ObjDefnID = -1;
-
-// static
-void CJS_Report::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
-  ObjDefnID =
-      pEngine->DefineObj("Report", eObjType, JSConstructor<CJS_Report, Report>,
-                         JSDestructor<CJS_Report>);
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
-}
-
-Report::Report(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
-
-Report::~Report() {}
-
-CJS_Return Report::writeText(CJS_Runtime* pRuntime,
-                             const std::vector<v8::Local<v8::Value>>& params) {
-  // Unsafe, not supported.
-  return CJS_Return(true);
-}
-
-CJS_Return Report::save(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
-  // Unsafe, not supported.
-  return CJS_Return(true);
-}
diff --git a/fxjs/cjs_report.h b/fxjs/cjs_report.h
deleted file mode 100644
index 50259ef..0000000
--- a/fxjs/cjs_report.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_REPORT_H_
-#define FXJS_CJS_REPORT_H_
-
-#include <vector>
-
-#include "fxjs/JS_Define.h"
-
-class Report : public CJS_EmbedObj {
- public:
-  explicit Report(CJS_Object* pJSObject);
-  ~Report() override;
-
- public:
-  CJS_Return save(CJS_Runtime* pRuntime,
-                  const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return writeText(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params);
-};
-
-class CJS_Report : public CJS_Object {
- public:
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
-  explicit CJS_Report(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Report() override {}
-
-  JS_STATIC_METHOD(save, Report)
-  JS_STATIC_METHOD(writeText, Report);
-
- private:
-  static int ObjDefnID;
-  static const JSMethodSpec MethodSpecs[];
-};
-
-#endif  // FXJS_CJS_REPORT_H_
diff --git a/fxjs/cjs_result.cpp b/fxjs/cjs_result.cpp
new file mode 100644
index 0000000..92f2b0b
--- /dev/null
+++ b/fxjs/cjs_result.cpp
@@ -0,0 +1,19 @@
+// 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 "fxjs/cjs_result.h"
+
+CJS_Result::CJS_Result() {}
+
+CJS_Result::CJS_Result(v8::Local<v8::Value> ret) : return_(ret) {}
+
+CJS_Result::CJS_Result(const WideString& err) : error_(err) {}
+
+CJS_Result::CJS_Result(JSMessage id) : CJS_Result(JSGetStringFromID(id)) {}
+
+CJS_Result::CJS_Result(const CJS_Result&) = default;
+
+CJS_Result::~CJS_Result() = default;
diff --git a/fxjs/cjs_result.h b/fxjs/cjs_result.h
new file mode 100644
index 0000000..5e1ed7c
--- /dev/null
+++ b/fxjs/cjs_result.h
@@ -0,0 +1,57 @@
+// 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 FXJS_CJS_RESULT_H_
+#define FXJS_CJS_RESULT_H_
+
+#include "fxjs/js_resources.h"
+#include "third_party/base/optional.h"
+#include "v8/include/v8.h"
+
+class CJS_Result {
+ public:
+  // Wrap constructors with static methods so we can apply WARN_UNUSED_RESULT,
+  // otherwise we can't catch places where someone mistakenly writes:
+  //
+  //     if (error)
+  //       CJS_Result(JS_ERROR_CODE);
+  //
+  // instead of
+  //
+  //     if (error)
+  //       return CJS_Result(JS_ERROR_CODE);
+  //
+  static CJS_Result Success() WARN_UNUSED_RESULT { return CJS_Result(); }
+  static CJS_Result Success(v8::Local<v8::Value> value) WARN_UNUSED_RESULT {
+    return CJS_Result(value);
+  }
+  static CJS_Result Failure(const WideString& str) WARN_UNUSED_RESULT {
+    return CJS_Result(str);
+  }
+  static CJS_Result Failure(JSMessage id) WARN_UNUSED_RESULT {
+    return CJS_Result(id);
+  }
+
+  CJS_Result(const CJS_Result&);
+  ~CJS_Result();
+
+  bool HasError() const { return error_.has_value(); }
+  WideString Error() const { return error_.value(); }
+
+  bool HasReturn() const { return !return_.IsEmpty(); }
+  v8::Local<v8::Value> Return() const { return return_; }
+
+ private:
+  CJS_Result();                               // Successful but empty return.
+  explicit CJS_Result(v8::Local<v8::Value>);  // Successful return with value.
+  explicit CJS_Result(const WideString&);     // Error with custom message.
+  explicit CJS_Result(JSMessage id);          // Error with stock message.
+
+  Optional<WideString> error_;
+  v8::Local<v8::Value> return_;
+};
+
+#endif  // FXJS_CJS_RESULT_H_
diff --git a/fxjs/cjs_return.cpp b/fxjs/cjs_return.cpp
deleted file mode 100644
index adfd1c5..0000000
--- a/fxjs/cjs_return.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/cjs_return.h"
-
-CJS_Return::CJS_Return(bool result) : is_error_(!result) {}
-
-CJS_Return::CJS_Return(const WideString& err) : is_error_(true), error_(err) {}
-
-CJS_Return::CJS_Return(v8::Local<v8::Value> ret)
-    : is_error_(false), return_(ret) {}
-
-CJS_Return::CJS_Return(const CJS_Return&) = default;
-
-CJS_Return::~CJS_Return() = default;
diff --git a/fxjs/cjs_return.h b/fxjs/cjs_return.h
deleted file mode 100644
index 61c5778..0000000
--- a/fxjs/cjs_return.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_RETURN_H_
-#define FXJS_CJS_RETURN_H_
-
-#include "fxjs/fxjs_v8.h"
-
-class CJS_Return {
- public:
-  explicit CJS_Return(bool);
-  explicit CJS_Return(const WideString&);
-  explicit CJS_Return(v8::Local<v8::Value>);
-  CJS_Return(const CJS_Return&);
-  ~CJS_Return();
-
-  bool HasError() const { return is_error_; }
-  WideString Error() const { return error_; }
-
-  bool HasReturn() const { return !return_.IsEmpty(); }
-  v8::Local<v8::Value> Return() const { return return_; }
-
- private:
-  CJS_Return() = delete;
-
-  bool is_error_ = false;
-  WideString error_;
-  v8::Local<v8::Value> return_;
-};
-
-#endif  // FXJS_CJS_RETURN_H_
diff --git a/fxjs/cjs_runtime.cpp b/fxjs/cjs_runtime.cpp
index c8e95e1..919d47a 100644
--- a/fxjs/cjs_runtime.cpp
+++ b/fxjs/cjs_runtime.cpp
@@ -9,7 +9,7 @@
 #include <algorithm>
 
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fxjs/JS_Define.h"
+#include "fxjs/cfx_globaldata.h"
 #include "fxjs/cjs_annot.h"
 #include "fxjs/cjs_app.h"
 #include "fxjs/cjs_border.h"
@@ -19,66 +19,35 @@
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_event.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventhandler.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_font.h"
 #include "fxjs/cjs_global.h"
 #include "fxjs/cjs_globalarrays.h"
 #include "fxjs/cjs_globalconsts.h"
-#include "fxjs/cjs_globaldata.h"
 #include "fxjs/cjs_highlight.h"
 #include "fxjs/cjs_icon.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_position.h"
-#include "fxjs/cjs_printparamsobj.h"
 #include "fxjs/cjs_publicmethods.h"
-#include "fxjs/cjs_report.h"
 #include "fxjs/cjs_scalehow.h"
 #include "fxjs/cjs_scalewhen.h"
 #include "fxjs/cjs_style.h"
 #include "fxjs/cjs_timerobj.h"
 #include "fxjs/cjs_util.h"
 #include "fxjs/cjs_zoomtype.h"
-#include "public/fpdf_formfill.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fxjs/cfxjse_value.h"
-#endif  // PDF_ENABLE_XFA
-
-// static
-void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {
-  FXJS_Initialize(slot, reinterpret_cast<v8::Isolate*>(isolate));
-}
-
-// static
-void IJS_Runtime::Destroy() {
-  FXJS_Release();
-}
-
-// static
-IJS_Runtime* IJS_Runtime::Create(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  return new CJS_Runtime(pFormFillEnv);
-}
-
-// static
-CJS_Runtime* CJS_Runtime::CurrentRuntimeFromIsolate(v8::Isolate* pIsolate) {
-  return static_cast<CJS_Runtime*>(
-      CFXJS_Engine::CurrentEngineFromIsolate(pIsolate));
-}
+#include "fxjs/js_define.h"
+#include "third_party/base/ptr_util.h"
 
 CJS_Runtime::CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv)
-    : m_pFormFillEnv(pFormFillEnv),
-      m_bBlocking(false),
-      m_isolateManaged(false) {
+    : m_pFormFillEnv(pFormFillEnv) {
   v8::Isolate* pIsolate = nullptr;
-
   IPDF_JSPLATFORM* pPlatform = m_pFormFillEnv->GetFormFillInfo()->m_pJsPlatform;
   if (pPlatform->version <= 2) {
     unsigned int embedderDataSlot = 0;
     v8::Isolate* pExternalIsolate = nullptr;
     if (pPlatform->version == 2) {
-      pExternalIsolate = reinterpret_cast<v8::Isolate*>(pPlatform->m_isolate);
+      pExternalIsolate = static_cast<v8::Isolate*>(pPlatform->m_isolate);
       embedderDataSlot = pPlatform->m_v8EmbedderSlot;
     }
     FXJS_Initialize(embedderDataSlot, pExternalIsolate);
@@ -86,27 +55,21 @@
   m_isolateManaged = FXJS_GetIsolate(&pIsolate);
   SetIsolate(pIsolate);
 
-#ifdef PDF_ENABLE_XFA
   v8::Isolate::Scope isolate_scope(pIsolate);
   v8::HandleScope handle_scope(pIsolate);
-#endif
-
   if (m_isolateManaged || FXJS_GlobalIsolateRefCount() == 0)
     DefineJSObjects();
 
-  IJS_EventContext* pContext = NewEventContext();
+  ScopedEventContext pContext(this);
   InitializeEngine();
-  ReleaseEventContext(pContext);
   SetFormFillEnvToDocument();
 }
 
 CJS_Runtime::~CJS_Runtime() {
-  NotifyObservedPtrs();
+  NotifyObservers();
   ReleaseEngine();
-  if (m_isolateManaged) {
-    GetIsolate()->Dispose();
-    SetIsolate(nullptr);
-  }
+  if (m_isolateManaged)
+    DisposeIsolate();
 }
 
 void CJS_Runtime::DefineJSObjects() {
@@ -151,9 +114,8 @@
   CJS_GlobalConsts::DefineJSObjects(this);
   CJS_GlobalArrays::DefineJSObjects(this);
 
-  // ObjDefIDs 21 - 23.
+  // ObjDefIDs 21 - 22.
   CJS_TimerObj::DefineJSObjects(this);
-  CJS_PrintParamsObj::DefineJSObjects(this);
   CJS_Annot::DefineJSObjects(this);
 }
 
@@ -163,11 +125,8 @@
 }
 
 void CJS_Runtime::ReleaseEventContext(IJS_EventContext* pContext) {
-  auto it = std::find(m_EventContextArray.begin(), m_EventContextArray.end(),
-                      pdfium::FakeUniquePtr<CJS_EventContext>(
-                          static_cast<CJS_EventContext*>(pContext)));
-  if (it != m_EventContextArray.end())
-    m_EventContextArray.erase(it);
+  ASSERT(pContext == m_EventContextArray.back().get());
+  m_EventContextArray.pop_back();
 }
 
 CJS_EventContext* CJS_Runtime::GetCurrentEventContext() const {
@@ -175,43 +134,34 @@
                                      : m_EventContextArray.back().get();
 }
 
+TimerHandlerIface* CJS_Runtime::GetTimerHandler() const {
+  return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
+}
+
 void CJS_Runtime::SetFormFillEnvToDocument() {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::HandleScope handle_scope(GetIsolate());
-  v8::Local<v8::Context> context = NewLocalContext();
+  v8::Local<v8::Context> context = GetV8Context();
   v8::Context::Scope context_scope(context);
 
   v8::Local<v8::Object> pThis = GetThisObj();
   if (pThis.IsEmpty())
     return;
 
-  if (CFXJS_Engine::GetObjDefnID(pThis) != CJS_Document::GetObjDefnID())
-    return;
-
-  CJS_Document* pJSDocument =
-      static_cast<CJS_Document*>(GetObjectPrivate(pThis));
+  auto pJSDocument = JSGetObject<CJS_Document>(pThis);
   if (!pJSDocument)
     return;
 
-  Document* pDocument = static_cast<Document*>(pJSDocument->GetEmbedObject());
-  if (!pDocument)
-    return;
-
-  pDocument->SetFormFillEnv(m_pFormFillEnv.Get());
+  pJSDocument->SetFormFillEnv(m_pFormFillEnv.Get());
 }
 
 CPDFSDK_FormFillEnvironment* CJS_Runtime::GetFormFillEnv() const {
   return m_pFormFillEnv.Get();
 }
 
-int CJS_Runtime::ExecuteScript(const WideString& script, WideString* info) {
-  FXJSErr error = {};
-  int nRet = Execute(script, &error);
-  if (nRet < 0) {
-    *info = WideString::Format(L"[ Line: %05d { %ls } ] : %s", error.linnum - 1,
-                               error.srcline, error.message);
-  }
-  return nRet;
+Optional<IJS_Runtime::JS_Error> CJS_Runtime::ExecuteScript(
+    const WideString& script) {
+  return Execute(script);
 }
 
 bool CJS_Runtime::AddEventToSet(const FieldEvent& event) {
@@ -222,56 +172,51 @@
   m_FieldEventSet.erase(event);
 }
 
-#ifdef PDF_ENABLE_XFA
-WideString ChangeObjName(const WideString& str) {
-  WideString sRet = str;
-  sRet.Replace(L"_", L".");
-  return sRet;
+CJS_Runtime* CJS_Runtime::AsCJSRuntime() {
+  return this;
 }
 
-bool CJS_Runtime::GetValueByName(const ByteStringView& utf8Name,
-                                 CFXJSE_Value* pValue) {
+bool CJS_Runtime::GetValueByNameFromGlobalObject(ByteStringView utf8Name,
+                                                 v8::Local<v8::Value>* pValue) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  v8::Local<v8::Context> context = NewLocalContext();
+  v8::Local<v8::Context> context = GetV8Context();
   v8::Context::Scope context_scope(context);
-  v8::Local<v8::Value> propvalue = context->Global()->Get(
+  v8::Local<v8::String> str =
       v8::String::NewFromUtf8(GetIsolate(), utf8Name.unterminated_c_str(),
-                              v8::String::kNormalString, utf8Name.GetLength()));
-  if (propvalue.IsEmpty()) {
-    pValue->SetUndefined();
+                              v8::NewStringType::kNormal, utf8Name.GetLength())
+          .ToLocalChecked();
+  v8::MaybeLocal<v8::Value> maybe_propvalue =
+      context->Global()->Get(context, str);
+  if (maybe_propvalue.IsEmpty())
     return false;
-  }
-  pValue->ForceSetValue(propvalue);
+
+  *pValue = maybe_propvalue.ToLocalChecked();
   return true;
 }
 
-bool CJS_Runtime::SetValueByName(const ByteStringView& utf8Name,
-                                 CFXJSE_Value* pValue) {
-  if (utf8Name.IsEmpty() || !pValue)
+bool CJS_Runtime::SetValueByNameInGlobalObject(ByteStringView utf8Name,
+                                               v8::Local<v8::Value> pValue) {
+  if (utf8Name.IsEmpty() || pValue.IsEmpty())
     return false;
 
   v8::Isolate* pIsolate = GetIsolate();
   v8::Isolate::Scope isolate_scope(pIsolate);
-  v8::HandleScope handle_scope(pIsolate);
-  v8::Local<v8::Context> context = NewLocalContext();
+  v8::Local<v8::Context> context = GetV8Context();
   v8::Context::Scope context_scope(context);
-  v8::Local<v8::Value> propvalue =
-      v8::Local<v8::Value>::New(GetIsolate(), pValue->DirectGetValue());
-  context->Global()->Set(
+  v8::Local<v8::String> str =
       v8::String::NewFromUtf8(pIsolate, utf8Name.unterminated_c_str(),
-                              v8::String::kNormalString, utf8Name.GetLength()),
-      propvalue);
-  return true;
+                              v8::NewStringType::kNormal, utf8Name.GetLength())
+          .ToLocalChecked();
+  v8::Maybe<bool> result = context->Global()->Set(context, str, pValue);
+  return result.IsJust() && result.FromJust();
 }
-#endif
 
 v8::Local<v8::Value> CJS_Runtime::MaybeCoerceToNumber(
     v8::Local<v8::Value> value) {
   bool bAllowNaN = false;
   if (value->IsString()) {
-    ByteString bstr = ByteString::FromUnicode(ToWideString(value));
-    if (bstr.GetLength() == 0)
+    ByteString bstr = ToWideString(value).ToDefANSI();
+    if (bstr.IsEmpty())
       return value;
     if (bstr == "NaN")
       bAllowNaN = true;
diff --git a/fxjs/cjs_runtime.h b/fxjs/cjs_runtime.h
index 7ecc0d5..b3f8ef6 100644
--- a/fxjs/cjs_runtime.h
+++ b/fxjs/cjs_runtime.h
@@ -7,38 +7,39 @@
 #ifndef FXJS_CJS_RUNTIME_H_
 #define FXJS_CJS_RUNTIME_H_
 
-#include <map>
 #include <memory>
 #include <set>
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/observable.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-#include "fxjs/cjs_eventhandler.h"
-#include "fxjs/fxjs_v8.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "core/fxcrt/timerhandler_iface.h"
+#include "fxjs/cfxjs_engine.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/ijs_runtime.h"
 
 class CJS_EventContext;
+class CPDFSDK_FormFillEnvironment;
 
-class CJS_Runtime : public IJS_Runtime,
-                    public CFXJS_Engine,
-                    public Observable<CJS_Runtime> {
+class CJS_Runtime final : public IJS_Runtime,
+                          public CFXJS_Engine,
+                          public Observable {
  public:
   using FieldEvent = std::pair<WideString, JS_EVENT_T>;
 
-  static CJS_Runtime* CurrentRuntimeFromIsolate(v8::Isolate* pIsolate);
-
   explicit CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv);
   ~CJS_Runtime() override;
 
-  // IJS_Runtime
+  // IJS_Runtime:
+  CJS_Runtime* AsCJSRuntime() override;
   IJS_EventContext* NewEventContext() override;
   void ReleaseEventContext(IJS_EventContext* pContext) override;
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
-  int ExecuteScript(const WideString& script, WideString* info) override;
+  Optional<IJS_Runtime::JS_Error> ExecuteScript(
+      const WideString& script) override;
 
   CJS_EventContext* GetCurrentEventContext() const;
+  TimerHandlerIface* GetTimerHandler() const;
 
   // Returns true if the event isn't already found in the set.
   bool AddEventToSet(const FieldEvent& event);
@@ -52,21 +53,19 @@
   // value will be returned, otherwise |value| is returned.
   v8::Local<v8::Value> MaybeCoerceToNumber(v8::Local<v8::Value> value);
 
-#ifdef PDF_ENABLE_XFA
-  bool GetValueByName(const ByteStringView& utf8Name,
-                      CFXJSE_Value* pValue) override;
-  bool SetValueByName(const ByteStringView& utf8Name,
-                      CFXJSE_Value* pValue) override;
-#endif  // PDF_ENABLE_XFA
+  bool GetValueByNameFromGlobalObject(ByteStringView utf8Name,
+                                      v8::Local<v8::Value>* pValue);
+  bool SetValueByNameInGlobalObject(ByteStringView utf8Name,
+                                    v8::Local<v8::Value> pValue);
 
  private:
   void DefineJSObjects();
   void SetFormFillEnvToDocument();
 
   std::vector<std::unique_ptr<CJS_EventContext>> m_EventContextArray;
-  CPDFSDK_FormFillEnvironment::ObservedPtr m_pFormFillEnv;
-  bool m_bBlocking;
-  bool m_isolateManaged;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  bool m_bBlocking = false;
+  bool m_isolateManaged = false;
   std::set<FieldEvent> m_FieldEventSet;
 };
 
diff --git a/fxjs/cjs_runtimestub.cpp b/fxjs/cjs_runtimestub.cpp
index 9196728..6e313de 100644
--- a/fxjs/cjs_runtimestub.cpp
+++ b/fxjs/cjs_runtimestub.cpp
@@ -4,57 +4,33 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-#include <memory>
+#include "fxjs/cjs_runtimestub.h"
 
-#include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cjs_event_context_stub.h"
-#include "fxjs/ijs_runtime.h"
 #include "third_party/base/ptr_util.h"
 
-class CJS_RuntimeStub final : public IJS_Runtime {
- public:
-  explicit CJS_RuntimeStub(CPDFSDK_FormFillEnvironment* pFormFillEnv)
-      : m_pFormFillEnv(pFormFillEnv) {}
-  ~CJS_RuntimeStub() override {}
+CJS_RuntimeStub::CJS_RuntimeStub(CPDFSDK_FormFillEnvironment* pFormFillEnv)
+    : m_pFormFillEnv(pFormFillEnv) {}
 
-  IJS_EventContext* NewEventContext() override {
-    if (!m_pContext)
-      m_pContext = pdfium::MakeUnique<CJS_EventContextStub>();
-    return m_pContext.get();
-  }
+CJS_RuntimeStub::~CJS_RuntimeStub() = default;
 
-  void ReleaseEventContext(IJS_EventContext* pContext) override {}
+IJS_EventContext* CJS_RuntimeStub::NewEventContext() {
+  if (!m_pContext)
+    m_pContext = pdfium::MakeUnique<CJS_EventContextStub>();
+  return m_pContext.get();
+}
 
-  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override {
-    return m_pFormFillEnv.Get();
-  }
+void CJS_RuntimeStub::ReleaseEventContext(IJS_EventContext* pContext) {}
 
-#ifdef PDF_ENABLE_XFA
-  bool GetValueByName(const ByteStringView&, CFXJSE_Value*) override {
-    return false;
-  }
+CPDFSDK_FormFillEnvironment* CJS_RuntimeStub::GetFormFillEnv() const {
+  return m_pFormFillEnv.Get();
+}
 
-  bool SetValueByName(const ByteStringView&, CFXJSE_Value*) override {
-    return false;
-  }
-#endif  // PDF_ENABLE_XFA
+CJS_Runtime* CJS_RuntimeStub::AsCJSRuntime() {
+  return nullptr;
+}
 
-  int ExecuteScript(const WideString& script, WideString* info) override {
-    return 0;
-  }
-
- protected:
-  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
-  std::unique_ptr<CJS_EventContextStub> m_pContext;
-};
-
-// static
-void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {}
-
-// static
-void IJS_Runtime::Destroy() {}
-
-// static
-IJS_Runtime* IJS_Runtime::Create(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  return new CJS_RuntimeStub(pFormFillEnv);
+Optional<IJS_Runtime::JS_Error> CJS_RuntimeStub::ExecuteScript(
+    const WideString& script) {
+  return pdfium::nullopt;
 }
diff --git a/fxjs/cjs_runtimestub.h b/fxjs/cjs_runtimestub.h
new file mode 100644
index 0000000..2b5e713
--- /dev/null
+++ b/fxjs/cjs_runtimestub.h
@@ -0,0 +1,38 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_CJS_RUNTIMESTUB_H_
+#define FXJS_CJS_RUNTIMESTUB_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/ijs_runtime.h"
+
+class CPDFSDK_FormFillEnvironment;
+class IJS_EventContext;
+
+class CJS_RuntimeStub final : public IJS_Runtime {
+ public:
+  explicit CJS_RuntimeStub(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+  ~CJS_RuntimeStub() override;
+
+  // IJS_Runtime:
+  CJS_Runtime* AsCJSRuntime() override;
+  IJS_EventContext* NewEventContext() override;
+  void ReleaseEventContext(IJS_EventContext* pContext) override;
+  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
+
+  Optional<IJS_Runtime::JS_Error> ExecuteScript(
+      const WideString& script) override;
+
+ private:
+  UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
+  std::unique_ptr<IJS_EventContext> m_pContext;
+};
+
+#endif  // FXJS_CJS_RUNTIMESTUB_H_
diff --git a/fxjs/cjs_scalehow.cpp b/fxjs/cjs_scalehow.cpp
index 762863b..999949c 100644
--- a/fxjs/cjs_scalehow.cpp
+++ b/fxjs/cjs_scalehow.cpp
@@ -16,5 +16,5 @@
 void CJS_ScaleHow::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID =
       pEngine->DefineObj("scaleHow", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_scalehow.h b/fxjs/cjs_scalehow.h
index 8177a3e..f8c7eb5 100644
--- a/fxjs/cjs_scalehow.h
+++ b/fxjs/cjs_scalehow.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_SCALEHOW_H_
 #define FXJS_CJS_SCALEHOW_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_ScaleHow : public CJS_Object {
+class CJS_ScaleHow final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_ScaleHow(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_ScaleHow() override {}
+  CJS_ScaleHow() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_scalewhen.cpp b/fxjs/cjs_scalewhen.cpp
index 59f0487..5f39153 100644
--- a/fxjs/cjs_scalewhen.cpp
+++ b/fxjs/cjs_scalewhen.cpp
@@ -18,5 +18,5 @@
 void CJS_ScaleWhen::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID =
       pEngine->DefineObj("scaleWhen", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_scalewhen.h b/fxjs/cjs_scalewhen.h
index e97a285..ef046f9 100644
--- a/fxjs/cjs_scalewhen.h
+++ b/fxjs/cjs_scalewhen.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_SCALEWHEN_H_
 #define FXJS_CJS_SCALEWHEN_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_ScaleWhen : public CJS_Object {
+class CJS_ScaleWhen final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_ScaleWhen(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_ScaleWhen() override {}
+  CJS_ScaleWhen() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_style.cpp b/fxjs/cjs_style.cpp
index be95e9a..c068702 100644
--- a/fxjs/cjs_style.cpp
+++ b/fxjs/cjs_style.cpp
@@ -19,5 +19,5 @@
 // static
 void CJS_Style::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID = pEngine->DefineObj("style", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_style.h b/fxjs/cjs_style.h
index 46ab66b..6e3ee2f 100644
--- a/fxjs/cjs_style.h
+++ b/fxjs/cjs_style.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_STYLE_H_
 #define FXJS_CJS_STYLE_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_Style : public CJS_Object {
+class CJS_Style final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Style(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Style() override {}
+  CJS_Style() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/cjs_timerobj.cpp b/fxjs/cjs_timerobj.cpp
index 410ad08..5fa1ac3 100644
--- a/fxjs/cjs_timerobj.cpp
+++ b/fxjs/cjs_timerobj.cpp
@@ -7,6 +7,7 @@
 #include "fxjs/cjs_timerobj.h"
 
 #include "fxjs/global_timer.h"
+#include "fxjs/js_define.h"
 
 int CJS_TimerObj::ObjDefnID = -1;
 
@@ -18,15 +19,14 @@
 // static
 void CJS_TimerObj::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID = pEngine->DefineObj("TimerObj", FXJSOBJTYPE_DYNAMIC,
-                                 JSConstructor<CJS_TimerObj, TimerObj>,
-                                 JSDestructor<CJS_TimerObj>);
+                                 JSConstructor<CJS_TimerObj>, JSDestructor);
 }
 
-TimerObj::TimerObj(CJS_Object* pJSObject)
-    : CJS_EmbedObj(pJSObject), m_nTimerID(0) {}
+CJS_TimerObj::CJS_TimerObj(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-TimerObj::~TimerObj() {}
+CJS_TimerObj::~CJS_TimerObj() = default;
 
-void TimerObj::SetTimer(GlobalTimer* pTimer) {
+void CJS_TimerObj::SetTimer(GlobalTimer* pTimer) {
   m_nTimerID = pTimer->GetTimerID();
 }
diff --git a/fxjs/cjs_timerobj.h b/fxjs/cjs_timerobj.h
index be09555..69effa6 100644
--- a/fxjs/cjs_timerobj.h
+++ b/fxjs/cjs_timerobj.h
@@ -7,32 +7,25 @@
 #ifndef FXJS_CJS_TIMEROBJ_H_
 #define FXJS_CJS_TIMEROBJ_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
 class GlobalTimer;
 
-class TimerObj : public CJS_EmbedObj {
+class CJS_TimerObj final : public CJS_Object {
  public:
-  explicit TimerObj(CJS_Object* pJSObject);
-  ~TimerObj() override;
+  static int GetObjDefnID();
+  static void DefineJSObjects(CFXJS_Engine* pEngine);
+
+  CJS_TimerObj(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_TimerObj() override;
 
   void SetTimer(GlobalTimer* pTimer);
   int GetTimerID() const { return m_nTimerID; }
 
  private:
-  int m_nTimerID;  // Weak reference to GlobalTimer through global map.
-};
-
-class CJS_TimerObj : public CJS_Object {
- public:
-  static int GetObjDefnID();
-  static void DefineJSObjects(CFXJS_Engine* pEngine);
-
-  explicit CJS_TimerObj(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_TimerObj() override {}
-
- private:
   static int ObjDefnID;
+
+  int m_nTimerID = 0;  // Weak reference to GlobalTimer through global map.
 };
 
 #endif  // FXJS_CJS_TIMEROBJ_H_
diff --git a/fxjs/cjs_util.cpp b/fxjs/cjs_util.cpp
index 57267ad..1f95c31 100644
--- a/fxjs/cjs_util.cpp
+++ b/fxjs/cjs_util.cpp
@@ -11,19 +11,20 @@
 #include <algorithm>
 #include <cmath>
 #include <cwctype>
-#include <string>
 #include <vector>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/JS_Define.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventhandler.h"
+#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_publicmethods.h"
 #include "fxjs/cjs_runtime.h"
+#include "fxjs/fx_date_helpers.h"
+#include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
 
-#if _FX_OS_ == _FX_OS_ANDROID_
+#if defined(OS_ANDROID)
 #include <ctype.h>
 #endif
 
@@ -48,13 +49,23 @@
     {L"ddd", L"%a"},  {L"dd", L"%d"},  {L"yyyy", L"%Y"}, {L"yy", L"%y"},
     {L"HH", L"%H"},   {L"hh", L"%I"},  {L"MM", L"%M"},   {L"ss", L"%S"},
     {L"TT", L"%p"},
-#if defined(_WIN32)
+#if defined(OS_WIN)
     {L"tt", L"%p"},   {L"h", L"%#I"},
 #else
     {L"tt", L"%P"},   {L"h", L"%l"},
 #endif
 };
 
+enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
+
+wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
+  if (eMode == kLowerCase && FXSYS_iswupper(input))
+    return input | 0x20;
+  if (eMode == kUpperCase && FXSYS_iswlower(input))
+    return input & ~0x20;
+  return input;
+}
+
 }  // namespace
 
 const JSMethodSpec CJS_Util::MethodSpecs[] = {
@@ -65,103 +76,107 @@
     {"byteToChar", byteToChar_static}};
 
 int CJS_Util::ObjDefnID = -1;
+const char CJS_Util::kName[] = "util";
+
+// static
+int CJS_Util::GetObjDefnID() {
+  return ObjDefnID;
+}
 
 // static
 void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) {
-  ObjDefnID =
-      pEngine->DefineObj("util", FXJSOBJTYPE_STATIC,
-                         JSConstructor<CJS_Util, util>, JSDestructor<CJS_Util>);
-  DefineMethods(pEngine, ObjDefnID, MethodSpecs, FX_ArraySize(MethodSpecs));
+  ObjDefnID = pEngine->DefineObj(CJS_Util::kName, FXJSOBJTYPE_STATIC,
+                                 JSConstructor<CJS_Util>, JSDestructor);
+  DefineMethods(pEngine, ObjDefnID, MethodSpecs);
 }
 
-util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
+CJS_Util::CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
+    : CJS_Object(pObject, pRuntime) {}
 
-util::~util() {}
+CJS_Util::~CJS_Util() = default;
 
-CJS_Return util::printf(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
-  const size_t iSize = params.size();
-  if (iSize < 1)
-    return CJS_Return(false);
+CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params) {
+  const size_t num_params = params.size();
+  if (num_params < 1)
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  std::wstring unsafe_fmt_string(pRuntime->ToWideString(params[0]).c_str());
-  std::vector<std::wstring> unsafe_conversion_specifiers;
-  int iOffset = 0;
-  int iOffend = 0;
-  unsafe_fmt_string.insert(unsafe_fmt_string.begin(), L'S');
-  while (iOffset != -1) {
-    iOffend = unsafe_fmt_string.find(L"%", iOffset + 1);
-    std::wstring strSub;
-    if (iOffend == -1)
-      strSub = unsafe_fmt_string.substr(iOffset);
-    else
-      strSub = unsafe_fmt_string.substr(iOffset, iOffend - iOffset);
-    unsafe_conversion_specifiers.push_back(strSub);
-    iOffset = iOffend;
+  // Use 'S' as a sentinel to ensure we always have some text before the first
+  // format specifier.
+  WideString unsafe_fmt_string = L'S' + pRuntime->ToWideString(params[0]);
+  std::vector<WideString> unsafe_conversion_specifiers;
+
+  {
+    size_t offset = 0;
+    while (true) {
+      Optional<size_t> offset_end = unsafe_fmt_string.Find(L"%", offset + 1);
+      if (!offset_end.has_value()) {
+        unsafe_conversion_specifiers.push_back(
+            unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
+        break;
+      }
+
+      unsafe_conversion_specifiers.push_back(
+          unsafe_fmt_string.Substr(offset, offset_end.value() - offset));
+      offset = offset_end.value();
+    }
   }
 
-  std::wstring c_strResult;
-  for (size_t iIndex = 0; iIndex < unsafe_conversion_specifiers.size();
-       ++iIndex) {
-    std::wstring c_strFormat = unsafe_conversion_specifiers[iIndex];
-    if (iIndex == 0) {
-      c_strResult = c_strFormat;
+  WideString result = unsafe_conversion_specifiers[0];
+  for (size_t i = 1; i < unsafe_conversion_specifiers.size(); ++i) {
+    WideString fmt = unsafe_conversion_specifiers[i];
+    if (i >= num_params) {
+      result += fmt;
       continue;
     }
 
-    if (iIndex >= iSize) {
-      c_strResult += c_strFormat;
-      continue;
-    }
-
-    WideString strSegment;
-    switch (ParseDataType(&c_strFormat)) {
+    WideString segment;
+    switch (ParseDataType(&fmt)) {
       case UTIL_INT:
-        strSegment = WideString::Format(c_strFormat.c_str(),
-                                        pRuntime->ToInt32(params[iIndex]));
+        segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
         break;
       case UTIL_DOUBLE:
-        strSegment = WideString::Format(c_strFormat.c_str(),
-                                        pRuntime->ToDouble(params[iIndex]));
+        segment =
+            WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
         break;
       case UTIL_STRING:
-        strSegment =
-            WideString::Format(c_strFormat.c_str(),
-                               pRuntime->ToWideString(params[iIndex]).c_str());
+        segment = WideString::Format(fmt.c_str(),
+                                     pRuntime->ToWideString(params[i]).c_str());
         break;
       default:
-        strSegment = WideString::Format(L"%ls", c_strFormat.c_str());
+        segment = WideString::Format(L"%ls", fmt.c_str());
         break;
     }
-    c_strResult += strSegment.c_str();
+    result += segment;
   }
 
-  c_strResult.erase(c_strResult.begin());
-  return CJS_Return(pRuntime->NewString(c_strResult.c_str()));
+  // Remove the 'S' sentinel introduced earlier.
+  DCHECK_EQ(L'S', result[0]);
+  auto result_view = result.AsStringView();
+  return CJS_Result::Success(
+      pRuntime->NewString(result_view.Last(result_view.GetLength() - 1)));
 }
 
-CJS_Return util::printd(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params) {
   const size_t iSize = params.size();
   if (iSize < 2)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   if (params[1].IsEmpty() || !params[1]->IsDate())
-    return CJS_Return(JSGetStringFromID(JSMessage::kSecondParamNotDateError));
+    return CJS_Result::Failure(JSMessage::kSecondParamNotDateError);
 
   v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
-  if (v8_date.IsEmpty() || std::isnan(pRuntime->ToDouble(v8_date))) {
-    return CJS_Return(
-        JSGetStringFromID(JSMessage::kSecondParamInvalidDateError));
-  }
+  if (v8_date.IsEmpty() || std::isnan(pRuntime->ToDouble(v8_date)))
+    return CJS_Result::Failure(JSMessage::kSecondParamInvalidDateError);
 
-  double date = JS_LocalTime(pRuntime->ToDouble(v8_date));
-  int year = JS_GetYearFromTime(date);
-  int month = JS_GetMonthFromTime(date) + 1;  // One-based.
-  int day = JS_GetDayFromTime(date);
-  int hour = JS_GetHourFromTime(date);
-  int min = JS_GetMinFromTime(date);
-  int sec = JS_GetSecFromTime(date);
+  double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
+  int year = FX_GetYearFromTime(date);
+  int month = FX_GetMonthFromTime(date) + 1;  // One-based.
+  int day = FX_GetDayFromTime(date);
+  int hour = FX_GetHourFromTime(date);
+  int min = FX_GetMinFromTime(date);
+  int sec = FX_GetSecFromTime(date);
 
   if (params[0]->IsNumber()) {
     WideString swResult;
@@ -179,101 +194,91 @@
                                       month, day, hour, min, sec);
         break;
       default:
-        return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+        return CJS_Result::Failure(JSMessage::kValueError);
     }
 
-    return CJS_Return(pRuntime->NewString(swResult.c_str()));
+    return CJS_Result::Success(pRuntime->NewString(swResult.AsStringView()));
   }
 
-  if (params[0]->IsString()) {
-    // We don't support XFAPicture at the moment.
-    if (iSize > 2 && pRuntime->ToBoolean(params[2]))
-      return CJS_Return(JSGetStringFromID(JSMessage::kNotSupportedError));
+  if (!params[0]->IsString())
+    return CJS_Result::Failure(JSMessage::kTypeError);
 
-    // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
-    // pre-existing %-directives before inserting our own.
-    std::basic_string<wchar_t> cFormat =
-        pRuntime->ToWideString(params[0]).c_str();
-    cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
-                  cFormat.end());
+  // We don't support XFAPicture at the moment.
+  if (iSize > 2 && pRuntime->ToBoolean(params[2]))
+    return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
-    for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) {
-      int iStart = 0;
-      int iEnd;
-      while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) !=
-             -1) {
-        cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark),
-                        TbConvertTable[i].lpszCppMark);
-        iStart = iEnd;
-      }
+  // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
+  // pre-existing %-directives before inserting our own.
+  std::basic_string<wchar_t> cFormat =
+      pRuntime->ToWideString(params[0]).c_str();
+  cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
+                cFormat.end());
+
+  for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) {
+    int iStart = 0;
+    int iEnd;
+    while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != -1) {
+      cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark),
+                      TbConvertTable[i].lpszCppMark);
+      iStart = iEnd;
     }
+  }
 
-    if (year < 0)
-      return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+  if (year < 0)
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-    static const TbConvertAdditional cTableAd[] = {
-        {L"m", month}, {L"d", day},
-        {L"H", hour},  {L"h", hour > 12 ? hour - 12 : hour},
-        {L"M", min},   {L"s", sec},
-    };
+  const TbConvertAdditional cTableAd[] = {
+      {L"m", month}, {L"d", day},
+      {L"H", hour},  {L"h", hour > 12 ? hour - 12 : hour},
+      {L"M", min},   {L"s", sec},
+  };
 
-    for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) {
-      int iStart = 0;
-      int iEnd;
-      while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) {
-        if (iEnd > 0) {
-          if (cFormat[iEnd - 1] == L'%') {
-            iStart = iEnd + 1;
-            continue;
-          }
+  for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) {
+    int iStart = 0;
+    int iEnd;
+    while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) {
+      if (iEnd > 0) {
+        if (cFormat[iEnd - 1] == L'%') {
+          iStart = iEnd + 1;
+          continue;
         }
-        cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark),
-                        WideString::Format(L"%d", cTableAd[i].iValue).c_str());
-        iStart = iEnd;
       }
+      cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark),
+                      WideString::Format(L"%d", cTableAd[i].iValue).c_str());
+      iStart = iEnd;
     }
-
-    struct tm time = {};
-    time.tm_year = year - 1900;
-    time.tm_mon = month - 1;
-    time.tm_mday = day;
-    time.tm_hour = hour;
-    time.tm_min = min;
-    time.tm_sec = sec;
-
-    wchar_t buf[64] = {};
-    FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time);
-    cFormat = buf;
-    return CJS_Return(pRuntime->NewString(cFormat.c_str()));
   }
 
-  return CJS_Return(JSGetStringFromID(JSMessage::kTypeError));
+  struct tm time = {};
+  time.tm_year = year - 1900;
+  time.tm_mon = month - 1;
+  time.tm_mday = day;
+  time.tm_hour = hour;
+  time.tm_min = min;
+  time.tm_sec = sec;
+
+  wchar_t buf[64] = {};
+  FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time);
+  cFormat = buf;
+  return CJS_Result::Success(pRuntime->NewString(cFormat.c_str()));
 }
 
-CJS_Return util::printx(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Util::printx(CJS_Runtime* pRuntime,
+                            const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  return CJS_Return(
-      pRuntime->NewString(printx(pRuntime->ToWideString(params[0]),
-                                 pRuntime->ToWideString(params[1]))
-                              .c_str()));
+  return CJS_Result::Success(
+      pRuntime->NewString(StringPrintx(pRuntime->ToWideString(params[0]),
+                                       pRuntime->ToWideString(params[1]))
+                              .AsStringView()));
 }
 
-enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
-
-static wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
-  if (eMode == kLowerCase && FXSYS_isupper(input))
-    return input | 0x20;
-  if (eMode == kUpperCase && FXSYS_islower(input))
-    return input & ~0x20;
-  return input;
-}
-
-WideString util::printx(const WideString& wsFormat,
-                        const WideString& wsSource) {
+// static
+WideString CJS_Util::StringPrintx(const WideString& wsFormat,
+                                  const WideString& wsSource) {
   WideString wsResult;
+  wsResult.Reserve(wsFormat.GetLength());
   size_t iSourceIdx = 0;
   size_t iFormatIdx = 0;
   CaseMode eCaseMode = kPreserveCase;
@@ -311,7 +316,7 @@
       } break;
       case 'X': {
         if (iSourceIdx < wsSource.GetLength()) {
-          if (FXSYS_iswalnum(wsSource[iSourceIdx])) {
+          if (isascii(wsSource[iSourceIdx]) && isalnum(wsSource[iSourceIdx])) {
             wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
             ++iFormatIdx;
           }
@@ -322,7 +327,7 @@
       } break;
       case 'A': {
         if (iSourceIdx < wsSource.GetLength()) {
-          if (FXSYS_iswalpha(wsSource[iSourceIdx])) {
+          if (isascii(wsSource[iSourceIdx]) && isalpha(wsSource[iSourceIdx])) {
             wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
             ++iFormatIdx;
           }
@@ -333,7 +338,7 @@
       } break;
       case '9': {
         if (iSourceIdx < wsSource.GetLength()) {
-          if (std::iswdigit(wsSource[iSourceIdx])) {
+          if (FXSYS_IsDecimalDigit(wsSource[iSourceIdx])) {
             wsResult += wsSource[iSourceIdx];
             ++iFormatIdx;
           }
@@ -359,47 +364,45 @@
   return wsResult;
 }
 
-CJS_Return util::scand(CJS_Runtime* pRuntime,
-                       const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Util::scand(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 2)
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString sFormat = pRuntime->ToWideString(params[0]);
   WideString sDate = pRuntime->ToWideString(params[1]);
-  double dDate = JS_GetDateTime();
+  double dDate = FX_GetDateTime();
   if (sDate.GetLength() > 0)
-    dDate = CJS_PublicMethods::MakeRegularDate(sDate, sFormat, nullptr);
-
+    dDate = CJS_PublicMethods::ParseDateUsingFormat(sDate, sFormat, nullptr);
   if (std::isnan(dDate))
-    return CJS_Return(pRuntime->NewUndefined());
-  return CJS_Return(pRuntime->NewDate(dDate));
+    return CJS_Result::Success(pRuntime->NewUndefined());
+
+  return CJS_Result::Success(pRuntime->NewDate(dDate));
 }
 
-CJS_Return util::byteToChar(CJS_Runtime* pRuntime,
-                            const std::vector<v8::Local<v8::Value>>& params) {
+CJS_Result CJS_Util::byteToChar(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int arg = pRuntime->ToInt32(params[0]);
   if (arg < 0 || arg > 255)
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
   WideString wStr(static_cast<wchar_t>(arg));
-  return CJS_Return(pRuntime->NewString(wStr.c_str()));
+  return CJS_Result::Success(pRuntime->NewString(wStr.AsStringView()));
 }
 
-// Ensure that sFormat contains at most one well-understood printf formatting
-// directive which is safe to use with a single argument, and return the type
-// of argument expected, or -1 otherwise. If -1 is returned, it is NOT safe
-// to use sFormat with printf() and it must be copied byte-by-byte.
-int util::ParseDataType(std::wstring* sFormat) {
+// static
+int CJS_Util::ParseDataType(WideString* sFormat) {
   enum State { BEFORE, FLAGS, WIDTH, PRECISION, SPECIFIER, AFTER };
 
   int result = -1;
   State state = BEFORE;
   size_t precision_digits = 0;
   size_t i = 0;
-  while (i < sFormat->length()) {
+  while (i < sFormat->GetLength()) {
     wchar_t c = (*sFormat)[i];
     switch (state) {
       case BEFORE:
@@ -417,7 +420,7 @@
       case WIDTH:
         if (c == L'*')
           return -1;
-        if (std::iswdigit(c)) {
+        if (FXSYS_IsDecimalDigit(c)) {
           // Stay in same state.
         } else if (c == L'.') {
           state = PRECISION;
@@ -429,7 +432,7 @@
       case PRECISION:
         if (c == L'*')
           return -1;
-        if (std::iswdigit(c)) {
+        if (FXSYS_IsDecimalDigit(c)) {
           // Stay in same state.
           ++precision_digits;
         } else {
@@ -448,7 +451,7 @@
           // Map s to S since we always deal internally with wchar_t strings.
           // TODO(tsepez): Probably 100% borked. %S is not a standard
           // conversion.
-          (*sFormat)[i] = L'S';
+          sFormat->SetAt(i, L'S');
           result = UTIL_STRING;
         } else {
           return -1;
diff --git a/fxjs/cjs_util.h b/fxjs/cjs_util.h
index cc2026d..6f55b3a 100644
--- a/fxjs/cjs_util.h
+++ b/fxjs/cjs_util.h
@@ -7,57 +7,59 @@
 #ifndef FXJS_CJS_UTIL_H_
 #define FXJS_CJS_UTIL_H_
 
-#include <string>
 #include <vector>
 
-#include "fxjs/JS_Define.h"
+#include "core/fxcrt/widestring.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/js_define.h"
 
 // Return values for ParseDataType() below.
 #define UTIL_INT 0
 #define UTIL_DOUBLE 1
 #define UTIL_STRING 2
 
-class util : public CJS_EmbedObj {
+class CJS_Util final : public CJS_Object {
  public:
-  explicit util(CJS_Object* pJSObject);
-  ~util() override;
-
-  CJS_Return printd(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return printf(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return printx(CJS_Runtime* pRuntime,
-                    const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return scand(CJS_Runtime* pRuntime,
-                   const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Return byteToChar(CJS_Runtime* pRuntime,
-                        const std::vector<v8::Local<v8::Value>>& params);
-
-  static WideString printx(const WideString& cFormat,
-                           const WideString& cSource);
-
- private:
-  friend class CJS_Util_ParseDataType_Test;
-
-  static int ParseDataType(std::wstring* sFormat);
-};
-
-class CJS_Util : public CJS_Object {
- public:
+  static int GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Util(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Util() override {}
+  CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
+  ~CJS_Util() override;
 
-  JS_STATIC_METHOD(printd, util);
-  JS_STATIC_METHOD(printf, util);
-  JS_STATIC_METHOD(printx, util);
-  JS_STATIC_METHOD(scand, util);
-  JS_STATIC_METHOD(byteToChar, util);
+  // Ensure that |sFormat| contains at most one well-understood printf
+  // formatting directive which is safe to use with a single argument, and
+  // return the type of argument expected, or -1 otherwise. If -1 is returned,
+  // it is NOT safe to use |sFormat| with printf() and it must be copied
+  // byte-by-byte.
+  //
+  // Exposed for testing.
+  static int ParseDataType(WideString* sFormat);
+
+  // Exposed for testing.
+  static WideString StringPrintx(const WideString& cFormat,
+                                 const WideString& cSource);
+
+  JS_STATIC_METHOD(printd, CJS_Util)
+  JS_STATIC_METHOD(printf, CJS_Util)
+  JS_STATIC_METHOD(printx, CJS_Util)
+  JS_STATIC_METHOD(scand, CJS_Util)
+  JS_STATIC_METHOD(byteToChar, CJS_Util)
 
  private:
   static int ObjDefnID;
+  static const char kName[];
   static const JSMethodSpec MethodSpecs[];
+
+  CJS_Result printd(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result printf(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result printx(CJS_Runtime* pRuntime,
+                    const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result scand(CJS_Runtime* pRuntime,
+                   const std::vector<v8::Local<v8::Value>>& params);
+  CJS_Result byteToChar(CJS_Runtime* pRuntime,
+                        const std::vector<v8::Local<v8::Value>>& params);
 };
 
 #endif  // FXJS_CJS_UTIL_H_
diff --git a/fxjs/cjs_util_unittest.cpp b/fxjs/cjs_util_unittest.cpp
index 6f43f0f..d405746 100644
--- a/fxjs/cjs_util_unittest.cpp
+++ b/fxjs/cjs_util_unittest.cpp
@@ -5,7 +5,6 @@
 #include "fxjs/cjs_util.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 
 TEST(CJS_Util, ParseDataType) {
   struct ParseDataTypeCase {
@@ -106,8 +105,8 @@
   };
 
   for (size_t i = 0; i < FX_ArraySize(cases); i++) {
-    std::wstring input(cases[i].input_string);
-    EXPECT_EQ(cases[i].expected, util::ParseDataType(&input))
+    WideString input(cases[i].input_string);
+    EXPECT_EQ(cases[i].expected, CJS_Util::ParseDataType(&input))
         << cases[i].input_string;
   }
 }
diff --git a/fxjs/cjs_v8.cpp b/fxjs/cjs_v8.cpp
deleted file mode 100644
index 8f77ec6..0000000
--- a/fxjs/cjs_v8.cpp
+++ /dev/null
@@ -1,241 +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 "fxjs/cjs_v8.h"
-
-#ifdef PDF_ENABLE_XFA
-#include "fxjs/cfxjse_context.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
-#endif  // PDF_ENABLE_XFA
-
-CJS_V8::CJS_V8(v8::Isolate* isolate) : m_isolate(isolate) {}
-
-CJS_V8::~CJS_V8() {
-  m_V8PersistentContext.Reset();
-}
-
-v8::Local<v8::Value> CJS_V8::GetObjectProperty(
-    v8::Local<v8::Object> pObj,
-    const WideString& wsPropertyName) {
-  if (pObj.IsEmpty())
-    return v8::Local<v8::Value>();
-  v8::Local<v8::Value> val;
-  if (!pObj->Get(m_isolate->GetCurrentContext(),
-                 NewString(wsPropertyName.AsStringView()))
-           .ToLocal(&val))
-    return v8::Local<v8::Value>();
-  return val;
-}
-
-std::vector<WideString> CJS_V8::GetObjectPropertyNames(
-    v8::Local<v8::Object> pObj) {
-  if (pObj.IsEmpty())
-    return std::vector<WideString>();
-
-  v8::Local<v8::Array> val;
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  if (!pObj->GetPropertyNames(context).ToLocal(&val))
-    return std::vector<WideString>();
-
-  std::vector<WideString> result;
-  for (uint32_t i = 0; i < val->Length(); ++i) {
-    result.push_back(ToWideString(val->Get(context, i).ToLocalChecked()));
-  }
-
-  return result;
-}
-
-void CJS_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
-                               const WideString& wsPropertyName,
-                               v8::Local<v8::Value> pPut) {
-  if (pObj.IsEmpty())
-    return;
-  pObj->Set(m_isolate->GetCurrentContext(),
-            NewString(wsPropertyName.AsStringView()), pPut)
-      .FromJust();
-}
-
-v8::Local<v8::Array> CJS_V8::NewArray() {
-  return v8::Array::New(m_isolate);
-}
-
-unsigned CJS_V8::PutArrayElement(v8::Local<v8::Array> pArray,
-                                 unsigned index,
-                                 v8::Local<v8::Value> pValue) {
-  if (pArray.IsEmpty())
-    return 0;
-  if (pArray->Set(m_isolate->GetCurrentContext(), index, pValue).IsNothing())
-    return 0;
-  return 1;
-}
-
-v8::Local<v8::Value> CJS_V8::GetArrayElement(v8::Local<v8::Array> pArray,
-                                             unsigned index) {
-  if (pArray.IsEmpty())
-    return v8::Local<v8::Value>();
-  v8::Local<v8::Value> val;
-  if (!pArray->Get(m_isolate->GetCurrentContext(), index).ToLocal(&val))
-    return v8::Local<v8::Value>();
-  return val;
-}
-
-unsigned CJS_V8::GetArrayLength(v8::Local<v8::Array> pArray) {
-  if (pArray.IsEmpty())
-    return 0;
-  return pArray->Length();
-}
-
-v8::Local<v8::Context> CJS_V8::NewLocalContext() {
-  return v8::Local<v8::Context>::New(m_isolate, m_V8PersistentContext);
-}
-
-v8::Local<v8::Context> CJS_V8::GetPersistentContext() {
-  return m_V8PersistentContext.Get(m_isolate);
-}
-
-v8::Local<v8::Number> CJS_V8::NewNumber(int number) {
-  return v8::Int32::New(m_isolate, number);
-}
-
-v8::Local<v8::Number> CJS_V8::NewNumber(double number) {
-  return v8::Number::New(m_isolate, number);
-}
-
-v8::Local<v8::Number> CJS_V8::NewNumber(float number) {
-  return v8::Number::New(m_isolate, (float)number);
-}
-
-v8::Local<v8::Boolean> CJS_V8::NewBoolean(bool b) {
-  return v8::Boolean::New(m_isolate, b);
-}
-
-v8::Local<v8::String> CJS_V8::NewString(const ByteStringView& str) {
-  v8::Isolate* pIsolate = m_isolate ? m_isolate : v8::Isolate::GetCurrent();
-  return v8::String::NewFromUtf8(pIsolate, str.unterminated_c_str(),
-                                 v8::NewStringType::kNormal, str.GetLength())
-      .ToLocalChecked();
-}
-
-v8::Local<v8::String> CJS_V8::NewString(const WideStringView& str) {
-  // Conversion from pdfium's wchar_t wide-strings to v8's uint16_t
-  // wide-strings isn't handled by v8, so use UTF8 as a common
-  // intermediate format.
-  return NewString(FX_UTF8Encode(str).AsStringView());
-}
-
-v8::Local<v8::Value> CJS_V8::NewNull() {
-  return v8::Null(m_isolate);
-}
-
-v8::Local<v8::Value> CJS_V8::NewUndefined() {
-  return v8::Undefined(m_isolate);
-}
-
-v8::Local<v8::Date> CJS_V8::NewDate(double d) {
-  return v8::Date::New(m_isolate->GetCurrentContext(), d)
-      .ToLocalChecked()
-      .As<v8::Date>();
-}
-
-int CJS_V8::ToInt32(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return 0;
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  v8::MaybeLocal<v8::Int32> maybe_int32 = pValue->ToInt32(context);
-  if (maybe_int32.IsEmpty())
-    return 0;
-  return maybe_int32.ToLocalChecked()->Value();
-}
-
-bool CJS_V8::ToBoolean(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return false;
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  v8::MaybeLocal<v8::Boolean> maybe_boolean = pValue->ToBoolean(context);
-  if (maybe_boolean.IsEmpty())
-    return false;
-  return maybe_boolean.ToLocalChecked()->Value();
-}
-
-double CJS_V8::ToDouble(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return 0.0;
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  v8::MaybeLocal<v8::Number> maybe_number = pValue->ToNumber(context);
-  if (maybe_number.IsEmpty())
-    return 0.0;
-  return maybe_number.ToLocalChecked()->Value();
-}
-
-WideString CJS_V8::ToWideString(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return WideString();
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
-  if (maybe_string.IsEmpty())
-    return WideString();
-  v8::String::Utf8Value s(m_isolate, maybe_string.ToLocalChecked());
-  return WideString::FromUTF8(ByteStringView(*s, s.length()));
-}
-
-ByteString CJS_V8::ToByteString(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return ByteString();
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
-  if (maybe_string.IsEmpty())
-    return ByteString();
-  v8::String::Utf8Value s(m_isolate, maybe_string.ToLocalChecked());
-  return ByteString(*s);
-}
-
-v8::Local<v8::Object> CJS_V8::ToObject(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty() || !pValue->IsObject())
-    return v8::Local<v8::Object>();
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  return pValue->ToObject(context).ToLocalChecked();
-}
-
-v8::Local<v8::Array> CJS_V8::ToArray(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty() || !pValue->IsArray())
-    return v8::Local<v8::Array>();
-  v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
-  return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
-}
-
-void CJS_V8::SetConstArray(const WideString& name, v8::Local<v8::Array> array) {
-  m_ConstArrays[name] = v8::Global<v8::Array>(GetIsolate(), array);
-}
-
-v8::Local<v8::Array> CJS_V8::GetConstArray(const WideString& name) {
-  return v8::Local<v8::Array>::New(GetIsolate(), m_ConstArrays[name]);
-}
-
-#ifdef PDF_ENABLE_XFA
-CXFA_Object* CJS_V8::ToXFAObject(v8::Local<v8::Value> obj) {
-  ASSERT(!obj.IsEmpty());
-
-  if (!obj->IsObject())
-    return nullptr;
-
-  CFXJSE_HostObject* hostObj =
-      FXJSE_RetrieveObjectBinding(obj.As<v8::Object>(), nullptr);
-  if (!hostObj || hostObj->type() != CFXJSE_HostObject::kXFA)
-    return nullptr;
-  return static_cast<CXFA_Object*>(hostObj);
-}
-
-v8::Local<v8::Value> CJS_V8::NewXFAObject(
-    CXFA_Object* obj,
-    v8::Global<v8::FunctionTemplate>& tmpl) {
-  v8::EscapableHandleScope scope(m_isolate);
-  v8::Local<v8::FunctionTemplate> klass =
-      v8::Local<v8::FunctionTemplate>::New(m_isolate, tmpl);
-  v8::Local<v8::Object> object = klass->InstanceTemplate()->NewInstance();
-  FXJSE_UpdateObjectBinding(object, obj);
-  return scope.Escape(object);
-}
-#endif  // PDF_ENABLE_XFA
diff --git a/fxjs/cjs_v8.h b/fxjs/cjs_v8.h
deleted file mode 100644
index b79d236..0000000
--- a/fxjs/cjs_v8.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_CJS_V8_H_
-#define FXJS_CJS_V8_H_
-
-#include <v8-util.h>
-#include <v8.h>
-
-#include <map>
-#include <vector>
-
-#include "core/fxcrt/bytestring.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/widestring.h"
-
-#ifdef PDF_ENABLE_XFA
-class CXFA_Object;
-#endif  // PDF_ENABLE_XFA
-
-class CJS_V8 {
- public:
-  explicit CJS_V8(v8::Isolate* pIsolate);
-  virtual ~CJS_V8();
-
-  v8::Isolate* GetIsolate() const { return m_isolate; }
-
-  v8::Local<v8::Context> NewLocalContext();
-  v8::Local<v8::Context> GetPersistentContext();
-
-  v8::Local<v8::Value> NewNull();
-  v8::Local<v8::Value> NewUndefined();
-  v8::Local<v8::Array> NewArray();
-  v8::Local<v8::Number> NewNumber(int number);
-  v8::Local<v8::Number> NewNumber(double number);
-  v8::Local<v8::Number> NewNumber(float number);
-  v8::Local<v8::Boolean> NewBoolean(bool b);
-  v8::Local<v8::String> NewString(const ByteStringView& str);
-  v8::Local<v8::String> NewString(const WideStringView& str);
-  v8::Local<v8::Date> NewDate(double d);
-
-  int ToInt32(v8::Local<v8::Value> pValue);
-  bool ToBoolean(v8::Local<v8::Value> pValue);
-  double ToDouble(v8::Local<v8::Value> pValue);
-  WideString ToWideString(v8::Local<v8::Value> pValue);
-  ByteString ToByteString(v8::Local<v8::Value> pValue);
-  v8::Local<v8::Object> ToObject(v8::Local<v8::Value> pValue);
-  v8::Local<v8::Array> ToArray(v8::Local<v8::Value> pValue);
-
-#ifdef PDF_ENABLE_XFA
-  CXFA_Object* ToXFAObject(v8::Local<v8::Value> obj);
-  v8::Local<v8::Value> NewXFAObject(CXFA_Object* obj,
-                                    v8::Global<v8::FunctionTemplate>& tmpl);
-#endif  // PDF_ENABLE_XFA
-
-  // Arrays.
-  unsigned GetArrayLength(v8::Local<v8::Array> pArray);
-  v8::Local<v8::Value> GetArrayElement(v8::Local<v8::Array> pArray,
-                                       unsigned index);
-  unsigned PutArrayElement(v8::Local<v8::Array> pArray,
-                           unsigned index,
-                           v8::Local<v8::Value> pValue);
-
-  void SetConstArray(const WideString& name, v8::Local<v8::Array> array);
-  v8::Local<v8::Array> GetConstArray(const WideString& name);
-
-  // Objects.
-  std::vector<WideString> GetObjectPropertyNames(v8::Local<v8::Object> pObj);
-  v8::Local<v8::Value> GetObjectProperty(v8::Local<v8::Object> pObj,
-                                         const WideString& PropertyName);
-  void PutObjectProperty(v8::Local<v8::Object> pObj,
-                         const WideString& PropertyName,
-                         v8::Local<v8::Value> pValue);
-
- protected:
-  void SetIsolate(v8::Isolate* pIsolate) { m_isolate = pIsolate; }
-  void ClearConstArray() { m_ConstArrays.clear(); }
-
-  void ResetPersistentContext(v8::Local<v8::Context> context) {
-    m_V8PersistentContext.Reset(m_isolate, context);
-  }
-  void ReleasePersistentContext() { m_V8PersistentContext.Reset(); }
-
- private:
-  v8::Isolate* m_isolate;
-  std::map<WideString, v8::Global<v8::Array>> m_ConstArrays;
-  v8::Global<v8::Context> m_V8PersistentContext;
-};
-
-#endif  // FXJS_CJS_V8_H_
diff --git a/fxjs/cjs_zoomtype.cpp b/fxjs/cjs_zoomtype.cpp
index 528bba0..cdaa2d5 100644
--- a/fxjs/cjs_zoomtype.cpp
+++ b/fxjs/cjs_zoomtype.cpp
@@ -21,5 +21,5 @@
 void CJS_Zoomtype::DefineJSObjects(CFXJS_Engine* pEngine) {
   ObjDefnID =
       pEngine->DefineObj("zoomtype", FXJSOBJTYPE_STATIC, nullptr, nullptr);
-  DefineConsts(pEngine, ObjDefnID, ConstSpecs, FX_ArraySize(ConstSpecs));
+  DefineConsts(pEngine, ObjDefnID, ConstSpecs);
 }
diff --git a/fxjs/cjs_zoomtype.h b/fxjs/cjs_zoomtype.h
index 14db0a4..de268cd 100644
--- a/fxjs/cjs_zoomtype.h
+++ b/fxjs/cjs_zoomtype.h
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_ZOOMTYPE_H_
 #define FXJS_CJS_ZOOMTYPE_H_
 
-#include "fxjs/JS_Define.h"
+#include "fxjs/cjs_object.h"
 
-class CJS_Zoomtype : public CJS_Object {
+class CJS_Zoomtype final : public CJS_Object {
  public:
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  explicit CJS_Zoomtype(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
-  ~CJS_Zoomtype() override {}
+  CJS_Zoomtype() = delete;
 
  private:
   static int ObjDefnID;
diff --git a/fxjs/fx_date_helpers.cpp b/fxjs/fx_date_helpers.cpp
new file mode 100644
index 0000000..5256fb1
--- /dev/null
+++ b/fxjs/fx_date_helpers.cpp
@@ -0,0 +1,551 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/fx_date_helpers.h"
+
+#include <time.h>
+
+#include <cmath>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_system.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+
+namespace fxjs {
+namespace {
+
+constexpr uint16_t daysMonth[12] = {0,   31,  59,  90,  120, 151,
+                                    181, 212, 243, 273, 304, 334};
+constexpr uint16_t leapDaysMonth[12] = {0,   31,  60,  91,  121, 152,
+                                        182, 213, 244, 274, 305, 335};
+
+double Mod(double x, double y) {
+  double r = fmod(x, y);
+  if (r < 0)
+    r += y;
+  return r;
+}
+
+double GetLocalTZA() {
+  if (!IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
+    return 0;
+  time_t t = 0;
+  FXSYS_time(&t);
+  FXSYS_localtime(&t);
+#if defined(OS_WIN)
+  // In gcc 'timezone' is a global variable declared in time.h. In VC++, that
+  // variable was removed in VC++ 2015, with _get_timezone replacing it.
+  long timezone = 0;
+  _get_timezone(&timezone);
+#endif
+  return (double)(-(timezone * 1000));
+}
+
+int GetDaylightSavingTA(double d) {
+  if (!IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
+    return 0;
+  time_t t = (time_t)(d / 1000);
+  struct tm* tmp = FXSYS_localtime(&t);
+  if (!tmp)
+    return 0;
+  if (tmp->tm_isdst > 0)
+    // One hour.
+    return (int)60 * 60 * 1000;
+  return 0;
+}
+
+bool IsLeapYear(int year) {
+  return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 != 0));
+}
+
+int DayFromYear(int y) {
+  return (int)(365 * (y - 1970.0) + floor((y - 1969.0) / 4) -
+               floor((y - 1901.0) / 100) + floor((y - 1601.0) / 400));
+}
+
+double TimeFromYear(int y) {
+  return 86400000.0 * DayFromYear(y);
+}
+
+double TimeFromYearMonth(int y, int m) {
+  const uint16_t* pMonth = IsLeapYear(y) ? leapDaysMonth : daysMonth;
+  return TimeFromYear(y) + ((double)pMonth[m]) * 86400000;
+}
+
+int Day(double t) {
+  return static_cast<int>(floor(t / 86400000.0));
+}
+
+int YearFromTime(double t) {
+  // estimate the time.
+  int y = 1970 + static_cast<int>(t / (365.2425 * 86400000.0));
+  if (TimeFromYear(y) <= t) {
+    while (TimeFromYear(y + 1) <= t)
+      y++;
+  } else {
+    while (TimeFromYear(y) > t)
+      y--;
+  }
+  return y;
+}
+
+int DayWithinYear(double t) {
+  int year = YearFromTime(t);
+  int day = Day(t);
+  return day - DayFromYear(year);
+}
+
+int MonthFromTime(double t) {
+  // Check for negative |day| values and check for January.
+  int day = DayWithinYear(t);
+  if (day < 0)
+    return -1;
+  if (day < 31)
+    return 0;
+
+  if (IsLeapYear(YearFromTime(t)))
+    --day;
+
+  // Check for February onwards.
+  static constexpr int kCumulativeDaysInMonths[] = {
+      59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
+  for (size_t i = 0; i < FX_ArraySize(kCumulativeDaysInMonths); ++i) {
+    if (day < kCumulativeDaysInMonths[i])
+      return i + 1;
+  }
+
+  return -1;
+}
+
+int DateFromTime(double t) {
+  int day = DayWithinYear(t);
+  int year = YearFromTime(t);
+  int leap = IsLeapYear(year);
+  int month = MonthFromTime(t);
+  switch (month) {
+    case 0:
+      return day + 1;
+    case 1:
+      return day - 30;
+    case 2:
+      return day - 58 - leap;
+    case 3:
+      return day - 89 - leap;
+    case 4:
+      return day - 119 - leap;
+    case 5:
+      return day - 150 - leap;
+    case 6:
+      return day - 180 - leap;
+    case 7:
+      return day - 211 - leap;
+    case 8:
+      return day - 242 - leap;
+    case 9:
+      return day - 272 - leap;
+    case 10:
+      return day - 303 - leap;
+    case 11:
+      return day - 333 - leap;
+    default:
+      return 0;
+  }
+}
+
+size_t FindSubWordLength(const WideString& str, size_t nStart) {
+  pdfium::span<const wchar_t> data = str.span();
+  size_t i = nStart;
+  while (i < data.size() && std::iswalnum(data[i]))
+    ++i;
+  return i - nStart;
+}
+
+}  // namespace
+
+const wchar_t* const kMonths[12] = {L"Jan", L"Feb", L"Mar", L"Apr",
+                                    L"May", L"Jun", L"Jul", L"Aug",
+                                    L"Sep", L"Oct", L"Nov", L"Dec"};
+
+const wchar_t* const kFullMonths[12] = {L"January", L"February", L"March",
+                                        L"April",   L"May",      L"June",
+                                        L"July",    L"August",   L"September",
+                                        L"October", L"November", L"December"};
+
+static constexpr size_t KMonthAbbreviationLength = 3;  // Anything in |kMonths|.
+static constexpr size_t kLongestFullMonthLength = 9;   // September
+
+double FX_GetDateTime() {
+  if (!IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
+    return 0;
+
+  time_t t = FXSYS_time(nullptr);
+  struct tm* pTm = FXSYS_localtime(&t);
+  double t1 = TimeFromYear(pTm->tm_year + 1900);
+  return t1 + pTm->tm_yday * 86400000.0 + pTm->tm_hour * 3600000.0 +
+         pTm->tm_min * 60000.0 + pTm->tm_sec * 1000.0;
+}
+
+int FX_GetYearFromTime(double dt) {
+  return YearFromTime(dt);
+}
+
+int FX_GetMonthFromTime(double dt) {
+  return MonthFromTime(dt);
+}
+
+int FX_GetDayFromTime(double dt) {
+  return DateFromTime(dt);
+}
+
+int FX_GetHourFromTime(double dt) {
+  return (int)Mod(floor(dt / (60 * 60 * 1000)), 24);
+}
+
+int FX_GetMinFromTime(double dt) {
+  return (int)Mod(floor(dt / (60 * 1000)), 60);
+}
+
+int FX_GetSecFromTime(double dt) {
+  return (int)Mod(floor(dt / 1000), 60);
+}
+
+bool FX_IsValidMonth(int m) {
+  return m >= 1 && m <= 12;
+}
+
+// TODO(thestig): Should this take the month into consideration?
+bool FX_IsValidDay(int d) {
+  return d >= 1 && d <= 31;
+}
+
+// TODO(thestig): Should 24 be allowed? Similarly, 60 for minutes and seconds.
+bool FX_IsValid24Hour(int h) {
+  return h >= 0 && h <= 24;
+}
+
+bool FX_IsValidMinute(int m) {
+  return m >= 0 && m <= 60;
+}
+
+bool FX_IsValidSecond(int s) {
+  return s >= 0 && s <= 60;
+}
+
+double FX_LocalTime(double d) {
+  return d + GetLocalTZA() + GetDaylightSavingTA(d);
+}
+
+double FX_MakeDay(int nYear, int nMonth, int nDate) {
+  double y = static_cast<double>(nYear);
+  double m = static_cast<double>(nMonth);
+  double dt = static_cast<double>(nDate);
+  double ym = y + floor(m / 12);
+  double mn = Mod(m, 12);
+  double t = TimeFromYearMonth(static_cast<int>(ym), static_cast<int>(mn));
+  if (YearFromTime(t) != ym || MonthFromTime(t) != mn || DateFromTime(t) != 1)
+    return std::nan("");
+
+  return Day(t) + dt - 1;
+}
+
+double FX_MakeTime(int nHour, int nMin, int nSec, int nMs) {
+  double h = static_cast<double>(nHour);
+  double m = static_cast<double>(nMin);
+  double s = static_cast<double>(nSec);
+  double milli = static_cast<double>(nMs);
+  return h * 3600000 + m * 60000 + s * 1000 + milli;
+}
+
+double FX_MakeDate(double day, double time) {
+  if (!std::isfinite(day) || !std::isfinite(time))
+    return std::nan("");
+
+  return day * 86400000 + time;
+}
+
+int FX_ParseStringInteger(const WideString& str,
+                          size_t nStart,
+                          size_t* pSkip,
+                          size_t nMaxStep) {
+  int nRet = 0;
+  size_t nSkip = 0;
+  for (size_t i = nStart; i < str.GetLength(); ++i) {
+    if (i - nStart > 10)
+      break;
+
+    wchar_t c = str[i];
+    if (!FXSYS_IsDecimalDigit(c))
+      break;
+
+    nRet = nRet * 10 + FXSYS_DecimalCharToInt(c);
+    ++nSkip;
+    if (nSkip >= nMaxStep)
+      break;
+  }
+
+  *pSkip = nSkip;
+  return nRet;
+}
+
+ConversionStatus FX_ParseDateUsingFormat(const WideString& value,
+                                         const WideString& format,
+                                         double* result) {
+  double dt = FX_GetDateTime();
+  if (format.IsEmpty() || value.IsEmpty()) {
+    *result = dt;
+    return ConversionStatus::kSuccess;
+  }
+
+  int nYear = FX_GetYearFromTime(dt);
+  int nMonth = FX_GetMonthFromTime(dt) + 1;
+  int nDay = FX_GetDayFromTime(dt);
+  int nHour = FX_GetHourFromTime(dt);
+  int nMin = FX_GetMinFromTime(dt);
+  int nSec = FX_GetSecFromTime(dt);
+  int nYearSub = 99;  // nYear - 2000;
+  bool bPm = false;
+  bool bExit = false;
+  bool bBadFormat = false;
+  size_t i = 0;
+  size_t j = 0;
+
+  while (i < format.GetLength()) {
+    if (bExit)
+      break;
+
+    wchar_t c = format[i];
+    switch (c) {
+      case ':':
+      case '.':
+      case '-':
+      case '\\':
+      case '/':
+        i++;
+        j++;
+        break;
+
+      case 'y':
+      case 'm':
+      case 'd':
+      case 'H':
+      case 'h':
+      case 'M':
+      case 's':
+      case 't': {
+        size_t oldj = j;
+        size_t nSkip = 0;
+        size_t remaining = format.GetLength() - i - 1;
+
+        if (remaining == 0 || format[i + 1] != c) {
+          switch (c) {
+            case 'y':
+              i++;
+              j++;
+              break;
+            case 'm':
+              nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'd':
+              nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'H':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'h':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 'M':
+              nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 's':
+              nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i++;
+              j += nSkip;
+              break;
+            case 't':
+              bPm = (j < value.GetLength() && value[j] == 'p');
+              i++;
+              j++;
+              break;
+          }
+        } else if (remaining == 1 || format[i + 2] != c) {
+          switch (c) {
+            case 'y':
+              nYear = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'm':
+              nMonth = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'd':
+              nDay = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'H':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'h':
+              nHour = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 'M':
+              nMin = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 's':
+              nSec = FX_ParseStringInteger(value, j, &nSkip, 2);
+              i += 2;
+              j += nSkip;
+              break;
+            case 't':
+              bPm = (j + 1 < value.GetLength() && value[j] == 'p' &&
+                     value[j + 1] == 'm');
+              i += 2;
+              j += 2;
+              break;
+          }
+        } else if (remaining == 2 || format[i + 3] != c) {
+          switch (c) {
+            case 'm': {
+              bool bFind = false;
+              nSkip = FindSubWordLength(value, j);
+              if (nSkip == KMonthAbbreviationLength) {
+                WideString sMonth = value.Substr(j, KMonthAbbreviationLength);
+                for (size_t m = 0; m < FX_ArraySize(kMonths); ++m) {
+                  if (sMonth.CompareNoCase(kMonths[m]) == 0) {
+                    nMonth = m + 1;
+                    i += 3;
+                    j += nSkip;
+                    bFind = true;
+                    break;
+                  }
+                }
+              }
+
+              if (!bFind) {
+                nMonth = FX_ParseStringInteger(value, j, &nSkip, 3);
+                i += 3;
+                j += nSkip;
+              }
+            } break;
+            case 'y':
+              break;
+            default:
+              i += 3;
+              j += 3;
+              break;
+          }
+        } else if (remaining == 3 || format[i + 4] != c) {
+          switch (c) {
+            case 'y':
+              nYear = FX_ParseStringInteger(value, j, &nSkip, 4);
+              j += nSkip;
+              i += 4;
+              break;
+            case 'm': {
+              bool bFind = false;
+              nSkip = FindSubWordLength(value, j);
+              if (nSkip <= kLongestFullMonthLength) {
+                WideString sMonth = value.Substr(j, nSkip);
+                sMonth.MakeLower();
+                for (size_t m = 0; m < FX_ArraySize(kFullMonths); ++m) {
+                  WideString sFullMonths = WideString(kFullMonths[m]);
+                  sFullMonths.MakeLower();
+                  if (sFullMonths.Contains(sMonth.c_str())) {
+                    nMonth = m + 1;
+                    i += 4;
+                    j += nSkip;
+                    bFind = true;
+                    break;
+                  }
+                }
+              }
+
+              if (!bFind) {
+                nMonth = FX_ParseStringInteger(value, j, &nSkip, 4);
+                i += 4;
+                j += nSkip;
+              }
+            } break;
+            default:
+              i += 4;
+              j += 4;
+              break;
+          }
+        } else {
+          if (j >= value.GetLength() || format[i] != value[j]) {
+            bBadFormat = true;
+            bExit = true;
+          }
+          i++;
+          j++;
+        }
+
+        if (oldj == j) {
+          bBadFormat = true;
+          bExit = true;
+        }
+        break;
+      }
+
+      default:
+        if (value.GetLength() <= j) {
+          bExit = true;
+        } else if (format[i] != value[j]) {
+          bBadFormat = true;
+          bExit = true;
+        }
+
+        i++;
+        j++;
+        break;
+    }
+  }
+
+  if (bBadFormat)
+    return ConversionStatus::kBadFormat;
+
+  if (bPm)
+    nHour += 12;
+
+  if (nYear >= 0 && nYear <= nYearSub)
+    nYear += 2000;
+
+  if (!FX_IsValidMonth(nMonth) || !FX_IsValidDay(nDay) ||
+      !FX_IsValid24Hour(nHour) || !FX_IsValidMinute(nMin) ||
+      !FX_IsValidSecond(nSec)) {
+    return ConversionStatus::kBadDate;
+  }
+
+  dt = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
+                   FX_MakeTime(nHour, nMin, nSec, 0));
+  if (std::isnan(dt))
+    return ConversionStatus::kBadDate;
+
+  *result = dt;
+  return ConversionStatus::kSuccess;
+}
+
+}  // namespace fxjs
diff --git a/fxjs/fx_date_helpers.h b/fxjs/fx_date_helpers.h
new file mode 100644
index 0000000..3657694
--- /dev/null
+++ b/fxjs/fx_date_helpers.h
@@ -0,0 +1,68 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_FX_DATE_HELPERS_H_
+#define FXJS_FX_DATE_HELPERS_H_
+
+#include <stddef.h>
+
+#include "core/fxcrt/fx_string.h"
+
+namespace fxjs {
+
+enum class ConversionStatus { kSuccess = 0, kBadFormat, kBadDate };
+
+extern const wchar_t* const kMonths[12];
+extern const wchar_t* const kFullMonths[12];
+
+double FX_GetDateTime();
+int FX_GetYearFromTime(double dt);
+int FX_GetMonthFromTime(double dt);
+int FX_GetDayFromTime(double dt);
+int FX_GetHourFromTime(double dt);
+int FX_GetMinFromTime(double dt);
+int FX_GetSecFromTime(double dt);
+bool FX_IsValidMonth(int m);
+bool FX_IsValidDay(int d);
+bool FX_IsValid24Hour(int h);
+bool FX_IsValidMinute(int m);
+bool FX_IsValidSecond(int s);
+double FX_LocalTime(double d);
+double FX_MakeDay(int nYear, int nMonth, int nDay);
+double FX_MakeTime(int nHour, int nMin, int nSec, int nMs);
+double FX_MakeDate(double day, double time);
+
+int FX_ParseStringInteger(const WideString& str,
+                          size_t nStart,
+                          size_t* pSkip,
+                          size_t nMaxStep);
+
+ConversionStatus FX_ParseDateUsingFormat(const WideString& value,
+                                         const WideString& format,
+                                         double* result);
+
+}  // namespace fxjs
+
+using fxjs::FX_GetDateTime;
+using fxjs::FX_GetDayFromTime;
+using fxjs::FX_GetHourFromTime;
+using fxjs::FX_GetMinFromTime;
+using fxjs::FX_GetMonthFromTime;
+using fxjs::FX_GetSecFromTime;
+using fxjs::FX_GetYearFromTime;
+using fxjs::FX_IsValid24Hour;
+using fxjs::FX_IsValidDay;
+using fxjs::FX_IsValidMinute;
+using fxjs::FX_IsValidMonth;
+using fxjs::FX_IsValidSecond;
+using fxjs::FX_LocalTime;
+using fxjs::FX_MakeDate;
+using fxjs::FX_MakeDay;
+using fxjs::FX_MakeTime;
+using fxjs::FX_ParseDateUsingFormat;
+using fxjs::FX_ParseStringInteger;
+
+#endif  // FXJS_FX_DATE_HELPERS_H_
diff --git a/fxjs/fx_date_helpers_unittest.cpp b/fxjs/fx_date_helpers_unittest.cpp
new file mode 100644
index 0000000..fb756b9
--- /dev/null
+++ b/fxjs/fx_date_helpers_unittest.cpp
@@ -0,0 +1,108 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/fx_date_helpers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr double kMilliSecondsInADay = 1000 * 60 * 60 * 24;
+
+}  // namespace
+
+TEST(FX_DateHelper, GetYearFromTime) {
+  static constexpr struct {
+    double time_ms;
+    int expected_year;
+  } kTests[] = {
+      {-400 * kMilliSecondsInADay, 1968},
+      {-1, 1969},
+      {0, 1970},
+      {1, 1970},
+      {364.9 * kMilliSecondsInADay, 1970},
+      {365.0 * kMilliSecondsInADay, 1971},
+      {365.1 * kMilliSecondsInADay, 1971},
+      {2 * 365.0 * kMilliSecondsInADay, 1972},
+      // 1972 is a leap year, so there should be an extra day.
+      {3 * 365.0 * kMilliSecondsInADay, 1972},
+      {(3 * 365.0 + 1) * kMilliSecondsInADay, 1973},
+  };
+
+  for (const auto& test : kTests) {
+    EXPECT_EQ(test.expected_year, FX_GetYearFromTime(test.time_ms))
+        << test.time_ms;
+  }
+}
+
+TEST(FX_DateHelper, GetMonthFromTime) {
+  static constexpr struct {
+    double time_ms;
+    int expected_month;  // Zero-based.
+  } kTests[] = {
+      {-400 * kMilliSecondsInADay, 10},
+      {-1, 11},
+      {0, 0},
+      {1, 0},
+      {364.9 * kMilliSecondsInADay, 11},
+      {365.0 * kMilliSecondsInADay, 0},
+      {365.1 * kMilliSecondsInADay, 0},
+      // 1972 is a leap year, so there should be an extra day.
+      {2 * 365.0 * kMilliSecondsInADay, 0},
+      {3 * 365.0 * kMilliSecondsInADay, 11},
+      {(3 * 365.0 + 1) * kMilliSecondsInADay, 0},
+      // Tests boundaries for all months in 1970 not already covered above.
+      {30 * kMilliSecondsInADay, 0},
+      {31 * kMilliSecondsInADay, 1},
+      {58 * kMilliSecondsInADay, 1},
+      {59 * kMilliSecondsInADay, 2},
+      {89 * kMilliSecondsInADay, 2},
+      {90 * kMilliSecondsInADay, 3},
+      {119 * kMilliSecondsInADay, 3},
+      {120 * kMilliSecondsInADay, 4},
+      {150 * kMilliSecondsInADay, 4},
+      {151 * kMilliSecondsInADay, 5},
+      {180 * kMilliSecondsInADay, 5},
+      {181 * kMilliSecondsInADay, 6},
+      {211 * kMilliSecondsInADay, 6},
+      {212 * kMilliSecondsInADay, 7},
+      {242 * kMilliSecondsInADay, 7},
+      {243 * kMilliSecondsInADay, 8},
+      {272 * kMilliSecondsInADay, 8},
+      {273 * kMilliSecondsInADay, 9},
+      {303 * kMilliSecondsInADay, 9},
+      {304 * kMilliSecondsInADay, 10},
+      {333 * kMilliSecondsInADay, 10},
+      {334 * kMilliSecondsInADay, 11},
+      {364 * kMilliSecondsInADay, 11},
+      // Tests boundaries for all months in 1972 not already covered above.
+      {(2 * 365.0 + 30) * kMilliSecondsInADay, 0},
+      {(2 * 365.0 + 31) * kMilliSecondsInADay, 1},
+      {(2 * 365.0 + 59) * kMilliSecondsInADay, 1},
+      {(2 * 365.0 + 60) * kMilliSecondsInADay, 2},
+      {(2 * 365.0 + 90) * kMilliSecondsInADay, 2},
+      {(2 * 365.0 + 91) * kMilliSecondsInADay, 3},
+      {(2 * 365.0 + 120) * kMilliSecondsInADay, 3},
+      {(2 * 365.0 + 121) * kMilliSecondsInADay, 4},
+      {(2 * 365.0 + 151) * kMilliSecondsInADay, 4},
+      {(2 * 365.0 + 152) * kMilliSecondsInADay, 5},
+      {(2 * 365.0 + 181) * kMilliSecondsInADay, 5},
+      {(2 * 365.0 + 182) * kMilliSecondsInADay, 6},
+      {(2 * 365.0 + 212) * kMilliSecondsInADay, 6},
+      {(2 * 365.0 + 213) * kMilliSecondsInADay, 7},
+      {(2 * 365.0 + 243) * kMilliSecondsInADay, 7},
+      {(2 * 365.0 + 244) * kMilliSecondsInADay, 8},
+      {(2 * 365.0 + 273) * kMilliSecondsInADay, 8},
+      {(2 * 365.0 + 274) * kMilliSecondsInADay, 9},
+      {(2 * 365.0 + 304) * kMilliSecondsInADay, 9},
+      {(2 * 365.0 + 305) * kMilliSecondsInADay, 10},
+      {(2 * 365.0 + 334) * kMilliSecondsInADay, 10},
+      {(2 * 365.0 + 335) * kMilliSecondsInADay, 11},
+  };
+
+  for (const auto& test : kTests) {
+    EXPECT_EQ(test.expected_month, FX_GetMonthFromTime(test.time_ms))
+        << test.time_ms;
+  }
+}
diff --git a/fxjs/fxjs_v8.cpp b/fxjs/fxjs_v8.cpp
deleted file mode 100644
index 50345cf..0000000
--- a/fxjs/fxjs_v8.cpp
+++ /dev/null
@@ -1,554 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/fxjs_v8.h"
-
-#include <vector>
-
-#include "third_party/base/allocator/partition_allocator/partition_alloc.h"
-
-// Keep this consistent with the values defined in gin/public/context_holder.h
-// (without actually requiring a dependency on gin itself for the standalone
-// embedders of PDFIum). The value we want to use is:
-//   kPerContextDataStartIndex + kEmbedderPDFium, which is 3.
-static const unsigned int kPerContextDataIndex = 3u;
-static unsigned int g_embedderDataSlot = 1u;
-static v8::Isolate* g_isolate = nullptr;
-static size_t g_isolate_ref_count = 0;
-static FXJS_ArrayBufferAllocator* g_arrayBufferAllocator = nullptr;
-static v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr;
-static wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData";
-
-class CFXJS_PerObjectData {
- public:
-  explicit CFXJS_PerObjectData(int nObjDefID)
-      : m_ObjDefID(nObjDefID), m_pPrivate(nullptr) {}
-
-  static void SetInObject(CFXJS_PerObjectData* pData,
-                          v8::Local<v8::Object> pObj) {
-    if (pObj->InternalFieldCount() == 2) {
-      pObj->SetAlignedPointerInInternalField(0, pData);
-      pObj->SetAlignedPointerInInternalField(
-          1, static_cast<void*>(kPerObjectDataTag));
-    }
-  }
-
-  static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) {
-    if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 ||
-        pObj->GetAlignedPointerFromInternalField(1) !=
-            static_cast<void*>(kPerObjectDataTag)) {
-      return nullptr;
-    }
-    return static_cast<CFXJS_PerObjectData*>(
-        pObj->GetAlignedPointerFromInternalField(0));
-  }
-
-  const int m_ObjDefID;
-  void* m_pPrivate;
-};
-
-class CFXJS_ObjDefinition {
- public:
-  static int MaxID(v8::Isolate* pIsolate) {
-    return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.size();
-  }
-
-  static CFXJS_ObjDefinition* ForID(v8::Isolate* pIsolate, int id) {
-    // Note: GetAt() halts if out-of-range even in release builds.
-    return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray[id].get();
-  }
-
-  CFXJS_ObjDefinition(v8::Isolate* isolate,
-                      const char* sObjName,
-                      FXJSOBJTYPE eObjType,
-                      CFXJS_Engine::Constructor pConstructor,
-                      CFXJS_Engine::Destructor pDestructor)
-      : m_ObjName(sObjName),
-        m_ObjType(eObjType),
-        m_pConstructor(pConstructor),
-        m_pDestructor(pDestructor),
-        m_pIsolate(isolate) {
-    v8::Isolate::Scope isolate_scope(isolate);
-    v8::HandleScope handle_scope(isolate);
-
-    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
-    fun->InstanceTemplate()->SetInternalFieldCount(2);
-    fun->SetCallHandler([](const v8::FunctionCallbackInfo<v8::Value>& info) {
-      v8::Local<v8::Object> holder = info.Holder();
-      ASSERT(holder->InternalFieldCount() == 2);
-      holder->SetAlignedPointerInInternalField(0, nullptr);
-      holder->SetAlignedPointerInInternalField(1, nullptr);
-    });
-    if (eObjType == FXJSOBJTYPE_GLOBAL) {
-      fun->InstanceTemplate()->Set(
-          v8::Symbol::GetToStringTag(isolate),
-          v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal)
-              .ToLocalChecked());
-    }
-    m_FunctionTemplate.Reset(isolate, fun);
-
-    v8::Local<v8::Signature> sig = v8::Signature::New(isolate, fun);
-    m_Signature.Reset(isolate, sig);
-  }
-
-  int AssignID() {
-    FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_pIsolate);
-    pData->m_ObjectDefnArray.emplace_back(this);
-    return pData->m_ObjectDefnArray.size() - 1;
-  }
-
-  v8::Local<v8::ObjectTemplate> GetInstanceTemplate() {
-    v8::EscapableHandleScope scope(m_pIsolate);
-    v8::Local<v8::FunctionTemplate> function =
-        m_FunctionTemplate.Get(m_pIsolate);
-    return scope.Escape(function->InstanceTemplate());
-  }
-
-  v8::Local<v8::Signature> GetSignature() {
-    v8::EscapableHandleScope scope(m_pIsolate);
-    return scope.Escape(m_Signature.Get(m_pIsolate));
-  }
-
-  const char* const m_ObjName;
-  const FXJSOBJTYPE m_ObjType;
-  const CFXJS_Engine::Constructor m_pConstructor;
-  const CFXJS_Engine::Destructor m_pDestructor;
-
-  v8::Isolate* m_pIsolate;
-  v8::Global<v8::FunctionTemplate> m_FunctionTemplate;
-  v8::Global<v8::Signature> m_Signature;
-};
-
-static v8::Local<v8::ObjectTemplate> GetGlobalObjectTemplate(
-    v8::Isolate* pIsolate) {
-  int maxID = CFXJS_ObjDefinition::MaxID(pIsolate);
-  for (int i = 0; i < maxID; ++i) {
-    CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(pIsolate, i);
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL)
-      return pObjDef->GetInstanceTemplate();
-  }
-  if (!g_DefaultGlobalObjectTemplate) {
-    v8::Local<v8::ObjectTemplate> hGlobalTemplate =
-        v8::ObjectTemplate::New(pIsolate);
-    hGlobalTemplate->Set(
-        v8::Symbol::GetToStringTag(pIsolate),
-        v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-            .ToLocalChecked());
-    g_DefaultGlobalObjectTemplate =
-        new v8::Global<v8::ObjectTemplate>(pIsolate, hGlobalTemplate);
-  }
-  return g_DefaultGlobalObjectTemplate->Get(pIsolate);
-}
-
-void* FXJS_ArrayBufferAllocator::Allocate(size_t length) {
-  if (length > kMaxAllowedBytes)
-    return nullptr;
-  void* p = AllocateUninitialized(length);
-  if (p)
-    memset(p, 0, length);
-  return p;
-}
-
-void* FXJS_ArrayBufferAllocator::AllocateUninitialized(size_t length) {
-  if (length > kMaxAllowedBytes)
-    return nullptr;
-  return pdfium::base::PartitionAllocGeneric(
-      gArrayBufferPartitionAllocator.root(), length, "FXJS_ArrayBuffer");
-}
-
-void FXJS_ArrayBufferAllocator::Free(void* data, size_t length) {
-  pdfium::base::PartitionFreeGeneric(gArrayBufferPartitionAllocator.root(),
-                                     data);
-}
-
-void V8TemplateMapTraits::Dispose(v8::Isolate* isolate,
-                                  v8::Global<v8::Object> value,
-                                  void* key) {
-  v8::Local<v8::Object> obj = value.Get(isolate);
-  if (obj.IsEmpty())
-    return;
-  int id = CFXJS_Engine::GetObjDefnID(obj);
-  if (id == -1)
-    return;
-  CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(isolate, id);
-  if (!pObjDef)
-    return;
-  if (pObjDef->m_pDestructor) {
-    pObjDef->m_pDestructor(CFXJS_Engine::CurrentEngineFromIsolate(isolate),
-                           obj);
-  }
-  CFXJS_Engine::FreeObjectPrivate(obj);
-}
-
-V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo(
-    const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
-  V8TemplateMap* pMap =
-      (FXJS_PerIsolateData::Get(data.GetIsolate()))->m_pDynamicObjsMap.get();
-  return pMap ? &pMap->m_map : nullptr;
-}
-
-void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) {
-  if (g_isolate) {
-    ASSERT(g_embedderDataSlot == embedderDataSlot);
-    ASSERT(g_isolate == pIsolate);
-    return;
-  }
-  g_embedderDataSlot = embedderDataSlot;
-  g_isolate = pIsolate;
-}
-
-void FXJS_Release() {
-  ASSERT(!g_isolate || g_isolate_ref_count == 0);
-  delete g_DefaultGlobalObjectTemplate;
-  g_DefaultGlobalObjectTemplate = nullptr;
-  g_isolate = nullptr;
-
-  delete g_arrayBufferAllocator;
-  g_arrayBufferAllocator = nullptr;
-}
-
-bool FXJS_GetIsolate(v8::Isolate** pResultIsolate) {
-  if (g_isolate) {
-    *pResultIsolate = g_isolate;
-    return false;
-  }
-  // Provide backwards compatibility when no external isolate.
-  if (!g_arrayBufferAllocator)
-    g_arrayBufferAllocator = new FXJS_ArrayBufferAllocator();
-  v8::Isolate::CreateParams params;
-  params.array_buffer_allocator = g_arrayBufferAllocator;
-  *pResultIsolate = v8::Isolate::New(params);
-  return true;
-}
-
-size_t FXJS_GlobalIsolateRefCount() {
-  return g_isolate_ref_count;
-}
-
-V8TemplateMap::V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
-
-V8TemplateMap::~V8TemplateMap() {}
-
-void V8TemplateMap::set(void* key, v8::Local<v8::Object> handle) {
-  ASSERT(!m_map.Contains(key));
-  m_map.Set(key, handle);
-}
-
-FXJS_PerIsolateData::~FXJS_PerIsolateData() {}
-
-// static
-void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) {
-  if (!pIsolate->GetData(g_embedderDataSlot))
-    pIsolate->SetData(g_embedderDataSlot, new FXJS_PerIsolateData(pIsolate));
-}
-
-// static
-FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
-  return static_cast<FXJS_PerIsolateData*>(
-      pIsolate->GetData(g_embedderDataSlot));
-}
-
-FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate)
-    : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {}
-
-CFXJS_Engine::CFXJS_Engine() : CJS_V8(nullptr) {}
-
-CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : CJS_V8(pIsolate) {}
-
-CFXJS_Engine::~CFXJS_Engine() = default;
-
-// static
-CFXJS_Engine* CFXJS_Engine::CurrentEngineFromIsolate(v8::Isolate* pIsolate) {
-  return static_cast<CFXJS_Engine*>(
-      pIsolate->GetCurrentContext()->GetAlignedPointerFromEmbedderData(
-          kPerContextDataIndex));
-}
-
-// static
-int CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
-  CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
-  return pData ? pData->m_ObjDefID : -1;
-}
-
-// static
-void CFXJS_Engine::FreeObjectPrivate(void* pPerObjectData) {
-  delete static_cast<CFXJS_PerObjectData*>(pPerObjectData);
-}
-
-// static
-void CFXJS_Engine::FreeObjectPrivate(v8::Local<v8::Object> pObj) {
-  CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
-  pObj->SetAlignedPointerInInternalField(0, nullptr);
-  pObj->SetAlignedPointerInInternalField(1, nullptr);
-  delete pData;
-}
-
-int CFXJS_Engine::DefineObj(const char* sObjName,
-                            FXJSOBJTYPE eObjType,
-                            CFXJS_Engine::Constructor pConstructor,
-                            CFXJS_Engine::Destructor pDestructor) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  FXJS_PerIsolateData::SetUp(GetIsolate());
-  CFXJS_ObjDefinition* pObjDef = new CFXJS_ObjDefinition(
-      GetIsolate(), sObjName, eObjType, pConstructor, pDestructor);
-  return pObjDef->AssignID();
-}
-
-void CFXJS_Engine::DefineObjMethod(int nObjDefnID,
-                                   const char* sMethodName,
-                                   v8::FunctionCallback pMethodCall) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  CFXJS_ObjDefinition* pObjDef =
-      CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID);
-  v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
-      GetIsolate(), pMethodCall, v8::Local<v8::Value>(),
-      pObjDef->GetSignature());
-  fun->RemovePrototype();
-  pObjDef->GetInstanceTemplate()->Set(NewString(sMethodName), fun,
-                                      v8::ReadOnly);
-}
-
-void CFXJS_Engine::DefineObjProperty(int nObjDefnID,
-                                     const char* sPropName,
-                                     v8::AccessorGetterCallback pPropGet,
-                                     v8::AccessorSetterCallback pPropPut) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  CFXJS_ObjDefinition* pObjDef =
-      CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID);
-  pObjDef->GetInstanceTemplate()->SetAccessor(NewString(sPropName), pPropGet,
-                                              pPropPut);
-}
-
-void CFXJS_Engine::DefineObjAllProperties(
-    int nObjDefnID,
-    v8::NamedPropertyQueryCallback pPropQurey,
-    v8::NamedPropertyGetterCallback pPropGet,
-    v8::NamedPropertySetterCallback pPropPut,
-    v8::NamedPropertyDeleterCallback pPropDel) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  CFXJS_ObjDefinition* pObjDef =
-      CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID);
-  pObjDef->GetInstanceTemplate()->SetNamedPropertyHandler(pPropGet, pPropPut,
-                                                          pPropQurey, pPropDel);
-}
-
-void CFXJS_Engine::DefineObjConst(int nObjDefnID,
-                                  const char* sConstName,
-                                  v8::Local<v8::Value> pDefault) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  CFXJS_ObjDefinition* pObjDef =
-      CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID);
-  pObjDef->GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault);
-}
-
-void CFXJS_Engine::DefineGlobalMethod(const char* sMethodName,
-                                      v8::FunctionCallback pMethodCall) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  v8::Local<v8::FunctionTemplate> fun =
-      v8::FunctionTemplate::New(GetIsolate(), pMethodCall);
-  fun->RemovePrototype();
-  GetGlobalObjectTemplate(GetIsolate())
-      ->Set(NewString(sMethodName), fun, v8::ReadOnly);
-}
-
-void CFXJS_Engine::DefineGlobalConst(const wchar_t* sConstName,
-                                     v8::FunctionCallback pConstGetter) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  v8::Local<v8::FunctionTemplate> fun =
-      v8::FunctionTemplate::New(GetIsolate(), pConstGetter);
-  fun->RemovePrototype();
-  GetGlobalObjectTemplate(GetIsolate())
-      ->SetAccessorProperty(NewString(sConstName), fun);
-}
-
-void CFXJS_Engine::InitializeEngine() {
-  if (GetIsolate() == g_isolate)
-    ++g_isolate_ref_count;
-
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-
-  // This has to happen before we call GetGlobalObjectTemplate because that
-  // method gets the PerIsolateData from GetIsolate().
-  FXJS_PerIsolateData::SetUp(GetIsolate());
-
-  v8::Local<v8::Context> v8Context = v8::Context::New(
-      GetIsolate(), nullptr, GetGlobalObjectTemplate(GetIsolate()));
-  v8::Context::Scope context_scope(v8Context);
-
-  v8Context->SetAlignedPointerInEmbedderData(kPerContextDataIndex, this);
-
-  int maxID = CFXJS_ObjDefinition::MaxID(GetIsolate());
-  m_StaticObjects.resize(maxID + 1);
-  for (int i = 0; i < maxID; ++i) {
-    CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(GetIsolate(), i);
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
-      CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i),
-                                       v8Context->Global()
-                                           ->GetPrototype()
-                                           ->ToObject(v8Context)
-                                           .ToLocalChecked());
-      if (pObjDef->m_pConstructor) {
-        pObjDef->m_pConstructor(this, v8Context->Global()
-                                          ->GetPrototype()
-                                          ->ToObject(v8Context)
-                                          .ToLocalChecked());
-      }
-    } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) {
-      v8::Local<v8::String> pObjName = NewString(pObjDef->m_ObjName);
-      v8::Local<v8::Object> obj = NewFxDynamicObj(i, true);
-      if (!obj.IsEmpty()) {
-        v8Context->Global()->Set(v8Context, pObjName, obj).FromJust();
-        m_StaticObjects[i] = new v8::Global<v8::Object>(GetIsolate(), obj);
-      } else {
-        m_StaticObjects[i] = nullptr;
-      }
-    }
-  }
-  ResetPersistentContext(v8Context);
-}
-
-void CFXJS_Engine::ReleaseEngine() {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::HandleScope handle_scope(GetIsolate());
-  v8::Local<v8::Context> context = NewLocalContext();
-  v8::Context::Scope context_scope(context);
-  FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(GetIsolate());
-  if (!pData)
-    return;
-
-  ClearConstArray();
-
-  int maxID = CFXJS_ObjDefinition::MaxID(GetIsolate());
-  for (int i = 0; i < maxID; ++i) {
-    CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(GetIsolate(), i);
-    v8::Local<v8::Object> pObj;
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
-      pObj =
-          context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
-    } else if (m_StaticObjects[i] && !m_StaticObjects[i]->IsEmpty()) {
-      pObj = v8::Local<v8::Object>::New(GetIsolate(), *m_StaticObjects[i]);
-      delete m_StaticObjects[i];
-      m_StaticObjects[i] = nullptr;
-    }
-
-    if (!pObj.IsEmpty()) {
-      if (pObjDef->m_pDestructor)
-        pObjDef->m_pDestructor(this, pObj);
-      FreeObjectPrivate(pObj);
-    }
-  }
-
-  ReleasePersistentContext();
-
-  if (GetIsolate() == g_isolate && --g_isolate_ref_count > 0)
-    return;
-
-  delete pData;
-  GetIsolate()->SetData(g_embedderDataSlot, nullptr);
-}
-
-int CFXJS_Engine::Execute(const WideString& script, FXJSErr* pError) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::TryCatch try_catch(GetIsolate());
-  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
-  v8::Local<v8::Script> compiled_script;
-  if (!v8::Script::Compile(context, NewString(script.AsStringView()))
-           .ToLocal(&compiled_script)) {
-    v8::String::Utf8Value error(GetIsolate(), try_catch.Exception());
-    // TODO(tsepez): return error via pError->message.
-    return -1;
-  }
-
-  v8::Local<v8::Value> result;
-  if (!compiled_script->Run(context).ToLocal(&result)) {
-    v8::String::Utf8Value error(GetIsolate(), try_catch.Exception());
-    // TODO(tsepez): return error via pError->message.
-    return -1;
-  }
-  return 0;
-}
-
-v8::Local<v8::Object> CFXJS_Engine::NewFxDynamicObj(int nObjDefnID,
-                                                    bool bStatic) {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
-  if (nObjDefnID == -1) {
-    v8::Local<v8::ObjectTemplate> objTempl =
-        v8::ObjectTemplate::New(GetIsolate());
-    v8::Local<v8::Object> obj;
-    if (!objTempl->NewInstance(context).ToLocal(&obj))
-      return v8::Local<v8::Object>();
-    return obj;
-  }
-
-  FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(GetIsolate());
-  if (!pData)
-    return v8::Local<v8::Object>();
-
-  if (nObjDefnID < 0 || nObjDefnID >= CFXJS_ObjDefinition::MaxID(GetIsolate()))
-    return v8::Local<v8::Object>();
-
-  CFXJS_ObjDefinition* pObjDef =
-      CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID);
-  v8::Local<v8::Object> obj;
-  if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj))
-    return v8::Local<v8::Object>();
-
-  CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID);
-  CFXJS_PerObjectData::SetInObject(pObjData, obj);
-  if (pObjDef->m_pConstructor)
-    pObjDef->m_pConstructor(this, obj);
-
-  if (!bStatic && FXJS_PerIsolateData::Get(GetIsolate())->m_pDynamicObjsMap)
-    FXJS_PerIsolateData::Get(GetIsolate())
-        ->m_pDynamicObjsMap->set(pObjData, obj);
-
-  return obj;
-}
-
-v8::Local<v8::Object> CFXJS_Engine::GetThisObj() {
-  v8::Isolate::Scope isolate_scope(GetIsolate());
-  if (!FXJS_PerIsolateData::Get(GetIsolate()))
-    return v8::Local<v8::Object>();
-
-  // Return the global object.
-  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
-  return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
-}
-
-void CFXJS_Engine::Error(const WideString& message) {
-  GetIsolate()->ThrowException(NewString(message.AsStringView()));
-}
-
-void CFXJS_Engine::SetObjectPrivate(v8::Local<v8::Object> pObj, void* p) {
-  CFXJS_PerObjectData* pPerObjectData =
-      CFXJS_PerObjectData::GetFromObject(pObj);
-  if (!pPerObjectData)
-    return;
-  pPerObjectData->m_pPrivate = p;
-}
-
-void* CFXJS_Engine::GetObjectPrivate(v8::Local<v8::Object> pObj) {
-  CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
-  if (!pData && !pObj.IsEmpty()) {
-    // It could be a global proxy object.
-    v8::Local<v8::Value> v = pObj->GetPrototype();
-    v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
-    if (v->IsObject()) {
-      pData = CFXJS_PerObjectData::GetFromObject(
-          v->ToObject(context).ToLocalChecked());
-    }
-  }
-  return pData ? pData->m_pPrivate : nullptr;
-}
diff --git a/fxjs/fxjs_v8.h b/fxjs/fxjs_v8.h
deleted file mode 100644
index 4575235..0000000
--- a/fxjs/fxjs_v8.h
+++ /dev/null
@@ -1,195 +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
-
-// FXJS_V8 is a layer that makes it easier to define native objects in V8, but
-// has no knowledge of PDF-specific native objects. It could in theory be used
-// to implement other sets of native objects.
-
-// PDFium code should include this file rather than including V8 headers
-// directly.
-
-#ifndef FXJS_FXJS_V8_H_
-#define FXJS_FXJS_V8_H_
-
-#include <v8-util.h>
-#include <v8.h>
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_string.h"
-#include "fxjs/cjs_v8.h"
-
-#ifdef PDF_ENABLE_XFA
-// Header for CFXJSE_RuntimeData. FXJS_V8 doesn't interpret this class,
-// it is just passed along to XFA.
-#include "fxjs/cfxjse_runtimedata.h"
-#endif  // PDF_ENABLE_XFA
-
-class CFXJS_Engine;
-class CFXJS_ObjDefinition;
-
-// FXJS_V8 places no restrictions on this class; it merely passes it
-// on to caller-provided methods.
-class IJS_EventContext;  // A description of the event that caused JS execution.
-
-enum FXJSOBJTYPE {
-  FXJSOBJTYPE_DYNAMIC = 0,  // Created by native method and returned to JS.
-  FXJSOBJTYPE_STATIC,       // Created by init and hung off of global object.
-  FXJSOBJTYPE_GLOBAL,       // The global object itself (may only appear once).
-};
-
-struct FXJSErr {
-  const wchar_t* message;
-  const wchar_t* srcline;
-  unsigned linnum;
-};
-
-// Global weak map to save dynamic objects.
-class V8TemplateMapTraits : public v8::StdMapTraits<void*, v8::Object> {
- public:
-  typedef v8::GlobalValueMap<void*, v8::Object, V8TemplateMapTraits> MapType;
-  typedef void WeakCallbackDataType;
-
-  static WeakCallbackDataType*
-  WeakCallbackParameter(MapType* map, void* key, v8::Local<v8::Object> value) {
-    return key;
-  }
-  static MapType* MapFromWeakCallbackInfo(
-      const v8::WeakCallbackInfo<WeakCallbackDataType>&);
-
-  static void* KeyFromWeakCallbackInfo(
-      const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
-    return data.GetParameter();
-  }
-  static const v8::PersistentContainerCallbackType kCallbackType =
-      v8::kWeakWithInternalFields;
-  static void DisposeWeak(
-      const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {}
-  static void OnWeakCallback(
-      const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {}
-  static void Dispose(v8::Isolate* isolate,
-                      v8::Global<v8::Object> value,
-                      void* key);
-  static void DisposeCallbackData(WeakCallbackDataType* callbackData) {}
-};
-
-class V8TemplateMap {
- public:
-  typedef v8::GlobalValueMap<void*, v8::Object, V8TemplateMapTraits> MapType;
-
-  explicit V8TemplateMap(v8::Isolate* isolate);
-  ~V8TemplateMap();
-
-  void set(void* key, v8::Local<v8::Object> handle);
-
-  friend class V8TemplateMapTraits;
-
- private:
-  MapType m_map;
-};
-
-class FXJS_PerIsolateData {
- public:
-  ~FXJS_PerIsolateData();
-
-  static void SetUp(v8::Isolate* pIsolate);
-  static FXJS_PerIsolateData* Get(v8::Isolate* pIsolate);
-
-  std::vector<std::unique_ptr<CFXJS_ObjDefinition>> m_ObjectDefnArray;
-#ifdef PDF_ENABLE_XFA
-  std::unique_ptr<CFXJSE_RuntimeData> m_pFXJSERuntimeData;
-#endif  // PDF_ENABLE_XFA
-  std::unique_ptr<V8TemplateMap> m_pDynamicObjsMap;
-
- protected:
-  explicit FXJS_PerIsolateData(v8::Isolate* pIsolate);
-};
-
-class FXJS_ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
-  static const size_t kMaxAllowedBytes = 0x10000000;
-  void* Allocate(size_t length) override;
-  void* AllocateUninitialized(size_t length) override;
-  void Free(void* data, size_t length) override;
-};
-
-void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate);
-void FXJS_Release();
-
-// Gets the global isolate set by FXJS_Initialize(), or makes a new one each
-// time if there is no such isolate. Returns true if a new isolate had to be
-// created.
-bool FXJS_GetIsolate(v8::Isolate** pResultIsolate);
-
-// Get the global isolate's ref count.
-size_t FXJS_GlobalIsolateRefCount();
-
-class CFXJS_Engine : public CJS_V8 {
- public:
-  explicit CFXJS_Engine(v8::Isolate* pIsolate);
-  ~CFXJS_Engine() override;
-
-  using Constructor = void (*)(CFXJS_Engine* pEngine,
-                               v8::Local<v8::Object> obj);
-  using Destructor = void (*)(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
-  static CFXJS_Engine* CurrentEngineFromIsolate(v8::Isolate* pIsolate);
-  static int GetObjDefnID(v8::Local<v8::Object> pObj);
-
-  // Always returns a valid, newly-created objDefnID.
-  int DefineObj(const char* sObjName,
-                FXJSOBJTYPE eObjType,
-                Constructor pConstructor,
-                Destructor pDestructor);
-
-  void DefineObjMethod(int nObjDefnID,
-                       const char* sMethodName,
-                       v8::FunctionCallback pMethodCall);
-  void DefineObjProperty(int nObjDefnID,
-                         const char* sPropName,
-                         v8::AccessorGetterCallback pPropGet,
-                         v8::AccessorSetterCallback pPropPut);
-  void DefineObjAllProperties(int nObjDefnID,
-                              v8::NamedPropertyQueryCallback pPropQurey,
-                              v8::NamedPropertyGetterCallback pPropGet,
-                              v8::NamedPropertySetterCallback pPropPut,
-                              v8::NamedPropertyDeleterCallback pPropDel);
-  void DefineObjConst(int nObjDefnID,
-                      const char* sConstName,
-                      v8::Local<v8::Value> pDefault);
-  void DefineGlobalMethod(const char* sMethodName,
-                          v8::FunctionCallback pMethodCall);
-  void DefineGlobalConst(const wchar_t* sConstName,
-                         v8::FunctionCallback pConstGetter);
-
-  // Called after FXJS_Define* calls made.
-  void InitializeEngine();
-  void ReleaseEngine();
-
-  // Called after FXJS_InitializeEngine call made.
-  int Execute(const WideString& script, FXJSErr* perror);
-
-  v8::Local<v8::Object> GetThisObj();
-
-  v8::Local<v8::Object> NewFxDynamicObj(int nObjDefnID, bool bStatic = false);
-
-  // Native object binding.
-  void SetObjectPrivate(v8::Local<v8::Object> pObj, void* p);
-  void* GetObjectPrivate(v8::Local<v8::Object> pObj);
-  static void FreeObjectPrivate(void* p);
-  static void FreeObjectPrivate(v8::Local<v8::Object> pObj);
-
-  void Error(const WideString& message);
-
- protected:
-  CFXJS_Engine();
-
- private:
-  std::vector<v8::Global<v8::Object>*> m_StaticObjects;
-};
-
-#endif  // FXJS_FXJS_V8_H_
diff --git a/fxjs/fxjs_v8_embeddertest.cpp b/fxjs/fxjs_v8_embeddertest.cpp
deleted file mode 100644
index d975264..0000000
--- a/fxjs/fxjs_v8_embeddertest.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/js_embedder_test.h"
-
-namespace {
-
-const double kExpected0 = 6.0;
-const double kExpected1 = 7.0;
-const double kExpected2 = 8.0;
-
-const wchar_t kScript0[] = L"fred = 6";
-const wchar_t kScript1[] = L"fred = 7";
-const wchar_t kScript2[] = L"fred = 8";
-
-}  // namespace
-
-class FXJSV8EmbedderTest : public JSEmbedderTest {
- public:
-  void ExecuteInCurrentContext(const WideString& script) {
-    FXJSErr error;
-    int sts = engine()->Execute(script, &error);
-    EXPECT_EQ(0, sts);
-  }
-  void CheckAssignmentInCurrentContext(double expected) {
-    v8::Local<v8::Object> This = engine()->GetThisObj();
-    v8::Local<v8::Value> fred = engine()->GetObjectProperty(This, L"fred");
-    EXPECT_TRUE(fred->IsNumber());
-    EXPECT_EQ(expected, engine()->ToDouble(fred));
-  }
-};
-
-TEST_F(FXJSV8EmbedderTest, Getters) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  ExecuteInCurrentContext(WideString(kScript1));
-  CheckAssignmentInCurrentContext(kExpected1);
-}
-
-TEST_F(FXJSV8EmbedderTest, MultipleEngines) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-
-  CFXJS_Engine engine1(isolate());
-  engine1.InitializeEngine();
-
-  CFXJS_Engine engine2(isolate());
-  engine2.InitializeEngine();
-
-  v8::Context::Scope context_scope(GetV8Context());
-  ExecuteInCurrentContext(WideString(kScript0));
-  CheckAssignmentInCurrentContext(kExpected0);
-
-  {
-    v8::Local<v8::Context> context1 = engine1.NewLocalContext();
-    v8::Context::Scope context_scope1(context1);
-    ExecuteInCurrentContext(WideString(kScript1));
-    CheckAssignmentInCurrentContext(kExpected1);
-  }
-
-  engine1.ReleaseEngine();
-
-  {
-    v8::Local<v8::Context> context2 = engine2.NewLocalContext();
-    v8::Context::Scope context_scope2(context2);
-    ExecuteInCurrentContext(WideString(kScript2));
-    CheckAssignmentInCurrentContext(kExpected2);
-  }
-
-  engine2.ReleaseEngine();
-  CheckAssignmentInCurrentContext(kExpected0);
-}
-
-TEST_F(FXJSV8EmbedderTest, EmptyLocal) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  v8::Local<v8::Value> empty;
-  EXPECT_FALSE(engine()->ToBoolean(empty));
-  EXPECT_EQ(0, engine()->ToInt32(empty));
-  EXPECT_EQ(0.0, engine()->ToDouble(empty));
-  EXPECT_EQ(L"", engine()->ToWideString(empty));
-  EXPECT_TRUE(engine()->ToObject(empty).IsEmpty());
-  EXPECT_TRUE(engine()->ToArray(empty).IsEmpty());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewNull) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto nullz = engine()->NewNull();
-  EXPECT_FALSE(engine()->ToBoolean(nullz));
-  EXPECT_EQ(0, engine()->ToInt32(nullz));
-  EXPECT_EQ(0.0, engine()->ToDouble(nullz));
-  EXPECT_EQ(L"null", engine()->ToWideString(nullz));
-  EXPECT_TRUE(engine()->ToObject(nullz).IsEmpty());
-  EXPECT_TRUE(engine()->ToArray(nullz).IsEmpty());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewUndefined) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto undef = engine()->NewUndefined();
-  EXPECT_FALSE(engine()->ToBoolean(undef));
-  EXPECT_EQ(0, engine()->ToInt32(undef));
-  EXPECT_TRUE(std::isnan(engine()->ToDouble(undef)));
-  EXPECT_EQ(L"undefined", engine()->ToWideString(undef));
-  EXPECT_TRUE(engine()->ToObject(undef).IsEmpty());
-  EXPECT_TRUE(engine()->ToArray(undef).IsEmpty());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewBoolean) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto boolz = engine()->NewBoolean(true);
-  EXPECT_TRUE(engine()->ToBoolean(boolz));
-  EXPECT_EQ(1, engine()->ToInt32(boolz));
-  EXPECT_EQ(1.0, engine()->ToDouble(boolz));
-  EXPECT_EQ(L"true", engine()->ToWideString(boolz));
-  EXPECT_TRUE(engine()->ToObject(boolz).IsEmpty());
-  EXPECT_TRUE(engine()->ToArray(boolz).IsEmpty());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewNumber) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto num = engine()->NewNumber(42.1);
-  EXPECT_TRUE(engine()->ToBoolean(num));
-  EXPECT_EQ(42, engine()->ToInt32(num));
-  EXPECT_EQ(42.1, engine()->ToDouble(num));
-  EXPECT_EQ(L"42.1", engine()->ToWideString(num));
-  EXPECT_TRUE(engine()->ToObject(num).IsEmpty());
-  EXPECT_TRUE(engine()->ToArray(num).IsEmpty());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewString) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto str = engine()->NewString(L"123");
-  EXPECT_TRUE(engine()->ToBoolean(str));
-  EXPECT_EQ(123, engine()->ToInt32(str));
-  EXPECT_EQ(123, engine()->ToDouble(str));
-  EXPECT_EQ(L"123", engine()->ToWideString(str));
-  EXPECT_TRUE(engine()->ToObject(str).IsEmpty());
-  EXPECT_TRUE(engine()->ToArray(str).IsEmpty());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewDate) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto date = engine()->NewDate(1111111111);
-  EXPECT_TRUE(engine()->ToBoolean(date));
-  EXPECT_EQ(1111111111, engine()->ToInt32(date));
-  EXPECT_EQ(1111111111.0, engine()->ToDouble(date));
-  EXPECT_NE(L"", engine()->ToWideString(date));  // exact format varies.
-  EXPECT_TRUE(engine()->ToObject(date)->IsObject());
-  EXPECT_TRUE(engine()->ToArray(date).IsEmpty());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewArray) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto array = engine()->NewArray();
-  EXPECT_EQ(0u, engine()->GetArrayLength(array));
-  EXPECT_FALSE(engine()->GetArrayElement(array, 2).IsEmpty());
-  EXPECT_TRUE(engine()->GetArrayElement(array, 2)->IsUndefined());
-  EXPECT_EQ(0u, engine()->GetArrayLength(array));
-
-  engine()->PutArrayElement(array, 3, engine()->NewNumber(12));
-  EXPECT_FALSE(engine()->GetArrayElement(array, 2).IsEmpty());
-  EXPECT_TRUE(engine()->GetArrayElement(array, 2)->IsUndefined());
-  EXPECT_FALSE(engine()->GetArrayElement(array, 3).IsEmpty());
-  EXPECT_TRUE(engine()->GetArrayElement(array, 3)->IsNumber());
-  EXPECT_EQ(4u, engine()->GetArrayLength(array));
-
-  EXPECT_TRUE(engine()->ToBoolean(array));
-  EXPECT_EQ(0, engine()->ToInt32(array));
-  double d = engine()->ToDouble(array);
-  EXPECT_NE(d, d);  // i.e. NaN.
-  EXPECT_EQ(L",,,12", engine()->ToWideString(array));
-  EXPECT_TRUE(engine()->ToObject(array)->IsObject());
-  EXPECT_TRUE(engine()->ToArray(array)->IsArray());
-}
-
-TEST_F(FXJSV8EmbedderTest, NewObject) {
-  v8::Isolate::Scope isolate_scope(isolate());
-  v8::HandleScope handle_scope(isolate());
-  v8::Context::Scope context_scope(GetV8Context());
-
-  auto object = engine()->NewFxDynamicObj(-1);
-  ASSERT_FALSE(object.IsEmpty());
-  EXPECT_EQ(0u, engine()->GetObjectPropertyNames(object).size());
-  EXPECT_FALSE(engine()->GetObjectProperty(object, L"clams").IsEmpty());
-  EXPECT_TRUE(engine()->GetObjectProperty(object, L"clams")->IsUndefined());
-  EXPECT_EQ(0u, engine()->GetObjectPropertyNames(object).size());
-
-  engine()->PutObjectProperty(object, L"clams", engine()->NewNumber(12));
-  EXPECT_FALSE(engine()->GetObjectProperty(object, L"clams").IsEmpty());
-  EXPECT_TRUE(engine()->GetObjectProperty(object, L"clams")->IsNumber());
-  EXPECT_EQ(1u, engine()->GetObjectPropertyNames(object).size());
-  EXPECT_EQ(L"clams", engine()->GetObjectPropertyNames(object)[0]);
-
-  EXPECT_TRUE(engine()->ToBoolean(object));
-  EXPECT_EQ(0, engine()->ToInt32(object));
-  double d = engine()->ToDouble(object);
-  EXPECT_NE(d, d);  // i.e. NaN.
-  EXPECT_EQ(L"[object Object]", engine()->ToWideString(object));
-  EXPECT_TRUE(engine()->ToObject(object)->IsObject());
-  EXPECT_TRUE(engine()->ToArray(object).IsEmpty());
-}
diff --git a/fxjs/fxjse.h b/fxjs/fxjse.h
deleted file mode 100644
index 873fcf5..0000000
--- a/fxjs/fxjse.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_FXJSE_H_
-#define FXJS_FXJSE_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "v8/include/v8.h"
-
-class CFXJSE_Arguments;
-class CFXJSE_Value;
-class CJS_Return;
-
-// C++ object which is retrieved from v8 object's slot.
-class CFXJSE_HostObject {
- public:
-  virtual ~CFXJSE_HostObject() {}
-
-  // Small layering violation here, but we need to distinguish between the
-  // two kinds of subclasses.
-  enum Type { kXFA, kFM2JS };
-  Type type() const { return type_; }
-
- protected:
-  explicit CFXJSE_HostObject(Type type) { type_ = type; }
-
-  Type type_;
-};
-
-typedef CJS_Return (*FXJSE_MethodCallback)(
-    const v8::FunctionCallbackInfo<v8::Value>& info,
-    const WideString& functionName);
-typedef void (*FXJSE_FuncCallback)(CFXJSE_Value* pThis,
-                                   const ByteStringView& szFuncName,
-                                   CFXJSE_Arguments& args);
-typedef void (*FXJSE_PropAccessor)(CFXJSE_Value* pObject,
-                                   const ByteStringView& szPropName,
-                                   CFXJSE_Value* pValue);
-typedef int32_t (*FXJSE_PropTypeGetter)(CFXJSE_Value* pObject,
-                                        const ByteStringView& szPropName,
-                                        bool bQueryIn);
-
-enum FXJSE_ClassPropTypes {
-  FXJSE_ClassPropType_None,
-  FXJSE_ClassPropType_Property,
-  FXJSE_ClassPropType_Method
-};
-
-struct FXJSE_FUNCTION_DESCRIPTOR {
-  const char* name;
-  FXJSE_FuncCallback callbackProc;
-};
-
-struct FXJSE_CLASS_DESCRIPTOR {
-  const char* name;
-  const FXJSE_FUNCTION_DESCRIPTOR* methods;
-  int32_t methNum;
-  FXJSE_PropTypeGetter dynPropTypeGetter;
-  FXJSE_PropAccessor dynPropGetter;
-  FXJSE_PropAccessor dynPropSetter;
-  FXJSE_MethodCallback dynMethodCall;
-};
-
-void FXJSE_ThrowMessage(const ByteStringView& utf8Message);
-
-#endif  // FXJS_FXJSE_H_
diff --git a/fxjs/global_timer.cpp b/fxjs/global_timer.cpp
index 6f7f09c..b837508 100644
--- a/fxjs/global_timer.cpp
+++ b/fxjs/global_timer.cpp
@@ -6,41 +6,52 @@
 
 #include "fxjs/global_timer.h"
 
-GlobalTimer::GlobalTimer(app* pObj,
-                         CPDFSDK_FormFillEnvironment* pFormFillEnv,
+#include <map>
+
+#include "core/fxcrt/timerhandler_iface.h"
+#include "fxjs/cjs_app.h"
+#include "third_party/base/no_destructor.h"
+
+namespace {
+
+using TimerMap = std::map<int32_t, GlobalTimer*>;
+TimerMap& GetGlobalTimerMap() {
+  static pdfium::base::NoDestructor<TimerMap> timer_map;
+  return *timer_map;
+}
+
+}  // namespace
+
+GlobalTimer::GlobalTimer(CJS_App* pObj,
                          CJS_Runtime* pRuntime,
-                         int nType,
+                         Type nType,
                          const WideString& script,
                          uint32_t dwElapse,
                          uint32_t dwTimeOut)
-    : m_nTimerID(0),
-      m_pEmbedObj(pObj),
-      m_bProcessing(false),
-      m_nType(nType),
+    : m_nType(nType),
+      m_nTimerID(pRuntime->GetTimerHandler()->SetTimer(dwElapse, Trigger)),
       m_dwTimeOut(dwTimeOut),
       m_swJScript(script),
       m_pRuntime(pRuntime),
-      m_pFormFillEnv(pFormFillEnv) {
-  CFX_SystemHandler* pHandler = m_pFormFillEnv->GetSysHandler();
-  m_nTimerID = pHandler->SetTimer(dwElapse, Trigger);
-  if (m_nTimerID)
-    (*GetGlobalTimerMap())[m_nTimerID] = this;
+      m_pEmbedApp(pObj) {
+  if (HasValidID())
+    GetGlobalTimerMap()[m_nTimerID] = this;
 }
 
 GlobalTimer::~GlobalTimer() {
-  if (!m_nTimerID)
+  if (!HasValidID())
     return;
 
-  if (GetRuntime())
-    m_pFormFillEnv->GetSysHandler()->KillTimer(m_nTimerID);
+  if (m_pRuntime && m_pRuntime->GetTimerHandler())
+    m_pRuntime->GetTimerHandler()->KillTimer(m_nTimerID);
 
-  GetGlobalTimerMap()->erase(m_nTimerID);
+  GetGlobalTimerMap().erase(m_nTimerID);
 }
 
 // static
-void GlobalTimer::Trigger(int nTimerID) {
-  auto it = GetGlobalTimerMap()->find(nTimerID);
-  if (it == GetGlobalTimerMap()->end())
+void GlobalTimer::Trigger(int32_t nTimerID) {
+  auto it = GetGlobalTimerMap().find(nTimerID);
+  if (it == GetGlobalTimerMap().end())
     return;
 
   GlobalTimer* pTimer = it->second;
@@ -48,33 +59,30 @@
     return;
 
   pTimer->m_bProcessing = true;
-  if (pTimer->m_pEmbedObj)
-    pTimer->m_pEmbedObj->TimerProc(pTimer);
+  if (pTimer->m_pEmbedApp)
+    pTimer->m_pEmbedApp->TimerProc(pTimer);
 
   // Timer proc may have destroyed timer, find it again.
-  it = GetGlobalTimerMap()->find(nTimerID);
-  if (it == GetGlobalTimerMap()->end())
+  it = GetGlobalTimerMap().find(nTimerID);
+  if (it == GetGlobalTimerMap().end())
     return;
 
   pTimer = it->second;
   pTimer->m_bProcessing = false;
   if (pTimer->IsOneShot())
-    pTimer->m_pEmbedObj->CancelProc(pTimer);
+    pTimer->m_pEmbedApp->CancelProc(pTimer);
 }
 
 // static
-void GlobalTimer::Cancel(int nTimerID) {
-  auto it = GetGlobalTimerMap()->find(nTimerID);
-  if (it == GetGlobalTimerMap()->end())
+void GlobalTimer::Cancel(int32_t nTimerID) {
+  auto it = GetGlobalTimerMap().find(nTimerID);
+  if (it == GetGlobalTimerMap().end())
     return;
 
   GlobalTimer* pTimer = it->second;
-  pTimer->m_pEmbedObj->CancelProc(pTimer);
+  pTimer->m_pEmbedApp->CancelProc(pTimer);
 }
 
-// static
-GlobalTimer::TimerMap* GlobalTimer::GetGlobalTimerMap() {
-  // Leak the timer array at shutdown.
-  static auto* s_TimerMap = new TimerMap;
-  return s_TimerMap;
+bool GlobalTimer::HasValidID() const {
+  return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
 }
diff --git a/fxjs/global_timer.h b/fxjs/global_timer.h
index ec8806b..ef6bbb6 100644
--- a/fxjs/global_timer.h
+++ b/fxjs/global_timer.h
@@ -7,44 +7,44 @@
 #ifndef FXJS_GLOBAL_TIMER_H_
 #define FXJS_GLOBAL_TIMER_H_
 
-#include <map>
+#include "fxjs/cjs_runtime.h"
 
-#include "fxjs/cjs_app.h"
+class CJS_App;
 
 class GlobalTimer {
  public:
-  GlobalTimer(app* pObj,
-              CPDFSDK_FormFillEnvironment* pFormFillEnv,
+  enum class Type : bool {
+    kRepeating = false,
+    kOneShot = true,
+  };
+
+  GlobalTimer(CJS_App* pObj,
               CJS_Runtime* pRuntime,
-              int nType,
+              Type nType,
               const WideString& script,
               uint32_t dwElapse,
               uint32_t dwTimeOut);
   ~GlobalTimer();
 
-  static void Trigger(int nTimerID);
-  static void Cancel(int nTimerID);
+  static void Trigger(int32_t nTimerID);
+  static void Cancel(int32_t nTimerID);
 
-  bool IsOneShot() const { return m_nType == 1; }
+  bool IsOneShot() const { return m_nType == Type::kOneShot; }
   uint32_t GetTimeOut() const { return m_dwTimeOut; }
-  int GetTimerID() const { return m_nTimerID; }
+  int32_t GetTimerID() const { return m_nTimerID; }
   CJS_Runtime* GetRuntime() const { return m_pRuntime.Get(); }
   WideString GetJScript() const { return m_swJScript; }
 
  private:
-  using TimerMap = std::map<uint32_t, GlobalTimer*>;
-  static TimerMap* GetGlobalTimerMap();
+  bool HasValidID() const;
 
-  uint32_t m_nTimerID;
-  app* const m_pEmbedObj;
-  bool m_bProcessing;
-
-  // data
-  const int m_nType;  // 0:Interval; 1:TimeOut
+  const Type m_nType;
+  bool m_bProcessing = false;
+  const int32_t m_nTimerID;
   const uint32_t m_dwTimeOut;
   const WideString m_swJScript;
-  CJS_Runtime::ObservedPtr m_pRuntime;
-  CPDFSDK_FormFillEnvironment::ObservedPtr m_pFormFillEnv;
+  ObservedPtr<CJS_Runtime> m_pRuntime;
+  CJS_App* const m_pEmbedApp;
 };
 
 #endif  // FXJS_GLOBAL_TIMER_H_
diff --git a/fxjs/ijs_event_context.h b/fxjs/ijs_event_context.h
index 9b8dd8e..e2c6407 100644
--- a/fxjs/ijs_event_context.h
+++ b/fxjs/ijs_event_context.h
@@ -9,6 +9,8 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "fxjs/ijs_runtime.h"
+#include "third_party/base/optional.h"
 
 class CPDF_Bookmark;
 class CPDF_FormField;
@@ -20,7 +22,10 @@
 // may trigger new events on top of one another.
 class IJS_EventContext {
  public:
-  virtual bool RunScript(const WideString& script, WideString* info) = 0;
+  virtual ~IJS_EventContext() = default;
+
+  virtual Optional<IJS_Runtime::JS_Error> RunScript(
+      const WideString& script) = 0;
 
   virtual void OnApp_Init() = 0;
 
@@ -52,39 +57,36 @@
   virtual void OnField_Focus(bool bModifier,
                              bool bShift,
                              CPDF_FormField* pTarget,
-                             const WideString& Value) = 0;
+                             WideString* Value) = 0;
   virtual void OnField_Blur(bool bModifier,
                             bool bShift,
                             CPDF_FormField* pTarget,
-                            const WideString& Value) = 0;
-
+                            WideString* Value) = 0;
   virtual void OnField_Calculate(CPDF_FormField* pSource,
                                  CPDF_FormField* pTarget,
-                                 WideString& Value,
-                                 bool& bRc) = 0;
-  virtual void OnField_Format(CPDF_FormField* pTarget,
-                              WideString& Value,
-                              bool bWillCommit) = 0;
-  virtual void OnField_Keystroke(WideString& strChange,
+                                 WideString* Value,
+                                 bool* bRc) = 0;
+  virtual void OnField_Format(CPDF_FormField* pTarget, WideString* Value) = 0;
+  virtual void OnField_Keystroke(WideString* strChange,
                                  const WideString& strChangeEx,
                                  bool KeyDown,
                                  bool bModifier,
-                                 int& nSelEnd,
-                                 int& nSelStart,
+                                 int* nSelEnd,
+                                 int* nSelStart,
                                  bool bShift,
                                  CPDF_FormField* pTarget,
-                                 WideString& Value,
+                                 WideString* Value,
                                  bool bWillCommit,
                                  bool bFieldFull,
-                                 bool& bRc) = 0;
-  virtual void OnField_Validate(WideString& strChange,
+                                 bool* bRc) = 0;
+  virtual void OnField_Validate(WideString* strChange,
                                 const WideString& strChangeEx,
                                 bool bKeyDown,
                                 bool bModifier,
                                 bool bShift,
                                 CPDF_FormField* pTarget,
-                                WideString& Value,
-                                bool& bRc) = 0;
+                                WideString* Value,
+                                bool* bRc) = 0;
 
   virtual void OnScreen_Focus(bool bModifier,
                               bool bShift,
@@ -125,9 +127,6 @@
   virtual void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
   virtual void OnConsole_Exec() = 0;
   virtual void OnExternal_Exec() = 0;
-
- protected:
-  virtual ~IJS_EventContext() {}
 };
 
 #endif  // FXJS_IJS_EVENT_CONTEXT_H_
diff --git a/fxjs/ijs_runtime.cpp b/fxjs/ijs_runtime.cpp
new file mode 100644
index 0000000..34a846e
--- /dev/null
+++ b/fxjs/ijs_runtime.cpp
@@ -0,0 +1,51 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/ijs_runtime.h"
+
+#include "fxjs/cjs_runtimestub.h"
+#include "third_party/base/ptr_util.h"
+
+#ifdef PDF_ENABLE_V8
+#include "fxjs/cfxjs_engine.h"
+#include "fxjs/cjs_runtime.h"
+#endif
+
+IJS_Runtime::ScopedEventContext::ScopedEventContext(IJS_Runtime* pRuntime)
+    : m_pRuntime(pRuntime), m_pContext(pRuntime->NewEventContext()) {}
+
+IJS_Runtime::ScopedEventContext::~ScopedEventContext() {
+  m_pRuntime->ReleaseEventContext(m_pContext.Release());
+}
+
+// static
+void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {
+#ifdef PDF_ENABLE_V8
+  FXJS_Initialize(slot, static_cast<v8::Isolate*>(isolate));
+#endif
+}
+
+// static
+void IJS_Runtime::Destroy() {
+#ifdef PDF_ENABLE_V8
+  FXJS_Release();
+#endif
+}
+
+// static
+std::unique_ptr<IJS_Runtime> IJS_Runtime::Create(
+    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
+#ifdef PDF_ENABLE_V8
+  if (pFormFillEnv->IsJSPlatformPresent())
+    return pdfium::MakeUnique<CJS_Runtime>(pFormFillEnv);
+#endif
+  return pdfium::MakeUnique<CJS_RuntimeStub>(pFormFillEnv);
+}
+
+IJS_Runtime::~IJS_Runtime() = default;
+
+IJS_Runtime::JS_Error::JS_Error(int line,
+                                int column,
+                                const WideString& exception)
+    : line(line), column(column), exception(exception) {}
diff --git a/fxjs/ijs_runtime.h b/fxjs/ijs_runtime.h
index e649aad..ca103aa 100644
--- a/fxjs/ijs_runtime.h
+++ b/fxjs/ijs_runtime.h
@@ -7,38 +7,59 @@
 #ifndef FXJS_IJS_RUNTIME_H_
 #define FXJS_IJS_RUNTIME_H_
 
+#include <memory>
+
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
 
-#ifdef PDF_ENABLE_XFA
-#include "fxjs/fxjse.h"
-#endif  // PDF_ENABLE_XFA
-
+class CFXJSE_Value;
+class CJS_Runtime;
 class CPDFSDK_FormFillEnvironment;
 class IJS_EventContext;
 
-// Owns the FJXS objects needed to actually execute JS.
+// Owns the FJXS objects needed to actually execute JS, if possible. This
+// virtual interface is backed by either an actual JS runtime, or a stub,
+// when JS is not present.
 class IJS_Runtime {
  public:
+  struct JS_Error {
+    int line;
+    int column;
+    WideString exception;
+
+    JS_Error(int line, int column, const WideString& exception);
+  };
+
+  class ScopedEventContext {
+   public:
+    explicit ScopedEventContext(IJS_Runtime* pRuntime);
+    ~ScopedEventContext();
+
+    IJS_EventContext* Get() const { return m_pContext.Get(); }
+    IJS_EventContext* operator->() const { return m_pContext.Get(); }
+
+   private:
+    UnownedPtr<IJS_Runtime> const m_pRuntime;
+    UnownedPtr<IJS_EventContext> m_pContext;
+  };
+
   static void Initialize(unsigned int slot, void* isolate);
   static void Destroy();
-  static IJS_Runtime* Create(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  virtual ~IJS_Runtime() {}
+  static std::unique_ptr<IJS_Runtime> Create(
+      CPDFSDK_FormFillEnvironment* pFormFillEnv);
 
+  virtual ~IJS_Runtime();
+
+  virtual CJS_Runtime* AsCJSRuntime() = 0;
   virtual IJS_EventContext* NewEventContext() = 0;
   virtual void ReleaseEventContext(IJS_EventContext* pContext) = 0;
   virtual CPDFSDK_FormFillEnvironment* GetFormFillEnv() const = 0;
-  virtual int ExecuteScript(const WideString& script, WideString* info) = 0;
-
-#ifdef PDF_ENABLE_XFA
-  virtual bool GetValueByName(const ByteStringView& utf8Name,
-                              CFXJSE_Value* pValue) = 0;
-  virtual bool SetValueByName(const ByteStringView& utf8Name,
-                              CFXJSE_Value* pValue) = 0;
-#endif  // PDF_ENABLE_XFA
+  virtual Optional<JS_Error> ExecuteScript(const WideString& script) = 0;
 
  protected:
-  IJS_Runtime() {}
+  IJS_Runtime() = default;
 };
 
 #endif  // FXJS_IJS_RUNTIME_H_
diff --git a/fxjs/js_define.cpp b/fxjs/js_define.cpp
new file mode 100644
index 0000000..0c0c02c
--- /dev/null
+++ b/fxjs/js_define.cpp
@@ -0,0 +1,101 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/js_define.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <vector>
+
+#include "core/fxcrt/fx_extension.h"
+#include "fxjs/cjs_document.h"
+#include "fxjs/cjs_object.h"
+#include "fxjs/fx_date_helpers.h"
+
+void JSDestructor(v8::Local<v8::Object> obj) {
+  CFXJS_Engine::SetObjectPrivate(obj, nullptr);
+}
+
+double JS_DateParse(const WideString& str) {
+  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
+  v8::Isolate::Scope isolate_scope(pIsolate);
+  v8::HandleScope scope(pIsolate);
+
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+
+  // Use the built-in object method.
+  v8::Local<v8::Value> v =
+      context->Global()
+          ->Get(context, v8::String::NewFromUtf8(pIsolate, "Date",
+                                                 v8::NewStringType::kNormal)
+                             .ToLocalChecked())
+          .ToLocalChecked();
+  if (v->IsObject()) {
+    v8::Local<v8::Object> o = v->ToObject(context).ToLocalChecked();
+    v = o->Get(context, v8::String::NewFromUtf8(pIsolate, "parse",
+                                                v8::NewStringType::kNormal)
+                            .ToLocalChecked())
+            .ToLocalChecked();
+    if (v->IsFunction()) {
+      v8::Local<v8::Function> funC = v8::Local<v8::Function>::Cast(v);
+      const int argc = 1;
+      v8::Local<v8::Value> timeStr =
+          v8::String::NewFromUtf8(pIsolate,
+                                  FX_UTF8Encode(str.AsStringView()).c_str(),
+                                  v8::NewStringType::kNormal)
+              .ToLocalChecked();
+      v8::Local<v8::Value> argv[argc] = {timeStr};
+      v = funC->Call(context, context->Global(), argc, argv).ToLocalChecked();
+      if (v->IsNumber()) {
+        double date = v->ToNumber(context).ToLocalChecked()->Value();
+        if (!std::isfinite(date))
+          return date;
+        return FX_LocalTime(date);
+      }
+    }
+  }
+  return 0;
+}
+
+std::vector<v8::Local<v8::Value>> ExpandKeywordParams(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& originals,
+    size_t nKeywords,
+    ...) {
+  ASSERT(nKeywords);
+
+  std::vector<v8::Local<v8::Value>> result(nKeywords, v8::Local<v8::Value>());
+  size_t size = std::min(originals.size(), nKeywords);
+  for (size_t i = 0; i < size; ++i)
+    result[i] = originals[i];
+
+  if (originals.size() != 1 || !originals[0]->IsObject() ||
+      originals[0]->IsArray()) {
+    return result;
+  }
+  result[0] = v8::Local<v8::Value>();  // Make unknown.
+
+  v8::Local<v8::Object> pObj = pRuntime->ToObject(originals[0]);
+  va_list ap;
+  va_start(ap, nKeywords);
+  for (size_t i = 0; i < nKeywords; ++i) {
+    const char* property = va_arg(ap, const char*);
+    v8::Local<v8::Value> v8Value = pRuntime->GetObjectProperty(pObj, property);
+    if (!v8Value->IsUndefined())
+      result[i] = v8Value;
+  }
+  va_end(ap);
+
+  return result;
+}
+
+bool IsExpandedParamKnown(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() &&
+         (value->IsString() || value->IsNumber() || value->IsBoolean() ||
+          value->IsDate() || value->IsObject() || value->IsNull() ||
+          value->IsUndefined());
+}
diff --git a/fxjs/js_define.h b/fxjs/js_define.h
new file mode 100644
index 0000000..2c15c9f
--- /dev/null
+++ b/fxjs/js_define.h
@@ -0,0 +1,160 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_JS_DEFINE_H_
+#define FXJS_JS_DEFINE_H_
+
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/cfxjs_engine.h"
+#include "fxjs/cjs_result.h"
+#include "fxjs/cjs_runtime.h"
+#include "fxjs/js_resources.h"
+#include "third_party/base/ptr_util.h"
+
+class CJS_Object;
+
+double JS_DateParse(const WideString& str);
+
+// Some JS methods have the bizarre convention that they may also be called
+// with a single argument which is an object containing the actual arguments
+// as its properties. The varying arguments to this method are the property
+// names as wchar_t string literals corresponding to each positional argument.
+// The result will always contain |nKeywords| value, check for the unspecified
+// ones in the result using IsExpandedParamKnown() below.
+std::vector<v8::Local<v8::Value>> ExpandKeywordParams(
+    CJS_Runtime* pRuntime,
+    const std::vector<v8::Local<v8::Value>>& originals,
+    size_t nKeywords,
+    ...);
+
+bool IsExpandedParamKnown(v8::Local<v8::Value> value);
+
+// All JS classes have a name, an object defintion ID, and the ability to
+// register themselves with FXJS_V8. We never make a BASE class on its own
+// because it can't really do anything.
+
+// Rich JS classes provide constants, methods, properties, and the ability
+// to construct native object state.
+
+template <class T>
+static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+  pEngine->SetObjectPrivate(
+      obj, pdfium::MakeUnique<T>(obj, static_cast<CJS_Runtime*>(pEngine)));
+}
+
+// CJS_Object has virtual dtor, template not required.
+void JSDestructor(v8::Local<v8::Object> obj);
+
+template <class C>
+UnownedPtr<C> JSGetObject(v8::Local<v8::Object> obj) {
+  if (CFXJS_Engine::GetObjDefnID(obj) != C::GetObjDefnID())
+    return nullptr;
+
+  CJS_Object* pJSObj = CFXJS_Engine::GetObjectPrivate(obj);
+  if (!pJSObj)
+    return nullptr;
+
+  return UnownedPtr<C>(static_cast<C*>(pJSObj));
+}
+
+template <class C, CJS_Result (C::*M)(CJS_Runtime*)>
+void JSPropGetter(const char* prop_name_string,
+                  const char* class_name_string,
+                  v8::Local<v8::String> property,
+                  const v8::PropertyCallbackInfo<v8::Value>& info) {
+  auto pObj = JSGetObject<C>(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  CJS_Result result = (pObj.Get()->*M)(pRuntime);
+  if (result.HasError()) {
+    pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
+                                        result.Error()));
+    return;
+  }
+
+  if (result.HasReturn())
+    info.GetReturnValue().Set(result.Return());
+}
+
+template <class C, CJS_Result (C::*M)(CJS_Runtime*, v8::Local<v8::Value>)>
+void JSPropSetter(const char* prop_name_string,
+                  const char* class_name_string,
+                  v8::Local<v8::String> property,
+                  v8::Local<v8::Value> value,
+                  const v8::PropertyCallbackInfo<void>& info) {
+  auto pObj = JSGetObject<C>(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  CJS_Result result = (pObj.Get()->*M)(pRuntime, value);
+  if (result.HasError()) {
+    pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
+                                        result.Error()));
+  }
+}
+
+template <class C,
+          CJS_Result (C::*M)(CJS_Runtime*,
+                             const std::vector<v8::Local<v8::Value>>&)>
+void JSMethod(const char* method_name_string,
+              const char* class_name_string,
+              const v8::FunctionCallbackInfo<v8::Value>& info) {
+  auto pObj = JSGetObject<C>(info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  std::vector<v8::Local<v8::Value>> parameters;
+  for (unsigned int i = 0; i < (unsigned int)info.Length(); i++)
+    parameters.push_back(info[i]);
+
+  CJS_Result result = (pObj.Get()->*M)(pRuntime, parameters);
+  if (result.HasError()) {
+    pRuntime->Error(JSFormatErrorString(class_name_string, method_name_string,
+                                        result.Error()));
+    return;
+  }
+
+  if (result.HasReturn())
+    info.GetReturnValue().Set(result.Return());
+}
+
+#define JS_STATIC_PROP(err_name, prop_name, class_name)           \
+  static void get_##prop_name##_static(                           \
+      v8::Local<v8::String> property,                             \
+      const v8::PropertyCallbackInfo<v8::Value>& info) {          \
+    JSPropGetter<class_name, &class_name::get_##prop_name>(       \
+        #err_name, class_name::kName, property, info);            \
+  }                                                               \
+  static void set_##prop_name##_static(                           \
+      v8::Local<v8::String> property, v8::Local<v8::Value> value, \
+      const v8::PropertyCallbackInfo<void>& info) {               \
+    JSPropSetter<class_name, &class_name::set_##prop_name>(       \
+        #err_name, class_name::kName, property, value, info);     \
+  }
+
+#define JS_STATIC_METHOD(method_name, class_name)                            \
+  static void method_name##_static(                                          \
+      const v8::FunctionCallbackInfo<v8::Value>& info) {                     \
+    JSMethod<class_name, &class_name::method_name>(#method_name,             \
+                                                   class_name::kName, info); \
+  }
+
+#endif  // FXJS_JS_DEFINE_H_
diff --git a/fxjs/js_resources.cpp b/fxjs/js_resources.cpp
index bfffbc3..3bc8df3 100644
--- a/fxjs/js_resources.cpp
+++ b/fxjs/js_resources.cpp
@@ -7,62 +7,100 @@
 #include "fxjs/js_resources.h"
 
 WideString JSGetStringFromID(JSMessage msg) {
+  const char* msg_string = "";
   switch (msg) {
     case JSMessage::kAlert:
-      return L"Alert";
+      msg_string = "Alert";
+      break;
     case JSMessage::kParamError:
-      return L"Incorrect number of parameters passed to function.";
+      msg_string = "Incorrect number of parameters passed to function.";
+      break;
     case JSMessage::kInvalidInputError:
-      return L"The input value is invalid.";
+      msg_string = "The input value is invalid.";
+      break;
     case JSMessage::kParamTooLongError:
-      return L"The input value is too long.";
+      msg_string = "The input value is too long.";
+      break;
     case JSMessage::kParseDateError:
-      return L"The input value can't be parsed as a valid date/time (%s).";
+      msg_string =
+          "The input value can't be parsed as a valid date/time (%ls).";
+      break;
     case JSMessage::kRangeBetweenError:
-      return L"The input value must be greater than or equal to %s"
-             L" and less than or equal to %s.";
+      msg_string =
+          "The input value must be greater than or equal to %ls"
+          " and less than or equal to %ls.";
+      break;
     case JSMessage::kRangeGreaterError:
-      return L"The input value must be greater than or equal to %s.";
+      msg_string = "The input value must be greater than or equal to %ls.";
+      break;
     case JSMessage::kRangeLessError:
-      return L"The input value must be less than or equal to %s.";
+      msg_string = "The input value must be less than or equal to %ls.";
+      break;
     case JSMessage::kNotSupportedError:
-      return L"Operation not supported.";
+      msg_string = "Operation not supported.";
+      break;
     case JSMessage::kBusyError:
-      return L"System is busy.";
+      msg_string = "System is busy.";
+      break;
     case JSMessage::kDuplicateEventError:
-      return L"Duplicate formfield event found.";
-    case JSMessage::kRunSuccess:
-      return L"Script ran successfully.";
+      msg_string = "Duplicate formfield event found.";
+      break;
     case JSMessage::kSecondParamNotDateError:
-      return L"The second parameter can't be converted to a Date.";
+      msg_string = "The second parameter can't be converted to a Date.";
+      break;
     case JSMessage::kSecondParamInvalidDateError:
-      return L"The second parameter is an invalid Date!";
+      msg_string = "The second parameter is an invalid Date.";
+      break;
     case JSMessage::kGlobalNotFoundError:
-      return L"Global value not found.";
+      msg_string = "Global value not found.";
+      break;
     case JSMessage::kReadOnlyError:
-      return L"Cannot assign to readonly property.";
+      msg_string = "Cannot assign to readonly property.";
+      break;
     case JSMessage::kTypeError:
-      return L"Incorrect parameter type.";
+      msg_string = "Incorrect parameter type.";
+      break;
     case JSMessage::kValueError:
-      return L"Incorrect parameter value.";
+      msg_string = "Incorrect parameter value.";
+      break;
     case JSMessage::kPermissionError:
-      return L"Permission denied.";
+      msg_string = "Permission denied.";
+      break;
     case JSMessage::kBadObjectError:
-      return L"Object no longer exists.";
+      msg_string = "Object no longer exists.";
+      break;
+    case JSMessage::kObjectTypeError:
+      msg_string = "Object is of the wrong type.";
+      break;
+    case JSMessage::kUnknownProperty:
+      msg_string = "Unknown property.";
+      break;
+    case JSMessage::kInvalidSetError:
+      msg_string = "Set not possible, invalid or unknown.";
+      break;
+    case JSMessage::kUserGestureRequiredError:
+      msg_string = "User gesture required.";
+      break;
     case JSMessage::kTooManyOccurances:
-      return L"Too many occurances";
+      msg_string = "Too many occurances.";
+      break;
+    case JSMessage::kUnknownMethod:
+      msg_string = "Unknown method.";
+      break;
+    default:
+      NOTREACHED();
+      break;
   }
-  NOTREACHED();
-  return L"";
+  return WideString::FromASCII(msg_string);
 }
 
 WideString JSFormatErrorString(const char* class_name,
                                const char* property_name,
                                const WideString& details) {
-  WideString result = WideString::FromLocal(class_name);
+  WideString result = WideString::FromDefANSI(class_name);
   if (property_name) {
     result += L".";
-    result += WideString::FromLocal(property_name);
+    result += WideString::FromDefANSI(property_name);
   }
   result += L": ";
   result += details;
diff --git a/fxjs/js_resources.h b/fxjs/js_resources.h
index 0b664c9..8a30862 100644
--- a/fxjs/js_resources.h
+++ b/fxjs/js_resources.h
@@ -10,8 +10,7 @@
 #include "core/fxcrt/widestring.h"
 
 enum class JSMessage {
-  kRunSuccess = 0,
-  kAlert,
+  kAlert = 1,
   kParamError,
   kInvalidInputError,
   kParamTooLongError,
@@ -30,7 +29,12 @@
   kValueError,
   kPermissionError,
   kBadObjectError,
-  kTooManyOccurances
+  kObjectTypeError,
+  kUnknownProperty,
+  kInvalidSetError,
+  kUserGestureRequiredError,
+  kTooManyOccurances,
+  kUnknownMethod,
 };
 
 WideString JSGetStringFromID(JSMessage msg);
diff --git a/fxjs/xfa/DEPS b/fxjs/xfa/DEPS
new file mode 100644
index 0000000..5f8b44a
--- /dev/null
+++ b/fxjs/xfa/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  '+xfa/fgas',
+  '+xfa/fxfa'
+]
diff --git a/fxjs/xfa/cfxjse_app_embeddertest.cpp b/fxjs/xfa/cfxjse_app_embeddertest.cpp
new file mode 100644
index 0000000..628f56e
--- /dev/null
+++ b/fxjs/xfa/cfxjse_app_embeddertest.cpp
@@ -0,0 +1,15 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+
+class CFXJSE_AppEmbedderTest : public XFAJSEmbedderTest {};
+
+// Should not crash.
+TEST_F(CFXJSE_AppEmbedderTest, BUG_1252) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  EXPECT_FALSE(Execute("app.activeDocs()"));
+}
diff --git a/fxjs/xfa/cfxjse_arguments.cpp b/fxjs/xfa/cfxjse_arguments.cpp
new file mode 100644
index 0000000..8bfc8e3
--- /dev/null
+++ b/fxjs/xfa/cfxjse_arguments.cpp
@@ -0,0 +1,59 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_arguments.h"
+
+#include "fxjs/xfa/cfxjse_context.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/ptr_util.h"
+
+CFXJSE_Arguments::CFXJSE_Arguments(
+    const v8::FunctionCallbackInfo<v8::Value>* pInfo,
+    CFXJSE_Value* pRetValue)
+    : m_pInfo(pInfo), m_pRetValue(pRetValue) {}
+
+CFXJSE_Arguments::~CFXJSE_Arguments() {}
+
+int32_t CFXJSE_Arguments::GetLength() const {
+  return m_pInfo->Length();
+}
+
+std::unique_ptr<CFXJSE_Value> CFXJSE_Arguments::GetValue(int32_t index) const {
+  auto pArgValue = pdfium::MakeUnique<CFXJSE_Value>(v8::Isolate::GetCurrent());
+  pArgValue->ForceSetValue((*m_pInfo)[index]);
+  return pArgValue;
+}
+
+bool CFXJSE_Arguments::GetBoolean(int32_t index) const {
+  return (*m_pInfo)[index]->BooleanValue(m_pInfo->GetIsolate());
+}
+
+int32_t CFXJSE_Arguments::GetInt32(int32_t index) const {
+  return static_cast<int32_t>(
+      (*m_pInfo)[index]
+          ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext())
+          .FromMaybe(0.0));
+}
+
+float CFXJSE_Arguments::GetFloat(int32_t index) const {
+  return static_cast<float>(
+      (*m_pInfo)[index]
+          ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext())
+          .FromMaybe(0.0));
+}
+
+ByteString CFXJSE_Arguments::GetUTF8String(int32_t index) const {
+  v8::Isolate* isolate = m_pInfo->GetIsolate();
+  v8::Local<v8::Value> info = (*m_pInfo)[index];
+  v8::Local<v8::String> hString =
+      info->ToString(isolate->GetCurrentContext()).ToLocalChecked();
+  v8::String::Utf8Value szStringVal(isolate, hString);
+  return ByteString(*szStringVal);
+}
+
+CFXJSE_Value* CFXJSE_Arguments::GetReturnValue() const {
+  return m_pRetValue.Get();
+}
diff --git a/fxjs/xfa/cfxjse_arguments.h b/fxjs/xfa/cfxjse_arguments.h
new file mode 100644
index 0000000..e048bc2
--- /dev/null
+++ b/fxjs/xfa/cfxjse_arguments.h
@@ -0,0 +1,37 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_ARGUMENTS_H_
+#define FXJS_XFA_CFXJSE_ARGUMENTS_H_
+
+#include <memory>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/v8.h"
+
+class CFXJSE_Value;
+
+class CFXJSE_Arguments {
+ public:
+  CFXJSE_Arguments(const v8::FunctionCallbackInfo<v8::Value>* pInfo,
+                   CFXJSE_Value* pRetValue);
+  ~CFXJSE_Arguments();
+
+  int32_t GetLength() const;
+  std::unique_ptr<CFXJSE_Value> GetValue(int32_t index) const;
+  bool GetBoolean(int32_t index) const;
+  int32_t GetInt32(int32_t index) const;
+  float GetFloat(int32_t index) const;
+  ByteString GetUTF8String(int32_t index) const;
+  CFXJSE_Value* GetReturnValue() const;
+
+ private:
+  UnownedPtr<const v8::FunctionCallbackInfo<v8::Value>> const m_pInfo;
+  UnownedPtr<CFXJSE_Value> const m_pRetValue;
+};
+
+#endif  // FXJS_XFA_CFXJSE_ARGUMENTS_H_
diff --git a/fxjs/xfa/cfxjse_class.cpp b/fxjs/xfa/cfxjse_class.cpp
new file mode 100644
index 0000000..f6caf07
--- /dev/null
+++ b/fxjs/xfa/cfxjse_class.cpp
@@ -0,0 +1,343 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_class.h"
+
+#include <memory>
+#include <utility>
+
+#include "fxjs/cjs_result.h"
+#include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_arguments.h"
+#include "fxjs/xfa/cfxjse_context.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/ptr_util.h"
+
+using pdfium::fxjse::kClassTag;
+using pdfium::fxjse::kFuncTag;
+
+namespace {
+
+FXJSE_FUNCTION_DESCRIPTOR* AsFunctionDescriptor(void* ptr) {
+  auto* result = static_cast<FXJSE_FUNCTION_DESCRIPTOR*>(ptr);
+  return result && result->tag == kFuncTag ? result : nullptr;
+}
+
+FXJSE_CLASS_DESCRIPTOR* AsClassDescriptor(void* ptr) {
+  auto* result = static_cast<FXJSE_CLASS_DESCRIPTOR*>(ptr);
+  return result && result->tag == kClassTag ? result : nullptr;
+}
+
+void V8FunctionCallback_Wrapper(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
+      AsFunctionDescriptor(info.Data().As<v8::External>()->Value());
+  if (!lpFunctionInfo)
+    return;
+
+  ByteStringView szFunctionName(lpFunctionInfo->name);
+  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
+  lpThisValue->ForceSetValue(info.Holder());
+  auto lpRetValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
+  CFXJSE_Arguments impl(&info, lpRetValue.get());
+  lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl);
+  if (!lpRetValue->DirectGetValue().IsEmpty())
+    info.GetReturnValue().Set(lpRetValue->DirectGetValue());
+}
+
+void V8ConstructorCallback_Wrapper(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (!info.IsConstructCall())
+    return;
+
+  const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
+      AsClassDescriptor(info.Data().As<v8::External>()->Value());
+  if (!lpClassDefinition)
+    return;
+
+  ASSERT(info.Holder()->InternalFieldCount() == 2);
+  info.Holder()->SetAlignedPointerInInternalField(0, nullptr);
+  info.Holder()->SetAlignedPointerInInternalField(1, nullptr);
+}
+
+void Context_GlobalObjToString(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+      AsClassDescriptor(info.Data().As<v8::External>()->Value());
+  if (!lpClass)
+    return;
+
+  if (info.This() == info.Holder() && lpClass->name) {
+    ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name);
+    info.GetReturnValue().Set(
+        v8::String::NewFromUtf8(info.GetIsolate(), szStringVal.c_str(),
+                                v8::NewStringType::kNormal,
+                                szStringVal.GetLength())
+            .ToLocalChecked());
+    return;
+  }
+  v8::Local<v8::String> local_str =
+      info.Holder()
+          ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext())
+          .FromMaybe(v8::Local<v8::String>());
+  info.GetReturnValue().Set(local_str);
+}
+
+void DynPropGetterAdapter_MethodCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> hCallBackInfo = info.Data().As<v8::Object>();
+  if (hCallBackInfo->InternalFieldCount() != 2)
+    return;
+
+  auto* pClassDescriptor = static_cast<const FXJSE_CLASS_DESCRIPTOR*>(
+      hCallBackInfo->GetAlignedPointerFromInternalField(0));
+  if (pClassDescriptor != &GlobalClassDescriptor &&
+      pClassDescriptor != &NormalClassDescriptor &&
+      pClassDescriptor != &VariablesClassDescriptor &&
+      pClassDescriptor != &kFormCalcFM2JSDescriptor) {
+    return;
+  }
+
+  v8::Local<v8::String> hPropName =
+      hCallBackInfo->GetInternalField(1).As<v8::String>();
+  if (hPropName.IsEmpty())
+    return;
+
+  v8::String::Utf8Value szPropName(info.GetIsolate(), hPropName);
+  CJS_Result result =
+      pClassDescriptor->dynMethodCall(info, WideString::FromUTF8(*szPropName));
+
+  if (result.HasError()) {
+    WideString err = JSFormatErrorString(pClassDescriptor->name, *szPropName,
+                                         result.Error());
+    v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(
+        info.GetIsolate(), err.ToDefANSI().c_str(), v8::NewStringType::kNormal);
+    info.GetIsolate()->ThrowException(str.ToLocalChecked());
+    return;
+  }
+
+  if (result.HasReturn())
+    info.GetReturnValue().Set(result.Return());
+}
+
+void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
+                          CFXJSE_Value* pObject,
+                          ByteStringView szPropName,
+                          CFXJSE_Value* pValue) {
+  ASSERT(lpClass);
+
+  int32_t nPropType =
+      lpClass->dynPropTypeGetter == nullptr
+          ? FXJSE_ClassPropType_Property
+          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
+  if (nPropType == FXJSE_ClassPropType_Property) {
+    if (lpClass->dynPropGetter)
+      lpClass->dynPropGetter(pObject, szPropName, pValue);
+  } else if (nPropType == FXJSE_ClassPropType_Method) {
+    if (lpClass->dynMethodCall && pValue) {
+      v8::Isolate* pIsolate = pValue->GetIsolate();
+      v8::HandleScope hscope(pIsolate);
+      v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
+          v8::ObjectTemplate::New(pIsolate);
+      hCallBackInfoTemplate->SetInternalFieldCount(2);
+      v8::Local<v8::Object> hCallBackInfo =
+          hCallBackInfoTemplate
+              ->NewInstance(pValue->GetIsolate()->GetCurrentContext())
+              .ToLocalChecked();
+      hCallBackInfo->SetAlignedPointerInInternalField(
+          0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
+      hCallBackInfo->SetInternalField(
+          1, v8::String::NewFromUtf8(
+                 pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()),
+                 v8::NewStringType::kNormal, szPropName.GetLength())
+                 .ToLocalChecked());
+      pValue->ForceSetValue(
+          v8::Function::New(pValue->GetIsolate()->GetCurrentContext(),
+                            DynPropGetterAdapter_MethodCallback, hCallBackInfo,
+                            0, v8::ConstructorBehavior::kThrow)
+              .ToLocalChecked());
+    }
+  }
+}
+
+void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
+                          CFXJSE_Value* pObject,
+                          ByteStringView szPropName,
+                          CFXJSE_Value* pValue) {
+  ASSERT(lpClass);
+  int32_t nPropType =
+      lpClass->dynPropTypeGetter == nullptr
+          ? FXJSE_ClassPropType_Property
+          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
+  if (nPropType != FXJSE_ClassPropType_Method) {
+    if (lpClass->dynPropSetter)
+      lpClass->dynPropSetter(pObject, szPropName, pValue);
+  }
+}
+
+bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
+                         CFXJSE_Value* pObject,
+                         ByteStringView szPropName) {
+  ASSERT(lpClass);
+  int32_t nPropType =
+      lpClass->dynPropTypeGetter == nullptr
+          ? FXJSE_ClassPropType_Property
+          : lpClass->dynPropTypeGetter(pObject, szPropName, true);
+  return nPropType != FXJSE_ClassPropType_None;
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Local<v8::Object> thisObject = info.Holder();
+  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+      AsClassDescriptor(info.Data().As<v8::External>()->Value());
+  if (!lpClass)
+    return;
+
+  v8::HandleScope scope(info.GetIsolate());
+  v8::String::Utf8Value szPropName(info.GetIsolate(), property);
+  ByteStringView szFxPropName(*szPropName, szPropName.length());
+  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
+  lpThisValue->ForceSetValue(thisObject);
+  if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) {
+    info.GetReturnValue().Set(v8::DontDelete);
+    return;
+  }
+  const int32_t iV8Absent = 64;
+  info.GetReturnValue().Set(iV8Absent);
+}
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> thisObject = info.Holder();
+  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+      AsClassDescriptor(info.Data().As<v8::External>()->Value());
+  if (!lpClass)
+    return;
+
+  v8::String::Utf8Value szPropName(info.GetIsolate(), property);
+  ByteStringView szFxPropName(*szPropName, szPropName.length());
+  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
+  lpThisValue->ForceSetValue(thisObject);
+  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
+  DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
+                       lpNewValue.get());
+  info.GetReturnValue().Set(lpNewValue->DirectGetValue());
+}
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> thisObject = info.Holder();
+  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+      AsClassDescriptor(info.Data().As<v8::External>()->Value());
+  if (!lpClass)
+    return;
+
+  v8::String::Utf8Value szPropName(info.GetIsolate(), property);
+  ByteStringView szFxPropName(*szPropName, szPropName.length());
+  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
+  lpThisValue->ForceSetValue(thisObject);
+  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
+  lpNewValue->ForceSetValue(value);
+  DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
+                       lpNewValue.get());
+  info.GetReturnValue().Set(value);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
+}
+
+void SetUpNamedPropHandler(v8::Isolate* pIsolate,
+                           v8::Local<v8::ObjectTemplate>* pObjectTemplate,
+                           const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
+  v8::NamedPropertyHandlerConfiguration configuration(
+      lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : nullptr,
+      lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : nullptr,
+      lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback
+                                           : nullptr,
+      nullptr, NamedPropertyEnumeratorCallback,
+      v8::External::New(pIsolate,
+                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
+      v8::PropertyHandlerFlags::kNonMasking);
+  (*pObjectTemplate)->SetHandler(configuration);
+}
+
+}  // namespace
+
+// static
+CFXJSE_Class* CFXJSE_Class::Create(
+    CFXJSE_Context* lpContext,
+    const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
+    bool bIsJSGlobal) {
+  if (!lpContext || !lpClassDefinition)
+    return nullptr;
+
+  CFXJSE_Class* pExistingClass =
+      lpContext->GetClassByName(lpClassDefinition->name);
+  if (pExistingClass)
+    return pExistingClass;
+
+  v8::Isolate* pIsolate = lpContext->GetIsolate();
+  auto pClass = pdfium::MakeUnique<CFXJSE_Class>(lpContext);
+  pClass->m_szClassName = lpClassDefinition->name;
+  pClass->m_lpClassDefinition = lpClassDefinition;
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
+      pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
+      v8::External::New(
+          pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
+  hFunctionTemplate->SetClassName(
+      v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name,
+                              v8::NewStringType::kNormal)
+          .ToLocalChecked());
+  hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2);
+  v8::Local<v8::ObjectTemplate> hObjectTemplate =
+      hFunctionTemplate->InstanceTemplate();
+  SetUpNamedPropHandler(pIsolate, &hObjectTemplate, lpClassDefinition);
+
+  if (lpClassDefinition->methNum) {
+    for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
+      v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
+          pIsolate, V8FunctionCallback_Wrapper,
+          v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
+                                          lpClassDefinition->methods + i)));
+      fun->RemovePrototype();
+      hObjectTemplate->Set(
+          v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name,
+                                  v8::NewStringType::kNormal)
+              .ToLocalChecked(),
+          fun,
+          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
+    }
+  }
+
+  if (bIsJSGlobal) {
+    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
+        pIsolate, Context_GlobalObjToString,
+        v8::External::New(
+            pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
+    fun->RemovePrototype();
+    hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString",
+                                                 v8::NewStringType::kNormal)
+                             .ToLocalChecked(),
+                         fun);
+  }
+  pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate);
+  CFXJSE_Class* pResult = pClass.get();
+  lpContext->AddClass(std::move(pClass));
+  return pResult;
+}
+
+CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext) : m_pContext(lpContext) {}
+
+CFXJSE_Class::~CFXJSE_Class() {}
diff --git a/fxjs/xfa/cfxjse_class.h b/fxjs/xfa/cfxjse_class.h
new file mode 100644
index 0000000..f5bc232
--- /dev/null
+++ b/fxjs/xfa/cfxjse_class.h
@@ -0,0 +1,40 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_CLASS_H_
+#define FXJS_XFA_CFXJSE_CLASS_H_
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/xfa/fxjse.h"
+#include "v8/include/v8.h"
+
+class CFXJSE_Context;
+class CFXJSE_Value;
+struct FXJSE_CLASS_DESCRIPTOR;
+
+class CFXJSE_Class {
+ public:
+  static CFXJSE_Class* Create(CFXJSE_Context* pContext,
+                              const FXJSE_CLASS_DESCRIPTOR* lpClassDefintion,
+                              bool bIsJSGlobal);
+
+  explicit CFXJSE_Class(CFXJSE_Context* lpContext);
+  ~CFXJSE_Class();
+
+  CFXJSE_Context* GetContext() const { return m_pContext.Get(); }
+  v8::Global<v8::FunctionTemplate>& GetTemplate() { return m_hTemplate; }
+
+ protected:
+  friend class CFXJSE_Context;
+  friend class CFXJSE_Value;
+
+  ByteString m_szClassName;
+  UnownedPtr<const FXJSE_CLASS_DESCRIPTOR> m_lpClassDefinition;
+  UnownedPtr<CFXJSE_Context> const m_pContext;
+  v8::Global<v8::FunctionTemplate> m_hTemplate;
+};
+
+#endif  // FXJS_XFA_CFXJSE_CLASS_H_
diff --git a/fxjs/xfa/cfxjse_context.cpp b/fxjs/xfa/cfxjse_context.cpp
new file mode 100644
index 0000000..1ff1417
--- /dev/null
+++ b/fxjs/xfa/cfxjse_context.cpp
@@ -0,0 +1,327 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_context.h"
+
+#include <utility>
+
+#include "fxjs/cfxjs_engine.h"
+#include "fxjs/xfa/cfxjse_class.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "fxjs/xfa/cfxjse_runtimedata.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+const char szCompatibleModeScript[] =
+    "(function(global, list) {\n"
+    "  'use strict';\n"
+    "  var objname;\n"
+    "  for (objname in list) {\n"
+    "    var globalobj = global[objname];\n"
+    "    if (globalobj) {\n"
+    "      list[objname].forEach(function(name) {\n"
+    "        if (!globalobj[name]) {\n"
+    "          Object.defineProperty(globalobj, name, {\n"
+    "            writable: true,\n"
+    "            enumerable: false,\n"
+    "            value: (function(obj) {\n"
+    "              if (arguments.length === 0) {\n"
+    "                throw new TypeError('missing argument 0 when calling "
+    "                    function ' + objname + '.' + name);\n"
+    "              }\n"
+    "              return globalobj.prototype[name].apply(obj, "
+    "                  Array.prototype.slice.call(arguments, 1));\n"
+    "            })\n"
+    "          });\n"
+    "        }\n"
+    "      });\n"
+    "    }\n"
+    "  }\n"
+    "}(this, {String: ['substr', 'toUpperCase']}));";
+
+const char szConsoleScript[] =
+    "console.show = function() {};\n"
+    "\n"
+    "console.println = function(...args) {\n"
+    "  this.log(...args);\n"
+    "};";
+
+// Only address matters, values are for humans debuging here.
+char g_FXJSEHostObjectTag[] = "FXJSE Host Object";
+char g_FXJSEProxyObjectTag[] = "FXJSE Proxy Object";
+
+v8::Local<v8::Object> CreateReturnValue(v8::Isolate* pIsolate,
+                                        v8::TryCatch* trycatch) {
+  v8::Local<v8::Object> hReturnValue = v8::Object::New(pIsolate);
+  if (!trycatch->HasCaught())
+    return hReturnValue;
+
+  v8::Local<v8::Message> hMessage = trycatch->Message();
+  if (hMessage.IsEmpty())
+    return hReturnValue;
+
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+  v8::Local<v8::Value> hException = trycatch->Exception();
+  if (hException->IsObject()) {
+    v8::Local<v8::String> hNameStr =
+        v8::String::NewFromUtf8(pIsolate, "name", v8::NewStringType::kNormal)
+            .ToLocalChecked();
+    v8::Local<v8::Value> hValue =
+        hException.As<v8::Object>()->Get(context, hNameStr).ToLocalChecked();
+    if (hValue->IsString() || hValue->IsStringObject()) {
+      hReturnValue->Set(context, 0, hValue).FromJust();
+    } else {
+      v8::Local<v8::String> hErrorStr =
+          v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
+              .ToLocalChecked();
+      hReturnValue->Set(context, 0, hErrorStr).FromJust();
+    }
+
+    v8::Local<v8::String> hMessageStr =
+        v8::String::NewFromUtf8(pIsolate, "message", v8::NewStringType::kNormal)
+            .ToLocalChecked();
+    hValue =
+        hException.As<v8::Object>()->Get(context, hMessageStr).ToLocalChecked();
+    if (hValue->IsString() || hValue->IsStringObject())
+      hReturnValue->Set(context, 1, hValue).FromJust();
+    else
+      hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
+  } else {
+    v8::Local<v8::String> hErrorStr =
+        v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
+            .ToLocalChecked();
+    hReturnValue->Set(context, 0, hErrorStr).FromJust();
+    hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
+  }
+  hReturnValue->Set(context, 2, hException).FromJust();
+  int line = hMessage->GetLineNumber(context).FromMaybe(0);
+  hReturnValue->Set(context, 3, v8::Integer::New(pIsolate, line)).FromJust();
+  v8::Local<v8::String> source =
+      hMessage->GetSourceLine(context).FromMaybe(v8::Local<v8::String>());
+  hReturnValue->Set(context, 4, source).FromJust();
+  int column = hMessage->GetStartColumn(context).FromMaybe(0);
+  hReturnValue->Set(context, 5, v8::Integer::New(pIsolate, column)).FromJust();
+  column = hMessage->GetEndColumn(context).FromMaybe(0);
+  hReturnValue->Set(context, 6, v8::Integer::New(pIsolate, column)).FromJust();
+  return hReturnValue;
+}
+
+class CFXJSE_ScopeUtil_IsolateHandleContext {
+ public:
+  explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext)
+      : m_parent(pContext->GetIsolate()), m_cscope(pContext->GetContext()) {}
+  CFXJSE_ScopeUtil_IsolateHandleContext(
+      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
+  CFXJSE_ScopeUtil_IsolateHandleContext& operator=(
+      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
+
+ private:
+  void* operator new(size_t size) = delete;
+  void operator delete(void*, size_t) = delete;
+
+  CFXJSE_ScopeUtil_IsolateHandle m_parent;
+  v8::Context::Scope m_cscope;
+};
+
+void FXJSE_UpdateProxyBinding(v8::Local<v8::Object> hObject) {
+  ASSERT(!hObject.IsEmpty());
+  ASSERT(hObject->InternalFieldCount() == 2);
+  hObject->SetAlignedPointerInInternalField(0, g_FXJSEProxyObjectTag);
+  hObject->SetAlignedPointerInInternalField(1, nullptr);
+}
+
+}  // namespace
+
+void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
+                               CFXJSE_HostObject* lpNewBinding) {
+  ASSERT(!hObject.IsEmpty());
+  ASSERT(hObject->InternalFieldCount() == 2);
+  hObject->SetAlignedPointerInInternalField(0, g_FXJSEHostObjectTag);
+  hObject->SetAlignedPointerInInternalField(1, lpNewBinding);
+}
+
+void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hObject) {
+  ASSERT(!hObject.IsEmpty());
+  ASSERT(hObject->InternalFieldCount() == 2);
+  hObject->SetAlignedPointerInInternalField(0, nullptr);
+  hObject->SetAlignedPointerInInternalField(1, nullptr);
+}
+
+CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(
+    v8::Local<v8::Object> hJSObject) {
+  ASSERT(!hJSObject.IsEmpty());
+  if (!hJSObject->IsObject())
+    return nullptr;
+
+  v8::Local<v8::Object> hObject = hJSObject;
+  if (hObject->InternalFieldCount() != 2 ||
+      hObject->GetAlignedPointerFromInternalField(0) == g_FXJSEProxyObjectTag) {
+    v8::Local<v8::Value> hProtoObject = hObject->GetPrototype();
+    if (hProtoObject.IsEmpty() || !hProtoObject->IsObject())
+      return nullptr;
+
+    hObject = hProtoObject.As<v8::Object>();
+    if (hObject->InternalFieldCount() != 2)
+      return nullptr;
+  }
+  if (hObject->GetAlignedPointerFromInternalField(0) != g_FXJSEHostObjectTag)
+    return nullptr;
+
+  return static_cast<CFXJSE_HostObject*>(
+      hObject->GetAlignedPointerFromInternalField(1));
+}
+
+// static
+std::unique_ptr<CFXJSE_Context> CFXJSE_Context::Create(
+    v8::Isolate* pIsolate,
+    const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
+    CFXJSE_HostObject* pGlobalObject) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  auto pContext = pdfium::MakeUnique<CFXJSE_Context>(pIsolate);
+
+  v8::Local<v8::ObjectTemplate> hObjectTemplate;
+  if (pGlobalClass) {
+    CFXJSE_Class* pGlobalClassObj =
+        CFXJSE_Class::Create(pContext.get(), pGlobalClass, true);
+    ASSERT(pGlobalClassObj);
+    v8::Local<v8::FunctionTemplate> hFunctionTemplate =
+        v8::Local<v8::FunctionTemplate>::New(pIsolate,
+                                             pGlobalClassObj->m_hTemplate);
+    hObjectTemplate = hFunctionTemplate->InstanceTemplate();
+  } else {
+    hObjectTemplate = v8::ObjectTemplate::New(pIsolate);
+    hObjectTemplate->SetInternalFieldCount(2);
+  }
+  hObjectTemplate->Set(
+      v8::Symbol::GetToStringTag(pIsolate),
+      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
+          .ToLocalChecked());
+
+  v8::Local<v8::Context> hNewContext =
+      v8::Context::New(pIsolate, nullptr, hObjectTemplate);
+
+  v8::Local<v8::Object> pThisProxy = hNewContext->Global();
+  FXJSE_UpdateProxyBinding(pThisProxy);
+
+  v8::Local<v8::Object> pThis = pThisProxy->GetPrototype().As<v8::Object>();
+  FXJSE_UpdateObjectBinding(pThis, pGlobalObject);
+
+  v8::Local<v8::Context> hRootContext = v8::Local<v8::Context>::New(
+      pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext);
+  hNewContext->SetSecurityToken(hRootContext->GetSecurityToken());
+  pContext->m_hContext.Reset(pIsolate, hNewContext);
+  return pContext;
+}
+
+CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
+
+CFXJSE_Context::~CFXJSE_Context() {}
+
+std::unique_ptr<CFXJSE_Value> CFXJSE_Context::GetGlobalObject() {
+  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
+  v8::Local<v8::Context> hContext =
+      v8::Local<v8::Context>::New(GetIsolate(), m_hContext);
+  v8::Local<v8::Object> hGlobalObject =
+      hContext->Global()->GetPrototype().As<v8::Object>();
+  pValue->ForceSetValue(hGlobalObject);
+  return pValue;
+}
+
+v8::Local<v8::Context> CFXJSE_Context::GetContext() {
+  return v8::Local<v8::Context>::New(GetIsolate(), m_hContext);
+}
+
+void CFXJSE_Context::AddClass(std::unique_ptr<CFXJSE_Class> pClass) {
+  m_rgClasses.push_back(std::move(pClass));
+}
+
+CFXJSE_Class* CFXJSE_Context::GetClassByName(ByteStringView szName) const {
+  auto pClass =
+      std::find_if(m_rgClasses.begin(), m_rgClasses.end(),
+                   [szName](const std::unique_ptr<CFXJSE_Class>& item) {
+                     return szName == item->m_szClassName;
+                   });
+  return pClass != m_rgClasses.end() ? pClass->get() : nullptr;
+}
+
+void CFXJSE_Context::EnableCompatibleMode() {
+  ExecuteScript(szCompatibleModeScript, nullptr, nullptr);
+  ExecuteScript(szConsoleScript, nullptr, nullptr);
+}
+
+bool CFXJSE_Context::ExecuteScript(const char* szScript,
+                                   CFXJSE_Value* lpRetValue,
+                                   CFXJSE_Value* lpNewThisObject) {
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
+  v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
+  v8::TryCatch trycatch(GetIsolate());
+  v8::Local<v8::String> hScriptString =
+      v8::String::NewFromUtf8(GetIsolate(), szScript,
+                              v8::NewStringType::kNormal)
+          .ToLocalChecked();
+  if (!lpNewThisObject) {
+    v8::Local<v8::Script> hScript;
+    if (v8::Script::Compile(hContext, hScriptString).ToLocal(&hScript)) {
+      ASSERT(!trycatch.HasCaught());
+      v8::Local<v8::Value> hValue;
+      if (hScript->Run(hContext).ToLocal(&hValue)) {
+        ASSERT(!trycatch.HasCaught());
+        if (lpRetValue)
+          lpRetValue->ForceSetValue(hValue);
+        return true;
+      }
+    }
+    if (lpRetValue)
+      lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
+    return false;
+  }
+
+  v8::Local<v8::Value> hNewThis = v8::Local<v8::Value>::New(
+      GetIsolate(), lpNewThisObject->DirectGetValue());
+  ASSERT(!hNewThis.IsEmpty());
+  v8::Local<v8::String> hEval =
+      v8::String::NewFromUtf8(GetIsolate(),
+                              "(function () { return eval(arguments[0]); })",
+                              v8::NewStringType::kNormal)
+          .ToLocalChecked();
+  v8::Local<v8::Script> hWrapper =
+      v8::Script::Compile(hContext, hEval).ToLocalChecked();
+  v8::Local<v8::Value> hWrapperValue;
+  if (hWrapper->Run(hContext).ToLocal(&hWrapperValue)) {
+    ASSERT(!trycatch.HasCaught());
+    v8::Local<v8::Function> hWrapperFn = hWrapperValue.As<v8::Function>();
+    v8::Local<v8::Value> rgArgs[] = {hScriptString};
+    v8::Local<v8::Value> hValue;
+    if (hWrapperFn->Call(hContext, hNewThis.As<v8::Object>(), 1, rgArgs)
+            .ToLocal(&hValue)) {
+      ASSERT(!trycatch.HasCaught());
+      if (lpRetValue)
+        lpRetValue->ForceSetValue(hValue);
+      return true;
+    }
+  }
+
+#ifndef NDEBUG
+  v8::String::Utf8Value error(GetIsolate(), trycatch.Exception());
+  fprintf(stderr, "JS Error: %s\n", *error);
+
+  v8::Local<v8::Message> message = trycatch.Message();
+  if (!message.IsEmpty()) {
+    v8::Local<v8::Context> context(GetIsolate()->GetCurrentContext());
+    int linenum = message->GetLineNumber(context).FromJust();
+    v8::String::Utf8Value sourceline(
+        GetIsolate(), message->GetSourceLine(context).ToLocalChecked());
+    fprintf(stderr, "Line %d: %s\n", linenum, *sourceline);
+  }
+#endif  // NDEBUG
+
+  if (lpRetValue)
+    lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
+  return false;
+}
diff --git a/fxjs/xfa/cfxjse_context.h b/fxjs/xfa/cfxjse_context.h
new file mode 100644
index 0000000..b519e77
--- /dev/null
+++ b/fxjs/xfa/cfxjse_context.h
@@ -0,0 +1,58 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_CONTEXT_H_
+#define FXJS_XFA_CFXJSE_CONTEXT_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/v8.h"
+
+class CFXJS_Engine;
+class CFXJSE_Class;
+class CFXJSE_HostObject;
+class CFXJSE_Value;
+struct FXJSE_CLASS_DESCRIPTOR;
+
+class CFXJSE_Context {
+ public:
+  static std::unique_ptr<CFXJSE_Context> Create(
+      v8::Isolate* pIsolate,
+      const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
+      CFXJSE_HostObject* pGlobalObject);
+
+  explicit CFXJSE_Context(v8::Isolate* pIsolate);
+  ~CFXJSE_Context();
+
+  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  v8::Local<v8::Context> GetContext();
+  std::unique_ptr<CFXJSE_Value> GetGlobalObject();
+  void AddClass(std::unique_ptr<CFXJSE_Class> pClass);
+  CFXJSE_Class* GetClassByName(ByteStringView szName) const;
+  void EnableCompatibleMode();
+  bool ExecuteScript(const char* szScript,
+                     CFXJSE_Value* lpRetValue,
+                     CFXJSE_Value* lpNewThisObject);
+
+ private:
+  CFXJSE_Context(const CFXJSE_Context&) = delete;
+  CFXJSE_Context& operator=(const CFXJSE_Context&) = delete;
+
+  v8::Global<v8::Context> m_hContext;
+  UnownedPtr<v8::Isolate> m_pIsolate;
+  std::vector<std::unique_ptr<CFXJSE_Class>> m_rgClasses;
+};
+
+void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
+                               CFXJSE_HostObject* lpNewBinding);
+
+void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hJSObject);
+CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Object> hJSObject);
+
+#endif  // FXJS_XFA_CFXJSE_CONTEXT_H_
diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp
new file mode 100644
index 0000000..9be2498
--- /dev/null
+++ b/fxjs/xfa/cfxjse_engine.cpp
@@ -0,0 +1,825 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_engine.h"
+
+#include <utility>
+
+#include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/cfx_widetextbuf.h"
+#include "core/fxcrt/fx_extension.h"
+#include "fxjs/cjs_runtime.h"
+#include "fxjs/xfa/cfxjse_class.h"
+#include "fxjs/xfa/cfxjse_context.h"
+#include "fxjs/xfa/cfxjse_formcalc_context.h"
+#include "fxjs/xfa/cfxjse_resolveprocessor.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fxfa/cxfa_eventparam.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_nodehelper.h"
+#include "xfa/fxfa/parser/cxfa_object.h"
+#include "xfa/fxfa/parser/cxfa_thisproxy.h"
+#include "xfa/fxfa/parser/cxfa_treelist.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+using pdfium::fxjse::kClassTag;
+
+const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = {
+    kClassTag,  // tag
+    "Root",     // name
+    nullptr,    // methods
+    0,          // method count
+    CFXJSE_Engine::GlobalPropTypeGetter,
+    CFXJSE_Engine::GlobalPropertyGetter,
+    CFXJSE_Engine::GlobalPropertySetter,
+    CFXJSE_Engine::NormalMethodCall,
+};
+
+const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = {
+    kClassTag,    // tag
+    "XFAObject",  // name
+    nullptr,      // methods
+    0,            // method count
+    CFXJSE_Engine::NormalPropTypeGetter,
+    CFXJSE_Engine::NormalPropertyGetter,
+    CFXJSE_Engine::NormalPropertySetter,
+    CFXJSE_Engine::NormalMethodCall,
+};
+
+const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor = {
+    kClassTag,          // tag
+    "XFAScriptObject",  // name
+    nullptr,            // methods
+    0,                  // method count
+    CFXJSE_Engine::NormalPropTypeGetter,
+    CFXJSE_Engine::GlobalPropertyGetter,
+    CFXJSE_Engine::GlobalPropertySetter,
+    CFXJSE_Engine::NormalMethodCall,
+};
+
+namespace {
+
+const char kFormCalcRuntime[] = "pfm_rt";
+
+CXFA_ThisProxy* ToThisProxy(CFXJSE_Value* pValue) {
+  CFXJSE_HostObject* pHostObject = pValue->ToHostObject();
+  return pHostObject ? ToThisProxy(pHostObject->AsCXFAObject()) : nullptr;
+}
+
+}  // namespace
+
+// static
+CXFA_Object* CFXJSE_Engine::ToObject(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (!info.Holder()->IsObject())
+    return nullptr;
+
+  CFXJSE_HostObject* pHostObj =
+      FXJSE_RetrieveObjectBinding(info.Holder().As<v8::Object>());
+  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+}
+
+// static.
+CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_Value* pValue) {
+  CFXJSE_HostObject* pHostObj = pValue->ToHostObject();
+  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+}
+
+CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument,
+                             CJS_Runtime* fxjs_runtime)
+    : CFX_V8(fxjs_runtime->GetIsolate()),
+      m_pSubordinateRuntime(fxjs_runtime),
+      m_pDocument(pDocument),
+      m_JsContext(CFXJSE_Context::Create(fxjs_runtime->GetIsolate(),
+                                         &GlobalClassDescriptor,
+                                         pDocument->GetRoot())),
+      m_ResolveProcessor(pdfium::MakeUnique<CFXJSE_ResolveProcessor>()) {
+  RemoveBuiltInObjs(m_JsContext.get());
+  m_JsContext->EnableCompatibleMode();
+
+  // Don't know if this can happen before we remove the builtin objs and set
+  // compatibility mode.
+  m_pJsClass =
+      CFXJSE_Class::Create(m_JsContext.get(), &NormalClassDescriptor, false);
+}
+
+CFXJSE_Engine::~CFXJSE_Engine() {
+  for (const auto& pair : m_mapVariableToContext)
+    delete ToThisProxy(pair.second->GetGlobalObject().get());
+
+  for (const auto& pair : m_mapObjectToValue)
+    pair.second->ClearHostObject();
+}
+
+bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType,
+                              WideStringView wsScript,
+                              CFXJSE_Value* hRetValue,
+                              CXFA_Object* pThisObject) {
+  ByteString btScript;
+  AutoRestorer<CXFA_Script::Type> typeRestorer(&m_eScriptType);
+  m_eScriptType = eScriptType;
+  if (eScriptType == CXFA_Script::Type::Formcalc) {
+    if (!m_FM2JSContext) {
+      m_FM2JSContext = pdfium::MakeUnique<CFXJSE_FormCalcContext>(
+          GetIsolate(), m_JsContext.get(), m_pDocument.Get());
+    }
+    CFX_WideTextBuf wsJavaScript;
+    if (!CFXJSE_FormCalcContext::Translate(wsScript, &wsJavaScript)) {
+      hRetValue->SetUndefined();
+      return false;
+    }
+    btScript = FX_UTF8Encode(wsJavaScript.AsStringView());
+  } else {
+    btScript = FX_UTF8Encode(wsScript);
+  }
+  AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
+  m_pThisObject = pThisObject;
+
+  CFXJSE_Value* pValue =
+      pThisObject ? GetOrCreateJSBindingFromMap(pThisObject) : nullptr;
+  IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime.Get());
+  return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pValue);
+}
+
+bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode,
+                                    WideStringView propname,
+                                    CFXJSE_Value* pValue,
+                                    uint32_t dwFlag,
+                                    bool bSetting) {
+  if (!refNode)
+    return false;
+
+  XFA_RESOLVENODE_RS resolveRs;
+  if (!ResolveObjects(refNode, propname, &resolveRs, dwFlag, nullptr))
+    return false;
+  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Nodes) {
+    pValue->Assign(
+        GetOrCreateJSBindingFromMap(resolveRs.objects.front().Get()));
+    return true;
+  }
+  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Attribute &&
+      resolveRs.script_attribute.callback) {
+    CJX_Object* jsObject = resolveRs.objects.front()->JSObject();
+    (*resolveRs.script_attribute.callback)(
+        jsObject, pValue, bSetting, resolveRs.script_attribute.attribute);
+  }
+  return true;
+}
+
+// static
+void CFXJSE_Engine::GlobalPropertySetter(CFXJSE_Value* pObject,
+                                         ByteStringView szPropName,
+                                         CFXJSE_Value* pValue) {
+  CXFA_Object* lpOrginalNode = ToObject(pObject);
+  CXFA_Document* pDoc = lpOrginalNode->GetDocument();
+  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
+  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
+  if (lpOrginalNode->IsThisProxy())
+    pRefNode = ToNode(lpScriptContext->GetVariablesThis(lpOrginalNode, false));
+
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+  if (lpScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), pValue,
+          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings |
+              XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
+              XFA_RESOLVENODE_Attributes,
+          true)) {
+    return;
+  }
+  if (lpOrginalNode->IsThisProxy() && pValue && pValue->IsUndefined()) {
+    pObject->DeleteObjectProperty(szPropName);
+    return;
+  }
+  CXFA_FFNotify* pNotify = pDoc->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  auto* pCJSRuntime =
+      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  if (!pCJSRuntime)
+    return;
+
+  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
+  IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
+  pCJSRuntime->SetValueByNameInGlobalObject(
+      szPropName, v8::Local<v8::Value>::New(lpScriptContext->GetIsolate(),
+                                            pValue->DirectGetValue()));
+}
+
+// static
+void CFXJSE_Engine::GlobalPropertyGetter(CFXJSE_Value* pObject,
+                                         ByteStringView szPropName,
+                                         CFXJSE_Value* pValue) {
+  CXFA_Object* pOriginalObject = ToObject(pObject);
+  CXFA_Document* pDoc = pOriginalObject->GetDocument();
+  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+
+  pValue->SetUndefined();  // Assume failure.
+  if (lpScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
+    if (szPropName == kFormCalcRuntime) {
+      lpScriptContext->m_FM2JSContext->GlobalPropertyGetter(pValue);
+      return;
+    }
+    XFA_HashCode uHashCode = static_cast<XFA_HashCode>(
+        FX_HashCode_GetW(wsPropName.AsStringView(), false));
+    if (uHashCode != XFA_HASHCODE_Layout) {
+      CXFA_Object* pObj =
+          lpScriptContext->GetDocument()->GetXFAObject(uHashCode);
+      if (pObj) {
+        pValue->Assign(lpScriptContext->GetOrCreateJSBindingFromMap(pObj));
+        return;
+      }
+    }
+  }
+
+  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
+  if (pOriginalObject->IsThisProxy()) {
+    pRefNode =
+        ToNode(lpScriptContext->GetVariablesThis(pOriginalObject, false));
+  }
+  if (lpScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), pValue,
+          XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
+              XFA_RESOLVENODE_Attributes,
+          false)) {
+    return;
+  }
+  if (lpScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), pValue,
+          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false)) {
+    return;
+  }
+
+  CXFA_Object* pScriptObject =
+      lpScriptContext->GetVariablesThis(pOriginalObject, true);
+  if (pScriptObject && lpScriptContext->QueryVariableValue(
+                           pScriptObject->AsNode(), szPropName, pValue, true)) {
+    return;
+  }
+
+  CXFA_FFNotify* pNotify = pDoc->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  auto* pCJSRuntime =
+      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  if (!pCJSRuntime)
+    return;
+
+  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
+  IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
+  v8::Local<v8::Value> temp_value;
+  if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_value))
+    return;
+
+  if (temp_value.IsEmpty())
+    return;
+
+  pValue->ForceSetValue(temp_value);
+}
+
+int32_t CFXJSE_Engine::GlobalPropTypeGetter(CFXJSE_Value* pOriginalValue,
+                                            ByteStringView szPropName,
+                                            bool bQueryIn) {
+  CXFA_Object* pObject = ToObject(pOriginalValue);
+  if (!pObject)
+    return FXJSE_ClassPropType_None;
+
+  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = lpScriptContext->GetVariablesThis(pObject, false);
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+  if (pObject->JSObject()->HasMethod(wsPropName))
+    return FXJSE_ClassPropType_Method;
+
+  return FXJSE_ClassPropType_Property;
+}
+
+// static
+void CFXJSE_Engine::NormalPropertyGetter(CFXJSE_Value* pOriginalValue,
+                                         ByteStringView szPropName,
+                                         CFXJSE_Value* pReturnValue) {
+  pReturnValue->SetUndefined();  // Assume failure.
+  CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
+  if (!pOriginalObject)
+    return;
+
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+  CFXJSE_Engine* lpScriptContext =
+      pOriginalObject->GetDocument()->GetScriptContext();
+  CXFA_Object* pObject =
+      lpScriptContext->GetVariablesThis(pOriginalObject, false);
+  if (wsPropName.EqualsASCII("xfa")) {
+    CFXJSE_Value* pValue = lpScriptContext->GetOrCreateJSBindingFromMap(
+        lpScriptContext->GetDocument()->GetRoot());
+    pReturnValue->Assign(pValue);
+    return;
+  }
+
+  bool bRet = lpScriptContext->QueryNodeByFlag(
+      ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
+      XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
+          XFA_RESOLVENODE_Attributes,
+      false);
+  if (bRet)
+    return;
+
+  if (pObject == lpScriptContext->GetThisObject() ||
+      (lpScriptContext->GetType() == CXFA_Script::Type::Javascript &&
+       !lpScriptContext->IsStrictScopeInJavaScript())) {
+    bRet = lpScriptContext->QueryNodeByFlag(
+        ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
+        XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false);
+  }
+  if (bRet)
+    return;
+
+  CXFA_Object* pScriptObject =
+      lpScriptContext->GetVariablesThis(pOriginalObject, true);
+  if (!pScriptObject)
+    return;
+
+  bRet = lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
+                                             pReturnValue, true);
+  if (bRet)
+    return;
+
+  Optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName(
+      pObject->GetElementType(), wsPropName.AsStringView());
+  if (info.has_value()) {
+    CJX_Object* jsObject = pObject->JSObject();
+    (*info.value().callback)(jsObject, pReturnValue, false,
+                             info.value().attribute);
+    return;
+  }
+
+  CXFA_FFNotify* pNotify = pObject->GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  auto* pCJSRuntime =
+      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  if (!pCJSRuntime)
+    return;
+
+  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
+  IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
+  v8::Local<v8::Value> temp_local;
+  if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_local))
+    return;
+
+  if (temp_local.IsEmpty())
+    return;
+
+  pReturnValue->ForceSetValue(temp_local);
+}
+
+// static
+void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue,
+                                         ByteStringView szPropName,
+                                         CFXJSE_Value* pReturnValue) {
+  CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
+  if (!pOriginalObject)
+    return;
+
+  CFXJSE_Engine* lpScriptContext =
+      pOriginalObject->GetDocument()->GetScriptContext();
+  CXFA_Object* pObject =
+      lpScriptContext->GetVariablesThis(pOriginalObject, false);
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+  WideStringView wsPropNameView = wsPropName.AsStringView();
+  Optional<XFA_SCRIPTATTRIBUTEINFO> info =
+      XFA_GetScriptAttributeByName(pObject->GetElementType(), wsPropNameView);
+  if (info.has_value()) {
+    CJX_Object* jsObject = pObject->JSObject();
+    (*info.value().callback)(jsObject, pReturnValue, true,
+                             info.value().attribute);
+    return;
+  }
+
+  if (pObject->IsNode()) {
+    if (wsPropNameView[0] == '#')
+      wsPropNameView = wsPropNameView.Last(wsPropNameView.GetLength() - 1);
+
+    CXFA_Node* pNode = ToNode(pObject);
+    CXFA_Node* pPropOrChild = nullptr;
+    XFA_Element eType = XFA_GetElementByName(wsPropNameView);
+    if (eType != XFA_Element::Unknown) {
+      pPropOrChild =
+          pNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eType);
+    } else {
+      pPropOrChild = pNode->GetFirstChildByName(wsPropNameView);
+    }
+
+    if (pPropOrChild) {
+      info = XFA_GetScriptAttributeByName(pPropOrChild->GetElementType(),
+                                          L"{default}");
+      if (info.has_value()) {
+        pPropOrChild->JSObject()->ScriptSomDefaultValue(pReturnValue, true,
+                                                        XFA_Attribute::Unknown);
+        return;
+      }
+    }
+  }
+
+  CXFA_Object* pScriptObject =
+      lpScriptContext->GetVariablesThis(pOriginalObject, true);
+  if (pScriptObject) {
+    lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
+                                        pReturnValue, false);
+  }
+}
+
+int32_t CFXJSE_Engine::NormalPropTypeGetter(CFXJSE_Value* pOriginalValue,
+                                            ByteStringView szPropName,
+                                            bool bQueryIn) {
+  CXFA_Object* pObject = ToObject(pOriginalValue);
+  if (!pObject)
+    return FXJSE_ClassPropType_None;
+
+  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = lpScriptContext->GetVariablesThis(pObject, false);
+  XFA_Element eType = pObject->GetElementType();
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+  if (pObject->JSObject()->HasMethod(wsPropName))
+    return FXJSE_ClassPropType_Method;
+
+  if (bQueryIn &&
+      !XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView())) {
+    return FXJSE_ClassPropType_None;
+  }
+  return FXJSE_ClassPropType_Property;
+}
+
+CJS_Result CFXJSE_Engine::NormalMethodCall(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const WideString& functionName) {
+  CXFA_Object* pObject = ToObject(info);
+  if (!pObject)
+    return CJS_Result::Failure(L"no Holder() present.");
+
+  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = lpScriptContext->GetVariablesThis(pObject, false);
+
+  std::vector<v8::Local<v8::Value>> parameters;
+  for (int i = 0; i < info.Length(); i++)
+    parameters.push_back(info[i]);
+
+  return pObject->JSObject()->RunMethod(functionName, parameters);
+}
+
+bool CFXJSE_Engine::IsStrictScopeInJavaScript() {
+  return m_pDocument->is_strict_scoping();
+}
+
+CXFA_Script::Type CFXJSE_Engine::GetType() {
+  return m_eScriptType;
+}
+
+CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Node* pScriptNode,
+                                                      CXFA_Node* pSubform) {
+  if (!pScriptNode || !pSubform)
+    return nullptr;
+
+  auto pNewContext =
+      CFXJSE_Context::Create(GetIsolate(), &VariablesClassDescriptor,
+                             new CXFA_ThisProxy(pSubform, pScriptNode));
+  RemoveBuiltInObjs(pNewContext.get());
+  pNewContext->EnableCompatibleMode();
+  CFXJSE_Context* pResult = pNewContext.get();
+  m_mapVariableToContext[pScriptNode] = std::move(pNewContext);
+  return pResult;
+}
+
+CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject,
+                                             bool bScriptNode) {
+  CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
+  if (!pProxy)
+    return pObject;
+
+  return bScriptNode ? pProxy->GetScriptNode() : pProxy->GetThisNode();
+}
+
+bool CFXJSE_Engine::RunVariablesScript(CXFA_Node* pScriptNode) {
+  if (!pScriptNode)
+    return false;
+
+  if (pScriptNode->GetElementType() != XFA_Element::Script)
+    return true;
+
+  CXFA_Node* pParent = pScriptNode->GetParent();
+  if (!pParent || pParent->GetElementType() != XFA_Element::Variables)
+    return false;
+
+  auto it = m_mapVariableToContext.find(pScriptNode);
+  if (it != m_mapVariableToContext.end() && it->second)
+    return true;
+
+  CXFA_Node* pTextNode = pScriptNode->GetFirstChild();
+  if (!pTextNode)
+    return false;
+
+  Optional<WideString> wsScript =
+      pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true);
+  if (!wsScript)
+    return false;
+
+  ByteString btScript = wsScript->ToUTF8();
+  auto hRetValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  CXFA_Node* pThisObject = pParent->GetParent();
+  CFXJSE_Context* pVariablesContext =
+      CreateVariablesContext(pScriptNode, pThisObject);
+  AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
+  m_pThisObject = pThisObject;
+  return pVariablesContext->ExecuteScript(btScript.c_str(), hRetValue.get(),
+                                          nullptr);
+}
+
+bool CFXJSE_Engine::QueryVariableValue(CXFA_Node* pScriptNode,
+                                       ByteStringView szPropName,
+                                       CFXJSE_Value* pValue,
+                                       bool bGetter) {
+  if (!pScriptNode || pScriptNode->GetElementType() != XFA_Element::Script)
+    return false;
+
+  CXFA_Node* variablesNode = pScriptNode->GetParent();
+  if (!variablesNode ||
+      variablesNode->GetElementType() != XFA_Element::Variables)
+    return false;
+
+  auto it = m_mapVariableToContext.find(pScriptNode);
+  if (it == m_mapVariableToContext.end() || !it->second)
+    return false;
+
+  CFXJSE_Context* pVariableContext = it->second.get();
+  std::unique_ptr<CFXJSE_Value> pObject = pVariableContext->GetGlobalObject();
+  auto hVariableValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  if (!bGetter) {
+    pObject->SetObjectOwnProperty(szPropName, pValue);
+    return true;
+  }
+
+  if (!pObject->HasObjectOwnProperty(szPropName, false))
+    return false;
+
+  pObject->GetObjectProperty(szPropName, hVariableValue.get());
+  if (hVariableValue->IsFunction())
+    pValue->SetFunctionBind(hVariableValue.get(), pObject.get());
+  else if (bGetter)
+    pValue->Assign(hVariableValue.get());
+  else
+    hVariableValue.get()->Assign(pValue);
+  return true;
+}
+
+void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) const {
+  const ByteStringView kObjNames[2] = {"Number", "Date"};
+  std::unique_ptr<CFXJSE_Value> pObject = pContext->GetGlobalObject();
+  auto hProp = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  for (const auto& obj : kObjNames) {
+    if (pObject->GetObjectProperty(obj, hProp.get()))
+      pObject->DeleteObjectProperty(obj);
+  }
+}
+
+bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject,
+                                   WideStringView wsExpression,
+                                   XFA_RESOLVENODE_RS* resolveNodeRS,
+                                   uint32_t dwStyles,
+                                   CXFA_Node* bindNode) {
+  if (wsExpression.IsEmpty())
+    return false;
+
+  if (m_eScriptType != CXFA_Script::Type::Formcalc ||
+      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
+    m_upObjectArray.clear();
+  }
+  if (refObject && refObject->IsNode() &&
+      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
+    m_upObjectArray.push_back(refObject->AsNode());
+  }
+
+  bool bNextCreate = false;
+  CXFA_NodeHelper* pNodeHelper = m_ResolveProcessor->GetNodeHelper();
+  if (dwStyles & XFA_RESOLVENODE_CreateNode)
+    pNodeHelper->SetCreateNodeType(bindNode);
+
+  pNodeHelper->m_pCreateParent = nullptr;
+  pNodeHelper->m_iCurAllStart = -1;
+
+  CFXJSE_ResolveNodeData rndFind(this);
+  int32_t nStart = 0;
+  int32_t nLevel = 0;
+
+  std::vector<UnownedPtr<CXFA_Object>> findObjects;
+  findObjects.emplace_back(refObject ? refObject : m_pDocument->GetRoot());
+  int32_t nNodes = 0;
+  while (true) {
+    nNodes = pdfium::CollectionSize<int32_t>(findObjects);
+    int32_t i = 0;
+    rndFind.m_dwStyles = dwStyles;
+    m_ResolveProcessor->SetCurStart(nStart);
+    nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind);
+    if (nStart < 1) {
+      if ((dwStyles & XFA_RESOLVENODE_CreateNode) && !bNextCreate) {
+        CXFA_Node* pDataNode = nullptr;
+        nStart = pNodeHelper->m_iCurAllStart;
+        if (nStart != -1) {
+          pDataNode = m_pDocument->GetNotBindNode(findObjects);
+          if (pDataNode) {
+            findObjects.clear();
+            findObjects.emplace_back(pDataNode);
+            break;
+          }
+        } else {
+          pDataNode = findObjects.front()->AsNode();
+          findObjects.clear();
+          findObjects.emplace_back(pDataNode);
+          break;
+        }
+        dwStyles |= XFA_RESOLVENODE_Bind;
+        findObjects.clear();
+        findObjects.emplace_back(pNodeHelper->m_pAllStartParent.Get());
+        continue;
+      }
+      break;
+    }
+    if (bNextCreate) {
+      int32_t checked_length =
+          pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
+      if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
+                                  nStart == checked_length, this)) {
+        continue;
+      }
+      break;
+    }
+    std::vector<UnownedPtr<CXFA_Object>> retObjects;
+    while (i < nNodes) {
+      bool bDataBind = false;
+      if (((dwStyles & XFA_RESOLVENODE_Bind) ||
+           (dwStyles & XFA_RESOLVENODE_CreateNode)) &&
+          nNodes > 1) {
+        CFXJSE_ResolveNodeData rndBind(nullptr);
+        m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind);
+        m_ResolveProcessor->SetIndexDataBind(rndBind.m_wsCondition, i, nNodes);
+        bDataBind = true;
+      }
+      rndFind.m_CurObject = findObjects[i++].Get();
+      rndFind.m_nLevel = nLevel;
+      rndFind.m_dwFlag = XFA_ResolveNode_RSType_Nodes;
+      if (!m_ResolveProcessor->Resolve(rndFind))
+        continue;
+
+      if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute &&
+          rndFind.m_ScriptAttribute.callback &&
+          nStart <
+              pdfium::base::checked_cast<int32_t>(wsExpression.GetLength())) {
+        auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+        CJX_Object* jsObject = rndFind.m_Objects.front()->JSObject();
+        (*rndFind.m_ScriptAttribute.callback)(
+            jsObject, pValue.get(), false, rndFind.m_ScriptAttribute.attribute);
+        if (!pValue->IsEmpty())
+          rndFind.m_Objects.front() = ToObject(pValue.get());
+      }
+      if (!m_upObjectArray.empty())
+        m_upObjectArray.pop_back();
+      retObjects.insert(retObjects.end(), rndFind.m_Objects.begin(),
+                        rndFind.m_Objects.end());
+      rndFind.m_Objects.clear();
+      if (bDataBind)
+        break;
+    }
+    findObjects.clear();
+
+    nNodes = pdfium::CollectionSize<int32_t>(retObjects);
+    if (nNodes < 1) {
+      if (dwStyles & XFA_RESOLVENODE_CreateNode) {
+        bNextCreate = true;
+        if (!pNodeHelper->m_pCreateParent) {
+          pNodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject.Get());
+          pNodeHelper->m_iCreateCount = 1;
+        }
+        int32_t checked_length =
+            pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
+        if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
+                                    nStart == checked_length, this)) {
+          continue;
+        }
+      }
+      break;
+    }
+
+    findObjects = std::move(retObjects);
+    rndFind.m_Objects.clear();
+    if (nLevel == 0)
+      dwStyles &= ~(XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
+
+    nLevel++;
+  }
+
+  if (!bNextCreate) {
+    resolveNodeRS->dwFlags = rndFind.m_dwFlag;
+    if (nNodes > 0) {
+      resolveNodeRS->objects.insert(resolveNodeRS->objects.end(),
+                                    findObjects.begin(), findObjects.end());
+    }
+    if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute) {
+      resolveNodeRS->script_attribute = rndFind.m_ScriptAttribute;
+      return true;
+    }
+  }
+  if (dwStyles & (XFA_RESOLVENODE_CreateNode | XFA_RESOLVENODE_Bind |
+                  XFA_RESOLVENODE_BindNew)) {
+    if (pNodeHelper->m_pCreateParent)
+      resolveNodeRS->objects.emplace_back(pNodeHelper->m_pCreateParent.Get());
+    else
+      pNodeHelper->CreateNodeForCondition(rndFind.m_wsCondition);
+
+    resolveNodeRS->dwFlags = pNodeHelper->m_iCreateFlag;
+    if (resolveNodeRS->dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
+      if (pNodeHelper->m_iCurAllStart != -1)
+        resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_CreateNodeMidAll;
+    }
+
+    if (!bNextCreate && (dwStyles & XFA_RESOLVENODE_CreateNode))
+      resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_ExistNodes;
+
+    return !resolveNodeRS->objects.empty();
+  }
+  return nNodes > 0;
+}
+
+void CFXJSE_Engine::AddToCacheList(std::unique_ptr<CXFA_List> pList) {
+  m_CacheList.push_back(std::move(pList));
+}
+
+CFXJSE_Value* CFXJSE_Engine::GetOrCreateJSBindingFromMap(CXFA_Object* pObject) {
+  if (pObject->IsNode())
+    RunVariablesScript(pObject->AsNode());
+
+  auto iter = m_mapObjectToValue.find(pObject);
+  if (iter != m_mapObjectToValue.end())
+    return iter->second.get();
+
+  auto jsValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  jsValue->SetHostObject(pObject, m_pJsClass.Get());
+
+  CFXJSE_Value* pValue = jsValue.get();
+  m_mapObjectToValue.insert(std::make_pair(pObject, std::move(jsValue)));
+  return pValue;
+}
+
+void CFXJSE_Engine::RemoveJSBindingFromMap(CXFA_Object* pObject) {
+  auto iter = m_mapObjectToValue.find(pObject);
+  if (iter == m_mapObjectToValue.end())
+    return;
+
+  iter->second->ClearHostObject();
+  m_mapObjectToValue.erase(iter);
+}
+
+void CFXJSE_Engine::SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray) {
+  m_pScriptNodeArray = pArray;
+}
+
+void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) {
+  if (m_pScriptNodeArray && !pdfium::ContainsValue(*m_pScriptNodeArray, pNode))
+    m_pScriptNodeArray->push_back(pNode);
+}
+
+CXFA_Object* CFXJSE_Engine::ToXFAObject(v8::Local<v8::Value> obj) {
+  if (obj.IsEmpty() || !obj->IsObject())
+    return nullptr;
+
+  CFXJSE_HostObject* pHostObj =
+      FXJSE_RetrieveObjectBinding(obj.As<v8::Object>());
+  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+}
+
+v8::Local<v8::Value> CFXJSE_Engine::NewXFAObject(
+    CXFA_Object* obj,
+    v8::Global<v8::FunctionTemplate>& tmpl) {
+  v8::EscapableHandleScope scope(GetIsolate());
+  v8::Local<v8::FunctionTemplate> klass =
+      v8::Local<v8::FunctionTemplate>::New(GetIsolate(), tmpl);
+  v8::Local<v8::Object> object = klass->InstanceTemplate()
+                                     ->NewInstance(m_JsContext->GetContext())
+                                     .ToLocalChecked();
+  FXJSE_UpdateObjectBinding(object, obj);
+  return scope.Escape(object);
+}
diff --git a/fxjs/xfa/cfxjse_engine.h b/fxjs/xfa/cfxjse_engine.h
new file mode 100644
index 0000000..addf0cc
--- /dev/null
+++ b/fxjs/xfa/cfxjse_engine.h
@@ -0,0 +1,141 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_ENGINE_H_
+#define FXJS_XFA_CFXJSE_ENGINE_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/cfx_v8.h"
+#include "v8/include/v8.h"
+#include "xfa/fxfa/cxfa_eventparam.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_script.h"
+#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+
+class CFXJSE_Class;
+class CFXJSE_Context;
+class CFXJSE_FormCalcContext;
+class CFXJSE_ResolveProcessor;
+class CJS_Runtime;
+class CXFA_List;
+
+// Flags for |dwStyles| argument to CFXJSE_Engine::ResolveObjects().
+#define XFA_RESOLVENODE_Children 0x0001
+#define XFA_RESOLVENODE_TagName 0x0002
+#define XFA_RESOLVENODE_Attributes 0x0004
+#define XFA_RESOLVENODE_Properties 0x0008
+#define XFA_RESOLVENODE_Siblings 0x0020
+#define XFA_RESOLVENODE_Parent 0x0040
+#define XFA_RESOLVENODE_AnyChild 0x0080
+#define XFA_RESOLVENODE_ALL 0x0100
+#define XFA_RESOLVENODE_CreateNode 0x0400
+#define XFA_RESOLVENODE_Bind 0x0800
+#define XFA_RESOLVENODE_BindNew 0x1000
+
+class CFXJSE_Engine final : public CFX_V8 {
+ public:
+  static CXFA_Object* ToObject(const v8::FunctionCallbackInfo<v8::Value>& info);
+  static CXFA_Object* ToObject(CFXJSE_Value* pValue);
+  static void GlobalPropertyGetter(CFXJSE_Value* pObject,
+                                   ByteStringView szPropName,
+                                   CFXJSE_Value* pValue);
+  static void GlobalPropertySetter(CFXJSE_Value* pObject,
+                                   ByteStringView szPropName,
+                                   CFXJSE_Value* pValue);
+  static void NormalPropertyGetter(CFXJSE_Value* pObject,
+                                   ByteStringView szPropName,
+                                   CFXJSE_Value* pValue);
+  static void NormalPropertySetter(CFXJSE_Value* pObject,
+                                   ByteStringView szPropName,
+                                   CFXJSE_Value* pValue);
+  static CJS_Result NormalMethodCall(
+      const v8::FunctionCallbackInfo<v8::Value>& info,
+      const WideString& functionName);
+  static int32_t NormalPropTypeGetter(CFXJSE_Value* pObject,
+                                      ByteStringView szPropName,
+                                      bool bQueryIn);
+  static int32_t GlobalPropTypeGetter(CFXJSE_Value* pObject,
+                                      ByteStringView szPropName,
+                                      bool bQueryIn);
+
+  CFXJSE_Engine(CXFA_Document* pDocument, CJS_Runtime* fxjs_runtime);
+  ~CFXJSE_Engine() override;
+
+  void SetEventParam(CXFA_EventParam* param) { m_eventParam = param; }
+  CXFA_EventParam* GetEventParam() const { return m_eventParam.Get(); }
+  bool RunScript(CXFA_Script::Type eScriptType,
+                 WideStringView wsScript,
+                 CFXJSE_Value* pRetValue,
+                 CXFA_Object* pThisObject);
+
+  bool ResolveObjects(CXFA_Object* refObject,
+                      WideStringView wsExpression,
+                      XFA_RESOLVENODE_RS* resolveNodeRS,
+                      uint32_t dwStyles,
+                      CXFA_Node* bindNode);
+
+  CFXJSE_Value* GetOrCreateJSBindingFromMap(CXFA_Object* pObject);
+  void RemoveJSBindingFromMap(CXFA_Object* pObject);
+
+  void AddToCacheList(std::unique_ptr<CXFA_List> pList);
+  CXFA_Object* GetThisObject() const { return m_pThisObject.Get(); }
+
+  void SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray);
+  void AddNodesOfRunScript(CXFA_Node* pNode);
+  CFXJSE_Class* GetJseNormalClass() const { return m_pJsClass.Get(); }
+
+  void SetRunAtType(XFA_AttributeValue eRunAt) { m_eRunAtType = eRunAt; }
+  bool IsRunAtClient() { return m_eRunAtType != XFA_AttributeValue::Server; }
+
+  CXFA_Script::Type GetType();
+  std::vector<CXFA_Node*>* GetUpObjectArray() { return &m_upObjectArray; }
+  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+
+  CXFA_Object* ToXFAObject(v8::Local<v8::Value> obj);
+  v8::Local<v8::Value> NewXFAObject(CXFA_Object* obj,
+                                    v8::Global<v8::FunctionTemplate>& tmpl);
+
+ private:
+  CFXJSE_Context* CreateVariablesContext(CXFA_Node* pScriptNode,
+                                         CXFA_Node* pSubform);
+  void RemoveBuiltInObjs(CFXJSE_Context* pContext) const;
+  bool QueryNodeByFlag(CXFA_Node* refNode,
+                       WideStringView propname,
+                       CFXJSE_Value* pValue,
+                       uint32_t dwFlag,
+                       bool bSetting);
+  bool IsStrictScopeInJavaScript();
+  CXFA_Object* GetVariablesThis(CXFA_Object* pObject, bool bScriptNode);
+  bool QueryVariableValue(CXFA_Node* pScriptNode,
+                          ByteStringView szPropName,
+                          CFXJSE_Value* pValue,
+                          bool bGetter);
+  bool RunVariablesScript(CXFA_Node* pScriptNode);
+
+  UnownedPtr<CJS_Runtime> const m_pSubordinateRuntime;
+  UnownedPtr<CXFA_Document> const m_pDocument;
+  std::unique_ptr<CFXJSE_Context> m_JsContext;
+  UnownedPtr<CFXJSE_Class> m_pJsClass;
+  CXFA_Script::Type m_eScriptType = CXFA_Script::Type::Unknown;
+  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Value>> m_mapObjectToValue;
+  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Context>>
+      m_mapVariableToContext;
+  UnownedPtr<CXFA_EventParam> m_eventParam;
+  std::vector<CXFA_Node*> m_upObjectArray;
+  // CacheList holds the List items so we can clean them up when we're done.
+  std::vector<std::unique_ptr<CXFA_List>> m_CacheList;
+  UnownedPtr<std::vector<CXFA_Node*>> m_pScriptNodeArray;
+  std::unique_ptr<CFXJSE_ResolveProcessor> const m_ResolveProcessor;
+  std::unique_ptr<CFXJSE_FormCalcContext> m_FM2JSContext;
+  UnownedPtr<CXFA_Object> m_pThisObject;
+  XFA_AttributeValue m_eRunAtType = XFA_AttributeValue::Client;
+};
+
+#endif  //  FXJS_XFA_CFXJSE_ENGINE_H_
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
new file mode 100644
index 0000000..7a6796c
--- /dev/null
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -0,0 +1,5891 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_formcalc_context.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+#include <utility>
+
+#include "core/fxcrt/cfx_widetextbuf.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_random.h"
+#include "fxjs/xfa/cfxjse_arguments.h"
+#include "fxjs/xfa/cfxjse_class.h"
+#include "fxjs/xfa/cfxjse_context.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fgas/crt/cfgas_decimal.h"
+#include "xfa/fgas/crt/locale_iface.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
+#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_localevalue.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+using pdfium::fxjse::kClassTag;
+using pdfium::fxjse::kFuncTag;
+
+namespace {
+
+const double kFinancialPrecision = 0.00000001;
+
+const wchar_t kStrCode[] = L"0123456789abcdef";
+
+struct XFA_FMHtmlReserveCode {
+  uint32_t m_uCode;
+  const char* m_htmlReserve;
+};
+
+// Sorted by |m_htmlReserve|.
+const XFA_FMHtmlReserveCode kReservesForDecode[] = {
+    {198, "AElig"},   {193, "Aacute"},   {194, "Acirc"},    {192, "Agrave"},
+    {913, "Alpha"},   {197, "Aring"},    {195, "Atilde"},   {196, "Auml"},
+    {914, "Beta"},    {199, "Ccedil"},   {935, "Chi"},      {8225, "Dagger"},
+    {916, "Delta"},   {208, "ETH"},      {201, "Eacute"},   {202, "Ecirc"},
+    {200, "Egrave"},  {917, "Epsilon"},  {919, "Eta"},      {203, "Euml"},
+    {915, "Gamma"},   {922, "Kappa"},    {923, "Lambda"},   {924, "Mu"},
+    {209, "Ntilde"},  {925, "Nu"},       {338, "OElig"},    {211, "Oacute"},
+    {212, "Ocirc"},   {210, "Ograve"},   {937, "Omega"},    {927, "Omicron"},
+    {216, "Oslash"},  {213, "Otilde"},   {214, "Ouml"},     {934, "Phi"},
+    {928, "Pi"},      {936, "Psi"},      {929, "Rho"},      {352, "Scaron"},
+    {931, "Sigma"},   {222, "THORN"},    {932, "Tau"},      {920, "Theta"},
+    {218, "Uacute"},  {219, "Ucirc"},    {217, "Ugrave"},   {933, "Upsilon"},
+    {220, "Uuml"},    {926, "Xi"},       {221, "Yacute"},   {376, "Yuml"},
+    {918, "Zeta"},    {225, "aacute"},   {226, "acirc"},    {180, "acute"},
+    {230, "aelig"},   {224, "agrave"},   {8501, "alefsym"}, {945, "alpha"},
+    {38, "amp"},      {8743, "and"},     {8736, "ang"},     {39, "apos"},
+    {229, "aring"},   {8776, "asymp"},   {227, "atilde"},   {228, "auml"},
+    {8222, "bdquo"},  {946, "beta"},     {166, "brvbar"},   {8226, "bull"},
+    {8745, "cap"},    {231, "ccedil"},   {184, "cedil"},    {162, "cent"},
+    {967, "chi"},     {710, "circ"},     {9827, "clubs"},   {8773, "cong"},
+    {169, "copy"},    {8629, "crarr"},   {8746, "cup"},     {164, "current"},
+    {8659, "dArr"},   {8224, "dagger"},  {8595, "darr"},    {176, "deg"},
+    {948, "delta"},   {9830, "diams"},   {247, "divide"},   {233, "eacute"},
+    {234, "ecirc"},   {232, "egrave"},   {8709, "empty"},   {8195, "emsp"},
+    {8194, "ensp"},   {949, "epsilon"},  {8801, "equiv"},   {951, "eta"},
+    {240, "eth"},     {235, "euml"},     {8364, "euro"},    {8707, "exist"},
+    {402, "fnof"},    {8704, "forall"},  {189, "frac12"},   {188, "frac14"},
+    {190, "frac34"},  {8260, "frasl"},   {947, "gamma"},    {8805, "ge"},
+    {62, "gt"},       {8660, "hArr"},    {8596, "harr"},    {9829, "hearts"},
+    {8230, "hellip"}, {237, "iacute"},   {238, "icirc"},    {161, "iexcl"},
+    {236, "igrave"},  {8465, "image"},   {8734, "infin"},   {8747, "int"},
+    {953, "iota"},    {191, "iquest"},   {8712, "isin"},    {239, "iuml"},
+    {954, "kappa"},   {8656, "lArr"},    {205, "lacute"},   {955, "lambda"},
+    {9001, "lang"},   {171, "laquo"},    {8592, "larr"},    {8968, "lceil"},
+    {206, "lcirc"},   {8220, "ldquo"},   {8804, "le"},      {8970, "lfloor"},
+    {204, "lgrave"},  {921, "lota"},     {8727, "lowast"},  {9674, "loz"},
+    {8206, "lrm"},    {8249, "lsaquo"},  {8216, "lsquo"},   {60, "lt"},
+    {207, "luml"},    {175, "macr"},     {8212, "mdash"},   {181, "micro"},
+    {183, "middot"},  {8722, "minus"},   {956, "mu"},       {8711, "nabla"},
+    {160, "nbsp"},    {8211, "ndash"},   {8800, "ne"},      {8715, "ni"},
+    {172, "not"},     {8713, "notin"},   {8836, "nsub"},    {241, "ntilde"},
+    {957, "nu"},      {243, "oacute"},   {244, "ocirc"},    {339, "oelig"},
+    {242, "ograve"},  {8254, "oline"},   {969, "omega"},    {959, "omicron"},
+    {8853, "oplus"},  {8744, "or"},      {170, "ordf"},     {186, "ordm"},
+    {248, "oslash"},  {245, "otilde"},   {8855, "otimes"},  {246, "ouml"},
+    {182, "para"},    {8706, "part"},    {8240, "permil"},  {8869, "perp"},
+    {966, "phi"},     {960, "pi"},       {982, "piv"},      {177, "plusmn"},
+    {8242, "prime"},  {8719, "prod"},    {8733, "prop"},    {968, "psi"},
+    {163, "pund"},    {34, "quot"},      {8658, "rArr"},    {8730, "radic"},
+    {9002, "rang"},   {187, "raquo"},    {8594, "rarr"},    {8969, "rceil"},
+    {8476, "real"},   {174, "reg"},      {8971, "rfloor"},  {961, "rho"},
+    {8207, "rlm"},    {8250, "rsaquo"},  {8217, "rsquo"},   {353, "saron"},
+    {8218, "sbquo"},  {8901, "sdot"},    {167, "sect"},     {173, "shy"},
+    {963, "sigma"},   {962, "sigmaf"},   {8764, "sim"},     {9824, "spades"},
+    {8834, "sub"},    {8838, "sube"},    {8721, "sum"},     {8835, "sup"},
+    {185, "sup1"},    {178, "sup2"},     {179, "sup3"},     {8839, "supe"},
+    {223, "szlig"},   {964, "tau"},      {8221, "tdquo"},   {8756, "there4"},
+    {952, "theta"},   {977, "thetasym"}, {8201, "thinsp"},  {254, "thorn"},
+    {732, "tilde"},   {215, "times"},    {8482, "trade"},   {8657, "uArr"},
+    {250, "uacute"},  {8593, "uarr"},    {251, "ucirc"},    {249, "ugrave"},
+    {168, "uml"},     {978, "upsih"},    {965, "upsilon"},  {252, "uuml"},
+    {8472, "weierp"}, {958, "xi"},       {253, "yacute"},   {165, "yen"},
+    {255, "yuml"},    {950, "zeta"},     {8205, "zwj"},     {8204, "zwnj"},
+};
+
+// Sorted by |m_uCode|.
+const XFA_FMHtmlReserveCode kReservesForEncode[] = {
+    {34, "quot"},     {38, "amp"},      {39, "apos"},      {60, "lt"},
+    {62, "gt"},       {160, "nbsp"},    {161, "iexcl"},    {162, "cent"},
+    {163, "pund"},    {164, "current"}, {165, "yen"},      {166, "brvbar"},
+    {167, "sect"},    {168, "uml"},     {169, "copy"},     {170, "ordf"},
+    {171, "laquo"},   {172, "not"},     {173, "shy"},      {174, "reg"},
+    {175, "macr"},    {176, "deg"},     {177, "plusmn"},   {178, "sup2"},
+    {179, "sup3"},    {180, "acute"},   {181, "micro"},    {182, "para"},
+    {183, "middot"},  {184, "cedil"},   {185, "sup1"},     {186, "ordm"},
+    {187, "raquo"},   {188, "frac14"},  {189, "frac12"},   {190, "frac34"},
+    {191, "iquest"},  {192, "Agrave"},  {193, "Aacute"},   {194, "Acirc"},
+    {195, "Atilde"},  {196, "Auml"},    {197, "Aring"},    {198, "AElig"},
+    {199, "Ccedil"},  {200, "Egrave"},  {201, "Eacute"},   {202, "Ecirc"},
+    {203, "Euml"},    {204, "lgrave"},  {205, "lacute"},   {206, "lcirc"},
+    {207, "luml"},    {208, "ETH"},     {209, "Ntilde"},   {210, "Ograve"},
+    {211, "Oacute"},  {212, "Ocirc"},   {213, "Otilde"},   {214, "Ouml"},
+    {215, "times"},   {216, "Oslash"},  {217, "Ugrave"},   {218, "Uacute"},
+    {219, "Ucirc"},   {220, "Uuml"},    {221, "Yacute"},   {222, "THORN"},
+    {223, "szlig"},   {224, "agrave"},  {225, "aacute"},   {226, "acirc"},
+    {227, "atilde"},  {228, "auml"},    {229, "aring"},    {230, "aelig"},
+    {231, "ccedil"},  {232, "egrave"},  {233, "eacute"},   {234, "ecirc"},
+    {235, "euml"},    {236, "igrave"},  {237, "iacute"},   {238, "icirc"},
+    {239, "iuml"},    {240, "eth"},     {241, "ntilde"},   {242, "ograve"},
+    {243, "oacute"},  {244, "ocirc"},   {245, "otilde"},   {246, "ouml"},
+    {247, "divide"},  {248, "oslash"},  {249, "ugrave"},   {250, "uacute"},
+    {251, "ucirc"},   {252, "uuml"},    {253, "yacute"},   {254, "thorn"},
+    {255, "yuml"},    {338, "OElig"},   {339, "oelig"},    {352, "Scaron"},
+    {353, "saron"},   {376, "Yuml"},    {402, "fnof"},     {710, "circ"},
+    {732, "tilde"},   {913, "Alpha"},   {914, "Beta"},     {915, "Gamma"},
+    {916, "Delta"},   {917, "Epsilon"}, {918, "Zeta"},     {919, "Eta"},
+    {920, "Theta"},   {921, "lota"},    {922, "Kappa"},    {923, "Lambda"},
+    {924, "Mu"},      {925, "Nu"},      {926, "Xi"},       {927, "Omicron"},
+    {928, "Pi"},      {929, "Rho"},     {931, "Sigma"},    {932, "Tau"},
+    {933, "Upsilon"}, {934, "Phi"},     {935, "Chi"},      {936, "Psi"},
+    {937, "Omega"},   {945, "alpha"},   {946, "beta"},     {947, "gamma"},
+    {948, "delta"},   {949, "epsilon"}, {950, "zeta"},     {951, "eta"},
+    {952, "theta"},   {953, "iota"},    {954, "kappa"},    {955, "lambda"},
+    {956, "mu"},      {957, "nu"},      {958, "xi"},       {959, "omicron"},
+    {960, "pi"},      {961, "rho"},     {962, "sigmaf"},   {963, "sigma"},
+    {964, "tau"},     {965, "upsilon"}, {966, "phi"},      {967, "chi"},
+    {968, "psi"},     {969, "omega"},   {977, "thetasym"}, {978, "upsih"},
+    {982, "piv"},     {8194, "ensp"},   {8195, "emsp"},    {8201, "thinsp"},
+    {8204, "zwnj"},   {8205, "zwj"},    {8206, "lrm"},     {8207, "rlm"},
+    {8211, "ndash"},  {8212, "mdash"},  {8216, "lsquo"},   {8217, "rsquo"},
+    {8218, "sbquo"},  {8220, "ldquo"},  {8221, "tdquo"},   {8222, "bdquo"},
+    {8224, "dagger"}, {8225, "Dagger"}, {8226, "bull"},    {8230, "hellip"},
+    {8240, "permil"}, {8242, "prime"},  {8249, "lsaquo"},  {8250, "rsaquo"},
+    {8254, "oline"},  {8260, "frasl"},  {8364, "euro"},    {8465, "image"},
+    {8472, "weierp"}, {8476, "real"},   {8482, "trade"},   {8501, "alefsym"},
+    {8592, "larr"},   {8593, "uarr"},   {8594, "rarr"},    {8595, "darr"},
+    {8596, "harr"},   {8629, "crarr"},  {8656, "lArr"},    {8657, "uArr"},
+    {8658, "rArr"},   {8659, "dArr"},   {8660, "hArr"},    {8704, "forall"},
+    {8706, "part"},   {8707, "exist"},  {8709, "empty"},   {8711, "nabla"},
+    {8712, "isin"},   {8713, "notin"},  {8715, "ni"},      {8719, "prod"},
+    {8721, "sum"},    {8722, "minus"},  {8727, "lowast"},  {8730, "radic"},
+    {8733, "prop"},   {8734, "infin"},  {8736, "ang"},     {8743, "and"},
+    {8744, "or"},     {8745, "cap"},    {8746, "cup"},     {8747, "int"},
+    {8756, "there4"}, {8764, "sim"},    {8773, "cong"},    {8776, "asymp"},
+    {8800, "ne"},     {8801, "equiv"},  {8804, "le"},      {8805, "ge"},
+    {8834, "sub"},    {8835, "sup"},    {8836, "nsub"},    {8838, "sube"},
+    {8839, "supe"},   {8853, "oplus"},  {8855, "otimes"},  {8869, "perp"},
+    {8901, "sdot"},   {8968, "lceil"},  {8969, "rceil"},   {8970, "lfloor"},
+    {8971, "rfloor"}, {9001, "lang"},   {9002, "rang"},    {9674, "loz"},
+    {9824, "spades"}, {9827, "clubs"},  {9829, "hearts"},  {9830, "diams"},
+};
+
+const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFM2JSFunctions[] = {
+    {kFuncTag, "Abs", CFXJSE_FormCalcContext::Abs},
+    {kFuncTag, "Avg", CFXJSE_FormCalcContext::Avg},
+    {kFuncTag, "Ceil", CFXJSE_FormCalcContext::Ceil},
+    {kFuncTag, "Count", CFXJSE_FormCalcContext::Count},
+    {kFuncTag, "Floor", CFXJSE_FormCalcContext::Floor},
+    {kFuncTag, "Max", CFXJSE_FormCalcContext::Max},
+    {kFuncTag, "Min", CFXJSE_FormCalcContext::Min},
+    {kFuncTag, "Mod", CFXJSE_FormCalcContext::Mod},
+    {kFuncTag, "Round", CFXJSE_FormCalcContext::Round},
+    {kFuncTag, "Sum", CFXJSE_FormCalcContext::Sum},
+    {kFuncTag, "Date", CFXJSE_FormCalcContext::Date},
+    {kFuncTag, "Date2Num", CFXJSE_FormCalcContext::Date2Num},
+    {kFuncTag, "DateFmt", CFXJSE_FormCalcContext::DateFmt},
+    {kFuncTag, "IsoDate2Num", CFXJSE_FormCalcContext::IsoDate2Num},
+    {kFuncTag, "IsoTime2Num", CFXJSE_FormCalcContext::IsoTime2Num},
+    {kFuncTag, "LocalDateFmt", CFXJSE_FormCalcContext::LocalDateFmt},
+    {kFuncTag, "LocalTimeFmt", CFXJSE_FormCalcContext::LocalTimeFmt},
+    {kFuncTag, "Num2Date", CFXJSE_FormCalcContext::Num2Date},
+    {kFuncTag, "Num2GMTime", CFXJSE_FormCalcContext::Num2GMTime},
+    {kFuncTag, "Num2Time", CFXJSE_FormCalcContext::Num2Time},
+    {kFuncTag, "Time", CFXJSE_FormCalcContext::Time},
+    {kFuncTag, "Time2Num", CFXJSE_FormCalcContext::Time2Num},
+    {kFuncTag, "TimeFmt", CFXJSE_FormCalcContext::TimeFmt},
+    {kFuncTag, "Apr", CFXJSE_FormCalcContext::Apr},
+    {kFuncTag, "Cterm", CFXJSE_FormCalcContext::CTerm},
+    {kFuncTag, "FV", CFXJSE_FormCalcContext::FV},
+    {kFuncTag, "Ipmt", CFXJSE_FormCalcContext::IPmt},
+    {kFuncTag, "NPV", CFXJSE_FormCalcContext::NPV},
+    {kFuncTag, "Pmt", CFXJSE_FormCalcContext::Pmt},
+    {kFuncTag, "PPmt", CFXJSE_FormCalcContext::PPmt},
+    {kFuncTag, "PV", CFXJSE_FormCalcContext::PV},
+    {kFuncTag, "Rate", CFXJSE_FormCalcContext::Rate},
+    {kFuncTag, "Term", CFXJSE_FormCalcContext::Term},
+    {kFuncTag, "Choose", CFXJSE_FormCalcContext::Choose},
+    {kFuncTag, "Exists", CFXJSE_FormCalcContext::Exists},
+    {kFuncTag, "HasValue", CFXJSE_FormCalcContext::HasValue},
+    {kFuncTag, "Oneof", CFXJSE_FormCalcContext::Oneof},
+    {kFuncTag, "Within", CFXJSE_FormCalcContext::Within},
+    {kFuncTag, "If", CFXJSE_FormCalcContext::If},
+    {kFuncTag, "Eval", CFXJSE_FormCalcContext::Eval},
+    {kFuncTag, "Translate", CFXJSE_FormCalcContext::eval_translation},
+    {kFuncTag, "Ref", CFXJSE_FormCalcContext::Ref},
+    {kFuncTag, "UnitType", CFXJSE_FormCalcContext::UnitType},
+    {kFuncTag, "UnitValue", CFXJSE_FormCalcContext::UnitValue},
+    {kFuncTag, "At", CFXJSE_FormCalcContext::At},
+    {kFuncTag, "Concat", CFXJSE_FormCalcContext::Concat},
+    {kFuncTag, "Decode", CFXJSE_FormCalcContext::Decode},
+    {kFuncTag, "Encode", CFXJSE_FormCalcContext::Encode},
+    {kFuncTag, "Format", CFXJSE_FormCalcContext::Format},
+    {kFuncTag, "Left", CFXJSE_FormCalcContext::Left},
+    {kFuncTag, "Len", CFXJSE_FormCalcContext::Len},
+    {kFuncTag, "Lower", CFXJSE_FormCalcContext::Lower},
+    {kFuncTag, "Ltrim", CFXJSE_FormCalcContext::Ltrim},
+    {kFuncTag, "Parse", CFXJSE_FormCalcContext::Parse},
+    {kFuncTag, "Replace", CFXJSE_FormCalcContext::Replace},
+    {kFuncTag, "Right", CFXJSE_FormCalcContext::Right},
+    {kFuncTag, "Rtrim", CFXJSE_FormCalcContext::Rtrim},
+    {kFuncTag, "Space", CFXJSE_FormCalcContext::Space},
+    {kFuncTag, "Str", CFXJSE_FormCalcContext::Str},
+    {kFuncTag, "Stuff", CFXJSE_FormCalcContext::Stuff},
+    {kFuncTag, "Substr", CFXJSE_FormCalcContext::Substr},
+    {kFuncTag, "Uuid", CFXJSE_FormCalcContext::Uuid},
+    {kFuncTag, "Upper", CFXJSE_FormCalcContext::Upper},
+    {kFuncTag, "WordNum", CFXJSE_FormCalcContext::WordNum},
+    {kFuncTag, "Get", CFXJSE_FormCalcContext::Get},
+    {kFuncTag, "Post", CFXJSE_FormCalcContext::Post},
+    {kFuncTag, "Put", CFXJSE_FormCalcContext::Put},
+    {kFuncTag, "pos_op", CFXJSE_FormCalcContext::positive_operator},
+    {kFuncTag, "neg_op", CFXJSE_FormCalcContext::negative_operator},
+    {kFuncTag, "log_or_op", CFXJSE_FormCalcContext::logical_or_operator},
+    {kFuncTag, "log_and_op", CFXJSE_FormCalcContext::logical_and_operator},
+    {kFuncTag, "log_not_op", CFXJSE_FormCalcContext::logical_not_operator},
+    {kFuncTag, "eq_op", CFXJSE_FormCalcContext::equality_operator},
+    {kFuncTag, "neq_op", CFXJSE_FormCalcContext::notequality_operator},
+    {kFuncTag, "lt_op", CFXJSE_FormCalcContext::less_operator},
+    {kFuncTag, "le_op", CFXJSE_FormCalcContext::lessequal_operator},
+    {kFuncTag, "gt_op", CFXJSE_FormCalcContext::greater_operator},
+    {kFuncTag, "ge_op", CFXJSE_FormCalcContext::greaterequal_operator},
+    {kFuncTag, "plus_op", CFXJSE_FormCalcContext::plus_operator},
+    {kFuncTag, "minus_op", CFXJSE_FormCalcContext::minus_operator},
+    {kFuncTag, "mul_op", CFXJSE_FormCalcContext::multiple_operator},
+    {kFuncTag, "div_op", CFXJSE_FormCalcContext::divide_operator},
+    {kFuncTag, "asgn_val_op", CFXJSE_FormCalcContext::assign_value_operator},
+    {kFuncTag, "dot_acc", CFXJSE_FormCalcContext::dot_accessor},
+    {kFuncTag, "dotdot_acc", CFXJSE_FormCalcContext::dotdot_accessor},
+    {kFuncTag, "concat_obj", CFXJSE_FormCalcContext::concat_fm_object},
+    {kFuncTag, "is_obj", CFXJSE_FormCalcContext::is_fm_object},
+    {kFuncTag, "is_ary", CFXJSE_FormCalcContext::is_fm_array},
+    {kFuncTag, "get_val", CFXJSE_FormCalcContext::get_fm_value},
+    {kFuncTag, "get_jsobj", CFXJSE_FormCalcContext::get_fm_jsobj},
+    {kFuncTag, "var_filter", CFXJSE_FormCalcContext::fm_var_filter},
+};
+
+const uint8_t kAltTableDate[] = {
+    255, 255, 255, 3,   9,   255, 255, 255, 255, 255, 255,
+    255, 2,   255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 1,   255, 255, 255, 255, 255, 255, 255, 255,
+};
+static_assert(FX_ArraySize(kAltTableDate) == L'a' - L'A' + 1,
+              "Invalid kAltTableDate size.");
+
+const uint8_t kAltTableTime[] = {
+    14,  255, 255, 3,   9,   255, 255, 15,  255, 255, 255,
+    255, 6,   255, 255, 255, 255, 255, 7,   255, 255, 255,
+    255, 255, 1,   17,  255, 255, 255, 255, 255, 255, 255,
+};
+static_assert(FX_ArraySize(kAltTableTime) == L'a' - L'A' + 1,
+              "Invalid kAltTableTime size.");
+
+void AlternateDateTimeSymbols(WideString* pPattern,
+                              const WideString& wsAltSymbols,
+                              bool bIsDate) {
+  const uint8_t* pAltTable = bIsDate ? kAltTableDate : kAltTableTime;
+  int32_t nLength = pPattern->GetLength();
+  bool bInConstRange = false;
+  bool bEscape = false;
+  int32_t i = 0;
+  while (i < nLength) {
+    wchar_t wc = (*pPattern)[i];
+    if (wc == L'\'') {
+      bInConstRange = !bInConstRange;
+      if (bEscape) {
+        i++;
+      } else {
+        pPattern->Delete(i);
+        nLength--;
+      }
+      bEscape = !bEscape;
+      continue;
+    }
+    if (!bInConstRange && wc >= L'A' && wc <= L'a') {
+      uint8_t nAlt = pAltTable[wc - L'A'];
+      if (nAlt != 255)
+        pPattern->SetAt(i, wsAltSymbols[nAlt]);
+    }
+    i++;
+    bEscape = false;
+  }
+}
+
+std::pair<bool, uint32_t> PatternStringType(ByteStringView bsPattern) {
+  WideString wsPattern = WideString::FromUTF8(bsPattern);
+  if (L"datetime" == wsPattern.First(8))
+    return {true, XFA_VT_DATETIME};
+  if (L"date" == wsPattern.First(4)) {
+    auto pos = wsPattern.Find(L"time");
+    uint32_t type =
+        pos.has_value() && pos.value() != 0 ? XFA_VT_DATETIME : XFA_VT_DATE;
+    return {true, type};
+  }
+  if (L"time" == wsPattern.First(4))
+    return {true, XFA_VT_TIME};
+  if (L"text" == wsPattern.First(4))
+    return {true, XFA_VT_TEXT};
+  if (L"num" == wsPattern.First(3)) {
+    uint32_t type;
+    if (L"integer" == wsPattern.Substr(4, 7)) {
+      type = XFA_VT_INTEGER;
+    } else if (L"decimal" == wsPattern.Substr(4, 7)) {
+      type = XFA_VT_DECIMAL;
+    } else if (L"currency" == wsPattern.Substr(4, 8)) {
+      type = XFA_VT_FLOAT;
+    } else if (L"percent" == wsPattern.Substr(4, 7)) {
+      type = XFA_VT_FLOAT;
+    } else {
+      type = XFA_VT_FLOAT;
+    }
+    return {true, type};
+  }
+
+  uint32_t type = XFA_VT_NULL;
+  wsPattern.MakeLower();
+  const wchar_t* pData = wsPattern.c_str();
+  int32_t iLength = wsPattern.GetLength();
+  int32_t iIndex = 0;
+  bool bSingleQuotation = false;
+  while (iIndex < iLength) {
+    wchar_t wsPatternChar = pData[iIndex];
+    if (wsPatternChar == 0x27) {
+      bSingleQuotation = !bSingleQuotation;
+      iIndex++;
+      continue;
+    }
+    if (bSingleQuotation) {
+      iIndex++;
+      continue;
+    }
+
+    if (wsPatternChar == 'h' || wsPatternChar == 'k')
+      return {false, XFA_VT_TIME};
+    if (wsPatternChar == 'x' || wsPatternChar == 'o' || wsPatternChar == '0')
+      return {false, XFA_VT_TEXT};
+    if (wsPatternChar == 'v' || wsPatternChar == '8' || wsPatternChar == '$')
+      return {false, XFA_VT_FLOAT};
+    if (wsPatternChar == 'y' || wsPatternChar == 'j') {
+      iIndex++;
+      wchar_t timePatternChar;
+      while (iIndex < iLength) {
+        timePatternChar = pData[iIndex];
+        if (timePatternChar == 0x27) {
+          bSingleQuotation = !bSingleQuotation;
+          iIndex++;
+          continue;
+        }
+        if (!bSingleQuotation && timePatternChar == 't')
+          return {false, XFA_VT_DATETIME};
+        iIndex++;
+      }
+      return {false, XFA_VT_DATE};
+    }
+
+    if (wsPatternChar == 'a') {
+      type = XFA_VT_TEXT;
+    } else if (wsPatternChar == 'z' || wsPatternChar == 's' ||
+               wsPatternChar == 'e' || wsPatternChar == ',' ||
+               wsPatternChar == '.') {
+      type = XFA_VT_FLOAT;
+    }
+    iIndex++;
+  }
+
+  if (type == XFA_VT_NULL)
+    type = XFA_VT_TEXT | XFA_VT_FLOAT;
+  return {false, type};
+}
+
+CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_Value* pValue) {
+  CFXJSE_HostObject* pHostObj = pValue->ToHostObject();
+  return pHostObj ? pHostObj->AsFormCalcContext() : nullptr;
+}
+
+LocaleIface* LocaleFromString(CXFA_Document* pDoc,
+                              CXFA_LocaleMgr* pMgr,
+                              ByteStringView bsLocale) {
+  if (!bsLocale.IsEmpty())
+    return pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale));
+
+  CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
+  return pThisNode->GetLocale();
+}
+
+WideString FormatFromString(LocaleIface* pLocale, ByteStringView bsFormat) {
+  if (!bsFormat.IsEmpty())
+    return WideString::FromUTF8(bsFormat);
+
+  return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
+}
+
+FX_LOCALEDATETIMESUBCATEGORY SubCategoryFromInt(int32_t iStyle) {
+  switch (iStyle) {
+    case 1:
+      return FX_LOCALEDATETIMESUBCATEGORY_Short;
+    case 3:
+      return FX_LOCALEDATETIMESUBCATEGORY_Long;
+    case 4:
+      return FX_LOCALEDATETIMESUBCATEGORY_Full;
+    case 0:
+    case 2:
+    default:
+      return FX_LOCALEDATETIMESUBCATEGORY_Medium;
+  }
+}
+
+ByteString GetLocalDateTimeFormat(CXFA_Document* pDoc,
+                                  int32_t iStyle,
+                                  ByteStringView bsLocale,
+                                  bool bStandard,
+                                  bool bIsDate) {
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  if (!pLocale)
+    return ByteString();
+
+  FX_LOCALEDATETIMESUBCATEGORY category = SubCategoryFromInt(iStyle);
+  WideString wsLocal = bIsDate ? pLocale->GetDatePattern(category)
+                               : pLocale->GetTimePattern(category);
+  if (!bStandard)
+    AlternateDateTimeSymbols(&wsLocal, pLocale->GetDateTimeSymbols(), bIsDate);
+  return wsLocal.ToUTF8();
+}
+
+bool IsWhitespace(char c) {
+  return c == 0x20 || c == 0x09 || c == 0x0B || c == 0x0C || c == 0x0A ||
+         c == 0x0D;
+}
+
+bool IsPartOfNumber(char ch) {
+  return std::isdigit(ch) || ch == '-' || ch == '.';
+}
+
+bool IsPartOfNumberW(wchar_t ch) {
+  return FXSYS_IsDecimalDigit(ch) || ch == L'-' || ch == L'.';
+}
+
+ByteString GUIDString(bool bSeparator) {
+  uint8_t data[16];
+  FX_Random_GenerateMT(reinterpret_cast<uint32_t*>(data), 4);
+  data[6] = (data[6] & 0x0F) | 0x40;
+
+  ByteString bsGUID;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<char> pBuf = bsGUID.GetBuffer(40);
+    size_t out_index = 0;
+    for (size_t i = 0; i < 16; ++i, out_index += 2) {
+      if (bSeparator && (i == 4 || i == 6 || i == 8 || i == 10))
+        pBuf[out_index++] = L'-';
+
+      FXSYS_IntToTwoHexChars(data[i], &pBuf[out_index]);
+    }
+  }
+  bsGUID.ReleaseBuffer(bSeparator ? 36 : 32);
+  return bsGUID;
+}
+
+bool IsIsoDateFormat(pdfium::span<const char> pData,
+                     int32_t* pStyle,
+                     int32_t* pYear,
+                     int32_t* pMonth,
+                     int32_t* pDay) {
+  int32_t& iStyle = *pStyle;
+  int32_t& iYear = *pYear;
+  int32_t& iMonth = *pMonth;
+  int32_t& iDay = *pDay;
+
+  iYear = 0;
+  iMonth = 1;
+  iDay = 1;
+
+  if (pData.size() < 4)
+    return false;
+
+  char szYear[5];
+  szYear[4] = '\0';
+  for (int32_t i = 0; i < 4; ++i) {
+    if (!std::isdigit(pData[i]))
+      return false;
+
+    szYear[i] = pData[i];
+  }
+  iYear = FXSYS_atoi(szYear);
+  iStyle = 0;
+  if (pData.size() == 4)
+    return true;
+
+  iStyle = pData[4] == '-' ? 1 : 0;
+
+  size_t iPosOff = iStyle == 0 ? 4 : 5;
+  if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
+    return false;
+
+  char szBuffer[3] = {};
+  szBuffer[0] = pData[iPosOff];
+  szBuffer[1] = pData[iPosOff + 1];
+  iMonth = FXSYS_atoi(szBuffer);
+  if (iMonth > 12 || iMonth < 1)
+    return false;
+
+  if (iStyle == 0) {
+    iPosOff += 2;
+    if (pData.size() == 6)
+      return true;
+  } else {
+    iPosOff += 3;
+    if (pData.size() == 7)
+      return true;
+  }
+  if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
+    return false;
+
+  szBuffer[0] = pData[iPosOff];
+  szBuffer[1] = pData[iPosOff + 1];
+  iDay = FXSYS_atoi(szBuffer);
+  if (iPosOff + 2 < pData.size())
+    return false;
+
+  if (iMonth == 2) {
+    bool bIsLeap = (!(iYear % 4) && (iYear % 100)) || !(iYear % 400);
+    return iDay <= (bIsLeap ? 29 : 28);
+  }
+
+  if (iMonth < 8)
+    return iDay <= (iMonth % 2 == 0 ? 30 : 31);
+  return iDay <= (iMonth % 2 == 0 ? 31 : 30);
+}
+
+bool IsIsoTimeFormat(pdfium::span<const char> pData,
+                     int32_t* pHour,
+                     int32_t* pMinute,
+                     int32_t* pSecond,
+                     int32_t* pMilliSecond,
+                     int32_t* pZoneHour,
+                     int32_t* pZoneMinute) {
+  int32_t& iHour = *pHour;
+  int32_t& iMinute = *pMinute;
+  int32_t& iSecond = *pSecond;
+  int32_t& iMilliSecond = *pMilliSecond;
+  int32_t& iZoneHour = *pZoneHour;
+  int32_t& iZoneMinute = *pZoneMinute;
+
+  iHour = 0;
+  iMinute = 0;
+  iSecond = 0;
+  iMilliSecond = 0;
+  iZoneHour = 0;
+  iZoneMinute = 0;
+
+  if (pData.empty())
+    return false;
+
+  size_t iZone = 0;
+  size_t i = 0;
+  while (i < pData.size()) {
+    if (!std::isdigit(pData[i]) && pData[i] != ':') {
+      iZone = i;
+      break;
+    }
+    ++i;
+  }
+  if (i == pData.size())
+    iZone = pData.size();
+
+  char szBuffer[3] = {};
+  size_t iPos = 0;
+  size_t iIndex = 0;
+  while (iIndex < iZone) {
+    if (!std::isdigit(pData[iIndex]))
+      return false;
+
+    szBuffer[0] = pData[iIndex];
+    if (!std::isdigit(pData[iIndex + 1]))
+      return false;
+
+    szBuffer[1] = pData[iIndex + 1];
+    if (FXSYS_atoi(szBuffer) > 60)
+      return false;
+
+    if (pData[2] == ':') {
+      if (iPos == 0) {
+        iHour = FXSYS_atoi(szBuffer);
+        ++iPos;
+      } else if (iPos == 1) {
+        iMinute = FXSYS_atoi(szBuffer);
+        ++iPos;
+      } else {
+        iSecond = FXSYS_atoi(szBuffer);
+      }
+      iIndex += 3;
+    } else {
+      if (iPos == 0) {
+        iHour = FXSYS_atoi(szBuffer);
+        ++iPos;
+      } else if (iPos == 1) {
+        iMinute = FXSYS_atoi(szBuffer);
+        ++iPos;
+      } else if (iPos == 2) {
+        iSecond = FXSYS_atoi(szBuffer);
+        ++iPos;
+      }
+      iIndex += 2;
+    }
+  }
+
+  if (iIndex < pData.size() && pData[iIndex] == '.') {
+    constexpr int kSubSecondLength = 3;
+    if (iIndex + kSubSecondLength >= pData.size())
+      return false;
+
+    ++iIndex;
+    char szMilliSeconds[kSubSecondLength + 1];
+    for (int j = 0; j < kSubSecondLength; ++j) {
+      char c = pData[iIndex + j];
+      if (!std::isdigit(c))
+        return false;
+      szMilliSeconds[j] = c;
+    }
+    szMilliSeconds[kSubSecondLength] = '\0';
+
+    iMilliSecond = FXSYS_atoi(szMilliSeconds);
+    if (iMilliSecond > 100) {
+      iMilliSecond = 0;
+      return false;
+    }
+    iIndex += kSubSecondLength;
+  }
+
+  if (iIndex < pData.size() && FXSYS_towlower(pData[iIndex]) == 'z')
+    return true;
+
+  int32_t iSign = 1;
+  if (iIndex < pData.size()) {
+    if (pData[iIndex] == '+') {
+      ++iIndex;
+    } else if (pData[iIndex] == '-') {
+      iSign = -1;
+      ++iIndex;
+    }
+  }
+  iPos = 0;
+  while (iIndex < pData.size()) {
+    if (!std::isdigit(pData[iIndex]))
+      return false;
+
+    szBuffer[0] = pData[iIndex];
+    if (!std::isdigit(pData[iIndex + 1]))
+      return false;
+
+    szBuffer[1] = pData[iIndex + 1];
+    if (FXSYS_atoi(szBuffer) > 60)
+      return false;
+
+    if (pData[2] == ':') {
+      if (iPos == 0) {
+        iZoneHour = FXSYS_atoi(szBuffer);
+      } else if (iPos == 1) {
+        iZoneMinute = FXSYS_atoi(szBuffer);
+      }
+      iIndex += 3;
+    } else {
+      if (!iPos) {
+        iZoneHour = FXSYS_atoi(szBuffer);
+        ++iPos;
+      } else if (iPos == 1) {
+        iZoneMinute = FXSYS_atoi(szBuffer);
+        ++iPos;
+      }
+      iIndex += 2;
+    }
+  }
+  if (iIndex < pData.size())
+    return false;
+
+  iZoneHour *= iSign;
+  return true;
+}
+
+bool IsIsoDateTimeFormat(pdfium::span<const char> pData,
+                         int32_t* pYear,
+                         int32_t* pMonth,
+                         int32_t* pDay,
+                         int32_t* pHour,
+                         int32_t* pMinute,
+                         int32_t* pSecond,
+                         int32_t* pMilliSecond,
+                         int32_t* pZoneHour,
+                         int32_t* pZoneMinute) {
+  int32_t& iYear = *pYear;
+  int32_t& iMonth = *pMonth;
+  int32_t& iDay = *pDay;
+  int32_t& iHour = *pHour;
+  int32_t& iMinute = *pMinute;
+  int32_t& iSecond = *pSecond;
+  int32_t& iMilliSecond = *pMilliSecond;
+  int32_t& iZoneHour = *pZoneHour;
+  int32_t& iZoneMinute = *pZoneMinute;
+
+  iYear = 0;
+  iMonth = 0;
+  iDay = 0;
+  iHour = 0;
+  iMinute = 0;
+  iSecond = 0;
+
+  if (pData.empty())
+    return false;
+
+  size_t iIndex = 0;
+  while (pData[iIndex] != 'T' && pData[iIndex] != 't') {
+    if (iIndex >= pData.size())
+      return false;
+    ++iIndex;
+  }
+  if (iIndex != 8 && iIndex != 10)
+    return false;
+
+  int32_t iStyle = -1;
+  if (!IsIsoDateFormat(pData.subspan(0, iIndex), &iStyle, &iYear, &iMonth,
+                       &iDay)) {
+    return false;
+  }
+  if (pData[iIndex] != 'T' && pData[iIndex] != 't')
+    return true;
+
+  return IsIsoTimeFormat(pData.subspan(iIndex + 1), &iHour, &iMinute, &iSecond,
+                         &iMilliSecond, &iZoneHour, &iZoneMinute);
+}
+
+int32_t DateString2Num(ByteStringView bsDate) {
+  int32_t iLength = bsDate.GetLength();
+  int32_t iYear = 0;
+  int32_t iMonth = 0;
+  int32_t iDay = 0;
+  if (iLength <= 10) {
+    int32_t iStyle = -1;
+    if (!IsIsoDateFormat(bsDate.span(), &iStyle, &iYear, &iMonth, &iDay))
+      return 0;
+  } else {
+    int32_t iHour = 0;
+    int32_t iMinute = 0;
+    int32_t iSecond = 0;
+    int32_t iMilliSecond = 0;
+    int32_t iZoneHour = 0;
+    int32_t iZoneMinute = 0;
+    if (!IsIsoDateTimeFormat(bsDate.span(), &iYear, &iMonth, &iDay, &iHour,
+                             &iMinute, &iSecond, &iMilliSecond, &iZoneHour,
+                             &iZoneMinute)) {
+      return 0;
+    }
+  }
+
+  float dDays = 0;
+  int32_t i = 1;
+  if (iYear < 1900)
+    return 0;
+
+  while (iYear - i >= 1900) {
+    dDays +=
+        ((!((iYear - i) % 4) && ((iYear - i) % 100)) || !((iYear - i) % 400))
+            ? 366
+            : 365;
+    ++i;
+  }
+  i = 1;
+  while (i < iMonth) {
+    if (i == 2)
+      dDays += ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) ? 29 : 28;
+    else if (i <= 7)
+      dDays += (i % 2 == 0) ? 30 : 31;
+    else
+      dDays += (i % 2 == 0) ? 31 : 30;
+
+    ++i;
+  }
+  i = 0;
+  while (iDay - i > 0) {
+    ++dDays;
+    ++i;
+  }
+  return (int32_t)dDays;
+}
+
+void GetLocalTimeZone(int32_t* pHour, int32_t* pMin, int32_t* pSec) {
+  time_t now;
+  FXSYS_time(&now);
+
+  struct tm* pGmt = gmtime(&now);
+  struct tm* pLocal = FXSYS_localtime(&now);
+  *pHour = pLocal->tm_hour - pGmt->tm_hour;
+  *pMin = pLocal->tm_min - pGmt->tm_min;
+  *pSec = pLocal->tm_sec - pGmt->tm_sec;
+}
+
+bool HTMLSTR2Code(const WideString& pData, uint32_t* iCode) {
+  auto cmpFunc = [](const XFA_FMHtmlReserveCode& iter, ByteStringView val) {
+    return strcmp(val.unterminated_c_str(), iter.m_htmlReserve) > 0;
+  };
+  if (!pData.IsASCII())
+    return false;
+  ByteString temp = pData.ToASCII();
+  const XFA_FMHtmlReserveCode* result = std::lower_bound(
+      std::begin(kReservesForDecode), std::end(kReservesForDecode),
+      temp.AsStringView(), cmpFunc);
+  if (result != std::end(kReservesForDecode) &&
+      !strcmp(temp.c_str(), result->m_htmlReserve)) {
+    *iCode = result->m_uCode;
+    return true;
+  }
+  return false;
+}
+
+bool HTMLCode2STR(uint32_t iCode, WideString* wsHTMLReserve) {
+  auto cmpFunc = [](const XFA_FMHtmlReserveCode iter, const uint32_t& val) {
+    return iter.m_uCode < val;
+  };
+  const XFA_FMHtmlReserveCode* result =
+      std::lower_bound(std::begin(kReservesForEncode),
+                       std::end(kReservesForEncode), iCode, cmpFunc);
+  if (result != std::end(kReservesForEncode) && result->m_uCode == iCode) {
+    *wsHTMLReserve = WideString::FromASCII(result->m_htmlReserve);
+    return true;
+  }
+  return false;
+}
+
+WideString DecodeURL(const WideString& wsURL) {
+  const wchar_t* pData = wsURL.c_str();
+  size_t iLen = wsURL.GetLength();
+  CFX_WideTextBuf wsResultBuf;
+  for (size_t i = 0; i < iLen; ++i) {
+    wchar_t ch = pData[i];
+    if ('%' != ch) {
+      wsResultBuf.AppendChar(ch);
+      continue;
+    }
+
+    wchar_t chTemp = 0;
+    int32_t iCount = 0;
+    while (iCount < 2) {
+      if (++i >= iLen)
+        break;
+      chTemp *= 16;
+      ch = pData[i];
+      if (!FXSYS_IsWideHexDigit(ch))
+        return WideString();
+      chTemp += FXSYS_WideHexCharToInt(ch);
+      ++iCount;
+    }
+    wsResultBuf.AppendChar(chTemp);
+  }
+  wsResultBuf.AppendChar(0);
+  return wsResultBuf.MakeString();
+}
+
+WideString DecodeMLInternal(const WideString& wsHTML, bool bIsHTML) {
+  const wchar_t* pData = wsHTML.c_str();
+  size_t iLen = wsHTML.GetLength();
+  CFX_WideTextBuf wsResultBuf;
+  for (size_t i = 0; i < iLen; ++i) {
+    wchar_t ch = pData[i];
+    if (ch != '&') {
+      wsResultBuf.AppendChar(ch);
+      continue;
+    }
+
+    if (++i >= iLen)
+      break;
+    ch = pData[i];
+    if (ch == '#') {
+      if (++i >= iLen)
+        break;
+      ch = pData[i];
+      if (ch != 'x' && ch != 'X')
+        return WideString();
+      if (++i >= iLen)
+        break;
+      ch = pData[i];
+      uint32_t iCode = 0;
+      while (ch != ';' && i < iLen) {
+        iCode *= 16;
+        if (!FXSYS_IsWideHexDigit(ch))
+          return WideString();
+        iCode += FXSYS_WideHexCharToInt(ch);
+        if (++i >= iLen)
+          break;
+        ch = pData[i];
+      }
+      wsResultBuf.AppendChar(iCode);
+      continue;
+    }
+
+    wchar_t szBuffer[9];
+    size_t iStrIndex = 0;
+    while (ch != ';' && i < iLen) {
+      if (iStrIndex < 8)
+        szBuffer[iStrIndex++] = ch;
+      if (++i >= iLen)
+        break;
+      ch = pData[i];
+    }
+    szBuffer[iStrIndex] = 0;
+    if (bIsHTML) {
+      uint32_t iData = 0;
+      if (HTMLSTR2Code(szBuffer, &iData))
+        wsResultBuf.AppendChar((wchar_t)iData);
+    } else {
+      if (wcscmp(szBuffer, L"quot") == 0)
+        wsResultBuf.AppendChar('"');
+      else if (wcscmp(szBuffer, L"amp") == 0)
+        wsResultBuf.AppendChar('&');
+      else if (wcscmp(szBuffer, L"apos") == 0)
+        wsResultBuf.AppendChar('\'');
+      else if (wcscmp(szBuffer, L"lt") == 0)
+        wsResultBuf.AppendChar('<');
+      else if (wcscmp(szBuffer, L"gt") == 0)
+        wsResultBuf.AppendChar('>');
+    }
+  }
+
+  wsResultBuf.AppendChar(0);
+  return wsResultBuf.MakeString();
+}
+
+WideString DecodeHTML(const WideString& wsHTML) {
+  return DecodeMLInternal(wsHTML, true);
+}
+
+WideString DecodeXML(const WideString& wsXML) {
+  return DecodeMLInternal(wsXML, false);
+}
+
+WideString EncodeURL(const ByteString& bsURL) {
+  static const wchar_t kStrUnsafe[] = {' ', '<',  '>', '"', '#', '%', '{', '}',
+                                       '|', '\\', '^', '~', '[', ']', '`'};
+  static const wchar_t kStrReserved[] = {';', '/', '?', ':', '@', '=', '&'};
+  static const wchar_t kStrSpecial[] = {'$',  '-', '+', '!', '*',
+                                        '\'', '(', ')', ','};
+
+  WideString wsURL = WideString::FromUTF8(bsURL.AsStringView());
+  CFX_WideTextBuf wsResultBuf;
+  wchar_t szEncode[4];
+  szEncode[0] = '%';
+  szEncode[3] = 0;
+  for (wchar_t ch : wsURL) {
+    size_t i = 0;
+    size_t iCount = FX_ArraySize(kStrUnsafe);
+    while (i < iCount) {
+      if (ch == kStrUnsafe[i]) {
+        int32_t iIndex = ch / 16;
+        szEncode[1] = kStrCode[iIndex];
+        szEncode[2] = kStrCode[ch - iIndex * 16];
+        wsResultBuf << szEncode;
+        break;
+      }
+      ++i;
+    }
+    if (i < iCount)
+      continue;
+
+    i = 0;
+    iCount = FX_ArraySize(kStrReserved);
+    while (i < iCount) {
+      if (ch == kStrReserved[i]) {
+        int32_t iIndex = ch / 16;
+        szEncode[1] = kStrCode[iIndex];
+        szEncode[2] = kStrCode[ch - iIndex * 16];
+        wsResultBuf << szEncode;
+        break;
+      }
+      ++i;
+    }
+    if (i < iCount)
+      continue;
+
+    i = 0;
+    iCount = FX_ArraySize(kStrSpecial);
+    while (i < iCount) {
+      if (ch == kStrSpecial[i]) {
+        wsResultBuf.AppendChar(ch);
+        break;
+      }
+      ++i;
+    }
+    if (i < iCount)
+      continue;
+
+    if ((ch >= 0x80 && ch <= 0xff) || ch <= 0x1f || ch == 0x7f) {
+      int32_t iIndex = ch / 16;
+      szEncode[1] = kStrCode[iIndex];
+      szEncode[2] = kStrCode[ch - iIndex * 16];
+      wsResultBuf << szEncode;
+    } else if (ch >= 0x20 && ch <= 0x7e) {
+      wsResultBuf.AppendChar(ch);
+    } else {
+      const wchar_t iRadix = 16;
+      WideString wsBuffer;
+      while (ch >= iRadix) {
+        wchar_t tmp = kStrCode[ch % iRadix];
+        ch /= iRadix;
+        wsBuffer += tmp;
+      }
+      wsBuffer += kStrCode[ch];
+      int32_t iLen = wsBuffer.GetLength();
+      if (iLen < 2)
+        break;
+
+      int32_t iIndex = 0;
+      if (iLen % 2 != 0) {
+        szEncode[1] = '0';
+        szEncode[2] = wsBuffer[iLen - 1];
+        iIndex = iLen - 2;
+      } else {
+        szEncode[1] = wsBuffer[iLen - 1];
+        szEncode[2] = wsBuffer[iLen - 2];
+        iIndex = iLen - 3;
+      }
+      wsResultBuf << szEncode;
+      while (iIndex > 0) {
+        szEncode[1] = wsBuffer[iIndex];
+        szEncode[2] = wsBuffer[iIndex - 1];
+        iIndex -= 2;
+        wsResultBuf << szEncode;
+      }
+    }
+  }
+  wsResultBuf.AppendChar(0);
+  return wsResultBuf.MakeString();
+}
+
+WideString EncodeHTML(const ByteString& bsHTML) {
+  WideString wsHTML = WideString::FromUTF8(bsHTML.AsStringView());
+  wchar_t szEncode[9];
+  szEncode[0] = '&';
+  szEncode[1] = '#';
+  szEncode[2] = 'x';
+  CFX_WideTextBuf wsResultBuf;
+  for (uint32_t ch : wsHTML) {
+    WideString htmlReserve;
+    if (HTMLCode2STR(ch, &htmlReserve)) {
+      wsResultBuf.AppendChar(L'&');
+      wsResultBuf << htmlReserve;
+      wsResultBuf.AppendChar(L';');
+    } else if (ch >= 32 && ch <= 126) {
+      wsResultBuf.AppendChar(static_cast<wchar_t>(ch));
+    } else if (ch < 256) {
+      int32_t iIndex = ch / 16;
+      szEncode[3] = kStrCode[iIndex];
+      szEncode[4] = kStrCode[ch - iIndex * 16];
+      szEncode[5] = ';';
+      szEncode[6] = 0;
+      wsResultBuf << szEncode;
+    } else if (ch < 65536) {
+      int32_t iBigByte = ch / 256;
+      int32_t iLittleByte = ch % 256;
+      szEncode[3] = kStrCode[iBigByte / 16];
+      szEncode[4] = kStrCode[iBigByte % 16];
+      szEncode[5] = kStrCode[iLittleByte / 16];
+      szEncode[6] = kStrCode[iLittleByte % 16];
+      szEncode[7] = ';';
+      szEncode[8] = 0;
+      wsResultBuf << szEncode;
+    } else {
+      // TODO(tsepez): Handle codepoint not in BMP.
+    }
+  }
+  wsResultBuf.AppendChar(0);
+  return wsResultBuf.MakeString();
+}
+
+WideString EncodeXML(const ByteString& bsXML) {
+  WideString wsXML = WideString::FromUTF8(bsXML.AsStringView());
+  CFX_WideTextBuf wsResultBuf;
+  wchar_t szEncode[9];
+  szEncode[0] = '&';
+  szEncode[1] = '#';
+  szEncode[2] = 'x';
+  for (uint32_t ch : wsXML) {
+    switch (ch) {
+      case '"':
+        wsResultBuf.AppendChar('&');
+        wsResultBuf << WideStringView(L"quot");
+        wsResultBuf.AppendChar(';');
+        break;
+      case '&':
+        wsResultBuf.AppendChar('&');
+        wsResultBuf << WideStringView(L"amp");
+        wsResultBuf.AppendChar(';');
+        break;
+      case '\'':
+        wsResultBuf.AppendChar('&');
+        wsResultBuf << WideStringView(L"apos");
+        wsResultBuf.AppendChar(';');
+        break;
+      case '<':
+        wsResultBuf.AppendChar('&');
+        wsResultBuf << WideStringView(L"lt");
+        wsResultBuf.AppendChar(';');
+        break;
+      case '>':
+        wsResultBuf.AppendChar('&');
+        wsResultBuf << WideStringView(L"gt");
+        wsResultBuf.AppendChar(';');
+        break;
+      default: {
+        if (ch >= 32 && ch <= 126) {
+          wsResultBuf.AppendChar(static_cast<wchar_t>(ch));
+        } else if (ch < 256) {
+          int32_t iIndex = ch / 16;
+          szEncode[3] = kStrCode[iIndex];
+          szEncode[4] = kStrCode[ch - iIndex * 16];
+          szEncode[5] = ';';
+          szEncode[6] = 0;
+          wsResultBuf << szEncode;
+        } else if (ch < 65536) {
+          int32_t iBigByte = ch / 256;
+          int32_t iLittleByte = ch % 256;
+          szEncode[3] = kStrCode[iBigByte / 16];
+          szEncode[4] = kStrCode[iBigByte % 16];
+          szEncode[5] = kStrCode[iLittleByte / 16];
+          szEncode[6] = kStrCode[iLittleByte % 16];
+          szEncode[7] = ';';
+          szEncode[8] = 0;
+          wsResultBuf << szEncode;
+        } else {
+          // TODO(tsepez): Handle codepoint not in BMP.
+        }
+        break;
+      }
+    }
+  }
+  wsResultBuf.AppendChar(0);
+  return wsResultBuf.MakeString();
+}
+
+ByteString TrillionUS(ByteStringView bsData) {
+  static const ByteStringView pUnits[] = {"zero",  "one",  "two", "three",
+                                          "four",  "five", "six", "seven",
+                                          "eight", "nine"};
+  static const ByteStringView pCapUnits[] = {"Zero",  "One",  "Two", "Three",
+                                             "Four",  "Five", "Six", "Seven",
+                                             "Eight", "Nine"};
+  static const ByteStringView pTens[] = {
+      "Ten",     "Eleven",  "Twelve",    "Thirteen", "Fourteen",
+      "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
+  static const ByteStringView pLastTens[] = {"Twenty", "Thirty", "Forty",
+                                             "Fifty",  "Sixty",  "Seventy",
+                                             "Eighty", "Ninety"};
+  static const ByteStringView pComm[] = {" Hundred ", " Thousand ", " Million ",
+                                         " Billion ", "Trillion"};
+  const char* pData = bsData.unterminated_c_str();
+  int32_t iLength = bsData.GetLength();
+  int32_t iComm = 0;
+  if (iLength > 12)
+    iComm = 4;
+  else if (iLength > 9)
+    iComm = 3;
+  else if (iLength > 6)
+    iComm = 2;
+  else if (iLength > 3)
+    iComm = 1;
+
+  int32_t iFirstCount = iLength % 3;
+  if (iFirstCount == 0)
+    iFirstCount = 3;
+
+  std::ostringstream strBuf;
+  int32_t iIndex = 0;
+  if (iFirstCount == 3) {
+    if (pData[iIndex] != '0') {
+      strBuf << pCapUnits[pData[iIndex] - '0'];
+      strBuf << pComm[0];
+    }
+    if (pData[iIndex + 1] == '0') {
+      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+    } else {
+      if (pData[iIndex + 1] > '1') {
+        strBuf << pLastTens[pData[iIndex + 1] - '2'];
+        strBuf << "-";
+        strBuf << pUnits[pData[iIndex + 2] - '0'];
+      } else if (pData[iIndex + 1] == '1') {
+        strBuf << pTens[pData[iIndex + 2] - '0'];
+      } else if (pData[iIndex + 1] == '0') {
+        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+      }
+    }
+    iIndex += 3;
+  } else if (iFirstCount == 2) {
+    if (pData[iIndex] == '0') {
+      strBuf << pCapUnits[pData[iIndex + 1] - '0'];
+    } else {
+      if (pData[iIndex] > '1') {
+        strBuf << pLastTens[pData[iIndex] - '2'];
+        strBuf << "-";
+        strBuf << pUnits[pData[iIndex + 1] - '0'];
+      } else if (pData[iIndex] == '1') {
+        strBuf << pTens[pData[iIndex + 1] - '0'];
+      } else if (pData[iIndex] == '0') {
+        strBuf << pCapUnits[pData[iIndex + 1] - '0'];
+      }
+    }
+    iIndex += 2;
+  } else if (iFirstCount == 1) {
+    strBuf << pCapUnits[pData[iIndex] - '0'];
+    ++iIndex;
+  }
+  if (iLength > 3 && iFirstCount > 0) {
+    strBuf << pComm[iComm];
+    --iComm;
+  }
+  while (iIndex < iLength) {
+    if (pData[iIndex] != '0') {
+      strBuf << pCapUnits[pData[iIndex] - '0'];
+      strBuf << pComm[0];
+    }
+    if (pData[iIndex + 1] == '0') {
+      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+    } else {
+      if (pData[iIndex + 1] > '1') {
+        strBuf << pLastTens[pData[iIndex + 1] - '2'];
+        strBuf << "-";
+        strBuf << pUnits[pData[iIndex + 2] - '0'];
+      } else if (pData[iIndex + 1] == '1') {
+        strBuf << pTens[pData[iIndex + 2] - '0'];
+      } else if (pData[iIndex + 1] == '0') {
+        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+      }
+    }
+    if (iIndex < iLength - 3) {
+      strBuf << pComm[iComm];
+      --iComm;
+    }
+    iIndex += 3;
+  }
+  return ByteString(strBuf);
+}
+
+ByteString WordUS(const ByteString& bsData, int32_t iStyle) {
+  const char* pData = bsData.c_str();
+  int32_t iLength = bsData.GetLength();
+  if (iStyle < 0 || iStyle > 2) {
+    return ByteString();
+  }
+
+  std::ostringstream strBuf;
+
+  int32_t iIndex = 0;
+  while (iIndex < iLength) {
+    if (pData[iIndex] == '.')
+      break;
+    ++iIndex;
+  }
+  int32_t iInteger = iIndex;
+  iIndex = 0;
+  while (iIndex < iInteger) {
+    int32_t iCount = (iInteger - iIndex) % 12;
+    if (!iCount && iInteger - iIndex > 0)
+      iCount = 12;
+
+    strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
+    iIndex += iCount;
+    if (iIndex < iInteger)
+      strBuf << " Trillion ";
+  }
+
+  if (iStyle > 0)
+    strBuf << " Dollars";
+
+  if (iStyle > 1 && iInteger < iLength) {
+    strBuf << " And ";
+    iIndex = iInteger + 1;
+    while (iIndex < iLength) {
+      int32_t iCount = (iLength - iIndex) % 12;
+      if (!iCount && iLength - iIndex > 0)
+        iCount = 12;
+
+      strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
+      iIndex += iCount;
+      if (iIndex < iLength)
+        strBuf << " Trillion ";
+    }
+    strBuf << " Cents";
+  }
+  return ByteString(strBuf);
+}
+
+}  // namespace
+
+const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor = {
+    kClassTag,                              // tag
+    "XFA_FM2JS_FormCalcClass",              // name
+    kFormCalcFM2JSFunctions,                // methods
+    FX_ArraySize(kFormCalcFM2JSFunctions),  // number of methods
+    nullptr,                                // dynamic prop type
+    nullptr,                                // dynamic prop getter
+    nullptr,                                // dynamic prop setter
+    nullptr,                                // dynamic prop method call
+};
+
+// static
+void CFXJSE_FormCalcContext::Abs(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Abs");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double dValue = ValueToDouble(pThis, argOne.get());
+  if (dValue < 0)
+    dValue = -dValue;
+
+  args.GetReturnValue()->SetDouble(dValue);
+}
+
+// static
+void CFXJSE_FormCalcContext::Avg(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  uint32_t uCount = 0;
+  double dSum = 0.0;
+  for (int32_t i = 0; i < argc; i++) {
+    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
+    if (argValue->IsNull())
+      continue;
+
+    if (!argValue->IsArray()) {
+      dSum += ValueToDouble(pThis, argValue.get());
+      uCount++;
+      continue;
+    }
+
+    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    argValue->GetObjectProperty("length", lengthValue.get());
+    int32_t iLength = lengthValue->ToInteger();
+
+    if (iLength > 2) {
+      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
+
+      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      if (propertyValue->IsNull()) {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          auto defaultPropValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+          GetObjectDefaultValue(jsObjectValue.get(), defaultPropValue.get());
+          if (defaultPropValue->IsNull())
+            continue;
+
+          dSum += ValueToDouble(pThis, defaultPropValue.get());
+          uCount++;
+        }
+      } else {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+          jsObjectValue->GetObjectProperty(
+              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+          if (newPropertyValue->IsNull())
+            continue;
+
+          dSum += ValueToDouble(pThis, newPropertyValue.get());
+          uCount++;
+        }
+      }
+    }
+  }
+  if (uCount == 0) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  args.GetReturnValue()->SetDouble(dSum / uCount);
+}
+
+// static
+void CFXJSE_FormCalcContext::Ceil(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ceil");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argValue.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  args.GetReturnValue()->SetFloat(ceil(ValueToFloat(pThis, argValue.get())));
+}
+
+// static
+void CFXJSE_FormCalcContext::Count(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  int32_t iCount = 0;
+  for (int32_t i = 0; i < args.GetLength(); i++) {
+    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
+    if (argValue->IsNull())
+      continue;
+
+    if (argValue->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectProperty("length", lengthValue.get());
+
+      int32_t iLength = lengthValue->ToInteger();
+      if (iLength <= 2) {
+        pContext->ThrowArgumentMismatchException();
+        return;
+      }
+
+      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
+      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
+      if (propertyValue->IsNull()) {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+          if (!newPropertyValue->IsNull())
+            iCount++;
+        }
+      } else {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          jsObjectValue->GetObjectProperty(
+              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+          iCount += newPropertyValue->IsNull() ? 0 : 1;
+        }
+      }
+    } else if (argValue->IsObject()) {
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
+      if (!newPropertyValue->IsNull())
+        iCount++;
+    } else {
+      iCount++;
+    }
+  }
+  args.GetReturnValue()->SetInteger(iCount);
+}
+
+// static
+void CFXJSE_FormCalcContext::Floor(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Floor");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argValue.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  args.GetReturnValue()->SetFloat(floor(ValueToFloat(pThis, argValue.get())));
+}
+
+// static
+void CFXJSE_FormCalcContext::Max(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  uint32_t uCount = 0;
+  double dMaxValue = 0.0;
+  for (int32_t i = 0; i < args.GetLength(); i++) {
+    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
+    if (argValue->IsNull())
+      continue;
+
+    if (argValue->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectProperty("length", lengthValue.get());
+      int32_t iLength = lengthValue->ToInteger();
+      if (iLength <= 2) {
+        pContext->ThrowArgumentMismatchException();
+        return;
+      }
+
+      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
+      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
+      if (propertyValue->IsNull()) {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+          if (newPropertyValue->IsNull())
+            continue;
+
+          uCount++;
+          double dValue = ValueToDouble(pThis, newPropertyValue.get());
+          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
+        }
+      } else {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          jsObjectValue->GetObjectProperty(
+              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+          if (newPropertyValue->IsNull())
+            continue;
+
+          uCount++;
+          double dValue = ValueToDouble(pThis, newPropertyValue.get());
+          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
+        }
+      }
+    } else if (argValue->IsObject()) {
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
+      if (newPropertyValue->IsNull())
+        continue;
+
+      uCount++;
+      double dValue = ValueToDouble(pThis, newPropertyValue.get());
+      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
+    } else {
+      uCount++;
+      double dValue = ValueToDouble(pThis, argValue.get());
+      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
+    }
+  }
+  if (uCount == 0) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  args.GetReturnValue()->SetDouble(dMaxValue);
+}
+
+// static
+void CFXJSE_FormCalcContext::Min(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  uint32_t uCount = 0;
+  double dMinValue = 0.0;
+  for (int32_t i = 0; i < args.GetLength(); i++) {
+    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
+    if (argValue->IsNull())
+      continue;
+
+    if (argValue->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectProperty("length", lengthValue.get());
+      int32_t iLength = lengthValue->ToInteger();
+      if (iLength <= 2) {
+        pContext->ThrowArgumentMismatchException();
+        return;
+      }
+
+      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
+      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
+      if (propertyValue->IsNull()) {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+          if (newPropertyValue->IsNull())
+            continue;
+
+          uCount++;
+          double dValue = ValueToDouble(pThis, newPropertyValue.get());
+          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
+        }
+      } else {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          jsObjectValue->GetObjectProperty(
+              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+          if (newPropertyValue->IsNull())
+            continue;
+
+          uCount++;
+          double dValue = ValueToDouble(pThis, newPropertyValue.get());
+          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
+        }
+      }
+    } else if (argValue->IsObject()) {
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
+      if (newPropertyValue->IsNull())
+        continue;
+
+      uCount++;
+      double dValue = ValueToDouble(pThis, newPropertyValue.get());
+      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
+    } else {
+      uCount++;
+      double dValue = ValueToDouble(pThis, argValue.get());
+      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
+    }
+  }
+  if (uCount == 0) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  args.GetReturnValue()->SetDouble(dMinValue);
+}
+
+// static
+void CFXJSE_FormCalcContext::Mod(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 2) {
+    pContext->ThrowParamCountMismatchException(L"Mod");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
+  if (argOne->IsNull() || argTwo->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  bool argOneResult;
+  double dDividend = ExtractDouble(pThis, argOne.get(), &argOneResult);
+  bool argTwoResult;
+  double dDivisor = ExtractDouble(pThis, argTwo.get(), &argTwoResult);
+  if (!argOneResult || !argTwoResult) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  if (dDivisor == 0.0) {
+    pContext->ThrowDivideByZeroException();
+    return;
+  }
+
+  args.GetReturnValue()->SetDouble(dDividend -
+                                   dDivisor * (int32_t)(dDividend / dDivisor));
+}
+
+// static
+void CFXJSE_FormCalcContext::Round(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 2) {
+    pContext->ThrowParamCountMismatchException(L"Round");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  if (argOne->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  bool dValueRet;
+  double dValue = ExtractDouble(pThis, argOne.get(), &dValueRet);
+  if (!dValueRet) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  uint8_t uPrecision = 0;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
+    if (argTwo->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+
+    bool dPrecisionRet;
+    double dPrecision = ExtractDouble(pThis, argTwo.get(), &dPrecisionRet);
+    if (!dPrecisionRet) {
+      pContext->ThrowArgumentMismatchException();
+      return;
+    }
+
+    uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
+  }
+
+  CFGAS_Decimal decimalValue(static_cast<float>(dValue), uPrecision);
+  args.GetReturnValue()->SetDouble(decimalValue.ToDouble());
+}
+
+// static
+void CFXJSE_FormCalcContext::Sum(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc == 0) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  uint32_t uCount = 0;
+  double dSum = 0.0;
+  for (int32_t i = 0; i < argc; i++) {
+    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
+    if (argValue->IsNull())
+      continue;
+
+    if (argValue->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectProperty("length", lengthValue.get());
+      int32_t iLength = lengthValue->ToInteger();
+      if (iLength <= 2) {
+        pContext->ThrowArgumentMismatchException();
+        return;
+      }
+
+      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
+      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      if (propertyValue->IsNull()) {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+          if (newPropertyValue->IsNull())
+            continue;
+
+          dSum += ValueToDouble(pThis, jsObjectValue.get());
+          uCount++;
+        }
+      } else {
+        for (int32_t j = 2; j < iLength; j++) {
+          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          jsObjectValue->GetObjectProperty(
+              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+          if (newPropertyValue->IsNull())
+            continue;
+
+          dSum += ValueToDouble(pThis, newPropertyValue.get());
+          uCount++;
+        }
+      }
+    } else if (argValue->IsObject()) {
+      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
+      if (newPropertyValue->IsNull())
+        continue;
+
+      dSum += ValueToDouble(pThis, argValue.get());
+      uCount++;
+    } else {
+      dSum += ValueToDouble(pThis, argValue.get());
+      uCount++;
+    }
+  }
+  if (uCount == 0) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  args.GetReturnValue()->SetDouble(dSum);
+}
+
+// static
+void CFXJSE_FormCalcContext::Date(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  if (args.GetLength() != 0) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date");
+    return;
+  }
+
+  time_t currentTime;
+  FXSYS_time(&currentTime);
+  struct tm* pTmStruct = gmtime(&currentTime);
+
+  args.GetReturnValue()->SetInteger(DateString2Num(
+      ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900,
+                         pTmStruct->tm_mon + 1, pTmStruct->tm_mday)
+          .AsStringView()));
+}
+
+// static
+void CFXJSE_FormCalcContext::Date2Num(CFXJSE_Value* pThis,
+                                      ByteStringView bsFuncName,
+                                      CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, dateValue.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsDate = ValueToUTF8String(dateValue.get());
+  ByteString bsFormat;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
+    if (ValueIsNull(pThis, formatValue.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsFormat = ValueToUTF8String(formatValue.get());
+  }
+
+  ByteString bsLocale;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
+    if (ValueIsNull(pThis, localeValue.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(localeValue.get());
+  }
+
+  ByteString bsIsoDate =
+      Local2IsoDate(pThis, bsDate.AsStringView(), bsFormat.AsStringView(),
+                    bsLocale.AsStringView());
+  args.GetReturnValue()->SetInteger(DateString2Num(bsIsoDate.AsStringView()));
+}
+
+// static
+void CFXJSE_FormCalcContext::DateFmt(CFXJSE_Value* pThis,
+                                     ByteStringView bsFuncName,
+                                     CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num");
+    return;
+  }
+
+  int32_t iStyle = 0;
+  if (argc > 0) {
+    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
+    if (argStyle->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+
+    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    if (iStyle < 0 || iStyle > 4)
+      iStyle = 0;
+  }
+
+  ByteString bsLocale;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
+    if (argLocale->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(argLocale.get());
+  }
+
+  ByteString bsFormat =
+      GetStandardDateFormat(pThis, iStyle, bsLocale.AsStringView());
+  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::IsoDate2Num(CFXJSE_Value* pThis,
+                                         ByteStringView bsFuncName,
+                                         CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"IsoDate2Num");
+    return;
+  }
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (argOne->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  ByteString bsArg = ValueToUTF8String(argOne.get());
+  args.GetReturnValue()->SetInteger(DateString2Num(bsArg.AsStringView()));
+}
+
+// static
+void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis,
+                                         ByteStringView bsFuncName,
+                                         CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 1) {
+    pContext->ThrowParamCountMismatchException(L"IsoTime2Num");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  CXFA_Document* pDoc = pContext->GetDocument();
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  ByteString bsArg = ValueToUTF8String(argOne.get());
+  auto pos = bsArg.Find('T', 0);
+  if (!pos.has_value() || pos.value() == bsArg.GetLength() - 1) {
+    args.GetReturnValue()->SetInteger(0);
+    return;
+  }
+  bsArg = bsArg.Last(bsArg.GetLength() - (pos.value() + 1));
+
+  CXFA_LocaleValue timeValue(XFA_VT_TIME,
+                             WideString::FromUTF8(bsArg.AsStringView()), pMgr);
+  if (!timeValue.IsValid()) {
+    args.GetReturnValue()->SetInteger(0);
+    return;
+  }
+
+  CFX_DateTime uniTime = timeValue.GetTime();
+  int32_t hour = uniTime.GetHour();
+  int32_t min = uniTime.GetMinute();
+  int32_t second = uniTime.GetSecond();
+  int32_t milSecond = uniTime.GetMillisecond();
+
+  // TODO(dsinclair): See if there is other time conversion code in pdfium and
+  //   consolidate.
+  int32_t mins = hour * 60 + min;
+  mins -= (pMgr->GetDefLocale()->GetTimeZone().tzHour * 60);
+  while (mins > 1440)
+    mins -= 1440;
+  while (mins < 0)
+    mins += 1440;
+  hour = mins / 60;
+  min = mins % 60;
+
+  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
+                                    second * 1000 + milSecond + 1);
+}
+
+// static
+void CFXJSE_FormCalcContext::LocalDateFmt(CFXJSE_Value* pThis,
+                                          ByteStringView bsFuncName,
+                                          CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalDateFmt");
+    return;
+  }
+
+  int32_t iStyle = 0;
+  if (argc > 0) {
+    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
+    if (argStyle->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    if (iStyle > 4 || iStyle < 0)
+      iStyle = 0;
+  }
+
+  ByteString bsLocale;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
+    if (argLocale->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(argLocale.get());
+  }
+
+  ByteString bsFormat =
+      GetLocalDateFormat(pThis, iStyle, bsLocale.AsStringView(), false);
+  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::LocalTimeFmt(CFXJSE_Value* pThis,
+                                          ByteStringView bsFuncName,
+                                          CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalTimeFmt");
+    return;
+  }
+
+  int32_t iStyle = 0;
+  if (argc > 0) {
+    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
+    if (argStyle->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    if (iStyle > 4 || iStyle < 0)
+      iStyle = 0;
+  }
+
+  ByteString bsLocale;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
+    if (argLocale->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(argLocale.get());
+  }
+
+  ByteString bsFormat =
+      GetLocalTimeFormat(pThis, iStyle, bsLocale.AsStringView(), false);
+  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Num2Date(CFXJSE_Value* pThis,
+                                      ByteStringView bsFuncName,
+                                      CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Date");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, dateValue.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  int32_t dDate = (int32_t)ValueToFloat(pThis, dateValue.get());
+  if (dDate < 1) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsFormat;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
+    if (ValueIsNull(pThis, formatValue.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsFormat = ValueToUTF8String(formatValue.get());
+  }
+
+  ByteString bsLocale;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
+    if (ValueIsNull(pThis, localeValue.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(localeValue.get());
+  }
+
+  int32_t iYear = 1900;
+  int32_t iMonth = 1;
+  int32_t iDay = 1;
+  int32_t i = 0;
+  while (dDate > 0) {
+    if (iMonth == 2) {
+      if ((!((iYear + i) % 4) && ((iYear + i) % 100)) || !((iYear + i) % 400)) {
+        if (dDate > 29) {
+          ++iMonth;
+          if (iMonth > 12) {
+            iMonth = 1;
+            ++i;
+          }
+          iDay = 1;
+          dDate -= 29;
+        } else {
+          iDay += static_cast<int32_t>(dDate) - 1;
+          dDate = 0;
+        }
+      } else {
+        if (dDate > 28) {
+          ++iMonth;
+          if (iMonth > 12) {
+            iMonth = 1;
+            ++i;
+          }
+          iDay = 1;
+          dDate -= 28;
+        } else {
+          iDay += static_cast<int32_t>(dDate) - 1;
+          dDate = 0;
+        }
+      }
+    } else if (iMonth < 8) {
+      if ((iMonth % 2 == 0)) {
+        if (dDate > 30) {
+          ++iMonth;
+          if (iMonth > 12) {
+            iMonth = 1;
+            ++i;
+          }
+          iDay = 1;
+          dDate -= 30;
+        } else {
+          iDay += static_cast<int32_t>(dDate) - 1;
+          dDate = 0;
+        }
+      } else {
+        if (dDate > 31) {
+          ++iMonth;
+          if (iMonth > 12) {
+            iMonth = 1;
+            ++i;
+          }
+          iDay = 1;
+          dDate -= 31;
+        } else {
+          iDay += static_cast<int32_t>(dDate) - 1;
+          dDate = 0;
+        }
+      }
+    } else {
+      if (iMonth % 2 != 0) {
+        if (dDate > 30) {
+          ++iMonth;
+          if (iMonth > 12) {
+            iMonth = 1;
+            ++i;
+          }
+          iDay = 1;
+          dDate -= 30;
+        } else {
+          iDay += static_cast<int32_t>(dDate) - 1;
+          dDate = 0;
+        }
+      } else {
+        if (dDate > 31) {
+          ++iMonth;
+          if (iMonth > 12) {
+            iMonth = 1;
+            ++i;
+          }
+          iDay = 1;
+          dDate -= 31;
+        } else {
+          iDay += static_cast<int32_t>(dDate) - 1;
+          dDate = 0;
+        }
+      }
+    }
+  }
+
+  ByteString bsLocalDate = IsoDate2Local(
+      pThis,
+      ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(),
+      bsFormat.AsStringView(), bsLocale.AsStringView());
+  args.GetReturnValue()->SetString(bsLocalDate.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Num2GMTime(CFXJSE_Value* pThis,
+                                        ByteStringView bsFuncName,
+                                        CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2GMTime");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
+  if (timeValue->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  int32_t iTime = (int32_t)ValueToFloat(pThis, timeValue.get());
+  if (abs(iTime) < 1.0) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsFormat;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
+    if (formatValue->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsFormat = ValueToUTF8String(formatValue.get());
+  }
+
+  ByteString bsLocale;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
+    if (localeValue->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(localeValue.get());
+  }
+
+  ByteString bsGMTTime = Num2AllTime(pThis, iTime, bsFormat.AsStringView(),
+                                     bsLocale.AsStringView(), true);
+  args.GetReturnValue()->SetString(bsGMTTime.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Num2Time(CFXJSE_Value* pThis,
+                                      ByteStringView bsFuncName,
+                                      CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Time");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
+  if (timeValue->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  float fTime = ValueToFloat(pThis, timeValue.get());
+  if (fabs(fTime) < 1.0) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsFormat;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
+    if (formatValue->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsFormat = ValueToUTF8String(formatValue.get());
+  }
+
+  ByteString bsLocale;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
+    if (localeValue->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(localeValue.get());
+  }
+
+  ByteString bsLocalTime =
+      Num2AllTime(pThis, static_cast<int32_t>(fTime), bsFormat.AsStringView(),
+                  bsLocale.AsStringView(), false);
+  args.GetReturnValue()->SetString(bsLocalTime.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Time(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  if (args.GetLength() != 0) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time");
+    return;
+  }
+
+  time_t now;
+  FXSYS_time(&now);
+
+  struct tm* pGmt = gmtime(&now);
+  args.GetReturnValue()->SetInteger(
+      (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000);
+}
+
+// static
+void CFXJSE_FormCalcContext::Time2Num(CFXJSE_Value* pThis,
+                                      ByteStringView bsFuncName,
+                                      CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time2Num");
+    return;
+  }
+
+  ByteString bsTime;
+  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, timeValue.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  bsTime = ValueToUTF8String(timeValue.get());
+
+  ByteString bsFormat;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
+    if (ValueIsNull(pThis, formatValue.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsFormat = ValueToUTF8String(formatValue.get());
+  }
+
+  ByteString bsLocale;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
+    if (ValueIsNull(pThis, localeValue.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(localeValue.get());
+  }
+
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  LocaleIface* pLocale = nullptr;
+  if (bsLocale.IsEmpty()) {
+    CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
+    pLocale = pThisNode->GetLocale();
+  } else {
+    pLocale =
+        pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale.AsStringView()));
+  }
+
+  WideString wsFormat;
+  if (bsFormat.IsEmpty())
+    wsFormat = pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
+  else
+    wsFormat = WideString::FromUTF8(bsFormat.AsStringView());
+
+  wsFormat = L"time{" + wsFormat + L"}";
+  CXFA_LocaleValue localeValue(XFA_VT_TIME,
+                               WideString::FromUTF8(bsTime.AsStringView()),
+                               wsFormat, pLocale, pMgr);
+  if (!localeValue.IsValid()) {
+    args.GetReturnValue()->SetInteger(0);
+    return;
+  }
+
+  CFX_DateTime uniTime = localeValue.GetTime();
+  int32_t hour = uniTime.GetHour();
+  int32_t min = uniTime.GetMinute();
+  int32_t second = uniTime.GetSecond();
+  int32_t milSecond = uniTime.GetMillisecond();
+  int32_t mins = hour * 60 + min;
+
+  mins -= (CXFA_TimeZoneProvider().GetTimeZone().tzHour * 60);
+  while (mins > 1440)
+    mins -= 1440;
+
+  while (mins < 0)
+    mins += 1440;
+
+  hour = mins / 60;
+  min = mins % 60;
+  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
+                                    second * 1000 + milSecond + 1);
+}
+
+// static
+void CFXJSE_FormCalcContext::TimeFmt(CFXJSE_Value* pThis,
+                                     ByteStringView bsFuncName,
+                                     CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"TimeFmt");
+    return;
+  }
+
+  int32_t iStyle = 0;
+  if (argc > 0) {
+    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
+    if (argStyle->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    if (iStyle > 4 || iStyle < 0)
+      iStyle = 0;
+  }
+
+  ByteString bsLocale;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
+    if (argLocale->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(argLocale.get());
+  }
+
+  ByteString bsFormat =
+      GetStandardTimeFormat(pThis, iStyle, bsLocale.AsStringView());
+  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_Value* pThis,
+                                                 ByteStringView bsDate,
+                                                 ByteStringView bsFormat,
+                                                 ByteStringView bsLocale) {
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  if (!pDoc)
+    return ByteString();
+
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  if (!pLocale)
+    return ByteString();
+
+  WideString wsFormat = FormatFromString(pLocale, bsFormat);
+  CFX_DateTime dt = CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate),
+                                     wsFormat, pLocale, pMgr)
+                        .GetDate();
+
+  return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(),
+                            dt.GetDay());
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_Value* pThis,
+                                                 ByteStringView bsDate,
+                                                 ByteStringView bsFormat,
+                                                 ByteStringView bsLocale) {
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  if (!pDoc)
+    return ByteString();
+
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  if (!pLocale)
+    return ByteString();
+
+  WideString wsFormat = FormatFromString(pLocale, bsFormat);
+  WideString wsRet;
+  CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate), pMgr)
+      .FormatPatterns(wsRet, wsFormat, pLocale, XFA_VALUEPICTURE_Display);
+  return wsRet.ToUTF8();
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_Value* pThis,
+                                                 ByteStringView bsTime,
+                                                 ByteStringView bsFormat,
+                                                 ByteStringView bsLocale) {
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  if (!pDoc)
+    return ByteString();
+
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  if (!pLocale)
+    return ByteString();
+
+  WideString wsFormat = {
+      L"time{", FormatFromString(pLocale, bsFormat).AsStringView(), L"}"};
+  CXFA_LocaleValue widgetValue(XFA_VT_TIME, WideString::FromUTF8(bsTime), pMgr);
+  WideString wsRet;
+  widgetValue.FormatPatterns(wsRet, wsFormat, pLocale,
+                             XFA_VALUEPICTURE_Display);
+  return wsRet.ToUTF8();
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_Value* pThis,
+                                                      int32_t iStyle,
+                                                      ByteStringView bsLocale,
+                                                      bool bStandard) {
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  if (!pDoc)
+    return ByteString();
+
+  return GetLocalDateTimeFormat(pDoc, iStyle, bsLocale, bStandard,
+                                /*bIsDate=*/true);
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_Value* pThis,
+                                                      int32_t iStyle,
+                                                      ByteStringView bsLocale,
+                                                      bool bStandard) {
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  if (!pDoc)
+    return ByteString();
+
+  return GetLocalDateTimeFormat(pDoc, iStyle, bsLocale, bStandard,
+                                /*bIsDate=*/false);
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::GetStandardDateFormat(
+    CFXJSE_Value* pThis,
+    int32_t iStyle,
+    ByteStringView bsLocale) {
+  return GetLocalDateFormat(pThis, iStyle, bsLocale, true);
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat(
+    CFXJSE_Value* pThis,
+    int32_t iStyle,
+    ByteStringView bsLocale) {
+  return GetLocalTimeFormat(pThis, iStyle, bsLocale, true);
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_Value* pThis,
+                                               int32_t iTime,
+                                               ByteStringView bsFormat,
+                                               ByteStringView bsLocale,
+                                               bool bGM) {
+  int32_t iHour = 0;
+  int32_t iMin = 0;
+  int32_t iSec = 0;
+  iHour = static_cast<int>(iTime) / 3600000;
+  iMin = (static_cast<int>(iTime) - iHour * 3600000) / 60000;
+  iSec = (static_cast<int>(iTime) - iHour * 3600000 - iMin * 60000) / 1000;
+
+  if (!bGM) {
+    int32_t iZoneHour;
+    int32_t iZoneMin;
+    int32_t iZoneSec;
+    GetLocalTimeZone(&iZoneHour, &iZoneMin, &iZoneSec);
+    iHour += iZoneHour;
+    iMin += iZoneMin;
+    iSec += iZoneSec;
+  }
+
+  return IsoTime2Local(
+      pThis,
+      ByteString::Format("%02d:%02d:%02d", iHour, iMin, iSec).AsStringView(),
+      bsFormat, bsLocale);
+}
+
+// static
+void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 3) {
+    pContext->ThrowParamCountMismatchException(L"Apr");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double nPrincipal = ValueToDouble(pThis, argOne.get());
+  double nPayment = ValueToDouble(pThis, argTwo.get());
+  double nPeriods = ValueToDouble(pThis, argThree.get());
+  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods <= 0) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  double r = 2 * (nPeriods * nPayment - nPrincipal) / (nPeriods * nPrincipal);
+  double nTemp = 1;
+  for (int32_t i = 0; i < nPeriods; ++i)
+    nTemp *= (1 + r);
+
+  double nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
+  while (fabs(nRet) > kFinancialPrecision) {
+    double nDerivative =
+        ((nTemp + r * nPeriods * (nTemp / (1 + r))) * (nTemp - 1) -
+         (r * nTemp * nPeriods * (nTemp / (1 + r)))) /
+        ((nTemp - 1) * (nTemp - 1));
+    if (nDerivative == 0) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+
+    r = r - nRet / nDerivative;
+    nTemp = 1;
+    for (int32_t i = 0; i < nPeriods; ++i) {
+      nTemp *= (1 + r);
+    }
+    nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
+  }
+  args.GetReturnValue()->SetDouble(r * 12);
+}
+
+// static
+void CFXJSE_FormCalcContext::CTerm(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 3) {
+    pContext->ThrowParamCountMismatchException(L"CTerm");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float nRate = ValueToFloat(pThis, argOne.get());
+  float nFutureValue = ValueToFloat(pThis, argTwo.get());
+  float nInitAmount = ValueToFloat(pThis, argThree.get());
+  if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  args.GetReturnValue()->SetFloat(log((float)(nFutureValue / nInitAmount)) /
+                                  log((float)(1 + nRate)));
+}
+
+// static
+void CFXJSE_FormCalcContext::FV(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 3) {
+    pContext->ThrowParamCountMismatchException(L"FV");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double nAmount = ValueToDouble(pThis, argOne.get());
+  double nRate = ValueToDouble(pThis, argTwo.get());
+  double nPeriod = ValueToDouble(pThis, argThree.get());
+  if ((nRate < 0) || (nPeriod <= 0) || (nAmount <= 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  double dResult = 0;
+  if (nRate) {
+    double nTemp = 1;
+    for (int i = 0; i < nPeriod; ++i) {
+      nTemp *= 1 + nRate;
+    }
+    dResult = nAmount * (nTemp - 1) / nRate;
+  } else {
+    dResult = nAmount * nPeriod;
+  }
+
+  args.GetReturnValue()->SetDouble(dResult);
+}
+
+// static
+void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 5) {
+    pContext->ThrowParamCountMismatchException(L"IPmt");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
+  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
+      ValueIsNull(pThis, argFive.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
+  float nRate = ValueToFloat(pThis, argTwo.get());
+  float nPayment = ValueToFloat(pThis, argThree.get());
+  float nFirstMonth = ValueToFloat(pThis, argFour.get());
+  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
+  if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
+      (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  float nRateOfMonth = nRate / 12;
+  int32_t iNums =
+      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
+                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
+                log10((float)(1 + nRateOfMonth)));
+  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
+
+  if (nPayment < nPrincipalAmount * nRateOfMonth) {
+    args.GetReturnValue()->SetFloat(0);
+    return;
+  }
+
+  int32_t i = 0;
+  for (i = 0; i < nFirstMonth - 1; ++i)
+    nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
+
+  float nSum = 0;
+  for (; i < iEnd; ++i) {
+    nSum += nPrincipalAmount * nRateOfMonth;
+    nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
+  }
+  args.GetReturnValue()->SetFloat(nSum);
+}
+
+// static
+void CFXJSE_FormCalcContext::NPV(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  int32_t argc = args.GetLength();
+  if (argc < 3) {
+    pContext->ThrowParamCountMismatchException(L"NPV");
+    return;
+  }
+
+  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
+  for (int32_t i = 0; i < argc; i++) {
+    argValues.push_back(GetSimpleValue(pThis, args, i));
+    if (ValueIsNull(pThis, argValues[i].get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+  }
+
+  double nRate = ValueToDouble(pThis, argValues[0].get());
+  if (nRate <= 0) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  std::vector<double> data(argc - 1);
+  for (int32_t i = 1; i < argc; i++)
+    data.push_back(ValueToDouble(pThis, argValues[i].get()));
+
+  double nSum = 0;
+  int32_t iIndex = 0;
+  for (int32_t i = 0; i < argc - 1; i++) {
+    double nTemp = 1;
+    for (int32_t j = 0; j <= i; j++)
+      nTemp *= 1 + nRate;
+
+    double nNum = data[iIndex++];
+    nSum += nNum / nTemp;
+  }
+  args.GetReturnValue()->SetDouble(nSum);
+}
+
+// static
+void CFXJSE_FormCalcContext::Pmt(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 3) {
+    pContext->ThrowParamCountMismatchException(L"Pmt");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float nPrincipal = ValueToFloat(pThis, argOne.get());
+  float nRate = ValueToFloat(pThis, argTwo.get());
+  float nPeriods = ValueToFloat(pThis, argThree.get());
+  if ((nPrincipal <= 0) || (nRate <= 0) || (nPeriods <= 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  float nTmp = 1 + nRate;
+  float nSum = nTmp;
+  for (int32_t i = 0; i < nPeriods - 1; ++i)
+    nSum *= nTmp;
+
+  args.GetReturnValue()->SetFloat((nPrincipal * nRate * nSum) / (nSum - 1));
+}
+
+// static
+void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 5) {
+    pContext->ThrowParamCountMismatchException(L"PPmt");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
+  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
+      ValueIsNull(pThis, argFive.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
+  float nRate = ValueToFloat(pThis, argTwo.get());
+  float nPayment = ValueToFloat(pThis, argThree.get());
+  float nFirstMonth = ValueToFloat(pThis, argFour.get());
+  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
+  if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
+      (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  float nRateOfMonth = nRate / 12;
+  int32_t iNums =
+      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
+                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
+                log10((float)(1 + nRateOfMonth)));
+  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
+  if (nPayment < nPrincipalAmount * nRateOfMonth) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  int32_t i = 0;
+  for (i = 0; i < nFirstMonth - 1; ++i)
+    nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
+
+  float nTemp = 0;
+  float nSum = 0;
+  for (; i < iEnd; ++i) {
+    nTemp = nPayment - nPrincipalAmount * nRateOfMonth;
+    nSum += nTemp;
+    nPrincipalAmount -= nTemp;
+  }
+  args.GetReturnValue()->SetFloat(nSum);
+}
+
+// static
+void CFXJSE_FormCalcContext::PV(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 3) {
+    pContext->ThrowParamCountMismatchException(L"PV");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double nAmount = ValueToDouble(pThis, argOne.get());
+  double nRate = ValueToDouble(pThis, argTwo.get());
+  double nPeriod = ValueToDouble(pThis, argThree.get());
+  if ((nAmount <= 0) || (nRate < 0) || (nPeriod <= 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  double nTemp = 1;
+  for (int32_t i = 0; i < nPeriod; ++i)
+    nTemp *= 1 + nRate;
+
+  nTemp = 1 / nTemp;
+  args.GetReturnValue()->SetDouble(nAmount * ((1 - nTemp) / nRate));
+}
+
+// static
+void CFXJSE_FormCalcContext::Rate(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 3) {
+    pContext->ThrowParamCountMismatchException(L"Rate");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float nFuture = ValueToFloat(pThis, argOne.get());
+  float nPresent = ValueToFloat(pThis, argTwo.get());
+  float nTotalNumber = ValueToFloat(pThis, argThree.get());
+  if ((nFuture <= 0) || (nPresent < 0) || (nTotalNumber <= 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  args.GetReturnValue()->SetFloat(
+      FXSYS_pow((float)(nFuture / nPresent), (float)(1 / nTotalNumber)) - 1);
+}
+
+// static
+void CFXJSE_FormCalcContext::Term(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 3) {
+    pContext->ThrowParamCountMismatchException(L"Term");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
+      ValueIsNull(pThis, argThree.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float nMount = ValueToFloat(pThis, argOne.get());
+  float nRate = ValueToFloat(pThis, argTwo.get());
+  float nFuture = ValueToFloat(pThis, argThree.get());
+  if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  args.GetReturnValue()->SetFloat(log((float)(nFuture / nMount * nRate) + 1) /
+                                  log((float)(1 + nRate)));
+}
+
+// static
+void CFXJSE_FormCalcContext::Choose(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  int32_t argc = args.GetLength();
+  if (argc < 2) {
+    pContext->ThrowParamCountMismatchException(L"Choose");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  int32_t iIndex = (int32_t)ValueToFloat(pThis, argOne.get());
+  if (iIndex < 1) {
+    args.GetReturnValue()->SetString("");
+    return;
+  }
+
+  bool bFound = false;
+  bool bStopCounterFlags = false;
+  int32_t iArgIndex = 1;
+  int32_t iValueIndex = 0;
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) {
+    std::unique_ptr<CFXJSE_Value> argIndexValue = args.GetValue(iArgIndex);
+    if (argIndexValue->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argIndexValue->GetObjectProperty("length", lengthValue.get());
+      int32_t iLength = lengthValue->ToInteger();
+      if (iLength > 3)
+        bStopCounterFlags = true;
+
+      iValueIndex += (iLength - 2);
+      if (iValueIndex >= iIndex) {
+        auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+        auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+        auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+        argIndexValue->GetObjectPropertyByIdx(1, propertyValue.get());
+        argIndexValue->GetObjectPropertyByIdx(
+            (iLength - 1) - (iValueIndex - iIndex), jsObjectValue.get());
+        if (propertyValue->IsNull()) {
+          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+        } else {
+          jsObjectValue->GetObjectProperty(
+              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+        }
+        ByteString bsChosen = ValueToUTF8String(newPropertyValue.get());
+        args.GetReturnValue()->SetString(bsChosen.AsStringView());
+        bFound = true;
+      }
+    } else {
+      iValueIndex++;
+      if (iValueIndex == iIndex) {
+        ByteString bsChosen = ValueToUTF8String(argIndexValue.get());
+        args.GetReturnValue()->SetString(bsChosen.AsStringView());
+        bFound = true;
+      }
+    }
+    iArgIndex++;
+  }
+  if (!bFound)
+    args.GetReturnValue()->SetString("");
+}
+
+// static
+void CFXJSE_FormCalcContext::Exists(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Exists");
+    return;
+  }
+  args.GetReturnValue()->SetInteger(args.GetValue(0)->IsObject());
+}
+
+// static
+void CFXJSE_FormCalcContext::HasValue(CFXJSE_Value* pThis,
+                                      ByteStringView bsFuncName,
+                                      CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"HasValue");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (!argOne->IsString()) {
+    args.GetReturnValue()->SetInteger(argOne->IsNumber() ||
+                                      argOne->IsBoolean());
+    return;
+  }
+
+  ByteString bsValue = argOne->ToString();
+  bsValue.TrimLeft();
+  args.GetReturnValue()->SetInteger(!bsValue.IsEmpty());
+}
+
+// static
+void CFXJSE_FormCalcContext::Oneof(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  if (args.GetLength() < 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Oneof");
+    return;
+  }
+
+  bool bFlags = false;
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::vector<std::unique_ptr<CFXJSE_Value>> parameterValues =
+      unfoldArgs(pThis, args);
+  for (const auto& value : parameterValues) {
+    if (simpleValueCompare(pThis, argOne.get(), value.get())) {
+      bFlags = true;
+      break;
+    }
+  }
+
+  args.GetReturnValue()->SetInteger(bFlags);
+}
+
+// static
+void CFXJSE_FormCalcContext::Within(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  if (args.GetLength() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Within");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (argOne->IsNull()) {
+    args.GetReturnValue()->SetUndefined();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argLow = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> argHigh = GetSimpleValue(pThis, args, 2);
+  if (argOne->IsNumber()) {
+    float oneNumber = ValueToFloat(pThis, argOne.get());
+    float lowNumber = ValueToFloat(pThis, argLow.get());
+    float heightNumber = ValueToFloat(pThis, argHigh.get());
+    args.GetReturnValue()->SetInteger((oneNumber >= lowNumber) &&
+                                      (oneNumber <= heightNumber));
+    return;
+  }
+
+  ByteString bsOne = ValueToUTF8String(argOne.get());
+  ByteString bsLow = ValueToUTF8String(argLow.get());
+  ByteString bsHeight = ValueToUTF8String(argHigh.get());
+  args.GetReturnValue()->SetInteger(
+      (bsOne.Compare(bsLow.AsStringView()) >= 0) &&
+      (bsOne.Compare(bsHeight.AsStringView()) <= 0));
+}
+
+// static
+void CFXJSE_FormCalcContext::If(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args) {
+  if (args.GetLength() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"If");
+    return;
+  }
+
+  args.GetReturnValue()->Assign(GetSimpleValue(pThis, args, 0)->ToBoolean()
+                                    ? GetSimpleValue(pThis, args, 1).get()
+                                    : GetSimpleValue(pThis, args, 2).get());
+}
+
+// static
+void CFXJSE_FormCalcContext::Eval(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 1) {
+    pContext->ThrowParamCountMismatchException(L"Eval");
+    return;
+  }
+
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  std::unique_ptr<CFXJSE_Value> scriptValue = GetSimpleValue(pThis, args, 0);
+  ByteString bsUtf8Script = ValueToUTF8String(scriptValue.get());
+  if (bsUtf8Script.IsEmpty()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  CFX_WideTextBuf wsJavaScriptBuf;
+  if (!CFXJSE_FormCalcContext::Translate(
+          WideString::FromUTF8(bsUtf8Script.AsStringView()).AsStringView(),
+          &wsJavaScriptBuf)) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Context> pNewContext(
+      CFXJSE_Context::Create(pIsolate, nullptr, nullptr));
+
+  auto returnValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  pNewContext->ExecuteScript(
+      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).c_str(), returnValue.get(),
+      nullptr);
+
+  args.GetReturnValue()->Assign(returnValue.get());
+}
+
+// static
+void CFXJSE_FormCalcContext::Ref(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  if (args.GetLength() != 1) {
+    pContext->ThrowParamCountMismatchException(L"Ref");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  if (!argOne->IsArray() && !argOne->IsObject() && !argOne->IsBoolean() &&
+      !argOne->IsString() && !argOne->IsNull() && !argOne->IsNumber()) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  if (argOne->IsBoolean() || argOne->IsString() || argOne->IsNumber()) {
+    args.GetReturnValue()->Assign(argOne.get());
+    return;
+  }
+
+  std::vector<std::unique_ptr<CFXJSE_Value>> values;
+  for (int32_t i = 0; i < 3; i++)
+    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+
+  int intVal = 3;
+  if (argOne->IsNull()) {
+    // TODO(dsinclair): Why is this 4 when the others are all 3?
+    intVal = 4;
+    values[2]->SetNull();
+  } else if (argOne->IsArray()) {
+#ifndef NDEBUG
+    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    argOne->GetObjectProperty("length", lengthValue.get());
+    ASSERT(lengthValue->ToInteger() >= 3);
+#endif
+
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
+    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
+    if (!propertyValue->IsNull() || jsObjectValue->IsNull()) {
+      pContext->ThrowArgumentMismatchException();
+      return;
+    }
+
+    values[2]->Assign(jsObjectValue.get());
+  } else if (argOne->IsObject()) {
+    values[2]->Assign(argOne.get());
+  }
+
+  values[0]->SetInteger(intVal);
+  values[1]->SetNull();
+  args.GetReturnValue()->SetArray(values);
+}
+
+// static
+void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis,
+                                      ByteStringView bsFuncName,
+                                      CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitType");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
+  if (unitspanValue->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get());
+  if (bsUnitspan.IsEmpty()) {
+    args.GetReturnValue()->SetString("in");
+    return;
+  }
+
+  enum XFA_FM2JS_VALUETYPE_ParserStatus {
+    VALUETYPE_START,
+    VALUETYPE_HAVEINVALIDCHAR,
+    VALUETYPE_HAVEDIGIT,
+    VALUETYPE_HAVEDIGITWHITE,
+    VALUETYPE_ISCM,
+    VALUETYPE_ISMM,
+    VALUETYPE_ISPT,
+    VALUETYPE_ISMP,
+    VALUETYPE_ISIN,
+  };
+  bsUnitspan.MakeLower();
+  WideString wsType = WideString::FromUTF8(bsUnitspan.AsStringView());
+  const wchar_t* pData = wsType.c_str();
+  int32_t u = 0;
+  int32_t uLen = wsType.GetLength();
+  while (IsWhitespace(pData[u]))
+    u++;
+
+  XFA_FM2JS_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
+  wchar_t typeChar;
+  // TODO(dsinclair): Cleanup this parser, figure out what the various checks
+  //    are for.
+  while (u < uLen) {
+    typeChar = pData[u];
+    if (IsWhitespace(typeChar)) {
+      if (eParserStatus != VALUETYPE_HAVEDIGIT &&
+          eParserStatus != VALUETYPE_HAVEDIGITWHITE) {
+        eParserStatus = VALUETYPE_ISIN;
+        break;
+      }
+      eParserStatus = VALUETYPE_HAVEDIGITWHITE;
+    } else if (IsPartOfNumberW(typeChar)) {
+      if (eParserStatus == VALUETYPE_HAVEDIGITWHITE) {
+        eParserStatus = VALUETYPE_ISIN;
+        break;
+      }
+      eParserStatus = VALUETYPE_HAVEDIGIT;
+    } else if ((typeChar == 'c' || typeChar == 'p') && (u + 1 < uLen)) {
+      wchar_t nextChar = pData[u + 1];
+      if ((eParserStatus == VALUETYPE_START ||
+           eParserStatus == VALUETYPE_HAVEDIGIT ||
+           eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
+          !IsPartOfNumberW(nextChar)) {
+        eParserStatus = (typeChar == 'c') ? VALUETYPE_ISCM : VALUETYPE_ISPT;
+        break;
+      }
+      eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
+    } else if (typeChar == 'm' && (u + 1 < uLen)) {
+      wchar_t nextChar = pData[u + 1];
+      if ((eParserStatus == VALUETYPE_START ||
+           eParserStatus == VALUETYPE_HAVEDIGIT ||
+           eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
+          !IsPartOfNumberW(nextChar)) {
+        eParserStatus = VALUETYPE_ISMM;
+        if (nextChar == 'p' || ((u + 5 < uLen) && pData[u + 1] == 'i' &&
+                                pData[u + 2] == 'l' && pData[u + 3] == 'l' &&
+                                pData[u + 4] == 'i' && pData[u + 5] == 'p')) {
+          eParserStatus = VALUETYPE_ISMP;
+        }
+        break;
+      }
+    } else {
+      eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
+    }
+    u++;
+  }
+  switch (eParserStatus) {
+    case VALUETYPE_ISCM:
+      args.GetReturnValue()->SetString("cm");
+      break;
+    case VALUETYPE_ISMM:
+      args.GetReturnValue()->SetString("mm");
+      break;
+    case VALUETYPE_ISPT:
+      args.GetReturnValue()->SetString("pt");
+      break;
+    case VALUETYPE_ISMP:
+      args.GetReturnValue()->SetString("mp");
+      break;
+    default:
+      args.GetReturnValue()->SetString("in");
+      break;
+  }
+}
+
+// static
+void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis,
+                                       ByteStringView bsFuncName,
+                                       CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitValue");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
+  if (unitspanValue->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get());
+  const char* pData = bsUnitspan.c_str();
+  if (!pData) {
+    args.GetReturnValue()->SetInteger(0);
+    return;
+  }
+
+  size_t u = 0;
+  while (IsWhitespace(pData[u]))
+    ++u;
+
+  while (u < bsUnitspan.GetLength()) {
+    if (!IsPartOfNumber(pData[u]))
+      break;
+    ++u;
+  }
+
+  char* pTemp = nullptr;
+  double dFirstNumber = strtod(pData, &pTemp);
+  while (IsWhitespace(pData[u]))
+    ++u;
+
+  size_t uLen = bsUnitspan.GetLength();
+  ByteString bsFirstUnit;
+  while (u < uLen) {
+    if (pData[u] == ' ')
+      break;
+
+    bsFirstUnit += pData[u];
+    ++u;
+  }
+  bsFirstUnit.MakeLower();
+
+  ByteString bsUnit;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> unitValue = GetSimpleValue(pThis, args, 1);
+    ByteString bsUnitTemp = ValueToUTF8String(unitValue.get());
+    const char* pChar = bsUnitTemp.c_str();
+    size_t uVal = 0;
+    while (IsWhitespace(pChar[uVal]))
+      ++uVal;
+
+    while (uVal < bsUnitTemp.GetLength()) {
+      if (!std::isdigit(pChar[uVal]) && pChar[uVal] != '.')
+        break;
+      ++uVal;
+    }
+    while (IsWhitespace(pChar[uVal]))
+      ++uVal;
+
+    size_t uValLen = bsUnitTemp.GetLength();
+    while (uVal < uValLen) {
+      if (pChar[uVal] == ' ')
+        break;
+
+      bsUnit += pChar[uVal];
+      ++uVal;
+    }
+    bsUnit.MakeLower();
+  } else {
+    bsUnit = bsFirstUnit;
+  }
+
+  double dResult = 0;
+  if (bsFirstUnit == "in" || bsFirstUnit == "inches") {
+    if (bsUnit == "mm" || bsUnit == "millimeters")
+      dResult = dFirstNumber * 25.4;
+    else if (bsUnit == "cm" || bsUnit == "centimeters")
+      dResult = dFirstNumber * 2.54;
+    else if (bsUnit == "pt" || bsUnit == "points")
+      dResult = dFirstNumber / 72;
+    else if (bsUnit == "mp" || bsUnit == "millipoints")
+      dResult = dFirstNumber / 72000;
+    else
+      dResult = dFirstNumber;
+  } else if (bsFirstUnit == "mm" || bsFirstUnit == "millimeters") {
+    if (bsUnit == "mm" || bsUnit == "millimeters")
+      dResult = dFirstNumber;
+    else if (bsUnit == "cm" || bsUnit == "centimeters")
+      dResult = dFirstNumber / 10;
+    else if (bsUnit == "pt" || bsUnit == "points")
+      dResult = dFirstNumber / 25.4 / 72;
+    else if (bsUnit == "mp" || bsUnit == "millipoints")
+      dResult = dFirstNumber / 25.4 / 72000;
+    else
+      dResult = dFirstNumber / 25.4;
+  } else if (bsFirstUnit == "cm" || bsFirstUnit == "centimeters") {
+    if (bsUnit == "mm" || bsUnit == "millimeters")
+      dResult = dFirstNumber * 10;
+    else if (bsUnit == "cm" || bsUnit == "centimeters")
+      dResult = dFirstNumber;
+    else if (bsUnit == "pt" || bsUnit == "points")
+      dResult = dFirstNumber / 2.54 / 72;
+    else if (bsUnit == "mp" || bsUnit == "millipoints")
+      dResult = dFirstNumber / 2.54 / 72000;
+    else
+      dResult = dFirstNumber / 2.54;
+  } else if (bsFirstUnit == "pt" || bsFirstUnit == "points") {
+    if (bsUnit == "mm" || bsUnit == "millimeters")
+      dResult = dFirstNumber / 72 * 25.4;
+    else if (bsUnit == "cm" || bsUnit == "centimeters")
+      dResult = dFirstNumber / 72 * 2.54;
+    else if (bsUnit == "pt" || bsUnit == "points")
+      dResult = dFirstNumber;
+    else if (bsUnit == "mp" || bsUnit == "millipoints")
+      dResult = dFirstNumber * 1000;
+    else
+      dResult = dFirstNumber / 72;
+  } else if (bsFirstUnit == "mp" || bsFirstUnit == "millipoints") {
+    if (bsUnit == "mm" || bsUnit == "millimeters")
+      dResult = dFirstNumber / 72000 * 25.4;
+    else if (bsUnit == "cm" || bsUnit == "centimeters")
+      dResult = dFirstNumber / 72000 * 2.54;
+    else if (bsUnit == "pt" || bsUnit == "points")
+      dResult = dFirstNumber / 1000;
+    else if (bsUnit == "mp" || bsUnit == "millipoints")
+      dResult = dFirstNumber;
+    else
+      dResult = dFirstNumber / 72000;
+  }
+  args.GetReturnValue()->SetDouble(dResult);
+}
+
+// static
+void CFXJSE_FormCalcContext::At(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"At");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString stringTwo = ValueToUTF8String(argTwo.get());
+  if (stringTwo.IsEmpty()) {
+    args.GetReturnValue()->SetInteger(1);
+    return;
+  }
+
+  ByteString stringOne = ValueToUTF8String(argOne.get());
+  auto pos = stringOne.Find(stringTwo.AsStringView());
+  args.GetReturnValue()->SetInteger(pos.has_value() ? pos.value() + 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::Concat(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Concat");
+    return;
+  }
+
+  ByteString bsResult;
+  bool bAllNull = true;
+  for (int32_t i = 0; i < argc; i++) {
+    std::unique_ptr<CFXJSE_Value> value = GetSimpleValue(pThis, args, i);
+    if (ValueIsNull(pThis, value.get()))
+      continue;
+
+    bAllNull = false;
+    bsResult += ValueToUTF8String(value.get());
+  }
+
+  if (bAllNull) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  args.GetReturnValue()->SetString(bsResult.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Decode(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Decode");
+    return;
+  }
+
+  if (argc == 1) {
+    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+    if (ValueIsNull(pThis, argOne.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+
+    WideString decoded = DecodeURL(
+        WideString::FromUTF8(ValueToUTF8String(argOne.get()).AsStringView()));
+
+    args.GetReturnValue()->SetString(
+        FX_UTF8Encode(decoded.AsStringView()).AsStringView());
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsToDecode = ValueToUTF8String(argOne.get());
+  ByteString bsIdentify = ValueToUTF8String(argTwo.get());
+  WideString decoded;
+
+  WideString wsToDecode = WideString::FromUTF8(bsToDecode.AsStringView());
+
+  if (bsIdentify.EqualNoCase("html"))
+    decoded = DecodeHTML(wsToDecode);
+  else if (bsIdentify.EqualNoCase("xml"))
+    decoded = DecodeXML(wsToDecode);
+  else
+    decoded = DecodeURL(wsToDecode);
+
+  args.GetReturnValue()->SetString(
+      FX_UTF8Encode(decoded.AsStringView()).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Encode(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Encode");
+    return;
+  }
+
+  if (argc == 1) {
+    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+    if (ValueIsNull(pThis, argOne.get())) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+
+    WideString encoded = EncodeURL(ValueToUTF8String(argOne.get()));
+    args.GetReturnValue()->SetString(
+        FX_UTF8Encode(encoded.AsStringView()).AsStringView());
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsToEncode = ValueToUTF8String(argOne.get());
+  ByteString bsIdentify = ValueToUTF8String(argTwo.get());
+  WideString encoded;
+  if (bsIdentify.EqualNoCase("html"))
+    encoded = EncodeHTML(bsToEncode);
+  else if (bsIdentify.EqualNoCase("xml"))
+    encoded = EncodeXML(bsToEncode);
+  else
+    encoded = EncodeURL(bsToEncode);
+
+  args.GetReturnValue()->SetString(
+      FX_UTF8Encode(encoded.AsStringView()).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() < 2) {
+    pContext->ThrowParamCountMismatchException(L"Format");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  ByteString bsPattern = ValueToUTF8String(argOne.get());
+
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  ByteString bsValue = ValueToUTF8String(argTwo.get());
+
+  CXFA_Document* pDoc = pContext->GetDocument();
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
+  LocaleIface* pLocale = pThisNode->GetLocale();
+  WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
+  WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
+  bool bPatternIsString;
+  uint32_t dwPatternType;
+  std::tie(bPatternIsString, dwPatternType) =
+      PatternStringType(bsPattern.AsStringView());
+  if (!bPatternIsString) {
+    switch (dwPatternType) {
+      case XFA_VT_DATETIME: {
+        auto iTChar = wsPattern.Find(L'T');
+        if (!iTChar.has_value()) {
+          args.GetReturnValue()->SetString("");
+          return;
+        }
+        WideString wsDatePattern(L"date{");
+        wsDatePattern += wsPattern.First(iTChar.value()) + L"} ";
+
+        WideString wsTimePattern(L"time{");
+        wsTimePattern +=
+            wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}";
+        wsPattern = wsDatePattern + wsTimePattern;
+      } break;
+      case XFA_VT_DATE: {
+        wsPattern = L"date{" + wsPattern + L"}";
+      } break;
+      case XFA_VT_TIME: {
+        wsPattern = L"time{" + wsPattern + L"}";
+      } break;
+      case XFA_VT_TEXT: {
+        wsPattern = L"text{" + wsPattern + L"}";
+      } break;
+      case XFA_VT_FLOAT: {
+        wsPattern = L"num{" + wsPattern + L"}";
+      } break;
+      default: {
+        WideString wsTestPattern = L"num{" + wsPattern + L"}";
+        CXFA_LocaleValue tempLocaleValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
+                                         pLocale, pMgr);
+        if (tempLocaleValue.IsValid()) {
+          wsPattern = std::move(wsTestPattern);
+          dwPatternType = XFA_VT_FLOAT;
+        } else {
+          wsPattern = L"text{" + wsPattern + L"}";
+          dwPatternType = XFA_VT_TEXT;
+        }
+      } break;
+    }
+  }
+  CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
+                               pMgr);
+  WideString wsRet;
+  if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale,
+                                  XFA_VALUEPICTURE_Display)) {
+    args.GetReturnValue()->SetString("");
+    return;
+  }
+
+  args.GetReturnValue()->SetString(wsRet.ToUTF8().AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Left(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Left");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  if ((ValueIsNull(pThis, argOne.get())) ||
+      (ValueIsNull(pThis, argTwo.get()))) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsSource = ValueToUTF8String(argOne.get());
+  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
+  args.GetReturnValue()->SetString(bsSource.First(count).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Len(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Len");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsSource = ValueToUTF8String(argOne.get());
+  args.GetReturnValue()->SetInteger(bsSource.GetLength());
+}
+
+// static
+void CFXJSE_FormCalcContext::Lower(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Lower");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  CFX_WideTextBuf szLowBuf;
+  ByteString bsArg = ValueToUTF8String(argOne.get());
+  WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
+  for (wchar_t ch : wsArg) {
+    if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE))
+      ch += 32;
+    else if (ch == 0x100 || ch == 0x102 || ch == 0x104)
+      ch += 1;
+    szLowBuf.AppendChar(ch);
+  }
+  szLowBuf.AppendChar(0);
+
+  args.GetReturnValue()->SetString(
+      FX_UTF8Encode(szLowBuf.AsStringView()).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Ltrim(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ltrim");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsSource = ValueToUTF8String(argOne.get());
+  bsSource.TrimLeft();
+  args.GetReturnValue()->SetString(bsSource.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Parse(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 2) {
+    pContext->ThrowParamCountMismatchException(L"Parse");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  if (ValueIsNull(pThis, argTwo.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsPattern = ValueToUTF8String(argOne.get());
+  ByteString bsValue = ValueToUTF8String(argTwo.get());
+  CXFA_Document* pDoc = pContext->GetDocument();
+  CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
+  CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
+  LocaleIface* pLocale = pThisNode->GetLocale();
+  WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
+  WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
+  bool bPatternIsString;
+  uint32_t dwPatternType;
+  std::tie(bPatternIsString, dwPatternType) =
+      PatternStringType(bsPattern.AsStringView());
+  if (bPatternIsString) {
+    CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
+                                 pMgr);
+    if (!localeValue.IsValid()) {
+      args.GetReturnValue()->SetString("");
+      return;
+    }
+    args.GetReturnValue()->SetString(
+        localeValue.GetValue().ToUTF8().AsStringView());
+    return;
+  }
+
+  switch (dwPatternType) {
+    case XFA_VT_DATETIME: {
+      auto iTChar = wsPattern.Find(L'T');
+      if (!iTChar.has_value()) {
+        args.GetReturnValue()->SetString("");
+        return;
+      }
+      WideString wsDatePattern(L"date{" + wsPattern.First(iTChar.value()) +
+                               L"} ");
+      WideString wsTimePattern(
+          L"time{" +
+          wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}");
+      wsPattern = wsDatePattern + wsTimePattern;
+      CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
+                                   pMgr);
+      if (!localeValue.IsValid()) {
+        args.GetReturnValue()->SetString("");
+        return;
+      }
+      args.GetReturnValue()->SetString(
+          localeValue.GetValue().ToUTF8().AsStringView());
+      return;
+    }
+    case XFA_VT_DATE: {
+      wsPattern = L"date{" + wsPattern + L"}";
+      CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
+                                   pMgr);
+      if (!localeValue.IsValid()) {
+        args.GetReturnValue()->SetString("");
+        return;
+      }
+      args.GetReturnValue()->SetString(
+          localeValue.GetValue().ToUTF8().AsStringView());
+      return;
+    }
+    case XFA_VT_TIME: {
+      wsPattern = L"time{" + wsPattern + L"}";
+      CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
+                                   pMgr);
+      if (!localeValue.IsValid()) {
+        args.GetReturnValue()->SetString("");
+        return;
+      }
+      args.GetReturnValue()->SetString(
+          localeValue.GetValue().ToUTF8().AsStringView());
+      return;
+    }
+    case XFA_VT_TEXT: {
+      wsPattern = L"text{" + wsPattern + L"}";
+      CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsPattern, pLocale,
+                                   pMgr);
+      if (!localeValue.IsValid()) {
+        args.GetReturnValue()->SetString("");
+        return;
+      }
+      args.GetReturnValue()->SetString(
+          localeValue.GetValue().ToUTF8().AsStringView());
+      return;
+    }
+    case XFA_VT_FLOAT: {
+      wsPattern = L"num{" + wsPattern + L"}";
+      CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsPattern, pLocale,
+                                   pMgr);
+      if (!localeValue.IsValid()) {
+        args.GetReturnValue()->SetString("");
+        return;
+      }
+      args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
+      return;
+    }
+    default: {
+      {
+        WideString wsTestPattern = L"num{" + wsPattern + L"}";
+        CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
+                                     pLocale, pMgr);
+        if (localeValue.IsValid()) {
+          args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
+          return;
+        }
+      }
+
+      {
+        WideString wsTestPattern = L"text{" + wsPattern + L"}";
+        CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsTestPattern,
+                                     pLocale, pMgr);
+        if (localeValue.IsValid()) {
+          args.GetReturnValue()->SetString(
+              localeValue.GetValue().ToUTF8().AsStringView());
+          return;
+        }
+      }
+      args.GetReturnValue()->SetString("");
+      return;
+    }
+  }
+}
+
+// static
+void CFXJSE_FormCalcContext::Replace(CFXJSE_Value* pThis,
+                                     ByteStringView bsFuncName,
+                                     CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 2 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Replace");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  ByteString bsOne;
+  ByteString bsTwo;
+  if (!ValueIsNull(pThis, argOne.get()) && !ValueIsNull(pThis, argTwo.get())) {
+    bsOne = ValueToUTF8String(argOne.get());
+    bsTwo = ValueToUTF8String(argTwo.get());
+  }
+
+  ByteString bsThree;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+    bsThree = ValueToUTF8String(argThree.get());
+  }
+
+  size_t iFindLen = bsTwo.GetLength();
+  std::ostringstream szResult;
+  size_t iFindIndex = 0;
+  for (size_t u = 0; u < bsOne.GetLength(); ++u) {
+    char ch = static_cast<char>(bsOne[u]);
+    if (ch != static_cast<char>(bsTwo[iFindIndex])) {
+      szResult << ch;
+      continue;
+    }
+
+    size_t iTemp = u + 1;
+    ++iFindIndex;
+    while (iFindIndex < iFindLen) {
+      uint8_t chTemp = bsOne[iTemp];
+      if (chTemp != bsTwo[iFindIndex]) {
+        iFindIndex = 0;
+        break;
+      }
+
+      ++iTemp;
+      ++iFindIndex;
+    }
+    if (iFindIndex == iFindLen) {
+      szResult << bsThree;
+      u += iFindLen - 1;
+      iFindIndex = 0;
+    } else {
+      szResult << ch;
+    }
+  }
+  szResult << '\0';
+  args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str()));
+}
+
+// static
+void CFXJSE_FormCalcContext::Right(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Right");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  if ((ValueIsNull(pThis, argOne.get())) ||
+      (ValueIsNull(pThis, argTwo.get()))) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsSource = ValueToUTF8String(argOne.get());
+  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
+  args.GetReturnValue()->SetString(bsSource.Last(count).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Rtrim(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Rtrim");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsSource = ValueToUTF8String(argOne.get());
+  bsSource.TrimRight();
+  args.GetReturnValue()->SetString(bsSource.AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Space(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Space");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (argOne->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  int32_t count = std::max(0, ValueToInteger(pThis, argOne.get()));
+  std::ostringstream spaceString;
+  int32_t index = 0;
+  while (index < count) {
+    spaceString << ' ';
+    index++;
+  }
+  spaceString << '\0';
+  args.GetReturnValue()->SetString(ByteStringView(spaceString.str().c_str()));
+}
+
+// static
+void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Str");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
+  if (numberValue->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  float fNumber = ValueToFloat(pThis, numberValue.get());
+
+  int32_t iWidth = 10;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> widthValue = GetSimpleValue(pThis, args, 1);
+    iWidth = static_cast<int32_t>(ValueToFloat(pThis, widthValue.get()));
+  }
+
+  int32_t iPrecision = 0;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> precisionValue =
+        GetSimpleValue(pThis, args, 2);
+    iPrecision = std::max(
+        0, static_cast<int32_t>(ValueToFloat(pThis, precisionValue.get())));
+  }
+
+  ByteString bsFormat = "%";
+  if (iPrecision) {
+    bsFormat += ".";
+    bsFormat += ByteString::FormatInteger(iPrecision);
+  }
+  bsFormat += "f";
+  ByteString bsNumber = ByteString::Format(bsFormat.c_str(), fNumber);
+
+  const char* pData = bsNumber.c_str();
+  int32_t iLength = bsNumber.GetLength();
+  int32_t u = 0;
+  while (u < iLength) {
+    if (pData[u] == '.')
+      break;
+
+    ++u;
+  }
+
+  std::ostringstream resultBuf;
+  if (u > iWidth || (iPrecision + u) >= iWidth) {
+    int32_t i = 0;
+    while (i < iWidth) {
+      resultBuf << '*';
+      ++i;
+    }
+    resultBuf << '\0';
+    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+    return;
+  }
+
+  if (u == iLength) {
+    if (iLength > iWidth) {
+      int32_t i = 0;
+      while (i < iWidth) {
+        resultBuf << '*';
+        ++i;
+      }
+    } else {
+      int32_t i = 0;
+      while (i < iWidth - iLength) {
+        resultBuf << ' ';
+        ++i;
+      }
+      resultBuf << pData;
+    }
+    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+    return;
+  }
+
+  int32_t iLeavingSpace = iWidth - u - iPrecision;
+  if (iPrecision != 0)
+    iLeavingSpace--;
+
+  int32_t i = 0;
+  while (i < iLeavingSpace) {
+    resultBuf << ' ';
+    ++i;
+  }
+  i = 0;
+  while (i < u) {
+    resultBuf << pData[i];
+    ++i;
+  }
+  if (iPrecision != 0)
+    resultBuf << '.';
+
+  u++;
+  i = 0;
+  while (u < iLength) {
+    if (i >= iPrecision)
+      break;
+
+    resultBuf << pData[u];
+    ++i;
+    ++u;
+  }
+  while (i < iPrecision) {
+    resultBuf << '0';
+    ++i;
+  }
+  resultBuf << '\0';
+  args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+}
+
+// static
+void CFXJSE_FormCalcContext::Stuff(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 3 || argc > 4) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Stuff");
+    return;
+  }
+
+  ByteString bsSource;
+  ByteString bsInsert;
+  int32_t iLength = 0;
+  int32_t iStart = 0;
+  int32_t iDelete = 0;
+  std::unique_ptr<CFXJSE_Value> sourceValue = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> deleteValue = GetSimpleValue(pThis, args, 2);
+  if (!sourceValue->IsNull() && !startValue->IsNull() &&
+      !deleteValue->IsNull()) {
+    bsSource = ValueToUTF8String(sourceValue.get());
+    iLength = bsSource.GetLength();
+    iStart = pdfium::clamp(
+        static_cast<int32_t>(ValueToFloat(pThis, startValue.get())), 1,
+        iLength);
+    iDelete = std::max(
+        0, static_cast<int32_t>(ValueToFloat(pThis, deleteValue.get())));
+  }
+
+  if (argc > 3) {
+    std::unique_ptr<CFXJSE_Value> insertValue = GetSimpleValue(pThis, args, 3);
+    bsInsert = ValueToUTF8String(insertValue.get());
+  }
+
+  --iStart;
+  std::ostringstream szResult;
+  int32_t i = 0;
+  while (i < iStart) {
+    szResult << static_cast<char>(bsSource[i]);
+    ++i;
+  }
+  szResult << bsInsert.AsStringView();
+  i = iStart + iDelete;
+  while (i < iLength) {
+    szResult << static_cast<char>(bsSource[i]);
+    ++i;
+  }
+  szResult << '\0';
+  args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str()));
+}
+
+// static
+void CFXJSE_FormCalcContext::Substr(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args) {
+  if (args.GetLength() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Substr");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> string_value = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> start_value = GetSimpleValue(pThis, args, 1);
+  std::unique_ptr<CFXJSE_Value> end_value = GetSimpleValue(pThis, args, 2);
+  if (ValueIsNull(pThis, string_value.get()) ||
+      ValueIsNull(pThis, start_value.get()) ||
+      ValueIsNull(pThis, end_value.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  ByteString bsSource = ValueToUTF8String(string_value.get());
+  size_t iLength = bsSource.GetLength();
+  if (iLength == 0) {
+    args.GetReturnValue()->SetString("");
+    return;
+  }
+
+  // |start_value| is 1-based. Assume first character if |start_value| is less
+  // than 1, per spec. Subtract 1 since |iStart| is 0-based.
+  size_t iStart = std::max(ValueToInteger(pThis, start_value.get()), 1) - 1;
+  if (iStart >= iLength) {
+    args.GetReturnValue()->SetString("");
+    return;
+  }
+
+  // Negative values are treated as 0. Can't clamp() due to sign mismatches.
+  size_t iCount = std::max(ValueToInteger(pThis, end_value.get()), 0);
+  iCount = std::min(iCount, iLength - iStart);
+  args.GetReturnValue()->SetString(
+      bsSource.Substr(iStart, iCount).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Uuid(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 0 || argc > 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Uuid");
+    return;
+  }
+
+  int32_t iNum = 0;
+  if (argc > 0) {
+    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+    iNum = static_cast<int32_t>(ValueToFloat(pThis, argOne.get()));
+  }
+  args.GetReturnValue()->SetString(GUIDString(!!iNum).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Upper(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Upper");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (ValueIsNull(pThis, argOne.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  CFX_WideTextBuf upperStringBuf;
+  ByteString bsArg = ValueToUTF8String(argOne.get());
+  WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
+  const wchar_t* pData = wsArg.c_str();
+  size_t i = 0;
+  while (i < wsArg.GetLength()) {
+    int32_t ch = pData[i];
+    if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE))
+      ch -= 32;
+    else if (ch == 0x101 || ch == 0x103 || ch == 0x105)
+      ch -= 1;
+
+    upperStringBuf.AppendChar(ch);
+    ++i;
+  }
+  upperStringBuf.AppendChar(0);
+
+  args.GetReturnValue()->SetString(
+      FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::WordNum(CFXJSE_Value* pThis,
+                                     ByteStringView bsFuncName,
+                                     CFXJSE_Arguments& args) {
+  int32_t argc = args.GetLength();
+  if (argc < 1 || argc > 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"WordNum");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
+  if (numberValue->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  float fNumber = ValueToFloat(pThis, numberValue.get());
+
+  int32_t iIdentifier = 0;
+  if (argc > 1) {
+    std::unique_ptr<CFXJSE_Value> identifierValue =
+        GetSimpleValue(pThis, args, 1);
+    if (identifierValue->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    iIdentifier =
+        static_cast<int32_t>(ValueToFloat(pThis, identifierValue.get()));
+  }
+
+  ByteString bsLocale;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
+    if (localeValue->IsNull()) {
+      args.GetReturnValue()->SetNull();
+      return;
+    }
+    bsLocale = ValueToUTF8String(localeValue.get());
+  }
+
+  if (std::isnan(fNumber) || fNumber < 0.0f ||
+      fNumber > 922337203685477550.0f) {
+    args.GetReturnValue()->SetString("*");
+    return;
+  }
+
+  args.GetReturnValue()->SetString(
+      WordUS(ByteString::Format("%.2f", fNumber), iIdentifier).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Get(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 1) {
+    pContext->ThrowParamCountMismatchException(L"Get");
+    return;
+  }
+
+  CXFA_Document* pDoc = pContext->GetDocument();
+  if (!pDoc)
+    return;
+
+  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  if (!pAppProvider)
+    return;
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  ByteString bsUrl = ValueToUTF8String(argOne.get());
+  RetainPtr<IFX_SeekableReadStream> pFile =
+      pAppProvider->DownloadURL(WideString::FromUTF8(bsUrl.AsStringView()));
+  if (!pFile)
+    return;
+
+  int32_t size = pFile->GetSize();
+  std::vector<uint8_t> dataBuf(size);
+  pFile->ReadBlock(dataBuf.data(), size);
+  args.GetReturnValue()->SetString(ByteStringView(dataBuf));
+}
+
+// static
+void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  int32_t argc = args.GetLength();
+  if (argc < 2 || argc > 5) {
+    pContext->ThrowParamCountMismatchException(L"Post");
+    return;
+  }
+
+  CXFA_Document* pDoc = pContext->GetDocument();
+  if (!pDoc)
+    return;
+
+  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  if (!pAppProvider)
+    return;
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  ByteString bsURL = ValueToUTF8String(argOne.get());
+
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  ByteString bsData = ValueToUTF8String(argTwo.get());
+
+  ByteString bsContentType;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+    bsContentType = ValueToUTF8String(argThree.get());
+  }
+
+  ByteString bsEncode;
+  if (argc > 3) {
+    std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
+    bsEncode = ValueToUTF8String(argFour.get());
+  }
+
+  ByteString bsHeader;
+  if (argc > 4) {
+    std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
+    bsHeader = ValueToUTF8String(argFive.get());
+  }
+
+  WideString decodedResponse;
+  if (!pAppProvider->PostRequestURL(
+          WideString::FromUTF8(bsURL.AsStringView()),
+          WideString::FromUTF8(bsData.AsStringView()),
+          WideString::FromUTF8(bsContentType.AsStringView()),
+          WideString::FromUTF8(bsEncode.AsStringView()),
+          WideString::FromUTF8(bsHeader.AsStringView()), decodedResponse)) {
+    pContext->ThrowServerDeniedException();
+    return;
+  }
+  args.GetReturnValue()->SetString(decodedResponse.ToUTF8().AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  int32_t argc = args.GetLength();
+  if (argc < 2 || argc > 3) {
+    pContext->ThrowParamCountMismatchException(L"Put");
+    return;
+  }
+
+  CXFA_Document* pDoc = pContext->GetDocument();
+  if (!pDoc)
+    return;
+
+  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  if (!pAppProvider)
+    return;
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  ByteString bsURL = ValueToUTF8String(argOne.get());
+
+  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  ByteString bsData = ValueToUTF8String(argTwo.get());
+
+  ByteString bsEncode;
+  if (argc > 2) {
+    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
+    bsEncode = ValueToUTF8String(argThree.get());
+  }
+
+  if (!pAppProvider->PutRequestURL(
+          WideString::FromUTF8(bsURL.AsStringView()),
+          WideString::FromUTF8(bsData.AsStringView()),
+          WideString::FromUTF8(bsEncode.AsStringView()))) {
+    pContext->ThrowServerDeniedException();
+    return;
+  }
+
+  args.GetReturnValue()->SetString("");
+}
+
+// static
+void CFXJSE_FormCalcContext::assign_value_operator(CFXJSE_Value* pThis,
+                                                   ByteStringView bsFuncName,
+                                                   CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 2) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> lValue = args.GetValue(0);
+  std::unique_ptr<CFXJSE_Value> rValue = GetSimpleValue(pThis, args, 1);
+  if (lValue->IsArray()) {
+    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+    auto leftLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    lValue->GetObjectProperty("length", leftLengthValue.get());
+    int32_t iLeftLength = leftLengthValue->ToInteger();
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    lValue->GetObjectPropertyByIdx(1, propertyValue.get());
+    if (propertyValue->IsNull()) {
+      for (int32_t i = 2; i < iLeftLength; i++) {
+        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
+        if (!SetObjectDefaultValue(jsObjectValue.get(), rValue.get())) {
+          pContext->ThrowNoDefaultPropertyException(bsFuncName);
+          return;
+        }
+      }
+    } else {
+      for (int32_t i = 2; i < iLeftLength; i++) {
+        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
+        jsObjectValue->SetObjectProperty(
+            propertyValue->ToString().AsStringView(), rValue.get());
+      }
+    }
+  } else if (lValue->IsObject()) {
+    if (!SetObjectDefaultValue(lValue.get(), rValue.get())) {
+      pContext->ThrowNoDefaultPropertyException(bsFuncName);
+      return;
+    }
+  }
+  args.GetReturnValue()->Assign(rValue.get());
+}
+
+// static
+void CFXJSE_FormCalcContext::logical_or_operator(CFXJSE_Value* pThis,
+                                                 ByteStringView bsFuncName,
+                                                 CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() && argSecond->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float first = ValueToFloat(pThis, argFirst.get());
+  float second = ValueToFloat(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger((first || second) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::logical_and_operator(CFXJSE_Value* pThis,
+                                                  ByteStringView bsFuncName,
+                                                  CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() && argSecond->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  float first = ValueToFloat(pThis, argFirst.get());
+  float second = ValueToFloat(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger((first && second) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::equality_operator(CFXJSE_Value* pThis,
+                                               ByteStringView bsFuncName,
+                                               CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  if (fm_ref_equal(pThis, args)) {
+    args.GetReturnValue()->SetInteger(1);
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() || argSecond->IsNull()) {
+    args.GetReturnValue()->SetInteger(
+        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+    return;
+  }
+
+  if (argFirst->IsString() && argSecond->IsString()) {
+    args.GetReturnValue()->SetInteger(argFirst->ToString() ==
+                                      argSecond->ToString());
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger((first == second) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::notequality_operator(CFXJSE_Value* pThis,
+                                                  ByteStringView bsFuncName,
+                                                  CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  if (fm_ref_equal(pThis, args)) {
+    args.GetReturnValue()->SetInteger(0);
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() || argSecond->IsNull()) {
+    args.GetReturnValue()->SetInteger(
+        (argFirst->IsNull() && argSecond->IsNull()) ? 0 : 1);
+    return;
+  }
+
+  if (argFirst->IsString() && argSecond->IsString()) {
+    args.GetReturnValue()->SetInteger(argFirst->ToString() !=
+                                      argSecond->ToString());
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger(first != second);
+}
+
+// static
+bool CFXJSE_FormCalcContext::fm_ref_equal(CFXJSE_Value* pThis,
+                                          CFXJSE_Arguments& args) {
+  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
+  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
+  if (!argFirst->IsArray() || !argSecond->IsArray())
+    return false;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  auto firstFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  auto secondFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  argFirst->GetObjectPropertyByIdx(0, firstFlagValue.get());
+  argSecond->GetObjectPropertyByIdx(0, secondFlagValue.get());
+  if (firstFlagValue->ToInteger() != 3 || secondFlagValue->ToInteger() != 3)
+    return false;
+
+  auto firstJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  auto secondJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  argFirst->GetObjectPropertyByIdx(2, firstJSObject.get());
+  argSecond->GetObjectPropertyByIdx(2, secondJSObject.get());
+  if (firstJSObject->IsNull() || secondJSObject->IsNull())
+    return false;
+
+  return firstJSObject->ToHostObject() == secondJSObject->ToHostObject();
+}
+
+// static
+void CFXJSE_FormCalcContext::less_operator(CFXJSE_Value* pThis,
+                                           ByteStringView bsFuncName,
+                                           CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() || argSecond->IsNull()) {
+    args.GetReturnValue()->SetInteger(0);
+    return;
+  }
+
+  if (argFirst->IsString() && argSecond->IsString()) {
+    int result =
+        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) < 0;
+    args.GetReturnValue()->SetInteger(result);
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger((first < second) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::lessequal_operator(CFXJSE_Value* pThis,
+                                                ByteStringView bsFuncName,
+                                                CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() || argSecond->IsNull()) {
+    args.GetReturnValue()->SetInteger(
+        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+    return;
+  }
+
+  if (argFirst->IsString() && argSecond->IsString()) {
+    int result =
+        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) <= 0;
+    args.GetReturnValue()->SetInteger(result);
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger((first <= second) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::greater_operator(CFXJSE_Value* pThis,
+                                              ByteStringView bsFuncName,
+                                              CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() || argSecond->IsNull()) {
+    args.GetReturnValue()->SetInteger(0);
+    return;
+  }
+
+  if (argFirst->IsString() && argSecond->IsString()) {
+    int result =
+        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) > 0;
+    args.GetReturnValue()->SetInteger(result);
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger((first > second) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::greaterequal_operator(CFXJSE_Value* pThis,
+                                                   ByteStringView bsFuncName,
+                                                   CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() || argSecond->IsNull()) {
+    args.GetReturnValue()->SetInteger(
+        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+    return;
+  }
+
+  if (argFirst->IsString() && argSecond->IsString()) {
+    int result =
+        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) >= 0;
+    args.GetReturnValue()->SetInteger(result);
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetInteger((first >= second) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::plus_operator(CFXJSE_Value* pThis,
+                                           ByteStringView bsFuncName,
+                                           CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
+  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
+  if (ValueIsNull(pThis, argFirst.get()) &&
+      ValueIsNull(pThis, argSecond.get())) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetDouble(first + second);
+}
+
+// static
+void CFXJSE_FormCalcContext::minus_operator(CFXJSE_Value* pThis,
+                                            ByteStringView bsFuncName,
+                                            CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() && argSecond->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetDouble(first - second);
+}
+
+// static
+void CFXJSE_FormCalcContext::multiple_operator(CFXJSE_Value* pThis,
+                                               ByteStringView bsFuncName,
+                                               CFXJSE_Arguments& args) {
+  if (args.GetLength() != 2) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() && argSecond->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  double second = ValueToDouble(pThis, argSecond.get());
+  args.GetReturnValue()->SetDouble(first * second);
+}
+
+// static
+void CFXJSE_FormCalcContext::divide_operator(CFXJSE_Value* pThis,
+                                             ByteStringView bsFuncName,
+                                             CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 2) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
+  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
+  if (argFirst->IsNull() && argSecond->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double second = ValueToDouble(pThis, argSecond.get());
+  if (second == 0.0) {
+    pContext->ThrowDivideByZeroException();
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argFirst.get());
+  args.GetReturnValue()->SetDouble(first / second);
+}
+
+// static
+void CFXJSE_FormCalcContext::positive_operator(CFXJSE_Value* pThis,
+                                               ByteStringView bsFuncName,
+                                               CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (argOne->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  args.GetReturnValue()->SetDouble(0.0 + ValueToDouble(pThis, argOne.get()));
+}
+
+// static
+void CFXJSE_FormCalcContext::negative_operator(CFXJSE_Value* pThis,
+                                               ByteStringView bsFuncName,
+                                               CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (argOne->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+  args.GetReturnValue()->SetDouble(0.0 - ValueToDouble(pThis, argOne.get()));
+}
+
+// static
+void CFXJSE_FormCalcContext::logical_not_operator(CFXJSE_Value* pThis,
+                                                  ByteStringView bsFuncName,
+                                                  CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  if (argOne->IsNull()) {
+    args.GetReturnValue()->SetNull();
+    return;
+  }
+
+  double first = ValueToDouble(pThis, argOne.get());
+  args.GetReturnValue()->SetInteger((first == 0.0) ? 1 : 0);
+}
+
+// static
+void CFXJSE_FormCalcContext::dot_accessor(CFXJSE_Value* pThis,
+                                          ByteStringView bsFuncName,
+                                          CFXJSE_Arguments& args) {
+  DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/true);
+}
+
+// static
+void CFXJSE_FormCalcContext::dotdot_accessor(CFXJSE_Value* pThis,
+                                             ByteStringView bsFuncName,
+                                             CFXJSE_Arguments& args) {
+  DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/false);
+}
+
+// static
+void CFXJSE_FormCalcContext::eval_translation(CFXJSE_Value* pThis,
+                                              ByteStringView bsFuncName,
+                                              CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 1) {
+    pContext->ThrowParamCountMismatchException(L"Eval");
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
+  ByteString bsArg = ValueToUTF8String(argOne.get());
+  if (bsArg.IsEmpty()) {
+    pContext->ThrowArgumentMismatchException();
+    return;
+  }
+
+  WideString wsScript = WideString::FromUTF8(bsArg.AsStringView());
+  CFX_WideTextBuf wsJavaScriptBuf;
+  if (!CFXJSE_FormCalcContext::Translate(wsScript.AsStringView(),
+                                         &wsJavaScriptBuf)) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+
+  args.GetReturnValue()->SetString(
+      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).AsStringView());
+}
+
+// static
+void CFXJSE_FormCalcContext::is_fm_object(CFXJSE_Value* pThis,
+                                          ByteStringView bsFuncName,
+                                          CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    args.GetReturnValue()->SetBoolean(false);
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  args.GetReturnValue()->SetBoolean(argOne->IsObject());
+}
+
+// static
+void CFXJSE_FormCalcContext::is_fm_array(CFXJSE_Value* pThis,
+                                         ByteStringView bsFuncName,
+                                         CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    args.GetReturnValue()->SetBoolean(false);
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  args.GetReturnValue()->SetBoolean(argOne->IsArray());
+}
+
+// static
+void CFXJSE_FormCalcContext::get_fm_value(CFXJSE_Value* pThis,
+                                          ByteStringView bsFuncName,
+                                          CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 1) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  if (argOne->IsArray()) {
+    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
+    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
+    if (propertyValue->IsNull()) {
+      GetObjectDefaultValue(jsObjectValue.get(), args.GetReturnValue());
+      return;
+    }
+
+    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
+                                     args.GetReturnValue());
+    return;
+  }
+
+  if (argOne->IsObject()) {
+    GetObjectDefaultValue(argOne.get(), args.GetReturnValue());
+    return;
+  }
+
+  args.GetReturnValue()->Assign(argOne.get());
+}
+
+// static
+void CFXJSE_FormCalcContext::get_fm_jsobj(CFXJSE_Value* pThis,
+                                          ByteStringView bsFuncName,
+                                          CFXJSE_Arguments& args) {
+  if (args.GetLength() != 1) {
+    ToFormCalcContext(pThis)->ThrowCompilerErrorException();
+    return;
+  }
+
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  if (!argOne->IsArray()) {
+    args.GetReturnValue()->Assign(argOne.get());
+    return;
+  }
+
+#ifndef NDEBUG
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  argOne->GetObjectProperty("length", lengthValue.get());
+  ASSERT(lengthValue->ToInteger() >= 3);
+#endif
+
+  argOne->GetObjectPropertyByIdx(2, args.GetReturnValue());
+}
+
+// static
+void CFXJSE_FormCalcContext::fm_var_filter(CFXJSE_Value* pThis,
+                                           ByteStringView bsFuncName,
+                                           CFXJSE_Arguments& args) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  if (args.GetLength() != 1) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
+  if (!argOne->IsArray()) {
+    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
+    args.GetReturnValue()->Assign(simpleValue.get());
+    return;
+  }
+
+#ifndef NDEBUG
+  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  argOne->GetObjectProperty("length", lengthValue.get());
+  ASSERT(lengthValue->ToInteger() >= 3);
+#endif
+
+  auto flagsValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  argOne->GetObjectPropertyByIdx(0, flagsValue.get());
+  int32_t iFlags = flagsValue->ToInteger();
+  if (iFlags != 3 && iFlags != 4) {
+    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
+    args.GetReturnValue()->Assign(simpleValue.get());
+    return;
+  }
+
+  if (iFlags == 4) {
+    std::vector<std::unique_ptr<CFXJSE_Value>> values;
+    for (int32_t i = 0; i < 3; i++)
+      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+
+    values[0]->SetInteger(3);
+    values[1]->SetNull();
+    values[2]->SetNull();
+    args.GetReturnValue()->SetArray(values);
+    return;
+  }
+
+  auto objectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  argOne->GetObjectPropertyByIdx(2, objectValue.get());
+  if (objectValue->IsNull()) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+  args.GetReturnValue()->Assign(argOne.get());
+}
+
+// static
+void CFXJSE_FormCalcContext::concat_fm_object(CFXJSE_Value* pThis,
+                                              ByteStringView bsFuncName,
+                                              CFXJSE_Arguments& args) {
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  uint32_t iLength = 0;
+  int32_t argc = args.GetLength();
+  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
+  for (int32_t i = 0; i < argc; i++) {
+    argValues.push_back(args.GetValue(i));
+    if (argValues[i]->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValues[i]->GetObjectProperty("length", lengthValue.get());
+      int32_t length = lengthValue->ToInteger();
+      iLength = iLength + ((length > 2) ? (length - 2) : 0);
+    }
+    ++iLength;
+  }
+
+  std::vector<std::unique_ptr<CFXJSE_Value>> returnValues;
+  for (int32_t i = 0; i < (int32_t)iLength; i++)
+    returnValues.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+
+  int32_t index = 0;
+  for (int32_t i = 0; i < argc; i++) {
+    if (argValues[i]->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argValues[i]->GetObjectProperty("length", lengthValue.get());
+
+      int32_t length = lengthValue->ToInteger();
+      for (int32_t j = 2; j < length; j++) {
+        argValues[i]->GetObjectPropertyByIdx(j, returnValues[index].get());
+        index++;
+      }
+    }
+    returnValues[index]->Assign(argValues[i].get());
+    index++;
+  }
+  args.GetReturnValue()->SetArray(returnValues);
+}
+
+// static
+std::unique_ptr<CFXJSE_Value> CFXJSE_FormCalcContext::GetSimpleValue(
+    CFXJSE_Value* pThis,
+    CFXJSE_Arguments& args,
+    uint32_t index) {
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  ASSERT(index < (uint32_t)args.GetLength());
+
+  std::unique_ptr<CFXJSE_Value> argIndex = args.GetValue(index);
+  if (!argIndex->IsArray() && !argIndex->IsObject())
+    return argIndex;
+
+  if (argIndex->IsArray()) {
+    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    argIndex->GetObjectProperty("length", lengthValue.get());
+    int32_t iLength = lengthValue->ToInteger();
+    auto simpleValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    if (iLength < 3) {
+      simpleValue.get()->SetUndefined();
+      return simpleValue;
+    }
+
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    argIndex->GetObjectPropertyByIdx(1, propertyValue.get());
+    argIndex->GetObjectPropertyByIdx(2, jsObjectValue.get());
+    if (propertyValue->IsNull()) {
+      GetObjectDefaultValue(jsObjectValue.get(), simpleValue.get());
+      return simpleValue;
+    }
+
+    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
+                                     simpleValue.get());
+    return simpleValue;
+  }
+
+  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  GetObjectDefaultValue(argIndex.get(), defaultValue.get());
+  return defaultValue;
+}
+
+// static
+bool CFXJSE_FormCalcContext::ValueIsNull(CFXJSE_Value* pThis,
+                                         CFXJSE_Value* arg) {
+  if (!arg || arg->IsNull())
+    return true;
+
+  if (!arg->IsArray() && !arg->IsObject())
+    return false;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  if (arg->IsArray()) {
+    int32_t iLength = hvalue_get_array_length(pThis, arg);
+    if (iLength < 3)
+      return true;
+
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    arg->GetObjectPropertyByIdx(1, propertyValue.get());
+    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
+    if (propertyValue->IsNull()) {
+      auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      GetObjectDefaultValue(jsObjectValue.get(), defaultValue.get());
+      return defaultValue->IsNull();
+    }
+
+    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
+                                     newPropertyValue.get());
+    return newPropertyValue->IsNull();
+  }
+
+  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  GetObjectDefaultValue(arg, defaultValue.get());
+  return defaultValue->IsNull();
+}
+
+// static
+int32_t CFXJSE_FormCalcContext::hvalue_get_array_length(CFXJSE_Value* pThis,
+                                                        CFXJSE_Value* arg) {
+  if (!arg || !arg->IsArray())
+    return 0;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  arg->GetObjectProperty("length", lengthValue.get());
+  return lengthValue->ToInteger();
+}
+
+// static
+bool CFXJSE_FormCalcContext::simpleValueCompare(CFXJSE_Value* pThis,
+                                                CFXJSE_Value* firstValue,
+                                                CFXJSE_Value* secondValue) {
+  if (!firstValue)
+    return false;
+
+  if (firstValue->IsString()) {
+    ByteString bsFirst = ValueToUTF8String(firstValue);
+    ByteString bsSecond = ValueToUTF8String(secondValue);
+    return bsFirst == bsSecond;
+  }
+  if (firstValue->IsNumber()) {
+    float first = ValueToFloat(pThis, firstValue);
+    float second = ValueToFloat(pThis, secondValue);
+    return first == second;
+  }
+  if (firstValue->IsBoolean())
+    return firstValue->ToBoolean() == secondValue->ToBoolean();
+
+  return firstValue->IsNull() && secondValue && secondValue->IsNull();
+}
+
+// static
+std::vector<std::unique_ptr<CFXJSE_Value>> CFXJSE_FormCalcContext::unfoldArgs(
+    CFXJSE_Value* pThis,
+    CFXJSE_Arguments& args) {
+  std::vector<std::unique_ptr<CFXJSE_Value>> results;
+
+  int32_t iCount = 0;
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  int32_t argc = args.GetLength();
+  std::vector<std::unique_ptr<CFXJSE_Value>> argsValue;
+  static constexpr int kStart = 1;
+  for (int32_t i = 0; i < argc - kStart; i++) {
+    argsValue.push_back(args.GetValue(i + kStart));
+    if (argsValue[i]->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argsValue[i]->GetObjectProperty("length", lengthValue.get());
+      int32_t iLength = lengthValue->ToInteger();
+      iCount += ((iLength > 2) ? (iLength - 2) : 0);
+    } else {
+      ++iCount;
+    }
+  }
+
+  for (int32_t i = 0; i < iCount; i++)
+    results.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+
+  int32_t index = 0;
+  for (int32_t i = 0; i < argc - kStart; i++) {
+    if (argsValue[i]->IsArray()) {
+      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argsValue[i]->GetObjectProperty("length", lengthValue.get());
+      int32_t iLength = lengthValue->ToInteger();
+      if (iLength < 3)
+        continue;
+
+      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      argsValue[i]->GetObjectPropertyByIdx(1, propertyValue.get());
+      if (propertyValue->IsNull()) {
+        for (int32_t j = 2; j < iLength; j++) {
+          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          GetObjectDefaultValue(jsObjectValue.get(), results[index].get());
+          index++;
+        }
+      } else {
+        for (int32_t j = 2; j < iLength; j++) {
+          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
+          jsObjectValue->GetObjectProperty(
+              propertyValue->ToString().AsStringView(), results[index].get());
+          index++;
+        }
+      }
+    } else if (argsValue[i]->IsObject()) {
+      GetObjectDefaultValue(argsValue[i].get(), results[index].get());
+      index++;
+    } else {
+      results[index]->Assign(argsValue[i].get());
+      index++;
+    }
+  }
+  return results;
+}
+
+// static
+void CFXJSE_FormCalcContext::GetObjectDefaultValue(
+    CFXJSE_Value* pValue,
+    CFXJSE_Value* pDefaultValue) {
+  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue));
+  if (!pNode) {
+    pDefaultValue->SetNull();
+    return;
+  }
+  pNode->JSObject()->ScriptSomDefaultValue(pDefaultValue, false,
+                                           XFA_Attribute::Unknown);
+}
+
+// static
+bool CFXJSE_FormCalcContext::SetObjectDefaultValue(CFXJSE_Value* pValue,
+                                                   CFXJSE_Value* hNewValue) {
+  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue));
+  if (!pNode)
+    return false;
+
+  pNode->JSObject()->ScriptSomDefaultValue(hNewValue, true,
+                                           XFA_Attribute::Unknown);
+  return true;
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::GenerateSomExpression(ByteStringView bsName,
+                                                         int32_t iIndexFlags,
+                                                         int32_t iIndexValue,
+                                                         bool bIsStar) {
+  if (bIsStar)
+    return ByteString(bsName, "[*]");
+
+  if (iIndexFlags == 0)
+    return ByteString(bsName);
+
+  if (iIndexFlags == 1 || iIndexValue == 0) {
+    return ByteString(bsName, "[") + ByteString::FormatInteger(iIndexValue) +
+           "]";
+  }
+
+  const bool bNegative = iIndexValue < 0;
+  ByteString bsSomExp(bsName);
+  if (iIndexFlags == 2)
+    bsSomExp += bNegative ? "[-" : "[+";
+  else
+    bsSomExp += bNegative ? "[" : "[-";
+  iIndexValue = bNegative ? 0 - iIndexValue : iIndexValue;
+  bsSomExp += ByteString::FormatInteger(iIndexValue);
+  bsSomExp += "]";
+  return bsSomExp;
+}
+
+// static
+bool CFXJSE_FormCalcContext::GetObjectForName(CFXJSE_Value* pThis,
+                                              CFXJSE_Value* accessorValue,
+                                              ByteStringView bsAccessorName) {
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  if (!pDoc)
+    return false;
+
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  XFA_RESOLVENODE_RS resolveNodeRS;
+  uint32_t dwFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
+                     XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
+  bool bRet = pScriptContext->ResolveObjects(
+      pScriptContext->GetThisObject(),
+      WideString::FromUTF8(bsAccessorName).AsStringView(), &resolveNodeRS,
+      dwFlags, nullptr);
+  if (bRet && resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
+    accessorValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(
+        resolveNodeRS.objects.front().Get()));
+    return true;
+  }
+  return false;
+}
+
+// static
+bool CFXJSE_FormCalcContext::ResolveObjects(CFXJSE_Value* pThis,
+                                            CFXJSE_Value* pRefValue,
+                                            ByteStringView bsSomExp,
+                                            XFA_RESOLVENODE_RS* resolveNodeRS,
+                                            bool bDotAccessor,
+                                            bool bHasNoResolveName) {
+  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
+  if (!pDoc)
+    return false;
+
+  WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  CXFA_Object* pNode = nullptr;
+  uint32_t dFlags = 0UL;
+  if (bDotAccessor) {
+    if (pRefValue && pRefValue->IsNull()) {
+      pNode = pScriptContext->GetThisObject();
+      dFlags = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
+    } else {
+      pNode = CFXJSE_Engine::ToObject(pRefValue);
+      if (!pNode)
+        return false;
+
+      if (bHasNoResolveName) {
+        WideString wsName;
+        if (CXFA_Node* pXFANode = pNode->AsNode()) {
+          Optional<WideString> ret =
+              pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
+          if (ret)
+            wsName = *ret;
+        }
+        if (wsName.IsEmpty())
+          wsName = L"#" + WideString::FromASCII(pNode->GetClassName());
+
+        wsSomExpression = wsName + wsSomExpression;
+        dFlags = XFA_RESOLVENODE_Siblings;
+      } else {
+        dFlags = (bsSomExp == "*")
+                     ? (XFA_RESOLVENODE_Children)
+                     : (XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
+                        XFA_RESOLVENODE_Properties);
+      }
+    }
+  } else {
+    pNode = CFXJSE_Engine::ToObject(pRefValue);
+    dFlags = XFA_RESOLVENODE_AnyChild;
+  }
+  return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
+                                        resolveNodeRS, dFlags, nullptr);
+}
+
+// static
+void CFXJSE_FormCalcContext::ParseResolveResult(
+    CFXJSE_Value* pThis,
+    const XFA_RESOLVENODE_RS& resolveNodeRS,
+    CFXJSE_Value* pParentValue,
+    std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
+    bool* bAttribute) {
+  ASSERT(bAttribute);
+
+  resultValues->clear();
+
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+
+  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
+    *bAttribute = false;
+    CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
+    for (auto& pObject : resolveNodeRS.objects) {
+      resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+      resultValues->back()->Assign(
+          pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get()));
+    }
+    return;
+  }
+
+  *bAttribute = true;
+  if (resolveNodeRS.script_attribute.callback &&
+      resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
+    for (auto& pObject : resolveNodeRS.objects) {
+      auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+      CJX_Object* jsObject = pObject->JSObject();
+      (*resolveNodeRS.script_attribute.callback)(
+          jsObject, pValue.get(), false,
+          resolveNodeRS.script_attribute.attribute);
+      resultValues->push_back(std::move(pValue));
+      *bAttribute = false;
+    }
+  }
+  if (!*bAttribute)
+    return;
+  if (!pParentValue || !pParentValue->IsObject())
+    return;
+
+  resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+  resultValues->back()->Assign(pParentValue);
+}
+
+// static
+int32_t CFXJSE_FormCalcContext::ValueToInteger(CFXJSE_Value* pThis,
+                                               CFXJSE_Value* pValue) {
+  if (!pValue || pValue->IsEmpty())
+    return 0;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  if (pValue->IsArray()) {
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    pValue->GetObjectPropertyByIdx(1, propertyValue.get());
+    pValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
+    if (propertyValue->IsNull()) {
+      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+      return ValueToInteger(pThis, newPropertyValue.get());
+    }
+
+    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
+                                     newPropertyValue.get());
+    return ValueToInteger(pThis, newPropertyValue.get());
+  }
+  if (pValue->IsObject()) {
+    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    GetObjectDefaultValue(pValue, newPropertyValue.get());
+    return ValueToInteger(pThis, newPropertyValue.get());
+  }
+  if (pValue->IsString())
+    return FXSYS_atoi(pValue->ToString().c_str());
+  return pValue->ToInteger();
+}
+
+// static
+float CFXJSE_FormCalcContext::ValueToFloat(CFXJSE_Value* pThis,
+                                           CFXJSE_Value* arg) {
+  if (!arg)
+    return 0.0f;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  if (arg->IsArray()) {
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    arg->GetObjectPropertyByIdx(1, propertyValue.get());
+    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
+    if (propertyValue->IsNull()) {
+      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+      return ValueToFloat(pThis, newPropertyValue.get());
+    }
+    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
+                                     newPropertyValue.get());
+    return ValueToFloat(pThis, newPropertyValue.get());
+  }
+  if (arg->IsObject()) {
+    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    GetObjectDefaultValue(arg, newPropertyValue.get());
+    return ValueToFloat(pThis, newPropertyValue.get());
+  }
+  if (arg->IsString())
+    return strtof(arg->ToString().c_str(), nullptr);
+  if (arg->IsUndefined() || arg->IsEmpty())
+    return 0.0f;
+  return arg->ToFloat();
+}
+
+// static
+double CFXJSE_FormCalcContext::ValueToDouble(CFXJSE_Value* pThis,
+                                             CFXJSE_Value* arg) {
+  if (!arg)
+    return 0;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  if (arg->IsArray()) {
+    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    arg->GetObjectPropertyByIdx(1, propertyValue.get());
+    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
+    if (propertyValue->IsNull()) {
+      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
+      return ValueToDouble(pThis, newPropertyValue.get());
+    }
+    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
+                                     newPropertyValue.get());
+    return ValueToDouble(pThis, newPropertyValue.get());
+  }
+  if (arg->IsObject()) {
+    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    GetObjectDefaultValue(arg, newPropertyValue.get());
+    return ValueToDouble(pThis, newPropertyValue.get());
+  }
+  if (arg->IsString())
+    return strtod(arg->ToString().c_str(), nullptr);
+  if (arg->IsUndefined() || arg->IsEmpty())
+    return 0;
+  return arg->ToDouble();
+}
+
+// static.
+double CFXJSE_FormCalcContext::ExtractDouble(CFXJSE_Value* pThis,
+                                             CFXJSE_Value* src,
+                                             bool* ret) {
+  ASSERT(ret);
+  *ret = true;
+
+  if (!src)
+    return 0;
+
+  if (!src->IsArray())
+    return ValueToDouble(pThis, src);
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  src->GetObjectProperty("length", lengthValue.get());
+  int32_t iLength = lengthValue->ToInteger();
+  if (iLength <= 2) {
+    *ret = false;
+    return 0.0;
+  }
+
+  auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  src->GetObjectPropertyByIdx(1, propertyValue.get());
+  src->GetObjectPropertyByIdx(2, jsObjectValue.get());
+  if (propertyValue->IsNull())
+    return ValueToDouble(pThis, jsObjectValue.get());
+
+  auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+  jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
+                                   newPropertyValue.get());
+  return ValueToDouble(pThis, newPropertyValue.get());
+}
+
+// static
+ByteString CFXJSE_FormCalcContext::ValueToUTF8String(CFXJSE_Value* arg) {
+  if (!arg || arg->IsNull() || arg->IsUndefined() || arg->IsEmpty())
+    return ByteString();
+  if (arg->IsBoolean())
+    return arg->ToBoolean() ? "1" : "0";
+  return arg->ToString();
+}
+
+// static.
+bool CFXJSE_FormCalcContext::Translate(WideStringView wsFormcalc,
+                                       CFX_WideTextBuf* wsJavascript) {
+  if (wsFormcalc.IsEmpty()) {
+    wsJavascript->Clear();
+    return true;
+  }
+
+  CXFA_FMParser parser(wsFormcalc);
+  std::unique_ptr<CXFA_FMAST> ast = parser.Parse();
+  if (!ast || parser.HasError())
+    return false;
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  if (!ast->ToJavaScript(wsJavascript))
+    return false;
+
+  wsJavascript->AppendChar(0);
+  return !CXFA_IsTooBig(wsJavascript);
+}
+
+CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
+                                               CFXJSE_Context* pScriptContext,
+                                               CXFA_Document* pDoc)
+    : m_pIsolate(pScriptIsolate),
+      m_pValue(pdfium::MakeUnique<CFXJSE_Value>(pScriptIsolate)),
+      m_pDocument(pDoc) {
+  m_pValue->SetHostObject(
+      this,
+      CFXJSE_Class::Create(pScriptContext, &kFormCalcFM2JSDescriptor, false));
+}
+
+CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() = default;
+
+CFXJSE_FormCalcContext* CFXJSE_FormCalcContext::AsFormCalcContext() {
+  return this;
+}
+
+void CFXJSE_FormCalcContext::GlobalPropertyGetter(CFXJSE_Value* pValue) {
+  pValue->Assign(m_pValue.get());
+}
+
+// static
+void CFXJSE_FormCalcContext::DotAccessorCommon(CFXJSE_Value* pThis,
+                                               ByteStringView bsFuncName,
+                                               CFXJSE_Arguments& args,
+                                               bool bDotAccessor) {
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
+  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+  int32_t argc = args.GetLength();
+  if (argc < 4 || argc > 5) {
+    pContext->ThrowCompilerErrorException();
+    return;
+  }
+
+  bool bIsStar = true;
+  int32_t iIndexValue = 0;
+  if (argc > 4) {
+    bIsStar = false;
+    iIndexValue = ValueToInteger(pThis, args.GetValue(4).get());
+  }
+
+  const ByteString bsName = args.GetUTF8String(2);
+  const bool bHasNoResolveName = bDotAccessor && bsName.IsEmpty();
+  ByteString bsSomExp = GenerateSomExpression(
+      bsName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar);
+
+  std::unique_ptr<CFXJSE_Value> argAccessor = args.GetValue(0);
+  if (argAccessor->IsArray()) {
+    auto pLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    argAccessor->GetObjectProperty("length", pLengthValue.get());
+    int32_t iLength = pLengthValue->ToInteger();
+    if (iLength < 3) {
+      pContext->ThrowArgumentMismatchException();
+      return;
+    }
+
+    int32_t iCounter = 0;
+    auto hJSObjValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
+    std::vector<std::vector<std::unique_ptr<CFXJSE_Value>>> resolveValues(
+        iLength - 2);
+    bool bAttribute = false;
+    for (int32_t i = 2; i < iLength; i++) {
+      argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get());
+      XFA_RESOLVENODE_RS resolveNodeRS;
+      if (ResolveObjects(pThis, hJSObjValue.get(), bsSomExp.AsStringView(),
+                         &resolveNodeRS, bDotAccessor, bHasNoResolveName)) {
+        ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(),
+                           &resolveValues[i - 2], &bAttribute);
+        iCounter += resolveValues[i - 2].size();
+      }
+    }
+    if (iCounter < 1) {
+      pContext->ThrowPropertyNotInObjectException(
+          WideString::FromUTF8(bsName.AsStringView()),
+          WideString::FromUTF8(bsSomExp.AsStringView()));
+      return;
+    }
+
+    std::vector<std::unique_ptr<CFXJSE_Value>> values;
+    for (int32_t i = 0; i < iCounter + 2; i++)
+      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+
+    values[0]->SetInteger(1);
+    if (bAttribute)
+      values[1]->SetString(bsName.AsStringView());
+    else
+      values[1]->SetNull();
+
+    int32_t iIndex = 2;
+    for (int32_t i = 0; i < iLength - 2; i++) {
+      for (size_t j = 0; j < resolveValues[i].size(); j++) {
+        values[iIndex]->Assign(resolveValues[i][j].get());
+        iIndex++;
+      }
+    }
+    args.GetReturnValue()->SetArray(values);
+    return;
+  }
+
+  XFA_RESOLVENODE_RS resolveNodeRS;
+  bool bRet = false;
+  ByteString bsAccessorName = args.GetUTF8String(1);
+  if (argAccessor->IsObject() ||
+      (argAccessor->IsNull() && bsAccessorName.IsEmpty())) {
+    bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(),
+                          &resolveNodeRS, bDotAccessor, bHasNoResolveName);
+  } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() &&
+             GetObjectForName(pThis, argAccessor.get(),
+                              bsAccessorName.AsStringView())) {
+    bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(),
+                          &resolveNodeRS, bDotAccessor, bHasNoResolveName);
+  }
+  if (!bRet) {
+    pContext->ThrowPropertyNotInObjectException(
+        WideString::FromUTF8(bsName.AsStringView()),
+        WideString::FromUTF8(bsSomExp.AsStringView()));
+    return;
+  }
+
+  std::vector<std::unique_ptr<CFXJSE_Value>> resolveValues;
+  bool bAttribute = false;
+  ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues,
+                     &bAttribute);
+
+  std::vector<std::unique_ptr<CFXJSE_Value>> values;
+  for (size_t i = 0; i < resolveValues.size() + 2; i++)
+    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
+
+  values[0]->SetInteger(1);
+  if (bAttribute)
+    values[1]->SetString(bsName.AsStringView());
+  else
+    values[1]->SetNull();
+
+  for (size_t i = 0; i < resolveValues.size(); i++)
+    values[i + 2]->Assign(resolveValues[i].get());
+
+  args.GetReturnValue()->SetArray(values);
+}
+
+void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException(
+    ByteStringView name) const {
+  ThrowException(WideString::FromUTF8(name) +
+                 WideString::FromASCII(" doesn't have a default property."));
+}
+
+void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const {
+  ThrowException(WideString::FromASCII("Compiler error."));
+}
+
+void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const {
+  ThrowException(WideString::FromASCII("Divide by zero."));
+}
+
+void CFXJSE_FormCalcContext::ThrowServerDeniedException() const {
+  ThrowException(WideString::FromASCII("Server does not permit operation."));
+}
+
+void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException(
+    const WideString& name,
+    const WideString& exp) const {
+  ThrowException(
+      WideString::FromASCII("An attempt was made to reference property '") +
+      name + WideString::FromASCII("' of a non-object in SOM expression ") +
+      exp + L".");
+}
+
+void CFXJSE_FormCalcContext::ThrowParamCountMismatchException(
+    const WideString& method) const {
+  ThrowException(
+      WideString::FromASCII("Incorrect number of parameters calling method '") +
+      method + L"'.");
+}
+
+void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const {
+  ThrowException(WideString::FromASCII(
+      "Argument mismatch in property or function argument."));
+}
+
+void CFXJSE_FormCalcContext::ThrowException(const WideString& str) const {
+  ASSERT(!str.IsEmpty());
+  FXJSE_ThrowMessage(str.ToUTF8().AsStringView());
+}
diff --git a/fxjs/xfa/cfxjse_formcalc_context.h b/fxjs/xfa/cfxjse_formcalc_context.h
new file mode 100644
index 0000000..cf1bc95
--- /dev/null
+++ b/fxjs/xfa/cfxjse_formcalc_context.h
@@ -0,0 +1,415 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
+#define FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/xfa/fxjse.h"
+#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+
+class CFXJSE_Arguments;
+class CFXJSE_Context;
+class CFX_WideTextBuf;
+class CXFA_Document;
+
+class CFXJSE_FormCalcContext final : public CFXJSE_HostObject {
+ public:
+  CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
+                         CFXJSE_Context* pScriptContext,
+                         CXFA_Document* pDoc);
+  ~CFXJSE_FormCalcContext() override;
+
+  // CFXJSE_HostObject:
+  CFXJSE_FormCalcContext* AsFormCalcContext() override;
+
+  static void Abs(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Avg(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Ceil(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Count(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Floor(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Max(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Min(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Mod(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Round(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Sum(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Date(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Date2Num(CFXJSE_Value* pThis,
+                       ByteStringView bsFuncName,
+                       CFXJSE_Arguments& args);
+  static void DateFmt(CFXJSE_Value* pThis,
+                      ByteStringView bsFuncName,
+                      CFXJSE_Arguments& args);
+  static void IsoDate2Num(CFXJSE_Value* pThis,
+                          ByteStringView bsFuncName,
+                          CFXJSE_Arguments& args);
+  static void IsoTime2Num(CFXJSE_Value* pThis,
+                          ByteStringView bsFuncName,
+                          CFXJSE_Arguments& args);
+  static void LocalDateFmt(CFXJSE_Value* pThis,
+                           ByteStringView bsFuncName,
+                           CFXJSE_Arguments& args);
+  static void LocalTimeFmt(CFXJSE_Value* pThis,
+                           ByteStringView bsFuncName,
+                           CFXJSE_Arguments& args);
+  static void Num2Date(CFXJSE_Value* pThis,
+                       ByteStringView bsFuncName,
+                       CFXJSE_Arguments& args);
+  static void Num2GMTime(CFXJSE_Value* pThis,
+                         ByteStringView bsFuncName,
+                         CFXJSE_Arguments& args);
+  static void Num2Time(CFXJSE_Value* pThis,
+                       ByteStringView bsFuncName,
+                       CFXJSE_Arguments& args);
+  static void Time(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Time2Num(CFXJSE_Value* pThis,
+                       ByteStringView bsFuncName,
+                       CFXJSE_Arguments& args);
+  static void TimeFmt(CFXJSE_Value* pThis,
+                      ByteStringView bsFuncName,
+                      CFXJSE_Arguments& args);
+
+  static ByteString Local2IsoDate(CFXJSE_Value* pThis,
+                                  ByteStringView bsDate,
+                                  ByteStringView bsFormat,
+                                  ByteStringView bsLocale);
+  static ByteString IsoDate2Local(CFXJSE_Value* pThis,
+                                  ByteStringView bsDate,
+                                  ByteStringView bsFormat,
+                                  ByteStringView bsLocale);
+  static ByteString IsoTime2Local(CFXJSE_Value* pThis,
+                                  ByteStringView bsTime,
+                                  ByteStringView bsFormat,
+                                  ByteStringView bsLocale);
+  static ByteString GetLocalDateFormat(CFXJSE_Value* pThis,
+                                       int32_t iStyle,
+                                       ByteStringView bsLocale,
+                                       bool bStandard);
+  static ByteString GetLocalTimeFormat(CFXJSE_Value* pThis,
+                                       int32_t iStyle,
+                                       ByteStringView bsLocale,
+                                       bool bStandard);
+  static ByteString GetStandardDateFormat(CFXJSE_Value* pThis,
+                                          int32_t iStyle,
+                                          ByteStringView bsLocale);
+  static ByteString GetStandardTimeFormat(CFXJSE_Value* pThis,
+                                          int32_t iStyle,
+                                          ByteStringView bsLocale);
+  static ByteString Num2AllTime(CFXJSE_Value* pThis,
+                                int32_t iTime,
+                                ByteStringView bsFormat,
+                                ByteStringView bsLocale,
+                                bool bGM);
+
+  static void Apr(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void CTerm(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void FV(CFXJSE_Value* pThis,
+                 ByteStringView bsFuncName,
+                 CFXJSE_Arguments& args);
+  static void IPmt(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void NPV(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Pmt(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void PPmt(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void PV(CFXJSE_Value* pThis,
+                 ByteStringView bsFuncName,
+                 CFXJSE_Arguments& args);
+  static void Rate(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Term(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Choose(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void Exists(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void HasValue(CFXJSE_Value* pThis,
+                       ByteStringView bsFuncName,
+                       CFXJSE_Arguments& args);
+  static void Oneof(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Within(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void If(CFXJSE_Value* pThis,
+                 ByteStringView bsFuncName,
+                 CFXJSE_Arguments& args);
+  static void Eval(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Ref(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void UnitType(CFXJSE_Value* pThis,
+                       ByteStringView bsFuncName,
+                       CFXJSE_Arguments& args);
+  static void UnitValue(CFXJSE_Value* pThis,
+                        ByteStringView bsFuncName,
+                        CFXJSE_Arguments& args);
+
+  static void At(CFXJSE_Value* pThis,
+                 ByteStringView bsFuncName,
+                 CFXJSE_Arguments& args);
+  static void Concat(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void Decode(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void Encode(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void Format(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void Left(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Len(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Lower(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Ltrim(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Parse(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Replace(CFXJSE_Value* pThis,
+                      ByteStringView bsFuncName,
+                      CFXJSE_Arguments& args);
+  static void Right(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Rtrim(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Space(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Str(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Stuff(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void Substr(CFXJSE_Value* pThis,
+                     ByteStringView bsFuncName,
+                     CFXJSE_Arguments& args);
+  static void Uuid(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Upper(CFXJSE_Value* pThis,
+                    ByteStringView bsFuncName,
+                    CFXJSE_Arguments& args);
+  static void WordNum(CFXJSE_Value* pThis,
+                      ByteStringView bsFuncName,
+                      CFXJSE_Arguments& args);
+
+  static void Get(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void Post(CFXJSE_Value* pThis,
+                   ByteStringView bsFuncName,
+                   CFXJSE_Arguments& args);
+  static void Put(CFXJSE_Value* pThis,
+                  ByteStringView bsFuncName,
+                  CFXJSE_Arguments& args);
+  static void assign_value_operator(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args);
+  static void logical_or_operator(CFXJSE_Value* pThis,
+                                  ByteStringView bsFuncName,
+                                  CFXJSE_Arguments& args);
+  static void logical_and_operator(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args);
+  static void equality_operator(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args);
+  static void notequality_operator(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args);
+  static bool fm_ref_equal(CFXJSE_Value* pThis, CFXJSE_Arguments& args);
+  static void less_operator(CFXJSE_Value* pThis,
+                            ByteStringView bsFuncName,
+                            CFXJSE_Arguments& args);
+  static void lessequal_operator(CFXJSE_Value* pThis,
+                                 ByteStringView bsFuncName,
+                                 CFXJSE_Arguments& args);
+  static void greater_operator(CFXJSE_Value* pThis,
+                               ByteStringView bsFuncName,
+                               CFXJSE_Arguments& args);
+  static void greaterequal_operator(CFXJSE_Value* pThis,
+                                    ByteStringView bsFuncName,
+                                    CFXJSE_Arguments& args);
+  static void plus_operator(CFXJSE_Value* pThis,
+                            ByteStringView bsFuncName,
+                            CFXJSE_Arguments& args);
+  static void minus_operator(CFXJSE_Value* pThis,
+                             ByteStringView bsFuncName,
+                             CFXJSE_Arguments& args);
+  static void multiple_operator(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args);
+  static void divide_operator(CFXJSE_Value* pThis,
+                              ByteStringView bsFuncName,
+                              CFXJSE_Arguments& args);
+  static void positive_operator(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args);
+  static void negative_operator(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args);
+  static void logical_not_operator(CFXJSE_Value* pThis,
+                                   ByteStringView bsFuncName,
+                                   CFXJSE_Arguments& args);
+  static void dot_accessor(CFXJSE_Value* pThis,
+                           ByteStringView bsFuncName,
+                           CFXJSE_Arguments& args);
+  static void dotdot_accessor(CFXJSE_Value* pThis,
+                              ByteStringView bsFuncName,
+                              CFXJSE_Arguments& args);
+  static void eval_translation(CFXJSE_Value* pThis,
+                               ByteStringView bsFuncName,
+                               CFXJSE_Arguments& args);
+  static void is_fm_object(CFXJSE_Value* pThis,
+                           ByteStringView bsFuncName,
+                           CFXJSE_Arguments& args);
+  static void is_fm_array(CFXJSE_Value* pThis,
+                          ByteStringView bsFuncName,
+                          CFXJSE_Arguments& args);
+  static void get_fm_value(CFXJSE_Value* pThis,
+                           ByteStringView bsFuncName,
+                           CFXJSE_Arguments& args);
+  static void get_fm_jsobj(CFXJSE_Value* pThis,
+                           ByteStringView bsFuncName,
+                           CFXJSE_Arguments& args);
+  static void fm_var_filter(CFXJSE_Value* pThis,
+                            ByteStringView bsFuncName,
+                            CFXJSE_Arguments& args);
+  static void concat_fm_object(CFXJSE_Value* pThis,
+                               ByteStringView bsFuncName,
+                               CFXJSE_Arguments& args);
+
+  static int32_t hvalue_get_array_length(CFXJSE_Value* pThis,
+                                         CFXJSE_Value* arg);
+  static bool simpleValueCompare(CFXJSE_Value* pThis,
+                                 CFXJSE_Value* firstValue,
+                                 CFXJSE_Value* secondValue);
+  static std::vector<std::unique_ptr<CFXJSE_Value>> unfoldArgs(
+      CFXJSE_Value* pThis,
+      CFXJSE_Arguments& args);
+  static void GetObjectDefaultValue(CFXJSE_Value* pObjectValue,
+                                    CFXJSE_Value* pDefaultValue);
+  static bool SetObjectDefaultValue(CFXJSE_Value* pObjectValue,
+                                    CFXJSE_Value* pNewValue);
+  static ByteString GenerateSomExpression(ByteStringView bsName,
+                                          int32_t iIndexFlags,
+                                          int32_t iIndexValue,
+                                          bool bIsStar);
+  static bool GetObjectForName(CFXJSE_Value* pThis,
+                               CFXJSE_Value* accessorValue,
+                               ByteStringView bsAccessorName);
+  static bool ResolveObjects(CFXJSE_Value* pThis,
+                             CFXJSE_Value* pParentValue,
+                             ByteStringView bsSomExp,
+                             XFA_RESOLVENODE_RS* resolveNodeRS,
+                             bool bdotAccessor,
+                             bool bHasNoResolveName);
+  static void ParseResolveResult(
+      CFXJSE_Value* pThis,
+      const XFA_RESOLVENODE_RS& resolveNodeRS,
+      CFXJSE_Value* pParentValue,
+      std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
+      bool* bAttribute);
+
+  static std::unique_ptr<CFXJSE_Value> GetSimpleValue(CFXJSE_Value* pThis,
+                                                      CFXJSE_Arguments& args,
+                                                      uint32_t index);
+  static bool ValueIsNull(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
+  static int32_t ValueToInteger(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
+  static float ValueToFloat(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
+  static double ValueToDouble(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
+  static ByteString ValueToUTF8String(CFXJSE_Value* pValue);
+  static double ExtractDouble(CFXJSE_Value* pThis,
+                              CFXJSE_Value* src,
+                              bool* ret);
+
+  static bool Translate(WideStringView wsFormcalc,
+                        CFX_WideTextBuf* wsJavascript);
+
+  void GlobalPropertyGetter(CFXJSE_Value* pValue);
+
+ private:
+  static void DotAccessorCommon(CFXJSE_Value* pThis,
+                                ByteStringView bsFuncName,
+                                CFXJSE_Arguments& args,
+                                bool bDotAccessor);
+
+  v8::Isolate* GetScriptRuntime() const { return m_pIsolate.Get(); }
+  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+
+  void ThrowNoDefaultPropertyException(ByteStringView name) const;
+  void ThrowCompilerErrorException() const;
+  void ThrowDivideByZeroException() const;
+  void ThrowServerDeniedException() const;
+  void ThrowPropertyNotInObjectException(const WideString& name,
+                                         const WideString& exp) const;
+  void ThrowArgumentMismatchException() const;
+  void ThrowParamCountMismatchException(const WideString& method) const;
+  void ThrowException(const WideString& str) const;
+
+  UnownedPtr<v8::Isolate> m_pIsolate;
+  std::unique_ptr<CFXJSE_Value> m_pValue;
+  UnownedPtr<CXFA_Document> const m_pDocument;
+};
+
+#endif  // FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
new file mode 100644
index 0000000..19fc730
--- /dev/null
+++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -0,0 +1,1709 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+#include "xfa/fxfa/cxfa_eventparam.h"
+
+class CFXJSE_FormCalcContextEmbedderTest : public XFAJSEmbedderTest {
+ public:
+  CFXJSE_FormCalcContextEmbedderTest() = default;
+  ~CFXJSE_FormCalcContextEmbedderTest() override = default;
+
+ protected:
+  bool ExecuteExpectNull(ByteStringView input) {
+    return Execute(input) && GetValue()->IsNull();
+  }
+};
+
+// TODO(dsinclair): Comment out tests are broken and need to be fixed.
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateEmpty) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  const char input[] = "";
+  EXPECT_TRUE(Execute(input));
+  // TODO(dsinclair): This should probably throw as a blank formcalc script
+  // is invalid.
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateNumber) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  const char input[] = "123";
+  EXPECT_TRUE(Execute(input));
+
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsInteger());
+  EXPECT_EQ(123, value->ToInteger()) << "Program: " << input;
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Numeric) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"123 + 456", 579},
+               {"2 - 3 * 10 / 2 + 7", -6},
+               {"10 * 3 + 5 * 4", 50},
+               {"(5 - \"abc\") * 3", 15},
+               {"\"100\" / 10e1", 1},
+               {"5 + null + 3", 8},
+               // {"if (\"abc\") then\n"
+               //  "  10\n"
+               //  "else\n"
+               //  "  20\n"
+               //  "endif",
+               //  20},
+               // {"3 / 0 + 1", 0},
+               {"-(17)", -17},
+               {"-(-17)", 17},
+               {"+(17)", 17},
+               {"+(-17)", -17},
+               {"if (1 < 2) then\n1\nendif", 1},
+               {"if (\"abc\" > \"def\") then\n"
+                "  1 and 0\n"
+                "else\n"
+                "  0\n"
+                "endif",
+                0}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Strings) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+      {"\"abc\"", "abc"},
+      {"concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")",
+       "The total is 2 dollars and 57 cents."}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Booleans) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    bool result;
+  } tests[] = {{"0 and 1 or 2 > 1", true},
+               {"2 < 3 not 1 == 1", false},
+               {"\"abc\" | 2", true},
+               {"1 or 0", true},
+               {"0 | 0", false},
+               {"0 or 1 | 0 or 0", true},
+               {"1 and 0", false},
+               // {"0 & 0", true},  // TODO(dsinclair) Confirm with Reader.
+               {"0 and 1 & 0 and 0", false},
+               {"not(\"true\")", true},
+               {"not(1)", false},
+               {"3 == 3", true},
+               {"3 <> 4", true},
+               {"\"abc\" eq \"def\"", false},
+               {"\"def\" ne \"abc\"", true},
+               {"5 + 5 == 10", true},
+               {"5 + 5 <> \"10\"", false},
+               {"3 < 3", false},
+               {"3 > 4", false},
+               {"\"abc\" <= \"def\"", true},
+               {"\"def\" > \"abc\"", true},
+               {"12 >= 12", true},
+               {"\"true\" < \"false\"", false}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
+    EXPECT_EQ(tests[i].result, value->ToBoolean())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Abs) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"Abs(1.03)", 1.03f}, {"Abs(-1.03)", 1.03f}, {"Abs(0)", 0.0f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Avg) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"Avg(0, 32, 16)", 16.0f}, {"Avg(2.5, 17, null)", 9.75f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ceil) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"Ceil(2.5875)", 3}, {"Ceil(-5.9)", -5}, {"Ceil(\"abc\")", 0}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Count) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"Count(\"Tony\", \"Blue\", 41)", 3}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Floor) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"Floor(21.3409873)", 21},
+               {"Floor(5.999965342)", 5},
+               {"Floor(3.2 * 15)", 48}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Max) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"Max(234, 15, 107)", 234},
+               {"Max(\"abc\", 15, \"Tony Blue\")", 15},
+               {"Max(\"abc\")", 0}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Min) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"Min(234, 15, 107)", 15},
+               // TODO(dsinclair): Verify with Reader; I believe this should
+               // have a return of 0.
+               // {"Min(\"abc\", 15, \"Tony Blue\")", 15},
+               {"Min(\"abc\")", 0}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Mod) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"Mod(64, -3)", 1}, {"Mod(-13, 3)", -1}, {"Mod(\"abc\", 2)", 0}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Round) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"Round(12.389764537, 4)", 12.3898f},
+               {"Round(20/3, 2)", 6.67f},
+               {"Round(8.9897, \"abc\")", 9.0f},
+               {"Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Sum) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"Sum(2, 4, 6, 8)", 20},
+               {"Sum(-2, 4, -6, 8)", 4},
+               {"Sum(4, 16, \"abc\", 19)", 39}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+// TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Date) {
+//   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+//
+//   TODO(dsinclair): Make compatible with windows.
+//   time_t seconds = time(nullptr);
+//   int days = seconds / (60 * 60 * 24);
+
+//   EXPECT_TRUE(Execute("Date()"));
+
+//   CFXJSE_Value* value = GetValue();
+//   EXPECT_TRUE(value->IsNumber());
+//   EXPECT_EQ(days, value->ToInteger());
+// }
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Date2Num) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {
+      // {"Date2Num(\"Mar 15, 1996\")", 35138},
+      {"Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1},
+      {"Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138},
+      // {"Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277},
+      {"Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296},
+      {"Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")",
+       29}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DateFmt) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+      // {"DateFmt(1)", "M/D/YY"},
+      // {"DateFmt(2, \"fr_CA\")", "YY-MM-DD"},
+      {"DateFmt(3, \"de_DE\")", "D. MMMM YYYY"},
+      // {"DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY"}
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, IsoDate2Num) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"IsoDate2Num(\"1900\")", 1},
+               {"IsoDate2Num(\"1900-01\")", 1},
+               {"IsoDate2Num(\"1900-01-01\")", 1},
+               {"IsoDate2Num(\"19960315T20:20:20\")", 35138},
+               {"IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")", 29}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_IsoTime2Num) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"IsoTime2Num(\"00:00:00Z\")", 1}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, LocalDateFmt) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {// {"LocalDateFmt(1, \"de_DE\")", "tt.MM.uu"},
+               // {"LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj"},
+               {"LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj"},
+               {"LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_LocalTimeFmt) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"LocalTimeFmt(1, \"de_DE\")", "HH:mm"},
+               {"LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss"},
+               {"LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z"},
+               {"LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Num2Date) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+      {"Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900"},
+      {"Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")", "16-Mrz-1996"},
+      // {"Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", "
+      //  "\"YY-MM-DD\", \"fr_CA\"))",
+      //  "Jan 1, 1902"}
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString()) << "Program: " << tests[i].program;
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2GMTime) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {// Broken on Windows only.
+               {"Num2GMTime(1, \"HH:MM:SS\")", "00:00:00"},
+               // Below broken on other platforms.
+               {"Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT"},
+               {"Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")",
+                "12.13 Uhr GMT"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+// TODO(dsinclair): Broken on Mac ...
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2Time) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Num2Time(1, \"HH:MM:SS\")", "00:00:00"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+// TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Time) {
+//   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+//   TODO(dsinclair): Make compatible with windows.
+//   struct timeval tp;
+//   gettimeofday(&tp, nullptr);
+
+//   EXPECT_TRUE(Execute("Time()"));
+
+//   CFXJSE_Value* value = GetValue();
+//   EXPECT_TRUE(value->IsInteger());
+//   EXPECT_EQ(tp.tv_sec * 1000L + tp.tv_usec / 1000, value->ToInteger())
+//       << "Program: Time()";
+// }
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2Num) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {
+      // {"Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1},
+      {"Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")", 47593001}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, TimeFmt) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+      // {"TimeFmt(1)", "h::MM A"},
+      {"TimeFmt(2, \"fr_CA\")", "HH:MM:SS"},
+      {"TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z"},
+      // {"TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z"}
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Apr) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"Apr(35000, 269.50, 360)", 0.08515404566f},
+               {"Apr(210000 * 0.75, 850 + 110, 25 * 26)", 0.07161332404f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, CTerm) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {
+      // {"CTerm(0.02, 1000, 100)", 116.2767474515f},
+      {"CTerm(0.10, 500000, 12000)", 39.13224648502f},
+      // {"CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)", 176.02226044975f}
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, FV) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f},
+               {"FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, IPmt) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f},
+               {"IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f},
+               {"IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_NPV) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"NPV(0.065, 5000)", 4694.83568075117f},
+               {"NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f},
+               {"NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Pmt) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {// {"Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f},
+               {"Pmt(25000, 0.085, 12)", 3403.82145169876f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, PPmt) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {
+      {"PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f},
+      {"PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f},
+      // {"PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, PV) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {
+      {"PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f},
+      // {"PV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Rate) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {{"Rate(12000, 8000, 5)", 0.0844717712f},
+               {"Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Term) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {// {"Term(475, .05, 1500)", 3.00477517728f},
+               {"Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Choose) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+      {"Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")", "Person"},
+      {"Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9"},
+      {"Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")",
+       "F"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Exists) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  EXPECT_TRUE(Execute("Exists(\"hello world\")"));
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsInteger());
+  EXPECT_FALSE(value->ToBoolean());
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, HasValue) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    bool result;
+  } tests[] = {{"HasValue(2)", true}, {"HasValue(\" \")", false}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
+    EXPECT_EQ(tests[i].result, value->ToBoolean())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Oneof) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    bool result;
+  } tests[] = {
+      {"Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true},
+      {"Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")",
+       true},
+      {"Oneof(3, 1, 25)", false},
+      {"Oneof(3, 3, null)", true},
+      {"Oneof(3, null, null)", false},
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
+    EXPECT_EQ(tests[i].result, value->ToBoolean())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Within) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    bool result;
+  } tests[] = {{"Within(\"C\", \"A\", \"D\")", true},
+               {"Within(1.5, 0, 2)", true},
+               {"Within(-1, 0, 2)", false}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
+    EXPECT_EQ(tests[i].result, value->ToBoolean())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Eval) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"eval(\"10*3+5*4\")", 50}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Null) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Null()", "null"},
+               {"Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+
+  EXPECT_TRUE(Execute("Null() + 5"));
+
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsInteger());
+  EXPECT_EQ(5, value->ToInteger());
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ref) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Ref(\"10*3+5*4\")", "10*3+5*4"}, {"Ref(\"hello\")", "hello"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitType) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"UnitType(\"36 in\")", "in"},
+               {"UnitType(\"2.54centimeters\")", "cm"},
+               {"UnitType(\"picas\")", "pt"},
+               {"UnitType(\"2.cm\")", "cm"},
+               {"UnitType(\"2.zero cm\")", "in"},
+               {"UnitType(\"kilometers\")", "in"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitValue) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    float result;
+  } tests[] = {
+      {"UnitValue(\"2in\")", 2.0f}, {"UnitValue(\"2in\", \"cm\")", 5.08f},
+      // {"UnitValue(\"6\", \"pt\")", 432f},
+      // {"UnitType(\"A\", \"cm\")", 0.0f},
+      // {"UnitType(\"5.08cm\", \"kilograms\")", 2.0f}
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsNumber());
+    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, At) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {{"At(\"ABCDEFGH\", \"AB\")", 1},
+               {"At(\"ABCDEFGH\", \"F\")", 6},
+               {"At(23412931298471, 29)", 5}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Concat) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Concat(\"ABC\", \"DEF\")", "ABCDEF"},
+               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"},
+               {"Concat(\"You owe \", WordNum(1154.67, 2), \".\")",
+                "You owe One Thousand One Hundred Fifty-four Dollars And "
+                "Sixty-seven Cents."}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Decode) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+      // HTML
+      {R"(Decode("", "html"))", ""},
+      {R"(Decode("abc&Acirc;xyz", "html"))", "abc\xC3\x82xyz"},
+      {R"(Decode("abc&NoneSuchButVeryLongIndeed;", "html"))", "abc"},
+      {R"(Decode("&#x0041;&AElig;&Aacute;", "html"))", "A\xC3\x86\xC3\x81"},
+      {R"(Decode("xyz&#", "html"))", "xyz"},
+      {R"(Decode("|&zzzzzz;|", "html"))", "||"},
+
+      // XML
+      {R"(Decode("", "xml"))", ""},
+      {R"(Decode("~!@#$%%^&amp;*()_+|`", "xml"))", "~!@#$%%^&*()_+|`"},
+      {R"(Decode("abc&nonesuchbutverylongindeed;", "xml"))", "abc"},
+      {R"(Decode("&quot;&#x45;&lt;&gt;[].&apos;", "xml"))", "\"E<>[].'"},
+      {R"(Decode("xyz&#", "xml"))", "xyz"},
+      {R"(Decode("|&zzzzzz;|", "xml"))", "||"},
+
+      // URL
+      {R"(Decode("", "url"))", ""},
+      {R"(Decode("~%26^&*()_+|`{", "url"))", "~&^&*()_+|`{"},
+      {R"(Decode("~%26^&*()_+|`{", "mbogo"))", "~&^&*()_+|`{"},
+      {R"(Decode("~%26^&*()_+|`{"))", "~&^&*()_+|`{"},
+      {R"(Decode("~%~~"))", ""},
+      {R"(Decode("?%~"))", ""},
+      {R"(Decode("?%"))", "?"},
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Encode) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+    {"Encode(\"X/~&^*<=>?|\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
+    {"Encode(\"X/~&^*<=>?|\", \"mbogo\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
+    {"Encode(\"X/~&^*<=>?|\", \"url\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
+    {"Encode(\"X/~&^*<=>?|\", \"xml\")", "X/~&amp;^*&lt;=&gt;?|"},
+    {"Encode(\"X/~&^*<=>?|\", \"html\")", "X/~&amp;^*&lt;=&gt;?|"},
+
+    {"Encode(\"\\u0022\\u00f5\\ufed0\", \"url\")", "%22%f5%fe%d0"},
+    {"Encode(\"\\u0022\\u00f4\\ufed0\", \"xml\")", "&quot;&#xf4;&#xfed0;"},
+    {"Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")", "&quot;&otilde;&#xfed0;"},
+
+#if !defined(OS_WIN)
+    // Windows wchar_t isn't wide enough to handle these anyways.
+    // TODO(tsepez): fix surrogate encodings.
+    {"Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9"},
+    {"Encode(\"\\uD83D\\uDCA9\", \"xml\")", ""},
+    {"Encode(\"\\uD83D\\uDCA9\", \"html\")", ""},
+#endif  // !defined(OS_WIN)
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Format) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002"},
+               {"Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Left) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Left(\"ABCDEFGH\", 3)", "ABC"},
+               {"Left(\"Tony Blue\", 5)", "Tony "}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Len) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    int result;
+  } tests[] = {
+      {"Len(\"ABCDEFGH\")", 8}, {"Len(4)", 1}, {"Len(Str(4.532, 6, 4))", 6}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsInteger());
+    EXPECT_EQ(tests[i].result, value->ToInteger())
+        << "Program: " << tests[i].program;
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Lower) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Lower(\"ABC\")", "abc"},
+               {"Lower(\"21 Main St.\")", "21 main st."},
+               {"Lower(15)", "15"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+// This is testing for an OOB read, so will likely only fail under ASAN.
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, bug_854623) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  const uint8_t test_string[] = {
+      0x4c, 0x6f, 0x77, 0x65, 0x72, 0x28, 0x22, 0xc3,
+      0x85, 0xc3, 0x85, 0xc3, 0x85, 0x22, 0x29};  // Lower("ÅÅÅ")
+  Execute(ByteString(test_string, sizeof(test_string)).AsStringView());
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ltrim) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Ltrim(\"   ABCD\")", "ABCD"},
+               {"Ltrim(Rtrim(\"    Tony Blue    \"))", "Tony Blue"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Parse) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+
+  EXPECT_TRUE(Execute("Parse(\"$9,999,999.99\", \"$1,234,567.89\")"));
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsNumber());
+  EXPECT_FLOAT_EQ(1234567.89f, value->ToFloat());
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Replace) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Replace(\"Tony Blue\", \"Tony\", \"Chris\")", "Chris Blue"},
+               {"Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH"},
+               {"Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Right) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Right(\"ABCDEFGH\", 3)", "FGH"},
+               {"Right(\"Tony Blue\", 5)", " Blue"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rtrim) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Rtrim(\"ABCD   \")", "ABCD"},
+               {"Rtrim(\"Tony Blue      \t\")", "Tony Blue"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Space) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Space(5)", "     "},
+               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Str) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Str(2.456)", "         2"},
+               {"Str(4.532, 6, 4)", "4.5320"},
+               {"Str(234.458, 4)", " 234"},
+               {"Str(31.2345, 4, 2)", "****"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Stuff) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue"},
+               {"Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH"},
+               {"Stuff(\"members-list@myweb.com\", 0, 0, \"cc:\")",
+                "cc:members-list@myweb.com"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Substr) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  // Test wrong number of parameters.
+  EXPECT_FALSE(Execute("Substr()"));
+  EXPECT_FALSE(Execute("Substr(1)"));
+  EXPECT_FALSE(Execute("Substr(1, 2)"));
+  EXPECT_FALSE(Execute("Substr(1, 2, 3, 4)"));
+
+  // Test null input.
+  EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, 4)"));
+  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, 4)"));
+  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", 0, null)"));
+  EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, 4)"));
+  EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, null)"));
+  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, null)"));
+  EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, null)"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } static const kTests[] = {{"Substr(\"ABCDEFG\", -1, 4)", "ABCD"},
+                             {"Substr(\"ABCDEFG\", 0, 4)", "ABCD"},
+                             {"Substr(\"ABCDEFG\", 3, 4)", "CDEF"},
+                             {"Substr(\"ABCDEFG\", 4, 4)", "DEFG"},
+                             {"Substr(\"ABCDEFG\", 5, 4)", "EFG"},
+                             {"Substr(\"ABCDEFG\", 6, 4)", "FG"},
+                             {"Substr(\"ABCDEFG\", 7, 4)", "G"},
+                             {"Substr(\"ABCDEFG\", 8, 4)", ""},
+                             {"Substr(\"ABCDEFG\", 5, -1)", ""},
+                             {"Substr(\"ABCDEFG\", 5, 0)", ""},
+                             {"Substr(\"ABCDEFG\", 5, 1)", "E"},
+                             {"Substr(\"abcdefghi\", 5, 3)", "efg"},
+                             {"Substr(3214, 2, 1)", "2"},
+                             {"Substr(\"21 Waterloo St.\", 4, 5)", "Water"}};
+
+  for (const auto& test : kTests) {
+    EXPECT_TRUE(Execute(test.program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(test.result, value->ToString().c_str())
+        << "Program: " << test.program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Uuid) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  EXPECT_TRUE(Execute("Uuid()"));
+
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsString());
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Upper) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {{"Upper(\"abc\")", "ABC"},
+               {"Upper(\"21 Main St.\")", "21 MAIN ST."},
+               {"Upper(15)", "15"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, WordNum) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  struct {
+    const char* program;
+    const char* result;
+  } tests[] = {
+      // {"WordNum(123.45)",
+      //  "One Hundred and Twenty-three"},  // This looks like it's wrong in the
+      //                                    // Formcalc document.
+      // {"WordNum(123.45, 1)", "One Hundred and Twenty-three Dollars"},
+      {"WordNum(1154.67, 2)",
+       "One Thousand One Hundred Fifty-four Dollars And Sixty-seven Cents"},
+      {"WordNum(43, 2)", "Forty-three Dollars And Zero Cents"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(Execute(tests[i].program));
+
+    CFXJSE_Value* value = GetValue();
+    EXPECT_TRUE(value->IsString());
+    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
+        << "Program: " << tests[i].program << " Result: '" << value->ToString()
+        << "'";
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Get) {
+  // TODO(dsinclair): Is this supported?
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Post) {
+  // TODO(dsinclair): Is this supported?
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Put) {
+  // TODO(dsinclair): Is this supported?
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, InvalidFunctions) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  const char* const tests[] = {
+      "F()",
+      "()",
+      "()()()",
+      "Round(2.0)()",
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_FALSE(ExecuteSilenceFailure(tests[i]));
+  }
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, MethodCall) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  const char test[] = {"$form.form1.TextField11.getAttribute(\"h\")"};
+  EXPECT_TRUE(Execute(test));
+
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsString());
+  EXPECT_STREQ("12.7mm", value->ToString().c_str());
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, GetXFAEventChange) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  CXFA_EventParam params;
+  params.m_wsChange = L"changed";
+
+  CFXJSE_Engine* context = GetScriptContext();
+  context->SetEventParam(&params);
+
+  const char test[] = {"xfa.event.change"};
+  EXPECT_TRUE(Execute(test));
+
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsString());
+  EXPECT_STREQ("changed", value->ToString().c_str());
+  context->SetEventParam(nullptr);
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventChange) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  CXFA_EventParam params;
+  CFXJSE_Engine* context = GetScriptContext();
+  context->SetEventParam(&params);
+
+  const char test[] = {"xfa.event.change = \"changed\""};
+  EXPECT_TRUE(Execute(test));
+  EXPECT_EQ(L"changed", params.m_wsChange);
+  context->SetEventParam(nullptr);
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventFullTextFails) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  CXFA_EventParam params;
+  params.m_wsFullText = L"Original Full Text";
+
+  CFXJSE_Engine* context = GetScriptContext();
+  context->SetEventParam(&params);
+
+  const char test[] = {"xfa.event.fullText = \"Changed Full Text\""};
+  EXPECT_TRUE(Execute(test));
+  EXPECT_EQ(L"Original Full Text", params.m_wsFullText);
+  context->SetEventParam(nullptr);
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  CXFA_EventParam params;
+  params.m_wsPrevText = L"1234";
+  params.m_iSelStart = 1;
+  params.m_iSelEnd = 3;
+
+  CFXJSE_Engine* context = GetScriptContext();
+  context->SetEventParam(&params);
+
+  // Moving end to start works fine.
+  EXPECT_TRUE(Execute("xfa.event.selEnd = \"1\""));
+  EXPECT_EQ(1, params.m_iSelStart);
+  EXPECT_EQ(1, params.m_iSelEnd);
+
+  // Moving end before end, forces start to move in response.
+  EXPECT_TRUE(Execute("xfa.event.selEnd = \"0\""));
+  EXPECT_EQ(0, params.m_iSelStart);
+  EXPECT_EQ(0, params.m_iSelEnd);
+
+  // Negatives not allowed
+  EXPECT_TRUE(Execute("xfa.event.selEnd = \"-1\""));
+  EXPECT_EQ(0, params.m_iSelStart);
+  EXPECT_EQ(0, params.m_iSelEnd);
+
+  // Negatives not allowed
+  EXPECT_TRUE(Execute("xfa.event.selStart = \"-1\""));
+  EXPECT_EQ(0, params.m_iSelStart);
+  EXPECT_EQ(0, params.m_iSelEnd);
+
+  params.m_iSelEnd = 1;
+
+  // Moving start to end works fine.
+  EXPECT_TRUE(Execute("xfa.event.selStart = \"1\""));
+  EXPECT_EQ(1, params.m_iSelStart);
+  EXPECT_EQ(1, params.m_iSelEnd);
+
+  // Moving start after end moves end.
+  EXPECT_TRUE(Execute("xfa.event.selStart = \"2\""));
+  EXPECT_EQ(2, params.m_iSelStart);
+  EXPECT_EQ(2, params.m_iSelEnd);
+
+  // Setting End past end of string clamps to string length;
+  EXPECT_TRUE(Execute("xfa.event.selEnd = \"20\""));
+  EXPECT_EQ(2, params.m_iSelStart);
+  EXPECT_EQ(4, params.m_iSelEnd);
+
+  // Setting Start past end of string clamps to string length;
+  EXPECT_TRUE(Execute("xfa.event.selStart = \"20\""));
+  EXPECT_EQ(4, params.m_iSelStart);
+  EXPECT_EQ(4, params.m_iSelEnd);
+
+  context->SetEventParam(nullptr);
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  CXFA_EventParam params;
+  params.m_bCancelAction = false;
+
+  CFXJSE_Engine* context = GetScriptContext();
+  context->SetEventParam(&params);
+
+  EXPECT_TRUE(Execute("xfa.event.cancelAction"));
+
+  CFXJSE_Value* value = GetValue();
+  EXPECT_TRUE(value->IsBoolean());
+  EXPECT_FALSE(value->ToBoolean());
+
+  EXPECT_TRUE(Execute("xfa.event.cancelAction = \"true\""));
+  EXPECT_TRUE(params.m_bCancelAction);
+
+  context->SetEventParam(nullptr);
+}
+
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  CXFA_EventParam params;
+  params.m_wsChange = L"g";
+  params.m_wsPrevText = L"abcd";
+  params.m_iSelStart = 1;
+  params.m_iSelEnd = 3;
+
+  CFXJSE_Engine* context = GetScriptContext();
+  context->SetEventParam(&params);
+
+  EXPECT_EQ(L"abcd", params.m_wsPrevText);
+  EXPECT_EQ(L"agd", params.GetNewText());
+  EXPECT_EQ(L"g", params.m_wsChange);
+  EXPECT_EQ(1, params.m_iSelStart);
+  EXPECT_EQ(3, params.m_iSelEnd);
+
+  const char change_event[] = {"xfa.event.change = \"xyz\""};
+  EXPECT_TRUE(Execute(change_event));
+
+  EXPECT_EQ(L"abcd", params.m_wsPrevText);
+  EXPECT_EQ(L"xyz", params.m_wsChange);
+  EXPECT_EQ(L"axyzd", params.GetNewText());
+  EXPECT_EQ(1, params.m_iSelStart);
+  EXPECT_EQ(3, params.m_iSelEnd);
+
+  const char sel_event[] = {"xfa.event.selEnd = \"1\""};
+  EXPECT_TRUE(Execute(sel_event));
+
+  EXPECT_EQ(L"abcd", params.m_wsPrevText);
+  EXPECT_EQ(L"xyz", params.m_wsChange);
+  EXPECT_EQ(L"axyzbcd", params.GetNewText());
+  EXPECT_EQ(1, params.m_iSelStart);
+  EXPECT_EQ(1, params.m_iSelEnd);
+
+  context->SetEventParam(nullptr);
+}
+
+// Should not crash.
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, BUG_1223) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  EXPECT_FALSE(Execute("!.somExpression=0"));
+}
diff --git a/fxjs/xfa/cfxjse_isolatetracker.cpp b/fxjs/xfa/cfxjse_isolatetracker.cpp
new file mode 100644
index 0000000..6a29e44
--- /dev/null
+++ b/fxjs/xfa/cfxjse_isolatetracker.cpp
@@ -0,0 +1,25 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
+
+#include "fxjs/xfa/cfxjse_runtimedata.h"
+
+CFXJSE_ScopeUtil_IsolateHandle::CFXJSE_ScopeUtil_IsolateHandle(
+    v8::Isolate* pIsolate)
+    : m_iscope(pIsolate), m_hscope(pIsolate) {}
+
+CFXJSE_ScopeUtil_IsolateHandle::~CFXJSE_ScopeUtil_IsolateHandle() = default;
+
+CFXJSE_ScopeUtil_IsolateHandleRootContext::
+    CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate)
+    : CFXJSE_ScopeUtil_IsolateHandle(pIsolate),
+      m_cscope(v8::Local<v8::Context>::New(
+          pIsolate,
+          CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext)) {}
+
+CFXJSE_ScopeUtil_IsolateHandleRootContext::
+    ~CFXJSE_ScopeUtil_IsolateHandleRootContext() = default;
diff --git a/fxjs/xfa/cfxjse_isolatetracker.h b/fxjs/xfa/cfxjse_isolatetracker.h
new file mode 100644
index 0000000..020142d
--- /dev/null
+++ b/fxjs/xfa/cfxjse_isolatetracker.h
@@ -0,0 +1,46 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
+#define FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
+
+#include "v8/include/v8.h"
+
+class CFXJSE_ScopeUtil_IsolateHandle {
+ public:
+  explicit CFXJSE_ScopeUtil_IsolateHandle(v8::Isolate* pIsolate);
+  CFXJSE_ScopeUtil_IsolateHandle(const CFXJSE_ScopeUtil_IsolateHandle&) =
+      delete;
+  CFXJSE_ScopeUtil_IsolateHandle& operator=(
+      const CFXJSE_ScopeUtil_IsolateHandle&) = delete;
+  ~CFXJSE_ScopeUtil_IsolateHandle();
+
+ private:
+  void* operator new(size_t size) = delete;
+  void operator delete(void*, size_t) = delete;
+
+  v8::Isolate::Scope m_iscope;
+  v8::HandleScope m_hscope;
+};
+
+class CFXJSE_ScopeUtil_IsolateHandleRootContext final
+    : public CFXJSE_ScopeUtil_IsolateHandle {
+ public:
+  explicit CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate);
+  CFXJSE_ScopeUtil_IsolateHandleRootContext(
+      const CFXJSE_ScopeUtil_IsolateHandleRootContext&) = delete;
+  CFXJSE_ScopeUtil_IsolateHandleRootContext& operator=(
+      const CFXJSE_ScopeUtil_IsolateHandleRootContext&) = delete;
+  ~CFXJSE_ScopeUtil_IsolateHandleRootContext();
+
+ private:
+  void* operator new(size_t size) = delete;
+  void operator delete(void*, size_t) = delete;
+
+  v8::Context::Scope m_cscope;
+};
+
+#endif  // FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
diff --git a/fxjs/xfa/cfxjse_resolveprocessor.cpp b/fxjs/xfa/cfxjse_resolveprocessor.cpp
new file mode 100644
index 0000000..89883d6
--- /dev/null
+++ b/fxjs/xfa/cfxjse_resolveprocessor.cpp
@@ -0,0 +1,735 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_resolveprocessor.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/fx_extension.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_nodehelper.h"
+#include "xfa/fxfa/parser/cxfa_object.h"
+#include "xfa/fxfa/parser/cxfa_occur.h"
+#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+namespace {
+
+void DoPredicateFilter(WideString wsCondition,
+                       size_t iFoundCount,
+                       CFXJSE_ResolveNodeData* pRnd) {
+  ASSERT(iFoundCount == pRnd->m_Objects.size());
+  WideString wsExpression;
+  CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown;
+  if (wsCondition.First(2).EqualsASCII(".[") && wsCondition.Back() == L']')
+    eLangType = CXFA_Script::Type::Formcalc;
+  else if (wsCondition.First(2).EqualsASCII(".(") && wsCondition.Back() == L')')
+    eLangType = CXFA_Script::Type::Javascript;
+  else
+    return;
+
+  wsExpression = wsCondition.Substr(2, wsCondition.GetLength() - 3);
+  for (size_t i = iFoundCount; i > 0; --i) {
+    auto pRetValue =
+        pdfium::MakeUnique<CFXJSE_Value>(pRnd->m_pSC->GetIsolate());
+    bool bRet =
+        pRnd->m_pSC->RunScript(eLangType, wsExpression.AsStringView(),
+                               pRetValue.get(), pRnd->m_Objects[i - 1].Get());
+    if (!bRet || !pRetValue->ToBoolean())
+      pRnd->m_Objects.erase(pRnd->m_Objects.begin() + i - 1);
+  }
+}
+
+}  // namespace
+
+CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor()
+    : m_pNodeHelper(pdfium::MakeUnique<CXFA_NodeHelper>()) {}
+
+CFXJSE_ResolveProcessor::~CFXJSE_ResolveProcessor() = default;
+
+bool CFXJSE_ResolveProcessor::Resolve(CFXJSE_ResolveNodeData& rnd) {
+  if (!rnd.m_CurObject)
+    return false;
+
+  if (!rnd.m_CurObject->IsNode()) {
+    if (rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) {
+      return ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd,
+                                   rnd.m_wsName.AsStringView());
+    }
+    return false;
+  }
+  if (rnd.m_dwStyles & XFA_RESOLVENODE_AnyChild)
+    return ResolveAnyChild(rnd);
+
+  if (rnd.m_wsName.GetLength()) {
+    wchar_t wch = rnd.m_wsName[0];
+    switch (wch) {
+      case '$':
+        return ResolveDollar(rnd);
+      case '!':
+        return ResolveExcalmatory(rnd);
+      case '#':
+        return ResolveNumberSign(rnd);
+      case '*':
+        return ResolveAsterisk(rnd);
+      // TODO(dsinclair): We could probably remove this.
+      case '.':
+        return ResolveAnyChild(rnd);
+      default:
+        break;
+    }
+  }
+  if (rnd.m_uHashName == XFA_HASHCODE_This && rnd.m_nLevel == 0) {
+    rnd.m_Objects.emplace_back(rnd.m_pSC->GetThisObject());
+    return true;
+  }
+  if (rnd.m_CurObject->GetElementType() == XFA_Element::Xfa) {
+    CXFA_Object* pObjNode =
+        rnd.m_pSC->GetDocument()->GetXFAObject(rnd.m_uHashName);
+    if (pObjNode) {
+      rnd.m_Objects.emplace_back(pObjNode);
+    } else if (rnd.m_uHashName == XFA_HASHCODE_Xfa) {
+      rnd.m_Objects.push_back(rnd.m_CurObject);
+    } else if ((rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) &&
+               ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd,
+                                     rnd.m_wsName.AsStringView())) {
+      return true;
+    }
+    if (!rnd.m_Objects.empty())
+      FilterCondition(rnd.m_wsCondition, &rnd);
+
+    return !rnd.m_Objects.empty();
+  }
+  if (!ResolveNormal(rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa)
+    rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot());
+
+  return !rnd.m_Objects.empty();
+}
+
+bool CFXJSE_ResolveProcessor::ResolveAnyChild(CFXJSE_ResolveNodeData& rnd) {
+  CXFA_Node* pParent = ToNode(rnd.m_CurObject.Get());
+  if (!pParent)
+    return false;
+
+  WideStringView wsName = rnd.m_wsName.AsStringView();
+  WideString wsCondition = rnd.m_wsCondition;
+  const bool bClassName = !wsName.IsEmpty() && wsName[0] == '#';
+  CXFA_Node* const pChild =
+      bClassName
+          ? pParent->GetOneChildOfClass(wsName.Last(wsName.GetLength() - 1))
+          : pParent->GetOneChildNamed(wsName);
+  if (!pChild)
+    return false;
+
+  if (wsCondition.IsEmpty()) {
+    rnd.m_Objects.emplace_back(pChild);
+    return true;
+  }
+
+  std::vector<CXFA_Node*> nodes;
+  for (const auto& pObject : rnd.m_Objects)
+    nodes.push_back(pObject->AsNode());
+
+  std::vector<CXFA_Node*> siblings = pChild->GetSiblings(bClassName);
+  nodes.insert(nodes.end(), siblings.begin(), siblings.end());
+  rnd.m_Objects =
+      std::vector<UnownedPtr<CXFA_Object>>(nodes.begin(), nodes.end());
+  FilterCondition(wsCondition, &rnd);
+  return !rnd.m_Objects.empty();
+}
+
+bool CFXJSE_ResolveProcessor::ResolveDollar(CFXJSE_ResolveNodeData& rnd) {
+  WideString wsName = rnd.m_wsName;
+  WideString wsCondition = rnd.m_wsCondition;
+  int32_t iNameLen = wsName.GetLength();
+  if (iNameLen == 1) {
+    rnd.m_Objects.push_back(rnd.m_CurObject);
+    return true;
+  }
+  if (rnd.m_nLevel > 0)
+    return false;
+
+  XFA_HashCode dwNameHash = static_cast<XFA_HashCode>(
+      FX_HashCode_GetW(wsName.AsStringView().Last(iNameLen - 1), false));
+  if (dwNameHash == XFA_HASHCODE_Xfa) {
+    rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot());
+  } else {
+    CXFA_Object* pObjNode = rnd.m_pSC->GetDocument()->GetXFAObject(dwNameHash);
+    if (pObjNode)
+      rnd.m_Objects.emplace_back(pObjNode);
+  }
+  if (!rnd.m_Objects.empty())
+    FilterCondition(wsCondition, &rnd);
+  return !rnd.m_Objects.empty();
+}
+
+bool CFXJSE_ResolveProcessor::ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd) {
+  if (rnd.m_nLevel > 0)
+    return false;
+
+  CXFA_Node* datasets =
+      ToNode(rnd.m_pSC->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
+  if (!datasets)
+    return false;
+
+  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  rndFind.m_CurObject = datasets;
+  rndFind.m_wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1);
+  rndFind.m_uHashName = static_cast<XFA_HashCode>(
+      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
+  rndFind.m_nLevel = rnd.m_nLevel + 1;
+  rndFind.m_dwStyles = XFA_RESOLVENODE_Children;
+  rndFind.m_wsCondition = rnd.m_wsCondition;
+  Resolve(rndFind);
+
+  rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
+                       rndFind.m_Objects.end());
+  return !rnd.m_Objects.empty();
+}
+
+bool CFXJSE_ResolveProcessor::ResolveNumberSign(CFXJSE_ResolveNodeData& rnd) {
+  WideString wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1);
+  WideString wsCondition = rnd.m_wsCondition;
+  CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get());
+  if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
+    return true;
+
+  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  rndFind.m_nLevel = rnd.m_nLevel + 1;
+  rndFind.m_dwStyles = rnd.m_dwStyles;
+  rndFind.m_dwStyles |= XFA_RESOLVENODE_TagName;
+  rndFind.m_dwStyles &= ~XFA_RESOLVENODE_Attributes;
+  rndFind.m_wsName = std::move(wsName);
+  rndFind.m_uHashName = static_cast<XFA_HashCode>(
+      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
+  rndFind.m_wsCondition = wsCondition;
+  rndFind.m_CurObject = curNode;
+  ResolveNormal(rndFind);
+  if (rndFind.m_Objects.empty())
+    return false;
+
+  if (wsCondition.IsEmpty() &&
+      pdfium::ContainsValue(rndFind.m_Objects, curNode)) {
+    rnd.m_Objects.emplace_back(curNode);
+  } else {
+    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
+                         rndFind.m_Objects.end());
+  }
+  return !rnd.m_Objects.empty();
+}
+
+bool CFXJSE_ResolveProcessor::ResolveForAttributeRs(CXFA_Object* curNode,
+                                                    CFXJSE_ResolveNodeData& rnd,
+                                                    WideStringView strAttr) {
+  Optional<XFA_SCRIPTATTRIBUTEINFO> info =
+      XFA_GetScriptAttributeByName(curNode->GetElementType(), strAttr);
+  if (!info.has_value())
+    return false;
+
+  rnd.m_ScriptAttribute = info.value();
+  rnd.m_Objects.emplace_back(curNode);
+  rnd.m_dwFlag = XFA_ResolveNode_RSType_Attribute;
+  return true;
+}
+
+bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) {
+  if (rnd.m_nLevel > 32 || !rnd.m_CurObject->IsNode())
+    return false;
+
+  CXFA_Node* curNode = rnd.m_CurObject->AsNode();
+  size_t nNum = rnd.m_Objects.size();
+  uint32_t dwStyles = rnd.m_dwStyles;
+  WideString& wsName = rnd.m_wsName;
+  XFA_HashCode uNameHash = rnd.m_uHashName;
+  WideString& wsCondition = rnd.m_wsCondition;
+
+  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  rndFind.m_wsName = rnd.m_wsName;
+  rndFind.m_wsCondition = rnd.m_wsCondition;
+  rndFind.m_nLevel = rnd.m_nLevel + 1;
+  rndFind.m_uHashName = uNameHash;
+
+  std::vector<CXFA_Node*> children;
+  std::vector<CXFA_Node*> properties;
+  CXFA_Node* pVariablesNode = nullptr;
+  CXFA_Node* pPageSetNode = nullptr;
+  for (CXFA_Node* pChild = curNode->GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    if (pChild->GetElementType() == XFA_Element::Variables) {
+      pVariablesNode = pChild;
+      continue;
+    }
+    if (pChild->GetElementType() == XFA_Element::PageSet) {
+      pPageSetNode = pChild;
+      continue;
+    }
+    if (curNode->HasProperty(pChild->GetElementType()))
+      properties.push_back(pChild);
+    else
+      children.push_back(pChild);
+  }
+  if ((dwStyles & XFA_RESOLVENODE_Properties) && pVariablesNode) {
+    if (pVariablesNode->GetClassHashCode() == uNameHash) {
+      rnd.m_Objects.emplace_back(pVariablesNode);
+    } else {
+      rndFind.m_CurObject = pVariablesNode;
+      SetStylesForChild(dwStyles, rndFind);
+      WideString wsSaveCondition = std::move(rndFind.m_wsCondition);
+      ResolveNormal(rndFind);
+      rndFind.m_wsCondition = std::move(wsSaveCondition);
+      rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
+                           rndFind.m_Objects.end());
+      rndFind.m_Objects.clear();
+    }
+    if (rnd.m_Objects.size() > nNum) {
+      FilterCondition(wsCondition, &rnd);
+      return !rnd.m_Objects.empty();
+    }
+  }
+
+  if (dwStyles & XFA_RESOLVENODE_Children) {
+    bool bSetFlag = false;
+    if (pPageSetNode && (dwStyles & XFA_RESOLVENODE_Properties))
+      children.push_back(pPageSetNode);
+
+    for (CXFA_Node* child : children) {
+      if (dwStyles & XFA_RESOLVENODE_TagName) {
+        if (child->GetClassHashCode() == uNameHash)
+          rnd.m_Objects.emplace_back(child);
+      } else if (child->GetNameHash() == uNameHash) {
+        rnd.m_Objects.emplace_back(child);
+      }
+
+      if (child->GetElementType() != XFA_Element::PageSet &&
+          child->IsTransparent()) {
+        if (!bSetFlag) {
+          SetStylesForChild(dwStyles, rndFind);
+          bSetFlag = true;
+        }
+        rndFind.m_CurObject = child;
+
+        WideString wsSaveCondition = std::move(rndFind.m_wsCondition);
+        ResolveNormal(rndFind);
+        rndFind.m_wsCondition = std::move(wsSaveCondition);
+        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
+                             rndFind.m_Objects.end());
+        rndFind.m_Objects.clear();
+      }
+    }
+    if (rnd.m_Objects.size() > nNum) {
+      if (!(dwStyles & XFA_RESOLVENODE_ALL)) {
+        std::vector<CXFA_Node*> upArrayNodes;
+        if (curNode->IsTransparent()) {
+          CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get());
+          if (pCurrent) {
+            upArrayNodes =
+                pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName));
+          }
+        }
+        if (upArrayNodes.size() > rnd.m_Objects.size()) {
+          CXFA_Object* pSaveObject = rnd.m_Objects.front().Get();
+          rnd.m_Objects = std::vector<UnownedPtr<CXFA_Object>>(
+              upArrayNodes.begin(), upArrayNodes.end());
+          rnd.m_Objects.front() = pSaveObject;
+        }
+      }
+      FilterCondition(wsCondition, &rnd);
+      return !rnd.m_Objects.empty();
+    }
+  }
+  if (dwStyles & XFA_RESOLVENODE_Attributes) {
+    if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
+      return 1;
+  }
+  if (dwStyles & XFA_RESOLVENODE_Properties) {
+    for (CXFA_Node* pChildProperty : properties) {
+      if (pChildProperty->IsUnnamed()) {
+        if (pChildProperty->GetClassHashCode() == uNameHash)
+          rnd.m_Objects.emplace_back(pChildProperty);
+        continue;
+      }
+      if (pChildProperty->GetNameHash() == uNameHash &&
+          pChildProperty->GetElementType() != XFA_Element::Extras &&
+          pChildProperty->GetElementType() != XFA_Element::Items) {
+        rnd.m_Objects.emplace_back(pChildProperty);
+      }
+    }
+    if (rnd.m_Objects.size() > nNum) {
+      FilterCondition(wsCondition, &rnd);
+      return !rnd.m_Objects.empty();
+    }
+
+    CXFA_Node* pProp = nullptr;
+    if (XFA_Element::Subform == curNode->GetElementType() &&
+        XFA_HASHCODE_Occur == uNameHash) {
+      CXFA_Node* pInstanceManager = curNode->GetInstanceMgrOfSubform();
+      if (pInstanceManager) {
+        pProp = pInstanceManager->JSObject()->GetOrCreateProperty<CXFA_Occur>(
+            0, XFA_Element::Occur);
+      }
+    } else {
+      XFA_Element eType = XFA_GetElementByName(wsName.AsStringView());
+      if (eType == XFA_Element::PageSet) {
+        pProp = curNode->JSObject()->GetProperty<CXFA_Node>(0, eType);
+      } else if (eType != XFA_Element::Unknown) {
+        pProp = curNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eType);
+      }
+    }
+    if (pProp) {
+      rnd.m_Objects.emplace_back(pProp);
+      return !rnd.m_Objects.empty();
+    }
+  }
+
+  CXFA_Node* const parentNode = curNode->GetParent();
+  uint32_t uCurClassHash = curNode->GetClassHashCode();
+  if (!parentNode) {
+    if (uCurClassHash == uNameHash) {
+      rnd.m_Objects.emplace_back(curNode);
+      FilterCondition(wsCondition, &rnd);
+      if (!rnd.m_Objects.empty())
+        return true;
+    }
+    return false;
+  }
+
+  if (dwStyles & XFA_RESOLVENODE_Siblings) {
+    CXFA_Node* child = parentNode->GetFirstChild();
+    uint32_t dwSubStyles =
+        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties;
+    if (dwStyles & XFA_RESOLVENODE_TagName)
+      dwSubStyles |= XFA_RESOLVENODE_TagName;
+    if (dwStyles & XFA_RESOLVENODE_ALL)
+      dwSubStyles |= XFA_RESOLVENODE_ALL;
+
+    rndFind.m_dwStyles = dwSubStyles;
+    while (child) {
+      if (child == curNode) {
+        if (dwStyles & XFA_RESOLVENODE_TagName) {
+          if (uCurClassHash == uNameHash)
+            rnd.m_Objects.emplace_back(curNode);
+        } else {
+          if (child->GetNameHash() == uNameHash) {
+            rnd.m_Objects.emplace_back(curNode);
+            if (rnd.m_nLevel == 0 && wsCondition.IsEmpty()) {
+              rnd.m_Objects.clear();
+              rnd.m_Objects.emplace_back(curNode);
+              return true;
+            }
+          }
+        }
+        child = child->GetNextSibling();
+        continue;
+      }
+
+      if (dwStyles & XFA_RESOLVENODE_TagName) {
+        if (child->GetClassHashCode() == uNameHash)
+          rnd.m_Objects.emplace_back(child);
+      } else if (child->GetNameHash() == uNameHash) {
+        rnd.m_Objects.emplace_back(child);
+      }
+
+      bool bInnerSearch = false;
+      if (parentNode->HasProperty(child->GetElementType())) {
+        if ((child->GetElementType() == XFA_Element::Variables ||
+             child->GetElementType() == XFA_Element::PageSet)) {
+          bInnerSearch = true;
+        }
+      } else if (child->IsTransparent()) {
+        bInnerSearch = true;
+      }
+      if (bInnerSearch) {
+        rndFind.m_CurObject = child;
+        WideString wsOriginCondition = std::move(rndFind.m_wsCondition);
+        uint32_t dwOriginStyle = rndFind.m_dwStyles;
+        rndFind.m_dwStyles = dwOriginStyle | XFA_RESOLVENODE_ALL;
+        ResolveNormal(rndFind);
+        rndFind.m_dwStyles = dwOriginStyle;
+        rndFind.m_wsCondition = std::move(wsOriginCondition);
+        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
+                             rndFind.m_Objects.end());
+        rndFind.m_Objects.clear();
+      }
+      child = child->GetNextSibling();
+    }
+    if (rnd.m_Objects.size() > nNum) {
+      if (parentNode->IsTransparent()) {
+        std::vector<CXFA_Node*> upArrayNodes;
+        CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get());
+        if (pCurrent) {
+          upArrayNodes =
+              pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName));
+        }
+        if (upArrayNodes.size() > rnd.m_Objects.size()) {
+          CXFA_Object* pSaveObject = rnd.m_Objects.front().Get();
+          rnd.m_Objects = std::vector<UnownedPtr<CXFA_Object>>(
+              upArrayNodes.begin(), upArrayNodes.end());
+          rnd.m_Objects.front() = pSaveObject;
+        }
+      }
+      FilterCondition(wsCondition, &rnd);
+      return !rnd.m_Objects.empty();
+    }
+  }
+
+  if (dwStyles & XFA_RESOLVENODE_Parent) {
+    uint32_t dwSubStyles = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent |
+                           XFA_RESOLVENODE_Properties;
+    if (dwStyles & XFA_RESOLVENODE_TagName)
+      dwSubStyles |= XFA_RESOLVENODE_TagName;
+    if (dwStyles & XFA_RESOLVENODE_ALL)
+      dwSubStyles |= XFA_RESOLVENODE_ALL;
+
+    rndFind.m_dwStyles = dwSubStyles;
+    rndFind.m_CurObject = parentNode;
+    rnd.m_pSC->GetUpObjectArray()->push_back(parentNode);
+    ResolveNormal(rndFind);
+    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
+                         rndFind.m_Objects.end());
+    rndFind.m_Objects.clear();
+    if (rnd.m_Objects.size() > nNum)
+      return true;
+  }
+  return false;
+}
+
+bool CFXJSE_ResolveProcessor::ResolveAsterisk(CFXJSE_ResolveNodeData& rnd) {
+  CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get());
+  std::vector<CXFA_Node*> array = curNode->GetNodeListWithFilter(
+      XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties);
+  rnd.m_Objects.insert(rnd.m_Objects.end(), array.begin(), array.end());
+  return !rnd.m_Objects.empty();
+}
+
+int32_t CFXJSE_ResolveProcessor::GetFilter(WideStringView wsExpression,
+                                           int32_t nStart,
+                                           CFXJSE_ResolveNodeData& rnd) {
+  ASSERT(nStart > -1);
+
+  int32_t iLength = wsExpression.GetLength();
+  if (nStart >= iLength)
+    return 0;
+
+  WideString& wsName = rnd.m_wsName;
+  WideString& wsCondition = rnd.m_wsCondition;
+  int32_t nNameCount = 0;
+  int32_t nConditionCount = 0;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> pNameBuf = wsName.GetBuffer(iLength - nStart);
+    pdfium::span<wchar_t> pConditionBuf =
+        wsCondition.GetBuffer(iLength - nStart);
+    pdfium::span<const wchar_t> pSrc = wsExpression.span();
+    std::vector<int32_t> stack;
+    int32_t nType = -1;
+    wchar_t wPrev = 0;
+    wchar_t wCur;
+    bool bIsCondition = false;
+    while (nStart < iLength) {
+      wCur = pSrc[nStart++];
+      if (wCur == '.') {
+        if (nNameCount == 0) {
+          rnd.m_dwStyles |= XFA_RESOLVENODE_AnyChild;
+          continue;
+        }
+        if (wPrev == '\\') {
+          pNameBuf[nNameCount - 1] = wPrev = '.';
+          continue;
+        }
+
+        wchar_t wLookahead = nStart < iLength ? pSrc[nStart] : 0;
+        if (wLookahead != '[' && wLookahead != '(' && nType < 0)
+          break;
+      }
+      if (wCur == '[' || wCur == '(') {
+        bIsCondition = true;
+      } else if (wCur == '.' && nStart < iLength &&
+                 (pSrc[nStart] == '[' || pSrc[nStart] == '(')) {
+        bIsCondition = true;
+      }
+      if (bIsCondition)
+        pConditionBuf[nConditionCount++] = wCur;
+      else
+        pNameBuf[nNameCount++] = wCur;
+
+      if ((nType == 0 && wCur == ']') || (nType == 1 && wCur == ')') ||
+          (nType == 2 && wCur == '"')) {
+        nType = stack.empty() ? -1 : stack.back();
+        if (!stack.empty())
+          stack.pop_back();
+      } else if (wCur == '[') {
+        stack.push_back(nType);
+        nType = 0;
+      } else if (wCur == '(') {
+        stack.push_back(nType);
+        nType = 1;
+      } else if (wCur == '"') {
+        stack.push_back(nType);
+        nType = 2;
+      }
+      wPrev = wCur;
+    }
+    if (!stack.empty())
+      return -1;
+  }
+  wsName.ReleaseBuffer(nNameCount);
+  wsCondition.ReleaseBuffer(nConditionCount);
+  wsName.Trim();
+  wsCondition.Trim();
+  rnd.m_uHashName =
+      static_cast<XFA_HashCode>(FX_HashCode_GetW(wsName.AsStringView(), false));
+  return nStart;
+}
+
+void CFXJSE_ResolveProcessor::ConditionArray(size_t iCurIndex,
+                                             WideString wsCondition,
+                                             size_t iFoundCount,
+                                             CFXJSE_ResolveNodeData* pRnd) {
+  size_t iLen = wsCondition.GetLength();
+  bool bRelative = false;
+  bool bAll = false;
+  size_t i = 1;
+  for (; i < iLen; ++i) {
+    wchar_t ch = wsCondition[i];
+    if (ch == ' ')
+      continue;
+    if (ch == '+' || ch == '-')
+      bRelative = true;
+    else if (ch == '*')
+      bAll = true;
+
+    break;
+  }
+  if (bAll) {
+    if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
+      if (pRnd->m_dwStyles & XFA_RESOLVENODE_Bind) {
+        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+        m_pNodeHelper->m_iCreateCount = 1;
+        pRnd->m_Objects.clear();
+        m_pNodeHelper->m_iCurAllStart = -1;
+        m_pNodeHelper->m_pAllStartParent = nullptr;
+      } else if (m_pNodeHelper->m_iCurAllStart == -1) {
+        m_pNodeHelper->m_iCurAllStart = m_iCurStart;
+        m_pNodeHelper->m_pAllStartParent = ToNode(pRnd->m_CurObject.Get());
+      }
+    } else if (pRnd->m_dwStyles & XFA_RESOLVENODE_BindNew) {
+      if (m_pNodeHelper->m_iCurAllStart == -1)
+        m_pNodeHelper->m_iCurAllStart = m_iCurStart;
+    }
+    return;
+  }
+  if (iFoundCount == 1 && !iLen)
+    return;
+
+  int32_t iIndex = wsCondition.Substr(i, iLen - 1 - i).GetInteger();
+  if (bRelative)
+    iIndex += iCurIndex;
+
+  if (iIndex < 0 || static_cast<size_t>(iIndex) >= iFoundCount) {
+    if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
+      m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+      m_pNodeHelper->m_iCreateCount = iIndex - iFoundCount + 1;
+    }
+    pRnd->m_Objects.clear();
+  } else {
+    pRnd->m_Objects =
+        std::vector<UnownedPtr<CXFA_Object>>(1, pRnd->m_Objects[iIndex]);
+  }
+}
+
+void CFXJSE_ResolveProcessor::FilterCondition(WideString wsCondition,
+                                              CFXJSE_ResolveNodeData* pRnd) {
+  size_t iCurIndex = 0;
+  const std::vector<CXFA_Node*>* pArray = pRnd->m_pSC->GetUpObjectArray();
+  if (!pArray->empty()) {
+    CXFA_Node* pNode = pArray->back();
+    bool bIsProperty = pNode->IsProperty();
+    bool bIsClassIndex =
+        pNode->IsUnnamed() ||
+        (bIsProperty && pNode->GetElementType() != XFA_Element::PageSet);
+    iCurIndex = pNode->GetIndex(bIsProperty, bIsClassIndex);
+  }
+
+  size_t iFoundCount = pRnd->m_Objects.size();
+  wsCondition.Trim();
+
+  int32_t iLen = wsCondition.GetLength();
+  if (!iLen) {
+    if (pRnd->m_dwStyles & XFA_RESOLVENODE_ALL)
+      return;
+    if (iFoundCount == 1)
+      return;
+
+    if (iFoundCount <= iCurIndex) {
+      if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
+        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+        m_pNodeHelper->m_iCreateCount = iCurIndex - iFoundCount + 1;
+      }
+      pRnd->m_Objects.clear();
+      return;
+    }
+
+    pRnd->m_Objects =
+        std::vector<UnownedPtr<CXFA_Object>>(1, pRnd->m_Objects[iCurIndex]);
+    return;
+  }
+
+  wchar_t wTypeChar = wsCondition[0];
+  switch (wTypeChar) {
+    case '[':
+      ConditionArray(iCurIndex, wsCondition, iFoundCount, pRnd);
+      return;
+    case '.':
+      if (iLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '('))
+        DoPredicateFilter(wsCondition, iFoundCount, pRnd);
+      return;
+    case '(':
+    case '"':
+    default:
+      return;
+  }
+}
+
+void CFXJSE_ResolveProcessor::SetStylesForChild(uint32_t dwParentStyles,
+                                                CFXJSE_ResolveNodeData& rnd) {
+  uint32_t dwSubStyles = XFA_RESOLVENODE_Children;
+  if (dwParentStyles & XFA_RESOLVENODE_TagName)
+    dwSubStyles |= XFA_RESOLVENODE_TagName;
+
+  dwSubStyles &= ~XFA_RESOLVENODE_Parent;
+  dwSubStyles &= ~XFA_RESOLVENODE_Siblings;
+  dwSubStyles &= ~XFA_RESOLVENODE_Properties;
+  dwSubStyles |= XFA_RESOLVENODE_ALL;
+  rnd.m_dwStyles = dwSubStyles;
+}
+
+void CFXJSE_ResolveProcessor::SetIndexDataBind(WideString& wsNextCondition,
+                                               int32_t& iIndex,
+                                               int32_t iCount) {
+  if (m_pNodeHelper->CreateNodeForCondition(wsNextCondition)) {
+    if (m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) {
+      iIndex = 0;
+    } else {
+      iIndex = iCount - 1;
+    }
+  } else {
+    iIndex = iCount - 1;
+  }
+}
+
+CFXJSE_ResolveNodeData::CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC)
+    : m_pSC(pSC) {}
+
+CFXJSE_ResolveNodeData::~CFXJSE_ResolveNodeData() = default;
diff --git a/fxjs/xfa/cfxjse_resolveprocessor.h b/fxjs/xfa/cfxjse_resolveprocessor.h
new file mode 100644
index 0000000..6e4fcba
--- /dev/null
+++ b/fxjs/xfa/cfxjse_resolveprocessor.h
@@ -0,0 +1,76 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_
+#define FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "xfa/fxfa/fxfa_basic.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+
+class CXFA_NodeHelper;
+
+class CFXJSE_ResolveNodeData {
+ public:
+  explicit CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC);
+  ~CFXJSE_ResolveNodeData();
+
+  UnownedPtr<CFXJSE_Engine> const m_pSC;
+  UnownedPtr<CXFA_Object> m_CurObject;
+  WideString m_wsName;
+  WideString m_wsCondition;
+  XFA_HashCode m_uHashName = XFA_HASHCODE_None;
+  int32_t m_nLevel = 0;
+  uint32_t m_dwStyles = XFA_RESOLVENODE_Children;
+  XFA_ResolveNode_RSType m_dwFlag = XFA_ResolveNode_RSType_Nodes;
+  std::vector<UnownedPtr<CXFA_Object>> m_Objects;
+  XFA_SCRIPTATTRIBUTEINFO m_ScriptAttribute;
+};
+
+class CFXJSE_ResolveProcessor {
+ public:
+  CFXJSE_ResolveProcessor();
+  ~CFXJSE_ResolveProcessor();
+
+  bool Resolve(CFXJSE_ResolveNodeData& rnd);
+  int32_t GetFilter(WideStringView wsExpression,
+                    int32_t nStart,
+                    CFXJSE_ResolveNodeData& rnd);
+  void SetIndexDataBind(WideString& wsNextCondition,
+                        int32_t& iIndex,
+                        int32_t iCount);
+  void SetCurStart(int32_t start) { m_iCurStart = start; }
+
+  CXFA_NodeHelper* GetNodeHelper() { return m_pNodeHelper.get(); }
+
+ private:
+  bool ResolveForAttributeRs(CXFA_Object* curNode,
+                             CFXJSE_ResolveNodeData& rnd,
+                             WideStringView strAttr);
+  bool ResolveAnyChild(CFXJSE_ResolveNodeData& rnd);
+  bool ResolveDollar(CFXJSE_ResolveNodeData& rnd);
+  bool ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd);
+  bool ResolveNumberSign(CFXJSE_ResolveNodeData& rnd);
+  bool ResolveAsterisk(CFXJSE_ResolveNodeData& rnd);
+  bool ResolveNormal(CFXJSE_ResolveNodeData& rnd);
+  void SetStylesForChild(uint32_t dwParentStyles, CFXJSE_ResolveNodeData& rnd);
+
+  void ConditionArray(size_t iCurIndex,
+                      WideString wsCondition,
+                      size_t iFoundCount,
+                      CFXJSE_ResolveNodeData* pRnd);
+  void FilterCondition(WideString wsCondition, CFXJSE_ResolveNodeData* pRnd);
+
+  int32_t m_iCurStart = 0;
+  std::unique_ptr<CXFA_NodeHelper> const m_pNodeHelper;
+};
+
+#endif  // FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_
diff --git a/fxjs/xfa/cfxjse_runtimedata.cpp b/fxjs/xfa/cfxjse_runtimedata.cpp
new file mode 100644
index 0000000..0478e3e
--- /dev/null
+++ b/fxjs/xfa/cfxjse_runtimedata.cpp
@@ -0,0 +1,55 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_runtimedata.h"
+
+#include <utility>
+
+#include "fxjs/cfxjs_engine.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
+
+CFXJSE_RuntimeData::CFXJSE_RuntimeData() = default;
+
+CFXJSE_RuntimeData::~CFXJSE_RuntimeData() = default;
+
+std::unique_ptr<CFXJSE_RuntimeData> CFXJSE_RuntimeData::Create(
+    v8::Isolate* pIsolate) {
+  std::unique_ptr<CFXJSE_RuntimeData> pRuntimeData(new CFXJSE_RuntimeData());
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+
+  v8::Local<v8::FunctionTemplate> hFuncTemplate =
+      v8::FunctionTemplate::New(pIsolate);
+
+  v8::Local<v8::ObjectTemplate> hGlobalTemplate =
+      hFuncTemplate->InstanceTemplate();
+  hGlobalTemplate->Set(
+      v8::Symbol::GetToStringTag(pIsolate),
+      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
+          .ToLocalChecked());
+
+  v8::Local<v8::Context> hContext =
+      v8::Context::New(pIsolate, 0, hGlobalTemplate);
+
+  ASSERT(hContext->Global()->InternalFieldCount() == 0);
+  ASSERT(hContext->Global()
+             ->GetPrototype()
+             .As<v8::Object>()
+             ->InternalFieldCount() == 0);
+
+  hContext->SetSecurityToken(v8::External::New(pIsolate, pIsolate));
+  pRuntimeData->m_hRootContextGlobalTemplate.Reset(pIsolate, hFuncTemplate);
+  pRuntimeData->m_hRootContext.Reset(pIsolate, hContext);
+  return pRuntimeData;
+}
+
+CFXJSE_RuntimeData* CFXJSE_RuntimeData::Get(v8::Isolate* pIsolate) {
+  FXJS_PerIsolateData::SetUp(pIsolate);
+
+  FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(pIsolate);
+  if (!pData->m_pFXJSERuntimeData)
+    pData->m_pFXJSERuntimeData = CFXJSE_RuntimeData::Create(pIsolate);
+  return static_cast<CFXJSE_RuntimeData*>(pData->m_pFXJSERuntimeData.get());
+}
diff --git a/fxjs/xfa/cfxjse_runtimedata.h b/fxjs/xfa/cfxjse_runtimedata.h
new file mode 100644
index 0000000..148b01e
--- /dev/null
+++ b/fxjs/xfa/cfxjse_runtimedata.h
@@ -0,0 +1,34 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_RUNTIMEDATA_H_
+#define FXJS_XFA_CFXJSE_RUNTIMEDATA_H_
+
+#include <memory>
+
+#include "fxjs/cfxjs_engine.h"
+#include "v8/include/v8.h"
+
+class CFXJSE_RuntimeData : public FXJS_PerIsolateData::ExtensionIface {
+ public:
+  ~CFXJSE_RuntimeData() override;
+
+  static CFXJSE_RuntimeData* Get(v8::Isolate* pIsolate);
+
+  v8::Global<v8::FunctionTemplate> m_hRootContextGlobalTemplate;
+  v8::Global<v8::Context> m_hRootContext;
+
+ protected:
+  CFXJSE_RuntimeData();
+
+  static std::unique_ptr<CFXJSE_RuntimeData> Create(v8::Isolate* pIsolate);
+
+ private:
+  CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete;
+  CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete;
+};
+
+#endif  // FXJS_XFA_CFXJSE_RUNTIMEDATA_H_
diff --git a/fxjs/xfa/cfxjse_value.cpp b/fxjs/xfa/cfxjse_value.cpp
new file mode 100644
index 0000000..1d4cec4
--- /dev/null
+++ b/fxjs/xfa/cfxjse_value.cpp
@@ -0,0 +1,470 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/cfxjse_value.h"
+
+#include <math.h>
+
+#include "fxjs/xfa/cfxjse_class.h"
+#include "fxjs/xfa/cfxjse_context.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
+
+namespace {
+
+double ftod(float fNumber) {
+  static_assert(sizeof(float) == 4, "float of incorrect size");
+
+  uint32_t nFloatBits = (uint32_t&)fNumber;
+  uint8_t nExponent = (uint8_t)(nFloatBits >> 23);
+  if (nExponent == 0 || nExponent == 255)
+    return fNumber;
+
+  int8_t nErrExp = nExponent - 150;
+  if (nErrExp >= 0)
+    return fNumber;
+
+  double dwError = pow(2.0, nErrExp), dwErrorHalf = dwError / 2;
+  double dNumber = fNumber, dNumberAbs = fabs(fNumber);
+  double dNumberAbsMin = dNumberAbs - dwErrorHalf,
+         dNumberAbsMax = dNumberAbs + dwErrorHalf;
+  int32_t iErrPos = 0;
+  if (floor(dNumberAbsMin) == floor(dNumberAbsMax)) {
+    dNumberAbsMin = fmod(dNumberAbsMin, 1.0);
+    dNumberAbsMax = fmod(dNumberAbsMax, 1.0);
+    int32_t iErrPosMin = 1, iErrPosMax = 38;
+    do {
+      int32_t iMid = (iErrPosMin + iErrPosMax) / 2;
+      double dPow = pow(10.0, iMid);
+      if (floor(dNumberAbsMin * dPow) == floor(dNumberAbsMax * dPow)) {
+        iErrPosMin = iMid + 1;
+      } else {
+        iErrPosMax = iMid;
+      }
+    } while (iErrPosMin < iErrPosMax);
+    iErrPos = iErrPosMax;
+  }
+  double dPow = pow(10.0, iErrPos);
+  return fNumber < 0 ? ceil(dNumber * dPow - 0.5) / dPow
+                     : floor(dNumber * dPow + 0.5) / dPow;
+}
+
+}  // namespace
+
+void FXJSE_ThrowMessage(ByteStringView utf8Message) {
+  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
+  ASSERT(pIsolate);
+
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::String> hMessage =
+      v8::String::NewFromUtf8(pIsolate, utf8Message.unterminated_c_str(),
+                              v8::NewStringType::kNormal,
+                              utf8Message.GetLength())
+          .ToLocalChecked();
+  v8::Local<v8::Value> hError = v8::Exception::Error(hMessage);
+  pIsolate->ThrowException(hError);
+}
+
+CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
+
+CFXJSE_Value::~CFXJSE_Value() {}
+
+CFXJSE_HostObject* CFXJSE_Value::ToHostObject() const {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> pValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  ASSERT(!pValue.IsEmpty());
+  if (!pValue->IsObject())
+    return nullptr;
+
+  return FXJSE_RetrieveObjectBinding(pValue.As<v8::Object>());
+}
+
+void CFXJSE_Value::SetHostObject(CFXJSE_HostObject* lpObject,
+                                 CFXJSE_Class* pClass) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::FunctionTemplate> hClass =
+      v8::Local<v8::FunctionTemplate>::New(GetIsolate(), pClass->m_hTemplate);
+  v8::Local<v8::Object> hObject =
+      hClass->InstanceTemplate()
+          ->NewInstance(GetIsolate()->GetCurrentContext())
+          .ToLocalChecked();
+  FXJSE_UpdateObjectBinding(hObject, lpObject);
+  m_hValue.Reset(GetIsolate(), hObject);
+}
+
+void CFXJSE_Value::ClearHostObject() {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  FXJSE_ClearObjectBinding(m_hValue.Get(GetIsolate()).As<v8::Object>());
+  v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
+  m_hValue.Reset(GetIsolate(), hValue);
+}
+
+void CFXJSE_Value::SetArray(
+    const std::vector<std::unique_ptr<CFXJSE_Value>>& values) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Array> hArrayObject =
+      v8::Array::New(GetIsolate(), values.size());
+  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
+  uint32_t count = 0;
+  for (auto& v : values) {
+    if (v->IsEmpty())
+      v->SetUndefined();
+    hArrayObject
+        ->Set(
+            context, count++,
+            v8::Local<v8::Value>::New(GetIsolate(), v.get()->DirectGetValue()))
+        .FromJust();
+  }
+  m_hValue.Reset(GetIsolate(), hArrayObject);
+}
+
+void CFXJSE_Value::SetFloat(float fFloat) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> pValue = v8::Number::New(GetIsolate(), ftod(fFloat));
+  m_hValue.Reset(GetIsolate(), pValue);
+}
+
+bool CFXJSE_Value::SetObjectProperty(ByteStringView szPropName,
+                                     CFXJSE_Value* lpPropValue) {
+  ASSERT(lpPropValue);
+  if (lpPropValue->IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hObject =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  if (!hObject->IsObject())
+    return false;
+
+  v8::Local<v8::String> hPropName =
+      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
+                              v8::NewStringType::kNormal,
+                              szPropName.GetLength())
+          .ToLocalChecked();
+  v8::Local<v8::Value> hPropValue =
+      v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->DirectGetValue());
+  v8::Maybe<bool> result = hObject.As<v8::Object>()->Set(
+      GetIsolate()->GetCurrentContext(), hPropName, hPropValue);
+  return result.IsJust() && result.FromJust();
+}
+
+bool CFXJSE_Value::GetObjectProperty(ByteStringView szPropName,
+                                     CFXJSE_Value* lpPropValue) {
+  ASSERT(lpPropValue);
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hObject =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  if (!hObject->IsObject())
+    return false;
+
+  v8::Local<v8::String> hPropName =
+      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
+                              v8::NewStringType::kNormal,
+                              szPropName.GetLength())
+          .ToLocalChecked();
+  v8::Local<v8::Value> hPropValue =
+      hObject.As<v8::Object>()
+          ->Get(GetIsolate()->GetCurrentContext(), hPropName)
+          .ToLocalChecked();
+  lpPropValue->ForceSetValue(hPropValue);
+  return true;
+}
+
+bool CFXJSE_Value::GetObjectPropertyByIdx(uint32_t uPropIdx,
+                                          CFXJSE_Value* lpPropValue) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hObject =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  if (!hObject->IsObject())
+    return false;
+
+  v8::Local<v8::Value> hPropValue =
+      hObject.As<v8::Object>()
+          ->Get(GetIsolate()->GetCurrentContext(), uPropIdx)
+          .ToLocalChecked();
+  lpPropValue->ForceSetValue(hPropValue);
+  return true;
+}
+
+bool CFXJSE_Value::DeleteObjectProperty(ByteStringView szPropName) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hObject =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  if (!hObject->IsObject())
+    return false;
+
+  v8::Local<v8::String> hPropName =
+      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
+                              v8::NewStringType::kNormal,
+                              szPropName.GetLength())
+          .ToLocalChecked();
+  return hObject.As<v8::Object>()
+      ->Delete(GetIsolate()->GetCurrentContext(), hPropName)
+      .FromJust();
+}
+
+bool CFXJSE_Value::HasObjectOwnProperty(ByteStringView szPropName,
+                                        bool bUseTypeGetter) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hObject =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  if (!hObject->IsObject())
+    return false;
+
+  v8::Local<v8::String> hKey =
+      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
+                              v8::NewStringType::kNormal,
+                              szPropName.GetLength())
+          .ToLocalChecked();
+  return hObject.As<v8::Object>()
+             ->HasRealNamedProperty(GetIsolate()->GetCurrentContext(), hKey)
+             .FromJust() ||
+         (bUseTypeGetter &&
+          hObject.As<v8::Object>()
+              ->HasOwnProperty(GetIsolate()->GetCurrentContext(), hKey)
+              .FromMaybe(false));
+}
+
+bool CFXJSE_Value::SetObjectOwnProperty(ByteStringView szPropName,
+                                        CFXJSE_Value* lpPropValue) {
+  ASSERT(lpPropValue);
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hObject =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  if (!hObject->IsObject())
+    return false;
+
+  v8::Local<v8::String> hPropName =
+      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
+                              v8::NewStringType::kNormal,
+                              szPropName.GetLength())
+          .ToLocalChecked();
+  v8::Local<v8::Value> pValue =
+      v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->m_hValue);
+  return hObject.As<v8::Object>()
+      ->DefineOwnProperty(GetIsolate()->GetCurrentContext(), hPropName, pValue)
+      .FromMaybe(false);
+}
+
+bool CFXJSE_Value::SetFunctionBind(CFXJSE_Value* lpOldFunction,
+                                   CFXJSE_Value* lpNewThis) {
+  ASSERT(lpOldFunction);
+  ASSERT(lpNewThis);
+
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> rgArgs[2];
+  v8::Local<v8::Value> hOldFunction =
+      v8::Local<v8::Value>::New(GetIsolate(), lpOldFunction->DirectGetValue());
+  if (hOldFunction.IsEmpty() || !hOldFunction->IsFunction())
+    return false;
+
+  rgArgs[0] = hOldFunction;
+  v8::Local<v8::Value> hNewThis =
+      v8::Local<v8::Value>::New(GetIsolate(), lpNewThis->DirectGetValue());
+  if (hNewThis.IsEmpty())
+    return false;
+
+  rgArgs[1] = hNewThis;
+  v8::Local<v8::String> hBinderFuncSource =
+      v8::String::NewFromUtf8(GetIsolate(),
+                              "(function (oldfunction, newthis) { return "
+                              "oldfunction.bind(newthis); })",
+                              v8::NewStringType::kNormal)
+          .ToLocalChecked();
+  v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
+  v8::Local<v8::Function> hBinderFunc =
+      v8::Script::Compile(hContext, hBinderFuncSource)
+          .ToLocalChecked()
+          ->Run(hContext)
+          .ToLocalChecked()
+          .As<v8::Function>();
+  v8::Local<v8::Value> hBoundFunction =
+      hBinderFunc->Call(hContext, hContext->Global(), 2, rgArgs)
+          .ToLocalChecked();
+  if (hBoundFunction.IsEmpty() || !hBoundFunction->IsFunction())
+    return false;
+
+  m_hValue.Reset(GetIsolate(), hBoundFunction);
+  return true;
+}
+
+bool CFXJSE_Value::IsEmpty() const {
+  return m_hValue.IsEmpty();
+}
+
+bool CFXJSE_Value::IsUndefined() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsUndefined();
+}
+
+bool CFXJSE_Value::IsNull() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsNull();
+}
+
+bool CFXJSE_Value::IsBoolean() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsBoolean();
+}
+
+bool CFXJSE_Value::IsString() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsString();
+}
+
+bool CFXJSE_Value::IsNumber() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsNumber();
+}
+
+bool CFXJSE_Value::IsInteger() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsInt32();
+}
+
+bool CFXJSE_Value::IsObject() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsObject();
+}
+
+bool CFXJSE_Value::IsArray() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsArray();
+}
+
+bool CFXJSE_Value::IsFunction() const {
+  if (IsEmpty())
+    return false;
+
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->IsFunction();
+}
+
+bool CFXJSE_Value::ToBoolean() const {
+  ASSERT(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->BooleanValue(GetIsolate());
+}
+
+float CFXJSE_Value::ToFloat() const {
+  ASSERT(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return static_cast<float>(
+      hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
+}
+
+double CFXJSE_Value::ToDouble() const {
+  ASSERT(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0);
+}
+
+int32_t CFXJSE_Value::ToInteger() const {
+  ASSERT(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  return static_cast<int32_t>(
+      hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
+}
+
+ByteString CFXJSE_Value::ToString() const {
+  ASSERT(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  v8::Local<v8::String> hString =
+      hValue->ToString(GetIsolate()->GetCurrentContext()).ToLocalChecked();
+  v8::String::Utf8Value hStringVal(GetIsolate(), hString);
+  return ByteString(*hStringVal);
+}
+
+void CFXJSE_Value::SetUndefined() {
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue = v8::Undefined(GetIsolate());
+  m_hValue.Reset(GetIsolate(), hValue);
+}
+
+void CFXJSE_Value::SetNull() {
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
+  m_hValue.Reset(GetIsolate(), hValue);
+}
+
+void CFXJSE_Value::SetBoolean(bool bBoolean) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue = v8::Boolean::New(GetIsolate(), !!bBoolean);
+  m_hValue.Reset(GetIsolate(), hValue);
+}
+
+void CFXJSE_Value::SetInteger(int32_t nInteger) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue = v8::Integer::New(GetIsolate(), nInteger);
+  m_hValue.Reset(GetIsolate(), hValue);
+}
+
+void CFXJSE_Value::SetDouble(double dDouble) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue = v8::Number::New(GetIsolate(), dDouble);
+  m_hValue.Reset(GetIsolate(), hValue);
+}
+
+void CFXJSE_Value::SetString(ByteStringView szString) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
+  v8::Local<v8::Value> hValue =
+      v8::String::NewFromUtf8(GetIsolate(), szString.unterminated_c_str(),
+                              v8::NewStringType::kNormal, szString.GetLength())
+          .ToLocalChecked();
+  m_hValue.Reset(GetIsolate(), hValue);
+}
diff --git a/fxjs/xfa/cfxjse_value.h b/fxjs/xfa/cfxjse_value.h
new file mode 100644
index 0000000..44cc58c
--- /dev/null
+++ b/fxjs/xfa/cfxjse_value.h
@@ -0,0 +1,91 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_CFXJSE_VALUE_H_
+#define FXJS_XFA_CFXJSE_VALUE_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "v8/include/v8.h"
+
+class CFXJSE_Class;
+class CFXJSE_HostObject;
+
+class CFXJSE_Value {
+ public:
+  explicit CFXJSE_Value(v8::Isolate* pIsolate);
+  ~CFXJSE_Value();
+
+  bool IsEmpty() const;
+  bool IsUndefined() const;
+  bool IsNull() const;
+  bool IsBoolean() const;
+  bool IsString() const;
+  bool IsNumber() const;
+  bool IsInteger() const;
+  bool IsObject() const;
+  bool IsArray() const;
+  bool IsFunction() const;
+  bool ToBoolean() const;
+  float ToFloat() const;
+  double ToDouble() const;
+  int32_t ToInteger() const;
+  ByteString ToString() const;
+  WideString ToWideString() const {
+    return WideString::FromUTF8(ToString().AsStringView());
+  }
+  CFXJSE_HostObject* ToHostObject() const;
+
+  void SetUndefined();
+  void SetNull();
+  void SetBoolean(bool bBoolean);
+  void SetInteger(int32_t nInteger);
+  void SetDouble(double dDouble);
+  void SetString(ByteStringView szString);
+  void SetFloat(float fFloat);
+
+  void SetHostObject(CFXJSE_HostObject* lpObject, CFXJSE_Class* pClass);
+  void ClearHostObject();
+
+  void SetArray(const std::vector<std::unique_ptr<CFXJSE_Value>>& values);
+
+  bool GetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue);
+  bool SetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue);
+  bool GetObjectPropertyByIdx(uint32_t uPropIdx, CFXJSE_Value* lpPropValue);
+  bool DeleteObjectProperty(ByteStringView szPropName);
+  bool HasObjectOwnProperty(ByteStringView szPropName, bool bUseTypeGetter);
+  bool SetObjectOwnProperty(ByteStringView szPropName,
+                            CFXJSE_Value* lpPropValue);
+  bool SetFunctionBind(CFXJSE_Value* lpOldFunction, CFXJSE_Value* lpNewThis);
+
+  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  const v8::Global<v8::Value>& DirectGetValue() const { return m_hValue; }
+  void ForceSetValue(v8::Local<v8::Value> hValue) {
+    m_hValue.Reset(GetIsolate(), hValue);
+  }
+  void Assign(const CFXJSE_Value* lpValue) {
+    ASSERT(lpValue);
+    if (lpValue) {
+      m_hValue.Reset(GetIsolate(), lpValue->m_hValue);
+    } else {
+      m_hValue.Reset();
+    }
+  }
+
+ private:
+  CFXJSE_Value() = delete;
+  CFXJSE_Value(const CFXJSE_Value&) = delete;
+  CFXJSE_Value& operator=(const CFXJSE_Value&) = delete;
+
+  UnownedPtr<v8::Isolate> const m_pIsolate;
+  v8::Global<v8::Value> m_hValue;
+};
+
+#endif  // FXJS_XFA_CFXJSE_VALUE_H_
diff --git a/fxjs/xfa/cfxjse_value_embeddertest.cpp b/fxjs/xfa/cfxjse_value_embeddertest.cpp
new file mode 100644
index 0000000..e9c39c1
--- /dev/null
+++ b/fxjs/xfa/cfxjse_value_embeddertest.cpp
@@ -0,0 +1,44 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/xfa/cfxjse_value.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+#include "third_party/base/ptr_util.h"
+
+class CFXJSE_ValueEmbedderTest : public XFAJSEmbedderTest {};
+
+TEST_F(CFXJSE_ValueEmbedderTest, Empty) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  EXPECT_TRUE(pValue->IsEmpty());
+  EXPECT_FALSE(pValue->IsUndefined());
+  EXPECT_FALSE(pValue->IsNull());
+  EXPECT_FALSE(pValue->IsBoolean());
+  EXPECT_FALSE(pValue->IsString());
+  EXPECT_FALSE(pValue->IsNumber());
+  EXPECT_FALSE(pValue->IsObject());
+  EXPECT_FALSE(pValue->IsArray());
+  EXPECT_FALSE(pValue->IsFunction());
+}
+
+TEST_F(CFXJSE_ValueEmbedderTest, EmptyArrayInsert) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  // Test inserting empty values into arrays.
+  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  std::vector<std::unique_ptr<CFXJSE_Value>> vec;
+  vec.push_back(std::move(pValue));
+
+  CFXJSE_Value array(GetIsolate());
+  array.SetArray(vec);
+  EXPECT_TRUE(array.IsArray());
+}
diff --git a/fxjs/xfa/cjx_arc.cpp b/fxjs/xfa/cjx_arc.cpp
deleted file mode 100644
index 92b626d..0000000
--- a/fxjs/xfa/cjx_arc.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_arc.h"
-
-#include "xfa/fxfa/parser/cxfa_arc.h"
-
-CJX_Arc::CJX_Arc(CXFA_Arc* node) : CJX_Node(node) {}
-
-CJX_Arc::~CJX_Arc() = default;
-
-void CJX_Arc::circular(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Arc::hand(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Arc::startAngle(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Arc::sweepAngle(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Arc::use(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Arc::usehref(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_arc.h b/fxjs/xfa/cjx_arc.h
deleted file mode 100644
index a9a6bb6..0000000
--- a/fxjs/xfa/cjx_arc.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_ARC_H_
-#define FXJS_XFA_CJX_ARC_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Arc;
-
-class CJX_Arc : public CJX_Node {
- public:
-  explicit CJX_Arc(CXFA_Arc* node);
-  ~CJX_Arc() override;
-
-  JS_PROP(circular);
-  JS_PROP(hand);
-  JS_PROP(startAngle);
-  JS_PROP(sweepAngle);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_ARC_H_
diff --git a/fxjs/xfa/cjx_area.cpp b/fxjs/xfa/cjx_area.cpp
deleted file mode 100644
index ee62194..0000000
--- a/fxjs/xfa/cjx_area.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_area.h"
-
-#include "xfa/fxfa/parser/cxfa_area.h"
-
-CJX_Area::CJX_Area(CXFA_Area* node) : CJX_Container(node) {}
-
-CJX_Area::~CJX_Area() = default;
-
-void CJX_Area::colSpan(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Area::relevant(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Area::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Area::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Area::x(CFXJSE_Value* pValue,
-                 bool bSetting,
-                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Area::y(CFXJSE_Value* pValue,
-                 bool bSetting,
-                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_area.h b/fxjs/xfa/cjx_area.h
deleted file mode 100644
index 94083b3..0000000
--- a/fxjs/xfa/cjx_area.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_AREA_H_
-#define FXJS_XFA_CJX_AREA_H_
-
-#include "fxjs/xfa/cjx_container.h"
-
-class CXFA_Area;
-
-class CJX_Area : public CJX_Container {
- public:
-  explicit CJX_Area(CXFA_Area* node);
-  ~CJX_Area() override;
-
-  JS_PROP(colSpan);
-  JS_PROP(relevant);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(x);
-  JS_PROP(y);
-};
-
-#endif  // FXJS_XFA_CJX_AREA_H_
diff --git a/fxjs/xfa/cjx_assist.cpp b/fxjs/xfa/cjx_assist.cpp
deleted file mode 100644
index fcd7f37..0000000
--- a/fxjs/xfa/cjx_assist.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_assist.h"
-
-#include "xfa/fxfa/parser/cxfa_assist.h"
-
-CJX_Assist::CJX_Assist(CXFA_Assist* node) : CJX_Node(node) {}
-
-CJX_Assist::~CJX_Assist() = default;
-
-void CJX_Assist::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Assist::role(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Assist::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_assist.h b/fxjs/xfa/cjx_assist.h
deleted file mode 100644
index 9e7c9c5..0000000
--- a/fxjs/xfa/cjx_assist.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_ASSIST_H_
-#define FXJS_XFA_CJX_ASSIST_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Assist;
-
-class CJX_Assist : public CJX_Node {
- public:
-  explicit CJX_Assist(CXFA_Assist* node);
-  ~CJX_Assist() override;
-
-  JS_PROP(role);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_ASSIST_H_
diff --git a/fxjs/xfa/cjx_barcode.cpp b/fxjs/xfa/cjx_barcode.cpp
deleted file mode 100644
index 7520cc3..0000000
--- a/fxjs/xfa/cjx_barcode.cpp
+++ /dev/null
@@ -1,133 +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 "fxjs/xfa/cjx_barcode.h"
-
-#include "xfa/fxfa/parser/cxfa_barcode.h"
-
-CJX_Barcode::CJX_Barcode(CXFA_Barcode* node) : CJX_Node(node) {}
-
-CJX_Barcode::~CJX_Barcode() = default;
-
-void CJX_Barcode::charEncoding(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::checksum(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::dataColumnCount(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::dataLength(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::dataPrep(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::dataRowCount(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::endChar(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::errorCorrectionLevel(CFXJSE_Value* pValue,
-                                       bool bSetting,
-                                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::moduleHeight(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::moduleWidth(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::printCheckDigit(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::rowColumnRatio(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::startChar(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::textLocation(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::truncate(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::type(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::upsMode(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Barcode::wideNarrowRatio(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_barcode.h b/fxjs/xfa/cjx_barcode.h
deleted file mode 100644
index f5d8881..0000000
--- a/fxjs/xfa/cjx_barcode.h
+++ /dev/null
@@ -1,41 +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 FXJS_XFA_CJX_BARCODE_H_
-#define FXJS_XFA_CJX_BARCODE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Barcode;
-
-class CJX_Barcode : public CJX_Node {
- public:
-  explicit CJX_Barcode(CXFA_Barcode* arc);
-  ~CJX_Barcode() override;
-
-  JS_PROP(charEncoding);
-  JS_PROP(checksum);
-  JS_PROP(dataColumnCount);
-  JS_PROP(dataLength);
-  JS_PROP(dataPrep);
-  JS_PROP(dataRowCount);
-  JS_PROP(endChar);
-  JS_PROP(errorCorrectionLevel);
-  JS_PROP(moduleHeight);
-  JS_PROP(moduleWidth);
-  JS_PROP(printCheckDigit);
-  JS_PROP(rowColumnRatio);
-  JS_PROP(startChar);
-  JS_PROP(textLocation);
-  JS_PROP(truncate);
-  JS_PROP(type);
-  JS_PROP(upsMode);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(wideNarrowRatio);
-};
-
-#endif  // FXJS_XFA_CJX_BARCODE_H_
diff --git a/fxjs/xfa/cjx_bind.cpp b/fxjs/xfa/cjx_bind.cpp
deleted file mode 100644
index a73312d..0000000
--- a/fxjs/xfa/cjx_bind.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_bind.h"
-
-#include "xfa/fxfa/parser/cxfa_bind.h"
-
-CJX_Bind::CJX_Bind(CXFA_Bind* node) : CJX_Node(node) {}
-
-CJX_Bind::~CJX_Bind() = default;
-
-void CJX_Bind::contentType(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bind::match(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bind::ref(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bind::transferEncoding(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bind::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bind::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_bind.h b/fxjs/xfa/cjx_bind.h
deleted file mode 100644
index 9fc8ab8..0000000
--- a/fxjs/xfa/cjx_bind.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_BIND_H_
-#define FXJS_XFA_CJX_BIND_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Bind;
-
-class CJX_Bind : public CJX_Node {
- public:
-  explicit CJX_Bind(CXFA_Bind* node);
-  ~CJX_Bind() override;
-
-  JS_PROP(contentType);
-  JS_PROP(match);
-  JS_PROP(ref);
-  JS_PROP(transferEncoding);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_BIND_H_
diff --git a/fxjs/xfa/cjx_binditems.cpp b/fxjs/xfa/cjx_binditems.cpp
deleted file mode 100644
index dd1f30d..0000000
--- a/fxjs/xfa/cjx_binditems.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_binditems.h"
-
-#include "xfa/fxfa/parser/cxfa_binditems.h"
-
-CJX_BindItems::CJX_BindItems(CXFA_BindItems* node) : CJX_Node(node) {}
-
-CJX_BindItems::~CJX_BindItems() = default;
-
-void CJX_BindItems::connection(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BindItems::labelRef(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BindItems::valueRef(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_binditems.h b/fxjs/xfa/cjx_binditems.h
deleted file mode 100644
index e47b3fa..0000000
--- a/fxjs/xfa/cjx_binditems.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_BINDITEMS_H_
-#define FXJS_XFA_CJX_BINDITEMS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_BindItems;
-
-class CJX_BindItems : public CJX_Node {
- public:
-  explicit CJX_BindItems(CXFA_BindItems* node);
-  ~CJX_BindItems() override;
-
-  JS_PROP(connection);
-  JS_PROP(labelRef);
-  JS_PROP(valueRef);
-};
-
-#endif  // FXJS_XFA_CJX_BINDITEMS_H_
diff --git a/fxjs/xfa/cjx_bookend.cpp b/fxjs/xfa/cjx_bookend.cpp
deleted file mode 100644
index 2399e26..0000000
--- a/fxjs/xfa/cjx_bookend.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_bookend.h"
-
-#include "xfa/fxfa/parser/cxfa_bookend.h"
-
-CJX_Bookend::CJX_Bookend(CXFA_Bookend* node) : CJX_Node(node) {}
-
-CJX_Bookend::~CJX_Bookend() = default;
-
-void CJX_Bookend::leader(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bookend::trailer(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bookend::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Bookend::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_bookend.h b/fxjs/xfa/cjx_bookend.h
deleted file mode 100644
index 0165091..0000000
--- a/fxjs/xfa/cjx_bookend.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_BOOKEND_H_
-#define FXJS_XFA_CJX_BOOKEND_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Bookend;
-
-class CJX_Bookend : public CJX_Node {
- public:
-  explicit CJX_Bookend(CXFA_Bookend* node);
-  ~CJX_Bookend() override;
-
-  JS_PROP(leader);
-  JS_PROP(trailer);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_BOOKEND_H_
diff --git a/fxjs/xfa/cjx_boolean.cpp b/fxjs/xfa/cjx_boolean.cpp
index 761d0a2..d44e039 100644
--- a/fxjs/xfa/cjx_boolean.cpp
+++ b/fxjs/xfa/cjx_boolean.cpp
@@ -6,26 +6,22 @@
 
 #include "fxjs/xfa/cjx_boolean.h"
 
-#include "fxjs/cfxjse_value.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_boolean.h"
 
-CJX_Boolean::CJX_Boolean(CXFA_Boolean* node) : CJX_Content(node) {}
+CJX_Boolean::CJX_Boolean(CXFA_Boolean* node) : CJX_Object(node) {}
 
 CJX_Boolean::~CJX_Boolean() = default;
 
-void CJX_Boolean::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+bool CJX_Boolean::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 void CJX_Boolean::defaultValue(CFXJSE_Value* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
   if (!bSetting) {
-    WideString wsValue = GetContent(true);
-    pValue->SetBoolean(wsValue == L"1");
+    pValue->SetBoolean(GetContent(true).EqualsASCII("1"));
     return;
   }
 
@@ -36,20 +32,13 @@
   int32_t iValue = FXSYS_atoi(newValue.c_str());
   WideString wsNewValue(iValue == 0 ? L"0" : L"1");
   WideString wsFormatValue(wsNewValue);
-  CXFA_WidgetAcc* pContainerWidgetAcc =
-      ToNode(GetXFAObject())->GetContainerWidgetAcc();
-  if (pContainerWidgetAcc)
-    wsFormatValue = pContainerWidgetAcc->GetFormatDataValue(wsNewValue);
+  CXFA_Node* pContainerNode = ToNode(GetXFAObject())->GetContainerNode();
+  if (pContainerNode)
+    wsFormatValue = pContainerNode->GetFormatDataValue(wsNewValue);
 
   SetContent(wsNewValue, wsFormatValue, true, true, true);
 }
 
-void CJX_Boolean::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
 void CJX_Boolean::value(CFXJSE_Value* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_boolean.h b/fxjs/xfa/cjx_boolean.h
index c90291a..d2a85ba 100644
--- a/fxjs/xfa/cjx_boolean.h
+++ b/fxjs/xfa/cjx_boolean.h
@@ -7,19 +7,27 @@
 #ifndef FXJS_XFA_CJX_BOOLEAN_H_
 #define FXJS_XFA_CJX_BOOLEAN_H_
 
-#include "fxjs/xfa/cjx_content.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Boolean;
 
-class CJX_Boolean : public CJX_Content {
+class CJX_Boolean final : public CJX_Object {
  public:
   explicit CJX_Boolean(CXFA_Boolean* node);
   ~CJX_Boolean() override;
 
-  JS_PROP(use);
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(usehref);
-  JS_PROP(value);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(defaultValue); /* {default} */
+  JSE_PROP(value);
+
+ private:
+  using Type__ = CJX_Boolean;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::Boolean;
 };
 
 #endif  // FXJS_XFA_CJX_BOOLEAN_H_
diff --git a/fxjs/xfa/cjx_border.cpp b/fxjs/xfa/cjx_border.cpp
deleted file mode 100644
index 92355a8..0000000
--- a/fxjs/xfa/cjx_border.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_border.h"
-
-#include "xfa/fxfa/parser/cxfa_border.h"
-
-CJX_Border::CJX_Border(CXFA_Border* node) : CJX_Node(node) {}
-
-CJX_Border::~CJX_Border() = default;
-
-void CJX_Border::breakValue(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Border::hand(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Border::presence(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Border::relevant(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Border::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Border::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_border.h b/fxjs/xfa/cjx_border.h
deleted file mode 100644
index b95ca4c..0000000
--- a/fxjs/xfa/cjx_border.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_BORDER_H_
-#define FXJS_XFA_CJX_BORDER_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Border;
-
-class CJX_Border : public CJX_Node {
- public:
-  explicit CJX_Border(CXFA_Border* node);
-  ~CJX_Border() override;
-
-  JS_PROP(breakValue); /* break */
-  JS_PROP(hand);
-  JS_PROP(presence);
-  JS_PROP(relevant);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_BORDER_H_
diff --git a/fxjs/xfa/cjx_break.cpp b/fxjs/xfa/cjx_break.cpp
deleted file mode 100644
index d04a05f..0000000
--- a/fxjs/xfa/cjx_break.cpp
+++ /dev/null
@@ -1,85 +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 "fxjs/xfa/cjx_break.h"
-
-#include "xfa/fxfa/parser/cxfa_break.h"
-
-CJX_Break::CJX_Break(CXFA_Break* node) : CJX_Node(node) {}
-
-CJX_Break::~CJX_Break() = default;
-
-void CJX_Break::after(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::afterTarget(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::before(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::beforeTarget(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::bookendLeader(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::bookendTrailer(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::overflowLeader(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::overflowTarget(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::overflowTrailer(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::startNew(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Break::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_break.h b/fxjs/xfa/cjx_break.h
deleted file mode 100644
index 86cb249..0000000
--- a/fxjs/xfa/cjx_break.h
+++ /dev/null
@@ -1,33 +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 FXJS_XFA_CJX_BREAK_H_
-#define FXJS_XFA_CJX_BREAK_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Break;
-
-class CJX_Break : public CJX_Node {
- public:
-  explicit CJX_Break(CXFA_Break* node);
-  ~CJX_Break() override;
-
-  JS_PROP(after);
-  JS_PROP(afterTarget);
-  JS_PROP(before);
-  JS_PROP(beforeTarget);
-  JS_PROP(bookendLeader);
-  JS_PROP(bookendTrailer);
-  JS_PROP(overflowLeader);
-  JS_PROP(overflowTarget);
-  JS_PROP(overflowTrailer);
-  JS_PROP(startNew);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_BREAK_H_
diff --git a/fxjs/xfa/cjx_breakafter.cpp b/fxjs/xfa/cjx_breakafter.cpp
deleted file mode 100644
index 76a5a3b..0000000
--- a/fxjs/xfa/cjx_breakafter.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_breakafter.h"
-
-#include "xfa/fxfa/parser/cxfa_breakafter.h"
-
-CJX_BreakAfter::CJX_BreakAfter(CXFA_BreakAfter* node) : CJX_Node(node) {}
-
-CJX_BreakAfter::~CJX_BreakAfter() = default;
-
-void CJX_BreakAfter::use(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakAfter::startNew(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakAfter::trailer(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakAfter::targetType(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakAfter::usehref(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakAfter::target(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakAfter::leader(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_breakafter.h b/fxjs/xfa/cjx_breakafter.h
deleted file mode 100644
index aa1eccc..0000000
--- a/fxjs/xfa/cjx_breakafter.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_BREAKAFTER_H_
-#define FXJS_XFA_CJX_BREAKAFTER_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_BreakAfter;
-
-class CJX_BreakAfter : public CJX_Node {
- public:
-  explicit CJX_BreakAfter(CXFA_BreakAfter* node);
-  ~CJX_BreakAfter() override;
-
-  JS_PROP(leader);
-  JS_PROP(startNew);
-  JS_PROP(target);
-  JS_PROP(targetType);
-  JS_PROP(trailer);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_BREAKAFTER_H_
diff --git a/fxjs/xfa/cjx_breakbefore.cpp b/fxjs/xfa/cjx_breakbefore.cpp
deleted file mode 100644
index 83fa01e..0000000
--- a/fxjs/xfa/cjx_breakbefore.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_breakbefore.h"
-
-#include "xfa/fxfa/parser/cxfa_breakbefore.h"
-
-CJX_BreakBefore::CJX_BreakBefore(CXFA_BreakBefore* node) : CJX_Node(node) {}
-
-CJX_BreakBefore::~CJX_BreakBefore() = default;
-
-void CJX_BreakBefore::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakBefore::startNew(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakBefore::trailer(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakBefore::targetType(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakBefore::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakBefore::target(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_BreakBefore::leader(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_breakbefore.h b/fxjs/xfa/cjx_breakbefore.h
deleted file mode 100644
index 3f8dbbb..0000000
--- a/fxjs/xfa/cjx_breakbefore.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_BREAKBEFORE_H_
-#define FXJS_XFA_CJX_BREAKBEFORE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_BreakBefore;
-
-class CJX_BreakBefore : public CJX_Node {
- public:
-  explicit CJX_BreakBefore(CXFA_BreakBefore* node);
-  ~CJX_BreakBefore() override;
-
-  JS_PROP(leader);
-  JS_PROP(startNew);
-  JS_PROP(target);
-  JS_PROP(targetType);
-  JS_PROP(trailer);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_BREAKBEFORE_H_
diff --git a/fxjs/xfa/cjx_button.cpp b/fxjs/xfa/cjx_button.cpp
deleted file mode 100644
index b5bd6eb..0000000
--- a/fxjs/xfa/cjx_button.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_button.h"
-
-#include "xfa/fxfa/parser/cxfa_button.h"
-
-CJX_Button::CJX_Button(CXFA_Button* node) : CJX_Node(node) {}
-
-CJX_Button::~CJX_Button() = default;
-
-void CJX_Button::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Button::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Button::highlight(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_button.h b/fxjs/xfa/cjx_button.h
deleted file mode 100644
index 3732aae..0000000
--- a/fxjs/xfa/cjx_button.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_BUTTON_H_
-#define FXJS_XFA_CJX_BUTTON_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Button;
-
-class CJX_Button : public CJX_Node {
- public:
-  explicit CJX_Button(CXFA_Button* node);
-  ~CJX_Button() override;
-
-  JS_PROP(highlight);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_BUTTON_H_
diff --git a/fxjs/xfa/cjx_calculate.cpp b/fxjs/xfa/cjx_calculate.cpp
deleted file mode 100644
index e5db4df..0000000
--- a/fxjs/xfa/cjx_calculate.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_calculate.h"
-
-#include "xfa/fxfa/parser/cxfa_calculate.h"
-
-CJX_Calculate::CJX_Calculate(CXFA_Calculate* node) : CJX_Node(node) {}
-
-CJX_Calculate::~CJX_Calculate() = default;
-
-void CJX_Calculate::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Calculate::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Calculate::override(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_calculate.h b/fxjs/xfa/cjx_calculate.h
deleted file mode 100644
index 2410f1b..0000000
--- a/fxjs/xfa/cjx_calculate.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CALCULATE_H_
-#define FXJS_XFA_CJX_CALCULATE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Calculate;
-
-class CJX_Calculate : public CJX_Node {
- public:
-  explicit CJX_Calculate(CXFA_Calculate* node);
-  ~CJX_Calculate() override;
-
-  JS_PROP(override);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CALCULATE_H_
diff --git a/fxjs/xfa/cjx_caption.cpp b/fxjs/xfa/cjx_caption.cpp
deleted file mode 100644
index 5c7f6a8..0000000
--- a/fxjs/xfa/cjx_caption.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_caption.h"
-
-#include "xfa/fxfa/parser/cxfa_caption.h"
-
-CJX_Caption::CJX_Caption(CXFA_Caption* node) : CJX_Node(node) {}
-
-CJX_Caption::~CJX_Caption() = default;
-
-void CJX_Caption::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Caption::reserve(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Caption::presence(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Caption::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Caption::placement(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_caption.h b/fxjs/xfa/cjx_caption.h
deleted file mode 100644
index 9af6915..0000000
--- a/fxjs/xfa/cjx_caption.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CAPTION_H_
-#define FXJS_XFA_CJX_CAPTION_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Caption;
-
-class CJX_Caption : public CJX_Node {
- public:
-  explicit CJX_Caption(CXFA_Caption* node);
-  ~CJX_Caption() override;
-
-  JS_PROP(placement);
-  JS_PROP(presence);
-  JS_PROP(reserve);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CAPTION_H_
diff --git a/fxjs/xfa/cjx_certificate.cpp b/fxjs/xfa/cjx_certificate.cpp
deleted file mode 100644
index 1d10565..0000000
--- a/fxjs/xfa/cjx_certificate.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_certificate.h"
-
-#include "xfa/fxfa/parser/cxfa_certificate.h"
-
-CJX_Certificate::CJX_Certificate(CXFA_Certificate* node) : CJX_TextNode(node) {}
-
-CJX_Certificate::~CJX_Certificate() = default;
-
-void CJX_Certificate::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Certificate::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_certificate.h b/fxjs/xfa/cjx_certificate.h
deleted file mode 100644
index af62b91..0000000
--- a/fxjs/xfa/cjx_certificate.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CERTIFICATE_H_
-#define FXJS_XFA_CJX_CERTIFICATE_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Certificate;
-
-class CJX_Certificate : public CJX_TextNode {
- public:
-  explicit CJX_Certificate(CXFA_Certificate* node);
-  ~CJX_Certificate() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CERTIFICATE_H_
diff --git a/fxjs/xfa/cjx_certificates.cpp b/fxjs/xfa/cjx_certificates.cpp
deleted file mode 100644
index 6828630..0000000
--- a/fxjs/xfa/cjx_certificates.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_certificates.h"
-
-#include "xfa/fxfa/parser/cxfa_certificates.h"
-
-CJX_Certificates::CJX_Certificates(CXFA_Certificates* node) : CJX_Node(node) {}
-
-CJX_Certificates::~CJX_Certificates() = default;
-
-void CJX_Certificates::url(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Certificates::use(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Certificates::credentialServerPolicy(CFXJSE_Value* pValue,
-                                              bool bSetting,
-                                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Certificates::usehref(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Certificates::urlPolicy(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_certificates.h b/fxjs/xfa/cjx_certificates.h
deleted file mode 100644
index 160d88d..0000000
--- a/fxjs/xfa/cjx_certificates.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CERTIFICATES_H_
-#define FXJS_XFA_CJX_CERTIFICATES_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Certificates;
-
-class CJX_Certificates : public CJX_Node {
- public:
-  explicit CJX_Certificates(CXFA_Certificates* node);
-  ~CJX_Certificates() override;
-
-  JS_PROP(credentialServerPolicy);
-  JS_PROP(url);
-  JS_PROP(urlPolicy);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CERTIFICATES_H_
diff --git a/fxjs/xfa/cjx_checkbutton.cpp b/fxjs/xfa/cjx_checkbutton.cpp
deleted file mode 100644
index 8c5b1e6..0000000
--- a/fxjs/xfa/cjx_checkbutton.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_checkbutton.h"
-
-#include "xfa/fxfa/parser/cxfa_checkbutton.h"
-
-CJX_CheckButton::CJX_CheckButton(CXFA_CheckButton* node) : CJX_Node(node) {}
-
-CJX_CheckButton::~CJX_CheckButton() = default;
-
-void CJX_CheckButton::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_CheckButton::allowNeutral(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_CheckButton::mark(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_CheckButton::shape(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_CheckButton::size(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_CheckButton::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_checkbutton.h b/fxjs/xfa/cjx_checkbutton.h
deleted file mode 100644
index 18cca68..0000000
--- a/fxjs/xfa/cjx_checkbutton.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_CHECKBUTTON_H_
-#define FXJS_XFA_CJX_CHECKBUTTON_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_CheckButton;
-
-class CJX_CheckButton : public CJX_Node {
- public:
-  explicit CJX_CheckButton(CXFA_CheckButton* node);
-  ~CJX_CheckButton() override;
-
-  JS_PROP(allowNeutral);
-  JS_PROP(mark);
-  JS_PROP(shape);
-  JS_PROP(size);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CHECKBUTTON_H_
diff --git a/fxjs/xfa/cjx_choicelist.cpp b/fxjs/xfa/cjx_choicelist.cpp
deleted file mode 100644
index 3c85a03..0000000
--- a/fxjs/xfa/cjx_choicelist.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_choicelist.h"
-
-#include "xfa/fxfa/parser/cxfa_choicelist.h"
-
-CJX_ChoiceList::CJX_ChoiceList(CXFA_ChoiceList* node) : CJX_Node(node) {}
-
-CJX_ChoiceList::~CJX_ChoiceList() = default;
-
-void CJX_ChoiceList::use(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ChoiceList::open(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ChoiceList::commitOn(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ChoiceList::textEntry(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ChoiceList::usehref(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_choicelist.h b/fxjs/xfa/cjx_choicelist.h
deleted file mode 100644
index 40336f2..0000000
--- a/fxjs/xfa/cjx_choicelist.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CHOICELIST_H_
-#define FXJS_XFA_CJX_CHOICELIST_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_ChoiceList;
-
-class CJX_ChoiceList : public CJX_Node {
- public:
-  explicit CJX_ChoiceList(CXFA_ChoiceList* node);
-  ~CJX_ChoiceList() override;
-
-  JS_PROP(commitOn);
-  JS_PROP(open);
-  JS_PROP(textEntry);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CHOICELIST_H_
diff --git a/fxjs/xfa/cjx_color.cpp b/fxjs/xfa/cjx_color.cpp
deleted file mode 100644
index fe7da05..0000000
--- a/fxjs/xfa/cjx_color.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_color.h"
-
-#include "xfa/fxfa/parser/cxfa_color.h"
-
-CJX_Color::CJX_Color(CXFA_Color* node) : CJX_Node(node) {}
-
-CJX_Color::~CJX_Color() = default;
-
-void CJX_Color::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Color::cSpace(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Color::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Color::value(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_color.h b/fxjs/xfa/cjx_color.h
deleted file mode 100644
index c625753..0000000
--- a/fxjs/xfa/cjx_color.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_COLOR_H_
-#define FXJS_XFA_CJX_COLOR_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Color;
-
-class CJX_Color : public CJX_Node {
- public:
-  explicit CJX_Color(CXFA_Color* node);
-  ~CJX_Color() override;
-
-  JS_PROP(cSpace);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_COLOR_H_
diff --git a/fxjs/xfa/cjx_comb.cpp b/fxjs/xfa/cjx_comb.cpp
deleted file mode 100644
index 77144af..0000000
--- a/fxjs/xfa/cjx_comb.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_comb.h"
-
-#include "xfa/fxfa/parser/cxfa_comb.h"
-
-CJX_Comb::CJX_Comb(CXFA_Comb* node) : CJX_Node(node) {}
-
-CJX_Comb::~CJX_Comb() = default;
-
-void CJX_Comb::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Comb::numberOfCells(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_Integer(pValue, bSetting, eAttribute);
-}
-
-void CJX_Comb::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_comb.h b/fxjs/xfa/cjx_comb.h
deleted file mode 100644
index 1c8d631..0000000
--- a/fxjs/xfa/cjx_comb.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_COMB_H_
-#define FXJS_XFA_CJX_COMB_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Comb;
-
-class CJX_Comb : public CJX_Node {
- public:
-  explicit CJX_Comb(CXFA_Comb* node);
-  ~CJX_Comb() override;
-
-  JS_PROP(numberOfCells);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_COMB_H_
diff --git a/fxjs/xfa/cjx_command.cpp b/fxjs/xfa/cjx_command.cpp
deleted file mode 100644
index 30e654b..0000000
--- a/fxjs/xfa/cjx_command.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_command.h"
-
-#include "xfa/fxfa/parser/cxfa_command.h"
-
-CJX_Command::CJX_Command(CXFA_Command* node) : CJX_Node(node) {}
-
-CJX_Command::~CJX_Command() = default;
-
-void CJX_Command::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Command::timeout(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Command::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_command.h b/fxjs/xfa/cjx_command.h
deleted file mode 100644
index c4605a3..0000000
--- a/fxjs/xfa/cjx_command.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_COMMAND_H_
-#define FXJS_XFA_CJX_COMMAND_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Command;
-
-class CJX_Command : public CJX_Node {
- public:
-  explicit CJX_Command(CXFA_Command* node);
-  ~CJX_Command() override;
-
-  JS_PROP(timeout);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_COMMAND_H_
diff --git a/fxjs/xfa/cjx_connect.cpp b/fxjs/xfa/cjx_connect.cpp
deleted file mode 100644
index d58420d..0000000
--- a/fxjs/xfa/cjx_connect.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_connect.h"
-
-#include "xfa/fxfa/parser/cxfa_connect.h"
-
-CJX_Connect::CJX_Connect(CXFA_Connect* node) : CJX_Node(node) {}
-
-CJX_Connect::~CJX_Connect() = default;
-
-void CJX_Connect::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Connect::timeout(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Connect::connection(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Connect::usage(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Connect::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Connect::delayedOpen(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Connect::ref(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_connect.h b/fxjs/xfa/cjx_connect.h
deleted file mode 100644
index 2c27917..0000000
--- a/fxjs/xfa/cjx_connect.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CONNECT_H_
-#define FXJS_XFA_CJX_CONNECT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Connect;
-
-class CJX_Connect : public CJX_Node {
- public:
-  explicit CJX_Connect(CXFA_Connect* node);
-  ~CJX_Connect() override;
-
-  JS_PROP(connection);
-  JS_PROP(delayedOpen);
-  JS_PROP(ref);
-  JS_PROP(timeout);
-  JS_PROP(usage);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CONNECT_H_
diff --git a/fxjs/xfa/cjx_connectstring.cpp b/fxjs/xfa/cjx_connectstring.cpp
deleted file mode 100644
index 75c6359..0000000
--- a/fxjs/xfa/cjx_connectstring.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_connectstring.h"
-
-#include "xfa/fxfa/parser/cxfa_connectstring.h"
-
-CJX_ConnectString::CJX_ConnectString(CXFA_ConnectString* node)
-    : CJX_TextNode(node) {}
-
-CJX_ConnectString::~CJX_ConnectString() = default;
-
-void CJX_ConnectString::use(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ConnectString::usehref(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_connectstring.h b/fxjs/xfa/cjx_connectstring.h
deleted file mode 100644
index 969c92b..0000000
--- a/fxjs/xfa/cjx_connectstring.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CONNECTSTRING_H_
-#define FXJS_XFA_CJX_CONNECTSTRING_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_ConnectString;
-
-class CJX_ConnectString : public CJX_TextNode {
- public:
-  explicit CJX_ConnectString(CXFA_ConnectString* node);
-  ~CJX_ConnectString() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_CONNECTSTRING_H_
diff --git a/fxjs/xfa/cjx_container.cpp b/fxjs/xfa/cjx_container.cpp
index cf0aca0..44b1ff8 100644
--- a/fxjs/xfa/cjx_container.cpp
+++ b/fxjs/xfa/cjx_container.cpp
@@ -8,8 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_class.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_field.h"
@@ -19,22 +20,26 @@
     {"getDeltas", getDeltas_static}};
 
 CJX_Container::CJX_Container(CXFA_Node* node) : CJX_Node(node) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Container::~CJX_Container() {}
 
-CJS_Return CJX_Container::getDelta(
-    CJS_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+bool CJX_Container::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_Container::getDeltas(
-    CJS_V8* runtime,
+CJS_Result CJX_Container::getDelta(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_ArrayNodeList* pFormNodes = new CXFA_ArrayNodeList(GetDocument());
-  return CJS_Return(runtime->NewXFAObject(
-      pFormNodes,
+  return CJS_Result::Success();
+}
+
+CJS_Result CJX_Container::getDeltas(
+    CFX_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  auto* pEngine = static_cast<CFXJSE_Engine*>(runtime);
+  return CJS_Result::Success(pEngine->NewXFAObject(
+      new CXFA_ArrayNodeList(GetDocument()),
       GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
 }
diff --git a/fxjs/xfa/cjx_container.h b/fxjs/xfa/cjx_container.h
index 0e2170f..51675e5 100644
--- a/fxjs/xfa/cjx_container.h
+++ b/fxjs/xfa/cjx_container.h
@@ -7,8 +7,8 @@
 #ifndef FXJS_XFA_CJX_CONTAINER_H_
 #define FXJS_XFA_CJX_CONTAINER_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Node;
 
@@ -17,10 +17,17 @@
   explicit CJX_Container(CXFA_Node* node);
   ~CJX_Container() override;
 
-  JS_METHOD(getDelta, CJX_Container);
-  JS_METHOD(getDeltas, CJX_Container);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_METHOD(getDelta);
+  JSE_METHOD(getDeltas);
 
  private:
+  using Type__ = CJX_Container;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Container;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_content.cpp b/fxjs/xfa/cjx_content.cpp
deleted file mode 100644
index dd25829..0000000
--- a/fxjs/xfa/cjx_content.cpp
+++ /dev/null
@@ -1,14 +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 "fxjs/xfa/cjx_content.h"
-
-#include "fxjs/cfxjse_value.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
-
-CJX_Content::CJX_Content(CXFA_Object* obj) : CJX_Object(obj) {}
-
-CJX_Content::~CJX_Content() {}
diff --git a/fxjs/xfa/cjx_content.h b/fxjs/xfa/cjx_content.h
deleted file mode 100644
index 983a750..0000000
--- a/fxjs/xfa/cjx_content.h
+++ /dev/null
@@ -1,21 +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 FXJS_XFA_CJX_CONTENT_H_
-#define FXJS_XFA_CJX_CONTENT_H_
-
-#include "fxjs/CJX_Define.h"
-#include "fxjs/xfa/cjx_object.h"
-
-class CXFA_Content;
-
-class CJX_Content : public CJX_Object {
- public:
-  explicit CJX_Content(CXFA_Object* obj);
-  ~CJX_Content() override;
-};
-
-#endif  // FXJS_XFA_CJX_CONTENT_H_
diff --git a/fxjs/xfa/cjx_contentarea.cpp b/fxjs/xfa/cjx_contentarea.cpp
deleted file mode 100644
index d1e74e7..0000000
--- a/fxjs/xfa/cjx_contentarea.cpp
+++ /dev/null
@@ -1,44 +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 "fxjs/xfa/cjx_contentarea.h"
-
-#include "xfa/fxfa/parser/cxfa_contentarea.h"
-
-CJX_ContentArea::CJX_ContentArea(CXFA_ContentArea* node)
-    : CJX_Container(node) {}
-
-CJX_ContentArea::~CJX_ContentArea() = default;
-
-void CJX_ContentArea::x(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ContentArea::y(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ContentArea::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ContentArea::relevant(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ContentArea::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_contentarea.h b/fxjs/xfa/cjx_contentarea.h
deleted file mode 100644
index d97e108..0000000
--- a/fxjs/xfa/cjx_contentarea.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CONTENTAREA_H_
-#define FXJS_XFA_CJX_CONTENTAREA_H_
-
-#include "fxjs/xfa/cjx_container.h"
-
-class CXFA_ContentArea;
-
-class CJX_ContentArea : public CJX_Container {
- public:
-  explicit CJX_ContentArea(CXFA_ContentArea* node);
-  ~CJX_ContentArea() override;
-
-  JS_PROP(relevant);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(x);
-  JS_PROP(y);
-};
-
-#endif  // FXJS_XFA_CJX_CONTENTAREA_H_
diff --git a/fxjs/xfa/cjx_corner.cpp b/fxjs/xfa/cjx_corner.cpp
deleted file mode 100644
index 2ed1922..0000000
--- a/fxjs/xfa/cjx_corner.cpp
+++ /dev/null
@@ -1,61 +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 "fxjs/xfa/cjx_corner.h"
-
-#include "xfa/fxfa/parser/cxfa_corner.h"
-
-CJX_Corner::CJX_Corner(CXFA_Corner* node) : CJX_Node(node) {}
-
-CJX_Corner::~CJX_Corner() = default;
-
-void CJX_Corner::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Corner::stroke(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Corner::presence(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Corner::inverted(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Corner::thickness(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Corner::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Corner::join(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Corner::radius(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_corner.h b/fxjs/xfa/cjx_corner.h
deleted file mode 100644
index 83251a5..0000000
--- a/fxjs/xfa/cjx_corner.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_CORNER_H_
-#define FXJS_XFA_CJX_CORNER_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Corner;
-
-class CJX_Corner : public CJX_Node {
- public:
-  explicit CJX_Corner(CXFA_Corner* node);
-  ~CJX_Corner() override;
-
-  JS_PROP(inverted);
-  JS_PROP(join);
-  JS_PROP(presence);
-  JS_PROP(radius);
-  JS_PROP(stroke);
-  JS_PROP(thickness);
-  JS_PROP(usehref);
-  JS_PROP(use);
-};
-
-#endif  // FXJS_XFA_CJX_CORNER_H_
diff --git a/fxjs/xfa/cjx_datavalue.cpp b/fxjs/xfa/cjx_datavalue.cpp
deleted file mode 100644
index b90ed45..0000000
--- a/fxjs/xfa/cjx_datavalue.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_datavalue.h"
-
-#include "xfa/fxfa/parser/cxfa_datavalue.h"
-
-CJX_DataValue::CJX_DataValue(CXFA_DataValue* node) : CJX_Node(node) {}
-
-CJX_DataValue::~CJX_DataValue() = default;
-
-void CJX_DataValue::contentType(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DataValue::contains(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DataValue::defaultValue(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_DataValue::value(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_DataValue::isNull(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_BOOL(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_datavalue.h b/fxjs/xfa/cjx_datavalue.h
deleted file mode 100644
index 16e8ed4..0000000
--- a/fxjs/xfa/cjx_datavalue.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_DATAVALUE_H_
-#define FXJS_XFA_CJX_DATAVALUE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_DataValue;
-
-class CJX_DataValue : public CJX_Node {
- public:
-  explicit CJX_DataValue(CXFA_DataValue* node);
-  ~CJX_DataValue() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(contains);
-  JS_PROP(contentType);
-  JS_PROP(isNull);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_DATAVALUE_H_
diff --git a/fxjs/xfa/cjx_datawindow.cpp b/fxjs/xfa/cjx_datawindow.cpp
index 5209cb4..d165140 100644
--- a/fxjs/xfa/cjx_datawindow.cpp
+++ b/fxjs/xfa/cjx_datawindow.cpp
@@ -8,7 +8,7 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cscript_datawindow.h"
 
 const CJX_MethodSpec CJX_DataWindow::MethodSpecs[] = {
@@ -19,33 +19,37 @@
 
 CJX_DataWindow::CJX_DataWindow(CScript_DataWindow* window)
     : CJX_Object(window) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_DataWindow::~CJX_DataWindow() {}
 
-CJS_Return CJX_DataWindow::moveCurrentRecord(
-    CJS_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+bool CJX_DataWindow::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_DataWindow::record(
-    CJS_V8* runtime,
+CJS_Result CJX_DataWindow::moveCurrentRecord(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_DataWindow::gotoRecord(
-    CJS_V8* runtime,
+CJS_Result CJX_DataWindow::record(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_DataWindow::isRecordGroup(
-    CJS_V8* runtime,
+CJS_Result CJX_DataWindow::gotoRecord(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
+}
+
+CJS_Result CJX_DataWindow::isRecordGroup(
+    CFX_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
 
 void CJX_DataWindow::recordsBefore(CFXJSE_Value* pValue,
diff --git a/fxjs/xfa/cjx_datawindow.h b/fxjs/xfa/cjx_datawindow.h
index dfaa6a4..6f3ad54 100644
--- a/fxjs/xfa/cjx_datawindow.h
+++ b/fxjs/xfa/cjx_datawindow.h
@@ -7,29 +7,36 @@
 #ifndef FXJS_XFA_CJX_DATAWINDOW_H_
 #define FXJS_XFA_CJX_DATAWINDOW_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CFXJSE_Value;
 class CScript_DataWindow;
 
-class CJX_DataWindow : public CJX_Object {
+class CJX_DataWindow final : public CJX_Object {
  public:
   explicit CJX_DataWindow(CScript_DataWindow* window);
   ~CJX_DataWindow() override;
 
-  JS_METHOD(gotoRecord, CJX_DataWindow);
-  JS_METHOD(isRecordGroup, CJX_DataWindow);
-  JS_METHOD(moveCurrentRecord, CJX_DataWindow);
-  JS_METHOD(record, CJX_DataWindow);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(currentRecordNumber);
-  JS_PROP(isDefined);
-  JS_PROP(recordsAfter);
-  JS_PROP(recordsBefore);
+  JSE_METHOD(gotoRecord);
+  JSE_METHOD(isRecordGroup);
+  JSE_METHOD(moveCurrentRecord);
+  JSE_METHOD(record);
+
+  JSE_PROP(currentRecordNumber);
+  JSE_PROP(isDefined);
+  JSE_PROP(recordsAfter);
+  JSE_PROP(recordsBefore);
 
  private:
+  using Type__ = CJX_DataWindow;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::DataWindow;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_date.cpp b/fxjs/xfa/cjx_date.cpp
deleted file mode 100644
index ade95a9..0000000
--- a/fxjs/xfa/cjx_date.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_date.h"
-
-#include "xfa/fxfa/parser/cxfa_date.h"
-
-CJX_Date::CJX_Date(CXFA_Date* node) : CJX_Content(node) {}
-
-CJX_Date::~CJX_Date() = default;
-
-void CJX_Date::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Date::defaultValue(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Date::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Date::value(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_date.h b/fxjs/xfa/cjx_date.h
deleted file mode 100644
index 96dfd0e..0000000
--- a/fxjs/xfa/cjx_date.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_DATE_H_
-#define FXJS_XFA_CJX_DATE_H_
-
-#include "fxjs/xfa/cjx_content.h"
-
-class CXFA_Date;
-
-class CJX_Date : public CJX_Content {
- public:
-  explicit CJX_Date(CXFA_Date* node);
-  ~CJX_Date() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_DATE_H_
diff --git a/fxjs/xfa/cjx_datetime.cpp b/fxjs/xfa/cjx_datetime.cpp
deleted file mode 100644
index 35ffd05..0000000
--- a/fxjs/xfa/cjx_datetime.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_datetime.h"
-
-#include "xfa/fxfa/parser/cxfa_datetime.h"
-
-CJX_DateTime::CJX_DateTime(CXFA_DateTime* node) : CJX_Node(node) {}
-
-CJX_DateTime::~CJX_DateTime() = default;
-
-void CJX_DateTime::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DateTime::defaultValue(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_DateTime::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DateTime::value(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_datetime.h b/fxjs/xfa/cjx_datetime.h
deleted file mode 100644
index 9cf7d2f..0000000
--- a/fxjs/xfa/cjx_datetime.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_DATETIME_H_
-#define FXJS_XFA_CJX_DATETIME_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_DateTime;
-
-class CJX_DateTime : public CJX_Node {
- public:
-  explicit CJX_DateTime(CXFA_DateTime* node);
-  ~CJX_DateTime() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_DATETIME_H_
diff --git a/fxjs/xfa/cjx_datetimeedit.cpp b/fxjs/xfa/cjx_datetimeedit.cpp
deleted file mode 100644
index 284dbf0..0000000
--- a/fxjs/xfa/cjx_datetimeedit.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_datetimeedit.h"
-
-#include "xfa/fxfa/parser/cxfa_datetimeedit.h"
-
-CJX_DateTimeEdit::CJX_DateTimeEdit(CXFA_DateTimeEdit* node) : CJX_Node(node) {}
-
-CJX_DateTimeEdit::~CJX_DateTimeEdit() = default;
-
-void CJX_DateTimeEdit::use(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DateTimeEdit::usehref(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DateTimeEdit::hScrollPolicy(CFXJSE_Value* pValue,
-                                     bool bSetting,
-                                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_datetimeedit.h b/fxjs/xfa/cjx_datetimeedit.h
deleted file mode 100644
index 067c6bb..0000000
--- a/fxjs/xfa/cjx_datetimeedit.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_DATETIMEEDIT_H_
-#define FXJS_XFA_CJX_DATETIMEEDIT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_DateTimeEdit;
-
-class CJX_DateTimeEdit : public CJX_Node {
- public:
-  explicit CJX_DateTimeEdit(CXFA_DateTimeEdit* node);
-  ~CJX_DateTimeEdit() override;
-
-  JS_PROP(hScrollPolicy);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_DATETIMEEDIT_H_
diff --git a/fxjs/xfa/cjx_decimal.cpp b/fxjs/xfa/cjx_decimal.cpp
deleted file mode 100644
index 66763ba..0000000
--- a/fxjs/xfa/cjx_decimal.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_decimal.h"
-
-#include "xfa/fxfa/parser/cxfa_decimal.h"
-
-CJX_Decimal::CJX_Decimal(CXFA_Decimal* node) : CJX_Content(node) {}
-
-CJX_Decimal::~CJX_Decimal() = default;
-
-void CJX_Decimal::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Decimal::fracDigits(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Decimal::defaultValue(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Decimal::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Decimal::value(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Decimal::leadDigits(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_decimal.h b/fxjs/xfa/cjx_decimal.h
deleted file mode 100644
index 5753112..0000000
--- a/fxjs/xfa/cjx_decimal.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_DECIMAL_H_
-#define FXJS_XFA_CJX_DECIMAL_H_
-
-#include "fxjs/xfa/cjx_content.h"
-
-class CXFA_Decimal;
-
-class CJX_Decimal : public CJX_Content {
- public:
-  explicit CJX_Decimal(CXFA_Decimal* node);
-  ~CJX_Decimal() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(fracDigits);
-  JS_PROP(leadDigits);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_DECIMAL_H_
diff --git a/fxjs/xfa/cjx_defaultui.cpp b/fxjs/xfa/cjx_defaultui.cpp
deleted file mode 100644
index 63995cd..0000000
--- a/fxjs/xfa/cjx_defaultui.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_defaultui.h"
-
-#include "xfa/fxfa/parser/cxfa_defaultui.h"
-
-CJX_DefaultUi::CJX_DefaultUi(CXFA_DefaultUi* node) : CJX_Node(node) {}
-
-CJX_DefaultUi::~CJX_DefaultUi() = default;
-
-void CJX_DefaultUi::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DefaultUi::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_defaultui.h b/fxjs/xfa/cjx_defaultui.h
deleted file mode 100644
index 8b8983b..0000000
--- a/fxjs/xfa/cjx_defaultui.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_DEFAULTUI_H_
-#define FXJS_XFA_CJX_DEFAULTUI_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_DefaultUi;
-
-class CJX_DefaultUi : public CJX_Node {
- public:
-  explicit CJX_DefaultUi(CXFA_DefaultUi* node);
-  ~CJX_DefaultUi() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_DEFAULTUI_H_
diff --git a/fxjs/xfa/cjx_delete.cpp b/fxjs/xfa/cjx_delete.cpp
deleted file mode 100644
index 35fe991..0000000
--- a/fxjs/xfa/cjx_delete.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_delete.h"
-
-#include "xfa/fxfa/parser/cxfa_delete.h"
-
-CJX_Delete::CJX_Delete(CXFA_Delete* node) : CJX_TextNode(node) {}
-
-CJX_Delete::~CJX_Delete() = default;
-
-void CJX_Delete::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Delete::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_delete.h b/fxjs/xfa/cjx_delete.h
deleted file mode 100644
index 413a140..0000000
--- a/fxjs/xfa/cjx_delete.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_DELETE_H_
-#define FXJS_XFA_CJX_DELETE_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Delete;
-
-class CJX_Delete : public CJX_TextNode {
- public:
-  explicit CJX_Delete(CXFA_Delete* node);
-  ~CJX_Delete() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_DELETE_H_
diff --git a/fxjs/xfa/cjx_delta.cpp b/fxjs/xfa/cjx_delta.cpp
index 6d8cbd5..3ad20a6 100644
--- a/fxjs/xfa/cjx_delta.cpp
+++ b/fxjs/xfa/cjx_delta.cpp
@@ -8,23 +8,28 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_delta.h"
 
 const CJX_MethodSpec CJX_Delta::MethodSpecs[] = {{"restore", restore_static}};
 
 CJX_Delta::CJX_Delta(CXFA_Delta* delta) : CJX_Object(delta) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Delta::~CJX_Delta() {}
 
-CJS_Return CJX_Delta::restore(CJS_V8* runtime,
+bool CJX_Delta::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Delta::restore(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
 void CJX_Delta::currentValue(CFXJSE_Value* pValue,
diff --git a/fxjs/xfa/cjx_delta.h b/fxjs/xfa/cjx_delta.h
index 2b981cd..716dd1e 100644
--- a/fxjs/xfa/cjx_delta.h
+++ b/fxjs/xfa/cjx_delta.h
@@ -7,23 +7,30 @@
 #ifndef FXJS_XFA_CJX_DELTA_H_
 #define FXJS_XFA_CJX_DELTA_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Delta;
 
-class CJX_Delta : public CJX_Object {
+class CJX_Delta final : public CJX_Object {
  public:
   explicit CJX_Delta(CXFA_Delta* delta);
   ~CJX_Delta() override;
 
-  JS_METHOD(restore, CJX_Delta);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(currentValue);
-  JS_PROP(savedValue);
-  JS_PROP(target);
+  JSE_METHOD(restore);
+
+  JSE_PROP(currentValue);
+  JSE_PROP(savedValue);
+  JSE_PROP(target);
 
  private:
+  using Type__ = CJX_Delta;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::Delta;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_deltas.cpp b/fxjs/xfa/cjx_deltas.cpp
deleted file mode 100644
index e8969fb..0000000
--- a/fxjs/xfa/cjx_deltas.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_deltas.h"
-
-#include "xfa/fxfa/parser/cxfa_deltas.h"
-
-CJX_Deltas::CJX_Deltas(CXFA_Deltas* src) : CJX_List(src) {}
-
-CJX_Deltas::~CJX_Deltas() {}
diff --git a/fxjs/xfa/cjx_deltas.h b/fxjs/xfa/cjx_deltas.h
deleted file mode 100644
index c94753f..0000000
--- a/fxjs/xfa/cjx_deltas.h
+++ /dev/null
@@ -1,20 +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 FXJS_XFA_CJX_DELTAS_H_
-#define FXJS_XFA_CJX_DELTAS_H_
-
-#include "fxjs/xfa/cjx_list.h"
-
-class CXFA_Deltas;
-
-class CJX_Deltas : public CJX_List {
- public:
-  explicit CJX_Deltas(CXFA_Deltas* node);
-  ~CJX_Deltas() override;
-};
-
-#endif  // FXJS_XFA_CJX_DELTAS_H_
diff --git a/fxjs/xfa/cjx_desc.cpp b/fxjs/xfa/cjx_desc.cpp
index a49ae63..2bbff2b 100644
--- a/fxjs/xfa/cjx_desc.cpp
+++ b/fxjs/xfa/cjx_desc.cpp
@@ -8,33 +8,27 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_desc.h"
 
 const CJX_MethodSpec CJX_Desc::MethodSpecs[] = {{"metadata", metadata_static}};
 
 CJX_Desc::CJX_Desc(CXFA_Desc* desc) : CJX_Node(desc) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Desc::~CJX_Desc() {}
 
-CJS_Return CJX_Desc::metadata(CJS_V8* runtime,
+bool CJX_Desc::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Desc::metadata(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 0 && params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewString(""));
-}
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-void CJX_Desc::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Desc::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  return CJS_Result::Success(runtime->NewString(""));
 }
diff --git a/fxjs/xfa/cjx_desc.h b/fxjs/xfa/cjx_desc.h
index 4d21c7b..62cdec8 100644
--- a/fxjs/xfa/cjx_desc.h
+++ b/fxjs/xfa/cjx_desc.h
@@ -7,22 +7,26 @@
 #ifndef FXJS_XFA_CJX_DESC_H_
 #define FXJS_XFA_CJX_DESC_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Desc;
 
-class CJX_Desc : public CJX_Node {
+class CJX_Desc final : public CJX_Node {
  public:
   explicit CJX_Desc(CXFA_Desc* desc);
   ~CJX_Desc() override;
 
-  JS_METHOD(metadata, CJX_Desc);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(use);
-  JS_PROP(usehref);
+  JSE_METHOD(metadata);
 
  private:
+  using Type__ = CJX_Desc;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Desc;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_digestmethod.cpp b/fxjs/xfa/cjx_digestmethod.cpp
deleted file mode 100644
index b85fc00..0000000
--- a/fxjs/xfa/cjx_digestmethod.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_digestmethod.h"
-
-#include "xfa/fxfa/parser/cxfa_digestmethod.h"
-
-CJX_DigestMethod::CJX_DigestMethod(CXFA_DigestMethod* node) : CJX_Node(node) {}
-
-CJX_DigestMethod::~CJX_DigestMethod() = default;
-
-void CJX_DigestMethod::use(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DigestMethod::usehref(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_digestmethod.h b/fxjs/xfa/cjx_digestmethod.h
deleted file mode 100644
index d14e6f1..0000000
--- a/fxjs/xfa/cjx_digestmethod.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_DIGESTMETHOD_H_
-#define FXJS_XFA_CJX_DIGESTMETHOD_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_DigestMethod;
-
-class CJX_DigestMethod : public CJX_Node {
- public:
-  explicit CJX_DigestMethod(CXFA_DigestMethod* node);
-  ~CJX_DigestMethod() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_DIGESTMETHOD_H_
diff --git a/fxjs/xfa/cjx_digestmethods.cpp b/fxjs/xfa/cjx_digestmethods.cpp
deleted file mode 100644
index ae7cbcd..0000000
--- a/fxjs/xfa/cjx_digestmethods.cpp
+++ /dev/null
@@ -1,32 +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 "fxjs/xfa/cjx_digestmethods.h"
-
-#include "xfa/fxfa/parser/cxfa_digestmethods.h"
-
-CJX_DigestMethods::CJX_DigestMethods(CXFA_DigestMethods* node)
-    : CJX_Node(node) {}
-
-CJX_DigestMethods::~CJX_DigestMethods() = default;
-
-void CJX_DigestMethods::use(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DigestMethods::type(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_DigestMethods::usehref(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_digestmethods.h b/fxjs/xfa/cjx_digestmethods.h
deleted file mode 100644
index b3addc3..0000000
--- a/fxjs/xfa/cjx_digestmethods.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_DIGESTMETHODS_H_
-#define FXJS_XFA_CJX_DIGESTMETHODS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_DigestMethods;
-
-class CJX_DigestMethods : public CJX_Node {
- public:
-  explicit CJX_DigestMethods(CXFA_DigestMethods* node);
-  ~CJX_DigestMethods() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_DIGESTMETHODS_H_
diff --git a/fxjs/xfa/cjx_draw.cpp b/fxjs/xfa/cjx_draw.cpp
index a1baabf..4e8ddc1 100644
--- a/fxjs/xfa/cjx_draw.cpp
+++ b/fxjs/xfa/cjx_draw.cpp
@@ -6,96 +6,15 @@
 
 #include "fxjs/xfa/cjx_draw.h"
 
-#include "fxjs/cfxjse_value.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_draw.h"
 
 CJX_Draw::CJX_Draw(CXFA_Draw* node) : CJX_Container(node) {}
 
 CJX_Draw::~CJX_Draw() = default;
 
-void CJX_Draw::h(CFXJSE_Value* pValue,
-                 bool bSetting,
-                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::w(CFXJSE_Value* pValue,
-                 bool bSetting,
-                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::x(CFXJSE_Value* pValue,
-                 bool bSetting,
-                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::y(CFXJSE_Value* pValue,
-                 bool bSetting,
-                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::hAlign(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::rotate(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::presence(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::vAlign(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::maxH(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::maxW(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::minH(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::minW(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::relevant(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+bool CJX_Draw::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 void CJX_Draw::rawValue(CFXJSE_Value* pValue,
@@ -112,7 +31,7 @@
     if (content.IsEmpty())
       pValue->SetNull();
     else
-      pValue->SetString(content.UTF8Encode().AsStringView());
+      pValue->SetString(content.ToUTF8().AsStringView());
 
     return;
   }
@@ -120,34 +39,10 @@
   if (!pValue || !pValue->IsString())
     return;
 
-  XFA_Element uiType = GetXFANode()->GetWidgetAcc()->GetUIType();
-  if (uiType != XFA_Element::Text)
+  ASSERT(GetXFANode()->IsWidgetReady());
+  if (GetXFANode()->GetFFWidgetType() != XFA_FFWidgetType::kText)
     return;
 
   WideString wsNewValue = pValue->ToWideString();
   SetContent(wsNewValue, wsNewValue, true, true, true);
 }
-
-void CJX_Draw::colSpan(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::locale(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Draw::anchorType(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_draw.h b/fxjs/xfa/cjx_draw.h
index 1d5603a..bf3c3a7 100644
--- a/fxjs/xfa/cjx_draw.h
+++ b/fxjs/xfa/cjx_draw.h
@@ -8,34 +8,26 @@
 #define FXJS_XFA_CJX_DRAW_H_
 
 #include "fxjs/xfa/cjx_container.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Draw;
 
-class CJX_Draw : public CJX_Container {
+class CJX_Draw final : public CJX_Container {
  public:
   explicit CJX_Draw(CXFA_Draw* node);
   ~CJX_Draw() override;
 
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(anchorType);
-  JS_PROP(colSpan);
-  JS_PROP(h);
-  JS_PROP(hAlign);
-  JS_PROP(locale);
-  JS_PROP(maxH);
-  JS_PROP(maxW);
-  JS_PROP(minH);
-  JS_PROP(minW);
-  JS_PROP(presence);
-  JS_PROP(rawValue);
-  JS_PROP(relevant);
-  JS_PROP(rotate);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(vAlign);
-  JS_PROP(w);
-  JS_PROP(x);
-  JS_PROP(y);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(defaultValue); /* {default} */
+  JSE_PROP(rawValue);
+
+ private:
+  using Type__ = CJX_Draw;
+  using ParentType__ = CJX_Container;
+
+  static const TypeTag static_type__ = TypeTag::Draw;
 };
 
 #endif  // FXJS_XFA_CJX_DRAW_H_
diff --git a/fxjs/xfa/cjx_edge.cpp b/fxjs/xfa/cjx_edge.cpp
deleted file mode 100644
index ea1fa35..0000000
--- a/fxjs/xfa/cjx_edge.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_edge.h"
-
-#include "xfa/fxfa/parser/cxfa_edge.h"
-
-CJX_Edge::CJX_Edge(CXFA_Edge* node) : CJX_Node(node) {}
-
-CJX_Edge::~CJX_Edge() = default;
-
-void CJX_Edge::cap(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Edge::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Edge::stroke(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Edge::presence(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Edge::thickness(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Edge::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_edge.h b/fxjs/xfa/cjx_edge.h
deleted file mode 100644
index ef4ddeb..0000000
--- a/fxjs/xfa/cjx_edge.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_EDGE_H_
-#define FXJS_XFA_CJX_EDGE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Edge;
-
-class CJX_Edge : public CJX_Node {
- public:
-  explicit CJX_Edge(CXFA_Edge* node);
-  ~CJX_Edge() override;
-
-  JS_PROP(cap);
-  JS_PROP(presence);
-  JS_PROP(stroke);
-  JS_PROP(thickness);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_EDGE_H_
diff --git a/fxjs/xfa/cjx_encoding.cpp b/fxjs/xfa/cjx_encoding.cpp
deleted file mode 100644
index debfe40..0000000
--- a/fxjs/xfa/cjx_encoding.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_encoding.h"
-
-#include "xfa/fxfa/parser/cxfa_encoding.h"
-
-CJX_Encoding::CJX_Encoding(CXFA_Encoding* node) : CJX_Node(node) {}
-
-CJX_Encoding::~CJX_Encoding() = default;
-
-void CJX_Encoding::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Encoding::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_encoding.h b/fxjs/xfa/cjx_encoding.h
deleted file mode 100644
index ea82333..0000000
--- a/fxjs/xfa/cjx_encoding.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_ENCODING_H_
-#define FXJS_XFA_CJX_ENCODING_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Encoding;
-
-class CJX_Encoding : public CJX_Node {
- public:
-  explicit CJX_Encoding(CXFA_Encoding* node);
-  ~CJX_Encoding() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_ENCODING_H_
diff --git a/fxjs/xfa/cjx_encodings.cpp b/fxjs/xfa/cjx_encodings.cpp
deleted file mode 100644
index 667929f..0000000
--- a/fxjs/xfa/cjx_encodings.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_encodings.h"
-
-#include "xfa/fxfa/parser/cxfa_encodings.h"
-
-CJX_Encodings::CJX_Encodings(CXFA_Encodings* node) : CJX_Node(node) {}
-
-CJX_Encodings::~CJX_Encodings() = default;
-
-void CJX_Encodings::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Encodings::type(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Encodings::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_encodings.h b/fxjs/xfa/cjx_encodings.h
deleted file mode 100644
index da4f617..0000000
--- a/fxjs/xfa/cjx_encodings.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_ENCODINGS_H_
-#define FXJS_XFA_CJX_ENCODINGS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Encodings;
-
-class CJX_Encodings : public CJX_Node {
- public:
-  explicit CJX_Encodings(CXFA_Encodings* node);
-  ~CJX_Encodings() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_ENCODINGS_H_
diff --git a/fxjs/xfa/cjx_encrypt.cpp b/fxjs/xfa/cjx_encrypt.cpp
index dbed1f2..f50133d 100644
--- a/fxjs/xfa/cjx_encrypt.cpp
+++ b/fxjs/xfa/cjx_encrypt.cpp
@@ -12,18 +12,10 @@
 
 CJX_Encrypt::~CJX_Encrypt() = default;
 
-void CJX_Encrypt::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+bool CJX_Encrypt::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 void CJX_Encrypt::format(CFXJSE_Value* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {}
-
-void CJX_Encrypt::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_encrypt.h b/fxjs/xfa/cjx_encrypt.h
index 85fac15..826bc1f 100644
--- a/fxjs/xfa/cjx_encrypt.h
+++ b/fxjs/xfa/cjx_encrypt.h
@@ -8,17 +8,25 @@
 #define FXJS_XFA_CJX_ENCRYPT_H_
 
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Encrypt;
 
-class CJX_Encrypt : public CJX_Node {
+class CJX_Encrypt final : public CJX_Node {
  public:
   explicit CJX_Encrypt(CXFA_Encrypt* node);
   ~CJX_Encrypt() override;
 
-  JS_PROP(format);
-  JS_PROP(use);
-  JS_PROP(usehref);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(format);
+
+ private:
+  using Type__ = CJX_Encrypt;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Encrypt;
 };
 
 #endif  // FXJS_XFA_CJX_ENCRYPT_H_
diff --git a/fxjs/xfa/cjx_event.cpp b/fxjs/xfa/cjx_event.cpp
deleted file mode 100644
index 8cb2bce..0000000
--- a/fxjs/xfa/cjx_event.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_event.h"
-
-#include "xfa/fxfa/parser/cxfa_event.h"
-
-CJX_Event::CJX_Event(CXFA_Event* node) : CJX_Node(node) {}
-
-CJX_Event::~CJX_Event() = default;
-
-void CJX_Event::ref(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Event::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Event::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Event::activity(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_event.h b/fxjs/xfa/cjx_event.h
deleted file mode 100644
index 6c6d920..0000000
--- a/fxjs/xfa/cjx_event.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_EVENT_H_
-#define FXJS_XFA_CJX_EVENT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Event;
-
-class CJX_Event : public CJX_Node {
- public:
-  explicit CJX_Event(CXFA_Event* node);
-  ~CJX_Event() override;
-
-  JS_PROP(activity);
-  JS_PROP(ref);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_EVENT_H_
diff --git a/fxjs/xfa/cjx_eventpseudomodel.cpp b/fxjs/xfa/cjx_eventpseudomodel.cpp
index 3b2d977..d037eb7 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.cpp
+++ b/fxjs/xfa/cjx_eventpseudomodel.cpp
@@ -6,10 +6,11 @@
 
 #include "fxjs/xfa/cjx_eventpseudomodel.h"
 
+#include <algorithm>
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
@@ -17,28 +18,28 @@
 
 namespace {
 
-void StringProperty(CFXJSE_Value* pValue, WideString& wsValue, bool bSetting) {
+void StringProperty(CFXJSE_Value* pReturn, WideString* wsValue, bool bSetting) {
   if (bSetting) {
-    wsValue = pValue->ToWideString();
+    *wsValue = pReturn->ToWideString();
     return;
   }
-  pValue->SetString(wsValue.UTF8Encode().AsStringView());
+  pReturn->SetString(wsValue->ToUTF8().AsStringView());
 }
 
-void InterProperty(CFXJSE_Value* pValue, int32_t& iValue, bool bSetting) {
+void IntegerProperty(CFXJSE_Value* pReturn, int32_t* iValue, bool bSetting) {
   if (bSetting) {
-    iValue = pValue->ToInteger();
+    *iValue = pReturn->ToInteger();
     return;
   }
-  pValue->SetInteger(iValue);
+  pReturn->SetInteger(*iValue);
 }
 
-void BooleanProperty(CFXJSE_Value* pValue, bool& bValue, bool bSetting) {
+void BooleanProperty(CFXJSE_Value* pReturn, bool* bValue, bool bSetting) {
   if (bSetting) {
-    bValue = pValue->ToBoolean();
+    *bValue = pReturn->ToBoolean();
     return;
   }
-  pValue->SetBoolean(bValue);
+  pReturn->SetBoolean(*bValue);
 }
 
 }  // namespace
@@ -49,11 +50,21 @@
 
 CJX_EventPseudoModel::CJX_EventPseudoModel(CScript_EventPseudoModel* model)
     : CJX_Object(model) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_EventPseudoModel::~CJX_EventPseudoModel() {}
 
+bool CJX_EventPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+void CJX_EventPseudoModel::cancelAction(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::CancelAction, bSetting);
+}
+
 void CJX_EventPseudoModel::change(CFXJSE_Value* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
@@ -93,7 +104,15 @@
 void CJX_EventPseudoModel::newText(CFXJSE_Value* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::NewText, bSetting);
+  if (bSetting)
+    return;
+
+  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  if (!pEventParam)
+    return;
+
+  pValue->SetString(pEventParam->GetNewText().ToUTF8().AsStringView());
 }
 
 void CJX_EventPseudoModel::prevContentType(CFXJSE_Value* pValue,
@@ -150,106 +169,115 @@
   Property(pValue, XFA_Event::Target, bSetting);
 }
 
-CJS_Return CJX_EventPseudoModel::emit(
-    CJS_V8* runtime,
+CJS_Result CJX_EventPseudoModel::emit(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  if (!pScriptContext)
-    return CJS_Return(true);
-
   CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
   if (!pEventParam)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_FFWidgetHandler* pWidgetHandler = pNotify->GetWidgetHandler();
   if (!pWidgetHandler)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
-  pWidgetHandler->ProcessEvent(pEventParam->m_pTarget, pEventParam);
-  return CJS_Return(true);
+  pWidgetHandler->ProcessEvent(pEventParam->m_pTarget.Get(), pEventParam);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_EventPseudoModel::reset(
-    CJS_V8* runtime,
+CJS_Result CJX_EventPseudoModel::reset(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  if (!pScriptContext)
-    return CJS_Return(true);
-
   CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
-  if (!pEventParam)
-    return CJS_Return(true);
+  if (pEventParam)
+    *pEventParam = CXFA_EventParam();
 
-  pEventParam->Reset();
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
 void CJX_EventPseudoModel::Property(CFXJSE_Value* pValue,
                                     XFA_Event dwFlag,
                                     bool bSetting) {
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  if (!pScriptContext)
+  // Only the cancelAction, selStart, selEnd and change properties are writable.
+  if (bSetting && dwFlag != XFA_Event::CancelAction &&
+      dwFlag != XFA_Event::SelectionStart &&
+      dwFlag != XFA_Event::SelectionEnd && dwFlag != XFA_Event::Change) {
     return;
+  }
 
+  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
   if (!pEventParam)
     return;
 
   switch (dwFlag) {
     case XFA_Event::CancelAction:
-      BooleanProperty(pValue, pEventParam->m_bCancelAction, bSetting);
+      BooleanProperty(pValue, &pEventParam->m_bCancelAction, bSetting);
       break;
     case XFA_Event::Change:
-      StringProperty(pValue, pEventParam->m_wsChange, bSetting);
+      StringProperty(pValue, &pEventParam->m_wsChange, bSetting);
       break;
     case XFA_Event::CommitKey:
-      InterProperty(pValue, pEventParam->m_iCommitKey, bSetting);
+      IntegerProperty(pValue, &pEventParam->m_iCommitKey, bSetting);
       break;
     case XFA_Event::FullText:
-      StringProperty(pValue, pEventParam->m_wsFullText, bSetting);
+      StringProperty(pValue, &pEventParam->m_wsFullText, bSetting);
       break;
     case XFA_Event::Keydown:
-      BooleanProperty(pValue, pEventParam->m_bKeyDown, bSetting);
+      BooleanProperty(pValue, &pEventParam->m_bKeyDown, bSetting);
       break;
     case XFA_Event::Modifier:
-      BooleanProperty(pValue, pEventParam->m_bModifier, bSetting);
+      BooleanProperty(pValue, &pEventParam->m_bModifier, bSetting);
       break;
     case XFA_Event::NewContentType:
-      StringProperty(pValue, pEventParam->m_wsNewContentType, bSetting);
+      StringProperty(pValue, &pEventParam->m_wsNewContentType, bSetting);
       break;
     case XFA_Event::NewText:
-      StringProperty(pValue, pEventParam->m_wsNewText, bSetting);
+      NOTREACHED();
       break;
     case XFA_Event::PreviousContentType:
-      StringProperty(pValue, pEventParam->m_wsPrevContentType, bSetting);
+      StringProperty(pValue, &pEventParam->m_wsPrevContentType, bSetting);
       break;
     case XFA_Event::PreviousText:
-      StringProperty(pValue, pEventParam->m_wsPrevText, bSetting);
+      StringProperty(pValue, &pEventParam->m_wsPrevText, bSetting);
       break;
     case XFA_Event::Reenter:
-      BooleanProperty(pValue, pEventParam->m_bReenter, bSetting);
+      BooleanProperty(pValue, &pEventParam->m_bReenter, bSetting);
       break;
     case XFA_Event::SelectionEnd:
-      InterProperty(pValue, pEventParam->m_iSelEnd, bSetting);
+      IntegerProperty(pValue, &pEventParam->m_iSelEnd, bSetting);
+
+      pEventParam->m_iSelEnd = std::max(0, pEventParam->m_iSelEnd);
+      pEventParam->m_iSelEnd =
+          std::min(static_cast<size_t>(pEventParam->m_iSelEnd),
+                   pEventParam->m_wsPrevText.GetLength());
+      pEventParam->m_iSelStart =
+          std::min(pEventParam->m_iSelStart, pEventParam->m_iSelEnd);
       break;
     case XFA_Event::SelectionStart:
-      InterProperty(pValue, pEventParam->m_iSelStart, bSetting);
+      IntegerProperty(pValue, &pEventParam->m_iSelStart, bSetting);
+      pEventParam->m_iSelStart = std::max(0, pEventParam->m_iSelStart);
+      pEventParam->m_iSelStart =
+          std::min(static_cast<size_t>(pEventParam->m_iSelStart),
+                   pEventParam->m_wsPrevText.GetLength());
+      pEventParam->m_iSelEnd =
+          std::max(pEventParam->m_iSelStart, pEventParam->m_iSelEnd);
       break;
     case XFA_Event::Shift:
-      BooleanProperty(pValue, pEventParam->m_bShift, bSetting);
+      BooleanProperty(pValue, &pEventParam->m_bShift, bSetting);
       break;
     case XFA_Event::SoapFaultCode:
-      StringProperty(pValue, pEventParam->m_wsSoapFaultCode, bSetting);
+      StringProperty(pValue, &pEventParam->m_wsSoapFaultCode, bSetting);
       break;
     case XFA_Event::SoapFaultString:
-      StringProperty(pValue, pEventParam->m_wsSoapFaultString, bSetting);
+      StringProperty(pValue, &pEventParam->m_wsSoapFaultString, bSetting);
       break;
     case XFA_Event::Target:
-      break;
     default:
       break;
   }
diff --git a/fxjs/xfa/cjx_eventpseudomodel.h b/fxjs/xfa/cjx_eventpseudomodel.h
index 92d731c..6f3cc84 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.h
+++ b/fxjs/xfa/cjx_eventpseudomodel.h
@@ -7,8 +7,8 @@
 #ifndef FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
 #define FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CFXJSE_Value;
 class CScript_EventPseudoModel;
@@ -33,35 +33,43 @@
   CancelAction
 };
 
-class CJX_EventPseudoModel : public CJX_Object {
+class CJX_EventPseudoModel final : public CJX_Object {
  public:
   explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model);
   ~CJX_EventPseudoModel() override;
 
-  JS_METHOD(emit, CJX_EventPseudoModel);
-  JS_METHOD(reset, CJX_EventPseudoModel);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(change);
-  JS_PROP(commitKey);
-  JS_PROP(fullText);
-  JS_PROP(keyDown);
-  JS_PROP(modifier);
-  JS_PROP(newContentType);
-  JS_PROP(newText);
-  JS_PROP(prevContentType);
-  JS_PROP(prevText);
-  JS_PROP(reenter);
-  JS_PROP(selEnd);
-  JS_PROP(selStart);
-  JS_PROP(shift);
-  JS_PROP(soapFaultCode);
-  JS_PROP(soapFaultString);
-  JS_PROP(target);
+  JSE_METHOD(emit);
+  JSE_METHOD(reset);
+
+  JSE_PROP(cancelAction);
+  JSE_PROP(change);
+  JSE_PROP(commitKey);
+  JSE_PROP(fullText);
+  JSE_PROP(keyDown);
+  JSE_PROP(modifier);
+  JSE_PROP(newContentType);
+  JSE_PROP(newText);
+  JSE_PROP(prevContentType);
+  JSE_PROP(prevText);
+  JSE_PROP(reenter);
+  JSE_PROP(selEnd);
+  JSE_PROP(selStart);
+  JSE_PROP(shift);
+  JSE_PROP(soapFaultCode);
+  JSE_PROP(soapFaultString);
+  JSE_PROP(target);
 
  private:
-  void Property(CFXJSE_Value* pValue, XFA_Event dwFlag, bool bSetting);
+  using Type__ = CJX_EventPseudoModel;
+  using ParentType__ = CJX_Object;
 
+  static const TypeTag static_type__ = TypeTag::EventPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
+
+  void Property(CFXJSE_Value* pValue, XFA_Event dwFlag, bool bSetting);
 };
 
 #endif  // FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_exclgroup.cpp b/fxjs/xfa/cjx_exclgroup.cpp
index 8d814dc..00bf539 100644
--- a/fxjs/xfa/cjx_exclgroup.cpp
+++ b/fxjs/xfa/cjx_exclgroup.cpp
@@ -8,9 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/fxfa.h"
@@ -25,98 +25,106 @@
     {"selectedMember", selectedMember_static}};
 
 CJX_ExclGroup::CJX_ExclGroup(CXFA_ExclGroup* group) : CJX_Node(group) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_ExclGroup::~CJX_ExclGroup() {}
 
-CJS_Return CJX_ExclGroup::execEvent(
-    CJS_V8* runtime,
+bool CJX_ExclGroup::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_ExclGroup::execEvent(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   execSingleEventByName(runtime->ToWideString(params[0]).AsStringView(),
                         XFA_Element::ExclGroup);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_ExclGroup::execInitialize(
-    CJS_V8* runtime,
+CJS_Result CJX_ExclGroup::execInitialize(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify)
-    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize);
-  return CJS_Return(true);
+    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize, false,
+                                  true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_ExclGroup::execCalculate(
-    CJS_V8* runtime,
+CJS_Result CJX_ExclGroup::execCalculate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify)
-    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate);
-  return CJS_Return(true);
+    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate, false,
+                                  true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_ExclGroup::execValidate(
-    CJS_V8* runtime,
+CJS_Result CJX_ExclGroup::execValidate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* notify = GetDocument()->GetNotify();
   if (!notify)
-    return CJS_Return(runtime->NewBoolean(false));
+    return CJS_Result::Success(runtime->NewBoolean(false));
 
-  int32_t iRet = notify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Validate);
-  return CJS_Return(runtime->NewBoolean(iRet != XFA_EVENTERROR_Error));
+  XFA_EventError iRet = notify->ExecEventByDeepFirst(
+      GetXFANode(), XFA_EVENT_Validate, false, true);
+  return CJS_Result::Success(
+      runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-CJS_Return CJX_ExclGroup::selectedMember(
-    CJS_V8* runtime,
+CJS_Result CJX_ExclGroup::selectedMember(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(runtime->NewNull());
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success(runtime->NewNull());
 
   CXFA_Node* pReturnNode = nullptr;
   if (params.empty()) {
-    pReturnNode = pWidgetAcc->GetSelectedMember();
+    pReturnNode = node->GetSelectedMember();
   } else {
-    pReturnNode = pWidgetAcc->SetSelectedMember(
+    pReturnNode = node->SetSelectedMember(
         runtime->ToWideString(params[0]).AsStringView(), true);
   }
   if (!pReturnNode)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pReturnNode);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pReturnNode);
 
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
 void CJX_ExclGroup::defaultValue(CFXJSE_Value* pValue,
                                  bool bSetting,
                                  XFA_Attribute eAttribute) {
-  CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc();
-  if (!pWidgetAcc)
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
     return;
 
   if (bSetting) {
-    pWidgetAcc->SetSelectedMemberByValue(pValue->ToWideString().AsStringView(),
-                                         true, true, true);
+    node->SetSelectedMemberByValue(pValue->ToWideString().AsStringView(), true,
+                                   true, true);
     return;
   }
 
@@ -126,7 +134,7 @@
     pValue->SetNull();
     return;
   }
-  pValue->SetString(wsValue.UTF8Encode().AsStringView());
+  pValue->SetString(wsValue.ToUTF8().AsStringView());
 }
 
 void CJX_ExclGroup::rawValue(CFXJSE_Value* pValue,
@@ -139,152 +147,9 @@
                               bool bSetting,
                               XFA_Attribute eAttribute) {}
 
-void CJX_ExclGroup::access(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::accessKey(CFXJSE_Value* pValue,
+void CJX_ExclGroup::errorText(CFXJSE_Value* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::anchorType(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::borderColor(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Som_BorderColor(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::borderWidth(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Som_BorderWidth(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::colSpan(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::fillColor(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Som_FillColor(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::h(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::hAlign(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::layout(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::mandatory(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Som_Mandatory(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::mandatoryMessage(CFXJSE_Value* pValue,
-                                     bool bSetting,
-                                     XFA_Attribute eAttribute) {
-  Script_Som_MandatoryMessage(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::maxH(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::maxW(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::minH(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::minW(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::presence(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::relevant(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::validationMessage(CFXJSE_Value* pValue,
-                                      bool bSetting,
-                                      XFA_Attribute eAttribute) {
-  Script_Som_ValidationMessage(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::vAlign(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::w(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::x(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExclGroup::y(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  if (bSetting)
+    ThrowInvalidPropertyException();
 }
diff --git a/fxjs/xfa/cjx_exclgroup.h b/fxjs/xfa/cjx_exclgroup.h
index a972fba..b459808 100644
--- a/fxjs/xfa/cjx_exclgroup.h
+++ b/fxjs/xfa/cjx_exclgroup.h
@@ -7,52 +7,35 @@
 #ifndef FXJS_XFA_CJX_EXCLGROUP_H_
 #define FXJS_XFA_CJX_EXCLGROUP_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_ExclGroup;
 
-class CJX_ExclGroup : public CJX_Node {
+class CJX_ExclGroup final : public CJX_Node {
  public:
   explicit CJX_ExclGroup(CXFA_ExclGroup* group);
   ~CJX_ExclGroup() override;
 
-  JS_METHOD(execCalculate, CJX_ExclGroup);
-  JS_METHOD(execEvent, CJX_ExclGroup);
-  JS_METHOD(execInitialize, CJX_ExclGroup);
-  JS_METHOD(execValidate, CJX_ExclGroup);
-  JS_METHOD(selectedMember, CJX_ExclGroup);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(access);
-  JS_PROP(accessKey);
-  JS_PROP(anchorType);
-  JS_PROP(borderColor);
-  JS_PROP(borderWidth);
-  JS_PROP(colSpan);
-  JS_PROP(fillColor);
-  JS_PROP(h);
-  JS_PROP(hAlign);
-  JS_PROP(layout);
-  JS_PROP(mandatory);
-  JS_PROP(mandatoryMessage);
-  JS_PROP(maxH);
-  JS_PROP(maxW);
-  JS_PROP(minH);
-  JS_PROP(minW);
-  JS_PROP(presence);
-  JS_PROP(rawValue);
-  JS_PROP(relevant);
-  JS_PROP(transient);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(validationMessage);
-  JS_PROP(vAlign);
-  JS_PROP(w);
-  JS_PROP(x);
-  JS_PROP(y);
+  JSE_METHOD(execCalculate);
+  JSE_METHOD(execEvent);
+  JSE_METHOD(execInitialize);
+  JSE_METHOD(execValidate);
+  JSE_METHOD(selectedMember);
+
+  JSE_PROP(defaultValue); /* {default} */
+  JSE_PROP(errorText);
+  JSE_PROP(rawValue);
+  JSE_PROP(transient);
 
  private:
+  using Type__ = CJX_ExclGroup;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::ExclGroup;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_exdata.cpp b/fxjs/xfa/cjx_exdata.cpp
deleted file mode 100644
index 6ce72c4..0000000
--- a/fxjs/xfa/cjx_exdata.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_exdata.h"
-
-#include "xfa/fxfa/parser/cxfa_exdata.h"
-
-CJX_ExData::CJX_ExData(CXFA_ExData* node) : CJX_Content(node) {}
-
-CJX_ExData::~CJX_ExData() = default;
-
-void CJX_ExData::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExData::contentType(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExData::transferEncoding(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExData::defaultValue(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExData::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExData::maxLength(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExData::href(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_exdata.h b/fxjs/xfa/cjx_exdata.h
deleted file mode 100644
index e059da8..0000000
--- a/fxjs/xfa/cjx_exdata.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_EXDATA_H_
-#define FXJS_XFA_CJX_EXDATA_H_
-
-#include "fxjs/xfa/cjx_content.h"
-
-class CXFA_ExData;
-
-class CJX_ExData : public CJX_Content {
- public:
-  explicit CJX_ExData(CXFA_ExData* node);
-  ~CJX_ExData() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(contentType);
-  JS_PROP(href);
-  JS_PROP(maxLength);
-  JS_PROP(transferEncoding);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_EXDATA_H_
diff --git a/fxjs/xfa/cjx_execute.cpp b/fxjs/xfa/cjx_execute.cpp
deleted file mode 100644
index 5fc4a77..0000000
--- a/fxjs/xfa/cjx_execute.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_execute.h"
-
-#include "xfa/fxfa/parser/cxfa_execute.h"
-
-CJX_Execute::CJX_Execute(CXFA_Execute* node) : CJX_Node(node) {}
-
-CJX_Execute::~CJX_Execute() = default;
-
-void CJX_Execute::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Execute::connection(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Execute::runAt(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Execute::executeType(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Execute::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_execute.h b/fxjs/xfa/cjx_execute.h
deleted file mode 100644
index ef7f629..0000000
--- a/fxjs/xfa/cjx_execute.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_EXECUTE_H_
-#define FXJS_XFA_CJX_EXECUTE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Execute;
-
-class CJX_Execute : public CJX_Node {
- public:
-  explicit CJX_Execute(CXFA_Execute* node);
-  ~CJX_Execute() override;
-
-  JS_PROP(connection);
-  JS_PROP(executeType);
-  JS_PROP(runAt);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_EXECUTE_H_
diff --git a/fxjs/xfa/cjx_exobject.cpp b/fxjs/xfa/cjx_exobject.cpp
deleted file mode 100644
index 2252d53..0000000
--- a/fxjs/xfa/cjx_exobject.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_exobject.h"
-
-#include "xfa/fxfa/parser/cxfa_exobject.h"
-
-CJX_ExObject::CJX_ExObject(CXFA_ExObject* node) : CJX_Node(node) {}
-
-CJX_ExObject::~CJX_ExObject() = default;
-
-void CJX_ExObject::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExObject::codeType(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExObject::archive(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExObject::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExObject::codeBase(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ExObject::classId(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_exobject.h b/fxjs/xfa/cjx_exobject.h
deleted file mode 100644
index 2fec24a..0000000
--- a/fxjs/xfa/cjx_exobject.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_EXOBJECT_H_
-#define FXJS_XFA_CJX_EXOBJECT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_ExObject;
-
-class CJX_ExObject : public CJX_Node {
- public:
-  explicit CJX_ExObject(CXFA_ExObject* node);
-  ~CJX_ExObject() override;
-
-  JS_PROP(archive);
-  JS_PROP(classId);
-  JS_PROP(codeBase);
-  JS_PROP(codeType);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_EXOBJECT_H_
diff --git a/fxjs/xfa/cjx_extras.cpp b/fxjs/xfa/cjx_extras.cpp
index 73ea8ea..a2e2eea 100644
--- a/fxjs/xfa/cjx_extras.cpp
+++ b/fxjs/xfa/cjx_extras.cpp
@@ -12,18 +12,10 @@
 
 CJX_Extras::~CJX_Extras() = default;
 
-void CJX_Extras::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+bool CJX_Extras::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 void CJX_Extras::type(CFXJSE_Value* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {}
-
-void CJX_Extras::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_extras.h b/fxjs/xfa/cjx_extras.h
index 9f57903..0723575 100644
--- a/fxjs/xfa/cjx_extras.h
+++ b/fxjs/xfa/cjx_extras.h
@@ -8,17 +8,25 @@
 #define FXJS_XFA_CJX_EXTRAS_H_
 
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Extras;
 
-class CJX_Extras : public CJX_Node {
+class CJX_Extras final : public CJX_Node {
  public:
   explicit CJX_Extras(CXFA_Extras* node);
   ~CJX_Extras() override;
 
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(type);
+
+ private:
+  using Type__ = CJX_Extras;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Extras;
 };
 
 #endif  // FXJS_XFA_CJX_EXTRAS_H_
diff --git a/fxjs/xfa/cjx_field.cpp b/fxjs/xfa/cjx_field.cpp
index 299c6ef..ef334a3 100644
--- a/fxjs/xfa/cjx_field.cpp
+++ b/fxjs/xfa/cjx_field.cpp
@@ -8,9 +8,10 @@
 
 #include <vector>
 
-#include "core/fxcrt/cfx_decimal.h"
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/fxfa.h"
@@ -33,177 +34,185 @@
     {"setItemState", setItemState_static}};
 
 CJX_Field::CJX_Field(CXFA_Field* field) : CJX_Container(field) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Field::~CJX_Field() {}
 
-CJS_Return CJX_Field::clearItems(
-    CJS_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (pWidgetAcc)
-    pWidgetAcc->DeleteItem(-1, true, false);
-  return CJS_Return(true);
+bool CJX_Field::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_Field::execEvent(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::clearItems(
+    CFX_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_Node* node = GetXFANode();
+  if (node->IsWidgetReady())
+    node->DeleteItem(-1, true, false);
+  return CJS_Result::Success();
+}
+
+CJS_Result CJX_Field::execEvent(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString eventString = runtime->ToWideString(params[0]);
-  int32_t iRet =
+  XFA_EventError iRet =
       execSingleEventByName(eventString.AsStringView(), XFA_Element::Field);
-  if (eventString != L"validate")
-    return CJS_Return(true);
+  if (!eventString.EqualsASCII("validate"))
+    return CJS_Result::Success();
 
-  return CJS_Return(runtime->NewBoolean(iRet != XFA_EVENTERROR_Error));
+  return CJS_Result::Success(
+      runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-CJS_Return CJX_Field::execInitialize(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::execInitialize(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize, false,
                                   false);
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Field::deleteItem(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::deleteItem(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(true);
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success();
 
-  bool bValue = pWidgetAcc->DeleteItem(runtime->ToInt32(params[0]), true, true);
-  return CJS_Return(runtime->NewBoolean(bValue));
+  bool bValue = node->DeleteItem(runtime->ToInt32(params[0]), true, true);
+  return CJS_Result::Success(runtime->NewBoolean(bValue));
 }
 
-CJS_Return CJX_Field::getSaveItem(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::getSaveItem(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int32_t iIndex = runtime->ToInt32(params[0]);
   if (iIndex < 0)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(runtime->NewNull());
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success(runtime->NewNull());
 
-  Optional<WideString> value = pWidgetAcc->GetChoiceListItem(iIndex, true);
+  Optional<WideString> value = node->GetChoiceListItem(iIndex, true);
   if (!value)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
-  return CJS_Return(runtime->NewString(value->UTF8Encode().AsStringView()));
+  return CJS_Result::Success(
+      runtime->NewString(value->ToUTF8().AsStringView()));
 }
 
-CJS_Return CJX_Field::boundItem(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::boundItem(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(true);
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success();
 
   WideString value = runtime->ToWideString(params[0]);
-  WideString boundValue = pWidgetAcc->GetItemValue(value.AsStringView());
-  return CJS_Return(runtime->NewString(boundValue.UTF8Encode().AsStringView()));
+  WideString boundValue = node->GetItemValue(value.AsStringView());
+  return CJS_Result::Success(
+      runtime->NewString(boundValue.ToUTF8().AsStringView()));
 }
 
-CJS_Return CJX_Field::getItemState(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::getItemState(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(true);
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success();
 
-  int32_t state = pWidgetAcc->GetItemState(runtime->ToInt32(params[0]));
-  return CJS_Return(runtime->NewBoolean(state != 0));
+  int32_t state = node->GetItemState(runtime->ToInt32(params[0]));
+  return CJS_Result::Success(runtime->NewBoolean(state != 0));
 }
 
-CJS_Return CJX_Field::execCalculate(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::execCalculate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate, false,
                                   false);
   }
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Field::getDisplayItem(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::getDisplayItem(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int32_t iIndex = runtime->ToInt32(params[0]);
   if (iIndex < 0)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(runtime->NewNull());
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success(runtime->NewNull());
 
-  Optional<WideString> value = pWidgetAcc->GetChoiceListItem(iIndex, false);
+  Optional<WideString> value = node->GetChoiceListItem(iIndex, false);
   if (!value)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
-  return CJS_Return(runtime->NewString(value->UTF8Encode().AsStringView()));
+  return CJS_Result::Success(
+      runtime->NewString(value->ToUTF8().AsStringView()));
 }
 
-CJS_Return CJX_Field::setItemState(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::setItemState(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(true);
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success();
 
   int32_t iIndex = runtime->ToInt32(params[0]);
   if (runtime->ToInt32(params[1]) != 0) {
-    pWidgetAcc->SetItemState(iIndex, true, true, true, true);
-    return CJS_Return(true);
+    node->SetItemState(iIndex, true, true, true, true);
+    return CJS_Result::Success();
   }
-  if (pWidgetAcc->GetItemState(iIndex))
-    pWidgetAcc->SetItemState(iIndex, false, true, true, true);
+  if (node->GetItemState(iIndex))
+    node->SetItemState(iIndex, false, true, true, true);
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Field::addItem(CJS_V8* runtime,
+CJS_Result CJX_Field::addItem(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1 && params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(GetXFAObject())->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return CJS_Return(true);
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
+    return CJS_Result::Success();
 
   WideString label;
   if (params.size() >= 1)
@@ -213,53 +222,49 @@
   if (params.size() >= 2)
     value = runtime->ToWideString(params[1]);
 
-  pWidgetAcc->InsertItem(label, value, true);
-  return CJS_Return(true);
+  node->InsertItem(label, value, true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Field::execValidate(
-    CJS_V8* runtime,
+CJS_Result CJX_Field::execValidate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(runtime->NewBoolean(false));
+    return CJS_Result::Success(runtime->NewBoolean(false));
 
-  int32_t iRet = pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Validate,
-                                               false, false);
-  return CJS_Return(runtime->NewBoolean(iRet != XFA_EVENTERROR_Error));
+  XFA_EventError iRet = pNotify->ExecEventByDeepFirst(
+      GetXFANode(), XFA_EVENT_Validate, false, false);
+  return CJS_Result::Success(
+      runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
 void CJX_Field::defaultValue(CFXJSE_Value* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
   CXFA_Node* xfaNode = GetXFANode();
-  CXFA_WidgetAcc* pWidgetAcc = xfaNode->GetWidgetAcc();
-  if (!pWidgetAcc)
+  if (!xfaNode->IsWidgetReady())
     return;
 
   if (bSetting) {
     if (pValue) {
-      pWidgetAcc->SetPreNull(pWidgetAcc->IsNull());
-      pWidgetAcc->SetIsNull(pValue->IsNull());
+      xfaNode->SetPreNull(xfaNode->IsNull());
+      xfaNode->SetIsNull(pValue->IsNull());
     }
 
     WideString wsNewText;
     if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
       wsNewText = pValue->ToWideString();
+    if (xfaNode->GetUIChildNode()->GetElementType() == XFA_Element::NumericEdit)
+      wsNewText = xfaNode->NumericLimit(wsNewText);
 
-    CXFA_Node* pUIChild = pWidgetAcc->GetUIChild();
-    if (pUIChild->GetElementType() == XFA_Element::NumericEdit) {
-      wsNewText = pWidgetAcc->NumericLimit(
-          wsNewText, pWidgetAcc->GetLeadDigits(), pWidgetAcc->GetFracDigits());
-    }
-
-    CXFA_WidgetAcc* pContainerWidgetAcc = xfaNode->GetContainerWidgetAcc();
+    CXFA_Node* pContainerNode = xfaNode->GetContainerNode();
     WideString wsFormatText(wsNewText);
-    if (pContainerWidgetAcc)
-      wsFormatText = pContainerWidgetAcc->GetFormatDataValue(wsNewText);
+    if (pContainerNode)
+      wsFormatText = pContainerNode->GetFormatDataValue(wsNewText);
 
     SetContent(wsNewText, wsFormatText, true, true, true);
     return;
@@ -271,64 +276,79 @@
     return;
   }
 
-  CXFA_Node* pUIChild = pWidgetAcc->GetUIChild();
   CXFA_Node* formValue = xfaNode->GetFormValueIfExists();
   CXFA_Node* pNode = formValue ? formValue->GetFirstChild() : nullptr;
   if (pNode && pNode->GetElementType() == XFA_Element::Decimal) {
-    if (pUIChild->GetElementType() == XFA_Element::NumericEdit &&
+    if (xfaNode->GetUIChildNode()->GetElementType() ==
+            XFA_Element::NumericEdit &&
         (pNode->JSObject()->GetInteger(XFA_Attribute::FracDigits) == -1)) {
-      pValue->SetString(content.UTF8Encode().AsStringView());
+      pValue->SetString(content.ToUTF8().AsStringView());
     } else {
-      CFX_Decimal decimal(content.AsStringView());
-      pValue->SetFloat((float)(double)decimal);
+      CFGAS_Decimal decimal(content.AsStringView());
+      pValue->SetFloat(decimal.ToFloat());
     }
   } else if (pNode && pNode->GetElementType() == XFA_Element::Integer) {
     pValue->SetInteger(FXSYS_wtoi(content.c_str()));
   } else if (pNode && pNode->GetElementType() == XFA_Element::Boolean) {
-    pValue->SetBoolean(FXSYS_wtoi(content.c_str()) == 0 ? false : true);
+    pValue->SetBoolean(FXSYS_wtoi(content.c_str()) != 0);
   } else if (pNode && pNode->GetElementType() == XFA_Element::Float) {
-    CFX_Decimal decimal(content.AsStringView());
-    pValue->SetFloat((float)(double)decimal);
+    CFGAS_Decimal decimal(content.AsStringView());
+    pValue->SetFloat(decimal.ToFloat());
   } else {
-    pValue->SetString(content.UTF8Encode().AsStringView());
+    pValue->SetString(content.ToUTF8().AsStringView());
   }
 }
 
 void CJX_Field::editValue(CFXJSE_Value* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {
-  CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc();
-  if (!pWidgetAcc)
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
     return;
 
   if (bSetting) {
-    pWidgetAcc->SetValue(XFA_VALUEPICTURE_Edit, pValue->ToWideString());
+    node->SetValue(XFA_VALUEPICTURE_Edit, pValue->ToWideString());
     return;
   }
   pValue->SetString(
-      pWidgetAcc->GetValue(XFA_VALUEPICTURE_Edit).UTF8Encode().AsStringView());
+      node->GetValue(XFA_VALUEPICTURE_Edit).ToUTF8().AsStringView());
 }
 
 void CJX_Field::formatMessage(CFXJSE_Value* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
-  Script_Som_Message(pValue, bSetting, XFA_SOM_FormatMessage);
+  ScriptSomMessage(pValue, bSetting, XFA_SOM_FormatMessage);
 }
 
 void CJX_Field::formattedValue(CFXJSE_Value* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
-  CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc();
-  if (!pWidgetAcc)
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
     return;
 
   if (bSetting) {
-    pWidgetAcc->SetValue(XFA_VALUEPICTURE_Display, pValue->ToWideString());
+    node->SetValue(XFA_VALUEPICTURE_Display, pValue->ToWideString());
     return;
   }
-  pValue->SetString(pWidgetAcc->GetValue(XFA_VALUEPICTURE_Display)
-                        .UTF8Encode()
-                        .AsStringView());
+  pValue->SetString(
+      node->GetValue(XFA_VALUEPICTURE_Display).ToUTF8().AsStringView());
+}
+
+void CJX_Field::length(CFXJSE_Value* pValue,
+                       bool bSetting,
+                       XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady()) {
+    pValue->SetInteger(0);
+    return;
+  }
+  pValue->SetInteger(node->CountChoiceListItems(true));
 }
 
 void CJX_Field::parentSubform(CFXJSE_Value* pValue,
@@ -344,130 +364,22 @@
 void CJX_Field::selectedIndex(CFXJSE_Value* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
-  CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc();
-  if (!pWidgetAcc)
+  CXFA_Node* node = GetXFANode();
+  if (!node->IsWidgetReady())
     return;
 
   if (!bSetting) {
-    pValue->SetInteger(pWidgetAcc->GetSelectedItem(0));
+    pValue->SetInteger(node->GetSelectedItem(0));
     return;
   }
 
   int32_t iIndex = pValue->ToInteger();
   if (iIndex == -1) {
-    pWidgetAcc->ClearAllSelections();
+    node->ClearAllSelections();
     return;
   }
 
-  pWidgetAcc->SetItemState(iIndex, true, true, true, true);
-}
-
-void CJX_Field::access(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::accessKey(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::anchorType(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::borderColor(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Som_BorderColor(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::borderWidth(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Som_BorderWidth(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::colSpan(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::fillColor(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Som_FillColor(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::fontColor(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Som_FontColor(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::h(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::hAlign(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::locale(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::mandatory(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Som_Mandatory(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::mandatoryMessage(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Som_MandatoryMessage(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::maxH(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::maxW(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::minH(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::minW(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::presence(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  node->SetItemState(iIndex, true, true, true, true);
 }
 
 void CJX_Field::rawValue(CFXJSE_Value* pValue,
@@ -475,57 +387,3 @@
                          XFA_Attribute eAttribute) {
   defaultValue(pValue, bSetting, eAttribute);
 }
-
-void CJX_Field::relevant(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::rotate(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::validationMessage(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute) {
-  Script_Som_ValidationMessage(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::vAlign(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::w(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::x(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Field::y(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_field.h b/fxjs/xfa/cjx_field.h
index 4f7238f..6e16e7a 100644
--- a/fxjs/xfa/cjx_field.h
+++ b/fxjs/xfa/cjx_field.h
@@ -7,65 +7,46 @@
 #ifndef FXJS_XFA_CJX_FIELD_H_
 #define FXJS_XFA_CJX_FIELD_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_container.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Field;
 
-class CJX_Field : public CJX_Container {
+class CJX_Field final : public CJX_Container {
  public:
   explicit CJX_Field(CXFA_Field* field);
   ~CJX_Field() override;
 
-  JS_METHOD(addItem, CJX_Field);
-  JS_METHOD(boundItem, CJX_Field);
-  JS_METHOD(clearItems, CJX_Field);
-  JS_METHOD(deleteItem, CJX_Field);
-  JS_METHOD(execCalculate, CJX_Field);
-  JS_METHOD(execEvent, CJX_Field);
-  JS_METHOD(execInitialize, CJX_Field);
-  JS_METHOD(execValidate, CJX_Field);
-  JS_METHOD(getDisplayItem, CJX_Field);
-  JS_METHOD(getItemState, CJX_Field);
-  JS_METHOD(getSaveItem, CJX_Field);
-  JS_METHOD(setItemState, CJX_Field);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(access);
-  JS_PROP(accessKey);
-  JS_PROP(anchorType);
-  JS_PROP(borderColor);
-  JS_PROP(borderWidth);
-  JS_PROP(colSpan);
-  JS_PROP(editValue);
-  JS_PROP(fillColor);
-  JS_PROP(fontColor);
-  JS_PROP(formatMessage);
-  JS_PROP(formattedValue);
-  JS_PROP(h);
-  JS_PROP(hAlign);
-  JS_PROP(locale);
-  JS_PROP(mandatory);
-  JS_PROP(mandatoryMessage);
-  JS_PROP(maxH);
-  JS_PROP(maxW);
-  JS_PROP(minH);
-  JS_PROP(minW);
-  JS_PROP(parentSubform);
-  JS_PROP(presence);
-  JS_PROP(rawValue);
-  JS_PROP(relevant);
-  JS_PROP(rotate);
-  JS_PROP(selectedIndex);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(validationMessage);
-  JS_PROP(vAlign);
-  JS_PROP(w);
-  JS_PROP(x);
-  JS_PROP(y);
+  JSE_METHOD(addItem);
+  JSE_METHOD(boundItem);
+  JSE_METHOD(clearItems);
+  JSE_METHOD(deleteItem);
+  JSE_METHOD(execCalculate);
+  JSE_METHOD(execEvent);
+  JSE_METHOD(execInitialize);
+  JSE_METHOD(execValidate);
+  JSE_METHOD(getDisplayItem);
+  JSE_METHOD(getItemState);
+  JSE_METHOD(getSaveItem);
+  JSE_METHOD(setItemState);
+
+  JSE_PROP(defaultValue); /* {default} */
+  JSE_PROP(editValue);
+  JSE_PROP(formatMessage);
+  JSE_PROP(formattedValue);
+  JSE_PROP(length);
+  JSE_PROP(parentSubform);
+  JSE_PROP(rawValue);
+  JSE_PROP(selectedIndex);
 
  private:
+  using Type__ = CJX_Field;
+  using ParentType__ = CJX_Container;
+
+  static const TypeTag static_type__ = TypeTag::Field;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_fill.cpp b/fxjs/xfa/cjx_fill.cpp
deleted file mode 100644
index 1c6aa95..0000000
--- a/fxjs/xfa/cjx_fill.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_fill.h"
-
-#include "xfa/fxfa/parser/cxfa_fill.h"
-
-CJX_Fill::CJX_Fill(CXFA_Fill* node) : CJX_Node(node) {}
-
-CJX_Fill::~CJX_Fill() = default;
-
-void CJX_Fill::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Fill::presence(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Fill::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_fill.h b/fxjs/xfa/cjx_fill.h
deleted file mode 100644
index 869e3cc..0000000
--- a/fxjs/xfa/cjx_fill.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_FILL_H_
-#define FXJS_XFA_CJX_FILL_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Fill;
-
-class CJX_Fill : public CJX_Node {
- public:
-  explicit CJX_Fill(CXFA_Fill* node);
-  ~CJX_Fill() override;
-
-  JS_PROP(presence);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_FILL_H_
diff --git a/fxjs/xfa/cjx_filter.cpp b/fxjs/xfa/cjx_filter.cpp
deleted file mode 100644
index e43dfa8..0000000
--- a/fxjs/xfa/cjx_filter.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_filter.h"
-
-#include "xfa/fxfa/parser/cxfa_filter.h"
-
-CJX_Filter::CJX_Filter(CXFA_Filter* node) : CJX_Node(node) {}
-
-CJX_Filter::~CJX_Filter() = default;
-
-void CJX_Filter::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Filter::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Filter::addRevocationInfo(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_filter.h b/fxjs/xfa/cjx_filter.h
deleted file mode 100644
index 275d509..0000000
--- a/fxjs/xfa/cjx_filter.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_FILTER_H_
-#define FXJS_XFA_CJX_FILTER_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Filter;
-
-class CJX_Filter : public CJX_Node {
- public:
-  explicit CJX_Filter(CXFA_Filter* node);
-  ~CJX_Filter() override;
-
-  JS_PROP(addRevocationInfo);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_FILTER_H_
diff --git a/fxjs/xfa/cjx_float.cpp b/fxjs/xfa/cjx_float.cpp
deleted file mode 100644
index 0480812..0000000
--- a/fxjs/xfa/cjx_float.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_float.h"
-
-#include "xfa/fxfa/parser/cxfa_float.h"
-
-CJX_Float::CJX_Float(CXFA_Float* node) : CJX_Content(node) {}
-
-CJX_Float::~CJX_Float() = default;
-
-void CJX_Float::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Float::defaultValue(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Float::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Float::value(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_float.h b/fxjs/xfa/cjx_float.h
deleted file mode 100644
index 1c9489b..0000000
--- a/fxjs/xfa/cjx_float.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_FLOAT_H_
-#define FXJS_XFA_CJX_FLOAT_H_
-
-#include "fxjs/xfa/cjx_content.h"
-
-class CXFA_Float;
-
-class CJX_Float : public CJX_Content {
- public:
-  explicit CJX_Float(CXFA_Float* node);
-  ~CJX_Float() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_FLOAT_H_
diff --git a/fxjs/xfa/cjx_font.cpp b/fxjs/xfa/cjx_font.cpp
deleted file mode 100644
index d0c6237..0000000
--- a/fxjs/xfa/cjx_font.cpp
+++ /dev/null
@@ -1,115 +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 "fxjs/xfa/cjx_font.h"
-
-#include "xfa/fxfa/parser/cxfa_font.h"
-
-CJX_Font::CJX_Font(CXFA_Font* node) : CJX_Node(node) {}
-
-CJX_Font::~CJX_Font() = default;
-
-void CJX_Font::lineThrough(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::typeface(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::fontHorizontalScale(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::kerningMode(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::underline(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::baselineShift(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::overlinePeriod(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::letterSpacing(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::lineThroughPeriod(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::fontVerticalScale(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::size(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::posture(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::weight(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::underlinePeriod(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Font::overline(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_font.h b/fxjs/xfa/cjx_font.h
deleted file mode 100644
index 65b40e4..0000000
--- a/fxjs/xfa/cjx_font.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_FONT_H_
-#define FXJS_XFA_CJX_FONT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Font;
-
-class CJX_Font : public CJX_Node {
- public:
-  explicit CJX_Font(CXFA_Font* node);
-  ~CJX_Font() override;
-
-  JS_PROP(baselineShift);
-  JS_PROP(fontHorizontalScale);
-  JS_PROP(fontVerticalScale);
-  JS_PROP(kerningMode);
-  JS_PROP(letterSpacing);
-  JS_PROP(lineThrough);
-  JS_PROP(lineThroughPeriod);
-  JS_PROP(overline);
-  JS_PROP(overlinePeriod);
-  JS_PROP(posture);
-  JS_PROP(size);
-  JS_PROP(typeface);
-  JS_PROP(underline);
-  JS_PROP(underlinePeriod);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(weight);
-};
-
-#endif  // FXJS_XFA_CJX_FONT_H_
diff --git a/fxjs/xfa/cjx_form.cpp b/fxjs/xfa/cjx_form.cpp
index 54f06fe..07e8602 100644
--- a/fxjs/xfa/cjx_form.cpp
+++ b/fxjs/xfa/cjx_form.cpp
@@ -8,9 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
@@ -26,98 +26,117 @@
     {"remerge", remerge_static}};
 
 CJX_Form::CJX_Form(CXFA_Form* form) : CJX_Model(form) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Form::~CJX_Form() {}
 
-CJS_Return CJX_Form::formNodes(
-    CJS_V8* runtime,
+bool CJX_Form::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Form::formNodes(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pDataNode = ToNode(runtime->ToXFAObject(params[0]));
+  CXFA_Node* pDataNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   if (!pDataNode)
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  std::vector<CXFA_Node*> formItems;
   CXFA_ArrayNodeList* pFormNodes = new CXFA_ArrayNodeList(GetDocument());
-  pFormNodes->SetArrayNodeList(formItems);
-
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pFormNodes);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pFormNodes);
+
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
-CJS_Return CJX_Form::remerge(CJS_V8* runtime,
+CJS_Result CJX_Form::remerge(CFX_V8* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   GetDocument()->DoDataRemerge(true);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Form::execInitialize(
-    CJS_V8* runtime,
+CJS_Result CJX_Form::execInitialize(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify)
-    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize);
-  return CJS_Return(true);
+    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize, false,
+                                  true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Form::recalculate(
-    CJS_V8* runtime,
+CJS_Result CJX_Form::recalculate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_EventParam* pEventParam =
       GetDocument()->GetScriptContext()->GetEventParam();
-  if (pEventParam->m_eType == XFA_EVENT_Calculate ||
-      pEventParam->m_eType == XFA_EVENT_InitCalculate) {
-    return CJS_Return(true);
+  if (pEventParam && (pEventParam->m_eType == XFA_EVENT_Calculate ||
+                      pEventParam->m_eType == XFA_EVENT_InitCalculate)) {
+    return CJS_Result::Success();
   }
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify || runtime->ToInt32(params[0]) != 0)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
-  pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate);
-  pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Validate);
-  pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Ready, true);
-  return CJS_Return(true);
+  pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate, false, true);
+  pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Validate, false, true);
+  pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Ready, true, true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Form::execCalculate(
-    CJS_V8* runtime,
+CJS_Result CJX_Form::execCalculate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify)
-    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate);
-  return CJS_Return(true);
+    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate, false,
+                                  true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Form::execValidate(
-    CJS_V8* runtime,
+CJS_Result CJX_Form::execValidate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 0)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(runtime->NewBoolean(false));
+    return CJS_Result::Success(runtime->NewBoolean(false));
 
-  int32_t iRet =
-      pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Validate);
-  return CJS_Return(runtime->NewBoolean(iRet != XFA_EVENTERROR_Error));
+  XFA_EventError iRet = pNotify->ExecEventByDeepFirst(
+      GetXFANode(), XFA_EVENT_Validate, false, true);
+  return CJS_Result::Success(
+      runtime->NewBoolean(iRet != XFA_EventError::kError));
+}
+
+void CJX_Form::checksumS(CFXJSE_Value* pValue,
+                         bool bSetting,
+                         XFA_Attribute eAttribute) {
+  if (bSetting) {
+    SetAttribute(XFA_Attribute::Checksum, pValue->ToWideString().AsStringView(),
+                 false);
+    return;
+  }
+
+  Optional<WideString> checksum = TryAttribute(XFA_Attribute::Checksum, false);
+  pValue->SetString(checksum ? checksum->ToUTF8().AsStringView() : "");
 }
diff --git a/fxjs/xfa/cjx_form.h b/fxjs/xfa/cjx_form.h
index e4d64da..c7b1ce3 100644
--- a/fxjs/xfa/cjx_form.h
+++ b/fxjs/xfa/cjx_form.h
@@ -7,24 +7,33 @@
 #ifndef FXJS_XFA_CJX_FORM_H_
 #define FXJS_XFA_CJX_FORM_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_model.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Form;
 
-class CJX_Form : public CJX_Model {
+class CJX_Form final : public CJX_Model {
  public:
   explicit CJX_Form(CXFA_Form* form);
   ~CJX_Form() override;
 
-  JS_METHOD(execCalculate, CJX_Form);
-  JS_METHOD(execInitialize, CJX_Form);
-  JS_METHOD(execValidate, CJX_Form);
-  JS_METHOD(formNodes, CJX_Form);
-  JS_METHOD(recalculate, CJX_Form);
-  JS_METHOD(remerge, CJX_Form);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_METHOD(execCalculate);
+  JSE_METHOD(execInitialize);
+  JSE_METHOD(execValidate);
+  JSE_METHOD(formNodes);
+  JSE_METHOD(recalculate);
+  JSE_METHOD(remerge);
+
+  JSE_PROP(checksumS);
 
  private:
+  using Type__ = CJX_Form;
+  using ParentType__ = CJX_Model;
+
+  static const TypeTag static_type__ = TypeTag::Form;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_format.cpp b/fxjs/xfa/cjx_format.cpp
deleted file mode 100644
index 9786502..0000000
--- a/fxjs/xfa/cjx_format.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_format.h"
-
-#include "xfa/fxfa/parser/cxfa_format.h"
-
-CJX_Format::CJX_Format(CXFA_Format* node) : CJX_Node(node) {}
-
-CJX_Format::~CJX_Format() = default;
-
-void CJX_Format::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Format::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_format.h b/fxjs/xfa/cjx_format.h
deleted file mode 100644
index 5e8c971..0000000
--- a/fxjs/xfa/cjx_format.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_FORMAT_H_
-#define FXJS_XFA_CJX_FORMAT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Format;
-
-class CJX_Format : public CJX_Node {
- public:
-  explicit CJX_Format(CXFA_Format* node);
-  ~CJX_Format() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_FORMAT_H_
diff --git a/fxjs/xfa/cjx_handler.cpp b/fxjs/xfa/cjx_handler.cpp
index 3e09a75..21be88c 100644
--- a/fxjs/xfa/cjx_handler.cpp
+++ b/fxjs/xfa/cjx_handler.cpp
@@ -12,24 +12,10 @@
 
 CJX_Handler::~CJX_Handler() = default;
 
-void CJX_Handler::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Handler::type(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+bool CJX_Handler::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 void CJX_Handler::version(CFXJSE_Value* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {}
-
-void CJX_Handler::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_handler.h b/fxjs/xfa/cjx_handler.h
index 373328b..348eaf1 100644
--- a/fxjs/xfa/cjx_handler.h
+++ b/fxjs/xfa/cjx_handler.h
@@ -8,18 +8,25 @@
 #define FXJS_XFA_CJX_HANDLER_H_
 
 #include "fxjs/xfa/cjx_textnode.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Handler;
 
-class CJX_Handler : public CJX_TextNode {
+class CJX_Handler final : public CJX_TextNode {
  public:
   explicit CJX_Handler(CXFA_Handler* node);
   ~CJX_Handler() override;
 
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(version);
-  JS_PROP(usehref);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(version);
+
+ private:
+  using Type__ = CJX_Handler;
+  using ParentType__ = CJX_TextNode;
+
+  static const TypeTag static_type__ = TypeTag::Handler;
 };
 
 #endif  // FXJS_XFA_CJX_HANDLER_H_
diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp
index 12aa2bd..c45495c 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel.cpp
@@ -9,18 +9,18 @@
 #include <memory>
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
 namespace {
 
-int32_t FilterName(const WideStringView& wsExpression,
+int32_t FilterName(WideStringView wsExpression,
                    int32_t nStart,
                    WideString& wsFilter) {
   ASSERT(nStart > -1);
@@ -28,16 +28,18 @@
   if (nStart >= iLength)
     return iLength;
 
-  wchar_t* pBuf = wsFilter.GetBuffer(iLength - nStart);
   int32_t nCount = 0;
-  const wchar_t* pSrc = wsExpression.unterminated_c_str();
-  wchar_t wCur;
-  while (nStart < iLength) {
-    wCur = pSrc[nStart++];
-    if (wCur == ',')
-      break;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> pBuf = wsFilter.GetBuffer(iLength - nStart);
+    const wchar_t* pSrc = wsExpression.unterminated_c_str();
+    while (nStart < iLength) {
+      wchar_t wCur = pSrc[nStart++];
+      if (wCur == ',')
+        break;
 
-    pBuf[nCount++] = wCur;
+      pBuf[nCount++] = wCur;
+    }
   }
   wsFilter.ReleaseBuffer(nCount);
   wsFilter.Trim();
@@ -65,11 +67,15 @@
 
 CJX_HostPseudoModel::CJX_HostPseudoModel(CScript_HostPseudoModel* model)
     : CJX_Object(model) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_HostPseudoModel::~CJX_HostPseudoModel() {}
 
+bool CJX_HostPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_HostPseudoModel::appType(CFXJSE_Value* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
@@ -93,11 +99,11 @@
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
   if (bSetting) {
-    pNotify->GetDocEnvironment()->SetCalculationsEnabled(hDoc,
-                                                         pValue->ToBoolean());
+    hDoc->GetDocEnvironment()->SetCalculationsEnabled(hDoc,
+                                                      pValue->ToBoolean());
     return;
   }
-  pValue->SetBoolean(pNotify->GetDocEnvironment()->IsCalculationsEnabled(hDoc));
+  pValue->SetBoolean(hDoc->GetDocEnvironment()->IsCalculationsEnabled(hDoc));
 }
 
 void CJX_HostPseudoModel::currentPage(CFXJSE_Value* pValue,
@@ -109,10 +115,10 @@
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
   if (bSetting) {
-    pNotify->GetDocEnvironment()->SetCurrentPage(hDoc, pValue->ToInteger());
+    hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, pValue->ToInteger());
     return;
   }
-  pValue->SetInteger(pNotify->GetDocEnvironment()->GetCurrentPage(hDoc));
+  pValue->SetInteger(hDoc->GetDocEnvironment()->GetCurrentPage(hDoc));
 }
 
 void CJX_HostPseudoModel::language(CFXJSE_Value* pValue,
@@ -123,11 +129,11 @@
     return;
 
   if (bSetting) {
-    ThrowException(L"Unable to set language value.");
+    ThrowException(WideString::FromASCII("Unable to set language value."));
     return;
   }
   pValue->SetString(
-      pNotify->GetAppProvider()->GetLanguage().UTF8Encode().AsStringView());
+      pNotify->GetAppProvider()->GetLanguage().ToUTF8().AsStringView());
 }
 
 void CJX_HostPseudoModel::numPages(CFXJSE_Value* pValue,
@@ -139,10 +145,10 @@
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
   if (bSetting) {
-    ThrowException(L"Unable to set numPages value.");
+    ThrowException(WideString::FromASCII("Unable to set numPages value."));
     return;
   }
-  pValue->SetInteger(pNotify->GetDocEnvironment()->CountPages(hDoc));
+  pValue->SetInteger(hDoc->GetDocEnvironment()->CountPages(hDoc));
 }
 
 void CJX_HostPseudoModel::platform(CFXJSE_Value* pValue,
@@ -153,11 +159,11 @@
     return;
 
   if (bSetting) {
-    ThrowException(L"Unable to set platform value.");
+    ThrowException(WideString::FromASCII("Unable to set platform value."));
     return;
   }
   pValue->SetString(
-      pNotify->GetAppProvider()->GetPlatform().UTF8Encode().AsStringView());
+      pNotify->GetAppProvider()->GetPlatform().ToUTF8().AsStringView());
 }
 
 void CJX_HostPseudoModel::title(CFXJSE_Value* pValue,
@@ -172,13 +178,13 @@
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
   if (bSetting) {
-    pNotify->GetDocEnvironment()->SetTitle(hDoc, pValue->ToWideString());
+    hDoc->GetDocEnvironment()->SetTitle(hDoc, pValue->ToWideString());
     return;
   }
 
   WideString wsTitle;
-  pNotify->GetDocEnvironment()->GetTitle(hDoc, wsTitle);
-  pValue->SetString(wsTitle.UTF8Encode().AsStringView());
+  hDoc->GetDocEnvironment()->GetTitle(hDoc, wsTitle);
+  pValue->SetString(wsTitle.ToUTF8().AsStringView());
 }
 
 void CJX_HostPseudoModel::validationsEnabled(CFXJSE_Value* pValue,
@@ -190,12 +196,11 @@
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
   if (bSetting) {
-    pNotify->GetDocEnvironment()->SetValidationsEnabled(hDoc,
-                                                        pValue->ToBoolean());
+    hDoc->GetDocEnvironment()->SetValidationsEnabled(hDoc, pValue->ToBoolean());
     return;
   }
 
-  bool bEnabled = pNotify->GetDocEnvironment()->IsValidationsEnabled(hDoc);
+  bool bEnabled = hDoc->GetDocEnvironment()->IsValidationsEnabled(hDoc);
   pValue->SetBoolean(bEnabled);
 }
 
@@ -205,12 +210,8 @@
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return;
 
-  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
-  if (!pNotify)
-    return;
-
   if (bSetting) {
-    ThrowException(L"Unable to set variation value.");
+    ThrowException(WideString::FromASCII("Unable to set variation value."));
     return;
   }
   pValue->SetString("Full");
@@ -219,12 +220,8 @@
 void CJX_HostPseudoModel::version(CFXJSE_Value* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
-  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
-  if (!pNotify)
-    return;
-
   if (bSetting) {
-    ThrowException(L"Unable to set version value.");
+    ThrowException(WideString::FromASCII("Unable to set version value."));
     return;
   }
   pValue->SetString("11");
@@ -242,88 +239,78 @@
     return;
   }
   pValue->SetString(
-      pNotify->GetAppProvider()->GetAppName().UTF8Encode().AsStringView());
+      pNotify->GetAppProvider()->GetAppName().ToUTF8().AsStringView());
 }
 
-CJS_Return CJX_HostPseudoModel::gotoURL(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::gotoURL(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
   WideString URL = runtime->ToWideString(params[0]);
-  pNotify->GetDocEnvironment()->GotoURL(hDoc, URL);
-  return CJS_Return(true);
+  hDoc->GetDocEnvironment()->GotoURL(hDoc, URL);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::openList(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::openList(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_Node* pNode = nullptr;
   if (params[0]->IsObject()) {
-    pNode = ToNode(runtime->ToXFAObject(params[0]));
+    pNode =
+        ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   } else if (params[0]->IsString()) {
     CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-    if (!pScriptContext)
-      return CJS_Return(true);
-
     CXFA_Object* pObject = pScriptContext->GetThisObject();
     if (!pObject)
-      return CJS_Return(true);
+      return CJS_Result::Success();
 
     uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
                       XFA_RESOLVENODE_Siblings;
     XFA_RESOLVENODE_RS resolveNodeRS;
-    bool iRet = pScriptContext->ResolveObjects(
+    bool bRet = pScriptContext->ResolveObjects(
         pObject, runtime->ToWideString(params[0]).AsStringView(),
         &resolveNodeRS, dwFlag, nullptr);
-    if (!iRet || !resolveNodeRS.objects.front()->IsNode())
-      return CJS_Return(true);
+    if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+      return CJS_Result::Success();
 
     pNode = resolveNodeRS.objects.front()->AsNode();
   }
 
-  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
-  if (!pDocLayout)
-    return CJS_Return(true);
+  if (pNode)
+    pNotify->OpenDropDownList(pNode);
 
-  CXFA_FFWidget* hWidget =
-      pNotify->GetHWidget(pDocLayout->GetLayoutItem(pNode));
-  if (!hWidget)
-    return CJS_Return(true);
-
-  pNotify->GetDocEnvironment()->SetFocusWidget(pNotify->GetHDOC(), hWidget);
-  pNotify->OpenDropDownList(hWidget);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::response(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::response(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 4)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   WideString question;
   if (params.size() >= 1)
@@ -343,32 +330,33 @@
 
   WideString answer =
       pNotify->GetAppProvider()->Response(question, title, defaultAnswer, mark);
-  return CJS_Return(runtime->NewString(answer.UTF8Encode().AsStringView()));
+  return CJS_Result::Success(
+      runtime->NewString(answer.ToUTF8().AsStringView()));
 }
 
-CJS_Return CJX_HostPseudoModel::documentInBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::documentInBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(runtime->NewNumber(0));
+  return CJS_Result::Success(runtime->NewNumber(0));
 }
 
-CJS_Return CJX_HostPseudoModel::resetData(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::resetData(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() > 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   WideString expression;
   if (params.size() >= 1)
     expression = runtime->ToWideString(params[0]);
 
   if (expression.IsEmpty()) {
-    pNotify->ResetData();
-    return CJS_Return(true);
+    pNotify->ResetData(nullptr);
+    return CJS_Result::Success();
   }
 
   int32_t iStart = 0;
@@ -378,124 +366,118 @@
   while (iStart < iExpLength) {
     iStart = FilterName(expression.AsStringView(), iStart, wsName);
     CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-    if (!pScriptContext)
-      return CJS_Return(true);
-
     CXFA_Object* pObject = pScriptContext->GetThisObject();
     if (!pObject)
-      return CJS_Return(true);
+      return CJS_Result::Success();
 
     uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
                       XFA_RESOLVENODE_Siblings;
     XFA_RESOLVENODE_RS resolveNodeRS;
-    bool iRet = pScriptContext->ResolveObjects(pObject, wsName.AsStringView(),
+    bool bRet = pScriptContext->ResolveObjects(pObject, wsName.AsStringView(),
                                                &resolveNodeRS, dwFlag, nullptr);
-    if (!iRet || !resolveNodeRS.objects.front()->IsNode())
+    if (!bRet || !resolveNodeRS.objects.front()->IsNode())
       continue;
 
     pNode = resolveNodeRS.objects.front()->AsNode();
-    pNotify->ResetData(pNode->GetWidgetAcc());
+    pNotify->ResetData(pNode->IsWidgetReady() ? pNode : nullptr);
   }
   if (!pNode)
-    pNotify->ResetData();
+    pNotify->ResetData(nullptr);
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::beep(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::beep(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (params.size() > 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   uint32_t dwType = 4;
   if (params.size() >= 1)
     dwType = runtime->ToInt32(params[0]);
 
   pNotify->GetAppProvider()->Beep(dwType);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::setFocus(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::setFocus(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_Node* pNode = nullptr;
   if (params.size() >= 1) {
     if (params[0]->IsObject()) {
-      pNode = ToNode(runtime->ToXFAObject(params[0]));
+      pNode =
+          ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
     } else if (params[0]->IsString()) {
       CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-      if (!pScriptContext)
-        return CJS_Return(true);
-
       CXFA_Object* pObject = pScriptContext->GetThisObject();
       if (!pObject)
-        return CJS_Return(true);
+        return CJS_Result::Success();
 
       uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
                         XFA_RESOLVENODE_Siblings;
       XFA_RESOLVENODE_RS resolveNodeRS;
-      bool iRet = pScriptContext->ResolveObjects(
+      bool bRet = pScriptContext->ResolveObjects(
           pObject, runtime->ToWideString(params[0]).AsStringView(),
           &resolveNodeRS, dwFlag, nullptr);
-      if (!iRet || !resolveNodeRS.objects.front()->IsNode())
-        return CJS_Return(true);
+      if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+        return CJS_Result::Success();
 
       pNode = resolveNodeRS.objects.front()->AsNode();
     }
   }
   pNotify->SetFocusWidgetNode(pNode);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::getFocus(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::getFocus(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_Node* pNode = pNotify->GetFocusWidgetNode();
   if (!pNode)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
 
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
-CJS_Return CJX_HostPseudoModel::messageBox(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::messageBox(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (params.empty() || params.size() > 4)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   WideString message;
   if (params.size() >= 1)
@@ -505,43 +487,43 @@
   if (params.size() >= 2)
     title = runtime->ToWideString(params[1]);
 
-  uint32_t messageType = XFA_MBICON_Error;
+  uint32_t messageType = static_cast<uint32_t>(AlertIcon::kDefault);
   if (params.size() >= 3) {
     messageType = runtime->ToInt32(params[2]);
-    if (messageType > XFA_MBICON_Status)
-      messageType = XFA_MBICON_Error;
+    if (messageType > static_cast<uint32_t>(AlertIcon::kStatus))
+      messageType = static_cast<uint32_t>(AlertIcon::kDefault);
   }
 
-  uint32_t buttonType = XFA_MB_OK;
+  uint32_t buttonType = static_cast<uint32_t>(AlertButton::kDefault);
   if (params.size() >= 4) {
     buttonType = runtime->ToInt32(params[3]);
-    if (buttonType > XFA_MB_YesNoCancel)
-      buttonType = XFA_MB_OK;
+    if (buttonType > static_cast<uint32_t>(AlertButton::kYesNoCancel))
+      buttonType = static_cast<uint32_t>(AlertButton::kDefault);
   }
 
   int32_t iValue = pNotify->GetAppProvider()->MsgBox(message, title,
                                                      messageType, buttonType);
-  return CJS_Return(runtime->NewNumber(iValue));
+  return CJS_Result::Success(runtime->NewNumber(iValue));
 }
 
-CJS_Return CJX_HostPseudoModel::documentCountInBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::documentCountInBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(runtime->NewNumber(0));
+  return CJS_Result::Success(runtime->NewNumber(0));
 }
 
-CJS_Return CJX_HostPseudoModel::print(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::print(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (params.size() != 8)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   uint32_t dwOptions = 0;
   if (runtime->ToBoolean(params[0]))
@@ -560,29 +542,29 @@
   int32_t nStartPage = runtime->ToInt32(params[1]);
   int32_t nEndPage = runtime->ToInt32(params[2]);
 
-  pNotify->GetDocEnvironment()->Print(pNotify->GetHDOC(), nStartPage, nEndPage,
-                                      dwOptions);
-  return CJS_Return(true);
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  hDoc->GetDocEnvironment()->Print(hDoc, nStartPage, nEndPage, dwOptions);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::importData(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::importData(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::exportData(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::exportData(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   WideString filePath;
   if (params.size() >= 1)
@@ -592,40 +574,41 @@
   if (params.size() >= 2)
     XDP = runtime->ToBoolean(params[1]);
 
-  pNotify->GetDocEnvironment()->ExportData(pNotify->GetHDOC(), filePath, XDP);
-  return CJS_Return(true);
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  hDoc->GetDocEnvironment()->ExportData(hDoc, filePath, XDP);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::pageUp(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::pageUp(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  int32_t nCurPage = pNotify->GetDocEnvironment()->GetCurrentPage(hDoc);
+  int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc);
   int32_t nNewPage = 0;
   if (nCurPage <= 1)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   nNewPage = nCurPage - 1;
-  pNotify->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
-  return CJS_Return(true);
+  hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_HostPseudoModel::pageDown(
-    CJS_V8* runtime,
+CJS_Result CJX_HostPseudoModel::pageDown(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  int32_t nCurPage = pNotify->GetDocEnvironment()->GetCurrentPage(hDoc);
-  int32_t nPageCount = pNotify->GetDocEnvironment()->CountPages(hDoc);
+  int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc);
+  int32_t nPageCount = hDoc->GetDocEnvironment()->CountPages(hDoc);
   if (!nPageCount || nCurPage == nPageCount)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   int32_t nNewPage = 0;
   if (nCurPage >= nPageCount)
@@ -633,6 +616,6 @@
   else
     nNewPage = nCurPage + 1;
 
-  pNotify->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
-  return CJS_Return(true);
+  hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  return CJS_Result::Success();
 }
diff --git a/fxjs/xfa/cjx_hostpseudomodel.h b/fxjs/xfa/cjx_hostpseudomodel.h
index b4d6fe6..cdc293d 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.h
+++ b/fxjs/xfa/cjx_hostpseudomodel.h
@@ -7,50 +7,57 @@
 #ifndef FXJS_XFA_CJX_HOSTPSEUDOMODEL_H_
 #define FXJS_XFA_CJX_HOSTPSEUDOMODEL_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CFXJSE_Value;
 class CScript_HostPseudoModel;
 
-class CJX_HostPseudoModel : public CJX_Object {
+class CJX_HostPseudoModel final : public CJX_Object {
  public:
   explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model);
   ~CJX_HostPseudoModel() override;
 
-  JS_METHOD(beep, CJX_HostPseudoModel);
-  JS_METHOD(documentCountInBatch, CJX_HostPseudoModel);
-  JS_METHOD(documentInBatch, CJX_HostPseudoModel);
-  JS_METHOD(exportData, CJX_HostPseudoModel);
-  JS_METHOD(getFocus, CJX_HostPseudoModel);
-  JS_METHOD(gotoURL, CJX_HostPseudoModel);
-  JS_METHOD(importData, CJX_HostPseudoModel);
-  JS_METHOD(messageBox, CJX_HostPseudoModel);
-  JS_METHOD(openList, CJX_HostPseudoModel);
-  JS_METHOD(pageDown, CJX_HostPseudoModel);
-  JS_METHOD(pageUp, CJX_HostPseudoModel);
-  JS_METHOD(print, CJX_HostPseudoModel);
-  JS_METHOD(resetData, CJX_HostPseudoModel);
-  JS_METHOD(response, CJX_HostPseudoModel);
-  JS_METHOD(setFocus, CJX_HostPseudoModel);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(appType);
-  JS_PROP(calculationsEnabled);
-  JS_PROP(currentPage);
-  JS_PROP(language);
-  JS_PROP(numPages);
-  JS_PROP(platform);
-  JS_PROP(title);
-  JS_PROP(validationsEnabled);
-  JS_PROP(variation);
-  JS_PROP(version);
+  JSE_METHOD(beep);
+  JSE_METHOD(documentCountInBatch);
+  JSE_METHOD(documentInBatch);
+  JSE_METHOD(exportData);
+  JSE_METHOD(getFocus);
+  JSE_METHOD(gotoURL);
+  JSE_METHOD(importData);
+  JSE_METHOD(messageBox);
+  JSE_METHOD(openList);
+  JSE_METHOD(pageDown);
+  JSE_METHOD(pageUp);
+  JSE_METHOD(print);
+  JSE_METHOD(resetData);
+  JSE_METHOD(response);
+  JSE_METHOD(setFocus);
+
+  JSE_PROP(appType);
+  JSE_PROP(calculationsEnabled);
+  JSE_PROP(currentPage);
+  JSE_PROP(language);
+  JSE_PROP(numPages);
+  JSE_PROP(platform);
+  JSE_PROP(title);
+  JSE_PROP(validationsEnabled);
+  JSE_PROP(variation);
+  JSE_PROP(version);
 
   // TODO(dsinclair): Remove when xfa_basic_data_element_script is removed.
   // Doesn't exist in spec
-  JS_PROP(name);
+  JSE_PROP(name);
 
  private:
+  using Type__ = CJX_HostPseudoModel;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::HostPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
new file mode 100644
index 0000000..1d42898
--- /dev/null
+++ b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
@@ -0,0 +1,15 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+
+class CJX_HostPseudoModelEmbedderTest : public XFAJSEmbedderTest {};
+
+// Should not crash.
+TEST_F(CJX_HostPseudoModelEmbedderTest, BUG_1256) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  EXPECT_TRUE(Execute("$host.openList(1)"));
+}
diff --git a/fxjs/xfa/cjx_image.cpp b/fxjs/xfa/cjx_image.cpp
deleted file mode 100644
index 0ea2131..0000000
--- a/fxjs/xfa/cjx_image.cpp
+++ /dev/null
@@ -1,61 +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 "fxjs/xfa/cjx_image.h"
-
-#include "xfa/fxfa/parser/cxfa_image.h"
-
-CJX_Image::CJX_Image(CXFA_Image* node) : CJX_Node(node) {}
-
-CJX_Image::~CJX_Image() = default;
-
-void CJX_Image::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Image::contentType(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Image::transferEncoding(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Image::defaultValue(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue_Read(pValue, bSetting, eAttribute);
-}
-
-void CJX_Image::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Image::aspect(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Image::value(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue_Read(pValue, bSetting, eAttribute);
-}
-
-void CJX_Image::href(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_image.h b/fxjs/xfa/cjx_image.h
deleted file mode 100644
index 5cd120a..0000000
--- a/fxjs/xfa/cjx_image.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_IMAGE_H_
-#define FXJS_XFA_CJX_IMAGE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Image;
-
-class CJX_Image : public CJX_Node {
- public:
-  explicit CJX_Image(CXFA_Image* node);
-  ~CJX_Image() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(aspect);
-  JS_PROP(contentType);
-  JS_PROP(href);
-  JS_PROP(transferEncoding);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_IMAGE_H_
diff --git a/fxjs/xfa/cjx_imageedit.cpp b/fxjs/xfa/cjx_imageedit.cpp
deleted file mode 100644
index a325eca..0000000
--- a/fxjs/xfa/cjx_imageedit.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_imageedit.h"
-
-#include "xfa/fxfa/parser/cxfa_imageedit.h"
-
-CJX_ImageEdit::CJX_ImageEdit(CXFA_ImageEdit* node) : CJX_Node(node) {}
-
-CJX_ImageEdit::~CJX_ImageEdit() = default;
-
-void CJX_ImageEdit::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ImageEdit::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ImageEdit::data(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_imageedit.h b/fxjs/xfa/cjx_imageedit.h
deleted file mode 100644
index ebf4fa3..0000000
--- a/fxjs/xfa/cjx_imageedit.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_IMAGEEDIT_H_
-#define FXJS_XFA_CJX_IMAGEEDIT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_ImageEdit;
-
-class CJX_ImageEdit : public CJX_Node {
- public:
-  explicit CJX_ImageEdit(CXFA_ImageEdit* node);
-  ~CJX_ImageEdit() override;
-
-  JS_PROP(data);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_IMAGEEDIT_H_
diff --git a/fxjs/xfa/cjx_insert.cpp b/fxjs/xfa/cjx_insert.cpp
deleted file mode 100644
index cf2ae32..0000000
--- a/fxjs/xfa/cjx_insert.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_insert.h"
-
-#include "xfa/fxfa/parser/cxfa_insert.h"
-
-CJX_Insert::CJX_Insert(CXFA_Insert* node) : CJX_TextNode(node) {}
-
-CJX_Insert::~CJX_Insert() = default;
-
-void CJX_Insert::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Insert::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_insert.h b/fxjs/xfa/cjx_insert.h
deleted file mode 100644
index 3b73df8..0000000
--- a/fxjs/xfa/cjx_insert.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_INSERT_H_
-#define FXJS_XFA_CJX_INSERT_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Insert;
-
-class CJX_Insert : public CJX_TextNode {
- public:
-  explicit CJX_Insert(CXFA_Insert* node);
-  ~CJX_Insert() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_INSERT_H_
diff --git a/fxjs/xfa/cjx_instancemanager.cpp b/fxjs/xfa/cjx_instancemanager.cpp
index 830541e..160c4a5 100644
--- a/fxjs/xfa/cjx_instancemanager.cpp
+++ b/fxjs/xfa/cjx_instancemanager.cpp
@@ -9,13 +9,13 @@
 #include <algorithm>
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_instancemanager.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
 
 const CJX_MethodSpec CJX_InstanceManager::MethodSpecs[] = {
@@ -27,11 +27,15 @@
 
 CJX_InstanceManager::CJX_InstanceManager(CXFA_InstanceManager* mgr)
     : CJX_Node(mgr) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_InstanceManager::~CJX_InstanceManager() {}
 
+bool CJX_InstanceManager::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) {
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
@@ -55,7 +59,7 @@
     WideString wsInstanceName = WideString(
         wsInstManagerName.IsEmpty()
             ? wsInstManagerName
-            : wsInstManagerName.Right(wsInstManagerName.GetLength() - 1));
+            : wsInstManagerName.Last(wsInstManagerName.GetLength() - 1));
     uint32_t dInstanceNameHash =
         FX_HashCode_GetW(wsInstanceName.AsStringView(), false);
     CXFA_Node* pPrevSibling = iDesired == 0
@@ -98,12 +102,8 @@
       pNotify->RunNodeInitialize(pNewInstance);
     }
   }
-
-  CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
-  if (pLayoutPro) {
-    pLayoutPro->AddChangedContainer(
-        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
-  }
+  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
+      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
   return 0;
 }
 
@@ -124,19 +124,20 @@
 
   GetXFANode()->RemoveItem(pMoveInstance, false);
   GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true);
-  CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
-  if (pLayoutPro) {
-    pLayoutPro->AddChangedContainer(
-        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
-  }
+  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
+      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
   return 0;
 }
 
-CJS_Return CJX_InstanceManager::moveInstance(
-    CJS_V8* runtime,
+CJS_Result CJX_InstanceManager::moveInstance(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  if (doc->GetFormType() != FormType::kXFAFull)
+    return CJS_Result::Failure(JSMessage::kNotSupportedError);
+
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int32_t iFrom = runtime->ToInt32(params[0]);
   int32_t iTo = runtime->ToInt32(params[1]);
@@ -144,7 +145,7 @@
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CXFA_Node* pToInstance = GetXFANode()->GetItemIfExists(iTo);
   if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform)
@@ -156,28 +157,32 @@
     pNotify->RunSubformIndexChange(pFromInstance);
   }
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_InstanceManager::removeInstance(
-    CJS_V8* runtime,
+CJS_Result CJX_InstanceManager::removeInstance(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  if (doc->GetFormType() != FormType::kXFAFull)
+    return CJS_Result::Failure(JSMessage::kNotSupportedError);
+
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int32_t iIndex = runtime->ToInt32(params[0]);
   int32_t iCount = GetXFANode()->GetCount();
   if (iIndex < 0 || iIndex >= iCount)
-    return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError));
+    return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
   if (iCount - 1 < iMin)
-    return CJS_Return(JSGetStringFromID(JSMessage::kTooManyOccurances));
+    return CJS_Result::Failure(JSMessage::kTooManyOccurances);
 
   CXFA_Node* pRemoveInstance = GetXFANode()->GetItemIfExists(iIndex);
   if (!pRemoveInstance)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   GetXFANode()->RemoveItem(pRemoveInstance, true);
 
@@ -191,29 +196,34 @@
       }
     }
   }
-  CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
-  if (pLayoutPro) {
-    pLayoutPro->AddChangedContainer(
-        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
-  }
-  return CJS_Return(true);
+  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
+      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_InstanceManager::setInstances(
-    CJS_V8* runtime,
+CJS_Result CJX_InstanceManager::setInstances(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  if (doc->GetFormType() != FormType::kXFAFull)
+    return CJS_Result::Failure(JSMessage::kNotSupportedError);
+
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   SetInstances(runtime->ToInt32(params[0]));
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_InstanceManager::addInstance(
-    CJS_V8* runtime,
+CJS_Result CJX_InstanceManager::addInstance(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  if (doc->GetFormType() != FormType::kXFAFull)
+    return CJS_Result::Failure(JSMessage::kNotSupportedError);
+
   if (!params.empty() && params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   bool fFlags = true;
   if (params.size() == 1)
@@ -223,38 +233,38 @@
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
   if (iMax >= 0 && iCount >= iMax)
-    return CJS_Return(JSGetStringFromID(JSMessage::kTooManyOccurances));
+    return CJS_Result::Failure(JSMessage::kTooManyOccurances);
 
   CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(fFlags);
   if (!pNewInstance)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
   GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->RunNodeInitialize(pNewInstance);
-
-    CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
-    if (pLayoutPro) {
-      pLayoutPro->AddChangedContainer(
-          ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
-    }
+    GetDocument()->GetLayoutProcessor()->AddChangedContainer(
+        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
   }
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewInstance);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pNewInstance);
 
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
-CJS_Return CJX_InstanceManager::insertInstance(
-    CJS_V8* runtime,
+CJS_Result CJX_InstanceManager::insertInstance(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  if (doc->GetFormType() != FormType::kXFAFull)
+    return CJS_Result::Failure(JSMessage::kNotSupportedError);
+
   if (params.size() != 1 && params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int32_t iIndex = runtime->ToInt32(params[0]);
   bool bBind = false;
@@ -263,35 +273,32 @@
 
   int32_t iCount = GetXFANode()->GetCount();
   if (iIndex < 0 || iIndex > iCount)
-    return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError));
+    return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
   if (iMax >= 0 && iCount >= iMax)
-    return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError));
+    return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(bBind);
   if (!pNewInstance)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
   GetXFANode()->InsertItem(pNewInstance, iIndex, iCount, true);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->RunNodeInitialize(pNewInstance);
-    CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
-    if (pLayoutPro) {
-      pLayoutPro->AddChangedContainer(
-          ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
-    }
+    GetDocument()->GetLayoutProcessor()->AddChangedContainer(
+        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
   }
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewInstance);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pNewInstance);
 
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
 void CJX_InstanceManager::max(CFXJSE_Value* pValue,
@@ -320,8 +327,8 @@
                                 bool bSetting,
                                 XFA_Attribute eAttribute) {
   if (bSetting) {
-    pValue->SetInteger(GetXFANode()->GetCount());
+    SetInstances(pValue->ToInteger());
     return;
   }
-  SetInstances(pValue->ToInteger());
+  pValue->SetInteger(GetXFANode()->GetCount());
 }
diff --git a/fxjs/xfa/cjx_instancemanager.h b/fxjs/xfa/cjx_instancemanager.h
index 74efad5..3fae9ab 100644
--- a/fxjs/xfa/cjx_instancemanager.h
+++ b/fxjs/xfa/cjx_instancemanager.h
@@ -7,32 +7,39 @@
 #ifndef FXJS_XFA_CJX_INSTANCEMANAGER_H_
 #define FXJS_XFA_CJX_INSTANCEMANAGER_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_InstanceManager;
 
-class CJX_InstanceManager : public CJX_Node {
+class CJX_InstanceManager final : public CJX_Node {
  public:
   explicit CJX_InstanceManager(CXFA_InstanceManager* mgr);
   ~CJX_InstanceManager() override;
 
-  JS_METHOD(addInstance, CJX_InstanceManager);
-  JS_METHOD(insertInstance, CJX_InstanceManager);
-  JS_METHOD(moveInstance, CJX_InstanceManager);
-  JS_METHOD(removeInstance, CJX_InstanceManager);
-  JS_METHOD(setInstances, CJX_InstanceManager);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(count);
-  JS_PROP(max);
-  JS_PROP(min);
+  JSE_METHOD(addInstance);
+  JSE_METHOD(insertInstance);
+  JSE_METHOD(moveInstance);
+  JSE_METHOD(removeInstance);
+  JSE_METHOD(setInstances);
+
+  JSE_PROP(count);
+  JSE_PROP(max);
+  JSE_PROP(min);
 
   int32_t MoveInstance(int32_t iTo, int32_t iFrom);
 
  private:
-  int32_t SetInstances(int32_t iDesired);
+  using Type__ = CJX_InstanceManager;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::InstanceManager;
   static const CJX_MethodSpec MethodSpecs[];
+
+  int32_t SetInstances(int32_t iDesired);
 };
 
 #endif  // FXJS_XFA_CJX_INSTANCEMANAGER_H_
diff --git a/fxjs/xfa/cjx_integer.cpp b/fxjs/xfa/cjx_integer.cpp
deleted file mode 100644
index f1840dd..0000000
--- a/fxjs/xfa/cjx_integer.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_integer.h"
-
-#include "xfa/fxfa/parser/cxfa_integer.h"
-
-CJX_Integer::CJX_Integer(CXFA_Integer* node) : CJX_Content(node) {}
-
-CJX_Integer::~CJX_Integer() = default;
-
-void CJX_Integer::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Integer::defaultValue(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Integer::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Integer::value(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_integer.h b/fxjs/xfa/cjx_integer.h
deleted file mode 100644
index 90daabf..0000000
--- a/fxjs/xfa/cjx_integer.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_INTEGER_H_
-#define FXJS_XFA_CJX_INTEGER_H_
-
-#include "fxjs/xfa/cjx_content.h"
-
-class CXFA_Integer;
-
-class CJX_Integer : public CJX_Content {
- public:
-  explicit CJX_Integer(CXFA_Integer* node);
-  ~CJX_Integer() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_INTEGER_H_
diff --git a/fxjs/xfa/cjx_issuers.cpp b/fxjs/xfa/cjx_issuers.cpp
deleted file mode 100644
index 5cd381d..0000000
--- a/fxjs/xfa/cjx_issuers.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_issuers.h"
-
-#include "xfa/fxfa/parser/cxfa_issuers.h"
-
-CJX_Issuers::CJX_Issuers(CXFA_Issuers* node) : CJX_Node(node) {}
-
-CJX_Issuers::~CJX_Issuers() = default;
-
-void CJX_Issuers::type(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Issuers::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Issuers::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_issuers.h b/fxjs/xfa/cjx_issuers.h
deleted file mode 100644
index 5d76e72..0000000
--- a/fxjs/xfa/cjx_issuers.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_ISSUERS_H_
-#define FXJS_XFA_CJX_ISSUERS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Issuers;
-
-class CJX_Issuers : public CJX_Node {
- public:
-  explicit CJX_Issuers(CXFA_Issuers* node);
-  ~CJX_Issuers() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_ISSUERS_H_
diff --git a/fxjs/xfa/cjx_items.cpp b/fxjs/xfa/cjx_items.cpp
deleted file mode 100644
index e4afdd0..0000000
--- a/fxjs/xfa/cjx_items.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_items.h"
-
-#include "xfa/fxfa/parser/cxfa_items.h"
-
-CJX_Items::CJX_Items(CXFA_Items* node) : CJX_Node(node) {}
-
-CJX_Items::~CJX_Items() = default;
-
-void CJX_Items::ref(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Items::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Items::presence(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Items::save(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Items::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_items.h b/fxjs/xfa/cjx_items.h
deleted file mode 100644
index 94f2b43..0000000
--- a/fxjs/xfa/cjx_items.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_ITEMS_H_
-#define FXJS_XFA_CJX_ITEMS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Items;
-
-class CJX_Items : public CJX_Node {
- public:
-  explicit CJX_Items(CXFA_Items* node);
-  ~CJX_Items() override;
-
-  JS_PROP(presence);
-  JS_PROP(ref);
-  JS_PROP(save);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_ITEMS_H_
diff --git a/fxjs/xfa/cjx_keep.cpp b/fxjs/xfa/cjx_keep.cpp
deleted file mode 100644
index 25238e3..0000000
--- a/fxjs/xfa/cjx_keep.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_keep.h"
-
-#include "xfa/fxfa/parser/cxfa_keep.h"
-
-CJX_Keep::CJX_Keep(CXFA_Keep* node) : CJX_Node(node) {}
-
-CJX_Keep::~CJX_Keep() = default;
-
-void CJX_Keep::next(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Keep::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Keep::previous(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Keep::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Keep::intact(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_keep.h b/fxjs/xfa/cjx_keep.h
deleted file mode 100644
index fa8a111..0000000
--- a/fxjs/xfa/cjx_keep.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_KEEP_H_
-#define FXJS_XFA_CJX_KEEP_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Keep;
-
-class CJX_Keep : public CJX_Node {
- public:
-  explicit CJX_Keep(CXFA_Keep* node);
-  ~CJX_Keep() override;
-
-  JS_PROP(intact);
-  JS_PROP(next);
-  JS_PROP(previous);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_KEEP_H_
diff --git a/fxjs/xfa/cjx_keyusage.cpp b/fxjs/xfa/cjx_keyusage.cpp
deleted file mode 100644
index 99947e9..0000000
--- a/fxjs/xfa/cjx_keyusage.cpp
+++ /dev/null
@@ -1,85 +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 "fxjs/xfa/cjx_keyusage.h"
-
-#include "xfa/fxfa/parser/cxfa_keyusage.h"
-
-CJX_KeyUsage::CJX_KeyUsage(CXFA_KeyUsage* node) : CJX_Node(node) {}
-
-CJX_KeyUsage::~CJX_KeyUsage() = default;
-
-void CJX_KeyUsage::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::nonRepudiation(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::encipherOnly(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::type(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::digitalSignature(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::crlSign(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::keyAgreement(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::keyEncipherment(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::dataEncipherment(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::keyCertSign(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_KeyUsage::decipherOnly(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_keyusage.h b/fxjs/xfa/cjx_keyusage.h
deleted file mode 100644
index 3ff546d..0000000
--- a/fxjs/xfa/cjx_keyusage.h
+++ /dev/null
@@ -1,33 +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 FXJS_XFA_CJX_KEYUSAGE_H_
-#define FXJS_XFA_CJX_KEYUSAGE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_KeyUsage;
-
-class CJX_KeyUsage : public CJX_Node {
- public:
-  explicit CJX_KeyUsage(CXFA_KeyUsage* node);
-  ~CJX_KeyUsage() override;
-
-  JS_PROP(crlSign);
-  JS_PROP(dataEncipherment);
-  JS_PROP(decipherOnly);
-  JS_PROP(digitalSignature);
-  JS_PROP(encipherOnly);
-  JS_PROP(keyAgreement);
-  JS_PROP(keyCertSign);
-  JS_PROP(keyEncipherment);
-  JS_PROP(nonRepudiation);
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_KEYUSAGE_H_
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.cpp b/fxjs/xfa/cjx_layoutpseudomodel.cpp
index 384bd09..35852a8 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.cpp
+++ b/fxjs/xfa/cjx_layoutpseudomodel.cpp
@@ -7,23 +7,28 @@
 #include "fxjs/xfa/cjx_layoutpseudomodel.h"
 
 #include <set>
+#include <utility>
 
 #include "core/fxcrt/fx_coordinates.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_class.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_layoutitem.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
-#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_form.h"
-#include "xfa/fxfa/parser/cxfa_layoutitem.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h"
 
 const CJX_MethodSpec CJX_LayoutPseudoModel::MethodSpecs[] = {
     {"absPage", absPage_static},
@@ -48,11 +53,15 @@
 
 CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model)
     : CJX_Object(model) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {}
 
+bool CJX_LayoutPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_LayoutPseudoModel::ready(CFXJSE_Value* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
@@ -60,7 +69,7 @@
   if (!pNotify)
     return;
   if (bSetting) {
-    ThrowException(L"Unable to set ready value.");
+    ThrowException(WideString::FromASCII("Unable to set ready value."));
     return;
   }
 
@@ -68,32 +77,30 @@
   pValue->SetBoolean(iStatus >= 2);
 }
 
-CJS_Return CJX_LayoutPseudoModel::HWXY(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::HWXY(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params,
     XFA_LAYOUTMODEL_HWXY layoutModel) {
   if (params.empty() || params.size() > 3)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  CXFA_Node* pNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   if (!pNode)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
-  WideString unit(L"pt");
+  WideString unit = WideString::FromASCII("pt");
   if (params.size() >= 2) {
     WideString tmp_unit = runtime->ToWideString(params[1]);
     if (!tmp_unit.IsEmpty())
-      unit = tmp_unit;
+      unit = std::move(tmp_unit);
   }
   int32_t iIndex = params.size() >= 3 ? runtime->ToInt32(params[2]) : 0;
-
-  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
-  if (!pDocLayout)
-    return CJS_Return(true);
-
-  CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
+  CXFA_ContentLayoutItem* pLayoutItem =
+      ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
   if (!pLayoutItem)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   while (iIndex > 0 && pLayoutItem) {
     pLayoutItem = pLayoutItem->GetNext();
@@ -101,7 +108,7 @@
   }
 
   if (!pLayoutItem)
-    return CJS_Return(runtime->NewNumber(0.0));
+    return CJS_Result::Success(runtime->NewNumber(0.0));
 
   CXFA_Measurement measure;
   CFX_RectF rtRect = pLayoutItem->GetRect(true);
@@ -120,46 +127,47 @@
       break;
   }
 
-  float fValue =
-      measure.ToUnit(CXFA_Measurement::GetUnitFromString(unit.AsStringView()));
-  return CJS_Return(runtime->NewNumber(FXSYS_round(fValue * 1000) / 1000.0f));
+  XFA_Unit eUnit = CXFA_Measurement::GetUnitFromString(unit.AsStringView());
+  if (eUnit == XFA_Unit::Unknown)
+    return CJS_Result::Failure(JSMessage::kValueError);
+
+  float fValue = measure.ToUnit(eUnit);
+  return CJS_Result::Success(
+      runtime->NewNumber(FXSYS_roundf(fValue * 1000) / 1000.0f));
 }
 
-CJS_Return CJX_LayoutPseudoModel::h(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::h(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return HWXY(runtime, params, XFA_LAYOUTMODEL_H);
 }
 
-CJS_Return CJX_LayoutPseudoModel::w(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::w(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return HWXY(runtime, params, XFA_LAYOUTMODEL_W);
 }
 
-CJS_Return CJX_LayoutPseudoModel::x(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::x(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return HWXY(runtime, params, XFA_LAYOUTMODEL_X);
 }
 
-CJS_Return CJX_LayoutPseudoModel::y(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::y(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return HWXY(runtime, params, XFA_LAYOUTMODEL_Y);
 }
 
-CJS_Return CJX_LayoutPseudoModel::NumberedPageCount(CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFX_V8* runtime,
                                                     bool bNumbered) {
-  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
-  if (!pDocLayout)
-    return CJS_Return(true);
-
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
   int32_t iPageCount = 0;
   int32_t iPageNum = pDocLayout->CountPages();
   if (bNumbered) {
     for (int32_t i = 0; i < iPageNum; i++) {
-      CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
+      CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
       if (!pLayoutPage)
         continue;
 
@@ -170,41 +178,40 @@
   } else {
     iPageCount = iPageNum;
   }
-  return CJS_Return(runtime->NewNumber(iPageCount));
+  return CJS_Result::Success(runtime->NewNumber(iPageCount));
 }
 
-CJS_Return CJX_LayoutPseudoModel::pageCount(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::pageCount(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return NumberedPageCount(runtime, true);
 }
 
-CJS_Return CJX_LayoutPseudoModel::pageSpan(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::pageSpan(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  CXFA_Node* pNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   if (!pNode)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
-  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
-  if (!pDocLayout)
-    return CJS_Return(true);
-
-  CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
+  CXFA_ContentLayoutItem* pLayoutItem =
+      ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
   if (!pLayoutItem)
-    return CJS_Return(runtime->NewNumber(-1));
+    return CJS_Result::Success(runtime->NewNumber(-1));
 
   int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex();
   int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
   int32_t iPageSpan = iLast - iFirst + 1;
-  return CJS_Return(runtime->NewNumber(iPageSpan));
+  return CJS_Result::Success(runtime->NewNumber(iPageSpan));
 }
 
-CJS_Return CJX_LayoutPseudoModel::page(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::page(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, false);
 }
@@ -214,73 +221,72 @@
     int32_t iPageNo,
     const WideString& wsType,
     bool bOnPageArea) {
-  CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
+  CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
   if (!pLayoutPage)
     return std::vector<CXFA_Node*>();
 
   std::vector<CXFA_Node*> retArray;
-  if (wsType == L"pageArea") {
-    if (pLayoutPage->m_pFormNode)
-      retArray.push_back(pLayoutPage->m_pFormNode);
+  if (wsType.EqualsASCII("pageArea")) {
+    if (pLayoutPage->GetFormNode())
+      retArray.push_back(pLayoutPage->GetFormNode());
     return retArray;
   }
-  if (wsType == L"contentArea") {
-    for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
-         pItem = pItem->m_pNextSibling) {
-      if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea)
-        retArray.push_back(pItem->m_pFormNode);
+  if (wsType.EqualsASCII("contentArea")) {
+    for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
+         pItem = pItem->GetNextSibling()) {
+      if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea)
+        retArray.push_back(pItem->GetFormNode());
     }
     return retArray;
   }
   std::set<CXFA_Node*> formItems;
   if (wsType.IsEmpty()) {
-    if (pLayoutPage->m_pFormNode)
-      retArray.push_back(pLayoutPage->m_pFormNode);
+    if (pLayoutPage->GetFormNode())
+      retArray.push_back(pLayoutPage->GetFormNode());
 
-    for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
-         pItem = pItem->m_pNextSibling) {
-      if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
-        retArray.push_back(pItem->m_pFormNode);
+    for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
+         pItem = pItem->GetNextSibling()) {
+      if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea) {
+        retArray.push_back(pItem->GetFormNode());
         if (!bOnPageArea) {
-          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
-                                    CXFA_TraverseStrategy_ContentLayoutItem>
-          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
-          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
-               pItemChild; pItemChild = iterator.MoveToNext()) {
-            if (!pItemChild->IsContentLayoutItem()) {
+          CXFA_LayoutItemIterator iterator(pItem->GetFirstChild());
+          for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
+               pChild = iterator.MoveToNext()) {
+            CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
+            if (!pItemChild)
               continue;
-            }
-            XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
+
+            XFA_Element eType = pItemChild->GetFormNode()->GetElementType();
             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
               continue;
             }
-            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
               continue;
 
-            formItems.insert(pItemChild->m_pFormNode);
-            retArray.push_back(pItemChild->m_pFormNode);
+            formItems.insert(pItemChild->GetFormNode());
+            retArray.push_back(pItemChild->GetFormNode());
           }
         }
       } else {
         if (bOnPageArea) {
-          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
-                                    CXFA_TraverseStrategy_ContentLayoutItem>
-          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
-          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
-               pItemChild; pItemChild = iterator.MoveToNext()) {
-            if (!pItemChild->IsContentLayoutItem()) {
+          CXFA_LayoutItemIterator iterator(pItem);
+          for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
+               pChild = iterator.MoveToNext()) {
+            CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
+            if (!pItemChild)
               continue;
-            }
-            XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
+
+            XFA_Element eType = pItemChild->GetFormNode()->GetElementType();
             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
               continue;
             }
-            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
               continue;
-            formItems.insert(pItemChild->m_pFormNode);
-            retArray.push_back(pItemChild->m_pFormNode);
+
+            formItems.insert(pItemChild->GetFormNode());
+            retArray.push_back(pItemChild->GetFormNode());
           }
         }
       }
@@ -289,52 +295,50 @@
   }
 
   XFA_Element eType = XFA_Element::Unknown;
-  if (wsType == L"field")
+  if (wsType.EqualsASCII("field"))
     eType = XFA_Element::Field;
-  else if (wsType == L"draw")
+  else if (wsType.EqualsASCII("draw"))
     eType = XFA_Element::Draw;
-  else if (wsType == L"subform")
+  else if (wsType.EqualsASCII("subform"))
     eType = XFA_Element::Subform;
-  else if (wsType == L"area")
+  else if (wsType.EqualsASCII("area"))
     eType = XFA_Element::Area;
 
   if (eType != XFA_Element::Unknown) {
-    for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
-         pItem = pItem->m_pNextSibling) {
-      if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
+    for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
+         pItem = pItem->GetNextSibling()) {
+      if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea) {
         if (!bOnPageArea) {
-          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
-                                    CXFA_TraverseStrategy_ContentLayoutItem>
-          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
-          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
-               pItemChild; pItemChild = iterator.MoveToNext()) {
-            if (!pItemChild->IsContentLayoutItem())
+          CXFA_LayoutItemIterator iterator(pItem->GetFirstChild());
+          for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
+               pChild = iterator.MoveToNext()) {
+            CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
+            if (!pItemChild)
               continue;
-            if (pItemChild->m_pFormNode->GetElementType() != eType)
+            if (pItemChild->GetFormNode()->GetElementType() != eType)
               continue;
-            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
               continue;
 
-            formItems.insert(pItemChild->m_pFormNode);
-            retArray.push_back(pItemChild->m_pFormNode);
+            formItems.insert(pItemChild->GetFormNode());
+            retArray.push_back(pItemChild->GetFormNode());
           }
         }
       } else {
         if (bOnPageArea) {
-          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
-                                    CXFA_TraverseStrategy_ContentLayoutItem>
-          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
-          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
-               pItemChild; pItemChild = iterator.MoveToNext()) {
-            if (!pItemChild->IsContentLayoutItem())
+          CXFA_LayoutItemIterator iterator(pItem);
+          for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
+               pChild = iterator.MoveToNext()) {
+            CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
+            if (!pItemChild)
               continue;
-            if (pItemChild->m_pFormNode->GetElementType() != eType)
+            if (pItemChild->GetFormNode()->GetElementType() != eType)
               continue;
-            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
               continue;
 
-            formItems.insert(pItemChild->m_pFormNode);
-            retArray.push_back(pItemChild->m_pFormNode);
+            formItems.insert(pItemChild->GetFormNode());
+            retArray.push_back(pItemChild->GetFormNode());
           }
         }
       }
@@ -343,11 +347,11 @@
   return retArray;
 }
 
-CJS_Return CJX_LayoutPseudoModel::pageContent(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::pageContent(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int32_t iIndex = 0;
   if (params.size() >= 1)
@@ -363,120 +367,119 @@
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
-  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
-  if (!pDocLayout)
-    return CJS_Return(true);
-
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
   auto pArrayNodeList = pdfium::MakeUnique<CXFA_ArrayNodeList>(GetDocument());
   pArrayNodeList->SetArrayNodeList(
       GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea));
 
   // TODO(dsinclair): Who owns the array once we release it? Won't this leak?
-  return CJS_Return(runtime->NewXFAObject(
+  return CJS_Result::Success(static_cast<CFXJSE_Engine*>(runtime)->NewXFAObject(
       pArrayNodeList.release(),
       GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
 }
 
-CJS_Return CJX_LayoutPseudoModel::absPageCount(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::absPageCount(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return NumberedPageCount(runtime, false);
 }
 
-CJS_Return CJX_LayoutPseudoModel::absPageCountInBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::absPageCountInBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(runtime->NewNumber(0));
+  return CJS_Result::Success(runtime->NewNumber(0));
 }
 
-CJS_Return CJX_LayoutPseudoModel::sheetCountInBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::sheetCountInBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(runtime->NewNumber(0));
+  return CJS_Result::Success(runtime->NewNumber(0));
 }
 
-CJS_Return CJX_LayoutPseudoModel::relayout(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::relayout(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_Node* pRootNode = GetDocument()->GetRoot();
+  auto* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
   CXFA_Form* pFormRoot =
       pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
-  CXFA_Node* pContentRootNode = pFormRoot->GetFirstChild();
-  CXFA_LayoutProcessor* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
-  if (pContentRootNode)
-    pLayoutProcessor->AddChangedContainer(pContentRootNode);
-
-  pLayoutProcessor->SetForceReLayout(true);
-  return CJS_Return(true);
+  if (pFormRoot) {
+    CXFA_Node* pContentRootNode = pFormRoot->GetFirstChild();
+    if (pContentRootNode)
+      pLayoutProcessor->AddChangedContainer(pContentRootNode);
+  }
+  pLayoutProcessor->SetForceRelayout(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_LayoutPseudoModel::absPageSpan(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::absPageSpan(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return pageSpan(runtime, params);
 }
 
-CJS_Return CJX_LayoutPseudoModel::absPageInBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::absPageInBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewNumber(0));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(runtime->NewNumber(0));
 }
 
-CJS_Return CJX_LayoutPseudoModel::sheetInBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::sheetInBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewNumber(0));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(runtime->NewNumber(0));
 }
 
-CJS_Return CJX_LayoutPseudoModel::sheet(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::sheet(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, true);
 }
 
-CJS_Return CJX_LayoutPseudoModel::relayoutPageArea(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::relayoutPageArea(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_LayoutPseudoModel::sheetCount(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::sheetCount(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return NumberedPageCount(runtime, false);
 }
 
-CJS_Return CJX_LayoutPseudoModel::absPage(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::absPage(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, true);
 }
 
-CJS_Return CJX_LayoutPseudoModel::PageInternals(
-    CJS_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::PageInternals(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params,
     bool bAbsPage) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  CXFA_Node* pNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   if (!pNode)
-    return CJS_Return(runtime->NewNumber(0));
+    return CJS_Result::Success(runtime->NewNumber(0));
 
-  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
-  if (!pDocLayout)
-    return CJS_Return(true);
-
-  CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
+  CXFA_ContentLayoutItem* pLayoutItem =
+      ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
   if (!pLayoutItem)
-    return CJS_Return(runtime->NewNumber(-1));
+    return CJS_Result::Success(runtime->NewNumber(-1));
 
   int32_t iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
-  return CJS_Return(runtime->NewNumber(bAbsPage ? iPage : iPage + 1));
+  return CJS_Result::Success(runtime->NewNumber(bAbsPage ? iPage : iPage + 1));
 }
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.h b/fxjs/xfa/cjx_layoutpseudomodel.h
index 6dbef60..d5f0cba 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.h
+++ b/fxjs/xfa/cjx_layoutpseudomodel.h
@@ -9,8 +9,8 @@
 
 #include <vector>
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 enum XFA_LAYOUTMODEL_HWXY {
   XFA_LAYOUTMODEL_H,
@@ -24,47 +24,54 @@
 class CXFA_LayoutProcessor;
 class CXFA_Node;
 
-class CJX_LayoutPseudoModel : public CJX_Object {
+class CJX_LayoutPseudoModel final : public CJX_Object {
  public:
   explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model);
   ~CJX_LayoutPseudoModel() override;
 
-  JS_METHOD(absPage, CJX_LayoutPseudoModel);
-  JS_METHOD(absPageCount, CJX_LayoutPseudoModel);
-  JS_METHOD(absPageCountInBatch, CJX_LayoutPseudoModel);
-  JS_METHOD(absPageInBatch, CJX_LayoutPseudoModel);
-  JS_METHOD(absPageSpan, CJX_LayoutPseudoModel);
-  JS_METHOD(h, CJX_LayoutPseudoModel);
-  JS_METHOD(page, CJX_LayoutPseudoModel);
-  JS_METHOD(pageContent, CJX_LayoutPseudoModel);
-  JS_METHOD(pageCount, CJX_LayoutPseudoModel);
-  JS_METHOD(pageSpan, CJX_LayoutPseudoModel);
-  JS_METHOD(relayout, CJX_LayoutPseudoModel);
-  JS_METHOD(relayoutPageArea, CJX_LayoutPseudoModel);
-  JS_METHOD(sheet, CJX_LayoutPseudoModel);
-  JS_METHOD(sheetCount, CJX_LayoutPseudoModel);
-  JS_METHOD(sheetCountInBatch, CJX_LayoutPseudoModel);
-  JS_METHOD(sheetInBatch, CJX_LayoutPseudoModel);
-  JS_METHOD(w, CJX_LayoutPseudoModel);
-  JS_METHOD(x, CJX_LayoutPseudoModel);
-  JS_METHOD(y, CJX_LayoutPseudoModel);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(ready);
+  JSE_METHOD(absPage);
+  JSE_METHOD(absPageCount);
+  JSE_METHOD(absPageCountInBatch);
+  JSE_METHOD(absPageInBatch);
+  JSE_METHOD(absPageSpan);
+  JSE_METHOD(h);
+  JSE_METHOD(page);
+  JSE_METHOD(pageContent);
+  JSE_METHOD(pageCount);
+  JSE_METHOD(pageSpan);
+  JSE_METHOD(relayout);
+  JSE_METHOD(relayoutPageArea);
+  JSE_METHOD(sheet);
+  JSE_METHOD(sheetCount);
+  JSE_METHOD(sheetCountInBatch);
+  JSE_METHOD(sheetInBatch);
+  JSE_METHOD(w);
+  JSE_METHOD(x);
+  JSE_METHOD(y);
+
+  JSE_PROP(ready);
 
  private:
-  CJS_Return NumberedPageCount(CJS_V8* runtime, bool bNumbered);
-  CJS_Return HWXY(CJS_V8* runtime,
+  using Type__ = CJX_LayoutPseudoModel;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::LayoutPseudoModel;
+  static const CJX_MethodSpec MethodSpecs[];
+
+  CJS_Result NumberedPageCount(CFX_V8* runtime, bool bNumbered);
+  CJS_Result HWXY(CFX_V8* runtime,
                   const std::vector<v8::Local<v8::Value>>& params,
                   XFA_LAYOUTMODEL_HWXY layoutModel);
   std::vector<CXFA_Node*> GetObjArray(CXFA_LayoutProcessor* pDocLayout,
                                       int32_t iPageNo,
                                       const WideString& wsType,
                                       bool bOnPageArea);
-  CJS_Return PageInternals(CJS_V8* runtime,
+  CJS_Result PageInternals(CFX_V8* runtime,
                            const std::vector<v8::Local<v8::Value>>& params,
                            bool bAbsPage);
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_LAYOUTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_line.cpp b/fxjs/xfa/cjx_line.cpp
deleted file mode 100644
index 4856939..0000000
--- a/fxjs/xfa/cjx_line.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_line.h"
-
-#include "xfa/fxfa/parser/cxfa_line.h"
-
-CJX_Line::CJX_Line(CXFA_Line* node) : CJX_Node(node) {}
-
-CJX_Line::~CJX_Line() = default;
-
-void CJX_Line::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Line::slope(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Line::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Line::hand(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_line.h b/fxjs/xfa/cjx_line.h
deleted file mode 100644
index 7041c12..0000000
--- a/fxjs/xfa/cjx_line.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_LINE_H_
-#define FXJS_XFA_CJX_LINE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Line;
-
-class CJX_Line : public CJX_Node {
- public:
-  explicit CJX_Line(CXFA_Line* node);
-  ~CJX_Line() override;
-
-  JS_PROP(hand);
-  JS_PROP(slope);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_LINE_H_
diff --git a/fxjs/xfa/cjx_linear.cpp b/fxjs/xfa/cjx_linear.cpp
deleted file mode 100644
index 1c22699..0000000
--- a/fxjs/xfa/cjx_linear.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_linear.h"
-
-#include "xfa/fxfa/parser/cxfa_linear.h"
-
-CJX_Linear::CJX_Linear(CXFA_Linear* node) : CJX_Node(node) {}
-
-CJX_Linear::~CJX_Linear() = default;
-
-void CJX_Linear::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Linear::type(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Linear::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_linear.h b/fxjs/xfa/cjx_linear.h
deleted file mode 100644
index 30a54bf..0000000
--- a/fxjs/xfa/cjx_linear.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_LINEAR_H_
-#define FXJS_XFA_CJX_LINEAR_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Linear;
-
-class CJX_Linear : public CJX_Node {
- public:
-  explicit CJX_Linear(CXFA_Linear* node);
-  ~CJX_Linear() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_LINEAR_H_
diff --git a/fxjs/xfa/cjx_list.cpp b/fxjs/xfa/cjx_list.cpp
index 9cbd32d..7c0cf23 100644
--- a/fxjs/xfa/cjx_list.cpp
+++ b/fxjs/xfa/cjx_list.cpp
@@ -8,9 +8,10 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_class.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_list.h"
@@ -22,66 +23,76 @@
                                                 {"remove", remove_static}};
 
 CJX_List::CJX_List(CXFA_List* list) : CJX_Object(list) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_List::~CJX_List() {}
 
-CXFA_List* CJX_List::GetXFAList() {
-  return static_cast<CXFA_List*>(GetXFAObject());
+bool CJX_List::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_List::append(CJS_V8* runtime,
+CXFA_List* CJX_List::GetXFAList() {
+  return ToList(GetXFAObject());
+}
+
+CJS_Result CJX_List::append(CFX_V8* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  auto* pNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   if (!pNode)
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
   GetXFAList()->Append(pNode);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_List::insert(CJS_V8* runtime,
+CJS_Result CJX_List::insert(CFX_V8* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNewNode = ToNode(runtime->ToXFAObject(params[0]));
+  auto* pNewNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   if (!pNewNode)
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
-  auto* pBeforeNode = ToNode(runtime->ToXFAObject(params[1]));
-  GetXFAList()->Insert(pNewNode, pBeforeNode);
-  return CJS_Return(true);
+  auto* pBeforeNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[1]));
+  if (!GetXFAList()->Insert(pNewNode, pBeforeNode))
+    return CJS_Result::Failure(JSMessage::kValueError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_List::remove(CJS_V8* runtime,
+CJS_Result CJX_List::remove(CFX_V8* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  auto* pNode =
+      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
   if (!pNode)
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kValueError);
 
   GetXFAList()->Remove(pNode);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_List::item(CJS_V8* runtime,
+CJS_Result CJX_List::item(CFX_V8* runtime,
                           const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   int32_t index = runtime->ToInt32(params[0]);
   size_t cast_index = static_cast<size_t>(index);
   if (index < 0 || cast_index >= GetXFAList()->GetLength())
-    return CJS_Return(JSGetStringFromID(JSMessage::kInvalidInputError));
+    return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
-  return CJS_Return(runtime->NewXFAObject(
+  return CJS_Result::Success(static_cast<CFXJSE_Engine*>(runtime)->NewXFAObject(
       GetXFAList()->Item(cast_index),
       GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
 }
diff --git a/fxjs/xfa/cjx_list.h b/fxjs/xfa/cjx_list.h
index 83801b7..842b859 100644
--- a/fxjs/xfa/cjx_list.h
+++ b/fxjs/xfa/cjx_list.h
@@ -7,8 +7,8 @@
 #ifndef FXJS_XFA_CJX_LIST_H_
 #define FXJS_XFA_CJX_LIST_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_List;
 
@@ -17,17 +17,24 @@
   explicit CJX_List(CXFA_List* list);
   ~CJX_List() override;
 
-  JS_METHOD(append, CJX_List);
-  JS_METHOD(insert, CJX_List);
-  JS_METHOD(item, CJX_List);
-  JS_METHOD(remove, CJX_List);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(length);
+  JSE_METHOD(append);
+  JSE_METHOD(insert);
+  JSE_METHOD(item);
+  JSE_METHOD(remove);
+
+  JSE_PROP(length);
 
  private:
-  CXFA_List* GetXFAList();
+  using Type__ = CJX_List;
+  using ParentType__ = CJX_Object;
 
+  static const TypeTag static_type__ = TypeTag::List;
   static const CJX_MethodSpec MethodSpecs[];
+
+  CXFA_List* GetXFAList();
 };
 
 #endif  // FXJS_XFA_CJX_LIST_H_
diff --git a/fxjs/xfa/cjx_list_embeddertest.cpp b/fxjs/xfa/cjx_list_embeddertest.cpp
new file mode 100644
index 0000000..02450dc
--- /dev/null
+++ b/fxjs/xfa/cjx_list_embeddertest.cpp
@@ -0,0 +1,15 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+
+class CJX_ListEmbedderTest : public XFAJSEmbedderTest {};
+
+// Should not crash.
+TEST_F(CJX_ListEmbedderTest, BUG_1263) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+
+  EXPECT_FALSE(Execute("nodes.insert($form,$)"));
+}
diff --git a/fxjs/xfa/cjx_logpseudomodel.cpp b/fxjs/xfa/cjx_logpseudomodel.cpp
index 810ddfa..8cb9ee7 100644
--- a/fxjs/xfa/cjx_logpseudomodel.cpp
+++ b/fxjs/xfa/cjx_logpseudomodel.cpp
@@ -8,7 +8,7 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cscript_logpseudomodel.h"
 
 const CJX_MethodSpec CJX_LogPseudoModel::MethodSpecs[] = {
@@ -20,37 +20,48 @@
 
 CJX_LogPseudoModel::CJX_LogPseudoModel(CScript_LogPseudoModel* model)
     : CJX_Object(model) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_LogPseudoModel::~CJX_LogPseudoModel() {}
 
-CJS_Return CJX_LogPseudoModel::message(
-    CJS_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+bool CJX_LogPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_LogPseudoModel::traceEnabled(
-    CJS_V8* runtime,
+CJS_Result CJX_LogPseudoModel::message(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  // Uncomment to allow using xfa.log.message(""); from JS.
+  // fprintf(stderr, "LOG\n");
+  // for (auto& val : params) {
+  //   v8::String::Utf8Value str(runtime->GetIsolate(), val);
+  //   fprintf(stderr, "  %ls\n", WideString::FromUTF8(*str).c_str());
+  // }
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_LogPseudoModel::traceActivate(
-    CJS_V8* runtime,
+CJS_Result CJX_LogPseudoModel::traceEnabled(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_LogPseudoModel::traceDeactivate(
-    CJS_V8* runtime,
+CJS_Result CJX_LogPseudoModel::traceActivate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_LogPseudoModel::trace(
-    CJS_V8* runtime,
+CJS_Result CJX_LogPseudoModel::traceDeactivate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+  return CJS_Result::Success();
+}
+
+CJS_Result CJX_LogPseudoModel::trace(
+    CFX_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
 }
diff --git a/fxjs/xfa/cjx_logpseudomodel.h b/fxjs/xfa/cjx_logpseudomodel.h
index 772bf30..fda3bb9 100644
--- a/fxjs/xfa/cjx_logpseudomodel.h
+++ b/fxjs/xfa/cjx_logpseudomodel.h
@@ -7,25 +7,32 @@
 #ifndef FXJS_XFA_CJX_LOGPSEUDOMODEL_H_
 #define FXJS_XFA_CJX_LOGPSEUDOMODEL_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CScript_LogPseudoModel;
 
 // TODO(dsinclair): This doesn't exist in the spec. Remove after
 // xfa_basic_data_element_script is removed.
-class CJX_LogPseudoModel : public CJX_Object {
+class CJX_LogPseudoModel final : public CJX_Object {
  public:
   explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model);
   ~CJX_LogPseudoModel() override;
 
-  JS_METHOD(message, CJX_LogPseudoModel);
-  JS_METHOD(traceEnabled, CJX_LogPseudoModel);
-  JS_METHOD(traceActivate, CJX_LogPseudoModel);
-  JS_METHOD(traceDeactivate, CJX_LogPseudoModel);
-  JS_METHOD(trace, CJX_LogPseudoModel);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_METHOD(message);
+  JSE_METHOD(traceEnabled);
+  JSE_METHOD(traceActivate);
+  JSE_METHOD(traceDeactivate);
+  JSE_METHOD(trace);
 
  private:
+  using Type__ = CJX_LogPseudoModel;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::LogPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_manifest.cpp b/fxjs/xfa/cjx_manifest.cpp
index 0882238..f94232e 100644
--- a/fxjs/xfa/cjx_manifest.cpp
+++ b/fxjs/xfa/cjx_manifest.cpp
@@ -8,49 +8,30 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_manifest.h"
 
 const CJX_MethodSpec CJX_Manifest::MethodSpecs[] = {
     {"evaluate", evaluate_static}};
 
 CJX_Manifest::CJX_Manifest(CXFA_Manifest* manifest) : CJX_Node(manifest) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Manifest::~CJX_Manifest() {}
 
-CJS_Return CJX_Manifest::evaluate(
-    CJS_V8* runtime,
+bool CJX_Manifest::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Manifest::evaluate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  return CJS_Return(
-      runtime->NewBoolean(!!ToNode(GetXFAObject())->GetWidgetAcc()));
-}
-
-void CJX_Manifest::defaultValue(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Manifest::action(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Manifest::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Manifest::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  return CJS_Result::Success(
+      runtime->NewBoolean(GetXFANode()->IsWidgetReady()));
 }
diff --git a/fxjs/xfa/cjx_manifest.h b/fxjs/xfa/cjx_manifest.h
index 75fcacd..8380ac6 100644
--- a/fxjs/xfa/cjx_manifest.h
+++ b/fxjs/xfa/cjx_manifest.h
@@ -7,24 +7,26 @@
 #ifndef FXJS_XFA_CJX_MANIFEST_H_
 #define FXJS_XFA_CJX_MANIFEST_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Manifest;
 
-class CJX_Manifest : public CJX_Node {
+class CJX_Manifest final : public CJX_Node {
  public:
   explicit CJX_Manifest(CXFA_Manifest* manifest);
   ~CJX_Manifest() override;
 
-  JS_METHOD(evaluate, CJX_Manifest);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(action);
-  JS_PROP(use);
-  JS_PROP(usehref);
+  JSE_METHOD(evaluate);
 
  private:
+  using Type__ = CJX_Manifest;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Manifest;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_map.cpp b/fxjs/xfa/cjx_map.cpp
deleted file mode 100644
index 7a1fa14..0000000
--- a/fxjs/xfa/cjx_map.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_map.h"
-
-#include "xfa/fxfa/parser/cxfa_map.h"
-
-CJX_Map::CJX_Map(CXFA_Map* node) : CJX_Node(node) {}
-
-CJX_Map::~CJX_Map() = default;
-
-void CJX_Map::use(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Map::bind(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Map::usehref(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Map::from(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_map.h b/fxjs/xfa/cjx_map.h
deleted file mode 100644
index dba7eb6..0000000
--- a/fxjs/xfa/cjx_map.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_MAP_H_
-#define FXJS_XFA_CJX_MAP_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Map;
-
-class CJX_Map : public CJX_Node {
- public:
-  explicit CJX_Map(CXFA_Map* node);
-  ~CJX_Map() override;
-
-  JS_PROP(bind);
-  JS_PROP(from);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_MAP_H_
diff --git a/fxjs/xfa/cjx_margin.cpp b/fxjs/xfa/cjx_margin.cpp
deleted file mode 100644
index 62a180b..0000000
--- a/fxjs/xfa/cjx_margin.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_margin.h"
-
-#include "xfa/fxfa/parser/cxfa_margin.h"
-
-CJX_Margin::CJX_Margin(CXFA_Margin* node) : CJX_Node(node) {}
-
-CJX_Margin::~CJX_Margin() = default;
-
-void CJX_Margin::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Margin::leftInset(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Margin::bottomInset(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Margin::topInset(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Margin::rightInset(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Margin::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_margin.h b/fxjs/xfa/cjx_margin.h
deleted file mode 100644
index 0ca884f..0000000
--- a/fxjs/xfa/cjx_margin.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_MARGIN_H_
-#define FXJS_XFA_CJX_MARGIN_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Margin;
-
-class CJX_Margin : public CJX_Node {
- public:
-  explicit CJX_Margin(CXFA_Margin* node);
-  ~CJX_Margin() override;
-
-  JS_PROP(bottomInset);
-  JS_PROP(leftInset);
-  JS_PROP(rightInset);
-  JS_PROP(topInset);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_MARGIN_H_
diff --git a/fxjs/xfa/cjx_mdp.cpp b/fxjs/xfa/cjx_mdp.cpp
deleted file mode 100644
index 59730f6..0000000
--- a/fxjs/xfa/cjx_mdp.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_mdp.h"
-
-#include "xfa/fxfa/parser/cxfa_mdp.h"
-
-CJX_Mdp::CJX_Mdp(CXFA_Mdp* node) : CJX_Node(node) {}
-
-CJX_Mdp::~CJX_Mdp() = default;
-
-void CJX_Mdp::use(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Mdp::signatureType(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Mdp::usehref(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Mdp::permissions(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_mdp.h b/fxjs/xfa/cjx_mdp.h
deleted file mode 100644
index bdce2b8..0000000
--- a/fxjs/xfa/cjx_mdp.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_MDP_H_
-#define FXJS_XFA_CJX_MDP_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Mdp;
-
-class CJX_Mdp : public CJX_Node {
- public:
-  explicit CJX_Mdp(CXFA_Mdp* node);
-  ~CJX_Mdp() override;
-
-  JS_PROP(permissions);
-  JS_PROP(signatureType);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_MDP_H_
diff --git a/fxjs/xfa/cjx_medium.cpp b/fxjs/xfa/cjx_medium.cpp
deleted file mode 100644
index e8986d9..0000000
--- a/fxjs/xfa/cjx_medium.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_medium.h"
-
-#include "xfa/fxfa/parser/cxfa_medium.h"
-
-CJX_Medium::CJX_Medium(CXFA_Medium* node) : CJX_Node(node) {}
-
-CJX_Medium::~CJX_Medium() = default;
-
-void CJX_Medium::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Medium::orientation(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Medium::imagingBBox(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Medium::shortValue(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Medium::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Medium::stock(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Medium::longValue(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_medium.h b/fxjs/xfa/cjx_medium.h
deleted file mode 100644
index 99023dc..0000000
--- a/fxjs/xfa/cjx_medium.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_MEDIUM_H_
-#define FXJS_XFA_CJX_MEDIUM_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Medium;
-
-class CJX_Medium : public CJX_Node {
- public:
-  explicit CJX_Medium(CXFA_Medium* node);
-  ~CJX_Medium() override;
-
-  JS_PROP(imagingBBox);
-  JS_PROP(longValue); /* long */
-  JS_PROP(orientation);
-  JS_PROP(shortValue); /* short */
-  JS_PROP(stock);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_MEDIUM_H_
diff --git a/fxjs/xfa/cjx_message.cpp b/fxjs/xfa/cjx_message.cpp
deleted file mode 100644
index c9f3f6a..0000000
--- a/fxjs/xfa/cjx_message.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_message.h"
-
-#include "xfa/fxfa/parser/cxfa_message.h"
-
-CJX_Message::CJX_Message(CXFA_Message* node) : CJX_Node(node) {}
-
-CJX_Message::~CJX_Message() = default;
-
-void CJX_Message::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Message::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_message.h b/fxjs/xfa/cjx_message.h
deleted file mode 100644
index a2a461e..0000000
--- a/fxjs/xfa/cjx_message.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_MESSAGE_H_
-#define FXJS_XFA_CJX_MESSAGE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Message;
-
-class CJX_Message : public CJX_Node {
- public:
-  explicit CJX_Message(CXFA_Message* node);
-  ~CJX_Message() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_MESSAGE_H_
diff --git a/fxjs/xfa/cjx_model.cpp b/fxjs/xfa/cjx_model.cpp
index aa5d6b2..4fa0a2e 100644
--- a/fxjs/xfa/cjx_model.cpp
+++ b/fxjs/xfa/cjx_model.cpp
@@ -8,11 +8,12 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_delta.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 
 const CJX_MethodSpec CJX_Model::MethodSpecs[] = {
     {"clearErrorList", clearErrorList_static},
@@ -20,22 +21,26 @@
     {"isCompatibleNS", isCompatibleNS_static}};
 
 CJX_Model::CJX_Model(CXFA_Node* node) : CJX_Node(node) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Model::~CJX_Model() {}
 
-CJS_Return CJX_Model::clearErrorList(
-    CJS_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params) {
-  return CJS_Return(true);
+bool CJX_Model::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_Model::createNode(
-    CJS_V8* runtime,
+CJS_Result CJX_Model::clearErrorList(
+    CFX_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Result::Success();
+}
+
+CJS_Result CJX_Model::createNode(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString name;
   if (params.size() > 1)
@@ -46,14 +51,14 @@
     nameSpace = runtime->ToWideString(params[2]);
 
   WideString tagName = runtime->ToWideString(params[0]);
-  XFA_Element eType = CXFA_Node::NameToElement(tagName);
+  XFA_Element eType = XFA_GetElementByName(tagName.AsStringView());
   CXFA_Node* pNewNode = GetXFANode()->CreateSamePacketNode(eType);
   if (!pNewNode)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
   if (!name.IsEmpty()) {
     if (!pNewNode->HasAttribute(XFA_Attribute::Name))
-      return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+      return CJS_Result::Failure(JSMessage::kParamError);
 
     pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name, name.AsStringView(),
                                        true);
@@ -62,24 +67,23 @@
   }
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewNode);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNewNode);
 
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
-CJS_Return CJX_Model::isCompatibleNS(
-    CJS_V8* runtime,
+CJS_Result CJX_Model::isCompatibleNS(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString nameSpace;
   if (params.size() >= 1)
     nameSpace = runtime->ToWideString(params[0]);
 
-  return CJS_Return(
+  return CJS_Result::Success(
       runtime->NewBoolean(TryNamespace().value_or(WideString()) == nameSpace));
 }
 
diff --git a/fxjs/xfa/cjx_model.h b/fxjs/xfa/cjx_model.h
index fae1cc7..9fd54f5 100644
--- a/fxjs/xfa/cjx_model.h
+++ b/fxjs/xfa/cjx_model.h
@@ -7,8 +7,8 @@
 #ifndef FXJS_XFA_CJX_MODEL_H_
 #define FXJS_XFA_CJX_MODEL_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Node;
 
@@ -17,14 +17,21 @@
   explicit CJX_Model(CXFA_Node* obj);
   ~CJX_Model() override;
 
-  JS_METHOD(clearErrorList, CJX_Model);
-  JS_METHOD(createNode, CJX_Model);
-  JS_METHOD(isCompatibleNS, CJX_Model);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(aliasNode);
-  JS_PROP(context);
+  JSE_METHOD(clearErrorList);
+  JSE_METHOD(createNode);
+  JSE_METHOD(isCompatibleNS);
+
+  JSE_PROP(aliasNode);
+  JSE_PROP(context);
 
  private:
+  using Type__ = CJX_Model;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Model;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
index cf2642c..3877f72 100644
--- a/fxjs/xfa/cjx_node.cpp
+++ b/fxjs/xfa/cjx_node.cpp
@@ -7,23 +7,29 @@
 #include "fxjs/xfa/cjx_node.h"
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "core/fxcrt/cfx_memorystream.h"
 #include "core/fxcrt/fx_codepage.h"
-#include "fxjs/cfxjse_engine.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_document_parser.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_simple_parser.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 namespace {
 
-enum class EventAppliesToo {
+enum class EventAppliesToo : uint8_t {
   kNone = 0,
   kAll = 1,
   kAllNonRecursive = 2,
@@ -36,49 +42,89 @@
 
 struct XFA_ExecEventParaInfo {
  public:
-  uint32_t m_uHash;
-  const wchar_t* m_lpcEventName;
+  uint32_t m_uHash;  // hashed as wide string.
   XFA_EVENTTYPE m_eventType;
   EventAppliesToo m_validFlags;
 };
 
+#undef PARA
+#define PARA(a, b, c, d) a, c, d
 const XFA_ExecEventParaInfo gs_eventParaInfos[] = {
-    {0x109d7ce7, L"mouseEnter", XFA_EVENT_MouseEnter, EventAppliesToo::kField},
-    {0x1bfc72d9, L"preOpen", XFA_EVENT_PreOpen, EventAppliesToo::kChoiceList},
-    {0x2196a452, L"initialize", XFA_EVENT_Initialize, EventAppliesToo::kAll},
-    {0x27410f03, L"mouseExit", XFA_EVENT_MouseExit, EventAppliesToo::kField},
-    {0x36f1c6d8, L"preSign", XFA_EVENT_PreSign, EventAppliesToo::kSignature},
-    {0x4731d6ba, L"exit", XFA_EVENT_Exit, EventAppliesToo::kAllNonRecursive},
-    {0x7233018a, L"validate", XFA_EVENT_Validate, EventAppliesToo::kAll},
-    {0x8808385e, L"indexChange", XFA_EVENT_IndexChange,
-     EventAppliesToo::kSubform},
-    {0x891f4606, L"change", XFA_EVENT_Change,
-     EventAppliesToo::kFieldOrExclusion},
-    {0x9f693b21, L"mouseDown", XFA_EVENT_MouseDown, EventAppliesToo::kField},
-    {0xcdce56b3, L"full", XFA_EVENT_Full, EventAppliesToo::kFieldOrExclusion},
-    {0xd576d08e, L"mouseUp", XFA_EVENT_MouseUp, EventAppliesToo::kField},
-    {0xd95657a6, L"click", XFA_EVENT_Click, EventAppliesToo::kFieldOrExclusion},
-    {0xdbfbe02e, L"calculate", XFA_EVENT_Calculate, EventAppliesToo::kAll},
-    {0xe25fa7b8, L"postOpen", XFA_EVENT_PostOpen, EventAppliesToo::kChoiceList},
-    {0xe28dce7e, L"enter", XFA_EVENT_Enter, EventAppliesToo::kAllNonRecursive},
-    {0xfd54fbb7, L"postSign", XFA_EVENT_PostSign, EventAppliesToo::kSignature},
+    {PARA(0x109d7ce7,
+          "mouseEnter",
+          XFA_EVENT_MouseEnter,
+          EventAppliesToo::kField)},
+    {PARA(0x1bfc72d9,
+          "preOpen",
+          XFA_EVENT_PreOpen,
+          EventAppliesToo::kChoiceList)},
+    {PARA(0x2196a452,
+          "initialize",
+          XFA_EVENT_Initialize,
+          EventAppliesToo::kAll)},
+    {PARA(0x27410f03,
+          "mouseExit",
+          XFA_EVENT_MouseExit,
+          EventAppliesToo::kField)},
+    {PARA(0x36f1c6d8,
+          "preSign",
+          XFA_EVENT_PreSign,
+          EventAppliesToo::kSignature)},
+    {PARA(0x4731d6ba,
+          "exit",
+          XFA_EVENT_Exit,
+          EventAppliesToo::kAllNonRecursive)},
+    {PARA(0x7233018a, "validate", XFA_EVENT_Validate, EventAppliesToo::kAll)},
+    {PARA(0x8808385e,
+          "indexChange",
+          XFA_EVENT_IndexChange,
+          EventAppliesToo::kSubform)},
+    {PARA(0x891f4606,
+          "change",
+          XFA_EVENT_Change,
+          EventAppliesToo::kFieldOrExclusion)},
+    {PARA(0x9f693b21,
+          "mouseDown",
+          XFA_EVENT_MouseDown,
+          EventAppliesToo::kField)},
+    {PARA(0xcdce56b3,
+          "full",
+          XFA_EVENT_Full,
+          EventAppliesToo::kFieldOrExclusion)},
+    {PARA(0xd576d08e, "mouseUp", XFA_EVENT_MouseUp, EventAppliesToo::kField)},
+    {PARA(0xd95657a6,
+          "click",
+          XFA_EVENT_Click,
+          EventAppliesToo::kFieldOrExclusion)},
+    {PARA(0xdbfbe02e, "calculate", XFA_EVENT_Calculate, EventAppliesToo::kAll)},
+    {PARA(0xe25fa7b8,
+          "postOpen",
+          XFA_EVENT_PostOpen,
+          EventAppliesToo::kChoiceList)},
+    {PARA(0xe28dce7e,
+          "enter",
+          XFA_EVENT_Enter,
+          EventAppliesToo::kAllNonRecursive)},
+    {PARA(0xfd54fbb7,
+          "postSign",
+          XFA_EVENT_PostSign,
+          EventAppliesToo::kSignature)},
 };
+#undef PARA
 
 const XFA_ExecEventParaInfo* GetEventParaInfoByName(
-    const WideStringView& wsEventName) {
+    WideStringView wsEventName) {
+  if (wsEventName.IsEmpty())
+    return nullptr;
+
   uint32_t uHash = FX_HashCode_GetW(wsEventName, false);
-  int32_t iStart = 0;
-  int32_t iEnd = (sizeof(gs_eventParaInfos) / sizeof(gs_eventParaInfos[0])) - 1;
-  do {
-    int32_t iMid = (iStart + iEnd) / 2;
-    const XFA_ExecEventParaInfo* eventParaInfo = &gs_eventParaInfos[iMid];
-    if (uHash == eventParaInfo->m_uHash)
-      return eventParaInfo;
-    if (uHash < eventParaInfo->m_uHash)
-      iEnd = iMid - 1;
-    else
-      iStart = iMid + 1;
-  } while (iStart <= iEnd);
+  auto* result = std::lower_bound(
+      std::begin(gs_eventParaInfos), std::end(gs_eventParaInfos), uHash,
+      [](const XFA_ExecEventParaInfo& iter, const uint16_t& hash) {
+        return iter.m_uHash < hash;
+      });
+  if (result != std::end(gs_eventParaInfos) && result->m_uHash == uHash)
+    return result;
   return nullptr;
 }
 
@@ -98,115 +144,122 @@
     {"setElement", setElement_static}};
 
 CJX_Node::CJX_Node(CXFA_Node* node) : CJX_Tree(node) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Node::~CJX_Node() = default;
 
-CXFA_Node* CJX_Node::GetXFANode() {
-  return static_cast<CXFA_Node*>(GetXFAObject());
+bool CJX_Node::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-const CXFA_Node* CJX_Node::GetXFANode() const {
-  return static_cast<const CXFA_Node*>(GetXFAObject());
+CXFA_Node* CJX_Node::GetXFANode() const {
+  return ToNode(GetXFAObject());
 }
 
-CJS_Return CJX_Node::applyXSL(CJS_V8* runtime,
+CJS_Result CJX_Node::applyXSL(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   // TODO(weili): check whether we need to implement this, pdfium:501.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Node::assignNode(
-    CJS_V8* runtime,
+CJS_Result CJX_Node::assignNode(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   // TODO(weili): check whether we need to implement this, pdfium:501.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Node::clone(CJS_V8* runtime,
+CJS_Result CJX_Node::clone(CFX_V8* runtime,
                            const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0]));
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pCloneNode);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pCloneNode);
+
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
-CJS_Return CJX_Node::getAttribute(
-    CJS_V8* runtime,
+CJS_Result CJX_Node::getAttribute(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
-  return CJS_Return(runtime->NewString(
-      GetAttribute(expression.AsStringView()).UTF8Encode().AsStringView()));
+  return CJS_Result::Success(runtime->NewString(
+      GetAttribute(expression.AsStringView()).ToUTF8().AsStringView()));
 }
 
-CJS_Return CJX_Node::getElement(
-    CJS_V8* runtime,
+CJS_Result CJX_Node::getElement(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
   int32_t iValue = params.size() >= 2 ? runtime->ToInt32(params[1]) : 0;
+  XFA_Element eElement = XFA_GetElementByName(expression.AsStringView());
+  if (eElement == XFA_Element::Unknown)
+    return CJS_Result::Success(runtime->NewNull());
 
-  CXFA_Node* pNode = GetOrCreateProperty<CXFA_Node>(
-      iValue, CXFA_Node::NameToElement(expression));
+  CXFA_Node* pNode = GetOrCreateProperty<CXFA_Node>(iValue, eElement);
   if (!pNode)
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
+
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
-CJS_Return CJX_Node::isPropertySpecified(
-    CJS_V8* runtime,
+CJS_Result CJX_Node::isPropertySpecified(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
-  XFA_Attribute attr = CXFA_Node::NameToAttribute(expression.AsStringView());
-  if (attr != XFA_Attribute::Unknown && HasAttribute(attr))
-    return CJS_Return(runtime->NewBoolean(true));
+  Optional<XFA_ATTRIBUTEINFO> attr =
+      XFA_GetAttributeByName(expression.AsStringView());
+  if (attr.has_value() && HasAttribute(attr.value().attribute))
+    return CJS_Result::Success(runtime->NewBoolean(true));
+
+  XFA_Element eType = XFA_GetElementByName(expression.AsStringView());
+  if (eType == XFA_Element::Unknown)
+    return CJS_Result::Success(runtime->NewBoolean(false));
 
   bool bParent = params.size() < 2 || runtime->ToBoolean(params[1]);
   int32_t iIndex = params.size() == 3 ? runtime->ToInt32(params[2]) : 0;
-  XFA_Element eType = CXFA_Node::NameToElement(expression);
   bool bHas = !!GetOrCreateProperty<CXFA_Node>(iIndex, eType);
   if (!bHas && bParent && GetXFANode()->GetParent()) {
     // Also check on the parent.
     auto* jsnode = GetXFANode()->GetParent()->JSObject();
-    bHas = jsnode->HasAttribute(attr) ||
+    bHas = jsnode->HasAttribute(attr.value().attribute) ||
            !!jsnode->GetOrCreateProperty<CXFA_Node>(iIndex, eType);
   }
-  return CJS_Return(runtime->NewBoolean(bHas));
+  return CJS_Result::Success(runtime->NewBoolean(bHas));
 }
 
-CJS_Return CJX_Node::loadXML(CJS_V8* runtime,
+CJS_Result CJX_Node::loadXML(CFX_V8* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   ByteString expression = runtime->ToByteString(params[0]);
   if (expression.IsEmpty())
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   bool bIgnoreRoot = true;
   if (params.size() >= 2)
@@ -216,16 +269,17 @@
   if (params.size() >= 3)
     bOverwrite = runtime->ToBoolean(params[2]);
 
-  auto pParser = pdfium::MakeUnique<CXFA_SimpleParser>(GetDocument());
-  if (!pParser)
-    return CJS_Return(true);
-
+  auto pParser = pdfium::MakeUnique<CXFA_DocumentParser>(GetDocument());
   CFX_XMLNode* pXMLNode = pParser->ParseXMLData(expression);
   if (!pXMLNode)
-    return CJS_Return(true);
+    return CJS_Result::Success();
+
+  CFX_XMLDocument* top_xml_doc =
+      GetXFANode()->GetDocument()->GetNotify()->GetHDOC()->GetXMLDocument();
+  top_xml_doc->AppendNodesFrom(pParser->GetXMLDoc().get());
 
   if (bIgnoreRoot &&
-      (pXMLNode->GetType() != FX_XMLNODE_Element ||
+      (pXMLNode->GetType() != CFX_XMLNode::Type::kElement ||
        XFA_RecognizeRichText(static_cast<CFX_XMLElement*>(pXMLNode)))) {
     bIgnoreRoot = false;
   }
@@ -237,37 +291,36 @@
                                     WideString(wsContentType), false, false);
   }
 
-  std::unique_ptr<CFX_XMLNode> pFakeXMLRoot(pFakeRoot->GetXMLMappingNode());
+  CFX_XMLNode* pFakeXMLRoot = pFakeRoot->GetXMLMappingNode();
   if (!pFakeXMLRoot) {
     CFX_XMLNode* pThisXMLRoot = GetXFANode()->GetXMLMappingNode();
-    pFakeXMLRoot = pThisXMLRoot ? pThisXMLRoot->Clone() : nullptr;
-  }
-  if (!pFakeXMLRoot) {
-    pFakeXMLRoot = pdfium::MakeUnique<CFX_XMLElement>(
-        WideString(GetXFANode()->GetClassName()));
+    CFX_XMLNode* clone;
+    if (pThisXMLRoot) {
+      clone = pThisXMLRoot->Clone(top_xml_doc);
+    } else {
+      clone = top_xml_doc->CreateNode<CFX_XMLElement>(
+          WideString::FromASCII(GetXFANode()->GetClassName()));
+    }
+    pFakeXMLRoot = clone;
   }
 
   if (bIgnoreRoot) {
-    CFX_XMLNode* pXMLChild = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
+    CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild();
     while (pXMLChild) {
-      CFX_XMLNode* pXMLSibling =
-          pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling);
-      pXMLNode->RemoveChildNode(pXMLChild);
-      pFakeXMLRoot->InsertChildNode(pXMLChild);
+      CFX_XMLNode* pXMLSibling = pXMLChild->GetNextSibling();
+      pXMLNode->RemoveChild(pXMLChild);
+      pFakeXMLRoot->AppendLastChild(pXMLChild);
       pXMLChild = pXMLSibling;
     }
   } else {
-    CFX_XMLNode* pXMLParent = pXMLNode->GetNodeItem(CFX_XMLNode::Parent);
-    if (pXMLParent)
-      pXMLParent->RemoveChildNode(pXMLNode);
-
-    pFakeXMLRoot->InsertChildNode(pXMLNode);
+    pXMLNode->RemoveSelfIfParented();
+    pFakeXMLRoot->AppendLastChild(pXMLNode);
   }
 
-  pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot.get());
+  pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot);
   pFakeRoot = pParser->GetRootNode();
   if (!pFakeRoot)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   if (bOverwrite) {
     CXFA_Node* pChild = GetXFANode()->GetFirstChild();
@@ -275,26 +328,26 @@
     int32_t index = 0;
     while (pNewChild) {
       CXFA_Node* pItem = pNewChild->GetNextSibling();
-      pFakeRoot->RemoveChild(pNewChild, true);
-      GetXFANode()->InsertChild(index++, pNewChild);
-      pNewChild->SetFlag(XFA_NodeFlag_Initialized, true);
+      pFakeRoot->RemoveChildAndNotify(pNewChild, true);
+      GetXFANode()->InsertChildAndNotify(index++, pNewChild);
+      pNewChild->SetFlagAndNotify(XFA_NodeFlag_Initialized);
       pNewChild = pItem;
     }
 
     while (pChild) {
       CXFA_Node* pItem = pChild->GetNextSibling();
-      GetXFANode()->RemoveChild(pChild, true);
-      pFakeRoot->InsertChild(pChild, nullptr);
+      GetXFANode()->RemoveChildAndNotify(pChild, true);
+      pFakeRoot->InsertChildAndNotify(pChild, nullptr);
       pChild = pItem;
     }
 
     if (GetXFANode()->GetPacketType() == XFA_PacketType::Form &&
         GetXFANode()->GetElementType() == XFA_Element::ExData) {
       CFX_XMLNode* pTempXMLNode = GetXFANode()->GetXMLMappingNode();
-      GetXFANode()->SetXMLMappingNode(pFakeXMLRoot.release());
-      GetXFANode()->SetFlag(XFA_NodeFlag_OwnXMLNode, false);
-      if (pTempXMLNode && !pTempXMLNode->GetNodeItem(CFX_XMLNode::Parent))
-        pFakeXMLRoot.reset(pTempXMLNode);
+      GetXFANode()->SetXMLMappingNode(pFakeXMLRoot);
+
+      if (pTempXMLNode && !pTempXMLNode->GetParent())
+        pFakeXMLRoot = pTempXMLNode;
       else
         pFakeXMLRoot = nullptr;
     }
@@ -303,96 +356,94 @@
     CXFA_Node* pChild = pFakeRoot->GetFirstChild();
     while (pChild) {
       CXFA_Node* pItem = pChild->GetNextSibling();
-      pFakeRoot->RemoveChild(pChild, true);
-      GetXFANode()->InsertChild(pChild, nullptr);
-      pChild->SetFlag(XFA_NodeFlag_Initialized, true);
+      pFakeRoot->RemoveChildAndNotify(pChild, true);
+      GetXFANode()->InsertChildAndNotify(pChild, nullptr);
+      pChild->SetFlagAndNotify(XFA_NodeFlag_Initialized);
       pChild = pItem;
     }
   }
 
   if (pFakeXMLRoot) {
-    pFakeRoot->SetXMLMappingNode(pFakeXMLRoot.release());
-    pFakeRoot->SetFlag(XFA_NodeFlag_OwnXMLNode, false);
+    pFakeRoot->SetXMLMappingNode(std::move(pFakeXMLRoot));
   }
-  pFakeRoot->SetFlag(XFA_NodeFlag_HasRemovedChildren, false);
+  pFakeRoot->SetFlag(XFA_NodeFlag_HasRemovedChildren);
 
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Node::saveFilteredXML(
-    CJS_V8* runtime,
+CJS_Result CJX_Node::saveFilteredXML(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // TODO(weili): Check whether we need to implement this, pdfium:501.
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Node::saveXML(CJS_V8* runtime,
+CJS_Result CJX_Node::saveXML(CFX_V8* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() > 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  if (params.size() == 1 && runtime->ToWideString(params[0]) != L"pretty")
-    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  if (params.size() == 1 &&
+      !runtime->ToWideString(params[0]).EqualsASCII("pretty")) {
+    return CJS_Result::Failure(JSMessage::kValueError);
+  }
 
   // TODO(weili): Check whether we need to save pretty print XML, pdfium:501.
 
-  WideString bsXMLHeader = L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+  ByteString bsXMLHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
   if (GetXFANode()->GetPacketType() != XFA_PacketType::Form &&
       GetXFANode()->GetPacketType() != XFA_PacketType::Datasets) {
-    return CJS_Return(runtime->NewString(""));
+    return CJS_Result::Success(runtime->NewString(""));
   }
 
   CFX_XMLNode* pElement = nullptr;
   if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) {
     pElement = GetXFANode()->GetXMLMappingNode();
-    if (!pElement || pElement->GetType() != FX_XMLNODE_Element) {
-      return CJS_Return(
-          runtime->NewString(bsXMLHeader.UTF8Encode().AsStringView()));
+    if (!pElement || pElement->GetType() != CFX_XMLNode::Type::kElement) {
+      return CJS_Result::Success(
+          runtime->NewString(bsXMLHeader.AsStringView()));
     }
 
     XFA_DataExporter_DealWithDataGroupNode(GetXFANode());
   }
 
-  auto pMemoryStream = pdfium::MakeRetain<CFX_MemoryStream>(true);
-  auto pStream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(pMemoryStream, true);
-  pStream->SetCodePage(FX_CODEPAGE_UTF8);
-  pStream->WriteString(bsXMLHeader.AsStringView());
+  auto pMemoryStream = pdfium::MakeRetain<CFX_MemoryStream>();
+  pMemoryStream->WriteString(bsXMLHeader.AsStringView());
 
-  if (GetXFANode()->GetPacketType() == XFA_PacketType::Form)
-    XFA_DataExporter_RegenerateFormFile(GetXFANode(), pStream, nullptr, true);
-  else
-    pElement->SaveXMLNode(pStream);
+  if (GetXFANode()->GetPacketType() == XFA_PacketType::Form) {
+    XFA_DataExporter_RegenerateFormFile(GetXFANode(), pMemoryStream, true);
+  } else {
+    pElement->Save(pMemoryStream);
+  }
 
-  return CJS_Return(runtime->NewString(
+  return CJS_Result::Success(runtime->NewString(
       ByteStringView(pMemoryStream->GetBuffer(), pMemoryStream->GetSize())));
 }
 
-CJS_Return CJX_Node::setAttribute(
-    CJS_V8* runtime,
+CJS_Result CJX_Node::setAttribute(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
+  // Note: yes, arglist is spec'd absolutely backwards from what any sane
+  // person would do, namely value first, attribute second.
   WideString attributeValue = runtime->ToWideString(params[0]);
   WideString attribute = runtime->ToWideString(params[1]);
+
+  // Pass them to our method, however, in the more usual manner.
   SetAttribute(attribute.AsStringView(), attributeValue.AsStringView(), true);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Node::setElement(
-    CJS_V8* runtime,
+CJS_Result CJX_Node::setElement(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1 && params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   // TODO(weili): check whether we need to implement this, pdfium:501.
-  return CJS_Return(true);
-}
-
-void CJX_Node::id(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  return CJS_Result::Success();
 }
 
 void CJX_Node::ns(CFXJSE_Value* pValue,
@@ -403,7 +454,7 @@
     return;
   }
   pValue->SetString(
-      TryNamespace().value_or(WideString()).UTF8Encode().AsStringView());
+      TryNamespace().value_or(WideString()).ToUTF8().AsStringView());
 }
 
 void CJX_Node::model(CFXJSE_Value* pValue,
@@ -413,7 +464,7 @@
     ThrowInvalidPropertyException();
     return;
   }
-  pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(
+  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
       GetXFANode()->GetModelNode()));
 }
 
@@ -449,28 +500,29 @@
     return;
   }
 
-  std::vector<CXFA_Node*> properties = GetXFANode()->GetNodeList(
-      XFA_NODEFILTER_OneOfProperty, XFA_Element::Unknown);
+  std::vector<CXFA_Node*> properties =
+      GetXFANode()->GetNodeListWithFilter(XFA_NODEFILTER_OneOfProperty);
   if (!properties.empty()) {
-    pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(
-        properties.front()));
+    pValue->Assign(
+        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+            properties.front()));
   }
 }
 
-int32_t CJX_Node::execSingleEventByName(const WideStringView& wsEventName,
-                                        XFA_Element eType) {
+XFA_EventError CJX_Node::execSingleEventByName(WideStringView wsEventName,
+                                               XFA_Element eType) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
   const XFA_ExecEventParaInfo* eventParaInfo =
       GetEventParaInfoByName(wsEventName);
   if (!eventParaInfo)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
   switch (eventParaInfo->m_validFlags) {
     case EventAppliesToo::kNone:
-      return XFA_EVENTERROR_NotExist;
+      return XFA_EventError::kNotExist;
     case EventAppliesToo::kAll:
     case EventAppliesToo::kAllNonRecursive:
       return pNotify->ExecEventByDeepFirst(
@@ -478,13 +530,13 @@
           eventParaInfo->m_validFlags == EventAppliesToo::kAll);
     case EventAppliesToo::kSubform:
       if (eType != XFA_Element::Subform)
-        return XFA_EVENTERROR_NotExist;
+        return XFA_EventError::kNotExist;
 
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     case EventAppliesToo::kFieldOrExclusion: {
       if (eType != XFA_Element::ExclGroup && eType != XFA_Element::Field)
-        return XFA_EVENTERROR_NotExist;
+        return XFA_EventError::kNotExist;
 
       CXFA_Node* pParentNode = GetXFANode()->GetParent();
       if (pParentNode &&
@@ -498,35 +550,30 @@
     }
     case EventAppliesToo::kField:
       if (eType != XFA_Element::Field)
-        return XFA_EVENTERROR_NotExist;
+        return XFA_EventError::kNotExist;
 
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     case EventAppliesToo::kSignature: {
-      CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc();
-      if (!pWidgetAcc)
-        return XFA_EVENTERROR_NotExist;
-
-      CXFA_Node* pUINode = pWidgetAcc->GetUIChild();
-      if (pUINode->GetElementType() != XFA_Element::Signature)
-        return XFA_EVENTERROR_NotExist;
-
+      if (!GetXFANode()->IsWidgetReady())
+        return XFA_EventError::kNotExist;
+      if (GetXFANode()->GetUIChildNode()->GetElementType() !=
+          XFA_Element::Signature) {
+        return XFA_EventError::kNotExist;
+      }
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     }
     case EventAppliesToo::kChoiceList: {
-      CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc();
-      if (!pWidgetAcc)
-        return XFA_EVENTERROR_NotExist;
-
-      CXFA_Node* pUINode = pWidgetAcc->GetUIChild();
-      if (pUINode->GetElementType() != XFA_Element::ChoiceList ||
-          pWidgetAcc->IsListBox()) {
-        return XFA_EVENTERROR_NotExist;
+      if (!GetXFANode()->IsWidgetReady())
+        return XFA_EventError::kNotExist;
+      if (GetXFANode()->GetUIChildNode()->GetElementType() !=
+          XFA_Element::ChoiceList) {
+        return XFA_EventError::kNotExist;
       }
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     }
   }
-  return XFA_EVENTERROR_NotExist;
+  return XFA_EventError::kNotExist;
 }
diff --git a/fxjs/xfa/cjx_node.h b/fxjs/xfa/cjx_node.h
index f7cbce8..1cbceb6 100644
--- a/fxjs/xfa/cjx_node.h
+++ b/fxjs/xfa/cjx_node.h
@@ -7,9 +7,9 @@
 #ifndef FXJS_XFA_CJX_NODE_H_
 #define FXJS_XFA_CJX_NODE_H_
 
-#include "fxjs/CJX_Define.h"
-#include "fxjs/xfa/cjx_object.h"
 #include "fxjs/xfa/cjx_tree.h"
+#include "fxjs/xfa/jse_define.h"
+#include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CXFA_Node;
@@ -19,33 +19,38 @@
   explicit CJX_Node(CXFA_Node* node);
   ~CJX_Node() override;
 
-  JS_METHOD(applyXSL, CJX_Node);
-  JS_METHOD(assignNode, CJX_Node);
-  JS_METHOD(clone, CJX_Node);
-  JS_METHOD(getAttribute, CJX_Node);
-  JS_METHOD(getElement, CJX_Node);
-  JS_METHOD(isPropertySpecified, CJX_Node);
-  JS_METHOD(loadXML, CJX_Node);
-  JS_METHOD(saveFilteredXML, CJX_Node);
-  JS_METHOD(saveXML, CJX_Node);
-  JS_METHOD(setAttribute, CJX_Node);
-  JS_METHOD(setElement, CJX_Node);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(id);
-  JS_PROP(isContainer);
-  JS_PROP(isNull);
-  JS_PROP(model);
-  JS_PROP(ns);
-  JS_PROP(oneOfChild);
+  JSE_METHOD(applyXSL);
+  JSE_METHOD(assignNode);
+  JSE_METHOD(clone);
+  JSE_METHOD(getAttribute);
+  JSE_METHOD(getElement);
+  JSE_METHOD(isPropertySpecified);
+  JSE_METHOD(loadXML);
+  JSE_METHOD(saveFilteredXML);
+  JSE_METHOD(saveXML);
+  JSE_METHOD(setAttribute);
+  JSE_METHOD(setElement);
 
-  CXFA_Node* GetXFANode();
-  const CXFA_Node* GetXFANode() const;
+  JSE_PROP(isContainer);
+  JSE_PROP(isNull);
+  JSE_PROP(model);
+  JSE_PROP(ns);
+  JSE_PROP(oneOfChild);
+
+  CXFA_Node* GetXFANode() const;
 
  protected:
-  int32_t execSingleEventByName(const WideStringView& wsEventName,
-                                XFA_Element eType);
+  XFA_EventError execSingleEventByName(WideStringView wsEventName,
+                                       XFA_Element eType);
 
  private:
+  using Type__ = CJX_Node;
+  using ParentType__ = CJX_Tree;
+
+  static const TypeTag static_type__ = TypeTag::Node;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_numericedit.cpp b/fxjs/xfa/cjx_numericedit.cpp
deleted file mode 100644
index c76cc66..0000000
--- a/fxjs/xfa/cjx_numericedit.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_numericedit.h"
-
-#include "xfa/fxfa/parser/cxfa_numericedit.h"
-
-CJX_NumericEdit::CJX_NumericEdit(CXFA_NumericEdit* node) : CJX_Node(node) {}
-
-CJX_NumericEdit::~CJX_NumericEdit() = default;
-
-void CJX_NumericEdit::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_NumericEdit::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_NumericEdit::hScrollPolicy(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_numericedit.h b/fxjs/xfa/cjx_numericedit.h
deleted file mode 100644
index 43f9d00..0000000
--- a/fxjs/xfa/cjx_numericedit.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_NUMERICEDIT_H_
-#define FXJS_XFA_CJX_NUMERICEDIT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_NumericEdit;
-
-class CJX_NumericEdit : public CJX_Node {
- public:
-  explicit CJX_NumericEdit(CXFA_NumericEdit* node);
-  ~CJX_NumericEdit() override;
-
-  JS_PROP(hScrollPolicy);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_NUMERICEDIT_H_
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index 6408cf3..64006b3 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -6,29 +6,31 @@
 
 #include "fxjs/xfa/cjx_object.h"
 
+#include <set>
 #include <tuple>
 
-#include "core/fxcrt/cfx_decimal.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
-#include "fxjs/cjs_return.h"
+#include "fxjs/cjs_result.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_boolean.h"
 #include "fxjs/xfa/cjx_draw.h"
 #include "fxjs/xfa/cjx_field.h"
 #include "fxjs/xfa/cjx_instancemanager.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
 #include "xfa/fxfa/parser/cxfa_border.h"
 #include "xfa/fxfa/parser/cxfa_datavalue.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxfa/parser/cxfa_fill.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
@@ -37,6 +39,7 @@
 #include "xfa/fxfa/parser/cxfa_subform.h"
 #include "xfa/fxfa/parser/cxfa_validate.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 namespace {
@@ -51,15 +54,15 @@
   pData = new WideString(*reinterpret_cast<WideString*>(pData));
 }
 
-XFA_MAPDATABLOCKCALLBACKINFO deleteWideStringCallBack = {XFA_DeleteWideString,
-                                                         XFA_CopyWideString};
+const XFA_MAPDATABLOCKCALLBACKINFO deleteWideStringCallBack = {
+    XFA_DeleteWideString, XFA_CopyWideString};
 
 enum XFA_KEYTYPE {
   XFA_KEYTYPE_Custom,
   XFA_KEYTYPE_Element,
 };
 
-void* GetMapKey_Custom(const WideStringView& wsKey) {
+void* GetMapKey_Custom(WideStringView wsKey) {
   uint32_t dwKey = FX_HashCode_GetW(wsKey, false);
   return (void*)(uintptr_t)((dwKey << 1) | XFA_KEYTYPE_Custom);
 }
@@ -70,11 +73,6 @@
                             XFA_KEYTYPE_Element);
 }
 
-void XFA_DefaultFreeData(void* pData) {}
-
-XFA_MAPDATABLOCKCALLBACKINFO gs_XFADefaultFreeData = {XFA_DefaultFreeData,
-                                                      nullptr};
-
 std::tuple<int32_t, int32_t, int32_t> StrToRGB(const WideString& strRGB) {
   int32_t r = 0;
   int32_t g = 0;
@@ -111,7 +109,7 @@
 struct XFA_MAPDATABLOCK {
   uint8_t* GetData() const { return (uint8_t*)this + sizeof(XFA_MAPDATABLOCK); }
 
-  XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo;
+  const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo;
   int32_t iBytes;
 };
 
@@ -129,10 +127,13 @@
   ClearMapModuleBuffer();
 }
 
-void CJX_Object::DefineMethods(const CJX_MethodSpec method_specs[],
-                               size_t count) {
-  for (size_t i = 0; i < count; ++i)
-    method_specs_[method_specs[i].pName] = method_specs[i].pMethodCall;
+bool CJX_Object::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__;
+}
+
+void CJX_Object::DefineMethods(pdfium::span<const CJX_MethodSpec> methods) {
+  for (const auto& item : methods)
+    method_specs_[item.pName] = item.pMethodCall;
 }
 
 CXFA_Document* CJX_Object::GetDocument() const {
@@ -146,8 +147,7 @@
     ThrowInvalidPropertyException();
     return;
   }
-  pValue->SetString(
-      FX_UTF8Encode(GetXFAObject()->GetClassName()).AsStringView());
+  pValue->SetString(GetXFAObject()->GetClassName());
 }
 
 int32_t CJX_Object::Subform_and_SubformSet_InstanceIndex() {
@@ -164,51 +164,49 @@
 }
 
 bool CJX_Object::HasMethod(const WideString& func) const {
-  return pdfium::ContainsKey(method_specs_, func.UTF8Encode());
+  return pdfium::ContainsKey(method_specs_, func.ToUTF8());
 }
 
-CJS_Return CJX_Object::RunMethod(
+CJS_Result CJX_Object::RunMethod(
     const WideString& func,
     const std::vector<v8::Local<v8::Value>>& params) {
-  auto it = method_specs_.find(func.UTF8Encode());
+  auto it = method_specs_.find(func.ToUTF8());
   if (it == method_specs_.end())
-    return CJS_Return(false);
+    return CJS_Result::Failure(JSMessage::kUnknownMethod);
+
   return it->second(this, GetXFAObject()->GetDocument()->GetScriptContext(),
                     params);
 }
 
 void CJX_Object::ThrowTooManyOccurancesException(const WideString& obj) const {
-  ThrowException(
-      L"The element [%ls] has violated its allowable number of occurrences.",
-      obj.c_str());
+  ThrowException(WideString::FromASCII("The element [") + obj +
+                 WideString::FromASCII(
+                     "] has violated its allowable number of occurrences."));
 }
 
 void CJX_Object::ThrowInvalidPropertyException() const {
-  ThrowException(L"Invalid property set operation.");
+  ThrowException(WideString::FromASCII("Invalid property set operation."));
 }
 
 void CJX_Object::ThrowIndexOutOfBoundsException() const {
-  ThrowException(L"Index value is out of bounds.");
+  ThrowException(WideString::FromASCII("Index value is out of bounds."));
 }
 
 void CJX_Object::ThrowParamCountMismatchException(
     const WideString& method) const {
-  ThrowException(L"Incorrect number of parameters calling method '%.16s'.",
-                 method.c_str());
+  ThrowException(
+      WideString::FromASCII("Incorrect number of parameters calling method '") +
+      method + WideString::FromASCII("'."));
 }
 
 void CJX_Object::ThrowArgumentMismatchException() const {
-  ThrowException(L"Argument mismatch in property or function argument.");
+  ThrowException(WideString::FromASCII(
+      "Argument mismatch in property or function argument."));
 }
 
-void CJX_Object::ThrowException(const wchar_t* str, ...) const {
-  va_list arg_ptr;
-  va_start(arg_ptr, str);
-  WideString wsMessage = WideString::FormatV(str, arg_ptr);
-  va_end(arg_ptr);
-
-  ASSERT(!wsMessage.IsEmpty());
-  FXJSE_ThrowMessage(wsMessage.UTF8Encode().AsStringView());
+void CJX_Object::ThrowException(const WideString& str) const {
+  ASSERT(!str.IsEmpty());
+  FXJSE_ThrowMessage(str.ToUTF8().AsStringView());
 }
 
 bool CJX_Object::HasAttribute(XFA_Attribute eAttr) {
@@ -216,53 +214,55 @@
   return HasMapModuleKey(pKey);
 }
 
-bool CJX_Object::SetAttribute(XFA_Attribute eAttr,
-                              const WideStringView& wsValue,
+void CJX_Object::SetAttribute(XFA_Attribute eAttr,
+                              WideStringView wsValue,
                               bool bNotify) {
   switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
     case XFA_AttributeType::Enum: {
-      Optional<XFA_AttributeEnum> item =
-          CXFA_Node::NameToAttributeEnum(wsValue);
-      return SetEnum(
-          eAttr,
-          item ? *item : *(ToNode(GetXFAObject())->GetDefaultEnum(eAttr)),
-          bNotify);
+      Optional<XFA_AttributeValue> item = XFA_GetAttributeValueByName(wsValue);
+      SetEnum(eAttr,
+              item ? *item : *(ToNode(GetXFAObject())->GetDefaultEnum(eAttr)),
+              bNotify);
+      break;
     }
     case XFA_AttributeType::CData:
-      return SetCData(eAttr, WideString(wsValue), bNotify, false);
+      SetCData(eAttr, WideString(wsValue), bNotify, false);
+      break;
     case XFA_AttributeType::Boolean:
-      return SetBoolean(eAttr, wsValue != L"0", bNotify);
+      SetBoolean(eAttr, !wsValue.EqualsASCII("0"), bNotify);
+      break;
     case XFA_AttributeType::Integer:
-      return SetInteger(eAttr,
-                        FXSYS_round(FXSYS_wcstof(wsValue.unterminated_c_str(),
-                                                 wsValue.GetLength(), nullptr)),
-                        bNotify);
+      SetInteger(eAttr,
+                 FXSYS_roundf(FXSYS_wcstof(wsValue.unterminated_c_str(),
+                                           wsValue.GetLength(), nullptr)),
+                 bNotify);
+      break;
     case XFA_AttributeType::Measure:
-      return SetMeasure(eAttr, CXFA_Measurement(wsValue), bNotify);
+      SetMeasure(eAttr, CXFA_Measurement(wsValue), bNotify);
+      break;
     default:
       break;
   }
-  return false;
 }
 
-void CJX_Object::SetMapModuleString(void* pKey, const WideStringView& wsValue) {
-  SetMapModuleBuffer(pKey, (void*)wsValue.unterminated_c_str(),
+void CJX_Object::SetMapModuleString(void* pKey, WideStringView wsValue) {
+  SetMapModuleBuffer(pKey, const_cast<wchar_t*>(wsValue.unterminated_c_str()),
                      wsValue.GetLength() * sizeof(wchar_t), nullptr);
 }
 
-bool CJX_Object::SetAttribute(const WideStringView& wsAttr,
-                              const WideStringView& wsValue,
+void CJX_Object::SetAttribute(WideStringView wsAttr,
+                              WideStringView wsValue,
                               bool bNotify) {
-  XFA_Attribute attr = CXFA_Node::NameToAttribute(wsValue);
-  if (attr != XFA_Attribute::Unknown)
-    return SetAttribute(attr, wsValue, bNotify);
-
+  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
+  if (attr.has_value()) {
+    SetAttribute(attr.value().attribute, wsValue, bNotify);
+    return;
+  }
   void* pKey = GetMapKey_Custom(wsAttr);
   SetMapModuleString(pKey, wsValue);
-  return true;
 }
 
-WideString CJX_Object::GetAttribute(const WideStringView& attr) {
+WideString CJX_Object::GetAttribute(WideStringView attr) {
   return TryAttribute(attr, true).value_or(WideString());
 }
 
@@ -274,11 +274,10 @@
                                               bool bUseDefault) {
   switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
     case XFA_AttributeType::Enum: {
-      Optional<XFA_AttributeEnum> value = TryEnum(eAttr, bUseDefault);
+      Optional<XFA_AttributeValue> value = TryEnum(eAttr, bUseDefault);
       if (!value)
         return {};
-
-      return {CXFA_Node::AttributeEnumToName(*value)};
+      return WideString::FromASCII(XFA_AttributeValueToName(*value));
     }
     case XFA_AttributeType::CData:
       return TryCData(eAttr, bUseDefault);
@@ -287,20 +286,20 @@
       Optional<bool> value = TryBoolean(eAttr, bUseDefault);
       if (!value)
         return {};
-      return {*value ? L"1" : L"0"};
+      return WideString(*value ? L"1" : L"0");
     }
     case XFA_AttributeType::Integer: {
       Optional<int32_t> iValue = TryInteger(eAttr, bUseDefault);
       if (!iValue)
         return {};
-      return {WideString::Format(L"%d", *iValue)};
+      return WideString::Format(L"%d", *iValue);
     }
     case XFA_AttributeType::Measure: {
       Optional<CXFA_Measurement> value = TryMeasure(eAttr, bUseDefault);
       if (!value)
         return {};
 
-      return {value->ToString()};
+      return value->ToString();
     }
     default:
       break;
@@ -308,113 +307,99 @@
   return {};
 }
 
-Optional<WideString> CJX_Object::TryAttribute(const WideStringView& wsAttr,
+Optional<WideString> CJX_Object::TryAttribute(WideStringView wsAttr,
                                               bool bUseDefault) {
-  XFA_Attribute attr = CXFA_Node::NameToAttribute(wsAttr);
-  if (attr != XFA_Attribute::Unknown)
-    return TryAttribute(attr, bUseDefault);
-
-  void* pKey = GetMapKey_Custom(wsAttr);
-  WideStringView wsValueC;
-  if (!GetMapModuleString(pKey, wsValueC))
-    return {};
-
-  return {WideString(wsValueC)};
+  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
+  if (attr.has_value())
+    return TryAttribute(attr.value().attribute, bUseDefault);
+  return GetMapModuleString(GetMapKey_Custom(wsAttr));
 }
 
-void CJX_Object::RemoveAttribute(const WideStringView& wsAttr) {
+void CJX_Object::RemoveAttribute(WideStringView wsAttr) {
   void* pKey = GetMapKey_Custom(wsAttr);
   if (pKey)
     RemoveMapModuleKey(pKey);
 }
 
 Optional<bool> CJX_Object::TryBoolean(XFA_Attribute eAttr, bool bUseDefault) {
-  void* pValue = nullptr;
   void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  if (GetMapModuleValue(pKey, pValue))
-    return {!!pValue};
+  Optional<void*> value = GetMapModuleValue(pKey);
+  if (value.has_value())
+    return !!value.value();
   if (!bUseDefault)
     return {};
-
   return ToNode(GetXFAObject())->GetDefaultBoolean(eAttr);
 }
 
-bool CJX_Object::SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, XFA_AttributeType::Boolean,
-                                  (void*)(uintptr_t)bValue, bNotify);
-  if (elem)
-    elem->SetString(CXFA_Node::AttributeToName(eAttr), bValue ? L"1" : L"0");
-  return true;
+void CJX_Object::SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify) {
+  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)bValue, bNotify);
+  if (elem) {
+    elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
+                       bValue ? L"1" : L"0");
+  }
 }
 
 bool CJX_Object::GetBoolean(XFA_Attribute eAttr) {
   return TryBoolean(eAttr, true).value_or(false);
 }
 
-bool CJX_Object::SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, XFA_AttributeType::Integer,
-                                  (void*)(uintptr_t)iValue, bNotify);
+void CJX_Object::SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify) {
+  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)iValue, bNotify);
   if (elem) {
-    elem->SetString(CXFA_Node::AttributeToName(eAttr),
-                    WideString::Format(L"%d", iValue));
+    elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
+                       WideString::Format(L"%d", iValue));
   }
-  return true;
 }
 
-int32_t CJX_Object::GetInteger(XFA_Attribute eAttr) {
+int32_t CJX_Object::GetInteger(XFA_Attribute eAttr) const {
   return TryInteger(eAttr, true).value_or(0);
 }
 
 Optional<int32_t> CJX_Object::TryInteger(XFA_Attribute eAttr,
-                                         bool bUseDefault) {
+                                         bool bUseDefault) const {
   void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  void* pValue = nullptr;
-  if (GetMapModuleValue(pKey, pValue))
-    return {static_cast<int32_t>(reinterpret_cast<uintptr_t>(pValue))};
+  Optional<void*> value = GetMapModuleValue(pKey);
+  if (value.has_value())
+    return static_cast<int32_t>(reinterpret_cast<uintptr_t>(value.value()));
   if (!bUseDefault)
     return {};
-
   return ToNode(GetXFAObject())->GetDefaultInteger(eAttr);
 }
 
-Optional<XFA_AttributeEnum> CJX_Object::TryEnum(XFA_Attribute eAttr,
-                                                bool bUseDefault) {
+Optional<XFA_AttributeValue> CJX_Object::TryEnum(XFA_Attribute eAttr,
+                                                 bool bUseDefault) const {
   void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  void* pValue = nullptr;
-  if (GetMapModuleValue(pKey, pValue)) {
-    return {
-        static_cast<XFA_AttributeEnum>(reinterpret_cast<uintptr_t>(pValue))};
+  Optional<void*> value = GetMapModuleValue(pKey);
+  if (value.has_value()) {
+    return static_cast<XFA_AttributeValue>(
+        reinterpret_cast<uintptr_t>(value.value()));
   }
   if (!bUseDefault)
     return {};
-
   return ToNode(GetXFAObject())->GetDefaultEnum(eAttr);
 }
 
-bool CJX_Object::SetEnum(XFA_Attribute eAttr,
-                         XFA_AttributeEnum eValue,
+void CJX_Object::SetEnum(XFA_Attribute eAttr,
+                         XFA_AttributeValue eValue,
                          bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, XFA_AttributeType::Enum,
-                                  (void*)(uintptr_t)eValue, bNotify);
+  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)eValue, bNotify);
   if (elem) {
-    elem->SetString(CXFA_Node::AttributeToName(eAttr),
-                    CXFA_Node::AttributeEnumToName(eValue));
+    elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
+                       WideString::FromASCII(XFA_AttributeValueToName(eValue)));
   }
-  return true;
 }
 
-XFA_AttributeEnum CJX_Object::GetEnum(XFA_Attribute eAttr) {
-  return TryEnum(eAttr, true).value_or(XFA_AttributeEnum::Unknown);
+XFA_AttributeValue CJX_Object::GetEnum(XFA_Attribute eAttr) const {
+  return TryEnum(eAttr, true).value_or(XFA_AttributeValue::Unknown);
 }
 
-bool CJX_Object::SetMeasure(XFA_Attribute eAttr,
+void CJX_Object::SetMeasure(XFA_Attribute eAttr,
                             CXFA_Measurement mValue,
                             bool bNotify) {
   void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
   OnChanging(eAttr, bNotify);
   SetMapModuleBuffer(pKey, &mValue, sizeof(CXFA_Measurement), nullptr);
   OnChanged(eAttr, bNotify, false);
-  return true;
 }
 
 Optional<CXFA_Measurement> CJX_Object::TryMeasure(XFA_Attribute eAttr,
@@ -422,20 +407,19 @@
   void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
   void* pValue;
   int32_t iBytes;
-  if (GetMapModuleBuffer(pKey, pValue, iBytes, true) &&
+  if (GetMapModuleBuffer(pKey, &pValue, &iBytes) &&
       iBytes == sizeof(CXFA_Measurement)) {
-    return {*reinterpret_cast<CXFA_Measurement*>(pValue)};
+    return *static_cast<CXFA_Measurement*>(pValue);
   }
   if (!bUseDefault)
     return {};
-
   return ToNode(GetXFAObject())->GetDefaultMeasurement(eAttr);
 }
 
 Optional<float> CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const {
   Optional<CXFA_Measurement> measure = TryMeasure(attr, false);
   if (measure)
-    return {measure->ToUnit(XFA_Unit::Pt)};
+    return measure->ToUnit(XFA_Unit::Pt);
   return {};
 }
 
@@ -443,11 +427,16 @@
   return TryMeasure(eAttr, true).value_or(CXFA_Measurement());
 }
 
-WideString CJX_Object::GetCData(XFA_Attribute eAttr) {
+float CJX_Object::GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const {
+  auto measure = TryMeasure(eAttr, true).value_or(CXFA_Measurement());
+  return measure.ToUnit(unit);
+}
+
+WideString CJX_Object::GetCData(XFA_Attribute eAttr) const {
   return TryCData(eAttr, true).value_or(WideString());
 }
 
-bool CJX_Object::SetCData(XFA_Attribute eAttr,
+void CJX_Object::SetCData(XFA_Attribute eAttr,
                           const WideString& wsValue,
                           bool bNotify,
                           bool bScriptModify) {
@@ -466,58 +455,27 @@
 
   if (!xfaObj->IsNeedSavingXMLNode() || eAttr == XFA_Attribute::QualifiedName ||
       eAttr == XFA_Attribute::BindingNode) {
-    return true;
+    return;
   }
 
   if (eAttr == XFA_Attribute::Name &&
       (xfaObj->GetElementType() == XFA_Element::DataValue ||
        xfaObj->GetElementType() == XFA_Element::DataGroup)) {
-    return true;
+    return;
   }
 
-  auto* elem = static_cast<CFX_XMLElement*>(xfaObj->GetXMLMappingNode());
   if (eAttr == XFA_Attribute::Value) {
-    FX_XMLNODETYPE eXMLType = elem->GetType();
-    switch (eXMLType) {
-      case FX_XMLNODE_Element:
-        if (xfaObj->IsAttributeInXML()) {
-          elem->SetString(WideString(GetCData(XFA_Attribute::QualifiedName)),
-                          wsValue);
-        } else {
-          bool bDeleteChildren = true;
-          if (xfaObj->GetPacketType() == XFA_PacketType::Datasets) {
-            for (CXFA_Node* pChildDataNode = xfaObj->GetFirstChild();
-                 pChildDataNode;
-                 pChildDataNode = pChildDataNode->GetNextSibling()) {
-              if (!pChildDataNode->GetBindItems()->empty()) {
-                bDeleteChildren = false;
-                break;
-              }
-            }
-          }
-          if (bDeleteChildren)
-            elem->DeleteChildren();
-
-          elem->SetTextData(wsValue);
-        }
-        break;
-      case FX_XMLNODE_Text:
-        static_cast<CFX_XMLText*>(xfaObj->GetXMLMappingNode())
-            ->SetText(wsValue);
-        break;
-      default:
-        NOTREACHED();
-    }
-    return true;
+    xfaObj->SetToXML(wsValue);
+    return;
   }
-  ASSERT(elem->GetType() == FX_XMLNODE_Element);
 
-  WideString wsAttrName = CXFA_Node::AttributeToName(eAttr);
+  WideString wsAttrName = WideString::FromASCII(XFA_AttributeToName(eAttr));
   if (eAttr == XFA_Attribute::ContentType)
     wsAttrName = L"xfa:" + wsAttrName;
 
-  elem->SetString(wsAttrName, wsValue);
-  return true;
+  CFX_XMLElement* elem = ToXMLElement(xfaObj->GetXMLMappingNode());
+  elem->SetAttribute(wsAttrName, wsValue);
+  return;
 }
 
 void CJX_Object::SetAttributeValue(const WideString& wsValue,
@@ -525,94 +483,58 @@
                                    bool bNotify,
                                    bool bScriptModify) {
   auto* xfaObj = ToNode(GetXFAObject());
-
   void* pKey =
       GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value);
+
   OnChanging(XFA_Attribute::Value, bNotify);
   WideString* pClone = new WideString(wsValue);
+
   SetUserData(pKey, pClone, &deleteWideStringCallBack);
   OnChanged(XFA_Attribute::Value, bNotify, bScriptModify);
+
   if (!xfaObj->IsNeedSavingXMLNode())
     return;
 
-  auto* elem = static_cast<CFX_XMLElement*>(xfaObj->GetXMLMappingNode());
-  FX_XMLNODETYPE eXMLType = elem->GetType();
-  switch (eXMLType) {
-    case FX_XMLNODE_Element:
-      if (xfaObj->IsAttributeInXML()) {
-        elem->SetString(WideString(GetCData(XFA_Attribute::QualifiedName)),
-                        wsXMLValue);
-      } else {
-        bool bDeleteChildren = true;
-        if (xfaObj->GetPacketType() == XFA_PacketType::Datasets) {
-          for (CXFA_Node* pChildDataNode = xfaObj->GetFirstChild();
-               pChildDataNode;
-               pChildDataNode = pChildDataNode->GetNextSibling()) {
-            if (!pChildDataNode->GetBindItems()->empty()) {
-              bDeleteChildren = false;
-              break;
-            }
-          }
-        }
-        if (bDeleteChildren)
-          elem->DeleteChildren();
-
-        elem->SetTextData(wsXMLValue);
-      }
-      break;
-    case FX_XMLNODE_Text:
-      static_cast<CFX_XMLText*>(xfaObj->GetXMLMappingNode())
-          ->SetText(wsXMLValue);
-      break;
-    default:
-      ASSERT(0);
-  }
+  xfaObj->SetToXML(wsXMLValue);
 }
 
 Optional<WideString> CJX_Object::TryCData(XFA_Attribute eAttr,
-                                          bool bUseDefault) {
+                                          bool bUseDefault) const {
   void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
   if (eAttr == XFA_Attribute::Value) {
     void* pData;
     int32_t iBytes = 0;
     WideString* pStr = nullptr;
-    if (GetMapModuleBuffer(pKey, pData, iBytes, true) &&
-        iBytes == sizeof(void*)) {
+    if (GetMapModuleBuffer(pKey, &pData, &iBytes) && iBytes == sizeof(void*)) {
       memcpy(&pData, pData, iBytes);
       pStr = reinterpret_cast<WideString*>(pData);
     }
     if (pStr)
-      return {*pStr};
+      return *pStr;
   } else {
-    WideStringView wsValueC;
-    if (GetMapModuleString(pKey, wsValueC))
-      return {WideString(wsValueC)};
+    Optional<WideString> value = GetMapModuleString(pKey);
+    if (value.has_value())
+      return value;
   }
   if (!bUseDefault)
     return {};
-
   return ToNode(GetXFAObject())->GetDefaultCData(eAttr);
 }
 
 CFX_XMLElement* CJX_Object::SetValue(XFA_Attribute eAttr,
-                                     XFA_AttributeType eType,
                                      void* pValue,
                                      bool bNotify) {
   void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
   OnChanging(eAttr, bNotify);
   SetMapModuleValue(pKey, pValue);
   OnChanged(eAttr, bNotify, false);
-  if (!ToNode(GetXFAObject())->IsNeedSavingXMLNode())
-    return nullptr;
 
-  auto* elem =
-      static_cast<CFX_XMLElement*>(ToNode(GetXFAObject())->GetXMLMappingNode());
-  ASSERT(elem->GetType() == FX_XMLNODE_Element);
-
-  return elem;
+  CXFA_Node* pNode = ToNode(GetXFAObject());
+  return pNode->IsNeedSavingXMLNode() ? ToXMLElement(pNode->GetXMLMappingNode())
+                                      : nullptr;
 }
 
-bool CJX_Object::SetContent(const WideString& wsContent,
+void CJX_Object::SetContent(const WideString& wsContent,
                             const WideString& wsXMLValue,
                             bool bNotify,
                             bool bScriptModify,
@@ -628,71 +550,50 @@
           break;
 
         CXFA_Node* pChildValue = pValue->GetFirstChild();
-        ASSERT(pChildValue);
         pChildValue->JSObject()->SetCData(XFA_Attribute::ContentType,
                                           L"text/xml", false, false);
         pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                             bScriptModify, false);
+
         CXFA_Node* pBind = ToNode(GetXFAObject())->GetBindData();
         if (bSyncData && pBind) {
-          std::vector<WideString> wsSaveTextArray;
-          size_t iSize = 0;
-          if (!wsContent.IsEmpty()) {
-            size_t iStart = 0;
-            size_t iLength = wsContent.GetLength();
-            auto iEnd = wsContent.Find(L'\n', iStart);
-            iEnd = !iEnd.has_value() ? iLength : iEnd;
-            while (iEnd.value() >= iStart) {
-              wsSaveTextArray.push_back(
-                  wsContent.Mid(iStart, iEnd.value() - iStart));
-              iStart = iEnd.value() + 1;
-              if (iStart >= iLength)
-                break;
+          std::vector<WideString> wsSaveTextArray =
+              fxcrt::Split(wsContent, L'\n');
+          std::vector<CXFA_Node*> valueNodes =
+              pBind->GetNodeListForType(XFA_Element::DataValue);
 
-              iEnd = wsContent.Find(L'\n', iStart);
-              if (!iEnd.has_value()) {
-                wsSaveTextArray.push_back(
-                    wsContent.Mid(iStart, iLength - iStart));
-              }
-            }
-            iSize = wsSaveTextArray.size();
-          }
-          if (iSize == 0) {
-            while (CXFA_Node* pChildNode = pBind->GetFirstChild()) {
-              pBind->RemoveChild(pChildNode, true);
-            }
-          } else {
-            std::vector<CXFA_Node*> valueNodes = pBind->GetNodeList(
-                XFA_NODEFILTER_Children, XFA_Element::DataValue);
-            size_t iDatas = valueNodes.size();
-            if (iDatas < iSize) {
-              size_t iAddNodes = iSize - iDatas;
-              CXFA_Node* pValueNodes = nullptr;
+          // Adusting node count might have side effects, do not trust that
+          // we'll ever actually get there.
+          size_t tries = 0;
+          while (valueNodes.size() != wsSaveTextArray.size()) {
+            if (++tries > 4)
+              return;
+            if (valueNodes.size() < wsSaveTextArray.size()) {
+              size_t iAddNodes = wsSaveTextArray.size() - valueNodes.size();
               while (iAddNodes-- > 0) {
-                pValueNodes =
+                CXFA_Node* pValueNodes =
                     pBind->CreateSamePacketNode(XFA_Element::DataValue);
                 pValueNodes->JSObject()->SetCData(XFA_Attribute::Name, L"value",
                                                   false, false);
                 pValueNodes->CreateXMLMappingNode();
-                pBind->InsertChild(pValueNodes, nullptr);
+                pBind->InsertChildAndNotify(pValueNodes, nullptr);
               }
-              pValueNodes = nullptr;
-            } else if (iDatas > iSize) {
-              size_t iDelNodes = iDatas - iSize;
-              while (iDelNodes-- > 0) {
-                pBind->RemoveChild(pBind->GetFirstChild(), true);
-              }
+            } else {
+              size_t iDelNodes = valueNodes.size() - wsSaveTextArray.size();
+              for (size_t i = 0; i < iDelNodes; ++i)
+                pBind->RemoveChildAndNotify(valueNodes[i], true);
             }
-            int32_t i = 0;
-            for (CXFA_Node* pValueNode = pBind->GetFirstChild(); pValueNode;
-                 pValueNode = pValueNode->GetNextSibling()) {
-              pValueNode->JSObject()->SetAttributeValue(
-                  wsSaveTextArray[i], wsSaveTextArray[i], false, false);
-              i++;
-            }
+            valueNodes = pBind->GetNodeListForType(XFA_Element::DataValue);
           }
-          for (const auto& pArrayNode : *(pBind->GetBindItems())) {
-            if (pArrayNode.Get() != ToNode(GetXFAObject())) {
+          ASSERT(valueNodes.size() == wsSaveTextArray.size());
+          size_t i = 0;
+          for (CXFA_Node* pValueNode : valueNodes) {
+            pValueNode->JSObject()->SetAttributeValue(
+                wsSaveTextArray[i], wsSaveTextArray[i], false, false);
+            i++;
+          }
+          for (auto* pArrayNode : pBind->GetBindItemsCopy()) {
+            if (pArrayNode != ToNode(GetXFAObject())) {
               pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                                  bScriptModify, false);
             }
@@ -717,8 +618,8 @@
       if (pBindNode && bSyncData) {
         pBindNode->JSObject()->SetContent(wsContent, wsXMLValue, bNotify,
                                           bScriptModify, false);
-        for (const auto& pArrayNode : *(pBindNode->GetBindItems())) {
-          if (pArrayNode.Get() != ToNode(GetXFAObject())) {
+        for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) {
+          if (pArrayNode != ToNode(GetXFAObject())) {
             pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                                true, false);
           }
@@ -734,8 +635,8 @@
             TryAttribute(XFA_Attribute::ContentType, false);
         if (ret)
           wsContentType = *ret;
-        if (wsContentType == L"text/html") {
-          wsContentType = L"";
+        if (wsContentType.EqualsASCII("text/html")) {
+          wsContentType.clear();
           SetAttribute(XFA_Attribute::ContentType, wsContentType.AsStringView(),
                        false);
         }
@@ -745,13 +646,15 @@
       if (!pContentRawDataNode) {
         pContentRawDataNode =
             ToNode(GetXFAObject())
-                ->CreateSamePacketNode((wsContentType == L"text/xml")
+                ->CreateSamePacketNode(wsContentType.EqualsASCII("text/xml")
                                            ? XFA_Element::Sharpxml
                                            : XFA_Element::Sharptext);
-        ToNode(GetXFAObject())->InsertChild(pContentRawDataNode, nullptr);
+        ToNode(GetXFAObject())
+            ->InsertChildAndNotify(pContentRawDataNode, nullptr);
       }
-      return pContentRawDataNode->JSObject()->SetContent(
+      pContentRawDataNode->JSObject()->SetContent(
           wsContent, wsXMLValue, bNotify, bScriptModify, bSyncData);
+      return;
     }
     case XFA_ObjectType::NodeC:
     case XFA_ObjectType::TextNode:
@@ -785,16 +688,15 @@
       break;
   }
   if (!pNode)
-    return false;
+    return;
 
   SetAttributeValue(wsContent, wsXMLValue, bNotify, bScriptModify);
   if (pBindNode && bSyncData) {
-    for (const auto& pArrayNode : *(pBindNode->GetBindItems())) {
+    for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) {
       pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                          bScriptModify, false);
     }
   }
-  return true;
 }
 
 WideString CJX_Object::GetContent(bool bScriptModify) {
@@ -831,16 +733,17 @@
         if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExData) {
           Optional<WideString> contentType =
               TryAttribute(XFA_Attribute::ContentType, false);
-          if (contentType) {
-            if (*contentType == L"text/html")
+          if (contentType.has_value()) {
+            if (contentType.value().EqualsASCII("text/html"))
               element = XFA_Element::SharpxHTML;
-            else if (*contentType == L"text/xml")
+            else if (contentType.value().EqualsASCII("text/xml"))
               element = XFA_Element::Sharpxml;
           }
         }
         pContentRawDataNode =
             ToNode(GetXFAObject())->CreateSamePacketNode(element);
-        ToNode(GetXFAObject())->InsertChild(pContentRawDataNode, nullptr);
+        ToNode(GetXFAObject())
+            ->InsertChildAndNotify(pContentRawDataNode, nullptr);
       }
       return pContentRawDataNode->JSObject()->TryContent(bScriptModify, true);
     }
@@ -848,6 +751,7 @@
     case XFA_ObjectType::NodeV:
     case XFA_ObjectType::TextNode:
       pNode = ToNode(GetXFAObject());
+      FALLTHROUGH;
     default:
       if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue)
         pNode = ToNode(GetXFAObject());
@@ -856,9 +760,7 @@
   if (pNode) {
     if (bScriptModify) {
       CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-      if (pScriptContext)
-        GetDocument()->GetScriptContext()->AddNodesOfRunScript(
-            ToNode(GetXFAObject()));
+      pScriptContext->AddNodesOfRunScript(ToNode(GetXFAObject()));
     }
     return TryCData(XFA_Attribute::Value, false);
   }
@@ -869,91 +771,50 @@
   if (ToNode(GetXFAObject())->IsModelNode() ||
       ToNode(GetXFAObject())->GetElementType() == XFA_Element::Packet) {
     CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode();
-    if (!pXMLNode || pXMLNode->GetType() != FX_XMLNODE_Element)
+    CFX_XMLElement* element = ToXMLElement(pXMLNode);
+    if (!element)
       return {};
 
-    return {static_cast<CFX_XMLElement*>(pXMLNode)->GetNamespaceURI()};
+    return element->GetNamespaceURI();
   }
 
   if (ToNode(GetXFAObject())->GetPacketType() != XFA_PacketType::Datasets)
     return ToNode(GetXFAObject())->GetModelNode()->JSObject()->TryNamespace();
 
   CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode();
-  if (!pXMLNode || pXMLNode->GetType() != FX_XMLNODE_Element)
+  CFX_XMLElement* element = ToXMLElement(pXMLNode);
+  if (!element)
     return {};
 
   if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue &&
-      GetEnum(XFA_Attribute::Contains) == XFA_AttributeEnum::MetaData) {
+      GetEnum(XFA_Attribute::Contains) == XFA_AttributeValue::MetaData) {
     WideString wsNamespace;
-    bool ret = XFA_FDEExtension_ResolveNamespaceQualifier(
-        static_cast<CFX_XMLElement*>(pXMLNode),
-        GetCData(XFA_Attribute::QualifiedName), &wsNamespace);
-    if (!ret)
+    if (!XFA_FDEExtension_ResolveNamespaceQualifier(
+            element, GetCData(XFA_Attribute::QualifiedName), &wsNamespace)) {
       return {};
-    return {wsNamespace};
+    }
+    return wsNamespace;
   }
-  return {static_cast<CFX_XMLElement*>(pXMLNode)->GetNamespaceURI()};
+  return element->GetNamespaceURI();
 }
 
 std::pair<CXFA_Node*, int32_t> CJX_Object::GetPropertyInternal(
     int32_t index,
     XFA_Element eProperty) const {
-  const CXFA_Node* xfaNode = ToNode(GetXFAObject());
-  if (index < 0 || index >= xfaNode->PropertyOccuranceCount(eProperty))
-    return {nullptr, 0};
-
-  int32_t iCount = 0;
-  for (CXFA_Node* pNode = xfaNode->GetFirstChild(); pNode;
-       pNode = pNode->GetNextSibling()) {
-    if (pNode->GetElementType() == eProperty) {
-      iCount++;
-      if (iCount > index)
-        return {pNode, iCount};
-    }
-  }
-  return {nullptr, iCount};
+  return ToNode(GetXFAObject())->GetProperty(index, eProperty);
 }
 
 CXFA_Node* CJX_Object::GetOrCreatePropertyInternal(int32_t index,
                                                    XFA_Element eProperty) {
-  CXFA_Node* xfaNode = ToNode(GetXFAObject());
-  if (index < 0 || index >= xfaNode->PropertyOccuranceCount(eProperty))
-    return nullptr;
-
-  int32_t iCount = 0;
-  CXFA_Node* node;
-  std::tie(node, iCount) = GetPropertyInternal(index, eProperty);
-  if (node)
-    return node;
-
-  if (xfaNode->HasPropertyFlags(eProperty, XFA_PROPERTYFLAG_OneOf)) {
-    for (CXFA_Node* pNode = xfaNode->GetFirstChild(); pNode;
-         pNode = pNode->GetNextSibling()) {
-      if (xfaNode->HasPropertyFlags(pNode->GetElementType(),
-                                    XFA_PROPERTYFLAG_OneOf)) {
-        return nullptr;
-      }
-    }
-  }
-
-  CXFA_Node* pNewNode = nullptr;
-  for (; iCount <= index; ++iCount) {
-    pNewNode = GetDocument()->CreateNode(xfaNode->GetPacketType(), eProperty);
-    if (!pNewNode)
-      return nullptr;
-
-    xfaNode->InsertChild(pNewNode, nullptr);
-    pNewNode->SetFlag(XFA_NodeFlag_Initialized, true);
-  }
-  return pNewNode;
+  return ToNode(GetXFAObject())->GetOrCreateProperty(index, eProperty);
 }
 
-bool CJX_Object::SetUserData(void* pKey,
-                             void* pData,
-                             XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
-  SetMapModuleBuffer(pKey, &pData, sizeof(void*),
-                     pCallbackInfo ? pCallbackInfo : &gs_XFADefaultFreeData);
-  return true;
+void CJX_Object::SetUserData(
+    void* pKey,
+    void* pData,
+    const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
+  ASSERT(pCallbackInfo);
+  SetMapModuleBuffer(pKey, &pData, sizeof(void*), pCallbackInfo);
 }
 
 XFA_MAPMODULEDATA* CJX_Object::CreateMapModuleData() {
@@ -970,40 +831,42 @@
   CreateMapModuleData()->m_ValueMap[pKey] = pValue;
 }
 
-bool CJX_Object::GetMapModuleValue(void* pKey, void*& pValue) {
-  for (CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
+Optional<void*> CJX_Object::GetMapModuleValue(void* pKey) const {
+  std::set<const CXFA_Node*> visited;
+  for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
        pNode = pNode->GetTemplateNodeIfExists()) {
+    if (!visited.insert(pNode).second)
+      break;
+
     XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData();
     if (pModule) {
       auto it = pModule->m_ValueMap.find(pKey);
-      if (it != pModule->m_ValueMap.end()) {
-        pValue = it->second;
-        return true;
-      }
+      if (it != pModule->m_ValueMap.end())
+        return it->second;
     }
     if (pNode->GetPacketType() == XFA_PacketType::Datasets)
       break;
   }
-  return false;
+  return {};
 }
 
-bool CJX_Object::GetMapModuleString(void* pKey, WideStringView& wsValue) {
-  void* pValue;
+Optional<WideString> CJX_Object::GetMapModuleString(void* pKey) const {
+  void* pRawValue;
   int32_t iBytes;
-  if (!GetMapModuleBuffer(pKey, pValue, iBytes, true))
-    return false;
+  if (!GetMapModuleBuffer(pKey, &pRawValue, &iBytes))
+    return {};
 
   // Defensive measure: no out-of-bounds pointers even if zero length.
   int32_t iChars = iBytes / sizeof(wchar_t);
-  wsValue = WideStringView(iChars ? (const wchar_t*)pValue : nullptr, iChars);
-  return true;
+  return WideString(iChars ? static_cast<const wchar_t*>(pRawValue) : nullptr,
+                    iChars);
 }
 
 void CJX_Object::SetMapModuleBuffer(
     void* pKey,
     void* pValue,
     int32_t iBytes,
-    XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
+    const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
   XFA_MAPDATABLOCK*& pBuffer = CreateMapModuleData()->m_BufferMap[pKey];
   if (!pBuffer) {
     pBuffer = reinterpret_cast<XFA_MAPDATABLOCK*>(
@@ -1027,12 +890,15 @@
 }
 
 bool CJX_Object::GetMapModuleBuffer(void* pKey,
-                                    void*& pValue,
-                                    int32_t& iBytes,
-                                    bool bProtoAlso) const {
+                                    void** pValue,
+                                    int32_t* pBytes) const {
+  std::set<const CXFA_Node*> visited;
   XFA_MAPDATABLOCK* pBuffer = nullptr;
   for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
        pNode = pNode->GetTemplateNodeIfExists()) {
+    if (!visited.insert(pNode).second)
+      break;
+
     XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData();
     if (pModule) {
       auto it = pModule->m_BufferMap.find(pKey);
@@ -1041,14 +907,14 @@
         break;
       }
     }
-    if (!bProtoAlso || pNode->GetPacketType() == XFA_PacketType::Datasets)
+    if (pNode->GetPacketType() == XFA_PacketType::Datasets)
       break;
   }
   if (!pBuffer)
     return false;
 
-  pValue = pBuffer->GetData();
-  iBytes = pBuffer->iBytes;
+  *pValue = pBuffer->GetData();
+  *pBytes = pBuffer->iBytes;
   return true;
 }
 
@@ -1158,9 +1024,9 @@
 
   WideString wsValue = ToNode(pDstModule)->JSObject()->GetContent(false);
   WideString wsFormatValue(wsValue);
-  CXFA_WidgetAcc* pWidgetAcc = ToNode(pDstModule)->GetContainerWidgetAcc();
-  if (pWidgetAcc)
-    wsFormatValue = pWidgetAcc->GetFormatDataValue(wsValue);
+  CXFA_Node* pNode = ToNode(pDstModule)->GetContainerNode();
+  if (pNode)
+    wsFormatValue = pNode->GetFormatDataValue(wsValue);
 
   ToNode(pDstModule)
       ->JSObject()
@@ -1207,11 +1073,11 @@
   return std::move(calc_data_);
 }
 
-void CJX_Object::Script_Attribute_String(CFXJSE_Value* pValue,
-                                         bool bSetting,
-                                         XFA_Attribute eAttribute) {
+void CJX_Object::ScriptAttributeString(CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute eAttribute) {
   if (!bSetting) {
-    pValue->SetString(GetAttribute(eAttribute).UTF8Encode().AsStringView());
+    pValue->SetString(GetAttribute(eAttribute).ToUTF8().AsStringView());
     return;
   }
 
@@ -1224,29 +1090,31 @@
 
   CXFA_Node* pTemplateNode =
       ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Template));
+  CXFA_Subform* pSubForm =
+      pTemplateNode->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
   CXFA_Proto* pProtoRoot =
-      pTemplateNode->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform)
-          ->GetFirstChildByClass<CXFA_Proto>(XFA_Element::Proto);
+      pSubForm ? pSubForm->GetFirstChildByClass<CXFA_Proto>(XFA_Element::Proto)
+               : nullptr;
 
   WideString wsID;
   WideString wsSOM;
   if (!wsValue.IsEmpty()) {
     if (wsValue[0] == '#')
-      wsID = WideString(wsValue.c_str() + 1, wsValue.GetLength() - 1);
+      wsID = wsValue.Substr(1, wsValue.GetLength() - 1);
     else
-      wsSOM = wsValue;
+      wsSOM = std::move(wsValue);
   }
 
   CXFA_Node* pProtoNode = nullptr;
   if (!wsSOM.IsEmpty()) {
     XFA_RESOLVENODE_RS resolveNodeRS;
-    bool iRet = GetDocument()->GetScriptContext()->ResolveObjects(
+    bool bRet = GetDocument()->GetScriptContext()->ResolveObjects(
         pProtoRoot, wsSOM.AsStringView(), &resolveNodeRS,
         XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
             XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
             XFA_RESOLVENODE_Siblings,
         nullptr);
-    if (iRet && resolveNodeRS.objects.front()->IsNode())
+    if (bRet && resolveNodeRS.objects.front()->IsNode())
       pProtoNode = resolveNodeRS.objects.front()->AsNode();
 
   } else if (!wsID.IsEmpty()) {
@@ -1258,25 +1126,23 @@
   CXFA_Node* pHeadChild = ToNode(GetXFAObject())->GetFirstChild();
   while (pHeadChild) {
     CXFA_Node* pSibling = pHeadChild->GetNextSibling();
-    ToNode(GetXFAObject())->RemoveChild(pHeadChild, true);
+    ToNode(GetXFAObject())->RemoveChildAndNotify(pHeadChild, true);
     pHeadChild = pSibling;
   }
 
-  std::unique_ptr<CXFA_Node> pProtoForm(pProtoNode->CloneTemplateToForm(true));
+  CXFA_Node* pProtoForm = pProtoNode->CloneTemplateToForm(true);
   pHeadChild = pProtoForm->GetFirstChild();
   while (pHeadChild) {
     CXFA_Node* pSibling = pHeadChild->GetNextSibling();
-    pProtoForm->RemoveChild(pHeadChild, true);
-    ToNode(GetXFAObject())->InsertChild(pHeadChild, nullptr);
+    pProtoForm->RemoveChildAndNotify(pHeadChild, true);
+    ToNode(GetXFAObject())->InsertChildAndNotify(pHeadChild, nullptr);
     pHeadChild = pSibling;
   }
-
-  GetDocument()->RemovePurgeNode(pProtoForm.get());
 }
 
-void CJX_Object::Script_Attribute_BOOL(CFXJSE_Value* pValue,
-                                       bool bSetting,
-                                       XFA_Attribute eAttribute) {
+void CJX_Object::ScriptAttributeBool(CFXJSE_Value* pValue,
+                                     bool bSetting,
+                                     XFA_Attribute eAttribute) {
   if (bSetting) {
     SetBoolean(eAttribute, pValue->ToBoolean(), true);
     return;
@@ -1284,9 +1150,9 @@
   pValue->SetString(GetBoolean(eAttribute) ? "1" : "0");
 }
 
-void CJX_Object::Script_Attribute_Integer(CFXJSE_Value* pValue,
-                                          bool bSetting,
-                                          XFA_Attribute eAttribute) {
+void CJX_Object::ScriptAttributeInteger(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {
   if (bSetting) {
     SetInteger(eAttribute, pValue->ToInteger(), true);
     return;
@@ -1294,9 +1160,9 @@
   pValue->SetInteger(GetInteger(eAttribute));
 }
 
-void CJX_Object::Script_Som_FontColor(CFXJSE_Value* pValue,
-                                      bool bSetting,
-                                      XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomFontColor(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
   CXFA_Font* font = ToNode(object_.Get())->GetOrCreateFontIfPossible();
   if (!font)
     return;
@@ -1319,9 +1185,9 @@
   pValue->SetString(ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
 }
 
-void CJX_Object::Script_Som_FillColor(CFXJSE_Value* pValue,
-                                      bool bSetting,
-                                      XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomFillColor(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
   CXFA_Fill* borderfill = border->GetOrCreateFillIfPossible();
   if (!borderfill)
@@ -1344,12 +1210,12 @@
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(color);
   pValue->SetString(
-      WideString::Format(L"%d,%d,%d", r, g, b).UTF8Encode().AsStringView());
+      WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView());
 }
 
-void CJX_Object::Script_Som_BorderColor(CFXJSE_Value* pValue,
-                                        bool bSetting,
-                                        XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomBorderColor(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
   int32_t iSize = border->CountEdges();
   if (bSetting) {
@@ -1375,21 +1241,24 @@
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(color);
   pValue->SetString(
-      WideString::Format(L"%d,%d,%d", r, g, b).UTF8Encode().AsStringView());
+      WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView());
 }
 
-void CJX_Object::Script_Som_BorderWidth(CFXJSE_Value* pValue,
-                                        bool bSetting,
-                                        XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomBorderWidth(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
   if (bSetting) {
     CXFA_Edge* edge = border->GetEdgeIfExists(0);
     CXFA_Measurement thickness =
         edge ? edge->GetMSThickness() : CXFA_Measurement(0.5, XFA_Unit::Pt);
-    pValue->SetString(thickness.ToString().UTF8Encode().AsStringView());
+    pValue->SetString(thickness.ToString().ToUTF8().AsStringView());
     return;
   }
 
+  if (pValue->IsEmpty())
+    return;
+
   WideString wsThickness = pValue->ToWideString();
   for (int32_t i = 0; i < border->CountEdges(); ++i) {
     CXFA_Edge* edge = border->GetEdgeIfExists(i);
@@ -1398,9 +1267,9 @@
   }
 }
 
-void CJX_Object::Script_Som_Message(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_SOM_MESSAGETYPE iMessageType) {
+void CJX_Object::ScriptSomMessage(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_SOM_MESSAGETYPE iMessageType) {
   bool bNew = false;
   CXFA_Validate* validate = ToNode(object_.Get())->GetValidateIfExists();
   if (!validate) {
@@ -1455,39 +1324,24 @@
     default:
       break;
   }
-  pValue->SetString(wsMessage.UTF8Encode().AsStringView());
+  pValue->SetString(wsMessage.ToUTF8().AsStringView());
 }
 
-void CJX_Object::Script_Som_ValidationMessage(CFXJSE_Value* pValue,
-                                              bool bSetting,
-                                              XFA_Attribute eAttribute) {
-  Script_Som_Message(pValue, bSetting, XFA_SOM_ValidationMessage);
+void CJX_Object::ScriptSomValidationMessage(CFXJSE_Value* pValue,
+                                            bool bSetting,
+                                            XFA_Attribute eAttribute) {
+  ScriptSomMessage(pValue, bSetting, XFA_SOM_ValidationMessage);
 }
 
-void CJX_Object::Script_Som_MandatoryMessage(CFXJSE_Value* pValue,
-                                             bool bSetting,
-                                             XFA_Attribute eAttribute) {
-  Script_Som_Message(pValue, bSetting, XFA_SOM_MandatoryMessage);
+void CJX_Object::ScriptSomMandatoryMessage(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {
+  ScriptSomMessage(pValue, bSetting, XFA_SOM_MandatoryMessage);
 }
 
-void CJX_Object::Script_Field_Length(CFXJSE_Value* pValue,
-                                     bool bSetting,
-                                     XFA_Attribute eAttribute) {
-  if (bSetting) {
-    ThrowInvalidPropertyException();
-    return;
-  }
-  if (!ToNode(object_.Get())->GetWidgetAcc()) {
-    pValue->SetInteger(0);
-    return;
-  }
-  pValue->SetInteger(
-      ToNode(object_.Get())->GetWidgetAcc()->CountChoiceListItems(true));
-}
-
-void CJX_Object::Script_Som_DefaultValue(CFXJSE_Value* pValue,
-                                         bool bSetting,
-                                         XFA_Attribute /* unused */) {
+void CJX_Object::ScriptSomDefaultValue(CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute /* unused */) {
   XFA_Element eType = ToNode(GetXFAObject())->GetElementType();
 
   // TODO(dsinclair): This should look through the properties on the node to see
@@ -1511,34 +1365,36 @@
 
   if (bSetting) {
     WideString wsNewValue;
-    if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
+    if (pValue &&
+        !(pValue->IsEmpty() || pValue->IsNull() || pValue->IsUndefined())) {
       wsNewValue = pValue->ToWideString();
+    }
 
     WideString wsFormatValue(wsNewValue);
-    CXFA_WidgetAcc* pContainerWidgetAcc = nullptr;
+    CXFA_Node* pContainerNode = nullptr;
     if (ToNode(GetXFAObject())->GetPacketType() == XFA_PacketType::Datasets) {
       WideString wsPicture;
-      for (const auto& pFormNode : *(ToNode(GetXFAObject())->GetBindItems())) {
+      for (auto* pFormNode : ToNode(GetXFAObject())->GetBindItemsCopy()) {
         if (!pFormNode || pFormNode->HasRemovedChildren())
           continue;
 
-        pContainerWidgetAcc = pFormNode->GetContainerWidgetAcc();
-        if (pContainerWidgetAcc) {
+        pContainerNode = pFormNode->GetContainerNode();
+        if (pContainerNode) {
           wsPicture =
-              pContainerWidgetAcc->GetPictureContent(XFA_VALUEPICTURE_DataBind);
+              pContainerNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
         }
         if (!wsPicture.IsEmpty())
           break;
 
-        pContainerWidgetAcc = nullptr;
+        pContainerNode = nullptr;
       }
     } else if (ToNode(GetXFAObject())->GetPacketType() ==
                XFA_PacketType::Form) {
-      pContainerWidgetAcc = ToNode(GetXFAObject())->GetContainerWidgetAcc();
+      pContainerNode = ToNode(GetXFAObject())->GetContainerNode();
     }
 
-    if (pContainerWidgetAcc)
-      wsFormatValue = pContainerWidgetAcc->GetFormatDataValue(wsNewValue);
+    if (pContainerNode)
+      wsFormatValue = pContainerNode->GetFormatDataValue(wsNewValue);
 
     SetContent(wsNewValue, wsFormatValue, true, true, true);
     return;
@@ -1551,16 +1407,16 @@
   } else if (eType == XFA_Element::Integer) {
     pValue->SetInteger(FXSYS_wtoi(content.c_str()));
   } else if (eType == XFA_Element::Float || eType == XFA_Element::Decimal) {
-    CFX_Decimal decimal(content.AsStringView());
-    pValue->SetFloat((float)(double)decimal);
+    CFGAS_Decimal decimal(content.AsStringView());
+    pValue->SetFloat(decimal.ToFloat());
   } else {
-    pValue->SetString(content.UTF8Encode().AsStringView());
+    pValue->SetString(content.ToUTF8().AsStringView());
   }
 }
 
-void CJX_Object::Script_Som_DefaultValue_Read(CFXJSE_Value* pValue,
-                                              bool bSetting,
-                                              XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomDefaultValue_Read(CFXJSE_Value* pValue,
+                                            bool bSetting,
+                                            XFA_Attribute eAttribute) {
   if (bSetting) {
     ThrowInvalidPropertyException();
     return;
@@ -1571,12 +1427,12 @@
     pValue->SetNull();
     return;
   }
-  pValue->SetString(content.UTF8Encode().AsStringView());
+  pValue->SetString(content.ToUTF8().AsStringView());
 }
 
-void CJX_Object::Script_Som_DataNode(CFXJSE_Value* pValue,
-                                     bool bSetting,
-                                     XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomDataNode(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
   if (bSetting) {
     ThrowInvalidPropertyException();
     return;
@@ -1588,13 +1444,13 @@
     return;
   }
 
-  pValue->Assign(
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pDataNode));
+  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+      pDataNode));
 }
 
-void CJX_Object::Script_Som_Mandatory(CFXJSE_Value* pValue,
-                                      bool bSetting,
-                                      XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomMandatory(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
   CXFA_Validate* validate =
       ToNode(object_.Get())->GetOrCreateValidateIfPossible();
   if (!validate)
@@ -1605,13 +1461,12 @@
     return;
   }
 
-  WideString str = CXFA_Node::AttributeEnumToName(validate->GetNullTest());
-  pValue->SetString(str.UTF8Encode().AsStringView());
+  pValue->SetString(XFA_AttributeValueToName(validate->GetNullTest()));
 }
 
-void CJX_Object::Script_Som_InstanceIndex(CFXJSE_Value* pValue,
-                                          bool bSetting,
-                                          XFA_Attribute eAttribute) {
+void CJX_Object::ScriptSomInstanceIndex(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {
   if (!bSetting) {
     pValue->SetInteger(Subform_and_SubformSet_InstanceIndex());
     return;
@@ -1648,57 +1503,6 @@
   }
 }
 
-void CJX_Object::Script_Subform_InstanceManager(CFXJSE_Value* pValue,
-                                                bool bSetting,
-                                                XFA_AttributeEnum eAttribute) {
-  if (bSetting) {
-    ThrowInvalidPropertyException();
-    return;
-  }
-
-  WideString wsName = GetCData(XFA_Attribute::Name);
-  CXFA_Node* pInstanceMgr = nullptr;
-  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
-       pNode = pNode->GetPrevSibling()) {
-    if (pNode->GetElementType() == XFA_Element::InstanceManager) {
-      WideString wsInstMgrName =
-          pNode->JSObject()->GetCData(XFA_Attribute::Name);
-      if (wsInstMgrName.GetLength() >= 1 && wsInstMgrName[0] == '_' &&
-          wsInstMgrName.Right(wsInstMgrName.GetLength() - 1) == wsName) {
-        pInstanceMgr = pNode;
-      }
-      break;
-    }
-  }
-  if (!pInstanceMgr) {
-    pValue->SetNull();
-    return;
-  }
-
-  pValue->Assign(
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pInstanceMgr));
-}
-
-void CJX_Object::Script_SubmitFormat_Mode(CFXJSE_Value* pValue,
-                                          bool bSetting,
-                                          XFA_Attribute eAttribute) {}
-
-void CJX_Object::Script_Form_Checksum(CFXJSE_Value* pValue,
-                                      bool bSetting,
-                                      XFA_Attribute eAttribute) {
-  if (bSetting) {
-    SetAttribute(XFA_Attribute::Checksum, pValue->ToWideString().AsStringView(),
-                 false);
-    return;
-  }
-
-  Optional<WideString> checksum = TryAttribute(XFA_Attribute::Checksum, false);
-  pValue->SetString(checksum ? checksum->UTF8Encode().AsStringView() : "");
-}
-
-void CJX_Object::Script_ExclGroup_ErrorText(CFXJSE_Value* pValue,
-                                            bool bSetting,
-                                            XFA_Attribute eAttribute) {
-  if (bSetting)
-    ThrowInvalidPropertyException();
-}
+void CJX_Object::ScriptSubmitFormatMode(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h
index c252b45..0633b5f 100644
--- a/fxjs/xfa/cjx_object.h
+++ b/fxjs/xfa/cjx_object.h
@@ -14,13 +14,16 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/widestring.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/jse_define.h"
 #include "third_party/base/optional.h"
+#include "third_party/base/span.h"
 #include "xfa/fxfa/fxfa_basic.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
 
+class CFX_XMLElement;
 class CFXJSE_Value;
-class CJS_V8;
+class CFX_V8;
+class CJX_Object;
 class CXFA_CalcData;
 class CXFA_Document;
 class CXFA_LayoutItem;
@@ -28,10 +31,11 @@
 class CXFA_Object;
 struct XFA_MAPMODULEDATA;
 
-typedef CJS_Return (*CJX_MethodCall)(
+typedef CJS_Result (*CJX_MethodCall)(
     CJX_Object* obj,
-    CJS_V8* runtime,
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params);
+
 struct CJX_MethodSpec {
   const char* pName;
   CJX_MethodCall pMethodCall;
@@ -53,42 +57,78 @@
 
 class CJX_Object {
  public:
+  // Corresponds 1:1 with CJX_ subclasses.
+  enum class TypeTag {
+    Boolean,
+    Container,
+    DataWindow,
+    Delta,
+    Desc,
+    Draw,
+    Encrypt,
+    EventPseudoModel,
+    ExclGroup,
+    Extras,
+    Field,
+    Form,
+    Handler,
+    HostPseudoModel,
+    InstanceManager,
+    LayoutPseudoModel,
+    List,
+    LogPseudoModel,
+    Manifest,
+    Model,
+    Node,
+    Object,
+    Occur,
+    Packet,
+    Script,
+    SignaturePesudoModel,
+    Source,
+    Subform,
+    SubformSet,
+    Template,
+    TextNode,
+    Tree,
+    TreeList,
+    WsdlConnection,
+    Xfa,
+  };
+
   explicit CJX_Object(CXFA_Object* obj);
   virtual ~CJX_Object();
 
-  JS_PROP(className);
+  virtual bool DynamicTypeIs(TypeTag eType) const;
 
-  CXFA_Object* GetXFAObject() { return object_.Get(); }
-  const CXFA_Object* GetXFAObject() const { return object_.Get(); }
+  JSE_PROP(className);
 
   CXFA_Document* GetDocument() const;
+  CXFA_Object* GetXFAObject() const { return object_.Get(); }
 
   void SetCalcRecursionCount(size_t count) { calc_recursion_count_ = count; }
   size_t GetCalcRecursionCount() const { return calc_recursion_count_; }
 
-  void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_ = item; }
+  void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_.Reset(item); }
   CXFA_LayoutItem* GetLayoutItem() const { return layout_item_.Get(); }
 
   bool HasMethod(const WideString& func) const;
-  CJS_Return RunMethod(const WideString& func,
+  CJS_Result RunMethod(const WideString& func,
                        const std::vector<v8::Local<v8::Value>>& params);
 
   bool HasAttribute(XFA_Attribute eAttr);
-  bool SetAttribute(XFA_Attribute eAttr,
-                    const WideStringView& wsValue,
+  void SetAttribute(XFA_Attribute eAttr, WideStringView wsValue, bool bNotify);
+  void SetAttribute(WideStringView wsAttr,
+                    WideStringView wsValue,
                     bool bNotify);
-  bool SetAttribute(const WideStringView& wsAttr,
-                    const WideStringView& wsValue,
-                    bool bNotify);
-  void RemoveAttribute(const WideStringView& wsAttr);
-  WideString GetAttribute(const WideStringView& attr);
+  void RemoveAttribute(WideStringView wsAttr);
+  WideString GetAttribute(WideStringView attr);
   WideString GetAttribute(XFA_Attribute attr);
-  Optional<WideString> TryAttribute(const WideStringView& wsAttr,
-                                    bool bUseDefault);
+  Optional<WideString> TryAttribute(WideStringView wsAttr, bool bUseDefault);
   Optional<WideString> TryAttribute(XFA_Attribute eAttr, bool bUseDefault);
 
   Optional<WideString> TryContent(bool bScriptModify, bool bProto);
-  bool SetContent(const WideString& wsContent,
+  void SetContent(const WideString& wsContent,
                   const WideString& wsXMLValue,
                   bool bNotify,
                   bool bScriptModify,
@@ -112,94 +152,56 @@
                          bool bNotify,
                          bool bScriptModify);
 
-  void Script_Attribute_String(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute);
-  void Script_Attribute_BOOL(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute);
-  void Script_Attribute_Integer(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute);
+  // Not actual properties, but invoked as property handlers to cover
+  // a broad range of underlying properties.
+  JSE_PROP(ScriptAttributeString);
+  JSE_PROP(ScriptAttributeBool);
+  JSE_PROP(ScriptAttributeInteger);
+  JSE_PROP(ScriptSomFontColor);
+  JSE_PROP(ScriptSomFillColor);
+  JSE_PROP(ScriptSomBorderColor);
+  JSE_PROP(ScriptSomBorderWidth);
+  JSE_PROP(ScriptSomValidationMessage);
+  JSE_PROP(ScriptSomMandatoryMessage);
+  JSE_PROP(ScriptSomDefaultValue);
+  JSE_PROP(ScriptSomDefaultValue_Read);
+  JSE_PROP(ScriptSomDataNode);
+  JSE_PROP(ScriptSomMandatory);
+  JSE_PROP(ScriptSomInstanceIndex);
+  JSE_PROP(ScriptSubmitFormatMode);
 
-  void Script_Som_FontColor(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute);
-  void Script_Som_FillColor(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute);
-  void Script_Som_BorderColor(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute);
-  void Script_Som_BorderWidth(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute);
-  void Script_Som_ValidationMessage(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_Attribute eAttribute);
-  void Script_Som_MandatoryMessage(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute);
-  void Script_Field_Length(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute);
-  void Script_Som_DefaultValue(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute);
-  void Script_Som_DefaultValue_Read(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_Attribute eAttribute);
-  void Script_Som_DataNode(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute);
-  void Script_Som_Mandatory(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute);
-  void Script_Som_InstanceIndex(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute);
-  void Script_Som_Message(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_SOM_MESSAGETYPE iMessageType);
-  void Script_Subform_InstanceManager(CFXJSE_Value* pValue,
-                                      bool bSetting,
-                                      XFA_AttributeEnum eAttribute);
-  void Script_SubmitFormat_Mode(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute);
-  void Script_Form_Checksum(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute);
-  void Script_ExclGroup_ErrorText(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute);
+  void ScriptSomMessage(CFXJSE_Value* pValue,
+                        bool bSetting,
+                        XFA_SOM_MESSAGETYPE iMessageType);
 
   Optional<WideString> TryNamespace();
 
-  Optional<int32_t> TryInteger(XFA_Attribute eAttr, bool bUseDefault);
-  bool SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify);
-  int32_t GetInteger(XFA_Attribute eAttr);
+  Optional<int32_t> TryInteger(XFA_Attribute eAttr, bool bUseDefault) const;
+  void SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify);
+  int32_t GetInteger(XFA_Attribute eAttr) const;
 
-  Optional<WideString> TryCData(XFA_Attribute eAttr, bool bUseDefault);
-  bool SetCData(XFA_Attribute eAttr,
+  Optional<WideString> TryCData(XFA_Attribute eAttr, bool bUseDefault) const;
+  void SetCData(XFA_Attribute eAttr,
                 const WideString& wsValue,
                 bool bNotify,
                 bool bScriptModify);
-  WideString GetCData(XFA_Attribute eAttr);
+  WideString GetCData(XFA_Attribute eAttr) const;
 
-  Optional<XFA_AttributeEnum> TryEnum(XFA_Attribute eAttr, bool bUseDefault);
-  bool SetEnum(XFA_Attribute eAttr, XFA_AttributeEnum eValue, bool bNotify);
-  XFA_AttributeEnum GetEnum(XFA_Attribute eAttr);
+  Optional<XFA_AttributeValue> TryEnum(XFA_Attribute eAttr,
+                                       bool bUseDefault) const;
+  void SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify);
+  XFA_AttributeValue GetEnum(XFA_Attribute eAttr) const;
 
   Optional<bool> TryBoolean(XFA_Attribute eAttr, bool bUseDefault);
-  bool SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify);
+  void SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify);
   bool GetBoolean(XFA_Attribute eAttr);
 
   Optional<CXFA_Measurement> TryMeasure(XFA_Attribute eAttr,
                                         bool bUseDefault) const;
   Optional<float> TryMeasureAsFloat(XFA_Attribute attr) const;
-  bool SetMeasure(XFA_Attribute eAttr, CXFA_Measurement mValue, bool bNotify);
+  void SetMeasure(XFA_Attribute eAttr, CXFA_Measurement mValue, bool bNotify);
   CXFA_Measurement GetMeasure(XFA_Attribute eAttr) const;
+  float GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const;
 
   void MergeAllData(CXFA_Object* pDstModule);
 
@@ -217,22 +219,14 @@
   void ThrowTooManyOccurancesException(const WideString& obj) const;
 
  protected:
-  void DefineMethods(const CJX_MethodSpec method_specs[], size_t count);
-
+  void DefineMethods(pdfium::span<const CJX_MethodSpec> methods);
   void MoveBufferMapData(CXFA_Object* pSrcModule, CXFA_Object* pDstModule);
-  void SetMapModuleString(void* pKey, const WideStringView& wsValue);
-  void ThrowException(const wchar_t* str, ...) const;
+  void SetMapModuleString(void* pKey, WideStringView wsValue);
+  void ThrowException(const WideString& str) const;
 
  private:
-  void Script_Boolean_DefaultValue(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute);
-  void Script_Draw_DefaultValue(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute);
-  void Script_Field_DefaultValue(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute);
+  using Type__ = CJX_Object;
+  static const TypeTag static_type__ = TypeTag::Object;
 
   std::pair<CXFA_Node*, int32_t> GetPropertyInternal(int32_t index,
                                                      XFA_Element eType) const;
@@ -240,31 +234,25 @@
 
   void OnChanged(XFA_Attribute eAttr, bool bNotify, bool bScriptModify);
   void OnChanging(XFA_Attribute eAttr, bool bNotify);
-  bool SetUserData(void* pKey,
+  void SetUserData(void* pKey,
                    void* pData,
-                   XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
+                   const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
 
   // Returns a pointer to the XML node that needs to be updated with the new
   // attribute value. |nullptr| if no update is needed.
-  CFX_XMLElement* SetValue(XFA_Attribute eAttr,
-                           XFA_AttributeType eType,
-                           void* pValue,
-                           bool bNotify);
+  CFX_XMLElement* SetValue(XFA_Attribute eAttr, void* pValue, bool bNotify);
   int32_t Subform_and_SubformSet_InstanceIndex();
 
   XFA_MAPMODULEDATA* CreateMapModuleData();
   XFA_MAPMODULEDATA* GetMapModuleData() const;
   void SetMapModuleValue(void* pKey, void* pValue);
-  bool GetMapModuleValue(void* pKey, void*& pValue);
-  bool GetMapModuleString(void* pKey, WideStringView& wsValue);
+  Optional<void*> GetMapModuleValue(void* pKey) const;
+  Optional<WideString> GetMapModuleString(void* pKey) const;
   void SetMapModuleBuffer(void* pKey,
                           void* pValue,
                           int32_t iBytes,
-                          XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
-  bool GetMapModuleBuffer(void* pKey,
-                          void*& pValue,
-                          int32_t& iBytes,
-                          bool bProtoAlso) const;
+                          const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
+  bool GetMapModuleBuffer(void* pKey, void** pValue, int32_t* pBytes) const;
   bool HasMapModuleKey(void* pKey);
   void ClearMapModuleBuffer();
   void RemoveMapModuleKey(void* pKey);
@@ -278,4 +266,9 @@
   size_t calc_recursion_count_ = 0;
 };
 
+typedef void (*XFA_ATTRIBUTE_CALLBACK)(CJX_Object* pNode,
+                                       CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute eAttribute);
+
 #endif  // FXJS_XFA_CJX_OBJECT_H_
diff --git a/fxjs/xfa/cjx_occur.cpp b/fxjs/xfa/cjx_occur.cpp
index f57cd96..de8942d 100644
--- a/fxjs/xfa/cjx_occur.cpp
+++ b/fxjs/xfa/cjx_occur.cpp
@@ -8,13 +8,17 @@
 
 #include <algorithm>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
 
 CJX_Occur::CJX_Occur(CXFA_Occur* node) : CJX_Node(node) {}
 
 CJX_Occur::~CJX_Occur() = default;
 
+bool CJX_Occur::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Occur::max(CFXJSE_Value* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {
@@ -36,21 +40,3 @@
   }
   occur->SetMin(pValue->ToInteger());
 }
-
-void CJX_Occur::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Occur::initial(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Occur::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_occur.h b/fxjs/xfa/cjx_occur.h
index fe17423..8b912b6 100644
--- a/fxjs/xfa/cjx_occur.h
+++ b/fxjs/xfa/cjx_occur.h
@@ -8,19 +8,26 @@
 #define FXJS_XFA_CJX_OCCUR_H_
 
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Occur;
 
-class CJX_Occur : public CJX_Node {
+class CJX_Occur final : public CJX_Node {
  public:
   explicit CJX_Occur(CXFA_Occur* node);
   ~CJX_Occur() override;
 
-  JS_PROP(initial);
-  JS_PROP(max);
-  JS_PROP(min);
-  JS_PROP(use);
-  JS_PROP(usehref);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(max);
+  JSE_PROP(min);
+
+ private:
+  using Type__ = CJX_Occur;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Occur;
 };
 
 #endif  // FXJS_XFA_CJX_OCCUR_H_
diff --git a/fxjs/xfa/cjx_oid.cpp b/fxjs/xfa/cjx_oid.cpp
deleted file mode 100644
index 1320f8d..0000000
--- a/fxjs/xfa/cjx_oid.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_oid.h"
-
-#include "xfa/fxfa/parser/cxfa_oid.h"
-
-CJX_Oid::CJX_Oid(CXFA_Oid* node) : CJX_TextNode(node) {}
-
-CJX_Oid::~CJX_Oid() = default;
-
-void CJX_Oid::use(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Oid::usehref(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_oid.h b/fxjs/xfa/cjx_oid.h
deleted file mode 100644
index 2578388..0000000
--- a/fxjs/xfa/cjx_oid.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_OID_H_
-#define FXJS_XFA_CJX_OID_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Oid;
-
-class CJX_Oid : public CJX_TextNode {
- public:
-  explicit CJX_Oid(CXFA_Oid* node);
-  ~CJX_Oid() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_OID_H_
diff --git a/fxjs/xfa/cjx_oids.cpp b/fxjs/xfa/cjx_oids.cpp
deleted file mode 100644
index a006974..0000000
--- a/fxjs/xfa/cjx_oids.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_oids.h"
-
-#include "xfa/fxfa/parser/cxfa_oids.h"
-
-CJX_Oids::CJX_Oids(CXFA_Oids* node) : CJX_Node(node) {}
-
-CJX_Oids::~CJX_Oids() = default;
-
-void CJX_Oids::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Oids::type(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Oids::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_oids.h b/fxjs/xfa/cjx_oids.h
deleted file mode 100644
index 9951206..0000000
--- a/fxjs/xfa/cjx_oids.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_OIDS_H_
-#define FXJS_XFA_CJX_OIDS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Oids;
-
-class CJX_Oids : public CJX_Node {
- public:
-  explicit CJX_Oids(CXFA_Oids* node);
-  ~CJX_Oids() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_OIDS_H_
diff --git a/fxjs/xfa/cjx_operation.cpp b/fxjs/xfa/cjx_operation.cpp
deleted file mode 100644
index bb412bd..0000000
--- a/fxjs/xfa/cjx_operation.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_operation.h"
-
-#include "xfa/fxfa/parser/cxfa_operation.h"
-
-CJX_Operation::CJX_Operation(CXFA_Operation* node) : CJX_TextNode(node) {}
-
-CJX_Operation::~CJX_Operation() = default;
-
-void CJX_Operation::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Operation::output(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Operation::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Operation::input(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_operation.h b/fxjs/xfa/cjx_operation.h
deleted file mode 100644
index b32ea62..0000000
--- a/fxjs/xfa/cjx_operation.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_OPERATION_H_
-#define FXJS_XFA_CJX_OPERATION_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Operation;
-
-class CJX_Operation : public CJX_TextNode {
- public:
-  explicit CJX_Operation(CXFA_Operation* node);
-  ~CJX_Operation() override;
-
-  JS_PROP(input);
-  JS_PROP(output);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_OPERATION_H_
diff --git a/fxjs/xfa/cjx_overflow.cpp b/fxjs/xfa/cjx_overflow.cpp
deleted file mode 100644
index b6572d4..0000000
--- a/fxjs/xfa/cjx_overflow.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_overflow.h"
-
-#include "xfa/fxfa/parser/cxfa_overflow.h"
-
-CJX_Overflow::CJX_Overflow(CXFA_Overflow* node) : CJX_Node(node) {}
-
-CJX_Overflow::~CJX_Overflow() = default;
-
-void CJX_Overflow::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Overflow::trailer(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Overflow::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Overflow::target(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Overflow::leader(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_overflow.h b/fxjs/xfa/cjx_overflow.h
deleted file mode 100644
index c4f94b8..0000000
--- a/fxjs/xfa/cjx_overflow.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_OVERFLOW_H_
-#define FXJS_XFA_CJX_OVERFLOW_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Overflow;
-
-class CJX_Overflow : public CJX_Node {
- public:
-  explicit CJX_Overflow(CXFA_Overflow* node);
-  ~CJX_Overflow() override;
-
-  JS_PROP(leader);
-  JS_PROP(target);
-  JS_PROP(trailer);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_OVERFLOW_H_
diff --git a/fxjs/xfa/cjx_packet.cpp b/fxjs/xfa/cjx_packet.cpp
index fdebe62..2c8b67d 100644
--- a/fxjs/xfa/cjx_packet.cpp
+++ b/fxjs/xfa/cjx_packet.cpp
@@ -6,10 +6,16 @@
 
 #include "fxjs/xfa/cjx_packet.h"
 
+#include <utility>
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmltext.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_packet.h"
 
 const CJX_MethodSpec CJX_Packet::MethodSpecs[] = {
@@ -18,74 +24,79 @@
     {"setAttribute", setAttribute_static}};
 
 CJX_Packet::CJX_Packet(CXFA_Packet* packet) : CJX_Node(packet) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Packet::~CJX_Packet() {}
 
-CJS_Return CJX_Packet::getAttribute(
-    CJS_V8* runtime,
+bool CJX_Packet::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Packet::getAttribute(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString attributeValue;
-  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
-  if (pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element) {
-    attributeValue = static_cast<CFX_XMLElement*>(pXMLNode)->GetString(
-        runtime->ToWideString(params[0]).c_str());
-  }
-  return CJS_Return(
-      runtime->NewString(attributeValue.UTF8Encode().AsStringView()));
+  CFX_XMLElement* element = ToXMLElement(GetXFANode()->GetXMLMappingNode());
+  if (element)
+    attributeValue = element->GetAttribute(runtime->ToWideString(params[0]));
+
+  return CJS_Result::Success(
+      runtime->NewString(attributeValue.ToUTF8().AsStringView()));
 }
 
-CJS_Return CJX_Packet::setAttribute(
-    CJS_V8* runtime,
+CJS_Result CJX_Packet::setAttribute(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
-  if (pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element) {
-    static_cast<CFX_XMLElement*>(pXMLNode)->SetString(
-        runtime->ToWideString(params[1]), runtime->ToWideString(params[0]));
+  CFX_XMLElement* element = ToXMLElement(GetXFANode()->GetXMLMappingNode());
+  if (element) {
+    element->SetAttribute(runtime->ToWideString(params[1]),
+                          runtime->ToWideString(params[0]));
   }
-  return CJS_Return(runtime->NewNull());
+  return CJS_Result::Success(runtime->NewNull());
 }
 
-CJS_Return CJX_Packet::removeAttribute(
-    CJS_V8* runtime,
+CJS_Result CJX_Packet::removeAttribute(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
-  if (pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element) {
+  CFX_XMLElement* pElement = ToXMLElement(GetXFANode()->GetXMLMappingNode());
+  if (pElement) {
     WideString name = runtime->ToWideString(params[0]);
-    CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-    if (pXMLElement->HasAttribute(name.c_str()))
-      pXMLElement->RemoveAttribute(name.c_str());
+    if (pElement->HasAttribute(name))
+      pElement->RemoveAttribute(name);
   }
-  return CJS_Return(runtime->NewNull());
+  return CJS_Result::Success(runtime->NewNull());
 }
 
 void CJX_Packet::content(CFXJSE_Value* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
-  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
+  CFX_XMLElement* element = ToXMLElement(GetXFANode()->GetXMLMappingNode());
   if (bSetting) {
-    if (pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element) {
-      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-      pXMLElement->SetTextData(pValue->ToWideString());
+    if (element) {
+      element->AppendLastChild(
+          GetXFANode()
+              ->GetDocument()
+              ->GetNotify()
+              ->GetHDOC()
+              ->GetXMLDocument()
+              ->CreateNode<CFX_XMLText>(pValue->ToWideString()));
     }
     return;
   }
 
   WideString wsTextData;
-  if (pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element) {
-    CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-    wsTextData = pXMLElement->GetTextData();
-  }
+  if (element)
+    wsTextData = element->GetTextData();
 
-  pValue->SetString(wsTextData.UTF8Encode().AsStringView());
+  pValue->SetString(wsTextData.ToUTF8().AsStringView());
 }
diff --git a/fxjs/xfa/cjx_packet.h b/fxjs/xfa/cjx_packet.h
index ac1c170..df4a987 100644
--- a/fxjs/xfa/cjx_packet.h
+++ b/fxjs/xfa/cjx_packet.h
@@ -7,23 +7,30 @@
 #ifndef FXJS_XFA_CJX_PACKET_H_
 #define FXJS_XFA_CJX_PACKET_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Packet;
 
-class CJX_Packet : public CJX_Node {
+class CJX_Packet final : public CJX_Node {
  public:
   explicit CJX_Packet(CXFA_Packet* packet);
   ~CJX_Packet() override;
 
-  JS_METHOD(getAttribute, CJX_Packet);
-  JS_METHOD(removeAttribute, CJX_Packet);
-  JS_METHOD(setAttribute, CJX_Packet);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(content);
+  JSE_METHOD(getAttribute);
+  JSE_METHOD(removeAttribute);
+  JSE_METHOD(setAttribute);
+
+  JSE_PROP(content);
 
  private:
+  using Type__ = CJX_Packet;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Packet;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_pagearea.cpp b/fxjs/xfa/cjx_pagearea.cpp
deleted file mode 100644
index 4085d38..0000000
--- a/fxjs/xfa/cjx_pagearea.cpp
+++ /dev/null
@@ -1,61 +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 "fxjs/xfa/cjx_pagearea.h"
-
-#include "xfa/fxfa/parser/cxfa_pagearea.h"
-
-CJX_PageArea::CJX_PageArea(CXFA_PageArea* node) : CJX_Container(node) {}
-
-CJX_PageArea::~CJX_PageArea() = default;
-
-void CJX_PageArea::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageArea::pagePosition(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageArea::oddOrEven(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageArea::relevant(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageArea::initialNumber(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageArea::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageArea::numbered(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageArea::blankOrNotBlank(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_pagearea.h b/fxjs/xfa/cjx_pagearea.h
deleted file mode 100644
index 8935fa6..0000000
--- a/fxjs/xfa/cjx_pagearea.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_PAGEAREA_H_
-#define FXJS_XFA_CJX_PAGEAREA_H_
-
-#include "fxjs/xfa/cjx_container.h"
-
-class CXFA_PageArea;
-
-class CJX_PageArea : public CJX_Container {
- public:
-  explicit CJX_PageArea(CXFA_PageArea* node);
-  ~CJX_PageArea() override;
-
-  JS_PROP(blankOrNotBlank);
-  JS_PROP(initialNumber);
-  JS_PROP(numbered);
-  JS_PROP(oddOrEven);
-  JS_PROP(pagePosition);
-  JS_PROP(relevant);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_PAGEAREA_H_
diff --git a/fxjs/xfa/cjx_pageset.cpp b/fxjs/xfa/cjx_pageset.cpp
deleted file mode 100644
index cfab790..0000000
--- a/fxjs/xfa/cjx_pageset.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_pageset.h"
-
-#include "xfa/fxfa/parser/cxfa_pageset.h"
-
-CJX_PageSet::CJX_PageSet(CXFA_PageSet* node) : CJX_Container(node) {}
-
-CJX_PageSet::~CJX_PageSet() = default;
-
-void CJX_PageSet::relation(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageSet::relevant(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageSet::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PageSet::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_pageset.h b/fxjs/xfa/cjx_pageset.h
deleted file mode 100644
index 7e0ab17..0000000
--- a/fxjs/xfa/cjx_pageset.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_PAGESET_H_
-#define FXJS_XFA_CJX_PAGESET_H_
-
-#include "fxjs/xfa/cjx_container.h"
-
-class CXFA_PageSet;
-
-class CJX_PageSet : public CJX_Container {
- public:
-  explicit CJX_PageSet(CXFA_PageSet* node);
-  ~CJX_PageSet() override;
-
-  JS_PROP(relation);
-  JS_PROP(relevant);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_PAGESET_H_
diff --git a/fxjs/xfa/cjx_para.cpp b/fxjs/xfa/cjx_para.cpp
deleted file mode 100644
index 3a12e47..0000000
--- a/fxjs/xfa/cjx_para.cpp
+++ /dev/null
@@ -1,97 +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 "fxjs/xfa/cjx_para.h"
-
-#include "xfa/fxfa/parser/cxfa_para.h"
-
-CJX_Para::CJX_Para(CXFA_Para* node) : CJX_Node(node) {}
-
-CJX_Para::~CJX_Para() = default;
-
-void CJX_Para::hAlign(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::textIndent(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::marginRight(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::marginLeft(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::radixOffset(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::preserve(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::spaceBelow(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::vAlign(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::tabDefault(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::tabStops(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::lineHeight(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Para::spaceAbove(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_para.h b/fxjs/xfa/cjx_para.h
deleted file mode 100644
index 6bf2841..0000000
--- a/fxjs/xfa/cjx_para.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_PARA_H_
-#define FXJS_XFA_CJX_PARA_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Para;
-
-class CJX_Para : public CJX_Node {
- public:
-  explicit CJX_Para(CXFA_Para* node);
-  ~CJX_Para() override;
-
-  JS_PROP(hAlign);
-  JS_PROP(lineHeight);
-  JS_PROP(marginLeft);
-  JS_PROP(marginRight);
-  JS_PROP(preserve);
-  JS_PROP(radixOffset);
-  JS_PROP(spaceAbove);
-  JS_PROP(spaceBelow);
-  JS_PROP(tabDefault);
-  JS_PROP(tabStops);
-  JS_PROP(textIndent);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(vAlign);
-};
-
-#endif  // FXJS_XFA_CJX_PARA_H_
diff --git a/fxjs/xfa/cjx_password.cpp b/fxjs/xfa/cjx_password.cpp
deleted file mode 100644
index a44354b..0000000
--- a/fxjs/xfa/cjx_password.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_password.h"
-
-#include "xfa/fxfa/parser/cxfa_password.h"
-
-CJX_Password::CJX_Password(CXFA_Password* node) : CJX_TextNode(node) {}
-
-CJX_Password::~CJX_Password() = default;
-
-void CJX_Password::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Password::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_password.h b/fxjs/xfa/cjx_password.h
deleted file mode 100644
index ede93cc..0000000
--- a/fxjs/xfa/cjx_password.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_PASSWORD_H_
-#define FXJS_XFA_CJX_PASSWORD_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Password;
-
-class CJX_Password : public CJX_TextNode {
- public:
-  explicit CJX_Password(CXFA_Password* node);
-  ~CJX_Password() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_PASSWORD_H_
diff --git a/fxjs/xfa/cjx_passwordedit.cpp b/fxjs/xfa/cjx_passwordedit.cpp
deleted file mode 100644
index 8b1c591..0000000
--- a/fxjs/xfa/cjx_passwordedit.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_passwordedit.h"
-
-#include "xfa/fxfa/parser/cxfa_passwordedit.h"
-
-CJX_PasswordEdit::CJX_PasswordEdit(CXFA_PasswordEdit* node) : CJX_Node(node) {}
-
-CJX_PasswordEdit::~CJX_PasswordEdit() = default;
-
-void CJX_PasswordEdit::use(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PasswordEdit::passwordChar(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PasswordEdit::usehref(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_PasswordEdit::hScrollPolicy(CFXJSE_Value* pValue,
-                                     bool bSetting,
-                                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_passwordedit.h b/fxjs/xfa/cjx_passwordedit.h
deleted file mode 100644
index e4a5fcb..0000000
--- a/fxjs/xfa/cjx_passwordedit.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_PASSWORDEDIT_H_
-#define FXJS_XFA_CJX_PASSWORDEDIT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_PasswordEdit;
-
-class CJX_PasswordEdit : public CJX_Node {
- public:
-  explicit CJX_PasswordEdit(CXFA_PasswordEdit* node);
-  ~CJX_PasswordEdit() override;
-
-  JS_PROP(hScrollPolicy);
-  JS_PROP(passwordChar);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_PASSWORDEDIT_H_
diff --git a/fxjs/xfa/cjx_pattern.cpp b/fxjs/xfa/cjx_pattern.cpp
deleted file mode 100644
index 5acaa84..0000000
--- a/fxjs/xfa/cjx_pattern.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_pattern.h"
-
-#include "xfa/fxfa/parser/cxfa_pattern.h"
-
-CJX_Pattern::CJX_Pattern(CXFA_Pattern* node) : CJX_Node(node) {}
-
-CJX_Pattern::~CJX_Pattern() = default;
-
-void CJX_Pattern::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Pattern::type(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Pattern::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_pattern.h b/fxjs/xfa/cjx_pattern.h
deleted file mode 100644
index 81754b8..0000000
--- a/fxjs/xfa/cjx_pattern.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_PATTERN_H_
-#define FXJS_XFA_CJX_PATTERN_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Pattern;
-
-class CJX_Pattern : public CJX_Node {
- public:
-  explicit CJX_Pattern(CXFA_Pattern* node);
-  ~CJX_Pattern() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_PATTERN_H_
diff --git a/fxjs/xfa/cjx_picture.cpp b/fxjs/xfa/cjx_picture.cpp
deleted file mode 100644
index 81f5ec4..0000000
--- a/fxjs/xfa/cjx_picture.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_picture.h"
-
-#include "xfa/fxfa/parser/cxfa_picture.h"
-
-CJX_Picture::CJX_Picture(CXFA_Picture* node) : CJX_Node(node) {}
-
-CJX_Picture::~CJX_Picture() = default;
-
-void CJX_Picture::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Picture::defaultValue(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Picture::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Picture::value(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_picture.h b/fxjs/xfa/cjx_picture.h
deleted file mode 100644
index 25497c3..0000000
--- a/fxjs/xfa/cjx_picture.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_PICTURE_H_
-#define FXJS_XFA_CJX_PICTURE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Picture;
-
-class CJX_Picture : public CJX_Node {
- public:
-  explicit CJX_Picture(CXFA_Picture* node);
-  ~CJX_Picture() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_PICTURE_H_
diff --git a/fxjs/xfa/cjx_query.cpp b/fxjs/xfa/cjx_query.cpp
deleted file mode 100644
index f6f255f..0000000
--- a/fxjs/xfa/cjx_query.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_query.h"
-
-#include "xfa/fxfa/parser/cxfa_query.h"
-
-CJX_Query::CJX_Query(CXFA_Query* node) : CJX_Node(node) {}
-
-CJX_Query::~CJX_Query() = default;
-
-void CJX_Query::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Query::commandType(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Query::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_query.h b/fxjs/xfa/cjx_query.h
deleted file mode 100644
index 1057fa2..0000000
--- a/fxjs/xfa/cjx_query.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_QUERY_H_
-#define FXJS_XFA_CJX_QUERY_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Query;
-
-class CJX_Query : public CJX_Node {
- public:
-  explicit CJX_Query(CXFA_Query* node);
-  ~CJX_Query() override;
-
-  JS_PROP(commandType);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_QUERY_H_
diff --git a/fxjs/xfa/cjx_radial.cpp b/fxjs/xfa/cjx_radial.cpp
deleted file mode 100644
index bd8b1a2..0000000
--- a/fxjs/xfa/cjx_radial.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_radial.h"
-
-#include "xfa/fxfa/parser/cxfa_radial.h"
-
-CJX_Radial::CJX_Radial(CXFA_Radial* node) : CJX_Node(node) {}
-
-CJX_Radial::~CJX_Radial() = default;
-
-void CJX_Radial::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Radial::type(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Radial::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_radial.h b/fxjs/xfa/cjx_radial.h
deleted file mode 100644
index 3073797..0000000
--- a/fxjs/xfa/cjx_radial.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_RADIAL_H_
-#define FXJS_XFA_CJX_RADIAL_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Radial;
-
-class CJX_Radial : public CJX_Node {
- public:
-  explicit CJX_Radial(CXFA_Radial* node);
-  ~CJX_Radial() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_RADIAL_H_
diff --git a/fxjs/xfa/cjx_reason.cpp b/fxjs/xfa/cjx_reason.cpp
deleted file mode 100644
index c9bb6c3..0000000
--- a/fxjs/xfa/cjx_reason.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_reason.h"
-
-#include "xfa/fxfa/parser/cxfa_reason.h"
-
-CJX_Reason::CJX_Reason(CXFA_Reason* node) : CJX_TextNode(node) {}
-
-CJX_Reason::~CJX_Reason() = default;
-
-void CJX_Reason::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Reason::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_reason.h b/fxjs/xfa/cjx_reason.h
deleted file mode 100644
index 465cede..0000000
--- a/fxjs/xfa/cjx_reason.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_REASON_H_
-#define FXJS_XFA_CJX_REASON_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Reason;
-
-class CJX_Reason : public CJX_TextNode {
- public:
-  explicit CJX_Reason(CXFA_Reason* node);
-  ~CJX_Reason() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_REASON_H_
diff --git a/fxjs/xfa/cjx_reasons.cpp b/fxjs/xfa/cjx_reasons.cpp
deleted file mode 100644
index 11a9868..0000000
--- a/fxjs/xfa/cjx_reasons.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_reasons.h"
-
-#include "xfa/fxfa/parser/cxfa_reasons.h"
-
-CJX_Reasons::CJX_Reasons(CXFA_Reasons* node) : CJX_Node(node) {}
-
-CJX_Reasons::~CJX_Reasons() = default;
-
-void CJX_Reasons::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Reasons::type(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Reasons::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_reasons.h b/fxjs/xfa/cjx_reasons.h
deleted file mode 100644
index a5da62a..0000000
--- a/fxjs/xfa/cjx_reasons.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_REASONS_H_
-#define FXJS_XFA_CJX_REASONS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Reasons;
-
-class CJX_Reasons : public CJX_Node {
- public:
-  explicit CJX_Reasons(CXFA_Reasons* node);
-  ~CJX_Reasons() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_REASONS_H_
diff --git a/fxjs/xfa/cjx_recordset.cpp b/fxjs/xfa/cjx_recordset.cpp
deleted file mode 100644
index c4fe21f..0000000
--- a/fxjs/xfa/cjx_recordset.cpp
+++ /dev/null
@@ -1,63 +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 "fxjs/xfa/cjx_recordset.h"
-
-#include <algorithm>
-
-#include "xfa/fxfa/parser/cxfa_recordset.h"
-
-CJX_RecordSet::CJX_RecordSet(CXFA_RecordSet* node) : CJX_Node(node) {}
-
-CJX_RecordSet::~CJX_RecordSet() = default;
-
-void CJX_RecordSet::max(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RecordSet::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RecordSet::eofAction(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RecordSet::cursorType(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RecordSet::lockType(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RecordSet::bofAction(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RecordSet::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RecordSet::cursorLocation(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_recordset.h b/fxjs/xfa/cjx_recordset.h
deleted file mode 100644
index 7c34f6e..0000000
--- a/fxjs/xfa/cjx_recordset.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_RECORDSET_H_
-#define FXJS_XFA_CJX_RECORDSET_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_RecordSet;
-
-class CJX_RecordSet : public CJX_Node {
- public:
-  explicit CJX_RecordSet(CXFA_RecordSet* node);
-  ~CJX_RecordSet() override;
-
-  JS_PROP(bofAction);
-  JS_PROP(cursorLocation);
-  JS_PROP(cursorType);
-  JS_PROP(eofAction);
-  JS_PROP(lockType);
-  JS_PROP(max);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_RECORDSET_H_
diff --git a/fxjs/xfa/cjx_rectangle.cpp b/fxjs/xfa/cjx_rectangle.cpp
deleted file mode 100644
index 155a29f..0000000
--- a/fxjs/xfa/cjx_rectangle.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_rectangle.h"
-
-#include "xfa/fxfa/parser/cxfa_rectangle.h"
-
-CJX_Rectangle::CJX_Rectangle(CXFA_Rectangle* node) : CJX_Node(node) {}
-
-CJX_Rectangle::~CJX_Rectangle() = default;
-
-void CJX_Rectangle::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Rectangle::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Rectangle::hand(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_rectangle.h b/fxjs/xfa/cjx_rectangle.h
deleted file mode 100644
index c08f7a9..0000000
--- a/fxjs/xfa/cjx_rectangle.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_RECTANGLE_H_
-#define FXJS_XFA_CJX_RECTANGLE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Rectangle;
-
-class CJX_Rectangle : public CJX_Node {
- public:
-  explicit CJX_Rectangle(CXFA_Rectangle* node);
-  ~CJX_Rectangle() override;
-
-  JS_PROP(hand);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_RECTANGLE_H_
diff --git a/fxjs/xfa/cjx_ref.cpp b/fxjs/xfa/cjx_ref.cpp
deleted file mode 100644
index 5930815..0000000
--- a/fxjs/xfa/cjx_ref.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_ref.h"
-
-#include "xfa/fxfa/parser/cxfa_ref.h"
-
-CJX_Ref::CJX_Ref(CXFA_Ref* node) : CJX_TextNode(node) {}
-
-CJX_Ref::~CJX_Ref() = default;
-
-void CJX_Ref::use(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Ref::usehref(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_ref.h b/fxjs/xfa/cjx_ref.h
deleted file mode 100644
index 4366282..0000000
--- a/fxjs/xfa/cjx_ref.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_REF_H_
-#define FXJS_XFA_CJX_REF_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Ref;
-
-class CJX_Ref : public CJX_TextNode {
- public:
-  explicit CJX_Ref(CXFA_Ref* node);
-  ~CJX_Ref() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_REF_H_
diff --git a/fxjs/xfa/cjx_rootelement.cpp b/fxjs/xfa/cjx_rootelement.cpp
deleted file mode 100644
index 31fbb00..0000000
--- a/fxjs/xfa/cjx_rootelement.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_rootelement.h"
-
-#include "xfa/fxfa/parser/cxfa_rootelement.h"
-
-CJX_RootElement::CJX_RootElement(CXFA_RootElement* node) : CJX_TextNode(node) {}
-
-CJX_RootElement::~CJX_RootElement() = default;
-
-void CJX_RootElement::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_RootElement::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_rootelement.h b/fxjs/xfa/cjx_rootelement.h
deleted file mode 100644
index 40790df..0000000
--- a/fxjs/xfa/cjx_rootelement.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_ROOTELEMENT_H_
-#define FXJS_XFA_CJX_ROOTELEMENT_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_RootElement;
-
-class CJX_RootElement : public CJX_TextNode {
- public:
-  explicit CJX_RootElement(CXFA_RootElement* node);
-  ~CJX_RootElement() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_ROOTELEMENT_H_
diff --git a/fxjs/xfa/cjx_script.cpp b/fxjs/xfa/cjx_script.cpp
index 4016c13..482a57e 100644
--- a/fxjs/xfa/cjx_script.cpp
+++ b/fxjs/xfa/cjx_script.cpp
@@ -6,29 +6,15 @@
 
 #include "fxjs/xfa/cjx_script.h"
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
 
 CJX_Script::CJX_Script(CXFA_Script* node) : CJX_Node(node) {}
 
 CJX_Script::~CJX_Script() = default;
 
-void CJX_Script::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Script::contentType(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Script::runAt(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+bool CJX_Script::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 void CJX_Script::stateless(CFXJSE_Value* pValue,
@@ -40,27 +26,3 @@
   }
   pValue->SetString(FX_UTF8Encode(WideStringView(L"0", 1)).AsStringView());
 }
-
-void CJX_Script::defaultValue(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Script::binding(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Script::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Script::value(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_script.h b/fxjs/xfa/cjx_script.h
index ad5f994..af7c985 100644
--- a/fxjs/xfa/cjx_script.h
+++ b/fxjs/xfa/cjx_script.h
@@ -8,22 +8,25 @@
 #define FXJS_XFA_CJX_SCRIPT_H_
 
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Script;
 
-class CJX_Script : public CJX_Node {
+class CJX_Script final : public CJX_Node {
  public:
   explicit CJX_Script(CXFA_Script* node);
   ~CJX_Script() override;
 
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(binding);
-  JS_PROP(contentType);
-  JS_PROP(runAt);
-  JS_PROP(stateless);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(stateless);
+
+ private:
+  using Type__ = CJX_Script;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Script;
 };
 
 #endif  // FXJS_XFA_CJX_SCRIPT_H_
diff --git a/fxjs/xfa/cjx_select.cpp b/fxjs/xfa/cjx_select.cpp
deleted file mode 100644
index d1cb259..0000000
--- a/fxjs/xfa/cjx_select.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_select.h"
-
-#include "xfa/fxfa/parser/cxfa_select.h"
-
-CJX_Select::CJX_Select(CXFA_Select* node) : CJX_TextNode(node) {}
-
-CJX_Select::~CJX_Select() = default;
-
-void CJX_Select::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Select::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_select.h b/fxjs/xfa/cjx_select.h
deleted file mode 100644
index 334c003..0000000
--- a/fxjs/xfa/cjx_select.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SELECT_H_
-#define FXJS_XFA_CJX_SELECT_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Select;
-
-class CJX_Select : public CJX_TextNode {
- public:
-  explicit CJX_Select(CXFA_Select* node);
-  ~CJX_Select() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SELECT_H_
diff --git a/fxjs/xfa/cjx_setproperty.cpp b/fxjs/xfa/cjx_setproperty.cpp
deleted file mode 100644
index 871fc4d..0000000
--- a/fxjs/xfa/cjx_setproperty.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_setproperty.h"
-
-#include "xfa/fxfa/parser/cxfa_setproperty.h"
-
-CJX_SetProperty::CJX_SetProperty(CXFA_SetProperty* node) : CJX_Node(node) {}
-
-CJX_SetProperty::~CJX_SetProperty() = default;
-
-void CJX_SetProperty::connection(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SetProperty::target(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_setproperty.h b/fxjs/xfa/cjx_setproperty.h
deleted file mode 100644
index 9929fef..0000000
--- a/fxjs/xfa/cjx_setproperty.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SETPROPERTY_H_
-#define FXJS_XFA_CJX_SETPROPERTY_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_SetProperty;
-
-class CJX_SetProperty : public CJX_Node {
- public:
-  explicit CJX_SetProperty(CXFA_SetProperty* node);
-  ~CJX_SetProperty() override;
-
-  JS_PROP(connection);
-  JS_PROP(target);
-};
-
-#endif  // FXJS_XFA_CJX_SETPROPERTY_H_
diff --git a/fxjs/xfa/cjx_signature.cpp b/fxjs/xfa/cjx_signature.cpp
deleted file mode 100644
index 912d9d8..0000000
--- a/fxjs/xfa/cjx_signature.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_signature.h"
-
-#include "xfa/fxfa/parser/cxfa_signature.h"
-
-CJX_Signature::CJX_Signature(CXFA_Signature* node) : CJX_Node(node) {}
-
-CJX_Signature::~CJX_Signature() = default;
-
-void CJX_Signature::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Signature::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_signature.h b/fxjs/xfa/cjx_signature.h
deleted file mode 100644
index bed0b1a..0000000
--- a/fxjs/xfa/cjx_signature.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SIGNATURE_H_
-#define FXJS_XFA_CJX_SIGNATURE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Signature;
-
-class CJX_Signature : public CJX_Node {
- public:
-  explicit CJX_Signature(CXFA_Signature* node);
-  ~CJX_Signature() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SIGNATURE_H_
diff --git a/fxjs/xfa/cjx_signatureproperties.cpp b/fxjs/xfa/cjx_signatureproperties.cpp
deleted file mode 100644
index 6f2e7a7..0000000
--- a/fxjs/xfa/cjx_signatureproperties.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_signatureproperties.h"
-
-#include "xfa/fxfa/parser/cxfa_signatureproperties.h"
-
-CJX_SignatureProperties::CJX_SignatureProperties(CXFA_SignatureProperties* node)
-    : CJX_Node(node) {}
-
-CJX_SignatureProperties::~CJX_SignatureProperties() = default;
-
-void CJX_SignatureProperties::use(CFXJSE_Value* pValue,
-                                  bool bSetting,
-                                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SignatureProperties::usehref(CFXJSE_Value* pValue,
-                                      bool bSetting,
-                                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_signatureproperties.h b/fxjs/xfa/cjx_signatureproperties.h
deleted file mode 100644
index ffd47a3..0000000
--- a/fxjs/xfa/cjx_signatureproperties.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SIGNATUREPROPERTIES_H_
-#define FXJS_XFA_CJX_SIGNATUREPROPERTIES_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_SignatureProperties;
-
-/* Deprecated */
-class CJX_SignatureProperties : public CJX_Node {
- public:
-  explicit CJX_SignatureProperties(CXFA_SignatureProperties* node);
-  ~CJX_SignatureProperties() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SIGNATUREPROPERTIES_H_
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.cpp b/fxjs/xfa/cjx_signaturepseudomodel.cpp
index 202532c..97ecd6e 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.cpp
+++ b/fxjs/xfa/cjx_signaturepseudomodel.cpp
@@ -8,8 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
 
 const CJX_MethodSpec CJX_SignaturePseudoModel::MethodSpecs[] = {
@@ -21,39 +22,47 @@
 CJX_SignaturePseudoModel::CJX_SignaturePseudoModel(
     CScript_SignaturePseudoModel* model)
     : CJX_Object(model) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() {}
 
-CJS_Return CJX_SignaturePseudoModel::verifySignature(
-    CJS_V8* runtime,
+bool CJX_SignaturePseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_SignaturePseudoModel::verifySignature(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 4)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewNumber(0));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(runtime->NewNumber(0));
 }
 
-CJS_Return CJX_SignaturePseudoModel::sign(
-    CJS_V8* runtime,
+CJS_Result CJX_SignaturePseudoModel::sign(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 3 || params.size() > 7)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewBoolean(false));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(runtime->NewBoolean(false));
 }
 
-CJS_Return CJX_SignaturePseudoModel::enumerate(
-    CJS_V8* runtime,
+CJS_Result CJX_SignaturePseudoModel::enumerate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_SignaturePseudoModel::clear(
-    CJS_V8* runtime,
+CJS_Result CJX_SignaturePseudoModel::clear(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewBoolean(false));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(runtime->NewBoolean(false));
 }
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.h b/fxjs/xfa/cjx_signaturepseudomodel.h
index 88f90fb..ed77238 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.h
+++ b/fxjs/xfa/cjx_signaturepseudomodel.h
@@ -7,22 +7,29 @@
 #ifndef FXJS_XFA_CJX_SIGNATUREPSEUDOMODEL_H_
 #define FXJS_XFA_CJX_SIGNATUREPSEUDOMODEL_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CScript_SignaturePseudoModel;
 
-class CJX_SignaturePseudoModel : public CJX_Object {
+class CJX_SignaturePseudoModel final : public CJX_Object {
  public:
   explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model);
   ~CJX_SignaturePseudoModel() override;
 
-  JS_METHOD(verifySignature /*verify*/, CJX_SignaturePseudoModel);
-  JS_METHOD(sign, CJX_SignaturePseudoModel);
-  JS_METHOD(enumerate, CJX_SignaturePseudoModel);
-  JS_METHOD(clear, CJX_SignaturePseudoModel);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_METHOD(verifySignature /*verify*/);
+  JSE_METHOD(sign);
+  JSE_METHOD(enumerate);
+  JSE_METHOD(clear);
 
  private:
+  using Type__ = CJX_SignaturePseudoModel;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::SignaturePesudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_signdata.cpp b/fxjs/xfa/cjx_signdata.cpp
deleted file mode 100644
index eca7430..0000000
--- a/fxjs/xfa/cjx_signdata.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_signdata.h"
-
-#include "xfa/fxfa/parser/cxfa_signdata.h"
-
-CJX_SignData::CJX_SignData(CXFA_SignData* node) : CJX_Node(node) {}
-
-CJX_SignData::~CJX_SignData() = default;
-
-void CJX_SignData::ref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SignData::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SignData::operation(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SignData::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SignData::target(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_signdata.h b/fxjs/xfa/cjx_signdata.h
deleted file mode 100644
index 7d9b175..0000000
--- a/fxjs/xfa/cjx_signdata.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SIGNDATA_H_
-#define FXJS_XFA_CJX_SIGNDATA_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_SignData;
-
-class CJX_SignData : public CJX_Node {
- public:
-  explicit CJX_SignData(CXFA_SignData* node);
-  ~CJX_SignData() override;
-
-  JS_PROP(operation);
-  JS_PROP(ref);
-  JS_PROP(target);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SIGNDATA_H_
diff --git a/fxjs/xfa/cjx_signing.cpp b/fxjs/xfa/cjx_signing.cpp
deleted file mode 100644
index 378f725..0000000
--- a/fxjs/xfa/cjx_signing.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_signing.h"
-
-#include "xfa/fxfa/parser/cxfa_signing.h"
-
-CJX_Signing::CJX_Signing(CXFA_Signing* node) : CJX_Node(node) {}
-
-CJX_Signing::~CJX_Signing() = default;
-
-void CJX_Signing::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Signing::type(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Signing::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_signing.h b/fxjs/xfa/cjx_signing.h
deleted file mode 100644
index 8b35509..0000000
--- a/fxjs/xfa/cjx_signing.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SIGNING_H_
-#define FXJS_XFA_CJX_SIGNING_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Signing;
-
-class CJX_Signing : public CJX_Node {
- public:
-  explicit CJX_Signing(CXFA_Signing* node);
-  ~CJX_Signing() override;
-
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SIGNING_H_
diff --git a/fxjs/xfa/cjx_soapaction.cpp b/fxjs/xfa/cjx_soapaction.cpp
deleted file mode 100644
index 854d2d7..0000000
--- a/fxjs/xfa/cjx_soapaction.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_soapaction.h"
-
-#include "xfa/fxfa/parser/cxfa_soapaction.h"
-
-CJX_SoapAction::CJX_SoapAction(CXFA_SoapAction* node) : CJX_TextNode(node) {}
-
-CJX_SoapAction::~CJX_SoapAction() = default;
-
-void CJX_SoapAction::use(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SoapAction::usehref(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_soapaction.h b/fxjs/xfa/cjx_soapaction.h
deleted file mode 100644
index efc90e4..0000000
--- a/fxjs/xfa/cjx_soapaction.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SOAPACTION_H_
-#define FXJS_XFA_CJX_SOAPACTION_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_SoapAction;
-
-class CJX_SoapAction : public CJX_TextNode {
- public:
-  explicit CJX_SoapAction(CXFA_SoapAction* node);
-  ~CJX_SoapAction() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SOAPACTION_H_
diff --git a/fxjs/xfa/cjx_soapaddress.cpp b/fxjs/xfa/cjx_soapaddress.cpp
deleted file mode 100644
index be7e84c..0000000
--- a/fxjs/xfa/cjx_soapaddress.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_soapaddress.h"
-
-#include "xfa/fxfa/parser/cxfa_soapaddress.h"
-
-CJX_SoapAddress::CJX_SoapAddress(CXFA_SoapAddress* node) : CJX_TextNode(node) {}
-
-CJX_SoapAddress::~CJX_SoapAddress() = default;
-
-void CJX_SoapAddress::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SoapAddress::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_soapaddress.h b/fxjs/xfa/cjx_soapaddress.h
deleted file mode 100644
index 3e6e004..0000000
--- a/fxjs/xfa/cjx_soapaddress.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SOAPADDRESS_H_
-#define FXJS_XFA_CJX_SOAPADDRESS_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_SoapAddress;
-
-class CJX_SoapAddress : public CJX_TextNode {
- public:
-  explicit CJX_SoapAddress(CXFA_SoapAddress* node);
-  ~CJX_SoapAddress() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SOAPADDRESS_H_
diff --git a/fxjs/xfa/cjx_solid.cpp b/fxjs/xfa/cjx_solid.cpp
deleted file mode 100644
index e692153..0000000
--- a/fxjs/xfa/cjx_solid.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_solid.h"
-
-#include "xfa/fxfa/parser/cxfa_solid.h"
-
-CJX_Solid::CJX_Solid(CXFA_Solid* node) : CJX_Node(node) {}
-
-CJX_Solid::~CJX_Solid() = default;
-
-void CJX_Solid::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Solid::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_solid.h b/fxjs/xfa/cjx_solid.h
deleted file mode 100644
index 3b8dd5e..0000000
--- a/fxjs/xfa/cjx_solid.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SOLID_H_
-#define FXJS_XFA_CJX_SOLID_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Solid;
-
-class CJX_Solid : public CJX_Node {
- public:
-  explicit CJX_Solid(CXFA_Solid* node);
-  ~CJX_Solid() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SOLID_H_
diff --git a/fxjs/xfa/cjx_source.cpp b/fxjs/xfa/cjx_source.cpp
index 399a6c3..f6d3187 100644
--- a/fxjs/xfa/cjx_source.cpp
+++ b/fxjs/xfa/cjx_source.cpp
@@ -8,8 +8,8 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_source.h"
 
 const CJX_MethodSpec CJX_Source::MethodSpecs[] = {
@@ -32,148 +32,157 @@
     {"updateBatch", updateBatch_static}};
 
 CJX_Source::CJX_Source(CXFA_Source* src) : CJX_Node(src) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Source::~CJX_Source() {}
 
-CJS_Return CJX_Source::next(CJS_V8* runtime,
+bool CJX_Source::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Source::next(CFX_V8* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::cancelBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_Source::cancelBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::first(CJS_V8* runtime,
+CJS_Result CJX_Source::first(CFX_V8* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::updateBatch(
-    CJS_V8* runtime,
+CJS_Result CJX_Source::updateBatch(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::previous(
-    CJS_V8* runtime,
+CJS_Result CJX_Source::previous(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::isBOF(CJS_V8* runtime,
+CJS_Result CJX_Source::isBOF(CFX_V8* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::isEOF(CJS_V8* runtime,
+CJS_Result CJX_Source::isEOF(CFX_V8* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::cancel(CJS_V8* runtime,
+CJS_Result CJX_Source::cancel(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::update(CJS_V8* runtime,
+CJS_Result CJX_Source::update(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::open(CJS_V8* runtime,
+CJS_Result CJX_Source::open(CFX_V8* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::deleteItem(
-    CJS_V8* runtime,
+CJS_Result CJX_Source::deleteItem(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::addNew(CJS_V8* runtime,
+CJS_Result CJX_Source::addNew(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::requery(
-    CJS_V8* runtime,
+CJS_Result CJX_Source::requery(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::resync(CJS_V8* runtime,
+CJS_Result CJX_Source::resync(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::close(CJS_V8* runtime,
+CJS_Result CJX_Source::close(CFX_V8* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::last(CJS_V8* runtime,
+CJS_Result CJX_Source::last(CFX_V8* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Source::hasDataChanged(
-    CJS_V8* runtime,
+CJS_Result CJX_Source::hasDataChanged(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(true);
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success();
 }
 
 void CJX_Source::db(CFXJSE_Value* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {}
-
-void CJX_Source::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Source::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_source.h b/fxjs/xfa/cjx_source.h
index de84912..cd70601 100644
--- a/fxjs/xfa/cjx_source.h
+++ b/fxjs/xfa/cjx_source.h
@@ -7,39 +7,44 @@
 #ifndef FXJS_XFA_CJX_SOURCE_H_
 #define FXJS_XFA_CJX_SOURCE_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Source;
 
-class CJX_Source : public CJX_Node {
+class CJX_Source final : public CJX_Node {
  public:
   explicit CJX_Source(CXFA_Source* src);
   ~CJX_Source() override;
 
-  JS_METHOD(addNew, CJX_Source);
-  JS_METHOD(cancel, CJX_Source);
-  JS_METHOD(cancelBatch, CJX_Source);
-  JS_METHOD(close, CJX_Source);
-  JS_METHOD(deleteItem /*delete*/, CJX_Source);
-  JS_METHOD(first, CJX_Source);
-  JS_METHOD(hasDataChanged, CJX_Source);
-  JS_METHOD(isBOF, CJX_Source);
-  JS_METHOD(isEOF, CJX_Source);
-  JS_METHOD(last, CJX_Source);
-  JS_METHOD(next, CJX_Source);
-  JS_METHOD(open, CJX_Source);
-  JS_METHOD(previous, CJX_Source);
-  JS_METHOD(requery, CJX_Source);
-  JS_METHOD(resync, CJX_Source);
-  JS_METHOD(update, CJX_Source);
-  JS_METHOD(updateBatch, CJX_Source);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(db);
-  JS_PROP(use);
-  JS_PROP(usehref);
+  JSE_METHOD(addNew);
+  JSE_METHOD(cancel);
+  JSE_METHOD(cancelBatch);
+  JSE_METHOD(close);
+  JSE_METHOD(deleteItem /*delete*/);
+  JSE_METHOD(first);
+  JSE_METHOD(hasDataChanged);
+  JSE_METHOD(isBOF);
+  JSE_METHOD(isEOF);
+  JSE_METHOD(last);
+  JSE_METHOD(next);
+  JSE_METHOD(open);
+  JSE_METHOD(previous);
+  JSE_METHOD(requery);
+  JSE_METHOD(resync);
+  JSE_METHOD(update);
+  JSE_METHOD(updateBatch);
+
+  JSE_PROP(db);
 
  private:
+  using Type__ = CJX_Source;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Source;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_sourceset.cpp b/fxjs/xfa/cjx_sourceset.cpp
deleted file mode 100644
index 5bfabde..0000000
--- a/fxjs/xfa/cjx_sourceset.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_sourceset.h"
-
-#include "xfa/fxfa/parser/cxfa_sourceset.h"
-
-CJX_SourceSet::CJX_SourceSet(CXFA_SourceSet* node) : CJX_Model(node) {}
-
-CJX_SourceSet::~CJX_SourceSet() = default;
-
-void CJX_SourceSet::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SourceSet::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_sourceset.h b/fxjs/xfa/cjx_sourceset.h
deleted file mode 100644
index de4ac68..0000000
--- a/fxjs/xfa/cjx_sourceset.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SOURCESET_H_
-#define FXJS_XFA_CJX_SOURCESET_H_
-
-#include "fxjs/xfa/cjx_model.h"
-
-class CXFA_SourceSet;
-
-class CJX_SourceSet : public CJX_Model {
- public:
-  explicit CJX_SourceSet(CXFA_SourceSet* node);
-  ~CJX_SourceSet() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SOURCESET_H_
diff --git a/fxjs/xfa/cjx_speak.cpp b/fxjs/xfa/cjx_speak.cpp
deleted file mode 100644
index 161b245..0000000
--- a/fxjs/xfa/cjx_speak.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_speak.h"
-
-#include "xfa/fxfa/parser/cxfa_speak.h"
-
-CJX_Speak::CJX_Speak(CXFA_Speak* node) : CJX_TextNode(node) {}
-
-CJX_Speak::~CJX_Speak() = default;
-
-void CJX_Speak::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Speak::priority(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Speak::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Speak::disable(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_speak.h b/fxjs/xfa/cjx_speak.h
deleted file mode 100644
index 5698105..0000000
--- a/fxjs/xfa/cjx_speak.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_SPEAK_H_
-#define FXJS_XFA_CJX_SPEAK_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Speak;
-
-class CJX_Speak : public CJX_TextNode {
- public:
-  explicit CJX_Speak(CXFA_Speak* node);
-  ~CJX_Speak() override;
-
-  JS_PROP(disable);
-  JS_PROP(priority);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SPEAK_H_
diff --git a/fxjs/xfa/cjx_stipple.cpp b/fxjs/xfa/cjx_stipple.cpp
deleted file mode 100644
index ae520c9..0000000
--- a/fxjs/xfa/cjx_stipple.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_stipple.h"
-
-#include "xfa/fxfa/parser/cxfa_stipple.h"
-
-CJX_Stipple::CJX_Stipple(CXFA_Stipple* node) : CJX_Node(node) {}
-
-CJX_Stipple::~CJX_Stipple() = default;
-
-void CJX_Stipple::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Stipple::rate(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Stipple::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_stipple.h b/fxjs/xfa/cjx_stipple.h
deleted file mode 100644
index 68ddd83..0000000
--- a/fxjs/xfa/cjx_stipple.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_STIPPLE_H_
-#define FXJS_XFA_CJX_STIPPLE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Stipple;
-
-class CJX_Stipple : public CJX_Node {
- public:
-  explicit CJX_Stipple(CXFA_Stipple* node);
-  ~CJX_Stipple() override;
-
-  JS_PROP(rate);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_STIPPLE_H_
diff --git a/fxjs/xfa/cjx_subform.cpp b/fxjs/xfa/cjx_subform.cpp
index 947d66d..8a72b5b 100644
--- a/fxjs/xfa/cjx_subform.cpp
+++ b/fxjs/xfa/cjx_subform.cpp
@@ -8,8 +8,10 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/fxfa.h"
@@ -23,59 +25,66 @@
     {"execValidate", execValidate_static}};
 
 CJX_Subform::CJX_Subform(CXFA_Node* node) : CJX_Container(node) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Subform::~CJX_Subform() {}
 
-CJS_Return CJX_Subform::execEvent(
-    CJS_V8* runtime,
+bool CJX_Subform::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Subform::execEvent(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   execSingleEventByName(runtime->ToWideString(params[0]).AsStringView(),
                         XFA_Element::Subform);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Subform::execInitialize(
-    CJS_V8* runtime,
+CJS_Result CJX_Subform::execInitialize(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify)
-    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize);
-  return CJS_Return(true);
+    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize, false,
+                                  true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Subform::execCalculate(
-    CJS_V8* runtime,
+CJS_Result CJX_Subform::execCalculate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify)
-    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate);
-  return CJS_Return(true);
+    pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate, false,
+                                  true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Subform::execValidate(
-    CJS_V8* runtime,
+CJS_Result CJX_Subform::execValidate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
-    return CJS_Return(runtime->NewBoolean(false));
+    return CJS_Result::Success(runtime->NewBoolean(false));
 
-  int32_t iRet =
-      pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Validate);
-  return CJS_Return(runtime->NewBoolean(iRet != XFA_EVENTERROR_Error));
+  XFA_EventError iRet = pNotify->ExecEventByDeepFirst(
+      GetXFANode(), XFA_EVENT_Validate, false, true);
+  return CJS_Result::Success(
+      runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
 void CJX_Subform::locale(CFXJSE_Value* pValue,
@@ -87,143 +96,36 @@
   }
 
   WideString wsLocaleName = GetXFANode()->GetLocaleName().value_or(L"");
-  pValue->SetString(wsLocaleName.UTF8Encode().AsStringView());
+  pValue->SetString(wsLocaleName.ToUTF8().AsStringView());
 }
 
-void CJX_Subform::instanceIndex(CFXJSE_Value* pValue,
-                                bool bSetting,
-                                XFA_Attribute eAttribute) {
-  Script_Som_InstanceIndex(pValue, bSetting, eAttribute);
-}
+void CJX_Subform::instanceManager(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
 
-void CJX_Subform::allowMacro(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
+  WideString wsName = GetCData(XFA_Attribute::Name);
+  CXFA_Node* pInstanceMgr = nullptr;
+  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
+       pNode = pNode->GetPrevSibling()) {
+    if (pNode->GetElementType() == XFA_Element::InstanceManager) {
+      WideString wsInstMgrName =
+          pNode->JSObject()->GetCData(XFA_Attribute::Name);
+      if (wsInstMgrName.GetLength() >= 1 && wsInstMgrName[0] == '_' &&
+          wsInstMgrName.Last(wsInstMgrName.GetLength() - 1) == wsName) {
+        pInstanceMgr = pNode;
+      }
+      break;
+    }
+  }
+  if (!pInstanceMgr) {
+    pValue->SetNull();
+    return;
+  }
 
-void CJX_Subform::anchorType(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::colSpan(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::columnWidths(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::h(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::hAlign(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::layout(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::maxH(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::maxW(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::minH(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::minW(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::presence(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::relevant(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::restoreState(CFXJSE_Value* pValue,
-                               bool bSetting,
-                               XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::scope(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::validationMessage(CFXJSE_Value* pValue,
-                                    bool bSetting,
-                                    XFA_Attribute eAttribute) {
-  Script_Som_ValidationMessage(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::vAlign(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::w(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::x(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Subform::y(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+      pInstanceMgr));
 }
diff --git a/fxjs/xfa/cjx_subform.h b/fxjs/xfa/cjx_subform.h
index a1c535b..83ac66e 100644
--- a/fxjs/xfa/cjx_subform.h
+++ b/fxjs/xfa/cjx_subform.h
@@ -7,47 +7,32 @@
 #ifndef FXJS_XFA_CJX_SUBFORM_H_
 #define FXJS_XFA_CJX_SUBFORM_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_container.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Delta;
 
-class CJX_Subform : public CJX_Container {
+class CJX_Subform final : public CJX_Container {
  public:
   explicit CJX_Subform(CXFA_Node* container);
   ~CJX_Subform() override;
 
-  JS_METHOD(execCalculate, CJX_Subform);
-  JS_METHOD(execEvent, CJX_Subform);
-  JS_METHOD(execInitialize, CJX_Subform);
-  JS_METHOD(execValidate, CJX_Subform);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(allowMacro);
-  JS_PROP(anchorType);
-  JS_PROP(colSpan);
-  JS_PROP(columnWidths);
-  JS_PROP(h);
-  JS_PROP(hAlign);
-  JS_PROP(instanceIndex);
-  JS_PROP(layout);
-  JS_PROP(locale);
-  JS_PROP(maxH);
-  JS_PROP(maxW);
-  JS_PROP(minH);
-  JS_PROP(minW);
-  JS_PROP(presence);
-  JS_PROP(relevant);
-  JS_PROP(restoreState);
-  JS_PROP(scope);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(validationMessage);
-  JS_PROP(vAlign);
-  JS_PROP(w);
-  JS_PROP(x);
-  JS_PROP(y);
+  JSE_METHOD(execCalculate);
+  JSE_METHOD(execEvent);
+  JSE_METHOD(execInitialize);
+  JSE_METHOD(execValidate);
+
+  JSE_PROP(instanceManager);
+  JSE_PROP(locale);
 
  private:
+  using Type__ = CJX_Subform;
+  using ParentType__ = CJX_Container;
+
+  static const TypeTag static_type__ = TypeTag::Subform;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_subformset.cpp b/fxjs/xfa/cjx_subformset.cpp
deleted file mode 100644
index cd36abb..0000000
--- a/fxjs/xfa/cjx_subformset.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_subformset.h"
-
-#include "xfa/fxfa/parser/cxfa_subformset.h"
-
-CJX_SubformSet::CJX_SubformSet(CXFA_SubformSet* node) : CJX_Container(node) {}
-
-CJX_SubformSet::~CJX_SubformSet() = default;
-
-void CJX_SubformSet::use(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SubformSet::relation(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SubformSet::relevant(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SubformSet::usehref(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_SubformSet::instanceIndex(CFXJSE_Value* pValue,
-                                   bool bSetting,
-                                   XFA_Attribute eAttribute) {
-  Script_Som_InstanceIndex(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_subformset.h b/fxjs/xfa/cjx_subformset.h
deleted file mode 100644
index 589b44d..0000000
--- a/fxjs/xfa/cjx_subformset.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SUBFORMSET_H_
-#define FXJS_XFA_CJX_SUBFORMSET_H_
-
-#include "fxjs/xfa/cjx_container.h"
-
-class CXFA_SubformSet;
-
-class CJX_SubformSet : public CJX_Container {
- public:
-  explicit CJX_SubformSet(CXFA_SubformSet* node);
-  ~CJX_SubformSet() override;
-
-  JS_PROP(instanceIndex);
-  JS_PROP(relation);
-  JS_PROP(relevant);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_SUBFORMSET_H_
diff --git a/fxjs/xfa/cjx_subjectdn.cpp b/fxjs/xfa/cjx_subjectdn.cpp
deleted file mode 100644
index 0a5b4ab..0000000
--- a/fxjs/xfa/cjx_subjectdn.cpp
+++ /dev/null
@@ -1,19 +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 "fxjs/xfa/cjx_subjectdn.h"
-
-#include "xfa/fxfa/parser/cxfa_subjectdn.h"
-
-CJX_SubjectDN::CJX_SubjectDN(CXFA_SubjectDN* node) : CJX_Node(node) {}
-
-CJX_SubjectDN::~CJX_SubjectDN() = default;
-
-void CJX_SubjectDN::delimiter(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_subjectdn.h b/fxjs/xfa/cjx_subjectdn.h
deleted file mode 100644
index d13218b..0000000
--- a/fxjs/xfa/cjx_subjectdn.h
+++ /dev/null
@@ -1,22 +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 FXJS_XFA_CJX_SUBJECTDN_H_
-#define FXJS_XFA_CJX_SUBJECTDN_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_SubjectDN;
-
-class CJX_SubjectDN : public CJX_Node {
- public:
-  explicit CJX_SubjectDN(CXFA_SubjectDN* node);
-  ~CJX_SubjectDN() override;
-
-  JS_PROP(delimiter);
-};
-
-#endif  // FXJS_XFA_CJX_SUBJECTDN_H_
diff --git a/fxjs/xfa/cjx_subjectdns.cpp b/fxjs/xfa/cjx_subjectdns.cpp
deleted file mode 100644
index e7d9db5..0000000
--- a/fxjs/xfa/cjx_subjectdns.cpp
+++ /dev/null
@@ -1,19 +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 "fxjs/xfa/cjx_subjectdns.h"
-
-#include "xfa/fxfa/parser/cxfa_subjectdns.h"
-
-CJX_SubjectDNs::CJX_SubjectDNs(CXFA_SubjectDNs* node) : CJX_Node(node) {}
-
-CJX_SubjectDNs::~CJX_SubjectDNs() = default;
-
-void CJX_SubjectDNs::type(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_subjectdns.h b/fxjs/xfa/cjx_subjectdns.h
deleted file mode 100644
index 104d826..0000000
--- a/fxjs/xfa/cjx_subjectdns.h
+++ /dev/null
@@ -1,22 +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 FXJS_XFA_CJX_SUBJECTDNS_H_
-#define FXJS_XFA_CJX_SUBJECTDNS_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_SubjectDNs;
-
-class CJX_SubjectDNs : public CJX_Node {
- public:
-  explicit CJX_SubjectDNs(CXFA_SubjectDNs* node);
-  ~CJX_SubjectDNs() override;
-
-  JS_PROP(type);
-};
-
-#endif  // FXJS_XFA_CJX_SUBJECTDNS_H_
diff --git a/fxjs/xfa/cjx_submit.cpp b/fxjs/xfa/cjx_submit.cpp
deleted file mode 100644
index 292e432..0000000
--- a/fxjs/xfa/cjx_submit.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_submit.h"
-
-#include "xfa/fxfa/parser/cxfa_submit.h"
-
-CJX_Submit::CJX_Submit(CXFA_Submit* node) : CJX_Node(node) {}
-
-CJX_Submit::~CJX_Submit() = default;
-
-void CJX_Submit::format(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Submit::embedPDF(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Submit::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Submit::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Submit::target(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Submit::textEncoding(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Submit::xdpContent(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_submit.h b/fxjs/xfa/cjx_submit.h
deleted file mode 100644
index aefec32..0000000
--- a/fxjs/xfa/cjx_submit.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_SUBMIT_H_
-#define FXJS_XFA_CJX_SUBMIT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Submit;
-
-class CJX_Submit : public CJX_Node {
- public:
-  explicit CJX_Submit(CXFA_Submit* node);
-  ~CJX_Submit() override;
-
-  JS_PROP(embedPDF);
-  JS_PROP(format);
-  JS_PROP(target);
-  JS_PROP(textEncoding);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(xdpContent);
-};
-
-#endif  // FXJS_XFA_CJX_SUBMIT_H_
diff --git a/fxjs/xfa/cjx_template.cpp b/fxjs/xfa/cjx_template.cpp
index 816c9d4..b3d9d90 100644
--- a/fxjs/xfa/cjx_template.cpp
+++ b/fxjs/xfa/cjx_template.cpp
@@ -8,8 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_template.h"
 
@@ -22,60 +23,69 @@
     {"remerge", remerge_static}};
 
 CJX_Template::CJX_Template(CXFA_Template* tmpl) : CJX_Model(tmpl) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Template::~CJX_Template() {}
 
-CJS_Return CJX_Template::formNodes(
-    CJS_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params) {
-  if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewBoolean(true));
+bool CJX_Template::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_Template::remerge(
-    CJS_V8* runtime,
+CJS_Result CJX_Template::formNodes(
+    CFX_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(runtime->NewBoolean(true));
+}
+
+CJS_Result CJX_Template::remerge(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   GetDocument()->DoDataRemerge(true);
-  return CJS_Return(true);
+  return CJS_Result::Success();
 }
 
-CJS_Return CJX_Template::execInitialize(
-    CJS_V8* runtime,
+CJS_Result CJX_Template::execInitialize(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(
-      runtime->NewBoolean(!!ToNode(GetXFAObject())->GetWidgetAcc()));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(
+      runtime->NewBoolean(GetXFANode()->IsWidgetReady()));
 }
 
-CJS_Return CJX_Template::recalculate(
-    CJS_V8* runtime,
+CJS_Result CJX_Template::recalculate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewBoolean(true));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(runtime->NewBoolean(true));
 }
 
-CJS_Return CJX_Template::execCalculate(
-    CJS_V8* runtime,
+CJS_Result CJX_Template::execCalculate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(
-      runtime->NewBoolean(!!ToNode(GetXFAObject())->GetWidgetAcc()));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(
+      runtime->NewBoolean(GetXFANode()->IsWidgetReady()));
 }
 
-CJS_Return CJX_Template::execValidate(
-    CJS_V8* runtime,
+CJS_Result CJX_Template::execValidate(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(
-      runtime->NewBoolean(!!ToNode(GetXFAObject())->GetWidgetAcc()));
+    return CJS_Result::Failure(JSMessage::kParamError);
+
+  return CJS_Result::Success(
+      runtime->NewBoolean(GetXFANode()->IsWidgetReady()));
 }
diff --git a/fxjs/xfa/cjx_template.h b/fxjs/xfa/cjx_template.h
index 2c82788..d396459 100644
--- a/fxjs/xfa/cjx_template.h
+++ b/fxjs/xfa/cjx_template.h
@@ -7,27 +7,34 @@
 #ifndef FXJS_XFA_CJX_TEMPLATE_H_
 #define FXJS_XFA_CJX_TEMPLATE_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_model.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Template;
 
-class CJX_Template : public CJX_Model {
+class CJX_Template final : public CJX_Model {
  public:
   explicit CJX_Template(CXFA_Template* tmpl);
   ~CJX_Template() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   /* The docs list a |createNode| method on Template but that method already
    * exists on Model, also the |createNode| docs say it exists on Model not
    * on Template so I'm leaving |createNode| out of the template methods. */
-  JS_METHOD(execCalculate, CJX_Template);
-  JS_METHOD(execInitialize, CJX_Template);
-  JS_METHOD(execValidate, CJX_Template);
-  JS_METHOD(formNodes, CJX_Template);
-  JS_METHOD(recalculate, CJX_Template);
-  JS_METHOD(remerge, CJX_Template);
+  JSE_METHOD(execCalculate);
+  JSE_METHOD(execInitialize);
+  JSE_METHOD(execValidate);
+  JSE_METHOD(formNodes);
+  JSE_METHOD(recalculate);
+  JSE_METHOD(remerge);
 
  private:
+  using Type__ = CJX_Template;
+  using ParentType__ = CJX_Model;
+
+  static const TypeTag static_type__ = TypeTag::Template;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_text.cpp b/fxjs/xfa/cjx_text.cpp
deleted file mode 100644
index eda2ff8..0000000
--- a/fxjs/xfa/cjx_text.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_text.h"
-
-#include "xfa/fxfa/parser/cxfa_text.h"
-
-CJX_Text::CJX_Text(CXFA_Text* node) : CJX_Content(node) {}
-
-CJX_Text::~CJX_Text() = default;
-
-void CJX_Text::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Text::maxChars(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Text::defaultValue(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Text::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Text::value(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_text.h b/fxjs/xfa/cjx_text.h
deleted file mode 100644
index 386a7a9..0000000
--- a/fxjs/xfa/cjx_text.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_TEXT_H_
-#define FXJS_XFA_CJX_TEXT_H_
-
-#include "fxjs/xfa/cjx_content.h"
-
-class CXFA_Text;
-
-class CJX_Text : public CJX_Content {
- public:
-  explicit CJX_Text(CXFA_Text* node);
-  ~CJX_Text() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(maxChars);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_TEXT_H_
diff --git a/fxjs/xfa/cjx_textedit.cpp b/fxjs/xfa/cjx_textedit.cpp
deleted file mode 100644
index e1a6faa..0000000
--- a/fxjs/xfa/cjx_textedit.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "fxjs/xfa/cjx_textedit.h"
-
-#include "xfa/fxfa/parser/cxfa_textedit.h"
-
-CJX_TextEdit::CJX_TextEdit(CXFA_TextEdit* node) : CJX_Node(node) {}
-
-CJX_TextEdit::~CJX_TextEdit() = default;
-
-void CJX_TextEdit::vScrollPolicy(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TextEdit::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TextEdit::allowRichText(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TextEdit::multiLine(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TextEdit::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TextEdit::hScrollPolicy(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_textedit.h b/fxjs/xfa/cjx_textedit.h
deleted file mode 100644
index c7f42c9..0000000
--- a/fxjs/xfa/cjx_textedit.h
+++ /dev/null
@@ -1,27 +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 FXJS_XFA_CJX_TEXTEDIT_H_
-#define FXJS_XFA_CJX_TEXTEDIT_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_TextEdit;
-
-class CJX_TextEdit : public CJX_Node {
- public:
-  explicit CJX_TextEdit(CXFA_TextEdit* node);
-  ~CJX_TextEdit() override;
-
-  JS_PROP(allowRichText);
-  JS_PROP(hScrollPolicy);
-  JS_PROP(multiLine);
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(vScrollPolicy);
-};
-
-#endif  // FXJS_XFA_CJX_TEXTEDIT_H_
diff --git a/fxjs/xfa/cjx_textnode.cpp b/fxjs/xfa/cjx_textnode.cpp
index 133510c..756a71d 100644
--- a/fxjs/xfa/cjx_textnode.cpp
+++ b/fxjs/xfa/cjx_textnode.cpp
@@ -6,21 +6,25 @@
 
 #include "fxjs/xfa/cjx_textnode.h"
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 CJX_TextNode::CJX_TextNode(CXFA_Node* node) : CJX_Node(node) {}
 
 CJX_TextNode::~CJX_TextNode() {}
 
+bool CJX_TextNode::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_TextNode::defaultValue(CFXJSE_Value* pValue,
                                 bool bSetting,
                                 XFA_Attribute attr) {
-  Script_Som_DefaultValue(pValue, bSetting, attr);
+  ScriptSomDefaultValue(pValue, bSetting, attr);
 }
 
 void CJX_TextNode::value(CFXJSE_Value* pValue,
                          bool bSetting,
                          XFA_Attribute attr) {
-  Script_Som_DefaultValue(pValue, bSetting, attr);
+  ScriptSomDefaultValue(pValue, bSetting, attr);
 }
diff --git a/fxjs/xfa/cjx_textnode.h b/fxjs/xfa/cjx_textnode.h
index 12d44d7..6d74658 100644
--- a/fxjs/xfa/cjx_textnode.h
+++ b/fxjs/xfa/cjx_textnode.h
@@ -7,8 +7,8 @@
 #ifndef FXJS_XFA_CJX_TEXTNODE_H_
 #define FXJS_XFA_CJX_TEXTNODE_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Node;
 
@@ -17,8 +17,17 @@
   explicit CJX_TextNode(CXFA_Node* node);
   ~CJX_TextNode() override;
 
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(value);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(defaultValue); /* {default} */
+  JSE_PROP(value);
+
+ private:
+  using Type__ = CJX_TextNode;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::TextNode;
 };
 
 #endif  // FXJS_XFA_CJX_TEXTNODE_H_
diff --git a/fxjs/xfa/cjx_time.cpp b/fxjs/xfa/cjx_time.cpp
deleted file mode 100644
index 74855fd..0000000
--- a/fxjs/xfa/cjx_time.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_time.h"
-
-#include "xfa/fxfa/parser/cxfa_time.h"
-
-CJX_Time::CJX_Time(CXFA_Time* node) : CJX_Content(node) {}
-
-CJX_Time::~CJX_Time() = default;
-
-void CJX_Time::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Time::defaultValue(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
-
-void CJX_Time::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Time::value(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Som_DefaultValue(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_time.h b/fxjs/xfa/cjx_time.h
deleted file mode 100644
index 95b05cc..0000000
--- a/fxjs/xfa/cjx_time.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_TIME_H_
-#define FXJS_XFA_CJX_TIME_H_
-
-#include "fxjs/xfa/cjx_content.h"
-
-class CXFA_Time;
-
-class CJX_Time : public CJX_Content {
- public:
-  explicit CJX_Time(CXFA_Time* node);
-  ~CJX_Time() override;
-
-  JS_PROP(defaultValue); /* {default} */
-  JS_PROP(use);
-  JS_PROP(usehref);
-  JS_PROP(value);
-};
-
-#endif  // FXJS_XFA_CJX_TIME_H_
diff --git a/fxjs/xfa/cjx_timestamp.cpp b/fxjs/xfa/cjx_timestamp.cpp
deleted file mode 100644
index 011d696..0000000
--- a/fxjs/xfa/cjx_timestamp.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_timestamp.h"
-
-#include "xfa/fxfa/parser/cxfa_timestamp.h"
-
-CJX_TimeStamp::CJX_TimeStamp(CXFA_TimeStamp* node) : CJX_Node(node) {}
-
-CJX_TimeStamp::~CJX_TimeStamp() = default;
-
-void CJX_TimeStamp::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TimeStamp::type(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TimeStamp::server(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_TimeStamp::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_timestamp.h b/fxjs/xfa/cjx_timestamp.h
deleted file mode 100644
index ad7ab02..0000000
--- a/fxjs/xfa/cjx_timestamp.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_TIMESTAMP_H_
-#define FXJS_XFA_CJX_TIMESTAMP_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_TimeStamp;
-
-class CJX_TimeStamp : public CJX_Node {
- public:
-  explicit CJX_TimeStamp(CXFA_TimeStamp* node);
-  ~CJX_TimeStamp() override;
-
-  JS_PROP(server);
-  JS_PROP(type);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_TIMESTAMP_H_
diff --git a/fxjs/xfa/cjx_tooltip.cpp b/fxjs/xfa/cjx_tooltip.cpp
deleted file mode 100644
index 5d6e242..0000000
--- a/fxjs/xfa/cjx_tooltip.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_tooltip.h"
-
-#include "xfa/fxfa/parser/cxfa_tooltip.h"
-
-CJX_ToolTip::CJX_ToolTip(CXFA_ToolTip* node) : CJX_TextNode(node) {}
-
-CJX_ToolTip::~CJX_ToolTip() = default;
-
-void CJX_ToolTip::use(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_ToolTip::usehref(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_tooltip.h b/fxjs/xfa/cjx_tooltip.h
deleted file mode 100644
index 178d11b..0000000
--- a/fxjs/xfa/cjx_tooltip.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_TOOLTIP_H_
-#define FXJS_XFA_CJX_TOOLTIP_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_ToolTip;
-
-class CJX_ToolTip : public CJX_TextNode {
- public:
-  explicit CJX_ToolTip(CXFA_ToolTip* node);
-  ~CJX_ToolTip() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_TOOLTIP_H_
diff --git a/fxjs/xfa/cjx_traversal.cpp b/fxjs/xfa/cjx_traversal.cpp
deleted file mode 100644
index ffdcd35..0000000
--- a/fxjs/xfa/cjx_traversal.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_traversal.h"
-
-#include "xfa/fxfa/parser/cxfa_traversal.h"
-
-CJX_Traversal::CJX_Traversal(CXFA_Traversal* node) : CJX_Node(node) {}
-
-CJX_Traversal::~CJX_Traversal() = default;
-
-void CJX_Traversal::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Traversal::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_traversal.h b/fxjs/xfa/cjx_traversal.h
deleted file mode 100644
index d9072f3..0000000
--- a/fxjs/xfa/cjx_traversal.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_TRAVERSAL_H_
-#define FXJS_XFA_CJX_TRAVERSAL_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Traversal;
-
-class CJX_Traversal : public CJX_Node {
- public:
-  explicit CJX_Traversal(CXFA_Traversal* node);
-  ~CJX_Traversal() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_TRAVERSAL_H_
diff --git a/fxjs/xfa/cjx_traverse.cpp b/fxjs/xfa/cjx_traverse.cpp
deleted file mode 100644
index f0d5345..0000000
--- a/fxjs/xfa/cjx_traverse.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_traverse.h"
-
-#include "xfa/fxfa/parser/cxfa_traverse.h"
-
-CJX_Traverse::CJX_Traverse(CXFA_Traverse* node) : CJX_Node(node) {}
-
-CJX_Traverse::~CJX_Traverse() = default;
-
-void CJX_Traverse::ref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Traverse::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Traverse::operation(CFXJSE_Value* pValue,
-                             bool bSetting,
-                             XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Traverse::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_traverse.h b/fxjs/xfa/cjx_traverse.h
deleted file mode 100644
index 2eef88e..0000000
--- a/fxjs/xfa/cjx_traverse.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_TRAVERSE_H_
-#define FXJS_XFA_CJX_TRAVERSE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Traverse;
-
-class CJX_Traverse : public CJX_Node {
- public:
-  explicit CJX_Traverse(CXFA_Traverse* node);
-  ~CJX_Traverse() override;
-
-  JS_PROP(operation);
-  JS_PROP(ref);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_TRAVERSE_H_
diff --git a/fxjs/xfa/cjx_tree.cpp b/fxjs/xfa/cjx_tree.cpp
index e20115b..cc13b7a 100644
--- a/fxjs/xfa/cjx_tree.cpp
+++ b/fxjs/xfa/cjx_tree.cpp
@@ -8,9 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
 #include "xfa/fxfa/parser/cxfa_attachnodelist.h"
@@ -24,22 +24,23 @@
     {"resolveNodes", resolveNodes_static}};
 
 CJX_Tree::CJX_Tree(CXFA_Object* obj) : CJX_Object(obj) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_Tree::~CJX_Tree() {}
 
-CJS_Return CJX_Tree::resolveNode(
-    CJS_V8* runtime,
+bool CJX_Tree::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_Tree::resolveNode(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  if (!pScriptContext)
-    return CJS_Return(true);
-
   CXFA_Object* refNode = GetXFAObject();
   if (refNode->GetElementType() == XFA_Element::Xfa)
     refNode = pScriptContext->GetThisObject();
@@ -51,54 +52,50 @@
   if (!pScriptContext->ResolveObjects(ToNode(refNode),
                                       expression.AsStringView(), &resolveNodeRS,
                                       dwFlag, nullptr)) {
-    return CJS_Return(runtime->NewNull());
+    return CJS_Result::Success(runtime->NewNull());
   }
 
   if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    CXFA_Object* pObject = resolveNodeRS.objects.front();
+    CXFA_Object* pObject = resolveNodeRS.objects.front().Get();
     CFXJSE_Value* value =
-        GetDocument()->GetScriptContext()->GetJSValueFromMap(pObject);
-    if (!value)
-      return CJS_Return(runtime->NewNull());
+        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pObject);
 
-    return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+    return CJS_Result::Success(
+        value->DirectGetValue().Get(runtime->GetIsolate()));
   }
 
-  const XFA_SCRIPTATTRIBUTEINFO* lpAttributeInfo =
-      resolveNodeRS.pScriptAttribute;
-  if (!lpAttributeInfo ||
-      lpAttributeInfo->eValueType != XFA_ScriptType::Object) {
-    return CJS_Return(runtime->NewNull());
+  if (!resolveNodeRS.script_attribute.callback ||
+      resolveNodeRS.script_attribute.eValueType != XFA_ScriptType::Object) {
+    return CJS_Result::Success(runtime->NewNull());
   }
 
   auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
   CJX_Object* jsObject = resolveNodeRS.objects.front()->JSObject();
-  (jsObject->*(lpAttributeInfo->callback))(pValue.get(), false,
-                                           lpAttributeInfo->attribute);
-  return CJS_Return(pValue->DirectGetValue().Get(runtime->GetIsolate()));
+  (*resolveNodeRS.script_attribute.callback)(
+      jsObject, pValue.get(), false, resolveNodeRS.script_attribute.attribute);
+  return CJS_Result::Success(
+      pValue->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
-CJS_Return CJX_Tree::resolveNodes(
-    CJS_V8* runtime,
+CJS_Result CJX_Tree::resolveNodes(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_Object* refNode = GetXFAObject();
   if (refNode->GetElementType() == XFA_Element::Xfa)
     refNode = GetDocument()->GetScriptContext()->GetThisObject();
 
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  if (!pScriptContext)
-    return CJS_Return(true);
-
   auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
   ResolveNodeList(pValue.get(), runtime->ToWideString(params[0]),
                   XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
                       XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
                       XFA_RESOLVENODE_Siblings,
                   ToNode(refNode));
-  return CJS_Return(pValue->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(
+      pValue->DirectGetValue().Get(runtime->GetIsolate()));
 }
 
 void CJX_Tree::all(CFXJSE_Value* pValue,
@@ -122,33 +119,25 @@
     return;
   }
 
-  WideString wsExpression = L"#" + GetXFAObject()->GetClassName() + L"[*]";
+  WideString wsExpression =
+      L"#" + WideString::FromASCII(GetXFAObject()->GetClassName()) + L"[*]";
   ResolveNodeList(pValue, wsExpression,
                   XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_ALL, nullptr);
 }
 
-void CJX_Tree::name(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
 void CJX_Tree::nodes(CFXJSE_Value* pValue,
                      bool bSetting,
                      XFA_Attribute eAttribute) {
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  if (!pScriptContext)
-    return;
-
   if (bSetting) {
     WideString wsMessage = L"Unable to set ";
-    FXJSE_ThrowMessage(wsMessage.UTF8Encode().AsStringView());
+    FXJSE_ThrowMessage(wsMessage.ToUTF8().AsStringView());
     return;
   }
 
+  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_AttachNodeList* pNodeList =
       new CXFA_AttachNodeList(GetDocument(), ToNode(GetXFAObject()));
-  pValue->SetObject(pNodeList, pScriptContext->GetJseNormalClass());
+  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
 }
 
 void CJX_Tree::parent(CFXJSE_Value* pValue,
@@ -165,7 +154,8 @@
     return;
   }
 
-  pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(pParent));
+  pValue->Assign(
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pParent));
 }
 
 void CJX_Tree::index(CFXJSE_Value* pValue,
@@ -175,7 +165,10 @@
     ThrowInvalidPropertyException();
     return;
   }
-  pValue->SetInteger(ToNode(GetXFAObject())->GetNodeSameNameIndex());
+
+  CXFA_Node* pNode = ToNode(GetXFAObject());
+  size_t iIndex = pNode ? pNode->GetIndexByName() : 0;
+  pValue->SetInteger(pdfium::base::checked_cast<int32_t>(iIndex));
 }
 
 void CJX_Tree::classIndex(CFXJSE_Value* pValue,
@@ -185,7 +178,10 @@
     ThrowInvalidPropertyException();
     return;
   }
-  pValue->SetInteger(ToNode(GetXFAObject())->GetNodeSameClassIndex());
+
+  CXFA_Node* pNode = ToNode(GetXFAObject());
+  size_t iIndex = pNode ? pNode->GetIndexByClassName() : 0;
+  pValue->SetInteger(pdfium::base::checked_cast<int32_t>(iIndex));
 }
 
 void CJX_Tree::somExpression(CFXJSE_Value* pValue,
@@ -197,43 +193,41 @@
   }
 
   WideString wsSOMExpression = GetXFAObject()->GetSOMExpression();
-  pValue->SetString(wsSOMExpression.UTF8Encode().AsStringView());
+  pValue->SetString(wsSOMExpression.ToUTF8().AsStringView());
 }
 
 void CJX_Tree::ResolveNodeList(CFXJSE_Value* pValue,
                                WideString wsExpression,
                                uint32_t dwFlag,
                                CXFA_Node* refNode) {
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  if (!pScriptContext)
-    return;
   if (!refNode)
     refNode = ToNode(GetXFAObject());
 
   XFA_RESOLVENODE_RS resolveNodeRS;
+  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(),
                                  &resolveNodeRS, dwFlag, nullptr);
   CXFA_ArrayNodeList* pNodeList = new CXFA_ArrayNodeList(GetDocument());
   if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    for (CXFA_Object* pObject : resolveNodeRS.objects) {
+    for (auto& pObject : resolveNodeRS.objects) {
       if (pObject->IsNode())
         pNodeList->Append(pObject->AsNode());
     }
   } else {
-    if (resolveNodeRS.pScriptAttribute &&
-        resolveNodeRS.pScriptAttribute->eValueType == XFA_ScriptType::Object) {
-      for (CXFA_Object* pObject : resolveNodeRS.objects) {
-        auto pValue =
+    if (resolveNodeRS.script_attribute.callback &&
+        resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
+      for (auto& pObject : resolveNodeRS.objects) {
+        auto innerValue =
             pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
         CJX_Object* jsObject = pObject->JSObject();
-        (jsObject->*(resolveNodeRS.pScriptAttribute->callback))(
-            pValue.get(), false, resolveNodeRS.pScriptAttribute->attribute);
-
-        CXFA_Object* obj = CFXJSE_Engine::ToObject(pValue.get(), nullptr);
+        (*resolveNodeRS.script_attribute.callback)(
+            jsObject, innerValue.get(), false,
+            resolveNodeRS.script_attribute.attribute);
+        CXFA_Object* obj = CFXJSE_Engine::ToObject(innerValue.get());
         if (obj->IsNode())
           pNodeList->Append(obj->AsNode());
       }
     }
   }
-  pValue->SetObject(pNodeList, pScriptContext->GetJseNormalClass());
+  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
 }
diff --git a/fxjs/xfa/cjx_tree.h b/fxjs/xfa/cjx_tree.h
index 9389d2e..cbef888 100644
--- a/fxjs/xfa/cjx_tree.h
+++ b/fxjs/xfa/cjx_tree.h
@@ -7,8 +7,8 @@
 #ifndef FXJS_XFA_CJX_TREE_H_
 #define FXJS_XFA_CJX_TREE_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Object;
 class CXFA_Node;
@@ -18,25 +18,31 @@
   explicit CJX_Tree(CXFA_Object* obj);
   ~CJX_Tree() override;
 
-  JS_METHOD(resolveNode, CJX_Tree);
-  JS_METHOD(resolveNodes, CJX_Tree);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(all);
-  JS_PROP(classAll);
-  JS_PROP(classIndex);
-  JS_PROP(index);
-  JS_PROP(name);
-  JS_PROP(nodes);
-  JS_PROP(parent);
-  JS_PROP(somExpression);
+  JSE_METHOD(resolveNode);
+  JSE_METHOD(resolveNodes);
+
+  JSE_PROP(all);
+  JSE_PROP(classAll);
+  JSE_PROP(classIndex);
+  JSE_PROP(index);
+  JSE_PROP(nodes);
+  JSE_PROP(parent);
+  JSE_PROP(somExpression);
 
  private:
+  using Type__ = CJX_Tree;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::Tree;
+  static const CJX_MethodSpec MethodSpecs[];
+
   void ResolveNodeList(CFXJSE_Value* pValue,
                        WideString wsExpression,
                        uint32_t dwFlag,
                        CXFA_Node* refNode);
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_TREE_H_
diff --git a/fxjs/xfa/cjx_treelist.cpp b/fxjs/xfa/cjx_treelist.cpp
index b91a0b7..51bbb0a 100644
--- a/fxjs/xfa/cjx_treelist.cpp
+++ b/fxjs/xfa/cjx_treelist.cpp
@@ -8,9 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_treelist.h"
@@ -19,30 +19,33 @@
     {"namedItem", namedItem_static}};
 
 CJX_TreeList::CJX_TreeList(CXFA_TreeList* list) : CJX_List(list) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_TreeList::~CJX_TreeList() {}
 
-CXFA_TreeList* CJX_TreeList::GetXFATreeList() {
-  return static_cast<CXFA_TreeList*>(GetXFAObject());
+bool CJX_TreeList::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Return CJX_TreeList::namedItem(
-    CJS_V8* runtime,
+CXFA_TreeList* CJX_TreeList::GetXFATreeList() {
+  return ToTreeList(GetXFAObject());
+}
+
+CJS_Result CJX_TreeList::namedItem(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+    return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_Node* pNode = GetXFATreeList()->NamedItem(
       runtime->ToWideString(params[0]).AsStringView());
   if (!pNode)
-    return CJS_Return(true);
+    return CJS_Result::Success();
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
-  if (!value)
-    return CJS_Return(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
 
-  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(
+      value->DirectGetValue().Get(runtime->GetIsolate()));
 }
diff --git a/fxjs/xfa/cjx_treelist.h b/fxjs/xfa/cjx_treelist.h
index 29a95e6..294ab26 100644
--- a/fxjs/xfa/cjx_treelist.h
+++ b/fxjs/xfa/cjx_treelist.h
@@ -7,22 +7,29 @@
 #ifndef FXJS_XFA_CJX_TREELIST_H_
 #define FXJS_XFA_CJX_TREELIST_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_list.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_TreeList;
 
-class CJX_TreeList : public CJX_List {
+class CJX_TreeList final : public CJX_List {
  public:
   explicit CJX_TreeList(CXFA_TreeList* list);
   ~CJX_TreeList() override;
 
-  JS_METHOD(namedItem, CJX_TreeList);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_METHOD(namedItem);
 
  private:
-  CXFA_TreeList* GetXFATreeList();
+  using Type__ = CJX_TreeList;
+  using ParentType__ = CJX_List;
 
+  static const TypeTag static_type__ = TypeTag::TreeList;
   static const CJX_MethodSpec MethodSpecs[];
+
+  CXFA_TreeList* GetXFATreeList();
 };
 
 #endif  // FXJS_XFA_CJX_TREELIST_H_
diff --git a/fxjs/xfa/cjx_ui.cpp b/fxjs/xfa/cjx_ui.cpp
deleted file mode 100644
index 90db789..0000000
--- a/fxjs/xfa/cjx_ui.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_ui.h"
-
-#include "xfa/fxfa/parser/cxfa_ui.h"
-
-CJX_Ui::CJX_Ui(CXFA_Ui* node) : CJX_Node(node) {}
-
-CJX_Ui::~CJX_Ui() = default;
-
-void CJX_Ui::use(CFXJSE_Value* pValue,
-                 bool bSetting,
-                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Ui::usehref(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_ui.h b/fxjs/xfa/cjx_ui.h
deleted file mode 100644
index e82b1de..0000000
--- a/fxjs/xfa/cjx_ui.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_UI_H_
-#define FXJS_XFA_CJX_UI_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Ui;
-
-class CJX_Ui : public CJX_Node {
- public:
-  explicit CJX_Ui(CXFA_Ui* node);
-  ~CJX_Ui() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_UI_H_
diff --git a/fxjs/xfa/cjx_update.cpp b/fxjs/xfa/cjx_update.cpp
deleted file mode 100644
index e5d1ef2..0000000
--- a/fxjs/xfa/cjx_update.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_update.h"
-
-#include "xfa/fxfa/parser/cxfa_update.h"
-
-CJX_Update::CJX_Update(CXFA_Update* node) : CJX_TextNode(node) {}
-
-CJX_Update::~CJX_Update() = default;
-
-void CJX_Update::use(CFXJSE_Value* pValue,
-                     bool bSetting,
-                     XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Update::usehref(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_update.h b/fxjs/xfa/cjx_update.h
deleted file mode 100644
index c1acb4b..0000000
--- a/fxjs/xfa/cjx_update.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_UPDATE_H_
-#define FXJS_XFA_CJX_UPDATE_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Update;
-
-class CJX_Update : public CJX_TextNode {
- public:
-  explicit CJX_Update(CXFA_Update* node);
-  ~CJX_Update() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_UPDATE_H_
diff --git a/fxjs/xfa/cjx_uri.cpp b/fxjs/xfa/cjx_uri.cpp
deleted file mode 100644
index 6ffbb68..0000000
--- a/fxjs/xfa/cjx_uri.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_uri.h"
-
-#include "xfa/fxfa/parser/cxfa_uri.h"
-
-CJX_Uri::CJX_Uri(CXFA_Uri* node) : CJX_TextNode(node) {}
-
-CJX_Uri::~CJX_Uri() = default;
-
-void CJX_Uri::use(CFXJSE_Value* pValue,
-                  bool bSetting,
-                  XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Uri::usehref(CFXJSE_Value* pValue,
-                      bool bSetting,
-                      XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_uri.h b/fxjs/xfa/cjx_uri.h
deleted file mode 100644
index 805bc5c..0000000
--- a/fxjs/xfa/cjx_uri.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_URI_H_
-#define FXJS_XFA_CJX_URI_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_Uri;
-
-class CJX_Uri : public CJX_TextNode {
- public:
-  explicit CJX_Uri(CXFA_Uri* node);
-  ~CJX_Uri() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_URI_H_
diff --git a/fxjs/xfa/cjx_user.cpp b/fxjs/xfa/cjx_user.cpp
deleted file mode 100644
index a42f927..0000000
--- a/fxjs/xfa/cjx_user.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_user.h"
-
-#include "xfa/fxfa/parser/cxfa_user.h"
-
-CJX_User::CJX_User(CXFA_User* node) : CJX_TextNode(node) {}
-
-CJX_User::~CJX_User() = default;
-
-void CJX_User::use(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_User::usehref(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_user.h b/fxjs/xfa/cjx_user.h
deleted file mode 100644
index 6111df8..0000000
--- a/fxjs/xfa/cjx_user.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_USER_H_
-#define FXJS_XFA_CJX_USER_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_User;
-
-class CJX_User : public CJX_TextNode {
- public:
-  explicit CJX_User(CXFA_User* node);
-  ~CJX_User() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_USER_H_
diff --git a/fxjs/xfa/cjx_validate.cpp b/fxjs/xfa/cjx_validate.cpp
deleted file mode 100644
index 16c264e..0000000
--- a/fxjs/xfa/cjx_validate.cpp
+++ /dev/null
@@ -1,43 +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 "fxjs/xfa/cjx_validate.h"
-
-#include "xfa/fxfa/parser/cxfa_validate.h"
-
-CJX_Validate::CJX_Validate(CXFA_Validate* node) : CJX_Node(node) {}
-
-CJX_Validate::~CJX_Validate() = default;
-
-void CJX_Validate::use(CFXJSE_Value* pValue,
-                       bool bSetting,
-                       XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Validate::scriptTest(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Validate::nullTest(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Validate::usehref(CFXJSE_Value* pValue,
-                           bool bSetting,
-                           XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Validate::formatTest(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_validate.h b/fxjs/xfa/cjx_validate.h
deleted file mode 100644
index c54923d..0000000
--- a/fxjs/xfa/cjx_validate.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_VALIDATE_H_
-#define FXJS_XFA_CJX_VALIDATE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Validate;
-
-class CJX_Validate : public CJX_Node {
- public:
-  explicit CJX_Validate(CXFA_Validate* node);
-  ~CJX_Validate() override;
-
-  JS_PROP(formatTest);
-  JS_PROP(nullTest);
-  JS_PROP(scriptTest);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_VALIDATE_H_
diff --git a/fxjs/xfa/cjx_value.cpp b/fxjs/xfa/cjx_value.cpp
deleted file mode 100644
index 8fe3b23..0000000
--- a/fxjs/xfa/cjx_value.cpp
+++ /dev/null
@@ -1,37 +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 "fxjs/xfa/cjx_value.h"
-
-#include "xfa/fxfa/parser/cxfa_value.h"
-
-CJX_Value::CJX_Value(CXFA_Value* node) : CJX_Node(node) {}
-
-CJX_Value::~CJX_Value() = default;
-
-void CJX_Value::use(CFXJSE_Value* pValue,
-                    bool bSetting,
-                    XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Value::relevant(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Value::usehref(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Value::override(CFXJSE_Value* pValue,
-                         bool bSetting,
-                         XFA_Attribute eAttribute) {
-  Script_Attribute_BOOL(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_value.h b/fxjs/xfa/cjx_value.h
deleted file mode 100644
index f9c2a2c..0000000
--- a/fxjs/xfa/cjx_value.h
+++ /dev/null
@@ -1,25 +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 FXJS_XFA_CJX_VALUE_H_
-#define FXJS_XFA_CJX_VALUE_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_Value;
-
-class CJX_Value : public CJX_Node {
- public:
-  explicit CJX_Value(CXFA_Value* node);
-  ~CJX_Value() override;
-
-  JS_PROP(override);
-  JS_PROP(relevant);
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_VALUE_H_
diff --git a/fxjs/xfa/cjx_variables.cpp b/fxjs/xfa/cjx_variables.cpp
deleted file mode 100644
index 0904c9f..0000000
--- a/fxjs/xfa/cjx_variables.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_variables.h"
-
-#include "xfa/fxfa/parser/cxfa_variables.h"
-
-CJX_Variables::CJX_Variables(CXFA_Variables* node) : CJX_Container(node) {}
-
-CJX_Variables::~CJX_Variables() = default;
-
-void CJX_Variables::use(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Variables::usehref(CFXJSE_Value* pValue,
-                            bool bSetting,
-                            XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_variables.h b/fxjs/xfa/cjx_variables.h
deleted file mode 100644
index 8ed373e..0000000
--- a/fxjs/xfa/cjx_variables.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_VARIABLES_H_
-#define FXJS_XFA_CJX_VARIABLES_H_
-
-#include "fxjs/xfa/cjx_container.h"
-
-class CXFA_Variables;
-
-class CJX_Variables : public CJX_Container {
- public:
-  explicit CJX_Variables(CXFA_Variables* node);
-  ~CJX_Variables() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_VARIABLES_H_
diff --git a/fxjs/xfa/cjx_wsdladdress.cpp b/fxjs/xfa/cjx_wsdladdress.cpp
deleted file mode 100644
index fc6a77c..0000000
--- a/fxjs/xfa/cjx_wsdladdress.cpp
+++ /dev/null
@@ -1,25 +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 "fxjs/xfa/cjx_wsdladdress.h"
-
-#include "xfa/fxfa/parser/cxfa_wsdladdress.h"
-
-CJX_WsdlAddress::CJX_WsdlAddress(CXFA_WsdlAddress* node) : CJX_TextNode(node) {}
-
-CJX_WsdlAddress::~CJX_WsdlAddress() = default;
-
-void CJX_WsdlAddress::use(CFXJSE_Value* pValue,
-                          bool bSetting,
-                          XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_WsdlAddress::usehref(CFXJSE_Value* pValue,
-                              bool bSetting,
-                              XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_wsdladdress.h b/fxjs/xfa/cjx_wsdladdress.h
deleted file mode 100644
index a742d1a..0000000
--- a/fxjs/xfa/cjx_wsdladdress.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef FXJS_XFA_CJX_WSDLADDRESS_H_
-#define FXJS_XFA_CJX_WSDLADDRESS_H_
-
-#include "fxjs/xfa/cjx_textnode.h"
-
-class CXFA_WsdlAddress;
-
-class CJX_WsdlAddress : public CJX_TextNode {
- public:
-  explicit CJX_WsdlAddress(CXFA_WsdlAddress* node);
-  ~CJX_WsdlAddress() override;
-
-  JS_PROP(use);
-  JS_PROP(usehref);
-};
-
-#endif  // FXJS_XFA_CJX_WSDLADDRESS_H_
diff --git a/fxjs/xfa/cjx_wsdlconnection.cpp b/fxjs/xfa/cjx_wsdlconnection.cpp
index 4c6ca46..7821232 100644
--- a/fxjs/xfa/cjx_wsdlconnection.cpp
+++ b/fxjs/xfa/cjx_wsdlconnection.cpp
@@ -8,8 +8,9 @@
 
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_wsdlconnection.h"
 
 const CJX_MethodSpec CJX_WsdlConnection::MethodSpecs[] = {
@@ -17,27 +18,20 @@
 
 CJX_WsdlConnection::CJX_WsdlConnection(CXFA_WsdlConnection* connection)
     : CJX_Node(connection) {
-  DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
+  DefineMethods(MethodSpecs);
 }
 
 CJX_WsdlConnection::~CJX_WsdlConnection() {}
 
-CJS_Return CJX_WsdlConnection::execute(
-    CJS_V8* runtime,
+bool CJX_WsdlConnection::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
+CJS_Result CJX_WsdlConnection::execute(
+    CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty() && params.size() != 1)
-    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
-  return CJS_Return(runtime->NewBoolean(false));
-}
+    return CJS_Result::Failure(JSMessage::kParamError);
 
-void CJX_WsdlConnection::dataDescription(CFXJSE_Value* pValue,
-                                         bool bSetting,
-                                         XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_WsdlConnection::execute(CFXJSE_Value* pValue,
-                                 bool bSetting,
-                                 XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  return CJS_Result::Success(runtime->NewBoolean(false));
 }
diff --git a/fxjs/xfa/cjx_wsdlconnection.h b/fxjs/xfa/cjx_wsdlconnection.h
index d755b5d..bd5db40 100644
--- a/fxjs/xfa/cjx_wsdlconnection.h
+++ b/fxjs/xfa/cjx_wsdlconnection.h
@@ -7,22 +7,26 @@
 #ifndef FXJS_XFA_CJX_WSDLCONNECTION_H_
 #define FXJS_XFA_CJX_WSDLCONNECTION_H_
 
-#include "fxjs/CJX_Define.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_WsdlConnection;
 
-class CJX_WsdlConnection : public CJX_Node {
+class CJX_WsdlConnection final : public CJX_Node {
  public:
   explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection);
   ~CJX_WsdlConnection() override;
 
-  JS_METHOD(execute, CJX_WsdlConnection);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
 
-  JS_PROP(dataDescription);
-  JS_PROP(execute);
+  JSE_METHOD(execute);
 
  private:
+  using Type__ = CJX_WsdlConnection;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::WsdlConnection;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_xfa.cpp b/fxjs/xfa/cjx_xfa.cpp
index bc0d7f1..08c6a80 100644
--- a/fxjs/xfa/cjx_xfa.cpp
+++ b/fxjs/xfa/cjx_xfa.cpp
@@ -6,8 +6,8 @@
 
 #include "fxjs/xfa/cjx_xfa.h"
 
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_xfa.h"
 
@@ -15,25 +15,21 @@
 
 CJX_Xfa::~CJX_Xfa() = default;
 
+bool CJX_Xfa::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Xfa::thisValue(CFXJSE_Value* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
   if (bSetting)
     return;
 
-  CXFA_Object* pThis = GetDocument()->GetScriptContext()->GetThisObject();
-  ASSERT(pThis);
-  pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(pThis));
-}
-
-void CJX_Xfa::timeStamp(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
-
-void CJX_Xfa::uuid(CFXJSE_Value* pValue,
-                   bool bSetting,
-                   XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
+  auto* pScriptContext = GetDocument()->GetScriptContext();
+  CXFA_Object* pThis = pScriptContext->GetThisObject();
+  if (!pThis) {
+    pValue->SetNull();
+    return;
+  }
+  pValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(pThis));
 }
diff --git a/fxjs/xfa/cjx_xfa.h b/fxjs/xfa/cjx_xfa.h
index 0c8ed7e..63b0f5f 100644
--- a/fxjs/xfa/cjx_xfa.h
+++ b/fxjs/xfa/cjx_xfa.h
@@ -8,17 +8,25 @@
 #define FXJS_XFA_CJX_XFA_H_
 
 #include "fxjs/xfa/cjx_model.h"
+#include "fxjs/xfa/jse_define.h"
 
 class CXFA_Xfa;
 
-class CJX_Xfa : public CJX_Model {
+class CJX_Xfa final : public CJX_Model {
  public:
   explicit CJX_Xfa(CXFA_Xfa* node);
   ~CJX_Xfa() override;
 
-  JS_PROP(thisValue); /* this */
-  JS_PROP(timeStamp);
-  JS_PROP(uuid);
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
+  JSE_PROP(thisValue); /* this */
+
+ private:
+  using Type__ = CJX_Xfa;
+  using ParentType__ = CJX_Model;
+
+  static const TypeTag static_type__ = TypeTag::Xfa;
 };
 
 #endif  // FXJS_XFA_CJX_XFA_H_
diff --git a/fxjs/xfa/cjx_xmlconnection.cpp b/fxjs/xfa/cjx_xmlconnection.cpp
deleted file mode 100644
index 6622aab..0000000
--- a/fxjs/xfa/cjx_xmlconnection.cpp
+++ /dev/null
@@ -1,20 +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 "fxjs/xfa/cjx_xmlconnection.h"
-
-#include "xfa/fxfa/parser/cxfa_xmlconnection.h"
-
-CJX_XmlConnection::CJX_XmlConnection(CXFA_XmlConnection* node)
-    : CJX_Node(node) {}
-
-CJX_XmlConnection::~CJX_XmlConnection() = default;
-
-void CJX_XmlConnection::dataDescription(CFXJSE_Value* pValue,
-                                        bool bSetting,
-                                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_xmlconnection.h b/fxjs/xfa/cjx_xmlconnection.h
deleted file mode 100644
index 03c5c37..0000000
--- a/fxjs/xfa/cjx_xmlconnection.h
+++ /dev/null
@@ -1,22 +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 FXJS_XFA_CJX_XMLCONNECTION_H_
-#define FXJS_XFA_CJX_XMLCONNECTION_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_XmlConnection;
-
-class CJX_XmlConnection : public CJX_Node {
- public:
-  explicit CJX_XmlConnection(CXFA_XmlConnection* node);
-  ~CJX_XmlConnection() override;
-
-  JS_PROP(dataDescription);
-};
-
-#endif  // FXJS_XFA_CJX_XMLCONNECTION_H_
diff --git a/fxjs/xfa/cjx_xsdconnection.cpp b/fxjs/xfa/cjx_xsdconnection.cpp
deleted file mode 100644
index aa7d42a..0000000
--- a/fxjs/xfa/cjx_xsdconnection.cpp
+++ /dev/null
@@ -1,20 +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 "fxjs/xfa/cjx_xsdconnection.h"
-
-#include "xfa/fxfa/parser/cxfa_xsdconnection.h"
-
-CJX_XsdConnection::CJX_XsdConnection(CXFA_XsdConnection* node)
-    : CJX_Node(node) {}
-
-CJX_XsdConnection::~CJX_XsdConnection() = default;
-
-void CJX_XsdConnection::dataDescription(CFXJSE_Value* pValue,
-                                        bool bSetting,
-                                        XFA_Attribute eAttribute) {
-  Script_Attribute_String(pValue, bSetting, eAttribute);
-}
diff --git a/fxjs/xfa/cjx_xsdconnection.h b/fxjs/xfa/cjx_xsdconnection.h
deleted file mode 100644
index 66c79a1..0000000
--- a/fxjs/xfa/cjx_xsdconnection.h
+++ /dev/null
@@ -1,22 +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 FXJS_XFA_CJX_XSDCONNECTION_H_
-#define FXJS_XFA_CJX_XSDCONNECTION_H_
-
-#include "fxjs/xfa/cjx_node.h"
-
-class CXFA_XsdConnection;
-
-class CJX_XsdConnection : public CJX_Node {
- public:
-  explicit CJX_XsdConnection(CXFA_XsdConnection* node);
-  ~CJX_XsdConnection() override;
-
-  JS_PROP(dataDescription);
-};
-
-#endif  // FXJS_XFA_CJX_XSDCONNECTION_H_
diff --git a/fxjs/xfa/fxjse.cpp b/fxjs/xfa/fxjse.cpp
new file mode 100644
index 0000000..ff6176b
--- /dev/null
+++ b/fxjs/xfa/fxjse.cpp
@@ -0,0 +1,28 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "fxjs/xfa/fxjse.h"
+
+namespace pdfium {
+namespace fxjse {
+
+const char kFuncTag[] = "function descriptor tag";
+const char kClassTag[] = "class descriptor tag";
+
+}  // namespace fxjse
+}  // namespace pdfium
+
+CFXJSE_HostObject::CFXJSE_HostObject() = default;
+
+CFXJSE_HostObject::~CFXJSE_HostObject() = default;
+
+CFXJSE_FormCalcContext* CFXJSE_HostObject::AsFormCalcContext() {
+  return nullptr;
+}
+
+CXFA_Object* CFXJSE_HostObject::AsCXFAObject() {
+  return nullptr;
+}
diff --git a/fxjs/xfa/fxjse.h b/fxjs/xfa/fxjse.h
new file mode 100644
index 0000000..5aa6b39
--- /dev/null
+++ b/fxjs/xfa/fxjse.h
@@ -0,0 +1,85 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef FXJS_XFA_FXJSE_H_
+#define FXJS_XFA_FXJSE_H_
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "v8/include/v8.h"
+
+namespace pdfium {
+namespace fxjse {
+
+extern const char kFuncTag[];
+extern const char kClassTag[];
+
+}  // namespace fxjse
+}  // namespace pdfium
+
+class CFXJSE_Arguments;
+class CFXJSE_FormCalcContext;
+class CFXJSE_Value;
+class CJS_Result;
+class CXFA_Object;
+
+// C++ object which is retrieved from v8 object's slot.
+class CFXJSE_HostObject {
+ public:
+  virtual ~CFXJSE_HostObject();
+
+  // Two subclasses.
+  virtual CFXJSE_FormCalcContext* AsFormCalcContext();
+  virtual CXFA_Object* AsCXFAObject();
+
+ protected:
+  CFXJSE_HostObject();
+};
+
+typedef CJS_Result (*FXJSE_MethodCallback)(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const WideString& functionName);
+typedef void (*FXJSE_FuncCallback)(CFXJSE_Value* pThis,
+                                   ByteStringView szFuncName,
+                                   CFXJSE_Arguments& args);
+typedef void (*FXJSE_PropAccessor)(CFXJSE_Value* pObject,
+                                   ByteStringView szPropName,
+                                   CFXJSE_Value* pValue);
+typedef int32_t (*FXJSE_PropTypeGetter)(CFXJSE_Value* pObject,
+                                        ByteStringView szPropName,
+                                        bool bQueryIn);
+
+enum FXJSE_ClassPropTypes {
+  FXJSE_ClassPropType_None,
+  FXJSE_ClassPropType_Property,
+  FXJSE_ClassPropType_Method
+};
+
+struct FXJSE_FUNCTION_DESCRIPTOR {
+  const char* tag;  // pdfium::kFuncTag always.
+  const char* name;
+  FXJSE_FuncCallback callbackProc;
+};
+
+struct FXJSE_CLASS_DESCRIPTOR {
+  const char* tag;  // pdfium::kClassTag always.
+  const char* name;
+  const FXJSE_FUNCTION_DESCRIPTOR* methods;
+  int32_t methNum;
+  FXJSE_PropTypeGetter dynPropTypeGetter;
+  FXJSE_PropAccessor dynPropGetter;
+  FXJSE_PropAccessor dynPropSetter;
+  FXJSE_MethodCallback dynMethodCall;
+};
+
+extern const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor;
+
+void FXJSE_ThrowMessage(ByteStringView utf8Message);
+
+#endif  // FXJS_XFA_FXJSE_H_
diff --git a/fxjs/xfa/jse_define.h b/fxjs/xfa/jse_define.h
new file mode 100644
index 0000000..26405c9
--- /dev/null
+++ b/fxjs/xfa/jse_define.h
@@ -0,0 +1,35 @@
+// 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 FXJS_XFA_JSE_DEFINE_H_
+#define FXJS_XFA_JSE_DEFINE_H_
+
+#include <vector>
+
+#include "fxjs/cjs_result.h"
+
+class CFX_V8;
+
+#define JSE_METHOD(method_name)                                      \
+  static CJS_Result method_name##_static(                            \
+      CJX_Object* node, CFX_V8* runtime,                             \
+      const std::vector<v8::Local<v8::Value>>& params) {             \
+    if (!node->DynamicTypeIs(static_type__))                         \
+      return CJS_Result::Failure(JSMessage::kBadObjectError);        \
+    return static_cast<Type__*>(node)->method_name(runtime, params); \
+  }                                                                  \
+  CJS_Result method_name(CFX_V8* runtime,                            \
+                         const std::vector<v8::Local<v8::Value>>& params)
+
+#define JSE_PROP(prop_name)                                               \
+  static void prop_name##_static(CJX_Object* node, CFXJSE_Value* value,   \
+                                 bool setting, XFA_Attribute attribute) { \
+    if (node->DynamicTypeIs(static_type__))                               \
+      static_cast<Type__*>(node)->prop_name(value, setting, attribute);   \
+  }                                                                       \
+  void prop_name(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute)
+
+#endif  // FXJS_XFA_JSE_DEFINE_H_
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
deleted file mode 100644
index 6d2a699..0000000
--- a/infra/config/cq.cfg
+++ /dev/null
@@ -1,105 +0,0 @@
-# See http://luci-config.appspot.com/schemas/projects/refs:cq.cfg for the
-# documentation of this file format.
-
-version: 1
-cq_name: "pdfium"
-git_repo_url: "https://pdfium.googlesource.com/pdfium.git"
-cq_status_url: "https://chromium-cq-status.appspot.com"
-
-gerrit {}
-
-verifiers {
-  gerrit_cq_ability {
-     committer_list: "project-pdfium-committers"
-     dry_run_access_list: "project-pdfium-tryjob-access"
-  }
-
-  tree_status {
-    tree_status_url: "https://pdfium-status.appspot.com"
-  }
-
-  try_job {
-    try_job_retry_config {
-      try_job_retry_quota: 0
-    }
-
-    buckets {
-      name: "master.tryserver.client.pdfium"
-      builders {
-        name: "android"
-      }
-      builders {
-        name: "linux"
-      }
-      builders {
-        name: "linux_asan_lsan"
-      }
-      builders {
-        name: "linux_msan"
-      }
-      builders {
-        name: "linux_no_v8"
-      }
-      builders {
-        name: "linux_skia"
-      }
-      builders {
-        name: "linux_xfa"
-      }
-      builders {
-        name: "linux_xfa_asan_lsan"
-      }
-      builders {
-        name: "linux_xfa_msan"
-      }
-      builders {
-        name: "linux_xfa_rel"
-      }
-      builders {
-        name: "mac"
-      }
-      builders {
-        name: "mac_no_v8"
-      }
-      builders {
-        name: "mac_skia"
-      }
-      builders {
-        name: "mac_xfa"
-      }
-      builders {
-        name: "mac_xfa_rel"
-      }
-      builders {
-        name: "win"
-      }
-      builders {
-        name: "win_asan"
-      }
-      builders {
-        name: "win_no_v8"
-      }
-      builders {
-        name: "win_skia"
-      }
-      builders {
-        name: "win_xfa"
-      }
-      builders {
-        name: "win_xfa_32"
-      }
-      builders {
-        name: "win_xfa_asan"
-      }
-      builders {
-        name: "win_xfa_msvc"
-      }
-      builders {
-        name: "win_xfa_msvc_32"
-      }
-      builders {
-        name: "win_xfa_rel"
-      }
-    }
-  }
-}
diff --git a/pdfium.bp b/pdfium.bp
deleted file mode 100644
index e188280..0000000
--- a/pdfium.bp
+++ /dev/null
@@ -1,78 +0,0 @@
-cc_library_shared {
-    name: "libpdfium",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfdrm",
-        "libpdfiumformfiller",
-        "libpdfiumfpdfapi",
-        "libpdfiumfpdfdoc",
-        "libpdfiumfpdftext",
-        "libpdfiumfxcodec",
-        "libpdfiumfxcrt",
-        "libpdfiumfxge",
-        "libpdfiumfxjs",
-        "libpdfiumpwl",
-        "libpdfiumfx_agg",
-        "libpdfiumbigint",
-        "libpdfiumpdfiumbase",
-        "libpdfiumlcms2",
-        "libpdfiumfx_libopenjpeg",
-    ],
-
-    // TODO: figure out why turning on exceptions requires manually linking libdl
-    shared_libs: [
-        "libdl",
-        "libft2",
-        "libz",
-        "libjpeg",
-    ],
-
-    srcs: [
-        "fpdfsdk/cba_annotiterator.cpp",
-        "fpdfsdk/cfx_systemhandler.cpp",
-        "fpdfsdk/cpdfsdk_annot.cpp",
-        "fpdfsdk/cpdfsdk_annothandlermgr.cpp",
-        "fpdfsdk/cpdfsdk_annotiteration.cpp",
-        "fpdfsdk/cpdfsdk_baannot.cpp",
-        "fpdfsdk/cpdfsdk_baannothandler.cpp",
-        "fpdfsdk/cpdfsdk_datetime.cpp",
-        "fpdfsdk/cpdfsdk_formfillenvironment.cpp",
-        "fpdfsdk/cpdfsdk_interform.cpp",
-        "fpdfsdk/cpdfsdk_pageview.cpp",
-        "fpdfsdk/cpdfsdk_widget.cpp",
-        "fpdfsdk/cpdfsdk_widgethandler.cpp",
-        "fpdfsdk/fpdf_dataavail.cpp",
-        "fpdfsdk/fpdf_ext.cpp",
-        "fpdfsdk/fpdf_flatten.cpp",
-        "fpdfsdk/fpdf_progressive.cpp",
-        "fpdfsdk/fpdf_searchex.cpp",
-        "fpdfsdk/fpdf_structtree.cpp",
-        "fpdfsdk/fpdf_sysfontinfo.cpp",
-        "fpdfsdk/fpdf_transformpage.cpp",
-        "fpdfsdk/fpdfannot.cpp",
-        "fpdfsdk/fpdfattachment.cpp",
-        "fpdfsdk/fpdfcatalog.cpp",
-        "fpdfsdk/fpdfdoc.cpp",
-        "fpdfsdk/fpdfeditimg.cpp",
-        "fpdfsdk/fpdfeditpage.cpp",
-        "fpdfsdk/fpdfeditpath.cpp",
-        "fpdfsdk/fpdfedittext.cpp",
-        "fpdfsdk/fpdfformfill.cpp",
-        "fpdfsdk/fpdfppo.cpp",
-        "fpdfsdk/fpdfsave.cpp",
-        "fpdfsdk/fpdftext.cpp",
-        "fpdfsdk/fpdfview.cpp",
-        "fpdfsdk/fsdk_actionhandler.cpp",
-        "fpdfsdk/fsdk_filewriteadapter.cpp",
-        "fpdfsdk/fsdk_pauseadapter.cpp",
-        "fpdfsdk/pdfsdk_fieldaction.cpp",
-    ],
-
-    export_include_dirs: ["public"],
-
-    include_dirs: [
-        "external/freetype/include",
-        "external/freetype/include/freetype",
-    ],
-}
diff --git a/pdfium.gni b/pdfium.gni
index e48750e..00a3886 100644
--- a/pdfium.gni
+++ b/pdfium.gni
@@ -15,6 +15,9 @@
   #    //build/config/freetype.
   pdf_bundle_freetype = pdf_bundle_freetype_override
 
+  # Generate logging messages for click events that reach PDFium
+  pdf_enable_click_logging = false
+
   # Build PDFium either with or without v8 support.
   pdf_enable_v8 = pdf_enable_v8_override
 
@@ -53,15 +56,15 @@
   # Enable callgrind for performance profiling
   enable_callgrind = false
 
-  # Enable coverage information
-  use_coverage = false
-
   # Don't build against bundled zlib.
   use_system_zlib = false
 
   # Don't build against bundled lcms2.
   use_system_lcms2 = false
 
+  # Don't build against bundled libopenjpeg2.
+  use_system_libopenjpeg2 = false
+
   # Don't build against bundled libpng.
   use_system_libpng = false
 }
@@ -69,3 +72,6 @@
 if (pdf_use_skia && pdf_use_skia_paths) {
   assert(false, "Enable at most ONE of pdf_use_skia and pdf_use_skia_paths")
 }
+
+assert(!pdf_is_complete_lib || !is_component_build,
+       "pdf_is_complete_lib=true requires is_component_build=false")
diff --git a/pdfiumfdrm.bp b/pdfiumfdrm.bp
deleted file mode 100644
index b59898f..0000000
--- a/pdfiumfdrm.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfdrm",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-    ],
-
-    srcs: [
-        "core/fdrm/crypto/fx_crypt.cpp",
-        "core/fdrm/crypto/fx_crypt_aes.cpp",
-        "core/fdrm/crypto/fx_crypt_sha.cpp",
-    ],
-}
diff --git a/pdfiumformfiller.bp b/pdfiumformfiller.bp
deleted file mode 100644
index a6619df..0000000
--- a/pdfiumformfiller.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-cc_library_static {
-    name: "libpdfiumformfiller",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-    ],
-
-    srcs: [
-        "fpdfsdk/formfiller/cba_fontmap.cpp",
-        "fpdfsdk/formfiller/cffl_button.cpp",
-        "fpdfsdk/formfiller/cffl_checkbox.cpp",
-        "fpdfsdk/formfiller/cffl_combobox.cpp",
-        "fpdfsdk/formfiller/cffl_formfiller.cpp",
-        "fpdfsdk/formfiller/cffl_interactiveformfiller.cpp",
-        "fpdfsdk/formfiller/cffl_listbox.cpp",
-        "fpdfsdk/formfiller/cffl_pushbutton.cpp",
-        "fpdfsdk/formfiller/cffl_radiobutton.cpp",
-        "fpdfsdk/formfiller/cffl_textfield.cpp",
-        "fpdfsdk/formfiller/cffl_textobject.cpp",
-    ],
-
-    include_dirs: [
-        "external/freetype/include",
-        "external/freetype/include/freetype",
-    ],
-}
diff --git a/pdfiumfpdfapi.bp b/pdfiumfpdfapi.bp
deleted file mode 100644
index 2beb9c6..0000000
--- a/pdfiumfpdfapi.bp
+++ /dev/null
@@ -1,189 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfpdfapi",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-        "libpdfiumlcms2",
-        "libpdfiumfdrm",
-    ],
-
-    srcs: [
-        "core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp",
-        "core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp",
-        "core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp",
-        "core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp",
-        "core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp",
-        "core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp",
-        "core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp",
-        "core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp",
-        "core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp",
-        "core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp",
-        "core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp",
-        "core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp",
-        "core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp",
-        "core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp",
-        "core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp",
-        "core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp",
-        "core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp",
-        "core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp",
-        "core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp",
-        "core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp",
-        "core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp",
-        "core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp",
-        "core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp",
-        "core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp",
-        "core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp",
-        "core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp",
-        "core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp",
-        "core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp",
-        "core/fpdfapi/cmaps/Japan1/H_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp",
-        "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp",
-        "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp",
-        "core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp",
-        "core/fpdfapi/cmaps/Japan1/V_1.cpp",
-        "core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp",
-        "core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp",
-        "core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp",
-        "core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp",
-        "core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp",
-        "core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp",
-        "core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp",
-        "core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp",
-        "core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp",
-        "core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp",
-        "core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp",
-        "core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp",
-        "core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp",
-        "core/fpdfapi/cmaps/fpdf_cmaps.cpp",
-        "core/fpdfapi/cpdf_modulemgr.cpp",
-        "core/fpdfapi/cpdf_pagerendercontext.cpp",
-        "core/fpdfapi/edit/cpdf_creator.cpp",
-        "core/fpdfapi/edit/cpdf_encryptor.cpp",
-        "core/fpdfapi/edit/cpdf_flateencoder.cpp",
-        "core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp",
-        "core/fpdfapi/font/cfx_cttgsubtable.cpp",
-        "core/fpdfapi/font/cfx_stockfontarray.cpp",
-        "core/fpdfapi/font/cpdf_cid2unicodemap.cpp",
-        "core/fpdfapi/font/cpdf_cidfont.cpp",
-        "core/fpdfapi/font/cpdf_cmap.cpp",
-        "core/fpdfapi/font/cpdf_cmapmanager.cpp",
-        "core/fpdfapi/font/cpdf_cmapparser.cpp",
-        "core/fpdfapi/font/cpdf_font.cpp",
-        "core/fpdfapi/font/cpdf_fontencoding.cpp",
-        "core/fpdfapi/font/cpdf_fontglobals.cpp",
-        "core/fpdfapi/font/cpdf_simplefont.cpp",
-        "core/fpdfapi/font/cpdf_tounicodemap.cpp",
-        "core/fpdfapi/font/cpdf_truetypefont.cpp",
-        "core/fpdfapi/font/cpdf_type1font.cpp",
-        "core/fpdfapi/font/cpdf_type3char.cpp",
-        "core/fpdfapi/font/cpdf_type3font.cpp",
-        "core/fpdfapi/page/cpdf_allstates.cpp",
-        "core/fpdfapi/page/cpdf_clippath.cpp",
-        "core/fpdfapi/page/cpdf_color.cpp",
-        "core/fpdfapi/page/cpdf_colorspace.cpp",
-        "core/fpdfapi/page/cpdf_colorstate.cpp",
-        "core/fpdfapi/page/cpdf_contentmark.cpp",
-        "core/fpdfapi/page/cpdf_contentmarkitem.cpp",
-        "core/fpdfapi/page/cpdf_contentparser.cpp",
-        "core/fpdfapi/page/cpdf_devicecs.cpp",
-        "core/fpdfapi/page/cpdf_docpagedata.cpp",
-        "core/fpdfapi/page/cpdf_expintfunc.cpp",
-        "core/fpdfapi/page/cpdf_form.cpp",
-        "core/fpdfapi/page/cpdf_formobject.cpp",
-        "core/fpdfapi/page/cpdf_function.cpp",
-        "core/fpdfapi/page/cpdf_generalstate.cpp",
-        "core/fpdfapi/page/cpdf_graphicstates.cpp",
-        "core/fpdfapi/page/cpdf_iccprofile.cpp",
-        "core/fpdfapi/page/cpdf_image.cpp",
-        "core/fpdfapi/page/cpdf_imageobject.cpp",
-        "core/fpdfapi/page/cpdf_meshstream.cpp",
-        "core/fpdfapi/page/cpdf_page.cpp",
-        "core/fpdfapi/page/cpdf_pagemodule.cpp",
-        "core/fpdfapi/page/cpdf_pageobject.cpp",
-        "core/fpdfapi/page/cpdf_pageobjectholder.cpp",
-        "core/fpdfapi/page/cpdf_pageobjectlist.cpp",
-        "core/fpdfapi/page/cpdf_path.cpp",
-        "core/fpdfapi/page/cpdf_pathobject.cpp",
-        "core/fpdfapi/page/cpdf_pattern.cpp",
-        "core/fpdfapi/page/cpdf_patterncs.cpp",
-        "core/fpdfapi/page/cpdf_psengine.cpp",
-        "core/fpdfapi/page/cpdf_psfunc.cpp",
-        "core/fpdfapi/page/cpdf_sampledfunc.cpp",
-        "core/fpdfapi/page/cpdf_shadingobject.cpp",
-        "core/fpdfapi/page/cpdf_shadingpattern.cpp",
-        "core/fpdfapi/page/cpdf_stitchfunc.cpp",
-        "core/fpdfapi/page/cpdf_streamcontentparser.cpp",
-        "core/fpdfapi/page/cpdf_streamparser.cpp",
-        "core/fpdfapi/page/cpdf_textobject.cpp",
-        "core/fpdfapi/page/cpdf_textstate.cpp",
-        "core/fpdfapi/page/cpdf_tilingpattern.cpp",
-        "core/fpdfapi/parser/cfdf_document.cpp",
-        "core/fpdfapi/parser/cpdf_array.cpp",
-        "core/fpdfapi/parser/cpdf_boolean.cpp",
-        "core/fpdfapi/parser/cpdf_cross_ref_avail.cpp",
-        "core/fpdfapi/parser/cpdf_crypto_handler.cpp",
-        "core/fpdfapi/parser/cpdf_data_avail.cpp",
-        "core/fpdfapi/parser/cpdf_dictionary.cpp",
-        "core/fpdfapi/parser/cpdf_document.cpp",
-        "core/fpdfapi/parser/cpdf_hint_tables.cpp",
-        "core/fpdfapi/parser/cpdf_indirect_object_holder.cpp",
-        "core/fpdfapi/parser/cpdf_linearized_header.cpp",
-        "core/fpdfapi/parser/cpdf_name.cpp",
-        "core/fpdfapi/parser/cpdf_null.cpp",
-        "core/fpdfapi/parser/cpdf_number.cpp",
-        "core/fpdfapi/parser/cpdf_object.cpp",
-        "core/fpdfapi/parser/cpdf_object_avail.cpp",
-        "core/fpdfapi/parser/cpdf_object_walker.cpp",
-        "core/fpdfapi/parser/cpdf_page_object_avail.cpp",
-        "core/fpdfapi/parser/cpdf_parser.cpp",
-        "core/fpdfapi/parser/cpdf_read_validator.cpp",
-        "core/fpdfapi/parser/cpdf_reference.cpp",
-        "core/fpdfapi/parser/cpdf_security_handler.cpp",
-        "core/fpdfapi/parser/cpdf_simple_parser.cpp",
-        "core/fpdfapi/parser/cpdf_stream.cpp",
-        "core/fpdfapi/parser/cpdf_stream_acc.cpp",
-        "core/fpdfapi/parser/cpdf_string.cpp",
-        "core/fpdfapi/parser/cpdf_syntax_parser.cpp",
-        "core/fpdfapi/parser/fpdf_parser_decode.cpp",
-        "core/fpdfapi/parser/fpdf_parser_utility.cpp",
-        "core/fpdfapi/render/cpdf_charposlist.cpp",
-        "core/fpdfapi/render/cpdf_devicebuffer.cpp",
-        "core/fpdfapi/render/cpdf_dibsource.cpp",
-        "core/fpdfapi/render/cpdf_dibtransferfunc.cpp",
-        "core/fpdfapi/render/cpdf_docrenderdata.cpp",
-        "core/fpdfapi/render/cpdf_imagecacheentry.cpp",
-        "core/fpdfapi/render/cpdf_imageloader.cpp",
-        "core/fpdfapi/render/cpdf_imagerenderer.cpp",
-        "core/fpdfapi/render/cpdf_pagerendercache.cpp",
-        "core/fpdfapi/render/cpdf_progressiverenderer.cpp",
-        "core/fpdfapi/render/cpdf_rendercontext.cpp",
-        "core/fpdfapi/render/cpdf_renderoptions.cpp",
-        "core/fpdfapi/render/cpdf_renderstatus.cpp",
-        "core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp",
-        "core/fpdfapi/render/cpdf_textrenderer.cpp",
-        "core/fpdfapi/render/cpdf_transferfunc.cpp",
-        "core/fpdfapi/render/cpdf_type3cache.cpp",
-        "core/fpdfapi/render/cpdf_type3glyphs.cpp",
-    ],
-
-    include_dirs: [
-        "external/freetype/include",
-        "external/freetype/include/freetype",
-    ],
-}
diff --git a/pdfiumfpdfdoc.bp b/pdfiumfpdfdoc.bp
deleted file mode 100644
index c3493a8..0000000
--- a/pdfiumfpdfdoc.bp
+++ /dev/null
@@ -1,49 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfpdfdoc",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-    ],
-
-    srcs: [
-        "core/fpdfdoc/cline.cpp",
-        "core/fpdfdoc/cpdf_aaction.cpp",
-        "core/fpdfdoc/cpdf_action.cpp",
-        "core/fpdfdoc/cpdf_actionfields.cpp",
-        "core/fpdfdoc/cpdf_annot.cpp",
-        "core/fpdfdoc/cpdf_annotlist.cpp",
-        "core/fpdfdoc/cpdf_apsettings.cpp",
-        "core/fpdfdoc/cpdf_bookmark.cpp",
-        "core/fpdfdoc/cpdf_bookmarktree.cpp",
-        "core/fpdfdoc/cpdf_defaultappearance.cpp",
-        "core/fpdfdoc/cpdf_dest.cpp",
-        "core/fpdfdoc/cpdf_docjsactions.cpp",
-        "core/fpdfdoc/cpdf_filespec.cpp",
-        "core/fpdfdoc/cpdf_formcontrol.cpp",
-        "core/fpdfdoc/cpdf_formfield.cpp",
-        "core/fpdfdoc/cpdf_iconfit.cpp",
-        "core/fpdfdoc/cpdf_interform.cpp",
-        "core/fpdfdoc/cpdf_link.cpp",
-        "core/fpdfdoc/cpdf_linklist.cpp",
-        "core/fpdfdoc/cpdf_metadata.cpp",
-        "core/fpdfdoc/cpdf_nametree.cpp",
-        "core/fpdfdoc/cpdf_numbertree.cpp",
-        "core/fpdfdoc/cpdf_occontext.cpp",
-        "core/fpdfdoc/cpdf_pagelabel.cpp",
-        "core/fpdfdoc/cpdf_structelement.cpp",
-        "core/fpdfdoc/cpdf_structtree.cpp",
-        "core/fpdfdoc/cpdf_variabletext.cpp",
-        "core/fpdfdoc/cpdf_viewerpreferences.cpp",
-        "core/fpdfdoc/cpvt_fontmap.cpp",
-        "core/fpdfdoc/cpvt_generateap.cpp",
-        "core/fpdfdoc/cpvt_wordinfo.cpp",
-        "core/fpdfdoc/csection.cpp",
-        "core/fpdfdoc/ctypeset.cpp",
-    ],
-
-    include_dirs: [
-        "external/freetype/include",
-        "external/freetype/include/freetype",
-    ],
-}
diff --git a/pdfiumfpdftext.bp b/pdfiumfpdftext.bp
deleted file mode 100644
index 7ae75d7..0000000
--- a/pdfiumfpdftext.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfpdftext",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-    ],
-
-    srcs: [
-        "core/fpdftext/cpdf_linkextract.cpp",
-        "core/fpdftext/cpdf_textpage.cpp",
-        "core/fpdftext/cpdf_textpagefind.cpp",
-        "core/fpdftext/unicodenormalizationdata.cpp",
-    ],
-
-    include_dirs: [
-        "external/freetype/include",
-        "external/freetype/include/freetype",
-    ],
-}
diff --git a/pdfiumfxcodec.bp b/pdfiumfxcodec.bp
deleted file mode 100644
index f8b5fb7..0000000
--- a/pdfiumfxcodec.bp
+++ /dev/null
@@ -1,49 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfxcodec",
-    defaults: ["pdfium-core"],
-
-    cflags: [
-        "-Wno-pointer-to-int-cast",
-        "-DUSE_SYSTEM_ZLIB",
-        "-DUSE_SYSTEM_LIBJPEG",
-    ],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-        "libpdfiumlcms2",
-        "libpdfiumfx_libopenjpeg",
-    ],
-
-    shared_libs: [
-        "libz",
-        "libjpeg",
-    ],
-
-    srcs: [
-        "core/fxcodec/codec/ccodec_scanlinedecoder.cpp",
-        "core/fxcodec/codec/fx_codec.cpp",
-        "core/fxcodec/codec/fx_codec_fax.cpp",
-        "core/fxcodec/codec/fx_codec_flate.cpp",
-        "core/fxcodec/codec/fx_codec_icc.cpp",
-        "core/fxcodec/codec/fx_codec_jbig.cpp",
-        "core/fxcodec/codec/fx_codec_jpeg.cpp",
-        "core/fxcodec/codec/fx_codec_jpx_opj.cpp",
-        "core/fxcodec/jbig2/JBig2_ArithDecoder.cpp",
-        "core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp",
-        "core/fxcodec/jbig2/JBig2_BitStream.cpp",
-        "core/fxcodec/jbig2/JBig2_Context.cpp",
-        "core/fxcodec/jbig2/JBig2_GrdProc.cpp",
-        "core/fxcodec/jbig2/JBig2_GrrdProc.cpp",
-        "core/fxcodec/jbig2/JBig2_HtrdProc.cpp",
-        "core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp",
-        "core/fxcodec/jbig2/JBig2_HuffmanTable.cpp",
-        "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.cpp",
-        "core/fxcodec/jbig2/JBig2_Image.cpp",
-        "core/fxcodec/jbig2/JBig2_PatternDict.cpp",
-        "core/fxcodec/jbig2/JBig2_PddProc.cpp",
-        "core/fxcodec/jbig2/JBig2_SddProc.cpp",
-        "core/fxcodec/jbig2/JBig2_Segment.cpp",
-        "core/fxcodec/jbig2/JBig2_SymbolDict.cpp",
-        "core/fxcodec/jbig2/JBig2_TrdProc.cpp",
-    ],
-}
diff --git a/pdfiumfxcrt.bp b/pdfiumfxcrt.bp
deleted file mode 100644
index 5acbc28..0000000
--- a/pdfiumfxcrt.bp
+++ /dev/null
@@ -1,42 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfxcrt",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumpdfiumbase"
-    ],
-
-    shared_libs: [
-        "libft2",
-    ],
-
-    srcs: [
-        "core/fxcrt/bytestring.cpp",
-        "core/fxcrt/cfx_binarybuf.cpp",
-        "core/fxcrt/cfx_bitstream.cpp",
-        "core/fxcrt/cfx_datetime.cpp",
-        "core/fxcrt/cfx_fileaccess_posix.cpp",
-        "core/fxcrt/cfx_fileaccess_windows.cpp",
-        "core/fxcrt/cfx_memorystream.cpp",
-        "core/fxcrt/cfx_seekablemultistream.cpp",
-        "core/fxcrt/cfx_utf8decoder.cpp",
-        "core/fxcrt/cfx_widetextbuf.cpp",
-        "core/fxcrt/fx_bidi.cpp",
-        "core/fxcrt/fx_coordinates.cpp",
-        "core/fxcrt/fx_extension.cpp",
-        "core/fxcrt/fx_memory.cpp",
-        "core/fxcrt/fx_random.cpp",
-        "core/fxcrt/fx_stream.cpp",
-        "core/fxcrt/fx_string.cpp",
-        "core/fxcrt/fx_system.cpp",
-        "core/fxcrt/fx_ucddata.cpp",
-        "core/fxcrt/fx_unicode.cpp",
-        "core/fxcrt/widestring.cpp",
-        "core/fxcrt/xml/cxml_attritem.cpp",
-        "core/fxcrt/xml/cxml_content.cpp",
-        "core/fxcrt/xml/cxml_databufacc.cpp",
-        "core/fxcrt/xml/cxml_element.cpp",
-        "core/fxcrt/xml/cxml_object.cpp",
-        "core/fxcrt/xml/cxml_parser.cpp",
-    ],
-}
diff --git a/pdfiumfxge.bp b/pdfiumfxge.bp
deleted file mode 100644
index 9c72701..0000000
--- a/pdfiumfxge.bp
+++ /dev/null
@@ -1,82 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfxge",
-    defaults: ["pdfium-core"],
-
-    cflags: [
-        "-DDEFINE_PS_TABLES",
-        "-DEFINE_PS_TABLES_DATA",
-    ],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-        "libpdfiumfx_agg",
-    ],
-
-    srcs: [
-        "core/fxge/android/cfpf_skiabufferfont.cpp",
-        "core/fxge/android/cfpf_skiadevicemodule.cpp",
-        "core/fxge/android/cfpf_skiafilefont.cpp",
-        "core/fxge/android/cfpf_skiafont.cpp",
-        "core/fxge/android/cfpf_skiafontdescriptor.cpp",
-        "core/fxge/android/cfpf_skiafontmgr.cpp",
-        "core/fxge/android/cfpf_skiapathfont.cpp",
-        "core/fxge/android/cfx_androidfontinfo.cpp",
-        "core/fxge/android/fx_android_imp.cpp",
-        "core/fxge/cfx_cliprgn.cpp",
-        "core/fxge/cfx_color.cpp",
-        "core/fxge/cfx_facecache.cpp",
-        "core/fxge/cfx_folderfontinfo.cpp",
-        "core/fxge/cfx_font.cpp",
-        "core/fxge/cfx_fontcache.cpp",
-        "core/fxge/cfx_fontmapper.cpp",
-        "core/fxge/cfx_fontmgr.cpp",
-        "core/fxge/cfx_gemodule.cpp",
-        "core/fxge/cfx_graphstate.cpp",
-        "core/fxge/cfx_graphstatedata.cpp",
-        "core/fxge/cfx_pathdata.cpp",
-        "core/fxge/cfx_renderdevice.cpp",
-        "core/fxge/cfx_substfont.cpp",
-        "core/fxge/cfx_unicodeencoding.cpp",
-        "core/fxge/cttfontdesc.cpp",
-        "core/fxge/dib/cfx_bitmapcomposer.cpp",
-        "core/fxge/dib/cfx_bitmapstorer.cpp",
-        "core/fxge/dib/cfx_dibextractor.cpp",
-        "core/fxge/dib/cfx_dibitmap.cpp",
-        "core/fxge/dib/cfx_dibsource.cpp",
-        "core/fxge/dib/cfx_filtereddib.cpp",
-        "core/fxge/dib/cfx_imagerenderer.cpp",
-        "core/fxge/dib/cfx_imagestretcher.cpp",
-        "core/fxge/dib/cfx_imagetransformer.cpp",
-        "core/fxge/dib/cfx_scanlinecompositor.cpp",
-        "core/fxge/dib/cstretchengine.cpp",
-        "core/fxge/dib/fx_dib_main.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitFixed.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSans.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSerif.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp",
-        "core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp",
-        "core/fxge/freetype/fx_freetype.cpp",
-        "core/fxge/fx_ge_fontmap.cpp",
-        "core/fxge/fx_ge_linux.cpp",
-        "core/fxge/fx_ge_text.cpp",
-        "core/fxge/ifx_renderdevicedriver.cpp",
-
-        "core/fxge/agg/fx_agg_driver.cpp",
-    ],
-
-    include_dirs: [
-        "external/freetype/include",
-        "external/freetype/include/freetype",
-    ],
-}
diff --git a/pdfiumfxjs.bp b/pdfiumfxjs.bp
deleted file mode 100644
index 572c95b..0000000
--- a/pdfiumfxjs.bp
+++ /dev/null
@@ -1,13 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfxjs",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfxcrt"
-    ],
-
-    srcs: [
-        "fxjs/cjs_event_context_stub.cpp",
-        "fxjs/cjs_runtimestub.cpp",
-    ],
-}
diff --git a/pdfiumpwl.bp b/pdfiumpwl.bp
deleted file mode 100644
index 78bd562..0000000
--- a/pdfiumpwl.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-cc_library_static {
-    name: "libpdfiumpwl",
-    defaults: ["pdfium-core"],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-    ],
-
-    srcs: [
-        "fpdfsdk/pwl/cpwl_appstream.cpp",
-        "fpdfsdk/pwl/cpwl_button.cpp",
-        "fpdfsdk/pwl/cpwl_caret.cpp",
-        "fpdfsdk/pwl/cpwl_combo_box.cpp",
-        "fpdfsdk/pwl/cpwl_edit.cpp",
-        "fpdfsdk/pwl/cpwl_edit_ctrl.cpp",
-        "fpdfsdk/pwl/cpwl_edit_impl.cpp",
-        "fpdfsdk/pwl/cpwl_font_map.cpp",
-        "fpdfsdk/pwl/cpwl_icon.cpp",
-        "fpdfsdk/pwl/cpwl_list_box.cpp",
-        "fpdfsdk/pwl/cpwl_list_impl.cpp",
-        "fpdfsdk/pwl/cpwl_scroll_bar.cpp",
-        "fpdfsdk/pwl/cpwl_special_button.cpp",
-        "fpdfsdk/pwl/cpwl_timer.cpp",
-        "fpdfsdk/pwl/cpwl_timer_handler.cpp",
-        "fpdfsdk/pwl/cpwl_wnd.cpp",
-    ],
-
-    include_dirs: [
-        "external/freetype/include",
-        "external/freetype/include/freetype",
-    ],
-}
diff --git a/public/PRESUBMIT.py b/public/PRESUBMIT.py
index 35bd873..5081823 100644
--- a/public/PRESUBMIT.py
+++ b/public/PRESUBMIT.py
@@ -13,8 +13,9 @@
   src_path = input_api.os_path.dirname(input_api.PresubmitLocalPath())
   check_script = input_api.os_path.join(
       src_path, 'testing' , 'tools' , 'api_check.py')
+  cmd = [input_api.python_executable, check_script]
   try:
-    input_api.subprocess.check_output(check_script)
+    input_api.subprocess.check_output(cmd)
     return []
   except input_api.subprocess.CalledProcessError as error:
     return [output_api.PresubmitError('api_check.py failed:',
diff --git a/public/cpp/fpdf_deleters.h b/public/cpp/fpdf_deleters.h
index 238ef30..633ddf5 100644
--- a/public/cpp/fpdf_deleters.h
+++ b/public/cpp/fpdf_deleters.h
@@ -5,15 +5,22 @@
 #ifndef PUBLIC_CPP_FPDF_DELETERS_H_
 #define PUBLIC_CPP_FPDF_DELETERS_H_
 
+#include "public/fpdf_annot.h"
 #include "public/fpdf_dataavail.h"
 #include "public/fpdf_edit.h"
 #include "public/fpdf_formfill.h"
+#include "public/fpdf_javascript.h"
 #include "public/fpdf_structtree.h"
 #include "public/fpdf_text.h"
+#include "public/fpdf_transformpage.h"
 #include "public/fpdfview.h"
 
 // Custom deleters for using FPDF_* types with std::unique_ptr<>.
 
+struct FPDFAnnotationDeleter {
+  inline void operator()(FPDF_ANNOTATION annot) { FPDFPage_CloseAnnot(annot); }
+};
+
 struct FPDFAvailDeleter {
   inline void operator()(FPDF_AVAIL avail) { FPDFAvail_Destroy(avail); }
 };
@@ -22,30 +29,58 @@
   inline void operator()(FPDF_BITMAP bitmap) { FPDFBitmap_Destroy(bitmap); }
 };
 
+struct FPDFClipPathDeleter {
+  inline void operator()(FPDF_CLIPPATH clip_path) {
+    FPDF_DestroyClipPath(clip_path);
+  }
+};
+
 struct FPDFDocumentDeleter {
   inline void operator()(FPDF_DOCUMENT doc) { FPDF_CloseDocument(doc); }
 };
 
+struct FPDFFontDeleter {
+  inline void operator()(FPDF_FONT font) { FPDFFont_Close(font); }
+};
+
 struct FPDFFormHandleDeleter {
   inline void operator()(FPDF_FORMHANDLE form) {
     FPDFDOC_ExitFormFillEnvironment(form);
   }
 };
 
-struct FPDFTextPageDeleter {
-  inline void operator()(FPDF_TEXTPAGE text) { FPDFText_ClosePage(text); }
+struct FPDFJavaScriptActionDeleter {
+  inline void operator()(FPDF_JAVASCRIPT_ACTION javascript) {
+    FPDFDoc_CloseJavaScriptAction(javascript);
+  }
 };
 
 struct FPDFPageDeleter {
   inline void operator()(FPDF_PAGE page) { FPDF_ClosePage(page); }
 };
 
+struct FPDFPageLinkDeleter {
+  inline void operator()(FPDF_PAGELINK pagelink) {
+    FPDFLink_CloseWebLinks(pagelink);
+  }
+};
+
+struct FPDFPageObjectDeleter {
+  inline void operator()(FPDF_PAGEOBJECT object) {
+    FPDFPageObj_Destroy(object);
+  }
+};
+
 struct FPDFStructTreeDeleter {
   inline void operator()(FPDF_STRUCTTREE tree) { FPDF_StructTree_Close(tree); }
 };
 
-struct FPDFFontDeleter {
-  inline void operator()(FPDF_FONT font) { FPDFFont_Close(font); }
+struct FPDFTextFindDeleter {
+  inline void operator()(FPDF_SCHHANDLE handle) { FPDFText_FindClose(handle); }
+};
+
+struct FPDFTextPageDeleter {
+  inline void operator()(FPDF_TEXTPAGE text) { FPDFText_ClosePage(text); }
 };
 
 #endif  // PUBLIC_CPP_FPDF_DELETERS_H_
diff --git a/public/cpp/fpdf_scopers.h b/public/cpp/fpdf_scopers.h
new file mode 100644
index 0000000..ff57c1b
--- /dev/null
+++ b/public/cpp/fpdf_scopers.h
@@ -0,0 +1,67 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PUBLIC_CPP_FPDF_SCOPERS_H_
+#define PUBLIC_CPP_FPDF_SCOPERS_H_
+
+#include <memory>
+#include <type_traits>
+
+#include "public/cpp/fpdf_deleters.h"
+
+// Versions of FPDF types that clean up the object at scope exit.
+
+using ScopedFPDFAnnotation =
+    std::unique_ptr<std::remove_pointer<FPDF_ANNOTATION>::type,
+                    FPDFAnnotationDeleter>;
+
+using ScopedFPDFAvail =
+    std::unique_ptr<std::remove_pointer<FPDF_AVAIL>::type, FPDFAvailDeleter>;
+
+using ScopedFPDFBitmap =
+    std::unique_ptr<std::remove_pointer<FPDF_BITMAP>::type, FPDFBitmapDeleter>;
+
+using ScopedFPDFClipPath =
+    std::unique_ptr<std::remove_pointer<FPDF_CLIPPATH>::type,
+                    FPDFClipPathDeleter>;
+
+using ScopedFPDFDocument =
+    std::unique_ptr<std::remove_pointer<FPDF_DOCUMENT>::type,
+                    FPDFDocumentDeleter>;
+
+using ScopedFPDFFont =
+    std::unique_ptr<std::remove_pointer<FPDF_FONT>::type, FPDFFontDeleter>;
+
+using ScopedFPDFFormHandle =
+    std::unique_ptr<std::remove_pointer<FPDF_FORMHANDLE>::type,
+                    FPDFFormHandleDeleter>;
+
+using ScopedFPDFJavaScriptAction =
+    std::unique_ptr<std::remove_pointer<FPDF_JAVASCRIPT_ACTION>::type,
+                    FPDFJavaScriptActionDeleter>;
+
+using ScopedFPDFPage =
+    std::unique_ptr<std::remove_pointer<FPDF_PAGE>::type, FPDFPageDeleter>;
+
+using ScopedFPDFPageLink =
+    std::unique_ptr<std::remove_pointer<FPDF_PAGELINK>::type,
+                    FPDFPageLinkDeleter>;
+
+using ScopedFPDFPageObject =
+    std::unique_ptr<std::remove_pointer<FPDF_PAGEOBJECT>::type,
+                    FPDFPageObjectDeleter>;
+
+using ScopedFPDFStructTree =
+    std::unique_ptr<std::remove_pointer<FPDF_STRUCTTREE>::type,
+                    FPDFStructTreeDeleter>;
+
+using ScopedFPDFTextFind =
+    std::unique_ptr<std::remove_pointer<FPDF_SCHHANDLE>::type,
+                    FPDFTextFindDeleter>;
+
+using ScopedFPDFTextPage =
+    std::unique_ptr<std::remove_pointer<FPDF_TEXTPAGE>::type,
+                    FPDFTextPageDeleter>;
+
+#endif  // PUBLIC_CPP_FPDF_SCOPERS_H_
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index d5ef54c..58da809 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -5,6 +5,8 @@
 #ifndef PUBLIC_FPDF_ANNOT_H_
 #define PUBLIC_FPDF_ANNOT_H_
 
+#include <stddef.h>
+
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
@@ -63,17 +65,6 @@
 #define FPDF_ANNOT_APPEARANCEMODE_DOWN 2
 #define FPDF_ANNOT_APPEARANCEMODE_COUNT 3
 
-#define FPDF_OBJECT_UNKNOWN 0
-#define FPDF_OBJECT_BOOLEAN 1
-#define FPDF_OBJECT_NUMBER 2
-#define FPDF_OBJECT_STRING 3
-#define FPDF_OBJECT_NAME 4
-#define FPDF_OBJECT_ARRAY 5
-#define FPDF_OBJECT_DICTIONARY 6
-#define FPDF_OBJECT_STREAM 7
-#define FPDF_OBJECT_NULLOBJ 8
-#define FPDF_OBJECT_REFERENCE 9
-
 // Refer to PDF Reference version 1.7 table 8.70 for field flags common to all
 // interactive form field types.
 #define FPDF_FORMFLAG_NONE 0
@@ -84,6 +75,7 @@
 // Refer to PDF Reference version 1.7 table 8.77 for field flags specific to
 // interactive form text fields.
 #define FPDF_FORMFLAG_TEXT_MULTILINE (1 << 12)
+#define FPDF_FORMFLAG_TEXT_PASSWORD (1 << 13)
 
 // Refer to PDF Reference version 1.7 table 8.79 for field flags specific to
 // interactive form choice fields.
@@ -283,7 +275,7 @@
 
 // Experimental API.
 // Check if the annotation is of a type that has attachment points
-// (i.e. quadpoints). Quadpoints are the vertices of the rectange that
+// (i.e. quadpoints). Quadpoints are the vertices of the rectangle that
 // encompasses the texts affected by the annotation. They provide the
 // coordinates in the page where the annotation is attached. Only text markup
 // annotations (i.e. highlight, strikeout, squiggly, and underline) and link
@@ -297,29 +289,58 @@
 FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot);
 
 // Experimental API.
-// Set the attachment points (i.e. quadpoints) of an annotation. If the
-// annotation's appearance stream is defined and this annotation is of a type
-// with quadpoints, then update the bounding box too if the new quadpoints
+// Replace the attachment points (i.e. quadpoints) set of an annotation at
+// |quad_index|. This index needs to be within the result of
+// FPDFAnnot_CountAttachmentPoints().
+// If the annotation's appearance stream is defined and this annotation is of a
+// type with quadpoints, then update the bounding box too if the new quadpoints
 // define a bigger one.
 //
-//   annot      - handle to an annotation.
-//   quadPoints - the quadpoints to be set.
+//   annot       - handle to an annotation.
+//   quad_index  - index of the set of quadpoints.
+//   quad_points - the quadpoints to be set.
 //
 // Returns true if successful.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,
-                              const FS_QUADPOINTSF* quadPoints);
+                              size_t quad_index,
+                              const FS_QUADPOINTSF* quad_points);
+
+// Experimental API.
+// Append to the list of attachment points (i.e. quadpoints) of an annotation.
+// If the annotation's appearance stream is defined and this annotation is of a
+// type with quadpoints, then update the bounding box too if the new quadpoints
+// define a bigger one.
+//
+//   annot       - handle to an annotation.
+//   quad_points - the quadpoints to be set.
+//
+// Returns true if successful.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_AppendAttachmentPoints(FPDF_ANNOTATION annot,
+                                 const FS_QUADPOINTSF* quad_points);
+
+// Experimental API.
+// Get the number of sets of quadpoints of an annotation.
+//
+//   annot  - handle to an annotation.
+//
+// Returns the number of sets of quadpoints, or 0 on failure.
+FPDF_EXPORT size_t FPDF_CALLCONV
+FPDFAnnot_CountAttachmentPoints(FPDF_ANNOTATION annot);
 
 // Experimental API.
 // Get the attachment points (i.e. quadpoints) of an annotation.
 //
-//   annot      - handle to an annotation.
-//   quadPoints - receives the quadpoints; must not be NULL.
+//   annot       - handle to an annotation.
+//   quad_index  - index of the set of quadpoints.
+//   quad_points - receives the quadpoints; must not be NULL.
 //
 // Returns true if successful.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,
-                              FS_QUADPOINTSF* quadPoints);
+                              size_t quad_index,
+                              FS_QUADPOINTSF* quad_points);
 
 // Experimental API.
 // Set the annotation rectangle defining the location of the annotation. If the
@@ -371,7 +392,7 @@
 //
 //   annot  - handle to an annotation.
 //   key    - the key to the dictionary entry to be set, encoded in UTF-8.
-//   value  - the string value to be set, encoded in UTF16-LE.
+//   value  - the string value to be set, encoded in UTF-16LE.
 //
 // Returns true if successful.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -390,24 +411,40 @@
 //
 //   annot  - handle to an annotation.
 //   key    - the key to the requested dictionary entry, encoded in UTF-8.
-//   buffer - buffer for holding the value string, encoded in UTF16-LE.
-//   buflen - length of the buffer.
+//   buffer - buffer for holding the value string, encoded in UTF-16LE.
+//   buflen - length of the buffer in bytes.
 //
-// Returns the length of the string value.
+// Returns the length of the string value in bytes.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAnnot_GetStringValue(FPDF_ANNOTATION annot,
                          FPDF_BYTESTRING key,
-                         void* buffer,
+                         FPDF_WCHAR* buffer,
                          unsigned long buflen);
 
 // Experimental API.
+// Get the float value corresponding to |key| in |annot|'s dictionary. Writes
+// value to |value| and returns True if |key| exists in the dictionary and
+// |key|'s corresponding value is a number (FPDF_OBJECT_NUMBER), False
+// otherwise.
+//
+//   annot  - handle to an annotation.
+//   key    - the key to the requested dictionary entry, encoded in UTF-8.
+//   value  - receives the value, must not be NULL.
+//
+// Returns True if value found, False otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetNumberValue(FPDF_ANNOTATION annot,
+                         FPDF_BYTESTRING key,
+                         float* value);
+
+// Experimental API.
 // Set the AP (appearance string) in |annot|'s dictionary for a given
 // |appearanceMode|.
 //
 //   annot          - handle to an annotation.
 //   appearanceMode - the appearance mode (normal, rollover or down) for which
 //                    to get the AP.
-//   value          - the string value to be set, encoded in UTF16-LE. If
+//   value          - the string value to be set, encoded in UTF-16LE. If
 //                    nullptr is passed, the AP is cleared for that mode. If the
 //                    mode is Normal, APs for all modes are cleared.
 //
@@ -430,14 +467,14 @@
 //   annot          - handle to an annotation.
 //   appearanceMode - the appearance mode (normal, rollover or down) for which
 //                    to get the AP.
-//   buffer         - buffer for holding the value string, encoded in UTF16-LE.
-//   buflen         - length of the buffer.
+//   buffer         - buffer for holding the value string, encoded in UTF-16LE.
+//   buflen         - length of the buffer in bytes.
 //
-// Returns the length of the string value.
+// Returns the length of the string value in bytes.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAnnot_GetAP(FPDF_ANNOTATION annot,
                 FPDF_ANNOT_APPEARANCEMODE appearanceMode,
-                void* buffer,
+                FPDF_WCHAR* buffer,
                 unsigned long buflen);
 
 // Experimental API.
@@ -472,15 +509,16 @@
                                                        int flags);
 
 // Experimental API.
-// Get the annotation flags of |annot|, which is an interactive form
-// annotation in |page|.
+// Get the annotation flags of |annot|.
 //
-//   page     - handle to a page.
-//   annot    - handle to an interactive form annotation.
+//    hHandle     -   handle to the form fill module, returned by
+//                    FPDFDOC_InitFormFillEnvironment().
+//    annot       -   handle to an interactive form annotation.
 //
 // Returns the annotation flags specific to interactive forms.
 FPDF_EXPORT int FPDF_CALLCONV
-FPDFAnnot_GetFormFieldFlags(FPDF_PAGE page, FPDF_ANNOTATION annot);
+FPDFAnnot_GetFormFieldFlags(FPDF_FORMHANDLE handle,
+                            FPDF_ANNOTATION annot);
 
 // Experimental API.
 // Retrieves an interactive form annotation whose rectangle contains a given
@@ -489,18 +527,134 @@
 //
 //
 //    hHandle     -   handle to the form fill module, returned by
-//                    FPDFDOC_InitFormFillEnvironment.
+//                    FPDFDOC_InitFormFillEnvironment().
 //    page        -   handle to the page, returned by FPDF_LoadPage function.
-//    page_x      -   X position in PDF "user space".
-//    page_y      -   Y position in PDF "user space".
+//    point       -   position in PDF "user space".
 //
 // Returns the interactive form annotation whose rectangle contains the given
 // coordinates on the page. If there is no such annotation, return NULL.
 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
 FPDFAnnot_GetFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
                               FPDF_PAGE page,
-                              double page_x,
-                              double page_y);
+                              const FS_POINTF* point);
+
+// Experimental API.
+// Gets the name of |annot|, which is an interactive form annotation.
+// |buffer| is only modified if |buflen| is longer than the length of contents.
+// In case of error, nothing will be added to |buffer| and the return value will
+// be 0. Note that return value of empty string is 2 for "\0\0".
+//
+//    hHandle     -   handle to the form fill module, returned by
+//                    FPDFDOC_InitFormFillEnvironment().
+//    annot       -   handle to an interactive form annotation.
+//    buffer      -   buffer for holding the name string, encoded in UTF-16LE.
+//    buflen      -   length of the buffer in bytes.
+//
+// Returns the length of the string value in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldName(FPDF_FORMHANDLE hHandle,
+                           FPDF_ANNOTATION annot,
+                           FPDF_WCHAR* buffer,
+                           unsigned long buflen);
+
+// Experimental API.
+// Gets the form field type of |annot|, which is an interactive form annotation.
+//
+//    hHandle     -   handle to the form fill module, returned by
+//                    FPDFDOC_InitFormFillEnvironment().
+//    annot       -   handle to an interactive form annotation.
+//
+// Returns the type of the form field (one of the FPDF_FORMFIELD_* values) on
+// success. Returns -1 on error.
+// See field types in fpdf_formfill.h.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFAnnot_GetFormFieldType(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot);
+
+// Experimental API.
+// Gets the value of |annot|, which is an interactive form annotation.
+// |buffer| is only modified if |buflen| is longer than the length of contents.
+// In case of error, nothing will be added to |buffer| and the return value will
+// be 0. Note that return value of empty string is 2 for "\0\0".
+//
+//    hHandle     -   handle to the form fill module, returned by
+//                    FPDFDOC_InitFormFillEnvironment().
+//    annot       -   handle to an interactive form annotation.
+//    buffer      -   buffer for holding the value string, encoded in UTF-16LE.
+//    buflen      -   length of the buffer in bytes.
+//
+// Returns the length of the string value in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle,
+                            FPDF_ANNOTATION annot,
+                            FPDF_WCHAR* buffer,
+                            unsigned long buflen);
+
+// Experimental API.
+// Get the number of options in the |annot|'s "Opt" dictionary. Intended for
+// use with listbox and combobox widget annotations.
+//
+//   hHandle - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//
+// Returns the number of options in "Opt" dictionary on success. Return value
+// will be -1 if annotation does not have an "Opt" dictionary or other error.
+FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetOptionCount(FPDF_FORMHANDLE hHandle,
+                                                       FPDF_ANNOTATION annot);
+
+// Experimental API.
+// Get the string value for the label of the option at |index| in |annot|'s
+// "Opt" dictionary. Intended for use with listbox and combobox widget
+// annotations. |buffer| is only modified if |buflen| is longer than the length
+// of contents. If index is out of range or in case of other error, nothing
+// will be added to |buffer| and the return value will be 0. Note that
+// return value of empty string is 2 for "\0\0".
+//
+//   hHandle - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//   index   - numeric index of the option in the "Opt" array
+//   buffer  - buffer for holding the value string, encoded in UTF-16LE.
+//   buflen  - length of the buffer in bytes.
+//
+// Returns the length of the string value in bytes.
+// If |annot| does not have an "Opt" array, |index| is out of range or if any
+// other error occurs, returns 0.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetOptionLabel(FPDF_FORMHANDLE hHandle,
+                         FPDF_ANNOTATION annot,
+                         int index,
+                         FPDF_WCHAR* buffer,
+                         unsigned long buflen);
+
+// Experimental API.
+// Get the float value of the font size for an |annot| with variable text.
+// If 0, the font is to be auto-sized: its size is computed as a function of
+// the height of the annotation rectangle.
+//
+//   hHandle - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//   value   - Required. Float which will be set to font size on success.
+//
+// Returns true if the font size was set in |value|, false on error or if
+// |value| not provided.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,
+                      FPDF_ANNOTATION annot,
+                      float* value);
+
+// Experimental API.
+// Determine if |annot| is a form widget that is checked. Intended for use with
+// checkbox and radio button widgets.
+//
+//   hHandle - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//
+// Returns true if |annot| is a form widget and is checked, false otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle,
+                                                        FPDF_ANNOTATION annot);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/public/fpdf_attachment.h b/public/fpdf_attachment.h
index 7b55974..791a90b 100644
--- a/public/fpdf_attachment.h
+++ b/public/fpdf_attachment.h
@@ -64,13 +64,13 @@
 // and the returned length is 0.
 //
 //   attachment - handle to an attachment.
-//   buffer     - buffer for holding the file name, encoded in UTF16-LE.
-//   buflen     - length of the buffer.
+//   buffer     - buffer for holding the file name, encoded in UTF-16LE.
+//   buflen     - length of the buffer in bytes.
 //
-// Returns the length of the file name.
+// Returns the length of the file name in bytes.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAttachment_GetName(FPDF_ATTACHMENT attachment,
-                       void* buffer,
+                       FPDF_WCHAR* buffer,
                        unsigned long buflen);
 
 // Experimental API.
@@ -101,7 +101,7 @@
 //
 //   attachment - handle to an attachment.
 //   key        - the key to the dictionary entry, encoded in UTF-8.
-//   value      - the string value to be set, encoded in UTF16-LE.
+//   value      - the string value to be set, encoded in UTF-16LE.
 //
 // Returns true if successful.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -121,14 +121,14 @@
 //
 //   attachment - handle to an attachment.
 //   key        - the key to the requested string value, encoded in UTF-8.
-//   buffer     - buffer for holding the string value encoded in UTF16-LE.
-//   buflen     - length of the buffer.
+//   buffer     - buffer for holding the string value encoded in UTF-16LE.
+//   buflen     - length of the buffer in bytes.
 //
-// Returns the length of the dictionary value string.
+// Returns the length of the dictionary value string in bytes.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment,
                               FPDF_BYTESTRING key,
-                              void* buffer,
+                              FPDF_WCHAR* buffer,
                               unsigned long buflen);
 
 // Experimental API.
@@ -138,8 +138,8 @@
 // INT_MAX is supported.
 //
 //   attachment - handle to an attachment.
-//   contents   - buffer holding the file data to be written in raw bytes.
-//   len        - length of file data.
+//   contents   - buffer holding the file data to write to |attachment|.
+//   len        - length of file data in bytes.
 //
 // Returns true if successful.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -154,8 +154,8 @@
 // returned length is 0.
 //
 //   attachment - handle to an attachment.
-//   buffer     - buffer for holding the file data in raw bytes.
-//   buflen     - length of the buffer.
+//   buffer     - buffer for holding the file data from |attachment|.
+//   buflen     - length of the buffer in bytes.
 //
 // Returns the length of the file.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
diff --git a/public/fpdf_dataavail.h b/public/fpdf_dataavail.h
index 3ce9211..ad70b7f 100644
--- a/public/fpdf_dataavail.h
+++ b/public/fpdf_dataavail.h
@@ -59,7 +59,7 @@
 //
 // Returns a handle to the document availability provider, or NULL on error.
 //
-// |FPDFAvail_Destroy| must be called when done with the availability provider.
+// FPDFAvail_Destroy() must be called when done with the availability provider.
 FPDF_EXPORT FPDF_AVAIL FPDF_CALLCONV FPDFAvail_Create(FX_FILEAVAIL* file_avail,
                                                       FPDF_FILEACCESS* file);
 
@@ -105,7 +105,7 @@
 // |PDF_DATA_ERROR| or |PDF_DATA_AVAIL|.
 // if hints is nullptr, the function just check current document availability.
 //
-// Once all data is available, call |FPDFAvail_GetDocument| to get a document
+// Once all data is available, call FPDFAvail_GetDocument() to get a document
 // handle.
 FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsDocAvail(FPDF_AVAIL avail,
                                                    FX_DOWNLOADHINTS* hints);
@@ -117,8 +117,10 @@
 //
 // Returns a handle to the document.
 //
-// When |FPDFAvail_IsDocAvail| returns TRUE, call |FPDFAvail_GetDocument| to
+// When FPDFAvail_IsDocAvail() returns TRUE, call FPDFAvail_GetDocument() to
 // retrieve the document handle.
+// See the comments for FPDF_LoadDocument() regarding the encoding for
+// |password|.
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDFAvail_GetDocument(FPDF_AVAIL avail, FPDF_BYTESTRING password);
 
@@ -146,7 +148,7 @@
 //   PDF_DATA_NOTAVAIL: Data not yet available.
 //   PDF_DATA_AVAIL: Data available.
 //
-// This function can be called only after |FPDFAvail_GetDocument| is called.
+// This function can be called only after FPDFAvail_GetDocument() is called.
 // Applications should call this function whenever new data arrives and process
 // all the generated download |hints|, if any, until this function returns
 // |PDF_DATA_ERROR| or |PDF_DATA_AVAIL|. Applications can then perform page
@@ -170,14 +172,14 @@
 //   PDF_FORM_AVAIL: Data available.
 //   PDF_FORM_NOTEXIST: No form data.
 //
-// This function can be called only after |FPDFAvail_GetDocument| is called.
+// This function can be called only after FPDFAvail_GetDocument() is called.
 // The application should call this function whenever new data arrives and
 // process all the generated download |hints|, if any, until the function
 // |PDF_FORM_ERROR|, |PDF_FORM_AVAIL| or |PDF_FORM_NOTEXIST|.
 // if hints is nullptr, the function just check current form availability.
 //
 // Applications can then perform page loading. It is recommend to call
-// |FPDFDOC_InitFormFillEnvironment| when |PDF_FORM_AVAIL| is returned.
+// FPDFDOC_InitFormFillEnvironment() when |PDF_FORM_AVAIL| is returned.
 FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsFormAvail(FPDF_AVAIL avail,
                                                     FX_DOWNLOADHINTS* hints);
 
@@ -190,7 +192,7 @@
 //   PDF_NOT_LINEARIZED
 //   PDF_LINEARIZATION_UNKNOWN
 //
-// |FPDFAvail_IsLinearized| will return |PDF_LINEARIZED| or |PDF_NOT_LINEARIZED|
+// FPDFAvail_IsLinearized() will return |PDF_LINEARIZED| or |PDF_NOT_LINEARIZED|
 // when we have 1k  of data. If the files size less than 1k, it returns
 // |PDF_LINEARIZATION_UNKNOWN| as there is insufficient information to determine
 // if the PDF is linearlized.
diff --git a/public/fpdf_doc.h b/public/fpdf_doc.h
index b523575..565314e 100644
--- a/public/fpdf_doc.h
+++ b/public/fpdf_doc.h
@@ -93,7 +93,7 @@
 //
 // Returns the handle to the bookmark, or NULL if |title| can't be found.
 //
-// |FPDFBookmark_Find| will always return the first bookmark found even if
+// FPDFBookmark_Find() will always return the first bookmark found even if
 // multiple bookmarks have the same |title|.
 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
 FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title);
@@ -113,7 +113,7 @@
 //   bookmark - handle to the bookmark.
 //
 // Returns the handle to the action data, or NULL if no action is associated
-// with |bookmark|. When NULL is returned, |FPDFBookmark_GetDest| should be
+// with |bookmark|. When NULL is returned, FPDFBookmark_GetDest() should be
 // called to get the |bookmark| destination data.
 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
 FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark);
@@ -136,23 +136,25 @@
 //   action   - handle to the action. |action| must be a |PDFACTION_GOTO| or
 //              |PDFACTION_REMOTEGOTO|.
 //
-// Returns a handle to the destination data.
+// Returns a handle to the destination data, or NULL on error, typically
+// because the arguments were bad or the action was of the wrong type.
 //
-// In the case of |PDFACTION_REMOTEGOTO|, you should first call
-// |FPDFAction_GetFilePath| then load that document, the document handle from
-// that document should pass as |document| to |FPDFAction_GetDest|.
+// In the case of |PDFACTION_REMOTEGOTO|, you must first call
+// FPDFAction_GetFilePath(), then load the document at that path, then pass
+// the document handle from that document as |document| to FPDFAction_GetDest().
 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document,
                                                        FPDF_ACTION action);
 
-// Get file path of a |PDFACTION_REMOTEGOTO| |action|.
+// Get the file path of |action|.
 //
 //   action - handle to the action. |action| must be a |PDFACTION_LAUNCH| or
-//            |PDFACTION_REMOTEGOTO|
+//            |PDFACTION_REMOTEGOTO|.
 //   buffer - a buffer for output the path string. May be NULL.
 //   buflen - the length of the buffer, in bytes. May be 0.
 //
 // Returns the number of bytes in the file path, including the trailing NUL
-// character.
+// character, or 0 on error, typically because the arguments were bad or the
+// action was of the wrong type.
 //
 // Regardless of the platform, the |buffer| is always in UTF-8 encoding.
 // If |buflen| is less than the returned length, or |buffer| is NULL, |buffer|
@@ -160,14 +162,16 @@
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFAction_GetFilePath(FPDF_ACTION action, void* buffer, unsigned long buflen);
 
-// Get the URI path of a |PDFACTION_URI| |action|.
+// Get the URI path of |action|.
 //
 //   document - handle to the document.
 //   action   - handle to the action. Must be a |PDFACTION_URI|.
 //   buffer   - a buffer for the path string. May be NULL.
 //   buflen   - the length of the buffer, in bytes. May be 0.
 //
-// Returns the number of bytes in the URI path, including trailing zeros.
+// Returns the number of bytes in the URI path, including the trailing NUL
+// character, or 0 on error, typically because the arguments were bad or the
+// action was of the wrong type.
 //
 // The |buffer| is always encoded in 7-bit ASCII. If |buflen| is less than the
 // returned length, or |buffer| is NULL, |buffer| will not be modified.
@@ -182,9 +186,9 @@
 //   document - handle to the document.
 //   dest     - handle to the destination.
 //
-// Returns the page index containing |dest|. Page indices start from 0.
-FPDF_EXPORT unsigned long FPDF_CALLCONV
-FPDFDest_GetPageIndex(FPDF_DOCUMENT document, FPDF_DEST dest);
+// Returns the 0-based page index containing |dest|. Returns -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document,
+                                                        FPDF_DEST dest);
 
 // Get the view (fit type) specified by |dest|.
 // Experimental API. Subject to change.
@@ -214,9 +218,9 @@
 // hasYVal or hasZoomVal flags are true.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFDest_GetLocationInPage(FPDF_DEST dest,
-                           FPDF_BOOL* hasXCoord,
-                           FPDF_BOOL* hasYCoord,
-                           FPDF_BOOL* hasZoom,
+                           FPDF_BOOL* hasXVal,
+                           FPDF_BOOL* hasYVal,
+                           FPDF_BOOL* hasZoomVal,
                            FS_FLOAT* x,
                            FS_FLOAT* y,
                            FS_FLOAT* zoom);
@@ -230,7 +234,7 @@
 // Returns a handle to the link, or NULL if no link found at the given point.
 //
 // You can convert coordinates from screen coordinates to page coordinates using
-// |FPDF_DeviceToPage|.
+// FPDF_DeviceToPage().
 FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page,
                                                             double x,
                                                             double y);
@@ -245,7 +249,7 @@
 // Larger Z-order numbers are closer to the front.
 //
 // You can convert coordinates from screen coordinates to page coordinates using
-// |FPDF_DeviceToPage|.
+// FPDF_DeviceToPage().
 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
                                                             double x,
                                                             double y);
@@ -256,7 +260,7 @@
 //   link     - handle to the link.
 //
 // Returns a handle to the destination, or NULL if there is no destination
-// associated with the link. In this case, you should call |FPDFLink_GetAction|
+// associated with the link. In this case, you should call FPDFLink_GetAction()
 // to retrieve the action associated with |link|.
 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document,
                                                      FPDF_LINK link);
@@ -270,43 +274,43 @@
 
 // Enumerates all the link annotations in |page|.
 //
-//   page      - handle to the page.
-//   startPos  - the start position, should initially be 0 and is updated with
-//               the next start position on return.
-//   linkAnnot - the link handle for |startPos|.
+//   page       - handle to the page.
+//   start_pos  - the start position, should initially be 0 and is updated with
+//                the next start position on return.
+//   link_annot - the link handle for |startPos|.
 //
 // Returns TRUE on success.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page,
-                                                       int* startPos,
-                                                       FPDF_LINK* linkAnnot);
+                                                       int* start_pos,
+                                                       FPDF_LINK* link_annot);
 
-// Get the rectangle for |linkAnnot|.
+// Get the rectangle for |link_annot|.
 //
-//   linkAnnot - handle to the link annotation.
-//   rect      - the annotation rectangle.
+//   link_annot - handle to the link annotation.
+//   rect       - the annotation rectangle.
 //
 // Returns true on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK linkAnnot,
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK link_annot,
                                                           FS_RECTF* rect);
 
-// Get the count of quadrilateral points to the |linkAnnot|.
+// Get the count of quadrilateral points to the |link_annot|.
 //
-//   linkAnnot - handle to the link annotation.
+//   link_annot - handle to the link annotation.
 //
 // Returns the count of quadrilateral points.
-FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK linkAnnot);
+FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK link_annot);
 
-// Get the quadrilateral points for the specified |quadIndex| in |linkAnnot|.
+// Get the quadrilateral points for the specified |quad_index| in |link_annot|.
 //
-//   linkAnnot  - handle to the link annotation.
-//   quadIndex  - the specified quad point index.
-//   quadPoints - receives the quadrilateral points.
+//   link_annot  - handle to the link annotation.
+//   quad_index  - the specified quad point index.
+//   quad_points - receives the quadrilateral points.
 //
 // Returns true on success.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFLink_GetQuadPoints(FPDF_LINK linkAnnot,
-                       int quadIndex,
-                       FS_QUADPOINTSF* quadPoints);
+FPDFLink_GetQuadPoints(FPDF_LINK link_annot,
+                       int quad_index,
+                       FS_QUADPOINTSF* quad_points);
 
 // Get meta-data |tag| content from |document|.
 //
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 685fbdd..8e0d234 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -48,6 +48,7 @@
 #define FPDF_SEGMENT_BEZIERTO 1
 #define FPDF_SEGMENT_MOVETO 2
 
+#define FPDF_FILLMODE_NONE 0
 #define FPDF_FILLMODE_ALTERNATE 1
 #define FPDF_FILLMODE_WINDING 2
 
@@ -66,6 +67,8 @@
 #define FPDF_PRINTMODE_TEXTONLY 1
 #define FPDF_PRINTMODE_POSTSCRIPT2 2
 #define FPDF_PRINTMODE_POSTSCRIPT3 3
+#define FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH 4
+#define FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH 5
 
 typedef struct FPDF_IMAGEOBJ_METADATA {
   // The image width in pixels.
@@ -97,15 +100,15 @@
 // Create a new PDF page.
 //
 //   document   - handle to document.
-//   page_index - suggested index of the page to create. If it is larger than
-//                document's current last index(L), the created page index is
-//                the next available index -- L+1.
-//   width      - the page width.
-//   height     - the page height.
+//   page_index - suggested 0-based index of the page to create. If it is larger
+//                than document's current last index(L), the created page index
+//                is the next available index -- L+1.
+//   width      - the page width in points.
+//   height     - the page height in points.
 //
-// Returns the handle to the new page.
+// Returns the handle to the new page or NULL on failure.
 //
-// The page should be closed with CPDF_ClosePage() when finished as
+// The page should be closed with FPDF_ClosePage() when finished as
 // with any other page in the document.
 FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDFPage_New(FPDF_DOCUMENT document,
                                                  int page_index,
@@ -148,14 +151,18 @@
 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertObject(FPDF_PAGE page,
                                                      FPDF_PAGEOBJECT page_obj);
 
-// Get number of page objects inside |page|.
+// Experimental API.
+// Remove |page_obj| from |page|.
 //
-//   page - handle to a page.
+//   page     - handle to a page
+//   page_obj - handle to a page object to be removed.
 //
-// Returns the number of objects in |page|.
+// Returns TRUE on success.
 //
-// DEPRECATED. Please use FPDFPage_CountObjects.
-FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObject(FPDF_PAGE page);
+// Ownership is transferred to the caller. Call FPDFPageObj_Destroy() to free
+// it.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPage_RemoveObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_obj);
 
 // Get number of page objects inside |page|.
 //
@@ -203,7 +210,7 @@
 //
 //   page_object - handle to a page object.
 //
-// Returns TRUE if |pageObject| contains transparency.
+// Returns TRUE if |page_object| contains transparency.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT page_object);
 
@@ -268,47 +275,312 @@
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
 FPDFPageObj_NewImageObj(FPDF_DOCUMENT document);
 
-// Load an image from a JPEG image file and then set it into |image_object|.
+// Experimental API.
+// Get number of content marks in |page_object|.
 //
-//   pages        - pointer to the start of all loaded pages, may be NULL.
-//   nCount       - number of |pages|, may be 0.
-//   image_object - handle to an image object.
-//   fileAccess   - file access handler which specifies the JPEG image file.
+//   page_object - handle to a page object.
 //
-// Returns TRUE on success.
+// Returns the number of content marks in |page_object|, or -1 in case of
+// failure.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_CountMarks(FPDF_PAGEOBJECT page_object);
+
+// Experimental API.
+// Get content mark in |page_object| at |index|.
 //
-// The image object might already have an associated image, which is shared and
-// cached by the loaded pages. In that case, we need to clear the cached image
-// for all the loaded pages. Pass |pages| and page count (|nCount|) to this API
-// to clear the image cache. If the image is not previously shared, or NULL is a
-// valid |pages| value.
+//   page_object - handle to a page object.
+//   index       - the index of a page object.
+//
+// Returns the handle to the content mark, or NULL on failure. The handle is
+// still owned by the library, and it should not be freed directly. It becomes
+// invalid if the page object is destroyed, either directly or indirectly by
+// unloading the page.
+FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
+FPDFPageObj_GetMark(FPDF_PAGEOBJECT page_object, unsigned long index);
+
+// Experimental API.
+// Add a new content mark to a |page_object|.
+//
+//   page_object - handle to a page object.
+//   name        - the name (tag) of the mark.
+//
+// Returns the handle to the content mark, or NULL on failure. The handle is
+// still owned by the library, and it should not be freed directly. It becomes
+// invalid if the page object is destroyed, either directly or indirectly by
+// unloading the page.
+FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
+FPDFPageObj_AddMark(FPDF_PAGEOBJECT page_object, FPDF_BYTESTRING name);
+
+// Experimental API.
+// Removes a content |mark| from a |page_object|.
+// The mark handle will be invalid after the removal.
+//
+//   page_object - handle to a page object.
+//   mark        - handle to a content mark in that object to remove.
+//
+// Returns TRUE if the operation succeeded, FALSE if it failed.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFImageObj_LoadJpegFile(FPDF_PAGE* pages,
-                          int nCount,
-                          FPDF_PAGEOBJECT image_object,
-                          FPDF_FILEACCESS* fileAccess);
+FPDFPageObj_RemoveMark(FPDF_PAGEOBJECT page_object, FPDF_PAGEOBJECTMARK mark);
+
+// Experimental API.
+// Get the name of a content mark.
+//
+//   mark       - handle to a content mark.
+//   buffer     - buffer for holding the returned name in UTF-16LE. This is only
+//                modified if |buflen| is longer than the length of the name.
+//                Optional, pass null to just retrieve the size of the buffer
+//                needed.
+//   buflen     - length of the buffer.
+//   out_buflen - pointer to variable that will receive the minimum buffer size
+//                to contain the name. Not filled if FALSE is returned.
+//
+// Returns TRUE if the operation succeeded, FALSE if it failed.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetName(FPDF_PAGEOBJECTMARK mark,
+                        void* buffer,
+                        unsigned long buflen,
+                        unsigned long* out_buflen);
+
+// Experimental API.
+// Get the number of key/value pair parameters in |mark|.
+//
+//   mark   - handle to a content mark.
+//
+// Returns the number of key/value pair parameters |mark|, or -1 in case of
+// failure.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObjMark_CountParams(FPDF_PAGEOBJECTMARK mark);
+
+// Experimental API.
+// Get the key of a property in a content mark.
+//
+//   mark       - handle to a content mark.
+//   index      - index of the property.
+//   buffer     - buffer for holding the returned key in UTF-16LE. This is only
+//                modified if |buflen| is longer than the length of the key.
+//                Optional, pass null to just retrieve the size of the buffer
+//                needed.
+//   buflen     - length of the buffer.
+//   out_buflen - pointer to variable that will receive the minimum buffer size
+//                to contain the key. Not filled if FALSE is returned.
+//
+// Returns TRUE if the operation was successful, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamKey(FPDF_PAGEOBJECTMARK mark,
+                            unsigned long index,
+                            void* buffer,
+                            unsigned long buflen,
+                            unsigned long* out_buflen);
+
+// Experimental API.
+// Get the type of the value of a property in a content mark by key.
+//
+//   mark   - handle to a content mark.
+//   key    - string key of the property.
+//
+// Returns the type of the value, or FPDF_OBJECT_UNKNOWN in case of failure.
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDFPageObjMark_GetParamValueType(FPDF_PAGEOBJECTMARK mark,
+                                  FPDF_BYTESTRING key);
+
+// Experimental API.
+// Get the value of a number property in a content mark by key as int.
+// FPDFPageObjMark_GetParamValueType() should have returned FPDF_OBJECT_NUMBER
+// for this property.
+//
+//   mark      - handle to a content mark.
+//   key       - string key of the property.
+//   out_value - pointer to variable that will receive the value. Not filled if
+//               false is returned.
+//
+// Returns TRUE if the key maps to a number value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamIntValue(FPDF_PAGEOBJECTMARK mark,
+                                 FPDF_BYTESTRING key,
+                                 int* out_value);
+
+// Experimental API.
+// Get the value of a string property in a content mark by key.
+//
+//   mark       - handle to a content mark.
+//   key        - string key of the property.
+//   buffer     - buffer for holding the returned value in UTF-16LE. This is
+//                only modified if |buflen| is longer than the length of the
+//                value.
+//                Optional, pass null to just retrieve the size of the buffer
+//                needed.
+//   buflen     - length of the buffer.
+//   out_buflen - pointer to variable that will receive the minimum buffer size
+//                to contain the value. Not filled if FALSE is returned.
+//
+// Returns TRUE if the key maps to a string/blob value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamStringValue(FPDF_PAGEOBJECTMARK mark,
+                                    FPDF_BYTESTRING key,
+                                    void* buffer,
+                                    unsigned long buflen,
+                                    unsigned long* out_buflen);
+
+// Experimental API.
+// Get the value of a blob property in a content mark by key.
+//
+//   mark       - handle to a content mark.
+//   key        - string key of the property.
+//   buffer     - buffer for holding the returned value. This is only modified
+//                if |buflen| is at least as long as the length of the value.
+//                Optional, pass null to just retrieve the size of the buffer
+//                needed.
+//   buflen     - length of the buffer.
+//   out_buflen - pointer to variable that will receive the minimum buffer size
+//                to contain the value. Not filled if FALSE is returned.
+//
+// Returns TRUE if the key maps to a string/blob value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_GetParamBlobValue(FPDF_PAGEOBJECTMARK mark,
+                                  FPDF_BYTESTRING key,
+                                  void* buffer,
+                                  unsigned long buflen,
+                                  unsigned long* out_buflen);
+
+// Experimental API.
+// Set the value of an int property in a content mark by key. If a parameter
+// with key |key| exists, its value is set to |value|. Otherwise, it is added as
+// a new parameter.
+//
+//   document    - handle to the document.
+//   page_object - handle to the page object with the mark.
+//   mark        - handle to a content mark.
+//   key         - string key of the property.
+//   value       - int value to set.
+//
+// Returns TRUE if the operation succeeded, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetIntParam(FPDF_DOCUMENT document,
+                            FPDF_PAGEOBJECT page_object,
+                            FPDF_PAGEOBJECTMARK mark,
+                            FPDF_BYTESTRING key,
+                            int value);
+
+// Experimental API.
+// Set the value of a string property in a content mark by key. If a parameter
+// with key |key| exists, its value is set to |value|. Otherwise, it is added as
+// a new parameter.
+//
+//   document    - handle to the document.
+//   page_object - handle to the page object with the mark.
+//   mark        - handle to a content mark.
+//   key         - string key of the property.
+//   value       - string value to set.
+//
+// Returns TRUE if the operation succeeded, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetStringParam(FPDF_DOCUMENT document,
+                               FPDF_PAGEOBJECT page_object,
+                               FPDF_PAGEOBJECTMARK mark,
+                               FPDF_BYTESTRING key,
+                               FPDF_BYTESTRING value);
+
+// Experimental API.
+// Set the value of a blob property in a content mark by key. If a parameter
+// with key |key| exists, its value is set to |value|. Otherwise, it is added as
+// a new parameter.
+//
+//   document    - handle to the document.
+//   page_object - handle to the page object with the mark.
+//   mark        - handle to a content mark.
+//   key         - string key of the property.
+//   value       - pointer to blob value to set.
+//   value_len   - size in bytes of |value|.
+//
+// Returns TRUE if the operation succeeded, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetBlobParam(FPDF_DOCUMENT document,
+                             FPDF_PAGEOBJECT page_object,
+                             FPDF_PAGEOBJECTMARK mark,
+                             FPDF_BYTESTRING key,
+                             void* value,
+                             unsigned long value_len);
+
+// Experimental API.
+// Removes a property from a content mark by key.
+//
+//   page_object - handle to the page object with the mark.
+//   mark        - handle to a content mark.
+//   key         - string key of the property.
+//
+// Returns TRUE if the operation succeeded, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_RemoveParam(FPDF_PAGEOBJECT page_object,
+                            FPDF_PAGEOBJECTMARK mark,
+                            FPDF_BYTESTRING key);
 
 // Load an image from a JPEG image file and then set it into |image_object|.
 //
 //   pages        - pointer to the start of all loaded pages, may be NULL.
-//   nCount       - number of |pages|, may be 0.
+//   count        - number of |pages|, may be 0.
 //   image_object - handle to an image object.
-//   fileAccess   - file access handler which specifies the JPEG image file.
+//   file_access  - file access handler which specifies the JPEG image file.
 //
 // Returns TRUE on success.
 //
 // The image object might already have an associated image, which is shared and
 // cached by the loaded pages. In that case, we need to clear the cached image
-// for all the loaded pages. Pass |pages| and page count (|nCount|) to this API
+// for all the loaded pages. Pass |pages| and page count (|count|) to this API
+// to clear the image cache. If the image is not previously shared, or NULL is a
+// valid |pages| value.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_LoadJpegFile(FPDF_PAGE* pages,
+                          int count,
+                          FPDF_PAGEOBJECT image_object,
+                          FPDF_FILEACCESS* file_access);
+
+// Load an image from a JPEG image file and then set it into |image_object|.
+//
+//   pages        - pointer to the start of all loaded pages, may be NULL.
+//   count        - number of |pages|, may be 0.
+//   image_object - handle to an image object.
+//   file_access  - file access handler which specifies the JPEG image file.
+//
+// Returns TRUE on success.
+//
+// The image object might already have an associated image, which is shared and
+// cached by the loaded pages. In that case, we need to clear the cached image
+// for all the loaded pages. Pass |pages| and page count (|count|) to this API
 // to clear the image cache. If the image is not previously shared, or NULL is a
 // valid |pages| value. This function loads the JPEG image inline, so the image
-// content is copied to the file. This allows |fileAccess| and its associated
+// content is copied to the file. This allows |file_access| and its associated
 // data to be deleted after this function returns.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFImageObj_LoadJpegFileInline(FPDF_PAGE* pages,
-                                int nCount,
+                                int count,
                                 FPDF_PAGEOBJECT image_object,
-                                FPDF_FILEACCESS* fileAccess);
+                                FPDF_FILEACCESS* file_access);
+
+// Experimental API.
+// Get the transform matrix of an image object.
+//
+//   image_object - handle to an image object.
+//   a            - matrix value.
+//   b            - matrix value.
+//   c            - matrix value.
+//   d            - matrix value.
+//   e            - matrix value.
+//   f            - matrix value.
+//
+// The matrix is composed as:
+//   |a c e|
+//   |b d f|
+// and used to scale, rotate, shear and translate the image.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object,
+                       double* a,
+                       double* b,
+                       double* c,
+                       double* d,
+                       double* e,
+                       double* f);
 
 // Set the transform matrix of |image_object|.
 //
@@ -323,7 +595,7 @@
 // The matrix is composed as:
 //   |a c e|
 //   |b d f|
-// and can be used to scale, rotate, shear and translate the |page| annotations.
+// and can be used to scale, rotate, shear and translate the |image_object|.
 //
 // Returns TRUE on success.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -338,14 +610,14 @@
 // Set |bitmap| to |image_object|.
 //
 //   pages        - pointer to the start of all loaded pages, may be NULL.
-//   nCount       - number of |pages|, may be 0.
+//   count        - number of |pages|, may be 0.
 //   image_object - handle to an image object.
 //   bitmap       - handle of the bitmap.
 //
 // Returns TRUE on success.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFImageObj_SetBitmap(FPDF_PAGE* pages,
-                       int nCount,
+                       int count,
                        FPDF_PAGEOBJECT image_object,
                        FPDF_BITMAP bitmap);
 
@@ -365,8 +637,8 @@
 // the decoded image data.
 //
 //   image_object - handle to an image object.
-//   buffer       - buffer for holding the decoded image data in raw bytes.
-//   buflen       - length of the buffer.
+//   buffer       - buffer for holding the decoded image data.
+//   buflen       - length of the buffer in bytes.
 //
 // Returns the length of the decoded image data.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -379,8 +651,8 @@
 // |buflen| is longer than the length of the raw image data.
 //
 //   image_object - handle to an image object.
-//   buffer       - buffer for holding the raw image data in raw bytes.
-//   buflen       - length of the buffer.
+//   buffer       - buffer for holding the raw image data.
+//   buflen       - length of the buffer in bytes.
 //
 // Returns the length of the raw image data.
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -479,46 +751,66 @@
 FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page_object,
                          FPDF_BYTESTRING blend_mode);
 
-// Set the stroke RGBA of a path. Range of values: 0 - 255.
+// Set the stroke RGBA of a page object. Range of values: 0 - 255.
 //
-// path   - the handle to the path object.
-// R      - the red component for the path stroke color.
-// G      - the green component for the path stroke color.
-// B      - the blue component for the path stroke color.
-// A      - the stroke alpha for the path.
+// page_object  - the handle to the page object.
+// R            - the red component for the object's stroke color.
+// G            - the green component for the object's stroke color.
+// B            - the blue component for the object's stroke color.
+// A            - the stroke alpha for the object.
 //
 // Returns TRUE on success.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPath_SetStrokeColor(FPDF_PAGEOBJECT path,
-                        unsigned int R,
-                        unsigned int G,
-                        unsigned int B,
-                        unsigned int A);
+FPDFPageObj_SetStrokeColor(FPDF_PAGEOBJECT page_object,
+                           unsigned int R,
+                           unsigned int G,
+                           unsigned int B,
+                           unsigned int A);
 
-// Get the stroke RGBA of a path. Range of values: 0 - 255.
+// Get the stroke RGBA of a page object. Range of values: 0 - 255.
 //
-// path   - the handle to the path object.
-// R      - the red component of the path stroke color.
-// G      - the green component of the path stroke color.
-// B      - the blue component of the path stroke color.
-// A      - the stroke alpha of the path.
+// page_object  - the handle to the page object.
+// R            - the red component of the path stroke color.
+// G            - the green component of the object's stroke color.
+// B            - the blue component of the object's stroke color.
+// A            - the stroke alpha of the object.
 //
 // Returns TRUE on success.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPath_GetStrokeColor(FPDF_PAGEOBJECT path,
-                        unsigned int* R,
-                        unsigned int* G,
-                        unsigned int* B,
-                        unsigned int* A);
+FPDFPageObj_GetStrokeColor(FPDF_PAGEOBJECT page_object,
+                           unsigned int* R,
+                           unsigned int* G,
+                           unsigned int* B,
+                           unsigned int* A);
 
-// Set the stroke width of a path.
+// Set the stroke width of a page object.
 //
-// path   - the handle to the path object.
+// path   - the handle to the page object.
 // width  - the width of the stroke.
 //
 // Returns TRUE on success
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFPath_SetStrokeWidth(FPDF_PAGEOBJECT path, float width);
+FPDFPageObj_SetStrokeWidth(FPDF_PAGEOBJECT page_object, float width);
+
+// Experimental API.
+// Get the stroke width of a page object.
+//
+// path   - the handle to the page object.
+// width  - the width of the stroke.
+//
+// Returns TRUE on success
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetStrokeWidth(FPDF_PAGEOBJECT page_object, float* width);
+
+// Get the line join of |page_object|.
+//
+// page_object  - handle to a page object.
+//
+// Returns the line join, or -1 on failure.
+// Line join can be one of following: FPDF_LINEJOIN_MITER, FPDF_LINEJOIN_ROUND,
+// FPDF_LINEJOIN_BEVEL
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_GetLineJoin(FPDF_PAGEOBJECT page_object);
 
 // Set the line join of |page_object|.
 //
@@ -527,8 +819,18 @@
 //
 // Line join can be one of following: FPDF_LINEJOIN_MITER, FPDF_LINEJOIN_ROUND,
 // FPDF_LINEJOIN_BEVEL
-FPDF_EXPORT void FPDF_CALLCONV FPDFPath_SetLineJoin(FPDF_PAGEOBJECT page_object,
-                                                    int line_join);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetLineJoin(FPDF_PAGEOBJECT page_object, int line_join);
+
+// Get the line cap of |page_object|.
+//
+// page_object - handle to a page object.
+//
+// Returns the line cap, or -1 on failure.
+// Line cap can be one of following: FPDF_LINECAP_BUTT, FPDF_LINECAP_ROUND,
+// FPDF_LINECAP_PROJECTING_SQUARE
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPageObj_GetLineCap(FPDF_PAGEOBJECT page_object);
 
 // Set the line cap of |page_object|.
 //
@@ -537,38 +839,40 @@
 //
 // Line cap can be one of following: FPDF_LINECAP_BUTT, FPDF_LINECAP_ROUND,
 // FPDF_LINECAP_PROJECTING_SQUARE
-FPDF_EXPORT void FPDF_CALLCONV FPDFPath_SetLineCap(FPDF_PAGEOBJECT page_object,
-                                                   int line_cap);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetLineCap(FPDF_PAGEOBJECT page_object, int line_cap);
 
-// Set the fill RGBA of a path. Range of values: 0 - 255.
+// Set the fill RGBA of a page object. Range of values: 0 - 255.
 //
-// path   - the handle to the path object.
-// R      - the red component for the path fill color.
-// G      - the green component for the path fill color.
-// B      - the blue component for the path fill color.
-// A      - the fill alpha for the path.
+// page_object  - the handle to the page object.
+// R            - the red component for the object's fill color.
+// G            - the green component for the object's fill color.
+// B            - the blue component for the object's fill color.
+// A            - the fill alpha for the object.
 //
 // Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetFillColor(FPDF_PAGEOBJECT path,
-                                                          unsigned int R,
-                                                          unsigned int G,
-                                                          unsigned int B,
-                                                          unsigned int A);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object,
+                         unsigned int R,
+                         unsigned int G,
+                         unsigned int B,
+                         unsigned int A);
 
-// Get the fill RGBA of a path. Range of values: 0 - 255.
+// Get the fill RGBA of a page object. Range of values: 0 - 255.
 //
-// path   - the handle to the path object.
-// R      - the red component of the path fill color.
-// G      - the green component of the path fill color.
-// B      - the blue component of the path fill color.
-// A      - the fill alpha of the path.
+// page_object  - the handle to the page object.
+// R            - the red component of the object's fill color.
+// G            - the green component of the object's fill color.
+// B            - the blue component of the object's fill color.
+// A            - the fill alpha of the object.
 //
 // Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetFillColor(FPDF_PAGEOBJECT path,
-                                                          unsigned int* R,
-                                                          unsigned int* G,
-                                                          unsigned int* B,
-                                                          unsigned int* A);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_GetFillColor(FPDF_PAGEOBJECT page_object,
+                         unsigned int* R,
+                         unsigned int* G,
+                         unsigned int* B,
+                         unsigned int* A);
 
 // Experimental API.
 // Get number of segments inside |path|.
@@ -679,8 +983,7 @@
 // Set the drawing mode of a path.
 //
 // path     - the handle to the path object.
-// fillmode - the filling mode to be set: 0 for no fill, 1 for alternate, 2 for
-// winding.
+// fillmode - the filling mode to be set: one of the FPDF_FILLMODE_* flags.
 // stroke   - a boolean specifying if the path should be stroked or not.
 //
 // Returns TRUE on success
@@ -688,6 +991,48 @@
                                                          int fillmode,
                                                          FPDF_BOOL stroke);
 
+// Experimental API.
+// Get the drawing mode of a path.
+//
+// path     - the handle to the path object.
+// fillmode - the filling mode of the path: one of the FPDF_FILLMODE_* flags.
+// stroke   - a boolean specifying if the path is stroked or not.
+//
+// Returns TRUE on success
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetDrawMode(FPDF_PAGEOBJECT path,
+                                                         int* fillmode,
+                                                         FPDF_BOOL* stroke);
+
+// Experimental API.
+// Get the transform matrix of a path.
+//
+//   path   - handle to a path.
+//   matrix - pointer to struct to receive the matrix value.
+//
+// The matrix is composed as:
+//   |a c e|
+//   |b d f|
+// and used to scale, rotate, shear and translate the path.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetMatrix(FPDF_PAGEOBJECT path,
+                                                       FS_MATRIX* matrix);
+
+// Experimental API.
+// Set the transform matrix of a path.
+//
+//   path   - handle to a path.
+//   matrix - pointer to struct with the matrix value.
+//
+// The matrix is composed as:
+//   |a c e|
+//   |b d f|
+// and can be used to scale, rotate, shear and translate the path.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetMatrix(FPDF_PAGEOBJECT path,
+                                                       const FS_MATRIX* matrix);
+
 // Create a new text object using one of the standard PDF fonts.
 //
 // document   - handle to the document.
@@ -719,7 +1064,7 @@
 // type.
 // cid        - a boolean specifying if the font is a CID font or not.
 //
-// The loaded font can be closed using FPDF_Font_Close.
+// The loaded font can be closed using FPDFFont_Close.
 //
 // Returns NULL on failure
 FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFText_LoadFont(FPDF_DOCUMENT document,
@@ -728,21 +1073,43 @@
                                                       int font_type,
                                                       FPDF_BOOL cid);
 
-// Set the fill RGBA of a text object. Range of values: 0 - 255.
+// Experimental API.
+// Loads one of the standard 14 fonts per PDF spec 1.7 page 416. The preferred
+// way of using font style is using a dash to separate the name from the style,
+// for example 'Helvetica-BoldItalic'.
 //
-// text_object  - handle to the text object.
-// R            - the red component for the path fill color.
-// G            - the green component for the path fill color.
-// B            - the blue component for the path fill color.
-// A            - the fill alpha for the path.
+// document   - handle to the document.
+// font       - string containing the font name, without spaces.
+//
+// The loaded font can be closed using FPDFFont_Close.
+//
+// Returns NULL on failure.
+FPDF_EXPORT FPDF_FONT FPDF_CALLCONV
+FPDFText_LoadStandardFont(FPDF_DOCUMENT document, FPDF_BYTESTRING font);
+
+// Experimental API.
+// Get the transform matrix of a text object.
+//
+//   text   - handle to a text.
+//   matrix - pointer to struct with the matrix value.
+//
+// The matrix is composed as:
+//   |a c e|
+//   |b d f|
+// and used to scale, rotate, shear and translate the text.
 //
 // Returns TRUE on success.
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDFText_SetFillColor(FPDF_PAGEOBJECT text_object,
-                      unsigned int R,
-                      unsigned int G,
-                      unsigned int B,
-                      unsigned int A);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFTextObj_GetMatrix(FPDF_PAGEOBJECT text,
+                                                          FS_MATRIX* matrix);
+
+// Experimental API.
+// Get the font size of a text object.
+//
+//   text - handle to a text.
+//
+// Returns the font size of the text object, measured in points (about 1/72
+// inch) on success; 0 on failure.
+FPDF_EXPORT float FPDF_CALLCONV FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text);
 
 // Close a loaded PDF font.
 //
@@ -761,6 +1128,100 @@
                           FPDF_FONT font,
                           float font_size);
 
+// Experimental API.
+// Get the text rendering mode of a text object.
+//
+// text     - the handle to the text object.
+//
+// Returns one of the known FPDF_TEXT_RENDERMODE enum values on success,
+// FPDF_TEXTRENDERMODE_UNKNOWN on error.
+FPDF_EXPORT FPDF_TEXT_RENDERMODE FPDF_CALLCONV
+FPDFTextObj_GetTextRenderMode(FPDF_PAGEOBJECT text);
+
+// Experimental API.
+// Set the text rendering mode of a text object.
+//
+// text         - the handle to the text object.
+// render_mode  - the FPDF_TEXT_RENDERMODE enum value to be set (cannot set to
+//                FPDF_TEXTRENDERMODE_UNKNOWN).
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFTextObj_SetTextRenderMode(FPDF_PAGEOBJECT text,
+                              FPDF_TEXT_RENDERMODE render_mode);
+
+// Experimental API.
+// Get the font name of a text object.
+//
+// text             - the handle to the text object.
+// buffer           - the address of a buffer that receives the font name.
+// length           - the size, in bytes, of |buffer|.
+//
+// Returns the number of bytes in the font name (including the trailing NUL
+// character) on success, 0 on error.
+//
+// Regardless of the platform, the |buffer| is always in UTF-8 encoding.
+// If |length| is less than the returned length, or |buffer| is NULL, |buffer|
+// will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFTextObj_GetFontName(FPDF_PAGEOBJECT text,
+                        void* buffer,
+                        unsigned long length);
+
+// Experimental API.
+// Get the text of a text object.
+//
+// text_object      - the handle to the text object.
+// text_page        - the handle to the text page.
+// buffer           - the address of a buffer that receives the text.
+// length           - the size, in bytes, of |buffer|.
+//
+// Returns the number of bytes in the text (including the trailing NUL
+// character) on success, 0 on error.
+//
+// Regardless of the platform, the |buffer| is always in UTF-16LE encoding.
+// If |length| is less than the returned length, or |buffer| is NULL, |buffer|
+// will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object,
+                    FPDF_TEXTPAGE text_page,
+                    void* buffer,
+                    unsigned long length);
+
+// Experimental API.
+// Get number of page objects inside |form_object|.
+//
+//   form_object - handle to a form object.
+//
+// Returns the number of objects in |form_object| on success, -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object);
+
+// Experimental API.
+// Get page object in |form_object| at |index|.
+//
+//   form_object - handle to a form object.
+//   index       - the 0-based index of a page object.
+//
+// Returns the handle to the page object, or NULL on error.
+FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
+FPDFFormObj_GetObject(FPDF_PAGEOBJECT form_object, unsigned long index);
+
+// Experimental API.
+// Get the transform matrix of a form object.
+//
+//   form_object - handle to a form.
+//   matrix      - pointer to struct to receive the matrix value.
+//
+// The matrix is composed as:
+//   |a c e|
+//   |b d f|
+// and used to scale, rotate, shear and translate the form object.
+//
+// Returns TRUE on success.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFFormObj_GetMatrix(FPDF_PAGEOBJECT form_object, FS_MATRIX* matrix);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/public/fpdf_ext.h b/public/fpdf_ext.h
index e488c52..b1784dd 100644
--- a/public/fpdf_ext.h
+++ b/public/fpdf_ext.h
@@ -7,6 +7,8 @@
 #ifndef PUBLIC_FPDF_EXT_H_
 #define PUBLIC_FPDF_EXT_H_
 
+#include <time.h>
+
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
@@ -67,6 +69,25 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FSDK_SetUnSpObjProcessHandler(UNSUPPORT_INFO* unsp_info);
 
+// Set replacement function for calls to time().
+//
+// This API is intended to be used only for testing, thus may cause PDFium to
+// behave poorly in production environments.
+//
+//   func - Function pointer to alternate implementation of time(), or
+//          NULL to restore to actual time() call itself.
+FPDF_EXPORT void FPDF_CALLCONV FSDK_SetTimeFunction(time_t (*func)());
+
+// Set replacement function for calls to localtime().
+//
+// This API is intended to be used only for testing, thus may cause PDFium to
+// behave poorly in production environments.
+//
+//   func - Function pointer to alternate implementation of localtime(), or
+//          NULL to restore to actual localtime() call itself.
+FPDF_EXPORT void FPDF_CALLCONV
+FSDK_SetLocaltimeFunction(struct tm* (*func)(const time_t*));
+
 // Unknown page mode.
 #define PAGEMODE_UNKNOWN -1
 // Document outline, and thumbnails hidden.
diff --git a/public/fpdf_formfill.h b/public/fpdf_formfill.h
index c2e2bd5..95c24b4 100644
--- a/public/fpdf_formfill.h
+++ b/public/fpdf_formfill.h
@@ -7,19 +7,42 @@
 #ifndef PUBLIC_FPDF_FORMFILL_H_
 #define PUBLIC_FPDF_FORMFILL_H_
 
+// clang-format off
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
-typedef void* FPDF_FORMHANDLE;
-
 // These values are return values for a public API, so should not be changed
 // other than the count when adding new values.
-#define FORMTYPE_NONE 0       // Document contains no forms
-#define FORMTYPE_ACRO_FORM 1  // Forms are specified using AcroForm spec
-#define FORMTYPE_XFA_FULL 2   // Forms are specified using the entire XFA spec
-#define FORMTYPE_XFA_FOREGROUND \
-  3  // Forms are specified using the XFAF subset of XFA spec
-#define FORMTYPE_COUNT 4  // The number of form types
+#define FORMTYPE_NONE 0            // Document contains no forms
+#define FORMTYPE_ACRO_FORM 1       // Forms are specified using AcroForm spec
+#define FORMTYPE_XFA_FULL 2        // Forms are specified using entire XFA spec
+#define FORMTYPE_XFA_FOREGROUND 3  // Forms are specified using the XFAF subset
+                                   // of XFA spec
+#define FORMTYPE_COUNT 4           // The number of form types
+
+#define JSPLATFORM_ALERT_BUTTON_OK 0           // OK button
+#define JSPLATFORM_ALERT_BUTTON_OKCANCEL 1     // OK & Cancel buttons
+#define JSPLATFORM_ALERT_BUTTON_YESNO 2        // Yes & No buttons
+#define JSPLATFORM_ALERT_BUTTON_YESNOCANCEL 3  // Yes, No & Cancel buttons
+#define JSPLATFORM_ALERT_BUTTON_DEFAULT JSPLATFORM_ALERT_BUTTON_OK
+
+#define JSPLATFORM_ALERT_ICON_ERROR 0     // Error
+#define JSPLATFORM_ALERT_ICON_WARNING 1   // Warning
+#define JSPLATFORM_ALERT_ICON_QUESTION 2  // Question
+#define JSPLATFORM_ALERT_ICON_STATUS 3    // Status
+#define JSPLATFORM_ALERT_ICON_ASTERISK 4  // Asterisk
+#define JSPLATFORM_ALERT_ICON_DEFAULT JSPLATFORM_ALERT_ICON_ERROR
+
+#define JSPLATFORM_ALERT_RETURN_OK 1      // OK
+#define JSPLATFORM_ALERT_RETURN_CANCEL 2  // Cancel
+#define JSPLATFORM_ALERT_RETURN_NO 3      // No
+#define JSPLATFORM_ALERT_RETURN_YES 4     // Yes
+
+#define JSPLATFORM_BEEP_ERROR 0           // Error
+#define JSPLATFORM_BEEP_WARNING 1         // Warning
+#define JSPLATFORM_BEEP_QUESTION 2        // Question
+#define JSPLATFORM_BEEP_STATUS 3          // Status
+#define JSPLATFORM_BEEP_DEFAULT 4         // Default
 
 // Exported Functions
 #ifdef __cplusplus
@@ -27,103 +50,89 @@
 #endif
 
 typedef struct _IPDF_JsPlatform {
-  /**
-  * Version number of the interface. Currently must be 2.
-  **/
+  /*
+   * Version number of the interface. Currently must be 2.
+   */
   int version;
 
   /* Version 1. */
 
-  /**
-  * Method: app_alert
-  *           pop up a dialog to show warning or hint.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  *           Msg         -   A string containing the message to be displayed.
-  *           Title       -   The title of the dialog.
-  *           Type        -   The stype of button group.
-  *                           0-OK(default);
-  *                           1-OK,Cancel;
-  *                           2-Yes,NO;
-  *                           3-Yes, NO, Cancel.
-  *           nIcon       -   The Icon type.
-  *                           0-Error(default);
-  *                           1-Warning;
-  *                           2-Question;
-  *                           3-Status.
-  *                           4-Asterisk
-  * Return Value:
-  *           The return value could be the folowing type:
-  *                           1-OK;
-  *                           2-Cancel;
-  *                           3-NO;
-  *                           4-Yes;
-  */
+  /*
+   * Method: app_alert
+   *       Pop up a dialog to show warning or hint.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       Msg         -   A string containing the message to be displayed.
+   *       Title       -   The title of the dialog.
+   *       Type        -   The type of button group, one of the
+   *                       JSPLATFORM_ALERT_BUTTON_* values above.
+   *       nIcon       -   The type of the icon, one of the
+   *                       JSPLATFORM_ALERT_ICON_* above.
+   * Return Value:
+   *       Option selected by user in dialogue, one of the
+   *       JSPLATFORM_ALERT_RETURN_* values above.
+   */
   int (*app_alert)(struct _IPDF_JsPlatform* pThis,
                    FPDF_WIDESTRING Msg,
                    FPDF_WIDESTRING Title,
                    int Type,
                    int Icon);
 
-  /**
-  * Method: app_beep
-  *           Causes the system to play a sound.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  *           nType       -   The sound type.
-  *                           0 - Error
-  *                           1 - Warning
-  *                           2 - Question
-  *                           3 - Status
-  *                           4 - Default (default value)
-  * Return Value:
-  *           None
-  */
+  /*
+   * Method: app_beep
+   *       Causes the system to play a sound.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself
+   *       nType       -   The sound type, see JSPLATFORM_BEEP_TYPE_*
+   *                       above.
+   * Return Value:
+   *       None
+   */
   void (*app_beep)(struct _IPDF_JsPlatform* pThis, int nType);
 
-  /**
-  * Method: app_response
-  *           Displays a dialog box containing a question and an entry field for
-  * the user to reply to the question.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  *           Question    -   The question to be posed to the user.
-  *           Title       -   The title of the dialog box.
-  *           Default     -   A default value for the answer to the question. If
-  * not specified, no default value is presented.
-  *           cLabel      -   A short string to appear in front of and on the
-  * same line as the edit text field.
-  *           bPassword   -   If true, indicates that the user's response should
-  * show as asterisks (*) or bullets (?) to mask the response, which might be
-  * sensitive information. The default is false.
-  *           response    -   A string buffer allocated by SDK, to receive the
-  * user's response.
-  *           length      -   The length of the buffer, number of bytes.
-  * Currently, It's always be 2048.
-  * Return Value:
-  *       Number of bytes the complete user input would actually require, not
-  * including trailing zeros, regardless of the value of the length
-  *       parameter or the presence of the response buffer.
-  * Comments:
-  *       No matter on what platform, the response buffer should be always
-  * written using UTF-16LE encoding. If a response buffer is
-  *       present and the size of the user input exceeds the capacity of the
-  * buffer as specified by the length parameter, only the
-  *       first "length" bytes of the user input are to be written to the
-  * buffer.
-  */
+  /*
+   * Method: app_response
+   *       Displays a dialog box containing a question and an entry field for
+   *       the user to reply to the question.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself
+   *       Question    -   The question to be posed to the user.
+   *       Title       -   The title of the dialog box.
+   *       Default     -   A default value for the answer to the question. If
+   *                       not specified, no default value is presented.
+   *       cLabel      -   A short string to appear in front of and on the
+   *                       same line as the edit text field.
+   *       bPassword   -   If true, indicates that the user's response should
+   *                       be shown as asterisks (*) or bullets (?) to mask
+   *                       the response, which might be sensitive information.
+   *       response    -   A string buffer allocated by PDFium, to receive the
+   *                       user's response.
+   *       length      -   The length of the buffer in bytes. Currently, it is
+   *                       always 2048.
+   * Return Value:
+   *       Number of bytes the complete user input would actually require, not
+   *       including trailing zeros, regardless of the value of the length
+   *       parameter or the presence of the response buffer.
+   * Comments:
+   *       No matter on what platform, the response buffer should be always
+   *       written using UTF-16LE encoding. If a response buffer is
+   *       present and the size of the user input exceeds the capacity of the
+   *       buffer as specified by the length parameter, only the
+   *       first "length" bytes of the user input are to be written to the
+   *       buffer.
+   */
   int (*app_response)(struct _IPDF_JsPlatform* pThis,
                       FPDF_WIDESTRING Question,
                       FPDF_WIDESTRING Title,
@@ -134,63 +143,64 @@
                       int length);
 
   /*
-  * Method: Doc_getFilePath
-  *           Get the file path of the current document.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  *           filePath    -   The string buffer to receive the file path. Can be
-  * NULL.
-  *           length      -   The length of the buffer, number of bytes. Can be
-  * 0.
-  * Return Value:
-  *       Number of bytes the filePath consumes, including trailing zeros.
-  * Comments:
-  *       The filePath should be always input in local encoding.
-  *
-  *       The return value always indicated number of bytes required for the
-  *       buffer , even when there is no buffer specified, or the buffer size is
-  *       less than required. In this case, the buffer will not be modified.
-  */
+   * Method: Doc_getFilePath
+   *       Get the file path of the current document.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself
+   *       filePath    -   The string buffer to receive the file path. Can
+   *                       be NULL.
+   *       length      -   The length of the buffer, number of bytes. Can
+   *                       be 0.
+   * Return Value:
+   *       Number of bytes the filePath consumes, including trailing zeros.
+   * Comments:
+   *       The filePath should always be provided in the local encoding.
+   *       The return value always indicated number of bytes required for
+   *       the buffer, even when there is no buffer specified, or the buffer
+   *       size is less than required. In this case, the buffer will not
+   *       be modified.
+   */
   int (*Doc_getFilePath)(struct _IPDF_JsPlatform* pThis,
                          void* filePath,
                          int length);
 
   /*
-  * Method: Doc_mail
-  *           Mails the data buffer as an attachment to all recipients, with or
-  * without user interaction.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  *           mailData    -   Pointer to the data buffer to be sent.Can be NULL.
-  *           length      -   The size,in bytes, of the buffer pointed by
-  * mailData parameter.Can be 0.
-  *           bUI         -   If true, the rest of the parameters are used in a
-  * compose-new-message window that is displayed to the user. If false, the cTo
-  * parameter is required and all others are optional.
-  *           To          -   A semicolon-delimited list of recipients for the
-  * message.
-  *           Subject     -   The subject of the message. The length limit is 64
-  * KB.
-  *           CC          -   A semicolon-delimited list of CC recipients for
-  * the message.
-  *           BCC         -   A semicolon-delimited list of BCC recipients for
-  * the message.
-  *           Msg         -   The content of the message. The length limit is 64
-  * KB.
-  * Return Value:
-  *           None.
-  * Comments:
-  *           If the parameter mailData is NULL or length is 0, the current
-  * document will be mailed as an attachment to all recipients.
-  */
+   * Method: Doc_mail
+   *       Mails the data buffer as an attachment to all recipients, with or
+   *       without user interaction.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself
+   *       mailData    -   Pointer to the data buffer to be sent. Can be NULL.
+   *       length      -   The size,in bytes, of the buffer pointed by
+   *                       mailData parameter. Can be 0.
+   *       bUI         -   If true, the rest of the parameters are used in a
+   *                       compose-new-message window that is displayed to the
+   *                       user. If false, the cTo parameter is required and
+   *                       all others are optional.
+   *       To          -   A semicolon-delimited list of recipients for the
+   *                       message.
+   *       Subject     -   The subject of the message. The length limit is
+   *                       64 KB.
+   *       CC          -   A semicolon-delimited list of CC recipients for
+   *                       the message.
+   *       BCC         -   A semicolon-delimited list of BCC recipients for
+   *                       the message.
+   *       Msg         -   The content of the message. The length limit is
+   *                       64 KB.
+   * Return Value:
+   *       None.
+   * Comments:
+   *       If the parameter mailData is NULL or length is 0, the current
+   *       document will be mailed as an attachment to all recipients.
+   */
   void (*Doc_mail)(struct _IPDF_JsPlatform* pThis,
                    void* mailData,
                    int length,
@@ -202,29 +212,32 @@
                    FPDF_WIDESTRING Msg);
 
   /*
-  * Method: Doc_print
-  *           Prints all or a specific number of pages of the document.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself.
-  *           bUI         -   If true, will cause a UI to be presented to the
-  * user to obtain printing information and confirm the action.
-  *           nStart      -   A 0-based index that defines the start of an
-  * inclusive range of pages.
-  *           nEnd        -   A 0-based index that defines the end of an
-  * inclusive page range.
-  *           bSilent     -   If true, suppresses the cancel dialog box while
-  * the document is printing. The default is false.
-  *           bShrinkToFit    -   If true, the page is shrunk (if necessary) to
-  * fit within the imageable area of the printed page.
-  *           bPrintAsImage   -   If true, print pages as an image.
-  *           bReverse    -   If true, print from nEnd to nStart.
-  *           bAnnotations    -   If true (the default), annotations are
-  * printed.
-  */
+   * Method: Doc_print
+   *       Prints all or a specific number of pages of the document.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis         -   Pointer to the interface structure itself.
+   *       bUI           -   If true, will cause a UI to be presented to the
+   *                         user to obtain printing information and confirm
+   *                         the action.
+   *       nStart        -   A 0-based index that defines the start of an
+   *                         inclusive range of pages.
+   *       nEnd          -   A 0-based index that defines the end of an
+   *                         inclusive page range.
+   *       bSilent       -   If true, suppresses the cancel dialog box while
+   *                         the document is printing. The default is false.
+   *       bShrinkToFit  -   If true, the page is shrunk (if necessary) to
+   *                         fit within the imageable area of the printed page.
+   *       bPrintAsImage -   If true, print pages as an image.
+   *       bReverse      -   If true, print from nEnd to nStart.
+   *       bAnnotations  -   If true (the default), annotations are
+   *                         printed.
+   * Return Value:
+   *       None.
+   */
   void (*Doc_print)(struct _IPDF_JsPlatform* pThis,
                     FPDF_BOOL bUI,
                     int nStart,
@@ -236,68 +249,66 @@
                     FPDF_BOOL bAnnotations);
 
   /*
-  * Method: Doc_submitForm
-  *           Send the form data to a specified URL.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  *           formData    -   Pointer to the data buffer to be sent.
-  *           length      -   The size,in bytes, of the buffer pointed by
-  * formData parameter.
-  *           URL         -   The URL to send to.
-  * Return Value:
-  *           None.
-  *
-  */
+   * Method: Doc_submitForm
+   *       Send the form data to a specified URL.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself
+   *       formData    -   Pointer to the data buffer to be sent.
+   *       length      -   The size,in bytes, of the buffer pointed by
+   *                       formData parameter.
+   *       URL         -   The URL to send to.
+   * Return Value:
+   *       None.
+   */
   void (*Doc_submitForm)(struct _IPDF_JsPlatform* pThis,
                          void* formData,
                          int length,
                          FPDF_WIDESTRING URL);
 
   /*
-  * Method: Doc_gotoPage
-  *           Jump to a specified page.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  *           nPageNum    -   The specified page number, zero for the first
-  * page.
-  * Return Value:
-  *           None.
-  *
-  */
+   * Method: Doc_gotoPage
+   *       Jump to a specified page.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself
+   *       nPageNum    -   The specified page number, zero for the first page.
+   * Return Value:
+   *       None.
+   *
+   */
   void (*Doc_gotoPage)(struct _IPDF_JsPlatform* pThis, int nPageNum);
+
   /*
-  * Method: Field_browse
-  *           Show a file selection dialog, and return the selected file path.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself.
-  *           filePath    -   Pointer to the data buffer to receive the file
-  * path.Can be NULL.
-  *           length      -   The length of the buffer, number of bytes. Can be
-  * 0.
-  * Return Value:
-  *       Number of bytes the filePath consumes, including trailing zeros.
-  * Comments:
-  *       The filePath shoule be always input in local encoding.
-  */
+   * Method: Field_browse
+   *       Show a file selection dialog, and return the selected file path.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       filePath    -   Pointer to the data buffer to receive the file
+   *                       path. Can be NULL.
+   *       length      -   The length of the buffer, in bytes. Can be 0.
+   * Return Value:
+   *       Number of bytes the filePath consumes, including trailing zeros.
+   * Comments:
+   *       The filePath shoule always be provided in local encoding.
+   */
   int (*Field_browse)(struct _IPDF_JsPlatform* pThis,
                       void* filePath,
                       int length);
 
-  /**
-  *   pointer to FPDF_FORMFILLINFO interface.
-  **/
+  /*
+   * Pointer to FPDF_FORMFILLINFO interface.
+   */
   void* m_pFormfillinfo;
 
   /* Version 2. */
@@ -317,19 +328,19 @@
 #define FXCT_HBEAM 4
 #define FXCT_HAND 5
 
-/**
+/*
  * Function signature for the callback function passed to the FFI_SetTimer
  * method.
  * Parameters:
  *          idEvent     -   Identifier of the timer.
  * Return value:
  *          None.
- **/
+ */
 typedef void (*TimerCallback)(int idEvent);
 
-/**
+/*
  * Declares of a struct type to the local system time.
-**/
+ */
 typedef struct _FPDF_SYSTEMTIME {
   unsigned short wYear;         /* years since 1900 */
   unsigned short wMonth;        /* months since January - [0,11] */
@@ -342,92 +353,75 @@
 } FPDF_SYSTEMTIME;
 
 #ifdef PDF_ENABLE_XFA
-// XFA
-/**
- * @name Pageview  event flags
- */
-/*@{*/
-/** @brief After a new pageview is added. */
-#define FXFA_PAGEVIEWEVENT_POSTADDED 1
-/** @brief After a pageview is removed. */
-#define FXFA_PAGEVIEWEVENT_POSTREMOVED 3
-/*@}*/
 
-// menu
-/**
- * @name Macro Definitions for Right Context Menu Features Of XFA Fields
- */
-/*@{*/
+// Pageview event flags
+#define FXFA_PAGEVIEWEVENT_POSTADDED 1    // After a new pageview is added.
+#define FXFA_PAGEVIEWEVENT_POSTREMOVED 3  // After a pageview is removed.
+
+// Definitions for Right Context Menu Features Of XFA Fields
 #define FXFA_MENU_COPY 1
 #define FXFA_MENU_CUT 2
 #define FXFA_MENU_SELECTALL 4
 #define FXFA_MENU_UNDO 8
 #define FXFA_MENU_REDO 16
 #define FXFA_MENU_PASTE 32
-/*@}*/
 
-// file type
-/**
- * @name Macro Definitions for File Type.
- */
-/*@{*/
+// Definitions for File Type.
 #define FXFA_SAVEAS_XML 1
 #define FXFA_SAVEAS_XDP 2
-/*@}*/
+
 #endif  // PDF_ENABLE_XFA
 
 typedef struct _FPDF_FORMFILLINFO {
-  /**
+  /*
    * Version number of the interface. Currently must be 1 (when PDFium is built
    *  without the XFA module) or must be 2 (when built with the XFA module).
-   **/
+   */
   int version;
 
   /* Version 1. */
-  /**
-   *Method: Release
-   *         Give implementation a chance to release any data after the
-   *         interface is no longer used
-   *Interface Version:
-   *         1
-   *Implementation Required:
-   *         No
-   *Comments:
-   *         Called by Foxit SDK during the final cleanup process.
-   *Parameters:
-   *         pThis       -   Pointer to the interface structure itself
-   *Return Value:
-   *         None
+  /*
+   * Method: Release
+   *       Give the implementation a chance to release any resources after the
+   *       interface is no longer used.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       No
+   * Comments:
+   *       Called by PDFium during the final cleanup process.
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself
+   * Return Value:
+   *       None
    */
   void (*Release)(struct _FPDF_FORMFILLINFO* pThis);
 
-  /**
+  /*
    * Method: FFI_Invalidate
-   *          Invalidate the client area within the specified rectangle.
+   *       Invalidate the client area within the specified rectangle.
    * Interface Version:
-   *          1
+   *       1
    * Implementation Required:
-      *           yes
+   *       yes
    * Parameters:
-   *          pThis       -   Pointer to the interface structure itself.
-   *          page        -   Handle to the page. Returned by FPDF_LoadPage
-   *function.
-   *          left        -   Left position of the client area in PDF page
-   *coordinate.
-   *          top         -   Top  position of the client area in PDF page
-   *coordinate.
-   *          right       -   Right position of the client area in PDF page
-   *coordinate.
-   *          bottom      -   Bottom position of the client area in PDF page
-   *coordinate.
+   *       pThis       -   Pointer to the interface structure itself.
+   *       page        -   Handle to the page. Returned by FPDF_LoadPage().
+   *       left        -   Left position of the client area in PDF page
+   *                       coordinates.
+   *       top         -   Top position of the client area in PDF page
+   *                       coordinates.
+   *       right       -   Right position of the client area in PDF page
+   *                       coordinates.
+   *       bottom      -   Bottom position of the client area in PDF page
+   *                       coordinates.
    * Return Value:
-   *          None.
-   *
-   *comments:
-   *          All positions are measured in PDF "user space".
-   *          Implementation should call FPDF_RenderPageBitmap() function for
-   *repainting a specified page area.
-  */
+   *       None.
+   * Comments:
+   *       All positions are measured in PDF "user space".
+   *       Implementation should call FPDF_RenderPageBitmap() for repainting
+   *       the specified page area.
+   */
   void (*FFI_Invalidate)(struct _FPDF_FORMFILLINFO* pThis,
                          FPDF_PAGE page,
                          double left,
@@ -435,38 +429,34 @@
                          double right,
                          double bottom);
 
-  /**
+  /*
    * Method: FFI_OutputSelectedRect
-   *          When user is taking the mouse to select texts on a form field,
-   * this callback function will keep
-   *          returning the selected areas to the implementation.
-   *
+   *       When the user selects text in form fields with the mouse, this
+   *       callback function will be invoked with the selected areas.
    * Interface Version:
-   *          1
+   *       1
    * Implementation Required:
-   *          No
+   *       No
    * Parameters:
-   *          pThis       -   Pointer to the interface structure itself.
-   *          page        -   Handle to the page. Returned by FPDF_LoadPage
-   * function.
-   *          left        -   Left position of the client area in PDF page
-   * coordinate.
-   *          top         -   Top  position of the client area in PDF page
-   * coordinate.
-   *          right       -   Right position of the client area in PDF page
-   * coordinate.
-   *          bottom      -   Bottom position of the client area in PDF page
-   * coordinate.
+   *       pThis       -   Pointer to the interface structure itself.
+   *       page        -   Handle to the page. Returned by FPDF_LoadPage()/
+   *       left        -   Left position of the client area in PDF page
+   *                       coordinates.
+   *       top         -   Top position of the client area in PDF page
+   *                       coordinates.
+   *       right       -   Right position of the client area in PDF page
+   *                       coordinates.
+   *       bottom      -   Bottom position of the client area in PDF page
+   *                       coordinates.
    * Return Value:
-   *          None.
-   *
-   * comments:
-   *          This CALLBACK function is useful for implementing special text
-   * selection effect. Implementation should
-   *          first records the returned rectangles, then draw them one by one
-   * at the painting period, last,remove all
-   *          the recorded rectangles when finish painting.
-  */
+   *       None.
+   * Comments:
+   *       This callback function is useful for implementing special text
+   *       selection effects. An implementation should first record the
+   *       returned rectangles, then draw them one by one during the next
+   *       painting period. Lastly, it should remove all the recorded
+   *       rectangles when finished painting.
+   */
   void (*FFI_OutputSelectedRect)(struct _FPDF_FORMFILLINFO* pThis,
                                  FPDF_PAGE page,
                                  double left,
@@ -474,274 +464,289 @@
                                  double right,
                                  double bottom);
 
-  /**
-  * Method: FFI_SetCursor
-  *           Set the Cursor shape.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *       nCursorType -   Cursor type. see Flags for Cursor type for the
-  * details.
-  *   Return value:
-  *       None.
-  * */
+  /*
+   * Method: FFI_SetCursor
+   *       Set the Cursor shape.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       nCursorType -   Cursor type, see Flags for Cursor type for details.
+   * Return value:
+   *       None.
+   */
   void (*FFI_SetCursor)(struct _FPDF_FORMFILLINFO* pThis, int nCursorType);
 
-  /**
-  * Method: FFI_SetTimer
-  *       This method installs a system timer. An interval value is specified,
-  *       and every time that interval elapses, the system must call into the
-  *       callback function with the timer ID as returned by this function.
-  * Interface Version:
-  *       1
-  * Implementation Required:
-  *       yes
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *       uElapse     -   Specifies the time-out value, in milliseconds.
-  *       lpTimerFunc -   A pointer to the callback function-TimerCallback.
-  * Return value:
-  *       The timer identifier of the new timer if the function is successful.
-  *       An application passes this value to the FFI_KillTimer method to kill
-  *       the timer. Nonzero if it is successful; otherwise, it is zero.
-  * */
+  /*
+   * Method: FFI_SetTimer
+   *       This method installs a system timer. An interval value is specified,
+   *       and every time that interval elapses, the system must call into the
+   *       callback function with the timer ID as returned by this function.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       uElapse     -   Specifies the time-out value, in milliseconds.
+   *       lpTimerFunc -   A pointer to the callback function-TimerCallback.
+   * Return value:
+   *       The timer identifier of the new timer if the function is successful.
+   *       An application passes this value to the FFI_KillTimer method to kill
+   *       the timer. Nonzero if it is successful; otherwise, it is zero.
+   */
   int (*FFI_SetTimer)(struct _FPDF_FORMFILLINFO* pThis,
                       int uElapse,
                       TimerCallback lpTimerFunc);
 
-  /**
-  * Method: FFI_KillTimer
-  *       This method uninstalls a system timer identified by nIDEvent, as
-  *       set by an earlier call to FFI_SetTimer.
-  * Interface Version:
-  *       1
-  * Implementation Required:
-  *       yes
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *       nTimerID    -   The timer ID returned by FFI_SetTimer function.
-  * Return value:
-  *       None.
-  * */
+  /*
+   * Method: FFI_KillTimer
+   *       This method uninstalls a system timer, as set by an earlier call to
+   *       FFI_SetTimer.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       nTimerID    -   The timer ID returned by FFI_SetTimer function.
+   * Return value:
+   *       None.
+   */
   void (*FFI_KillTimer)(struct _FPDF_FORMFILLINFO* pThis, int nTimerID);
 
-  /**
-  * Method: FFI_GetLocalTime
-  *           This method receives the current local time on the system.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *   Return value:
-  *       None.
-  * */
+  /*
+   * Method: FFI_GetLocalTime
+   *       This method receives the current local time on the system.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   * Return value:
+   *       The local time. See FPDF_SYSTEMTIME above for details.
+   * Note: Unused.
+   */
   FPDF_SYSTEMTIME (*FFI_GetLocalTime)(struct _FPDF_FORMFILLINFO* pThis);
 
-  /**
-  * Method: FFI_OnChange
-  *           This method will be invoked to notify implementation when the
-  * value of any FormField on the document had been changed.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           no
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *   Return value:
-  *       None.
-  * */
+  /*
+   * Method: FFI_OnChange
+   *       This method will be invoked to notify the implementation when the
+   *       value of any FormField on the document had been changed.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       no
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   * Return value:
+   *       None.
+   */
   void (*FFI_OnChange)(struct _FPDF_FORMFILLINFO* pThis);
 
-  /**
-  * Method: FFI_GetPage
-  *           This method receives the page pointer associated with a specified
-  * page index.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *       document    -   Handle to document. Returned by FPDF_LoadDocument
-  * function.
-  *       nPageIndex  -   Index number of the page. 0 for the first page.
-  * Return value:
-  *       Handle to the page. Returned by FPDF_LoadPage function.
-  * Comments:
-  *       In some cases, the document-level JavaScript action may refer to a
-  * page which hadn't been loaded yet.
-  *       To successfully run the javascript action, implementation need to load
-  * the page for SDK.
-  * */
+  /*
+   * Method: FFI_GetPage
+   *       This method receives the page handle associated with a specified
+   *       page index.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       document    -   Handle to document. Returned by FPDF_LoadDocument().
+   *       nPageIndex  -   Index number of the page. 0 for the first page.
+   * Return value:
+   *       Handle to the page, as previously returned to the implementation by
+   *       FPDF_LoadPage().
+   * Comments:
+   *       The implementation is expected to keep track of the page handles it
+   *       receives from PDFium, and their mappings to page numbers. In some
+   *       cases, the document-level JavaScript action may refer to a page
+   *       which hadn't been loaded yet. To successfully run the Javascript
+   *       action, the implementation needs to load the page.
+   */
   FPDF_PAGE (*FFI_GetPage)(struct _FPDF_FORMFILLINFO* pThis,
                              FPDF_DOCUMENT document,
                              int nPageIndex);
 
-  /**
-  * Method: FFI_GetCurrentPage
-  *       This method receives the current page pointer.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *       document    -   Handle to document. Returned by FPDF_LoadDocument
-  * function.
-  * Return value:
-  *       Handle to the page. Returned by FPDF_LoadPage function.
-  * */
+  /*
+   * Method: FFI_GetCurrentPage
+   *       This method receives the handle to the current page.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       document    -   Handle to document. Returned by FPDF_LoadDocument().
+   * Return value:
+   *       Handle to the page. Returned by FPDF_LoadPage().
+   * Comments:
+   *       The implementation is expected to keep track of the current page,
+   *       e.g. the current page can be the one that is most visible on screen.
+   */
   FPDF_PAGE (*FFI_GetCurrentPage)(struct _FPDF_FORMFILLINFO* pThis,
                                     FPDF_DOCUMENT document);
 
-  /**
-  * Method: FFI_GetRotation
-  *           This method receives currently rotation of the page view.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis       -   Pointer to the interface structure itself.
-  *       page        -   Handle to page. Returned by FPDF_LoadPage function.
-  * Return value:
-  *       The page rotation. Should be 0(0 degree),1(90 degree),2(180
-  * degree),3(270 degree), in a clockwise direction.
-  *
-  * Note: Unused.
-  * */
+  /*
+   * Method: FFI_GetRotation
+   *       This method receives currently rotation of the page view.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis       -   Pointer to the interface structure itself.
+   *       page        -   Handle to page, as returned by FPDF_LoadPage().
+   * Return value:
+   *       A number to indicate the page rotation in 90 degree increments
+   *       in a clockwise direction:
+   *         0 - 0 degrees
+   *         1 - 90 degrees
+   *         2 - 180 degrees
+   *         3 - 270 degrees
+   * Note: Unused.
+   */
   int (*FFI_GetRotation)(struct _FPDF_FORMFILLINFO* pThis, FPDF_PAGE page);
 
-  /**
-  * Method: FFI_ExecuteNamedAction
-  *           This method will execute an named action.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       namedAction     -   A byte string which indicates the named action,
-  * terminated by 0.
-  * Return value:
-  *       None.
-  * Comments:
-  *       See the named actions description of <<PDF Reference, version 1.7>>
-  * for more details.
-  * */
+  /*
+   * Method: FFI_ExecuteNamedAction
+   *       This method will execute a named action.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       yes
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       namedAction     -   A byte string which indicates the named action,
+   *                           terminated by 0.
+   * Return value:
+   *       None.
+   * Comments:
+   *       See the named actions description of <<PDF Reference, version 1.7>>
+   *       for more details.
+   */
   void (*FFI_ExecuteNamedAction)(struct _FPDF_FORMFILLINFO* pThis,
                                  FPDF_BYTESTRING namedAction);
-  /**
-  * @brief This method will be called when a text field is getting or losing a
-  * focus.
-  *
-  * @param[in] pThis      Pointer to the interface structure itself.
-  * @param[in] value      The string value of the form field, in UTF-16LE
-  * format.
-  * @param[in] valueLen   The length of the string value, number of characters
-  * (not bytes).
-  * @param[in] is_focus   True if the form field is getting a focus, False for
-  * losing a focus.
-  *
-  * @return None.
-  *
-  * @note Currently,only support text field and combobox field.
-  * */
+  /*
+   * Method: FFI_SetTextFieldFocus
+   *       Called when a text field is getting or losing focus.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       no
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       value           -   The string value of the form field, in UTF-16LE
+   *                           format.
+   *       valueLen        -   The length of the string value. This is the
+   *                           number of characters, not bytes.
+   *       is_focus        -   True if the form field is getting focus, false
+   *                           if the form field is losing focus.
+   * Return value:
+   *       None.
+   * Comments:
+   *       Only supports text fields and combobox fields.
+   */
   void (*FFI_SetTextFieldFocus)(struct _FPDF_FORMFILLINFO* pThis,
                                 FPDF_WIDESTRING value,
                                 FPDF_DWORD valueLen,
                                 FPDF_BOOL is_focus);
 
-  /**
-  * Method: FFI_DoURIAction
-  *           This action resolves to a uniform resource identifier.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           No
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       bsURI           -   A byte string which indicates the uniform resource
-  * identifier, terminated by 0.
-  * Return value:
-  *       None.
-  * Comments:
-  *       See the URI actions description of <<PDF Reference, version 1.7>> for
-  * more details.
-  * */
+  /*
+   * Method: FFI_DoURIAction
+   *       Ask the implementation to navigate to a uniform resource identifier.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       No
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       bsURI           -   A byte string which indicates the uniform
+   *                           resource identifier, terminated by 0.
+   * Return value:
+   *       None.
+   * Comments:
+   *       See the URI actions description of <<PDF Reference, version 1.7>>
+   *       for more details.
+   */
   void (*FFI_DoURIAction)(struct _FPDF_FORMFILLINFO* pThis,
                           FPDF_BYTESTRING bsURI);
 
-  /**
-  * Method: FFI_DoGoToAction
-  *           This action changes the view to a specified destination.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           No
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       nPageIndex      -   The index of the PDF page.
-  *       zoomMode        -   The zoom mode for viewing page. See below.
-  *       fPosArray       -   The float array which carries the position info.
-  *       sizeofArray     -   The size of float array.
-  *
-  * PDFZoom values:
-  *   - XYZ = 1
-  *   - FITPAGE = 2
-  *   - FITHORZ = 3
-  *   - FITVERT = 4
-  *   - FITRECT = 5
-  *   - FITBBOX = 6
-  *   - FITBHORZ = 7
-  *   - FITBVERT = 8
-  *
-  * Return value:
-  *       None.
-  * Comments:
-  *       See the Destinations description of <<PDF Reference, version 1.7>> in
-  *8.2.1 for more details.
-  **/
+  /*
+   * Method: FFI_DoGoToAction
+   *       This action changes the view to a specified destination.
+   * Interface Version:
+   *       1
+   * Implementation Required:
+   *       No
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       nPageIndex      -   The index of the PDF page.
+   *       zoomMode        -   The zoom mode for viewing page. See below.
+   *       fPosArray       -   The float array which carries the position info.
+   *       sizeofArray     -   The size of float array.
+   * PDFZoom values:
+   *         - XYZ = 1
+   *         - FITPAGE = 2
+   *         - FITHORZ = 3
+   *         - FITVERT = 4
+   *         - FITRECT = 5
+   *         - FITBBOX = 6
+   *         - FITBHORZ = 7
+   *         - FITBVERT = 8
+   * Return value:
+   *       None.
+   * Comments:
+   *       See the Destinations description of <<PDF Reference, version 1.7>>
+   *       in 8.2.1 for more details.
+   */
   void (*FFI_DoGoToAction)(struct _FPDF_FORMFILLINFO* pThis,
                            int nPageIndex,
                            int zoomMode,
                            float* fPosArray,
                            int sizeofArray);
 
-  /**
-  *   pointer to IPDF_JSPLATFORM interface
-  **/
+  /*
+   * Pointer to IPDF_JSPLATFORM interface.
+   * Unused if PDFium is built without V8 support. Otherwise, if NULL, then
+   * JavaScript will be prevented from executing while rendering the document.
+   */
   IPDF_JSPLATFORM* m_pJsPlatform;
 
-#ifdef PDF_ENABLE_XFA
-  /* Version 2. */
-  /**
-    * Method: FFI_DisplayCaret
-    *           This method will show the caret at specified position.
-    * Interface Version:
-    *           2
-    * Implementation Required:
-    *           yes
-    * Parameters:
-    *       pThis           -   Pointer to the interface structure itself.
-    *       page            -   Handle to page. Returned by FPDF_LoadPage
-    *function.
-    *       left            -   Left position of the client area in PDF page
-    *coordinate.
-    *       top             -   Top position of the client area in PDF page
-    *coordinate.
-    *       right           -   Right position of the client area in PDF page
-    *coordinate.
-    *       bottom          -   Bottom position of the client area in PDF page
-    *coordinate.
-    * Return value:
-    *       None.
-    **/
+  /* Version 2 - Experimental. */
+  /*
+   * Whether the XFA module is disabled when built with the XFA module.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   */
+  FPDF_BOOL xfa_disabled;
+
+  /*
+   * Method: FFI_DisplayCaret
+   *       This method will show the caret at specified position.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       page            -   Handle to page. Returned by FPDF_LoadPage().
+   *       left            -   Left position of the client area in PDF page
+   *                           coordinates.
+   *       top             -   Top position of the client area in PDF page
+   *                           coordinates.
+   *       right           -   Right position of the client area in PDF page
+   *                           coordinates.
+   *       bottom          -   Bottom position of the client area in PDF page
+   *                           coordinates.
+   * Return value:
+   *       None.
+   */
   void (*FFI_DisplayCaret)(struct _FPDF_FORMFILLINFO* pThis,
                            FPDF_PAGE page,
                            FPDF_BOOL bVisible,
@@ -750,83 +755,79 @@
                            double right,
                            double bottom);
 
-  /**
-  * Method: FFI_GetCurrentPageIndex
-  *           This method will get the current page index.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       document        -   Handle to document. Returned by FPDF_LoadDocument
-  *function.
-  * Return value:
-  *       The index of current page.
-  **/
+  /*
+   * Method: FFI_GetCurrentPageIndex
+   *       This method will get the current page index.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       document        -   Handle to document from FPDF_LoadDocument().
+   * Return value:
+   *       The index of current page.
+   */
   int (*FFI_GetCurrentPageIndex)(struct _FPDF_FORMFILLINFO* pThis,
                                  FPDF_DOCUMENT document);
 
-  /**
-  * Method: FFI_SetCurrentPage
-  *           This method will set the current page.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       document        -   Handle to document. Returned by FPDF_LoadDocument
-  *function.
-  *       iCurPage        -   The index of the PDF page.
-  * Return value:
-  *       None.
-  **/
+  /*
+   * Method: FFI_SetCurrentPage
+   *       This method will set the current page.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       document        -   Handle to document from FPDF_LoadDocument().
+   *       iCurPage        -   The index of the PDF page.
+   * Return value:
+   *       None.
+   */
   void (*FFI_SetCurrentPage)(struct _FPDF_FORMFILLINFO* pThis,
                              FPDF_DOCUMENT document,
                              int iCurPage);
 
-  /**
+  /*
   * Method: FFI_GotoURL
-  *           This method will link to the specified URL.
+  *       This method will navigate to the specified URL.
   * Interface Version:
-  *           2
+  *       Ignored if |version| < 2.
   * Implementation Required:
-  *           no
+  *       Required for XFA, otherwise set to NULL.
   * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       document        -   Handle to document. Returned by FPDF_LoadDocument
-  *function.
-  *       wsURL           -   The string value of the URL, in UTF-16LE format.
+  *       pThis            -   Pointer to the interface structure itself.
+  *       document         -   Handle to document from FPDF_LoadDocument().
+  *       wsURL            -   The string value of the URL, in UTF-16LE format.
   * Return value:
   *       None.
-  **/
+  */
   void (*FFI_GotoURL)(struct _FPDF_FORMFILLINFO* pThis,
                       FPDF_DOCUMENT document,
                       FPDF_WIDESTRING wsURL);
 
-  /**
-  * Method: FFI_GetPageViewRect
-  *           This method will get the current page view rectangle.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       page            -   Handle to page. Returned by FPDF_LoadPage
-  *function.
-  *       left            -   The pointer to receive left position of the page
-  *view area in PDF page coordinate.
-  *       top             -   The pointer to receive top position of the page
-  *view area in PDF page coordinate.
-  *       right           -   The pointer to receive right position of the
-  *client area in PDF page coordinate.
-  *       bottom          -   The pointer to receive bottom position of the
-  *client area in PDF page coordinate.
-  * Return value:
-  *       None.
-  **/
+  /*
+   * Method: FFI_GetPageViewRect
+   *       This method will get the current page view rectangle.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       page            -   Handle to page. Returned by FPDF_LoadPage().
+   *       left            -   The pointer to receive left position of the page
+   *                           view area in PDF page coordinates.
+   *       top             -   The pointer to receive top position of the page
+   *                           view area in PDF page coordinates.
+   *       right           -   The pointer to receive right position of the
+   *                           page view area in PDF page coordinates.
+   *       bottom          -   The pointer to receive bottom position of the
+   *                           page view area in PDF page coordinates.
+   * Return value:
+   *     None.
+   */
   void (*FFI_GetPageViewRect)(struct _FPDF_FORMFILLINFO* pThis,
                               FPDF_PAGE page,
                               double* left,
@@ -834,54 +835,53 @@
                               double* right,
                               double* bottom);
 
-  /**
-  * Method: FFI_PageEvent
-  *     This method fires when pages have been added to or deleted from the XFA
-  *     document.
-  * Interface Version:
-  *     2
-  * Implementation Required:
-  *     yes
-  * Parameters:
-  *     pThis       -   Pointer to the interface structure itself.
-  *     page_count  -   The number of pages to be added to or deleted from the
-  *                     document.
-  *     event_type  -   See FXFA_PAGEVIEWEVENT_* above.
-  * Return value:
-  *       None.
-  * Comments:
-  *           The pages to be added or deleted always start from the last page
-  *           of document. This means that if parameter page_count is 2 and
-  *           event type is FXFA_PAGEVIEWEVENT_POSTADDED, 2 new pages have been
-  *           appended to the tail of document; If page_count is 2 and
-  *           event type is FXFA_PAGEVIEWEVENT_POSTREMOVED, the last 2 pages
-  *           have been deleted.
-  **/
+  /*
+   * Method: FFI_PageEvent
+   *       This method fires when pages have been added to or deleted from
+   *       the XFA document.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       page_count      -   The number of pages to be added or deleted.
+   *       event_type      -   See FXFA_PAGEVIEWEVENT_* above.
+   * Return value:
+   *       None.
+   * Comments:
+   *       The pages to be added or deleted always start from the last page
+   *       of document. This means that if parameter page_count is 2 and
+   *       event type is FXFA_PAGEVIEWEVENT_POSTADDED, 2 new pages have been
+   *       appended to the tail of document; If page_count is 2 and
+   *       event type is FXFA_PAGEVIEWEVENT_POSTREMOVED, the last 2 pages
+   *       have been deleted.
+   */
   void (*FFI_PageEvent)(struct _FPDF_FORMFILLINFO* pThis,
                         int page_count,
                         FPDF_DWORD event_type);
 
-  /**
-  * Method: FFI_PopupMenu
-  *           This method will track the right context menu for XFA fields.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       page            -   Handle to page. Returned by FPDF_LoadPage
-  *function.
-  *       hWidget         -   Handle to XFA fields.
-  *       menuFlag        -   The menu flags. Please refer to macro definition
-  *of FXFA_MENU_XXX and this can be one or a combination of these macros.
-  *       x               -   X position of the client area in PDF page
-  *coordinate.
-  *       y               -   Y position of the client area in PDF page
-  *coordinate.
-  * Return value:
-  *       TRUE indicates success; otherwise false.
-  **/
+  /*
+   * Method: FFI_PopupMenu
+   *       This method will track the right context menu for XFA fields.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       page            -   Handle to page. Returned by FPDF_LoadPage().
+   *       hWidget         -   Always null, exists for compatibility.
+   *       menuFlag        -   The menu flags. Please refer to macro definition
+   *                           of FXFA_MENU_XXX and this can be one or a
+   *                           combination of these macros.
+   *       x               -   X position of the client area in PDF page
+   *                           coordinates.
+   *       y               -   Y position of the client area in PDF page
+   *                           coordinates.
+   * Return value:
+   *       TRUE indicates success; otherwise false.
+   */
   FPDF_BOOL (*FFI_PopupMenu)(struct _FPDF_FORMFILLINFO* pThis,
                              FPDF_PAGE page,
                              FPDF_WIDGET hWidget,
@@ -889,51 +889,51 @@
                              float x,
                              float y);
 
-  /**
-  * Method: FFI_OpenFile
-  *           This method will open the specified file with the specified mode.
-  * Interface Version
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       fileFlag        -   The file flag.Please refer to macro definition of
-  *FXFA_SAVEAS_XXX and this can be one of these macros.
-  *       wsURL           -   The string value of the file URL, in UTF-16LE
-  *format.
-  *       mode            -   The mode for open file.
-  * Return value:
-  *       The handle to FPDF_FILEHANDLER.
-  **/
+  /*
+   * Method: FFI_OpenFile
+   *       This method will open the specified file with the specified mode.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       fileFlag        -   The file flag. Please refer to macro definition
+   *                           of FXFA_SAVEAS_XXX and use one of these macros.
+   *       wsURL           -   The string value of the file URL, in UTF-16LE
+   *                           format.
+   *       mode            -   The mode for open file, e.g. "rb" or "wb".
+   * Return value:
+   *       The handle to FPDF_FILEHANDLER.
+   */
   FPDF_FILEHANDLER* (*FFI_OpenFile)(struct _FPDF_FORMFILLINFO* pThis,
                                     int fileFlag,
                                     FPDF_WIDESTRING wsURL,
                                     const char* mode);
 
-  /**
-  * Method: FFI_EmailTo
-  *           This method will email the specified file stream to the specified
-  *contacter.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       pFileHandler    -   Handle to the FPDF_FILEHANDLER.
-  *       pTo             -   A semicolon-delimited list of recipients for the
-  *message,in UTF-16LE format.
-  *       pSubject        -   The subject of the message,in UTF-16LE format.
-  *       pCC             -   A semicolon-delimited list of CC recipients for
-  *the message,in UTF-16LE format.
-  *       pBcc            -   A semicolon-delimited list of BCC recipients for
-  *the message,in UTF-16LE format.
-  *       pMsg            -   Pointer to the data buffer to be sent.Can be
-  *NULL,in UTF-16LE format.
-  * Return value:
-  *       None.
-  **/
+  /*
+   * Method: FFI_EmailTo
+   *       This method will email the specified file stream to the specified
+   *       contact.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       pFileHandler    -   Handle to the FPDF_FILEHANDLER.
+   *       pTo             -   A semicolon-delimited list of recipients for the
+   *                           message,in UTF-16LE format.
+   *       pSubject        -   The subject of the message,in UTF-16LE format.
+   *       pCC             -   A semicolon-delimited list of CC recipients for
+   *                           the message,in UTF-16LE format.
+   *       pBcc            -   A semicolon-delimited list of BCC recipients for
+   *                           the message,in UTF-16LE format.
+   *       pMsg            -   Pointer to the data buffer to be sent.Can be
+   *                           NULL,in UTF-16LE format.
+   * Return value:
+   *       None.
+   */
   void (*FFI_EmailTo)(struct _FPDF_FORMFILLINFO* pThis,
                       FPDF_FILEHANDLER* fileHandler,
                       FPDF_WIDESTRING pTo,
@@ -942,502 +942,600 @@
                       FPDF_WIDESTRING pBcc,
                       FPDF_WIDESTRING pMsg);
 
-  /**
-  * Method: FFI_UploadTo
-  *           This method will get upload the specified file stream to the
-  *specified URL.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       pFileHandler    -   Handle to the FPDF_FILEHANDLER.
-  *       fileFlag        -   The file flag.Please refer to macro definition of
-  *FXFA_SAVEAS_XXX and this can be one of these macros.
-  *       uploadTo        -   Pointer to the URL path, in UTF-16LE format.
-  * Return value:
-  *       None.
-  **/
+  /*
+   * Method: FFI_UploadTo
+   *       This method will upload the specified file stream to the
+   *       specified URL.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       pFileHandler    -   Handle to the FPDF_FILEHANDLER.
+   *       fileFlag        -   The file flag. Please refer to macro definition
+   *                           of FXFA_SAVEAS_XXX and use one of these macros.
+   *       uploadTo        -   Pointer to the URL path, in UTF-16LE format.
+   * Return value:
+   *       None.
+   */
   void (*FFI_UploadTo)(struct _FPDF_FORMFILLINFO* pThis,
                        FPDF_FILEHANDLER* fileHandler,
                        int fileFlag,
                        FPDF_WIDESTRING uploadTo);
 
-  /**
-  * Method: FFI_GetPlatform
-  *           This method will get the current platform.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       platform        -   Pointer to the data buffer to receive the
-  *platform.Can be NULL,in UTF-16LE format.
-  *       length          -   The length of the buffer, number of bytes. Can be
-  *0.
-  * Return value:
-  *       The length of the buffer, number of bytes.
-  **/
+  /*
+   * Method: FFI_GetPlatform
+   *       This method will get the current platform.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       platform        -   Pointer to the data buffer to receive the
+   *                           platform,in UTF-16LE format. Can be NULL.
+   *       length          -   The length of the buffer in bytes. Can be
+   *                           0 to query the required size.
+   * Return value:
+   *       The length of the buffer, number of bytes.
+   */
   int (*FFI_GetPlatform)(struct _FPDF_FORMFILLINFO* pThis,
                          void* platform,
                          int length);
 
-  /**
-  * Method: FFI_GetLanguage
-  *           This method will get the current language.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       language        -   Pointer to the data buffer to receive the current
-  *language.Can be NULL.
-  *       length          -   The length of the buffer, number of bytes. Can be
-  *0.
-  * Return value:
-  *       The length of the buffer, number of bytes.
-  **/
+  /*
+   * Method: FFI_GetLanguage
+   *       This method will get the current language.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       language        -   Pointer to the data buffer to receive the
+   *                           current language. Can be NULL.
+   *       length          -   The length of the buffer in bytes. Can be
+   *                           0 to query the required size.
+   * Return value:
+   *       The length of the buffer, number of bytes.
+   */
   int (*FFI_GetLanguage)(struct _FPDF_FORMFILLINFO* pThis,
                          void* language,
                          int length);
 
-  /**
+  /*
   * Method: FFI_DownloadFromURL
-  *           This method will download the specified file from the URL.
+  *       This method will download the specified file from the URL.
   * Interface Version:
-  *           2
+  *       Ignored if |version| < 2.
   * Implementation Required:
-  *           yes
+  *       Required for XFA, otherwise set to NULL.
   * Parameters:
   *       pThis           -   Pointer to the interface structure itself.
   *       URL             -   The string value of the file URL, in UTF-16LE
-  *format.
+  *                           format.
   * Return value:
   *       The handle to FPDF_FILEHANDLER.
-  **/
-  FPDF_LPFILEHANDLER (*FFI_DownloadFromURL)(struct _FPDF_FORMFILLINFO* pThis,
-                                             FPDF_WIDESTRING URL);
-  /**
-  * Method: FFI_PostRequestURL
-  *           This method will post the request to the server URL.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       wsURL           -   The string value of the server URL, in UTF-16LE
-  *format.
-  *       wsData          -   The post data,in UTF-16LE format.
-  *       wsContentType   -   The content type of the request data,in UTF-16LE
-  *format.
-  *       wsEncode        -   The encode type,in UTF-16LE format.
-  *       wsHeader        -   The request header,in UTF-16LE format.
-  *       response        -   Pointer to the FPDF_BSTR to receive the response
-  *data from server,,in UTF-16LE format.
-  * Return value:
-  *       TRUE indicates success, otherwise FALSE.
-  **/
+  */
+  FPDF_FILEHANDLER* (*FFI_DownloadFromURL)(struct _FPDF_FORMFILLINFO* pThis,
+                                           FPDF_WIDESTRING URL);
+  /*
+   * Method: FFI_PostRequestURL
+   *       This method will post the request to the server URL.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       wsURL           -   The string value of the server URL, in UTF-16LE
+   *                           format.
+   *       wsData          -   The post data,in UTF-16LE format.
+   *       wsContentType   -   The content type of the request data, in
+   *                           UTF-16LE format.
+   *       wsEncode        -   The encode type, in UTF-16LE format.
+   *       wsHeader        -   The request header,in UTF-16LE format.
+   *       response        -   Pointer to the FPDF_BSTR to receive the response
+   *                           data from the server, in UTF-16LE format.
+   * Return value:
+   *       TRUE indicates success, otherwise FALSE.
+   */
   FPDF_BOOL (*FFI_PostRequestURL)(struct _FPDF_FORMFILLINFO* pThis,
-                                    FPDF_WIDESTRING wsURL,
-                                    FPDF_WIDESTRING wsData,
-                                    FPDF_WIDESTRING wsContentType,
-                                    FPDF_WIDESTRING wsEncode,
-                                    FPDF_WIDESTRING wsHeader,
-                                    FPDF_BSTR* respone);
+                                  FPDF_WIDESTRING wsURL,
+                                  FPDF_WIDESTRING wsData,
+                                  FPDF_WIDESTRING wsContentType,
+                                  FPDF_WIDESTRING wsEncode,
+                                  FPDF_WIDESTRING wsHeader,
+                                  FPDF_BSTR* response);
 
-  /**
-  * Method: FFI_PutRequestURL
-  *           This method will put the request to the server URL.
-  * Interface Version:
-  *           2
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *       pThis           -   Pointer to the interface structure itself.
-  *       wsURL           -   The string value of the server URL, in UTF-16LE
-  *format.
-  *       wsData          -   The put data, in UTF-16LE format.
-  *       wsEncode        -   The encode type, in UTR-16LE format.
-  * Return value:
-  *       TRUE indicates success, otherwise FALSE.
-  **/
+  /*
+   * Method: FFI_PutRequestURL
+   *       This method will put the request to the server URL.
+   * Interface Version:
+   *       Ignored if |version| < 2.
+   * Implementation Required:
+   *       Required for XFA, otherwise set to NULL.
+   * Parameters:
+   *       pThis           -   Pointer to the interface structure itself.
+   *       wsURL           -   The string value of the server URL, in UTF-16LE
+   *                           format.
+   *       wsData          -   The put data, in UTF-16LE format.
+   *       wsEncode        -   The encode type, in UTR-16LE format.
+   * Return value:
+   *       TRUE indicates success, otherwise FALSE.
+   */
   FPDF_BOOL (*FFI_PutRequestURL)(struct _FPDF_FORMFILLINFO* pThis,
                                    FPDF_WIDESTRING wsURL,
                                    FPDF_WIDESTRING wsData,
                                    FPDF_WIDESTRING wsEncode);
-#endif  // PDF_ENABLE_XFA
 } FPDF_FORMFILLINFO;
 
-/**
+/*
  * Function: FPDFDOC_InitFormFillEnvironment
- *          Init form fill environment.
- * Comments:
- *          This function should be called before any form fill operation.
+ *       Initialize form fill environment.
  * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          pFormFillInfo   -   Pointer to a FPDF_FORMFILLINFO structure.
+ *       document        -   Handle to document from FPDF_LoadDocument().
+ *       pFormFillInfo   -   Pointer to a FPDF_FORMFILLINFO structure.
  * Return Value:
- *          Return handler to the form fill module. NULL means fails.
- **/
+ *       Handle to the form fill module, or NULL on failure.
+ * Comments:
+ *       This function should be called before any form fill operation.
+ */
 FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV
 FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document,
                                 FPDF_FORMFILLINFO* formInfo);
 
-/**
+/*
  * Function: FPDFDOC_ExitFormFillEnvironment
- *          Exit form fill environment.
+ *       Take ownership of |hHandle| and exit form fill environment.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
  * Return Value:
- *          NULL.
- **/
+ *       None.
+ * Comments:
+ *       This function is a no-op when |hHandle| is null.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDFDOC_ExitFormFillEnvironment(FPDF_FORMHANDLE hHandle);
 
-/**
+/*
  * Function: FORM_OnAfterLoadPage
- *          This method is required for implementing all the form related
- *functions. Should be invoked after user
- *          successfully loaded a PDF page, and method
- *FPDFDOC_InitFormFillEnvironment had been invoked.
+ *       This method is required for implementing all the form related
+ *       functions. Should be invoked after user successfully loaded a
+ *       PDF page, and FPDFDOC_InitFormFillEnvironment() has been invoked.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
+ *       hHandle     -   Handle to the form fill module, as eturned by
+ *                       FPDFDOC_InitFormFillEnvironment().
  * Return Value:
- *          NONE.
- **/
+ *       None.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FORM_OnAfterLoadPage(FPDF_PAGE page,
                                                     FPDF_FORMHANDLE hHandle);
 
-/**
+/*
  * Function: FORM_OnBeforeClosePage
- *          This method is required for implementing all the form related
- *functions. Should be invoked before user
- *          close the PDF page.
+ *       This method is required for implementing all the form related
+ *       functions. Should be invoked before user closes the PDF page.
  * Parameters:
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *function.
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
+ *        page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *        hHandle     -   Handle to the form fill module, as returned by
+ *                        FPDFDOC_InitFormFillEnvironment().
  * Return Value:
- *          NONE.
- **/
+ *        None.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FORM_OnBeforeClosePage(FPDF_PAGE page,
                                                       FPDF_FORMHANDLE hHandle);
 
-/**
-* Function: FORM_DoDocumentJSAction
-*           This method is required for performing Document-level JavaScript
-*action. It should be invoked after the PDF document
-*           had been loaded.
-* Parameters:
-*           hHandle     -   Handle to the form fill module. Returned by
-*FPDFDOC_InitFormFillEnvironment.
-* Return Value:
-*           NONE
-* Comments:
-*           If there is Document-level JavaScript action embedded in the
-*document, this method will execute the javascript action;
-*           otherwise, the method will do nothing.
-**/
-FPDF_EXPORT void FPDF_CALLCONV FORM_DoDocumentJSAction(FPDF_FORMHANDLE hHandle);
+/*
+ * Function: FORM_DoDocumentJSAction
+ *       This method is required for performing document-level JavaScript
+ *       actions. It should be invoked after the PDF document has been loaded.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ * Return Value:
+ *       None.
+ * Comments:
+ *       If there is document-level JavaScript action embedded in the
+ *       document, this method will execute the JavaScript action. Otherwise,
+ *       the method will do nothing.
+ */
+FPDF_EXPORT void FPDF_CALLCONV
+FORM_DoDocumentJSAction(FPDF_FORMHANDLE hHandle);
 
-/**
-* Function: FORM_DoDocumentOpenAction
-*           This method is required for performing open-action when the document
-*is opened.
-* Parameters:
-*           hHandle     -   Handle to the form fill module. Returned by
-*FPDFDOC_InitFormFillEnvironment.
-* Return Value:
-*           NONE
-* Comments:
-*           This method will do nothing if there is no open-actions embedded in
-*the document.
-**/
+/*
+ * Function: FORM_DoDocumentOpenAction
+ *       This method is required for performing open-action when the document
+ *       is opened.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ * Return Value:
+ *       None.
+ * Comments:
+ *       This method will do nothing if there are no open-actions embedded
+ *       in the document.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FORM_DoDocumentOpenAction(FPDF_FORMHANDLE hHandle);
 
-// additional actions type of document.
-#define FPDFDOC_AACTION_WC \
-  0x10  // WC, before closing document, JavaScript action.
-#define FPDFDOC_AACTION_WS \
-  0x11  // WS, before saving document, JavaScript action.
-#define FPDFDOC_AACTION_DS 0x12  // DS, after saving document, JavaScript
-                                 // action.
-#define FPDFDOC_AACTION_WP \
-  0x13  // WP, before printing document, JavaScript action.
-#define FPDFDOC_AACTION_DP \
-  0x14  // DP, after printing document, JavaScript action.
+// Additional actions type of document:
+//   WC, before closing document, JavaScript action.
+//   WS, before saving document, JavaScript action.
+//   DS, after saving document, JavaScript action.
+//   WP, before printing document, JavaScript action.
+//   DP, after printing document, JavaScript action.
+#define FPDFDOC_AACTION_WC 0x10
+#define FPDFDOC_AACTION_WS 0x11
+#define FPDFDOC_AACTION_DS 0x12
+#define FPDFDOC_AACTION_WP 0x13
+#define FPDFDOC_AACTION_DP 0x14
 
-/**
-* Function: FORM_DoDocumentAAction
-*           This method is required for performing the document's
-*additional-action.
-* Parameters:
-*           hHandle     -   Handle to the form fill module. Returned by
-*FPDFDOC_InitFormFillEnvironment.
-*           aaType      -   The type of the additional-actions which defined
-*above.
-* Return Value:
-*           NONE
-* Comments:
-*           This method will do nothing if there is no document
-*additional-action corresponding to the specified aaType.
-**/
-
+/*
+ * Function: FORM_DoDocumentAAction
+ *       This method is required for performing the document's
+ *       additional-action.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module. Returned by
+ *                       FPDFDOC_InitFormFillEnvironment.
+ *       aaType      -   The type of the additional-actions which defined
+ *                       above.
+ * Return Value:
+ *       None.
+ * Comments:
+ *       This method will do nothing if there is no document
+ *       additional-action corresponding to the specified |aaType|.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FORM_DoDocumentAAction(FPDF_FORMHANDLE hHandle,
                                                       int aaType);
 
-// Additional-action types of page object
-#define FPDFPAGE_AACTION_OPEN \
-  0  // /O -- An action to be performed when the page is opened
-#define FPDFPAGE_AACTION_CLOSE \
-  1  // /C -- An action to be performed when the page is closed
+// Additional-action types of page object:
+//   OPEN (/O) -- An action to be performed when the page is opened
+//   CLOSE (/C) -- An action to be performed when the page is closed
+#define FPDFPAGE_AACTION_OPEN 0
+#define FPDFPAGE_AACTION_CLOSE 1
 
-/**
-* Function: FORM_DoPageAAction
-*           This method is required for performing the page object's
-*additional-action when opened or closed.
-* Parameters:
-*           page        -   Handle to the page. Returned by FPDF_LoadPage
-*function.
-*           hHandle     -   Handle to the form fill module. Returned by
-*FPDFDOC_InitFormFillEnvironment.
-*           aaType      -   The type of the page object's additional-actions
-*which defined above.
-* Return Value:
-*           NONE
-* Comments:
-*           This method will do nothing if no additional-action corresponding to
-*the specified aaType exists.
-**/
+/*
+ * Function: FORM_DoPageAAction
+ *       This method is required for performing the page object's
+ *       additional-action when opened or closed.
+ * Parameters:
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       aaType      -   The type of the page object's additional-actions
+ *                       which defined above.
+ * Return Value:
+ *       None.
+ * Comments:
+ *       This method will do nothing if no additional-action corresponding
+ *       to the specified |aaType| exists.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page,
                                                   FPDF_FORMHANDLE hHandle,
                                                   int aaType);
 
-/**
+/*
  * Function: FORM_OnMouseMove
- *          You can call this member function when the mouse cursor moves.
+ *       Call this member function when the mouse cursor moves.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *function.
- *          modifier        -   Indicates whether various virtual keys are down.
- *          page_x      -   Specifies the x-coordinate of the cursor in PDF user
- *space.
- *          page_y      -   Specifies the y-coordinate of the cursor in PDF user
- *space.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       modifier    -   Indicates whether various virtual keys are down.
+ *       page_x      -   Specifies the x-coordinate of the cursor in PDF user
+ *                       space.
+ *       page_y      -   Specifies the y-coordinate of the cursor in PDF user
+ *                       space.
  * Return Value:
- *          TRUE indicates success; otherwise false.
- **/
+ *       True indicates success; otherwise false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseMove(FPDF_FORMHANDLE hHandle,
                                                      FPDF_PAGE page,
                                                      int modifier,
                                                      double page_x,
                                                      double page_y);
 
-/**
+/*
  * Function: FORM_OnFocus
- *          This function focuses the form annotation at a given point. If the
- *          annotation at the point already has focus, nothing happens. If there
- *          is no annotation at the point, remove form focus.
+ *       This function focuses the form annotation at a given point. If the
+ *       annotation at the point already has focus, nothing happens. If there
+ *       is no annotation at the point, removes form focus.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *                          FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage.
- *          modifier    -   Indicates whether various virtual keys are down.
- *          page_x      -   Specifies the x-coordinate of the cursor in PDF user
- *                          space.
- *          page_y      -   Specifies the y-coordinate of the cursor in PDF user
- *                          space.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       modifier    -   Indicates whether various virtual keys are down.
+ *       page_x      -   Specifies the x-coordinate of the cursor in PDF user
+ *                       space.
+ *       page_y      -   Specifies the y-coordinate of the cursor in PDF user
+ *                       space.
  * Return Value:
- *          TRUE if there is an annotation at the given point and it has focus.
- **/
+ *       True if there is an annotation at the given point and it has focus.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle,
                                                  FPDF_PAGE page,
                                                  int modifier,
                                                  double page_x,
                                                  double page_y);
 
-/**
+/*
  * Function: FORM_OnLButtonDown
- *          You can call this member function when the user presses the left
- *mouse button.
+ *       Call this member function when the user presses the left
+ *       mouse button.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *function.
- *          modifier        -   Indicates whether various virtual keys are down.
- *          page_x      -   Specifies the x-coordinate of the cursor in PDF user
- *space.
- *          page_y      -   Specifies the y-coordinate of the cursor in PDF user
- *space.
+ *       hHandle     -   Handle to the form fill module. as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       modifier    -   Indicates whether various virtual keys are down.
+ *       page_x      -   Specifies the x-coordinate of the cursor in PDF user
+ *                       space.
+ *       page_y      -   Specifies the y-coordinate of the cursor in PDF user
+ *                       space.
  * Return Value:
- *          TRUE indicates success; otherwise false.
- **/
+ *       True indicates success; otherwise false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle,
                                                        FPDF_PAGE page,
                                                        int modifier,
                                                        double page_x,
                                                        double page_y);
 
-/**
+/*
+ * Function: FORM_OnRButtonDown
+ *       Same as above, execpt for the right mouse button.
+ * Comments:
+ *       At the present time, has no effect except in XFA builds, but is
+ *       included for the sake of symmetry.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle,
+                                                       FPDF_PAGE page,
+                                                       int modifier,
+                                                       double page_x,
+                                                       double page_y);
+/*
  * Function: FORM_OnLButtonUp
- *          You can call this member function when the user releases the left
- *mouse button.
+ *       Call this member function when the user releases the left
+ *       mouse button.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *function.
- *          modifier    -   Indicates whether various virtual keys are down.
- *          page_x      -   Specifies the x-coordinate of the cursor in device.
- *          page_y      -   Specifies the y-coordinate of the cursor in device.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page. as returned by FPDF_LoadPage().
+ *       modifier    -   Indicates whether various virtual keys are down.
+ *       page_x      -   Specifies the x-coordinate of the cursor in device.
+ *       page_y      -   Specifies the y-coordinate of the cursor in device.
  * Return Value:
- *          TRUE indicates success; otherwise false.
- **/
+ *       True indicates success; otherwise false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle,
                                                      FPDF_PAGE page,
                                                      int modifier,
                                                      double page_x,
                                                      double page_y);
 
-#ifdef PDF_ENABLE_XFA
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle,
-                                                       FPDF_PAGE page,
-                                                       int modifier,
-                                                       double page_x,
-                                                       double page_y);
+/*
+ * Function: FORM_OnRButtonUp
+ *       Same as above, execpt for the right mouse button.
+ * Comments:
+ *       At the present time, has no effect except in XFA builds, but is
+ *       included for the sake of symmetry.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle,
                                                      FPDF_PAGE page,
                                                      int modifier,
                                                      double page_x,
                                                      double page_y);
-#endif  // PDF_ENABLE_XFA
 
-/**
- * Function: FORM_OnKeyDown
- *          You can call this member function when a nonsystem key is pressed.
+/*
+ * Function: FORM_OnLButtonDoubleClick
+ *       Call this member function when the user double clicks the
+ *       left mouse button.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *function.
- *          nKeyCode    -   Indicates whether various virtual keys are down.
- *          modifier    -   Contains the scan code, key-transition code,
- *previous key state, and context code.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       modifier    -   Indicates whether various virtual keys are down.
+ *       page_x      -   Specifies the x-coordinate of the cursor in PDF user
+ *                       space.
+ *       page_y      -   Specifies the y-coordinate of the cursor in PDF user
+ *                       space.
  * Return Value:
- *          TRUE indicates success; otherwise false.
- **/
+ *       True indicates success; otherwise false.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_OnLButtonDoubleClick(FPDF_FORMHANDLE hHandle,
+                          FPDF_PAGE page,
+                          int modifier,
+                          double page_x,
+                          double page_y);
+
+/*
+ * Function: FORM_OnKeyDown
+ *       Call this member function when a nonsystem key is pressed.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, aseturned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       nKeyCode    -   Indicates whether various virtual keys are down.
+ *       modifier    -   Contains the scan code, key-transition code,
+ *                       previous key state, and context code.
+ * Return Value:
+ *       True indicates success; otherwise false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle,
                                                    FPDF_PAGE page,
                                                    int nKeyCode,
                                                    int modifier);
 
-/**
+/*
  * Function: FORM_OnKeyUp
- *          You can call this member function when a nonsystem key is released.
+ *       Call this member function when a nonsystem key is released.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *function.
- *          nKeyCode    -   The virtual-key code of the given key.
- *          modifier    -   Contains the scan code, key-transition code,
- *previous key state, and context code.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       nKeyCode    -   The virtual-key code of the given key.
+ *       modifier    -   Contains the scan code, key-transition code,
+ *                       previous key state, and context code.
  * Return Value:
- *          TRUE indicates success; otherwise false.
- **/
+ *       True indicates success; otherwise false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle,
                                                  FPDF_PAGE page,
                                                  int nKeyCode,
                                                  int modifier);
 
-/**
+/*
  * Function: FORM_OnChar
- *          You can call this member function when a keystroke translates to a
- *nonsystem character.
+ *       Call this member function when a keystroke translates to a
+ *       nonsystem character.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *function.
- *          nChar       -   The character code value of the key.
- *          modifier    -   Contains the scan code, key-transition code,
- *previous key state, and context code.
+ *        hHandle    -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *        page       -   Handle to the page, as returned by FPDF_LoadPage().
+ *        nChar      -   The character code value of the key.
+ *        modifier   -   Contains the scan code, key-transition code,
+ *                       previous key state, and context code.
  * Return Value:
- *          TRUE indicates success; otherwise false.
- **/
+ *       True indicates success; otherwise false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle,
                                                 FPDF_PAGE page,
                                                 int nChar,
                                                 int modifier);
 
-/**
- * Function: FORM_GetSelectedText
- *          You can call this function to obtain selected text within
- *          a form text field or form combobox text field.
+/*
+ * Experimental API
+ * Function: FORM_GetFocusedText
+ *       Call this function to obtain the text within the current focused
+ *       field, if any.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *                          FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *                          function.
- *          buffer      -   Buffer for holding the selected text, encoded
- *                          in UTF16-LE. If NULL, |buffer| is not modified.
- *          buflen      -   Length of |buffer| in bytes. If |buflen|
-                            is less than the length of the selected text
-                            string, |buffer| is not modified.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       buffer      -   Buffer for holding the form text, encoded in
+ *                       UTF-16LE. If NULL, |buffer| is not modified.
+ *       buflen      -   Length of |buffer| in bytes. If |buflen| is less
+ *                       than the length of the form text string, |buffer| is
+ *                       not modified.
  * Return Value:
- *          Length in bytes of selected text in form text field or form combobox
- *          text field.
- **/
+ *       Length in bytes for the text in the focused field.
+ */
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FORM_GetFocusedText(FPDF_FORMHANDLE hHandle,
+                    FPDF_PAGE page,
+                    void* buffer,
+                    unsigned long buflen);
+
+/*
+ * Function: FORM_GetSelectedText
+ *       Call this function to obtain selected text within a form text
+ *       field or form combobox text field.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, asr eturned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ *       buffer      -   Buffer for holding the selected text, encoded in
+ *                       UTF-16LE. If NULL, |buffer| is not modified.
+ *       buflen      -   Length of |buffer| in bytes. If |buflen| is less
+ *                       than the length of the selected text string,
+ *                       |buffer| is not modified.
+ * Return Value:
+ *       Length in bytes of selected text in form text field or form combobox
+ *       text field.
+ */
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FORM_GetSelectedText(FPDF_FORMHANDLE hHandle,
                      FPDF_PAGE page,
                      void* buffer,
                      unsigned long buflen);
 
-/**
+/*
  * Function: FORM_ReplaceSelection
- *          You can call this function to replace the selected text in a form
- *          text field or user-editable form combobox text field with another
- *          text string (which can be empty or non-empty). If there is no
- *          selected text, this function will append the replacement text after
- *          the current caret position.
+ *       Call this function to replace the selected text in a form
+ *       text field or user-editable form combobox text field with another
+ *       text string (which can be empty or non-empty). If there is no
+ *       selected text, this function will append the replacement text after
+ *       the current caret position.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *                          FPDFDOC_InitFormFillEnvironment.
- *          page        -   Handle to the page. Returned by FPDF_LoadPage
- *                          function.
- *          wsText      -   The text to be inserted, in UTF-16LE format.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as Returned by FPDF_LoadPage().
+ *       wsText      -   The text to be inserted, in UTF-16LE format.
  * Return Value:
- *          None.
- **/
+ *       None.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle,
                                                      FPDF_PAGE page,
                                                      FPDF_WIDESTRING wsText);
 
-/**
- * Function: FORM_ForceToKillFocus.
- *          You can call this member function to force to kill the focus of the
- *form field which got focus.
- *          It would kill the focus on the form field, save the value of form
- *field if it's changed by user.
+/*
+ * Function: FORM_CanUndo
+ *       Find out if it is possible for the current focused widget in a given
+ *       form to perform an undo operation.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
  * Return Value:
- *          TRUE indicates success; otherwise false.
- **/
+ *       True if it is possible to undo.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanUndo(FPDF_FORMHANDLE hHandle,
+                                                 FPDF_PAGE page);
+
+/*
+ * Function: FORM_CanRedo
+ *       Find out if it is possible for the current focused widget in a given
+ *       form to perform a redo operation.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ * Return Value:
+ *       True if it is possible to redo.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanRedo(FPDF_FORMHANDLE hHandle,
+                                                 FPDF_PAGE page);
+
+/*
+ * Function: FORM_Undo
+ *       Make the current focussed widget perform an undo operation.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module. as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as returned by FPDF_LoadPage().
+ * Return Value:
+ *       True if the undo operation succeeded.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Undo(FPDF_FORMHANDLE hHandle,
+                                              FPDF_PAGE page);
+
+/*
+ * Function: FORM_Redo
+ *       Make the current focussed widget perform a redo operation.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       page        -   Handle to the page, as eturned by FPDF_LoadPage().
+ * Return Value:
+ *       True if the redo operation succeeded.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Redo(FPDF_FORMHANDLE hHandle,
+                                              FPDF_PAGE page);
+
+/*
+ * Function: FORM_ForceToKillFocus.
+ *       Call this member function to force to kill the focus of the form
+ *       field which has focus. If it would kill the focus of a form field,
+ *       save the value of form field if was changed by theuser.
+ * Parameters:
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ * Return Value:
+ *       True indicates success; otherwise false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle);
 
@@ -1463,24 +1561,25 @@
 #define FPDF_FORMFIELD_XFA_TEXTFIELD 15   // XFA text field type.
 #endif                                    // PDF_ENABLE_XFA
 
-#ifndef PDF_ENABLE_XFA
-#define FPDF_FORMFIELD_COUNT 8
-#else
+#ifdef PDF_ENABLE_XFA
 #define FPDF_FORMFIELD_COUNT 16
+#else  // PDF_ENABLE_XFA
+#define FPDF_FORMFIELD_COUNT 8
 #endif  // PDF_ENABLE_XFA
 
 #ifdef PDF_ENABLE_XFA
-#define IS_XFA_FORMFIELD(type)                                              \
-  ((type == FPDF_FORMFIELD_XFA) || (type == FPDF_FORMFIELD_XFA_CHECKBOX) || \
-   (type == FPDF_FORMFIELD_XFA_COMBOBOX) ||                                 \
-   (type == FPDF_FORMFIELD_XFA_IMAGEFIELD) ||                               \
-   (type == FPDF_FORMFIELD_XFA_LISTBOX) ||                                  \
-   (type == FPDF_FORMFIELD_XFA_PUSHBUTTON) ||                               \
-   (type == FPDF_FORMFIELD_XFA_SIGNATURE) ||                                \
-   (type == FPDF_FORMFIELD_XFA_TEXTFIELD))
+#define IS_XFA_FORMFIELD(type)                  \
+  (((type) == FPDF_FORMFIELD_XFA) ||            \
+   ((type) == FPDF_FORMFIELD_XFA_CHECKBOX) ||   \
+   ((type) == FPDF_FORMFIELD_XFA_COMBOBOX) ||   \
+   ((type) == FPDF_FORMFIELD_XFA_IMAGEFIELD) || \
+   ((type) == FPDF_FORMFIELD_XFA_LISTBOX) ||    \
+   ((type) == FPDF_FORMFIELD_XFA_PUSHBUTTON) || \
+   ((type) == FPDF_FORMFIELD_XFA_SIGNATURE) ||  \
+   ((type) == FPDF_FORMFIELD_XFA_TEXTFIELD))
 #endif  // PDF_ENABLE_XFA
 
-/**
+/*
  * Function: FPDFPage_HasFormFieldAtPoint
  *     Get the form field type by point.
  * Parameters:
@@ -1492,14 +1591,14 @@
  * Return Value:
  *     Return the type of the form field; -1 indicates no field.
  *     See field types above.
- **/
+ */
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFPage_HasFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
                              FPDF_PAGE page,
                              double page_x,
                              double page_y);
 
-/**
+/*
  * Function: FPDFPage_FormFieldZOrderAtPoint
  *     Get the form field z-order by point.
  * Parameters:
@@ -1511,110 +1610,109 @@
  * Return Value:
  *     Return the z-order of the form field; -1 indicates no field.
  *     Higher numbers are closer to the front.
- **/
+ */
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFPage_FormFieldZOrderAtPoint(FPDF_FORMHANDLE hHandle,
                                 FPDF_PAGE page,
                                 double page_x,
                                 double page_y);
 
-/**
+/*
  * Function: FPDF_SetFormFieldHighlightColor
- *          Set the highlight color of specified or all the form fields in the
- *document.
+ *       Set the highlight color of the specified (or all) form fields
+ *       in the document.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          doc         -   Handle to the document. Returned by
- *FPDF_LoadDocument function.
- *          fieldType   -   A 32-bit integer indicating the type of a form
- *field(defined above).
- *          color       -   The highlight color of the form field.Constructed by
- *0xxxrrggbb.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       doc         -   Handle to the document, as returned by
+ *                       FPDF_LoadDocument().
+ *       fieldType   -   A 32-bit integer indicating the type of a form
+ *                       field (defined above).
+ *       color       -   The highlight color of the form field. Constructed by
+ *                       0xxxrrggbb.
  * Return Value:
- *          NONE.
+ *       None.
  * Comments:
- *          When the parameter fieldType is set to FPDF_FORMFIELD_UNKNOWN, the
- *          highlight color will be applied to all the form fields in the
- *          document.
- *          Please refresh the client window to show the highlight immediately
- *          if necessary.
- **/
+ *       When the parameter fieldType is set to FPDF_FORMFIELD_UNKNOWN, the
+ *       highlight color will be applied to all the form fields in the
+ *       document.
+ *       Please refresh the client window to show the highlight immediately
+ *       if necessary.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_SetFormFieldHighlightColor(FPDF_FORMHANDLE hHandle,
                                 int fieldType,
                                 unsigned long color);
 
-/**
+/*
  * Function: FPDF_SetFormFieldHighlightAlpha
- *          Set the transparency of the form field highlight color in the
- *document.
+ *       Set the transparency of the form field highlight color in the
+ *       document.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
- *          doc         -   Handle to the document. Returned by
- *FPDF_LoadDocument function.
- *          alpha       -   The transparency of the form field highlight color.
- *between 0-255.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
+ *       doc         -   Handle to the document, as returaned by
+ *                       FPDF_LoadDocument().
+ *       alpha       -   The transparency of the form field highlight color,
+ *                       between 0-255.
  * Return Value:
- *          NONE.
- **/
+ *       None.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_SetFormFieldHighlightAlpha(FPDF_FORMHANDLE hHandle, unsigned char alpha);
 
-/**
+/*
  * Function: FPDF_RemoveFormFieldHighlight
- *          Remove the form field highlight color in the document.
+ *       Remove the form field highlight color in the document.
  * Parameters:
- *          hHandle     -   Handle to the form fill module. Returned by
- *FPDFDOC_InitFormFillEnvironment.
+ *       hHandle     -   Handle to the form fill module, as returned by
+ *                       FPDFDOC_InitFormFillEnvironment().
  * Return Value:
- *          NONE.
+ *       None.
  * Comments:
- *          Please refresh the client window to remove the highlight immediately
- *if necessary.
- **/
+ *       Please refresh the client window to remove the highlight immediately
+ *       if necessary.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_RemoveFormFieldHighlight(FPDF_FORMHANDLE hHandle);
 
-/**
+/*
 * Function: FPDF_FFLDraw
-*           Render FormFields and popup window on a page to a device independent
-*bitmap.
+*       Render FormFields and popup window on a page to a device independent
+*       bitmap.
 * Parameters:
-*           hHandle     -   Handle to the form fill module. Returned by
-*FPDFDOC_InitFormFillEnvironment.
-*           bitmap      -   Handle to the device independent bitmap (as the
-*output buffer).
-*                           Bitmap handle can be created by FPDFBitmap_Create
-*function.
-*           page        -   Handle to the page. Returned by FPDF_LoadPage
-*function.
-*           start_x     -   Left pixel position of the display area in the
-*device coordinate.
-*           start_y     -   Top pixel position of the display area in the device
-*coordinate.
-*           size_x      -   Horizontal size (in pixels) for displaying the page.
-*           size_y      -   Vertical size (in pixels) for displaying the page.
-*           rotate      -   Page orientation: 0 (normal), 1 (rotated 90 degrees
-*clockwise),
-*                               2 (rotated 180 degrees), 3 (rotated 90 degrees
-*counter-clockwise).
-*           flags       -   0 for normal display, or combination of flags
-*defined above.
+*       hHandle      -   Handle to the form fill module, as returned by
+*                        FPDFDOC_InitFormFillEnvironment().
+*       bitmap       -   Handle to the device independent bitmap (as the
+*                        output buffer). Bitmap handles can be created by
+*                        FPDFBitmap_Create().
+*       page         -   Handle to the page, as returned by FPDF_LoadPage().
+*       start_x      -   Left pixel position of the display area in the
+*                        device coordinates.
+*       start_y      -   Top pixel position of the display area in the device
+*                        coordinates.
+*       size_x       -   Horizontal size (in pixels) for displaying the page.
+*       size_y       -   Vertical size (in pixels) for displaying the page.
+*       rotate       -   Page orientation: 0 (normal), 1 (rotated 90 degrees
+*                        clockwise), 2 (rotated 180 degrees), 3 (rotated 90
+*                        degrees counter-clockwise).
+*       flags        -   0 for normal display, or combination of flags
+*                        defined above.
 * Return Value:
-*           None.
+*       None.
 * Comments:
-*           This function is designed to render annotations that are
-*user-interactive, which are widget annotation (for FormFields) and popup
-*annotation.
-*           With FPDF_ANNOT flag, this function will render popup annotation
-*when users mouse-hover on non-widget annotation. Regardless of FPDF_ANNOT flag,
-*this function will always render widget annotations for FormFields.
-*           In order to implement the FormFill functions, implementation should
-*call this function after rendering functions, such as FPDF_RenderPageBitmap or
-*FPDF_RenderPageBitmap_Start, finish rendering the page contents.
-**/
+*       This function is designed to render annotations that are
+*       user-interactive, which are widget annotations (for FormFields) and
+*       popup annotations.
+*       With the FPDF_ANNOT flag, this function will render a popup annotation
+*       when users mouse-hover on a non-widget annotation. Regardless of
+*       FPDF_ANNOT flag, this function will always render widget annotations
+*       for FormFields.
+*       In order to implement the FormFill functions, implementation should
+*       call this function after rendering functions, such as
+*       FPDF_RenderPageBitmap() or FPDF_RenderPageBitmap_Start(), have
+*       finished rendering the page contents.
+*/
 FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle,
                                             FPDF_BITMAP bitmap,
                                             FPDF_PAGE page,
@@ -1637,222 +1735,81 @@
                                               int flags);
 #endif
 
-/**
+/*
  * Experimental API
  * Function: FPDF_GetFormType
- *                      Returns the type of form contained in the PDF document.
+ *           Returns the type of form contained in the PDF document.
  * Parameters:
- *                      document                -       Handle to document.
- *Returned by FPDF_LoadDocument function.
- *                      docType                 -       Document type defined as
- *FORMTYPE_xxx.
+ *           document - Handle to document.
  * Return Value:
- *                      Integer value representing one of the FORMTYPE_xxx
- *values.
- **/
+ *           Integer value representing one of the FORMTYPE_ values.
+ * Comments:
+ *           If |document| is NULL, then the return value is FORMTYPE_NONE.
+ */
 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetFormType(FPDF_DOCUMENT document);
 
-#ifdef PDF_ENABLE_XFA
-/**
- * Function: FPDF_LoadXFA
- *          If the document consists of XFA fields, there should call this
- *method to load XFA fields.
+/*
+ * Experimental API
+ * Function: FORM_SetIndexSelected
+ *           Selects/deselects the value at the given |index| of the focused
+ *           annotation.
  * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
+ *           hHandle     -   Handle to the form fill module. Returned by
+ *                           FPDFDOC_InitFormFillEnvironment.
+ *           page        -   Handle to the page. Returned by FPDF_LoadPage
+ *           index       -   0-based index of value to be set as
+ *                           selected/unselected
+ *           selected    -   true to select, false to deselect
  * Return Value:
- *          TRUE indicates success,otherwise FALSE.
- **/
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_LoadXFA(FPDF_DOCUMENT document);
+ *           TRUE if the operation succeeded.
+ *           FALSE if the operation failed or widget is not a supported type.
+ * Comments:
+ *           Intended for use with listbox/combobox widget types. Comboboxes
+ *           have at most a single value selected at a time which cannot be
+ *           deselected. Deselect on a combobox is a no-op that returns false.
+ *           Default implementation is a no-op that will return false for
+ *           other types.
+ *           Not currently supported for XFA forms - will return false.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FORM_SetIndexSelected(FPDF_FORMHANDLE hHandle,
+                      FPDF_PAGE page,
+                      int index,
+                      FPDF_BOOL selected);
 
-/**
- * Function: FPDF_Widget_Undo
- *          This method will implement the undo feature for the specified xfa
- *field.
+/*
+ * Experimental API
+ * Function: FORM_IsIndexSelected
+ *           Returns whether or not the value at |index| of the focused
+ *           annotation is currently selected.
  * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
+ *           hHandle     -   Handle to the form fill module. Returned by
+ *                           FPDFDOC_InitFormFillEnvironment.
+ *           page        -   Handle to the page. Returned by FPDF_LoadPage
+ *           index       -   0-based Index of value to check
  * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Undo(FPDF_DOCUMENT document,
-                                                FPDF_WIDGET hWidget);
-/**
- * Function: FPDF_Widget_Redo
- *          This method will implement the redo feature for the specified xfa
- *field.
- * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Redo(FPDF_DOCUMENT document,
-                                                FPDF_WIDGET hWidget);
-/**
- * Function: FPDF_Widget_SelectAll
- *          This method will implement the select all feature for the specified
- *xfa field.
- * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_SelectAll(FPDF_DOCUMENT document,
-                                                     FPDF_WIDGET hWidget);
-/**
- * Function: FPDF_Widget_Copy
- *          This method will implement the copy feature for the specified xfa
- *field.
- * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
- *          wsText          -   Pointer to data buffer to receive the copied
- *data, in UTF-16LE format.
- *          size            -   The data buffer size.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Copy(FPDF_DOCUMENT document,
-                                                FPDF_WIDGET hWidget,
-                                                FPDF_WIDESTRING wsText,
-                                                FPDF_DWORD* size);
-/**
- * Function: FPDF_Widget_Cut
- *          This method will implement the cut feature for the specified xfa
- *field.
- * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
- *          wsText          -   Pointer to data buffer to receive the cut
- *data,in UTF-16LE format.
- *          size            -   The data buffer size,not the byte number.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Cut(FPDF_DOCUMENT document,
-                                               FPDF_WIDGET hWidget,
-                                               FPDF_WIDESTRING wsText,
-                                               FPDF_DWORD* size);
-/**
- * Function: FPDF_Widget_Paste
- *          This method will implement the paste feature for the specified xfa
- *field.
- * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
- *          wsText          -   The paste text buffer, in UTF-16LE format.
- *          size            -   The data buffer size,not the byte number.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV FPDF_Widget_Paste(FPDF_DOCUMENT document,
-                                                 FPDF_WIDGET hWidget,
-                                                 FPDF_WIDESTRING wsText,
-                                                 FPDF_DWORD size);
-/**
- * Function: FPDF_Widget_ReplaceSpellCheckWord
- *          This method will implement the spell check feature for the specified
- *xfa field.
- * Parameters:
- *          document        -   Handle to document. Returned by
- *FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
- *          x               -   The x value of the specified point.
- *          y               -   The y value of the specified point.
- *          bsText          -   The text buffer needed to be speck check, in
- *UTF-16LE format.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_Widget_ReplaceSpellCheckWord(FPDF_DOCUMENT document,
-                                  FPDF_WIDGET hWidget,
-                                  float x,
-                                  float y,
-                                  FPDF_BYTESTRING bsText);
-/**
- * Function: FPDF_Widget_GetSpellCheckWords
- *          This method will implement the spell check feature for the specified
- *          xfa field.
- * Parameters:
- *          document        -   Handle to document as returned by
- *                              FPDF_LoadDocument function.
- *          hWidget         -   Handle to the xfa field.
- *          x               -   The x value of the specified point.
- *          y               -   The y value of the specified point.
- *          stringHandle    -   Pointer to FPDF_STRINGHANDLE to receive the
- *                              speck check text buffer, in UTF-16LE format.
- *                              Caller must free using FPDF_StringHandleRelease.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_Widget_GetSpellCheckWords(FPDF_DOCUMENT document,
-                               FPDF_WIDGET hWidget,
-                               float x,
-                               float y,
-                               FPDF_STRINGHANDLE* stringHandle);
-/**
- * Function: FPDF_StringHandleCounts
- *          This method will get the count of the text buffer.
- * Parameters:
- *          stringHandle    -   Pointer to FPDF_STRINGHANDLE.
- * Return Value:
- *          None.
- **/
-FPDF_EXPORT int FPDF_CALLCONV
-FPDF_StringHandleCounts(FPDF_STRINGHANDLE stringHandle);
-/**
- * Function: FPDF_StringHandleGetStringByIndex
- *          This method will get the specified index of the text buffer.
- * Parameters:
- *          stringHandle    -   Pointer to FPDF_STRINGHANDLE.
- *          index           -   The specified index of text buffer.
- *          bsText          -   Pointer to data buffer to receive the text
- *buffer, in UTF-16LE format.
- *          size            -   The byte size of data buffer.
- * Return Value:
- *          TRUE indicates success, otherwise FALSE.
- **/
+ *           TRUE if value at |index| is currently selected.
+ *           FALSE if value at |index| is not selected or widget is not a
+ *           supported type.
+ * Comments:
+ *           Intended for use with listbox/combobox widget types. Default
+ *           implementation is a no-op that will return false for other types.
+ *           Not currently supported for XFA forms - will return false.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_StringHandleGetStringByIndex(FPDF_STRINGHANDLE stringHandle,
-                                  int index,
-                                  FPDF_BYTESTRING bsText,
-                                  FPDF_DWORD* size);
-/**
- * Function: FPDF_StringHandleRelease
- *          This method will release the FPDF_STRINGHANDLE.
+FORM_IsIndexSelected(FPDF_FORMHANDLE hHandle, FPDF_PAGE page, int index);
+
+/*
+ * Function: FPDF_LoadXFA
+ *          If the document consists of XFA fields, call this method to
+ *          attempt to load XFA fields.
  * Parameters:
- *          stringHandle    -   Pointer to FPDF_STRINGHANDLE.
+ *          document     -   Handle to document from FPDF_LoadDocument().
  * Return Value:
- *          None.
- **/
-FPDF_EXPORT void FPDF_CALLCONV
-FPDF_StringHandleRelease(FPDF_STRINGHANDLE stringHandle);
-/**
- * Function: FPDF_StringHandleAddString
- *          This method will add the specified text buffer.
- * Parameters:
- *          stringHandle    -   Pointer to FPDF_STRINGHANDLE.
- *          bsText          -   Pointer to data buffer of the text buffer, in
- *UTF-16LE format.
- *          size            -   The byte size of data buffer.
- * Return Value:
- *          TRUE indicates success, otherwise FALSE.
- **/
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_StringHandleAddString(FPDF_STRINGHANDLE stringHandle,
-                           FPDF_BYTESTRING bsText,
-                           FPDF_DWORD size);
-#endif  // PDF_ENABLE_XFA
+ *          TRUE upon success, otherwise FALSE. If XFA support is not built
+ *          into PDFium, performs no action and always returns FALSE.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_LoadXFA(FPDF_DOCUMENT document);
 
 #ifdef __cplusplus
 }
diff --git a/public/fpdf_fwlevent.h b/public/fpdf_fwlevent.h
index 591484a..c8593e9 100644
--- a/public/fpdf_fwlevent.h
+++ b/public/fpdf_fwlevent.h
@@ -14,17 +14,6 @@
 extern "C" {
 #endif  // __cplusplus
 
-typedef int FPDF_INT32;
-typedef unsigned int FPDF_UINT32;
-typedef float FPDF_FLOAT;
-
-// Event types.
-typedef enum {
-  FWL_EVENTTYPE_Mouse = 0,
-  FWL_EVENTTYPE_MouseWheel,
-  FWL_EVENTTYPE_Key,
-} FWL_EVENTTYPE;
-
 // Key flags.
 typedef enum {
   FWL_EVENTFLAG_ShiftKey = 1 << 0,
@@ -38,40 +27,6 @@
   FWL_EVENTFLAG_RightButtonDown = 1 << 8,
 } FWL_EVENTFLAG;
 
-// Mouse messages.
-typedef enum {
-  FWL_EVENTMOUSECMD_LButtonDown = 1,
-  FWL_EVENTMOUSECMD_LButtonUp,
-  FWL_EVENTMOUSECMD_LButtonDblClk,
-  FWL_EVENTMOUSECMD_RButtonDown,
-  FWL_EVENTMOUSECMD_RButtonUp,
-  FWL_EVENTMOUSECMD_RButtonDblClk,
-  FWL_EVENTMOUSECMD_MButtonDown,
-  FWL_EVENTMOUSECMD_MButtonUp,
-  FWL_EVENTMOUSECMD_MButtonDblClk,
-  FWL_EVENTMOUSECMD_MouseMove,
-  FWL_EVENTMOUSECMD_MouseEnter,
-  FWL_EVENTMOUSECMD_MouseHover,
-  FWL_EVENTMOUSECMD_MouseLeave,
-} FWL_EVENT_MOUSECMD;
-
-// Mouse events.
-struct FWL_EVENT_MOUSE {
-  FPDF_UINT32 command;
-  FPDF_DWORD flag;
-  FPDF_FLOAT x;
-  FPDF_FLOAT y;
-};
-
-// Mouse wheel events.
-struct FWL_EVENT_MOUSEWHEEL {
-  FPDF_DWORD flag;
-  FPDF_FLOAT x;
-  FPDF_FLOAT y;
-  FPDF_FLOAT deltaX;
-  FPDF_FLOAT deltaY;
-};
-
 // Virtual keycodes.
 typedef enum {
   FWL_VKEY_Back = 0x08,
@@ -245,38 +200,6 @@
   FWL_VKEY_Unknown = 0,
 } FWL_VKEYCODE;
 
-// Key event commands.
-typedef enum {
-  FWL_EVENTKEYCMD_KeyDown = 1,
-  FWL_EVENTKEYCMD_KeyUp,
-  FWL_EVENTKEYCMD_Char,
-} FWL_EVENTKEYCMD;
-
-// Key events.
-struct FWL_EVENT_KEY {
-  FPDF_UINT32 command;
-  FPDF_DWORD flag;
-  union {
-    // Virtual key code.
-    FPDF_UINT32 vkcode;
-    // Character code.
-    FPDF_DWORD charcode;
-  } code;
-};
-
-// Event types.
-struct FWL_EVENT {
-  // Structure size.
-  FPDF_UINT32 size;
-  // FWL_EVENTTYPE.
-  FPDF_UINT32 type;
-  union {
-    struct FWL_EVENT_MOUSE mouse;
-    struct FWL_EVENT_MOUSEWHEEL wheel;
-    struct FWL_EVENT_KEY key;
-  } s;
-};
-
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/public/fpdf_javascript.h b/public/fpdf_javascript.h
new file mode 100644
index 0000000..19f3810
--- /dev/null
+++ b/public/fpdf_javascript.h
@@ -0,0 +1,77 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PUBLIC_FPDF_JAVASCRIPT_H_
+#define PUBLIC_FPDF_JAVASCRIPT_H_
+
+// NOLINTNEXTLINE(build/include)
+#include "fpdfview.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+// Experimental API.
+// Get the number of JavaScript actions in |document|.
+//
+//   document - handle to a document.
+//
+// Returns the number of JavaScript actions in |document| or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFDoc_GetJavaScriptActionCount(FPDF_DOCUMENT document);
+
+// Experimental API.
+// Get the JavaScript action at |index| in |document|.
+//
+//   document - handle to a document.
+//   index    - the index of the requested JavaScript action.
+//
+// Returns the handle to the JavaScript action, or NULL on failure.
+// Caller owns the returned handle and must close it with
+// FPDFDoc_CloseJavaScriptAction().
+FPDF_EXPORT FPDF_JAVASCRIPT_ACTION FPDF_CALLCONV
+FPDFDoc_GetJavaScriptAction(FPDF_DOCUMENT document, int index);
+
+// Experimental API.
+// Close a loaded FPDF_JAVASCRIPT_ACTION object.
+
+//   javascript - Handle to a JavaScript action.
+FPDF_EXPORT void FPDF_CALLCONV
+FPDFDoc_CloseJavaScriptAction(FPDF_JAVASCRIPT_ACTION javascript);
+
+// Experimental API.
+// Get the name from the |javascript| handle. |buffer| is only modified if
+// |buflen| is longer than the length of the name. On errors, |buffer| is
+// unmodified and the returned length is 0.
+//
+//   javascript - handle to an JavaScript action.
+//   buffer     - buffer for holding the name, encoded in UTF-16LE.
+//   buflen     - length of the buffer in bytes.
+//
+// Returns the length of the JavaScript action name in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFJavaScriptAction_GetName(FPDF_JAVASCRIPT_ACTION javascript,
+                             FPDF_WCHAR* buffer,
+                             unsigned long buflen);
+
+// Experimental API.
+// Get the script from the |javascript| handle. |buffer| is only modified if
+// |buflen| is longer than the length of the script. On errors, |buffer| is
+// unmodified and the returned length is 0.
+//
+//   javascript - handle to an JavaScript action.
+//   buffer     - buffer for holding the name, encoded in UTF-16LE.
+//   buflen     - length of the buffer in bytes.
+//
+// Returns the length of the JavaScript action name in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFJavaScriptAction_GetScript(FPDF_JAVASCRIPT_ACTION javascript,
+                               FPDF_WCHAR* buffer,
+                               unsigned long buflen);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // PUBLIC_FPDF_JAVASCRIPT_H_
diff --git a/public/fpdf_ppo.h b/public/fpdf_ppo.h
index 88c3162..e9f3f66 100644
--- a/public/fpdf_ppo.h
+++ b/public/fpdf_ppo.h
@@ -28,6 +28,30 @@
                                                      FPDF_BYTESTRING pagerange,
                                                      int index);
 
+// Experimental API.
+// Create a new document from |src_doc|.  The pages of |src_doc| will be
+// combined to provide |num_pages_on_x_axis x num_pages_on_y_axis| pages per
+// |output_doc| page.
+//
+//   src_doc             - The document to be imported.
+//   output_width        - The output page width in PDF "user space" units.
+//   output_height       - The output page height in PDF "user space" units.
+//   num_pages_on_x_axis - The number of pages on X Axis.
+//   num_pages_on_y_axis - The number of pages on Y Axis.
+//
+// Return value:
+//   A handle to the created document, or NULL on failure.
+//
+// Comments:
+//   number of pages per page = num_pages_on_x_axis * num_pages_on_y_axis
+//
+FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
+FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc,
+                       float output_width,
+                       float output_height,
+                       size_t num_pages_on_x_axis,
+                       size_t num_pages_on_y_axis);
+
 // Copy the viewer preferences from |src_doc| into |dest_doc|.
 //
 //   dest_doc - Document to write the viewer preferences into.
diff --git a/public/fpdf_progressive.h b/public/fpdf_progressive.h
index 3f0ae29..99803b8 100644
--- a/public/fpdf_progressive.h
+++ b/public/fpdf_progressive.h
@@ -7,14 +7,13 @@
 #ifndef PUBLIC_FPDF_PROGRESSIVE_H_
 #define PUBLIC_FPDF_PROGRESSIVE_H_
 
+// clang-format off
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
 // Flags for progressive process status.
 #define FPDF_RENDER_READY 0
-#define FPDF_RENDER_READER 0  // Deprecated
 #define FPDF_RENDER_TOBECONTINUED 1
-#define FPDF_RENDER_TOBECOUNTINUED 1  // Deprecated.
 #define FPDF_RENDER_DONE 2
 #define FPDF_RENDER_FAILED 3
 
@@ -24,24 +23,23 @@
 
 // IFPDF_RENDERINFO interface.
 typedef struct _IFSDK_PAUSE {
-  /**
-  * Version number of the interface. Currently must be 1.
-  **/
+  /*
+   * Version number of the interface. Currently must be 1.
+   */
   int version;
 
   /*
-  * Method: NeedToPauseNow
-  *           Check if we need to pause a progressive process now.
-  * Interface Version:
-  *           1
-  * Implementation Required:
-  *           yes
-  * Parameters:
-  *           pThis       -   Pointer to the interface structure itself
-  * Return Value:
-  *            Non-zero for pause now, 0 for continue.
-  *
-  */
+   * Method: NeedToPauseNow
+   *           Check if we need to pause a progressive process now.
+   * Interface Version:
+   *           1
+   * Implementation Required:
+   *           yes
+   * Parameters:
+   *           pThis       -   Pointer to the interface structure itself
+   * Return Value:
+   *           Non-zero for pause now, 0 for continue.
+   */
   FPDF_BOOL (*NeedToPauseNow)(struct _IFSDK_PAUSE* pThis);
 
   // A user defined data pointer, used by user's application. Can be NULL.
@@ -53,28 +51,25 @@
 //          progressively.
 // Parameters:
 //          bitmap      -   Handle to the device independent bitmap (as the
-//          output buffer).
-//                          Bitmap handle can be created by FPDFBitmap_Create
-//                          function.
-//          page        -   Handle to the page. Returned by FPDF_LoadPage
-//          function.
+//                          output buffer). Bitmap handle can be created by
+//                          FPDFBitmap_Create().
+//          page        -   Handle to the page, as returned by FPDF_LoadPage().
 //          start_x     -   Left pixel position of the display area in the
-//          bitmap coordinate.
+//                          bitmap coordinates.
 //          start_y     -   Top pixel position of the display area in the bitmap
-//          coordinate.
+//                          coordinates.
 //          size_x      -   Horizontal size (in pixels) for displaying the page.
 //          size_y      -   Vertical size (in pixels) for displaying the page.
 //          rotate      -   Page orientation: 0 (normal), 1 (rotated 90 degrees
-//          clockwise),
-//                              2 (rotated 180 degrees), 3 (rotated 90 degrees
-//                              counter-clockwise).
+//                          clockwise), 2 (rotated 180 degrees), 3 (rotated 90
+//                          degrees counter-clockwise).
 //          flags       -   0 for normal display, or combination of flags
 //                          defined in fpdfview.h. With FPDF_ANNOT flag, it
 //                          renders all annotations that does not require
 //                          user-interaction, which are all annotations except
 //                          widget and popup annotations.
 //          pause       -   The IFSDK_PAUSE interface.A callback mechanism
-//          allowing the page rendering process
+//                          allowing the page rendering process
 // Return value:
 //          Rendering Status. See flags for progressive process status for the
 //          details.
@@ -92,12 +87,11 @@
 // Function: FPDF_RenderPage_Continue
 //          Continue rendering a PDF page.
 // Parameters:
-//          page        -   Handle to the page. Returned by FPDF_LoadPage
-//          function.
-//          pause       -   The IFSDK_PAUSE interface.A callback mechanism
-//          allowing the page rendering process
-//                          to be paused before it's finished. This can be NULL
-//                          if you don't want to pause.
+//          page        -   Handle to the page, as returned by FPDF_LoadPage().
+//          pause       -   The IFSDK_PAUSE interface (a callback mechanism
+//                          allowing the page rendering process to be paused
+//                          before it's finished). This can be NULL if you
+//                          don't want to pause.
 // Return value:
 //          The rendering status. See flags for progressive process status for
 //          the details.
@@ -109,10 +103,9 @@
 //          called after finishing rendering or
 //          cancel the rendering.
 // Parameters:
-//          page        -   Handle to the page. Returned by FPDF_LoadPage
-//          function.
+//          page        -   Handle to the page, as returned by FPDF_LoadPage().
 // Return value:
-//          NULL
+//          None.
 FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage_Close(FPDF_PAGE page);
 
 #ifdef __cplusplus
diff --git a/public/fpdf_save.h b/public/fpdf_save.h
index 6ffca52..527d942 100644
--- a/public/fpdf_save.h
+++ b/public/fpdf_save.h
@@ -7,6 +7,7 @@
 #ifndef PUBLIC_FPDF_SAVE_H_
 #define PUBLIC_FPDF_SAVE_H_
 
+// clang-format off
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
@@ -21,7 +22,6 @@
   //
   int version;
 
-  //
   // Method: WriteBlock
   //          Output a block of data in your custom way.
   // Interface Version:
@@ -36,24 +36,21 @@
   //          size        -   The size of the buffer.
   // Return value:
   //          Should be non-zero if successful, zero for error.
-  //
   int (*WriteBlock)(struct FPDF_FILEWRITE_* pThis,
                     const void* pData,
                     unsigned long size);
 } FPDF_FILEWRITE;
 
-/** @brief Incremental. */
+ // Flags for FPDF_SaveAsCopy()
 #define FPDF_INCREMENTAL 1
-/** @brief No Incremental. */
 #define FPDF_NO_INCREMENTAL 2
-/** @brief Remove security. */
 #define FPDF_REMOVE_SECURITY 3
 
 // Function: FPDF_SaveAsCopy
 //          Saves the copy of specified document in custom way.
 // Parameters:
-//          document        -   Handle to document. Returned by
-//          FPDF_LoadDocument and FPDF_CreateNewDocument.
+//          document        -   Handle to document, as returned by
+//                              FPDF_LoadDocument() or FPDF_CreateNewDocument().
 //          pFileWrite      -   A pointer to a custom file write structure.
 //          flags           -   The creating flags.
 // Return value:
@@ -64,14 +61,14 @@
                                                     FPDF_DWORD flags);
 
 // Function: FPDF_SaveWithVersion
-//          Same as function ::FPDF_SaveAsCopy, except the file version of the
-//          saved document could be specified by user.
+//          Same as FPDF_SaveAsCopy(), except the file version of the
+//          saved document can be specified by the caller.
 // Parameters:
 //          document        -   Handle to document.
 //          pFileWrite      -   A pointer to a custom file write structure.
 //          flags           -   The creating flags.
 //          fileVersion     -   The PDF file version. File version: 14 for 1.4,
-//          15 for 1.5, ...
+//                              15 for 1.5, ...
 // Return value:
 //          TRUE if succeed, FALSE if failed.
 //
diff --git a/public/fpdf_searchex.h b/public/fpdf_searchex.h
index 207c104..4b67786 100644
--- a/public/fpdf_searchex.h
+++ b/public/fpdf_searchex.h
@@ -17,7 +17,7 @@
 // Get the character index in |text_page| internal character list.
 //
 //   text_page  - a text page information structure.
-//   nTextIndex - index of the text returned from |FPDFText_GetText|.
+//   nTextIndex - index of the text returned from FPDFText_GetText().
 //
 // Returns the index of the character in internal character list. -1 for error.
 FPDF_EXPORT int FPDF_CALLCONV
@@ -28,7 +28,7 @@
 //   text_page  - a text page information structure.
 //   nCharIndex - index of the character in internal character list.
 //
-// Returns the index of the text returned from |FPDFText_GetText|. -1 for error.
+// Returns the index of the text returned from FPDFText_GetText(). -1 for error.
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFText_GetTextIndexFromCharIndex(FPDF_TEXTPAGE text_page, int nCharIndex);
 
diff --git a/public/fpdf_structtree.h b/public/fpdf_structtree.h
index 2e86ae6..a8083d5 100644
--- a/public/fpdf_structtree.h
+++ b/public/fpdf_structtree.h
@@ -7,6 +7,7 @@
 #ifndef PUBLIC_FPDF_STRUCTTREE_H_
 #define PUBLIC_FPDF_STRUCTTREE_H_
 
+// clang-format off
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
@@ -17,28 +18,27 @@
 // Function: FPDF_StructTree_GetForPage
 //          Get the structure tree for a page.
 // Parameters:
-//          page        -   Handle to the page. Returned by FPDF_LoadPage
-//          function.
+//          page        -   Handle to the page, as returned by FPDF_LoadPage().
 // Return value:
 //          A handle to the structure tree or NULL on error.
 FPDF_EXPORT FPDF_STRUCTTREE FPDF_CALLCONV
 FPDF_StructTree_GetForPage(FPDF_PAGE page);
 
 // Function: FPDF_StructTree_Close
-//          Release the resource allocate by FPDF_StructTree_GetForPage.
+//          Release a resource allocated by FPDF_StructTree_GetForPage().
 // Parameters:
-//          struct_tree -   Handle to the struct tree. Returned by
-//          FPDF_StructTree_LoadPage function.
+//          struct_tree -   Handle to the structure tree, as returned by
+//                          FPDF_StructTree_LoadPage().
 // Return value:
-//          NULL
+//          None.
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_StructTree_Close(FPDF_STRUCTTREE struct_tree);
 
 // Function: FPDF_StructTree_CountChildren
 //          Count the number of children for the structure tree.
 // Parameters:
-//          struct_tree -   Handle to the struct tree. Returned by
-//          FPDF_StructTree_LoadPage function.
+//          struct_tree -   Handle to the structure tree, as returned by
+//                          FPDF_StructTree_LoadPage().
 // Return value:
 //          The number of children, or -1 on error.
 FPDF_EXPORT int FPDF_CALLCONV
@@ -47,8 +47,8 @@
 // Function: FPDF_StructTree_GetChildAtIndex
 //          Get a child in the structure tree.
 // Parameters:
-//          struct_tree -   Handle to the struct tree. Returned by
-//          FPDF_StructTree_LoadPage function.
+//          struct_tree -   Handle to the structure tree, as returned by
+//                          FPDF_StructTree_LoadPage().
 //          index       -   The index for the child, 0-based.
 // Return value:
 //          The child at the n-th index or NULL on error.
diff --git a/public/fpdf_sysfontinfo.h b/public/fpdf_sysfontinfo.h
index 49302c8..936de96 100644
--- a/public/fpdf_sysfontinfo.h
+++ b/public/fpdf_sysfontinfo.h
@@ -7,6 +7,7 @@
 #ifndef PUBLIC_FPDF_SYSFONTINFO_H_
 #define PUBLIC_FPDF_SYSFONTINFO_H_
 
+// clang-format off
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
@@ -18,6 +19,9 @@
 #define FXFONT_HANGEUL_CHARSET 129
 #define FXFONT_GB2312_CHARSET 134
 #define FXFONT_CHINESEBIG5_CHARSET 136
+#define FXFONT_ARABIC_CHARSET 178
+#define FXFONT_CYRILLIC_CHARSET 204
+#define FXFONT_EASTERNEUROPEAN_CHARSET 238
 
 /* Font pitch and family flags */
 #define FXFONT_FF_FIXEDPITCH (1 << 0)
@@ -33,84 +37,84 @@
 extern "C" {
 #endif
 
-/**
+/*
  * Interface: FPDF_SYSFONTINFO
  *          Interface for getting system font information and font mapping
  */
 typedef struct _FPDF_SYSFONTINFO {
-  /**
+  /*
    * Version number of the interface. Currently must be 1.
-   **/
+   */
   int version;
 
-  /**
+  /*
    * Method: Release
    *          Give implementation a chance to release any data after the
-   * interface is no longer used
+   *          interface is no longer used.
    * Interface Version:
    *          1
    * Implementation Required:
    *          No
-   * Comments:
-   *          Called by Foxit SDK during the final cleanup process.
    * Parameters:
    *          pThis       -   Pointer to the interface structure itself
    * Return Value:
    *          None
+   * Comments:
+   *          Called by PDFium during the final cleanup process.
    */
   void (*Release)(struct _FPDF_SYSFONTINFO* pThis);
 
-  /**
+  /*
    * Method: EnumFonts
    *          Enumerate all fonts installed on the system
    * Interface Version:
    *          1
    * Implementation Required:
    *          No
-   * Comments:
-   *          Implementation should call FPDF_AddIntalledFont() function for
-   * each font found.
-   *          Only TrueType/OpenType and Type1 fonts are accepted by Foxit SDK.
    * Parameters:
    *          pThis       -   Pointer to the interface structure itself
    *          pMapper     -   An opaque pointer to internal font mapper, used
-   * when calling FPDF_AddInstalledFont
+   *                          when calling FPDF_AddInstalledFont().
    * Return Value:
    *          None
+   * Comments:
+   *          Implementations should call FPDF_AddIntalledFont() function for
+   *          each font found. Only TrueType/OpenType and Type1 fonts are accepted
+   *          by PDFium.
    */
   void (*EnumFonts)(struct _FPDF_SYSFONTINFO* pThis, void* pMapper);
 
-  /**
+  /*
    * Method: MapFont
    *          Use the system font mapper to get a font handle from requested
-   *parameters
+   *          parameters.
    * Interface Version:
    *          1
    * Implementation Required:
-   *          Yes only if GetFont method is not implemented.
-   * Comments:
-   *          If the system supports native font mapper (like Windows),
-   *implementation can implement this method to get a font handle.
-   *          Otherwise, Foxit SDK will do the mapping and then call GetFont
-   *method.
-   *          Only TrueType/OpenType and Type1 fonts are accepted by Foxit SDK.
+   *          Required if GetFont method is not implemented.
    * Parameters:
    *          pThis       -   Pointer to the interface structure itself
    *          weight      -   Weight of the requested font. 400 is normal and
-   *700 is bold.
+   *                          700 is bold.
    *          bItalic     -   Italic option of the requested font, TRUE or
-   *FALSE.
+   *                          FALSE.
    *          charset     -   Character set identifier for the requested font.
-   *See above defined constants.
+   *                          See above defined constants.
    *          pitch_family -  A combination of flags. See above defined
-   *constants.
+   *                          constants.
    *          face        -   Typeface name. Currently use system local encoding
-   *only.
+   *                          only.
    *          bExact      -   Obsolete: this parameter is now ignored.
    * Return Value:
    *          An opaque pointer for font handle, or NULL if system mapping is
-   *not supported.
-   **/
+   *          not supported.
+   * Comments:
+   *          If the system supports native font mapper (like Windows),
+   *          implementation can implement this method to get a font handle.
+   *          Otherwise, PDFium will do the mapping and then call GetFont
+   *          method. Only TrueType/OpenType and Type1 fonts are accepted
+   *          by PDFium.
+   */
   void* (*MapFont)(struct _FPDF_SYSFONTINFO* pThis,
                    int weight,
                    FPDF_BOOL bItalic,
@@ -119,56 +123,53 @@
                    const char* face,
                    FPDF_BOOL* bExact);
 
-  /**
+  /*
    * Method: GetFont
    *          Get a handle to a particular font by its internal ID
    * Interface Version:
    *          1
    * Implementation Required:
-   *          Yes only if MapFont method is not implemented.
-   * Comments:
-   *          If the system mapping not supported, Foxit SDK will do the font
-   *mapping and use this method to get a font handle.
-   * Parameters:
-   *          pThis       -   Pointer to the interface structure itself
-   *          face        -   Typeface name. Currently use system local encoding
-   *only.
+   *          Required if MapFont method is not implemented.
    * Return Value:
    *          An opaque pointer for font handle.
-   **/
+   * Parameters:
+   *          pThis       -   Pointer to the interface structure itself
+   *          face        -   Typeface name in system local encoding.
+   * Comments:
+   *          If the system mapping not supported, PDFium will do the font
+   *          mapping and use this method to get a font handle.
+   */
   void* (*GetFont)(struct _FPDF_SYSFONTINFO* pThis, const char* face);
 
-  /**
+  /*
    * Method: GetFontData
    *          Get font data from a font
    * Interface Version:
    *          1
    * Implementation Required:
    *          Yes
-   * Comments:
-   *          Can read either full font file, or a particular TrueType/OpenType
-   *table
    * Parameters:
    *          pThis       -   Pointer to the interface structure itself
    *          hFont       -   Font handle returned by MapFont or GetFont method
    *          table       -   TrueType/OpenType table identifier (refer to
-   *TrueType specification).
-   *                          0 for the whole font file.
+   *                          TrueType specification), or 0 for the whole file.
    *          buffer      -   The buffer receiving the font data. Can be NULL if
-   *not provided
-   *          buf_size    -   Buffer size, can be zero if not provided
+   *                          not provided.
+   *          buf_size    -   Buffer size, can be zero if not provided.
    * Return Value:
    *          Number of bytes needed, if buffer not provided or not large
-   *enough,
-   *          or number of bytes written into buffer otherwise.
-   **/
+   *          enough, or number of bytes written into buffer otherwise.
+   * Comments:
+   *          Can read either the full font file, or a particular
+   *          TrueType/OpenType table.
+   */
   unsigned long (*GetFontData)(struct _FPDF_SYSFONTINFO* pThis,
                                void* hFont,
                                unsigned int table,
                                unsigned char* buffer,
                                unsigned long buf_size);
 
-  /**
+  /*
    * Method: GetFaceName
    *          Get face name from a font handle
    * Interface Version:
@@ -179,19 +180,18 @@
    *          pThis       -   Pointer to the interface structure itself
    *          hFont       -   Font handle returned by MapFont or GetFont method
    *          buffer      -   The buffer receiving the face name. Can be NULL if
-   *not provided
+   *                          not provided
    *          buf_size    -   Buffer size, can be zero if not provided
    * Return Value:
    *          Number of bytes needed, if buffer not provided or not large
-   *enough,
-   *          or number of bytes written into buffer otherwise.
-   **/
+   *          enough, or number of bytes written into buffer otherwise.
+   */
   unsigned long (*GetFaceName)(struct _FPDF_SYSFONTINFO* pThis,
                                void* hFont,
                                char* buffer,
                                unsigned long buf_size);
 
-  /**
+  /*
    * Method: GetFontCharset
    *          Get character set information for a font handle
    * Interface Version:
@@ -203,10 +203,10 @@
    *          hFont       -   Font handle returned by MapFont or GetFont method
    * Return Value:
    *          Character set identifier. See defined constants above.
-   **/
+   */
   int (*GetFontCharset)(struct _FPDF_SYSFONTINFO* pThis, void* hFont);
 
-  /**
+  /*
    * Method: DeleteFont
    *          Delete a font handle
    * Interface Version:
@@ -218,20 +218,20 @@
    *          hFont       -   Font handle returned by MapFont or GetFont method
    * Return Value:
    *          None
-   **/
+   */
   void (*DeleteFont)(struct _FPDF_SYSFONTINFO* pThis, void* hFont);
 } FPDF_SYSFONTINFO;
 
-/**
+/*
  * Struct: FPDF_CharsetFontMap
  *    Provides the name of a font to use for a given charset value.
- **/
+ */
 typedef struct FPDF_CharsetFontMap_ {
   int charset;  // Character Set Enum value, see FXFONT_*_CHARSET above.
   const char* fontname;  // Name of default font to use with that charset.
 } FPDF_CharsetFontMap;
 
-/**
+/*
  * Function: FPDF_GetDefaultTTFMap
  *    Returns a pointer to the default character set to TT Font name map. The
  *    map is an array of FPDF_CharsetFontMap structs, with its end indicated
@@ -240,71 +240,69 @@
  *     None.
  * Return Value:
  *     Pointer to the Charset Font Map.
- **/
+ */
 FPDF_EXPORT const FPDF_CharsetFontMap* FPDF_CALLCONV FPDF_GetDefaultTTFMap();
 
-/**
+/*
  * Function: FPDF_AddInstalledFont
- *          Add a system font to the list in Foxit SDK.
+ *          Add a system font to the list in PDFium.
  * Comments:
  *          This function is only called during the system font list building
- *process.
+ *          process.
  * Parameters:
  *          mapper          -   Opaque pointer to Foxit font mapper
  *          face            -   The font face name
  *          charset         -   Font character set. See above defined constants.
  * Return Value:
  *          None.
- **/
+ */
 FPDF_EXPORT void FPDF_CALLCONV FPDF_AddInstalledFont(void* mapper,
                                                      const char* face,
                                                      int charset);
 
-/**
+/*
  * Function: FPDF_SetSystemFontInfo
- *          Set the system font info interface into Foxit SDK
- * Comments:
- *          Platform support implementation should implement required methods of
- *FFDF_SYSFONTINFO interface,
- *          then call this function during SDK initialization process.
+ *          Set the system font info interface into PDFium
  * Parameters:
  *          pFontInfo       -   Pointer to a FPDF_SYSFONTINFO structure
  * Return Value:
  *          None
- **/
+ * Comments:
+ *          Platform support implementation should implement required methods of
+ *          FFDF_SYSFONTINFO interface, then call this function during PDFium
+ *          initialization process.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_SetSystemFontInfo(FPDF_SYSFONTINFO* pFontInfo);
 
-/**
+/*
  * Function: FPDF_GetDefaultSystemFontInfo
  *          Get default system font info interface for current platform
- * Comments:
- *          For some platforms Foxit SDK implement a default version of system
- *font info interface.
- *          The default implementation can be used in FPDF_SetSystemFontInfo
- *function.
  * Parameters:
  *          None
  * Return Value:
  *          Pointer to a FPDF_SYSFONTINFO structure describing the default
- *interface.
- *          Or NULL if the platform doesn't have a default interface.
+ *          interface, or NULL if the platform doesn't have a default interface.
  *          Application should call FPDF_FreeDefaultSystemFontInfo to free the
- *returned pointer.
- **/
+ *          returned pointer.
+ * Comments:
+ *          For some platforms, PDFium implements a default version of system
+ *          font info interface. The default implementation can be passed to
+ *          FPDF_SetSystemFontInfo().
+ */
 FPDF_EXPORT FPDF_SYSFONTINFO* FPDF_CALLCONV FPDF_GetDefaultSystemFontInfo();
 
-/**
+/*
  * Function: FPDF_FreeDefaultSystemFontInfo
  *           Free a default system font info interface
- * Comments:
- *           This function should be called on the output from
- *FPDF_SetSystemFontInfo once it is no longer needed by the client.
  * Parameters:
  *           pFontInfo       -   Pointer to a FPDF_SYSFONTINFO structure
  * Return Value:
- *          None
- **/
+ *           None
+ * Comments:
+ *           This function should be called on the output from
+ *           FPDF_SetSystemFontInfo() once it is no longer needed.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_FreeDefaultSystemFontInfo(FPDF_SYSFONTINFO* pFontInfo);
 
diff --git a/public/fpdf_text.h b/public/fpdf_text.h
index 043dc16..65554e4 100644
--- a/public/fpdf_text.h
+++ b/public/fpdf_text.h
@@ -7,6 +7,7 @@
 #ifndef PUBLIC_FPDF_TEXT_H_
 #define PUBLIC_FPDF_TEXT_H_
 
+// clang-format off
 // NOLINTNEXTLINE(build/include)
 #include "fpdfview.h"
 
@@ -19,7 +20,7 @@
 //          Prepare information about all characters in a page.
 // Parameters:
 //          page    -   Handle to the page. Returned by FPDF_LoadPage function
-//          (in FPDFVIEW module).
+//                      (in FPDFVIEW module).
 // Return value:
 //          A handle to the text page information structure.
 //          NULL if something goes wrong.
@@ -34,7 +35,7 @@
 //          structure.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 // Return Value:
 //          None.
 //
@@ -44,7 +45,7 @@
 //          Get number of characters in a page.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 // Return value:
 //          Number of characters in the page. Return -1 for error.
 //          Generated characters, like additional space characters, new line
@@ -62,7 +63,7 @@
 //          Get Unicode of a character in a page.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          index       -   Zero-based index of the character.
 // Return value:
 //          The Unicode of the particular character.
@@ -77,30 +78,156 @@
 //          Get the font size of a particular character.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          index       -   Zero-based index of the character.
 // Return value:
 //          The font size of the particular character, measured in points (about
-//          1/72 inch).
-//          This is the typographic size of the font (so called "em size").
+//          1/72 inch). This is the typographic size of the font (so called
+//          "em size").
 //
 FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetFontSize(FPDF_TEXTPAGE text_page,
                                                       int index);
 
+// Experimental API.
+// Function: FPDFText_GetFontInfo
+//          Get the font name and flags of a particular character.
+// Parameters:
+//          text_page - Handle to a text page information structure.
+//                      Returned by FPDFText_LoadPage function.
+//          index     - Zero-based index of the character.
+//          buffer    - A buffer receiving the font name.
+//          buflen    - The length of |buffer| in bytes.
+//          flags     - Optional pointer to an int receiving the font flags.
+//                      These flags should be interpreted per PDF spec 1.7
+//                      Section 5.7.1 Font Descriptor Flags.
+// Return value:
+//          On success, return the length of the font name, including the
+//          trailing NUL character, in bytes. If this length is less than or
+//          equal to |length|, |buffer| is set to the font name, |flags| is
+//          set to the font flags. |buffer| is in UTF-8 encoding. Return 0 on
+//          failure.
+//
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFText_GetFontInfo(FPDF_TEXTPAGE text_page,
+                     int index,
+                     void* buffer,
+                     unsigned long buflen,
+                     int* flags);
+
+// Experimental API.
+// Function: FPDFText_GetFontWeight
+//          Get the font weight of a particular character.
+// Parameters:
+//          text_page   -   Handle to a text page information structure.
+//                          Returned by FPDFText_LoadPage function.
+//          index       -   Zero-based index of the character.
+// Return value:
+//          On success, return the font weight of the particular character. If
+//          |text_page| is invalid, if |index| is out of bounds, or if the
+//          character's text object is undefined, return -1.
+//
+FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetFontWeight(FPDF_TEXTPAGE text_page,
+                                                     int index);
+
+// Experimental API.
+// Function: FPDFText_GetTextRenderMode
+//          Get text rendering mode of character.
+// Parameters:
+//          text_page   -   Handle to a text page information structure.
+//                          Returned by FPDFText_LoadPage function.
+//          index       -   Zero-based index of the character.
+// Return Value:
+//          On success, return the render mode value. A valid value is of type
+//          FPDF_TEXT_RENDERMODE. If |text_page| is invalid, if |index| is out
+//          of bounds, or if the text object is undefined, then return
+//          FPDF_TEXTRENDERMODE_UNKNOWN.
+//
+FPDF_EXPORT FPDF_TEXT_RENDERMODE FPDF_CALLCONV
+FPDFText_GetTextRenderMode(FPDF_TEXTPAGE text_page, int index);
+
+// Experimental API.
+// Function: FPDFText_GetFillColor
+//          Get the fill color of a particular character.
+// Parameters:
+//          text_page      -   Handle to a text page information structure.
+//                             Returned by FPDFText_LoadPage function.
+//          index          -   Zero-based index of the character.
+//          R              -   Pointer to an unsigned int number receiving the
+//                             red value of the fill color.
+//          G              -   Pointer to an unsigned int number receiving the
+//                             green value of the fill color.
+//          B              -   Pointer to an unsigned int number receiving the
+//                             blue value of the fill color.
+//          A              -   Pointer to an unsigned int number receiving the
+//                             alpha value of the fill color.
+// Return value:
+//          Whether the call succeeded. If false, |R|, |G|, |B| and |A| are
+//          unchanged.
+//
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetFillColor(FPDF_TEXTPAGE text_page,
+                      int index,
+                      unsigned int* R,
+                      unsigned int* G,
+                      unsigned int* B,
+                      unsigned int* A);
+
+// Experimental API.
+// Function: FPDFText_GetStrokeColor
+//          Get the stroke color of a particular character.
+// Parameters:
+//          text_page      -   Handle to a text page information structure.
+//                             Returned by FPDFText_LoadPage function.
+//          index          -   Zero-based index of the character.
+//          R              -   Pointer to an unsigned int number receiving the
+//                             red value of the stroke color.
+//          G              -   Pointer to an unsigned int number receiving the
+//                             green value of the stroke color.
+//          B              -   Pointer to an unsigned int number receiving the
+//                             blue value of the stroke color.
+//          A              -   Pointer to an unsigned int number receiving the
+//                             alpha value of the stroke color.
+// Return value:
+//          Whether the call succeeded. If false, |R|, |G|, |B| and |A| are
+//          unchanged.
+//
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetStrokeColor(FPDF_TEXTPAGE text_page,
+                        int index,
+                        unsigned int* R,
+                        unsigned int* G,
+                        unsigned int* B,
+                        unsigned int* A);
+
+// Experimental API.
+// Function: FPDFText_GetCharAngle
+//          Get character rotation angle.
+// Parameters:
+//          text_page   -   Handle to a text page information structure.
+//                          Returned by FPDFText_LoadPage function.
+//          index       -   Zero-based index of the character.
+// Return Value:
+//          On success, return the angle value in radian. Value will always be
+//          greater or equal to 0. If |text_page| is invalid, or if |index| is
+//          out of bounds, then return -1.
+//
+FPDF_EXPORT float FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,
+                                                      int index);
+
 // Function: FPDFText_GetCharBox
 //          Get bounding box of a particular character.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          index       -   Zero-based index of the character.
 //          left        -   Pointer to a double number receiving left position
-//          of the character box.
+//                          of the character box.
 //          right       -   Pointer to a double number receiving right position
-//          of the character box.
+//                          of the character box.
 //          bottom      -   Pointer to a double number receiving bottom position
-//          of the character box.
+//                          of the character box.
 //          top         -   Pointer to a double number receiving top position of
-//          the character box.
+//                          the character box.
 // Return Value:
 //          On success, return TRUE and fill in |left|, |right|, |bottom|, and
 //          |top|. If |text_page| is invalid, or if |index| is out of bounds,
@@ -115,16 +242,54 @@
                                                         double* bottom,
                                                         double* top);
 
+// Experimental API.
+// Function: FPDFText_GetLooseCharBox
+//          Get a "loose" bounding box of a particular character, i.e., covering
+//          the entire glyph bounds, without taking the actual glyph shape into
+//          account.
+// Parameters:
+//          text_page   -   Handle to a text page information structure.
+//                          Returned by FPDFText_LoadPage function.
+//          index       -   Zero-based index of the character.
+//          rect        -   Pointer to a FS_RECTF receiving the character box.
+// Return Value:
+//          On success, return TRUE and fill in |rect|. If |text_page| is
+//          invalid, or if |index| is out of bounds, then return FALSE, and the
+//          |rect| out parameter remains unmodified.
+// Comments:
+//          All positions are measured in PDF "user space".
+//
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetLooseCharBox(FPDF_TEXTPAGE text_page, int index, FS_RECTF* rect);
+
+// Experimental API.
+// Function: FPDFText_GetMatrix
+//          Get the effective transformation matrix for a particular character.
+// Parameters:
+//          text_page   -   Handle to a text page information structure.
+//                          Returned by FPDFText_LoadPage().
+//          index       -   Zero-based index of the character.
+//          matrix      -   Pointer to a FS_MATRIX receiving the transformation
+//                          matrix.
+// Return Value:
+//          On success, return TRUE and fill in |matrix|. If |text_page| is
+//          invalid, or if |index| is out of bounds, or if |matrix| is NULL,
+//          then return FALSE, and |matrix| remains unmodified.
+//
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetMatrix(FPDF_TEXTPAGE text_page,
+                                                       int index,
+                                                       FS_MATRIX* matrix);
+
 // Function: FPDFText_GetCharOrigin
 //          Get origin of a particular character.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          index       -   Zero-based index of the character.
 //          x           -   Pointer to a double number receiving x coordinate of
-//          the character origin.
+//                          the character origin.
 //          y           -   Pointer to a double number receiving y coordinate of
-//          the character origin.
+//                          the character origin.
 // Return Value:
 //          Whether the call succeeded. If false, x and y are unchanged.
 // Comments:
@@ -141,18 +306,17 @@
 //          page.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          x           -   X position in PDF "user space".
 //          y           -   Y position in PDF "user space".
 //          xTolerance  -   An x-axis tolerance value for character hit
-//          detection, in point unit.
+//                          detection, in point units.
 //          yTolerance  -   A y-axis tolerance value for character hit
-//          detection, in point unit.
+//                          detection, in point units.
 // Return Value:
 //          The zero-based index of the character at, or nearby the point (x,y).
 //          If there is no character at or nearby the point, return value will
-//          be -1.
-//          If an error occurs, -3 will be returned.
+//          be -1. If an error occurs, -3 will be returned.
 //
 FPDF_EXPORT int FPDF_CALLCONV
 FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page,
@@ -165,13 +329,13 @@
 //          Extract unicode text string from the page.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          start_index -   Index for the start characters.
 //          count       -   Number of characters to be extracted.
 //          result      -   A buffer (allocated by application) receiving the
-//          extracted unicodes.
-//                          The size of the buffer must be able to hold the
-//                          number of characters plus a terminator.
+//                          extracted unicodes. The size of the buffer must be
+//                          able to hold the number of characters plus a
+//                          terminator.
 // Return Value:
 //          Number of characters written into the result buffer, including the
 //          trailing terminator.
@@ -187,18 +351,16 @@
 //          Count number of rectangular areas occupied by a segment of texts.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          start_index -   Index for the start characters.
 //          count       -   Number of characters.
 // Return value:
 //          Number of rectangles. Zero for error.
 // Comments:
 //          This function, along with FPDFText_GetRect can be used by
-//          applications to detect the position
-//          on the page for a text segment, so proper areas can be highlighted
-//          or something.
-//          FPDFTEXT will automatically merge small character boxes into bigger
-//          one if those characters
+//          applications to detect the position on the page for a text segment,
+//          so proper areas can be highlighted. FPDFTEXT will automatically
+//          merge small character boxes into bigger one if those characters
 //          are on the same line and use same font settings.
 //
 FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page,
@@ -210,21 +372,21 @@
 //          FPDFText_CountRects.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          rect_index  -   Zero-based index for the rectangle.
 //          left        -   Pointer to a double value receiving the rectangle
-//          left boundary.
+//                          left boundary.
 //          top         -   Pointer to a double value receiving the rectangle
-//          top boundary.
+//                          top boundary.
 //          right       -   Pointer to a double value receiving the rectangle
-//          right boundary.
+//                          right boundary.
 //          bottom      -   Pointer to a double value receiving the rectangle
-//          bottom boundary.
+//                          bottom boundary.
 // Return Value:
 //          On success, return TRUE and fill in |left|, |top|, |right|, and
-//          |bottom|. If |link_page| is invalid then return FALSE, and the out
-//          parameters remain unmodified. If |link_page| is valid but
-//          |link_index| is out of bounds, then return FALSE and set the out
+//          |bottom|. If |text_page| is invalid then return FALSE, and the out
+//          parameters remain unmodified. If |text_page| is valid but
+//          |rect_index| is out of bounds, then return FALSE and set the out
 //          parameters to 0.
 //
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page,
@@ -238,23 +400,21 @@
 //          Extract unicode text within a rectangular boundary on the page.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          left        -   Left boundary.
 //          top         -   Top boundary.
 //          right       -   Right boundary.
 //          bottom      -   Bottom boundary.
 //          buffer      -   A unicode buffer.
 //          buflen      -   Number of characters (not bytes) for the buffer,
-//          excluding an additional terminator.
+//                          excluding an additional terminator.
 // Return Value:
 //          If buffer is NULL or buflen is zero, return number of characters
-//          (not bytes) of text present within
-//          the rectangle, excluding a terminating NUL.  Generally you should
-//          pass a buffer at least one larger
-//          than this if you want a terminating NUL, which will be provided if
-//          space is available.
-//          Otherwise, return number of characters copied into the buffer,
-//          including the terminating NUL
+//          (not bytes) of text present within the rectangle, excluding a
+//          terminating NUL. Generally you should pass a buffer at least one
+//          larger than this if you want a terminating NUL, which will be
+//          provided if space is available. Otherwise, return number of
+//          characters copied into the buffer, including the terminating NUL
 //          when space for it is available.
 // Comment:
 //          If the buffer is too small, as much text as will fit is copied into
@@ -269,16 +429,19 @@
                                                       int buflen);
 
 // Flags used by FPDFText_FindStart function.
-#define FPDF_MATCHCASE \
-  0x00000001  // If not set, it will not match case by default.
-#define FPDF_MATCHWHOLEWORD \
-  0x00000002  // If not set, it will not match the whole word by default.
+//
+// If not set, it will not match case by default.
+#define FPDF_MATCHCASE 0x00000001
+// If not set, it will not match the whole word by default.
+#define FPDF_MATCHWHOLEWORD 0x00000002
+// If not set, it will skip past the current match to look for the next match.
+#define FPDF_CONSECUTIVE 0x00000004
 
 // Function: FPDFText_FindStart
 //          Start a search.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 //          findwhat    -   A unicode match pattern.
 //          flags       -   Option flags.
 //          start_index -   Start from this character. -1 for end of the page.
@@ -296,7 +459,7 @@
 //          Search in the direction from page start to end.
 // Parameters:
 //          handle      -   A search context handle returned by
-//          FPDFText_FindStart.
+//                          FPDFText_FindStart.
 // Return Value:
 //          Whether a match is found.
 //
@@ -306,7 +469,7 @@
 //          Search in the direction from page end to start.
 // Parameters:
 //          handle      -   A search context handle returned by
-//          FPDFText_FindStart.
+//                          FPDFText_FindStart.
 // Return Value:
 //          Whether a match is found.
 //
@@ -316,7 +479,7 @@
 //          Get the starting character index of the search result.
 // Parameters:
 //          handle      -   A search context handle returned by
-//          FPDFText_FindStart.
+//                          FPDFText_FindStart.
 // Return Value:
 //          Index for the starting character.
 //
@@ -326,7 +489,7 @@
 //          Get the number of matched characters in the search result.
 // Parameters:
 //          handle      -   A search context handle returned by
-//          FPDFText_FindStart.
+//                          FPDFText_FindStart.
 // Return Value:
 //          Number of matched characters.
 //
@@ -336,7 +499,7 @@
 //          Release a search context.
 // Parameters:
 //          handle      -   A search context handle returned by
-//          FPDFText_FindStart.
+//                          FPDFText_FindStart.
 // Return Value:
 //          None.
 //
@@ -346,22 +509,18 @@
 //          Prepare information about weblinks in a page.
 // Parameters:
 //          text_page   -   Handle to a text page information structure.
-//          Returned by FPDFText_LoadPage function.
+//                          Returned by FPDFText_LoadPage function.
 // Return Value:
-//          A handle to the page's links information structure.
+//          A handle to the page's links information structure, or
 //          NULL if something goes wrong.
 // Comments:
 //          Weblinks are those links implicitly embedded in PDF pages. PDF also
-//          has a type of
-//          annotation called "link", FPDFTEXT doesn't deal with that kind of
-//          link.
-//          FPDFTEXT weblink feature is useful for automatically detecting links
-//          in the page
-//          contents. For example, things like "http://www.foxitsoftware.com"
-//          will be detected,
-//          so applications can allow user to click on those characters to
-//          activate the link,
-//          even the PDF doesn't come with link annotations.
+//          has a type of annotation called "link" (FPDFTEXT doesn't deal with
+//          that kind of link). FPDFTEXT weblink feature is useful for
+//          automatically detecting links in the page contents. For example,
+//          things like "https://www.example.com" will be detected, so
+//          applications can allow user to click on those characters to activate
+//          the link, even the PDF doesn't come with link annotations.
 //
 //          FPDFLink_CloseWebLinks must be called to release resources.
 //
@@ -383,16 +542,16 @@
 //          link_page   -   Handle returned by FPDFLink_LoadWebLinks.
 //          link_index  -   Zero-based index for the link.
 //          buffer      -   A unicode buffer for the result.
-//          buflen      -   Number of characters (not bytes) for the buffer,
-//                          including an additional terminator.
+//          buflen      -   Number of 16-bit code units (not bytes) for the
+//                          buffer, including an additional terminator.
 // Return Value:
-//          If |buffer| is NULL or |buflen| is zero, return the number of
-//          characters (not bytes) needed to buffer the result (an additional
+//          If |buffer| is NULL or |buflen| is zero, return the number of 16-bit
+//          code units (not bytes) needed to buffer the result (an additional
 //          terminator is included in this count).
 //          Otherwise, copy the result into |buffer|, truncating at |buflen| if
-//          the result is too large to fit, and return the number of characters
-//          actually copied into the buffer (the additional terminator is also
-//          included in this count).
+//          the result is too large to fit, and return the number of 16-bit code
+//          units actually copied into the buffer (the additional terminator is
+//          also included in this count).
 //          If |link_index| does not correspond to a valid link, then the result
 //          is an empty string.
 //
@@ -441,6 +600,26 @@
                                                      double* right,
                                                      double* bottom);
 
+// Experimental API.
+// Function: FPDFLink_GetTextRange
+//          Fetch the start char index and char count for a link.
+// Parameters:
+//          link_page         -   Handle returned by FPDFLink_LoadWebLinks.
+//          link_index        -   Zero-based index for the link.
+//          start_char_index  -   pointer to int receiving the start char index
+//          char_count        -   pointer to int receiving the char count
+// Return Value:
+//          On success, return TRUE and fill in |start_char_index| and
+//          |char_count|. if |link_page| is invalid or if |link_index| does
+//          not correspond to a valid link, then return FALSE and the out
+//          parameters remain unmodified.
+//
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFLink_GetTextRange(FPDF_PAGELINK link_page,
+                      int link_index,
+                      int* start_char_index,
+                      int* char_count);
+
 // Function: FPDFLink_CloseWebLinks
 //          Release resources used by weblink feature.
 // Parameters:
diff --git a/public/fpdf_thumbnail.h b/public/fpdf_thumbnail.h
new file mode 100644
index 0000000..8ee8580
--- /dev/null
+++ b/public/fpdf_thumbnail.h
@@ -0,0 +1,59 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PUBLIC_FPDF_THUMBNAIL_H_
+#define PUBLIC_FPDF_THUMBNAIL_H_
+
+#include <stdint.h>
+
+// NOLINTNEXTLINE(build/include)
+#include "fpdfview.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Experimental API.
+// Gets the decoded data from the thumbnail of |page| if it exists.
+// This only modifies |buffer| if |buflen| less than or equal to the
+// size of the decoded data. Returns the size of the decoded
+// data or 0 if thumbnail DNE. Optional, pass null to just retrieve
+// the size of the buffer needed.
+//
+//   page    - handle to a page.
+//   buffer  - buffer for holding the decoded image data.
+//   buflen  - length of the buffer in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFPage_GetDecodedThumbnailData(FPDF_PAGE page,
+                                 void* buffer,
+                                 unsigned long buflen);
+
+// Experimental API.
+// Gets the raw data from the thumbnail of |page| if it exists.
+// This only modifies |buffer| if |buflen| is less than or equal to
+// the size of the raw data. Returns the size of the raw data or 0
+// if thumbnail DNE. Optional, pass null to just retrieve the size
+// of the buffer needed.
+//
+//   page    - handle to a page.
+//   buffer  - buffer for holding the raw image data.
+//   buflen  - length of the buffer in bytes.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFPage_GetRawThumbnailData(FPDF_PAGE page,
+                             void* buffer,
+                             unsigned long buflen);
+
+// Experimental API.
+// Returns the thumbnail of |page| as a FPDF_BITMAP. Returns a nullptr
+// if unable to access the thumbnail's stream.
+//
+//   page - handle to a page.
+FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
+FPDFPage_GetThumbnailAsBitmap(FPDF_PAGE page);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // PUBLIC_FPDF_THUMBNAIL_H_
diff --git a/public/fpdf_transformpage.h b/public/fpdf_transformpage.h
index a6ed04a..38ef113 100644
--- a/public/fpdf_transformpage.h
+++ b/public/fpdf_transformpage.h
@@ -14,18 +14,15 @@
 extern "C" {
 #endif
 
-typedef void* FPDF_PAGEARCSAVER;
-typedef void* FPDF_PAGEARCLOADER;
-
 /**
-*  Set "MediaBox" entry to the page dictionary.
-* @param[in] page   - Handle to a page.
-* @param[in] left   - The left of the rectangle.
-* @param[in] bottom - The bottom of the rectangle.
-* @param[in] right  - The right of the rectangle.
-* @param[in] top    - The top of the rectangle.
-* @retval None.
-*/
+ * Set "MediaBox" entry to the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - The left of the rectangle.
+ * bottom - The bottom of the rectangle.
+ * right  - The right of the rectangle.
+ * top    - The top of the rectangle.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetMediaBox(FPDF_PAGE page,
                                                     float left,
                                                     float bottom,
@@ -33,50 +30,95 @@
                                                     float top);
 
 /**
-*  Set "CropBox" entry to the page dictionary.
-* @param[in] page   - Handle to a page.
-* @param[in] left   - The left of the rectangle.
-* @param[in] bottom - The bottom of the rectangle.
-* @param[in] right  - The right of the rectangle.
-* @param[in] top    - The top of the rectangle.
-* @retval None.
-*/
+ * Set "CropBox" entry to the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - The left of the rectangle.
+ * bottom - The bottom of the rectangle.
+ * right  - The right of the rectangle.
+ * top    - The top of the rectangle.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetCropBox(FPDF_PAGE page,
                                                    float left,
                                                    float bottom,
                                                    float right,
                                                    float top);
 
-/**  Get "MediaBox" entry from the page dictionary.
-* @param[in] page   - Handle to a page.
-* @param[in] left   - Pointer to a double value receiving the left of the
-* rectangle.
-* @param[in] bottom - Pointer to a double value receiving the bottom of the
-* rectangle.
-* @param[in] right  - Pointer to a double value receiving the right of the
-* rectangle.
-* @param[in] top    - Pointer to a double value receiving the top of the
-* rectangle.
-* @retval True if success,else fail.
-*/
+/**
+ * Set "BleedBox" entry to the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - The left of the rectangle.
+ * bottom - The bottom of the rectangle.
+ * right  - The right of the rectangle.
+ * top    - The top of the rectangle.
+ */
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetBleedBox(FPDF_PAGE page,
+                                                    float left,
+                                                    float bottom,
+                                                    float right,
+                                                    float top);
+
+/**
+ * Set "TrimBox" entry to the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - The left of the rectangle.
+ * bottom - The bottom of the rectangle.
+ * right  - The right of the rectangle.
+ * top    - The top of the rectangle.
+ */
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetTrimBox(FPDF_PAGE page,
+                                                   float left,
+                                                   float bottom,
+                                                   float right,
+                                                   float top);
+
+/**
+ * Set "ArtBox" entry to the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - The left of the rectangle.
+ * bottom - The bottom of the rectangle.
+ * right  - The right of the rectangle.
+ * top    - The top of the rectangle.
+ */
+FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetArtBox(FPDF_PAGE page,
+                                                  float left,
+                                                  float bottom,
+                                                  float right,
+                                                  float top);
+
+/**
+ * Get "MediaBox" entry from the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - Pointer to a float value receiving the left of the rectangle.
+ * bottom - Pointer to a float value receiving the bottom of the rectangle.
+ * right  - Pointer to a float value receiving the right of the rectangle.
+ * top    - Pointer to a float value receiving the top of the rectangle.
+ *
+ * On success, return true and write to the out parameters. Otherwise return
+ * false and leave the out parameters unmodified.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetMediaBox(FPDF_PAGE page,
                                                          float* left,
                                                          float* bottom,
                                                          float* right,
                                                          float* top);
 
-/**  Get "CropBox" entry from the page dictionary.
-* @param[in] page   - Handle to a page.
-* @param[in] left   - Pointer to a double value receiving the left of the
-* rectangle.
-* @param[in] bottom - Pointer to a double value receiving the bottom of the
-* rectangle.
-* @param[in] right  - Pointer to a double value receiving the right of the
-* rectangle.
-* @param[in] top    - Pointer to a double value receiving the top of the
-* rectangle.
-* @retval True if success,else fail.
-*/
+/**
+ * Get "CropBox" entry from the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - Pointer to a float value receiving the left of the rectangle.
+ * bottom - Pointer to a float value receiving the bottom of the rectangle.
+ * right  - Pointer to a float value receiving the right of the rectangle.
+ * top    - Pointer to a float value receiving the top of the rectangle.
+ *
+ * On success, return true and write to the out parameters. Otherwise return
+ * false and leave the out parameters unmodified.
+ */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetCropBox(FPDF_PAGE page,
                                                         float* left,
                                                         float* bottom,
@@ -84,6 +126,60 @@
                                                         float* top);
 
 /**
+ * Get "BleedBox" entry from the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - Pointer to a float value receiving the left of the rectangle.
+ * bottom - Pointer to a float value receiving the bottom of the rectangle.
+ * right  - Pointer to a float value receiving the right of the rectangle.
+ * top    - Pointer to a float value receiving the top of the rectangle.
+ *
+ * On success, return true and write to the out parameters. Otherwise return
+ * false and leave the out parameters unmodified.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetBleedBox(FPDF_PAGE page,
+                                                         float* left,
+                                                         float* bottom,
+                                                         float* right,
+                                                         float* top);
+
+/**
+ * Get "TrimBox" entry from the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - Pointer to a float value receiving the left of the rectangle.
+ * bottom - Pointer to a float value receiving the bottom of the rectangle.
+ * right  - Pointer to a float value receiving the right of the rectangle.
+ * top    - Pointer to a float value receiving the top of the rectangle.
+ *
+ * On success, return true and write to the out parameters. Otherwise return
+ * false and leave the out parameters unmodified.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetTrimBox(FPDF_PAGE page,
+                                                        float* left,
+                                                        float* bottom,
+                                                        float* right,
+                                                        float* top);
+
+/**
+ * Get "ArtBox" entry from the page dictionary.
+ *
+ * page   - Handle to a page.
+ * left   - Pointer to a float value receiving the left of the rectangle.
+ * bottom - Pointer to a float value receiving the bottom of the rectangle.
+ * right  - Pointer to a float value receiving the right of the rectangle.
+ * top    - Pointer to a float value receiving the top of the rectangle.
+ *
+ * On success, return true and write to the out parameters. Otherwise return
+ * false and leave the out parameters unmodified.
+ */
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetArtBox(FPDF_PAGE page,
+                                                       float* left,
+                                                       float* bottom,
+                                                       float* right,
+                                                       float* top);
+
+/**
  * Apply transforms to |page|.
  *
  * If |matrix| is provided it will be applied to transform the page.
@@ -91,29 +187,30 @@
  * If neither |matrix| or |clipRect| are provided this method returns |false|.
  * Returns |true| if transforms are applied.
  *
- * @param[in] page        - Page handle.
- * @param[in] matrix      - Transform matrix.
- * @param[in] clipRect    - Clipping rectangle.
- * @Note. This function will transform the whole page, and would take effect to
- * all the objects in the page.
+ * This function will transform the whole page, and would take effect to all the
+ * objects in the page.
+ *
+ * page        - Page handle.
+ * matrix      - Transform matrix.
+ * clipRect    - Clipping rectangle.
  */
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPage_TransFormWithClip(FPDF_PAGE page,
-                           FS_MATRIX* matrix,
-                           FS_RECTF* clipRect);
+                           const FS_MATRIX* matrix,
+                           const FS_RECTF* clipRect);
 
 /**
-* Transform (scale, rotate, shear, move) the clip path of page object.
-* @param[in] page_object - Handle to a page object. Returned by
-* FPDFPageObj_NewImageObj.
-* @param[in] a  - The coefficient "a" of the matrix.
-* @param[in] b  - The coefficient "b" of the matrix.
-* @param[in] c  - The coefficient "c" of the matrix.
-* @param[in] d  - The coefficient "d" of the matrix.
-* @param[in] e  - The coefficient "e" of the matrix.
-* @param[in] f  - The coefficient "f" of the matrix.
-* @retval None.
-*/
+ * Transform (scale, rotate, shear, move) the clip path of page object.
+ * page_object - Handle to a page object. Returned by
+ * FPDFPageObj_NewImageObj().
+ *
+ * a  - The coefficient "a" of the matrix.
+ * b  - The coefficient "b" of the matrix.
+ * c  - The coefficient "c" of the matrix.
+ * d  - The coefficient "d" of the matrix.
+ * e  - The coefficient "e" of the matrix.
+ * f  - The coefficient "f" of the matrix.
+ */
 FPDF_EXPORT void FPDF_CALLCONV
 FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
                               double a,
@@ -123,39 +220,84 @@
                               double e,
                               double f);
 
+// Experimental API.
+// Get the clip path of the page object.
+//
+//   page object - Handle to a page object. Returned by e.g.
+//                 FPDFPage_GetObject().
+//
+// Caller does not take ownership of the returned FPDF_CLIPPATH. Instead, it
+// remains valid until FPDF_ClosePage() is called for the page containing
+// page_object.
+FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV
+FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object);
+
+// Experimental API.
+// Get number of paths inside |clip_path|.
+//
+//   clip_path - handle to a clip_path.
+//
+// Returns the number of objects in |clip_path| or -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path);
+
+// Experimental API.
+// Get number of segments inside one path of |clip_path|.
+//
+//   clip_path  - handle to a clip_path.
+//   path_index - index into the array of paths of the clip path.
+//
+// Returns the number of segments or -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index);
+
+// Experimental API.
+// Get segment in one specific path of |clip_path| at index.
+//
+//   clip_path     - handle to a clip_path.
+//   path_index    - the index of a path.
+//   segment_index - the index of a segment.
+//
+// Returns the handle to the segment, or NULL on failure.  The caller does not
+// take ownership of the returned FPDF_CLIPPATH. Instead, it remains valid until
+// FPDF_ClosePage() is called for the page containing page_object.
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,
+                            int path_index,
+                            int segment_index);
+
 /**
-* Create a new clip path, with a rectangle inserted.
-*
-* @param[in] left   - The left of the clip box.
-* @param[in] bottom - The bottom of the clip box.
-* @param[in] right  - The right of the clip box.
-* @param[in] top    - The top of the clip box.
-* @retval a handle to the clip path.
-*/
+ * Create a new clip path, with a rectangle inserted.
+ *
+ * Caller takes ownership of the returned FPDF_CLIPPATH. It should be freed with
+ * FPDF_DestroyClipPath().
+ *
+ * left   - The left of the clip box.
+ * bottom - The bottom of the clip box.
+ * right  - The right of the clip box.
+ * top    - The top of the clip box.
+ */
 FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left,
                                                             float bottom,
                                                             float right,
                                                             float top);
 
 /**
-* Destroy the clip path.
-*
-* @param[in] clipPath - A handle to the clip path.
-* Destroy the clip path.
-* @retval None.
-*/
+ * Destroy the clip path.
+ *
+ * clipPath - A handle to the clip path. It will be invalid after this call.
+ */
 FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath);
 
 /**
-* Clip the page content, the page content that outside the clipping region
-* become invisible.
-*
-* @param[in] page        - A page handle.
-* @param[in] clipPath    - A handle to the clip path.
-* @Note. A clip path will be inserted before the page content stream or content
-* array. In this way, the page content will be clipped
-* by this clip path.
-*/
+ * Clip the page content, the page content that outside the clipping region
+ * become invisible.
+ *
+ * A clip path will be inserted before the page content stream or content array.
+ * In this way, the page content will be clipped by this clip path.
+ *
+ * page        - A page handle.
+ * clipPath    - A handle to the clip path. (Does not take ownership.)
+ */
 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page,
                                                        FPDF_CLIPPATH clipPath);
 
diff --git a/public/fpdfview.h b/public/fpdfview.h
index ffa4678..0542b05 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -10,6 +10,10 @@
 #ifndef PUBLIC_FPDFVIEW_H_
 #define PUBLIC_FPDFVIEW_H_
 
+// clang-format off
+
+#include <stddef.h>
+
 #if defined(_WIN32) && !defined(__WINDOWS__)
 #include <windows.h>
 #endif
@@ -32,46 +36,52 @@
 #define FPDF_OBJECT_NULLOBJ 8
 #define FPDF_OBJECT_REFERENCE 9
 
-// PDF types
-typedef void* FPDF_ACTION;
-typedef void* FPDF_ANNOTATION;
-typedef void* FPDF_ATTACHMENT;
-typedef void* FPDF_BITMAP;
-typedef void* FPDF_BOOKMARK;
-typedef void* FPDF_CLIPPATH;
-typedef void* FPDF_DEST;
-typedef void* FPDF_DOCUMENT;
-typedef void* FPDF_FONT;
-typedef void* FPDF_LINK;
-typedef void* FPDF_PAGE;
-typedef void* FPDF_PAGELINK;
-typedef void* FPDF_PAGEOBJECT;  // Page object(text, path, etc)
-typedef void* FPDF_PAGERANGE;
-typedef void* FPDF_RECORDER;
-typedef void* FPDF_SCHHANDLE;
-typedef void* FPDF_STRUCTELEMENT;
-typedef void* FPDF_STRUCTTREE;
-typedef void* FPDF_TEXTPAGE;
-typedef void const* FPDF_PATHSEGMENT;
+// PDF text rendering modes
+typedef enum {
+  FPDF_TEXTRENDERMODE_UNKNOWN = -1,
+  FPDF_TEXTRENDERMODE_FILL = 0,
+  FPDF_TEXTRENDERMODE_STROKE = 1,
+  FPDF_TEXTRENDERMODE_FILL_STROKE = 2,
+  FPDF_TEXTRENDERMODE_INVISIBLE = 3,
+  FPDF_TEXTRENDERMODE_FILL_CLIP = 4,
+  FPDF_TEXTRENDERMODE_STROKE_CLIP = 5,
+  FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP = 6,
+  FPDF_TEXTRENDERMODE_CLIP = 7,
+  FPDF_TEXTRENDERMODE_LAST = FPDF_TEXTRENDERMODE_CLIP,
+} FPDF_TEXT_RENDERMODE;
 
-#ifdef PDF_ENABLE_XFA
-typedef void* FPDF_STRINGHANDLE;
-typedef void* FPDF_WIDGET;
-#endif  // PDF_ENABLE_XFA
+// PDF types - use incomplete types (never completed) just for API type safety.
+typedef struct fpdf_action_t__* FPDF_ACTION;
+typedef struct fpdf_annotation_t__* FPDF_ANNOTATION;
+typedef struct fpdf_attachment_t__* FPDF_ATTACHMENT;
+typedef struct fpdf_bitmap_t__* FPDF_BITMAP;
+typedef struct fpdf_bookmark_t__* FPDF_BOOKMARK;
+typedef struct fpdf_clippath_t__* FPDF_CLIPPATH;
+typedef struct fpdf_dest_t__* FPDF_DEST;
+typedef struct fpdf_document_t__* FPDF_DOCUMENT;
+typedef struct fpdf_font_t__* FPDF_FONT;
+typedef struct fpdf_form_handle_t__* FPDF_FORMHANDLE;
+typedef struct fpdf_javascript_action_t* FPDF_JAVASCRIPT_ACTION;
+typedef struct fpdf_link_t__* FPDF_LINK;
+typedef struct fpdf_page_t__* FPDF_PAGE;
+typedef struct fpdf_pagelink_t__* FPDF_PAGELINK;
+typedef struct fpdf_pageobject_t__* FPDF_PAGEOBJECT;  // (text, path, etc.)
+typedef struct fpdf_pageobjectmark_t__* FPDF_PAGEOBJECTMARK;
+typedef struct fpdf_pagerange_t__* FPDF_PAGERANGE;
+typedef const struct fpdf_pathsegment_t* FPDF_PATHSEGMENT;
+typedef void* FPDF_RECORDER;  // Passed into skia.
+typedef struct fpdf_schhandle_t__* FPDF_SCHHANDLE;
+typedef struct fpdf_structelement_t__* FPDF_STRUCTELEMENT;
+typedef struct fpdf_structtree_t__* FPDF_STRUCTTREE;
+typedef struct fpdf_textpage_t__* FPDF_TEXTPAGE;
+typedef struct fpdf_widget_t__* FPDF_WIDGET;
 
 // Basic data types
 typedef int FPDF_BOOL;
-typedef int FPDF_ERROR;
+typedef int FPDF_RESULT;
 typedef unsigned long FPDF_DWORD;
 typedef float FS_FLOAT;
 
-#ifdef PDF_ENABLE_XFA
-typedef void* FPDF_LPVOID;
-typedef void const* FPDF_LPCVOID;
-typedef char const* FPDF_LPCSTR;
-typedef int FPDF_RESULT;
-#endif
-
 // Duplex types
 typedef enum _FPDF_DUPLEXTYPE_ {
   DuplexUndefined = 0,
@@ -82,7 +92,6 @@
 
 // String types
 typedef unsigned short FPDF_WCHAR;
-typedef unsigned char const* FPDF_LPCBYTE;
 
 // FPDFSDK may use three types of strings: byte string, wide string (UTF-16LE
 // encoded), and platform dependent string
@@ -92,16 +101,13 @@
 // bytes (except surrogation), with the low byte first.
 typedef const unsigned short* FPDF_WIDESTRING;
 
-#ifdef PDF_ENABLE_XFA
-// Structure for a byte string.
-// Note, a byte string commonly means a UTF-16LE formated string.
-typedef struct _FPDF_BSTR {
-  // String buffer.
-  char* str;
-  // Length of the string, in bytes.
-  int len;
+// Structure for persisting a string beyond the duration of a callback.
+// Note: although represented as a char*, string may be interpreted as
+// a UTF-16LE formated string. Used only by XFA callbacks.
+typedef struct FPDF_BSTR_ {
+  char* str;  // String buffer, manipulate only with FPDF_BStr_* methods.
+  int len;    // Length of the string, in bytes.
 } FPDF_BSTR;
-#endif  // PDF_ENABLE_XFA
 
 // For Windows programmers: In most cases it's OK to treat FPDF_WIDESTRING as a
 // Windows unicode string, however, special care needs to be taken if you
@@ -144,6 +150,24 @@
 // Const Pointer to FS_RECTF structure.
 typedef const FS_RECTF* FS_LPCRECTF;
 
+// Rectangle size. Coordinate system agnostic.
+typedef struct FS_SIZEF_ {
+  float width;
+  float height;
+} * FS_LPSIZEF, FS_SIZEF;
+
+// Const Pointer to FS_SIZEF structure.
+typedef const FS_SIZEF* FS_LPCSIZEF;
+
+// 2D Point. Coordinate system agnostic.
+typedef struct FS_POINTF_ {
+  float x;
+  float y;
+} * FS_LPPOINTF, FS_POINTF;
+
+// Const Pointer to FS_POINTF structure.
+typedef const FS_POINTF* FS_LPCPOINTF;
+
 // Annotation enums.
 typedef int FPDF_ANNOTATION_SUBTYPE;
 typedef int FPDF_ANNOT_APPEARANCEMODE;
@@ -151,12 +175,29 @@
 // Dictionary value types.
 typedef int FPDF_OBJECT_TYPE;
 
-#if defined(_WIN32) && defined(FPDFSDK_EXPORTS)
-// On Windows system, functions are exported in a DLL
+#if defined(COMPONENT_BUILD)
+// FPDF_EXPORT should be consistent with |export| in the pdfium_fuzzer
+// template in testing/fuzzers/BUILD.gn.
+#if defined(WIN32)
+#if defined(FPDF_IMPLEMENTATION)
 #define FPDF_EXPORT __declspec(dllexport)
-#define FPDF_CALLCONV __stdcall
+#else
+#define FPDF_EXPORT __declspec(dllimport)
+#endif  // defined(FPDF_IMPLEMENTATION)
+#else
+#if defined(FPDF_IMPLEMENTATION)
+#define FPDF_EXPORT __attribute__((visibility("default")))
 #else
 #define FPDF_EXPORT
+#endif  // defined(FPDF_IMPLEMENTATION)
+#endif  // defined(WIN32)
+#else
+#define FPDF_EXPORT
+#endif  // defined(COMPONENT_BUILD)
+
+#if defined(WIN32) && defined(FPDFSDK_EXPORTS)
+#define FPDF_CALLCONV __stdcall
+#else
 #define FPDF_CALLCONV
 #endif
 
@@ -173,12 +214,14 @@
 //          None.
 // Comments:
 //          Convenience function to call FPDF_InitLibraryWithConfig() for
-//          backwards comatibility purposes.
+//          backwards compatibility purposes. This will be deprecated in the
+//          future.
 FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary();
 
 // Process-wide options for initializing the library.
 typedef struct FPDF_LIBRARY_CONFIG_ {
   // Version number of the interface. Currently must be 2.
+  // Support for version 1 will be deprecated in the future.
   int version;
 
   // Array of paths to scan in place of the defaults when using built-in
@@ -193,9 +236,9 @@
   void* m_pIsolate;
 
   // The embedder data slot to use in the v8::Isolate to store PDFium's
-  // per-isolate data. The value needs to be between 0 and
-  // v8::Internals::kNumIsolateDataLots (exclusive). Note that 0 is fine
-  // for most embedders.
+  // per-isolate data. The value needs to be in the range
+  // [0, |v8::Internals::kNumIsolateDataLots|). Note that 0 is fine for most
+  // embedders.
   unsigned int m_v8EmbedderSlot;
 } FPDF_LIBRARY_CONFIG;
 
@@ -267,19 +310,6 @@
 FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi);
 #endif  // PDFIUM_PRINT_TEXT_WITH_GDI
 
-// Function: FPDF_SetPrintPostscriptLevel
-//          Set postscript printing level when printing on Windows.
-//          Experimental API.
-// Parameters:
-//          postscript_level- 0 to disable postscript printing,
-//                            2 to print with postscript level 2,
-//                            3 to print with postscript level 3.
-//                            All other values are invalid.
-// Return value:
-//          True if successful, false if unsuccessful (typically invalid input).
-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
-FPDF_SetPrintPostscriptLevel(int postscript_level);
-
 // Function: FPDF_SetPrintMode
 //          Set printing mode when printing on Windows.
 //          Experimental API.
@@ -287,8 +317,14 @@
 //          mode - FPDF_PRINTMODE_EMF to output EMF (default)
 //                 FPDF_PRINTMODE_TEXTONLY to output text only (for charstream
 //                 devices)
-//                 FPDF_PRINTMODE_POSTSCRIPT2 to output level 2 postscript
-//                 FPDF_PRINTMODE_POSTSCRIPT3 to output level 3 postscript
+//                 FPDF_PRINTMODE_POSTSCRIPT2 to output level 2 PostScript into
+//                 EMF as a series of GDI comments.
+//                 FPDF_PRINTMODE_POSTSCRIPT3 to output level 3 PostScript into
+//                 EMF as a series of GDI comments.
+//                 FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH to output level 2
+//                 PostScript via ExtEscape() in PASSTHROUGH mode.
+//                 FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH to output level 3
+//                 PostScript via ExtEscape() in PASSTHROUGH mode.
 // Return value:
 //          True if successful, false if unsuccessful (typically invalid input).
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode);
@@ -300,12 +336,19 @@
 //          file_path -  Path to the PDF file (including extension).
 //          password  -  A string used as the password for the PDF file.
 //                       If no password is needed, empty or NULL can be used.
+//                       See comments below regarding the encoding.
 // Return value:
 //          A handle to the loaded document, or NULL on failure.
 // Comments:
 //          Loaded document can be closed by FPDF_CloseDocument().
 //          If this function fails, you can use FPDF_GetLastError() to retrieve
 //          the reason why it failed.
+//
+//          The encoding for |password| can be either UTF-8 or Latin-1. PDFs,
+//          depending on the security handler revision, will only accept one or
+//          the other encoding. If |password|'s encoding and the PDF's expected
+//          encoding do not match, FPDF_LoadDocument() will automatically
+//          convert |password| to the other encoding.
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password);
 
@@ -323,6 +366,9 @@
 //          The loaded document can be closed by FPDF_CloseDocument.
 //          If this function fails, you can use FPDF_GetLastError() to retrieve
 //          the reason why it failed.
+//
+//          See the comments for FPDF_LoadDocument() regarding the encoding for
+//          |password|.
 // Notes:
 //          If PDFium is built with the XFA module, the application should call
 //          FPDF_LoadXFA() function after the PDF document loaded to support XFA
@@ -337,6 +383,7 @@
 
   // A function pointer for getting a block of data from a specific position.
   // Position is specified by byte offset from the beginning of the file.
+  // The pointer to the buffer is never NULL and the size is never 0.
   // The position and size will never go out of range of the file length.
   // It may be possible for FPDFSDK to call this function multiple times for
   // the same position.
@@ -351,92 +398,102 @@
   void* m_Param;
 } FPDF_FILEACCESS;
 
-#ifdef PDF_ENABLE_XFA
-/**
- * @brief Structure for file reading or writing (I/O).
+/*
+ * Structure for file reading or writing (I/O).
  *
- * @note This is a handler and should be implemented by callers.
+ * Note: This is a handler and should be implemented by callers,
+ * and is only used from XFA.
  */
-typedef struct _FPDF_FILEHANDLER {
-  /**
-   * @brief User-defined data.
-   * @note Callers can use this field to track controls.
+typedef struct FPDF_FILEHANDLER_ {
+  /*
+   * User-defined data.
+   * Note: Callers can use this field to track controls.
    */
-  FPDF_LPVOID clientData;
-  /**
-   * @brief Callback function to release the current file stream object.
-   *
-   * @param[in] clientData    Pointer to user-defined data.
-   *
-   * @return None.
-   */
-  void (*Release)(FPDF_LPVOID clientData);
-  /**
-   * @brief Callback function to retrieve the current file stream size.
-   *
-   * @param[in] clientData    Pointer to user-defined data.
-   *
-   * @return Size of file stream.
-   */
-  FPDF_DWORD (*GetSize)(FPDF_LPVOID clientData);
-  /**
-   * @brief Callback function to read data from the current file stream.
-   *
-   * @param[in]   clientData  Pointer to user-defined data.
-   * @param[in]   offset      Offset position starts from the beginning of file
-   * stream. This parameter indicates reading position.
-   * @param[in]   buffer      Memory buffer to store data which are read from
-   * file stream. This parameter should not be <b>NULL</b>.
-   * @param[in]   size        Size of data which should be read from file
-   * stream, in bytes. The buffer indicated by the parameter <i>buffer</i>
-   * should be enough to store specified data.
-   *
-   * @return 0 for success, other value for failure.
-   */
-  FPDF_RESULT (*ReadBlock)(FPDF_LPVOID clientData,
-                           FPDF_DWORD offset,
-                           FPDF_LPVOID buffer,
-                           FPDF_DWORD size);
-  /**
-   * @brief   Callback function to write data into the current file stream.
-   *
-   * @param[in]   clientData  Pointer to user-defined data.
-   * @param[in]   offset      Offset position starts from the beginning of file
-   * stream. This parameter indicates writing position.
-   * @param[in]   buffer      Memory buffer contains data which is written into
-   * file stream. This parameter should not be <b>NULL</b>.
-   * @param[in]   size        Size of data which should be written into file
-   * stream, in bytes.
-   *
-   * @return 0 for success, other value for failure.
-   */
-  FPDF_RESULT (*WriteBlock)(FPDF_LPVOID clientData,
-                            FPDF_DWORD offset,
-                            FPDF_LPCVOID buffer,
-                            FPDF_DWORD size);
-  /**
-   * @brief   Callback function to flush all internal accessing buffers.
-   *
-   * @param[in]   clientData  Pointer to user-defined data.
-   *
-   * @return 0 for success, other value for failure.
-   */
-  FPDF_RESULT (*Flush)(FPDF_LPVOID clientData);
-  /**
-   * @brief   Callback function to change file size.
-   *
-   * @details This function is called under writing mode usually. Implementer
-   * can determine whether to realize it based on application requests.
-   *
-   * @param[in]   clientData  Pointer to user-defined data.
-   * @param[in]   size        New size of file stream, in bytes.
-   *
-   * @return 0 for success, other value for failure.
-   */
-  FPDF_RESULT (*Truncate)(FPDF_LPVOID clientData, FPDF_DWORD size);
-} FPDF_FILEHANDLER, *FPDF_LPFILEHANDLER;
+  void* clientData;
 
-#endif
+  /*
+   * Callback function to release the current file stream object.
+   *
+   * Parameters:
+   *       clientData   -  Pointer to user-defined data.
+   * Returns:
+   *       None.
+   */
+  void (*Release)(void* clientData);
+
+  /*
+   * Callback function to retrieve the current file stream size.
+   *
+   * Parameters:
+   *       clientData   -  Pointer to user-defined data.
+   * Returns:
+   *       Size of file stream.
+   */
+  FPDF_DWORD (*GetSize)(void* clientData);
+
+  /*
+   * Callback function to read data from the current file stream.
+   *
+   * Parameters:
+   *       clientData   -  Pointer to user-defined data.
+   *       offset       -  Offset position starts from the beginning of file
+   *                       stream. This parameter indicates reading position.
+   *       buffer       -  Memory buffer to store data which are read from
+   *                       file stream. This parameter should not be NULL.
+   *       size         -  Size of data which should be read from file stream,
+   *                       in bytes. The buffer indicated by |buffer| must be
+   *                       large enough to store specified data.
+   * Returns:
+   *       0 for success, other value for failure.
+   */
+  FPDF_RESULT (*ReadBlock)(void* clientData,
+                           FPDF_DWORD offset,
+                           void* buffer,
+                           FPDF_DWORD size);
+
+  /*
+   * Callback function to write data into the current file stream.
+   *
+   * Parameters:
+   *       clientData   -  Pointer to user-defined data.
+   *       offset       -  Offset position starts from the beginning of file
+   *                       stream. This parameter indicates writing position.
+   *       buffer       -  Memory buffer contains data which is written into
+   *                       file stream. This parameter should not be NULL.
+   *       size         -  Size of data which should be written into file
+   *                       stream, in bytes.
+   * Returns:
+   *       0 for success, other value for failure.
+   */
+  FPDF_RESULT (*WriteBlock)(void* clientData,
+                            FPDF_DWORD offset,
+                            const void* buffer,
+                            FPDF_DWORD size);
+  /*
+   * Callback function to flush all internal accessing buffers.
+   *
+   * Parameters:
+   *       clientData   -  Pointer to user-defined data.
+   * Returns:
+   *       0 for success, other value for failure.
+   */
+  FPDF_RESULT (*Flush)(void* clientData);
+
+  /*
+   * Callback function to change file size.
+   *
+   * Description:
+   *       This function is called under writing mode usually. Implementer
+   *       can determine whether to realize it based on application requests.
+   * Parameters:
+   *       clientData   -  Pointer to user-defined data.
+   *       size         -  New size of file stream, in bytes.
+   * Returns:
+   *       0 for success, other value for failure.
+   */
+  FPDF_RESULT (*Truncate)(void* clientData, FPDF_DWORD size);
+} FPDF_FILEHANDLER;
+
 // Function: FPDF_LoadCustomDocument
 //          Load PDF document from a custom access descriptor.
 // Parameters:
@@ -445,10 +502,14 @@
 // Return value:
 //          A handle to the loaded document, or NULL on failure.
 // Comments:
-//          The application must keep the file resources valid until the PDF
-//          document is closed.
+//          The application must keep the file resources |pFileAccess| points to
+//          valid until the returned FPDF_DOCUMENT is closed. |pFileAccess|
+//          itself does not need to outlive the FPDF_DOCUMENT.
 //
-//          The loaded document can be closed with FPDF_CloseDocument.
+//          The loaded document can be closed with FPDF_CloseDocument().
+//
+//          See the comments for FPDF_LoadDocument() regarding the encoding for
+//          |password|.
 // Notes:
 //          If PDFium is built with the XFA module, the application should call
 //          FPDF_LoadXFA() function after the PDF document loaded to support XFA
@@ -493,6 +554,21 @@
 //          function is not defined.
 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError();
 
+// Function: FPDF_DocumentHasValidCrossReferenceTable
+//          Whether the document's cross reference table is valid or not.
+//          Experimental API.
+// Parameters:
+//          document    -   Handle to a document. Returned by FPDF_LoadDocument.
+// Return value:
+//          True if the PDF parser did not encounter problems parsing the cross
+//          reference table. False if the parser could not parse the cross
+//          reference table and the table had to be rebuild from other data
+//          within the document.
+// Comments:
+//          The return value can change over time as the PDF parser evolves.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document);
+
 // Function: FPDF_GetDocPermission
 //          Get file permission flags of the document.
 // Parameters:
@@ -536,6 +612,16 @@
 FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDF_LoadPage(FPDF_DOCUMENT document,
                                                   int page_index);
 
+// Experimental API
+// Function: FPDF_GetPageWidthF
+//          Get page width.
+// Parameters:
+//          page        -   Handle to the page. Returned by FPDF_LoadPage().
+// Return value:
+//          Page width (excluding non-displayable area) measured in points.
+//          One point is 1/72 inch (around 0.3528 mm).
+FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageWidthF(FPDF_PAGE page);
+
 // Function: FPDF_GetPageWidth
 //          Get page width.
 // Parameters:
@@ -543,8 +629,21 @@
 // Return value:
 //          Page width (excluding non-displayable area) measured in points.
 //          One point is 1/72 inch (around 0.3528 mm).
+// Note:
+//          Prefer FPDF_GetPageWidthF() above. This will be deprecated in the
+//          future.
 FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageWidth(FPDF_PAGE page);
 
+// Experimental API
+// Function: FPDF_GetPageHeightF
+//          Get page height.
+// Parameters:
+//          page        -   Handle to the page. Returned by FPDF_LoadPage().
+// Return value:
+//          Page height (excluding non-displayable area) measured in points.
+//          One point is 1/72 inch (around 0.3528 mm)
+FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageHeightF(FPDF_PAGE page);
+
 // Function: FPDF_GetPageHeight
 //          Get page height.
 // Parameters:
@@ -552,6 +651,9 @@
 // Return value:
 //          Page height (excluding non-displayable area) measured in points.
 //          One point is 1/72 inch (around 0.3528 mm)
+// Note:
+//          Prefer FPDF_GetPageHeightF() above. This will be deprecated in the
+//          future.
 FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageHeight(FPDF_PAGE page);
 
 // Experimental API.
@@ -567,6 +669,21 @@
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetPageBoundingBox(FPDF_PAGE page,
                                                             FS_RECTF* rect);
 
+// Experimental API.
+// Function: FPDF_GetPageSizeByIndexF
+//          Get the size of the page at the given index.
+// Parameters:
+//          document    -   Handle to document. Returned by FPDF_LoadDocument().
+//          page_index  -   Page index, zero for the first page.
+//          size        -   Pointer to a FS_SIZEF to receive the page size.
+//                          (in points).
+// Return value:
+//          Non-zero for success. 0 for error (document or page not found).
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,
+                         int page_index,
+                         FS_SIZEF* size);
+
 // Function: FPDF_GetPageSizeByIndex
 //          Get the size of the page at the given index.
 // Parameters:
@@ -578,6 +695,9 @@
 //                          (in points).
 // Return value:
 //          Non-zero for success. 0 for error (document or page not found).
+// Note:
+//          Prefer FPDF_GetPageSizeByIndexF() above. This will be deprecated in
+//          the future.
 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,
                                                       int page_index,
                                                       double* width,
@@ -593,9 +713,9 @@
 #define FPDF_NO_NATIVETEXT 0x04
 // Grayscale output.
 #define FPDF_GRAYSCALE 0x08
-// Set if you want to get some debug info.
+// Obsolete, has no effect, retained for compatibility.
 #define FPDF_DEBUG_INFO 0x80
-// Set if you don't want to catch exceptions.
+// Obsolete, has no effect, retained for compatibility.
 #define FPDF_NO_CATCH 0x100
 // Limit image cache size.
 #define FPDF_RENDER_LIMITEDIMAGECACHE 0x200
@@ -749,7 +869,8 @@
 //          page_y      -   A pointer to a double receiving the converted Y
 //                          value in page coordinates.
 // Return value:
-//          None.
+//          Returns true if the conversion succeeds, and |page_x| and |page_y|
+//          successfully receives the converted coordinates.
 // Comments:
 //          The page coordinate system has its origin at the left-bottom corner
 //          of the page, with the X-axis on the bottom going to the right, and
@@ -767,16 +888,16 @@
 //          You must make sure the start_x, start_y, size_x, size_y
 //          and rotate parameters have exactly same values as you used in
 //          the FPDF_RenderPage() function call.
-FPDF_EXPORT void FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page,
-                                                 int start_x,
-                                                 int start_y,
-                                                 int size_x,
-                                                 int size_y,
-                                                 int rotate,
-                                                 int device_x,
-                                                 int device_y,
-                                                 double* page_x,
-                                                 double* page_y);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page,
+                                                      int start_x,
+                                                      int start_y,
+                                                      int size_x,
+                                                      int size_y,
+                                                      int rotate,
+                                                      int device_x,
+                                                      int device_y,
+                                                      double* page_x,
+                                                      double* page_y);
 
 // Function: FPDF_PageToDevice
 //          Convert the page coordinates of a point to screen coordinates.
@@ -800,19 +921,20 @@
 //          device_y    -   A pointer to an integer receiving the result Y
 //                          value in device coordinates.
 // Return value:
-//          None.
+//          Returns true if the conversion succeeds, and |device_x| and
+//          |device_y| successfully receives the converted coordinates.
 // Comments:
 //          See comments for FPDF_DeviceToPage().
-FPDF_EXPORT void FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page,
-                                                 int start_x,
-                                                 int start_y,
-                                                 int size_x,
-                                                 int size_y,
-                                                 int rotate,
-                                                 double page_x,
-                                                 double page_y,
-                                                 int* device_x,
-                                                 int* device_y);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page,
+                                                      int start_x,
+                                                      int start_y,
+                                                      int size_x,
+                                                      int size_y,
+                                                      int rotate,
+                                                      double page_x,
+                                                      double page_y,
+                                                      int* device_x,
+                                                      int* device_y);
 
 // Function: FPDFBitmap_Create
 //          Create a device independent bitmap (FXDIB).
@@ -842,7 +964,8 @@
 //
 //          This function allocates enough memory for holding all pixels in the
 //          bitmap, but it doesn't initialize the buffer. Applications can use
-//          FPDFBitmap_FillRect to fill the bitmap using any color.
+//          FPDFBitmap_FillRect() to fill the bitmap using any color. If the OS
+//          allows it, this function can allocate up to 4 GB of memory.
 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width,
                                                         int height,
                                                         int alpha);
@@ -1019,6 +1142,28 @@
 FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV
 FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document);
 
+// Function: FPDF_VIEWERREF_GetPrintPageRangeCount
+//          Returns the number of elements in a FPDF_PAGERANGE.
+//          Experimental API.
+// Parameters:
+//          pagerange   -   Handle to the page range.
+// Return value:
+//          The number of elements in the page range. Returns 0 on error.
+FPDF_EXPORT size_t FPDF_CALLCONV
+FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange);
+
+// Function: FPDF_VIEWERREF_GetPrintPageRangeElement
+//          Returns an element from a FPDF_PAGERANGE.
+//          Experimental API.
+// Parameters:
+//          pagerange   -   Handle to the page range.
+//          index       -   Index of the element.
+// Return value:
+//          The value of the element in the page range at a given index.
+//          Returns -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange, size_t index);
+
 // Function: FPDF_VIEWERREF_GetDuplex
 //          Returns the paper handling option to be used when printing from
 //          the print dialog.
@@ -1095,20 +1240,33 @@
                                                       void* buffer,
                                                       long* buflen);
 
+#ifdef PDF_ENABLE_V8
+// Function: FPDF_GetRecommendedV8Flags
+//          Returns a space-separated string of command line flags that are
+//          recommended to be passed into V8 via V8::SetFlagsFromString()
+//          prior to initializing the PDFium library.
+// Parameters:
+//          None.
+// Return value:
+//          NUL-terminated string of the form "--flag1 --flag2".
+//          The caller must not attempt to modify or free the result.
+FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags();
+#endif  // PDF_ENABLE_V8
+
 #ifdef PDF_ENABLE_XFA
 // Function: FPDF_BStr_Init
-//          Helper function to initialize a byte string.
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* str);
+//          Helper function to initialize a FPDF_BSTR.
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* bstr);
 
 // Function: FPDF_BStr_Set
-//          Helper function to set string data.
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* str,
-                                                    FPDF_LPCSTR bstr,
+//          Helper function to copy string data into the FPDF_BSTR.
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr,
+                                                    const char* cstr,
                                                     int length);
 
 // Function: FPDF_BStr_Clear
-//          Helper function to clear a byte string.
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* str);
+//          Helper function to clear a FPDF_BSTR.
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* bstr);
 #endif  // PDF_ENABLE_XFA
 
 #ifdef __cplusplus
diff --git a/samples/BUILD.gn b/samples/BUILD.gn
index 66b02f6..581d05a 100644
--- a/samples/BUILD.gn
+++ b/samples/BUILD.gn
@@ -7,9 +7,7 @@
 
 group("samples") {
   testonly = true
-  deps = [
-    ":pdfium_test",
-  ]
+  deps = [ ":pdfium_test" ]
 }
 
 config("pdfium_samples_config") {
@@ -20,14 +18,10 @@
     "PNG_USE_READ_MACROS",
   ]
   include_dirs = [ ".." ]
-  if (pdf_enable_v8) {
-    defines += [ "PDF_ENABLE_V8" ]
-  }
-  if (pdf_enable_xfa) {
-    defines += [ "PDF_ENABLE_XFA" ]
-  }
   if (pdf_use_skia) {
     defines += [ "PDF_ENABLE_SKIA" ]
+  } else if (pdf_use_skia_paths) {
+    defines += [ "PDF_ENABLE_SKIA_PATHS" ]
   }
   if (is_asan) {
     defines += [ "PDF_ENABLE_ASAN" ]
@@ -36,37 +30,39 @@
   if (enable_callgrind) {
     defines += [ "ENABLE_CALLGRIND" ]
   }
-
-  if (use_coverage && is_clang) {
-    cflags += [
-      "--coverage",
-      "-g",
-      "-O0",
-    ]
-    ldflags += [ "--coverage" ]
-  }
 }
 
 executable("pdfium_test") {
   testonly = true
   sources = [
     "pdfium_test.cc",
+    "pdfium_test_dump_helper.cc",
+    "pdfium_test_dump_helper.h",
+    "pdfium_test_event_helper.cc",
+    "pdfium_test_event_helper.h",
+    "pdfium_test_write_helper.cc",
+    "pdfium_test_write_helper.h",
   ]
+
+  # Note: One should write programs that depend on ../:pdfium. Whereas this
+  # sample program does not set a good example, and depends on PDFium internals,
+  # as well as test support code, for convenience.
   deps = [
-    "../:image_diff",
-    "../:pdfium",
-    "../:test_support",
-    "//build/config:exe_and_shlib_deps",
+    "../:pdfium_public_headers",
+    "../fpdfsdk",
+    "../testing/:test_support",
+    "../testing/image_diff",
+    "../third_party:pdfium_base",
     "//build/win:default_exe_manifest",
   ]
   configs += [ ":pdfium_samples_config" ]
 
   if (pdf_enable_v8) {
-    deps += [ "//v8:v8_libplatform" ]
-    include_dirs = [
-      "//v8",
-      "//v8/include",
+    deps += [
+      "//v8:v8_headers",
+      "//v8:v8_libplatform",
     ]
+    include_dirs = [ "//v8" ]
     configs += [ "//v8:external_startup_data" ]
   }
   if (pdf_use_skia || pdf_use_skia_paths) {
diff --git a/samples/DEPS b/samples/DEPS
index 26f9ee2..c77b78b 100644
--- a/samples/DEPS
+++ b/samples/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  '+core/fxcrt',
   '+public',
   '+third_party/skia/include',
   '+v8',
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index 1549bd7..dc539dd 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -7,20 +7,22 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <bitset>
 #include <iterator>
 #include <map>
 #include <memory>
 #include <sstream>
 #include <string>
-#include <utility>
 #include <vector>
 
 #if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_
 #define _SKIA_SUPPORT_
 #endif
 
-#include "public/cpp/fpdf_deleters.h"
+#if defined PDF_ENABLE_SKIA_PATHS && !defined _SKIA_SUPPORT_PATHS_
+#define _SKIA_SUPPORT_PATHS_
+#endif
+
+#include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_annot.h"
 #include "public/fpdf_attachment.h"
 #include "public/fpdf_dataavail.h"
@@ -31,9 +33,16 @@
 #include "public/fpdf_structtree.h"
 #include "public/fpdf_text.h"
 #include "public/fpdfview.h"
-#include "testing/image_diff/image_diff_png.h"
-#include "testing/test_support.h"
+#include "samples/pdfium_test_dump_helper.h"
+#include "samples/pdfium_test_event_helper.h"
+#include "samples/pdfium_test_write_helper.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/test_loader.h"
+#include "testing/utils/file_util.h"
+#include "testing/utils/hash.h"
+#include "testing/utils/path_service.h"
 #include "third_party/base/logging.h"
+#include "third_party/base/optional.h"
 
 #ifdef _WIN32
 #include <io.h>
@@ -46,23 +55,30 @@
 #endif  // ENABLE_CALLGRIND
 
 #ifdef PDF_ENABLE_V8
+#include "testing/v8_initializer.h"
 #include "v8/include/libplatform/libplatform.h"
 #include "v8/include/v8.h"
 #endif  // PDF_ENABLE_V8
 
-#ifdef PDF_ENABLE_SKIA
-#include "third_party/skia/include/core/SkPictureRecorder.h"
-#include "third_party/skia/include/core/SkStream.h"
-#endif
-
 #ifdef _WIN32
 #define access _access
 #define snprintf _snprintf
 #define R_OK 4
 #endif
 
+// wordexp is a POSIX function that is only available on OSX and non-Android
+// Linux platforms.
+#if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
+#define WORDEXP_AVAILABLE
+#endif
+
+#ifdef WORDEXP_AVAILABLE
+#include <wordexp.h>
+#endif  // WORDEXP_AVAILABLE
+
 enum OutputFormat {
   OUTPUT_NONE,
+  OUTPUT_PAGEINFO,
   OUTPUT_STRUCTURE,
   OUTPUT_TEXT,
   OUTPUT_PPM,
@@ -82,46 +98,115 @@
 namespace {
 
 struct Options {
-  Options()
-      : show_config(false),
-        show_metadata(false),
-        send_events(false),
-        render_oneshot(false),
-        save_attachments(false),
-        save_images(false),
-#ifdef ENABLE_CALLGRIND
-        callgrind_delimiters(false),
-#endif  // ENABLE_CALLGRIND
-        pages(false),
-        md5(false),
-        output_format(OUTPUT_NONE) {
-  }
+  Options() = default;
 
-  bool show_config;
-  bool show_metadata;
-  bool send_events;
-  bool render_oneshot;
-  bool save_attachments;
-  bool save_images;
+  bool show_config = false;
+  bool show_metadata = false;
+  bool send_events = false;
+  bool use_load_mem_document = false;
+  bool render_oneshot = false;
+  bool lcd_text = false;
+  bool no_nativetext = false;
+  bool grayscale = false;
+  bool limit_cache = false;
+  bool force_halftone = false;
+  bool printing = false;
+  bool no_smoothtext = false;
+  bool no_smoothimage = false;
+  bool no_smoothpath = false;
+  bool reverse_byte_order = false;
+  bool save_attachments = false;
+  bool save_images = false;
+  bool save_thumbnails = false;
+  bool save_thumbnails_decoded = false;
+  bool save_thumbnails_raw = false;
+#ifdef PDF_ENABLE_V8
+  bool disable_javascript = false;
+#ifdef PDF_ENABLE_XFA
+  bool disable_xfa = false;
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
+  bool pages = false;
+  bool md5 = false;
 #ifdef ENABLE_CALLGRIND
-  bool callgrind_delimiters;
-#endif  // ENABLE_CALLGRIND
-  bool pages;
-  bool md5;
-  OutputFormat output_format;
+  bool callgrind_delimiters = false;
+#endif
+#if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
+  bool linux_no_system_fonts = false;
+#endif
+  OutputFormat output_format = OUTPUT_NONE;
+  std::string password;
   std::string scale_factor_as_string;
   std::string exe_path;
   std::string bin_directory;
   std::string font_directory;
-  // 0-based page numbers to be rendered.
-  int first_page;
-  int last_page;
+  int first_page = 0;  // First 0-based page number to renderer.
+  int last_page = 0;   // Last 0-based page number to renderer.
+  time_t time = -1;
 };
 
-struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
+int PageRenderFlagsFromOptions(const Options& options) {
+  int flags = FPDF_ANNOT;
+  if (options.lcd_text)
+    flags |= FPDF_LCD_TEXT;
+  if (options.no_nativetext)
+    flags |= FPDF_NO_NATIVETEXT;
+  if (options.grayscale)
+    flags |= FPDF_GRAYSCALE;
+  if (options.limit_cache)
+    flags |= FPDF_RENDER_LIMITEDIMAGECACHE;
+  if (options.force_halftone)
+    flags |= FPDF_RENDER_FORCEHALFTONE;
+  if (options.printing)
+    flags |= FPDF_PRINTING;
+  if (options.no_smoothtext)
+    flags |= FPDF_RENDER_NO_SMOOTHTEXT;
+  if (options.no_smoothimage)
+    flags |= FPDF_RENDER_NO_SMOOTHIMAGE;
+  if (options.no_smoothpath)
+    flags |= FPDF_RENDER_NO_SMOOTHPATH;
+  if (options.reverse_byte_order)
+    flags |= FPDF_REVERSE_BYTE_ORDER;
+  return flags;
+}
+
+Optional<std::string> ExpandDirectoryPath(const std::string& path) {
+#if defined(WORDEXP_AVAILABLE)
+  wordexp_t expansion;
+  if (wordexp(path.c_str(), &expansion, 0) != 0 || expansion.we_wordc < 1) {
+    wordfree(&expansion);
+    return {};
+  }
+  // Need to contruct the return value before hand, since wordfree will
+  // deallocate |expansion|.
+  Optional<std::string> ret_val = {expansion.we_wordv[0]};
+  wordfree(&expansion);
+  return ret_val;
+#else
+  return {path};
+#endif  // WORDEXP_AVAILABLE
+}
+
+Optional<const char*> GetCustomFontPath(const Options& options) {
+#if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
+  // Set custom font path to an empty path. This avoids the fallback to default
+  // font paths.
+  if (options.linux_no_system_fonts)
+    return nullptr;
+#endif
+
+  // No custom font path. Use default.
+  if (options.font_directory.empty())
+    return pdfium::nullopt;
+
+  // Set custom font path to |options.font_directory|.
+  return options.font_directory.c_str();
+}
+
+struct FPDF_FORMFILLINFO_PDFiumTest final : public FPDF_FORMFILLINFO {
   // Hold a map of the currently loaded pages in order to avoid them
   // to get loaded twice.
-  std::map<int, std::unique_ptr<void, FPDFPageDeleter>> loaded_pages;
+  std::map<int, ScopedFPDFPage> loaded_pages;
 
   // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
   // make use of it.
@@ -133,487 +218,13 @@
   return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
 }
 
-bool CheckDimensions(int stride, int width, int height) {
-  if (stride < 0 || width < 0 || height < 0)
-    return false;
-  if (height > 0 && width > INT_MAX / height)
-    return false;
-  return true;
-}
-
-void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
+void OutputMD5Hash(const char* file_name, const uint8_t* buffer, int len) {
   // Get the MD5 hash and write it to stdout.
-  std::string hash =
-      GenerateMD5Base16(reinterpret_cast<const uint8_t*>(buffer), len);
+  std::string hash = GenerateMD5Base16(buffer, len);
   printf("MD5:%s:%s\n", file_name, hash.c_str());
 }
 
-std::string WritePpm(const char* pdf_name,
-                     int num,
-                     const void* buffer_void,
-                     int stride,
-                     int width,
-                     int height) {
-  const auto* buffer = reinterpret_cast<const char*>(buffer_void);
-
-  if (!CheckDimensions(stride, width, height))
-    return "";
-
-  int out_len = width * height;
-  if (out_len > INT_MAX / 3)
-    return "";
-  out_len *= 3;
-
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
-  FILE* fp = fopen(filename, "wb");
-  if (!fp)
-    return "";
-  fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
-  // Source data is B, G, R, unused.
-  // Dest data is R, G, B.
-  std::vector<char> result(out_len);
-  for (int h = 0; h < height; ++h) {
-    const char* src_line = buffer + (stride * h);
-    char* dest_line = result.data() + (width * h * 3);
-    for (int w = 0; w < width; ++w) {
-      // R
-      dest_line[w * 3] = src_line[(w * 4) + 2];
-      // G
-      dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
-      // B
-      dest_line[(w * 3) + 2] = src_line[w * 4];
-    }
-  }
-  if (fwrite(result.data(), out_len, 1, fp) != 1)
-    fprintf(stderr, "Failed to write to %s\n", filename);
-  fclose(fp);
-  return std::string(filename);
-}
-
-void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
-  char filename[256];
-  int chars_formatted =
-      snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return;
-  }
-
-  FILE* fp = fopen(filename, "w");
-  if (!fp) {
-    fprintf(stderr, "Failed to open %s for output\n", filename);
-    return;
-  }
-
-  // Output in UTF32-LE.
-  uint32_t bom = 0x0000FEFF;
-  if (fwrite(&bom, sizeof(bom), 1, fp) != 1) {
-    fprintf(stderr, "Failed to write to %s\n", filename);
-    (void)fclose(fp);
-    return;
-  }
-
-  std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page));
-  for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
-    uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
-    if (fwrite(&c, sizeof(c), 1, fp) != 1) {
-      fprintf(stderr, "Failed to write to %s\n", filename);
-      break;
-    }
-  }
-  (void)fclose(fp);
-}
-
-const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
-  if (subtype == FPDF_ANNOT_TEXT)
-    return "Text";
-  if (subtype == FPDF_ANNOT_LINK)
-    return "Link";
-  if (subtype == FPDF_ANNOT_FREETEXT)
-    return "FreeText";
-  if (subtype == FPDF_ANNOT_LINE)
-    return "Line";
-  if (subtype == FPDF_ANNOT_SQUARE)
-    return "Square";
-  if (subtype == FPDF_ANNOT_CIRCLE)
-    return "Circle";
-  if (subtype == FPDF_ANNOT_POLYGON)
-    return "Polygon";
-  if (subtype == FPDF_ANNOT_POLYLINE)
-    return "PolyLine";
-  if (subtype == FPDF_ANNOT_HIGHLIGHT)
-    return "Highlight";
-  if (subtype == FPDF_ANNOT_UNDERLINE)
-    return "Underline";
-  if (subtype == FPDF_ANNOT_SQUIGGLY)
-    return "Squiggly";
-  if (subtype == FPDF_ANNOT_STRIKEOUT)
-    return "StrikeOut";
-  if (subtype == FPDF_ANNOT_STAMP)
-    return "Stamp";
-  if (subtype == FPDF_ANNOT_CARET)
-    return "Caret";
-  if (subtype == FPDF_ANNOT_INK)
-    return "Ink";
-  if (subtype == FPDF_ANNOT_POPUP)
-    return "Popup";
-  if (subtype == FPDF_ANNOT_FILEATTACHMENT)
-    return "FileAttachment";
-  if (subtype == FPDF_ANNOT_SOUND)
-    return "Sound";
-  if (subtype == FPDF_ANNOT_MOVIE)
-    return "Movie";
-  if (subtype == FPDF_ANNOT_WIDGET)
-    return "Widget";
-  if (subtype == FPDF_ANNOT_SCREEN)
-    return "Screen";
-  if (subtype == FPDF_ANNOT_PRINTERMARK)
-    return "PrinterMark";
-  if (subtype == FPDF_ANNOT_TRAPNET)
-    return "TrapNet";
-  if (subtype == FPDF_ANNOT_WATERMARK)
-    return "Watermark";
-  if (subtype == FPDF_ANNOT_THREED)
-    return "3D";
-  if (subtype == FPDF_ANNOT_RICHMEDIA)
-    return "RichMedia";
-  if (subtype == FPDF_ANNOT_XFAWIDGET)
-    return "XFAWidget";
-  NOTREACHED();
-  return "";
-}
-
-void AppendFlagString(const char* flag, std::string* output) {
-  if (!output->empty())
-    *output += ", ";
-  *output += flag;
-}
-
-std::string AnnotFlagsToString(int flags) {
-  std::string str;
-  if (flags & FPDF_ANNOT_FLAG_INVISIBLE)
-    AppendFlagString("Invisible", &str);
-  if (flags & FPDF_ANNOT_FLAG_HIDDEN)
-    AppendFlagString("Hidden", &str);
-  if (flags & FPDF_ANNOT_FLAG_PRINT)
-    AppendFlagString("Print", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOZOOM)
-    AppendFlagString("NoZoom", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOROTATE)
-    AppendFlagString("NoRotate", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOVIEW)
-    AppendFlagString("NoView", &str);
-  if (flags & FPDF_ANNOT_FLAG_READONLY)
-    AppendFlagString("ReadOnly", &str);
-  if (flags & FPDF_ANNOT_FLAG_LOCKED)
-    AppendFlagString("Locked", &str);
-  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW)
-    AppendFlagString("ToggleNoView", &str);
-  return str;
-}
-
-const char* PageObjectTypeToCString(int type) {
-  if (type == FPDF_PAGEOBJ_TEXT)
-    return "Text";
-  if (type == FPDF_PAGEOBJ_PATH)
-    return "Path";
-  if (type == FPDF_PAGEOBJ_IMAGE)
-    return "Image";
-  if (type == FPDF_PAGEOBJ_SHADING)
-    return "Shading";
-  if (type == FPDF_PAGEOBJ_FORM)
-    return "Form";
-  NOTREACHED();
-  return "";
-}
-
-void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
-  // Open the output text file.
-  char filename[256];
-  int chars_formatted =
-      snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num);
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return;
-  }
-  FILE* fp = fopen(filename, "w");
-  if (!fp) {
-    fprintf(stderr, "Failed to open %s for output\n", filename);
-    return;
-  }
-
-  int annot_count = FPDFPage_GetAnnotCount(page);
-  fprintf(fp, "Number of annotations: %d\n\n", annot_count);
-
-  // Iterate through all annotations on this page.
-  for (int i = 0; i < annot_count; ++i) {
-    // Retrieve the annotation object and its subtype.
-    fprintf(fp, "Annotation #%d:\n", i + 1);
-    FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, i);
-    if (!annot) {
-      fprintf(fp, "Failed to retrieve annotation!\n\n");
-      continue;
-    }
-    FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
-    fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype));
-
-    // Retrieve the annotation flags.
-    fprintf(fp, "Flags set: %s\n",
-            AnnotFlagsToString(FPDFAnnot_GetFlags(annot)).c_str());
-
-    // Retrieve the annotation's object count and object types.
-    const int obj_count = FPDFAnnot_GetObjectCount(annot);
-    fprintf(fp, "Number of objects: %d\n", obj_count);
-    if (obj_count > 0) {
-      fprintf(fp, "Object types: ");
-      for (int j = 0; j < obj_count; ++j) {
-        const char* type = PageObjectTypeToCString(
-            FPDFPageObj_GetType(FPDFAnnot_GetObject(annot, j)));
-        fprintf(fp, "%s  ", type);
-      }
-      fprintf(fp, "\n");
-    }
-
-    // Retrieve the annotation's color and interior color.
-    unsigned int R;
-    unsigned int G;
-    unsigned int B;
-    unsigned int A;
-    if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A)) {
-      fprintf(fp, "Failed to retrieve color.\n");
-    } else {
-      fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
-    }
-    if (!FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_InteriorColor, &R, &G,
-                            &B, &A)) {
-      fprintf(fp, "Failed to retrieve interior color.\n");
-    } else {
-      fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
-    }
-
-    // Retrieve the annotation's contents and author.
-    static constexpr char kContentsKey[] = "Contents";
-    static constexpr char kAuthorKey[] = "T";
-    unsigned long len =
-        FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0);
-    std::vector<char> buf(len);
-    FPDFAnnot_GetStringValue(annot, kContentsKey, buf.data(), len);
-    fprintf(fp, "Content: %ls\n",
-            GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                .c_str());
-    len = FPDFAnnot_GetStringValue(annot, kAuthorKey, nullptr, 0);
-    buf.clear();
-    buf.resize(len);
-    FPDFAnnot_GetStringValue(annot, kAuthorKey, buf.data(), len);
-    fprintf(fp, "Author: %ls\n",
-            GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
-                .c_str());
-
-    // Retrieve the annotation's quadpoints if it is a markup annotation.
-    if (FPDFAnnot_HasAttachmentPoints(annot)) {
-      FS_QUADPOINTSF quadpoints;
-      if (FPDFAnnot_GetAttachmentPoints(annot, &quadpoints)) {
-        fprintf(fp,
-                "Quadpoints: (%.3f, %.3f), (%.3f, %.3f), (%.3f, %.3f), (%.3f, "
-                "%.3f)\n",
-                quadpoints.x1, quadpoints.y1, quadpoints.x2, quadpoints.y2,
-                quadpoints.x3, quadpoints.y3, quadpoints.x4, quadpoints.y4);
-      } else {
-        fprintf(fp, "Failed to retrieve quadpoints.\n");
-      }
-    }
-
-    // Retrieve the annotation's rectangle coordinates.
-    FS_RECTF rect;
-    if (FPDFAnnot_GetRect(annot, &rect)) {
-      fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n",
-              rect.left, rect.bottom, rect.right, rect.top);
-    } else {
-      fprintf(fp, "Failed to retrieve annotation rectangle.\n");
-    }
-
-    FPDFPage_CloseAnnot(annot);
-  }
-
-  (void)fclose(fp);
-}
-
-std::string WritePng(const char* pdf_name,
-                     int num,
-                     const void* buffer_void,
-                     int stride,
-                     int width,
-                     int height) {
-  if (!CheckDimensions(stride, width, height))
-    return "";
-
-  std::vector<unsigned char> png_encoding;
-  const auto* buffer = static_cast<const unsigned char*>(buffer_void);
-  if (!image_diff_png::EncodeBGRAPNG(
-          buffer, width, height, stride, false, &png_encoding)) {
-    fprintf(stderr, "Failed to convert bitmap to PNG\n");
-    return "";
-  }
-
-  char filename[256];
-  int chars_formatted = snprintf(
-      filename, sizeof(filename), "%s.%d.png", pdf_name, num);
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return "";
-  }
-
-  FILE* fp = fopen(filename, "wb");
-  if (!fp) {
-    fprintf(stderr, "Failed to open %s for output\n", filename);
-    return "";
-  }
-
-  size_t bytes_written = fwrite(
-      &png_encoding.front(), 1, png_encoding.size(), fp);
-  if (bytes_written != png_encoding.size())
-    fprintf(stderr, "Failed to write to %s\n", filename);
-
-  (void)fclose(fp);
-  return std::string(filename);
-}
-
-#ifdef _WIN32
-std::string WriteBmp(const char* pdf_name,
-                     int num,
-                     const void* buffer,
-                     int stride,
-                     int width,
-                     int height) {
-  if (!CheckDimensions(stride, width, height))
-    return "";
-
-  int out_len = stride * height;
-  if (out_len > INT_MAX / 3)
-    return "";
-
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
-  FILE* fp = fopen(filename, "wb");
-  if (!fp)
-    return "";
-
-  BITMAPINFO bmi = {};
-  bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
-  bmi.bmiHeader.biWidth = width;
-  bmi.bmiHeader.biHeight = -height;  // top-down image
-  bmi.bmiHeader.biPlanes = 1;
-  bmi.bmiHeader.biBitCount = 32;
-  bmi.bmiHeader.biCompression = BI_RGB;
-  bmi.bmiHeader.biSizeImage = 0;
-
-  BITMAPFILEHEADER file_header = {};
-  file_header.bfType = 0x4d42;
-  file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
-  file_header.bfOffBits = file_header.bfSize - out_len;
-
-  if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 ||
-      fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 ||
-      fwrite(buffer, out_len, 1, fp) != 1) {
-    fprintf(stderr, "Failed to write to %s\n", filename);
-  }
-  fclose(fp);
-  return std::string(filename);
-}
-
-void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
-
-  HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
-
-  int width = static_cast<int>(FPDF_GetPageWidth(page));
-  int height = static_cast<int>(FPDF_GetPageHeight(page));
-  HRGN rgn = CreateRectRgn(0, 0, width, height);
-  SelectClipRgn(dc, rgn);
-  DeleteObject(rgn);
-
-  SelectObject(dc, GetStockObject(NULL_PEN));
-  SelectObject(dc, GetStockObject(WHITE_BRUSH));
-  // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
-  Rectangle(dc, 0, 0, width + 1, height + 1);
-
-  FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
-                  FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
-
-  DeleteEnhMetaFile(CloseEnhMetaFile(dc));
-}
-
-int CALLBACK EnhMetaFileProc(HDC hdc,
-                             HANDLETABLE* handle_table,
-                             const ENHMETARECORD* record,
-                             int objects_count,
-                             LPARAM param) {
-  std::vector<const ENHMETARECORD*>& items =
-      *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
-  items.push_back(record);
-  return 1;
-}
-
-void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
-  char filename[256];
-  snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num);
-  FILE* fp = fopen(filename, "wb");
-  if (!fp)
-    return;
-
-  HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
-
-  int width = static_cast<int>(FPDF_GetPageWidth(page));
-  int height = static_cast<int>(FPDF_GetPageHeight(page));
-  FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
-                  FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
-
-  HENHMETAFILE emf = CloseEnhMetaFile(dc);
-  std::vector<const ENHMETARECORD*> items;
-  EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
-  for (const ENHMETARECORD* record : items) {
-    if (record->iType != EMR_GDICOMMENT)
-      continue;
-
-    const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
-    const char* data = reinterpret_cast<const char*>(comment->Data);
-    uint16_t size = *reinterpret_cast<const uint16_t*>(data);
-    if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) {
-      fprintf(stderr, "Failed to write to %s\n", filename);
-      break;
-    }
-  }
-  fclose(fp);
-  DeleteEnhMetaFile(emf);
-}
-#endif  // _WIN32
-
-#ifdef PDF_ENABLE_SKIA
-std::string WriteSkp(const char* pdf_name,
-                     int num,
-                     SkPictureRecorder* recorder) {
-  char filename[256];
-  int chars_formatted =
-      snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
-
-  if (chars_formatted < 0 ||
-      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-    fprintf(stderr, "Filename %s is too long\n", filename);
-    return "";
-  }
-
-  sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
-  SkFILEWStream wStream(filename);
-  picture->serialize(&wStream);
-  return std::string(filename);
-}
-#endif
-
+#ifdef PDF_ENABLE_V8
 // These example JS platform callback handlers are entirely optional,
 // and exist here to show the flow of information from a document back
 // to the embedder.
@@ -629,6 +240,10 @@
   return 0;
 }
 
+void ExampleAppBeep(IPDF_JSPLATFORM*, int type) {
+  printf("BEEP!!! %d\n", type);
+}
+
 int ExampleAppResponse(IPDF_JSPLATFORM*,
                        FPDF_WIDESTRING question,
                        FPDF_WIDESTRING title,
@@ -652,8 +267,12 @@
   return 4;
 }
 
-void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
-  printf("Goto Page: %d\n", page_number);
+int ExampleDocGetFilePath(IPDF_JSPLATFORM*, void* file_path, int length) {
+  static const char kPath[] = "myfile.pdf";
+  constexpr int kRequired = static_cast<int>(sizeof(kPath));
+  if (file_path && length >= kRequired)
+    memcpy(file_path, kPath, kRequired);
+  return kRequired;
 }
 
 void ExampleDocMail(IPDF_JSPLATFORM*,
@@ -671,6 +290,44 @@
          GetPlatformWString(Msg).c_str());
 }
 
+void ExampleDocPrint(IPDF_JSPLATFORM*,
+                     FPDF_BOOL bUI,
+                     int nStart,
+                     int nEnd,
+                     FPDF_BOOL bSilent,
+                     FPDF_BOOL bShrinkToFit,
+                     FPDF_BOOL bPrintAsImage,
+                     FPDF_BOOL bReverse,
+                     FPDF_BOOL bAnnotations) {
+  printf("Doc Print: %d, %d, %d, %d, %d, %d, %d, %d\n", bUI, nStart, nEnd,
+         bSilent, bShrinkToFit, bPrintAsImage, bReverse, bAnnotations);
+}
+
+void ExampleDocSubmitForm(IPDF_JSPLATFORM*,
+                          void* formData,
+                          int length,
+                          FPDF_WIDESTRING url) {
+  printf("Doc Submit Form: url=%ls + %d data bytes:\n",
+         GetPlatformWString(url).c_str(), length);
+  uint8_t* ptr = reinterpret_cast<uint8_t*>(formData);
+  for (int i = 0; i < length; ++i)
+    printf(" %02x", ptr[i]);
+  printf("\n");
+}
+
+void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
+  printf("Goto Page: %d\n", page_number);
+}
+
+int ExampleFieldBrowse(IPDF_JSPLATFORM*, void* file_path, int length) {
+  static const char kPath[] = "selected.txt";
+  constexpr int kRequired = static_cast<int>(sizeof(kPath));
+  if (file_path && length >= kRequired)
+    memcpy(file_path, kPath, kRequired);
+  return kRequired;
+}
+#endif  // PDF_ENABLE_V8
+
 void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
   std::string feature = "Unknown";
   switch (type) {
@@ -715,6 +372,17 @@
   printf("Unsupported feature: %s.\n", feature.c_str());
 }
 
+// |arg| is expected to be "--key=value", and |key| is "--key=".
+bool ParseSwitchKeyValue(const std::string& arg,
+                         const std::string& key,
+                         std::string* value) {
+  if (arg.size() <= key.size() || arg.compare(0, key.size(), key) != 0)
+    return false;
+
+  *value = arg.substr(key.size());
+  return true;
+}
+
 bool ParseCommandLine(const std::vector<std::string>& args,
                       Options* options,
                       std::vector<std::string>* files) {
@@ -723,6 +391,7 @@
 
   options->exe_path = args[0];
   size_t cur_idx = 1;
+  std::string value;
   for (; cur_idx < args.size(); ++cur_idx) {
     const std::string& cur_arg = args[cur_idx];
     if (cur_arg == "--show-config") {
@@ -731,16 +400,56 @@
       options->show_metadata = true;
     } else if (cur_arg == "--send-events") {
       options->send_events = true;
+    } else if (cur_arg == "--mem-document") {
+      options->use_load_mem_document = true;
     } else if (cur_arg == "--render-oneshot") {
       options->render_oneshot = true;
+    } else if (cur_arg == "--lcd-text") {
+      options->lcd_text = true;
+    } else if (cur_arg == "--no-nativetext") {
+      options->no_nativetext = true;
+    } else if (cur_arg == "--grayscale") {
+      options->grayscale = true;
+    } else if (cur_arg == "--limit-cache") {
+      options->limit_cache = true;
+    } else if (cur_arg == "--force-halftone") {
+      options->force_halftone = true;
+    } else if (cur_arg == "--printing") {
+      options->printing = true;
+    } else if (cur_arg == "--no-smoothtext") {
+      options->no_smoothtext = true;
+    } else if (cur_arg == "--no-smoothimage") {
+      options->no_smoothimage = true;
+    } else if (cur_arg == "--no-smoothpath") {
+      options->no_smoothpath = true;
+    } else if (cur_arg == "--reverse-byte-order") {
+      options->reverse_byte_order = true;
     } else if (cur_arg == "--save-attachments") {
       options->save_attachments = true;
     } else if (cur_arg == "--save-images") {
       options->save_images = true;
+    } else if (cur_arg == "--save-thumbs") {
+      options->save_thumbnails = true;
+    } else if (cur_arg == "--save-thumbs-dec") {
+      options->save_thumbnails_decoded = true;
+    } else if (cur_arg == "--save-thumbs-raw") {
+      options->save_thumbnails_raw = true;
+#ifdef PDF_ENABLE_V8
+    } else if (cur_arg == "--disable-javascript") {
+      options->disable_javascript = true;
+#ifdef PDF_ENABLE_XFA
+    } else if (cur_arg == "--disable-xfa") {
+      options->disable_xfa = true;
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 #ifdef ENABLE_CALLGRIND
     } else if (cur_arg == "--callgrind-delim") {
       options->callgrind_delimiters = true;
-#endif  // ENABLE_CALLGRIND
+#endif
+#if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
+    } else if (cur_arg == "--no-system-fonts") {
+      options->linux_no_system_fonts = true;
+#endif
     } else if (cur_arg == "--ppm") {
       if (options->output_format != OUTPUT_NONE) {
         fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
@@ -772,14 +481,27 @@
         return false;
       }
       options->output_format = OUTPUT_SKP;
-#endif
-    } else if (cur_arg.size() > 11 &&
-               cur_arg.compare(0, 11, "--font-dir=") == 0) {
+#endif  // PDF_ENABLE_SKIA
+    } else if (ParseSwitchKeyValue(cur_arg, "--font-dir=", &value)) {
       if (!options->font_directory.empty()) {
         fprintf(stderr, "Duplicate --font-dir argument\n");
         return false;
       }
-      options->font_directory = cur_arg.substr(11);
+      std::string path = value;
+      auto expanded_path = ExpandDirectoryPath(path);
+      if (!expanded_path) {
+        fprintf(stderr, "Failed to expand --font-dir, %s\n", path.c_str());
+        return false;
+      }
+
+      if (!PathService::DirectoryExists(expanded_path.value())) {
+        fprintf(stderr, "--font-dir, %s, appears to not be a directory\n",
+                path.c_str());
+        return false;
+      }
+
+      options->font_directory = expanded_path.value();
+
 #ifdef _WIN32
     } else if (cur_arg == "--emf") {
       if (options->output_format != OUTPUT_NONE) {
@@ -809,35 +531,52 @@
 
 #ifdef PDF_ENABLE_V8
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-    } else if (cur_arg.size() > 10 &&
-               cur_arg.compare(0, 10, "--bin-dir=") == 0) {
+    } else if (ParseSwitchKeyValue(cur_arg, "--bin-dir=", &value)) {
       if (!options->bin_directory.empty()) {
         fprintf(stderr, "Duplicate --bin-dir argument\n");
         return false;
       }
-      options->bin_directory = cur_arg.substr(10);
+      std::string path = value;
+      auto expanded_path = ExpandDirectoryPath(path);
+      if (!expanded_path) {
+        fprintf(stderr, "Failed to expand --bin-dir, %s\n", path.c_str());
+        return false;
+      }
+      options->bin_directory = expanded_path.value();
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 #endif  // PDF_ENABLE_V8
 
-    } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
+    } else if (ParseSwitchKeyValue(cur_arg, "--password=", &value)) {
+      if (!options->password.empty()) {
+        fprintf(stderr, "Duplicate --password argument\n");
+        return false;
+      }
+      options->password = value;
+    } else if (ParseSwitchKeyValue(cur_arg, "--scale=", &value)) {
       if (!options->scale_factor_as_string.empty()) {
         fprintf(stderr, "Duplicate --scale argument\n");
         return false;
       }
-      options->scale_factor_as_string = cur_arg.substr(8);
+      options->scale_factor_as_string = value;
+    } else if (cur_arg == "--show-pageinfo") {
+      if (options->output_format != OUTPUT_NONE) {
+        fprintf(stderr, "Duplicate or conflicting --show-pageinfo argument\n");
+        return false;
+      }
+      options->output_format = OUTPUT_PAGEINFO;
     } else if (cur_arg == "--show-structure") {
       if (options->output_format != OUTPUT_NONE) {
         fprintf(stderr, "Duplicate or conflicting --show-structure argument\n");
         return false;
       }
       options->output_format = OUTPUT_STRUCTURE;
-    } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
+    } else if (ParseSwitchKeyValue(cur_arg, "--pages=", &value)) {
       if (options->pages) {
         fprintf(stderr, "Duplicate --pages argument\n");
         return false;
       }
       options->pages = true;
-      const std::string pages_string = cur_arg.substr(8);
+      const std::string pages_string = value;
       size_t first_dash = pages_string.find("-");
       if (first_dash == std::string::npos) {
         std::stringstream(pages_string) >> options->first_page;
@@ -850,6 +589,17 @@
       }
     } else if (cur_arg == "--md5") {
       options->md5 = true;
+    } else if (ParseSwitchKeyValue(cur_arg, "--time=", &value)) {
+      if (options->time > -1) {
+        fprintf(stderr, "Duplicate --time argument\n");
+        return false;
+      }
+      const std::string time_string = value;
+      std::stringstream(time_string) >> options->time;
+      if (options->time < 0) {
+        fprintf(stderr, "Invalid --time argument, must be non-negative\n");
+        return false;
+      }
     } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
       fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
       return false;
@@ -892,7 +642,6 @@
       fprintf(stderr, "Unknown error %ld", err);
   }
   fprintf(stderr, ".\n");
-  return;
 }
 
 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
@@ -901,82 +650,6 @@
 
 void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
 
-void SendPageEvents(FPDF_FORMHANDLE form,
-                    FPDF_PAGE page,
-                    const std::string& events) {
-  auto lines = StringSplit(events, '\n');
-  for (auto line : lines) {
-    auto command = StringSplit(line, '#');
-    if (command[0].empty())
-      continue;
-    auto tokens = StringSplit(command[0], ',');
-    if (tokens[0] == "charcode") {
-      if (tokens.size() == 2) {
-        int keycode = atoi(tokens[1].c_str());
-        FORM_OnChar(form, page, keycode, 0);
-      } else {
-        fprintf(stderr, "charcode: bad args\n");
-      }
-    } else if (tokens[0] == "keycode") {
-      if (tokens.size() == 2) {
-        int keycode = atoi(tokens[1].c_str());
-        FORM_OnKeyDown(form, page, keycode, 0);
-        FORM_OnKeyUp(form, page, keycode, 0);
-      } else {
-        fprintf(stderr, "keycode: bad args\n");
-      }
-    } else if (tokens[0] == "mousedown") {
-      if (tokens.size() == 4) {
-        int x = atoi(tokens[2].c_str());
-        int y = atoi(tokens[3].c_str());
-        if (tokens[1] == "left")
-          FORM_OnLButtonDown(form, page, 0, x, y);
-#ifdef PDF_ENABLE_XFA
-        else if (tokens[1] == "right")
-          FORM_OnRButtonDown(form, page, 0, x, y);
-#endif
-        else
-          fprintf(stderr, "mousedown: bad button name\n");
-      } else {
-        fprintf(stderr, "mousedown: bad args\n");
-      }
-    } else if (tokens[0] == "mouseup") {
-      if (tokens.size() == 4) {
-        int x = atoi(tokens[2].c_str());
-        int y = atoi(tokens[3].c_str());
-        if (tokens[1] == "left")
-          FORM_OnLButtonUp(form, page, 0, x, y);
-#ifdef PDF_ENABLE_XFA
-        else if (tokens[1] == "right")
-          FORM_OnRButtonUp(form, page, 0, x, y);
-#endif
-        else
-          fprintf(stderr, "mouseup: bad button name\n");
-      } else {
-        fprintf(stderr, "mouseup: bad args\n");
-      }
-    } else if (tokens[0] == "mousemove") {
-      if (tokens.size() == 3) {
-        int x = atoi(tokens[1].c_str());
-        int y = atoi(tokens[2].c_str());
-        FORM_OnMouseMove(form, page, 0, x, y);
-      } else {
-        fprintf(stderr, "mousemove: bad args\n");
-      }
-    } else if (tokens[0] == "focus") {
-      if (tokens.size() == 3) {
-        int x = atoi(tokens[1].c_str());
-        int y = atoi(tokens[2].c_str());
-        FORM_OnFocus(form, page, 0, x, y);
-      } else {
-        fprintf(stderr, "focus: bad args\n");
-      }
-    } else {
-      fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
-    }
-  }
-}
-
 FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
                           FPDF_DOCUMENT doc,
                           int index) {
@@ -987,234 +660,18 @@
   if (iter != loaded_pages.end())
     return iter->second.get();
 
-  FPDF_PAGE page = FPDF_LoadPage(doc, index);
+  ScopedFPDFPage page(FPDF_LoadPage(doc, index));
   if (!page)
     return nullptr;
 
+  // Mark the page as loaded first to prevent infinite recursion.
+  FPDF_PAGE page_ptr = page.get();
+  loaded_pages[index] = std::move(page);
+
   FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
-  FORM_OnAfterLoadPage(page, form_handle);
-  FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
-  loaded_pages[index].reset(page);
-  return page;
-}
-
-std::wstring ConvertToWString(const unsigned short* buf,
-                              unsigned long buf_size) {
-  std::wstring result;
-  result.reserve(buf_size);
-  std::copy(buf, buf + buf_size, std::back_inserter(result));
-  return result;
-}
-
-void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
-  static const size_t kBufSize = 1024;
-  unsigned short buf[kBufSize];
-  unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
-  printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str());
-
-  memset(buf, 0, sizeof(buf));
-  len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
-  if (len > 0)
-    printf(": '%ls'", ConvertToWString(buf, len).c_str());
-
-  memset(buf, 0, sizeof(buf));
-  len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
-  if (len > 0)
-    printf(" (%ls)", ConvertToWString(buf, len).c_str());
-  printf("\n");
-
-  for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
-    FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
-    // If the child is not an Element then this will return null. This can
-    // happen if the element is things like an object reference or a stream.
-    if (!sub_child)
-      continue;
-
-    DumpChildStructure(sub_child, indent + 1);
-  }
-}
-
-void DumpPageStructure(FPDF_PAGE page, const int page_idx) {
-  std::unique_ptr<void, FPDFStructTreeDeleter> tree(
-      FPDF_StructTree_GetForPage(page));
-  if (!tree) {
-    fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
-    return;
-  }
-
-  printf("Structure Tree for Page %d\n", page_idx);
-  for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
-    FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
-    if (!child) {
-      fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
-      continue;
-    }
-    DumpChildStructure(child, 0);
-  }
-  printf("\n\n");
-}
-
-void DumpMetaData(FPDF_DOCUMENT doc) {
-  constexpr const char* meta_tags[] = {"Title",        "Author",  "Subject",
-                                       "Keywords",     "Creator", "Producer",
-                                       "CreationDate", "ModDate"};
-  for (const char* meta_tag : meta_tags) {
-    char meta_buffer[4096];
-    unsigned long len =
-        FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
-    if (!len)
-      continue;
-
-    auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
-    printf("%-12s = %ls (%lu bytes)\n", meta_tag,
-           GetPlatformWString(meta_string).c_str(), len);
-  }
-}
-
-void SaveAttachments(FPDF_DOCUMENT doc, const std::string& name) {
-  for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) {
-    FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i);
-
-    // Retrieve the attachment file name.
-    std::string attachment_name;
-    unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0);
-    if (len) {
-      std::vector<char> buf(len);
-      unsigned long actual_len =
-          FPDFAttachment_GetName(attachment, buf.data(), len);
-      if (actual_len == len) {
-        attachment_name =
-            GetPlatformString(reinterpret_cast<unsigned short*>(buf.data()));
-      }
-    }
-    if (attachment_name.empty()) {
-      fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
-      continue;
-    }
-
-    // Calculate the full attachment file name.
-    char save_name[256];
-    int chars_formatted =
-        snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(),
-                 attachment_name.c_str());
-    if (chars_formatted < 0 ||
-        static_cast<size_t>(chars_formatted) >= sizeof(save_name)) {
-      fprintf(stderr, "Filename %s is too long\n", save_name);
-      continue;
-    }
-
-    // Retrieve the attachment.
-    len = FPDFAttachment_GetFile(attachment, nullptr, 0);
-    std::vector<char> data_buf(len);
-    if (len) {
-      unsigned long actual_len =
-          FPDFAttachment_GetFile(attachment, data_buf.data(), len);
-      if (actual_len != len)
-        data_buf.clear();
-    }
-    if (data_buf.empty()) {
-      fprintf(stderr, "Attachment \"%s\" is empty.\n", attachment_name.c_str());
-      continue;
-    }
-
-    // Write the attachment file.
-    FILE* fp = fopen(save_name, "wb");
-    if (!fp) {
-      fprintf(stderr, "Failed to open %s for saving attachment.\n", save_name);
-      continue;
-    }
-
-    size_t written_len = fwrite(data_buf.data(), 1, len, fp);
-    if (written_len == len) {
-      fprintf(stderr, "Saved attachment \"%s\" as: %s.\n",
-              attachment_name.c_str(), save_name);
-    } else {
-      fprintf(stderr, "Failed to write to %s\n", save_name);
-    }
-    fclose(fp);
-  }
-}
-
-void SaveImages(FPDF_PAGE page, const char* pdf_name, int page_num) {
-  for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
-    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
-    if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE)
-      continue;
-
-    std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
-        FPDFImageObj_GetBitmap(obj));
-    if (!bitmap) {
-      fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
-              i + 1, page_num + 1);
-      continue;
-    }
-
-    int format = FPDFBitmap_GetFormat(bitmap.get());
-    if (format == FPDFBitmap_Unknown) {
-      fprintf(stderr,
-              "Image object #%d on page #%d has a bitmap of unknown format.\n",
-              i + 1, page_num + 1);
-      continue;
-    }
-
-    std::vector<unsigned char> png_encoding;
-    const unsigned char* buffer =
-        static_cast<const unsigned char*>(FPDFBitmap_GetBuffer(bitmap.get()));
-    int width = FPDFBitmap_GetWidth(bitmap.get());
-    int height = FPDFBitmap_GetHeight(bitmap.get());
-    int stride = FPDFBitmap_GetStride(bitmap.get());
-    bool ret = false;
-    switch (format) {
-      case FPDFBitmap_Gray:
-        ret = image_diff_png::EncodeGrayPNG(buffer, width, height, stride,
-                                            &png_encoding);
-        break;
-      case FPDFBitmap_BGR:
-        ret = image_diff_png::EncodeBGRPNG(buffer, width, height, stride,
-                                           &png_encoding);
-        break;
-      case FPDFBitmap_BGRx:
-        ret = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride, true,
-                                            &png_encoding);
-        break;
-      case FPDFBitmap_BGRA:
-        ret = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride,
-                                            false, &png_encoding);
-        break;
-      default:
-        NOTREACHED();
-    }
-    if (!ret) {
-      fprintf(stderr,
-              "Failed to convert image object #%d on page #%d to png.\n", i + 1,
-              page_num + 1);
-      continue;
-    }
-
-    char filename[256];
-    int chars_formatted = snprintf(filename, sizeof(filename), "%s.%d.%d.png",
-                                   pdf_name, page_num, i);
-    if (chars_formatted < 0 ||
-        static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
-      fprintf(stderr, "Filename %s for saving image is too long\n", filename);
-      continue;
-    }
-
-    FILE* fp = fopen(filename, "wb");
-    if (!fp) {
-      fprintf(stderr, "Failed to open %s for saving image.\n", filename);
-      continue;
-    }
-
-    size_t bytes_written =
-        fwrite(&png_encoding.front(), 1, png_encoding.size(), fp);
-    if (bytes_written != png_encoding.size())
-      fprintf(stderr, "Failed to write to %s.\n", filename);
-    else
-      fprintf(stderr, "Successfully wrote embedded image %s.\n", filename);
-
-    (void)fclose(fp);
-  }
+  FORM_OnAfterLoadPage(page_ptr, form_handle);
+  FORM_DoPageAAction(page_ptr, form_handle, FPDFPAGE_AACTION_OPEN);
+  return page_ptr;
 }
 
 // Note, for a client using progressive rendering you'd want to determine if you
@@ -1237,53 +694,60 @@
   if (options.send_events)
     SendPageEvents(form, page, events);
   if (options.save_images)
-    SaveImages(page, name.c_str(), page_index);
+    WriteImages(page, name.c_str(), page_index);
+  if (options.save_thumbnails)
+    WriteThumbnail(page, name.c_str(), page_index);
+  if (options.save_thumbnails_decoded)
+    WriteDecodedThumbnailStream(page, name.c_str(), page_index);
+  if (options.save_thumbnails_raw)
+    WriteRawThumbnailStream(page, name.c_str(), page_index);
+  if (options.output_format == OUTPUT_PAGEINFO) {
+    DumpPageInfo(page, page_index);
+    return true;
+  }
   if (options.output_format == OUTPUT_STRUCTURE) {
     DumpPageStructure(page, page_index);
     return true;
   }
 
-  std::unique_ptr<void, FPDFTextPageDeleter> text_page(FPDFText_LoadPage(page));
-
+  ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
   double scale = 1.0;
   if (!options.scale_factor_as_string.empty())
     std::stringstream(options.scale_factor_as_string) >> scale;
 
-  auto width = static_cast<int>(FPDF_GetPageWidth(page) * scale);
-  auto height = static_cast<int>(FPDF_GetPageHeight(page) * scale);
+  auto width = static_cast<int>(FPDF_GetPageWidthF(page) * scale);
+  auto height = static_cast<int>(FPDF_GetPageHeightF(page) * scale);
   int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
-  std::unique_ptr<void, FPDFBitmapDeleter> bitmap(
-      FPDFBitmap_Create(width, height, alpha));
+  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, alpha));
 
   if (bitmap) {
     FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
     FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
 
+    int flags = PageRenderFlagsFromOptions(options);
     if (options.render_oneshot) {
       // Note, client programs probably want to use this method instead of the
       // progressive calls. The progressive calls are if you need to pause the
       // rendering to update the UI, the PDF renderer will break when possible.
-      FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0,
-                            FPDF_ANNOT);
+      FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0, flags);
     } else {
       IFSDK_PAUSE pause;
       pause.version = 1;
       pause.NeedToPauseNow = &NeedToPauseNow;
 
       int rv = FPDF_RenderPageBitmap_Start(bitmap.get(), page, 0, 0, width,
-                                           height, 0, FPDF_ANNOT, &pause);
-      while (rv == FPDF_RENDER_TOBECOUNTINUED)
+                                           height, 0, flags, &pause);
+      while (rv == FPDF_RENDER_TOBECONTINUED)
         rv = FPDF_RenderPage_Continue(page, &pause);
     }
 
-    FPDF_FFLDraw(form, bitmap.get(), page, 0, 0, width, height, 0, FPDF_ANNOT);
+    FPDF_FFLDraw(form, bitmap.get(), page, 0, 0, width, height, 0, flags);
 
     if (!options.render_oneshot)
       FPDF_RenderPage_Close(page);
 
     int stride = FPDFBitmap_GetStride(bitmap.get());
-    const char* buffer =
-        reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap.get()));
+    void* buffer = FPDFBitmap_GetBuffer(bitmap.get());
 
     std::string image_file_name;
     switch (options.output_format) {
@@ -1335,8 +799,10 @@
 
     // Write the filename and the MD5 of the buffer to stdout if we wrote a
     // file.
-    if (options.md5 && !image_file_name.empty())
-      OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
+    if (options.md5 && !image_file_name.empty()) {
+      OutputMD5Hash(image_file_name.c_str(),
+                    static_cast<const uint8_t*>(buffer), stride * height);
+    }
   } else {
     fprintf(stderr, "Page was too large to be rendered.\n");
   }
@@ -1347,31 +813,12 @@
 }
 
 void RenderPdf(const std::string& name,
-               const char* pBuf,
+               const char* buf,
                size_t len,
                const Options& options,
                const std::string& events) {
-  IPDF_JSPLATFORM platform_callbacks = {};
-  platform_callbacks.version = 3;
-  platform_callbacks.app_alert = ExampleAppAlert;
-  platform_callbacks.app_response = ExampleAppResponse;
-  platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
-  platform_callbacks.Doc_mail = ExampleDocMail;
+  TestLoader loader({buf, len});
 
-  // The pdf_avail must outlive doc.
-  std::unique_ptr<void, FPDFAvailDeleter> pdf_avail;
-  // The document must outlive |form_callbacks.loaded_pages|.
-  std::unique_ptr<void, FPDFDocumentDeleter> doc;
-  FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
-#ifdef PDF_ENABLE_XFA
-  form_callbacks.version = 2;
-#else   // PDF_ENABLE_XFA
-  form_callbacks.version = 1;
-#endif  // PDF_ENABLE_XFA
-  form_callbacks.FFI_GetPage = GetPageForIndex;
-  form_callbacks.m_pJsPlatform = &platform_callbacks;
-
-  TestLoader loader(pBuf, len);
   FPDF_FILEACCESS file_access = {};
   file_access.m_FileLen = static_cast<unsigned long>(len);
   file_access.m_GetBlock = TestLoader::GetBlock;
@@ -1385,31 +832,42 @@
   hints.version = 1;
   hints.AddSegment = Add_Segment;
 
-  int nRet = PDF_DATA_NOTAVAIL;
-  bool bIsLinearized = false;
-  pdf_avail.reset(FPDFAvail_Create(&file_avail, &file_access));
+  // |pdf_avail| must outlive |doc|.
+  ScopedFPDFAvail pdf_avail(FPDFAvail_Create(&file_avail, &file_access));
 
-  if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
-    doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
-    if (doc) {
-      while (nRet == PDF_DATA_NOTAVAIL)
-        nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
+  // |doc| must outlive |form_callbacks.loaded_pages|.
+  ScopedFPDFDocument doc;
 
-      if (nRet == PDF_DATA_ERROR) {
-        fprintf(stderr, "Unknown error in checking if doc was available.\n");
-        return;
-      }
-      nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
-      if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
-        fprintf(stderr,
-                "Error %d was returned in checking if form was available.\n",
-                nRet);
-        return;
-      }
-      bIsLinearized = true;
-    }
+  const char* password =
+      options.password.empty() ? nullptr : options.password.c_str();
+  bool is_linearized = false;
+  if (options.use_load_mem_document) {
+    doc.reset(FPDF_LoadMemDocument(buf, len, password));
   } else {
-    doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
+    if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
+      int avail_status = PDF_DATA_NOTAVAIL;
+      doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), password));
+      if (doc) {
+        while (avail_status == PDF_DATA_NOTAVAIL)
+          avail_status = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
+
+        if (avail_status == PDF_DATA_ERROR) {
+          fprintf(stderr, "Unknown error in checking if doc was available.\n");
+          return;
+        }
+        avail_status = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
+        if (avail_status == PDF_FORM_ERROR ||
+            avail_status == PDF_FORM_NOTAVAIL) {
+          fprintf(stderr,
+                  "Error %d was returned in checking if form was available.\n",
+                  avail_status);
+          return;
+        }
+        is_linearized = true;
+      }
+    } else {
+      doc.reset(FPDF_LoadCustomDocument(&file_access, password));
+    }
   }
 
   if (!doc) {
@@ -1417,23 +875,57 @@
     return;
   }
 
+  if (!FPDF_DocumentHasValidCrossReferenceTable(doc.get()))
+    fprintf(stderr, "Document has invalid cross reference table\n");
+
   (void)FPDF_GetDocPermissions(doc.get());
 
   if (options.show_metadata)
     DumpMetaData(doc.get());
 
   if (options.save_attachments)
-    SaveAttachments(doc.get(), name);
+    WriteAttachments(doc.get(), name);
 
-  std::unique_ptr<void, FPDFFormHandleDeleter> form(
+#ifdef PDF_ENABLE_V8
+  IPDF_JSPLATFORM platform_callbacks = {};
+  platform_callbacks.version = 3;
+  platform_callbacks.app_alert = ExampleAppAlert;
+  platform_callbacks.app_beep = ExampleAppBeep;
+  platform_callbacks.app_response = ExampleAppResponse;
+  platform_callbacks.Doc_getFilePath = ExampleDocGetFilePath;
+  platform_callbacks.Doc_mail = ExampleDocMail;
+  platform_callbacks.Doc_print = ExampleDocPrint;
+  platform_callbacks.Doc_submitForm = ExampleDocSubmitForm;
+  platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
+  platform_callbacks.Field_browse = ExampleFieldBrowse;
+#endif  // PDF_ENABLE_V8
+
+  FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
+#ifdef PDF_ENABLE_XFA
+  form_callbacks.version = 2;
+  form_callbacks.xfa_disabled =
+      options.disable_xfa || options.disable_javascript;
+#else   // PDF_ENABLE_XFA
+  form_callbacks.version = 1;
+#endif  // PDF_ENABLE_XFA
+  form_callbacks.FFI_GetPage = GetPageForIndex;
+
+#ifdef PDF_ENABLE_V8
+  if (!options.disable_javascript)
+    form_callbacks.m_pJsPlatform = &platform_callbacks;
+#endif  // PDF_ENABLE_V8
+
+  ScopedFPDFFormHandle form(
       FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
   form_callbacks.form_handle = form.get();
 
 #ifdef PDF_ENABLE_XFA
-  int doc_type = FPDF_GetFormType(doc.get());
-  if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) {
-    if (!FPDF_LoadXFA(doc.get()))
-      fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
+  if (!options.disable_xfa && !options.disable_javascript) {
+    int doc_type = FPDF_GetFormType(doc.get());
+    if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) {
+      if (!FPDF_LoadXFA(doc.get()))
+        fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
+    }
   }
 #endif  // PDF_ENABLE_XFA
 
@@ -1455,12 +947,12 @@
   int first_page = options.pages ? options.first_page : 0;
   int last_page = options.pages ? options.last_page + 1 : page_count;
   for (int i = first_page; i < last_page; ++i) {
-    if (bIsLinearized) {
-      nRet = PDF_DATA_NOTAVAIL;
-      while (nRet == PDF_DATA_NOTAVAIL)
-        nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
+    if (is_linearized) {
+      int avail_status = PDF_DATA_NOTAVAIL;
+      while (avail_status == PDF_DATA_NOTAVAIL)
+        avail_status = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
 
-      if (nRet == PDF_DATA_ERROR) {
+      if (avail_status == PDF_DATA_ERROR) {
         fprintf(stderr, "Unknown error in checking if page %d is available.\n",
                 i);
         return;
@@ -1483,7 +975,7 @@
 void ShowConfig() {
   std::string config;
   std::string maybe_comma;
-#if PDF_ENABLE_V8
+#ifdef PDF_ENABLE_V8
   config.append(maybe_comma);
   config.append("V8");
   maybe_comma = ",";
@@ -1503,26 +995,64 @@
   config.append("ASAN");
   maybe_comma = ",";
 #endif  // PDF_ENABLE_ASAN
+#if defined(PDF_ENABLE_SKIA)
+  config.append(maybe_comma);
+  config.append("SKIA");
+  maybe_comma = ",";
+#elif defined(PDF_ENABLE_SKIA_PATHS)
+  config.append(maybe_comma);
+  config.append("SKIAPATHS");
+  maybe_comma = ",";
+#endif
   printf("%s\n", config.c_str());
 }
 
 constexpr char kUsageString[] =
     "Usage: pdfium_test [OPTION] [FILE]...\n"
-    "  --show-config       - print build options and exit\n"
-    "  --show-metadata     - print the file metadata\n"
-    "  --show-structure    - print the structure elements from the document\n"
-    "  --send-events       - send input described by .evt file\n"
-    "  --render-oneshot    - render image without using progressive renderer\n"
-    "  --save-attachments  - write embedded attachments "
+    "  --show-config        - print build options and exit\n"
+    "  --show-metadata      - print the file metadata\n"
+    "  --show-pageinfo      - print information about pages\n"
+    "  --show-structure     - print the structure elements from the document\n"
+    "  --send-events        - send input described by .evt file\n"
+    "  --mem-document       - load document with FPDF_LoadMemDocument()\n"
+    "  --render-oneshot     - render image without using progressive renderer\n"
+    "  --lcd-text           - render text optimized for LCD displays\n"
+    "  --no-nativetext      - render without using the native text output\n"
+    "  --grayscale          - render grayscale output\n"
+    "  --limit-cache        - render limiting image cache size\n"
+    "  --force-halftone     - render forcing halftone\n"
+    "  --printing           - render as if for printing\n"
+    "  --no-smoothtext      - render disabling text anti-aliasing\n"
+    "  --no-smoothimage     - render disabling image anti-alisasing\n"
+    "  --no-smoothpath      - render disabling path anti-aliasing\n"
+    "  --reverse-byte-order - render to BGRA, if supported by the output "
+    "format\n"
+    "  --save-attachments   - write embedded attachments "
     "<pdf-name>.attachment.<attachment-name>\n"
-    "  --save-images       - write embedded images "
+    "  --save-images        - write embedded images "
     "<pdf-name>.<page-number>.<object-number>.png\n"
+    "  --save-thumbs        - write page thumbnails "
+    "<pdf-name>.thumbnail.<page-number>.png\n"
+    "  --save-thumbs-dec    - write page thumbnails' decoded stream data"
+    "<pdf-name>.thumbnail.decoded.<page-number>.png\n"
+    "  --save-thumbs-raw    - write page thumbnails' raw stream data"
+    "<pdf-name>.thumbnail.raw.<page-number>.png\n"
+#ifdef PDF_ENABLE_V8
+    "  --disable-javascript - do not execute JS in PDF files\n"
+#ifdef PDF_ENABLE_XFA
+    "  --disable-xfa        - do not process XFA forms\n"
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 #ifdef ENABLE_CALLGRIND
-    "  --callgrind-delim   - delimit interesting section when using callgrind\n"
-#endif  // ENABLE_CALLGRIND
-    "  --bin-dir=<path>    - override path to v8 external data\n"
-    "  --font-dir=<path>   - override path to external fonts\n"
-    "  --scale=<number>    - scale output size by number (e.g. 0.5)\n"
+    "  --callgrind-delim    - delimit interesting section when using "
+    "callgrind\n"
+#endif
+#if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__))
+    "  --no-system-fonts    - do not use system fonts, overrides --font-dir\n"
+#endif
+    "  --bin-dir=<path>     - override path to v8 external data\n"
+    "  --font-dir=<path>    - override path to external fonts\n"
+    "  --scale=<number>     - scale output size by number (e.g. 0.5)\n"
     "  --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
 #ifdef _WIN32
     "  --bmp   - write page images <pdf-name>.<page-number>.bmp\n"
@@ -1531,7 +1061,7 @@
     "<pdf-name>.<page-number>.ps\n"
     "  --ps3   - write page raw PostScript (Lvl 3) "
     "<pdf-name>.<page-number>.ps\n"
-#endif  // _WIN32
+#endif
     "  --txt   - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
     "  --png   - write page images <pdf-name>.<page-number>.png\n"
     "  --ppm   - write page images <pdf-name>.<page-number>.ppm\n"
@@ -1540,6 +1070,7 @@
     "  --skp   - write page images <pdf-name>.<page-number>.skp\n"
 #endif
     "  --md5   - write output image paths and their md5 hashes to stdout.\n"
+    "  --time=<number> - Seconds since the epoch to set system time.\n"
     "";
 
 }  // namespace
@@ -1564,14 +1095,16 @@
   }
 
 #ifdef PDF_ENABLE_V8
-  v8::Platform* platform;
+  std::unique_ptr<v8::Platform> platform;
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  v8::StartupData natives;
   v8::StartupData snapshot;
-  InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
-                        &snapshot, &platform);
+  if (!options.disable_javascript) {
+    platform = InitializeV8ForPDFiumWithStartupData(
+        options.exe_path, options.bin_directory, &snapshot);
+  }
 #else   // V8_USE_EXTERNAL_STARTUP_DATA
-  InitializeV8ForPDFium(options.exe_path, &platform);
+  if (!options.disable_javascript)
+    platform = InitializeV8ForPDFium(options.exe_path);
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 #endif  // PDF_ENABLE_V8
 
@@ -1581,12 +1114,13 @@
   config.m_pIsolate = nullptr;
   config.m_v8EmbedderSlot = 0;
 
-  const char* path_array[2];
-  if (!options.font_directory.empty()) {
-    path_array[0] = options.font_directory.c_str();
-    path_array[1] = nullptr;
+  const char* path_array[2] = {nullptr, nullptr};
+  Optional<const char*> custom_font_path = GetCustomFontPath(options);
+  if (custom_font_path.has_value()) {
+    path_array[0] = custom_font_path.value();
     config.m_pUserFontPaths = path_array;
   }
+
   FPDF_InitLibraryWithConfig(&config);
 
   UNSUPPORT_INFO unsupported_info = {};
@@ -1595,6 +1129,14 @@
 
   FSDK_SetUnSpObjProcessHandler(&unsupported_info);
 
+  if (options.time > -1) {
+    // This must be a static var to avoid explicit capture, so the lambda can be
+    // converted to a function ptr.
+    static time_t time_ret = options.time;
+    FSDK_SetTimeFunction([]() { return time_ret; });
+    FSDK_SetLocaltimeFunction([](const time_t* tp) { return gmtime(tp); });
+  }
+
   for (const std::string& filename : files) {
     size_t file_length = 0;
     std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
@@ -1636,14 +1178,14 @@
   }
 
   FPDF_DestroyLibrary();
-#ifdef PDF_ENABLE_V8
-  v8::V8::ShutdownPlatform();
-  delete platform;
 
+#ifdef PDF_ENABLE_V8
+  if (!options.disable_javascript) {
+    v8::V8::ShutdownPlatform();
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  free(const_cast<char*>(natives.data));
-  free(const_cast<char*>(snapshot.data));
+    free(const_cast<char*>(snapshot.data));
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
+  }
 #endif  // PDF_ENABLE_V8
 
   return 0;
diff --git a/samples/pdfium_test_dump_helper.cc b/samples/pdfium_test_dump_helper.cc
new file mode 100644
index 0000000..047a670
--- /dev/null
+++ b/samples/pdfium_test_dump_helper.cc
@@ -0,0 +1,118 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/pdfium_test_dump_helper.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_transformpage.h"
+#include "testing/fx_string_testhelpers.h"
+
+using GetBoxInfoFunc =
+    std::function<bool(FPDF_PAGE, float*, float*, float*, float*)>;
+
+namespace {
+
+std::wstring ConvertToWString(const unsigned short* buf,
+                              unsigned long buf_size) {
+  std::wstring result;
+  result.reserve(buf_size);
+  std::copy(buf, buf + buf_size, std::back_inserter(result));
+  return result;
+}
+
+void DumpBoxInfo(GetBoxInfoFunc func,
+                 const char* box_type,
+                 FPDF_PAGE page,
+                 int page_idx) {
+  FS_RECTF rect;
+  bool ret = func(page, &rect.left, &rect.bottom, &rect.right, &rect.top);
+  if (!ret) {
+    printf("Page %d: No %s.\n", page_idx, box_type);
+    return;
+  }
+  printf("Page %d: %s: %0.2f %0.2f %0.2f %0.2f\n", page_idx, box_type,
+         rect.left, rect.bottom, rect.right, rect.top);
+}
+
+}  // namespace
+
+void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) {
+  static const size_t kBufSize = 1024;
+  unsigned short buf[kBufSize];
+  unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
+  printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str());
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetTitle(child, buf, kBufSize);
+  if (len > 0)
+    printf(": '%ls'", ConvertToWString(buf, len).c_str());
+
+  memset(buf, 0, sizeof(buf));
+  len = FPDF_StructElement_GetAltText(child, buf, kBufSize);
+  if (len > 0)
+    printf(" (%ls)", ConvertToWString(buf, len).c_str());
+  printf("\n");
+
+  for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) {
+    FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
+    // If the child is not an Element then this will return null. This can
+    // happen if the element is things like an object reference or a stream.
+    if (!sub_child)
+      continue;
+
+    DumpChildStructure(sub_child, indent + 1);
+  }
+}
+
+void DumpPageInfo(FPDF_PAGE page, int page_idx) {
+  DumpBoxInfo(&FPDFPage_GetMediaBox, "MediaBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetCropBox, "CropBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetBleedBox, "BleedBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetTrimBox, "TrimBox", page, page_idx);
+  DumpBoxInfo(&FPDFPage_GetArtBox, "ArtBox", page, page_idx);
+}
+
+void DumpPageStructure(FPDF_PAGE page, int page_idx) {
+  ScopedFPDFStructTree tree(FPDF_StructTree_GetForPage(page));
+  if (!tree) {
+    fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx);
+    return;
+  }
+
+  printf("Structure Tree for Page %d\n", page_idx);
+  for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) {
+    FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i);
+    if (!child) {
+      fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx);
+      continue;
+    }
+    DumpChildStructure(child, 0);
+  }
+  printf("\n\n");
+}
+
+void DumpMetaData(FPDF_DOCUMENT doc) {
+  static constexpr const char* kMetaTags[] = {
+      "Title",   "Author",   "Subject",      "Keywords",
+      "Creator", "Producer", "CreationDate", "ModDate"};
+  for (const char* meta_tag : kMetaTags) {
+    char meta_buffer[4096];
+    unsigned long len =
+        FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
+    if (!len)
+      continue;
+
+    auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
+    printf("%-12s = %ls (%lu bytes)\n", meta_tag,
+           GetPlatformWString(meta_string).c_str(), len);
+  }
+}
diff --git a/samples/pdfium_test_dump_helper.h b/samples/pdfium_test_dump_helper.h
new file mode 100644
index 0000000..17cbd8f
--- /dev/null
+++ b/samples/pdfium_test_dump_helper.h
@@ -0,0 +1,15 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
+#define SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
+
+#include "public/fpdfview.h"
+
+void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent);
+void DumpPageInfo(FPDF_PAGE page, int page_idx);
+void DumpPageStructure(FPDF_PAGE page, int page_idx);
+void DumpMetaData(FPDF_DOCUMENT doc);
+
+#endif  // SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
diff --git a/samples/pdfium_test_event_helper.cc b/samples/pdfium_test_event_helper.cc
new file mode 100644
index 0000000..a3ddbef
--- /dev/null
+++ b/samples/pdfium_test_event_helper.cc
@@ -0,0 +1,165 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/pdfium_test_event_helper.h"
+
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "public/fpdf_fwlevent.h"
+#include "public/fpdfview.h"
+#include "testing/fx_string_testhelpers.h"
+
+namespace {
+void SendCharCodeEvent(FPDF_FORMHANDLE form,
+                       FPDF_PAGE page,
+                       const std::vector<std::string>& tokens) {
+  if (tokens.size() != 2) {
+    fprintf(stderr, "charcode: bad args\n");
+    return;
+  }
+
+  int keycode = atoi(tokens[1].c_str());
+  FORM_OnChar(form, page, keycode, 0);
+}
+
+void SendKeyCodeEvent(FPDF_FORMHANDLE form,
+                      FPDF_PAGE page,
+                      const std::vector<std::string>& tokens) {
+  if (tokens.size() != 2) {
+    fprintf(stderr, "keycode: bad args\n");
+    return;
+  }
+
+  int keycode = atoi(tokens[1].c_str());
+  FORM_OnKeyDown(form, page, keycode, 0);
+  FORM_OnKeyUp(form, page, keycode, 0);
+}
+
+uint32_t GetModifiers(std::string modifiers_string) {
+  int modifiers = 0;
+  if (modifiers_string.find("shift") != std::string::npos)
+    modifiers |= FWL_EVENTFLAG_ShiftKey;
+  if (modifiers_string.find("control") != std::string::npos)
+    modifiers |= FWL_EVENTFLAG_ControlKey;
+  if (modifiers_string.find("alt") != std::string::npos)
+    modifiers |= FWL_EVENTFLAG_AltKey;
+
+  return modifiers;
+}
+
+void SendMouseDownEvent(FPDF_FORMHANDLE form,
+                        FPDF_PAGE page,
+                        const std::vector<std::string>& tokens) {
+  if (tokens.size() != 4 && tokens.size() != 5) {
+    fprintf(stderr, "mousedown: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[2].c_str());
+  int y = atoi(tokens[3].c_str());
+  uint32_t modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
+
+  if (tokens[1] == "left")
+    FORM_OnLButtonDown(form, page, modifiers, x, y);
+  else if (tokens[1] == "right")
+    FORM_OnRButtonDown(form, page, modifiers, x, y);
+  else
+    fprintf(stderr, "mousedown: bad button name\n");
+}
+
+void SendMouseUpEvent(FPDF_FORMHANDLE form,
+                      FPDF_PAGE page,
+                      const std::vector<std::string>& tokens) {
+  if (tokens.size() != 4 && tokens.size() != 5) {
+    fprintf(stderr, "mouseup: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[2].c_str());
+  int y = atoi(tokens[3].c_str());
+  int modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
+  if (tokens[1] == "left")
+    FORM_OnLButtonUp(form, page, modifiers, x, y);
+  else if (tokens[1] == "right")
+    FORM_OnRButtonUp(form, page, modifiers, x, y);
+  else
+    fprintf(stderr, "mouseup: bad button name\n");
+}
+
+void SendMouseDoubleClickEvent(FPDF_FORMHANDLE form,
+                               FPDF_PAGE page,
+                               const std::vector<std::string>& tokens) {
+  if (tokens.size() != 4 && tokens.size() != 5) {
+    fprintf(stderr, "mousedoubleclick: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[2].c_str());
+  int y = atoi(tokens[3].c_str());
+  int modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
+  if (tokens[1] != "left") {
+    fprintf(stderr, "mousedoubleclick: bad button name\n");
+    return;
+  }
+  FORM_OnLButtonDoubleClick(form, page, modifiers, x, y);
+}
+
+void SendMouseMoveEvent(FPDF_FORMHANDLE form,
+                        FPDF_PAGE page,
+                        const std::vector<std::string>& tokens) {
+  if (tokens.size() != 3) {
+    fprintf(stderr, "mousemove: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[1].c_str());
+  int y = atoi(tokens[2].c_str());
+  FORM_OnMouseMove(form, page, 0, x, y);
+}
+
+void SendFocusEvent(FPDF_FORMHANDLE form,
+                    FPDF_PAGE page,
+                    const std::vector<std::string>& tokens) {
+  if (tokens.size() != 3) {
+    fprintf(stderr, "focus: bad args\n");
+    return;
+  }
+
+  int x = atoi(tokens[1].c_str());
+  int y = atoi(tokens[2].c_str());
+  FORM_OnFocus(form, page, 0, x, y);
+}
+}  // namespace
+
+void SendPageEvents(FPDF_FORMHANDLE form,
+                    FPDF_PAGE page,
+                    const std::string& events) {
+  auto lines = StringSplit(events, '\n');
+  for (auto line : lines) {
+    auto command = StringSplit(line, '#');
+    if (command[0].empty())
+      continue;
+    auto tokens = StringSplit(command[0], ',');
+    if (tokens[0] == "charcode") {
+      SendCharCodeEvent(form, page, tokens);
+    } else if (tokens[0] == "keycode") {
+      SendKeyCodeEvent(form, page, tokens);
+    } else if (tokens[0] == "mousedown") {
+      SendMouseDownEvent(form, page, tokens);
+    } else if (tokens[0] == "mouseup") {
+      SendMouseUpEvent(form, page, tokens);
+    } else if (tokens[0] == "mousedoubleclick") {
+      SendMouseDoubleClickEvent(form, page, tokens);
+    } else if (tokens[0] == "mousemove") {
+      SendMouseMoveEvent(form, page, tokens);
+    } else if (tokens[0] == "focus") {
+      SendFocusEvent(form, page, tokens);
+    } else {
+      fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
+    }
+  }
+}
diff --git a/samples/pdfium_test_event_helper.h b/samples/pdfium_test_event_helper.h
new file mode 100644
index 0000000..e823369
--- /dev/null
+++ b/samples/pdfium_test_event_helper.h
@@ -0,0 +1,17 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
+#define SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
+
+#include <string>
+
+#include "public/fpdf_formfill.h"
+#include "public/fpdfview.h"
+
+void SendPageEvents(FPDF_FORMHANDLE form,
+                    FPDF_PAGE page,
+                    const std::string& events);
+
+#endif  // SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
diff --git a/samples/pdfium_test_write_helper.cc b/samples/pdfium_test_write_helper.cc
new file mode 100644
index 0000000..122d783
--- /dev/null
+++ b/samples/pdfium_test_write_helper.cc
@@ -0,0 +1,773 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/pdfium_test_write_helper.h"
+
+#include <limits.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_annot.h"
+#include "public/fpdf_attachment.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdf_thumbnail.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/image_diff/image_diff_png.h"
+#include "third_party/base/logging.h"
+
+namespace {
+
+bool CheckDimensions(int stride, int width, int height) {
+  if (stride < 0 || width < 0 || height < 0)
+    return false;
+  if (height > 0 && stride > INT_MAX / height)
+    return false;
+  return true;
+}
+
+const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
+  if (subtype == FPDF_ANNOT_TEXT)
+    return "Text";
+  if (subtype == FPDF_ANNOT_LINK)
+    return "Link";
+  if (subtype == FPDF_ANNOT_FREETEXT)
+    return "FreeText";
+  if (subtype == FPDF_ANNOT_LINE)
+    return "Line";
+  if (subtype == FPDF_ANNOT_SQUARE)
+    return "Square";
+  if (subtype == FPDF_ANNOT_CIRCLE)
+    return "Circle";
+  if (subtype == FPDF_ANNOT_POLYGON)
+    return "Polygon";
+  if (subtype == FPDF_ANNOT_POLYLINE)
+    return "PolyLine";
+  if (subtype == FPDF_ANNOT_HIGHLIGHT)
+    return "Highlight";
+  if (subtype == FPDF_ANNOT_UNDERLINE)
+    return "Underline";
+  if (subtype == FPDF_ANNOT_SQUIGGLY)
+    return "Squiggly";
+  if (subtype == FPDF_ANNOT_STRIKEOUT)
+    return "StrikeOut";
+  if (subtype == FPDF_ANNOT_STAMP)
+    return "Stamp";
+  if (subtype == FPDF_ANNOT_CARET)
+    return "Caret";
+  if (subtype == FPDF_ANNOT_INK)
+    return "Ink";
+  if (subtype == FPDF_ANNOT_POPUP)
+    return "Popup";
+  if (subtype == FPDF_ANNOT_FILEATTACHMENT)
+    return "FileAttachment";
+  if (subtype == FPDF_ANNOT_SOUND)
+    return "Sound";
+  if (subtype == FPDF_ANNOT_MOVIE)
+    return "Movie";
+  if (subtype == FPDF_ANNOT_WIDGET)
+    return "Widget";
+  if (subtype == FPDF_ANNOT_SCREEN)
+    return "Screen";
+  if (subtype == FPDF_ANNOT_PRINTERMARK)
+    return "PrinterMark";
+  if (subtype == FPDF_ANNOT_TRAPNET)
+    return "TrapNet";
+  if (subtype == FPDF_ANNOT_WATERMARK)
+    return "Watermark";
+  if (subtype == FPDF_ANNOT_THREED)
+    return "3D";
+  if (subtype == FPDF_ANNOT_RICHMEDIA)
+    return "RichMedia";
+  if (subtype == FPDF_ANNOT_XFAWIDGET)
+    return "XFAWidget";
+  NOTREACHED();
+  return "";
+}
+
+void AppendFlagString(const char* flag, std::string* output) {
+  if (!output->empty())
+    *output += ", ";
+  *output += flag;
+}
+
+std::string AnnotFlagsToString(int flags) {
+  std::string str;
+  if (flags & FPDF_ANNOT_FLAG_INVISIBLE)
+    AppendFlagString("Invisible", &str);
+  if (flags & FPDF_ANNOT_FLAG_HIDDEN)
+    AppendFlagString("Hidden", &str);
+  if (flags & FPDF_ANNOT_FLAG_PRINT)
+    AppendFlagString("Print", &str);
+  if (flags & FPDF_ANNOT_FLAG_NOZOOM)
+    AppendFlagString("NoZoom", &str);
+  if (flags & FPDF_ANNOT_FLAG_NOROTATE)
+    AppendFlagString("NoRotate", &str);
+  if (flags & FPDF_ANNOT_FLAG_NOVIEW)
+    AppendFlagString("NoView", &str);
+  if (flags & FPDF_ANNOT_FLAG_READONLY)
+    AppendFlagString("ReadOnly", &str);
+  if (flags & FPDF_ANNOT_FLAG_LOCKED)
+    AppendFlagString("Locked", &str);
+  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW)
+    AppendFlagString("ToggleNoView", &str);
+  return str;
+}
+
+const char* PageObjectTypeToCString(int type) {
+  if (type == FPDF_PAGEOBJ_TEXT)
+    return "Text";
+  if (type == FPDF_PAGEOBJ_PATH)
+    return "Path";
+  if (type == FPDF_PAGEOBJ_IMAGE)
+    return "Image";
+  if (type == FPDF_PAGEOBJ_SHADING)
+    return "Shading";
+  if (type == FPDF_PAGEOBJ_FORM)
+    return "Form";
+  NOTREACHED();
+  return "";
+}
+
+std::vector<uint8_t> EncodePng(pdfium::span<const uint8_t> input,
+                               int width,
+                               int height,
+                               int stride,
+                               int format) {
+  std::vector<uint8_t> png;
+  switch (format) {
+    case FPDFBitmap_Unknown:
+      break;
+    case FPDFBitmap_Gray:
+      png = image_diff_png::EncodeGrayPNG(input, width, height, stride);
+      break;
+    case FPDFBitmap_BGR:
+      png = image_diff_png::EncodeBGRPNG(input, width, height, stride);
+      break;
+    case FPDFBitmap_BGRx:
+      png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
+                                          /*discard_transparency=*/true);
+      break;
+    case FPDFBitmap_BGRA:
+      png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
+                                          /*discard_transparency=*/false);
+      break;
+    default:
+      NOTREACHED();
+  }
+  return png;
+}
+
+#ifdef _WIN32
+int CALLBACK EnhMetaFileProc(HDC hdc,
+                             HANDLETABLE* handle_table,
+                             const ENHMETARECORD* record,
+                             int objects_count,
+                             LPARAM param) {
+  std::vector<const ENHMETARECORD*>& items =
+      *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
+  items.push_back(record);
+  return 1;
+}
+#endif  // _WIN32
+
+}  // namespace
+
+std::string WritePpm(const char* pdf_name,
+                     int num,
+                     void* buffer_void,
+                     int stride,
+                     int width,
+                     int height) {
+  if (!CheckDimensions(stride, width, height))
+    return "";
+
+  int out_len = width * height;
+  if (out_len > INT_MAX / 3)
+    return "";
+
+  out_len *= 3;
+
+  char filename[256];
+  snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
+  FILE* fp = fopen(filename, "wb");
+  if (!fp)
+    return "";
+
+  fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
+  // Source data is B, G, R, unused.
+  // Dest data is R, G, B.
+  const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buffer_void);
+  std::vector<uint8_t> result(out_len);
+  for (int h = 0; h < height; ++h) {
+    const uint8_t* src_line = buffer + (stride * h);
+    uint8_t* dest_line = result.data() + (width * h * 3);
+    for (int w = 0; w < width; ++w) {
+      // R
+      dest_line[w * 3] = src_line[(w * 4) + 2];
+      // G
+      dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
+      // B
+      dest_line[(w * 3) + 2] = src_line[w * 4];
+    }
+  }
+  if (fwrite(result.data(), out_len, 1, fp) != 1)
+    fprintf(stderr, "Failed to write to %s\n", filename);
+
+  fclose(fp);
+  return std::string(filename);
+}
+
+void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
+  char filename[256];
+  int chars_formatted =
+      snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
+  if (chars_formatted < 0 ||
+      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
+    fprintf(stderr, "Filename %s is too long\n", filename);
+    return;
+  }
+
+  FILE* fp = fopen(filename, "w");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for output\n", filename);
+    return;
+  }
+
+  // Output in UTF32-LE.
+  uint32_t bom = 0x0000FEFF;
+  if (fwrite(&bom, sizeof(bom), 1, fp) != 1) {
+    fprintf(stderr, "Failed to write to %s\n", filename);
+    (void)fclose(fp);
+    return;
+  }
+
+  ScopedFPDFTextPage textpage(FPDFText_LoadPage(page));
+  for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) {
+    uint32_t c = FPDFText_GetUnicode(textpage.get(), i);
+    if (fwrite(&c, sizeof(c), 1, fp) != 1) {
+      fprintf(stderr, "Failed to write to %s\n", filename);
+      break;
+    }
+  }
+  (void)fclose(fp);
+}
+
+void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
+  // Open the output text file.
+  char filename[256];
+  int chars_formatted =
+      snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num);
+  if (chars_formatted < 0 ||
+      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
+    fprintf(stderr, "Filename %s is too long\n", filename);
+    return;
+  }
+
+  FILE* fp = fopen(filename, "w");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for output\n", filename);
+    return;
+  }
+
+  int annot_count = FPDFPage_GetAnnotCount(page);
+  fprintf(fp, "Number of annotations: %d\n\n", annot_count);
+
+  // Iterate through all annotations on this page.
+  for (int i = 0; i < annot_count; ++i) {
+    // Retrieve the annotation object and its subtype.
+    fprintf(fp, "Annotation #%d:\n", i + 1);
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
+    if (!annot) {
+      fprintf(fp, "Failed to retrieve annotation!\n\n");
+      continue;
+    }
+
+    FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot.get());
+    fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype));
+
+    // Retrieve the annotation flags.
+    fprintf(fp, "Flags set: %s\n",
+            AnnotFlagsToString(FPDFAnnot_GetFlags(annot.get())).c_str());
+
+    // Retrieve the annotation's object count and object types.
+    const int obj_count = FPDFAnnot_GetObjectCount(annot.get());
+    fprintf(fp, "Number of objects: %d\n", obj_count);
+    if (obj_count > 0) {
+      fprintf(fp, "Object types: ");
+      for (int j = 0; j < obj_count; ++j) {
+        const char* type = PageObjectTypeToCString(
+            FPDFPageObj_GetType(FPDFAnnot_GetObject(annot.get(), j)));
+        fprintf(fp, "%s  ", type);
+      }
+      fprintf(fp, "\n");
+    }
+
+    // Retrieve the annotation's color and interior color.
+    unsigned int R;
+    unsigned int G;
+    unsigned int B;
+    unsigned int A;
+    if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R, &G, &B,
+                           &A)) {
+      fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
+    } else {
+      fprintf(fp, "Failed to retrieve color.\n");
+    }
+    if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_InteriorColor, &R,
+                           &G, &B, &A)) {
+      fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
+    } else {
+      fprintf(fp, "Failed to retrieve interior color.\n");
+    }
+
+    // Retrieve the annotation's contents and author.
+    static constexpr char kContentsKey[] = "Contents";
+    static constexpr char kAuthorKey[] = "T";
+    unsigned long length_bytes =
+        FPDFAnnot_GetStringValue(annot.get(), kContentsKey, nullptr, 0);
+    std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+    FPDFAnnot_GetStringValue(annot.get(), kContentsKey, buf.data(),
+                             length_bytes);
+    fprintf(fp, "Content: %ls\n", GetPlatformWString(buf.data()).c_str());
+    length_bytes =
+        FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, nullptr, 0);
+    buf = GetFPDFWideStringBuffer(length_bytes);
+    FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, buf.data(), length_bytes);
+    fprintf(fp, "Author: %ls\n", GetPlatformWString(buf.data()).c_str());
+
+    // Retrieve the annotation's quadpoints if it is a markup annotation.
+    if (FPDFAnnot_HasAttachmentPoints(annot.get())) {
+      size_t qp_count = FPDFAnnot_CountAttachmentPoints(annot.get());
+      fprintf(fp, "Number of quadpoints sets: %zu\n", qp_count);
+
+      // Iterate through all quadpoints of the current annotation
+      for (size_t j = 0; j < qp_count; ++j) {
+        FS_QUADPOINTSF quadpoints;
+        if (FPDFAnnot_GetAttachmentPoints(annot.get(), j, &quadpoints)) {
+          fprintf(fp,
+                  "Quadpoints set #%zu: (%.3f, %.3f), (%.3f, %.3f), "
+                  "(%.3f, %.3f), (%.3f, %.3f)\n",
+                  j + 1, quadpoints.x1, quadpoints.y1, quadpoints.x2,
+                  quadpoints.y2, quadpoints.x3, quadpoints.y3, quadpoints.x4,
+                  quadpoints.y4);
+        } else {
+          fprintf(fp, "Failed to retrieve quadpoints set #%zu.\n", j + 1);
+        }
+      }
+    }
+
+    // Retrieve the annotation's rectangle coordinates.
+    FS_RECTF rect;
+    if (FPDFAnnot_GetRect(annot.get(), &rect)) {
+      fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n",
+              rect.left, rect.bottom, rect.right, rect.top);
+    } else {
+      fprintf(fp, "Failed to retrieve annotation rectangle.\n");
+    }
+  }
+
+  (void)fclose(fp);
+}
+
+std::string WritePng(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height) {
+  if (!CheckDimensions(stride, width, height))
+    return "";
+
+  auto input =
+      pdfium::make_span(static_cast<uint8_t*>(buffer), stride * height);
+  std::vector<uint8_t> png_encoding =
+      EncodePng(input, width, height, stride, FPDFBitmap_BGRA);
+  if (png_encoding.empty()) {
+    fprintf(stderr, "Failed to convert bitmap to PNG\n");
+    return "";
+  }
+
+  char filename[256];
+  int chars_formatted =
+      snprintf(filename, sizeof(filename), "%s.%d.png", pdf_name, num);
+  if (chars_formatted < 0 ||
+      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
+    fprintf(stderr, "Filename %s is too long\n", filename);
+    return "";
+  }
+
+  FILE* fp = fopen(filename, "wb");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for output\n", filename);
+    return "";
+  }
+
+  size_t bytes_written =
+      fwrite(&png_encoding.front(), 1, png_encoding.size(), fp);
+  if (bytes_written != png_encoding.size())
+    fprintf(stderr, "Failed to write to %s\n", filename);
+
+  (void)fclose(fp);
+  return std::string(filename);
+}
+
+#ifdef _WIN32
+std::string WriteBmp(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height) {
+  if (!CheckDimensions(stride, width, height))
+    return "";
+
+  int out_len = stride * height;
+  if (out_len > INT_MAX / 3)
+    return "";
+
+  char filename[256];
+  snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
+  FILE* fp = fopen(filename, "wb");
+  if (!fp)
+    return "";
+
+  BITMAPINFO bmi = {};
+  bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
+  bmi.bmiHeader.biWidth = width;
+  bmi.bmiHeader.biHeight = -height;  // top-down image
+  bmi.bmiHeader.biPlanes = 1;
+  bmi.bmiHeader.biBitCount = 32;
+  bmi.bmiHeader.biCompression = BI_RGB;
+  bmi.bmiHeader.biSizeImage = 0;
+
+  BITMAPFILEHEADER file_header = {};
+  file_header.bfType = 0x4d42;
+  file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
+  file_header.bfOffBits = file_header.bfSize - out_len;
+
+  if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 ||
+      fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 ||
+      fwrite(buffer, out_len, 1, fp) != 1) {
+    fprintf(stderr, "Failed to write to %s\n", filename);
+  }
+  fclose(fp);
+  return std::string(filename);
+}
+
+void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
+  char filename[256];
+  snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
+
+  HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
+
+  int width = static_cast<int>(FPDF_GetPageWidthF(page));
+  int height = static_cast<int>(FPDF_GetPageHeightF(page));
+  HRGN rgn = CreateRectRgn(0, 0, width, height);
+  SelectClipRgn(dc, rgn);
+  DeleteObject(rgn);
+
+  SelectObject(dc, GetStockObject(NULL_PEN));
+  SelectObject(dc, GetStockObject(WHITE_BRUSH));
+  // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
+  Rectangle(dc, 0, 0, width + 1, height + 1);
+
+  FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
+
+  DeleteEnhMetaFile(CloseEnhMetaFile(dc));
+}
+
+void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
+  char filename[256];
+  snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num);
+  FILE* fp = fopen(filename, "wb");
+  if (!fp)
+    return;
+
+  HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
+
+  int width = static_cast<int>(FPDF_GetPageWidthF(page));
+  int height = static_cast<int>(FPDF_GetPageHeightF(page));
+  FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
+
+  HENHMETAFILE emf = CloseEnhMetaFile(dc);
+  std::vector<const ENHMETARECORD*> items;
+  EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
+  for (const ENHMETARECORD* record : items) {
+    if (record->iType != EMR_GDICOMMENT)
+      continue;
+
+    const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
+    const char* data = reinterpret_cast<const char*>(comment->Data);
+    uint16_t size = *reinterpret_cast<const uint16_t*>(data);
+    if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) {
+      fprintf(stderr, "Failed to write to %s\n", filename);
+      break;
+    }
+  }
+  fclose(fp);
+  DeleteEnhMetaFile(emf);
+}
+#endif  // _WIN32
+
+#ifdef PDF_ENABLE_SKIA
+std::string WriteSkp(const char* pdf_name,
+                     int num,
+                     SkPictureRecorder* recorder) {
+  char filename[256];
+  int chars_formatted =
+      snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
+
+  if (chars_formatted < 0 ||
+      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
+    fprintf(stderr, "Filename %s is too long\n", filename);
+    return "";
+  }
+
+  sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
+  SkFILEWStream wStream(filename);
+  picture->serialize(&wStream);
+  return std::string(filename);
+}
+#endif
+
+enum class ThumbnailDecodeType { kBitmap, kRawStream, kDecodedStream };
+
+bool GetThumbnailFilename(char* name_buf,
+                          size_t name_buf_size,
+                          const char* pdf_name,
+                          int page_num,
+                          ThumbnailDecodeType decode_type) {
+  const char* format;
+  switch (decode_type) {
+    case ThumbnailDecodeType::kBitmap:
+      format = "%s.thumbnail.%d.png";
+      break;
+    case ThumbnailDecodeType::kDecodedStream:
+      format = "%s.thumbnail.decoded.%d.bin";
+      break;
+    case ThumbnailDecodeType::kRawStream:
+      format = "%s.thumbnail.raw.%d.bin";
+      break;
+  }
+
+  int chars_formatted =
+      snprintf(name_buf, name_buf_size, format, pdf_name, page_num);
+  if (chars_formatted < 0 ||
+      static_cast<size_t>(chars_formatted) >= name_buf_size) {
+    fprintf(stderr, "Filename %s for saving is too long.\n", name_buf);
+    return false;
+  }
+
+  return true;
+}
+
+void WriteBufferToFile(const void* buf,
+                       size_t buflen,
+                       const char* filename,
+                       const char* filetype) {
+  FILE* fp = fopen(filename, "wb");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for saving %s.", filename, filetype);
+    return;
+  }
+
+  size_t bytes_written = fwrite(buf, 1, buflen, fp);
+  if (bytes_written == buflen)
+    fprintf(stderr, "Successfully wrote %s %s.\n", filetype, filename);
+  else
+    fprintf(stderr, "Failed to write to %s.\n", filename);
+  fclose(fp);
+}
+
+std::vector<uint8_t> EncodeBitmapToPng(ScopedFPDFBitmap bitmap) {
+  std::vector<uint8_t> png_encoding;
+  int format = FPDFBitmap_GetFormat(bitmap.get());
+  if (format == FPDFBitmap_Unknown)
+    return png_encoding;
+
+  int width = FPDFBitmap_GetWidth(bitmap.get());
+  int height = FPDFBitmap_GetHeight(bitmap.get());
+  int stride = FPDFBitmap_GetStride(bitmap.get());
+  if (!CheckDimensions(stride, width, height))
+    return png_encoding;
+
+  auto input = pdfium::make_span(
+      static_cast<const uint8_t*>(FPDFBitmap_GetBuffer(bitmap.get())),
+      stride * height);
+
+  png_encoding = EncodePng(input, width, height, stride, format);
+  return png_encoding;
+}
+
+void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name) {
+  for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) {
+    FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i);
+
+    // Retrieve the attachment file name.
+    std::string attachment_name;
+    unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
+    if (length_bytes) {
+      std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+      unsigned long actual_length_bytes =
+          FPDFAttachment_GetName(attachment, buf.data(), length_bytes);
+      if (actual_length_bytes == length_bytes)
+        attachment_name = GetPlatformString(buf.data());
+    }
+    if (attachment_name.empty()) {
+      fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
+      continue;
+    }
+
+    // Calculate the full attachment file name.
+    char save_name[256];
+    int chars_formatted =
+        snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(),
+                 attachment_name.c_str());
+    if (chars_formatted < 0 ||
+        static_cast<size_t>(chars_formatted) >= sizeof(save_name)) {
+      fprintf(stderr, "Filename %s is too long.\n", save_name);
+      continue;
+    }
+
+    // Retrieve the attachment.
+    length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0);
+    std::vector<char> data_buf(length_bytes);
+    if (length_bytes) {
+      unsigned long actual_length_bytes =
+          FPDFAttachment_GetFile(attachment, data_buf.data(), length_bytes);
+      if (actual_length_bytes != length_bytes)
+        data_buf.clear();
+    }
+    if (data_buf.empty()) {
+      fprintf(stderr, "Attachment \"%s\" is empty.\n", attachment_name.c_str());
+      continue;
+    }
+
+    // Write the attachment file.
+    WriteBufferToFile(data_buf.data(), length_bytes, save_name, "attachment");
+  }
+}
+
+void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num) {
+  for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
+    FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
+    if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE)
+      continue;
+
+    ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
+    if (!bitmap) {
+      fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
+              i + 1, page_num + 1);
+      continue;
+    }
+
+    char filename[256];
+    int chars_formatted = snprintf(filename, sizeof(filename), "%s.%d.%d.png",
+                                   pdf_name, page_num, i);
+    if (chars_formatted < 0 ||
+        static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
+      fprintf(stderr, "Filename %s for saving image is too long.\n", filename);
+      continue;
+    }
+
+    std::vector<uint8_t> png_encoding = EncodeBitmapToPng(std::move(bitmap));
+    if (png_encoding.empty()) {
+      fprintf(stderr,
+              "Failed to convert image object #%d, on page #%d to png.\n",
+              i + 1, page_num + 1);
+      continue;
+    }
+
+    WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename,
+                      "image");
+  }
+}
+
+void WriteDecodedThumbnailStream(FPDF_PAGE page,
+                                 const char* pdf_name,
+                                 int page_num) {
+  char filename[256];
+  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
+                            ThumbnailDecodeType::kDecodedStream)) {
+    return;
+  }
+
+  unsigned long decoded_data_size =
+      FPDFPage_GetDecodedThumbnailData(page, nullptr, 0u);
+
+  // Only continue if there actually is a thumbnail for this page
+  if (decoded_data_size == 0) {
+    fprintf(stderr, "Failed to get decoded thumbnail for page #%d.\n",
+            page_num + 1);
+    return;
+  }
+
+  std::vector<uint8_t> thumb_buf(decoded_data_size);
+  if (FPDFPage_GetDecodedThumbnailData(
+          page, thumb_buf.data(), decoded_data_size) != decoded_data_size) {
+    fprintf(stderr, "Failed to get decoded thumbnail data for %s.\n", filename);
+    return;
+  }
+
+  WriteBufferToFile(thumb_buf.data(), decoded_data_size, filename,
+                    "decoded thumbnail");
+}
+
+void WriteRawThumbnailStream(FPDF_PAGE page,
+                             const char* pdf_name,
+                             int page_num) {
+  char filename[256];
+  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
+                            ThumbnailDecodeType::kRawStream)) {
+    return;
+  }
+
+  unsigned long raw_data_size = FPDFPage_GetRawThumbnailData(page, nullptr, 0u);
+
+  // Only continue if there actually is a thumbnail for this page
+  if (raw_data_size == 0) {
+    fprintf(stderr, "Failed to get raw thumbnail data for page #%d.\n",
+            page_num + 1);
+    return;
+  }
+
+  std::vector<uint8_t> thumb_buf(raw_data_size);
+  if (FPDFPage_GetRawThumbnailData(page, thumb_buf.data(), raw_data_size) !=
+      raw_data_size) {
+    fprintf(stderr, "Failed to get raw thumbnail data for %s.\n", filename);
+    return;
+  }
+
+  WriteBufferToFile(thumb_buf.data(), raw_data_size, filename, "raw thumbnail");
+}
+
+void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num) {
+  char filename[256];
+  if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
+                            ThumbnailDecodeType::kBitmap)) {
+    return;
+  }
+
+  ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
+  if (!thumb_bitmap) {
+    fprintf(stderr, "Thumbnail of page #%d has an empty bitmap.\n",
+            page_num + 1);
+    return;
+  }
+
+  std::vector<uint8_t> png_encoding =
+      EncodeBitmapToPng(std::move(thumb_bitmap));
+  if (png_encoding.empty()) {
+    fprintf(stderr, "Failed to convert thumbnail of page #%d to png.\n",
+            page_num + 1);
+    return;
+  }
+
+  WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename,
+                    "thumbnail");
+}
diff --git a/samples/pdfium_test_write_helper.h b/samples/pdfium_test_write_helper.h
new file mode 100644
index 0000000..b53d555
--- /dev/null
+++ b/samples/pdfium_test_write_helper.h
@@ -0,0 +1,59 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
+#define SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+
+#ifdef PDF_ENABLE_SKIA
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkStream.h"
+#endif
+
+std::string WritePpm(const char* pdf_name,
+                     int num,
+                     void* buffer_void,
+                     int stride,
+                     int width,
+                     int height);
+void WriteText(FPDF_PAGE page, const char* pdf_name, int num);
+void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num);
+std::string WritePng(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height);
+
+#ifdef _WIN32
+std::string WriteBmp(const char* pdf_name,
+                     int num,
+                     void* buffer,
+                     int stride,
+                     int width,
+                     int height);
+void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num);
+void WritePS(FPDF_PAGE page, const char* pdf_name, int num);
+#endif  // _WIN32
+
+#ifdef PDF_ENABLE_SKIA
+std::string WriteSkp(const char* pdf_name,
+                     int num,
+                     SkPictureRecorder* recorder);
+#endif  // PDF_ENABLE_SKIA
+
+void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name);
+void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num);
+void WriteDecodedThumbnailStream(FPDF_PAGE page,
+                                 const char* pdf_name,
+                                 int page_num);
+void WriteRawThumbnailStream(FPDF_PAGE page,
+                             const char* pdf_name,
+                             int page_num);
+void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num);
+
+#endif  // SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 7ca7c19..d49587c 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -6,6 +6,8 @@
 import("//build/config/ui.gni")
 import("//testing/test.gni")
 import("//third_party/skia/gn/shared_sources.gni")
+import("//third_party/skia/third_party/skcms/skcms.gni")
+import("features.gni")
 
 if (current_cpu == "arm") {
   import("//build/config/arm.gni")
@@ -15,30 +17,13 @@
 }
 
 skia_support_gpu = !is_ios
-skia_support_pdf = false  #!is_ios && (enable_basic_printing || enable_print_preview)
+skia_support_pdf = false
 
 # External-facing config for dependent code.
 config("skia_config") {
-  include_dirs = [
-    "config",
-    "ext",
-    "//third_party/skia/include/c",
-    "//third_party/skia/include/codec",
-    "//third_party/skia/include/config",
-    "//third_party/skia/include/core",
-    "//third_party/skia/include/effects",
-    "//third_party/skia/include/encode",
-    "//third_party/skia/include/images",
-    "//third_party/skia/include/lazy",
-    "//third_party/skia/include/pathops",
-    "//third_party/skia/include/pdf",
-    "//third_party/skia/include/pipe",
-    "//third_party/skia/include/ports",
-    "//third_party/skia/include/utils",
-    "//third_party/skia/third_party/vulkan",
-  ]
+  include_dirs = [ "//third_party/skia" ]
 
-  defines = []
+  defines = [ "SK_USER_CONFIG_HEADER=\"../../skia/config/SkUserConfig.h\"" ]
 
   if (is_win) {
     defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ]
@@ -52,15 +37,15 @@
   }
 
   if (skia_support_gpu) {
-    include_dirs += [
-      "//third_party/skia/include/gpu",
-      "//third_party/skia/src/gpu",
-    ]
     defines += [ "SK_SUPPORT_GPU=1" ]
   } else {
     defines += [ "SK_SUPPORT_GPU=0" ]
   }
 
+  if (skia_use_gl) {
+    defines += [ "SK_GL" ]
+  }
+
   if (is_android) {
     defines += [
       "SK_BUILD_FOR_ANDROID",
@@ -79,30 +64,6 @@
 
 # Internal-facing config for Skia library code.
 config("skia_library_config") {
-  # These include directories are only included for Skia code and are not
-  # exported to dependents. It's not clear if this is on purpose, but this
-  # matches the GYP build.
-  include_dirs = [
-    "//third_party/skia/include/private",
-    "//third_party/skia/include/client/android",
-    "//third_party/skia/src/core",
-    "//third_party/skia/src/image",
-    "//third_party/skia/src/opts",
-    "//third_party/skia/src/pdf",
-    "//third_party/skia/src/ports",
-    "//third_party/skia/src/sfnt",
-    "//third_party/skia/src/shaders",
-    "//third_party/skia/src/sksl",
-    "//third_party/skia/src/utils",
-    "//third_party/skia/src/lazy",
-  ]
-  if (is_mac || is_ios) {
-    include_dirs += [ "//third_party/skia/include/utils/mac" ]
-  }
-  if (is_mac) {
-    include_dirs += [ "//third_party/skia/include/utils/ios" ]
-  }
-
   defines = []
 
   if (is_component_build) {
@@ -153,11 +114,6 @@
   }
 
   if (is_win) {
-    include_dirs += [
-      "//third_party/skia/include/utils/win",
-      "//third_party/skia/src/utils/win",
-    ]
-
     defines += [
       # On windows, GDI handles are a scarse system-wide resource so we have to
       # keep the glyph cache, which holds up to 4 GDI handles per entry, to a
@@ -167,7 +123,6 @@
 
     cflags = [
       "/wd4244",  # conversion from 'type1( __int64)' to 'type2 (unsigned int)'
-      "/wd4267",  # conversion from 'size_t' (64 bit) to 'type'(32 bit).
       "/wd4341",  # signed value is out of range for enum constant.
       "/wd4345",  # Object is default-initialized if initialization is omitted.
       "/wd4390",  # ';'empty statement found in looping;is it what was intended?
@@ -176,8 +131,35 @@
                   # assembly code contains flow control(jmp or jcc) statements.
 
       "/wd4800",  # forcing value to bool 'true/false'(assigning int to bool).
+      "/wd5041",  # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17
+    ]
+    cflags_cc = [ "/std:c++17" ]
+  } else {
+    cflags_cc = [ "-std=c++17" ]
+    cflags_objcc = [ "-std=c++17" ]
+  }
+}
+
+source_set("skcms") {
+  cflags = []
+  if (!is_win || is_clang) {
+    cflags += [
+      "-w",
+      "-std=c11",
     ]
   }
+
+  # LLVM automatically sets the equivalent of GCC's -mfp16-format=ieee on ARM
+  # builds by default, while GCC itself does not. We need it to enable support
+  # for half-precision floating point data types used by SKCMS on ARM.
+  if (is_linux && !is_clang && current_cpu == "arm") {
+    cflags += [ "-mfp16-format=ieee" ]
+  }
+
+  public = [ "//third_party/skia/include/third_party/skcms/skcms.h" ]
+  include_dirs = [ "//third_party/skia/include/third_party/skcms" ]
+  sources =
+      rebase_path(skcms_sources, ".", "//third_party/skia/third_party/skcms")
 }
 
 component("skia") {
@@ -188,11 +170,11 @@
   ]
 
   # The skia sources values are relative to the skia_dir, so we need to rebase.
-  sources += skia_core_sources
-  sources += skia_effects_sources
+  sources += skia_sksl_gpu_sources
   sources += skia_sksl_sources
   sources += skia_utils_sources
   sources += [
+    "//third_party/skia/src/codec/SkMasks.cpp",
     "//third_party/skia/src/fonts/SkFontMgr_indirect.cpp",
     "//third_party/skia/src/fonts/SkRemotableFontMgr.cpp",
     "//third_party/skia/src/images/SkImageEncoder.cpp",
@@ -237,12 +219,14 @@
   if (skia_support_gpu) {
     sources += skia_gpu_sources
     sources += skia_null_gpu_sources
+    if (skia_use_gl) {
+      sources += skia_gl_gpu_sources
+    }
   }
 
   # Remove unused util files include in utils.gni
   sources -= [
     "//third_party/skia/src/utils/SkCamera.cpp",
-    "//third_party/skia/src/utils/SkDumpCanvas.cpp",
     "//third_party/skia/src/utils/SkFrontBufferedStream.cpp",
     "//third_party/skia/src/utils/SkInterpolator.cpp",
     "//third_party/skia/src/utils/SkParsePath.cpp",
@@ -319,10 +303,12 @@
   public_configs = [ ":skia_config" ]
 
   deps = [
+    ":skcms",
     ":skia_opts",
     "../third_party:zlib",
     "//:freetype_common",
   ]
+  public_deps = [ ":skia_core_and_effects" ]
 
   if (is_linux) {
     deps += [ "//third_party/icu:icuuc" ]
@@ -332,19 +318,12 @@
     set_sources_assignment_filter([])
     set_sources_assignment_filter(sources_assignment_filter)
     deps += [
-      "//third_party/android_tools:cpu_features",
+      "//third_party/android_sdk:cpu_features",
       "//third_party/expat",
       "//third_party/freetype-android:freetype",
     ]
   }
 
-  if (skia_support_pdf) {
-    deps += [ "//third_party/sfntly" ]
-    sources += skia_pdf_sources
-  } else {
-    sources += [ "//third_party/skia/src/pdf/SkDocument_PDF_None.cpp" ]
-  }
-
   if (is_android && !is_debug) {
     configs -= [ "//build/config/compiler:default_optimization" ]
     configs += [ "//build/config/compiler:optimize_max" ]
@@ -359,15 +338,63 @@
       "//third_party/skia/src/utils/mac/SkStream_mac.cpp",
     ]
     set_sources_assignment_filter(sources_assignment_filter)
-
-    # To disable warning "CGContextSelectFont' is deprecated"
-    cflags = [ "-Wno-deprecated-declarations" ]
   }
+
+  if (skia_support_pdf) {
+    deps += [ "//third_party/sfntly" ]
+    sources += skia_pdf_sources
+  } else {
+    sources += [ "//third_party/skia/src/pdf/SkDocument_PDF_None.cpp" ]
+  }
+}
+
+# Template for things that are logically part of :skia, but need to be split out
+# so custom compile flags can be applied.
+#
+# These are all opted out of check_includes, due to (logically) being part of
+# skia.
+template("skia_source_set") {
+  source_set(target_name) {
+    forward_variables_from(invoker, "*")
+
+    check_includes = false
+
+    if (!is_debug) {
+      configs -= [ "//build/config/compiler:default_optimization" ]
+      configs += [ "//build/config/compiler:optimize_max" ]
+    }
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      ":skia_config",
+      ":skia_library_config",
+      "//build/config/compiler:no_chromium_code",
+    ]
+    public_configs = [ ":skia_config" ]
+
+    if (is_win) {
+      cflags_cc = [
+        "/std:c++17",
+        "/wd5041",  # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17
+      ]
+    } else {
+      cflags_cc = [ "-std=c++17" ]
+    }
+  }
+}
+
+skia_source_set("skia_core_and_effects") {
+  defines = skia_core_defines
+  sources = skia_core_sources
+  sources += skia_effects_sources
+  sources += skia_effects_imagefilter_sources
+
+  visibility = [ ":skia" ]
 }
 
 # Separated out so it can be compiled with different flags for SSE.
 if (current_cpu == "x86" || current_cpu == "x64") {
-  source_set("skia_opts_sse3") {
+  skia_source_set("skia_opts_sse3") {
     sources = skia_opts.ssse3_sources
     if (!is_win || is_clang) {
       cflags = [ "-mssse3" ]
@@ -376,14 +403,8 @@
       defines = [ "SK_CPU_SSE_LEVEL=31" ]
     }
     visibility = [ ":skia_opts" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":skia_config",
-      ":skia_library_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
   }
-  source_set("skia_opts_sse41") {
+  skia_source_set("skia_opts_sse41") {
     sources = skia_opts.sse41_sources
     if (!is_win || is_clang) {
       cflags = [ "-msse4.1" ]
@@ -392,14 +413,8 @@
       defines = [ "SK_CPU_SSE_LEVEL=41" ]
     }
     visibility = [ ":skia_opts" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":skia_config",
-      ":skia_library_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
   }
-  source_set("skia_opts_sse42") {
+  skia_source_set("skia_opts_sse42") {
     sources = skia_opts.sse42_sources
     if (!is_win || is_clang) {
       cflags = [ "-msse4.2" ]
@@ -408,14 +423,8 @@
       defines = [ "SK_CPU_SSE_LEVEL=42" ]
     }
     visibility = [ ":skia_opts" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":skia_config",
-      ":skia_library_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
   }
-  source_set("skia_opts_avx") {
+  skia_source_set("skia_opts_avx") {
     sources = skia_opts.avx_sources
     if (!is_win) {
       cflags = [ "-mavx" ]
@@ -424,14 +433,8 @@
       cflags = [ "/arch:AVX" ]
     }
     visibility = [ ":skia_opts" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":skia_config",
-      ":skia_library_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
   }
-  source_set("skia_opts_hsw") {
+  skia_source_set("skia_opts_hsw") {
     sources = skia_opts.hsw_sources
     if (!is_win) {
       cflags = [
@@ -446,15 +449,10 @@
       cflags = [ "/arch:AVX2" ]
     }
     visibility = [ ":skia_opts" ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      ":skia_config",
-      ":skia_library_config",
-      "//build/config/compiler:no_chromium_code",
-    ]
   }
 }
-source_set("skia_opts") {
+
+skia_source_set("skia_opts") {
   cflags = []
   defines = []
 
@@ -509,12 +507,5 @@
     configs += [ "//build/config/compiler:optimize_max" ]
   }
 
-  configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [
-    ":skia_config",
-    ":skia_library_config",
-    "//build/config/compiler:no_chromium_code",
-  ]
-
   visibility = [ ":skia" ]
 }
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index bb9862b..679a19c 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -1,27 +1,20 @@
+
 /*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright 2006 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
  */
 
+
 #ifndef SKIA_CONFIG_SKUSERCONFIG_H_
 #define SKIA_CONFIG_SKUSERCONFIG_H_
 
 /*  SkTypes.h, the root of the public header files, does the following trick:
 
-    #include <SkPreConfig.h>
-    #include <SkUserConfig.h>
-    #include <SkPostConfig.h>
+    #include "SkPreConfig.h"
+    #include "SkUserConfig.h"
+    #include "SkPostConfig.h"
 
     SkPreConfig.h runs first, and it is responsible for initializing certain
     skia defines.
@@ -56,16 +49,14 @@
 // #define SK_DEBUG
 // #define SK_RELEASE
 
-// #ifdef DCHECK_ALWAYS_ON
-//     #undef SK_RELEASE
-//     #define SK_DEBUG
-// #endif
-
-/*  If, in debugging mode, Skia needs to stop (presumably to invoke a debugger)
-    it will call SK_CRASH(). If this is not defined it, it is defined in
-    SkPostConfig.h to write to an illegal address
+/*  Skia has certain debug-only code that is extremely intensive even for debug
+    builds.  This code is useful for diagnosing specific issues, but is not
+    generally applicable, therefore it must be explicitly enabled to avoid
+    the performance impact. By default these flags are undefined, but can be
+    enabled by uncommenting them below.
  */
-// #define SK_CRASH() *(int *)(uintptr_t)0 = 0
+// #define SK_DEBUG_GLYPH_CACHE
+// #define SK_DEBUG_PATH
 
 /*  preconfig will have attempted to determine the endianness of the system,
     but you can change these mutually exclusive flags here.
@@ -73,22 +64,15 @@
 // #define SK_CPU_BENDIAN
 // #define SK_CPU_LENDIAN
 
-/*  If zlib is available and you want to support the flate compression
-    algorithm (used in PDF generation), define SK_ZLIB_INCLUDE to be the
-    include path.
- */
-// #define SK_ZLIB_INCLUDE <zlib.h>
+/*  Most compilers use the same bit endianness for bit flags in a byte as the
+    system byte endianness, and this is the default. If for some reason this
+    needs to be overridden, specify which of the mutually exclusive flags to
+    use. For example, some atom processors in certain configurations have big
+    endian byte order but little endian bit orders.
+*/
+// #define SK_UINT8_BITFIELD_BENDIAN
+// #define SK_UINT8_BITFIELD_LENDIAN
 
-/*  Define this to allow PDF scalars above 32k.  The PDF/A spec doesn't allow
-    them, but modern PDF interpreters should handle them just fine.
- */
-// #define SK_ALLOW_LARGE_PDF_SCALARS
-
-/*  Define this to provide font subsetter for font subsetting when generating
-    PDF documents.
- */
-// #define SK_SFNTLY_SUBSETTER \
-//    "third_party/sfntly/src/cpp/src/sample/chromium/font_subsetter.h"
 
 /*  To write debug messages to a console, skia will call SkDebugf(...) following
     printf conventions (e.g. const char* format, ...). If you want to redirect
@@ -96,34 +80,52 @@
  */
 // #define SkDebugf(...)  MyFunction(__VA_ARGS__)
 
-/*  If SK_DEBUG is defined, then you can optionally define SK_SUPPORT_UNITTEST
-    which will run additional self-tests at startup. These can take a long time,
-    so this flag is optional.
+/*
+ *  To specify a different default font cache limit, define this. If this is
+ *  undefined, skia will use a built-in value.
  */
-#ifdef SK_DEBUG
-#define SK_SUPPORT_UNITTEST
-#endif
+// #define SK_DEFAULT_FONT_CACHE_LIMIT   (1024 * 1024)
 
-/* If cross process SkPictureImageFilters are not explicitly enabled then
-   they are always disabled.
+/*
+ *  To specify the default size of the image cache, undefine this and set it to
+ *  the desired value (in bytes). SkGraphics.h as a runtime API to set this
+ *  value as well. If this is undefined, a built-in value will be used.
  */
-#ifndef SK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
-#ifndef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
-#define SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
-#endif
-#endif
+// #define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024)
 
-/* If your system embeds skia and has complex event logging, define this
-   symbol to name a file that maps the following macros to your system's
-   equivalents:
-       SK_TRACE_EVENT0(event)
-       SK_TRACE_EVENT1(event, name1, value1)
-       SK_TRACE_EVENT2(event, name1, value1, name2, value2)
-   src/utils/SkDebugTrace.h has a trivial implementation that writes to
-   the debug output stream. If SK_USER_TRACE_INCLUDE_FILE is not defined,
-   SkTrace.h will define the above three macros to do nothing.
-*/
-#undef SK_USER_TRACE_INCLUDE_FILE
+/*  Define this to set the upper limit for text to support LCD. Values that
+    are very large increase the cost in the font cache and draw slower, without
+    improving readability. If this is undefined, Skia will use its default
+    value (e.g. 48)
+ */
+// #define SK_MAX_SIZE_FOR_LCDTEXT     48
+
+/*  Change the ordering to work in X windows.
+ */
+// #ifdef SK_SAMPLES_FOR_X
+//         #define SK_R32_SHIFT    16
+//         #define SK_G32_SHIFT    8
+//         #define SK_B32_SHIFT    0
+//         #define SK_A32_SHIFT    24
+// #endif
+
+
+/* Determines whether to build code that supports the GPU backend. Some classes
+   that are not GPU-specific, such as SkShader subclasses, have optional code
+   that is used allows them to interact with the GPU backend. If you'd like to
+   omit this code set SK_SUPPORT_GPU to 0. This also allows you to omit the gpu
+   directories from your include search path when you're not building the GPU
+   backend. Defaults to 1 (build the GPU code).
+ */
+// #define SK_SUPPORT_GPU 1
+
+/* Skia makes use of histogram logging macros to trace the frequency of
+ * events. By default, Skia provides no-op versions of these macros.
+ * Skia consumers can provide their own definitions of these macros to
+ * integrate with their histogram collection backend.
+ */
+// #define SK_HISTOGRAM_BOOLEAN(name, value)
+// #define SK_HISTOGRAM_ENUMERATION(name, value, boundary_value)
 
 // ===== Begin Chrome-specific definitions =====
 
@@ -133,24 +135,12 @@
 #define GR_MAX_OFFSCREEN_AA_DIM 512
 
 // Log the file and line number for assertions.
-#define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, false, __VA_ARGS__)
+#define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, __VA_ARGS__)
 SK_API void SkDebugf_FileLine(const char* file,
                               int line,
-                              bool fatal,
                               const char* format,
                               ...);
 
-// Marking the debug print as "fatal" will cause a debug break, so we don't need
-// a separate crash call here.
-#define SK_DEBUGBREAK(cond)                                           \
-  do {                                                                \
-    if (!(cond)) {                                                    \
-      SkDebugf_FileLine(__FILE__, __LINE__, true,                     \
-                        "%s:%d: failed assertion \"%s\"\n", __FILE__, \
-                        __LINE__, #cond);                             \
-    }                                                                 \
-  } while (false)
-
 #if !defined(ANDROID)  // On Android, we use the skia default settings.
 #define SK_A32_SHIFT 24
 #define SK_R32_SHIFT 16
@@ -190,11 +180,6 @@
 
 #endif
 
-// The default crash macro writes to badbeef which can cause some strange
-// problems. Instead, pipe this through to the logging function as a fatal
-// assertion.
-#define SK_CRASH() SkDebugf_FileLine(__FILE__, __LINE__, true, "SK_CRASH")
-
 // These flags are no longer defined in Skia, but we have them (temporarily)
 // until we update our call-sites (typically these are for API changes).
 //
diff --git a/skia/ext/google_logging.cc b/skia/ext/google_logging.cc
index 9906167..10d7674 100644
--- a/skia/ext/google_logging.cc
+++ b/skia/ext/google_logging.cc
@@ -11,11 +11,7 @@
 
 #include "third_party/skia/include/core/SkTypes.h"
 
-void SkDebugf_FileLine(const char* file,
-                       int line,
-                       bool fatal,
-                       const char* format,
-                       ...) {
+void SkDebugf_FileLine(const char* file, int line, const char* format, ...) {
   va_list ap;
   va_start(ap, format);
 
diff --git a/skia/features.gni b/skia/features.gni
new file mode 100644
index 0000000..47771d1
--- /dev/null
+++ b/skia/features.gni
@@ -0,0 +1,9 @@
+# Copyright 2019 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+  # Enable experimental SkiaRenderer Dawn backend.
+  skia_use_dawn = false
+  skia_use_gl = true
+}
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
new file mode 100644
index 0000000..787aa6d
--- /dev/null
+++ b/testing/BUILD.gn
@@ -0,0 +1,121 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../pdfium.gni")
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "fx_string_testhelpers.cpp",
+    "fx_string_testhelpers.h",
+    "invalid_seekable_read_stream.cpp",
+    "invalid_seekable_read_stream.h",
+    "pseudo_retainable.h",
+    "string_write_stream.cpp",
+    "string_write_stream.h",
+    "test_loader.cpp",
+    "test_loader.h",
+    "test_support.cpp",
+    "test_support.h",
+    "utils/bitmap_saver.cpp",
+    "utils/bitmap_saver.h",
+    "utils/file_util.cpp",
+    "utils/file_util.h",
+    "utils/hash.cpp",
+    "utils/hash.h",
+    "utils/path_service.cpp",
+    "utils/path_service.h",
+  ]
+  data = [ "resources/" ]
+  deps = [
+    "../:pdfium_public_headers",
+    "../core/fdrm",
+    "../core/fxcrt",
+    "../core/fxge",
+    "image_diff",
+  ]
+  configs += [ "../:pdfium_core_config" ]
+  visibility = [ "../*" ]
+
+  if (pdf_enable_v8) {
+    sources += [
+      "v8_initializer.cpp",
+      "v8_initializer.h",
+    ]
+    deps += [
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+    configs += [ "//v8:external_startup_data" ]
+  }
+}
+
+source_set("unit_test_support") {
+  testonly = true
+  sources = []
+  deps = []
+
+  configs += [ "../:pdfium_core_config" ]
+  visibility = [ "../*" ]
+
+  if (pdf_enable_xfa) {
+    sources += [
+      "xfa_unit_test_support.cpp",
+      "xfa_unit_test_support.h",
+    ]
+    deps += [
+      "../:pdfium",
+      "../core/fxge",
+      "../xfa/fgas",
+      "//testing/gtest",
+    ]
+  }
+}
+
+source_set("embedder_test_support") {
+  testonly = true
+  sources = [
+    "embedder_test.cpp",
+    "embedder_test.h",
+    "embedder_test_mock_delegate.h",
+    "embedder_test_timer_handling_delegate.h",
+    "fake_file_access.cpp",
+    "fake_file_access.h",
+    "range_set.cpp",
+    "range_set.h",
+  ]
+
+  deps = [
+    ":test_support",
+    "../:pdfium_public_headers",
+    "../core/fdrm",
+    "../core/fxcrt",
+    "../third_party:pdfium_base",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  configs += [ "../:pdfium_core_config" ]
+  visibility = [ "../*" ]
+
+  if (pdf_enable_v8) {
+    sources += [
+      "js_embedder_test.cpp",
+      "js_embedder_test.h",
+    ]
+    deps += [ "../fxjs" ]
+
+    if (pdf_enable_xfa) {
+      sources += [
+        "xfa_js_embedder_test.cpp",
+        "xfa_js_embedder_test.h",
+      ]
+      deps += [
+        "../fpdfsdk",
+        "../fpdfsdk/fpdfxfa",
+        "../xfa/fxfa",
+        "../xfa/fxfa/parser",
+      ]
+    }
+  }
+}
diff --git a/testing/SUPPRESSIONS b/testing/SUPPRESSIONS
index 1edc158..b3059e9 100644
--- a/testing/SUPPRESSIONS
+++ b/testing/SUPPRESSIONS
@@ -299,7 +299,6 @@
 
 Choose.pdf * * *
 data_binding.pdf * * *
-Date_FormCale.pdf * * *
 # TODO(npm): Add proper evt for MouseEvents.
 MouseEvents_enter.pdf * * *
 MouseEvents_exit.pdf * * *
@@ -316,8 +315,43 @@
 bug_679643.in * * noxfa
 bug_735912.in * * noxfa
 
+# xfa_specific
+
+# TODO(pdfium:1106): Remove after associated bug is fixed
+resolve_nodes_1.pdf * * *
+
 #
 # Pixel tests
 #
 bug_492.in * nov8 *
 
+# TODO(pdfium:304): Remove after associated bug is fixed
+bug_304.pdf * * *
+
+# TODO(pdfium:1331): Remove after associated bug is fixed
+bug_1331.in * * *
+
+# TODO(pdfium:1461): Remove after associated bug is fixed
+bug_1402.in win * *
+
+# TODO(pdfium:1457): Remove after associated bug is fixed
+bug_1457.in * * *
+
+# TODO(chromium:451366): Remove after associated bug is fixed
+bug_451366.in * * *
+
+# TODO(chromium:1012369): Remove after associated bug is fixed
+bug_1012369.in * * *
+
+# xfa_specific
+
+# TODO(pdfium:1107): Remove after associated bug is fixed
+standard_symbols.pdf * * *
+# TODO(pdfium:1168): Remove after associated bug is fixed
+xfa_bmp_image.in * * *
+# TODO(pdfium:1095): Remove after associated bug is fixed
+xfa_example.in win * *
+# TODO(pdfium:1167): Remove after associated bug is fixed
+xfa_gif_image.in * * *
+# TODO(pdfium:1095): Remove after associated bug is fixed
+xfa_textfield.in win * *
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index b434353..9bf4a18 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -6,22 +6,28 @@
 
 #include <limits.h>
 
-#include <fstream>
 #include <list>
+#include <map>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "core/fdrm/crypto/fx_crypt.h"
+#include "core/fdrm/fx_crypt.h"
+#include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_dataavail.h"
 #include "public/fpdf_edit.h"
 #include "public/fpdf_text.h"
 #include "public/fpdfview.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "testing/image_diff/image_diff_png.h"
-#include "testing/test_support.h"
+#include "testing/test_loader.h"
+#include "testing/utils/bitmap_saver.h"
+#include "testing/utils/file_util.h"
+#include "testing/utils/hash.h"
 #include "testing/utils/path_service.h"
+#include "third_party/base/logging.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 
 #ifdef PDF_ENABLE_V8
 #include "v8/include/v8-platform.h"
@@ -31,40 +37,31 @@
 namespace {
 
 int GetBitmapBytesPerPixel(FPDF_BITMAP bitmap) {
-  const int format = FPDFBitmap_GetFormat(bitmap);
-  switch (format) {
-    case FPDFBitmap_Gray:
-      return 1;
-    case FPDFBitmap_BGR:
-      return 3;
-    case FPDFBitmap_BGRx:
-    case FPDFBitmap_BGRA:
-      return 4;
-    default:
-      ASSERT(false);
-      return 0;
-  }
+  return EmbedderTest::BytesPerPixelForFormat(FPDFBitmap_GetFormat(bitmap));
 }
 
+#if defined(OS_WIN)
+int CALLBACK GetRecordProc(HDC hdc,
+                           HANDLETABLE* handle_table,
+                           const ENHMETARECORD* record,
+                           int objects_count,
+                           LPARAM param) {
+  auto& records = *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
+  records.push_back(record);
+  return 1;
+}
+#endif  // defined(OS_WIN)
+
 }  // namespace
 
 EmbedderTest::EmbedderTest()
-    : default_delegate_(new EmbedderTest::Delegate()),
-      document_(nullptr),
-      form_handle_(nullptr),
-      avail_(nullptr),
-      external_isolate_(nullptr),
-      loader_(nullptr),
-      file_length_(0),
-      file_contents_(nullptr) {
-  memset(&file_access_, 0, sizeof(file_access_));
-  delegate_ = default_delegate_.get();
-
+    : default_delegate_(pdfium::MakeUnique<EmbedderTest::Delegate>()),
+      delegate_(default_delegate_.get()) {
   FPDF_FILEWRITE::version = 1;
   FPDF_FILEWRITE::WriteBlock = WriteBlockCallback;
 }
 
-EmbedderTest::~EmbedderTest() {}
+EmbedderTest::~EmbedderTest() = default;
 
 void EmbedderTest::SetUp() {
   FPDF_LIBRARY_CONFIG config;
@@ -80,65 +77,96 @@
   info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline;
   FSDK_SetUnSpObjProcessHandler(info);
 
-  m_SavedDocument = nullptr;
+  saved_document_ = nullptr;
 }
 
 void EmbedderTest::TearDown() {
+  // Use an EXPECT_EQ() here and continue to let TearDown() finish as cleanly as
+  // possible. This can fail when an ASSERT test fails in a test case.
+  EXPECT_EQ(0U, page_map_.size());
+  EXPECT_EQ(0U, saved_page_map_.size());
+
   if (document_) {
     FORM_DoDocumentAAction(form_handle_, FPDFDOC_AACTION_WC);
-    FPDFDOC_ExitFormFillEnvironment(form_handle_);
-    FPDF_CloseDocument(document_);
+    CloseDocument();
   }
 
   FPDFAvail_Destroy(avail_);
   FPDF_DestroyLibrary();
-  delete loader_;
+  loader_.reset();
 }
 
+#ifdef PDF_ENABLE_V8
+void EmbedderTest::SetExternalIsolate(void* isolate) {
+  external_isolate_ = static_cast<v8::Isolate*>(isolate);
+}
+#endif  // PDF_ENABLE_V8
+
 bool EmbedderTest::CreateEmptyDocument() {
   document_ = FPDF_CreateNewDocument();
   if (!document_)
     return false;
 
-  form_handle_ = SetupFormFillEnvironment(document_);
+  form_handle_ =
+      SetupFormFillEnvironment(document_, JavaScriptOption::kEnableJavaScript);
   return true;
 }
 
 bool EmbedderTest::OpenDocument(const std::string& filename) {
-  return OpenDocumentWithOptions(filename, nullptr, false);
+  return OpenDocumentWithOptions(filename, nullptr,
+                                 LinearizeOption::kDefaultLinearize,
+                                 JavaScriptOption::kEnableJavaScript);
 }
 
 bool EmbedderTest::OpenDocumentLinearized(const std::string& filename) {
-  return OpenDocumentWithOptions(filename, nullptr, true);
+  return OpenDocumentWithOptions(filename, nullptr,
+                                 LinearizeOption::kMustLinearize,
+                                 JavaScriptOption::kEnableJavaScript);
 }
 
 bool EmbedderTest::OpenDocumentWithPassword(const std::string& filename,
                                             const char* password) {
-  return OpenDocumentWithOptions(filename, password, false);
+  return OpenDocumentWithOptions(filename, password,
+                                 LinearizeOption::kDefaultLinearize,
+                                 JavaScriptOption::kEnableJavaScript);
+}
+
+bool EmbedderTest::OpenDocumentWithoutJavaScript(const std::string& filename) {
+  return OpenDocumentWithOptions(filename, nullptr,
+                                 LinearizeOption::kDefaultLinearize,
+                                 JavaScriptOption::kDisableJavaScript);
 }
 
 bool EmbedderTest::OpenDocumentWithOptions(const std::string& filename,
                                            const char* password,
-                                           bool must_linearize) {
+                                           LinearizeOption linearize_option,
+                                           JavaScriptOption javascript_option) {
   std::string file_path;
   if (!PathService::GetTestFilePath(filename, &file_path))
     return false;
+
   file_contents_ = GetFileContents(file_path.c_str(), &file_length_);
   if (!file_contents_)
     return false;
 
   EXPECT_TRUE(!loader_);
-  loader_ = new TestLoader(file_contents_.get(), file_length_);
+  loader_ = pdfium::MakeUnique<TestLoader>(
+      pdfium::make_span(file_contents_.get(), file_length_));
+
+  memset(&file_access_, 0, sizeof(file_access_));
   file_access_.m_FileLen = static_cast<unsigned long>(file_length_);
   file_access_.m_GetBlock = TestLoader::GetBlock;
-  file_access_.m_Param = loader_;
+  file_access_.m_Param = loader_.get();
+
   fake_file_access_ = pdfium::MakeUnique<FakeFileAccess>(&file_access_);
-  return OpenDocumentHelper(password, must_linearize, fake_file_access_.get(),
-                            &document_, &avail_, &form_handle_);
+  return OpenDocumentHelper(password, linearize_option, javascript_option,
+                            fake_file_access_.get(), &document_, &avail_,
+                            &form_handle_);
 }
 
 bool EmbedderTest::OpenDocumentHelper(const char* password,
-                                      bool must_linearize,
+                                      LinearizeOption linearize_option,
+                                      JavaScriptOption javascript_option,
                                       FakeFileAccess* network_simulator,
                                       FPDF_DOCUMENT* document,
                                       FPDF_AVAIL* avail,
@@ -178,12 +206,11 @@
         nRet = FPDFAvail_IsPageAvail(*avail, i,
                                      network_simulator->GetDownloadHints());
       }
-
       if (nRet == PDF_DATA_ERROR)
         return false;
     }
   } else {
-    if (must_linearize)
+    if (linearize_option == LinearizeOption::kMustLinearize)
       return false;
     network_simulator->SetWholeFileAvailable();
     *document =
@@ -191,17 +218,27 @@
     if (!*document)
       return false;
   }
-  *form_handle = SetupFormFillEnvironment(*document);
-#ifdef PDF_ENABLE_XFA
+  *form_handle = SetupFormFillEnvironment(*document, javascript_option);
+
   int doc_type = FPDF_GetFormType(*document);
   if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND)
     FPDF_LoadXFA(*document);
-#endif  // PDF_ENABLE_XFA
+
   (void)FPDF_GetDocPermissions(*document);
   return true;
 }
 
-FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(FPDF_DOCUMENT doc) {
+void EmbedderTest::CloseDocument() {
+  FPDFDOC_ExitFormFillEnvironment(form_handle_);
+  form_handle_ = nullptr;
+
+  FPDF_CloseDocument(document_);
+  document_ = nullptr;
+}
+
+FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(
+    FPDF_DOCUMENT doc,
+    JavaScriptOption javascript_option) {
   IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this);
   memset(platform, '\0', sizeof(IPDF_JSPLATFORM));
   platform->version = 2;
@@ -218,12 +255,14 @@
   formfillinfo->FFI_SetTimer = SetTimerTrampoline;
   formfillinfo->FFI_KillTimer = KillTimerTrampoline;
   formfillinfo->FFI_GetPage = GetPageTrampoline;
-  formfillinfo->m_pJsPlatform = platform;
+  formfillinfo->FFI_DoURIAction = DoURIActionTrampoline;
+
+  if (javascript_option == JavaScriptOption::kEnableJavaScript)
+    formfillinfo->m_pJsPlatform = platform;
+
   FPDF_FORMHANDLE form_handle =
       FPDFDOC_InitFormFillEnvironment(doc, formfillinfo);
-  FPDF_SetFormFieldHighlightColor(form_handle, FPDF_FORMFIELD_UNKNOWN,
-                                  0xFFE4DD);
-  FPDF_SetFormFieldHighlightAlpha(form_handle, 100);
+  SetInitialFormFieldHighlight(form_handle);
   return form_handle;
 }
 
@@ -249,116 +288,261 @@
 }
 
 FPDF_PAGE EmbedderTest::LoadPage(int page_number) {
+  return LoadPageCommon(page_number, true);
+}
+
+FPDF_PAGE EmbedderTest::LoadPageNoEvents(int page_number) {
+  return LoadPageCommon(page_number, false);
+}
+
+FPDF_PAGE EmbedderTest::LoadPageCommon(int page_number, bool do_events) {
   ASSERT(form_handle_);
-  // First check whether it is loaded already.
-  auto it = page_map_.find(page_number);
-  if (it != page_map_.end())
-    return it->second;
+  ASSERT(page_number >= 0);
+  ASSERT(!pdfium::ContainsKey(page_map_, page_number));
 
   FPDF_PAGE page = FPDF_LoadPage(document_, page_number);
   if (!page)
     return nullptr;
 
-  FORM_OnAfterLoadPage(page, form_handle_);
-  FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN);
-  // Cache the page.
+  if (do_events) {
+    FORM_OnAfterLoadPage(page, form_handle_);
+    FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN);
+  }
   page_map_[page_number] = page;
-  page_reverse_map_[page] = page_number;
   return page;
 }
 
-FPDF_BITMAP EmbedderTest::RenderPage(FPDF_PAGE page) {
-  return RenderPageWithFlags(page, form_handle_, 0);
+void EmbedderTest::UnloadPage(FPDF_PAGE page) {
+  UnloadPageCommon(page, true);
 }
 
-FPDF_BITMAP EmbedderTest::RenderPageWithFlags(FPDF_PAGE page,
-                                              FPDF_FORMHANDLE handle,
-                                              int flags) {
-  int width = static_cast<int>(FPDF_GetPageWidth(page));
-  int height = static_cast<int>(FPDF_GetPageHeight(page));
+void EmbedderTest::UnloadPageNoEvents(FPDF_PAGE page) {
+  UnloadPageCommon(page, false);
+}
+
+void EmbedderTest::UnloadPageCommon(FPDF_PAGE page, bool do_events) {
+  ASSERT(form_handle_);
+  int page_number = GetPageNumberForLoadedPage(page);
+  if (page_number < 0) {
+    NOTREACHED();
+    return;
+  }
+  if (do_events) {
+    FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE);
+    FORM_OnBeforeClosePage(page, form_handle_);
+  }
+  FPDF_ClosePage(page);
+  page_map_.erase(page_number);
+}
+
+void EmbedderTest::SetInitialFormFieldHighlight(FPDF_FORMHANDLE form) {
+  FPDF_SetFormFieldHighlightColor(form, FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD);
+  FPDF_SetFormFieldHighlightAlpha(form, 100);
+}
+
+ScopedFPDFBitmap EmbedderTest::RenderLoadedPage(FPDF_PAGE page) {
+  return RenderLoadedPageWithFlags(page, 0);
+}
+
+ScopedFPDFBitmap EmbedderTest::RenderLoadedPageWithFlags(FPDF_PAGE page,
+                                                         int flags) {
+  if (GetPageNumberForLoadedPage(page) < 0) {
+    NOTREACHED();
+    return nullptr;
+  }
+  return RenderPageWithFlags(page, form_handle_, flags);
+}
+
+ScopedFPDFBitmap EmbedderTest::RenderSavedPage(FPDF_PAGE page) {
+  return RenderSavedPageWithFlags(page, 0);
+}
+
+ScopedFPDFBitmap EmbedderTest::RenderSavedPageWithFlags(FPDF_PAGE page,
+                                                        int flags) {
+  if (GetPageNumberForSavedPage(page) < 0) {
+    NOTREACHED();
+    return nullptr;
+  }
+  return RenderPageWithFlags(page, saved_form_handle_, flags);
+}
+
+// static
+ScopedFPDFBitmap EmbedderTest::RenderPageWithFlags(FPDF_PAGE page,
+                                                   FPDF_FORMHANDLE handle,
+                                                   int flags) {
+  int width = static_cast<int>(FPDF_GetPageWidthF(page));
+  int height = static_cast<int>(FPDF_GetPageHeightF(page));
   int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
-  FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, alpha);
+  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, alpha));
   FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
-  FPDFBitmap_FillRect(bitmap, 0, 0, width, height, fill_color);
-  FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, flags);
-  FPDF_FFLDraw(handle, bitmap, page, 0, 0, width, height, 0, flags);
+  FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color);
+  FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0, flags);
+  FPDF_FFLDraw(handle, bitmap.get(), page, 0, 0, width, height, 0, flags);
   return bitmap;
 }
 
-void EmbedderTest::UnloadPage(FPDF_PAGE page) {
-  ASSERT(form_handle_);
-  FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE);
-  FORM_OnBeforeClosePage(page, form_handle_);
-  FPDF_ClosePage(page);
-
-  auto it = page_reverse_map_.find(page);
-  if (it == page_reverse_map_.end())
-    return;
-
-  page_map_.erase(it->second);
-  page_reverse_map_.erase(it);
+// static
+ScopedFPDFBitmap EmbedderTest::RenderPage(FPDF_PAGE page) {
+  return RenderPageWithFlags(page, nullptr, 0);
 }
 
-FPDF_DOCUMENT EmbedderTest::OpenSavedDocument(const char* password) {
+#if defined(OS_WIN)
+// static
+std::vector<uint8_t> EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page,
+                                                            int flags) {
+  HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
+
+  int width = static_cast<int>(FPDF_GetPageWidthF(page));
+  int height = static_cast<int>(FPDF_GetPageHeightF(page));
+  HRGN rgn = CreateRectRgn(0, 0, width, height);
+  SelectClipRgn(dc, rgn);
+  DeleteObject(rgn);
+
+  SelectObject(dc, GetStockObject(NULL_PEN));
+  SelectObject(dc, GetStockObject(WHITE_BRUSH));
+  // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
+  Rectangle(dc, 0, 0, width + 1, height + 1);
+
+  FPDF_RenderPage(dc, page, 0, 0, width, height, 0, flags);
+
+  HENHMETAFILE emf = CloseEnhMetaFile(dc);
+  size_t size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr);
+  std::vector<uint8_t> buffer(size_in_bytes);
+  GetEnhMetaFileBits(emf, size_in_bytes, buffer.data());
+  DeleteEnhMetaFile(emf);
+  return buffer;
+}
+
+// static
+std::string EmbedderTest::GetPostScriptFromEmf(
+    pdfium::span<const uint8_t> emf_data) {
+  // This comes from Emf::InitFromData() in Chromium.
+  HENHMETAFILE emf = SetEnhMetaFileBits(emf_data.size(), emf_data.data());
+  if (!emf)
+    return std::string();
+
+  // This comes from Emf::Enumerator::Enumerator() in Chromium.
+  std::vector<const ENHMETARECORD*> records;
+  if (!EnumEnhMetaFile(nullptr, emf, &GetRecordProc, &records, nullptr)) {
+    DeleteEnhMetaFile(emf);
+    return std::string();
+  }
+
+  // This comes from PostScriptMetaFile::SafePlayback() in Chromium.
+  std::string ps_data;
+  for (const auto* record : records) {
+    if (record->iType != EMR_GDICOMMENT)
+      continue;
+
+    // PostScript data is encapsulated inside EMF comment records.
+    // The first two bytes of the comment indicate the string length. The rest
+    // is the actual string data.
+    const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
+    const char* data = reinterpret_cast<const char*>(comment->Data);
+    uint16_t size = *reinterpret_cast<const uint16_t*>(data);
+    data += 2;
+    ps_data.append(data, size);
+  }
+  DeleteEnhMetaFile(emf);
+  return ps_data;
+}
+#endif  // defined(OS_WIN)
+
+FPDF_DOCUMENT EmbedderTest::OpenSavedDocument() {
+  return OpenSavedDocumentWithPassword(nullptr);
+}
+
+// static
+int EmbedderTest::BytesPerPixelForFormat(int format) {
+  switch (format) {
+    case FPDFBitmap_Gray:
+      return 1;
+    case FPDFBitmap_BGR:
+      return 3;
+    case FPDFBitmap_BGRx:
+    case FPDFBitmap_BGRA:
+      return 4;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+FPDF_DOCUMENT EmbedderTest::OpenSavedDocumentWithPassword(
+    const char* password) {
   memset(&saved_file_access_, 0, sizeof(saved_file_access_));
-  saved_file_access_.m_FileLen = m_String.size();
+  saved_file_access_.m_FileLen = data_string_.size();
   saved_file_access_.m_GetBlock = GetBlockFromString;
-  saved_file_access_.m_Param = &m_String;
+  // Copy data to prevent clearing it before saved document close.
+  saved_document_file_data_ = data_string_;
+  saved_file_access_.m_Param = &saved_document_file_data_;
 
   saved_fake_file_access_ =
       pdfium::MakeUnique<FakeFileAccess>(&saved_file_access_);
 
-  EXPECT_TRUE(OpenDocumentHelper(password, false, saved_fake_file_access_.get(),
-                                 &m_SavedDocument, &m_SavedAvail,
-                                 &m_SavedForm));
-  return m_SavedDocument;
+  EXPECT_TRUE(OpenDocumentHelper(
+      password, LinearizeOption::kDefaultLinearize,
+      JavaScriptOption::kEnableJavaScript, saved_fake_file_access_.get(),
+      &saved_document_, &saved_avail_, &saved_form_handle_));
+  return saved_document_;
 }
 
 void EmbedderTest::CloseSavedDocument() {
-  ASSERT(m_SavedDocument);
+  ASSERT(saved_document_);
 
-  FPDFDOC_ExitFormFillEnvironment(m_SavedForm);
-  FPDF_CloseDocument(m_SavedDocument);
-  FPDFAvail_Destroy(m_SavedAvail);
+  FPDFDOC_ExitFormFillEnvironment(saved_form_handle_);
+  FPDF_CloseDocument(saved_document_);
+  FPDFAvail_Destroy(saved_avail_);
 
-  m_SavedForm = nullptr;
-  m_SavedDocument = nullptr;
-  m_SavedAvail = nullptr;
+  saved_form_handle_ = nullptr;
+  saved_document_ = nullptr;
+  saved_avail_ = nullptr;
 }
 
 FPDF_PAGE EmbedderTest::LoadSavedPage(int page_number) {
-  ASSERT(m_SavedDocument);
+  ASSERT(saved_form_handle_);
+  ASSERT(page_number >= 0);
+  ASSERT(!pdfium::ContainsKey(saved_page_map_, page_number));
 
-  EXPECT_LT(page_number, FPDF_GetPageCount(m_SavedDocument));
-  FPDF_PAGE page = FPDF_LoadPage(m_SavedDocument, page_number);
+  FPDF_PAGE page = FPDF_LoadPage(saved_document_, page_number);
+  if (!page)
+    return nullptr;
 
-  ASSERT(page);
+  FORM_OnAfterLoadPage(page, saved_form_handle_);
+  FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_OPEN);
+  saved_page_map_[page_number] = page;
   return page;
 }
 
-FPDF_BITMAP EmbedderTest::RenderSavedPage(FPDF_PAGE page) {
-  return RenderPageWithFlags(page, m_SavedForm, 0);
-}
-
 void EmbedderTest::CloseSavedPage(FPDF_PAGE page) {
-  ASSERT(page);
+  ASSERT(saved_form_handle_);
+
+  int page_number = GetPageNumberForSavedPage(page);
+  if (page_number < 0) {
+    NOTREACHED();
+    return;
+  }
+
+  FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page, saved_form_handle_);
   FPDF_ClosePage(page);
+
+  saved_page_map_.erase(page_number);
 }
 
 void EmbedderTest::VerifySavedRendering(FPDF_PAGE page,
                                         int width,
                                         int height,
                                         const char* md5) {
-  ASSERT(m_SavedDocument);
+  ASSERT(saved_document_);
   ASSERT(page);
 
-  FPDF_BITMAP new_bitmap = RenderPageWithFlags(page, m_SavedForm, FPDF_ANNOT);
-  CompareBitmap(new_bitmap, width, height, md5);
-  FPDFBitmap_Destroy(new_bitmap);
+  ScopedFPDFBitmap bitmap = RenderSavedPageWithFlags(page, FPDF_ANNOT);
+  CompareBitmap(bitmap.get(), width, height, md5);
 }
 
 void EmbedderTest::VerifySavedDocument(int width, int height, const char* md5) {
-  OpenSavedDocument();
+  ASSERT_TRUE(OpenSavedDocument());
   FPDF_PAGE page = LoadSavedPage(0);
   VerifySavedRendering(page, width, height, md5);
   CloseSavedPage(page);
@@ -418,13 +602,26 @@
 }
 
 // static
+void EmbedderTest::DoURIActionTrampoline(FPDF_FORMFILLINFO* info,
+                                         FPDF_BYTESTRING uri) {
+  EmbedderTest* test = static_cast<EmbedderTest*>(info);
+  return test->delegate_->DoURIAction(uri);
+}
+
+// static
 std::string EmbedderTest::HashBitmap(FPDF_BITMAP bitmap) {
+  int stride = FPDFBitmap_GetStride(bitmap);
+  int usable_bytes_per_row =
+      GetBitmapBytesPerPixel(bitmap) * FPDFBitmap_GetWidth(bitmap);
+  int height = FPDFBitmap_GetHeight(bitmap);
+  auto span = pdfium::make_span(
+      static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap)), stride * height);
+
+  CRYPT_md5_context context = CRYPT_MD5Start();
+  for (int i = 0; i < height; ++i)
+    CRYPT_MD5Update(&context, span.subspan(i * stride, usable_bytes_per_row));
   uint8_t digest[16];
-  CRYPT_MD5Generate(static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap)),
-                    FPDFBitmap_GetWidth(bitmap) *
-                        GetBitmapBytesPerPixel(bitmap) *
-                        FPDFBitmap_GetHeight(bitmap),
-                    digest);
+  CRYPT_MD5Finish(&context, digest);
   return CryptToBase16(digest);
 }
 
@@ -432,25 +629,7 @@
 // static
 void EmbedderTest::WriteBitmapToPng(FPDF_BITMAP bitmap,
                                     const std::string& filename) {
-  const int stride = FPDFBitmap_GetStride(bitmap);
-  const int width = FPDFBitmap_GetWidth(bitmap);
-  const int height = FPDFBitmap_GetHeight(bitmap);
-  const auto* buffer =
-      static_cast<const unsigned char*>(FPDFBitmap_GetBuffer(bitmap));
-
-  std::vector<unsigned char> png_encoding;
-  bool encoded = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride,
-                                               false, &png_encoding);
-
-  ASSERT_TRUE(encoded);
-  ASSERT_LT(filename.size(), 256u);
-
-  std::ofstream png_file;
-  png_file.open(filename, std::ios_base::out | std::ios_base::binary);
-  png_file.write(reinterpret_cast<char*>(&png_encoding.front()),
-                 png_encoding.size());
-  ASSERT_TRUE(png_file.good());
-  png_file.close();
+  BitmapSaver::WriteBitmapToPng(bitmap, filename);
 }
 #endif
 
@@ -479,7 +658,12 @@
                                      const void* data,
                                      unsigned long size) {
   EmbedderTest* pThis = static_cast<EmbedderTest*>(pFileWrite);
-  pThis->m_String.append(static_cast<const char*>(data), size);
+
+  pThis->data_string_.append(static_cast<const char*>(data), size);
+
+  if (pThis->filestream_.is_open())
+    pThis->filestream_.write(static_cast<const char*>(data), size);
+
   return 1;
 }
 
@@ -489,13 +673,47 @@
                                      unsigned char* buf,
                                      unsigned long size) {
   std::string* new_file = static_cast<std::string*>(param);
-  if (!new_file || pos + size < pos)
+  if (!new_file || pos + size < pos) {
+    NOTREACHED();
     return 0;
+  }
 
-  unsigned long file_size = new_file->size();
-  if (pos + size > file_size)
+  if (pos + size > new_file->size()) {
+    NOTREACHED();
     return 0;
+  }
 
   memcpy(buf, new_file->data() + pos, size);
   return 1;
 }
+
+// static
+int EmbedderTest::GetPageNumberForPage(const PageNumberToHandleMap& page_map,
+                                       FPDF_PAGE page) {
+  for (const auto& it : page_map) {
+    if (it.second == page) {
+      int page_number = it.first;
+      ASSERT(page_number >= 0);
+      return page_number;
+    }
+  }
+  return -1;
+}
+
+int EmbedderTest::GetPageNumberForLoadedPage(FPDF_PAGE page) const {
+  return GetPageNumberForPage(page_map_, page);
+}
+
+int EmbedderTest::GetPageNumberForSavedPage(FPDF_PAGE page) const {
+  return GetPageNumberForPage(saved_page_map_, page);
+}
+
+#ifndef NDEBUG
+void EmbedderTest::OpenPDFFileForWrite(const std::string& filename) {
+  filestream_.open(filename, std::ios_base::binary);
+}
+
+void EmbedderTest::ClosePDFFileForWrite() {
+  filestream_.close();
+}
+#endif
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index 98432e3..662333c 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -5,22 +5,23 @@
 #ifndef TESTING_EMBEDDER_TEST_H_
 #define TESTING_EMBEDDER_TEST_H_
 
+#include <fstream>
 #include <map>
 #include <memory>
 #include <string>
+#include <vector>
 
+#include "build/build_config.h"
+#include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_dataavail.h"
 #include "public/fpdf_ext.h"
 #include "public/fpdf_formfill.h"
 #include "public/fpdf_save.h"
 #include "public/fpdfview.h"
 #include "testing/fake_file_access.h"
+#include "testing/free_deleter.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-
-#ifdef PDF_ENABLE_V8
-#include "v8/include/v8.h"
-#endif  // PDF_ENABLE_v8
+#include "third_party/base/span.h"
 
 class TestLoader;
 
@@ -32,9 +33,12 @@
                      public FPDF_FORMFILLINFO,
                      public FPDF_FILEWRITE {
  public:
+  enum class LinearizeOption { kDefaultLinearize, kMustLinearize };
+  enum class JavaScriptOption { kDisableJavaScript, kEnableJavaScript };
+
   class Delegate {
    public:
-    virtual ~Delegate() {}
+    virtual ~Delegate() = default;
 
     // Equivalent to UNSUPPORT_INFO::FSDK_UnSupport_Handler().
     virtual void UnsupportedHandler(int type) {}
@@ -57,6 +61,9 @@
     virtual FPDF_PAGE GetPage(FPDF_FORMFILLINFO* info,
                               FPDF_DOCUMENT document,
                               int page_index);
+
+    // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIAction().
+    virtual void DoURIAction(FPDF_BYTESTRING uri) {}
   };
 
   EmbedderTest();
@@ -67,36 +74,42 @@
 
 #ifdef PDF_ENABLE_V8
   // Call before SetUp to pass shared isolate, otherwise PDFium creates one.
-  void SetExternalIsolate(void* isolate) {
-    external_isolate_ = static_cast<v8::Isolate*>(isolate);
-  }
+  void SetExternalIsolate(void* isolate);
 #endif  // PDF_ENABLE_V8
 
   void SetDelegate(Delegate* delegate) {
     delegate_ = delegate ? delegate : default_delegate_.get();
   }
 
-  FPDF_DOCUMENT document() { return document_; }
-  FPDF_FORMHANDLE form_handle() { return form_handle_; }
+  FPDF_DOCUMENT document() const { return document_; }
+  FPDF_FORMHANDLE form_handle() const { return form_handle_; }
 
   // Create an empty document, and its form fill environment. Returns true
   // on success or false on failure.
   bool CreateEmptyDocument();
 
   // Open the document specified by |filename|, and create its form fill
-  // environment, or return false on failure.
-  // The filename is relative to the test data directory where we store all the
-  // test files.
-  // |password| can be nullptr if there is none.
+  // environment, or return false on failure. The |filename| is relative to
+  // the test data directory where we store all the test files. |password| can
+  // be nullptr if the file is not password protected. If |javascript_opts|
+  // is kDisableJavascript, then the document will be given stubs in place
+  // of the real JS engine.
   virtual bool OpenDocumentWithOptions(const std::string& filename,
                                        const char* password,
-                                       bool must_linearize);
+                                       LinearizeOption linearize_option,
+                                       JavaScriptOption javascript_option);
 
   // Variants provided for convenience.
   bool OpenDocument(const std::string& filename);
   bool OpenDocumentLinearized(const std::string& filename);
   bool OpenDocumentWithPassword(const std::string& filename,
                                 const char* password);
+  bool OpenDocumentWithoutJavaScript(const std::string& filename);
+
+  // Close the document from a previous OpenDocument() call. This happens
+  // automatically at tear-down, and is usually not explicitly required,
+  // unless testing multiple documents or duplicate destruction.
+  void CloseDocument();
 
   // Perform JavaScript actions that are to run at document open time.
   void DoOpenActions();
@@ -105,38 +118,92 @@
   int GetFirstPageNum();
   int GetPageCount();
 
-  // Load a specific page of the open document.
+  // Load a specific page of the open document with a given non-negative
+  // |page_number|. On success, fire form events for the page and return a page
+  // handle. On failure, return nullptr.
+  // The caller does not own the returned page handle, but must call
+  // UnloadPage() on it when done.
+  // The caller cannot call this for a |page_number| if it already obtained and
+  // holds the page handle for that page.
   FPDF_PAGE LoadPage(int page_number);
 
-  // Convert a loaded page into a bitmap.
-  FPDF_BITMAP RenderPage(FPDF_PAGE page);
+  // Same as LoadPage(), but does not fire form events.
+  FPDF_PAGE LoadPageNoEvents(int page_number);
 
-  // Convert a loaded page into a bitmap with page rendering flags specified.
-  // See public/fpdfview.h for a list of page rendering flags.
-  FPDF_BITMAP RenderPageWithFlags(FPDF_PAGE page,
-                                  FPDF_FORMHANDLE handle,
-                                  int flags);
-
-  // Relese the resources obtained from LoadPage(). Further use of |page|
-  // is prohibited after this call is made.
+  // Fire form unload events and release the resources for a |page| obtained
+  // from LoadPage(). Further use of |page| is prohibited after calling this.
   void UnloadPage(FPDF_PAGE page);
 
+  // Same as UnloadPage(), but does not fire form events.
+  void UnloadPageNoEvents(FPDF_PAGE page);
+
+  // Apply standard highlighting color/alpha to forms.
+  void SetInitialFormFieldHighlight(FPDF_FORMHANDLE form);
+
+  // RenderLoadedPageWithFlags() with no flags.
+  ScopedFPDFBitmap RenderLoadedPage(FPDF_PAGE page);
+
+  // Convert |page| loaded via LoadPage() into a bitmap with the specified page
+  // rendering |flags|.
+  //
+  // See public/fpdfview.h for a list of page rendering flags.
+  ScopedFPDFBitmap RenderLoadedPageWithFlags(FPDF_PAGE page, int flags);
+
+  // RenderSavedPageWithFlags() with no flags.
+  ScopedFPDFBitmap RenderSavedPage(FPDF_PAGE page);
+
+  // Convert |page| loaded via LoadSavedPage() into a bitmap with the specified
+  // page rendering |flags|.
+  //
+  // See public/fpdfview.h for a list of page rendering flags.
+  ScopedFPDFBitmap RenderSavedPageWithFlags(FPDF_PAGE page, int flags);
+
+  // Convert |page| into a bitmap with the specified page rendering |flags|.
+  // The form handle associated with |page| should be passed in via |handle|.
+  // If |handle| is nullptr, then forms on the page will not be rendered.
+  //
+  // See public/fpdfview.h for a list of page rendering flags.
+  // If none of the above Render methods are appropriate, then use this one.
+  static ScopedFPDFBitmap RenderPageWithFlags(FPDF_PAGE page,
+                                              FPDF_FORMHANDLE handle,
+                                              int flags);
+
+  // Simplified form of RenderPageWithFlags() with no handle and no flags.
+  static ScopedFPDFBitmap RenderPage(FPDF_PAGE page);
+
+#if defined(OS_WIN)
+  // Convert |page| into EMF with the specified page rendering |flags|.
+  static std::vector<uint8_t> RenderPageWithFlagsToEmf(FPDF_PAGE page,
+                                                       int flags);
+
+  // Get the PostScript data from |emf_data|.
+  static std::string GetPostScriptFromEmf(pdfium::span<const uint8_t> emf_data);
+#endif  // defined(OS_WIN)
+
+  // Return bytes for each of the FPDFBitmap_* format types.
+  static int BytesPerPixelForFormat(int format);
+
  protected:
+  using PageNumberToHandleMap = std::map<int, FPDF_PAGE>;
+
   bool OpenDocumentHelper(const char* password,
-                          bool must_linearize,
+                          LinearizeOption linearize_option,
+                          JavaScriptOption javascript_option,
                           FakeFileAccess* network_simulator,
                           FPDF_DOCUMENT* document,
                           FPDF_AVAIL* avail,
                           FPDF_FORMHANDLE* form_handle);
 
-  FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc);
+  FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc,
+                                           JavaScriptOption javascript_option);
 
-  // Return the hash of |bitmap|.
+  // Return the hash of only the pixels in |bitmap|. i.e. Excluding the gap, if
+  // any, at the end of a row where the stride is larger than width * bpp.
   static std::string HashBitmap(FPDF_BITMAP bitmap);
 
 #ifndef NDEBUG
   // For debugging purposes.
-  // Write |bitmap| to a png file.
+  // Write |bitmap| as a PNG to |filename|.
   static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename);
 #endif
 
@@ -146,19 +213,21 @@
                             int expected_height,
                             const char* expected_md5sum);
 
-  void ClearString() { m_String.clear(); }
-  const std::string& GetString() const { return m_String; }
+  void ClearString() { data_string_.clear(); }
+  const std::string& GetString() const { return data_string_; }
 
   static int GetBlockFromString(void* param,
                                 unsigned long pos,
                                 unsigned char* buf,
                                 unsigned long size);
 
-  FPDF_DOCUMENT OpenSavedDocument(const char* password = nullptr);
+  // See comments in the respective non-Saved versions of these methods.
+  FPDF_DOCUMENT OpenSavedDocument();
+  FPDF_DOCUMENT OpenSavedDocumentWithPassword(const char* password);
   void CloseSavedDocument();
   FPDF_PAGE LoadSavedPage(int page_number);
-  FPDF_BITMAP RenderSavedPage(FPDF_PAGE page);
   void CloseSavedPage(FPDF_PAGE page);
+
   void VerifySavedRendering(FPDF_PAGE page,
                             int width,
                             int height,
@@ -167,25 +236,37 @@
 
   void SetWholeFileAvailable();
 
-  Delegate* delegate_;
+#ifndef NDEBUG
+  // For debugging purposes.
+  // While open, write any data that gets passed to WriteBlockCallback() to
+  // |filename|. This is typically used to capture data from FPDF_SaveAsCopy()
+  // calls.
+  void OpenPDFFileForWrite(const std::string& filename);
+  void ClosePDFFileForWrite();
+#endif
+
   std::unique_ptr<Delegate> default_delegate_;
-  FPDF_DOCUMENT document_;
-  FPDF_FORMHANDLE form_handle_;
-  FPDF_AVAIL avail_;
-  FPDF_FILEACCESS file_access_;  // must outlive avail_.
-  void* external_isolate_;
-  TestLoader* loader_;
-  size_t file_length_;
+  Delegate* delegate_;
+
+  FPDF_DOCUMENT document_ = nullptr;
+  FPDF_FORMHANDLE form_handle_ = nullptr;
+  FPDF_AVAIL avail_ = nullptr;
+  FPDF_FILEACCESS file_access_;                       // must outlive |avail_|.
+  std::unique_ptr<FakeFileAccess> fake_file_access_;  // must outlive |avail_|.
+
+  void* external_isolate_ = nullptr;
+  std::unique_ptr<TestLoader> loader_;
+  size_t file_length_ = 0;
   std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
-  std::map<int, FPDF_PAGE> page_map_;
-  std::map<FPDF_PAGE, int> page_reverse_map_;
-  FPDF_DOCUMENT m_SavedDocument;
-  FPDF_FORMHANDLE m_SavedForm;
-  FPDF_AVAIL m_SavedAvail;
-  FPDF_FILEACCESS saved_file_access_;  // must outlive m_SavedAvail.
-  std::unique_ptr<FakeFileAccess> fake_file_access_;  // must outlive avail_.
-  std::unique_ptr<FakeFileAccess>
-      saved_fake_file_access_;  // must outlive m_SavedAvail.
+  PageNumberToHandleMap page_map_;
+
+  FPDF_DOCUMENT saved_document_ = nullptr;
+  FPDF_FORMHANDLE saved_form_handle_ = nullptr;
+  FPDF_AVAIL saved_avail_ = nullptr;
+  FPDF_FILEACCESS saved_file_access_;  // must outlive |saved_avail_|.
+  // must outlive |saved_avail_|.
+  std::unique_ptr<FakeFileAccess> saved_fake_file_access_;
+  PageNumberToHandleMap saved_page_map_;
 
  private:
   static void UnsupportedHandlerTrampoline(UNSUPPORT_INFO*, int type);
@@ -201,11 +282,28 @@
   static FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info,
                                      FPDF_DOCUMENT document,
                                      int page_index);
+  static void DoURIActionTrampoline(FPDF_FORMFILLINFO* info,
+                                    FPDF_BYTESTRING uri);
   static int WriteBlockCallback(FPDF_FILEWRITE* pFileWrite,
                                 const void* data,
                                 unsigned long size);
 
-  std::string m_String;
+  // Helper method for the methods below.
+  static int GetPageNumberForPage(const PageNumberToHandleMap& page_map,
+                                  FPDF_PAGE page);
+  // Find |page| inside |page_map_| and return the associated page number, or -1
+  // if |page| cannot be found.
+  int GetPageNumberForLoadedPage(FPDF_PAGE page) const;
+
+  // Same as GetPageNumberForLoadedPage(), but with |saved_page_map_|.
+  int GetPageNumberForSavedPage(FPDF_PAGE page) const;
+
+  void UnloadPageCommon(FPDF_PAGE page, bool do_events);
+  FPDF_PAGE LoadPageCommon(int page_number, bool do_events);
+
+  std::string data_string_;
+  std::string saved_document_file_data_;
+  std::ofstream filestream_;
 };
 
 #endif  // TESTING_EMBEDDER_TEST_H_
diff --git a/testing/embedder_test_main.cpp b/testing/embedder_test_main.cpp
index ade6118..34dfb06 100644
--- a/testing/embedder_test_main.cpp
+++ b/testing/embedder_test_main.cpp
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <string>
 
 #include "core/fxcrt/fx_memory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 
 #ifdef PDF_ENABLE_V8
+#include "testing/v8_initializer.h"
 #include "v8/include/v8-platform.h"
 #include "v8/include/v8.h"
 #endif  // PDF_ENABLE_v8
@@ -20,7 +21,6 @@
 
 #ifdef PDF_ENABLE_V8
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-v8::StartupData* g_v8_natives = nullptr;
 v8::StartupData* g_v8_snapshot = nullptr;
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 #endif  // PDF_ENABLE_V8
@@ -28,22 +28,21 @@
 // The loading time of the CFGAS_FontMgr is linear in the number of times it is
 // loaded. So, if a test suite has a lot of tests that need a font manager they
 // can end up executing very, very slowly.
-class Environment : public testing::Environment {
+class Environment final : public testing::Environment {
  public:
   void SetUp() override {
 #ifdef PDF_ENABLE_V8
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-    if (g_v8_natives && g_v8_snapshot) {
-      InitializeV8ForPDFium(g_exe_path, std::string(), nullptr, nullptr,
-                            &platform_);
+    if (g_v8_snapshot) {
+      platform_ = InitializeV8ForPDFiumWithStartupData(g_exe_path,
+                                                       std::string(), nullptr);
     } else {
-      g_v8_natives = new v8::StartupData;
       g_v8_snapshot = new v8::StartupData;
-      InitializeV8ForPDFium(g_exe_path, std::string(), g_v8_natives,
-                            g_v8_snapshot, &platform_);
+      platform_ = InitializeV8ForPDFiumWithStartupData(
+          g_exe_path, std::string(), g_v8_snapshot);
     }
 #else
-    InitializeV8ForPDFium(g_exe_path, &platform_);
+    platform_ = InitializeV8ForPDFium(g_exe_path);
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 #endif  // FPDF_ENABLE_V8
   }
@@ -51,13 +50,12 @@
   void TearDown() override {
 #ifdef PDF_ENABLE_V8
     v8::V8::ShutdownPlatform();
-    delete platform_;
 #endif  // PDF_ENABLE_V8
   }
 
  private:
 #ifdef PDF_ENABLE_V8
-  v8::Platform* platform_;
+  std::unique_ptr<v8::Platform> platform_;
 #endif  // PDF_ENABLE_V8
 };
 
@@ -83,8 +81,6 @@
 
 #ifdef PDF_ENABLE_V8
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  if (g_v8_natives)
-    free(const_cast<char*>(g_v8_natives->data));
   if (g_v8_snapshot)
     free(const_cast<char*>(g_v8_snapshot->data));
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
diff --git a/testing/embedder_test_mock_delegate.h b/testing/embedder_test_mock_delegate.h
index 4d43b5c..c3f2820 100644
--- a/testing/embedder_test_mock_delegate.h
+++ b/testing/embedder_test_mock_delegate.h
@@ -8,7 +8,7 @@
 #include "testing/embedder_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-class EmbedderTestMockDelegate : public EmbedderTest::Delegate {
+class EmbedderTestMockDelegate final : public EmbedderTest::Delegate {
  public:
   MOCK_METHOD1(UnsupportedHandler, void(int type));
   MOCK_METHOD4(
diff --git a/testing/embedder_test_timer_handling_delegate.h b/testing/embedder_test_timer_handling_delegate.h
index 3196615..a32ad20 100644
--- a/testing/embedder_test_timer_handling_delegate.h
+++ b/testing/embedder_test_timer_handling_delegate.h
@@ -11,9 +11,9 @@
 #include <vector>
 
 #include "testing/embedder_test.h"
-#include "testing/test_support.h"
+#include "testing/fx_string_testhelpers.h"
 
-class EmbedderTestTimerHandlingDelegate : public EmbedderTest::Delegate {
+class EmbedderTestTimerHandlingDelegate final : public EmbedderTest::Delegate {
  public:
   struct AlertRecord {
     std::wstring message;
@@ -77,7 +77,7 @@
 
   void SetFailNextTimer() { fail_next_timer_ = true; }
 
- protected:
+ private:
   std::multimap<int, Timer> expiry_to_timer_map_;  // Keyed by timeout.
   bool fail_next_timer_ = false;
   int next_timer_id_ = 0;
diff --git a/testing/fake_file_access.cpp b/testing/fake_file_access.cpp
index c69f278..723f772 100644
--- a/testing/fake_file_access.cpp
+++ b/testing/fake_file_access.cpp
@@ -14,7 +14,7 @@
 
 namespace {
 
-class FileAccessWrapper : public FPDF_FILEACCESS {
+class FileAccessWrapper final : public FPDF_FILEACCESS {
  public:
   explicit FileAccessWrapper(FakeFileAccess* simulator)
       : simulator_(simulator) {
@@ -32,10 +32,10 @@
   }
 
  private:
-  fxcrt::UnownedPtr<FakeFileAccess> simulator_;
+  UnownedPtr<FakeFileAccess> simulator_;
 };
 
-class FileAvailImpl : public FX_FILEAVAIL {
+class FileAvailImpl final : public FX_FILEAVAIL {
  public:
   explicit FileAvailImpl(FakeFileAccess* simulator) : simulator_(simulator) {
     version = 1;
@@ -50,10 +50,10 @@
   }
 
  private:
-  fxcrt::UnownedPtr<FakeFileAccess> simulator_;
+  UnownedPtr<FakeFileAccess> simulator_;
 };
 
-class DownloadHintsImpl : public FX_DOWNLOADHINTS {
+class DownloadHintsImpl final : public FX_DOWNLOADHINTS {
  public:
   explicit DownloadHintsImpl(FakeFileAccess* simulator)
       : simulator_(simulator) {
@@ -69,7 +69,7 @@
   }
 
  private:
-  fxcrt::UnownedPtr<FakeFileAccess> simulator_;
+  UnownedPtr<FakeFileAccess> simulator_;
 };
 
 }  // namespace
@@ -99,8 +99,12 @@
 int FakeFileAccess::GetBlock(unsigned long position,
                              unsigned char* pBuf,
                              unsigned long size) {
+  if (!pBuf || !size)
+    return false;
+
   if (!IsDataAvail(static_cast<size_t>(position), static_cast<size_t>(size)))
     return false;
+
   return file_access_->m_GetBlock(file_access_->m_Param, position, pBuf, size);
 }
 
diff --git a/testing/fake_file_access.h b/testing/fake_file_access.h
index 1328b16..c8c08e3 100644
--- a/testing/fake_file_access.h
+++ b/testing/fake_file_access.h
@@ -31,7 +31,7 @@
   void SetWholeFileAvailable();
 
  private:
-  fxcrt::UnownedPtr<FPDF_FILEACCESS> file_access_;
+  UnownedPtr<FPDF_FILEACCESS> file_access_;
   std::unique_ptr<FPDF_FILEACCESS> file_access_wrapper_;
   std::unique_ptr<FX_FILEAVAIL> file_avail_;
   std::unique_ptr<FX_DOWNLOADHINTS> download_hints_;
diff --git a/testing/free_deleter.h b/testing/free_deleter.h
new file mode 100644
index 0000000..58f40da
--- /dev/null
+++ b/testing/free_deleter.h
@@ -0,0 +1,19 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FREE_DELETER_H_
+#define TESTING_FREE_DELETER_H_
+
+#include <stdlib.h>
+
+namespace pdfium {
+
+// Used with std::unique_ptr to free() objects that can't be deleted.
+struct FreeDeleter {
+  inline void operator()(void* ptr) const { free(ptr); }
+};
+
+}  // namespace pdfium
+
+#endif  // TESTING_FREE_DELETER_H_
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
new file mode 100644
index 0000000..03e0610
--- /dev/null
+++ b/testing/fuzzers/BUILD.gn
@@ -0,0 +1,514 @@
+# Copyright 2016 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+
+config("fuzzer_config") {
+  configs = [ "../..:pdfium_core_config" ]
+
+  defines = [
+    "PNG_PREFIX",
+    "PNG_USE_READ_MACROS",
+  ]
+  include_dirs = [ "../.." ]
+}
+
+# All possible "pdfium_fuzzer"s.
+fuzzer_list = [
+  "pdf_cmap_fuzzer",
+  "pdf_codec_a85_fuzzer",
+  "pdf_codec_fax_fuzzer",
+  "pdf_codec_icc_fuzzer",
+  "pdf_codec_jbig2_fuzzer",
+  "pdf_codec_rle_fuzzer",
+  "pdf_font_fuzzer",
+  "pdf_hint_table_fuzzer",
+  "pdf_jpx_fuzzer",
+  "pdf_psengine_fuzzer",
+  "pdf_scanlinecompositor_fuzzer",
+  "pdf_streamparser_fuzzer",
+  "pdf_xml_fuzzer",
+  "pdfium_fuzzer",
+]
+if (pdf_enable_v8) {
+  fuzzer_list += [
+    "pdf_cjs_util_fuzzer",
+    "pdf_fx_date_helpers_fuzzer",
+  ]
+  if (pdf_enable_xfa) {
+    fuzzer_list += [
+      "pdf_bidi_fuzzer",
+      "pdf_cfgas_stringformatter_fuzzer",
+      "pdf_cfx_barcode_fuzzer",
+      "pdf_codec_jpeg_fuzzer",
+      "pdf_css_fuzzer",
+      "pdf_fm2js_fuzzer",
+      "pdf_formcalc_context_fuzzer",
+      "pdf_formcalc_fuzzer",
+      "pdfium_xfa_fuzzer",
+      "pdfium_xfa_lpm_fuzzer",
+    ]
+    if (pdf_enable_xfa_bmp) {
+      fuzzer_list += [ "pdf_codec_bmp_fuzzer" ]
+    }
+    if (pdf_enable_xfa_gif) {
+      fuzzer_list += [
+        "pdf_codec_gif_fuzzer",
+        "pdf_lzw_fuzzer",
+      ]
+    }
+    if (pdf_enable_xfa_png) {
+      fuzzer_list += [ "pdf_codec_png_fuzzer" ]
+    }
+    if (pdf_enable_xfa_tiff) {
+      fuzzer_list += [ "pdf_codec_tiff_fuzzer" ]
+    }
+  }
+}
+if (is_clang) {
+  # Fuzzers that use FuzzedDataProvider can only be built with Clang.
+  fuzzer_list += [ "pdf_nametree_fuzzer" ]
+}
+
+group("fuzzers") {
+  testonly = true
+  deps = []
+  foreach(fuzzer, fuzzer_list) {
+    deps += [ ":${fuzzer}_src" ]
+  }
+}
+
+source_set("fuzzer_init") {
+  testonly = true
+  sources = [ "pdf_fuzzer_init.cc" ]
+  include_dirs = [ "../.." ]
+  deps = [ "../../:pdfium_public_headers" ]
+}
+
+source_set("fuzzer_init_public") {
+  testonly = true
+  sources = [ "pdf_fuzzer_init_public.cc" ]
+  include_dirs = [ "../.." ]
+  deps = [ "../../:pdfium_public_headers" ]
+  if (pdf_enable_v8) {
+    configs += [ "//v8:external_startup_data" ]
+    deps += [
+      "../:test_support",
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+  }
+}
+
+if (is_component_build) {
+  group("fuzzer_impls") {
+    deps = []
+    foreach(fuzzer, fuzzer_list) {
+      deps += [ ":${fuzzer}_impl" ]
+    }
+  }
+}
+
+source_set("fuzzer_helper") {
+  testonly = !is_component_build
+  sources = [
+    "pdfium_fuzzer_helper.cc",
+    "pdfium_fuzzer_helper.h",
+  ]
+  configs += [ ":fuzzer_config" ]
+  deps = [
+    "../../:pdfium_public_headers",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+source_set("fuzzer_utils") {
+  # In component builds, the pdfium target (which is not testonly) depends on
+  # the fuzzer sources, which may depend on this target, so add testonly only in
+  # non-component builds.
+  testonly = !is_component_build
+  sources = [
+    "pdfium_fuzzer_util.cc",
+    "pdfium_fuzzer_util.h",
+  ]
+  deps = [ "../../:pdfium_public_headers" ]
+  include_dirs = [ "../.." ]
+}
+
+template("pdfium_fuzzer") {
+  if (defined(invoker.public_fuzzer) && invoker.public_fuzzer) {
+    init_dep = ":fuzzer_init_public"
+  } else {
+    init_dep = ":fuzzer_init"
+  }
+  if (is_component_build) {
+    # In component builds, fuzzers are split into "_impl" and "_src" targets.
+    # The "_impl" target exports the fuzzer implementation and gets statically
+    # linked into the PDFium shared library.  The "_src" target is a thin
+    # wrapper that imports the fuzzer from PDFium; this gets linked into the
+    # real fuzzer executable.  In static builds, there's only a single "_src"
+    # target that contains the implementation and statically links in PDFium.
+
+    impl_name = target_name + "_impl"
+    template_target_name = target_name
+    source_set("${target_name}_src") {
+      testonly = true
+      sources = [ "component_fuzzer_template.cc" ]
+      deps = [
+        "../../:pdfium",
+        init_dep,
+      ]
+      configs += [ ":fuzzer_config" ]
+      defines = [ "FUZZER_IMPL=${template_target_name}" ]
+    }
+  } else {
+    impl_name = target_name + "_src"
+  }
+  source_set(impl_name) {
+    sources = invoker.sources
+    deps = []
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      ":fuzzer_config",
+    ]
+    if (is_component_build) {
+      # |export| should be consistent with FPDF_EXPORT In public/fpdfview.h.
+      if (is_win) {
+        export = "__declspec(dllexport)"
+      } else {
+        export = "__attribute__((visibility(\"default\")))"
+      }
+      defines = [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ]
+      deps += [ "../../:pdfium_public_headers" ]
+    } else {
+      testonly = true
+      deps += [
+        "../../:pdfium",
+        init_dep,
+      ]
+    }
+  }
+}
+
+if (pdf_enable_v8) {
+  pdfium_fuzzer("pdf_cjs_util_fuzzer") {
+    sources = [ "pdf_cjs_util_fuzzer.cc" ]
+    deps = [
+      "../../core/fxcrt",
+      "../../fxjs",
+    ]
+  }
+  pdfium_fuzzer("pdf_fx_date_helpers_fuzzer") {
+    sources = [ "pdf_fx_date_helpers_fuzzer.cc" ]
+    deps = [
+      "../../core/fxcrt",
+      "../../fxjs",
+    ]
+  }
+
+  if (pdf_enable_xfa) {
+    pdfium_fuzzer("pdf_bidi_fuzzer") {
+      sources = [ "pdf_bidi_fuzzer.cc" ]
+      deps = [
+        "../../:freetype_common",
+        "../../core/fxcrt",
+        "../../core/fxge",
+        "../../xfa/fgas",
+        "../../xfa/fgas/layout",
+        "//third_party/icu:icuuc",
+      ]
+    }
+
+    pdfium_fuzzer("pdf_cfgas_stringformatter_fuzzer") {
+      sources = [ "pdf_cfgas_stringformatter_fuzzer.cc" ]
+      deps = [
+        "../../core/fxcrt",
+        "../../xfa/fgas",
+        "../../xfa/fxfa/parser",
+      ]
+    }
+
+    pdfium_fuzzer("pdf_cfx_barcode_fuzzer") {
+      sources = [ "pdf_cfx_barcode_fuzzer.cc" ]
+      deps = [
+        "../../core/fxcrt",
+        "../../fxbarcode",
+        "//third_party/icu:icuuc",
+      ]
+    }
+
+    if (pdf_enable_xfa_bmp) {
+      pdfium_fuzzer("pdf_codec_bmp_fuzzer") {
+        sources = [
+          "pdf_codec_bmp_fuzzer.cc",
+          "xfa_codec_fuzzer.h",
+        ]
+        deps = [
+          "../../core/fxcodec",
+          "../../core/fxcrt",
+          "../../core/fxge",
+          "../../third_party:pdfium_base",
+        ]
+      }
+    }
+
+    if (pdf_enable_xfa_gif) {
+      pdfium_fuzzer("pdf_codec_gif_fuzzer") {
+        sources = [
+          "pdf_codec_gif_fuzzer.cc",
+          "xfa_codec_fuzzer.h",
+        ]
+        deps = [
+          "../../core/fxcodec",
+          "../../core/fxcrt",
+          "../../core/fxge",
+          "../../third_party:pdfium_base",
+        ]
+      }
+
+      pdfium_fuzzer("pdf_lzw_fuzzer") {
+        sources = [ "pdf_lzw_fuzzer.cc" ]
+        deps = [
+          "../../core/fxcodec",
+          "../../third_party:pdfium_base",
+        ]
+      }
+    }
+
+    pdfium_fuzzer("pdf_codec_jpeg_fuzzer") {
+      sources = [
+        "pdf_codec_jpeg_fuzzer.cc",
+        "xfa_codec_fuzzer.h",
+      ]
+      deps = [
+        "../../core/fxcodec",
+        "../../core/fxcrt",
+        "../../core/fxge",
+        "../../third_party:pdfium_base",
+      ]
+    }
+
+    if (pdf_enable_xfa_png) {
+      pdfium_fuzzer("pdf_codec_png_fuzzer") {
+        sources = [
+          "pdf_codec_png_fuzzer.cc",
+          "xfa_codec_fuzzer.h",
+        ]
+        deps = [
+          "../../core/fxcodec",
+          "../../core/fxcrt",
+          "../../core/fxge",
+          "../../third_party:pdfium_base",
+        ]
+      }
+    }
+
+    if (pdf_enable_xfa_tiff) {
+      pdfium_fuzzer("pdf_codec_tiff_fuzzer") {
+        sources = [
+          "pdf_codec_tiff_fuzzer.cc",
+          "xfa_codec_fuzzer.h",
+        ]
+        deps = [
+          "../../core/fxcodec",
+          "../../core/fxcrt",
+          "../../core/fxge",
+          "../../third_party:pdfium_base",
+        ]
+      }
+    }
+
+    pdfium_fuzzer("pdf_css_fuzzer") {
+      sources = [ "pdf_css_fuzzer.cc" ]
+      deps = [
+        "../../core/fxcrt",
+        "../../core/fxcrt/css",
+      ]
+    }
+
+    pdfium_fuzzer("pdf_fm2js_fuzzer") {
+      sources = [ "pdf_fm2js_fuzzer.cc" ]
+      deps = [
+        "../../core/fxcrt",
+        "../../fxjs",
+      ]
+    }
+
+    pdfium_fuzzer("pdf_formcalc_context_fuzzer") {
+      sources = [ "pdf_formcalc_context_fuzzer.cc" ]
+      deps = [
+        ":fuzzer_helper",
+        "../../:pdfium_public_headers",
+        "../../core/fxcrt",
+        "../../fpdfsdk",
+        "../../fpdfsdk/fpdfxfa",
+        "../../fxjs",
+        "../../xfa/fxfa",
+      ]
+      public_fuzzer = true
+    }
+
+    pdfium_fuzzer("pdf_formcalc_fuzzer") {
+      sources = [ "pdf_formcalc_fuzzer.cc" ]
+      deps = [
+        "../../core/fxcrt",
+        "../../xfa/fxfa/fm2js",
+      ]
+    }
+
+    pdfium_fuzzer("pdfium_xfa_fuzzer") {
+      sources = [ "pdfium_xfa_fuzzer.cc" ]
+      deps = [
+        ":fuzzer_helper",
+        "../../:pdfium_public_headers",
+      ]
+      public_fuzzer = true
+    }
+
+    pdfium_fuzzer("pdfium_xfa_lpm_fuzzer") {
+      sources = [
+        "pdfium_xfa_lpm_fuzz_stub.cc",
+        "pdfium_xfa_lpm_fuzz_stub.h",
+      ]
+      deps = [
+        ":fuzzer_helper",
+        "../../:pdfium_public_headers",
+      ]
+      public_fuzzer = true
+    }
+  }
+}
+
+if (is_clang) {
+  pdfium_fuzzer("pdf_nametree_fuzzer") {
+    sources = [ "pdf_nametree_fuzzer.cc" ]
+    deps = [
+      "../../core/fpdfapi/page",
+      "../../core/fpdfapi/parser",
+      "../../core/fpdfdoc",
+      "../../third_party:pdfium_base",
+    ]
+  }
+}
+
+pdfium_fuzzer("pdf_cmap_fuzzer") {
+  sources = [ "pdf_cmap_fuzzer.cc" ]
+  deps = [
+    "../../:freetype_common",
+    "../../core/fpdfapi/font",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+pdfium_fuzzer("pdf_codec_a85_fuzzer") {
+  sources = [ "pdf_codec_a85_fuzzer.cc" ]
+  deps = [
+    "../../core/fxcodec",
+    "../../core/fxcrt",
+  ]
+}
+
+pdfium_fuzzer("pdf_codec_fax_fuzzer") {
+  sources = [ "pdf_codec_fax_fuzzer.cc" ]
+  deps = [
+    ":fuzzer_utils",
+    "../../core/fxcodec",
+  ]
+}
+
+pdfium_fuzzer("pdf_codec_icc_fuzzer") {
+  sources = [ "pdf_codec_icc_fuzzer.cc" ]
+  deps = [
+    "../../core/fxcodec",
+    "../../third_party:pdfium_base",
+    "../../third_party/:lcms2",
+  ]
+}
+
+pdfium_fuzzer("pdf_codec_jbig2_fuzzer") {
+  sources = [ "pdf_codec_jbig2_fuzzer.cc" ]
+  deps = [
+    ":fuzzer_utils",
+    "../../core/fpdfapi/parser",
+    "../../core/fxcodec",
+    "../../core/fxcrt",
+    "../../core/fxge",
+  ]
+}
+
+pdfium_fuzzer("pdf_codec_rle_fuzzer") {
+  sources = [ "pdf_codec_rle_fuzzer.cc" ]
+  deps = [
+    "../../core/fxcodec",
+    "../../core/fxcrt",
+  ]
+}
+
+pdfium_fuzzer("pdf_font_fuzzer") {
+  sources = [ "pdf_font_fuzzer.cc" ]
+  deps = [ "../../:pdfium_public_headers" ]
+}
+
+pdfium_fuzzer("pdf_hint_table_fuzzer") {
+  sources = [ "pdf_hint_table_fuzzer.cc" ]
+  deps = [
+    "../../core/fpdfapi/parser",
+    "../../core/fxcrt",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+pdfium_fuzzer("pdf_jpx_fuzzer") {
+  sources = [ "pdf_jpx_fuzzer.cc" ]
+  deps = [
+    "../../core/fpdfapi/page",
+    "../../core/fxcodec",
+    "../../core/fxcrt",
+    "../../core/fxge",
+  ]
+}
+
+pdfium_fuzzer("pdf_psengine_fuzzer") {
+  sources = [ "pdf_psengine_fuzzer.cc" ]
+  deps = [
+    "../../core/fpdfapi/page",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+pdfium_fuzzer("pdf_scanlinecompositor_fuzzer") {
+  sources = [ "pdf_scanlinecompositor_fuzzer.cc" ]
+  deps = [
+    ":fuzzer_utils",
+    "../../core/fxge",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+pdfium_fuzzer("pdf_streamparser_fuzzer") {
+  sources = [ "pdf_streamparser_fuzzer.cc" ]
+  deps = [
+    "../../core/fpdfapi/page",
+    "../../core/fpdfapi/parser",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+pdfium_fuzzer("pdf_xml_fuzzer") {
+  sources = [ "pdf_xml_fuzzer.cc" ]
+  deps = [
+    "../../core/fxcrt",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+pdfium_fuzzer("pdfium_fuzzer") {
+  sources = [ "pdfium_fuzzer.cc" ]
+  deps = [ ":fuzzer_helper" ]
+  public_fuzzer = true
+}
diff --git a/testing/fuzzers/DEPS b/testing/fuzzers/DEPS
new file mode 100644
index 0000000..fe9eaf6
--- /dev/null
+++ b/testing/fuzzers/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  '+fxbarcode',
+  '+xfa',
+]
diff --git a/testing/fuzzers/component_fuzzer_template.cc b/testing/fuzzers/component_fuzzer_template.cc
new file mode 100644
index 0000000..89883f5
--- /dev/null
+++ b/testing/fuzzers/component_fuzzer_template.cc
@@ -0,0 +1,19 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstddef>
+#include <cstdint>
+
+#include "public/fpdfview.h"
+
+// This template is used in component builds to forward to the real fuzzers
+// which are exported from the PDFium shared library.
+
+// FUZZER_IMPL is a macro defined at build time that contains the name of the
+// real fuzzer.
+extern "C" FPDF_EXPORT int FUZZER_IMPL(const uint8_t* data, size_t size);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return FUZZER_IMPL(data, size);
+}
diff --git a/testing/fuzzers/pdf_bidi_fuzzer.cc b/testing/fuzzers/pdf_bidi_fuzzer.cc
new file mode 100644
index 0000000..614df52
--- /dev/null
+++ b/testing/fuzzers/pdf_bidi_fuzzer.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+
+#include "core/fxcrt/widestring.h"
+#include "core/fxge/cfx_font.h"
+#include "core/fxge/fx_font.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/layout/cfx_char.h"
+#include "xfa/fgas/layout/cfx_rtfbreak.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  auto fontmgr = pdfium::MakeUnique<CFGAS_FontMgr>();
+
+  auto font = pdfium::MakeUnique<CFX_Font>();
+  font->LoadSubst("Arial", true, 0, FXFONT_FW_NORMAL, 0, 0, 0);
+  assert(font);
+
+  CFX_RTFBreak rtf_break(FX_LAYOUTSTYLE_ExpandTab);
+  rtf_break.SetLineBreakTolerance(1);
+  rtf_break.SetFont(CFGAS_GEFont::LoadFont(std::move(font), fontmgr.get()));
+  rtf_break.SetFontSize(12);
+
+  WideString input =
+      WideString::FromUTF16LE(reinterpret_cast<const unsigned short*>(data),
+                              size / sizeof(unsigned short));
+  for (wchar_t ch : input)
+    rtf_break.AppendChar(ch);
+
+  std::vector<CFX_Char> chars =
+      rtf_break.GetCurrentLineForTesting()->m_LineChars;
+  CFX_Char::BidiLine(&chars, chars.size());
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
new file mode 100644
index 0000000..b6a6663
--- /dev/null
+++ b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fgas/crt/cfgas_stringformatter.h"
+
+#include <stdint.h>
+
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_string.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+
+namespace {
+
+const wchar_t* const kLocales[] = {L"en", L"fr", L"jp", L"zh"};
+const FX_DATETIMETYPE kTypes[] = {FX_DATETIMETYPE_Date, FX_DATETIMETYPE_Time,
+                                  FX_DATETIMETYPE_DateTime,
+                                  FX_DATETIMETYPE_TimeDate};
+
+}  // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 5 || size > 128)  // Big strings are unlikely to help.
+    return 0;
+
+  // Static for speed.
+  static std::vector<std::unique_ptr<CXFA_LocaleMgr>> mgrs;
+  if (mgrs.empty()) {
+    for (const auto* locale : kLocales)
+      mgrs.push_back(pdfium::MakeUnique<CXFA_LocaleMgr>(nullptr, locale));
+  }
+
+  uint8_t test_selector = data[0] % 10;
+  uint8_t locale_selector = data[1] % FX_ArraySize(kLocales);
+  uint8_t type_selector = data[2] % FX_ArraySize(kTypes);
+  data += 3;
+  size -= 3;
+
+  size_t pattern_len = size / 2;
+  size_t value_len = size - pattern_len;
+  WideString pattern =
+      WideString::FromLatin1(ByteStringView(data, pattern_len));
+  WideString value =
+      WideString::FromLatin1(ByteStringView(data + pattern_len, value_len));
+
+  auto fmt = pdfium::MakeUnique<CFGAS_StringFormatter>(
+      mgrs[locale_selector].get(), pattern);
+
+  WideString result;
+  CFX_DateTime dt;
+  switch (test_selector) {
+    case 0:
+      fmt->FormatText(value, &result);
+      break;
+    case 1:
+      fmt->FormatNum(value, &result);
+      break;
+    case 2:
+      fmt->FormatDateTime(value, kTypes[type_selector], &result);
+      break;
+    case 3:
+      fmt->FormatNull(&result);
+      break;
+    case 4:
+      fmt->FormatZero(&result);
+      break;
+    case 5:
+      fmt->ParseText(value, &result);
+      break;
+    case 6:
+      fmt->ParseNum(value, &result);
+      break;
+    case 7:
+      fmt->ParseDateTime(value, kTypes[type_selector], &dt);
+      break;
+    case 8:
+      fmt->ParseNull(value);
+      break;
+    case 9:
+      fmt->ParseZero(value);
+      break;
+  }
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
new file mode 100644
index 0000000..7b31b2b
--- /dev/null
+++ b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "core/fxcrt/fx_string.h"
+#include "fxbarcode/cfx_barcode.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 2 * sizeof(uint16_t))
+    return 0;
+
+  BC_TYPE type = static_cast<BC_TYPE>(data[0] % (BC_LAST + 1));
+
+  // Only used one byte, but align with uint16_t for string below.
+  data += sizeof(uint16_t);
+  size -= sizeof(uint16_t);
+
+  auto barcode = CFX_Barcode::Create(type);
+
+  // TODO(tsepez): Setup more options from |data|.
+  barcode->SetModuleHeight(300);
+  barcode->SetModuleWidth(420);
+  barcode->SetHeight(298);
+  barcode->SetWidth(418);
+
+  WideString content = WideString::FromUTF16LE(
+      reinterpret_cast<const uint16_t*>(data), size / sizeof(uint16_t));
+
+  if (!barcode->Encode(content.AsStringView()))
+    return 0;
+
+  // TODO(tsepez): Output to device.
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_cjs_util_fuzzer.cc b/testing/fuzzers/pdf_cjs_util_fuzzer.cc
new file mode 100644
index 0000000..5ccb65b
--- /dev/null
+++ b/testing/fuzzers/pdf_cjs_util_fuzzer.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/widestring.h"
+#include "fxjs/cjs_util.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  auto* short_data = reinterpret_cast<const unsigned short*>(data);
+  size_t short_size = size / sizeof(unsigned short);
+  if (short_size > 1) {
+    WideString input = WideString::FromUTF16LE(short_data, short_size);
+    CJS_Util::ParseDataType(&input);
+  }
+  if (short_size > 2) {
+    size_t short_len1 = short_size / 2;
+    size_t short_len2 = short_size - short_len1;
+    WideString input1 = WideString::FromUTF16LE(short_data, short_len1);
+    WideString input2 =
+        WideString::FromUTF16LE(short_data + short_len1, short_len2);
+    CJS_Util::StringPrintx(input1, input2);
+  }
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_cmap_fuzzer.cc b/testing/fuzzers/pdf_cmap_fuzzer.cc
new file mode 100644
index 0000000..180a6a7
--- /dev/null
+++ b/testing/fuzzers/pdf_cmap_fuzzer.cc
@@ -0,0 +1,16 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+
+#include "core/fpdfapi/font/cpdf_cmap.h"
+#include "third_party/base/span.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size > 256 * 1024)
+    return 0;
+
+  pdfium::MakeRetain<CPDF_CMap>(pdfium::make_span(data, size));
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_codec_a85_fuzzer.cc b/testing/fuzzers/pdf_codec_a85_fuzzer.cc
new file mode 100644
index 0000000..f9ae1fe
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_a85_fuzzer.cc
@@ -0,0 +1,16 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+
+#include "core/fxcodec/basic/basicmodule.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+  BasicModule::A85Encode({data, size}, &dest_buf, &dest_size);
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_codec_bmp_fuzzer.cc b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
new file mode 100644
index 0000000..71f9150
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
@@ -0,0 +1,9 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/xfa_codec_fuzzer.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_BMP);
+}
diff --git a/testing/fuzzers/pdf_codec_fax_fuzzer.cc b/testing/fuzzers/pdf_codec_fax_fuzzer.cc
new file mode 100644
index 0000000..d0c2984
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_fax_fuzzer.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+
+#include "core/fxcodec/fax/faxmodule.h"
+#include "core/fxcodec/scanlinedecoder.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static constexpr size_t kParameterSize = 21;
+  if (size < kParameterSize)
+    return 0;
+
+  // Limit data size to prevent fuzzer timeout.
+  static constexpr size_t kMaxDataSize = 256 * 1024;
+  if (size > kParameterSize + kMaxDataSize)
+    return 0;
+
+  int width = GetInteger(data);
+  int height = GetInteger(data + 4);
+  int K = GetInteger(data + 8);
+  int Columns = GetInteger(data + 12);
+  int Rows = GetInteger(data + 16);
+  bool EndOfLine = !(data[20] & 0x01);
+  bool ByteAlign = !(data[20] & 0x02);
+  // This controls if fxcodec::FaxDecoder::InvertBuffer() gets called.
+  // The method is not interesting, and calling it doubles the runtime.
+  const bool kBlackIs1 = false;
+  data += kParameterSize;
+  size -= kParameterSize;
+
+  std::unique_ptr<ScanlineDecoder> decoder =
+      FaxModule::CreateDecoder({data, size}, width, height, K, EndOfLine,
+                               ByteAlign, kBlackIs1, Columns, Rows);
+
+  if (decoder) {
+    int line = 0;
+    while (decoder->GetScanline(line))
+      line++;
+  }
+
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_codec_gif_fuzzer.cc b/testing/fuzzers/pdf_codec_gif_fuzzer.cc
new file mode 100644
index 0000000..69129e7
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_gif_fuzzer.cc
@@ -0,0 +1,9 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/xfa_codec_fuzzer.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_GIF);
+}
diff --git a/testing/fuzzers/pdf_codec_icc_fuzzer.cc b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
new file mode 100644
index 0000000..ca027331
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
@@ -0,0 +1,23 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+
+#include "core/fxcodec/icc/iccmodule.h"
+#include "third_party/base/span.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::unique_ptr<CLcmsCmm> transform =
+      IccModule::CreateTransformSRGB(pdfium::make_span(data, size));
+
+  if (transform) {
+    float src[4];
+    float dst[4];
+    for (int i = 0; i < 4; i++)
+      src[i] = 0.5f;
+    IccModule::Translate(transform.get(), transform->components(), src, dst);
+  }
+
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
new file mode 100644
index 0000000..2878d7c
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
@@ -0,0 +1,48 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+
+#include "core/fxcodec/jbig2/JBig2_Context.h"
+#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
+#include "core/fxcodec/jbig2/jbig2module.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_dib.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "third_party/base/ptr_util.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  const size_t kParameterSize = 8;
+  if (size < kParameterSize)
+    return 0;
+
+  uint32_t width = GetInteger(data);
+  uint32_t height = GetInteger(data + 4);
+  size -= kParameterSize;
+  data += kParameterSize;
+
+  static constexpr uint32_t kMemLimit = 512000000;   // 512 MB
+  static constexpr uint32_t k1bppRgbComponents = 4;  // From CFX_DIBitmap impl.
+  FX_SAFE_UINT32 mem = width;
+  mem *= height;
+  mem *= k1bppRgbComponents;
+  if (!mem.IsValid() || mem.ValueOrDie() > kMemLimit)
+    return 0;
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!bitmap->Create(width, height, FXDIB_1bppRgb))
+    return 0;
+
+  Jbig2Module module;
+  Jbig2Context jbig2_context;
+  std::unique_ptr<JBig2_DocumentContext> document_context;
+  FXCODEC_STATUS status = module.StartDecode(
+      &jbig2_context, &document_context, width, height, {data, size}, 1, {}, 0,
+      bitmap->GetBuffer(), bitmap->GetPitch(), nullptr);
+
+  while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+    status = module.ContinueDecode(&jbig2_context, nullptr);
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
new file mode 100644
index 0000000..eaa0889
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
@@ -0,0 +1,9 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/xfa_codec_fuzzer.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_JPG);
+}
diff --git a/testing/fuzzers/pdf_codec_png_fuzzer.cc b/testing/fuzzers/pdf_codec_png_fuzzer.cc
new file mode 100644
index 0000000..61a6574
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_png_fuzzer.cc
@@ -0,0 +1,9 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/xfa_codec_fuzzer.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_PNG);
+}
diff --git a/testing/fuzzers/pdf_codec_rle_fuzzer.cc b/testing/fuzzers/pdf_codec_rle_fuzzer.cc
new file mode 100644
index 0000000..7b40b01
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_rle_fuzzer.cc
@@ -0,0 +1,16 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+
+#include "core/fxcodec/basic/basicmodule.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
+  uint32_t dest_size = 0;
+  BasicModule::RunLengthEncode({data, size}, &dest_buf, &dest_size);
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_codec_tiff_fuzzer.cc b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
new file mode 100644
index 0000000..187c311
--- /dev/null
+++ b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
@@ -0,0 +1,9 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/xfa_codec_fuzzer.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_TIFF);
+}
diff --git a/testing/fuzzers/pdf_css_fuzzer.cc b/testing/fuzzers/pdf_css_fuzzer.cc
new file mode 100644
index 0000000..5f1471d
--- /dev/null
+++ b/testing/fuzzers/pdf_css_fuzzer.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "core/fxcrt/css/cfx_css.h"
+#include "core/fxcrt/css/cfx_csssyntaxparser.h"
+#include "core/fxcrt/fx_string.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  WideString input =
+      WideString::FromUTF8(ByteStringView(data, static_cast<size_t>(size)));
+
+  // If we convert the input into an empty string bail out.
+  if (input.IsEmpty())
+    return 0;
+
+  CFX_CSSSyntaxParser parser(input.c_str(), input.GetLength());
+  CFX_CSSSyntaxStatus status;
+  do {
+    status = parser.DoSyntaxParse();
+  } while (status != CFX_CSSSyntaxStatus::Error &&
+           status != CFX_CSSSyntaxStatus::EOS);
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_fm2js_fuzzer.cc b/testing/fuzzers/pdf_fm2js_fuzzer.cc
new file mode 100644
index 0000000..67daa46
--- /dev/null
+++ b/testing/fuzzers/pdf_fm2js_fuzzer.cc
@@ -0,0 +1,23 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstddef>
+#include <cstdint>
+
+#include "core/fxcrt/cfx_widetextbuf.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_string.h"
+#include "fxjs/xfa/cfxjse_formcalc_context.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FX_SAFE_SIZE_T safe_size = size;
+  if (!safe_size.IsValid())
+    return 0;
+
+  CFX_WideTextBuf js;
+  WideString input =
+      WideString::FromUTF8(ByteStringView(data, safe_size.ValueOrDie()));
+  CFXJSE_FormCalcContext::Translate(input.AsStringView(), &js);
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_font_fuzzer.cc b/testing/fuzzers/pdf_font_fuzzer.cc
new file mode 100644
index 0000000..7c59630
--- /dev/null
+++ b/testing/fuzzers/pdf_font_fuzzer.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstring>
+#include <memory>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_edit.h"
+#include "public/fpdfview.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 2)
+    return 0;
+
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 612, 792));
+  int font_type = data[0];
+  FPDF_BOOL cid = data[1];
+  data += 2;
+  size -= 2;
+  ScopedFPDFFont font(FPDFText_LoadFont(doc.get(), data, size, font_type, cid));
+  if (!font)
+    return 0;
+
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_CreateTextObj(doc.get(), font.get(), 12.0f);
+  FPDFPage_InsertObject(page.get(), text_object);
+  FPDFPage_GenerateContent(page.get());
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_formcalc_context_fuzzer.cc b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
new file mode 100644
index 0000000..e2d73a8
--- /dev/null
+++ b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
@@ -0,0 +1,546 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "core/fxcrt/fx_string.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+#include "v8/include/v8.h"
+#include "xfa/fxfa/cxfa_eventparam.h"
+
+namespace {
+
+// testing/resources/simple_xfa.pdf
+const uint8_t kSimpleXfaPdfData[] = {
+    0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x37, 0x0a, 0x25, 0xa0, 0xf2,
+    0xa4, 0xf4, 0x0a, 0x31, 0x20, 0x30, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3c,
+    0x3c, 0x0a, 0x20, 0x20, 0x2f, 0x41, 0x63, 0x72, 0x6f, 0x46, 0x6f, 0x72,
+    0x6d, 0x20, 0x32, 0x20, 0x30, 0x20, 0x52, 0x0a, 0x20, 0x20, 0x2f, 0x45,
+    0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3c, 0x3c,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x41, 0x44, 0x42, 0x45, 0x20, 0x3c,
+    0x3c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x42, 0x61, 0x73,
+    0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x2f, 0x31, 0x2e,
+    0x37, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x45, 0x78, 0x74,
+    0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x20,
+    0x38, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3e, 0x3e, 0x0a, 0x20, 0x20, 0x3e,
+    0x3e, 0x0a, 0x20, 0x20, 0x2f, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x52, 0x65,
+    0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x72, 0x75, 0x65,
+    0x0a, 0x20, 0x20, 0x2f, 0x50, 0x61, 0x67, 0x65, 0x73, 0x20, 0x38, 0x20,
+    0x30, 0x20, 0x52, 0x0a, 0x20, 0x20, 0x2f, 0x54, 0x79, 0x70, 0x65, 0x20,
+    0x2f, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x0a, 0x3e, 0x3e, 0x0a,
+    0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a, 0x0a, 0x32, 0x20, 0x30, 0x20, 0x6f,
+    0x62, 0x6a, 0x20, 0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f, 0x58, 0x46, 0x41,
+    0x20, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x70, 0x72, 0x65, 0x61,
+    0x6d, 0x62, 0x6c, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x33, 0x20,
+    0x30, 0x20, 0x52, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x63, 0x6f, 0x6e,
+    0x66, 0x69, 0x67, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x34, 0x20, 0x30,
+    0x20, 0x52, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x74, 0x65, 0x6d, 0x70,
+    0x6c, 0x61, 0x74, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x35, 0x20,
+    0x30, 0x20, 0x52, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x6c, 0x6f, 0x63,
+    0x61, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+    0x36, 0x20, 0x30, 0x20, 0x52, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x70,
+    0x6f, 0x73, 0x74, 0x61, 0x6d, 0x62, 0x6c, 0x65, 0x29, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x37, 0x20, 0x30, 0x20, 0x52, 0x0a, 0x20, 0x20, 0x5d, 0x0a,
+    0x3e, 0x3e, 0x0a, 0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a, 0x0a, 0x33, 0x20,
+    0x30, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f,
+    0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x31, 0x32, 0x34, 0x0a, 0x3e,
+    0x3e, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x0a, 0x3c, 0x78, 0x64,
+    0x70, 0x3a, 0x78, 0x64, 0x70, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a,
+    0x78, 0x64, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+    0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+    0x2f, 0x78, 0x64, 0x70, 0x2f, 0x22, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x53,
+    0x74, 0x61, 0x6d, 0x70, 0x3d, 0x22, 0x32, 0x30, 0x31, 0x38, 0x2d, 0x30,
+    0x32, 0x2d, 0x32, 0x33, 0x54, 0x32, 0x31, 0x3a, 0x33, 0x37, 0x3a, 0x31,
+    0x31, 0x5a, 0x22, 0x20, 0x75, 0x75, 0x69, 0x64, 0x3d, 0x22, 0x32, 0x31,
+    0x34, 0x38, 0x32, 0x37, 0x39, 0x38, 0x2d, 0x37, 0x62, 0x66, 0x30, 0x2d,
+    0x34, 0x30, 0x61, 0x34, 0x2d, 0x62, 0x63, 0x35, 0x64, 0x2d, 0x33, 0x63,
+    0x65, 0x66, 0x64, 0x63, 0x63, 0x66, 0x33, 0x32, 0x62, 0x35, 0x22, 0x3e,
+    0x0a, 0x65, 0x6e, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x0a, 0x65,
+    0x6e, 0x64, 0x6f, 0x62, 0x6a, 0x0a, 0x34, 0x20, 0x30, 0x20, 0x6f, 0x62,
+    0x6a, 0x20, 0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f, 0x4c, 0x65, 0x6e, 0x67,
+    0x74, 0x68, 0x20, 0x36, 0x34, 0x32, 0x0a, 0x3e, 0x3e, 0x0a, 0x73, 0x74,
+    0x72, 0x65, 0x61, 0x6d, 0x0a, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+    0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70,
+    0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x78, 0x66, 0x61, 0x2e, 0x6f,
+    0x72, 0x67, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x78, 0x63,
+    0x69, 0x2f, 0x33, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x3c, 0x61, 0x67,
+    0x65, 0x6e, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x64, 0x65,
+    0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x3c,
+    0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e,
+    0x70, 0x64, 0x66, 0x3c, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61,
+    0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x70, 0x64, 0x66,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x66, 0x6f, 0x6e, 0x74, 0x49,
+    0x6e, 0x66, 0x6f, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x70, 0x64,
+    0x66, 0x3e, 0x0a, 0x3c, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x3e, 0x0a,
+    0x3c, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x3e, 0x0a, 0x20, 0x20,
+    0x3c, 0x70, 0x64, 0x66, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x76,
+    0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x2e, 0x37, 0x3c, 0x2f,
+    0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e,
+    0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3e, 0x38, 0x3c,
+    0x2f, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
+    0x69, 0x6f, 0x6e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3e, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x3c, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x6f, 0x6c,
+    0x69, 0x63, 0x79, 0x3e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f,
+    0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70,
+    0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x3e, 0x58, 0x46, 0x41, 0x3c, 0x2f,
+    0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x3e,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x61,
+    0x63, 0x74, 0x69, 0x76, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x69, 0x6e, 0x74,
+    0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x3e, 0x0a, 0x20, 0x20,
+    0x3c, 0x2f, 0x70, 0x64, 0x66, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x78, 0x64,
+    0x70, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x63, 0x6b,
+    0x65, 0x74, 0x73, 0x3e, 0x2a, 0x3c, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65,
+    0x74, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x78, 0x64, 0x70, 0x3e,
+    0x0a, 0x20, 0x20, 0x3c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
+    0x69, 0x6f, 0x6e, 0x3e, 0x70, 0x64, 0x66, 0x3c, 0x2f, 0x64, 0x65, 0x73,
+    0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20,
+    0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x72, 0x75, 0x6e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73,
+    0x3e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3c, 0x2f, 0x72, 0x75, 0x6e,
+    0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x3c,
+    0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0a, 0x3c, 0x2f, 0x70,
+    0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x3e, 0x0a, 0x3c, 0x61, 0x63, 0x72,
+    0x6f, 0x62, 0x61, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x61, 0x63, 0x72,
+    0x6f, 0x62, 0x61, 0x74, 0x37, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65,
+    0x72, 0x3e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x3c, 0x2f,
+    0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x64, 0x65,
+    0x72, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x61, 0x63, 0x72, 0x6f, 0x62,
+    0x61, 0x74, 0x37, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x76, 0x61, 0x6c, 0x69,
+    0x64, 0x61, 0x74, 0x65, 0x3e, 0x70, 0x72, 0x65, 0x53, 0x75, 0x62, 0x6d,
+    0x69, 0x74, 0x3c, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65,
+    0x3e, 0x0a, 0x3c, 0x2f, 0x61, 0x63, 0x72, 0x6f, 0x62, 0x61, 0x74, 0x3e,
+    0x0a, 0x3c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3e, 0x0a, 0x65,
+    0x6e, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x0a, 0x65, 0x6e, 0x64,
+    0x6f, 0x62, 0x6a, 0x0a, 0x35, 0x20, 0x30, 0x20, 0x6f, 0x62, 0x6a, 0x20,
+    0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+    0x20, 0x34, 0x38, 0x32, 0x0a, 0x3e, 0x3e, 0x0a, 0x73, 0x74, 0x72, 0x65,
+    0x61, 0x6d, 0x0a, 0x3c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
+    0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70,
+    0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x78, 0x66, 0x61, 0x2e, 0x6f,
+    0x72, 0x67, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x78, 0x66,
+    0x61, 0x2d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2f, 0x33,
+    0x2e, 0x33, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x73, 0x75, 0x62,
+    0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x66,
+    0x6f, 0x72, 0x6d, 0x31, 0x22, 0x20, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74,
+    0x3d, 0x22, 0x74, 0x62, 0x22, 0x20, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72,
+    0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x61, 0x75, 0x74, 0x6f,
+    0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x67, 0x65,
+    0x53, 0x65, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x70, 0x61, 0x67, 0x65, 0x41, 0x72, 0x65, 0x61, 0x20, 0x6e, 0x61, 0x6d,
+    0x65, 0x3d, 0x22, 0x50, 0x61, 0x67, 0x65, 0x31, 0x22, 0x20, 0x69, 0x64,
+    0x3d, 0x22, 0x50, 0x61, 0x67, 0x65, 0x31, 0x22, 0x3e, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+    0x6e, 0x74, 0x41, 0x72, 0x65, 0x61, 0x20, 0x78, 0x3d, 0x22, 0x30, 0x2e,
+    0x32, 0x35, 0x69, 0x6e, 0x22, 0x20, 0x79, 0x3d, 0x22, 0x30, 0x2e, 0x32,
+    0x35, 0x69, 0x6e, 0x22, 0x20, 0x77, 0x3d, 0x22, 0x38, 0x69, 0x6e, 0x22,
+    0x20, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x2e, 0x35, 0x69, 0x6e, 0x22, 0x20,
+    0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x70,
+    0x61, 0x67, 0x65, 0x41, 0x72, 0x65, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x2f, 0x70, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6e,
+    0x61, 0x6d, 0x65, 0x3d, 0x22, 0x54, 0x65, 0x78, 0x74, 0x46, 0x69, 0x65,
+    0x6c, 0x64, 0x31, 0x22, 0x20, 0x79, 0x3d, 0x22, 0x33, 0x31, 0x2e, 0x37,
+    0x35, 0x6d, 0x6d, 0x22, 0x20, 0x78, 0x3d, 0x22, 0x34, 0x34, 0x2e, 0x34,
+    0x35, 0x6d, 0x6d, 0x22, 0x20, 0x77, 0x3d, 0x22, 0x31, 0x31, 0x34, 0x2e,
+    0x32, 0x39, 0x31, 0x6d, 0x6d, 0x22, 0x20, 0x68, 0x3d, 0x22, 0x31, 0x32,
+    0x2e, 0x37, 0x6d, 0x6d, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
+    0x22, 0x54, 0x65, 0x78, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x31,
+    0x22, 0x20, 0x79, 0x3d, 0x22, 0x32, 0x32, 0x32, 0x2e, 0x32, 0x35, 0x6d,
+    0x6d, 0x22, 0x20, 0x78, 0x3d, 0x22, 0x34, 0x34, 0x2e, 0x34, 0x35, 0x6d,
+    0x6d, 0x22, 0x20, 0x77, 0x3d, 0x22, 0x38, 0x32, 0x2e, 0x35, 0x35, 0x6d,
+    0x6d, 0x22, 0x20, 0x68, 0x3d, 0x22, 0x31, 0x32, 0x2e, 0x37, 0x6d, 0x6d,
+    0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x66, 0x69, 0x65,
+    0x6c, 0x64, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x73, 0x75, 0x62, 0x66,
+    0x6f, 0x72, 0x6d, 0x3e, 0x0a, 0x3c, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c,
+    0x61, 0x74, 0x65, 0x3e, 0x0a, 0x65, 0x6e, 0x64, 0x73, 0x74, 0x72, 0x65,
+    0x61, 0x6d, 0x0a, 0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a, 0x0a, 0x36, 0x20,
+    0x30, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f,
+    0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x33, 0x34, 0x35, 0x35, 0x0a,
+    0x3e, 0x3e, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x0a, 0x3c, 0x6c,
+    0x6f, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x20, 0x78, 0x6d, 0x6c,
+    0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+    0x77, 0x77, 0x2e, 0x78, 0x66, 0x61, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x73,
+    0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x78, 0x66, 0x61, 0x2d, 0x6c, 0x6f,
+    0x63, 0x61, 0x6c, 0x65, 0x2d, 0x73, 0x65, 0x74, 0x2f, 0x32, 0x2e, 0x37,
+    0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+    0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x65, 0x6e, 0x5f, 0x55,
+    0x53, 0x22, 0x20, 0x64, 0x65, 0x73, 0x63, 0x3d, 0x22, 0x45, 0x6e, 0x67,
+    0x6c, 0x69, 0x73, 0x68, 0x20, 0x28, 0x55, 0x6e, 0x69, 0x74, 0x65, 0x64,
+    0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x29, 0x22, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x3c, 0x63, 0x61, 0x6c, 0x65, 0x6e, 0x64, 0x61, 0x72,
+    0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x20, 0x6e, 0x61, 0x6d, 0x65,
+    0x3d, 0x22, 0x67, 0x72, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x61, 0x6e, 0x22,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e,
+    0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e,
+    0x4a, 0x61, 0x6e, 0x75, 0x61, 0x72, 0x79, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e,
+    0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x46, 0x65, 0x62, 0x72, 0x75,
+    0x61, 0x72, 0x79, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e,
+    0x74, 0x68, 0x3e, 0x4d, 0x61, 0x72, 0x63, 0x68, 0x3c, 0x2f, 0x6d, 0x6f,
+    0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x41, 0x70, 0x72, 0x69,
+    0x6c, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68,
+    0x3e, 0x4d, 0x61, 0x79, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f,
+    0x6e, 0x74, 0x68, 0x3e, 0x4a, 0x75, 0x6e, 0x65, 0x3c, 0x2f, 0x6d, 0x6f,
+    0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x4a, 0x75, 0x6c, 0x79,
+    0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e,
+    0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74,
+    0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x53, 0x65, 0x70, 0x74, 0x65, 0x6d,
+    0x62, 0x65, 0x72, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e,
+    0x74, 0x68, 0x3e, 0x4f, 0x63, 0x74, 0x6f, 0x62, 0x65, 0x72, 0x3c, 0x2f,
+    0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x4e, 0x6f,
+    0x76, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74,
+    0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x44, 0x65, 0x63, 0x65, 0x6d, 0x62,
+    0x65, 0x72, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68,
+    0x4e, 0x61, 0x6d, 0x65, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73,
+    0x20, 0x61, 0x62, 0x62, 0x72, 0x3d, 0x22, 0x31, 0x22, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74,
+    0x68, 0x3e, 0x4a, 0x61, 0x6e, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d,
+    0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x46, 0x65, 0x62, 0x3c, 0x2f, 0x6d, 0x6f,
+    0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x4d, 0x61, 0x72, 0x3c,
+    0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x41,
+    0x70, 0x72, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74,
+    0x68, 0x3e, 0x4d, 0x61, 0x79, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d,
+    0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x4a, 0x75, 0x6e, 0x3c, 0x2f, 0x6d, 0x6f,
+    0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x4a, 0x75, 0x6c, 0x3c,
+    0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x41,
+    0x75, 0x67, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74,
+    0x68, 0x3e, 0x53, 0x65, 0x70, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d,
+    0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x4f, 0x63, 0x74, 0x3c, 0x2f, 0x6d, 0x6f,
+    0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x4e, 0x6f, 0x76, 0x3c,
+    0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x44,
+    0x65, 0x63, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x6d, 0x6f, 0x6e, 0x74, 0x68,
+    0x4e, 0x61, 0x6d, 0x65, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x64, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x79,
+    0x3e, 0x53, 0x75, 0x6e, 0x64, 0x61, 0x79, 0x3c, 0x2f, 0x64, 0x61, 0x79,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64,
+    0x61, 0x79, 0x3e, 0x4d, 0x6f, 0x6e, 0x64, 0x61, 0x79, 0x3c, 0x2f, 0x64,
+    0x61, 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x64, 0x61, 0x79, 0x3e, 0x54, 0x75, 0x65, 0x73, 0x64, 0x61, 0x79,
+    0x3c, 0x2f, 0x64, 0x61, 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x79, 0x3e, 0x57, 0x65, 0x64, 0x6e,
+    0x65, 0x73, 0x64, 0x61, 0x79, 0x3c, 0x2f, 0x64, 0x61, 0x79, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x79,
+    0x3e, 0x54, 0x68, 0x75, 0x72, 0x73, 0x64, 0x61, 0x79, 0x3c, 0x2f, 0x64,
+    0x61, 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x64, 0x61, 0x79, 0x3e, 0x46, 0x72, 0x69, 0x64, 0x61, 0x79, 0x3c,
+    0x2f, 0x64, 0x61, 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x3c, 0x64, 0x61, 0x79, 0x3e, 0x53, 0x61, 0x74, 0x75, 0x72,
+    0x64, 0x61, 0x79, 0x3c, 0x2f, 0x64, 0x61, 0x79, 0x3e, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x61, 0x79, 0x4e, 0x61, 0x6d,
+    0x65, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64,
+    0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x61, 0x62, 0x62, 0x72,
+    0x3d, 0x22, 0x31, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x3c, 0x64, 0x61, 0x79, 0x3e, 0x53, 0x75, 0x6e, 0x3c, 0x2f,
+    0x64, 0x61, 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x64, 0x61, 0x79, 0x3e, 0x4d, 0x6f, 0x6e, 0x3c, 0x2f, 0x64,
+    0x61, 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x64, 0x61, 0x79, 0x3e, 0x54, 0x75, 0x65, 0x3c, 0x2f, 0x64, 0x61,
+    0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x64, 0x61, 0x79, 0x3e, 0x57, 0x65, 0x64, 0x3c, 0x2f, 0x64, 0x61, 0x79,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64,
+    0x61, 0x79, 0x3e, 0x54, 0x68, 0x75, 0x3c, 0x2f, 0x64, 0x61, 0x79, 0x3e,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x61,
+    0x79, 0x3e, 0x46, 0x72, 0x69, 0x3c, 0x2f, 0x64, 0x61, 0x79, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x79,
+    0x3e, 0x53, 0x61, 0x74, 0x3c, 0x2f, 0x64, 0x61, 0x79, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x61, 0x79, 0x4e, 0x61,
+    0x6d, 0x65, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x6d, 0x65, 0x72, 0x69, 0x64, 0x69, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65,
+    0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x6d, 0x65, 0x72, 0x69, 0x64, 0x69, 0x65, 0x6d, 0x3e, 0x41, 0x4d, 0x3c,
+    0x2f, 0x6d, 0x65, 0x72, 0x69, 0x64, 0x69, 0x65, 0x6d, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x65, 0x72, 0x69,
+    0x64, 0x69, 0x65, 0x6d, 0x3e, 0x50, 0x4d, 0x3c, 0x2f, 0x6d, 0x65, 0x72,
+    0x69, 0x64, 0x69, 0x65, 0x6d, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x2f, 0x6d, 0x65, 0x72, 0x69, 0x64, 0x69, 0x65, 0x6d, 0x4e,
+    0x61, 0x6d, 0x65, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x65, 0x72, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x72, 0x61, 0x3e,
+    0x42, 0x43, 0x3c, 0x2f, 0x65, 0x72, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x72, 0x61, 0x3e, 0x41, 0x44,
+    0x3c, 0x2f, 0x65, 0x72, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x2f, 0x65, 0x72, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x3e,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x63, 0x61, 0x6c, 0x65, 0x6e,
+    0x64, 0x61, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74,
+    0x74, 0x65, 0x72, 0x6e, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72,
+    0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x66, 0x75, 0x6c, 0x6c,
+    0x22, 0x3e, 0x45, 0x45, 0x45, 0x45, 0x2c, 0x20, 0x4d, 0x4d, 0x4d, 0x4d,
+    0x20, 0x44, 0x2c, 0x20, 0x59, 0x59, 0x59, 0x59, 0x3c, 0x2f, 0x64, 0x61,
+    0x74, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61,
+    0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22,
+    0x6c, 0x6f, 0x6e, 0x67, 0x22, 0x3e, 0x4d, 0x4d, 0x4d, 0x4d, 0x20, 0x44,
+    0x2c, 0x20, 0x59, 0x59, 0x59, 0x59, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65,
+    0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x74,
+    0x65, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x6d, 0x65,
+    0x64, 0x22, 0x3e, 0x4d, 0x4d, 0x4d, 0x20, 0x44, 0x2c, 0x20, 0x59, 0x59,
+    0x59, 0x59, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x74,
+    0x65, 0x72, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20,
+    0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x22,
+    0x3e, 0x4d, 0x2f, 0x44, 0x2f, 0x59, 0x59, 0x3c, 0x2f, 0x64, 0x61, 0x74,
+    0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x3e, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x74,
+    0x65, 0x72, 0x6e, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74,
+    0x69, 0x6d, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x3e,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x6d, 0x65,
+    0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65,
+    0x3d, 0x22, 0x66, 0x75, 0x6c, 0x6c, 0x22, 0x3e, 0x68, 0x3a, 0x4d, 0x4d,
+    0x3a, 0x53, 0x53, 0x20, 0x41, 0x20, 0x5a, 0x3c, 0x2f, 0x74, 0x69, 0x6d,
+    0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x3e, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x61, 0x74,
+    0x74, 0x65, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x6c,
+    0x6f, 0x6e, 0x67, 0x22, 0x3e, 0x68, 0x3a, 0x4d, 0x4d, 0x3a, 0x53, 0x53,
+    0x20, 0x41, 0x20, 0x5a, 0x3c, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x61,
+    0x74, 0x74, 0x65, 0x72, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72,
+    0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x6d, 0x65, 0x64, 0x22,
+    0x3e, 0x68, 0x3a, 0x4d, 0x4d, 0x3a, 0x53, 0x53, 0x20, 0x41, 0x3c, 0x2f,
+    0x74, 0x69, 0x6d, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x3e,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x6d, 0x65,
+    0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65,
+    0x3d, 0x22, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x22, 0x3e, 0x68, 0x3a, 0x4d,
+    0x4d, 0x20, 0x41, 0x3c, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x61, 0x74,
+    0x74, 0x65, 0x72, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f,
+    0x74, 0x69, 0x6d, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x54,
+    0x69, 0x6d, 0x65, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x3e, 0x47,
+    0x79, 0x4d, 0x64, 0x6b, 0x48, 0x6d, 0x73, 0x53, 0x45, 0x44, 0x46, 0x77,
+    0x57, 0x61, 0x68, 0x4b, 0x7a, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65,
+    0x54, 0x69, 0x6d, 0x65, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x3e,
+    0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+    0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x3e, 0x0a, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x50,
+    0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
+    0x22, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x22, 0x3e, 0x7a, 0x2c,
+    0x7a, 0x7a, 0x39, 0x2e, 0x7a, 0x7a, 0x7a, 0x3c, 0x2f, 0x6e, 0x75, 0x6d,
+    0x62, 0x65, 0x72, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65,
+    0x72, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d,
+    0x65, 0x3d, 0x22, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22,
+    0x3e, 0x24, 0x7a, 0x2c, 0x7a, 0x7a, 0x39, 0x2e, 0x39, 0x39, 0x7c, 0x28,
+    0x24, 0x7a, 0x2c, 0x7a, 0x7a, 0x39, 0x2e, 0x39, 0x39, 0x29, 0x3c, 0x2f,
+    0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72,
+    0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75,
+    0x6d, 0x62, 0x65, 0x72, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20,
+    0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e,
+    0x74, 0x22, 0x3e, 0x7a, 0x2c, 0x7a, 0x7a, 0x39, 0x25, 0x3c, 0x2f, 0x6e,
+    0x75, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x6e, 0x75, 0x6d, 0x62,
+    0x65, 0x72, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53,
+    0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x79, 0x6d,
+    0x62, 0x6f, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x64, 0x65,
+    0x63, 0x69, 0x6d, 0x61, 0x6c, 0x22, 0x3e, 0x2e, 0x3c, 0x2f, 0x6e, 0x75,
+    0x6d, 0x62, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65,
+    0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65,
+    0x3d, 0x22, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x3e,
+    0x2c, 0x3c, 0x2f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x79, 0x6d,
+    0x62, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+    0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x70, 0x65, 0x72, 0x63, 0x65,
+    0x6e, 0x74, 0x22, 0x3e, 0x25, 0x3c, 0x2f, 0x6e, 0x75, 0x6d, 0x62, 0x65,
+    0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x79,
+    0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x6d,
+    0x69, 0x6e, 0x75, 0x73, 0x22, 0x3e, 0x2d, 0x3c, 0x2f, 0x6e, 0x75, 0x6d,
+    0x62, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+    0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
+    0x22, 0x7a, 0x65, 0x72, 0x6f, 0x22, 0x3e, 0x30, 0x3c, 0x2f, 0x6e, 0x75,
+    0x6d, 0x62, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+    0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x79,
+    0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x20, 0x3c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x79,
+    0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x73,
+    0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x22, 0x3e, 0x24, 0x3c, 0x2f, 0x63, 0x75,
+    0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x63, 0x75, 0x72,
+    0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x20,
+    0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x69, 0x73, 0x6f, 0x6e, 0x61, 0x6d,
+    0x65, 0x22, 0x3e, 0x55, 0x53, 0x44, 0x3c, 0x2f, 0x63, 0x75, 0x72, 0x72,
+    0x65, 0x6e, 0x63, 0x79, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x63, 0x75, 0x72, 0x72, 0x65,
+    0x6e, 0x63, 0x79, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x20, 0x6e, 0x61,
+    0x6d, 0x65, 0x3d, 0x22, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x22,
+    0x3e, 0x2e, 0x3c, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79,
+    0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x79,
+    0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c,
+    0x74, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x73, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61,
+    0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x4d, 0x79, 0x72,
+    0x69, 0x61, 0x64, 0x20, 0x50, 0x72, 0x6f, 0x22, 0x2f, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61,
+    0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x4d, 0x69, 0x6e,
+    0x69, 0x6f, 0x6e, 0x20, 0x50, 0x72, 0x6f, 0x22, 0x2f, 0x3e, 0x0a, 0x20,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61,
+    0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x43, 0x6f, 0x75,
+    0x72, 0x69, 0x65, 0x72, 0x20, 0x53, 0x74, 0x64, 0x22, 0x2f, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x66,
+    0x61, 0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x41, 0x64,
+    0x6f, 0x62, 0x65, 0x20, 0x50, 0x69, 0x20, 0x53, 0x74, 0x64, 0x22, 0x2f,
+    0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x79, 0x70,
+    0x65, 0x66, 0x61, 0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22,
+    0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x48, 0x65, 0x62, 0x72, 0x65, 0x77,
+    0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74,
+    0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65,
+    0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x41, 0x72, 0x61, 0x62,
+    0x69, 0x63, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x20, 0x6e, 0x61,
+    0x6d, 0x65, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x54, 0x68,
+    0x61, 0x69, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x20, 0x6e, 0x61,
+    0x6d, 0x65, 0x3d, 0x22, 0x4b, 0x6f, 0x7a, 0x75, 0x6b, 0x61, 0x20, 0x47,
+    0x6f, 0x74, 0x68, 0x69, 0x63, 0x20, 0x50, 0x72, 0x6f, 0x2d, 0x56, 0x49,
+    0x20, 0x4d, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x20, 0x6e, 0x61,
+    0x6d, 0x65, 0x3d, 0x22, 0x4b, 0x6f, 0x7a, 0x75, 0x6b, 0x61, 0x20, 0x4d,
+    0x69, 0x6e, 0x63, 0x68, 0x6f, 0x20, 0x50, 0x72, 0x6f, 0x2d, 0x56, 0x49,
+    0x20, 0x52, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x20, 0x6e, 0x61,
+    0x6d, 0x65, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x4d, 0x69,
+    0x6e, 0x67, 0x20, 0x53, 0x74, 0x64, 0x20, 0x4c, 0x22, 0x2f, 0x3e, 0x0a,
+    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x66,
+    0x61, 0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x41, 0x64,
+    0x6f, 0x62, 0x65, 0x20, 0x53, 0x6f, 0x6e, 0x67, 0x20, 0x53, 0x74, 0x64,
+    0x20, 0x4c, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    0x3c, 0x74, 0x79, 0x70, 0x65, 0x66, 0x61, 0x63, 0x65, 0x20, 0x6e, 0x61,
+    0x6d, 0x65, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x4d, 0x79,
+    0x75, 0x6e, 0x67, 0x6a, 0x6f, 0x20, 0x53, 0x74, 0x64, 0x20, 0x4d, 0x22,
+    0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x74, 0x79, 0x70,
+    0x65, 0x66, 0x61, 0x63, 0x65, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f,
+    0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x3e, 0x0a, 0x3c, 0x2f, 0x6c, 0x6f,
+    0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x3e, 0x0a, 0x65, 0x6e, 0x64,
+    0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x0a, 0x65, 0x6e, 0x64, 0x6f, 0x62,
+    0x6a, 0x0a, 0x37, 0x20, 0x30, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3c, 0x3c,
+    0x0a, 0x20, 0x20, 0x2f, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x31,
+    0x31, 0x0a, 0x3e, 0x3e, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x0a,
+    0x3c, 0x2f, 0x78, 0x64, 0x70, 0x3a, 0x78, 0x64, 0x70, 0x3e, 0x0a, 0x65,
+    0x6e, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x0a, 0x65, 0x6e, 0x64,
+    0x6f, 0x62, 0x6a, 0x0a, 0x38, 0x20, 0x30, 0x20, 0x6f, 0x62, 0x6a, 0x20,
+    0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f, 0x54, 0x79, 0x70, 0x65, 0x20, 0x2f,
+    0x50, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x43, 0x6f, 0x75,
+    0x6e, 0x74, 0x20, 0x31, 0x0a, 0x20, 0x20, 0x2f, 0x4b, 0x69, 0x64, 0x73,
+    0x20, 0x5b, 0x39, 0x20, 0x30, 0x20, 0x52, 0x5d, 0x0a, 0x3e, 0x3e, 0x0a,
+    0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a, 0x0a, 0x39, 0x20, 0x30, 0x20, 0x6f,
+    0x62, 0x6a, 0x20, 0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f, 0x54, 0x79, 0x70,
+    0x65, 0x20, 0x2f, 0x50, 0x61, 0x67, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x50,
+    0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x32, 0x20, 0x30, 0x20, 0x52, 0x0a,
+    0x20, 0x20, 0x2f, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x42, 0x6f, 0x78, 0x20,
+    0x5b, 0x30, 0x20, 0x30, 0x20, 0x36, 0x31, 0x32, 0x20, 0x37, 0x39, 0x32,
+    0x5d, 0x0a, 0x3e, 0x3e, 0x0a, 0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a, 0x0a,
+    0x78, 0x72, 0x65, 0x66, 0x0a, 0x30, 0x20, 0x31, 0x30, 0x0a, 0x30, 0x30,
+    0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x36, 0x35, 0x35,
+    0x33, 0x35, 0x20, 0x66, 0x20, 0x0a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0x30, 0x30, 0x31, 0x35, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x6e,
+    0x20, 0x0a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x39, 0x39,
+    0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x6e, 0x20, 0x0a, 0x30, 0x30,
+    0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x35, 0x38, 0x20, 0x30, 0x30, 0x30,
+    0x30, 0x30, 0x20, 0x6e, 0x20, 0x0a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0x30, 0x35, 0x33, 0x34, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x6e,
+    0x20, 0x0a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x32, 0x32, 0x38,
+    0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x6e, 0x20, 0x0a, 0x30, 0x30,
+    0x30, 0x30, 0x30, 0x30, 0x31, 0x37, 0x36, 0x32, 0x20, 0x30, 0x30, 0x30,
+    0x30, 0x30, 0x20, 0x6e, 0x20, 0x0a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0x35, 0x32, 0x37, 0x30, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x6e,
+    0x20, 0x0a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35, 0x33, 0x33, 0x32,
+    0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x6e, 0x20, 0x0a, 0x30, 0x30,
+    0x30, 0x30, 0x30, 0x30, 0x35, 0x33, 0x39, 0x35, 0x20, 0x30, 0x30, 0x30,
+    0x30, 0x30, 0x20, 0x6e, 0x20, 0x0a, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65,
+    0x72, 0x20, 0x3c, 0x3c, 0x0a, 0x20, 0x20, 0x2f, 0x52, 0x6f, 0x6f, 0x74,
+    0x20, 0x31, 0x20, 0x30, 0x20, 0x52, 0x0a, 0x20, 0x20, 0x2f, 0x53, 0x69,
+    0x7a, 0x65, 0x20, 0x31, 0x30, 0x0a, 0x3e, 0x3e, 0x0a, 0x73, 0x74, 0x61,
+    0x72, 0x74, 0x78, 0x72, 0x65, 0x66, 0x0a, 0x35, 0x34, 0x37, 0x32, 0x0a,
+    0x25, 0x25, 0x45, 0x4f, 0x46, 0x0a};
+
+}  // namespace
+
+class PDFiumFormCalcContextFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumFormCalcContextFuzzer(const uint8_t* data, size_t size)
+      : data_(data), size_(size) {}
+  ~PDFiumFormCalcContextFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 2; }
+
+  // Return false if XFA doesn't load as otherwise we're duplicating the work
+  // done by the non-xfa fuzzer.
+  bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+    return FPDF_LoadXFA(doc);
+  }
+
+  void OnRenderFinished(FPDF_DOCUMENT doc) override {
+    auto* context = static_cast<CPDFXFA_Context*>(
+        CPDFDocumentFromFPDFDocument(doc)->GetExtension());
+    CXFA_Document* xfa_document = context->GetXFADoc()->GetXFADoc();
+    CFXJSE_Engine* script_context = xfa_document->GetScriptContext();
+
+    CXFA_EventParam params;
+    params.m_bCancelAction = false;
+    script_context->SetEventParam(&params);
+    ByteStringView data_view(data_, size_);
+
+    auto value = pdfium::MakeUnique<CFXJSE_Value>(script_context->GetIsolate());
+    script_context->RunScript(CXFA_Script::Type::Formcalc,
+                              WideString::FromUTF8(data_view).AsStringView(),
+                              value.get(), xfa_document->GetRoot());
+
+    script_context->SetEventParam(nullptr);
+  }
+
+ private:
+  const uint8_t* data_;
+  size_t size_;
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  PDFiumFormCalcContextFuzzer fuzzer(data, size);
+  fuzzer.RenderPdf(reinterpret_cast<const char*>(kSimpleXfaPdfData),
+                   sizeof(kSimpleXfaPdfData));
+  return 0;
+}
diff --git a/testing/libfuzzer/pdf_formcalc_fuzzer.cc b/testing/fuzzers/pdf_formcalc_fuzzer.cc
similarity index 100%
rename from testing/libfuzzer/pdf_formcalc_fuzzer.cc
rename to testing/fuzzers/pdf_formcalc_fuzzer.cc
diff --git a/testing/fuzzers/pdf_fuzzer_init.cc b/testing/fuzzers/pdf_fuzzer_init.cc
new file mode 100644
index 0000000..954eed0
--- /dev/null
+++ b/testing/fuzzers/pdf_fuzzer_init.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+#include "public/fpdfview.h"
+
+// Initialize the library once for all runs of the fuzzer.
+struct TestCase {
+  TestCase() {
+    memset(&config, '\0', sizeof(config));
+    config.version = 2;
+    config.m_pUserFontPaths = nullptr;
+    config.m_pIsolate = nullptr;
+    config.m_v8EmbedderSlot = 0;
+    FPDF_InitLibraryWithConfig(&config);
+  }
+  FPDF_LIBRARY_CONFIG config;
+};
+
+// pdf_fuzzer_init.cc and pdfium_fuzzer_init_public.cc are mutually exclusive
+// and should not be built together. They deliberately have the same global
+// variable.
+static TestCase* g_test_case = new TestCase();
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.cc b/testing/fuzzers/pdf_fuzzer_init_public.cc
new file mode 100644
index 0000000..5ece0bc
--- /dev/null
+++ b/testing/fuzzers/pdf_fuzzer_init_public.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+#include <memory>
+
+#include "public/fpdf_ext.h"
+
+#ifdef PDF_ENABLE_V8
+#include "testing/free_deleter.h"
+#include "testing/v8_initializer.h"
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8.h"
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#elif defined(__APPLE__)
+#include <mach-o/dyld.h>
+#else  // Linux
+#include <linux/limits.h>
+#include <unistd.h>
+#endif  // _WIN32
+
+namespace {
+
+#ifdef PDF_ENABLE_V8
+std::string ProgramPath() {
+  std::string result;
+
+#ifdef _WIN32
+  char path[MAX_PATH];
+  DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH);
+  if (len != 0)
+    result = std::string(path, len);
+#elif defined(__APPLE__)
+  char path[PATH_MAX];
+  unsigned int len = PATH_MAX;
+  if (!_NSGetExecutablePath(path, &len)) {
+    std::unique_ptr<char, pdfium::FreeDeleter> resolved_path(
+        realpath(path, nullptr));
+    if (resolved_path.get())
+      result = std::string(resolved_path.get());
+  }
+#else  // Linux
+  char path[PATH_MAX];
+  ssize_t len = readlink("/proc/self/exe", path, PATH_MAX);
+  if (len > 0)
+    result = std::string(path, len);
+#endif
+  return result;
+}
+#endif  // PDF_ENABLE_V8
+
+}  // namespace
+
+// Initialize the library once for all runs of the fuzzer.
+struct TestCase {
+  TestCase() {
+#ifdef PDF_ENABLE_V8
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+    platform = InitializeV8ForPDFiumWithStartupData(
+        ProgramPath(), std::string(), &snapshot_blob);
+#else
+    platform = InitializeV8ForPDFium(ProgramPath());
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+#endif  // PDF_ENABLE_V8
+
+    memset(&config, '\0', sizeof(config));
+    config.version = 2;
+    config.m_pUserFontPaths = nullptr;
+    config.m_pIsolate = nullptr;
+    config.m_v8EmbedderSlot = 0;
+    FPDF_InitLibraryWithConfig(&config);
+
+    memset(&unsupport_info, '\0', sizeof(unsupport_info));
+    unsupport_info.version = 1;
+    unsupport_info.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {};
+    FSDK_SetUnSpObjProcessHandler(&unsupport_info);
+  }
+
+#ifdef PDF_ENABLE_V8
+  std::unique_ptr<v8::Platform> platform;
+  v8::StartupData snapshot_blob;
+#endif
+
+  FPDF_LIBRARY_CONFIG config;
+  UNSUPPORT_INFO unsupport_info;
+};
+
+// pdf_fuzzer_init.cc and pdfium_fuzzer_init_public.cc are mutually exclusive
+// and should not be built together. They deliberately have the same global
+// variable.
+static TestCase* g_test_case = new TestCase();
diff --git a/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
new file mode 100644
index 0000000..e31decc
--- /dev/null
+++ b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "core/fxcrt/widestring.h"
+#include "fxjs/fx_date_helpers.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  auto* short_data = reinterpret_cast<const unsigned short*>(data);
+  size_t short_size = size / sizeof(unsigned short);
+  if (short_size > 2 && short_size < 8192) {
+    double ignore;
+    size_t short_len1 = short_size / 2;
+    size_t short_len2 = short_size - short_len1;
+    WideString input1 = WideString::FromUTF16LE(short_data, short_len1);
+    WideString input2 =
+        WideString::FromUTF16LE(short_data + short_len1, short_len2);
+    FX_ParseDateUsingFormat(input1, input2, &ignore);
+  }
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_hint_table_fuzzer.cc b/testing/fuzzers/pdf_hint_table_fuzzer.cc
new file mode 100644
index 0000000..1540074
--- /dev/null
+++ b/testing/fuzzers/pdf_hint_table_fuzzer.cc
@@ -0,0 +1,93 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+
+#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_hint_tables.h"
+#include "core/fpdfapi/parser/cpdf_linearized_header.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fxcrt/cfx_bitstream.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
+
+int32_t GetData(const int32_t** data32, const uint8_t** data, size_t* size) {
+  const int32_t* ret = *data32;
+  ++(*data32);
+  *data += 4;
+  *size -= 4;
+  return *ret;
+}
+
+class HintTableForFuzzing final : public CPDF_HintTables {
+ public:
+  HintTableForFuzzing(CPDF_LinearizedHeader* pLinearized,
+                      int shared_hint_table_offset)
+      : CPDF_HintTables(nullptr, pLinearized),
+        shared_hint_table_offset_(shared_hint_table_offset) {}
+  ~HintTableForFuzzing() {}
+
+  void Fuzz(const uint8_t* data, size_t size) {
+    if (shared_hint_table_offset_ <= 0)
+      return;
+
+    if (size < static_cast<size_t>(shared_hint_table_offset_))
+      return;
+
+    CFX_BitStream bs(pdfium::make_span(data, size));
+    if (!ReadPageHintTable(&bs))
+      return;
+    ReadSharedObjHintTable(&bs, shared_hint_table_offset_);
+  }
+
+ private:
+  int shared_hint_table_offset_;
+};
+
+class FakeLinearized final : public CPDF_LinearizedHeader {
+ public:
+  explicit FakeLinearized(CPDF_Dictionary* linearized_dict)
+      : CPDF_LinearizedHeader(linearized_dict, 0) {}
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Need 28 bytes for |linearized_dict|.
+  // The header section of page offset hint table is 36 bytes.
+  // The header section of shared object hint table is 24 bytes.
+  if (size < 28 + 36 + 24)
+    return 0;
+
+  const int32_t* data32 = reinterpret_cast<const int32_t*>(data);
+
+  auto linearized_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  // Set initial value.
+  linearized_dict->SetNewFor<CPDF_Boolean>("Linearized", true);
+  // Set first page end offset
+  linearized_dict->SetNewFor<CPDF_Number>("E", GetData(&data32, &data, &size));
+  // Set page count
+  linearized_dict->SetNewFor<CPDF_Number>("N", GetData(&data32, &data, &size));
+  // Set first page obj num
+  linearized_dict->SetNewFor<CPDF_Number>("O", GetData(&data32, &data, &size));
+  // Set first page no
+  linearized_dict->SetNewFor<CPDF_Number>("P", GetData(&data32, &data, &size));
+
+  auto hint_info = pdfium::MakeRetain<CPDF_Array>();
+  // Add primary hint stream offset
+  hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
+  // Add primary hint stream size
+  hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
+  // Set hint stream info.
+  linearized_dict->SetFor("H", std::move(hint_info));
+
+  const int shared_hint_table_offset = GetData(&data32, &data, &size);
+
+  {
+    FakeLinearized linearized(linearized_dict.Get());
+    HintTableForFuzzing hint_table(&linearized, shared_hint_table_offset);
+    hint_table.Fuzz(data, size);
+  }
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_jpx_fuzzer.cc b/testing/fuzzers/pdf_jpx_fuzzer.cc
new file mode 100644
index 0000000..3986ae2
--- /dev/null
+++ b/testing/fuzzers/pdf_jpx_fuzzer.cc
@@ -0,0 +1,78 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fxcodec/jpx/cjpx_decoder.h"
+#include "core/fxcodec/jpx/jpxmodule.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_dib.h"
+
+namespace {
+
+const uint32_t kMaxJPXFuzzSize = 100 * 1024 * 1024;  // 100 MB
+
+bool CheckImageSize(const CJPX_Decoder::JpxImageInfo& image_info) {
+  static constexpr uint32_t kMemLimitBytes = 1024 * 1024 * 1024;  // 1 GB.
+  FX_SAFE_UINT32 mem = image_info.width;
+  mem *= image_info.height;
+  mem *= image_info.components;
+  return mem.IsValid() && mem.ValueOrDie() <= kMemLimitBytes;
+}
+
+}  // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 1)
+    return 0;
+
+  std::unique_ptr<CJPX_Decoder> decoder = JpxModule::CreateDecoder(
+      {data + 1, size - 1},
+      static_cast<CJPX_Decoder::ColorSpaceOption>(data[0] % 3));
+  if (!decoder)
+    return 0;
+
+  // A call to StartDecode could be too expensive if image size is very big, so
+  // check size before calling StartDecode().
+  CJPX_Decoder::JpxImageInfo image_info = decoder->GetInfo();
+  if (!CheckImageSize(image_info))
+    return 0;
+
+  if (!decoder->StartDecode())
+    return 0;
+
+  // StartDecode() could change image size, so check again.
+  image_info = decoder->GetInfo();
+  if (!CheckImageSize(image_info))
+    return 0;
+
+  FXDIB_Format format;
+  if (image_info.components == 1) {
+    format = FXDIB_8bppRgb;
+  } else if (image_info.components <= 3) {
+    format = FXDIB_Rgb;
+  } else if (image_info.components == 4) {
+    format = FXDIB_Rgb32;
+  } else {
+    image_info.width = (image_info.width * image_info.components + 2) / 3;
+    format = FXDIB_Rgb;
+  }
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!bitmap->Create(image_info.width, image_info.height, format))
+    return 0;
+
+  if (bitmap->GetHeight() <= 0 ||
+      kMaxJPXFuzzSize / bitmap->GetPitch() <
+          static_cast<uint32_t>(bitmap->GetHeight()))
+    return 0;
+
+  decoder->Decode(bitmap->GetBuffer(), bitmap->GetPitch(),
+                  /*swap_rgb=*/false);
+
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_lzw_fuzzer.cc b/testing/fuzzers/pdf_lzw_fuzzer.cc
new file mode 100644
index 0000000..e4d993e
--- /dev/null
+++ b/testing/fuzzers/pdf_lzw_fuzzer.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
+#include "third_party/base/numerics/safe_conversions.h"
+
+// Between 2x and 5x is a standard range for LZW according to a quick
+// search of papers. Running up to 10x to catch any niche cases.
+constexpr uint32_t kMinCompressionRatio = 2;
+constexpr uint32_t kMaxCompressionRatio = 10;
+
+void LZWFuzz(const uint8_t* src_buf,
+             size_t src_size,
+             uint8_t color_exp,
+             uint8_t code_exp) {
+  std::unique_ptr<CFX_LZWDecompressor> decompressor =
+      CFX_LZWDecompressor::Create(color_exp, code_exp);
+  if (!decompressor)
+    return;
+
+  for (uint32_t compressions_ratio = kMinCompressionRatio;
+       compressions_ratio <= kMaxCompressionRatio; compressions_ratio++) {
+    std::vector<uint8_t> dest_buf(compressions_ratio * src_size);
+    // This cast should be safe since the caller is checking for overflow on
+    // the initial data.
+    uint32_t dest_size = static_cast<uint32_t>(dest_buf.size());
+    if (CFX_GifDecodeStatus::InsufficientDestSize !=
+        decompressor->Decode(src_buf, src_size, dest_buf.data(), &dest_size)) {
+      return;
+    }
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Need at least 3 bytes to do anything.
+  if (size < 3)
+    return 0;
+
+  // Normally the GIF would provide the code and color sizes, instead, going
+  // to assume they are the first two bytes of data provided.
+  uint8_t color_exp = data[0];
+  uint8_t code_exp = data[1];
+  const uint8_t* lzw_data = data + 2;
+  uint32_t lzw_data_size = static_cast<uint32_t>(size - 2);
+  // Check that there isn't going to be an overflow in the destination buffer
+  // size.
+  if (lzw_data_size >
+      std::numeric_limits<uint32_t>::max() / kMaxCompressionRatio) {
+    return 0;
+  }
+
+  LZWFuzz(lzw_data, lzw_data_size, color_exp, code_exp);
+
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_nametree_fuzzer.cc b/testing/fuzzers/pdf_nametree_fuzzer.cc
new file mode 100644
index 0000000..9a37024
--- /dev/null
+++ b/testing/fuzzers/pdf_nametree_fuzzer.cc
@@ -0,0 +1,75 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdint>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_streamparser.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fpdfdoc/cpdf_nametree.h"
+#include "third_party/base/span.h"
+
+struct Params {
+  bool delete_backwards;
+  uint8_t count;
+  std::vector<WideString> names;
+};
+
+std::vector<WideString> GetNames(uint8_t count,
+                                 FuzzedDataProvider* data_provider) {
+  std::vector<WideString> names;
+  names.reserve(count);
+  for (size_t i = 0; i < count; ++i) {
+    // The name is not that interesting here. Keep it short.
+    constexpr size_t kMaxNameLen = 10;
+    std::string str = data_provider->ConsumeRandomLengthString(kMaxNameLen);
+    names.push_back(WideString::FromUTF16LE(
+        reinterpret_cast<const unsigned short*>(str.data()),
+        str.size() / sizeof(unsigned short)));
+  }
+  return names;
+}
+
+Params GetParams(FuzzedDataProvider* data_provider) {
+  Params params;
+  params.delete_backwards = data_provider->ConsumeBool();
+  params.count = data_provider->ConsumeIntegralInRange(1, 255);
+  params.names = GetNames(params.count, data_provider);
+  return params;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+  Params params = GetParams(&data_provider);
+
+  // |remaining| needs to outlive |parser|.
+  std::vector<uint8_t> remaining =
+      data_provider.ConsumeRemainingBytes<uint8_t>();
+  if (remaining.empty())
+    return 0;
+
+  CPDF_StreamParser parser(remaining);
+  auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  CPDF_NameTree name_tree(dict.Get());
+  for (const auto& name : params.names) {
+    RetainPtr<CPDF_Object> obj = parser.ReadNextObject(
+        /*bAllowNestedArray*/ true, /*bInArray=*/false, /*dwRecursionLevel=*/0);
+    if (!obj)
+      break;
+
+    name_tree.AddValueAndName(std::move(obj), name);
+  }
+
+  if (params.delete_backwards) {
+    for (size_t i = params.count; i > 0; --i)
+      name_tree.DeleteValueAndName(i);
+  } else {
+    for (size_t i = 0; i < params.count; ++i)
+      name_tree.DeleteValueAndName(0);
+  }
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_psengine_fuzzer.cc b/testing/fuzzers/pdf_psengine_fuzzer.cc
new file mode 100644
index 0000000..d72088d
--- /dev/null
+++ b/testing/fuzzers/pdf_psengine_fuzzer.cc
@@ -0,0 +1,15 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+
+#include "core/fpdfapi/page/cpdf_psengine.h"
+#include "third_party/base/span.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  CPDF_PSEngine engine;
+  if (engine.Parse(pdfium::make_span(data, size)))
+    engine.Execute();
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
new file mode 100644
index 0000000..4b20068
--- /dev/null
+++ b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
@@ -0,0 +1,66 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/cfx_cliprgn.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_dib.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+constexpr FXDIB_Format kFormat[] = {
+    FXDIB_Invalid, FXDIB_1bppRgb,   FXDIB_8bppRgb,  FXDIB_Rgb,
+    FXDIB_Rgb32,   FXDIB_1bppMask,  FXDIB_8bppMask, FXDIB_8bppRgba,
+    FXDIB_Rgba,    FXDIB_Argb,      FXDIB_1bppCmyk, FXDIB_8bppCmyk,
+    FXDIB_Cmyk,    FXDIB_8bppCmyka, FXDIB_Cmyka};
+
+}  // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  constexpr size_t kParameterSize = 33;
+  if (size < kParameterSize)
+    return 0;
+
+  int width = GetInteger(data);
+  int height = GetInteger(data + 4);
+  uint32_t argb = GetInteger(data + 8);
+  int src_left = GetInteger(data + 12);
+  int src_top = GetInteger(data + 16);
+  int dest_left = GetInteger(data + 20);
+  int dest_top = GetInteger(data + 24);
+
+  BlendMode blend_mode = static_cast<BlendMode>(
+      data[28] % (static_cast<int>(BlendMode::kLast) + 1));
+  FXDIB_Format dest_format = kFormat[data[29] % FX_ArraySize(kFormat)];
+  FXDIB_Format src_format = kFormat[data[30] % FX_ArraySize(kFormat)];
+  bool is_clip = !(data[31] % 2);
+  bool is_rgb_byte_order = !(data[32] % 2);
+  size -= kParameterSize;
+  data += kParameterSize;
+
+  auto src_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  auto dest_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!src_bitmap->Create(width, height, src_format) ||
+      !dest_bitmap->Create(width, height, dest_format)) {
+    return 0;
+  }
+  if (!src_bitmap->GetBuffer() || !dest_bitmap->GetBuffer()) {
+    return 0;
+  }
+
+  std::unique_ptr<CFX_ClipRgn> clip_rgn;
+  if (is_clip)
+    clip_rgn = pdfium::MakeUnique<CFX_ClipRgn>(width, height);
+  if (src_bitmap->IsAlphaMask()) {
+    dest_bitmap->CompositeMask(dest_left, dest_top, width, height, src_bitmap,
+                               argb, src_left, src_top, blend_mode,
+                               clip_rgn.get(), is_rgb_byte_order);
+  } else {
+    dest_bitmap->CompositeBitmap(dest_left, dest_top, width, height, src_bitmap,
+                                 src_left, src_top, blend_mode, clip_rgn.get(),
+                                 is_rgb_byte_order);
+  }
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_streamparser_fuzzer.cc b/testing/fuzzers/pdf_streamparser_fuzzer.cc
new file mode 100644
index 0000000..2bbda5e
--- /dev/null
+++ b/testing/fuzzers/pdf_streamparser_fuzzer.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+
+#include "core/fpdfapi/page/cpdf_streamparser.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
+#include "third_party/base/span.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  CPDF_StreamParser parser(pdfium::make_span(data, size));
+  while (RetainPtr<CPDF_Object> pObj = parser.ReadNextObject(true, false, 0))
+    continue;
+
+  return 0;
+}
diff --git a/testing/fuzzers/pdf_xml_fuzzer.cc b/testing/fuzzers/pdf_xml_fuzzer.cc
new file mode 100644
index 0000000..e858f5b
--- /dev/null
+++ b/testing/fuzzers/pdf_xml_fuzzer.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "third_party/base/span.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FX_SAFE_SIZE_T safe_size = size;
+  if (!safe_size.IsValid())
+    return 0;
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::make_span(data, size));
+  CFX_XMLParser parser(stream);
+  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
+  if (!doc || !doc->GetRoot())
+    return 0;
+
+  for (CFX_XMLNode* pXMLNode = doc->GetRoot()->GetFirstChild(); pXMLNode;
+       pXMLNode = pXMLNode->GetNextSibling()) {
+    if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement)
+      break;
+  }
+  return 0;
+}
diff --git a/testing/fuzzers/pdfium_fuzzer.cc b/testing/fuzzers/pdfium_fuzzer.cc
new file mode 100644
index 0000000..dc15378
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumFuzzer() = default;
+  ~PDFiumFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 1; }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  PDFiumFuzzer fuzzer;
+  fuzzer.RenderPdf(reinterpret_cast<const char*>(data), size);
+  return 0;
+}
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.cc b/testing/fuzzers/pdfium_fuzzer_helper.cc
new file mode 100644
index 0000000..266666d
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer_helper.cc
@@ -0,0 +1,235 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+#include <assert.h>
+#include <limits.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "public/cpp/fpdf_scopers.h"
+#include "public/fpdf_dataavail.h"
+#include "public/fpdf_ext.h"
+#include "public/fpdf_text.h"
+#include "third_party/base/span.h"
+
+namespace {
+
+class FuzzerTestLoader {
+ public:
+  explicit FuzzerTestLoader(pdfium::span<const char> span) : m_Span(span) {}
+
+  static int GetBlock(void* param,
+                      unsigned long pos,
+                      unsigned char* pBuf,
+                      unsigned long size) {
+    FuzzerTestLoader* pLoader = static_cast<FuzzerTestLoader*>(param);
+    if (pos + size < pos || pos + size > pLoader->m_Span.size()) {
+      NOTREACHED();
+      return 0;
+    }
+
+    memcpy(pBuf, &pLoader->m_Span[pos], size);
+    return 1;
+  }
+
+ private:
+  const pdfium::span<const char> m_Span;
+};
+
+int ExampleAppAlert(IPDF_JSPLATFORM*,
+                    FPDF_WIDESTRING,
+                    FPDF_WIDESTRING,
+                    int,
+                    int) {
+  return 0;
+}
+
+int ExampleAppResponse(IPDF_JSPLATFORM*,
+                       FPDF_WIDESTRING question,
+                       FPDF_WIDESTRING title,
+                       FPDF_WIDESTRING default_value,
+                       FPDF_WIDESTRING label,
+                       FPDF_BOOL is_password,
+                       void* response,
+                       int length) {
+  // UTF-16, always LE regardless of platform.
+  uint8_t* ptr = static_cast<uint8_t*>(response);
+  ptr[0] = 'N';
+  ptr[1] = 0;
+  ptr[2] = 'o';
+  ptr[3] = 0;
+  return 4;
+}
+
+void ExampleDocGotoPage(IPDF_JSPLATFORM*, int pageNumber) {}
+
+void ExampleDocMail(IPDF_JSPLATFORM*,
+                    void* mailData,
+                    int length,
+                    FPDF_BOOL UI,
+                    FPDF_WIDESTRING To,
+                    FPDF_WIDESTRING Subject,
+                    FPDF_WIDESTRING CC,
+                    FPDF_WIDESTRING BCC,
+                    FPDF_WIDESTRING Msg) {}
+
+FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
+  return true;
+}
+
+void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {}
+
+std::pair<int, int> GetRenderingAndFormFlagFromData(const char* data,
+                                                    size_t len) {
+  std::string data_str = std::string(data, len);
+  std::size_t data_hash = std::hash<std::string>()(data_str);
+
+  // The largest flag value is 0x4FFF, so just take 16 bits from |data_hash| at
+  // a time.
+  int render_flags = data_hash & 0xffff;
+  int form_flags = (data_hash >> 16) & 0xffff;
+  return std::make_pair(render_flags, form_flags);
+}
+
+}  // namespace
+
+PDFiumFuzzerHelper::PDFiumFuzzerHelper() = default;
+
+PDFiumFuzzerHelper::~PDFiumFuzzerHelper() = default;
+
+bool PDFiumFuzzerHelper::OnFormFillEnvLoaded(FPDF_DOCUMENT doc) {
+  return true;
+}
+
+void PDFiumFuzzerHelper::RenderPdf(const char* data, size_t len) {
+  int render_flags;
+  int form_flags;
+  std::tie(render_flags, form_flags) =
+      GetRenderingAndFormFlagFromData(data, len);
+
+  IPDF_JSPLATFORM platform_callbacks;
+  memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
+  platform_callbacks.version = 3;
+  platform_callbacks.app_alert = ExampleAppAlert;
+  platform_callbacks.app_response = ExampleAppResponse;
+  platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
+  platform_callbacks.Doc_mail = ExampleDocMail;
+
+  FPDF_FORMFILLINFO form_callbacks;
+  memset(&form_callbacks, '\0', sizeof(form_callbacks));
+  form_callbacks.version = GetFormCallbackVersion();
+  form_callbacks.m_pJsPlatform = &platform_callbacks;
+
+  FuzzerTestLoader loader({data, len});
+  FPDF_FILEACCESS file_access;
+  memset(&file_access, '\0', sizeof(file_access));
+  file_access.m_FileLen = static_cast<unsigned long>(len);
+  file_access.m_GetBlock = FuzzerTestLoader::GetBlock;
+  file_access.m_Param = &loader;
+
+  FX_FILEAVAIL file_avail;
+  memset(&file_avail, '\0', sizeof(file_avail));
+  file_avail.version = 1;
+  file_avail.IsDataAvail = Is_Data_Avail;
+
+  FX_DOWNLOADHINTS hints;
+  memset(&hints, '\0', sizeof(hints));
+  hints.version = 1;
+  hints.AddSegment = Add_Segment;
+
+  ScopedFPDFAvail pdf_avail(FPDFAvail_Create(&file_avail, &file_access));
+
+  int nRet = PDF_DATA_NOTAVAIL;
+  bool bIsLinearized = false;
+  ScopedFPDFDocument doc;
+  if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
+    doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
+    if (doc) {
+      while (nRet == PDF_DATA_NOTAVAIL)
+        nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
+
+      if (nRet == PDF_DATA_ERROR)
+        return;
+
+      nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
+      if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL)
+        return;
+
+      bIsLinearized = true;
+    }
+  } else {
+    doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
+  }
+
+  if (!doc)
+    return;
+
+  (void)FPDF_GetDocPermissions(doc.get());
+
+  ScopedFPDFFormHandle form(
+      FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
+  if (!OnFormFillEnvLoaded(doc.get()))
+    return;
+
+  FPDF_SetFormFieldHighlightColor(form.get(), FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD);
+  FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
+  FORM_DoDocumentJSAction(form.get());
+  FORM_DoDocumentOpenAction(form.get());
+
+  int page_count = FPDF_GetPageCount(doc.get());
+  for (int i = 0; i < page_count; ++i) {
+    if (bIsLinearized) {
+      nRet = PDF_DATA_NOTAVAIL;
+      while (nRet == PDF_DATA_NOTAVAIL)
+        nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
+
+      if (nRet == PDF_DATA_ERROR)
+        return;
+    }
+    RenderPage(doc.get(), form.get(), i, render_flags, form_flags);
+  }
+  OnRenderFinished(doc.get());
+  FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
+}
+
+bool PDFiumFuzzerHelper::RenderPage(FPDF_DOCUMENT doc,
+                                    FPDF_FORMHANDLE form,
+                                    int page_index,
+                                    int render_flags,
+                                    int form_flags) {
+  ScopedFPDFPage page(FPDF_LoadPage(doc, page_index));
+  if (!page)
+    return false;
+
+  ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get()));
+  FORM_OnAfterLoadPage(page.get(), form);
+  FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_OPEN);
+
+  const double scale = 1.0;
+  int width = static_cast<int>(FPDF_GetPageWidthF(page.get()) * scale);
+  int height = static_cast<int>(FPDF_GetPageHeightF(page.get()) * scale);
+  ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, 0));
+  if (bitmap) {
+    FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, 0xFFFFFFFF);
+    FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0,
+                          render_flags);
+    FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0,
+                 form_flags);
+  }
+  FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page.get(), form);
+  return !!bitmap;
+}
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.h b/testing/fuzzers/pdfium_fuzzer_helper.h
new file mode 100644
index 0000000..af14941
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer_helper.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_
+#define TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_
+
+#include <stdint.h>
+
+#include "public/fpdfview.h"
+
+class PDFiumFuzzerHelper {
+ public:
+  void RenderPdf(const char* data, size_t len);
+
+  virtual int GetFormCallbackVersion() const = 0;
+  virtual bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc);
+  virtual void OnRenderFinished(FPDF_DOCUMENT doc) {}
+
+ protected:
+  PDFiumFuzzerHelper();
+  virtual ~PDFiumFuzzerHelper();
+
+ private:
+  bool RenderPage(FPDF_DOCUMENT doc,
+                  FPDF_FORMHANDLE form,
+                  int page_index,
+                  int render_flags,
+                  int form_flags);
+};
+
+#endif  // TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_
diff --git a/testing/fuzzers/pdfium_fuzzer_util.cc b/testing/fuzzers/pdfium_fuzzer_util.cc
new file mode 100644
index 0000000..9238f0e
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer_util.cc
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+
+int GetInteger(const uint8_t* data) {
+  return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
diff --git a/testing/fuzzers/pdfium_fuzzer_util.h b/testing/fuzzers/pdfium_fuzzer_util.h
new file mode 100644
index 0000000..d82f44b
--- /dev/null
+++ b/testing/fuzzers/pdfium_fuzzer_util.h
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_PDFIUM_FUZZER_UTIL_H_
+#define TESTING_FUZZERS_PDFIUM_FUZZER_UTIL_H_
+
+#include <stdint.h>
+
+// Returns an integer from the first 4 bytes of |data|.
+int GetInteger(const uint8_t* data);
+
+#endif  // TESTING_FUZZERS_PDFIUM_FUZZER_UTIL_H_
diff --git a/testing/fuzzers/pdfium_xfa_fuzzer.cc b/testing/fuzzers/pdfium_xfa_fuzzer.cc
new file mode 100644
index 0000000..f9a69d4
--- /dev/null
+++ b/testing/fuzzers/pdfium_xfa_fuzzer.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumXFAFuzzer : public PDFiumFuzzerHelper {
+ public:
+  PDFiumXFAFuzzer() = default;
+  ~PDFiumXFAFuzzer() override = default;
+
+  int GetFormCallbackVersion() const override { return 2; }
+
+  // Return false if XFA doesn't load as otherwise we're duplicating the work
+  // done by the non-xfa fuzzer.
+  bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+    int form_type = FPDF_GetFormType(doc);
+    if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+      return false;
+    return FPDF_LoadXFA(doc);
+  }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  PDFiumXFAFuzzer fuzzer;
+  fuzzer.RenderPdf(reinterpret_cast<const char*>(data), size);
+  return 0;
+}
diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
new file mode 100644
index 0000000..c4b55b6
--- /dev/null
+++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+#include "testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h"
+
+class PDFiumLpmFuzzStub : public PDFiumFuzzerHelper {
+ public:
+  PDFiumLpmFuzzStub() = default;
+  ~PDFiumLpmFuzzStub() override = default;
+
+  int GetFormCallbackVersion() const override { return 2; }
+  // Allow fuzzer to fuzz XFA but don't require it to fuzz.
+  bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+    FPDF_LoadXFA(doc);
+    return true;
+  }
+};
+
+void PdfiumXFALPMFuzzStub(const char* pdf, size_t size) {
+  PDFiumLpmFuzzStub fuzz_stub;
+  fuzz_stub.RenderPdf(pdf, size);
+}
diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
new file mode 100644
index 0000000..bb6bf1d
--- /dev/null
+++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_PDFIUM_XFA_LPM_FUZZ_STUB_H_
+#define TESTING_FUZZERS_PDFIUM_XFA_LPM_FUZZ_STUB_H_
+
+#include "public/fpdfview.h"
+
+// LPM defines LLVMFuzzerTestOneInput, this function should be used by the LPM
+// harness to pass the deserialized proto to PDFium.
+FPDF_EXPORT void PdfiumXFALPMFuzzStub(const char* pdf, size_t size);
+
+#endif  // TESTING_FUZZERS_PDFIUM_XFA_LPM_FUZZ_STUB_H_
diff --git a/testing/fuzzers/xfa_codec_fuzzer.h b/testing/fuzzers/xfa_codec_fuzzer.h
new file mode 100644
index 0000000..4f5668d
--- /dev/null
+++ b/testing/fuzzers/xfa_codec_fuzzer.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_XFA_CODEC_FUZZER_H_
+#define TESTING_FUZZERS_XFA_CODEC_FUZZER_H_
+
+#include <memory>
+
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcodec/progressivedecoder.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
+
+// Support up to 64 MB. This prevents trivial OOM when MSAN is on and
+// time outs.
+const int kXFACodecFuzzerPixelLimit = 64000000;
+
+class XFACodecFuzzer {
+ public:
+  static int Fuzz(const uint8_t* data, size_t size, FXCODEC_IMAGE_TYPE type) {
+    auto* mgr = fxcodec::ModuleMgr::GetInstance();
+    std::unique_ptr<ProgressiveDecoder> decoder =
+        mgr->CreateProgressiveDecoder();
+    auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+        pdfium::make_span(data, size));
+    CFX_DIBAttribute attr;
+    FXCODEC_STATUS status = decoder->LoadImageInfo(source, type, &attr, true);
+    if (status != FXCODEC_STATUS_FRAME_READY)
+      return 0;
+
+    // Skipping very large images, since they will take a long time and may lead
+    // to OOM.
+    FX_SAFE_UINT32 bitmap_size = decoder->GetHeight();
+    bitmap_size *= decoder->GetWidth();
+    bitmap_size *= 4;  // From CFX_DIBitmap impl.
+    if (!bitmap_size.IsValid() ||
+        bitmap_size.ValueOrDie() > kXFACodecFuzzerPixelLimit) {
+      return 0;
+    }
+
+    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+    bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb);
+
+    size_t frames;
+    std::tie(status, frames) = decoder->GetFrames();
+    if (status != FXCODEC_STATUS_DECODE_READY || frames == 0)
+      return 0;
+
+    status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
+                                  bitmap->GetHeight());
+    while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+      status = decoder->ContinueDecode();
+
+    return 0;
+  }
+};
+
+#endif  // TESTING_FUZZERS_XFA_CODEC_FUZZER_H_
diff --git a/testing/fx_string_testhelpers.cpp b/testing/fx_string_testhelpers.cpp
index 443cc89..4a7bda7 100644
--- a/testing/fx_string_testhelpers.cpp
+++ b/testing/fx_string_testhelpers.cpp
@@ -6,7 +6,9 @@
 
 #include <iomanip>
 #include <ios>
-#include <string>
+
+#include "core/fxcrt/fx_string.h"
+#include "third_party/base/span.h"
 
 std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt) {
   os << dt.GetYear() << "-" << std::to_string(dt.GetMonth()) << "-"
@@ -16,3 +18,60 @@
      << std::to_string(dt.GetMillisecond());
   return os;
 }
+
+std::vector<std::string> StringSplit(const std::string& str, char delimiter) {
+  std::vector<std::string> result;
+  size_t pos = 0;
+  while (1) {
+    size_t found = str.find(delimiter, pos);
+    if (found == std::string::npos)
+      break;
+
+    result.push_back(str.substr(pos, found - pos));
+    pos = found + 1;
+  }
+  result.push_back(str.substr(pos));
+  return result;
+}
+
+std::string GetPlatformString(FPDF_WIDESTRING wstr) {
+  WideString wide_string =
+      WideString::FromUTF16LE(wstr, WideString::WStringLength(wstr));
+  return std::string(wide_string.ToUTF8().c_str());
+}
+
+std::wstring GetPlatformWString(FPDF_WIDESTRING wstr) {
+  if (!wstr)
+    return std::wstring();
+
+  size_t characters = 0;
+  while (wstr[characters])
+    ++characters;
+
+  std::wstring platform_string(characters, L'\0');
+  for (size_t i = 0; i < characters + 1; ++i) {
+    const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&wstr[i]);
+    platform_string[i] = ptr[0] + 256 * ptr[1];
+  }
+  return platform_string;
+}
+
+ScopedFPDFWideString GetFPDFWideString(const std::wstring& wstr) {
+  size_t length = sizeof(uint16_t) * (wstr.length() + 1);
+  ScopedFPDFWideString result(static_cast<FPDF_WCHAR*>(malloc(length)));
+  pdfium::span<uint8_t> result_span(reinterpret_cast<uint8_t*>(result.get()),
+                                    length);
+  size_t i = 0;
+  for (wchar_t w : wstr) {
+    result_span[i++] = w & 0xff;
+    result_span[i++] = (w >> 8) & 0xff;
+  }
+  result_span[i++] = 0;
+  result_span[i] = 0;
+  return result;
+}
+
+std::vector<FPDF_WCHAR> GetFPDFWideStringBuffer(size_t length_bytes) {
+  ASSERT(length_bytes % sizeof(FPDF_WCHAR) == 0);
+  return std::vector<FPDF_WCHAR>(length_bytes / sizeof(FPDF_WCHAR));
+}
diff --git a/testing/fx_string_testhelpers.h b/testing/fx_string_testhelpers.h
index 5a269a3..b90838a 100644
--- a/testing/fx_string_testhelpers.h
+++ b/testing/fx_string_testhelpers.h
@@ -5,61 +5,36 @@
 #ifndef TESTING_FX_STRING_TESTHELPERS_H_
 #define TESTING_FX_STRING_TESTHELPERS_H_
 
+#include <memory>
 #include <ostream>
+#include <string>
+#include <vector>
 
 #include "core/fxcrt/cfx_datetime.h"
-#include "core/fxcrt/fx_stream.h"
+#include "public/fpdfview.h"
+#include "testing/free_deleter.h"
 
 // Output stream operator so GTEST macros work with CFX_DateTime objects.
 std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt);
 
-class CFX_InvalidSeekableReadStream : public IFX_SeekableReadStream {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+std::vector<std::string> StringSplit(const std::string& str, char delimiter);
 
-  // IFX_SeekableReadStream overrides:
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override {
-    return false;
-  }
-  FX_FILESIZE GetSize() override { return data_size_; }
+// Converts a FPDF_WIDESTRING to a std::string.
+// Deals with differences between UTF16LE and UTF8.
+std::string GetPlatformString(FPDF_WIDESTRING wstr);
 
- private:
-  explicit CFX_InvalidSeekableReadStream(FX_FILESIZE data_size)
-      : data_size_(data_size) {}
+// Converts a FPDF_WIDESTRING to a std::wstring.
+// Deals with differences between UTF16LE and wchar_t.
+std::wstring GetPlatformWString(FPDF_WIDESTRING wstr);
 
-  FX_FILESIZE data_size_;
-};
+using ScopedFPDFWideString = std::unique_ptr<FPDF_WCHAR, pdfium::FreeDeleter>;
 
-class CFX_BufferSeekableReadStream : public IFX_SeekableReadStream {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+// Returns a newly allocated FPDF_WIDESTRING.
+// Deals with differences between UTF16LE and wchar_t.
+ScopedFPDFWideString GetFPDFWideString(const std::wstring& wstr);
 
-  // IFX_SeekableReadStream:
-  bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override {
-    if (offset < 0 || static_cast<size_t>(offset) >= data_size_)
-      return false;
-
-    if (static_cast<size_t>(offset) + size > data_size_)
-      size = data_size_ - static_cast<size_t>(offset);
-    if (size == 0)
-      return false;
-
-    memcpy(buffer, data_ + offset, size);
-    return true;
-  }
-
-  FX_FILESIZE GetSize() override {
-    return static_cast<FX_FILESIZE>(data_size_);
-  }
-
- private:
-  CFX_BufferSeekableReadStream(const unsigned char* src, size_t src_size)
-      : data_(src), data_size_(src_size) {}
-
-  const unsigned char* data_;
-  size_t data_size_;
-};
+// Returns a FPDF_WCHAR vector of |length_bytes| bytes. |length_bytes| must be a
+// multiple of sizeof(FPDF_WCHAR).
+std::vector<FPDF_WCHAR> GetFPDFWideStringBuffer(size_t length_bytes);
 
 #endif  // TESTING_FX_STRING_TESTHELPERS_H_
diff --git a/testing/gmock/BUILD.gn b/testing/gmock/BUILD.gn
new file mode 100644
index 0000000..d7de2e9
--- /dev/null
+++ b/testing/gmock/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# The file/directory layout of Google Test is not yet considered stable. Until
+# it stabilizes, Chromium code MUST use this target instead of reaching directly
+# into //third_party/googletest.
+
+import("//build_overrides/build.gni")
+
+source_set("gmock") {
+  testonly = true
+  sources = [
+    "include/gmock/gmock-actions.h",
+    "include/gmock/gmock-generated-function-mockers.h",
+    "include/gmock/gmock-matchers.h",
+    "include/gmock/gmock.h",
+  ]
+  deps = [ "//third_party/googletest:gmock" ]
+
+  # TODO(crbug.com/806952): Depending on gmock_mutant only if build_with_chromium,
+  # because gmock_mutant depends on //base which uses C++14. Since gmock is a
+  # third_party library used by other projects it should not include C++14 only code.
+  if (build_with_chromium) {
+    # Allow Chromium targets depending on gmock to #include testing/gmock_mutant.h
+    # without triggering a `gn check` error.
+    public_deps = [ "//testing:gmock_mutant" ]
+  }
+
+  public_configs = [
+    "//third_party/googletest:gmock_config",
+    "//third_party/googletest:gtest_config",
+  ]
+}
+
+# The file/directory layout of Google Test is not yet considered stable. Until
+# it stabilizes, Chromium code MUST use this target instead of reaching directly
+# into //third_party/googletest.
+source_set("gmock_main") {
+  testonly = true
+  deps = [ "//third_party/googletest:gmock_main" ]
+}
diff --git a/testing/gmock/include/gmock/DEPS b/testing/gmock/include/gmock/DEPS
new file mode 100644
index 0000000..c817a84
--- /dev/null
+++ b/testing/gmock/include/gmock/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/googletest/src/googlemock/include/gmock',
+]
diff --git a/testing/gmock/include/gmock/gmock-actions.h b/testing/gmock/include/gmock/gmock-actions.h
new file mode 100644
index 0000000..fb193e5
--- /dev/null
+++ b/testing/gmock/include/gmock/gmock-actions.h
@@ -0,0 +1,10 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googlemock/include/gmock/gmock-actions.h"
diff --git a/testing/gmock/include/gmock/gmock-generated-function-mockers.h b/testing/gmock/include/gmock/gmock-generated-function-mockers.h
new file mode 100644
index 0000000..57cbc0a
--- /dev/null
+++ b/testing/gmock/include/gmock/gmock-generated-function-mockers.h
@@ -0,0 +1,10 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googlemock/include/gmock/gmock-generated-function-mockers.h"
diff --git a/testing/gmock/include/gmock/gmock-matchers.h b/testing/gmock/include/gmock/gmock-matchers.h
new file mode 100644
index 0000000..25d25e9
--- /dev/null
+++ b/testing/gmock/include/gmock/gmock-matchers.h
@@ -0,0 +1,10 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googlemock/include/gmock/gmock-matchers.h"
diff --git a/testing/gmock/include/gmock/gmock.h b/testing/gmock/include/gmock/gmock.h
new file mode 100644
index 0000000..a344bcb
--- /dev/null
+++ b/testing/gmock/include/gmock/gmock.h
@@ -0,0 +1,10 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googlemock/include/gmock/gmock.h"
diff --git a/testing/gtest/BUILD.gn b/testing/gtest/BUILD.gn
new file mode 100644
index 0000000..ad0b269
--- /dev/null
+++ b/testing/gtest/BUILD.gn
@@ -0,0 +1,94 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build_overrides/gtest.gni")
+if (is_ios) {
+  import("//build/buildflag_header.gni")
+  import("//build/config/coverage/coverage.gni")
+  import("//build/config/ios/ios_sdk.gni")
+}
+
+config("gtest_direct_config") {
+  visibility = [ ":*" ]
+  defines = [ "UNIT_TEST" ]
+}
+
+# The file/directory layout of Google Test is not yet considered stable. Until
+# it stabilizes, Chromium code MUST use this target instead of reaching directly
+# into //third_party/googletest.
+static_library("gtest") {
+  testonly = true
+
+  sources = [
+    "include/gtest/gtest-death-test.h",
+    "include/gtest/gtest-message.h",
+    "include/gtest/gtest-param-test.h",
+    "include/gtest/gtest-spi.h",
+    "include/gtest/gtest.h",
+    "include/gtest/gtest_prod.h",
+
+    # This is a workaround for the issues below.
+    #
+    # 1) This target needs to be a static_library (not a source set) on Mac to
+    #    avoid the build errors in
+    #    https://codereview.chromium.org/2779193002#msg82.
+    # 2) A static_library must have at least one source file, to avoid build
+    #    errors on Mac and Windows. https://crbug.com/710334
+    # 3) A static_library with complete_static_lib = true, which would not
+    #    require adding the empty file, will result in duplicate symbols on
+    #    Android. https://codereview.chromium.org/2852613002/#ps20001
+    "empty.cc",
+  ]
+  public_deps = [ "//third_party/googletest:gtest" ]
+
+  public_configs = [ ":gtest_direct_config" ]
+
+  if (gtest_include_multiprocess) {
+    sources += [
+      "../multiprocess_func_list.cc",
+      "../multiprocess_func_list.h",
+    ]
+  }
+
+  if (gtest_include_platform_test) {
+    sources += [ "../platform_test.h" ]
+  }
+
+  if ((is_mac || is_ios) && gtest_include_objc_support) {
+    if (is_ios) {
+      set_sources_assignment_filter([])
+    }
+    sources += [
+      "../gtest_mac.h",
+      "../gtest_mac.mm",
+    ]
+    if (gtest_include_platform_test) {
+      sources += [ "../platform_test_mac.mm" ]
+    }
+    set_sources_assignment_filter(sources_assignment_filter)
+  }
+
+  if (is_ios && gtest_include_ios_coverage) {
+    sources += [
+      "../coverage_util_ios.h",
+      "../coverage_util_ios.mm",
+    ]
+    deps = [ ":ios_enable_coverage" ]
+  }
+}
+
+# The file/directory layout of Google Test is not yet considered stable. Until
+# it stabilizes, Chromium code MUST use this target instead of reaching directly
+# into //third_party/googletest.
+source_set("gtest_main") {
+  testonly = true
+  deps = [ "//third_party/googletest:gtest_main" ]
+}
+
+if (is_ios) {
+  buildflag_header("ios_enable_coverage") {
+    header = "ios_enable_coverage.h"
+    flags = [ "IOS_ENABLE_COVERAGE=$use_clang_coverage" ]
+  }
+}
diff --git a/testing/gtest/empty.cc b/testing/gtest/empty.cc
new file mode 100644
index 0000000..5186b59
--- /dev/null
+++ b/testing/gtest/empty.cc
@@ -0,0 +1,3 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
diff --git a/testing/gtest/include/gtest/DEPS b/testing/gtest/include/gtest/DEPS
new file mode 100644
index 0000000..eb5e079
--- /dev/null
+++ b/testing/gtest/include/gtest/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  '+third_party/googletest/src/googletest/include/gtest',
+]
+
diff --git a/testing/gtest/include/gtest/gtest.h b/testing/gtest/include/gtest/gtest.h
new file mode 100644
index 0000000..9425b25
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest.h
@@ -0,0 +1,10 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
diff --git a/testing/gtest/include/gtest/gtest_prod.h b/testing/gtest/include/gtest/gtest_prod.h
new file mode 100644
index 0000000..2d67b42
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest_prod.h
@@ -0,0 +1,10 @@
+// Copyright 2018 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest_prod.h"
diff --git a/testing/image_diff/BUILD.gn b/testing/image_diff/BUILD.gn
new file mode 100644
index 0000000..141769b
--- /dev/null
+++ b/testing/image_diff/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+
+source_set("image_diff") {
+  testonly = true
+  sources = [
+    "image_diff_png.cpp",
+    "image_diff_png.h",
+  ]
+  configs += [ "../../:pdfium_core_config" ]
+  deps = [
+    "../../third_party:pdfium_base",
+    "../../third_party:png",
+    "../../third_party:zlib",
+  ]
+}
diff --git a/testing/image_diff/image_diff.cpp b/testing/image_diff/image_diff.cpp
index 806e0c8..c8f9caf 100644
--- a/testing/image_diff/image_diff.cpp
+++ b/testing/image_diff/image_diff.cpp
@@ -14,13 +14,13 @@
 #include <string.h>
 
 #include <algorithm>
-#include <iostream>
 #include <map>
 #include <string>
 #include <vector>
 
 #include "core/fxcrt/fx_memory.h"
 #include "testing/image_diff/image_diff_png.h"
+#include "testing/utils/path_service.h"
 #include "third_party/base/logging.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
@@ -29,13 +29,13 @@
 #endif
 
 // Return codes used by this utility.
-static const int kStatusSame = 0;
-static const int kStatusDifferent = 1;
-static const int kStatusError = 2;
+constexpr int kStatusSame = 0;
+constexpr int kStatusDifferent = 1;
+constexpr int kStatusError = 2;
 
 // Color codes.
-static const uint32_t RGBA_RED = 0x000000ff;
-static const uint32_t RGBA_ALPHA = 0xff000000;
+constexpr uint32_t RGBA_RED = 0x000000ff;
+constexpr uint32_t RGBA_ALPHA = 0xff000000;
 
 class Image {
  public:
@@ -45,31 +45,17 @@
   bool has_image() const { return w_ > 0 && h_ > 0; }
   int w() const { return w_; }
   int h() const { return h_; }
-  const unsigned char* data() const { return &data_.front(); }
+  pdfium::span<const uint8_t> span() const { return data_; }
 
   // Creates the image from the given filename on disk, and returns true on
   // success.
   bool CreateFromFilename(const std::string& path) {
-    FILE* f = fopen(path.c_str(), "rb");
-    if (!f)
-      return false;
+    return CreateFromFilenameImpl(path, /*reverse_byte_order=*/false);
+  }
 
-    std::vector<unsigned char> compressed;
-    const size_t kBufSize = 1024;
-    unsigned char buf[kBufSize];
-    size_t num_read = 0;
-    while ((num_read = fread(buf, 1, kBufSize, f)) > 0) {
-      compressed.insert(compressed.end(), buf, buf + num_read);
-    }
-
-    fclose(f);
-
-    if (!image_diff_png::DecodePNG(compressed.data(), compressed.size(), &data_,
-                                   &w_, &h_)) {
-      Clear();
-      return false;
-    }
-    return true;
+  // Same as CreateFromFilename(), but with BGRA instead of RGBA ordering.
+  bool CreateFromFilenameWithReverseByteOrder(const std::string& path) {
+    return CreateFromFilenameImpl(path, /*reverse_byte_order=*/true);
   }
 
   void Clear() {
@@ -93,6 +79,30 @@
   }
 
  private:
+  bool CreateFromFilenameImpl(const std::string& path,
+                              bool reverse_byte_order) {
+    FILE* f = fopen(path.c_str(), "rb");
+    if (!f)
+      return false;
+
+    std::vector<uint8_t> compressed;
+    const size_t kBufSize = 1024;
+    uint8_t buf[kBufSize];
+    size_t num_read = 0;
+    while ((num_read = fread(buf, 1, kBufSize, f)) > 0) {
+      compressed.insert(compressed.end(), buf, buf + num_read);
+    }
+
+    fclose(f);
+
+    data_ = image_diff_png::DecodePNG(compressed, reverse_byte_order, &w_, &h_);
+    if (data_.empty()) {
+      Clear();
+      return false;
+    }
+    return true;
+  }
+
   bool pixel_in_bounds(int x, int y) const {
     return x >= 0 && x < w_ && y >= 0 && y < h_;
   }
@@ -103,7 +113,7 @@
   int w_;
   int h_;
 
-  std::vector<unsigned char> data_;
+  std::vector<uint8_t> data_;
 };
 
 float CalculateDifferencePercentage(const Image& actual, int pixels_different) {
@@ -158,7 +168,7 @@
   int w = std::min(baseline.w(), actual.w());
   int h = std::min(baseline.h(), actual.h());
 
-  // Count occurences of each RGBA pixel value of baseline in the overlap.
+  // Count occurrences of each RGBA pixel value of baseline in the overlap.
   std::map<uint32_t, int32_t> baseline_histogram;
   for (int y = 0; y < h; ++y) {
     for (int x = 0; x < w; ++x) {
@@ -184,31 +194,42 @@
   return CalculateDifferencePercentage(actual, pixels_different);
 }
 
-void PrintHelp() {
+void PrintHelp(const std::string& binary_name) {
   fprintf(
       stderr,
       "Usage:\n"
-      "  image_diff [--histogram] <compare file> <reference file>\n"
+      "  %s OPTIONS <compare file> <reference file>\n"
       "    Compares two files on disk, returning 0 when they are the same;\n"
-      "    passing \"--histogram\" additionally calculates a diff of the\n"
-      "    RGBA value histograms (which is resistant to shifts in layout)\n"
-      "  image_diff --diff <compare file> <reference file> <output file>\n"
+      "    Passing \"--histogram\" additionally calculates a diff of the\n"
+      "    RGBA value histograms. (which is resistant to shifts in layout)\n"
+      "    Passing \"--reverse-byte-order\" additionally assumes the compare\n"
+      "    file has BGRA byte ordering.\n"
+      "  %s --diff <compare file> <reference file> <output file>\n"
       "    Compares two files on disk, outputs an image that visualizes the\n"
-      "    difference to <output file>\n");
+      "    difference to <output file>\n",
+      binary_name.c_str(), binary_name.c_str());
 }
 
-int CompareImages(const std::string& file1,
+int CompareImages(const std::string& binary_name,
+                  const std::string& file1,
                   const std::string& file2,
-                  bool compare_histograms) {
+                  bool compare_histograms,
+                  bool reverse_byte_order) {
   Image actual_image;
   Image baseline_image;
 
-  if (!actual_image.CreateFromFilename(file1)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file1.c_str());
+  bool actual_load_result =
+      reverse_byte_order
+          ? actual_image.CreateFromFilenameWithReverseByteOrder(file1)
+          : actual_image.CreateFromFilename(file1);
+  if (!actual_load_result) {
+    fprintf(stderr, "%s: Unable to open file \"%s\"\n", binary_name.c_str(),
+            file1.c_str());
     return kStatusError;
   }
   if (!baseline_image.CreateFromFilename(file2)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file2.c_str());
+    fprintf(stderr, "%s: Unable to open file \"%s\"\n", binary_name.c_str(),
+            file2.c_str());
     return kStatusError;
   }
 
@@ -259,18 +280,21 @@
   return same;
 }
 
-int DiffImages(const std::string& file1,
+int DiffImages(const std::string& binary_name,
+               const std::string& file1,
                const std::string& file2,
                const std::string& out_file) {
   Image actual_image;
   Image baseline_image;
 
   if (!actual_image.CreateFromFilename(file1)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file1.c_str());
+    fprintf(stderr, "%s: Unable to open file \"%s\"\n", binary_name.c_str(),
+            file1.c_str());
     return kStatusError;
   }
   if (!baseline_image.CreateFromFilename(file2)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file2.c_str());
+    fprintf(stderr, "%s: Unable to open file \"%s\"\n", binary_name.c_str(),
+            file2.c_str());
     return kStatusError;
   }
 
@@ -279,10 +303,10 @@
   if (same)
     return kStatusSame;
 
-  std::vector<unsigned char> png_encoding;
-  image_diff_png::EncodeRGBAPNG(diff_image.data(), diff_image.w(),
-                                diff_image.h(), diff_image.w() * 4,
-                                &png_encoding);
+  std::vector<uint8_t> png_encoding = image_diff_png::EncodeRGBAPNG(
+      diff_image.span(), diff_image.w(), diff_image.h(), diff_image.w() * 4);
+  if (png_encoding.empty())
+    return kStatusError;
 
   FILE* f = fopen(out_file.c_str(), "wb");
   if (!f)
@@ -301,10 +325,15 @@
 
   bool histograms = false;
   bool produce_diff_image = false;
+  bool reverse_byte_order = false;
   std::string filename1;
   std::string filename2;
   std::string diff_filename;
 
+  // Strip the path from the first arg
+  const char* last_separator = strrchr(argv[0], PATH_SEPARATOR);
+  std::string binary_name = last_separator ? last_separator + 1 : argv[0];
+
   int i;
   for (i = 1; i < argc; ++i) {
     const char* arg = argv[i];
@@ -314,6 +343,8 @@
       histograms = true;
     } else if (strcmp(arg, "--diff") == 0) {
       produce_diff_image = true;
+    } else if (strcmp(arg, "--reverse-byte-order") == 0) {
+      reverse_byte_order = true;
     }
   }
   if (i < argc)
@@ -325,12 +356,13 @@
 
   if (produce_diff_image) {
     if (!diff_filename.empty()) {
-      return DiffImages(filename1, filename2, diff_filename);
+      return DiffImages(binary_name, filename1, filename2, diff_filename);
     }
   } else if (!filename2.empty()) {
-    return CompareImages(filename1, filename2, histograms);
+    return CompareImages(binary_name, filename1, filename2, histograms,
+                         reverse_byte_order);
   }
 
-  PrintHelp();
+  PrintHelp(binary_name);
   return kStatusError;
 }
diff --git a/testing/image_diff/image_diff_png.cpp b/testing/image_diff/image_diff_png.cpp
index 48b5ae1..dcd9d69 100644
--- a/testing/image_diff/image_diff_png.cpp
+++ b/testing/image_diff/image_diff_png.cpp
@@ -16,8 +16,14 @@
 
 #include <string>
 
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/logging.h"
+
+#ifdef USE_SYSTEM_ZLIB
+#include <zlib.h>
+#else
 #include "third_party/zlib/zlib.h"
+#endif
 
 #ifdef USE_SYSTEM_LIBPNG
 #include <png.h>
@@ -55,13 +61,13 @@
 };
 
 // Converts BGRA->RGBA and RGBA->BGRA.
-void ConvertBetweenBGRAandRGBA(const unsigned char* input,
+void ConvertBetweenBGRAandRGBA(const uint8_t* input,
                                int pixel_width,
-                               unsigned char* output,
+                               uint8_t* output,
                                bool* is_opaque) {
   for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &input[x * 4];
-    unsigned char* pixel_out = &output[x * 4];
+    const uint8_t* pixel_in = &input[x * 4];
+    uint8_t* pixel_out = &output[x * 4];
     pixel_out[0] = pixel_in[2];
     pixel_out[1] = pixel_in[1];
     pixel_out[2] = pixel_in[0];
@@ -69,91 +75,82 @@
   }
 }
 
-void ConvertBGRtoRGB(const unsigned char* bgr,
+void ConvertBGRtoRGB(const uint8_t* bgr,
                      int pixel_width,
-                     unsigned char* rgb,
+                     uint8_t* rgb,
                      bool* is_opaque) {
   for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &bgr[x * 3];
-    unsigned char* pixel_out = &rgb[x * 3];
+    const uint8_t* pixel_in = &bgr[x * 3];
+    uint8_t* pixel_out = &rgb[x * 3];
     pixel_out[0] = pixel_in[2];
     pixel_out[1] = pixel_in[1];
     pixel_out[2] = pixel_in[0];
   }
 }
 
-void ConvertRGBAtoRGB(const unsigned char* rgba,
+void ConvertRGBAtoRGB(const uint8_t* rgba,
                       int pixel_width,
-                      unsigned char* rgb,
+                      uint8_t* rgb,
                       bool* is_opaque) {
   for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &rgba[x * 4];
-    unsigned char* pixel_out = &rgb[x * 3];
+    const uint8_t* pixel_in = &rgba[x * 4];
+    uint8_t* pixel_out = &rgb[x * 3];
     pixel_out[0] = pixel_in[0];
     pixel_out[1] = pixel_in[1];
     pixel_out[2] = pixel_in[2];
   }
 }
 
-}  // namespace
-
 // Decoder
 //
 // This code is based on WebKit libpng interface (PNGImageDecoder), which is
 // in turn based on the Mozilla png decoder.
 
-namespace {
-
 // Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
-const double kMaxGamma = 21474.83;  // Maximum gamma accepted by png library.
-const double kDefaultGamma = 2.2;
-const double kInverseGamma = 1.0 / kDefaultGamma;
+constexpr double kDefaultGamma = 2.2;
+
+// Maximum gamma accepted by PNG library.
+constexpr double kMaxGamma = 21474.83;
+
+constexpr double kInverseGamma = 1.0 / kDefaultGamma;
 
 class PngDecoderState {
  public:
-  // Output is a vector<unsigned char>.
-  PngDecoderState(ColorFormat ofmt, std::vector<unsigned char>* o)
-      : output_format(ofmt),
-        output_channels(0),
-        is_opaque(true),
-        output(o),
-        row_converter(nullptr),
-        width(0),
-        height(0),
-        done(false) {}
+  PngDecoderState(ColorFormat ofmt, std::vector<uint8_t>* out)
+      : output_format(ofmt), output(out) {}
 
-  ColorFormat output_format;
-  int output_channels;
+  const ColorFormat output_format;
+  int output_channels = 0;
 
   // Used during the reading of an SkBitmap. Defaults to true until we see a
   // pixel with anything other than an alpha of 255.
-  bool is_opaque;
+  bool is_opaque = true;
 
   // An intermediary buffer for decode output.
-  std::vector<unsigned char>* output;
+  std::vector<uint8_t>* const output;
 
   // Called to convert a row from the library to the correct output format.
-  // When NULL, no conversion is necessary.
-  void (*row_converter)(const unsigned char* in,
+  // When null, no conversion is necessary.
+  void (*row_converter)(const uint8_t* in,
                         int w,
-                        unsigned char* out,
-                        bool* is_opaque);
+                        uint8_t* out,
+                        bool* is_opaque) = nullptr;
 
   // Size of the image, set in the info callback.
-  int width;
-  int height;
+  int width = 0;
+  int height = 0;
 
   // Set to true when we've found the end of the data.
-  bool done;
+  bool done = false;
 };
 
-void ConvertRGBtoRGBA(const unsigned char* rgb,
+void ConvertRGBtoRGBA(const uint8_t* rgb,
                       int pixel_width,
-                      unsigned char* rgba,
+                      uint8_t* rgba,
                       bool* is_opaque) {
   for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &rgb[x * 3];
-    unsigned char* pixel_out = &rgba[x * 4];
+    const uint8_t* pixel_in = &rgb[x * 3];
+    uint8_t* pixel_out = &rgba[x * 4];
     pixel_out[0] = pixel_in[0];
     pixel_out[1] = pixel_in[1];
     pixel_out[2] = pixel_in[2];
@@ -161,13 +158,13 @@
   }
 }
 
-void ConvertRGBtoBGRA(const unsigned char* rgb,
+void ConvertRGBtoBGRA(const uint8_t* rgb,
                       int pixel_width,
-                      unsigned char* bgra,
+                      uint8_t* bgra,
                       bool* is_opaque) {
   for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &rgb[x * 3];
-    unsigned char* pixel_out = &bgra[x * 4];
+    const uint8_t* pixel_in = &rgb[x * 3];
+    uint8_t* pixel_out = &bgra[x * 4];
     pixel_out[0] = pixel_in[2];
     pixel_out[1] = pixel_in[1];
     pixel_out[2] = pixel_in[0];
@@ -298,10 +295,10 @@
     return;
   }
 
-  unsigned char* base = NULL;
+  uint8_t* base = nullptr;
   base = &state->output->front();
 
-  unsigned char* dest = &base[state->width * state->output_channels * row_num];
+  uint8_t* dest = &base[state->width * state->output_channels * row_num];
   if (state->row_converter)
     state->row_converter(new_row, state->width, dest, &state->is_opaque);
   else
@@ -322,76 +319,72 @@
 class PngReadStructDestroyer {
  public:
   PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {}
-  ~PngReadStructDestroyer() { png_destroy_read_struct(ps_, pi_, NULL); }
+  ~PngReadStructDestroyer() { png_destroy_read_struct(ps_, pi_, nullptr); }
 
  private:
   png_struct** ps_;
   png_info** pi_;
 };
 
-bool BuildPNGStruct(const unsigned char* input,
-                    size_t input_size,
+bool BuildPNGStruct(pdfium::span<const uint8_t> input,
                     png_struct** png_ptr,
                     png_info** info_ptr) {
-  if (input_size < 8)
+  if (input.size() < 8)
     return false;  // Input data too small to be a png
 
   // Have libpng check the signature, it likes the first 8 bytes.
-  if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
+  if (png_sig_cmp(const_cast<uint8_t*>(input.data()), 0, 8) != 0)
     return false;
 
-  *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  *png_ptr =
+      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
   if (!*png_ptr)
     return false;
 
   *info_ptr = png_create_info_struct(*png_ptr);
   if (!*info_ptr) {
-    png_destroy_read_struct(png_ptr, NULL, NULL);
+    png_destroy_read_struct(png_ptr, nullptr, nullptr);
     return false;
   }
 
   return true;
 }
 
-}  // namespace
-
-// static
-bool Decode(const unsigned char* input,
-            size_t input_size,
-            ColorFormat format,
-            std::vector<unsigned char>* output,
-            int* w,
-            int* h) {
-  png_struct* png_ptr = NULL;
-  png_info* info_ptr = NULL;
-  if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
-    return false;
+std::vector<uint8_t> Decode(pdfium::span<const uint8_t> input,
+                            ColorFormat format,
+                            int* w,
+                            int* h) {
+  std::vector<uint8_t> output;
+  png_struct* png_ptr = nullptr;
+  png_info* info_ptr = nullptr;
+  if (!BuildPNGStruct(input, &png_ptr, &info_ptr))
+    return output;
 
   PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
   if (setjmp(png_jmpbuf(png_ptr))) {
     // The destroyer will ensure that the structures are cleaned up in this
     // case, even though we may get here as a jump from random parts of the
     // PNG library called below.
-    return false;
+    return output;
   }
 
-  PngDecoderState state(format, output);
+  PngDecoderState state(format, &output);
 
   png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
                               &DecodeRowCallback, &DecodeEndCallback);
-  png_process_data(png_ptr, info_ptr, const_cast<unsigned char*>(input),
-                   input_size);
+  png_process_data(png_ptr, info_ptr, const_cast<uint8_t*>(input.data()),
+                   input.size());
 
   if (!state.done) {
     // Fed it all the data but the library didn't think we got all the data, so
     // this file must be truncated.
-    output->clear();
-    return false;
+    output.clear();
+    return output;
   }
 
   *w = state.width;
   *h = state.height;
-  return true;
+  return output;
 }
 
 // Encoder
@@ -399,13 +392,11 @@
 // This section of the code is based on nsPNGEncoder.cpp in Mozilla
 // (Copyright 2005 Google Inc.)
 
-namespace {
-
 // Passed around as the io_ptr in the png structs so our callbacks know where
 // to write data.
 struct PngEncoderState {
-  explicit PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
-  std::vector<unsigned char>* out;
+  explicit PngEncoderState(std::vector<uint8_t>* o) : out(o) {}
+  std::vector<uint8_t>* out;
 };
 
 // Called by libpng to flush its internal buffer to ours.
@@ -421,13 +412,13 @@
   // we're required to provide this function by libpng.
 }
 
-void ConvertBGRAtoRGB(const unsigned char* bgra,
+void ConvertBGRAtoRGB(const uint8_t* bgra,
                       int pixel_width,
-                      unsigned char* rgb,
+                      uint8_t* rgb,
                       bool* is_opaque) {
   for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &bgra[x * 4];
-    unsigned char* pixel_out = &rgb[x * 3];
+    const uint8_t* pixel_in = &bgra[x * 4];
+    uint8_t* pixel_out = &rgb[x * 3];
     pixel_out[0] = pixel_in[2];
     pixel_out[1] = pixel_in[1];
     pixel_out[2] = pixel_in[0];
@@ -488,9 +479,9 @@
 #endif  // PNG_TEXT_SUPPORTED
 
 // The type of functions usable for converting between pixel formats.
-typedef void (*FormatConverter)(const unsigned char* in,
+typedef void (*FormatConverter)(const uint8_t* in,
                                 int w,
-                                unsigned char* out,
+                                uint8_t* out,
                                 bool* is_opaque);
 
 // libpng uses a wacky setjmp-based API, which makes the compiler nervous.
@@ -503,7 +494,7 @@
                    int width,
                    int height,
                    int row_byte_width,
-                   const unsigned char* input,
+                   pdfium::span<const uint8_t> input,
                    int compression_level,
                    int png_output_color_type,
                    int output_color_components,
@@ -512,7 +503,7 @@
 #ifdef PNG_TEXT_SUPPORTED
   CommentWriter comment_writer(comments);
 #endif
-  unsigned char* row_buffer = NULL;
+  uint8_t* row_buffer = nullptr;
 
   // Make sure to not declare any locals here -- locals in the presence
   // of setjmp() in C++ code makes gcc complain.
@@ -543,14 +534,13 @@
   if (!converter) {
     // No conversion needed, give the data directly to libpng.
     for (int y = 0; y < height; y++) {
-      png_write_row(png_ptr,
-                    const_cast<unsigned char*>(&input[y * row_byte_width]));
+      png_write_row(png_ptr, const_cast<uint8_t*>(&input[y * row_byte_width]));
     }
   } else {
     // Needs conversion using a separate buffer.
-    row_buffer = new unsigned char[width * output_color_components];
+    row_buffer = new uint8_t[width * output_color_components];
     for (int y = 0; y < height; y++) {
-      converter(&input[y * row_byte_width], width, row_buffer, NULL);
+      converter(&input[y * row_byte_width], width, row_buffer, nullptr);
       png_write_row(png_ptr, row_buffer);
     }
     delete[] row_buffer;
@@ -560,33 +550,33 @@
   return true;
 }
 
-}  // namespace
+std::vector<uint8_t> EncodeWithCompressionLevel(
+    pdfium::span<const uint8_t> input,
+    ColorFormat format,
+    const int width,
+    const int height,
+    int row_byte_width,
+    bool discard_transparency,
+    const std::vector<Comment>& comments,
+    int compression_level) {
+  std::vector<uint8_t> output;
 
-// static
-bool EncodeWithCompressionLevel(const unsigned char* input,
-                                ColorFormat format,
-                                const int width,
-                                const int height,
-                                int row_byte_width,
-                                bool discard_transparency,
-                                const std::vector<Comment>& comments,
-                                int compression_level,
-                                std::vector<unsigned char>* output) {
-  // Run to convert an input row into the output row format, NULL means no
+  // Run to convert an input row into the output row format, nullptr means no
   // conversion is necessary.
   FormatConverter converter = nullptr;
 
-  int input_color_components, output_color_components;
+  int input_color_components;
+  int output_color_components;
   int png_output_color_type;
   switch (format) {
     case FORMAT_BGR:
       converter = ConvertBGRtoRGB;
+      FALLTHROUGH;
 
     case FORMAT_RGB:
       input_color_components = 3;
       output_color_components = 3;
       png_output_color_type = PNG_COLOR_TYPE_RGB;
-      discard_transparency = false;
       break;
 
     case FORMAT_RGBA:
@@ -619,100 +609,92 @@
       input_color_components = 1;
       output_color_components = 1;
       png_output_color_type = PNG_COLOR_TYPE_GRAY;
-      discard_transparency = false;
       break;
 
     default:
       NOTREACHED();
-      return false;
+      return output;
   }
 
   // Row stride should be at least as long as the length of the data.
   if (row_byte_width < input_color_components * width)
-    return false;
+    return output;
 
   png_struct* png_ptr =
-      png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+      png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
   if (!png_ptr)
-    return false;
+    return output;
   png_info* info_ptr = png_create_info_struct(png_ptr);
   if (!info_ptr) {
-    png_destroy_write_struct(&png_ptr, NULL);
-    return false;
+    png_destroy_write_struct(&png_ptr, nullptr);
+    return output;
   }
 
-  PngEncoderState state(output);
+  PngEncoderState state(&output);
   bool success =
       DoLibpngWrite(png_ptr, info_ptr, &state, width, height, row_byte_width,
                     input, compression_level, png_output_color_type,
                     output_color_components, converter, comments);
   png_destroy_write_struct(&png_ptr, &info_ptr);
 
-  return success;
+  if (!success)
+    output.clear();
+  return output;
 }
 
-// static
-bool Encode(const unsigned char* input,
-            ColorFormat format,
-            const int width,
-            const int height,
-            int row_byte_width,
-            bool discard_transparency,
-            const std::vector<Comment>& comments,
-            std::vector<unsigned char>* output) {
+std::vector<uint8_t> Encode(pdfium::span<const uint8_t> input,
+                            ColorFormat format,
+                            const int width,
+                            const int height,
+                            int row_byte_width,
+                            bool discard_transparency,
+                            const std::vector<Comment>& comments) {
   return EncodeWithCompressionLevel(input, format, width, height,
                                     row_byte_width, discard_transparency,
-                                    comments, Z_DEFAULT_COMPRESSION, output);
+                                    comments, Z_DEFAULT_COMPRESSION);
 }
 
-// Decode a PNG into an RGBA pixel array.
-bool DecodePNG(const unsigned char* input,
-               size_t input_size,
-               std::vector<unsigned char>* output,
-               int* width,
-               int* height) {
-  return Decode(input, input_size, FORMAT_RGBA, output, width, height);
+}  // namespace
+
+std::vector<uint8_t> DecodePNG(pdfium::span<const uint8_t> input,
+                               bool reverse_byte_order,
+                               int* width,
+                               int* height) {
+  ColorFormat format = reverse_byte_order ? FORMAT_BGRA : FORMAT_RGBA;
+  return Decode(input, format, width, height);
 }
 
-// Encode a BGR pixel array into a PNG.
-bool EncodeBGRPNG(const unsigned char* input,
-                  int width,
-                  int height,
-                  int row_byte_width,
-                  std::vector<unsigned char>* output) {
+std::vector<uint8_t> EncodeBGRPNG(pdfium::span<const uint8_t> input,
+                                  int width,
+                                  int height,
+                                  int row_byte_width) {
   return Encode(input, FORMAT_BGR, width, height, row_byte_width, false,
-                std::vector<Comment>(), output);
+                std::vector<Comment>());
 }
 
-// Encode an RGBA pixel array into a PNG.
-bool EncodeRGBAPNG(const unsigned char* input,
-                   int width,
-                   int height,
-                   int row_byte_width,
-                   std::vector<unsigned char>* output) {
+std::vector<uint8_t> EncodeRGBAPNG(pdfium::span<const uint8_t> input,
+                                   int width,
+                                   int height,
+                                   int row_byte_width) {
   return Encode(input, FORMAT_RGBA, width, height, row_byte_width, false,
-                std::vector<Comment>(), output);
+                std::vector<Comment>());
 }
 
-// Encode an BGRA pixel array into a PNG.
-bool EncodeBGRAPNG(const unsigned char* input,
-                   int width,
-                   int height,
-                   int row_byte_width,
-                   bool discard_transparency,
-                   std::vector<unsigned char>* output) {
+std::vector<uint8_t> EncodeBGRAPNG(pdfium::span<const uint8_t> input,
+                                   int width,
+                                   int height,
+                                   int row_byte_width,
+                                   bool discard_transparency) {
   return Encode(input, FORMAT_BGRA, width, height, row_byte_width,
-                discard_transparency, std::vector<Comment>(), output);
+                discard_transparency, std::vector<Comment>());
 }
 
-// Encode a grayscale pixel array into a PNG.
-bool EncodeGrayPNG(const unsigned char* input,
-                   int width,
-                   int height,
-                   int row_byte_width,
-                   std::vector<unsigned char>* output) {
+std::vector<uint8_t> EncodeGrayPNG(pdfium::span<const uint8_t> input,
+                                   int width,
+                                   int height,
+                                   int row_byte_width) {
   return Encode(input, FORMAT_GRAY, width, height, row_byte_width, false,
-                std::vector<Comment>(), output);
+                std::vector<Comment>());
 }
 
 }  // namespace image_diff_png
diff --git a/testing/image_diff/image_diff_png.h b/testing/image_diff/image_diff_png.h
index b334b20..2c39bf0 100644
--- a/testing/image_diff/image_diff_png.h
+++ b/testing/image_diff/image_diff_png.h
@@ -9,43 +9,41 @@
 
 #include <vector>
 
+#include "third_party/base/span.h"
+
 namespace image_diff_png {
 
-// Decode a PNG into an RGBA pixel array.
-bool DecodePNG(const unsigned char* input,
-               size_t input_size,
-               std::vector<unsigned char>* output,
-               int* width,
-               int* height);
+// Decode a PNG into an RGBA pixel array, or BGRA pixel array if
+// |reverse_byte_order| is set to true.
+std::vector<uint8_t> DecodePNG(pdfium::span<const uint8_t> input,
+                               bool reverse_byte_order,
+                               int* width,
+                               int* height);
 
 // Encode a BGR pixel array into a PNG.
-bool EncodeBGRPNG(const unsigned char* input,
-                  int width,
-                  int height,
-                  int row_byte_width,
-                  std::vector<unsigned char>* output);
+std::vector<uint8_t> EncodeBGRPNG(pdfium::span<const uint8_t> input,
+                                  int width,
+                                  int height,
+                                  int row_byte_width);
 
 // Encode an RGBA pixel array into a PNG.
-bool EncodeRGBAPNG(const unsigned char* input,
-                   int width,
-                   int height,
-                   int row_byte_width,
-                   std::vector<unsigned char>* output);
+std::vector<uint8_t> EncodeRGBAPNG(pdfium::span<const uint8_t> input,
+                                   int width,
+                                   int height,
+                                   int row_byte_width);
 
 // Encode an BGRA pixel array into a PNG.
-bool EncodeBGRAPNG(const unsigned char* input,
-                   int width,
-                   int height,
-                   int row_byte_width,
-                   bool discard_transparency,
-                   std::vector<unsigned char>* output);
+std::vector<uint8_t> EncodeBGRAPNG(pdfium::span<const uint8_t> input,
+                                   int width,
+                                   int height,
+                                   int row_byte_width,
+                                   bool discard_transparency);
 
 // Encode a grayscale pixel array into a PNG.
-bool EncodeGrayPNG(const unsigned char* input,
-                   int width,
-                   int height,
-                   int row_byte_width,
-                   std::vector<unsigned char>* output);
+std::vector<uint8_t> EncodeGrayPNG(pdfium::span<const uint8_t> input,
+                                   int width,
+                                   int height,
+                                   int row_byte_width);
 
 }  // namespace image_diff_png
 
diff --git a/testing/invalid_seekable_read_stream.cpp b/testing/invalid_seekable_read_stream.cpp
new file mode 100644
index 0000000..a4116b0
--- /dev/null
+++ b/testing/invalid_seekable_read_stream.cpp
@@ -0,0 +1,20 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/invalid_seekable_read_stream.h"
+
+InvalidSeekableReadStream::InvalidSeekableReadStream(FX_FILESIZE data_size)
+    : data_size_(data_size) {}
+
+InvalidSeekableReadStream::~InvalidSeekableReadStream() = default;
+
+bool InvalidSeekableReadStream::ReadBlockAtOffset(void* buffer,
+                                                  FX_FILESIZE offset,
+                                                  size_t size) {
+  return false;
+}
+
+FX_FILESIZE InvalidSeekableReadStream::GetSize() {
+  return data_size_;
+}
diff --git a/testing/invalid_seekable_read_stream.h b/testing/invalid_seekable_read_stream.h
new file mode 100644
index 0000000..9322bc6
--- /dev/null
+++ b/testing/invalid_seekable_read_stream.h
@@ -0,0 +1,29 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_INVALID_SEEKABLE_READ_STREAM_H_
+#define TESTING_INVALID_SEEKABLE_READ_STREAM_H_
+
+#include "core/fxcrt/fx_stream.h"
+
+// A stream used for testing where reads always fail.
+class InvalidSeekableReadStream final : public IFX_SeekableReadStream {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  // IFX_SeekableReadStream overrides:
+  bool ReadBlockAtOffset(void* buffer,
+                         FX_FILESIZE offset,
+                         size_t size) override;
+  FX_FILESIZE GetSize() override;
+
+ private:
+  explicit InvalidSeekableReadStream(FX_FILESIZE data_size);
+  ~InvalidSeekableReadStream() override;
+
+  const FX_FILESIZE data_size_;
+};
+
+#endif  // TESTING_INVALID_SEEKABLE_READ_STREAM_H_
diff --git a/testing/js_embedder_test.cpp b/testing/js_embedder_test.cpp
index e9eb70d..57e6c4d 100644
--- a/testing/js_embedder_test.cpp
+++ b/testing/js_embedder_test.cpp
@@ -4,26 +4,27 @@
 
 #include "testing/js_embedder_test.h"
 
+#include "fxjs/cfxjs_engine.h"
 #include "third_party/base/ptr_util.h"
 
 JSEmbedderTest::JSEmbedderTest()
-    : m_pArrayBufferAllocator(pdfium::MakeUnique<FXJS_ArrayBufferAllocator>()) {
-}
+    : m_pArrayBufferAllocator(
+          pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>()) {}
 
 JSEmbedderTest::~JSEmbedderTest() {}
 
 void JSEmbedderTest::SetUp() {
   v8::Isolate::CreateParams params;
   params.array_buffer_allocator = m_pArrayBufferAllocator.get();
-  m_pIsolate = v8::Isolate::New(params);
+  m_pIsolate.reset(v8::Isolate::New(params));
 
-  EmbedderTest::SetExternalIsolate(m_pIsolate);
+  EmbedderTest::SetExternalIsolate(isolate());
   EmbedderTest::SetUp();
 
-  v8::Isolate::Scope isolate_scope(m_pIsolate);
-  v8::HandleScope handle_scope(m_pIsolate);
-  FXJS_PerIsolateData::SetUp(m_pIsolate);
-  m_Engine = pdfium::MakeUnique<CFXJS_Engine>(m_pIsolate);
+  v8::Isolate::Scope isolate_scope(isolate());
+  v8::HandleScope handle_scope(isolate());
+  FXJS_PerIsolateData::SetUp(isolate());
+  m_Engine = pdfium::MakeUnique<CFXJS_Engine>(isolate());
   m_Engine->InitializeEngine();
 }
 
@@ -31,14 +32,9 @@
   m_Engine->ReleaseEngine();
   m_Engine.reset();
   EmbedderTest::TearDown();
-  m_pIsolate->Dispose();
-  m_pIsolate = nullptr;
-}
-
-v8::Isolate* JSEmbedderTest::isolate() {
-  return m_pIsolate;
+  m_pIsolate.reset();
 }
 
 v8::Local<v8::Context> JSEmbedderTest::GetV8Context() {
-  return m_Engine->GetPersistentContext();
+  return m_Engine->GetV8Context();
 }
diff --git a/testing/js_embedder_test.h b/testing/js_embedder_test.h
index 1f27dbe..dca0bd7 100644
--- a/testing/js_embedder_test.h
+++ b/testing/js_embedder_test.h
@@ -7,8 +7,12 @@
 
 #include <memory>
 
-#include "fxjs/fxjs_v8.h"
+#include "fxjs/cfx_v8.h"
 #include "testing/embedder_test.h"
+#include "v8/include/v8.h"
+
+class CFXJS_Engine;
+class CFX_V8ArrayBufferAllocator;
 
 class JSEmbedderTest : public EmbedderTest {
  public:
@@ -18,13 +22,13 @@
   void SetUp() override;
   void TearDown() override;
 
-  v8::Isolate* isolate();
+  v8::Isolate* isolate() const { return m_pIsolate.get(); }
+  CFXJS_Engine* engine() const { return m_Engine.get(); }
   v8::Local<v8::Context> GetV8Context();
-  CFXJS_Engine* engine() { return m_Engine.get(); }
 
  private:
-  std::unique_ptr<FXJS_ArrayBufferAllocator> m_pArrayBufferAllocator;
-  v8::Isolate* m_pIsolate = nullptr;
+  std::unique_ptr<CFX_V8ArrayBufferAllocator> m_pArrayBufferAllocator;
+  std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> m_pIsolate;
   std::unique_ptr<CFXJS_Engine> m_Engine;
 };
 
diff --git a/testing/libfuzzer/BUILD.gn b/testing/libfuzzer/BUILD.gn
deleted file mode 100644
index 20c64d9..0000000
--- a/testing/libfuzzer/BUILD.gn
+++ /dev/null
@@ -1,220 +0,0 @@
-# Copyright 2016 The PDFium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("../../pdfium.gni")
-
-config("libfuzzer_config") {
-  configs = [ "../..:pdfium_core_config" ]
-
-  defines = [
-    "PNG_PREFIX",
-    "PNG_USE_READ_MACROS",
-  ]
-  include_dirs = [ "../.." ]
-  if (pdf_enable_v8) {
-    defines += [ "PDF_ENABLE_V8" ]
-  }
-  if (pdf_enable_xfa) {
-    defines += [ "PDF_ENABLE_XFA" ]
-  }
-}
-
-group("libfuzzer") {
-  testonly = true
-  deps = [
-    ":pdf_cmap_fuzzer",
-    ":pdf_codec_a85_fuzzer",
-    ":pdf_codec_fax_fuzzer",
-    ":pdf_codec_icc_fuzzer",
-    ":pdf_codec_jbig2_fuzzer",
-    ":pdf_codec_rle_fuzzer",
-    ":pdf_font_fuzzer",
-    ":pdf_hint_table_fuzzer",
-    ":pdf_jpx_fuzzer",
-    ":pdf_psengine_fuzzer",
-    ":pdf_streamparser_fuzzer",
-  ]
-  if (pdf_enable_xfa) {
-    deps += [
-      ":pdf_cfx_barcode_fuzzer",
-      ":pdf_cfx_saxreader_fuzzer",
-      ":pdf_codec_bmp_fuzzer",
-      ":pdf_codec_gif_fuzzer",
-      ":pdf_codec_jpeg_fuzzer",
-      ":pdf_codec_png_fuzzer",
-      ":pdf_codec_tiff_fuzzer",
-      ":pdf_css_fuzzer",
-      ":pdf_fm2js_fuzzer",
-      ":pdf_formcalc_fuzzer",
-      ":pdf_lzw_fuzzer",
-      ":pdf_xml_fuzzer",
-    ]
-  }
-}
-
-template("pdfium_fuzzer") {
-  source_set(target_name) {
-    sources = invoker.sources + [ "pdf_fuzzer_init.cc" ]
-    deps = [
-      "../..:pdfium",
-    ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
-    }
-    testonly = true
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      "//build/config/compiler:no_chromium_code",
-      ":libfuzzer_config",
-    ]
-  }
-}
-
-if (pdf_enable_xfa) {
-  pdfium_fuzzer("pdf_cfx_barcode_fuzzer") {
-    sources = [
-      "pdf_cfx_barcode_fuzzer.cc",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_cfx_saxreader_fuzzer") {
-    sources = [
-      "pdf_cfx_saxreader_fuzzer.cc",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_codec_bmp_fuzzer") {
-    sources = [
-      "pdf_codec_bmp_fuzzer.cc",
-      "xfa_codec_fuzzer.h",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_codec_gif_fuzzer") {
-    sources = [
-      "pdf_codec_gif_fuzzer.cc",
-      "xfa_codec_fuzzer.h",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_codec_jpeg_fuzzer") {
-    sources = [
-      "pdf_codec_jpeg_fuzzer.cc",
-      "xfa_codec_fuzzer.h",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_codec_png_fuzzer") {
-    sources = [
-      "pdf_codec_png_fuzzer.cc",
-      "xfa_codec_fuzzer.h",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_codec_tiff_fuzzer") {
-    sources = [
-      "pdf_codec_tiff_fuzzer.cc",
-      "xfa_codec_fuzzer.h",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_css_fuzzer") {
-    sources = [
-      "pdf_css_fuzzer.cc",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_fm2js_fuzzer") {
-    sources = [
-      "pdf_fm2js_fuzzer.cc",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_formcalc_fuzzer") {
-    sources = [
-      "pdf_formcalc_fuzzer.cc",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_lzw_fuzzer") {
-    sources = [
-      "pdf_lzw_fuzzer.cc",
-    ]
-  }
-
-  pdfium_fuzzer("pdf_xml_fuzzer") {
-    sources = [
-      "pdf_xml_fuzzer.cc",
-    ]
-  }
-}
-
-pdfium_fuzzer("pdf_cmap_fuzzer") {
-  sources = [
-    "pdf_cmap_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_codec_a85_fuzzer") {
-  sources = [
-    "pdf_codec_a85_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_codec_fax_fuzzer") {
-  sources = [
-    "pdf_codec_fax_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_codec_icc_fuzzer") {
-  deps = [
-    "../../third_party/:lcms2",
-  ]
-  sources = [
-    "pdf_codec_icc_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_codec_jbig2_fuzzer") {
-  sources = [
-    "pdf_codec_jbig2_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_codec_rle_fuzzer") {
-  sources = [
-    "pdf_codec_rle_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_font_fuzzer") {
-  sources = [
-    "pdf_font_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_hint_table_fuzzer") {
-  sources = [
-    "pdf_hint_table_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_jpx_fuzzer") {
-  sources = [
-    "pdf_jpx_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_psengine_fuzzer") {
-  sources = [
-    "pdf_psengine_fuzzer.cc",
-  ]
-}
-
-pdfium_fuzzer("pdf_streamparser_fuzzer") {
-  sources = [
-    "pdf_streamparser_fuzzer.cc",
-  ]
-}
diff --git a/testing/libfuzzer/DEPS b/testing/libfuzzer/DEPS
deleted file mode 100644
index b1d034e..0000000
--- a/testing/libfuzzer/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  '+xfa',
-]
diff --git a/testing/libfuzzer/pdf_cfx_barcode_fuzzer.cc b/testing/libfuzzer/pdf_cfx_barcode_fuzzer.cc
deleted file mode 100644
index 3334e86..0000000
--- a/testing/libfuzzer/pdf_cfx_barcode_fuzzer.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
-#include "xfa/fwl/cfx_barcode.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < 2 * sizeof(wchar_t))
-    return 0;
-
-  BC_TYPE type = static_cast<BC_TYPE>(data[0] % (BC_DATAMATRIX + 1));
-
-  // Only used one byte, but align with wchar_t for string below.
-  data += sizeof(wchar_t);
-  size -= sizeof(wchar_t);
-
-  CFX_Barcode barcode;
-  if (!barcode.Create(type))
-    return 0;
-
-  // TODO(tsepez): Setup more options from |data|.
-  barcode.SetModuleHeight(300);
-  barcode.SetModuleWidth(420);
-  barcode.SetHeight(298);
-  barcode.SetWidth(418);
-
-  WideStringView content(reinterpret_cast<const wchar_t*>(data),
-                         size / sizeof(wchar_t));
-
-  if (!barcode.Encode(content))
-    return 0;
-
-  // TODO(tsepez): Output to device.
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc b/testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc
deleted file mode 100644
index a811a8e..0000000
--- a/testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "core/fxcrt/cfx_memorystream.h"
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/xml/cfx_saxreader.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  CFX_SAXReader reader;
-  if (reader.StartParse(pdfium::MakeRetain<CFX_MemoryStream>(
-                            const_cast<uint8_t*>(data), size, false),
-                        0, -1, CFX_SaxParseMode_NotSkipSpace) < 0) {
-    return 0;
-  }
-
-  while (1) {
-    int32_t ret = reader.ContinueParse();
-    if (ret < 0 || ret > 99)
-      break;
-  }
-
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_cmap_fuzzer.cc b/testing/libfuzzer/pdf_cmap_fuzzer.cc
deleted file mode 100644
index b0e41d5..0000000
--- a/testing/libfuzzer/pdf_cmap_fuzzer.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-
-#include "core/fpdfapi/font/cpdf_cmap.h"
-#include "third_party/base/ptr_util.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  auto cmap = pdfium::MakeRetain<CPDF_CMap>();
-  cmap->LoadEmbedded(data, size);
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_codec_a85_fuzzer.cc b/testing/libfuzzer/pdf_codec_a85_fuzzer.cc
deleted file mode 100644
index 20bd792..0000000
--- a/testing/libfuzzer/pdf_codec_a85_fuzzer.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-#include <memory>
-
-#include "core/fxcodec/codec/ccodec_basicmodule.h"
-#include "core/fxcrt/fx_memory.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  uint8_t* dest_buf = nullptr;
-  uint32_t dest_size = 0;
-  CCodec_BasicModule encoder_module;
-  encoder_module.A85Encode(data, size, &dest_buf, &dest_size);
-  FX_Free(dest_buf);
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_codec_bmp_fuzzer.cc b/testing/libfuzzer/pdf_codec_bmp_fuzzer.cc
deleted file mode 100644
index 6c80fb5..0000000
--- a/testing/libfuzzer/pdf_codec_bmp_fuzzer.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/libfuzzer/xfa_codec_fuzzer.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_BMP);
-}
diff --git a/testing/libfuzzer/pdf_codec_fax_fuzzer.cc b/testing/libfuzzer/pdf_codec_fax_fuzzer.cc
deleted file mode 100644
index f6cc1e7..0000000
--- a/testing/libfuzzer/pdf_codec_fax_fuzzer.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-#include <memory>
-
-#include "core/fxcodec/codec/ccodec_faxmodule.h"
-#include "core/fxcodec/codec/ccodec_scanlinedecoder.h"
-
-static int GetInteger(const uint8_t* data) {
-  return data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  const int kParameterSize = 21;
-  if (size < kParameterSize)
-    return 0;
-
-  int width = GetInteger(data);
-  int height = GetInteger(data + 4);
-  int K = GetInteger(data + 8);
-  int Columns = GetInteger(data + 12);
-  int Rows = GetInteger(data + 16);
-  bool EndOfLine = !(data[20] & 0x01);
-  bool ByteAlign = !(data[20] & 0x02);
-  bool BlackIs1 = !(data[20] & 0x04);
-  data += kParameterSize;
-  size -= kParameterSize;
-
-  CCodec_FaxModule fax_module;
-  std::unique_ptr<CCodec_ScanlineDecoder> decoder(
-      fax_module.CreateDecoder(data, size, width, height, K, EndOfLine,
-                               ByteAlign, BlackIs1, Columns, Rows));
-
-  if (decoder) {
-    int line = 0;
-    while (decoder->GetScanline(line))
-      line++;
-  }
-
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_codec_gif_fuzzer.cc b/testing/libfuzzer/pdf_codec_gif_fuzzer.cc
deleted file mode 100644
index 613ed1e..0000000
--- a/testing/libfuzzer/pdf_codec_gif_fuzzer.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/libfuzzer/xfa_codec_fuzzer.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_GIF);
-}
diff --git a/testing/libfuzzer/pdf_codec_icc_fuzzer.cc b/testing/libfuzzer/pdf_codec_icc_fuzzer.cc
deleted file mode 100644
index 7021017..0000000
--- a/testing/libfuzzer/pdf_codec_icc_fuzzer.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-
-#include "core/fxcodec/codec/ccodec_iccmodule.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  CCodec_IccModule icc_module;
-  uint32_t nComponent = 0;
-  std::unique_ptr<CLcmsCmm> transform =
-      icc_module.CreateTransform_sRGB(data, size, &nComponent);
-
-  if (transform) {
-    float src[4];
-    float dst[4];
-    for (int i = 0; i < 4; i++)
-      src[i] = 0.5f;
-    icc_module.SetComponents(nComponent);
-    icc_module.Translate(transform.get(), src, dst);
-  }
-
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_codec_jbig2_fuzzer.cc b/testing/libfuzzer/pdf_codec_jbig2_fuzzer.cc
deleted file mode 100644
index 7b8e2aa..0000000
--- a/testing/libfuzzer/pdf_codec_jbig2_fuzzer.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fxcodec/JBig2_DocumentContext.h"
-#include "core/fxcodec/codec/ccodec_jbig2module.h"
-#include "core/fxcodec/jbig2/JBig2_Context.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "third_party/base/ptr_util.h"
-
-static uint32_t GetInteger(const uint8_t* data) {
-  return data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  const size_t kParameterSize = 8;
-  if (size < kParameterSize)
-    return 0;
-
-  uint32_t width = GetInteger(data);
-  uint32_t height = GetInteger(data + 4);
-  size -= kParameterSize;
-  data += kParameterSize;
-
-  static constexpr uint32_t kMemLimit = 512000000;   // 512 MB
-  static constexpr uint32_t k1bppRgbComponents = 4;  // From CFX_DIBitmap impl.
-  FX_SAFE_UINT32 mem = width;
-  mem *= height;
-  mem *= k1bppRgbComponents;
-  if (!mem.IsValid() || mem.ValueOrDie() > kMemLimit)
-    return 0;
-
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!bitmap->Create(width, height, FXDIB_1bppRgb))
-    return 0;
-
-  auto stream = pdfium::MakeUnique<CPDF_Stream>();
-  stream->AsStream()->SetData(data, size);
-
-  auto src_stream = pdfium::MakeRetain<CPDF_StreamAcc>(stream->AsStream());
-  src_stream->LoadAllDataRaw();
-
-  CCodec_Jbig2Module module;
-  CCodec_Jbig2Context jbig2_context;
-  std::unique_ptr<JBig2_DocumentContext> document_context;
-  FXCODEC_STATUS status = module.StartDecode(
-      &jbig2_context, &document_context, width, height, src_stream, nullptr,
-      bitmap->GetBuffer(), bitmap->GetPitch(), nullptr);
-
-  while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-    status = module.ContinueDecode(&jbig2_context, nullptr);
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_codec_jpeg_fuzzer.cc b/testing/libfuzzer/pdf_codec_jpeg_fuzzer.cc
deleted file mode 100644
index 862bfad..0000000
--- a/testing/libfuzzer/pdf_codec_jpeg_fuzzer.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/libfuzzer/xfa_codec_fuzzer.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_JPG);
-}
diff --git a/testing/libfuzzer/pdf_codec_png_fuzzer.cc b/testing/libfuzzer/pdf_codec_png_fuzzer.cc
deleted file mode 100644
index 94e9321..0000000
--- a/testing/libfuzzer/pdf_codec_png_fuzzer.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/libfuzzer/xfa_codec_fuzzer.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_PNG);
-}
diff --git a/testing/libfuzzer/pdf_codec_rle_fuzzer.cc b/testing/libfuzzer/pdf_codec_rle_fuzzer.cc
deleted file mode 100644
index c86671e..0000000
--- a/testing/libfuzzer/pdf_codec_rle_fuzzer.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-#include <memory>
-
-#include "core/fxcodec/codec/ccodec_basicmodule.h"
-#include "core/fxcrt/fx_memory.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  uint8_t* dest_buf = nullptr;
-  uint32_t dest_size = 0;
-  CCodec_BasicModule encoder_module;
-  encoder_module.RunLengthEncode(data, size, &dest_buf, &dest_size);
-  FX_Free(dest_buf);
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_codec_tiff_fuzzer.cc b/testing/libfuzzer/pdf_codec_tiff_fuzzer.cc
deleted file mode 100644
index 483ac28..0000000
--- a/testing/libfuzzer/pdf_codec_tiff_fuzzer.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/libfuzzer/xfa_codec_fuzzer.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  return XFACodecFuzzer::Fuzz(data, size, FXCODEC_IMAGE_TIF);
-}
diff --git a/testing/libfuzzer/pdf_css_fuzzer.cc b/testing/libfuzzer/pdf_css_fuzzer.cc
deleted file mode 100644
index 7cd924e..0000000
--- a/testing/libfuzzer/pdf_css_fuzzer.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxcrt/css/cfx_css.h"
-#include "core/fxcrt/css/cfx_csssyntaxparser.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/retain_ptr.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  WideString input =
-      WideString::FromUTF8(ByteStringView(data, static_cast<size_t>(size)));
-
-  // If we convert the input into an empty string bail out.
-  if (input.GetLength() == 0)
-    return 0;
-
-  CFX_CSSSyntaxParser parser(input.c_str(), input.GetLength());
-  CFX_CSSSyntaxStatus status;
-  do {
-    status = parser.DoSyntaxParse();
-  } while (status != CFX_CSSSyntaxStatus::Error &&
-           status != CFX_CSSSyntaxStatus::EOS);
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_fm2js_fuzzer.cc b/testing/libfuzzer/pdf_fm2js_fuzzer.cc
deleted file mode 100644
index 2541dfb..0000000
--- a/testing/libfuzzer/pdf_fm2js_fuzzer.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstddef>
-#include <cstdint>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_string.h"
-#include "fxjs/cfxjse_formcalc_context.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  FX_SAFE_SIZE_T safe_size = size;
-  if (!safe_size.IsValid())
-    return 0;
-
-  CFX_WideTextBuf js;
-  WideString input =
-      WideString::FromUTF8(ByteStringView(data, safe_size.ValueOrDie()));
-  CFXJSE_FormCalcContext::Translate(input.AsStringView(), &js);
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_font_fuzzer.cc b/testing/libfuzzer/pdf_font_fuzzer.cc
deleted file mode 100644
index aed6661..0000000
--- a/testing/libfuzzer/pdf_font_fuzzer.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstring>
-#include <memory>
-
-#include "public/cpp/fpdf_deleters.h"
-#include "public/fpdf_edit.h"
-#include "public/fpdfview.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < 2)
-    return 0;
-
-  std::unique_ptr<void, FPDFDocumentDeleter> doc(FPDF_CreateNewDocument());
-  std::unique_ptr<void, FPDFPageDeleter> page(
-      FPDFPage_New(doc.get(), 0, 612, 792));
-  int font_type = data[0];
-  FPDF_BOOL cid = data[1];
-  data += 2;
-  size -= 2;
-  std::unique_ptr<void, FPDFFontDeleter> font(
-      FPDFText_LoadFont(doc.get(), data, size, font_type, cid));
-  if (!font)
-    return 0;
-
-  FPDF_PAGEOBJECT text_object =
-      FPDFPageObj_CreateTextObj(doc.get(), font.get(), 12.0f);
-  FPDFPage_InsertObject(page.get(), text_object);
-  FPDFPage_GenerateContent(page.get());
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_fuzzer_init.cc b/testing/libfuzzer/pdf_fuzzer_init.cc
deleted file mode 100644
index 4b9790c..0000000
--- a/testing/libfuzzer/pdf_fuzzer_init.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string.h>
-
-#include "public/fpdfview.h"
-
-// Initialize the library once for all runs of the fuzzer.
-struct TestCase {
-  TestCase() {
-    memset(&config, '\0', sizeof(config));
-    config.version = 2;
-    config.m_pUserFontPaths = nullptr;
-    config.m_pIsolate = nullptr;
-    config.m_v8EmbedderSlot = 0;
-    FPDF_InitLibraryWithConfig(&config);
-  }
-  FPDF_LIBRARY_CONFIG config;
-};
-static TestCase* testCase = new TestCase();
diff --git a/testing/libfuzzer/pdf_hint_table_fuzzer.cc b/testing/libfuzzer/pdf_hint_table_fuzzer.cc
deleted file mode 100644
index ee51e25..0000000
--- a/testing/libfuzzer/pdf_hint_table_fuzzer.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-
-#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_hint_tables.h"
-#include "core/fpdfapi/parser/cpdf_linearized_header.h"
-#include "core/fpdfapi/parser/cpdf_number.h"
-#include "core/fxcrt/cfx_bitstream.h"
-#include "third_party/base/ptr_util.h"
-
-int32_t GetData(const int32_t** data32, const uint8_t** data, size_t* size) {
-  const int32_t* ret = *data32;
-  ++(*data32);
-  *data += 4;
-  *size -= 4;
-  return *ret;
-}
-
-class HintTableForFuzzing : public CPDF_HintTables {
- public:
-  HintTableForFuzzing(CPDF_LinearizedHeader* pLinearized,
-                      int shared_hint_table_offset)
-      : CPDF_HintTables(nullptr, pLinearized),
-        shared_hint_table_offset_(shared_hint_table_offset) {}
-  ~HintTableForFuzzing() {}
-
-  void Fuzz(const uint8_t* data, size_t size) {
-    if (shared_hint_table_offset_ <= 0)
-      return;
-
-    if (size < static_cast<size_t>(shared_hint_table_offset_))
-      return;
-
-    CFX_BitStream bs(data, size);
-    if (!ReadPageHintTable(&bs))
-      return;
-    ReadSharedObjHintTable(&bs, shared_hint_table_offset_);
-  }
-
- private:
-  int shared_hint_table_offset_;
-};
-
-class FakeLinearized : public CPDF_LinearizedHeader {
- public:
-  explicit FakeLinearized(CPDF_Dictionary* linearized_dict)
-      : CPDF_LinearizedHeader(linearized_dict, 0) {}
-};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  // Need 28 bytes for |linearized_dict|.
-  // The header section of page offset hint table is 36 bytes.
-  // The header section of shared object hint table is 24 bytes.
-  if (size < 28 + 36 + 24)
-    return 0;
-
-  const int32_t* data32 = reinterpret_cast<const int32_t*>(data);
-
-  auto linearized_dict = pdfium::MakeUnique<CPDF_Dictionary>();
-  // Set initial value.
-  linearized_dict->SetNewFor<CPDF_Boolean>("Linearized", true);
-  // Set first page end offset
-  linearized_dict->SetNewFor<CPDF_Number>("E", GetData(&data32, &data, &size));
-  // Set page count
-  linearized_dict->SetNewFor<CPDF_Number>("N", GetData(&data32, &data, &size));
-  // Set first page obj num
-  linearized_dict->SetNewFor<CPDF_Number>("O", GetData(&data32, &data, &size));
-  // Set first page no
-  linearized_dict->SetNewFor<CPDF_Number>("P", GetData(&data32, &data, &size));
-
-  auto hint_info = pdfium::MakeUnique<CPDF_Array>();
-  // Add primary hint stream offset
-  hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
-  // Add primary hint stream size
-  hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
-  // Set hint stream info.
-  linearized_dict->SetFor("H", std::move(hint_info));
-
-  const int shared_hint_table_offset = GetData(&data32, &data, &size);
-
-  {
-    FakeLinearized linearized(linearized_dict.get());
-    HintTableForFuzzing hint_table(&linearized, shared_hint_table_offset);
-    hint_table.Fuzz(data, size);
-  }
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_jpx_fuzzer.cc b/testing/libfuzzer/pdf_jpx_fuzzer.cc
deleted file mode 100644
index da65bc2..0000000
--- a/testing/libfuzzer/pdf_jpx_fuzzer.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "core/fxcodec/codec/ccodec_jpxmodule.h"
-#include "core/fxcodec/codec/cjpx_decoder.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-
-CCodec_JpxModule g_module;
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::unique_ptr<CJPX_Decoder> decoder =
-      g_module.CreateDecoder(data, size, nullptr);
-  if (!decoder)
-    return 0;
-
-  uint32_t width;
-  uint32_t height;
-  uint32_t components;
-  g_module.GetImageInfo(decoder.get(), &width, &height, &components);
-
-  static constexpr uint32_t kMemLimit = 1024 * 1024 * 1024;  // 1 GB.
-  FX_SAFE_UINT32 mem = width;
-  mem *= height;
-  mem *= components;
-  if (!mem.IsValid() || mem.ValueOrDie() > kMemLimit)
-    return 0;
-
-  FXDIB_Format format;
-  if (components == 1) {
-    format = FXDIB_8bppRgb;
-  } else if (components <= 3) {
-    format = FXDIB_Rgb;
-  } else if (components == 4) {
-    format = FXDIB_Rgb32;
-  } else {
-    width = (width * components + 2) / 3;
-    format = FXDIB_Rgb;
-  }
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!bitmap->Create(width, height, format))
-    return 0;
-
-  std::vector<uint8_t> output_offsets(components);
-  for (uint32_t i = 0; i < components; ++i)
-    output_offsets[i] = i;
-
-  g_module.Decode(decoder.get(), bitmap->GetBuffer(), bitmap->GetPitch(),
-                  output_offsets);
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_lzw_fuzzer.cc b/testing/libfuzzer/pdf_lzw_fuzzer.cc
deleted file mode 100644
index 71c2589..0000000
--- a/testing/libfuzzer/pdf_lzw_fuzzer.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <vector>
-
-#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
-#include "third_party/base/numerics/safe_conversions.h"
-
-// Between 2x and 5x is a standard range for LZW according to a quick
-// search of papers. Running up to 10x to catch any niche cases.
-constexpr uint32_t kMinCompressionRatio = 2;
-constexpr uint32_t kMaxCompressionRatio = 10;
-
-void LZWFuzz(const uint8_t* src_buf,
-             size_t src_size,
-             uint8_t color_exp,
-             uint8_t code_exp) {
-  std::unique_ptr<CFX_LZWDecompressor> decompressor =
-      CFX_LZWDecompressor::Create(color_exp, code_exp);
-  if (!decompressor)
-    return;
-
-  for (uint32_t compressions_ratio = kMinCompressionRatio;
-       compressions_ratio <= kMaxCompressionRatio; compressions_ratio++) {
-    std::vector<uint8_t> des_buf(compressions_ratio * src_size);
-    // This cast should be safe since the caller is checking for overflow on
-    // the initial data.
-    uint32_t des_size = static_cast<uint32_t>(des_buf.size());
-    if (CFX_GifDecodeStatus::InsufficientDestSize !=
-        decompressor->Decode(const_cast<uint8_t*>(src_buf), src_size,
-                             des_buf.data(), &des_size))
-      return;
-  }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  // Need at least 3 bytes to do anything.
-  if (size < 3)
-    return 0;
-
-  // Normally the GIF would provide the code and color sizes, instead, going
-  // to assume they are the first two bytes of data provided.
-  uint8_t color_exp = data[0];
-  uint8_t code_exp = data[1];
-  const uint8_t* lzw_data = data + 2;
-  uint32_t lzw_data_size = static_cast<uint32_t>(size - 2);
-  // Check that there isn't going to be an overflow in the destination buffer
-  // size.
-  if (lzw_data_size >
-      std::numeric_limits<uint32_t>::max() / kMaxCompressionRatio) {
-    return 0;
-  }
-
-  LZWFuzz(lzw_data, lzw_data_size, color_exp, code_exp);
-
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_psengine_fuzzer.cc b/testing/libfuzzer/pdf_psengine_fuzzer.cc
deleted file mode 100644
index fcdfed8..0000000
--- a/testing/libfuzzer/pdf_psengine_fuzzer.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-
-#include "core/fpdfapi/page/cpdf_psengine.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  CPDF_PSEngine engine;
-  if (engine.Parse(reinterpret_cast<const char*>(data), size))
-    engine.Execute();
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_streamparser_fuzzer.cc b/testing/libfuzzer/pdf_streamparser_fuzzer.cc
deleted file mode 100644
index 46113d4..0000000
--- a/testing/libfuzzer/pdf_streamparser_fuzzer.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstdint>
-#include <memory>
-
-#include "core/fpdfapi/page/cpdf_streamparser.h"
-#include "core/fpdfapi/parser/cpdf_object.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  CPDF_StreamParser parser(data, size);
-  while (std::unique_ptr<CPDF_Object> pObj =
-             parser.ReadNextObject(true, false, 0))
-    continue;
-
-  return 0;
-}
diff --git a/testing/libfuzzer/pdf_xml_fuzzer.cc b/testing/libfuzzer/pdf_xml_fuzzer.cc
deleted file mode 100644
index d8010f9..0000000
--- a/testing/libfuzzer/pdf_xml_fuzzer.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstddef>
-#include <cstdint>
-#include <memory>
-
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/xml/cfx_xmldoc.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "core/fxcrt/xml/cfx_xmlparser.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-CFX_XMLNode* XFA_FDEExtension_GetDocumentNode(
-    CFX_XMLDoc* pXMLDoc,
-    bool bVerifyWellFormness = false) {
-  if (!pXMLDoc) {
-    return nullptr;
-  }
-  CFX_XMLNode* pXMLFakeRoot = pXMLDoc->GetRoot();
-  for (CFX_XMLNode* pXMLNode =
-           pXMLFakeRoot->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLNode; pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    if (pXMLNode->GetType() == FX_XMLNODE_Element) {
-      if (bVerifyWellFormness) {
-        for (CFX_XMLNode* pNextNode =
-                 pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling);
-             pNextNode;
-             pNextNode = pNextNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-          if (pNextNode->GetType() == FX_XMLNODE_Element) {
-            return nullptr;
-          }
-        }
-      }
-      return pXMLNode;
-    }
-  }
-  return nullptr;
-}
-
-}  // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  FX_SAFE_SIZE_T safe_size = size;
-  if (!safe_size.IsValid())
-    return 0;
-
-  RetainPtr<CFX_SeekableStreamProxy> stream =
-      pdfium::MakeRetain<CFX_SeekableStreamProxy>(const_cast<uint8_t*>(data),
-                                                  size);
-  auto doc = pdfium::MakeUnique<CFX_XMLDoc>();
-  if (!doc->LoadXML(pdfium::MakeUnique<CFX_XMLParser>(doc->GetRoot(), stream)))
-    return 0;
-
-  if (doc->DoLoad() < 100)
-    return 0;
-
-  (void)XFA_FDEExtension_GetDocumentNode(doc.get());
-  return 0;
-}
diff --git a/testing/libfuzzer/unittest_main.cc b/testing/libfuzzer/unittest_main.cc
deleted file mode 100644
index f6b29e4..0000000
--- a/testing/libfuzzer/unittest_main.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// A simple unit-test style driver for libfuzzer tests.
-// Usage: <fuzzer_test> <file>...
-
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <vector>
-
-// Libfuzzer API.
-extern "C" {
-// User function.
-int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size);
-// Initialization function.
-__attribute__((weak)) int LLVMFuzzerInitialize(int* argc, char*** argv);
-}
-
-std::vector<char> readFile(std::string path) {
-  std::ifstream in(path);
-  return std::vector<char>((std::istreambuf_iterator<char>(in)),
-                           std::istreambuf_iterator<char>());
-}
-
-int main(int argc, char** argv) {
-  if (argc == 1) {
-    std::cerr << "Usage: " << argv[0] << " <file>..." << std::endl;
-    exit(1);
-  }
-
-  if (LLVMFuzzerInitialize)
-    LLVMFuzzerInitialize(&argc, &argv);
-
-  for (int i = 1; i < argc; ++i) {
-    std::cout << argv[i] << std::endl;
-    auto v = readFile(argv[i]);
-    LLVMFuzzerTestOneInput((const unsigned char*)v.data(), v.size());
-  }
-}
diff --git a/testing/libfuzzer/xfa_codec_fuzzer.h b/testing/libfuzzer/xfa_codec_fuzzer.h
deleted file mode 100644
index b1d888f..0000000
--- a/testing/libfuzzer/xfa_codec_fuzzer.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef TESTING_LIBFUZZER_XFA_CODEC_FUZZER_H_
-#define TESTING_LIBFUZZER_XFA_CODEC_FUZZER_H_
-
-#include <memory>
-
-#include "core/fxcodec/codec/ccodec_bmpmodule.h"
-#include "core/fxcodec/codec/ccodec_gifmodule.h"
-#include "core/fxcodec/codec/ccodec_pngmodule.h"
-#include "core/fxcodec/codec/ccodec_progressivedecoder.h"
-#include "core/fxcodec/codec/ccodec_tiffmodule.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-#include "testing/fx_string_testhelpers.h"
-#include "third_party/base/ptr_util.h"
-
-// Support up to 64 MB. This prevents trivial OOM when MSAN is on and
-// time outs.
-const int kXFACodecFuzzerPixelLimit = 64000000;
-
-class XFACodecFuzzer {
- public:
-  static int Fuzz(const uint8_t* data, size_t size, FXCODEC_IMAGE_TYPE type) {
-    auto mgr = pdfium::MakeUnique<CCodec_ModuleMgr>();
-    mgr->SetBmpModule(pdfium::MakeUnique<CCodec_BmpModule>());
-    mgr->SetGifModule(pdfium::MakeUnique<CCodec_GifModule>());
-    mgr->SetPngModule(pdfium::MakeUnique<CCodec_PngModule>());
-    mgr->SetTiffModule(pdfium::MakeUnique<CCodec_TiffModule>());
-
-    std::unique_ptr<CCodec_ProgressiveDecoder> decoder =
-        mgr->CreateProgressiveDecoder();
-    auto source = pdfium::MakeRetain<CFX_BufferSeekableReadStream>(data, size);
-    FXCODEC_STATUS status = decoder->LoadImageInfo(source, type, nullptr, true);
-    if (status != FXCODEC_STATUS_FRAME_READY)
-      return 0;
-
-    // Skipping very large images, since they will take a long time and may lead
-    // to OOM.
-    FX_SAFE_UINT32 bitmap_size = decoder->GetHeight();
-    bitmap_size *= decoder->GetWidth();
-    bitmap_size *= 4;  // From CFX_DIBitmap impl.
-    if (!bitmap_size.IsValid() ||
-        bitmap_size.ValueOrDie() > kXFACodecFuzzerPixelLimit) {
-      return 0;
-    }
-
-    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb);
-
-    size_t frames;
-    std::tie(status, frames) = decoder->GetFrames();
-    if (status != FXCODEC_STATUS_DECODE_READY || frames == 0)
-      return 0;
-
-    status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
-                                  bitmap->GetHeight());
-    while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
-      status = decoder->ContinueDecode();
-
-    return 0;
-  }
-};
-
-#endif  // TESTING_LIBFUZZER_XFA_CODEC_FUZZER_H_
diff --git a/testing/pseudo_retainable.h b/testing/pseudo_retainable.h
new file mode 100644
index 0000000..c4390d6
--- /dev/null
+++ b/testing/pseudo_retainable.h
@@ -0,0 +1,26 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_PSEUDO_RETAINABLE_H_
+#define TESTING_PSEUDO_RETAINABLE_H_
+
+class PseudoRetainable {
+ public:
+  PseudoRetainable() = default;
+  void Retain() { ++retain_count_; }
+  void Release() {
+    if (++release_count_ == retain_count_)
+      alive_ = false;
+  }
+  bool alive() const { return alive_; }
+  int retain_count() const { return retain_count_; }
+  int release_count() const { return release_count_; }
+
+ private:
+  bool alive_ = true;
+  int retain_count_ = 0;
+  int release_count_ = 0;
+};
+
+#endif  // TESTING_PSEUDO_RETAINABLE_H_
diff --git a/testing/resources/annotation_highlight_rollover_ap.in b/testing/resources/annotation_highlight_rollover_ap.in
index 426ed88..4ee637d 100644
--- a/testing/resources/annotation_highlight_rollover_ap.in
+++ b/testing/resources/annotation_highlight_rollover_ap.in
@@ -78,9 +78,6 @@
 endobj
 
 {{xref}}
-trailer <<
-  /Root 1 0 R
-  /Size 6
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/annotation_markup_multiline_no_ap.in b/testing/resources/annotation_markup_multiline_no_ap.in
new file mode 100644
index 0000000..5107831
--- /dev/null
+++ b/testing/resources/annotation_markup_multiline_no_ap.in
@@ -0,0 +1,97 @@
+{{header}}
+
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 595 842]
+  /Annots [
+    4 0 R
+    5 0 R
+    6 0 R
+    7 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /Rect [ 108 602 506 640 ]
+  /NM (Hilight-1)
+  /F 4
+  /QuadPoints [
+    107.7896 639.9486 505.8939 639.9486 107.7896 629.1634 505.8939 629.1634
+    107.7896 626.2871 505.8939 626.2871 107.7896 615.5011 505.8939 615.5011
+    107.7896 612.6248 380.1389 612.6248 107.7896 601.8397 380.1389 601.8397
+  ]
+  /C [ 1 1 0 ]
+  /Contents ()
+>>
+endobj
+
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Underline
+  /Rect [ 108 552 506 590 ]
+  /NM (Underline-1)
+  /F 4
+  /QuadPoints [
+    107.7896 589.9486 505.8939 589.9486 107.7896 579.1634 505.8939 579.1634
+    107.7896 576.2871 505.8939 576.2871 107.7896 565.5011 505.8939 565.5011
+    107.7896 562.6248 380.1389 562.6248 107.7896 551.8397 380.1389 551.8397
+  ]
+  /C [ 0 0 0 ]
+  /Contents ()
+>>
+endobj
+
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Squiggly
+  /Rect [ 108 502 506 540 ]
+  /NM (Squiggly-1)
+  /F 4
+  /QuadPoints [
+    107.7896 539.9486 505.8939 539.9486 107.7896 529.1634 505.8939 529.1634
+    107.7896 526.2871 505.8939 526.2871 107.7896 515.5011 505.8939 515.5011
+    107.7896 512.6248 380.1389 512.6248 107.7896 501.8397 380.1389 501.8397
+  ]
+  /C [ 0 0 0 ]
+  /Contents ()
+>>
+endobj
+
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /StrikeOut
+  /Rect [ 108 452 506 490 ]
+  /NM (StrikeOut-1)
+  /F 4
+  /QuadPoints [
+    107.7896 489.9486 505.8939 489.9486 107.7896 479.1634 505.8939 479.1634
+    107.7896 476.2871 505.8939 476.2871 107.7896 465.5011 505.8939 465.5011
+    107.7896 462.6248 380.1389 462.6248 107.7896 451.8397 380.1389 451.8397
+  ]
+  /C [ 0 0 0 ]
+  /Contents ()
+>>
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/annotation_markup_multiline_no_ap.pdf b/testing/resources/annotation_markup_multiline_no_ap.pdf
new file mode 100644
index 0000000..deac036
--- /dev/null
+++ b/testing/resources/annotation_markup_multiline_no_ap.pdf
@@ -0,0 +1,108 @@
+%PDF-1.7
+% ò¤ô
+
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 595 842]
+  /Annots [
+    4 0 R
+    5 0 R
+    6 0 R
+    7 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /Rect [ 108 602 506 640 ]
+  /NM (Hilight-1)
+  /F 4
+  /QuadPoints [
+    107.7896 639.9486 505.8939 639.9486 107.7896 629.1634 505.8939 629.1634
+    107.7896 626.2871 505.8939 626.2871 107.7896 615.5011 505.8939 615.5011
+    107.7896 612.6248 380.1389 612.6248 107.7896 601.8397 380.1389 601.8397
+  ]
+  /C [ 1 1 0 ]
+  /Contents ()
+>>
+endobj
+
+5 0 obj <<
+  /Type /Annot
+  /Subtype /Underline
+  /Rect [ 108 552 506 590 ]
+  /NM (Underline-1)
+  /F 4
+  /QuadPoints [
+    107.7896 589.9486 505.8939 589.9486 107.7896 579.1634 505.8939 579.1634
+    107.7896 576.2871 505.8939 576.2871 107.7896 565.5011 505.8939 565.5011
+    107.7896 562.6248 380.1389 562.6248 107.7896 551.8397 380.1389 551.8397
+  ]
+  /C [ 0 0 0 ]
+  /Contents ()
+>>
+endobj
+
+6 0 obj <<
+  /Type /Annot
+  /Subtype /Squiggly
+  /Rect [ 108 502 506 540 ]
+  /NM (Squiggly-1)
+  /F 4
+  /QuadPoints [
+    107.7896 539.9486 505.8939 539.9486 107.7896 529.1634 505.8939 529.1634
+    107.7896 526.2871 505.8939 526.2871 107.7896 515.5011 505.8939 515.5011
+    107.7896 512.6248 380.1389 512.6248 107.7896 501.8397 380.1389 501.8397
+  ]
+  /C [ 0 0 0 ]
+  /Contents ()
+>>
+endobj
+
+7 0 obj <<
+  /Type /Annot
+  /Subtype /StrikeOut
+  /Rect [ 108 452 506 490 ]
+  /NM (StrikeOut-1)
+  /F 4
+  /QuadPoints [
+    107.7896 489.9486 505.8939 489.9486 107.7896 479.1634 505.8939 479.1634
+    107.7896 476.2871 505.8939 476.2871 107.7896 465.5011 505.8939 465.5011
+    107.7896 462.6248 380.1389 462.6248 107.7896 451.8397 380.1389 451.8397
+  ]
+  /C [ 0 0 0 ]
+  /Contents ()
+>>
+endobj
+
+xref
+0 8
+0000000000 65535 f 
+0000000016 00000 n 
+0000000070 00000 n 
+0000000136 00000 n 
+0000000281 00000 n 
+0000000671 00000 n 
+0000001063 00000 n 
+0000001453 00000 n 
+trailer<< /Root 1 0 R /Size 8 >>
+startxref
+1845
+%%EOF
diff --git a/testing/resources/bad_annots_entry.in b/testing/resources/bad_annots_entry.in
new file mode 100644
index 0000000..7572cb6
--- /dev/null
+++ b/testing/resources/bad_annots_entry.in
@@ -0,0 +1,54 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm << /Fields [ 4 0 R ] /DR 5 0 R >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 300 ]
+  /Contents 8 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+{{object 4 0}} []
+endobj
+{{object 5 0}} <<
+  /Font 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /F1 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 150 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bad_annots_entry.pdf b/testing/resources/bad_annots_entry.pdf
new file mode 100644
index 0000000..a900a22
--- /dev/null
+++ b/testing/resources/bad_annots_entry.pdf
@@ -0,0 +1,69 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm << /Fields [ 4 0 R ] /DR 5 0 R >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 300 ]
+  /Contents 8 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+4 0 obj []
+endobj
+5 0 obj <<
+  /Font 6 0 R
+>>
+endobj
+6 0 obj <<
+  /F1 7 0 R
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+  /Length 51
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 150 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000114 00000 n 
+0000000179 00000 n 
+0000000315 00000 n 
+0000000333 00000 n 
+0000000368 00000 n 
+0000000401 00000 n 
+0000000477 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+579
+%%EOF
diff --git a/testing/resources/bug_1029.pdf b/testing/resources/bug_1029.pdf
new file mode 100644
index 0000000..c03f03d
--- /dev/null
+++ b/testing/resources/bug_1029.pdf
Binary files differ
diff --git a/testing/resources/bug_1139.in b/testing/resources/bug_1139.in
new file mode 100644
index 0000000..d5603f0
--- /dev/null
+++ b/testing/resources/bug_1139.in
@@ -0,0 +1,55 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+>>
+stream
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(\003Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1139.pdf b/testing/resources/bug_1139.pdf
new file mode 100644
index 0000000..d89695e
--- /dev/null
+++ b/testing/resources/bug_1139.pdf
@@ -0,0 +1,68 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+>>
+stream
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(\003Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000303 00000 n 
+0000000381 00000 n 
+0000000457 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+589
+%%EOF
diff --git a/testing/resources/bug_1206.in b/testing/resources/bug_1206.in
new file mode 100644
index 0000000..cb606ae
--- /dev/null
+++ b/testing/resources/bug_1206.in
@@ -0,0 +1,68 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [6 0 R 7 0 R]
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 rg
+0 0 20 20 re B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0.0 0.0 20.0 20.0]
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 rg
+0 0 20 20 re B*
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Text
+  /Rect [100 130 120 150]
+  /Contents (Goodbye world)
+  /P 3 0 R
+  /F 28
+  /AP << /N 5 0 R >>
+  /T (Someone)
+  /Popup 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Popup
+  /Rect [0 0 60 80]
+  /F 28
+  /Parent 6 0 R
+  /Open false
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1206.pdf b/testing/resources/bug_1206.pdf
new file mode 100644
index 0000000..fa0cc18
--- /dev/null
+++ b/testing/resources/bug_1206.pdf
@@ -0,0 +1,82 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [6 0 R 7 0 R]
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+4 0 obj <<
+  /Length 29
+>>
+stream
+q
+1 0 0 rg
+0 0 20 20 re B*
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0.0 0.0 20.0 20.0]
+  /Length 29
+>>
+stream
+q
+1 0 0 rg
+0 0 20 20 re B*
+Q
+endstream
+endobj
+6 0 obj <<
+  /Type /Annot
+  /Subtype /Text
+  /Rect [100 130 120 150]
+  /Contents (Goodbye world)
+  /P 3 0 R
+  /F 28
+  /AP << /N 5 0 R >>
+  /T (Someone)
+  /Popup 7 0 R
+>>
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Popup
+  /Rect [0 0 60 80]
+  /F 28
+  /Parent 6 0 R
+  /Open false
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000250 00000 n 
+0000000330 00000 n 
+0000000472 00000 n 
+0000000649 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+761
+%%EOF
diff --git a/testing/resources/bug_1265.in b/testing/resources/bug_1265.in
new file mode 100644
index 0000000..dc4d027
--- /dev/null
+++ b/testing/resources/bug_1265.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 5 0 R
+  /Pages 2 0 R
+  /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /MediaBox [0 0 1 1]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+  <template><subform><overflow trailer="$"/><occur/><exclGroup h="757pt">
+endstream
+endobj
+{{object 5 0}} <<
+  /XFA 4 0 R
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{trailer}}
+{{xref}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/bug_1265.pdf b/testing/resources/bug_1265.pdf
new file mode 100644
index 0000000..6735061
--- /dev/null
+++ b/testing/resources/bug_1265.pdf
@@ -0,0 +1,56 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /AcroForm 5 0 R
+  /Pages 2 0 R
+  /NeedsRendering true
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /MediaBox [0 0 1 1]
+>>
+endobj
+4 0 obj <<
+  /Length 121
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+  <template><subform><overflow trailer="$"/><occur/><exclGroup h="757pt">
+endstream
+endobj
+5 0 obj <<
+  /XFA 4 0 R
+>>
+endobj
+6 0 obj <<
+  /Length 0
+>>
+stream
+endstream
+endobj
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000109 00000 n 
+0000000172 00000 n 
+0000000263 00000 n 
+0000000436 00000 n 
+0000000470 00000 n 
+startxref
+558
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/bug_1301.in b/testing/resources/bug_1301.in
new file mode 100644
index 0000000..21c68cb
--- /dev/null
+++ b/testing/resources/bug_1301.in
@@ -0,0 +1,33 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
+  <subform name="Form01" layout="tb" locale="en_ZA" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1" initialNumber="1">
+        <contentArea x="10.455mm" w="190.5mm" h="286mm"/>
+        <occur min="1" max="1"/>
+      </pageArea>
+    </pageSet>
+    <subform w="190.5mm" h="286mm" name="TForm1SubForm1">
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageArea" startNew="1"/>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{endxref}}
+%%EOF
diff --git a/testing/resources/bug_1301.pdf b/testing/resources/bug_1301.pdf
new file mode 100644
index 0000000..285c7f7
--- /dev/null
+++ b/testing/resources/bug_1301.pdf
@@ -0,0 +1,241 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 538
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
+  <subform name="Form01" layout="tb" locale="en_ZA" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1" initialNumber="1">
+        <contentArea x="10.455mm" w="190.5mm" h="286mm"/>
+        <occur min="1" max="1"/>
+      </pageArea>
+    </pageSet>
+    <subform w="190.5mm" h="286mm" name="TForm1SubForm1">
+    </subform>
+    <subform w="190.5mm" h="286mm">
+      <breakBefore targetType="pageArea" startNew="1"/>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000001818 00000 n 
+0000005326 00000 n 
+0000005388 00000 n 
+0000005451 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+{{endxref}}
+%%EOF
diff --git a/testing/resources/bug_1388_cmap.fragment b/testing/resources/bug_1388_cmap.fragment
new file mode 100644
index 0000000..a9a8354
--- /dev/null
+++ b/testing/resources/bug_1388_cmap.fragment
@@ -0,0 +1,8 @@
+/CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo <<
+/Registry (AAAAAB+Test) /Ordering (UCS) /Supplement 0 >> def
+/CMapName /AAAAAB+Test def
+1 begincodespacerange <20> <20> endcodespacerange
+10 beginbfrange
+<20> <20> <b0a9>
+endbfrange
+endcmap CMapName currentdict /CMap defineresource pop end end
diff --git a/testing/resources/bug_1388_truetype_font.fragment b/testing/resources/bug_1388_truetype_font.fragment
new file mode 100644
index 0000000..d9a2c75
--- /dev/null
+++ b/testing/resources/bug_1388_truetype_font.fragment
@@ -0,0 +1,37 @@
+789cdd965b6c944514c7ff33b3b76eb7edf6ba6d29edb6dddeb7dddee875717b87062f2b12a262
+036b296d13b65d29901269a2125379c0006aa289d110e465a2a56aa288517c308a023e18154511
+d4048ca212bcbc007e9ed9fda654e383bef8e06cbe6fce6fce65ce9cc9b7336000dcf4b2000303
+6b6e193977dc053863345ab0a2af7f0015c8250e137b6fbb23d0f8d2ee6d2701f608f1dae16824
+06d7cf27882fd1d333bc7dab377aeff667003ba9996f536c349affc65b9f00a29e1e3e1a998a19
+0604c55b49068ed1cd3b36cdb9079f26d14f3c3d3612d978e5b777f3c9977240cb180dd82bc414
+711fb16f2cba75da01cc139f27766d9e1c8e208869f2f7a978d1c8748c05f018e9c754be1391e8
+48ddfee3e4ef788ec6666293535b8d0da0584e65ef8b6d1989a53c7ef475c0d64cfc1e542dacc0
+ddfb7e796d7d5af057145151a8bdb271f082ea3f7bb42d4ab37f230af945351f38128dfcc4a431
+892713f6a2301e697113f11141554e6838dca8a7dc812f31948841aabd34bb839fe24f11ef49f4
+ec5b34b124879527db08549b073f3c7e7ae755b2a9578e5e6ae8a2ba9a398849eceb72e0d973a7
+95d612af57bc4fe491780acc3c8266660564e08d9bc533bcec55f1d43bedbc5e0bb36161598cc7
+d7f0e7f6d735ff2f5b57b0b3a3bdadb56559735363437da0aed65f535d5559515ee62b2d29f616
+152e2d58929f97ebc9c9cecacc4877a7a5a6b8929d490ebbcd6a119cc1bf6a754fad3f907b36fe
+f204a9f304e3a43ac50b83d5eeea5a7f48e942094d286119d296216d348fde3b5f6c457f61d15d
+048ce0260d9c60b906b1d8cca2cc6c265895c66e828da0579bd90906934c7010946a485a3c8f53
+05709a904c50aea3b9088ab52685a0476b52095a34a411b4bb4c7013b4694827e8d20132089ab4
+4fa6ca40a793a57cb4269be066ed9343102c32c143e0d73eb904ddda2c4f99959a90af347a714b
+9446430141870eb0946099864295a80e50a4d6a3cbeb55b9e900c504951a4a083a6b4c2855d1fc
+26f8d4a43a7419418d0e5dae8aa83515aaf01a2a55b4352654a92568a856596ba85185d7cbf62b
+1f0db504cd1aea16e716b8911b7d037b03b3a89b45ad845fa246a25aa24aa252a242a25ca24cc2
+27511a464918c5125e8922894289a51205124b24f225f22472253c123912d912591299121912e9
+126e893489548914099744b284532249c2216197b085610dc31286088387c11a81461c61c63cdb
+f370cdbf6cea834ee3e70c837bf8573c13a970d1bf58169cbc16397c390ff15ede8d9dbc950ff2
+15bc93b7f3001fe5ebb89d1719b3ec09760685d825927804e97c15dfcd0fe276d6c62442c88007
+337c17ff0956e3aaf13d3bcc5ee6f7b15d18e6d9fc7e16e233ac9b1fc359e305b48b0efe3c5ab9
+133b442b9f62236c867f6e4c1a0ff243c60eb1019d22c40e1a87902756634ef8443a5b2fee314e
+891eacc790e8325e15015c330e1bc7c44d6240dc8a0ba20e1fe15351202a8543b4e13b7c2d82f8
+809fc003780863c24fa7d317c24be7cefba211674486c8c265bc2356e2435cc2495182a3a20f57
+f0267e67e3f811c7d901a87342fdd8c2e92730d4d5ddd8909dc9aa86cb864b239e3cd65dc17a97
+b1240bb3db9893b3ce5cbbadabb9526455e7f39c6ee7223199b19e165eeee31df14d0804023548
+ec4728140aa8d6509f599c5e3ccf27aeefe717af5fa3f95331b7e87ce1a6cce044dec289ea4487
+290b92d799b285e49da66ca5fd3d60ca361a7f5b9d7e9624336a4256fb2f4c9993ec33654172bf
+295b48de64ca56ba1fcd9ab28dc6e754a8305d3206308e6d88a2014d68a31fc27d03e3dba20d4d
+6d6dff89c15a8c600ba6c864121374b837a08e2e0c5e528c6c991a9f9cf036d4d57bff411c2c9c
+f4c6107af177e7bcb91b746748ec8cbabdb9a92e1c898b07376dd80deb8f7fe83fa2fac577ab3f
+00dfbbf54d0a
diff --git a/testing/resources/bug_1399.pdf b/testing/resources/bug_1399.pdf
new file mode 100644
index 0000000..1b2e30c
--- /dev/null
+++ b/testing/resources/bug_1399.pdf
Binary files differ
diff --git a/testing/resources/bug_182.pdf b/testing/resources/bug_182.pdf
new file mode 100644
index 0000000..bf35cc9
--- /dev/null
+++ b/testing/resources/bug_182.pdf
Binary files differ
diff --git a/testing/resources/bug_306123.in b/testing/resources/bug_306123.in
new file mode 100644
index 0000000..e946897
--- /dev/null
+++ b/testing/resources/bug_306123.in
@@ -0,0 +1,1059 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.8/">
+  <subform layout="tb" locale="en_GB_EURO">
+    <pageSet>
+      <pageArea>
+        <contentArea x="3mm" w="204mm" h="277mm" y="5mm"/>
+        <medium stock="a4" short="210mm" long="297mm"/>
+        <draw w="210mm" h="297mm"/>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="284mm" x="5mm" w="68mm" h="12mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+        <field y="283mm" x="47mm" w="62mm" h="9mm" presence="invisible">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="284mm" x="73mm" w="69mm" h="12mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+        <field w="62mm" h="9mm" access="readOnly">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field w="62mm" h="9mm" access="readOnly">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="284mm" x="142mm" w="65mm" h="8mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+        <field w="62mm" h="9mm" access="readOnly">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="292mm" x="176mm" w="31mm" h="4.528mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+      </pageArea>
+    </pageSet>
+    <subform w="203mm" layout="tb">
+      <field w="0.5mm" h="0.5mm" relevant="-print">
+        <ui>
+          <button highlight="inverted"/>
+        </ui>
+      </field>
+      <subform w="203mm" layout="tb">
+        <overflow leader="Bloog"/>
+      </subform>
+      <subform w="203mm" layout="tb" presence="hidden">
+        <breakBefore targetType="pageArea" startNew="1"/>
+      </subform>
+      <subform w="203mm" minH="6.421mm" layout="tb">
+        <subform y="6.421mm" w="203mm" minH="52.27mm" layout="tb">
+          <margin leftInset="1mm"/>
+          <subform w="203mm" layout="lr-tb">
+            <draw w="127mm" h="0.375472in">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <exclGroup name="RadioButtonList" layout="lr-tb">
+              <field w="30mm" h="6mm" name="yes">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Agree</text>
+                </items>
+              </field>
+              <field w="30mm" h="6mm" name="no">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Disagree</text>
+                </items>
+              </field>
+              <validate nullTest="error"/>
+            </exclGroup>
+            <margin bottomInset="2mm"/>
+          </subform>
+          <subform w="203mm" layout="lr-tb">
+            <draw w="127mm" minH="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <exclGroup name="RadioButtonList" layout="lr-tb">
+              <field w="30mm" h="6mm" name="yes">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Agree</text>
+                </items>
+              </field>
+              <field w="30mm" h="6mm" name="no">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Disagree</text>
+                </items>
+              </field>
+              <validate nullTest="error"/>
+            </exclGroup>
+            <margin bottomInset="2mm"/>
+          </subform>
+          <subform w="203mm" layout="lr-tb">
+            <draw w="127mm" minH="10.462mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <exclGroup name="RadioButtonList" layout="lr-tb">
+              <field w="30mm" h="6mm" name="yes">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Agree</text>
+                </items>
+              </field>
+              <field w="30mm" h="6mm" name="no">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Disagree</text>
+                </items>
+              </field>
+              <validate nullTest="error"/>
+            </exclGroup>
+            <margin bottomInset="2mm"/>
+          </subform>
+          <subform w="202mm" minH="34.081mm" layout="lr-tb">
+            <draw w="7.95275in" h="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <field w="202mm" minH="28mm">
+              <ui>
+                <textEdit multiLine="1">
+                </textEdit>
+              </ui>
+              <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+            </field>
+            <margin topInset="1mm" bottomInset="1mm"/>
+          </subform>
+          <subform w="202mm" minH="34.081mm" layout="lr-tb" y="3mm">
+            <draw w="7.95275in" h="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <field w="202mm" minH="28mm">
+              <ui>
+                <textEdit multiLine="1">
+                </textEdit>
+              </ui>
+              <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+            </field>
+            <margin topInset="1mm" bottomInset="1mm"/>
+          </subform>
+          <subform w="202mm" minH="34.081mm" layout="lr-tb" y="37.081mm">
+            <draw w="7.95275in" h="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <field w="202mm" minH="28mm">
+              <ui>
+                <textEdit multiLine="1">
+                </textEdit>
+              </ui>
+              <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+            </field>
+            <margin topInset="1mm" bottomInset="1mm"/>
+          </subform>
+          <subform w="203mm" minH="228.176mm" layout="tb">
+            <subform minH="8mm" layout="lr-tb" w="194mm">
+              <field w="53mm" h="8mm">
+                <ui>
+                  <button highlight="outline"/>
+                </ui>
+                <margin bottomInset="0mm"/>
+              </field>
+              <margin topInset="2mm" bottomInset="1mm"/>
+            </subform>
+            <margin topInset="3mm"/>
+          </subform>
+        </subform>
+        <overflow leader="Bloog"/>
+      </subform>
+      <subform w="203mm" layout="tb" presence="hidden">
+        <breakBefore targetType="pageArea" startNew="1"/>
+      </subform>
+      <subform w="203mm" layout="tb">
+        <subform w="203.999mm">
+          <draw minW="203mm" x="0.001mm" y="0mm" h="6.421mm">
+            <ui>
+              <textEdit/>
+            </ui>
+            <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1.27mm" rightInset="1.27mm"/>
+          </draw>
+          <draw h="7.479mm" minW="203mm" y="6.421mm" x="0.001mm">
+            <ui>
+              <textEdit/>
+            </ui>
+            <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1.27mm" rightInset="1.27mm"/>
+          </draw>
+          <draw h="7.427mm" w="7.992125in" y="0mm">
+            <ui>
+              <textEdit/>
+            </ui>
+            <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="2mm" rightInset="1.27mm"/>
+          </draw>
+          <margin leftInset="0mm"/>
+        </subform>
+        <subform w="203mm" minH="52.27mm" layout="lr-tb">
+          <subform w="201mm" h="48.807mm">
+            <draw y="0mm" x="0.001mm" w="7.913347in" h="1.826917in">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="2mm" bottomInset="2mm" leftInset="1mm" rightInset="1.5mm"/>
+            </draw>
+            <margin topInset="2mm" bottomInset="2mm"/>
+          </subform>
+          <margin leftInset="1mm"/>
+          <subform w="202mm" minH="48mm" layout="lr-tb">
+            <margin topInset="0mm" bottomInset="1mm"/>
+            <draw h="6.773mm" w="199mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform minH="8mm" layout="lr-tb" name="Show" w="46mm">
+              <field h="8mm" w="45mm">
+                <ui>
+                  <button highlight="outline"/>
+                </ui>
+                <margin bottomInset="0mm"/>
+              </field>
+              <margin topInset="3mm" bottomInset="2mm"/>
+              <keep intact="contentArea"/>
+            </subform>
+            <subform minH="8mm" layout="lr-tb" name="Hide" w="25mm">
+              <margin topInset="3mm" bottomInset="2mm"/>
+              <keep intact="contentArea"/>
+              <field w="22mm" h="8mm">
+                <ui>
+                  <button highlight="outline"/>
+                </ui>
+                <margin bottomInset="0mm"/>
+              </field>
+            </subform>
+            <subform w="201mm" minH="55.309mm" layout="lr-tb" name="table">
+              <field w="62mm" h="9mm">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <draw w="201mm" minH="6.421mm">
+                <ui>
+                  <textEdit/>
+                </ui>
+                <margin topInset="1mm" bottomInset="3mm" leftInset="1mm" rightInset="1mm"/>
+              </draw>
+              <subform layout="table" columnWidths="30mm 171mm">
+                <subform layout="row"  x="0.074mm" y="0.006mm">
+                  <draw h="8mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                  <draw h="8mm" x="0.001mm" y="0mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                  <occur max="-1"/>
+                </subform>
+                <subform layout="row" x="0.074mm" y="0mm">
+                  <field w="67mm" minH="7.234mm" access="protected" x="0.005mm" y="0mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </field>
+                  <field w="67mm" x="0.004mm" y="0mm" locale="en_GB" access="protected" minH="8mm">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <occur min="0" max="-1"/>
+                </subform>
+                <subform layout="row" x="0.034mm" y="0mm">
+                  <draw minH="7.234mm" w="67mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                  <draw minH="7.234mm" w="67mm" presence="invisible" x="0.001mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                </subform>
+                <overflow leader="HeaderRow"/>
+              </subform>
+            </subform>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+                Show.presence = "visible";
+                Hide.presence = "hidden";
+                table.presence = "hidden";
+              </script>
+            </event>
+          </subform>
+          <subform w="202mm" minH="48mm" layout="lr-tb" y="3mm">
+            <margin topInset="0mm" bottomInset="1mm"/>
+            <draw h="6.773mm" w="108mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform w="201mm" minH="19.001mm" layout="lr-tb">
+              <subform minH="8mm" layout="lr-tb" name="show">
+                <field w="41mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <subform w="22mm" minH="8mm" layout="lr-tb" name="hide">
+                <field w="22mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <field w="62mm" h="9mm" access="protected" presence="hidden">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <subform w="201mm" minH="55.309mm" layout="lr-tb" name="table">
+                <field w="62mm" h="9mm">
+                  <ui>
+                    <textEdit>
+                    </textEdit>
+                  </ui>
+                  <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                </field>
+                <field y="0mm" x="0mm" w="62mm" h="9mm">
+                  <ui>
+                    <textEdit>
+                    </textEdit>
+                  </ui>
+                  <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                </field>
+                <draw h="0.252778in" w="201mm">
+                  <ui>
+                    <textEdit/>
+                  </ui>
+                  <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+                </draw>
+                <subform layout="table" columnWidths="52.5mm 2.066931in 25mm 71mm">
+                  <subform layout="row" x="0.056mm">
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit/>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit/>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit/>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <occur max="-1"/>
+                  </subform>
+                  <subform layout="row" x="0.056mm">
+                    <field w="67mm" minH="7.234mm" x="0.004mm" y="0mm" locale="en_GB" access="protected">
+                      <ui>
+                        <textEdit multiLine="1">
+                        </textEdit>
+                      </ui>
+                      <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                    </field>
+                    <field w="67mm" minH="7.234mm" y="0mm" access="protected">
+                      <ui>
+                        <textEdit multiLine="1">
+                        </textEdit>
+                      </ui>
+                      <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                    </field>
+                    <field w="67mm" minH="7.234mm" access="protected">
+                      <ui>
+                        <dateTimeEdit>
+                        </dateTimeEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                      <format>
+                        <picture
+                            >date{DD/MM/YYYY}</picture>
+                      </format>
+                    </field>
+                    <field w="67mm" minH="7.234mm" y="0mm" access="protected">
+                      <ui>
+                        <textEdit multiLine="1">
+                        </textEdit>
+                      </ui>
+                      <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                    </field>
+                    <occur min="0" max="-1"/>
+                  </subform>
+                  <subform layout="row" x="0.016mm">
+                    <draw minH="7.234mm" w="67mm" presence="invisible" x="0.001mm">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw minH="7.234mm" w="67mm" presence="invisible">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw minH="7.234mm" w="67mm">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw minH="7.234mm" w="67mm" presence="invisible">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                  </subform>
+                  <overflow leader="HeaderRow"/>
+                </subform>
+              </subform>
+              <occur min="0" max="-1"/>
+            </subform>
+          </subform>
+          <subform w="202mm" minH="147.001mm" layout="lr-tb">
+            <draw h="6.773mm" w="108mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform minH="237.19mm" layout="lr-tb" w="202mm">
+              <subform minH="8mm" layout="lr-tb" name="show">
+                <field w="89mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <subform minH="8mm" layout="lr-tb" name="hide">
+                <field w="22mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <field w="62mm" h="9mm" access="protected" presence="hidden">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <subform w="202mm" minH="224.19mm" layout="lr-tb">
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb" y="3mm">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb" y="3mm">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6.036mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+              </subform>
+              <occur min="0" max="-1"/>
+            </subform>
+          </subform>
+          <subform w="202mm" minH="147.001mm" layout="lr-tb">
+            <draw h="6.773mm" w="108mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform minH="237.19mm" layout="lr-tb" w="202mm">
+              <subform minH="8mm" layout="lr-tb" name="show">
+                <field w="60mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <subform minH="8mm" layout="lr-tb" name="hide">
+                <field w="22mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <field w="62mm" h="9mm" access="protected" presence="hidden">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <subform w="202mm" minH="224.19mm" layout="lr-tb">
+                <subform w="127mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="51mm" minH="9mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="26mm" minH="9mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="0mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="127mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="51mm" minH="9mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="76mm" minH="9mm" access="protected">
+                    <ui>
+                      <dateTimeEdit>
+                      </dateTimeEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="0mm" rightInset="1mm"/>
+                    <format>
+                      <picture
+                          >date{DD/MM/YYYY}</picture>
+                    </format>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="127mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="51mm" minH="9mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="76mm" minH="9mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="0mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" h="17.439mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="17.578mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="10mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="20.35mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="17.578mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="15mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="11.651mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="11.651mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="7mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="17.578mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="17.578mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="10mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="11.192mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="11mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="6mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                  <margin bottomInset="2mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="9.537mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="9.537mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+              </subform>
+              <occur min="0" max="-1"/>
+            </subform>
+          </subform>
+          <subform w="22mm" minH="8mm" layout="lr-tb" name="save" y="3mm">
+            <field w="22mm" h="8mm">
+              <ui>
+                <button highlight="outline"/>
+              </ui>
+              <margin bottomInset="0mm"/>
+            </field>
+            <margin topInset="3mm" bottomInset="2mm"/>
+          </subform>
+        </subform>
+        <overflow leader="Bloog"/>
+      </subform>
+      <subform layout="tb" w="203mm">
+        <margin bottomInset="0mm" leftInset="0mm" topInset="0mm"/>
+        <variables>
+        </variables>
+        <subform layout="lr-tb" minH="8.802mm" w="203mm">
+          <keep intact="contentArea"/>
+          <margin leftInset="0mm"/>
+          <draw h="0.238889in" name="tbd" w="203mm">
+            <margin bottomInset="1mm" leftInset="2mm" rightInset="1mm" topInset="1mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <draw h="8mm" w="203mm">
+            <margin bottomInset="0.5mm" leftInset="2mm" rightInset="0.5mm" topInset="2mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <subform layout="lr-tb" name="fieldCaption" w="50mm" presence="hidden">
+            <keep intact="contentArea"/>
+            <margin leftInset="2mm"/>
+            <draw h="8.802mm" name="Normal" w="48mm">
+              <margin bottomInset="0.5mm" leftInset="0mm" rightInset="0.5mm" topInset="0.5mm"/>
+              <ui>
+                <textEdit/>
+              </ui>
+            </draw>
+            <draw h="8.802mm" name="Star" w="48mm">
+              <margin bottomInset="0.5mm" leftInset="0mm" rightInset="0.5mm" topInset="0.5mm"/>
+              <ui>
+                <textEdit/>
+              </ui>
+            </draw>
+          </subform>
+          <field access="readOnly" h="9mm" w="62mm">
+            <margin bottomInset="1mm" leftInset="1mm" rightInset="1mm" topInset="1mm"/>
+            <ui>
+              <textEdit>
+              </textEdit>
+            </ui>
+          </field>
+          <field h="6mm" name="Button1" w="28.575mm">
+            <ui>
+              <button highlight="inverted"/>
+            </ui>
+          </field>
+          <field h="6mm" name="Button2" w="28.575mm">
+            <ui>
+              <button highlight="inverted"/>
+            </ui>
+          </field>
+          <field h="6mm" name="verifySig" w="28.575mm">
+            <ui>
+              <button highlight="inverted"/>
+            </ui>
+          </field>
+          <field h="0.1mm" name="concat" w="0.1mm">
+            <calculate>
+              <script contentType="application/x-javascript">
+	        fieldCaption.Star.presence = "hidden";
+	        fieldCaption.Normal.presence = "visible";
+	        this.rawValue = "Sig Not Required";
+              </script>
+            </calculate>
+            <margin bottomInset="0mm" leftInset="0mm" rightInset="0mm" topInset="0mm"/>
+            <validate>
+              <message>
+                <text name="scriptTest"/>
+              </message>
+            </validate>
+          </field>
+        </subform>
+        <subform layout="lr-tb" w="203mm" presence="hidden">
+          <keep intact="contentArea"/>
+          <margin bottomInset="1mm"/>
+          <draw h="6.269mm" w="7.992125in">
+            <margin bottomInset="0mm" leftInset="2mm" rightInset="0.5mm" topInset="2mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <draw h="5mm" name="Text1" w="139.483mm">
+            <margin bottomInset="0mm" leftInset="2mm" rightInset="0mm" topInset="0.5mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <field access="readOnly" h="0.217639in" name="TextField1" w="0.604861in">
+            <margin bottomInset="0mm" leftInset="0mm" rightInset="0mm" topInset="0mm"/>
+            <ui>
+              <textEdit>
+              </textEdit>
+            </ui>
+            <validate>
+              <message>
+                <text name="scriptTest"/>
+              </message>
+            </validate>
+          </field>
+          <draw h="5mm" name="Text1" w="0.786167in">
+            <margin bottomInset="0mm" leftInset="0mm" rightInset="0.5mm" topInset="0.5mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <field h="9mm" name="url" w="62mm">
+            <margin bottomInset="1mm" leftInset="1mm" rightInset="1mm" topInset="1mm"/>
+            <ui>
+              <textEdit>
+              </textEdit>
+            </ui>
+          </field>
+        </subform>
+      </subform>
+    </subform>
+    <proto>
+      <subform w="202.001mm" name="Bloog" x="0.003mm" y="0.008mm">
+        <occur max="-1"/>
+        <draw w="202mm" h="6mm" x="0.001mm" y="0.001mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+      </subform>
+    </proto>
+  </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_306123.pdf b/testing/resources/bug_306123.pdf
new file mode 100644
index 0000000..5bd1900
--- /dev/null
+++ b/testing/resources/bug_306123.pdf
@@ -0,0 +1,1268 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 42305
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.8/">
+  <subform layout="tb" locale="en_GB_EURO">
+    <pageSet>
+      <pageArea>
+        <contentArea x="3mm" w="204mm" h="277mm" y="5mm"/>
+        <medium stock="a4" short="210mm" long="297mm"/>
+        <draw w="210mm" h="297mm"/>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field y="0mm" x="0mm" w="62mm" h="9mm">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="284mm" x="5mm" w="68mm" h="12mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+        <field y="283mm" x="47mm" w="62mm" h="9mm" presence="invisible">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="284mm" x="73mm" w="69mm" h="12mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+        <field w="62mm" h="9mm" access="readOnly">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <field w="62mm" h="9mm" access="readOnly">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="284mm" x="142mm" w="65mm" h="8mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+        <field w="62mm" h="9mm" access="readOnly">
+          <ui>
+            <textEdit>
+            </textEdit>
+          </ui>
+          <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+        </field>
+        <draw y="292mm" x="176mm" w="31mm" h="4.528mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+      </pageArea>
+    </pageSet>
+    <subform w="203mm" layout="tb">
+      <field w="0.5mm" h="0.5mm" relevant="-print">
+        <ui>
+          <button highlight="inverted"/>
+        </ui>
+      </field>
+      <subform w="203mm" layout="tb">
+        <overflow leader="Bloog"/>
+      </subform>
+      <subform w="203mm" layout="tb" presence="hidden">
+        <breakBefore targetType="pageArea" startNew="1"/>
+      </subform>
+      <subform w="203mm" minH="6.421mm" layout="tb">
+        <subform y="6.421mm" w="203mm" minH="52.27mm" layout="tb">
+          <margin leftInset="1mm"/>
+          <subform w="203mm" layout="lr-tb">
+            <draw w="127mm" h="0.375472in">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <exclGroup name="RadioButtonList" layout="lr-tb">
+              <field w="30mm" h="6mm" name="yes">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Agree</text>
+                </items>
+              </field>
+              <field w="30mm" h="6mm" name="no">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Disagree</text>
+                </items>
+              </field>
+              <validate nullTest="error"/>
+            </exclGroup>
+            <margin bottomInset="2mm"/>
+          </subform>
+          <subform w="203mm" layout="lr-tb">
+            <draw w="127mm" minH="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <exclGroup name="RadioButtonList" layout="lr-tb">
+              <field w="30mm" h="6mm" name="yes">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Agree</text>
+                </items>
+              </field>
+              <field w="30mm" h="6mm" name="no">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Disagree</text>
+                </items>
+              </field>
+              <validate nullTest="error"/>
+            </exclGroup>
+            <margin bottomInset="2mm"/>
+          </subform>
+          <subform w="203mm" layout="lr-tb">
+            <draw w="127mm" minH="10.462mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <exclGroup name="RadioButtonList" layout="lr-tb">
+              <field w="30mm" h="6mm" name="yes">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Agree</text>
+                </items>
+              </field>
+              <field w="30mm" h="6mm" name="no">
+                <ui>
+                  <checkButton shape="round">
+                  </checkButton>
+                </ui>
+                <margin leftInset="3mm" rightInset="1mm"/>
+                <items>
+                  <text>Disagree</text>
+                </items>
+              </field>
+              <validate nullTest="error"/>
+            </exclGroup>
+            <margin bottomInset="2mm"/>
+          </subform>
+          <subform w="202mm" minH="34.081mm" layout="lr-tb">
+            <draw w="7.95275in" h="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <field w="202mm" minH="28mm">
+              <ui>
+                <textEdit multiLine="1">
+                </textEdit>
+              </ui>
+              <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+            </field>
+            <margin topInset="1mm" bottomInset="1mm"/>
+          </subform>
+          <subform w="202mm" minH="34.081mm" layout="lr-tb" y="3mm">
+            <draw w="7.95275in" h="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <field w="202mm" minH="28mm">
+              <ui>
+                <textEdit multiLine="1">
+                </textEdit>
+              </ui>
+              <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+            </field>
+            <margin topInset="1mm" bottomInset="1mm"/>
+          </subform>
+          <subform w="202mm" minH="34.081mm" layout="lr-tb" y="37.081mm">
+            <draw w="7.95275in" h="9.537mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+            </draw>
+            <field w="202mm" minH="28mm">
+              <ui>
+                <textEdit multiLine="1">
+                </textEdit>
+              </ui>
+              <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+            </field>
+            <margin topInset="1mm" bottomInset="1mm"/>
+          </subform>
+          <subform w="203mm" minH="228.176mm" layout="tb">
+            <subform minH="8mm" layout="lr-tb" w="194mm">
+              <field w="53mm" h="8mm">
+                <ui>
+                  <button highlight="outline"/>
+                </ui>
+                <margin bottomInset="0mm"/>
+              </field>
+              <margin topInset="2mm" bottomInset="1mm"/>
+            </subform>
+            <margin topInset="3mm"/>
+          </subform>
+        </subform>
+        <overflow leader="Bloog"/>
+      </subform>
+      <subform w="203mm" layout="tb" presence="hidden">
+        <breakBefore targetType="pageArea" startNew="1"/>
+      </subform>
+      <subform w="203mm" layout="tb">
+        <subform w="203.999mm">
+          <draw minW="203mm" x="0.001mm" y="0mm" h="6.421mm">
+            <ui>
+              <textEdit/>
+            </ui>
+            <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1.27mm" rightInset="1.27mm"/>
+          </draw>
+          <draw h="7.479mm" minW="203mm" y="6.421mm" x="0.001mm">
+            <ui>
+              <textEdit/>
+            </ui>
+            <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1.27mm" rightInset="1.27mm"/>
+          </draw>
+          <draw h="7.427mm" w="7.992125in" y="0mm">
+            <ui>
+              <textEdit/>
+            </ui>
+            <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="2mm" rightInset="1.27mm"/>
+          </draw>
+          <margin leftInset="0mm"/>
+        </subform>
+        <subform w="203mm" minH="52.27mm" layout="lr-tb">
+          <subform w="201mm" h="48.807mm">
+            <draw y="0mm" x="0.001mm" w="7.913347in" h="1.826917in">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="2mm" bottomInset="2mm" leftInset="1mm" rightInset="1.5mm"/>
+            </draw>
+            <margin topInset="2mm" bottomInset="2mm"/>
+          </subform>
+          <margin leftInset="1mm"/>
+          <subform w="202mm" minH="48mm" layout="lr-tb">
+            <margin topInset="0mm" bottomInset="1mm"/>
+            <draw h="6.773mm" w="199mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform minH="8mm" layout="lr-tb" name="Show" w="46mm">
+              <field h="8mm" w="45mm">
+                <ui>
+                  <button highlight="outline"/>
+                </ui>
+                <margin bottomInset="0mm"/>
+              </field>
+              <margin topInset="3mm" bottomInset="2mm"/>
+              <keep intact="contentArea"/>
+            </subform>
+            <subform minH="8mm" layout="lr-tb" name="Hide" w="25mm">
+              <margin topInset="3mm" bottomInset="2mm"/>
+              <keep intact="contentArea"/>
+              <field w="22mm" h="8mm">
+                <ui>
+                  <button highlight="outline"/>
+                </ui>
+                <margin bottomInset="0mm"/>
+              </field>
+            </subform>
+            <subform w="201mm" minH="55.309mm" layout="lr-tb" name="table">
+              <field w="62mm" h="9mm">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <draw w="201mm" minH="6.421mm">
+                <ui>
+                  <textEdit/>
+                </ui>
+                <margin topInset="1mm" bottomInset="3mm" leftInset="1mm" rightInset="1mm"/>
+              </draw>
+              <subform layout="table" columnWidths="30mm 171mm">
+                <subform layout="row"  x="0.074mm" y="0.006mm">
+                  <draw h="8mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                  <draw h="8mm" x="0.001mm" y="0mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                  <occur max="-1"/>
+                </subform>
+                <subform layout="row" x="0.074mm" y="0mm">
+                  <field w="67mm" minH="7.234mm" access="protected" x="0.005mm" y="0mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </field>
+                  <field w="67mm" x="0.004mm" y="0mm" locale="en_GB" access="protected" minH="8mm">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <occur min="0" max="-1"/>
+                </subform>
+                <subform layout="row" x="0.034mm" y="0mm">
+                  <draw minH="7.234mm" w="67mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                  <draw minH="7.234mm" w="67mm" presence="invisible" x="0.001mm">
+                    <ui>
+                      <textEdit>
+                      </textEdit>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                  </draw>
+                </subform>
+                <overflow leader="HeaderRow"/>
+              </subform>
+            </subform>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+                Show.presence = "visible";
+                Hide.presence = "hidden";
+                table.presence = "hidden";
+              </script>
+            </event>
+          </subform>
+          <subform w="202mm" minH="48mm" layout="lr-tb" y="3mm">
+            <margin topInset="0mm" bottomInset="1mm"/>
+            <draw h="6.773mm" w="108mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform w="201mm" minH="19.001mm" layout="lr-tb">
+              <subform minH="8mm" layout="lr-tb" name="show">
+                <field w="41mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <subform w="22mm" minH="8mm" layout="lr-tb" name="hide">
+                <field w="22mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <field w="62mm" h="9mm" access="protected" presence="hidden">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <subform w="201mm" minH="55.309mm" layout="lr-tb" name="table">
+                <field w="62mm" h="9mm">
+                  <ui>
+                    <textEdit>
+                    </textEdit>
+                  </ui>
+                  <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                </field>
+                <field y="0mm" x="0mm" w="62mm" h="9mm">
+                  <ui>
+                    <textEdit>
+                    </textEdit>
+                  </ui>
+                  <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                </field>
+                <draw h="0.252778in" w="201mm">
+                  <ui>
+                    <textEdit/>
+                  </ui>
+                  <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+                </draw>
+                <subform layout="table" columnWidths="52.5mm 2.066931in 25mm 71mm">
+                  <subform layout="row" x="0.056mm">
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit/>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit/>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw h="15mm">
+                      <ui>
+                        <textEdit/>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <occur max="-1"/>
+                  </subform>
+                  <subform layout="row" x="0.056mm">
+                    <field w="67mm" minH="7.234mm" x="0.004mm" y="0mm" locale="en_GB" access="protected">
+                      <ui>
+                        <textEdit multiLine="1">
+                        </textEdit>
+                      </ui>
+                      <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                    </field>
+                    <field w="67mm" minH="7.234mm" y="0mm" access="protected">
+                      <ui>
+                        <textEdit multiLine="1">
+                        </textEdit>
+                      </ui>
+                      <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                    </field>
+                    <field w="67mm" minH="7.234mm" access="protected">
+                      <ui>
+                        <dateTimeEdit>
+                        </dateTimeEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                      <format>
+                        <picture
+                            >date{DD/MM/YYYY}</picture>
+                      </format>
+                    </field>
+                    <field w="67mm" minH="7.234mm" y="0mm" access="protected">
+                      <ui>
+                        <textEdit multiLine="1">
+                        </textEdit>
+                      </ui>
+                      <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                    </field>
+                    <occur min="0" max="-1"/>
+                  </subform>
+                  <subform layout="row" x="0.016mm">
+                    <draw minH="7.234mm" w="67mm" presence="invisible" x="0.001mm">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw minH="7.234mm" w="67mm" presence="invisible">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw minH="7.234mm" w="67mm">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                    <draw minH="7.234mm" w="67mm" presence="invisible">
+                      <ui>
+                        <textEdit>
+                        </textEdit>
+                      </ui>
+                      <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+                    </draw>
+                  </subform>
+                  <overflow leader="HeaderRow"/>
+                </subform>
+              </subform>
+              <occur min="0" max="-1"/>
+            </subform>
+          </subform>
+          <subform w="202mm" minH="147.001mm" layout="lr-tb">
+            <draw h="6.773mm" w="108mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform minH="237.19mm" layout="lr-tb" w="202mm">
+              <subform minH="8mm" layout="lr-tb" name="show">
+                <field w="89mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <subform minH="8mm" layout="lr-tb" name="hide">
+                <field w="22mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <field w="62mm" h="9mm" access="protected" presence="hidden">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <subform w="202mm" minH="224.19mm" layout="lr-tb">
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb" y="3mm">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb" y="3mm">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6.036mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+              </subform>
+              <occur min="0" max="-1"/>
+            </subform>
+          </subform>
+          <subform w="202mm" minH="147.001mm" layout="lr-tb">
+            <draw h="6.773mm" w="108mm">
+              <ui>
+                <textEdit/>
+              </ui>
+              <margin topInset="1.27mm" bottomInset="1.27mm" leftInset="1mm" rightInset="1.27mm"/>
+            </draw>
+            <subform minH="237.19mm" layout="lr-tb" w="202mm">
+              <subform minH="8mm" layout="lr-tb" name="show">
+                <field w="60mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <subform minH="8mm" layout="lr-tb" name="hide">
+                <field w="22mm" h="8mm">
+                  <ui>
+                    <button highlight="outline"/>
+                  </ui>
+                  <margin bottomInset="0mm"/>
+                </field>
+                <margin topInset="3mm" bottomInset="2mm"/>
+                <keep intact="contentArea"/>
+              </subform>
+              <field w="62mm" h="9mm" access="protected" presence="hidden">
+                <ui>
+                  <textEdit>
+                  </textEdit>
+                </ui>
+                <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+              </field>
+              <subform w="202mm" minH="224.19mm" layout="lr-tb">
+                <subform w="127mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="51mm" minH="9mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="26mm" minH="9mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="0mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="127mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="51mm" minH="9mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="76mm" minH="9mm" access="protected">
+                    <ui>
+                      <dateTimeEdit>
+                      </dateTimeEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="0mm" rightInset="1mm"/>
+                    <format>
+                      <picture
+                          >date{DD/MM/YYYY}</picture>
+                    </format>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="127mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="51mm" minH="9mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="76mm" minH="9mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="0mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" h="17.439mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="17.578mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="10mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="20.35mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="17.578mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="15mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="11.651mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="11.651mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="7mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="17.578mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="17.578mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="10mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                </subform>
+                <subform w="202mm" minH="15.694mm" layout="lr-tb">
+                  <draw w="127mm" minH="11.192mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="2mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="25mm" minH="11mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="6mm" bottomInset="1mm" leftInset="3mm" rightInset="1mm"/>
+                  </field>
+                  <keep intact="contentArea"/>
+                  <margin bottomInset="2mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="9.537mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="9.537mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+                <subform w="202mm" minH="34.081mm" layout="lr-tb">
+                  <draw w="7.95275in" h="6.875mm">
+                    <ui>
+                      <textEdit/>
+                    </ui>
+                    <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="1mm" rightInset="0.5mm"/>
+                  </draw>
+                  <field w="202mm" minH="28mm" access="protected">
+                    <ui>
+                      <textEdit multiLine="1">
+                      </textEdit>
+                    </ui>
+                    <margin topInset="1mm" bottomInset="1mm" leftInset="1mm" rightInset="1mm"/>
+                  </field>
+                  <margin topInset="1mm" bottomInset="1mm"/>
+                </subform>
+              </subform>
+              <occur min="0" max="-1"/>
+            </subform>
+          </subform>
+          <subform w="22mm" minH="8mm" layout="lr-tb" name="save" y="3mm">
+            <field w="22mm" h="8mm">
+              <ui>
+                <button highlight="outline"/>
+              </ui>
+              <margin bottomInset="0mm"/>
+            </field>
+            <margin topInset="3mm" bottomInset="2mm"/>
+          </subform>
+        </subform>
+        <overflow leader="Bloog"/>
+      </subform>
+      <subform layout="tb" w="203mm">
+        <margin bottomInset="0mm" leftInset="0mm" topInset="0mm"/>
+        <variables>
+        </variables>
+        <subform layout="lr-tb" minH="8.802mm" w="203mm">
+          <keep intact="contentArea"/>
+          <margin leftInset="0mm"/>
+          <draw h="0.238889in" name="tbd" w="203mm">
+            <margin bottomInset="1mm" leftInset="2mm" rightInset="1mm" topInset="1mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <draw h="8mm" w="203mm">
+            <margin bottomInset="0.5mm" leftInset="2mm" rightInset="0.5mm" topInset="2mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <subform layout="lr-tb" name="fieldCaption" w="50mm" presence="hidden">
+            <keep intact="contentArea"/>
+            <margin leftInset="2mm"/>
+            <draw h="8.802mm" name="Normal" w="48mm">
+              <margin bottomInset="0.5mm" leftInset="0mm" rightInset="0.5mm" topInset="0.5mm"/>
+              <ui>
+                <textEdit/>
+              </ui>
+            </draw>
+            <draw h="8.802mm" name="Star" w="48mm">
+              <margin bottomInset="0.5mm" leftInset="0mm" rightInset="0.5mm" topInset="0.5mm"/>
+              <ui>
+                <textEdit/>
+              </ui>
+            </draw>
+          </subform>
+          <field access="readOnly" h="9mm" w="62mm">
+            <margin bottomInset="1mm" leftInset="1mm" rightInset="1mm" topInset="1mm"/>
+            <ui>
+              <textEdit>
+              </textEdit>
+            </ui>
+          </field>
+          <field h="6mm" name="Button1" w="28.575mm">
+            <ui>
+              <button highlight="inverted"/>
+            </ui>
+          </field>
+          <field h="6mm" name="Button2" w="28.575mm">
+            <ui>
+              <button highlight="inverted"/>
+            </ui>
+          </field>
+          <field h="6mm" name="verifySig" w="28.575mm">
+            <ui>
+              <button highlight="inverted"/>
+            </ui>
+          </field>
+          <field h="0.1mm" name="concat" w="0.1mm">
+            <calculate>
+              <script contentType="application/x-javascript">
+	        fieldCaption.Star.presence = "hidden";
+	        fieldCaption.Normal.presence = "visible";
+	        this.rawValue = "Sig Not Required";
+              </script>
+            </calculate>
+            <margin bottomInset="0mm" leftInset="0mm" rightInset="0mm" topInset="0mm"/>
+            <validate>
+              <message>
+                <text name="scriptTest"/>
+              </message>
+            </validate>
+          </field>
+        </subform>
+        <subform layout="lr-tb" w="203mm" presence="hidden">
+          <keep intact="contentArea"/>
+          <margin bottomInset="1mm"/>
+          <draw h="6.269mm" w="7.992125in">
+            <margin bottomInset="0mm" leftInset="2mm" rightInset="0.5mm" topInset="2mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <draw h="5mm" name="Text1" w="139.483mm">
+            <margin bottomInset="0mm" leftInset="2mm" rightInset="0mm" topInset="0.5mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <field access="readOnly" h="0.217639in" name="TextField1" w="0.604861in">
+            <margin bottomInset="0mm" leftInset="0mm" rightInset="0mm" topInset="0mm"/>
+            <ui>
+              <textEdit>
+              </textEdit>
+            </ui>
+            <validate>
+              <message>
+                <text name="scriptTest"/>
+              </message>
+            </validate>
+          </field>
+          <draw h="5mm" name="Text1" w="0.786167in">
+            <margin bottomInset="0mm" leftInset="0mm" rightInset="0.5mm" topInset="0.5mm"/>
+            <ui>
+              <textEdit/>
+            </ui>
+          </draw>
+          <field h="9mm" name="url" w="62mm">
+            <margin bottomInset="1mm" leftInset="1mm" rightInset="1mm" topInset="1mm"/>
+            <ui>
+              <textEdit>
+              </textEdit>
+            </ui>
+          </field>
+        </subform>
+      </subform>
+    </subform>
+    <proto>
+      <subform w="202.001mm" name="Bloog" x="0.003mm" y="0.008mm">
+        <occur max="-1"/>
+        <draw w="202mm" h="6mm" x="0.001mm" y="0.001mm">
+          <ui>
+            <textEdit/>
+          </ui>
+          <margin topInset="0.5mm" bottomInset="0.5mm" leftInset="0.5mm" rightInset="0.5mm"/>
+        </draw>
+      </subform>
+    </proto>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000043587 00000 n 
+0000047095 00000 n 
+0000047157 00000 n 
+0000047220 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+47297
+%%EOF
diff --git a/testing/resources/bug_459580.in b/testing/resources/bug_459580.in
index f20c141..3c4363f 100644
--- a/testing/resources/bug_459580.in
+++ b/testing/resources/bug_459580.in
@@ -1,47 +1,47 @@
-{{header}}

-{{object 1 0}} <<

-  /Type /Catalog

-  /Pages 2 0 R

->>

-{{object 2 0}} <<

-  /Type /Pages

-  /MediaBox [ 0 0 600 600 ]

-  /Count 1

-  /Kids [ 3 0 R ]

->>

-endobj

-{{object 3 0}} <<

-  /Type /Page

-  /Parent 2 0 R

-  /Resources <<

-    /Font <<

-      /F1 4 0 R

-    >>

-  >>

-  /Contents 6 0 R

->>

-endobj

-{{object 4 0}} <<

-  /Type /Font

-  /Subtype /Type1

-  /BaseFont /Times-Roman

->>

-endobj

-

-{{object 6 0}} <<

->>

-stream

-BT

-/F1 12 Tf

-200 500 Td

-(Hello, world! This is a test pdf for bug 459580.) Tj

-ET

-endstream

-endobj

-{{xref}}

-trailer <<

-  /$$ze 6

-  /Root 1 0 R

->>

-{{startxref}}

-%%EOF

+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 600 600 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+
+{{object 6 0}} <<
+>>
+stream
+BT
+/F1 12 Tf
+200 500 Td
+(Hello, world! This is a test pdf for bug 459580.) Tj
+ET
+endstream
+endobj
+{{xref}}
+trailer <<
+  /$$ze 6
+  /Root 1 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_459580.pdf b/testing/resources/bug_459580.pdf
index af33efc..66a5641 100644
--- a/testing/resources/bug_459580.pdf
+++ b/testing/resources/bug_459580.pdf
@@ -1,57 +1,57 @@
-%PDF-1.7

-% ò¤ô

-1 0 obj <<

-  /Type /Catalog

-  /Pages 2 0 R

->>

-2 0 obj <<

-  /Type /Pages

-  /MediaBox [ 0 0 600 600 ]

-  /Count 1

-  /Kids [ 3 0 R ]

->>

-endobj

-3 0 obj <<

-  /Type /Page

-  /Parent 2 0 R

-  /Resources <<

-    /Font <<

-      /F1 4 0 R

-    >>

-  >>

-  /Contents 6 0 R

->>

-endobj

-4 0 obj <<

-  /Type /Font

-  /Subtype /Type1

-  /BaseFont /Times-Roman

->>

-endobj

-

-6 0 obj <<

->>

-stream

-BT

-/F1 12 Tf

-200 500 Td

-(Hello, world! This is a test pdf for bug 459580.) Tj

-ET

-endstream

-endobj

-xref

-0 7

-0000000000 65535 f 

-0000000015 00000 n 

-0000000061 00000 n 

-0000000154 00000 n 

-0000000374 00000 n 

-0000000000 65535 f 

-0000000450 00000 n 

-trailer <<

-  /$$ze 6

-  /Root 1 0 R

->>

-startxref

-963

-%%EOF

+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 600 600 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+
+6 0 obj <<
+>>
+stream
+BT
+/F1 12 Tf
+200 500 Td
+(Hello, world! This is a test pdf for bug 459580.) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000061 00000 n 
+0000000154 00000 n 
+0000000280 00000 n 
+0000000000 65535 f 
+0000000359 00000 n 
+trailer <<
+  /$$ze 6
+  /Root 1 0 R
+>>
+startxref
+478
+%%EOF
diff --git a/testing/resources/bug_507316.in b/testing/resources/bug_507316.in
index 87dd5c3..1da3743 100644
--- a/testing/resources/bug_507316.in
+++ b/testing/resources/bug_507316.in
@@ -118,9 +118,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Size 16
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/bug_507316.pdf b/testing/resources/bug_507316.pdf
index ecc205c..5947b3c 100644
--- a/testing/resources/bug_507316.pdf
+++ b/testing/resources/bug_507316.pdf
@@ -137,8 +137,8 @@
 0000001365 00000 n 
 0000001433 00000 n 
 trailer <<
-  /Size 16
   /Root 1 0 R
+  /Size 16
 >>
 startxref
 1534
diff --git a/testing/resources/bug_544880.in b/testing/resources/bug_544880.in
index 926a491..b004dd9 100644
--- a/testing/resources/bug_544880.in
+++ b/testing/resources/bug_544880.in
@@ -11,9 +11,6 @@
 >>
 endobj
 {{xref}}
-trailer <<
-  /Size 3
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/bug_544880.pdf b/testing/resources/bug_544880.pdf
index 4a8e5b9..687ea32 100644
--- a/testing/resources/bug_544880.pdf
+++ b/testing/resources/bug_544880.pdf
@@ -17,8 +17,8 @@
 0000000015 00000 n 
 0000000068 00000 n 
 trailer <<
-  /Size 3
   /Root 1 0 R
+  /Size 3
 >>
 startxref
 131
diff --git a/testing/resources/bug_547706.in b/testing/resources/bug_547706.in
index 4c31f19..7a993a8 100644
--- a/testing/resources/bug_547706.in
+++ b/testing/resources/bug_547706.in
@@ -35,9 +35,6 @@
 endobj
 
 {{xref}}
-trailer <<
-  /Root 1 0 R
-  /Size 9
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/bug_551460.in b/testing/resources/bug_551460.in
deleted file mode 100644
index 5ed2f3f..0000000
--- a/testing/resources/bug_551460.in
+++ /dev/null
@@ -1,75 +0,0 @@
-{{header}}
-
-{{object 1 0}}
-<<
-  /Pages 2 0 R
->>
-endobj
-
-{{object 2 0}}
-<<
-  /Kids [ 3 0 R ]
->>
-endobj
-
-{{object 3 0}}
-<<
-  /Contents 6 0 R
-  /Resources 5 0 R
->>
-endobj
-
-{{object 6 0}}
-<<>>
-stream
-0.0 G
-0.0 1.0 0.0 rg
-25 175 175 -150 re
-/Sh sh
-endstream
-endobj
-
-{{object 5 0}}
-<<
-  /Shading <<
-    /Sh 7 0 R
-  >>
->>
-endobj
-
-{{object 7 0}}
-<<
-  /ShadingType 1
-  /ColorSpace /DeviceCMYK
-  /Coords [0.0 0.0 1.0 1.0]
-  /Function 9 0 R
-  /Extend [true true]
->>
-
-{{object 9 0}}
-<<
-  /FunctionType 3
-  /Domain [0.0 1.0 0.0 1.0]
-  /Functions [10 0 R]
-  /Bounds [2.0]
-  /Encode [0.0 1.0]
->>
-endobj
-
-{{object 10 0}}
-<<
-  /FunctionType 2
-  /Domain [0.0 1.0 0.0 1.0]
-  /C0 [0.0 0.0]
-  /C1 [1.0 1.0]
-  /N 1
->>
-endobj
-
-{{xref}}
-trailer <<
-  /Root 1 0 R
-  /Size 11
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/bug_554151.in b/testing/resources/bug_554151.in
deleted file mode 100644
index 3087159..0000000
--- a/testing/resources/bug_554151.in
+++ /dev/null
@@ -1,59 +0,0 @@
-{{header}}
-
-{{object 1 0}}
-<<
-  /Pages 2 0 R
->>
-endobj
-
-{{object 2 0}}
-<<
-  /Kids [ 3 0 R ]
->>
-endobj
-
-{{object 3 0}}
-<<
-  /Contents 6 0 R
-  /Resources 5 0 R
->>
-endobj
-
-{{object 6 0}}
-<<>>
-stream
-612 0.0 0.0 104000 0 0 cm
-/XBad Do
-endstream
-endobj
-
-{{object 5 0}}
-<<
-  /XObject <<
-    /XBad 7 0 R
-  >>
->>
-endobj
-
-{{object 7 0}}
-<<
-  /Type /XObject
-  /Subtype /Image
-  /Width 612
-  /Height 104000
-  /ColorSpace /DeviceRGB
-  /BitsPerComponent 4
-  /Length 0
-  /Decode [1.0]
->>
-stream
-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-endobj
-
-{{xref}}
-trailer <<
-  /Root 1 0 R
-  /Size 8
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/bug_554151.pdf b/testing/resources/bug_554151.pdf
deleted file mode 100644
index fa770a7..0000000
--- a/testing/resources/bug_554151.pdf
+++ /dev/null
@@ -1,70 +0,0 @@
-%PDF-1.7
-% ò¤ô
-
-1 0 obj
-<<
-  /Pages 2 0 R
->>
-endobj
-
-2 0 obj
-<<
-  /Kids [ 3 0 R ]
->>
-endobj
-
-3 0 obj
-<<
-  /Contents 6 0 R
-  /Resources 5 0 R
->>
-endobj
-
-6 0 obj
-<<>>
-stream
-612 0.0 0.0 104000 0 0 cm
-/XBad Do
-endstream
-endobj
-
-5 0 obj
-<<
-  /XObject <<
-    /XBad 7 0 R
-  >>
->>
-endobj
-
-7 0 obj
-<<
-  /Type /XObject
-  /Subtype /Image
-  /Width 612
-  /Height 104000
-  /ColorSpace /DeviceRGB
-  /BitsPerComponent 4
-  /Length 0
-  /Decode [1.0]
->>
-stream
-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-endobj
-
-xref
-0 8
-0000000000 65535 f 
-0000000016 00000 n 
-0000000053 00000 n 
-0000000093 00000 n 
-0000000000 65535 f 
-0000000225 00000 n 
-0000000152 00000 n 
-0000000282 00000 n 
-trailer <<
-  /Root 1 0 R
-  /Size 8
->>
-startxref
-1370
-%%EOF
diff --git a/testing/resources/bug_555784.in b/testing/resources/bug_555784.in
index c6a1f43..95018f8 100644
--- a/testing/resources/bug_555784.in
+++ b/testing/resources/bug_555784.in
@@ -66,9 +66,6 @@
 endobj
 
 {{xref}}
-trailer <<
-  /Root 1 0 R
-  /Size 11
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/bug_557223.in b/testing/resources/bug_557223.in
deleted file mode 100644
index 78f590d..0000000
--- a/testing/resources/bug_557223.in
+++ /dev/null
Binary files differ
diff --git a/testing/resources/bug_557223.pdf b/testing/resources/bug_557223.pdf
deleted file mode 100644
index e80805a..0000000
--- a/testing/resources/bug_557223.pdf
+++ /dev/null
Binary files differ
diff --git a/testing/resources/bug_57.in b/testing/resources/bug_57.in
index 0825efd..207aae4 100644
--- a/testing/resources/bug_57.in
+++ b/testing/resources/bug_57.in
@@ -40,9 +40,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Size 6
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/bug_57.pdf b/testing/resources/bug_57.pdf
index 0c3f7df..66ec4ec 100644
--- a/testing/resources/bug_57.pdf
+++ b/testing/resources/bug_57.pdf
@@ -49,8 +49,8 @@
 0000000280 00000 n 
 0000000409 00000 n 
 trailer <<
-  /Size 6
   /Root 1 0 R
+  /Size 6
 >>
 startxref
 506
diff --git a/testing/resources/bug_572871.in b/testing/resources/bug_572871.in
index 20cb943..dc17591 100644
--- a/testing/resources/bug_572871.in
+++ b/testing/resources/bug_572871.in
@@ -88,9 +88,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Size 14
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/bug_572871.pdf b/testing/resources/bug_572871.pdf
index 1f26bc5..3a8b71e 100644
--- a/testing/resources/bug_572871.pdf
+++ b/testing/resources/bug_572871.pdf
@@ -105,8 +105,8 @@
 0000000981 00000 n 
 0000001049 00000 n 
 trailer <<
-  /Size 14
   /Root 1 0 R
+  /Size 14
 >>
 startxref
 1132
diff --git a/testing/resources/bug_642.in b/testing/resources/bug_642.in
new file mode 100644
index 0000000..929e5e3
--- /dev/null
+++ b/testing/resources/bug_642.in
@@ -0,0 +1,122 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F15 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+1 0 0 1 20 100 cm
+BT
+/F15 20 Tf
+[(ABCD)]TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /Name /F15
+  /FirstChar 65
+  /LastChar 68
+  /CharProcs <<
+    /a65 6 0 R
+    /a66 7 0 R
+    /a67 8 0 R
+    /a68 9 0 R
+  >>
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+  >>
+  /Encoding <<
+    /Differences [65 /a65 /a66 /a67 /a68 ]
+  >>
+  /FontBBox [-4 -21 114 70]
+  /FontMatrix [0.0156 0 0 0.0156 0 0]
+  /Widths [62.27 58.82 59.97 63.43]
+>>
+endobj
+{{object 6 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789c6dd0cd6a02311007f009011706e95e3d083b4fd0642df1e324580bdd
+83a0a71e4a41687b2cb4456fc2e6d1f6517c843d7a584c2729b85136107e
+cc844cfe643cba1f4d48d3036f33a3b1a68f1c7fd018aeb52ffdc1fb172e
+0a542f640caa67eea22a56b4fbdd7fa25aac1f2947b5a4d79cf41b164b02
+80298475cda18b7307c275204b1b919495a7378fb99b1f3dfd2c6698d59e
+411a334d4f1e9dc47c278d672b632a1932552242d8904958889000ce06ca
+961ec3d112e75c4bca70b43e40d632e07e7d8be63ba75bb63cbdb970bcc0
+d1c43fb20ebf9636fe49eb389673e76bf0a9c00dfe0123b76dbb
+endstream
+endobj
+{{object 7 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789c9590b10e823010868f3090dce223704f60416d6433414c6430d1c9c1
+38a9a3834667fa683c0a8fc0c840a885ab061217dab45faeedddfdfd6534
+8d6614d0dc2cb920b9a46b880f9401b5d384edc5e58e718ae2483240b135
+a728d21dbd9eef1b8a78bfa6104542a7908233a60969330a801635430338
+5a41969b10c02f3a4cca0e5ed5c1adfb709a01f458709edbf46bda46b6ad
+15b16249461cb0dc1f0032d57fe9719ecb559c814ed043a80e99453e16aa
+5f85f1f7631e8bf02b76b764aff3afe5ed6ed4e226c5037e0056029f50
+endstream
+endobj
+{{object 8 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cadd1b14ac4401006e03f040c4c615a0be1e6053489dcaa5b2d9c2798
+42d0ca42acd45250d1d6cc9bf82abe897904cb2bc47576b267219642e023
+93cdcceebfceeffa036ed9f1ce1e3bc7cef34d470f34f75a6c79bf9bbe5c
+dfd1a2a7e682e79e9a132d53d39ff2d3e3f32d358bb323d6f7255f76dc5e
+51bf64a088520850c7b15486b852b4f65209ca4fd4b5a0fe40f92a988d28
+de05415745c1005d2cf852821413ba1e9849b552ea35fa3750add1263f94
+12326fc690287e23464cd8e07f67c8fc353d644623ef7a0276a24c053b6d
+660396c4262c976d584a5bb0cc0e6109de23e5097d52ba3a25652d761bc1
+76135307bd95d43d55013aeee99cbe019a657396
+endstream
+endobj
+{{object 9 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789c3333d633315630500061530b055373851443ae422e531320df00c405
+4924e772397972e9872b989a70e97b0045b9f43d7d154a8a4a53b9f49d02
+9c150cb9f45d14a20d150c62b93c5d14fe03c101060610f50342fd636060
+fcdf0044402e0383fd0330c5ff014c31ff00538c7fc014c33f08558f42d9
+3740a803604a1e4a3d0053fc50ea03168afd071eea0f0d283cf6a13910c5
+f150af403d06f36d037210fc430e256898414310189ea0a0052110400979
+603c70b97a72057201003d309464
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_642.pdf b/testing/resources/bug_642.pdf
new file mode 100644
index 0000000..88c0f4b
--- /dev/null
+++ b/testing/resources/bug_642.pdf
@@ -0,0 +1,138 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F15 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 200 200]
+>>
+endobj
+4 0 obj <<
+  /Length 46
+>>
+stream
+1 0 0 1 20 100 cm
+BT
+/F15 20 Tf
+[(ABCD)]TJ
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type3
+  /Name /F15
+  /FirstChar 65
+  /LastChar 68
+  /CharProcs <<
+    /a65 6 0 R
+    /a66 7 0 R
+    /a67 8 0 R
+    /a68 9 0 R
+  >>
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+  >>
+  /Encoding <<
+    /Differences [65 /a65 /a66 /a67 /a68 ]
+  >>
+  /FontBBox [-4 -21 114 70]
+  /FontMatrix [0.0156 0 0 0.0156 0 0]
+  /Widths [62.27 58.82 59.97 63.43]
+>>
+endobj
+6 0 obj <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 480
+>>
+stream
+789c6dd0cd6a02311007f009011706e95e3d083b4fd0642df1e324580bdd
+83a0a71e4a41687b2cb4456fc2e6d1f6517c843d7a584c2729b85136107e
+cc844cfe643cba1f4d48d3036f33a3b1a68f1c7fd018aeb52ffdc1fb172e
+0a542f640caa67eea22a56b4fbdd7fa25aac1f2947b5a4d79cf41b164b02
+80298475cda18b7307c275204b1b919495a7378fb99b1f3dfd2c6698d59e
+411a334d4f1e9dc47c278d672b632a1932552242d8904958889000ce06ca
+961ec3d112e75c4bca70b43e40d632e07e7d8be63ba75bb63cbdb970bcc0
+d1c43fb20ebf9636fe49eb389673e76bf0a9c00dfe0123b76dbb
+endstream
+endobj
+7 0 obj <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 425
+>>
+stream
+789c9590b10e823010868f3090dce223704f60416d6433414c6430d1c9c1
+38a9a3834667fa683c0a8fc0c840a885ab061217dab45faeedddfdfd6534
+8d6614d0dc2cb920b9a46b880f9401b5d384edc5e58e718ae2483240b135
+a728d21dbd9eef1b8a78bfa6104542a7908233a60969330a801635430338
+5a41969b10c02f3a4cca0e5ed5c1adfb709a01f458709edbf46bda46b6ad
+15b16249461cb0dc1f0032d57fe9719ecb559c814ed043a80e99453e16aa
+5f85f1f7631e8bf02b76b764aff3afe5ed6ed4e226c5037e0056029f50
+endstream
+endobj
+8 0 obj <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 529
+>>
+stream
+789cadd1b14ac4401006e03f040c4c615a0be1e6053489dcaa5b2d9c2798
+42d0ca42acd45250d1d6cc9bf82abe897904cb2bc47576b267219642e023
+93cdcceebfceeffa036ed9f1ce1e3bc7cef34d470f34f75a6c79bf9bbe5c
+dfd1a2a7e682e79e9a132d53d39ff2d3e3f32d358bb323d6f7255f76dc5e
+51bf64a088520850c7b15486b852b4f65209ca4fd4b5a0fe40f92a988d28
+de05415745c1005d2cf852821413ba1e9849b552ea35fa3750add1263f94
+12326fc690287e23464cd8e07f67c8fc353d644623ef7a0276a24c053b6d
+660396c4262c976d584a5bb0cc0e6109de23e5097d52ba3a25652d761bc1
+76135307bd95d43d55013aeee99cbe019a657396
+endstream
+endobj
+9 0 obj <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 395
+>>
+stream
+789c3333d633315630500061530b055373851443ae422e531320df00c405
+4924e772397972e9872b989a70e97b0045b9f43d7d154a8a4a53b9f49d02
+9c150cb9f45d14a20d150c62b93c5d14fe03c101060610f50342fd636060
+fcdf0044402e0383fd0330c5ff014c31ff00538c7fc014c33f08558f42d9
+3740a803604a1e4a3d0053fc50ea03168afd071eea0f0d283cf6a13910c5
+f150af403d06f36d037210fc430e256898414310189ea0a0052110400979
+603c70b97a72057201003d309464
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000310 00000 n 
+0000000407 00000 n 
+0000000799 00000 n 
+0000001372 00000 n 
+0000001890 00000 n 
+0000002512 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+3000
+%%EOF
diff --git a/testing/resources/bug_713197.in b/testing/resources/bug_713197.in
index 49eaee2..e84591c 100644
--- a/testing/resources/bug_713197.in
+++ b/testing/resources/bug_713197.in
@@ -1,27 +1,24 @@
-{{header}}

-{{object 1 0}} <<

-  /Type /Catalog

-  /Pages 2 0 R

->>

-endobj

-{{object 2 0}}<<

-  /Type /Pages

-  /MediaBox [0 0 600 800]

-  /Count 1

-  /Kids [ 3 0 R ]

->>

-endobj

-{{object 3 0}}<<

-  /Type /Page

-  /Parent 2 0 R

-  /Resources <<>>

-  /Rotate -90

->>

-endobj

-{{xref}}

-trailer <<

-  /Size 4

-  /Root 1 0 R

->>

-{{startxref}}

-%%EOF

+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}}<<
+  /Type /Pages
+  /MediaBox [0 0 600 800]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}}<<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<>>
+  /Rotate -90
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_713197.pdf b/testing/resources/bug_713197.pdf
index 571cde9..51f47d9 100644
--- a/testing/resources/bug_713197.pdf
+++ b/testing/resources/bug_713197.pdf
@@ -1,34 +1,34 @@
 %PDF-1.7
-% ò¤ô

-1 0 obj <<

-  /Type /Catalog

-  /Pages 2 0 R

->>

-endobj

-2 0 obj<<

-  /Type /Pages

-  /MediaBox [0 0 600 800]

-  /Count 1

-  /Kids [ 3 0 R ]

->>

-endobj

-3 0 obj<<

-  /Type /Page

-  /Parent 2 0 R

-  /Resources <<>>

-  /Rotate -90

->>

-endobj

+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj<<
+  /Type /Pages
+  /MediaBox [0 0 600 800]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj<<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<>>
+  /Rotate -90
+>>
+endobj
 xref
 0 4
 0000000000 65535 f 
-0000000016 00000 n 
-0000000074 00000 n 
-0000000171 00000 n 
-trailer <<

-  /Size 4

-  /Root 1 0 R

->>

+0000000015 00000 n 
+0000000068 00000 n 
+0000000158 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 4
+>>
 startxref
-260

-%%EOF

+240
+%%EOF
diff --git a/testing/resources/bug_773229.pdf b/testing/resources/bug_773229.pdf
new file mode 100644
index 0000000..5f34a1a
--- /dev/null
+++ b/testing/resources/bug_773229.pdf
@@ -0,0 +1,78 @@
+%PDF-1.7

+1 0 obj

+<<

+    /Type /Catalog

+    /Pages 2 0 R

+    /AcroForm 4 0 R

+>>

+endobj

+2 0 obj

+<<

+    /Type /Pages

+    /Count 1

+    /Kids [3 0 R]

+>>

+endobj

+3 0 obj

+<<

+    /Type /Page

+    /Parent 2 0 R

+>>

+endobj

+4 0 obj

+<<

+    /XFA [

+        (xdp:xdp) 5 0 R

+        (form) 6 0 R

+        (</xdp:xdp>) 7 0 R

+    ]

+>>

+endobj

+5 0 obj

+<< >>

+stream

+<?xml version="1.0" encoding="UTF-8"?>

+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">

+endstream

+endobj

+6 0 obj

+<< >>

+stream

+<config></config>

+<?xml version="1.0"?>

+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">

+    <subform>

+        <field name="field1">

+            <event activity="docReady">

+                <script contentType="application/x-javascript">

+                    app.alert(app.runtimeHighlight);

+                </script>

+            </event>

+        </field>

+    </subform>

+</template>

+endstream

+endobj

+7 0 obj

+<< >>

+stream

+</xdp:xdp>

+endstream

+endobj

+xref

+0 8

+0000000000 65535 f

+0000000010 00000 n

+0000000094 00000 n

+0000000170 00000 n

+0000000231 00000 n

+0000000350 00000 n

+0000000481 00000 n

+0000002637 00000 n

+trailer

+<<

+    /Root 1 0 R

+>>

+startxref

+2692

+%%EOF
\ No newline at end of file
diff --git a/testing/resources/bug_821454.in b/testing/resources/bug_821454.in
new file mode 100644
index 0000000..168c983
--- /dev/null
+++ b/testing/resources/bug_821454.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}}
+<<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names 14 0 R
+  /AcroForm << /Fields [ 8 0 R 9 0 R ] >>
+>>
+endobj
+{{object 2 0}}
+<<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}}
+<<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /MediaBox [ 0 0 300 600 ]
+  /Annots [ 8 0 R 9 0 R ]
+>>
+endobj
+{{object 6 0}}
+<<
+{{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 8 0}}
+<<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [ 100 350 200 380 ]
+  /Dest (target10)
+>>
+endobj
+{{object 9 0}}
+<<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [ 100 400 200 430 ]
+  /Dest (target100)
+>>
+endobj
+{{object 14 0}}
+<<
+  /Dests 15 0 R
+>>
+endobj
+{{object 15 0}}
+<<
+  /Names [
+    (target10) 16 0 R
+    (target100) 17 0 R
+  ]
+>>
+endobj
+{{object 16 0}}
+<<
+  /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
+{{object 17 0}}
+<<
+  /D [3 0 R /XYZ 150 250 0]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_821454.pdf b/testing/resources/bug_821454.pdf
new file mode 100644
index 0000000..d6229a5
--- /dev/null
+++ b/testing/resources/bug_821454.pdf
@@ -0,0 +1,96 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj
+<<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names 14 0 R
+  /AcroForm << /Fields [ 8 0 R 9 0 R ] >>
+>>
+endobj
+2 0 obj
+<<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj
+<<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /MediaBox [ 0 0 300 600 ]
+  /Annots [ 8 0 R 9 0 R ]
+>>
+endobj
+6 0 obj
+<<
+/Length 0
+>>
+stream
+endstream
+endobj
+8 0 obj
+<<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [ 100 350 200 380 ]
+  /Dest (target10)
+>>
+endobj
+9 0 obj
+<<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [ 100 400 200 430 ]
+  /Dest (target100)
+>>
+endobj
+14 0 obj
+<<
+  /Dests 15 0 R
+>>
+endobj
+15 0 obj
+<<
+  /Names [
+    (target10) 16 0 R
+    (target100) 17 0 R
+  ]
+>>
+endobj
+16 0 obj
+<<
+  /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
+17 0 obj
+<<
+  /D [3 0 R /XYZ 150 250 0]
+>>
+endobj
+xref
+0 18
+0000000000 65535 f 
+0000000015 00000 n 
+0000000126 00000 n 
+0000000191 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000314 00000 n 
+0000000000 65535 f 
+0000000362 00000 n 
+0000000462 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000563 00000 n 
+0000000601 00000 n 
+0000000683 00000 n 
+0000000733 00000 n 
+trailer<< /Root 1 0 R /Size 18 >>
+startxref
+783
+%%EOF
diff --git a/testing/resources/bug_828049.pdf b/testing/resources/bug_828049.pdf
new file mode 100644
index 0000000..d8942bb
--- /dev/null
+++ b/testing/resources/bug_828049.pdf
@@ -0,0 +1,6 @@
+%PDF
+1 0 obj<</Pages<</Contents <<
+>>stream
+0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+endobj
+trailer<</Root 1 0 R>>
\ No newline at end of file
diff --git a/testing/resources/bug_861842.in b/testing/resources/bug_861842.in
new file mode 100644
index 0000000..244da49
--- /dev/null
+++ b/testing/resources/bug_861842.in
Binary files differ
diff --git a/testing/resources/bug_861842.pdf b/testing/resources/bug_861842.pdf
new file mode 100644
index 0000000..51aaad5
--- /dev/null
+++ b/testing/resources/bug_861842.pdf
Binary files differ
diff --git a/testing/resources/bug_890322.in b/testing/resources/bug_890322.in
new file mode 100644
index 0000000..0c68775
--- /dev/null
+++ b/testing/resources/bug_890322.in
@@ -0,0 +1,57 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm << /Fields [ 4 0 R ] >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages /Count 1 /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /Contents 6 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /F 4
+  /FT /Btn
+  /Rect [ 75 80 125 120 ]
+  /T (PushButton)
+  /Ff 65536
+  /H
+  /P
+  /AP << /N 5 0 R >>
+  /MK << /BG [ 1.0 0.0 0.0 ] >>
+>>
+endobj
+{{object 5 0}} <<
+  /Subtype /Form
+  /BBox [ 0 40 50 00 ]
+  {{streamlen}}
+>>
+stream
+q
+0 0 1 rg
+0 0 50 40 re B*
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_890322.pdf b/testing/resources/bug_890322.pdf
new file mode 100644
index 0000000..2900c1b
--- /dev/null
+++ b/testing/resources/bug_890322.pdf
@@ -0,0 +1,70 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm << /Fields [ 4 0 R ] >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages /Count 1 /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /Contents 6 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /F 4
+  /FT /Btn
+  /Rect [ 75 80 125 120 ]
+  /T (PushButton)
+  /Ff 65536
+  /H
+  /P
+  /AP << /N 5 0 R >>
+  /MK << /BG [ 1.0 0.0 0.0 ] >>
+>>
+endobj
+5 0 obj <<
+  /Subtype /Form
+  /BBox [ 0 40 50 00 ]
+  /Length 29
+>>
+stream
+q
+0 0 1 rg
+0 0 50 40 re B*
+Q
+endstream
+endobj
+6 0 obj <<
+  /Length 4
+>>
+stream
+q
+Q
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000104 00000 n 
+0000000165 00000 n 
+0000000282 00000 n 
+0000000474 00000 n 
+0000000594 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+648
+%%EOF
diff --git a/testing/resources/bug_896366.in b/testing/resources/bug_896366.in
new file mode 100644
index 0000000..86bd6fe
--- /dev/null
+++ b/testing/resources/bug_896366.in
@@ -0,0 +1,60 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+  /Contents [6 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /F 4
+  /FT /Btn
+  /Rect [ 75 80 125 120 ]
+  /T (PushButton)
+  /Ff 65536
+  /H
+  /P
+  /AP << /N 5 0 R >>
+  /MK << /BG [ 1.0 0.0 0.0 ] >>
+>>
+endobj
+{{object 5 0}} <<
+  /Subtype /Form
+  /BBox [ 0 40 50 00 ]
+  {{streamlen}}
+>>
+stream
+q
+0 0 1 rg
+0 0 50 40 re B*
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+1 0 0 -1 0 792 cm
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_896366.pdf b/testing/resources/bug_896366.pdf
new file mode 100644
index 0000000..0310fb9
--- /dev/null
+++ b/testing/resources/bug_896366.pdf
@@ -0,0 +1,73 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [4 0 R]
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+  /Contents [6 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /F 4
+  /FT /Btn
+  /Rect [ 75 80 125 120 ]
+  /T (PushButton)
+  /Ff 65536
+  /H
+  /P
+  /AP << /N 5 0 R >>
+  /MK << /BG [ 1.0 0.0 0.0 ] >>
+>>
+endobj
+5 0 obj <<
+  /Subtype /Form
+  /BBox [ 0 40 50 00 ]
+  /Length 29
+>>
+stream
+q
+0 0 1 rg
+0 0 50 40 re B*
+Q
+endstream
+endobj
+6 0 obj <<
+  /Length 18
+>>
+stream
+1 0 0 -1 0 792 cm
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000108 00000 n 
+0000000171 00000 n 
+0000000286 00000 n 
+0000000478 00000 n 
+0000000598 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+667
+%%EOF
diff --git a/testing/resources/bug_900552.in b/testing/resources/bug_900552.in
new file mode 100644
index 0000000..08763f8
--- /dev/null
+++ b/testing/resources/bug_900552.in
@@ -0,0 +1,76 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 8 0 R
+>>
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources 5 0 R
+  /Contents 4 0 R
+  /Annots [6 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+  (OC)
+  /V
+  BDC
+  BT
+   /F1 20 Tf
+   100 100 Td
+   (Test) Tj
+  ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Properties 6 0 R
+  /Font <<F1 7 0 R>>
+>>
+endobj
+{{object 6 0}} <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /F 4
+  /Rect [200 200 400 400]
+  /V <</A (b)>>
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 9 0 R
+>>
+endobj
+{{object 9 0}} <<
+  {{streamlen}}
+>>
+stream
+function run() {
+  this.getField('txt1').value = 'a';
+}
+app.setTimeOut('run()', 1000)
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_900552.pdf b/testing/resources/bug_900552.pdf
new file mode 100644
index 0000000..573e8ae
--- /dev/null
+++ b/testing/resources/bug_900552.pdf
@@ -0,0 +1,92 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 8 0 R
+>>
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources 5 0 R
+  /Contents 4 0 R
+  /Annots [6 0 R]
+>>
+endobj
+4 0 obj <<
+  /Length 68
+>>
+stream
+  (OC)
+  /V
+  BDC
+  BT
+   /F1 20 Tf
+   100 100 Td
+   (Test) Tj
+  ET
+endstream
+endobj
+5 0 obj <<
+  /Properties 6 0 R
+  /Font <<F1 7 0 R>>
+>>
+endobj
+6 0 obj <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /F 4
+  /Rect [200 200 400 400]
+  /V <</A (b)>>
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 9 0 R
+>>
+endobj
+9 0 obj <<
+  /Length 86
+>>
+stream
+function run() {
+  this.getField('txt1').value = 'a';
+}
+app.setTimeOut('run()', 1000)
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000081 00000 n 
+0000000144 00000 n 
+0000000276 00000 n 
+0000000395 00000 n 
+0000000457 00000 n 
+0000000583 00000 n 
+0000000659 00000 n 
+0000000725 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+862
+%%EOF
diff --git a/testing/resources/bug_901654.in b/testing/resources/bug_901654.in
new file mode 100644
index 0000000..703e354
--- /dev/null
+++ b/testing/resources/bug_901654.in
@@ -0,0 +1,73 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 8 0 R
+>>
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources 5 0 R
+  /Contents 4 0 R
+  /Annots [6 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  >>
+stream
+  /V
+  scn
+  BT
+   /F1 20 Tf
+   50 50 Td
+   (Test) Tj
+  ET
+endstream
+endobj
+{{object 5 0}} <<
+   /Pattern 6 0 R
+   /Font <<F1 7 0 R>>
+  >>
+endobj
+{{object 6 0}} <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /F 4
+  /Rect [200 200 400 400]
+  /V <</PatternType 2 /Shading <</Test (a)>> >>
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 9 0 R
+>>
+endobj
+{{object 9 0}} <<>>
+stream
+function run()
+{
+  this.getField('txt1').value='a';
+}
+app.setTimeOut('run()',3000);
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_901654.pdf b/testing/resources/bug_901654.pdf
new file mode 100644
index 0000000..a98fe8b
--- /dev/null
+++ b/testing/resources/bug_901654.pdf
@@ -0,0 +1,89 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 8 0 R
+>>
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources 5 0 R
+  /Contents 4 0 R
+  /Annots [6 0 R]
+>>
+endobj
+4 0 obj <<
+  >>
+stream
+  /V
+  scn
+  BT
+   /F1 20 Tf
+   50 50 Td
+   (Test) Tj
+  ET
+endstream
+endobj
+5 0 obj <<
+   /Pattern 6 0 R
+   /Font <<F1 7 0 R>>
+  >>
+endobj
+6 0 obj <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /F 4
+  /Rect [200 200 400 400]
+  /V <</PatternType 2 /Shading <</Test (a)>> >>
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 9 0 R
+>>
+endobj
+9 0 obj <<>>
+stream
+function run()
+{
+  this.getField('txt1').value='a';
+}
+app.setTimeOut('run()',3000);
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000081 00000 n 
+0000000144 00000 n 
+0000000276 00000 n 
+0000000375 00000 n 
+0000000438 00000 n 
+0000000596 00000 n 
+0000000672 00000 n 
+0000000738 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+859
+%%EOF
diff --git a/testing/resources/bug_901654_2.in b/testing/resources/bug_901654_2.in
new file mode 100644
index 0000000..a1fe2e1
--- /dev/null
+++ b/testing/resources/bug_901654_2.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 8 0 R
+>>
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources 5 0 R
+  /Contents 4 0 R
+  /Annots [6 0 R]
+>>
+endobj
+{{object 4 0}} <<
+>>
+stream
+  /NM
+  scn
+  BT
+   /F1 20 Tf
+   50 50 Td
+   (Test) Tj
+  ET
+endstream
+endobj
+{{object 5 0}} <<
+   /Pattern 6 0 R
+   /Font <<F1 7 0 R>>
+>>
+endobj
+{{object 6 0}} <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /NM <</PatternType 2 /Shading <</Test (a)>> >>
+  /F 4
+  /Rect [200 200 400 400]
+  /V (t)
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 9 0 R
+>>
+endobj
+{{object 9 0}} <<
+>>
+stream
+function run() {
+  a = this.getAnnot(0,'');
+  a.name = 'newname';
+}
+app.setTimeOut('run()',3000);
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_901654_2.pdf b/testing/resources/bug_901654_2.pdf
new file mode 100644
index 0000000..3e8bbfe
--- /dev/null
+++ b/testing/resources/bug_901654_2.pdf
@@ -0,0 +1,91 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 8 0 R
+>>
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources 5 0 R
+  /Contents 4 0 R
+  /Annots [6 0 R]
+>>
+endobj
+4 0 obj <<
+>>
+stream
+  /NM
+  scn
+  BT
+   /F1 20 Tf
+   50 50 Td
+   (Test) Tj
+  ET
+endstream
+endobj
+5 0 obj <<
+   /Pattern 6 0 R
+   /Font <<F1 7 0 R>>
+>>
+endobj
+6 0 obj <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /NM <</PatternType 2 /Shading <</Test (a)>> >>
+  /F 4
+  /Rect [200 200 400 400]
+  /V (t)
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 9 0 R
+>>
+endobj
+9 0 obj <<
+>>
+stream
+function run() {
+  a = this.getAnnot(0,'');
+  a.name = 'newname';
+}
+app.setTimeOut('run()',3000);
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000081 00000 n 
+0000000144 00000 n 
+0000000276 00000 n 
+0000000374 00000 n 
+0000000435 00000 n 
+0000000603 00000 n 
+0000000679 00000 n 
+0000000745 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+881
+%%EOF
diff --git a/testing/resources/bug_905142.in b/testing/resources/bug_905142.in
new file mode 100644
index 0000000..d37f68f
--- /dev/null
+++ b/testing/resources/bug_905142.in
@@ -0,0 +1,31 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [ 3 0 R ]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 1 0 R
+  /Contents [ 4 0 R ]
+  /MediaBox [ 0 0 612 792 ]
+>>
+endobj
+{{object 4 0}} <<
+  /Filter /FlateDecode
+  /Length 0
+>>
+stream
+
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_905142.pdf b/testing/resources/bug_905142.pdf
new file mode 100644
index 0000000..9f2898f
--- /dev/null
+++ b/testing/resources/bug_905142.pdf
@@ -0,0 +1,42 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [ 3 0 R ]
+  /Count 1
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 1 0 R
+  /Contents [ 4 0 R ]
+  /MediaBox [ 0 0 612 792 ]
+>>
+endobj
+4 0 obj <<
+  /Filter /FlateDecode
+  /Length 0
+>>
+stream
+
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000133 00000 n 
+0000000234 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+308
+%%EOF
diff --git a/testing/resources/bug_921.in b/testing/resources/bug_921.in
index 9c48b20..a0c2e73 100644
--- a/testing/resources/bug_921.in
+++ b/testing/resources/bug_921.in
@@ -43,7 +43,7 @@
 >>
 endobj
 {{object 6 0}} <<
-/Length 2784
+{{streamlen}}
 >>
 stream
 BT
diff --git a/testing/resources/bug_925981.in b/testing/resources/bug_925981.in
new file mode 100644
index 0000000..3d6e37a
--- /dev/null
+++ b/testing/resources/bug_925981.in
@@ -0,0 +1,60 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 2
+  /Kids [ 3 0 R 7 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+{{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_925981.pdf b/testing/resources/bug_925981.pdf
new file mode 100644
index 0000000..8d856e1
--- /dev/null
+++ b/testing/resources/bug_925981.pdf
@@ -0,0 +1,74 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 2
+  /Kids [ 3 0 R 7 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+/Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+7 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000167 00000 n 
+0000000309 00000 n 
+0000000387 00000 n 
+0000000463 00000 n 
+0000000595 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+646
+%%EOF
diff --git a/testing/resources/bug_972518.in b/testing/resources/bug_972518.in
new file mode 100644
index 0000000..8c6dc94
--- /dev/null
+++ b/testing/resources/bug_972518.in
@@ -0,0 +1,26 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{object 4 0}} <<
+  /Type /EmbeddedFile
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://b" locale="en_US" name="Report">
+  <subform name="head">
+    <draw name="costTitle81m.893mm">
+      <value>
+        <text>Cost</text>
+      </value>
+      <font typeface="mbogo">
+      </font>
+    </draw>
+  </subform>
+</template>
+endobj
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_972518.pdf b/testing/resources/bug_972518.pdf
new file mode 100644
index 0000000..51185f5
--- /dev/null
+++ b/testing/resources/bug_972518.pdf
@@ -0,0 +1,85 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Type /EmbeddedFile
+  /Length 456
+>>
+stream
+<template xmlns="http://b" locale="en_US" name="Report">
+  <subform name="head">
+    <draw name="costTitle81m.893mm">
+      <value>
+        <text>Cost</text>
+      </value>
+      <font typeface="mbogo">
+      </font>
+    </draw>
+  </subform>
+</template>
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000852 00000 n 
+0000000915 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+992
+%%EOF
diff --git a/testing/resources/calculate.in b/testing/resources/calculate.in
new file mode 100644
index 0000000..3e40bf8
--- /dev/null
+++ b/testing/resources/calculate.in
@@ -0,0 +1,62 @@
+{{header}}
+{{object 1 0}} <<
+  /AcroForm 11 0 R
+  /Pages 5 0 R
+  /Type /Catalog
+>>
+endobj
+
+{{object 11 0}} <<
+  /Fields [57 0 R 61 0 R]
+>>
+endobj
+
+{{object 5 0}} <<
+  /Count 1
+  /Type /Pages
+  /Kids [12 0 R]
+>>
+endobj
+
+{{object 12 0}} <<
+  /CropBox [0.0 0.0 720.0 540.0]
+  /Annots [57 0 R 61 0 R]
+  /Parent 5 0 R
+  /MediaBox[0.0 0.0 720.0 540.0]
+  /TrimBox[0.0 0.0 720.0 540.0]
+  /Type /Page
+>>
+endobj
+
+{{object 57 0}} <<
+  /Rect [142.13 307.409 208.842 331.442]
+  /Subtype /Widget
+  /F 4
+  /P 12 0 R
+  /Q 2
+  /T (Calc1_A)
+  /V (5)
+  /DV (5)
+  /FT /Tx
+  /Type /Annot
+>>
+endobj
+
+{{object 61 0}} <<
+  /Rect [290.306 307.409 357.793 331.442]
+  /Subtype /Widget
+  /F 4
+  /P 12 0 R
+  /Q 2
+  /T (Calc1_B)
+  /V (2)
+  /DV (2)
+  /FT /Tx
+  /Type /Annot
+>>
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/calculate.pdf b/testing/resources/calculate.pdf
new file mode 100644
index 0000000..8b2b944
--- /dev/null
+++ b/testing/resources/calculate.pdf
@@ -0,0 +1,127 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 11 0 R
+  /Pages 5 0 R
+  /Type /Catalog
+>>
+endobj
+
+11 0 obj <<
+  /Fields [57 0 R 61 0 R]
+>>
+endobj
+
+5 0 obj <<
+  /Count 1
+  /Type /Pages
+  /Kids [12 0 R]
+>>
+endobj
+
+12 0 obj <<
+  /CropBox [0.0 0.0 720.0 540.0]
+  /Annots [57 0 R 61 0 R]
+  /Parent 5 0 R
+  /MediaBox[0.0 0.0 720.0 540.0]
+  /TrimBox[0.0 0.0 720.0 540.0]
+  /Type /Page
+>>
+endobj
+
+57 0 obj <<
+  /Rect [142.13 307.409 208.842 331.442]
+  /Subtype /Widget
+  /F 4
+  /P 12 0 R
+  /Q 2
+  /T (Calc1_A)
+  /V (5)
+  /DV (5)
+  /FT /Tx
+  /Type /Annot
+>>
+endobj
+
+61 0 obj <<
+  /Rect [290.306 307.409 357.793 331.442]
+  /Subtype /Widget
+  /F 4
+  /P 12 0 R
+  /Q 2
+  /T (Calc1_B)
+  /V (2)
+  /DV (2)
+  /FT /Tx
+  /Type /Annot
+>>
+endobj
+
+xref
+0 62
+0000000000 65535 f 
+0000000015 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000137 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000088 00000 n 
+0000000202 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000379 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000547 00000 n 
+trailer<< /Root 1 0 R /Size 62 >>
+startxref
+716
+%%EOF
diff --git a/testing/resources/click_form.in b/testing/resources/click_form.in
new file mode 100644
index 0000000..439f818
--- /dev/null
+++ b/testing/resources/click_form.in
@@ -0,0 +1,1026 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [6 0 R 7 0 R 8 0 R 12 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 300]
+  /Resources <<
+    /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+  /Annots [6 0 R 7 0 R 9 0 R 10 0 R 11 0 R 13 0 R 14 0 R 15 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /Name /F1
+  /BaseFont /Courier
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 1 0 0 cm
+BT /F1 12 Tf 14.4 TL ET
+BT 1 0 0 1 85.2 170 Tm (This form contains) Tj T* ET
+BT 1 0 0 1 49.2 155 Tm (checkboxes and radio buttons) Tj T* ET
+BT /F1 14 Tf 16.8 TL ET
+q
+1 w
+1 0 1 RG
+1 .752941 .796078 rg
+n 135.5 250.5 19 19 re B*
+Q
+q
+2 w
+.1 .1 .1 RG
+.8 .843 1 rg
+n 136 211 18 18 re B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 110 m
+104 114.9706 99.97056 119 95 119 c
+90.02944 119 86 114.9706 86 110 c
+86 105.0294 90.02944 101 95 101 c
+99.97056 101 104 105.0294 104 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+154 110 m
+154 114.9706 149.9706 119 145 119 c
+140.0294 119 136 114.9706 136 110 c
+136 105.0294 140.0294 101 145 101 c
+149.9706 101 154 105.0294 154 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+204 110 m
+204 114.9706 199.9706 119 195 119 c
+190.0294 119 186 114.9706 186 110 c
+186 105.0294 190.0294 101 195 101 c
+199.9706 101 204 105.0294 204 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 60 m
+104 64.97056 99.97056 69 95 69 c
+90.02944 69 86 64.97056 86 60 c
+86 55.02944 90.02944 51 95 51 c
+99.97056 51 104 55.02944 104 60 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+154 60 m
+154 64.97056 149.9706 69 145 69 c
+140.0294 69 136 64.97056 136 60 c
+136 55.02944 140.0294 51 145 51 c
+149.9706 51 154 55.02944 154 60 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+204 60 m
+204 64.97056 199.9706 69 195 69 c
+190.0294 69 186 64.97056 186 60 c
+186 55.02944 190.0294 51 195 51 c
+199.9706 51 204 55.02944 204 60 c
+B*
+Q
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /Ff 1
+  /P 3 0 R
+  /Rect [135 250 155 270]
+  /T (readOnlyCheckbox)
+  /TU (readOnlyCheckbox)
+  /AP <<
+    /N <<
+      /Yes 16 0 R
+      /Off 17 0 R
+    >>
+    /D <<
+      /Yes 18 0 R
+      /Off 19 0 R
+    >>
+    /R <<
+      /Yes 20 0 R
+      /Off 21 0 R
+    >>
+  >>
+  /AS /Yes
+  /BS <<
+    /S /S
+    /W 1
+  >>
+  /H /N
+  /MK <<
+    /BC [1 0 1]
+    /BG [1 .752941 .796078]
+    /CA (4)
+  >>
+  /V /Yes
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /Ff 2
+  /P 3 0 R
+  /Rect [135 210 155 230]
+  /T (checkbox)
+  /TU (checkbox)
+  /AP <<
+    /N <<
+      /Yes 22 0 R
+      /Off 23 0 R
+    >>
+    /D <<
+      /Yes 24 0 R
+      /Off 25 0 R
+    >>
+    /R <<
+      /Yes 26 0 R
+      /Off 27 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [.1 .1 .1]
+    /BG [.8 .843 1]
+    /CA (5)
+  >>
+  /V /Off
+>>
+endobj
+{{object 8 0}} <<
+  /FT /Btn
+  /Ff 49153
+  /T (readOnlyRadioButton)
+  /TU (readOnlyRadioButton1)
+  /Kids [9 0 R 10 0 R 11 0 R]
+  /V /value3
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 8 0 R
+  /Rect [85 100 105 120]
+  /AP <<
+    /N <<
+      /value1 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value1 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value1 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 8 0 R
+  /Rect [135 100 155 120]
+  /AP <<
+    /N <<
+      /value2 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value2 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value2 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 8 0 R
+  /Rect [185 100 205 120]
+  /AP <<
+    /N <<
+      /value3 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value3 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value3 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /value3
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+{{object 12 0}} <<
+  /FT /Btn
+  /Ff 49154
+  /T (radioButton)
+  /TU (radioButton1)
+  /Kids [13 0 R 14 0 R 15 0 R]
+  /V /value3
+>>
+endobj
+{{object 13 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [85 50 105 70]
+  /AP <<
+    /N <<
+      /value1 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value1 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value1 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+{{object 14 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [135 50 155 70]
+  /AP <<
+    /N <<
+      /value2 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value2 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value2 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+{{object 15 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [185 50 205 70]
+  /AP <<
+    /N <<
+      /value3 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value3 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value3 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /value3
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+{{object 16 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 1 .752941 .796078 rg 0 0 20 20 re f
+1 0 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+q
+0 0 1 rg 0 0 1 RG
+11.90655 14.48997 m
+13.73412 16.72368 l
+14.54638 17.62618 16.37396 18.07744 17.59234 18.07744 c
+17.75028 17.71643 l
+15.65195 15.23454 l
+10.95891 9.029805 l
+7.709889 3.998329 l
+6.920195 2.644568 l
+6.807382 2.418942 6.672006 2.148189 6.446379 2.012813 c
+6.153064 1.877437 5.476184 1.877437 5.160306 1.877437 c
+4.438301 1.877437 4.280362 1.877437 4.122423 1.990251 c
+3.896797 2.148189 3.783983 2.464067 3.626045 2.779944 c
+3.129666 3.817827 2.249721 6.074095 2.249721 7.202228 c
+2.249721 7.585794 2.475348 7.766295 2.768663 7.969359 c
+3.242479 8.307799 3.919359 8.691365 4.505989 8.691365 c
+4.957242 8.691365 5.002368 8.307799 5.137744 7.946797 c
+5.588997 6.886351 l
+5.656685 6.70585 5.859749 6.074095 6.107939 6.074095 c
+6.333565 6.074095 6.604318 6.593036 6.694568 6.750975 c
+11.90655 14.48997 l
+h
+f
+Q
+endstream
+endobj
+{{object 17 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 1 .752941 .796078 rg 0 0 20 20 re f
+1 0 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+endstream
+endobj
+{{object 18 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .9 .677647 .716471 rg 0 0 20 20 re f
+.9 0 .9 RG 1 w 0.5 0.5 19 19 re s
+Q
+q
+0 0 .9 rg 0 0 .9 RG
+11.90655 14.48997 m
+13.73412 16.72368 l
+14.54638 17.62618 16.37396 18.07744 17.59234 18.07744 c
+17.75028 17.71643 l
+15.65195 15.23454 l
+10.95891 9.029805 l
+7.709889 3.998329 l
+6.920195 2.644568 l
+6.807382 2.418942 6.672006 2.148189 6.446379 2.012813 c
+6.153064 1.877437 5.476184 1.877437 5.160306 1.877437 c
+4.438301 1.877437 4.280362 1.877437 4.122423 1.990251 c
+3.896797 2.148189 3.783983 2.464067 3.626045 2.779944 c
+3.129666 3.817827 2.249721 6.074095 2.249721 7.202228 c
+2.249721 7.585794 2.475348 7.766295 2.768663 7.969359 c
+3.242479 8.307799 3.919359 8.691365 4.505989 8.691365 c
+4.957242 8.691365 5.002368 8.307799 5.137744 7.946797 c
+5.588997 6.886351 l
+5.656685 6.70585 5.859749 6.074095 6.107939 6.074095 c
+6.333565 6.074095 6.604318 6.593036 6.694568 6.750975 c
+11.90655 14.48997 l
+h
+f
+Q
+endstream
+endobj
+{{object 19 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .9 .677647 .716471 rg 0 0 20 20 re f
+.9 0 .9 RG 1 w 0.5 0.5 19 19 re s
+Q
+endstream
+endobj
+{{object 20 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 1 .777647 .816471 rg 0 0 20 20 re f
+1 .1 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+q
+.1 .1 1 rg .1 .1 1 RG
+11.90655 14.48997 m
+13.73412 16.72368 l
+14.54638 17.62618 16.37396 18.07744 17.59234 18.07744 c
+17.75028 17.71643 l
+15.65195 15.23454 l
+10.95891 9.029805 l
+7.709889 3.998329 l
+6.920195 2.644568 l
+6.807382 2.418942 6.672006 2.148189 6.446379 2.012813 c
+6.153064 1.877437 5.476184 1.877437 5.160306 1.877437 c
+4.438301 1.877437 4.280362 1.877437 4.122423 1.990251 c
+3.896797 2.148189 3.783983 2.464067 3.626045 2.779944 c
+3.129666 3.817827 2.249721 6.074095 2.249721 7.202228 c
+2.249721 7.585794 2.475348 7.766295 2.768663 7.969359 c
+3.242479 8.307799 3.919359 8.691365 4.505989 8.691365 c
+4.957242 8.691365 5.002368 8.307799 5.137744 7.946797 c
+5.588997 6.886351 l
+5.656685 6.70585 5.859749 6.074095 6.107939 6.074095 c
+6.333565 6.074095 6.604318 6.593036 6.694568 6.750975 c
+11.90655 14.48997 l
+h
+f
+Q
+endstream
+endobj
+{{object 21 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 1 .777647 .816471 rg 0 0 20 20 re f
+1 .1 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+endstream
+endobj
+{{object 22 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .8 .843 1 rg 0 0 20 20 re f
+.1 .1 .1 RG 2 w 1.0 1.0 18 18 re s
+Q
+q
+.1 .1 .1 rg .1 .1 .1 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 23 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .8 .843 1 rg 0 0 20 20 re f
+.1 .1 .1 RG 2 w 1.0 1.0 18 18 re s
+Q
+endstream
+endobj
+{{object 24 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .72 .7587 .9 rg 0 0 20 20 re f
+.09 .09 .09 RG 2 w 1.0 1.0 18 18 re s
+Q
+q
+.09 .09 .09 rg .09 .09 .09 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 25 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .72 .7587 .9 rg 0 0 20 20 re f
+.09 .09 .09 RG 2 w 1.0 1.0 18 18 re s
+Q
+endstream
+endobj
+{{object 26 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .82 .8587 1 rg 0 0 20 20 re f
+.19 .19 .19 RG 2 w 1.0 1.0 18 18 re s
+Q
+q
+.19 .19 .19 rg .19 .19 .19 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 27 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .82 .8587 1 rg 0 0 20 20 re f
+.19 .19 .19 RG 2 w 1.0 1.0 18 18 re s
+Q
+endstream
+endobj
+{{object 28 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 0 0 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .501961 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+q
+1 .752941 .796078 rg 1 .752941 .796078 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 29 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 0 0 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .501961 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+endstream
+endobj
+{{object 30 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 0 0 .9 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .451765 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+q
+.9 .677647 .716471 rg .9 .677647 .716471 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 31 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G 0 0 .9 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .451765 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+endstream
+endobj
+{{object 32 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .1 .1 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+.1 .551765 .1 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+q
+1 .777647 .816471 rg 1 .777647 .816471 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 33 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  {{streamlen}}
+>>
+stream
+q
+1 g 1 G .1 .1 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+.1 .551765 .1 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/click_form.pdf b/testing/resources/click_form.pdf
new file mode 100644
index 0000000..23c4af9
--- /dev/null
+++ b/testing/resources/click_form.pdf
@@ -0,0 +1,1066 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [6 0 R 7 0 R 8 0 R 12 0 R]
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 300]
+  /Resources <<
+    /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+  /Annots [6 0 R 7 0 R 9 0 R 10 0 R 11 0 R 13 0 R 14 0 R 15 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /Name /F1
+  /BaseFont /Courier
+>>
+endobj
+5 0 obj <<
+  /Length 1411
+>>
+stream
+q
+1 0 0 1 0 0 cm
+BT /F1 12 Tf 14.4 TL ET
+BT 1 0 0 1 85.2 170 Tm (This form contains) Tj T* ET
+BT 1 0 0 1 49.2 155 Tm (checkboxes and radio buttons) Tj T* ET
+BT /F1 14 Tf 16.8 TL ET
+q
+1 w
+1 0 1 RG
+1 .752941 .796078 rg
+n 135.5 250.5 19 19 re B*
+Q
+q
+2 w
+.1 .1 .1 RG
+.8 .843 1 rg
+n 136 211 18 18 re B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 110 m
+104 114.9706 99.97056 119 95 119 c
+90.02944 119 86 114.9706 86 110 c
+86 105.0294 90.02944 101 95 101 c
+99.97056 101 104 105.0294 104 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+154 110 m
+154 114.9706 149.9706 119 145 119 c
+140.0294 119 136 114.9706 136 110 c
+136 105.0294 140.0294 101 145 101 c
+149.9706 101 154 105.0294 154 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+204 110 m
+204 114.9706 199.9706 119 195 119 c
+190.0294 119 186 114.9706 186 110 c
+186 105.0294 190.0294 101 195 101 c
+199.9706 101 204 105.0294 204 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 60 m
+104 64.97056 99.97056 69 95 69 c
+90.02944 69 86 64.97056 86 60 c
+86 55.02944 90.02944 51 95 51 c
+99.97056 51 104 55.02944 104 60 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+154 60 m
+154 64.97056 149.9706 69 145 69 c
+140.0294 69 136 64.97056 136 60 c
+136 55.02944 140.0294 51 145 51 c
+149.9706 51 154 55.02944 154 60 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+204 60 m
+204 64.97056 199.9706 69 195 69 c
+190.0294 69 186 64.97056 186 60 c
+186 55.02944 190.0294 51 195 51 c
+199.9706 51 204 55.02944 204 60 c
+B*
+Q
+Q
+endstream
+endobj
+6 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /Ff 1
+  /P 3 0 R
+  /Rect [135 250 155 270]
+  /T (readOnlyCheckbox)
+  /TU (readOnlyCheckbox)
+  /AP <<
+    /N <<
+      /Yes 16 0 R
+      /Off 17 0 R
+    >>
+    /D <<
+      /Yes 18 0 R
+      /Off 19 0 R
+    >>
+    /R <<
+      /Yes 20 0 R
+      /Off 21 0 R
+    >>
+  >>
+  /AS /Yes
+  /BS <<
+    /S /S
+    /W 1
+  >>
+  /H /N
+  /MK <<
+    /BC [1 0 1]
+    /BG [1 .752941 .796078]
+    /CA (4)
+  >>
+  /V /Yes
+>>
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /Ff 2
+  /P 3 0 R
+  /Rect [135 210 155 230]
+  /T (checkbox)
+  /TU (checkbox)
+  /AP <<
+    /N <<
+      /Yes 22 0 R
+      /Off 23 0 R
+    >>
+    /D <<
+      /Yes 24 0 R
+      /Off 25 0 R
+    >>
+    /R <<
+      /Yes 26 0 R
+      /Off 27 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [.1 .1 .1]
+    /BG [.8 .843 1]
+    /CA (5)
+  >>
+  /V /Off
+>>
+endobj
+8 0 obj <<
+  /FT /Btn
+  /Ff 49153
+  /T (readOnlyRadioButton)
+  /TU (readOnlyRadioButton1)
+  /Kids [9 0 R 10 0 R 11 0 R]
+  /V /value3
+>>
+endobj
+9 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 8 0 R
+  /Rect [85 100 105 120]
+  /AP <<
+    /N <<
+      /value1 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value1 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value1 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 8 0 R
+  /Rect [135 100 155 120]
+  /AP <<
+    /N <<
+      /value2 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value2 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value2 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+11 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 8 0 R
+  /Rect [185 100 205 120]
+  /AP <<
+    /N <<
+      /value3 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value3 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value3 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /value3
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+12 0 obj <<
+  /FT /Btn
+  /Ff 49154
+  /T (radioButton)
+  /TU (radioButton1)
+  /Kids [13 0 R 14 0 R 15 0 R]
+  /V /value3
+>>
+endobj
+13 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [85 50 105 70]
+  /AP <<
+    /N <<
+      /value1 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value1 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value1 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+14 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [135 50 155 70]
+  /AP <<
+    /N <<
+      /value2 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value2 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value2 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /Off
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+15 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /F 4
+  /P 3 0 R
+  /Parent 12 0 R
+  /Rect [185 50 205 70]
+  /AP <<
+    /N <<
+      /value3 28 0 R
+      /Off 29 0 R
+    >>
+    /D <<
+      /value3 30 0 R
+      /Off 31 0 R
+    >>
+    /R <<
+      /value3 32 0 R
+      /Off 33 0 R
+    >>
+  >>
+  /AS /value3
+  /BS <<
+    /S /S
+    /W 2
+  >>
+  /H /N
+  /MK <<
+    /BC [0 .501961 0]
+    /BG [0 0 1]
+    /CA (5)
+  >>
+>>
+endobj
+16 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 901
+>>
+stream
+q
+1 g 1 G 1 .752941 .796078 rg 0 0 20 20 re f
+1 0 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+q
+0 0 1 rg 0 0 1 RG
+11.90655 14.48997 m
+13.73412 16.72368 l
+14.54638 17.62618 16.37396 18.07744 17.59234 18.07744 c
+17.75028 17.71643 l
+15.65195 15.23454 l
+10.95891 9.029805 l
+7.709889 3.998329 l
+6.920195 2.644568 l
+6.807382 2.418942 6.672006 2.148189 6.446379 2.012813 c
+6.153064 1.877437 5.476184 1.877437 5.160306 1.877437 c
+4.438301 1.877437 4.280362 1.877437 4.122423 1.990251 c
+3.896797 2.148189 3.783983 2.464067 3.626045 2.779944 c
+3.129666 3.817827 2.249721 6.074095 2.249721 7.202228 c
+2.249721 7.585794 2.475348 7.766295 2.768663 7.969359 c
+3.242479 8.307799 3.919359 8.691365 4.505989 8.691365 c
+4.957242 8.691365 5.002368 8.307799 5.137744 7.946797 c
+5.588997 6.886351 l
+5.656685 6.70585 5.859749 6.074095 6.107939 6.074095 c
+6.333565 6.074095 6.604318 6.593036 6.694568 6.750975 c
+11.90655 14.48997 l
+h
+f
+Q
+endstream
+endobj
+17 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 80
+>>
+stream
+q
+1 g 1 G 1 .752941 .796078 rg 0 0 20 20 re f
+1 0 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+endstream
+endobj
+18 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 906
+>>
+stream
+q
+1 g 1 G .9 .677647 .716471 rg 0 0 20 20 re f
+.9 0 .9 RG 1 w 0.5 0.5 19 19 re s
+Q
+q
+0 0 .9 rg 0 0 .9 RG
+11.90655 14.48997 m
+13.73412 16.72368 l
+14.54638 17.62618 16.37396 18.07744 17.59234 18.07744 c
+17.75028 17.71643 l
+15.65195 15.23454 l
+10.95891 9.029805 l
+7.709889 3.998329 l
+6.920195 2.644568 l
+6.807382 2.418942 6.672006 2.148189 6.446379 2.012813 c
+6.153064 1.877437 5.476184 1.877437 5.160306 1.877437 c
+4.438301 1.877437 4.280362 1.877437 4.122423 1.990251 c
+3.896797 2.148189 3.783983 2.464067 3.626045 2.779944 c
+3.129666 3.817827 2.249721 6.074095 2.249721 7.202228 c
+2.249721 7.585794 2.475348 7.766295 2.768663 7.969359 c
+3.242479 8.307799 3.919359 8.691365 4.505989 8.691365 c
+4.957242 8.691365 5.002368 8.307799 5.137744 7.946797 c
+5.588997 6.886351 l
+5.656685 6.70585 5.859749 6.074095 6.107939 6.074095 c
+6.333565 6.074095 6.604318 6.593036 6.694568 6.750975 c
+11.90655 14.48997 l
+h
+f
+Q
+endstream
+endobj
+19 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 83
+>>
+stream
+q
+1 g 1 G .9 .677647 .716471 rg 0 0 20 20 re f
+.9 0 .9 RG 1 w 0.5 0.5 19 19 re s
+Q
+endstream
+endobj
+20 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 906
+>>
+stream
+q
+1 g 1 G 1 .777647 .816471 rg 0 0 20 20 re f
+1 .1 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+q
+.1 .1 1 rg .1 .1 1 RG
+11.90655 14.48997 m
+13.73412 16.72368 l
+14.54638 17.62618 16.37396 18.07744 17.59234 18.07744 c
+17.75028 17.71643 l
+15.65195 15.23454 l
+10.95891 9.029805 l
+7.709889 3.998329 l
+6.920195 2.644568 l
+6.807382 2.418942 6.672006 2.148189 6.446379 2.012813 c
+6.153064 1.877437 5.476184 1.877437 5.160306 1.877437 c
+4.438301 1.877437 4.280362 1.877437 4.122423 1.990251 c
+3.896797 2.148189 3.783983 2.464067 3.626045 2.779944 c
+3.129666 3.817827 2.249721 6.074095 2.249721 7.202228 c
+2.249721 7.585794 2.475348 7.766295 2.768663 7.969359 c
+3.242479 8.307799 3.919359 8.691365 4.505989 8.691365 c
+4.957242 8.691365 5.002368 8.307799 5.137744 7.946797 c
+5.588997 6.886351 l
+5.656685 6.70585 5.859749 6.074095 6.107939 6.074095 c
+6.333565 6.074095 6.604318 6.593036 6.694568 6.750975 c
+11.90655 14.48997 l
+h
+f
+Q
+endstream
+endobj
+21 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 81
+>>
+stream
+q
+1 g 1 G 1 .777647 .816471 rg 0 0 20 20 re f
+1 .1 1 RG 1 w 0.5 0.5 19 19 re s
+Q
+endstream
+endobj
+22 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 303
+>>
+stream
+q
+1 g 1 G .8 .843 1 rg 0 0 20 20 re f
+.1 .1 .1 RG 2 w 1.0 1.0 18 18 re s
+Q
+q
+.1 .1 .1 rg .1 .1 .1 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+23 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 75
+>>
+stream
+q
+1 g 1 G .8 .843 1 rg 0 0 20 20 re f
+.1 .1 .1 RG 2 w 1.0 1.0 18 18 re s
+Q
+endstream
+endobj
+24 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 315
+>>
+stream
+q
+1 g 1 G .72 .7587 .9 rg 0 0 20 20 re f
+.09 .09 .09 RG 2 w 1.0 1.0 18 18 re s
+Q
+q
+.09 .09 .09 rg .09 .09 .09 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+25 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 81
+>>
+stream
+q
+1 g 1 G .72 .7587 .9 rg 0 0 20 20 re f
+.09 .09 .09 RG 2 w 1.0 1.0 18 18 re s
+Q
+endstream
+endobj
+26 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 314
+>>
+stream
+q
+1 g 1 G .82 .8587 1 rg 0 0 20 20 re f
+.19 .19 .19 RG 2 w 1.0 1.0 18 18 re s
+Q
+q
+.19 .19 .19 rg .19 .19 .19 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+27 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 80
+>>
+stream
+q
+1 g 1 G .82 .8587 1 rg 0 0 20 20 re f
+.19 .19 .19 RG 2 w 1.0 1.0 18 18 re s
+Q
+endstream
+endobj
+28 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 573
+>>
+stream
+q
+1 g 1 G 0 0 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .501961 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+q
+1 .752941 .796078 rg 1 .752941 .796078 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+29 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 327
+>>
+stream
+q
+1 g 1 G 0 0 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .501961 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+endstream
+endobj
+30 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 576
+>>
+stream
+q
+1 g 1 G 0 0 .9 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .451765 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+q
+.9 .677647 .716471 rg .9 .677647 .716471 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+31 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 328
+>>
+stream
+q
+1 g 1 G 0 0 .9 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+0 .451765 0 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+endstream
+endobj
+32 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 577
+>>
+stream
+q
+1 g 1 G .1 .1 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+.1 .551765 .1 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+q
+1 .777647 .816471 rg 1 .777647 .816471 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+33 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox [0 0 20 20]
+  /Resources <<
+    /ProcSet [/PDF]
+  >>
+  /Length 331
+>>
+stream
+q
+1 g 1 G .1 .1 1 rg
+1 0 0 1 10 10 cm
+10 0 m
+10 5.5231 5.5231 10 0 10 c
+-5.5231 10 -10 5.5231 -10 0 c
+-10 -5.5231 -5.5231 -10 0 -10 c
+5.5231 -10 10 -5.5231 10 0 c
+f
+Q
+q
+.1 .551765 .1 RG 2 w
+1 0 0 1 10 10 cm
+9 0 m
+9 4.97079 4.97079 9 0 9 c
+-4.97079 9 -9 4.97079 -9 0 c
+-9 -4.97079 -4.97079 -9 0 -9 c
+4.97079 -9 9 -4.97079 9 0 c
+s
+Q
+endstream
+endobj
+xref
+0 34
+0000000000 65535 f 
+0000000015 00000 n 
+0000000127 00000 n 
+0000000190 00000 n 
+0000000457 00000 n 
+0000000543 00000 n 
+0000002007 00000 n 
+0000002479 00000 n 
+0000002930 00000 n 
+0000003073 00000 n 
+0000003496 00000 n 
+0000003921 00000 n 
+0000004349 00000 n 
+0000004478 00000 n 
+0000004901 00000 n 
+0000005325 00000 n 
+0000005752 00000 n 
+0000006815 00000 n 
+0000007056 00000 n 
+0000008124 00000 n 
+0000008368 00000 n 
+0000009436 00000 n 
+0000009678 00000 n 
+0000010143 00000 n 
+0000010379 00000 n 
+0000010856 00000 n 
+0000011098 00000 n 
+0000011574 00000 n 
+0000011815 00000 n 
+0000012550 00000 n 
+0000013039 00000 n 
+0000013777 00000 n 
+0000014267 00000 n 
+0000015006 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 34
+>>
+startxref
+15499
+%%EOF
diff --git a/testing/resources/clip_path.in b/testing/resources/clip_path.in
new file mode 100644
index 0000000..fd13f8e
--- /dev/null
+++ b/testing/resources/clip_path.in
@@ -0,0 +1,39 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /MediaBox [0 0 100 50]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Contents 4 0 R
+  /Parent 2 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+10 15 m
+40 15 l
+40 35 l
+10 35 l
+W n
+0 0 1 RG
+10 10 m
+25 40 l
+40 10 l
+s
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/clip_path.pdf b/testing/resources/clip_path.pdf
new file mode 100644
index 0000000..8d27bcb
--- /dev/null
+++ b/testing/resources/clip_path.pdf
@@ -0,0 +1,50 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /MediaBox [0 0 100 50]
+  /Count 1
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Contents 4 0 R
+  /Parent 2 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 71
+>>
+stream
+10 15 m
+40 15 l
+40 35 l
+10 35 l
+W n
+0 0 1 RG
+10 10 m
+25 40 l
+40 10 l
+s
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000156 00000 n 
+0000000225 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+347
+%%EOF
diff --git a/testing/resources/combobox_form.in b/testing/resources/combobox_form.in
index 6c9f914..3f1004d 100644
--- a/testing/resources/combobox_form.in
+++ b/testing/resources/combobox_form.in
@@ -1,82 +1,88 @@
 {{header}}
-{{object 1 0}}
-<<
+{{object 1 0}} <<
   /Type /Catalog
   /Pages 2 0 R
-  /AcroForm << /Fields [ 8 0 R 9 0 R 10 0 R ] /DR 4 0 R >>
+  /AcroForm <<
+    /Fields [ 8 0 R 9 0 R 10 0 R ]
+    /DR 4 0 R
+  >>
 >>
 endobj
-{{object 2 0}}
-<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >>
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
 endobj
-{{object 3 0}}
-<<
+{{object 3 0}} <<
   /Type /Page
   /Parent 2 0 R
   /Resources 4 0 R
-  /MediaBox [ 0 0 300 300 ]
+  /MediaBox [ 0 0 300 600 ]
   /Contents 7 0 R
   /Annots [ 8 0 R 9 0 R 10 0 R ]
 >>
 endobj
-{{object 4 0}}
-<< /Font 5 0 R >>
+{{object 4 0}} <<
+  /Font 5 0 R
+>>
 endobj
-{{object 5 0}}
-<< /F1 6 0 R >>
+{{object 5 0}} <<
+  /F1 6 0 R
+>>
 endobj
-{{object 6 0}}
-<<
+{{object 6 0}} <<
   /Type /Font
   /Subtype /Type1
   /BaseFont /Helvetica
 >>
 endobj
-{{object 7 0}}
-<< /Length 51 >>
+{{object 7 0}} <<
+  {{streamlen}}
+>>
 stream
 BT
 0 0 0 rg
 /F1 12 Tf
-100 150 Td
+100 450 Td
 (Test Form) Tj
 ET
 endstream
 endobj
-{{object 8 0}}
-<<
+{{object 8 0}} <<
   /Type /Annot
+  /Subtype /Widget
   /FT /Ch
   /Ff 393216
   /T (Combo_Editable)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 50 200 80 ]
-  /Subtype /Widget
+  /Rect [ 100 350 200 380 ]
   /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
 >>
 endobj
-{{object 9 0}}
-<<
+{{object 9 0}} <<
   /Type /Annot
+  /Subtype /Widget
   /FT /Ch
   /Ff 131072
   /T (Combo1)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 100 200 130 ]
-  /Subtype /Widget
-  /Opt [(Apple) (Banana) (Cherry)]
+  /Rect [ 100 400 200 430 ]
+  /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+        (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+        (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+        (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
   /V (Banana)
 >>
 endobj
-{{object 10 0}}
-<<
+{{object 10 0}} <<
   /Type /Annot
+  /Subtype /Widget
   /FT /Ch
   /Ff 131073
   /T (Combo_ReadOnly)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 200 200 230 ]
-  /Subtype /Widget
+  /Rect [ 100 500 200 530 ]
   /Opt [(Dog) (Elephant) (Frog)]
 >>
 endobj
diff --git a/testing/resources/combobox_form.pdf b/testing/resources/combobox_form.pdf
index 5249276..5b61db5 100644
--- a/testing/resources/combobox_form.pdf
+++ b/testing/resources/combobox_form.pdf
@@ -1,83 +1,89 @@
 %PDF-1.7
 % ò¤ô
-1 0 obj
-<<
+1 0 obj <<
   /Type /Catalog
   /Pages 2 0 R
-  /AcroForm << /Fields [ 8 0 R 9 0 R 10 0 R ] /DR 4 0 R >>
+  /AcroForm <<
+    /Fields [ 8 0 R 9 0 R 10 0 R ]
+    /DR 4 0 R
+  >>
 >>
 endobj
-2 0 obj
-<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >>
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
 endobj
-3 0 obj
-<<
+3 0 obj <<
   /Type /Page
   /Parent 2 0 R
   /Resources 4 0 R
-  /MediaBox [ 0 0 300 300 ]
+  /MediaBox [ 0 0 300 600 ]
   /Contents 7 0 R
   /Annots [ 8 0 R 9 0 R 10 0 R ]
 >>
 endobj
-4 0 obj
-<< /Font 5 0 R >>
+4 0 obj <<
+  /Font 5 0 R
+>>
 endobj
-5 0 obj
-<< /F1 6 0 R >>
+5 0 obj <<
+  /F1 6 0 R
+>>
 endobj
-6 0 obj
-<<
+6 0 obj <<
   /Type /Font
   /Subtype /Type1
   /BaseFont /Helvetica
 >>
 endobj
-7 0 obj
-<< /Length 51 >>
+7 0 obj <<
+  /Length 51
+>>
 stream
 BT
 0 0 0 rg
 /F1 12 Tf
-100 150 Td
+100 450 Td
 (Test Form) Tj
 ET
 endstream
 endobj
-8 0 obj
-<<
+8 0 obj <<
   /Type /Annot
+  /Subtype /Widget
   /FT /Ch
   /Ff 393216
   /T (Combo_Editable)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 50 200 80 ]
-  /Subtype /Widget
+  /Rect [ 100 350 200 380 ]
   /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
 >>
 endobj
-9 0 obj
-<<
+9 0 obj <<
   /Type /Annot
+  /Subtype /Widget
   /FT /Ch
   /Ff 131072
   /T (Combo1)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 100 200 130 ]
-  /Subtype /Widget
-  /Opt [(Apple) (Banana) (Cherry)]
+  /Rect [ 100 400 200 430 ]
+  /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+        (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+        (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+        (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
   /V (Banana)
 >>
 endobj
-10 0 obj
-<<
+10 0 obj <<
   /Type /Annot
+  /Subtype /Widget
   /FT /Ch
   /Ff 131073
   /T (Combo_ReadOnly)
   /DA (0 0 0 rg /F1 12 Tf)
-  /Rect [ 100 200 200 230 ]
-  /Subtype /Widget
+  /Rect [ 100 500 200 530 ]
   /Opt [(Dog) (Elephant) (Frog)]
 >>
 endobj
@@ -85,16 +91,19 @@
 0 11
 0000000000 65535 f 
 0000000015 00000 n 
-0000000127 00000 n 
-0000000186 00000 n 
-0000000335 00000 n 
-0000000368 00000 n 
-0000000399 00000 n 
-0000000475 00000 n 
-0000000575 00000 n 
-0000000779 00000 n 
-0000000975 00000 n 
-trailer<< /Root 1 0 R /Size 11 >>
+0000000137 00000 n 
+0000000202 00000 n 
+0000000351 00000 n 
+0000000386 00000 n 
+0000000419 00000 n 
+0000000495 00000 n 
+0000000597 00000 n 
+0000000803 00000 n 
+0000001259 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 11
+>>
 startxref
-1164
+1448
 %%EOF
diff --git a/testing/resources/cropped_text.in b/testing/resources/cropped_text.in
new file mode 100644
index 0000000..d6e2d60
--- /dev/null
+++ b/testing/resources/cropped_text.in
@@ -0,0 +1,110 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 4
+  /Kids [ 6 0 R 7 0 R 8 0 R 9 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /CropBox [ 50 50 150 150 ]
+  /BleedBox [ 50 60 140 145 ]
+  /TrimBox [ 55 60 140 145 ]
+  /ArtBox [ 55 65 140 145 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ -50 -50 200 200 ]
+  /CropBox [ 50 50 150 150 ]
+  /BleedBox [ 0 10 150 145 ]
+  /TrimBox [ 25 30 140 145 ]
+  /ArtBox [ 50 60 135 140 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /CropBox [ 60 60 150 150 ]
+  /BleedBox [ 10 20 150 145 ]
+  /TrimBox [ 35 30 140 145 ]
+  /ArtBox [ 65 75 140 148 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /CropBox [ 150 150 60 60 ]
+  /BleedBox [ 160 165 0 10 ]
+  /TrimBox [ 155 165 25 30 ]
+  /ArtBox [ 140 145 65 70 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/cropped_text.pdf b/testing/resources/cropped_text.pdf
new file mode 100644
index 0000000..3a93dbb
--- /dev/null
+++ b/testing/resources/cropped_text.pdf
@@ -0,0 +1,126 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 4
+  /Kids [ 6 0 R 7 0 R 8 0 R 9 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+5 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+6 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /CropBox [ 50 50 150 150 ]
+  /BleedBox [ 50 60 140 145 ]
+  /TrimBox [ 55 60 140 145 ]
+  /ArtBox [ 55 65 140 145 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+7 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ -50 -50 200 200 ]
+  /CropBox [ 50 50 150 150 ]
+  /BleedBox [ 0 10 150 145 ]
+  /TrimBox [ 25 30 140 145 ]
+  /ArtBox [ 50 60 135 140 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+8 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /CropBox [ 60 60 150 150 ]
+  /BleedBox [ 10 20 150 145 ]
+  /TrimBox [ 35 30 140 145 ]
+  /ArtBox [ 65 75 140 148 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 200 200 ]
+  /CropBox [ 150 150 60 60 ]
+  /BleedBox [ 160 165 0 10 ]
+  /TrimBox [ 155 165 25 30 ]
+  /ArtBox [ 140 145 65 70 ]
+  /Resources <<
+    /Font <<
+      /F1 3 0 R
+      /F2 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000151 00000 n 
+0000000229 00000 n 
+0000000305 00000 n 
+0000000439 00000 n 
+0000000725 00000 n 
+0000001014 00000 n 
+0000001300 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+1585
+%%EOF
diff --git a/testing/resources/document_aactions.in b/testing/resources/document_aactions.in
new file mode 100644
index 0000000..5c7ceed
--- /dev/null
+++ b/testing/resources/document_aactions.in
@@ -0,0 +1,139 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 3 0 R
+  /AA 11 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 3
+  /Kids [4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /CO [9 0 R]
+  /Fields [
+    9 0 R
+    7 0 R
+  ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 7 0}} <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txtName2)
+  /F 4
+  /AP <</N 8 0 R>>
+  /Rect [20 20 400 60]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+>>
+endobj
+{{object 9 0}} <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txtName1)
+  /F 4
+  /AP <</N 10 0 R>>
+  /Rect [200 200 400 260]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+>>
+endobj
+{{object 11 0}} <<
+  /WP 14 0 R
+  /DP 16 0 R
+  /WS 18 0 R
+  /DS 20 0 R
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 13 0 R
+>>
+endobj
+{{object 13 0}} <<
+  {{streamlen}}
+>>
+stream
+app.alert('Will Close');
+endstream
+endobj
+{{object 14 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 15 0 R
+>>
+endobj
+{{object 15 0}} <<
+  {{streamlen}}
+>>
+stream
+app.alert('Will Print');
+endstream
+endobj
+{{object 16 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 17 0 R
+>>
+endobj
+{{object 17 0}} <<
+  {{streamlen}}
+>>
+stream
+app.alert('Did Print');
+endstream
+endobj
+{{object 18 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 19 0 R
+>>
+endobj
+{{object 19 0}} <<
+  {{streamlen}}
+>>
+stream
+app.alert('Will Save');
+endstream
+endobj
+{{object 20 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 21 0 R
+>>
+endobj
+{{object 21 0}} <<
+  {{streamlen}}
+>>
+stream
+app.alert('Did Save');
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Size 14
+  /Root 1 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/document_aactions.pdf b/testing/resources/document_aactions.pdf
new file mode 100644
index 0000000..b921772
--- /dev/null
+++ b/testing/resources/document_aactions.pdf
@@ -0,0 +1,164 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 3 0 R
+  /AA 11 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 3
+  /Kids [4 0 R]
+>>
+endobj
+3 0 obj <<
+  /CO [9 0 R]
+  /Fields [
+    9 0 R
+    7 0 R
+  ]
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+7 0 obj <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txtName2)
+  /F 4
+  /AP <</N 8 0 R>>
+  /Rect [20 20 400 60]
+>>
+endobj
+8 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+>>
+endobj
+9 0 obj <<
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txtName1)
+  /F 4
+  /AP <</N 10 0 R>>
+  /Rect [200 200 400 260]
+>>
+endobj
+10 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+>>
+endobj
+11 0 obj <<
+  /WP 14 0 R
+  /DP 16 0 R
+  /WS 18 0 R
+  /DS 20 0 R
+>>
+endobj
+12 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 13 0 R
+>>
+endobj
+13 0 obj <<
+  /Length 25
+>>
+stream
+app.alert('Will Close');
+endstream
+endobj
+14 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 15 0 R
+>>
+endobj
+15 0 obj <<
+  /Length 25
+>>
+stream
+app.alert('Will Print');
+endstream
+endobj
+16 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 17 0 R
+>>
+endobj
+17 0 obj <<
+  /Length 24
+>>
+stream
+app.alert('Did Print');
+endstream
+endobj
+18 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 19 0 R
+>>
+endobj
+19 0 obj <<
+  /Length 24
+>>
+stream
+app.alert('Will Save');
+endstream
+endobj
+20 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 21 0 R
+>>
+endobj
+21 0 obj <<
+  /Length 23
+>>
+stream
+app.alert('Did Save');
+endstream
+endobj
+xref
+0 22
+0000000000 65535 f 
+0000000015 00000 n 
+0000000099 00000 n 
+0000000162 00000 n 
+0000000233 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000310 00000 n 
+0000000440 00000 n 
+0000000509 00000 n 
+0000000643 00000 n 
+0000000713 00000 n 
+0000000787 00000 n 
+0000000855 00000 n 
+0000000932 00000 n 
+0000001000 00000 n 
+0000001077 00000 n 
+0000001145 00000 n 
+0000001221 00000 n 
+0000001289 00000 n 
+0000001365 00000 n 
+0000001433 00000 n 
+trailer <<
+  /Size 14
+  /Root 1 0 R
+>>
+startxref
+1508
+%%EOF
diff --git a/testing/resources/empty_xref.pdf b/testing/resources/empty_xref.pdf
new file mode 100644
index 0000000..ed02848
--- /dev/null
+++ b/testing/resources/empty_xref.pdf
@@ -0,0 +1,70 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000303 00000 n 
+0000000381 00000 n 
+0000000457 00000 n 
+trailer<< /Root 1 0 R /Size 7 >>
+startxref
+578
+%%EOF
+xref
+0 0
+trailer<< /Root 1 0 R /Size 0 /Prev 578 >>
+startxref
+780
+%%EOF
diff --git a/testing/resources/encrypted_hello_world_r2.pdf b/testing/resources/encrypted_hello_world_r2.pdf
new file mode 100644
index 0000000..79ebbdc
--- /dev/null
+++ b/testing/resources/encrypted_hello_world_r2.pdf
Binary files differ
diff --git a/testing/resources/encrypted_hello_world_r2_bad_okey.pdf b/testing/resources/encrypted_hello_world_r2_bad_okey.pdf
new file mode 100644
index 0000000..637812b
--- /dev/null
+++ b/testing/resources/encrypted_hello_world_r2_bad_okey.pdf
Binary files differ
diff --git a/testing/resources/encrypted_hello_world_r3.pdf b/testing/resources/encrypted_hello_world_r3.pdf
new file mode 100644
index 0000000..d74f05e
--- /dev/null
+++ b/testing/resources/encrypted_hello_world_r3.pdf
Binary files differ
diff --git a/testing/resources/encrypted_hello_world_r3_bad_okey.pdf b/testing/resources/encrypted_hello_world_r3_bad_okey.pdf
new file mode 100644
index 0000000..721c20b
--- /dev/null
+++ b/testing/resources/encrypted_hello_world_r3_bad_okey.pdf
Binary files differ
diff --git a/testing/resources/encrypted_hello_world_r5.pdf b/testing/resources/encrypted_hello_world_r5.pdf
new file mode 100644
index 0000000..82c550e
--- /dev/null
+++ b/testing/resources/encrypted_hello_world_r5.pdf
Binary files differ
diff --git a/testing/resources/encrypted_hello_world_r6.pdf b/testing/resources/encrypted_hello_world_r6.pdf
new file mode 100644
index 0000000..b507a15
--- /dev/null
+++ b/testing/resources/encrypted_hello_world_r6.pdf
Binary files differ
diff --git a/testing/resources/find_text_consecutive.in b/testing/resources/find_text_consecutive.in
new file mode 100644
index 0000000..9e35e1d
--- /dev/null
+++ b/testing/resources/find_text_consecutive.in
@@ -0,0 +1,45 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+{{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(aaaaaaaaaa bbbbbbbbb) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/find_text_consecutive.pdf b/testing/resources/find_text_consecutive.pdf
new file mode 100644
index 0000000..5083d75
--- /dev/null
+++ b/testing/resources/find_text_consecutive.pdf
@@ -0,0 +1,57 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+/Length 51
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(aaaaaaaaaa bbbbbbbbb) Tj
+ET
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000287 00000 n 
+0000000365 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+465
+%%EOF
diff --git a/testing/resources/font_matrix.in b/testing/resources/font_matrix.in
new file mode 100644
index 0000000..6057e86
--- /dev/null
+++ b/testing/resources/font_matrix.in
@@ -0,0 +1,59 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+  /MediaBox [ 0 0 100 200 ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+12 0 0 10 30 40 cm
+BT
+3 5 Td /F1 1 Tf (A1) Tj
+ET
+Q
+q
+1 0 0 1 20 60 cm
+BT
+12 0 0 10 30 20 Tm
+-1 -2 Td /F1 1 Tf (A2) Tj
+ET
+Q
+q
+1 0 0 0.833333 10 80 cm
+BT
+50 60 Td /F1 12 Tf (A3) Tj
+ET
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/font_matrix.pdf b/testing/resources/font_matrix.pdf
new file mode 100644
index 0000000..435a126
--- /dev/null
+++ b/testing/resources/font_matrix.pdf
@@ -0,0 +1,71 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+  /MediaBox [ 0 0 100 200 ]
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Length 186
+>>
+stream
+q
+12 0 0 10 30 40 cm
+BT
+3 5 Td /F1 1 Tf (A1) Tj
+ET
+Q
+q
+1 0 0 1 20 60 cm
+BT
+12 0 0 10 30 20 Tm
+-1 -2 Td /F1 1 Tf (A2) Tj
+ET
+Q
+q
+1 0 0 0.833333 10 80 cm
+BT
+50 60 Td /F1 12 Tf (A3) Tj
+ET
+Q
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000133 00000 n 
+0000000287 00000 n 
+0000000365 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+603
+%%EOF
diff --git a/testing/resources/font_tests/name_windows.ttf b/testing/resources/font_tests/name_windows.ttf
new file mode 100644
index 0000000..019ecef
--- /dev/null
+++ b/testing/resources/font_tests/name_windows.ttf
Binary files differ
diff --git a/testing/resources/font_weight.in b/testing/resources/font_weight.in
new file mode 100644
index 0000000..7e79cde
--- /dev/null
+++ b/testing/resources/font_weight.in
@@ -0,0 +1,88 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 7 0 R
+    >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+0 30 Td
+/F1 12 Tf
+(1) Tj
+/F2 12 Tf
+(X) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /FontDescriptor 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /FontName /Helvetica
+  /StemV 80
+  /ItalicAngle 0
+  /Ascent 776
+  /Flags 0
+  /FontBBox [-250 -236 2827 1000]
+  /CapHeight 763
+  /Descent -223
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /TimesNewRomanPSMT
+  /Encoding /WinAnsiEncoding
+  /FirstChar 31
+  /FontDescriptor 8 0 R
+  /LastChar 252
+>>
+endobj
+{{object 8 0}} <<
+  /Type /FontDescriptor
+  /Ascent 891
+  /CapHeight 656
+  /Descent -216
+  /Flags 34
+  /FontBBox [-568 -307 2000 1007]
+  /FontFamily (Times New Roman)
+  /FontName /TimesNewRomanPSMT
+  /FontStretch /Normal
+  /FontWeight 400
+  /ItalicAngle 0
+  /MissingWidth 778
+  /StemV 82
+  /XHeight -546
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/font_weight.pdf b/testing/resources/font_weight.pdf
new file mode 100644
index 0000000..78049d0
--- /dev/null
+++ b/testing/resources/font_weight.pdf
@@ -0,0 +1,103 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+      /F2 7 0 R
+    >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 48
+>>
+stream
+BT
+0 30 Td
+/F1 12 Tf
+(1) Tj
+/F2 12 Tf
+(X) Tj
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /FontDescriptor 6 0 R
+>>
+endobj
+6 0 obj <<
+  /Type /FontDescriptor
+  /FontName /Helvetica
+  /StemV 80
+  /ItalicAngle 0
+  /Ascent 776
+  /Flags 0
+  /FontBBox [-250 -236 2827 1000]
+  /CapHeight 763
+  /Descent -223
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /TimesNewRomanPSMT
+  /Encoding /WinAnsiEncoding
+  /FirstChar 31
+  /FontDescriptor 8 0 R
+  /LastChar 252
+>>
+endobj
+8 0 obj <<
+  /Type /FontDescriptor
+  /Ascent 891
+  /CapHeight 656
+  /Descent -216
+  /Flags 34
+  /FontBBox [-568 -307 2000 1007]
+  /FontFamily (Times New Roman)
+  /FontName /TimesNewRomanPSMT
+  /FontStretch /Normal
+  /FontWeight 400
+  /ItalicAngle 0
+  /MissingWidth 778
+  /StemV 82
+  /XHeight -546
+>>
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000303 00000 n 
+0000000402 00000 n 
+0000000502 00000 n 
+0000000691 00000 n 
+0000000863 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+1170
+%%EOF
diff --git a/testing/resources/fonts/Ahem.ttf b/testing/resources/fonts/Ahem.ttf
new file mode 100644
index 0000000..4d4785a
--- /dev/null
+++ b/testing/resources/fonts/Ahem.ttf
Binary files differ
diff --git a/testing/resources/form_object.in b/testing/resources/form_object.in
new file mode 100644
index 0000000..9d196ba
--- /dev/null
+++ b/testing/resources/form_object.in
@@ -0,0 +1,80 @@
+{{header}}
+{{object 1 0}} <<
+/FormType 1
+/Subtype /Form
+/Resources
+<<
+/Font
+<<
+/F1 2 0 R
+>>
+>>
+/Type /XObject
+/BBox [0 0 612 446]
+/Length 0
+>>
+stream
+  BT
+  /F1 24 Tf
+  1 0 0 1 260 254 Tm
+  (Hello)Tj
+  ET
+  BT
+  /F1 24 Tf
+  1 0 0 1 260 204 Tm
+  (World)Tj
+  ET
+
+endstream
+endobj
+{{object 3 0}} <<
+/ProcSet [/PDF]
+/XObject
+<<
+/Im1 1 0 R
+>>
+>>
+endobj
+{{object 4 0}} <<
+/Resources 3 0 R
+/Parent 5 0 R
+/Contents 6 0 R
+/Type /Page
+/MediaBox [0 0 62 69]
+>>
+endobj
+{{object 6 0}} <<
+/Length 0
+>>
+stream
+q
+1 0 0 1 -260 -203 cm
+/Im1 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+/Kids [4 0 R]
+/Type /Pages
+/Count 1
+>>
+endobj
+{{object 2 0}} <<
+/Subtype /Type1
+/Name /F1
+/Type /Font
+/BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} <<
+/Type /Catalog
+/Pages 5 0 R
+>>
+endobj
+{{xref}}
+trailer <<
+/Root 7 0 R
+/Size 8
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/form_object.pdf b/testing/resources/form_object.pdf
new file mode 100644
index 0000000..543f0bd
--- /dev/null
+++ b/testing/resources/form_object.pdf
@@ -0,0 +1,91 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+/FormType 1
+/Subtype /Form
+/Resources
+<<
+/Font
+<<
+/F1 2 0 R
+>>
+>>
+/Type /XObject
+/BBox [0 0 612 446]
+/Length 0
+>>
+stream
+  BT
+  /F1 24 Tf
+  1 0 0 1 260 254 Tm
+  (Hello)Tj
+  ET
+  BT
+  /F1 24 Tf
+  1 0 0 1 260 204 Tm
+  (World)Tj
+  ET
+
+endstream
+endobj
+3 0 obj <<
+/ProcSet [/PDF]
+/XObject
+<<
+/Im1 1 0 R
+>>
+>>
+endobj
+4 0 obj <<
+/Resources 3 0 R
+/Parent 5 0 R
+/Contents 6 0 R
+/Type /Page
+/MediaBox [0 0 62 69]
+>>
+endobj
+6 0 obj <<
+/Length 0
+>>
+stream
+q
+1 0 0 1 -260 -203 cm
+/Im1 Do
+Q
+endstream
+endobj
+5 0 obj <<
+/Kids [4 0 R]
+/Type /Pages
+/Count 1
+>>
+endobj
+2 0 obj <<
+/Subtype /Type1
+/Name /F1
+/Type /Font
+/BaseFont /Helvetica
+>>
+endobj
+7 0 obj <<
+/Type /Catalog
+/Pages 5 0 R
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000578 00000 n 
+0000000275 00000 n 
+0000000338 00000 n 
+0000000521 00000 n 
+0000000440 00000 n 
+0000000658 00000 n 
+trailer <<
+/Root 7 0 R
+/Size 8
+>>
+startxref
+707
+%%EOF
diff --git a/testing/resources/goto_action.in b/testing/resources/goto_action.in
new file mode 100644
index 0000000..0e7d73f
--- /dev/null
+++ b/testing/resources/goto_action.in
@@ -0,0 +1,46 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /T (txtName)
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+{{object 5 0}} <<
+  /S /GoTo
+  /D [1 2 3]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/goto_action.pdf b/testing/resources/goto_action.pdf
new file mode 100644
index 0000000..93be39c
--- /dev/null
+++ b/testing/resources/goto_action.pdf
@@ -0,0 +1,58 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /T (txtName)
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+5 0 obj <<
+  /S /GoTo
+  /D [1 2 3]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000230 00000 n 
+0000000464 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+509
+%%EOF
diff --git a/testing/resources/hello_world_compressed_stream.pdf b/testing/resources/hello_world_compressed_stream.pdf
new file mode 100644
index 0000000..7fc799a
--- /dev/null
+++ b/testing/resources/hello_world_compressed_stream.pdf
Binary files differ
diff --git a/testing/resources/hello_world_split_streams.in b/testing/resources/hello_world_split_streams.in
new file mode 100644
index 0000000..667dd36
--- /dev/null
+++ b/testing/resources/hello_world_split_streams.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents [6 0 R 7 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Greetings, world!) Tj
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hello_world_split_streams.pdf b/testing/resources/hello_world_split_streams.pdf
new file mode 100644
index 0000000..824f003
--- /dev/null
+++ b/testing/resources/hello_world_split_streams.pdf
@@ -0,0 +1,79 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents [6 0 R 7 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+7 0 obj <<
+  /Length 45
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Greetings, world!) Tj
+endstream
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000311 00000 n 
+0000000389 00000 n 
+0000000465 00000 n 
+0000000599 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+695
+%%EOF
diff --git a/testing/resources/hint_table_102p.bin b/testing/resources/hint_table_102p.bin
new file mode 100644
index 0000000..4008b0d
--- /dev/null
+++ b/testing/resources/hint_table_102p.bin
Binary files differ
diff --git a/testing/resources/javascript/annot_properties.in b/testing/resources/javascript/annot_properties.in
new file mode 100644
index 0000000..939365c
--- /dev/null
+++ b/testing/resources/javascript/annot_properties.in
@@ -0,0 +1,93 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 20 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [10 0 R]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [22 0 R]
+  /Tabs /R
+>>
+endobj
+{{object 20 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 21 0 R
+>>
+endobj
+{{object 21 0}} <<
+  {{streamlen}}
+>>
+stream
+app.alert("Test that non-existent annot fetch gives error");
+try {
+  var nonesuch = this.getAnnot(0, "nonesuch");
+  app.alert("nonesuch: " + typeof nonesuch);
+} catch (e) {
+  app.alert("SUCCESS: " + e);
+}
+app.alert("Test initial cases");
+try {
+  var annot = this.getAnnot(0, "Annot-1");
+  app.alert("annot: " + typeof annot);
+  app.alert("hidden: " + annot.hidden);
+  app.alert("name: " + annot.name);
+  app.alert("type: " + annot.type);
+
+  annot.hidden = true;
+  app.alert("hidden now: " + annot.hidden);
+  annot.hidden = false;
+  app.alert("hidden now: " + annot.hidden);
+
+  annot.name = "nonesuch";
+  app.alert("name now: " + annot.name);
+} catch (e) {
+  app.alert("ERROR: " + e);
+}
+app.alert("Test assigment to read-only property gives error");
+try {
+  annot.type = 42;
+} catch (e) {
+  app.alert("SUCCESS: " + e);
+}
+app.alert("Test lookup after name change gives error");
+try {
+  annot = this.getAnnot(0, "Annot-1");
+  app.alert("annot after name change: " + typeof annot);
+} catch (e) {
+  app.alert("SUCCESS: " + e);
+}
+app.alert("Test lookup under changed name");
+try {
+  nonesuch = this.getAnnot(0, "nonesuch");
+  app.alert("nonesuch after name change: " + typeof nonesuch);
+} catch (e) {
+  app.alert("ERROR: " + e);
+}
+endstream
+endobj
+{{object 22 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /Rect [475 681 512 690]
+  /NM (Annot-1)
+  /F 2
+  /QuadPoints [475 688 512 688 475 679 512 679]
+  /C [0.0001108646 0.001760244 0.9982184]
+  /Contents ()
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/annot_properties_expected.txt b/testing/resources/javascript/annot_properties_expected.txt
new file mode 100644
index 0000000..54548e6
--- /dev/null
+++ b/testing/resources/javascript/annot_properties_expected.txt
@@ -0,0 +1,16 @@
+Alert: Test that non-existent annot fetch gives error
+Alert: SUCCESS: Document.getAnnot: Object no longer exists.
+Alert: Test initial cases
+Alert: annot: object
+Alert: hidden: true
+Alert: name: Annot-1
+Alert: type: Highlight
+Alert: hidden now: true
+Alert: hidden now: false
+Alert: name now: nonesuch
+Alert: Test assigment to read-only property gives error
+Alert: SUCCESS: Annot.type: Cannot assign to readonly property.
+Alert: Test lookup after name change gives error
+Alert: SUCCESS: Document.getAnnot: Object no longer exists.
+Alert: Test lookup under changed name
+Alert: nonesuch after name change: object
diff --git a/testing/resources/javascript/app_alert.in b/testing/resources/javascript/app_alert.in
deleted file mode 100644
index 75aecc9..0000000
--- a/testing/resources/javascript/app_alert.in
+++ /dev/null
@@ -1,71 +0,0 @@
-{{header}}
-{{object 1 0}} <<
-  /Type /Catalog
-  /Pages 2 0 R
-  /OpenAction 10 0 R
->>
-endobj
-{{object 2 0}} <<
-  /Type /Pages
-  /Count 1
-  /Kids [
-    3 0 R
-  ]
->>
-endobj
-% Page number 0.
-{{object 3 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /Contents [21 0 R]
-  /MediaBox [0 0 612 792]
->>
-% OpenAction action
-{{object 10 0}} <<
-  /Type /Action
-  /S /JavaScript
-  /JS 11 0 R
->>
-endobj
-% JS program to exexute
-{{object 11 0}} <<
->>
-stream
-app.alert("This test passes if alert() logs output under the test utility.");
-app.alert("message", 1, 2, "title");
-app.alert({"cMsg": "message", "cTitle": "title"});
-app.alert({"cMsg": "message", "cTitle": "title", "nIcon": 3, "nType": 4});
-app.alert(undefined);
-app.alert(null);
-app.alert(true);
-app.alert(false);
-app.alert(42);
-app.alert([1, 2, 3]);
-app.alert([1, 2, {"color": "red"}]);
-app.alert({"color": "red"}, 5, 6, "title"); 
-try {
-  app.alert();
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-try {
-  app.alert({});
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-try {
-  app.alert({"color": "red", "size": 42});
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-endstream
-endobj
-{{xref}}
-trailer <<
-  /Root 1 0 R
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/javascript/app_alert_expected.txt b/testing/resources/javascript/app_alert_expected.txt
deleted file mode 100644
index b44fa73..0000000
--- a/testing/resources/javascript/app_alert_expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Alert: This test passes if alert() logs output under the test utility.
-title[icon=1,type=2]: message
-title: message
-title[icon=3,type=4]: message
-Alert: undefined
-Alert: null
-Alert: true
-Alert: false
-Alert: 42
-Alert: [1, 2, 3]
-Alert: [1, 2, [object Object]]
-title[icon=5,type=6]: [object Object]
-Alert: Caught expected error app.alert: Incorrect number of parameters passed to function.
-Alert: Caught expected error app.alert: Incorrect number of parameters passed to function.
-Alert: Caught expected error app.alert: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/app_mailmsg.in b/testing/resources/javascript/app_mailmsg.in
deleted file mode 100644
index 990709b..0000000
--- a/testing/resources/javascript/app_mailmsg.in
+++ /dev/null
@@ -1,72 +0,0 @@
-{{header}}
-{{object 1 0}} <<
-  /Type /Catalog
-  /Pages 2 0 R
-  /OpenAction 10 0 R
->>
-endobj
-{{object 2 0}} <<
-  /Type /Pages
-  /Count 1
-  /Kids [
-    3 0 R
-  ]
->>
-endobj
-% Page number 0.
-{{object 3 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /Contents [21 0 R]
-  /MediaBox [0 0 612 792]
->>
-% OpenAction action
-{{object 10 0}} <<
-  /Type /Action
-  /S /JavaScript
-  /JS 11 0 R
->>
-endobj
-% JS program to exexute
-{{object 11 0}} <<
->>
-stream
-app.alert("This test passes if mailMsg() logs output under the test utility.");
-app.mailMsg(true);
-app.mailMsg(false, "user@example.com");
-app.mailMsg(false, "user@example.com", "cc@example.com",
-            "bcc@example.com", "subject", "body");
-app.mailMsg({"bUI": true});
-app.mailMsg({"bUI": false, "cTo": "user@example.com"});
-app.mailMsg({"bUI": false,
-             "cTo": "user@example.com",
-             "cCc": "cc@example.com",
-             "cBcc": "bcc@example.com",
-             "cSubject": "subject",
-             "cMsg": "body"});
-try {
-  app.mailMsg();
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-try {
-  app.mailMsg(false);
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-try {
-  app.mailMsg({"color": "red", "size": 42});
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-endstream
-endobj
-{{xref}}
-trailer <<
-  /Root 1 0 R
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/javascript/app_mailmsg_expected.txt b/testing/resources/javascript/app_mailmsg_expected.txt
deleted file mode 100644
index 59928ae..0000000
--- a/testing/resources/javascript/app_mailmsg_expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Alert: This test passes if mailMsg() logs output under the test utility.
-Mail Msg: 1, to=, cc=, bcc=, subject=, body=
-Mail Msg: 0, to=user@example.com, cc=, bcc=, subject=, body=
-Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=subject, body=body
-Mail Msg: 1, to=, cc=, bcc=, subject=, body=
-Mail Msg: 0, to=user@example.com, cc=, bcc=, subject=, body=
-Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=subject, body=body
-Alert: Caught expected error app.mailMsg: Incorrect number of parameters passed to function.
-Alert: Caught expected error app.mailMsg: Incorrect number of parameters passed to function.
-Alert: Caught expected error app.mailMsg: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/app_methods.in b/testing/resources/javascript/app_methods.in
new file mode 100644
index 0000000..81fa660
--- /dev/null
+++ b/testing/resources/javascript/app_methods.in
@@ -0,0 +1,97 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include expect.js}}
+
+try {
+  expect("app.alert('message', 1, 2, 'title')", 0);
+  expect("app.alert({'cMsg': 'message', 'cTitle': 'title'})", 0);
+  expect("app.alert({'cMsg': 'message', 'cTitle': 'title', 'nIcon': 3, 'nType': 4})", 0);
+  expect("app.alert(undefined)", 0);
+  expect("app.alert(null)", 0);
+  expect("app.alert(true)", 0);
+  expect("app.alert(false)", 0);
+  expect("app.alert(42)", 0);
+  expect("app.alert([1, 2, 3])", 0);
+  expect("app.alert([1, 2, {'color': 'red'}])", 0);
+  expect("app.alert({'color': 'red'}, 5, 6, 'title')", 0);
+  expectError("app.alert()");
+  expectError("app.alert({})");
+  expectError("app.alert({'color': 'red', 'size': 42})");
+
+  expect("app.beep(1)", undefined);
+
+  expectError("app.browseForDoc()");
+  expect("app.execDialog()", undefined);
+  expectError("app.execMenuItem()");
+  expect("app.findComponent()", undefined);
+  expect("app.goBack()", undefined);
+  expect("app.goForward()", undefined);
+  expect("app.launchURL()", undefined);
+  expectError("app.newDoc()");
+  expect("app.newFDF()", undefined);
+  expectError("app.openDoc()");
+  expect("app.openFDF()", undefined);
+  expectError("app.popUpMenuEx()");
+  expectError("app.popUpMenu()");
+
+  expect("app.mailMsg(true)", undefined);
+  expect("app.mailMsg(false, 'user@example.com')", undefined);
+  expect("app.mailMsg(false, 'user@example.com', 'cc@example.com', " +
+         "'bcc@example.com', 'subject', 'body')", undefined);
+  expect("app.mailMsg({'bUI': true})", undefined);
+  expect("app.mailMsg({'bUI': false, 'cTo': 'user@example.com'})", undefined);
+  expect("app.mailMsg({'bUI': false, 'cTo': 'user@example.com', " +
+         "'cCc': 'cc@example.com', 'cBcc': 'bcc@example.com', " +
+         "'cSubject': 'subject', 'cMsg': 'body'})", undefined);
+  expectError("app.mailMsg()");
+  expectError("app.mailMsg(false)");
+  expectError("app.mailMsg({'color': 'red', 'size': 42})");
+
+  expect("app.response('question')", 'No');
+  expect("app.response('question', 'title', 'default', true, 'label')", 'No');
+  expect("app.response({'cQuestion': 'question'})", 'No');
+  expect("app.response({'cQuestion': 'question', 'cTitle': 'title', " +
+         "'cDefault': 'default', 'bPassword': true, 'cLabel': 'label'})", 'No');
+  expectError("app.response()");
+  expectError("app.response({})");
+
+} catch (e) {
+  app.alert('Truly unexpected error: ' + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/app_methods_expected.txt b/testing/resources/javascript/app_methods_expected.txt
new file mode 100644
index 0000000..015ff28
--- /dev/null
+++ b/testing/resources/javascript/app_methods_expected.txt
@@ -0,0 +1,65 @@
+title[icon=1,type=2]: message
+Alert: PASS: app.alert('message', 1, 2, 'title') = 0
+title: message
+Alert: PASS: app.alert({'cMsg': 'message', 'cTitle': 'title'}) = 0
+title[icon=3,type=4]: message
+Alert: PASS: app.alert({'cMsg': 'message', 'cTitle': 'title', 'nIcon': 3, 'nType': 4}) = 0
+Alert: undefined
+Alert: PASS: app.alert(undefined) = 0
+Alert: null
+Alert: PASS: app.alert(null) = 0
+Alert: true
+Alert: PASS: app.alert(true) = 0
+Alert: false
+Alert: PASS: app.alert(false) = 0
+Alert: 42
+Alert: PASS: app.alert(42) = 0
+Alert: [1, 2, 3]
+Alert: PASS: app.alert([1, 2, 3]) = 0
+Alert: [1, 2, [object Object]]
+Alert: PASS: app.alert([1, 2, {'color': 'red'}]) = 0
+title[icon=5,type=6]: [object Object]
+Alert: PASS: app.alert({'color': 'red'}, 5, 6, 'title') = 0
+Alert: PASS: app.alert() threw app.alert: Incorrect number of parameters passed to function.
+Alert: PASS: app.alert({}) threw app.alert: Incorrect number of parameters passed to function.
+Alert: PASS: app.alert({'color': 'red', 'size': 42}) threw app.alert: Incorrect number of parameters passed to function.
+BEEP!!! 1
+Alert: PASS: app.beep(1) = undefined
+Alert: FAIL: app.browseForDoc() = undefined, expected to throw
+Alert: PASS: app.execDialog() = undefined
+Alert: PASS: app.execMenuItem() threw app.execMenuItem: Operation not supported.
+Alert: PASS: app.findComponent() = undefined
+Alert: PASS: app.goBack() = undefined
+Alert: PASS: app.goForward() = undefined
+Alert: PASS: app.launchURL() = undefined
+Alert: PASS: app.newDoc() threw app.newDoc: Operation not supported.
+Alert: PASS: app.newFDF() = undefined
+Alert: PASS: app.openDoc() threw app.openDoc: Operation not supported.
+Alert: PASS: app.openFDF() = undefined
+Alert: PASS: app.popUpMenuEx() threw app.popUpMenuEx: Operation not supported.
+Alert: PASS: app.popUpMenu() threw app.popUpMenu: Operation not supported.
+Mail Msg: 1, to=, cc=, bcc=, subject=, body=
+Alert: PASS: app.mailMsg(true) = undefined
+Mail Msg: 0, to=user@example.com, cc=, bcc=, subject=, body=
+Alert: PASS: app.mailMsg(false, 'user@example.com') = undefined
+Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=subject, body=body
+Alert: PASS: app.mailMsg(false, 'user@example.com', 'cc@example.com', 'bcc@example.com', 'subject', 'body') = undefined
+Mail Msg: 1, to=, cc=, bcc=, subject=, body=
+Alert: PASS: app.mailMsg({'bUI': true}) = undefined
+Mail Msg: 0, to=user@example.com, cc=, bcc=, subject=, body=
+Alert: PASS: app.mailMsg({'bUI': false, 'cTo': 'user@example.com'}) = undefined
+Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=subject, body=body
+Alert: PASS: app.mailMsg({'bUI': false, 'cTo': 'user@example.com', 'cCc': 'cc@example.com', 'cBcc': 'bcc@example.com', 'cSubject': 'subject', 'cMsg': 'body'}) = undefined
+Alert: PASS: app.mailMsg() threw app.mailMsg: Incorrect number of parameters passed to function.
+Alert: PASS: app.mailMsg(false) threw app.mailMsg: Incorrect number of parameters passed to function.
+Alert: PASS: app.mailMsg({'color': 'red', 'size': 42}) threw app.mailMsg: Incorrect number of parameters passed to function.
+PDF: question, defaultValue=, label=, isPassword=0, length=2048
+Alert: PASS: app.response('question') = No
+title: question, defaultValue=default, label=label, isPassword=1, length=2048
+Alert: PASS: app.response('question', 'title', 'default', true, 'label') = No
+PDF: question, defaultValue=, label=, isPassword=0, length=2048
+Alert: PASS: app.response({'cQuestion': 'question'}) = No
+title: question, defaultValue=default, label=label, isPassword=1, length=2048
+Alert: PASS: app.response({'cQuestion': 'question', 'cTitle': 'title', 'cDefault': 'default', 'bPassword': true, 'cLabel': 'label'}) = No
+Alert: PASS: app.response() threw app.response: Incorrect number of parameters passed to function.
+Alert: PASS: app.response({}) threw app.response: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/app_properties.in b/testing/resources/javascript/app_properties.in
new file mode 100644
index 0000000..1355e64
--- /dev/null
+++ b/testing/resources/javascript/app_properties.in
@@ -0,0 +1,125 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 4
+  /Kids [
+    3 0 R
+    4 0 R
+    5 0 R
+    6 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+% Page number 1.
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+% Page number 2.
+{{object 5 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+% Page number 3.
+{{object 6 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+
+% Info
+{{object 9 0}} <<
+  /Author (Joe Random Author)
+  /Creator (Joe Random Creator)
+>>
+endobj
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+var app_props = [
+  'activeDocs',
+  'calculate',
+  'formsVersion',
+  'fs',
+  'fullscreen',
+  'language',
+  'media',
+  'platform',
+  'runtimeHighlight',
+  'viewerType',
+  'viewerVariation',
+  'viewerVersion'
+];
+
+function testGetProps(props) {
+  app.alert('*** Getting properties ***');
+  for (var i = 0; i < props.length; ++i) {
+    try {
+      var expr1 = "app." + props[i];
+      var expr2 = "typeof " + expr1;
+      app.alert(expr1 + " is " + eval(expr2) + ' ' + eval(expr1));
+    } catch (e) {
+      app.alert("ERROR: " + e.toString());
+    }
+  }
+}
+
+function testSetProps(props) {
+  app.alert('*** Setting properties ***');
+  for (var i = 0; i < props.length; ++i) {
+    try {
+      var expr1 = "app." + props[i] + ' = 3;'
+      app.alert(expr1 + " yields " + eval(expr1));
+    } catch (e) {
+      app.alert("ERROR: " + e.toString());
+    }
+  }
+}
+
+testGetProps(app_props);
+testSetProps(app_props);
+testGetProps(app_props);
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Info 9 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/app_properties_expected.txt b/testing/resources/javascript/app_properties_expected.txt
new file mode 100644
index 0000000..a221272
--- /dev/null
+++ b/testing/resources/javascript/app_properties_expected.txt
@@ -0,0 +1,39 @@
+Alert: *** Getting properties ***
+Alert: app.activeDocs is object [object global]
+Alert: app.calculate is boolean true
+Alert: app.formsVersion is number 7
+Alert: ERROR: app.fs: Operation not supported.
+Alert: ERROR: app.fullscreen: Operation not supported.
+Alert: app.language is string ENU
+Alert: ERROR: app.media: Operation not supported.
+Alert: app.platform is string WIN
+Alert: app.runtimeHighlight is boolean false
+Alert: app.viewerType is string pdfium
+Alert: app.viewerVariation is string Full
+Alert: app.viewerVersion is number 8
+Alert: *** Setting properties ***
+Alert: ERROR: app.activeDocs: Operation not supported.
+Alert: app.calculate = 3; yields 3
+Alert: ERROR: app.formsVersion: Operation not supported.
+Alert: ERROR: app.fs: Operation not supported.
+Alert: ERROR: app.fullscreen: Operation not supported.
+Alert: ERROR: app.language: Operation not supported.
+Alert: ERROR: app.media: Operation not supported.
+Alert: ERROR: app.platform: Operation not supported.
+Alert: app.runtimeHighlight = 3; yields 3
+Alert: ERROR: app.viewerType: Operation not supported.
+Alert: ERROR: app.viewerVariation: Operation not supported.
+Alert: ERROR: app.viewerVersion: Operation not supported.
+Alert: *** Getting properties ***
+Alert: app.activeDocs is object [object global]
+Alert: app.calculate is boolean true
+Alert: app.formsVersion is number 7
+Alert: ERROR: app.fs: Operation not supported.
+Alert: ERROR: app.fullscreen: Operation not supported.
+Alert: app.language is string ENU
+Alert: ERROR: app.media: Operation not supported.
+Alert: app.platform is string WIN
+Alert: app.runtimeHighlight is boolean true
+Alert: app.viewerType is string pdfium
+Alert: app.viewerVariation is string Full
+Alert: app.viewerVersion is number 8
diff --git a/testing/resources/javascript/app_props.in b/testing/resources/javascript/app_props.in
deleted file mode 100644
index 89511b0..0000000
--- a/testing/resources/javascript/app_props.in
+++ /dev/null
@@ -1,124 +0,0 @@
-{{header}}
-{{object 1 0}} <<
-  /Type /Catalog
-  /Pages 2 0 R
-  /OpenAction 10 0 R
->>
-endobj
-{{object 2 0}} <<
-  /Type /Pages
-  /Count 4
-  /Kids [
-    3 0 R
-    4 0 R
-    5 0 R
-    6 0 R
-  ]
->>
-endobj
-% Page number 0.
-{{object 3 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-% Page number 1.
-{{object 4 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-% Page number 2.
-{{object 5 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-% Page number 3.
-{{object 6 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-
-% Info
-{{object 9 0}} <<
-  /Author (Joe Random Author)
-  /Creator (Joe Random Creator)
->>
-endobj
-% OpenAction action
-{{object 10 0}} <<
-  /Type /Action
-  /S /JavaScript
-  /JS 11 0 R
->>
-endobj
-% JS program to exexute
-{{object 11 0}} <<
->>
-stream
-var app_props = [
-  'activeDocs',
-  'calculate',
-  'formsVersion',
-  'fs',
-  'fullscreen',
-  'language',
-  'media',
-  'platform',
-  'runtimeHighlight',
-  'viewerType',
-  'viewerVariation',
-  'viewerVersion'
-];
-
-function testGetProps(props) {
-  app.alert('*** Getting properties ***');
-  for (var i = 0; i < props.length; ++i) {
-    try {
-      var expr1 = "app." + props[i];
-      var expr2 = "typeof " + expr1;
-      app.alert(expr1 + " is " + eval(expr2) + ' ' + eval(expr1));
-    } catch (e) {
-      app.alert("ERROR: " + e.toString());
-    }
-  }
-}
-
-function testSetProps(props) {
-  app.alert('*** Setting properties ***');
-  for (var i = 0; i < props.length; ++i) {
-    try {
-      var expr1 = "app." + props[i] + ' = 3;'
-      app.alert(expr1 + " yields " + eval(expr1));
-    } catch (e) {
-      app.alert("ERROR: " + e.toString());
-    }
-  }
-}
-
-testGetProps(app_props);
-testSetProps(app_props);
-testGetProps(app_props);
-endstream
-endobj
-{{xref}}
-trailer <<
-  /Root 1 0 R
-  /Info 9 0 R
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/javascript/app_props_expected.txt b/testing/resources/javascript/app_props_expected.txt
deleted file mode 100644
index a17e0f8..0000000
--- a/testing/resources/javascript/app_props_expected.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Alert: *** Getting properties ***
-Alert: app.activeDocs is object [object global]
-Alert: app.calculate is boolean true
-Alert: app.formsVersion is number 7
-Alert: ERROR: app.fs: 
-Alert: ERROR: app.fullscreen: 
-Alert: app.language is string ENU
-Alert: ERROR: app.media: 
-Alert: app.platform is string WIN
-Alert: app.runtimeHighlight is boolean false
-Alert: app.viewerType is string pdfium
-Alert: app.viewerVariation is string Full
-Alert: app.viewerVersion is number 8
-Alert: *** Setting properties ***
-Alert: ERROR: app.activeDocs: 
-Alert: app.calculate = 3; yields 3
-Alert: ERROR: app.formsVersion: 
-Alert: ERROR: app.fs: 
-Alert: ERROR: app.fullscreen: 
-Alert: ERROR: app.language: 
-Alert: ERROR: app.media: 
-Alert: ERROR: app.platform: 
-Alert: app.runtimeHighlight = 3; yields 3
-Alert: ERROR: app.viewerType: 
-Alert: ERROR: app.viewerVariation: 
-Alert: ERROR: app.viewerVersion: 
-Alert: *** Getting properties ***
-Alert: app.activeDocs is object [object global]
-Alert: app.calculate is boolean true
-Alert: app.formsVersion is number 7
-Alert: ERROR: app.fs: 
-Alert: ERROR: app.fullscreen: 
-Alert: app.language is string ENU
-Alert: ERROR: app.media: 
-Alert: app.platform is string WIN
-Alert: app.runtimeHighlight is boolean true
-Alert: app.viewerType is string pdfium
-Alert: app.viewerVariation is string Full
-Alert: app.viewerVersion is number 8
diff --git a/testing/resources/javascript/app_repsonse.in b/testing/resources/javascript/app_repsonse.in
deleted file mode 100644
index bd78da3..0000000
--- a/testing/resources/javascript/app_repsonse.in
+++ /dev/null
@@ -1,75 +0,0 @@
-{{header}}
-{{object 1 0}} <<
-  /Type /Catalog
-  /Pages 2 0 R
-  /OpenAction 10 0 R
->>
-endobj
-{{object 2 0}} <<
-  /Type /Pages
-  /Count 1
-  /Kids [
-    3 0 R
-  ]
->>
-endobj
-% Page number 0.
-{{object 3 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /Contents [21 0 R]
-  /MediaBox [0 0 612 792]
->>
-% OpenAction action
-{{object 10 0}} <<
-  /Type /Action
-  /S /JavaScript
-  /JS 11 0 R
->>
-endobj
-% JS program to exexute
-{{object 11 0}} <<
->>
-stream
-var result;
-try {
-  result = app.response("question");
-  app.alert("result: " + result);
-  result = app.response("question", "title", "default", true, "label");
-  app.alert("result: " + result);
-  result = app.response({"cQuestion": "question"});
-  app.alert("result: " + result);
-  result = app.response({
-    "cQuestion": "question",
-    "cTitle": "title",
-    "cDefault": "default",
-    "bPassword": true,
-    "cLabel": "label"
-  });
-  app.alert("result: " + result);
-} catch (e) {
-  app.alert("unexpected error " + e);
-}
-try {
-  app.response();
-  app.alert("unexpected success");
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-try {
-  app.response({});
-  app.alert("unexpected success");
-} catch (e) {
-  app.alert("Caught expected error " + e);
-}
-endstream
-endobj
-{{xref}}
-trailer <<
-  /Root 1 0 R
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/javascript/app_repsonse_expected.txt b/testing/resources/javascript/app_repsonse_expected.txt
deleted file mode 100644
index 8a29959..0000000
--- a/testing/resources/javascript/app_repsonse_expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-PDF: question, defaultValue=, label=, isPassword=0, length=2048
-Alert: result: No
-title: question, defaultValue=default, label=label, isPassword=1, length=2048
-Alert: result: No
-PDF: question, defaultValue=, label=, isPassword=0, length=2048
-Alert: result: No
-title: question, defaultValue=default, label=label, isPassword=1, length=2048
-Alert: result: No
-Alert: Caught expected error app.response: Incorrect number of parameters passed to function.
-Alert: Caught expected error app.response: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/apply.in b/testing/resources/javascript/apply.in
index 1342c1a..d944e83 100644
--- a/testing/resources/javascript/apply.in
+++ b/testing/resources/javascript/apply.in
@@ -32,6 +32,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 app.alert('Applying to util itself - should succeed');
@@ -68,8 +69,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/array_buffer.in b/testing/resources/javascript/array_buffer.in
index 1f3e32d..06371ba 100644
--- a/testing/resources/javascript/array_buffer.in
+++ b/testing/resources/javascript/array_buffer.in
@@ -32,6 +32,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 app.alert("This test attempts to make array buffers until exhausted");
@@ -54,15 +55,13 @@
 try {
   test(1000);
   test(2000000);
-  test(4000000000);
+  test(2147483647);
 } catch (e) {
   app.alert("Caught error " + e);
 }
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/array_buffer_expected.txt b/testing/resources/javascript/array_buffer_expected.txt
index f8f3bf2..715e638 100644
--- a/testing/resources/javascript/array_buffer_expected.txt
+++ b/testing/resources/javascript/array_buffer_expected.txt
@@ -1,5 +1,5 @@
 Alert: This test attempts to make array buffers until exhausted
 Alert: Trying size 1000
 Alert: Trying size 2000000
-Alert: Trying size 4000000000
+Alert: Trying size 2147483647
 Alert: Caught error RangeError: Array buffer allocation failed
diff --git a/testing/resources/javascript/bug_361.in b/testing/resources/javascript/bug_361.in
index 92cd8e3..4af86a6 100644
--- a/testing/resources/javascript/bug_361.in
+++ b/testing/resources/javascript/bug_361.in
@@ -45,6 +45,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 function testField(x) {
@@ -105,8 +106,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_361_expected.txt b/testing/resources/javascript/bug_361_expected.txt
index 04d72bc..9840334 100644
--- a/testing/resources/javascript/bug_361_expected.txt
+++ b/testing/resources/javascript/bug_361_expected.txt
@@ -45,5 +45,5 @@
 Alert: Answer for "[1, 2, 3, 4]" is: string [1, 2, 3, 4]
 Alert: Answer for "[object Object]" is: string [object Object]
 Alert: Answer for "{a: 1, b: 2}" is: string {a: 1, b: 2}
-Alert: Answer for "function (x) { return x+1; }" is: string function (x) { return x+1; }
+Alert: Answer for "function(x) { return x+1; }" is: string function(x) { return x+1; }
 Alert: Answer for "function(x) { return x+1; }" is: string function(x) { return x+1; }
diff --git a/testing/resources/javascript/bug_492_1.in b/testing/resources/javascript/bug_492_1.in
index f2b03b0..9d48ad5 100644
--- a/testing/resources/javascript/bug_492_1.in
+++ b/testing/resources/javascript/bug_492_1.in
@@ -52,6 +52,7 @@
 endobj
 % JS program to exexute
 {{object 21 0}} <<
+  {{streamlen}}
 >>
 stream
   var annots = this.getAnnots();
@@ -88,8 +89,6 @@
 endobj
 
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_494057.in b/testing/resources/javascript/bug_494057.in
index 188b7bf..bbfa13e 100644
--- a/testing/resources/javascript/bug_494057.in
+++ b/testing/resources/javascript/bug_494057.in
@@ -89,6 +89,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 function TestGetFieldWithDelay() {
@@ -109,8 +110,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_679642.in b/testing/resources/javascript/bug_679642.in
index 2241723..644a259 100644
--- a/testing/resources/javascript/bug_679642.in
+++ b/testing/resources/javascript/bug_679642.in
@@ -88,6 +88,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 var theName = "MyField";
@@ -116,25 +117,29 @@
 endstream
 endobj
 {{object 23 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 <?xml version="1.0" encoding="UTF-8"?>
 <xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
 endstream
 endobj
 {{object 29 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 <config></config>
 <template></template>
 endstream
 endobj
 {{object 30 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 </xdp:xdp>
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_679642_expected.txt b/testing/resources/javascript/bug_679642_expected.txt
index 9e99cc6..b576eae 100644
--- a/testing/resources/javascript/bug_679642_expected.txt
+++ b/testing/resources/javascript/bug_679642_expected.txt
@@ -1,3 +1,2 @@
 Alert: Starting ...
 Alert: Firing ...
-Alert: failed: Field.page: Object no longer exists.
diff --git a/testing/resources/javascript/bug_679643.in b/testing/resources/javascript/bug_679643.in
index e964386..c60fc0f 100644
--- a/testing/resources/javascript/bug_679643.in
+++ b/testing/resources/javascript/bug_679643.in
@@ -88,6 +88,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 var theName = "MyField";
@@ -111,25 +112,29 @@
 endstream
 endobj
 {{object 23 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 <?xml version="1.0" encoding="UTF-8"?>
 <xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
 endstream
 endobj
 {{object 29 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 <config></config>
 <template></template>
 endstream
 endobj
 {{object 30 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 </xdp:xdp>
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_679643_expected.txt b/testing/resources/javascript/bug_679643_expected.txt
index 36d4a31..b576eae 100644
--- a/testing/resources/javascript/bug_679643_expected.txt
+++ b/testing/resources/javascript/bug_679643_expected.txt
@@ -1,3 +1,2 @@
 Alert: Starting ...
 Alert: Firing ...
-Alert: failed: Annot.name: Object no longer exists.
diff --git a/testing/resources/javascript/bug_695826.in b/testing/resources/javascript/bug_695826.in
index b20908b..e17eaf1 100644
--- a/testing/resources/javascript/bug_695826.in
+++ b/testing/resources/javascript/bug_695826.in
@@ -32,16 +32,18 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
-var obj = new this.constructor;
-obj.author = 3;
+try {
+  var obj = new this.constructor;
+  obj.author = 3;
+} catch (e) {
+}
 app.alert('Done!');
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_735912.in b/testing/resources/javascript/bug_735912.in
index 7f42d1b..06bd9a3 100644
--- a/testing/resources/javascript/bug_735912.in
+++ b/testing/resources/javascript/bug_735912.in
@@ -55,6 +55,7 @@
 endobj
 % JS program to exexute
 {{object 31 0}} <<
+  {{streamlen}}
 >>
 stream
 this.getField("MyField").setFocus();
@@ -70,6 +71,7 @@
 endobj
 % JS program to exexute
 {{object 35 0}} <<
+  {{streamlen}}
 >>
 stream
 this.removeField("MyField");
@@ -85,31 +87,36 @@
 endobj
 % JS program to exexute
 {{object 37 0}} <<
+  {{streamlen}}
 >>
 stream
 this.removeField("MyField");
 endstream
 endobj
 {{object 43 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 <?xml version="1.0" encoding="UTF-8"?>
 <xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
 endstream
 endobj
 {{object 49 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 <config></config>
 <template></template>
 endstream
 endobj
 {{object 50 0}} <<
->>stream
+  {{streamlen}}
+>>
+stream
 </xdp:xdp>
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_740166.in b/testing/resources/javascript/bug_740166.in
index 1e2eb91..425374d 100644
--- a/testing/resources/javascript/bug_740166.in
+++ b/testing/resources/javascript/bug_740166.in
@@ -45,6 +45,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 app.alert(util.printf("Values = %0.1x .9999 %x", 1, 2));
@@ -54,8 +55,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/bug_895152.in b/testing/resources/javascript/bug_895152.in
new file mode 100644
index 0000000..63b2a4b
--- /dev/null
+++ b/testing/resources/javascript/bug_895152.in
@@ -0,0 +1,76 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 4 0 R
+  /OpenAction 7 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 600 700]
+  /Resources <<>>
+  /Annots [5 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Fields [5 0 R]
+>>
+endobj
+{{object 5 0}} <<
+  /V /TestV
+  /FT /Tx
+  /Type /Annot
+  /Subtype /Widget
+  /T (txt1)
+  /F 4
+  /AP <</N 6 0 R>>
+  /Rect [200 200 400 400]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 8 0 R
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+function run() {
+  var doc = this;
+  Object.prototype.__defineSetter__('V', function() {
+    doc.resetForm();
+  });
+  var other_info = this.info;
+}
+try {
+  run();
+  app.alert('*** PASS ***');
+} catch (e) {
+  app.alert('Caught: ' + e);
+}
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Info 5 0 R
+>>
+{{startxref}}
+%%EOF
+
diff --git a/testing/resources/javascript/bug_895152_expected.txt b/testing/resources/javascript/bug_895152_expected.txt
new file mode 100644
index 0000000..a7c97be
--- /dev/null
+++ b/testing/resources/javascript/bug_895152_expected.txt
@@ -0,0 +1 @@
+Alert: *** PASS ***
diff --git a/testing/resources/javascript/bug_959274_1.in b/testing/resources/javascript/bug_959274_1.in
new file mode 100644
index 0000000..74909c0
--- /dev/null
+++ b/testing/resources/javascript/bug_959274_1.in
@@ -0,0 +1,36 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /Contents [21 0 R]
+  /MediaBox [0 0 612 792]
+>>
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS /app.alert#28#22FAILURE#22#29
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_959274_1_expected.txt b/testing/resources/javascript/bug_959274_1_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/bug_959274_1_expected.txt
diff --git a/testing/resources/javascript/bug_983867.in b/testing/resources/javascript/bug_983867.in
new file mode 100644
index 0000000..910be61
--- /dev/null
+++ b/testing/resources/javascript/bug_983867.in
@@ -0,0 +1,73 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 4 0 R
+  /Pages 2 0 R
+  /OpenAction 7 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /MediaBox [ 0 0 612 792 ]
+  /Parent 2 0 R
+  /Type /Page
+  /CropBox [ 0 0 612 792 ]
+  /Annots [ 5 0 R 6 0 R ]
+>>
+endobj
+{{object 4 0}} <<
+  /Fields [ 5 0 R 6 0 R ]
+>>
+endobj
+{{object 5 0}} <<
+  /AA 6 0 R
+  /DV (default value)
+  /T (textfld1)
+  /Rect [ 116 615 393 677 ]
+  /F 4
+  /V (default value)
+  /Subtype /Widget
+  /Type /Annot
+  /FT /Tx
+>>
+endobj
+{{object 6 0}} <<
+  /Ff 0
+  /RV (blah)
+  /Rect [ 226 735 373 771 ]
+  /T (textfld2)
+  /V <<
+    /S /JavaScript
+    /JS (
+      this.a += 1;
+      if (this.a == 3) this.resetForm();
+    )
+    /Type /Action
+  >>
+  /F 4
+  /Subtype /Widget
+  /Type /Annot
+  /FT /Tx
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS (
+    this.a = 1;
+    this.getField("textfld1").setFocus();
+    this.getField("textfld1").value = "1";
+    this.getField("textfld2").setFocus();
+    app.alert("Finished !!!");
+  )
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_983867_expected.txt b/testing/resources/javascript/bug_983867_expected.txt
new file mode 100644
index 0000000..cc87d2b
--- /dev/null
+++ b/testing/resources/javascript/bug_983867_expected.txt
@@ -0,0 +1 @@
+Alert: Finished !!!
diff --git a/testing/resources/javascript/color_methods.in b/testing/resources/javascript/color_methods.in
new file mode 100644
index 0000000..bf03bb4
--- /dev/null
+++ b/testing/resources/javascript/color_methods.in
@@ -0,0 +1,113 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include expect.js}}
+
+try {
+  expectError("color.convert()");
+  expectError("color.convert(1)");
+  expectError("color.convert(undefined, 'RGB')");
+  expectError("color.convert('BOGUS', 'RGB')");
+  expectError("color.convert('{}', 'RGB')");
+
+  // Can't convert transparent into anything else.
+  expect("color.convert(['T'], 'BOGUS')", "T");
+  expect("color.convert(['T'], 'T')", "T");
+  expect("color.convert(['T'], 'G')", "T");
+  expect("color.convert(['T'], 'RGB')", "T");
+  expect("color.convert(['T'], 'CMYK')", "T");
+
+  expect("color.convert(['G', 0.50], 'BOGUS')", "T");
+  expect("color.convert(['G', 0.50], 'T')", "T");
+  expect("color.convert(['G', 0.50], 'G')", "G,0.5");
+  expect("color.convert(['G', 0.50], 'RGB')", "RGB,0.5,0.5,0.5");
+  expect("color.convert(['G', 0.50], 'CMYK')", "CMYK,0,0,0,0.5");
+
+  expect("color.convert(['RGB', 0.25, 0.50, 0.75], 'BOGUS')", "T");
+  expect("color.convert(['RGB', 0.25, 0.50, 0.75], 'T')", "T");
+  expect("color.convert(['RGB', 1.00, 1.00, 1.00], 'G')", "G,1");
+  expect("color.convert(['RGB', 0.25, 0.50, 0.75], 'RGB')", "RGB,0.25,0.5,0.75");
+  expect("color.convert(['RGB', 0.25, 0.50, 0.75], 'CMYK')", "CMYK,0.75,0.5,0.25,0.25");
+
+  expect("color.convert(['CMYK',0.25,0.25,0.25,0.50], 'BOGUS')", "T");
+  expect("color.convert(['CMYK',0.25,0.25,0.25,0.50], 'T')", "T");
+  expect("color.convert(['CMYK',0.25,0.25,0.25,0.50], 'G')", "G,0.25");
+  expect("color.convert(['CMYK',0.25,0.25,0.25,0.50], 'RGB')", "RGB,0.25,0.25,0.25");
+  expect("color.convert(['CMYK',0.25,0.25,0.25,0.50], 'CMYK')", "CMYK,0.25,0.25,0.25,0.5");
+
+  expectError("color.equal()");
+  expectError("color.equal(1)");
+  expectError("color.equal(undefined, undefined)");
+  expectError("color.equal(undefined, 'BOGUS')");
+  expectError("color.equal('BOGUS', 'BOGUS')");
+  expectError("color.equal('BOGUS', ['T'])");
+  expectError("color.equal(['T'], 'BOGUS')");
+
+  expect("color.equal(['T'], ['T'])", true);
+  expect("color.equal(['T'], ['G', 0])", false);
+  expect("color.equal(['T'], ['RGB', 0, 0, 0])", false);
+  expect("color.equal(['T'], ['CMYK', 0, 0, 0, 0])", false);
+
+  expect("color.equal(['G', 0.50], ['T'])", false);
+  expect("color.equal(['G', 0.50], ['G', 0])", false);
+  expect("color.equal(['G', 0.50], ['G', 0.50])", true);
+  expect("color.equal(['G', 0.50], ['RGB', 0, 0, 0])", false);
+  expect("color.equal(['G', 0.50], ['RGB', 0.50, 0.50, 0.50])", true);
+  expect("color.equal(['G', 0.50], ['CMYK', 0, 0, 0, 0])", false);
+  expect("color.equal(['G', 0.50], ['CMYK', 0, 0, 0, 0.50])", true);
+
+  expect("color.equal(['RGB', 0.25, 0.25, 0.25], ['T'])", false);
+  expect("color.equal(['RGB', 0.25, 0.25, 0.25], ['G', 0])", false);
+  expect("color.equal(['RGB', 0.25, 0.25, 0.25], ['G', 0.25])", true);
+  expect("color.equal(['RGB', 0.25, 0.25, 0.25], ['RGB', 0, 0, 0])", false);
+  expect("color.equal(['RGB', 0.25, 0.25, 0.25], ['RGB', 0.25, 0.25, 0.25])", true);
+  expect("color.equal(['RGB', 0.25, 0.25, 0.25], ['CMYK', 0, 0, 0, 0])", false);
+  expect("color.equal(['RGB', 0.25, 0.25, 0.25], ['CMYK', 0.75, 0.75, 0.75, 0.75])", true);
+
+  expect("color.equal(['CMYK', 0.25, 0.25, 0.25, 0.50], ['T'])", false);
+  expect("color.equal(['CMYK', 0.00, 0.25, 0.25, 0.50], ['G', 0])", false);
+  expect("color.equal(['CMYK', 0.00, 0.00, 0.00, 0.50], ['G', 0.50])", true);
+  expect("color.equal(['CMYK', 0.75, 0.50, 0.25, 0.25], ['RGB', 0, 0, 0])", false);
+  expect("color.equal(['CMYK', 0.75, 0.50, 0.25, 0.25], ['RGB', 0.25, 0.50, 0.75])", true);
+  expect("color.equal(['CMYK', 0.25, 0.25, 0.25, 0.50], ['CMYK', 0, 0, 0, 0])", false);
+  expect("color.equal(['CMYK', 0.25, 0.25, 0.25, 0.50], ['CMYK', 0.25, 0.25, 0.25, 0.50])", true);
+} catch (e) {
+  app.alert("Truly unexpected error: " + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/color_methods_expected.txt b/testing/resources/javascript/color_methods_expected.txt
new file mode 100644
index 0000000..21a1b25
--- /dev/null
+++ b/testing/resources/javascript/color_methods_expected.txt
@@ -0,0 +1,57 @@
+Alert: PASS: color.convert() threw color.convert: Incorrect number of parameters passed to function.
+Alert: PASS: color.convert(1) threw color.convert: Incorrect number of parameters passed to function.
+Alert: PASS: color.convert(undefined, 'RGB') threw color.convert: Incorrect parameter type.
+Alert: PASS: color.convert('BOGUS', 'RGB') threw color.convert: Incorrect parameter type.
+Alert: PASS: color.convert('{}', 'RGB') threw color.convert: Incorrect parameter type.
+Alert: PASS: color.convert(['T'], 'BOGUS') = T
+Alert: PASS: color.convert(['T'], 'T') = T
+Alert: PASS: color.convert(['T'], 'G') = T
+Alert: PASS: color.convert(['T'], 'RGB') = T
+Alert: PASS: color.convert(['T'], 'CMYK') = T
+Alert: PASS: color.convert(['G', 0.50], 'BOGUS') = T
+Alert: PASS: color.convert(['G', 0.50], 'T') = T
+Alert: PASS: color.convert(['G', 0.50], 'G') = G,0.5
+Alert: PASS: color.convert(['G', 0.50], 'RGB') = RGB,0.5,0.5,0.5
+Alert: PASS: color.convert(['G', 0.50], 'CMYK') = CMYK,0,0,0,0.5
+Alert: PASS: color.convert(['RGB', 0.25, 0.50, 0.75], 'BOGUS') = T
+Alert: PASS: color.convert(['RGB', 0.25, 0.50, 0.75], 'T') = T
+Alert: PASS: color.convert(['RGB', 1.00, 1.00, 1.00], 'G') = G,1
+Alert: PASS: color.convert(['RGB', 0.25, 0.50, 0.75], 'RGB') = RGB,0.25,0.5,0.75
+Alert: PASS: color.convert(['RGB', 0.25, 0.50, 0.75], 'CMYK') = CMYK,0.75,0.5,0.25,0.25
+Alert: PASS: color.convert(['CMYK',0.25,0.25,0.25,0.50], 'BOGUS') = T
+Alert: PASS: color.convert(['CMYK',0.25,0.25,0.25,0.50], 'T') = T
+Alert: PASS: color.convert(['CMYK',0.25,0.25,0.25,0.50], 'G') = G,0.25
+Alert: PASS: color.convert(['CMYK',0.25,0.25,0.25,0.50], 'RGB') = RGB,0.25,0.25,0.25
+Alert: PASS: color.convert(['CMYK',0.25,0.25,0.25,0.50], 'CMYK') = CMYK,0.25,0.25,0.25,0.5
+Alert: PASS: color.equal() threw color.equal: Incorrect number of parameters passed to function.
+Alert: PASS: color.equal(1) threw color.equal: Incorrect number of parameters passed to function.
+Alert: PASS: color.equal(undefined, undefined) threw color.equal: Incorrect parameter type.
+Alert: PASS: color.equal(undefined, 'BOGUS') threw color.equal: Incorrect parameter type.
+Alert: PASS: color.equal('BOGUS', 'BOGUS') threw color.equal: Incorrect parameter type.
+Alert: PASS: color.equal('BOGUS', ['T']) threw color.equal: Incorrect parameter type.
+Alert: PASS: color.equal(['T'], 'BOGUS') threw color.equal: Incorrect parameter type.
+Alert: PASS: color.equal(['T'], ['T']) = true
+Alert: PASS: color.equal(['T'], ['G', 0]) = false
+Alert: PASS: color.equal(['T'], ['RGB', 0, 0, 0]) = false
+Alert: PASS: color.equal(['T'], ['CMYK', 0, 0, 0, 0]) = false
+Alert: PASS: color.equal(['G', 0.50], ['T']) = false
+Alert: PASS: color.equal(['G', 0.50], ['G', 0]) = false
+Alert: PASS: color.equal(['G', 0.50], ['G', 0.50]) = true
+Alert: PASS: color.equal(['G', 0.50], ['RGB', 0, 0, 0]) = false
+Alert: PASS: color.equal(['G', 0.50], ['RGB', 0.50, 0.50, 0.50]) = true
+Alert: PASS: color.equal(['G', 0.50], ['CMYK', 0, 0, 0, 0]) = false
+Alert: PASS: color.equal(['G', 0.50], ['CMYK', 0, 0, 0, 0.50]) = true
+Alert: PASS: color.equal(['RGB', 0.25, 0.25, 0.25], ['T']) = false
+Alert: PASS: color.equal(['RGB', 0.25, 0.25, 0.25], ['G', 0]) = false
+Alert: PASS: color.equal(['RGB', 0.25, 0.25, 0.25], ['G', 0.25]) = true
+Alert: PASS: color.equal(['RGB', 0.25, 0.25, 0.25], ['RGB', 0, 0, 0]) = false
+Alert: PASS: color.equal(['RGB', 0.25, 0.25, 0.25], ['RGB', 0.25, 0.25, 0.25]) = true
+Alert: PASS: color.equal(['RGB', 0.25, 0.25, 0.25], ['CMYK', 0, 0, 0, 0]) = false
+Alert: PASS: color.equal(['RGB', 0.25, 0.25, 0.25], ['CMYK', 0.75, 0.75, 0.75, 0.75]) = true
+Alert: PASS: color.equal(['CMYK', 0.25, 0.25, 0.25, 0.50], ['T']) = false
+Alert: PASS: color.equal(['CMYK', 0.00, 0.25, 0.25, 0.50], ['G', 0]) = false
+Alert: PASS: color.equal(['CMYK', 0.00, 0.00, 0.00, 0.50], ['G', 0.50]) = true
+Alert: PASS: color.equal(['CMYK', 0.75, 0.50, 0.25, 0.25], ['RGB', 0, 0, 0]) = false
+Alert: PASS: color.equal(['CMYK', 0.75, 0.50, 0.25, 0.25], ['RGB', 0.25, 0.50, 0.75]) = true
+Alert: PASS: color.equal(['CMYK', 0.25, 0.25, 0.25, 0.50], ['CMYK', 0, 0, 0, 0]) = false
+Alert: PASS: color.equal(['CMYK', 0.25, 0.25, 0.25, 0.50], ['CMYK', 0.25, 0.25, 0.25, 0.50]) = true
diff --git a/testing/resources/javascript/color_properties.in b/testing/resources/javascript/color_properties.in
new file mode 100644
index 0000000..7d09656
--- /dev/null
+++ b/testing/resources/javascript/color_properties.in
@@ -0,0 +1,76 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [21 0 R]
+  /MediaBox [0 0 612 792]
+>>
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+var colorNames = [
+  "transparent", "black", "white", "red", "green", "blue", "cyan",
+  "magenta", "yellow", "dkGray", "gray", "ltGray"
+];
+var newValues = [
+  ["T"], ["G", 20], ["RGB", 10, 20, 30], ["CMYK", 10, 20, 30, 40], ["BOGUS", 4]
+];
+try {
+  var x, y, c, v;
+  app.alert("Original values");
+  for (x = 0; x < colorNames.length; ++x) {
+    c = colorNames[x];
+    app.alert(c + ": " + color[c]);
+  }
+  for (y = 0; y < newValues.length; ++y) {
+    v = newValues[y];
+    app.alert("Setting to " + v);
+    for (x = 0; x < colorNames.length; ++x) {
+      c = colorNames[x];
+      color[c] = v;
+    }
+    app.alert("Updated values");
+    for (x = 0; x < colorNames.length; ++x) {
+      c = colorNames[x];
+      app.alert(c + ": " + color[c]);
+    }
+  }
+} catch (e) {
+  app.alert("FAILURE" + e);
+}
+try {
+  app.alert("Testing wrong parameter type");
+  color.red = 42;
+} catch (e) {
+  app.alert("SUCCESS: " + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/color_properties_expected.txt b/testing/resources/javascript/color_properties_expected.txt
new file mode 100644
index 0000000..98e9e45
--- /dev/null
+++ b/testing/resources/javascript/color_properties_expected.txt
@@ -0,0 +1,85 @@
+Alert: Original values
+Alert: transparent: T
+Alert: black: G,0
+Alert: white: G,1
+Alert: red: RGB,1,0,0
+Alert: green: RGB,0,1,0
+Alert: blue: RGB,0,0,1
+Alert: cyan: CMYK,1,0,0,0
+Alert: magenta: CMYK,0,1,0,0
+Alert: yellow: CMYK,0,0,1,0
+Alert: dkGray: G,0.25
+Alert: gray: G,0.5
+Alert: ltGray: G,0.75
+Alert: Setting to T
+Alert: Updated values
+Alert: transparent: T
+Alert: black: T
+Alert: white: T
+Alert: red: T
+Alert: green: T
+Alert: blue: T
+Alert: cyan: T
+Alert: magenta: T
+Alert: yellow: T
+Alert: dkGray: T
+Alert: gray: T
+Alert: ltGray: T
+Alert: Setting to G,20
+Alert: Updated values
+Alert: transparent: G,20
+Alert: black: G,20
+Alert: white: G,20
+Alert: red: G,20
+Alert: green: G,20
+Alert: blue: G,20
+Alert: cyan: G,20
+Alert: magenta: G,20
+Alert: yellow: G,20
+Alert: dkGray: G,20
+Alert: gray: G,20
+Alert: ltGray: G,20
+Alert: Setting to RGB,10,20,30
+Alert: Updated values
+Alert: transparent: RGB,10,20,30
+Alert: black: RGB,10,20,30
+Alert: white: RGB,10,20,30
+Alert: red: RGB,10,20,30
+Alert: green: RGB,10,20,30
+Alert: blue: RGB,10,20,30
+Alert: cyan: RGB,10,20,30
+Alert: magenta: RGB,10,20,30
+Alert: yellow: RGB,10,20,30
+Alert: dkGray: RGB,10,20,30
+Alert: gray: RGB,10,20,30
+Alert: ltGray: RGB,10,20,30
+Alert: Setting to CMYK,10,20,30,40
+Alert: Updated values
+Alert: transparent: CMYK,10,20,30,40
+Alert: black: CMYK,10,20,30,40
+Alert: white: CMYK,10,20,30,40
+Alert: red: CMYK,10,20,30,40
+Alert: green: CMYK,10,20,30,40
+Alert: blue: CMYK,10,20,30,40
+Alert: cyan: CMYK,10,20,30,40
+Alert: magenta: CMYK,10,20,30,40
+Alert: yellow: CMYK,10,20,30,40
+Alert: dkGray: CMYK,10,20,30,40
+Alert: gray: CMYK,10,20,30,40
+Alert: ltGray: CMYK,10,20,30,40
+Alert: Setting to BOGUS,4
+Alert: Updated values
+Alert: transparent: T
+Alert: black: T
+Alert: white: T
+Alert: red: T
+Alert: green: T
+Alert: blue: T
+Alert: cyan: T
+Alert: magenta: T
+Alert: yellow: T
+Alert: dkGray: T
+Alert: gray: T
+Alert: ltGray: T
+Alert: Testing wrong parameter type
+Alert: SUCCESS: color.red: Incorrect parameter type.
diff --git a/testing/resources/javascript/console_methods.in b/testing/resources/javascript/console_methods.in
new file mode 100644
index 0000000..0fbba3f
--- /dev/null
+++ b/testing/resources/javascript/console_methods.in
@@ -0,0 +1,49 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+try {
+  app.alert("testing console methods");
+  console.clear();
+  console.hide();
+  console.println("clams");
+  console.show();
+} catch (e) {
+  app.alert("FAILURE: " + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/console_methods_expected.txt b/testing/resources/javascript/console_methods_expected.txt
new file mode 100644
index 0000000..c4ea072
--- /dev/null
+++ b/testing/resources/javascript/console_methods_expected.txt
@@ -0,0 +1 @@
+Alert: testing console methods
diff --git a/testing/resources/javascript/constructor.in b/testing/resources/javascript/constructor.in
new file mode 100644
index 0000000..1211f89
--- /dev/null
+++ b/testing/resources/javascript/constructor.in
@@ -0,0 +1,77 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+function testIllegalConstructor(name, allowed) {
+  const constructorString = name + ".constructor";
+  let constructor;
+  try {
+    constructor = eval(constructorString);
+  } catch (e) {
+    app.alert("FAIL: No such " + constructorString);
+    return;
+  }
+  try {
+    constructor();
+    app.alert("FAIL: " + constructorString + "(): returned");
+  } catch (e) {
+    app.alert("PASS: " + constructorString + "(): " + e);
+  }
+  try {
+    new constructor;
+    app.alert("FAIL: new " + constructorString + ": returned");
+  } catch (e) {
+    app.alert("PASS: new " + constructorString + ": " + e);
+  }
+}
+testIllegalConstructor("this");
+testIllegalConstructor("app");
+testIllegalConstructor("event");
+testIllegalConstructor("font");
+testIllegalConstructor("global");
+testIllegalConstructor("util");
+testIllegalConstructor("style");
+testIllegalConstructor("color");
+testIllegalConstructor("border");
+testIllegalConstructor("display");
+testIllegalConstructor("console");
+testIllegalConstructor("position");
+testIllegalConstructor("highlight");
+testIllegalConstructor("zoomtype");
+testIllegalConstructor("scaleHow");
+testIllegalConstructor("scaleWhen");
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/constructor_expected.txt b/testing/resources/javascript/constructor_expected.txt
new file mode 100644
index 0000000..af03337
--- /dev/null
+++ b/testing/resources/javascript/constructor_expected.txt
@@ -0,0 +1,32 @@
+Alert: PASS: this.constructor(): illegal constructor
+Alert: PASS: new this.constructor: not a dynamic object
+Alert: PASS: app.constructor(): illegal constructor
+Alert: PASS: new app.constructor: not a dynamic object
+Alert: PASS: event.constructor(): illegal constructor
+Alert: PASS: new event.constructor: not a dynamic object
+Alert: PASS: font.constructor(): illegal constructor
+Alert: PASS: new font.constructor: not a dynamic object
+Alert: PASS: global.constructor(): illegal constructor
+Alert: PASS: new global.constructor: not a dynamic object
+Alert: PASS: util.constructor(): illegal constructor
+Alert: PASS: new util.constructor: not a dynamic object
+Alert: PASS: style.constructor(): illegal constructor
+Alert: PASS: new style.constructor: not a dynamic object
+Alert: PASS: color.constructor(): illegal constructor
+Alert: PASS: new color.constructor: not a dynamic object
+Alert: PASS: border.constructor(): illegal constructor
+Alert: PASS: new border.constructor: not a dynamic object
+Alert: PASS: display.constructor(): illegal constructor
+Alert: PASS: new display.constructor: not a dynamic object
+Alert: PASS: console.constructor(): illegal constructor
+Alert: PASS: new console.constructor: not a dynamic object
+Alert: PASS: position.constructor(): illegal constructor
+Alert: PASS: new position.constructor: not a dynamic object
+Alert: PASS: highlight.constructor(): illegal constructor
+Alert: PASS: new highlight.constructor: not a dynamic object
+Alert: PASS: zoomtype.constructor(): illegal constructor
+Alert: PASS: new zoomtype.constructor: not a dynamic object
+Alert: PASS: scaleHow.constructor(): illegal constructor
+Alert: PASS: new scaleHow.constructor: not a dynamic object
+Alert: PASS: scaleWhen.constructor(): illegal constructor
+Alert: PASS: new scaleWhen.constructor: not a dynamic object
diff --git a/testing/resources/javascript/consts.in b/testing/resources/javascript/consts.in
index 6fb8215..dbfd579 100644
--- a/testing/resources/javascript/consts.in
+++ b/testing/resources/javascript/consts.in
@@ -32,6 +32,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 
@@ -129,9 +130,7 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
 
diff --git a/testing/resources/javascript/document_methods.in b/testing/resources/javascript/document_methods.in
index 8c5a14a..9571e55 100644
--- a/testing/resources/javascript/document_methods.in
+++ b/testing/resources/javascript/document_methods.in
@@ -55,6 +55,7 @@
 >>
 % Contents of the page.
 {{object 8 0}} <<
+  {{streamlen}}
 >>
 stream
 BT
@@ -79,29 +80,10 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
-function expect(str, expected) {
-  try {
-    var result = eval(str);
-    if (result == expected) {
-      app.alert('PASS: ' + str + ' = ' + result);
-    } else {
-      app.alert('FAIL: ' + str + ' = ' + result + ', expected = ' + expected);
-    }
-  } catch (e) {
-    app.alert('ERROR: ' + e.toString());
-  }
-}
-
-function expectError(str) {
-  try {
-    var result = eval(str);
-    app.alert('FAIL: ' + str + ' = ' + result + ', expected to throw error');
-  } catch (e) {
-    app.alert('PASS: ' + str + ' threw error ' + e.toString());
-  }
-}
+{{include expect.js}}
 
 // "Unsupported" methods are present in the document object, but not
 // implemented. They always return |undefined| regardless of arguments.
@@ -132,7 +114,8 @@
    expect('typeof this.calculateNow', 'function');
 
    // TODO(tsepez): test with no permissions.
-   // TODO(tsepez): test success cases.
+
+   expect('this.calculateNow()', undefined)
 }
 
 function testGetAnnot() {
@@ -185,7 +168,11 @@
   // Argument can not be negative.
   expectError('this.getNthFieldName(-1)');
 
-  // TODO(tsepez): test success cases.
+  // Argument can not be huge.
+  expectError('this.getNthFieldName(400000)');
+
+  // TODO(tsepez): add field object so this case works.
+  expectError('this.getNthFieldName(0)');
 }
 
 function testGetPageNthWord() {
@@ -208,7 +195,8 @@
    expect('typeof this.getPageNthWordQuads', 'function');
 
   // TODO(tsepez): test with no permissions.
-  // TODO(tsepez): test success cases.
+
+  expectError('this.getPageNthWordQuads()');
 }
 
 function testGetPageNumWords() {
@@ -227,29 +215,43 @@
 }
 
 function testGetPrintParams() {
-   // Method is present.
-   expect('typeof this.getPrintParams', 'function');
+  // Method is present.
+  expect('typeof this.getPrintParams', 'function');
 
-  // TODO(tsepez): test success cases.
+  // Method always returns in error.
+  expectError('this.getPrintParams()');
+  expectError('this.getPrintParams(42)');
 }
 
 function testGotoNamedDest() {
-   // Method is present.
-   expect('typeof this.gotoNamedDest', 'function');
+  // Method is present.
+  expect('typeof this.gotoNamedDest', 'function');
 
-   // Method needs exactly one argument.
-   expectError('this.gotoNamedDest()');
-   expectError('this.gotoNamedDest(1, 2)');
+  // Method needs exactly one argument.
+  expectError('this.gotoNamedDest()');
+  expectError('this.gotoNamedDest(1, 2)');
 
-   // TODO(tonikitoo): test success cases.
+  // TODO(tsepez): add actual named dest matching this.
+  expectError('this.gotoNamedDest("chicago")');
 }
 
 function testMailDoc() {
-   // Method is present.
-   expect('typeof this.mailDoc', 'function');
+  // Method is present.
+  expect('typeof this.mailDoc', 'function');
 
   // TODO(tsepez): test with no permissions.
-  // TODO(tsepez): test success cases.
+
+  // Success cases.
+  expect('this.mailDoc()', undefined);
+  expect('this.mailDoc(false, "user@example.com", "cc@example.com", ' +
+             '"bcc@example.com", "Lottery Winner", "You won the lottery!")',
+         undefined);
+  expect('this.mailDoc({})', undefined);
+  expect('this.mailDoc({"bUI": false, "cTo": "user@example.com", ' +
+            '"cCc": "cc@example.com", "cBcc": "bcc@example.com", ' +
+            '"cSubject": "LotteryWinner", "cMsg": "You won the lottery!", ' +
+            '"bogus": "yes"})',
+         undefined);
 }
 
 function testMailForm() {
@@ -257,14 +259,26 @@
    expect('typeof this.mailForm', 'function');
 
   // TODO(tsepez): test with no permissions.
-  // TODO(tsepez): test success cases.
+
+  // Success cases.
+  expect('this.mailForm()', undefined);
+  expect('this.mailForm(false, "user@example.com", "cc@example.com", ' +
+             '"bcc@example.com", "Lottery Winner", "You won the lottery!")',
+         undefined);
+  expect('this.mailForm({})', undefined);
+  expect('this.mailForm({"bUI": false, "cTo": "user@example.com", ' +
+            '"cCc": "cc@example.com", "cBcc": "bcc@example.com", ' +
+            '"cSubject": "LotteryWinner", "cMsg": "You won the lottery!", ' +
+            '"bogus": "yes"})',
+         undefined);
 }
 
 function testPrint() {
-   // Method is present.
-   expect('typeof this.print', 'function');
+  // Method is present.
+  expect('typeof this.print', 'function');
 
-  // TODO(tsepez): test success cases.
+  // Successful only when invoked by a user gesture.
+  expectError('this.print()', undefined);
 }
 
 function testRemoveField() {
@@ -283,7 +297,10 @@
    expect('typeof this.resetForm', 'function');
 
   // TODO(tsepez): test with no permissions.
-  // TODO(tsepez): test success cases.
+
+  // TODO(tsepez): Add form to document to match these arguments.
+  expect('this.resetForm()', undefined);
+  expect('this.resetForm("myform")', undefined);
 }
 
 function testSubmitForm() {
@@ -293,7 +310,8 @@
   // Method requires at least one argument.
   expectError('this.submitForm()');
 
-  // TODO(tsepez): test success cases.
+  // Successful only when invoked by a user gesture.
+  expectError('this.submitForm("myform", true, true, ["name", "age"])');
 }
 
 try {
@@ -309,6 +327,7 @@
   testUnsupported('this.exportAsXFDF');
   testUnsupported('this.extractPages');
   testUnsupported('this.getAnnot3D');
+  testUnsupported('this.getAnnots3D');
   testUnsupported('this.getLinks');
   testUnsupported('this.getOCGs');
   testUnsupported('this.getPageBox');
diff --git a/testing/resources/javascript/document_methods_expected.txt b/testing/resources/javascript/document_methods_expected.txt
index 4a2c4d7..ec42224 100644
--- a/testing/resources/javascript/document_methods_expected.txt
+++ b/testing/resources/javascript/document_methods_expected.txt
@@ -32,6 +32,9 @@
 Alert: PASS: typeof this.getAnnot3D = function
 Alert: PASS: this.getAnnot3D() = undefined
 Alert: PASS: this.getAnnot3D(1, 2, "clams", [1, 2, 3]) = undefined
+Alert: PASS: typeof this.getAnnots3D = function
+Alert: PASS: this.getAnnots3D() = undefined
+Alert: PASS: this.getAnnots3D(1, 2, "clams", [1, 2, 3]) = undefined
 Alert: PASS: typeof this.getLinks = function
 Alert: PASS: this.getLinks() = undefined
 Alert: PASS: this.getLinks(1, 2, "clams", [1, 2, 3]) = undefined
@@ -70,43 +73,70 @@
 Alert: PASS: this.syncAnnotScan(1, 2, "clams", [1, 2, 3]) = undefined
 Alert: *** Testing Supported Methods ***
 Alert: PASS: typeof this.addIcon = function
-Alert: PASS: this.addIcon() threw error Document.addIcon: Incorrect number of parameters passed to function.
-Alert: PASS: this.addIcon(1) threw error Document.addIcon: Incorrect number of parameters passed to function.
-Alert: PASS: this.addIcon(1, 2, 3) threw error Document.addIcon: Incorrect number of parameters passed to function.
-Alert: PASS: this.addIcon("myicon", 3) threw error Document.addIcon: Incorrect parameter type.
-Alert: PASS: this.addIcon("myicon", undefined) threw error Document.addIcon: Incorrect parameter type.
+Alert: PASS: this.addIcon() threw Document.addIcon: Incorrect number of parameters passed to function.
+Alert: PASS: this.addIcon(1) threw Document.addIcon: Incorrect number of parameters passed to function.
+Alert: PASS: this.addIcon(1, 2, 3) threw Document.addIcon: Incorrect number of parameters passed to function.
+Alert: PASS: this.addIcon("myicon", 3) threw Document.addIcon: Incorrect parameter type.
+Alert: PASS: this.addIcon("myicon", undefined) threw Document.addIcon: Incorrect parameter type.
 Alert: PASS: typeof this.calculateNow = function
+Alert: PASS: this.calculateNow() = undefined
 Alert: PASS: typeof this.getAnnot = function
-Alert: PASS: this.getAnnot() threw error Document.getAnnot: Incorrect number of parameters passed to function.
-Alert: PASS: this.getAnnot(0) threw error Document.getAnnot: Incorrect number of parameters passed to function.
-Alert: PASS: this.getAnnot(0, "test", 0) threw error Document.getAnnot: Incorrect number of parameters passed to function.
+Alert: PASS: this.getAnnot() threw Document.getAnnot: Incorrect number of parameters passed to function.
+Alert: PASS: this.getAnnot(0) threw Document.getAnnot: Incorrect number of parameters passed to function.
+Alert: PASS: this.getAnnot(0, "test", 0) threw Document.getAnnot: Incorrect number of parameters passed to function.
 Alert: PASS: typeof this.getAnnots = function
 Alert: PASS: typeof this.getField = function
-Alert: PASS: this.getField() threw error Document.getField: Incorrect number of parameters passed to function.
+Alert: PASS: this.getField() threw Document.getField: Incorrect number of parameters passed to function.
 Alert: PASS: typeof this.getIcon = function
-Alert: PASS: this.getIcon() threw error Document.getIcon: Incorrect number of parameters passed to function.
-Alert: PASS: this.getIcon(1, 2) threw error Document.getIcon: Incorrect number of parameters passed to function.
+Alert: PASS: this.getIcon() threw Document.getIcon: Incorrect number of parameters passed to function.
+Alert: PASS: this.getIcon(1, 2) threw Document.getIcon: Incorrect number of parameters passed to function.
 Alert: PASS: typeof this.getNthFieldName = function
-Alert: PASS: this.getNthFieldName() threw error Document.getNthFieldName: Incorrect number of parameters passed to function.
-Alert: PASS: this.getNthFieldName(-1) threw error Document.getNthFieldName: Incorrect parameter value.
+Alert: PASS: this.getNthFieldName() threw Document.getNthFieldName: Incorrect number of parameters passed to function.
+Alert: PASS: this.getNthFieldName(-1) threw Document.getNthFieldName: Incorrect parameter value.
+Alert: PASS: this.getNthFieldName(400000) threw Document.getNthFieldName: Object no longer exists.
+Alert: PASS: this.getNthFieldName(0) threw Document.getNthFieldName: Object no longer exists.
 Alert: PASS: typeof this.getPageNthWord = function
 Alert: PASS: this.getPageNthWord(0, 0, true, "clams", [1, 2]) = Hello,
-Alert: PASS: this.getPageNthWord(-1, 0, true) threw error Document.getPageNthWord: Incorrect parameter value.
-Alert: PASS: this.getPageNthWord(6, 0, true) threw error Document.getPageNthWord: Incorrect parameter value.
+Alert: PASS: this.getPageNthWord(-1, 0, true) threw Document.getPageNthWord: Incorrect parameter value.
+Alert: PASS: this.getPageNthWord(6, 0, true) threw Document.getPageNthWord: Incorrect parameter value.
 Alert: PASS: typeof this.getPageNthWordQuads = function
+Alert: PASS: this.getPageNthWordQuads() threw Document.getPageNthWordQuads: Operation not supported.
 Alert: PASS: typeof this.getPageNumWords = function
 Alert: PASS: this.getPageNumWords(0, "clams", [1, 2]) = 2
-Alert: PASS: this.getPageNumWords(-1) threw error Document.getPageNumWords: Incorrect parameter value.
-Alert: PASS: this.getPageNumWords(6) threw error Document.getPageNumWords: Incorrect parameter value.
+Alert: PASS: this.getPageNumWords(-1) threw Document.getPageNumWords: Incorrect parameter value.
+Alert: PASS: this.getPageNumWords(6) threw Document.getPageNumWords: Incorrect parameter value.
 Alert: PASS: typeof this.getPrintParams = function
+Alert: PASS: this.getPrintParams() threw Document.getPrintParams: Operation not supported.
+Alert: PASS: this.getPrintParams(42) threw Document.getPrintParams: Operation not supported.
 Alert: PASS: typeof this.gotoNamedDest = function
-Alert: PASS: this.gotoNamedDest() threw error Document.gotoNamedDest: Incorrect number of parameters passed to function.
-Alert: PASS: this.gotoNamedDest(1, 2) threw error Document.gotoNamedDest: Incorrect number of parameters passed to function.
+Alert: PASS: this.gotoNamedDest() threw Document.gotoNamedDest: Incorrect number of parameters passed to function.
+Alert: PASS: this.gotoNamedDest(1, 2) threw Document.gotoNamedDest: Incorrect number of parameters passed to function.
+Alert: PASS: this.gotoNamedDest("chicago") threw Document.gotoNamedDest: Object no longer exists.
 Alert: PASS: typeof this.mailDoc = function
+Mail Msg: 1, to=, cc=, bcc=, subject=, body=
+Alert: PASS: this.mailDoc() = undefined
+Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=Lottery Winner, body=You won the lottery!
+Alert: PASS: this.mailDoc(false, "user@example.com", "cc@example.com", "bcc@example.com", "Lottery Winner", "You won the lottery!") = undefined
+Mail Msg: 1, to=, cc=, bcc=, subject=, body=
+Alert: PASS: this.mailDoc({}) = undefined
+Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=LotteryWinner, body=You won the lottery!
+Alert: PASS: this.mailDoc({"bUI": false, "cTo": "user@example.com", "cCc": "cc@example.com", "cBcc": "bcc@example.com", "cSubject": "LotteryWinner", "cMsg": "You won the lottery!", "bogus": "yes"}) = undefined
 Alert: PASS: typeof this.mailForm = function
+Mail Msg: 1, to=, cc=, bcc=, subject=, body=
+Alert: PASS: this.mailForm() = undefined
+Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=Lottery Winner, body=You won the lottery!
+Alert: PASS: this.mailForm(false, "user@example.com", "cc@example.com", "bcc@example.com", "Lottery Winner", "You won the lottery!") = undefined
+Mail Msg: 1, to=, cc=, bcc=, subject=, body=
+Alert: PASS: this.mailForm({}) = undefined
+Mail Msg: 0, to=user@example.com, cc=cc@example.com, bcc=bcc@example.com, subject=LotteryWinner, body=You won the lottery!
+Alert: PASS: this.mailForm({"bUI": false, "cTo": "user@example.com", "cCc": "cc@example.com", "cBcc": "bcc@example.com", "cSubject": "LotteryWinner", "cMsg": "You won the lottery!", "bogus": "yes"}) = undefined
 Alert: PASS: typeof this.print = function
+Alert: PASS: this.print() threw Document.print: User gesture required.
 Alert: PASS: typeof this.removeField = function
-Alert: PASS: this.removeField() threw error Document.removeField: Incorrect number of parameters passed to function.
+Alert: PASS: this.removeField() threw Document.removeField: Incorrect number of parameters passed to function.
 Alert: PASS: typeof this.resetForm = function
+Alert: PASS: this.resetForm() = undefined
+Alert: PASS: this.resetForm("myform") = undefined
 Alert: PASS: typeof this.submitForm = function
-Alert: PASS: this.submitForm() threw error Document.submitForm: Incorrect number of parameters passed to function.
+Alert: PASS: this.submitForm() threw Document.submitForm: Incorrect number of parameters passed to function.
+Alert: PASS: this.submitForm("myform", true, true, ["name", "age"]) threw Document.submitForm: User gesture required.
diff --git a/testing/resources/javascript/document_properties.in b/testing/resources/javascript/document_properties.in
new file mode 100644
index 0000000..fa7d92a
--- /dev/null
+++ b/testing/resources/javascript/document_properties.in
@@ -0,0 +1,150 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 4
+  /Kids [
+    3 0 R
+    4 0 R
+    5 0 R
+    6 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+% Page number 1.
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+% Page number 2.
+{{object 5 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+% Page number 3.
+{{object 6 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /MediaBox [0 0 612 792]
+>>
+
+% Info
+{{object 9 0}} <<
+  /Author (Joe Random Author)
+  /Creator (Joe Random Creator)
+>>
+endobj
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+var document_props = [
+  'ADBE',
+  'author',
+  'baseURL',
+  'bookmarkRoot',
+  'calculate',
+  'Collab',
+  'creationDate',
+  'creator',
+  'delay',
+  'dirty',
+  'documentFileName',
+  'external',
+  'filesize',
+  'icons',
+  'info',
+  'keywords',
+  'layout',
+  'media',
+  'modDate',
+  'mouseX',
+  'mouseY',
+  'numFields',
+  'numPages',
+  'pageNum',
+  'pageWindowRect',
+  'path',
+  'producer',
+  'subject',
+  'title',
+  'URL',
+  'zoom',
+  'zoomType',
+];
+
+function testGetProps(props) {
+  app.alert('*** Getting properties ***');
+  for (var i = 0; i < props.length; ++i) {
+    try {
+      var expr1 = "this." + props[i];
+      var expr2 = "typeof " + expr1;
+      app.alert(expr1 + " is " + eval(expr2) + ' ' + eval(expr1));
+    } catch (e) {
+      app.alert("ERROR: " + e.toString());
+    }
+  }
+}
+
+function testSetProps(props, value) {
+  app.alert('*** Setting properties to ' + value + ' ***');
+  for (var i = 0; i < props.length; ++i) {
+    try {
+      var expr1 = "this." + props[i] + ' = ' + value + ';'
+      app.alert(expr1 + " yields " + eval(expr1));
+    } catch (e) {
+      app.alert("ERROR: " + e.toString());
+    }
+  }
+}
+
+testGetProps(document_props);
+testSetProps(document_props, 'true');
+testSetProps(document_props, 'false');
+testSetProps(document_props, '["red", 0, "blue", 42]');
+testSetProps(document_props, '{"red": 0, "blue": 42}');
+testSetProps(document_props, '"red"');
+testSetProps(document_props, '3');
+testGetProps(document_props);
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Info 9 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/document_properties_expected.txt b/testing/resources/javascript/document_properties_expected.txt
new file mode 100644
index 0000000..5bab82f
--- /dev/null
+++ b/testing/resources/javascript/document_properties_expected.txt
@@ -0,0 +1,270 @@
+Alert: *** Getting properties ***
+Alert: this.ADBE is undefined undefined
+Alert: this.author is string Joe Random Author
+Alert: this.baseURL is string 
+Alert: this.bookmarkRoot is undefined undefined
+Alert: this.calculate is boolean true
+Alert: this.Collab is undefined undefined
+Alert: this.creationDate is string 
+Alert: this.creator is string Joe Random Creator
+Alert: this.delay is boolean false
+Alert: this.dirty is boolean false
+Alert: this.documentFileName is string 
+Alert: this.external is boolean true
+Alert: this.filesize is number 0
+Alert: this.icons is undefined undefined
+Alert: this.info is object [object Object]
+Alert: this.keywords is string 
+Alert: this.layout is undefined undefined
+Alert: this.media is undefined undefined
+Alert: this.modDate is string 
+Alert: this.mouseX is undefined undefined
+Alert: this.mouseY is undefined undefined
+Alert: this.numFields is number 0
+Alert: this.numPages is number 4
+Alert: this.pageNum is undefined undefined
+Alert: this.pageWindowRect is undefined undefined
+Alert: this.path is string /myfile.pdf
+Alert: this.producer is string 
+Alert: this.subject is string 
+Alert: this.title is string 
+Alert: this.URL is string myfile.pdf
+Alert: this.zoom is undefined undefined
+Alert: this.zoomType is undefined undefined
+Alert: *** Setting properties to true ***
+Alert: this.ADBE = true; yields true
+Alert: this.author = true; yields true
+Alert: this.baseURL = true; yields true
+Alert: this.bookmarkRoot = true; yields true
+Alert: this.calculate = true; yields true
+Alert: this.Collab = true; yields true
+Alert: this.creationDate = true; yields true
+Alert: this.creator = true; yields true
+Alert: this.delay = true; yields true
+Alert: this.dirty = true; yields true
+Alert: ERROR: Document.documentFileName: Cannot assign to readonly property.
+Alert: this.external = true; yields true
+Alert: ERROR: Document.filesize: Cannot assign to readonly property.
+Alert: ERROR: Document.icons: Cannot assign to readonly property.
+Alert: ERROR: Document.info: Cannot assign to readonly property.
+Alert: this.keywords = true; yields true
+Alert: this.layout = true; yields true
+Alert: this.media = true; yields true
+Alert: this.modDate = true; yields true
+Alert: this.mouseX = true; yields true
+Alert: this.mouseY = true; yields true
+Alert: ERROR: Document.numFields: Cannot assign to readonly property.
+Alert: ERROR: Document.numPages: Cannot assign to readonly property.
+Goto Page: 1
+Alert: this.pageNum = true; yields true
+Alert: this.pageWindowRect = true; yields true
+Alert: ERROR: Document.path: Cannot assign to readonly property.
+Alert: this.producer = true; yields true
+Alert: this.subject = true; yields true
+Alert: this.title = true; yields true
+Alert: ERROR: Document.URL: Cannot assign to readonly property.
+Alert: this.zoom = true; yields true
+Alert: this.zoomType = true; yields true
+Alert: *** Setting properties to false ***
+Alert: this.ADBE = false; yields false
+Alert: this.author = false; yields false
+Alert: this.baseURL = false; yields false
+Alert: this.bookmarkRoot = false; yields false
+Alert: this.calculate = false; yields false
+Alert: this.Collab = false; yields false
+Alert: this.creationDate = false; yields false
+Alert: this.creator = false; yields false
+Alert: this.delay = false; yields false
+Alert: this.dirty = false; yields false
+Alert: ERROR: Document.documentFileName: Cannot assign to readonly property.
+Alert: this.external = false; yields false
+Alert: ERROR: Document.filesize: Cannot assign to readonly property.
+Alert: ERROR: Document.icons: Cannot assign to readonly property.
+Alert: ERROR: Document.info: Cannot assign to readonly property.
+Alert: this.keywords = false; yields false
+Alert: this.layout = false; yields false
+Alert: this.media = false; yields false
+Alert: this.modDate = false; yields false
+Alert: this.mouseX = false; yields false
+Alert: this.mouseY = false; yields false
+Alert: ERROR: Document.numFields: Cannot assign to readonly property.
+Alert: ERROR: Document.numPages: Cannot assign to readonly property.
+Goto Page: 0
+Alert: this.pageNum = false; yields false
+Alert: this.pageWindowRect = false; yields false
+Alert: ERROR: Document.path: Cannot assign to readonly property.
+Alert: this.producer = false; yields false
+Alert: this.subject = false; yields false
+Alert: this.title = false; yields false
+Alert: ERROR: Document.URL: Cannot assign to readonly property.
+Alert: this.zoom = false; yields false
+Alert: this.zoomType = false; yields false
+Alert: *** Setting properties to ["red", 0, "blue", 42] ***
+Alert: this.ADBE = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.author = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.baseURL = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.bookmarkRoot = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.calculate = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.Collab = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.creationDate = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.creator = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.delay = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.dirty = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: ERROR: Document.documentFileName: Cannot assign to readonly property.
+Alert: this.external = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: ERROR: Document.filesize: Cannot assign to readonly property.
+Alert: ERROR: Document.icons: Cannot assign to readonly property.
+Alert: ERROR: Document.info: Cannot assign to readonly property.
+Alert: this.keywords = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.layout = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.media = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.modDate = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.mouseX = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.mouseY = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: ERROR: Document.numFields: Cannot assign to readonly property.
+Alert: ERROR: Document.numPages: Cannot assign to readonly property.
+Goto Page: 0
+Alert: this.pageNum = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.pageWindowRect = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: ERROR: Document.path: Cannot assign to readonly property.
+Alert: this.producer = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.subject = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.title = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: ERROR: Document.URL: Cannot assign to readonly property.
+Alert: this.zoom = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: this.zoomType = ["red", 0, "blue", 42]; yields red,0,blue,42
+Alert: *** Setting properties to {"red": 0, "blue": 42} ***
+Alert: this.ADBE = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.author = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.baseURL = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.bookmarkRoot = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.calculate = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.Collab = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.creationDate = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.creator = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.delay = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.dirty = {"red": 0, "blue": 42}; yields [object Object]
+Alert: ERROR: Document.documentFileName: Cannot assign to readonly property.
+Alert: this.external = {"red": 0, "blue": 42}; yields [object Object]
+Alert: ERROR: Document.filesize: Cannot assign to readonly property.
+Alert: ERROR: Document.icons: Cannot assign to readonly property.
+Alert: ERROR: Document.info: Cannot assign to readonly property.
+Alert: this.keywords = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.layout = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.media = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.modDate = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.mouseX = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.mouseY = {"red": 0, "blue": 42}; yields [object Object]
+Alert: ERROR: Document.numFields: Cannot assign to readonly property.
+Alert: ERROR: Document.numPages: Cannot assign to readonly property.
+Goto Page: 0
+Alert: this.pageNum = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.pageWindowRect = {"red": 0, "blue": 42}; yields [object Object]
+Alert: ERROR: Document.path: Cannot assign to readonly property.
+Alert: this.producer = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.subject = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.title = {"red": 0, "blue": 42}; yields [object Object]
+Alert: ERROR: Document.URL: Cannot assign to readonly property.
+Alert: this.zoom = {"red": 0, "blue": 42}; yields [object Object]
+Alert: this.zoomType = {"red": 0, "blue": 42}; yields [object Object]
+Alert: *** Setting properties to "red" ***
+Alert: this.ADBE = "red"; yields red
+Alert: this.author = "red"; yields red
+Alert: this.baseURL = "red"; yields red
+Alert: this.bookmarkRoot = "red"; yields red
+Alert: this.calculate = "red"; yields red
+Alert: this.Collab = "red"; yields red
+Alert: this.creationDate = "red"; yields red
+Alert: this.creator = "red"; yields red
+Alert: this.delay = "red"; yields red
+Alert: this.dirty = "red"; yields red
+Alert: ERROR: Document.documentFileName: Cannot assign to readonly property.
+Alert: this.external = "red"; yields red
+Alert: ERROR: Document.filesize: Cannot assign to readonly property.
+Alert: ERROR: Document.icons: Cannot assign to readonly property.
+Alert: ERROR: Document.info: Cannot assign to readonly property.
+Alert: this.keywords = "red"; yields red
+Alert: this.layout = "red"; yields red
+Alert: this.media = "red"; yields red
+Alert: this.modDate = "red"; yields red
+Alert: this.mouseX = "red"; yields red
+Alert: this.mouseY = "red"; yields red
+Alert: ERROR: Document.numFields: Cannot assign to readonly property.
+Alert: ERROR: Document.numPages: Cannot assign to readonly property.
+Goto Page: 0
+Alert: this.pageNum = "red"; yields red
+Alert: this.pageWindowRect = "red"; yields red
+Alert: ERROR: Document.path: Cannot assign to readonly property.
+Alert: this.producer = "red"; yields red
+Alert: this.subject = "red"; yields red
+Alert: this.title = "red"; yields red
+Alert: ERROR: Document.URL: Cannot assign to readonly property.
+Alert: this.zoom = "red"; yields red
+Alert: this.zoomType = "red"; yields red
+Alert: *** Setting properties to 3 ***
+Alert: this.ADBE = 3; yields 3
+Alert: this.author = 3; yields 3
+Alert: this.baseURL = 3; yields 3
+Alert: this.bookmarkRoot = 3; yields 3
+Alert: this.calculate = 3; yields 3
+Alert: this.Collab = 3; yields 3
+Alert: this.creationDate = 3; yields 3
+Alert: this.creator = 3; yields 3
+Alert: this.delay = 3; yields 3
+Alert: this.dirty = 3; yields 3
+Alert: ERROR: Document.documentFileName: Cannot assign to readonly property.
+Alert: this.external = 3; yields 3
+Alert: ERROR: Document.filesize: Cannot assign to readonly property.
+Alert: ERROR: Document.icons: Cannot assign to readonly property.
+Alert: ERROR: Document.info: Cannot assign to readonly property.
+Alert: this.keywords = 3; yields 3
+Alert: this.layout = 3; yields 3
+Alert: this.media = 3; yields 3
+Alert: this.modDate = 3; yields 3
+Alert: this.mouseX = 3; yields 3
+Alert: this.mouseY = 3; yields 3
+Alert: ERROR: Document.numFields: Cannot assign to readonly property.
+Alert: ERROR: Document.numPages: Cannot assign to readonly property.
+Goto Page: 3
+Alert: this.pageNum = 3; yields 3
+Alert: this.pageWindowRect = 3; yields 3
+Alert: ERROR: Document.path: Cannot assign to readonly property.
+Alert: this.producer = 3; yields 3
+Alert: this.subject = 3; yields 3
+Alert: this.title = 3; yields 3
+Alert: ERROR: Document.URL: Cannot assign to readonly property.
+Alert: this.zoom = 3; yields 3
+Alert: this.zoomType = 3; yields 3
+Alert: *** Getting properties ***
+Alert: this.ADBE is undefined undefined
+Alert: this.author is string 3
+Alert: this.baseURL is string 3
+Alert: this.bookmarkRoot is undefined undefined
+Alert: this.calculate is boolean true
+Alert: this.Collab is undefined undefined
+Alert: this.creationDate is string 3
+Alert: this.creator is string 3
+Alert: this.delay is boolean true
+Alert: this.dirty is boolean true
+Alert: this.documentFileName is string 
+Alert: this.external is boolean true
+Alert: this.filesize is number 0
+Alert: this.icons is undefined undefined
+Alert: this.info is object [object Object]
+Alert: this.keywords is string 3
+Alert: this.layout is undefined undefined
+Alert: this.media is undefined undefined
+Alert: this.modDate is string 3
+Alert: this.mouseX is undefined undefined
+Alert: this.mouseY is undefined undefined
+Alert: this.numFields is number 0
+Alert: this.numPages is number 4
+Alert: this.pageNum is undefined undefined
+Alert: this.pageWindowRect is undefined undefined
+Alert: this.path is string /myfile.pdf
+Alert: this.producer is string 3
+Alert: this.subject is string 3
+Alert: this.title is string 3
+Alert: this.URL is string myfile.pdf
+Alert: this.zoom is undefined undefined
+Alert: this.zoomType is undefined undefined
diff --git a/testing/resources/javascript/document_props.in b/testing/resources/javascript/document_props.in
deleted file mode 100644
index 1882f79..0000000
--- a/testing/resources/javascript/document_props.in
+++ /dev/null
@@ -1,144 +0,0 @@
-{{header}}
-{{object 1 0}} <<
-  /Type /Catalog
-  /Pages 2 0 R
-  /OpenAction 10 0 R
->>
-endobj
-{{object 2 0}} <<
-  /Type /Pages
-  /Count 4
-  /Kids [
-    3 0 R
-    4 0 R
-    5 0 R
-    6 0 R
-  ]
->>
-endobj
-% Page number 0.
-{{object 3 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-% Page number 1.
-{{object 4 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-% Page number 2.
-{{object 5 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-% Page number 3.
-{{object 6 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /MediaBox [0 0 612 792]
->>
-
-% Info
-{{object 9 0}} <<
-  /Author (Joe Random Author)
-  /Creator (Joe Random Creator)
->>
-endobj
-% OpenAction action
-{{object 10 0}} <<
-  /Type /Action
-  /S /JavaScript
-  /JS 11 0 R
->>
-endobj
-% JS program to exexute
-{{object 11 0}} <<
->>
-stream
-var document_props = [
-  'ADBE',
-  'author',
-  'baseURL',
-  'bookmarkRoot',
-  'calculate',
-  'Collab',
-  'creationDate',
-  'creator',
-  'delay',
-  'dirty',
-  'documentFileName',
-  'external',
-  'filesize',
-  'icons',
-  'info',
-  'keywords',
-  'layout',
-  'media',
-  'modDate',
-  'mouseX',
-  'mouseY',
-  'numFields',
-  'numPages',
-  'pageNum',
-  'pageWindowRect',
-  'path',
-  'producer',
-  'subject',
-  'title',
-  'URL',
-  'zoom',
-  'zoomType',
-];
-
-function testGetProps(props) {
-  app.alert('*** Getting properties ***');
-  for (var i = 0; i < props.length; ++i) {
-    try {
-      var expr1 = "this." + props[i];
-      var expr2 = "typeof " + expr1;
-      app.alert(expr1 + " is " + eval(expr2) + ' ' + eval(expr1));
-    } catch (e) {
-      app.alert("ERROR: " + e.toString());
-    }
-  }
-}
-
-function testSetProps(props) {
-  app.alert('*** Setting properties ***');
-  for (var i = 0; i < props.length; ++i) {
-    try {
-      var expr1 = "this." + props[i] + ' = 3;'
-      app.alert(expr1 + " yields " + eval(expr1));
-    } catch (e) {
-      app.alert("ERROR: " + e.toString());
-    }
-  }
-}
-
-testGetProps(document_props);
-testSetProps(document_props);
-testGetProps(document_props);
-endstream
-endobj
-{{xref}}
-trailer <<
-  /Root 1 0 R
-  /Info 9 0 R
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/javascript/document_props_expected.txt b/testing/resources/javascript/document_props_expected.txt
deleted file mode 100644
index 28172ee..0000000
--- a/testing/resources/javascript/document_props_expected.txt
+++ /dev/null
@@ -1,100 +0,0 @@
-Alert: *** Getting properties ***
-Alert: this.ADBE is undefined undefined
-Alert: this.author is string Joe Random Author
-Alert: this.baseURL is string 
-Alert: this.bookmarkRoot is undefined undefined
-Alert: this.calculate is boolean true
-Alert: this.Collab is undefined undefined
-Alert: this.creationDate is string 
-Alert: this.creator is string Joe Random Creator
-Alert: this.delay is boolean false
-Alert: this.dirty is boolean false
-Alert: this.documentFileName is string 
-Alert: this.external is boolean true
-Alert: this.filesize is number 0
-Alert: this.icons is undefined undefined
-Alert: this.info is object [object Object]
-Alert: this.keywords is string 
-Alert: this.layout is undefined undefined
-Alert: this.media is undefined undefined
-Alert: this.modDate is string 
-Alert: this.mouseX is undefined undefined
-Alert: this.mouseY is undefined undefined
-Alert: this.numFields is number 0
-Alert: this.numPages is number 4
-Alert: this.pageNum is undefined undefined
-Alert: this.pageWindowRect is undefined undefined
-Alert: this.path is string /
-Alert: this.producer is string 
-Alert: this.subject is string 
-Alert: this.title is string 
-Alert: this.URL is string 
-Alert: this.zoom is undefined undefined
-Alert: this.zoomType is undefined undefined
-Alert: *** Setting properties ***
-Alert: this.ADBE = 3; yields 3
-Alert: this.author = 3; yields 3
-Alert: this.baseURL = 3; yields 3
-Alert: this.bookmarkRoot = 3; yields 3
-Alert: this.calculate = 3; yields 3
-Alert: this.Collab = 3; yields 3
-Alert: this.creationDate = 3; yields 3
-Alert: this.creator = 3; yields 3
-Alert: this.delay = 3; yields 3
-Alert: this.dirty = 3; yields 3
-Alert: ERROR: Document.documentFileName: Cannot assign to readonly property.
-Alert: this.external = 3; yields 3
-Alert: ERROR: Document.filesize: Cannot assign to readonly property.
-Alert: ERROR: Document.icons: Cannot assign to readonly property.
-Alert: ERROR: Document.info: Cannot assign to readonly property.
-Alert: this.keywords = 3; yields 3
-Alert: this.layout = 3; yields 3
-Alert: this.media = 3; yields 3
-Alert: this.modDate = 3; yields 3
-Alert: this.mouseX = 3; yields 3
-Alert: this.mouseY = 3; yields 3
-Alert: ERROR: Document.numFields: Cannot assign to readonly property.
-Alert: ERROR: Document.numPages: Cannot assign to readonly property.
-Goto Page: 3
-Alert: this.pageNum = 3; yields 3
-Alert: this.pageWindowRect = 3; yields 3
-Alert: ERROR: Document.path: Cannot assign to readonly property.
-Alert: this.producer = 3; yields 3
-Alert: this.subject = 3; yields 3
-Alert: this.title = 3; yields 3
-Alert: ERROR: Document.URL: Cannot assign to readonly property.
-Alert: this.zoom = 3; yields 3
-Alert: this.zoomType = 3; yields 3
-Alert: *** Getting properties ***
-Alert: this.ADBE is undefined undefined
-Alert: this.author is string 3
-Alert: this.baseURL is string 3
-Alert: this.bookmarkRoot is undefined undefined
-Alert: this.calculate is boolean true
-Alert: this.Collab is undefined undefined
-Alert: this.creationDate is string 3
-Alert: this.creator is string 3
-Alert: this.delay is boolean true
-Alert: this.dirty is boolean true
-Alert: this.documentFileName is string 
-Alert: this.external is boolean true
-Alert: this.filesize is number 0
-Alert: this.icons is undefined undefined
-Alert: this.info is object [object Object]
-Alert: this.keywords is string 3
-Alert: this.layout is undefined undefined
-Alert: this.media is undefined undefined
-Alert: this.modDate is string 3
-Alert: this.mouseX is undefined undefined
-Alert: this.mouseY is undefined undefined
-Alert: this.numFields is number 0
-Alert: this.numPages is number 4
-Alert: this.pageNum is undefined undefined
-Alert: this.pageWindowRect is undefined undefined
-Alert: this.path is string /
-Alert: this.producer is string 3
-Alert: this.subject is string 3
-Alert: this.title is string 3
-Alert: this.URL is string 
-Alert: this.zoom is undefined undefined
-Alert: this.zoomType is undefined undefined
diff --git a/testing/resources/javascript/event_properties.in b/testing/resources/javascript/event_properties.in
new file mode 100644
index 0000000..e179a2a
--- /dev/null
+++ b/testing/resources/javascript/event_properties.in
@@ -0,0 +1,173 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm << /Fields [ 4 0 R 10 0 R ] /DR 5 0 R >>
+>>
+endobj
+{{object 2 0}} <<
+  /Count 1
+  /Kids [ 3 0 R ]
+  /Type /Pages
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 200 ]
+  /Contents 8 0 R
+  /Annots [ 4 0 R 10 0 R ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 100 200 130 ]
+  /Subtype /Widget
+  /AA <<
+    /F 9 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Font 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /F1 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 9 0}} <<
+  /JS (
+    {{include expect.js}}
+
+    try {
+      app.alert("*** starting test ***");
+
+      expect("event.change", "");
+      expect("event.change = 'boo'", "boo");
+      expect("event.change", "boo");
+
+      expect("event.changeEx", "");
+      expectError("event.changeEx = 'boo'");
+
+      expect("event.commitKey", "");
+      expectError("event.commitKey = 'boo'");
+
+      // FieldFull applies to events named Keystroke only.
+      // TODO(tsepez): figure out a way to set event names for tests.
+      expectError("event.fieldFull");
+      expectError("event.fieldFull = 'boo'");
+
+      expect("event.keyDown", "");
+      expectError("event.keyDown = 'boo'");
+
+      expect("event.modifier", "");
+      expectError("event.modifier = 'boo'");
+
+      expect("event.name", "Format");
+      expectError("event.name = 'boo'");
+
+      expect("event.rc", false);
+      expect("event.rc = 'boo'", "boo");
+      expect("event.rc", true);
+      expect("event.rc = false", false);
+
+      // TODO(tsepez): is silently ignoring update correct here?
+      expect("event.richChange", undefined);
+      expect("event.richChange = 'boo'", "boo");
+      expect("event.richChange", undefined);
+
+      // TODO(tsepez): is silently ignoring update correct here?
+      expect("event.richChangeEx", undefined);
+      expect("event.richChangeEx = 'boo'", "boo");
+      expect("event.richChangeEx", undefined);
+
+      // TODO(tsepez): is silently ignoring update correct here?
+      expect("event.richValue", undefined);
+      expect("event.richValue = 'boo'", "boo");
+      expect("event.richValue", undefined);
+
+      // selEnd applies to events named Keystroke only.
+      // TODO(tsepez): figure out a way to set event names for tests.
+      expect("event.selEnd", undefined);
+      expect("event.selEnd = 'boo'", "boo");
+      expect("event.selEnd", undefined);
+
+      // selEnd applies to events named Keystroke only.
+      // TODO(tsepez): figure out a way to set event names for tests.
+      expect("event.selStart", undefined);
+      expect("event.selStart = 'boo'", "boo");
+      expect("event.selStart", undefined);
+
+      expect("event.shift", false);
+      expectError("event.shift = 'boo'");
+
+      // TODO(tsepez): dig deeper into object.
+      expect("event.source", "[object Object]");
+      expectError("event.source = 'boo'");
+
+      // TODO(tsepez): dig deeper into object.
+      expect("event.target", "[object Object]");
+      expectError("event.target = 'boo'");
+
+      expect("event.targetName", "Text Box");
+      expectError("event.targetName = 'boo'");
+
+      expect("event.type", "Field");
+      expectError("event.type = 'boo'");
+
+      expect("event.value", "");
+      expect("event.value = 'boo'", "boo");
+      expect("event.value", "boo");
+      expect("event.value = ''", "");
+      expect("event.value", "");
+      expect("event.value = 0", 0);
+      expect("event.value", 0);
+      expect("event.value = 2", 2);
+      expect("event.value", 2);
+      expectError("event.value = true");
+      expectError("event.value = false");
+      expectError("event.value = undefined");
+      expectError("event.value = null");
+
+      expect("event.willCommit", true);
+      expectError("event.willCommit = 'boo'");
+      expect("event.willCommit", true);
+
+      app.alert("*** ending test ***");
+    } catch (e) {
+      app.alert("Truly unexpected error occured: " + e);
+    }
+  )
+  /S /JavaScript
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /FT /Tx
+  /T (Text2)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 40 200 70 ]
+  /Subtype /Widget
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/event_properties_expected.txt b/testing/resources/javascript/event_properties_expected.txt
new file mode 100644
index 0000000..227841c
--- /dev/null
+++ b/testing/resources/javascript/event_properties_expected.txt
@@ -0,0 +1,62 @@
+Alert: *** starting test ***
+Alert: PASS: event.change = 
+Alert: PASS: event.change = 'boo' = boo
+Alert: PASS: event.change = boo
+Alert: PASS: event.changeEx = 
+Alert: PASS: event.changeEx = 'boo' threw event.changeEx: Operation not supported.
+Alert: PASS: event.commitKey = 0
+Alert: PASS: event.commitKey = 'boo' threw event.commitKey: Operation not supported.
+Alert: PASS: event.fieldFull threw event.fieldFull: unrecognized event
+Alert: PASS: event.fieldFull = 'boo' threw event.fieldFull: Operation not supported.
+Alert: PASS: event.keyDown = false
+Alert: PASS: event.keyDown = 'boo' threw event.keyDown: Operation not supported.
+Alert: PASS: event.modifier = false
+Alert: PASS: event.modifier = 'boo' threw event.modifier: Operation not supported.
+Alert: PASS: event.name = Format
+Alert: PASS: event.name = 'boo' threw event.name: Operation not supported.
+Alert: PASS: event.rc = false
+Alert: PASS: event.rc = 'boo' = boo
+Alert: PASS: event.rc = true
+Alert: PASS: event.rc = false = false
+Alert: PASS: event.richChange = undefined
+Alert: PASS: event.richChange = 'boo' = boo
+Alert: PASS: event.richChange = undefined
+Alert: PASS: event.richChangeEx = undefined
+Alert: PASS: event.richChangeEx = 'boo' = boo
+Alert: PASS: event.richChangeEx = undefined
+Alert: PASS: event.richValue = undefined
+Alert: PASS: event.richValue = 'boo' = boo
+Alert: PASS: event.richValue = undefined
+Alert: PASS: event.selEnd = undefined
+Alert: PASS: event.selEnd = 'boo' = boo
+Alert: PASS: event.selEnd = undefined
+Alert: PASS: event.selStart = undefined
+Alert: PASS: event.selStart = 'boo' = boo
+Alert: PASS: event.selStart = undefined
+Alert: PASS: event.shift = false
+Alert: PASS: event.shift = 'boo' threw event.shift: Operation not supported.
+Alert: PASS: event.source = [object Object]
+Alert: PASS: event.source = 'boo' threw event.source: Operation not supported.
+Alert: PASS: event.target = [object Object]
+Alert: PASS: event.target = 'boo' threw event.target: Operation not supported.
+Alert: PASS: event.targetName = Text Box
+Alert: PASS: event.targetName = 'boo' threw event.targetName: Operation not supported.
+Alert: PASS: event.type = Field
+Alert: PASS: event.type = 'boo' threw event.type: Operation not supported.
+Alert: PASS: event.value = 
+Alert: PASS: event.value = 'boo' = boo
+Alert: PASS: event.value = boo
+Alert: PASS: event.value = '' = 
+Alert: PASS: event.value = 
+Alert: PASS: event.value = 0 = 0
+Alert: PASS: event.value = 0
+Alert: PASS: event.value = 2 = 2
+Alert: PASS: event.value = 2
+Alert: PASS: event.value = true threw event.value: Set not possible, invalid or unknown.
+Alert: PASS: event.value = false threw event.value: Set not possible, invalid or unknown.
+Alert: PASS: event.value = undefined threw event.value: Set not possible, invalid or unknown.
+Alert: PASS: event.value = null threw event.value: Set not possible, invalid or unknown.
+Alert: PASS: event.willCommit = true
+Alert: PASS: event.willCommit = 'boo' threw event.willCommit: Operation not supported.
+Alert: PASS: event.willCommit = true
+Alert: *** ending test ***
diff --git a/testing/resources/javascript/expect.js b/testing/resources/javascript/expect.js
new file mode 100644
index 0000000..832f1d6
--- /dev/null
+++ b/testing/resources/javascript/expect.js
@@ -0,0 +1,21 @@
+function expect(expression, expected) {
+  try {
+    var actual = eval(expression);
+    if (actual == expected) {
+      app.alert('PASS: ' + expression + ' = ' + actual);
+    } else {
+      app.alert('FAIL: ' + expression + ' = ' + actual + ', expected ' + expected + " ");
+    }
+  } catch (e) {
+    app.alert('ERROR: ' + e);
+  }
+}
+
+function expectError(expression) {
+  try {
+    var actual = eval(expression);
+    app.alert('FAIL: ' + expression + ' = ' + actual + ', expected to throw');
+  } catch (e) {
+    app.alert('PASS: ' + expression + ' threw ' + e);
+  }
+}
diff --git a/testing/resources/javascript/field.fragment b/testing/resources/javascript/field.fragment
new file mode 100644
index 0000000..335e1ae
--- /dev/null
+++ b/testing/resources/javascript/field.fragment
@@ -0,0 +1,141 @@
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 4 0 R
+  /OpenAction 15 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [
+    5 0 R
+    6 0 R
+    7 0 R
+    8 0 R
+    9 0 R
+    10 0 R
+    11 0 R
+  ]
+>>
+endobj
+% Forms
+{{object 4 0}} <<
+  /Fields [5 0 R]
+>>
+endobj
+% Fields
+{{object 5 0}} <<
+  /T (MyField)
+  /Type /Annot
+  /Subtype /Widget
+  /Rect [100 101 400 401]
+  /Kids [
+    6 0 R
+    7 0 R
+    8 0 R
+    9 0 R
+    10 0 R
+    11 0 R
+    12 0 R
+  ]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /Parent 5 0 R
+  /T (MyText)
+  /Rect [200 201 220 221]
+  /V (bleen)
+  /DV (grue)
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 65536
+  /Parent 5 0 R
+  /T (MyPushButton)
+  /Rect [220 221 240 241]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 32768
+  /Parent 5 0 R
+  /T (MyRadio)
+  /Rect [240 241 260 261]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Btn
+  /Ff 0
+  /Parent 5 0 R
+  /T (MyCheckBox)
+  /Rect [260 261 280 281]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /Parent 5 0 R
+  /T (MyMultiSelect)
+  /Rect [280 281 300 301]
+  /Opt [
+    (foo)
+    (bar)
+    [(qux) (Qux)]
+  ]
+  /V (qux)
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 0
+  /Parent 5 0 R
+  /T (MySingleSelect)
+  /Rect [300 301 320 321]
+  /Opt [
+    [(foo) (Foo)]
+    [(bar) (Bar)]
+    [(qux) (Qux)]
+  ]
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /Ff 1048576
+  /Parent 5 0 R
+  /T (MyFile)
+  /Rect [200 201 220 221]
+  /V ("/path/to/file.pdf")
+>>
+endobj
+% OpenAction action
+{{object 15 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 16 0 R
+>>
+endobj
diff --git a/testing/resources/javascript/field.in b/testing/resources/javascript/field.in
deleted file mode 100644
index c23b81a..0000000
--- a/testing/resources/javascript/field.in
+++ /dev/null
@@ -1,126 +0,0 @@
-{{header}}
-{{object 1 0}} <<
-  /Type /Catalog
-  /Pages 2 0 R
-  /AcroForm 4 0 R
-  /OpenAction 10 0 R
->>
-endobj
-{{object 2 0}} <<
-  /Type /Pages
-  /Count 1
-  /Kids [
-    3 0 R
-  ]
->>
-endobj
-% Page number 0.
-{{object 3 0}} <<
-  /Type /Page
-  /Parent 2 0 R
-  /Resources <<
-    /Font <</F1 15 0 R>>
-  >>
-  /Contents [21 0 R]
-  /MediaBox [0 0 612 792]
->>
-endobj
-% Forms
-{{object 4 0}} <<
-  /Fields [5 0 R]
->>
-endobj
-% Fields
-{{object 5 0}} <<
-  /T (MyField)
-  /Type /Annot
-  /Subtype /Widget
-  /Rect [100 100 400 400]
-  /Kids [
-    6 0 R
-    7 0 R
-    8 0 R
-    9 0 R
-  ]
->>
-endobj
-{{object 6 0}} <<
-  /FT /Tx
-  /Parent 5 0 R
-  /T (Sub_X)
-  /Type /Annot
-  /Subtype /Widget
-  /Rect [200 200 220 220]
->>
-endobj
-{{object 7 0}} <<
-  /FT /Tx
-  /Parent 5 0 R
-  /T (Sub_A)
-  /Type /Annot
-  /Subtype /Widget
-  /Rect [220 220 240 240]
->>
-endobj
-{{object 8 0}} <<
-  /FT /Tx
-  /Parent 5 0 R
-  /T (Sub_Z)
-  /Type /Annot
-  /Subtype /Widget
-  /Rect [240 240 260 260]
->>
-endobj
-{{object 9 0}} <<
-  /FT /Tx
-  /Parent 5 0 R
-  /T (Sub_B)
-  /Type /Annot
-  /Subtype /Widget
-  /Rect [260 260 280 280]
->>
-endobj
-% OpenAction action
-{{object 10 0}} <<
-  /Type /Action
-  /S /JavaScript
-  /JS 11 0 R
->>
-endobj
-% JS program to exexute
-{{object 11 0}} <<
->>
-stream
-function TestGetField() {
-  try {
-    var field = this.getField("MyField");
-    app.alert("field is " + field.name);
-    var sub_a = this.getField("MyField.Sub_A");
-    app.alert("sub_a is " + sub_a.name);
-    var nonesuch = this.getField("MyField.nonesuch");
-    app.alert("nonesuch is " + nonesuch);
-  } catch (e) {
-    app.alert("Unexpected error: " + e);
-  }
-}
-function TestGetArray() {
-  try {
-    var subs = this.getField("MyField").getArray();
-    app.alert("found " + subs.length + " sub-fields:");
-    for (i = 0; i < subs.length; ++i) {
-      app.alert(subs[i].name);
-    }
-  } catch (e) {
-    app.alert("Unexpected error: " + e);
-  }
-}
-TestGetField();
-TestGetArray();
-endstream
-endobj
-{{xref}}
-trailer <<
-  /Root 1 0 R
->>
-{{startxref}}
-%%EOF
diff --git a/testing/resources/javascript/field_expected.txt b/testing/resources/javascript/field_expected.txt
deleted file mode 100644
index 04aafd7..0000000
--- a/testing/resources/javascript/field_expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Alert: field is MyField
-Alert: sub_a is MyField.Sub_A
-Alert: nonesuch is undefined
-Alert: found 4 sub-fields:
-Alert: MyField.Sub_A
-Alert: MyField.Sub_B
-Alert: MyField.Sub_X
-Alert: MyField.Sub_Z
diff --git a/testing/resources/javascript/field_methods.in b/testing/resources/javascript/field_methods.in
new file mode 100644
index 0000000..81b9eed
--- /dev/null
+++ b/testing/resources/javascript/field_methods.in
@@ -0,0 +1,115 @@
+{{header}}
+{{include field.fragment}}
+
+% JS program to exexute
+{{object 16 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include expect.js}}
+
+function testGetField() {
+  try {
+    var empty =  this.getField("");
+    app.alert("empty is " + empty.name);
+    var field = this.getField("MyField");
+    app.alert("field is " + field.name);
+    var button = this.getField("MyField.MyPushButton");
+    app.alert("button is " + button.name);
+    var nonesuch = this.getField("MyField.nonesuch");
+    app.alert("nonesuch is " + nonesuch);
+    var dotdot = this.getField("MyField..nonesuch");
+    app.alert("dotdot is " + dotdot.name);
+    var tripledot = this.getField("MyField...nonesuch");
+    app.alert("tripledot is " + tripledot.name);
+    var quaddot = this.getField("MyField....nonesuch");
+    app.alert("quaddot is " + quaddot.name);
+    var dotdot1 = this.getField("MyField..nonesuch      ");
+    app.alert("dotdot1 is " + dotdot1.name);
+    var dotdot2 = this.getField("MyField..MyPushButton");
+    app.alert("dotdot2 is " + dotdot2.name);
+    var dotdot3 = this.getField("MyField..3");
+    app.alert("dotdot3 is " + dotdot3.name);
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+function testGetArray() {
+  try {
+    var subs = this.getField("MyField").getArray();
+    app.alert("found " + subs.length + " sub-fields:");
+    for (i = 0; i < subs.length; ++i) {
+      app.alert(subs[i].name);
+    }
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+testGetField();
+testGetArray();
+
+expect("this.getField('MyField.MyPushButton').buttonGetCaption()", "");
+expect("this.getField('MyField.MyPushButton').buttonGetIcon()", "[object Object]");
+expect("this.getField('MyField.MyPushButton').buttonImportIcon()", undefined);
+
+expect("this.getField('MyField.MyFile').browseForFileToSubmit()", undefined);
+
+expect("this.getField('MyField.MyMultiSelect').getItemAt(0)", "foo");
+expect("this.getField('MyField.MyMultiSelect').getItemAt(1, false)", "bar");
+expect("this.getField('MyField.MyMultiSelect').getItemAt(1, true)", "bar");
+expect("this.getField('MyField.MyMultiSelect').getItemAt(2, false)", "Qux");
+expect("this.getField('MyField.MyMultiSelect').getItemAt(2, true)", "qux");
+expect("this.getField('MyField.MyMultiSelect').getItemAt(3)", "");
+expect("this.getField('MyField.MyMultiSelect').getItemAt()", "qux");
+expect("this.getField('MyField.MyMultiSelect').getItemAt(172)", "qux");
+expect("this.getField('MyField.MyMultiSelect').getItemAt(-400)", "");
+expect("this.getField('MyField.MyMultiSelect').getItemAt('zzzz')", "foo");
+
+expectError("this.getField('MyField').checkThisBox()");
+expectError("this.getField('MyField').checkThisBox({})");
+
+expectError("this.getField('MyField').defaultIsChecked()");
+expect("this.getField('MyField').defaultIsChecked(0)", false);
+
+expectError("this.getField('MyField').isBoxChecked()");
+expectError("this.getField('MyField').isBoxChecked(20)");
+expect("this.getField('MyField').isBoxChecked(0)", false);
+
+expectError("this.getField('MyField').isDefaultChecked()", false);
+expectError("this.getField('MyField').isDefaultChecked(20)", false);
+expect("this.getField('MyField').isDefaultChecked(0)", false);
+
+expect("this.getField('MyField').setFocus()", undefined);
+
+app.alert("These mismatched cases always throw");
+expectError("this.getField('MyField.MyText').browseForFileToSubmit()");
+expectError("this.getField('MyField').checkThisBox(0, true)");
+expectError("this.getField('MyField').getItemAt(0)");
+
+app.alert("These always succeed without actually doing anything:");
+expect("this.getField('MyField').clearItems()", undefined);
+expect("this.getField('MyField').deleteItemAt()", undefined);
+expect("this.getField('MyField').insertItemAt()", undefined);
+expect("this.getField('MyField').setAction()", undefined);
+expect("this.getField('MyField').setItems()");
+
+app.alert("These always throw operation not supported errors:");
+expectError("this.getField('MyField').buttonSetCaption()");
+expectError("this.getField('MyField').buttonSetIcon()");
+expectError("this.getField('MyField').getLock()");
+expectError("this.getField('MyField').setLock()");
+expectError("this.getField('MyField').signatureGetModifications()");
+expectError("this.getField('MyField').signatureGetSeedValue()");
+expectError("this.getField('MyField').signatureInfo()");
+expectError("this.getField('MyField').signatureSetSeedValue()");
+expectError("this.getField('MyField').signatureSign()");
+expectError("this.getField('MyField').signatureValidate()");
+
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/field_methods_expected.txt b/testing/resources/javascript/field_methods_expected.txt
new file mode 100644
index 0000000..8d7ec84
--- /dev/null
+++ b/testing/resources/javascript/field_methods_expected.txt
@@ -0,0 +1,64 @@
+Alert: empty is 
+Alert: field is MyField
+Alert: button is MyField.MyPushButton
+Alert: nonesuch is undefined
+Alert: dotdot is 
+Alert: tripledot is MyField..nonesuch
+Alert: quaddot is MyField..nonesuch
+Alert: dotdot1 is 
+Alert: dotdot2 is MyField.MyPushButton
+Alert: dotdot3 is MyField
+Alert: found 7 sub-fields:
+Alert: MyField.MyCheckBox
+Alert: MyField.MyFile
+Alert: MyField.MyMultiSelect
+Alert: MyField.MyPushButton
+Alert: MyField.MyRadio
+Alert: MyField.MySingleSelect
+Alert: MyField.MyText
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption() = 
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon() = [object Object]
+Alert: PASS: this.getField('MyField.MyPushButton').buttonImportIcon() = undefined
+Alert: PASS: this.getField('MyField.MyFile').browseForFileToSubmit() = undefined
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(0) = foo
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(1, false) = bar
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(1, true) = bar
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(2, false) = Qux
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(2, true) = qux
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(3) = 
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt() = qux
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(172) = qux
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(-400) = 
+Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt('zzzz') = foo
+Alert: PASS: this.getField('MyField').checkThisBox() threw Field.checkThisBox: Incorrect number of parameters passed to function.
+Alert: PASS: this.getField('MyField').checkThisBox({}) threw Field.checkThisBox: Object is of the wrong type.
+Alert: PASS: this.getField('MyField').defaultIsChecked() threw Field.defaultIsChecked: Incorrect number of parameters passed to function.
+Alert: PASS: this.getField('MyField').defaultIsChecked(0) = false
+Alert: PASS: this.getField('MyField').isBoxChecked() threw Field.isBoxChecked: Incorrect parameter value.
+Alert: PASS: this.getField('MyField').isBoxChecked(20) threw Field.isBoxChecked: Incorrect parameter value.
+Alert: PASS: this.getField('MyField').isBoxChecked(0) = false
+Alert: PASS: this.getField('MyField').isDefaultChecked() threw Field.isDefaultChecked: Incorrect parameter value.
+Alert: PASS: this.getField('MyField').isDefaultChecked(20) threw Field.isDefaultChecked: Incorrect parameter value.
+Alert: PASS: this.getField('MyField').isDefaultChecked(0) = false
+Alert: PASS: this.getField('MyField').setFocus() = undefined
+Alert: These mismatched cases always throw
+Alert: PASS: this.getField('MyField.MyText').browseForFileToSubmit() threw Field.browseForFileToSubmit: Object is of the wrong type.
+Alert: PASS: this.getField('MyField').checkThisBox(0, true) threw Field.checkThisBox: Object is of the wrong type.
+Alert: PASS: this.getField('MyField').getItemAt(0) threw Field.getItemAt: Object is of the wrong type.
+Alert: These always succeed without actually doing anything:
+Alert: PASS: this.getField('MyField').clearItems() = undefined
+Alert: PASS: this.getField('MyField').deleteItemAt() = undefined
+Alert: PASS: this.getField('MyField').insertItemAt() = undefined
+Alert: PASS: this.getField('MyField').setAction() = undefined
+Alert: PASS: this.getField('MyField').setItems() = undefined
+Alert: These always throw operation not supported errors:
+Alert: PASS: this.getField('MyField').buttonSetCaption() threw Field.buttonSetCaption: Operation not supported.
+Alert: PASS: this.getField('MyField').buttonSetIcon() threw Field.buttonSetIcon: Operation not supported.
+Alert: PASS: this.getField('MyField').getLock() threw Field.getLock: Operation not supported.
+Alert: PASS: this.getField('MyField').setLock() threw Field.setLock: Operation not supported.
+Alert: PASS: this.getField('MyField').signatureGetModifications() threw Field.signatureGetModifications: Operation not supported.
+Alert: PASS: this.getField('MyField').signatureGetSeedValue() threw Field.signatureGetSeedValue: Operation not supported.
+Alert: PASS: this.getField('MyField').signatureInfo() threw Field.signatureInfo: Operation not supported.
+Alert: PASS: this.getField('MyField').signatureSetSeedValue() threw Field.signatureSetSeedValue: Operation not supported.
+Alert: PASS: this.getField('MyField').signatureSign() threw Field.signatureSign: Operation not supported.
+Alert: PASS: this.getField('MyField').signatureValidate() threw Field.signatureValidate: Operation not supported.
diff --git a/testing/resources/javascript/field_properties.in b/testing/resources/javascript/field_properties.in
new file mode 100644
index 0000000..2126384
--- /dev/null
+++ b/testing/resources/javascript/field_properties.in
@@ -0,0 +1,154 @@
+{{header}}
+{{include field.fragment}}
+% JS program to exexute
+{{object 16 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include property_test_helpers.js}}
+function testProperties() {
+  try {
+    var field = this.getField("MyField");
+    var text = this.getField("MyField.MyText");
+    var button = this.getField("MyField.MyPushButton");
+    var radio = this.getField("MyField.MyRadio");
+    var list = this.getField("MyField.MyMultiSelect");
+    var check = this.getField("MyField.MyCheckBox");
+    var file = this.getField("MyField.MyFile");
+    app.alert('Testing properties under delay');
+    testRWProperty(field, "delay", false, true);
+    // TODO(tsepez): try this case, too.
+    app.alert('Testing properties under non-delay');
+    testRWProperty(field, "delay", true, false);
+    testFieldPropertiesCase(field);
+    testTextPropertiesCase(text);
+    testPushButtonPropertiesCase(button);
+    testRadioButtonPropertiesCase(radio);
+    testCheckBoxPropertiesCase(check);
+    testListBoxPropertiesCase(list);
+    testFileSelectPropertiesCase(file);
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+function testFieldPropertiesCase(field) {
+  testROProperty(field, "name", "MyField");
+  // TODO(tsepez): this is rect of first child somehow.
+  testRWProperty(field, "rect", [200,221,220,201], [100,121,120,101]);
+ // Put it back to where it started.
+  testRWProperty(field, "rect", [100,121,120,101], [200,221,220,201]);
+}
+
+function testTextPropertiesCase(field) {
+  try {
+    // TODO(tsepez): devise tests and uncomment.
+    testRIProperty(field, "alignment", "left", "center");
+    testRWProperty(field, "borderStyle", "solid", "inset");
+    testRIProperty(field, "calcOrderIndex", -1, 100);
+    testRIProperty(field, "charLimit", 0, 100);
+    testRIProperty(field, "comb", false, true);
+    // testRIProperty(field, "commitOnSelChange", false, true);
+    // testROProperty(field, "currentValueIndices", "clams");
+    testXXProperty(field, "defaultStyle");
+    testRIProperty(field, "defaultValue", "grue", "clams");
+    testRIProperty(field, "doNotScroll", false, true);
+    testRIProperty(field, "doNotSpellCheck", false, true);
+    testRWProperty(field, "display", 2, 3);
+    testROProperty(field, "doc", "[object global]");
+    // testROProperty(field, "editable", "clams");
+    testRWProperty(field, "hidden", false, true);
+    testRIProperty(field, "fileSelect", false, true);
+    testRIProperty(field, "fillColor", "T", ["RGB", 0, 0, 0]);
+    testRWProperty(field, "lineWidth", 1, 4);
+    testRIProperty(field, "multiline", false, true);
+    // testROProperty(field, "multipleSelection", "clams");
+    testROProperty(field, "name", "MyField.MyText");
+    // testROProperty(field, "numItems", "clams");
+    testROProperty(field, "page", 0);
+    testRIProperty(field, "password", false, 42);
+    testRWProperty(field, "print", true, false);
+    testRIProperty(field, "readonly", false, true);
+    testROProperty(field, "rect", [200,221,220,201]);
+    // testROProperty(field, "required", "clams");
+    testRIProperty(field, "richText", false, true);
+    testRIProperty(field, "richValue", undefined, "clams");
+    testRIProperty(field, "rotation", 0, 42);
+    testRIProperty(field, "source", undefined, "clams");
+    testRIProperty(field, "strokeColor", "T", ["RGB", 0, 0, 0]);
+    testRIProperty(field, "submitName", undefined, "clams");
+    testRIProperty(field, "textColor", "T", ["RGB", 0, 0, 0]);
+    // testROProperty(field, "textFont", "clams");
+    testRIProperty(field, "textSize", 0, 32);
+    testROProperty(field, "type", "text");
+    testRIProperty(field, "userName", "");
+    testRWProperty(field, "value", "bleen", "clams");
+    testROProperty(field, "valueAsString", "clams");  // Set by previous line.
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+function testPushButtonPropertiesCase(field) {
+  try {
+    testRIProperty(field, "buttonAlignX", 0, 50);
+    testRIProperty(field, "buttonAlignY", 0, 50);
+    testRIProperty(field, "buttonFitBounds", false);
+    testRIProperty(field, "buttonPosition", 0);
+    testRIProperty(field, "buttonScaleHow", 0);
+    testRIProperty(field, "buttonScaleWhen", 0);
+    testRIProperty(field, "highlight", "invert");
+    testROProperty(field, "type", "button");
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+function testRadioButtonPropertiesCase(field) {
+  try {
+    testROProperty(field, "exportValues", "N");
+    testRIProperty(field, "radiosInUnison", false);
+    testRIProperty(field, "style", "check");
+    testROProperty(field, "type", "radiobutton");
+    testRIProperty(field, "value", "Off");
+    testROProperty(field, "valueAsString", "Off");
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+function testCheckBoxPropertiesCase(field) {
+  try {
+    testROProperty(field, "exportValues", "N");
+    testRIProperty(field, "style", "check");
+    testROProperty(field, "type", "checkbox");
+    testRIProperty(field, "value", "Off");
+    testROProperty(field, "valueAsString", "Off");
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+function testListBoxPropertiesCase(field) {
+  try {
+    testRWProperty(field, "currentValueIndices", 2, 1);
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+function testFileSelectPropertiesCase(field) {
+  try {
+    testRIProperty(field, "fileSelect", true);
+  } catch (e) {
+    app.alert("Unexpected error: " + e);
+  }
+}
+
+testProperties();
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/field_properties_expected.txt b/testing/resources/javascript/field_properties_expected.txt
new file mode 100644
index 0000000..3b2e6f6
--- /dev/null
+++ b/testing/resources/javascript/field_properties_expected.txt
@@ -0,0 +1,122 @@
+Alert: Testing properties under delay
+Alert: PASS: delay = false
+Alert: PASS: delay = true
+Alert: Testing properties under non-delay
+Alert: PASS: delay = true
+Alert: PASS: delay = false
+Alert: PASS: name = MyField
+Alert: PASS: name threw Field.name: Operation not supported.
+Alert: PASS: rect = 200,221,220,201
+Alert: PASS: rect = 100,121,120,101
+Alert: PASS: rect = 100,121,120,101
+Alert: PASS: rect = 200,221,220,201
+Alert: PASS: alignment = left
+Alert: PASS: alignment = left
+Alert: PASS: borderStyle = solid
+Alert: PASS: borderStyle = inset
+Alert: PASS: calcOrderIndex = -1
+Alert: PASS: calcOrderIndex = -1
+Alert: PASS: charLimit = 0
+Alert: PASS: charLimit = 0
+Alert: PASS: comb = false
+Alert: PASS: comb = false
+Alert: PASS: defaultStyle threw Field.defaultStyle: Operation not supported.
+Alert: PASS: defaultStyle threw Field.defaultStyle: Operation not supported.
+Alert: PASS: defaultValue = grue
+Alert: PASS: defaultValue = grue
+Alert: PASS: doNotScroll = false
+Alert: PASS: doNotScroll = false
+Alert: PASS: doNotSpellCheck = false
+Alert: PASS: doNotSpellCheck = false
+Alert: PASS: display = 2
+Alert: PASS: display = 3
+Alert: PASS: doc = [object global]
+Alert: PASS: doc threw Field.doc: Operation not supported.
+Alert: PASS: hidden = false
+Alert: PASS: hidden = true
+Alert: PASS: fileSelect = false
+Alert: PASS: fileSelect = false
+Alert: PASS: fillColor = T
+Alert: PASS: fillColor = T
+Alert: PASS: lineWidth = 1
+Alert: PASS: lineWidth = 4
+Alert: PASS: multiline = false
+Alert: PASS: multiline = false
+Alert: PASS: name = MyField.MyText
+Alert: PASS: name threw Field.name: Operation not supported.
+Alert: PASS: page = 0
+Alert: PASS: page threw Field.page: Cannot assign to readonly property.
+Alert: PASS: password = false
+Alert: PASS: password = false
+Alert: PASS: print = true
+Alert: PASS: print = false
+Alert: PASS: readonly = false
+Alert: PASS: readonly = false
+Alert: PASS: rect = 200,221,220,201
+Alert: PASS: rect threw Field.rect: Incorrect parameter value.
+Alert: PASS: richText = false
+Alert: PASS: richText = false
+Alert: PASS: richValue = undefined
+Alert: PASS: richValue = undefined
+Alert: PASS: rotation = 0
+Alert: PASS: rotation = 0
+Alert: PASS: source = undefined
+Alert: PASS: source = undefined
+Alert: PASS: strokeColor = T
+Alert: PASS: strokeColor = T
+Alert: PASS: submitName = undefined
+Alert: PASS: submitName = undefined
+Alert: PASS: textColor = T
+Alert: PASS: textColor = T
+Alert: PASS: textSize = 0
+Alert: PASS: textSize = 0
+Alert: PASS: type = text
+Alert: PASS: type threw Field.type: Operation not supported.
+Alert: PASS: userName = 
+Alert: PASS: userName = 
+Alert: PASS: value = bleen
+Alert: PASS: value = clams
+Alert: PASS: valueAsString = clams
+Alert: PASS: valueAsString threw Field.valueAsString: Operation not supported.
+Alert: PASS: buttonAlignX = 0
+Alert: PASS: buttonAlignX = 0
+Alert: PASS: buttonAlignY = 0
+Alert: PASS: buttonAlignY = 0
+Alert: PASS: buttonFitBounds = false
+Alert: PASS: buttonFitBounds = false
+Alert: PASS: buttonPosition = 0
+Alert: PASS: buttonPosition = 0
+Alert: PASS: buttonScaleHow = false
+Alert: PASS: buttonScaleHow = false
+Alert: PASS: buttonScaleWhen = 0
+Alert: PASS: buttonScaleWhen = 0
+Alert: PASS: highlight = invert
+Alert: PASS: highlight = invert
+Alert: PASS: type = button
+Alert: PASS: type threw Field.type: Operation not supported.
+Alert: PASS: exportValues = N
+Alert: PASS: exportValues threw Field.exportValues: Object no longer exists.
+Alert: PASS: radiosInUnison = false
+Alert: PASS: radiosInUnison = false
+Alert: PASS: style = check
+Alert: PASS: style = check
+Alert: PASS: type = radiobutton
+Alert: PASS: type threw Field.type: Operation not supported.
+Alert: PASS: value = Off
+Alert: PASS: value = Off
+Alert: PASS: valueAsString = Off
+Alert: PASS: valueAsString threw Field.valueAsString: Operation not supported.
+Alert: PASS: exportValues = N
+Alert: PASS: exportValues threw Field.exportValues: Object no longer exists.
+Alert: PASS: style = check
+Alert: PASS: style = check
+Alert: PASS: type = checkbox
+Alert: PASS: type threw Field.type: Operation not supported.
+Alert: PASS: value = Off
+Alert: PASS: value = Off
+Alert: PASS: valueAsString = Off
+Alert: PASS: valueAsString threw Field.valueAsString: Operation not supported.
+Alert: PASS: currentValueIndices = 2
+Alert: PASS: currentValueIndices = 1
+Alert: PASS: fileSelect = true
+Alert: PASS: fileSelect = true
diff --git a/testing/resources/javascript/globals.in b/testing/resources/javascript/globals.in
index 4812101..de67392 100644
--- a/testing/resources/javascript/globals.in
+++ b/testing/resources/javascript/globals.in
@@ -32,6 +32,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 // The "global" object stores data in a C-like manner, and
@@ -152,8 +153,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/icons.in b/testing/resources/javascript/icons.in
new file mode 100644
index 0000000..1b035db
--- /dev/null
+++ b/testing/resources/javascript/icons.in
@@ -0,0 +1,59 @@
+{{header}}
+{{include field.fragment}}
+{{object 16 0}} <<
+  {{streamlen}}
+>>
+stream
+// In order to get icons from a document, you have to first add icons
+// to the document, and the only way to initially get an icon object,
+// it would seem, is from a button in a field. Then it can be added
+// any number of times, which does nothing but accumulate the list of
+// names of the added icons. Whee.
+try {
+  var doc = this;
+  app.alert("doc is " + doc);
+  var icon = doc.getField("MyField.MyPushButton").buttonGetIcon();
+  app.alert("icon is " + icon);
+  doc.addIcon("icon1", icon);
+  doc.addIcon("icon2", icon);
+  app.alert("icon list is now " + doc.icons);
+  var icon1 = doc.icons[0];
+  var icon2 = doc.icons[1];
+  app.alert("they are named " + icon1.name + " and " + icon2.name);
+  var icon1_again = doc.getIcon("icon1");
+  var icon2_again = doc.getIcon("icon2");
+  app.alert("they are also named " + icon1_again.name +
+            " and " + icon2_again.name);
+  app.alert("but they are made anew each time since " +
+             (icon1 == icon1_again) + " is returned from comparison");
+  app.alert("Prototype comparison is " + (icon1.__proto__ == icon1_again.__proto__));
+
+  // Icons are "dynamic" objects, so theoretically we can make them from
+  // the JS side as well, although they will not be bound on the fxjs side.
+  var dubious = new icon1.constructor();
+  app.alert("Made anew from JS side since comparison is " + (dubious == icon1));
+  app.alert("Prototype comparison is " + (dubious.__proto__ == icon1.__proto__));
+
+  // Can't retrieve the name because no fxjs binding present.
+  app.alert("Dubious name is " + dubious.name);
+
+  // No error setting the name because for an unbound object, control doesn't
+  // get far enough to reach the readonly check in the property handler.
+  dubious.name = "pebble";
+
+  // The previous was a silent no-op under the covers.
+  app.alert("name is unchanged from " + dubious.name);
+} catch (e) {
+  app.alert("error: " + e);
+}
+try {
+  icon1.name = "chowder";
+} catch (e) {
+  app.alert("As expected, trying to change the name gave: " + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/icons_expected.txt b/testing/resources/javascript/icons_expected.txt
new file mode 100644
index 0000000..833c28b
--- /dev/null
+++ b/testing/resources/javascript/icons_expected.txt
@@ -0,0 +1,12 @@
+Alert: doc is [object global]
+Alert: icon is [object Object]
+Alert: icon list is now [object Object],[object Object]
+Alert: they are named icon1 and icon2
+Alert: they are also named icon1 and icon2
+Alert: but they are made anew each time since false is returned from comparison
+Alert: Prototype comparison is true
+Alert: Made anew from JS side since comparison is false
+Alert: Prototype comparison is true
+Alert: Dubious name is undefined
+Alert: name is unchanged from undefined
+Alert: As expected, trying to change the name gave: Icon.name: Cannot assign to readonly property.
diff --git a/testing/resources/javascript/listbox_methods.in b/testing/resources/javascript/listbox_methods.in
new file mode 100644
index 0000000..258ebb3
--- /dev/null
+++ b/testing/resources/javascript/listbox_methods.in
@@ -0,0 +1,172 @@
+{{header}}
+{{include field.fragment}}
+{{object 16 0}} <<
+  {{streamlen}}
+>>
+stream
+
+function getFieldValue(name) {
+  try {
+    var field = this.getField(name);
+    var v = field.value;
+    app.alert('Value is: ' + typeof(v) + ' ' + v);
+  } catch (e) {
+    app.alert('For getField(' + name + ') ERROR: ' + e);
+  }
+}
+
+function testField(name, x) {
+  try {
+    var field = this.getField(name);
+    field.value = x;
+    var y = field.value;
+    app.alert('Answer for "' + x + '" is: ' + typeof(y) + ' ' + y);
+  } catch (e) {
+    app.alert('For testField(' + x + ') ERROR: ' + e);
+  }
+}
+
+getFieldValue('MyField.MySingleSelect');
+
+// Valid cases
+testField('MyField.MySingleSelect', 'foo');
+testField('MyField.MySingleSelect', 'bar');
+testField('MyField.MySingleSelect', 'qux');
+testField('MyField.MySingleSelect', ['bar']);
+testField('MyField.MySingleSelect', ['qux']);
+
+getFieldValue('MyField.MySingleSelect');
+
+// Invalid cases
+testField('MyField.MySingleSelect', '');
+testField('MyField.MySingleSelect', 'goats');
+testField('MyField.MySingleSelect', 'b4');
+testField('MyField.MySingleSelect', 'b4.5');
+testField('MyField.MySingleSelect', '4x');
+testField('MyField.MySingleSelect', '4.5x');
+testField('MyField.MySingleSelect', 4);
+testField('MyField.MySingleSelect', '4');
+testField('MyField.MySingleSelect', ' 4');
+testField('MyField.MySingleSelect', '4 ');
+testField('MyField.MySingleSelect', '                          4                                      ');
+testField('MyField.MySingleSelect', '4 3 2 1');
+testField('MyField.MySingleSelect', '-4');
+testField('MyField.MySingleSelect', '23.00000001');
+testField('MyField.MySingleSelect', '23.00000000000000001');
+testField('MyField.MySingleSelect', 40000000000000000000000000);
+testField('MyField.MySingleSelect', '40000000000000000000000000');
+testField('MyField.MySingleSelect', '25,5');
+testField('MyField.MySingleSelect', '1e+5');
+testField('MyField.MySingleSelect', '1e5');
+testField('MyField.MySingleSelect', '1e-5');
+testField('MyField.MySingleSelect', '-1e-5');
+testField('MyField.MySingleSelect', '1.2e5');
+testField('MyField.MySingleSelect', Infinity);
+testField('MyField.MySingleSelect', 'Infinity');
+testField('MyField.MySingleSelect', 'INFINITY');
+testField('MyField.MySingleSelect', 'INF');
+testField('MyField.MySingleSelect', NaN);
+testField('MyField.MySingleSelect', 'NaN');
+testField('MyField.MySingleSelect', 'NAN');
+testField('MyField.MySingleSelect', '0x100');
+testField('MyField.MySingleSelect', '0x100.1');
+testField('MyField.MySingleSelect', '0x100,1');
+testField('MyField.MySingleSelect', '0x100x1');
+testField('MyField.MySingleSelect', '123x6');
+testField('MyField.MySingleSelect', '123xy6');
+testField('MyField.MySingleSelect', '123.y6');
+testField('MyField.MySingleSelect', '1,000,000');
+testField('MyField.MySingleSelect', '1.2.3');
+testField('MyField.MySingleSelect', '1-3');
+testField('MyField.MySingleSelect', '1+3');
+testField('MyField.MySingleSelect', '1.-3');
+testField('MyField.MySingleSelect', '1.+3');
+testField('MyField.MySingleSelect', [1, 2, 3, 4]);
+testField('MyField.MySingleSelect', '[1, 2, 3, 4]');
+testField('MyField.MySingleSelect', {a: 1, b: 2});
+testField('MyField.MySingleSelect', '{a: 1, b: 2}');
+testField('MyField.MySingleSelect', function(x) { return x+1; });
+testField('MyField.MySingleSelect', 'function(x) { return x+1; }');
+testField('MyField.MySingleSelect', 'Foo');
+testField('MyField.MySingleSelect', 'Bar');
+testField('MyField.MySingleSelect', 'Qux');
+testField('MyField.MySingleSelect', ['bar', 'qux']);
+testField('MyField.MySingleSelect', ['foo', 1]);
+
+getFieldValue('MyField.MySingleSelect');
+
+getFieldValue('MyField.MyMultiSelect');
+
+// Valid cases
+testField('MyField.MyMultiSelect', 'foo');
+testField('MyField.MyMultiSelect', 'bar');
+testField('MyField.MyMultiSelect', 'qux');
+testField('MyField.MyMultiSelect', ['bar']);
+testField('MyField.MyMultiSelect', ['bar', 'qux']);
+testField('MyField.MyMultiSelect', [1, 2, 3, 4]);
+testField('MyField.MyMultiSelect', ['foo', 1]);
+testField('MyField.MyMultiSelect', ['qux']);
+
+getFieldValue('MyField.MyMultiSelect');
+
+// Invalid cases
+testField('MyField.MyMultiSelect', '');
+testField('MyField.MyMultiSelect', 'goats');
+testField('MyField.MyMultiSelect', 'b4');
+testField('MyField.MyMultiSelect', 'b4.5');
+testField('MyField.MyMultiSelect', '4x');
+testField('MyField.MyMultiSelect', '4.5x');
+testField('MyField.MyMultiSelect', 4);
+testField('MyField.MyMultiSelect', '4');
+testField('MyField.MyMultiSelect', ' 4');
+testField('MyField.MyMultiSelect', '4 ');
+testField('MyField.MyMultiSelect', '                          4                                      ');
+testField('MyField.MyMultiSelect', '4 3 2 1');
+testField('MyField.MyMultiSelect', '-4');
+testField('MyField.MyMultiSelect', '23.00000001');
+testField('MyField.MyMultiSelect', '23.00000000000000001');
+testField('MyField.MyMultiSelect', 40000000000000000000000000);
+testField('MyField.MyMultiSelect', '40000000000000000000000000');
+testField('MyField.MyMultiSelect', '25,5');
+testField('MyField.MyMultiSelect', '1e+5');
+testField('MyField.MyMultiSelect', '1e5');
+testField('MyField.MyMultiSelect', '1e-5');
+testField('MyField.MyMultiSelect', '-1e-5');
+testField('MyField.MyMultiSelect', '1.2e5');
+testField('MyField.MyMultiSelect', Infinity);
+testField('MyField.MyMultiSelect', 'Infinity');
+testField('MyField.MyMultiSelect', 'INFINITY');
+testField('MyField.MyMultiSelect', 'INF');
+testField('MyField.MyMultiSelect', NaN);
+testField('MyField.MyMultiSelect', 'NaN');
+testField('MyField.MyMultiSelect', 'NAN');
+testField('MyField.MyMultiSelect', '0x100');
+testField('MyField.MyMultiSelect', '0x100.1');
+testField('MyField.MyMultiSelect', '0x100,1');
+testField('MyField.MyMultiSelect', '0x100x1');
+testField('MyField.MyMultiSelect', '123x6');
+testField('MyField.MyMultiSelect', '123xy6');
+testField('MyField.MyMultiSelect', '123.y6');
+testField('MyField.MyMultiSelect', '1,000,000');
+testField('MyField.MyMultiSelect', '1.2.3');
+testField('MyField.MyMultiSelect', '1-3');
+testField('MyField.MyMultiSelect', '1+3');
+testField('MyField.MyMultiSelect', '1.-3');
+testField('MyField.MyMultiSelect', '1.+3');
+testField('MyField.MyMultiSelect', '[1, 2, 3, 4]');
+testField('MyField.MyMultiSelect', {a: 1, b: 2});
+testField('MyField.MyMultiSelect', '{a: 1, b: 2}');
+testField('MyField.MyMultiSelect', function(x) { return x+1; });
+testField('MyField.MyMultiSelect', 'function(x) { return x+1; }');
+testField('MyField.MyMultiSelect', 'Foo');
+testField('MyField.MyMultiSelect', 'Bar');
+testField('MyField.MyMultiSelect', 'Qux');
+
+getFieldValue('MyField.MyMultiSelect');
+
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/listbox_methods_expected.txt b/testing/resources/javascript/listbox_methods_expected.txt
new file mode 100644
index 0000000..6f6c572
--- /dev/null
+++ b/testing/resources/javascript/listbox_methods_expected.txt
@@ -0,0 +1,124 @@
+Alert: Value is: string 
+Alert: Answer for "foo" is: string foo
+Alert: Answer for "bar" is: string bar
+Alert: Answer for "qux" is: string qux
+Alert: Answer for "bar" is: string bar
+Alert: Answer for "qux" is: string qux
+Alert: Value is: string qux
+Alert: Answer for "" is: string 
+Alert: Answer for "goats" is: string 
+Alert: Answer for "b4" is: string 
+Alert: Answer for "b4.5" is: string 
+Alert: Answer for "4x" is: string 
+Alert: Answer for "4.5x" is: string 
+Alert: Answer for "4" is: string 
+Alert: Answer for "4" is: string 
+Alert: Answer for " 4" is: string 
+Alert: Answer for "4 " is: string 
+Alert: Answer for "                          4                                      " is: string 
+Alert: Answer for "4 3 2 1" is: string 
+Alert: Answer for "-4" is: string 
+Alert: Answer for "23.00000001" is: string 
+Alert: Answer for "23.00000000000000001" is: string 
+Alert: Answer for "4e+25" is: string 
+Alert: Answer for "40000000000000000000000000" is: string 
+Alert: Answer for "25,5" is: string 
+Alert: Answer for "1e+5" is: string 
+Alert: Answer for "1e5" is: string 
+Alert: Answer for "1e-5" is: string 
+Alert: Answer for "-1e-5" is: string 
+Alert: Answer for "1.2e5" is: string 
+Alert: Answer for "Infinity" is: string 
+Alert: Answer for "Infinity" is: string 
+Alert: Answer for "INFINITY" is: string 
+Alert: Answer for "INF" is: string 
+Alert: Answer for "NaN" is: string 
+Alert: Answer for "NaN" is: string 
+Alert: Answer for "NAN" is: string 
+Alert: Answer for "0x100" is: string 
+Alert: Answer for "0x100.1" is: string 
+Alert: Answer for "0x100,1" is: string 
+Alert: Answer for "0x100x1" is: string 
+Alert: Answer for "123x6" is: string 
+Alert: Answer for "123xy6" is: string 
+Alert: Answer for "123.y6" is: string 
+Alert: Answer for "1,000,000" is: string 
+Alert: Answer for "1.2.3" is: string 
+Alert: Answer for "1-3" is: string 
+Alert: Answer for "1+3" is: string 
+Alert: Answer for "1.-3" is: string 
+Alert: Answer for "1.+3" is: string 
+Alert: Answer for "1,2,3,4" is: string 
+Alert: Answer for "[1, 2, 3, 4]" is: string 
+Alert: Answer for "[object Object]" is: string 
+Alert: Answer for "{a: 1, b: 2}" is: string 
+Alert: Answer for "function(x) { return x+1; }" is: string 
+Alert: Answer for "function(x) { return x+1; }" is: string 
+Alert: Answer for "Foo" is: string 
+Alert: Answer for "Bar" is: string 
+Alert: Answer for "Qux" is: string 
+Alert: Answer for "bar,qux" is: string qux
+Alert: Answer for "foo,1" is: string foo
+Alert: Value is: string foo
+Alert: Value is: string qux
+Alert: Answer for "foo" is: string foo
+Alert: Answer for "bar" is: string bar
+Alert: Answer for "qux" is: string qux
+Alert: Answer for "bar" is: string bar
+Alert: Answer for "bar,qux" is: object bar,qux
+Alert: Answer for "1,2,3,4" is: string 
+Alert: Answer for "foo,1" is: string foo
+Alert: Answer for "qux" is: string qux
+Alert: Value is: string qux
+Alert: Answer for "" is: string 
+Alert: Answer for "goats" is: string 
+Alert: Answer for "b4" is: string 
+Alert: Answer for "b4.5" is: string 
+Alert: Answer for "4x" is: string 
+Alert: Answer for "4.5x" is: string 
+Alert: Answer for "4" is: string 
+Alert: Answer for "4" is: string 
+Alert: Answer for " 4" is: string 
+Alert: Answer for "4 " is: string 
+Alert: Answer for "                          4                                      " is: string 
+Alert: Answer for "4 3 2 1" is: string 
+Alert: Answer for "-4" is: string 
+Alert: Answer for "23.00000001" is: string 
+Alert: Answer for "23.00000000000000001" is: string 
+Alert: Answer for "4e+25" is: string 
+Alert: Answer for "40000000000000000000000000" is: string 
+Alert: Answer for "25,5" is: string 
+Alert: Answer for "1e+5" is: string 
+Alert: Answer for "1e5" is: string 
+Alert: Answer for "1e-5" is: string 
+Alert: Answer for "-1e-5" is: string 
+Alert: Answer for "1.2e5" is: string 
+Alert: Answer for "Infinity" is: string 
+Alert: Answer for "Infinity" is: string 
+Alert: Answer for "INFINITY" is: string 
+Alert: Answer for "INF" is: string 
+Alert: Answer for "NaN" is: string 
+Alert: Answer for "NaN" is: string 
+Alert: Answer for "NAN" is: string 
+Alert: Answer for "0x100" is: string 
+Alert: Answer for "0x100.1" is: string 
+Alert: Answer for "0x100,1" is: string 
+Alert: Answer for "0x100x1" is: string 
+Alert: Answer for "123x6" is: string 
+Alert: Answer for "123xy6" is: string 
+Alert: Answer for "123.y6" is: string 
+Alert: Answer for "1,000,000" is: string 
+Alert: Answer for "1.2.3" is: string 
+Alert: Answer for "1-3" is: string 
+Alert: Answer for "1+3" is: string 
+Alert: Answer for "1.-3" is: string 
+Alert: Answer for "1.+3" is: string 
+Alert: Answer for "[1, 2, 3, 4]" is: string 
+Alert: Answer for "[object Object]" is: string 
+Alert: Answer for "{a: 1, b: 2}" is: string 
+Alert: Answer for "function(x) { return x+1; }" is: string 
+Alert: Answer for "function(x) { return x+1; }" is: string 
+Alert: Answer for "Foo" is: string 
+Alert: Answer for "Bar" is: string 
+Alert: Answer for "Qux" is: string 
+Alert: Value is: string 
diff --git a/testing/resources/javascript/mouse_events.evt b/testing/resources/javascript/mouse_events.evt
index 5710506..dc0105b 100644
--- a/testing/resources/javascript/mouse_events.evt
+++ b/testing/resources/javascript/mouse_events.evt
@@ -1,10 +1,15 @@
 # Mouse in, mouse out.
 mousemove,125,225
 mousemove,125,100
-# Mouse in, click in field.
+# Mouse in, click in right field - no effect.
 mousemove,125,225
+mousedown,right,125,225
+mouseup,right,125,225
+# click left in field - has effect.
 mousedown,left,125,225
 mouseup,left,125,225
+# Double-click in field - no effect.
+mousedoubleclick,left,125,225
 # Mouse out, click elsewhere.
 # This should trigger an On Blur event. (Bug 881)
 mousemove,125,100
diff --git a/testing/resources/javascript/mouse_events.in b/testing/resources/javascript/mouse_events.in
index a72162a..4c6935e 100644
--- a/testing/resources/javascript/mouse_events.in
+++ b/testing/resources/javascript/mouse_events.in
@@ -26,7 +26,11 @@
 >>
 % Forms
 {{object 4 0}} <<
-  /Fields [5 0 R]
+  /Fields [
+    5 0 R
+    6 0 R
+    7 0 R
+  ]
 >>
 % Field with actions:
 % Cursor enter: E
@@ -36,10 +40,10 @@
 % Focus: Fo
 % Blur: Bl
 {{object 5 0}} <<
- /FT /Tx
- /T (MyField)
  /Type /Annot
  /Subtype /Widget
+ /FT /Tx
+ /T (MyField)
  /Rect [100 200 150 250]
  /AA <<
    /E 10 0 R
@@ -50,40 +54,144 @@
    /Bl 15 0 R
  >>
 >>
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (name)
+ /Rect [400 400 500 429]
+ /V (Tralfaz)
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (age)
+ /Rect [400 440 500 469]
+ /V (12)
+>>
+endobj
 {{object 10 0}} <<
   /Type /Action
   /S /JavaScript
-  /JS (app.alert\("enter"\);)
+  /JS (
+    app.alert\("enter"\);
+    try {
+       this.submitForm\("myform", true, true, ["name", "age"]\);
+       app.alert\("ERROR: this.submitForm\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.submitForm blocked with " + e\);
+    }
+    try {
+       this.print\(\);
+       app.alert\("ERROR: this.print\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.print blocked with " + e\);
+    }
+  )
 >>
 endobj
 {{object 11 0}} <<
   /Type /Action
   /S /JavaScript
-  /JS (app.alert\("exit"\);)
+  /JS (
+    app.alert\("exit"\);
+    try {
+       this.submitForm\("myform", true, true, ["name", "age"]\);
+       app.alert\("ERROR: this.submitForm\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.submitForm blocked with " + e\);
+    }
+    try {
+       this.print\(\);
+       app.alert\("ERROR: this.print\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.print blocked with " + e\);
+    }
+  )
 >>
 endobj
 {{object 12 0}} <<
   /Type /Action
   /S /JavaScript
-  /JS (app.alert\("down"\);)
+  /JS (
+    app.alert\("down"\);
+    try {
+       this.submitForm\("https://example.com", true, true, ["name", "age"]\);
+    } catch \(e\) {
+       app.alert\("ERROR: " + e\);
+    }
+    try {
+       this.print\(\);
+       this.print\(false, 1, 10, true, true, true, true, true\);
+       this.print\({}\);
+       this.print\({"bUi": false, "nStart": 42, "nEnd": 17,
+            "bSilent": true, "bShrinkToFit": true, "bPrintAsImage": true,
+            "bReverse": true, "bAnnotations": true, "bogus": "yes"}\);
+    } catch \(e\) {
+       app.alert\("ERROR: " + e\);
+    }
+  )
 >>
 endobj
 {{object 13 0}} <<
   /Type /Action
   /S /JavaScript
-  /JS (app.alert\("up"\);)
+  /JS (
+    app.alert\("up"\);
+    try {
+       // Use different bUrl value this time around.
+       this.submitForm\("https://example.com", false, true, ["name", "age"]\);
+    } catch \(e\) {
+       app.alert\("ERROR: " + e\);
+    }
+    try {
+       this.print\(\);
+    } catch \(e\) {
+       app.alert\("ERROR: " + e\);
+    }
+  )
 >>
 endobj
 {{object 14 0}} <<
   /Type /Action
   /S /JavaScript
-  /JS (app.alert\("focus"\);)
+  /JS (
+    app.alert\("focus"\);
+    try {
+       this.submitForm\("myform", true, true, ["name", "age"]\);
+       app.alert\("ERROR: this.submitForm\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.submitForm blocked with " + e\);
+    }
+    try {
+       this.print\(\);
+       app.alert\("ERROR: this.print\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.print blocked with " + e\);
+    }
+  )
 >>
 endobj
 {{object 15 0}} <<
   /Type /Action
   /S /JavaScript
-  /JS (app.alert\("blur"\);)
+  /JS (
+    app.alert\("blur"\);
+    try {
+       this.submitForm\("myform", true, true, ["name", "age"]\);
+       app.alert\("ERROR: this.submitForm\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.submitForm blocked with " + e\);
+    }
+    try {
+       this.print\(\);
+       app.alert\("ERROR: this.print\(\) must not be allowed to execute"\);
+    } catch \(e\) {
+       app.alert\("PASS: this.print blocked with " + e\);
+    }
+  )
 >>
 endobj
 {{xref}}
diff --git a/testing/resources/javascript/mouse_events_expected.txt b/testing/resources/javascript/mouse_events_expected.txt
index 3d9b8b6..531a38b 100644
--- a/testing/resources/javascript/mouse_events_expected.txt
+++ b/testing/resources/javascript/mouse_events_expected.txt
@@ -1,10 +1,38 @@
 Alert: enter
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
 Alert: exit
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
 Alert: enter
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
 Alert: down
+Doc Submit Form: url=https://example.com + 174 data bytes:
+ 25 46 44 46 2d 31 2e 32 0d 0a 31 20 30 20 6f 62 6a 0d 0a 3c 3c 2f 46 44 46 3c 3c 2f 46 3c 3c 2f 46 28 6d 79 66 69 6c 65 2e 70 64 66 29 2f 54 79 70 65 2f 46 69 6c 65 73 70 65 63 2f 55 46 28 6d 79 66 69 6c 65 2e 70 64 66 29 3e 3e 2f 46 69 65 6c 64 73 5b 3c 3c 2f 54 28 6e 61 6d 65 29 2f 56 28 54 72 61 6c 66 61 7a 29 3e 3e 3c 3c 2f 54 28 61 67 65 29 2f 56 28 31 32 29 3e 3e 5d 3e 3e 3e 3e 0d 0a 65 6e 64 6f 62 6a 0d 0a 0d 0a 74 72 61 69 6c 65 72 0d 0a 3c 3c 2f 52 6f 6f 74 20 31 20 30 20 52 3e 3e 0d 0a 25 25 45 4f 46 0d 0a
+Doc Print: 1, 0, 0, 0, 0, 0, 0, 0
+Doc Print: 0, 1, 10, 1, 1, 1, 1, 1
+Doc Print: 1, 0, 0, 0, 0, 0, 0, 0
+Doc Print: 1, 42, 17, 1, 1, 1, 1, 1
 Alert: focus
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
 Alert: up
-Alert: exit
-Alert: enter
+Doc Submit Form: url=https://example.com + 19 data bytes:
+ 6e 61 6d 65 3d 54 72 61 6c 66 61 7a 26 61 67 65 3d 31 32
+Doc Print: 1, 0, 0, 0, 0, 0, 0, 0
 Alert: focus
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
 Alert: exit
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
+Alert: enter
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
+Alert: focus
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
+Alert: exit
+Alert: PASS: this.submitForm blocked with Document.submitForm: User gesture required.
+Alert: PASS: this.print blocked with Document.print: User gesture required.
diff --git a/testing/resources/javascript/property_test_helpers.js b/testing/resources/javascript/property_test_helpers.js
new file mode 100644
index 0000000..47a25ef
--- /dev/null
+++ b/testing/resources/javascript/property_test_helpers.js
@@ -0,0 +1,98 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Treat two distinct objects with the same members as equal, e.g. in JS
+// ([0, 1] == [0, 1]) evaluates to false. Without this function, we can't
+// supply an expected result of object/array type for our tests.
+function kindaSortaEqual(arg1, arg2) {
+  if (arg1 == arg2) {
+    return true;
+  }
+  if (typeof arg1 != "object") {
+    return false;
+  }
+  if (typeof arg2 != "object") {
+    return false;
+  }
+  return arg1.toString() == arg2.toString();
+}
+
+function testReadProperty(that, prop, expected) {
+  try {
+    var actual = that[prop];
+    if (kindaSortaEqual(actual, expected)) {
+      app.alert('PASS: ' + prop + ' = ' + actual);
+    } else {
+      app.alert('FAIL: ' + prop + ' = ' + actual + ', expected = ' + expected);
+    }
+  } catch (e) {
+    app.alert('ERROR: ' + e.toString());
+  }
+}
+
+function testUnreadableProperty(that, prop) {
+  try {
+    var value = that[prop];
+    app.alert('FAIL: ' + prop + ', expected to throw');
+  } catch (e) {
+    app.alert('PASS: ' + prop + ' threw ' + e.toString());
+  }
+}
+
+function testWriteProperty(that, prop, newValue) {
+  try {
+    that[prop] = newValue;
+    var actual = that[prop];
+    if (kindaSortaEqual(actual, newValue)) {
+      app.alert('PASS: ' + prop + ' = ' + actual);
+    } else {
+      app.alert('FAIL: ' + prop + ' = ' + actual + ', expected = ' + newValue);
+    }
+  } catch (e) {
+    app.alert('ERROR: ' + e.toString());
+  }
+}
+
+function testWriteIgnoredProperty(that, prop, expected, newValue) {
+  try {
+    that[prop] = newValue;
+    var actual = that[prop];
+    if (kindaSortaEqual(actual, expected)) {
+      app.alert('PASS: ' + prop + ' = ' + actual);
+    } else {
+      app.alert('FAIL: ' + prop + ' = ' + actual + ', expected = ' + expected);
+    }
+  } catch (e) {
+    app.alert('ERROR: ' + e.toString());
+  }
+}
+
+function testUnwritableProperty(that, prop, newValue) {
+  try {
+    that[prop] = newValue;
+    app.alert('FAIL: ' + prop + ' = ' + newValue + ', expected to throw');
+  } catch (e) {
+    app.alert('PASS: ' + prop + ' threw ' + e.toString());
+  }
+}
+
+function testRWProperty(that, prop, expected, newValue) {
+  testReadProperty(that, prop, expected);
+  testWriteProperty(that, prop, newValue);
+}
+
+function testRIProperty(that, prop, expected, newValue) {
+  testReadProperty(that, prop, expected);
+  testWriteIgnoredProperty(that, prop, expected, newValue);
+}
+
+function testROProperty(that, prop, expected) {
+  testReadProperty(that, prop, expected);
+  testUnwritableProperty(that, prop, 42);
+}
+
+function testXXProperty(that, prop) {
+  testUnreadableProperty(that, prop);
+  testUnwritableProperty(that, prop, 42);
+}
diff --git a/testing/resources/javascript/public_methods.evt b/testing/resources/javascript/public_methods.evt
new file mode 100644
index 0000000..0c25248
--- /dev/null
+++ b/testing/resources/javascript/public_methods.evt
@@ -0,0 +1,4 @@
+mousedown,left,110,170
+mouseup,left,110,170
+charcode,65
+
diff --git a/testing/resources/javascript/public_methods.in b/testing/resources/javascript/public_methods.in
new file mode 100644
index 0000000..140d020
--- /dev/null
+++ b/testing/resources/javascript/public_methods.in
@@ -0,0 +1,650 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [
+      4 0 R
+      10 0 R
+      11 0 R
+      12 0 R
+    ]
+    /DR 5 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Count 1
+  /Kids [ 3 0 R ]
+  /Type /Pages
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 200 ]
+  /Contents 8 0 R
+  /Annots [
+    4 0 R
+    10 0 R
+    11 0 R
+    12 0 R
+  ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 160 200 190 ]
+  /Subtype /Widget
+  /AA <<
+    /C 16 0 R
+    /F 17 0 R
+    /K 18 0 R
+    /V 19 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Font 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /F1 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Text2)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 0 200 30]
+  /V (123)
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Text3)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 40 200 70]
+  /V (456)
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Text4)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [100 80 200 110]
+  /V (407.96)
+>>
+endobj
+{{object 16 0}} <<
+  /S /JavaScript
+  /JS (
+    app.alert('Unexpected script 16');
+  )
+>>
+endobj
+{{object 17 0}} <<
+  /S /JavaScript
+  /JS (
+    function expect(initial, expression, expected) {
+      try {
+        event.value = initial;
+        var actual = eval(expression);
+        if (actual == expected) {
+          app.alert('PASS: ' + expression + ' = ' + actual);
+        } else {
+          app.alert('FAIL: ' + expression + ' = ' + actual + ', expected ' + expected + " ");
+        }
+      } catch (e) {
+        app.alert('ERROR: ' + e);
+      }
+    }
+
+    function expectEventValue(initial, expression, expected) {
+      try {
+        event.value = initial;
+        eval(expression);
+        var actual = event.value;
+        if (actual == expected) {
+          app.alert('PASS: ' + expression + ' = ' + actual);
+        } else {
+          app.alert('FAIL: ' + expression + ' = ' + actual + ', expected ' + expected + " ");
+        }
+      } catch (e) {
+        app.alert('ERROR: ' + e);
+      }
+    }
+
+    function expectError(initial, expression) {
+      try {
+        event.value = initial;
+        var actual = eval(expression);
+        app.alert('FAIL: ' + expression + ' = ' + actual + ', expected to throw');
+      } catch (e) {
+        app.alert('PASS: ' + expression + ' threw ' + e);
+      }
+    }
+
+    try {
+      app.alert("*** starting test 1 ***");
+
+      expectError('', "AFDate_Format()");
+      expectError('', "AFDate_Format(1, 2)");
+      expectEventValue("GMT", "AFDate_Format(1)", "1/1/70");
+      expectEventValue("PDT", "AFDate_Format(1)", "5/9/14");
+      expectEventValue("GMT", "AFDate_Format('blooey')", "1/1");
+      expectEventValue("PDT", "AFDate_Format('blooey')", "5/9");
+
+      app.alert("**********************");
+
+      expectError('', "AFDate_FormatEx()");
+      expectError('', "AFDate_FormatEx(1, 2)");
+      expectEventValue("x", "AFDate_FormatEx(2)", "2");
+      expectEventValue("x", "AFDate_FormatEx('blooey')", "blooey");
+      expectEventValue("x", "AFDate_FormatEx('m/d')", "5/9");
+      // TODO(crbug.com/572901): This is wrong. AFDate_FormatEx() should throw
+      // an error like Adobe Acrobat.
+      expectEventValue("12302015", "AFDate_FormatEx('mm/dd/yyyy')", "12/02/2015");
+      expectEventValue("20122015", "AFDate_FormatEx('mm/dd/yyyy')", "05/09/2014");
+
+      app.alert("**********************");
+
+      expectError('', "AFDate_Keystroke()");
+      expectError('', "AFDate_Keystroke(1, 2)");
+      expectEventValue("04/19", "AFDate_Keystroke(2)", "04/19");
+      expectEventValue("04/19/15", "AFDate_Keystroke('blooey')", "04/19/15");
+
+      app.alert("**********************");
+
+      expectError('', "AFDate_KeystrokeEx()");
+      expectError('', "AFDate_KeystrokeEx(1, 2)");
+      expectEventValue("x", "AFDate_KeystrokeEx(2)", "x");
+      expectEventValue("x", "AFDate_KeystrokeEx('blooey')", "x");
+      expectEventValue("x", "AFDate_KeystrokeEx('m/d')", "x");
+
+      app.alert("**********************");
+
+      expectError('', "AFExtractNums()");
+      expectError('', "AFExtractNums(1, 2)");
+      expect('', "AFExtractNums('100 200')", "100,200");
+
+      app.alert("**********************");
+
+      expectError('', "AFMakeNumber()");
+      expectError('', "AFMakeNumber(1, 2)");
+      expect('', "AFMakeNumber('2blooey')", 0);
+      expect('', "AFMakeNumber(1)", 1);
+      expect('', "AFMakeNumber('1.2')", 1.2);
+      expect('', "AFMakeNumber('1,2')", 1.2);
+
+      app.alert("**********************");
+
+      expectError('', "AFMergeChange()");
+      expectError('', "AFMergeChange(1, 2)");
+      expect("one", "AFMergeChange(undefined)", "one");
+
+      app.alert("**********************");
+
+      expectError('', "AFNumber_Format()");
+      expectError('', "AFNumber_Format(0, 1, 0, 0, '', false, 42)");
+      expectEventValue("blooey", "AFNumber_Format(0, 1, 0, 0, '', false)", 0);
+      expectEventValue(12, "AFNumber_Format(0, 1, 0, 0, '', false)", 12);
+
+      app.alert("**********************");
+
+      expectError('', "AFNumber_Keystroke()");
+      expectError('', "AFNumber_Keystroke(1)");
+      expectError("abc", "AFNumber_Keystroke(1, 2)");
+      expectEventValue("123", "AFNumber_Keystroke(1, 2)", "123");
+      expectEventValue("123", "AFNumber_Keystroke(1, 2, 3)", "123");
+
+      app.alert("**********************");
+
+      expectError('', "AFParseDateEx()");
+      expectError('', "AFParseDateEx(1, 2, 3)");
+      expect('', "AFParseDateEx(1, 2)", "1399672130000");
+
+      app.alert("**********************");
+
+      // Requires at least 2 arguments.
+      expectError('', "AFPercent_Format()");
+      expectError('', "AFPercent_Format(0)");
+
+      // Extra arguments are ignored.
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0, 0)", "-512.3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0, 0, 0)", "-512.3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0, 0, 0, 0)", "-512.3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0, 0, 0, 0, 0)", "-512.3%");
+
+      // There seems to be an extra bPercentPrepend parameter that may have been
+      // added in later versions.
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0, 0)", "-512.3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0, 1)", "%-512.3");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0, 2)", "%-512.3");
+      expectEventValue('', "AFPercent_Format(10, 0, 0)", "0.0000000000%");
+      expectEventValue('', "AFPercent_Format(10, 0, 1)", "%0.0000000000");
+      expectEventValue('', "AFPercent_Format(10, 0, 2)", "%0.0000000000");
+
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 0, 0)", "98,765,432,100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 1, 0)", "98765432100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 2, 0)", "98.765.432.100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 0, 1)", "%98,765,432,100");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 1, 1)", "%98765432100");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 2, 1)", "%98.765.432.100");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 0, 2)", "%98,765,432,100");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 1, 2)", "%98765432100");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 2, 2)", "%98.765.432.100");
+
+      // InvalidArgsError
+      expectError(0, "AFPercent_Format(-3, 0)");
+      expectError(0, "AFPercent_Format(-3, 1)");
+      expectError(0, "AFPercent_Format(-1, 3)");
+      expectError(0, "AFPercent_Format(0, -3)");
+      expectError(0, "AFPercent_Format(0, -1)");
+      expectError(0, "AFPercent_Format(0, 50)");
+      expectError(0, "AFPercent_Format(0, 51)");
+
+      expectEventValue('', "AFPercent_Format(0, 0)", "0%");
+      expectEventValue('', "AFPercent_Format(0, 1)", "0%");
+      expectEventValue('', "AFPercent_Format(0, 2)", "0%");
+      expectEventValue('', "AFPercent_Format(0, 3)", "0%");
+      expectEventValue('', "AFPercent_Format(0, 4)", "0%");
+      expectEventValue('', "AFPercent_Format(1, 0)", "0.0%");
+      expectEventValue('', "AFPercent_Format(1, 1)", "0.0%");
+      expectEventValue('', "AFPercent_Format(1, 2)", "0,0%");
+      expectEventValue('', "AFPercent_Format(1, 3)", "0,0%");
+      expectEventValue('', "AFPercent_Format(1, 4)", "0.0%");
+      expectEventValue('', "AFPercent_Format(2, 0)", "0.00%");
+      expectEventValue('', "AFPercent_Format(2, 1)", "0.00%");
+      expectEventValue('', "AFPercent_Format(2, 2)", "0,00%");
+      expectEventValue('', "AFPercent_Format(2, 3)", "0,00%");
+      expectEventValue('', "AFPercent_Format(2, 4)", "0.00%");
+      expectEventValue('', "AFPercent_Format(3, 0)", "0.000%");
+      expectEventValue('', "AFPercent_Format(3, 1)", "0.000%");
+      expectEventValue('', "AFPercent_Format(3, 2)", "0,000%");
+      expectEventValue('', "AFPercent_Format(3, 3)", "0,000%");
+      expectEventValue('', "AFPercent_Format(3, 4)", "0.000%");
+      expectEventValue('', "AFPercent_Format(10, 0)", "0.0000000000%");
+      expectEventValue('', "AFPercent_Format(10, 1)", "0.0000000000%");
+      expectEventValue('', "AFPercent_Format(10, 2)", "0,0000000000%");
+      expectEventValue('', "AFPercent_Format(10, 3)", "0,0000000000%");
+      expectEventValue('', "AFPercent_Format(10, 4)", "0.0000000000%");
+
+      expectEventValue(0, "AFPercent_Format(0, 0)", "0%");
+      expectEventValue(0, "AFPercent_Format(0, 1)", "0%");
+      expectEventValue(0, "AFPercent_Format(0, 2)", "0%");
+      expectEventValue(0, "AFPercent_Format(0, 3)", "0%");
+      expectEventValue(0, "AFPercent_Format(0, 4)", "0%");
+      expectEventValue(0, "AFPercent_Format(1, 0)", "0.0%");
+      expectEventValue(0, "AFPercent_Format(1, 1)", "0.0%");
+      expectEventValue(0, "AFPercent_Format(1, 2)", "0,0%");
+      expectEventValue(0, "AFPercent_Format(1, 3)", "0,0%");
+      expectEventValue(0, "AFPercent_Format(1, 4)", "0.0%");
+      expectEventValue(0, "AFPercent_Format(2, 0)", "0.00%");
+      expectEventValue(0, "AFPercent_Format(2, 1)", "0.00%");
+      expectEventValue(0, "AFPercent_Format(2, 2)", "0,00%");
+      expectEventValue(0, "AFPercent_Format(2, 3)", "0,00%");
+      expectEventValue(0, "AFPercent_Format(2, 4)", "0.00%");
+      expectEventValue(0, "AFPercent_Format(3, 0)", "0.000%");
+      expectEventValue(0, "AFPercent_Format(3, 1)", "0.000%");
+      expectEventValue(0, "AFPercent_Format(3, 2)", "0,000%");
+      expectEventValue(0, "AFPercent_Format(3, 3)", "0,000%");
+      expectEventValue(0, "AFPercent_Format(3, 4)", "0.000%");
+      expectEventValue(0, "AFPercent_Format(10, 0)", "0.0000000000%");
+      expectEventValue(0, "AFPercent_Format(10, 1)", "0.0000000000%");
+      expectEventValue(0, "AFPercent_Format(10, 2)", "0,0000000000%");
+      expectEventValue(0, "AFPercent_Format(10, 3)", "0,0000000000%");
+      expectEventValue(0, "AFPercent_Format(10, 4)", "0.0000000000%");
+
+      expectEventValue(-5.1234, "AFPercent_Format(0, 0)", "-512%");
+      expectEventValue(-5.1234, "AFPercent_Format(0, 1)", "-512%");
+      expectEventValue(-5.1234, "AFPercent_Format(0, 2)", "-512%");
+      expectEventValue(-5.1234, "AFPercent_Format(0, 3)", "-512%");
+      expectEventValue(-5.1234, "AFPercent_Format(0, 4)", "-512%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 0)", "-512.3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 1)", "-512.3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 2)", "-512,3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 3)", "-512,3%");
+      expectEventValue(-5.1234, "AFPercent_Format(1, 4)", "-512.3%");
+      expectEventValue(-5.1234, "AFPercent_Format(2, 0)", "-512.34%");
+      expectEventValue(-5.1234, "AFPercent_Format(2, 1)", "-512.34%");
+      expectEventValue(-5.1234, "AFPercent_Format(2, 2)", "-512,34%");
+      expectEventValue(-5.1234, "AFPercent_Format(2, 3)", "-512,34%");
+      expectEventValue(-5.1234, "AFPercent_Format(2, 4)", "-512.34%");
+      expectEventValue(-5.1234, "AFPercent_Format(3, 0)", "-512.340%");
+      expectEventValue(-5.1234, "AFPercent_Format(3, 1)", "-512.340%");
+      expectEventValue(-5.1234, "AFPercent_Format(3, 2)", "-512,340%");
+      expectEventValue(-5.1234, "AFPercent_Format(3, 3)", "-512,340%");
+      expectEventValue(-5.1234, "AFPercent_Format(3, 4)", "-512.340%");
+      expectEventValue(-5.1234, "AFPercent_Format(10, 0)", "-512.3400000000%");
+      expectEventValue(-5.1234, "AFPercent_Format(10, 1)", "-512.3400000000%");
+      expectEventValue(-5.1234, "AFPercent_Format(10, 2)", "-512,3400000000%");
+      expectEventValue(-5.1234, "AFPercent_Format(10, 3)", "-512,3400000000%");
+      expectEventValue(-5.1234, "AFPercent_Format(10, 4)", "-512.3400000000%");
+
+      expectEventValue(5.1234, "AFPercent_Format(0, 0)", "512%");
+      expectEventValue(5.1234, "AFPercent_Format(0, 1)", "512%");
+      expectEventValue(5.1234, "AFPercent_Format(0, 2)", "512%");
+      expectEventValue(5.1234, "AFPercent_Format(0, 3)", "512%");
+      expectEventValue(5.1234, "AFPercent_Format(0, 4)", "512%");
+      expectEventValue(5.1234, "AFPercent_Format(1, 0)", "512.3%");
+      expectEventValue(5.1234, "AFPercent_Format(1, 1)", "512.3%");
+      expectEventValue(5.1234, "AFPercent_Format(1, 2)", "512,3%");
+      expectEventValue(5.1234, "AFPercent_Format(1, 3)", "512,3%");
+      expectEventValue(5.1234, "AFPercent_Format(1, 4)", "512.3%");
+      expectEventValue(5.1234, "AFPercent_Format(2, 0)", "512.34%");
+      expectEventValue(5.1234, "AFPercent_Format(2, 1)", "512.34%");
+      expectEventValue(5.1234, "AFPercent_Format(2, 2)", "512,34%");
+      expectEventValue(5.1234, "AFPercent_Format(2, 3)", "512,34%");
+      expectEventValue(5.1234, "AFPercent_Format(2, 4)", "512.34%");
+      expectEventValue(5.1234, "AFPercent_Format(3, 0)", "512.340%");
+      expectEventValue(5.1234, "AFPercent_Format(3, 1)", "512.340%");
+      expectEventValue(5.1234, "AFPercent_Format(3, 2)", "512,340%");
+      expectEventValue(5.1234, "AFPercent_Format(3, 3)", "512,340%");
+      expectEventValue(5.1234, "AFPercent_Format(3, 4)", "512.340%");
+      expectEventValue(5.1234, "AFPercent_Format(10, 0)", "512.3400000000%");
+      expectEventValue(5.1234, "AFPercent_Format(10, 1)", "512.3400000000%");
+      expectEventValue(5.1234, "AFPercent_Format(10, 2)", "512,3400000000%");
+      expectEventValue(5.1234, "AFPercent_Format(10, 3)", "512,3400000000%");
+      expectEventValue(5.1234, "AFPercent_Format(10, 4)", "512.3400000000%");
+
+      expectEventValue(12.3456, "AFPercent_Format(1, 0)", "1,234.6%");
+      expectEventValue(12.3456, "AFPercent_Format(4, 1)", "1234.5600%");
+
+      expectEventValue(0.009876, "AFPercent_Format(0, 0)", "1%");
+      expectEventValue(0.009876, "AFPercent_Format(0, 1)", "1%");
+      expectEventValue(0.009876, "AFPercent_Format(0, 2)", "1%");
+      expectEventValue(0.009876, "AFPercent_Format(0, 3)", "1%");
+      expectEventValue(0.009876, "AFPercent_Format(0, 4)", "1%");
+      expectEventValue(0.009876, "AFPercent_Format(1, 0)", "1.0%");
+      expectEventValue(0.009876, "AFPercent_Format(1, 1)", "1.0%");
+      expectEventValue(0.009876, "AFPercent_Format(1, 2)", "1,0%");
+      expectEventValue(0.009876, "AFPercent_Format(1, 3)", "1,0%");
+      expectEventValue(0.009876, "AFPercent_Format(1, 4)", "1.0%");
+      expectEventValue(0.009876, "AFPercent_Format(2, 0)", "0.99%");
+      expectEventValue(0.009876, "AFPercent_Format(2, 1)", "0.99%");
+      expectEventValue(0.009876, "AFPercent_Format(2, 2)", "0,99%");
+      expectEventValue(0.009876, "AFPercent_Format(2, 3)", "0,99%");
+      expectEventValue(0.009876, "AFPercent_Format(2, 4)", "0.99%");
+      expectEventValue(0.009876, "AFPercent_Format(3, 0)", "0.988%");
+      expectEventValue(0.009876, "AFPercent_Format(3, 1)", "0.988%");
+      expectEventValue(0.009876, "AFPercent_Format(3, 2)", "0,988%");
+      expectEventValue(0.009876, "AFPercent_Format(3, 3)", "0,988%");
+      expectEventValue(0.009876, "AFPercent_Format(3, 4)", "0.988%");
+      expectEventValue(0.009876, "AFPercent_Format(10, 0)", "0.9876000000%");
+      expectEventValue(0.009876, "AFPercent_Format(10, 1)", "0.9876000000%");
+      expectEventValue(0.009876, "AFPercent_Format(10, 2)", "0,9876000000%");
+      expectEventValue(0.009876, "AFPercent_Format(10, 3)", "0,9876000000%");
+      expectEventValue(0.009876, "AFPercent_Format(10, 4)", "0.9876000000%");
+
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 0)", "98,765,432,100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 1)", "98765432100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 2)", "98.765.432.100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 3)", "98765432100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(0, 4)", "98'765'432'100%");
+      expectEventValue(987654321.001234, "AFPercent_Format(1, 0)", "98,765,432,100.1%");
+      expectEventValue(987654321.001234, "AFPercent_Format(1, 1)", "98765432100.1%");
+      expectEventValue(987654321.001234, "AFPercent_Format(1, 2)", "98.765.432.100,1%");
+      expectEventValue(987654321.001234, "AFPercent_Format(1, 3)", "98765432100,1%");
+      expectEventValue(987654321.001234, "AFPercent_Format(1, 4)", "98'765'432'100.1%");
+      expectEventValue(987654321.001234, "AFPercent_Format(2, 0)", "98,765,432,100.12%");
+      expectEventValue(987654321.001234, "AFPercent_Format(2, 1)", "98765432100.12%");
+      expectEventValue(987654321.001234, "AFPercent_Format(2, 2)", "98.765.432.100,12%");
+      expectEventValue(987654321.001234, "AFPercent_Format(2, 3)", "98765432100,12%");
+      expectEventValue(987654321.001234, "AFPercent_Format(2, 4)", "98'765'432'100.12%");
+      expectEventValue(987654321.001234, "AFPercent_Format(3, 0)", "98,765,432,100.123%");
+      expectEventValue(987654321.001234, "AFPercent_Format(3, 1)", "98765432100.123%");
+      expectEventValue(987654321.001234, "AFPercent_Format(3, 2)", "98.765.432.100,123%");
+      expectEventValue(987654321.001234, "AFPercent_Format(3, 3)", "98765432100,123%");
+      expectEventValue(987654321.001234, "AFPercent_Format(3, 4)", "98'765'432'100.123%");
+      expectEventValue(987654321.001234, "AFPercent_Format(10, 0)", "98,765,432,100.1234130859%");
+      expectEventValue(987654321.001234, "AFPercent_Format(10, 1)", "98765432100.1234130859%");
+      expectEventValue(987654321.001234, "AFPercent_Format(10, 2)", "98.765.432.100,1234130859%");
+      expectEventValue(987654321.001234, "AFPercent_Format(10, 3)", "98765432100,1234130859%");
+      expectEventValue(987654321.001234, "AFPercent_Format(10, 4)", "98'765'432'100.1234130859%");
+
+      expectEventValue(987654321, "AFPercent_Format(0, 0)", "98,765,432,100%");
+      expectEventValue(987654321, "AFPercent_Format(0, 1)", "98765432100%");
+      expectEventValue(987654321, "AFPercent_Format(0, 2)", "98.765.432.100%");
+      expectEventValue(987654321, "AFPercent_Format(0, 3)", "98765432100%");
+      expectEventValue(987654321, "AFPercent_Format(0, 4)", "98'765'432'100%");
+      expectEventValue(987654321, "AFPercent_Format(1, 0)", "98,765,432,100.0%");
+      expectEventValue(987654321, "AFPercent_Format(1, 1)", "98765432100.0%");
+      expectEventValue(987654321, "AFPercent_Format(1, 2)", "98.765.432.100,0%");
+      expectEventValue(987654321, "AFPercent_Format(1, 3)", "98765432100,0%");
+      expectEventValue(987654321, "AFPercent_Format(1, 4)", "98'765'432'100.0%");
+      expectEventValue(987654321, "AFPercent_Format(2, 0)", "98,765,432,100.00%");
+      expectEventValue(987654321, "AFPercent_Format(2, 1)", "98765432100.00%");
+      expectEventValue(987654321, "AFPercent_Format(2, 2)", "98.765.432.100,00%");
+      expectEventValue(987654321, "AFPercent_Format(2, 3)", "98765432100,00%");
+      expectEventValue(987654321, "AFPercent_Format(2, 4)", "98'765'432'100.00%");
+      expectEventValue(987654321, "AFPercent_Format(3, 0)", "98,765,432,100.000%");
+      expectEventValue(987654321, "AFPercent_Format(3, 1)", "98765432100.000%");
+      expectEventValue(987654321, "AFPercent_Format(3, 2)", "98.765.432.100,000%");
+      expectEventValue(987654321, "AFPercent_Format(3, 3)", "98765432100,000%");
+      expectEventValue(987654321, "AFPercent_Format(3, 4)", "98'765'432'100.000%");
+      expectEventValue(987654321, "AFPercent_Format(10, 0)", "98,765,432,100.0000000000%");
+      expectEventValue(987654321, "AFPercent_Format(10, 1)", "98765432100.0000000000%");
+      expectEventValue(987654321, "AFPercent_Format(10, 2)", "98.765.432.100,0000000000%");
+      expectEventValue(987654321, "AFPercent_Format(10, 3)", "98765432100,0000000000%");
+      expectEventValue(987654321, "AFPercent_Format(10, 4)", "98'765'432'100.0000000000%");
+
+      expectEventValue(0.01, "AFPercent_Format(1, 0)", "1.0%");
+      expectEventValue(0.001, "AFPercent_Format(1, 0)", "0.1%");
+      expectEventValue(0.0001, "AFPercent_Format(1, 0)", "0.0%");
+      expectEventValue(0.00001, "AFPercent_Format(1, 0)", "0.0%");
+      expectEventValue(0.000001, "AFPercent_Format(1, 0)", "0.0%");
+      expectEventValue(0.000001, "AFPercent_Format(10, 2)", "0,0001000000%");
+
+      // Acrobat behaves strangely with smaller values.
+      // expectEventValue(0.0000001, "AFPercent_Format(1, 0)", "-170.0%");
+      // expectEventValue(0.00000001, "AFPercent_Format(1, 0)", "-180.0%");
+
+      expectEventValue(0, "AFPercent_Format(20, 0)", "0.00000000000000000000%");
+      expectEventValue(0, "AFPercent_Format(308, 0)", "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%");
+      // Acrobat starts failing here.
+      // expectEventValue(0, "AFPercent_Format(309, 0)", "-1.#IND000...000%");
+
+      expectEventValue(0.000001, "AFPercent_Format(308, 0)", "0.00009999999999999999123964644631712417321978136897087097167968750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%");
+      // After 512, Acrobat probably ran out of buffer space.
+      expectEventValue(0, "AFPercent_Format(513, 0)", "%");
+
+      app.alert("**********************");
+
+      expectError('', "AFPercent_Keystroke()");
+      expectError('', "AFPercent_Keystroke(1)");
+      expectError("abc", "AFPercent_Keystroke(1, 0)");
+      expectEventValue(".123", "AFPercent_Keystroke(1, 0)", ".123");
+
+      app.alert("**********************");
+
+      expectError('', "AFRange_Validate()", '');
+      expectError('', "AFRange_Validate(1, 2, 3, 4, 5)", '');
+      expectEventValue("1", "AFRange_Validate(true, 2, true, 4)", "1");  // Notifies "between".
+      expectEventValue("5", "AFRange_Validate(true, 2, true, 4)", "5");  // Notifies "between".
+      expectEventValue("1", "AFRange_Validate(true, 2, false, 4)", "1");  // Notifies "greater".
+      expectEventValue("5", "AFRange_Validate(false, 2, true, 4)", "5");  // Notifies "less".
+      expectEventValue("3", "AFRange_Validate(true, 2, true, 4)", "3");   // No notification.
+      expectEventValue("1", "AFRange_Validate(false, 2, true, 4)", "1");  // No notification.
+      expectEventValue("5", "AFRange_Validate(true, 2, false, 4)", "5");  // No notification.
+
+      app.alert("**********************");
+
+      expectError('', "AFSimple()", 2);
+      expectError('', "AFSimple(1, 2, 3, 4)");
+      expectError('', "AFSimple(1, 2, 3)");
+      expectError('', "AFSimple('nonesuch', 2, 3)");
+      expect('', "AFSimple('AVG', 2, 3)", 2.5);
+      expect('', "AFSimple('MIN', 2, 3)", 2);
+      expect('', "AFSimple('MAX', 2, 3)", 3);
+      expect('', "AFSimple('SUM', 2, 3)", 5);
+      expect('', "AFSimple('PRD', 2, 3)", 6);
+      expectError('', "AFSimple('AVG', 2, 'nonesuch')");
+      expectError('', "AFSimple('MIN', 2, 'nonesuch')");
+      expectError('', "AFSimple('MAX', 2, 'nonesuch')");
+      expectError('', "AFSimple('SUM', 2, 'nonesuch')");
+      expectError('', "AFSimple('PRD', 2, 'nonesuch')");
+
+      app.alert("**********************");
+
+      // These two should throw "TypeError", not "AFSimple_Calculate".
+      // https://crbug.com/pdfium/1396
+      expectError('', "AFSimple_Calculate()");
+      expectError('', "AFSimple_Calculate(1)");
+      // This should "pass", but currently throws an error.
+      // https://crbug.com/pdfium/1396
+      expectEventValue('', "AFSimple_Calculate('blooey', ['Text2', 'Text3'])", 0);
+      // This should fail with TypeError, since a field does not exist, but
+      // currently passes. https://crbug.com/pdfium/1396
+      expectEventValue('', "AFSimple_Calculate('AVG', [1, 'nonesuch', {'crud': 32}])", 0);
+      expectEventValue('', "AFSimple_Calculate('AVG', ['Text2', 'Text3'])", (123 + 456) / 2);
+      expectEventValue('', "AFSimple_Calculate('SUM', ['Text2', 'Text3'])", 123 + 456);
+      expectEventValue('', "AFSimple_Calculate('PRD', ['Text2', 'Text3'])", 123 * 456);
+      expectEventValue('', "AFSimple_Calculate('MIN', ['Text2', 'Text3'])", 123);
+      expectEventValue('', "AFSimple_Calculate('MAX', ['Text2', 'Text3'])", 456);
+      expectEventValue('', "AFSimple_Calculate('AVG', 'Text2, Text3')", (123 + 456) / 2);
+      expectEventValue('', "AFSimple_Calculate('AVG', ['Text4'])", 407.96);
+      expectEventValue('', "AFSimple_Calculate('SUM', ['Text4'])", 407.96);
+      expectEventValue('', "AFSimple_Calculate('MIN', ['Text4'])", 407.96);
+      expectEventValue('', "AFSimple_Calculate('MAX', ['Text4'])", 407.96);
+      expectEventValue('', "AFSimple_Calculate('AVG', ['Text2', 'Text4'])", (123 + 407.96) / 2);
+      expectEventValue('', "AFSimple_Calculate('SUM', ['Text2', 'Text4'])", 123 + 407.96);
+      expectEventValue('', "AFSimple_Calculate('PRD', ['Text2', 'Text4'])", 50179.08);
+
+      app.alert("**********************");
+
+      expectError('', "AFSpecial_Format()", '');
+      expectError('', "AFSpecial_Format(1, 2)", '');
+      expectEventValue('', "AFSpecial_Format(0)", "");
+      expectEventValue('', "AFSpecial_Format(1)", "-");
+      expectEventValue('', "AFSpecial_Format(2)", "-");
+      expectEventValue('', "AFSpecial_Format(3)", "--");
+      expectEventValue("0123456789", "AFSpecial_Format(0)", "01234");
+      expectEventValue("0123456789", "AFSpecial_Format(1)", "01234-5678");
+      expectEventValue("0123456789", "AFSpecial_Format(2)", "(012) 345-6789");
+      expectEventValue("0123456789", "AFSpecial_Format(3)", "012-34-5678");
+
+      app.alert("**********************");
+
+      expectError('', "AFSpecial_Keystroke()");
+      expectError('', "AFSpecial_Keystroke(65, 66)");
+      expectEventValue("abc", "AFSpecial_Keystroke(65)", "abc");
+
+      app.alert("**********************");
+
+      expectError('', "AFSpecial_KeystrokeEx()", '');
+      expectEventValue("12345", "AFSpecial_KeystrokeEx('')", "12345");      // No notification.
+      expectEventValue("123", "AFSpecial_KeystrokeEx('9999')", "123");      // Notifies invalid.
+      expectEventValue("12345", "AFSpecial_KeystrokeEx('9999')", "12345");  // Notifies too long.
+      expectEventValue("abcd", "AFSpecial_KeystrokeEx('9999')", "abcd");    // Notifies invalid.
+      expectEventValue("1234", "AFSpecial_KeystrokeEx('9999')", "1234");
+      expectEventValue("abcd", "AFSpecial_KeystrokeEx('XXXX')", "abcd");
+
+      app.alert("**********************");
+
+      expectError('', "AFTime_Format()");
+      expectError('', "AFTime_Format(1, 2)");
+      expectEventValue(0, "AFTime_Format(1)", "9:48 pm");
+
+      app.alert("**********************");
+
+      expectError('', "AFTime_FormatEx()");
+      expectError('', "AFTime_FormatEx('blooey', 42)");
+      expectEventValue(0, "AFTime_FormatEx('blooey')", "blooey");
+
+      app.alert("**********************");
+
+      expectError('', "AFTime_Keystroke()", '');
+      expectError('', "AFTime_Keystroke(1, 2)", '');
+      expectEventValue("12:03", "AFTime_Keystroke(65)", "12:03");
+
+      app.alert("**********************");
+
+      expectError('', "AFTime_KeystrokeEx()");
+      expectError('', "AFTime_KeystrokeEx(1, 2)");
+      expectEventValue("12:04", "AFTime_KeystrokeEx('blooey')", "12:04");
+
+      app.alert("*** ending test 1 ***");
+    } catch (e) {
+      app.alert("Truly unexpected error occured: " + e);
+    }
+  )
+>>
+endobj
+{{object 18 0}} <<
+  /S /JavaScript
+  /JS (
+    // Re-run tests that depend on the "will commit" status of the event
+    // that triggered their execution.
+    try {
+      app.alert("*** starting test 2 ***");
+
+      expectError('', "AFDate_Keystroke()");
+      expectError('', "AFDate_Keystroke(1, 2)");
+      expectEventValue("04/19", "AFDate_Keystroke(2)", "04/19");
+      expectEventValue("04/19/15", "AFDate_Keystroke('blooey')", "04/19/15");
+
+      app.alert("**********************");
+
+      expectError('', "AFNumber_Keystroke()");
+      expectError('', "AFNumber_Keystroke(1)");
+      expectError("abc", "AFNumber_Keystroke(1, 2)");
+      expectEventValue("123", "AFNumber_Keystroke(1, 2)", "123");
+      expectEventValue("123", "AFNumber_Keystroke(1, 2, 3)", "123");
+
+      app.alert("**********************");
+
+      expectError('', "AFSpecial_KeystrokeEx()", '');
+      expectEventValue("12345", "AFSpecial_KeystrokeEx('')", "12345");      // No notification.
+      expectEventValue("123", "AFSpecial_KeystrokeEx('9999')", "123");      // Notifies invalid.
+      expectEventValue("12345", "AFSpecial_KeystrokeEx('9999')", "12345");  // Notifies too long.
+      expectEventValue("abcd", "AFSpecial_KeystrokeEx('9999')", "abcd");    // Notifies invalid.
+      expectEventValue("1234", "AFSpecial_KeystrokeEx('9999')", "1234");
+      expectEventValue("abcd", "AFSpecial_KeystrokeEx('XXXX')", "abcd");
+
+      app.alert("**********************");
+
+      expectError('', "AFMergeChange()");
+      expectError('', "AFMergeChange(1, 2)");
+      expect("one", "AFMergeChange(undefined)", "Aone");
+
+      app.alert("*** ending test 2 ***");
+    } catch (e) {
+      app.alert("Truly unexpected error occured: " + e);
+    }
+  )
+>>
+endobj
+{{object 19 0}} <<
+  /S /JavaScript
+  /JS (
+    app.alert('Unexpected script 19');
+  )
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/public_methods_expected.txt b/testing/resources/javascript/public_methods_expected.txt
new file mode 100644
index 0000000..17abeff
--- /dev/null
+++ b/testing/resources/javascript/public_methods_expected.txt
@@ -0,0 +1,400 @@
+Alert: *** starting test 1 ***
+Alert: PASS: AFDate_Format() threw AFDate_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_Format(1, 2) threw AFDate_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_Format(1) = 1/1/70
+Alert: PASS: AFDate_Format(1) = 5/9/14
+Alert: PASS: AFDate_Format('blooey') = 1/1
+Alert: PASS: AFDate_Format('blooey') = 5/9
+Alert: **********************
+Alert: PASS: AFDate_FormatEx() threw AFDate_FormatEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_FormatEx(1, 2) threw AFDate_FormatEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_FormatEx(2) = 2
+Alert: PASS: AFDate_FormatEx('blooey') = blooey
+Alert: PASS: AFDate_FormatEx('m/d') = 5/9
+Alert: PASS: AFDate_FormatEx('mm/dd/yyyy') = 12/02/2015
+Alert: PASS: AFDate_FormatEx('mm/dd/yyyy') = 05/09/2014
+Alert: **********************
+Alert: PASS: AFDate_Keystroke() threw AFDate_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_Keystroke(1, 2) threw AFDate_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_Keystroke(2) = 04/19
+Alert: PASS: AFDate_Keystroke('blooey') = 04/19/15
+Alert: **********************
+Alert: PASS: AFDate_KeystrokeEx() threw AFDate_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
+Alert: PASS: AFDate_KeystrokeEx(1, 2) threw AFDate_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
+[icon=3,type=0]: The input value can't be parsed as a valid date/time (2).
+Alert: PASS: AFDate_KeystrokeEx(2) = x
+[icon=3,type=0]: The input value can't be parsed as a valid date/time (blooey).
+Alert: PASS: AFDate_KeystrokeEx('blooey') = x
+[icon=3,type=0]: The input value can't be parsed as a valid date/time (m/d).
+Alert: PASS: AFDate_KeystrokeEx('m/d') = x
+Alert: **********************
+Alert: PASS: AFExtractNums() threw AFExtractNums: Incorrect number of parameters passed to function.
+Alert: PASS: AFExtractNums(1, 2) threw AFExtractNums: Incorrect number of parameters passed to function.
+Alert: PASS: AFExtractNums('100 200') = 100,200
+Alert: **********************
+Alert: PASS: AFMakeNumber() threw AFMakeNumber: Incorrect number of parameters passed to function.
+Alert: PASS: AFMakeNumber(1, 2) threw AFMakeNumber: Incorrect number of parameters passed to function.
+Alert: PASS: AFMakeNumber('2blooey') = 0
+Alert: PASS: AFMakeNumber(1) = 1
+Alert: PASS: AFMakeNumber('1.2') = 1.2
+Alert: PASS: AFMakeNumber('1,2') = 1.2
+Alert: **********************
+Alert: PASS: AFMergeChange() threw AFMergeChange: Incorrect number of parameters passed to function.
+Alert: PASS: AFMergeChange(1, 2) threw AFMergeChange: Incorrect number of parameters passed to function.
+Alert: PASS: AFMergeChange(undefined) = one
+Alert: **********************
+Alert: PASS: AFNumber_Format() threw AFNumber_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFNumber_Format(0, 1, 0, 0, '', false, 42) threw AFNumber_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFNumber_Format(0, 1, 0, 0, '', false) = 0
+Alert: PASS: AFNumber_Format(0, 1, 0, 0, '', false) = 12
+Alert: **********************
+Alert: PASS: AFNumber_Keystroke() threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFNumber_Keystroke(1) threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
+[icon=3,type=0]: The input value is invalid.
+Alert: PASS: AFNumber_Keystroke(1, 2) threw AFNumber_Keystroke: The input value is invalid.
+Alert: PASS: AFNumber_Keystroke(1, 2) = 123
+Alert: PASS: AFNumber_Keystroke(1, 2, 3) = 123
+Alert: **********************
+Alert: PASS: AFParseDateEx() threw AFParseDateEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFParseDateEx(1, 2, 3) threw AFParseDateEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFParseDateEx(1, 2) = 1399672130000
+Alert: **********************
+Alert: PASS: AFPercent_Format() threw AFPercent_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFPercent_Format(0) threw AFPercent_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFPercent_Format(1, 0, 0) = -512.3%
+Alert: PASS: AFPercent_Format(1, 0, 0, 0) = -512.3%
+Alert: PASS: AFPercent_Format(1, 0, 0, 0, 0) = -512.3%
+Alert: PASS: AFPercent_Format(1, 0, 0, 0, 0, 0) = -512.3%
+Alert: PASS: AFPercent_Format(1, 0, 0) = -512.3%
+Alert: PASS: AFPercent_Format(1, 0, 1) = %-512.3
+Alert: PASS: AFPercent_Format(1, 0, 2) = %-512.3
+Alert: PASS: AFPercent_Format(10, 0, 0) = 0.0000000000%
+Alert: PASS: AFPercent_Format(10, 0, 1) = %0.0000000000
+Alert: PASS: AFPercent_Format(10, 0, 2) = %0.0000000000
+Alert: PASS: AFPercent_Format(0, 0, 0) = 98,765,432,100%
+Alert: PASS: AFPercent_Format(0, 1, 0) = 98765432100%
+Alert: PASS: AFPercent_Format(0, 2, 0) = 98.765.432.100%
+Alert: PASS: AFPercent_Format(0, 0, 1) = %98,765,432,100
+Alert: PASS: AFPercent_Format(0, 1, 1) = %98765432100
+Alert: PASS: AFPercent_Format(0, 2, 1) = %98.765.432.100
+Alert: PASS: AFPercent_Format(0, 0, 2) = %98,765,432,100
+Alert: PASS: AFPercent_Format(0, 1, 2) = %98765432100
+Alert: PASS: AFPercent_Format(0, 2, 2) = %98.765.432.100
+Alert: PASS: AFPercent_Format(-3, 0) threw AFPercent_Format: Incorrect parameter value.
+Alert: PASS: AFPercent_Format(-3, 1) threw AFPercent_Format: Incorrect parameter value.
+Alert: PASS: AFPercent_Format(-1, 3) threw AFPercent_Format: Incorrect parameter value.
+Alert: PASS: AFPercent_Format(0, -3) threw AFPercent_Format: Incorrect parameter value.
+Alert: PASS: AFPercent_Format(0, -1) threw AFPercent_Format: Incorrect parameter value.
+Alert: PASS: AFPercent_Format(0, 50) threw AFPercent_Format: Incorrect parameter value.
+Alert: PASS: AFPercent_Format(0, 51) threw AFPercent_Format: Incorrect parameter value.
+Alert: PASS: AFPercent_Format(0, 0) = 0%
+Alert: PASS: AFPercent_Format(0, 1) = 0%
+Alert: PASS: AFPercent_Format(0, 2) = 0%
+Alert: PASS: AFPercent_Format(0, 3) = 0%
+Alert: PASS: AFPercent_Format(0, 4) = 0%
+Alert: PASS: AFPercent_Format(1, 0) = 0.0%
+Alert: PASS: AFPercent_Format(1, 1) = 0.0%
+Alert: PASS: AFPercent_Format(1, 2) = 0,0%
+Alert: PASS: AFPercent_Format(1, 3) = 0,0%
+Alert: PASS: AFPercent_Format(1, 4) = 0.0%
+Alert: PASS: AFPercent_Format(2, 0) = 0.00%
+Alert: PASS: AFPercent_Format(2, 1) = 0.00%
+Alert: PASS: AFPercent_Format(2, 2) = 0,00%
+Alert: PASS: AFPercent_Format(2, 3) = 0,00%
+Alert: PASS: AFPercent_Format(2, 4) = 0.00%
+Alert: PASS: AFPercent_Format(3, 0) = 0.000%
+Alert: PASS: AFPercent_Format(3, 1) = 0.000%
+Alert: PASS: AFPercent_Format(3, 2) = 0,000%
+Alert: PASS: AFPercent_Format(3, 3) = 0,000%
+Alert: PASS: AFPercent_Format(3, 4) = 0.000%
+Alert: PASS: AFPercent_Format(10, 0) = 0.0000000000%
+Alert: PASS: AFPercent_Format(10, 1) = 0.0000000000%
+Alert: PASS: AFPercent_Format(10, 2) = 0,0000000000%
+Alert: PASS: AFPercent_Format(10, 3) = 0,0000000000%
+Alert: PASS: AFPercent_Format(10, 4) = 0.0000000000%
+Alert: PASS: AFPercent_Format(0, 0) = 0%
+Alert: PASS: AFPercent_Format(0, 1) = 0%
+Alert: PASS: AFPercent_Format(0, 2) = 0%
+Alert: PASS: AFPercent_Format(0, 3) = 0%
+Alert: PASS: AFPercent_Format(0, 4) = 0%
+Alert: PASS: AFPercent_Format(1, 0) = 0.0%
+Alert: PASS: AFPercent_Format(1, 1) = 0.0%
+Alert: PASS: AFPercent_Format(1, 2) = 0,0%
+Alert: PASS: AFPercent_Format(1, 3) = 0,0%
+Alert: PASS: AFPercent_Format(1, 4) = 0.0%
+Alert: PASS: AFPercent_Format(2, 0) = 0.00%
+Alert: PASS: AFPercent_Format(2, 1) = 0.00%
+Alert: PASS: AFPercent_Format(2, 2) = 0,00%
+Alert: PASS: AFPercent_Format(2, 3) = 0,00%
+Alert: PASS: AFPercent_Format(2, 4) = 0.00%
+Alert: PASS: AFPercent_Format(3, 0) = 0.000%
+Alert: PASS: AFPercent_Format(3, 1) = 0.000%
+Alert: PASS: AFPercent_Format(3, 2) = 0,000%
+Alert: PASS: AFPercent_Format(3, 3) = 0,000%
+Alert: PASS: AFPercent_Format(3, 4) = 0.000%
+Alert: PASS: AFPercent_Format(10, 0) = 0.0000000000%
+Alert: PASS: AFPercent_Format(10, 1) = 0.0000000000%
+Alert: PASS: AFPercent_Format(10, 2) = 0,0000000000%
+Alert: PASS: AFPercent_Format(10, 3) = 0,0000000000%
+Alert: PASS: AFPercent_Format(10, 4) = 0.0000000000%
+Alert: PASS: AFPercent_Format(0, 0) = -512%
+Alert: PASS: AFPercent_Format(0, 1) = -512%
+Alert: PASS: AFPercent_Format(0, 2) = -512%
+Alert: PASS: AFPercent_Format(0, 3) = -512%
+Alert: PASS: AFPercent_Format(0, 4) = -512%
+Alert: PASS: AFPercent_Format(1, 0) = -512.3%
+Alert: PASS: AFPercent_Format(1, 1) = -512.3%
+Alert: PASS: AFPercent_Format(1, 2) = -512,3%
+Alert: PASS: AFPercent_Format(1, 3) = -512,3%
+Alert: PASS: AFPercent_Format(1, 4) = -512.3%
+Alert: PASS: AFPercent_Format(2, 0) = -512.34%
+Alert: PASS: AFPercent_Format(2, 1) = -512.34%
+Alert: PASS: AFPercent_Format(2, 2) = -512,34%
+Alert: PASS: AFPercent_Format(2, 3) = -512,34%
+Alert: PASS: AFPercent_Format(2, 4) = -512.34%
+Alert: PASS: AFPercent_Format(3, 0) = -512.340%
+Alert: PASS: AFPercent_Format(3, 1) = -512.340%
+Alert: PASS: AFPercent_Format(3, 2) = -512,340%
+Alert: PASS: AFPercent_Format(3, 3) = -512,340%
+Alert: PASS: AFPercent_Format(3, 4) = -512.340%
+Alert: PASS: AFPercent_Format(10, 0) = -512.3400000000%
+Alert: PASS: AFPercent_Format(10, 1) = -512.3400000000%
+Alert: PASS: AFPercent_Format(10, 2) = -512,3400000000%
+Alert: PASS: AFPercent_Format(10, 3) = -512,3400000000%
+Alert: PASS: AFPercent_Format(10, 4) = -512.3400000000%
+Alert: PASS: AFPercent_Format(0, 0) = 512%
+Alert: PASS: AFPercent_Format(0, 1) = 512%
+Alert: PASS: AFPercent_Format(0, 2) = 512%
+Alert: PASS: AFPercent_Format(0, 3) = 512%
+Alert: PASS: AFPercent_Format(0, 4) = 512%
+Alert: PASS: AFPercent_Format(1, 0) = 512.3%
+Alert: PASS: AFPercent_Format(1, 1) = 512.3%
+Alert: PASS: AFPercent_Format(1, 2) = 512,3%
+Alert: PASS: AFPercent_Format(1, 3) = 512,3%
+Alert: PASS: AFPercent_Format(1, 4) = 512.3%
+Alert: PASS: AFPercent_Format(2, 0) = 512.34%
+Alert: PASS: AFPercent_Format(2, 1) = 512.34%
+Alert: PASS: AFPercent_Format(2, 2) = 512,34%
+Alert: PASS: AFPercent_Format(2, 3) = 512,34%
+Alert: PASS: AFPercent_Format(2, 4) = 512.34%
+Alert: PASS: AFPercent_Format(3, 0) = 512.340%
+Alert: PASS: AFPercent_Format(3, 1) = 512.340%
+Alert: PASS: AFPercent_Format(3, 2) = 512,340%
+Alert: PASS: AFPercent_Format(3, 3) = 512,340%
+Alert: PASS: AFPercent_Format(3, 4) = 512.340%
+Alert: PASS: AFPercent_Format(10, 0) = 512.3400000000%
+Alert: PASS: AFPercent_Format(10, 1) = 512.3400000000%
+Alert: PASS: AFPercent_Format(10, 2) = 512,3400000000%
+Alert: PASS: AFPercent_Format(10, 3) = 512,3400000000%
+Alert: PASS: AFPercent_Format(10, 4) = 512.3400000000%
+Alert: PASS: AFPercent_Format(1, 0) = 1,234.6%
+Alert: PASS: AFPercent_Format(4, 1) = 1234.5600%
+Alert: PASS: AFPercent_Format(0, 0) = 1%
+Alert: PASS: AFPercent_Format(0, 1) = 1%
+Alert: PASS: AFPercent_Format(0, 2) = 1%
+Alert: PASS: AFPercent_Format(0, 3) = 1%
+Alert: PASS: AFPercent_Format(0, 4) = 1%
+Alert: PASS: AFPercent_Format(1, 0) = 1.0%
+Alert: PASS: AFPercent_Format(1, 1) = 1.0%
+Alert: PASS: AFPercent_Format(1, 2) = 1,0%
+Alert: PASS: AFPercent_Format(1, 3) = 1,0%
+Alert: PASS: AFPercent_Format(1, 4) = 1.0%
+Alert: PASS: AFPercent_Format(2, 0) = 0.99%
+Alert: PASS: AFPercent_Format(2, 1) = 0.99%
+Alert: PASS: AFPercent_Format(2, 2) = 0,99%
+Alert: PASS: AFPercent_Format(2, 3) = 0,99%
+Alert: PASS: AFPercent_Format(2, 4) = 0.99%
+Alert: PASS: AFPercent_Format(3, 0) = 0.988%
+Alert: PASS: AFPercent_Format(3, 1) = 0.988%
+Alert: PASS: AFPercent_Format(3, 2) = 0,988%
+Alert: PASS: AFPercent_Format(3, 3) = 0,988%
+Alert: PASS: AFPercent_Format(3, 4) = 0.988%
+Alert: PASS: AFPercent_Format(10, 0) = 0.9876000000%
+Alert: PASS: AFPercent_Format(10, 1) = 0.9876000000%
+Alert: PASS: AFPercent_Format(10, 2) = 0,9876000000%
+Alert: PASS: AFPercent_Format(10, 3) = 0,9876000000%
+Alert: PASS: AFPercent_Format(10, 4) = 0.9876000000%
+Alert: PASS: AFPercent_Format(0, 0) = 98,765,432,100%
+Alert: PASS: AFPercent_Format(0, 1) = 98765432100%
+Alert: PASS: AFPercent_Format(0, 2) = 98.765.432.100%
+Alert: PASS: AFPercent_Format(0, 3) = 98765432100%
+Alert: PASS: AFPercent_Format(0, 4) = 98'765'432'100%
+Alert: PASS: AFPercent_Format(1, 0) = 98,765,432,100.1%
+Alert: PASS: AFPercent_Format(1, 1) = 98765432100.1%
+Alert: PASS: AFPercent_Format(1, 2) = 98.765.432.100,1%
+Alert: PASS: AFPercent_Format(1, 3) = 98765432100,1%
+Alert: PASS: AFPercent_Format(1, 4) = 98'765'432'100.1%
+Alert: PASS: AFPercent_Format(2, 0) = 98,765,432,100.12%
+Alert: PASS: AFPercent_Format(2, 1) = 98765432100.12%
+Alert: PASS: AFPercent_Format(2, 2) = 98.765.432.100,12%
+Alert: PASS: AFPercent_Format(2, 3) = 98765432100,12%
+Alert: PASS: AFPercent_Format(2, 4) = 98'765'432'100.12%
+Alert: PASS: AFPercent_Format(3, 0) = 98,765,432,100.123%
+Alert: PASS: AFPercent_Format(3, 1) = 98765432100.123%
+Alert: PASS: AFPercent_Format(3, 2) = 98.765.432.100,123%
+Alert: PASS: AFPercent_Format(3, 3) = 98765432100,123%
+Alert: PASS: AFPercent_Format(3, 4) = 98'765'432'100.123%
+Alert: PASS: AFPercent_Format(10, 0) = 98,765,432,100.1234130859%
+Alert: PASS: AFPercent_Format(10, 1) = 98765432100.1234130859%
+Alert: PASS: AFPercent_Format(10, 2) = 98.765.432.100,1234130859%
+Alert: PASS: AFPercent_Format(10, 3) = 98765432100,1234130859%
+Alert: PASS: AFPercent_Format(10, 4) = 98'765'432'100.1234130859%
+Alert: PASS: AFPercent_Format(0, 0) = 98,765,432,100%
+Alert: PASS: AFPercent_Format(0, 1) = 98765432100%
+Alert: PASS: AFPercent_Format(0, 2) = 98.765.432.100%
+Alert: PASS: AFPercent_Format(0, 3) = 98765432100%
+Alert: PASS: AFPercent_Format(0, 4) = 98'765'432'100%
+Alert: PASS: AFPercent_Format(1, 0) = 98,765,432,100.0%
+Alert: PASS: AFPercent_Format(1, 1) = 98765432100.0%
+Alert: PASS: AFPercent_Format(1, 2) = 98.765.432.100,0%
+Alert: PASS: AFPercent_Format(1, 3) = 98765432100,0%
+Alert: PASS: AFPercent_Format(1, 4) = 98'765'432'100.0%
+Alert: PASS: AFPercent_Format(2, 0) = 98,765,432,100.00%
+Alert: PASS: AFPercent_Format(2, 1) = 98765432100.00%
+Alert: PASS: AFPercent_Format(2, 2) = 98.765.432.100,00%
+Alert: PASS: AFPercent_Format(2, 3) = 98765432100,00%
+Alert: PASS: AFPercent_Format(2, 4) = 98'765'432'100.00%
+Alert: PASS: AFPercent_Format(3, 0) = 98,765,432,100.000%
+Alert: PASS: AFPercent_Format(3, 1) = 98765432100.000%
+Alert: PASS: AFPercent_Format(3, 2) = 98.765.432.100,000%
+Alert: PASS: AFPercent_Format(3, 3) = 98765432100,000%
+Alert: PASS: AFPercent_Format(3, 4) = 98'765'432'100.000%
+Alert: PASS: AFPercent_Format(10, 0) = 98,765,432,100.0000000000%
+Alert: PASS: AFPercent_Format(10, 1) = 98765432100.0000000000%
+Alert: PASS: AFPercent_Format(10, 2) = 98.765.432.100,0000000000%
+Alert: PASS: AFPercent_Format(10, 3) = 98765432100,0000000000%
+Alert: PASS: AFPercent_Format(10, 4) = 98'765'432'100.0000000000%
+Alert: PASS: AFPercent_Format(1, 0) = 1.0%
+Alert: PASS: AFPercent_Format(1, 0) = 0.1%
+Alert: PASS: AFPercent_Format(1, 0) = 0.0%
+Alert: PASS: AFPercent_Format(1, 0) = 0.0%
+Alert: PASS: AFPercent_Format(1, 0) = 0.0%
+Alert: PASS: AFPercent_Format(10, 2) = 0,0001000000%
+Alert: PASS: AFPercent_Format(20, 0) = 0.00000000000000000000%
+Alert: PASS: AFPercent_Format(308, 0) = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%
+Alert: PASS: AFPercent_Format(308, 0) = 0.00009999999999999999123964644631712417321978136897087097167968750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%
+Alert: PASS: AFPercent_Format(513, 0) = %
+Alert: **********************
+Alert: PASS: AFPercent_Keystroke() threw AFPercent_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFPercent_Keystroke(1) threw AFPercent_Keystroke: Incorrect number of parameters passed to function.
+[icon=3,type=0]: The input value is invalid.
+Alert: PASS: AFPercent_Keystroke(1, 0) threw AFPercent_Keystroke: The input value is invalid.
+Alert: PASS: AFPercent_Keystroke(1, 0) = .123
+Alert: **********************
+Alert: PASS: AFRange_Validate() threw AFRange_Validate: Incorrect number of parameters passed to function.
+Alert: PASS: AFRange_Validate(1, 2, 3, 4, 5) threw AFRange_Validate: Incorrect number of parameters passed to function.
+[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
+Alert: PASS: AFRange_Validate(true, 2, true, 4) = 1
+[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
+Alert: PASS: AFRange_Validate(true, 2, true, 4) = 5
+[icon=3,type=0]: The input value must be greater than or equal to 2.
+Alert: PASS: AFRange_Validate(true, 2, false, 4) = 1
+[icon=3,type=0]: The input value must be less than or equal to 4.
+Alert: PASS: AFRange_Validate(false, 2, true, 4) = 5
+Alert: PASS: AFRange_Validate(true, 2, true, 4) = 3
+Alert: PASS: AFRange_Validate(false, 2, true, 4) = 1
+Alert: PASS: AFRange_Validate(true, 2, false, 4) = 5
+Alert: **********************
+Alert: PASS: AFSimple() threw AFSimple: Incorrect number of parameters passed to function.
+Alert: PASS: AFSimple(1, 2, 3, 4) threw AFSimple: Incorrect number of parameters passed to function.
+Alert: PASS: AFSimple(1, 2, 3) threw AFSimple: Incorrect parameter value.
+Alert: PASS: AFSimple('nonesuch', 2, 3) threw AFSimple: Incorrect parameter value.
+Alert: PASS: AFSimple('AVG', 2, 3) = 2.5
+Alert: PASS: AFSimple('MIN', 2, 3) = 2
+Alert: PASS: AFSimple('MAX', 2, 3) = 3
+Alert: PASS: AFSimple('SUM', 2, 3) = 5
+Alert: PASS: AFSimple('PRD', 2, 3) = 6
+Alert: PASS: AFSimple('AVG', 2, 'nonesuch') threw AFSimple: Incorrect parameter value.
+Alert: PASS: AFSimple('MIN', 2, 'nonesuch') threw AFSimple: Incorrect parameter value.
+Alert: PASS: AFSimple('MAX', 2, 'nonesuch') threw AFSimple: Incorrect parameter value.
+Alert: PASS: AFSimple('SUM', 2, 'nonesuch') threw AFSimple: Incorrect parameter value.
+Alert: PASS: AFSimple('PRD', 2, 'nonesuch') threw AFSimple: Incorrect parameter value.
+Alert: **********************
+Alert: PASS: AFSimple_Calculate() threw AFSimple_Calculate: Incorrect number of parameters passed to function.
+Alert: PASS: AFSimple_Calculate(1) threw AFSimple_Calculate: Incorrect number of parameters passed to function.
+Alert: ERROR: AFSimple_Calculate: Incorrect parameter value.
+Alert: PASS: AFSimple_Calculate('AVG', [1, 'nonesuch', {'crud': 32}]) = 0
+Alert: PASS: AFSimple_Calculate('AVG', ['Text2', 'Text3']) = 289.5
+Alert: PASS: AFSimple_Calculate('SUM', ['Text2', 'Text3']) = 579
+Alert: PASS: AFSimple_Calculate('PRD', ['Text2', 'Text3']) = 56088
+Alert: PASS: AFSimple_Calculate('MIN', ['Text2', 'Text3']) = 123
+Alert: PASS: AFSimple_Calculate('MAX', ['Text2', 'Text3']) = 456
+Alert: PASS: AFSimple_Calculate('AVG', 'Text2, Text3') = 289.5
+Alert: PASS: AFSimple_Calculate('AVG', ['Text4']) = 407.96
+Alert: PASS: AFSimple_Calculate('SUM', ['Text4']) = 407.96
+Alert: PASS: AFSimple_Calculate('MIN', ['Text4']) = 407.96
+Alert: PASS: AFSimple_Calculate('MAX', ['Text4']) = 407.96
+Alert: PASS: AFSimple_Calculate('AVG', ['Text2', 'Text4']) = 265.48
+Alert: PASS: AFSimple_Calculate('SUM', ['Text2', 'Text4']) = 530.96
+Alert: PASS: AFSimple_Calculate('PRD', ['Text2', 'Text4']) = 50179.08
+Alert: **********************
+Alert: PASS: AFSpecial_Format() threw AFSpecial_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFSpecial_Format(1, 2) threw AFSpecial_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFSpecial_Format(0) = 
+Alert: PASS: AFSpecial_Format(1) = -
+Alert: PASS: AFSpecial_Format(2) = -
+Alert: PASS: AFSpecial_Format(3) = --
+Alert: PASS: AFSpecial_Format(0) = 01234
+Alert: PASS: AFSpecial_Format(1) = 01234-5678
+Alert: PASS: AFSpecial_Format(2) = (012) 345-6789
+Alert: PASS: AFSpecial_Format(3) = 012-34-5678
+Alert: **********************
+Alert: PASS: AFSpecial_Keystroke() threw AFSpecial_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFSpecial_Keystroke(65, 66) threw AFSpecial_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFSpecial_Keystroke(65) = abc
+Alert: **********************
+Alert: PASS: AFSpecial_KeystrokeEx() threw AFSpecial_KeystrokeEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFSpecial_KeystrokeEx('') = 12345
+[icon=3,type=0]: The input value is invalid.
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = 123
+[icon=3,type=0]: The input value is too long.
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = 12345
+[icon=3,type=0]: The input value is invalid.
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = abcd
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = 1234
+Alert: PASS: AFSpecial_KeystrokeEx('XXXX') = abcd
+Alert: **********************
+Alert: PASS: AFTime_Format() threw AFTime_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFTime_Format(1, 2) threw AFTime_Format: Incorrect number of parameters passed to function.
+Alert: PASS: AFTime_Format(1) = 9:48 pm
+Alert: **********************
+Alert: PASS: AFTime_FormatEx() threw AFTime_FormatEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFTime_FormatEx('blooey', 42) threw AFTime_FormatEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFTime_FormatEx('blooey') = blooey
+Alert: **********************
+Alert: PASS: AFTime_Keystroke() threw AFTime_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFTime_Keystroke(1, 2) threw AFTime_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFTime_Keystroke(65) = 12:03
+Alert: **********************
+Alert: PASS: AFTime_KeystrokeEx() threw AFTime_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
+Alert: PASS: AFTime_KeystrokeEx(1, 2) threw AFTime_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
+Alert: PASS: AFTime_KeystrokeEx('blooey') = 12:04
+Alert: *** ending test 1 ***
+Alert: *** starting test 2 ***
+Alert: PASS: AFDate_Keystroke() threw AFDate_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_Keystroke(1, 2) threw AFDate_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFDate_Keystroke(2) = 04/19
+Alert: PASS: AFDate_Keystroke('blooey') = 04/19/15
+Alert: **********************
+Alert: PASS: AFNumber_Keystroke() threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
+Alert: PASS: AFNumber_Keystroke(1) threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
+Alert: FAIL: AFNumber_Keystroke(1, 2) = undefined, expected to throw
+Alert: PASS: AFNumber_Keystroke(1, 2) = 123
+Alert: PASS: AFNumber_Keystroke(1, 2, 3) = 123
+Alert: **********************
+Alert: PASS: AFSpecial_KeystrokeEx() threw AFSpecial_KeystrokeEx: Incorrect number of parameters passed to function.
+Alert: PASS: AFSpecial_KeystrokeEx('') = 12345
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = 123
+[icon=3,type=0]: The input value is too long.
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = 12345
+[icon=3,type=0]: The input value is too long.
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = abcd
+[icon=3,type=0]: The input value is too long.
+Alert: PASS: AFSpecial_KeystrokeEx('9999') = 1234
+[icon=3,type=0]: The input value is too long.
+Alert: PASS: AFSpecial_KeystrokeEx('XXXX') = abcd
+Alert: **********************
+Alert: PASS: AFMergeChange() threw AFMergeChange: Incorrect number of parameters passed to function.
+Alert: PASS: AFMergeChange(1, 2) threw AFMergeChange: Incorrect number of parameters passed to function.
+Alert: PASS: AFMergeChange(undefined) = Aone
+Alert: *** ending test 2 ***
diff --git a/testing/resources/javascript/unsupported.in b/testing/resources/javascript/unsupported.in
new file mode 100644
index 0000000..4d1ad5e
--- /dev/null
+++ b/testing/resources/javascript/unsupported.in
@@ -0,0 +1,62 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+function expectUnsupportedDangerousFeature(what) {
+  try {
+    var result = eval("typeof " + what);
+    if (result == "undefined") {
+      app.alert('PASS: ' + what + ' is not implemented');
+    } else {
+      app.alert('FAIL: ' + what + ' is implemented, probably a bad idea.');
+      app.alert('FAIL: see https://crbug.com/853237');
+    }
+  } catch (e) {
+    app.alert('Unexpected error ' + e.toString());
+  }
+}
+
+try {
+  expectUnsupportedDangerousFeature("ADBC");
+  expectUnsupportedDangerousFeature("Directory");
+  expectUnsupportedDangerousFeature("Net");
+  expectUnsupportedDangerousFeature("dbg");
+  expectUnsupportedDangerousFeature("security");
+} catch (e) {
+  app.alert('Truly unexpected error: ' + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/unsupported_expected.txt b/testing/resources/javascript/unsupported_expected.txt
new file mode 100644
index 0000000..c9cb77b
--- /dev/null
+++ b/testing/resources/javascript/unsupported_expected.txt
@@ -0,0 +1,5 @@
+Alert: PASS: ADBC is not implemented
+Alert: PASS: Directory is not implemented
+Alert: PASS: Net is not implemented
+Alert: PASS: dbg is not implemented
+Alert: PASS: security is not implemented
diff --git a/testing/resources/javascript/util_bytetochar.in b/testing/resources/javascript/util_bytetochar.in
index a9adfbc..531e679 100644
--- a/testing/resources/javascript/util_bytetochar.in
+++ b/testing/resources/javascript/util_bytetochar.in
@@ -32,6 +32,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 function TestOneInput(x) {
@@ -69,8 +70,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/util_bytetochar_expected.txt b/testing/resources/javascript/util_bytetochar_expected.txt
index df15ee7..487b89c 100644
--- a/testing/resources/javascript/util_bytetochar_expected.txt
+++ b/testing/resources/javascript/util_bytetochar_expected.txt
@@ -1,4 +1,4 @@
-Alert: 0 => 
+Alert: 0 => 0
 Alert: 65 => 65
 Alert: 127 => 127
 Alert: 128 => 128
diff --git a/testing/resources/javascript/util_printd.in b/testing/resources/javascript/util_printd.in
index 4d8610d..e4c8afc 100644
--- a/testing/resources/javascript/util_printd.in
+++ b/testing/resources/javascript/util_printd.in
@@ -32,6 +32,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 function TestOneFormat(str, d) {
@@ -50,6 +51,24 @@
     app.alert(str + ": Caught error: " + e);
   }
 }
+
+// Invalid arguments.
+try {
+  util.printd();
+} catch (e) {
+  app.alert('PASS: ' + e);
+}
+try {
+  util.printd('mm');
+} catch (e) {
+  app.alert('PASS: ' + e);
+}
+try {
+  util.printd('mm', new Date(undefined));
+} catch (e) {
+  app.alert('PASS: ' + e);
+}
+
 // July 4th, 2014 11:59:59 AM local time.
 var d1 = new Date(2014, 06, 04, 15, 59, 58);
 TestOneFormat("mm/dd/yyyy HH:MM:ss", d1);
@@ -129,8 +148,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/util_printd_expected.txt b/testing/resources/javascript/util_printd_expected.txt
index 76ede71..a642a4a 100644
--- a/testing/resources/javascript/util_printd_expected.txt
+++ b/testing/resources/javascript/util_printd_expected.txt
@@ -1,7 +1,10 @@
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 15:59:58
-Alert: 0: D:20140704155958
-Alert: 1: 2014.07.04 15:59:58
-Alert: 2: 2014/07/04 15:59:58
+Alert: PASS: util.printd: Incorrect number of parameters passed to function.
+Alert: PASS: util.printd: Incorrect number of parameters passed to function.
+Alert: PASS: util.printd: The second parameter is an invalid Date.
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 14:59:58
+Alert: 0: D:20140704145958
+Alert: 1: 2014.07.04 14:59:58
+Alert: 2: 2014/07/04 14:59:58
 Alert: 3: Caught error: util.printd: Incorrect parameter value.
 Alert: mmmm: July
 Alert: mmm: Jul
@@ -13,9 +16,9 @@
 Alert: d: 4
 Alert: yyyy: 2014
 Alert: yy: 14
-Alert: HH: 15
-Alert: H: 15
-Alert: hh: 03
+Alert: HH: 14
+Alert: H: 14
+Alert: hh: 02
 Alert: MM: 59
 Alert: M: 59
 Alert: ss: 58
@@ -23,10 +26,10 @@
 Alert: t: t
 Alert: abc.efg.i.kl.nopqr..uvwxyzABC.EFG.I.KL.NOPQR..UVWXYZ0123456780: abc.efg.i.kl.nopqr..uvwxyzABC.EFG.I.KL.NOPQR..UVWXYZ0123456780
 Alert: !@#$^&*()-_<>[];:~: !@#$^&*()-_<>[];:~
-Alert: %z %d %%z %%d %%%z %%%d %%% hh:MM: z 4 z 4 z 4  03:59
+Alert: %z %d %%z %%d %%%z %%%d %%% hh:MM: z 4 z 4 z 4  02:59
 Alert: : 
 Alert: mm/dd/yyyy: 07/04/2014
-Alert: mm/dd/yyyy: 01/01/1850
+Alert: mm/dd/yyyy: 12/31/1849
 Alert: mm/dd/yyyy: 12/31/2525
 Alert: mm/dd/yyyy: Caught error: util.printd: The second parameter can't be converted to a Date.
 Alert: undefined: Caught error: util.printd: The second parameter can't be converted to a Date.
@@ -38,17 +41,17 @@
 Alert: clams,3: Caught error: util.printd: Incorrect parameter type.
 Alert: mm: 07
 Alert: mm: Caught error: util.printd: Operation not supported.
-Alert: mm/dd/yyyy HH:MM:ss: 07/03/1900 14:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 09/04/2015 15:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/03/1900 15:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 09/04/2015 14:59:58
 Alert: mm/dd/yyyy HH:MM:ss: 12/09/2015 15:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/06/2014 02:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/05/2014 11:34:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 16:00:05
-Alert: mm/dd/yyyy HH:MM:ss: 05/01/2014 15:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/06/2014 01:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/05/2014 10:34:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 15:00:05
+Alert: mm/dd/yyyy HH:MM:ss: 05/01/2014 14:59:58
 Alert: mm/dd/yyyy HH:MM:ss: 03/02/2014 15:59:58
 Alert: mm/dd/yyyy HH:MM:ss: Caught error: util.printd: Incorrect parameter value.
 Alert: mm/dd/yyyy HH:MM:ss: 12/30/2013 15:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 06/29/2014 15:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/03/2014 23:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 14:59:58
-Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 15:58:59
+Alert: mm/dd/yyyy HH:MM:ss: 06/29/2014 14:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/03/2014 22:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 13:59:58
+Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 14:58:59
diff --git a/testing/resources/javascript/util_printx.in b/testing/resources/javascript/util_printx.in
index 7085edc..3002051 100644
--- a/testing/resources/javascript/util_printx.in
+++ b/testing/resources/javascript/util_printx.in
@@ -32,6 +32,7 @@
 endobj
 % JS program to exexute
 {{object 11 0}} <<
+  {{streamlen}}
 >>
 stream
 function TestOneFormat(fmt, src) {
@@ -43,6 +44,19 @@
     app.alert(title + ": Caught error: " + e);
   }
 }
+
+// Error cases
+try {
+  util.printx();
+} catch (e) {
+  app.alert('PASS: ' + e);
+}
+try {
+  util.printx("99");
+} catch (e) {
+  app.alert('PASS: ' + e);
+}
+
 TestOneFormat("", "");
 TestOneFormat("", "123");
 TestOneFormat("??", "");
@@ -81,8 +95,6 @@
 endstream
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/javascript/util_printx_expected.txt b/testing/resources/javascript/util_printx_expected.txt
index 124767d..3854f0d 100644
--- a/testing/resources/javascript/util_printx_expected.txt
+++ b/testing/resources/javascript/util_printx_expected.txt
@@ -1,3 +1,5 @@
+Alert: PASS: util.printx: Incorrect number of parameters passed to function.
+Alert: PASS: util.printx: Incorrect number of parameters passed to function.
 Alert: ('', '') => ''
 Alert: ('', '123') => ''
 Alert: ('??', '') => ''
diff --git a/testing/resources/javascript/util_scand.in b/testing/resources/javascript/util_scand.in
new file mode 100644
index 0000000..25462d0
--- /dev/null
+++ b/testing/resources/javascript/util_scand.in
@@ -0,0 +1,57 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include expect.js}}
+
+try {
+  const tuesday = "Tue, 20 Dec 2016 21:48:50 GMT";
+  const friday = "Fri, 09 May 2014 11:22:03 GMT";
+  expectError("util.scand()");
+  expectError("util.scand('12/20/2016')");
+  expect("util.scand('mm/dd/yyyy', '12/20/2016').toUTCString()", tuesday);
+  expect("util.scand('dd/mm/yyyy', '20/12/2016').toUTCString()", tuesday);
+  expect("util.scand('yyyy/mm/dd', '2016/12/20').toUTCString()", tuesday);
+  expect("util.scand('dd/mmm/yyyy', '20/Dec/2016').toUTCString()", tuesday);
+  expect("util.scand('..dd:-:mmm/yyyy', '**20/*/Dec.2016').toUTCString()", tuesday);
+  expect("util.scand('hh:MM:ss', '11:22:03').toUTCString()", friday);
+  expect("util.scand('s:MM:hh', '3:22:11').toUTCString()", friday);
+} catch (e) {
+  app.alert("Truly unexpected error: " + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/util_scand_expected.txt b/testing/resources/javascript/util_scand_expected.txt
new file mode 100644
index 0000000..cafe793
--- /dev/null
+++ b/testing/resources/javascript/util_scand_expected.txt
@@ -0,0 +1,9 @@
+Alert: PASS: util.scand() threw util.scand: Incorrect number of parameters passed to function.
+Alert: PASS: util.scand('12/20/2016') threw util.scand: Incorrect number of parameters passed to function.
+Alert: PASS: util.scand('mm/dd/yyyy', '12/20/2016').toUTCString() = Tue, 20 Dec 2016 21:48:50 GMT
+Alert: PASS: util.scand('dd/mm/yyyy', '20/12/2016').toUTCString() = Tue, 20 Dec 2016 21:48:50 GMT
+Alert: PASS: util.scand('yyyy/mm/dd', '2016/12/20').toUTCString() = Tue, 20 Dec 2016 21:48:50 GMT
+Alert: PASS: util.scand('dd/mmm/yyyy', '20/Dec/2016').toUTCString() = Tue, 20 Dec 2016 21:48:50 GMT
+Alert: PASS: util.scand('..dd:-:mmm/yyyy', '**20/*/Dec.2016').toUTCString() = Tue, 20 Dec 2016 21:48:50 GMT
+Alert: PASS: util.scand('hh:MM:ss', '11:22:03').toUTCString() = Fri, 09 May 2014 11:22:03 GMT
+Alert: PASS: util.scand('s:MM:hh', '3:22:11').toUTCString() = Fri, 09 May 2014 11:22:03 GMT
diff --git a/testing/resources/javascript/v8_features.in b/testing/resources/javascript/v8_features.in
new file mode 100644
index 0000000..3d90473
--- /dev/null
+++ b/testing/resources/javascript/v8_features.in
@@ -0,0 +1,58 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+% OpenAction action
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+function expect(str, expected) {
+  try {
+    var result = eval(str);
+    if (result == expected) {
+      app.alert('PASS: ' + str + ' = ' + result);
+    } else {
+      app.alert('FAIL: ' + str + ' = ' + result + ', expected = ' + expected);
+    }
+  } catch (e) {
+    app.alert('ERROR: ' + e.toString());
+  }
+}
+
+try {
+  expect("typeof gc", "function");
+  expect("typeof WebAssembly", "undefined");
+} catch (e) {
+  app.alert('Truly unexpected error: ' + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/v8_features_expected.txt b/testing/resources/javascript/v8_features_expected.txt
new file mode 100644
index 0000000..59db0db
--- /dev/null
+++ b/testing/resources/javascript/v8_features_expected.txt
@@ -0,0 +1,2 @@
+Alert: PASS: typeof gc = function
+Alert: PASS: typeof WebAssembly = undefined
diff --git a/testing/resources/javascript/xfa_specific/bug_1017494.evt b/testing/resources/javascript/xfa_specific/bug_1017494.evt
new file mode 100644
index 0000000..bf27458
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1017494.evt
@@ -0,0 +1,3 @@
+mousemove,0,0
+focus,87,0
+charcode,1
diff --git a/testing/resources/javascript/xfa_specific/bug_1017494.in b/testing/resources/javascript/xfa_specific/bug_1017494.in
new file mode 100644
index 0000000..586a6ff
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1017494.in
@@ -0,0 +1,35 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streanlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform layout="tb" name="subform2">
+    <field w="100pt" h="420pt" name="Field0"/>
+      <draw name="Field1">
+      <ui>
+        <barcode type="ean8"/>
+      </ui>
+      <value>
+        <text>12ab,.</text>
+      </value>
+    </draw>
+    <pageSet>
+      <pageArea name="PageArea3">
+        <contentArea h="44pt"/>
+      </pageArea>
+    </pageSet>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
diff --git a/testing/resources/javascript/xfa_specific/bug_1042956.pdf b/testing/resources/javascript/xfa_specific/bug_1042956.pdf
new file mode 100644
index 0000000..cc42fd7
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1042956.pdf
@@ -0,0 +1,19 @@
+%PDF-1.7
+1 0 obj
+<</Type /Catalog /Pages 2 0 R /AcroForm <</XFA 30 0 R>> /NeedsRendering true>>
+endobj
+2 0 obj
+<</Type /Pages /Kids [3 0 R] /Count 1>>
+endobj
+3 0 obj
+<</Type /Page /Parent 2 0 R /MediaBox [0 0 3 3]>>
+endobj
+30 0 obj
+<</Length 285>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/"><config><acrobat><acrobat7><dynamicRender>required</dynamicRender></acrobat7></acrobat></config><template><subform><desc name="N01" use=" .[N01.use=&quot; .[N01.#use]&quot;]"></desc><proto><bindItems></bindItems></proto></subform></template></xdp>
+endstream
+endobj
+trailer
+<</Root 1 0 R /Size 31>>
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1043510.evt b/testing/resources/javascript/xfa_specific/bug_1043510.evt
new file mode 100644
index 0000000..891a49d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1043510.evt
@@ -0,0 +1 @@
+mousedoubleclick,left,0,0
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1043510.pdf b/testing/resources/javascript/xfa_specific/bug_1043510.pdf
new file mode 100644
index 0000000..24a3cf1
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1043510.pdf
Binary files differ
diff --git a/testing/resources/javascript/xfa_specific/bug_1238_2.pdf b/testing/resources/javascript/xfa_specific/bug_1238_2.pdf
new file mode 100644
index 0000000..0822846
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1238_2.pdf
@@ -0,0 +1,24 @@
+%PDF
+2 0 obj<<
+>>
+endobj
+4 0 obj<<
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+  <template any="clams">
+    <proto use="xfa.context.!">
+endstream
+endobj
+5 0 obj <<
+  /XFA 4 0 R
+>>
+endobj
+6 0 obj <<
+ /AcroForm 5 0 R
+ /Pages 2 0 R
+/>>
+endobj
+trailer <<
+  /Root 6 0 R
+>>
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1238_2_expected.txt b/testing/resources/javascript/xfa_specific/bug_1238_2_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1238_2_expected.txt
diff --git a/testing/resources/javascript/xfa_specific/bug_976753.evt b/testing/resources/javascript/xfa_specific/bug_976753.evt
new file mode 100644
index 0000000..beda23a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_976753.evt
@@ -0,0 +1,6 @@
+mousedown,left,200,95
+mouseup,left,200,95
+charcode,72
+charcode,72
+charcode,72
+charcode,72
diff --git a/testing/resources/javascript/xfa_specific/bug_976753.in b/testing/resources/javascript/xfa_specific/bug_976753.in
new file mode 100644
index 0000000..7c67601
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_976753.in
@@ -0,0 +1,42 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <event activity="docReady" ref="$form">
+      <script>
+        Field0.rawValue = "dog"
+      </script>
+    </event>
+    <field x="0" y="0" w="1000pt" h="1000pt" name="Field0">
+      <event activity="change">
+        <script contentType="application/x-javascript">
+          xfa.host.setFocus('form1.Field1');
+        </script>
+      </event>
+    </field>
+    <field x="100pt" y="100pt" w="100pt" h="100pt" name="Field1">
+      <event activity="enter"  listen="refAndDescendents">
+        <script contentType="application/x-javascript">
+          xfa.host.setFocus('form1.Field0')
+          Field0.rawValue="m9";
+        </script>
+      </event>
+    </field>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_985781.in b/testing/resources/javascript/xfa_specific/bug_985781.in
new file mode 100644
index 0000000..2b20d33
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_985781.in
@@ -0,0 +1,73 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+<subform allowMacro="1" layout="lr" mergeMode="consumeData" name="subform_0">
+  <pageSet name="page_0">
+    <pageArea name="pagearea_0">
+      <contentArea h="0.5in" w="0.25in" x="1.0in" y="10.0in"></contentArea>
+      <subform allowMacro="1" layout="table" mergeMode="consumeData" name="subform_1">
+        <validate>
+          <script contentType="application/x-javascript">
+            xfa.resolveNode("xfa.form.subform_0.subform_3.field_5").execCalculate();
+          </script>
+        </validate>
+      </subform>
+    </pageArea>
+  </pageSet>
+  <subform allowMacro="0" layout="position" mergeMode="matchTemplate" name="subform_2">
+    <field name="field_3">
+      <items presence="visible" save="0">
+        <text>a</text>
+        <text>b</text>
+        <text>c</text>
+        <text>d</text>
+      </items>
+      <ui>
+        <numericEdit hScrollPolicy="on"></numericEdit>
+      </ui>
+      <validate>
+        <script contentType="application/x-javascript">
+          xfa.template.remerge();
+        </script>
+      </validate>
+      <value override="0">
+      <text>37</text>
+      </value>
+    </field>
+  </subform>
+  <subform allowMacro="0" layout="tb" mergeMode="matchTemplate" name="subform_3">
+    <bind match="dataRef" ref="a"></bind>
+    <field name="field_5">
+      <bind match="dataRef" ref="a"></bind>
+      <calculate>
+        <script contentType="application/x-javascript">
+          xfa.resolveNode("xfa.form.subform_0.page_0.pagearea_0.subform_1").leader = "test";
+          app.alert("Finished !!!");
+        </script>
+      </calculate>
+      <items presence="invisible" save="0">
+        <text>254</text>
+      </items>
+      <ui>
+        <choiceList commitOn="select" open="multiSelect" textEntry="0"></choiceList>
+      </ui>
+    </field>
+  </subform>
+</subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_985781_expected.txt b/testing/resources/javascript/xfa_specific/bug_985781_expected.txt
new file mode 100644
index 0000000..cc87d2b
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_985781_expected.txt
@@ -0,0 +1 @@
+Alert: Finished !!!
diff --git a/testing/resources/javascript/xfa_specific/bug_991899.in b/testing/resources/javascript/xfa_specific/bug_991899.in
new file mode 100644
index 0000000..9871a31
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_991899.in
@@ -0,0 +1,69 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items save="1">
+          <text>Single</text>
+          <text>Married</text>
+          <text>Other</text>
+        </items>
+      </field>
+      <field name="field3" h="10.625mm" w="30.625mm" x="5mm" y="50mm">
+        <event activity="exit">
+          <script contentType="application/x-javascript">
+            f3_exit += 1;
+            if (f3_exit == 1) {
+              f1 = xfa.resolveNode("xfa.form..field1");
+              xfa.host.setFocus(f1);
+              f4 = xfa.resolveNode("xfa.form..field4");
+              f4.instanceManager.addInstance(1);
+              f4.instanceManager.removeInstance(0);
+              xfa.host.openList(f1);
+            }
+          </script>
+        </event>
+      </field>
+      <subform name="field4" x="5mm" y="5mm">
+        <occur max="-1"/>
+        <field name="field5" w="64.77mm" h="6.35mm">
+        </field>
+      </subform>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+          f3_exit = 0;
+          f3 = xfa.resolveNode("xfa.form..field3");
+          xfa.host.setFocus(f3);
+          f5 = xfa.resolveNode("xfa.form..field5");
+          xfa.host.setFocus(f5);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_991899_expected.txt b/testing/resources/javascript/xfa_specific/bug_991899_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_991899_expected.txt
diff --git a/testing/resources/javascript/xfa_specific/bug_993771.in b/testing/resources/javascript/xfa_specific/bug_993771.in
new file mode 100644
index 0000000..e94fa58
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_993771.in
@@ -0,0 +1,78 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /NeedsRendering true
+  /AcroForm <<
+    /XFA 5 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 795 842 ]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+  <subform id="subformId_0" mergeMode="consumeData" name="subform_0">
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        xfa.resolveNode("xfa.form.subform_0.subform_1").layout = "tb";
+      </script>
+    </event>
+    <subform id="subformId_1" layout="table" mergeMode="consumeData" name="subform_1" >
+      <field id="fieldId_2" name="field_2" >
+        <event activity="docReady" >
+          <script contentType="application/x-javascript">
+            xfa.resolveNode("xfa.form.subform_0.subform_2").execValidate();
+          </script>
+        </event>
+      </field>
+      <field id="fieldId_3" name="field_3" >
+          <validate scriptTest="disabled" >
+            <script contentType="application/x-javascript">
+              xfa.resolveNode("xfa.form.subform_0.subform_1").keep.next = "pageArea";
+            </script>
+          </validate>
+      </field>
+    </subform>
+    <subformSet>
+      <breakBefore id="breakBeforeId_3" startNew="0" ></breakBefore>
+      <subform allowMacro="1" id="subformId_2" mergeMode="consumeData" name="subform_2" >
+        <field id="fieldId_5" name="field_5" >
+          <validate scriptTest="disabled" >
+            <script contentType="application/x-javascript">
+              xfa.host.setFocus("xfa.form.subform_0.subform_1.field_3");
+            </script>
+          </validate>
+        </field>
+        <field id="fieldId_6" name="field_6" >
+          <validate scriptTest="disabled" >
+            <script contentType="application/x-javascript">
+              xfa.resolveNode("xfa.form.subform_0.subform_1").layout = "position";
+            </script>
+          </validate>
+        </field>
+      </subform>
+    </subformSet>
+  </subform>
+</template>
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_993771_expected.txt b/testing/resources/javascript/xfa_specific/bug_993771_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_993771_expected.txt
diff --git a/testing/resources/javascript/xfa_specific/bug_995712.evt b/testing/resources/javascript/xfa_specific/bug_995712.evt
new file mode 100644
index 0000000..3a0748a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_995712.evt
@@ -0,0 +1,3 @@
+mousedown,left,100,100
+mouseup,left,100,100
+keycode,13
diff --git a/testing/resources/javascript/xfa_specific/bug_995712.in b/testing/resources/javascript/xfa_specific/bug_995712.in
new file mode 100644
index 0000000..c100b37
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_995712.in
@@ -0,0 +1,67 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+  <subform name="form1">
+    <pageSet>
+      <pageArea id="Page1" name="Page1">
+        <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+        <medium long="11in" short="8.5in" stock="letter"/>
+      </pageArea>
+    </pageSet>
+    <subform h="10.5in" w="8in" name="subform2">
+      <field h="500mm" name="pushButton0" w="500mm" x="1mm" y="1mm">
+        <ui>
+          <button/>
+        </ui>
+        <caption>
+          <value>
+            <text>ClickMe</text>
+          </value>
+        </caption>
+        <border>
+          <edge stroke="raised"/>
+        </border>
+        <event activity="mouseUp">
+          <script contentType="application/x-javascript">
+            count_mouseUp += 1;
+            if (count_mouseUp == 2) {
+              f1 = xfa.resolveNode("xfa.form..field1");
+              xfa.host.setFocus(f1);
+              xfa.template.remerge();
+              xfa.host.openList(f1);
+            }
+          </script>
+        </event>
+      </field>
+      <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+        <ui>
+          <choiceList/>
+        </ui>
+        <items>
+          <text>Foo</text>
+        </items>
+      </field>
+    </subform>
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        count_mouseUp = 0;
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_995712_expected.txt b/testing/resources/javascript/xfa_specific/bug_995712_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_995712_expected.txt
diff --git a/testing/resources/javascript/xfa_specific/bug_996123.in b/testing/resources/javascript/xfa_specific/bug_996123.in
new file mode 100644
index 0000000..aa67426
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_996123.in
@@ -0,0 +1,47 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /XFA 4 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [ 0 0 612 792 ]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+  <subform name="subform1">
+    <event activity="docReady">
+      <script contentType="application/x-javascript">
+        xfa.host.openList("subform1.subform2.combobox")
+      </script>
+    </event>
+    <subform name="subform2" >
+      <field name="combobox">
+        <ui><choiceList></choiceList></ui>
+      </field>
+    </subform>
+  </subform>
+</template>
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_997021.in b/testing/resources/javascript/xfa_specific/bug_997021.in
new file mode 100644
index 0000000..53ae098
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_997021.in
@@ -0,0 +1,30 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="subform1">
+    <field name="Field0">
+      <ui>
+        <numericEdit>
+          <comb numberOfCells="64515" />
+        </numericEdit>
+      </ui>
+    </field>
+  </subform>
+  <subform name="subform2" />
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_997021_expected.txt b/testing/resources/javascript/xfa_specific/bug_997021_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_997021_expected.txt
diff --git a/testing/resources/javascript/xfa_specific/cross_engine_apply.in b/testing/resources/javascript/xfa_specific/cross_engine_apply.in
new file mode 100644
index 0000000..cdaad2a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/cross_engine_apply.in
@@ -0,0 +1,52 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        app.alert('Applying app method to xfa.host');
+        try {
+          var result = app.setTimeOut.apply(xfa.host, 'var x = 3;', 10);
+          app.alert('unexpectedly returned ' + result);
+        }
+        catch (e) {
+          app.alert('Caught: ' + e);
+        }
+        app.alert('Applying xfa.host method to app');
+        try {
+          var result = xfa.host.setFocus.apply(app);
+          app.alert('unexpectedly returned ' + result);
+        }
+        catch (e) {
+          app.alert('Caught: ' + e);
+        }
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/cross_engine_apply_expected.txt b/testing/resources/javascript/xfa_specific/cross_engine_apply_expected.txt
new file mode 100644
index 0000000..98e3f6c
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/cross_engine_apply_expected.txt
@@ -0,0 +1,4 @@
+Alert: Applying app method to xfa.host
+Alert: Caught: TypeError: CreateListFromArrayLike called on non-object
+Alert: Applying xfa.host method to app
+Alert: Caught: XFAObject.setFocus: no Holder() present.
diff --git a/testing/resources/javascript/xfa_specific/list_methods.in b/testing/resources/javascript/xfa_specific/list_methods.in
new file mode 100644
index 0000000..341c1c8
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/list_methods.in
@@ -0,0 +1,95 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="subform1">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+        <medium long="297mm" short="210mm" stock="a4"/>
+      </pageArea>
+    </pageSet>
+    <subform layout="tb" name="subform2">
+      <occur initial="1" max="10" min="0" name="occur1">
+      </occur>
+      <field h="10mm" name="field1" w="40mm" x="10mm" y="10mm">
+        <event activity="ready" ref="$form">
+          <script contentType="application/x-javascript">
+            {{include ../expect.js}}
+            try {
+              var field = xfa.resolveNode("field2");
+              expect("field", "[object XFAObject]");
+
+              var list = xfa.record.nodes;
+              expect("list", "[object XFAObject]");
+              expect("list.length", 1);
+
+              var item = list.item(0);
+              expect("item", "[object XFAObject]");
+              expectError("list.item(1)");
+
+              // Incorrect argument types.
+              expectError("list.append(100)");
+              expectError("list.append({foo: 3})");
+
+              expect("list.append(field)", undefined);
+              expect("list.length", 2);
+
+              // Can only appear once.
+              expect("list.append(field)", undefined);
+              expect("list.length", 2);
+
+              // Incorrect argument types.
+              expectError("list.remove(100)");
+              expectError("list.remove({foo: 3})");
+
+              expect("list.remove(item)", undefined);
+              expect("list.length", 1);
+
+              // Removing a second time doesn't change things.
+              expect("list.remove(item)", undefined);
+              expect("list.length", 1);
+
+              expect("list.remove(field)", undefined);
+              expect("list.length", 0);
+
+              // Can't insert without a |before| element.
+              expect("list.append(field)", undefined);
+              expect("list.insert(item, field)", undefined);
+              expect("list.length", 2);
+
+              // Can only appear once.
+              expect("list.insert(item, field)", undefined);
+              expect("list.length", 2);
+
+              // TODO(tsepez): insertion before self hits a hard CHECK();
+              // expectError("list.insert(item, item)");
+
+            } catch (e) {
+              app.alert("truly unexpected error: " + e);
+            }
+          </script>
+        </event>
+      </field>
+      <field h="10mm" name="field2" w="40mm" x="10mm" y="10mm">
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/list_methods_expected.txt b/testing/resources/javascript/xfa_specific/list_methods_expected.txt
new file mode 100644
index 0000000..997826b
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/list_methods_expected.txt
@@ -0,0 +1,24 @@
+Alert: PASS: field = [object XFAObject]
+Alert: PASS: list = [object XFAObject]
+Alert: PASS: list.length = 1
+Alert: PASS: item = [object XFAObject]
+Alert: PASS: list.item(1) threw XFAObject.item: The input value is invalid.
+Alert: PASS: list.append(100) threw XFAObject.append: Incorrect parameter value.
+Alert: PASS: list.append({foo: 3}) threw XFAObject.append: Incorrect parameter value.
+Alert: PASS: list.append(field) = undefined
+Alert: PASS: list.length = 2
+Alert: PASS: list.append(field) = undefined
+Alert: PASS: list.length = 2
+Alert: PASS: list.remove(100) threw XFAObject.remove: Incorrect parameter value.
+Alert: PASS: list.remove({foo: 3}) threw XFAObject.remove: Incorrect parameter value.
+Alert: PASS: list.remove(item) = undefined
+Alert: PASS: list.length = 1
+Alert: PASS: list.remove(item) = undefined
+Alert: PASS: list.length = 1
+Alert: PASS: list.remove(field) = undefined
+Alert: PASS: list.length = 0
+Alert: PASS: list.append(field) = undefined
+Alert: PASS: list.insert(item, field) = undefined
+Alert: PASS: list.length = 2
+Alert: PASS: list.insert(item, field) = undefined
+Alert: PASS: list.length = 2
diff --git a/testing/resources/javascript/xfa_specific/resolve_nodes_1.evt b/testing/resources/javascript/xfa_specific/resolve_nodes_1.evt
new file mode 100644
index 0000000..baef0b4
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/resolve_nodes_1.evt
@@ -0,0 +1,3 @@
+mousedown,left,400,30
+mouseup,left,400,30
+
diff --git a/testing/resources/xfa/resolve_nodes.pdf b/testing/resources/javascript/xfa_specific/resolve_nodes_1.pdf
similarity index 100%
copy from testing/resources/xfa/resolve_nodes.pdf
copy to testing/resources/javascript/xfa_specific/resolve_nodes_1.pdf
Binary files differ
diff --git a/testing/resources/javascript/xfa_specific/resolve_nodes_1_expected.txt b/testing/resources/javascript/xfa_specific/resolve_nodes_1_expected.txt
new file mode 100644
index 0000000..335a4e2
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/resolve_nodes_1_expected.txt
@@ -0,0 +1 @@
+: Find 12 items
diff --git a/testing/resources/javascript/xfa_specific/resolve_nodes_2.evt b/testing/resources/javascript/xfa_specific/resolve_nodes_2.evt
new file mode 100644
index 0000000..906fdcd
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/resolve_nodes_2.evt
@@ -0,0 +1,3 @@
+mousedown,left,43,94
+mouseup,left,43,94
+
diff --git a/testing/resources/xfa/resolve_nodes.pdf b/testing/resources/javascript/xfa_specific/resolve_nodes_2.pdf
similarity index 100%
copy from testing/resources/xfa/resolve_nodes.pdf
copy to testing/resources/javascript/xfa_specific/resolve_nodes_2.pdf
Binary files differ
diff --git a/testing/resources/javascript/xfa_specific/resolve_nodes_2_expected.txt b/testing/resources/javascript/xfa_specific/resolve_nodes_2_expected.txt
new file mode 100644
index 0000000..251a6ef
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/resolve_nodes_2_expected.txt
@@ -0,0 +1 @@
+: field
diff --git a/testing/resources/javascript/xfa_specific/xfa_container.in b/testing/resources/javascript/xfa_specific/xfa_container.in
new file mode 100644
index 0000000..ec71376
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_container.in
@@ -0,0 +1,42 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        expect("typeof my_doc.getDelta", "function");
+        expect("typeof my_doc.getDeltas", "function");
+        expect("my_doc.getDelta('args', 'not', 'checked')", undefined);
+        expect("my_doc.getDeltas('args', 'not', 'checked')", "[object XFAObject]");
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_container_expected.txt b/testing/resources/javascript/xfa_specific/xfa_container_expected.txt
new file mode 100644
index 0000000..c76d4d8
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_container_expected.txt
@@ -0,0 +1,4 @@
+Alert: PASS: typeof my_doc.getDelta = function
+Alert: PASS: typeof my_doc.getDeltas = function
+Alert: PASS: my_doc.getDelta('args', 'not', 'checked') = undefined
+Alert: PASS: my_doc.getDeltas('args', 'not', 'checked') = [object XFAObject]
diff --git a/testing/resources/javascript/xfa_specific/xfa_datawindow_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_datawindow_pseudomodel.in
new file mode 100644
index 0000000..b0855e9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_datawindow_pseudomodel.in
@@ -0,0 +1,49 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        testRIProperty(xfa.dataWindow, "currentRecordNumber", undefined);
+        testRIProperty(xfa.dataWindow, "isDefined", undefined);
+        testRIProperty(xfa.dataWindow, "recordsAfter", undefined);
+        testRIProperty(xfa.dataWindow, "recordsBefore", undefined);
+        app.alert("finished testing properties");
+        expect("xfa.dataWindow.gotoRecord()", undefined);
+        expect("xfa.dataWindow.isRecordGroup()", undefined);
+        expect("xfa.dataWindow.moveCurrentRecord()", undefined);
+        expect("xfa.dataWindow.record()", undefined);
+        app.alert("finished testing methods");
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_datawindow_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_datawindow_pseudomodel_expected.txt
new file mode 100644
index 0000000..52564ad
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_datawindow_pseudomodel_expected.txt
@@ -0,0 +1,14 @@
+Alert: PASS: currentRecordNumber = undefined
+Alert: PASS: currentRecordNumber = undefined
+Alert: PASS: isDefined = undefined
+Alert: PASS: isDefined = undefined
+Alert: PASS: recordsAfter = undefined
+Alert: PASS: recordsAfter = undefined
+Alert: PASS: recordsBefore = undefined
+Alert: PASS: recordsBefore = undefined
+Alert: finished testing properties
+Alert: PASS: xfa.dataWindow.gotoRecord() = undefined
+Alert: PASS: xfa.dataWindow.isRecordGroup() = undefined
+Alert: PASS: xfa.dataWindow.moveCurrentRecord() = undefined
+Alert: PASS: xfa.dataWindow.record() = undefined
+Alert: finished testing methods
diff --git a/testing/resources/javascript/xfa_specific/xfa_event_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_event_pseudomodel.in
new file mode 100644
index 0000000..f09e807
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_event_pseudomodel.in
@@ -0,0 +1,72 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../property_test_helpers.js}}
+        try {
+          if (typeof test_counter == "undefined") {
+            test_counter = 1;
+            testRWProperty(xfa.event, "cancelAction", false, true);
+            testRWProperty(xfa.event, "change", "", "new foo");
+            testRIProperty(xfa.event, "commitKey", 0);
+            testRIProperty(xfa.event, "fullText", "");
+            testRIProperty(xfa.event, "keyDown", false);
+            testRIProperty(xfa.event, "modifier", false);
+            testRIProperty(xfa.event, "newContentType", "");
+            testRIProperty(xfa.event, "newText", "new foo");  // From above.
+            testRIProperty(xfa.event, "prevContentType", "");
+            testRIProperty(xfa.event, "prevText", "");
+            testRIProperty(xfa.event, "reenter", "");
+            testRIProperty(xfa.event, "selEnd", 0);
+            testRIProperty(xfa.event, "selStart", 0);
+            testRIProperty(xfa.event, "shift", false);
+            testRIProperty(xfa.event, "soapFaultCode", "");
+            testRIProperty(xfa.event, "soapFaultString", "");
+            testRIProperty(xfa.event, "target", undefined);
+            xfa.event.emit();  // Signal into ourselves.
+          } else if (test_counter == 1) {
+            test_counter = 2;
+            app.alert("Triggered by emit()");
+            xfa.event.emit();  // Signal into ourselves again.
+          } else if (test_counter == 2) {
+            app.alert("Triggered by emit() again");
+          } else {
+            app.alert("Something weird happened");
+          }
+          xfa.event.reset();
+        } catch (e) {
+           app.alert("truly unexpected " + e);
+        }
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_event_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_event_pseudomodel_expected.txt
new file mode 100644
index 0000000..63f8aeb
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_event_pseudomodel_expected.txt
@@ -0,0 +1,36 @@
+Alert: PASS: cancelAction = false
+Alert: PASS: cancelAction = true
+Alert: PASS: change = 
+Alert: PASS: change = new foo
+Alert: PASS: commitKey = 0
+Alert: PASS: commitKey = 0
+Alert: PASS: fullText = 
+Alert: PASS: fullText = 
+Alert: PASS: keyDown = false
+Alert: PASS: keyDown = false
+Alert: PASS: modifier = false
+Alert: PASS: modifier = false
+Alert: PASS: newContentType = 
+Alert: PASS: newContentType = 
+Alert: PASS: newText = new foo
+Alert: PASS: newText = new foo
+Alert: PASS: prevContentType = 
+Alert: PASS: prevContentType = 
+Alert: PASS: prevText = 
+Alert: PASS: prevText = 
+Alert: PASS: reenter = false
+Alert: PASS: reenter = false
+Alert: PASS: selEnd = 0
+Alert: PASS: selEnd = 0
+Alert: PASS: selStart = 0
+Alert: PASS: selStart = 0
+Alert: PASS: shift = false
+Alert: PASS: shift = false
+Alert: PASS: soapFaultCode = 
+Alert: PASS: soapFaultCode = 
+Alert: PASS: soapFaultString = 
+Alert: PASS: soapFaultString = 
+Alert: PASS: target = undefined
+Alert: PASS: target = undefined
+Alert: Triggered by emit()
+Alert: Triggered by emit() again
diff --git a/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel.in
new file mode 100644
index 0000000..7c3600e
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel.in
@@ -0,0 +1,114 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        testROProperty(xfa.host, "appType", "Exchange");
+        testRWProperty(xfa.host, "calculationsEnabled", true, false);
+        testRIProperty(xfa.host, "currentPage", -1, 0);
+        testROProperty(xfa.host, "language", "");
+        testROProperty(xfa.host, "name", "Acrobat");
+        testROProperty(xfa.host, "numPages", 2);
+        testROProperty(xfa.host, "platform", "");
+        testRIProperty(xfa.host, "title", "");
+        testRWProperty(xfa.host, "validationsEnabled", true, false);
+        testROProperty(xfa.host, "variation", "Full");
+        testROProperty(xfa.host, "version", "11");
+        app.alert("finished testing properties");
+
+        expectError("xfa.host.beep(1, 1)");
+        expect("xfa.host.beep(42)");
+        expect("xfa.host.beep()");
+
+        expect("xfa.host.documentCountInBatch()", 0);
+        expect("xfa.host.documentCountInBatch(42, 'args', ['ignored'])", 0);
+
+        expect("xfa.host.documentInBatch()", 0);
+        expect("xfa.host.documentInBatch(42, 'args', ['ignored'])", 0);
+
+        expectError("xfa.host.exportData()");
+        expectError("xfa.host.exportData(1, 2, 3)");
+        expect("xfa.host.exportData('to-evil-place')");
+        expect("xfa.host.exportData('to-evil-place', false)");
+
+        expect("xfa.host.getFocus()");
+        expect("xfa.host.getFocus('args', 'ignored')");
+
+        expectError("xfa.host.gotoURL()");
+        expectError("xfa.host.gotoURL(1, 2)");
+        expect("xfa.host.gotoURL('http://example.com')");
+
+        expectError("xfa.host.importData()");
+        expectError("xfa.host.importData(1, 2)");
+        expect("xfa.host.importData('from-good-place')");
+
+        expectError("xfa.host.openList()");
+        expectError("xfa.host.openList(1, 2)");
+        expect("xfa.host.openList('my_doc.subform_combox_0.combox')");
+        expect("xfa.host.openList(eval('my_doc.subform_combox_0'))");
+
+        expect("xfa.host.pageUp()");
+        expect("xfa.host.pageUp('ignored arg')");
+        expect("xfa.host.pageDown()");
+        expect("xfa.host.pageDown('ignored arg')");
+
+        expectError("xfa.host.print(1, 2, 3, 4, 5, 6, 7)");
+        expectError("xfa.host.print(1, 2, 3, 4, 5, 6, 7, 8, 9)");
+        expect("xfa.host.print(true, 1, 1, true, true, true, true, true)");
+
+        expectError("xfa.host.response()");
+        expectError("xfa.host.response(1, 2, 3, 4, 5)");
+        expect("xfa.host.response('Are you sure?', 'Launch Missiles', 'Yes', 1)", "No");
+
+        expectError("xfa.host.setFocus()");
+        expectError("xfa.host.setFocus('one', 'two')");
+        expect("xfa.host.setFocus('my_doc.subform_combox_0')");
+        expect("xfa.host.getFocus()", "[object XFAObject]");
+        expect("xfa.host.setFocus(eval('my_doc.subform_combox_0'))");
+        expect("xfa.host.getFocus()", "[object XFAObject]");
+
+        expectError("xfa.host.resetData(1, 2)");
+        expect("xfa.host.resetData()");
+        expect("xfa.host.resetData('xfa.host')");
+
+        app.alert("finished testing methods");
+      </script>
+    </event>
+    <subform layout="tb" name="subform_combox_0">
+      <occur initial="1" max="10" min="0" name="occur_subform_combox_0">
+      </occur>
+      <field h="10mm" name="combox" w="40mm" x="10mm" y="10mm">
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
new file mode 100644
index 0000000..3835e4a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
@@ -0,0 +1,70 @@
+Alert: PASS: appType = Exchange
+Alert: PASS: appType threw Error: Invalid property set operation.
+Alert: PASS: calculationsEnabled = true
+Alert: PASS: calculationsEnabled = false
+Alert: PASS: currentPage = -1
+Alert: PASS: currentPage = -1
+Alert: PASS: language = 
+Alert: PASS: language threw Error: Unable to set language value.
+Alert: PASS: name = Acrobat
+Alert: PASS: name threw Error: Invalid property set operation.
+Alert: PASS: numPages = 2
+Alert: PASS: numPages threw Error: Unable to set numPages value.
+Alert: PASS: platform = 
+Alert: PASS: platform threw Error: Unable to set platform value.
+Alert: PASS: title = 
+Alert: PASS: title = 
+Alert: PASS: validationsEnabled = true
+Alert: PASS: validationsEnabled = false
+Alert: PASS: variation = Full
+Alert: PASS: variation threw Error: Unable to set variation value.
+Alert: PASS: version = 11
+Alert: PASS: version threw Error: Unable to set version value.
+Alert: finished testing properties
+Alert: PASS: xfa.host.beep(1, 1) threw XFAObject.beep: Incorrect number of parameters passed to function.
+BEEP!!! 42
+Alert: PASS: xfa.host.beep(42) = undefined
+BEEP!!! 4
+Alert: PASS: xfa.host.beep() = undefined
+Alert: PASS: xfa.host.documentCountInBatch() = 0
+Alert: PASS: xfa.host.documentCountInBatch(42, 'args', ['ignored']) = 0
+Alert: PASS: xfa.host.documentInBatch() = 0
+Alert: PASS: xfa.host.documentInBatch(42, 'args', ['ignored']) = 0
+Alert: PASS: xfa.host.exportData() threw XFAObject.exportData: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.exportData(1, 2, 3) threw XFAObject.exportData: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.exportData('to-evil-place') = undefined
+Alert: PASS: xfa.host.exportData('to-evil-place', false) = undefined
+Alert: PASS: xfa.host.getFocus() = undefined
+Alert: PASS: xfa.host.getFocus('args', 'ignored') = undefined
+Alert: PASS: xfa.host.gotoURL() threw XFAObject.gotoURL: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.gotoURL(1, 2) threw XFAObject.gotoURL: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.gotoURL('http://example.com') = undefined
+Alert: PASS: xfa.host.importData() threw XFAObject.importData: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.importData(1, 2) threw XFAObject.importData: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.importData('from-good-place') = undefined
+Alert: PASS: xfa.host.openList() threw XFAObject.openList: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.openList(1, 2) threw XFAObject.openList: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.openList('my_doc.subform_combox_0.combox') = undefined
+Alert: PASS: xfa.host.openList(eval('my_doc.subform_combox_0')) = undefined
+Alert: PASS: xfa.host.pageUp() = undefined
+Alert: PASS: xfa.host.pageUp('ignored arg') = undefined
+Alert: PASS: xfa.host.pageDown() = undefined
+Alert: PASS: xfa.host.pageDown('ignored arg') = undefined
+Alert: PASS: xfa.host.print(1, 2, 3, 4, 5, 6, 7) threw XFAObject.print: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.print(1, 2, 3, 4, 5, 6, 7, 8, 9) threw XFAObject.print: Incorrect number of parameters passed to function.
+Doc Print: 1, 1, 1, 2, 4, 8, 16, 32
+Alert: PASS: xfa.host.print(true, 1, 1, true, true, true, true, true) = undefined
+Alert: PASS: xfa.host.response() threw XFAObject.response: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.response(1, 2, 3, 4, 5) threw XFAObject.response: Incorrect number of parameters passed to function.
+Launch Missiles: Are you sure?, defaultValue=Yes, label=, isPassword=1, length=2048
+Alert: PASS: xfa.host.response('Are you sure?', 'Launch Missiles', 'Yes', 1) = No
+Alert: PASS: xfa.host.setFocus() threw XFAObject.setFocus: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.setFocus('one', 'two') threw XFAObject.setFocus: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.setFocus('my_doc.subform_combox_0') = undefined
+Alert: PASS: xfa.host.getFocus() = [object XFAObject]
+Alert: PASS: xfa.host.setFocus(eval('my_doc.subform_combox_0')) = undefined
+Alert: PASS: xfa.host.getFocus() = [object XFAObject]
+Alert: PASS: xfa.host.resetData(1, 2) threw XFAObject.resetData: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.host.resetData() = undefined
+Alert: PASS: xfa.host.resetData('xfa.host') = undefined
+Alert: finished testing methods
diff --git a/testing/resources/javascript/xfa_specific/xfa_layout_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_layout_pseudomodel.in
new file mode 100644
index 0000000..2a8a20f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_layout_pseudomodel.in
@@ -0,0 +1,120 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        testROProperty(xfa.layout, "ready", true);
+
+        expectError("xfa.layout.absPage()");
+        expectError("xfa.layout.absPage(1, 2)");
+        expect("xfa.layout.absPage(1)", 0);
+
+        expect("xfa.layout.absPageCount()", 2);
+        expect("xfa.layout.absPageCount('args', [42], 'ignored')", 2);
+
+        expect("xfa.layout.absPageCountInBatch()", 0);
+        expect("xfa.layout.absPageCountInBatch('args', [42], 'ignored')", 0);
+
+        expectError("xfa.layout.absPageInBatch()");
+        expectError("xfa.layout.absPageInBatch(1, 2)");
+        expect("xfa.layout.absPageInBatch(1)", 0);
+
+        expectError("xfa.layout.absPageSpan()");
+        expectError("xfa.layout.absPageSpan(1, 2)");
+        expect("xfa.layout.absPageSpan(1)");
+
+        expectError("xfa.layout.h()");
+        expectError("xfa.layout.h(4, 'is', 'toomany', 'args')");
+        expectError("xfa.layout.h(my_doc, 'bogounits')", 0);
+        expect("xfa.layout.h(my_doc, 'cm')", 0);
+        expect("xfa.layout.h(my_doc, 'in', 2)", 0);
+
+        expectError("xfa.layout.page()");
+        expectError("xfa.layout.page(1, 2)");
+        expect("xfa.layout.page(my_doc)", 1);
+
+        expectError("xfa.layout.pageContent()");
+        expectError("xfa.layout.pageContent(4, 'is', 'too', 'many')");
+        expect("xfa.layout.pageContent(1)", "[object XFAObject]");
+        expect("xfa.layout.pageContent(1).length", 2);
+
+        expect("xfa.layout.pageCount()", 2);
+        expect("xfa.layout.pageCount('all', 'args', ['ignored'])", 2);
+
+        expectError("xfa.layout.pageSpan()");
+        expectError("xfa.layout.pageSpan(2, 'toomany')");
+        expect("xfa.layout.pageSpan(my_doc)", 1);
+
+        expect("xfa.layout.relayout()");
+        expect("xfa.layout.relayout('args', ['ignored'], 42)");
+
+        expect("xfa.layout.relayoutPageArea()");
+        expect("xfa.layout.relayoutPageArea('args', ['ignored'], 42)");
+
+        expectError("xfa.layout.sheet()");
+        expectError("xfa.layout.sheet(1, 2)");
+        expect("xfa.layout.sheet(my_doc)", 0);
+
+        expect("xfa.layout.sheetCount()", 2);
+        expect("xfa.layout.sheetCount('args', ['ignored'], 42)", 2);
+
+        expect("xfa.layout.sheetCountInBatch()", 0);
+        expect("xfa.layout.sheetCountInBatch('args', ['ignored'], 42)", 0);
+
+        expectError("xfa.layout.sheetInBatch()");
+        expectError("xfa.layout.sheetInBatch(2, 'toomany')");
+        expect("xfa.layout.sheetInBatch(1)", 0);
+
+        expectError("xfa.layout.w()");
+        expectError("xfa.layout.w(4, 'is', 'toomany', 'args')");
+        expectError("xfa.layout.w(my_doc, 'bogounits')", 0);
+        expect("xfa.layout.w(my_doc, 'cm')", 0);
+        expect("xfa.layout.w(my_doc, 'in', 2)", 0);
+
+        expectError("xfa.layout.x()");
+        expectError("xfa.layout.x(4, 'is', 'toomany', 'args')");
+        expectError("xfa.layout.x(my_doc, 'bogounits')", 0);
+        expect("xfa.layout.x(my_doc, 'cm')", 0);
+        expect("xfa.layout.x(my_doc, 'in', 2)", 0);
+
+        expectError("xfa.layout.y()");
+        expectError("xfa.layout.y(4, 'is', 'toomany', 'args')");
+        expectError("xfa.layout.y(my_doc, 'bogounits')", 0);
+        expect("xfa.layout.y(my_doc, 'cm')", 0);
+        expect("xfa.layout.y(my_doc, 'in', 2)", 0);
+
+        app.alert("finished testing methods");
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_layout_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_layout_pseudomodel_expected.txt
new file mode 100644
index 0000000..fb2708f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_layout_pseudomodel_expected.txt
@@ -0,0 +1,62 @@
+Alert: PASS: ready = true
+Alert: PASS: ready threw Error: Unable to set ready value.
+Alert: PASS: xfa.layout.absPage() threw XFAObject.absPage: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.absPage(1, 2) threw XFAObject.absPage: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.absPage(1) = 0
+Alert: PASS: xfa.layout.absPageCount() = 2
+Alert: PASS: xfa.layout.absPageCount('args', [42], 'ignored') = 2
+Alert: PASS: xfa.layout.absPageCountInBatch() = 0
+Alert: PASS: xfa.layout.absPageCountInBatch('args', [42], 'ignored') = 0
+Alert: PASS: xfa.layout.absPageInBatch() threw XFAObject.absPageInBatch: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.absPageInBatch(1, 2) threw XFAObject.absPageInBatch: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.absPageInBatch(1) = 0
+Alert: PASS: xfa.layout.absPageSpan() threw XFAObject.absPageSpan: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.absPageSpan(1, 2) threw XFAObject.absPageSpan: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.absPageSpan(1) = undefined
+Alert: PASS: xfa.layout.h() threw XFAObject.h: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.h(4, 'is', 'toomany', 'args') threw XFAObject.h: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.h(my_doc, 'bogounits') threw XFAObject.h: Incorrect parameter value.
+Alert: PASS: xfa.layout.h(my_doc, 'cm') = 0
+Alert: PASS: xfa.layout.h(my_doc, 'in', 2) = 0
+Alert: PASS: xfa.layout.page() threw XFAObject.page: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.page(1, 2) threw XFAObject.page: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.page(my_doc) = 1
+Alert: PASS: xfa.layout.pageContent() threw XFAObject.pageContent: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.pageContent(4, 'is', 'too', 'many') threw XFAObject.pageContent: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.pageContent(1) = [object XFAObject]
+Alert: PASS: xfa.layout.pageContent(1).length = 2
+Alert: PASS: xfa.layout.pageCount() = 2
+Alert: PASS: xfa.layout.pageCount('all', 'args', ['ignored']) = 2
+Alert: PASS: xfa.layout.pageSpan() threw XFAObject.pageSpan: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.pageSpan(2, 'toomany') threw XFAObject.pageSpan: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.pageSpan(my_doc) = 1
+Alert: PASS: xfa.layout.relayout() = undefined
+Alert: PASS: xfa.layout.relayout('args', ['ignored'], 42) = undefined
+Alert: PASS: xfa.layout.relayoutPageArea() = undefined
+Alert: PASS: xfa.layout.relayoutPageArea('args', ['ignored'], 42) = undefined
+Alert: PASS: xfa.layout.sheet() threw XFAObject.sheet: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.sheet(1, 2) threw XFAObject.sheet: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.sheet(my_doc) = 0
+Alert: PASS: xfa.layout.sheetCount() = 2
+Alert: PASS: xfa.layout.sheetCount('args', ['ignored'], 42) = 2
+Alert: PASS: xfa.layout.sheetCountInBatch() = 0
+Alert: PASS: xfa.layout.sheetCountInBatch('args', ['ignored'], 42) = 0
+Alert: PASS: xfa.layout.sheetInBatch() threw XFAObject.sheetInBatch: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.sheetInBatch(2, 'toomany') threw XFAObject.sheetInBatch: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.sheetInBatch(1) = 0
+Alert: PASS: xfa.layout.w() threw XFAObject.w: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.w(4, 'is', 'toomany', 'args') threw XFAObject.w: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.w(my_doc, 'bogounits') threw XFAObject.w: Incorrect parameter value.
+Alert: PASS: xfa.layout.w(my_doc, 'cm') = 0
+Alert: PASS: xfa.layout.w(my_doc, 'in', 2) = 0
+Alert: PASS: xfa.layout.x() threw XFAObject.x: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.x(4, 'is', 'toomany', 'args') threw XFAObject.x: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.x(my_doc, 'bogounits') threw XFAObject.x: Incorrect parameter value.
+Alert: PASS: xfa.layout.x(my_doc, 'cm') = 0
+Alert: PASS: xfa.layout.x(my_doc, 'in', 2) = 0
+Alert: PASS: xfa.layout.y() threw XFAObject.y: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.y(4, 'is', 'toomany', 'args') threw XFAObject.y: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.layout.y(my_doc, 'bogounits') threw XFAObject.y: Incorrect parameter value.
+Alert: PASS: xfa.layout.y(my_doc, 'cm') = 0
+Alert: PASS: xfa.layout.y(my_doc, 'in', 2) = 0
+Alert: finished testing methods
diff --git a/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
new file mode 100644
index 0000000..2de417e
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
@@ -0,0 +1,46 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        try {
+          xfa.log.message('Important Information');
+          xfa.log.trace('Important Information');
+          xfa.log.traceActivate();
+          xfa.log.traceDeactivate();
+          app.alert('Is trace enabled - ' + xfa.log.traceEnabled());
+          app.alert('finished testing methods');
+        } catch (e) {
+          app.alert("error testing methods: " + e);
+        }
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel_expected.txt
new file mode 100644
index 0000000..2a2eefc
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel_expected.txt
@@ -0,0 +1,2 @@
+Alert: Is trace enabled - undefined
+Alert: finished testing methods
diff --git a/testing/resources/javascript/xfa_specific/xfa_model.in b/testing/resources/javascript/xfa_specific/xfa_model.in
new file mode 100644
index 0000000..2433224
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_model.in
@@ -0,0 +1,51 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        testRIProperty(xfa.form, "aliasNode", undefined);
+        testRIProperty(xfa.form, "context", undefined);
+        expect("typeof xfa.form.clearErrorList", "function");
+        expect("typeof xfa.form.createNode", "function");
+        expect("typeof xfa.form.isCompatibleNS", "function");
+        expect("xfa.form.clearErrorList('args', 'ignored')", undefined);
+        expectError("xfa.form.createNode()");
+        expect("xfa.form.createNode('bogus')", undefined);
+        expect("xfa.form.createNode('template')", "[object XFAObject]");
+        expectError("xfa.form.isCompatibleNS()");
+        expect("xfa.form.isCompatibleNS('bogus')", false);
+        expect("xfa.form.isCompatibleNS('')", true);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_model_expected.txt b/testing/resources/javascript/xfa_specific/xfa_model_expected.txt
new file mode 100644
index 0000000..7613cca
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_model_expected.txt
@@ -0,0 +1,14 @@
+Alert: PASS: aliasNode = undefined
+Alert: PASS: aliasNode = undefined
+Alert: PASS: context = undefined
+Alert: PASS: context = undefined
+Alert: PASS: typeof xfa.form.clearErrorList = function
+Alert: PASS: typeof xfa.form.createNode = function
+Alert: PASS: typeof xfa.form.isCompatibleNS = function
+Alert: PASS: xfa.form.clearErrorList('args', 'ignored') = undefined
+Alert: PASS: xfa.form.createNode() threw XFAObject.createNode: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.form.createNode('bogus') = null
+Alert: PASS: xfa.form.createNode('template') = [object XFAObject]
+Alert: PASS: xfa.form.isCompatibleNS() threw XFAObject.isCompatibleNS: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.form.isCompatibleNS('bogus') = false
+Alert: PASS: xfa.form.isCompatibleNS('') = true
diff --git a/testing/resources/javascript/xfa_specific/xfa_node.in b/testing/resources/javascript/xfa_specific/xfa_node.in
new file mode 100644
index 0000000..7157993
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_node.in
@@ -0,0 +1,89 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        testROProperty(my_doc, "isContainer", true);
+        testROProperty(my_doc, "isNull", false);
+        testROProperty(my_doc, "model", "[object XFAObject]");
+        testROProperty(my_doc, "ns", "");
+        testROProperty(my_doc, "oneOfChild", false);
+        expect("typeof my_doc.applyXSL", "function");
+        expect("typeof my_doc.assignNode", "function");
+        expect("typeof my_doc.clone", "function");
+        expect("typeof my_doc.getAttribute", "function");
+        expect("typeof my_doc.getElement", "function");
+        expect("typeof my_doc.isPropertySpecified", "function");
+        expect("typeof my_doc.loadXML", "function");
+        expect("typeof my_doc.saveFilteredXML", "function");
+        expect("typeof my_doc.saveXML", "function");
+        expect("typeof my_doc.setAttribute", "function");
+        expect("typeof my_doc.setElement", "function");
+        expectError("my_doc.applyXSL()");
+        expect("my_doc.applyXSL(42)", undefined);
+        expectError("my_doc.assignNode()");
+        expect("my_doc.assignNode(42)", undefined);
+        expectError("my_doc.clone()");
+        expect("my_doc.clone(true)", "[object XFAObject]");
+        expect("my_doc.clone(false)", "[object XFAObject]");
+        expectError("my_doc.getAttribute()");
+        expect("my_doc.getAttribute('bogus')", "");
+        expect("my_doc.getAttribute('layout')", "tb");
+        expectError("my_doc.getElement()");
+        expect("my_doc.getElement('bogus')", undefined);
+        expect("my_doc.getElement('pageSet')", "[object XFAObject]");
+        expectError("my_doc.isPropertySpecified()");
+        expect("my_doc.isPropertySpecified('bogus')", false);
+        expect("my_doc.isPropertySpecified('pageSet')", true);
+        expectError("my_doc.loadXML()");
+        expect("my_doc.loadXML('bogus')", undefined);
+        expect("my_doc.loadXML('\u003cpageSet\u003e\u003d/pageSet\u003e')", undefined);
+        expect("my_doc.saveFilteredXML('undefined', 'regardless', 'of', 'args')", undefined);
+        expect("my_doc.saveXML().length > 9000", true);          // Really long string.
+        expect("my_doc.saveXML('pretty').length > 9000", true);  // Really long string.
+        expectError("my_doc.saveXML('bogus')");
+        expectError("my_doc.setAttribute()");
+        expectError("my_doc.setElement()");
+        expect("my_doc.setElement('ns', 'something')", undefined);
+
+        // Test setting attributes in the XFA schema.
+        expect("my_doc.setAttribute('something', 'ns')", undefined);
+        expect("my_doc.getAttribute('ns')", 'something');
+
+        // Test free-form attributes outside of the XFA schema.
+        expect("my_doc.setAttribute('fake_value', 'fake_attr')", undefined);
+        expect("my_doc.getAttribute('fake_attr')", 'fake_value');
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_node_expected.txt b/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
new file mode 100644
index 0000000..68b73a9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
@@ -0,0 +1,51 @@
+Alert: PASS: isContainer = true
+Alert: PASS: isContainer threw Error: Invalid property set operation.
+Alert: PASS: isNull = false
+Alert: PASS: isNull threw Error: Invalid property set operation.
+Alert: PASS: model = [object XFAObject]
+Alert: PASS: model threw Error: Invalid property set operation.
+Alert: PASS: ns = 
+Alert: PASS: ns threw Error: Invalid property set operation.
+Alert: FAIL: oneOfChild = undefined, expected = false
+Alert: PASS: oneOfChild threw Error: Invalid property set operation.
+Alert: PASS: typeof my_doc.applyXSL = function
+Alert: PASS: typeof my_doc.assignNode = function
+Alert: PASS: typeof my_doc.clone = function
+Alert: PASS: typeof my_doc.getAttribute = function
+Alert: PASS: typeof my_doc.getElement = function
+Alert: PASS: typeof my_doc.isPropertySpecified = function
+Alert: PASS: typeof my_doc.loadXML = function
+Alert: PASS: typeof my_doc.saveFilteredXML = function
+Alert: PASS: typeof my_doc.saveXML = function
+Alert: PASS: typeof my_doc.setAttribute = function
+Alert: PASS: typeof my_doc.setElement = function
+Alert: PASS: my_doc.applyXSL() threw XFAObject.applyXSL: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.applyXSL(42) = undefined
+Alert: PASS: my_doc.assignNode() threw XFAObject.assignNode: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.assignNode(42) = undefined
+Alert: PASS: my_doc.clone() threw XFAObject.clone: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.clone(true) = [object XFAObject]
+Alert: PASS: my_doc.clone(false) = [object XFAObject]
+Alert: PASS: my_doc.getAttribute() threw XFAObject.getAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.getAttribute('bogus') = 
+Alert: PASS: my_doc.getAttribute('layout') = tb
+Alert: PASS: my_doc.getElement() threw XFAObject.getElement: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.getElement('bogus') = null
+Alert: PASS: my_doc.getElement('pageSet') = [object XFAObject]
+Alert: PASS: my_doc.isPropertySpecified() threw XFAObject.isPropertySpecified: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.isPropertySpecified('bogus') = false
+Alert: PASS: my_doc.isPropertySpecified('pageSet') = true
+Alert: PASS: my_doc.loadXML() threw XFAObject.loadXML: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.loadXML('bogus') = undefined
+Alert: PASS: my_doc.loadXML('<pageSet>=/pageSet>') = undefined
+Alert: PASS: my_doc.saveFilteredXML('undefined', 'regardless', 'of', 'args') = undefined
+Alert: PASS: my_doc.saveXML().length > 9000 = true
+Alert: PASS: my_doc.saveXML('pretty').length > 9000 = true
+Alert: PASS: my_doc.saveXML('bogus') threw XFAObject.saveXML: Incorrect parameter value.
+Alert: PASS: my_doc.setAttribute() threw XFAObject.setAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.setElement() threw XFAObject.setElement: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.setElement('ns', 'something') = undefined
+Alert: PASS: my_doc.setAttribute('something', 'ns') = undefined
+Alert: PASS: my_doc.getAttribute('ns') = something
+Alert: PASS: my_doc.setAttribute('fake_value', 'fake_attr') = undefined
+Alert: PASS: my_doc.getAttribute('fake_attr') = fake_value
diff --git a/testing/resources/javascript/xfa_specific/xfa_signature_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_signature_pseudomodel.in
new file mode 100644
index 0000000..0a173f7
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_signature_pseudomodel.in
@@ -0,0 +1,46 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        try {
+          xfa.signature.clear("foo");
+          xfa.signature.enumerate();
+          xfa.signature.sign("foo", "bar", "bam");
+          xfa.signature.verify("foo");
+          app.alert("finished testing methods");
+        } catch (e) {
+          app.alert("error testing methods: " + e);
+        }
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_signature_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_signature_pseudomodel_expected.txt
new file mode 100644
index 0000000..3149bdb
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_signature_pseudomodel_expected.txt
@@ -0,0 +1 @@
+Alert: finished testing methods
diff --git a/testing/resources/javascript/xfa_specific/xfa_subform.in b/testing/resources/javascript/xfa_specific/xfa_subform.in
new file mode 100644
index 0000000..6924a38
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_subform.in
@@ -0,0 +1,72 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        testRWProperty(my_doc, "locale", "en_US", "FR");
+        testRIProperty(my_doc, "instanceIndex", 0);
+        testRWProperty(my_doc, "layout", "tb", "landscape");
+        testRWProperty(my_doc, "validationMessage", "", "totally valid");
+        expect("typeof my_doc.execEvent", "function");
+        expect("typeof my_doc.execInitialize", "function");
+        expect("typeof my_doc.execCalculate", "function");
+        expect("typeof my_doc.execValidate", "function");
+        expectError("my_doc.execEvent()");
+        expectError("my_doc.execEvent('foo', 'bar')");
+        expect("my_doc.execEvent(undefined)", undefined);
+        expect("my_doc.execEvent({value: 'bogus'})", undefined);
+        expect("my_doc.execEvent('bogus')", undefined);
+        expect("my_doc.execEvent('change')", undefined);
+        expect("my_doc.execEvent('click')", undefined);
+        expect("my_doc.execEvent('enter')", undefined);
+        expect("my_doc.execEvent('exit')", undefined);
+        expect("my_doc.execEvent('full')", undefined);
+        expect("my_doc.execEvent('indexChange')", undefined);
+        expect("my_doc.execEvent('initialize')", undefined);
+        expect("my_doc.execEvent('mouseDown')", undefined);
+        expect("my_doc.execEvent('mouseEnter')", undefined);
+        expect("my_doc.execEvent('mouseExit')", undefined);
+        expect("my_doc.execEvent('postOpen')", undefined);
+        expect("my_doc.execEvent('postSign')", undefined);
+        expect("my_doc.execEvent('preOpen')", undefined);
+        expect("my_doc.execEvent('preSign')", undefined);
+        expectError("my_doc.execInitialize('foo')");
+        expect("my_doc.execInitialize()", undefined);
+        expectError("my_doc.execCalculate('foo')");
+        expect("my_doc.execCalculate()", undefined);
+        expectError("my_doc.execValidate('foo')");
+        expect("my_doc.execValidate()", true);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_subform_expected.txt b/testing/resources/javascript/xfa_specific/xfa_subform_expected.txt
new file mode 100644
index 0000000..68b2b24
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_subform_expected.txt
@@ -0,0 +1,37 @@
+Alert: PASS: locale = en_US
+Alert: PASS: locale = FR
+Alert: PASS: instanceIndex = 0
+Alert: PASS: instanceIndex = 0
+Alert: PASS: layout = tb
+Alert: PASS: layout = landscape
+Alert: PASS: validationMessage = 
+Alert: PASS: validationMessage = totally valid
+Alert: PASS: typeof my_doc.execEvent = function
+Alert: PASS: typeof my_doc.execInitialize = function
+Alert: PASS: typeof my_doc.execCalculate = function
+Alert: PASS: typeof my_doc.execValidate = function
+Alert: PASS: my_doc.execEvent() threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.execEvent('foo', 'bar') threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.execEvent(undefined) = undefined
+Alert: PASS: my_doc.execEvent({value: 'bogus'}) = undefined
+Alert: PASS: my_doc.execEvent('bogus') = undefined
+Alert: PASS: my_doc.execEvent('change') = undefined
+Alert: PASS: my_doc.execEvent('click') = undefined
+Alert: PASS: my_doc.execEvent('enter') = undefined
+Alert: PASS: my_doc.execEvent('exit') = undefined
+Alert: PASS: my_doc.execEvent('full') = undefined
+Alert: PASS: my_doc.execEvent('indexChange') = undefined
+Alert: PASS: my_doc.execEvent('initialize') = undefined
+Alert: PASS: my_doc.execEvent('mouseDown') = undefined
+Alert: PASS: my_doc.execEvent('mouseEnter') = undefined
+Alert: PASS: my_doc.execEvent('mouseExit') = undefined
+Alert: PASS: my_doc.execEvent('postOpen') = undefined
+Alert: PASS: my_doc.execEvent('postSign') = undefined
+Alert: PASS: my_doc.execEvent('preOpen') = undefined
+Alert: PASS: my_doc.execEvent('preSign') = undefined
+Alert: PASS: my_doc.execInitialize('foo') threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.execInitialize() = undefined
+Alert: PASS: my_doc.execCalculate('foo') threw XFAObject.execCalculate: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.execCalculate() = undefined
+Alert: PASS: my_doc.execValidate('foo') threw XFAObject.execValidate: Incorrect number of parameters passed to function.
+Alert: PASS: my_doc.execValidate() = true
diff --git a/testing/resources/javascript/xfa_specific/xfa_tree.in b/testing/resources/javascript/xfa_specific/xfa_tree.in
new file mode 100644
index 0000000..8f447fa
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_tree.in
@@ -0,0 +1,53 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="my_doc">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+      <pageArea id="Page2" name="Page2">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+        {{include ../expect.js}}
+        {{include ../property_test_helpers.js}}
+        testROProperty(xfa.form, "all", "[object XFAObject]");
+        testROProperty(xfa.form, "classAll",  "[object XFAObject]");
+        testROProperty(xfa.form, "classIndex", 0);
+        testROProperty(xfa.form, "index", 0);
+        testROProperty(xfa.form, "nodes", "[object XFAObject]");
+        testROProperty(xfa.form, "parent", "[object XFAObject]");
+        testROProperty(xfa.form, "somExpression", "xfa[0].form[0]");
+        expect("typeof xfa.form.resolveNode", "function");
+        expect("typeof xfa.form.resolveNodes", "function");
+        expectError("xfa.form.resolveNode()");
+        expect("xfa.form.resolveNode('bogus')", null);
+        expectError("xfa.form.resolveNodes()");
+        expect("xfa.form.resolveNodes('bogus')", "[object XFAObject]");
+        expect("xfa.form.resolveNodes('bogus').length", 0);
+      </script>
+    </event>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_tree_expected.txt b/testing/resources/javascript/xfa_specific/xfa_tree_expected.txt
new file mode 100644
index 0000000..dd1ef25
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_tree_expected.txt
@@ -0,0 +1,21 @@
+Alert: PASS: all = [object XFAObject]
+Alert: PASS: all threw Error: Invalid property set operation.
+Alert: PASS: classAll = [object XFAObject]
+Alert: PASS: classAll threw Error: Invalid property set operation.
+Alert: PASS: classIndex = 0
+Alert: PASS: classIndex threw Error: Invalid property set operation.
+Alert: PASS: index = 0
+Alert: PASS: index threw Error: Invalid property set operation.
+Alert: PASS: nodes = [object XFAObject]
+Alert: PASS: nodes threw Error: Unable to set 
+Alert: PASS: parent = [object XFAObject]
+Alert: PASS: parent threw Error: Invalid property set operation.
+Alert: PASS: somExpression = xfa[0].form[0]
+Alert: PASS: somExpression threw Error: Invalid property set operation.
+Alert: PASS: typeof xfa.form.resolveNode = function
+Alert: PASS: typeof xfa.form.resolveNodes = function
+Alert: PASS: xfa.form.resolveNode() threw XFAObject.resolveNode: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.form.resolveNode('bogus') = null
+Alert: PASS: xfa.form.resolveNodes() threw XFAObject.resolveNodes: Incorrect number of parameters passed to function.
+Alert: PASS: xfa.form.resolveNodes('bogus') = [object XFAObject]
+Alert: PASS: xfa.form.resolveNodes('bogus').length = 0
diff --git a/testing/resources/js.in b/testing/resources/js.in
new file mode 100644
index 0000000..7d35139
--- /dev/null
+++ b/testing/resources/js.in
@@ -0,0 +1,83 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names <</JavaScript 4 0 R>>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /CropBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+  /Names [
+    (normal) 5 0 R
+    (encoded_subtype) 6 0 R
+    (no_type) 7 0 R
+    (wrongtype) 8 0 R
+    (wrongsubtype) 9 0 R
+    (nojs) 10 0 R
+  ]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Action
+  /S /J#61v#61Script
+  /JS 11 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /S /JavaScript
+  /JS 12 0 R
+>>
+endobj
+{{object 8 0}} <<
+  /Type /action
+  /S /JavaScript
+  /JS 12 0 R
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Action
+  /S /Javascript
+  /JS 12 0 R
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Action
+  /S /JavaScript
+>>
+endobj
+{{object 11 0}} <<
+>>
+stream
+app.alert("ping");
+endstream
+endobj
+{{object 12 0}} <<
+>>
+stream
+app.alert("pong");
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Root 1 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/js.pdf b/testing/resources/js.pdf
new file mode 100644
index 0000000..07e9b75
--- /dev/null
+++ b/testing/resources/js.pdf
@@ -0,0 +1,99 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Names <</JavaScript 4 0 R>>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /CropBox [0 0 612 792]
+>>
+endobj
+4 0 obj <<
+  /Names [
+    (normal) 5 0 R
+    (encoded_subtype) 6 0 R
+    (no_type) 7 0 R
+    (wrongtype) 8 0 R
+    (wrongsubtype) 9 0 R
+    (nojs) 10 0 R
+  ]
+>>
+endobj
+5 0 obj <<
+  /Type /Action
+  /S /JavaScript
+  /JS 11 0 R
+>>
+endobj
+6 0 obj <<
+  /Type /Action
+  /S /J#61v#61Script
+  /JS 11 0 R
+>>
+endobj
+7 0 obj <<
+  /S /JavaScript
+  /JS 12 0 R
+>>
+endobj
+8 0 obj <<
+  /Type /action
+  /S /JavaScript
+  /JS 12 0 R
+>>
+endobj
+9 0 obj <<
+  /Type /Action
+  /S /Javascript
+  /JS 12 0 R
+>>
+endobj
+10 0 obj <<
+  /Type /Action
+  /S /JavaScript
+>>
+endobj
+11 0 obj <<
+>>
+stream
+app.alert("ping");
+endstream
+endobj
+12 0 obj <<
+>>
+stream
+app.alert("pong");
+endstream
+endobj
+xref
+0 13
+0000000000 65535 f 
+0000000015 00000 n 
+0000000099 00000 n 
+0000000162 00000 n 
+0000000264 00000 n 
+0000000432 00000 n 
+0000000499 00000 n 
+0000000570 00000 n 
+0000000621 00000 n 
+0000000688 00000 n 
+0000000755 00000 n 
+0000000810 00000 n 
+0000000868 00000 n 
+trailer <<
+  /Root 1 0 R
+>>
+startxref
+926
+%%EOF
diff --git a/testing/resources/latin_extended.pdf b/testing/resources/latin_extended.pdf
new file mode 100644
index 0000000..6c34ab0
--- /dev/null
+++ b/testing/resources/latin_extended.pdf
Binary files differ
diff --git a/testing/resources/linearized_bug_1055.pdf b/testing/resources/linearized_bug_1055.pdf
new file mode 100644
index 0000000..2f0f493
--- /dev/null
+++ b/testing/resources/linearized_bug_1055.pdf
Binary files differ
diff --git a/testing/resources/link_annots.in b/testing/resources/link_annots.in
new file mode 100644
index 0000000..076069c
--- /dev/null
+++ b/testing/resources/link_annots.in
@@ -0,0 +1,333 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 612 792]
+  /CropBox [0 0 612 792]
+  /Resources <<
+    /Font <<
+      /F1 7 0 R
+      /F2 8 0 R
+    >>
+    /ProcSet [/PDF /Text /ImageC]
+    /ExtGState <<
+      /GS0 23 0 R
+    >>
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /Annots [15 0 R 16 0 R]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 1) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+-12 -84 Td
+/F2 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+2 -53 Td
+(3.  An example of Highlight with text notes) Tj
+0 -18 Td
+(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
+0 -17 Td
+(as WebLinks in PDFium.)Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 2) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  {{streamlen}}
+  /BBox [293 530 349 542]
+  /Resources <<
+    /XObject <<
+      /Form0 10 0 R
+    >>
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  {{streamlen}}
+  /BBox [293 530 349 542]
+>>
+stream
+1.0 1.0 0.0 rg
+293 530 m
+349 530 l
+349 542 l
+293 542 l
+h f
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  {{streamlen}}
+  /BBox [83 440 178 453]
+  /Resources <<
+    /XObject <<
+      /Form0 12 0 R
+    >>
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 12 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  {{streamlen}}
+  /BBox [83 440 178 453]
+>>
+stream
+0.0 1.0 1.0 rg
+83 440 m
+178 440 l
+178 453 l
+83 453 l
+h f
+endstream
+endobj
+{{object 13 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  {{streamlen}}
+  /BBox [149 476 191 487]
+  /Resources <<
+    /XObject <<
+      /Form0 14 0 R
+    >>
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 14 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  {{streamlen}}
+  /BBox [149 476 191 487]
+>>
+stream
+0.0 1.0 0.0 rg
+149 476 m
+191 476 l
+191 487 l
+149 487 l
+h f
+endstream
+endobj
+{{object 15 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [69 633 542 653]
+  /Dest [3 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+{{object 16 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [80 613 542 633]
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+{{object 17 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [66 529 196 544]
+  /A <<
+    /Type /Action
+    /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+{{object 18 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [83 440 178 453]
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /A <<
+    /Type /Action
+    /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+{{object 19 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 9 0 R
+  >>
+  /NM (Highlight-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{object 20 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 11 0 R
+  >>
+  /NM (Highlight-2)
+  /F 4
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /P 3 0 R
+  /C [0.26667 0.78431 0.96078]
+  /Rect [83 440 178 453]
+>>
+endobj
+{{object 21 0}} <<
+  /Type /Annot
+  /Subtype /Popup
+  /Parent 22 0 R
+  /Rect [191 377 443 488]
+>>
+endobj
+{{object 22 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /Popup 21 0 R
+  /AP <<
+    /N 13 0 R
+  >>
+  /NM (Highlight-With-Popup-1)
+  /Contents (Text Note)
+  /QuadPoints [149 487 191 487 149 476 191 476]
+  /P 3 0 R
+  /C [0.14902 0.90196 0]
+  /Rect [149 476 191 487]
+  /F 4
+>>
+endobj
+{{object 23 0}} <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /BM /Normal
+>>
+endobj
+{{object 24 0}} <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /AIS false
+  /BM /Multiply
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/link_annots.pdf b/testing/resources/link_annots.pdf
new file mode 100644
index 0000000..b964bf5
--- /dev/null
+++ b/testing/resources/link_annots.pdf
@@ -0,0 +1,364 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+  /MediaBox [0 0 612 792]
+  /CropBox [0 0 612 792]
+  /Resources <<
+    /Font <<
+      /F1 7 0 R
+      /F2 8 0 R
+    >>
+    /ProcSet [/PDF /Text /ImageC]
+    /ExtGState <<
+      /GS0 23 0 R
+    >>
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 6 0 R
+  /Annots [15 0 R 16 0 R]
+>>
+endobj
+5 0 obj <<
+  /Length 486
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 1) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+-12 -84 Td
+/F2 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+2 -53 Td
+(3.  An example of Highlight with text notes) Tj
+0 -18 Td
+(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
+0 -17 Td
+(as WebLinks in PDFium.)Tj
+ET
+endstream
+endobj
+6 0 obj <<
+  /Length 185
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 2) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+ET
+endstream
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+8 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+9 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Length 18
+  /BBox [293 530 349 542]
+  /Resources <<
+    /XObject <<
+      /Form0 10 0 R
+    >>
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+10 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  /Length 59
+  /BBox [293 530 349 542]
+>>
+stream
+1.0 1.0 0.0 rg
+293 530 m
+349 530 l
+349 542 l
+293 542 l
+h f
+endstream
+endobj
+11 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Length 18
+  /BBox [83 440 178 453]
+  /Resources <<
+    /XObject <<
+      /Form0 12 0 R
+    >>
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+12 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  /Length 57
+  /BBox [83 440 178 453]
+>>
+stream
+0.0 1.0 1.0 rg
+83 440 m
+178 440 l
+178 453 l
+83 453 l
+h f
+endstream
+endobj
+13 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Length 18
+  /BBox [149 476 191 487]
+  /Resources <<
+    /XObject <<
+      /Form0 14 0 R
+    >>
+    /ExtGState <<
+      /GS0 24 0 R
+    >>
+  >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+14 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /Group <<
+    /S /Transparency
+  >>
+  /Length 59
+  /BBox [149 476 191 487]
+>>
+stream
+0.0 1.0 0.0 rg
+149 476 m
+191 476 l
+191 487 l
+149 487 l
+h f
+endstream
+endobj
+15 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [69 633 542 653]
+  /Dest [3 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+16 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [80 613 542 633]
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+endobj
+17 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [66 529 196 544]
+  /A <<
+    /Type /Action
+    /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+18 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [83 440 178 453]
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /A <<
+    /Type /Action
+    /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
+    /S /URI
+  >>
+  /F 4
+>>
+endobj
+19 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 9 0 R
+  >>
+  /NM (Highlight-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+20 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 11 0 R
+  >>
+  /NM (Highlight-2)
+  /F 4
+  /QuadPoints [83 453 178 453 83 440 178 440]
+  /P 3 0 R
+  /C [0.26667 0.78431 0.96078]
+  /Rect [83 440 178 453]
+>>
+endobj
+21 0 obj <<
+  /Type /Annot
+  /Subtype /Popup
+  /Parent 22 0 R
+  /Rect [191 377 443 488]
+>>
+endobj
+22 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /Popup 21 0 R
+  /AP <<
+    /N 13 0 R
+  >>
+  /NM (Highlight-With-Popup-1)
+  /Contents (Text Note)
+  /QuadPoints [149 487 191 487 149 476 191 476]
+  /P 3 0 R
+  /C [0.14902 0.90196 0]
+  /Rect [149 476 191 487]
+  /F 4
+>>
+endobj
+23 0 obj <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /BM /Normal
+>>
+endobj
+24 0 obj <<
+  /ca 1
+  /Type /ExtGState
+  /CA 1
+  /AIS false
+  /BM /Multiply
+>>
+endobj
+xref
+0 25
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000338 00000 n 
+0000000475 00000 n 
+0000000570 00000 n 
+0000001108 00000 n 
+0000001345 00000 n 
+0000001423 00000 n 
+0000001499 00000 n 
+0000001749 00000 n 
+0000001972 00000 n 
+0000002222 00000 n 
+0000002442 00000 n 
+0000002693 00000 n 
+0000002916 00000 n 
+0000003056 00000 n 
+0000003196 00000 n 
+0000003443 00000 n 
+0000003727 00000 n 
+0000003944 00000 n 
+0000004171 00000 n 
+0000004269 00000 n 
+0000004544 00000 n 
+0000004615 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 25
+>>
+startxref
+4701
+%%EOF
diff --git a/testing/resources/links_highlights_annots.in b/testing/resources/links_highlights_annots.in
new file mode 100644
index 0000000..9a5e2ec
--- /dev/null
+++ b/testing/resources/links_highlights_annots.in
@@ -0,0 +1,139 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 6 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 612 792]
+  /Annots [
+    8 0 R
+    9 0 R
+    10 0 R
+    11 0 R
+    12 0 R
+    13 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations) Tj
+0 -65 Td
+/F1 14 Tf
+(External Link ) Tj
+0 -104 Td
+/F1 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+% Forms
+{{object 6 0}} <<
+  /Fields [7 0 R]
+>>
+endobj
+% Fields
+{{object 7 0}} <<
+  /Kids [
+    8 0 R
+    9 0 R
+    10 0 R
+    11 0 R
+  ]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_LeftBottom)
+  /Rect [69 670 220 690]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_RightTop)
+  /Rect [69 440 220 460]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_LeftTop)
+  /Rect [69 600 221 620]
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_RightBottom)
+  /Rect [69 400 220 420]
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [69 633 542 653]
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (https://www.google.com/)
+  >>
+  /F 4
+>>
+endobj
+{{object 13 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /NM (Highlight-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/links_highlights_annots.pdf b/testing/resources/links_highlights_annots.pdf
new file mode 100644
index 0000000..7f392b4
--- /dev/null
+++ b/testing/resources/links_highlights_annots.pdf
@@ -0,0 +1,159 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 6 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+% Page number 0.
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 612 792]
+  /Annots [
+    8 0 R
+    9 0 R
+    10 0 R
+    11 0 R
+    12 0 R
+    13 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+4 0 obj <<
+  /Length 174
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations) Tj
+0 -65 Td
+/F1 14 Tf
+(External Link ) Tj
+0 -104 Td
+/F1 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+ET
+endstream
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+% Forms
+6 0 obj <<
+  /Fields [7 0 R]
+>>
+endobj
+% Fields
+7 0 obj <<
+  /Kids [
+    8 0 R
+    9 0 R
+    10 0 R
+    11 0 R
+  ]
+>>
+endobj
+8 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_LeftBottom)
+  /Rect [69 670 220 690]
+>>
+endobj
+9 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_RightTop)
+  /Rect [69 440 220 460]
+>>
+endobj
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_LeftTop)
+  /Rect [69 600 221 620]
+>>
+endobj
+11 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /P 3 0 R
+  /T (Sub_RightBottom)
+  /Rect [69 400 220 420]
+>>
+endobj
+12 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /BS <<
+    /W 0
+  >>
+  /Rect [69 633 542 653]
+  /A <<
+    /Type /Action
+    /S /URI
+    /URI (https://www.google.com/)
+  >>
+  /F 4
+>>
+endobj
+13 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /NM (Highlight-1)
+  /F 4
+  /QuadPoints [293 542 349 542 293 530 349 530]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 14
+0000000000 65535 f 
+0000000015 00000 n 
+0000000086 00000 n 
+0000000166 00000 n 
+0000000409 00000 n 
+0000000635 00000 n 
+0000000719 00000 n 
+0000000767 00000 n 
+0000000844 00000 n 
+0000000967 00000 n 
+0000001088 00000 n 
+0000001209 00000 n 
+0000001334 00000 n 
+0000001521 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 14
+>>
+startxref
+1711
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/listbox_form.in b/testing/resources/listbox_form.in
new file mode 100644
index 0000000..354d841
--- /dev/null
+++ b/testing/resources/listbox_form.in
@@ -0,0 +1,119 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+    /DR 4 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 4 0 R
+  /MediaBox [ 0 0 300 600 ]
+  /Contents 7 0 R
+  /Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+>>
+endobj
+{{object 4 0}} <<
+  /Font 5 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /F1 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 450 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 0
+  /T (Listbox_SingleSelect)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 350 200 380 ]
+  /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelect)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 400 200 430 ]
+  /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+        (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+        (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+        (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
+  /V (Banana)
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 1
+  /T (Listbox_ReadOnly)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 500 200 530 ]
+  /Opt [(Dog) (Elephant) (Frog)]
+>>
+endobj
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleSelected)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 200 200 230 ]
+  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+  /V [(Epsilon) (Gamma)]
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 0
+  /T (Listbox_SingleSelectLastSelected)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 100 200 130 ]
+  /Opt [(Alberta) (British Columbia) (Manitoba) (New Brunswick)
+        (Newfoundland and Labrador) (Nova Scotia) (Ontario)
+        (Prince Edward Island) (Quebec) (Saskatchewan) ]
+  /V (Saskatchewan)
+  /TI 9
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/listbox_form.pdf b/testing/resources/listbox_form.pdf
new file mode 100644
index 0000000..9e1393e
--- /dev/null
+++ b/testing/resources/listbox_form.pdf
@@ -0,0 +1,138 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+    /DR 4 0 R
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 4 0 R
+  /MediaBox [ 0 0 300 600 ]
+  /Contents 7 0 R
+  /Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+>>
+endobj
+4 0 obj <<
+  /Font 5 0 R
+>>
+endobj
+5 0 obj <<
+  /F1 6 0 R
+>>
+endobj
+6 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+7 0 obj <<
+  /Length 51
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 450 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+8 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 0
+  /T (Listbox_SingleSelect)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 350 200 380 ]
+  /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
+>>
+endobj
+9 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelect)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 400 200 430 ]
+  /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+        (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+        (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+        (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
+  /V (Banana)
+>>
+endobj
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 1
+  /T (Listbox_ReadOnly)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 500 200 530 ]
+  /Opt [(Dog) (Elephant) (Frog)]
+>>
+endobj
+11 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 2097152
+  /T (Listbox_MultiSelectMultipleSelected)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 200 200 230 ]
+  /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+  /V [(Epsilon) (Gamma)]
+>>
+endobj
+12 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 0
+  /T (Listbox_SingleSelectLastSelected)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 100 200 130 ]
+  /Opt [(Alberta) (British Columbia) (Manitoba) (New Brunswick)
+        (Newfoundland and Labrador) (Nova Scotia) (Ontario)
+        (Prince Edward Island) (Quebec) (Saskatchewan) ]
+  /V (Saskatchewan)
+  /TI 9
+>>
+endobj
+xref
+0 13
+0000000000 65535 f 
+0000000015 00000 n 
+0000000151 00000 n 
+0000000216 00000 n 
+0000000379 00000 n 
+0000000414 00000 n 
+0000000447 00000 n 
+0000000523 00000 n 
+0000000625 00000 n 
+0000000832 00000 n 
+0000001302 00000 n 
+0000001488 00000 n 
+0000001741 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 13
+>>
+startxref
+2119
+%%EOF
diff --git a/testing/resources/many_rectangles.in b/testing/resources/many_rectangles.in
new file mode 100644
index 0000000..110a7a8
--- /dev/null
+++ b/testing/resources/many_rectangles.in
@@ -0,0 +1,1232 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 300 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+>>
+stream
+q
+
+0.050000 0.033333 0.275000 rg
+1.000000 1.000000 8.000000 8.000000 re B*
+0.100000 0.033333 0.250000 rg
+11.000000 1.000000 8.000000 8.000000 re B*
+0.150000 0.033333 0.225000 rg
+21.000000 1.000000 8.000000 8.000000 re B*
+0.200000 0.033333 0.200000 rg
+31.000000 1.000000 8.000000 8.000000 re B*
+0.250000 0.033333 0.175000 rg
+41.000000 1.000000 8.000000 8.000000 re B*
+0.300000 0.033333 0.150000 rg
+51.000000 1.000000 8.000000 8.000000 re B*
+0.350000 0.033333 0.125000 rg
+61.000000 1.000000 8.000000 8.000000 re B*
+0.400000 0.033333 0.100000 rg
+71.000000 1.000000 8.000000 8.000000 re B*
+0.450000 0.033333 0.075000 rg
+81.000000 1.000000 8.000000 8.000000 re B*
+0.500000 0.033333 0.050000 rg
+91.000000 1.000000 8.000000 8.000000 re B*
+0.550000 0.033333 0.025000 rg
+101.000000 1.000000 8.000000 8.000000 re B*
+0.600000 0.033333 0.000000 rg
+111.000000 1.000000 8.000000 8.000000 re B*
+0.650000 0.033333 -0.025000 rg
+121.000000 1.000000 8.000000 8.000000 re B*
+0.700000 0.033333 -0.050000 rg
+131.000000 1.000000 8.000000 8.000000 re B*
+0.750000 0.033333 -0.075000 rg
+141.000000 1.000000 8.000000 8.000000 re B*
+0.800000 0.033333 -0.100000 rg
+151.000000 1.000000 8.000000 8.000000 re B*
+0.850000 0.033333 -0.125000 rg
+161.000000 1.000000 8.000000 8.000000 re B*
+0.900000 0.033333 -0.150000 rg
+171.000000 1.000000 8.000000 8.000000 re B*
+0.950000 0.033333 -0.175000 rg
+181.000000 1.000000 8.000000 8.000000 re B*
+1.000000 0.033333 -0.200000 rg
+191.000000 1.000000 8.000000 8.000000 re B*
+0.050000 0.066667 0.300000 rg
+1.000000 11.000000 8.000000 8.000000 re B*
+0.100000 0.066667 0.275000 rg
+11.000000 11.000000 8.000000 8.000000 re B*
+0.150000 0.066667 0.250000 rg
+21.000000 11.000000 8.000000 8.000000 re B*
+0.200000 0.066667 0.225000 rg
+31.000000 11.000000 8.000000 8.000000 re B*
+0.250000 0.066667 0.200000 rg
+41.000000 11.000000 8.000000 8.000000 re B*
+0.300000 0.066667 0.175000 rg
+51.000000 11.000000 8.000000 8.000000 re B*
+0.350000 0.066667 0.150000 rg
+61.000000 11.000000 8.000000 8.000000 re B*
+0.400000 0.066667 0.125000 rg
+71.000000 11.000000 8.000000 8.000000 re B*
+0.450000 0.066667 0.100000 rg
+81.000000 11.000000 8.000000 8.000000 re B*
+0.500000 0.066667 0.075000 rg
+91.000000 11.000000 8.000000 8.000000 re B*
+0.550000 0.066667 0.050000 rg
+101.000000 11.000000 8.000000 8.000000 re B*
+0.600000 0.066667 0.025000 rg
+111.000000 11.000000 8.000000 8.000000 re B*
+0.650000 0.066667 0.000000 rg
+121.000000 11.000000 8.000000 8.000000 re B*
+0.700000 0.066667 -0.025000 rg
+131.000000 11.000000 8.000000 8.000000 re B*
+0.750000 0.066667 -0.050000 rg
+141.000000 11.000000 8.000000 8.000000 re B*
+0.800000 0.066667 -0.075000 rg
+151.000000 11.000000 8.000000 8.000000 re B*
+0.850000 0.066667 -0.100000 rg
+161.000000 11.000000 8.000000 8.000000 re B*
+0.900000 0.066667 -0.125000 rg
+171.000000 11.000000 8.000000 8.000000 re B*
+0.950000 0.066667 -0.150000 rg
+181.000000 11.000000 8.000000 8.000000 re B*
+1.000000 0.066667 -0.175000 rg
+191.000000 11.000000 8.000000 8.000000 re B*
+0.050000 0.100000 0.325000 rg
+1.000000 21.000000 8.000000 8.000000 re B*
+0.100000 0.100000 0.300000 rg
+11.000000 21.000000 8.000000 8.000000 re B*
+0.150000 0.100000 0.275000 rg
+21.000000 21.000000 8.000000 8.000000 re B*
+0.200000 0.100000 0.250000 rg
+31.000000 21.000000 8.000000 8.000000 re B*
+0.250000 0.100000 0.225000 rg
+41.000000 21.000000 8.000000 8.000000 re B*
+0.300000 0.100000 0.200000 rg
+51.000000 21.000000 8.000000 8.000000 re B*
+0.350000 0.100000 0.175000 rg
+61.000000 21.000000 8.000000 8.000000 re B*
+0.400000 0.100000 0.150000 rg
+71.000000 21.000000 8.000000 8.000000 re B*
+0.450000 0.100000 0.125000 rg
+81.000000 21.000000 8.000000 8.000000 re B*
+0.500000 0.100000 0.100000 rg
+91.000000 21.000000 8.000000 8.000000 re B*
+0.550000 0.100000 0.075000 rg
+101.000000 21.000000 8.000000 8.000000 re B*
+0.600000 0.100000 0.050000 rg
+111.000000 21.000000 8.000000 8.000000 re B*
+0.650000 0.100000 0.025000 rg
+121.000000 21.000000 8.000000 8.000000 re B*
+0.700000 0.100000 0.000000 rg
+131.000000 21.000000 8.000000 8.000000 re B*
+0.750000 0.100000 -0.025000 rg
+141.000000 21.000000 8.000000 8.000000 re B*
+0.800000 0.100000 -0.050000 rg
+151.000000 21.000000 8.000000 8.000000 re B*
+0.850000 0.100000 -0.075000 rg
+161.000000 21.000000 8.000000 8.000000 re B*
+0.900000 0.100000 -0.100000 rg
+171.000000 21.000000 8.000000 8.000000 re B*
+0.950000 0.100000 -0.125000 rg
+181.000000 21.000000 8.000000 8.000000 re B*
+1.000000 0.100000 -0.150000 rg
+191.000000 21.000000 8.000000 8.000000 re B*
+0.050000 0.133333 0.350000 rg
+1.000000 31.000000 8.000000 8.000000 re B*
+0.100000 0.133333 0.325000 rg
+11.000000 31.000000 8.000000 8.000000 re B*
+0.150000 0.133333 0.300000 rg
+21.000000 31.000000 8.000000 8.000000 re B*
+0.200000 0.133333 0.275000 rg
+31.000000 31.000000 8.000000 8.000000 re B*
+0.250000 0.133333 0.250000 rg
+41.000000 31.000000 8.000000 8.000000 re B*
+0.300000 0.133333 0.225000 rg
+51.000000 31.000000 8.000000 8.000000 re B*
+0.350000 0.133333 0.200000 rg
+61.000000 31.000000 8.000000 8.000000 re B*
+0.400000 0.133333 0.175000 rg
+71.000000 31.000000 8.000000 8.000000 re B*
+0.450000 0.133333 0.150000 rg
+81.000000 31.000000 8.000000 8.000000 re B*
+0.500000 0.133333 0.125000 rg
+91.000000 31.000000 8.000000 8.000000 re B*
+0.550000 0.133333 0.100000 rg
+101.000000 31.000000 8.000000 8.000000 re B*
+0.600000 0.133333 0.075000 rg
+111.000000 31.000000 8.000000 8.000000 re B*
+0.650000 0.133333 0.050000 rg
+121.000000 31.000000 8.000000 8.000000 re B*
+0.700000 0.133333 0.025000 rg
+131.000000 31.000000 8.000000 8.000000 re B*
+0.750000 0.133333 0.000000 rg
+141.000000 31.000000 8.000000 8.000000 re B*
+0.800000 0.133333 -0.025000 rg
+151.000000 31.000000 8.000000 8.000000 re B*
+0.850000 0.133333 -0.050000 rg
+161.000000 31.000000 8.000000 8.000000 re B*
+0.900000 0.133333 -0.075000 rg
+171.000000 31.000000 8.000000 8.000000 re B*
+0.950000 0.133333 -0.100000 rg
+181.000000 31.000000 8.000000 8.000000 re B*
+1.000000 0.133333 -0.125000 rg
+191.000000 31.000000 8.000000 8.000000 re B*
+0.050000 0.166667 0.375000 rg
+1.000000 41.000000 8.000000 8.000000 re B*
+0.100000 0.166667 0.350000 rg
+11.000000 41.000000 8.000000 8.000000 re B*
+0.150000 0.166667 0.325000 rg
+21.000000 41.000000 8.000000 8.000000 re B*
+0.200000 0.166667 0.300000 rg
+31.000000 41.000000 8.000000 8.000000 re B*
+0.250000 0.166667 0.275000 rg
+41.000000 41.000000 8.000000 8.000000 re B*
+0.300000 0.166667 0.250000 rg
+51.000000 41.000000 8.000000 8.000000 re B*
+0.350000 0.166667 0.225000 rg
+61.000000 41.000000 8.000000 8.000000 re B*
+0.400000 0.166667 0.200000 rg
+71.000000 41.000000 8.000000 8.000000 re B*
+0.450000 0.166667 0.175000 rg
+81.000000 41.000000 8.000000 8.000000 re B*
+0.500000 0.166667 0.150000 rg
+91.000000 41.000000 8.000000 8.000000 re B*
+0.550000 0.166667 0.125000 rg
+101.000000 41.000000 8.000000 8.000000 re B*
+0.600000 0.166667 0.100000 rg
+111.000000 41.000000 8.000000 8.000000 re B*
+0.650000 0.166667 0.075000 rg
+121.000000 41.000000 8.000000 8.000000 re B*
+0.700000 0.166667 0.050000 rg
+131.000000 41.000000 8.000000 8.000000 re B*
+0.750000 0.166667 0.025000 rg
+141.000000 41.000000 8.000000 8.000000 re B*
+0.800000 0.166667 0.000000 rg
+151.000000 41.000000 8.000000 8.000000 re B*
+0.850000 0.166667 -0.025000 rg
+161.000000 41.000000 8.000000 8.000000 re B*
+0.900000 0.166667 -0.050000 rg
+171.000000 41.000000 8.000000 8.000000 re B*
+0.950000 0.166667 -0.075000 rg
+181.000000 41.000000 8.000000 8.000000 re B*
+1.000000 0.166667 -0.100000 rg
+191.000000 41.000000 8.000000 8.000000 re B*
+0.050000 0.200000 0.400000 rg
+1.000000 51.000000 8.000000 8.000000 re B*
+0.100000 0.200000 0.375000 rg
+11.000000 51.000000 8.000000 8.000000 re B*
+0.150000 0.200000 0.350000 rg
+21.000000 51.000000 8.000000 8.000000 re B*
+0.200000 0.200000 0.325000 rg
+31.000000 51.000000 8.000000 8.000000 re B*
+0.250000 0.200000 0.300000 rg
+41.000000 51.000000 8.000000 8.000000 re B*
+0.300000 0.200000 0.275000 rg
+51.000000 51.000000 8.000000 8.000000 re B*
+0.350000 0.200000 0.250000 rg
+61.000000 51.000000 8.000000 8.000000 re B*
+0.400000 0.200000 0.225000 rg
+71.000000 51.000000 8.000000 8.000000 re B*
+0.450000 0.200000 0.200000 rg
+81.000000 51.000000 8.000000 8.000000 re B*
+0.500000 0.200000 0.175000 rg
+91.000000 51.000000 8.000000 8.000000 re B*
+0.550000 0.200000 0.150000 rg
+101.000000 51.000000 8.000000 8.000000 re B*
+0.600000 0.200000 0.125000 rg
+111.000000 51.000000 8.000000 8.000000 re B*
+0.650000 0.200000 0.100000 rg
+121.000000 51.000000 8.000000 8.000000 re B*
+0.700000 0.200000 0.075000 rg
+131.000000 51.000000 8.000000 8.000000 re B*
+0.750000 0.200000 0.050000 rg
+141.000000 51.000000 8.000000 8.000000 re B*
+0.800000 0.200000 0.025000 rg
+151.000000 51.000000 8.000000 8.000000 re B*
+0.850000 0.200000 0.000000 rg
+161.000000 51.000000 8.000000 8.000000 re B*
+0.900000 0.200000 -0.025000 rg
+171.000000 51.000000 8.000000 8.000000 re B*
+0.950000 0.200000 -0.050000 rg
+181.000000 51.000000 8.000000 8.000000 re B*
+1.000000 0.200000 -0.075000 rg
+191.000000 51.000000 8.000000 8.000000 re B*
+0.050000 0.233333 0.425000 rg
+1.000000 61.000000 8.000000 8.000000 re B*
+0.100000 0.233333 0.400000 rg
+11.000000 61.000000 8.000000 8.000000 re B*
+0.150000 0.233333 0.375000 rg
+21.000000 61.000000 8.000000 8.000000 re B*
+0.200000 0.233333 0.350000 rg
+31.000000 61.000000 8.000000 8.000000 re B*
+0.250000 0.233333 0.325000 rg
+41.000000 61.000000 8.000000 8.000000 re B*
+0.300000 0.233333 0.300000 rg
+51.000000 61.000000 8.000000 8.000000 re B*
+0.350000 0.233333 0.275000 rg
+61.000000 61.000000 8.000000 8.000000 re B*
+0.400000 0.233333 0.250000 rg
+71.000000 61.000000 8.000000 8.000000 re B*
+0.450000 0.233333 0.225000 rg
+81.000000 61.000000 8.000000 8.000000 re B*
+0.500000 0.233333 0.200000 rg
+91.000000 61.000000 8.000000 8.000000 re B*
+0.550000 0.233333 0.175000 rg
+101.000000 61.000000 8.000000 8.000000 re B*
+0.600000 0.233333 0.150000 rg
+111.000000 61.000000 8.000000 8.000000 re B*
+0.650000 0.233333 0.125000 rg
+121.000000 61.000000 8.000000 8.000000 re B*
+0.700000 0.233333 0.100000 rg
+131.000000 61.000000 8.000000 8.000000 re B*
+0.750000 0.233333 0.075000 rg
+141.000000 61.000000 8.000000 8.000000 re B*
+0.800000 0.233333 0.050000 rg
+151.000000 61.000000 8.000000 8.000000 re B*
+0.850000 0.233333 0.025000 rg
+161.000000 61.000000 8.000000 8.000000 re B*
+0.900000 0.233333 0.000000 rg
+171.000000 61.000000 8.000000 8.000000 re B*
+0.950000 0.233333 -0.025000 rg
+181.000000 61.000000 8.000000 8.000000 re B*
+1.000000 0.233333 -0.050000 rg
+191.000000 61.000000 8.000000 8.000000 re B*
+0.050000 0.266667 0.450000 rg
+1.000000 71.000000 8.000000 8.000000 re B*
+0.100000 0.266667 0.425000 rg
+11.000000 71.000000 8.000000 8.000000 re B*
+0.150000 0.266667 0.400000 rg
+21.000000 71.000000 8.000000 8.000000 re B*
+0.200000 0.266667 0.375000 rg
+31.000000 71.000000 8.000000 8.000000 re B*
+0.250000 0.266667 0.350000 rg
+41.000000 71.000000 8.000000 8.000000 re B*
+0.300000 0.266667 0.325000 rg
+51.000000 71.000000 8.000000 8.000000 re B*
+0.350000 0.266667 0.300000 rg
+61.000000 71.000000 8.000000 8.000000 re B*
+0.400000 0.266667 0.275000 rg
+71.000000 71.000000 8.000000 8.000000 re B*
+0.450000 0.266667 0.250000 rg
+81.000000 71.000000 8.000000 8.000000 re B*
+0.500000 0.266667 0.225000 rg
+91.000000 71.000000 8.000000 8.000000 re B*
+0.550000 0.266667 0.200000 rg
+101.000000 71.000000 8.000000 8.000000 re B*
+0.600000 0.266667 0.175000 rg
+111.000000 71.000000 8.000000 8.000000 re B*
+0.650000 0.266667 0.150000 rg
+121.000000 71.000000 8.000000 8.000000 re B*
+0.700000 0.266667 0.125000 rg
+131.000000 71.000000 8.000000 8.000000 re B*
+0.750000 0.266667 0.100000 rg
+141.000000 71.000000 8.000000 8.000000 re B*
+0.800000 0.266667 0.075000 rg
+151.000000 71.000000 8.000000 8.000000 re B*
+0.850000 0.266667 0.050000 rg
+161.000000 71.000000 8.000000 8.000000 re B*
+0.900000 0.266667 0.025000 rg
+171.000000 71.000000 8.000000 8.000000 re B*
+0.950000 0.266667 0.000000 rg
+181.000000 71.000000 8.000000 8.000000 re B*
+1.000000 0.266667 -0.025000 rg
+191.000000 71.000000 8.000000 8.000000 re B*
+0.050000 0.300000 0.475000 rg
+1.000000 81.000000 8.000000 8.000000 re B*
+0.100000 0.300000 0.450000 rg
+11.000000 81.000000 8.000000 8.000000 re B*
+0.150000 0.300000 0.425000 rg
+21.000000 81.000000 8.000000 8.000000 re B*
+0.200000 0.300000 0.400000 rg
+31.000000 81.000000 8.000000 8.000000 re B*
+0.250000 0.300000 0.375000 rg
+41.000000 81.000000 8.000000 8.000000 re B*
+0.300000 0.300000 0.350000 rg
+51.000000 81.000000 8.000000 8.000000 re B*
+0.350000 0.300000 0.325000 rg
+61.000000 81.000000 8.000000 8.000000 re B*
+0.400000 0.300000 0.300000 rg
+71.000000 81.000000 8.000000 8.000000 re B*
+0.450000 0.300000 0.275000 rg
+81.000000 81.000000 8.000000 8.000000 re B*
+0.500000 0.300000 0.250000 rg
+91.000000 81.000000 8.000000 8.000000 re B*
+0.550000 0.300000 0.225000 rg
+101.000000 81.000000 8.000000 8.000000 re B*
+0.600000 0.300000 0.200000 rg
+111.000000 81.000000 8.000000 8.000000 re B*
+0.650000 0.300000 0.175000 rg
+121.000000 81.000000 8.000000 8.000000 re B*
+0.700000 0.300000 0.150000 rg
+131.000000 81.000000 8.000000 8.000000 re B*
+0.750000 0.300000 0.125000 rg
+141.000000 81.000000 8.000000 8.000000 re B*
+0.800000 0.300000 0.100000 rg
+151.000000 81.000000 8.000000 8.000000 re B*
+0.850000 0.300000 0.075000 rg
+161.000000 81.000000 8.000000 8.000000 re B*
+0.900000 0.300000 0.050000 rg
+171.000000 81.000000 8.000000 8.000000 re B*
+0.950000 0.300000 0.025000 rg
+181.000000 81.000000 8.000000 8.000000 re B*
+1.000000 0.300000 0.000000 rg
+191.000000 81.000000 8.000000 8.000000 re B*
+0.050000 0.333333 0.500000 rg
+1.000000 91.000000 8.000000 8.000000 re B*
+0.100000 0.333333 0.475000 rg
+11.000000 91.000000 8.000000 8.000000 re B*
+0.150000 0.333333 0.450000 rg
+21.000000 91.000000 8.000000 8.000000 re B*
+0.200000 0.333333 0.425000 rg
+31.000000 91.000000 8.000000 8.000000 re B*
+0.250000 0.333333 0.400000 rg
+41.000000 91.000000 8.000000 8.000000 re B*
+0.300000 0.333333 0.375000 rg
+51.000000 91.000000 8.000000 8.000000 re B*
+0.350000 0.333333 0.350000 rg
+61.000000 91.000000 8.000000 8.000000 re B*
+0.400000 0.333333 0.325000 rg
+71.000000 91.000000 8.000000 8.000000 re B*
+0.450000 0.333333 0.300000 rg
+81.000000 91.000000 8.000000 8.000000 re B*
+0.500000 0.333333 0.275000 rg
+91.000000 91.000000 8.000000 8.000000 re B*
+0.550000 0.333333 0.250000 rg
+101.000000 91.000000 8.000000 8.000000 re B*
+0.600000 0.333333 0.225000 rg
+111.000000 91.000000 8.000000 8.000000 re B*
+0.650000 0.333333 0.200000 rg
+121.000000 91.000000 8.000000 8.000000 re B*
+0.700000 0.333333 0.175000 rg
+131.000000 91.000000 8.000000 8.000000 re B*
+0.750000 0.333333 0.150000 rg
+141.000000 91.000000 8.000000 8.000000 re B*
+0.800000 0.333333 0.125000 rg
+151.000000 91.000000 8.000000 8.000000 re B*
+0.850000 0.333333 0.100000 rg
+161.000000 91.000000 8.000000 8.000000 re B*
+0.900000 0.333333 0.075000 rg
+171.000000 91.000000 8.000000 8.000000 re B*
+0.950000 0.333333 0.050000 rg
+181.000000 91.000000 8.000000 8.000000 re B*
+1.000000 0.333333 0.025000 rg
+191.000000 91.000000 8.000000 8.000000 re B*
+0.050000 0.366667 0.525000 rg
+1.000000 101.000000 8.000000 8.000000 re B*
+0.100000 0.366667 0.500000 rg
+11.000000 101.000000 8.000000 8.000000 re B*
+0.150000 0.366667 0.475000 rg
+21.000000 101.000000 8.000000 8.000000 re B*
+0.200000 0.366667 0.450000 rg
+31.000000 101.000000 8.000000 8.000000 re B*
+0.250000 0.366667 0.425000 rg
+41.000000 101.000000 8.000000 8.000000 re B*
+0.300000 0.366667 0.400000 rg
+51.000000 101.000000 8.000000 8.000000 re B*
+0.350000 0.366667 0.375000 rg
+61.000000 101.000000 8.000000 8.000000 re B*
+0.400000 0.366667 0.350000 rg
+71.000000 101.000000 8.000000 8.000000 re B*
+0.450000 0.366667 0.325000 rg
+81.000000 101.000000 8.000000 8.000000 re B*
+0.500000 0.366667 0.300000 rg
+91.000000 101.000000 8.000000 8.000000 re B*
+0.550000 0.366667 0.275000 rg
+101.000000 101.000000 8.000000 8.000000 re B*
+0.600000 0.366667 0.250000 rg
+111.000000 101.000000 8.000000 8.000000 re B*
+0.650000 0.366667 0.225000 rg
+121.000000 101.000000 8.000000 8.000000 re B*
+0.700000 0.366667 0.200000 rg
+131.000000 101.000000 8.000000 8.000000 re B*
+0.750000 0.366667 0.175000 rg
+141.000000 101.000000 8.000000 8.000000 re B*
+0.800000 0.366667 0.150000 rg
+151.000000 101.000000 8.000000 8.000000 re B*
+0.850000 0.366667 0.125000 rg
+161.000000 101.000000 8.000000 8.000000 re B*
+0.900000 0.366667 0.100000 rg
+171.000000 101.000000 8.000000 8.000000 re B*
+0.950000 0.366667 0.075000 rg
+181.000000 101.000000 8.000000 8.000000 re B*
+1.000000 0.366667 0.050000 rg
+191.000000 101.000000 8.000000 8.000000 re B*
+0.050000 0.400000 0.550000 rg
+1.000000 111.000000 8.000000 8.000000 re B*
+0.100000 0.400000 0.525000 rg
+11.000000 111.000000 8.000000 8.000000 re B*
+0.150000 0.400000 0.500000 rg
+21.000000 111.000000 8.000000 8.000000 re B*
+0.200000 0.400000 0.475000 rg
+31.000000 111.000000 8.000000 8.000000 re B*
+0.250000 0.400000 0.450000 rg
+41.000000 111.000000 8.000000 8.000000 re B*
+0.300000 0.400000 0.425000 rg
+51.000000 111.000000 8.000000 8.000000 re B*
+0.350000 0.400000 0.400000 rg
+61.000000 111.000000 8.000000 8.000000 re B*
+0.400000 0.400000 0.375000 rg
+71.000000 111.000000 8.000000 8.000000 re B*
+0.450000 0.400000 0.350000 rg
+81.000000 111.000000 8.000000 8.000000 re B*
+0.500000 0.400000 0.325000 rg
+91.000000 111.000000 8.000000 8.000000 re B*
+0.550000 0.400000 0.300000 rg
+101.000000 111.000000 8.000000 8.000000 re B*
+0.600000 0.400000 0.275000 rg
+111.000000 111.000000 8.000000 8.000000 re B*
+0.650000 0.400000 0.250000 rg
+121.000000 111.000000 8.000000 8.000000 re B*
+0.700000 0.400000 0.225000 rg
+131.000000 111.000000 8.000000 8.000000 re B*
+0.750000 0.400000 0.200000 rg
+141.000000 111.000000 8.000000 8.000000 re B*
+0.800000 0.400000 0.175000 rg
+151.000000 111.000000 8.000000 8.000000 re B*
+0.850000 0.400000 0.150000 rg
+161.000000 111.000000 8.000000 8.000000 re B*
+0.900000 0.400000 0.125000 rg
+171.000000 111.000000 8.000000 8.000000 re B*
+0.950000 0.400000 0.100000 rg
+181.000000 111.000000 8.000000 8.000000 re B*
+1.000000 0.400000 0.075000 rg
+191.000000 111.000000 8.000000 8.000000 re B*
+0.050000 0.433333 0.575000 rg
+1.000000 121.000000 8.000000 8.000000 re B*
+0.100000 0.433333 0.550000 rg
+11.000000 121.000000 8.000000 8.000000 re B*
+0.150000 0.433333 0.525000 rg
+21.000000 121.000000 8.000000 8.000000 re B*
+0.200000 0.433333 0.500000 rg
+31.000000 121.000000 8.000000 8.000000 re B*
+0.250000 0.433333 0.475000 rg
+41.000000 121.000000 8.000000 8.000000 re B*
+0.300000 0.433333 0.450000 rg
+51.000000 121.000000 8.000000 8.000000 re B*
+0.350000 0.433333 0.425000 rg
+61.000000 121.000000 8.000000 8.000000 re B*
+0.400000 0.433333 0.400000 rg
+71.000000 121.000000 8.000000 8.000000 re B*
+0.450000 0.433333 0.375000 rg
+81.000000 121.000000 8.000000 8.000000 re B*
+0.500000 0.433333 0.350000 rg
+91.000000 121.000000 8.000000 8.000000 re B*
+0.550000 0.433333 0.325000 rg
+101.000000 121.000000 8.000000 8.000000 re B*
+0.600000 0.433333 0.300000 rg
+111.000000 121.000000 8.000000 8.000000 re B*
+0.650000 0.433333 0.275000 rg
+121.000000 121.000000 8.000000 8.000000 re B*
+0.700000 0.433333 0.250000 rg
+131.000000 121.000000 8.000000 8.000000 re B*
+0.750000 0.433333 0.225000 rg
+141.000000 121.000000 8.000000 8.000000 re B*
+0.800000 0.433333 0.200000 rg
+151.000000 121.000000 8.000000 8.000000 re B*
+0.850000 0.433333 0.175000 rg
+161.000000 121.000000 8.000000 8.000000 re B*
+0.900000 0.433333 0.150000 rg
+171.000000 121.000000 8.000000 8.000000 re B*
+0.950000 0.433333 0.125000 rg
+181.000000 121.000000 8.000000 8.000000 re B*
+1.000000 0.433333 0.100000 rg
+191.000000 121.000000 8.000000 8.000000 re B*
+0.050000 0.466667 0.600000 rg
+1.000000 131.000000 8.000000 8.000000 re B*
+0.100000 0.466667 0.575000 rg
+11.000000 131.000000 8.000000 8.000000 re B*
+0.150000 0.466667 0.550000 rg
+21.000000 131.000000 8.000000 8.000000 re B*
+0.200000 0.466667 0.525000 rg
+31.000000 131.000000 8.000000 8.000000 re B*
+0.250000 0.466667 0.500000 rg
+41.000000 131.000000 8.000000 8.000000 re B*
+0.300000 0.466667 0.475000 rg
+51.000000 131.000000 8.000000 8.000000 re B*
+0.350000 0.466667 0.450000 rg
+61.000000 131.000000 8.000000 8.000000 re B*
+0.400000 0.466667 0.425000 rg
+71.000000 131.000000 8.000000 8.000000 re B*
+0.450000 0.466667 0.400000 rg
+81.000000 131.000000 8.000000 8.000000 re B*
+0.500000 0.466667 0.375000 rg
+91.000000 131.000000 8.000000 8.000000 re B*
+0.550000 0.466667 0.350000 rg
+101.000000 131.000000 8.000000 8.000000 re B*
+0.600000 0.466667 0.325000 rg
+111.000000 131.000000 8.000000 8.000000 re B*
+0.650000 0.466667 0.300000 rg
+121.000000 131.000000 8.000000 8.000000 re B*
+0.700000 0.466667 0.275000 rg
+131.000000 131.000000 8.000000 8.000000 re B*
+0.750000 0.466667 0.250000 rg
+141.000000 131.000000 8.000000 8.000000 re B*
+0.800000 0.466667 0.225000 rg
+151.000000 131.000000 8.000000 8.000000 re B*
+0.850000 0.466667 0.200000 rg
+161.000000 131.000000 8.000000 8.000000 re B*
+0.900000 0.466667 0.175000 rg
+171.000000 131.000000 8.000000 8.000000 re B*
+0.950000 0.466667 0.150000 rg
+181.000000 131.000000 8.000000 8.000000 re B*
+1.000000 0.466667 0.125000 rg
+191.000000 131.000000 8.000000 8.000000 re B*
+0.050000 0.500000 0.625000 rg
+1.000000 141.000000 8.000000 8.000000 re B*
+0.100000 0.500000 0.600000 rg
+11.000000 141.000000 8.000000 8.000000 re B*
+0.150000 0.500000 0.575000 rg
+21.000000 141.000000 8.000000 8.000000 re B*
+0.200000 0.500000 0.550000 rg
+31.000000 141.000000 8.000000 8.000000 re B*
+0.250000 0.500000 0.525000 rg
+41.000000 141.000000 8.000000 8.000000 re B*
+0.300000 0.500000 0.500000 rg
+51.000000 141.000000 8.000000 8.000000 re B*
+0.350000 0.500000 0.475000 rg
+61.000000 141.000000 8.000000 8.000000 re B*
+0.400000 0.500000 0.450000 rg
+71.000000 141.000000 8.000000 8.000000 re B*
+0.450000 0.500000 0.425000 rg
+81.000000 141.000000 8.000000 8.000000 re B*
+0.500000 0.500000 0.400000 rg
+91.000000 141.000000 8.000000 8.000000 re B*
+0.550000 0.500000 0.375000 rg
+101.000000 141.000000 8.000000 8.000000 re B*
+0.600000 0.500000 0.350000 rg
+111.000000 141.000000 8.000000 8.000000 re B*
+0.650000 0.500000 0.325000 rg
+121.000000 141.000000 8.000000 8.000000 re B*
+0.700000 0.500000 0.300000 rg
+131.000000 141.000000 8.000000 8.000000 re B*
+0.750000 0.500000 0.275000 rg
+141.000000 141.000000 8.000000 8.000000 re B*
+0.800000 0.500000 0.250000 rg
+151.000000 141.000000 8.000000 8.000000 re B*
+0.850000 0.500000 0.225000 rg
+161.000000 141.000000 8.000000 8.000000 re B*
+0.900000 0.500000 0.200000 rg
+171.000000 141.000000 8.000000 8.000000 re B*
+0.950000 0.500000 0.175000 rg
+181.000000 141.000000 8.000000 8.000000 re B*
+1.000000 0.500000 0.150000 rg
+191.000000 141.000000 8.000000 8.000000 re B*
+0.050000 0.533333 0.650000 rg
+1.000000 151.000000 8.000000 8.000000 re B*
+0.100000 0.533333 0.625000 rg
+11.000000 151.000000 8.000000 8.000000 re B*
+0.150000 0.533333 0.600000 rg
+21.000000 151.000000 8.000000 8.000000 re B*
+0.200000 0.533333 0.575000 rg
+31.000000 151.000000 8.000000 8.000000 re B*
+0.250000 0.533333 0.550000 rg
+41.000000 151.000000 8.000000 8.000000 re B*
+0.300000 0.533333 0.525000 rg
+51.000000 151.000000 8.000000 8.000000 re B*
+0.350000 0.533333 0.500000 rg
+61.000000 151.000000 8.000000 8.000000 re B*
+0.400000 0.533333 0.475000 rg
+71.000000 151.000000 8.000000 8.000000 re B*
+0.450000 0.533333 0.450000 rg
+81.000000 151.000000 8.000000 8.000000 re B*
+0.500000 0.533333 0.425000 rg
+91.000000 151.000000 8.000000 8.000000 re B*
+0.550000 0.533333 0.400000 rg
+101.000000 151.000000 8.000000 8.000000 re B*
+0.600000 0.533333 0.375000 rg
+111.000000 151.000000 8.000000 8.000000 re B*
+0.650000 0.533333 0.350000 rg
+121.000000 151.000000 8.000000 8.000000 re B*
+0.700000 0.533333 0.325000 rg
+131.000000 151.000000 8.000000 8.000000 re B*
+0.750000 0.533333 0.300000 rg
+141.000000 151.000000 8.000000 8.000000 re B*
+0.800000 0.533333 0.275000 rg
+151.000000 151.000000 8.000000 8.000000 re B*
+0.850000 0.533333 0.250000 rg
+161.000000 151.000000 8.000000 8.000000 re B*
+0.900000 0.533333 0.225000 rg
+171.000000 151.000000 8.000000 8.000000 re B*
+0.950000 0.533333 0.200000 rg
+181.000000 151.000000 8.000000 8.000000 re B*
+1.000000 0.533333 0.175000 rg
+191.000000 151.000000 8.000000 8.000000 re B*
+0.050000 0.566667 0.675000 rg
+1.000000 161.000000 8.000000 8.000000 re B*
+0.100000 0.566667 0.650000 rg
+11.000000 161.000000 8.000000 8.000000 re B*
+0.150000 0.566667 0.625000 rg
+21.000000 161.000000 8.000000 8.000000 re B*
+0.200000 0.566667 0.600000 rg
+31.000000 161.000000 8.000000 8.000000 re B*
+0.250000 0.566667 0.575000 rg
+41.000000 161.000000 8.000000 8.000000 re B*
+0.300000 0.566667 0.550000 rg
+51.000000 161.000000 8.000000 8.000000 re B*
+0.350000 0.566667 0.525000 rg
+61.000000 161.000000 8.000000 8.000000 re B*
+0.400000 0.566667 0.500000 rg
+71.000000 161.000000 8.000000 8.000000 re B*
+0.450000 0.566667 0.475000 rg
+81.000000 161.000000 8.000000 8.000000 re B*
+0.500000 0.566667 0.450000 rg
+91.000000 161.000000 8.000000 8.000000 re B*
+0.550000 0.566667 0.425000 rg
+101.000000 161.000000 8.000000 8.000000 re B*
+0.600000 0.566667 0.400000 rg
+111.000000 161.000000 8.000000 8.000000 re B*
+0.650000 0.566667 0.375000 rg
+121.000000 161.000000 8.000000 8.000000 re B*
+0.700000 0.566667 0.350000 rg
+131.000000 161.000000 8.000000 8.000000 re B*
+0.750000 0.566667 0.325000 rg
+141.000000 161.000000 8.000000 8.000000 re B*
+0.800000 0.566667 0.300000 rg
+151.000000 161.000000 8.000000 8.000000 re B*
+0.850000 0.566667 0.275000 rg
+161.000000 161.000000 8.000000 8.000000 re B*
+0.900000 0.566667 0.250000 rg
+171.000000 161.000000 8.000000 8.000000 re B*
+0.950000 0.566667 0.225000 rg
+181.000000 161.000000 8.000000 8.000000 re B*
+1.000000 0.566667 0.200000 rg
+191.000000 161.000000 8.000000 8.000000 re B*
+0.050000 0.600000 0.700000 rg
+1.000000 171.000000 8.000000 8.000000 re B*
+0.100000 0.600000 0.675000 rg
+11.000000 171.000000 8.000000 8.000000 re B*
+0.150000 0.600000 0.650000 rg
+21.000000 171.000000 8.000000 8.000000 re B*
+0.200000 0.600000 0.625000 rg
+31.000000 171.000000 8.000000 8.000000 re B*
+0.250000 0.600000 0.600000 rg
+41.000000 171.000000 8.000000 8.000000 re B*
+0.300000 0.600000 0.575000 rg
+51.000000 171.000000 8.000000 8.000000 re B*
+0.350000 0.600000 0.550000 rg
+61.000000 171.000000 8.000000 8.000000 re B*
+0.400000 0.600000 0.525000 rg
+71.000000 171.000000 8.000000 8.000000 re B*
+0.450000 0.600000 0.500000 rg
+81.000000 171.000000 8.000000 8.000000 re B*
+0.500000 0.600000 0.475000 rg
+91.000000 171.000000 8.000000 8.000000 re B*
+0.550000 0.600000 0.450000 rg
+101.000000 171.000000 8.000000 8.000000 re B*
+0.600000 0.600000 0.425000 rg
+111.000000 171.000000 8.000000 8.000000 re B*
+0.650000 0.600000 0.400000 rg
+121.000000 171.000000 8.000000 8.000000 re B*
+0.700000 0.600000 0.375000 rg
+131.000000 171.000000 8.000000 8.000000 re B*
+0.750000 0.600000 0.350000 rg
+141.000000 171.000000 8.000000 8.000000 re B*
+0.800000 0.600000 0.325000 rg
+151.000000 171.000000 8.000000 8.000000 re B*
+0.850000 0.600000 0.300000 rg
+161.000000 171.000000 8.000000 8.000000 re B*
+0.900000 0.600000 0.275000 rg
+171.000000 171.000000 8.000000 8.000000 re B*
+0.950000 0.600000 0.250000 rg
+181.000000 171.000000 8.000000 8.000000 re B*
+1.000000 0.600000 0.225000 rg
+191.000000 171.000000 8.000000 8.000000 re B*
+0.050000 0.633333 0.725000 rg
+1.000000 181.000000 8.000000 8.000000 re B*
+0.100000 0.633333 0.700000 rg
+11.000000 181.000000 8.000000 8.000000 re B*
+0.150000 0.633333 0.675000 rg
+21.000000 181.000000 8.000000 8.000000 re B*
+0.200000 0.633333 0.650000 rg
+31.000000 181.000000 8.000000 8.000000 re B*
+0.250000 0.633333 0.625000 rg
+41.000000 181.000000 8.000000 8.000000 re B*
+0.300000 0.633333 0.600000 rg
+51.000000 181.000000 8.000000 8.000000 re B*
+0.350000 0.633333 0.575000 rg
+61.000000 181.000000 8.000000 8.000000 re B*
+0.400000 0.633333 0.550000 rg
+71.000000 181.000000 8.000000 8.000000 re B*
+0.450000 0.633333 0.525000 rg
+81.000000 181.000000 8.000000 8.000000 re B*
+0.500000 0.633333 0.500000 rg
+91.000000 181.000000 8.000000 8.000000 re B*
+0.550000 0.633333 0.475000 rg
+101.000000 181.000000 8.000000 8.000000 re B*
+0.600000 0.633333 0.450000 rg
+111.000000 181.000000 8.000000 8.000000 re B*
+0.650000 0.633333 0.425000 rg
+121.000000 181.000000 8.000000 8.000000 re B*
+0.700000 0.633333 0.400000 rg
+131.000000 181.000000 8.000000 8.000000 re B*
+0.750000 0.633333 0.375000 rg
+141.000000 181.000000 8.000000 8.000000 re B*
+0.800000 0.633333 0.350000 rg
+151.000000 181.000000 8.000000 8.000000 re B*
+0.850000 0.633333 0.325000 rg
+161.000000 181.000000 8.000000 8.000000 re B*
+0.900000 0.633333 0.300000 rg
+171.000000 181.000000 8.000000 8.000000 re B*
+0.950000 0.633333 0.275000 rg
+181.000000 181.000000 8.000000 8.000000 re B*
+1.000000 0.633333 0.250000 rg
+191.000000 181.000000 8.000000 8.000000 re B*
+0.050000 0.666667 0.750000 rg
+1.000000 191.000000 8.000000 8.000000 re B*
+0.100000 0.666667 0.725000 rg
+11.000000 191.000000 8.000000 8.000000 re B*
+0.150000 0.666667 0.700000 rg
+21.000000 191.000000 8.000000 8.000000 re B*
+0.200000 0.666667 0.675000 rg
+31.000000 191.000000 8.000000 8.000000 re B*
+0.250000 0.666667 0.650000 rg
+41.000000 191.000000 8.000000 8.000000 re B*
+0.300000 0.666667 0.625000 rg
+51.000000 191.000000 8.000000 8.000000 re B*
+0.350000 0.666667 0.600000 rg
+61.000000 191.000000 8.000000 8.000000 re B*
+0.400000 0.666667 0.575000 rg
+71.000000 191.000000 8.000000 8.000000 re B*
+0.450000 0.666667 0.550000 rg
+81.000000 191.000000 8.000000 8.000000 re B*
+0.500000 0.666667 0.525000 rg
+91.000000 191.000000 8.000000 8.000000 re B*
+0.550000 0.666667 0.500000 rg
+101.000000 191.000000 8.000000 8.000000 re B*
+0.600000 0.666667 0.475000 rg
+111.000000 191.000000 8.000000 8.000000 re B*
+0.650000 0.666667 0.450000 rg
+121.000000 191.000000 8.000000 8.000000 re B*
+0.700000 0.666667 0.425000 rg
+131.000000 191.000000 8.000000 8.000000 re B*
+0.750000 0.666667 0.400000 rg
+141.000000 191.000000 8.000000 8.000000 re B*
+0.800000 0.666667 0.375000 rg
+151.000000 191.000000 8.000000 8.000000 re B*
+0.850000 0.666667 0.350000 rg
+161.000000 191.000000 8.000000 8.000000 re B*
+0.900000 0.666667 0.325000 rg
+171.000000 191.000000 8.000000 8.000000 re B*
+0.950000 0.666667 0.300000 rg
+181.000000 191.000000 8.000000 8.000000 re B*
+1.000000 0.666667 0.275000 rg
+191.000000 191.000000 8.000000 8.000000 re B*
+0.050000 0.700000 0.775000 rg
+1.000000 201.000000 8.000000 8.000000 re B*
+0.100000 0.700000 0.750000 rg
+11.000000 201.000000 8.000000 8.000000 re B*
+0.150000 0.700000 0.725000 rg
+21.000000 201.000000 8.000000 8.000000 re B*
+0.200000 0.700000 0.700000 rg
+31.000000 201.000000 8.000000 8.000000 re B*
+0.250000 0.700000 0.675000 rg
+41.000000 201.000000 8.000000 8.000000 re B*
+0.300000 0.700000 0.650000 rg
+51.000000 201.000000 8.000000 8.000000 re B*
+0.350000 0.700000 0.625000 rg
+61.000000 201.000000 8.000000 8.000000 re B*
+0.400000 0.700000 0.600000 rg
+71.000000 201.000000 8.000000 8.000000 re B*
+0.450000 0.700000 0.575000 rg
+81.000000 201.000000 8.000000 8.000000 re B*
+0.500000 0.700000 0.550000 rg
+91.000000 201.000000 8.000000 8.000000 re B*
+0.550000 0.700000 0.525000 rg
+101.000000 201.000000 8.000000 8.000000 re B*
+0.600000 0.700000 0.500000 rg
+111.000000 201.000000 8.000000 8.000000 re B*
+0.650000 0.700000 0.475000 rg
+121.000000 201.000000 8.000000 8.000000 re B*
+0.700000 0.700000 0.450000 rg
+131.000000 201.000000 8.000000 8.000000 re B*
+0.750000 0.700000 0.425000 rg
+141.000000 201.000000 8.000000 8.000000 re B*
+0.800000 0.700000 0.400000 rg
+151.000000 201.000000 8.000000 8.000000 re B*
+0.850000 0.700000 0.375000 rg
+161.000000 201.000000 8.000000 8.000000 re B*
+0.900000 0.700000 0.350000 rg
+171.000000 201.000000 8.000000 8.000000 re B*
+0.950000 0.700000 0.325000 rg
+181.000000 201.000000 8.000000 8.000000 re B*
+1.000000 0.700000 0.300000 rg
+191.000000 201.000000 8.000000 8.000000 re B*
+0.050000 0.733333 0.800000 rg
+1.000000 211.000000 8.000000 8.000000 re B*
+0.100000 0.733333 0.775000 rg
+11.000000 211.000000 8.000000 8.000000 re B*
+0.150000 0.733333 0.750000 rg
+21.000000 211.000000 8.000000 8.000000 re B*
+0.200000 0.733333 0.725000 rg
+31.000000 211.000000 8.000000 8.000000 re B*
+0.250000 0.733333 0.700000 rg
+41.000000 211.000000 8.000000 8.000000 re B*
+0.300000 0.733333 0.675000 rg
+51.000000 211.000000 8.000000 8.000000 re B*
+0.350000 0.733333 0.650000 rg
+61.000000 211.000000 8.000000 8.000000 re B*
+0.400000 0.733333 0.625000 rg
+71.000000 211.000000 8.000000 8.000000 re B*
+0.450000 0.733333 0.600000 rg
+81.000000 211.000000 8.000000 8.000000 re B*
+0.500000 0.733333 0.575000 rg
+91.000000 211.000000 8.000000 8.000000 re B*
+0.550000 0.733333 0.550000 rg
+101.000000 211.000000 8.000000 8.000000 re B*
+0.600000 0.733333 0.525000 rg
+111.000000 211.000000 8.000000 8.000000 re B*
+0.650000 0.733333 0.500000 rg
+121.000000 211.000000 8.000000 8.000000 re B*
+0.700000 0.733333 0.475000 rg
+131.000000 211.000000 8.000000 8.000000 re B*
+0.750000 0.733333 0.450000 rg
+141.000000 211.000000 8.000000 8.000000 re B*
+0.800000 0.733333 0.425000 rg
+151.000000 211.000000 8.000000 8.000000 re B*
+0.850000 0.733333 0.400000 rg
+161.000000 211.000000 8.000000 8.000000 re B*
+0.900000 0.733333 0.375000 rg
+171.000000 211.000000 8.000000 8.000000 re B*
+0.950000 0.733333 0.350000 rg
+181.000000 211.000000 8.000000 8.000000 re B*
+1.000000 0.733333 0.325000 rg
+191.000000 211.000000 8.000000 8.000000 re B*
+0.050000 0.766667 0.825000 rg
+1.000000 221.000000 8.000000 8.000000 re B*
+0.100000 0.766667 0.800000 rg
+11.000000 221.000000 8.000000 8.000000 re B*
+0.150000 0.766667 0.775000 rg
+21.000000 221.000000 8.000000 8.000000 re B*
+0.200000 0.766667 0.750000 rg
+31.000000 221.000000 8.000000 8.000000 re B*
+0.250000 0.766667 0.725000 rg
+41.000000 221.000000 8.000000 8.000000 re B*
+0.300000 0.766667 0.700000 rg
+51.000000 221.000000 8.000000 8.000000 re B*
+0.350000 0.766667 0.675000 rg
+61.000000 221.000000 8.000000 8.000000 re B*
+0.400000 0.766667 0.650000 rg
+71.000000 221.000000 8.000000 8.000000 re B*
+0.450000 0.766667 0.625000 rg
+81.000000 221.000000 8.000000 8.000000 re B*
+0.500000 0.766667 0.600000 rg
+91.000000 221.000000 8.000000 8.000000 re B*
+0.550000 0.766667 0.575000 rg
+101.000000 221.000000 8.000000 8.000000 re B*
+0.600000 0.766667 0.550000 rg
+111.000000 221.000000 8.000000 8.000000 re B*
+0.650000 0.766667 0.525000 rg
+121.000000 221.000000 8.000000 8.000000 re B*
+0.700000 0.766667 0.500000 rg
+131.000000 221.000000 8.000000 8.000000 re B*
+0.750000 0.766667 0.475000 rg
+141.000000 221.000000 8.000000 8.000000 re B*
+0.800000 0.766667 0.450000 rg
+151.000000 221.000000 8.000000 8.000000 re B*
+0.850000 0.766667 0.425000 rg
+161.000000 221.000000 8.000000 8.000000 re B*
+0.900000 0.766667 0.400000 rg
+171.000000 221.000000 8.000000 8.000000 re B*
+0.950000 0.766667 0.375000 rg
+181.000000 221.000000 8.000000 8.000000 re B*
+1.000000 0.766667 0.350000 rg
+191.000000 221.000000 8.000000 8.000000 re B*
+0.050000 0.800000 0.850000 rg
+1.000000 231.000000 8.000000 8.000000 re B*
+0.100000 0.800000 0.825000 rg
+11.000000 231.000000 8.000000 8.000000 re B*
+0.150000 0.800000 0.800000 rg
+21.000000 231.000000 8.000000 8.000000 re B*
+0.200000 0.800000 0.775000 rg
+31.000000 231.000000 8.000000 8.000000 re B*
+0.250000 0.800000 0.750000 rg
+41.000000 231.000000 8.000000 8.000000 re B*
+0.300000 0.800000 0.725000 rg
+51.000000 231.000000 8.000000 8.000000 re B*
+0.350000 0.800000 0.700000 rg
+61.000000 231.000000 8.000000 8.000000 re B*
+0.400000 0.800000 0.675000 rg
+71.000000 231.000000 8.000000 8.000000 re B*
+0.450000 0.800000 0.650000 rg
+81.000000 231.000000 8.000000 8.000000 re B*
+0.500000 0.800000 0.625000 rg
+91.000000 231.000000 8.000000 8.000000 re B*
+0.550000 0.800000 0.600000 rg
+101.000000 231.000000 8.000000 8.000000 re B*
+0.600000 0.800000 0.575000 rg
+111.000000 231.000000 8.000000 8.000000 re B*
+0.650000 0.800000 0.550000 rg
+121.000000 231.000000 8.000000 8.000000 re B*
+0.700000 0.800000 0.525000 rg
+131.000000 231.000000 8.000000 8.000000 re B*
+0.750000 0.800000 0.500000 rg
+141.000000 231.000000 8.000000 8.000000 re B*
+0.800000 0.800000 0.475000 rg
+151.000000 231.000000 8.000000 8.000000 re B*
+0.850000 0.800000 0.450000 rg
+161.000000 231.000000 8.000000 8.000000 re B*
+0.900000 0.800000 0.425000 rg
+171.000000 231.000000 8.000000 8.000000 re B*
+0.950000 0.800000 0.400000 rg
+181.000000 231.000000 8.000000 8.000000 re B*
+1.000000 0.800000 0.375000 rg
+191.000000 231.000000 8.000000 8.000000 re B*
+0.050000 0.833333 0.875000 rg
+1.000000 241.000000 8.000000 8.000000 re B*
+0.100000 0.833333 0.850000 rg
+11.000000 241.000000 8.000000 8.000000 re B*
+0.150000 0.833333 0.825000 rg
+21.000000 241.000000 8.000000 8.000000 re B*
+0.200000 0.833333 0.800000 rg
+31.000000 241.000000 8.000000 8.000000 re B*
+0.250000 0.833333 0.775000 rg
+41.000000 241.000000 8.000000 8.000000 re B*
+0.300000 0.833333 0.750000 rg
+51.000000 241.000000 8.000000 8.000000 re B*
+0.350000 0.833333 0.725000 rg
+61.000000 241.000000 8.000000 8.000000 re B*
+0.400000 0.833333 0.700000 rg
+71.000000 241.000000 8.000000 8.000000 re B*
+0.450000 0.833333 0.675000 rg
+81.000000 241.000000 8.000000 8.000000 re B*
+0.500000 0.833333 0.650000 rg
+91.000000 241.000000 8.000000 8.000000 re B*
+0.550000 0.833333 0.625000 rg
+101.000000 241.000000 8.000000 8.000000 re B*
+0.600000 0.833333 0.600000 rg
+111.000000 241.000000 8.000000 8.000000 re B*
+0.650000 0.833333 0.575000 rg
+121.000000 241.000000 8.000000 8.000000 re B*
+0.700000 0.833333 0.550000 rg
+131.000000 241.000000 8.000000 8.000000 re B*
+0.750000 0.833333 0.525000 rg
+141.000000 241.000000 8.000000 8.000000 re B*
+0.800000 0.833333 0.500000 rg
+151.000000 241.000000 8.000000 8.000000 re B*
+0.850000 0.833333 0.475000 rg
+161.000000 241.000000 8.000000 8.000000 re B*
+0.900000 0.833333 0.450000 rg
+171.000000 241.000000 8.000000 8.000000 re B*
+0.950000 0.833333 0.425000 rg
+181.000000 241.000000 8.000000 8.000000 re B*
+1.000000 0.833333 0.400000 rg
+191.000000 241.000000 8.000000 8.000000 re B*
+0.050000 0.866667 0.900000 rg
+1.000000 251.000000 8.000000 8.000000 re B*
+0.100000 0.866667 0.875000 rg
+11.000000 251.000000 8.000000 8.000000 re B*
+0.150000 0.866667 0.850000 rg
+21.000000 251.000000 8.000000 8.000000 re B*
+0.200000 0.866667 0.825000 rg
+31.000000 251.000000 8.000000 8.000000 re B*
+0.250000 0.866667 0.800000 rg
+41.000000 251.000000 8.000000 8.000000 re B*
+0.300000 0.866667 0.775000 rg
+51.000000 251.000000 8.000000 8.000000 re B*
+0.350000 0.866667 0.750000 rg
+61.000000 251.000000 8.000000 8.000000 re B*
+0.400000 0.866667 0.725000 rg
+71.000000 251.000000 8.000000 8.000000 re B*
+0.450000 0.866667 0.700000 rg
+81.000000 251.000000 8.000000 8.000000 re B*
+0.500000 0.866667 0.675000 rg
+91.000000 251.000000 8.000000 8.000000 re B*
+0.550000 0.866667 0.650000 rg
+101.000000 251.000000 8.000000 8.000000 re B*
+0.600000 0.866667 0.625000 rg
+111.000000 251.000000 8.000000 8.000000 re B*
+0.650000 0.866667 0.600000 rg
+121.000000 251.000000 8.000000 8.000000 re B*
+0.700000 0.866667 0.575000 rg
+131.000000 251.000000 8.000000 8.000000 re B*
+0.750000 0.866667 0.550000 rg
+141.000000 251.000000 8.000000 8.000000 re B*
+0.800000 0.866667 0.525000 rg
+151.000000 251.000000 8.000000 8.000000 re B*
+0.850000 0.866667 0.500000 rg
+161.000000 251.000000 8.000000 8.000000 re B*
+0.900000 0.866667 0.475000 rg
+171.000000 251.000000 8.000000 8.000000 re B*
+0.950000 0.866667 0.450000 rg
+181.000000 251.000000 8.000000 8.000000 re B*
+1.000000 0.866667 0.425000 rg
+191.000000 251.000000 8.000000 8.000000 re B*
+0.050000 0.900000 0.925000 rg
+1.000000 261.000000 8.000000 8.000000 re B*
+0.100000 0.900000 0.900000 rg
+11.000000 261.000000 8.000000 8.000000 re B*
+0.150000 0.900000 0.875000 rg
+21.000000 261.000000 8.000000 8.000000 re B*
+0.200000 0.900000 0.850000 rg
+31.000000 261.000000 8.000000 8.000000 re B*
+0.250000 0.900000 0.825000 rg
+41.000000 261.000000 8.000000 8.000000 re B*
+0.300000 0.900000 0.800000 rg
+51.000000 261.000000 8.000000 8.000000 re B*
+0.350000 0.900000 0.775000 rg
+61.000000 261.000000 8.000000 8.000000 re B*
+0.400000 0.900000 0.750000 rg
+71.000000 261.000000 8.000000 8.000000 re B*
+0.450000 0.900000 0.725000 rg
+81.000000 261.000000 8.000000 8.000000 re B*
+0.500000 0.900000 0.700000 rg
+91.000000 261.000000 8.000000 8.000000 re B*
+0.550000 0.900000 0.675000 rg
+101.000000 261.000000 8.000000 8.000000 re B*
+0.600000 0.900000 0.650000 rg
+111.000000 261.000000 8.000000 8.000000 re B*
+0.650000 0.900000 0.625000 rg
+121.000000 261.000000 8.000000 8.000000 re B*
+0.700000 0.900000 0.600000 rg
+131.000000 261.000000 8.000000 8.000000 re B*
+0.750000 0.900000 0.575000 rg
+141.000000 261.000000 8.000000 8.000000 re B*
+0.800000 0.900000 0.550000 rg
+151.000000 261.000000 8.000000 8.000000 re B*
+0.850000 0.900000 0.525000 rg
+161.000000 261.000000 8.000000 8.000000 re B*
+0.900000 0.900000 0.500000 rg
+171.000000 261.000000 8.000000 8.000000 re B*
+0.950000 0.900000 0.475000 rg
+181.000000 261.000000 8.000000 8.000000 re B*
+1.000000 0.900000 0.450000 rg
+191.000000 261.000000 8.000000 8.000000 re B*
+0.050000 0.933333 0.950000 rg
+1.000000 271.000000 8.000000 8.000000 re B*
+0.100000 0.933333 0.925000 rg
+11.000000 271.000000 8.000000 8.000000 re B*
+0.150000 0.933333 0.900000 rg
+21.000000 271.000000 8.000000 8.000000 re B*
+0.200000 0.933333 0.875000 rg
+31.000000 271.000000 8.000000 8.000000 re B*
+0.250000 0.933333 0.850000 rg
+41.000000 271.000000 8.000000 8.000000 re B*
+0.300000 0.933333 0.825000 rg
+51.000000 271.000000 8.000000 8.000000 re B*
+0.350000 0.933333 0.800000 rg
+61.000000 271.000000 8.000000 8.000000 re B*
+0.400000 0.933333 0.775000 rg
+71.000000 271.000000 8.000000 8.000000 re B*
+0.450000 0.933333 0.750000 rg
+81.000000 271.000000 8.000000 8.000000 re B*
+0.500000 0.933333 0.725000 rg
+91.000000 271.000000 8.000000 8.000000 re B*
+0.550000 0.933333 0.700000 rg
+101.000000 271.000000 8.000000 8.000000 re B*
+0.600000 0.933333 0.675000 rg
+111.000000 271.000000 8.000000 8.000000 re B*
+0.650000 0.933333 0.650000 rg
+121.000000 271.000000 8.000000 8.000000 re B*
+0.700000 0.933333 0.625000 rg
+131.000000 271.000000 8.000000 8.000000 re B*
+0.750000 0.933333 0.600000 rg
+141.000000 271.000000 8.000000 8.000000 re B*
+0.800000 0.933333 0.575000 rg
+151.000000 271.000000 8.000000 8.000000 re B*
+0.850000 0.933333 0.550000 rg
+161.000000 271.000000 8.000000 8.000000 re B*
+0.900000 0.933333 0.525000 rg
+171.000000 271.000000 8.000000 8.000000 re B*
+0.950000 0.933333 0.500000 rg
+181.000000 271.000000 8.000000 8.000000 re B*
+1.000000 0.933333 0.475000 rg
+191.000000 271.000000 8.000000 8.000000 re B*
+0.050000 0.966667 0.975000 rg
+1.000000 281.000000 8.000000 8.000000 re B*
+0.100000 0.966667 0.950000 rg
+11.000000 281.000000 8.000000 8.000000 re B*
+0.150000 0.966667 0.925000 rg
+21.000000 281.000000 8.000000 8.000000 re B*
+0.200000 0.966667 0.900000 rg
+31.000000 281.000000 8.000000 8.000000 re B*
+0.250000 0.966667 0.875000 rg
+41.000000 281.000000 8.000000 8.000000 re B*
+0.300000 0.966667 0.850000 rg
+51.000000 281.000000 8.000000 8.000000 re B*
+0.350000 0.966667 0.825000 rg
+61.000000 281.000000 8.000000 8.000000 re B*
+0.400000 0.966667 0.800000 rg
+71.000000 281.000000 8.000000 8.000000 re B*
+0.450000 0.966667 0.775000 rg
+81.000000 281.000000 8.000000 8.000000 re B*
+0.500000 0.966667 0.750000 rg
+91.000000 281.000000 8.000000 8.000000 re B*
+0.550000 0.966667 0.725000 rg
+101.000000 281.000000 8.000000 8.000000 re B*
+0.600000 0.966667 0.700000 rg
+111.000000 281.000000 8.000000 8.000000 re B*
+0.650000 0.966667 0.675000 rg
+121.000000 281.000000 8.000000 8.000000 re B*
+0.700000 0.966667 0.650000 rg
+131.000000 281.000000 8.000000 8.000000 re B*
+0.750000 0.966667 0.625000 rg
+141.000000 281.000000 8.000000 8.000000 re B*
+0.800000 0.966667 0.600000 rg
+151.000000 281.000000 8.000000 8.000000 re B*
+0.850000 0.966667 0.575000 rg
+161.000000 281.000000 8.000000 8.000000 re B*
+0.900000 0.966667 0.550000 rg
+171.000000 281.000000 8.000000 8.000000 re B*
+0.950000 0.966667 0.525000 rg
+181.000000 281.000000 8.000000 8.000000 re B*
+1.000000 0.966667 0.500000 rg
+191.000000 281.000000 8.000000 8.000000 re B*
+0.050000 1.000000 1.000000 rg
+1.000000 291.000000 8.000000 8.000000 re B*
+0.100000 1.000000 0.975000 rg
+11.000000 291.000000 8.000000 8.000000 re B*
+0.150000 1.000000 0.950000 rg
+21.000000 291.000000 8.000000 8.000000 re B*
+0.200000 1.000000 0.925000 rg
+31.000000 291.000000 8.000000 8.000000 re B*
+0.250000 1.000000 0.900000 rg
+41.000000 291.000000 8.000000 8.000000 re B*
+0.300000 1.000000 0.875000 rg
+51.000000 291.000000 8.000000 8.000000 re B*
+0.350000 1.000000 0.850000 rg
+61.000000 291.000000 8.000000 8.000000 re B*
+0.400000 1.000000 0.825000 rg
+71.000000 291.000000 8.000000 8.000000 re B*
+0.450000 1.000000 0.800000 rg
+81.000000 291.000000 8.000000 8.000000 re B*
+0.500000 1.000000 0.775000 rg
+91.000000 291.000000 8.000000 8.000000 re B*
+0.550000 1.000000 0.750000 rg
+101.000000 291.000000 8.000000 8.000000 re B*
+0.600000 1.000000 0.725000 rg
+111.000000 291.000000 8.000000 8.000000 re B*
+0.650000 1.000000 0.700000 rg
+121.000000 291.000000 8.000000 8.000000 re B*
+0.700000 1.000000 0.675000 rg
+131.000000 291.000000 8.000000 8.000000 re B*
+0.750000 1.000000 0.650000 rg
+141.000000 291.000000 8.000000 8.000000 re B*
+0.800000 1.000000 0.625000 rg
+151.000000 291.000000 8.000000 8.000000 re B*
+0.850000 1.000000 0.600000 rg
+161.000000 291.000000 8.000000 8.000000 re B*
+0.900000 1.000000 0.575000 rg
+171.000000 291.000000 8.000000 8.000000 re B*
+0.950000 1.000000 0.550000 rg
+181.000000 291.000000 8.000000 8.000000 re B*
+1.000000 1.000000 0.525000 rg
+191.000000 291.000000 8.000000 8.000000 re B*
+
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/many_rectangles.pdf b/testing/resources/many_rectangles.pdf
new file mode 100644
index 0000000..8030501
--- /dev/null
+++ b/testing/resources/many_rectangles.pdf
@@ -0,0 +1,1243 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 300 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+>>
+stream
+q
+
+0.050000 0.033333 0.275000 rg
+1.000000 1.000000 8.000000 8.000000 re B*
+0.100000 0.033333 0.250000 rg
+11.000000 1.000000 8.000000 8.000000 re B*
+0.150000 0.033333 0.225000 rg
+21.000000 1.000000 8.000000 8.000000 re B*
+0.200000 0.033333 0.200000 rg
+31.000000 1.000000 8.000000 8.000000 re B*
+0.250000 0.033333 0.175000 rg
+41.000000 1.000000 8.000000 8.000000 re B*
+0.300000 0.033333 0.150000 rg
+51.000000 1.000000 8.000000 8.000000 re B*
+0.350000 0.033333 0.125000 rg
+61.000000 1.000000 8.000000 8.000000 re B*
+0.400000 0.033333 0.100000 rg
+71.000000 1.000000 8.000000 8.000000 re B*
+0.450000 0.033333 0.075000 rg
+81.000000 1.000000 8.000000 8.000000 re B*
+0.500000 0.033333 0.050000 rg
+91.000000 1.000000 8.000000 8.000000 re B*
+0.550000 0.033333 0.025000 rg
+101.000000 1.000000 8.000000 8.000000 re B*
+0.600000 0.033333 0.000000 rg
+111.000000 1.000000 8.000000 8.000000 re B*
+0.650000 0.033333 -0.025000 rg
+121.000000 1.000000 8.000000 8.000000 re B*
+0.700000 0.033333 -0.050000 rg
+131.000000 1.000000 8.000000 8.000000 re B*
+0.750000 0.033333 -0.075000 rg
+141.000000 1.000000 8.000000 8.000000 re B*
+0.800000 0.033333 -0.100000 rg
+151.000000 1.000000 8.000000 8.000000 re B*
+0.850000 0.033333 -0.125000 rg
+161.000000 1.000000 8.000000 8.000000 re B*
+0.900000 0.033333 -0.150000 rg
+171.000000 1.000000 8.000000 8.000000 re B*
+0.950000 0.033333 -0.175000 rg
+181.000000 1.000000 8.000000 8.000000 re B*
+1.000000 0.033333 -0.200000 rg
+191.000000 1.000000 8.000000 8.000000 re B*
+0.050000 0.066667 0.300000 rg
+1.000000 11.000000 8.000000 8.000000 re B*
+0.100000 0.066667 0.275000 rg
+11.000000 11.000000 8.000000 8.000000 re B*
+0.150000 0.066667 0.250000 rg
+21.000000 11.000000 8.000000 8.000000 re B*
+0.200000 0.066667 0.225000 rg
+31.000000 11.000000 8.000000 8.000000 re B*
+0.250000 0.066667 0.200000 rg
+41.000000 11.000000 8.000000 8.000000 re B*
+0.300000 0.066667 0.175000 rg
+51.000000 11.000000 8.000000 8.000000 re B*
+0.350000 0.066667 0.150000 rg
+61.000000 11.000000 8.000000 8.000000 re B*
+0.400000 0.066667 0.125000 rg
+71.000000 11.000000 8.000000 8.000000 re B*
+0.450000 0.066667 0.100000 rg
+81.000000 11.000000 8.000000 8.000000 re B*
+0.500000 0.066667 0.075000 rg
+91.000000 11.000000 8.000000 8.000000 re B*
+0.550000 0.066667 0.050000 rg
+101.000000 11.000000 8.000000 8.000000 re B*
+0.600000 0.066667 0.025000 rg
+111.000000 11.000000 8.000000 8.000000 re B*
+0.650000 0.066667 0.000000 rg
+121.000000 11.000000 8.000000 8.000000 re B*
+0.700000 0.066667 -0.025000 rg
+131.000000 11.000000 8.000000 8.000000 re B*
+0.750000 0.066667 -0.050000 rg
+141.000000 11.000000 8.000000 8.000000 re B*
+0.800000 0.066667 -0.075000 rg
+151.000000 11.000000 8.000000 8.000000 re B*
+0.850000 0.066667 -0.100000 rg
+161.000000 11.000000 8.000000 8.000000 re B*
+0.900000 0.066667 -0.125000 rg
+171.000000 11.000000 8.000000 8.000000 re B*
+0.950000 0.066667 -0.150000 rg
+181.000000 11.000000 8.000000 8.000000 re B*
+1.000000 0.066667 -0.175000 rg
+191.000000 11.000000 8.000000 8.000000 re B*
+0.050000 0.100000 0.325000 rg
+1.000000 21.000000 8.000000 8.000000 re B*
+0.100000 0.100000 0.300000 rg
+11.000000 21.000000 8.000000 8.000000 re B*
+0.150000 0.100000 0.275000 rg
+21.000000 21.000000 8.000000 8.000000 re B*
+0.200000 0.100000 0.250000 rg
+31.000000 21.000000 8.000000 8.000000 re B*
+0.250000 0.100000 0.225000 rg
+41.000000 21.000000 8.000000 8.000000 re B*
+0.300000 0.100000 0.200000 rg
+51.000000 21.000000 8.000000 8.000000 re B*
+0.350000 0.100000 0.175000 rg
+61.000000 21.000000 8.000000 8.000000 re B*
+0.400000 0.100000 0.150000 rg
+71.000000 21.000000 8.000000 8.000000 re B*
+0.450000 0.100000 0.125000 rg
+81.000000 21.000000 8.000000 8.000000 re B*
+0.500000 0.100000 0.100000 rg
+91.000000 21.000000 8.000000 8.000000 re B*
+0.550000 0.100000 0.075000 rg
+101.000000 21.000000 8.000000 8.000000 re B*
+0.600000 0.100000 0.050000 rg
+111.000000 21.000000 8.000000 8.000000 re B*
+0.650000 0.100000 0.025000 rg
+121.000000 21.000000 8.000000 8.000000 re B*
+0.700000 0.100000 0.000000 rg
+131.000000 21.000000 8.000000 8.000000 re B*
+0.750000 0.100000 -0.025000 rg
+141.000000 21.000000 8.000000 8.000000 re B*
+0.800000 0.100000 -0.050000 rg
+151.000000 21.000000 8.000000 8.000000 re B*
+0.850000 0.100000 -0.075000 rg
+161.000000 21.000000 8.000000 8.000000 re B*
+0.900000 0.100000 -0.100000 rg
+171.000000 21.000000 8.000000 8.000000 re B*
+0.950000 0.100000 -0.125000 rg
+181.000000 21.000000 8.000000 8.000000 re B*
+1.000000 0.100000 -0.150000 rg
+191.000000 21.000000 8.000000 8.000000 re B*
+0.050000 0.133333 0.350000 rg
+1.000000 31.000000 8.000000 8.000000 re B*
+0.100000 0.133333 0.325000 rg
+11.000000 31.000000 8.000000 8.000000 re B*
+0.150000 0.133333 0.300000 rg
+21.000000 31.000000 8.000000 8.000000 re B*
+0.200000 0.133333 0.275000 rg
+31.000000 31.000000 8.000000 8.000000 re B*
+0.250000 0.133333 0.250000 rg
+41.000000 31.000000 8.000000 8.000000 re B*
+0.300000 0.133333 0.225000 rg
+51.000000 31.000000 8.000000 8.000000 re B*
+0.350000 0.133333 0.200000 rg
+61.000000 31.000000 8.000000 8.000000 re B*
+0.400000 0.133333 0.175000 rg
+71.000000 31.000000 8.000000 8.000000 re B*
+0.450000 0.133333 0.150000 rg
+81.000000 31.000000 8.000000 8.000000 re B*
+0.500000 0.133333 0.125000 rg
+91.000000 31.000000 8.000000 8.000000 re B*
+0.550000 0.133333 0.100000 rg
+101.000000 31.000000 8.000000 8.000000 re B*
+0.600000 0.133333 0.075000 rg
+111.000000 31.000000 8.000000 8.000000 re B*
+0.650000 0.133333 0.050000 rg
+121.000000 31.000000 8.000000 8.000000 re B*
+0.700000 0.133333 0.025000 rg
+131.000000 31.000000 8.000000 8.000000 re B*
+0.750000 0.133333 0.000000 rg
+141.000000 31.000000 8.000000 8.000000 re B*
+0.800000 0.133333 -0.025000 rg
+151.000000 31.000000 8.000000 8.000000 re B*
+0.850000 0.133333 -0.050000 rg
+161.000000 31.000000 8.000000 8.000000 re B*
+0.900000 0.133333 -0.075000 rg
+171.000000 31.000000 8.000000 8.000000 re B*
+0.950000 0.133333 -0.100000 rg
+181.000000 31.000000 8.000000 8.000000 re B*
+1.000000 0.133333 -0.125000 rg
+191.000000 31.000000 8.000000 8.000000 re B*
+0.050000 0.166667 0.375000 rg
+1.000000 41.000000 8.000000 8.000000 re B*
+0.100000 0.166667 0.350000 rg
+11.000000 41.000000 8.000000 8.000000 re B*
+0.150000 0.166667 0.325000 rg
+21.000000 41.000000 8.000000 8.000000 re B*
+0.200000 0.166667 0.300000 rg
+31.000000 41.000000 8.000000 8.000000 re B*
+0.250000 0.166667 0.275000 rg
+41.000000 41.000000 8.000000 8.000000 re B*
+0.300000 0.166667 0.250000 rg
+51.000000 41.000000 8.000000 8.000000 re B*
+0.350000 0.166667 0.225000 rg
+61.000000 41.000000 8.000000 8.000000 re B*
+0.400000 0.166667 0.200000 rg
+71.000000 41.000000 8.000000 8.000000 re B*
+0.450000 0.166667 0.175000 rg
+81.000000 41.000000 8.000000 8.000000 re B*
+0.500000 0.166667 0.150000 rg
+91.000000 41.000000 8.000000 8.000000 re B*
+0.550000 0.166667 0.125000 rg
+101.000000 41.000000 8.000000 8.000000 re B*
+0.600000 0.166667 0.100000 rg
+111.000000 41.000000 8.000000 8.000000 re B*
+0.650000 0.166667 0.075000 rg
+121.000000 41.000000 8.000000 8.000000 re B*
+0.700000 0.166667 0.050000 rg
+131.000000 41.000000 8.000000 8.000000 re B*
+0.750000 0.166667 0.025000 rg
+141.000000 41.000000 8.000000 8.000000 re B*
+0.800000 0.166667 0.000000 rg
+151.000000 41.000000 8.000000 8.000000 re B*
+0.850000 0.166667 -0.025000 rg
+161.000000 41.000000 8.000000 8.000000 re B*
+0.900000 0.166667 -0.050000 rg
+171.000000 41.000000 8.000000 8.000000 re B*
+0.950000 0.166667 -0.075000 rg
+181.000000 41.000000 8.000000 8.000000 re B*
+1.000000 0.166667 -0.100000 rg
+191.000000 41.000000 8.000000 8.000000 re B*
+0.050000 0.200000 0.400000 rg
+1.000000 51.000000 8.000000 8.000000 re B*
+0.100000 0.200000 0.375000 rg
+11.000000 51.000000 8.000000 8.000000 re B*
+0.150000 0.200000 0.350000 rg
+21.000000 51.000000 8.000000 8.000000 re B*
+0.200000 0.200000 0.325000 rg
+31.000000 51.000000 8.000000 8.000000 re B*
+0.250000 0.200000 0.300000 rg
+41.000000 51.000000 8.000000 8.000000 re B*
+0.300000 0.200000 0.275000 rg
+51.000000 51.000000 8.000000 8.000000 re B*
+0.350000 0.200000 0.250000 rg
+61.000000 51.000000 8.000000 8.000000 re B*
+0.400000 0.200000 0.225000 rg
+71.000000 51.000000 8.000000 8.000000 re B*
+0.450000 0.200000 0.200000 rg
+81.000000 51.000000 8.000000 8.000000 re B*
+0.500000 0.200000 0.175000 rg
+91.000000 51.000000 8.000000 8.000000 re B*
+0.550000 0.200000 0.150000 rg
+101.000000 51.000000 8.000000 8.000000 re B*
+0.600000 0.200000 0.125000 rg
+111.000000 51.000000 8.000000 8.000000 re B*
+0.650000 0.200000 0.100000 rg
+121.000000 51.000000 8.000000 8.000000 re B*
+0.700000 0.200000 0.075000 rg
+131.000000 51.000000 8.000000 8.000000 re B*
+0.750000 0.200000 0.050000 rg
+141.000000 51.000000 8.000000 8.000000 re B*
+0.800000 0.200000 0.025000 rg
+151.000000 51.000000 8.000000 8.000000 re B*
+0.850000 0.200000 0.000000 rg
+161.000000 51.000000 8.000000 8.000000 re B*
+0.900000 0.200000 -0.025000 rg
+171.000000 51.000000 8.000000 8.000000 re B*
+0.950000 0.200000 -0.050000 rg
+181.000000 51.000000 8.000000 8.000000 re B*
+1.000000 0.200000 -0.075000 rg
+191.000000 51.000000 8.000000 8.000000 re B*
+0.050000 0.233333 0.425000 rg
+1.000000 61.000000 8.000000 8.000000 re B*
+0.100000 0.233333 0.400000 rg
+11.000000 61.000000 8.000000 8.000000 re B*
+0.150000 0.233333 0.375000 rg
+21.000000 61.000000 8.000000 8.000000 re B*
+0.200000 0.233333 0.350000 rg
+31.000000 61.000000 8.000000 8.000000 re B*
+0.250000 0.233333 0.325000 rg
+41.000000 61.000000 8.000000 8.000000 re B*
+0.300000 0.233333 0.300000 rg
+51.000000 61.000000 8.000000 8.000000 re B*
+0.350000 0.233333 0.275000 rg
+61.000000 61.000000 8.000000 8.000000 re B*
+0.400000 0.233333 0.250000 rg
+71.000000 61.000000 8.000000 8.000000 re B*
+0.450000 0.233333 0.225000 rg
+81.000000 61.000000 8.000000 8.000000 re B*
+0.500000 0.233333 0.200000 rg
+91.000000 61.000000 8.000000 8.000000 re B*
+0.550000 0.233333 0.175000 rg
+101.000000 61.000000 8.000000 8.000000 re B*
+0.600000 0.233333 0.150000 rg
+111.000000 61.000000 8.000000 8.000000 re B*
+0.650000 0.233333 0.125000 rg
+121.000000 61.000000 8.000000 8.000000 re B*
+0.700000 0.233333 0.100000 rg
+131.000000 61.000000 8.000000 8.000000 re B*
+0.750000 0.233333 0.075000 rg
+141.000000 61.000000 8.000000 8.000000 re B*
+0.800000 0.233333 0.050000 rg
+151.000000 61.000000 8.000000 8.000000 re B*
+0.850000 0.233333 0.025000 rg
+161.000000 61.000000 8.000000 8.000000 re B*
+0.900000 0.233333 0.000000 rg
+171.000000 61.000000 8.000000 8.000000 re B*
+0.950000 0.233333 -0.025000 rg
+181.000000 61.000000 8.000000 8.000000 re B*
+1.000000 0.233333 -0.050000 rg
+191.000000 61.000000 8.000000 8.000000 re B*
+0.050000 0.266667 0.450000 rg
+1.000000 71.000000 8.000000 8.000000 re B*
+0.100000 0.266667 0.425000 rg
+11.000000 71.000000 8.000000 8.000000 re B*
+0.150000 0.266667 0.400000 rg
+21.000000 71.000000 8.000000 8.000000 re B*
+0.200000 0.266667 0.375000 rg
+31.000000 71.000000 8.000000 8.000000 re B*
+0.250000 0.266667 0.350000 rg
+41.000000 71.000000 8.000000 8.000000 re B*
+0.300000 0.266667 0.325000 rg
+51.000000 71.000000 8.000000 8.000000 re B*
+0.350000 0.266667 0.300000 rg
+61.000000 71.000000 8.000000 8.000000 re B*
+0.400000 0.266667 0.275000 rg
+71.000000 71.000000 8.000000 8.000000 re B*
+0.450000 0.266667 0.250000 rg
+81.000000 71.000000 8.000000 8.000000 re B*
+0.500000 0.266667 0.225000 rg
+91.000000 71.000000 8.000000 8.000000 re B*
+0.550000 0.266667 0.200000 rg
+101.000000 71.000000 8.000000 8.000000 re B*
+0.600000 0.266667 0.175000 rg
+111.000000 71.000000 8.000000 8.000000 re B*
+0.650000 0.266667 0.150000 rg
+121.000000 71.000000 8.000000 8.000000 re B*
+0.700000 0.266667 0.125000 rg
+131.000000 71.000000 8.000000 8.000000 re B*
+0.750000 0.266667 0.100000 rg
+141.000000 71.000000 8.000000 8.000000 re B*
+0.800000 0.266667 0.075000 rg
+151.000000 71.000000 8.000000 8.000000 re B*
+0.850000 0.266667 0.050000 rg
+161.000000 71.000000 8.000000 8.000000 re B*
+0.900000 0.266667 0.025000 rg
+171.000000 71.000000 8.000000 8.000000 re B*
+0.950000 0.266667 0.000000 rg
+181.000000 71.000000 8.000000 8.000000 re B*
+1.000000 0.266667 -0.025000 rg
+191.000000 71.000000 8.000000 8.000000 re B*
+0.050000 0.300000 0.475000 rg
+1.000000 81.000000 8.000000 8.000000 re B*
+0.100000 0.300000 0.450000 rg
+11.000000 81.000000 8.000000 8.000000 re B*
+0.150000 0.300000 0.425000 rg
+21.000000 81.000000 8.000000 8.000000 re B*
+0.200000 0.300000 0.400000 rg
+31.000000 81.000000 8.000000 8.000000 re B*
+0.250000 0.300000 0.375000 rg
+41.000000 81.000000 8.000000 8.000000 re B*
+0.300000 0.300000 0.350000 rg
+51.000000 81.000000 8.000000 8.000000 re B*
+0.350000 0.300000 0.325000 rg
+61.000000 81.000000 8.000000 8.000000 re B*
+0.400000 0.300000 0.300000 rg
+71.000000 81.000000 8.000000 8.000000 re B*
+0.450000 0.300000 0.275000 rg
+81.000000 81.000000 8.000000 8.000000 re B*
+0.500000 0.300000 0.250000 rg
+91.000000 81.000000 8.000000 8.000000 re B*
+0.550000 0.300000 0.225000 rg
+101.000000 81.000000 8.000000 8.000000 re B*
+0.600000 0.300000 0.200000 rg
+111.000000 81.000000 8.000000 8.000000 re B*
+0.650000 0.300000 0.175000 rg
+121.000000 81.000000 8.000000 8.000000 re B*
+0.700000 0.300000 0.150000 rg
+131.000000 81.000000 8.000000 8.000000 re B*
+0.750000 0.300000 0.125000 rg
+141.000000 81.000000 8.000000 8.000000 re B*
+0.800000 0.300000 0.100000 rg
+151.000000 81.000000 8.000000 8.000000 re B*
+0.850000 0.300000 0.075000 rg
+161.000000 81.000000 8.000000 8.000000 re B*
+0.900000 0.300000 0.050000 rg
+171.000000 81.000000 8.000000 8.000000 re B*
+0.950000 0.300000 0.025000 rg
+181.000000 81.000000 8.000000 8.000000 re B*
+1.000000 0.300000 0.000000 rg
+191.000000 81.000000 8.000000 8.000000 re B*
+0.050000 0.333333 0.500000 rg
+1.000000 91.000000 8.000000 8.000000 re B*
+0.100000 0.333333 0.475000 rg
+11.000000 91.000000 8.000000 8.000000 re B*
+0.150000 0.333333 0.450000 rg
+21.000000 91.000000 8.000000 8.000000 re B*
+0.200000 0.333333 0.425000 rg
+31.000000 91.000000 8.000000 8.000000 re B*
+0.250000 0.333333 0.400000 rg
+41.000000 91.000000 8.000000 8.000000 re B*
+0.300000 0.333333 0.375000 rg
+51.000000 91.000000 8.000000 8.000000 re B*
+0.350000 0.333333 0.350000 rg
+61.000000 91.000000 8.000000 8.000000 re B*
+0.400000 0.333333 0.325000 rg
+71.000000 91.000000 8.000000 8.000000 re B*
+0.450000 0.333333 0.300000 rg
+81.000000 91.000000 8.000000 8.000000 re B*
+0.500000 0.333333 0.275000 rg
+91.000000 91.000000 8.000000 8.000000 re B*
+0.550000 0.333333 0.250000 rg
+101.000000 91.000000 8.000000 8.000000 re B*
+0.600000 0.333333 0.225000 rg
+111.000000 91.000000 8.000000 8.000000 re B*
+0.650000 0.333333 0.200000 rg
+121.000000 91.000000 8.000000 8.000000 re B*
+0.700000 0.333333 0.175000 rg
+131.000000 91.000000 8.000000 8.000000 re B*
+0.750000 0.333333 0.150000 rg
+141.000000 91.000000 8.000000 8.000000 re B*
+0.800000 0.333333 0.125000 rg
+151.000000 91.000000 8.000000 8.000000 re B*
+0.850000 0.333333 0.100000 rg
+161.000000 91.000000 8.000000 8.000000 re B*
+0.900000 0.333333 0.075000 rg
+171.000000 91.000000 8.000000 8.000000 re B*
+0.950000 0.333333 0.050000 rg
+181.000000 91.000000 8.000000 8.000000 re B*
+1.000000 0.333333 0.025000 rg
+191.000000 91.000000 8.000000 8.000000 re B*
+0.050000 0.366667 0.525000 rg
+1.000000 101.000000 8.000000 8.000000 re B*
+0.100000 0.366667 0.500000 rg
+11.000000 101.000000 8.000000 8.000000 re B*
+0.150000 0.366667 0.475000 rg
+21.000000 101.000000 8.000000 8.000000 re B*
+0.200000 0.366667 0.450000 rg
+31.000000 101.000000 8.000000 8.000000 re B*
+0.250000 0.366667 0.425000 rg
+41.000000 101.000000 8.000000 8.000000 re B*
+0.300000 0.366667 0.400000 rg
+51.000000 101.000000 8.000000 8.000000 re B*
+0.350000 0.366667 0.375000 rg
+61.000000 101.000000 8.000000 8.000000 re B*
+0.400000 0.366667 0.350000 rg
+71.000000 101.000000 8.000000 8.000000 re B*
+0.450000 0.366667 0.325000 rg
+81.000000 101.000000 8.000000 8.000000 re B*
+0.500000 0.366667 0.300000 rg
+91.000000 101.000000 8.000000 8.000000 re B*
+0.550000 0.366667 0.275000 rg
+101.000000 101.000000 8.000000 8.000000 re B*
+0.600000 0.366667 0.250000 rg
+111.000000 101.000000 8.000000 8.000000 re B*
+0.650000 0.366667 0.225000 rg
+121.000000 101.000000 8.000000 8.000000 re B*
+0.700000 0.366667 0.200000 rg
+131.000000 101.000000 8.000000 8.000000 re B*
+0.750000 0.366667 0.175000 rg
+141.000000 101.000000 8.000000 8.000000 re B*
+0.800000 0.366667 0.150000 rg
+151.000000 101.000000 8.000000 8.000000 re B*
+0.850000 0.366667 0.125000 rg
+161.000000 101.000000 8.000000 8.000000 re B*
+0.900000 0.366667 0.100000 rg
+171.000000 101.000000 8.000000 8.000000 re B*
+0.950000 0.366667 0.075000 rg
+181.000000 101.000000 8.000000 8.000000 re B*
+1.000000 0.366667 0.050000 rg
+191.000000 101.000000 8.000000 8.000000 re B*
+0.050000 0.400000 0.550000 rg
+1.000000 111.000000 8.000000 8.000000 re B*
+0.100000 0.400000 0.525000 rg
+11.000000 111.000000 8.000000 8.000000 re B*
+0.150000 0.400000 0.500000 rg
+21.000000 111.000000 8.000000 8.000000 re B*
+0.200000 0.400000 0.475000 rg
+31.000000 111.000000 8.000000 8.000000 re B*
+0.250000 0.400000 0.450000 rg
+41.000000 111.000000 8.000000 8.000000 re B*
+0.300000 0.400000 0.425000 rg
+51.000000 111.000000 8.000000 8.000000 re B*
+0.350000 0.400000 0.400000 rg
+61.000000 111.000000 8.000000 8.000000 re B*
+0.400000 0.400000 0.375000 rg
+71.000000 111.000000 8.000000 8.000000 re B*
+0.450000 0.400000 0.350000 rg
+81.000000 111.000000 8.000000 8.000000 re B*
+0.500000 0.400000 0.325000 rg
+91.000000 111.000000 8.000000 8.000000 re B*
+0.550000 0.400000 0.300000 rg
+101.000000 111.000000 8.000000 8.000000 re B*
+0.600000 0.400000 0.275000 rg
+111.000000 111.000000 8.000000 8.000000 re B*
+0.650000 0.400000 0.250000 rg
+121.000000 111.000000 8.000000 8.000000 re B*
+0.700000 0.400000 0.225000 rg
+131.000000 111.000000 8.000000 8.000000 re B*
+0.750000 0.400000 0.200000 rg
+141.000000 111.000000 8.000000 8.000000 re B*
+0.800000 0.400000 0.175000 rg
+151.000000 111.000000 8.000000 8.000000 re B*
+0.850000 0.400000 0.150000 rg
+161.000000 111.000000 8.000000 8.000000 re B*
+0.900000 0.400000 0.125000 rg
+171.000000 111.000000 8.000000 8.000000 re B*
+0.950000 0.400000 0.100000 rg
+181.000000 111.000000 8.000000 8.000000 re B*
+1.000000 0.400000 0.075000 rg
+191.000000 111.000000 8.000000 8.000000 re B*
+0.050000 0.433333 0.575000 rg
+1.000000 121.000000 8.000000 8.000000 re B*
+0.100000 0.433333 0.550000 rg
+11.000000 121.000000 8.000000 8.000000 re B*
+0.150000 0.433333 0.525000 rg
+21.000000 121.000000 8.000000 8.000000 re B*
+0.200000 0.433333 0.500000 rg
+31.000000 121.000000 8.000000 8.000000 re B*
+0.250000 0.433333 0.475000 rg
+41.000000 121.000000 8.000000 8.000000 re B*
+0.300000 0.433333 0.450000 rg
+51.000000 121.000000 8.000000 8.000000 re B*
+0.350000 0.433333 0.425000 rg
+61.000000 121.000000 8.000000 8.000000 re B*
+0.400000 0.433333 0.400000 rg
+71.000000 121.000000 8.000000 8.000000 re B*
+0.450000 0.433333 0.375000 rg
+81.000000 121.000000 8.000000 8.000000 re B*
+0.500000 0.433333 0.350000 rg
+91.000000 121.000000 8.000000 8.000000 re B*
+0.550000 0.433333 0.325000 rg
+101.000000 121.000000 8.000000 8.000000 re B*
+0.600000 0.433333 0.300000 rg
+111.000000 121.000000 8.000000 8.000000 re B*
+0.650000 0.433333 0.275000 rg
+121.000000 121.000000 8.000000 8.000000 re B*
+0.700000 0.433333 0.250000 rg
+131.000000 121.000000 8.000000 8.000000 re B*
+0.750000 0.433333 0.225000 rg
+141.000000 121.000000 8.000000 8.000000 re B*
+0.800000 0.433333 0.200000 rg
+151.000000 121.000000 8.000000 8.000000 re B*
+0.850000 0.433333 0.175000 rg
+161.000000 121.000000 8.000000 8.000000 re B*
+0.900000 0.433333 0.150000 rg
+171.000000 121.000000 8.000000 8.000000 re B*
+0.950000 0.433333 0.125000 rg
+181.000000 121.000000 8.000000 8.000000 re B*
+1.000000 0.433333 0.100000 rg
+191.000000 121.000000 8.000000 8.000000 re B*
+0.050000 0.466667 0.600000 rg
+1.000000 131.000000 8.000000 8.000000 re B*
+0.100000 0.466667 0.575000 rg
+11.000000 131.000000 8.000000 8.000000 re B*
+0.150000 0.466667 0.550000 rg
+21.000000 131.000000 8.000000 8.000000 re B*
+0.200000 0.466667 0.525000 rg
+31.000000 131.000000 8.000000 8.000000 re B*
+0.250000 0.466667 0.500000 rg
+41.000000 131.000000 8.000000 8.000000 re B*
+0.300000 0.466667 0.475000 rg
+51.000000 131.000000 8.000000 8.000000 re B*
+0.350000 0.466667 0.450000 rg
+61.000000 131.000000 8.000000 8.000000 re B*
+0.400000 0.466667 0.425000 rg
+71.000000 131.000000 8.000000 8.000000 re B*
+0.450000 0.466667 0.400000 rg
+81.000000 131.000000 8.000000 8.000000 re B*
+0.500000 0.466667 0.375000 rg
+91.000000 131.000000 8.000000 8.000000 re B*
+0.550000 0.466667 0.350000 rg
+101.000000 131.000000 8.000000 8.000000 re B*
+0.600000 0.466667 0.325000 rg
+111.000000 131.000000 8.000000 8.000000 re B*
+0.650000 0.466667 0.300000 rg
+121.000000 131.000000 8.000000 8.000000 re B*
+0.700000 0.466667 0.275000 rg
+131.000000 131.000000 8.000000 8.000000 re B*
+0.750000 0.466667 0.250000 rg
+141.000000 131.000000 8.000000 8.000000 re B*
+0.800000 0.466667 0.225000 rg
+151.000000 131.000000 8.000000 8.000000 re B*
+0.850000 0.466667 0.200000 rg
+161.000000 131.000000 8.000000 8.000000 re B*
+0.900000 0.466667 0.175000 rg
+171.000000 131.000000 8.000000 8.000000 re B*
+0.950000 0.466667 0.150000 rg
+181.000000 131.000000 8.000000 8.000000 re B*
+1.000000 0.466667 0.125000 rg
+191.000000 131.000000 8.000000 8.000000 re B*
+0.050000 0.500000 0.625000 rg
+1.000000 141.000000 8.000000 8.000000 re B*
+0.100000 0.500000 0.600000 rg
+11.000000 141.000000 8.000000 8.000000 re B*
+0.150000 0.500000 0.575000 rg
+21.000000 141.000000 8.000000 8.000000 re B*
+0.200000 0.500000 0.550000 rg
+31.000000 141.000000 8.000000 8.000000 re B*
+0.250000 0.500000 0.525000 rg
+41.000000 141.000000 8.000000 8.000000 re B*
+0.300000 0.500000 0.500000 rg
+51.000000 141.000000 8.000000 8.000000 re B*
+0.350000 0.500000 0.475000 rg
+61.000000 141.000000 8.000000 8.000000 re B*
+0.400000 0.500000 0.450000 rg
+71.000000 141.000000 8.000000 8.000000 re B*
+0.450000 0.500000 0.425000 rg
+81.000000 141.000000 8.000000 8.000000 re B*
+0.500000 0.500000 0.400000 rg
+91.000000 141.000000 8.000000 8.000000 re B*
+0.550000 0.500000 0.375000 rg
+101.000000 141.000000 8.000000 8.000000 re B*
+0.600000 0.500000 0.350000 rg
+111.000000 141.000000 8.000000 8.000000 re B*
+0.650000 0.500000 0.325000 rg
+121.000000 141.000000 8.000000 8.000000 re B*
+0.700000 0.500000 0.300000 rg
+131.000000 141.000000 8.000000 8.000000 re B*
+0.750000 0.500000 0.275000 rg
+141.000000 141.000000 8.000000 8.000000 re B*
+0.800000 0.500000 0.250000 rg
+151.000000 141.000000 8.000000 8.000000 re B*
+0.850000 0.500000 0.225000 rg
+161.000000 141.000000 8.000000 8.000000 re B*
+0.900000 0.500000 0.200000 rg
+171.000000 141.000000 8.000000 8.000000 re B*
+0.950000 0.500000 0.175000 rg
+181.000000 141.000000 8.000000 8.000000 re B*
+1.000000 0.500000 0.150000 rg
+191.000000 141.000000 8.000000 8.000000 re B*
+0.050000 0.533333 0.650000 rg
+1.000000 151.000000 8.000000 8.000000 re B*
+0.100000 0.533333 0.625000 rg
+11.000000 151.000000 8.000000 8.000000 re B*
+0.150000 0.533333 0.600000 rg
+21.000000 151.000000 8.000000 8.000000 re B*
+0.200000 0.533333 0.575000 rg
+31.000000 151.000000 8.000000 8.000000 re B*
+0.250000 0.533333 0.550000 rg
+41.000000 151.000000 8.000000 8.000000 re B*
+0.300000 0.533333 0.525000 rg
+51.000000 151.000000 8.000000 8.000000 re B*
+0.350000 0.533333 0.500000 rg
+61.000000 151.000000 8.000000 8.000000 re B*
+0.400000 0.533333 0.475000 rg
+71.000000 151.000000 8.000000 8.000000 re B*
+0.450000 0.533333 0.450000 rg
+81.000000 151.000000 8.000000 8.000000 re B*
+0.500000 0.533333 0.425000 rg
+91.000000 151.000000 8.000000 8.000000 re B*
+0.550000 0.533333 0.400000 rg
+101.000000 151.000000 8.000000 8.000000 re B*
+0.600000 0.533333 0.375000 rg
+111.000000 151.000000 8.000000 8.000000 re B*
+0.650000 0.533333 0.350000 rg
+121.000000 151.000000 8.000000 8.000000 re B*
+0.700000 0.533333 0.325000 rg
+131.000000 151.000000 8.000000 8.000000 re B*
+0.750000 0.533333 0.300000 rg
+141.000000 151.000000 8.000000 8.000000 re B*
+0.800000 0.533333 0.275000 rg
+151.000000 151.000000 8.000000 8.000000 re B*
+0.850000 0.533333 0.250000 rg
+161.000000 151.000000 8.000000 8.000000 re B*
+0.900000 0.533333 0.225000 rg
+171.000000 151.000000 8.000000 8.000000 re B*
+0.950000 0.533333 0.200000 rg
+181.000000 151.000000 8.000000 8.000000 re B*
+1.000000 0.533333 0.175000 rg
+191.000000 151.000000 8.000000 8.000000 re B*
+0.050000 0.566667 0.675000 rg
+1.000000 161.000000 8.000000 8.000000 re B*
+0.100000 0.566667 0.650000 rg
+11.000000 161.000000 8.000000 8.000000 re B*
+0.150000 0.566667 0.625000 rg
+21.000000 161.000000 8.000000 8.000000 re B*
+0.200000 0.566667 0.600000 rg
+31.000000 161.000000 8.000000 8.000000 re B*
+0.250000 0.566667 0.575000 rg
+41.000000 161.000000 8.000000 8.000000 re B*
+0.300000 0.566667 0.550000 rg
+51.000000 161.000000 8.000000 8.000000 re B*
+0.350000 0.566667 0.525000 rg
+61.000000 161.000000 8.000000 8.000000 re B*
+0.400000 0.566667 0.500000 rg
+71.000000 161.000000 8.000000 8.000000 re B*
+0.450000 0.566667 0.475000 rg
+81.000000 161.000000 8.000000 8.000000 re B*
+0.500000 0.566667 0.450000 rg
+91.000000 161.000000 8.000000 8.000000 re B*
+0.550000 0.566667 0.425000 rg
+101.000000 161.000000 8.000000 8.000000 re B*
+0.600000 0.566667 0.400000 rg
+111.000000 161.000000 8.000000 8.000000 re B*
+0.650000 0.566667 0.375000 rg
+121.000000 161.000000 8.000000 8.000000 re B*
+0.700000 0.566667 0.350000 rg
+131.000000 161.000000 8.000000 8.000000 re B*
+0.750000 0.566667 0.325000 rg
+141.000000 161.000000 8.000000 8.000000 re B*
+0.800000 0.566667 0.300000 rg
+151.000000 161.000000 8.000000 8.000000 re B*
+0.850000 0.566667 0.275000 rg
+161.000000 161.000000 8.000000 8.000000 re B*
+0.900000 0.566667 0.250000 rg
+171.000000 161.000000 8.000000 8.000000 re B*
+0.950000 0.566667 0.225000 rg
+181.000000 161.000000 8.000000 8.000000 re B*
+1.000000 0.566667 0.200000 rg
+191.000000 161.000000 8.000000 8.000000 re B*
+0.050000 0.600000 0.700000 rg
+1.000000 171.000000 8.000000 8.000000 re B*
+0.100000 0.600000 0.675000 rg
+11.000000 171.000000 8.000000 8.000000 re B*
+0.150000 0.600000 0.650000 rg
+21.000000 171.000000 8.000000 8.000000 re B*
+0.200000 0.600000 0.625000 rg
+31.000000 171.000000 8.000000 8.000000 re B*
+0.250000 0.600000 0.600000 rg
+41.000000 171.000000 8.000000 8.000000 re B*
+0.300000 0.600000 0.575000 rg
+51.000000 171.000000 8.000000 8.000000 re B*
+0.350000 0.600000 0.550000 rg
+61.000000 171.000000 8.000000 8.000000 re B*
+0.400000 0.600000 0.525000 rg
+71.000000 171.000000 8.000000 8.000000 re B*
+0.450000 0.600000 0.500000 rg
+81.000000 171.000000 8.000000 8.000000 re B*
+0.500000 0.600000 0.475000 rg
+91.000000 171.000000 8.000000 8.000000 re B*
+0.550000 0.600000 0.450000 rg
+101.000000 171.000000 8.000000 8.000000 re B*
+0.600000 0.600000 0.425000 rg
+111.000000 171.000000 8.000000 8.000000 re B*
+0.650000 0.600000 0.400000 rg
+121.000000 171.000000 8.000000 8.000000 re B*
+0.700000 0.600000 0.375000 rg
+131.000000 171.000000 8.000000 8.000000 re B*
+0.750000 0.600000 0.350000 rg
+141.000000 171.000000 8.000000 8.000000 re B*
+0.800000 0.600000 0.325000 rg
+151.000000 171.000000 8.000000 8.000000 re B*
+0.850000 0.600000 0.300000 rg
+161.000000 171.000000 8.000000 8.000000 re B*
+0.900000 0.600000 0.275000 rg
+171.000000 171.000000 8.000000 8.000000 re B*
+0.950000 0.600000 0.250000 rg
+181.000000 171.000000 8.000000 8.000000 re B*
+1.000000 0.600000 0.225000 rg
+191.000000 171.000000 8.000000 8.000000 re B*
+0.050000 0.633333 0.725000 rg
+1.000000 181.000000 8.000000 8.000000 re B*
+0.100000 0.633333 0.700000 rg
+11.000000 181.000000 8.000000 8.000000 re B*
+0.150000 0.633333 0.675000 rg
+21.000000 181.000000 8.000000 8.000000 re B*
+0.200000 0.633333 0.650000 rg
+31.000000 181.000000 8.000000 8.000000 re B*
+0.250000 0.633333 0.625000 rg
+41.000000 181.000000 8.000000 8.000000 re B*
+0.300000 0.633333 0.600000 rg
+51.000000 181.000000 8.000000 8.000000 re B*
+0.350000 0.633333 0.575000 rg
+61.000000 181.000000 8.000000 8.000000 re B*
+0.400000 0.633333 0.550000 rg
+71.000000 181.000000 8.000000 8.000000 re B*
+0.450000 0.633333 0.525000 rg
+81.000000 181.000000 8.000000 8.000000 re B*
+0.500000 0.633333 0.500000 rg
+91.000000 181.000000 8.000000 8.000000 re B*
+0.550000 0.633333 0.475000 rg
+101.000000 181.000000 8.000000 8.000000 re B*
+0.600000 0.633333 0.450000 rg
+111.000000 181.000000 8.000000 8.000000 re B*
+0.650000 0.633333 0.425000 rg
+121.000000 181.000000 8.000000 8.000000 re B*
+0.700000 0.633333 0.400000 rg
+131.000000 181.000000 8.000000 8.000000 re B*
+0.750000 0.633333 0.375000 rg
+141.000000 181.000000 8.000000 8.000000 re B*
+0.800000 0.633333 0.350000 rg
+151.000000 181.000000 8.000000 8.000000 re B*
+0.850000 0.633333 0.325000 rg
+161.000000 181.000000 8.000000 8.000000 re B*
+0.900000 0.633333 0.300000 rg
+171.000000 181.000000 8.000000 8.000000 re B*
+0.950000 0.633333 0.275000 rg
+181.000000 181.000000 8.000000 8.000000 re B*
+1.000000 0.633333 0.250000 rg
+191.000000 181.000000 8.000000 8.000000 re B*
+0.050000 0.666667 0.750000 rg
+1.000000 191.000000 8.000000 8.000000 re B*
+0.100000 0.666667 0.725000 rg
+11.000000 191.000000 8.000000 8.000000 re B*
+0.150000 0.666667 0.700000 rg
+21.000000 191.000000 8.000000 8.000000 re B*
+0.200000 0.666667 0.675000 rg
+31.000000 191.000000 8.000000 8.000000 re B*
+0.250000 0.666667 0.650000 rg
+41.000000 191.000000 8.000000 8.000000 re B*
+0.300000 0.666667 0.625000 rg
+51.000000 191.000000 8.000000 8.000000 re B*
+0.350000 0.666667 0.600000 rg
+61.000000 191.000000 8.000000 8.000000 re B*
+0.400000 0.666667 0.575000 rg
+71.000000 191.000000 8.000000 8.000000 re B*
+0.450000 0.666667 0.550000 rg
+81.000000 191.000000 8.000000 8.000000 re B*
+0.500000 0.666667 0.525000 rg
+91.000000 191.000000 8.000000 8.000000 re B*
+0.550000 0.666667 0.500000 rg
+101.000000 191.000000 8.000000 8.000000 re B*
+0.600000 0.666667 0.475000 rg
+111.000000 191.000000 8.000000 8.000000 re B*
+0.650000 0.666667 0.450000 rg
+121.000000 191.000000 8.000000 8.000000 re B*
+0.700000 0.666667 0.425000 rg
+131.000000 191.000000 8.000000 8.000000 re B*
+0.750000 0.666667 0.400000 rg
+141.000000 191.000000 8.000000 8.000000 re B*
+0.800000 0.666667 0.375000 rg
+151.000000 191.000000 8.000000 8.000000 re B*
+0.850000 0.666667 0.350000 rg
+161.000000 191.000000 8.000000 8.000000 re B*
+0.900000 0.666667 0.325000 rg
+171.000000 191.000000 8.000000 8.000000 re B*
+0.950000 0.666667 0.300000 rg
+181.000000 191.000000 8.000000 8.000000 re B*
+1.000000 0.666667 0.275000 rg
+191.000000 191.000000 8.000000 8.000000 re B*
+0.050000 0.700000 0.775000 rg
+1.000000 201.000000 8.000000 8.000000 re B*
+0.100000 0.700000 0.750000 rg
+11.000000 201.000000 8.000000 8.000000 re B*
+0.150000 0.700000 0.725000 rg
+21.000000 201.000000 8.000000 8.000000 re B*
+0.200000 0.700000 0.700000 rg
+31.000000 201.000000 8.000000 8.000000 re B*
+0.250000 0.700000 0.675000 rg
+41.000000 201.000000 8.000000 8.000000 re B*
+0.300000 0.700000 0.650000 rg
+51.000000 201.000000 8.000000 8.000000 re B*
+0.350000 0.700000 0.625000 rg
+61.000000 201.000000 8.000000 8.000000 re B*
+0.400000 0.700000 0.600000 rg
+71.000000 201.000000 8.000000 8.000000 re B*
+0.450000 0.700000 0.575000 rg
+81.000000 201.000000 8.000000 8.000000 re B*
+0.500000 0.700000 0.550000 rg
+91.000000 201.000000 8.000000 8.000000 re B*
+0.550000 0.700000 0.525000 rg
+101.000000 201.000000 8.000000 8.000000 re B*
+0.600000 0.700000 0.500000 rg
+111.000000 201.000000 8.000000 8.000000 re B*
+0.650000 0.700000 0.475000 rg
+121.000000 201.000000 8.000000 8.000000 re B*
+0.700000 0.700000 0.450000 rg
+131.000000 201.000000 8.000000 8.000000 re B*
+0.750000 0.700000 0.425000 rg
+141.000000 201.000000 8.000000 8.000000 re B*
+0.800000 0.700000 0.400000 rg
+151.000000 201.000000 8.000000 8.000000 re B*
+0.850000 0.700000 0.375000 rg
+161.000000 201.000000 8.000000 8.000000 re B*
+0.900000 0.700000 0.350000 rg
+171.000000 201.000000 8.000000 8.000000 re B*
+0.950000 0.700000 0.325000 rg
+181.000000 201.000000 8.000000 8.000000 re B*
+1.000000 0.700000 0.300000 rg
+191.000000 201.000000 8.000000 8.000000 re B*
+0.050000 0.733333 0.800000 rg
+1.000000 211.000000 8.000000 8.000000 re B*
+0.100000 0.733333 0.775000 rg
+11.000000 211.000000 8.000000 8.000000 re B*
+0.150000 0.733333 0.750000 rg
+21.000000 211.000000 8.000000 8.000000 re B*
+0.200000 0.733333 0.725000 rg
+31.000000 211.000000 8.000000 8.000000 re B*
+0.250000 0.733333 0.700000 rg
+41.000000 211.000000 8.000000 8.000000 re B*
+0.300000 0.733333 0.675000 rg
+51.000000 211.000000 8.000000 8.000000 re B*
+0.350000 0.733333 0.650000 rg
+61.000000 211.000000 8.000000 8.000000 re B*
+0.400000 0.733333 0.625000 rg
+71.000000 211.000000 8.000000 8.000000 re B*
+0.450000 0.733333 0.600000 rg
+81.000000 211.000000 8.000000 8.000000 re B*
+0.500000 0.733333 0.575000 rg
+91.000000 211.000000 8.000000 8.000000 re B*
+0.550000 0.733333 0.550000 rg
+101.000000 211.000000 8.000000 8.000000 re B*
+0.600000 0.733333 0.525000 rg
+111.000000 211.000000 8.000000 8.000000 re B*
+0.650000 0.733333 0.500000 rg
+121.000000 211.000000 8.000000 8.000000 re B*
+0.700000 0.733333 0.475000 rg
+131.000000 211.000000 8.000000 8.000000 re B*
+0.750000 0.733333 0.450000 rg
+141.000000 211.000000 8.000000 8.000000 re B*
+0.800000 0.733333 0.425000 rg
+151.000000 211.000000 8.000000 8.000000 re B*
+0.850000 0.733333 0.400000 rg
+161.000000 211.000000 8.000000 8.000000 re B*
+0.900000 0.733333 0.375000 rg
+171.000000 211.000000 8.000000 8.000000 re B*
+0.950000 0.733333 0.350000 rg
+181.000000 211.000000 8.000000 8.000000 re B*
+1.000000 0.733333 0.325000 rg
+191.000000 211.000000 8.000000 8.000000 re B*
+0.050000 0.766667 0.825000 rg
+1.000000 221.000000 8.000000 8.000000 re B*
+0.100000 0.766667 0.800000 rg
+11.000000 221.000000 8.000000 8.000000 re B*
+0.150000 0.766667 0.775000 rg
+21.000000 221.000000 8.000000 8.000000 re B*
+0.200000 0.766667 0.750000 rg
+31.000000 221.000000 8.000000 8.000000 re B*
+0.250000 0.766667 0.725000 rg
+41.000000 221.000000 8.000000 8.000000 re B*
+0.300000 0.766667 0.700000 rg
+51.000000 221.000000 8.000000 8.000000 re B*
+0.350000 0.766667 0.675000 rg
+61.000000 221.000000 8.000000 8.000000 re B*
+0.400000 0.766667 0.650000 rg
+71.000000 221.000000 8.000000 8.000000 re B*
+0.450000 0.766667 0.625000 rg
+81.000000 221.000000 8.000000 8.000000 re B*
+0.500000 0.766667 0.600000 rg
+91.000000 221.000000 8.000000 8.000000 re B*
+0.550000 0.766667 0.575000 rg
+101.000000 221.000000 8.000000 8.000000 re B*
+0.600000 0.766667 0.550000 rg
+111.000000 221.000000 8.000000 8.000000 re B*
+0.650000 0.766667 0.525000 rg
+121.000000 221.000000 8.000000 8.000000 re B*
+0.700000 0.766667 0.500000 rg
+131.000000 221.000000 8.000000 8.000000 re B*
+0.750000 0.766667 0.475000 rg
+141.000000 221.000000 8.000000 8.000000 re B*
+0.800000 0.766667 0.450000 rg
+151.000000 221.000000 8.000000 8.000000 re B*
+0.850000 0.766667 0.425000 rg
+161.000000 221.000000 8.000000 8.000000 re B*
+0.900000 0.766667 0.400000 rg
+171.000000 221.000000 8.000000 8.000000 re B*
+0.950000 0.766667 0.375000 rg
+181.000000 221.000000 8.000000 8.000000 re B*
+1.000000 0.766667 0.350000 rg
+191.000000 221.000000 8.000000 8.000000 re B*
+0.050000 0.800000 0.850000 rg
+1.000000 231.000000 8.000000 8.000000 re B*
+0.100000 0.800000 0.825000 rg
+11.000000 231.000000 8.000000 8.000000 re B*
+0.150000 0.800000 0.800000 rg
+21.000000 231.000000 8.000000 8.000000 re B*
+0.200000 0.800000 0.775000 rg
+31.000000 231.000000 8.000000 8.000000 re B*
+0.250000 0.800000 0.750000 rg
+41.000000 231.000000 8.000000 8.000000 re B*
+0.300000 0.800000 0.725000 rg
+51.000000 231.000000 8.000000 8.000000 re B*
+0.350000 0.800000 0.700000 rg
+61.000000 231.000000 8.000000 8.000000 re B*
+0.400000 0.800000 0.675000 rg
+71.000000 231.000000 8.000000 8.000000 re B*
+0.450000 0.800000 0.650000 rg
+81.000000 231.000000 8.000000 8.000000 re B*
+0.500000 0.800000 0.625000 rg
+91.000000 231.000000 8.000000 8.000000 re B*
+0.550000 0.800000 0.600000 rg
+101.000000 231.000000 8.000000 8.000000 re B*
+0.600000 0.800000 0.575000 rg
+111.000000 231.000000 8.000000 8.000000 re B*
+0.650000 0.800000 0.550000 rg
+121.000000 231.000000 8.000000 8.000000 re B*
+0.700000 0.800000 0.525000 rg
+131.000000 231.000000 8.000000 8.000000 re B*
+0.750000 0.800000 0.500000 rg
+141.000000 231.000000 8.000000 8.000000 re B*
+0.800000 0.800000 0.475000 rg
+151.000000 231.000000 8.000000 8.000000 re B*
+0.850000 0.800000 0.450000 rg
+161.000000 231.000000 8.000000 8.000000 re B*
+0.900000 0.800000 0.425000 rg
+171.000000 231.000000 8.000000 8.000000 re B*
+0.950000 0.800000 0.400000 rg
+181.000000 231.000000 8.000000 8.000000 re B*
+1.000000 0.800000 0.375000 rg
+191.000000 231.000000 8.000000 8.000000 re B*
+0.050000 0.833333 0.875000 rg
+1.000000 241.000000 8.000000 8.000000 re B*
+0.100000 0.833333 0.850000 rg
+11.000000 241.000000 8.000000 8.000000 re B*
+0.150000 0.833333 0.825000 rg
+21.000000 241.000000 8.000000 8.000000 re B*
+0.200000 0.833333 0.800000 rg
+31.000000 241.000000 8.000000 8.000000 re B*
+0.250000 0.833333 0.775000 rg
+41.000000 241.000000 8.000000 8.000000 re B*
+0.300000 0.833333 0.750000 rg
+51.000000 241.000000 8.000000 8.000000 re B*
+0.350000 0.833333 0.725000 rg
+61.000000 241.000000 8.000000 8.000000 re B*
+0.400000 0.833333 0.700000 rg
+71.000000 241.000000 8.000000 8.000000 re B*
+0.450000 0.833333 0.675000 rg
+81.000000 241.000000 8.000000 8.000000 re B*
+0.500000 0.833333 0.650000 rg
+91.000000 241.000000 8.000000 8.000000 re B*
+0.550000 0.833333 0.625000 rg
+101.000000 241.000000 8.000000 8.000000 re B*
+0.600000 0.833333 0.600000 rg
+111.000000 241.000000 8.000000 8.000000 re B*
+0.650000 0.833333 0.575000 rg
+121.000000 241.000000 8.000000 8.000000 re B*
+0.700000 0.833333 0.550000 rg
+131.000000 241.000000 8.000000 8.000000 re B*
+0.750000 0.833333 0.525000 rg
+141.000000 241.000000 8.000000 8.000000 re B*
+0.800000 0.833333 0.500000 rg
+151.000000 241.000000 8.000000 8.000000 re B*
+0.850000 0.833333 0.475000 rg
+161.000000 241.000000 8.000000 8.000000 re B*
+0.900000 0.833333 0.450000 rg
+171.000000 241.000000 8.000000 8.000000 re B*
+0.950000 0.833333 0.425000 rg
+181.000000 241.000000 8.000000 8.000000 re B*
+1.000000 0.833333 0.400000 rg
+191.000000 241.000000 8.000000 8.000000 re B*
+0.050000 0.866667 0.900000 rg
+1.000000 251.000000 8.000000 8.000000 re B*
+0.100000 0.866667 0.875000 rg
+11.000000 251.000000 8.000000 8.000000 re B*
+0.150000 0.866667 0.850000 rg
+21.000000 251.000000 8.000000 8.000000 re B*
+0.200000 0.866667 0.825000 rg
+31.000000 251.000000 8.000000 8.000000 re B*
+0.250000 0.866667 0.800000 rg
+41.000000 251.000000 8.000000 8.000000 re B*
+0.300000 0.866667 0.775000 rg
+51.000000 251.000000 8.000000 8.000000 re B*
+0.350000 0.866667 0.750000 rg
+61.000000 251.000000 8.000000 8.000000 re B*
+0.400000 0.866667 0.725000 rg
+71.000000 251.000000 8.000000 8.000000 re B*
+0.450000 0.866667 0.700000 rg
+81.000000 251.000000 8.000000 8.000000 re B*
+0.500000 0.866667 0.675000 rg
+91.000000 251.000000 8.000000 8.000000 re B*
+0.550000 0.866667 0.650000 rg
+101.000000 251.000000 8.000000 8.000000 re B*
+0.600000 0.866667 0.625000 rg
+111.000000 251.000000 8.000000 8.000000 re B*
+0.650000 0.866667 0.600000 rg
+121.000000 251.000000 8.000000 8.000000 re B*
+0.700000 0.866667 0.575000 rg
+131.000000 251.000000 8.000000 8.000000 re B*
+0.750000 0.866667 0.550000 rg
+141.000000 251.000000 8.000000 8.000000 re B*
+0.800000 0.866667 0.525000 rg
+151.000000 251.000000 8.000000 8.000000 re B*
+0.850000 0.866667 0.500000 rg
+161.000000 251.000000 8.000000 8.000000 re B*
+0.900000 0.866667 0.475000 rg
+171.000000 251.000000 8.000000 8.000000 re B*
+0.950000 0.866667 0.450000 rg
+181.000000 251.000000 8.000000 8.000000 re B*
+1.000000 0.866667 0.425000 rg
+191.000000 251.000000 8.000000 8.000000 re B*
+0.050000 0.900000 0.925000 rg
+1.000000 261.000000 8.000000 8.000000 re B*
+0.100000 0.900000 0.900000 rg
+11.000000 261.000000 8.000000 8.000000 re B*
+0.150000 0.900000 0.875000 rg
+21.000000 261.000000 8.000000 8.000000 re B*
+0.200000 0.900000 0.850000 rg
+31.000000 261.000000 8.000000 8.000000 re B*
+0.250000 0.900000 0.825000 rg
+41.000000 261.000000 8.000000 8.000000 re B*
+0.300000 0.900000 0.800000 rg
+51.000000 261.000000 8.000000 8.000000 re B*
+0.350000 0.900000 0.775000 rg
+61.000000 261.000000 8.000000 8.000000 re B*
+0.400000 0.900000 0.750000 rg
+71.000000 261.000000 8.000000 8.000000 re B*
+0.450000 0.900000 0.725000 rg
+81.000000 261.000000 8.000000 8.000000 re B*
+0.500000 0.900000 0.700000 rg
+91.000000 261.000000 8.000000 8.000000 re B*
+0.550000 0.900000 0.675000 rg
+101.000000 261.000000 8.000000 8.000000 re B*
+0.600000 0.900000 0.650000 rg
+111.000000 261.000000 8.000000 8.000000 re B*
+0.650000 0.900000 0.625000 rg
+121.000000 261.000000 8.000000 8.000000 re B*
+0.700000 0.900000 0.600000 rg
+131.000000 261.000000 8.000000 8.000000 re B*
+0.750000 0.900000 0.575000 rg
+141.000000 261.000000 8.000000 8.000000 re B*
+0.800000 0.900000 0.550000 rg
+151.000000 261.000000 8.000000 8.000000 re B*
+0.850000 0.900000 0.525000 rg
+161.000000 261.000000 8.000000 8.000000 re B*
+0.900000 0.900000 0.500000 rg
+171.000000 261.000000 8.000000 8.000000 re B*
+0.950000 0.900000 0.475000 rg
+181.000000 261.000000 8.000000 8.000000 re B*
+1.000000 0.900000 0.450000 rg
+191.000000 261.000000 8.000000 8.000000 re B*
+0.050000 0.933333 0.950000 rg
+1.000000 271.000000 8.000000 8.000000 re B*
+0.100000 0.933333 0.925000 rg
+11.000000 271.000000 8.000000 8.000000 re B*
+0.150000 0.933333 0.900000 rg
+21.000000 271.000000 8.000000 8.000000 re B*
+0.200000 0.933333 0.875000 rg
+31.000000 271.000000 8.000000 8.000000 re B*
+0.250000 0.933333 0.850000 rg
+41.000000 271.000000 8.000000 8.000000 re B*
+0.300000 0.933333 0.825000 rg
+51.000000 271.000000 8.000000 8.000000 re B*
+0.350000 0.933333 0.800000 rg
+61.000000 271.000000 8.000000 8.000000 re B*
+0.400000 0.933333 0.775000 rg
+71.000000 271.000000 8.000000 8.000000 re B*
+0.450000 0.933333 0.750000 rg
+81.000000 271.000000 8.000000 8.000000 re B*
+0.500000 0.933333 0.725000 rg
+91.000000 271.000000 8.000000 8.000000 re B*
+0.550000 0.933333 0.700000 rg
+101.000000 271.000000 8.000000 8.000000 re B*
+0.600000 0.933333 0.675000 rg
+111.000000 271.000000 8.000000 8.000000 re B*
+0.650000 0.933333 0.650000 rg
+121.000000 271.000000 8.000000 8.000000 re B*
+0.700000 0.933333 0.625000 rg
+131.000000 271.000000 8.000000 8.000000 re B*
+0.750000 0.933333 0.600000 rg
+141.000000 271.000000 8.000000 8.000000 re B*
+0.800000 0.933333 0.575000 rg
+151.000000 271.000000 8.000000 8.000000 re B*
+0.850000 0.933333 0.550000 rg
+161.000000 271.000000 8.000000 8.000000 re B*
+0.900000 0.933333 0.525000 rg
+171.000000 271.000000 8.000000 8.000000 re B*
+0.950000 0.933333 0.500000 rg
+181.000000 271.000000 8.000000 8.000000 re B*
+1.000000 0.933333 0.475000 rg
+191.000000 271.000000 8.000000 8.000000 re B*
+0.050000 0.966667 0.975000 rg
+1.000000 281.000000 8.000000 8.000000 re B*
+0.100000 0.966667 0.950000 rg
+11.000000 281.000000 8.000000 8.000000 re B*
+0.150000 0.966667 0.925000 rg
+21.000000 281.000000 8.000000 8.000000 re B*
+0.200000 0.966667 0.900000 rg
+31.000000 281.000000 8.000000 8.000000 re B*
+0.250000 0.966667 0.875000 rg
+41.000000 281.000000 8.000000 8.000000 re B*
+0.300000 0.966667 0.850000 rg
+51.000000 281.000000 8.000000 8.000000 re B*
+0.350000 0.966667 0.825000 rg
+61.000000 281.000000 8.000000 8.000000 re B*
+0.400000 0.966667 0.800000 rg
+71.000000 281.000000 8.000000 8.000000 re B*
+0.450000 0.966667 0.775000 rg
+81.000000 281.000000 8.000000 8.000000 re B*
+0.500000 0.966667 0.750000 rg
+91.000000 281.000000 8.000000 8.000000 re B*
+0.550000 0.966667 0.725000 rg
+101.000000 281.000000 8.000000 8.000000 re B*
+0.600000 0.966667 0.700000 rg
+111.000000 281.000000 8.000000 8.000000 re B*
+0.650000 0.966667 0.675000 rg
+121.000000 281.000000 8.000000 8.000000 re B*
+0.700000 0.966667 0.650000 rg
+131.000000 281.000000 8.000000 8.000000 re B*
+0.750000 0.966667 0.625000 rg
+141.000000 281.000000 8.000000 8.000000 re B*
+0.800000 0.966667 0.600000 rg
+151.000000 281.000000 8.000000 8.000000 re B*
+0.850000 0.966667 0.575000 rg
+161.000000 281.000000 8.000000 8.000000 re B*
+0.900000 0.966667 0.550000 rg
+171.000000 281.000000 8.000000 8.000000 re B*
+0.950000 0.966667 0.525000 rg
+181.000000 281.000000 8.000000 8.000000 re B*
+1.000000 0.966667 0.500000 rg
+191.000000 281.000000 8.000000 8.000000 re B*
+0.050000 1.000000 1.000000 rg
+1.000000 291.000000 8.000000 8.000000 re B*
+0.100000 1.000000 0.975000 rg
+11.000000 291.000000 8.000000 8.000000 re B*
+0.150000 1.000000 0.950000 rg
+21.000000 291.000000 8.000000 8.000000 re B*
+0.200000 1.000000 0.925000 rg
+31.000000 291.000000 8.000000 8.000000 re B*
+0.250000 1.000000 0.900000 rg
+41.000000 291.000000 8.000000 8.000000 re B*
+0.300000 1.000000 0.875000 rg
+51.000000 291.000000 8.000000 8.000000 re B*
+0.350000 1.000000 0.850000 rg
+61.000000 291.000000 8.000000 8.000000 re B*
+0.400000 1.000000 0.825000 rg
+71.000000 291.000000 8.000000 8.000000 re B*
+0.450000 1.000000 0.800000 rg
+81.000000 291.000000 8.000000 8.000000 re B*
+0.500000 1.000000 0.775000 rg
+91.000000 291.000000 8.000000 8.000000 re B*
+0.550000 1.000000 0.750000 rg
+101.000000 291.000000 8.000000 8.000000 re B*
+0.600000 1.000000 0.725000 rg
+111.000000 291.000000 8.000000 8.000000 re B*
+0.650000 1.000000 0.700000 rg
+121.000000 291.000000 8.000000 8.000000 re B*
+0.700000 1.000000 0.675000 rg
+131.000000 291.000000 8.000000 8.000000 re B*
+0.750000 1.000000 0.650000 rg
+141.000000 291.000000 8.000000 8.000000 re B*
+0.800000 1.000000 0.625000 rg
+151.000000 291.000000 8.000000 8.000000 re B*
+0.850000 1.000000 0.600000 rg
+161.000000 291.000000 8.000000 8.000000 re B*
+0.900000 1.000000 0.575000 rg
+171.000000 291.000000 8.000000 8.000000 re B*
+0.950000 1.000000 0.550000 rg
+181.000000 291.000000 8.000000 8.000000 re B*
+1.000000 1.000000 0.525000 rg
+191.000000 291.000000 8.000000 8.000000 re B*
+
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000230 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+45360
+%%EOF
diff --git a/testing/resources/mona_lisa.jpg b/testing/resources/mona_lisa.jpg
new file mode 100644
index 0000000..df28704
--- /dev/null
+++ b/testing/resources/mona_lisa.jpg
Binary files differ
diff --git a/testing/resources/nonesuch_action.in b/testing/resources/nonesuch_action.in
new file mode 100644
index 0000000..c212b4e
--- /dev/null
+++ b/testing/resources/nonesuch_action.in
@@ -0,0 +1,45 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /T (txtName)
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+{{object 5 0}} <<
+  /S /NoneSuch
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/nonesuch_action.pdf b/testing/resources/nonesuch_action.pdf
new file mode 100644
index 0000000..9188d49
--- /dev/null
+++ b/testing/resources/nonesuch_action.pdf
@@ -0,0 +1,57 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /T (txtName)
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+5 0 obj <<
+  /S /NoneSuch
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000230 00000 n 
+0000000464 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+500
+%%EOF
diff --git a/testing/resources/pixel/axial_shading_point_at_border_no_extend.in b/testing/resources/pixel/axial_shading_point_at_border_no_extend.in
new file mode 100644
index 0000000..01f5690
--- /dev/null
+++ b/testing/resources/pixel/axial_shading_point_at_border_no_extend.in
@@ -0,0 +1,53 @@
+{{header}}
+{{object 1 0}} <<
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 400]
+  /Contents 4 0 R
+  /Resources <<
+    /Shading <<
+      /R9 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+50 0 0 50 150 100 cm
+10 10 280 380 re f
+/R9 sh
+endstream
+endobj
+{{object 5 0}} <<
+  /ShadingType 2
+  /ColorSpace /DeviceRGB
+  /Coords [0.0 0.0 1.0 0.0]
+  /Function 6 0 R
+  /Extend [false false]
+ >>
+endobj
+{{object 6 0}} <<
+  /FunctionType 2
+  /Domain [0.0 1.0]
+  /C0 [0.1 0.2 0.8]
+  /C1 [0.05 0.01 0.4]
+  /N 1
+>>
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected.pdf.0.png b/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected.pdf.0.png
new file mode 100644
index 0000000..075a8b2
--- /dev/null
+++ b/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1012369.in b/testing/resources/pixel/bug_1012369.in
new file mode 100644
index 0000000..7f56d35
--- /dev/null
+++ b/testing/resources/pixel/bug_1012369.in
@@ -0,0 +1,86 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 100 200]
+  /Resources <<
+    /XObject <<
+      % Both images are BGRA.
+      /ImBlue 5 0 R
+      /ImRed 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+100 0 0 100 0 0 cm
+/ImBlue Do
+Q
+q
+100 0 0 100 0 100 cm
+/ImRed Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /JPXDecode]
+  /Height 64
+  /Width 64
+{{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6fdf801801ca
+bf00cfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /JPXDecode]
+  /Height 64
+  /Width 64
+{{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6f00df801801
+cabfcfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1012369_expected.pdf.0.png b/testing/resources/pixel/bug_1012369_expected.pdf.0.png
new file mode 100644
index 0000000..ae4e6f97
--- /dev/null
+++ b/testing/resources/pixel/bug_1012369_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1021762.in b/testing/resources/pixel/bug_1021762.in
new file mode 100644
index 0000000..0aaf239
--- /dev/null
+++ b/testing/resources/pixel/bug_1021762.in
@@ -0,0 +1,108 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /Font <<
+      /Ft11 5 0 R
+    >>
+    /ProcSet [/PDF /Text]
+  >>
+  /MediaBox [0 0 100 100]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0 0 0 rg
+BT
+/Ft11 20 Tf
+1 0 0 1 20 40 Tm
+<0024002500260027>Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type0
+  /BaseFont /Arial-BoldMT
+  /Encoding /Identity-H
+  /ToUnicode 8 0 R
+  /DescendantFonts [6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /CIDFontType2
+  /BaseFont /Arial-BoldMT
+  /CIDToGIDMap /Identity
+  /CIDSystemInfo <<
+    /Registry (Adobe)
+    /Ordering (Identity)
+    /Supplement 0
+  >>
+  /FontDescriptor 7 0 R
+  /W [36 39 722.167969]
+>>
+endobj
+{{object 7 0}} <<
+  /Type /FontDescriptor
+  /FontName /Arial-BoldMT
+  /Descent -211.914063
+  /CapHeight 905.273438
+  /StemV 1
+  /Flags 32
+  /ItalicAngle 0
+  /Ascent 905.273438
+  /FontBBox [-627.929688 -376.464844 2000 1055.664063]
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin
+10000 dict begin
+begincmap
+/CIDSystemInfo <<
+  /Registry (Adobe)
+  /Ordering (UCS)
+  /Supplement 0
+>> def
+/CMapName /Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<0000> <FFFF>
+endcodespacerange
+1 beginbfrange
+<0024> <0027> [ <0041> <0042> <0043> <0044> ]
+endbfrange
+4 beginbfrange
+<09FF> <0A14> [ <1E86> <1E87> <1E88> <1E89> <1E8A> <1E8B> <1E8C> <1E8D> <1E8E> <1E8F> <1E90> <1E91> <1E92> <1E93> <1E94> <1E95> <1E96> <1E97> <1E98> <1E99> <1E9A> <1E9B> ]
+<0F5B> <0F5C> [ <1E9C> <1E9D> ]
+<0D47> <0D47> [ <1E9E> ]
+<0F5D> <0F5D> [ <1E9F> ]
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1021762_expected.pdf.0.png b/testing/resources/pixel/bug_1021762_expected.pdf.0.png
new file mode 100644
index 0000000..39d531a
--- /dev/null
+++ b/testing/resources/pixel/bug_1021762_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1087.pdf b/testing/resources/pixel/bug_1087.pdf
new file mode 100644
index 0000000..73bc682
--- /dev/null
+++ b/testing/resources/pixel/bug_1087.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_1087_expected.pdf.0.png b/testing/resources/pixel/bug_1087_expected.pdf.0.png
new file mode 100644
index 0000000..f1a283f
--- /dev/null
+++ b/testing/resources/pixel/bug_1087_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_113910_expected_win.pdf.0.png b/testing/resources/pixel/bug_113910_expected_win.pdf.0.png
new file mode 100644
index 0000000..1ff9194
--- /dev/null
+++ b/testing/resources/pixel/bug_113910_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1161.in b/testing/resources/pixel/bug_1161.in
new file mode 100644
index 0000000..1f02ab5
--- /dev/null
+++ b/testing/resources/pixel/bug_1161.in
@@ -0,0 +1,83 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 3 0 R
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Kids [4 0 R]
+  /Count 1
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Resources <<
+    /XObject << /X0 6 0 R >>
+  >>
+  /Parent 3 0 R
+  /Contents [5 0 R]
+  /MediaBox [0 0 400 400]
+>>
+endobj
+{{object 5 0}} <<
+{{streamlen}}
+>>
+stream
+q 1 0 0 1 50 50 cm /X0 Do Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /FormType 1
+  /Subtype /Form
+  /Resources <<
+    /ExtGState << /GS 7 0 R >>
+    /Pattern << /PAT 8 0 R >>
+  >>
+  /BBox [0 0 300 300]
+  {{streamlen}}
+>>
+stream
+/GS gs 0 0 0 RG 1 w
+/Pattern cs /PAT scn
+0 0 m
+200 0 l
+200 200 l
+0 200 l b
+endstream
+endobj
+{{object 7 0}} <<
+/Type /ExtGState
+/ca 0.2
+/CA 1
+>>
+endobj
+{{object 8 0}} <<
+/Type /Pattern
+/TilingType 1
+/PatternType 1
+/BBox [0 0 18 18]
+/PaintType 1
+/YStep 18
+/XStep 18
+{{streamlen}}
+>>
+stream
+0 0 0 rg
+0 0 18 18 re
+f 0 0 0 RG
+1 w
+3 -0.5 m
+3.25 -0.5 3.5 -0.25 3.5 0 c
+3.5 0.25 3.25 0.5 3 0.5 c
+2.75 0.5 2.5 0.25 2.5 0 c
+2.5 -0.25 2.75 -0.5 3 -0.5 c h
+0 0 0 rg
+f
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1161_expected.pdf.0.png b/testing/resources/pixel/bug_1161_expected.pdf.0.png
new file mode 100644
index 0000000..745b8c3
--- /dev/null
+++ b/testing/resources/pixel/bug_1161_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1236.in b/testing/resources/pixel/bug_1236.in
new file mode 100644
index 0000000..d99eabc
--- /dev/null
+++ b/testing/resources/pixel/bug_1236.in
@@ -0,0 +1,77 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [4 0 R]
+  /MediaBox [0 0 400 400]
+  /Resources <<
+    /XObject << /Xop2 5 0 R >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+200 0 0 200 0 0 cm
+/Xop2 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 400
+  /Height 400
+  /BitsPerComponent 1
+  /Filter [/ASCIIHexDecode /JBIG2Decode]
+  /ColorSpace /DeviceGray
+  /JBIG2Globals 6 0 R
+  /SMask 7 0 R
+  {{streamlen}}
+>>
+stream
+000000003000010000001300000dea00000353000017110000171151000000000001260001000000
+3500000dea000003530000000000000000020003fffdff02fefefeff7f86530fb6c922cfff7fff7f
+ff7fff7fff7fff7fff7fff7fffac
+endstream
+endobj
+{{object 6 0}} <<
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+0000000000010000000027000003fffdff02fefefe00000001000000010aabcf6e691c9aa3b0303f
+ff7fff7fff7f4fdfffac
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 100
+  /Height 100
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cedc1310100000c02a0dd6b60ffa2a6f003120000000060ed0000000080b92f6770e862
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1236_expected.pdf.0.png b/testing/resources/pixel/bug_1236_expected.pdf.0.png
new file mode 100644
index 0000000..2020fec
--- /dev/null
+++ b/testing/resources/pixel/bug_1236_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1283.in b/testing/resources/pixel/bug_1283.in
new file mode 100644
index 0000000..2120f86
--- /dev/null
+++ b/testing/resources/pixel/bug_1283.in
@@ -0,0 +1,64 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /OCProperties <<
+    /Configs <<
+        /Creator (First Config)
+        /Name (First Config)
+        /OFF [5 0 R]
+    >>
+    /D <<
+        /ON [5 0 R]
+        /Order [5 0 R]
+        /RBGroups []
+    >>
+    /OCGs [5 0 R]
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 400 400]
+  /Contents [4 0 R]
+  /Resources << /XObject << /Fm0 6 0 R >> >>
+>>
+endobj
+% Page 0 contents stream
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+1 0 0 1 50 50 cm /Fm0 Do
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /OCG
+  /Name (Square)
+>>
+endobj
+% Fm0 XObject
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 100 100]
+  /OC << /OCGs [5 0 R] /Type /OCMD >>
+>>
+stream
+0 G 0 g
+0 0 100 100 re
+b*
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1283_expected.pdf.0.png b/testing/resources/pixel/bug_1283_expected.pdf.0.png
new file mode 100644
index 0000000..76369d6
--- /dev/null
+++ b/testing/resources/pixel/bug_1283_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1286.in b/testing/resources/pixel/bug_1286.in
new file mode 100644
index 0000000..0a11df7
--- /dev/null
+++ b/testing/resources/pixel/bug_1286.in
@@ -0,0 +1,72 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Contents 5 0 R
+  /MediaBox [0 0 600 850]
+  /Parent 2 0 R
+  /Resources <<
+    /ColorSpace << /CS0 [/Pattern] >>
+    /ExtGState << /GS0 6 0 R /GS1 7 0 R >>
+    /Pattern << /P0 4 0 R >>
+    /ProcSet [/PDF /Text]
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Pattern
+  /BBox [0 0 72 72]
+  /PaintType 1
+  /PatternType 1
+  /Resources << /ExtGState << /GS0 7 0 R >> >>
+  /TilingType 1
+  /XStep 72
+  /YStep 72
+  {{streamlen}}
+>>
+stream
+0 0 72 72 re B
+endstream
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS0 gs /Pattern cs /P0 scn
+50 700 100 100 re
+B
+Q
+q
+/GS1 gs /Pattern cs /P0 scn
+50 580 100 100 re
+B
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /ExtGState
+  /CA 0.199997
+  /ca 0.199997
+>>
+endobj
+{{object 7 0}} <<
+  /Type /ExtGState
+  /CA 1
+  /ca 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1286_expected.pdf.0.png b/testing/resources/pixel/bug_1286_expected.pdf.0.png
new file mode 100644
index 0000000..de21a23
--- /dev/null
+++ b/testing/resources/pixel/bug_1286_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_1.in b/testing/resources/pixel/bug_1288_1.in
new file mode 100644
index 0000000..d7a1a7c
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_1.in
@@ -0,0 +1,62 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ColorSpace << /CS1 [/Pattern /DeviceRGB] >>
+    /Pattern << /P1 5 0 R >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+4 w /CS1 cs 1 1 1 /P1 scn
+q 75 75 100 100 re f Q
+q 75 75 100 100 re S Q
+Q
+q
+4 w /CS1 cs 1 1 1 /P1 scn
+q 25 25 100 100 re f Q
+q 25 25 100 100 re S Q
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  /PaintType 2
+  /TilingType 3
+  /BBox [0 0 8 8]
+  /YStep 8
+  /XStep 8
+  {{streamlen}}
+>>
+stream
+q
+0 0 m 8 0 l S
+0 2 m 8 2 l S
+0 4 m 8 4 l S
+0 6 m 8 6 l S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1288_1_expected.pdf.0.png b/testing/resources/pixel/bug_1288_1_expected.pdf.0.png
new file mode 100644
index 0000000..42d98b4
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_2.in b/testing/resources/pixel/bug_1288_2.in
new file mode 100644
index 0000000..081c4d0
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_2.in
@@ -0,0 +1,67 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 300]
+  /Resources <<
+    /Pattern << /P0 5 0 R >>
+    /ColorSpace << /CS0 [/Pattern /DeviceRGB] >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0 TL
+q
+/CS0 cs 0 0 1 /P0 scn
+20 20 99 99 re
+f*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  /PaintType 2
+  /TilingType 1
+  /BBox [0 0 100 100]
+  /XStep 98
+  /YStep 4
+  {{streamlen}}
+>>
+stream
+q
+1 i
+0 0 100 100 re
+W n
+0 J 0 j 1 w 10 M [1 ]0 d
+2 4 m
+99 4 l
+1 3 m
+98 3 l
+2 2 m
+99 2 l
+1 1 m
+98 1 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1288_2_expected.pdf.0.png b/testing/resources/pixel/bug_1288_2_expected.pdf.0.png
new file mode 100644
index 0000000..708eada
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1294.in b/testing/resources/pixel/bug_1294.in
new file mode 100644
index 0000000..3b35bb1
--- /dev/null
+++ b/testing/resources/pixel/bug_1294.in
@@ -0,0 +1,45 @@
+{{header}}
+{{object 1 0}} <<
+    /Type /Catalog
+    /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Contents 5 0 R
+  /MediaBox [0 0 200 200]
+  /Parent 2 0 R
+  /Resources <<
+    /Pattern << /P0 4 0 R >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  {{streamlen}}
+>>
+stream
+/T
+'
+endstream
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+/P0 scn
+re
+f
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1294_expected.pdf.0.png b/testing/resources/pixel/bug_1294_expected.pdf.0.png
new file mode 100644
index 0000000..f97e340
--- /dev/null
+++ b/testing/resources/pixel/bug_1294_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1296.in b/testing/resources/pixel/bug_1296.in
new file mode 100644
index 0000000..4c768dd
--- /dev/null
+++ b/testing/resources/pixel/bug_1296.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 1 0 rg
+370 212 m
+246 210 l
+h
+f
+Q
+q
+0 0 0 rg
+1 0 0 1 0 612 cm
+0 0 m
+612 0 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1296_expected.pdf.0.png b/testing/resources/pixel/bug_1296_expected.pdf.0.png
new file mode 100644
index 0000000..e88e8d9
--- /dev/null
+++ b/testing/resources/pixel/bug_1296_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308.pdf b/testing/resources/pixel/bug_1308.pdf
new file mode 100644
index 0000000..a0f4ced
--- /dev/null
+++ b/testing/resources/pixel/bug_1308.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_1.in b/testing/resources/pixel/bug_1308_1.in
new file mode 100644
index 0000000..5b9ab3b
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_1.in
@@ -0,0 +1,85 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+    /Font <<
+      /F7 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0 0 0 rg
+BT
+10 0 0 10 100 100 Tm
+/F7 2 Tf
+(\000\000) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type0
+  /BaseFont /ArialMT
+  /Encoding /Identity-H
+  /ToUnicode /Identity-H
+  /DescendantFonts [6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /CIDFontType2
+  /DW 735
+  /CIDSystemInfo <<
+    /Supplement 0
+    /Registry (Adobe)
+    /Ordering (Identity)
+  >>
+  /BaseFont /ArialMT
+  /FontDescriptor 7 0 R
+  /CIDToGIDMap 8 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /FontDescriptor
+  /Descent -211
+  /CapHeight 715
+  /StemV 86
+  /Flags 4
+  /MaxWidth 1101
+  /FontName /ArialMT
+  /ItalicAngle 0
+  /Ascent 905
+  /FontBBox [-664 -324 2000 1005]
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+  /Filter /ASCIIHexDecode
+>>
+stream
+0191
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1308_1_expected.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected.pdf.0.png
new file mode 100644
index 0000000..44c0446
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png
new file mode 100644
index 0000000..70d0f23
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected.pdf.0.png b/testing/resources/pixel/bug_1308_expected.pdf.0.png
new file mode 100644
index 0000000..1c36944
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png
new file mode 100644
index 0000000..570f992
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected_win.pdf.0.png b/testing/resources/pixel/bug_1308_expected_win.pdf.0.png
new file mode 100644
index 0000000..0acd825
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1314.in b/testing/resources/pixel/bug_1314.in
new file mode 100644
index 0000000..8da683e
--- /dev/null
+++ b/testing/resources/pixel/bug_1314.in
@@ -0,0 +1,137 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 3
+  /Kids [3 0 R 4 0 R 5 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 300]
+  /Resources <<
+    /Shading <<
+      /Sh0 7 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 300]
+  /Resources <<
+    /Shading <<
+      /Sh0 8 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 300]
+  /Resources <<
+    /Shading <<
+      /Sh0 9 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+72 0 0 72 10 10 cm
+/Sh0 sh
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  /ShadingType 1
+  /ColorSpace /DeviceRGB
+  /Background [0 .5 .1]
+  /BBox [0 0 72 72]
+  /Domain [-1 1 -1 1]
+  /Function [10 0 R 13 0 R 13 0 R]
+>>
+endobj
+{{object 8 0}} <<
+  /ShadingType 1
+  /ColorSpace /DeviceRGB
+  /Background [0 .5 .1]
+  /BBox [0 0 72 72]
+  /Domain [-1 1 -1 1]
+  /Function [13 0 R 11 0 R 13 0 R]
+>>
+endobj
+{{object 9 0}} <<
+  /ShadingType 1
+  /ColorSpace /DeviceRGB
+  /Background [0 .5 .1]
+  /BBox [0 0 72 72]
+  /Domain [-1 1 -1 1]
+  /Function [13 0 R 13 0 R 12 0 R]
+>>
+endobj
+{{object 10 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [0 1]
+  {{streamlen}}
+>>
+stream
+{
+1.1 truncate
+}
+endstream
+endobj
+{{object 11 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [0 1]
+  {{streamlen}}
+>>
+stream
+{
+3123412341.1 truncate -3123412340.5 add
+}
+endstream
+endobj
+{{object 12 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [0 1]
+  {{streamlen}}
+>>
+stream
+{
+-1.9 truncate 1.5 add
+}
+endstream
+endobj
+{{object 13 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [0 1]
+  {{streamlen}}
+>>
+stream
+{
+0
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1314_expected.pdf.0.png b/testing/resources/pixel/bug_1314_expected.pdf.0.png
new file mode 100644
index 0000000..88355ae
--- /dev/null
+++ b/testing/resources/pixel/bug_1314_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1314_expected.pdf.1.png b/testing/resources/pixel/bug_1314_expected.pdf.1.png
new file mode 100644
index 0000000..cdc3cf4
--- /dev/null
+++ b/testing/resources/pixel/bug_1314_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1314_expected.pdf.2.png b/testing/resources/pixel/bug_1314_expected.pdf.2.png
new file mode 100644
index 0000000..450e4e8
--- /dev/null
+++ b/testing/resources/pixel/bug_1314_expected.pdf.2.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1321.in b/testing/resources/pixel/bug_1321.in
new file mode 100644
index 0000000..10e7187
--- /dev/null
+++ b/testing/resources/pixel/bug_1321.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 300]
+  /Resources <<
+    /Shading << /Sh0 5 0 R >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+72 0 0 72 10 10 cm
+/Sh0 sh
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /ShadingType 1
+  /ColorSpace /DeviceRGB
+  /Background [0 .5 .1]
+  /BBox [0 0 72 72]
+  /Domain [-1 1 -1 1]
+  /Function [6 0 R 7 0 R 8 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [-1 1]
+  {{streamlen}}
+>>
+stream
+{
+-5.5 round 6 add
+}
+endstream
+endobj
+{{object 7 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [-1 1]
+  {{streamlen}}
+>>
+stream
+{
+-5.4 round 6 add
+}
+endstream
+endobj
+{{object 8 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [-1 1]
+  {{streamlen}}
+>>
+stream
+{
+-5.6 round 6 add
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1321_expected.pdf.0.png b/testing/resources/pixel/bug_1321_expected.pdf.0.png
new file mode 100644
index 0000000..4a5d6dd
--- /dev/null
+++ b/testing/resources/pixel/bug_1321_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1330.in b/testing/resources/pixel/bug_1330.in
new file mode 100644
index 0000000..ebe70e5
--- /dev/null
+++ b/testing/resources/pixel/bug_1330.in
@@ -0,0 +1,40 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 600 400 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+12 w
+52.941177 217.61189 m
+52.941177 298.55426 132.137 364.171 229.829 364.171 c
+327.5213 364.17099 406.717 298.554 406.717 217.612 c
+406.71661 136.669525 327.521 71.0528 229.829 71.0528 c
+132.136505 71.052795 52.9412 136.67 52.9412 217.612 c
+h
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1330_expected.pdf.0.png b/testing/resources/pixel/bug_1330_expected.pdf.0.png
new file mode 100644
index 0000000..134f8bb
--- /dev/null
+++ b/testing/resources/pixel/bug_1330_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1331.in b/testing/resources/pixel/bug_1331.in
new file mode 100644
index 0000000..53c8b36
--- /dev/null
+++ b/testing/resources/pixel/bug_1331.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 300]
+  /Resources <<
+    /Shading << /Sh0 5 0 R >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+72 0 0 72 10 10 cm
+/Sh0 sh
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /ShadingType 1
+  /ColorSpace /DeviceRGB
+  /Background [0 .5 .1]
+  /BBox [0 0 72 72]
+  /Domain [-1 1 -1 1]
+  /Function [6 0 R 7 0 R 8 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [-1 1]
+  {{streamlen}}
+>>
+stream
+{
+1
+}
+endstream
+endobj
+{{object 7 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [-1 1]
+  {{streamlen}}
+>>
+stream
+{
+0
+}
+endstream
+endobj
+{{object 8 0}} <<
+  /FunctionType 4
+  /Domain [-1 1 -1 1]
+  /Range [0 1]
+  {{streamlen}}
+>>
+stream
+{
+0
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1331_expected.pdf.0.png b/testing/resources/pixel/bug_1331_expected.pdf.0.png
new file mode 100644
index 0000000..71649ff
--- /dev/null
+++ b/testing/resources/pixel/bug_1331_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1338.in b/testing/resources/pixel/bug_1338.in
new file mode 100644
index 0000000..3700527
--- /dev/null
+++ b/testing/resources/pixel/bug_1338.in
@@ -0,0 +1,79 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 500]
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0.5 0 0 0.5 0 0 cm
+1 0 0 rg
+450 950 m
+250 925 l
+250 550 l
+250 750 l
+450 950 l
+f*
+Q
+q
+0.5 0 0 0.5 0 0 cm
+0 1 0 rg
+450 450 m
+250 400 l
+350 100 l
+300 250 l
+450 450 l
+f*
+Q
+q
+0.5 0 0 0.5 0 0 cm
+0 0 1 rg
+75 250 m
+50 100 l
+300 100 l
+175 100 l
+75 250 l
+f*
+Q
+q
+0.5 0 0 0.5 0 0 cm
+1 1 0 rg
+250 950 m
+50 925 l
+50 550 l
+50 950 l
+250 950 l
+f*
+Q
+q
+0.5 0 0 0.5 0 0 cm
+0 0 0 rg
+570 750 m
+450 775 l
+300 550 l
+500 850 l
+570 750 l
+f*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1338_expected.pdf.0.png b/testing/resources/pixel/bug_1338_expected.pdf.0.png
new file mode 100644
index 0000000..5048783
--- /dev/null
+++ b/testing/resources/pixel/bug_1338_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1374.in b/testing/resources/pixel/bug_1374.in
new file mode 100644
index 0000000..5b5aed1
--- /dev/null
+++ b/testing/resources/pixel/bug_1374.in
@@ -0,0 +1,73 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 100]
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+50 50 m
+50 50 l
+50 50 l
+h
+50 51 m
+50 51 l
+50 51 l
+h
+50 52 m
+50 52 l
+50 52 l
+h
+50 53 m
+50 53 l
+50 53 l
+h
+50 54 m
+50 54 l
+50 54 l
+h
+50 55 m
+50 55 l
+50 55 l
+h
+50 56 m
+50 56 l
+50 56 l
+h
+50 57 m
+50 57 l
+50 57 l
+h
+50 58 m
+50 58 l
+50 58 l
+h
+50 59 m
+50 59 l
+50 59 l
+h
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1374_expected.pdf.0.png b/testing/resources/pixel/bug_1374_expected.pdf.0.png
new file mode 100644
index 0000000..319932a
--- /dev/null
+++ b/testing/resources/pixel/bug_1374_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388.in b/testing/resources/pixel/bug_1388.in
new file mode 100644
index 0000000..bd16578
--- /dev/null
+++ b/testing/resources/pixel/bug_1388.in
@@ -0,0 +1,85 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 345.87 344.348]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /TT0 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+BT
+2.057 w
+/TT0
+72 Tf
+2 Tr
+27.724017 242.394098 Td
+( ) Tj
+ET
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /FirstChar 32
+  /BaseFont /AAAAAD+Test
+  /FontDescriptor 6 0 R
+  /ToUnicode 7 0 R
+  /LastChar 32
+  /Widths [1055]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Descent -68
+  /MissingWidth 1000
+  /CapHeight 1149
+  /StemV 0
+  /FontFile2 8 0 R
+  /Flags 4
+  /FontBBox [0 -215 1000 932]
+  /FontName /AAAAAD+Test
+  /ItalicAngle 0
+  /Ascent 933
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1388_cmap.fragment}}
+endstream
+endobj
+{{object 8 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1388_truetype_font.fragment}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1388_2.in b/testing/resources/pixel/bug_1388_2.in
new file mode 100644
index 0000000..e2ce6ba
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_2.in
@@ -0,0 +1,67 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /TT2 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/TT2 12 Tf
+40 100 Td
+[(X  X)] TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /TimesNewRomanPSMT
+  /Encoding /WinAnsiEncoding
+  /FirstChar 31
+  /FontDescriptor 6 0 R
+  /LastChar 252
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 891
+  /CapHeight 656
+  /Descent -216
+  /Flags 34
+  /FontBBox [-568 -307 2000 1007]
+  /FontFamily (Times New Roman)
+  /FontName /TimesNewRomanPSMT
+  /FontStretch /Normal
+  /FontWeight 400
+  /ItalicAngle 0
+  /MissingWidth 778
+  /StemV 82
+  /XHeight -546
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1388_2_expected.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
new file mode 100644
index 0000000..422a7e2
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png
new file mode 100644
index 0000000..b1a4843
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png
new file mode 100644
index 0000000..58a14f4
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_3.in b/testing/resources/pixel/bug_1388_3.in
new file mode 100644
index 0000000..dc849a6
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_3.in
@@ -0,0 +1,225 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /TT2 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/TT2 12 Tf
+40 100 Td
+[(X \037 X)] TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /PDFium1388_3
+  /Encoding /WinAnsiEncoding
+  /FirstChar 88
+  /FontDescriptor 6 0 R
+  /LastChar 88
+  /Widths [750]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 905
+  /CapHeight 1000
+  /Descent -211
+  /Flags 32
+  /FontBBox [-628 -376 2000 1018]
+  /FontFamily (PDFium1388_3)
+  /FontFile2 7 0 R
+  /FontName /PDFium1388_3
+  /FontStretch /Normal
+  /FontWeight 700
+  /ItalicAngle 0
+  /StemV 138
+  /XHeight 1000
+>>
+endobj
+{{object 7 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cbd5a0b7854d5b55e7bef338f843c86570809f19c304c821922c9f04880486692497ca44a80
+48278075024441794c4d028a0a0872c5c147ac8f5b6b2b11cb43413999419cf09058aebdd6d682
+b55af4b6355fd5562d5c69abd5ab90d37fef1922b4de7e6deff7dd73b2ce5a7baff5afb5f6dafb
+ec336732c488c8858b46545fdf74c5acc8471f108d398ede519704ebeaa998bc689b6817cd983d
+def7dff34b9e27629bd00e2f5cd612a16de93bd03e0dba7ce1ca76e38be94fbc4594f7319163ee
+b591eb9639dfdf3f96485c4a64fbc5752d6d11cb22017f3b81775eb7f4e66b2ffdb8fa19a2d1f3
+d1be7b716bcba2be7d1f97c25706da9317a3236383bd09ed20da63162f6bbf69f87d3261f63e2e
+c396ae58d8424b29067c23da19cb5a6e8a389ea45ba05f8cb6b1bc6559eb63ae82dba1873ddb10
+59d1d67ec9ea5cf81fb314faa9911b5b237fb8fcbd1ca2348368f0cf48b9267ae8b679b66bb2ab
+3e21dd49f2d8fa4e5581e46f762d1f2ab90d27ec9cc4297900e398de7f25d526ed95f6fcc3a67a
+6c03184e2e1a4f5701574535aa4788ffe407a585ed3bb60970909fe4e255ba960f71daf82087c6
+e5a1f55189d54b37d502932681060e9267c8f65aff4c36c1319dc5fcc42c549ab422db7e6942cc
+9e4a4966c8a7209539c9a8291a955207d162aaad11e68dece00cd9c2bbf497e4d63bd6ee2f4708
+d7030dc6d5d8ce3f5832c4bf7268ff22eefffff0079afcd5d32fae9a36754a65c5a489137ce565
+e32f2a1de72db9706c7191678c7b74a1a15f50302a3f6f64ee889ce1c3860e19eccacecacc1894
+9ee674d86d9ae08cc6d5b9ebc386591436b522f7a59796cab6bb051d2de774844d035df5e7db98
+46589919e75bfa6179ed5f59fa9396fe014be632aaa8aa749c51e736cc57826e23c1e6ce0c41be
+27e86e36cc934abe42c99d4ace845c5808805197bb3868982c6cd499f52b1747ebc241b8eb1e94
+5eebae6d4d2f1d47dde983200e82648e7047bad988e94c097c44ddd46e4ece4c2465e6b98375e6
+48775066600a4f5dcb22b37166a82e985f58d85c3ace64b50bdd0b4c72d798d95e6542b52a8c69
+af351d2a8cb1448e86361bdde37aa377275cb420eccd58e45ed4323f648a96661963b0177183e6
+88d5efe67ed984f321b5a13bcfd5e68b685dee124336a3d13b0db37766e85c6da1bc3637c307b0
+dc531f8ed623f4dd2862c36c03d1f8c6e690c93622a421472247951c5fabbb4ef684af37cc3477
+8d7b71f4fa30a6262f6ad2ac9b0b637979fe1eab8ff2ea8c6853c85d6856e7bb9b5b82a3ba8751
+74d6cdf1917e63e4f99ad271ddaec1c9c2766765a7848ccc7385d6019d9294b9941a660d5496c9
+8cdc97614198c642039984dc1853a5bcb45652746125cc703433a0cc45989125665a6d38ea9a2a
+fb25deb4795c6e23fa096105b84f9e38bfa725d563f7b83e2129ca7532b0d4a03f2b9b5eaf5952
+229788a316738a1ca7abf6a4d2712b137cb23be232c0503e6a446d5b9aa78e47f90b0be5046f4e
+f869011ae6ba99a164dba005f931f28ff7369b3c2c35bd6735c3af929a75673503f0b01b2b79af
+dabf869bcea281bf6c57ced0bac5534d96f377d4ad497dc36c77c3ccb921a32e1a4ed5b6a1e9bc
+56525f39a04b49e6d0da90c8e72989e70ba5c5a29c3f602c1ba10c53f3e0cfae16f522536051aa
+0e66d49baef0a5c96b737a61e1ff8a49389ce78012d6298952ec4b582a4b73aaf7fcf6b4f3dae7
+65971115c8572be20d4d73a3d1f4f374f5d880a2d17ab7511f0d475b12d6ba056ec3e58ef6f01d
+7c473452173e3ba1096bffe67cb3feee660c62319b8ac5caa9a6dbcd36cdecf6b34db3e7867a5c
+78ea6c6a0ac538e3b5e19ae6ee31d0857a0c6cb9aa970ff4ca96215bd4c0b0d063dca954f93d7e
+a2754aaba90ed55e9860a4fa9c67fb182d4cf0649f4bf5e128a5eea675814cb19bf680f0d0c6d5
+00758104f9c5eeb823d3e74f800f19a6782cc7ebebb17a214c9da0fa4b1ff4ad3b2876d1353401
+ddbb6257c9ee5d717fd0a7f88469493ebe5cf19833a9760cf3e9813cc0c6833865a7a419a0fb40
+5b4087417624b48bde06592021768aadb17a1d1eb6c151766098d88651f9713d0ab24002d96fc3
+58b6d147a91e0d593d114fcb90e19f50a87cf10450d9b8ba40eb407b404741365a81eb16900512
+90b642b795b8d82a1e8fb97457205d3c466b415c7c87b219231ddebf1d77a9da3c12cf1eeaf307
+5ce2216a047132c515d40be2707b3f60f713877943acb45c95b0219e9ee573c17e3392de8c4436
+236417ae4cb5fd2069bf393e3447badf10cb1eac70b7c4ca262685b82bd7d7882adc444cb48ae5
+e4265dac01bf007c217801f802b18832559efe78b6cbb70ef1aa615e2d86d3855007440ef9c083
+228ff29559472c2b19a72336b6c48711d78a5c65922d326922b85338623edd3820fcaaf89be269
+83647e9b62aee1be4362a370d03058ad83d5083dfb9048c7cca6ab9134c5d3327d9d810cd18461
+36a12c3a7264a8f272e568790c8e0283459d184539d0dd200a683878bdb840f11de271aa07ff5e
+bc6894de7b403ca050df924e117e7a72694d8f6766f97a0369623ab4a6b8171370af0ade192faa
+f451a0488ca532103ef189b590d6aa451f8514c5ac45315351cc54144945b1fa48dc05cd5db019
+2f565344aca24ed016c872590d8fa1a03d4a1833d6d723468a5c14c67500a564e8cd8ba765c9cc
+726343862ab3dc784696affa9068c33a6f834fbf688f8fc8f5ad38204ad450c6c573f3252012c3
+723d244624a706c01c392587c428144216a6405c101bae9b011d6db990757c2c7d991f9345e2af
+f1d7e574f3a3684bfee3147f25c57f9ae4562f3f96bc29f8cf24ef0b8ce2efc1d935fc57b40512
+e707f8112a03e02d9e9059f037790f55831f477b11780ff804f0fdb1c297f4044fc4c190fba3b1
+cc1c39587e24e61d9f12744f4a18919f1286e4f8021efe03fe023e87ebfc17e063c05fe0bd341a
+fc30782e782f6fa797c09fe593681af8de14ff0fbc476089f3e7f83eaa048fc7b2640a66cc21d9
+9e985db26762946c358ed70ff267f82eca83e9d3b1a23cf4ee8c178dd1b30fc01fe3db787bac40
+1f1248e78fb310fb18465d745c721ac2b7c62aa493ced84143efe19dbcd39f5be1f7f84bfddb45
+99a7acb46cbb303c46a951616c37022e7e2f36902d1cf72fdf8c6b05191cab07e40775f2bb625a
+8519388331c971715a876b9792c2b8469444b8ba06b4a79454cd37d20c10878f35a0b5a075a0db
+f1f2d0c957836e01dd0aba4df5b4833a40abb09b44808800110122a21011202240448088284444
+45ef004944188830106120c20a1106220c441888b042c87cc3408415a2118846201a8168548846
+201a816804a251211a816804a25121fc40f881f003e157083f107e20fc40f815c20f841f08bf42
+94015106441910650a5106441910654094294419106540942984018401840184a1100610061006
+10864218401840180ae102c205840b089742b8807001e102c2a5102e353f1d2089e803a20f883e
+20fa14a20f883e20fa80e853883e20fa80e8e3abbac5b1c08b801c03e41820c714e41820c70039
+06c831053906c831408ea586deae8ac1b16cd680d682d68124b617d85e607b81ed55d85eb5bc3a
+40126b0261026102612a84098409840984a91026102610a6427401d1054417105d0ad105441710
+5d40742944975ab81d2089f8e717e53f3d35fc761672e259cbd7b10b155f4b27145f43c715bf8d
+ba15bf95b62b7e0bad577c355528be8a8a14873fc5db4977b2985e911dc8c1163003740d680568
+0b680fe830c8a1a4a3a0b741169fe41fad653b6638b638f6380e3b6c7b1c7d0e9e6d9f61df62df
+633f6cb7edb1f7d9b911c8e7996a1fc5d642f7a9eb5a5c3f02e121826bb592aaf944c49d887d76
+12ce897ca27ff049e3a31276b4841d2e617b4ad87d252c90c62f619adae90caac0fbb3ce42fe8c
+a2e9fa71504551f174ec4cf7ee3b31428f154dd613ec60925de8f7829f007583b683d6832a403e
+5029c803d2555f09ec43fed129970741c5a042902143504e0ede0b860c76fa7b7826db1e7f3193
+d2649ce2b1c01d881597812562c533c09e8b152fd003696c1f15cb4f45ec59ccdc2ef03d31fd5d
+a89f4eb2dd31fd00d8ce983e11ecea58f14560f362c5afe8814c7615e99a8436a5f86c8c5bf259
+317d0ecc66c6f40bc1bcb1e222695d82401e682f64217a17dc93428d494672c7f46960a363fa14
+69eda46239f1cc4ea52a3d1b48721147421ff5b090c6fc83f493fa03fa09c07f8fc26279bc6924
+34b0a39e049be34fd70f963e06e3801e0ba44b7b3c1fba53dc94fc597dbbe72efd51f8629e7dfa
+23fa45fabda50927baef41de77a910317d3ddef576f987eaebf432bdbdf45dbd4dbf5c6fd167e9
+577bd01fd3e7eb07659ad4cc427cd73ebd110e2fc3283c31fd124f42a558afdfacfbf5627d8a71
+50d6972a937e2b4a0fca0a902f197d1cea5be249c8357e5545820df697384e393a1df31c358e69
+0eb763b4e302478163987388d3e5cc726638d39d4ea7dda939b9939cc312569fdf2bbfd31a6677
+4966d7e45553b28b93fab24c7de5c59993d3e5640e150dbc61760d6b307b1752c302c3fcf36c77
+82a5e355cae6ae61e690066a68aa312bbd0d098735cbacf036988ec679a16ec6ee6d46afc937e1
+45a529946096ecda982fbfb3e866b4f19efc1e626ce4c67b9a9b29376765756ef590e983a7d407
+bfe2124e5dbd5f1eb9e78a05e6c30db343e65305cda64f0a5641738379bbfc46a38767f3ccba60
+0fcf92ac39d4a3457876dd2cd9af4582cd307b5799613567c18c8a258399b3860c6986fda4469a
+618e92764580c3ae5032d8a5675291b22b4acf54761a9376ddc78dba60b761281b0fd1716573dc
+43e7d860c5001bec2e2a52566e8385a4150bb90d95d885ca91aec3a45457260c9feb94239da960
+e6f82f4d3c299349032693542cc1beb4d19336c3c69eb519361636deffe3d15ae365f1f28e3547
+e4974461775d2b286c6e5eb938d75cb7c030bad774a4be3d2a0a2f58b858f29656b3c3dd1a34d7
+b8834677f991af501f91ea7277b09b8ed43585ba8ff85b83b1727f799dbb25d81cafae0a05ce8b
+75d740ac50d55738ab92ce42325675e02bd401a9ae96b102325640c6aaf657ab58754be4ba6f0c
+753ba9a6b9767e92c7f9a074ace1707e61734d8e2b325d2ee89e6985b96bf2f76bc476d2206fb3
+99e1ae31334152551a280d4815ee33a9ca92df04a654b96ba615e6ef673b532a17ba07bb6be86c
+69491a35989366369885b3e786e45231fd2d5f3d676df250ea5caa5b12c41fdaed8a709e6b496d
+5f79b47fd5d1d1d1d1262f1dde36a206b3647683397926327138102a1c6c46df4567fb84507ddd
+69697509ab174a2f9260ed329c94bccc8b0afad3f1d6e5e05df62e0797af0aedf1bc02df8a4378
+82af05e13d8eaf8a8d57afcf7c557cb447bebfb4c7c74f4a72bcae4a1ecb2bf42142bc0250c93d
+49ee1f5c0aa1d3d359da59d1e5e92aedaab0a377df7674eadbe5a334367ebba0766fdbd942406c
+6f46b191968cf7786c54810adc2505afb7d9dbc654bdfeb6d8ec6cd1070adb96f2daa6dcb79f9d
+90647f5bca09662219bde32cac230552ca0e054a3a49b6062e5f1e6811d9f6d328453b68945684
+772cb2de3d4bfd4bac77a54e72fe2176f28224a58e18eda65fb0b1cca038fb9c46d0676c242ba7
+cbb03a3fc547b73d74861ec2eb7d133dcc86e09d2d87aea2cb98061b2fddcd1eb5565a1fd0c5f4
+2dda6a3dc7d65b4f417f1ffd903e4306bfc693b282ae84fd55d44a1f88f7a8d9fa0e39e94e1a84
+77ba592c875ae80d9c9f208707e8417a9edd6a7d86a8c3683dfc55518002d60bd6692aa1bbb54e
+dbf1b467e97e3ac0ecd6426b093e218da628f75a6f586f531135d313b41b397959af762915d20d
+b491becd468a1f427a88be4ffd2c835f2d6a6d8711e9329a43cb691545e9297a990d618db6e3b6
+53d62dd6efb00a87d258e4b4843e6093d8157c9b96614db7dea279d4432f61bcf2ecd5e6693b6c
+f3faabadef593fc0dbf7732c9d1d642fd87cb67bcfdc6e3d6e3d4319c8a71c15b9127116d0067a
+817e447fa03ff2b5d65aba946623f28bac8019ac08157f838fe46bf81af11a5d84d15e8d6c3b68
+0b999891fd74800ea136ff457df41e1bc6f2d9e56c01bb9ffd9167f045fca87854ec153fd798f6
+24eaed260f6ad44edb681ffd845ea1a3cc06ff65ac915dcf56b07f67df637ddce427f8a79a53db
+a07da19db115f5f7f57f615d697d8277ee3cfa1aada6b5a8ed1314a7bdf4537a9dfe487fa23f33
+17ab648bd9e3cc647dec044fe3a3f90c1ee10fe3edf96971a5b85fbca04dd26ab41bb457b4b76c
+ff66dbec6871f49fdedeff40ffd3fdaf5acf59af62ed64c17f11d5a3a2b763556ca3c3f41abcbf
+49bfa2dfc8f503ffd3d85cf60d4469639bd883ec69f6227b957d8851923a47f3693c88a82bf88d
+a8d37afe007f10d18fca6f3af85bfc57fcf7fc136113a3c564f14df1b83045421c13bfd55c5a91
+769156aecdd0e66a1666c667bbc436dbb6d3b6cbf603db297b957d913d627fdfb1de7187f32767
+4acefcba9ffa17f79bfd71ac5d2756d26a54e231da8a75bf1773f0322afa5364dc471f6316f258
+212b46de53583d6b6057b0afb3f9ac95ad6777b26fb16fb347d956f60c468031700772f7f2009f
+cd5b782bbf83dfc9efe17b71eee73fe26ff0e3fc24321f21dcc22bcac56562ae982796630ced62
+8db80395bd5f3c258e8ad7c4efc4fbe224666d847681d6a1add61ed176687bb5576d5fb32dc3b9
+d576d8d66b7bd576da76daceed79f651f6f1f6ebed3bedbf71d81d931d8d8ebb1c3f77fcc91961
+a358093237cefdcf1e1f897bf002fe141fa6ad6527d15180b78e6c8cdc8b79988dbbe24f542dfa
+312f59528fdc86f3919afa3fb1ddaf99f23b0b768026b11769ad9d0bf98ff63e8ab15ff23eed08
+bf985e67613652db2196db5ee685b40bbb51273fc80fb01adacbabf81cfe5d41ec3d3c15dfc37a
+bf891e6437b036dac54eb2a9ec3656c1d6d2cf798e98cdeea02a6b2bd7581abb8c9d226440b76b
+8be81b7fff3f966c0afd923ee87f4ccbd46ec5fe94a08731a3bbe96df6247dce6cd609ec6e02bb
+510b7699bbb1de3792dcf5aec67db616f7e348ec204bed4769affcefafa3c23e5d5b4da7e87fe8
+03db7eaca81aeca4bfeb5fa23da6bd635558a5b8c37097d14edc778be912dc31ef61951c425bb6
+e6e34e4fc75ee2c35ddd48736911dd865def7ecbb4be6b6db06eb656d08f81fd9c8d639fb32edc
+110920aae8259cf7d19b6c33eec34bfeb5ffd8f62fa25efa90e5320ff3e17e38695b69ebb43d65
+db6b7bdef68abd1cd5be831ec58afe0d56733a46b0905ea50fe953e6c4dc8ca4713411f95622f7
+102de5cde210d5b23c8ae09e1d8b7dbc2635923678598fea7d17f7f321dc1ba7b04fcca7e7e938
+e36c0446b410f19df0d3803a5f03ebed98c10d2c8e9e45d8b54be8f7187716abc40bf938f2c3d3
+c3d8b57a91d32fe9b7a8b6a5f21a877d21c8e6c0d7a7f4755a840893a991756306f6d114ecac41
+f113d47b0c73510d1bcdbe0f5c1877681615d014db3b8cd3b8fe2bad4abe441cc233c6427f179e
+5ef97431fb26b2c8c638ced070368326f5cf420eaf31a199ec672a8b4778ab75a758d5bf947e4c
+4f624efcda4a4750157672eaacc73b10a57e2d21c841357b39ebb73b12bcda3f946c5abfa07487
+d6cf68a4d36eebe7e2202ba2346ca0b994eb75fdb9ea4cd595ae8fabae385345d5905da771292f
+2b1c5c38d8830bdeb8e8b4217a4ffb6df405195a6fea5707b8e3dd88850f84430b87178add9af3
+8bcf766beeb74e3f0765163d73ceef32784a66780a15a4648e2ca7a66481fef9295983bc2125db
+280d4fc9a46c47ff4bf217135a5aca6b5266d8cb454ae6885b929205fa6b52b20679494ab6d110
+ea4cc976f43f235d3552503d0e3a68191e9213b0d82ae57f91a008d62fe958563ea1b2f21ab466
+e1f3c275305a8a3bf546345bafeb58da72e33f0cff07cde620ca8d58a14b68053e11c85f9bd09c
+d61bdb96ac586e18ffb093bff965c85f1ff287339cc9ca4db35fb7f4e6c8e2f224f32567cab252
+b3267f1d94833b44b698fa858a96f29efa054f6a6671bc7eb22e21f9b9bfe1f90b04bdcb3a
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1388_3_expected.pdf.0.png b/testing/resources/pixel/bug_1388_3_expected.pdf.0.png
new file mode 100644
index 0000000..be5ef37
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png
new file mode 100644
index 0000000..bb97a96
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_expected.pdf.0.png b/testing/resources/pixel/bug_1388_expected.pdf.0.png
new file mode 100644
index 0000000..a78094f
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402.in b/testing/resources/pixel/bug_1402.in
new file mode 100644
index 0000000..c911e4f
--- /dev/null
+++ b/testing/resources/pixel/bug_1402.in
@@ -0,0 +1,83 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /G2 4 0 R
+    >>
+  >>
+  /Contents [5 0 R]
+  /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type0
+  /BaseFont /HonMincho-M
+  /Encoding /90pv-RKSJ-H
+  /DescendantFonts [7 0 R]
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/G2 6 Tf
+6 0 0 6 100 100 Tm
+0 0 0 1 k
+<eb42>Tj
+0 -12 TD
+<eb42>Tj
+-12 0 TD
+<eb42>Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Descent -120
+  /CapHeight 709
+  /StemV 81
+  /Flags 6
+  /XHeight 450
+  /Style <<
+    /Panose <010502020500000000000000>
+  >>
+  /FontBBox [-165 -221 1066 952]
+  /FontName /HonMincho-M
+  /ItalicAngle 0
+  /Ascent 880
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /CIDFontType2
+  /BaseFont /HonMincho-M
+  /FontDescriptor 6 0 R
+  /DW 1000
+  /W [1 [250] 18 19 500 54 [833] 81 [583]]
+  /CIDSystemInfo <<
+    /Supplement 2
+    /Registry (Adobe)
+    /Ordering (Japan1)
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1402_expected.pdf.0.png b/testing/resources/pixel/bug_1402_expected.pdf.0.png
new file mode 100644
index 0000000..cf913bd
--- /dev/null
+++ b/testing/resources/pixel/bug_1402_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png
new file mode 100644
index 0000000..9602820
--- /dev/null
+++ b/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1457.in b/testing/resources/pixel/bug_1457.in
new file mode 100644
index 0000000..a8f7be4
--- /dev/null
+++ b/testing/resources/pixel/bug_1457.in
@@ -0,0 +1,101 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /Ft11 5 0 R
+    >>
+  >>
+  /MediaBox [0 0 100 100]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0 0 0 rg
+BT
+/Ft11 20 Tf
+1 0 0 1 40 40 Tm
+<0004>Tj
+ET
+endstream
+endobj
+{{object 5 0}}  <<
+  /Type /Font
+  /Subtype /Type0
+  /BaseFont /Arial-BoldMT
+  /Encoding /Identity-H
+  /ToUnicode 8 0 R
+  /DescendantFonts [6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /CIDFontType2
+  /BaseFont /Arial-BoldMT
+  /CIDToGIDMap /Identity
+  /CIDSystemInfo <<
+    /Registry (Adobe)
+    /Ordering (Identity)
+    /Supplement 0
+  >>
+  /FontDescriptor 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /FontDescriptor
+  /FontName /Arial-BoldMT
+  /Descent -211.914063
+  /Ascent 905.273438
+  /CapHeight 905.273438
+  /StemV 1
+  /Flags 32
+  /ItalicAngle 0
+  /FontBBox [-627.929688 -376.464844 2000.000000 1055.664063]
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin
+10000 dict begin
+begincmap
+/CIDSystemInfo <<
+/Registry (Adobe)
+/Ordering (UCS)
+/Supplement 0
+>> def
+/CMapName /Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<0000> <FFFF>
+endcodespacerange
+99 beginbfrange
+<0004> <0004> [ <0041> ]
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1457_expected.pdf.0.png b/testing/resources/pixel/bug_1457_expected.pdf.0.png
new file mode 100644
index 0000000..8ba763c
--- /dev/null
+++ b/testing/resources/pixel/bug_1457_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_304.pdf b/testing/resources/pixel/bug_304.pdf
new file mode 100644
index 0000000..c03734f
--- /dev/null
+++ b/testing/resources/pixel/bug_304.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_304_expected.pdf.0.png b/testing/resources/pixel/bug_304_expected.pdf.0.png
new file mode 100644
index 0000000..e1b3d43
--- /dev/null
+++ b/testing/resources/pixel/bug_304_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_451366.in b/testing/resources/pixel/bug_451366.in
new file mode 100644
index 0000000..fd57e5b
--- /dev/null
+++ b/testing/resources/pixel/bug_451366.in
@@ -0,0 +1,49 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources  <<
+    /ProcSet [/PDF /Text /ImageC]
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 1 50 100 cm
+1 1 1 rg
+0 0 0 RG
+0 J
+0 j
+260 w
+10 M
+[] 0 d
+0 0 m
+0 0 l
+s
+Q
+q
+1 0 0 rg
+100 80 60 20 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_451366_expected.pdf.0.png b/testing/resources/pixel/bug_451366_expected.pdf.0.png
new file mode 100644
index 0000000..b3eb5e1
--- /dev/null
+++ b/testing/resources/pixel/bug_451366_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_453723.in b/testing/resources/pixel/bug_453723.in
new file mode 100644
index 0000000..ba3219d
--- /dev/null
+++ b/testing/resources/pixel/bug_453723.in
@@ -0,0 +1,57 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /XObject << /Im0 5 0 R >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Filter [/ASCIIHexDecode /JPXDecode]
+  /Width 2550
+  /Height 3300
+  {{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000002d6a7032
+68000000166968647200000ce4000009f60003070700000000000f636f6c720100000000001000
+0001356a703263ff4fff51002f0000000009f600000ce40000000000000000000009f600000ce4
+00000000000000000003070101070101070101ff52000c00000001010504040001ff5c00134040
+484850484850484850484850484850ff640025000143726561746564206279204f70656e4a5045
+472076657273696f6e20322e332e30ff90000a0000000000ae0001ff93e7ed19fda2bfb45ff68a
+00115054afff7fe98960158e0000000000000000018484bfff7f115054afff6f7e4b081c000000
+000000061212ff7f115054afff7fff7ff9fba00000000000000018484bff7f115054afff44dbcf
+c0c0000000000018484bff7f80efe013fc023f804ff00814005cafff7fbe838014005cafff7fec
+e714005cafff7e3a04b714005caffedcc77f808080808080808080808080808080ffd9
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_453723_expected.pdf.0.png b/testing/resources/pixel/bug_453723_expected.pdf.0.png
new file mode 100644
index 0000000..e1b3d43
--- /dev/null
+++ b/testing/resources/pixel/bug_453723_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_491_invisible.in b/testing/resources/pixel/bug_491_invisible.in
index 75b7e58..56632be 100644
--- a/testing/resources/pixel/bug_491_invisible.in
+++ b/testing/resources/pixel/bug_491_invisible.in
@@ -67,9 +67,6 @@
 >>
 endobj
 {{xref}}
-trailer <<
-  /Size 9
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/pixel/bug_491_unspecified.in b/testing/resources/pixel/bug_491_unspecified.in
index b1e7312..e93c816 100644
--- a/testing/resources/pixel/bug_491_unspecified.in
+++ b/testing/resources/pixel/bug_491_unspecified.in
@@ -66,9 +66,6 @@
 >>
 endobj
 {{xref}}
-trailer <<
-  /Size 9
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/pixel/bug_491_visible.in b/testing/resources/pixel/bug_491_visible.in
index 8e21b6c..1f5ab70 100644
--- a/testing/resources/pixel/bug_491_visible.in
+++ b/testing/resources/pixel/bug_491_visible.in
@@ -67,9 +67,6 @@
 >>
 endobj
 {{xref}}
-trailer <<
-  /Size 9
-  /Root 1 0 R
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/pixel/bug_512557.in b/testing/resources/pixel/bug_512557.in
deleted file mode 100644
index 5f35334..0000000
--- a/testing/resources/pixel/bug_512557.in
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_512557.pdf b/testing/resources/pixel/bug_512557.pdf
new file mode 100644
index 0000000..8fa68f8
--- /dev/null
+++ b/testing/resources/pixel/bug_512557.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png
new file mode 100644
index 0000000..b2c134a
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png
new file mode 100644
index 0000000..786d963
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png
new file mode 100644
index 0000000..b2c134a
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png
new file mode 100644
index 0000000..b2c134a
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png
new file mode 100644
index 0000000..b2c134a
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png
new file mode 100644
index 0000000..786d963
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected_win.pdf.0.png b/testing/resources/pixel/bug_528103_expected_win.pdf.0.png
index aca0606..8ccda23 100644
--- a/testing/resources/pixel/bug_528103_expected_win.pdf.0.png
+++ b/testing/resources/pixel/bug_528103_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png
new file mode 100644
index 0000000..b2c134a
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png
new file mode 100644
index 0000000..b2c134a
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png
new file mode 100644
index 0000000..b2c134a
--- /dev/null
+++ b/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_551460.in b/testing/resources/pixel/bug_551460.in
new file mode 100644
index 0000000..2ceeefe
--- /dev/null
+++ b/testing/resources/pixel/bug_551460.in
@@ -0,0 +1,72 @@
+{{header}}
+
+{{object 1 0}}
+<<
+  /Pages 2 0 R
+>>
+endobj
+
+{{object 2 0}}
+<<
+  /Kids [ 3 0 R ]
+>>
+endobj
+
+{{object 3 0}}
+<<
+  /Contents 6 0 R
+  /Resources 5 0 R
+>>
+endobj
+
+{{object 6 0}}
+<<>>
+stream
+0.0 G
+0.0 1.0 0.0 rg
+25 175 175 -150 re
+/Sh sh
+endstream
+endobj
+
+{{object 5 0}}
+<<
+  /Shading <<
+    /Sh 7 0 R
+  >>
+>>
+endobj
+
+{{object 7 0}}
+<<
+  /ShadingType 1
+  /ColorSpace /DeviceCMYK
+  /Coords [0.0 0.0 1.0 1.0]
+  /Function 9 0 R
+  /Extend [true true]
+>>
+
+{{object 9 0}}
+<<
+  /FunctionType 3
+  /Domain [0.0 1.0 0.0 1.0]
+  /Functions [10 0 R]
+  /Bounds [2.0]
+  /Encode [0.0 1.0]
+>>
+endobj
+
+{{object 10 0}}
+<<
+  /FunctionType 2
+  /Domain [0.0 1.0 0.0 1.0]
+  /C0 [0.0 0.0]
+  /C1 [1.0 1.0]
+  /N 1
+>>
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_551460_expected.pdf.0.png b/testing/resources/pixel/bug_551460_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/bug_551460_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_554151.in b/testing/resources/pixel/bug_554151.in
new file mode 100644
index 0000000..7a68f0a
--- /dev/null
+++ b/testing/resources/pixel/bug_554151.in
@@ -0,0 +1,56 @@
+{{header}}
+
+{{object 1 0}}
+<<
+  /Pages 2 0 R
+>>
+endobj
+
+{{object 2 0}}
+<<
+  /Kids [ 3 0 R ]
+>>
+endobj
+
+{{object 3 0}}
+<<
+  /Contents 6 0 R
+  /Resources 5 0 R
+>>
+endobj
+
+{{object 6 0}}
+<<>>
+stream
+612 0.0 0.0 104000 0 0 cm
+/XBad Do
+endstream
+endobj
+
+{{object 5 0}}
+<<
+  /XObject <<
+    /XBad 7 0 R
+  >>
+>>
+endobj
+
+{{object 7 0}}
+<<
+  /Type /XObject
+  /Subtype /Image
+  /Width 612
+  /Height 104000
+  /ColorSpace /DeviceRGB
+  /BitsPerComponent 4
+  /Length 0
+  /Decode [1.0]
+>>
+stream
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_554151_expected.pdf.0.png b/testing/resources/pixel/bug_554151_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/bug_554151_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_557223.in b/testing/resources/pixel/bug_557223.in
new file mode 100644
index 0000000..acb1ff2
--- /dev/null
+++ b/testing/resources/pixel/bug_557223.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [ 0 0 24.0 24.0 ]
+  /Resources <<
+    /XObject <<
+      /Im0 5 0 R
+    >>
+  >>
+>> endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+24.000000 0 0 24.000000 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [ /ASCIIHexDecode /JPXDecode ]
+  /Height 32
+  /Width 32
+  {{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a03e8200000002d6a703268
+0000001669686472000000f3000002d00003070700000000000f636f6c7201000000000012000000
+006a703263ff4fff51002f0000000002d0000017f30000000d00000000000001d0000000f3000000
+00000000000003070101070201070201ff52000c00000001000505030000ff5c0023226f186f006f
+006ebc66ea66ea66bc5f4c5f4c5f644803480348454fd24fd24f61ff90000a0000000000000001ff
+93cfcad61942502d0d8fb0501fcbe88591e32a7f42ddefcdebec6b111ff8b9f80765ae5a61817b3c
+635324f6d6364697c0460bb659d9421bf1ca090f801329fb3987f9deac158199797557a094d44a92
+de440f347138dfb56e5de866651198e948833597545dc22ceb84ee5d32a74976bb8b8dce9bb5a926
+2acc1a348bf96baaf7836817b0f244bbd3960d5f9eb4568f416a3689df93031aa1d49a9e5741cb00
+327b18705a3a466ea24752f2f17324f7db0040121d742d34d9fcd3a727adc1f26f4d442a905f7fa8
+18f62fea08c798995df8771a71d0ef10324a3545c413c3eb4b419f6f8738c257c015c523ef4aeae2
+1a4fd933cdbac5f0210f762711b516afc09bb4eed442f58cf45e79633e098fe17bf8c983057a9eab
+44ffffa1bc00388f625fd54d1f539216ae01727dc1f4d28016ab7bc3ba8ffc0c283f1c30c6132e70
+62a2b7be93d8e35343ff7296ab991a4abf1d3dcd629f7b94376b467bd8f8fb7c6087d22d08240373
+a782187a253be320d748170d14d94481499ec7def10004ca0fa6dc987cf42d3acb8b0b646e8beb89
+0cf5131e10c3bbf7c8bfdba9e865624eb5770df98bb5edaaf534d121dcea8d6ff8346fb3255240ac
+5b4c04111afd79b3489ecf6da8201404dff1a424a3aff2f51475f423dbe249efc6d935119f1b25cd
+83e64b8670cdff22720f3796dfdc3174dfab114439a870421b74848eb0eb89c7756f749c11a0de24
+264b85e44c52eb12ff195b19b51f14acf69e2ad5ae1a77bd5a52b6e8030000273453939f701a934c
+796a77ba17f554fa91952452406fc9d339d51672ee60c311ca64291edbd1eef26d02f5c2c2101ac5
+86457b4a4ea0c44bcee87d9263d66cea3f86413aaecd58a11329fb3987f9deac1581997975bdc307
+98dc0406fd703d574d19145461757b6790a1c8a467a3612f803d5b8d2257fbf61324c4c682a919ab
+47698528c0194f421184eca9828cb143f5bf160eeef903ed
+endstream
+endobj
+{{object 6 0}} <<
+  /CreationDate (D:20150216223039)
+  /ModDate (D:20150216223039)
+>> endobj
+{{xref}}
+trailer <<
+    /Info 6 0 R
+    /Root 1 0 R
+    /Size 7
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_557223_expected.pdf.0.png b/testing/resources/pixel/bug_557223_expected.pdf.0.png
new file mode 100644
index 0000000..5ac2c59
--- /dev/null
+++ b/testing/resources/pixel/bug_557223_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_601362.in b/testing/resources/pixel/bug_601362.in
new file mode 100644
index 0000000..29caa10
--- /dev/null
+++ b/testing/resources/pixel/bug_601362.in
@@ -0,0 +1,90 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 4 0 R
+      /F2 6 0 R
+    >>
+  >>
+  /Contents 8 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /ABCXYZ+Calibri
+  /FirstChar 1
+  /LastChar 1
+  /Widths [300]
+  /FontDescriptor 5 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /FontDescriptor
+  /Ascent 680
+  /CapHeight 680
+  /Descent -177
+  /Flags 4
+  /FontBBox [0 -177 770 680]
+  /FontName /ABCXYZ+Calibri
+  /ItalicAngle 2147483649
+  /MissingWidth 506
+  /StemV 115
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /BCDEFG+Calibri
+  /FirstChar 1
+  /LastChar 1
+  /Widths [300]
+  /FontDescriptor 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /FontDescriptor
+  /Ascent 680
+  /CapHeight 680
+  /Descent -177
+  /Flags 4
+  /FontBBox [0 -177 770 680]
+  /FontName /BCDEFG+Calibri
+  /ItalicAngle -20
+  /MissingWidth 506
+  /StemV 115
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+50 100 Td
+1 Tr
+/F1 20 Tf
+(A) Tj
+/F2 20 Tf
+(A) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_601362_expected.pdf.0.png b/testing/resources/pixel/bug_601362_expected.pdf.0.png
new file mode 100644
index 0000000..5d868d5
--- /dev/null
+++ b/testing/resources/pixel/bug_601362_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/bug_603518.pdf b/testing/resources/pixel/bug_603518.pdf
similarity index 100%
rename from testing/resources/bug_603518.pdf
rename to testing/resources/pixel/bug_603518.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_603518_expected.pdf.0.png b/testing/resources/pixel/bug_603518_expected.pdf.0.png
new file mode 100644
index 0000000..6d599ab
--- /dev/null
+++ b/testing/resources/pixel/bug_603518_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632.in b/testing/resources/pixel/bug_632.in
new file mode 100644
index 0000000..31c9ee5
--- /dev/null
+++ b/testing/resources/pixel/bug_632.in
@@ -0,0 +1,100 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [3 0 R 4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [5 0 R 7 0 R]
+  /MediaBox [0 0 300 200]
+  /Resources 8 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [6 0 R 7 0 R]
+  /MediaBox [0 0 300 200]
+  /Resources 8 0 R
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+200 0 500 200 re
+endstream
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+140 0 300 200 re
+endstream
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+W
+n
+/Pattern cs
+/P1 scn
+BT
+/F1 16 Tf
+100 100 Td
+(Hello, world!) Tj
+ET
+endstream
+endobj
+{{object 8 0}} <<
+  /Font << /F1 9 0 R >>
+  /Pattern << /P1 10 0 R >>
+  /ProcSet [/PDF /Text /ImageC /ImageI /ImageB]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Pattern
+  /PatternType 2
+  /Shading 11 0 R
+>>
+endobj
+{{object 11 0}} <<
+  /ShadingType 2
+  /ColorSpace /DeviceCMYK
+  /Coords [0.0 0.0 1.0 0.0]
+  /Function 12 0 R
+  /Extend [true true]
+>>
+endobj
+{{object 12 0}} <<
+  /FunctionType 0
+  /Domain [0.0 1.0]
+  /Range [0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]
+  /Size [2]
+  /BitsPerSample 1
+  /Filter /ASCIIHexDecode
+  {{streamlen}}
+>>
+stream
+ff
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_632_expected.pdf.0.png b/testing/resources/pixel/bug_632_expected.pdf.0.png
new file mode 100644
index 0000000..3807d93
--- /dev/null
+++ b/testing/resources/pixel/bug_632_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632_expected.pdf.1.png b/testing/resources/pixel/bug_632_expected.pdf.1.png
new file mode 100644
index 0000000..141f3d8
--- /dev/null
+++ b/testing/resources/pixel/bug_632_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_660850.in b/testing/resources/pixel/bug_660850.in
new file mode 100644
index 0000000..7eded31
--- /dev/null
+++ b/testing/resources/pixel/bug_660850.in
@@ -0,0 +1,41 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 400 200]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 1 1 rg
+20 w
+4 M
+[12 28] -14 d
+40 40 m
+360 40 l
+360 160 l
+40 160 l
+h
+B
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_660850_expected.pdf.0.png b/testing/resources/pixel/bug_660850_expected.pdf.0.png
new file mode 100644
index 0000000..98281e1
--- /dev/null
+++ b/testing/resources/pixel/bug_660850_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467_expected_win.pdf.0.png b/testing/resources/pixel/bug_665467_expected_win.pdf.0.png
new file mode 100644
index 0000000..8feb717
--- /dev/null
+++ b/testing/resources/pixel/bug_665467_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_71.in b/testing/resources/pixel/bug_71.in
index 6bb6f19..974c6b9 100644
--- a/testing/resources/pixel/bug_71.in
+++ b/testing/resources/pixel/bug_71.in
Binary files differ
diff --git a/testing/resources/pixel/bug_725555.in b/testing/resources/pixel/bug_725555.in
new file mode 100644
index 0000000..783087e
--- /dev/null
+++ b/testing/resources/pixel/bug_725555.in
@@ -0,0 +1,55 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 rg
+300 170 m
+300.00036 170.00012 l
+100 450 l
+300 170 l
+h
+f
+Q
+q
+0 1 0 rg
+400 170 m
+200 450 l
+400 170 l
+h
+f
+Q
+q
+0 0 1 rg
+500 170 m
+500.00036 170.10012 l
+300 450 l
+500 170 l
+h
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_725555_expected.pdf.0.png b/testing/resources/pixel/bug_725555_expected.pdf.0.png
new file mode 100644
index 0000000..2c4c731
--- /dev/null
+++ b/testing/resources/pixel/bug_725555_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_733528.in b/testing/resources/pixel/bug_733528.in
index 3602c3f..ce2a3eb 100644
--- a/testing/resources/pixel/bug_733528.in
+++ b/testing/resources/pixel/bug_733528.in
@@ -64,9 +64,6 @@
 >>
 endobj
 {{xref}}
-trailer <<
-  /Root 1 0 R
-  /Size 10
->>
+{{trailer}}
 {{startxref}}
 %%EOF
diff --git a/testing/resources/pixel/bug_733528_expected_win.pdf.0.png b/testing/resources/pixel/bug_733528_expected_win.pdf.0.png
new file mode 100644
index 0000000..a44b932
--- /dev/null
+++ b/testing/resources/pixel/bug_733528_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png
new file mode 100644
index 0000000..ffa94f0
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png
new file mode 100644
index 0000000..d27a9b4
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345.in b/testing/resources/pixel/bug_820345.in
new file mode 100644
index 0000000..2b440d5
--- /dev/null
+++ b/testing/resources/pixel/bug_820345.in
@@ -0,0 +1,70 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+  /FontDescriptor 7 0 R
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /StemV 440
+  /ItalicAngle 0
+  /FontName /Arial
+  /Ascent 776
+  /Type /FontDescriptor
+  /Flags 33
+  /FontBBox [-250 -236 2827 1000]
+  /CapHeight 763
+  /Descent -223
+  /MissingWidth 745
+  /MaxWidth 2658
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_820345_expected.pdf.0.png b/testing/resources/pixel/bug_820345_expected.pdf.0.png
new file mode 100644
index 0000000..e3c8e24
--- /dev/null
+++ b/testing/resources/pixel/bug_820345_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png b/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png
new file mode 100644
index 0000000..6e79402
--- /dev/null
+++ b/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected_win.pdf.0.png b/testing/resources/pixel/bug_820345_expected_win.pdf.0.png
new file mode 100644
index 0000000..c3b8b43
--- /dev/null
+++ b/testing/resources/pixel/bug_820345_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_828206.pdf b/testing/resources/pixel/bug_828206.pdf
new file mode 100644
index 0000000..7015f81
--- /dev/null
+++ b/testing/resources/pixel/bug_828206.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_828206_expected.pdf.0.png b/testing/resources/pixel/bug_828206_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/bug_828206_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_830221.pdf b/testing/resources/pixel/bug_830221.pdf
new file mode 100644
index 0000000..317a526
--- /dev/null
+++ b/testing/resources/pixel/bug_830221.pdf
@@ -0,0 +1,21 @@
+%PDF
+1 0 obj
+<</Contents 1 0 R/Resources 4 0 R/K
+j
+6 0 obj
+<</Functions[7 0 R]
+/Domain [0 1]
+/FunctionType 3/>>stream
+/1 CS]2 0 obj<< /Kids[1 0 R]/ ou
+3 0 obj
+<< /Pages 2 0 R>
+endobj
+4 0 obj
+<< /ColorSpace<</1 9 0 R>endobj
+7 0 obj
+<</Functions[6 0 R]/Domain[0 1]
+/FunctionType 3
+/n
+f
+9 0 obj[/Sepa/1 /DeviceRGB 7 0 R]trailer
+<</Root 3 0 R>>
\ No newline at end of file
diff --git a/testing/resources/pixel/bug_830221_expected.pdf.0.png b/testing/resources/pixel/bug_830221_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/bug_830221_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_842.in b/testing/resources/pixel/bug_842.in
new file mode 100644
index 0000000..255a902
--- /dev/null
+++ b/testing/resources/pixel/bug_842.in
@@ -0,0 +1,99 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 720 540]
+  /Contents 4 0 R
+  /Resources <<
+    /ProcSet [/PDF /ImageB /ImageC /ImageI]
+    /Pattern <<
+      /P25 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+{{streamlen}}
+>>
+stream
+q
+/Pattern cs /P25 scn
+461.25 480.37 253.13 31.5 re
+f*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /PatternType 2
+  /Shading <<
+    /Coords [587.81 511.88 587.81 448.88]
+    /Function 6 0 R
+    /Extend [true true]
+    /ShadingType 2
+    /ColorSpace /DeviceRGB
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  /FunctionType 3
+  /Functions [7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
+  /Encode [1 0 1 0 1 0 1 0 1 0]
+  /Bounds [0.325 0.5 0.5 0.675]
+  /Domain [0 1]
+>>
+endobj
+{{object 7 0}} <<
+  /FunctionType 2
+  /N 1
+  /C0 [0.81569 0.81569 0.81569]
+  /C1 [0.92941 0.92941 0.92941]
+  /Domain [0 1]
+>>
+endobj
+{{object 8 0}} <<
+  /FunctionType 2
+  /N 1
+  /C0 [0.73725 0.73725 0.73725]
+  /C1 [0.81569 0.81569 0.81569]
+  /Domain [0 1]
+>>
+endobj
+{{object 9 0}} <<
+  /FunctionType 2
+  /N 1
+  /C0 [0.73725 0.73725 0.73725]
+  /C1 [0.73725 0.73725 0.73725]
+  /Domain [0 1]
+>>
+endobj
+{{object 10 0}} <<
+  /FunctionType 2
+  /N 1
+  /C0 [0.81569 0.81569 0.81569]
+  /C1 [0.73725 0.73725 0.73725]
+  /Domain [0 1]
+>>
+endobj
+{{object 11 0}} <<
+  /FunctionType 2
+  /N 1
+  /C0 [0.92941 0.92941 0.92941]
+  /C1 [0.81569 0.81569 0.81569]
+  /Domain [0 1]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_842_expected.pdf.0.png b/testing/resources/pixel/bug_842_expected.pdf.0.png
new file mode 100644
index 0000000..7744fe8
--- /dev/null
+++ b/testing/resources/pixel/bug_842_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_843.in b/testing/resources/pixel/bug_843.in
new file mode 100644
index 0000000..fea0512
--- /dev/null
+++ b/testing/resources/pixel/bug_843.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /MediaBox [0 0 150 150]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+65.273 116.816 m
+73.392 116.816 79.984 110.183 79.984 102.005 c
+79.984 93.822 73.392 87.189 65.273 87.189 c
+57.149 87.189 50.566 93.822 50.566 102.005 c
+50.566 110.183 57.149 116.816 65.273 116.816 c
+f*
+65.273 116.816 m
+73.392 116.816 79.984 110.183 79.984 102.005 c
+79.984 93.822 73.392 87.189 65.273 87.189 c
+57.149 87.189 50.566 93.822 50.566 102.005 c
+50.566 110.183 57.149 116.816 65.273 116.816 c
+f*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_843_expected.pdf.0.png b/testing/resources/pixel/bug_843_expected.pdf.0.png
new file mode 100644
index 0000000..26524d8
--- /dev/null
+++ b/testing/resources/pixel/bug_843_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_845697.in b/testing/resources/pixel/bug_845697.in
new file mode 100644
index 0000000..92da761
--- /dev/null
+++ b/testing/resources/pixel/bug_845697.in
@@ -0,0 +1,63 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /ABCDEE+Calibri
+  /Encoding /WinAnsiEncoding
+  /FirstChar 65
+  /LastChar 74
+  /FontDescriptor 5 0 R
+  /Name /F1
+  /Widths [300 250 250 250 250 250 250 250 250]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /FontDescriptor
+  /CapHeight 750
+  /Descent -250
+  /Flags 32
+  /FontBBox [ -503 -250 1240 750 ]
+  /FontName /ABCDEE+Calibri
+  /FontWeight 400
+  /ItalicAngle 0
+  /StemV 52
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+50 100 Td
+/F1 20 Tf
+(ABCDEFGHI) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_845697_expected.pdf.0.png b/testing/resources/pixel/bug_845697_expected.pdf.0.png
new file mode 100644
index 0000000..3bc224b
--- /dev/null
+++ b/testing/resources/pixel/bug_845697_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_867501.pdf b/testing/resources/pixel/bug_867501.pdf
new file mode 100644
index 0000000..0b517b9
--- /dev/null
+++ b/testing/resources/pixel/bug_867501.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_867501_expected.pdf.0.png b/testing/resources/pixel/bug_867501_expected.pdf.0.png
new file mode 100644
index 0000000..bd6f8c1
--- /dev/null
+++ b/testing/resources/pixel/bug_867501_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_909762.in b/testing/resources/pixel/bug_909762.in
new file mode 100644
index 0000000..652fe23
--- /dev/null
+++ b/testing/resources/pixel/bug_909762.in
@@ -0,0 +1,127 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /Font 5 0 R
+    /ProcSet [/PDF /Text]
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F0 12 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /F0 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Font
+  /Subtype /Type0
+  /Encoding 7 0 R
+  /ToUnicode 8 0 R
+  /DescendantFonts [9 0 R]
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} <<
+  /Type /CMap
+  /CMapName /OneByteIdentityH
+  /CIDSystemInfo 10 0 R
+  {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo 3 dict dup begin
+/Registry (Adobe) def
+/Ordering (Identity) def
+/Supplement 0 def
+end def
+/CMapName /OneByteIdentityH def
+/CMapVersion 1.000 def
+/CMapType 1 def
+/UIDOffset 0 def
+/XUID [1 10 25404 9999] def
+/WMode 0 def
+1 begincodespacerange
+<00> <FF>
+endcodespacerange
+1 begincidrange
+<00> <FF> 0
+endcidrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+
+endstream
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CMapType 2 def
+/CMapName/R111 def
+1 begincodespacerange
+<00><ff>
+endcodespacerange
+82 beginbfrange
+<20><20><0020>
+<21><21><0021>
+<2c><2c><002c>
+<48><48><0048>
+<64><64><0064>
+<65><65><0065>
+<6c><6c><006c>
+<6f><6f><006f>
+<72><72><0072>
+<77><77><0077>
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end end
+
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 10 0}} <<
+  /Ordering (Identity)
+  /Registry (Adobe)
+  /Supplement 0
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_909762_expected.pdf.0.png b/testing/resources/pixel/bug_909762_expected.pdf.0.png
new file mode 100644
index 0000000..219c163
--- /dev/null
+++ b/testing/resources/pixel/bug_909762_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_909762_expected_win.pdf.0.png b/testing/resources/pixel/bug_909762_expected_win.pdf.0.png
new file mode 100644
index 0000000..c6d2727
--- /dev/null
+++ b/testing/resources/pixel/bug_909762_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_925736.pdf b/testing/resources/pixel/bug_925736.pdf
new file mode 100644
index 0000000..429d53a
--- /dev/null
+++ b/testing/resources/pixel/bug_925736.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_925736_expected.pdf.0.png b/testing/resources/pixel/bug_925736_expected.pdf.0.png
new file mode 100644
index 0000000..223f650
--- /dev/null
+++ b/testing/resources/pixel/bug_925736_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png b/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png
new file mode 100644
index 0000000..f71efde
--- /dev/null
+++ b/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_963885.in b/testing/resources/pixel/bug_963885.in
new file mode 100644
index 0000000..ed9b674
--- /dev/null
+++ b/testing/resources/pixel/bug_963885.in
@@ -0,0 +1,131 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [4 0 R]
+  /MediaBox [0 0 400 400]
+  /Resources <<
+    /XObject <<
+      /Xop2 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+200 0 0 200 0 0 cm
+/Xop2 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 255
+  /Height 65536
+  /BitsPerComponent 1
+  /Filter [/ASCIIHexDecode /JBIG2Decode]
+  /ColorSpace /DeviceGray
+  {{streamlen}}
+>>
+stream
+eb6e7e48001000e41bf6849395000000000000fb9800c10028000047001f0a0000000000000000
+ffffff00000000000000fb95000000000000fb008888888888888888a7a788888888c100000000
+000000420000000000000000000000000000001f0a0000a7a7ffffff32a7a7a7a7a7a7a7ffffff
+32a7a732a7a7a7a7a7a7a7a7a7a7a7aaa7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7010009a7a7a7
+a7a7a7a7a7a7a7a708ce00a7a7a727a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a73ba7a7a7a7
+a7a7a740a7a7a7a6a7a7a7a700007c000000000000ffffbf00007b00000801622100003d000000
+00fb11b3000a50020000150000f001362e7fe2000000000001fe00542000000000000020000001
+8013000000feea00ff4a3b000000000000000000000000000000004e4e0000007f000000000000
+00000000000000004e4e0000007f0034ab6155366f534d00000000000000004e4e0000007f0000
+0000000000000000000000000000004e4e0000007f000000000000000000000000000000000010
+000000007f00000000000000000000000000000000004e4e0000007f0000000000000000000000
+0000000000004f4e0000007f00000000000000000000000000000000004e4e0000007f00000000
+000000000000000000000000004e4e0000007f00000000000000000000000000000000004e4e00
+00007f000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000004e65300002000000
+00004e4e6530000200000000004e4e0000000600000000004e4e6530000200000000004e000000
+4e4e0000007f000000000000ee00000004ffff58164ff6e7e980ddbf004002fa002c000019642f
+ce650000647effe5000000271000808000b3000a50a4a4a40000000000004e4e0000007f000000
+00000000000000000000000000004f4e0000007f00000000000000000000000000000000004e4e
+0000007f00000000000000000052525252525252525252525252eeeeeeeeeeeeeeee525252522b
+2b2b2b2b2b2b2b2b2b2b2b0000020000001b0001fdffffff27675b00001600e883e7001e008015
+007f000130001c000000000000212de7000006ff150000001600000000000000005c0000160000
+0000000000005c000000000000015555555555555555555555550000561e3e9674f05555555555
+5555555555555555555500001b00dd00ff01000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000052525252525252525252525252eeeeeeee
+eeeeeeee525252522b2b2b2b2b2b2b2b2b2b2b2b0000020000001b0001fdffffff275d00000016
+00e883e7001e008015007f000130001c000000000000212de7000006ff15000000160000000000
+0000005c00001600000000000000005c00005c00001600000000000000005c0000000000000000
+00005c00001600000000000000005c000000000000000000000000000000000000005252525252
+5252525252525252eeeeeeeeeeeeeeee525252522b2b2b2b2b2b2b2b2b2b2b2b0000020000001b
+0001fdffffff27675b00001600e883e7001e008015007f000130001c000000000000212de70000
+06ff150000001600000000000000005c00001600000000000000005c0000000000000155555555
+555555555555555555555555555555555555555555555520555555555555405555555555555555
+5555555555555500001b00dd00ff01000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000052525252525252525252525252eeeeeeeeeeeeee
+ee525252522b2b2b2b2b2b2b2b2b2b2b2b0000020000001b0001fdffffff275d0000001600e883
+e7001e008015007f000130001c000000000000212de7000006ff15000000160000000000000000
+5c00001600000000000000005c0000555555555555555555555555555555555555205555555555
+554055555555555555555555555555555500001b00dd00ff010000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000052525252525252525252
+525252eeeeeeeeeeeeeeee525252522b2b2b2b2b2b2b2b2b2b2b2b2b976e3f1e56000001fdffff
+ff275d0000001600e883e7001e008015007f000130001c000000000000212de7000006ff150000
+001600000000000000005c00001600000000000000005c000000000000000000005c0000160000
+0000000000005c0000000000000000000000000000000000000052525252525252525252525252
+eeeeeeeeeeeeeeee525252522b2b2b2b2b2b2b2b2b2b2b2b0000020000001b0001fdffffff2767
+5b00001600e883e7001e008015007f000130001c000000000000212de7000006ff150000001600
+000000000000005c00001600000000000000005c00000000000001555555555555555555555555
+555555555555555555555555555555205555555555554055555555555555555555555555555500
+001b00dd00ff010000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000052525252525252525252525252eeeeeeeeeeeeeeee525252522b2b2b
+2b2b2b2b2b2b2b2b2b0000020000001b0001fdffffff275d0000001600e883e7001e008015007f
+000130001c000000000000212de7000006ff150000001600000000000000005c00001600000000
+000000005c00000000000000000000004e4e0000007f0000000000000000000000000000000000
+4e4e0000007f00000000000000000000000000000000004e4e0000007f000000000000ee000000
+04ffff58164ff6e7e980ddbf004002fa002c000019642fce650000647effe50000002710008080
+00b3000a50a4a4a415c5a4a476000000efff000000000001555555555555555555555555555555
+555555555555555555555555205555555555554055007f000130001c000000000000212de70000
+06ff150000001600000000000000005c00001600000000000000005c0000000000000000000000
+000000000000000052525252525252525252525252eeeeeeeeeeeeeeee525252522b2b2b2b2b2b
+2b2b2b2b2b2b0000020000001b0001fdffffff27675b00001600e883e7001e008015007f000130
+001c000000000000212de7000006ff150000001600000000000000005c00001600000000000000
+005c000000000000015555555555555555555555550000561e3e9674f055555555555555555555
+555555555500001b00dd00ff010000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000052525252525252525252525252eeeeeeeeeeeeeeee52
+5252522b2b2b2b2b2b2b2b2b2b2b2b0000020000001b0001fdffffff275d0000001600e883e700
+1e008015007f000130001c000000000000212de7000006ff150000001600000000000000005c00
+001600000000000000005c00005c00001600000000000000005c000000000000000000005c0000
+1600000000000000005c0000000000000000000000000000000000000052525252525252525252
+525252eeeeeeeeeeeeeeee525252522b2b2b2b2b2b2b2b2b2b2b2b0000020000001b0001fdffff
+ff27675b00001600e883e7001e008015007f000130001c000000000000212de7000006ff150000
+001600000000000000005c00001600000000000000005c00000000000001555555555555555555
+555555555555555555555555555555555555205555555555554055555555555555555555555555
+555500001b00dd00ff010000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000052525252525252525252525252eeeeeeeeeeeeeeee52525252
+2b2b2b2b2b2b2b2b2b2b2b2b0000020000001b0001fdffffff275d0000001600e883e7001e0080
+15007f000130001c000000000000212de7000006ff150000001600000000000000005c00001600
+000000000000005c0000
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_963885_expected.pdf.0.png b/testing/resources/pixel/bug_963885_expected.pdf.0.png
new file mode 100644
index 0000000..1ff3f1f
--- /dev/null
+++ b/testing/resources/pixel/bug_963885_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_981288.in b/testing/resources/pixel/bug_981288.in
new file mode 100644
index 0000000..749e745
--- /dev/null
+++ b/testing/resources/pixel/bug_981288.in
@@ -0,0 +1,67 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [3 0 R]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 595 842]
+  /Resources 4 0 R
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /ExtGState <<
+    /GSa 5 0 R
+  >>
+  /ColorSpace <<
+    /PCSp  [/Pattern /DeviceRGB]
+  >>
+  /Pattern <<
+    /Pat9 7 0 R
+  >>
+>>
+endobj
+{{object 5 0}} <<
+  /Type /ExtGState
+  /ca 1.0
+  /SA true
+  /CA 1.0
+  /AIS false
+  /SMask /None
+  /SM 0.02
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+/PCSp cs
+/Pat9 scn
+/GSa gs
+q
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  /TilingType 1
+  /PaintType 1
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_981288_expected.pdf.0.png b/testing/resources/pixel/bug_981288_expected.pdf.0.png
new file mode 100644
index 0000000..9fed69a
--- /dev/null
+++ b/testing/resources/pixel/bug_981288_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_981347.in b/testing/resources/pixel/bug_981347.in
new file mode 100644
index 0000000..843768a
--- /dev/null
+++ b/testing/resources/pixel/bug_981347.in
@@ -0,0 +1,66 @@
+{{header}}
+{{object 1 0}} <<
+   /Type /Catalog
+   /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents [ 4 0 R ]
+  /Resources <<
+    /ColorSpace <<
+      /Cs1P [ /Pattern 5 0 R ]
+    >>
+    /Pattern <<
+      /Pt1 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+/Cs1P cs
+/Pt1 scn
+endstream
+endobj
+{{object 5 0}} [
+  /DeviceN
+  [ /Black ]
+  /DeviceCMYK
+  7 0 R
+]
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PaintType 1
+  /PatternType 1
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+  /FunctionType 4
+  /Domain [ 0 1 ]
+  /Range [ 0 1 0 1 0 1 0 1 ]
+  {{streamlen}}
+>>
+stream
+{
+0 %c
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_981347_expected.pdf.0.png b/testing/resources/pixel/bug_981347_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/bug_981347_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_984811.in b/testing/resources/pixel/bug_984811.in
new file mode 100644
index 0000000..b0c0539
--- /dev/null
+++ b/testing/resources/pixel/bug_984811.in
@@ -0,0 +1,137 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /XObject <<
+      /X1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/X1 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 612 792]
+  /Resources <<
+    /Pattern <<
+      /P1 6 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+q
+/Pattern cs
+/P1 scn
+0 0 612 792 re f
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  /TilingType 1
+  /PaintType 1
+  /BBox [0 0 600 900]
+  /XStep 2876
+  /YStep 2876
+  /Resources <<
+    /XObject <<
+      /X2 7 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/X2 Do
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 600 900]
+  /Resources <<
+    /Font <<
+      /F1 8 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+BT
+100 200 Td
+/F1 16 Tf
+(   ) Tj
+ET
+Q
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /FirstChar 32
+  /BaseFont /AAAAAD+Test
+  /FontDescriptor 9 0 R
+  /ToUnicode 10 0 R
+  /LastChar 32
+  /Widths [1055]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /FontDescriptor
+  /Descent -68
+  /MissingWidth 1000
+  /CapHeight 1149
+  /StemV 0
+  /FontFile2 11 0 R
+  /Flags 4
+  /FontBBox [0 -215 1000 932]
+  /FontName /AAAAAD+Test
+  /ItalicAngle 0
+  /Ascent 933
+>>
+endobj
+{{object 10 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1388_cmap.fragment}}
+endstream
+endobj
+{{object 11 0}} <<
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+{{include ../bug_1388_truetype_font.fragment}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_984811_expected.pdf.0.png b/testing/resources/pixel/bug_984811_expected.pdf.0.png
new file mode 100644
index 0000000..3dd1900
--- /dev/null
+++ b/testing/resources/pixel/bug_984811_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png b/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png
new file mode 100644
index 0000000..e1ae9f2
--- /dev/null
+++ b/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected_win.pdf.0.png b/testing/resources/pixel/font_size_expected_win.pdf.0.png
new file mode 100644
index 0000000..a0d56aa
--- /dev/null
+++ b/testing/resources/pixel/font_size_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1.pdf b/testing/resources/pixel/generation_numbers1.pdf
new file mode 100644
index 0000000..2f63f04
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers1.pdf
@@ -0,0 +1,133 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000303 00000 n 
+0000000381 00000 n 
+0000000457 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+591
+%%EOF
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+xref
+0 7
+0000000006 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000798 00000 n 
+0000000303 00000 n 
+0000000381 00000 n 
+0000000000 00001 f 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+  /Prev 591
+>>
+startxref
+849
+%%EOF
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 1 R
+>>
+endobj
+6 1 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Goodbye, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000001068 00000 n 
+0000000303 00000 n 
+0000000381 00000 n 
+0000001210 00001 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+  /Prev 849
+>>
+startxref
+1344
+%%EOF
diff --git a/testing/resources/pixel/generation_numbers1_expected.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
new file mode 100644
index 0000000..f2d3ba9
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png
new file mode 100644
index 0000000..8d973ac
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png
new file mode 100644
index 0000000..7f1047c
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2.pdf b/testing/resources/pixel/generation_numbers2.pdf
new file mode 100644
index 0000000..9ad64a8
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers2.pdf
@@ -0,0 +1,147 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000303 00000 n 
+0000000381 00000 n 
+0000000457 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+591
+%%EOF
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 2
+  /Kids [ 7 0 R 7 0 R ]
+>>
+endobj
+7 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+>>
+endobj
+xref
+0 4
+0000000003 65535 f 
+0000000015 00000 n 
+0000000798 00000 n 
+0000000006 00001 f 
+6 2
+0000000000 00001 f 
+0000000897 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+  /Prev 591
+>>
+startxref
+948
+%%EOF
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 1 R ]
+>>
+endobj
+3 1 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 1 R
+>>
+endobj
+6 1 obj <<
+  /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Goodbye, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+xref
+0 4
+0000000007 65535 f 
+0000000015 00000 n 
+0000001151 00000 n 
+0000001244 00001 n 
+6 2
+0000001386 00001 n 
+0000000000 00001 f 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+  /Prev 948
+>>
+startxref
+1520
+%%EOF
diff --git a/testing/resources/pixel/generation_numbers2_expected.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
new file mode 100644
index 0000000..f2d3ba9
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png
new file mode 100644
index 0000000..8d973ac
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png
new file mode 100644
index 0000000..7f1047c
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/lzw1.in b/testing/resources/pixel/lzw1.in
new file mode 100644
index 0000000..46914c1
--- /dev/null
+++ b/testing/resources/pixel/lzw1.in
@@ -0,0 +1,55 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /XObject <<
+      /Im0 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Filter [/ASCIIHexDecode /LZWDecode /JPXDecode]
+  /Width 612
+  /Height 792
+  {{streamlen}}
+>>
+stream
+80002040c351404020068290e0a8100028663a1e4e06a380c8410d004522d0d16c68d10d0b1a4d06
+439408061881008c8000181c0f180003cc66f361c80330084358d1a31bfc9eff288005f189549a51
+30a24ae5947a44b00e0100d3ea3507f948000c86d400a040200002ff2e0002640201209050b35a2c
+f69b4bfe5625af10ce465309d0ca6410188f220279c0ca6e251408a47101d8ca72399a4de6e100c8
+5c33170c1fe9000432303faf3fd26cf5a14022502a2bdfe7f369a0b480980c12097d1801be801c05
+0005c57bf818daa6ee5fed9801
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/lzw1_expected.pdf.0.png b/testing/resources/pixel/lzw1_expected.pdf.0.png
new file mode 100644
index 0000000..e1b3d43
--- /dev/null
+++ b/testing/resources/pixel/lzw1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/matte.in b/testing/resources/pixel/matte.in
new file mode 100644
index 0000000..57d8e13
--- /dev/null
+++ b/testing/resources/pixel/matte.in
@@ -0,0 +1,172 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 100 150]
+  /Contents [4 0 R]
+  /Resources <<
+    /XObject <<
+      /Mask 5 0 R
+      /NoMask 7 0 R
+      /MaskTooFewComps 9 0 R
+      /MaskTooManyComps 11 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+40 0 0 60 0 0 cm
+/Mask Do
+Q
+q
+40 0 0 60 50 0 cm
+/NoMask Do
+Q
+q
+40 0 0 60 0 90 cm
+/MaskTooFewComps Do
+Q
+q
+40 0 0 60 50 90 cm
+/MaskTooManyComps Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 6 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2 1]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 8 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 10 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /SMask 12 0 R
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 12 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceGray
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Matte [0.0 0.2 1 0.5]
+  {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/matte_expected.pdf.0.png b/testing/resources/pixel/matte_expected.pdf.0.png
new file mode 100644
index 0000000..41c46c0
--- /dev/null
+++ b/testing/resources/pixel/matte_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_border.in b/testing/resources/pixel/radial_shading_point_at_border.in
new file mode 100644
index 0000000..498c04e
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border.in
@@ -0,0 +1,53 @@
+{{header}}
+{{object 1 0}} <<
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 400]
+  /Contents 4 0 R
+  /Resources <<
+    /Shading <<
+      /R9 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+50 0 0 50 150 100 cm
+10 10 280 380 re f
+/R9 sh
+endstream
+endobj
+{{object 5 0}} <<
+  /ShadingType 3
+  /ColorSpace /DeviceRGB
+  % Start is a point on the border of end
+  /Coords [-.223151 -.974784 0.0 0.0 0.0 1.0]
+  /Function 6 0 R
+  /Extend [true true]
+ >>
+endobj
+{{object 6 0}} <<
+  /FunctionType 2
+  /Domain [0.0 1.0]
+  /C0 [0.1 0.2 0.8]
+  /C1 [0.05 0.01 0.4]
+  /N 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/radial_shading_point_at_border_expected.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_border_expected.pdf.0.png
new file mode 100644
index 0000000..1640803
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_border_no_extend.in b/testing/resources/pixel/radial_shading_point_at_border_no_extend.in
new file mode 100644
index 0000000..f41a72e
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border_no_extend.in
@@ -0,0 +1,53 @@
+{{header}}
+{{object 1 0}} <<
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 400]
+  /Contents 4 0 R
+  /Resources <<
+    /Shading <<
+      /R9 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+50 0 0 50 150 100 cm
+10 10 280 380 re f
+/R9 sh
+endstream
+endobj
+{{object 5 0}} <<
+  /ShadingType 3
+  /ColorSpace /DeviceRGB
+  % Start is a point on the border of end
+  /Coords [-.223151 -.974784 0.0 0.0 0.0 1.0]
+  /Function 6 0 R
+  /Extend [false false]
+ >>
+endobj
+{{object 6 0}} <<
+  /FunctionType 2
+  /Domain [0.0 1.0]
+  /C0 [0.1 0.2 0.8]
+  /C1 [0.05 0.01 0.4]
+  /N 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected.pdf.0.png
new file mode 100644
index 0000000..dfceaf3
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_center.in b/testing/resources/pixel/radial_shading_point_at_center.in
new file mode 100644
index 0000000..d584a41
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_center.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 400]
+  /Contents 4 0 R
+  /Resources <<
+    /Shading <<
+      /R9 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+50000 0 0 50000 150 100 cm
+10 10 280 380 re f
+/R9 sh
+endstream
+endobj
+{{object 5 0}} <<
+  /ShadingType 3
+  /ColorSpace /DeviceRGB
+  /Coords [0.0 0.0 0.0 0.0 0.0 0.005] % Concentric circles
+  /Function 6 0 R
+  /Extend [true true]
+ >>
+endobj
+{{object 6 0}} <<
+  /FunctionType 2
+  /Domain [0.0 1.0]
+  /C0 [0.8 0.1 0.2]
+  /C1 [0.2 0.025 0.05]
+  /N 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/radial_shading_point_at_center_expected.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_center_expected.pdf.0.png
new file mode 100644
index 0000000..c69c190
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_center_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/type3.in b/testing/resources/pixel/type3.in
new file mode 100644
index 0000000..bfd7966
--- /dev/null
+++ b/testing/resources/pixel/type3.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 1 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 150]
+  /Resources <<
+    /ProcSet [/PDF /Text]
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 1 Tf
+2 50 10 0.4 20 55 Tm
+(A)Tj
+8 0 0 8 40 55 Tm
+(A)Tj
+4 4 4 0 60 55 Tm
+(A)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /FontBBox [0 0 32 52]
+  /FontMatrix [0.02 0 0 0.02 0 0]
+  /CharProcs <<
+    /C1 6 0 R
+  >>
+  /Encoding <<
+    /Type /Encoding
+    /Differences [65 /C1]
+  >>
+  /FirstChar 65
+  /LastChar 65
+  /Widths [18]
+  /Resources <<
+    /ProcSet [/PDF /ImageB]
+  >>
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/type3_expected.pdf.0.png b/testing/resources/pixel/type3_expected.pdf.0.png
new file mode 100644
index 0000000..6837bb8
--- /dev/null
+++ b/testing/resources/pixel/type3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/barcode_test.evt b/testing/resources/pixel/xfa_specific/barcode_test.evt
new file mode 100644
index 0000000..0844042
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/barcode_test.evt
@@ -0,0 +1,10 @@
+mousedown,left,200,95
+mouseup,left,200,95
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+mousedown,left,500,95
+mouseup,left,500,95
diff --git a/testing/resources/xfa/barcode_test.pdf b/testing/resources/pixel/xfa_specific/barcode_test.pdf
similarity index 100%
rename from testing/resources/xfa/barcode_test.pdf
rename to testing/resources/pixel/xfa_specific/barcode_test.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/barcode_test_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/barcode_test_expected.pdf.0.png
new file mode 100644
index 0000000..f49d591
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/barcode_test_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png
new file mode 100644
index 0000000..23b80a7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1026918.evt b/testing/resources/pixel/xfa_specific/bug_1026918.evt
new file mode 100644
index 0000000..3a16c14
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1026918.evt
@@ -0,0 +1,2 @@
+mousemove,0,0
+mousedown,left,0,0
\ No newline at end of file
diff --git a/testing/resources/pixel/xfa_specific/bug_1026918.in b/testing/resources/pixel/xfa_specific/bug_1026918.in
new file mode 100644
index 0000000..3dee61e
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1026918.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /XFA 4 0 R
+  >>
+  /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 3 3]
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+  <config>
+    <acrobat>
+      <acrobat7>
+        <dynamicRender>required</dynamicRender>
+      </acrobat7>
+    </acrobat>
+  </config>
+  <template>
+    <subform>
+      <bookend leader="$"></bookend>
+      <field>
+        <keep next="pageArea"></keep>
+        <ui>
+          <barcode type="code3Of9"></barcode>
+        </ui>
+      </field>
+    </subform>
+  </template>
+</xdp>
+endstream
+endobj
+{{trailer}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/xfa_specific/bug_1026918_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1026918_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1026918_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1258_1.in b/testing/resources/pixel/xfa_specific/bug_1258_1.in
new file mode 100644
index 0000000..8777b06
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1258_1.in
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1258_1_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1258_1_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1258_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1258_2.in b/testing/resources/pixel/xfa_specific/bug_1258_2.in
new file mode 100644
index 0000000..01740f3
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1258_2.in
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1258_2_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1258_2_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1258_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1258_3.in b/testing/resources/pixel/xfa_specific/bug_1258_3.in
new file mode 100644
index 0000000..b0c7d8c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1258_3.in
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1258_3_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1258_3_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1258_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1269_1.in b/testing/resources/pixel/xfa_specific/bug_1269_1.in
new file mode 100644
index 0000000..ec7b6da
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1269_1.in
@@ -0,0 +1,18 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_single_2_0.fragment}}
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+<subform use=" .[$host.calculationsEnabled=$]">
+<field>
+endstream
+endobj
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1269_1_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1269_1_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1269_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1269_2.in b/testing/resources/pixel/xfa_specific/bug_1269_2.in
new file mode 100644
index 0000000..80425e9
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1269_2.in
@@ -0,0 +1,19 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_single_2_0.fragment}}
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+<defaultUi use=" .[c=$]">
+<field>
+<contentArea name="c">
+endstream
+endobj
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1269_2_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1269_2_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1269_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1269_3.in b/testing/resources/pixel/xfa_specific/bug_1269_3.in
new file mode 100644
index 0000000..5bbbeb6
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1269_3.in
@@ -0,0 +1,18 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_single_2_0.fragment}}
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+<template use=" .[Space($)]">
+<field>
+endstream
+endobj
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1269_3_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1269_3_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1269_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1270.in b/testing/resources/pixel/xfa_specific/bug_1270.in
new file mode 100644
index 0000000..abf318e
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1270.in
@@ -0,0 +1,18 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_single_2_0.fragment}}
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+<subform use="$">
+<field>
+endstream
+endobj
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1270_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1270_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1270_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1272.in b/testing/resources/pixel/xfa_specific/bug_1272.in
new file mode 100644
index 0000000..290e5d4
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1272.in
@@ -0,0 +1,18 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_single_2_0.fragment}}
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+<subform use=" .[$.clone(1)]">
+<bindItems>
+endstream
+endobj
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1272_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1272_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1272_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1273.in b/testing/resources/pixel/xfa_specific/bug_1273.in
new file mode 100644
index 0000000..4f8f144
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1273.in
@@ -0,0 +1,19 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_single_2_0.fragment}}
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template>
+<subform>
+<breakBefore>
+<script>host.openList("""")</script>
+endstream
+endobj
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1273_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1273_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1273_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1282.in b/testing/resources/pixel/xfa_specific/bug_1282.in
new file mode 100644
index 0000000..61d8a0d
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1282.in
@@ -0,0 +1,65 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{object 2 0}} <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (datasets)
+    10 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.8/">
+  <subform name="topmostSubform" layout="tb" locale="en_US">
+    <pageSet>
+      <pageArea id="PageArea1" name="PageArea1">
+        <contentArea name="ContentArea1" x="0pt" y="0pt" w="612pt" h="792pt" />
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform name="Page1" x="0pt" y="0pt" w="612pt" h="792pt">
+      <field name="foo.bar" x="100pt" y="100pt" w="60pt" h="16pt">
+        <ui><choiceList open="multiSelect" /></ui>
+        <value><text>Select one</text></value>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{object 10 0}} <<
+  {{streamlen}}
+>>
+stream
+<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
+  <xfa:data>
+    <topmostSubform>
+      <foo.bar>
+        <value>Select one</value>
+      </foo.bar>
+    </topmostSubform>
+  </xfa:data>
+</xfa:datasets>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1282_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1282_expected.pdf.0.png
new file mode 100644
index 0000000..6b03382
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1282_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_983137.in b/testing/resources/pixel/xfa_specific/bug_983137.in
new file mode 100644
index 0000000..5b3b989
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_983137.in
@@ -0,0 +1,51 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform layout="tb" name="subform1">
+    <pageSet id="page" relation="orderedOccurrence">
+      <occur initial="1" max="1" min="1"/>
+      <pageArea id="Page1" name="Page1">
+        <occur max="1" min="1"/>
+        <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+        <medium long="297mm" short="210mm" stock="a4"/>
+      </pageArea>
+    </pageSet>
+    <event activity="docReady" ref="$host">
+      <script contentType="application/x-javascript">
+      </script>
+    </event>
+    <subform layout="tb" name="subform2">
+      <variables name="vara">
+        <script contentType="application/x-javascript" name="srpt">
+          var z = xfa.record.nodes;
+          z.append(this)
+        </script>
+      </variables>
+      <occur initial="1" max="10" min="0" name="occur1">
+      </occur>
+      <field h="10mm" name="field1" w="40mm" x="10mm" y="10mm">
+        <event activity="ready" ref="$form">
+          <script contentType="application/x-javascript">
+            var b = xfa.resolveNode("vara.srpt");
+          </script>
+        </event>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_983137_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_983137_expected.pdf.0.png
new file mode 100644
index 0000000..e4e08ee
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_983137_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection.evt b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection.evt
new file mode 100644
index 0000000..ec52e98
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection.evt
@@ -0,0 +1,5 @@
+mousedown,left,370,114
+mouseup,left,370,114
+
+mousedown,left,370,145,shift
+mouseup,left,370,145,shift
diff --git a/testing/resources/xfa/dynamic_list_box_allow_multiple_selection.pdf b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection.pdf
similarity index 100%
rename from testing/resources/xfa/dynamic_list_box_allow_multiple_selection.pdf
rename to testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected.pdf.0.png
new file mode 100644
index 0000000..aded8b5
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_mac.pdf.0.png
new file mode 100644
index 0000000..aebb572
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill.evt b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill.evt
new file mode 100644
index 0000000..7750a6f
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill.evt
@@ -0,0 +1,116 @@
+mousedown,left,250,140
+mouseup,left,250,140
+
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+keycode,9
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+
+mousedown,left,0,0
+mouseup,left,0,0
diff --git a/testing/resources/xfa/dynamic_password_field_background_fill.pdf b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill.pdf
similarity index 100%
rename from testing/resources/xfa/dynamic_password_field_background_fill.pdf
rename to testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png
new file mode 100644
index 0000000..d20d7ea
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png
new file mode 100644
index 0000000..734b7e4
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/xfa/dynamic_table_color_and_width.pdf b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width.pdf
similarity index 100%
rename from testing/resources/xfa/dynamic_table_color_and_width.pdf
rename to testing/resources/pixel/xfa_specific/dynamic_table_color_and_width.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected.pdf.0.png
new file mode 100644
index 0000000..1375930
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png
new file mode 100644
index 0000000..eae383c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/resolve_nodes_0.evt b/testing/resources/pixel/xfa_specific/resolve_nodes_0.evt
new file mode 100644
index 0000000..e8518ab
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/resolve_nodes_0.evt
@@ -0,0 +1,3 @@
+mousedown,left,550,30
+mouseup,left,550,30
+
diff --git a/testing/resources/xfa/resolve_nodes.pdf b/testing/resources/pixel/xfa_specific/resolve_nodes_0.pdf
similarity index 100%
rename from testing/resources/xfa/resolve_nodes.pdf
rename to testing/resources/pixel/xfa_specific/resolve_nodes_0.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png
new file mode 100644
index 0000000..a3d9830
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/xfa/standard_symbols.pdf b/testing/resources/pixel/xfa_specific/standard_symbols.pdf
similarity index 100%
rename from testing/resources/xfa/standard_symbols.pdf
rename to testing/resources/pixel/xfa_specific/standard_symbols.pdf
Binary files differ
diff --git a/testing/resources/xfa/static_list_box_caption.pdf b/testing/resources/pixel/xfa_specific/static_list_box_caption.pdf
similarity index 100%
rename from testing/resources/xfa/static_list_box_caption.pdf
rename to testing/resources/pixel/xfa_specific/static_list_box_caption.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected.pdf.0.png
new file mode 100644
index 0000000..b164cc4
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png
new file mode 100644
index 0000000..7cba6b9
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png
new file mode 100644
index 0000000..71edafd
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/xfa/static_password_field_rotate.pdf b/testing/resources/pixel/xfa_specific/static_password_field_rotate.pdf
similarity index 100%
rename from testing/resources/xfa/static_password_field_rotate.pdf
rename to testing/resources/pixel/xfa_specific/static_password_field_rotate.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected.pdf.0.png
new file mode 100644
index 0000000..1565e3e
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected.pdf.1.png
new file mode 100644
index 0000000..c73f1a9
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
new file mode 100644
index 0000000..58aee9e
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png
new file mode 100644
index 0000000..ad36aa9
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png
new file mode 100644
index 0000000..ea939ac
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png
new file mode 100644
index 0000000..c73f1a9
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_example.in b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example.in
new file mode 100644
index 0000000..2d8a1cb
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example.in
@@ -0,0 +1,36 @@
+{{header}}
+{{include ../../../xfa_catalog_1_0.fragment}}
+{{include ../../../xfa_object_2_0.fragment}}
+{{include ../../../xfa_preamble_3_0.fragment}}
+{{include ../../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="zh_CN" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="0.25in" y="0.25in" w="7.75in" h="11.25in"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <draw name="HelloWorld" y="0.5in" x="0.5in" w="5in" h="2in">
+       <font typeface="Ahem" size="20pt"/>
+        <value>
+          <text>Hello, world.</text>
+        </value>
+      </draw>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../../xfa_locale_6_0.fragment}}
+{{include ../../../xfa_postamble_7_0.fragment}}
+{{include ../../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected.pdf.0.png
new file mode 100644
index 0000000..e1e62cc
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield.evt b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield.evt
new file mode 100644
index 0000000..1279468
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield.evt
@@ -0,0 +1,8 @@
+mousedown,left,20,20
+charcode,72
+charcode,101
+charcode,108
+charcode,108
+charcode,111
+charcode,33
+mousedown,left,20,60
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield.in b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield.in
new file mode 100644
index 0000000..31a37ea
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield.in
@@ -0,0 +1,55 @@
+{{header}}
+{{include ../../../xfa_catalog_1_0.fragment}}
+{{include ../../../xfa_object_2_0.fragment}}
+{{include ../../../xfa_preamble_3_0.fragment}}
+{{include ../../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="TextField1" y="0pt" x="0pt" w="200pt" h="32pt">
+        <font typeface="Ahem" size="20pt"/>
+        <ui>
+          <textEdit/>
+        </ui>
+        <para vAlign="middle"/>
+      </field>
+      <field name="Button1" y="42pt" x="0pt" w="100pt" h="32pt">
+        <ui>
+          <button highlight="inverted"/>
+        </ui>
+        <font typeface="Ahem" size="20pt"/>
+        <caption>
+          <value>
+            <text>Dummy</text>
+          </value>
+          <para vAlign="middle" hAlign="center"/>
+        </caption>
+        <border hand="right">
+          <edge stroke="raised"/>
+          <fill>
+            <color value="212,208,200"/>
+          </fill>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../../xfa_locale_6_0.fragment}}
+{{include ../../../xfa_postamble_7_0.fragment}}
+{{include ../../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected.pdf.0.png
new file mode 100644
index 0000000..4d9a934
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_bmp_image.in b/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
new file mode 100644
index 0000000..9b444f1
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/bmp">Qk0qHgAAAAAAAHoAAABsAAAAMgAAADIAAAABABgAAAAAALAdAAATCwAAEwsAAAAAAAAAAAAAQkdScwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected.pdf.0.png
new file mode 100644
index 0000000..5204fd7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_gif_image.in b/testing/resources/pixel/xfa_specific/xfa_gif_image.in
new file mode 100644
index 0000000..2bf4f86
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_gif_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/gif">R0lGODdh+gD6AIABAP8AAP///ywAAAAA+gD6AAAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2Oz+v3/L7/DxgoOEhYaHiImKi4yNjo+AgZKTlJWWl5iZmpucnZ6fkJGio6SlpqeoqaqrrK2ur6ChsrO0tba3uLm6u7y9vr+wscLDxMXGx8jJysvMzc7PwMHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fLz9PX29/j5+vv8/f7/8PMKDAgQQLGjyIMKHChQwbOnwIMaLEiRQrWryIMaPGV40cO3r8CDKkyJEkS5o8iTKlypUsW7p8CTOmzJk0a9q8iTOnzp08e/r8CTSo0KFEixo9ijSp0qVMmzp9CjWq1KlUq1q9ijWr1q1cu3r9Cjas2LFky14oAAA7</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_gif_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_gif_image_expected.pdf.0.png
new file mode 100644
index 0000000..5204fd7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_gif_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_jpg_image.in b/testing/resources/pixel/xfa_specific/xfa_jpg_image.in
new file mode 100644
index 0000000..72e54db
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_jpg_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/jpeg">/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAD6APoDAREAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAWAQEBAQAAAAAAAAAAAAAAAAAACAn/2gAMAwEAAhADEAAAAYvlPfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAACg/9oACAEBAAEFAgAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAoP/aAAgBAwEBPwEAH//EABQRAQAAAAAAAAAAAAAAAAAAAKD/2gAIAQIBAT8BAB//xAAUEAEAAAAAAAAAAAAAAAAAAACg/9oACAEBAAY/AgAf/8QAFBABAAAAAAAAAAAAAAAAAAAAoP/aAAgBAQABPyEAH//aAAwDAQACAAMAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAoP/aAAgBAwEBPxAAH//EABQRAQAAAAAAAAAAAAAAAAAAAKD/2gAIAQIBAT8QAB//xAAUEAEAAAAAAAAAAAAAAAAAAACg/9oACAEBAAE/EAAf/9k=</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected.pdf.0.png
new file mode 100644
index 0000000..283cded
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/xfa/xfa_node_caption.pdf b/testing/resources/pixel/xfa_specific/xfa_node_caption.pdf
similarity index 100%
rename from testing/resources/xfa/xfa_node_caption.pdf
rename to testing/resources/pixel/xfa_specific/xfa_node_caption.pdf
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
new file mode 100644
index 0000000..6855361
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
new file mode 100644
index 0000000..1f5c1bb
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_png_image.in b/testing/resources/pixel/xfa_specific/xfa_png_image.in
new file mode 100644
index 0000000..9460234
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_png_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/png">iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AQMAAACyIsh+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gkbExkvM+ErrgAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAA1BMVEX/AAAZ4gk3AAAAHklEQVQYGe3BAQEAAACCoP6vdkjAAAAAAAAAAIBjASA6AAHLOxA4AAAAAElFTkSuQmCC</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_png_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_png_image_expected.pdf.0.png
new file mode 100644
index 0000000..5204fd7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_png_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_rectangle_node.in b/testing/resources/pixel/xfa_specific/xfa_rectangle_node.in
new file mode 100644
index 0000000..1bda198
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_rectangle_node.in
@@ -0,0 +1,41 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <draw h="11.43mm" name="Rectangle3" w="30.48mm" x="170.18mm" y="65.868mm">
+        <ui>
+          <defaultUi />
+        </ui>
+        <value>
+          <rectangle>
+            <corner radius="5.08mm" />
+            <edge />
+          </rectangle>
+        </value>
+      </draw>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected.pdf.0.png
new file mode 100644
index 0000000..a7eb49e
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in
new file mode 100644
index 0000000..797f84c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/tiff">SUkqAAQDAAB4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjoO69g5xxIA/gAEAAEAAAAAAAAAAAEDAAEAAAD6AAAAAQEDAAEAAAD6AAAAAgEDAAMAAADyAwAAAwEDAAEAAAAIAAAABgEDAAEAAAACAAAADQECAEIAAAAYBAAADgECABIAAABaBAAAEQEEAAQAAAAIBAAAEgEDAAEAAAABAAAAFQEDAAEAAAADAAAAFgEDAAEAAABAAAAAFwEEAAQAAAD4AwAAGgEFAAEAAADiAwAAGwEFAAEAAADqAwAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAPQEDAAEAAAACAAAAAAAAAEgAAAABAAAASAAAAAEAAAAIAAgACADDAAAAwwAAAMMAAACzAAAACAAAAMsAAACOAQAAUQIAAC91c3IvbG9jYWwvZ29vZ2xlL2hvbWUvcmhhcnJpc29uL1BpY3R1cmVzL1JlZF9TcXVhcmVfZGVmbGF0ZS50aWZmAENyZWF0ZWQgd2l0aCBHSU1QAA==</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png
new file mode 100644
index 0000000..5204fd7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_image.in b/testing/resources/pixel/xfa_specific/xfa_tiff_image.in
new file mode 100644
index 0000000..3686715
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/tiff">SUkqAFQdAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAARAP4ABAABAAAAAAAAAAABAwABAAAAMgAAAAEBAwABAAAAMgAAAAIBAwADAAAANh4AAAMBAwABAAAAAQAAAAYBAwABAAAAAgAAAA0BAgA6AAAAPB4AAA4BAgASAAAAdh4AABEBBAABAAAACAAAABIBAwABAAAAAQAAABUBAwABAAAAAwAAABYBAwABAAAAQAAAABcBBAABAAAATB0AABoBBQABAAAAJh4AABsBBQABAAAALh4AABwBAwABAAAAAQAAACgBAwABAAAAAgAAAAAAAABIAAAAAQAAAEgAAAABAAAACAAIAAgAL3Vzci9sb2NhbC9nb29nbGUvaG9tZS9yaGFycmlzb24vUGljdHVyZXMvUmVkX1NxdWFyZS50aWZmAENyZWF0ZWQgd2l0aCBHSU1QAA==</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected.pdf.0.png
new file mode 100644
index 0000000..5204fd7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image.in b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image.in
new file mode 100644
index 0000000..7f7e3ee
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/tiff">SUkqAFoHAACAP8AQOCQWDQeEQmFQuGQ2HQ+IRGJROKRWLReMRmNRuOR2PR+QSGRSOSSWTSeEwKUSuWS2XS+YTGZTOaTWbTecTmCyqdT2fT+gUGhUOiUWjUeLTykUumU2nU+oVGpVOQUqqVesVmtVuuV2vRGrV+xWOyWWzWe0R2w2m2W23W+4XGi2u5XW7Xe8Xm9Q+6Xu/X/AYHBVe+4PDYfEYnFSzC4vHY/IZHJTvJ5XLZfMX7G5nOZ3PZ+oZvQaPSaXTTLRafVavWa2k67YbHZbOFanabfcbnJbbdb3fb+97zgcPicWzcLjcnlcuo8jmc/odGfc7pdXrdeV9Tsdvud2MdrveHxeOD+Dyef0djzen2e3lev3fH5br4fP7ffW/X8fv+Z/9P7AEAt3AUCQK1b/wNBMFLzBEFwdB62wbCEJwor8JQrDEMqnC8NQ7Dyjw5D8RRGnsQxJE8UJjE0UxZFqSxXF0YxkjcYRnG0brBHEdR2l8ax5H8dx9IEhxnIUiSPFkjSRJcRyVJknw1J0oSnCcpSpK8FStLEtwFLUuS+/cvTBMb5TFMkzvTM00TW8U1TZN7tzdOE5ulOU6Tu5c7TxPbiT1Pk/t7P1AUG2lBUJQ7XUNRFFtPRVGUe0FHUhSbMoCIA/wBA4JBYNB4RCYVC4ZDYdD4hEYlE4pFYtF4xGY1G45HY9H5BIZFI5JJZNJ4TApRK5ZLZdL5hMZlM5pNZtN5xOYLKp1PZ9P6BQaFQ6JRaNR4tPKRS6ZTadT6hUalU5BSqpV6xWa1W65Xa9EatX7FY7JZbNZ7RHbDabZbbdb7hcaLa7ldbtd7xeb1D7pe79f8BgcFV77g8Nh8RicVLMLi8dj8hkclO8nlctl8xfsbmc5nc9n6hm9Bo9JpdNMtFp9Vq9ZraTrthsdls4Vqdpt9xucltt1vd9v73vOBw+JxbNwuNyeVy6jyOZz+h0Z9zul1et15X1Ox2+53Yx2u94fF44P4PJ5/R2PN6fZ7eV6/d8fluvh8/t99b9fx+/5n/0/sAQC3cBQJArVv/A0EwUvMEQXB0HrbBsIQnCivwlCsMQyqcLw1DsPKPDkPxFEaexDEkTxQmMTRTFkWpLFcXRjGSNxhGcbRusEcR1HaXxrHkfx3H0gSHGchSJI8WSNJElxHJUmSfDUnShKcJylKkrwVK0sS3AUtS5L79y9MExvlMUyTO9MzTRNbxTVNk3u3N04Tm6U5TpO7lztPE9uJPU+T+3s/UBQbaUFQlDtdQ1EUW09FUZR7QUdSFJsygIgD/AEDgkFg0HhEJhULhkNh0PiERiUTikVi0XjEZjUbjkdj0fkEhkUjkklk0nhMClErlktl0vmExmUzmk1m03nE5gsqnU9n0/oFBoVDolFo1Hi08pFLplNp1PqFRqVTkFKqlXrFZrVbrldr0Rq1fsVjslls1ntEdsNptltt1vuFxotruV1u13vF5vUPul7v1/wGBwVXvuDw2HxGJxUswuLx2PyGRyU7yeVy2XzF+xuZzmdz2fqGb0Gj0ml00y0Wn1Wr1mtpOu2Gx2WzhWp2m33G5yW23W932/ve84HD4nFs3C43J5XLqPI5nP6HRn3O6XV63XlfU7Hb7ndjHa73h8Xjg/g8nn9HY83p9nt5Xr93x+W6+Hz+331v1/H7/mf/T+wBALdwFAkCtW/8DQTBS8wRBcHQetsGwhCcKK/CUKwxDKpwvDUOw8o8OQ/EURp7EMSRPFCYxNFMWRaksVxdGMZI3GEZxtG6wRxHUdpfGseR/HcfSBIcZyFIkjxZI0kSXEclSZJ8NSdKEpwnKUqSvBUrSxLcBS1Lkvv3L0wTG+UxTJM70zNNE1vFNU2Te7c3ThObpTlOk7uXO08T24k9T5P7ez9QFBtpQVCUO11DURRbT0VRlHtBR1IUmzKAiAP8AQOCQWDQeEQmFQuGQ2HQ+IRGJROKRWLReMRmNRuOR2PR+QSGRSOSSWTSeEwKUSuWS2XS+YTGZTOaTWbTecTmCyqdT2fT+gUGhUOiUWjUeLTykUumU2nU+oVGpVOQUqqVesVmtVuuV2vRGrV+xWOyWWzWe0R2w2m2W23W+4XGi2u5XW7Xe8Xm9Q+6Xu/X/AYHBVe+4PDYfEYnFSzC4vHY/IZHJTvJ5XLZfMX7G5nOZ3PZ+oZvQaPSaXTTLRafVavWa2k67YbHZbOFanabfcbnJbbdb3fb+97zgcPicWzcLjcnlcuo8jmc/odGfc7pdXrdeV9Tsdvud2MdrveHxeOD+Dyef0djzen2e3lev3fH5br4fP7ffW/X8fv+Z/9P7AEAt3AUCQK1b/wNBMFLzBEFwdB62wbCEJwor8JQrDEMqnC8NQ7Dyjw5D8RRGnsQxJE8UJjE0UxZFqSxXF0YxkjcYRnG0brBHEdR2l8ax5H8dx9IEhxnIUiSPFkjSRJcRyVJknw1J0oSnCcpSpK8FStLEtwFLUuS+/cvTBMb5TFMkzvTM00TW8U1TZN7tzdOE5ulOU6Tu5c7TxPbiICBEA/gAEAAEAAAAAAAAAAAEDAAEAAAD6AAAAAQEDAAEAAAD6AAAAAgEDAAMAAAA8CAAAAwEDAAEAAAAFAAAABgEDAAEAAAACAAAADQECAD4AAABiCAAAEQEEAAQAAABSCAAAEgEDAAEAAAABAAAAFQEDAAEAAAADAAAAFgEDAAEAAABAAAAAFwEEAAQAAABCCAAAGgEFAAEAAAAsCAAAGwEFAAEAAAA0CAAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAPQEDAAEAAAACAAAAAAAAAEgAAAABAAAASAAAAAEAAAAIAAgACADcAQAA3AEAANwBAAC+AQAACAAAAOQBAADAAwAAnAUAAC91c3IvbG9jYWwvZ29vZ2xlL2hvbWUvcmhhcnJpc29uL1BpY3R1cmVzL1JlZF9TcXVhcmVfbHp3LnRpZmYA</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected.pdf.0.png
new file mode 100644
index 0000000..5204fd7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image.in b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image.in
new file mode 100644
index 0000000..5fd86b5
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image.in
@@ -0,0 +1,39 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" w="250pt" h="250pt">
+        <value>
+          <image contentType="image/tiff">SUkqALgdAAB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8Afv8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP//ABL/AAD/AAD/AAD/AAD/AAD/AAD//wB+/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA//8AEv8AAP8AAP8AAP8AAP8AAP8AAP//AH7/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD//wAS/wAA/wAA/wAA/wAA/wAA/wAA//8AEQD+AAQAAQAAAAAAAAAAAQMAAQAAADIAAAABAQMAAQAAADIAAAACAQMAAwAAAJoeAAADAQMAAQAAAAWAAAAGAQMAAQAAAAIAAAANAQIAQwAAAKAeAAAOAQIAEgAAAOQeAAARAQQAAQAAAAgAAAASAQMAAQAAAAEAAAAVAQMAAQAAAAMAAAAWAQMAAQAAAEAAAAAXAQQAAQAAALAdAAAaAQUAAQAAAIoeAAAbAQUAAQAAAJIeAAAcAQMAAQAAAAEAAAAoAQMAAQAAAAIAAAAAAAAASAAAAAEAAABIAAAAAQAAAAgACAAIAC91c3IvbG9jYWwvZ29vZ2xlL2hvbWUvcmhhcnJpc29uL1BpY3R1cmVzL1JlZF9TcXVhcmVfcGFja2JpdHMudGlmZgAAQ3JlYXRlZCB3aXRoIEdJTVAA</image>
+        </value>
+        <border>
+          <edge thickness="0.254mm"/>
+          <corner thickness="0.254mm"/>
+        </border>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected.pdf.0.png
new file mode 100644
index 0000000..5204fd7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/rectangles_multi_pages.in b/testing/resources/rectangles_multi_pages.in
new file mode 100644
index 0000000..feb815c
--- /dev/null
+++ b/testing/resources/rectangles_multi_pages.in
@@ -0,0 +1,103 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 250 ]
+  /Count 5
+  /Kids [ 3 0 R 5 0 R 7 0 R 9 0 R 11 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 1 0 rg
+100 0 30 50 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Rotate 90
+  /Contents 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 1 1 rg
+100 0 30 50 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 8 0 R
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+1 0 0 rg
+100 0 30 50 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 10 0 R
+>>
+endobj
+{{object 10 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 1 0 rg
+100 0 30 50 re B*
+100 150 50 30 re B*
+Q
+endstream
+endobj
+{{object 11 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 12 0 R
+>>
+endobj
+{{object 12 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 90 80 60 re B*
+100 150 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/rectangles_multi_pages.pdf b/testing/resources/rectangles_multi_pages.pdf
new file mode 100644
index 0000000..22d44a4
--- /dev/null
+++ b/testing/resources/rectangles_multi_pages.pdf
@@ -0,0 +1,122 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 250 ]
+  /Count 5
+  /Kids [ 3 0 R 5 0 R 7 0 R 9 0 R 11 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 49
+>>
+stream
+q
+1 1 0 rg
+100 0 30 50 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Rotate 90
+  /Contents 6 0 R
+>>
+endobj
+6 0 obj <<
+  /Length 49
+>>
+stream
+q
+0 1 1 rg
+100 0 30 50 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+7 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 8 0 R
+>>
+endobj
+8 0 obj <<
+  /Length 49
+>>
+stream
+q
+1 0 0 rg
+100 0 30 50 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 10 0 R
+>>
+endobj
+10 0 obj <<
+  /Length 51
+>>
+stream
+q
+0 1 0 rg
+100 0 30 50 re B*
+100 150 50 30 re B*
+Q
+endstream
+endobj
+11 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 12 0 R
+>>
+endobj
+12 0 obj <<
+  /Length 50
+>>
+stream
+q
+0 0 0 rg
+0 90 80 60 re B*
+100 150 50 30 re B*
+Q
+endstream
+endobj
+xref
+0 13
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000186 00000 n 
+0000000255 00000 n 
+0000000355 00000 n 
+0000000437 00000 n 
+0000000537 00000 n 
+0000000606 00000 n 
+0000000706 00000 n 
+0000000776 00000 n 
+0000000879 00000 n 
+0000000950 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 13
+>>
+startxref
+1052
+%%EOF
diff --git a/testing/resources/redirect.pdf b/testing/resources/redirect.pdf
new file mode 100644
index 0000000..517bd5a
--- /dev/null
+++ b/testing/resources/redirect.pdf
@@ -0,0 +1,22 @@
+%PDF-1.7

+trailer

+<<

+/Root 1 0 R

+>>

+1 0 obj

+<<

+/Type /Catalog

+/Pages 2 0 R

+/OpenAction 2 0 R

+>>

+endobj

+

+2 0 obj

+<<

+/Type /Action

+/S /URI

+/URI (http://evilzone.org) // URL HERE

+>>

+endobj

+

+%%EOF

diff --git a/testing/resources/rotated_text.in b/testing/resources/rotated_text.in
new file mode 100644
index 0000000..6cd5b23
--- /dev/null
+++ b/testing/resources/rotated_text.in
@@ -0,0 +1,58 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+0 0 Td
+/F1 12 Tf
+0.70710678118 -0.70710678118 0.70710678118 0.70710678118 100 100 Tm
+(Hello,) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 -0.70710678118 0.70710678118 -0.70710678118 100 100 Tm
+( world!\r\n) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 0.70710678118 -0.70710678118 -0.70710678118 100 100 Tm
+(Goodbye,) Tj
+0 0 Td
+/F1 12 Tf
+0.70710678118 0.70710678118 -0.70710678118 0.70710678118 100 100 Tm
+( world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/rotated_text.pdf b/testing/resources/rotated_text.pdf
new file mode 100644
index 0000000..d6d8db8
--- /dev/null
+++ b/testing/resources/rotated_text.pdf
@@ -0,0 +1,70 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Length 406
+>>
+stream
+BT
+0 0 Td
+/F1 12 Tf
+0.70710678118 -0.70710678118 0.70710678118 0.70710678118 100 100 Tm
+(Hello,) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 -0.70710678118 0.70710678118 -0.70710678118 100 100 Tm
+( world!\r\n) Tj
+0 0 Td
+/F1 12 Tf
+-0.70710678118 0.70710678118 -0.70710678118 -0.70710678118 100 100 Tm
+(Goodbye,) Tj
+0 0 Td
+/F1 12 Tf
+0.70710678118 0.70710678118 -0.70710678118 0.70710678118 100 100 Tm
+( world!) Tj
+ET
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000287 00000 n 
+0000000365 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+823
+%%EOF
diff --git a/testing/resources/simple_thumbnail.in b/testing/resources/simple_thumbnail.in
new file mode 100644
index 0000000..a00b096
--- /dev/null
+++ b/testing/resources/simple_thumbnail.in
@@ -0,0 +1,115 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 2
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Thumb 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Thumb 6 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode /DCTDecode]
+  {{streamlen}}
+>>
+stream
+789cfb7fe3ff0306012f374f37064620d061d46160f8ff8f41d8b92835b1
+243545a13cb32443c1ddd337e0ff6d066706662626100202162062e56065
+656161e5626767e3e0e1e2e1e1e6e2e6e6e51312e0e513e4e3e616101310
+1416111515e5e11797101391101211150119c2c80cd4c3c2cac9caca29c2
+cbcd2b4232f87f88419083c188c188995190814990915990f1ff11062906
+46062666460624c0cac6ccc2ce0194136144110781ffb7187880aa990498
+05181818bf7a3030707c0e9965f9f30283b65b90c35af100c67853ef0090
+ca56e5d320da1e688e9c0003131030238c616105f20c18d9048415802672
+00ed616465ba70e0862ea3e366139bb7fafa9ce5f30e879c8a8d30bff4e1
+e744c6996bbe68ffc83bf6f5c691cfedd51b97ab5d0df14a2afdb51fe444
+41742706800d646664b46774c7a38009a6c010e83846261666563676a802
+462666164121614543061147560125834005152313a744e7c2a2c6a6890b
+a1ee65b367faa495f58427b9c96c6ad3fb04fd03e7b9b6160a8bbcd8cdc9
+f6edb7ecd17f93ac57bba7ffb9e299acc75321bb8f8579f546ede5de77be
+f0de3fb5c864ce994e9e2eb68f6292d36d6d24befedbba5de8cae2e3a7fc
+39845cf3c5b2e6ca5f0e35b35f5861b34a67f2a4558639dbcd74bf6fb876
+37374fc8c265f289ab532e9d54bc7ae7b97b67ff1b7b057607db593ab334
+b58d5f05b19ffea36a7da2e446e14d910bc1cf02afa47d364e50d7e7ba5b
+7a23e6d7cf9d5fb4369f94b07bf1b6c5f7cfbec9fff2eb03b9968acc9dcb
+b4f759557bcf6e7e17ae88fcec7bb772d7dd0b07c7162328ba90d204a3a0
+a2a1238381804260222cb6ec155fe89dd7d8aa306587c51d4eada0059b42
+e7acafd3b3624833f0333360dbef997758c4c1ca504cd2d1bb316eb68383
+f63417bf2b876a1e5ab5b24c4853c9503aafb467495746dad60605065bbb
+5745e7176c88395869f963eae7a8bc805fd7733acc9ab6b24404cd59c26d
+39af498e63e929c61d6aa7aafec3531f032829094cf25469f25498e0a922
+e4a932c98305c89d4f204508104a116005f202201fb220a5576000302818
+180a38061636c24340e081f0eb1bd50e4b199d4ecd7de9b4b04d2be9756b
+c17cbe3b9fece5ae39dcd99493c064a32122a863d0f176bea5a5a05151cb
+0cce159dc63a6db97a1107dcfca2e3b39537bce5395fed33a3aee73fc387
+e8777f825f3d0cf93047c566a1c7aa8d5c6794ae7465079e08cca88e59ce
+2b53feff2600fe084b25
+endstream
+endobj
+{{object 6 0}} <<
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode /DCTDecode]
+  {{streamlen}}
+>>
+stream
+789cfb7fe3ff0306012f374f37064620d061d46160f8ff8f41d8b92835b1
+243545a13cb32443c1ddd337e0ff6d066706662626100202162062e56065
+656161e5626767e3e0e1e2e1e1e6e2e6e6e51312e0e513e4e3e616101310
+1416111515e5e11797101391101211150119c2c80cd4c3c2cac9caca29c2
+cbcd2b4232f87f88419083c188c188995190814990915990f1ff110671b0
+fb1990011b2b075042044d1408fedf62e0616664601260166060607c3581
+8141f069e3bc549105c2ae9af392a63a00954cd56a50eb0ae00ce00d0e61
+6060071a232fc0c0c4c0cec08c308585958189994d40cc80514405682207
+d01e4656a607d71e7cdaf2c3e6973633d3e3b85339dbb65ddb3d8b9fcb8d
+39d8d57efbc9e9c72eeb645f5b76dd6ad7cbe42821d3850aa24eba8f416e
+1444776300d8446646467b46773c0a98600af481ce63646262e566852960
+646266111452646055324c1410165130300e0c4a9a7a49d9d129b811ea5c
+367ba6374a2d415b3e30c8ad2b2db03232dcb493bd6247f64fa7c9277543
+1d539b143b6f066c6def6d9ba8636c90d9a6e172d124e6c5d677331a8e4e
+6f0f304dd53aa920517955559eb743bb3327774bd1ddf98aada7f5b20e4e
+afda34c7abbe8579a9c9e7d756a7f6f53d73e90b14887e98fa2740f64cb5
+956fb25ac846671da3fdaf8f242bf62f4f36feb6bff5c4d3ab9a3c67c43c
+657c39181ef5cfce5d3cf5e86cb6ba75ca2bbf244deaac99127cd2afbd4f
+4421be6feebb0ddfa5fae741a9fe8b2b1fb72e792fc3bbd362737ef2916f
+f6c060501000c5023075c2c389515051c0d09141c120b0b071212c9aec15
+3f04ff7a905c637578425266fa4bf6b35c874b67683d78752a48ff8a50ca
+7696dd733b363c9bb5429375d79a0cf1d63d7379858e28192b05af3d7f42
+72ebefa78f1f5def3f6698be20c65a9187e7839a0b5b2297db3769bba203
+df620f7533ec307d7ded5eeaeed69f8e19abd69e6f87a53570ca1198e4a9
+328953610290f45069f26010f254994f20fa0508453f58811cc8df4cc0ac
+89e46f01450343864005c744b8af051e28fba49a2dd0c8befde6e38619f7
+823a0f363232297e0a5d7226ce51ed0cc793fa555b96647db16d3ab03af9
+947487d14429bf736177af07797906ba15febb71e2f2f5e5ddebe773fede
+a5c799a3105320f9e6f3feeddedb959c23591236e6ee9e92ee57baf7d499
+297be4ffdf040073884a15
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/simple_thumbnail.pdf b/testing/resources/simple_thumbnail.pdf
new file mode 100644
index 0000000..cac9d12
--- /dev/null
+++ b/testing/resources/simple_thumbnail.pdf
@@ -0,0 +1,128 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 2
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Thumb 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Thumb 6 0 R
+>>
+endobj
+5 0 obj <<
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode /DCTDecode]
+  /Length 1851
+>>
+stream
+789cfb7fe3ff0306012f374f37064620d061d46160f8ff8f41d8b92835b1
+243545a13cb32443c1ddd337e0ff6d066706662626100202162062e56065
+656161e5626767e3e0e1e2e1e1e6e2e6e6e51312e0e513e4e3e616101310
+1416111515e5e11797101391101211150119c2c80cd4c3c2cac9caca29c2
+cbcd2b4232f87f88419083c188c188995190814990915990f1ff11062906
+46062666460624c0cac6ccc2ce0194136144110781ffb7187880aa990498
+05181818bf7a3030707c0e9965f9f30283b65b90c35af100c67853ef0090
+ca56e5d320da1e688e9c0003131030238c616105f20c18d9048415802672
+00ed616465ba70e0862ea3e366139bb7fafa9ce5f30e879c8a8d30bff4e1
+e744c6996bbe68ffc83bf6f5c691cfedd51b97ab5d0df14a2afdb51fe444
+41742706800d646664b46774c7a38009a6c010e83846261666563676a802
+462666164121614543061147560125834005152313a744e7c2a2c6a6890b
+a1ee65b367faa495f58427b9c96c6ad3fb04fd03e7b9b6160a8bbcd8cdc9
+f6edb7ecd17f93ac57bba7ffb9e299acc75321bb8f8579f546ede5de77be
+f0de3fb5c864ce994e9e2eb68f6292d36d6d24befedbba5de8cae2e3a7fc
+39845cf3c5b2e6ca5f0e35b35f5861b34a67f2a4558639dbcd74bf6fb876
+37374fc8c265f289ab532e9d54bc7ae7b97b67ff1b7b057607db593ab334
+b58d5f05b19ffea36a7da2e446e14d910bc1cf02afa47d364e50d7e7ba5b
+7a23e6d7cf9d5fb4369f94b07bf1b6c5f7cfbec9fff2eb03b9968acc9dcb
+b4f759557bcf6e7e17ae88fcec7bb772d7dd0b07c7162328ba90d204a3a0
+a2a1238381804260222cb6ec155fe89dd7d8aa306587c51d4eada0059b42
+e7acafd3b3624833f0333360dbef997758c4c1ca504cd2d1bb316eb68383
+f63417bf2b876a1e5ab5b24c4853c9503aafb467495746dad60605065bbb
+5745e7176c88395869f963eae7a8bc805fd7733acc9ab6b24404cd59c26d
+39af498e63e929c61d6aa7aafec3531f032829094cf25469f25498e0a922
+e4a932c98305c89d4f204508104a116005f202201fb220a5576000302818
+180a38061636c24340e081f0eb1bd50e4b199d4ecd7de9b4b04d2be9756b
+c17cbe3b9fece5ae39dcd99493c064a32122a863d0f176bea5a5a05151cb
+0cce159dc63a6db97a1107dcfca2e3b39537bce5395fed33a3aee73fc387
+e8777f825f3d0cf93047c566a1c7aa8d5c6794ae7465079e08cca88e59ce
+2b53feff2600fe084b25
+endstream
+endobj
+6 0 obj <<
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode /DCTDecode]
+  /Length 1792
+>>
+stream
+789cfb7fe3ff0306012f374f37064620d061d46160f8ff8f41d8b92835b1
+243545a13cb32443c1ddd337e0ff6d066706662626100202162062e56065
+656161e5626767e3e0e1e2e1e1e6e2e6e6e51312e0e513e4e3e616101310
+1416111515e5e11797101391101211150119c2c80cd4c3c2cac9caca29c2
+cbcd2b4232f87f88419083c188c188995190814990915990f1ff110671b0
+fb1990011b2b075042044d1408fedf62e0616664601260166060607c3581
+8141f069e3bc549105c2ae9af392a63a00954cd56a50eb0ae00ce00d0e61
+6060071a232fc0c0c4c0cec08c308585958189994d40cc80514405682207
+d01e4656a607d71e7cdaf2c3e6973633d3e3b85339dbb65ddb3d8b9fcb8d
+39d8d57efbc9e9c72eeb645f5b76dd6ad7cbe42821d3850aa24eba8f416e
+1444776300d8446646467b46773c0a98600af481ce63646262e566852960
+646266111452646055324c1410165130300e0c4a9a7a49d9d129b811ea5c
+367ba6374a2d415b3e30c8ad2b2db03232dcb493bd6247f64fa7c9277543
+1d539b143b6f066c6def6d9ba8636c90d9a6e172d124e6c5d677331a8e4e
+6f0f304dd53aa920517955559eb743bb3327774bd1ddf98aada7f5b20e4e
+afda34c7abbe8579a9c9e7d756a7f6f53d73e90b14887e98fa2740f64cb5
+956fb25ac846671da3fdaf8f242bf62f4f36feb6bff5c4d3ab9a3c67c43c
+657c39181ef5cfce5d3cf5e86cb6ba75ca2bbf244deaac99127cd2afbd4f
+4421be6feebb0ddfa5fae741a9fe8b2b1fb72e792fc3bbd362737ef2916f
+f6c060501000c5023075c2c389515051c0d09141c120b0b071212c9aec15
+3f04ff7a905c637578425266fa4bf6b35c874b67683d78752a48ff8a50ca
+7696dd733b363c9bb5429375d79a0cf1d63d7379858e28192b05af3d7f42
+72ebefa78f1f5def3f6698be20c65a9187e7839a0b5b2297db3769bba203
+df620f7533ec307d7ded5eeaeed69f8e19abd69e6f87a53570ca1198e4a9
+328953610290f45069f26010f254994f20fa0508453f58811cc8df4cc0ac
+89e46f01450343864005c744b8af051e28fba49a2dd0c8befde6e38619f7
+823a0f363232297e0a5d7226ce51ed0cc793fa555b96647db16d3ab03af9
+947487d14429bf736177af07797906ba15febb71e2f2f5e5ddebe773fede
+a5c799a3105320f9e6f3feeddedb959c23591236e6ee9e92ee57baf7d499
+297be4ffdf040073884a15
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000177 00000 n 
+0000000243 00000 n 
+0000000309 00000 n 
+0000002337 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+4306
+%%EOF
diff --git a/testing/resources/simple_xfa.in b/testing/resources/simple_xfa.in
new file mode 100644
index 0000000..b20c746
--- /dev/null
+++ b/testing/resources/simple_xfa.in
@@ -0,0 +1,31 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="0.25in" y="0.25in" w="8in" h="10.5in" />
+      </pageArea>
+    </pageSet>
+    <field name="TextField1" y="31.75mm" x="44.45mm" w="114.291mm" h="12.7mm">
+    </field>
+    <field name="TextField11" y="222.25mm" x="44.45mm" w="82.55mm" h="12.7mm">
+    </field>
+  </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/simple_xfa.pdf b/testing/resources/simple_xfa.pdf
index ef8eb7d..89f9aea 100644
--- a/testing/resources/simple_xfa.pdf
+++ b/testing/resources/simple_xfa.pdf
Binary files differ
diff --git a/testing/resources/split_streams.in b/testing/resources/split_streams.in
new file mode 100644
index 0000000..b134769
--- /dev/null
+++ b/testing/resources/split_streams.in
@@ -0,0 +1,124 @@
+{{header}}
+{{object 1 0}}
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+{{object 2 0}}
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+{{object 3 0}}
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+  >>
+  /Type /Page
+>>
+endobj
+{{object 4 0}}
+[ 12 0 R 13 0 R 14 0 R ]
+endobj
+{{object 5 0}}
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+{{object 6 0}}
+<< /ca 0.705882 >>
+endobj
+{{object 7 0}}
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+{{object 8 0}}
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+{{object 9 0}}
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 10 0}}
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 12 0}}
+<< {{streamlen}} >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+endstream
+endobj
+
+{{object 13 0}}
+<< {{streamlen}} >>
+stream
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+endstream
+endobj
+
+{{object 14 0}}
+<< {{streamlen}} >>
+stream
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+Q
+endstream
+endobj
+
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Size 15
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/split_streams.pdf b/testing/resources/split_streams.pdf
new file mode 100644
index 0000000..d1768ca
--- /dev/null
+++ b/testing/resources/split_streams.pdf
@@ -0,0 +1,142 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+3 0 obj
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+  >>
+  /Type /Page
+>>
+endobj
+4 0 obj
+[ 12 0 R 13 0 R 14 0 R ]
+endobj
+5 0 obj
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+6 0 obj
+<< /ca 0.705882 >>
+endobj
+7 0 obj
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+8 0 obj
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+9 0 obj
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+10 0 obj
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+12 0 obj
+<< /Length 1699 >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+endstream
+endobj
+
+13 0 obj
+<< /Length 333 >>
+stream
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+endstream
+endobj
+
+14 0 obj
+<< /Length 115 >>
+stream
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+Q
+endstream
+endobj
+
+xref
+0 15
+0000000000 65535 f 
+0000000015 00000 n 
+0000000064 00000 n 
+0000000149 00000 n 
+0000000345 00000 n 
+0000000385 00000 n 
+0000000430 00000 n 
+0000000464 00000 n 
+0000000536 00000 n 
+0000000606 00000 n 
+0000000679 00000 n 
+0000000000 65535 f 
+0000000751 00000 n 
+0000002503 00000 n 
+0000002888 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 15
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+startxref
+3055
+%%EOF
diff --git a/testing/resources/tagged_alt_text.in b/testing/resources/tagged_alt_text.in
new file mode 100644
index 0000000..42ec7f1
--- /dev/null
+++ b/testing/resources/tagged_alt_text.in
@@ -0,0 +1,162 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 8 0 R
+  /Lang (en-US)
+  /MarkInfo <<
+    /Marked true
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Group <<
+    /CS /DeviceRGB
+    /I true
+    /S /Transparency
+  >>
+  /Resources <<
+    /ProcSet [/PDF /ImageC /ImageI /ImageB]
+    /XObject <<
+      /Tr8 5 0 R
+      /Im7 6 0 R
+    >>
+    /ExtGState <<
+      /EGS9 7 0 R
+    >>
+  >>
+  /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [-140 395 753 395.1]
+  /Group <<
+    /CS /DeviceRGB
+    /K true
+    /S /Transparency
+  >>
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+{{object 7 0}} <<
+  /ca 0.5
+  /CA 0.5
+>>
+endobj
+{{object 8 0}} <<
+  /Type /StructTreeRoot
+  /ParentTree 9 0 R
+  /K [11 0 R]
+  /RoleMap <<
+    /Document /Document
+    /Standard /P
+    /Figure /Figure
+  >>
+>>
+endobj
+{{object 9 0}} <<
+  /Nums [0 [10 0 R]]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /Figure
+  /A 13 0 R
+  /K [0]
+  /P 12 0 R
+  /Alt <feff0042006c00610063006b00200049006d006100670065>
+  /Pg 3 0 R
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /Document
+  /K [12 0 R]
+  /P 8 0 R
+  /T (TitleText)
+  /Pg 3 0 R
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /Standard
+  /A 14 0 R
+  /K [10 0 R]
+  /P 11 0 R
+  /T <feff00730079006d0062006f006c003a0020003100300030006b>
+  /Pg 3 0 R
+>>
+endobj
+{{object 13 0}} <<
+  /O /Layout
+  /Placement /Block
+  /BBox [281.1 685.3 331.1 735.3]
+  /Width 99.9
+  /Height 99.9
+>>
+endobj
+{{object 14 0}} <<
+  /O /Layout
+  /Placement /Block
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_alt_text.pdf b/testing/resources/tagged_alt_text.pdf
index a899ce1..c75504a 100644
--- a/testing/resources/tagged_alt_text.pdf
+++ b/testing/resources/tagged_alt_text.pdf
Binary files differ
diff --git a/testing/resources/text_color.in b/testing/resources/text_color.in
new file mode 100644
index 0000000..7e76a39
--- /dev/null
+++ b/testing/resources/text_color.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+10 160 Td
+/F1 36 Tf
+0 1 0 RG
+1 0 0 rg
+2 Tr
+(0) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/text_color.pdf b/testing/resources/text_color.pdf
new file mode 100644
index 0000000..8dbcdd1
--- /dev/null
+++ b/testing/resources/text_color.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Length 56
+>>
+stream
+BT
+10 160 Td
+/F1 36 Tf
+0 1 0 RG
+1 0 0 rg
+2 Tr
+(0) Tj
+ET
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000287 00000 n 
+0000000365 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+472
+%%EOF
diff --git a/testing/resources/text_font.pdf b/testing/resources/text_font.pdf
new file mode 100644
index 0000000..78f3bcc
--- /dev/null
+++ b/testing/resources/text_font.pdf
Binary files differ
diff --git a/testing/resources/text_form.in b/testing/resources/text_form.in
index c30f0ba..12ea13a 100644
--- a/testing/resources/text_form.in
+++ b/testing/resources/text_form.in
@@ -42,7 +42,7 @@
 >>
 endobj
 {{object 8 0}}
-<< /Length 51 >>
+<< {{streamlen}} >>
 stream
 BT
 0 0 0 rg
diff --git a/testing/resources/text_form_multiple.in b/testing/resources/text_form_multiple.in
index 576e155..6b3b7a5 100644
--- a/testing/resources/text_form_multiple.in
+++ b/testing/resources/text_form_multiple.in
@@ -3,7 +3,7 @@
 <<
   /Type /Catalog
   /Pages 2 0 R
-  /AcroForm << /Fields [ 4 0 R 9 0 R 10 0 R ] /DR 5 0 R >>
+  /AcroForm << /Fields [ 4 0 R 9 0 R 10 0 R 11 0 R ] /DR 5 0 R >>
 >>
 endobj
 {{object 2 0}}
@@ -16,7 +16,7 @@
   /Resources 5 0 R
   /MediaBox [ 0 0 300 300 ]
   /Contents 8 0 R
-  /Annots [ 4 0 R 9 0 R 10 0 R ]
+  /Annots [ 4 0 R 9 0 R 10 0 R 11 0 R ]
 >>
 endobj
 {{object 4 0}}
@@ -42,7 +42,7 @@
 >>
 endobj
 {{object 8 0}}
-<< /Length 51 >>
+<< {{streamlen}} >>
 stream
 BT
 0 0 0 rg
@@ -74,6 +74,16 @@
   /Rect [ 100 50 200 75 ]
   /Subtype /Widget
 >>
+{{object 11 0}}
+<<
+  /Type /Annot
+  /FT /Tx
+  /Ff 8192
+  /T (Password)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 10 200 35 ]
+  /Subtype /Widget
+>>
 endobj
 {{xref}}
 {{trailer}}
diff --git a/testing/resources/text_form_multiple.pdf b/testing/resources/text_form_multiple.pdf
index 172ab5e..cc27c09 100644
--- a/testing/resources/text_form_multiple.pdf
+++ b/testing/resources/text_form_multiple.pdf
@@ -4,7 +4,7 @@
 <<
   /Type /Catalog
   /Pages 2 0 R
-  /AcroForm << /Fields [ 4 0 R 9 0 R 10 0 R ] /DR 5 0 R >>
+  /AcroForm << /Fields [ 4 0 R 9 0 R 10 0 R 11 0 R ] /DR 5 0 R >>
 >>
 endobj
 2 0 obj
@@ -17,7 +17,7 @@
   /Resources 5 0 R
   /MediaBox [ 0 0 300 300 ]
   /Contents 8 0 R
-  /Annots [ 4 0 R 9 0 R 10 0 R ]
+  /Annots [ 4 0 R 9 0 R 10 0 R 11 0 R ]
 >>
 endobj
 4 0 obj
@@ -75,21 +75,35 @@
   /Rect [ 100 50 200 75 ]
   /Subtype /Widget
 >>
+11 0 obj
+<<
+  /Type /Annot
+  /FT /Tx
+  /Ff 8192
+  /T (Password)
+  /DA (0 0 0 rg /F1 12 Tf)
+  /Rect [ 100 10 200 35 ]
+  /Subtype /Widget
+>>
 endobj
 xref
-0 11
+0 12
 0000000000 65535 f 
 0000000015 00000 n 
-0000000127 00000 n 
-0000000186 00000 n 
-0000000335 00000 n 
-0000000471 00000 n 
-0000000504 00000 n 
-0000000535 00000 n 
-0000000611 00000 n 
-0000000711 00000 n 
-0000000855 00000 n 
-trailer<< /Root 1 0 R /Size 11 >>
+0000000134 00000 n 
+0000000193 00000 n 
+0000000349 00000 n 
+0000000485 00000 n 
+0000000518 00000 n 
+0000000549 00000 n 
+0000000625 00000 n 
+0000000725 00000 n 
+0000000869 00000 n 
+0000001027 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 12
+>>
 startxref
-1020
+1173
 %%EOF
diff --git a/testing/resources/text_form_negative_fontsize.in b/testing/resources/text_form_negative_fontsize.in
new file mode 100644
index 0000000..056b483
--- /dev/null
+++ b/testing/resources/text_form_negative_fontsize.in
@@ -0,0 +1,66 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [ 4 0 R ]
+    /DR 5 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}}
+<<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 300 ]
+  /Contents 8 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 -12 Tf)
+  /Rect [ 100 100 200 130 ]
+  /Subtype /Widget
+  /V (Mountain Lion)
+>>
+endobj
+{{object 5 0}} <<
+  /Font 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /F1 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+  BT
+  0 0 0 rg
+  /F1 -12 Tf
+  250 150 Td
+  (Test Form with Negative Font Size) Tj
+  ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/text_form_negative_fontsize.pdf b/testing/resources/text_form_negative_fontsize.pdf
new file mode 100644
index 0000000..b4efcc0
--- /dev/null
+++ b/testing/resources/text_form_negative_fontsize.pdf
@@ -0,0 +1,81 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [ 4 0 R ]
+    /DR 5 0 R
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj
+<<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 300 ]
+  /Contents 8 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 -12 Tf)
+  /Rect [ 100 100 200 130 ]
+  /Subtype /Widget
+  /V (Mountain Lion)
+>>
+endobj
+5 0 obj <<
+  /Font 6 0 R
+>>
+endobj
+6 0 obj <<
+  /F1 7 0 R
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+  /Length 88
+>>
+stream
+  BT
+  0 0 0 rg
+  /F1 -12 Tf
+  250 150 Td
+  (Test Form with Negative Font Size) Tj
+  ET
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000124 00000 n 
+0000000189 00000 n 
+0000000325 00000 n 
+0000000483 00000 n 
+0000000518 00000 n 
+0000000551 00000 n 
+0000000627 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+766
+%%EOF
diff --git a/testing/resources/text_in_page_marked.in b/testing/resources/text_in_page_marked.in
new file mode 100644
index 0000000..e338479
--- /dev/null
+++ b/testing/resources/text_in_page_marked.in
@@ -0,0 +1,138 @@
+{{header}}
+{{object 1 0}}
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+{{object 2 0}}
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+{{object 3 0}}
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+  >>
+  /Type /Page
+>>
+endobj
+{{object 4 0}}
+[ 12 0 R ]
+endobj
+{{object 5 0}}
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+{{object 6 0}}
+<< /ca 0.705882 >>
+endobj
+{{object 7 0}}
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+{{object 8 0}}
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+{{object 9 0}}
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 10 0}}
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 12 0}}
+<< {{streamlen}} >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+/Square <</Factor 1>> BDC
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+EMC
+/Square <</Factor 2>> BDC
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+EMC
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+/Prime BMC
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+EMC
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+/Square <</Factor 3>> BDC
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+EMC
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+/GreaterThanTen BMC
+/Prime BMC
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+EMC
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+/Prime BMC
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+EMC
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+/Square <</Factor 4>> BDC
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+EMC
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+/Bounds <</Position (Last)>> BDC
+/Prime BMC
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+EMC
+EMC
+EMC
+Q
+endstream
+endobj
+
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Size 13
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/text_in_page_marked.pdf b/testing/resources/text_in_page_marked.pdf
new file mode 100644
index 0000000..a30ab9d
--- /dev/null
+++ b/testing/resources/text_in_page_marked.pdf
@@ -0,0 +1,154 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+3 0 obj
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+  >>
+  /Type /Page
+>>
+endobj
+4 0 obj
+[ 12 0 R ]
+endobj
+5 0 obj
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+6 0 obj
+<< /ca 0.705882 >>
+endobj
+7 0 obj
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+8 0 obj
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+9 0 obj
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+10 0 obj
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+12 0 obj
+<< /Length 2433 >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+/Square <</Factor 1>> BDC
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+EMC
+/Square <</Factor 2>> BDC
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+EMC
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+/Prime BMC
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+EMC
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+/Square <</Factor 3>> BDC
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+EMC
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+/GreaterThanTen BMC
+/Prime BMC
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+EMC
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+/Prime BMC
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+EMC
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+/Square <</Factor 4>> BDC
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+EMC
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+/Bounds <</Position (Last)>> BDC
+/Prime BMC
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+EMC
+EMC
+EMC
+Q
+endstream
+endobj
+
+xref
+0 13
+0000000000 65535 f 
+0000000015 00000 n 
+0000000064 00000 n 
+0000000149 00000 n 
+0000000345 00000 n 
+0000000371 00000 n 
+0000000416 00000 n 
+0000000450 00000 n 
+0000000522 00000 n 
+0000000592 00000 n 
+0000000665 00000 n 
+0000000000 65535 f 
+0000000737 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 13
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+startxref
+3223
+%%EOF
diff --git a/testing/resources/text_in_page_marked_indirect.in b/testing/resources/text_in_page_marked_indirect.in
new file mode 100644
index 0000000..1f989f6
--- /dev/null
+++ b/testing/resources/text_in_page_marked_indirect.in
@@ -0,0 +1,144 @@
+{{header}}
+{{object 1 0}}
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+{{object 2 0}}
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+{{object 3 0}}
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+    /Properties <<
+      /LastBoundsProperty 13 0 R
+    >>
+  >>
+  /Type /Page
+>>
+endobj
+{{object 4 0}}
+[ 12 0 R ]
+endobj
+{{object 5 0}}
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+{{object 6 0}}
+<< /ca 0.705882 >>
+endobj
+{{object 7 0}}
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+{{object 8 0}}
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+{{object 9 0}}
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 10 0}}
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 12 0}}
+<< {{streamlen}} >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+/Square <</Factor 1>> BDC
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+EMC
+/Square <</Factor 2>> BDC
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+EMC
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+/Prime BMC
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+EMC
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+/Square <</Factor 3>> BDC
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+EMC
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+/GreaterThanTen BMC
+/Prime BMC
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+EMC
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+/Prime BMC
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+EMC
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+/Square <</Factor 4>> BDC
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+EMC
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+/Bounds /LastBoundsProperty BDC
+/Prime BMC
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+EMC
+EMC
+EMC
+Q
+endstream
+endobj
+{{object 13 0}}
+<< /Position (Last) >>
+endobj
+
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Size 14
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/text_in_page_marked_indirect.pdf b/testing/resources/text_in_page_marked_indirect.pdf
new file mode 100644
index 0000000..8756291
--- /dev/null
+++ b/testing/resources/text_in_page_marked_indirect.pdf
@@ -0,0 +1,161 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+3 0 obj
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+    /Properties <<
+      /LastBoundsProperty 13 0 R
+    >>
+  >>
+  /Type /Page
+>>
+endobj
+4 0 obj
+[ 12 0 R ]
+endobj
+5 0 obj
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+6 0 obj
+<< /ca 0.705882 >>
+endobj
+7 0 obj
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+8 0 obj
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+9 0 obj
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+10 0 obj
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+12 0 obj
+<< /Length 2432 >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+/Square <</Factor 1>> BDC
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+EMC
+/Square <</Factor 2>> BDC
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+EMC
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+/Prime BMC
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+EMC
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+/Square <</Factor 3>> BDC
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+EMC
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+/GreaterThanTen BMC
+/Prime BMC
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+EMC
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+/Prime BMC
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+EMC
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+/Square <</Factor 4>> BDC
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+EMC
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+/Bounds /LastBoundsProperty BDC
+/Prime BMC
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+EMC
+EMC
+EMC
+Q
+endstream
+endobj
+13 0 obj
+<< /Position (Last) >>
+endobj
+
+xref
+0 14
+0000000000 65535 f 
+0000000015 00000 n 
+0000000064 00000 n 
+0000000149 00000 n 
+0000000404 00000 n 
+0000000430 00000 n 
+0000000475 00000 n 
+0000000509 00000 n 
+0000000581 00000 n 
+0000000651 00000 n 
+0000000724 00000 n 
+0000000000 65535 f 
+0000000796 00000 n 
+0000003280 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 14
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+startxref
+3320
+%%EOF
diff --git a/testing/resources/text_render_mode.pdf b/testing/resources/text_render_mode.pdf
new file mode 100644
index 0000000..220e143
--- /dev/null
+++ b/testing/resources/text_render_mode.pdf
@@ -0,0 +1,75 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj 
+<<
+/Resources 2 0 R
+/Contents 3 0 R
+/Parent 4 0 R
+/Type /Page
+/MediaBox [0 0 612 446]
+>>
+endobj 
+3 0 obj 
+<<
+/Length 102
+>>
+stream
+  BT
+  /F1 24 Tf
+  0 Tr
+  1 0 0 1 260 250 Tm
+  (fill)Tj
+  1 Tr
+  1 0 0 1 260 200 Tm
+  (stroke)Tj
+  ET
+
+endstream 
+endobj 
+2 0 obj 
+<<
+/Font 
+<<
+/F1 5 0 R
+>>
+>>
+endobj 
+5 0 obj 
+<<
+/Subtype /Type1
+/Name /F1
+/Type /Font
+/BaseFont /Helvetica
+>>
+endobj 
+4 0 obj 
+<<
+/Kids [1 0 R]
+/Type /Pages
+/MediaBox [0 0 612 446]
+/Count 1
+>>
+endobj 
+6 0 obj 
+<<
+/Type /Catalog
+/Pages 4 0 R
+>>
+endobj xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000277 00000 n 
+0000000121 00000 n 
+0000000405 00000 n 
+0000000323 00000 n 
+0000000488 00000 n 
+trailer
+
+<<
+/Root 6 0 R
+/Size 7
+>>
+startxref
+538
+%%EOF
diff --git a/testing/resources/thumbnail_with_empty_stream.in b/testing/resources/thumbnail_with_empty_stream.in
new file mode 100644
index 0000000..d540171
--- /dev/null
+++ b/testing/resources/thumbnail_with_empty_stream.in
@@ -0,0 +1,33 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Thumb 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+>>
+stream
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/thumbnail_with_empty_stream.pdf b/testing/resources/thumbnail_with_empty_stream.pdf
new file mode 100644
index 0000000..2a1e9ef
--- /dev/null
+++ b/testing/resources/thumbnail_with_empty_stream.pdf
@@ -0,0 +1,44 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Thumb 4 0 R
+>>
+endobj
+4 0 obj <<
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+>>
+stream
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000227 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+378
+%%EOF
diff --git a/testing/resources/thumbnail_with_no_filters.in b/testing/resources/thumbnail_with_no_filters.in
new file mode 100644
index 0000000..188db2a
--- /dev/null
+++ b/testing/resources/thumbnail_with_no_filters.in
@@ -0,0 +1,41 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Thumb 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 5 0}} <<
+  /Width 10
+  /Height 10
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  {{streamlen}}
+>>
+stream
+z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z    z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/thumbnail_with_no_filters.pdf b/testing/resources/thumbnail_with_no_filters.pdf
new file mode 100644
index 0000000..1dafe2a
--- /dev/null
+++ b/testing/resources/thumbnail_with_no_filters.pdf
@@ -0,0 +1,53 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Thumb 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 0
+>>
+stream
+endstream
+endobj
+5 0 obj <<
+  /Width 10
+  /Height 10
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Length 301
+>>
+stream
+z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z    z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z  z
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000245 00000 n 
+0000000295 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+720
+%%EOF
diff --git a/testing/resources/uri_action.in b/testing/resources/uri_action.in
new file mode 100644
index 0000000..3ec7ef3
--- /dev/null
+++ b/testing/resources/uri_action.in
@@ -0,0 +1,46 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /T (txtName)
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+{{object 5 0}} <<
+  /S /URI
+  /URI (https://example.com/page.html)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/uri_action.pdf b/testing/resources/uri_action.pdf
new file mode 100644
index 0000000..ede0c5e
--- /dev/null
+++ b/testing/resources/uri_action.pdf
@@ -0,0 +1,58 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+  /A 5 0 R
+  /FT /Tx
+  /Ff 29360128
+  /T (txtName)
+  /Type /Annot
+  /Subtype /Link
+  /F 4
+  /M (D:20150514070426+05'30')
+  /Rect [1 1 199 199]
+  /BS  <<
+    /W 1
+    /S /S
+  >>
+  /DA (/Helv 0 Tf 0 0 0 rg)
+  /V ()
+>>
+endobj
+5 0 obj <<
+  /S /URI
+  /URI (https://example.com/page.html)
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000230 00000 n 
+0000000464 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+534
+%%EOF
diff --git a/testing/resources/vertical_text.in b/testing/resources/vertical_text.in
new file mode 100644
index 0000000..4a874c3
--- /dev/null
+++ b/testing/resources/vertical_text.in
@@ -0,0 +1,92 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type0
+  /Encoding /UniGB-UTF16-V
+  /BaseFont /Helvetica
+  /DescendantFonts [5 0 R]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /CIDFontType2
+  /BaseFont /Helvetica
+  /CIDSystemInfo <</Registry (Adobe) /Ordering (GB1) /Supplement 4>>
+  /FontDescriptor 6 0 R
+  /DW 1000
+  /W [
+    1 [278]  %space
+    2 [278]  %!
+    41 [722] %H
+    56 [944] %W
+    69 [556] %d
+    70 [556] %e
+    77 [222] %l
+    80 [556] %o
+    83 [333] %r
+  ]
+  /DW2 [0 -1000]
+  /W2 [
+    1 [-723 139 623]    %space
+    2 [-918 139 818]    %!
+    41 [-918 361 818]   %H
+    56 [-918 472 818]   %W
+    69 [-918 278 818]   %d
+    70 [-723 278 623]   %e
+    77 [-918 111 818]   %l
+    80 [-723 278 623]   %o
+    83 [-723 166.5 623] %r
+  ]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 718
+  /CapHeight 500
+  /Descent -207
+  /Flags 32
+  /FontBBox [-166 -225 1000 931]
+  /FontName /Helvetica
+  /ItalicAngle 0
+  /StemV 80
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 12 Tf
+10 190 Td
+(\000H\000e\000l\000l\000o\000 ) Tj
+(\000W\000o\000r\000l\000d\000!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/vertical_text.pdf b/testing/resources/vertical_text.pdf
new file mode 100644
index 0000000..37d8115
--- /dev/null
+++ b/testing/resources/vertical_text.pdf
@@ -0,0 +1,106 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type0
+  /Encoding /UniGB-UTF16-V
+  /BaseFont /Helvetica
+  /DescendantFonts [5 0 R]
+>>
+endobj
+5 0 obj <<
+  /Type /Font
+  /Subtype /CIDFontType2
+  /BaseFont /Helvetica
+  /CIDSystemInfo <</Registry (Adobe) /Ordering (GB1) /Supplement 4>>
+  /FontDescriptor 6 0 R
+  /DW 1000
+  /W [
+    1 [278]  %space
+    2 [278]  %!
+    41 [722] %H
+    56 [944] %W
+    69 [556] %d
+    70 [556] %e
+    77 [222] %l
+    80 [556] %o
+    83 [333] %r
+  ]
+  /DW2 [0 -1000]
+  /W2 [
+    1 [-723 139 623]    %space
+    2 [-918 139 818]    %!
+    41 [-918 361 818]   %H
+    56 [-918 472 818]   %W
+    69 [-918 278 818]   %d
+    70 [-723 278 623]   %e
+    77 [-918 111 818]   %l
+    80 [-723 278 623]   %o
+    83 [-723 166.5 623] %r
+  ]
+>>
+endobj
+6 0 obj <<
+  /Type /FontDescriptor
+  /Ascent 718
+  /CapHeight 500
+  /Descent -207
+  /Flags 32
+  /FontBBox [-166 -225 1000 931]
+  /FontName /Helvetica
+  /ItalicAngle 0
+  /StemV 80
+>>
+endobj
+7 0 obj <<
+  /Length 98
+>>
+stream
+BT
+/F1 12 Tf
+10 190 Td
+(\000H\000e\000l\000l\000o\000 ) Tj
+(\000W\000o\000r\000l\000d\000!) Tj
+ET
+endstream
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000287 00000 n 
+0000000417 00000 n 
+0000001039 00000 n 
+0000001228 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+1377
+%%EOF
diff --git a/testing/resources/viewer_ref.pdf b/testing/resources/viewer_ref.pdf
index 851ea27..ca53771 100644
--- a/testing/resources/viewer_ref.pdf
+++ b/testing/resources/viewer_ref.pdf
@@ -4,19 +4,24 @@
   /Type /Catalog
   /Pages 2 0 R
   /ViewerPreferences <<
+    /Direction /R2L
     /Foo /foo
     /HideToolbar true
-    /Direction /R2L
-    /ViewArea /CropBox
     /NumCopies 5
+    /PrintPageRange [0 2 4 4]
+    /ViewArea /CropBox
   >>
 >>
 endobj
 2 0 obj <<
   /Type /Pages
-  /Count 1
+  /Count 5
   /Kids [
     3 0 R
+    3 0 R
+    3 0 R
+    3 0 R
+    3 0 R
   ]
 >>
 endobj
@@ -40,7 +45,7 @@
 endobj
 % Content for page 0.
 21 0 obj <<
-  /Length 0
+  /Length 37
 >>
 stream
 BT
@@ -53,8 +58,8 @@
 0 22
 0000000000 65535 f 
 0000000015 00000 n 
-0000000193 00000 n 
-0000000281 00000 n 
+0000000223 00000 n 
+0000000351 00000 n 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
@@ -66,14 +71,14 @@
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
-0000000442 00000 n 
+0000000512 00000 n 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
 0000000000 65535 f 
-0000000537 00000 n 
+0000000607 00000 n 
 trailer<< /Root 1 0 R /Size 22 >>
 startxref
-625
+696
 %%EOF
diff --git a/testing/resources/xfa/xfa_combobox.in b/testing/resources/xfa/xfa_combobox.in
new file mode 100644
index 0000000..da86e23
--- /dev/null
+++ b/testing/resources/xfa/xfa_combobox.in
@@ -0,0 +1,40 @@
+{{header}}
+{{include ../xfa_catalog_1_0.fragment}}
+{{include ../xfa_object_2_0.fragment}}
+{{include ../xfa_preamble_3_0.fragment}}
+{{include ../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <choiceList open="userControl"/>
+        </ui>
+        <items save="1">
+          <text>clams</text>
+          <text>oysters</text>
+          <text>crabs</text>
+        </items>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../xfa_locale_6_0.fragment}}
+{{include ../xfa_postamble_7_0.fragment}}
+{{include ../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_combobox.pdf b/testing/resources/xfa/xfa_combobox.pdf
new file mode 100644
index 0000000..0989361
--- /dev/null
+++ b/testing/resources/xfa/xfa_combobox.pdf
@@ -0,0 +1,249 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 716
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <choiceList open="userControl"/>
+        </ui>
+        <items save="1">
+          <text>clams</text>
+          <text>oysters</text>
+          <text>crabs</text>
+        </items>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000001996 00000 n 
+0000005504 00000 n 
+0000005566 00000 n 
+0000005629 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+5706
+%%EOF
diff --git a/testing/resources/xfa/xfa_date_time_edit.in b/testing/resources/xfa/xfa_date_time_edit.in
new file mode 100644
index 0000000..a61fc2c
--- /dev/null
+++ b/testing/resources/xfa/xfa_date_time_edit.in
@@ -0,0 +1,35 @@
+{{header}}
+{{include ../xfa_catalog_1_0.fragment}}
+{{include ../xfa_object_2_0.fragment}}
+{{include ../xfa_preamble_3_0.fragment}}
+{{include ../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="TextField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <dateTimeEdit/>
+        </ui>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../xfa_locale_6_0.fragment}}
+{{include ../xfa_postamble_7_0.fragment}}
+{{include ../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_date_time_edit.pdf b/testing/resources/xfa/xfa_date_time_edit.pdf
new file mode 100644
index 0000000..04ea239
--- /dev/null
+++ b/testing/resources/xfa/xfa_date_time_edit.pdf
@@ -0,0 +1,244 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 567
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="TextField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <dateTimeEdit/>
+        </ui>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000001847 00000 n 
+0000005355 00000 n 
+0000005417 00000 n 
+0000005480 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+5557
+%%EOF
diff --git a/testing/resources/xfa/xfa_image_edit.in b/testing/resources/xfa/xfa_image_edit.in
new file mode 100644
index 0000000..0e36fdd
--- /dev/null
+++ b/testing/resources/xfa/xfa_image_edit.in
@@ -0,0 +1,38 @@
+{{header}}
+{{include ../xfa_catalog_1_0.fragment}}
+{{include ../xfa_object_2_0.fragment}}
+{{include ../xfa_preamble_3_0.fragment}}
+{{include ../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <imageEdit data="embed"/>
+        </ui>
+        <value>
+          <image contentType="image/jpg">FOOOEY</image>
+        </value>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../xfa_locale_6_0.fragment}}
+{{include ../xfa_postamble_7_0.fragment}}
+{{include ../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_image_edit.pdf b/testing/resources/xfa/xfa_image_edit.pdf
new file mode 100644
index 0000000..7496367
--- /dev/null
+++ b/testing/resources/xfa/xfa_image_edit.pdf
@@ -0,0 +1,247 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 667
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="ImageField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <imageEdit data="embed"/>
+        </ui>
+        <value>
+          <image contentType="image/jpg">FOOOEY</image>
+        </value>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000001947 00000 n 
+0000005455 00000 n 
+0000005517 00000 n 
+0000005580 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+5657
+%%EOF
diff --git a/testing/resources/xfa/xfa_multiline_textfield.in b/testing/resources/xfa/xfa_multiline_textfield.in
new file mode 100644
index 0000000..235f723
--- /dev/null
+++ b/testing/resources/xfa/xfa_multiline_textfield.in
@@ -0,0 +1,37 @@
+{{header}}
+{{include ../xfa_catalog_1_0.fragment}}
+{{include ../xfa_object_2_0.fragment}}
+{{include ../xfa_preamble_3_0.fragment}}
+{{include ../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="TextField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <textEdit multiLine="1">
+            <font typeface="Helvetica" size="16pt"/>
+          </textEdit>
+        </ui>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../xfa_locale_6_0.fragment}}
+{{include ../xfa_postamble_7_0.fragment}}
+{{include ../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_multiline_textfield.pdf b/testing/resources/xfa/xfa_multiline_textfield.pdf
new file mode 100644
index 0000000..6b7eae4
--- /dev/null
+++ b/testing/resources/xfa/xfa_multiline_textfield.pdf
@@ -0,0 +1,246 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
+2 0 obj <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
+3 0 obj <<
+  /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+  /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+  /Length 651
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+  <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+    <pageSet>
+      <pageArea name="Page1" id="Page1">
+        <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+        <medium stock="default" short="612pt" long="792pt"/>
+      </pageArea>
+    </pageSet>
+    <subform w="576pt" h="756pt" name="Page1">
+      <field name="TextField1" y="0pt" x="0pt" w="425pt" h="80pt">
+        <ui>
+          <textEdit multiLine="1">
+            <font typeface="Helvetica" size="16pt"/>
+          </textEdit>
+        </ui>
+      </field>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+  /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+  /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f 
+0000000015 00000 n 
+0000000199 00000 n 
+0000000358 00000 n 
+0000000534 00000 n 
+0000001228 00000 n 
+0000001931 00000 n 
+0000005439 00000 n 
+0000005501 00000 n 
+0000005564 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 10
+>>
+startxref
+5641
+%%EOF
diff --git a/testing/resources/xfa_catalog_1_0.fragment b/testing/resources/xfa_catalog_1_0.fragment
new file mode 100644
index 0000000..8793a00
--- /dev/null
+++ b/testing/resources/xfa_catalog_1_0.fragment
@@ -0,0 +1,13 @@
+{{object 1 0}} <<
+  /AcroForm 2 0 R
+  /Extensions <<
+    /ADBE <<
+      /BaseVersion /1.7
+      /ExtensionLevel 8
+    >>
+  >>
+  /NeedsRendering true
+  /Pages 8 0 R
+  /Type /Catalog
+>>
+endobj
diff --git a/testing/resources/xfa_config.xml b/testing/resources/xfa_config.xml
new file mode 100644
index 0000000..801fd14
--- /dev/null
+++ b/testing/resources/xfa_config.xml
@@ -0,0 +1,30 @@
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+  <destination>pdf</destination>
+  <pdf>
+    <fontInfo/>
+  </pdf>
+</agent>
+<present>
+  <pdf>
+    <version>1.7</version>
+    <adobeExtensionLevel>8</adobeExtensionLevel>
+    <renderPolicy>client</renderPolicy>
+    <scriptModel>XFA</scriptModel>
+    <interactive>1</interactive>
+  </pdf>
+  <xdp>
+    <packets>*</packets>
+  </xdp>
+  <destination>pdf</destination>
+  <script>
+    <runScripts>server</runScripts>
+  </script>
+</present>
+<acrobat>
+  <acrobat7>
+    <dynamicRender>required</dynamicRender>
+  </acrobat7>
+  <validate>preSubmit</validate>
+</acrobat>
+</config>
diff --git a/testing/resources/xfa_config_4_0.fragment b/testing/resources/xfa_config_4_0.fragment
new file mode 100644
index 0000000..3d3e944
--- /dev/null
+++ b/testing/resources/xfa_config_4_0.fragment
@@ -0,0 +1,7 @@
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include xfa_config.xml}}
+endstream
+endobj
diff --git a/testing/resources/xfa_locale.xml b/testing/resources/xfa_locale.xml
new file mode 100644
index 0000000..2873b5d
--- /dev/null
+++ b/testing/resources/xfa_locale.xml
@@ -0,0 +1,104 @@
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+  <locale name="en_US" desc="English (United States)">
+    <calendarSymbols name="gregorian">
+      <monthNames>
+        <month>January</month>
+        <month>February</month>
+        <month>March</month>
+        <month>April</month>
+        <month>May</month>
+        <month>June</month>
+        <month>July</month>
+        <month>August</month>
+        <month>September</month>
+        <month>October</month>
+        <month>November</month>
+        <month>December</month>
+      </monthNames>
+      <monthNames abbr="1">
+        <month>Jan</month>
+        <month>Feb</month>
+        <month>Mar</month>
+        <month>Apr</month>
+        <month>May</month>
+        <month>Jun</month>
+        <month>Jul</month>
+        <month>Aug</month>
+        <month>Sep</month>
+        <month>Oct</month>
+        <month>Nov</month>
+        <month>Dec</month>
+      </monthNames>
+      <dayNames>
+        <day>Sunday</day>
+        <day>Monday</day>
+        <day>Tuesday</day>
+        <day>Wednesday</day>
+        <day>Thursday</day>
+        <day>Friday</day>
+        <day>Saturday</day>
+      </dayNames>
+      <dayNames abbr="1">
+        <day>Sun</day>
+        <day>Mon</day>
+        <day>Tue</day>
+        <day>Wed</day>
+        <day>Thu</day>
+        <day>Fri</day>
+        <day>Sat</day>
+      </dayNames>
+      <meridiemNames>
+        <meridiem>AM</meridiem>
+        <meridiem>PM</meridiem>
+      </meridiemNames>
+      <eraNames>
+        <era>BC</era>
+        <era>AD</era>
+      </eraNames>
+    </calendarSymbols>
+    <datePatterns>
+      <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+      <datePattern name="long">MMMM D, YYYY</datePattern>
+      <datePattern name="med">MMM D, YYYY</datePattern>
+      <datePattern name="short">M/D/YY</datePattern>
+    </datePatterns>
+    <timePatterns>
+      <timePattern name="full">h:MM:SS A Z</timePattern>
+      <timePattern name="long">h:MM:SS A Z</timePattern>
+      <timePattern name="med">h:MM:SS A</timePattern>
+      <timePattern name="short">h:MM A</timePattern>
+    </timePatterns>
+    <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+    <numberPatterns>
+      <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+      <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+      <numberPattern name="percent">z,zz9%</numberPattern>
+    </numberPatterns>
+    <numberSymbols>
+      <numberSymbol name="decimal">.</numberSymbol>
+      <numberSymbol name="grouping">,</numberSymbol>
+      <numberSymbol name="percent">%</numberSymbol>
+      <numberSymbol name="minus">-</numberSymbol>
+      <numberSymbol name="zero">0</numberSymbol>
+    </numberSymbols>
+    <currencySymbols>
+      <currencySymbol name="symbol">$</currencySymbol>
+      <currencySymbol name="isoname">USD</currencySymbol>
+      <currencySymbol name="decimal">.</currencySymbol>
+    </currencySymbols>
+    <typefaces>
+      <typeface name="Myriad Pro"/>
+      <typeface name="Minion Pro"/>
+      <typeface name="Courier Std"/>
+      <typeface name="Adobe Pi Std"/>
+      <typeface name="Adobe Hebrew"/>
+      <typeface name="Adobe Arabic"/>
+      <typeface name="Adobe Thai"/>
+      <typeface name="Kozuka Gothic Pro-VI M"/>
+      <typeface name="Kozuka Mincho Pro-VI R"/>
+      <typeface name="Adobe Ming Std L"/>
+      <typeface name="Adobe Song Std L"/>
+      <typeface name="Adobe Myungjo Std M"/>
+    </typefaces>
+  </locale>
+</localeSet>
diff --git a/testing/resources/xfa_locale_6_0.fragment b/testing/resources/xfa_locale_6_0.fragment
new file mode 100644
index 0000000..8233e8e
--- /dev/null
+++ b/testing/resources/xfa_locale_6_0.fragment
@@ -0,0 +1,7 @@
+{{object 6 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include xfa_locale.xml}}
+endstream
+endobj
diff --git a/testing/resources/xfa_object_2_0.fragment b/testing/resources/xfa_object_2_0.fragment
new file mode 100644
index 0000000..c8b5a34
--- /dev/null
+++ b/testing/resources/xfa_object_2_0.fragment
@@ -0,0 +1,15 @@
+{{object 2 0}} <<
+  /XFA [
+    (preamble)
+    3 0 R
+    (config)
+    4 0 R
+    (template)
+    5 0 R
+    (localeSet)
+    6 0 R
+    (postamble)
+    7 0 R
+  ]
+>>
+endobj
diff --git a/testing/resources/xfa_object_single_2_0.fragment b/testing/resources/xfa_object_single_2_0.fragment
new file mode 100644
index 0000000..67f66d1
--- /dev/null
+++ b/testing/resources/xfa_object_single_2_0.fragment
@@ -0,0 +1,4 @@
+{{object 2 0}} <<
+  /XFA 3 0 R
+>>
+endobj
diff --git a/testing/resources/xfa_pages_8_0.fragment b/testing/resources/xfa_pages_8_0.fragment
new file mode 100644
index 0000000..ce089c4
--- /dev/null
+++ b/testing/resources/xfa_pages_8_0.fragment
@@ -0,0 +1,12 @@
+{{object 8 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [9 0 R]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
diff --git a/testing/resources/xfa_postamble.xml b/testing/resources/xfa_postamble.xml
new file mode 100644
index 0000000..1d90745
--- /dev/null
+++ b/testing/resources/xfa_postamble.xml
@@ -0,0 +1 @@
+</xdp:xdp>
diff --git a/testing/resources/xfa_postamble_7_0.fragment b/testing/resources/xfa_postamble_7_0.fragment
new file mode 100644
index 0000000..0cc197a
--- /dev/null
+++ b/testing/resources/xfa_postamble_7_0.fragment
@@ -0,0 +1,7 @@
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include xfa_postamble.xml}}
+endstream
+endobj
diff --git a/testing/resources/xfa_preamble.xml b/testing/resources/xfa_preamble.xml
new file mode 100644
index 0000000..a44947f
--- /dev/null
+++ b/testing/resources/xfa_preamble.xml
@@ -0,0 +1 @@
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
diff --git a/testing/resources/xfa_preamble_3_0.fragment b/testing/resources/xfa_preamble_3_0.fragment
new file mode 100644
index 0000000..0dc7939
--- /dev/null
+++ b/testing/resources/xfa_preamble_3_0.fragment
@@ -0,0 +1,7 @@
+{{object 3 0}} <<
+  {{streamlen}}
+>>
+stream
+{{include xfa_preamble.xml}}
+endstream
+endobj
diff --git a/testing/string_write_stream.cpp b/testing/string_write_stream.cpp
new file mode 100644
index 0000000..53141eb
--- /dev/null
+++ b/testing/string_write_stream.cpp
@@ -0,0 +1,32 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/string_write_stream.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/widestring.h"
+
+StringWriteStream::StringWriteStream() = default;
+
+StringWriteStream::~StringWriteStream() = default;
+
+FX_FILESIZE StringWriteStream::GetSize() {
+  return stream_.tellp();
+}
+
+bool StringWriteStream::Flush() {
+  return true;
+}
+
+bool StringWriteStream::WriteBlockAtOffset(const void* pData,
+                                           FX_FILESIZE offset,
+                                           size_t size) {
+  ASSERT(offset == 0);
+  stream_.write(static_cast<const char*>(pData), size);
+  return true;
+}
+
+bool StringWriteStream::WriteString(ByteStringView str) {
+  stream_.write(str.unterminated_c_str(), str.GetLength());
+  return true;
+}
diff --git a/testing/string_write_stream.h b/testing/string_write_stream.h
new file mode 100644
index 0000000..08a2174
--- /dev/null
+++ b/testing/string_write_stream.h
@@ -0,0 +1,32 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_STRING_WRITE_STREAM_H_
+#define TESTING_STRING_WRITE_STREAM_H_
+
+#include <sstream>
+#include <string>
+
+#include "core/fxcrt/fx_stream.h"
+
+class StringWriteStream final : public IFX_SeekableWriteStream {
+ public:
+  StringWriteStream();
+  ~StringWriteStream() override;
+
+  // IFX_SeekableWriteStream
+  FX_FILESIZE GetSize() override;
+  bool Flush() override;
+  bool WriteBlockAtOffset(const void* pData,
+                          FX_FILESIZE offset,
+                          size_t size) override;
+  bool WriteString(ByteStringView str) override;
+
+  std::string ToString() const { return stream_.str(); }
+
+ private:
+  std::ostringstream stream_;
+};
+
+#endif  // TESTING_STRING_WRITE_STREAM_H_
diff --git a/testing/test.gni b/testing/test.gni
index 3b9eddc..5a8505f 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -62,9 +62,6 @@
 
         # Don't output to the root or else conflict with the group() below.
         output_name = rebase_path(_exec_output, root_out_dir)
-        if (is_component_build || is_asan) {
-          data_deps += [ "//build/android:cpplib_stripped" ]
-        }
       }
 
       create_native_executable_dist(_dist_target) {
@@ -227,10 +224,6 @@
       }
       extra_substitutions += [ "GTEST_BUNDLE_ID_SUFFIX=$_bundle_id_suffix" ]
 
-      if (!defined(deps)) {
-        deps = []
-      }
-      deps += [ "//build/config:exe_and_shlib_deps" ]
       if (!defined(bundle_deps)) {
         bundle_deps = []
       }
@@ -243,8 +236,6 @@
 
       testonly = true
       deps += [
-        "//build/config:exe_and_shlib_deps",
-
         # Give tests the default manifest on Windows (a no-op elsewhere).
         "//build/win:default_exe_manifest",
       ]
@@ -269,3 +260,45 @@
     configs = default_executable_configs
   }
 }
+
+template("pdfium_unittest_source_set") {
+  source_set(target_name) {
+    _pdfium_root_dir = rebase_path(invoker.pdfium_root_dir, ".")
+
+    testonly = true
+    sources = invoker.sources
+    configs += [ _pdfium_root_dir + ":pdfium_core_config" ]
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
+    deps = [
+      _pdfium_root_dir + ":pdfium_unittest_deps",
+    ]
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+    visibility = [ _pdfium_root_dir + ":*" ]
+    forward_variables_from(invoker, [ "cflags" ])
+  }
+}
+
+template("pdfium_embeddertest_source_set") {
+  source_set(target_name) {
+    _pdfium_root_dir = rebase_path(invoker.pdfium_root_dir, ".")
+
+    testonly = true
+    sources = invoker.sources
+    configs += [ _pdfium_root_dir + ":pdfium_core_config" ]
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
+    deps = [
+      _pdfium_root_dir + ":pdfium_embeddertest_deps",
+    ]
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+    visibility = [ _pdfium_root_dir + ":*" ]
+    forward_variables_from(invoker, [ "cflags" ])
+  }
+}
diff --git a/testing/test_loader.cpp b/testing/test_loader.cpp
new file mode 100644
index 0000000..33ee331
--- /dev/null
+++ b/testing/test_loader.cpp
@@ -0,0 +1,26 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/test_loader.h"
+
+#include <string.h>
+
+#include "third_party/base/logging.h"
+
+TestLoader::TestLoader(pdfium::span<const char> span) : m_Span(span) {}
+
+// static
+int TestLoader::GetBlock(void* param,
+                         unsigned long pos,
+                         unsigned char* pBuf,
+                         unsigned long size) {
+  TestLoader* pLoader = static_cast<TestLoader*>(param);
+  if (pos + size < pos || pos + size > pLoader->m_Span.size()) {
+    NOTREACHED();
+    return 0;
+  }
+
+  memcpy(pBuf, &pLoader->m_Span[pos], size);
+  return 1;
+}
diff --git a/testing/test_loader.h b/testing/test_loader.h
new file mode 100644
index 0000000..17ca9e9
--- /dev/null
+++ b/testing/test_loader.h
@@ -0,0 +1,23 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_TEST_LOADER_H_
+#define TESTING_TEST_LOADER_H_
+
+#include "third_party/base/span.h"
+
+class TestLoader {
+ public:
+  explicit TestLoader(pdfium::span<const char> span);
+
+  static int GetBlock(void* param,
+                      unsigned long pos,
+                      unsigned char* pBuf,
+                      unsigned long size);
+
+ private:
+  const pdfium::span<const char> m_Span;
+};
+
+#endif  // TESTING_TEST_LOADER_H_
diff --git a/testing/test_support.cpp b/testing/test_support.cpp
index b32ec7d..94ce528 100644
--- a/testing/test_support.cpp
+++ b/testing/test_support.cpp
@@ -1,220 +1,15 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2019 PDFium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/test_support.h"
 
-#include <stdio.h>
-#include <string.h>
+#include "core/fxge/cfx_gemodule.h"
 
-#include "core/fdrm/crypto/fx_crypt.h"
-#include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_string.h"
-#include "testing/utils/path_service.h"
-
-#ifdef PDF_ENABLE_V8
-#include "v8/include/libplatform/libplatform.h"
-#include "v8/include/v8.h"
-#endif
-
-namespace {
-
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-// Returns the full path for an external V8 data file based on either
-// the currect exectuable path or an explicit override.
-std::string GetFullPathForSnapshotFile(const std::string& exe_path,
-                                       const std::string& bin_dir,
-                                       const std::string& filename) {
-  std::string result;
-  if (!bin_dir.empty()) {
-    result = bin_dir;
-    if (*bin_dir.rbegin() != PATH_SEPARATOR) {
-      result += PATH_SEPARATOR;
-    }
-  } else if (!exe_path.empty()) {
-    size_t last_separator = exe_path.rfind(PATH_SEPARATOR);
-    if (last_separator != std::string::npos) {
-      result = exe_path.substr(0, last_separator + 1);
-    }
-  }
-  result += filename;
-  return result;
+void InitializePDFTestEnvironment() {
+  CFX_GEModule::Create(nullptr);
 }
 
-bool GetExternalData(const std::string& exe_path,
-                     const std::string& bin_dir,
-                     const std::string& filename,
-                     v8::StartupData* result_data) {
-  std::string full_path =
-      GetFullPathForSnapshotFile(exe_path, bin_dir, filename);
-  size_t data_length = 0;
-  std::unique_ptr<char, pdfium::FreeDeleter> data_buffer =
-      GetFileContents(full_path.c_str(), &data_length);
-  if (!data_buffer)
-    return false;
-
-  result_data->data = data_buffer.release();
-  result_data->raw_size = data_length;
-  return true;
-}
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-
-void InitializeV8Common(const char* exe_path, v8::Platform** platform) {
-  v8::V8::InitializeICUDefaultLocation(exe_path);
-
-  *platform = v8::platform::CreateDefaultPlatform();
-  v8::V8::InitializePlatform(*platform);
-
-  // By enabling predictable mode, V8 won't post any background tasks.
-  // By enabling GC, it makes it easier to chase use-after-free.
-  const char v8_flags[] = "--predictable --expose-gc";
-  v8::V8::SetFlagsFromString(v8_flags, static_cast<int>(strlen(v8_flags)));
-  v8::V8::Initialize();
-}
-#endif  // PDF_ENABLE_V8
-
-}  // namespace
-
-std::unique_ptr<char, pdfium::FreeDeleter> GetFileContents(const char* filename,
-                                                           size_t* retlen) {
-  FILE* file = fopen(filename, "rb");
-  if (!file) {
-    fprintf(stderr, "Failed to open: %s\n", filename);
-    return nullptr;
-  }
-  (void)fseek(file, 0, SEEK_END);
-  size_t file_length = ftell(file);
-  if (!file_length) {
-    return nullptr;
-  }
-  (void)fseek(file, 0, SEEK_SET);
-  std::unique_ptr<char, pdfium::FreeDeleter> buffer(
-      static_cast<char*>(malloc(file_length)));
-  if (!buffer) {
-    return nullptr;
-  }
-  size_t bytes_read = fread(buffer.get(), 1, file_length, file);
-  (void)fclose(file);
-  if (bytes_read != file_length) {
-    fprintf(stderr, "Failed to read: %s\n", filename);
-    return nullptr;
-  }
-  *retlen = bytes_read;
-  return buffer;
-}
-
-std::string GetPlatformString(FPDF_WIDESTRING wstr) {
-  WideString wide_string =
-      WideString::FromUTF16LE(wstr, WideString::WStringLength(wstr));
-  return std::string(wide_string.UTF8Encode().c_str());
-}
-
-std::wstring GetPlatformWString(FPDF_WIDESTRING wstr) {
-  if (!wstr)
-    return nullptr;
-
-  size_t characters = 0;
-  while (wstr[characters])
-    ++characters;
-
-  std::wstring platform_string(characters, L'\0');
-  for (size_t i = 0; i < characters + 1; ++i) {
-    const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&wstr[i]);
-    platform_string[i] = ptr[0] + 256 * ptr[1];
-  }
-  return platform_string;
-}
-
-std::vector<std::string> StringSplit(const std::string& str, char delimiter) {
-  std::vector<std::string> result;
-  size_t pos = 0;
-  while (1) {
-    size_t found = str.find(delimiter, pos);
-    if (found == std::string::npos)
-      break;
-
-    result.push_back(str.substr(pos, found - pos));
-    pos = found + 1;
-  }
-  result.push_back(str.substr(pos));
-  return result;
-}
-
-std::unique_ptr<unsigned short, pdfium::FreeDeleter> GetFPDFWideString(
-    const std::wstring& wstr) {
-  size_t length = sizeof(uint16_t) * (wstr.length() + 1);
-  std::unique_ptr<unsigned short, pdfium::FreeDeleter> result(
-      static_cast<unsigned short*>(malloc(length)));
-  char* ptr = reinterpret_cast<char*>(result.get());
-  size_t i = 0;
-  for (wchar_t w : wstr) {
-    ptr[i++] = w & 0xff;
-    ptr[i++] = (w >> 8) & 0xff;
-  }
-  ptr[i++] = 0;
-  ptr[i] = 0;
-  return result;
-}
-
-std::string CryptToBase16(const uint8_t* digest) {
-  static char const zEncode[] = "0123456789abcdef";
-  std::string ret;
-  ret.resize(32);
-  for (int i = 0, j = 0; i < 16; i++, j += 2) {
-    uint8_t a = digest[i];
-    ret[j] = zEncode[(a >> 4) & 0xf];
-    ret[j + 1] = zEncode[a & 0xf];
-  }
-  return ret;
-}
-
-std::string GenerateMD5Base16(const uint8_t* data, uint32_t size) {
-  uint8_t digest[16];
-  CRYPT_MD5Generate(data, size, digest);
-  return CryptToBase16(digest);
-}
-
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-bool InitializeV8ForPDFium(const std::string& exe_path,
-                           const std::string& bin_dir,
-                           v8::StartupData* natives_blob,
-                           v8::StartupData* snapshot_blob,
-                           v8::Platform** platform) {
-  InitializeV8Common(exe_path.c_str(), platform);
-  if (natives_blob && snapshot_blob) {
-    if (!GetExternalData(exe_path, bin_dir, "natives_blob.bin", natives_blob))
-      return false;
-    if (!GetExternalData(exe_path, bin_dir, "snapshot_blob.bin", snapshot_blob))
-      return false;
-    v8::V8::SetNativesDataBlob(natives_blob);
-    v8::V8::SetSnapshotDataBlob(snapshot_blob);
-  }
-  return true;
-}
-#else   // V8_USE_EXTERNAL_STARTUP_DATA
-bool InitializeV8ForPDFium(const std::string& exe_path,
-                           v8::Platform** platform) {
-  InitializeV8Common(exe_path.c_str(), platform);
-  return true;
-}
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // PDF_ENABLE_V8
-
-TestLoader::TestLoader(const char* pBuf, size_t len)
-    : m_pBuf(pBuf), m_Len(len) {
-}
-
-// static
-int TestLoader::GetBlock(void* param,
-                         unsigned long pos,
-                         unsigned char* pBuf,
-                         unsigned long size) {
-  TestLoader* pLoader = static_cast<TestLoader*>(param);
-  if (pos + size < pos || pos + size > pLoader->m_Len)
-    return 0;
-
-  memcpy(pBuf, pLoader->m_pBuf + pos, size);
-  return 1;
+void DestroyPDFTestEnvironment() {
+  CFX_GEModule::Destroy();
 }
diff --git a/testing/test_support.h b/testing/test_support.h
index ec4b4ae..f237ea5 100644
--- a/testing/test_support.h
+++ b/testing/test_support.h
@@ -5,17 +5,7 @@
 #ifndef TESTING_TEST_SUPPORT_H_
 #define TESTING_TEST_SUPPORT_H_
 
-#include <stdlib.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "public/fpdfview.h"
-
-#if PDF_ENABLE_XFA
-#include "xfa/fgas/font/cfgas_fontmgr.h"
-#endif  // PDF_ENABLE_XFA
+#include <stdint.h>
 
 namespace pdfium {
 
@@ -54,69 +44,9 @@
   const wchar_t* expected;
 };
 
-// Used with std::unique_ptr to free() objects that can't be deleted.
-struct FreeDeleter {
-  inline void operator()(void* ptr) const { free(ptr); }
-};
-
 }  // namespace pdfium
 
-// Reads the entire contents of a file into a newly alloc'd buffer.
-std::unique_ptr<char, pdfium::FreeDeleter> GetFileContents(const char* filename,
-                                                           size_t* retlen);
-
-std::vector<std::string> StringSplit(const std::string& str, char delimiter);
-
-// Converts a FPDF_WIDESTRING to a std::string.
-// Deals with differences between UTF16LE and UTF8.
-std::string GetPlatformString(FPDF_WIDESTRING wstr);
-
-// Converts a FPDF_WIDESTRING to a std::wstring.
-// Deals with differences between UTF16LE and wchar_t.
-std::wstring GetPlatformWString(FPDF_WIDESTRING wstr);
-
-// Returns a newly allocated FPDF_WIDESTRING.
-// Deals with differences between UTF16LE and wchar_t.
-std::unique_ptr<unsigned short, pdfium::FreeDeleter> GetFPDFWideString(
-    const std::wstring& wstr);
-
-std::string CryptToBase16(const uint8_t* digest);
-std::string GenerateMD5Base16(const uint8_t* data, uint32_t size);
-
-#ifdef PDF_ENABLE_V8
-namespace v8 {
-class Platform;
-}
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-namespace v8 {
-class StartupData;
-}
-bool InitializeV8ForPDFium(const std::string& exe_path,
-                           const std::string& bin_dir,
-                           v8::StartupData* natives_blob,
-                           v8::StartupData* snapshot_blob,
-                           v8::Platform** platform);
-#else   // V8_USE_EXTERNAL_STARTUP_DATA
-bool InitializeV8ForPDFium(const std::string& exe_path,
-                           v8::Platform** platform);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // PDF_ENABLE_V8
-
-class TestLoader {
- public:
-  TestLoader(const char* pBuf, size_t len);
-  static int GetBlock(void* param,
-                      unsigned long pos,
-                      unsigned char* pBuf,
-                      unsigned long size);
-
- private:
-  const char* const m_pBuf;
-  const size_t m_Len;
-};
-
-#if PDF_ENABLE_XFA
-CFGAS_FontMgr* GetGlobalFontManager();
-#endif  // PDF_ENABLE_XFA
+void InitializePDFTestEnvironment();
+void DestroyPDFTestEnvironment();
 
 #endif  // TESTING_TEST_SUPPORT_H_
diff --git a/testing/tools/.style.yapf b/testing/tools/.style.yapf
new file mode 100644
index 0000000..de0c6a7
--- /dev/null
+++ b/testing/tools/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = chromium
diff --git a/testing/tools/PRESUBMIT.py b/testing/tools/PRESUBMIT.py
new file mode 100644
index 0000000..59a3858
--- /dev/null
+++ b/testing/tools/PRESUBMIT.py
@@ -0,0 +1,26 @@
+# Copyright 2019 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Presubmit script for PDFium testing tools.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details on the presubmit API built into depot_tools.
+"""
+
+
+def _CommonChecks(input_api, output_api):
+  tests = []
+  tests.extend(input_api.canned_checks.GetPylint(input_api, output_api))
+  return tests
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  tests = []
+  tests.extend(_CommonChecks(input_api, output_api))
+  return input_api.RunTests(tests)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  tests = []
+  tests.extend(_CommonChecks(input_api, output_api))
+  return input_api.RunTests(tests)
diff --git a/testing/tools/api_check.py b/testing/tools/api_check.py
index ee769bc..66b3077 100755
--- a/testing/tools/api_check.py
+++ b/testing/tools/api_check.py
@@ -2,13 +2,13 @@
 # Copyright 2017 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
-"""Verifies exported functions in public/*.h are in fpdfview_c_api_test.c.
+"""Verifies exported functions in public/*.h are in fpdf_view_c_api_test.c.
 
 This script gathers a list of functions from public/*.h that contain
 FPDF_EXPORT. It then gathers a list of functions from
-fpdfsdk/fpdfview_c_api_test.c. It then verifies both lists do not contain
-duplicates, and they match each other.
+fpdfsdk/fpdf_view_c_api_test.c. It then verifies both lists do not contain
+duplicates, and they match each other. It also checks that the order in
+fpdf_view_c_api_test.c is alphabetical within each section.
 
 """
 
@@ -16,10 +16,11 @@
 import re
 import sys
 
+
 def _IsValidFunctionName(function, filename):
   if function.startswith('FPDF'):
     return True
-  if function == 'FSDK_SetUnSpObjProcessHandler' and filename == 'fpdf_ext.h':
+  if function.startswith('FSDK_') and filename == 'fpdf_ext.h':
     return True
   if function.startswith('FORM_') and filename == 'fpdf_formfill.h':
     return True
@@ -70,15 +71,30 @@
   return functions
 
 
+def _CheckSorted(functions):
+  unsorted_functions = set()
+  for i in range(len(functions) - 1):
+    if functions[i] > functions[i + 1]:
+      unsorted_functions.add(functions[i])
+      unsorted_functions.add(functions[i + 1])
+  return unsorted_functions
+
+
 def _GetFunctionsFromTest(api_test_path):
   chk_regex = re.compile('^    CHK\((.*)\);\n$')
+  file_regex = re.compile('^    //.*\.h\n$')
   with open(api_test_path) as f:
     contents = f.readlines()
     functions = []
+    functions_in_file = []
     for line in contents:
+      if (file_regex.match(line)):
+        functions.append(functions_in_file)
+        functions_in_file = []
       match = chk_regex.match(line)
       if match:
-        functions.append(match.groups()[0])
+        functions_in_file.append(match.groups()[0])
+    functions.append(functions_in_file)
     return functions
 
 
@@ -101,19 +117,31 @@
   src_path = os.path.dirname(os.path.dirname(os.path.dirname(script_abspath)))
   public_functions = _GetFunctionsFromPublicHeaders(src_path)
 
-  api_test_relative_path = os.path.join('fpdfsdk', 'fpdfview_c_api_test.c')
+  api_test_relative_path = os.path.join('fpdfsdk', 'fpdf_view_c_api_test.c')
   api_test_path = os.path.join(src_path, api_test_relative_path)
-  test_functions = _GetFunctionsFromTest(api_test_path)
-
+  test_functions_per_section = _GetFunctionsFromTest(api_test_path)
   result = True
-  duplicate_public_functions = _FindDuplicates(public_functions)
-  check = _CheckAndPrintFailures(duplicate_public_functions,
-                                'Found duplicate functions in public headers')
+  unsorted_functions = set()
+  for functions in test_functions_per_section:
+    unsorted_functions |= _CheckSorted(functions)
+  check = _CheckAndPrintFailures(
+      unsorted_functions,
+      'Found CHKs that are not in alphabetical order within each section in %s'
+      % api_test_path)
   result = result and check
 
+  duplicate_public_functions = _FindDuplicates(public_functions)
+  check = _CheckAndPrintFailures(duplicate_public_functions,
+                                 'Found duplicate functions in public headers')
+  result = result and check
+
+  test_functions = [
+      function for functions in test_functions_per_section
+      for function in functions
+  ]
   duplicate_test_functions = _FindDuplicates(test_functions)
   check = _CheckAndPrintFailures(duplicate_test_functions,
-                                'Found duplicate functions in API test')
+                                 'Found duplicate functions in API test')
   result = result and check
 
   public_functions_set = set(public_functions)
@@ -126,9 +154,8 @@
   result = result and check
 
   if not result:
-    print ('Some checks failed. Make sure %s is in sync with the public API '
-           'headers.'
-           % api_test_relative_path);
+    print('Some checks failed. Make sure %s is in sync with the public API '
+          'headers.' % api_test_relative_path)
     return 1
 
   return 0
diff --git a/testing/tools/common.py b/testing/tools/common.py
index fc16004..108fcfd 100755
--- a/testing/tools/common.py
+++ b/testing/tools/common.py
@@ -10,6 +10,7 @@
 import subprocess
 import sys
 
+
 def os_name():
   if sys.platform.startswith('linux'):
     return 'linux'
@@ -28,7 +29,8 @@
     return e
 
 
-def RunCommandPropagateErr(cmd, stdout_has_errors=False,
+def RunCommandPropagateErr(cmd,
+                           stdout_has_errors=False,
                            exit_status_on_error=None):
   """Run a command as a subprocess.
 
@@ -72,7 +74,7 @@
     for line in output.split('\n'):
       line = line.strip()
       if line.startswith("MD5:"):
-          ret.append([x.strip() for x in line.lstrip("MD5:").rsplit(":", 1)])
+        ret.append([x.strip() for x in line.lstrip("MD5:").rsplit(":", 1)])
     return None, ret
   except subprocess.CalledProcessError as e:
     return e, None
@@ -141,14 +143,14 @@
   os.chdir(cwd)
   arg_match_output = re.search('%s = (.*)' % arg_name, gn_args_output).group(1)
   if verbose:
-    print >> sys.stderr, "Found '%s' for value of %s" % (arg_match_output, arg)
+    print >> sys.stderr, "Found '%s' for value of %s" % (arg_match_output,
+                                                         arg_name)
   return arg_match_output == 'true'
 
 
 def PrintWithTime(s):
   """Prints s prepended by a timestamp."""
-  print '[%s] %s' % (datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"),
-                     s)
+  print '[%s] %s' % (datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"), s)
 
 
 def PrintErr(s):
diff --git a/testing/tools/coverage/coverage_report.py b/testing/tools/coverage/coverage_report.py
index 95c88bf..5785eab 100755
--- a/testing/tools/coverage/coverage_report.py
+++ b/testing/tools/coverage/coverage_report.py
@@ -2,53 +2,55 @@
 # Copyright 2017 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+"""Generates a coverage report for given tests.
 
-"""Generates a coverage report for given binaries using llvm-gcov & lcov.
-
-Requires llvm-cov 3.5 or later.
-Requires lcov 1.11 or later.
-Requires that 'use_coverage = true' is set in args.gn.
+Requires that 'use_clang_coverage = true' is set in args.gn.
+Prefers that 'is_component_build = false' is set in args.gn.
 """
 
 import argparse
 from collections import namedtuple
+import fnmatch
 import os
 import pprint
-import re
 import subprocess
 import sys
 
-
 # Add src dir to path to avoid having to set PYTHONPATH.
 sys.path.append(
     os.path.abspath(
-       os.path.join(
-          os.path.dirname(__file__),
-          os.path.pardir,
-          os.path.pardir,
-          os.path.pardir)))
+        os.path.join(
+            os.path.dirname(__file__), os.path.pardir, os.path.pardir,
+            os.path.pardir)))
 
 from testing.tools.common import GetBooleanGnArg
 
-
 # 'binary' is the file that is to be run for the test.
 # 'use_test_runner' indicates if 'binary' depends on test_runner.py and thus
 # requires special handling.
-TestSpec = namedtuple('TestSpec', 'binary, use_test_runner')
+# 'opt_args' are optional arguments to pass to the test 'binary'.
+TestSpec = namedtuple('TestSpec', 'binary, use_test_runner, opt_args')
 
 # All of the coverage tests that the script knows how to run.
 COVERAGE_TESTS = {
-    'pdfium_unittests': TestSpec('pdfium_unittests', False),
-    'pdfium_embeddertests': TestSpec('pdfium_embeddertests', False),
-    'corpus_tests': TestSpec('run_corpus_tests.py', True),
-    'javascript_tests': TestSpec('run_javascript_tests.py', True),
-    'pixel_tests': TestSpec('run_pixel_tests.py', True),
+    'pdfium_unittests':
+        TestSpec('pdfium_unittests', False, []),
+    'pdfium_embeddertests':
+        TestSpec('pdfium_embeddertests', False, []),
+    'corpus_tests':
+        TestSpec('run_corpus_tests.py', True, []),
+    'corpus_tests_javascript_disabled':
+        TestSpec('run_corpus_tests.py', True, ['--disable-javascript']),
+    'javascript_tests':
+        TestSpec('run_javascript_tests.py', True, []),
+    'javascript_tests_javascript_disabled':
+        TestSpec('run_javascript_tests.py', True, ['--disable-javascript']),
+    'pixel_tests':
+        TestSpec('run_pixel_tests.py', True, []),
+    'pixel_tests_javascript_disabled':
+        TestSpec('run_pixel_tests.py', True, ['--disable-javascript']),
 }
 
-# Coverage tests that are known to take a long time to run, so are not in the
-# default set. The user must either explicitly invoke these tests or pass in
-# --slow.
-SLOW_TESTS = ['corpus_tests', 'javascript_tests', 'pixel_tests']
 
 class CoverageExecutor(object):
 
@@ -62,37 +64,32 @@
     self.dry_run = args['dry_run']
     self.verbose = args['verbose']
 
-    llvm_cov = self.determine_proper_llvm_cov()
-    if not llvm_cov:
-      print 'Unable to find appropriate llvm-cov to use'
-      sys.exit(1)
-    self.lcov_env = os.environ
-    self.lcov_env['LLVM_COV_BIN'] = llvm_cov
-
-    self.lcov = self.determine_proper_lcov()
-    if not self.lcov:
-      print 'Unable to find appropriate lcov to use'
-      sys.exit(1)
-
-    self.coverage_files = set()
     self.source_directory = args['source_directory']
     if not os.path.isdir(self.source_directory):
       parser.error("'%s' needs to be a directory" % self.source_directory)
 
+    self.llvm_directory = os.path.join(self.source_directory, 'third_party',
+                                       'llvm-build', 'Release+Asserts', 'bin')
+    if not os.path.isdir(self.llvm_directory):
+      parser.error("Cannot find LLVM bin directory , expected it to be in '%s'"
+                   % self.llvm_directory)
+
     self.build_directory = args['build_directory']
     if not os.path.isdir(self.build_directory):
       parser.error("'%s' needs to be a directory" % self.build_directory)
 
-    self.coverage_tests = self.calculate_coverage_tests(args)
+    (self.coverage_tests,
+     self.build_targets) = self.calculate_coverage_tests(args)
     if not self.coverage_tests:
       parser.error(
           'No valid tests in set to be run. This is likely due to bad command '
           'line arguments')
 
-    if not GetBooleanGnArg('use_coverage', self.build_directory, self.verbose):
+    if not GetBooleanGnArg('use_clang_coverage', self.build_directory,
+                           self.verbose):
       parser.error(
-          'use_coverage does not appear to be set to true for build, but is '
-          'needed')
+          'use_clang_coverage does not appear to be set to true for build, but '
+          'is needed')
 
     self.use_goma = GetBooleanGnArg('use_goma', self.build_directory,
                                     self.verbose)
@@ -103,8 +100,11 @@
         os.makedirs(self.output_directory)
     elif not os.path.isdir(self.output_directory):
       parser.error('%s exists, but is not a directory' % self.output_directory)
-    self.coverage_totals_path = os.path.join(self.output_directory,
-                                             'pdfium_totals.info')
+    elif len(os.listdir(self.output_directory)) > 0:
+      parser.error('%s is not empty, cowardly refusing to continue' %
+                   self.output_directory)
+
+    self.prof_data = os.path.join(self.output_directory, 'pdfium.profdata')
 
   def check_output(self, args, dry_run=False, env=None):
     """Dry run aware wrapper of subprocess.check_output()"""
@@ -130,96 +130,50 @@
       print 'call(%s) returned %s' % (args, output)
     return output
 
-  def call_lcov(self, args, dry_run=False, needs_directory=True):
-    """Wrapper to call lcov that adds appropriate arguments as needed."""
-    lcov_args = [
-        self.lcov, '--config-file',
-        os.path.join(self.source_directory, 'testing', 'tools', 'coverage',
-                     'lcovrc'),
-        '--gcov-tool',
-        os.path.join(self.source_directory, 'testing', 'tools', 'coverage',
-                     'llvm-gcov')
-    ]
-    if needs_directory:
-      lcov_args.extend(['--directory', self.source_directory])
-    if not self.verbose:
-      lcov_args.append('--quiet')
-    lcov_args.extend(args)
-    return self.call(lcov_args, dry_run=dry_run, env=self.lcov_env)
+  def call_silent(self, args, dry_run=False, env=None):
+    """Dry run aware wrapper of subprocess.call() that eats output from call"""
+    if dry_run:
+      print "Would have run '%s'" % ' '.join(args)
+      return 0
+
+    with open(os.devnull, 'w') as f:
+      output = subprocess.call(args, env=env, stdout=f)
+
+    if self.verbose:
+      print 'call_silent(%s) returned %s' % (args, output)
+    return output
 
   def calculate_coverage_tests(self, args):
     """Determine which tests should be run."""
     testing_tools_directory = os.path.join(self.source_directory, 'testing',
                                            'tools')
+    tests = args['tests'] if args['tests'] else COVERAGE_TESTS.keys()
     coverage_tests = {}
-    for name in COVERAGE_TESTS.keys():
+    build_targets = set()
+    for name in tests:
       test_spec = COVERAGE_TESTS[name]
       if test_spec.use_test_runner:
         binary_path = os.path.join(testing_tools_directory, test_spec.binary)
+        build_targets.add('pdfium_diff')
+        build_targets.add('pdfium_test')
       else:
         binary_path = os.path.join(self.build_directory, test_spec.binary)
-      coverage_tests[name] = TestSpec(binary_path, test_spec.use_test_runner)
+        build_targets.add(name)
+      coverage_tests[name] = TestSpec(binary_path, test_spec.use_test_runner,
+                                      test_spec.opt_args)
 
-    if args['tests']:
-      return {name: spec
-        for name, spec in coverage_tests.iteritems() if name in args['tests']}
-    elif not args['slow']:
-      return {name: spec
-        for name, spec in coverage_tests.iteritems() if name not in SLOW_TESTS}
-    else:
-      return coverage_tests
+    build_targets = list(build_targets)
 
-  def find_acceptable_binary(self, binary_name, version_regex,
-                             min_major_version, min_minor_version):
-    """Find the newest version of binary that meets the min version."""
-    min_version = (min_major_version, min_minor_version)
-    parsed_versions = {}
-    # When calling Bash builtins like this the command and arguments must be
-    # passed in as a single string instead of as separate list members.
-    potential_binaries = self.check_output(
-        ['bash', '-c', 'compgen -abck %s' % binary_name]).splitlines()
-    for binary in potential_binaries:
-      if self.verbose:
-        print 'Testing llvm-cov binary, %s' % binary
-      # Assuming that scripts that don't respond to --version correctly are not
-      # valid binaries and just happened to get globbed in. This is true for
-      # lcov and llvm-cov
-      try:
-        version_output = self.check_output([binary, '--version']).splitlines()
-      except subprocess.CalledProcessError:
-        if self.verbose:
-          print '--version returned failure status 1, so ignoring'
-        continue
-
-      for line in version_output:
-        matcher = re.match(version_regex, line)
-        if matcher:
-          parsed_version = (int(matcher.group(1)), int(matcher.group(2)))
-          if parsed_version >= min_version:
-            parsed_versions[parsed_version] = binary
-          break
-
-    if not parsed_versions:
-      return None
-    return parsed_versions[max(parsed_versions)]
-
-  def determine_proper_llvm_cov(self):
-    """Find a version of llvm_cov that will work with the script."""
-    version_regex = re.compile('.*LLVM version ([\d]+)\.([\d]+).*')
-    return self.find_acceptable_binary('llvm-cov', version_regex, 3, 5)
-
-  def determine_proper_lcov(self):
-    """Find a version of lcov that will work with the script."""
-    version_regex = re.compile('.*LCOV version ([\d]+)\.([\d]+).*')
-    return self.find_acceptable_binary('lcov', version_regex, 1, 11)
+    return coverage_tests, build_targets
 
   def build_binaries(self):
     """Build all the binaries that are going to be needed for coverage
     generation."""
     call_args = ['ninja']
     if self.use_goma:
-      call_args.extend(['-j', '250'])
-    call_args.extend(['-C', self.build_directory])
+      call_args += ['-j', '250']
+    call_args += ['-C', self.build_directory]
+    call_args += self.build_targets
     return self.call(call_args, dry_run=self.dry_run) == 0
 
   def generate_coverage(self, name, spec):
@@ -229,8 +183,7 @@
         name: Name associated with the test to be run. This is used as a label
               in the coverage data, so should be unique across all of the tests
               being run.
-        spec: Tuple containing the path to the binary to run, and if this test
-              uses test_runner.py.
+        spec: Tuple containing the TestSpec.
     """
     if self.verbose:
       print "Generating coverage for test '%s', using data '%s'" % (name, spec)
@@ -239,74 +192,78 @@
             ' @ %s') % (name, spec.binary)
       return False
 
-    if self.call_lcov(['--zerocounters'], dry_run=self.dry_run):
-      print 'Unable to clear counters for %s' % name
-      return False
-
     binary_args = [spec.binary]
+    if spec.opt_args:
+      binary_args.extend(spec.opt_args)
+    profile_pattern_string = '%8m'
+    expected_profraw_file = '%s.%s.profraw' % (name, profile_pattern_string)
+    expected_profraw_path = os.path.join(self.output_directory,
+                                         expected_profraw_file)
+
+    env = {
+        'LLVM_PROFILE_FILE': expected_profraw_path,
+        'PATH': os.getenv('PATH') + os.pathsep + self.llvm_directory
+    }
+
     if spec.use_test_runner:
       # Test runner performs multi-threading in the wrapper script, not the test
-      # binary, so need -j 1, otherwise multiple processes will be writing to
-      # the code coverage files, invalidating results.
-      # TODO(pdfium:811): Rewrite how test runner tests work, so that they can
-      # be run in multi-threaded mode.
-      binary_args.extend(['-j', '1', '--build-dir', self.build_directory])
-    if self.call(binary_args, dry_run=self.dry_run) and self.verbose:
+      # binary, so need to limit the number of instances of the binary being run
+      # to the max value in LLVM_PROFILE_FILE, which is 8.
+      binary_args.extend(['-j', '8', '--build-dir', self.build_directory])
+    if self.call(binary_args, dry_run=self.dry_run, env=env) and self.verbose:
       print('Running %s appears to have failed, which might affect '
             'results') % spec.binary
 
-    output_raw_path = os.path.join(self.output_directory, '%s_raw.info' % name)
-    if self.call_lcov(
-        ['--capture', '--test-name', name, '--output-file', output_raw_path],
-        dry_run=self.dry_run):
-      print 'Unable to capture coverage data for %s' % name
-      return False
-
-    output_filtered_path = os.path.join(self.output_directory,
-                                        '%s_filtered.info' % name)
-    output_filters = [
-        '/usr/include/*', '*third_party*', '*testing*', '*_unittest.cpp',
-        '*_embeddertest.cpp'
-    ]
-    if self.call_lcov(
-        ['--remove', output_raw_path] + output_filters +
-        ['--output-file', output_filtered_path],
-        dry_run=self.dry_run,
-        needs_directory=False):
-      print 'Unable to filter coverage data for %s' % name
-      return False
-
-    self.coverage_files.add(output_filtered_path)
     return True
 
-  def merge_coverage(self):
-    """Merge all of the coverage data sets into one for report generation."""
-    merge_args = []
-    for coverage_file in self.coverage_files:
-      merge_args.extend(['--add-tracefile', coverage_file])
+  def merge_raw_coverage_results(self):
+    """Merge raw coverage data sets into one one file for report generation."""
+    llvm_profdata_bin = os.path.join(self.llvm_directory, 'llvm-profdata')
 
-    merge_args.extend(['--output-file', self.coverage_totals_path])
-    return self.call_lcov(
-        merge_args, dry_run=self.dry_run, needs_directory=False) == 0
+    raw_data = []
+    raw_data_pattern = '*.profraw'
+    for file_name in os.listdir(self.output_directory):
+      if fnmatch.fnmatch(file_name, raw_data_pattern):
+        raw_data.append(os.path.join(self.output_directory, file_name))
 
-  def generate_report(self):
-    """Produce HTML coverage report based on combined coverage data set."""
-    config_file = os.path.join(
-        self.source_directory, 'testing', 'tools', 'coverage', 'lcovrc')
+    return self.call(
+        [llvm_profdata_bin, 'merge', '-o', self.prof_data, '-sparse=true'] +
+        raw_data) == 0
 
-    lcov_args = ['genhtml',
-      '--config-file', config_file,
-      '--legend',
-      '--demangle-cpp',
-      '--show-details',
-      '--prefix', self.source_directory,
-      '--ignore-errors',
-      'source', self.coverage_totals_path,
-      '--output-directory', self.output_directory]
-    return self.call(lcov_args, dry_run=self.dry_run) == 0
+  def generate_html_report(self):
+    """Generate HTML report by calling upstream coverage.py"""
+    coverage_bin = os.path.join(self.source_directory, 'tools', 'code_coverage',
+                                'coverage.py')
+    report_directory = os.path.join(self.output_directory, 'HTML')
+
+    coverage_args = ['-p', self.prof_data]
+    coverage_args += ['-b', self.build_directory]
+    coverage_args += ['-o', report_directory]
+    coverage_args += self.build_targets
+
+    # Whitelist the directories of interest
+    coverage_args += ['-f', 'core']
+    coverage_args += ['-f', 'fpdfsdk']
+    coverage_args += ['-f', 'fxbarcode']
+    coverage_args += ['-f', 'fxjs']
+    coverage_args += ['-f', 'public']
+    coverage_args += ['-f', 'samples']
+    coverage_args += ['-f', 'xfa']
+
+    # Blacklist test files
+    coverage_args += ['-i', '.*test.*']
+
+    # Component view is only useful for Chromium
+    coverage_args += ['--no-component-view']
+
+    return self.call([coverage_bin] + coverage_args) == 0
 
   def run(self):
     """Setup environment, execute the tests and generate coverage report"""
+    if not self.fetch_profiling_tools():
+      print 'Unable to fetch profiling tools'
+      return False
+
     if not self.build_binaries():
       print 'Failed to successfully build binaries'
       return False
@@ -316,28 +273,28 @@
         print 'Failed to successfully generate coverage data'
         return False
 
-    if not self.merge_coverage():
-      print 'Failed to successfully merge generated coverage data'
+    if not self.merge_raw_coverage_results():
+      print 'Failed to successfully merge raw coverage results'
       return False
 
-    if not self.generate_report():
-      print 'Failed to successfully generated coverage report'
+    if not self.generate_html_report():
+      print 'Failed to successfully generate HTML report'
       return False
 
     return True
 
+  def fetch_profiling_tools(self):
+    """Call coverage.py with no args to ensure profiling tools are present."""
+    return self.call_silent(
+        os.path.join(self.source_directory, 'tools', 'code_coverage',
+                     'coverage.py')) == 0
+
 
 def main():
   parser = argparse.ArgumentParser()
   parser.formatter_class = argparse.RawDescriptionHelpFormatter
-  parser.description = ('Generates a coverage report for given binaries using '
-                        'llvm-cov & lcov.\n\n'
-                        'Requires llvm-cov 3.5 or later.\n'
-                        'Requires lcov 1.11 or later.\n\n'
-                        'By default runs pdfium_unittests and '
-                        'pdfium_embeddertests. If --slow is passed in then all '
-                        'tests will be run. If any of the tests are specified '
-                        'on the command line, then only those will be run.')
+  parser.description = 'Generates a coverage report for given tests.'
+
   parser.add_argument(
       '-s',
       '--source_directory',
@@ -369,11 +326,6 @@
       help='Output additional diagnostic information',
       action='store_true')
   parser.add_argument(
-      '--slow',
-      help='Run all tests, even those known to take a long time. Ignored if '
-      'specific tests are passed in.',
-      action='store_true')
-  parser.add_argument(
       'tests',
       help='Tests to be run, defaults to all. Valid entries are %s' %
       COVERAGE_TESTS.keys(),
diff --git a/testing/tools/coverage/lcovrc b/testing/tools/coverage/lcovrc
deleted file mode 100644
index d2d39d7..0000000
--- a/testing/tools/coverage/lcovrc
+++ /dev/null
@@ -1,156 +0,0 @@
-# Specify an external style sheet file (same as --css-file option of genhtml)
-#genhtml_css_file = gcov.css
-
-# Specify coverage rate limits (in %) for classifying file entries
-# HI:   hi_limit <= rate <= 100         graph color: green
-# MED: med_limit <= rate <  hi_limit    graph color: orange
-# LO:         0  <= rate <  med_limit   graph color: red
-genhtml_hi_limit = 80
-genhtml_med_limit = 50
-
-# Width of line coverage field in source code view
-genhtml_line_field_width = 12
-
-# Width of branch coverage field in source code view
-genhtml_branch_field_width = 16
-
-# Width of overview image (used by --frames option of genhtml)
-genhtml_overview_width = 80
-
-# Resolution of overview navigation: this number specifies the maximum
-# difference in lines between the position a user selected from the overview
-# and the position the source code window is scrolled to (used by --frames
-# option of genhtml)
-genhtml_nav_resolution = 4
-
-# Clicking a line in the overview image should show the source code view at
-# a position a bit further up so that the requested line is not the first
-# line in the window. This number specifies that offset in lines (used by
-# --frames option of genhtml)
-genhtml_nav_offset = 10
-
-# Do not remove unused test descriptions if non-zero (same as
-# --keep-descriptions option of genhtml)
-genhtml_keep_descriptions = 0
-
-# Do not remove prefix from directory names if non-zero (same as --no-prefix
-# option of genhtml)
-genhtml_no_prefix = 0
-
-# Do not create source code view if non-zero (same as --no-source option of
-# genhtml)
-genhtml_no_source = 0
-
-# Replace tabs with number of spaces in source view (same as --num-spaces
-# option of genhtml)
-genhtml_num_spaces = 8
-
-# Highlight lines with converted-only data if non-zero (same as --highlight
-# option of genhtml)
-genhtml_highlight = 0
-
-# Include color legend in HTML output if non-zero (same as --legend option of
-# genhtml)
-genhtml_legend = 0
-
-# Use FILE as HTML prolog for generated pages (same as --html-prolog option of
-# genhtml)
-#genhtml_html_prolog = FILE
-
-# Use FILE as HTML epilog for generated pages (same as --html-epilog option of
-# genhtml)
-#genhtml_html_epilog = FILE
-
-# Use custom filename extension for pages (same as --html-extension option of
-# genhtml)
-#genhtml_html_extension = html
-
-# Compress all generated html files with gzip.
-#genhtml_html_gzip = 1
-
-# Include sorted overview pages (can be disabled by the --no-sort option of
-# genhtml)
-genhtml_sort = 1
-
-# Include function coverage data display (can be disabled by the
-# --no-func-coverage option of genhtml)
-genhtml_function_coverage = 1
-
-# Include branch coverage data display (can be disabled by the
-# --no-branch-coverage option of genhtml)
-genhtml_branch_coverage = 1
-
-# Specify the character set of all generated HTML pages
-genhtml_charset=UTF-8
-
-# Allow HTML markup in test case description text if non-zero
-genhtml_desc_html=0
-
-# Specify the precision for coverage rates
-#genhtml_precision=1
-
-# Location of the gcov tool (same as --gcov-info option of geninfo)
-#geninfo_gcov_tool = llvm-gcov
-
-# Adjust test names to include operating system information if non-zero
-#geninfo_adjust_testname = 0
-
-# Calculate checksum for each source code line if non-zero (same as --checksum
-# option of geninfo if non-zero, same as --no-checksum if zero)
-#geninfo_checksum = 1
-
-# Specify whether to capture coverage data for external source files (can
-# be overridden by the --external and --no-external options of geninfo/lcov)
-geninfo_external = 1
-
-# Enable libtool compatibility mode if non-zero (same as --compat-libtool option
-# of geninfo if non-zero, same as --no-compat-libtool if zero)
-#geninfo_compat_libtool = 0
-
-# Use gcov's --all-blocks option if non-zero
-#geninfo_gcov_all_blocks = 1
-
-# Specify compatiblity modes (same as --compat option of geninfo).
-#geninfo_compat = libtool=on, hammer=auto, split_crc=auto
-
-# Adjust path to source files by removing or changing path components that
-# match the specified pattern (Perl regular expression format)
-#geninfo_adjust_src_path = /tmp/build => /usr/src
-
-# Specify if geninfo should try to automatically determine the base-directory
-# when collecting coverage data.
-geninfo_auto_base = 1
-
-# Directory containing gcov kernel files
-# lcov_gcov_dir = /proc/gcov
-
-# Location of the insmod tool
-lcov_insmod_tool = /sbin/insmod
-
-# Location of the modprobe tool
-lcov_modprobe_tool = /sbin/modprobe
-
-# Location of the rmmod tool
-lcov_rmmod_tool = /sbin/rmmod
-
-# Location for temporary directories
-lcov_tmp_dir = /tmp
-
-# Show full paths during list operation if non-zero (same as --list-full-path
-# option of lcov)
-lcov_list_full_path = 0
-
-# Specify the maximum width for list output. This value is ignored when
-# lcov_list_full_path is non-zero.
-lcov_list_width = 80
-
-# Specify the maximum percentage of file names which may be truncated when
-# choosing a directory prefix in list output. This value is ignored when
-# lcov_list_full_path is non-zero.
-lcov_list_truncate_max = 20
-
-# Specify if function coverage data should be collected and processed.
-lcov_function_coverage = 1
-
-# Specify if branch coverage data should be collected and processed.
-lcov_branch_coverage = 1
diff --git a/testing/tools/coverage/llvm-gcov b/testing/tools/coverage/llvm-gcov
deleted file mode 100755
index 8141e7e..0000000
--- a/testing/tools/coverage/llvm-gcov
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-# Copyright 2017 The PDFium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Wrapper script to make llvm-cov behave like gcov, so it can be passed in as the --gcov-tool
-# parameter when using lcov. Specifically adds the keyword 'gcov' to the arguments being passed in,
-# to tell llvm-cov to operate in gcov compatibility mode.
-#
-# LLVM_COV_BIN needs to be set by caller and should the path to
-# a llvm-cov binary with a version of 3.5 or greater.
-
-set -e
-
-exec ${LLVM_COV_BIN} gcov $*
diff --git a/testing/tools/encode_pdf_filter.py b/testing/tools/encode_pdf_filter.py
new file mode 100755
index 0000000..2d56543
--- /dev/null
+++ b/testing/tools/encode_pdf_filter.py
@@ -0,0 +1,290 @@
+#!/usr/bin/env python3
+# Copyright 2019 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Encodes binary data using one or more PDF stream filters.
+
+This tool helps with the common task of converting binary data into ASCII PDF
+streams. In test PDFs (and the corresponding .in files), we often want the
+contents to be plain (or mostly plain) ASCII.
+
+Requires Python 3 (mainly for Ascii85 support). This should be fine for a
+manually-run script.
+"""
+
+import argparse
+import base64
+import collections
+import collections.abc
+import io
+import sys
+import zlib
+
+
+class _PdfStream:
+  _unique_filter_classes = []
+  _filter_classes = {}
+
+  @staticmethod
+  def GetFilterByName(name):
+    # Tolerate any case-insensitive match for "/Name" or "Name", or an alias.
+    key_name = name.lower()
+    if key_name and key_name[0] == '/':
+      key_name = key_name[:1]
+
+    filter_class = _PdfStream._filter_classes.get(key_name)
+    if not filter_class:
+      raise KeyError(name)
+
+    return filter_class
+
+  @staticmethod
+  def RegisterFilter(filter_class):
+    assert filter_class not in _PdfStream._unique_filter_classes
+    _PdfStream._unique_filter_classes.append(filter_class)
+
+    assert filter_class.name[0] == '/'
+    lower_name = filter_class.name.lower()
+    _PdfStream._filter_classes[lower_name] = filter_class
+    _PdfStream._filter_classes[lower_name[1:]] = filter_class
+
+    for alias in filter_class.aliases:
+      _PdfStream._filter_classes[alias.lower()] = filter_class
+
+  @staticmethod
+  def GetHelp():
+    text = 'Available filters:\n'
+    for filter_class in _PdfStream._unique_filter_classes:
+      text += '  {} (aliases: {})\n'.format(filter_class.name,
+                                            ', '.join(filter_class.aliases))
+    return text
+
+  def __init__(self, out_buffer, **kwargs):
+    del kwargs
+    self.buffer = out_buffer
+
+  def write(self, data):
+    self.buffer.write(data)
+
+  def flush(self):
+    self.buffer.flush()
+
+  def close(self):
+    self.buffer.close()
+
+
+class _SinkPdfStream(_PdfStream):
+
+  def __init__(self):
+    super().__init__(io.BytesIO())
+
+  def close(self):
+    # Don't call io.BytesIO.close(); this deallocates the written data.
+    self.flush()
+
+  def getbuffer(self):
+    return self.buffer.getbuffer()
+
+
+class _AsciiPdfStream(_PdfStream):
+
+  def __init__(self, out_buffer, wrapcol=0, **kwargs):
+    super().__init__(out_buffer, **kwargs)
+    self.wrapcol = wrapcol
+    self.column = 0
+
+  def write(self, data):
+    if not self.wrapcol:
+      self.buffer.write(data)
+      return
+
+    tail = self.wrapcol - self.column
+    self.buffer.write(data[:tail])
+    if tail >= len(data):
+      self.column += len(data)
+      return
+
+    for start in range(tail, len(data), self.wrapcol):
+      self.buffer.write(b'\n')
+      self.buffer.write(data[start:start + self.wrapcol])
+
+    tail = len(data) - tail
+    self.column = self.wrapcol - -tail % self.wrapcol
+
+
+class _Ascii85DecodePdfStream(_AsciiPdfStream):
+  name = '/ASCII85Decode'
+  aliases = ('ascii85', 'base85')
+
+  def __init__(self, out_buffer, **kwargs):
+    super().__init__(out_buffer, **kwargs)
+    self.trailer = b''
+
+  def write(self, data):
+    # Need to write ASCII85 in units of 4.
+    data = self.trailer + data
+    trailer_length = len(data) % 4
+    super().write(base64.a85encode(data[:-trailer_length]))
+    self.trailer = data[-trailer_length:]
+
+  def close(self):
+    super().write(base64.a85encode(self.trailer))
+    # Avoid breaking the end-of-data marker (but still try to wrap).
+    if self.wrapcol and self.column > self.wrapcol - 2:
+      self.buffer.write(b'\n')
+    self.buffer.write(b'~>')
+    self.buffer.close()
+
+
+class _AsciiHexDecodePdfStream(_AsciiPdfStream):
+  name = '/ASCIIHexDecode'
+  aliases = ('base16', 'hex', 'hexadecimal')
+
+  def __init__(self, out_buffer, **kwargs):
+    super().__init__(out_buffer, **kwargs)
+
+  def write(self, data):
+    super().write(base64.b16encode(data))
+
+
+class _FlateDecodePdfStream(_PdfStream):
+  name = '/FlateDecode'
+  aliases = ('deflate', 'flate', 'zlib')
+
+  def __init__(self, out_buffer, **kwargs):
+    super().__init__(out_buffer, **kwargs)
+    self.deflate = zlib.compressobj(level=9, memLevel=9)
+
+  def write(self, data):
+    self.buffer.write(self.deflate.compress(data))
+
+  def flush(self):
+    self.buffer.write(self.deflate.flush(zlib.Z_NO_FLUSH))
+
+  def close(self):
+    self.buffer.write(self.deflate.flush())
+    self.buffer.close()
+
+
+_PdfStream.RegisterFilter(_Ascii85DecodePdfStream)
+_PdfStream.RegisterFilter(_AsciiHexDecodePdfStream)
+_PdfStream.RegisterFilter(_FlateDecodePdfStream)
+
+_DEFAULT_FILTERS = (_Ascii85DecodePdfStream, _FlateDecodePdfStream)
+
+
+def _ParseCommandLine(argv):
+  arg_parser = argparse.ArgumentParser(
+      description='Encodes binary data using one or more PDF stream filters.',
+      epilog=_PdfStream.GetHelp(),
+      formatter_class=argparse.RawDescriptionHelpFormatter)
+  arg_parser.add_argument(
+      '-r',
+      '--raw',
+      action='store_true',
+      help='output raw bytes (no PDF stream header or trailer)')
+  arg_parser.add_argument(
+      '-l',
+      '--length',
+      action='store_true',
+      help='output actual /Length, instead of {{streamlen}}')
+  arg_parser.add_argument(
+      '-w',
+      '--wrap',
+      default=80,
+      type=int,
+      help='wrap ASCII lines at COLUMN; defaults to 80 (0 = off)',
+      metavar='COLUMN')
+  arg_parser.add_argument(
+      '-f',
+      '--filter',
+      action='append',
+      type=_PdfStream.GetFilterByName,
+      help=('one or more filters, in decoding order; defaults to ' + ' '.join(
+          [f.name for f in _DEFAULT_FILTERS])),
+      metavar='NAME')
+  arg_parser.add_argument(
+      'infile',
+      nargs='?',
+      default=sys.stdin,
+      type=argparse.FileType('r'),
+      help='input file; use - for standard input (default)')
+  arg_parser.add_argument(
+      'outfile',
+      nargs='?',
+      default=sys.stdout,
+      type=argparse.FileType('w'),
+      help='output file; use - for standard output (default)')
+  args = arg_parser.parse_intermixed_args(argv)
+  args.filter = args.filter or _DEFAULT_FILTERS
+  assert args.wrap >= 0, '--wrap COLUMN must be non-negative'
+  return args
+
+
+def _WrapWithFilters(out_buffer, filter_classes, **kwargs):
+  for filter_class in filter_classes:
+    out_buffer = filter_class(out_buffer, **kwargs)
+  return out_buffer
+
+
+def _CopyBytes(in_buffer, out_buffer):
+  data = bytearray(io.DEFAULT_BUFFER_SIZE)
+  while True:
+    data_length = in_buffer.readinto(data)
+    if not data_length:
+      return
+    out_buffer.write(data[:data_length])
+
+
+def _WritePdfStreamObject(out_buffer,
+                          data,
+                          entries,
+                          raw=False,
+                          use_streamlen=False):
+  if not raw:
+    out_buffer.write(b'<<\n')
+    entries['Length'] = len(data)
+    for k, v in entries.items():
+      v = _EncodePdfValue(v)
+      if k == 'Length' and use_streamlen:
+        out_buffer.write(b'  {{streamlen}}\n')
+      else:
+        out_buffer.write('  /{} {}\n'.format(k, v).encode('ascii'))
+    out_buffer.write(b'>>\nstream\n')
+
+  out_buffer.write(data)
+
+  if not raw:
+    if data and data[-1] != '\n':
+      out_buffer.write(b'\n')
+    out_buffer.write(b'endstream\n')
+
+
+def _EncodePdfValue(value):
+  if isinstance(value, collections.abc.Sequence):
+    value = '[' + ' '.join(value) + ']'
+  return value
+
+
+def main(argv):
+  args = _ParseCommandLine(argv)
+
+  encoded_sink = _SinkPdfStream()
+  with args.infile:
+    out_buffer = _WrapWithFilters(encoded_sink, args.filter, wrapcol=args.wrap)
+    _CopyBytes(args.infile.buffer, out_buffer)
+    out_buffer.close()
+
+  entries = collections.OrderedDict()
+  entries['Filter'] = [f.name for f in args.filter]
+  _WritePdfStreamObject(
+      args.outfile.buffer,
+      data=encoded_sink.getbuffer(),
+      entries=entries,
+      raw=args.raw,
+      use_streamlen=not args.length)
+  return args.outfile.close()
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/testing/tools/fixup_pdf_template.py b/testing/tools/fixup_pdf_template.py
index 10ca241..ee47c4b 100755
--- a/testing/tools/fixup_pdf_template.py
+++ b/testing/tools/fixup_pdf_template.py
@@ -2,24 +2,33 @@
 # Copyright 2014 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Expands a hand-written PDF testcase (template) into a valid PDF file.
 
 There are several places in a PDF file where byte-offsets are required. This
 script replaces {{name}}-style variables in the input with calculated results
 
-  {{header}}     - expands to the header comment required for PDF files.
-  {{xref}}       - expands to a generated xref table, noting the offset.
-  {{trailer}}    - expands to a standard trailer with "1 0 R" as the /Root.
-  {{startxref}   - expands to a startxref directive followed by correct offset.
+  {{include path/to/file}} - inserts file's contents into stream.
+  {{header}} - expands to the header comment required for PDF files.
+  {{xref}} - expands to a generated xref table, noting the offset.
+  {{trailer}} - expands to a standard trailer with "1 0 R" as the /Root.
+  {{startxref} - expands to a startxref directive followed by correct offset.
   {{object x y}} - expands to |x y obj| declaration, noting the offset.
+  {{streamlen}} - expands to |/Length n|.
 """
 
+import cStringIO
 import optparse
 import os
 import re
 import sys
 
+
+class StreamLenState:
+  START = 1
+  FIND_STREAM = 2
+  FIND_ENDSTREAM = 3
+
+
 class TemplateProcessor:
   HEADER_TOKEN = '{{header}}'
   HEADER_REPLACEMENT = '%PDF-1.7\n%\xa0\xf2\xa4\xf4'
@@ -30,22 +39,27 @@
   XREF_REPLACEMENT_N = '%010d %05d n \n'
   XREF_REPLACEMENT_F = '0000000000 65535 f \n'
   # XREF rows must be exactly 20 bytes - space required.
-  assert(len(XREF_REPLACEMENT_F) == 20)
+  assert len(XREF_REPLACEMENT_F) == 20
 
   TRAILER_TOKEN = '{{trailer}}'
-  TRAILER_REPLACEMENT = 'trailer<< /Root 1 0 R /Size %d >>'
+  TRAILER_REPLACEMENT = 'trailer <<\n  /Root 1 0 R\n  /Size %d\n>>'
 
-  STARTXREF_TOKEN= '{{startxref}}'
+  STARTXREF_TOKEN = '{{startxref}}'
   STARTXREF_REPLACEMENT = 'startxref\n%d'
 
   OBJECT_PATTERN = r'\{\{object\s+(\d+)\s+(\d+)\}\}'
   OBJECT_REPLACEMENT = r'\1 \2 obj'
 
+  STREAMLEN_TOKEN = '{{streamlen}}'
+  STREAMLEN_REPLACEMENT = '/Length %d'
+
   def __init__(self):
+    self.streamlen_state = StreamLenState.START
+    self.streamlens = []
     self.offset = 0
     self.xref_offset = 0
     self.max_object_number = 0
-    self.objects = { }
+    self.objects = {}
 
   def insert_xref_entry(self, object_number, generation_number):
     self.objects[object_number] = (self.offset, generation_number)
@@ -60,9 +74,30 @@
         result += self.XREF_REPLACEMENT_F
     return result
 
+  def preprocess_line(self, line):
+    if self.STREAMLEN_TOKEN in line:
+      assert self.streamlen_state == StreamLenState.START
+      self.streamlen_state = StreamLenState.FIND_STREAM
+      self.streamlens.append(0)
+      return
+
+    if (self.streamlen_state == StreamLenState.FIND_STREAM and
+        line.rstrip() == 'stream'):
+      self.streamlen_state = StreamLenState.FIND_ENDSTREAM
+      return
+
+    if self.streamlen_state == StreamLenState.FIND_ENDSTREAM:
+      if line.rstrip() == 'endstream':
+        self.streamlen_state = StreamLenState.START
+      else:
+        self.streamlens[-1] += len(line)
+
   def process_line(self, line):
     if self.HEADER_TOKEN in line:
       line = line.replace(self.HEADER_TOKEN, self.HEADER_REPLACEMENT)
+    if self.STREAMLEN_TOKEN in line:
+      sub = self.STREAMLEN_REPLACEMENT % self.streamlens.pop(0)
+      line = re.sub(self.STREAMLEN_TOKEN, sub, line)
     if self.XREF_TOKEN in line:
       self.xref_offset = self.offset
       line = self.generate_xref_table()
@@ -80,17 +115,43 @@
     return line
 
 
-def expand_file(input_path, output_path):
+def expand_file(infile, output_path):
   processor = TemplateProcessor()
   try:
-    with open(input_path, 'rb') as infile:
-      with open(output_path, 'wb') as outfile:
-        for line in infile:
-           outfile.write(processor.process_line(line))
+    with open(output_path, 'wb') as outfile:
+      preprocessed = cStringIO.StringIO()
+      for line in infile:
+        preprocessed.write(line)
+        processor.preprocess_line(line)
+      preprocessed.seek(0)
+      for line in preprocessed:
+        outfile.write(processor.process_line(line))
   except IOError:
     print >> sys.stderr, 'failed to process %s' % input_path
 
 
+def insert_includes(input_path, output_file, visited_set):
+  input_path = os.path.normpath(input_path)
+  if input_path in visited_set:
+    print >> sys.stderr, 'Circular inclusion %s, ignoring' % input_path
+    return
+  visited_set.add(input_path)
+  try:
+    with open(input_path, 'rb') as infile:
+      for line in infile:
+        match = re.match(r'\s*\{\{include\s+(.+)\}\}', line)
+        if match:
+          insert_includes(
+              os.path.join(os.path.dirname(input_path), match.group(1)),
+              output_file, visited_set)
+        else:
+          output_file.write(line)
+  except IOError:
+    print >> sys.stderr, 'failed to include %s' % input_path
+    raise
+  visited_set.discard(input_path)
+
+
 def main():
   parser = optparse.OptionParser()
   parser.add_option('--output-dir', default='')
@@ -101,8 +162,11 @@
     output_dir = os.path.dirname(testcase_path)
     if options.output_dir:
       output_dir = options.output_dir
+    intermediate_stream = cStringIO.StringIO()
+    insert_includes(testcase_path, intermediate_stream, set())
+    intermediate_stream.seek(0)
     output_path = os.path.join(output_dir, testcase_root + '.pdf')
-    expand_file(testcase_path, output_path)
+    expand_file(intermediate_stream, output_path)
   return 0
 
 
diff --git a/testing/tools/githelper.py b/testing/tools/githelper.py
index 2e94196..91a6c71 100644
--- a/testing/tools/githelper.py
+++ b/testing/tools/githelper.py
@@ -1,11 +1,11 @@
 # Copyright 2017 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Classes for dealing with git."""
 
 import subprocess
 
+# pylint: disable=relative-import
 from common import RunCommandPropagateErr
 
 
@@ -42,13 +42,12 @@
 
   def GetCurrentBranchName(self):
     """Returns a string with the current branch name."""
-    return RunCommandPropagateErr(
-        ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
-        exit_status_on_error=1).strip()
+    return RunCommandPropagateErr(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
+                                  exit_status_on_error=1).strip()
 
   def GetCurrentBranchHash(self):
-    return RunCommandPropagateErr(
-        ['git', 'rev-parse', 'HEAD'], exit_status_on_error=1).strip()
+    return RunCommandPropagateErr(['git', 'rev-parse', 'HEAD'],
+                                  exit_status_on_error=1).strip()
 
   def IsCurrentBranchClean(self):
     output = RunCommandPropagateErr(['git', 'status', '--porcelain'],
@@ -57,8 +56,8 @@
 
   def BranchExists(self, branch_name):
     """Return whether a branch with the given name exists."""
-    output = RunCommandPropagateErr(['git', 'rev-parse', '--verify',
-                                     branch_name])
+    output = RunCommandPropagateErr(
+        ['git', 'rev-parse', '--verify', branch_name])
     return output is not None
 
   def CloneLocal(self, source_repo, new_repo):
diff --git a/testing/tools/gold.py b/testing/tools/gold.py
index a822237..c686bba 100644
--- a/testing/tools/gold.py
+++ b/testing/tools/gold.py
@@ -2,11 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-
 import json
 import os
 import shlex
 import shutil
+import ssl
 import urllib2
 
 
@@ -17,7 +17,7 @@
   kv_pairs = shlex.split(kv_str)
   if len(kv_pairs) % 2:
     raise ValueError('Uneven number of key/value pairs. Got %s' % kv_str)
-  return { kv_pairs[i]:kv_pairs[i + 1] for i in xrange(0, len(kv_pairs), 2) }
+  return {kv_pairs[i]: kv_pairs[i + 1] for i in xrange(0, len(kv_pairs), 2)}
 
 
 # This module downloads a json provided by Skia Gold with the expected baselines
@@ -65,39 +65,39 @@
     Download the baseline json and return a list of the two baselines that
     should be used to match hashes (master and cl#).
     """
-    GOLD_BASELINE_URL = ('https://storage.googleapis.com/skia-infra-gm/'
-                         'hash_files/gold-pdfium-baseline.json')
-    try:
-      response = urllib2.urlopen(GOLD_BASELINE_URL)
-      json_data = response.read()
-    except (urllib2.HTTPError, urllib2.URLError) as e:
-      print ('Error: Unable to read skia gold json from %s: %s'
-             % (GOLD_BASELINE_URL, e))
-      return None
+    GOLD_BASELINE_URL = 'https://pdfium-gold.skia.org/json/baseline'
+
+    # If we have an issue number add it to the baseline URL
+    cl_number_str = self._properties.get('issue', None)
+    url = GOLD_BASELINE_URL + ('/' + cl_number_str if cl_number_str else '')
+
+    json_data = ''
+    MAX_TIMEOUT = 33  # 5 tries. (2, 4, 8, 16, 32)
+    timeout = 2
+    while True:
+      try:
+        response = urllib2.urlopen(url, timeout=timeout)
+        c_type = response.headers.get('Content-type', '')
+        EXPECTED_CONTENT_TYPE = 'application/json'
+        if c_type != EXPECTED_CONTENT_TYPE:
+          raise ValueError('Invalid content type. Got %s instead of %s' %
+                           (c_type, EXPECTED_CONTENT_TYPE))
+        json_data = response.read()
+        break  # If this line is reached, then no exception occurred.
+      except (ssl.SSLError, urllib2.HTTPError, urllib2.URLError) as e:
+        timeout *= 2
+        if timeout < MAX_TIMEOUT:
+          continue
+        print('Error: Unable to read skia gold json from %s: %s' % (url, e))
+        return None
 
     try:
       data = json.loads(json_data)
-    except ValueError:
-      print 'Error: Malformed json read from %s: %s' % (GOLD_BASELINE_URL, e)
+    except ValueError as e:
+      print 'Error: Malformed json read from %s: %s' % (url, e)
       return None
 
-    try:
-      master_baseline = data['master']
-    except (KeyError, TypeError):
-      print ('Error: "master" key not in json read from %s: %s'
-             % (GOLD_BASELINE_URL, e))
-      return None
-
-    cl_number_str = self._properties.get('issue')
-    if cl_number_str is None:
-      return [master_baseline]
-
-    try:
-      cl_baseline = data['changeLists'][cl_number_str]
-    except KeyError:
-      return [master_baseline]
-
-    return [cl_baseline, master_baseline]
+    return data.get('master', {})
 
   # Return values for MatchLocalResult().
   MATCH = 'match'
@@ -124,14 +124,13 @@
       return GoldBaseline.BASELINE_DOWNLOAD_FAILED
 
     found_test_case = False
-    for baseline in self._baselines:
-      if test_name in baseline:
-        found_test_case = True
-        if md5_hash in baseline[test_name]:
-          return GoldBaseline.MATCH
+    if test_name in self._baselines:
+      found_test_case = True
+      if md5_hash in self._baselines[test_name]:
+        return GoldBaseline.MATCH
 
-    return (GoldBaseline.MISMATCH if found_test_case
-            else GoldBaseline.NO_BASELINE)
+    return (GoldBaseline.MISMATCH
+            if found_test_case else GoldBaseline.NO_BASELINE)
 
 
 # This module collects and writes output in a format expected by the
@@ -177,92 +176,109 @@
 # }
 #
 class GoldResults(object):
-  def __init__(self, source_type, outputDir, propertiesStr, keyStr,
+
+  def __init__(self, source_type, output_dir, properties_str, key_str,
                ignore_hashes_file):
     """
     source_type is the source_type (=corpus) field used for all results.
     output_dir is the directory where the resulting images are copied and
                the dm.json file is written. If the directory exists it will
                be removed and recreated.
-    propertiesStr is a string with space separated key/value pairs that
+    properties_str is a string with space separated key/value pairs that
                is used to set the top level fields in the output JSON file.
-    keyStr is a string with space separated key/value pairs that
+    key_str is a string with space separated key/value pairs that
                is used to set the 'key' field in the output JSON file.
     ignore_hashes_file is a file that contains a list of image hashes
                that should be ignored.
     """
     self._source_type = source_type
-    self._properties = _ParseKeyValuePairs(propertiesStr)
-    self._properties["key"] = _ParseKeyValuePairs(keyStr)
-    self._results =  []
-    self._outputDir = outputDir
+    self._properties = _ParseKeyValuePairs(properties_str)
+    self._properties['key'] = _ParseKeyValuePairs(key_str)
+    self._results = []
+    self._passfail = []
+    self._output_dir = output_dir
 
     # make sure the output directory exists and is empty.
-    if os.path.exists(outputDir):
-      shutil.rmtree(outputDir, ignore_errors=True)
-    os.makedirs(outputDir)
+    if os.path.exists(output_dir):
+      shutil.rmtree(output_dir, ignore_errors=True)
+    os.makedirs(output_dir)
 
     self._ignore_hashes = set()
     if ignore_hashes_file:
       with open(ignore_hashes_file, 'r') as ig_file:
-        hashes=[x.strip() for x in ig_file.readlines() if x.strip()]
+        hashes = [x.strip() for x in ig_file.readlines() if x.strip()]
         self._ignore_hashes = set(hashes)
 
-  def AddTestResult(self, testName, md5Hash, outputImagePath):
+  def AddTestResult(self, testName, md5Hash, outputImagePath, matchResult):
     # If the hash is in the list of hashes to ignore then we don'try
     # make a copy, but add it to the result.
-    imgExt = os.path.splitext(outputImagePath)[1].lstrip(".")
+    imgExt = os.path.splitext(outputImagePath)[1].lstrip('.')
     if md5Hash not in self._ignore_hashes:
       # Copy the image to <output_dir>/<md5Hash>.<image_extension>
       if not imgExt:
-        raise ValueError("File %s does not have an extension" % outputImagePath)
-      newFilePath = os.path.join(self._outputDir, md5Hash + '.' + imgExt)
+        raise ValueError('File %s does not have an extension' % outputImagePath)
+      newFilePath = os.path.join(self._output_dir, md5Hash + '.' + imgExt)
       shutil.copy2(outputImagePath, newFilePath)
 
     # Add an entry to the list of test results
     self._results.append({
-      "key": {
-        "name": testName,
-        "source_type": self._source_type,
-      },
-      "md5": md5Hash,
-      "options": {
-        "ext": imgExt,
-        "gamma_correct": "no"
-      }
+        'key': {
+            'name': testName,
+            'source_type': self._source_type,
+        },
+        'md5': md5Hash,
+        'options': {
+            'ext': imgExt,
+            'gamma_correct': 'no'
+        }
     })
 
+    self._passfail.append((testName, matchResult))
+
   def WriteResults(self):
-    self._properties.update({
-      "results": self._results
-    })
+    self._properties.update({'results': self._results})
 
-    outputFileName = os.path.join(self._outputDir, "dm.json")
-    with open(outputFileName, 'wb') as outfile:
+    output_file_name = os.path.join(self._output_dir, 'dm.json')
+    with open(output_file_name, 'wb') as outfile:
       json.dump(self._properties, outfile, indent=1)
-      outfile.write("\n")
+      outfile.write('\n')
+
+    output_file_name = os.path.join(self._output_dir, 'passfail.json')
+    with open(output_file_name, 'wb') as outfile:
+      json.dump(self._passfail, outfile, indent=1)
+      outfile.write('\n')
+
 
 # Produce example output for manual testing.
-if __name__ == "__main__":
+def _Example():
   # Create a test directory with three empty 'image' files.
-  testDir = "./testdirectory"
-  if not os.path.exists(testDir):
-    os.makedirs(testDir)
-  open(os.path.join(testDir, "image1.png"), 'wb').close()
-  open(os.path.join(testDir, "image2.png"), 'wb').close()
-  open(os.path.join(testDir, "image3.png"), 'wb').close()
+  test_dir = './testdirectory'
+  if not os.path.exists(test_dir):
+    os.makedirs(test_dir)
+  open(os.path.join(test_dir, 'image1.png'), 'wb').close()
+  open(os.path.join(test_dir, 'image2.png'), 'wb').close()
+  open(os.path.join(test_dir, 'image3.png'), 'wb').close()
 
   # Create an instance and add results.
-  propStr = """build_number 2 "builder name" Builder-Name gitHash a4a338179013b029d6dd55e737b5bd648a9fb68c"""
+  prop_str = 'build_number 2 "builder name" Builder-Name gitHash ' \
+      'a4a338179013b029d6dd55e737b5bd648a9fb68c'
 
-  keyStr = "arch arm64 compiler Clang configuration Debug"
+  key_str = 'arch arm64 compiler Clang configuration Debug'
 
-  hash_file = os.path.join(testDir, "ignore_hashes.txt")
+  hash_file = os.path.join(test_dir, 'ignore_hashes.txt')
   with open(hash_file, 'wb') as f:
-    f.write("\n".join(["hash-1","hash-4"]) + "\n")
+    f.write('\n'.join(['hash-1', 'hash-4']) + '\n')
 
-  gr = GoldResults("pdfium", testDir, propStr, keyStr, hash_file)
-  gr.AddTestResult("test-1", "hash-1", os.path.join(testDir, "image1.png"))
-  gr.AddTestResult("test-2", "hash-2", os.path.join(testDir, "image2.png"))
-  gr.AddTestResult("test-3", "hash-3", os.path.join(testDir, "image3.png"))
+  output_dir = './output_directory'
+  gr = GoldResults('pdfium', output_dir, prop_str, key_str, hash_file)
+  gr.AddTestResult('test-1', 'hash-1', os.path.join(test_dir, 'image1.png'),
+                   GoldBaseline.MATCH)
+  gr.AddTestResult('test-2', 'hash-2', os.path.join(test_dir, 'image2.png'),
+                   GoldBaseline.MATCH)
+  gr.AddTestResult('test-3', 'hash-3', os.path.join(test_dir, 'image3.png'),
+                   GoldBaseline.MISMATCH)
   gr.WriteResults()
+
+
+if __name__ == '__main__':
+  _Example()
diff --git a/testing/tools/make_expected.sh b/testing/tools/make_expected.sh
index 3d4252f..5237e51 100755
--- a/testing/tools/make_expected.sh
+++ b/testing/tools/make_expected.sh
@@ -5,16 +5,32 @@
 # found in the LICENSE file.
 #
 # Script to generate expected result files.
-#
+
+# Arbitrary timestamp, expressed in seconds since the epoch, used to make sure
+# that tests that depend on the current time are stable. Happens to be the
+# timestamp of the first commit to repo, 2014/5/9 17:48:50.
+TEST_SEED_TIME=1399672130
+
+# Do this before "set -e" so "which" failing is not fatal.
+PNGOPTIMIZER="$(which optipng)"
+
 set -e
 while (( "$#" )); do
   INFILE="$1"
   echo $INFILE | grep -qs ' ' && echo space in filename detected && exit 1
-  out/Debug/pdfium_test --png $INFILE
+  EVTFILE="${INFILE%.*}.evt"
+  SEND_EVENTS=
+  if [ -f "$EVTFILE" ]; then
+    SEND_EVENTS="--send-events"
+  fi
+  out/Debug/pdfium_test $SEND_EVENTS --time=$TEST_SEED_TIME --png $INFILE
   RESULTS="$INFILE.*.png"
   for RESULT in $RESULTS ; do
-      EXPECTED=`echo -n $RESULT | sed 's/[.]pdf[.]/_expected.pdf./'`
-      mv $RESULT $EXPECTED
+    EXPECTED=`echo -n $RESULT | sed 's/[.]pdf[.]/_expected.pdf./'`
+    mv $RESULT $EXPECTED
+    if [ -n "$PNGOPTIMIZER" ]; then
+      "$PNGOPTIMIZER" $EXPECTED
+    fi
   done
   shift
 done
diff --git a/testing/tools/pngdiffer.py b/testing/tools/pngdiffer.py
index a9bc9d6..a469506 100755
--- a/testing/tools/pngdiffer.py
+++ b/testing/tools/pngdiffer.py
@@ -3,74 +3,93 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import distutils.spawn
+import itertools
 import os
 import shutil
 import sys
 
+# pylint: disable=relative-import
 import common
 
+
 class PNGDiffer():
-  def __init__(self, finder):
+
+  def __init__(self, finder, reverse_byte_order):
     self.pdfium_diff_path = finder.ExecutablePath('pdfium_diff')
     self.os_name = finder.os_name
+    self.reverse_byte_order = reverse_byte_order
+
+  def CheckMissingTools(self, regenerate_expected):
+    if (regenerate_expected and self.os_name == 'linux' and
+        not distutils.spawn.find_executable('optipng')):
+      return 'Please install "optipng" to regenerate expected images.'
+    return None
 
   def GetActualFiles(self, input_filename, source_dir, working_dir):
     actual_paths = []
     path_templates = PathTemplates(input_filename, source_dir, working_dir)
 
-    i = 0
-    while True:
-      actual_path = path_templates.GetActualPath(i)
-      expected_path = path_templates.GetExpectedPath(i)
+    for page in itertools.count():
+      actual_path = path_templates.GetActualPath(page)
+      expected_path = path_templates.GetExpectedPath(page)
       platform_expected_path = path_templates.GetPlatformExpectedPath(
-          self.os_name, i)
+          self.os_name, page)
       if os.path.exists(platform_expected_path):
         expected_path = platform_expected_path
       elif not os.path.exists(expected_path):
         break
       actual_paths.append(actual_path)
-      i += 1
+
     return actual_paths
 
   def HasDifferences(self, input_filename, source_dir, working_dir):
     path_templates = PathTemplates(input_filename, source_dir, working_dir)
 
-    i = 0
-    while True:
-      actual_path = path_templates.GetActualPath(i)
-      expected_path = path_templates.GetExpectedPath(i)
+    for page in itertools.count():
+      actual_path = path_templates.GetActualPath(page)
+      expected_path = path_templates.GetExpectedPath(page)
       # PDFium tests should be platform independent. Platform based results are
       # used to capture platform dependent implementations.
       platform_expected_path = path_templates.GetPlatformExpectedPath(
-          self.os_name, i)
+          self.os_name, page)
       if (not os.path.exists(expected_path) and
           not os.path.exists(platform_expected_path)):
-        if i == 0:
+        if page == 0:
           print "WARNING: no expected results files for " + input_filename
+        if os.path.exists(actual_path):
+          print('FAILURE: Missing expected result for 0-based page %d of %s' %
+                (page, input_filename))
+          return True
         break
       print "Checking " + actual_path
       sys.stdout.flush()
       if os.path.exists(expected_path):
-        error = common.RunCommand(
-            [self.pdfium_diff_path, expected_path, actual_path])
+        cmd = [self.pdfium_diff_path]
+        if self.reverse_byte_order:
+          cmd.append('--reverse-byte-order')
+        cmd.extend([expected_path, actual_path])
+        error = common.RunCommand(cmd)
       else:
-        error = 1;
+        error = 1
       if error:
         # When failed, we check against platform based results.
         if os.path.exists(platform_expected_path):
-          error = common.RunCommand(
-              [self.pdfium_diff_path, platform_expected_path, actual_path])
+          cmd = [self.pdfium_diff_path]
+          if self.reverse_byte_order:
+            cmd.append('--reverse-byte-order')
+          cmd.extend([platform_expected_path, actual_path])
+          error = common.RunCommand(cmd)
         if error:
           print "FAILURE: " + input_filename + "; " + str(error)
           return True
-      i += 1
+
     return False
 
   def Regenerate(self, input_filename, source_dir, working_dir, platform_only):
     path_templates = PathTemplates(input_filename, source_dir, working_dir)
 
-    page = 0
-    while True:
+    for page in itertools.count():
       # Loop through the generated page images. Stop when there is a page
       # missing a png, which means the document ended.
       actual_path = path_templates.GetActualPath(page)
@@ -88,12 +107,10 @@
       elif not platform_only:
         expected_path = path_templates.GetExpectedPath(page)
       else:
-        expected_path = None
+        continue
 
-      if expected_path is not None and os.path.exists(expected_path):
-        shutil.copyfile(actual_path, expected_path)
-
-      page += 1
+      shutil.copyfile(actual_path, expected_path)
+      common.RunCommand(['optipng', expected_path])
 
 
 ACTUAL_TEMPLATE = '.pdf.%d.png'
@@ -107,8 +124,8 @@
     input_root, _ = os.path.splitext(input_filename)
     self.actual_path_template = os.path.join(working_dir,
                                              input_root + ACTUAL_TEMPLATE)
-    self.expected_path = os.path.join(
-        source_dir, input_root + EXPECTED_TEMPLATE)
+    self.expected_path = os.path.join(source_dir,
+                                      input_root + EXPECTED_TEMPLATE)
     self.platform_expected_path = os.path.join(
         source_dir, input_root + PLATFORM_EXPECTED_TEMPLATE)
 
diff --git a/testing/tools/run_corpus_tests.py b/testing/tools/run_corpus_tests.py
index 1175de4..c1bec3a 100755
--- a/testing/tools/run_corpus_tests.py
+++ b/testing/tools/run_corpus_tests.py
@@ -5,14 +5,16 @@
 
 import sys
 
+# pylint: disable=relative-import
 import test_runner
 
+
 def main():
   runner = test_runner.TestRunner('corpus')
   runner.SetEnforceExpectedImages(True)
   runner.SetOneShotRenderer(True)
   return runner.Run()
 
+
 if __name__ == '__main__':
   sys.exit(main())
-
diff --git a/testing/tools/run_javascript_tests.py b/testing/tools/run_javascript_tests.py
index 76d2379..948c1af 100755
--- a/testing/tools/run_javascript_tests.py
+++ b/testing/tools/run_javascript_tests.py
@@ -5,11 +5,14 @@
 
 import sys
 
+# pylint: disable=relative-import
 import test_runner
 
+
 def main():
   runner = test_runner.TestRunner('javascript')
   return runner.Run()
 
+
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/testing/tools/run_pixel_tests.py b/testing/tools/run_pixel_tests.py
index 1d61966..0992e0a 100755
--- a/testing/tools/run_pixel_tests.py
+++ b/testing/tools/run_pixel_tests.py
@@ -5,12 +5,15 @@
 
 import sys
 
+# pylint: disable=relative-import
 import test_runner
 
+
 def main():
   runner = test_runner.TestRunner('pixel')
   runner.SetEnforceExpectedImages(True)
   return runner.Run()
 
+
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/testing/tools/safetynet_compare.py b/testing/tools/safetynet_compare.py
index 0cf1aec..c76ce44 100755
--- a/testing/tools/safetynet_compare.py
+++ b/testing/tools/safetynet_compare.py
@@ -2,11 +2,11 @@
 # Copyright 2017 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Compares the performance of two versions of the pdfium code."""
 
 import argparse
 import functools
+import glob
 import json
 import multiprocessing
 import os
@@ -16,6 +16,7 @@
 import sys
 import tempfile
 
+# pylint: disable=relative-import
 from common import GetBooleanGnArg
 from common import PrintErr
 from common import RunCommandPropagateErr
@@ -24,6 +25,7 @@
 from safetynet_conclusions import PrintConclusionsDictHumanReadable
 from safetynet_conclusions import RATING_IMPROVEMENT
 from safetynet_conclusions import RATING_REGRESSION
+from safetynet_image import ImageComparison
 
 
 def RunSingleTestCaseParallel(this, run_label, build_dir, test_case):
@@ -46,8 +48,7 @@
       self.safe_script_dir = os.path.join('testing', 'tools')
 
     self.safe_measure_script_path = os.path.abspath(
-        os.path.join(self.safe_script_dir,
-                     'safetynet_measure.py'))
+        os.path.join(self.safe_script_dir, 'safetynet_measure.py'))
 
     input_file_re = re.compile('^.+[.]pdf$')
     self.test_cases = []
@@ -80,12 +81,10 @@
     if self.args.branch_after:
       if self.args.this_repo:
         before, after = self._ProfileTwoOtherBranchesInThisRepo(
-            self.args.branch_before,
-            self.args.branch_after)
+            self.args.branch_before, self.args.branch_after)
       else:
-        before, after = self._ProfileTwoOtherBranches(
-            self.args.branch_before,
-            self.args.branch_after)
+        before, after = self._ProfileTwoOtherBranches(self.args.branch_before,
+                                                      self.args.branch_after)
     elif self.args.branch_before:
       if self.args.this_repo:
         before, after = self._ProfileCurrentAndOtherBranchInThisRepo(
@@ -107,6 +106,12 @@
 
     self._CleanUp(conclusions)
 
+    if self.args.png_dir:
+      image_comparison = ImageComparison(
+          self.after_build_dir, self.args.png_dir, ('before', 'after'),
+          self.args.num_workers, self.args.png_threshold)
+      image_comparison.Run(open_in_browser=not self.args.machine_readable)
+
     return 0
 
   def _FreezeMeasureScript(self):
@@ -118,8 +123,8 @@
     self.__FreezeFile(os.path.join('testing', 'tools', 'safetynet_measure.py'))
     self.__FreezeFile(os.path.join('testing', 'tools', 'common.py'))
 
-  def __FreezeFile(self, file):
-    RunCommandPropagateErr(['cp', file, self.safe_script_dir],
+  def __FreezeFile(self, filename):
+    RunCommandPropagateErr(['cp', filename, self.safe_script_dir],
                            exit_status_on_error=1)
 
   def _ProfileTwoOtherBranchesInThisRepo(self, before_branch, after_branch):
@@ -175,11 +180,9 @@
       mapping a test case name to the profiling values for that test case
       in the given branch.
     """
-    after = self._ProfileSeparateRepo('after',
-                                      self.after_build_dir,
+    after = self._ProfileSeparateRepo('after', self.after_build_dir,
                                       after_branch)
-    before = self._ProfileSeparateRepo('before',
-                                       self.before_build_dir,
+    before = self._ProfileSeparateRepo('before', self.before_build_dir,
                                        before_branch)
     return before, after
 
@@ -237,8 +240,7 @@
     self._BuildCurrentBranch(self.after_build_dir)
     after = self._MeasureCurrentBranch('after', self.after_build_dir)
 
-    before = self._ProfileSeparateRepo('before',
-                                       self.before_build_dir,
+    before = self._ProfileSeparateRepo('before', self.before_build_dir,
                                        other_branch)
 
     return before, after
@@ -301,8 +303,7 @@
       A dict mapping each test case name to the profiling values for that
       test case.
     """
-    build_dir = self._CreateTempRepo('repo_%s' % run_label,
-                                     relative_build_dir,
+    build_dir = self._CreateTempRepo('repo_%s' % run_label, relative_build_dir,
                                      branch)
 
     self._BuildCurrentBranch(build_dir)
@@ -334,13 +335,16 @@
     os.chdir(repo_dir)
     PrintErr('Syncing...')
 
-    cmd = ['gclient', 'config', '--unmanaged',
-           'https://pdfium.googlesource.com/pdfium.git']
+    cmd = [
+        'gclient', 'config', '--unmanaged',
+        'https://pdfium.googlesource.com/pdfium.git'
+    ]
     if self.args.cache_dir:
       cmd.append('--cache-dir=%s' % self.args.cache_dir)
     RunCommandPropagateErr(cmd, exit_status_on_error=1)
 
-    RunCommandPropagateErr(['gclient', 'sync'], exit_status_on_error=1)
+    RunCommandPropagateErr(['gclient', 'sync', '--force'],
+                           exit_status_on_error=1)
 
     PrintErr('Done.')
 
@@ -359,7 +363,6 @@
 
     return build_dir
 
-
   def _CheckoutBranch(self, branch):
     PrintErr("Checking out branch '%s'" % branch)
     self.git.Checkout(branch)
@@ -379,7 +382,8 @@
       build_dir: String with path to build directory
     """
     PrintErr('Syncing...')
-    RunCommandPropagateErr(['gclient', 'sync'], exit_status_on_error=1)
+    RunCommandPropagateErr(['gclient', 'sync', '--force'],
+                           exit_status_on_error=1)
     PrintErr('Done.')
 
     PrintErr('Building...')
@@ -436,15 +440,15 @@
     """
     results = {}
     pool = multiprocessing.Pool(self.args.num_workers)
-    worker_func = functools.partial(
-        RunSingleTestCaseParallel, self, run_label, build_dir)
+    worker_func = functools.partial(RunSingleTestCaseParallel, self, run_label,
+                                    build_dir)
 
     try:
       # The timeout is a workaround for http://bugs.python.org/issue8296
       # which prevents KeyboardInterrupt from working.
       one_year_in_seconds = 3600 * 24 * 365
-      worker_results = (pool.map_async(worker_func, self.test_cases)
-                        .get(one_year_in_seconds))
+      worker_results = (
+          pool.map_async(worker_func, self.test_cases).get(one_year_in_seconds))
       for worker_result in worker_results:
         test_case, result = worker_result
         if result is not None:
@@ -471,8 +475,10 @@
     Returns:
       The measured profiling value for that test case.
     """
-    command = [self.safe_measure_script_path, test_case,
-               '--build-dir=%s' % build_dir]
+    command = [
+        self.safe_measure_script_path, test_case,
+        '--build-dir=%s' % build_dir
+    ]
 
     if self.args.interesting_section:
       command.append('--interesting-section')
@@ -484,11 +490,20 @@
     if profile_file_path:
       command.append('--output-path=%s' % profile_file_path)
 
+    if self.args.png_dir:
+      command.append('--png')
+
+    if self.args.pages:
+      command.extend(['--pages', self.args.pages])
+
     output = RunCommandPropagateErr(command)
 
     if output is None:
       return None
 
+    if self.args.png_dir:
+      self._MoveImages(test_case, run_label)
+
     # Get the time number as output, making sure it's just a number
     output = output.strip()
     if re.match('^[0-9]+$', output):
@@ -496,11 +511,21 @@
 
     return None
 
+  def _MoveImages(self, test_case, run_label):
+    png_dir = os.path.join(self.args.png_dir, run_label)
+    if not os.path.exists(png_dir):
+      os.makedirs(png_dir)
+
+    test_case_dir, test_case_filename = os.path.split(test_case)
+    test_case_png_matcher = '%s.*.png' % test_case_filename
+    for output_png in glob.glob(
+        os.path.join(test_case_dir, test_case_png_matcher)):
+      shutil.move(output_png, png_dir)
+
   def _GetProfileFilePath(self, run_label, test_case):
     if self.args.output_dir:
-      output_filename = ('callgrind.out.%s.%s'
-                         % (test_case.replace('/', '_'),
-                            run_label))
+      output_filename = (
+          'callgrind.out.%s.%s' % (test_case.replace('/', '_'), run_label))
       return os.path.join(self.args.output_dir, output_filename)
     else:
       return None
@@ -583,69 +608,110 @@
 
 def main():
   parser = argparse.ArgumentParser()
-  parser.add_argument('input_paths', nargs='+',
-                      help='pdf files or directories to search for pdf files '
-                           'to run as test cases')
-  parser.add_argument('--branch-before',
-                      help='git branch to use as "before" for comparison. '
-                           'Omitting this will use the current branch '
-                           'without uncommitted changes as the baseline.')
-  parser.add_argument('--branch-after',
-                      help='git branch to use as "after" for comparison. '
-                           'Omitting this will use the current branch '
-                           'with uncommitted changes.')
-  parser.add_argument('--build-dir', default=os.path.join('out', 'Release'),
-                      help='relative path from the base source directory '
-                           'to the build directory')
-  parser.add_argument('--build-dir-before',
-                      help='relative path from the base source directory '
-                           'to the build directory for the "before" branch, if '
-                           'different from the build directory for the '
-                           '"after" branch')
-  parser.add_argument('--cache-dir', default=None,
-                      help='directory with a new or preexisting cache for '
-                           'downloads. Default is to not use a cache.')
-  parser.add_argument('--this-repo', action='store_true',
-                      help='use the repository where the script is instead of '
-                           'checking out a temporary one. This is faster and '
-                           'does not require downloads, but although it '
-                           'restores the state of the local repo, if the '
-                           'script is killed or crashes the changes can remain '
-                           'stashed and you may be on another branch.')
-  parser.add_argument('--profiler', default='callgrind',
-                      help='which profiler to use. Supports callgrind and '
-                           'perfstat for now. Default is callgrind.')
-  parser.add_argument('--interesting-section', action='store_true',
-                      help='whether to measure just the interesting section or '
-                           'the whole test harness. Limiting to only the '
-                           'interesting section does not work on Release since '
-                           'the delimiters are optimized out')
-  parser.add_argument('--num-workers', default=multiprocessing.cpu_count(),
-                      type=int, help='run NUM_WORKERS jobs in parallel')
-  parser.add_argument('--output-dir',
-                      help='directory to write the profile data output files')
-  parser.add_argument('--threshold-significant', default=0.02, type=float,
-                      help='variations in performance above this factor are '
-                           'considered significant')
-  parser.add_argument('--machine-readable', action='store_true',
-                      help='whether to get output for machines. If enabled the '
-                           'output will be a json with the format specified in '
-                           'ComparisonConclusions.GetOutputDict(). Default is '
-                           'human-readable.')
-  parser.add_argument('--case-order', default=None,
-                      help='what key to use when sorting test cases in the '
-                           'output. Accepted values are "after", "before", '
-                           '"ratio" and "rating". Default is sorting by test '
-                           'case path.')
+  parser.add_argument(
+      'input_paths',
+      nargs='+',
+      help='pdf files or directories to search for pdf files '
+      'to run as test cases')
+  parser.add_argument(
+      '--branch-before',
+      help='git branch to use as "before" for comparison. '
+      'Omitting this will use the current branch '
+      'without uncommitted changes as the baseline.')
+  parser.add_argument(
+      '--branch-after',
+      help='git branch to use as "after" for comparison. '
+      'Omitting this will use the current branch '
+      'with uncommitted changes.')
+  parser.add_argument(
+      '--build-dir',
+      default=os.path.join('out', 'Release'),
+      help='relative path from the base source directory '
+      'to the build directory')
+  parser.add_argument(
+      '--build-dir-before',
+      help='relative path from the base source directory '
+      'to the build directory for the "before" branch, if '
+      'different from the build directory for the '
+      '"after" branch')
+  parser.add_argument(
+      '--cache-dir',
+      default=None,
+      help='directory with a new or preexisting cache for '
+      'downloads. Default is to not use a cache.')
+  parser.add_argument(
+      '--this-repo',
+      action='store_true',
+      help='use the repository where the script is instead of '
+      'checking out a temporary one. This is faster and '
+      'does not require downloads, but although it '
+      'restores the state of the local repo, if the '
+      'script is killed or crashes the changes can remain '
+      'stashed and you may be on another branch.')
+  parser.add_argument(
+      '--profiler',
+      default='callgrind',
+      help='which profiler to use. Supports callgrind, '
+      'perfstat, and none. Default is callgrind.')
+  parser.add_argument(
+      '--interesting-section',
+      action='store_true',
+      help='whether to measure just the interesting section or '
+      'the whole test harness. Limiting to only the '
+      'interesting section does not work on Release since '
+      'the delimiters are optimized out')
+  parser.add_argument(
+      '--pages',
+      help='selects some pages to be rendered. Page numbers '
+      'are 0-based. "--pages A" will render only page A. '
+      '"--pages A-B" will render pages A to B '
+      '(inclusive).')
+  parser.add_argument(
+      '--num-workers',
+      default=multiprocessing.cpu_count(),
+      type=int,
+      help='run NUM_WORKERS jobs in parallel')
+  parser.add_argument(
+      '--output-dir', help='directory to write the profile data output files')
+  parser.add_argument(
+      '--png-dir',
+      default=None,
+      help='outputs pngs to the specified directory that can '
+      'be compared with a static html generated. Will '
+      'affect performance measurements.')
+  parser.add_argument(
+      '--png-threshold',
+      default=0.0,
+      type=float,
+      help='Requires --png-dir. Threshold above which a png '
+      'is considered to have changed.')
+  parser.add_argument(
+      '--threshold-significant',
+      default=0.02,
+      type=float,
+      help='variations in performance above this factor are '
+      'considered significant')
+  parser.add_argument(
+      '--machine-readable',
+      action='store_true',
+      help='whether to get output for machines. If enabled the '
+      'output will be a json with the format specified in '
+      'ComparisonConclusions.GetOutputDict(). Default is '
+      'human-readable.')
+  parser.add_argument(
+      '--case-order',
+      default=None,
+      help='what key to use when sorting test cases in the '
+      'output. Accepted values are "after", "before", '
+      '"ratio" and "rating". Default is sorting by test '
+      'case path.')
 
   args = parser.parse_args()
 
   # Always start at the pdfium src dir, which is assumed to be two level above
   # this script.
   pdfium_src_dir = os.path.join(
-      os.path.dirname(__file__),
-      os.path.pardir,
-      os.path.pardir)
+      os.path.dirname(__file__), os.path.pardir, os.path.pardir)
   os.chdir(pdfium_src_dir)
 
   git = GitHelper()
@@ -668,10 +734,31 @@
       PrintErr('"%s" is not a directory' % args.output_dir)
       return 1
 
+  if args.png_dir:
+    args.png_dir = os.path.expanduser(args.png_dir)
+    if not os.path.isdir(args.png_dir):
+      PrintErr('"%s" is not a directory' % args.png_dir)
+      return 1
+
   if args.threshold_significant <= 0.0:
     PrintErr('--threshold-significant should receive a positive float')
     return 1
 
+  if args.png_threshold:
+    if not args.png_dir:
+      PrintErr('--png-threshold requires --png-dir to be specified.')
+      return 1
+
+    if args.png_threshold <= 0.0:
+      PrintErr('--png-threshold should receive a positive float')
+      return 1
+
+  if args.pages:
+    if not re.match(r'^\d+(-\d+)?$', args.pages):
+      PrintErr('Supported formats for --pages are "--pages 7" and '
+               '"--pages 3-6"')
+      return 1
+
   run = CompareRun(args)
   return run.Run()
 
diff --git a/testing/tools/safetynet_conclusions.py b/testing/tools/safetynet_conclusions.py
index fdbc10d..8f0b28c 100644
--- a/testing/tools/safetynet_conclusions.py
+++ b/testing/tools/safetynet_conclusions.py
@@ -1,12 +1,10 @@
 # Copyright 2017 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Classes that draw conclusions out of a comparison and represent them."""
 
 from collections import Counter
 
-
 FORMAT_RED = '\033[01;31m{0}\033[00m'
 FORMAT_GREEN = '\033[01;32m{0}\033[00m'
 FORMAT_MAGENTA = '\033[01;35m{0}\033[00m'
@@ -20,10 +18,7 @@
 RATING_SMALL_CHANGE = 'small_change'
 
 RATINGS = [
-    RATING_FAILURE,
-    RATING_REGRESSION,
-    RATING_IMPROVEMENT,
-    RATING_NO_CHANGE,
+    RATING_FAILURE, RATING_REGRESSION, RATING_IMPROVEMENT, RATING_NO_CHANGE,
     RATING_SMALL_CHANGE
 ]
 
@@ -233,10 +228,12 @@
 
   def GetOutputDict(self):
     """Returns a dict with the test case's conclusions."""
-    return {'before': self.before,
-            'after': self.after,
-            'ratio': self.ratio,
-            'rating': self.rating}
+    return {
+        'before': self.before,
+        'after': self.after,
+        'ratio': self.ratio,
+        'rating': self.rating
+    }
 
 
 def PrintConclusionsDictHumanReadable(conclusions_dict, colored, key=None):
@@ -249,18 +246,16 @@
   """
   # Print header
   print '=' * 80
-  print '{0:>11s} {1:>15s}  {2}' .format(
-      '% Change',
-      'Time after',
-      'Test case')
+  print '{0:>11s} {1:>15s}  {2}'.format('% Change', 'Time after', 'Test case')
   print '-' * 80
 
   color = FORMAT_NORMAL
 
   # Print cases
   if key is not None:
-    case_pairs = sorted(conclusions_dict['comparison_by_case'].iteritems(),
-                        key=lambda kv: kv[1][key])
+    case_pairs = sorted(
+        conclusions_dict['comparison_by_case'].iteritems(),
+        key=lambda kv: kv[1][key])
   else:
     case_pairs = sorted(conclusions_dict['comparison_by_case'].iteritems())
 
@@ -270,14 +265,12 @@
 
     if case_dict['rating'] == RATING_FAILURE:
       print u'{} to measure time for {}'.format(
-          color.format('Failed'),
-          case_name).encode('utf-8')
+          color.format('Failed'), case_name).encode('utf-8')
       continue
 
-    print u'{0} {1:15,d}  {2}' .format(
+    print u'{0} {1:15,d}  {2}'.format(
         color.format('{:+11.4%}'.format(case_dict['ratio'])),
-        case_dict['after'],
-        case_name).encode('utf-8')
+        case_dict['after'], case_name).encode('utf-8')
 
   # Print totals
   totals = conclusions_dict['summary']
@@ -286,15 +279,12 @@
 
   if colored:
     color = FORMAT_MAGENTA if totals[RATING_FAILURE] else FORMAT_GREEN
-  print ('Failed to measure: %s'
-         % color.format(totals[RATING_FAILURE]))
+  print('Failed to measure: %s' % color.format(totals[RATING_FAILURE]))
 
   if colored:
     color = FORMAT_RED if totals[RATING_REGRESSION] else FORMAT_GREEN
-  print ('Regressions: %s'
-         % color.format(totals[RATING_REGRESSION]))
+  print('Regressions: %s' % color.format(totals[RATING_REGRESSION]))
 
   if colored:
     color = FORMAT_CYAN if totals[RATING_IMPROVEMENT] else FORMAT_GREEN
-  print ('Improvements: %s'
-         % color.format(totals[RATING_IMPROVEMENT]))
+  print('Improvements: %s' % color.format(totals[RATING_IMPROVEMENT]))
diff --git a/testing/tools/safetynet_image.py b/testing/tools/safetynet_image.py
new file mode 100644
index 0000000..f300615
--- /dev/null
+++ b/testing/tools/safetynet_image.py
@@ -0,0 +1,306 @@
+# Copyright 2017 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Compares pairs of page images and generates an HTML to look at differences.
+"""
+
+import functools
+import glob
+import multiprocessing
+import os
+import re
+import subprocess
+import sys
+import webbrowser
+
+# pylint: disable=relative-import
+from common import DirectoryFinder
+
+
+def GenerateOneDiffParallel(image_comparison, image):
+  return image_comparison.GenerateOneDiff(image)
+
+
+class ImageComparison(object):
+  """Compares pairs of page images and generates an HTML to look at differences.
+
+  The images are all assumed to have the same name and be in two directories:
+  [output_path]/[two_labels[0]] and [output_path]/[two_labels[1]]. For example,
+  if output_path is "/tmp/images" and two_labels is ("before", "after"),
+  images in /tmp/images/before will be compared to /tmp/images/after. The HTML
+  produced will be in /tmp/images/compare.html and have relative links to these
+  images, so /tmp/images is self-contained and can be moved around or shared.
+  """
+
+  def __init__(self, build_dir, output_path, two_labels, num_workers,
+               threshold_fraction):
+    """Constructor.
+
+    Args:
+      build_dir: Path to the build directory.
+      output_path: Path with the pngs and where the html will be created.
+      two_labels: Tuple of two strings that name the subdirectories in
+          output_path containing the images.
+      num_workers: Number of worker threads to start.
+      threshold_fraction: Minimum percentage (0.0 to 1.0) of pixels below which
+          an image is considered to have only small changes. They will not be
+          displayed on the HTML, only listed.
+    """
+    self.build_dir = build_dir
+    self.output_path = output_path
+    self.two_labels = two_labels
+    self.num_workers = num_workers
+    self.threshold = threshold_fraction * 100
+
+  def Run(self, open_in_browser):
+    """Runs the comparison and generates an HTML with the results.
+
+    Returns:
+        Exit status.
+    """
+
+    # Running a test defines a number of attributes on the fly.
+    # pylint: disable=attribute-defined-outside-init
+
+    if len(self.two_labels) != 2:
+      print >> sys.stderr, 'two_labels must be a tuple of length 2'
+      return 1
+
+    finder = DirectoryFinder(self.build_dir)
+    self.img_diff_bin = finder.ExecutablePath('pdfium_diff')
+
+    html_path = os.path.join(self.output_path, 'compare.html')
+
+    self.diff_path = os.path.join(self.output_path, 'diff')
+    if not os.path.exists(self.diff_path):
+      os.makedirs(self.diff_path)
+
+    self.image_locations = ImageLocations(self.output_path, self.diff_path,
+                                          self.two_labels)
+
+    difference = self._GenerateDiffs()
+
+    small_changes = []
+
+    with open(html_path, 'w') as f:
+      f.write('<html><body>')
+      f.write('<table>')
+      for image in self.image_locations.Images():
+        diff = difference[image]
+        if diff is None:
+          print >> sys.stderr, 'Failed to compare image %s' % image
+        elif diff > self.threshold:
+          self._WriteImageRows(f, image, diff)
+        else:
+          small_changes.append((image, diff))
+      self._WriteSmallChanges(f, small_changes)
+      f.write('</table>')
+      f.write('</body></html>')
+
+    if open_in_browser:
+      webbrowser.open(html_path)
+
+    return 0
+
+  def _GenerateDiffs(self):
+    """Runs a diff over all pairs of page images, producing diff images.
+
+    As a side effect, the diff images will be saved to [output_path]/diff
+    with the same image name.
+
+    Returns:
+      A dict mapping image names to percentage of pixels changes.
+    """
+    difference = {}
+    pool = multiprocessing.Pool(self.num_workers)
+    worker_func = functools.partial(GenerateOneDiffParallel, self)
+
+    try:
+      # The timeout is a workaround for http://bugs.python.org/issue8296
+      # which prevents KeyboardInterrupt from working.
+      one_year_in_seconds = 3600 * 24 * 365
+      worker_results = (
+          pool.map_async(
+              worker_func,
+              self.image_locations.Images()).get(one_year_in_seconds))
+      for worker_result in worker_results:
+        image, result = worker_result
+        difference[image] = result
+    except KeyboardInterrupt:
+      pool.terminate()
+      sys.exit(1)
+    else:
+      pool.close()
+
+    pool.join()
+
+    return difference
+
+  def GenerateOneDiff(self, image):
+    """Runs a diff over one pair of images, producing a diff image.
+
+    As a side effect, the diff image will be saved to [output_path]/diff
+    with the same image name.
+
+    Args:
+      image: Page image to compare.
+
+    Returns:
+      A tuple (image, diff), where image is the parameter and diff is the
+      percentage of pixels changed.
+    """
+    try:
+      subprocess.check_output([
+          self.img_diff_bin,
+          self.image_locations.Left(image),
+          self.image_locations.Right(image)
+      ])
+    except subprocess.CalledProcessError as e:
+      percentage_change = float(re.findall(r'\d+\.\d+', e.output)[0])
+    else:
+      return image, 0
+
+    try:
+      subprocess.check_output([
+          self.img_diff_bin, '--diff',
+          self.image_locations.Left(image),
+          self.image_locations.Right(image),
+          self.image_locations.Diff(image)
+      ])
+    except subprocess.CalledProcessError as e:
+      return image, percentage_change
+    else:
+      print >> sys.stderr, 'Warning: Should have failed the previous diff.'
+      return image, 0
+
+  def _GetRelativePath(self, absolute_path):
+    return os.path.relpath(absolute_path, start=self.output_path)
+
+  def _WriteImageRows(self, f, image, diff):
+    """Write table rows for a page image comparing its two versions.
+
+    Args:
+      f: Open HTML file to write to.
+      image: Image file name.
+      diff: Percentage of different pixels.
+    """
+    f.write('<tr><td colspan="2">')
+    f.write('%s (%.4f%% changed)' % (image, diff))
+    f.write('</td></tr>')
+
+    f.write('<tr>')
+    self._WritePageCompareTd(
+        f, self._GetRelativePath(self.image_locations.Left(image)),
+        self._GetRelativePath(self.image_locations.Right(image)))
+    self._WritePageTd(f, self._GetRelativePath(
+        self.image_locations.Diff(image)))
+    f.write('</tr>')
+
+  def _WritePageTd(self, f, image_path):
+    """Write table column with a single image.
+
+    Args:
+      f: Open HTML file to write to.
+      image_path: Path to image file.
+    """
+    f.write('<td>')
+    f.write('<img src="%s">' % image_path)
+    f.write('</td>')
+
+  def _WritePageCompareTd(self, f, normal_image_path, hover_image_path):
+    """Write table column for an image comparing its two versions.
+
+    Args:
+      f: Open HTML file to write to.
+      normal_image_path: Path to image to be used in the "normal" state.
+      hover_image_path: Path to image to be used in the "hover" state.
+    """
+    f.write('<td>')
+    f.write('<img src="%s" '
+            'onmouseover="this.src=\'%s\';" '
+            'onmouseout="this.src=\'%s\';">' %
+            (normal_image_path, hover_image_path, normal_image_path))
+    f.write('</td>')
+
+  def _WriteSmallChanges(self, f, small_changes):
+    """Write table rows for all images considered to have only small changes.
+
+    Args:
+      f: Open HTML file to write to.
+      small_changes: List of (image, change) tuples, where image is the page
+          image and change is the percentage of pixels changed.
+    """
+    for image, change in small_changes:
+      f.write('<tr><td colspan="2">')
+      if not change:
+        f.write('No change for: %s' % image)
+      else:
+        f.write('Small change of %.4f%% for: %s' % (change, image))
+      f.write('</td></tr>')
+
+
+class ImageLocations(object):
+  """Contains the locations of input and output image files.
+  """
+
+  def __init__(self, output_path, diff_path, two_labels):
+    """Constructor.
+
+    Args:
+      output_path: Path to directory with the pngs.
+      diff_path: Path to directory where the diffs will be generated.
+      two_labels: Tuple of two strings that name the subdirectories in
+          output_path containing the images.
+    """
+    self.output_path = output_path
+    self.diff_path = diff_path
+    self.two_labels = two_labels
+
+    self.left = self._FindImages(self.two_labels[0])
+    self.right = self._FindImages(self.two_labels[1])
+
+    self.images = list(self.left.viewkeys() & self.right.viewkeys())
+
+    # Sort by pdf filename, then page number
+    def KeyFn(s):
+      pieces = s.rsplit('.', 2)
+      return (pieces[0], int(pieces[1]))
+
+    self.images.sort(key=KeyFn)
+    self.diff = {
+        image: os.path.join(self.diff_path, image) for image in self.images
+    }
+
+  def _FindImages(self, label):
+    """Traverses a dir and builds a dict of all page images to compare in it.
+
+    Args:
+      label: name of subdirectory of output_path to traverse.
+
+    Returns:
+      Dict mapping page image names to the path of the image file.
+    """
+    image_path_matcher = os.path.join(self.output_path, label, '*.*.png')
+    image_paths = glob.glob(image_path_matcher)
+
+    image_dict = {
+        os.path.split(image_path)[1]: image_path for image_path in image_paths
+    }
+
+    return image_dict
+
+  def Images(self):
+    """Returns a list of all page images present in both directories."""
+    return self.images
+
+  def Left(self, test_case):
+    """Returns the path for a page image in the first subdirectory."""
+    return self.left[test_case]
+
+  def Right(self, test_case):
+    """Returns the path for a page image in the second subdirectory."""
+    return self.right[test_case]
+
+  def Diff(self, test_case):
+    """Returns the path for a page diff image."""
+    return self.diff[test_case]
diff --git a/testing/tools/safetynet_job.py b/testing/tools/safetynet_job.py
index 7416533..9b5cbfd 100755
--- a/testing/tools/safetynet_job.py
+++ b/testing/tools/safetynet_job.py
@@ -2,7 +2,6 @@
 # Copyright 2017 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Looks for performance regressions on all pushes since the last run.
 
 Run this nightly to have a periodical check for performance regressions.
@@ -16,6 +15,7 @@
 import os
 import sys
 
+# pylint: disable=relative-import
 from common import PrintWithTime
 from common import RunCommandPropagateErr
 from githelper import GitHelper
@@ -62,15 +62,9 @@
       there was an improvement and no regression.
     """
     pdfium_src_dir = os.path.join(
-        os.path.dirname(__file__),
-        os.path.pardir,
-        os.path.pardir)
+        os.path.dirname(__file__), os.path.pardir, os.path.pardir)
     os.chdir(pdfium_src_dir)
 
-    if not self.git.IsCurrentBranchClean() and not self.args.no_checkout:
-      PrintWithTime('Current branch is not clean, aborting')
-      return 1
-
     branch_to_restore = self.git.GetCurrentBranchName()
 
     if not self.args.no_checkout:
@@ -118,8 +112,8 @@
     """
     current = self.git.GetCurrentBranchHash()
 
-    PrintWithTime('Incremental run, current is %s, last is %s'
-                  % (current, last_revision_covered))
+    PrintWithTime('Incremental run, current is %s, last is %s' %
+                  (current, last_revision_covered))
 
     if not os.path.exists(self.context.run_output_dir):
       os.makedirs(self.context.run_output_dir)
@@ -127,18 +121,19 @@
     if current == last_revision_covered:
       PrintWithTime('No changes seen, finishing job')
       output_info = {
-          'metadata': self._BuildRunMetadata(last_revision_covered,
-                                             current,
-                                             False)}
+          'metadata':
+              self._BuildRunMetadata(last_revision_covered, current, False)
+      }
       self._WriteRawJson(output_info)
       return 0
 
     # Run compare
-    cmd = ['testing/tools/safetynet_compare.py',
-           '--this-repo',
-           '--machine-readable',
-           '--branch-before=%s' % last_revision_covered,
-           '--output-dir=%s' % self.context.run_output_dir]
+    cmd = [
+        'testing/tools/safetynet_compare.py', '--this-repo',
+        '--machine-readable',
+        '--branch-before=%s' % last_revision_covered,
+        '--output-dir=%s' % self.context.run_output_dir
+    ]
     cmd.extend(self.args.input_paths)
 
     json_output = RunCommandPropagateErr(cmd)
@@ -148,16 +143,14 @@
 
     output_info = json.loads(json_output)
 
-    run_metadata = self._BuildRunMetadata(last_revision_covered,
-                                          current,
-                                          True)
+    run_metadata = self._BuildRunMetadata(last_revision_covered, current, True)
     output_info.setdefault('metadata', {}).update(run_metadata)
     self._WriteRawJson(output_info)
 
-    PrintConclusionsDictHumanReadable(output_info,
-                                      colored=(not self.args.output_to_log
-                                               and not self.args.no_color),
-                                      key='after')
+    PrintConclusionsDictHumanReadable(
+        output_info,
+        colored=(not self.args.output_to_log and not self.args.no_color),
+        key='after')
 
     status = 0
 
@@ -198,22 +191,31 @@
 
 def main():
   parser = argparse.ArgumentParser()
-  parser.add_argument('results_dir',
-                      help='where to write the job results')
-  parser.add_argument('input_paths', nargs='+',
-                      help='pdf files or directories to search for pdf files '
-                           'to run as test cases')
-  parser.add_argument('--no-checkout', action='store_true',
-                      help='whether to skip checking out origin/master. Use '
-                           'for script debugging.')
-  parser.add_argument('--no-checkpoint', action='store_true',
-                      help='whether to skip writing the new checkpoint. Use '
-                           'for script debugging.')
-  parser.add_argument('--no-color', action='store_true',
-                      help='whether to write output without color escape '
-                           'codes.')
-  parser.add_argument('--output-to-log', action='store_true',
-                      help='whether to write output to a log file')
+  parser.add_argument('results_dir', help='where to write the job results')
+  parser.add_argument(
+      'input_paths',
+      nargs='+',
+      help='pdf files or directories to search for pdf files '
+      'to run as test cases')
+  parser.add_argument(
+      '--no-checkout',
+      action='store_true',
+      help='whether to skip checking out origin/master. Use '
+      'for script debugging.')
+  parser.add_argument(
+      '--no-checkpoint',
+      action='store_true',
+      help='whether to skip writing the new checkpoint. Use '
+      'for script debugging.')
+  parser.add_argument(
+      '--no-color',
+      action='store_true',
+      help='whether to write output without color escape '
+      'codes.')
+  parser.add_argument(
+      '--output-to-log',
+      action='store_true',
+      help='whether to write output to a log file')
   args = parser.parse_args()
 
   job_context = JobContext(args)
@@ -234,4 +236,3 @@
 
 if __name__ == '__main__':
   sys.exit(main())
-
diff --git a/testing/tools/safetynet_measure.py b/testing/tools/safetynet_measure.py
index 7b5bd5f..3577189 100755
--- a/testing/tools/safetynet_measure.py
+++ b/testing/tools/safetynet_measure.py
@@ -2,7 +2,6 @@
 # Copyright 2017 The PDFium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Measures performance for rendering a single test case with pdfium.
 
 The output is a number that is a metric which depends on the profiler specified.
@@ -14,14 +13,16 @@
 import subprocess
 import sys
 
+# pylint: disable=relative-import
 from common import PrintErr
 
-
 CALLGRIND_PROFILER = 'callgrind'
 PERFSTAT_PROFILER = 'perfstat'
+NONE_PROFILER = 'none'
 
 PDFIUM_TEST = 'pdfium_test'
 
+
 class PerformanceRun(object):
   """A single measurement of a test case."""
 
@@ -32,13 +33,13 @@
   def _CheckTools(self):
     """Returns whether the tool file paths are sane."""
     if not os.path.exists(self.pdfium_test_path):
-      PrintErr("FAILURE: Can't find test executable '%s'"
-               % self.pdfium_test_path)
+      PrintErr(
+          "FAILURE: Can't find test executable '%s'" % self.pdfium_test_path)
       PrintErr('Use --build-dir to specify its location.')
       return False
     if not os.access(self.pdfium_test_path, os.X_OK):
-      PrintErr("FAILURE: Test executable '%s' lacks execution permissions"
-               % self.pdfium_test_path)
+      PrintErr("FAILURE: Test executable '%s' lacks execution permissions" %
+               self.pdfium_test_path)
       return False
     return True
 
@@ -55,6 +56,8 @@
       time = self._RunCallgrind()
     elif self.args.profiler == PERFSTAT_PROFILER:
       time = self._RunPerfStat()
+    elif self.args.profiler == NONE_PROFILER:
+      time = self._RunWithoutProfiler()
     else:
       PrintErr('profiler=%s not supported, aborting' % self.args.profiler)
       return 1
@@ -76,10 +79,11 @@
     instrument_at_start = 'no' if self.args.interesting_section else 'yes'
     output_path = self.args.output_path or '/dev/null'
 
-    valgrind_cmd = (['valgrind', '--tool=callgrind',
-                     '--instr-atstart=%s' % instrument_at_start,
-                     '--callgrind-out-file=%s' % output_path]
-                    + self._BuildTestHarnessCommand())
+    valgrind_cmd = ([
+        'valgrind', '--tool=callgrind',
+        '--instr-atstart=%s' % instrument_at_start,
+        '--callgrind-out-file=%s' % output_path
+    ] + self._BuildTestHarnessCommand())
     output = subprocess.check_output(valgrind_cmd, stderr=subprocess.STDOUT)
 
     # Match the line with the instruction count, eg.
@@ -94,19 +98,38 @@
     """
     # --no-big-num: do not add thousands separators
     # -einstructions: print only instruction count
-    cmd_to_run = (['perf', 'stat', '--no-big-num', '-einstructions']
-                  + self._BuildTestHarnessCommand())
+    cmd_to_run = (['perf', 'stat', '--no-big-num', '-einstructions'] +
+                  self._BuildTestHarnessCommand())
     output = subprocess.check_output(cmd_to_run, stderr=subprocess.STDOUT)
 
     # Match the line with the instruction count, eg.
     # '        12345      instructions'
     return self._ExtractIrCount(r'\b(\d+)\b.*\binstructions\b', output)
 
+  def _RunWithoutProfiler(self):
+    """Runs test harness and measures performance without a profiler.
+
+    Returns:
+      int with the result of the measurement, in instructions or time. In this
+      case, always return 1 since no profiler is being used.
+    """
+    cmd_to_run = self._BuildTestHarnessCommand()
+    subprocess.check_output(cmd_to_run, stderr=subprocess.STDOUT)
+
+    # Return 1 for every run.
+    return 1
+
   def _BuildTestHarnessCommand(self):
     """Builds command to run the test harness."""
     cmd = [self.pdfium_test_path, '--send-events']
+
     if self.args.interesting_section:
       cmd.append('--callgrind-delim')
+    if self.args.png:
+      cmd.append('--png')
+    if self.args.pages:
+      cmd.append('--pages=%s' % self.args.pages)
+
     cmd.append(self.args.pdf_path)
     return cmd
 
@@ -123,25 +146,42 @@
 
 def main():
   parser = argparse.ArgumentParser()
-  parser.add_argument('pdf_path',
-                      help='test case to measure load and rendering time')
-  parser.add_argument('--build-dir', default=os.path.join('out', 'Release'),
-                      help='relative path to the build directory with '
-                           '%s' % PDFIUM_TEST)
-  parser.add_argument('--profiler', default=CALLGRIND_PROFILER,
-                      help='which profiler to use. Supports callgrind and '
-                           'perfstat for now.')
-  parser.add_argument('--interesting-section', action='store_true',
-                      help='whether to measure just the interesting section or '
-                           'the whole test harness. The interesting section is '
-                           'pdfium reading a pdf from memory and rendering '
-                           'it, which omits loading the time to load the file, '
-                           'initialize the library, terminate it, etc. '
-                           'Limiting to only the interesting section does not '
-                           'work on Release since the delimiters are optimized '
-                           'out. Callgrind only.')
-  parser.add_argument('--output-path',
-                      help='where to write the profile data output file')
+  parser.add_argument(
+      'pdf_path', help='test case to measure load and rendering time')
+  parser.add_argument(
+      '--build-dir',
+      default=os.path.join('out', 'Release'),
+      help='relative path to the build directory with '
+      '%s' % PDFIUM_TEST)
+  parser.add_argument(
+      '--profiler',
+      default=CALLGRIND_PROFILER,
+      help='which profiler to use. Supports callgrind, '
+      'perfstat, and none.')
+  parser.add_argument(
+      '--interesting-section',
+      action='store_true',
+      help='whether to measure just the interesting section or '
+      'the whole test harness. The interesting section is '
+      'pdfium reading a pdf from memory and rendering '
+      'it, which omits loading the time to load the file, '
+      'initialize the library, terminate it, etc. '
+      'Limiting to only the interesting section does not '
+      'work on Release since the delimiters are optimized '
+      'out. Callgrind only.')
+  parser.add_argument(
+      '--png',
+      action='store_true',
+      help='outputs a png image on the same location as the '
+      'pdf file')
+  parser.add_argument(
+      '--pages',
+      help='selects some pages to be rendered. Page numbers '
+      'are 0-based. "--pages A" will render only page A. '
+      '"--pages A-B" will render pages A to B '
+      '(inclusive).')
+  parser.add_argument(
+      '--output-path', help='where to write the profile data output file')
   args = parser.parse_args()
 
   if args.interesting_section and args.profiler != CALLGRIND_PROFILER:
diff --git a/testing/tools/suppressor.py b/testing/tools/suppressor.py
index 2b4b5d3..70eef99 100755
--- a/testing/tools/suppressor.py
+++ b/testing/tools/suppressor.py
@@ -5,39 +5,47 @@
 
 import os
 
+# pylint: disable=relative-import
 import common
 
+
 class Suppressor:
-  def __init__(self, finder, feature_string):
+
+  def __init__(self, finder, feature_string, js_disabled, xfa_disabled):
     feature_vector = feature_string.strip().split(",")
-    self.has_v8 = "V8" in feature_vector
-    self.has_xfa = "XFA" in feature_vector
+    self.has_v8 = not js_disabled and "V8" in feature_vector
+    self.has_xfa = (not js_disabled and not xfa_disabled and
+                    "XFA" in feature_vector)
     self.suppression_set = self._LoadSuppressedSet('SUPPRESSIONS', finder)
     self.image_suppression_set = self._LoadSuppressedSet(
-        'SUPPRESSIONS_IMAGE_DIFF',
-        finder)
+        'SUPPRESSIONS_IMAGE_DIFF', finder)
 
   def _LoadSuppressedSet(self, suppressions_filename, finder):
     v8_option = "v8" if self.has_v8 else "nov8"
     xfa_option = "xfa" if self.has_xfa else "noxfa"
     with open(os.path.join(finder.TestingDir(), suppressions_filename)) as f:
-      return set(self._FilterSuppressions(
-        common.os_name(), v8_option, xfa_option, self._ExtractSuppressions(f)))
+      return set(
+          self._FilterSuppressions(common.os_name(), v8_option, xfa_option,
+                                   self._ExtractSuppressions(f)))
 
   def _ExtractSuppressions(self, f):
-    return [y.split(' ') for y in
-            [x.split('#')[0].strip() for x in
-             f.readlines()] if y]
+    return [
+        y.split(' ') for y in [x.split('#')[0].strip()
+                               for x in f.readlines()] if y
+    ]
 
-  def _FilterSuppressions(self, os, js, xfa, unfiltered_list):
-    return [x[0] for x in unfiltered_list
-            if self._MatchSuppression(x, os, js, xfa)]
+  def _FilterSuppressions(self, os_name, js, xfa, unfiltered_list):
+    return [
+        x[0]
+        for x in unfiltered_list
+        if self._MatchSuppression(x, os_name, js, xfa)
+    ]
 
-  def _MatchSuppression(self, item, os, js, xfa):
-    os_column = item[1].split(",");
-    js_column = item[2].split(",");
-    xfa_column = item[3].split(",");
-    return (('*' in os_column or os in os_column) and
+  def _MatchSuppression(self, item, os_name, js, xfa):
+    os_column = item[1].split(",")
+    js_column = item[2].split(",")
+    xfa_column = item[3].split(",")
+    return (('*' in os_column or os_name in os_column) and
             ('*' in js_column or js in js_column) and
             ('*' in xfa_column or xfa in xfa_column))
 
diff --git a/testing/tools/test_runner.py b/testing/tools/test_runner.py
index e895552..d3640d6 100644
--- a/testing/tools/test_runner.py
+++ b/testing/tools/test_runner.py
@@ -3,7 +3,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import cStringIO
 import functools
 import multiprocessing
 import optparse
@@ -13,12 +12,24 @@
 import subprocess
 import sys
 
+# pylint: disable=relative-import
 import common
 import gold
 import pngdiffer
 import suppressor
 
-class KeyboardInterruptError(Exception): pass
+# Arbitrary timestamp, expressed in seconds since the epoch, used to make sure
+# that tests that depend on the current time are stable. Happens to be the
+# timestamp of the first commit to repo, 2014/5/9 17:48:50.
+TEST_SEED_TIME = "1399672130"
+
+# List of test types that should run text tests instead of pixel tests.
+TEXT_TESTS = ['javascript']
+
+
+class KeyboardInterruptError(Exception):
+  pass
+
 
 # Nomenclature:
 #   x_root - "x"
@@ -26,19 +37,34 @@
 #   x_path - "path/to/a/b/c/x.ext"
 #   c_dir - "path/to/a/b/c"
 
+
 def TestOneFileParallel(this, test_case):
   """Wrapper to call GenerateAndTest() and redirect output to stdout."""
   try:
     input_filename, source_dir = test_case
-    result = this.GenerateAndTest(input_filename, source_dir);
+    result = this.GenerateAndTest(input_filename, source_dir)
     return (result, input_filename, source_dir)
   except KeyboardInterrupt:
     raise KeyboardInterruptError()
 
 
+def DeleteFiles(files):
+  """Utility function to delete a list of files"""
+  for f in files:
+    if os.path.exists(f):
+      os.remove(f)
+
+
 class TestRunner:
+
   def __init__(self, dirname):
+    # Currently the only used directories are corpus, javascript, and pixel,
+    # which all correspond directly to the type for the test being run. In the
+    # future if there are tests that don't have this clean correspondence, then
+    # an argument for the type will need to be added.
     self.test_dir = dirname
+    self.test_type = dirname
+    self.delete_output_on_success = False
     self.enforce_expected_images = False
     self.oneshot_renderer = False
 
@@ -48,16 +74,12 @@
   #          (path_to_image, md5_hash_of_pixelbuffer)
   def GenerateAndTest(self, input_filename, source_dir):
     input_root, _ = os.path.splitext(input_filename)
-    expected_txt_path = os.path.join(source_dir, input_root + '_expected.txt')
-
     pdf_path = os.path.join(self.working_dir, input_root + '.pdf')
 
     # Remove any existing generated images from previous runs.
     actual_images = self.image_differ.GetActualFiles(input_filename, source_dir,
                                                      self.working_dir)
-    for image in actual_images:
-      if os.path.exists(image):
-        os.remove(image)
+    DeleteFiles(actual_images)
 
     sys.stdout.flush()
 
@@ -69,10 +91,13 @@
       return False, []
 
     results = []
-    if os.path.exists(expected_txt_path):
-      raised_exception = self.TestText(input_root, expected_txt_path, pdf_path)
+    if self.test_type in TEXT_TESTS:
+      expected_txt_path = os.path.join(source_dir, input_root + '_expected.txt')
+      raised_exception = self.TestText(input_filename, input_root,
+                                       expected_txt_path, pdf_path)
     else:
-      raised_exception, results = self.TestPixel(input_root, pdf_path)
+      use_ahem = 'use_ahem' in source_dir
+      raised_exception, results = self.TestPixel(pdf_path, use_ahem)
 
     if raised_exception is not None:
       print 'FAILURE: %s; %s' % (input_filename, raised_exception)
@@ -81,21 +106,29 @@
     if actual_images:
       if self.image_differ.HasDifferences(input_filename, source_dir,
                                           self.working_dir):
-        if (self.options.regenerate_expected
-            and not self.test_suppressor.IsResultSuppressed(input_filename)
-            and not self.test_suppressor.IsImageDiffSuppressed(input_filename)):
-          platform_only = (self.options.regenerate_expected == 'platform')
-          self.image_differ.Regenerate(input_filename, source_dir,
-                                       self.working_dir, platform_only)
+        self.RegenerateIfNeeded_(input_filename, source_dir)
         return False, results
     else:
-      if (self.enforce_expected_images
-          and not self.test_suppressor.IsImageDiffSuppressed(input_filename)):
+      if (self.enforce_expected_images and
+          not self.test_suppressor.IsImageDiffSuppressed(input_filename)):
+        self.RegenerateIfNeeded_(input_filename, source_dir)
         print 'FAILURE: %s; Missing expected images' % input_filename
         return False, results
 
+    if self.delete_output_on_success:
+      DeleteFiles(actual_images)
     return True, results
 
+  def RegenerateIfNeeded_(self, input_filename, source_dir):
+    if (not self.options.regenerate_expected or
+        self.test_suppressor.IsResultSuppressed(input_filename) or
+        self.test_suppressor.IsImageDiffSuppressed(input_filename)):
+      return
+
+    platform_only = (self.options.regenerate_expected == 'platform')
+    self.image_differ.Regenerate(input_filename, source_dir, self.working_dir,
+                                 platform_only)
+
   def Generate(self, source_dir, input_filename, input_root, pdf_path):
     original_path = os.path.join(source_dir, input_filename)
     input_path = os.path.join(source_dir, input_root + '.in')
@@ -112,24 +145,76 @@
 
     sys.stdout.flush()
 
-    return common.RunCommand(
-        [sys.executable, self.fixup_path, '--output-dir=' + self.working_dir,
-            input_path])
+    return common.RunCommand([
+        sys.executable, self.fixup_path, '--output-dir=' + self.working_dir,
+        input_path
+    ])
 
-  def TestText(self, input_root, expected_txt_path, pdf_path):
+  def TestText(self, input_filename, input_root, expected_txt_path, pdf_path):
     txt_path = os.path.join(self.working_dir, input_root + '.txt')
 
     with open(txt_path, 'w') as outfile:
-      cmd_to_run = [self.pdfium_test_path, '--send-events', pdf_path]
+      cmd_to_run = [
+          self.pdfium_test_path, '--send-events', '--time=' + TEST_SEED_TIME
+      ]
+
+      if self.options.disable_javascript:
+        cmd_to_run.append('--disable-javascript')
+
+      if self.options.disable_xfa:
+        cmd_to_run.append('--disable-xfa')
+
+      cmd_to_run.append(pdf_path)
       subprocess.check_call(cmd_to_run, stdout=outfile)
 
+    # If the expected file does not exist, the output is expected to be empty.
+    if not os.path.exists(expected_txt_path):
+      return self._VerifyEmptyText(txt_path)
+
+    # If JavaScript is disabled, the output should be empty.
+    # However, if the test is suppressed and JavaScript is disabled, do not
+    # verify that the text is empty so the suppressed test does not surprise.
+    if (self.options.disable_javascript and
+        not self.test_suppressor.IsResultSuppressed(input_filename)):
+      return self._VerifyEmptyText(txt_path)
+
     cmd = [sys.executable, self.text_diff_path, expected_txt_path, txt_path]
     return common.RunCommand(cmd)
 
-  def TestPixel(self, input_root, pdf_path):
-    cmd_to_run = [self.pdfium_test_path, '--send-events', '--png', '--md5']
+  def _VerifyEmptyText(self, txt_path):
+    try:
+      with open(txt_path, "r") as txt_file:
+        txt_data = txt_file.readlines()
+      if not len(txt_data):
+        return None
+      sys.stdout.write('Unexpected output:\n')
+      for line in txt_data:
+        sys.stdout.write(line)
+      raise Exception('%s should be empty.' % txt_path)
+    except Exception as e:
+      return e
+
+  def TestPixel(self, pdf_path, use_ahem):
+    cmd_to_run = [
+        self.pdfium_test_path, '--send-events', '--png', '--md5',
+        '--time=' + TEST_SEED_TIME
+    ]
+
     if self.oneshot_renderer:
       cmd_to_run.append('--render-oneshot')
+
+    if use_ahem:
+      cmd_to_run.append('--font-dir=%s' % self.font_dir)
+
+    if self.options.disable_javascript:
+      cmd_to_run.append('--disable-javascript')
+
+    if self.options.disable_xfa:
+      cmd_to_run.append('--disable-xfa')
+
+    if self.options.reverse_byte_order:
+      cmd_to_run.append('--reverse-byte-order')
+
     cmd_to_run.append(pdf_path)
     return common.RunCommandExtractHashedFiles(cmd_to_run)
 
@@ -143,15 +228,17 @@
         # becomes "example_005.pdf.0".
         test_name = os.path.splitext(os.path.split(img_path)[1])[0]
 
+        matched = "suppressed"
         if not self.test_suppressor.IsResultSuppressed(input_filename):
           matched = self.gold_baseline.MatchLocalResult(test_name, md5_hash)
           if matched == gold.GoldBaseline.MISMATCH:
             print 'Skia Gold hash mismatch for test case: %s' % test_name
-          elif matched ==  gold.GoldBaseline.NO_BASELINE:
+          elif matched == gold.GoldBaseline.NO_BASELINE:
             print 'No Skia Gold baseline found for test case: %s' % test_name
 
         if self.gold_results:
-          self.gold_results.AddTestResult(test_name, md5_hash, img_path)
+          self.gold_results.AddTestResult(test_name, md5_hash, img_path,
+                                          matched)
 
     if self.test_suppressor.IsResultSuppressed(input_filename):
       self.result_suppressed_cases.append(input_filename)
@@ -162,53 +249,95 @@
         self.failures.append(input_path)
 
   def Run(self):
+    # Running a test defines a number of attributes on the fly.
+    # pylint: disable=attribute-defined-outside-init
+
     parser = optparse.OptionParser()
 
-    parser.add_option('--build-dir', default=os.path.join('out', 'Debug'),
-                      help='relative path from the base source directory')
+    parser.add_option(
+        '--build-dir',
+        default=os.path.join('out', 'Debug'),
+        help='relative path from the base source directory')
 
-    parser.add_option('-j', default=multiprocessing.cpu_count(),
-                      dest='num_workers', type='int',
-                      help='run NUM_WORKERS jobs in parallel')
+    parser.add_option(
+        '-j',
+        default=multiprocessing.cpu_count(),
+        dest='num_workers',
+        type='int',
+        help='run NUM_WORKERS jobs in parallel')
 
-    parser.add_option('--gold_properties', default='', dest="gold_properties",
-                      help='Key value pairs that are written to the top level '
-                           'of the JSON file that is ingested by Gold.')
+    parser.add_option(
+        '--disable-javascript',
+        action="store_true",
+        dest="disable_javascript",
+        help='Prevents JavaScript from executing in PDF files.')
 
-    parser.add_option('--gold_key', default='', dest="gold_key",
-                      help='Key value pairs that are added to the "key" field '
-                           'of the JSON file that is ingested by Gold.')
+    parser.add_option(
+        '--disable-xfa',
+        action="store_true",
+        dest="disable_xfa",
+        help='Prevents processing XFA forms.')
 
-    parser.add_option('--gold_output_dir', default='', dest="gold_output_dir",
-                      help='Path of where to write the JSON output to be '
-                           'uploaded to Gold.')
+    parser.add_option(
+        '--gold_properties',
+        default='',
+        dest="gold_properties",
+        help='Key value pairs that are written to the top level '
+        'of the JSON file that is ingested by Gold.')
 
-    parser.add_option('--gold_ignore_hashes', default='',
-                      dest="gold_ignore_hashes",
-                      help='Path to a file with MD5 hashes we wish to ignore.')
+    parser.add_option(
+        '--gold_key',
+        default='',
+        dest="gold_key",
+        help='Key value pairs that are added to the "key" field '
+        'of the JSON file that is ingested by Gold.')
 
-    parser.add_option('--regenerate_expected', default='',
-                      dest="regenerate_expected",
-                      help='Regenerates expected images. Valid values are '
-                           '"all" to regenerate all expected pngs, and '
-                           '"platform" to regenerate only platform-specific '
-                           'expected pngs.')
+    parser.add_option(
+        '--gold_output_dir',
+        default='',
+        dest="gold_output_dir",
+        help='Path of where to write the JSON output to be '
+        'uploaded to Gold.')
 
-    parser.add_option('--ignore_errors', action="store_true",
-                      dest="ignore_errors",
-                      help='Prevents the return value from being non-zero '
-                           'when image comparison fails.')
+    parser.add_option(
+        '--gold_ignore_hashes',
+        default='',
+        dest="gold_ignore_hashes",
+        help='Path to a file with MD5 hashes we wish to ignore.')
+
+    parser.add_option(
+        '--regenerate_expected',
+        default='',
+        dest="regenerate_expected",
+        help='Regenerates expected images. Valid values are '
+        '"all" to regenerate all expected pngs, and '
+        '"platform" to regenerate only platform-specific '
+        'expected pngs.')
+
+    parser.add_option(
+        '--reverse-byte-order',
+        action='store_true',
+        dest="reverse_byte_order",
+        help='Run image-based tests using --reverse-byte-order.')
+
+    parser.add_option(
+        '--ignore_errors',
+        action="store_true",
+        dest="ignore_errors",
+        help='Prevents the return value from being non-zero '
+        'when image comparison fails.')
 
     self.options, self.args = parser.parse_args()
 
-    if (self.options.regenerate_expected
-        and self.options.regenerate_expected not in ['all', 'platform']) :
+    if (self.options.regenerate_expected and
+        self.options.regenerate_expected not in ['all', 'platform']):
       print 'FAILURE: --regenerate_expected must be "all" or "platform"'
       return 1
 
     finder = common.DirectoryFinder(self.options.build_dir)
     self.fixup_path = finder.ScriptPath('fixup_pdf_template.py')
     self.text_diff_path = finder.ScriptPath('text_diff.py')
+    self.font_dir = os.path.join(finder.TestingDir(), 'resources', 'fonts')
 
     self.source_dir = finder.TestingDir()
     if self.test_dir != 'corpus':
@@ -223,17 +352,25 @@
       return 1
 
     self.working_dir = finder.WorkingDir(os.path.join('testing', self.test_dir))
-    if not os.path.exists(self.working_dir):
-      os.makedirs(self.working_dir)
+    shutil.rmtree(self.working_dir, ignore_errors=True)
+    os.makedirs(self.working_dir)
 
-    self.feature_string = subprocess.check_output([self.pdfium_test_path,
-                                                   '--show-config'])
-    self.test_suppressor = suppressor.Suppressor(finder, self.feature_string)
-    self.image_differ = pngdiffer.PNGDiffer(finder)
+    self.feature_string = subprocess.check_output(
+        [self.pdfium_test_path, '--show-config'])
+    self.test_suppressor = suppressor.Suppressor(
+        finder, self.feature_string, self.options.disable_javascript,
+        self.options.disable_xfa)
+    self.image_differ = pngdiffer.PNGDiffer(finder,
+                                            self.options.reverse_byte_order)
+    error_message = self.image_differ.CheckMissingTools(
+        self.options.regenerate_expected)
+    if error_message:
+      print "FAILURE: %s" % error_message
+      return 1
 
     self.gold_baseline = gold.GoldBaseline(self.options.gold_properties)
 
-    walk_from_dir = finder.TestingDir(test_dir);
+    walk_from_dir = finder.TestingDir(test_dir)
 
     self.test_cases = []
     self.execution_suppressed_cases = []
@@ -247,7 +384,7 @@
           return 1
 
         self.test_cases.append((os.path.basename(input_path),
-                           os.path.dirname(input_path)))
+                                os.path.dirname(input_path)))
     else:
       for file_dir, _, filename_list in os.walk(walk_from_dir):
         for input_filename in filename_list:
@@ -259,6 +396,7 @@
               if os.path.isfile(input_path):
                 self.test_cases.append((input_filename, file_dir))
 
+    self.test_cases.sort()
     self.failures = []
     self.surprises = []
     self.result_suppressed_cases = []
@@ -266,11 +404,10 @@
     # Collect Gold results if an output directory was named.
     self.gold_results = None
     if self.options.gold_output_dir:
-      self.gold_results = gold.GoldResults('pdfium',
-                                           self.options.gold_output_dir,
-                                           self.options.gold_properties,
-                                           self.options.gold_key,
-                                           self.options.gold_ignore_hashes)
+      self.gold_results = gold.GoldResults(
+          self.test_type, self.options.gold_output_dir,
+          self.options.gold_properties, self.options.gold_key,
+          self.options.gold_ignore_hashes)
 
     if self.options.num_workers > 1 and len(self.test_cases) > 1:
       try:
@@ -334,6 +471,10 @@
     print
     print 'Test cases not executed: %d' % len(self.execution_suppressed_cases)
 
+  def SetDeleteOutputOnSuccess(self, new_value):
+    """Set whether to delete generated output if the test passes."""
+    self.delete_output_on_success = new_value
+
   def SetEnforceExpectedImages(self, new_value):
     """Set whether to enforce that each test case provide an expected image."""
     self.enforce_expected_images = new_value
diff --git a/testing/tools/text_diff.py b/testing/tools/text_diff.py
index 3a5bd7b..fdf45a0 100755
--- a/testing/tools/text_diff.py
+++ b/testing/tools/text_diff.py
@@ -6,17 +6,18 @@
 import difflib
 import sys
 
+
 def main(argv):
   if len(argv) != 3:
-     print '%s: invalid arguments' % argv[0]
-     return 2
+    print '%s: invalid arguments' % argv[0]
+    return 2
   filename1 = argv[1]
   filename2 = argv[2]
   try:
     with open(filename1, "r") as f1:
-      str1 = f1.readlines();
+      str1 = f1.readlines()
     with open(filename2, "r") as f2:
-      str2 = f2.readlines();
+      str2 = f2.readlines()
     diffs = difflib.unified_diff(
         str1, str2, fromfile=filename1, tofile=filename2)
   except Exception as e:
@@ -28,5 +29,6 @@
     status_code = 1
   return status_code
 
+
 if __name__ == '__main__':
   sys.exit(main(sys.argv))
diff --git a/testing/unit_test_main.cpp b/testing/unit_test_main.cpp
index 64b1299..5d50249 100644
--- a/testing/unit_test_main.cpp
+++ b/testing/unit_test_main.cpp
@@ -3,51 +3,21 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <string>
 
 #include "core/fxcrt/fx_memory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/test_support.h"
 
-#if PDF_ENABLE_XFA
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
-#include "xfa/fgas/font/cfgas_gefont.h"
+#ifdef PDF_ENABLE_V8
+#include "testing/v8_initializer.h"
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8.h"
+#endif  // PDF_ENABLE_V8
 
-namespace {
-
-// The loading time of the CFGAS_FontMgr is linear in the number of times it is
-// loaded. So, if a test suite has a lot of tests that need a font manager they
-// can end up executing very, very slowly.
-class Environment : public testing::Environment {
- public:
-  void SetUp() override {
-    // TODO(dsinclair): This font loading is slow. We should make a test font
-    // loader which loads up a single font we use in all tests.
-    CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(
-        IFX_SystemFontInfo::CreateDefault(nullptr));
-
-    font_mgr_ = pdfium::MakeUnique<CFGAS_FontMgr>();
-    if (!font_mgr_->EnumFonts())
-      font_mgr_ = nullptr;
-  }
-
-  void TearDown() override {
-    font_mgr_.reset();
-  }
-  CFGAS_FontMgr* FontManager() const { return font_mgr_.get(); }
-
- private:
-  std::unique_ptr<CFGAS_FontMgr> font_mgr_;
-};
-
-Environment* env_ = nullptr;
-
-}  // namespace
-
-CFGAS_FontMgr* GetGlobalFontManager() {
-  return env_->FontManager();
-}
+#ifdef PDF_ENABLE_XFA
+#include "testing/xfa_unit_test_support.h"
 #endif  // PDF_ENABLE_XFA
 
 // Can't use gtest-provided main since we need to initialize partition
@@ -55,13 +25,33 @@
 int main(int argc, char** argv) {
   FXMEM_InitializePartitionAlloc();
 
-#if PDF_ENABLE_XFA
-  env_ = new Environment();
-  // The env will be deleted by gtest.
-  AddGlobalTestEnvironment(env_);
+#ifdef PDF_ENABLE_V8
+  std::unique_ptr<v8::Platform> platform;
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  static v8::StartupData* snapshot = new v8::StartupData;
+  platform =
+      InitializeV8ForPDFiumWithStartupData(argv[0], std::string(), snapshot);
+#else  // V8_USE_EXTERNAL_STARTUP_DATA
+  platform = InitializeV8ForPDFium(argv[0]);
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+#endif  // PDF_ENABLE_V8
+
+  InitializePDFTestEnvironment();
+#ifdef PDF_ENABLE_XFA
+  InitializeXFATestEnvironment();
 #endif  // PDF_ENABLE_XFA
 
   testing::InitGoogleTest(&argc, argv);
   testing::InitGoogleMock(&argc, argv);
-  return RUN_ALL_TESTS();
+
+  int ret_val = RUN_ALL_TESTS();
+
+  DestroyPDFTestEnvironment();
+  // NOTE: XFA test environment, if present, destroyed by gtest.
+
+#ifdef PDF_ENABLE_V8
+  v8::V8::ShutdownPlatform();
+#endif  // PDF_ENABLE_V8
+
+  return ret_val;
 }
diff --git a/testing/utils/bitmap_saver.cpp b/testing/utils/bitmap_saver.cpp
new file mode 100644
index 0000000..524a9f6
--- /dev/null
+++ b/testing/utils/bitmap_saver.cpp
@@ -0,0 +1,51 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/utils/bitmap_saver.h"
+
+#include <fstream>
+#include <vector>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "testing/image_diff/image_diff_png.h"
+#include "third_party/base/logging.h"
+
+// static
+void BitmapSaver::WriteBitmapToPng(FPDF_BITMAP bitmap,
+                                   const std::string& filename) {
+  const int stride = FPDFBitmap_GetStride(bitmap);
+  const int width = FPDFBitmap_GetWidth(bitmap);
+  const int height = FPDFBitmap_GetHeight(bitmap);
+  CHECK(stride >= 0);
+  CHECK(width >= 0);
+  CHECK(height >= 0);
+  FX_SAFE_FILESIZE size = stride;
+  size *= height;
+  auto input = pdfium::make_span(
+      static_cast<const uint8_t*>(FPDFBitmap_GetBuffer(bitmap)),
+      pdfium::base::ValueOrDieForType<size_t>(size));
+
+  std::vector<uint8_t> png;
+  if (FPDFBitmap_GetFormat(bitmap) == FPDFBitmap_Gray) {
+    png = image_diff_png::EncodeGrayPNG(input, width, height, stride);
+  } else {
+    png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
+                                        /*discard_transparency=*/false);
+  }
+
+  DCHECK(!png.empty());
+  DCHECK(filename.size() < 256u);
+
+  std::ofstream png_file;
+  png_file.open(filename, std::ios_base::out | std::ios_base::binary);
+  png_file.write(reinterpret_cast<char*>(&png.front()), png.size());
+  DCHECK(png_file.good());
+  png_file.close();
+}
+
+// static
+void BitmapSaver::WriteBitmapToPng(CFX_DIBitmap* bitmap,
+                                   const std::string& filename) {
+  WriteBitmapToPng(reinterpret_cast<FPDF_BITMAP>(bitmap), filename);
+}
diff --git a/testing/utils/bitmap_saver.h b/testing/utils/bitmap_saver.h
new file mode 100644
index 0000000..9f931fc
--- /dev/null
+++ b/testing/utils/bitmap_saver.h
@@ -0,0 +1,21 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_UTILS_BITMAP_SAVER_H_
+#define TESTING_UTILS_BITMAP_SAVER_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+
+class CFX_DIBitmap;
+
+class BitmapSaver {
+ public:
+  static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename);
+  static void WriteBitmapToPng(CFX_DIBitmap* bitmap,
+                               const std::string& filename);
+};
+
+#endif  // TESTING_UTILS_BITMAP_SAVER_H_
diff --git a/testing/utils/file_util.cpp b/testing/utils/file_util.cpp
new file mode 100644
index 0000000..5d26d98
--- /dev/null
+++ b/testing/utils/file_util.cpp
@@ -0,0 +1,68 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/utils/file_util.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "testing/utils/path_service.h"
+
+std::unique_ptr<char, pdfium::FreeDeleter> GetFileContents(const char* filename,
+                                                           size_t* retlen) {
+  FILE* file = fopen(filename, "rb");
+  if (!file) {
+    fprintf(stderr, "Failed to open: %s\n", filename);
+    return nullptr;
+  }
+  (void)fseek(file, 0, SEEK_END);
+  size_t file_length = ftell(file);
+  if (!file_length) {
+    return nullptr;
+  }
+  (void)fseek(file, 0, SEEK_SET);
+  std::unique_ptr<char, pdfium::FreeDeleter> buffer(
+      static_cast<char*>(malloc(file_length)));
+  if (!buffer) {
+    return nullptr;
+  }
+  size_t bytes_read = fread(buffer.get(), 1, file_length, file);
+  (void)fclose(file);
+  if (bytes_read != file_length) {
+    fprintf(stderr, "Failed to read: %s\n", filename);
+    return nullptr;
+  }
+  *retlen = bytes_read;
+  return buffer;
+}
+
+FileAccessForTesting::FileAccessForTesting(const std::string& file_name) {
+  std::string file_path;
+  if (!PathService::GetTestFilePath(file_name, &file_path))
+    return;
+
+  file_contents_ = GetFileContents(file_path.c_str(), &file_length_);
+  if (!file_contents_)
+    return;
+
+  m_FileLen = static_cast<unsigned long>(file_length_);
+  m_GetBlock = SGetBlock;
+  m_Param = this;
+}
+
+int FileAccessForTesting::GetBlockImpl(unsigned long pos,
+                                       unsigned char* pBuf,
+                                       unsigned long size) {
+  memcpy(pBuf, file_contents_.get() + pos, size);
+  return size;
+}
+
+// static
+int FileAccessForTesting::SGetBlock(void* param,
+                                    unsigned long pos,
+                                    unsigned char* pBuf,
+                                    unsigned long size) {
+  auto* file_access = static_cast<FileAccessForTesting*>(param);
+  return file_access->GetBlockImpl(pos, pBuf, size);
+}
diff --git a/testing/utils/file_util.h b/testing/utils/file_util.h
new file mode 100644
index 0000000..352b882
--- /dev/null
+++ b/testing/utils/file_util.h
@@ -0,0 +1,37 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_UTILS_FILE_UTIL_H_
+#define TESTING_UTILS_FILE_UTIL_H_
+
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include "public/fpdfview.h"
+#include "testing/free_deleter.h"
+
+// Reads the entire contents of a file into a newly alloc'd buffer.
+std::unique_ptr<char, pdfium::FreeDeleter> GetFileContents(const char* filename,
+                                                           size_t* retlen);
+
+// Use an ordinary file anywhere a FPDF_FILEACCESS is required.
+class FileAccessForTesting final : public FPDF_FILEACCESS {
+ public:
+  explicit FileAccessForTesting(const std::string& file_name);
+
+ private:
+  static int SGetBlock(void* param,
+                       unsigned long pos,
+                       unsigned char* pBuf,
+                       unsigned long size);
+
+  int GetBlockImpl(unsigned long pos, unsigned char* pBuf, unsigned long size);
+
+  size_t file_length_;
+  std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
+};
+
+#endif  // TESTING_UTILS_FILE_UTIL_H_
diff --git a/testing/utils/hash.cpp b/testing/utils/hash.cpp
new file mode 100644
index 0000000..3170de2
--- /dev/null
+++ b/testing/utils/hash.cpp
@@ -0,0 +1,25 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/utils/hash.h"
+
+#include "core/fdrm/fx_crypt.h"
+
+std::string CryptToBase16(const uint8_t* digest) {
+  static char const zEncode[] = "0123456789abcdef";
+  std::string ret;
+  ret.resize(32);
+  for (int i = 0, j = 0; i < 16; i++, j += 2) {
+    uint8_t a = digest[i];
+    ret[j] = zEncode[(a >> 4) & 0xf];
+    ret[j + 1] = zEncode[a & 0xf];
+  }
+  return ret;
+}
+
+std::string GenerateMD5Base16(const uint8_t* data, uint32_t size) {
+  uint8_t digest[16];
+  CRYPT_MD5Generate({data, size}, digest);
+  return CryptToBase16(digest);
+}
diff --git a/testing/utils/hash.h b/testing/utils/hash.h
new file mode 100644
index 0000000..0e55165
--- /dev/null
+++ b/testing/utils/hash.h
@@ -0,0 +1,13 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_UTILS_HASH_H_
+#define TESTING_UTILS_HASH_H_
+
+#include <string>
+
+std::string CryptToBase16(const uint8_t* digest);
+std::string GenerateMD5Base16(const uint8_t* data, uint32_t size);
+
+#endif  // TESTING_UTILS_HASH_H_
diff --git a/testing/utils/path_service.cpp b/testing/utils/path_service.cpp
index 1030c2b..5e1ce39 100644
--- a/testing/utils/path_service.cpp
+++ b/testing/utils/path_service.cpp
@@ -8,8 +8,10 @@
 #include <Windows.h>
 #elif defined(__APPLE__)
 #include <mach-o/dyld.h>
+#include <sys/stat.h>
 #else  // Linux
 #include <linux/limits.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #endif  // _WIN32
 
@@ -17,6 +19,39 @@
 
 #include "core/fxcrt/fx_system.h"
 
+namespace {
+
+#if defined(__APPLE__) || (defined(ANDROID) && __ANDROID_API__ < 21)
+using stat_wrapper_t = struct stat;
+
+int CallStat(const char* path, stat_wrapper_t* sb) {
+  return stat(path, sb);
+}
+#elif !_WIN32
+using stat_wrapper_t = struct stat64;
+
+int CallStat(const char* path, stat_wrapper_t* sb) {
+  return stat64(path, sb);
+}
+#endif
+
+}  // namespace
+
+// static
+bool PathService::DirectoryExists(const std::string& path) {
+#ifdef _WIN32
+  DWORD fileattr = GetFileAttributesA(path.c_str());
+  if (fileattr != INVALID_FILE_ATTRIBUTES)
+    return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
+  return false;
+#else
+  stat_wrapper_t file_info;
+  if (CallStat(path.c_str(), &file_info) != 0)
+    return false;
+  return S_ISDIR(file_info.st_mode);
+#endif
+}
+
 // static
 bool PathService::EndsWithSeparator(const std::string& path) {
   return path.size() > 1 && path[path.size() - 1] == PATH_SEPARATOR;
@@ -88,10 +123,30 @@
 
   if (!EndsWithSeparator(*path))
     path->push_back(PATH_SEPARATOR);
-  path->append("testing");
-  path->push_back(PATH_SEPARATOR);
-  path->append("resources");
-  return true;
+
+  std::string potential_path = *path;
+  potential_path.append("testing");
+  potential_path.push_back(PATH_SEPARATOR);
+  potential_path.append("resources");
+  if (PathService::DirectoryExists(potential_path)) {
+    *path = potential_path;
+    return true;
+  }
+
+  potential_path = *path;
+  potential_path.append("third_party");
+  potential_path.push_back(PATH_SEPARATOR);
+  potential_path.append("pdfium");
+  potential_path.push_back(PATH_SEPARATOR);
+  potential_path.append("testing");
+  potential_path.push_back(PATH_SEPARATOR);
+  potential_path.append("resources");
+  if (PathService::DirectoryExists(potential_path)) {
+    *path = potential_path;
+    return true;
+  }
+
+  return false;
 }
 
 // static
diff --git a/testing/utils/path_service.h b/testing/utils/path_service.h
index 96fd69e..63df808 100644
--- a/testing/utils/path_service.h
+++ b/testing/utils/path_service.h
@@ -16,6 +16,9 @@
 // Get the various file directory and path information.
 class PathService {
  public:
+  // Return true when the path is a directory that exists.
+  static bool DirectoryExists(const std::string& path);
+
   // Return true when the path ends with a path separator.
   static bool EndsWithSeparator(const std::string& path);
 
@@ -23,12 +26,13 @@
   static bool GetExecutableDir(std::string* path);
 
   // Retrieve the root directory of the source tree.
-  // Assume executables always run from out/<Debug|Release>/, the source
+  // Assume executables always run from out/<build_dir_name>/, so the source
   // directory is two levels above the executable directory.
   static bool GetSourceDir(std::string* path);
 
   // Retrieve the test data directory where test files are stored.
-  // Currently, the test dir is under <source_dir>/testing/resources/.
+  // Try <source_dir>/testing/resources/ first. If it does not exist, try
+  // checking <source_dir>/third_party/pdfium/testing/resources/.
   static bool GetTestDataDir(std::string* path);
 
   // Get the full path for a test file under the test data directory.
diff --git a/testing/v8_initializer.cpp b/testing/v8_initializer.cpp
new file mode 100644
index 0000000..8564b2e
--- /dev/null
+++ b/testing/v8_initializer.cpp
@@ -0,0 +1,95 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/v8_initializer.h"
+
+#include <cstring>
+
+#include "public/fpdfview.h"
+#include "testing/utils/file_util.h"
+#include "testing/utils/path_service.h"
+#include "v8/include/libplatform/libplatform.h"
+#include "v8/include/v8.h"
+
+namespace {
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+// Returns the full path for an external V8 data file based on either
+// the currect exectuable path or an explicit override.
+std::string GetFullPathForSnapshotFile(const std::string& exe_path,
+                                       const std::string& bin_dir,
+                                       const std::string& filename) {
+  std::string result;
+  if (!bin_dir.empty()) {
+    result = bin_dir;
+    if (*bin_dir.rbegin() != PATH_SEPARATOR) {
+      result += PATH_SEPARATOR;
+    }
+  } else if (!exe_path.empty()) {
+    size_t last_separator = exe_path.rfind(PATH_SEPARATOR);
+    if (last_separator != std::string::npos) {
+      result = exe_path.substr(0, last_separator + 1);
+    }
+  }
+  result += filename;
+  return result;
+}
+
+bool GetExternalData(const std::string& exe_path,
+                     const std::string& bin_dir,
+                     const std::string& filename,
+                     v8::StartupData* result_data) {
+  std::string full_path =
+      GetFullPathForSnapshotFile(exe_path, bin_dir, filename);
+  size_t data_length = 0;
+  std::unique_ptr<char, pdfium::FreeDeleter> data_buffer =
+      GetFileContents(full_path.c_str(), &data_length);
+  if (!data_buffer)
+    return false;
+
+  result_data->data = data_buffer.release();
+  result_data->raw_size = data_length;
+  return true;
+}
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+
+std::unique_ptr<v8::Platform> InitializeV8Common(const std::string& exe_path) {
+  v8::V8::InitializeICUDefaultLocation(exe_path.c_str());
+
+  std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
+  v8::V8::InitializePlatform(platform.get());
+
+  const char* recommended_v8_flags = FPDF_GetRecommendedV8Flags();
+  v8::V8::SetFlagsFromString(recommended_v8_flags);
+
+  // By enabling predictable mode, V8 won't post any background tasks.
+  // By enabling GC, it makes it easier to chase use-after-free.
+  static const char kAdditionalV8Flags[] = "--predictable --expose-gc";
+  v8::V8::SetFlagsFromString(kAdditionalV8Flags);
+
+  v8::V8::Initialize();
+  return platform;
+}
+
+}  // namespace
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+std::unique_ptr<v8::Platform> InitializeV8ForPDFiumWithStartupData(
+    const std::string& exe_path,
+    const std::string& bin_dir,
+    v8::StartupData* snapshot_blob) {
+  std::unique_ptr<v8::Platform> platform = InitializeV8Common(exe_path);
+  if (snapshot_blob) {
+    if (!GetExternalData(exe_path, bin_dir, "snapshot_blob.bin", snapshot_blob))
+      return nullptr;
+    v8::V8::SetSnapshotDataBlob(snapshot_blob);
+  }
+  return platform;
+}
+#else   // V8_USE_EXTERNAL_STARTUP_DATA
+std::unique_ptr<v8::Platform> InitializeV8ForPDFium(
+    const std::string& exe_path) {
+  return InitializeV8Common(exe_path);
+}
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
diff --git a/testing/v8_initializer.h b/testing/v8_initializer.h
new file mode 100644
index 0000000..bd2301c
--- /dev/null
+++ b/testing/v8_initializer.h
@@ -0,0 +1,33 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_V8_INITIALIZER_H_
+#define TESTING_V8_INITIALIZER_H_
+
+#include <memory>
+#include <string>
+
+#ifndef PDF_ENABLE_V8
+#error "V8 must be enabled"
+#endif
+
+namespace v8 {
+class Platform;
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+class StartupData;
+#endif
+}  // namespace v8
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+// |snapshot_blob| is an optional out parameter.
+std::unique_ptr<v8::Platform> InitializeV8ForPDFiumWithStartupData(
+    const std::string& exe_path,
+    const std::string& bin_dir,
+    v8::StartupData* snapshot_blob);
+#else
+std::unique_ptr<v8::Platform> InitializeV8ForPDFium(
+    const std::string& exe_path);
+#endif
+
+#endif  // TESTING_V8_INITIALIZER_H_
diff --git a/testing/xfa_js_embedder_test.cpp b/testing/xfa_js_embedder_test.cpp
index d007c87..2f9cc67 100644
--- a/testing/xfa_js_embedder_test.cpp
+++ b/testing/xfa_js_embedder_test.cpp
@@ -6,15 +6,17 @@
 
 #include <string>
 
+#include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
-#include "fpdfsdk/fsdk_define.h"
-#include "fxjs/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/cxfa_ffapp.h"
 
 XFAJSEmbedderTest::XFAJSEmbedderTest()
-    : array_buffer_allocator_(pdfium::MakeUnique<FXJS_ArrayBufferAllocator>()) {
-}
+    : array_buffer_allocator_(
+          pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>()) {}
 
 XFAJSEmbedderTest::~XFAJSEmbedderTest() {}
 
@@ -26,9 +28,13 @@
 
   EmbedderTest::SetExternalIsolate(isolate_);
   EmbedderTest::SetUp();
+
+  CXFA_FFApp::SkipFontLoadForTesting(true);
 }
 
 void XFAJSEmbedderTest::TearDown() {
+  CXFA_FFApp::SkipFontLoadForTesting(false);
+
   value_.reset();
   script_context_ = nullptr;
 
@@ -38,29 +44,40 @@
   isolate_ = nullptr;
 }
 
-CXFA_Document* XFAJSEmbedderTest::GetXFADocument() {
-  return UnderlyingFromFPDFDocument(document())->GetXFADoc()->GetXFADoc();
+CXFA_Document* XFAJSEmbedderTest::GetXFADocument() const {
+  auto* pDoc = CPDFDocumentFromFPDFDocument(document());
+  if (!pDoc)
+    return nullptr;
+
+  auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
+  if (!pContext)
+    return nullptr;
+
+  return pContext->GetXFADoc()->GetXFADoc();
 }
 
-bool XFAJSEmbedderTest::OpenDocumentWithOptions(const std::string& filename,
-                                                const char* password,
-                                                bool must_linearize) {
-  if (!EmbedderTest::OpenDocumentWithOptions(filename, password,
-                                             must_linearize))
+bool XFAJSEmbedderTest::OpenDocumentWithOptions(
+    const std::string& filename,
+    const char* password,
+    LinearizeOption linearize_option,
+    JavaScriptOption javascript_option) {
+  // JS required for XFA.
+  ASSERT(javascript_option == JavaScriptOption::kEnableJavaScript);
+  if (!EmbedderTest::OpenDocumentWithOptions(
+          filename, password, linearize_option, javascript_option)) {
     return false;
-
+  }
   script_context_ = GetXFADocument()->GetScriptContext();
   return true;
 }
 
-bool XFAJSEmbedderTest::Execute(const ByteStringView& input) {
-  if (ExecuteHelper(input)) {
+bool XFAJSEmbedderTest::Execute(ByteStringView input) {
+  if (ExecuteHelper(input))
     return true;
-  }
 
   CFXJSE_Value msg(GetIsolate());
   value_->GetObjectPropertyByIdx(1, &msg);
-  fprintf(stderr, "JS: %.*s\n", static_cast<int>(input.GetLength()),
+  fprintf(stderr, "FormCalc: %.*s\n", static_cast<int>(input.GetLength()),
           input.unterminated_c_str());
   // If the parsing of the input fails, then v8 will not run, so there will be
   // no value here to print.
@@ -69,11 +86,11 @@
   return false;
 }
 
-bool XFAJSEmbedderTest::ExecuteSilenceFailure(const ByteStringView& input) {
+bool XFAJSEmbedderTest::ExecuteSilenceFailure(ByteStringView input) {
   return ExecuteHelper(input);
 }
 
-bool XFAJSEmbedderTest::ExecuteHelper(const ByteStringView& input) {
+bool XFAJSEmbedderTest::ExecuteHelper(ByteStringView input) {
   value_ = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
   return script_context_->RunScript(CXFA_Script::Type::Formcalc,
                                     WideString::FromUTF8(input).AsStringView(),
diff --git a/testing/xfa_js_embedder_test.h b/testing/xfa_js_embedder_test.h
index 5f45496..412ffd9 100644
--- a/testing/xfa_js_embedder_test.h
+++ b/testing/xfa_js_embedder_test.h
@@ -8,14 +8,13 @@
 #include <memory>
 #include <string>
 
-#include "fxjs/cfxjse_value.h"
-#include "fxjs/fxjs_v8.h"
 #include "testing/embedder_test.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
 
 class CFXJSE_Engine;
+class CFXJSE_Value;
+class CFX_V8ArrayBufferAllocator;
 
 class XFAJSEmbedderTest : public EmbedderTest {
  public:
@@ -27,23 +26,25 @@
   void TearDown() override;
   bool OpenDocumentWithOptions(const std::string& filename,
                                const char* password,
-                               bool must_linearize) override;
+                               LinearizeOption linearize_option,
+                               JavaScriptOption javascript_option) override;
 
   v8::Isolate* GetIsolate() const { return isolate_; }
-  CXFA_Document* GetXFADocument();
+  CXFA_Document* GetXFADocument() const;
 
-  bool Execute(const ByteStringView& input);
-  bool ExecuteSilenceFailure(const ByteStringView& input);
+  bool Execute(ByteStringView input);
+  bool ExecuteSilenceFailure(ByteStringView input);
 
+  CFXJSE_Engine* GetScriptContext() const { return script_context_; }
   CFXJSE_Value* GetValue() const { return value_.get(); }
 
  private:
-  std::unique_ptr<FXJS_ArrayBufferAllocator> array_buffer_allocator_;
+  std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
   std::unique_ptr<CFXJSE_Value> value_;
   v8::Isolate* isolate_ = nullptr;
   CFXJSE_Engine* script_context_ = nullptr;
 
-  bool ExecuteHelper(const ByteStringView& input);
+  bool ExecuteHelper(ByteStringView input);
 };
 
 #endif  // TESTING_XFA_JS_EMBEDDER_TEST_H_
diff --git a/testing/xfa_unit_test_support.cpp b/testing/xfa_unit_test_support.cpp
new file mode 100644
index 0000000..8cc2d6c
--- /dev/null
+++ b/testing/xfa_unit_test_support.cpp
@@ -0,0 +1,55 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/xfa_unit_test_support.h"
+
+#include <memory>
+#include <utility>
+
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+
+namespace {
+
+// The loading time of the CFGAS_FontMgr is linear in the number of times it is
+// loaded. So, if a test suite has a lot of tests that need a font manager they
+// can end up executing very, very slowly.
+class XFATestEnvironment final : public testing::Environment {
+ public:
+  // testing::Environment:
+  void SetUp() override {
+    auto font_mgr = pdfium::MakeUnique<CFGAS_FontMgr>();
+    if (font_mgr->EnumFonts())
+      font_mgr_ = std::move(font_mgr);
+  }
+  void TearDown() override { font_mgr_.reset(); }
+
+  CFGAS_FontMgr* FontManager() const { return font_mgr_.get(); }
+
+ private:
+  std::unique_ptr<CFGAS_FontMgr> font_mgr_;
+};
+
+XFATestEnvironment* g_env = nullptr;
+
+}  // namespace
+
+void InitializeXFATestEnvironment() {
+  // |g_env| will be deleted by gtest.
+  g_env = new XFATestEnvironment();
+  AddGlobalTestEnvironment(g_env);
+
+  // TODO(dsinclair): This font loading is slow. We should make a test font
+  // loader which loads up a single font we use in all tests.
+  CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(
+      SystemFontInfoIface::CreateDefault(nullptr));
+}
+
+CFGAS_FontMgr* GetGlobalFontManager() {
+  return g_env->FontManager();
+}
diff --git a/testing/xfa_unit_test_support.h b/testing/xfa_unit_test_support.h
new file mode 100644
index 0000000..0a54778
--- /dev/null
+++ b/testing/xfa_unit_test_support.h
@@ -0,0 +1,18 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_XFA_UNIT_TEST_SUPPORT_H_
+#define TESTING_XFA_UNIT_TEST_SUPPORT_H_
+
+#ifndef PDF_ENABLE_XFA
+#error "XFA must be enabled"
+#endif
+
+class CFGAS_FontMgr;
+
+void InitializeXFATestEnvironment();
+
+CFGAS_FontMgr* GetGlobalFontManager();
+
+#endif  // TESTING_XFA_UNIT_TEST_SUPPORT_H_
diff --git a/third_party/Android.bp b/third_party/Android.bp
index 2d294e0..824544e 100644
--- a/third_party/Android.bp
+++ b/third_party/Android.bp
@@ -3,12 +3,98 @@
     defaults: [
         "pdfium-common"
     ],
+    visibility: ["//external/pdfium:__subpackages__"],
 }
 
-build = [
-    "pdfiumfx_agg.bp",
-    "pdfiumbigint.bp",
-    "pdfiumlcms2.bp",
-    "pdfiumpdfiumbase.bp",
-    "pdfiumfx_libopenjpeg.bp",
-]
+cc_library_static {
+    name: "libpdfium-third_party-base",
+    defaults: ["pdfium-third-party"],
+    visibility: ["//external/pdfium:__subpackages__"],
+
+    arch: {
+        arm: {
+            cflags: [
+                "-DARCH_CPU_32_BITS",
+                "-DARCH_CPU_ARMEL",
+            ],
+        },
+        arm64: {
+            cflags: [
+                "-DARCH_CPU_64_BITS",
+                "-DARCH_CPU_ARM64",
+            ],
+        },
+        x86: {
+            cflags: [
+                "-DARCH_CPU_32_BITS",
+                "-DARCH_CPU_X86",
+            ],
+        },
+        x86_64: {
+            cflags: [
+                "-DARCH_CPU_64_BITS",
+                "-DARCH_CPU_X86_64",
+            ],
+        },
+    },
+
+    srcs: [
+        "base/debug/*.cc",
+        "base/allocator/partition_allocator/*.cc",
+    ],
+}
+
+cc_library_static {
+    name: "libpdfium-lcms2",
+    defaults: ["pdfium-third-party"],
+
+    cflags: [
+      // cmslut.cc is sloppy with aggregate initialization. Version 2.7 of this
+      // library doesn't appear to have this problem.
+      "-Wno-missing-braces",
+
+      // FindPrev() in cmsplugin.c is unused.
+      "-Wno-unused-function",
+    ],
+
+    srcs: [
+        "lcms/src/*.c",
+    ],
+}
+
+cc_library_static {
+    name: "libpdfium-libopenjpeg2",
+    defaults: ["pdfium-third-party"],
+
+    exclude_srcs: [
+        "libopenjpeg20/t1_generate_luts.c",
+    ],
+
+    srcs: [
+        "libopenjpeg20/*.c",
+    ],
+}
+
+cc_library_static {
+    name: "libpdfium-agg",
+    defaults: ["pdfium-third-party"],
+
+    cflags: [
+        // calc_butt_cap() in agg_vcgen_stroke.cpp is unused.
+        "-Wno-unused-function",
+    ],
+
+    srcs: [
+        "agg23/*.cpp",
+    ]
+}
+
+cc_library_static {
+    name: "libpdfium-skia_shared",
+    defaults: ["pdfium-third-party"],
+
+    srcs: [
+        "skia_shared/*.cpp",
+    ],
+}
+
diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn
index 1cfee77..887dacb 100644
--- a/third_party/BUILD.gn
+++ b/third_party/BUILD.gn
@@ -3,15 +3,14 @@
 # found in the LICENSE file.
 
 import("//build/config/arm.gni")
-import("//build/config/jumbo.gni")
 import("//build/config/linux/pkg_config.gni")
 import("//build_overrides/build.gni")
 import("../pdfium.gni")
 
 group("third_party") {
   deps = [
-    ":bigint",
     ":pdfium_base",
+    ":skia_shared",
   ]
   if (pdf_bundle_freetype) {
     deps += [ ":fx_freetype" ]
@@ -19,27 +18,32 @@
 }
 
 config("pdfium_third_party_config") {
-  configs = [ "..:pdfium_common_config" ]
+  configs = [
+    "..:pdfium_common_config",
+    "..:pdfium_public_config",
+  ]
 }
 
-static_library("bigint") {
-  configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [
-    "//build/config/compiler:no_chromium_code",
-    ":pdfium_third_party_config",
-  ]
-  sources = [
-    "bigint/BigInteger.cc",
-    "bigint/BigInteger.hh",
-    "bigint/BigIntegerLibrary.hh",
-    "bigint/BigIntegerUtils.cc",
-    "bigint/BigIntegerUtils.hh",
-    "bigint/BigUnsigned.cc",
-    "bigint/BigUnsigned.hh",
-    "bigint/BigUnsignedInABase.cc",
-    "bigint/BigUnsignedInABase.hh",
-    "bigint/NumberlikeArray.hh",
-  ]
+if (pdf_enable_xfa) {
+  source_set("bigint") {
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      ":pdfium_third_party_config",
+    ]
+    sources = [
+      "bigint/BigInteger.cc",
+      "bigint/BigInteger.hh",
+      "bigint/BigIntegerLibrary.hh",
+      "bigint/BigIntegerUtils.cc",
+      "bigint/BigIntegerUtils.hh",
+      "bigint/BigUnsigned.cc",
+      "bigint/BigUnsigned.hh",
+      "bigint/BigUnsignedInABase.cc",
+      "bigint/BigUnsignedInABase.hh",
+      "bigint/NumberlikeArray.hh",
+    ]
+  }
 }
 
 if (pdf_bundle_freetype) {
@@ -76,7 +80,7 @@
 
   # Tests may link against this even if the production library doesn't,
   # so it needs to be separate from it.
-  jumbo_static_library("fx_freetype") {
+  source_set("fx_freetype") {
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       "//build/config/compiler:no_chromium_code",
@@ -93,56 +97,79 @@
       "//third_party/freetype/include/pstables.h",
       "//third_party/freetype/src/include/freetype/config/ftconfig.h",
       "//third_party/freetype/src/include/freetype/config/ftheader.h",
+      "//third_party/freetype/src/include/freetype/config/ftmodule.h",
+      "//third_party/freetype/src/include/freetype/config/ftoption.h",
       "//third_party/freetype/src/include/freetype/config/ftstdlib.h",
       "//third_party/freetype/src/include/freetype/freetype.h",
       "//third_party/freetype/src/include/freetype/ftadvanc.h",
+      "//third_party/freetype/src/include/freetype/ftbbox.h",
+      "//third_party/freetype/src/include/freetype/ftbdf.h",
       "//third_party/freetype/src/include/freetype/ftbitmap.h",
-      "//third_party/freetype/src/include/freetype/ftcffdrv.h",
+      "//third_party/freetype/src/include/freetype/ftbzip2.h",
+      "//third_party/freetype/src/include/freetype/ftcache.h",
+      "//third_party/freetype/src/include/freetype/ftchapters.h",
+      "//third_party/freetype/src/include/freetype/ftcid.h",
+      "//third_party/freetype/src/include/freetype/ftcolor.h",
+      "//third_party/freetype/src/include/freetype/ftdriver.h",
       "//third_party/freetype/src/include/freetype/fterrdef.h",
       "//third_party/freetype/src/include/freetype/fterrors.h",
       "//third_party/freetype/src/include/freetype/ftfntfmt.h",
+      "//third_party/freetype/src/include/freetype/ftgasp.h",
       "//third_party/freetype/src/include/freetype/ftglyph.h",
+      "//third_party/freetype/src/include/freetype/ftgxval.h",
       "//third_party/freetype/src/include/freetype/ftgzip.h",
       "//third_party/freetype/src/include/freetype/ftimage.h",
       "//third_party/freetype/src/include/freetype/ftincrem.h",
       "//third_party/freetype/src/include/freetype/ftlcdfil.h",
       "//third_party/freetype/src/include/freetype/ftlist.h",
+      "//third_party/freetype/src/include/freetype/ftlzw.h",
+      "//third_party/freetype/src/include/freetype/ftmac.h",
       "//third_party/freetype/src/include/freetype/ftmm.h",
       "//third_party/freetype/src/include/freetype/ftmodapi.h",
-      "//third_party/freetype/src/include/freetype/ftmoderror.h",
-      "//third_party/freetype/src/include/freetype/ftobjs.h",
+      "//third_party/freetype/src/include/freetype/ftmoderr.h",
       "//third_party/freetype/src/include/freetype/ftotval.h",
       "//third_party/freetype/src/include/freetype/ftoutln.h",
+      "//third_party/freetype/src/include/freetype/ftparams.h",
+      "//third_party/freetype/src/include/freetype/ftpfr.h",
       "//third_party/freetype/src/include/freetype/ftrender.h",
       "//third_party/freetype/src/include/freetype/ftsizes.h",
       "//third_party/freetype/src/include/freetype/ftsnames.h",
-      "//third_party/freetype/src/include/freetype/ftstream.h",
+      "//third_party/freetype/src/include/freetype/ftstroke.h",
+      "//third_party/freetype/src/include/freetype/ftsynth.h",
       "//third_party/freetype/src/include/freetype/ftsystem.h",
       "//third_party/freetype/src/include/freetype/fttrigon.h",
-      "//third_party/freetype/src/include/freetype/ftttdrv.h",
       "//third_party/freetype/src/include/freetype/fttypes.h",
+      "//third_party/freetype/src/include/freetype/ftwinfnt.h",
       "//third_party/freetype/src/include/freetype/internal/autohint.h",
+      "//third_party/freetype/src/include/freetype/internal/cffotypes.h",
+      "//third_party/freetype/src/include/freetype/internal/cfftypes.h",
       "//third_party/freetype/src/include/freetype/internal/ftcalc.h",
       "//third_party/freetype/src/include/freetype/internal/ftdebug.h",
-      "//third_party/freetype/src/include/freetype/internal/ftdriver.h",
+      "//third_party/freetype/src/include/freetype/internal/ftdrv.h",
       "//third_party/freetype/src/include/freetype/internal/ftgloadr.h",
       "//third_party/freetype/src/include/freetype/internal/fthash.h",
       "//third_party/freetype/src/include/freetype/internal/ftmemory.h",
       "//third_party/freetype/src/include/freetype/internal/ftobjs.h",
-      "//third_party/freetype/src/include/freetype/internal/ftpic.h",
+      "//third_party/freetype/src/include/freetype/internal/ftpsprop.h",
       "//third_party/freetype/src/include/freetype/internal/ftrfork.h",
       "//third_party/freetype/src/include/freetype/internal/ftserv.h",
       "//third_party/freetype/src/include/freetype/internal/ftstream.h",
+      "//third_party/freetype/src/include/freetype/internal/fttrace.h",
       "//third_party/freetype/src/include/freetype/internal/ftvalid.h",
       "//third_party/freetype/src/include/freetype/internal/internal.h",
       "//third_party/freetype/src/include/freetype/internal/psaux.h",
       "//third_party/freetype/src/include/freetype/internal/pshints.h",
+      "//third_party/freetype/src/include/freetype/internal/services/svbdf.h",
+      "//third_party/freetype/src/include/freetype/internal/services/svcfftl.h",
       "//third_party/freetype/src/include/freetype/internal/services/svcid.h",
       "//third_party/freetype/src/include/freetype/internal/services/svfntfmt.h",
       "//third_party/freetype/src/include/freetype/internal/services/svgldict.h",
+      "//third_party/freetype/src/include/freetype/internal/services/svgxval.h",
       "//third_party/freetype/src/include/freetype/internal/services/svkern.h",
       "//third_party/freetype/src/include/freetype/internal/services/svmetric.h",
       "//third_party/freetype/src/include/freetype/internal/services/svmm.h",
+      "//third_party/freetype/src/include/freetype/internal/services/svotval.h",
+      "//third_party/freetype/src/include/freetype/internal/services/svpfr.h",
       "//third_party/freetype/src/include/freetype/internal/services/svpostnm.h",
       "//third_party/freetype/src/include/freetype/internal/services/svprop.h",
       "//third_party/freetype/src/include/freetype/internal/services/svpscmap.h",
@@ -151,6 +178,7 @@
       "//third_party/freetype/src/include/freetype/internal/services/svttcmap.h",
       "//third_party/freetype/src/include/freetype/internal/services/svtteng.h",
       "//third_party/freetype/src/include/freetype/internal/services/svttglyf.h",
+      "//third_party/freetype/src/include/freetype/internal/services/svwinfnt.h",
       "//third_party/freetype/src/include/freetype/internal/sfnt.h",
       "//third_party/freetype/src/include/freetype/internal/t1types.h",
       "//third_party/freetype/src/include/freetype/internal/tttypes.h",
@@ -162,15 +190,13 @@
       "//third_party/freetype/src/src/base/ftbase.c",
       "//third_party/freetype/src/src/base/ftbase.h",
       "//third_party/freetype/src/src/base/ftbitmap.c",
-      "//third_party/freetype/src/src/base/ftfntfmt.c",
+      "//third_party/freetype/src/src/base/ftdebug.c",
       "//third_party/freetype/src/src/base/ftglyph.c",
       "//third_party/freetype/src/src/base/ftinit.c",
-      "//third_party/freetype/src/src/base/ftlcdfil.c",
       "//third_party/freetype/src/src/base/ftmm.c",
       "//third_party/freetype/src/src/base/ftsystem.c",
       "//third_party/freetype/src/src/cff/cff.c",
       "//third_party/freetype/src/src/cff/cffobjs.h",
-      "//third_party/freetype/src/src/cff/cfftypes.h",
       "//third_party/freetype/src/src/cid/type1cid.c",
       "//third_party/freetype/src/src/psaux/psaux.c",
       "//third_party/freetype/src/src/pshinter/pshinter.c",
@@ -200,7 +226,7 @@
     }
   }
 
-  jumbo_static_library("fx_agg") {
+  source_set("fx_agg") {
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       "//build/config/compiler:no_chromium_code",
@@ -226,9 +252,7 @@
       "agg23/agg_vcgen_dash.cpp",
       "agg23/agg_vcgen_stroke.cpp",
     ]
-    deps = [
-      "..:fxcrt",
-    ]
+    deps = [ "../core/fxcrt" ]
   }
 }
 
@@ -246,7 +270,7 @@
   }
 }
 
-static_library("fx_lcms2") {
+source_set("fx_lcms2") {
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     "//build/config/compiler:no_chromium_code",
@@ -286,6 +310,7 @@
     "lcms/src/cmswtpnt.c",
     "lcms/src/cmsxform.c",
   ]
+  deps = [ "../core/fxcrt" ]
 }
 
 if (!build_with_chromium) {
@@ -314,14 +339,10 @@
     if (use_system_libjpeg) {
       public_configs = [ ":system_libjpeg_config" ]
     } else if (use_libjpeg_turbo) {
-      public_deps = [
-        "//third_party/libjpeg_turbo:libjpeg",
-      ]
+      public_deps = [ "//third_party/libjpeg_turbo:libjpeg" ]
       public_configs = [ ":libjpeg_turbo_config" ]
     } else {
-      public_deps = [
-        "//third_party/libjpeg:libjpeg",
-      ]
+      public_deps = [ "//third_party/libjpeg:libjpeg" ]
     }
   }
 }
@@ -335,9 +356,7 @@
   if (use_system_zlib) {
     public_configs = [ ":system_zlib_config" ]
   } else {
-    public_deps = [
-      "//third_party/zlib",
-    ]
+    public_deps = [ "//third_party/zlib" ]
   }
 }
 
@@ -351,9 +370,21 @@
   if (use_system_lcms2) {
     public_configs = [ ":lcms2_from_pkgconfig" ]
   } else {
-    public_deps = [
-      ":fx_lcms2",
-    ]
+    public_deps = [ ":fx_lcms2" ]
+  }
+}
+
+if (use_system_libopenjpeg2) {
+  pkg_config("libopenjpeg2_from_pkgconfig") {
+    defines = [ "USE_SYSTEM_LIBOPENJPEG2" ]
+    packages = [ "libopenjp2" ]
+  }
+}
+group("libopenjpeg2") {
+  if (use_system_libopenjpeg2) {
+    public_configs = [ ":libopenjpeg2_from_pkgconfig" ]
+  } else {
+    public_deps = [ ":fx_libopenjpeg" ]
   }
 }
 
@@ -367,10 +398,11 @@
   }
 }
 
-static_library("fx_libopenjpeg") {
+source_set("fx_libopenjpeg") {
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     "//build/config/compiler:no_chromium_code",
+    "//build/config/sanitizers:cfi_icall_generalize_pointers",
     ":pdfium_third_party_config",
 
     # Must be after no_chromium_code for warning flags to be ordered correctly.
@@ -410,13 +442,11 @@
   if (use_system_libpng) {
     public_configs = [ ":system_libpng_config" ]
   } else {
-    public_deps = [
-      ":fx_lpng",
-    ]
+    public_deps = [ ":fx_lpng" ]
   }
 }
 
-jumbo_static_library("fx_lpng") {
+source_set("fx_lpng") {
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     "//build/config/compiler:no_chromium_code",
@@ -450,20 +480,19 @@
 
   defines = []
   cflags = []
-  deps = [
-    ":zlib",
-  ]
+  deps = [ ":zlib" ]
 
   if (current_cpu == "x86" || current_cpu == "x64") {
     sources += [
-      "libpng16/contrib/intel/filter_sse2_intrinsics.c",
-      "libpng16/contrib/intel/intel_init.c",
+      "libpng16/intel/filter_sse2_intrinsics.c",
+      "libpng16/intel/intel_init.c",
     ]
     defines += [ "PNG_INTEL_SSE_OPT=1" ]
   } else if ((current_cpu == "arm" || current_cpu == "arm64") && arm_use_neon) {
     sources += [
       "libpng16/arm/arm_init.c",
       "libpng16/arm/filter_neon_intrinsics.c",
+      "libpng16/arm/palette_neon_intrinsics.c",
     ]
     defines += [
       "PNG_ARM_NEON_OPT=2",
@@ -477,8 +506,8 @@
   }
 }
 
-if (pdf_enable_xfa) {
-  static_library("fx_tiff") {
+if (pdf_enable_xfa_tiff) {
+  source_set("fx_tiff") {
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       "//build/config/compiler:no_chromium_code",
@@ -491,6 +520,7 @@
     }
     deps = [
       ":zlib",
+      "../core/fxcrt",
       "//third_party:jpeg",
     ]
     sources = [
@@ -535,7 +565,7 @@
   }
 }
 
-jumbo_source_set("pdfium_base") {
+source_set("pdfium_base") {
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     "//build/config/compiler:no_chromium_code",
@@ -545,26 +575,86 @@
     "base/allocator/partition_allocator/address_space_randomization.cc",
     "base/allocator/partition_allocator/address_space_randomization.h",
     "base/allocator/partition_allocator/oom.h",
+    "base/allocator/partition_allocator/oom_callback.cc",
+    "base/allocator/partition_allocator/oom_callback.h",
     "base/allocator/partition_allocator/page_allocator.cc",
     "base/allocator/partition_allocator/page_allocator.h",
+    "base/allocator/partition_allocator/page_allocator_constants.h",
+    "base/allocator/partition_allocator/page_allocator_internal.h",
+    "base/allocator/partition_allocator/page_allocator_internals_posix.h",
+    "base/allocator/partition_allocator/page_allocator_internals_win.h",
     "base/allocator/partition_allocator/partition_alloc.cc",
     "base/allocator/partition_allocator/partition_alloc.h",
+    "base/allocator/partition_allocator/partition_alloc_constants.h",
+    "base/allocator/partition_allocator/partition_bucket.cc",
+    "base/allocator/partition_allocator/partition_bucket.h",
+    "base/allocator/partition_allocator/partition_cookie.h",
+    "base/allocator/partition_allocator/partition_direct_map_extent.h",
+    "base/allocator/partition_allocator/partition_freelist_entry.h",
+    "base/allocator/partition_allocator/partition_oom.cc",
+    "base/allocator/partition_allocator/partition_oom.h",
+    "base/allocator/partition_allocator/partition_page.cc",
+    "base/allocator/partition_allocator/partition_page.h",
+    "base/allocator/partition_allocator/partition_root_base.cc",
+    "base/allocator/partition_allocator/partition_root_base.h",
+    "base/allocator/partition_allocator/random.cc",
+    "base/allocator/partition_allocator/random.h",
     "base/allocator/partition_allocator/spin_lock.cc",
     "base/allocator/partition_allocator/spin_lock.h",
     "base/base_export.h",
     "base/bits.h",
     "base/compiler_specific.h",
+    "base/debug/alias.cc",
+    "base/debug/alias.h",
     "base/logging.h",
-    "base/macros.h",
+    "base/no_destructor.h",
     "base/numerics/safe_conversions.h",
+    "base/numerics/safe_conversions_arm_impl.h",
     "base/numerics/safe_conversions_impl.h",
     "base/numerics/safe_math.h",
     "base/numerics/safe_math_impl.h",
     "base/optional.h",
-    "base/ptr_util.h",
+    "base/span.h",
     "base/stl_util.h",
     "base/sys_byteorder.h",
     "base/template_util.h",
-    "build/build_config.h",
+  ]
+
+  if (is_win) {
+    sources += [
+      "base/win/win_util.cc",
+      "base/win/win_util.h",
+    ]
+  }
+}
+
+source_set("pdfium_base_test_support") {
+  testonly = true
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+    ":pdfium_third_party_config",
+  ]
+  sources = []
+  deps = []
+
+  if (is_posix || is_fuchsia) {
+    sources += [
+      "base/test/scoped_locale.cc",
+      "base/test/scoped_locale.h",
+    ]
+    deps += [ "//testing/gtest" ]
+  }
+}
+
+source_set("skia_shared") {
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+    ":pdfium_third_party_config",
+  ]
+  sources = [
+    "skia_shared/SkFloatToDecimal.cpp",
+    "skia_shared/SkFloatToDecimal.h",
   ]
 }
diff --git a/third_party/DEPS b/third_party/DEPS
index 93ca1a0..dfd263b 100644
--- a/third_party/DEPS
+++ b/third_party/DEPS
@@ -3,5 +3,5 @@
   '+core/fxcrt/fx_coordinates.h',
   '+core/fxcrt/fx_memory.h',
   '+core/fxcrt/fx_system.h',
-  '+build',
+  '+core/fxcrt/unowned_ptr.h',
 ]
diff --git a/third_party/agg23/0004-ubsan-sweep-scanline-error.patch b/third_party/agg23/0004-ubsan-sweep-scanline-error.patch
new file mode 100644
index 0000000..cde293f
--- /dev/null
+++ b/third_party/agg23/0004-ubsan-sweep-scanline-error.patch
@@ -0,0 +1,54 @@
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+index c90bdafdd..1fe9a0c32 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+@@ -495,4 +495,11 @@ void outline_aa::sort_cells()
+     }
+     m_sorted = true;
+ }
++// static
++int rasterizer_scanline_aa::calculate_area(int cover, int shift)
++{
++    unsigned int result = cover;
++    result <<= shift;
++    return result;
++}
+ }
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.h b/third_party/agg23/agg_rasterizer_scanline_aa.h
+index c747ee379..281933710 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.h
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.h
+@@ -338,7 +338,6 @@ public:
+                 const cell_aa* cur_cell = *cells;
+                 int x    = cur_cell->x;
+                 int area = cur_cell->area;
+-                unsigned alpha;
+                 cover += cur_cell->cover;
+                 while(--num_cells) {
+                     cur_cell = *++cells;
+@@ -349,14 +348,14 @@ public:
+                     cover += cur_cell->cover;
+                 }
+                 if(area) {
+-                    alpha = calculate_alpha((cover << (poly_base_shift + 1)) - area, no_smooth);
++                    unsigned alpha = calculate_alpha(calculate_area(cover, poly_base_shift + 1) - area, no_smooth);
+                     if(alpha) {
+                         sl.add_cell(x, alpha);
+                     }
+                     x++;
+                 }
+                 if(num_cells && cur_cell->x > x) {
+-                    alpha = calculate_alpha(cover << (poly_base_shift + 1), no_smooth);
++                    unsigned alpha = calculate_alpha(calculate_area(cover, poly_base_shift + 1), no_smooth);
+                     if(alpha) {
+                         sl.add_span(x, cur_cell->x - x, alpha);
+                     }
+@@ -459,6 +458,8 @@ private:
+         m_prev_y = y;
+     }
+ private:
++    static int calculate_area(int cover, int shift);
++
+     outline_aa     m_outline;
+     filling_rule_e m_filling_rule;
+     int            m_clipped_start_x;
diff --git a/third_party/agg23/0005-assignment-return-values.patch b/third_party/agg23/0005-assignment-return-values.patch
new file mode 100644
index 0000000..4a0006f
--- /dev/null
+++ b/third_party/agg23/0005-assignment-return-values.patch
@@ -0,0 +1,40 @@
+diff --git a/third_party/agg23/agg_array.h b/third_party/agg23/agg_array.h
+index d521b04..8dcb0af 100644
+--- a/third_party/agg23/agg_array.h
++++ b/third_party/agg23/agg_array.h
+@@ -32,7 +32,7 @@
+     pod_array() : m_size(0), m_capacity(0), m_array(0) {}
+     pod_array(unsigned cap, unsigned extra_tail = 0);
+     pod_array(const pod_array<T>&);
+-    const pod_array<T>& operator = (const pod_array<T>&);
++    pod_array<T>& operator = (const pod_array<T>&);
+     void capacity(unsigned cap, unsigned extra_tail = 0);
+     unsigned capacity() const
+     {
+@@ -144,7 +144,7 @@
+ {
+   memcpy(m_array, v.m_array, sizeof(T) * v.m_size);
+ }
+-template<class T> const pod_array<T>&
++template<class T> pod_array<T>&
+ pod_array<T>::operator = (const pod_array<T>&v)
+ {
+     allocate(v.m_size);
+@@ -166,7 +166,7 @@
+     pod_deque();
+     pod_deque(unsigned block_ptr_inc);
+     pod_deque(const pod_deque<T, S>& v);
+-    const pod_deque<T, S>& operator = (const pod_deque<T, S>& v);
++    pod_deque<T, S>& operator = (const pod_deque<T, S>& v);
+     void remove_all()
+     {
+         m_size = 0;
+@@ -323,7 +323,7 @@
+     }
+ }
+ template<class T, unsigned S>
+-const pod_deque<T, S>& pod_deque<T, S>::operator = (const pod_deque<T, S>& v)
++pod_deque<T, S>& pod_deque<T, S>::operator = (const pod_deque<T, S>& v)
+ {
+     unsigned i;
+     for(i = m_num_blocks; i < v.m_num_blocks; ++i) {
diff --git a/third_party/agg23/0006-ubsan-sweep-scanline-error.patch b/third_party/agg23/0006-ubsan-sweep-scanline-error.patch
new file mode 100644
index 0000000..ec3707d
--- /dev/null
+++ b/third_party/agg23/0006-ubsan-sweep-scanline-error.patch
@@ -0,0 +1,70 @@
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+index 1fe9a0c32..9254d830d 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+@@ -502,4 +502,16 @@ int rasterizer_scanline_aa::calculate_area(int cover, int shift)
+     result <<= shift;
+     return result;
+ }
++// static
++bool rasterizer_scanline_aa::safe_add(int* op1, int op2)
++{
++    pdfium::base::CheckedNumeric<int> safeOp1 = *op1;
++    safeOp1 += op2;
++    if(!safeOp1.IsValid()) {
++        return false;
++    }
++
++    *op1 = safeOp1.ValueOrDie();
++    return true;
++}
+ }
+diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.h b/third_party/agg23/agg_rasterizer_scanline_aa.h
+index 281933710..eade78333 100644
+--- a/third_party/agg23/agg_rasterizer_scanline_aa.h
++++ b/third_party/agg23/agg_rasterizer_scanline_aa.h
+@@ -338,14 +338,33 @@ public:
+                 const cell_aa* cur_cell = *cells;
+                 int x    = cur_cell->x;
+                 int area = cur_cell->area;
+-                cover += cur_cell->cover;
++                bool seen_area_overflow = false;
++                bool seen_cover_overflow = false;
++                if(!safe_add(&cover, cur_cell->cover)) {
++                    break;
++                }
+                 while(--num_cells) {
+                     cur_cell = *++cells;
+                     if(cur_cell->x != x) {
+                         break;
+                     }
+-                    area  += cur_cell->area;
+-                    cover += cur_cell->cover;
++                    if(seen_area_overflow) {
++                        continue;
++                    }
++                    if(!safe_add(&area, cur_cell->area)) {
++                        seen_area_overflow = true;
++                        continue;
++                    }
++                    if(!safe_add(&cover, cur_cell->cover)) {
++                        seen_cover_overflow = true;
++                        break;
++                    }
++                }
++                if(seen_area_overflow) {
++                    continue;
++                }
++                if(seen_cover_overflow) {
++                    break;
+                 }
+                 if(area) {
+                     unsigned alpha = calculate_alpha(calculate_area(cover, poly_base_shift + 1) - area, no_smooth);
+@@ -459,6 +478,7 @@ private:
+     }
+ private:
+     static int calculate_area(int cover, int shift);
++    static bool safe_add(int* op1, int op2);
+ 
+     outline_aa     m_outline;
+     filling_rule_e m_filling_rule;
diff --git a/third_party/agg23/0007-unused-struct.patch b/third_party/agg23/0007-unused-struct.patch
new file mode 100644
index 0000000..a1e2c89
--- /dev/null
+++ b/third_party/agg23/0007-unused-struct.patch
@@ -0,0 +1,19 @@
+diff --git a/third_party/agg23/agg_basics.h b/third_party/agg23/agg_basics.h
+index fc155561e..2a1c2af2f 100644
+--- a/third_party/agg23/agg_basics.h
++++ b/third_party/agg23/agg_basics.h
+@@ -266,14 +266,6 @@ struct point_type  {
+     point_type() {}
+     point_type(float x_, float y_, unsigned flag_ = 0) : x(x_), y(y_), flag(flag_) {}
+ };
+-struct point_type_flag : public point_type {
+-    unsigned flag;
+-    point_type_flag()
+-    {
+-        flag = 0;
+-    }
+-    point_type_flag(float x_, float y_, unsigned flag_ = 0) : point_type(x_, y_), flag(flag_) {}
+-};
+ struct vertex_type  {
+     float   x, y;
+     unsigned cmd;
diff --git a/third_party/agg23/README.pdfium b/third_party/agg23/README.pdfium
index 4b1ff49..0319903 100644
--- a/third_party/agg23/README.pdfium
+++ b/third_party/agg23/README.pdfium
@@ -16,3 +16,11 @@
 non-enumeral type in conditional.
 0002-ubsan-error-fixes.path: Fix UBSan errors for overflows.
 0003-ubsan-render-line-error.patch: Fix UBSan overflow error in render_line.
+0004-ubsan-sweep-scanline-error.patch: Fix UBSan left shift of negative value
+error in sweep_scanline.
+0005-assignment-return-values.patch: Fix assignment operator return values in
+agg_array.h.
+0006-ubsan-sweep-scanline-error.patch: Fix UBSan integer overflow error in
+sweep_scanline.
+0007-unused-struct.patch: Remove unused struct point_type_flag, which has a
+shadow variable.
diff --git a/third_party/agg23/agg_array.h b/third_party/agg23/agg_array.h
index d521b04..8dcb0af 100644
--- a/third_party/agg23/agg_array.h
+++ b/third_party/agg23/agg_array.h
@@ -32,7 +32,7 @@
     pod_array() : m_size(0), m_capacity(0), m_array(0) {}
     pod_array(unsigned cap, unsigned extra_tail = 0);
     pod_array(const pod_array<T>&);
-    const pod_array<T>& operator = (const pod_array<T>&);
+    pod_array<T>& operator = (const pod_array<T>&);
     void capacity(unsigned cap, unsigned extra_tail = 0);
     unsigned capacity() const
     {
@@ -144,7 +144,7 @@
 {
   memcpy(m_array, v.m_array, sizeof(T) * v.m_size);
 }
-template<class T> const pod_array<T>&
+template<class T> pod_array<T>&
 pod_array<T>::operator = (const pod_array<T>&v)
 {
     allocate(v.m_size);
@@ -166,7 +166,7 @@
     pod_deque();
     pod_deque(unsigned block_ptr_inc);
     pod_deque(const pod_deque<T, S>& v);
-    const pod_deque<T, S>& operator = (const pod_deque<T, S>& v);
+    pod_deque<T, S>& operator = (const pod_deque<T, S>& v);
     void remove_all()
     {
         m_size = 0;
@@ -323,7 +323,7 @@
     }
 }
 template<class T, unsigned S>
-const pod_deque<T, S>& pod_deque<T, S>::operator = (const pod_deque<T, S>& v)
+pod_deque<T, S>& pod_deque<T, S>::operator = (const pod_deque<T, S>& v)
 {
     unsigned i;
     for(i = m_num_blocks; i < v.m_num_blocks; ++i) {
diff --git a/third_party/agg23/agg_basics.h b/third_party/agg23/agg_basics.h
index fc15556..2a1c2af 100644
--- a/third_party/agg23/agg_basics.h
+++ b/third_party/agg23/agg_basics.h
@@ -266,14 +266,6 @@
     point_type() {}
     point_type(float x_, float y_, unsigned flag_ = 0) : x(x_), y(y_), flag(flag_) {}
 };
-struct point_type_flag : public point_type {
-    unsigned flag;
-    point_type_flag()
-    {
-        flag = 0;
-    }
-    point_type_flag(float x_, float y_, unsigned flag_ = 0) : point_type(x_, y_), flag(flag_) {}
-};
 struct vertex_type  {
     float   x, y;
     unsigned cmd;
diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
index c90bdaf..9254d83 100644
--- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp
+++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp
@@ -495,4 +495,23 @@
     }
     m_sorted = true;
 }
+// static
+int rasterizer_scanline_aa::calculate_area(int cover, int shift)
+{
+    unsigned int result = cover;
+    result <<= shift;
+    return result;
+}
+// static
+bool rasterizer_scanline_aa::safe_add(int* op1, int op2)
+{
+    pdfium::base::CheckedNumeric<int> safeOp1 = *op1;
+    safeOp1 += op2;
+    if(!safeOp1.IsValid()) {
+        return false;
+    }
+
+    *op1 = safeOp1.ValueOrDie();
+    return true;
+}
 }
diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.h b/third_party/agg23/agg_rasterizer_scanline_aa.h
index c747ee3..eade783 100644
--- a/third_party/agg23/agg_rasterizer_scanline_aa.h
+++ b/third_party/agg23/agg_rasterizer_scanline_aa.h
@@ -338,25 +338,43 @@
                 const cell_aa* cur_cell = *cells;
                 int x    = cur_cell->x;
                 int area = cur_cell->area;
-                unsigned alpha;
-                cover += cur_cell->cover;
+                bool seen_area_overflow = false;
+                bool seen_cover_overflow = false;
+                if(!safe_add(&cover, cur_cell->cover)) {
+                    break;
+                }
                 while(--num_cells) {
                     cur_cell = *++cells;
                     if(cur_cell->x != x) {
                         break;
                     }
-                    area  += cur_cell->area;
-                    cover += cur_cell->cover;
+                    if(seen_area_overflow) {
+                        continue;
+                    }
+                    if(!safe_add(&area, cur_cell->area)) {
+                        seen_area_overflow = true;
+                        continue;
+                    }
+                    if(!safe_add(&cover, cur_cell->cover)) {
+                        seen_cover_overflow = true;
+                        break;
+                    }
+                }
+                if(seen_area_overflow) {
+                    continue;
+                }
+                if(seen_cover_overflow) {
+                    break;
                 }
                 if(area) {
-                    alpha = calculate_alpha((cover << (poly_base_shift + 1)) - area, no_smooth);
+                    unsigned alpha = calculate_alpha(calculate_area(cover, poly_base_shift + 1) - area, no_smooth);
                     if(alpha) {
                         sl.add_cell(x, alpha);
                     }
                     x++;
                 }
                 if(num_cells && cur_cell->x > x) {
-                    alpha = calculate_alpha(cover << (poly_base_shift + 1), no_smooth);
+                    unsigned alpha = calculate_alpha(calculate_area(cover, poly_base_shift + 1), no_smooth);
                     if(alpha) {
                         sl.add_span(x, cur_cell->x - x, alpha);
                     }
@@ -459,6 +477,9 @@
         m_prev_y = y;
     }
 private:
+    static int calculate_area(int cover, int shift);
+    static bool safe_add(int* op1, int op2);
+
     outline_aa     m_outline;
     filling_rule_e m_filling_rule;
     int            m_clipped_start_x;
diff --git a/third_party/agg23/agg_vertex_sequence.h b/third_party/agg23/agg_vertex_sequence.h
index 448e57b..80eabbb 100644
--- a/third_party/agg23/agg_vertex_sequence.h
+++ b/third_party/agg23/agg_vertex_sequence.h
@@ -31,7 +31,7 @@
     typedef pod_deque<T, S> base_type;
     void add(const T& val);
     void modify_last(const T& val);
-    void close(bool remove_flag);
+    void close(bool closed);
 };
 template<class T, unsigned S>
 void vertex_sequence<T, S>::add(const T& val)
diff --git a/third_party/android_sdk/BUILD.gn b/third_party/android_sdk/BUILD.gn
new file mode 100644
index 0000000..da970c3
--- /dev/null
+++ b/third_party/android_sdk/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+config("cpu_features_include") {
+  include_dirs = [ "$android_ndk_root/sources/android/cpufeatures" ]
+}
+
+config("cpu_features_warnings") {
+  if (is_clang) {
+    # cpu-features.c has few unused functions on x86 b/26403333
+    cflags = [ "-Wno-unused-function" ]
+  }
+}
+
+source_set("cpu_features") {
+  sources = [ "$android_ndk_root/sources/android/cpufeatures/cpu-features.c" ]
+  public_configs = [ ":cpu_features_include" ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+
+    # Must be after no_chromium_code for warning flags to be ordered correctly.
+    ":cpu_features_warnings",
+  ]
+}
diff --git a/third_party/base/allocator/partition_allocator/address_space_randomization.cc b/third_party/base/allocator/partition_allocator/address_space_randomization.cc
index 08f22be..c6f268f 100644
--- a/third_party/base/allocator/partition_allocator/address_space_randomization.cc
+++ b/third_party/base/allocator/partition_allocator/address_space_randomization.cc
@@ -4,98 +4,31 @@
 
 #include "third_party/base/allocator/partition_allocator/address_space_randomization.h"
 
+#include "build/build_config.h"
 #include "third_party/base/allocator/partition_allocator/page_allocator.h"
+#include "third_party/base/allocator/partition_allocator/random.h"
 #include "third_party/base/allocator/partition_allocator/spin_lock.h"
-#include "third_party/build/build_config.h"
+#include "third_party/base/logging.h"
 
 #if defined(OS_WIN)
-#include <windows.h>
-#else
-#include <sys/time.h>
-#include <unistd.h>
-#endif
+#include <windows.h>  // Must be in front of other Windows header files.
 
-// VersionHelpers.h must be included after windows.h.
-#if defined(OS_WIN)
 #include <VersionHelpers.h>
 #endif
 
 namespace pdfium {
 namespace base {
 
-namespace {
-
-// This is the same PRNG as used by tcmalloc for mapping address randomness;
-// see http://burtleburtle.net/bob/rand/smallprng.html
-struct ranctx {
-  subtle::SpinLock lock;
-  bool initialized;
-  uint32_t a;
-  uint32_t b;
-  uint32_t c;
-  uint32_t d;
-};
-
-#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
-
-uint32_t ranvalInternal(ranctx* x) {
-  uint32_t e = x->a - rot(x->b, 27);
-  x->a = x->b ^ rot(x->c, 17);
-  x->b = x->c + x->d;
-  x->c = x->d + e;
-  x->d = e + x->a;
-  return x->d;
-}
-
-#undef rot
-
-uint32_t ranval(ranctx* x) {
-  subtle::SpinLock::Guard guard(x->lock);
-  if (UNLIKELY(!x->initialized)) {
-    x->initialized = true;
-    char c;
-    uint32_t seed = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&c));
-    uint32_t pid;
-    uint32_t usec;
-#if defined(OS_WIN)
-    pid = GetCurrentProcessId();
-    SYSTEMTIME st;
-    GetSystemTime(&st);
-    usec = static_cast<uint32_t>(st.wMilliseconds * 1000);
-#else
-    pid = static_cast<uint32_t>(getpid());
-    struct timeval tv;
-    gettimeofday(&tv, 0);
-    usec = static_cast<uint32_t>(tv.tv_usec);
-#endif
-    seed ^= pid;
-    seed ^= usec;
-    x->a = 0xf1ea5eed;
-    x->b = x->c = x->d = seed;
-    for (int i = 0; i < 20; ++i) {
-      (void)ranvalInternal(x);
-    }
-  }
-  uint32_t ret = ranvalInternal(x);
-  return ret;
-}
-
-static struct ranctx s_ranctx;
-
-}  // namespace
-
-// Calculates a random preferred mapping address. In calculating an address, we
-// balance good ASLR against not fragmenting the address space too badly.
 void* GetRandomPageBase() {
-  uintptr_t random;
-  random = static_cast<uintptr_t>(ranval(&s_ranctx));
-#if defined(ARCH_CPU_X86_64)
-  random <<= 32UL;
-  random |= static_cast<uintptr_t>(ranval(&s_ranctx));
-// This address mask gives a low likelihood of address space collisions. We
-// handle the situation gracefully if there is a collision.
-#if defined(OS_WIN)
-  random &= 0x3ffffffffffUL;
+  uintptr_t random = static_cast<uintptr_t>(RandomValue());
+
+#if defined(ARCH_CPU_64_BITS)
+  random <<= 32ULL;
+  random |= static_cast<uintptr_t>(RandomValue());
+
+// The kASLRMask and kASLROffset constants will be suitable for the
+// OS and build configuration.
+#if defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
   // Windows >= 8.1 has the full 47 bits. Use them where available.
   static bool windows_81 = false;
   static bool windows_81_initialized = false;
@@ -104,38 +37,32 @@
     windows_81_initialized = true;
   }
   if (!windows_81) {
-    random += 0x10000000000UL;
+    random &= internal::kASLRMaskBefore8_10;
+  } else {
+    random &= internal::kASLRMask;
   }
-#elif defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  // This range is copied from the TSan source, but works for all tools.
-  random &= 0x007fffffffffUL;
-  random += 0x7e8000000000UL;
+  random += internal::kASLROffset;
 #else
-  // Linux and OS X support the full 47-bit user space of x64 processors.
-  random &= 0x3fffffffffffUL;
-#endif
-#elif defined(ARCH_CPU_ARM64)
-  // ARM64 on Linux has 39-bit user space.
-  random &= 0x3fffffffffUL;
-  random += 0x1000000000UL;
-#else  // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_ARM64)
+  random &= internal::kASLRMask;
+  random += internal::kASLROffset;
+#endif  // defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+#else   // defined(ARCH_CPU_32_BITS)
 #if defined(OS_WIN)
   // On win32 host systems the randomization plus huge alignment causes
   // excessive fragmentation. Plus most of these systems lack ASLR, so the
   // randomization isn't buying anything. In that case we just skip it.
-  // TODO(jschuh): Just dump the randomization when HE-ASLR is present.
-  static BOOL isWow64 = -1;
-  if (isWow64 == -1 && !IsWow64Process(GetCurrentProcess(), &isWow64))
-    isWow64 = FALSE;
-  if (!isWow64)
+  // TODO(palmer): Just dump the randomization when HE-ASLR is present.
+  static BOOL is_wow64 = -1;
+  if (is_wow64 == -1 && !IsWow64Process(GetCurrentProcess(), &is_wow64))
+    is_wow64 = FALSE;
+  if (!is_wow64)
     return nullptr;
 #endif  // defined(OS_WIN)
-  // This is a good range on Windows, Linux and Mac.
-  // Allocates in the 0.5-1.5GB region.
-  random &= 0x3fffffff;
-  random += 0x20000000;
-#endif  // defined(ARCH_CPU_X86_64)
-  random &= kPageAllocationGranularityBaseMask;
+  random &= internal::kASLRMask;
+  random += internal::kASLROffset;
+#endif  // defined(ARCH_CPU_32_BITS)
+
+  DCHECK_EQ(0ULL, (random & kPageAllocationGranularityOffsetMask));
   return reinterpret_cast<void*>(random);
 }
 
diff --git a/third_party/base/allocator/partition_allocator/address_space_randomization.h b/third_party/base/allocator/partition_allocator/address_space_randomization.h
index 97c5f60..5cb2ccc 100644
--- a/third_party/base/allocator/partition_allocator/address_space_randomization.h
+++ b/third_party/base/allocator/partition_allocator/address_space_randomization.h
@@ -2,17 +2,202 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION
-#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/page_allocator.h"
+#include "third_party/base/base_export.h"
 
 namespace pdfium {
 namespace base {
 
 // Calculates a random preferred mapping address. In calculating an address, we
 // balance good ASLR against not fragmenting the address space too badly.
-void* GetRandomPageBase();
+BASE_EXPORT void* GetRandomPageBase();
+
+namespace internal {
+
+constexpr uintptr_t AslrAddress(uintptr_t mask) {
+  return mask & kPageAllocationGranularityBaseMask;
+}
+constexpr uintptr_t AslrMask(uintptr_t bits) {
+  return AslrAddress((1ULL << bits) - 1ULL);
+}
+
+// Turn off formatting, because the thicket of nested ifdefs below is
+// incomprehensible without indentation. It is also incomprehensible with
+// indentation, but the only other option is a combinatorial explosion of
+// *_{win,linux,mac,foo}_{32,64}.h files.
+//
+// clang-format off
+
+#if defined(ARCH_CPU_64_BITS)
+
+  #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+
+    // We shouldn't allocate system pages at all for sanitizer builds. However,
+    // we do, and if random hint addresses interfere with address ranges
+    // hard-coded in those tools, bad things happen. This address range is
+    // copied from TSAN source but works with all tools. See
+    // https://crbug.com/539863.
+    constexpr uintptr_t kASLRMask = AslrAddress(0x007fffffffffULL);
+    constexpr uintptr_t kASLROffset = AslrAddress(0x7e8000000000ULL);
+
+  #elif defined(OS_WIN)
+
+    // Windows 8.10 and newer support the full 48 bit address range. Older
+    // versions of Windows only support 44 bits. Since kASLROffset is non-zero
+    // and may cause a carry, use 47 and 43 bit masks. See
+    // http://www.alex-ionescu.com/?p=246
+    constexpr uintptr_t kASLRMask = AslrMask(47);
+    constexpr uintptr_t kASLRMaskBefore8_10 = AslrMask(43);
+    // Try not to map pages into the range where Windows loads DLLs by default.
+    constexpr uintptr_t kASLROffset = 0x80000000ULL;
+
+  #elif defined(OS_MACOSX)
+
+    // macOS as of 10.12.5 does not clean up entries in page map levels 3/4
+    // [PDP/PML4] created from mmap or mach_vm_allocate, even after the region
+    // is destroyed. Using a virtual address space that is too large causes a
+    // leak of about 1 wired [can never be paged out] page per call to mmap. The
+    // page is only reclaimed when the process is killed. Confine the hint to a
+    // 39-bit section of the virtual address space.
+    //
+    // This implementation adapted from
+    // https://chromium-review.googlesource.com/c/v8/v8/+/557958. The difference
+    // is that here we clamp to 39 bits, not 32.
+    //
+    // TODO(crbug.com/738925): Remove this limitation if/when the macOS behavior
+    // changes.
+    constexpr uintptr_t kASLRMask = AslrMask(38);
+    constexpr uintptr_t kASLROffset = AslrAddress(0x1000000000ULL);
+
+  #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+    #if defined(ARCH_CPU_X86_64)
+
+      // Linux (and macOS) support the full 47-bit user space of x64 processors.
+      // Use only 46 to allow the kernel a chance to fulfill the request.
+      constexpr uintptr_t kASLRMask = AslrMask(46);
+      constexpr uintptr_t kASLROffset = AslrAddress(0);
+
+    #elif defined(ARCH_CPU_ARM64)
+
+      #if defined(OS_ANDROID)
+
+      // Restrict the address range on Android to avoid a large performance
+      // regression in single-process WebViews. See https://crbug.com/837640.
+      constexpr uintptr_t kASLRMask = AslrMask(30);
+      constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL);
+
+      #else
+
+      // ARM64 on Linux has 39-bit user space. Use 38 bits since kASLROffset
+      // could cause a carry.
+      constexpr uintptr_t kASLRMask = AslrMask(38);
+      constexpr uintptr_t kASLROffset = AslrAddress(0x1000000000ULL);
+
+      #endif
+
+    #elif defined(ARCH_CPU_PPC64)
+
+      #if defined(OS_AIX)
+
+        // AIX has 64 bits of virtual addressing, but we limit the address range
+        // to (a) minimize segment lookaside buffer (SLB) misses; and (b) use
+        // extra address space to isolate the mmap regions.
+        constexpr uintptr_t kASLRMask = AslrMask(30);
+        constexpr uintptr_t kASLROffset = AslrAddress(0x400000000000ULL);
+
+      #elif defined(ARCH_CPU_BIG_ENDIAN)
+
+        // Big-endian Linux PPC has 44 bits of virtual addressing. Use 42.
+        constexpr uintptr_t kASLRMask = AslrMask(42);
+        constexpr uintptr_t kASLROffset = AslrAddress(0);
+
+      #else  // !defined(OS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
+
+        // Little-endian Linux PPC has 48 bits of virtual addressing. Use 46.
+        constexpr uintptr_t kASLRMask = AslrMask(46);
+        constexpr uintptr_t kASLROffset = AslrAddress(0);
+
+      #endif  // !defined(OS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
+
+    #elif defined(ARCH_CPU_S390X)
+
+      // Linux on Z uses bits 22 - 32 for Region Indexing, which translates to
+      // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel a
+      // chance to fulfill the request.
+      constexpr uintptr_t kASLRMask = AslrMask(40);
+      constexpr uintptr_t kASLROffset = AslrAddress(0);
+
+    #elif defined(ARCH_CPU_S390)
+
+      // 31 bits of virtual addressing. Truncate to 29 bits to allow the kernel
+      // a chance to fulfill the request.
+      constexpr uintptr_t kASLRMask = AslrMask(29);
+      constexpr uintptr_t kASLROffset = AslrAddress(0);
+
+    #else  // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
+           // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
+
+      // For all other POSIX variants, use 30 bits.
+      constexpr uintptr_t kASLRMask = AslrMask(30);
+
+      #if defined(OS_SOLARIS)
+
+        // For our Solaris/illumos mmap hint, we pick a random address in the
+        // bottom half of the top half of the address space (that is, the third
+        // quarter). Because we do not MAP_FIXED, this will be treated only as a
+        // hint -- the system will not fail to mmap because something else
+        // happens to already be mapped at our random address. We deliberately
+        // set the hint high enough to get well above the system's break (that
+        // is, the heap); Solaris and illumos will try the hint and if that
+        // fails allocate as if there were no hint at all. The high hint
+        // prevents the break from getting hemmed in at low values, ceding half
+        // of the address space to the system heap.
+        constexpr uintptr_t kASLROffset = AslrAddress(0x80000000ULL);
+
+      #elif defined(OS_AIX)
+
+        // The range 0x30000000 - 0xD0000000 is available on AIX; choose the
+        // upper range.
+        constexpr uintptr_t kASLROffset = AslrAddress(0x90000000ULL);
+
+      #else  // !defined(OS_SOLARIS) && !defined(OS_AIX)
+
+        // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
+        // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macOS
+        // 10.6 and 10.7.
+        constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL);
+
+      #endif  // !defined(OS_SOLARIS) && !defined(OS_AIX)
+
+    #endif  // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) &&
+            // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
+
+  #endif  // defined(OS_POSIX)
+
+#elif defined(ARCH_CPU_32_BITS)
+
+  // This is a good range on 32-bit Windows and Android (the only platforms on
+  // which we support 32-bitness). Allocates in the 0.5 - 1.5 GiB region. There
+  // is no issue with carries here.
+  constexpr uintptr_t kASLRMask = AslrMask(30);
+  constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL);
+
+#else
+
+  #error Please tell us about your exotic hardware! Sounds interesting.
+
+#endif  // defined(ARCH_CPU_32_BITS)
+
+// clang-format on
+
+}  // namespace internal
 
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
diff --git a/third_party/base/allocator/partition_allocator/oom.h b/third_party/base/allocator/partition_allocator/oom.h
index 41f29b5..bbd1ead 100644
--- a/third_party/base/allocator/partition_allocator/oom.h
+++ b/third_party/base/allocator/partition_allocator/oom.h
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_ALLOCATOR_OOM_H
-#define BASE_ALLOCATOR_OOM_H
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_
 
+#include "third_party/base/allocator/partition_allocator/oom_callback.h"
 #include "third_party/base/logging.h"
 
 #if defined(OS_WIN)
@@ -23,15 +24,17 @@
 #define OOM_CRASH()                                                     \
   do {                                                                  \
     OOM_CRASH_PREVENT_ICF();                                            \
+    base::internal::RunPartitionAllocOomCallback();                     \
     ::RaiseException(0xE0000008, EXCEPTION_NONCONTINUABLE, 0, nullptr); \
     IMMEDIATE_CRASH();                                                  \
   } while (0)
 #else
-#define OOM_CRASH()          \
-  do {                       \
-    OOM_CRASH_PREVENT_ICF(); \
-    IMMEDIATE_CRASH();       \
+#define OOM_CRASH()                                 \
+  do {                                              \
+    base::internal::RunPartitionAllocOomCallback(); \
+    OOM_CRASH_PREVENT_ICF();                        \
+    IMMEDIATE_CRASH();                              \
   } while (0)
 #endif
 
-#endif  // BASE_ALLOCATOR_OOM_H
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_
diff --git a/third_party/base/allocator/partition_allocator/oom_callback.cc b/third_party/base/allocator/partition_allocator/oom_callback.cc
new file mode 100644
index 0000000..9143438
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/oom_callback.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/allocator/partition_allocator/oom_callback.h"
+
+#include "third_party/base/logging.h"
+
+namespace pdfium {
+namespace base {
+
+namespace {
+PartitionAllocOomCallback g_oom_callback;
+}  // namespace
+
+void SetPartitionAllocOomCallback(PartitionAllocOomCallback callback) {
+  DCHECK(!g_oom_callback);
+  g_oom_callback = callback;
+}
+
+namespace internal {
+void RunPartitionAllocOomCallback() {
+  if (g_oom_callback)
+    g_oom_callback();
+}
+}  // namespace internal
+
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/oom_callback.h b/third_party/base/allocator/partition_allocator/oom_callback.h
new file mode 100644
index 0000000..044b167
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/oom_callback.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_
+
+#include "third_party/base/base_export.h"
+
+namespace pdfium {
+namespace base {
+typedef void (*PartitionAllocOomCallback)();
+// Registers a callback to be invoked during an OOM_CRASH(). OOM_CRASH is
+// invoked by users of PageAllocator (including PartitionAlloc) to signify an
+// allocation failure from the platform.
+BASE_EXPORT void SetPartitionAllocOomCallback(
+    PartitionAllocOomCallback callback);
+
+namespace internal {
+BASE_EXPORT void RunPartitionAllocOomCallback();
+}  // namespace internal
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator.cc b/third_party/base/allocator/partition_allocator/page_allocator.cc
index abe159b..14e9379 100644
--- a/third_party/base/allocator/partition_allocator/page_allocator.cc
+++ b/third_party/base/allocator/partition_allocator/page_allocator.cc
@@ -8,118 +8,109 @@
 
 #include <atomic>
 
+#include "build/build_config.h"
 #include "third_party/base/allocator/partition_allocator/address_space_randomization.h"
-#include "third_party/base/base_export.h"
+#include "third_party/base/allocator/partition_allocator/page_allocator_internal.h"
+#include "third_party/base/allocator/partition_allocator/spin_lock.h"
+#include "third_party/base/bits.h"
 #include "third_party/base/logging.h"
-#include "third_party/build/build_config.h"
+#include "third_party/base/numerics/safe_math.h"
 
-#if defined(OS_POSIX)
-
-#include <errno.h>
-#include <sys/mman.h>
-
-#ifndef MADV_FREE
-#define MADV_FREE MADV_DONTNEED
-#endif
-
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-// On POSIX |mmap| uses a nearby address if the hint address is blocked.
-static const bool kHintIsAdvisory = true;
-static std::atomic<int32_t> s_allocPageErrorCode{0};
-
-#elif defined(OS_WIN)
-
+#if defined(OS_WIN)
 #include <windows.h>
+#endif
 
-// |VirtualAlloc| will fail if allocation at the hint address is blocked.
-static const bool kHintIsAdvisory = false;
-static std::atomic<int32_t> s_allocPageErrorCode{ERROR_SUCCESS};
-
+#if defined(OS_WIN)
+#include "third_party/base/allocator/partition_allocator/page_allocator_internals_win.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h"
 #else
-#error Unknown OS
-#endif  // defined(OS_POSIX)
+#error Platform not supported.
+#endif
 
 namespace pdfium {
 namespace base {
 
-// This internal function wraps the OS-specific page allocation call:
-// |VirtualAlloc| on Windows, and |mmap| on POSIX.
-static void* SystemAllocPages(
-    void* hint,
-    size_t length,
-    PageAccessibilityConfiguration page_accessibility) {
-  DCHECK(!(length & kPageAllocationGranularityOffsetMask));
-  DCHECK(!(reinterpret_cast<uintptr_t>(hint) &
-           kPageAllocationGranularityOffsetMask));
-  void* ret;
-#if defined(OS_WIN)
-  DWORD access_flag =
-      page_accessibility == PageAccessible ? PAGE_READWRITE : PAGE_NOACCESS;
-  ret = VirtualAlloc(hint, length, MEM_RESERVE | MEM_COMMIT, access_flag);
-  if (!ret)
-    s_allocPageErrorCode = GetLastError();
-#else
-  int access_flag = page_accessibility == PageAccessible
-                        ? (PROT_READ | PROT_WRITE)
-                        : PROT_NONE;
-  ret = mmap(hint, length, access_flag, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  if (ret == MAP_FAILED) {
-    s_allocPageErrorCode = errno;
-    ret = 0;
+namespace {
+
+// We may reserve/release address space on different threads.
+subtle::SpinLock* GetReserveLock() {
+  static subtle::SpinLock* s_reserveLock = nullptr;
+  if (!s_reserveLock)
+    s_reserveLock = new subtle::SpinLock();
+  return s_reserveLock;
+}
+
+// We only support a single block of reserved address space.
+void* s_reservation_address = nullptr;
+size_t s_reservation_size = 0;
+
+void* AllocPagesIncludingReserved(void* address,
+                                  size_t length,
+                                  PageAccessibilityConfiguration accessibility,
+                                  PageTag page_tag,
+                                  bool commit) {
+  void* ret =
+      SystemAllocPages(address, length, accessibility, page_tag, commit);
+  if (ret == nullptr) {
+    const bool cant_alloc_length = kHintIsAdvisory || address == nullptr;
+    if (cant_alloc_length) {
+      // The system cannot allocate |length| bytes. Release any reserved address
+      // space and try once more.
+      ReleaseReservation();
+      ret = SystemAllocPages(address, length, accessibility, page_tag, commit);
+    }
   }
-#endif
   return ret;
 }
 
-// Trims base to given length and alignment. Windows returns null on failure and
-// frees base.
-static void* TrimMapping(void* base,
-                         size_t base_length,
-                         size_t trim_length,
-                         uintptr_t align,
-                         PageAccessibilityConfiguration page_accessibility) {
-  size_t pre_slack = reinterpret_cast<uintptr_t>(base) & (align - 1);
-  if (pre_slack)
-    pre_slack = align - pre_slack;
+// Trims |base| to given |trim_length| and |alignment|.
+//
+// On failure, on Windows, this function returns nullptr and frees |base|.
+void* TrimMapping(void* base,
+                  size_t base_length,
+                  size_t trim_length,
+                  uintptr_t alignment,
+                  PageAccessibilityConfiguration accessibility,
+                  bool commit) {
+  size_t pre_slack = reinterpret_cast<uintptr_t>(base) & (alignment - 1);
+  if (pre_slack) {
+    pre_slack = alignment - pre_slack;
+  }
   size_t post_slack = base_length - pre_slack - trim_length;
   DCHECK(base_length >= trim_length || pre_slack || post_slack);
   DCHECK(pre_slack < base_length);
   DCHECK(post_slack < base_length);
-  void* ret = base;
+  return TrimMappingInternal(base, base_length, trim_length, accessibility,
+                             commit, pre_slack, post_slack);
+}
 
-#if defined(OS_POSIX)  // On POSIX we can resize the allocation run.
-  (void)page_accessibility;
-  if (pre_slack) {
-    int res = munmap(base, pre_slack);
-    CHECK(!res);
-    ret = reinterpret_cast<char*>(base) + pre_slack;
-  }
-  if (post_slack) {
-    int res = munmap(reinterpret_cast<char*>(ret) + trim_length, post_slack);
-    CHECK(!res);
-  }
-#else  // On Windows we can't resize the allocation run.
-  if (pre_slack || post_slack) {
-    ret = reinterpret_cast<char*>(base) + pre_slack;
-    FreePages(base, base_length);
-    ret = SystemAllocPages(ret, trim_length, page_accessibility);
-  }
-#endif
+}  // namespace
 
-  return ret;
+void* SystemAllocPages(void* hint,
+                       size_t length,
+                       PageAccessibilityConfiguration accessibility,
+                       PageTag page_tag,
+                       bool commit) {
+  DCHECK(!(length & kPageAllocationGranularityOffsetMask));
+  DCHECK(!(reinterpret_cast<uintptr_t>(hint) &
+           kPageAllocationGranularityOffsetMask));
+  DCHECK(commit || accessibility == PageInaccessible);
+  return SystemAllocPagesInternal(hint, length, accessibility, page_tag,
+                                  commit);
 }
 
 void* AllocPages(void* address,
                  size_t length,
                  size_t align,
-                 PageAccessibilityConfiguration page_accessibility) {
+                 PageAccessibilityConfiguration accessibility,
+                 PageTag page_tag,
+                 bool commit) {
   DCHECK(length >= kPageAllocationGranularity);
   DCHECK(!(length & kPageAllocationGranularityOffsetMask));
   DCHECK(align >= kPageAllocationGranularity);
-  DCHECK(!(align & kPageAllocationGranularityOffsetMask));
+  // Alignment must be power of 2 for masking math to work.
+  DCHECK(pdfium::base::bits::IsPowerOfTwo(align));
   DCHECK(!(reinterpret_cast<uintptr_t>(address) &
            kPageAllocationGranularityOffsetMask));
   uintptr_t align_offset_mask = align - 1;
@@ -127,34 +118,44 @@
   DCHECK(!(reinterpret_cast<uintptr_t>(address) & align_offset_mask));
 
   // If the client passed null as the address, choose a good one.
-  if (!address) {
+  if (address == nullptr) {
     address = GetRandomPageBase();
     address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) &
                                       align_base_mask);
   }
 
   // First try to force an exact-size, aligned allocation from our random base.
-  for (int count = 0; count < 3; ++count) {
-    void* ret = SystemAllocPages(address, length, page_accessibility);
-    if (kHintIsAdvisory || ret) {
+#if defined(ARCH_CPU_32_BITS)
+  // On 32 bit systems, first try one random aligned address, and then try an
+  // aligned address derived from the value of |ret|.
+  constexpr int kExactSizeTries = 2;
+#else
+  // On 64 bit systems, try 3 random aligned addresses.
+  constexpr int kExactSizeTries = 3;
+#endif
+
+  for (int i = 0; i < kExactSizeTries; ++i) {
+    void* ret = AllocPagesIncludingReserved(address, length, accessibility,
+                                            page_tag, commit);
+    if (ret != nullptr) {
       // If the alignment is to our liking, we're done.
       if (!(reinterpret_cast<uintptr_t>(ret) & align_offset_mask))
         return ret;
+      // Free the memory and try again.
       FreePages(ret, length);
-#if defined(ARCH_CPU_32_BITS)
-      address = reinterpret_cast<void*>(
-          (reinterpret_cast<uintptr_t>(ret) + align) & align_base_mask);
-#endif
-    } else if (!address) {  // We know we're OOM when an unhinted allocation
-                            // fails.
-      return nullptr;
     } else {
-#if defined(ARCH_CPU_32_BITS)
-      address = reinterpret_cast<char*>(address) + align;
-#endif
+      // |ret| is null; if this try was unhinted, we're OOM.
+      if (kHintIsAdvisory || address == nullptr)
+        return nullptr;
     }
 
-#if !defined(ARCH_CPU_32_BITS)
+#if defined(ARCH_CPU_32_BITS)
+    // For small address spaces, try the first aligned address >= |ret|. Note
+    // |ret| may be null, in which case |address| becomes null.
+    address = reinterpret_cast<void*>(
+        (reinterpret_cast<uintptr_t>(ret) + align_offset_mask) &
+        align_base_mask);
+#else  // defined(ARCH_CPU_64_BITS)
     // Keep trying random addresses on systems that have a large address space.
     address = GetRandomPageBase();
     address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) &
@@ -162,21 +163,21 @@
 #endif
   }
 
-  // Map a larger allocation so we can force alignment, but continue randomizing
-  // only on 64-bit POSIX.
+  // Make a larger allocation so we can force alignment.
   size_t try_length = length + (align - kPageAllocationGranularity);
   CHECK(try_length >= length);
   void* ret;
 
   do {
-    // Don't continue to burn cycles on mandatory hints (Windows).
+    // Continue randomizing only on POSIX.
     address = kHintIsAdvisory ? GetRandomPageBase() : nullptr;
-    ret = SystemAllocPages(address, try_length, page_accessibility);
+    ret = AllocPagesIncludingReserved(address, try_length, accessibility,
+                                      page_tag, commit);
     // The retries are for Windows, where a race can steal our mapping on
     // resize.
-  } while (ret &&
-           (ret = TrimMapping(ret, try_length, length, align,
-                              page_accessibility)) == nullptr);
+  } while (ret != nullptr &&
+           (ret = TrimMapping(ret, try_length, length, align, accessibility,
+                              commit)) == nullptr);
 
   return ret;
 }
@@ -185,92 +186,67 @@
   DCHECK(!(reinterpret_cast<uintptr_t>(address) &
            kPageAllocationGranularityOffsetMask));
   DCHECK(!(length & kPageAllocationGranularityOffsetMask));
-#if defined(OS_POSIX)
-  int ret = munmap(address, length);
-  CHECK(!ret);
-#else
-  BOOL ret = VirtualFree(address, 0, MEM_RELEASE);
-  CHECK(ret);
-#endif
+  FreePagesInternal(address, length);
 }
 
-void SetSystemPagesInaccessible(void* address, size_t length) {
+bool TrySetSystemPagesAccess(void* address,
+                             size_t length,
+                             PageAccessibilityConfiguration accessibility) {
   DCHECK(!(length & kSystemPageOffsetMask));
-#if defined(OS_POSIX)
-  int ret = mprotect(address, length, PROT_NONE);
-  CHECK(!ret);
-#else
-  BOOL ret = VirtualFree(address, length, MEM_DECOMMIT);
-  CHECK(ret);
-#endif
+  return TrySetSystemPagesAccessInternal(address, length, accessibility);
 }
 
-bool SetSystemPagesAccessible(void* address, size_t length) {
+void SetSystemPagesAccess(void* address,
+                          size_t length,
+                          PageAccessibilityConfiguration accessibility) {
   DCHECK(!(length & kSystemPageOffsetMask));
-#if defined(OS_POSIX)
-  return !mprotect(address, length, PROT_READ | PROT_WRITE);
-#else
-  return !!VirtualAlloc(address, length, MEM_COMMIT, PAGE_READWRITE);
-#endif
+  SetSystemPagesAccessInternal(address, length, accessibility);
 }
 
 void DecommitSystemPages(void* address, size_t length) {
-  DCHECK(!(length & kSystemPageOffsetMask));
-#if defined(OS_POSIX)
-  int ret = madvise(address, length, MADV_FREE);
-  if (ret != 0 && errno == EINVAL) {
-    // MADV_FREE only works on Linux 4.5+ . If request failed,
-    // retry with older MADV_DONTNEED . Note that MADV_FREE
-    // being defined at compile time doesn't imply runtime support.
-    ret = madvise(address, length, MADV_DONTNEED);
-  }
-  CHECK(!ret);
-#else
-  SetSystemPagesInaccessible(address, length);
-#endif
+  DCHECK_EQ(0UL, length & kSystemPageOffsetMask);
+  DecommitSystemPagesInternal(address, length);
 }
 
-void RecommitSystemPages(void* address, size_t length) {
-  DCHECK(!(length & kSystemPageOffsetMask));
-#if defined(OS_POSIX)
-  (void)address;
-#else
-  CHECK(SetSystemPagesAccessible(address, length));
-#endif
+bool RecommitSystemPages(void* address,
+                         size_t length,
+                         PageAccessibilityConfiguration accessibility) {
+  DCHECK_EQ(0UL, length & kSystemPageOffsetMask);
+  DCHECK(PageInaccessible != accessibility);
+  return RecommitSystemPagesInternal(address, length, accessibility);
 }
 
 void DiscardSystemPages(void* address, size_t length) {
-  DCHECK(!(length & kSystemPageOffsetMask));
-#if defined(OS_POSIX)
-  // On POSIX, the implementation detail is that discard and decommit are the
-  // same, and lead to pages that are returned to the system immediately and
-  // get replaced with zeroed pages when touched. So we just call
-  // DecommitSystemPages() here to avoid code duplication.
-  DecommitSystemPages(address, length);
-#else
-  // On Windows discarded pages are not returned to the system immediately and
-  // not guaranteed to be zeroed when returned to the application.
-  using DiscardVirtualMemoryFunction =
-      DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size);
-  static DiscardVirtualMemoryFunction discard_virtual_memory =
-      reinterpret_cast<DiscardVirtualMemoryFunction>(-1);
-  if (discard_virtual_memory ==
-      reinterpret_cast<DiscardVirtualMemoryFunction>(-1))
-    discard_virtual_memory =
-        reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
-            GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
-  // Use DiscardVirtualMemory when available because it releases faster than
-  // MEM_RESET.
-  DWORD ret = 1;
-  if (discard_virtual_memory)
-    ret = discard_virtual_memory(address, length);
-  // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on
-  // failure.
-  if (ret) {
-    void* ret = VirtualAlloc(address, length, MEM_RESET, PAGE_READWRITE);
-    CHECK(ret);
+  DCHECK_EQ(0UL, length & kSystemPageOffsetMask);
+  DiscardSystemPagesInternal(address, length);
+}
+
+bool ReserveAddressSpace(size_t size) {
+  // To avoid deadlock, call only SystemAllocPages.
+  subtle::SpinLock::Guard guard(*GetReserveLock());
+  if (s_reservation_address == nullptr) {
+    void* mem = SystemAllocPages(nullptr, size, PageInaccessible,
+                                 PageTag::kChromium, false);
+    if (mem != nullptr) {
+      // We guarantee this alignment when reserving address space.
+      DCHECK(!(reinterpret_cast<uintptr_t>(mem) &
+               kPageAllocationGranularityOffsetMask));
+      s_reservation_address = mem;
+      s_reservation_size = size;
+      return true;
+    }
   }
-#endif
+  return false;
+}
+
+void ReleaseReservation() {
+  // To avoid deadlock, call only FreePages.
+  subtle::SpinLock::Guard guard(*GetReserveLock());
+  if (s_reservation_address != nullptr) {
+    FreePages(s_reservation_address, s_reservation_size);
+    s_reservation_address = nullptr;
+    s_reservation_size = 0;
+  }
 }
 
 uint32_t GetAllocPageErrorCode() {
diff --git a/third_party/base/allocator/partition_allocator/page_allocator.h b/third_party/base/allocator/partition_allocator/page_allocator.h
index aee2532..9b076f9 100644
--- a/third_party/base/allocator/partition_allocator/page_allocator.h
+++ b/third_party/base/allocator/partition_allocator/page_allocator.h
@@ -2,132 +2,194 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H
-#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
 
 #include <stdint.h>
 
 #include <cstddef>
 
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/page_allocator_constants.h"
 #include "third_party/base/base_export.h"
 #include "third_party/base/compiler_specific.h"
-#include "third_party/build/build_config.h"
 
 namespace pdfium {
 namespace base {
 
-#if defined(OS_WIN)
-static const size_t kPageAllocationGranularityShift = 16;  // 64KB
-#elif defined(_MIPS_ARCH_LOONGSON)
-static const size_t kPageAllocationGranularityShift = 14;  // 16KB
-#else
-static const size_t kPageAllocationGranularityShift = 12;  // 4KB
-#endif
-static const size_t kPageAllocationGranularity =
-    1 << kPageAllocationGranularityShift;
-static const size_t kPageAllocationGranularityOffsetMask =
-    kPageAllocationGranularity - 1;
-static const size_t kPageAllocationGranularityBaseMask =
-    ~kPageAllocationGranularityOffsetMask;
-
-// All Blink-supported systems have 4096 sized system pages and can handle
-// permissions and commit / decommit at this granularity.
-// Loongson have 16384 sized system pages.
-#if defined(_MIPS_ARCH_LOONGSON)
-static const size_t kSystemPageSize = 16384;
-#else
-static const size_t kSystemPageSize = 4096;
-#endif
-static const size_t kSystemPageOffsetMask = kSystemPageSize - 1;
-static const size_t kSystemPageBaseMask = ~kSystemPageOffsetMask;
-
 enum PageAccessibilityConfiguration {
-  PageAccessible,
   PageInaccessible,
+  PageRead,
+  PageReadWrite,
+  PageReadExecute,
+  // This flag is deprecated and will go away soon.
+  // TODO(bbudge) Remove this as soon as V8 doesn't need RWX pages.
+  PageReadWriteExecute,
+};
+
+// macOS supports tagged memory regions, to help in debugging. On Android,
+// these tags are used to name anonymous mappings.
+enum class PageTag {
+  kFirst = 240,           // Minimum tag value.
+  kBlinkGC = 252,         // Blink GC pages.
+  kPartitionAlloc = 253,  // PartitionAlloc, no matter the partition.
+  kChromium = 254,        // Chromium page.
+  kV8 = 255,              // V8 heap pages.
+  kLast = kV8             // Maximum tag value.
 };
 
 // Allocate one or more pages.
-// The requested address is just a hint; the actual address returned may
-// differ. The returned address will be aligned at least to align bytes.
-// len is in bytes, and must be a multiple of kPageAllocationGranularity.
-// align is in bytes, and must be a power-of-two multiple of
-// kPageAllocationGranularity.
-// If addr is null, then a suitable and randomized address will be chosen
+//
+// The requested |address| is just a hint; the actual address returned may
+// differ. The returned address will be aligned at least to |align| bytes.
+// |length| is in bytes, and must be a multiple of |kPageAllocationGranularity|.
+// |align| is in bytes, and must be a power-of-two multiple of
+// |kPageAllocationGranularity|.
+//
+// If |address| is null, then a suitable and randomized address will be chosen
 // automatically.
-// PageAccessibilityConfiguration controls the permission of the
-// allocated pages.
+//
+// |page_accessibility| controls the permission of the allocated pages.
+// |page_tag| is used on some platforms to identify the source of the
+// allocation. Use PageTag::kChromium as a catch-all category.
+//
 // This call will return null if the allocation cannot be satisfied.
 BASE_EXPORT void* AllocPages(void* address,
-                             size_t len,
+                             size_t length,
                              size_t align,
-                             PageAccessibilityConfiguration);
+                             PageAccessibilityConfiguration page_accessibility,
+                             PageTag tag,
+                             bool commit = true);
 
-// Free one or more pages.
-// addr and len must match a previous call to allocPages().
+// Free one or more pages starting at |address| and continuing for |length|
+// bytes.
+//
+// |address| and |length| must match a previous call to |AllocPages|. Therefore,
+// |address| must be aligned to |kPageAllocationGranularity| bytes, and |length|
+// must be a multiple of |kPageAllocationGranularity|.
 BASE_EXPORT void FreePages(void* address, size_t length);
 
-// Mark one or more system pages as being inaccessible.
-// Subsequently accessing any address in the range will fault, and the
-// addresses will not be re-used by future allocations.
-// len must be a multiple of kSystemPageSize bytes.
-BASE_EXPORT void SetSystemPagesInaccessible(void* address, size_t length);
+// Mark one or more system pages, starting at |address| with the given
+// |page_accessibility|. |length| must be a multiple of |kSystemPageSize| bytes.
+//
+// Returns true if the permission change succeeded. In most cases you must
+// |CHECK| the result.
+BASE_EXPORT WARN_UNUSED_RESULT bool TrySetSystemPagesAccess(
+    void* address,
+    size_t length,
+    PageAccessibilityConfiguration page_accessibility);
 
-// Mark one or more system pages as being accessible.
-// The pages will be readable and writeable.
-// len must be a multiple of kSystemPageSize bytes.
-// The result bool value indicates whether the permission
-// change succeeded or not. You must check the result
-// (in most cases you need to CHECK that it is true).
-BASE_EXPORT WARN_UNUSED_RESULT bool SetSystemPagesAccessible(void* address,
-                                                             size_t length);
+// Mark one or more system pages, starting at |address| with the given
+// |page_accessibility|. |length| must be a multiple of |kSystemPageSize| bytes.
+//
+// Performs a CHECK that the operation succeeds.
+BASE_EXPORT void SetSystemPagesAccess(
+    void* address,
+    size_t length,
+    PageAccessibilityConfiguration page_accessibility);
 
-// Decommit one or more system pages. Decommitted means that the physical memory
-// is released to the system, but the virtual address space remains reserved.
-// System pages are re-committed by calling recommitSystemPages(). Touching
-// a decommitted page _may_ fault.
-// Clients should not make any assumptions about the contents of decommitted
-// system pages, before or after they write to the page. The only guarantee
-// provided is that the contents of the system page will be deterministic again
-// after recommitting and writing to it. In particlar note that system pages are
-// not guaranteed to be zero-filled upon re-commit. len must be a multiple of
-// kSystemPageSize bytes.
+// Decommit one or more system pages starting at |address| and continuing for
+// |length| bytes. |length| must be a multiple of |kSystemPageSize|.
+//
+// Decommitted means that physical resources (RAM or swap) backing the allocated
+// virtual address range are released back to the system, but the address space
+// is still allocated to the process (possibly using up page table entries or
+// other accounting resources). Any access to a decommitted region of memory
+// is an error and will generate a fault.
+//
+// This operation is not atomic on all platforms.
+//
+// Note: "Committed memory" is a Windows Memory Subsystem concept that ensures
+// processes will not fault when touching a committed memory region. There is
+// no analogue in the POSIX memory API where virtual memory pages are
+// best-effort allocated resources on the first touch. To create a
+// platform-agnostic abstraction, this API simulates the Windows "decommit"
+// state by both discarding the region (allowing the OS to avoid swap
+// operations) and changing the page protections so accesses fault.
+//
+// TODO(ajwong): This currently does not change page protections on POSIX
+// systems due to a perf regression. Tracked at http://crbug.com/766882.
 BASE_EXPORT void DecommitSystemPages(void* address, size_t length);
 
-// Recommit one or more system pages. Decommitted system pages must be
-// recommitted before they are read are written again.
-// Note that this operation may be a no-op on some platforms.
-// len must be a multiple of kSystemPageSize bytes.
-BASE_EXPORT void RecommitSystemPages(void* address, size_t length);
+// Recommit one or more system pages, starting at |address| and continuing for
+// |length| bytes with the given |page_accessibility|. |length| must be a
+// multiple of |kSystemPageSize|.
+//
+// Decommitted system pages must be recommitted with their original permissions
+// before they are used again.
+//
+// Returns true if the recommit change succeeded. In most cases you must |CHECK|
+// the result.
+BASE_EXPORT WARN_UNUSED_RESULT bool RecommitSystemPages(
+    void* address,
+    size_t length,
+    PageAccessibilityConfiguration page_accessibility);
 
-// Discard one or more system pages. Discarding is a hint to the system that
-// the page is no longer required. The hint may:
-// - Do nothing.
-// - Discard the page immediately, freeing up physical pages.
-// - Discard the page at some time in the future in response to memory pressure.
-// Only committed pages should be discarded. Discarding a page does not
-// decommit it, and it is valid to discard an already-discarded page.
-// A read or write to a discarded page will not fault.
-// Reading from a discarded page may return the original page content, or a
-// page full of zeroes.
+// Discard one or more system pages starting at |address| and continuing for
+// |length| bytes. |length| must be a multiple of |kSystemPageSize|.
+//
+// Discarding is a hint to the system that the page is no longer required. The
+// hint may:
+//   - Do nothing.
+//   - Discard the page immediately, freeing up physical pages.
+//   - Discard the page at some time in the future in response to memory
+//   pressure.
+//
+// Only committed pages should be discarded. Discarding a page does not decommit
+// it, and it is valid to discard an already-discarded page. A read or write to
+// a discarded page will not fault.
+//
+// Reading from a discarded page may return the original page content, or a page
+// full of zeroes.
+//
 // Writing to a discarded page is the only guaranteed way to tell the system
 // that the page is required again. Once written to, the content of the page is
 // guaranteed stable once more. After being written to, the page content may be
 // based on the original page content, or a page of zeroes.
-// len must be a multiple of kSystemPageSize bytes.
 BASE_EXPORT void DiscardSystemPages(void* address, size_t length);
 
-ALWAYS_INLINE uintptr_t RoundUpToSystemPage(uintptr_t address) {
+// Rounds up |address| to the next multiple of |kSystemPageSize|. Returns
+// 0 for an |address| of 0.
+constexpr ALWAYS_INLINE uintptr_t RoundUpToSystemPage(uintptr_t address) {
   return (address + kSystemPageOffsetMask) & kSystemPageBaseMask;
 }
 
-ALWAYS_INLINE uintptr_t RoundDownToSystemPage(uintptr_t address) {
+// Rounds down |address| to the previous multiple of |kSystemPageSize|. Returns
+// 0 for an |address| of 0.
+constexpr ALWAYS_INLINE uintptr_t RoundDownToSystemPage(uintptr_t address) {
   return address & kSystemPageBaseMask;
 }
 
-// Returns errno (or GetLastError code) when mmap (or VirtualAlloc) fails.
+// Rounds up |address| to the next multiple of |kPageAllocationGranularity|.
+// Returns 0 for an |address| of 0.
+constexpr ALWAYS_INLINE uintptr_t
+RoundUpToPageAllocationGranularity(uintptr_t address) {
+  return (address + kPageAllocationGranularityOffsetMask) &
+         kPageAllocationGranularityBaseMask;
+}
+
+// Rounds down |address| to the previous multiple of
+// |kPageAllocationGranularity|. Returns 0 for an |address| of 0.
+constexpr ALWAYS_INLINE uintptr_t
+RoundDownToPageAllocationGranularity(uintptr_t address) {
+  return address & kPageAllocationGranularityBaseMask;
+}
+
+// Reserves (at least) |size| bytes of address space, aligned to
+// |kPageAllocationGranularity|. This can be called early on to make it more
+// likely that large allocations will succeed. Returns true if the reservation
+// succeeded, false if the reservation failed or a reservation was already made.
+BASE_EXPORT bool ReserveAddressSpace(size_t size);
+
+// Releases any reserved address space. |AllocPages| calls this automatically on
+// an allocation failure. External allocators may also call this on failure.
+BASE_EXPORT void ReleaseReservation();
+
+// Returns |errno| (POSIX) or the result of |GetLastError| (Windows) when |mmap|
+// (POSIX) or |VirtualAlloc| (Windows) fails.
 BASE_EXPORT uint32_t GetAllocPageErrorCode();
 
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_constants.h b/third_party/base/allocator/partition_allocator/page_allocator_constants.h
new file mode 100644
index 0000000..567e3a3
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/page_allocator_constants.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_
+
+#include <stddef.h>
+
+#include "build/build_config.h"
+
+namespace pdfium {
+namespace base {
+#if defined(OS_WIN) || defined(ARCH_CPU_PPC64)
+static constexpr size_t kPageAllocationGranularityShift = 16;  // 64KB
+#elif defined(_MIPS_ARCH_LOONGSON)
+static constexpr size_t kPageAllocationGranularityShift = 14;  // 16KB
+#else
+static constexpr size_t kPageAllocationGranularityShift = 12;  // 4KB
+#endif
+static constexpr size_t kPageAllocationGranularity =
+    1 << kPageAllocationGranularityShift;
+static constexpr size_t kPageAllocationGranularityOffsetMask =
+    kPageAllocationGranularity - 1;
+static constexpr size_t kPageAllocationGranularityBaseMask =
+    ~kPageAllocationGranularityOffsetMask;
+
+#if defined(_MIPS_ARCH_LOONGSON)
+static constexpr size_t kSystemPageSize = 16384;
+#elif defined(ARCH_CPU_PPC64)
+// Modern ppc64 systems support 4KB and 64KB page sizes.
+// Since 64KB is the de-facto standard on the platform
+// and binaries compiled for 64KB are likely to work on 4KB systems,
+// 64KB is a good choice here.
+static constexpr size_t kSystemPageSize = 65536;
+#else
+static constexpr size_t kSystemPageSize = 4096;
+#endif
+static constexpr size_t kSystemPageOffsetMask = kSystemPageSize - 1;
+static_assert((kSystemPageSize & (kSystemPageSize - 1)) == 0,
+              "kSystemPageSize must be power of 2");
+static constexpr size_t kSystemPageBaseMask = ~kSystemPageOffsetMask;
+
+static constexpr size_t kPageMetadataShift = 5;  // 32 bytes per partition page.
+static constexpr size_t kPageMetadataSize = 1 << kPageMetadataShift;
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_internal.h b/third_party/base/allocator/partition_allocator/page_allocator_internal.h
new file mode 100644
index 0000000..2284314
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/page_allocator_internal.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_
+
+namespace pdfium {
+namespace base {
+
+void* SystemAllocPages(void* hint,
+                       size_t length,
+                       PageAccessibilityConfiguration accessibility,
+                       PageTag page_tag,
+                       bool commit);
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h b/third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h
new file mode 100644
index 0000000..2d62cb8
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h
@@ -0,0 +1,216 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
+
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <mach/mach.h>
+#endif
+#if defined(OS_ANDROID)
+#include <sys/prctl.h>
+#endif
+#if defined(OS_LINUX)
+#include <sys/resource.h>
+
+#include <algorithm>
+#endif
+
+#include "third_party/base/allocator/partition_allocator/page_allocator.h"
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+namespace pdfium {
+namespace base {
+
+#if defined(OS_ANDROID)
+namespace {
+const char* PageTagToName(PageTag tag) {
+  // Important: All the names should be string literals. As per prctl.h in
+  // //third_party/android_ndk the kernel keeps a pointer to the name instead
+  // of copying it.
+  //
+  // Having the name in .rodata ensures that the pointer remains valid as
+  // long as the mapping is alive.
+  switch (tag) {
+    case PageTag::kBlinkGC:
+      return "blink_gc";
+    case PageTag::kPartitionAlloc:
+      return "partition_alloc";
+    case PageTag::kChromium:
+      return "chromium";
+    case PageTag::kV8:
+      return "v8";
+    default:
+      DCHECK(false);
+      return "";
+  }
+}
+}  // namespace
+#endif  // defined(OS_ANDROID)
+
+// |mmap| uses a nearby address if the hint address is blocked.
+constexpr bool kHintIsAdvisory = true;
+std::atomic<int32_t> s_allocPageErrorCode{0};
+
+int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
+  switch (accessibility) {
+    case PageRead:
+      return PROT_READ;
+    case PageReadWrite:
+      return PROT_READ | PROT_WRITE;
+    case PageReadExecute:
+      return PROT_READ | PROT_EXEC;
+    case PageReadWriteExecute:
+      return PROT_READ | PROT_WRITE | PROT_EXEC;
+    default:
+      NOTREACHED();
+      FALLTHROUGH;
+    case PageInaccessible:
+      return PROT_NONE;
+  }
+}
+
+void* SystemAllocPagesInternal(void* hint,
+                               size_t length,
+                               PageAccessibilityConfiguration accessibility,
+                               PageTag page_tag,
+                               bool commit) {
+#if defined(OS_MACOSX)
+  // Use a custom tag to make it easier to distinguish Partition Alloc regions
+  // in vmmap(1). Tags between 240-255 are supported.
+  DCHECK(PageTag::kFirst <= page_tag);
+  DCHECK(PageTag::kLast >= page_tag);
+  int fd = VM_MAKE_TAG(static_cast<int>(page_tag));
+#else
+  int fd = -1;
+#endif
+
+  int access_flag = GetAccessFlags(accessibility);
+  int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
+
+  // TODO(https://crbug.com/927411): Remove once Fuchsia uses a native page
+  // allocator, rather than relying on POSIX compatibility.
+#if defined(OS_FUCHSIA)
+  if (page_tag == PageTag::kV8) {
+    map_flags |= MAP_JIT;
+  }
+#endif
+
+  void* ret = mmap(hint, length, access_flag, map_flags, fd, 0);
+  if (ret == MAP_FAILED) {
+    s_allocPageErrorCode = errno;
+    ret = nullptr;
+  }
+
+#if defined(OS_ANDROID)
+  // On Android, anonymous mappings can have a name attached to them. This is
+  // useful for debugging, and double-checking memory attribution.
+  if (ret) {
+    // No error checking on purpose, testing only.
+    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ret, length,
+          PageTagToName(page_tag));
+  }
+#endif
+
+  return ret;
+}
+
+void* TrimMappingInternal(void* base,
+                          size_t base_length,
+                          size_t trim_length,
+                          PageAccessibilityConfiguration accessibility,
+                          bool commit,
+                          size_t pre_slack,
+                          size_t post_slack) {
+  void* ret = base;
+  // We can resize the allocation run. Release unneeded memory before and after
+  // the aligned range.
+  if (pre_slack) {
+    int res = munmap(base, pre_slack);
+    CHECK(!res);
+    ret = reinterpret_cast<char*>(base) + pre_slack;
+  }
+  if (post_slack) {
+    int res = munmap(reinterpret_cast<char*>(ret) + trim_length, post_slack);
+    CHECK(!res);
+  }
+  return ret;
+}
+
+bool TrySetSystemPagesAccessInternal(
+    void* address,
+    size_t length,
+    PageAccessibilityConfiguration accessibility) {
+  return 0 == mprotect(address, length, GetAccessFlags(accessibility));
+}
+
+void SetSystemPagesAccessInternal(
+    void* address,
+    size_t length,
+    PageAccessibilityConfiguration accessibility) {
+  CHECK_EQ(0, mprotect(address, length, GetAccessFlags(accessibility)));
+}
+
+void FreePagesInternal(void* address, size_t length) {
+  CHECK(!munmap(address, length));
+}
+
+void DecommitSystemPagesInternal(void* address, size_t length) {
+  // In POSIX, there is no decommit concept. Discarding is an effective way of
+  // implementing the Windows semantics where the OS is allowed to not swap the
+  // pages in the region.
+  //
+  // TODO(ajwong): Also explore setting PageInaccessible to make the protection
+  // semantics consistent between Windows and POSIX. This might have a perf cost
+  // though as both decommit and recommit would incur an extra syscall.
+  // http://crbug.com/766882
+  DiscardSystemPages(address, length);
+}
+
+bool RecommitSystemPagesInternal(void* address,
+                                 size_t length,
+                                 PageAccessibilityConfiguration accessibility) {
+#if defined(OS_MACOSX)
+  // On macOS, to update accounting, we need to make another syscall. For more
+  // details, see https://crbug.com/823915.
+  madvise(address, length, MADV_FREE_REUSE);
+#endif
+
+  // On POSIX systems, the caller need simply read the memory to recommit it.
+  // This has the correct behavior because the API requires the permissions to
+  // be the same as before decommitting and all configurations can read.
+  return true;
+}
+
+void DiscardSystemPagesInternal(void* address, size_t length) {
+#if defined(OS_MACOSX)
+  int ret = madvise(address, length, MADV_FREE_REUSABLE);
+  if (ret) {
+    // MADV_FREE_REUSABLE sometimes fails, so fall back to MADV_DONTNEED.
+    ret = madvise(address, length, MADV_DONTNEED);
+  }
+  CHECK(0 == ret);
+#else
+  // We have experimented with other flags, but with suboptimal results.
+  //
+  // MADV_FREE (Linux): Makes our memory measurements less predictable;
+  // performance benefits unclear.
+  //
+  // Therefore, we just do the simple thing: MADV_DONTNEED.
+  CHECK(!madvise(address, length, MADV_DONTNEED));
+#endif
+}
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
diff --git a/third_party/base/allocator/partition_allocator/page_allocator_internals_win.h b/third_party/base/allocator/partition_allocator/page_allocator_internals_win.h
new file mode 100644
index 0000000..2ba0a25
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/page_allocator_internals_win.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
+
+#include "third_party/base/allocator/partition_allocator/oom.h"
+#include "third_party/base/allocator/partition_allocator/page_allocator_internal.h"
+
+namespace pdfium {
+namespace base {
+
+// |VirtualAlloc| will fail if allocation at the hint address is blocked.
+constexpr bool kHintIsAdvisory = false;
+std::atomic<int32_t> s_allocPageErrorCode{ERROR_SUCCESS};
+
+int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
+  switch (accessibility) {
+    case PageRead:
+      return PAGE_READONLY;
+    case PageReadWrite:
+      return PAGE_READWRITE;
+    case PageReadExecute:
+      return PAGE_EXECUTE_READ;
+    case PageReadWriteExecute:
+      return PAGE_EXECUTE_READWRITE;
+    default:
+      NOTREACHED();
+      FALLTHROUGH;
+    case PageInaccessible:
+      return PAGE_NOACCESS;
+  }
+}
+
+void* SystemAllocPagesInternal(void* hint,
+                               size_t length,
+                               PageAccessibilityConfiguration accessibility,
+                               PageTag page_tag,
+                               bool commit) {
+  DWORD access_flag = GetAccessFlags(accessibility);
+  const DWORD type_flags = commit ? (MEM_RESERVE | MEM_COMMIT) : MEM_RESERVE;
+  void* ret = VirtualAlloc(hint, length, type_flags, access_flag);
+  if (ret == nullptr) {
+    s_allocPageErrorCode = GetLastError();
+  }
+  return ret;
+}
+
+void* TrimMappingInternal(void* base,
+                          size_t base_length,
+                          size_t trim_length,
+                          PageAccessibilityConfiguration accessibility,
+                          bool commit,
+                          size_t pre_slack,
+                          size_t post_slack) {
+  void* ret = base;
+  if (pre_slack || post_slack) {
+    // We cannot resize the allocation run. Free it and retry at the aligned
+    // address within the freed range.
+    ret = reinterpret_cast<char*>(base) + pre_slack;
+    FreePages(base, base_length);
+    ret = SystemAllocPages(ret, trim_length, accessibility, PageTag::kChromium,
+                           commit);
+  }
+  return ret;
+}
+
+bool TrySetSystemPagesAccessInternal(
+    void* address,
+    size_t length,
+    PageAccessibilityConfiguration accessibility) {
+  if (accessibility == PageInaccessible)
+    return VirtualFree(address, length, MEM_DECOMMIT) != 0;
+  return nullptr != VirtualAlloc(address, length, MEM_COMMIT,
+                                 GetAccessFlags(accessibility));
+}
+
+void SetSystemPagesAccessInternal(
+    void* address,
+    size_t length,
+    PageAccessibilityConfiguration accessibility) {
+  if (accessibility == PageInaccessible) {
+    if (!VirtualFree(address, length, MEM_DECOMMIT)) {
+      // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
+      // report we get the error number.
+      CHECK_EQ(static_cast<uint32_t>(ERROR_SUCCESS), GetLastError());
+    }
+  } else {
+    if (!VirtualAlloc(address, length, MEM_COMMIT,
+                      GetAccessFlags(accessibility))) {
+      int32_t error = GetLastError();
+      if (error == ERROR_COMMITMENT_LIMIT)
+        OOM_CRASH();
+      // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash
+      // report we get the error number.
+      CHECK_EQ(ERROR_SUCCESS, error);
+    }
+  }
+}
+
+void FreePagesInternal(void* address, size_t length) {
+  CHECK(VirtualFree(address, 0, MEM_RELEASE));
+}
+
+void DecommitSystemPagesInternal(void* address, size_t length) {
+  SetSystemPagesAccess(address, length, PageInaccessible);
+}
+
+bool RecommitSystemPagesInternal(void* address,
+                                 size_t length,
+                                 PageAccessibilityConfiguration accessibility) {
+  return TrySetSystemPagesAccess(address, length, accessibility);
+}
+
+void DiscardSystemPagesInternal(void* address, size_t length) {
+  // On Windows, discarded pages are not returned to the system immediately and
+  // not guaranteed to be zeroed when returned to the application.
+  using DiscardVirtualMemoryFunction =
+      DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size);
+  static DiscardVirtualMemoryFunction discard_virtual_memory =
+      reinterpret_cast<DiscardVirtualMemoryFunction>(-1);
+  if (discard_virtual_memory ==
+      reinterpret_cast<DiscardVirtualMemoryFunction>(-1))
+    discard_virtual_memory =
+        reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
+            GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
+  // Use DiscardVirtualMemory when available because it releases faster than
+  // MEM_RESET.
+  DWORD ret = 1;
+  if (discard_virtual_memory) {
+    ret = discard_virtual_memory(address, length);
+  }
+  // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on
+  // failure.
+  if (ret) {
+    void* ptr = VirtualAlloc(address, length, MEM_RESET, PAGE_READWRITE);
+    CHECK(ptr);
+  }
+}
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_alloc.cc b/third_party/base/allocator/partition_allocator/partition_alloc.cc
index ff366b8..19ce17b 100644
--- a/third_party/base/allocator/partition_allocator/partition_alloc.cc
+++ b/third_party/base/allocator/partition_allocator/partition_alloc.cc
@@ -6,172 +6,208 @@
 
 #include <string.h>
 
-#include "third_party/base/allocator/partition_allocator/oom.h"
-#include "third_party/base/allocator/partition_allocator/spin_lock.h"
-#include "third_party/base/compiler_specific.h"
+#include <memory>
+#include <type_traits>
 
-// Two partition pages are used as guard / metadata page so make sure the super
-// page size is bigger.
-static_assert(pdfium::base::kPartitionPageSize * 4 <=
-                  pdfium::base::kSuperPageSize,
-              "ok super page size");
-static_assert(!(pdfium::base::kSuperPageSize %
-                pdfium::base::kPartitionPageSize),
-              "ok super page multiple");
-// Four system pages gives us room to hack out a still-guard-paged piece
-// of metadata in the middle of a guard partition page.
-static_assert(pdfium::base::kSystemPageSize * 4 <=
-                  pdfium::base::kPartitionPageSize,
-              "ok partition page size");
-static_assert(!(pdfium::base::kPartitionPageSize %
-                pdfium::base::kSystemPageSize),
-              "ok partition page multiple");
-static_assert(sizeof(pdfium::base::PartitionPage) <=
-                  pdfium::base::kPageMetadataSize,
-              "PartitionPage should not be too big");
-static_assert(sizeof(pdfium::base::PartitionBucket) <=
-                  pdfium::base::kPageMetadataSize,
-              "PartitionBucket should not be too big");
-static_assert(sizeof(pdfium::base::PartitionSuperPageExtentEntry) <=
-                  pdfium::base::kPageMetadataSize,
-              "PartitionSuperPageExtentEntry should not be too big");
-static_assert(pdfium::base::kPageMetadataSize *
-                      pdfium::base::kNumPartitionPagesPerSuperPage <=
-                  pdfium::base::kSystemPageSize,
-              "page metadata fits in hole");
-// Check that some of our zanier calculations worked out as expected.
-static_assert(pdfium::base::kGenericSmallestBucket == 8,
-              "generic smallest bucket");
-static_assert(pdfium::base::kGenericMaxBucketed == 983040,
-              "generic max bucketed");
-static_assert(pdfium::base::kMaxSystemPagesPerSlotSpan < (1 << 8),
-              "System pages per slot span must be less than 128.");
+#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
+#include "third_party/base/allocator/partition_allocator/partition_oom.h"
+#include "third_party/base/allocator/partition_allocator/partition_page.h"
+#include "third_party/base/allocator/partition_allocator/spin_lock.h"
 
 namespace pdfium {
 namespace base {
 
-subtle::SpinLock PartitionRootBase::gInitializedLock;
-bool PartitionRootBase::gInitialized = false;
-PartitionPage PartitionRootBase::gSeedPage;
-PartitionBucket PartitionRootBase::gPagedBucket;
-void (*PartitionRootBase::gOomHandlingFunction)() = nullptr;
-PartitionAllocHooks::AllocationHook* PartitionAllocHooks::allocation_hook_ =
-    nullptr;
-PartitionAllocHooks::FreeHook* PartitionAllocHooks::free_hook_ = nullptr;
+// Two partition pages are used as guard / metadata page so make sure the super
+// page size is bigger.
+static_assert(kPartitionPageSize * 4 <= kSuperPageSize, "ok super page size");
+static_assert(!(kSuperPageSize % kPartitionPageSize), "ok super page multiple");
+// Four system pages gives us room to hack out a still-guard-paged piece
+// of metadata in the middle of a guard partition page.
+static_assert(kSystemPageSize * 4 <= kPartitionPageSize,
+              "ok partition page size");
+static_assert(!(kPartitionPageSize % kSystemPageSize),
+              "ok partition page multiple");
+static_assert(sizeof(internal::PartitionPage) <= kPageMetadataSize,
+              "PartitionPage should not be too big");
+static_assert(sizeof(internal::PartitionBucket) <= kPageMetadataSize,
+              "PartitionBucket should not be too big");
+static_assert(sizeof(internal::PartitionSuperPageExtentEntry) <=
+                  kPageMetadataSize,
+              "PartitionSuperPageExtentEntry should not be too big");
+static_assert(kPageMetadataSize * kNumPartitionPagesPerSuperPage <=
+                  kSystemPageSize,
+              "page metadata fits in hole");
+// Limit to prevent callers accidentally overflowing an int size.
+static_assert(kGenericMaxDirectMapped <=
+                  (1UL << 31) + kPageAllocationGranularity,
+              "maximum direct mapped allocation");
+// Check that some of our zanier calculations worked out as expected.
+static_assert(kGenericSmallestBucket == 8, "generic smallest bucket");
+static_assert(kGenericMaxBucketed == 983040, "generic max bucketed");
+static_assert(kMaxSystemPagesPerSlotSpan < (1 << 8),
+              "System pages per slot span must be less than 128.");
 
-static uint8_t PartitionBucketNumSystemPages(size_t size) {
-  // This works out reasonably for the current bucket sizes of the generic
-  // allocator, and the current values of partition page size and constants.
-  // Specifically, we have enough room to always pack the slots perfectly into
-  // some number of system pages. The only waste is the waste associated with
-  // unfaulted pages (i.e. wasted address space).
-  // TODO: we end up using a lot of system pages for very small sizes. For
-  // example, we'll use 12 system pages for slot size 24. The slot size is
-  // so small that the waste would be tiny with just 4, or 1, system pages.
-  // Later, we can investigate whether there are anti-fragmentation benefits
-  // to using fewer system pages.
-  double best_waste_ratio = 1.0f;
-  uint16_t best_pages = 0;
-  if (size > kMaxSystemPagesPerSlotSpan * kSystemPageSize) {
-    DCHECK(!(size % kSystemPageSize));
-    best_pages = static_cast<uint16_t>(size / kSystemPageSize);
-    CHECK(best_pages < (1 << 8));
-    return static_cast<uint8_t>(best_pages);
-  }
-  DCHECK(size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize);
-  for (uint16_t i = kNumSystemPagesPerPartitionPage - 1;
-       i <= kMaxSystemPagesPerSlotSpan; ++i) {
-    size_t page_size = kSystemPageSize * i;
-    size_t num_slots = page_size / size;
-    size_t waste = page_size - (num_slots * size);
-    // Leaving a page unfaulted is not free; the page will occupy an empty page
-    // table entry.  Make a simple attempt to account for that.
-    size_t num_remainder_pages = i & (kNumSystemPagesPerPartitionPage - 1);
-    size_t num_unfaulted_pages =
-        num_remainder_pages
-            ? (kNumSystemPagesPerPartitionPage - num_remainder_pages)
-            : 0;
-    waste += sizeof(void*) * num_unfaulted_pages;
-    double waste_ratio = (double)waste / (double)page_size;
-    if (waste_ratio < best_waste_ratio) {
-      best_waste_ratio = waste_ratio;
-      best_pages = i;
-    }
-  }
-  DCHECK(best_pages > 0);
-  CHECK(best_pages <= kMaxSystemPagesPerSlotSpan);
-  return static_cast<uint8_t>(best_pages);
+internal::PartitionRootBase::PartitionRootBase() = default;
+internal::PartitionRootBase::~PartitionRootBase() = default;
+PartitionRoot::PartitionRoot() = default;
+PartitionRoot::~PartitionRoot() = default;
+PartitionRootGeneric::PartitionRootGeneric() = default;
+PartitionRootGeneric::~PartitionRootGeneric() = default;
+PartitionAllocatorGeneric::PartitionAllocatorGeneric() = default;
+PartitionAllocatorGeneric::~PartitionAllocatorGeneric() = default;
+
+subtle::SpinLock* GetLock() {
+  static subtle::SpinLock* s_initialized_lock = nullptr;
+  if (!s_initialized_lock)
+    s_initialized_lock = new subtle::SpinLock();
+  return s_initialized_lock;
 }
 
-static void PartitionAllocBaseInit(PartitionRootBase* root) {
+static bool g_initialized = false;
+
+void (*internal::PartitionRootBase::gOomHandlingFunction)() = nullptr;
+std::atomic<bool> PartitionAllocHooks::hooks_enabled_(false);
+subtle::SpinLock PartitionAllocHooks::set_hooks_lock_;
+std::atomic<PartitionAllocHooks::AllocationObserverHook*>
+    PartitionAllocHooks::allocation_observer_hook_(nullptr);
+std::atomic<PartitionAllocHooks::FreeObserverHook*>
+    PartitionAllocHooks::free_observer_hook_(nullptr);
+std::atomic<PartitionAllocHooks::AllocationOverrideHook*>
+    PartitionAllocHooks::allocation_override_hook_(nullptr);
+std::atomic<PartitionAllocHooks::FreeOverrideHook*>
+    PartitionAllocHooks::free_override_hook_(nullptr);
+std::atomic<PartitionAllocHooks::ReallocOverrideHook*>
+    PartitionAllocHooks::realloc_override_hook_(nullptr);
+
+void PartitionAllocHooks::SetObserverHooks(AllocationObserverHook* alloc_hook,
+                                           FreeObserverHook* free_hook) {
+  subtle::SpinLock::Guard guard(set_hooks_lock_);
+
+  // Chained hooks are not supported. Registering a non-null hook when a
+  // non-null hook is already registered indicates somebody is trying to
+  // overwrite a hook.
+  CHECK((!allocation_observer_hook_ && !free_observer_hook_) ||
+        (!alloc_hook && !free_hook));
+  allocation_observer_hook_ = alloc_hook;
+  free_observer_hook_ = free_hook;
+
+  hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
+}
+
+void PartitionAllocHooks::SetOverrideHooks(AllocationOverrideHook* alloc_hook,
+                                           FreeOverrideHook* free_hook,
+                                           ReallocOverrideHook realloc_hook) {
+  subtle::SpinLock::Guard guard(set_hooks_lock_);
+
+  CHECK((!allocation_override_hook_ && !free_override_hook_ &&
+         !realloc_override_hook_) ||
+        (!alloc_hook && !free_hook && !realloc_hook));
+  allocation_override_hook_ = alloc_hook;
+  free_override_hook_ = free_hook;
+  realloc_override_hook_ = realloc_hook;
+
+  hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
+}
+
+void PartitionAllocHooks::AllocationObserverHookIfEnabled(
+    void* address,
+    size_t size,
+    const char* type_name) {
+  if (AllocationObserverHook* hook =
+          allocation_observer_hook_.load(std::memory_order_relaxed)) {
+    hook(address, size, type_name);
+  }
+}
+
+bool PartitionAllocHooks::AllocationOverrideHookIfEnabled(
+    void** out,
+    int flags,
+    size_t size,
+    const char* type_name) {
+  if (AllocationOverrideHook* hook =
+          allocation_override_hook_.load(std::memory_order_relaxed)) {
+    return hook(out, flags, size, type_name);
+  }
+  return false;
+}
+
+void PartitionAllocHooks::FreeObserverHookIfEnabled(void* address) {
+  if (FreeObserverHook* hook =
+          free_observer_hook_.load(std::memory_order_relaxed)) {
+    hook(address);
+  }
+}
+
+bool PartitionAllocHooks::FreeOverrideHookIfEnabled(void* address) {
+  if (FreeOverrideHook* hook =
+          free_override_hook_.load(std::memory_order_relaxed)) {
+    return hook(address);
+  }
+  return false;
+}
+
+void PartitionAllocHooks::ReallocObserverHookIfEnabled(void* old_address,
+                                                       void* new_address,
+                                                       size_t size,
+                                                       const char* type_name) {
+  // Report a reallocation as a free followed by an allocation.
+  AllocationObserverHook* allocation_hook =
+      allocation_observer_hook_.load(std::memory_order_relaxed);
+  FreeObserverHook* free_hook =
+      free_observer_hook_.load(std::memory_order_relaxed);
+  if (allocation_hook && free_hook) {
+    free_hook(old_address);
+    allocation_hook(new_address, size, type_name);
+  }
+}
+bool PartitionAllocHooks::ReallocOverrideHookIfEnabled(size_t* out,
+                                                       void* address) {
+  if (ReallocOverrideHook* hook =
+          realloc_override_hook_.load(std::memory_order_relaxed)) {
+    return hook(out, address);
+  }
+  return false;
+}
+
+static void PartitionAllocBaseInit(internal::PartitionRootBase* root) {
   DCHECK(!root->initialized);
   {
-    subtle::SpinLock::Guard guard(PartitionRootBase::gInitializedLock);
-    if (!PartitionRootBase::gInitialized) {
-      PartitionRootBase::gInitialized = true;
-      // We mark the seed page as free to make sure it is skipped by our
-      // logic to find a new active page.
-      PartitionRootBase::gPagedBucket.active_pages_head =
-          &PartitionRootGeneric::gSeedPage;
+    subtle::SpinLock::Guard guard(*GetLock());
+    if (!g_initialized) {
+      g_initialized = true;
+      // We mark the sentinel bucket/page as free to make sure it is skipped by
+      // our logic to find a new active page.
+      internal::PartitionBucket::get_sentinel_bucket()->active_pages_head =
+          internal::PartitionPage::get_sentinel_page();
     }
   }
 
   root->initialized = true;
-  root->total_size_of_committed_pages = 0;
-  root->total_size_of_super_pages = 0;
-  root->total_size_of_direct_mapped_pages = 0;
-  root->next_super_page = 0;
-  root->next_partition_page = 0;
-  root->next_partition_page_end = 0;
-  root->first_extent = 0;
-  root->current_extent = 0;
-  root->direct_map_list = 0;
-
-  memset(&root->global_empty_page_ring, '\0',
-         sizeof(root->global_empty_page_ring));
-  root->global_empty_page_ring_index = 0;
 
   // This is a "magic" value so we can test if a root pointer is valid.
   root->inverted_self = ~reinterpret_cast<uintptr_t>(root);
 }
 
-static void PartitionBucketInitBase(PartitionBucket* bucket,
-                                    PartitionRootBase* root) {
-  bucket->active_pages_head = &PartitionRootGeneric::gSeedPage;
-  bucket->empty_pages_head = 0;
-  bucket->decommitted_pages_head = 0;
-  bucket->num_full_pages = 0;
-  bucket->num_system_pages_per_slot_span =
-      PartitionBucketNumSystemPages(bucket->slot_size);
-}
-
 void PartitionAllocGlobalInit(void (*oom_handling_function)()) {
   DCHECK(oom_handling_function);
-  PartitionRootBase::gOomHandlingFunction = oom_handling_function;
+  internal::PartitionRootBase::gOomHandlingFunction = oom_handling_function;
 }
 
-void PartitionAllocInit(PartitionRoot* root,
-                        size_t num_buckets,
-                        size_t max_allocation) {
-  PartitionAllocBaseInit(root);
+void PartitionRoot::Init(size_t bucket_count, size_t maximum_allocation) {
+  PartitionAllocBaseInit(this);
 
-  root->num_buckets = num_buckets;
-  root->max_allocation = max_allocation;
-  size_t i;
-  for (i = 0; i < root->num_buckets; ++i) {
-    PartitionBucket* bucket = &root->buckets()[i];
-    if (!i)
-      bucket->slot_size = kAllocationGranularity;
-    else
-      bucket->slot_size = i << kBucketShift;
-    PartitionBucketInitBase(bucket, root);
+  num_buckets = bucket_count;
+  max_allocation = maximum_allocation;
+  for (size_t i = 0; i < num_buckets; ++i) {
+    internal::PartitionBucket& bucket = buckets()[i];
+    bucket.Init(i == 0 ? kAllocationGranularity : (i << kBucketShift));
   }
 }
 
-void PartitionAllocGenericInit(PartitionRootGeneric* root) {
-  subtle::SpinLock::Guard guard(root->lock);
+void PartitionRootGeneric::Init() {
+  subtle::SpinLock::Guard guard(lock);
 
-  PartitionAllocBaseInit(root);
+  PartitionAllocBaseInit(this);
 
   // Precalculate some shift and mask constants used in the hot path.
   // Example: malloc(41) == 101001 binary.
@@ -187,7 +223,7 @@
       order_index_shift = 0;
     else
       order_index_shift = order - (kGenericNumBucketsPerOrderBits + 1);
-    root->order_index_shifts[order] = order_index_shift;
+    order_index_shifts[order] = order_index_shift;
     size_t sub_order_index_mask;
     if (order == kBitsPerSizeT) {
       // This avoids invoking undefined behavior for an excessive shift.
@@ -197,7 +233,7 @@
       sub_order_index_mask = ((static_cast<size_t>(1) << order) - 1) >>
                              (kGenericNumBucketsPerOrderBits + 1);
     }
-    root->order_sub_index_masks[order] = sub_order_index_mask;
+    order_sub_index_masks[order] = sub_order_index_mask;
   }
 
   // Set up the actual usable buckets first.
@@ -208,780 +244,72 @@
   // code simpler and the structures more generic.
   size_t i, j;
   size_t current_size = kGenericSmallestBucket;
-  size_t currentIncrement =
+  size_t current_increment =
       kGenericSmallestBucket >> kGenericNumBucketsPerOrderBits;
-  PartitionBucket* bucket = &root->buckets[0];
+  internal::PartitionBucket* bucket = &buckets[0];
   for (i = 0; i < kGenericNumBucketedOrders; ++i) {
     for (j = 0; j < kGenericNumBucketsPerOrder; ++j) {
-      bucket->slot_size = current_size;
-      PartitionBucketInitBase(bucket, root);
+      bucket->Init(current_size);
       // Disable psuedo buckets so that touching them faults.
       if (current_size % kGenericSmallestBucket)
-        bucket->active_pages_head = 0;
-      current_size += currentIncrement;
+        bucket->active_pages_head = nullptr;
+      current_size += current_increment;
       ++bucket;
     }
-    currentIncrement <<= 1;
+    current_increment <<= 1;
   }
   DCHECK(current_size == 1 << kGenericMaxBucketedOrder);
-  DCHECK(bucket == &root->buckets[0] + kGenericNumBuckets);
+  DCHECK(bucket == &buckets[0] + kGenericNumBuckets);
 
   // Then set up the fast size -> bucket lookup table.
-  bucket = &root->buckets[0];
-  PartitionBucket** bucketPtr = &root->bucket_lookups[0];
+  bucket = &buckets[0];
+  internal::PartitionBucket** bucket_ptr = &bucket_lookups[0];
   for (order = 0; order <= kBitsPerSizeT; ++order) {
     for (j = 0; j < kGenericNumBucketsPerOrder; ++j) {
       if (order < kGenericMinBucketedOrder) {
         // Use the bucket of the finest granularity for malloc(0) etc.
-        *bucketPtr++ = &root->buckets[0];
+        *bucket_ptr++ = &buckets[0];
       } else if (order > kGenericMaxBucketedOrder) {
-        *bucketPtr++ = &PartitionRootGeneric::gPagedBucket;
+        *bucket_ptr++ = internal::PartitionBucket::get_sentinel_bucket();
       } else {
-        PartitionBucket* validBucket = bucket;
+        internal::PartitionBucket* valid_bucket = bucket;
         // Skip over invalid buckets.
-        while (validBucket->slot_size % kGenericSmallestBucket)
-          validBucket++;
-        *bucketPtr++ = validBucket;
+        while (valid_bucket->slot_size % kGenericSmallestBucket)
+          valid_bucket++;
+        *bucket_ptr++ = valid_bucket;
         bucket++;
       }
     }
   }
-  DCHECK(bucket == &root->buckets[0] + kGenericNumBuckets);
-  DCHECK(bucketPtr ==
-         &root->bucket_lookups[0] +
-             ((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder));
+  DCHECK(bucket == &buckets[0] + kGenericNumBuckets);
+  DCHECK(bucket_ptr == &bucket_lookups[0] +
+                           ((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder));
   // And there's one last bucket lookup that will be hit for e.g. malloc(-1),
   // which tries to overflow to a non-existant order.
-  *bucketPtr = &PartitionRootGeneric::gPagedBucket;
+  *bucket_ptr = internal::PartitionBucket::get_sentinel_bucket();
 }
 
-#if !defined(ARCH_CPU_64_BITS)
-static NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages() {
-  OOM_CRASH();
-}
-#endif
-
-static NOINLINE void PartitionOutOfMemory(const PartitionRootBase* root) {
-#if !defined(ARCH_CPU_64_BITS)
-  // Check whether this OOM is due to a lot of super pages that are allocated
-  // but not committed, probably due to http://crbug.com/421387.
-  if (root->total_size_of_super_pages +
-          root->total_size_of_direct_mapped_pages -
-          root->total_size_of_committed_pages >
-      kReasonableSizeOfUnusedPages) {
-    PartitionOutOfMemoryWithLotsOfUncommitedPages();
-  }
-#endif
-  if (PartitionRootBase::gOomHandlingFunction)
-    (*PartitionRootBase::gOomHandlingFunction)();
-  OOM_CRASH();
-}
-
-static NOINLINE void PartitionExcessiveAllocationSize() {
-  OOM_CRASH();
-}
-
-static NOINLINE void PartitionBucketFull() {
-  OOM_CRASH();
-}
-
-// partitionPageStateIs*
-// Note that it's only valid to call these functions on pages found on one of
-// the page lists. Specifically, you can't call these functions on full pages
-// that were detached from the active list.
-static bool ALWAYS_INLINE
-PartitionPageStateIsActive(const PartitionPage* page) {
-  DCHECK(page != &PartitionRootGeneric::gSeedPage);
-  DCHECK(!page->page_offset);
-  return (page->num_allocated_slots > 0 &&
-          (page->freelist_head || page->num_unprovisioned_slots));
-}
-
-static bool ALWAYS_INLINE PartitionPageStateIsFull(const PartitionPage* page) {
-  DCHECK(page != &PartitionRootGeneric::gSeedPage);
-  DCHECK(!page->page_offset);
-  bool ret = (page->num_allocated_slots == PartitionBucketSlots(page->bucket));
-  if (ret) {
-    DCHECK(!page->freelist_head);
-    DCHECK(!page->num_unprovisioned_slots);
-  }
-  return ret;
-}
-
-static bool ALWAYS_INLINE PartitionPageStateIsEmpty(const PartitionPage* page) {
-  DCHECK(page != &PartitionRootGeneric::gSeedPage);
-  DCHECK(!page->page_offset);
-  return (!page->num_allocated_slots && page->freelist_head);
-}
-
-static bool ALWAYS_INLINE
-PartitionPageStateIsDecommitted(const PartitionPage* page) {
-  DCHECK(page != &PartitionRootGeneric::gSeedPage);
-  DCHECK(!page->page_offset);
-  bool ret = (!page->num_allocated_slots && !page->freelist_head);
-  if (ret) {
-    DCHECK(!page->num_unprovisioned_slots);
-    DCHECK(page->empty_cache_index == -1);
-  }
-  return ret;
-}
-
-static void PartitionIncreaseCommittedPages(PartitionRootBase* root,
-                                            size_t len) {
-  root->total_size_of_committed_pages += len;
-  DCHECK(root->total_size_of_committed_pages <=
-         root->total_size_of_super_pages +
-             root->total_size_of_direct_mapped_pages);
-}
-
-static void PartitionDecreaseCommittedPages(PartitionRootBase* root,
-                                            size_t len) {
-  root->total_size_of_committed_pages -= len;
-  DCHECK(root->total_size_of_committed_pages <=
-         root->total_size_of_super_pages +
-             root->total_size_of_direct_mapped_pages);
-}
-
-static ALWAYS_INLINE void PartitionDecommitSystemPages(PartitionRootBase* root,
-                                                       void* address,
-                                                       size_t length) {
-  DecommitSystemPages(address, length);
-  PartitionDecreaseCommittedPages(root, length);
-}
-
-static ALWAYS_INLINE void PartitionRecommitSystemPages(PartitionRootBase* root,
-                                                       void* address,
-                                                       size_t length) {
-  RecommitSystemPages(address, length);
-  PartitionIncreaseCommittedPages(root, length);
-}
-
-static ALWAYS_INLINE void* PartitionAllocPartitionPages(
-    PartitionRootBase* root,
-    int flags,
-    uint16_t num_partition_pages) {
-  DCHECK(!(reinterpret_cast<uintptr_t>(root->next_partition_page) %
-           kPartitionPageSize));
-  DCHECK(!(reinterpret_cast<uintptr_t>(root->next_partition_page_end) %
-           kPartitionPageSize));
-  DCHECK(num_partition_pages <= kNumPartitionPagesPerSuperPage);
-  size_t total_size = kPartitionPageSize * num_partition_pages;
-  size_t num_partition_pages_left =
-      (root->next_partition_page_end - root->next_partition_page) >>
-      kPartitionPageShift;
-  if (LIKELY(num_partition_pages_left >= num_partition_pages)) {
-    // In this case, we can still hand out pages from the current super page
-    // allocation.
-    char* ret = root->next_partition_page;
-    root->next_partition_page += total_size;
-    PartitionIncreaseCommittedPages(root, total_size);
-    return ret;
-  }
-
-  // Need a new super page. We want to allocate super pages in a continguous
-  // address region as much as possible. This is important for not causing
-  // page table bloat and not fragmenting address spaces in 32 bit
-  // architectures.
-  char* requestedAddress = root->next_super_page;
-  char* super_page = reinterpret_cast<char*>(AllocPages(
-      requestedAddress, kSuperPageSize, kSuperPageSize, PageAccessible));
-  if (UNLIKELY(!super_page))
-    return 0;
-
-  root->total_size_of_super_pages += kSuperPageSize;
-  PartitionIncreaseCommittedPages(root, total_size);
-
-  root->next_super_page = super_page + kSuperPageSize;
-  char* ret = super_page + kPartitionPageSize;
-  root->next_partition_page = ret + total_size;
-  root->next_partition_page_end = root->next_super_page - kPartitionPageSize;
-  // Make the first partition page in the super page a guard page, but leave a
-  // hole in the middle.
-  // This is where we put page metadata and also a tiny amount of extent
-  // metadata.
-  SetSystemPagesInaccessible(super_page, kSystemPageSize);
-  SetSystemPagesInaccessible(super_page + (kSystemPageSize * 2),
-                             kPartitionPageSize - (kSystemPageSize * 2));
-  // Also make the last partition page a guard page.
-  SetSystemPagesInaccessible(super_page + (kSuperPageSize - kPartitionPageSize),
-                             kPartitionPageSize);
-
-  // If we were after a specific address, but didn't get it, assume that
-  // the system chose a lousy address. Here most OS'es have a default
-  // algorithm that isn't randomized. For example, most Linux
-  // distributions will allocate the mapping directly before the last
-  // successful mapping, which is far from random. So we just get fresh
-  // randomness for the next mapping attempt.
-  if (requestedAddress && requestedAddress != super_page)
-    root->next_super_page = 0;
-
-  // We allocated a new super page so update super page metadata.
-  // First check if this is a new extent or not.
-  PartitionSuperPageExtentEntry* latest_extent =
-      reinterpret_cast<PartitionSuperPageExtentEntry*>(
-          PartitionSuperPageToMetadataArea(super_page));
-  // By storing the root in every extent metadata object, we have a fast way
-  // to go from a pointer within the partition to the root object.
-  latest_extent->root = root;
-  // Most new extents will be part of a larger extent, and these three fields
-  // are unused, but we initialize them to 0 so that we get a clear signal
-  // in case they are accidentally used.
-  latest_extent->super_page_base = 0;
-  latest_extent->super_pages_end = 0;
-  latest_extent->next = 0;
-
-  PartitionSuperPageExtentEntry* current_extent = root->current_extent;
-  bool isNewExtent = (super_page != requestedAddress);
-  if (UNLIKELY(isNewExtent)) {
-    if (UNLIKELY(!current_extent)) {
-      DCHECK(!root->first_extent);
-      root->first_extent = latest_extent;
-    } else {
-      DCHECK(current_extent->super_page_base);
-      current_extent->next = latest_extent;
-    }
-    root->current_extent = latest_extent;
-    latest_extent->super_page_base = super_page;
-    latest_extent->super_pages_end = super_page + kSuperPageSize;
-  } else {
-    // We allocated next to an existing extent so just nudge the size up a
-    // little.
-    DCHECK(current_extent->super_pages_end);
-    current_extent->super_pages_end += kSuperPageSize;
-    DCHECK(ret >= current_extent->super_page_base &&
-           ret < current_extent->super_pages_end);
-  }
-  return ret;
-}
-
-static ALWAYS_INLINE uint16_t
-PartitionBucketPartitionPages(const PartitionBucket* bucket) {
-  return (bucket->num_system_pages_per_slot_span +
-          (kNumSystemPagesPerPartitionPage - 1)) /
-         kNumSystemPagesPerPartitionPage;
-}
-
-static ALWAYS_INLINE void PartitionPageReset(PartitionPage* page) {
-  DCHECK(PartitionPageStateIsDecommitted(page));
-
-  page->num_unprovisioned_slots = PartitionBucketSlots(page->bucket);
-  DCHECK(page->num_unprovisioned_slots);
-
-  page->next_page = nullptr;
-}
-
-static ALWAYS_INLINE void PartitionPageSetup(PartitionPage* page,
-                                             PartitionBucket* bucket) {
-  // The bucket never changes. We set it up once.
-  page->bucket = bucket;
-  page->empty_cache_index = -1;
-
-  PartitionPageReset(page);
-
-  // If this page has just a single slot, do not set up page offsets for any
-  // page metadata other than the first one. This ensures that attempts to
-  // touch invalid page metadata fail.
-  if (page->num_unprovisioned_slots == 1)
-    return;
-
-  uint16_t num_partition_pages = PartitionBucketPartitionPages(bucket);
-  char* page_char_ptr = reinterpret_cast<char*>(page);
-  for (uint16_t i = 1; i < num_partition_pages; ++i) {
-    page_char_ptr += kPageMetadataSize;
-    PartitionPage* secondary_page =
-        reinterpret_cast<PartitionPage*>(page_char_ptr);
-    secondary_page->page_offset = i;
-  }
-}
-
-static ALWAYS_INLINE char* PartitionPageAllocAndFillFreelist(
-    PartitionPage* page) {
-  DCHECK(page != &PartitionRootGeneric::gSeedPage);
-  uint16_t num_slots = page->num_unprovisioned_slots;
-  DCHECK(num_slots);
-  PartitionBucket* bucket = page->bucket;
-  // We should only get here when _every_ slot is either used or unprovisioned.
-  // (The third state is "on the freelist". If we have a non-empty freelist, we
-  // should not get here.)
-  DCHECK(num_slots + page->num_allocated_slots == PartitionBucketSlots(bucket));
-  // Similarly, make explicitly sure that the freelist is empty.
-  DCHECK(!page->freelist_head);
-  DCHECK(page->num_allocated_slots >= 0);
-
-  size_t size = bucket->slot_size;
-  char* base = reinterpret_cast<char*>(PartitionPageToPointer(page));
-  char* return_object = base + (size * page->num_allocated_slots);
-  char* firstFreelistPointer = return_object + size;
-  char* firstFreelistPointerExtent =
-      firstFreelistPointer + sizeof(PartitionFreelistEntry*);
-  // Our goal is to fault as few system pages as possible. We calculate the
-  // page containing the "end" of the returned slot, and then allow freelist
-  // pointers to be written up to the end of that page.
-  char* sub_page_limit = reinterpret_cast<char*>(
-      RoundUpToSystemPage(reinterpret_cast<size_t>(firstFreelistPointer)));
-  char* slots_limit = return_object + (size * num_slots);
-  char* freelist_limit = sub_page_limit;
-  if (UNLIKELY(slots_limit < freelist_limit))
-    freelist_limit = slots_limit;
-
-  uint16_t num_new_freelist_entries = 0;
-  if (LIKELY(firstFreelistPointerExtent <= freelist_limit)) {
-    // Only consider used space in the slot span. If we consider wasted
-    // space, we may get an off-by-one when a freelist pointer fits in the
-    // wasted space, but a slot does not.
-    // We know we can fit at least one freelist pointer.
-    num_new_freelist_entries = 1;
-    // Any further entries require space for the whole slot span.
-    num_new_freelist_entries += static_cast<uint16_t>(
-        (freelist_limit - firstFreelistPointerExtent) / size);
-  }
-
-  // We always return an object slot -- that's the +1 below.
-  // We do not neccessarily create any new freelist entries, because we cross
-  // sub page boundaries frequently for large bucket sizes.
-  DCHECK(num_new_freelist_entries + 1 <= num_slots);
-  num_slots -= (num_new_freelist_entries + 1);
-  page->num_unprovisioned_slots = num_slots;
-  page->num_allocated_slots++;
-
-  if (LIKELY(num_new_freelist_entries)) {
-    char* freelist_pointer = firstFreelistPointer;
-    PartitionFreelistEntry* entry =
-        reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
-    page->freelist_head = entry;
-    while (--num_new_freelist_entries) {
-      freelist_pointer += size;
-      PartitionFreelistEntry* next_entry =
-          reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
-      entry->next = PartitionFreelistMask(next_entry);
-      entry = next_entry;
-    }
-    entry->next = PartitionFreelistMask(0);
-  } else {
-    page->freelist_head = 0;
-  }
-  return return_object;
-}
-
-// This helper function scans a bucket's active page list for a suitable new
-// active page.
-// When it finds a suitable new active page (one that has free slots and is not
-// empty), it is set as the new active page. If there is no suitable new
-// active page, the current active page is set to the seed page.
-// As potential pages are scanned, they are tidied up according to their state.
-// Empty pages are swept on to the empty page list, decommitted pages on to the
-// decommitted page list and full pages are unlinked from any list.
-static bool PartitionSetNewActivePage(PartitionBucket* bucket) {
-  PartitionPage* page = bucket->active_pages_head;
-  if (page == &PartitionRootBase::gSeedPage)
-    return false;
-
-  PartitionPage* next_page;
-
-  for (; page; page = next_page) {
-    next_page = page->next_page;
-    DCHECK(page->bucket == bucket);
-    DCHECK(page != bucket->empty_pages_head);
-    DCHECK(page != bucket->decommitted_pages_head);
-
-    // Deal with empty and decommitted pages.
-    if (LIKELY(PartitionPageStateIsActive(page))) {
-      // This page is usable because it has freelist entries, or has
-      // unprovisioned slots we can create freelist entries from.
-      bucket->active_pages_head = page;
-      return true;
-    }
-    if (LIKELY(PartitionPageStateIsEmpty(page))) {
-      page->next_page = bucket->empty_pages_head;
-      bucket->empty_pages_head = page;
-    } else if (LIKELY(PartitionPageStateIsDecommitted(page))) {
-      page->next_page = bucket->decommitted_pages_head;
-      bucket->decommitted_pages_head = page;
-    } else {
-      DCHECK(PartitionPageStateIsFull(page));
-      // If we get here, we found a full page. Skip over it too, and also
-      // tag it as full (via a negative value). We need it tagged so that
-      // free'ing can tell, and move it back into the active page list.
-      page->num_allocated_slots = -page->num_allocated_slots;
-      ++bucket->num_full_pages;
-      // num_full_pages is a uint16_t for efficient packing so guard against
-      // overflow to be safe.
-      if (UNLIKELY(!bucket->num_full_pages))
-        PartitionBucketFull();
-      // Not necessary but might help stop accidents.
-      page->next_page = 0;
-    }
-  }
-
-  bucket->active_pages_head = &PartitionRootGeneric::gSeedPage;
-  return false;
-}
-
-static ALWAYS_INLINE PartitionDirectMapExtent* partitionPageToDirectMapExtent(
-    PartitionPage* page) {
-  DCHECK(PartitionBucketIsDirectMapped(page->bucket));
-  return reinterpret_cast<PartitionDirectMapExtent*>(
-      reinterpret_cast<char*>(page) + 3 * kPageMetadataSize);
-}
-
-static ALWAYS_INLINE void PartitionPageSetRawSize(PartitionPage* page,
-                                                  size_t size) {
-  size_t* raw_size_ptr = PartitionPageGetRawSizePtr(page);
-  if (UNLIKELY(raw_size_ptr != nullptr))
-    *raw_size_ptr = size;
-}
-
-static ALWAYS_INLINE PartitionPage* PartitionDirectMap(PartitionRootBase* root,
-                                                       int flags,
-                                                       size_t raw_size) {
-  size_t size = PartitionDirectMapSize(raw_size);
-
-  // Because we need to fake looking like a super page, we need to allocate
-  // a bunch of system pages more than "size":
-  // - The first few system pages are the partition page in which the super
-  // page metadata is stored. We fault just one system page out of a partition
-  // page sized clump.
-  // - We add a trailing guard page on 32-bit (on 64-bit we rely on the
-  // massive address space plus randomization instead).
-  size_t map_size = size + kPartitionPageSize;
-#if !defined(ARCH_CPU_64_BITS)
-  map_size += kSystemPageSize;
-#endif
-  // Round up to the allocation granularity.
-  map_size += kPageAllocationGranularityOffsetMask;
-  map_size &= kPageAllocationGranularityBaseMask;
-
-  // TODO: these pages will be zero-filled. Consider internalizing an
-  // allocZeroed() API so we can avoid a memset() entirely in this case.
-  char* ptr = reinterpret_cast<char*>(
-      AllocPages(0, map_size, kSuperPageSize, PageAccessible));
-  if (UNLIKELY(!ptr))
-    return nullptr;
-
-  size_t committed_page_size = size + kSystemPageSize;
-  root->total_size_of_direct_mapped_pages += committed_page_size;
-  PartitionIncreaseCommittedPages(root, committed_page_size);
-
-  char* slot = ptr + kPartitionPageSize;
-  SetSystemPagesInaccessible(ptr + (kSystemPageSize * 2),
-                             kPartitionPageSize - (kSystemPageSize * 2));
-#if !defined(ARCH_CPU_64_BITS)
-  SetSystemPagesInaccessible(ptr, kSystemPageSize);
-  SetSystemPagesInaccessible(slot + size, kSystemPageSize);
-#endif
-
-  PartitionSuperPageExtentEntry* extent =
-      reinterpret_cast<PartitionSuperPageExtentEntry*>(
-          PartitionSuperPageToMetadataArea(ptr));
-  extent->root = root;
-  // The new structures are all located inside a fresh system page so they
-  // will all be zeroed out. These DCHECKs are for documentation.
-  DCHECK(!extent->super_page_base);
-  DCHECK(!extent->super_pages_end);
-  DCHECK(!extent->next);
-  PartitionPage* page = PartitionPointerToPageNoAlignmentCheck(slot);
-  PartitionBucket* bucket = reinterpret_cast<PartitionBucket*>(
-      reinterpret_cast<char*>(page) + (kPageMetadataSize * 2));
-  DCHECK(!page->next_page);
-  DCHECK(!page->num_allocated_slots);
-  DCHECK(!page->num_unprovisioned_slots);
-  DCHECK(!page->page_offset);
-  DCHECK(!page->empty_cache_index);
-  page->bucket = bucket;
-  page->freelist_head = reinterpret_cast<PartitionFreelistEntry*>(slot);
-  PartitionFreelistEntry* next_entry =
-      reinterpret_cast<PartitionFreelistEntry*>(slot);
-  next_entry->next = PartitionFreelistMask(0);
-
-  DCHECK(!bucket->active_pages_head);
-  DCHECK(!bucket->empty_pages_head);
-  DCHECK(!bucket->decommitted_pages_head);
-  DCHECK(!bucket->num_system_pages_per_slot_span);
-  DCHECK(!bucket->num_full_pages);
-  bucket->slot_size = size;
-
-  PartitionDirectMapExtent* map_extent = partitionPageToDirectMapExtent(page);
-  map_extent->map_size = map_size - kPartitionPageSize - kSystemPageSize;
-  map_extent->bucket = bucket;
-
-  // Maintain the doubly-linked list of all direct mappings.
-  map_extent->next_extent = root->direct_map_list;
-  if (map_extent->next_extent)
-    map_extent->next_extent->prev_extent = map_extent;
-  map_extent->prev_extent = nullptr;
-  root->direct_map_list = map_extent;
-
-  return page;
-}
-
-static ALWAYS_INLINE void PartitionDirectUnmap(PartitionPage* page) {
-  PartitionRootBase* root = PartitionPageToRoot(page);
-  const PartitionDirectMapExtent* extent = partitionPageToDirectMapExtent(page);
-  size_t unmap_size = extent->map_size;
-
-  // Maintain the doubly-linked list of all direct mappings.
-  if (extent->prev_extent) {
-    DCHECK(extent->prev_extent->next_extent == extent);
-    extent->prev_extent->next_extent = extent->next_extent;
-  } else {
-    root->direct_map_list = extent->next_extent;
-  }
-  if (extent->next_extent) {
-    DCHECK(extent->next_extent->prev_extent == extent);
-    extent->next_extent->prev_extent = extent->prev_extent;
-  }
-
-  // Add on the size of the trailing guard page and preceeding partition
-  // page.
-  unmap_size += kPartitionPageSize + kSystemPageSize;
-
-  size_t uncommitted_page_size = page->bucket->slot_size + kSystemPageSize;
-  PartitionDecreaseCommittedPages(root, uncommitted_page_size);
-  DCHECK(root->total_size_of_direct_mapped_pages >= uncommitted_page_size);
-  root->total_size_of_direct_mapped_pages -= uncommitted_page_size;
-
-  DCHECK(!(unmap_size & kPageAllocationGranularityOffsetMask));
-
-  char* ptr = reinterpret_cast<char*>(PartitionPageToPointer(page));
-  // Account for the mapping starting a partition page before the actual
-  // allocation address.
-  ptr -= kPartitionPageSize;
-
-  FreePages(ptr, unmap_size);
-}
-
-void* PartitionAllocSlowPath(PartitionRootBase* root,
-                             int flags,
-                             size_t size,
-                             PartitionBucket* bucket) {
-  // The slow path is called when the freelist is empty.
-  DCHECK(!bucket->active_pages_head->freelist_head);
-
-  PartitionPage* new_page = nullptr;
-
-  // For the PartitionAllocGeneric API, we have a bunch of buckets marked
-  // as special cases. We bounce them through to the slow path so that we
-  // can still have a blazing fast hot path due to lack of corner-case
-  // branches.
-  bool returnNull = flags & PartitionAllocReturnNull;
-  if (UNLIKELY(PartitionBucketIsDirectMapped(bucket))) {
-    DCHECK(size > kGenericMaxBucketed);
-    DCHECK(bucket == &PartitionRootBase::gPagedBucket);
-    DCHECK(bucket->active_pages_head == &PartitionRootGeneric::gSeedPage);
-    if (size > kGenericMaxDirectMapped) {
-      if (returnNull)
-        return nullptr;
-      PartitionExcessiveAllocationSize();
-    }
-    new_page = PartitionDirectMap(root, flags, size);
-  } else if (LIKELY(PartitionSetNewActivePage(bucket))) {
-    // First, did we find an active page in the active pages list?
-    new_page = bucket->active_pages_head;
-    DCHECK(PartitionPageStateIsActive(new_page));
-  } else if (LIKELY(bucket->empty_pages_head != nullptr) ||
-             LIKELY(bucket->decommitted_pages_head != nullptr)) {
-    // Second, look in our lists of empty and decommitted pages.
-    // Check empty pages first, which are preferred, but beware that an
-    // empty page might have been decommitted.
-    while (LIKELY((new_page = bucket->empty_pages_head) != nullptr)) {
-      DCHECK(new_page->bucket == bucket);
-      DCHECK(PartitionPageStateIsEmpty(new_page) ||
-             PartitionPageStateIsDecommitted(new_page));
-      bucket->empty_pages_head = new_page->next_page;
-      // Accept the empty page unless it got decommitted.
-      if (new_page->freelist_head) {
-        new_page->next_page = nullptr;
-        break;
-      }
-      DCHECK(PartitionPageStateIsDecommitted(new_page));
-      new_page->next_page = bucket->decommitted_pages_head;
-      bucket->decommitted_pages_head = new_page;
-    }
-    if (UNLIKELY(!new_page) &&
-        LIKELY(bucket->decommitted_pages_head != nullptr)) {
-      new_page = bucket->decommitted_pages_head;
-      DCHECK(new_page->bucket == bucket);
-      DCHECK(PartitionPageStateIsDecommitted(new_page));
-      bucket->decommitted_pages_head = new_page->next_page;
-      void* addr = PartitionPageToPointer(new_page);
-      PartitionRecommitSystemPages(root, addr,
-                                   PartitionBucketBytes(new_page->bucket));
-      PartitionPageReset(new_page);
-    }
-    DCHECK(new_page);
-  } else {
-    // Third. If we get here, we need a brand new page.
-    uint16_t num_partition_pages = PartitionBucketPartitionPages(bucket);
-    void* rawPages =
-        PartitionAllocPartitionPages(root, flags, num_partition_pages);
-    if (LIKELY(rawPages != nullptr)) {
-      new_page = PartitionPointerToPageNoAlignmentCheck(rawPages);
-      PartitionPageSetup(new_page, bucket);
-    }
-  }
-
-  // Bail if we had a memory allocation failure.
-  if (UNLIKELY(!new_page)) {
-    DCHECK(bucket->active_pages_head == &PartitionRootGeneric::gSeedPage);
-    if (returnNull)
-      return nullptr;
-    PartitionOutOfMemory(root);
-  }
-
-  bucket = new_page->bucket;
-  DCHECK(bucket != &PartitionRootBase::gPagedBucket);
-  bucket->active_pages_head = new_page;
-  PartitionPageSetRawSize(new_page, size);
-
-  // If we found an active page with free slots, or an empty page, we have a
-  // usable freelist head.
-  if (LIKELY(new_page->freelist_head != nullptr)) {
-    PartitionFreelistEntry* entry = new_page->freelist_head;
-    PartitionFreelistEntry* new_head = PartitionFreelistMask(entry->next);
-    new_page->freelist_head = new_head;
-    new_page->num_allocated_slots++;
-    return entry;
-  }
-  // Otherwise, we need to build the freelist.
-  DCHECK(new_page->num_unprovisioned_slots);
-  return PartitionPageAllocAndFillFreelist(new_page);
-}
-
-static ALWAYS_INLINE void PartitionDecommitPage(PartitionRootBase* root,
-                                                PartitionPage* page) {
-  DCHECK(PartitionPageStateIsEmpty(page));
-  DCHECK(!PartitionBucketIsDirectMapped(page->bucket));
-  void* addr = PartitionPageToPointer(page);
-  PartitionDecommitSystemPages(root, addr, PartitionBucketBytes(page->bucket));
-
-  // We actually leave the decommitted page in the active list. We'll sweep
-  // it on to the decommitted page list when we next walk the active page
-  // list.
-  // Pulling this trick enables us to use a singly-linked page list for all
-  // cases, which is critical in keeping the page metadata structure down to
-  // 32 bytes in size.
-  page->freelist_head = 0;
-  page->num_unprovisioned_slots = 0;
-  DCHECK(PartitionPageStateIsDecommitted(page));
-}
-
-static void PartitionDecommitPageIfPossible(PartitionRootBase* root,
-                                            PartitionPage* page) {
-  DCHECK(page->empty_cache_index >= 0);
-  DCHECK(static_cast<unsigned>(page->empty_cache_index) < kMaxFreeableSpans);
-  DCHECK(page == root->global_empty_page_ring[page->empty_cache_index]);
-  page->empty_cache_index = -1;
-  if (PartitionPageStateIsEmpty(page))
-    PartitionDecommitPage(root, page);
-}
-
-static ALWAYS_INLINE void PartitionRegisterEmptyPage(PartitionPage* page) {
-  DCHECK(PartitionPageStateIsEmpty(page));
-  PartitionRootBase* root = PartitionPageToRoot(page);
-
-  // If the page is already registered as empty, give it another life.
-  if (page->empty_cache_index != -1) {
-    DCHECK(page->empty_cache_index >= 0);
-    DCHECK(static_cast<unsigned>(page->empty_cache_index) < kMaxFreeableSpans);
-    DCHECK(root->global_empty_page_ring[page->empty_cache_index] == page);
-    root->global_empty_page_ring[page->empty_cache_index] = 0;
-  }
-
-  int16_t current_index = root->global_empty_page_ring_index;
-  PartitionPage* pageToDecommit = root->global_empty_page_ring[current_index];
-  // The page might well have been re-activated, filled up, etc. before we get
-  // around to looking at it here.
-  if (pageToDecommit)
-    PartitionDecommitPageIfPossible(root, pageToDecommit);
-
-  // We put the empty slot span on our global list of "pages that were once
-  // empty". thus providing it a bit of breathing room to get re-used before
-  // we really free it. This improves performance, particularly on Mac OS X
-  // which has subpar memory management performance.
-  root->global_empty_page_ring[current_index] = page;
-  page->empty_cache_index = current_index;
-  ++current_index;
-  if (current_index == kMaxFreeableSpans)
-    current_index = 0;
-  root->global_empty_page_ring_index = current_index;
-}
-
-static void PartitionDecommitEmptyPages(PartitionRootBase* root) {
-  for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
-    PartitionPage* page = root->global_empty_page_ring[i];
-    if (page)
-      PartitionDecommitPageIfPossible(root, page);
-    root->global_empty_page_ring[i] = nullptr;
-  }
-}
-
-void PartitionFreeSlowPath(PartitionPage* page) {
-  PartitionBucket* bucket = page->bucket;
-  DCHECK(page != &PartitionRootGeneric::gSeedPage);
-  if (LIKELY(page->num_allocated_slots == 0)) {
-    // Page became fully unused.
-    if (UNLIKELY(PartitionBucketIsDirectMapped(bucket))) {
-      PartitionDirectUnmap(page);
-      return;
-    }
-    // If it's the current active page, change it. We bounce the page to
-    // the empty list as a force towards defragmentation.
-    if (LIKELY(page == bucket->active_pages_head))
-      (void)PartitionSetNewActivePage(bucket);
-    DCHECK(bucket->active_pages_head != page);
-
-    PartitionPageSetRawSize(page, 0);
-    DCHECK(!PartitionPageGetRawSize(page));
-
-    PartitionRegisterEmptyPage(page);
-  } else {
-    DCHECK(!PartitionBucketIsDirectMapped(bucket));
-    // Ensure that the page is full. That's the only valid case if we
-    // arrive here.
-    DCHECK(page->num_allocated_slots < 0);
-    // A transition of num_allocated_slots from 0 to -1 is not legal, and
-    // likely indicates a double-free.
-    CHECK(page->num_allocated_slots != -1);
-    page->num_allocated_slots = -page->num_allocated_slots - 2;
-    DCHECK(page->num_allocated_slots == PartitionBucketSlots(bucket) - 1);
-    // Fully used page became partially used. It must be put back on the
-    // non-full page list. Also make it the current page to increase the
-    // chances of it being filled up again. The old current page will be
-    // the next page.
-    DCHECK(!page->next_page);
-    if (LIKELY(bucket->active_pages_head != &PartitionRootGeneric::gSeedPage))
-      page->next_page = bucket->active_pages_head;
-    bucket->active_pages_head = page;
-    --bucket->num_full_pages;
-    // Special case: for a partition page with just a single slot, it may
-    // now be empty and we want to run it through the empty logic.
-    if (UNLIKELY(page->num_allocated_slots == 0))
-      PartitionFreeSlowPath(page);
-  }
-}
-
-bool partitionReallocDirectMappedInPlace(PartitionRootGeneric* root,
-                                         PartitionPage* page,
+bool PartitionReallocDirectMappedInPlace(PartitionRootGeneric* root,
+                                         internal::PartitionPage* page,
                                          size_t raw_size) {
-  DCHECK(PartitionBucketIsDirectMapped(page->bucket));
+  DCHECK(page->bucket->is_direct_mapped());
 
-  raw_size = PartitionCookieSizeAdjustAdd(raw_size);
+  raw_size = internal::PartitionCookieSizeAdjustAdd(raw_size);
 
   // Note that the new size might be a bucketed size; this function is called
   // whenever we're reallocating a direct mapped allocation.
-  size_t new_size = PartitionDirectMapSize(raw_size);
+  size_t new_size = internal::PartitionBucket::get_direct_map_size(raw_size);
   if (new_size < kGenericMinDirectMappedDownsize)
     return false;
 
   // bucket->slot_size is the current size of the allocation.
   size_t current_size = page->bucket->slot_size;
-  if (new_size == current_size)
-    return true;
-
-  char* char_ptr = static_cast<char*>(PartitionPageToPointer(page));
-
-  if (new_size < current_size) {
-    size_t map_size = partitionPageToDirectMapExtent(page)->map_size;
+  char* char_ptr = static_cast<char*>(internal::PartitionPage::ToPointer(page));
+  if (new_size == current_size) {
+    // No need to move any memory around, but update size and cookie below.
+  } else if (new_size < current_size) {
+    size_t map_size =
+        internal::PartitionDirectMapExtent::FromPage(page)->map_size;
 
     // Don't reallocate in-place if new size is less than 80 % of the full
     // map size, to avoid holding on to too much unused address space.
@@ -989,16 +317,16 @@
       return false;
 
     // Shrink by decommitting unneeded pages and making them inaccessible.
-    size_t decommitSize = current_size - new_size;
-    PartitionDecommitSystemPages(root, char_ptr + new_size, decommitSize);
-    SetSystemPagesInaccessible(char_ptr + new_size, decommitSize);
-  } else if (new_size <= partitionPageToDirectMapExtent(page)->map_size) {
+    size_t decommit_size = current_size - new_size;
+    root->DecommitSystemPages(char_ptr + new_size, decommit_size);
+    SetSystemPagesAccess(char_ptr + new_size, decommit_size, PageInaccessible);
+  } else if (new_size <=
+             internal::PartitionDirectMapExtent::FromPage(page)->map_size) {
     // Grow within the actually allocated memory. Just need to make the
     // pages accessible again.
     size_t recommit_size = new_size - current_size;
-    bool ret = SetSystemPagesAccessible(char_ptr + current_size, recommit_size);
-    CHECK(ret);
-    PartitionRecommitSystemPages(root, char_ptr + current_size, recommit_size);
+    SetSystemPagesAccess(char_ptr + current_size, recommit_size, PageReadWrite);
+    root->RecommitSystemPages(char_ptr + current_size, recommit_size);
 
 #if DCHECK_IS_ON()
     memset(char_ptr + current_size, kUninitializedByte, recommit_size);
@@ -1011,129 +339,175 @@
 
 #if DCHECK_IS_ON()
   // Write a new trailing cookie.
-  PartitionCookieWriteValue(char_ptr + raw_size - kCookieSize);
+  internal::PartitionCookieWriteValue(char_ptr + raw_size -
+                                      internal::kCookieSize);
 #endif
 
-  PartitionPageSetRawSize(page, raw_size);
-  DCHECK(PartitionPageGetRawSize(page) == raw_size);
+  page->set_raw_size(raw_size);
+  DCHECK(page->get_raw_size() == raw_size);
 
   page->bucket->slot_size = new_size;
   return true;
 }
 
-void* PartitionReallocGeneric(PartitionRootGeneric* root,
-                              void* ptr,
-                              size_t new_size,
-                              const char* type_name) {
+void* PartitionReallocGenericFlags(PartitionRootGeneric* root,
+                                   int flags,
+                                   void* ptr,
+                                   size_t new_size,
+                                   const char* type_name) {
 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  return realloc(ptr, new_size);
+  CHECK_MAX_SIZE_OR_RETURN_NULLPTR(new_size, flags);
+  void* result = realloc(ptr, new_size);
+  CHECK(result || flags & PartitionAllocReturnNull);
+  return result;
 #else
   if (UNLIKELY(!ptr))
-    return PartitionAllocGeneric(root, new_size, type_name);
+    return PartitionAllocGenericFlags(root, flags, new_size, type_name);
   if (UNLIKELY(!new_size)) {
-    PartitionFreeGeneric(root, ptr);
-    return 0;
+    root->Free(ptr);
+    return nullptr;
   }
 
-  if (new_size > kGenericMaxDirectMapped)
-    PartitionExcessiveAllocationSize();
+  if (new_size > kGenericMaxDirectMapped) {
+    if (flags & PartitionAllocReturnNull)
+      return nullptr;
+    internal::PartitionExcessiveAllocationSize();
+  }
 
-  DCHECK(PartitionPointerIsValid(PartitionCookieFreePointerAdjust(ptr)));
+  const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled();
+  bool overridden = false;
+  size_t actual_old_size;
+  if (UNLIKELY(hooks_enabled)) {
+    overridden = PartitionAllocHooks::ReallocOverrideHookIfEnabled(
+        &actual_old_size, ptr);
+  }
+  if (LIKELY(!overridden)) {
+    internal::PartitionPage* page = internal::PartitionPage::FromPointer(
+        internal::PartitionCookieFreePointerAdjust(ptr));
+    // TODO(palmer): See if we can afford to make this a CHECK.
+    DCHECK(root->IsValidPage(page));
 
-  PartitionPage* page =
-      PartitionPointerToPage(PartitionCookieFreePointerAdjust(ptr));
+    if (UNLIKELY(page->bucket->is_direct_mapped())) {
+      // We may be able to perform the realloc in place by changing the
+      // accessibility of memory pages and, if reducing the size, decommitting
+      // them.
+      if (PartitionReallocDirectMappedInPlace(root, page, new_size)) {
+        if (UNLIKELY(hooks_enabled)) {
+          PartitionAllocHooks::ReallocObserverHookIfEnabled(ptr, ptr, new_size,
+                                                            type_name);
+        }
+        return ptr;
+      }
+    }
 
-  if (UNLIKELY(PartitionBucketIsDirectMapped(page->bucket))) {
-    // We may be able to perform the realloc in place by changing the
-    // accessibility of memory pages and, if reducing the size, decommitting
-    // them.
-    if (partitionReallocDirectMappedInPlace(root, page, new_size)) {
-      PartitionAllocHooks::ReallocHookIfEnabled(ptr, ptr, new_size, type_name);
+    const size_t actual_new_size = root->ActualSize(new_size);
+    actual_old_size = PartitionAllocGetSize(ptr);
+
+    // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the
+    // new size is a significant percentage smaller. We could do the same if we
+    // determine it is a win.
+    if (actual_new_size == actual_old_size) {
+      // Trying to allocate a block of size |new_size| would give us a block of
+      // the same size as the one we've already got, so re-use the allocation
+      // after updating statistics (and cookies, if present).
+      page->set_raw_size(internal::PartitionCookieSizeAdjustAdd(new_size));
+#if DCHECK_IS_ON()
+      // Write a new trailing cookie when it is possible to keep track of
+      // |new_size| via the raw size pointer.
+      if (page->get_raw_size_ptr())
+        internal::PartitionCookieWriteValue(static_cast<char*>(ptr) + new_size);
+#endif
       return ptr;
     }
   }
 
-  size_t actual_new_size = PartitionAllocActualSize(root, new_size);
-  size_t actual_old_size = PartitionAllocGetSize(ptr);
-
-  // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the
-  // new size is a significant percentage smaller. We could do the same if we
-  // determine it is a win.
-  if (actual_new_size == actual_old_size) {
-    // Trying to allocate a block of size new_size would give us a block of
-    // the same size as the one we've already got, so re-use the allocation
-    // after updating statistics (and cookies, if present).
-    PartitionPageSetRawSize(page, PartitionCookieSizeAdjustAdd(new_size));
-#if DCHECK_IS_ON()
-    // Write a new trailing cookie when it is possible to keep track of
-    // |new_size| via the raw size pointer.
-    if (PartitionPageGetRawSizePtr(page))
-      PartitionCookieWriteValue(static_cast<char*>(ptr) + new_size);
-#endif
-    return ptr;
+  // This realloc cannot be resized in-place. Sadness.
+  void* ret = PartitionAllocGenericFlags(root, flags, new_size, type_name);
+  if (!ret) {
+    if (flags & PartitionAllocReturnNull)
+      return nullptr;
+    internal::PartitionExcessiveAllocationSize();
   }
 
-  // This realloc cannot be resized in-place. Sadness.
-  void* ret = PartitionAllocGeneric(root, new_size, type_name);
   size_t copy_size = actual_old_size;
   if (new_size < copy_size)
     copy_size = new_size;
 
   memcpy(ret, ptr, copy_size);
-  PartitionFreeGeneric(root, ptr);
+  root->Free(ptr);
   return ret;
 #endif
 }
 
-static size_t PartitionPurgePage(PartitionPage* page, bool discard) {
-  const PartitionBucket* bucket = page->bucket;
+void* PartitionRootGeneric::Realloc(void* ptr,
+                                    size_t new_size,
+                                    const char* type_name) {
+  return PartitionReallocGenericFlags(this, 0, ptr, new_size, type_name);
+}
+
+void* PartitionRootGeneric::TryRealloc(void* ptr,
+                                       size_t new_size,
+                                       const char* type_name) {
+  return PartitionReallocGenericFlags(this, PartitionAllocReturnNull, ptr,
+                                      new_size, type_name);
+}
+
+static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) {
+  const internal::PartitionBucket* bucket = page->bucket;
   size_t slot_size = bucket->slot_size;
   if (slot_size < kSystemPageSize || !page->num_allocated_slots)
     return 0;
 
-  size_t bucket_num_slots = PartitionBucketSlots(bucket);
+  size_t bucket_num_slots = bucket->get_slots_per_span();
   size_t discardable_bytes = 0;
 
-  size_t raw_size = PartitionPageGetRawSize(const_cast<PartitionPage*>(page));
+  size_t raw_size = page->get_raw_size();
   if (raw_size) {
-    uint32_t usedBytes = static_cast<uint32_t>(RoundUpToSystemPage(raw_size));
-    discardable_bytes = bucket->slot_size - usedBytes;
+    uint32_t used_bytes = static_cast<uint32_t>(RoundUpToSystemPage(raw_size));
+    discardable_bytes = bucket->slot_size - used_bytes;
     if (discardable_bytes && discard) {
-      char* ptr = reinterpret_cast<char*>(PartitionPageToPointer(page));
-      ptr += usedBytes;
+      char* ptr =
+          reinterpret_cast<char*>(internal::PartitionPage::ToPointer(page));
+      ptr += used_bytes;
       DiscardSystemPages(ptr, discardable_bytes);
     }
     return discardable_bytes;
   }
 
-  const size_t max_slot_count =
+  constexpr size_t kMaxSlotCount =
       (kPartitionPageSize * kMaxPartitionPagesPerSlotSpan) / kSystemPageSize;
-  DCHECK(bucket_num_slots <= max_slot_count);
+  DCHECK(bucket_num_slots <= kMaxSlotCount);
   DCHECK(page->num_unprovisioned_slots < bucket_num_slots);
   size_t num_slots = bucket_num_slots - page->num_unprovisioned_slots;
-  char slot_usage[max_slot_count];
+  char slot_usage[kMaxSlotCount];
+#if !defined(OS_WIN)
+  // The last freelist entry should not be discarded when using OS_WIN.
+  // DiscardVirtualMemory makes the contents of discarded memory undefined.
   size_t last_slot = static_cast<size_t>(-1);
+#endif
   memset(slot_usage, 1, num_slots);
-  char* ptr = reinterpret_cast<char*>(PartitionPageToPointer(page));
-  PartitionFreelistEntry* entry = page->freelist_head;
+  char* ptr = reinterpret_cast<char*>(internal::PartitionPage::ToPointer(page));
   // First, walk the freelist for this page and make a bitmap of which slots
   // are not in use.
-  while (entry) {
-    size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / slot_size;
-    DCHECK(slotIndex < num_slots);
-    slot_usage[slotIndex] = 0;
-    entry = PartitionFreelistMask(entry->next);
-    // If we have a slot where the masked freelist entry is 0, we can
-    // actually discard that freelist entry because touching a discarded
-    // page is guaranteed to return original content or 0.
-    // (Note that this optimization won't fire on big endian machines
-    // because the masking function is negation.)
-    if (!PartitionFreelistMask(entry))
-      last_slot = slotIndex;
+  for (internal::PartitionFreelistEntry* entry = page->freelist_head; entry;
+       /**/) {
+    size_t slot_index = (reinterpret_cast<char*>(entry) - ptr) / slot_size;
+    DCHECK(slot_index < num_slots);
+    slot_usage[slot_index] = 0;
+    entry = internal::EncodedPartitionFreelistEntry::Decode(entry->next);
+#if !defined(OS_WIN)
+    // If we have a slot where the masked freelist entry is 0, we can actually
+    // discard that freelist entry because touching a discarded page is
+    // guaranteed to return original content or 0. (Note that this optimization
+    // won't fire on big-endian machines because the masking function is
+    // negation.)
+    if (!internal::PartitionFreelistEntry::Encode(entry))
+      last_slot = slot_index;
+#endif
   }
 
-  // If the slot(s) at the end of the slot span are not in used, we can
-  // truncate them entirely and rewrite the freelist.
+  // If the slot(s) at the end of the slot span are not in used, we can truncate
+  // them entirely and rewrite the freelist.
   size_t truncated_slots = 0;
   while (!slot_usage[num_slots - 1]) {
     truncated_slots++;
@@ -1142,62 +516,75 @@
   }
   // First, do the work of calculating the discardable bytes. Don't actually
   // discard anything unless the discard flag was passed in.
-  char* begin_ptr = nullptr;
-  char* end_ptr = nullptr;
-  size_t unprovisioned_bytes = 0;
   if (truncated_slots) {
-    begin_ptr = ptr + (num_slots * slot_size);
-    end_ptr = begin_ptr + (slot_size * truncated_slots);
+    size_t unprovisioned_bytes = 0;
+    char* begin_ptr = ptr + (num_slots * slot_size);
+    char* end_ptr = begin_ptr + (slot_size * truncated_slots);
     begin_ptr = reinterpret_cast<char*>(
         RoundUpToSystemPage(reinterpret_cast<size_t>(begin_ptr)));
-    // We round the end pointer here up and not down because we're at the
-    // end of a slot span, so we "own" all the way up the page boundary.
+    // We round the end pointer here up and not down because we're at the end of
+    // a slot span, so we "own" all the way up the page boundary.
     end_ptr = reinterpret_cast<char*>(
         RoundUpToSystemPage(reinterpret_cast<size_t>(end_ptr)));
-    DCHECK(end_ptr <= ptr + PartitionBucketBytes(bucket));
+    DCHECK(end_ptr <= ptr + bucket->get_bytes_per_span());
     if (begin_ptr < end_ptr) {
       unprovisioned_bytes = end_ptr - begin_ptr;
       discardable_bytes += unprovisioned_bytes;
     }
-  }
-  if (unprovisioned_bytes && discard) {
-    DCHECK(truncated_slots > 0);
-    size_t num_new_entries = 0;
-    page->num_unprovisioned_slots += static_cast<uint16_t>(truncated_slots);
-    // Rewrite the freelist.
-    PartitionFreelistEntry** entry_ptr = &page->freelist_head;
-    for (size_t slotIndex = 0; slotIndex < num_slots; ++slotIndex) {
-      if (slot_usage[slotIndex])
-        continue;
-      PartitionFreelistEntry* entry = reinterpret_cast<PartitionFreelistEntry*>(
-          ptr + (slot_size * slotIndex));
-      *entry_ptr = PartitionFreelistMask(entry);
-      entry_ptr = reinterpret_cast<PartitionFreelistEntry**>(entry);
-      num_new_entries++;
+    if (unprovisioned_bytes && discard) {
+      DCHECK(truncated_slots > 0);
+      size_t num_new_entries = 0;
+      page->num_unprovisioned_slots += static_cast<uint16_t>(truncated_slots);
+
+      // Rewrite the freelist.
+      internal::PartitionFreelistEntry* head = nullptr;
+      internal::PartitionFreelistEntry* back = head;
+      for (size_t slot_index = 0; slot_index < num_slots; ++slot_index) {
+        if (slot_usage[slot_index])
+          continue;
+
+        auto* entry = reinterpret_cast<internal::PartitionFreelistEntry*>(
+            ptr + (slot_size * slot_index));
+        if (!head) {
+          head = entry;
+          back = entry;
+        } else {
+          back->next = internal::PartitionFreelistEntry::Encode(entry);
+          back = entry;
+        }
+        num_new_entries++;
+#if !defined(OS_WIN)
+        last_slot = slot_index;
+#endif
+      }
+
+      page->freelist_head = head;
+      if (back)
+        back->next = internal::PartitionFreelistEntry::Encode(nullptr);
+
+      DCHECK(num_new_entries == num_slots - page->num_allocated_slots);
+      // Discard the memory.
+      DiscardSystemPages(begin_ptr, unprovisioned_bytes);
     }
-    // Terminate the freelist chain.
-    *entry_ptr = nullptr;
-    // The freelist head is stored unmasked.
-    page->freelist_head = PartitionFreelistMask(page->freelist_head);
-    DCHECK(num_new_entries == num_slots - page->num_allocated_slots);
-    // Discard the memory.
-    DiscardSystemPages(begin_ptr, unprovisioned_bytes);
   }
 
-  // Next, walk the slots and for any not in use, consider where the system
-  // page boundaries occur. We can release any system pages back to the
-  // system as long as we don't interfere with a freelist pointer or an
-  // adjacent slot.
+  // Next, walk the slots and for any not in use, consider where the system page
+  // boundaries occur. We can release any system pages back to the system as
+  // long as we don't interfere with a freelist pointer or an adjacent slot.
   for (size_t i = 0; i < num_slots; ++i) {
     if (slot_usage[i])
       continue;
     // The first address we can safely discard is just after the freelist
-    // pointer. There's one quirk: if the freelist pointer is actually a
-    // null, we can discard that pointer value too.
+    // pointer. There's one quirk: if the freelist pointer is actually NULL, we
+    // can discard that pointer value too.
     char* begin_ptr = ptr + (i * slot_size);
     char* end_ptr = begin_ptr + slot_size;
+#if !defined(OS_WIN)
     if (i != last_slot)
-      begin_ptr += sizeof(PartitionFreelistEntry);
+      begin_ptr += sizeof(internal::PartitionFreelistEntry);
+#else
+    begin_ptr += sizeof(internal::PartitionFreelistEntry);
+#endif
     begin_ptr = reinterpret_cast<char*>(
         RoundUpToSystemPage(reinterpret_cast<size_t>(begin_ptr)));
     end_ptr = reinterpret_cast<char*>(
@@ -1212,32 +599,33 @@
   return discardable_bytes;
 }
 
-static void PartitionPurgeBucket(PartitionBucket* bucket) {
-  if (bucket->active_pages_head != &PartitionRootGeneric::gSeedPage) {
-    for (PartitionPage* page = bucket->active_pages_head; page;
+static void PartitionPurgeBucket(internal::PartitionBucket* bucket) {
+  if (bucket->active_pages_head !=
+      internal::PartitionPage::get_sentinel_page()) {
+    for (internal::PartitionPage* page = bucket->active_pages_head; page;
          page = page->next_page) {
-      DCHECK(page != &PartitionRootGeneric::gSeedPage);
-      (void)PartitionPurgePage(page, true);
+      DCHECK(page != internal::PartitionPage::get_sentinel_page());
+      PartitionPurgePage(page, true);
     }
   }
 }
 
-void PartitionPurgeMemory(PartitionRoot* root, int flags) {
+void PartitionRoot::PurgeMemory(int flags) {
   if (flags & PartitionPurgeDecommitEmptyPages)
-    PartitionDecommitEmptyPages(root);
+    DecommitEmptyPages();
   // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages
-  // here because that flag is only useful for allocations >= system page
-  // size. We only have allocations that large inside generic partitions
-  // at the moment.
+  // here because that flag is only useful for allocations >= system page size.
+  // We only have allocations that large inside generic partitions at the
+  // moment.
 }
 
-void PartitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) {
-  subtle::SpinLock::Guard guard(root->lock);
+void PartitionRootGeneric::PurgeMemory(int flags) {
+  subtle::SpinLock::Guard guard(lock);
   if (flags & PartitionPurgeDecommitEmptyPages)
-    PartitionDecommitEmptyPages(root);
+    DecommitEmptyPages();
   if (flags & PartitionPurgeDiscardUnusedSystemPages) {
     for (size_t i = 0; i < kGenericNumBuckets; ++i) {
-      PartitionBucket* bucket = &root->buckets[i];
+      internal::PartitionBucket* bucket = &buckets[i];
       if (bucket->slot_size >= kSystemPageSize)
         PartitionPurgeBucket(bucket);
     }
@@ -1245,47 +633,48 @@
 }
 
 static void PartitionDumpPageStats(PartitionBucketMemoryStats* stats_out,
-                                   const PartitionPage* page) {
-  uint16_t bucket_num_slots = PartitionBucketSlots(page->bucket);
+                                   internal::PartitionPage* page) {
+  uint16_t bucket_num_slots = page->bucket->get_slots_per_span();
 
-  if (PartitionPageStateIsDecommitted(page)) {
+  if (page->is_decommitted()) {
     ++stats_out->num_decommitted_pages;
     return;
   }
 
-  stats_out->discardable_bytes +=
-      PartitionPurgePage(const_cast<PartitionPage*>(page), false);
+  stats_out->discardable_bytes += PartitionPurgePage(page, false);
 
-  size_t raw_size = PartitionPageGetRawSize(const_cast<PartitionPage*>(page));
-  if (raw_size)
+  size_t raw_size = page->get_raw_size();
+  if (raw_size) {
     stats_out->active_bytes += static_cast<uint32_t>(raw_size);
-  else
+  } else {
     stats_out->active_bytes +=
         (page->num_allocated_slots * stats_out->bucket_slot_size);
+  }
 
   size_t page_bytes_resident =
       RoundUpToSystemPage((bucket_num_slots - page->num_unprovisioned_slots) *
                           stats_out->bucket_slot_size);
   stats_out->resident_bytes += page_bytes_resident;
-  if (PartitionPageStateIsEmpty(page)) {
+  if (page->is_empty()) {
     stats_out->decommittable_bytes += page_bytes_resident;
     ++stats_out->num_empty_pages;
-  } else if (PartitionPageStateIsFull(page)) {
+  } else if (page->is_full()) {
     ++stats_out->num_full_pages;
   } else {
-    DCHECK(PartitionPageStateIsActive(page));
+    DCHECK(page->is_active());
     ++stats_out->num_active_pages;
   }
 }
 
 static void PartitionDumpBucketStats(PartitionBucketMemoryStats* stats_out,
-                                     const PartitionBucket* bucket) {
-  DCHECK(!PartitionBucketIsDirectMapped(bucket));
+                                     const internal::PartitionBucket* bucket) {
+  DCHECK(!bucket->is_direct_mapped());
   stats_out->is_valid = false;
-  // If the active page list is empty (== &PartitionRootGeneric::gSeedPage),
-  // the bucket might still need to be reported if it has a list of empty,
-  // decommitted or full pages.
-  if (bucket->active_pages_head == &PartitionRootGeneric::gSeedPage &&
+  // If the active page list is empty (==
+  // internal::PartitionPage::get_sentinel_page()), the bucket might still need
+  // to be reported if it has a list of empty, decommitted or full pages.
+  if (bucket->active_pages_head ==
+          internal::PartitionPage::get_sentinel_page() &&
       !bucket->empty_pages_head && !bucket->decommitted_pages_head &&
       !bucket->num_full_pages)
     return;
@@ -1295,42 +684,41 @@
   stats_out->is_direct_map = false;
   stats_out->num_full_pages = static_cast<size_t>(bucket->num_full_pages);
   stats_out->bucket_slot_size = bucket->slot_size;
-  uint16_t bucket_num_slots = PartitionBucketSlots(bucket);
+  uint16_t bucket_num_slots = bucket->get_slots_per_span();
   size_t bucket_useful_storage = stats_out->bucket_slot_size * bucket_num_slots;
-  stats_out->allocated_page_size = PartitionBucketBytes(bucket);
+  stats_out->allocated_page_size = bucket->get_bytes_per_span();
   stats_out->active_bytes = bucket->num_full_pages * bucket_useful_storage;
   stats_out->resident_bytes =
       bucket->num_full_pages * stats_out->allocated_page_size;
 
-  for (const PartitionPage* page = bucket->empty_pages_head; page;
+  for (internal::PartitionPage* page = bucket->empty_pages_head; page;
        page = page->next_page) {
-    DCHECK(PartitionPageStateIsEmpty(page) ||
-           PartitionPageStateIsDecommitted(page));
+    DCHECK(page->is_empty() || page->is_decommitted());
     PartitionDumpPageStats(stats_out, page);
   }
-  for (const PartitionPage* page = bucket->decommitted_pages_head; page;
+  for (internal::PartitionPage* page = bucket->decommitted_pages_head; page;
        page = page->next_page) {
-    DCHECK(PartitionPageStateIsDecommitted(page));
+    DCHECK(page->is_decommitted());
     PartitionDumpPageStats(stats_out, page);
   }
 
-  if (bucket->active_pages_head != &PartitionRootGeneric::gSeedPage) {
-    for (const PartitionPage* page = bucket->active_pages_head; page;
+  if (bucket->active_pages_head !=
+      internal::PartitionPage::get_sentinel_page()) {
+    for (internal::PartitionPage* page = bucket->active_pages_head; page;
          page = page->next_page) {
-      DCHECK(page != &PartitionRootGeneric::gSeedPage);
+      DCHECK(page != internal::PartitionPage::get_sentinel_page());
       PartitionDumpPageStats(stats_out, page);
     }
   }
 }
 
-void PartitionDumpStatsGeneric(PartitionRootGeneric* partition,
-                               const char* partition_name,
-                               bool is_light_dump,
-                               PartitionStatsDumper* dumper) {
+void PartitionRootGeneric::DumpStats(const char* partition_name,
+                                     bool is_light_dump,
+                                     PartitionStatsDumper* dumper) {
   PartitionMemoryStats stats = {0};
-  stats.total_mmapped_bytes = partition->total_size_of_super_pages +
-                              partition->total_size_of_direct_mapped_pages;
-  stats.total_committed_bytes = partition->total_size_of_committed_pages;
+  stats.total_mmapped_bytes =
+      total_size_of_super_pages + total_size_of_direct_mapped_pages;
+  stats.total_committed_bytes = total_size_of_committed_pages;
 
   size_t direct_mapped_allocations_total_size = 0;
 
@@ -1347,13 +735,13 @@
   PartitionBucketMemoryStats bucket_stats[kGenericNumBuckets];
   size_t num_direct_mapped_allocations = 0;
   {
-    subtle::SpinLock::Guard guard(partition->lock);
+    subtle::SpinLock::Guard guard(lock);
 
     for (size_t i = 0; i < kGenericNumBuckets; ++i) {
-      const PartitionBucket* bucket = &partition->buckets[i];
+      const internal::PartitionBucket* bucket = &buckets[i];
       // Don't report the pseudo buckets that the generic allocator sets up in
       // order to preserve a fast size->bucket map (see
-      // PartitionAllocGenericInit for details).
+      // PartitionRootGeneric::Init() for details).
       if (!bucket->active_pages_head)
         bucket_stats[i].is_valid = false;
       else
@@ -1366,7 +754,7 @@
       }
     }
 
-    for (PartitionDirectMapExtent *extent = partition->direct_map_list;
+    for (internal::PartitionDirectMapExtent* extent = direct_map_list;
          extent && num_direct_mapped_allocations < kMaxReportableDirectMaps;
          extent = extent->next_extent, ++num_direct_mapped_allocations) {
       DCHECK(!extent->next_extent ||
@@ -1381,8 +769,8 @@
 
   if (!is_light_dump) {
     // Call |PartitionsDumpBucketStats| after collecting stats because it can
-    // try to allocate using |PartitionAllocGeneric| and it can't obtain the
-    // lock.
+    // try to allocate using |PartitionRootGeneric::Alloc()| and it can't
+    // obtain the lock.
     for (size_t i = 0; i < kGenericNumBuckets; ++i) {
       if (bucket_stats[i].is_valid)
         dumper->PartitionsDumpBucketStats(partition_name, &bucket_stats[i]);
@@ -1391,16 +779,15 @@
     for (size_t i = 0; i < num_direct_mapped_allocations; ++i) {
       uint32_t size = direct_map_lengths[i];
 
-      PartitionBucketMemoryStats stats;
-      memset(&stats, '\0', sizeof(stats));
-      stats.is_valid = true;
-      stats.is_direct_map = true;
-      stats.num_full_pages = 1;
-      stats.allocated_page_size = size;
-      stats.bucket_slot_size = size;
-      stats.active_bytes = size;
-      stats.resident_bytes = size;
-      dumper->PartitionsDumpBucketStats(partition_name, &stats);
+      PartitionBucketMemoryStats mapped_stats = {};
+      mapped_stats.is_valid = true;
+      mapped_stats.is_direct_map = true;
+      mapped_stats.num_full_pages = 1;
+      mapped_stats.allocated_page_size = size;
+      mapped_stats.bucket_slot_size = size;
+      mapped_stats.active_bytes = size;
+      mapped_stats.resident_bytes = size;
+      dumper->PartitionsDumpBucketStats(partition_name, &mapped_stats);
     }
   }
 
@@ -1409,31 +796,46 @@
   dumper->PartitionDumpTotals(partition_name, &stats);
 }
 
-void PartitionDumpStats(PartitionRoot* partition,
-                        const char* partition_name,
-                        bool is_light_dump,
-                        PartitionStatsDumper* dumper) {
-  static const size_t kMaxReportableBuckets = 4096 / sizeof(void*);
-  PartitionBucketMemoryStats memory_stats[kMaxReportableBuckets];
-  const size_t partitionNumBuckets = partition->num_buckets;
-  DCHECK(partitionNumBuckets <= kMaxReportableBuckets);
-
-  for (size_t i = 0; i < partitionNumBuckets; ++i)
-    PartitionDumpBucketStats(&memory_stats[i], &partition->buckets()[i]);
-
-  // PartitionsDumpBucketStats is called after collecting stats because it
-  // can use PartitionAlloc to allocate and this can affect the statistics.
+void PartitionRoot::DumpStats(const char* partition_name,
+                              bool is_light_dump,
+                              PartitionStatsDumper* dumper) {
   PartitionMemoryStats stats = {0};
-  stats.total_mmapped_bytes = partition->total_size_of_super_pages;
-  stats.total_committed_bytes = partition->total_size_of_committed_pages;
-  DCHECK(!partition->total_size_of_direct_mapped_pages);
-  for (size_t i = 0; i < partitionNumBuckets; ++i) {
-    if (memory_stats[i].is_valid) {
-      stats.total_resident_bytes += memory_stats[i].resident_bytes;
-      stats.total_active_bytes += memory_stats[i].active_bytes;
-      stats.total_decommittable_bytes += memory_stats[i].decommittable_bytes;
-      stats.total_discardable_bytes += memory_stats[i].discardable_bytes;
-      if (!is_light_dump)
+  stats.total_mmapped_bytes = total_size_of_super_pages;
+  stats.total_committed_bytes = total_size_of_committed_pages;
+  DCHECK(!total_size_of_direct_mapped_pages);
+
+  static constexpr size_t kMaxReportableBuckets = 4096 / sizeof(void*);
+  std::unique_ptr<PartitionBucketMemoryStats[]> memory_stats;
+  if (!is_light_dump) {
+    memory_stats = std::unique_ptr<PartitionBucketMemoryStats[]>(
+        new PartitionBucketMemoryStats[kMaxReportableBuckets]);
+  }
+
+  const size_t partition_num_buckets = num_buckets;
+  DCHECK(partition_num_buckets <= kMaxReportableBuckets);
+
+  for (size_t i = 0; i < partition_num_buckets; ++i) {
+    PartitionBucketMemoryStats bucket_stats = {0};
+    PartitionDumpBucketStats(&bucket_stats, &buckets()[i]);
+    if (bucket_stats.is_valid) {
+      stats.total_resident_bytes += bucket_stats.resident_bytes;
+      stats.total_active_bytes += bucket_stats.active_bytes;
+      stats.total_decommittable_bytes += bucket_stats.decommittable_bytes;
+      stats.total_discardable_bytes += bucket_stats.discardable_bytes;
+    }
+    if (!is_light_dump) {
+      if (bucket_stats.is_valid)
+        memory_stats[i] = bucket_stats;
+      else
+        memory_stats[i].is_valid = false;
+    }
+  }
+  if (!is_light_dump) {
+    // PartitionsDumpBucketStats is called after collecting stats because it
+    // can use PartitionRoot::Alloc() to allocate and this can affect the
+    // statistics.
+    for (size_t i = 0; i < partition_num_buckets; ++i) {
+      if (memory_stats[i].is_valid)
         dumper->PartitionsDumpBucketStats(partition_name, &memory_stats[i]);
     }
   }
diff --git a/third_party/base/allocator/partition_allocator/partition_alloc.h b/third_party/base/allocator/partition_allocator/partition_alloc.h
index 7e8415c..e3ce36c 100644
--- a/third_party/base/allocator/partition_allocator/partition_alloc.h
+++ b/third_party/base/allocator/partition_allocator/partition_alloc.h
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H
-#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_
 
 // DESCRIPTION
-// partitionAlloc() / PartitionAllocGeneric() and PartitionFree() /
-// PartitionFreeGeneric() are approximately analagous to malloc() and free().
+// PartitionRoot::Alloc() / PartitionRootGeneric::Alloc() and PartitionFree() /
+// PartitionRootGeneric::Free() are approximately analagous to malloc() and
+// free().
 //
 // The main difference is that a PartitionRoot / PartitionRootGeneric object
 // must be supplied to these functions, representing a specific "heap partition"
@@ -23,14 +24,14 @@
 // PartitionRoot is really just a header adjacent to other data areas provided
 // by the allocator class.
 //
-// The partitionAlloc() variant of the API has the following caveats:
+// The PartitionRoot::Alloc() variant of the API has the following caveats:
 // - Allocations and frees against a single partition must be single threaded.
 // - Allocations must not exceed a max size, chosen at compile-time via a
 // templated parameter to PartitionAllocator.
 // - Allocation sizes must be aligned to the system pointer size.
 // - Allocations are bucketed exactly according to size.
 //
-// And for PartitionAllocGeneric():
+// And for PartitionRootGeneric::Alloc():
 // - Multi-threaded use against a single partition is ok; locking is handled.
 // - Allocations of any arbitrary size can be handled (subject to a limit of
 // INT_MAX bytes for security reasons).
@@ -62,304 +63,115 @@
 #include <limits.h>
 #include <string.h>
 
+#include "build/build_config.h"
 #include "third_party/base/allocator/partition_allocator/page_allocator.h"
+#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
+#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
+#include "third_party/base/allocator/partition_allocator/partition_cookie.h"
+#include "third_party/base/allocator/partition_allocator/partition_page.h"
+#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
 #include "third_party/base/allocator/partition_allocator/spin_lock.h"
+#include "third_party/base/base_export.h"
 #include "third_party/base/bits.h"
 #include "third_party/base/compiler_specific.h"
 #include "third_party/base/logging.h"
+#include "third_party/base/stl_util.h"
 #include "third_party/base/sys_byteorder.h"
-#include "third_party/build/build_config.h"
 
 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 #include <stdlib.h>
 #endif
 
+// We use this to make MEMORY_TOOL_REPLACES_ALLOCATOR behave the same for max
+// size as other alloc code.
+#define CHECK_MAX_SIZE_OR_RETURN_NULLPTR(size, flags) \
+  if (size > kGenericMaxDirectMapped) {               \
+    if (flags & PartitionAllocReturnNull) {           \
+      return nullptr;                                 \
+    }                                                 \
+    CHECK(false);                                     \
+  }
+
 namespace pdfium {
 namespace base {
 
-// Allocation granularity of sizeof(void*) bytes.
-static const size_t kAllocationGranularity = sizeof(void*);
-static const size_t kAllocationGranularityMask = kAllocationGranularity - 1;
-static const size_t kBucketShift = (kAllocationGranularity == 8) ? 3 : 2;
+class PartitionStatsDumper;
 
-// Underlying partition storage pages are a power-of-two size. It is typical
-// for a partition page to be based on multiple system pages. Most references to
-// "page" refer to partition pages.
-// We also have the concept of "super pages" -- these are the underlying system
-// allocations we make. Super pages contain multiple partition pages inside them
-// and include space for a small amount of metadata per partition page.
-// Inside super pages, we store "slot spans". A slot span is a continguous range
-// of one or more partition pages that stores allocations of the same size.
-// Slot span sizes are adjusted depending on the allocation size, to make sure
-// the packing does not lead to unused (wasted) space at the end of the last
-// system page of the span. For our current max slot span size of 64k and other
-// constant values, we pack _all_ PartitionAllocGeneric() sizes perfectly up
-// against the end of a system page.
-#if defined(_MIPS_ARCH_LOONGSON)
-static const size_t kPartitionPageShift = 16;  // 64KB
-#else
-static const size_t kPartitionPageShift = 14;  // 16KB
-#endif
-static const size_t kPartitionPageSize = 1 << kPartitionPageShift;
-static const size_t kPartitionPageOffsetMask = kPartitionPageSize - 1;
-static const size_t kPartitionPageBaseMask = ~kPartitionPageOffsetMask;
-static const size_t kMaxPartitionPagesPerSlotSpan = 4;
-
-// To avoid fragmentation via never-used freelist entries, we hand out partition
-// freelist sections gradually, in units of the dominant system page size.
-// What we're actually doing is avoiding filling the full partition page (16 KB)
-// with freelist pointers right away. Writing freelist pointers will fault and
-// dirty a private page, which is very wasteful if we never actually store
-// objects there.
-static const size_t kNumSystemPagesPerPartitionPage =
-    kPartitionPageSize / kSystemPageSize;
-static const size_t kMaxSystemPagesPerSlotSpan =
-    kNumSystemPagesPerPartitionPage * kMaxPartitionPagesPerSlotSpan;
-
-// We reserve virtual address space in 2MB chunks (aligned to 2MB as well).
-// These chunks are called "super pages". We do this so that we can store
-// metadata in the first few pages of each 2MB aligned section. This leads to
-// a very fast free(). We specifically choose 2MB because this virtual address
-// block represents a full but single PTE allocation on ARM, ia32 and x64.
-//
-// The layout of the super page is as follows. The sizes below are the same
-// for 32 bit and 64 bit.
-//
-//   | Guard page (4KB)    |
-//   | Metadata page (4KB) |
-//   | Guard pages (8KB)   |
-//   | Slot span           |
-//   | Slot span           |
-//   | ...                 |
-//   | Slot span           |
-//   | Guard page (4KB)    |
-//
-//   - Each slot span is a contiguous range of one or more PartitionPages.
-//   - The metadata page has the following format. Note that the PartitionPage
-//     that is not at the head of a slot span is "unused". In other words,
-//     the metadata for the slot span is stored only in the first PartitionPage
-//     of the slot span. Metadata accesses to other PartitionPages are
-//     redirected to the first PartitionPage.
-//
-//     | SuperPageExtentEntry (32B)                 |
-//     | PartitionPage of slot span 1 (32B, used)   |
-//     | PartitionPage of slot span 1 (32B, unused) |
-//     | PartitionPage of slot span 1 (32B, unused) |
-//     | PartitionPage of slot span 2 (32B, used)   |
-//     | PartitionPage of slot span 3 (32B, used)   |
-//     | ...                                        |
-//     | PartitionPage of slot span N (32B, unused) |
-//
-// A direct mapped page has a similar layout to fake it looking like a super
-// page:
-//
-//     | Guard page (4KB)     |
-//     | Metadata page (4KB)  |
-//     | Guard pages (8KB)    |
-//     | Direct mapped object |
-//     | Guard page (4KB)     |
-//
-//    - The metadata page has the following layout:
-//
-//     | SuperPageExtentEntry (32B)    |
-//     | PartitionPage (32B)           |
-//     | PartitionBucket (32B)         |
-//     | PartitionDirectMapExtent (8B) |
-static const size_t kSuperPageShift = 21;  // 2MB
-static const size_t kSuperPageSize = 1 << kSuperPageShift;
-static const size_t kSuperPageOffsetMask = kSuperPageSize - 1;
-static const size_t kSuperPageBaseMask = ~kSuperPageOffsetMask;
-static const size_t kNumPartitionPagesPerSuperPage =
-    kSuperPageSize / kPartitionPageSize;
-
-static const size_t kPageMetadataShift = 5;  // 32 bytes per partition page.
-static const size_t kPageMetadataSize = 1 << kPageMetadataShift;
-
-// The following kGeneric* constants apply to the generic variants of the API.
-// The "order" of an allocation is closely related to the power-of-two size of
-// the allocation. More precisely, the order is the bit index of the
-// most-significant-bit in the allocation size, where the bit numbers starts
-// at index 1 for the least-significant-bit.
-// In terms of allocation sizes, order 0 covers 0, order 1 covers 1, order 2
-// covers 2->3, order 3 covers 4->7, order 4 covers 8->15.
-static const size_t kGenericMinBucketedOrder = 4;  // 8 bytes.
-static const size_t kGenericMaxBucketedOrder =
-    20;  // Largest bucketed order is 1<<(20-1) (storing 512KB -> almost 1MB)
-static const size_t kGenericNumBucketedOrders =
-    (kGenericMaxBucketedOrder - kGenericMinBucketedOrder) + 1;
-// Eight buckets per order (for the higher orders), e.g. order 8 is 128, 144,
-// 160, ..., 240:
-static const size_t kGenericNumBucketsPerOrderBits = 3;
-static const size_t kGenericNumBucketsPerOrder =
-    1 << kGenericNumBucketsPerOrderBits;
-static const size_t kGenericNumBuckets =
-    kGenericNumBucketedOrders * kGenericNumBucketsPerOrder;
-static const size_t kGenericSmallestBucket = 1
-                                             << (kGenericMinBucketedOrder - 1);
-static const size_t kGenericMaxBucketSpacing =
-    1 << ((kGenericMaxBucketedOrder - 1) - kGenericNumBucketsPerOrderBits);
-static const size_t kGenericMaxBucketed =
-    (1 << (kGenericMaxBucketedOrder - 1)) +
-    ((kGenericNumBucketsPerOrder - 1) * kGenericMaxBucketSpacing);
-static const size_t kGenericMinDirectMappedDownsize =
-    kGenericMaxBucketed +
-    1;  // Limit when downsizing a direct mapping using realloc().
-static const size_t kGenericMaxDirectMapped = INT_MAX - kSystemPageSize;
-static const size_t kBitsPerSizeT = sizeof(void*) * CHAR_BIT;
-
-// Constants for the memory reclaim logic.
-static const size_t kMaxFreeableSpans = 16;
-
-// If the total size in bytes of allocated but not committed pages exceeds this
-// value (probably it is a "out of virtual address space" crash),
-// a special crash stack trace is generated at |partitionOutOfMemory|.
-// This is to distinguish "out of virtual address space" from
-// "out of physical memory" in crash reports.
-static const size_t kReasonableSizeOfUnusedPages = 1024 * 1024 * 1024;  // 1GiB
-
-#if DCHECK_IS_ON()
-// These two byte values match tcmalloc.
-static const unsigned char kUninitializedByte = 0xAB;
-static const unsigned char kFreedByte = 0xCD;
-static const size_t kCookieSize =
-    16;  // Handles alignment up to XMM instructions on Intel.
-static const unsigned char kCookieValue[kCookieSize] = {
-    0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xD0, 0x0D,
-    0x13, 0x37, 0xF0, 0x05, 0xBA, 0x11, 0xAB, 0x1E};
-#endif
-
-struct PartitionBucket;
-struct PartitionRootBase;
-
-struct PartitionFreelistEntry {
-  PartitionFreelistEntry* next;
-};
-
-// Some notes on page states. A page can be in one of four major states:
-// 1) Active.
-// 2) Full.
-// 3) Empty.
-// 4) Decommitted.
-// An active page has available free slots. A full page has no free slots. An
-// empty page has no free slots, and a decommitted page is an empty page that
-// had its backing memory released back to the system.
-// There are two linked lists tracking the pages. The "active page" list is an
-// approximation of a list of active pages. It is an approximation because
-// full, empty and decommitted pages may briefly be present in the list until
-// we next do a scan over it.
-// The "empty page" list is an accurate list of pages which are either empty
-// or decommitted.
-//
-// The significant page transitions are:
-// - free() will detect when a full page has a slot free()'d and immediately
-// return the page to the head of the active list.
-// - free() will detect when a page is fully emptied. It _may_ add it to the
-// empty list or it _may_ leave it on the active list until a future list scan.
-// - malloc() _may_ scan the active page list in order to fulfil the request.
-// If it does this, full, empty and decommitted pages encountered will be
-// booted out of the active list. If there are no suitable active pages found,
-// an empty or decommitted page (if one exists) will be pulled from the empty
-// list on to the active list.
-struct PartitionPage {
-  PartitionFreelistEntry* freelist_head;
-  PartitionPage* next_page;
-  PartitionBucket* bucket;
-  // Deliberately signed, 0 for empty or decommitted page, -n for full pages:
-  int16_t num_allocated_slots;
-  uint16_t num_unprovisioned_slots;
-  uint16_t page_offset;
-  int16_t empty_cache_index;  // -1 if not in the empty cache.
-};
-
-struct PartitionBucket {
-  PartitionPage* active_pages_head;  // Accessed most in hot path => goes first.
-  PartitionPage* empty_pages_head;
-  PartitionPage* decommitted_pages_head;
-  uint32_t slot_size;
-  unsigned num_system_pages_per_slot_span : 8;
-  unsigned num_full_pages : 24;
-};
-
-// An "extent" is a span of consecutive superpages. We link to the partition's
-// next extent (if there is one) at the very start of a superpage's metadata
-// area.
-struct PartitionSuperPageExtentEntry {
-  PartitionRootBase* root;
-  char* super_page_base;
-  char* super_pages_end;
-  PartitionSuperPageExtentEntry* next;
-};
-
-struct PartitionDirectMapExtent {
-  PartitionDirectMapExtent* next_extent;
-  PartitionDirectMapExtent* prev_extent;
-  PartitionBucket* bucket;
-  size_t map_size;  // Mapped size, not including guard pages and meta-data.
-};
-
-struct BASE_EXPORT PartitionRootBase {
-  size_t total_size_of_committed_pages;
-  size_t total_size_of_super_pages;
-  size_t total_size_of_direct_mapped_pages;
-  // Invariant: total_size_of_committed_pages <=
-  //                total_size_of_super_pages +
-  //                total_size_of_direct_mapped_pages.
-  unsigned num_buckets;
-  unsigned max_allocation;
-  bool initialized;
-  char* next_super_page;
-  char* next_partition_page;
-  char* next_partition_page_end;
-  PartitionSuperPageExtentEntry* current_extent;
-  PartitionSuperPageExtentEntry* first_extent;
-  PartitionDirectMapExtent* direct_map_list;
-  PartitionPage* global_empty_page_ring[kMaxFreeableSpans];
-  int16_t global_empty_page_ring_index;
-  uintptr_t inverted_self;
-
-  static subtle::SpinLock gInitializedLock;
-  static bool gInitialized;
-  // gSeedPage is used as a sentinel to indicate that there is no page
-  // in the active page list. We can use nullptr, but in that case we need
-  // to add a null-check branch to the hot allocation path. We want to avoid
-  // that.
-  static PartitionPage gSeedPage;
-  static PartitionBucket gPagedBucket;
-  // gOomHandlingFunction is invoked when ParitionAlloc hits OutOfMemory.
-  static void (*gOomHandlingFunction)();
+enum PartitionPurgeFlags {
+  // Decommitting the ring list of empty pages is reasonably fast.
+  PartitionPurgeDecommitEmptyPages = 1 << 0,
+  // Discarding unused system pages is slower, because it involves walking all
+  // freelists in all active partition pages of all buckets >= system page
+  // size. It often frees a similar amount of memory to decommitting the empty
+  // pages, though.
+  PartitionPurgeDiscardUnusedSystemPages = 1 << 1,
 };
 
 // Never instantiate a PartitionRoot directly, instead use PartitionAlloc.
-struct PartitionRoot : public PartitionRootBase {
+struct BASE_EXPORT PartitionRoot : public internal::PartitionRootBase {
+  PartitionRoot();
+  ~PartitionRoot() override;
+  // This references the buckets OFF the edge of this struct. All uses of
+  // PartitionRoot must have the bucket array come right after.
+  //
   // The PartitionAlloc templated class ensures the following is correct.
-  ALWAYS_INLINE PartitionBucket* buckets() {
-    return reinterpret_cast<PartitionBucket*>(this + 1);
+  ALWAYS_INLINE internal::PartitionBucket* buckets() {
+    return reinterpret_cast<internal::PartitionBucket*>(this + 1);
   }
-  ALWAYS_INLINE const PartitionBucket* buckets() const {
-    return reinterpret_cast<const PartitionBucket*>(this + 1);
+  ALWAYS_INLINE const internal::PartitionBucket* buckets() const {
+    return reinterpret_cast<const internal::PartitionBucket*>(this + 1);
   }
+
+  void Init(size_t bucket_count, size_t maximum_allocation);
+
+  ALWAYS_INLINE void* Alloc(size_t size, const char* type_name);
+  ALWAYS_INLINE void* AllocFlags(int flags, size_t size, const char* type_name);
+
+  void PurgeMemory(int flags) override;
+
+  void DumpStats(const char* partition_name,
+                 bool is_light_dump,
+                 PartitionStatsDumper* dumper);
 };
 
 // Never instantiate a PartitionRootGeneric directly, instead use
 // PartitionAllocatorGeneric.
-struct PartitionRootGeneric : public PartitionRootBase {
+struct BASE_EXPORT PartitionRootGeneric : public internal::PartitionRootBase {
+  PartitionRootGeneric();
+  ~PartitionRootGeneric() override;
   subtle::SpinLock lock;
   // Some pre-computed constants.
-  size_t order_index_shifts[kBitsPerSizeT + 1];
-  size_t order_sub_index_masks[kBitsPerSizeT + 1];
+  size_t order_index_shifts[kBitsPerSizeT + 1] = {};
+  size_t order_sub_index_masks[kBitsPerSizeT + 1] = {};
   // The bucket lookup table lets us map a size_t to a bucket quickly.
   // The trailing +1 caters for the overflow case for very large allocation
   // sizes.  It is one flat array instead of a 2D array because in the 2D
   // world, we'd need to index array[blah][max+1] which risks undefined
   // behavior.
-  PartitionBucket*
-      bucket_lookups[((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder) + 1];
-  PartitionBucket buckets[kGenericNumBuckets];
-};
+  internal::PartitionBucket*
+      bucket_lookups[((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder) + 1] =
+          {};
+  internal::PartitionBucket buckets[kGenericNumBuckets] = {};
 
-// Flags for PartitionAllocGenericFlags.
-enum PartitionAllocFlags {
-  PartitionAllocReturnNull = 1 << 0,
+  // Public API.
+  void Init();
+
+  ALWAYS_INLINE void* Alloc(size_t size, const char* type_name);
+  ALWAYS_INLINE void* AllocFlags(int flags, size_t size, const char* type_name);
+  ALWAYS_INLINE void Free(void* ptr);
+
+  NOINLINE void* Realloc(void* ptr, size_t new_size, const char* type_name);
+  // Overload that may return nullptr if reallocation isn't possible. In this
+  // case, |ptr| remains valid.
+  NOINLINE void* TryRealloc(void* ptr, size_t new_size, const char* type_name);
+
+  ALWAYS_INLINE size_t ActualSize(size_t size);
+
+  void PurgeMemory(int flags) override;
+
+  void DumpStats(const char* partition_name,
+                 bool is_light_dump,
+                 PartitionStatsDumper* partition_stats_dumper);
 };
 
 // Struct used to retrieve total memory usage of a partition. Used by
@@ -409,456 +221,117 @@
 };
 
 BASE_EXPORT void PartitionAllocGlobalInit(void (*oom_handling_function)());
-BASE_EXPORT void PartitionAllocInit(PartitionRoot*,
-                                    size_t num_buckets,
-                                    size_t max_allocation);
-BASE_EXPORT void PartitionAllocGenericInit(PartitionRootGeneric*);
 
-enum PartitionPurgeFlags {
-  // Decommitting the ring list of empty pages is reasonably fast.
-  PartitionPurgeDecommitEmptyPages = 1 << 0,
-  // Discarding unused system pages is slower, because it involves walking all
-  // freelists in all active partition pages of all buckets >= system page
-  // size. It often frees a similar amount of memory to decommitting the empty
-  // pages, though.
-  PartitionPurgeDiscardUnusedSystemPages = 1 << 1,
-};
-
-BASE_EXPORT void PartitionPurgeMemory(PartitionRoot*, int);
-BASE_EXPORT void PartitionPurgeMemoryGeneric(PartitionRootGeneric*, int);
-
-BASE_EXPORT NOINLINE void* PartitionAllocSlowPath(PartitionRootBase*,
-                                                  int,
-                                                  size_t,
-                                                  PartitionBucket*);
-BASE_EXPORT NOINLINE void PartitionFreeSlowPath(PartitionPage*);
-BASE_EXPORT NOINLINE void* PartitionReallocGeneric(PartitionRootGeneric*,
-                                                   void*,
-                                                   size_t,
-                                                   const char* type_name);
-
-BASE_EXPORT void PartitionDumpStats(PartitionRoot*,
-                                    const char* partition_name,
-                                    bool is_light_dump,
-                                    PartitionStatsDumper*);
-BASE_EXPORT void PartitionDumpStatsGeneric(PartitionRootGeneric*,
-                                           const char* partition_name,
-                                           bool is_light_dump,
-                                           PartitionStatsDumper*);
-
+// PartitionAlloc supports setting hooks to observe allocations/frees as they
+// occur as well as 'override' hooks that allow overriding those operations.
 class BASE_EXPORT PartitionAllocHooks {
  public:
-  typedef void AllocationHook(void* address, size_t, const char* type_name);
-  typedef void FreeHook(void* address);
-
-  static void SetAllocationHook(AllocationHook* hook) {
-    allocation_hook_ = hook;
-  }
-  static void SetFreeHook(FreeHook* hook) { free_hook_ = hook; }
-
-  static void AllocationHookIfEnabled(void* address,
+  // Log allocation and free events.
+  typedef void AllocationObserverHook(void* address,
                                       size_t size,
-                                      const char* type_name) {
-    AllocationHook* hook = allocation_hook_;
-    if (UNLIKELY(hook != nullptr))
-      hook(address, size, type_name);
+                                      const char* type_name);
+  typedef void FreeObserverHook(void* address);
+
+  // If it returns true, the allocation has been overridden with the pointer in
+  // *out.
+  typedef bool AllocationOverrideHook(void** out,
+                                      int flags,
+                                      size_t size,
+                                      const char* type_name);
+  // If it returns true, then the allocation was overridden and has been freed.
+  typedef bool FreeOverrideHook(void* address);
+  // If it returns true, the underlying allocation is overridden and *out holds
+  // the size of the underlying allocation.
+  typedef bool ReallocOverrideHook(size_t* out, void* address);
+
+  // To unhook, call Set*Hooks with nullptrs.
+  static void SetObserverHooks(AllocationObserverHook* alloc_hook,
+                               FreeObserverHook* free_hook);
+  static void SetOverrideHooks(AllocationOverrideHook* alloc_hook,
+                               FreeOverrideHook* free_hook,
+                               ReallocOverrideHook realloc_hook);
+
+  // Helper method to check whether hooks are enabled. This is an optimization
+  // so that if a function needs to call observer and override hooks in two
+  // different places this value can be cached and only loaded once.
+  static bool AreHooksEnabled() {
+    return hooks_enabled_.load(std::memory_order_relaxed);
   }
 
-  static void FreeHookIfEnabled(void* address) {
-    FreeHook* hook = free_hook_;
-    if (UNLIKELY(hook != nullptr))
-      hook(address);
-  }
+  static void AllocationObserverHookIfEnabled(void* address,
+                                              size_t size,
+                                              const char* type_name);
+  static bool AllocationOverrideHookIfEnabled(void** out,
+                                              int flags,
+                                              size_t size,
+                                              const char* type_name);
 
-  static void ReallocHookIfEnabled(void* old_address,
-                                   void* new_address,
-                                   size_t size,
-                                   const char* type_name) {
-    // Report a reallocation as a free followed by an allocation.
-    AllocationHook* allocation_hook = allocation_hook_;
-    FreeHook* free_hook = free_hook_;
-    if (UNLIKELY(allocation_hook && free_hook)) {
-      free_hook(old_address);
-      allocation_hook(new_address, size, type_name);
-    }
-  }
+  static void FreeObserverHookIfEnabled(void* address);
+  static bool FreeOverrideHookIfEnabled(void* address);
+
+  static void ReallocObserverHookIfEnabled(void* old_address,
+                                           void* new_address,
+                                           size_t size,
+                                           const char* type_name);
+  static bool ReallocOverrideHookIfEnabled(size_t* out, void* address);
 
  private:
-  // Pointers to hook functions that PartitionAlloc will call on allocation and
-  // free if the pointers are non-null.
-  static AllocationHook* allocation_hook_;
-  static FreeHook* free_hook_;
+  // Single bool that is used to indicate whether observer or allocation hooks
+  // are set to reduce the numbers of loads required to check whether hooking is
+  // enabled.
+  static std::atomic<bool> hooks_enabled_;
+
+  // Lock used to synchronize Set*Hooks calls.
+  static subtle::SpinLock set_hooks_lock_;
+
+  static std::atomic<AllocationObserverHook*> allocation_observer_hook_;
+  static std::atomic<FreeObserverHook*> free_observer_hook_;
+
+  static std::atomic<AllocationOverrideHook*> allocation_override_hook_;
+  static std::atomic<FreeOverrideHook*> free_override_hook_;
+  static std::atomic<ReallocOverrideHook*> realloc_override_hook_;
 };
 
-ALWAYS_INLINE PartitionFreelistEntry* PartitionFreelistMask(
-    PartitionFreelistEntry* ptr) {
-// We use bswap on little endian as a fast mask for two reasons:
-// 1) If an object is freed and its vtable used where the attacker doesn't
-// get the chance to run allocations between the free and use, the vtable
-// dereference is likely to fault.
-// 2) If the attacker has a linear buffer overflow and elects to try and
-// corrupt a freelist pointer, partial pointer overwrite attacks are
-// thwarted.
-// For big endian, similar guarantees are arrived at with a negation.
-#if defined(ARCH_CPU_BIG_ENDIAN)
-  uintptr_t masked = ~reinterpret_cast<uintptr_t>(ptr);
-#else
-  uintptr_t masked = ByteSwapUintPtrT(reinterpret_cast<uintptr_t>(ptr));
-#endif
-  return reinterpret_cast<PartitionFreelistEntry*>(masked);
+ALWAYS_INLINE void* PartitionRoot::Alloc(size_t size, const char* type_name) {
+  return AllocFlags(0, size, type_name);
 }
 
-ALWAYS_INLINE size_t PartitionCookieSizeAdjustAdd(size_t size) {
-#if DCHECK_IS_ON()
-  // Add space for cookies, checking for integer overflow. TODO(palmer):
-  // Investigate the performance and code size implications of using
-  // CheckedNumeric throughout PA.
-  DCHECK(size + (2 * kCookieSize) > size);
-  size += 2 * kCookieSize;
-#endif
-  return size;
-}
-
-ALWAYS_INLINE size_t PartitionCookieSizeAdjustSubtract(size_t size) {
-#if DCHECK_IS_ON()
-  // Remove space for cookies.
-  DCHECK(size >= 2 * kCookieSize);
-  size -= 2 * kCookieSize;
-#endif
-  return size;
-}
-
-ALWAYS_INLINE void* PartitionCookieFreePointerAdjust(void* ptr) {
-#if DCHECK_IS_ON()
-  // The value given to the application is actually just after the cookie.
-  ptr = static_cast<char*>(ptr) - kCookieSize;
-#endif
-  return ptr;
-}
-
-ALWAYS_INLINE void PartitionCookieWriteValue(void* ptr) {
-#if DCHECK_IS_ON()
-  auto* cookie_ptr = reinterpret_cast<unsigned char*>(ptr);
-  for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr)
-    *cookie_ptr = kCookieValue[i];
-#endif
-}
-
-ALWAYS_INLINE void PartitionCookieCheckValue(void* ptr) {
-#if DCHECK_IS_ON()
-  auto* cookie_ptr = reinterpret_cast<unsigned char*>(ptr);
-  for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr)
-    DCHECK(*cookie_ptr == kCookieValue[i]);
-#endif
-}
-
-ALWAYS_INLINE char* PartitionSuperPageToMetadataArea(char* ptr) {
-  auto pointer_as_uint = reinterpret_cast<uintptr_t>(ptr);
-  DCHECK(!(pointer_as_uint & kSuperPageOffsetMask));
-  // The metadata area is exactly one system page (the guard page) into the
-  // super page.
-  return reinterpret_cast<char*>(pointer_as_uint + kSystemPageSize);
-}
-
-ALWAYS_INLINE PartitionPage* PartitionPointerToPageNoAlignmentCheck(void* ptr) {
-  auto pointer_as_uint = reinterpret_cast<uintptr_t>(ptr);
-  auto* super_page_ptr =
-      reinterpret_cast<char*>(pointer_as_uint & kSuperPageBaseMask);
-  uintptr_t partition_page_index =
-      (pointer_as_uint & kSuperPageOffsetMask) >> kPartitionPageShift;
-  // Index 0 is invalid because it is the metadata and guard area and
-  // the last index is invalid because it is a guard page.
-  DCHECK(partition_page_index);
-  DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1);
-  auto* page = reinterpret_cast<PartitionPage*>(
-      PartitionSuperPageToMetadataArea(super_page_ptr) +
-      (partition_page_index << kPageMetadataShift));
-  // Partition pages in the same slot span can share the same page object.
-  // Adjust for that.
-  size_t delta = page->page_offset << kPageMetadataShift;
-  page =
-      reinterpret_cast<PartitionPage*>(reinterpret_cast<char*>(page) - delta);
-  return page;
-}
-
-ALWAYS_INLINE void* PartitionPageToPointer(const PartitionPage* page) {
-  auto pointer_as_uint = reinterpret_cast<uintptr_t>(page);
-  uintptr_t super_page_offset = (pointer_as_uint & kSuperPageOffsetMask);
-  DCHECK(super_page_offset > kSystemPageSize);
-  DCHECK(super_page_offset < kSystemPageSize + (kNumPartitionPagesPerSuperPage *
-                                                kPageMetadataSize));
-  uintptr_t partition_page_index =
-      (super_page_offset - kSystemPageSize) >> kPageMetadataShift;
-  // Index 0 is invalid because it is the metadata area and the last index is
-  // invalid because it is a guard page.
-  DCHECK(partition_page_index);
-  DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1);
-  uintptr_t super_page_base = (pointer_as_uint & kSuperPageBaseMask);
-  auto* ret = reinterpret_cast<void*>(
-      super_page_base + (partition_page_index << kPartitionPageShift));
-  return ret;
-}
-
-ALWAYS_INLINE PartitionPage* PartitionPointerToPage(void* ptr) {
-  PartitionPage* page = PartitionPointerToPageNoAlignmentCheck(ptr);
-  // Checks that the pointer is a multiple of bucket size.
-  DCHECK(!((reinterpret_cast<uintptr_t>(ptr) -
-            reinterpret_cast<uintptr_t>(PartitionPageToPointer(page))) %
-           page->bucket->slot_size));
-  return page;
-}
-
-ALWAYS_INLINE bool PartitionBucketIsDirectMapped(
-    const PartitionBucket* bucket) {
-  return !bucket->num_system_pages_per_slot_span;
-}
-
-ALWAYS_INLINE size_t PartitionBucketBytes(const PartitionBucket* bucket) {
-  return bucket->num_system_pages_per_slot_span * kSystemPageSize;
-}
-
-ALWAYS_INLINE uint16_t PartitionBucketSlots(const PartitionBucket* bucket) {
-  return static_cast<uint16_t>(PartitionBucketBytes(bucket) /
-                               bucket->slot_size);
-}
-
-ALWAYS_INLINE size_t* PartitionPageGetRawSizePtr(PartitionPage* page) {
-  // For single-slot buckets which span more than one partition page, we
-  // have some spare metadata space to store the raw allocation size. We
-  // can use this to report better statistics.
-  PartitionBucket* bucket = page->bucket;
-  if (bucket->slot_size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize)
-    return nullptr;
-
-  DCHECK((bucket->slot_size % kSystemPageSize) == 0);
-  DCHECK(PartitionBucketIsDirectMapped(bucket) ||
-         PartitionBucketSlots(bucket) == 1);
-  page++;
-  return reinterpret_cast<size_t*>(&page->freelist_head);
-}
-
-ALWAYS_INLINE size_t PartitionPageGetRawSize(PartitionPage* page) {
-  size_t* raw_size_ptr = PartitionPageGetRawSizePtr(page);
-  if (UNLIKELY(raw_size_ptr != nullptr))
-    return *raw_size_ptr;
-  return 0;
-}
-
-ALWAYS_INLINE PartitionRootBase* PartitionPageToRoot(PartitionPage* page) {
-  auto* extent_entry = reinterpret_cast<PartitionSuperPageExtentEntry*>(
-      reinterpret_cast<uintptr_t>(page) & kSystemPageBaseMask);
-  return extent_entry->root;
-}
-
-ALWAYS_INLINE bool PartitionPointerIsValid(void* ptr) {
-  PartitionPage* page = PartitionPointerToPage(ptr);
-  PartitionRootBase* root = PartitionPageToRoot(page);
-  return root->inverted_self == ~reinterpret_cast<uintptr_t>(root);
-}
-
-ALWAYS_INLINE void* PartitionBucketAlloc(PartitionRootBase* root,
-                                         int flags,
-                                         size_t size,
-                                         PartitionBucket* bucket) {
-  PartitionPage* page = bucket->active_pages_head;
-  // Check that this page is neither full nor freed.
-  DCHECK(page->num_allocated_slots >= 0);
-  void* ret = page->freelist_head;
-  if (LIKELY(ret)) {
-    // If these asserts fire, you probably corrupted memory.
-    DCHECK(PartitionPointerIsValid(ret));
-    // All large allocations must go through the slow path to correctly
-    // update the size metadata.
-    DCHECK(PartitionPageGetRawSize(page) == 0);
-    PartitionFreelistEntry* new_head =
-        PartitionFreelistMask(static_cast<PartitionFreelistEntry*>(ret)->next);
-    page->freelist_head = new_head;
-    page->num_allocated_slots++;
-  } else {
-    ret = PartitionAllocSlowPath(root, flags, size, bucket);
-    DCHECK(!ret || PartitionPointerIsValid(ret));
-  }
-#if DCHECK_IS_ON()
-  if (!ret)
-    return nullptr;
-  // Fill the uninitialized pattern, and write the cookies.
-  page = PartitionPointerToPage(ret);
-  size_t slot_size = page->bucket->slot_size;
-  size_t raw_size = PartitionPageGetRawSize(page);
-  if (raw_size) {
-    DCHECK(raw_size == size);
-    slot_size = raw_size;
-  }
-  size_t no_cookie_size = PartitionCookieSizeAdjustSubtract(slot_size);
-  auto* char_ret = static_cast<char*>(ret);
-  // The value given to the application is actually just after the cookie.
-  ret = char_ret + kCookieSize;
-  memset(ret, kUninitializedByte, no_cookie_size);
-  PartitionCookieWriteValue(char_ret);
-  PartitionCookieWriteValue(char_ret + kCookieSize + no_cookie_size);
-#endif
-  return ret;
-}
-
-ALWAYS_INLINE void* PartitionAlloc(PartitionRoot* root,
-                                   size_t size,
-                                   const char* type_name) {
+ALWAYS_INLINE void* PartitionRoot::AllocFlags(int flags,
+                                              size_t size,
+                                              const char* type_name) {
 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+  CHECK_MAX_SIZE_OR_RETURN_NULLPTR(size, flags);
   void* result = malloc(size);
   CHECK(result);
   return result;
 #else
+  DCHECK(max_allocation == 0 || size <= max_allocation);
+  void* result;
+  const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled();
+  if (UNLIKELY(hooks_enabled)) {
+    if (PartitionAllocHooks::AllocationOverrideHookIfEnabled(&result, flags,
+                                                             size, type_name)) {
+      PartitionAllocHooks::AllocationObserverHookIfEnabled(result, size,
+                                                           type_name);
+      return result;
+    }
+  }
   size_t requested_size = size;
-  size = PartitionCookieSizeAdjustAdd(size);
-  DCHECK(root->initialized);
+  size = internal::PartitionCookieSizeAdjustAdd(size);
+  DCHECK(initialized);
   size_t index = size >> kBucketShift;
-  DCHECK(index < root->num_buckets);
+  DCHECK(index < num_buckets);
   DCHECK(size == index << kBucketShift);
-  PartitionBucket* bucket = &root->buckets()[index];
-  void* result = PartitionBucketAlloc(root, 0, size, bucket);
-  PartitionAllocHooks::AllocationHookIfEnabled(result, requested_size,
-                                               type_name);
+  internal::PartitionBucket* bucket = &buckets()[index];
+  result = AllocFromBucket(bucket, flags, size);
+  if (UNLIKELY(hooks_enabled)) {
+    PartitionAllocHooks::AllocationObserverHookIfEnabled(result, requested_size,
+                                                         type_name);
+  }
   return result;
 #endif  // defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 }
 
-ALWAYS_INLINE void PartitionFreeWithPage(void* ptr, PartitionPage* page) {
-// If these asserts fire, you probably corrupted memory.
-#if DCHECK_IS_ON()
-  size_t slot_size = page->bucket->slot_size;
-  size_t raw_size = PartitionPageGetRawSize(page);
-  if (raw_size)
-    slot_size = raw_size;
-  PartitionCookieCheckValue(ptr);
-  PartitionCookieCheckValue(reinterpret_cast<char*>(ptr) + slot_size -
-                            kCookieSize);
-  memset(ptr, kFreedByte, slot_size);
-#endif
-  DCHECK(page->num_allocated_slots);
-  PartitionFreelistEntry* freelist_head = page->freelist_head;
-  DCHECK(!freelist_head || PartitionPointerIsValid(freelist_head));
-  CHECK(ptr != freelist_head);  // Catches an immediate double free.
-  // Look for double free one level deeper in debug.
-  DCHECK(!freelist_head || ptr != PartitionFreelistMask(freelist_head->next));
-  auto* entry = static_cast<PartitionFreelistEntry*>(ptr);
-  entry->next = PartitionFreelistMask(freelist_head);
-  page->freelist_head = entry;
-  --page->num_allocated_slots;
-  if (UNLIKELY(page->num_allocated_slots <= 0)) {
-    PartitionFreeSlowPath(page);
-  } else {
-    // All single-slot allocations must go through the slow path to
-    // correctly update the size metadata.
-    DCHECK(PartitionPageGetRawSize(page) == 0);
-  }
-}
-
-ALWAYS_INLINE void PartitionFree(void* ptr) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  free(ptr);
-#else
-  PartitionAllocHooks::FreeHookIfEnabled(ptr);
-  ptr = PartitionCookieFreePointerAdjust(ptr);
-  DCHECK(PartitionPointerIsValid(ptr));
-  PartitionPage* page = PartitionPointerToPage(ptr);
-  PartitionFreeWithPage(ptr, page);
-#endif
-}
-
-ALWAYS_INLINE PartitionBucket* PartitionGenericSizeToBucket(
-    PartitionRootGeneric* root,
-    size_t size) {
-  size_t order = kBitsPerSizeT - bits::CountLeadingZeroBitsSizeT(size);
-  // The order index is simply the next few bits after the most significant bit.
-  size_t order_index = (size >> root->order_index_shifts[order]) &
-                       (kGenericNumBucketsPerOrder - 1);
-  // And if the remaining bits are non-zero we must bump the bucket up.
-  size_t sub_order_index = size & root->order_sub_index_masks[order];
-  PartitionBucket* bucket =
-      root->bucket_lookups[(order << kGenericNumBucketsPerOrderBits) +
-                           order_index + !!sub_order_index];
-  DCHECK(!bucket->slot_size || bucket->slot_size >= size);
-  DCHECK(!(bucket->slot_size % kGenericSmallestBucket));
-  return bucket;
-}
-
-ALWAYS_INLINE void* PartitionAllocGenericFlags(PartitionRootGeneric* root,
-                                               int flags,
-                                               size_t size,
-                                               const char* type_name) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  void* result = malloc(size);
-  CHECK(result || flags & PartitionAllocReturnNull);
-  return result;
-#else
-  DCHECK(root->initialized);
-  size_t requested_size = size;
-  size = PartitionCookieSizeAdjustAdd(size);
-  PartitionBucket* bucket = PartitionGenericSizeToBucket(root, size);
-  void* ret = nullptr;
-  {
-    subtle::SpinLock::Guard guard(root->lock);
-    ret = PartitionBucketAlloc(root, flags, size, bucket);
-  }
-  PartitionAllocHooks::AllocationHookIfEnabled(ret, requested_size, type_name);
-  return ret;
-#endif
-}
-
-ALWAYS_INLINE void* PartitionAllocGeneric(PartitionRootGeneric* root,
-                                          size_t size,
-                                          const char* type_name) {
-  return PartitionAllocGenericFlags(root, 0, size, type_name);
-}
-
-ALWAYS_INLINE void PartitionFreeGeneric(PartitionRootGeneric* root, void* ptr) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  free(ptr);
-#else
-  DCHECK(root->initialized);
-
-  if (UNLIKELY(!ptr))
-    return;
-
-  PartitionAllocHooks::FreeHookIfEnabled(ptr);
-  ptr = PartitionCookieFreePointerAdjust(ptr);
-  DCHECK(PartitionPointerIsValid(ptr));
-  PartitionPage* page = PartitionPointerToPage(ptr);
-  {
-    subtle::SpinLock::Guard guard(root->lock);
-    PartitionFreeWithPage(ptr, page);
-  }
-#endif
-}
-
-ALWAYS_INLINE size_t PartitionDirectMapSize(size_t size) {
-  // Caller must check that the size is not above the kGenericMaxDirectMapped
-  // limit before calling. This also guards against integer overflow in the
-  // calculation here.
-  DCHECK(size <= kGenericMaxDirectMapped);
-  return (size + kSystemPageOffsetMask) & kSystemPageBaseMask;
-}
-
-ALWAYS_INLINE size_t PartitionAllocActualSize(PartitionRootGeneric* root,
-                                              size_t size) {
-#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  return size;
-#else
-  DCHECK(root->initialized);
-  size = PartitionCookieSizeAdjustAdd(size);
-  PartitionBucket* bucket = PartitionGenericSizeToBucket(root, size);
-  if (LIKELY(!PartitionBucketIsDirectMapped(bucket))) {
-    size = bucket->slot_size;
-  } else if (size > kGenericMaxDirectMapped) {
-    // Too large to allocate => return the size unchanged.
-  } else {
-    DCHECK(bucket == &PartitionRootBase::gPagedBucket);
-    size = PartitionDirectMapSize(size);
-  }
-  return PartitionCookieSizeAdjustSubtract(size);
-#endif
-}
-
 ALWAYS_INLINE bool PartitionAllocSupportsGetSize() {
 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
   return false;
@@ -871,34 +344,179 @@
   // No need to lock here. Only |ptr| being freed by another thread could
   // cause trouble, and the caller is responsible for that not happening.
   DCHECK(PartitionAllocSupportsGetSize());
-  ptr = PartitionCookieFreePointerAdjust(ptr);
-  DCHECK(PartitionPointerIsValid(ptr));
-  PartitionPage* page = PartitionPointerToPage(ptr);
+  ptr = internal::PartitionCookieFreePointerAdjust(ptr);
+  internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr);
+  // TODO(palmer): See if we can afford to make this a CHECK.
+  DCHECK(internal::PartitionRootBase::IsValidPage(page));
   size_t size = page->bucket->slot_size;
-  return PartitionCookieSizeAdjustSubtract(size);
+  return internal::PartitionCookieSizeAdjustSubtract(size);
 }
 
-// N (or more accurately, N - sizeof(void*)) represents the largest size in
-// bytes that will be handled by a SizeSpecificPartitionAllocator.
-// Attempts to partitionAlloc() more than this amount will fail.
+ALWAYS_INLINE void PartitionFree(void* ptr) {
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+  free(ptr);
+#else
+  // TODO(palmer): Check ptr alignment before continuing. Shall we do the check
+  // inside PartitionCookieFreePointerAdjust?
+  if (PartitionAllocHooks::AreHooksEnabled()) {
+    PartitionAllocHooks::FreeObserverHookIfEnabled(ptr);
+    if (PartitionAllocHooks::FreeOverrideHookIfEnabled(ptr))
+      return;
+  }
+
+  ptr = internal::PartitionCookieFreePointerAdjust(ptr);
+  internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr);
+  // TODO(palmer): See if we can afford to make this a CHECK.
+  DCHECK(internal::PartitionRootBase::IsValidPage(page));
+  page->Free(ptr);
+#endif
+}
+
+ALWAYS_INLINE internal::PartitionBucket* PartitionGenericSizeToBucket(
+    PartitionRootGeneric* root,
+    size_t size) {
+  size_t order = kBitsPerSizeT - bits::CountLeadingZeroBitsSizeT(size);
+  // The order index is simply the next few bits after the most significant bit.
+  size_t order_index = (size >> root->order_index_shifts[order]) &
+                       (kGenericNumBucketsPerOrder - 1);
+  // And if the remaining bits are non-zero we must bump the bucket up.
+  size_t sub_order_index = size & root->order_sub_index_masks[order];
+  internal::PartitionBucket* bucket =
+      root->bucket_lookups[(order << kGenericNumBucketsPerOrderBits) +
+                           order_index + !!sub_order_index];
+  CHECK(bucket);
+  DCHECK(!bucket->slot_size || bucket->slot_size >= size);
+  DCHECK(!(bucket->slot_size % kGenericSmallestBucket));
+  return bucket;
+}
+
+ALWAYS_INLINE void* PartitionAllocGenericFlags(PartitionRootGeneric* root,
+                                               int flags,
+                                               size_t size,
+                                               const char* type_name) {
+  DCHECK(flags < PartitionAllocLastFlag << 1);
+
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+  CHECK_MAX_SIZE_OR_RETURN_NULLPTR(size, flags);
+  const bool zero_fill = flags & PartitionAllocZeroFill;
+  void* result = zero_fill ? calloc(1, size) : malloc(size);
+  CHECK(result || flags & PartitionAllocReturnNull);
+  return result;
+#else
+  DCHECK(root->initialized);
+  // Only SizeSpecificPartitionAllocator should use max_allocation.
+  DCHECK(root->max_allocation == 0);
+  void* result;
+  const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled();
+  if (UNLIKELY(hooks_enabled)) {
+    if (PartitionAllocHooks::AllocationOverrideHookIfEnabled(&result, flags,
+                                                             size, type_name)) {
+      PartitionAllocHooks::AllocationObserverHookIfEnabled(result, size,
+                                                           type_name);
+      return result;
+    }
+  }
+  size_t requested_size = size;
+  size = internal::PartitionCookieSizeAdjustAdd(size);
+  internal::PartitionBucket* bucket = PartitionGenericSizeToBucket(root, size);
+  {
+    subtle::SpinLock::Guard guard(root->lock);
+    result = root->AllocFromBucket(bucket, flags, size);
+  }
+  if (UNLIKELY(hooks_enabled)) {
+    PartitionAllocHooks::AllocationObserverHookIfEnabled(result, requested_size,
+                                                         type_name);
+  }
+
+  return result;
+#endif
+}
+
+ALWAYS_INLINE void* PartitionRootGeneric::Alloc(size_t size,
+                                                const char* type_name) {
+  return PartitionAllocGenericFlags(this, 0, size, type_name);
+}
+
+ALWAYS_INLINE void* PartitionRootGeneric::AllocFlags(int flags,
+                                                     size_t size,
+                                                     const char* type_name) {
+  return PartitionAllocGenericFlags(this, flags, size, type_name);
+}
+
+ALWAYS_INLINE void PartitionRootGeneric::Free(void* ptr) {
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+  free(ptr);
+#else
+  DCHECK(initialized);
+
+  if (UNLIKELY(!ptr))
+    return;
+
+  if (PartitionAllocHooks::AreHooksEnabled()) {
+    PartitionAllocHooks::FreeObserverHookIfEnabled(ptr);
+    if (PartitionAllocHooks::FreeOverrideHookIfEnabled(ptr))
+      return;
+  }
+
+  ptr = internal::PartitionCookieFreePointerAdjust(ptr);
+  internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr);
+  // TODO(palmer): See if we can afford to make this a CHECK.
+  DCHECK(IsValidPage(page));
+  {
+    subtle::SpinLock::Guard guard(lock);
+    page->Free(ptr);
+  }
+#endif
+}
+
+BASE_EXPORT void* PartitionReallocGenericFlags(PartitionRootGeneric* root,
+                                               int flags,
+                                               void* ptr,
+                                               size_t new_size,
+                                               const char* type_name);
+
+ALWAYS_INLINE size_t PartitionRootGeneric::ActualSize(size_t size) {
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+  return size;
+#else
+  DCHECK(initialized);
+  size = internal::PartitionCookieSizeAdjustAdd(size);
+  internal::PartitionBucket* bucket = PartitionGenericSizeToBucket(this, size);
+  if (LIKELY(!bucket->is_direct_mapped())) {
+    size = bucket->slot_size;
+  } else if (size > kGenericMaxDirectMapped) {
+    // Too large to allocate => return the size unchanged.
+  } else {
+    size = internal::PartitionBucket::get_direct_map_size(size);
+  }
+  return internal::PartitionCookieSizeAdjustSubtract(size);
+#endif
+}
+
 template <size_t N>
 class SizeSpecificPartitionAllocator {
  public:
+  SizeSpecificPartitionAllocator() {
+    memset(actual_buckets_, 0,
+           sizeof(internal::PartitionBucket) * pdfium::size(actual_buckets_));
+  }
+  ~SizeSpecificPartitionAllocator() = default;
   static const size_t kMaxAllocation = N - kAllocationGranularity;
   static const size_t kNumBuckets = N / kAllocationGranularity;
-  void init() {
-    PartitionAllocInit(&partition_root_, kNumBuckets, kMaxAllocation);
-  }
+  void init() { partition_root_.Init(kNumBuckets, kMaxAllocation); }
   ALWAYS_INLINE PartitionRoot* root() { return &partition_root_; }
 
  private:
   PartitionRoot partition_root_;
-  PartitionBucket actual_buckets_[kNumBuckets];
+  internal::PartitionBucket actual_buckets_[kNumBuckets];
 };
 
-class PartitionAllocatorGeneric {
+class BASE_EXPORT PartitionAllocatorGeneric {
  public:
-  void init() { PartitionAllocGenericInit(&partition_root_); }
+  PartitionAllocatorGeneric();
+  ~PartitionAllocatorGeneric();
+
+  void init() { partition_root_.Init(); }
   ALWAYS_INLINE PartitionRootGeneric* root() { return &partition_root_; }
 
  private:
@@ -908,4 +526,4 @@
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_alloc_constants.h b/third_party/base/allocator/partition_allocator/partition_alloc_constants.h
new file mode 100644
index 0000000..8ebc4f5
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_alloc_constants.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_
+
+#include <limits.h>
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/page_allocator_constants.h"
+#include "third_party/base/logging.h"
+
+namespace pdfium {
+namespace base {
+
+// Allocation granularity of sizeof(void*) bytes.
+static const size_t kAllocationGranularity = sizeof(void*);
+static const size_t kAllocationGranularityMask = kAllocationGranularity - 1;
+static const size_t kBucketShift = (kAllocationGranularity == 8) ? 3 : 2;
+
+// Underlying partition storage pages (`PartitionPage`s) are a power-of-2 size.
+// It is typical for a `PartitionPage` to be based on multiple system pages.
+// Most references to "page" refer to `PartitionPage`s.
+//
+// *Super pages* are the underlying system allocations we make. Super pages
+// contain multiple partition pages and include space for a small amount of
+// metadata per partition page.
+//
+// Inside super pages, we store *slot spans*. A slot span is a continguous range
+// of one or more `PartitionPage`s that stores allocations of the same size.
+// Slot span sizes are adjusted depending on the allocation size, to make sure
+// the packing does not lead to unused (wasted) space at the end of the last
+// system page of the span. For our current maximum slot span size of 64 KiB and
+// other constant values, we pack _all_ `PartitionRootGeneric::Alloc` sizes
+// perfectly up against the end of a system page.
+
+#if defined(_MIPS_ARCH_LOONGSON)
+static const size_t kPartitionPageShift = 16;  // 64 KiB
+#elif defined(ARCH_CPU_PPC64)
+static const size_t kPartitionPageShift = 18;  // 256 KiB
+#else
+static const size_t kPartitionPageShift = 14;  // 16 KiB
+#endif
+static const size_t kPartitionPageSize = 1 << kPartitionPageShift;
+static const size_t kPartitionPageOffsetMask = kPartitionPageSize - 1;
+static const size_t kPartitionPageBaseMask = ~kPartitionPageOffsetMask;
+// TODO: Should this be 1 if defined(_MIPS_ARCH_LOONGSON)?
+static const size_t kMaxPartitionPagesPerSlotSpan = 4;
+
+// To avoid fragmentation via never-used freelist entries, we hand out partition
+// freelist sections gradually, in units of the dominant system page size. What
+// we're actually doing is avoiding filling the full `PartitionPage` (16 KiB)
+// with freelist pointers right away. Writing freelist pointers will fault and
+// dirty a private page, which is very wasteful if we never actually store
+// objects there.
+
+static const size_t kNumSystemPagesPerPartitionPage =
+    kPartitionPageSize / kSystemPageSize;
+static const size_t kMaxSystemPagesPerSlotSpan =
+    kNumSystemPagesPerPartitionPage * kMaxPartitionPagesPerSlotSpan;
+
+// We reserve virtual address space in 2 MiB chunks (aligned to 2 MiB as well).
+// These chunks are called *super pages*. We do this so that we can store
+// metadata in the first few pages of each 2 MiB-aligned section. This makes
+// freeing memory very fast. We specifically choose 2 MiB because this virtual
+// address block represents a full but single PTE allocation on ARM, ia32 and
+// x64.
+//
+// The layout of the super page is as follows. The sizes below are the same for
+// 32- and 64-bit platforms.
+//
+//     +-----------------------+
+//     | Guard page (4 KiB)    |
+//     | Metadata page (4 KiB) |
+//     | Guard pages (8 KiB)   |
+//     | Slot span             |
+//     | Slot span             |
+//     | ...                   |
+//     | Slot span             |
+//     | Guard page (4 KiB)    |
+//     +-----------------------+
+//
+// Each slot span is a contiguous range of one or more `PartitionPage`s.
+//
+// The metadata page has the following format. Note that the `PartitionPage`
+// that is not at the head of a slot span is "unused". In other words, the
+// metadata for the slot span is stored only in the first `PartitionPage` of the
+// slot span. Metadata accesses to other `PartitionPage`s are redirected to the
+// first `PartitionPage`.
+//
+//     +---------------------------------------------+
+//     | SuperPageExtentEntry (32 B)                 |
+//     | PartitionPage of slot span 1 (32 B, used)   |
+//     | PartitionPage of slot span 1 (32 B, unused) |
+//     | PartitionPage of slot span 1 (32 B, unused) |
+//     | PartitionPage of slot span 2 (32 B, used)   |
+//     | PartitionPage of slot span 3 (32 B, used)   |
+//     | ...                                         |
+//     | PartitionPage of slot span N (32 B, unused) |
+//     +---------------------------------------------+
+//
+// A direct-mapped page has a similar layout to fake it looking like a super
+// page:
+//
+//     +-----------------------+
+//     | Guard page (4 KiB)    |
+//     | Metadata page (4 KiB) |
+//     | Guard pages (8 KiB)   |
+//     | Direct mapped object  |
+//     | Guard page (4 KiB)    |
+//     +-----------------------+
+//
+// A direct-mapped page's metadata page has the following layout:
+//
+//     +--------------------------------+
+//     | SuperPageExtentEntry (32 B)    |
+//     | PartitionPage (32 B)           |
+//     | PartitionBucket (32 B)         |
+//     | PartitionDirectMapExtent (8 B) |
+//     +--------------------------------+
+
+static const size_t kSuperPageShift = 21;  // 2 MiB
+static const size_t kSuperPageSize = 1 << kSuperPageShift;
+static const size_t kSuperPageOffsetMask = kSuperPageSize - 1;
+static const size_t kSuperPageBaseMask = ~kSuperPageOffsetMask;
+static const size_t kNumPartitionPagesPerSuperPage =
+    kSuperPageSize / kPartitionPageSize;
+
+// The following kGeneric* constants apply to the generic variants of the API.
+// The "order" of an allocation is closely related to the power-of-1 size of the
+// allocation. More precisely, the order is the bit index of the
+// most-significant-bit in the allocation size, where the bit numbers starts at
+// index 1 for the least-significant-bit.
+//
+// In terms of allocation sizes, order 0 covers 0, order 1 covers 1, order 2
+// covers 2->3, order 3 covers 4->7, order 4 covers 8->15.
+
+static const size_t kGenericMinBucketedOrder = 4;  // 8 bytes.
+// The largest bucketed order is 1 << (20 - 1), storing [512 KiB, 1 MiB):
+static const size_t kGenericMaxBucketedOrder = 20;
+static const size_t kGenericNumBucketedOrders =
+    (kGenericMaxBucketedOrder - kGenericMinBucketedOrder) + 1;
+// Eight buckets per order (for the higher orders), e.g. order 8 is 128, 144,
+// 160, ..., 240:
+static const size_t kGenericNumBucketsPerOrderBits = 3;
+static const size_t kGenericNumBucketsPerOrder =
+    1 << kGenericNumBucketsPerOrderBits;
+static const size_t kGenericNumBuckets =
+    kGenericNumBucketedOrders * kGenericNumBucketsPerOrder;
+static const size_t kGenericSmallestBucket = 1
+                                             << (kGenericMinBucketedOrder - 1);
+static const size_t kGenericMaxBucketSpacing =
+    1 << ((kGenericMaxBucketedOrder - 1) - kGenericNumBucketsPerOrderBits);
+static const size_t kGenericMaxBucketed =
+    (1 << (kGenericMaxBucketedOrder - 1)) +
+    ((kGenericNumBucketsPerOrder - 1) * kGenericMaxBucketSpacing);
+// Limit when downsizing a direct mapping using `realloc`:
+static const size_t kGenericMinDirectMappedDownsize = kGenericMaxBucketed + 1;
+static const size_t kGenericMaxDirectMapped =
+    (1UL << 31) + kPageAllocationGranularity;  // 2 GiB plus 1 more page.
+static const size_t kBitsPerSizeT = sizeof(void*) * CHAR_BIT;
+
+// Constant for the memory reclaim logic.
+static const size_t kMaxFreeableSpans = 16;
+
+// If the total size in bytes of allocated but not committed pages exceeds this
+// value (probably it is a "out of virtual address space" crash), a special
+// crash stack trace is generated at
+// `PartitionOutOfMemoryWithLotsOfUncommitedPages`. This is to distinguish "out
+// of virtual address space" from "out of physical memory" in crash reports.
+static const size_t kReasonableSizeOfUnusedPages = 1024 * 1024 * 1024;  // 1 GiB
+
+// These byte values match tcmalloc.
+static const unsigned char kUninitializedByte = 0xAB;
+static const unsigned char kFreedByte = 0xCD;
+
+// Flags for `PartitionAllocGenericFlags`.
+enum PartitionAllocFlags {
+  PartitionAllocReturnNull = 1 << 0,
+  PartitionAllocZeroFill = 1 << 1,
+
+  PartitionAllocLastFlag = PartitionAllocZeroFill
+};
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_bucket.cc b/third_party/base/allocator/partition_allocator/partition_bucket.cc
new file mode 100644
index 0000000..7b24c90
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_bucket.cc
@@ -0,0 +1,569 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/oom.h"
+#include "third_party/base/allocator/partition_allocator/page_allocator.h"
+#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
+#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
+#include "third_party/base/allocator/partition_allocator/partition_oom.h"
+#include "third_party/base/allocator/partition_allocator/partition_page.h"
+#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+namespace {
+
+ALWAYS_INLINE PartitionPage* PartitionDirectMap(PartitionRootBase* root,
+                                                int flags,
+                                                size_t raw_size) {
+  size_t size = PartitionBucket::get_direct_map_size(raw_size);
+
+  // Because we need to fake looking like a super page, we need to allocate
+  // a bunch of system pages more than "size":
+  // - The first few system pages are the partition page in which the super
+  // page metadata is stored. We fault just one system page out of a partition
+  // page sized clump.
+  // - We add a trailing guard page on 32-bit (on 64-bit we rely on the
+  // massive address space plus randomization instead).
+  size_t map_size = size + kPartitionPageSize;
+#if !defined(ARCH_CPU_64_BITS)
+  map_size += kSystemPageSize;
+#endif
+  // Round up to the allocation granularity.
+  map_size += kPageAllocationGranularityOffsetMask;
+  map_size &= kPageAllocationGranularityBaseMask;
+
+  char* ptr = reinterpret_cast<char*>(AllocPages(nullptr, map_size,
+                                                 kSuperPageSize, PageReadWrite,
+                                                 PageTag::kPartitionAlloc));
+  if (UNLIKELY(!ptr))
+    return nullptr;
+
+  size_t committed_page_size = size + kSystemPageSize;
+  root->total_size_of_direct_mapped_pages += committed_page_size;
+  root->IncreaseCommittedPages(committed_page_size);
+
+  char* slot = ptr + kPartitionPageSize;
+  SetSystemPagesAccess(ptr + (kSystemPageSize * 2),
+                       kPartitionPageSize - (kSystemPageSize * 2),
+                       PageInaccessible);
+#if !defined(ARCH_CPU_64_BITS)
+  SetSystemPagesAccess(ptr, kSystemPageSize, PageInaccessible);
+  SetSystemPagesAccess(slot + size, kSystemPageSize, PageInaccessible);
+#endif
+
+  PartitionSuperPageExtentEntry* extent =
+      reinterpret_cast<PartitionSuperPageExtentEntry*>(
+          PartitionSuperPageToMetadataArea(ptr));
+  extent->root = root;
+  // The new structures are all located inside a fresh system page so they
+  // will all be zeroed out. These DCHECKs are for documentation.
+  DCHECK(!extent->super_page_base);
+  DCHECK(!extent->super_pages_end);
+  DCHECK(!extent->next);
+  PartitionPage* page = PartitionPage::FromPointerNoAlignmentCheck(slot);
+  PartitionBucket* bucket = reinterpret_cast<PartitionBucket*>(
+      reinterpret_cast<char*>(page) + (kPageMetadataSize * 2));
+  DCHECK(!page->next_page);
+  DCHECK(!page->num_allocated_slots);
+  DCHECK(!page->num_unprovisioned_slots);
+  DCHECK(!page->page_offset);
+  DCHECK(!page->empty_cache_index);
+  page->bucket = bucket;
+  page->freelist_head = reinterpret_cast<PartitionFreelistEntry*>(slot);
+  PartitionFreelistEntry* next_entry =
+      reinterpret_cast<PartitionFreelistEntry*>(slot);
+  next_entry->next = PartitionFreelistEntry::Encode(nullptr);
+
+  DCHECK(!bucket->active_pages_head);
+  DCHECK(!bucket->empty_pages_head);
+  DCHECK(!bucket->decommitted_pages_head);
+  DCHECK(!bucket->num_system_pages_per_slot_span);
+  DCHECK(!bucket->num_full_pages);
+  bucket->slot_size = size;
+
+  PartitionDirectMapExtent* map_extent =
+      PartitionDirectMapExtent::FromPage(page);
+  map_extent->map_size = map_size - kPartitionPageSize - kSystemPageSize;
+  map_extent->bucket = bucket;
+
+  // Maintain the doubly-linked list of all direct mappings.
+  map_extent->next_extent = root->direct_map_list;
+  if (map_extent->next_extent)
+    map_extent->next_extent->prev_extent = map_extent;
+  map_extent->prev_extent = nullptr;
+  root->direct_map_list = map_extent;
+
+  return page;
+}
+
+}  // namespace
+
+// static
+PartitionBucket PartitionBucket::sentinel_bucket_;
+
+PartitionBucket* PartitionBucket::get_sentinel_bucket() {
+  return &sentinel_bucket_;
+}
+
+// TODO(ajwong): This seems to interact badly with
+// get_pages_per_slot_span() which rounds the value from this up to a
+// multiple of kNumSystemPagesPerPartitionPage (aka 4) anyways.
+// http://crbug.com/776537
+//
+// TODO(ajwong): The waste calculation seems wrong. The PTE usage should cover
+// both used and unsed pages.
+// http://crbug.com/776537
+uint8_t PartitionBucket::get_system_pages_per_slot_span() {
+  // This works out reasonably for the current bucket sizes of the generic
+  // allocator, and the current values of partition page size and constants.
+  // Specifically, we have enough room to always pack the slots perfectly into
+  // some number of system pages. The only waste is the waste associated with
+  // unfaulted pages (i.e. wasted address space).
+  // TODO: we end up using a lot of system pages for very small sizes. For
+  // example, we'll use 12 system pages for slot size 24. The slot size is
+  // so small that the waste would be tiny with just 4, or 1, system pages.
+  // Later, we can investigate whether there are anti-fragmentation benefits
+  // to using fewer system pages.
+  double best_waste_ratio = 1.0f;
+  uint16_t best_pages = 0;
+  if (slot_size > kMaxSystemPagesPerSlotSpan * kSystemPageSize) {
+    // TODO(ajwong): Why is there a DCHECK here for this?
+    // http://crbug.com/776537
+    DCHECK(!(slot_size % kSystemPageSize));
+    best_pages = static_cast<uint16_t>(slot_size / kSystemPageSize);
+    // TODO(ajwong): Should this be checking against
+    // kMaxSystemPagesPerSlotSpan or numeric_limits<uint8_t>::max?
+    // http://crbug.com/776537
+    CHECK(best_pages < (1 << 8));
+    return static_cast<uint8_t>(best_pages);
+  }
+  DCHECK(slot_size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize);
+  for (uint16_t i = kNumSystemPagesPerPartitionPage - 1;
+       i <= kMaxSystemPagesPerSlotSpan; ++i) {
+    size_t page_size = kSystemPageSize * i;
+    size_t num_slots = page_size / slot_size;
+    size_t waste = page_size - (num_slots * slot_size);
+    // Leaving a page unfaulted is not free; the page will occupy an empty page
+    // table entry.  Make a simple attempt to account for that.
+    //
+    // TODO(ajwong): This looks wrong. PTEs are allocated for all pages
+    // regardless of whether or not they are wasted. Should it just
+    // be waste += i * sizeof(void*)?
+    // http://crbug.com/776537
+    size_t num_remainder_pages = i & (kNumSystemPagesPerPartitionPage - 1);
+    size_t num_unfaulted_pages =
+        num_remainder_pages
+            ? (kNumSystemPagesPerPartitionPage - num_remainder_pages)
+            : 0;
+    waste += sizeof(void*) * num_unfaulted_pages;
+    double waste_ratio =
+        static_cast<double>(waste) / static_cast<double>(page_size);
+    if (waste_ratio < best_waste_ratio) {
+      best_waste_ratio = waste_ratio;
+      best_pages = i;
+    }
+  }
+  DCHECK(best_pages > 0);
+  CHECK(best_pages <= kMaxSystemPagesPerSlotSpan);
+  return static_cast<uint8_t>(best_pages);
+}
+
+void PartitionBucket::Init(uint32_t new_slot_size) {
+  slot_size = new_slot_size;
+  active_pages_head = PartitionPage::get_sentinel_page();
+  empty_pages_head = nullptr;
+  decommitted_pages_head = nullptr;
+  num_full_pages = 0;
+  num_system_pages_per_slot_span = get_system_pages_per_slot_span();
+}
+
+NOINLINE void PartitionBucket::OnFull() {
+  OOM_CRASH();
+}
+
+ALWAYS_INLINE void* PartitionBucket::AllocNewSlotSpan(
+    PartitionRootBase* root,
+    int flags,
+    uint16_t num_partition_pages) {
+  DCHECK(!(reinterpret_cast<uintptr_t>(root->next_partition_page) %
+           kPartitionPageSize));
+  DCHECK(!(reinterpret_cast<uintptr_t>(root->next_partition_page_end) %
+           kPartitionPageSize));
+  DCHECK(num_partition_pages <= kNumPartitionPagesPerSuperPage);
+  size_t total_size = kPartitionPageSize * num_partition_pages;
+  size_t num_partition_pages_left =
+      (root->next_partition_page_end - root->next_partition_page) >>
+      kPartitionPageShift;
+  if (LIKELY(num_partition_pages_left >= num_partition_pages)) {
+    // In this case, we can still hand out pages from the current super page
+    // allocation.
+    char* ret = root->next_partition_page;
+
+    // Fresh System Pages in the SuperPages are decommited. Commit them
+    // before vending them back.
+    SetSystemPagesAccess(ret, total_size, PageReadWrite);
+
+    root->next_partition_page += total_size;
+    root->IncreaseCommittedPages(total_size);
+    return ret;
+  }
+
+  // Need a new super page. We want to allocate super pages in a continguous
+  // address region as much as possible. This is important for not causing
+  // page table bloat and not fragmenting address spaces in 32 bit
+  // architectures.
+  char* requested_address = root->next_super_page;
+  char* super_page = reinterpret_cast<char*>(
+      AllocPages(requested_address, kSuperPageSize, kSuperPageSize,
+                 PageReadWrite, PageTag::kPartitionAlloc));
+  if (UNLIKELY(!super_page))
+    return nullptr;
+
+  root->total_size_of_super_pages += kSuperPageSize;
+  root->IncreaseCommittedPages(total_size);
+
+  // |total_size| MUST be less than kSuperPageSize - (kPartitionPageSize*2).
+  // This is a trustworthy value because num_partition_pages is not user
+  // controlled.
+  //
+  // TODO(ajwong): Introduce a DCHECK.
+  root->next_super_page = super_page + kSuperPageSize;
+  char* ret = super_page + kPartitionPageSize;
+  root->next_partition_page = ret + total_size;
+  root->next_partition_page_end = root->next_super_page - kPartitionPageSize;
+  // Make the first partition page in the super page a guard page, but leave a
+  // hole in the middle.
+  // This is where we put page metadata and also a tiny amount of extent
+  // metadata.
+  SetSystemPagesAccess(super_page, kSystemPageSize, PageInaccessible);
+  SetSystemPagesAccess(super_page + (kSystemPageSize * 2),
+                       kPartitionPageSize - (kSystemPageSize * 2),
+                       PageInaccessible);
+  //  SetSystemPagesAccess(super_page + (kSuperPageSize -
+  //  kPartitionPageSize),
+  //                             kPartitionPageSize, PageInaccessible);
+  // All remaining slotspans for the unallocated PartitionPages inside the
+  // SuperPage are conceptually decommitted. Correctly set the state here
+  // so they do not occupy resources.
+  //
+  // TODO(ajwong): Refactor Page Allocator API so the SuperPage comes in
+  // decommited initially.
+  SetSystemPagesAccess(super_page + kPartitionPageSize + total_size,
+                       (kSuperPageSize - kPartitionPageSize - total_size),
+                       PageInaccessible);
+
+  // If we were after a specific address, but didn't get it, assume that
+  // the system chose a lousy address. Here most OS'es have a default
+  // algorithm that isn't randomized. For example, most Linux
+  // distributions will allocate the mapping directly before the last
+  // successful mapping, which is far from random. So we just get fresh
+  // randomness for the next mapping attempt.
+  if (requested_address && requested_address != super_page)
+    root->next_super_page = nullptr;
+
+  // We allocated a new super page so update super page metadata.
+  // First check if this is a new extent or not.
+  PartitionSuperPageExtentEntry* latest_extent =
+      reinterpret_cast<PartitionSuperPageExtentEntry*>(
+          PartitionSuperPageToMetadataArea(super_page));
+  // By storing the root in every extent metadata object, we have a fast way
+  // to go from a pointer within the partition to the root object.
+  latest_extent->root = root;
+  // Most new extents will be part of a larger extent, and these three fields
+  // are unused, but we initialize them to 0 so that we get a clear signal
+  // in case they are accidentally used.
+  latest_extent->super_page_base = nullptr;
+  latest_extent->super_pages_end = nullptr;
+  latest_extent->next = nullptr;
+
+  PartitionSuperPageExtentEntry* current_extent = root->current_extent;
+  bool is_new_extent = (super_page != requested_address);
+  if (UNLIKELY(is_new_extent)) {
+    if (UNLIKELY(!current_extent)) {
+      DCHECK(!root->first_extent);
+      root->first_extent = latest_extent;
+    } else {
+      DCHECK(current_extent->super_page_base);
+      current_extent->next = latest_extent;
+    }
+    root->current_extent = latest_extent;
+    latest_extent->super_page_base = super_page;
+    latest_extent->super_pages_end = super_page + kSuperPageSize;
+  } else {
+    // We allocated next to an existing extent so just nudge the size up a
+    // little.
+    DCHECK(current_extent->super_pages_end);
+    current_extent->super_pages_end += kSuperPageSize;
+    DCHECK(ret >= current_extent->super_page_base &&
+           ret < current_extent->super_pages_end);
+  }
+  return ret;
+}
+
+ALWAYS_INLINE uint16_t PartitionBucket::get_pages_per_slot_span() {
+  // Rounds up to nearest multiple of kNumSystemPagesPerPartitionPage.
+  return (num_system_pages_per_slot_span +
+          (kNumSystemPagesPerPartitionPage - 1)) /
+         kNumSystemPagesPerPartitionPage;
+}
+
+ALWAYS_INLINE void PartitionBucket::InitializeSlotSpan(PartitionPage* page) {
+  // The bucket never changes. We set it up once.
+  page->bucket = this;
+  page->empty_cache_index = -1;
+
+  page->Reset();
+
+  // If this page has just a single slot, do not set up page offsets for any
+  // page metadata other than the first one. This ensures that attempts to
+  // touch invalid page metadata fail.
+  if (page->num_unprovisioned_slots == 1)
+    return;
+
+  uint16_t num_partition_pages = get_pages_per_slot_span();
+  char* page_char_ptr = reinterpret_cast<char*>(page);
+  for (uint16_t i = 1; i < num_partition_pages; ++i) {
+    page_char_ptr += kPageMetadataSize;
+    PartitionPage* secondary_page =
+        reinterpret_cast<PartitionPage*>(page_char_ptr);
+    secondary_page->page_offset = i;
+  }
+}
+
+ALWAYS_INLINE char* PartitionBucket::AllocAndFillFreelist(PartitionPage* page) {
+  DCHECK(page != PartitionPage::get_sentinel_page());
+  uint16_t num_slots = page->num_unprovisioned_slots;
+  DCHECK(num_slots);
+  // We should only get here when _every_ slot is either used or unprovisioned.
+  // (The third state is "on the freelist". If we have a non-empty freelist, we
+  // should not get here.)
+  DCHECK(num_slots + page->num_allocated_slots == get_slots_per_span());
+  // Similarly, make explicitly sure that the freelist is empty.
+  DCHECK(!page->freelist_head);
+  DCHECK(page->num_allocated_slots >= 0);
+
+  size_t size = slot_size;
+  char* base = reinterpret_cast<char*>(PartitionPage::ToPointer(page));
+  char* return_object = base + (size * page->num_allocated_slots);
+  char* first_freelist_pointer = return_object + size;
+  char* first_freelist_pointer_extent =
+      first_freelist_pointer + sizeof(PartitionFreelistEntry*);
+  // Our goal is to fault as few system pages as possible. We calculate the
+  // page containing the "end" of the returned slot, and then allow freelist
+  // pointers to be written up to the end of that page.
+  char* sub_page_limit = reinterpret_cast<char*>(
+      RoundUpToSystemPage(reinterpret_cast<size_t>(first_freelist_pointer)));
+  char* slots_limit = return_object + (size * num_slots);
+  char* freelist_limit = sub_page_limit;
+  if (UNLIKELY(slots_limit < freelist_limit))
+    freelist_limit = slots_limit;
+
+  uint16_t num_new_freelist_entries = 0;
+  if (LIKELY(first_freelist_pointer_extent <= freelist_limit)) {
+    // Only consider used space in the slot span. If we consider wasted
+    // space, we may get an off-by-one when a freelist pointer fits in the
+    // wasted space, but a slot does not.
+    // We know we can fit at least one freelist pointer.
+    num_new_freelist_entries = 1;
+    // Any further entries require space for the whole slot span.
+    num_new_freelist_entries += static_cast<uint16_t>(
+        (freelist_limit - first_freelist_pointer_extent) / size);
+  }
+
+  // We always return an object slot -- that's the +1 below.
+  // We do not neccessarily create any new freelist entries, because we cross
+  // sub page boundaries frequently for large bucket sizes.
+  DCHECK(num_new_freelist_entries + 1 <= num_slots);
+  num_slots -= (num_new_freelist_entries + 1);
+  page->num_unprovisioned_slots = num_slots;
+  page->num_allocated_slots++;
+
+  if (LIKELY(num_new_freelist_entries)) {
+    char* freelist_pointer = first_freelist_pointer;
+    PartitionFreelistEntry* entry =
+        reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
+    page->freelist_head = entry;
+    while (--num_new_freelist_entries) {
+      freelist_pointer += size;
+      PartitionFreelistEntry* next_entry =
+          reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
+      entry->next = PartitionFreelistEntry::Encode(next_entry);
+      entry = next_entry;
+    }
+    entry->next = PartitionFreelistEntry::Encode(nullptr);
+  } else {
+    page->freelist_head = nullptr;
+  }
+  return return_object;
+}
+
+bool PartitionBucket::SetNewActivePage() {
+  PartitionPage* page = active_pages_head;
+  if (page == PartitionPage::get_sentinel_page())
+    return false;
+
+  PartitionPage* next_page;
+
+  for (; page; page = next_page) {
+    next_page = page->next_page;
+    DCHECK(page->bucket == this);
+    DCHECK(page != empty_pages_head);
+    DCHECK(page != decommitted_pages_head);
+
+    if (LIKELY(page->is_active())) {
+      // This page is usable because it has freelist entries, or has
+      // unprovisioned slots we can create freelist entries from.
+      active_pages_head = page;
+      return true;
+    }
+
+    // Deal with empty and decommitted pages.
+    if (LIKELY(page->is_empty())) {
+      page->next_page = empty_pages_head;
+      empty_pages_head = page;
+    } else if (LIKELY(page->is_decommitted())) {
+      page->next_page = decommitted_pages_head;
+      decommitted_pages_head = page;
+    } else {
+      DCHECK(page->is_full());
+      // If we get here, we found a full page. Skip over it too, and also
+      // tag it as full (via a negative value). We need it tagged so that
+      // free'ing can tell, and move it back into the active page list.
+      page->num_allocated_slots = -page->num_allocated_slots;
+      ++num_full_pages;
+      // num_full_pages is a uint16_t for efficient packing so guard against
+      // overflow to be safe.
+      if (UNLIKELY(!num_full_pages))
+        OnFull();
+      // Not necessary but might help stop accidents.
+      page->next_page = nullptr;
+    }
+  }
+
+  active_pages_head = PartitionPage::get_sentinel_page();
+  return false;
+}
+
+void* PartitionBucket::SlowPathAlloc(PartitionRootBase* root,
+                                     int flags,
+                                     size_t size,
+                                     bool* is_already_zeroed) {
+  // The slow path is called when the freelist is empty.
+  DCHECK(!active_pages_head->freelist_head);
+
+  PartitionPage* new_page = nullptr;
+  *is_already_zeroed = false;
+
+  // For the PartitionRootGeneric::Alloc() API, we have a bunch of buckets
+  // marked as special cases. We bounce them through to the slow path so that
+  // we can still have a blazing fast hot path due to lack of corner-case
+  // branches.
+  //
+  // Note: The ordering of the conditionals matter! In particular,
+  // SetNewActivePage() has a side-effect even when returning
+  // false where it sweeps the active page list and may move things into
+  // the empty or decommitted lists which affects the subsequent conditional.
+  bool return_null = flags & PartitionAllocReturnNull;
+  if (UNLIKELY(is_direct_mapped())) {
+    DCHECK(size > kGenericMaxBucketed);
+    DCHECK(this == get_sentinel_bucket());
+    DCHECK(active_pages_head == PartitionPage::get_sentinel_page());
+    if (size > kGenericMaxDirectMapped) {
+      if (return_null)
+        return nullptr;
+      PartitionExcessiveAllocationSize();
+    }
+    new_page = PartitionDirectMap(root, flags, size);
+#if !defined(OS_MACOSX)
+    // Turn off the optimization to see if it helps https://crbug.com/892550.
+    *is_already_zeroed = true;
+#endif
+  } else if (LIKELY(SetNewActivePage())) {
+    // First, did we find an active page in the active pages list?
+    new_page = active_pages_head;
+    DCHECK(new_page->is_active());
+  } else if (LIKELY(empty_pages_head != nullptr) ||
+             LIKELY(decommitted_pages_head != nullptr)) {
+    // Second, look in our lists of empty and decommitted pages.
+    // Check empty pages first, which are preferred, but beware that an
+    // empty page might have been decommitted.
+    while (LIKELY((new_page = empty_pages_head) != nullptr)) {
+      DCHECK(new_page->bucket == this);
+      DCHECK(new_page->is_empty() || new_page->is_decommitted());
+      empty_pages_head = new_page->next_page;
+      // Accept the empty page unless it got decommitted.
+      if (new_page->freelist_head) {
+        new_page->next_page = nullptr;
+        break;
+      }
+      DCHECK(new_page->is_decommitted());
+      new_page->next_page = decommitted_pages_head;
+      decommitted_pages_head = new_page;
+    }
+    if (UNLIKELY(!new_page) && LIKELY(decommitted_pages_head != nullptr)) {
+      new_page = decommitted_pages_head;
+      DCHECK(new_page->bucket == this);
+      DCHECK(new_page->is_decommitted());
+      decommitted_pages_head = new_page->next_page;
+      void* addr = PartitionPage::ToPointer(new_page);
+      root->RecommitSystemPages(addr, new_page->bucket->get_bytes_per_span());
+      new_page->Reset();
+      // TODO(https://crbug.com/890752): Optimizing here might cause pages to
+      // not be zeroed.
+      // *is_already_zeroed = true;
+    }
+    DCHECK(new_page);
+  } else {
+    // Third. If we get here, we need a brand new page.
+    uint16_t num_partition_pages = get_pages_per_slot_span();
+    void* raw_pages = AllocNewSlotSpan(root, flags, num_partition_pages);
+    if (LIKELY(raw_pages != nullptr)) {
+      new_page = PartitionPage::FromPointerNoAlignmentCheck(raw_pages);
+      InitializeSlotSpan(new_page);
+      // TODO(https://crbug.com/890752): Optimizing here causes pages to not be
+      // zeroed on at least macOS.
+      // *is_already_zeroed = true;
+    }
+  }
+
+  // Bail if we had a memory allocation failure.
+  if (UNLIKELY(!new_page)) {
+    DCHECK(active_pages_head == PartitionPage::get_sentinel_page());
+    if (return_null)
+      return nullptr;
+    root->OutOfMemory();
+  }
+
+  // TODO(ajwong): Is there a way to avoid the reading of bucket here?
+  // It seems like in many of the conditional branches above, |this| ==
+  // |new_page->bucket|. Maybe pull this into another function?
+  PartitionBucket* bucket = new_page->bucket;
+  DCHECK(bucket != get_sentinel_bucket());
+  bucket->active_pages_head = new_page;
+  new_page->set_raw_size(size);
+
+  // If we found an active page with free slots, or an empty page, we have a
+  // usable freelist head.
+  if (LIKELY(new_page->freelist_head != nullptr)) {
+    PartitionFreelistEntry* entry = new_page->freelist_head;
+    PartitionFreelistEntry* new_head =
+        EncodedPartitionFreelistEntry::Decode(entry->next);
+    new_page->freelist_head = new_head;
+    new_page->num_allocated_slots++;
+    return entry;
+  }
+  // Otherwise, we need to build the freelist.
+  DCHECK(new_page->num_unprovisioned_slots);
+  return AllocAndFillFreelist(new_page);
+}
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_bucket.h b/third_party/base/allocator/partition_allocator/partition_bucket.h
new file mode 100644
index 0000000..a89099b
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_bucket.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
+#include "third_party/base/base_export.h"
+#include "third_party/base/compiler_specific.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+struct PartitionPage;
+struct PartitionRootBase;
+
+struct PartitionBucket {
+  // Accessed most in hot path => goes first.
+  PartitionPage* active_pages_head;
+
+  PartitionPage* empty_pages_head;
+  PartitionPage* decommitted_pages_head;
+  uint32_t slot_size;
+  uint32_t num_system_pages_per_slot_span : 8;
+  uint32_t num_full_pages : 24;
+
+  // Public API.
+  void Init(uint32_t new_slot_size);
+
+  // Sets |is_already_zeroed| to true if the allocation was satisfied by
+  // requesting (a) new page(s) from the operating system, or false otherwise.
+  // This enables an optimization for when callers use |PartitionAllocZeroFill|:
+  // there is no need to call memset on fresh pages; the OS has already zeroed
+  // them. (See |PartitionRootBase::AllocFromBucket|.)
+  //
+  // Note the matching Free() functions are in PartitionPage.
+  BASE_EXPORT NOINLINE void* SlowPathAlloc(PartitionRootBase* root,
+                                           int flags,
+                                           size_t size,
+                                           bool* is_already_zeroed);
+
+  ALWAYS_INLINE bool is_direct_mapped() const {
+    return !num_system_pages_per_slot_span;
+  }
+  ALWAYS_INLINE size_t get_bytes_per_span() const {
+    // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153
+    // https://crbug.com/680657
+    return num_system_pages_per_slot_span * kSystemPageSize;
+  }
+  ALWAYS_INLINE uint16_t get_slots_per_span() const {
+    // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153
+    // https://crbug.com/680657
+    return static_cast<uint16_t>(get_bytes_per_span() / slot_size);
+  }
+
+  static ALWAYS_INLINE size_t get_direct_map_size(size_t size) {
+    // Caller must check that the size is not above the kGenericMaxDirectMapped
+    // limit before calling. This also guards against integer overflow in the
+    // calculation here.
+    DCHECK(size <= kGenericMaxDirectMapped);
+    return (size + kSystemPageOffsetMask) & kSystemPageBaseMask;
+  }
+
+  // TODO(ajwong): Can this be made private?  https://crbug.com/787153
+  static PartitionBucket* get_sentinel_bucket();
+
+  // This helper function scans a bucket's active page list for a suitable new
+  // active page.  When it finds a suitable new active page (one that has
+  // free slots and is not empty), it is set as the new active page. If there
+  // is no suitable new active page, the current active page is set to
+  // PartitionPage::get_sentinel_page(). As potential pages are scanned, they
+  // are tidied up according to their state. Empty pages are swept on to the
+  // empty page list, decommitted pages on to the decommitted page list and full
+  // pages are unlinked from any list.
+  //
+  // This is where the guts of the bucket maintenance is done!
+  bool SetNewActivePage();
+
+ private:
+  static void OutOfMemory(const PartitionRootBase* root);
+  static void OutOfMemoryWithLotsOfUncommitedPages();
+
+  static NOINLINE void OnFull();
+
+  // Returns a natural number of PartitionPages (calculated by
+  // get_system_pages_per_slot_span()) to allocate from the current
+  // SuperPage when the bucket runs out of slots.
+  ALWAYS_INLINE uint16_t get_pages_per_slot_span();
+
+  // Returns the number of system pages in a slot span.
+  //
+  // The calculation attemps to find the best number of System Pages to
+  // allocate for the given slot_size to minimize wasted space. It uses a
+  // heuristic that looks at number of bytes wasted after the last slot and
+  // attempts to account for the PTE usage of each System Page.
+  uint8_t get_system_pages_per_slot_span();
+
+  // Allocates a new slot span with size |num_partition_pages| from the
+  // current extent. Metadata within this slot span will be uninitialized.
+  // Returns nullptr on error.
+  ALWAYS_INLINE void* AllocNewSlotSpan(PartitionRootBase* root,
+                                       int flags,
+                                       uint16_t num_partition_pages);
+
+  // Each bucket allocates a slot span when it runs out of slots.
+  // A slot span's size is equal to get_pages_per_slot_span() number of
+  // PartitionPages. This function initializes all PartitionPage within the
+  // span to point to the first PartitionPage which holds all the metadata
+  // for the span and registers this bucket as the owner of the span. It does
+  // NOT put the slots into the bucket's freelist.
+  ALWAYS_INLINE void InitializeSlotSpan(PartitionPage* page);
+
+  // Allocates one slot from the given |page| and then adds the remainder to
+  // the current bucket. If the |page| was freshly allocated, it must have been
+  // passed through InitializeSlotSpan() first.
+  ALWAYS_INLINE char* AllocAndFillFreelist(PartitionPage* page);
+
+  static PartitionBucket sentinel_bucket_;
+};
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_cookie.h b/third_party/base/allocator/partition_allocator/partition_cookie.h
new file mode 100644
index 0000000..7cf4e84
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_cookie.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
+
+#include "third_party/base/compiler_specific.h"
+#include "third_party/base/logging.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+#if DCHECK_IS_ON()
+// Handles alignment up to XMM instructions on Intel.
+static constexpr size_t kCookieSize = 16;
+
+static constexpr unsigned char kCookieValue[kCookieSize] = {
+    0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xD0, 0x0D,
+    0x13, 0x37, 0xF0, 0x05, 0xBA, 0x11, 0xAB, 0x1E};
+#endif
+
+ALWAYS_INLINE void PartitionCookieCheckValue(void* ptr) {
+#if DCHECK_IS_ON()
+  unsigned char* cookie_ptr = reinterpret_cast<unsigned char*>(ptr);
+  for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr)
+    DCHECK(*cookie_ptr == kCookieValue[i]);
+#endif
+}
+
+ALWAYS_INLINE size_t PartitionCookieSizeAdjustAdd(size_t size) {
+#if DCHECK_IS_ON()
+  // Add space for cookies, checking for integer overflow. TODO(palmer):
+  // Investigate the performance and code size implications of using
+  // CheckedNumeric throughout PA.
+  DCHECK(size + (2 * kCookieSize) > size);
+  size += 2 * kCookieSize;
+#endif
+  return size;
+}
+
+ALWAYS_INLINE void* PartitionCookieFreePointerAdjust(void* ptr) {
+#if DCHECK_IS_ON()
+  // The value given to the application is actually just after the cookie.
+  ptr = static_cast<char*>(ptr) - kCookieSize;
+#endif
+  return ptr;
+}
+
+ALWAYS_INLINE size_t PartitionCookieSizeAdjustSubtract(size_t size) {
+#if DCHECK_IS_ON()
+  // Remove space for cookies.
+  DCHECK(size >= 2 * kCookieSize);
+  size -= 2 * kCookieSize;
+#endif
+  return size;
+}
+
+ALWAYS_INLINE void PartitionCookieWriteValue(void* ptr) {
+#if DCHECK_IS_ON()
+  unsigned char* cookie_ptr = reinterpret_cast<unsigned char*>(ptr);
+  for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr)
+    *cookie_ptr = kCookieValue[i];
+#endif
+}
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_direct_map_extent.h b/third_party/base/allocator/partition_allocator/partition_direct_map_extent.h
new file mode 100644
index 0000000..192c5b4
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_direct_map_extent.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
+
+#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
+#include "third_party/base/allocator/partition_allocator/partition_page.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+struct PartitionDirectMapExtent {
+  PartitionDirectMapExtent* next_extent;
+  PartitionDirectMapExtent* prev_extent;
+  PartitionBucket* bucket;
+  size_t map_size;  // Mapped size, not including guard pages and meta-data.
+
+  ALWAYS_INLINE static PartitionDirectMapExtent* FromPage(PartitionPage* page);
+};
+
+ALWAYS_INLINE PartitionDirectMapExtent* PartitionDirectMapExtent::FromPage(
+    PartitionPage* page) {
+  DCHECK(page->bucket->is_direct_mapped());
+  return reinterpret_cast<PartitionDirectMapExtent*>(
+      reinterpret_cast<char*>(page) + 3 * kPageMetadataSize);
+}
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_freelist_entry.h b/third_party/base/allocator/partition_allocator/partition_freelist_entry.h
new file mode 100644
index 0000000..5d46f0f
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_freelist_entry.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
+#include "third_party/base/compiler_specific.h"
+#include "third_party/base/sys_byteorder.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+struct EncodedPartitionFreelistEntry;
+
+struct PartitionFreelistEntry {
+  EncodedPartitionFreelistEntry* next;
+
+  PartitionFreelistEntry() = delete;
+  ~PartitionFreelistEntry() = delete;
+
+  ALWAYS_INLINE static EncodedPartitionFreelistEntry* Encode(
+      PartitionFreelistEntry* ptr) {
+    return reinterpret_cast<EncodedPartitionFreelistEntry*>(Transform(ptr));
+  }
+
+ private:
+  friend struct EncodedPartitionFreelistEntry;
+  static ALWAYS_INLINE void* Transform(void* ptr) {
+    // We use bswap on little endian as a fast mask for two reasons:
+    // 1) If an object is freed and its vtable used where the attacker doesn't
+    // get the chance to run allocations between the free and use, the vtable
+    // dereference is likely to fault.
+    // 2) If the attacker has a linear buffer overflow and elects to try and
+    // corrupt a freelist pointer, partial pointer overwrite attacks are
+    // thwarted.
+    // For big endian, similar guarantees are arrived at with a negation.
+#if defined(ARCH_CPU_BIG_ENDIAN)
+    uintptr_t masked = ~reinterpret_cast<uintptr_t>(ptr);
+#else
+    uintptr_t masked = ByteSwapUintPtrT(reinterpret_cast<uintptr_t>(ptr));
+#endif
+    return reinterpret_cast<void*>(masked);
+  }
+};
+
+struct EncodedPartitionFreelistEntry {
+  char scrambled[sizeof(PartitionFreelistEntry*)];
+
+  EncodedPartitionFreelistEntry() = delete;
+  ~EncodedPartitionFreelistEntry() = delete;
+
+  ALWAYS_INLINE static PartitionFreelistEntry* Decode(
+      EncodedPartitionFreelistEntry* ptr) {
+    return reinterpret_cast<PartitionFreelistEntry*>(
+        PartitionFreelistEntry::Transform(ptr));
+  }
+};
+
+static_assert(sizeof(PartitionFreelistEntry) ==
+                  sizeof(EncodedPartitionFreelistEntry),
+              "Should not have padding");
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_oom.cc b/third_party/base/allocator/partition_allocator/partition_oom.cc
new file mode 100644
index 0000000..a4052d1
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_oom.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/allocator/partition_allocator/partition_oom.h"
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/oom.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+void NOINLINE PartitionExcessiveAllocationSize() {
+  OOM_CRASH();
+}
+
+#if !defined(ARCH_CPU_64_BITS)
+NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages() {
+  OOM_CRASH();
+}
+#endif
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_oom.h b/third_party/base/allocator/partition_allocator/partition_oom.h
new file mode 100644
index 0000000..be43ff3
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_oom.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Holds functions for generating OOM errors from PartitionAlloc. This is
+// distinct from oom.h in that it is meant only for use in PartitionAlloc.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
+
+#include "build/build_config.h"
+#include "third_party/base/compiler_specific.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+NOINLINE void PartitionExcessiveAllocationSize();
+
+#if !defined(ARCH_CPU_64_BITS)
+NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages();
+#endif
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_page.cc b/third_party/base/allocator/partition_allocator/partition_page.cc
new file mode 100644
index 0000000..8b507c3
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_page.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/allocator/partition_allocator/partition_page.h"
+
+#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
+#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+namespace {
+
+ALWAYS_INLINE void PartitionDirectUnmap(PartitionPage* page) {
+  PartitionRootBase* root = PartitionRootBase::FromPage(page);
+  const PartitionDirectMapExtent* extent =
+      PartitionDirectMapExtent::FromPage(page);
+  size_t unmap_size = extent->map_size;
+
+  // Maintain the doubly-linked list of all direct mappings.
+  if (extent->prev_extent) {
+    DCHECK(extent->prev_extent->next_extent == extent);
+    extent->prev_extent->next_extent = extent->next_extent;
+  } else {
+    root->direct_map_list = extent->next_extent;
+  }
+  if (extent->next_extent) {
+    DCHECK(extent->next_extent->prev_extent == extent);
+    extent->next_extent->prev_extent = extent->prev_extent;
+  }
+
+  // Add on the size of the trailing guard page and preceeding partition
+  // page.
+  unmap_size += kPartitionPageSize + kSystemPageSize;
+
+  size_t uncommitted_page_size = page->bucket->slot_size + kSystemPageSize;
+  root->DecreaseCommittedPages(uncommitted_page_size);
+  DCHECK(root->total_size_of_direct_mapped_pages >= uncommitted_page_size);
+  root->total_size_of_direct_mapped_pages -= uncommitted_page_size;
+
+  DCHECK(!(unmap_size & kPageAllocationGranularityOffsetMask));
+
+  char* ptr = reinterpret_cast<char*>(PartitionPage::ToPointer(page));
+  // Account for the mapping starting a partition page before the actual
+  // allocation address.
+  ptr -= kPartitionPageSize;
+
+  FreePages(ptr, unmap_size);
+}
+
+ALWAYS_INLINE void PartitionRegisterEmptyPage(PartitionPage* page) {
+  DCHECK(page->is_empty());
+  PartitionRootBase* root = PartitionRootBase::FromPage(page);
+
+  // If the page is already registered as empty, give it another life.
+  if (page->empty_cache_index != -1) {
+    DCHECK(page->empty_cache_index >= 0);
+    DCHECK(static_cast<unsigned>(page->empty_cache_index) < kMaxFreeableSpans);
+    DCHECK(root->global_empty_page_ring[page->empty_cache_index] == page);
+    root->global_empty_page_ring[page->empty_cache_index] = nullptr;
+  }
+
+  int16_t current_index = root->global_empty_page_ring_index;
+  PartitionPage* page_to_decommit = root->global_empty_page_ring[current_index];
+  // The page might well have been re-activated, filled up, etc. before we get
+  // around to looking at it here.
+  if (page_to_decommit)
+    page_to_decommit->DecommitIfPossible(root);
+
+  // We put the empty slot span on our global list of "pages that were once
+  // empty". thus providing it a bit of breathing room to get re-used before
+  // we really free it. This improves performance, particularly on Mac OS X
+  // which has subpar memory management performance.
+  root->global_empty_page_ring[current_index] = page;
+  page->empty_cache_index = current_index;
+  ++current_index;
+  if (current_index == kMaxFreeableSpans)
+    current_index = 0;
+  root->global_empty_page_ring_index = current_index;
+}
+
+}  // namespace
+
+// static
+PartitionPage PartitionPage::sentinel_page_;
+
+PartitionPage* PartitionPage::get_sentinel_page() {
+  return &sentinel_page_;
+}
+
+void PartitionPage::FreeSlowPath() {
+  DCHECK(this != get_sentinel_page());
+  if (LIKELY(num_allocated_slots == 0)) {
+    // Page became fully unused.
+    if (UNLIKELY(bucket->is_direct_mapped())) {
+      PartitionDirectUnmap(this);
+      return;
+    }
+    // If it's the current active page, change it. We bounce the page to
+    // the empty list as a force towards defragmentation.
+    if (LIKELY(this == bucket->active_pages_head))
+      bucket->SetNewActivePage();
+    DCHECK(bucket->active_pages_head != this);
+
+    set_raw_size(0);
+    DCHECK(!get_raw_size());
+
+    PartitionRegisterEmptyPage(this);
+  } else {
+    DCHECK(!bucket->is_direct_mapped());
+    // Ensure that the page is full. That's the only valid case if we
+    // arrive here.
+    DCHECK(num_allocated_slots < 0);
+    // A transition of num_allocated_slots from 0 to -1 is not legal, and
+    // likely indicates a double-free.
+    CHECK(num_allocated_slots != -1);
+    num_allocated_slots = -num_allocated_slots - 2;
+    DCHECK(num_allocated_slots == bucket->get_slots_per_span() - 1);
+    // Fully used page became partially used. It must be put back on the
+    // non-full page list. Also make it the current page to increase the
+    // chances of it being filled up again. The old current page will be
+    // the next page.
+    DCHECK(!next_page);
+    if (LIKELY(bucket->active_pages_head != get_sentinel_page()))
+      next_page = bucket->active_pages_head;
+    bucket->active_pages_head = this;
+    --bucket->num_full_pages;
+    // Special case: for a partition page with just a single slot, it may
+    // now be empty and we want to run it through the empty logic.
+    if (UNLIKELY(num_allocated_slots == 0))
+      FreeSlowPath();
+  }
+}
+
+void PartitionPage::Decommit(PartitionRootBase* root) {
+  DCHECK(is_empty());
+  DCHECK(!bucket->is_direct_mapped());
+  void* addr = PartitionPage::ToPointer(this);
+  root->DecommitSystemPages(addr, bucket->get_bytes_per_span());
+
+  // We actually leave the decommitted page in the active list. We'll sweep
+  // it on to the decommitted page list when we next walk the active page
+  // list.
+  // Pulling this trick enables us to use a singly-linked page list for all
+  // cases, which is critical in keeping the page metadata structure down to
+  // 32 bytes in size.
+  freelist_head = nullptr;
+  num_unprovisioned_slots = 0;
+  DCHECK(is_decommitted());
+}
+
+void PartitionPage::DecommitIfPossible(PartitionRootBase* root) {
+  DCHECK(empty_cache_index >= 0);
+  DCHECK(static_cast<unsigned>(empty_cache_index) < kMaxFreeableSpans);
+  DCHECK(this == root->global_empty_page_ring[empty_cache_index]);
+  empty_cache_index = -1;
+  if (is_empty())
+    Decommit(root);
+}
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_page.h b/third_party/base/allocator/partition_allocator/partition_page.h
new file mode 100644
index 0000000..4bbb76b
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_page.h
@@ -0,0 +1,294 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
+
+#include <string.h>
+
+#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
+#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
+#include "third_party/base/allocator/partition_allocator/partition_cookie.h"
+#include "third_party/base/allocator/partition_allocator/partition_freelist_entry.h"
+#include "third_party/base/allocator/partition_allocator/random.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+struct PartitionRootBase;
+
+// Some notes on page states. A page can be in one of four major states:
+// 1) Active.
+// 2) Full.
+// 3) Empty.
+// 4) Decommitted.
+// An active page has available free slots. A full page has no free slots. An
+// empty page has no free slots, and a decommitted page is an empty page that
+// had its backing memory released back to the system.
+// There are two linked lists tracking the pages. The "active page" list is an
+// approximation of a list of active pages. It is an approximation because
+// full, empty and decommitted pages may briefly be present in the list until
+// we next do a scan over it.
+// The "empty page" list is an accurate list of pages which are either empty
+// or decommitted.
+//
+// The significant page transitions are:
+// - free() will detect when a full page has a slot free()'d and immediately
+// return the page to the head of the active list.
+// - free() will detect when a page is fully emptied. It _may_ add it to the
+// empty list or it _may_ leave it on the active list until a future list scan.
+// - malloc() _may_ scan the active page list in order to fulfil the request.
+// If it does this, full, empty and decommitted pages encountered will be
+// booted out of the active list. If there are no suitable active pages found,
+// an empty or decommitted page (if one exists) will be pulled from the empty
+// list on to the active list.
+//
+// TODO(ajwong): Evaluate if this should be named PartitionSlotSpanMetadata or
+// similar. If so, all uses of the term "page" in comments, member variables,
+// local variables, and documentation that refer to this concept should be
+// updated.
+struct PartitionPage {
+  PartitionFreelistEntry* freelist_head;
+  PartitionPage* next_page;
+  PartitionBucket* bucket;
+  // Deliberately signed, 0 for empty or decommitted page, -n for full pages:
+  int16_t num_allocated_slots;
+  uint16_t num_unprovisioned_slots;
+  uint16_t page_offset;
+  int16_t empty_cache_index;  // -1 if not in the empty cache.
+
+  // Public API
+
+  // Note the matching Alloc() functions are in PartitionPage.
+  BASE_EXPORT NOINLINE void FreeSlowPath();
+  ALWAYS_INLINE void Free(void* ptr);
+
+  void Decommit(PartitionRootBase* root);
+  void DecommitIfPossible(PartitionRootBase* root);
+
+  // Pointer manipulation functions. These must be static as the input |page|
+  // pointer may be the result of an offset calculation and therefore cannot
+  // be trusted. The objective of these functions is to sanitize this input.
+  ALWAYS_INLINE static void* ToPointer(const PartitionPage* page);
+  ALWAYS_INLINE static PartitionPage* FromPointerNoAlignmentCheck(void* ptr);
+  ALWAYS_INLINE static PartitionPage* FromPointer(void* ptr);
+
+  ALWAYS_INLINE const size_t* get_raw_size_ptr() const;
+  ALWAYS_INLINE size_t* get_raw_size_ptr() {
+    return const_cast<size_t*>(
+        const_cast<const PartitionPage*>(this)->get_raw_size_ptr());
+  }
+
+  ALWAYS_INLINE size_t get_raw_size() const;
+  ALWAYS_INLINE void set_raw_size(size_t size);
+
+  ALWAYS_INLINE void Reset();
+
+  // TODO(ajwong): Can this be made private?  https://crbug.com/787153
+  BASE_EXPORT static PartitionPage* get_sentinel_page();
+
+  // Page State accessors.
+  // Note that it's only valid to call these functions on pages found on one of
+  // the page lists. Specifically, you can't call these functions on full pages
+  // that were detached from the active list.
+  //
+  // This restriction provides the flexibity for some of the status fields to
+  // be repurposed when a page is taken off a list. See the negation of
+  // |num_allocated_slots| when a full page is removed from the active list
+  // for an example of such repurposing.
+  ALWAYS_INLINE bool is_active() const;
+  ALWAYS_INLINE bool is_full() const;
+  ALWAYS_INLINE bool is_empty() const;
+  ALWAYS_INLINE bool is_decommitted() const;
+
+ private:
+  // g_sentinel_page is used as a sentinel to indicate that there is no page
+  // in the active page list. We can use nullptr, but in that case we need
+  // to add a null-check branch to the hot allocation path. We want to avoid
+  // that.
+  //
+  // Note, this declaration is kept in the header as opposed to an anonymous
+  // namespace so the getter can be fully inlined.
+  static PartitionPage sentinel_page_;
+};
+static_assert(sizeof(PartitionPage) <= kPageMetadataSize,
+              "PartitionPage must be able to fit in a metadata slot");
+
+ALWAYS_INLINE char* PartitionSuperPageToMetadataArea(char* ptr) {
+  uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(ptr);
+  DCHECK(!(pointer_as_uint & kSuperPageOffsetMask));
+  // The metadata area is exactly one system page (the guard page) into the
+  // super page.
+  return reinterpret_cast<char*>(pointer_as_uint + kSystemPageSize);
+}
+
+ALWAYS_INLINE PartitionPage* PartitionPage::FromPointerNoAlignmentCheck(
+    void* ptr) {
+  uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(ptr);
+  char* super_page_ptr =
+      reinterpret_cast<char*>(pointer_as_uint & kSuperPageBaseMask);
+  uintptr_t partition_page_index =
+      (pointer_as_uint & kSuperPageOffsetMask) >> kPartitionPageShift;
+  // Index 0 is invalid because it is the metadata and guard area and
+  // the last index is invalid because it is a guard page.
+  DCHECK(partition_page_index);
+  DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1);
+  PartitionPage* page = reinterpret_cast<PartitionPage*>(
+      PartitionSuperPageToMetadataArea(super_page_ptr) +
+      (partition_page_index << kPageMetadataShift));
+  // Partition pages in the same slot span can share the same page object.
+  // Adjust for that.
+  size_t delta = page->page_offset << kPageMetadataShift;
+  page =
+      reinterpret_cast<PartitionPage*>(reinterpret_cast<char*>(page) - delta);
+  return page;
+}
+
+// Resturns start of the slot span for the PartitionPage.
+ALWAYS_INLINE void* PartitionPage::ToPointer(const PartitionPage* page) {
+  uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(page);
+
+  uintptr_t super_page_offset = (pointer_as_uint & kSuperPageOffsetMask);
+
+  // A valid |page| must be past the first guard System page and within
+  // the following metadata region.
+  DCHECK(super_page_offset > kSystemPageSize);
+  // Must be less than total metadata region.
+  DCHECK(super_page_offset < kSystemPageSize + (kNumPartitionPagesPerSuperPage *
+                                                kPageMetadataSize));
+  uintptr_t partition_page_index =
+      (super_page_offset - kSystemPageSize) >> kPageMetadataShift;
+  // Index 0 is invalid because it is the superpage extent metadata and the
+  // last index is invalid because the whole PartitionPage is set as guard
+  // pages for the metadata region.
+  DCHECK(partition_page_index);
+  DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1);
+  uintptr_t super_page_base = (pointer_as_uint & kSuperPageBaseMask);
+  void* ret = reinterpret_cast<void*>(
+      super_page_base + (partition_page_index << kPartitionPageShift));
+  return ret;
+}
+
+ALWAYS_INLINE PartitionPage* PartitionPage::FromPointer(void* ptr) {
+  PartitionPage* page = PartitionPage::FromPointerNoAlignmentCheck(ptr);
+  // Checks that the pointer is a multiple of bucket size.
+  DCHECK(!((reinterpret_cast<uintptr_t>(ptr) -
+            reinterpret_cast<uintptr_t>(PartitionPage::ToPointer(page))) %
+           page->bucket->slot_size));
+  return page;
+}
+
+ALWAYS_INLINE const size_t* PartitionPage::get_raw_size_ptr() const {
+  // For single-slot buckets which span more than one partition page, we
+  // have some spare metadata space to store the raw allocation size. We
+  // can use this to report better statistics.
+  if (bucket->slot_size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize)
+    return nullptr;
+
+  DCHECK((bucket->slot_size % kSystemPageSize) == 0);
+  DCHECK(bucket->is_direct_mapped() || bucket->get_slots_per_span() == 1);
+
+  const PartitionPage* the_next_page = this + 1;
+  return reinterpret_cast<const size_t*>(&the_next_page->freelist_head);
+}
+
+ALWAYS_INLINE size_t PartitionPage::get_raw_size() const {
+  const size_t* ptr = get_raw_size_ptr();
+  if (UNLIKELY(ptr != nullptr))
+    return *ptr;
+  return 0;
+}
+
+ALWAYS_INLINE void PartitionPage::Free(void* ptr) {
+#if DCHECK_IS_ON()
+  size_t slot_size = bucket->slot_size;
+  const size_t raw_size = get_raw_size();
+  if (raw_size) {
+    slot_size = raw_size;
+  }
+
+  // If these asserts fire, you probably corrupted memory.
+  PartitionCookieCheckValue(ptr);
+  PartitionCookieCheckValue(reinterpret_cast<char*>(ptr) + slot_size -
+                            kCookieSize);
+
+  memset(ptr, kFreedByte, slot_size);
+#endif
+
+  DCHECK(num_allocated_slots);
+  // Catches an immediate double free.
+  CHECK(ptr != freelist_head);
+  // Look for double free one level deeper in debug.
+  DCHECK(!freelist_head ||
+         ptr != EncodedPartitionFreelistEntry::Decode(freelist_head->next));
+  internal::PartitionFreelistEntry* entry =
+      static_cast<internal::PartitionFreelistEntry*>(ptr);
+  entry->next = internal::PartitionFreelistEntry::Encode(freelist_head);
+  freelist_head = entry;
+  --num_allocated_slots;
+  if (UNLIKELY(num_allocated_slots <= 0)) {
+    FreeSlowPath();
+  } else {
+    // All single-slot allocations must go through the slow path to
+    // correctly update the size metadata.
+    DCHECK(get_raw_size() == 0);
+  }
+}
+
+ALWAYS_INLINE bool PartitionPage::is_active() const {
+  DCHECK(this != get_sentinel_page());
+  DCHECK(!page_offset);
+  return (num_allocated_slots > 0 &&
+          (freelist_head || num_unprovisioned_slots));
+}
+
+ALWAYS_INLINE bool PartitionPage::is_full() const {
+  DCHECK(this != get_sentinel_page());
+  DCHECK(!page_offset);
+  bool ret = (num_allocated_slots == bucket->get_slots_per_span());
+  if (ret) {
+    DCHECK(!freelist_head);
+    DCHECK(!num_unprovisioned_slots);
+  }
+  return ret;
+}
+
+ALWAYS_INLINE bool PartitionPage::is_empty() const {
+  DCHECK(this != get_sentinel_page());
+  DCHECK(!page_offset);
+  return (!num_allocated_slots && freelist_head);
+}
+
+ALWAYS_INLINE bool PartitionPage::is_decommitted() const {
+  DCHECK(this != get_sentinel_page());
+  DCHECK(!page_offset);
+  bool ret = (!num_allocated_slots && !freelist_head);
+  if (ret) {
+    DCHECK(!num_unprovisioned_slots);
+    DCHECK(empty_cache_index == -1);
+  }
+  return ret;
+}
+
+ALWAYS_INLINE void PartitionPage::set_raw_size(size_t size) {
+  size_t* raw_size_ptr = get_raw_size_ptr();
+  if (UNLIKELY(raw_size_ptr != nullptr))
+    *raw_size_ptr = size;
+}
+
+ALWAYS_INLINE void PartitionPage::Reset() {
+  DCHECK(is_decommitted());
+
+  num_unprovisioned_slots = bucket->get_slots_per_span();
+  DCHECK(num_unprovisioned_slots);
+
+  next_page = nullptr;
+}
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
diff --git a/third_party/base/allocator/partition_allocator/partition_root_base.cc b/third_party/base/allocator/partition_allocator/partition_root_base.cc
new file mode 100644
index 0000000..3136584
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_root_base.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/allocator/partition_allocator/partition_root_base.h"
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/oom.h"
+#include "third_party/base/allocator/partition_allocator/partition_oom.h"
+#include "third_party/base/allocator/partition_allocator/partition_page.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+NOINLINE void PartitionRootBase::OutOfMemory() {
+#if !defined(ARCH_CPU_64_BITS)
+  // Check whether this OOM is due to a lot of super pages that are allocated
+  // but not committed, probably due to http://crbug.com/421387.
+  if (total_size_of_super_pages + total_size_of_direct_mapped_pages -
+          total_size_of_committed_pages >
+      kReasonableSizeOfUnusedPages) {
+    PartitionOutOfMemoryWithLotsOfUncommitedPages();
+  }
+#endif
+  if (PartitionRootBase::gOomHandlingFunction)
+    (*PartitionRootBase::gOomHandlingFunction)();
+  OOM_CRASH();
+}
+
+void PartitionRootBase::DecommitEmptyPages() {
+  for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
+    internal::PartitionPage* page = global_empty_page_ring[i];
+    if (page)
+      page->DecommitIfPossible(this);
+    global_empty_page_ring[i] = nullptr;
+  }
+}
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/partition_root_base.h b/third_party/base/allocator/partition_allocator/partition_root_base.h
new file mode 100644
index 0000000..2f4b70e
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/partition_root_base.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/page_allocator.h"
+#include "third_party/base/allocator/partition_allocator/partition_alloc_constants.h"
+#include "third_party/base/allocator/partition_allocator/partition_bucket.h"
+#include "third_party/base/allocator/partition_allocator/partition_direct_map_extent.h"
+#include "third_party/base/allocator/partition_allocator/partition_page.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+struct PartitionPage;
+struct PartitionRootBase;
+
+// An "extent" is a span of consecutive superpages. We link to the partition's
+// next extent (if there is one) to the very start of a superpage's metadata
+// area.
+struct PartitionSuperPageExtentEntry {
+  PartitionRootBase* root;
+  char* super_page_base;
+  char* super_pages_end;
+  PartitionSuperPageExtentEntry* next;
+};
+static_assert(
+    sizeof(PartitionSuperPageExtentEntry) <= kPageMetadataSize,
+    "PartitionSuperPageExtentEntry must be able to fit in a metadata slot");
+
+struct BASE_EXPORT PartitionRootBase {
+  PartitionRootBase();
+  virtual ~PartitionRootBase();
+  size_t total_size_of_committed_pages = 0;
+  size_t total_size_of_super_pages = 0;
+  size_t total_size_of_direct_mapped_pages = 0;
+  // Invariant: total_size_of_committed_pages <=
+  //                total_size_of_super_pages +
+  //                total_size_of_direct_mapped_pages.
+  unsigned num_buckets = 0;
+  unsigned max_allocation = 0;
+  bool initialized = false;
+  char* next_super_page = nullptr;
+  char* next_partition_page = nullptr;
+  char* next_partition_page_end = nullptr;
+  PartitionSuperPageExtentEntry* current_extent = nullptr;
+  PartitionSuperPageExtentEntry* first_extent = nullptr;
+  PartitionDirectMapExtent* direct_map_list = nullptr;
+  PartitionPage* global_empty_page_ring[kMaxFreeableSpans] = {};
+  int16_t global_empty_page_ring_index = 0;
+  uintptr_t inverted_self = 0;
+
+  // Public API
+
+  // Allocates out of the given bucket. Properly, this function should probably
+  // be in PartitionBucket, but because the implementation needs to be inlined
+  // for performance, and because it needs to inspect PartitionPage,
+  // it becomes impossible to have it in PartitionBucket as this causes a
+  // cyclical dependency on PartitionPage function implementations.
+  //
+  // Moving it a layer lower couples PartitionRootBase and PartitionBucket, but
+  // preserves the layering of the includes.
+  //
+  // Note the matching Free() functions are in PartitionPage.
+  ALWAYS_INLINE void* AllocFromBucket(PartitionBucket* bucket,
+                                      int flags,
+                                      size_t size);
+
+  ALWAYS_INLINE static bool IsValidPage(PartitionPage* page);
+  ALWAYS_INLINE static PartitionRootBase* FromPage(PartitionPage* page);
+
+  // gOomHandlingFunction is invoked when PartitionAlloc hits OutOfMemory.
+  static void (*gOomHandlingFunction)();
+  NOINLINE void OutOfMemory();
+
+  ALWAYS_INLINE void IncreaseCommittedPages(size_t len);
+  ALWAYS_INLINE void DecreaseCommittedPages(size_t len);
+  ALWAYS_INLINE void DecommitSystemPages(void* address, size_t length);
+  ALWAYS_INLINE void RecommitSystemPages(void* address, size_t length);
+
+  // Frees memory from this partition, if possible, by decommitting pages.
+  // |flags| is an OR of base::PartitionPurgeFlags.
+  virtual void PurgeMemory(int flags) = 0;
+  void DecommitEmptyPages();
+};
+
+ALWAYS_INLINE void* PartitionRootBase::AllocFromBucket(PartitionBucket* bucket,
+                                                       int flags,
+                                                       size_t size) {
+  bool zero_fill = flags & PartitionAllocZeroFill;
+  bool is_already_zeroed = false;
+
+  PartitionPage* page = bucket->active_pages_head;
+  // Check that this page is neither full nor freed.
+  DCHECK(page->num_allocated_slots >= 0);
+  void* ret = page->freelist_head;
+  if (LIKELY(ret != 0)) {
+    // If these DCHECKs fire, you probably corrupted memory. TODO(palmer): See
+    // if we can afford to make these CHECKs.
+    DCHECK(PartitionRootBase::IsValidPage(page));
+
+    // All large allocations must go through the slow path to correctly update
+    // the size metadata.
+    DCHECK(page->get_raw_size() == 0);
+    internal::PartitionFreelistEntry* new_head =
+        internal::EncodedPartitionFreelistEntry::Decode(
+            page->freelist_head->next);
+    page->freelist_head = new_head;
+    page->num_allocated_slots++;
+  } else {
+    ret = bucket->SlowPathAlloc(this, flags, size, &is_already_zeroed);
+    // TODO(palmer): See if we can afford to make this a CHECK.
+    DCHECK(!ret ||
+           PartitionRootBase::IsValidPage(PartitionPage::FromPointer(ret)));
+  }
+
+#if DCHECK_IS_ON()
+  if (!ret) {
+    return nullptr;
+  }
+
+  page = PartitionPage::FromPointer(ret);
+  // TODO(ajwong): Can |page->bucket| ever not be |this|? If not, can this just
+  // be bucket->slot_size?
+  size_t new_slot_size = page->bucket->slot_size;
+  size_t raw_size = page->get_raw_size();
+  if (raw_size) {
+    DCHECK(raw_size == size);
+    new_slot_size = raw_size;
+  }
+  size_t no_cookie_size = PartitionCookieSizeAdjustSubtract(new_slot_size);
+  char* char_ret = static_cast<char*>(ret);
+  // The value given to the application is actually just after the cookie.
+  ret = char_ret + kCookieSize;
+
+  // Fill the region kUninitializedByte or 0, and surround it with 2 cookies.
+  PartitionCookieWriteValue(char_ret);
+  if (!zero_fill) {
+    memset(ret, kUninitializedByte, no_cookie_size);
+  } else if (!is_already_zeroed) {
+    memset(ret, 0, no_cookie_size);
+  }
+  PartitionCookieWriteValue(char_ret + kCookieSize + no_cookie_size);
+#else
+  if (ret && zero_fill && !is_already_zeroed) {
+    memset(ret, 0, size);
+  }
+#endif
+
+  return ret;
+}
+
+ALWAYS_INLINE bool PartitionRootBase::IsValidPage(PartitionPage* page) {
+  PartitionRootBase* root = PartitionRootBase::FromPage(page);
+  return root->inverted_self == ~reinterpret_cast<uintptr_t>(root);
+}
+
+ALWAYS_INLINE PartitionRootBase* PartitionRootBase::FromPage(
+    PartitionPage* page) {
+  PartitionSuperPageExtentEntry* extent_entry =
+      reinterpret_cast<PartitionSuperPageExtentEntry*>(
+          reinterpret_cast<uintptr_t>(page) & kSystemPageBaseMask);
+  return extent_entry->root;
+}
+
+ALWAYS_INLINE void PartitionRootBase::IncreaseCommittedPages(size_t len) {
+  total_size_of_committed_pages += len;
+  DCHECK(total_size_of_committed_pages <=
+         total_size_of_super_pages + total_size_of_direct_mapped_pages);
+}
+
+ALWAYS_INLINE void PartitionRootBase::DecreaseCommittedPages(size_t len) {
+  total_size_of_committed_pages -= len;
+  DCHECK(total_size_of_committed_pages <=
+         total_size_of_super_pages + total_size_of_direct_mapped_pages);
+}
+
+ALWAYS_INLINE void PartitionRootBase::DecommitSystemPages(void* address,
+                                                          size_t length) {
+  ::pdfium::base::DecommitSystemPages(address, length);
+  DecreaseCommittedPages(length);
+}
+
+ALWAYS_INLINE void PartitionRootBase::RecommitSystemPages(void* address,
+                                                          size_t length) {
+  CHECK(::pdfium::base::RecommitSystemPages(address, length, PageReadWrite));
+  IncreaseCommittedPages(length);
+}
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_
diff --git a/third_party/base/allocator/partition_allocator/random.cc b/third_party/base/allocator/partition_allocator/random.cc
new file mode 100644
index 0000000..7a13855
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/random.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/allocator/partition_allocator/random.h"
+
+#include "build/build_config.h"
+#include "third_party/base/allocator/partition_allocator/spin_lock.h"
+#include "third_party/base/no_destructor.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#else
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+namespace pdfium {
+namespace base {
+
+// This is the same PRNG as used by tcmalloc for mapping address randomness;
+// see http://burtleburtle.net/bob/rand/smallprng.html.
+struct RandomContext {
+  subtle::SpinLock lock;
+  bool initialized;
+  uint32_t a;
+  uint32_t b;
+  uint32_t c;
+  uint32_t d;
+};
+
+namespace {
+
+#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+
+uint32_t RandomValueInternal(RandomContext* x) {
+  uint32_t e = x->a - rot(x->b, 27);
+  x->a = x->b ^ rot(x->c, 17);
+  x->b = x->c + x->d;
+  x->c = x->d + e;
+  x->d = e + x->a;
+  return x->d;
+}
+
+#undef rot
+
+RandomContext* GetRandomContext() {
+  static NoDestructor<RandomContext> g_random_context;
+  RandomContext* x = g_random_context.get();
+  subtle::SpinLock::Guard guard(x->lock);
+  if (UNLIKELY(!x->initialized)) {
+    x->initialized = true;
+    char c;
+    uint32_t seed = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&c));
+    uint32_t pid;
+    uint32_t usec;
+#if defined(OS_WIN)
+    pid = GetCurrentProcessId();
+    SYSTEMTIME st;
+    GetSystemTime(&st);
+    usec = static_cast<uint32_t>(st.wMilliseconds * 1000);
+#else
+    pid = static_cast<uint32_t>(getpid());
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    usec = static_cast<uint32_t>(tv.tv_usec);
+#endif
+    seed ^= pid;
+    seed ^= usec;
+    x->a = 0xf1ea5eed;
+    x->b = x->c = x->d = seed;
+    for (int i = 0; i < 20; ++i) {
+      RandomValueInternal(x);
+    }
+  }
+  return x;
+}
+
+}  // namespace
+
+uint32_t RandomValue() {
+  RandomContext* x = GetRandomContext();
+  subtle::SpinLock::Guard guard(x->lock);
+  return RandomValueInternal(x);
+}
+
+void SetMmapSeedForTesting(int64_t seed) {
+  RandomContext* x = GetRandomContext();
+  subtle::SpinLock::Guard guard(x->lock);
+  x->a = x->b = static_cast<uint32_t>(seed);
+  x->c = x->d = static_cast<uint32_t>(seed >> 32);
+  x->initialized = true;
+}
+
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/allocator/partition_allocator/random.h b/third_party/base/allocator/partition_allocator/random.h
new file mode 100644
index 0000000..e485d6d
--- /dev/null
+++ b/third_party/base/allocator/partition_allocator/random.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_
+
+#include <stdint.h>
+
+#include "third_party/base/base_export.h"
+
+namespace pdfium {
+namespace base {
+
+// Returns a random value. The generator's internal state is initialized with
+// `base::RandUint64` which is very unpredictable, but which is expensive due to
+// the need to call into the kernel. Therefore this generator uses a fast,
+// entirely user-space function after initialization.
+BASE_EXPORT uint32_t RandomValue();
+
+// Sets the seed for the random number generator to a known value, to cause the
+// RNG to generate a predictable sequence of outputs. May be called multiple
+// times.
+BASE_EXPORT void SetMmapSeedForTesting(uint64_t seed);
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_
diff --git a/third_party/base/allocator/partition_allocator/spin_lock.cc b/third_party/base/allocator/partition_allocator/spin_lock.cc
index 8d7151a..4205583 100644
--- a/third_party/base/allocator/partition_allocator/spin_lock.cc
+++ b/third_party/base/allocator/partition_allocator/spin_lock.cc
@@ -4,9 +4,12 @@
 
 #include "third_party/base/allocator/partition_allocator/spin_lock.h"
 
+#include "build/build_config.h"
+#include "third_party/base/logging.h"
+
 #if defined(OS_WIN)
 #include <windows.h>
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <sched.h>
 #endif
 
@@ -21,9 +24,12 @@
 // you really should be using a proper lock (such as |base::Lock|)rather than
 // these spinlocks.
 #if defined(OS_WIN)
+
 #define YIELD_PROCESSOR YieldProcessor()
 #define YIELD_THREAD SwitchToThread()
-#elif defined(COMPILER_GCC) || defined(__clang__)
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
 #if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_X86)
 #define YIELD_PROCESSOR __asm__ __volatile__("pause")
 #elif (defined(ARCH_CPU_ARMEL) && __ARM_ARCH >= 6) || defined(ARCH_CPU_ARM64)
@@ -37,22 +43,26 @@
 // Don't bother doing using .word here since r2 is the lowest supported mips64
 // that Chromium supports.
 #define YIELD_PROCESSOR __asm__ __volatile__("pause")
-#endif
-#endif
+#elif defined(ARCH_CPU_PPC64_FAMILY)
+#define YIELD_PROCESSOR __asm__ __volatile__("or 31,31,31")
+#elif defined(ARCH_CPU_S390_FAMILY)
+// just do nothing
+#define YIELD_PROCESSOR ((void)0)
+#endif  // ARCH
 
 #ifndef YIELD_PROCESSOR
 #warning "Processor yield not supported on this architecture."
 #define YIELD_PROCESSOR ((void)0)
 #endif
 
-#ifndef YIELD_THREAD
-#if defined(OS_POSIX)
 #define YIELD_THREAD sched_yield()
-#else
+
+#else  // Other OS
+
 #warning "Thread yield not supported on this OS."
 #define YIELD_THREAD ((void)0)
-#endif
-#endif
+
+#endif  // OS_WIN
 
 namespace pdfium {
 namespace base {
@@ -63,6 +73,9 @@
   // critical section defaults, and various other recommendations.
   // TODO(jschuh): Further tuning may be warranted.
   static const int kYieldProcessorTries = 1000;
+  // The value of |kYieldThreadTries| is completely made up.
+  static const int kYieldThreadTries = 10;
+  int yield_thread_count = 0;
   do {
     do {
       for (int count = 0; count < kYieldProcessorTries; ++count) {
@@ -73,8 +86,17 @@
           return;
       }
 
-      // Give the OS a chance to schedule something on this core.
-      YIELD_THREAD;
+      if (yield_thread_count < kYieldThreadTries) {
+        ++yield_thread_count;
+        // Give the OS a chance to schedule something on this core.
+        YIELD_THREAD;
+      } else {
+        // At this point, it's likely that the lock is held by a lower priority
+        // thread that is unavailable to finish its work because of higher
+        // priority threads spinning here. Sleeping should ensure that they make
+        // progress.
+        NOTREACHED();
+      }
     } while (lock_.load(std::memory_order_relaxed));
   } while (UNLIKELY(lock_.exchange(true, std::memory_order_acquire)));
 }
diff --git a/third_party/base/allocator/partition_allocator/spin_lock.h b/third_party/base/allocator/partition_allocator/spin_lock.h
index 7a42a29..5613fd1 100644
--- a/third_party/base/allocator/partition_allocator/spin_lock.h
+++ b/third_party/base/allocator/partition_allocator/spin_lock.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H
-#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H
+#ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H_
+#define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H_
 
 #include <atomic>
 #include <memory>
@@ -17,16 +17,14 @@
 // intended only for very short duration locks and assume a system with multiple
 // cores. For any potentially longer wait you should use a real lock, such as
 // |base::Lock|.
-//
-// |SpinLock|s MUST be globals. Using them as (e.g.) struct/class members will
-// result in an uninitialized lock, which is dangerously incorrect.
-
 namespace pdfium {
 namespace base {
 namespace subtle {
 
-class SpinLock {
+class BASE_EXPORT SpinLock {
  public:
+  constexpr SpinLock() = default;
+  ~SpinLock() = default;
   using Guard = std::lock_guard<SpinLock>;
 
   ALWAYS_INLINE void lock() {
@@ -42,13 +40,13 @@
  private:
   // This is called if the initial attempt to acquire the lock fails. It's
   // slower, but has a much better scheduling and power consumption behavior.
-  BASE_EXPORT void LockSlow();
+  void LockSlow();
 
-  std::atomic_int lock_;
+  std::atomic_int lock_{0};
 };
 
 }  // namespace subtle
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H
+#endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H_
diff --git a/third_party/base/bits.h b/third_party/base/bits.h
index 220be4b..1253683 100644
--- a/third_party/base/bits.h
+++ b/third_party/base/bits.h
@@ -4,12 +4,14 @@
 
 // This file defines some bit utilities.
 
-#ifndef BASE_BITS_H_
-#define BASE_BITS_H_
+#ifndef THIRD_PARTY_BASE_BITS_H_
+#define THIRD_PARTY_BASE_BITS_H_
 
 #include <stddef.h>
 #include <stdint.h>
 
+#include <type_traits>
+
 #include "third_party/base/compiler_specific.h"
 #include "third_party/base/logging.h"
 
@@ -21,94 +23,165 @@
 namespace base {
 namespace bits {
 
-// Returns the integer i such as 2^i <= n < 2^(i+1)
-inline int Log2Floor(uint32_t n) {
-  if (n == 0)
-    return -1;
-  int log = 0;
-  uint32_t value = n;
-  for (int i = 4; i >= 0; --i) {
-    int shift = (1 << i);
-    uint32_t x = value >> shift;
-    if (x != 0) {
-      value = x;
-      log += shift;
-    }
-  }
-  DCHECK_EQ(value, 1u);
-  return log;
-}
-
-// Returns the integer i such as 2^(i-1) < n <= 2^i
-inline int Log2Ceiling(uint32_t n) {
-  if (n == 0) {
-    return -1;
-  } else {
-    // Log2Floor returns -1 for 0, so the following works correctly for n=1.
-    return 1 + Log2Floor(n - 1);
-  }
+// Returns true iff |value| is a power of 2.
+template <typename T,
+          typename = typename std::enable_if<std::is_integral<T>::value>>
+constexpr inline bool IsPowerOfTwo(T value) {
+  // From "Hacker's Delight": Section 2.1 Manipulating Rightmost Bits.
+  //
+  // Only positive integers with a single bit set are powers of two. If only one
+  // bit is set in x (e.g. 0b00000100000000) then |x-1| will have that bit set
+  // to zero and all bits to its right set to 1 (e.g. 0b00000011111111). Hence
+  // |x & (x-1)| is 0 iff x is a power of two.
+  return value > 0 && (value & (value - 1)) == 0;
 }
 
 // Round up |size| to a multiple of alignment, which must be a power of two.
 inline size_t Align(size_t size, size_t alignment) {
-  DCHECK_EQ(alignment & (alignment - 1), 0u);
+  DCHECK(IsPowerOfTwo(alignment));
   return (size + alignment - 1) & ~(alignment - 1);
 }
 
-// These functions count the number of leading zeros in a binary value, starting
-// with the most significant bit. C does not have an operator to do this, but
-// fortunately the various compilers have built-ins that map to fast underlying
-// processor instructions.
+// CountLeadingZeroBits(value) returns the number of zero bits following the
+// most significant 1 bit in |value| if |value| is non-zero, otherwise it
+// returns {sizeof(T) * 8}.
+// Example: 00100010 -> 2
+//
+// CountTrailingZeroBits(value) returns the number of zero bits preceding the
+// least significant 1 bit in |value| if |value| is non-zero, otherwise it
+// returns {sizeof(T) * 8}.
+// Example: 00100010 -> 1
+//
+// C does not have an operator to do this, but fortunately the various
+// compilers have built-ins that map to fast underlying processor instructions.
 #if defined(COMPILER_MSVC)
 
-ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
+                            unsigned>::type
+    CountLeadingZeroBits(T x) {
+  static_assert(bits > 0, "invalid instantiation");
   unsigned long index;
-  return LIKELY(_BitScanReverse(&index, x)) ? (31 - index) : 32;
+  return LIKELY(_BitScanReverse(&index, static_cast<uint32_t>(x)))
+             ? (31 - index - (32 - bits))
+             : bits;
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
+                            unsigned>::type
+    CountLeadingZeroBits(T x) {
+  static_assert(bits > 0, "invalid instantiation");
+  unsigned long index;
+  return LIKELY(_BitScanReverse64(&index, static_cast<uint64_t>(x)))
+             ? (63 - index)
+             : 64;
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
+                            unsigned>::type
+    CountTrailingZeroBits(T x) {
+  static_assert(bits > 0, "invalid instantiation");
+  unsigned long index;
+  return LIKELY(_BitScanForward(&index, static_cast<uint32_t>(x))) ? index
+                                                                   : bits;
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
+                            unsigned>::type
+    CountTrailingZeroBits(T x) {
+  static_assert(bits > 0, "invalid instantiation");
+  unsigned long index;
+  return LIKELY(_BitScanForward64(&index, static_cast<uint64_t>(x))) ? index
+                                                                     : 64;
+}
+
+ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
+  return CountLeadingZeroBits(x);
 }
 
 #if defined(ARCH_CPU_64_BITS)
 
 // MSVC only supplies _BitScanForward64 when building for a 64-bit target.
 ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
-  unsigned long index;
-  return LIKELY(_BitScanReverse64(&index, x)) ? (63 - index) : 64;
+  return CountLeadingZeroBits(x);
 }
 
 #endif
 
 #elif defined(COMPILER_GCC)
 
-// This is very annoying. __builtin_clz has undefined behaviour for an input of
-// 0, even though there's clearly a return value that makes sense, and even
-// though some processor clz instructions have defined behaviour for 0. We could
-// drop to raw __asm__ to do better, but we'll avoid doing that unless we see
-// proof that we need to.
+// __builtin_clz has undefined behaviour for an input of 0, even though there's
+// clearly a return value that makes sense, and even though some processor clz
+// instructions have defined behaviour for 0. We could drop to raw __asm__ to
+// do better, but we'll avoid doing that unless we see proof that we need to.
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
+                            unsigned>::type
+    CountLeadingZeroBits(T value) {
+  static_assert(bits > 0, "invalid instantiation");
+  return LIKELY(value)
+             ? bits == 64
+                   ? __builtin_clzll(static_cast<uint64_t>(value))
+                   : __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits)
+             : bits;
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
+                            unsigned>::type
+    CountTrailingZeroBits(T value) {
+  return LIKELY(value) ? bits == 64
+                             ? __builtin_ctzll(static_cast<uint64_t>(value))
+                             : __builtin_ctz(static_cast<uint32_t>(value))
+                       : bits;
+}
+
 ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
-  return LIKELY(x) ? __builtin_clz(x) : 32;
+  return CountLeadingZeroBits(x);
 }
 
-ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
-  return LIKELY(x) ? __builtin_clzll(x) : 64;
-}
-
-#endif
-
 #if defined(ARCH_CPU_64_BITS)
 
-ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
-  return CountLeadingZeroBits64(x);
-}
-
-#else
-
-ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
-  return CountLeadingZeroBits32(x);
+ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
+  return CountLeadingZeroBits(x);
 }
 
 #endif
 
+#endif
+
+ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
+  return CountLeadingZeroBits(x);
+}
+
+ALWAYS_INLINE size_t CountTrailingZeroBitsSizeT(size_t x) {
+  return CountTrailingZeroBits(x);
+}
+
+// Returns the integer i such as 2^i <= n < 2^(i+1)
+inline int Log2Floor(uint32_t n) {
+  return 31 - CountLeadingZeroBits(n);
+}
+
+// Returns the integer i such as 2^(i-1) < n <= 2^i
+inline int Log2Ceiling(uint32_t n) {
+  // When n == 0, we want the function to return -1.
+  // When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is
+  // why the statement below starts with (n ? 32 : -1).
+  return (n ? 32 : -1) - CountLeadingZeroBits(n - 1);
+}
+
 }  // namespace bits
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // BASE_BITS_H_
+#endif  // THIRD_PARTY_BASE_BITS_H_
diff --git a/third_party/base/compiler_specific.h b/third_party/base/compiler_specific.h
index 3dbd1ab..947cbf3 100644
--- a/third_party/base/compiler_specific.h
+++ b/third_party/base/compiler_specific.h
@@ -2,83 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_COMPILER_SPECIFIC_H_
-#define BASE_COMPILER_SPECIFIC_H_
+#ifndef THIRD_PARTY_BASE_COMPILER_SPECIFIC_H_
+#define THIRD_PARTY_BASE_COMPILER_SPECIFIC_H_
 
 #include "build/build_config.h"
 
-#if defined(COMPILER_MSVC)
-
-// For _Printf_format_string_.
-#include <sal.h>
-
-// Macros for suppressing and disabling warnings on MSVC.
-//
-// Warning numbers are enumerated at:
-// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
-//
-// The warning pragma:
-// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
-//
-// Using __pragma instead of #pragma inside macros:
-// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
-
-// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and
-// for the next line of the source file.
-#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress : n))
-
-// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
-// The warning remains disabled until popped by MSVC_POP_WARNING.
-#define MSVC_PUSH_DISABLE_WARNING(n) \
-  __pragma(warning(push)) __pragma(warning(disable : n))
-
-// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level.  The level
-// remains in effect until popped by MSVC_POP_WARNING().  Use 0 to disable all
-// warnings.
-#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n))
-
-// Pop effects of innermost MSVC_PUSH_* macro.
-#define MSVC_POP_WARNING() __pragma(warning(pop))
-
-#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off))
-#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on))
-
-// Allows exporting a class that inherits from a non-exported base class.
-// This uses suppress instead of push/pop because the delimiter after the
-// declaration (either "," or "{") has to be placed before the pop macro.
-//
-// Example usage:
-// class EXPORT_API Foo : NON_EXPORTED_BASE(public Bar) {
-//
-// MSVC Compiler warning C4275:
-// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
-// Note that this is intended to be used only when no access to the base class'
-// static data is done through derived classes or inline methods. For more info,
-// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
-#define NON_EXPORTED_BASE(code) \
-  MSVC_SUPPRESS_WARNING(4275)   \
-  code
-
-#else  // Not MSVC
-
-#define _Printf_format_string_
-#define MSVC_SUPPRESS_WARNING(n)
-#define MSVC_PUSH_DISABLE_WARNING(n)
-#define MSVC_PUSH_WARNING_LEVEL(n)
-#define MSVC_POP_WARNING()
-#define MSVC_DISABLE_OPTIMIZE()
-#define MSVC_ENABLE_OPTIMIZE()
-#define NON_EXPORTED_BASE(code) code
-
-#endif  // COMPILER_MSVC
-
 // Annotate a variable indicating it's ok if the variable is not used.
 // (Typically used to silence a compiler warning when the assignment
 // is important for some other reason.)
 // Use like:
 //   int x = ...;
 //   ALLOW_UNUSED_LOCAL(x);
-#define ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0
+#define ALLOW_UNUSED_LOCAL(x) (void)x
 
 // Annotate a typedef or function indicating it's ok if it's not used.
 // Use like:
@@ -100,9 +35,9 @@
 #define NOINLINE
 #endif
 
-#if COMPILER_GCC && defined(NDEBUG)
+#if defined(COMPILER_GCC) && defined(NDEBUG)
 #define ALWAYS_INLINE inline __attribute__((__always_inline__))
-#elif COMPILER_MSVC && defined(NDEBUG)
+#elif defined(COMPILER_MSVC) && defined(NDEBUG)
 #define ALWAYS_INLINE __forceinline
 #else
 #define ALWAYS_INLINE inline
@@ -112,21 +47,29 @@
 // Use like:
 //   class ALIGNAS(16) MyClass { ... }
 //   ALIGNAS(16) int array[4];
+//
+// In most places you can use the C++11 keyword "alignas", which is preferred.
+//
+// But compilers have trouble mixing __attribute__((...)) syntax with
+// alignas(...) syntax.
+//
+// Doesn't work in clang or gcc:
+//   struct alignas(16) __attribute__((packed)) S { char c; };
+// Works in clang but not gcc:
+//   struct __attribute__((packed)) alignas(16) S2 { char c; };
+// Works in clang and gcc:
+//   struct alignas(16) S3 { char c; } __attribute__((packed));
+//
+// There are also some attributes that must be specified *before* a class
+// definition: visibility (used for exporting functions/classes) is one of
+// these attributes. This means that it is not possible to use alignas() with a
+// class that is marked as exported.
 #if defined(COMPILER_MSVC)
 #define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
 #elif defined(COMPILER_GCC)
 #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
 #endif
 
-// Return the byte alignment of the given type (available at compile time).
-// Use like:
-//   ALIGNOF(int32_t)  // this would be 4
-#if defined(COMPILER_MSVC)
-#define ALIGNOF(type) __alignof(type)
-#elif defined(COMPILER_GCC)
-#define ALIGNOF(type) __alignof__(type)
-#endif
-
 // Annotate a function indicating the caller must examine the return value.
 // Use like:
 //   int foo() WARN_UNUSED_RESULT;
@@ -145,7 +88,7 @@
 // (This is undocumented but matches what the system C headers do.)
 #if defined(COMPILER_GCC) || defined(__clang__)
 #define PRINTF_FORMAT(format_param, dots_param) \
-  __attribute__((format(printf, format_param, dots_param)))
+    __attribute__((format(printf, format_param, dots_param)))
 #else
 #define PRINTF_FORMAT(format_param, dots_param)
 #endif
@@ -174,14 +117,14 @@
 // Mark a memory region fully initialized.
 // Use this to annotate code that deliberately reads uninitialized data, for
 // example a GC scavenging root set pointers from the stack.
-#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size)
+#define MSAN_UNPOISON(p, size)  __msan_unpoison(p, size)
 
 // Check a memory region for initializedness, as if it was being used here.
 // If any bits are uninitialized, crash with an MSan report.
 // Use this to sanitize data which MSan won't be able to track, e.g. before
 // passing data to another process via shared memory.
 #define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \
-  __msan_check_mem_is_initialized(p, size)
+    __msan_check_mem_is_initialized(p, size)
 #else  // MEMORY_SANITIZER
 #define MSAN_UNPOISON(p, size)
 #define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
@@ -207,7 +150,7 @@
 
 // Macro for hinting that an expression is likely to be false.
 #if !defined(UNLIKELY)
-#if defined(COMPILER_GCC)
+#if defined(COMPILER_GCC) || defined(__clang__)
 #define UNLIKELY(x) __builtin_expect(!!(x), 0)
 #else
 #define UNLIKELY(x) (x)
@@ -215,7 +158,7 @@
 #endif  // !defined(UNLIKELY)
 
 #if !defined(LIKELY)
-#if defined(COMPILER_GCC)
+#if defined(COMPILER_GCC) || defined(__clang__)
 #define LIKELY(x) __builtin_expect(!!(x), 1)
 #else
 #define LIKELY(x) (x)
@@ -230,4 +173,11 @@
 #define HAS_FEATURE(FEATURE) 0
 #endif
 
-#endif  // BASE_COMPILER_SPECIFIC_H_
+// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
+#if defined(__clang__)
+#define FALLTHROUGH [[clang::fallthrough]]
+#else
+#define FALLTHROUGH
+#endif
+
+#endif  // THIRD_PARTY_BASE_COMPILER_SPECIFIC_H_
diff --git a/third_party/base/debug/alias.cc b/third_party/base/debug/alias.cc
new file mode 100644
index 0000000..6ee2ee9
--- /dev/null
+++ b/third_party/base/debug/alias.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/debug/alias.h"
+
+#include "build/build_config.h"
+
+namespace pdfium {
+namespace base {
+namespace debug {
+
+#if defined(COMPILER_MSVC)
+#pragma optimize("", off)
+#elif defined(__clang__)
+#pragma clang optimize off
+#endif
+
+void Alias(const void* var) {
+}
+
+#if defined(COMPILER_MSVC)
+#pragma optimize("", on)
+#elif defined(__clang__)
+#pragma clang optimize on
+#endif
+
+}  // namespace debug
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/debug/alias.h b/third_party/base/debug/alias.h
new file mode 100644
index 0000000..8228a6f
--- /dev/null
+++ b/third_party/base/debug/alias.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_DEBUG_ALIAS_H_
+#define THIRD_PARTY_BASE_DEBUG_ALIAS_H_
+
+namespace pdfium {
+namespace base {
+namespace debug {
+
+// Make the optimizer think that var is aliased. This is to prevent it from
+// optimizing out local variables that would not otherwise be live at the point
+// of a potential crash.
+// base::debug::Alias should only be used for local variables, not globals,
+// object members, or function return values - these must be copied to locals if
+// you want to ensure they are recorded in crash dumps.
+// Note that if the local variable is a pointer then its value will be retained
+// but the memory that it points to will probably not be saved in the crash
+// dump - by default only stack memory is saved. Therefore the aliasing
+// technique is usually only worthwhile with non-pointer variables. If you have
+// a pointer to an object and you want to retain the object's state you need to
+// copy the object or its fields to local variables. Example usage:
+//   int last_error = err_;
+//   base::debug::Alias(&last_error);
+//   DEBUG_ALIAS_FOR_CSTR(name_copy, p->name, 16);
+//   CHECK(false);
+void Alias(const void* var);
+
+}  // namespace debug
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_DEBUG_ALIAS_H_
diff --git a/third_party/base/logging.h b/third_party/base/logging.h
index 38b0dd9..9c0039d 100644
--- a/third_party/base/logging.h
+++ b/third_party/base/logging.h
@@ -2,38 +2,124 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_LOGGING_H_
-#define PDFIUM_THIRD_PARTY_BASE_LOGGING_H_
+#ifndef THIRD_PARTY_BASE_LOGGING_H_
+#define THIRD_PARTY_BASE_LOGGING_H_
 
 #include <assert.h>
 #include <stdlib.h>
 
-#ifndef _WIN32
-#define NULL_DEREF_IF_POSSIBLE \
-  *(reinterpret_cast<volatile char*>(NULL) + 42) = 0x42;
+#include "build/build_config.h"
+#include "third_party/base/compiler_specific.h"
+
+#if defined(COMPILER_GCC)
+
+#if defined(ARCH_CPU_X86_FAMILY)
+// int 3 will generate a SIGTRAP.
+#define TRAP_SEQUENCE() \
+  asm volatile(         \
+      "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
+
+#elif defined(ARCH_CPU_ARMEL)
+// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
+// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
+// cause a SIGTRAP from userspace without using a syscall (which would be a
+// problem for sandboxing).
+#define TRAP_SEQUENCE() \
+  asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
+
+#elif defined(ARCH_CPU_ARM64)
+// This will always generate a SIGTRAP on arm64.
+#define TRAP_SEQUENCE() \
+  asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
+
 #else
-#define NULL_DEREF_IF_POSSIBLE
+// Crash report accuracy will not be guaranteed on other architectures, but at
+// least this will crash as expected.
+#define TRAP_SEQUENCE() __builtin_trap()
+#endif  // ARCH_CPU_*
+
+#elif defined(COMPILER_MSVC)
+
+// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
+// instruction following the __debugbreak() to have it emit distinct locations
+// for CHECKs rather than collapsing them all together. It would be nice to use
+// a short intrinsic to do this (and perhaps have only one implementation for
+// both clang and MSVC), however clang-cl currently does not support intrinsics.
+// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
+// two implementations. Normally clang-cl's version will be 5 bytes (1 for
+// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
+// https://crbug.com/694670 clang-cl doesn't currently support %'ing
+// __COUNTER__, so eventually it will emit the dword form of push.
+// TODO(scottmg): Reinvestigate a short sequence that will work on both
+// compilers once clang supports more intrinsics. See https://crbug.com/693713.
+#if !defined(__clang__)
+#define TRAP_SEQUENCE() __debugbreak()
+#elif defined(ARCH_CPU_ARM64)
+#define TRAP_SEQUENCE() \
+  __asm volatile("brk #0\n hlt %0\n" ::"i"(__COUNTER__ % 65536));
+#else
+#define TRAP_SEQUENCE() ({ {__asm int 3 __asm ud2 __asm push __COUNTER__}; })
+#endif  // __clang__
+
+#else
+#error Port
+#endif  // COMPILER_GCC
+
+// CHECK() and the trap sequence can be invoked from a constexpr function.
+// This could make compilation fail on GCC, as it forbids directly using inline
+// asm inside a constexpr function. However, it allows calling a lambda
+// expression including the same asm.
+// The side effect is that the top of the stacktrace will not point to the
+// calling function, but to this anonymous lambda. This is still useful as the
+// full name of the lambda will typically include the name of the function that
+// calls CHECK() and the debugger will still break at the right line of code.
+#if !defined(COMPILER_GCC)
+#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE()
+#else
+#define WRAPPED_TRAP_SEQUENCE() \
+  do {                          \
+    [] { TRAP_SEQUENCE(); }();  \
+  } while (false)
 #endif
 
-#define CHECK(condition)   \
-  if (!(condition)) {      \
-    abort();               \
-    NULL_DEREF_IF_POSSIBLE \
-  }
+#if defined(__clang__) || defined(COMPILER_GCC)
+#define IMMEDIATE_CRASH()    \
+  ({                         \
+    WRAPPED_TRAP_SEQUENCE(); \
+    __builtin_unreachable(); \
+  })
+#else
+// This is supporting non-chromium user of logging.h to build with MSVC, like
+// pdfium. On MSVC there is no __builtin_unreachable().
+#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE()
+#endif
 
-// TODO(palmer): These are quick hacks to import PartitionAlloc with minimum
-// hassle. Look into pulling in the real DCHECK definition. It might be more
-// than we need, or have more dependencies than we want. In the meantime, this
-// is safe, at the cost of some performance.
+#define CHECK(condition)          \
+  do {                            \
+    if (UNLIKELY(!(condition))) { \
+      IMMEDIATE_CRASH();          \
+    }                             \
+  } while (0)
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() 0
+#else
+#define DCHECK_IS_ON() 1
+#endif
+
+// Debug mode: Use assert() for better diagnostics
+// Release mode, DCHECK_ALWAYS_ON: Use CHECK() since assert() is a no-op.
+// Release mode, no DCHECK_ALWAYS_ON: Use assert(), which is a no-op.
+#if defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
 #define DCHECK CHECK
-#define DCHECK_EQ(x, y) CHECK((x) == (y))
-#define DCHECK_IS_ON() true
+#else
+#define DCHECK assert
+#endif
 
-// TODO(palmer): Also a quick hack. IMMEDIATE_CRASH used to be simple in
-// Chromium base/, but it got way more complicated and has lots of base/
-// dependencies now. Sad!
-#define IMMEDIATE_CRASH() abort();
+#define CHECK_EQ(x, y) CHECK((x) == (y))
+#define CHECK_NE(x, y) CHECK((x) != (y))
+#define DCHECK_EQ(x, y) DCHECK((x) == (y))
+#define DCHECK_NE(x, y) DCHECK((x) != (y))
+#define NOTREACHED() DCHECK(false)
 
-#define NOTREACHED() assert(false)
-
-#endif  // PDFIUM_THIRD_PARTY_BASE_LOGGING_H_
+#endif  // THIRD_PARTY_BASE_LOGGING_H_
diff --git a/third_party/base/macros.h b/third_party/base/macros.h
deleted file mode 100644
index b6ec161..0000000
--- a/third_party/base/macros.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file contains macros and macro-like constructs (e.g., templates) that
-// are commonly used throughout Chromium source. (It may also contain things
-// that are closely related to things that are commonly used that belong in this
-// file.)
-
-#ifndef PDFIUM_THIRD_PARTY_BASE_MACROS_H_
-#define PDFIUM_THIRD_PARTY_BASE_MACROS_H_
-
-// The COMPILE_ASSERT macro can be used to verify that a compile time
-// expression is true. For example, you could use it to verify the
-// size of a static array:
-//
-//   COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES,
-//                  content_type_names_incorrect_size);
-//
-// or to make sure a struct is smaller than a certain size:
-//
-//   COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
-//
-// The second argument to the macro is the name of the variable. If
-// the expression is false, most compilers will issue a warning/error
-// containing the name of the variable.
-
-#undef COMPILE_ASSERT
-#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
-
-// A macro to disallow the copy constructor and operator= functions.
-// This should be used in the private: declarations for a class.
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-  TypeName(const TypeName&) = delete;      \
-  void operator=(const TypeName&) = delete
-
-#endif  // PDFIUM_THIRD_PARTY_BASE_MACROS_H_
diff --git a/third_party/base/no_destructor.h b/third_party/base/no_destructor.h
new file mode 100644
index 0000000..d9ff32e
--- /dev/null
+++ b/third_party/base/no_destructor.h
@@ -0,0 +1,100 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_NO_DESTRUCTOR_H_
+#define THIRD_PARTY_BASE_NO_DESTRUCTOR_H_
+
+#include <new>
+#include <utility>
+
+namespace pdfium {
+namespace base {
+
+// A wrapper that makes it easy to create an object of type T with static
+// storage duration that:
+// - is only constructed on first access
+// - never invokes the destructor
+// in order to satisfy the styleguide ban on global constructors and
+// destructors.
+//
+// Runtime constant example:
+// const std::string& GetLineSeparator() {
+//  // Forwards to std::string(size_t, char, const Allocator&) constructor.
+//   static const base::NoDestructor<std::string> s(5, '-');
+//   return *s;
+// }
+//
+// More complex initialization with a lambda:
+// const std::string& GetSessionNonce() {
+//   static const base::NoDestructor<std::string> nonce([] {
+//     std::string s(16);
+//     crypto::RandString(s.data(), s.size());
+//     return s;
+//   }());
+//   return *nonce;
+// }
+//
+// NoDestructor<T> stores the object inline, so it also avoids a pointer
+// indirection and a malloc. Also note that since C++11 static local variable
+// initialization is thread-safe and so is this pattern. Code should prefer to
+// use NoDestructor<T> over:
+// - A function scoped static T* or T& that is dynamically initialized.
+// - A global base::LazyInstance<T>.
+//
+// Note that since the destructor is never run, this *will* leak memory if used
+// as a stack or member variable. Furthermore, a NoDestructor<T> should never
+// have global scope as that may require a static initializer.
+template <typename T>
+class NoDestructor {
+ public:
+  // Not constexpr; just write static constexpr T x = ...; if the value should
+  // be a constexpr.
+  template <typename... Args>
+  explicit NoDestructor(Args&&... args) {
+    new (storage_) T(std::forward<Args>(args)...);
+  }
+
+  // Allows copy and move construction of the contained type, to allow
+  // construction from an initializer list, e.g. for std::vector.
+  explicit NoDestructor(const T& x) { new (storage_) T(x); }
+  explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
+
+  NoDestructor(const NoDestructor&) = delete;
+  NoDestructor& operator=(const NoDestructor&) = delete;
+
+  ~NoDestructor() = default;
+
+  const T& operator*() const { return *get(); }
+  T& operator*() { return *get(); }
+
+  const T* operator->() const { return get(); }
+  T* operator->() { return get(); }
+
+  const T* get() const { return reinterpret_cast<const T*>(storage_); }
+  T* get() { return reinterpret_cast<T*>(storage_); }
+
+ private:
+  alignas(T) char storage_[sizeof(T)];
+
+#if defined(LEAK_SANITIZER)
+  // TODO(https://crbug.com/812277): This is a hack to work around the fact
+  // that LSan doesn't seem to treat NoDestructor as a root for reachability
+  // analysis. This means that code like this:
+  //   static base::NoDestructor<std::vector<int>> v({1, 2, 3});
+  // is considered a leak. Using the standard leak sanitizer annotations to
+  // suppress leaks doesn't work: std::vector is implicitly constructed before
+  // calling the base::NoDestructor constructor.
+  //
+  // Unfortunately, I haven't been able to demonstrate this issue in simpler
+  // reproductions: until that's resolved, hold an explicit pointer to the
+  // placement-new'd object in leak sanitizer mode to help LSan realize that
+  // objects allocated by the contained type are still reachable.
+  T* storage_ptr_ = reinterpret_cast<T*>(storage_);
+#endif  // defined(LEAK_SANITIZER)
+};
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NO_DESTRUCTOR_H_
diff --git a/third_party/base/numerics/safe_conversions.h b/third_party/base/numerics/safe_conversions.h
index dc61d9c..bb18562 100644
--- a/third_party/base/numerics/safe_conversions.h
+++ b/third_party/base/numerics/safe_conversions.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_H_
-#define PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_H_
+#ifndef THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_H_
 
 #include <stddef.h>
 
@@ -13,89 +13,121 @@
 
 #include "third_party/base/numerics/safe_conversions_impl.h"
 
+#if defined(__ARMEL__) || defined(__arch64__)
+#include "third_party/base/numerics/safe_conversions_arm_impl.h"
+#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
+#else
+#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
+#endif
+
 namespace pdfium {
 namespace base {
+namespace internal {
 
-// The following are helper constexpr template functions and classes for safely
-// performing a range of conversions, assignments, and tests:
-//
-//  checked_cast<> - Analogous to static_cast<> for numeric types, except
-//      that it CHECKs that the specified numeric conversion will not overflow
-//      or underflow. NaN source will always trigger a CHECK.
-//      The default CHECK triggers a crash, but the handler can be overriden.
-//  saturated_cast<> - Analogous to static_cast<> for numeric types, except
-//      that it returns a saturated result when the specified numeric conversion
-//      would otherwise overflow or underflow. An NaN source returns 0 by
-//      default, but can be overridden to return a different result.
-//  strict_cast<> - Analogous to static_cast<> for numeric types, except that
-//      it will cause a compile failure if the destination type is not large
-//      enough to contain any value in the source type. It performs no runtime
-//      checking and thus introduces no runtime overhead.
-//  IsValueInRangeForNumericType<>() - A convenience function that returns true
-//      if the type supplied to the template parameter can represent the value
-//      passed as an argument to the function.
-//  IsValueNegative<>() - A convenience function that will accept any arithmetic
-//      type as an argument and will return whether the value is less than zero.
-//      Unsigned types always return false.
-//  SafeUnsignedAbs() - Returns the absolute value of the supplied integer
-//      parameter as an unsigned result (thus avoiding an overflow if the value
-//      is the signed, two's complement minimum).
-//  StrictNumeric<> - A wrapper type that performs assignments and copies via
-//      the strict_cast<> template, and can perform valid arithmetic comparisons
-//      across any range of arithmetic types. StrictNumeric is the return type
-//      for values extracted from a CheckedNumeric class instance. The raw
-//      arithmetic value is extracted via static_cast to the underlying type.
-//  MakeStrictNum() - Creates a new StrictNumeric from the underlying type of
-//      the supplied arithmetic or StrictNumeric type.
+#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+template <typename Dst, typename Src>
+struct SaturateFastAsmOp {
+  static const bool is_supported = false;
+  static constexpr Dst Do(Src) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<Dst>();
+  }
+};
+#endif  // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+
+// The following special case a few specific integer conversions where we can
+// eke out better performance than range checking.
+template <typename Dst, typename Src, typename Enable = void>
+struct IsValueInRangeFastOp {
+  static const bool is_supported = false;
+  static constexpr bool Do(Src value) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<bool>();
+  }
+};
+
+// Signed to signed range comparison.
+template <typename Dst, typename Src>
+struct IsValueInRangeFastOp<
+    Dst,
+    Src,
+    typename std::enable_if<
+        std::is_integral<Dst>::value && std::is_integral<Src>::value &&
+        std::is_signed<Dst>::value && std::is_signed<Src>::value &&
+        !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+  static const bool is_supported = true;
+
+  static constexpr bool Do(Src value) {
+    // Just downcast to the smaller type, sign extend it back to the original
+    // type, and then see if it matches the original value.
+    return value == static_cast<Dst>(value);
+  }
+};
+
+// Signed to unsigned range comparison.
+template <typename Dst, typename Src>
+struct IsValueInRangeFastOp<
+    Dst,
+    Src,
+    typename std::enable_if<
+        std::is_integral<Dst>::value && std::is_integral<Src>::value &&
+        !std::is_signed<Dst>::value && std::is_signed<Src>::value &&
+        !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+  static const bool is_supported = true;
+
+  static constexpr bool Do(Src value) {
+    // We cast a signed as unsigned to overflow negative values to the top,
+    // then compare against whichever maximum is smaller, as our upper bound.
+    return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
+  }
+};
 
 // Convenience function that returns true if the supplied value is in range
 // for the destination type.
 template <typename Dst, typename Src>
 constexpr bool IsValueInRangeForNumericType(Src value) {
-  return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid();
+  using SrcType = typename internal::UnderlyingType<Src>::type;
+  return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
+             ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
+                   static_cast<SrcType>(value))
+             : internal::DstRangeRelationToSrcRange<Dst>(
+                   static_cast<SrcType>(value))
+                   .IsValid();
 }
 
-// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
-struct CheckOnFailure {
-  template <typename T>
-  static T HandleFailure() {
-#if defined(__GNUC__) || defined(__clang__)
-    __builtin_trap();
-#else
-    ((void)(*(volatile char*)0 = 0));
-#endif
-    return T();
-  }
-};
-
 // checked_cast<> is analogous to static_cast<> for numeric types,
 // except that it CHECKs that the specified numeric conversion will not
 // overflow or underflow. NaN source will always trigger a CHECK.
-template <typename Dst, class CheckHandler = CheckOnFailure, typename Src>
+template <typename Dst,
+          class CheckHandler = internal::CheckOnFailure,
+          typename Src>
 constexpr Dst checked_cast(Src value) {
   // This throws a compile-time error on evaluating the constexpr if it can be
   // determined at compile-time as failing, otherwise it will CHECK at runtime.
   using SrcType = typename internal::UnderlyingType<Src>::type;
-  return IsValueInRangeForNumericType<Dst, SrcType>(value)
+  return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
              ? static_cast<Dst>(static_cast<SrcType>(value))
              : CheckHandler::template HandleFailure<Dst>();
 }
 
 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
+// You may provide your own limits (e.g. to saturated_cast) so long as you
+// implement all of the static constexpr member functions in the class below.
 template <typename T>
-struct SaturationDefaultHandler {
+struct SaturationDefaultLimits : public std::numeric_limits<T> {
   static constexpr T NaN() {
     return std::numeric_limits<T>::has_quiet_NaN
                ? std::numeric_limits<T>::quiet_NaN()
                : T();
   }
-  static constexpr T max() { return std::numeric_limits<T>::max(); }
+  using std::numeric_limits<T>::max;
   static constexpr T Overflow() {
     return std::numeric_limits<T>::has_infinity
                ? std::numeric_limits<T>::infinity()
                : std::numeric_limits<T>::max();
   }
-  static constexpr T lowest() { return std::numeric_limits<T>::lowest(); }
+  using std::numeric_limits<T>::lowest;
   static constexpr T Underflow() {
     return std::numeric_limits<T>::has_infinity
                ? std::numeric_limits<T>::infinity() * -1
@@ -103,8 +135,6 @@
   }
 };
 
-namespace internal {
-
 template <typename Dst, template <typename> class S, typename Src>
 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
   // For some reason clang generates much better code when the branch is
@@ -118,19 +148,68 @@
                     : S<Dst>::NaN());
 }
 
+// We can reduce the number of conditions and get slightly better performance
+// for normal signed and unsigned integer ranges. And in the specific case of
+// Arm, we can use the optimized saturation instructions.
+template <typename Dst, typename Src, typename Enable = void>
+struct SaturateFastOp {
+  static const bool is_supported = false;
+  static constexpr Dst Do(Src value) {
+    // Force a compile failure if instantiated.
+    return CheckOnFailure::template HandleFailure<Dst>();
+  }
+};
+
+template <typename Dst, typename Src>
+struct SaturateFastOp<
+    Dst,
+    Src,
+    typename std::enable_if<std::is_integral<Src>::value &&
+                            std::is_integral<Dst>::value &&
+                            SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
+  static const bool is_supported = true;
+  static Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); }
+};
+
+template <typename Dst, typename Src>
+struct SaturateFastOp<
+    Dst,
+    Src,
+    typename std::enable_if<std::is_integral<Src>::value &&
+                            std::is_integral<Dst>::value &&
+                            !SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
+  static const bool is_supported = true;
+  static Dst Do(Src value) {
+    // The exact order of the following is structured to hit the correct
+    // optimization heuristics across compilers. Do not change without
+    // checking the emitted code.
+    Dst saturated = CommonMaxOrMin<Dst, Src>(
+        IsMaxInRangeForNumericType<Dst, Src>() ||
+        (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
+    return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
+               ? static_cast<Dst>(value)
+               : saturated;
+  }
+};
+
 // saturated_cast<> is analogous to static_cast<> for numeric types, except
 // that the specified numeric conversion will saturate by default rather than
 // overflow or underflow, and NaN assignment to an integral will return 0.
 // All boundary condition behaviors can be overriden with a custom handler.
 template <typename Dst,
-          template <typename>
-          class SaturationHandler = SaturationDefaultHandler,
+          template <typename> class SaturationHandler = SaturationDefaultLimits,
           typename Src>
 constexpr Dst saturated_cast(Src value) {
   using SrcType = typename UnderlyingType<Src>::type;
-  return saturated_cast_impl<Dst, SaturationHandler, SrcType>(
-      value,
-      DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value));
+  return !IsCompileTimeConstant(value) &&
+                 SaturateFastOp<Dst, SrcType>::is_supported &&
+                 std::is_same<SaturationHandler<Dst>,
+                              SaturationDefaultLimits<Dst>>::value
+             ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
+             : saturated_cast_impl<Dst, SaturationHandler, SrcType>(
+                   static_cast<SrcType>(value),
+                   DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
+                       static_cast<SrcType>(value)));
 }
 
 // strict_cast<> is analogous to static_cast<> for numeric types, except that
@@ -237,30 +316,34 @@
   return os;
 }
 
-#define STRICT_COMPARISON_OP(NAME, OP)                               \
-  template <typename L, typename R,                                  \
-            typename std::enable_if<                                 \
-                internal::IsStrictOp<L, R>::value>::type* = nullptr> \
-  constexpr bool operator OP(const L lhs, const R rhs) {             \
-    return SafeCompare<NAME, typename UnderlyingType<L>::type,       \
-                       typename UnderlyingType<R>::type>(lhs, rhs);  \
+#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP)              \
+  template <typename L, typename R,                                     \
+            typename std::enable_if<                                    \
+                internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
+  constexpr bool operator OP(const L lhs, const R rhs) {                \
+    return SafeCompare<NAME, typename UnderlyingType<L>::type,          \
+                       typename UnderlyingType<R>::type>(lhs, rhs);     \
   }
 
-STRICT_COMPARISON_OP(IsLess, <);
-STRICT_COMPARISON_OP(IsLessOrEqual, <=);
-STRICT_COMPARISON_OP(IsGreater, >);
-STRICT_COMPARISON_OP(IsGreaterOrEqual, >=);
-STRICT_COMPARISON_OP(IsEqual, ==);
-STRICT_COMPARISON_OP(IsNotEqual, !=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)
 
-#undef STRICT_COMPARISON_OP
-};
+}  // namespace internal
 
+using internal::as_signed;
+using internal::as_unsigned;
+using internal::checked_cast;
 using internal::strict_cast;
 using internal::saturated_cast;
 using internal::SafeUnsignedAbs;
 using internal::StrictNumeric;
 using internal::MakeStrictNum;
+using internal::IsValueInRangeForNumericType;
+using internal::IsTypeInRangeForNumericType;
 using internal::IsValueNegative;
 
 // Explicitly make a shorter size_t alias for convenience.
@@ -269,4 +352,4 @@
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_H_
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_H_
diff --git a/third_party/base/numerics/safe_conversions_arm_impl.h b/third_party/base/numerics/safe_conversions_arm_impl.h
new file mode 100644
index 0000000..f5e4c4e
--- /dev/null
+++ b/third_party/base/numerics/safe_conversions_arm_impl.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
+
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "third_party/base/numerics/safe_conversions_impl.h"
+
+namespace pdfium {
+namespace base {
+namespace internal {
+
+// Fast saturation to a destination type.
+template <typename Dst, typename Src>
+struct SaturateFastAsmOp {
+  static constexpr bool is_supported =
+      std::is_signed<Src>::value && std::is_integral<Dst>::value &&
+      std::is_integral<Src>::value &&
+      IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&
+      IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&
+      !IsTypeInRangeForNumericType<Dst, Src>::value;
+
+  __attribute__((always_inline)) static Dst Do(Src value) {
+    int32_t src = value;
+    typename std::conditional<std::is_signed<Dst>::value, int32_t,
+                              uint32_t>::type result;
+    if (std::is_signed<Dst>::value) {
+      asm("ssat %[dst], %[shift], %[src]"
+          : [dst] "=r"(result)
+          : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32
+                                            ? IntegerBitsPlusSign<Dst>::value
+                                            : 32));
+    } else {
+      asm("usat %[dst], %[shift], %[src]"
+          : [dst] "=r"(result)
+          : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32
+                                            ? IntegerBitsPlusSign<Dst>::value
+                                            : 31));
+    }
+    return static_cast<Dst>(result);
+  }
+};
+
+}  // namespace internal
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
diff --git a/third_party/base/numerics/safe_conversions_impl.h b/third_party/base/numerics/safe_conversions_impl.h
index 4077b28..bdad911 100644
--- a/third_party/base/numerics/safe_conversions_impl.h
+++ b/third_party/base/numerics/safe_conversions_impl.h
@@ -2,14 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
-#define PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
+#ifndef THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
 
 #include <stdint.h>
 
 #include <limits>
 #include <type_traits>
 
+#if defined(__GNUC__) || defined(__clang__)
+#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
+#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define BASE_NUMERICS_LIKELY(x) (x)
+#define BASE_NUMERICS_UNLIKELY(x) (x)
+#endif
+
 namespace pdfium {
 namespace base {
 namespace internal {
@@ -77,6 +85,48 @@
                                 : static_cast<UnsignedT>(value);
 }
 
+// This allows us to switch paths on known compile-time constants.
+#if defined(__clang__) || defined(__GNUC__)
+constexpr bool CanDetectCompileTimeConstant() {
+  return true;
+}
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T v) {
+  return __builtin_constant_p(v);
+}
+#else
+constexpr bool CanDetectCompileTimeConstant() {
+  return false;
+}
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T) {
+  return false;
+}
+#endif
+template <typename T>
+constexpr bool MustTreatAsConstexpr(const T v) {
+  // Either we can't detect a compile-time constant, and must always use the
+  // constexpr path, or we know we have a compile-time constant.
+  return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
+}
+
+// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
+// Also used in a constexpr template to trigger a compilation failure on
+// an error condition.
+struct CheckOnFailure {
+  template <typename T>
+  static T HandleFailure() {
+#if defined(_MSC_VER)
+    __debugbreak();
+#elif defined(__GNUC__) || defined(__clang__)
+    __builtin_trap();
+#else
+    ((void)(*(volatile char*)0 = 0));
+#endif
+    return T();
+  }
+};
+
 enum IntegerRepresentation {
   INTEGER_REPRESENTATION_UNSIGNED,
   INTEGER_REPRESENTATION_SIGNED
@@ -143,7 +193,7 @@
  public:
   constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
       : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
-  constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {}
+  constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
   constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
   constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
   constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
@@ -338,6 +388,13 @@
   }
 };
 
+// Simple wrapper for statically checking if a type's range is contained.
+template <typename Dst, typename Src>
+struct IsTypeInRangeForNumericType {
+  static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+                            NUMERIC_RANGE_CONTAINED;
+};
+
 template <typename Dst,
           template <typename> class Bounds = std::numeric_limits,
           typename Src>
@@ -518,38 +575,16 @@
   static const bool is_contained = false;
 };
 
-// This hacks around libstdc++ 4.6 missing stuff in type_traits.
-#if defined(__GLIBCXX__)
-#define PRIV_GLIBCXX_4_7_0 20120322
-#define PRIV_GLIBCXX_4_5_4 20120702
-#define PRIV_GLIBCXX_4_6_4 20121127
-#if (__GLIBCXX__ < PRIV_GLIBCXX_4_7_0 || __GLIBCXX__ == PRIV_GLIBCXX_4_5_4 || \
-     __GLIBCXX__ == PRIV_GLIBCXX_4_6_4)
-#define PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX
-#undef PRIV_GLIBCXX_4_7_0
-#undef PRIV_GLIBCXX_4_5_4
-#undef PRIV_GLIBCXX_4_6_4
-#endif
-#endif
-
 // Extracts the underlying type from an enum.
 template <typename T, bool is_enum = std::is_enum<T>::value>
 struct ArithmeticOrUnderlyingEnum;
 
 template <typename T>
 struct ArithmeticOrUnderlyingEnum<T, true> {
-#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX)
-  using type = __underlying_type(T);
-#else
   using type = typename std::underlying_type<T>::type;
-#endif
   static const bool value = std::is_arithmetic<type>::value;
 };
 
-#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX)
-#undef PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX
-#endif
-
 template <typename T>
 struct ArithmeticOrUnderlyingEnum<T, false> {
   using type = T;
@@ -561,6 +596,9 @@
 class CheckedNumeric;
 
 template <typename T>
+class ClampedNumeric;
+
+template <typename T>
 class StrictNumeric;
 
 // Used to treat CheckedNumeric and arithmetic underlying types the same.
@@ -569,6 +607,7 @@
   using type = typename ArithmeticOrUnderlyingEnum<T>::type;
   static const bool is_numeric = std::is_arithmetic<type>::value;
   static const bool is_checked = false;
+  static const bool is_clamped = false;
   static const bool is_strict = false;
 };
 
@@ -577,6 +616,16 @@
   using type = T;
   static const bool is_numeric = true;
   static const bool is_checked = true;
+  static const bool is_clamped = false;
+  static const bool is_strict = false;
+};
+
+template <typename T>
+struct UnderlyingType<ClampedNumeric<T>> {
+  using type = T;
+  static const bool is_numeric = true;
+  static const bool is_checked = false;
+  static const bool is_clamped = true;
   static const bool is_strict = false;
 };
 
@@ -585,6 +634,7 @@
   using type = T;
   static const bool is_numeric = true;
   static const bool is_checked = false;
+  static const bool is_clamped = false;
   static const bool is_strict = true;
 };
 
@@ -596,12 +646,46 @@
 };
 
 template <typename L, typename R>
+struct IsClampedOp {
+  static const bool value =
+      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
+      (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
+      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
+};
+
+template <typename L, typename R>
 struct IsStrictOp {
   static const bool value =
       UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
-      (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict);
+      (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
+      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
+      !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
 };
 
+// as_signed<> returns the supplied integral value (or integral castable
+// Numeric template) cast as a signed integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_signed<
+    typename base::internal::UnderlyingType<Src>::type>::type
+as_signed(const Src value) {
+  static_assert(std::is_integral<decltype(as_signed(value))>::value,
+                "Argument must be a signed or unsigned integer type.");
+  return static_cast<decltype(as_signed(value))>(value);
+}
+
+// as_unsigned<> returns the supplied integral value (or integral castable
+// Numeric template) cast as an unsigned integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_unsigned<
+    typename base::internal::UnderlyingType<Src>::type>::type
+as_unsigned(const Src value) {
+  static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
+                "Argument must be a signed or unsigned integer type.");
+  return static_cast<decltype(as_unsigned(value))>(value);
+}
+
 template <typename L, typename R>
 constexpr bool IsLessImpl(const L lhs,
                           const R rhs,
@@ -727,8 +811,42 @@
              : C<L, R>::Test(lhs, rhs);
 }
 
+template <typename Dst, typename Src>
+constexpr bool IsMaxInRangeForNumericType() {
+  return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
+                                          std::numeric_limits<Src>::max());
+}
+
+template <typename Dst, typename Src>
+constexpr bool IsMinInRangeForNumericType() {
+  return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
+                                       std::numeric_limits<Src>::lowest());
+}
+
+template <typename Dst, typename Src>
+constexpr Dst CommonMax() {
+  return !IsMaxInRangeForNumericType<Dst, Src>()
+             ? Dst(std::numeric_limits<Dst>::max())
+             : Dst(std::numeric_limits<Src>::max());
+}
+
+template <typename Dst, typename Src>
+constexpr Dst CommonMin() {
+  return !IsMinInRangeForNumericType<Dst, Src>()
+             ? Dst(std::numeric_limits<Dst>::lowest())
+             : Dst(std::numeric_limits<Src>::lowest());
+}
+
+// This is a wrapper to generate return the max or min for a supplied type.
+// If the argument is false, the returned value is the maximum. If true the
+// returned value is the minimum.
+template <typename Dst, typename Src = Dst>
+constexpr Dst CommonMaxOrMin(bool is_min) {
+  return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
+}
+
 }  // namespace internal
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
diff --git a/third_party/base/numerics/safe_math.h b/third_party/base/numerics/safe_math.h
index f24ba02..3007cd7 100644
--- a/third_party/base/numerics/safe_math.h
+++ b/third_party/base/numerics/safe_math.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
-#define PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
+#ifndef THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
 
 #include <stddef.h>
 
@@ -178,7 +178,7 @@
   }
 
   // This friend method is available solely for providing more detailed logging
-  // in the the tests. Do not implement it in production code, because the
+  // in the tests. Do not implement it in production code, because the
   // underlying values may change at any time.
   template <typename U>
   friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
@@ -300,7 +300,7 @@
         Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
         Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
     return CheckedNumeric<T>(result, is_valid);
-  };
+  }
 
   // Assignment arithmetic operations.
   template <template <typename, typename, typename> class M, typename R>
@@ -311,7 +311,7 @@
                     Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
     *this = CheckedNumeric<T>(result, is_valid);
     return *this;
-  };
+  }
 
  private:
   CheckedNumericState<T> state_;
@@ -507,4 +507,4 @@
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_
diff --git a/third_party/base/numerics/safe_math_impl.h b/third_party/base/numerics/safe_math_impl.h
index 4bcc671..d91dd2b 100644
--- a/third_party/base/numerics/safe_math_impl.h
+++ b/third_party/base/numerics/safe_math_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
-#define PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
+#ifndef THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
+#define THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
 
 #include <stddef.h>
 #include <stdint.h>
@@ -44,7 +44,11 @@
 };
 
 // Probe for builtin math overflow support on Clang and version check on GCC.
-#if defined(__has_builtin)
+#if defined(EMSCRIPTEN)
+// Emscripten Clang reports that it has the builtins, it may be lowered to an
+// instruction that is unsupported in asm.js
+#define USE_OVERFLOW_BUILTINS (0)
+#elif defined(__has_builtin)
 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow))
 #elif defined(__GNUC__)
 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5)
@@ -640,4 +644,4 @@
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
+#endif  // THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_IMPL_H_
diff --git a/third_party/base/optional.h b/third_party/base/optional.h
index f92101c..5998105 100644
--- a/third_party/base/optional.h
+++ b/third_party/base/optional.h
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_OPTIONAL_H_
-#define PDFIUM_THIRD_PARTY_BASE_OPTIONAL_H_
+#ifndef THIRD_PARTY_BASE_OPTIONAL_H_
+#define THIRD_PARTY_BASE_OPTIONAL_H_
 
+#include <functional>
 #include <type_traits>
+#include <utility>
 
 #include "third_party/base/logging.h"
 
@@ -506,4 +508,4 @@
 template <class T>
 using Optional = pdfium::Optional<T>;
 
-#endif  // PDFIUM_THIRD_PARTY_BASE_OPTIONAL_H_
+#endif  // THIRD_PARTY_BASE_OPTIONAL_H_
diff --git a/third_party/base/ptr_util.h b/third_party/base/ptr_util.h
index b64261f..36f0920 100644
--- a/third_party/base/ptr_util.h
+++ b/third_party/base/ptr_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_PTR_UTIL_H_
-#define PDFIUM_THIRD_PARTY_BASE_PTR_UTIL_H_
+#ifndef THIRD_PARTY_BASE_PTR_UTIL_H_
+#define THIRD_PARTY_BASE_PTR_UTIL_H_
 
 #include <memory>
 #include <utility>
@@ -71,4 +71,4 @@
 
 }  // namespace pdfium
 
-#endif  // PDFIUM_THIRD_PARTY_BASE_PTR_UTIL_H_
+#endif  // THIRD_PARTY_BASE_PTR_UTIL_H_
diff --git a/third_party/base/span.h b/third_party/base/span.h
new file mode 100644
index 0000000..bb07f43
--- /dev/null
+++ b/third_party/base/span.h
@@ -0,0 +1,352 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_SPAN_H_
+#define THIRD_PARTY_BASE_SPAN_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/logging.h"
+
+namespace pdfium {
+
+template <typename T>
+class span;
+
+namespace internal {
+
+template <typename T>
+struct IsSpanImpl : std::false_type {};
+
+template <typename T>
+struct IsSpanImpl<span<T>> : std::true_type {};
+
+template <typename T>
+using IsSpan = IsSpanImpl<typename std::decay<T>::type>;
+
+template <typename T>
+struct IsStdArrayImpl : std::false_type {};
+
+template <typename T, size_t N>
+struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+using IsStdArray = IsStdArrayImpl<typename std::decay<T>::type>;
+
+template <typename From, typename To>
+using IsLegalSpanConversion = std::is_convertible<From*, To*>;
+
+template <typename Container, typename T>
+using ContainerHasConvertibleData =
+    IsLegalSpanConversion<typename std::remove_pointer<decltype(
+                              std::declval<Container>().data())>::type,
+                          T>;
+template <typename Container>
+using ContainerHasIntegralSize =
+    std::is_integral<decltype(std::declval<Container>().size())>;
+
+template <typename From, typename To>
+using EnableIfLegalSpanConversion =
+    typename std::enable_if<IsLegalSpanConversion<From, To>::value>::type;
+
+// SFINAE check if Container can be converted to a span<T>. Note that the
+// implementation details of this check differ slightly from the requirements in
+// the working group proposal: in particular, the proposal also requires that
+// the container conversion constructor participate in overload resolution only
+// if two additional conditions are true:
+//
+//   1. Container implements operator[].
+//   2. Container::value_type matches remove_const_t<element_type>.
+//
+// The requirements are relaxed slightly here: in particular, not requiring (2)
+// means that an immutable span can be easily constructed from a mutable
+// container.
+template <typename Container, typename T>
+using EnableIfSpanCompatibleContainer =
+    typename std::enable_if<!internal::IsSpan<Container>::value &&
+                            !internal::IsStdArray<Container>::value &&
+                            ContainerHasConvertibleData<Container, T>::value &&
+                            ContainerHasIntegralSize<Container>::value>::type;
+
+template <typename Container, typename T>
+using EnableIfConstSpanCompatibleContainer =
+    typename std::enable_if<std::is_const<T>::value &&
+                            !internal::IsSpan<Container>::value &&
+                            !internal::IsStdArray<Container>::value &&
+                            ContainerHasConvertibleData<Container, T>::value &&
+                            ContainerHasIntegralSize<Container>::value>::type;
+
+}  // namespace internal
+
+// A span is a value type that represents an array of elements of type T. Since
+// it only consists of a pointer to memory with an associated size, it is very
+// light-weight. It is cheap to construct, copy, move and use spans, so that
+// users are encouraged to use it as a pass-by-value parameter. A span does not
+// own the underlying memory, so care must be taken to ensure that a span does
+// not outlive the backing store.
+//
+// span is somewhat analogous to StringPiece, but with arbitrary element types,
+// allowing mutation if T is non-const.
+//
+// span is implicitly convertible from C++ arrays, as well as most [1]
+// container-like types that provide a data() and size() method (such as
+// std::vector<T>). A mutable span<T> can also be implicitly converted to an
+// immutable span<const T>.
+//
+// Consider using a span for functions that take a data pointer and size
+// parameter: it allows the function to still act on an array-like type, while
+// allowing the caller code to be a bit more concise.
+//
+// For read-only data access pass a span<const T>: the caller can supply either
+// a span<const T> or a span<T>, while the callee will have a read-only view.
+// For read-write access a mutable span<T> is required.
+//
+// Without span:
+//   Read-Only:
+//     // std::string HexEncode(const uint8_t* data, size_t size);
+//     std::vector<uint8_t> data_buffer = GenerateData();
+//     std::string r = HexEncode(data_buffer.data(), data_buffer.size());
+//
+//  Mutable:
+//     // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
+//     char str_buffer[100];
+//     SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
+//
+// With span:
+//   Read-Only:
+//     // std::string HexEncode(base::span<const uint8_t> data);
+//     std::vector<uint8_t> data_buffer = GenerateData();
+//     std::string r = HexEncode(data_buffer);
+//
+//  Mutable:
+//     // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
+//     char str_buffer[100];
+//     SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
+//
+// Spans with "const" and pointers
+// -------------------------------
+//
+// Const and pointers can get confusing. Here are vectors of pointers and their
+// corresponding spans (you can always make the span "more const" too):
+//
+//   const std::vector<int*>        =>  base::span<int* const>
+//   std::vector<const int*>        =>  base::span<const int*>
+//   const std::vector<const int*>  =>  base::span<const int* const>
+//
+// Differences from the working group proposal
+// -------------------------------------------
+//
+// https://wg21.link/P0122 is the latest working group proposal, Chromium
+// currently implements R6. The biggest difference is span does not support a
+// static extent template parameter. Other differences are documented in
+// subsections below.
+//
+// Differences from [views.constants]:
+// - no dynamic_extent constant
+//
+// Differences in constants and types:
+// - no element_type type alias
+// - no index_type type alias
+// - no different_type type alias
+// - no extent constant
+//
+// Differences from [span.cons]:
+// - no constructor from a pointer range
+// - no constructor from std::array
+//
+// Differences from [span.sub]:
+// - no templated first()
+// - no templated last()
+// - no templated subspan()
+// - using size_t instead of ptrdiff_t for indexing
+//
+// Differences from [span.obs]:
+// - using size_t instead of ptrdiff_t to represent size()
+//
+// Differences from [span.elem]:
+// - no operator ()()
+// - using size_t instead of ptrdiff_t for indexing
+
+// [span], class template span
+template <typename T>
+class span {
+ public:
+  using value_type = typename std::remove_cv<T>::type;
+  using pointer = T*;
+  using reference = T&;
+  using iterator = T*;
+  using const_iterator = const T*;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+  // [span.cons], span constructors, copy, assignment, and destructor
+  constexpr span() noexcept : data_(nullptr), size_(0) {}
+  constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) {}
+
+  // TODO(dcheng): Implement construction from a |begin| and |end| pointer.
+  template <size_t N>
+  constexpr span(T (&array)[N]) noexcept : span(array, N) {}
+  // TODO(dcheng): Implement construction from std::array.
+  // Conversion from a container that provides |T* data()| and |integral_type
+  // size()|.
+  template <typename Container,
+            typename = internal::EnableIfSpanCompatibleContainer<Container, T>>
+  constexpr span(Container& container)
+      : span(container.data(), container.size()) {}
+  template <
+      typename Container,
+      typename = internal::EnableIfConstSpanCompatibleContainer<Container, T>>
+  span(const Container& container) : span(container.data(), container.size()) {}
+  constexpr span(const span& other) noexcept = default;
+  // Conversions from spans of compatible types: this allows a span<T> to be
+  // seamlessly used as a span<const T>, but not the other way around.
+  template <typename U, typename = internal::EnableIfLegalSpanConversion<U, T>>
+  constexpr span(const span<U>& other) : span(other.data(), other.size()) {}
+  span& operator=(const span& other) noexcept = default;
+  ~span() noexcept {
+    if (!size_) {
+      // Empty spans might point to byte N+1 of a N-byte object, legal for
+      // C pointers but not UnownedPtrs.
+      data_.ReleaseBadPointer();
+    }
+  }
+
+  // [span.sub], span subviews
+  const span first(size_t count) const {
+    CHECK(count <= size_);
+    return span(data_.Get(), count);
+  }
+
+  const span last(size_t count) const {
+    CHECK(count <= size_);
+    return span(data_.Get() + (size_ - count), count);
+  }
+
+  const span subspan(size_t pos, size_t count = -1) const {
+    const auto npos = static_cast<size_t>(-1);
+    CHECK(pos <= size_);
+    CHECK(count == npos || count <= size_ - pos);
+    return span(data_.Get() + pos, count == npos ? size_ - pos : count);
+  }
+
+  // [span.obs], span observers
+  constexpr size_t size() const noexcept { return size_; }
+  constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
+  constexpr bool empty() const noexcept { return size_ == 0; }
+
+  // [span.elem], span element access
+  T& operator[](size_t index) const noexcept {
+    CHECK(index < size_);
+    return data_.Get()[index];
+  }
+  constexpr T* data() const noexcept { return data_.Get(); }
+
+  // [span.iter], span iterator support
+  constexpr iterator begin() const noexcept { return data_.Get(); }
+  constexpr iterator end() const noexcept { return data_.Get() + size_; }
+
+  constexpr const_iterator cbegin() const noexcept { return begin(); }
+  constexpr const_iterator cend() const noexcept { return end(); }
+
+  constexpr reverse_iterator rbegin() const noexcept {
+    return reverse_iterator(end());
+  }
+  constexpr reverse_iterator rend() const noexcept {
+    return reverse_iterator(begin());
+  }
+
+  constexpr const_reverse_iterator crbegin() const noexcept {
+    return const_reverse_iterator(cend());
+  }
+  constexpr const_reverse_iterator crend() const noexcept {
+    return const_reverse_iterator(cbegin());
+  }
+
+ private:
+  UnownedPtr<T> data_;
+  size_t size_;
+};
+
+// [span.comparison], span comparison operators
+// Relational operators. Equality is a element-wise comparison.
+template <typename T>
+constexpr bool operator==(span<T> lhs, span<T> rhs) noexcept {
+  return lhs.size() == rhs.size() &&
+         std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
+}
+
+template <typename T>
+constexpr bool operator!=(span<T> lhs, span<T> rhs) noexcept {
+  return !(lhs == rhs);
+}
+
+template <typename T>
+constexpr bool operator<(span<T> lhs, span<T> rhs) noexcept {
+  return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
+                                      rhs.cend());
+}
+
+template <typename T>
+constexpr bool operator<=(span<T> lhs, span<T> rhs) noexcept {
+  return !(rhs < lhs);
+}
+
+template <typename T>
+constexpr bool operator>(span<T> lhs, span<T> rhs) noexcept {
+  return rhs < lhs;
+}
+
+template <typename T>
+constexpr bool operator>=(span<T> lhs, span<T> rhs) noexcept {
+  return !(lhs < rhs);
+}
+
+// [span.objectrep], views of object representation
+template <typename T>
+span<const uint8_t> as_bytes(span<T> s) noexcept {
+  return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
+}
+
+template <typename T,
+          typename U = typename std::enable_if<!std::is_const<T>::value>::type>
+span<uint8_t> as_writable_bytes(span<T> s) noexcept {
+  return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
+}
+
+// Type-deducing helpers for constructing a span.
+template <typename T>
+constexpr span<T> make_span(T* data, size_t size) noexcept {
+  return span<T>(data, size);
+}
+
+template <typename T, size_t N>
+constexpr span<T> make_span(T (&array)[N]) noexcept {
+  return span<T>(array);
+}
+
+template <typename Container,
+          typename T = typename Container::value_type,
+          typename = internal::EnableIfSpanCompatibleContainer<Container, T>>
+constexpr span<T> make_span(Container& container) {
+  return span<T>(container);
+}
+
+template <
+    typename Container,
+    typename T = typename std::add_const<typename Container::value_type>::type,
+    typename = internal::EnableIfConstSpanCompatibleContainer<Container, T>>
+constexpr span<T> make_span(const Container& container) {
+  return span<T>(container);
+}
+
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_SPAN_H_
diff --git a/third_party/base/stl_util.h b/third_party/base/stl_util.h
index 6c36ddc..925c96a 100644
--- a/third_party/base/stl_util.h
+++ b/third_party/base/stl_util.h
@@ -2,18 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef PDFIUM_THIRD_PARTY_BASE_STL_UTIL_H_
-#define PDFIUM_THIRD_PARTY_BASE_STL_UTIL_H_
+#ifndef THIRD_PARTY_BASE_STL_UTIL_H_
+#define THIRD_PARTY_BASE_STL_UTIL_H_
 
 #include <algorithm>
 #include <iterator>
 #include <memory>
 #include <set>
+#include <vector>
 
 #include "third_party/base/numerics/safe_conversions.h"
+#include "third_party/base/numerics/safe_math.h"
 
 namespace pdfium {
 
+// C++11 implementation of C++17's std::size():
+// http://en.cppreference.com/w/cpp/iterator/size
+template <typename Container>
+constexpr auto size(const Container& c) -> decltype(c.size()) {
+  return c.size();
+}
+
+template <typename T, size_t N>
+constexpr size_t size(const T (&array)[N]) noexcept {
+  return N;
+}
+
 // Test to see if a set, map, hash_set or hash_map contains a particular key.
 // Returns true if the key is in the collection.
 template <typename Collection, typename Key>
@@ -74,6 +88,14 @@
   return std::min(std::max(v, lo), hi);
 }
 
+// Safely allocate a 1-dim vector big enough for |w| by |h| or die.
+template <typename T, typename A = std::allocator<T>>
+std::vector<T, A> Vector2D(size_t w, size_t h) {
+  pdfium::base::CheckedNumeric<size_t> safe_size = w;
+  safe_size *= h;
+  return std::vector<T, A>(safe_size.ValueOrDie());
+}
+
 }  // namespace pdfium
 
-#endif  // PDFIUM_THIRD_PARTY_BASE_STL_UTIL_H_
+#endif  // THIRD_PARTY_BASE_STL_UTIL_H_
diff --git a/third_party/base/sys_byteorder.h b/third_party/base/sys_byteorder.h
index 593abe1..8b252f1 100644
--- a/third_party/base/sys_byteorder.h
+++ b/third_party/base/sys_byteorder.h
@@ -8,13 +8,13 @@
 // Use the functions defined here rather than using the platform-specific
 // functions directly.
 
-#ifndef BASE_SYS_BYTEORDER_H_
-#define BASE_SYS_BYTEORDER_H_
+#ifndef THIRD_PARTY_BASE_SYS_BYTEORDER_H_
+#define THIRD_PARTY_BASE_SYS_BYTEORDER_H_
 
 #include <stdint.h>
 
+#include "build/build_config.h"
 #include "third_party/base/logging.h"
-#include "third_party/build/build_config.h"
 
 #if defined(COMPILER_MSVC)
 #include <stdlib.h>
@@ -138,4 +138,4 @@
 }  // namespace base
 }  // namespace pdfium
 
-#endif  // BASE_SYS_BYTEORDER_H_
+#endif  // THIRD_PARTY_BASE_SYS_BYTEORDER_H_
diff --git a/third_party/base/template_util.h b/third_party/base/template_util.h
index b246322..6f31f47 100644
--- a/third_party/base/template_util.h
+++ b/third_party/base/template_util.h
@@ -2,26 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_TEMPLATE_UTIL_H_
-#define BASE_TEMPLATE_UTIL_H_
+#ifndef THIRD_PARTY_BASE_TEMPLATE_UTIL_H_
+#define THIRD_PARTY_BASE_TEMPLATE_UTIL_H_
 
 #include <stddef.h>
 #include <iosfwd>
+#include <iterator>
 #include <type_traits>
 #include <utility>
+#include <vector>
 
-#include "third_party/build/build_config.h"
-
-// This hacks around libstdc++ 4.6 missing stuff in type_traits, while we need
-// to support it.
-#define CR_GLIBCXX_4_7_0 20120322
-#define CR_GLIBCXX_4_5_4 20120702
-#define CR_GLIBCXX_4_6_4 20121127
-#if defined(__GLIBCXX__) &&                                               \
-    (__GLIBCXX__ < CR_GLIBCXX_4_7_0 || __GLIBCXX__ == CR_GLIBCXX_4_5_4 || \
-     __GLIBCXX__ == CR_GLIBCXX_4_6_4)
-#define CR_USE_FALLBACKS_FOR_OLD_GLIBCXX
-#endif
+#include "build/build_config.h"
 
 // Some versions of libstdc++ have partial support for type_traits, but misses
 // a smaller subset while removing some of the older non-standard stuff. Assume
@@ -47,52 +38,30 @@
 
 namespace base {
 
-template <class T>
-struct is_non_const_reference : std::false_type {};
-template <class T>
-struct is_non_const_reference<T&> : std::true_type {};
-template <class T>
-struct is_non_const_reference<const T&> : std::false_type {};
-
-// is_assignable
+template <class T> struct is_non_const_reference : std::false_type {};
+template <class T> struct is_non_const_reference<T&> : std::true_type {};
+template <class T> struct is_non_const_reference<const T&> : std::false_type {};
 
 namespace internal {
 
-template <typename First, typename Second>
-struct SelectSecond {
-  using type = Second;
+// Implementation detail of base::void_t below.
+template <typename...>
+struct make_void {
+  using type = void;
 };
 
-struct Any {
-  Any(...);
-};
+}  // namespace internal
 
-// True case: If |Lvalue| can be assigned to from |Rvalue|, then the return
-// value is a true_type.
-template <class Lvalue, class Rvalue>
-typename internal::SelectSecond<
-    decltype((std::declval<Lvalue>() = std::declval<Rvalue>())),
-    std::true_type>::type
-IsAssignableTest(Lvalue&&, Rvalue&&);
+// base::void_t is an implementation of std::void_t from C++17.
+//
+// We use |base::internal::make_void| as a helper struct to avoid a C++14
+// defect:
+//   http://en.cppreference.com/w/cpp/types/void_t
+//   http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558
+template <typename... Ts>
+using void_t = typename ::base::internal::make_void<Ts...>::type;
 
-// False case: Otherwise the return value is a false_type.
-template <class Rvalue>
-std::false_type IsAssignableTest(internal::Any, Rvalue&&);
-
-// Default case: Neither Lvalue nor Rvalue is void. Uses IsAssignableTest to
-// determine the type of IsAssignableImpl.
-template <class Lvalue,
-          class Rvalue,
-          bool = std::is_void<Lvalue>::value || std::is_void<Rvalue>::value>
-struct IsAssignableImpl
-    : public std::common_type<decltype(
-          internal::IsAssignableTest(std::declval<Lvalue>(),
-                                     std::declval<Rvalue>()))>::type {};
-
-// Void case: Either Lvalue or Rvalue is void. Then the type of IsAssignableTest
-// is false_type.
-template <class Lvalue, class Rvalue>
-struct IsAssignableImpl<Lvalue, Rvalue, true> : public std::false_type {};
+namespace internal {
 
 // Uses expression SFINAE to detect whether using operator<< would work.
 template <typename T, typename = void>
@@ -103,58 +72,23 @@
                                              << std::declval<T>()))>
     : std::true_type {};
 
+// Used to detech whether the given type is an iterator.  This is normally used
+// with std::enable_if to provide disambiguation for functions that take
+// templatzed iterators as input.
+template <typename T, typename = void>
+struct is_iterator : std::false_type {};
+
+template <typename T>
+struct is_iterator<T,
+                   void_t<typename std::iterator_traits<T>::iterator_category>>
+    : std::true_type {};
+
 }  // namespace internal
 
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace.
-template <class Lvalue, class Rvalue>
-struct is_assignable : public internal::IsAssignableImpl<Lvalue, Rvalue> {};
-
-// is_copy_assignable is true if a T const& is assignable to a T&.
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace.
-template <class T>
-struct is_copy_assignable
-    : public is_assignable<typename std::add_lvalue_reference<T>::type,
-                           typename std::add_lvalue_reference<
-                               typename std::add_const<T>::type>::type> {};
-
-// is_move_assignable is true if a T&& is assignable to a T&.
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace.
-template <class T>
-struct is_move_assignable
-    : public is_assignable<typename std::add_lvalue_reference<T>::type,
-                           const typename std::add_rvalue_reference<T>::type> {
-};
-
-// underlying_type produces the integer type backing an enum type.
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace.
-#if defined(CR_USE_FALLBACKS_FOR_OLD_GLIBCXX)
-template <typename T>
-struct underlying_type {
-  using type = __underlying_type(T);
-};
-#else
-template <typename T>
-using underlying_type = std::underlying_type<T>;
-#endif
-
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace.
-#if defined(CR_USE_FALLBACKS_FOR_OLD_GLIBCXX)
-template <class T>
-using is_trivially_destructible = std::has_trivial_destructor<T>;
-#else
-template <class T>
-using is_trivially_destructible = std::is_trivially_destructible<T>;
-#endif
-
 // is_trivially_copyable is especially hard to get right.
 // - Older versions of libstdc++ will fail to have it like they do for other
-//   type traits. In this case we should provide it based on compiler
-//   intrinsics. This is covered by the CR_USE_FALLBACKS_FOR_OLD_GLIBCXX define.
+//   type traits. This has become a subset of the second point, but used to be
+//   handled independently.
 // - An experimental release of gcc includes most of type_traits but misses
 //   is_trivially_copyable, so we still have to avoid using libstdc++ in this
 //   case, which is covered by CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX.
@@ -175,8 +109,7 @@
 
 // TODO(crbug.com/554293): Remove this when all platforms have this in the std
 // namespace and it works with gcc as needed.
-#if defined(CR_USE_FALLBACKS_FOR_OLD_GLIBCXX) ||              \
-    defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \
+#if defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \
     defined(CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX)
 template <typename T>
 struct is_trivially_copyable {
@@ -186,7 +119,8 @@
 #if _GNUC_VER >= 501
   static constexpr bool value = __is_trivially_copyable(T);
 #else
-  static constexpr bool value = __has_trivial_copy(T);
+  static constexpr bool value =
+      __has_trivial_copy(T) && __has_trivial_destructor(T);
 #endif
 };
 #else
@@ -194,10 +128,26 @@
 using is_trivially_copyable = std::is_trivially_copyable<T>;
 #endif
 
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 7
+// Workaround for g++7 and earlier family.
+// Due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654, without this
+// Optional<std::vector<T>> where T is non-copyable causes a compile error.
+// As we know it is not trivially copy constructible, explicitly declare so.
+template <typename T>
+struct is_trivially_copy_constructible
+    : std::is_trivially_copy_constructible<T> {};
+
+template <typename... T>
+struct is_trivially_copy_constructible<std::vector<T...>> : std::false_type {};
+#else
+// Otherwise use std::is_trivially_copy_constructible as is.
+template <typename T>
+using is_trivially_copy_constructible = std::is_trivially_copy_constructible<T>;
+#endif
+
 }  // namespace base
 
-#undef CR_USE_FALLBACKS_FOR_OLD_GLIBCXX
 #undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
 #undef CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
 
-#endif  // BASE_TEMPLATE_UTIL_H_
+#endif  // THIRD_PARTY_BASE_TEMPLATE_UTIL_H_
diff --git a/third_party/base/test/scoped_locale.cc b/third_party/base/test/scoped_locale.cc
new file mode 100644
index 0000000..2643dd9
--- /dev/null
+++ b/third_party/base/test/scoped_locale.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/test/scoped_locale.h"
+
+#include <locale.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace pdfium {
+namespace base {
+
+ScopedLocale::ScopedLocale(const std::string& locale) {
+  prev_locale_ = setlocale(LC_ALL, nullptr);
+  EXPECT_TRUE(setlocale(LC_ALL, locale.c_str()) != nullptr)
+      << "Failed to set locale: " << locale;
+}
+
+ScopedLocale::~ScopedLocale() {
+  EXPECT_STREQ(prev_locale_.c_str(), setlocale(LC_ALL, prev_locale_.c_str()));
+}
+
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/test/scoped_locale.h b/third_party/base/test/scoped_locale.h
new file mode 100644
index 0000000..ef6d6ea
--- /dev/null
+++ b/third_party/base/test/scoped_locale.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_TEST_SCOPED_LOCALE_H_
+#define THIRD_PARTY_BASE_TEST_SCOPED_LOCALE_H_
+
+#include <string>
+
+namespace pdfium {
+namespace base {
+
+// Sets the given |locale| on construction, and restores the previous locale
+// on destruction.
+class ScopedLocale {
+ public:
+  explicit ScopedLocale(const std::string& locale);
+  ~ScopedLocale();
+
+ private:
+  std::string prev_locale_;
+
+  ScopedLocale(const ScopedLocale&) = delete;
+  ScopedLocale& operator=(const ScopedLocale&) = delete;
+};
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_TEST_SCOPED_LOCALE_H_
diff --git a/third_party/base/win/win_util.cc b/third_party/base/win/win_util.cc
new file mode 100644
index 0000000..ae2dba8
--- /dev/null
+++ b/third_party/base/win/win_util.cc
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/base/win/win_util.h"
+
+#include <windows.h>
+#include <processthreadsapi.h>
+
+namespace pdfium {
+namespace base {
+namespace win {
+
+bool IsUser32AndGdi32Available() {
+  static auto is_user32_and_gdi32_available = []() {
+    // If win32k syscalls aren't disabled, then user32 and gdi32 are available.
+
+    typedef decltype(
+        GetProcessMitigationPolicy)* GetProcessMitigationPolicyType;
+    GetProcessMitigationPolicyType get_process_mitigation_policy_func =
+        reinterpret_cast<GetProcessMitigationPolicyType>(GetProcAddress(
+            GetModuleHandle(L"kernel32.dll"), "GetProcessMitigationPolicy"));
+
+    if (!get_process_mitigation_policy_func)
+      return true;
+
+    PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {};
+    if (get_process_mitigation_policy_func(GetCurrentProcess(),
+                                           ProcessSystemCallDisablePolicy,
+                                           &policy, sizeof(policy))) {
+      return policy.DisallowWin32kSystemCalls == 0;
+    }
+
+    return true;
+  }();
+  return is_user32_and_gdi32_available;
+}
+
+}  // namespace win
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/win/win_util.h b/third_party/base/win/win_util.h
new file mode 100644
index 0000000..b27f2f1
--- /dev/null
+++ b/third_party/base/win/win_util.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BASE_WIN_WIN_UTIL_H_
+#define THIRD_PARTY_BASE_WIN_WIN_UTIL_H_
+
+namespace pdfium {
+namespace base {
+namespace win {
+
+// Returns true if the current process can make USER32 or GDI32 calls such as
+// CreateWindow and CreateDC. Windows 8 and above allow the kernel component
+// of these calls to be disabled which can cause undefined behaviour such as
+// crashes. This function can be used to guard areas of code using these calls
+// and provide a fallback path if necessary.
+bool IsUser32AndGdi32Available();
+
+}  // namespace win
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_WIN_WIN_UTIL_H_
diff --git a/third_party/bigint/BigInteger.cc b/third_party/bigint/BigInteger.cc
index 93b57e4..476f3e6 100644
--- a/third_party/bigint/BigInteger.cc
+++ b/third_party/bigint/BigInteger.cc
@@ -6,14 +6,15 @@
 
 #include "BigInteger.hh"
 
-void BigInteger::operator =(const BigInteger &x) {
+BigInteger& BigInteger::operator =(const BigInteger &x) {
 	// Calls like a = a have no effect
 	if (this == &x)
-		return;
+		return *this;
 	// Copy sign
 	sign = x.sign;
 	// Copy the rest
 	mag = x.mag;
+	return *this;
 }
 
 BigInteger::BigInteger(const Blk *b, Index blen, Sign s) : mag(b, blen) {
@@ -374,7 +375,7 @@
 // INCREMENT/DECREMENT OPERATORS
 
 // Prefix increment
-void BigInteger::operator ++() {
+BigInteger& BigInteger::operator ++() {
 	if (sign == negative) {
 		mag--;
 		if (mag == 0)
@@ -383,15 +384,18 @@
 		mag++;
 		sign = positive; // if not already
 	}
+	return *this;
 }
 
-// Postfix increment: same as prefix
-void BigInteger::operator ++(int) {
+// Postfix increment
+BigInteger BigInteger::operator ++(int) {
+	BigInteger temp(*this);
 	operator ++();
+	return temp;
 }
 
 // Prefix decrement
-void BigInteger::operator --() {
+BigInteger& BigInteger::operator --() {
 	if (sign == positive) {
 		mag--;
 		if (mag == 0)
@@ -400,10 +404,13 @@
 		mag++;
 		sign = negative;
 	}
+	return *this;
 }
 
-// Postfix decrement: same as prefix
-void BigInteger::operator --(int) {
+// Postfix decrement
+BigInteger BigInteger::operator --(int) {
+	BigInteger temp(*this);
 	operator --();
+	return temp;
 }
 
diff --git a/third_party/bigint/BigInteger.hh b/third_party/bigint/BigInteger.hh
index 320a22f..b5d3518 100644
--- a/third_party/bigint/BigInteger.hh
+++ b/third_party/bigint/BigInteger.hh
@@ -38,10 +38,10 @@
 	BigInteger() : sign(zero), mag() {}
 
 	// Copy constructor
-	BigInteger(const BigInteger &x) : sign(x.sign), mag(x.mag) {};
+	BigInteger(const BigInteger &x) : sign(x.sign), mag(x.mag) {}
 
 	// Assignment operator
-	void operator=(const BigInteger &x);
+	BigInteger& operator=(const BigInteger &x);
 
 	// Constructor that copies from a given array of blocks with a sign.
 	BigInteger(const Blk *b, Index blen, Sign s);
@@ -103,7 +103,7 @@
 	bool operator ==(const BigInteger &x) const {
 		return sign == x.sign && mag == x.mag;
 	}
-	bool operator !=(const BigInteger &x) const { return !operator ==(x); };
+	bool operator !=(const BigInteger &x) const { return !operator ==(x); }
 	bool operator < (const BigInteger &x) const { return compareTo(x) == less   ; }
 	bool operator <=(const BigInteger &x) const { return compareTo(x) != greater; }
 	bool operator >=(const BigInteger &x) const { return compareTo(x) != less   ; }
@@ -129,18 +129,18 @@
 	BigInteger operator %(const BigInteger &x) const;
 	BigInteger operator -() const;
 
-	void operator +=(const BigInteger &x);
-	void operator -=(const BigInteger &x);
-	void operator *=(const BigInteger &x);
-	void operator /=(const BigInteger &x);
-	void operator %=(const BigInteger &x);
+	BigInteger& operator +=(const BigInteger &x);
+	BigInteger& operator -=(const BigInteger &x);
+	BigInteger& operator *=(const BigInteger &x);
+	BigInteger& operator /=(const BigInteger &x);
+	BigInteger& operator %=(const BigInteger &x);
 	void flipSign();
 
 	// INCREMENT/DECREMENT OPERATORS
-	void operator ++(   );
-	void operator ++(int);
-	void operator --(   );
-	void operator --(int);
+	BigInteger& operator ++(   );
+	BigInteger operator ++(int);
+	BigInteger& operator --(   );
+	BigInteger operator --(int);
 };
 
 // NORMAL OPERATORS
@@ -191,16 +191,19 @@
  * belongs to the put-here operations.  See Assignment Operators in
  * BigUnsigned.hh.
  */
-inline void BigInteger::operator +=(const BigInteger &x) {
+inline BigInteger& BigInteger::operator +=(const BigInteger &x) {
 	add(*this, x);
+	return *this;
 }
-inline void BigInteger::operator -=(const BigInteger &x) {
+inline BigInteger& BigInteger::operator -=(const BigInteger &x) {
 	subtract(*this, x);
+	return *this;
 }
-inline void BigInteger::operator *=(const BigInteger &x) {
+inline BigInteger& BigInteger::operator *=(const BigInteger &x) {
 	multiply(*this, x);
+	return *this;
 }
-inline void BigInteger::operator /=(const BigInteger &x) {
+inline BigInteger& BigInteger::operator /=(const BigInteger &x) {
 	if (x.isZero())
         abort();
 	/* The following technique is slightly faster than copying *this first
@@ -209,13 +212,15 @@
 	divideWithRemainder(x, q);
 	// *this contains the remainder, but we overwrite it with the quotient.
 	*this = q;
+	return *this;
 }
-inline void BigInteger::operator %=(const BigInteger &x) {
+inline BigInteger& BigInteger::operator %=(const BigInteger &x) {
 	if (x.isZero())
         abort();
 	BigInteger q;
 	// Mods *this by x.  Don't care about quotient left in q.
 	divideWithRemainder(x, q);
+	return *this;
 }
 // This one is trivial
 inline void BigInteger::flipSign() {
diff --git a/third_party/bigint/BigUnsigned.cc b/third_party/bigint/BigUnsigned.cc
index e38e4aa..0713632 100644
--- a/third_party/bigint/BigUnsigned.cc
+++ b/third_party/bigint/BigUnsigned.cc
@@ -659,7 +659,7 @@
 // INCREMENT/DECREMENT OPERATORS
 
 // Prefix increment
-void BigUnsigned::operator ++() {
+BigUnsigned& BigUnsigned::operator ++() {
 	Index i;
 	bool carry = true;
 	for (i = 0; i < len && carry; i++) {
@@ -672,15 +672,18 @@
 		len++;
 		blk[i] = 1;
 	}
+	return *this;
 }
 
-// Postfix increment: same as prefix
-void BigUnsigned::operator ++(int) {
+// Postfix increment
+BigUnsigned BigUnsigned::operator ++(int) {
+	BigUnsigned temp(*this);
 	operator ++();
+	return temp;
 }
 
 // Prefix decrement
-void BigUnsigned::operator --() {
+BigUnsigned& BigUnsigned::operator --() {
 	if (len == 0)
         abort();
 	Index i;
@@ -692,9 +695,12 @@
 	// Zap possible leading zero (there can only be one)
 	if (blk[len - 1] == 0)
 		len--;
+	return *this;
 }
 
-// Postfix decrement: same as prefix
-void BigUnsigned::operator --(int) {
+// Postfix decrement
+BigUnsigned BigUnsigned::operator --(int) {
+	BigUnsigned temp(*this);
 	operator --();
+	return temp;
 }
diff --git a/third_party/bigint/BigUnsigned.hh b/third_party/bigint/BigUnsigned.hh
index de4c18e..56da651 100644
--- a/third_party/bigint/BigUnsigned.hh
+++ b/third_party/bigint/BigUnsigned.hh
@@ -46,8 +46,9 @@
 	BigUnsigned(const BigUnsigned &x) : NumberlikeArray<Blk>(x) {}
 
 	// Assignment operator
-	void operator=(const BigUnsigned &x) {
+	BigUnsigned& operator=(const BigUnsigned &x) {
 		NumberlikeArray<Blk>::operator =(x);
+		return *this;
 	}
 
 	// Constructor that copies from a given array of blocks.
@@ -218,24 +219,24 @@
 	BigUnsigned operator >>(int b) const;
 
 	// OVERLOADED ASSIGNMENT OPERATORS
-	void operator +=(const BigUnsigned &x);
-	void operator -=(const BigUnsigned &x);
-	void operator *=(const BigUnsigned &x);
-	void operator /=(const BigUnsigned &x);
-	void operator %=(const BigUnsigned &x);
-	void operator &=(const BigUnsigned &x);
-	void operator |=(const BigUnsigned &x);
-	void operator ^=(const BigUnsigned &x);
-	void operator <<=(int b);
-	void operator >>=(int b);
+	BigUnsigned& operator +=(const BigUnsigned &x);
+	BigUnsigned& operator -=(const BigUnsigned &x);
+	BigUnsigned& operator *=(const BigUnsigned &x);
+	BigUnsigned& operator /=(const BigUnsigned &x);
+	BigUnsigned& operator %=(const BigUnsigned &x);
+	BigUnsigned& operator &=(const BigUnsigned &x);
+	BigUnsigned& operator |=(const BigUnsigned &x);
+	BigUnsigned& operator ^=(const BigUnsigned &x);
+	BigUnsigned& operator <<=(int b);
+	BigUnsigned& operator >>=(int b);
 
 	/* INCREMENT/DECREMENT OPERATORS
 	 * To discourage messy coding, these do not return *this, so prefix
 	 * and postfix behave the same. */
-	void operator ++(   );
-	void operator ++(int);
-	void operator --(   );
-	void operator --(int);
+	BigUnsigned& operator ++(   );
+	BigUnsigned operator ++(int);
+	BigUnsigned& operator --(   );
+	BigUnsigned operator --(int);
 
 	// Helper function that needs access to BigUnsigned internals
 	friend Blk getShiftedBlock(const BigUnsigned &num, Index x,
@@ -307,16 +308,19 @@
 	return ans;
 }
 
-inline void BigUnsigned::operator +=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator +=(const BigUnsigned &x) {
 	add(*this, x);
+	return *this;
 }
-inline void BigUnsigned::operator -=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator -=(const BigUnsigned &x) {
 	subtract(*this, x);
+	return *this;
 }
-inline void BigUnsigned::operator *=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator *=(const BigUnsigned &x) {
 	multiply(*this, x);
+	return *this;
 }
-inline void BigUnsigned::operator /=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator /=(const BigUnsigned &x) {
 	if (x.isZero())
         abort();
 	/* The following technique is slightly faster than copying *this first
@@ -325,28 +329,35 @@
 	divideWithRemainder(x, q);
 	// *this contains the remainder, but we overwrite it with the quotient.
 	*this = q;
+	return *this;
 }
-inline void BigUnsigned::operator %=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator %=(const BigUnsigned &x) {
 	if (x.isZero())
         abort();
 	BigUnsigned q;
 	// Mods *this by x.  Don't care about quotient left in q.
 	divideWithRemainder(x, q);
+	return *this;
 }
-inline void BigUnsigned::operator &=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator &=(const BigUnsigned &x) {
 	bitAnd(*this, x);
+	return *this;
 }
-inline void BigUnsigned::operator |=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator |=(const BigUnsigned &x) {
 	bitOr(*this, x);
+	return *this;
 }
-inline void BigUnsigned::operator ^=(const BigUnsigned &x) {
+inline BigUnsigned& BigUnsigned::operator ^=(const BigUnsigned &x) {
 	bitXor(*this, x);
+	return *this;
 }
-inline void BigUnsigned::operator <<=(int b) {
+inline BigUnsigned& BigUnsigned::operator <<=(int b) {
 	bitShiftLeft(*this, b);
+	return *this;
 }
-inline void BigUnsigned::operator >>=(int b) {
+inline BigUnsigned& BigUnsigned::operator >>=(int b) {
 	bitShiftRight(*this, b);
+	return *this;
 }
 
 /* Templates for conversions of BigUnsigned to and from primitive integers.
diff --git a/third_party/bigint/BigUnsignedInABase.hh b/third_party/bigint/BigUnsignedInABase.hh
index dbd2cf9..fb293ba 100644
--- a/third_party/bigint/BigUnsignedInABase.hh
+++ b/third_party/bigint/BigUnsignedInABase.hh
@@ -64,9 +64,10 @@
 	BigUnsignedInABase(const BigUnsignedInABase &x) : NumberlikeArray<Digit>(x), base(x.base) {}
 
 	// Assignment operator
-	void operator =(const BigUnsignedInABase &x) {
+	BigUnsignedInABase& operator =(const BigUnsignedInABase &x) {
 		NumberlikeArray<Digit>::operator =(x);
 		base = x.base;
+		return *this;
 	}
 
 	// Constructor that copies from a given array of digits.
diff --git a/third_party/bigint/NumberlikeArray.hh b/third_party/bigint/NumberlikeArray.hh
index f791780..df58dae 100644
--- a/third_party/bigint/NumberlikeArray.hh
+++ b/third_party/bigint/NumberlikeArray.hh
@@ -70,7 +70,7 @@
 	NumberlikeArray(const NumberlikeArray<Blk> &x);
 
 	// Assignment operator
-	void operator=(const NumberlikeArray<Blk> &x);
+	NumberlikeArray<Blk>& operator=(const NumberlikeArray<Blk> &x);
 
 	// Constructor that copies from a given array of blocks
 	NumberlikeArray(const Blk *b, Index blen);
@@ -139,11 +139,11 @@
 }
 
 template <class Blk>
-void NumberlikeArray<Blk>::operator=(const NumberlikeArray<Blk> &x) {
+NumberlikeArray<Blk>& NumberlikeArray<Blk>::operator=(const NumberlikeArray<Blk> &x) {
 	/* Calls like a = a have no effect; catch them before the aliasing
 	 * causes a problem */
 	if (this == &x)
-		return;
+		return *this;
 	// Copy length
 	len = x.len;
 	// Expand array if necessary
@@ -152,6 +152,7 @@
 	Index i;
 	for (i = 0; i < len; i++)
 		blk[i] = x.blk[i];
+	return *this;
 }
 
 template <class Blk>
diff --git a/third_party/build/build_config.h b/third_party/build/build_config.h
deleted file mode 100644
index d4c66e0..0000000
--- a/third_party/build/build_config.h
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file adds defines about the platform we're currently building on.
-//  Operating System:
-//    OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) /
-//    OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI
-//    OS_CHROMEOS is set by the build system
-//  Compiler:
-//    COMPILER_MSVC / COMPILER_GCC
-//  Processor:
-//    ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64)
-//    ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
-
-#ifndef BUILD_BUILD_CONFIG_H_
-#define BUILD_BUILD_CONFIG_H_
-
-// 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
-// OS_NACL comes in two sandboxing technology flavors, SFI or Non-SFI.
-// PNaCl toolchain defines __native_client_nonsfi__ macro in Non-SFI build
-// mode, while it does not in SFI build mode.
-#if defined(__native_client_nonsfi__)
-#define OS_NACL_NONSFI
-#else
-#define OS_NACL_SFI
-#endif
-#elif defined(ANDROID)
-#define OS_ANDROID 1
-#elif defined(__APPLE__)
-// only include TargetConditions after testing ANDROID as some android builds
-// on mac don't have this header available and it's not needed unless the target
-// is really mac/ios.
-#include <TargetConditionals.h>
-#define OS_MACOSX 1
-#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
-#define OS_IOS 1
-#endif  // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
-#elif defined(__linux__)
-#define OS_LINUX 1
-// include a system header to pull in features.h for glibc/uclibc macros.
-#include <unistd.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(__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
-#else
-#error Please add support for your platform in build/build_config.h
-#endif
-
-#if defined(USE_OPENSSL_CERTS) && defined(USE_NSS_CERTS)
-#error Cannot use both OpenSSL and NSS for certificates
-#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_MACOSX) || defined(OS_LINUX) || defined(OS_FREEBSD) ||    \
-    defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_SOLARIS) ||  \
-    defined(OS_ANDROID) || defined(OS_OPENBSD) || defined(OS_SOLARIS) || \
-    defined(OS_ANDROID) || defined(OS_NACL) || defined(OS_QNX)
-#define OS_POSIX 1
-#endif
-
-// Use tcmalloc
-#if (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)) && \
-    !defined(NO_TCMALLOC)
-#define USE_TCMALLOC 1
-#endif
-
-// Compiler detection.
-#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(__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__) && defined(__LITTLE_ENDIAN__)
-#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(__PPC__)
-#define ARCH_CPU_PPC_FAMILY 1
-#define ARCH_CPU_PPC 1
-#define ARCH_CPU_32_BITS 1
-#define ARCH_CPU_BIG_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__)
-#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__)
-#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
-#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_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 base::string16::const_iterator and "char16*" are
-// equivalent types.
-#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER
-#endif
-
-#endif  // BUILD_BUILD_CONFIG_H_
diff --git a/third_party/freetype/README.pdfium b/third_party/freetype/README.pdfium
index 3e9aa69..56c5aa5 100644
--- a/third_party/freetype/README.pdfium
+++ b/third_party/freetype/README.pdfium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-8-1-145
-Revision: 2c048a8a622e9f44f255aa3316026f124ac9ecbc
+Version: VER-2-10-1-97
+Revision: e5038be70414cf66da6c4d5ce4e30375884c30d8
 Security Critical: yes
 License: FreeType License (FTL)
 License File: FTL.TXT
diff --git a/third_party/freetype/include/freetype-custom-config/ftoption.h b/third_party/freetype/include/freetype-custom-config/ftoption.h
index b986473..d3f43a0 100644
--- a/third_party/freetype/include/freetype-custom-config/ftoption.h
+++ b/third_party/freetype/include/freetype-custom-config/ftoption.h
@@ -512,33 +512,17 @@
 #undef FT_CONFIG_OPTION_USE_MODULE_ERRORS
 
 
-  /*************************************************************************/
-  /*                                                                       */
-  /* Position Independent Code                                             */
-  /*                                                                       */
-  /*   If this macro is set (which is _not_ the default), FreeType2 will   */
-  /*   avoid creating constants that require address fixups.  Instead the  */
-  /*   constants will be moved into a struct and additional intialization  */
-  /*   code will be used.                                                  */
-  /*                                                                       */
-  /*   Setting this macro is needed for systems that prohibit address      */
-  /*   fixups, such as BREW.  [Note that standard compilers like gcc or    */
-  /*   clang handle PIC generation automatically; you don't have to set    */
-  /*   FT_CONFIG_OPTION_PIC, which is only necessary for very special      */
-  /*   compilers.]                                                         */
-  /*                                                                       */
-  /*   Note that FT_CONFIG_OPTION_PIC support is not available for all     */
-  /*   modules (see `modules.cfg' for a complete list).  For building with */
-  /*   FT_CONFIG_OPTION_PIC support, do the following.                     */
-  /*                                                                       */
-  /*     0. Clone the repository.                                          */
-  /*     1. Define FT_CONFIG_OPTION_PIC.                                   */
-  /*     2. Remove all subdirectories in `src' that don't have             */
-  /*        FT_CONFIG_OPTION_PIC support.                                  */
-  /*     3. Comment out the corresponding modules in `modules.cfg'.        */
-  /*     4. Compile.                                                       */
-  /*                                                                       */
-/* #define FT_CONFIG_OPTION_PIC */
+  /**************************************************************************
+   *
+   * Error Strings
+   *
+   *   If this macro is set, `FT_Error_String` will return meaningful
+   *   descriptions.  This is not enabled by default to reduce the overall
+   *   size of FreeType.
+   *
+   *   More details can be found in the file `fterrors.h`.
+   */
+/* #define FT_CONFIG_OPTION_ERROR_STRINGS */
 
 
   /*************************************************************************/
diff --git a/third_party/freetype/include/pstables.h b/third_party/freetype/include/pstables.h
index 2a2b717..c215f16 100644
--- a/third_party/freetype/include/pstables.h
+++ b/third_party/freetype/include/pstables.h
@@ -1,19 +1,19 @@
-/***************************************************************************/
-/*                                                                         */
-/*  pstables.h                                                             */
-/*                                                                         */
-/*    PostScript glyph names.                                              */
-/*                                                                         */
-/*  Copyright 2005-2017 by                                                 */
-/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
-/*                                                                         */
-/*  This file is part of the FreeType project, and may only be used,       */
-/*  modified, and distributed under the terms of the FreeType project      */
-/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
-/*  this file you indicate that you have read the license and              */
-/*  understand and accept it fully.                                        */
-/*                                                                         */
-/***************************************************************************/
+/****************************************************************************
+ *
+ * pstables.h
+ *
+ *   PostScript glyph names.
+ *
+ * Copyright (C) 2005-2020 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
 
 
   /* This file has been generated automatically -- do not edit! */
@@ -609,12 +609,12 @@
 
 
   /*
-   *  This table is a compressed version of the Adobe Glyph List (AGL),
-   *  optimized for efficient searching.  It has been generated by the
-   *  `glnames.py' python script located in the `src/tools' directory.
+   * This table is a compressed version of the Adobe Glyph List (AGL),
+   * optimized for efficient searching.  It has been generated by the
+   * `glnames.py' python script located in the `src/tools' directory.
    *
-   *  The lookup function to get the Unicode value for a given string
-   *  is defined below the table.
+   * The lookup function to get the Unicode value for a given string
+   * is defined below the table.
    */
 
 #ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST
@@ -4137,7 +4137,7 @@
 
 #ifdef  DEFINE_PS_TABLES
   /*
-   *  This function searches the compressed table efficiently.
+   * This function searches the compressed table efficiently.
    */
   static unsigned long
   ft_get_adobe_glyph_index( const char*  name,
diff --git a/third_party/freetype/roll-freetype.sh b/third_party/freetype/roll-freetype.sh
index 0930a64..fd2c293 100755
--- a/third_party/freetype/roll-freetype.sh
+++ b/third_party/freetype/roll-freetype.sh
@@ -9,12 +9,13 @@
 set -u
 
 REVIEWERS=`paste -s -d, third_party/freetype/OWNERS`
-roll-dep -r "${REVIEWERS}" "$@" pdfium/third_party/freetype/src/
+roll-dep -r "${REVIEWERS}" "$@" third_party/freetype/src/
 FTVERSION=`git -C third_party/freetype/src/ describe --long`
 FTCOMMIT=`git -C third_party/freetype/src/ rev-parse HEAD`
 
 # Make sure our copy of pstables.h matches the one in freetype/src.
-cmp third_party/freetype/src/src/psnames/pstables.h \
+# May need to --bypass-hooks to prevent formatting of this file.
+cp third_party/freetype/src/src/psnames/pstables.h \
   third_party/freetype/include/pstables.h
 
 sed -i "s/^Version: .*\$/Version: ${FTVERSION%-*}/" \
@@ -23,4 +24,5 @@
   third_party/freetype/README.pdfium
 
 git add third_party/freetype/README.pdfium
+git add third_party/freetype/include/pstables.h
 git commit --quiet --amend --no-edit
diff --git a/third_party/googletest/BUILD.gn b/third_party/googletest/BUILD.gn
new file mode 100644
index 0000000..dbfc334
--- /dev/null
+++ b/third_party/googletest/BUILD.gn
@@ -0,0 +1,137 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("gtest_config") {
+  visibility = [ ":*" ]  # gmock also shares this config.
+
+  defines = [
+    # Chromium always links googletest statically, so no API qualifier is
+    # necessary. The definition in gtest-port.h at the time of this writing
+    # causes crashes in content_browsertests.
+    "GTEST_API_=",
+
+    # In order to allow regex matches in gtest to be shared between Windows
+    # and other systems, we tell gtest to always use its internal engine.
+    "GTEST_HAS_POSIX_RE=0",
+
+    # Enables C++11 features.
+    "GTEST_LANG_CXX11=1",
+
+    # Prevents gtest from including both <tr1/tuple> and <tuple>.
+    "GTEST_HAS_TR1_TUPLE=0",
+  ]
+
+  # Gtest headers need to be able to find themselves.
+  include_dirs = [ "src/googletest/include" ]
+
+  if (is_win) {
+    cflags = [ "/wd4800" ]  # Unused variable warning.
+  }
+}
+
+config("gmock_config") {
+  # Gmock headers need to be able to find themselves.
+  include_dirs = [
+    "custom",
+    "src/googlemock/include",
+  ]
+}
+
+# Do NOT depend on this directly. Use //testing/gtest instead.
+# See README.chromium for details.
+source_set("gtest") {
+  testonly = true
+  sources = [
+    "src/googletest/include/gtest/gtest-death-test.h",
+    "src/googletest/include/gtest/gtest-matchers.h",
+    "src/googletest/include/gtest/gtest-message.h",
+    "src/googletest/include/gtest/gtest-param-test.h",
+    "src/googletest/include/gtest/gtest-printers.h",
+    "src/googletest/include/gtest/gtest-spi.h",
+    "src/googletest/include/gtest/gtest-test-part.h",
+    "src/googletest/include/gtest/gtest-typed-test.h",
+    "src/googletest/include/gtest/gtest.h",
+    "src/googletest/include/gtest/gtest_pred_impl.h",
+    "src/googletest/include/gtest/internal/gtest-death-test-internal.h",
+    "src/googletest/include/gtest/internal/gtest-filepath.h",
+    "src/googletest/include/gtest/internal/gtest-internal.h",
+    "src/googletest/include/gtest/internal/gtest-linked_ptr.h",
+    "src/googletest/include/gtest/internal/gtest-param-util-generated.h",
+    "src/googletest/include/gtest/internal/gtest-param-util.h",
+    "src/googletest/include/gtest/internal/gtest-port.h",
+    "src/googletest/include/gtest/internal/gtest-string.h",
+    "src/googletest/include/gtest/internal/gtest-tuple.h",
+    "src/googletest/include/gtest/internal/gtest-type-util.h",
+
+    #"src/googletest/src/gtest-all.cc",  # Not needed by our build.
+    "src/googletest/src/gtest-death-test.cc",
+    "src/googletest/src/gtest-filepath.cc",
+    "src/googletest/src/gtest-internal-inl.h",
+    "src/googletest/src/gtest-matchers.cc",
+    "src/googletest/src/gtest-port.cc",
+    "src/googletest/src/gtest-printers.cc",
+    "src/googletest/src/gtest-test-part.cc",
+    "src/googletest/src/gtest-typed-test.cc",
+    "src/googletest/src/gtest.cc",
+  ]
+
+  # Some files include "src/gtest-internal-inl.h".
+  include_dirs = [ "src/googletest" ]
+
+  all_dependent_configs = [ ":gtest_config" ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+}
+
+# Do NOT depend on this directly. Use //testing/gtest:gtest_main instead.
+# See README.chromium for details.
+source_set("gtest_main") {
+  testonly = true
+  sources = [ "src/googletest/src/gtest_main.cc" ]
+  deps = [ ":gtest" ]
+}
+
+# Do NOT depend on this directly. Use //testing/gmock:gmock_main instead.
+# See README.chromium for details.
+source_set("gmock") {
+  testonly = true
+  sources = [
+    "src/googlemock/include/gmock/gmock-actions.h",
+    "src/googlemock/include/gmock/gmock-cardinalities.h",
+    "src/googlemock/include/gmock/gmock-generated-actions.h",
+    "src/googlemock/include/gmock/gmock-generated-function-mockers.h",
+    "src/googlemock/include/gmock/gmock-generated-matchers.h",
+    "src/googlemock/include/gmock/gmock-generated-nice-strict.h",
+    "src/googlemock/include/gmock/gmock-matchers.h",
+    "src/googlemock/include/gmock/gmock-spec-builders.h",
+    "src/googlemock/include/gmock/gmock.h",
+    "src/googlemock/include/gmock/internal/gmock-generated-internal-utils.h",
+    "src/googlemock/include/gmock/internal/gmock-internal-utils.h",
+    "src/googlemock/include/gmock/internal/gmock-port.h",
+
+    # gmock helpers.
+    "custom/gmock/internal/custom/gmock-port.h",
+
+    #"src/googlemock/src/gmock-all.cc",  # Not needed by our build.
+    "src/googlemock/src/gmock-cardinalities.cc",
+    "src/googlemock/src/gmock-internal-utils.cc",
+    "src/googlemock/src/gmock-matchers.cc",
+    "src/googlemock/src/gmock-spec-builders.cc",
+    "src/googlemock/src/gmock.cc",
+  ]
+
+  public_configs = [
+    ":gmock_config",
+    ":gtest_config",
+  ]
+}
+
+# Do NOT depend on this directly. Use //testing/gmock:gmock_main instead.
+# See README.chromium for details.
+static_library("gmock_main") {
+  testonly = true
+  sources = [ "src/googlemock/src/gmock_main.cc" ]
+  deps = [ ":gmock" ]
+}
diff --git a/third_party/googletest/README.pdfium b/third_party/googletest/README.pdfium
new file mode 100644
index 0000000..bd2d36a
--- /dev/null
+++ b/third_party/googletest/README.pdfium
@@ -0,0 +1,18 @@
+Name: Google Test: Google's C++ Testing Framework
+Short Name: googletest
+URL: https://github.com/google/googletest.git
+Version: 1.8.0.git-a45c24ac1878932e0dc5fbc0d78a699befd386d3
+License: BSD
+License File: NOT_SHIPPED
+Security critical: no
+
+Google Test is imported as-is, to facilitate version bumping. However, the
+file/directory layout of Google Test is not yet considered stable. Therefore,
+until Google Test's layout stabilizes, PDFium code MUST NOT depend on it
+directly. Instead, PDFium code MUST:
+
+* #include the headers in testing/gtest and testing/gmock
+* use //testing/gtest(:gtest_main) and //testing/gmock(:gmock_main) in BUILD.gn
+  deps
+
+This will allow us to adapt to Google Test changes with minimal disruption.
diff --git a/third_party/lcms/0000-cmserr-changes.patch b/third_party/lcms/0000-cmserr-changes.patch
index baf3577..31f56fd 100644
--- a/third_party/lcms/0000-cmserr-changes.patch
+++ b/third_party/lcms/0000-cmserr-changes.patch
@@ -11,7 +11,7 @@
  
  // This function is here to help applications to prevent mixing lcms versions on header and shared objects.
  int CMSEXPORT cmsGetEncodedCMMversion(void)
-@@ -65,140 +67,75 @@ long int CMSEXPORT cmsfilelength(FILE* f)
+@@ -67,140 +67,75 @@ long int CMSEXPORT cmsfilelength(FILE* f)
      return n;
  }
  
@@ -36,7 +36,7 @@
 -// *********************************************************************************
 -
 -// This is the default memory allocation function. It does a very coarse
--// check of amout of memory, just to prevent exploits
+-// check of amount of memory, just to prevent exploits
 -static
 -void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size)
 +cmsBool  _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin)
@@ -174,7 +174,7 @@
      }
      else {
  
-         // To reset it, we use the default allocators, which cannot be overriden
+         // To reset it, we use the default allocators, which cannot be overridden
          ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager;
 -    } 
 +    }
@@ -184,7 +184,7 @@
  void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr)
  {
      if (Plugin == NULL) {
-@@ -212,94 +149,15 @@ void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkTy
+@@ -214,94 +149,15 @@ void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkTy
          ptr ->ReallocPtr = Plugin -> ReallocPtr;
  
          // Make sure we revert to defaults
diff --git a/third_party/lcms/0005-old-fix-e-with-tilde.patch b/third_party/lcms/0005-old-fix-e-with-tilde.patch
index 2fc054d..9a389bc 100644
--- a/third_party/lcms/0005-old-fix-e-with-tilde.patch
+++ b/third_party/lcms/0005-old-fix-e-with-tilde.patch
@@ -1,60 +1,60 @@
 diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c
-index 4a5a4f093..9c20c49b0 100644
+index 6b2950e4e..508117bd3 100644
 --- a/third_party/lcms/src/cmsxform.c
 +++ b/third_party/lcms/src/cmsxform.c
-@@ -338,7 +338,7 @@ void NullFloatXFORM(_cmsTRANSFORM* p,
+@@ -341,7 +341,7 @@ void NullFloatXFORM(_cmsTRANSFORM* p,
  
  // 16 bit precision -----------------------------------------------------------------------------------------------------------
  
--// Null transformation, only applies formatters. No cach<E9>
+-// Null transformation, only applies formatters. No caché
 +// Null transformation, only applies formatters. No cache
  static
  void NullXFORM(_cmsTRANSFORM* p,
                 const void* in,
-@@ -436,7 +436,7 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
+@@ -442,7 +442,7 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
          p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
  }
  
--// Gamut check, No cach<E9>, 16 bits.
+-// Gamut check, No caché, 16 bits.
 +// Gamut check, No cache, 16 bits.
  static
  void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
                                    const void* in,
-@@ -473,7 +473,7 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
+@@ -481,7 +481,7 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
  }
  
  
--// No gamut check, Cach<E9>, 16 bits,
+-// No gamut check, Caché, 16 bits,
 +// No gamut check, Cache, 16 bits,
  static
  void CachedXFORM(_cmsTRANSFORM* p,
                   const void* in,
-@@ -828,7 +828,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
+@@ -839,7 +839,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
              p ->xform = NullFloatXFORM;
          }
          else {
--            // Float transforms don't use cach<E9>, always are non-NULL
+-            // Float transforms don't use caché, always are non-NULL
 +            // Float transforms don't use cache, always are non-NULL
              p ->xform = FloatXFORM;
          }
  
-@@ -867,16 +867,16 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
+@@ -878,16 +878,16 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
              if (*dwFlags & cmsFLAGS_NOCACHE) {
  
                  if (*dwFlags & cmsFLAGS_GAMUTCHECK)
--                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cach<E9>
+-                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no caché
 +                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cache
                  else
--                    p ->xform = PrecalculatedXFORM;  // No cach<E9>, no gamut check
+-                    p ->xform = PrecalculatedXFORM;  // No caché, no gamut check
 +                    p ->xform = PrecalculatedXFORM;  // No cache, no gamut check
              }
              else {
  
                  if (*dwFlags & cmsFLAGS_GAMUTCHECK)
--                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, cach<E9>
+-                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, caché
 +                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, cache
                  else
--                    p ->xform = CachedXFORM;  // No gamut check, cach<E9>
+-                    p ->xform = CachedXFORM;  // No gamut check, caché
 +                    p ->xform = CachedXFORM;  // No gamut check, cache
  
              }
diff --git a/third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch b/third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch
index eabf6a9..26f5938 100644
--- a/third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch
+++ b/third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch
@@ -1,20 +1,17 @@
 diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c
-index 9b27ca0cc..9ed4cad39 100644
+index 42bd36530..9cfd2282f 100644
 --- a/third_party/lcms/src/cmsnamed.c
 +++ b/third_party/lcms/src/cmsnamed.c
-@@ -540,7 +540,10 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn
-     v ->ContextID  = ContextID;
+@@ -546,7 +546,7 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn
  
-     while (v -> Allocated < n){
--        if (!GrowNamedColorList(v)) return NULL;
-+        if (!GrowNamedColorList(v)) {
+     while (v -> Allocated < n) {
+         if (!GrowNamedColorList(v)) {
+-            _cmsFree(ContextID, (void*) v);
 +            cmsFreeNamedColorList(v);
-+            return NULL;
-+        }
+             return NULL;
+         }
      }
- 
-     strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);
-@@ -571,7 +574,10 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
+@@ -579,7 +579,10 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
  
      // For really large tables we need this
      while (NewNC ->Allocated < v ->Allocated){
diff --git a/third_party/lcms/0009-uninit.patch b/third_party/lcms/0009-uninit.patch
deleted file mode 100644
index e9538c1..0000000
--- a/third_party/lcms/0009-uninit.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-npm@npm0:~/pdfium/pdfium/third_party/lcms$ git diff src/cmstypes.c
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index cb618600a..2a0ea9448 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -2966,7 +2966,7 @@ void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER
- {
-     cmsUInt32Number i, Count;
-     cmsNAMEDCOLORLIST* List;
--    char Name[34];
-+    char Name[33];
-     cmsUInt16Number PCS[3];
- 
- 
-@@ -2981,7 +2981,7 @@ void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER
-     for (i=0; i < Count; i++) {
- 
-         if (io ->Read(io, Name, 32, 1) != 1) goto Error;
--        Name[33] = 0;
-+        Name[32] = 0;
- 
-         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
- 
diff --git a/third_party/lcms/0010-memory-leak-Type_Curve_Read.patch b/third_party/lcms/0010-memory-leak-Type_Curve_Read.patch
deleted file mode 100644
index 7edc7f9..0000000
--- a/third_party/lcms/0010-memory-leak-Type_Curve_Read.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 06742b5..9fe5e2a 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -1112,7 +1112,10 @@ void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm
-                NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL);
-                if (!NewGamma) return NULL;
- 
--               if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) return NULL;
-+               if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) {
-+                 cmsFreeToneCurve(NewGamma);
-+                 return NULL;
-+               }
- 
-                *nItems = 1;
-                return NewGamma;
-@@ -2350,7 +2353,10 @@ cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUI
- 
-         for (i=0; i < Data ->nEntries; i++) {
- 
--            if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) return NULL;
-+            if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) {
-+              cmsStageFree(CLUT);
-+              return NULL;
-+            }
-             Data ->Tab.T[i] = FROM_8_TO_16(v);
-         }
- 
diff --git a/third_party/lcms/0011-memory-leak-AllocEmptyTransform.patch b/third_party/lcms/0011-memory-leak-AllocEmptyTransform.patch
deleted file mode 100644
index 3325196..0000000
--- a/third_party/lcms/0011-memory-leak-AllocEmptyTransform.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c
-index 9c20c49b0..b3802f0d5 100644
---- a/third_party/lcms/src/cmsxform.c
-+++ b/third_party/lcms/src/cmsxform.c
-@@ -761,7 +761,10 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
- 
-        // Allocate needed memory
-        _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
--       if (!p) return NULL;
-+       if (!p) {
-+          cmsPipelineFree(lut);
-+          return NULL;
-+      }
- 
-        // Store the proposed pipeline
-        p->Lut = lut;
-@@ -819,7 +822,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
-         if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
- 
-             cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
--            _cmsFree(ContextID, p);
-+            cmsDeleteTransform(p);
-             return NULL;
-         }
- 
-@@ -849,7 +852,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
-             if (p ->FromInput == NULL || p ->ToOutput == NULL) {
- 
-                 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
--                _cmsFree(ContextID, p);
-+                cmsDeleteTransform(p);
-                 return NULL;
-             }
- 
diff --git a/third_party/lcms/0012-memory-leak-Type_NamedColor_Read.patch b/third_party/lcms/0012-memory-leak-Type_NamedColor_Read.patch
deleted file mode 100644
index ce9e1bf..0000000
--- a/third_party/lcms/0012-memory-leak-Type_NamedColor_Read.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 941107e1a..f2119ae0a 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -3105,7 +3105,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i
- 
-     if (nDeviceCoords > cmsMAXCHANNELS) {
-         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords);
--        return 0;
-+        goto Error;
-     }
-     for (i=0; i < count; i++) {
- 
-@@ -3114,7 +3114,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i
-         char Root[33];
- 
-         memset(Colorant, 0, sizeof(Colorant));
--        if (io -> Read(io, Root, 32, 1) != 1) return NULL;
-+        if (io -> Read(io, Root, 32, 1) != 1) goto Error;
-         Root[32] = 0;  // To prevent exploits
- 
-         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
diff --git a/third_party/lcms/0013-memory-leak-OptimizeByResampling.patch b/third_party/lcms/0013-memory-leak-OptimizeByResampling.patch
deleted file mode 100644
index 48645d1..0000000
--- a/third_party/lcms/0013-memory-leak-OptimizeByResampling.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
-index f885ef3..684910d 100644
---- a/third_party/lcms/src/cmsopt.c
-+++ b/third_party/lcms/src/cmsopt.c
-@@ -612,7 +612,7 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
- 
-     // Allocate the CLUT
-     CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL);
--    if (CLUT == NULL) return FALSE;
-+    if (CLUT == NULL) goto Error;
- 
-     // Add the CLUT to the destination LUT
-     if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) {
diff --git a/third_party/lcms/0014-memory-leak-Type_MPEmatrix_Read.patch b/third_party/lcms/0014-memory-leak-Type_MPEmatrix_Read.patch
deleted file mode 100644
index 8cc477f..0000000
--- a/third_party/lcms/0014-memory-leak-Type_MPEmatrix_Read.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 441d6bb..15199c7 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -4203,7 +4203,11 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io
- 
-         cmsFloat32Number v;
- 
--        if (!_cmsReadFloat32Number(io, &v)) return NULL;
-+        if (!_cmsReadFloat32Number(io, &v)) {
-+            _cmsFree(self ->ContextID, Matrix);
-+            _cmsFree(self ->ContextID, Offsets);
-+            return NULL;
-+        }
-         Matrix[i] = v;
-     }
- 
-@@ -4212,7 +4216,11 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io
- 
-         cmsFloat32Number v;
- 
--        if (!_cmsReadFloat32Number(io, &v)) return NULL;
-+        if (!_cmsReadFloat32Number(io, &v)) {
-+            _cmsFree(self ->ContextID, Matrix);
-+            _cmsFree(self ->ContextID, Offsets);
-+            return NULL;
-+        }
-         Offsets[i] = v;
-     }
- 
diff --git a/third_party/lcms/0015-cmsStageAllocMatrix-param-swap.patch b/third_party/lcms/0015-cmsStageAllocMatrix-param-swap.patch
deleted file mode 100644
index 04f8543..0000000
--- a/third_party/lcms/0015-cmsStageAllocMatrix-param-swap.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c
-index 73e6726..9b0eb4b 100644
---- a/third_party/lcms/src/cmslut.c
-+++ b/third_party/lcms/src/cmslut.c
-@@ -414,13 +414,13 @@ cmsStage*  CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number R
- 
-     if (Offset != NULL) {
- 
--        NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Cols, sizeof(cmsFloat64Number));
-+        NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number));
-         if (NewElem->Offset == NULL) {
-            MatrixElemTypeFree(NewMPE);
-            return NULL;
-         }
- 
--        for (i=0; i < Cols; i++) {
-+        for (i=0; i < Rows; i++) {
-                 NewElem ->Offset[i] = Offset[i];
-         }
- 
diff --git a/third_party/lcms/0016-reject-nan.patch b/third_party/lcms/0016-reject-nan.patch
deleted file mode 100644
index c2b5068..0000000
--- a/third_party/lcms/0016-reject-nan.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c
-index 79b145d2d..29ddd9a49 100644
---- a/third_party/lcms/src/cmsplugin.c
-+++ b/third_party/lcms/src/cmsplugin.c
-@@ -179,6 +179,8 @@ cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
- 
-         tmp = _cmsAdjustEndianess32(tmp);
-         *n = *(cmsFloat32Number*) (void*) &tmp;
-+        if (isnan(*n))
-+            return FALSE;
-     }
-     return TRUE;
- }
diff --git a/third_party/lcms/0017-memory-leak-ReadSegmentedCurve.patch b/third_party/lcms/0017-memory-leak-ReadSegmentedCurve.patch
deleted file mode 100644
index 472bcad..0000000
--- a/third_party/lcms/0017-memory-leak-ReadSegmentedCurve.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 15199c7..04dd0c4 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -3968,7 +3968,7 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND
-             case cmsSigSampledCurveSeg: {
-                 cmsUInt32Number Count;
- 
--                if (!_cmsReadUInt32Number(io, &Count)) return NULL;
-+                if (!_cmsReadUInt32Number(io, &Count)) goto Error;
- 
-                 Segments[i].nGridPoints = Count;
-                 Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number));
-@@ -3987,7 +3987,7 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND
-                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
-                 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String);
-                 }
--                return NULL;
-+                goto Error;
- 
-          }
-      }
-@@ -4001,7 +4001,12 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND
-      return Curve;
- 
- Error:
--     if (Segments) _cmsFree(self ->ContextID, Segments);
-+     if (Segments) {
-+         for (i=0; i < nSegments; i++) {
-+             if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
-+         }
-+         _cmsFree(self ->ContextID, Segments);
-+     }
-      return NULL;
- }
- 
diff --git a/third_party/lcms/0018-backport-c0a98d86.patch b/third_party/lcms/0018-backport-c0a98d86.patch
deleted file mode 100644
index 34b5477..0000000
--- a/third_party/lcms/0018-backport-c0a98d86.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c
-index 5d5f35d..14c6856 100644
---- a/third_party/lcms/src/cmsintrp.c
-+++ b/third_party/lcms/src/cmsintrp.c
-@@ -215,7 +215,7 @@ void LinLerp1D(register const cmsUInt16Number Value[],
- // To prevent out of bounds indexing
- cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) 
- {
--    return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v);
-+    return ((v < 0.0f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v);
- }
- 
- // Floating-point version of 1D interpolation
-diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
-index 5f9f08a..3ed730a 100644
---- a/third_party/lcms/src/cmsio0.c
-+++ b/third_party/lcms/src/cmsio0.c
-@@ -1475,6 +1475,17 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
-     // If the element is already in memory, return the pointer
-     if (Icc -> TagPtrs[n]) {
- 
-+        if (Icc->TagTypeHandlers[n] == NULL) goto Error;
-+
-+        // Sanity check
-+        BaseType = Icc->TagTypeHandlers[n]->Signature;
-+        if (BaseType == 0) goto Error;
-+
-+        TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig);
-+        if (TagDescriptor == NULL) goto Error;
-+
-+        if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
-+
-         if (Icc ->TagSaveAsRaw[n]) goto Error;  // We don't support read raw tags as cooked
- 
-         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 04dd0c4..386439b 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -4297,8 +4297,12 @@ void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
- 
-     // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number
-     nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans;
--    for (i=0; i < nMaxGrids; i++) GridPoints[i] = (cmsUInt32Number) Dimensions8[i];
- 
-+    for (i = 0; i < nMaxGrids; i++) {
-+        if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
-+        GridPoints[i] = (cmsUInt32Number)Dimensions8[i];
-+    }
-+
-     // Allocate the true CLUT
-     mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL);
-     if (mpe == NULL) goto Error;
diff --git a/third_party/lcms/0019-utf8.patch b/third_party/lcms/0019-utf8.patch
index 351659d..11293c9 100644
--- a/third_party/lcms/0019-utf8.patch
+++ b/third_party/lcms/0019-utf8.patch
@@ -1,95 +1,95 @@
 diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
-index 6011fb201..d53360b1f 100644
+index 7df5bbc4d..49cfbbe8c 100644
 --- a/third_party/lcms/src/cmscgats.c
 +++ b/third_party/lcms/src/cmscgats.c
-@@ -255,7 +255,7 @@ static PROPERTY PredefinedProperties[] = {
-                                // needed.
+@@ -256,7 +256,7 @@ static PROPERTY PredefinedProperties[] = {
+                                                    // needed.
  
-         {"SAMPLE_BACKING",   WRITE_STRINGIFY},   // Identifies the backing material used behind the sample during
--                               // measurement. Allowed values are <93>black<94>, <93>white<94>, or {"na".
-+                               // measurement. Allowed values are "black" "white" or "na".
- 
-         {"CHISQ_DOF",        WRITE_STRINGIFY},   // Degrees of freedom associated with the Chi squared statistic
- 
+         {"SAMPLE_BACKING",   WRITE_STRINGIFY},     // Identifies the backing material used behind the sample during
+-                                                   // measurement. Allowed values are ?black?, ?white?, or {"na".
++                                                   // measurement. Allowed values are "black", "white", or {"na".
+                                                   
+         {"CHISQ_DOF",        WRITE_STRINGIFY},     // Degrees of freedom associated with the Chi squared statistic
+                                                    // below properties are new in recent specs:
 @@ -271,7 +271,7 @@ static PROPERTY PredefinedProperties[] = {
-                                // denote the use of filters such as none, D65, Red, Green or Blue.
+                                                    // denote the use of filters such as none, D65, Red, Green or Blue.
+                                                   
+        {"POLARIZATION",      WRITE_STRINGIFY},     // Identifies the use of a physical polarization filter during measurement. Allowed
+-                                                   // values are {"yes?, ?white?, ?none? or ?na?.
++                                                   // values are "yes", "white", "none" or "na".
  
-        {"POLARIZATION",      WRITE_STRINGIFY},   // Identifies the use of a physical polarization filter during measurement. Allowed
--                               // values are {"yes<94>, <93>white<94>, <93>none<94> or <93>na<94>.
-+                               // values are "yes" "white" "none" or "na".
- 
-        {"WEIGHTING_FUNCTION", WRITE_PAIR},   // Indicates such functions as: the CIE standard observer functions used in the
-                                // calculation of various data parameters (2 degree and 10 degree), CIE standard
+        {"WEIGHTING_FUNCTION", WRITE_PAIR},         // Indicates such functions as: the CIE standard observer functions used in the
+                                                    // calculation of various data parameters (2 degree and 10 degree), CIE standard
 diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 5647264a6..64845e263 100644
+index 6cb8d4be3..e95e6d910 100644
 --- a/third_party/lcms/src/cmstypes.c
 +++ b/third_party/lcms/src/cmstypes.c
-@@ -962,7 +962,7 @@ cmsBool  Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIO
+@@ -974,7 +974,7 @@ cmsBool  Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIO
      len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
  
      // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data
--    //(see clause 4.1 for the definition of <93>aligned<94>). Because the Unicode language
-+    //(see clause 4.1 for the definition of 'aligned'. Because the Unicode language
+-    //(see clause 4.1 for the definition of ?aligned?). Because the Unicode language
++    //(see clause 4.1 for the definition of "aligned"). Because the Unicode language
      // code and Unicode count immediately follow the ASCII description, their
      // alignment is not correct if the ASCII count is not a multiple of four. The
      // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and
-@@ -3066,10 +3066,10 @@ void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr)
+@@ -3091,10 +3091,10 @@ void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr)
  //The namedColor2Type is a count value and array of structures that provide color
  //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
  //device representation of the color are given. Both representations are 16-bit values.
--//The device representation corresponds to the header<92>s <93>color space of data<94> field.
--//This representation should be consistent with the <93>number of device components<94>
-+//The device representation corresponds to the header's 'color space of data' field.
-+//This representation should be consistent with the 'number of device components'
+-//The device representation corresponds to the header?s ?color space of data? field.
+-//This representation should be consistent with the ?number of device components?
++//The device representation corresponds to the header's "color space of data" field.
++//This representation should be consistent with the "number of device components"
  //field in the namedColor2Type. If this field is 0, device coordinates are not provided.
--//The PCS representation corresponds to the header<92>s PCS field. The PCS representation
+-//The PCS representation corresponds to the header?s PCS field. The PCS representation
 +//The PCS representation corresponds to the header's PCS field. The PCS representation
  //is always provided. Color names are fixed-length, 32-byte fields including null
  //termination. In order to maintain maximum portability, it is strongly recommended
  //that special characters of the 7-bit ASCII set not be used.
-@@ -3814,7 +3814,7 @@ void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr)
+@@ -3839,7 +3839,7 @@ void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr)
  // ********************************************************************************
  //
  //This type represents a set of viewing condition parameters including:
--//CIE <92>absolute<92> illuminant white point tristimulus values and CIE <92>absolute<92>
-+//CIE 'absolute'illuminant white point tristimulus values and CIE 'absolute'
+-//CIE ?absolute? illuminant white point tristimulus values and CIE ?absolute?
++//CIE 'absolute' illuminant white point tristimulus values and CIE 'absolute'
  //surround tristimulus values.
  
  static
-@@ -3901,7 +3901,7 @@ void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr)
+@@ -3926,7 +3926,7 @@ void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr)
  }
  
  // Each curve is stored in one or more curve segments, with break-points specified between curve segments.
--// The first curve segment always starts at <96>Infinity, and the last curve segment always ends at +Infinity. The
+-// The first curve segment always starts at ?Infinity, and the last curve segment always ends at +Infinity. The
 +// The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The
  // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be
  // specified either in terms of a formula, or by a sampled curve.
  
 diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c
-index b8795be2c..9eff1f7be 100644
+index 19e0cafb1..935effc66 100644
 --- a/third_party/lcms/src/cmsvirt.c
 +++ b/third_party/lcms/src/cmsvirt.c
 @@ -612,18 +612,18 @@ cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
  
  //sRGB Curves are defined by:
  //
--//If  R<92>sRGB,G<92>sRGB, B<92>sRGB < 0.04045
+-//If  R?sRGB,G?sRGB, B?sRGB < 0.04045
 +//If  R'sRGB,G'sRGB, B'sRGB < 0.04045
  //
--//    R =  R<92>sRGB / 12.92
--//    G =  G<92>sRGB / 12.92
--//    B =  B<92>sRGB / 12.92
+-//    R =  R?sRGB / 12.92
+-//    G =  G?sRGB / 12.92
+-//    B =  B?sRGB / 12.92
 +//    R =  R'sRGB / 12.92
 +//    G =  G'sRGB / 12.92
 +//    B =  B'sRGB / 12.92
  //
  //
--//else if  R<92>sRGB,G<92>sRGB, B<92>sRGB >= 0.04045
+-//else if  R?sRGB,G?sRGB, B?sRGB >= 0.04045
 +//else if  R'sRGB,G'sRGB, B'sRGB >= 0.04045
  //
--//    R = ((R<92>sRGB + 0.055) / 1.055)^2.4
--//    G = ((G<92>sRGB + 0.055) / 1.055)^2.4
--//    B = ((B<92>sRGB + 0.055) / 1.055)^2.4
+-//    R = ((R?sRGB + 0.055) / 1.055)^2.4
+-//    G = ((G?sRGB + 0.055) / 1.055)^2.4
+-//    B = ((B?sRGB + 0.055) / 1.055)^2.4
 +//    R = ((R'sRGB + 0.055) / 1.055)^2.4
 +//    G = ((G'sRGB + 0.055) / 1.055)^2.4
 +//    B = ((B'sRGB + 0.055) / 1.055)^2.4
diff --git a/third_party/lcms/0020-avoid-fixed-inf.patch b/third_party/lcms/0020-avoid-fixed-inf.patch
deleted file mode 100644
index b2397d7..0000000
--- a/third_party/lcms/0020-avoid-fixed-inf.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
-index e4a7e4521..23aa54402 100644
---- a/third_party/lcms/src/cmsopt.c
-+++ b/third_party/lcms/src/cmsopt.c
-@@ -1546,7 +1546,7 @@ void MatShaperEval16(register const cmsUInt16Number In[],
- 
- // This table converts from 8 bits to 1.14 after applying the curve
- static
--void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve)
-+cmsBool FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve)
- {
-     int i;
-     cmsFloat32Number R, y;
-@@ -1555,14 +1555,17 @@ void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve)
- 
-         R   = (cmsFloat32Number) (i / 255.0);
-         y   = cmsEvalToneCurveFloat(Curve, R);
-+        if (isinf(y))
-+            return FALSE;
- 
-         Table[i] = DOUBLE_TO_1FIXED14(y);
-     }
-+    return TRUE;
- }
- 
- // This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve
- static
--void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput)
-+cmsBool FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput)
- {
-     int i;
-     cmsFloat32Number R, Val;
-@@ -1571,6 +1574,8 @@ void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8Bi
- 
-         R   = (cmsFloat32Number) (i / 16384.0);
-         Val = cmsEvalToneCurveFloat(Curve, R);    // Val comes 0..1.0
-+        if (isinf(Val))
-+            return FALSE;
- 
-         if (Is8BitsOutput) {
- 
-@@ -1585,6 +1590,7 @@ void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8Bi
-         }
-         else Table[i]  = _cmsQuickSaturateWord(Val * 65535.0);
-     }
-+    return TRUE;
- }
- 
- // Compute the matrix-shaper structure
-@@ -1602,13 +1608,19 @@ cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, c
-     p -> ContextID = Dest -> ContextID;
- 
-     // Precompute tables
--    FillFirstShaper(p ->Shaper1R, Curve1[0]);
--    FillFirstShaper(p ->Shaper1G, Curve1[1]);
--    FillFirstShaper(p ->Shaper1B, Curve1[2]);
-+    if (!FillFirstShaper(p ->Shaper1R, Curve1[0]))
-+        goto Error;
-+    if (!FillFirstShaper(p ->Shaper1G, Curve1[1]))
-+        goto Error;
-+    if (!FillFirstShaper(p ->Shaper1B, Curve1[2]))
-+        goto Error;
- 
--    FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits);
--    FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits);
--    FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits);
-+    if (!FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits))
-+        goto Error;
-+    if (!FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits))
-+        goto Error;
-+    if (!FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits))
-+        goto Error;
- 
-     // Convert matrix to nFixed14. Note that those values may take more than 16 bits as
-     for (i=0; i < 3; i++) {
-@@ -1634,6 +1646,9 @@ cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, c
-     // Fill function pointers
-     _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper);
-     return TRUE;
-+    Error:
-+        _cmsFree(Dest->ContextID, p);
-+        return FALSE;
- }
- 
- //  8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast!
-@@ -1746,7 +1761,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
-         *dwFlags |= cmsFLAGS_NOCACHE;
- 
-         // Setup the optimizarion routines
--        SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat);
-+        if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat))
-+            goto Error;
-     }
- 
-     cmsPipelineFree(Src);
diff --git a/third_party/lcms/0021-sanitize-float-read.patch b/third_party/lcms/0021-sanitize-float-read.patch
deleted file mode 100644
index 568fd0e..0000000
--- a/third_party/lcms/0021-sanitize-float-read.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c
-index b95befb..4ba998b 100644
---- a/third_party/lcms/src/cmsplugin.c
-+++ b/third_party/lcms/src/cmsplugin.c
-@@ -182,7 +182,9 @@ cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
-         if (isnan(*n))
-             return FALSE;
-     }
--    return TRUE;
-+
-+    // fpclassify() required by C99
-+    return (fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL);
- }
- 
- 
diff --git a/third_party/lcms/0022-check-LUT-and-MPE.patch b/third_party/lcms/0022-check-LUT-and-MPE.patch
deleted file mode 100644
index 04ddac8..0000000
--- a/third_party/lcms/0022-check-LUT-and-MPE.patch
+++ /dev/null
@@ -1,170 +0,0 @@
-diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c
-index 9b0eb4b54..19d43361f 100644
---- a/third_party/lcms/src/cmslut.c
-+++ b/third_party/lcms/src/cmslut.c
-@@ -1255,21 +1255,39 @@ cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe)
- // ***********************************************************************************************************
- 
- // This function sets up the channel count
--
- static
--void BlessLUT(cmsPipeline* lut)
-+cmsBool BlessLUT(cmsPipeline* lut)
- {
-     // We can set the input/ouput channels only if we have elements.
-     if (lut ->Elements != NULL) {
- 
--        cmsStage *First, *Last;
-+        cmsStage* prev;
-+        cmsStage* next;
-+        cmsStage* First;
-+        cmsStage* Last;
- 
-         First  = cmsPipelineGetPtrToFirstStage(lut);
-         Last   = cmsPipelineGetPtrToLastStage(lut);
- 
--        if (First != NULL)lut ->InputChannels = First ->InputChannels;
--        if (Last != NULL) lut ->OutputChannels = Last ->OutputChannels;
-+        if (First == NULL || Last == NULL) return FALSE;
-+
-+        lut->InputChannels = First->InputChannels;
-+        lut->OutputChannels = Last->OutputChannels;
-+
-+        // Check chain consistency
-+        prev = First;
-+        next = prev->Next;
-+
-+        while (next != NULL)
-+        {
-+            if (next->InputChannels != prev->OutputChannels)
-+                return FALSE;
-+
-+            next = next->Next;
-+            prev = prev->Next;
-+        }
-     }
-+    return TRUE;
- }
- 
- 
-@@ -1331,6 +1349,7 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In
- {
-        cmsPipeline* NewLUT;
- 
-+       // A value of zero in channels is allowed as placeholder
-        if (InputChannels >= cmsMAXCHANNELS ||
-            OutputChannels >= cmsMAXCHANNELS) return NULL;
- 
-@@ -1348,7 +1367,11 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In
-        NewLUT ->Data        = NewLUT;
-        NewLUT ->ContextID   = ContextID;
- 
--       BlessLUT(NewLUT);
-+       if (!BlessLUT(NewLUT))
-+       {
-+           _cmsFree(ContextID, NewLUT);
-+           return NULL;
-+       }
- 
-        return NewLUT;
- }
-@@ -1454,7 +1477,12 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)
- 
-     NewLUT ->SaveAs8Bits    = lut ->SaveAs8Bits;
- 
--    BlessLUT(NewLUT);
-+    if (!BlessLUT(NewLUT))
-+    {
-+        _cmsFree(lut->ContextID, NewLUT);
-+        return NULL;
-+    }
-+
-     return NewLUT;
- }
- 
-@@ -1491,8 +1519,7 @@ int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage
-             return FALSE;
-     }
- 
--    BlessLUT(lut);
--    return TRUE;
-+    return BlessLUT(lut);
- }
- 
- // Unlink an element and return the pointer to it
-@@ -1547,6 +1574,7 @@ void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStag
-     else
-         cmsStageFree(Unlinked);
- 
-+    // May fail, but we ignore it
-     BlessLUT(lut);
- }
- 
-@@ -1573,8 +1601,7 @@ cmsBool  CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2)
-                 return FALSE;
-     }
- 
--    BlessLUT(l1);
--    return TRUE;
-+    return BlessLUT(l1);
- }
- 
- 
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index e5ed06c33..0256e247b 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -1755,8 +1755,8 @@ void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms
-     if (!_cmsReadUInt8Number(io, NULL)) goto Error;
- 
-     // Do some checking
--    if (InputChannels > cmsMAXCHANNELS)  goto Error;
--    if (OutputChannels > cmsMAXCHANNELS) goto Error;
-+    if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS)  goto Error;
-+    if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error;
- 
-    // Allocates an empty Pipeline
-     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
-@@ -2048,8 +2048,8 @@ void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm
-     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
- 
-     // Do some checking
--    if (InputChannels > cmsMAXCHANNELS)  goto Error;
--    if (OutputChannels > cmsMAXCHANNELS) goto Error;
-+    if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS)  goto Error;
-+    if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error;
- 
-     // Allocates an empty LUT
-     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
-@@ -2486,7 +2486,10 @@ void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, c
-     if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
-     if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
- 
--   // Allocates an empty LUT
-+    if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL;
-+    if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL;
-+
-+    // Allocates an empty LUT
-     NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
-     if (NewLUT == NULL) return NULL;
- 
-@@ -2794,6 +2797,9 @@ void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, c
-     if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
-     if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
- 
-+    if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL;
-+    if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL;
-+
-     // Padding
-     if (!_cmsReadUInt16Number(io, NULL)) return NULL;
- 
-@@ -4443,6 +4449,9 @@ void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU
-     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
-     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
- 
-+    if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL;
-+    if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL;
-+
-     // Allocates an empty LUT
-     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans);
-     if (NewLUT == NULL) return NULL;
diff --git a/third_party/lcms/0023-upstream-integer-overflow-MPEmatrix_Read.patch b/third_party/lcms/0023-upstream-integer-overflow-MPEmatrix_Read.patch
deleted file mode 100644
index 70a6bb9..0000000
--- a/third_party/lcms/0023-upstream-integer-overflow-MPEmatrix_Read.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
-index 5720c66a7..cce4cedba 100644
---- a/third_party/lcms/src/cmscgats.c
-+++ b/third_party/lcms/src/cmscgats.c
-@@ -150,23 +150,24 @@ typedef struct {
-         SUBALLOCATOR   Allocator;             // String suballocator -- just to keep it fast
- 
-         // Parser state machine
--        SYMBOL         sy;                    // Current symbol
--        int            ch;                    // Current character
-+        SYMBOL             sy;                // Current symbol
-+        int                ch;                // Current character
-+
-+        cmsInt32Number     inum;              // integer value
-+        cmsFloat64Number   dnum;              // real value
- 
--        int            inum;                  // integer value
--        cmsFloat64Number         dnum;                  // real value
-         char           id[MAXID];             // identifier
-         char           str[MAXSTR];           // string
- 
-         // Allowed keywords & datasets. They have visibility on whole stream
--        KEYVALUE*     ValidKeywords;
--        KEYVALUE*     ValidSampleID;
-+        KEYVALUE*      ValidKeywords;
-+        KEYVALUE*      ValidSampleID;
- 
-         char*          Source;                // Points to loc. being parsed
--        int            lineno;                // line counter for error reporting
-+        cmsInt32Number lineno;                // line counter for error reporting
- 
-         FILECTX*       FileStack[MAXINCLUDE]; // Stack of files being parsed
--        int            IncludeSP;             // Include Stack Pointer
-+        cmsInt32Number IncludeSP;             // Include Stack Pointer
- 
-         char*          MemoryBlock;           // The stream if holded in memory
- 
-@@ -568,8 +569,8 @@ void ReadReal(cmsIT8* it8, int inum)
-     // Exponent, example 34.00E+20
-     if (toupper(it8->ch) == 'E') {
- 
--        int e;
--        int sgn;
-+        cmsInt32Number e;
-+        cmsInt32Number sgn;
- 
-         NextCh(it8); sgn = 1;
- 
-@@ -587,7 +588,7 @@ void ReadReal(cmsIT8* it8, int inum)
-             e = 0;
-             while (isdigit(it8->ch)) {
- 
--                if ((cmsFloat64Number) e * 10L < INT_MAX)
-+                if ((cmsFloat64Number) e * 10L < (cmsFloat64Number) +2147483647.0)
-                     e = e * 10 + (it8->ch - '0');
- 
-                 NextCh(it8);
-@@ -777,7 +778,7 @@ void InSymbol(cmsIT8* it8)
- 
-                 while (isdigit(it8->ch)) {
- 
--                    if ((long) it8->inum * 10L > (long) INT_MAX) {
-+                    if ((cmsFloat64Number) it8->inum * 10L > (cmsFloat64Number) +2147483647.0) {
-                         ReadReal(it8, it8->inum);
-                         it8->sy = SDNUM;
-                         it8->dnum *= sign;
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 0256e247b..75f1fae32 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -4199,9 +4199,13 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io
-     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
- 
- 
-+    // Input and output chans may be ANY (up to 0xffff), 
-+    // but we choose to limit to 16 channels for now
-+    if (InputChans >= cmsMAXCHANNELS) return NULL;
-+    if (OutputChans >= cmsMAXCHANNELS) return NULL;
-+
-     nElems = InputChans * OutputChans;
- 
--    // Input and output chans may be ANY (up to 0xffff)
-     Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number));
-     if (Matrix == NULL) return NULL;
- 
diff --git a/third_party/lcms/0024-verify-size-before-reading.patch b/third_party/lcms/0024-verify-size-before-reading.patch
deleted file mode 100644
index e72e310..0000000
--- a/third_party/lcms/0024-verify-size-before-reading.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 75f1fae32..4d96a1ed6 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -173,6 +173,12 @@ cmsBool ReadPositionTable(struct _cms_typehandler_struct* self,
- {
-     cmsUInt32Number i;
-     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL;
-+    cmsUInt32Number currentPosition;
-+
-+    currentPosition = io->Tell(io);
-+    // Verify there is enough space left to read two cmsUInt32Number items for Count items.
-+    if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count)
-+        return FALSE;
- 
-     // Let's take the offsets to each element
-     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
diff --git a/third_party/lcms/0025-upstream-direct-leak-Type_MPE_Read.patch b/third_party/lcms/0025-upstream-direct-leak-Type_MPE_Read.patch
deleted file mode 100644
index 3393331..0000000
--- a/third_party/lcms/0025-upstream-direct-leak-Type_MPE_Read.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 75f1fae32..f92a92822 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -4460,18 +4460,19 @@ void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU
-     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans);
-     if (NewLUT == NULL) return NULL;
- 
--    if (!_cmsReadUInt32Number(io, &ElementCount)) return NULL;
--
--    if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) {
--        if (NewLUT != NULL) cmsPipelineFree(NewLUT);
--        *nItems = 0;
--        return NULL;
--    }
-+    if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error;
-+    if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error;
- 
-     // Success
-     *nItems = 1;
-     return NewLUT;
- 
-+    // Error
-+Error:
-+    if (NewLUT != NULL) cmsPipelineFree(NewLUT);
-+    *nItems = 0;
-+    return NULL;
-+
-     cmsUNUSED_PARAMETER(SizeOfTag);
- }
- 
diff --git a/third_party/lcms/0026-more-unsupported-characters.patch b/third_party/lcms/0026-more-unsupported-characters.patch
index a66d50c..66e9239 100644
--- a/third_party/lcms/0026-more-unsupported-characters.patch
+++ b/third_party/lcms/0026-more-unsupported-characters.patch
@@ -1,72 +1,40 @@
 diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
-index 23aa54402..abe26b93a 100644
+index f838b6eb7..5ea1b4c85 100644
 --- a/third_party/lcms/src/cmsopt.c
 +++ b/third_party/lcms/src/cmsopt.c
-@@ -1756,8 +1756,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
+@@ -1764,8 +1764,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
          _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1);
          _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2);
  
--        // In this particular optimization, cach<E9> does not help as it takes more time to deal with
--        // the cach<E9> that with the pixel handling
+-        // In this particular optimization, caché does not help as it takes more time to deal with
+-        // the caché that with the pixel handling
 +        // In this particular optimization, cache does not help as it takes more time to deal with
 +        // the cache that with the pixel handling
          *dwFlags |= cmsFLAGS_NOCACHE;
  
          // Setup the optimizarion routines
 diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 38091f159..5ea1d2d87 100644
+index e95e6d910..0c24da162 100644
 --- a/third_party/lcms/src/cmstypes.c
 +++ b/third_party/lcms/src/cmstypes.c
-@@ -4194,7 +4194,7 @@ cmsBool  Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER*
+@@ -4207,7 +4207,7 @@ cmsBool  Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER*
  // The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the
  // matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array
  // is organized as follows:
--// array = [e11, e12, <85>, e1P, e21, e22, <85>, e2P, <85>, eQ1, eQ2, <85>, eQP, e1, e2, <85>, eQ]
+-// array = [e11, e12, …, e1P, e21, e22, …, e2P, …, eQ1, eQ2, …, eQP, e1, e2, …, eQ]
 +// array = [e11, e12, ..., e1P, e21, e22, ..., e2P, ..., eQ1, eQ2, ..., eQP, e1, e2, ..., eQ]
  
  static
  void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
-npm@npm0:~/pdfium/pdfium$ 
-npm@npm0:~/pdfium/pdfium$ 
-npm@npm0:~/pdfium/pdfium$ 
-npm@npm0:~/pdfium/pdfium$ git diff
-diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
-index 23aa54402..abe26b93a 100644
---- a/third_party/lcms/src/cmsopt.c
-+++ b/third_party/lcms/src/cmsopt.c
-@@ -1756,8 +1756,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
-         _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1);
-         _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2);
- 
--        // In this particular optimization, cach<E9> does not help as it takes more time to deal with
--        // the cach<E9> that with the pixel handling
-+        // In this particular optimization, cache does not help as it takes more time to deal with
-+        // the cache that with the pixel handling
-         *dwFlags |= cmsFLAGS_NOCACHE;
- 
-         // Setup the optimizarion routines
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 38091f159..8b02f86ca 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -4194,7 +4194,7 @@ cmsBool  Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER*
- // The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the
- // matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array
- // is organized as follows:
--// array = [e11, e12, <85>, e1P, e21, e22, <85>, e2P, <85>, eQ1, eQ2, <85>, eQP, e1, e2, <85>, eQ]
-+// array = [e11, e12, ..., e1P, e21, e22, ..., e2P, ..., eQ1, eQ2, ..., eQP, e1, e2, ..., eQ]
- 
- static
- void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
-@@ -4713,10 +4713,10 @@ void *Type_vcgt_Read(struct _cms_typehandler_struct* self,
+@@ -4730,10 +4730,10 @@ void *Type_vcgt_Read(struct _cms_typehandler_struct* self,
              // Y = cX + f             | X < d
  
              // vcgt formula is:
--            // Y = (Max <96> Min) * (X ^ Gamma) + Min
+-            // Y = (Max – Min) * (X ^ Gamma) + Min
 +            // Y = (Max - Min) * (X ^ Gamma) + Min
  
              // So, the translation is
--            // a = (Max <96> Min) ^ ( 1 / Gamma)
+-            // a = (Max – Min) ^ ( 1 / Gamma)
 +            // a = (Max - Min) ^ ( 1 / Gamma)
              // e = Min
              // b=c=d=f=0
diff --git a/third_party/lcms/0030-const-data.patch b/third_party/lcms/0030-const-data.patch
new file mode 100644
index 0000000..dc8e37c
--- /dev/null
+++ b/third_party/lcms/0030-const-data.patch
@@ -0,0 +1,114 @@
+diff --git a/third_party/lcms/src/cmsalpha.c b/third_party/lcms/src/cmsalpha.c
+index 7d6aa345f..566f5fe9b 100644
+--- a/third_party/lcms/src/cmsalpha.c
++++ b/third_party/lcms/src/cmsalpha.c
+@@ -252,7 +252,7 @@ int FormatterPos(cmsUInt32Number frm)
+ static
+ cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
+ {
+-static cmsFormatterAlphaFn FormattersAlpha[5][5] = {
++static const cmsFormatterAlphaFn FormattersAlpha[5][5] = {
+ 
+        /* from 8 */  { copy8,      from8to16,   from8toHLF,   from8toFLT,   from8toDBL   },
+        /* from 16*/  { from16to8,  copy16,      from16toHLF,  from16toFLT,  from16toDBL  },
+diff --git a/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c
+index 6e36cf462..eadbed852 100644
+--- a/third_party/lcms/src/cmsgamma.c
++++ b/third_party/lcms/src/cmsgamma.c
+@@ -57,7 +57,7 @@ typedef struct _cmsParametricCurvesCollection_st {
+ static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R);
+ 
+ // The built-in list
+-static _cmsParametricCurvesCollection DefaultCurves = {
++static const _cmsParametricCurvesCollection DefaultCurves = {
+     9,                                  // # of curve types
+     { 1, 2, 3, 4, 5, 6, 7, 8, 108 },    // Parametric curve ID
+     { 1, 3, 4, 5, 7, 4, 5, 5, 1 },      // Parameters by type
+@@ -161,7 +161,7 @@ cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase*
+ 
+ // Search in type list, return position or -1 if not found
+ static
+-int IsInSet(int Type, _cmsParametricCurvesCollection* c)
++int IsInSet(int Type, const _cmsParametricCurvesCollection* c)
+ {
+     int i;
+ 
+@@ -174,9 +174,9 @@ int IsInSet(int Type, _cmsParametricCurvesCollection* c)
+ 
+ // Search for the collection which contains a specific type
+ static
+-_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index)
++const _cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index)
+ {
+-    _cmsParametricCurvesCollection* c;
++    const _cmsParametricCurvesCollection* c;
+     int Position;
+     _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin);
+ 
+@@ -269,7 +269,7 @@ cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntr
+     // is placed in advance to maximize performance.
+     if (Segments != NULL && (nSegments > 0)) {
+ 
+-        _cmsParametricCurvesCollection *c;
++        const _cmsParametricCurvesCollection *c;
+ 
+         p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*));
+         if (p ->SegInterp == NULL) goto Error;
+@@ -714,7 +714,7 @@ cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt
+     cmsCurveSegment Seg0;
+     int Pos = 0;
+     cmsUInt32Number size;
+-    _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos);
++    const _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos);
+ 
+     _cmsAssert(Params != NULL);
+ 
+diff --git a/third_party/lcms/src/cmshalf.c b/third_party/lcms/src/cmshalf.c
+index cdd4e37b7..cceb6f987 100644
+--- a/third_party/lcms/src/cmshalf.c
++++ b/third_party/lcms/src/cmshalf.c
+@@ -31,7 +31,7 @@
+ // This code is inspired in the paper "Fast Half Float Conversions"
+ // by Jeroen van der Zijp
+ 
+-static cmsUInt32Number Mantissa[2048] = {
++static const cmsUInt32Number Mantissa[2048] = {
+ 
+ 0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000,
+ 0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000,
+@@ -377,7 +377,7 @@ static cmsUInt32Number Mantissa[2048] = {
+ 0x387fc000, 0x387fe000
+ };
+ 
+-static cmsUInt16Number Offset[64] = {
++static const cmsUInt16Number Offset[64] = {
+ 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+@@ -391,7 +391,7 @@ static cmsUInt16Number Offset[64] = {
+ 0x0400, 0x0400, 0x0400, 0x0400
+ };
+ 
+-static cmsUInt32Number Exponent[64] = {
++static const cmsUInt32Number Exponent[64] = {
+ 0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000,
+ 0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000,
+ 0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000,
+@@ -405,7 +405,7 @@ static cmsUInt32Number Exponent[64] = {
+ 0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000
+ };
+ 
+-static cmsUInt16Number Base[512] = {
++static const cmsUInt16Number Base[512] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+@@ -460,7 +460,7 @@ static cmsUInt16Number Base[512] = {
+ 0xfc00, 0xfc00
+ };
+ 
+-static cmsUInt8Number  Shift[512] = {
++static const cmsUInt8Number  Shift[512] = {
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
diff --git a/third_party/lcms/0031-wrong-tag-element-count.patch b/third_party/lcms/0031-wrong-tag-element-count.patch
new file mode 100644
index 0000000..a62bc3d
--- /dev/null
+++ b/third_party/lcms/0031-wrong-tag-element-count.patch
@@ -0,0 +1,12 @@
+diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
+index cc5f89064..63bbe36a8 100644
+--- a/third_party/lcms/src/cmsio0.c
++++ b/third_party/lcms/src/cmsio0.c
+@@ -1616,6 +1616,7 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
+         _cmsTagSignature2String(String, sig);
+         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
+             String, TagDescriptor ->ElemCount, ElemCount);
++        goto Error;
+     }
+ 
+ 
diff --git a/third_party/lcms/0032-cgats-allocation.patch b/third_party/lcms/0032-cgats-allocation.patch
new file mode 100644
index 0000000..08204b5
--- /dev/null
+++ b/third_party/lcms/0032-cgats-allocation.patch
@@ -0,0 +1,24 @@
+diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
+index 55f74ede8..0738a1cce 100644
+--- a/third_party/lcms/src/cmscgats.c
++++ b/third_party/lcms/src/cmscgats.c
+@@ -1504,10 +1504,16 @@ void AllocateDataSet(cmsIT8* it8)
+     t-> nSamples   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+     t-> nPatches   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
+ 
+-    t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*));
+-    if (t->Data == NULL) {
++    if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)
++    {
++        SynError(it8, "AllocateDataSet: too much data");
++    }
++    else {
++        t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
++        if (t->Data == NULL) {
+ 
+-        SynError(it8, "AllocateDataSet: Unable to allocate data array");
++            SynError(it8, "AllocateDataSet: Unable to allocate data array");
++        }
+     }
+ 
+ }
diff --git a/third_party/lcms/0033-opt-integer-overflow.patch b/third_party/lcms/0033-opt-integer-overflow.patch
new file mode 100644
index 0000000..5743470
--- /dev/null
+++ b/third_party/lcms/0033-opt-integer-overflow.patch
@@ -0,0 +1,75 @@
+diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
+index 5ea1b4c85..8a1171820 100644
+--- a/third_party/lcms/src/cmsopt.c
++++ b/third_party/lcms/src/cmsopt.c
+@@ -104,6 +104,39 @@ typedef struct {
+ // Simple optimizations ----------------------------------------------------------------------------------------------------------
+ 
+ 
++// Clamp a fixed point integer to signed 28 bits to avoid overflow in
++// calculations.  Clamp is intended for use with colorants, requiring one bit
++// for a colorant and another two bits to avoid overflow when combining the
++// colors.
++cmsINLINE cmsS1Fixed14Number _FixedClamp(cmsS1Fixed14Number n) {
++  const cmsS1Fixed14Number max_positive = 268435455;  // 0x0FFFFFFF;
++  const cmsS1Fixed14Number max_negative = -268435456; // 0xF0000000;
++  // Normally expect the provided number to be in the range [0..1] (but in
++  // fixed 1.14 format), so can perform a quick check for this typical case
++  // to reduce number of compares.
++  const cmsS1Fixed14Number typical_range_mask = 0xFFFF8000;
++
++  if (!(n & typical_range_mask))
++    return n;
++  if (n < max_negative)
++     return max_negative;
++  if (n > max_positive)
++    return max_positive;
++  return n;
++}
++
++// Perform one row of matrix multiply with translation for MatShaperEval16().
++cmsINLINE cmsInt64Number _MatShaperEvaluateRow(cmsS1Fixed14Number* mat,
++                                               cmsS1Fixed14Number off,
++                                               cmsS1Fixed14Number r,
++                                               cmsS1Fixed14Number g,
++                                               cmsS1Fixed14Number b) {
++  return ((cmsInt64Number)mat[0] * r +
++          (cmsInt64Number)mat[1] * g +
++          (cmsInt64Number)mat[2] * b +
++          off + 0x2000) >> 14;
++}
++
+ // Remove an element in linked chain
+ static
+ void _RemoveElement(cmsStage** head)
+@@ -1527,7 +1560,8 @@ void MatShaperEval16(register const cmsUInt16Number In[],
+                      register const void* D)
+ {
+     MatShaper8Data* p = (MatShaper8Data*) D;
+-    cmsS1Fixed14Number l1, l2, l3, r, g, b;
++    cmsS1Fixed14Number r, g, b;
++    cmsInt64Number l1, l2, l3;
+     cmsUInt32Number ri, gi, bi;
+ 
+     // In this case (and only in this case!) we can use this simplification since
+@@ -1537,14 +1571,14 @@ void MatShaperEval16(register const cmsUInt16Number In[],
+     bi = In[2] & 0xFFU;
+ 
+     // Across first shaper, which also converts to 1.14 fixed point
+-    r = p->Shaper1R[ri];
+-    g = p->Shaper1G[gi];
+-    b = p->Shaper1B[bi];
++    r = _FixedClamp(p->Shaper1R[ri]);
++    g = _FixedClamp(p->Shaper1G[gi]);
++    b = _FixedClamp(p->Shaper1B[bi]);
+ 
+     // Evaluate the matrix in 1.14 fixed point
+-    l1 =  (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14;
+-    l2 =  (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14;
+-    l3 =  (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14;
++    l1 = _MatShaperEvaluateRow(p->Mat[0], p->Off[0], r, g, b);
++    l2 = _MatShaperEvaluateRow(p->Mat[1], p->Off[1], r, g, b);
++    l3 = _MatShaperEvaluateRow(p->Mat[2], p->Off[2], r, g, b);
+ 
+     // Now we have to clip to 0..1.0 range
+     ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384U : (cmsUInt32Number) l1);
diff --git a/third_party/lcms/README.pdfium b/third_party/lcms/README.pdfium
index b04da7d..5f0025b 100644
--- a/third_party/lcms/README.pdfium
+++ b/third_party/lcms/README.pdfium
@@ -1,6 +1,6 @@
 Name: Little CMS
 URL: http://www.littlecms.com/
-Version: 2.8
+Version: 2.9
 Security Critical: yes
 License: MIT License
 
@@ -17,27 +17,12 @@
 0005-old-fix-e-with-tilde.patch: like https://codereview.chromium.org/2411123003/ but better.
 0006-tag-type-confusion.patch: Fix a type confusion.
 0008-infinite-loop-GrowNamedColorList.patch: Fix infinite loop when calling GrowNamedColorList.
-0009-uninit.patch: Fix use uninitialized value and stack buffer overflow read.
-0010-memory-leak-Type_Curve_Read.patch: Fix memory leak in Type_Curve_Read.
-0011-memory-leak-AllocEmptyTransform.patch: Fix memory leak in AllocEmptyTransform.
-0012-memory-leak-Type_NamedColor_Read.patch: Fix memory leak in Type_NamedColor_Read.
-0013-memory-leak-OptimizeByResampling.patch: Fix memory leak in OptimizeByResampling.
-0014-memory-leak-Type_MPEmatrix_Read.patch: Fix memory leak in MPEmatrix_Read.
-0015-cmsStageAllocMatrix-param-swap.patch: Fix rows/cols swap in cmsStageAllocMatrix.
-0016-reject-nan.patch: Reject NaN when reading float numbers.
-0017-memory-leak-ReadSegmentedCurve.patch: Fix memory leak in ReadSegmentedCurve.
-0018-backport-c0a98d86.patch: Fix several issues. Backport from upstream
-    https://github.com/mm2/Little-CMS/commit/c0a98d86
 0019-utf8.patch: Encode source files as utf-8.
-0020-avoid-fixed-inf.patch: Avoid fixed number LUT optimization on inf values.
-0021-sanitize-float-read.patch: Sanitize floating point read. Partially backport
-    from upstream https://github.com/mm2/Little-CMS/commit/4011a6e3
-0022-check-LUT-and-MPE.patch: check LUT consistency and sanitize MPE profiles.
-0023-upstream-integer-overflow-MPEmatrix_Read.patch: fix some integer overflows.
-0024-verify-size-before-reading.patch: fix OOM issue when there won't be enough
-    data to read anyway.
-0025-upstream-direct-leak-Type_MPE_Read.patch: fix leak in cmstypes.c.
 0026-more-unsupported-characters.patch: remove other unsupported characters.
 0027-changes-from-beginning-of-time.patch: commented changes from initial commit.
 0028-do-not-quickfloor.patch: flooring errors may cause heap-buffer-overflow.
 0029-drop-register-keyword.patch: Remove deprecated 'register' keyword.
+0030-const-data.patch: Mark many data structures as const.
+0031-wrong-tag-element-count.patch: Handle tag element count mismatch as an error.
+0032-cgats-allocation.patch: Add check on CGATS memory allocation.
+0033-opt-integer-overflow.patch: Protect against integer overflow.
diff --git a/third_party/lcms/include/lcms2.h b/third_party/lcms/include/lcms2.h
index 2bf6f24..7653f8e 100644
--- a/third_party/lcms/include/lcms2.h
+++ b/third_party/lcms/include/lcms2.h
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -23,7 +23,7 @@
 //
 //---------------------------------------------------------------------------------
 //
-// Version 2.8
+// Version 2.9rc3
 //
 
 #ifndef _lcms2_H
@@ -38,7 +38,7 @@
 // #define CMS_DONT_USE_INT64        1
 
 // Uncomment this if your compiler doesn't work with fast floor function
-#define CMS_DONT_USE_FAST_FLOOR 1
+// #define CMS_DONT_USE_FAST_FLOOR 1
 
 // Uncomment this line if you want lcms to use the black point tag in profile,
 // if commented, lcms will compute the black point by its own.
@@ -49,7 +49,7 @@
 // #define CMS_USE_CPP_API
 
 // Uncomment this line if you need strict CGATS syntax. Makes CGATS files to
-// require "KEYWORD" on undefined identifiers, keep it comented out unless needed
+// require "KEYWORD" on undefined identifiers, keep it commented out unless needed
 // #define CMS_STRICT_CGATS  1
 
 // Uncomment to get rid of the tables for "half" float support
@@ -58,6 +58,9 @@
 // Uncomment to get rid of pthreads/windows dependency
 // #define CMS_NO_PTHREADS  1
 
+// Uncomment this for special windows mutex initialization (see lcms2_internal.h)
+// #define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+
 // ********** End of configuration toggles ******************************
 
 // Needed for streams
@@ -75,7 +78,7 @@
 #endif
 
 // Version/release
-#define LCMS_VERSION        2080
+#define LCMS_VERSION        2090
 
 // I will give the chance of redefining basic types for compilers that are not fully C99 compliant
 #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@@ -225,16 +228,21 @@
 #            define CMSAPI    __declspec(dllexport)
 #        else
 #           define CMSAPI     __declspec(dllimport)
-#       endif
+#        endif
 #     endif
 #  else
-#       define CMSEXPORT
-#       define CMSAPI
+#     define CMSEXPORT
+#     define CMSAPI
 #  endif
-#else
-# define CMSEXPORT
-# define CMSAPI
-#endif
+#else  // not Windows
+#  ifdef HAVE_FUNC_ATTRIBUTE_VISIBILITY
+#     define CMSEXPORT
+#     define CMSAPI    __attribute__((visibility("default")))
+#  else
+#     define CMSEXPORT
+#     define CMSAPI
+#  endif
+#endif  // CMS_IS_WINDOWS_
 
 #ifdef HasTHREADS
 # if HasTHREADS == 1
@@ -255,9 +263,9 @@
 #endif
 
 // D50 XYZ normalized to Y=1.0
-#define cmsD50X             0.9642
-#define cmsD50Y             1.0
-#define cmsD50Z             0.8249
+#define cmsD50X  0.9642
+#define cmsD50Y  1.0
+#define cmsD50Z  0.8249
 
 // V4 perceptual black
 #define cmsPERCEPTUAL_BLACK_X  0.00336
@@ -265,8 +273,8 @@
 #define cmsPERCEPTUAL_BLACK_Z  0.00287
 
 // Definitions in ICC spec
-#define cmsMagicNumber      0x61637370     // 'acsp'
-#define lcmsSignature       0x6c636d73     // 'lcms'
+#define cmsMagicNumber  0x61637370     // 'acsp'
+#define lcmsSignature   0x6c636d73     // 'lcms'
 
 
 // Base ICC type definitions
@@ -1125,7 +1133,7 @@
     cmsCIEXYZ        whitePoint;
     cmsFloat64Number Yb;
     cmsFloat64Number La;
-    int              surround;
+    cmsUInt32Number  surround;
     cmsFloat64Number D_value;
 
     } cmsViewingConditions;
@@ -1153,16 +1161,16 @@
 // The internal representation is none of your business.
 typedef struct _cms_curve_struct cmsToneCurve;
 
-CMSAPI cmsToneCurve*     CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, cmsInt32Number nSegments, const cmsCurveSegment Segments[]);
+CMSAPI cmsToneCurve*     CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, cmsUInt32Number nSegments, const cmsCurveSegment Segments[]);
 CMSAPI cmsToneCurve*     CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]);
 CMSAPI cmsToneCurve*     CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma);
-CMSAPI cmsToneCurve*     CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsInt32Number nEntries, const cmsUInt16Number values[]);
+CMSAPI cmsToneCurve*     CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number values[]);
 CMSAPI cmsToneCurve*     CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]);
 CMSAPI void              CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve);
 CMSAPI void              CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]);
 CMSAPI cmsToneCurve*     CMSEXPORT cmsDupToneCurve(const cmsToneCurve* Src);
 CMSAPI cmsToneCurve*     CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma);
-CMSAPI cmsToneCurve*     CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, const cmsToneCurve* InGamma);
+CMSAPI cmsToneCurve*     CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InGamma);
 CMSAPI cmsToneCurve*     CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, const cmsToneCurve* X,  const cmsToneCurve* Y, cmsUInt32Number nPoints);
 CMSAPI cmsBool           CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda);
 CMSAPI cmsFloat32Number  CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v);
@@ -1207,7 +1215,7 @@
 // Where to place/locate the stages in the pipeline chain
 typedef enum { cmsAT_BEGIN, cmsAT_END } cmsStageLoc;
 
-CMSAPI int               CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe);
+CMSAPI cmsBool           CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe);
 CMSAPI void              CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe);
 
 // This function is quite useful to analyze the structure of a Pipeline and retrieve the Stage elements
@@ -1430,7 +1438,7 @@
 CMSAPI cmsTagSignature   CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig);
 
 // Read and write raw data
-CMSAPI cmsInt32Number    CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* Buffer, cmsUInt32Number BufferSize);
+CMSAPI cmsUInt32Number   CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* Buffer, cmsUInt32Number BufferSize);
 CMSAPI cmsBool           CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size);
 
 // Access header data
@@ -1521,7 +1529,7 @@
 
 CMSAPI cmsBool           CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile);
 
-// Profile high level funtions ------------------------------------------------------------------------------------------
+// Profile high level functions ------------------------------------------------------------------------------------------
 
 CMSAPI cmsHPROFILE      CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess);
 CMSAPI cmsHPROFILE      CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *ICCProfile, const char *sAccess);
@@ -1581,21 +1589,21 @@
 CMSAPI cmsHPROFILE      CMSEXPORT cmsCreate_sRGBProfile(void);
 
 CMSAPI cmsHPROFILE      CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
-                                                             int nLUTPoints,
+                                                             cmsUInt32Number nLUTPoints,
                                                              cmsFloat64Number Bright,
                                                              cmsFloat64Number Contrast,
                                                              cmsFloat64Number Hue,
                                                              cmsFloat64Number Saturation,
-                                                             int TempSrc,
-                                                             int TempDest);
+                                                             cmsUInt32Number TempSrc,
+                                                             cmsUInt32Number TempDest);
 
-CMSAPI cmsHPROFILE      CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
+CMSAPI cmsHPROFILE      CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,
                                                              cmsFloat64Number Bright,
                                                              cmsFloat64Number Contrast,
                                                              cmsFloat64Number Hue,
                                                              cmsFloat64Number Saturation,
-                                                             int TempSrc,
-                                                             int TempDest);
+                                                             cmsUInt32Number TempSrc,
+                                                             cmsUInt32Number TempDest);
 
 CMSAPI cmsHPROFILE      CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID);
 CMSAPI cmsHPROFILE      CMSEXPORT cmsCreateNULLProfile(void);
@@ -1637,7 +1645,7 @@
 #define cmsFLAGS_BLACKPOINTCOMPENSATION   0x2000
 #define cmsFLAGS_NOWHITEONWHITEFIXUP      0x0004    // Don't fix scum dot
 #define cmsFLAGS_HIGHRESPRECALC           0x0400    // Use more memory to give better accurancy
-#define cmsFLAGS_LOWRESPRECALC            0x0800    // Use less memory to minimize resouces
+#define cmsFLAGS_LOWRESPRECALC            0x0800    // Use less memory to minimize resources
 
 // For devicelink creation
 #define cmsFLAGS_8BITS_DEVICELINK         0x0008   // Create 8 bits devicelinks
@@ -1807,7 +1815,7 @@
 
 // Persistence
 CMSAPI cmsHANDLE        CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName);
-CMSAPI cmsHANDLE        CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len);
+CMSAPI cmsHANDLE        CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len);
 // CMSAPI cmsHANDLE        CMSEXPORT cmsIT8LoadFromIOhandler(cmsContext ContextID, cmsIOHANDLER* io);
 
 CMSAPI cmsBool          CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName);
diff --git a/third_party/lcms/include/lcms2_plugin.h b/third_party/lcms/include/lcms2_plugin.h
index 7eda009..17bec42 100644
--- a/third_party/lcms/include/lcms2_plugin.h
+++ b/third_party/lcms/include/lcms2_plugin.h
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -318,7 +318,7 @@
 
 // Parametric curves. A negative type means same function but analytically inverted. Max. number of params is 10
 
-// Evaluator callback for user-suplied parametric curves. May implement more than one type
+// Evaluator callback for user-supplied parametric curves. May implement more than one type
 typedef  cmsFloat64Number (* cmsParametricCurveEvaluator)(cmsInt32Number Type, const cmsFloat64Number Params[10], cmsFloat64Number R);
 
 // Plug-in may implement an arbitrary number of parametric curves
@@ -428,7 +428,7 @@
     cmsUInt32Number     nSupportedTypes;    // In how many types this tag can come (MAX_TYPES_IN_LCMS_PLUGIN maximum)
     cmsTagTypeSignature SupportedTypes[MAX_TYPES_IN_LCMS_PLUGIN];
 
-    // For writting
+    // For writing
     cmsTagTypeSignature (* DecideType)(cmsFloat64Number ICCVersion, const void *Data);
 
 } cmsTagDescriptor;
diff --git a/third_party/lcms/src/cmsalpha.c b/third_party/lcms/src/cmsalpha.c
index 7d6aa34..3e6b7c3 100644
--- a/third_party/lcms/src/cmsalpha.c
+++ b/third_party/lcms/src/cmsalpha.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -26,7 +26,6 @@
 
 #include "lcms2_internal.h"
 
-
 // Alpha copy ------------------------------------------------------------------------------------------------------------------
 
 // Floor to byte, taking care of saturation
@@ -42,16 +41,16 @@
 
 // Return the size in bytes of a given formatter
 static
-int trueBytesSize(cmsUInt32Number Format)
+cmsUInt32Number trueBytesSize(cmsUInt32Number Format)
 {
-       int fmt_bytes = T_BYTES(Format);
+    cmsUInt32Number fmt_bytes = T_BYTES(Format);
 
-       // For double, the T_BYTES field returns zero
-       if (fmt_bytes == 0)
-              return sizeof(double);
-      
-       // Otherwise, it is already correct for all formats
-       return fmt_bytes;
+    // For double, the T_BYTES field returns zero
+    if (fmt_bytes == 0)
+        return sizeof(double);
+
+    // Otherwise, it is already correct for all formats
+    return fmt_bytes;
 }
 
 
@@ -90,8 +89,13 @@
 static
 void from8toHLF(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f;
        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
 }
 
 // From 16
@@ -122,8 +126,13 @@
 static
 void from16toHLF(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f;
        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
 }
 
 // From Float
@@ -158,8 +167,13 @@
 static
 void fromFLTtoHLF(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = *(cmsFloat32Number*)src;
        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
 }
 
 
@@ -168,27 +182,48 @@
 static
 void fromHLFto8(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
        *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
+
 }
 
 static
 void fromHLFto16(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
        *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
 }
 
 static
 void fromHLFtoFLT(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
 }
 
 static
 void fromHLFtoDBL(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
 }
 
 // From double
@@ -216,8 +251,13 @@
 static
 void fromDBLtoHLF(void* dst, const void* src)
 {
+#ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src;
        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
+#else
+    cmsUNUSED_PARAMETER(dst);
+    cmsUNUSED_PARAMETER(src);
+#endif
 }
 
 static
@@ -231,28 +271,29 @@
 static
 int FormatterPos(cmsUInt32Number frm)
 {
-       int  b = T_BYTES(frm);
+    cmsUInt32Number  b = T_BYTES(frm);
 
-       if (b == 0 && T_FLOAT(frm))
-              return 4; // DBL
-       if (b == 2 && T_FLOAT(frm))
-              return 2; // HLF
-       if (b == 4 && T_FLOAT(frm))
-              return 3; // FLT
-       if (b == 2 && !T_FLOAT(frm))
-              return 1; // 16
-       if (b == 1 && !T_FLOAT(frm))
-              return 0; // 8
+    if (b == 0 && T_FLOAT(frm))
+        return 4; // DBL
+#ifndef CMS_NO_HALF_SUPPORT
+    if (b == 2 && T_FLOAT(frm))
+        return 2; // HLF
+#endif
+    if (b == 4 && T_FLOAT(frm))
+        return 3; // FLT
+    if (b == 2 && !T_FLOAT(frm))
+        return 1; // 16
+    if (b == 1 && !T_FLOAT(frm))
+        return 0; // 8
 
-       return -1; // not recognized
-
+    return -1; // not recognized
 }
 
 // Obtains a alpha-to-alpha funmction formatter
 static
 cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
 {
-static cmsFormatterAlphaFn FormattersAlpha[5][5] = {
+static const cmsFormatterAlphaFn FormattersAlpha[5][5] = {
 
        /* from 8 */  { copy8,      from8to16,   from8toHLF,   from8toFLT,   from8toDBL   },
        /* from 16*/  { from16to8,  copy16,      from16toHLF,  from16toFLT,  from16toDBL  },
@@ -281,12 +322,12 @@
                                 cmsUInt32Number ComponentPointerIncrements[])
 {
        cmsUInt32Number channels[cmsMAXCHANNELS];
-       int extra = T_EXTRA(Format);
-       int nchannels = T_CHANNELS(Format);
-       int total_chans = nchannels + extra;
-       int i;       
-       int channelSize = trueBytesSize(Format);
-       int pixelSize = channelSize * total_chans;
+       cmsUInt32Number extra = T_EXTRA(Format);
+       cmsUInt32Number nchannels = T_CHANNELS(Format);
+       cmsUInt32Number total_chans = nchannels + extra;
+       cmsUInt32Number i;
+       cmsUInt32Number channelSize = trueBytesSize(Format);
+       cmsUInt32Number pixelSize = channelSize * total_chans;
        
 	   // Sanity check
 	   if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
@@ -339,11 +380,11 @@
                                 cmsUInt32Number ComponentPointerIncrements[])
 {
        cmsUInt32Number channels[cmsMAXCHANNELS];       
-       int extra = T_EXTRA(Format);
-       int nchannels = T_CHANNELS(Format);
-       int total_chans = nchannels + extra;
-       int i;
-       int channelSize = trueBytesSize(Format);
+       cmsUInt32Number extra = T_EXTRA(Format);
+       cmsUInt32Number nchannels = T_CHANNELS(Format);
+       cmsUInt32Number total_chans = nchannels + extra;
+       cmsUInt32Number i;
+       cmsUInt32Number channelSize = trueBytesSize(Format);
       
        // Sanity check
        if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
diff --git a/third_party/lcms/src/cmscam02.c b/third_party/lcms/src/cmscam02.c
index 5f0ac1f..9cc49fb 100644
--- a/third_party/lcms/src/cmscam02.c
+++ b/third_party/lcms/src/cmscam02.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
index 55f74ed..dba77f1 100644
--- a/third_party/lcms/src/cmscgats.c
+++ b/third_party/lcms/src/cmscgats.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -240,65 +240,64 @@
         {"PROD_DATE",        WRITE_STRINGIFY},   // Identifies year and month of production of the target in the form yyyy:mm.
         {"SERIAL",           WRITE_STRINGIFY},   // Uniquely identifies individual physical target.
 
-        {"MATERIAL",         WRITE_STRINGIFY},   // Identifies the material on which the target was produced using a code
-                               // uniquely identifying th e material. This is intend ed to be used for IT8.7
-                               // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
+        {"MATERIAL",         WRITE_STRINGIFY},    // Identifies the material on which the target was produced using a code
+                                                  // uniquely identifying th e material. This is intend ed to be used for IT8.7
+                                                  // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
 
-        {"INSTRUMENTATION",  WRITE_STRINGIFY},   // Used to report the specific instrumentation used (manufacturer and
-                               // model number) to generate the data reported. This data will often
-                               // provide more information about the particular data collected than an
-                               // extensive list of specific details. This is particularly important for
-                               // spectral data or data derived from spectrophotometry.
+        {"INSTRUMENTATION",  WRITE_STRINGIFY},    // Used to report the specific instrumentation used (manufacturer and
+                                                  // model number) to generate the data reported. This data will often
+                                                  // provide more information about the particular data collected than an
+                                                  // extensive list of specific details. This is particularly important for
+                                                  // spectral data or data derived from spectrophotometry.
 
-        {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide
-                               // a guide to the potential for issues of paper fluorescence, etc.
+        {"MEASUREMENT_SOURCE", WRITE_STRINGIFY},  // Illumination used for spectral measurements. This data helps provide
+                                                  // a guide to the potential for issues of paper fluorescence, etc.
 
-        {"PRINT_CONDITIONS", WRITE_STRINGIFY},   // Used to define the characteristics of the printed sheet being reported.
-                               // Where standard conditions have been defined (e.g., SWOP at nominal)
-                               // named conditions may suffice. Otherwise, detailed information is
-                               // needed.
+        {"PRINT_CONDITIONS", WRITE_STRINGIFY},     // Used to define the characteristics of the printed sheet being reported.
+                                                   // Where standard conditions have been defined (e.g., SWOP at nominal)
+                                                   // named conditions may suffice. Otherwise, detailed information is
+                                                   // needed.
 
-        {"SAMPLE_BACKING",   WRITE_STRINGIFY},   // Identifies the backing material used behind the sample during
-                               // measurement. Allowed values are "black" "white" or "na".
-
-        {"CHISQ_DOF",        WRITE_STRINGIFY},   // Degrees of freedom associated with the Chi squared statistic
-
-       // below properties are new in recent specs:
+        {"SAMPLE_BACKING",   WRITE_STRINGIFY},     // Identifies the backing material used behind the sample during
+                                                   // measurement. Allowed values are "black", "white", or {"na".
+                                                  
+        {"CHISQ_DOF",        WRITE_STRINGIFY},     // Degrees of freedom associated with the Chi squared statistic
+                                                   // below properties are new in recent specs:
 
         {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated
-                               // along with details of the geometry and the aperture size and shape. For example,
-                               // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
-                               // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
-                               // 45/0, sphere (specular included or excluded), etc.
+                                                   // along with details of the geometry and the aperture size and shape. For example,
+                                                   // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
+                                                   // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
+                                                   // 45/0, sphere (specular included or excluded), etc.
 
-       {"FILTER",            WRITE_STRINGIFY},   // Identifies the use of physical filter(s) during measurement. Typically used to
-                               // denote the use of filters such as none, D65, Red, Green or Blue.
+       {"FILTER",            WRITE_STRINGIFY},     // Identifies the use of physical filter(s) during measurement. Typically used to
+                                                   // denote the use of filters such as none, D65, Red, Green or Blue.
+                                                  
+       {"POLARIZATION",      WRITE_STRINGIFY},     // Identifies the use of a physical polarization filter during measurement. Allowed
+                                                   // values are "yes", "white", "none" or "na".
 
-       {"POLARIZATION",      WRITE_STRINGIFY},   // Identifies the use of a physical polarization filter during measurement. Allowed
-                               // values are "yes" "white" "none" or "na".
+       {"WEIGHTING_FUNCTION", WRITE_PAIR},         // Indicates such functions as: the CIE standard observer functions used in the
+                                                   // calculation of various data parameters (2 degree and 10 degree), CIE standard
+                                                   // illuminant functions used in the calculation of various data parameters (e.g., D50,
+                                                   // D65, etc.), density status response, etc. If used there shall be at least one
+                                                   // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
+                                                   // in the set shall be {"name" and shall identify the particular parameter used.
+                                                   // The second shall be {"value" and shall provide the value associated with that name.
+                                                   // For ASCII data, a string containing the Name and Value attribute pairs shall follow
+                                                   // the weighting function keyword. A semi-colon separates attribute pairs from each
+                                                   // other and within the attribute the name and value are separated by a comma.
 
-       {"WEIGHTING_FUNCTION", WRITE_PAIR},   // Indicates such functions as: the CIE standard observer functions used in the
-                               // calculation of various data parameters (2 degree and 10 degree), CIE standard
-                               // illuminant functions used in the calculation of various data parameters (e.g., D50,
-                               // D65, etc.), density status response, etc. If used there shall be at least one
-                               // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
-                               // in the set shall be {"name" and shall identify the particular parameter used.
-                               // The second shall be {"value" and shall provide the value associated with that name.
-                               // For ASCII data, a string containing the Name and Value attribute pairs shall follow
-                               // the weighting function keyword. A semi-colon separates attribute pairs from each
-                               // other and within the attribute the name and value are separated by a comma.
-
-       {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name
-                               // of the calculation, parameter is the name of the parameter used in the calculation
-                               // and value is the value of the parameter.
-
-       {"TARGET_TYPE",        WRITE_STRINGIFY},  // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
-
-       {"COLORANT",           WRITE_STRINGIFY},  // Identifies the colorant(s) used in creating the target.
-
-       {"TABLE_DESCRIPTOR",   WRITE_STRINGIFY},  // Describes the purpose or contents of a data table.
-
-       {"TABLE_NAME",         WRITE_STRINGIFY}   // Provides a short name for a data table.
+       {"COMPUTATIONAL_PARAMETER", WRITE_PAIR},    // Parameter that is used in computing a value from measured data. Name is the name
+                                                   // of the calculation, parameter is the name of the parameter used in the calculation
+                                                   // and value is the value of the parameter.
+                                                   
+       {"TARGET_TYPE",        WRITE_STRINGIFY},    // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
+                                                  
+       {"COLORANT",           WRITE_STRINGIFY},    // Identifies the colorant(s) used in creating the target.
+                                                  
+       {"TABLE_DESCRIPTOR",   WRITE_STRINGIFY},    // Describes the purpose or contents of a data table.
+                                                  
+       {"TABLE_NAME",         WRITE_STRINGIFY}     // Provides a short name for a data table.
 };
 
 #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
@@ -539,13 +538,13 @@
 
 //  Reads a Real number, tries to follow from integer number
 static
-void ReadReal(cmsIT8* it8, int inum)
+void ReadReal(cmsIT8* it8, cmsInt32Number inum)
 {
-    it8->dnum = (cmsFloat64Number) inum;
+    it8->dnum = (cmsFloat64Number)inum;
 
     while (isdigit(it8->ch)) {
 
-        it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
+        it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0');
         NextCh(it8);
     }
 
@@ -558,7 +557,7 @@
 
         while (isdigit(it8->ch)) {
 
-            frac = frac * 10.0 + (it8->ch - '0');
+            frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0');
             prec++;
             NextCh(it8);
         }
@@ -585,17 +584,19 @@
                 NextCh(it8);
             }
 
-            e = 0;
-            while (isdigit(it8->ch)) {
+        e = 0;
+        while (isdigit(it8->ch)) {
 
-                if ((cmsFloat64Number) e * 10L < (cmsFloat64Number) +2147483647.0)
-                    e = e * 10 + (it8->ch - '0');
+            cmsInt32Number digit = (it8->ch - '0');
 
-                NextCh(it8);
-            }
+            if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0)
+                e = e * 10 + digit;
 
-            e = sgn*e;
-            it8 -> dnum = it8 -> dnum * xpow10(e);
+            NextCh(it8);
+        }
+
+        e = sgn*e;
+        it8->dnum = it8->dnum * xpow10(e);
     }
 }
 
@@ -613,12 +614,12 @@
 
     if (*Buffer == '-' || *Buffer == '+') {
 
-         sign = (*Buffer == '-') ? -1 : 1;
-         Buffer++;
+        sign = (*Buffer == '-') ? -1 : 1;
+        Buffer++;
     }
 
 
-    while (*Buffer && isdigit((int) *Buffer)) {
+    while (*Buffer && isdigit((int)*Buffer)) {
 
         dnum = dnum * 10.0 + (*Buffer - '0');
         if (*Buffer) Buffer++;
@@ -627,11 +628,11 @@
     if (*Buffer == '.') {
 
         cmsFloat64Number frac = 0.0;      // fraction
-        int prec = 0;                     // precission
+        int prec = 0;                     // precision
 
         if (*Buffer) Buffer++;
 
-        while (*Buffer && isdigit((int) *Buffer)) {
+        while (*Buffer && isdigit((int)*Buffer)) {
 
             frac = frac * 10.0 + (*Buffer - '0');
             prec++;
@@ -662,17 +663,19 @@
                 if (*Buffer) Buffer++;
             }
 
-            e = 0;
-            while (*Buffer && isdigit((int) *Buffer)) {
+        e = 0;
+        while (*Buffer && isdigit((int)*Buffer)) {
 
-                if ((cmsFloat64Number) e * 10L < INT_MAX)
-                    e = e * 10 + (*Buffer - '0');
+            cmsInt32Number digit = (*Buffer - '0');
 
-                if (*Buffer) Buffer++;
-            }
+            if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0)
+                e = e * 10 + digit;
 
-            e = sgn*e;
-            dnum = dnum * xpow10(e);
+            if (*Buffer) Buffer++;
+        }
+
+        e = sgn*e;
+        dnum = dnum * xpow10(e);
     }
 
     return sign * dnum;
@@ -741,7 +744,7 @@
                             if (it8->ch >= 'A' && it8->ch <= 'F')  j = it8->ch -'A'+10;
                             else j = it8->ch - '0';
 
-                            if ((long) it8->inum * 16L > (long) INT_MAX)
+                            if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)
                             {
                                 SynError(it8, "Invalid hexadecimal number");
                                 return;
@@ -762,7 +765,7 @@
                         {
                             j = it8->ch - '0';
 
-                            if ((long) it8->inum * 2L > (long) INT_MAX)
+                            if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)
                             {
                                 SynError(it8, "Invalid binary number");
                                 return;
@@ -778,14 +781,16 @@
 
                 while (isdigit(it8->ch)) {
 
-                    if ((cmsFloat64Number) it8->inum * 10L > (cmsFloat64Number) +2147483647.0) {
+                    cmsInt32Number digit = (it8->ch - '0');
+
+                    if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) {
                         ReadReal(it8, it8->inum);
                         it8->sy = SDNUM;
                         it8->dnum *= sign;
                         return;
                     }
 
-                    it8->inum = it8->inum * 10 + (it8->ch - '0');
+                    it8->inum = it8->inum * 10 + digit;
                     NextCh(it8);
                 }
 
@@ -875,7 +880,7 @@
             k = 0;
             NextCh(it8);
 
-            while (k < MAXSTR && it8->ch != sng) {
+            while (k < (MAXSTR-1) && it8->ch != sng) {
 
                 if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
                 else {
@@ -1490,8 +1495,8 @@
 
 cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE  h, int n, const char *Sample)
 {
-        cmsIT8* it8 = (cmsIT8*) h;
-        return SetDataFormat(it8, n, Sample);
+    cmsIT8* it8 = (cmsIT8*)h;
+    return SetDataFormat(it8, n, Sample);
 }
 
 static
@@ -1504,10 +1509,16 @@
     t-> nSamples   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
     t-> nPatches   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
 
-    t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*));
-    if (t->Data == NULL) {
+    if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)
+    {
+        SynError(it8, "AllocateDataSet: too much data");
+    }
+    else {
+        t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
+        if (t->Data == NULL) {
 
-        SynError(it8, "AllocateDataSet: Unable to allocate data array");
+            SynError(it8, "AllocateDataSet: Unable to allocate data array");
+        }
     }
 
 }
@@ -1516,8 +1527,8 @@
 char* GetData(cmsIT8* it8, int nSet, int nField)
 {
     TABLE* t = GetTable(it8);
-    int  nSamples   = t -> nSamples;
-    int  nPatches   = t -> nPatches;
+    int nSamples    = t -> nSamples;
+    int nPatches    = t -> nPatches;
 
     if (nSet >= nPatches || nField >= nSamples)
         return NULL;
@@ -1948,67 +1959,67 @@
 
 
         case SIDENT:
-                strncpy(VarName, it8->id, MAXID-1);
-                VarName[MAXID-1] = 0;
+            strncpy(VarName, it8->id, MAXID - 1);
+            VarName[MAXID - 1] = 0;
 
-                if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) {
+            if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {
 
 #ifdef CMS_STRICT_CGATS
-                 return SynError(it8, "Undefined keyword '%s'", VarName);
+                return SynError(it8, "Undefined keyword '%s'", VarName);
 #else
-                    Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
-                    if (Key == NULL) return FALSE;
+                Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
+                if (Key == NULL) return FALSE;
 #endif
+            }
+
+            InSymbol(it8);
+            if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE;
+
+            if (Key->WriteAs != WRITE_PAIR) {
+                AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
+                    (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
+            }
+            else {
+                const char *Subkey;
+                char *Nextkey;
+                if (it8->sy != SSTRING)
+                    return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
+
+                // chop the string as a list of "subkey, value" pairs, using ';' as a separator
+                for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
+                {
+                    char *Value, *temp;
+
+                    //  identify token pair boundary
+                    Nextkey = (char*)strchr(Subkey, ';');
+                    if (Nextkey)
+                        *Nextkey++ = '\0';
+
+                    // for each pair, split the subkey and the value
+                    Value = (char*)strrchr(Subkey, ',');
+                    if (Value == NULL)
+                        return SynError(it8, "Invalid value for property '%s'.", VarName);
+
+                    // gobble the spaces before the coma, and the coma itself
+                    temp = Value++;
+                    do *temp-- = '\0'; while (temp >= Subkey && *temp == ' ');
+
+                    // gobble any space at the right
+                    temp = Value + strlen(Value) - 1;
+                    while (*temp == ' ') *temp-- = '\0';
+
+                    // trim the strings from the left
+                    Subkey += strspn(Subkey, " ");
+                    Value += strspn(Value, " ");
+
+                    if (Subkey[0] == 0 || Value[0] == 0)
+                        return SynError(it8, "Invalid value for property '%s'.", VarName);
+                    AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
                 }
+            }
 
-                InSymbol(it8);
-                if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE;
-
-                if(Key->WriteAs != WRITE_PAIR) {
-                    AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
-                                (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
-                }
-                else {
-                    const char *Subkey;
-                    char *Nextkey;
-                    if (it8->sy != SSTRING)
-                        return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
-
-                    // chop the string as a list of "subkey, value" pairs, using ';' as a separator
-                    for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
-                    {
-                        char *Value, *temp;
-
-                        //  identify token pair boundary
-                        Nextkey = (char*) strchr(Subkey, ';');
-                        if(Nextkey)
-                            *Nextkey++ = '\0';
-
-                        // for each pair, split the subkey and the value
-                        Value = (char*) strrchr(Subkey, ',');
-                        if(Value == NULL)
-                            return SynError(it8, "Invalid value for property '%s'.", VarName);
-
-                        // gobble the spaces before the coma, and the coma itself
-                        temp = Value++;
-                        do *temp-- = '\0'; while(temp >= Subkey && *temp == ' ');
-
-                        // gobble any space at the right
-                        temp = Value + strlen(Value) - 1;
-                        while(*temp == ' ') *temp-- = '\0';
-
-                        // trim the strings from the left
-                        Subkey += strspn(Subkey, " ");
-                        Value += strspn(Value, " ");
-
-                        if(Subkey[0] == 0 || Value[0] == 0)
-                            return SynError(it8, "Invalid value for property '%s'.", VarName);
-                        AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
-                    }
-                }
-
-                InSymbol(it8);
-                break;
+            InSymbol(it8);
+            break;
 
 
         case SEOLN: break;
@@ -2028,14 +2039,17 @@
 static
 void ReadType(cmsIT8* it8, char* SheetTypePtr)
 {
+    cmsInt32Number cnt = 0;
+
     // First line is a very special case.
 
     while (isseparator(it8->ch))
             NextCh(it8);
 
-    while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) {
+    while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) {
 
-        *SheetTypePtr++= (char) it8 ->ch;
+        if (cnt++ < MAXSTR) 
+            *SheetTypePtr++= (char) it8 ->ch;
         NextCh(it8);
     }
 
@@ -2228,10 +2242,10 @@
 // that should be something like some printable characters plus a \n
 // returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
 static
-int IsMyBlock(cmsUInt8Number* Buffer, int n)
+int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n)
 {
     int words = 1, space = 0, quot = 0;
-    int i;
+    cmsUInt32Number i;
 
     if (n < 10) return 0;   // Too small
 
@@ -2292,7 +2306,7 @@
 // ---------------------------------------------------------- Exported routines
 
 
-cmsHANDLE  CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len)
+cmsHANDLE  CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len)
 {
     cmsHANDLE hIT8;
     cmsIT8*  it8;
@@ -2301,7 +2315,7 @@
     _cmsAssert(Ptr != NULL);
     _cmsAssert(len != 0);
 
-    type = IsMyBlock((cmsUInt8Number*)Ptr, len);
+    type = IsMyBlock((const cmsUInt8Number*)Ptr, len);
     if (type == 0) return NULL;
 
     hIT8 = cmsIT8Alloc(ContextID);
@@ -2719,7 +2733,7 @@
 {
     const char* cLabelFld;
     char Type[256], Label[256];
-    int nTable;
+    cmsUInt32Number nTable;
 
     _cmsAssert(hIT8 != NULL);
 
@@ -2732,7 +2746,7 @@
     cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
     if (!cLabelFld) return -1;
 
-    if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3)
+    if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3)
             return -1;
 
     if (ExpectedType != NULL && *ExpectedType == 0)
diff --git a/third_party/lcms/src/cmscnvrt.c b/third_party/lcms/src/cmscnvrt.c
index 6c92148..082f1f8 100644
--- a/third_party/lcms/src/cmscnvrt.c
+++ b/third_party/lcms/src/cmscnvrt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -363,11 +363,12 @@
 
 // Compute the conversion layer
 static
-cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[],
-                                 cmsUInt32Number Intent,
-                                 cmsBool BPC,
-                                 cmsFloat64Number AdaptationState,
-                                 cmsMAT3* m, cmsVEC3* off)
+cmsBool ComputeConversion(cmsUInt32Number i, 
+                          cmsHPROFILE hProfiles[],
+                          cmsUInt32Number Intent,
+                          cmsBool BPC,
+                          cmsFloat64Number AdaptationState,
+                          cmsMAT3* m, cmsVEC3* off)
 {
 
     int k;
@@ -679,7 +680,7 @@
 
 // Translate black-preserving intents to ICC ones
 static
-int TranslateNonICCIntents(int Intent)
+cmsUInt32Number TranslateNonICCIntents(cmsUInt32Number Intent)
 {
     switch (Intent) {
         case INTENT_PRESERVE_K_ONLY_PERCEPTUAL:
diff --git a/third_party/lcms/src/cmserr.c b/third_party/lcms/src/cmserr.c
index 69b0550..d549f27 100644
--- a/third_party/lcms/src/cmserr.c
+++ b/third_party/lcms/src/cmserr.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -56,9 +56,11 @@
     long int p , n;
 
     p = ftell(f); // register current file position
+    if (p == -1L) 
+        return -1L;
 
     if (fseek(f, 0, SEEK_END) != 0) {
-        return -1;
+        return -1L;
     }
 
     n = ftell(f);
@@ -131,7 +133,7 @@
     }
     else {
 
-        // To reset it, we use the default allocators, which cannot be overriden
+        // To reset it, we use the default allocators, which cannot be overridden
         ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager;
     }
 }
diff --git a/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c
index 6e36cf4..b2d3e11 100644
--- a/third_party/lcms/src/cmsgamma.c
+++ b/third_party/lcms/src/cmsgamma.c
@@ -44,10 +44,11 @@
 // The list of supported parametric curves
 typedef struct _cmsParametricCurvesCollection_st {
 
-    int nFunctions;                                     // Number of supported functions in this chunk
-    int FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN];        // The identification types
-    int ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN];       // Number of parameters for each function
-    cmsParametricCurveEvaluator    Evaluator;           // The evaluator
+    cmsUInt32Number nFunctions;                                     // Number of supported functions in this chunk
+    cmsInt32Number  FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN];        // The identification types
+    cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN];       // Number of parameters for each function
+
+    cmsParametricCurveEvaluator Evaluator;                          // The evaluator
 
     struct _cmsParametricCurvesCollection_st* Next; // Next in list
 
@@ -57,7 +58,7 @@
 static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R);
 
 // The built-in list
-static _cmsParametricCurvesCollection DefaultCurves = {
+static const _cmsParametricCurvesCollection DefaultCurves = {
     9,                                  // # of curve types
     { 1, 2, 3, 4, 5, 6, 7, 8, 108 },    // Parametric curve ID
     { 1, 3, 4, 5, 7, 4, 5, 5, 1 },      // Parameters by type
@@ -161,11 +162,11 @@
 
 // Search in type list, return position or -1 if not found
 static
-int IsInSet(int Type, _cmsParametricCurvesCollection* c)
+int IsInSet(int Type, const _cmsParametricCurvesCollection* c)
 {
     int i;
 
-    for (i=0; i < c ->nFunctions; i++)
+    for (i=0; i < (int) c ->nFunctions; i++)
         if (abs(Type) == c ->FunctionTypes[i]) return i;
 
     return -1;
@@ -174,9 +175,9 @@
 
 // Search for the collection which contains a specific type
 static
-_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index)
+const _cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index)
 {
-    _cmsParametricCurvesCollection* c;
+    const _cmsParametricCurvesCollection* c;
     int Position;
     _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin);
 
@@ -209,20 +210,20 @@
 // no optimation curve is computed. nSegments may also be zero in the inverse case, where only the
 // optimization curve is given. Both features simultaneously is an error
 static
-cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntries,
-                                      cmsInt32Number nSegments, const cmsCurveSegment* Segments,
+cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries,
+                                      cmsUInt32Number nSegments, const cmsCurveSegment* Segments,
                                       const cmsUInt16Number* Values)
 {
     cmsToneCurve* p;
-    int i;
+    cmsUInt32Number i;
 
     // We allow huge tables, which are then restricted for smoothing operations
-    if (nEntries > 65530 || nEntries < 0) {
+    if (nEntries > 65530) {
         cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve of more than 65530 entries");
         return NULL;
     }
 
-    if (nEntries <= 0 && nSegments <= 0) {
+    if (nEntries == 0 && nSegments == 0) {
         cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve with zero segments and no table");
         return NULL;
     }
@@ -232,7 +233,7 @@
     if (!p) return NULL;
 
     // In this case, there are no segments
-    if (nSegments <= 0) {
+    if (nSegments == 0) {
         p ->Segments = NULL;
         p ->Evals = NULL;
     }
@@ -248,7 +249,7 @@
 
     // This 16-bit table contains a limited precision representation of the whole curve and is kept for
     // increasing xput on certain operations.
-    if (nEntries <= 0) {
+    if (nEntries == 0) {
         p ->Table16 = NULL;
     }
     else {
@@ -269,12 +270,12 @@
     // is placed in advance to maximize performance.
     if (Segments != NULL && (nSegments > 0)) {
 
-        _cmsParametricCurvesCollection *c;
+        const _cmsParametricCurvesCollection *c;
 
         p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*));
         if (p ->SegInterp == NULL) goto Error;
 
-        for (i=0; i< nSegments; i++) {
+        for (i=0; i < nSegments; i++) {
 
             // Type 0 is a special marker for table-based curves
             if (Segments[i].Type == 0)
@@ -330,7 +331,7 @@
 
     // Type 1 Reversed: X = Y ^1/gamma
     case -1:
-         if (R < 0) {
+        if (R < 0) {
 
             if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE)
                 Val = R;
@@ -338,80 +339,123 @@
                 Val = 0;
         }
         else
-            Val = pow(R, 1/Params[0]);
+        {
+            if (fabs(Params[0]) < MATRIX_DET_TOLERANCE)
+                Val = PLUS_INF;
+            else
+                Val = pow(R, 1 / Params[0]);
+        }
         break;
 
     // CIE 122-1966
     // Y = (aX + b)^Gamma  | X >= -b/a
     // Y = 0               | else
     case 2:
-        disc = -Params[2] / Params[1];
+    {
 
-        if (R >= disc ) {
+        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        {
+            Val = 0;
+        }
+        else
+        {
+            disc = -Params[2] / Params[1];
 
-            e = Params[1]*R + Params[2];
+            if (R >= disc) {
 
-            if (e > 0)
-                Val = pow(e, Params[0]);
+                e = Params[1] * R + Params[2];
+
+                if (e > 0)
+                    Val = pow(e, Params[0]);
+                else
+                    Val = 0;
+            }
             else
                 Val = 0;
         }
-        else
-            Val = 0;
-        break;
+    }
+    break;
 
      // Type 2 Reversed
      // X = (Y ^1/g  - b) / a
      case -2:
-         if (R < 0)
+     {
+         if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+             fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+         {
              Val = 0;
+         }
          else
-             Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
+         {
+             if (R < 0)
+                 Val = 0;
+             else
+                 Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1];
 
-         if (Val < 0)
-              Val = 0;
-         break;
+             if (Val < 0)
+                 Val = 0;
+         }
+     }         
+     break;
 
 
     // IEC 61966-3
     // Y = (aX + b)^Gamma | X <= -b/a
     // Y = c              | else
     case 3:
-        disc = -Params[2] / Params[1];
-        if (disc < 0)
-            disc = 0;
-
-        if (R >= disc) {
-
-            e = Params[1]*R + Params[2];
-
-            if (e > 0)
-                Val = pow(e, Params[0]) + Params[3];
-            else
-                Val = 0;
+    {
+        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        {
+            Val = 0;
         }
         else
-            Val = Params[3];
-        break;
+        {
+            disc = -Params[2] / Params[1];
+            if (disc < 0)
+                disc = 0;
+
+            if (R >= disc) {
+
+                e = Params[1] * R + Params[2];
+
+                if (e > 0)
+                    Val = pow(e, Params[0]) + Params[3];
+                else
+                    Val = 0;
+            }
+            else
+                Val = Params[3];
+        }
+    }
+    break;
 
 
     // Type 3 reversed
     // X=((Y-c)^1/g - b)/a      | (Y>=c)
     // X=-b/a                   | (Y<c)
     case -3:
-        if (R >= Params[3])  {
-
-            e = R - Params[3];
-
-            if (e > 0)
-                Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1];
-            else
-                Val = 0;
+    {
+        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        {
+            Val = 0;
         }
-        else {
-            Val = -Params[2] / Params[1];
+        else
+        {
+            if (R >= Params[3]) {
+
+                e = R - Params[3];
+
+                if (e > 0)
+                    Val = (pow(e, 1 / Params[0]) - Params[2]) / Params[1];
+                else
+                    Val = 0;
+            }
+            else {
+                Val = -Params[2] / Params[1];
+            }
         }
-        break;
+    }
+    break;
 
 
     // IEC 61966-2.1 (sRGB)
@@ -435,20 +479,31 @@
     // X=((Y^1/g-b)/a)    | Y >= (ad+b)^g
     // X=Y/c              | Y< (ad+b)^g
     case -4:
-        e = Params[1] * Params[4] + Params[2];
-        if (e < 0)
-            disc = 0;
+    {
+        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[1]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[3]) < MATRIX_DET_TOLERANCE)
+        {
+            Val = 0;
+        }
         else
-            disc = pow(e, Params[0]);
+        {
+            e = Params[1] * Params[4] + Params[2];
+            if (e < 0)
+                disc = 0;
+            else
+                disc = pow(e, Params[0]);
 
-        if (R >= disc) {
+            if (R >= disc) {
 
-            Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
+                Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1];
+            }
+            else {
+                Val = R / Params[3];
+            }
         }
-        else {
-            Val = R / Params[3];
-        }
-        break;
+    }
+    break;
 
 
     // Y = (aX + b)^Gamma + e | X >= d
@@ -472,20 +527,29 @@
     // X=((Y-e)1/g-b)/a   | Y >=(ad+b)^g+e), cd+f
     // X=(Y-f)/c          | else
     case -5:
-
-        disc = Params[3] * Params[4] + Params[6];
-        if (R >= disc) {
-
-            e = R - Params[5];
-            if (e < 0)
-                Val = 0;
-            else
-                Val = (pow(e, 1.0/Params[0]) - Params[2]) / Params[1];
+    {
+        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[3]) < MATRIX_DET_TOLERANCE)
+        {
+            Val = 0;
         }
-        else {
-            Val = (R - Params[6]) / Params[3];
+        else
+        {
+            disc = Params[3] * Params[4] + Params[6];
+            if (R >= disc) {
+
+                e = R - Params[5];
+                if (e < 0)
+                    Val = 0;
+                else
+                    Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1];
+            }
+            else {
+                Val = (R - Params[6]) / Params[3];
+            }
         }
-        break;
+    }
+    break;
 
 
     // Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf
@@ -503,12 +567,21 @@
 
     // ((Y - c) ^1/Gamma - b) / a
     case -6:
-        e = R - Params[3];
-        if (e < 0)
+    {
+        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        {
             Val = 0;
+        }
         else
-        Val = (pow(e, 1.0/Params[0]) - Params[2]) / Params[1];
-        break;
+        {
+            e = R - Params[3];
+            if (e < 0)
+                Val = 0;
+            else
+                Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1];
+        }
+    }
+    break;
 
 
     // Y = a * log (b * X^Gamma + c) + d
@@ -525,8 +598,19 @@
     // pow(10, (Y-d) / a) = b * X ^Gamma + c
     // pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X
     case -7:
-       Val = pow((pow(10.0, (R-Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]);
-       break;
+    {
+        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[1]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[2]) < MATRIX_DET_TOLERANCE)
+        {
+            Val = 0;
+        }
+        else
+        {
+            Val = pow((pow(10.0, (R - Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]);
+        }
+    }
+    break;
 
 
    //Y = a * b^(c*X+d) + e
@@ -542,12 +626,25 @@
        disc = R - Params[4];
        if (disc < 0) Val = 0;
        else
-           Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2];
+       {
+           if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+               fabs(Params[2]) < MATRIX_DET_TOLERANCE)
+           {
+               Val = 0;
+           }
+           else
+           {
+               Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2];
+           }
+       }
        break;
 
    // S-Shaped: (1 - (1-x)^1/g)^1/g
    case 108:
-      Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]);
+       if (fabs(Params[0]) < MATRIX_DET_TOLERANCE)
+           Val = 0;
+       else
+           Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]);
       break;
 
     // y = (1 - (1-x)^1/g)^1/g
@@ -567,33 +664,45 @@
     return Val;
 }
 
-// Evaluate a segmented function for a single value. Return -1 if no valid segment found .
+// Evaluate a segmented function for a single value. Return -Inf if no valid segment found .
 // If fn type is 0, perform an interpolation on the table
 static
 cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R)
 {
     int i;
+    cmsFloat32Number Out32;
+    cmsFloat64Number Out;
 
-    for (i = g ->nSegments-1; i >= 0 ; --i) {
+    for (i = (int) g->nSegments - 1; i >= 0; --i) {
 
         // Check for domain
-        if ((R > g ->Segments[i].x0) && (R <= g ->Segments[i].x1)) {
+        if ((R > g->Segments[i].x0) && (R <= g->Segments[i].x1)) {
 
             // Type == 0 means segment is sampled
-            if (g ->Segments[i].Type == 0) {
+            if (g->Segments[i].Type == 0) {
 
-                cmsFloat32Number R1 = (cmsFloat32Number) (R - g ->Segments[i].x0) / (g ->Segments[i].x1 - g ->Segments[i].x0);
-                cmsFloat32Number Out;
+                cmsFloat32Number R1 = (cmsFloat32Number)(R - g->Segments[i].x0) / (g->Segments[i].x1 - g->Segments[i].x0);
 
                 // Setup the table (TODO: clean that)
-                g ->SegInterp[i]-> Table = g ->Segments[i].SampledPoints;
+                g->SegInterp[i]->Table = g->Segments[i].SampledPoints;
 
-                g ->SegInterp[i] -> Interpolation.LerpFloat(&R1, &Out, g ->SegInterp[i]);
+                g->SegInterp[i]->Interpolation.LerpFloat(&R1, &Out32, g->SegInterp[i]);
+                Out = (cmsFloat64Number) Out32;
 
-                return Out;
             }
+            else {
+                Out = g->Evals[i](g->Segments[i].Type, g->Segments[i].Params, R);
+            }
+
+            if (isinf(Out))
+                return PLUS_INF;
             else
-                return g ->Evals[i](g->Segments[i].Type, g ->Segments[i].Params, R);
+            {
+                if (isinf(-Out))
+                    return MINUS_INF;
+            }
+
+            return Out;
         }
     }
 
@@ -616,13 +725,13 @@
 
 // Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the
 // floating point description empty.
-cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsInt32Number nEntries, const cmsUInt16Number Values[])
+cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number Values[])
 {
     return AllocateToneCurveStruct(ContextID, nEntries, 0, NULL, Values);
 }
 
 static
-int EntriesByGamma(cmsFloat64Number Gamma)
+cmsUInt32Number EntriesByGamma(cmsFloat64Number Gamma)
 {
     if (fabs(Gamma - 1.0) < 0.001) return 2;
     return 4096;
@@ -631,12 +740,12 @@
 
 // Create a segmented gamma, fill the table
 cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID,
-                                                   cmsInt32Number nSegments, const cmsCurveSegment Segments[])
+                                                   cmsUInt32Number nSegments, const cmsCurveSegment Segments[])
 {
-    int i;
+    cmsUInt32Number i;
     cmsFloat64Number R, Val;
     cmsToneCurve* g;
-    int nGridPoints = 4096;
+    cmsUInt32Number nGridPoints = 4096;
 
     _cmsAssert(Segments != NULL);
 
@@ -651,7 +760,7 @@
 
     // Once we have the floating point version, we can approximate a 16 bit table of 4096 entries
     // for performance reasons. This table would normally not be used except on 8/16 bits transforms.
-    for (i=0; i < nGridPoints; i++) {
+    for (i = 0; i < nGridPoints; i++) {
 
         R   = (cmsFloat64Number) i / (nGridPoints-1);
 
@@ -714,7 +823,7 @@
     cmsCurveSegment Seg0;
     int Pos = 0;
     cmsUInt32Number size;
-    _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos);
+    const _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos);
 
     _cmsAssert(Params != NULL);
 
@@ -882,7 +991,7 @@
     if (LutTable[0] < LutTable[p ->Domain[0]]) {
 
         // Table is overall ascending
-        for (i=p->Domain[0]-1; i >=0; --i) {
+        for (i = (int) p->Domain[0] - 1; i >= 0; --i) {
 
             y0 = LutTable[i];
             y1 = LutTable[i+1];
@@ -917,7 +1026,7 @@
 }
 
 // Reverse a gamma table
-cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, const cmsToneCurve* InCurve)
+cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InCurve)
 {
     cmsToneCurve *out;
     cmsFloat64Number a = 0, b = 0, y, x1, y1, x2, y2;
@@ -946,7 +1055,7 @@
     Ascending = !cmsIsToneCurveDescending(InCurve);
 
     // Iterate across Y axis
-    for (i=0; i <  nResultSamples; i++) {
+    for (i=0; i < (int) nResultSamples; i++) {
 
         y = (cmsFloat64Number) i * 65535.0 / (nResultSamples - 1);
 
@@ -1001,7 +1110,8 @@
 //   Output: smoothed vector (z): vector from 1 to m.
 
 static
-cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[], cmsFloat32Number z[], cmsFloat32Number lambda, int m)
+cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[], 
+                cmsFloat32Number z[], cmsFloat32Number lambda, int m)
 {
     int i, i1, i2;
     cmsFloat32Number *c, *d, *e;
@@ -1060,73 +1170,121 @@
 // Smooths a curve sampled at regular intervals.
 cmsBool  CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda)
 {
-    cmsFloat32Number w[MAX_NODES_IN_CURVE], y[MAX_NODES_IN_CURVE], z[MAX_NODES_IN_CURVE];
-    int i, nItems, Zeros, Poles;
+    cmsBool SuccessStatus = TRUE;
+    cmsFloat32Number *w, *y, *z;
+    cmsUInt32Number i, nItems, Zeros, Poles;
 
-    if (Tab == NULL) return FALSE;
-
-    if (cmsIsToneCurveLinear(Tab)) return TRUE; // Nothing to do
-
-    nItems = Tab -> nEntries;
-
-    if (nItems >= MAX_NODES_IN_CURVE) {
-        cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: too many points.");
-        return FALSE;
-    }
-
-    memset(w, 0, nItems * sizeof(cmsFloat32Number));
-    memset(y, 0, nItems * sizeof(cmsFloat32Number));
-    memset(z, 0, nItems * sizeof(cmsFloat32Number));
-
-    for (i=0; i < nItems; i++)
+    if (Tab != NULL && Tab->InterpParams != NULL)
     {
-        y[i+1] = (cmsFloat32Number) Tab -> Table16[i];
-        w[i+1] = 1.0;
-    }
+        cmsContext ContextID = Tab->InterpParams->ContextID;
 
-    if (!smooth2(Tab ->InterpParams->ContextID, w, y, z, (cmsFloat32Number) lambda, nItems)) return FALSE;
+        if (!cmsIsToneCurveLinear(Tab)) // Only non-linear curves need smoothing
+        {
+            nItems = Tab->nEntries;
+            if (nItems < MAX_NODES_IN_CURVE)
+            {
+                // Allocate one more item than needed
+                w = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number));
+                y = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number));
+                z = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number));
 
-    // Do some reality - checking...
-    Zeros = Poles = 0;
-    for (i=nItems; i > 1; --i) {
+                if (w != NULL && y != NULL && z != NULL) // Ensure no memory allocation failure
+                {
+                    memset(w, 0, (nItems + 1) * sizeof(cmsFloat32Number));
+                    memset(y, 0, (nItems + 1) * sizeof(cmsFloat32Number));
+                    memset(z, 0, (nItems + 1) * sizeof(cmsFloat32Number));
 
-        if (z[i] == 0.) Zeros++;
-        if (z[i] >= 65535.) Poles++;
-        if (z[i] < z[i-1]) {
-            cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic.");
-            return FALSE;
+                    for (i = 0; i < nItems; i++)
+                    {
+                        y[i + 1] = (cmsFloat32Number)Tab->Table16[i];
+                        w[i + 1] = 1.0;
+                    }
+
+                    if (smooth2(ContextID, w, y, z, (cmsFloat32Number)lambda, (int)nItems))
+                    {
+                        // Do some reality - checking...
+
+                        Zeros = Poles = 0;
+                        for (i = nItems; i > 1; --i)
+                        {
+                            if (z[i] == 0.) Zeros++;
+                            if (z[i] >= 65535.) Poles++;
+                            if (z[i] < z[i - 1])
+                            {
+                                cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic.");
+                                SuccessStatus = FALSE;
+                                break;
+                            }
+                        }
+
+                        if (SuccessStatus && Zeros > (nItems / 3))
+                        {
+                            cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros.");
+                            SuccessStatus = FALSE;
+                        }
+
+                        if (SuccessStatus && Poles > (nItems / 3))
+                        {
+                            cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles.");
+                            SuccessStatus = FALSE;
+                        }
+
+                        if (SuccessStatus) // Seems ok
+                        {
+                            for (i = 0; i < nItems; i++)
+                            {
+                                // Clamp to cmsUInt16Number
+                                Tab->Table16[i] = _cmsQuickSaturateWord(z[i + 1]);
+                            }
+                        }
+                    }
+                    else // Could not smooth
+                    {
+                        cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Function smooth2 failed.");
+                        SuccessStatus = FALSE;
+                    }
+                }
+                else // One or more buffers could not be allocated
+                {
+                    cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Could not allocate memory.");
+                    SuccessStatus = FALSE;
+                }
+
+                if (z != NULL)
+                    _cmsFree(ContextID, z);
+
+                if (y != NULL)
+                    _cmsFree(ContextID, y);
+
+                if (w != NULL)
+                    _cmsFree(ContextID, w);
+            }
+            else // too many items in the table
+            {
+                cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Too many points.");
+                SuccessStatus = FALSE;
+            }
         }
     }
-
-    if (Zeros > (nItems / 3)) {
-        cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros.");
-        return FALSE;
-    }
-    if (Poles > (nItems / 3)) {
-        cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles.");
-        return FALSE;
+    else // Tab parameter or Tab->InterpParams is NULL
+    {
+        // Can't signal an error here since the ContextID is not known at this point
+        SuccessStatus = FALSE;
     }
 
-    // Seems ok
-    for (i=0; i < nItems; i++) {
-
-        // Clamp to cmsUInt16Number
-        Tab -> Table16[i] = _cmsQuickSaturateWord(z[i+1]);
-    }
-
-    return TRUE;
+    return SuccessStatus;
 }
 
 // Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting
 // in a linear table. This way assures it is linear in 12 bits, which should be enought in most cases.
 cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve)
 {
-    cmsUInt32Number i;
+    int i;
     int diff;
 
     _cmsAssert(Curve != NULL);
 
-    for (i=0; i < Curve ->nEntries; i++) {
+    for (i=0; i < (int) Curve ->nEntries; i++) {
 
         diff = abs((int) Curve->Table16[i] - (int) _cmsQuantizeVal(i, Curve ->nEntries));
         if (diff > 0x0f)
@@ -1139,7 +1297,7 @@
 // Same, but for monotonicity
 cmsBool  CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t)
 {
-    int n;
+    cmsUInt32Number n;
     int i, last;
     cmsBool lDescending;
 
@@ -1156,7 +1314,7 @@
 
         last = t ->Table16[0];
 
-        for (i = 1; i < n; i++) {
+        for (i = 1; i < (int) n; i++) {
 
             if (t ->Table16[i] - last > 2) // We allow some ripple
                 return FALSE;
@@ -1169,7 +1327,7 @@
 
         last = t ->Table16[n-1];
 
-        for (i = n-2; i >= 0; --i) {
+        for (i = (int) n - 2; i >= 0; --i) {
 
             if (t ->Table16[i] - last > 2)
                 return FALSE;
diff --git a/third_party/lcms/src/cmsgmt.c b/third_party/lcms/src/cmsgmt.c
index b82f3be..027f201 100644
--- a/third_party/lcms/src/cmsgmt.c
+++ b/third_party/lcms/src/cmsgmt.c
@@ -1,590 +1,590 @@
-//---------------------------------------------------------------------------------

-//

-//  Little Color Management System

-//  Copyright (c) 1998-2016 Marti Maria Saguer

-//

-// Permission is hereby granted, free of charge, to any person obtaining

-// a copy of this software and associated documentation files (the "Software"),

-// to deal in the Software without restriction, including without limitation

-// the rights to use, copy, modify, merge, publish, distribute, sublicense,

-// and/or sell copies of the Software, and to permit persons to whom the Software

-// is furnished to do so, subject to the following conditions:

-//

-// The above copyright notice and this permission notice shall be included in

-// all copies or substantial portions of the Software.

-//

-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO

-// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE

-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION

-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION

-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

-//

-//---------------------------------------------------------------------------------

-//

-

-#include "lcms2_internal.h"

-

-

-// Auxiliary: append a Lab identity after the given sequence of profiles

-// and return the transform. Lab profile is closed, rest of profiles are kept open.

-cmsHTRANSFORM _cmsChain2Lab(cmsContext            ContextID,

-                            cmsUInt32Number        nProfiles,

-                            cmsUInt32Number        InputFormat,

-                            cmsUInt32Number        OutputFormat,

-                            const cmsUInt32Number  Intents[],

-                            const cmsHPROFILE      hProfiles[],

-                            const cmsBool          BPC[],

-                            const cmsFloat64Number AdaptationStates[],

-                            cmsUInt32Number        dwFlags)

-{

-    cmsHTRANSFORM xform;

-    cmsHPROFILE   hLab;

-    cmsHPROFILE   ProfileList[256];

-    cmsBool       BPCList[256];

-    cmsFloat64Number AdaptationList[256];

-    cmsUInt32Number IntentList[256];

-    cmsUInt32Number i;

-

-    // This is a rather big number and there is no need of dynamic memory

-    // since we are adding a profile, 254 + 1 = 255 and this is the limit

-    if (nProfiles > 254) return NULL;

-

-    // The output space

-    hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);

-    if (hLab == NULL) return NULL;

-

-    // Create a copy of parameters

-    for (i=0; i < nProfiles; i++) {

-

-        ProfileList[i]    = hProfiles[i];

-        BPCList[i]        = BPC[i];

-        AdaptationList[i] = AdaptationStates[i];

-        IntentList[i]     = Intents[i];

-    }

-

-    // Place Lab identity at chain's end.

-    ProfileList[nProfiles]    = hLab;

-    BPCList[nProfiles]        = 0;

-    AdaptationList[nProfiles] = 1.0;

-    IntentList[nProfiles]     = INTENT_RELATIVE_COLORIMETRIC;

-

-    // Create the transform

-    xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList,

-                                       BPCList,

-                                       IntentList,

-                                       AdaptationList,

-                                       NULL, 0,

-                                       InputFormat,

-                                       OutputFormat,

-                                       dwFlags);

-

-    cmsCloseProfile(hLab);

-

-    return xform;

-}

-

-

-// Compute K -> L* relationship. Flags may include black point compensation. In this case,

-// the relationship is assumed from the profile with BPC to a black point zero.

-static

-cmsToneCurve* ComputeKToLstar(cmsContext            ContextID,

-                               cmsUInt32Number       nPoints,

-                               cmsUInt32Number       nProfiles,

-                               const cmsUInt32Number Intents[],

-                               const cmsHPROFILE     hProfiles[],

-                               const cmsBool         BPC[],

-                               const cmsFloat64Number AdaptationStates[],

-                               cmsUInt32Number dwFlags)

-{

-    cmsToneCurve* out = NULL;

-    cmsUInt32Number i;

-    cmsHTRANSFORM xform;

-    cmsCIELab Lab;

-    cmsFloat32Number cmyk[4];

-    cmsFloat32Number* SampledPoints;

-

-    xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags);

-    if (xform == NULL) return NULL;

-

-    SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number));

-    if (SampledPoints  == NULL) goto Error;

-

-    for (i=0; i < nPoints; i++) {

-

-        cmyk[0] = 0;

-        cmyk[1] = 0;

-        cmyk[2] = 0;

-        cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1));

-

-        cmsDoTransform(xform, cmyk, &Lab, 1);

-        SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation

-    }

-

-    out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints);

-

-Error:

-

-    cmsDeleteTransform(xform);

-    if (SampledPoints) _cmsFree(ContextID, SampledPoints);

-

-    return out;

-}

-

-

-// Compute Black tone curve on a CMYK -> CMYK transform. This is done by

-// using the proof direction on both profiles to find K->L* relationship

-// then joining both curves. dwFlags may include black point compensation.

-cmsToneCurve* _cmsBuildKToneCurve(cmsContext        ContextID,

-                                   cmsUInt32Number   nPoints,

-                                   cmsUInt32Number   nProfiles,

-                                   const cmsUInt32Number Intents[],

-                                   const cmsHPROFILE hProfiles[],

-                                   const cmsBool     BPC[],

-                                   const cmsFloat64Number AdaptationStates[],

-                                   cmsUInt32Number   dwFlags)

-{

-    cmsToneCurve *in, *out, *KTone;

-

-    // Make sure CMYK -> CMYK

-    if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||

-        cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL;

-

-

-    // Make sure last is an output profile

-    if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL;

-

-    // Create individual curves. BPC works also as each K to L* is

-    // computed as a BPC to zero black point in case of L*

-    in  = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags);

-    if (in == NULL) return NULL;

-

-    out = ComputeKToLstar(ContextID, nPoints, 1,

-                            Intents + (nProfiles - 1),

-                            &hProfiles [nProfiles - 1],

-                            BPC + (nProfiles - 1),

-                            AdaptationStates + (nProfiles - 1),

-                            dwFlags);

-    if (out == NULL) {

-        cmsFreeToneCurve(in);

-        return NULL;

-    }

-

-    // Build the relationship. This effectively limits the maximum accuracy to 16 bits, but

-    // since this is used on black-preserving LUTs, we are not losing  accuracy in any case

-    KTone = cmsJoinToneCurve(ContextID, in, out, nPoints);

-

-    // Get rid of components

-    cmsFreeToneCurve(in); cmsFreeToneCurve(out);

-

-    // Something went wrong...

-    if (KTone == NULL) return NULL;

-

-    // Make sure it is monotonic

-    if (!cmsIsToneCurveMonotonic(KTone)) {

-        cmsFreeToneCurve(KTone);

-        return NULL;

-    }

-

-    return KTone;

-}

-

-

-// Gamut LUT Creation -----------------------------------------------------------------------------------------

-

-// Used by gamut & softproofing

-

-typedef struct {

-

-    cmsHTRANSFORM hInput;               // From whatever input color space. 16 bits to DBL

-    cmsHTRANSFORM hForward, hReverse;   // Transforms going from Lab to colorant and back

-    cmsFloat64Number Thereshold;        // The thereshold after which is considered out of gamut

-

-    } GAMUTCHAIN;

-

-// This sampler does compute gamut boundaries by comparing original

-// values with a transform going back and forth. Values above ERR_THERESHOLD

-// of maximum are considered out of gamut.

-

-#define ERR_THERESHOLD      5

-

-

-static

-int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)

-{

-    GAMUTCHAIN*  t = (GAMUTCHAIN* ) Cargo;

-    cmsCIELab LabIn1, LabOut1;

-    cmsCIELab LabIn2, LabOut2;

-    cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS];

-    cmsFloat64Number dE1, dE2, ErrorRatio;

-

-    // Assume in-gamut by default.

-    ErrorRatio = 1.0;

-

-    // Convert input to Lab

-    cmsDoTransform(t -> hInput, In, &LabIn1, 1);

-

-    // converts from PCS to colorant. This always

-    // does return in-gamut values,

-    cmsDoTransform(t -> hForward, &LabIn1, Proof, 1);

-

-    // Now, do the inverse, from colorant to PCS.

-    cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1);

-

-    memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab));

-

-    // Try again, but this time taking Check as input

-    cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1);

-    cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1);

-

-    // Take difference of direct value

-    dE1 = cmsDeltaE(&LabIn1, &LabOut1);

-

-    // Take difference of converted value

-    dE2 = cmsDeltaE(&LabIn2, &LabOut2);

-

-

-    // if dE1 is small and dE2 is small, value is likely to be in gamut

-    if (dE1 < t->Thereshold && dE2 < t->Thereshold)

-        Out[0] = 0;

-    else {

-

-        // if dE1 is small and dE2 is big, undefined. Assume in gamut

-        if (dE1 < t->Thereshold && dE2 > t->Thereshold)

-            Out[0] = 0;

-        else

-            // dE1 is big and dE2 is small, clearly out of gamut

-            if (dE1 > t->Thereshold && dE2 < t->Thereshold)

-                Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5);

-            else  {

-

-                // dE1 is big and dE2 is also big, could be due to perceptual mapping

-                // so take error ratio

-                if (dE2 == 0.0)

-                    ErrorRatio = dE1;

-                else

-                    ErrorRatio = dE1 / dE2;

-

-                if (ErrorRatio > t->Thereshold)

-                    Out[0] = (cmsUInt16Number)  _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);

-                else

-                    Out[0] = 0;

-            }

-    }

-

-

-    return TRUE;

-}

-

-// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs

-// the dE obtained is then annotated on the LUT. Values truly out of gamut are clipped to dE = 0xFFFE

-// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well.

-//

-// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors,

-// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should.

-

-cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,

-                                          cmsHPROFILE hProfiles[],

-                                          cmsBool  BPC[],

-                                          cmsUInt32Number Intents[],

-                                          cmsFloat64Number AdaptationStates[],

-                                          cmsUInt32Number nGamutPCSposition,

-                                          cmsHPROFILE hGamut)

-{

-    cmsHPROFILE hLab;

-    cmsPipeline* Gamut;

-    cmsStage* CLUT;

-    cmsUInt32Number dwFormat;

-    GAMUTCHAIN Chain;

-    int nChannels, nGridpoints;

-    cmsColorSpaceSignature ColorSpace;

-    cmsUInt32Number i;

-    cmsHPROFILE ProfileList[256];

-    cmsBool     BPCList[256];

-    cmsFloat64Number AdaptationList[256];

-    cmsUInt32Number IntentList[256];

-

-    memset(&Chain, 0, sizeof(GAMUTCHAIN));

-

-

-    if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) {

-        cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition);

-        return NULL;

-    }

-

-    hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);

-    if (hLab == NULL) return NULL;

-

-

-    // The figure of merit. On matrix-shaper profiles, should be almost zero as

-    // the conversion is pretty exact. On LUT based profiles, different resolutions

-    // of input and output CLUT may result in differences.

-

-    if (cmsIsMatrixShaper(hGamut)) {

-

-        Chain.Thereshold = 1.0;

-    }

-    else {

-        Chain.Thereshold = ERR_THERESHOLD;

-    }

-

-

-    // Create a copy of parameters

-    for (i=0; i < nGamutPCSposition; i++) {

-        ProfileList[i]    = hProfiles[i];

-        BPCList[i]        = BPC[i];

-        AdaptationList[i] = AdaptationStates[i];

-        IntentList[i]     = Intents[i];

-    }

-

-    // Fill Lab identity

-    ProfileList[nGamutPCSposition] = hLab;

-    BPCList[nGamutPCSposition] = 0;

-    AdaptationList[nGamutPCSposition] = 1.0;

-    IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC;

-

-

-    ColorSpace  = cmsGetColorSpace(hGamut);

-

-    nChannels   = cmsChannelsOf(ColorSpace);

-    nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);

-    dwFormat    = (CHANNELS_SH(nChannels)|BYTES_SH(2));

-

-    // 16 bits to Lab double

-    Chain.hInput = cmsCreateExtendedTransform(ContextID,

-        nGamutPCSposition + 1,

-        ProfileList,

-        BPCList,

-        IntentList,

-        AdaptationList,

-        NULL, 0,

-        dwFormat, TYPE_Lab_DBL,

-        cmsFLAGS_NOCACHE);

-

-

-    // Does create the forward step. Lab double to device

-    dwFormat    = (CHANNELS_SH(nChannels)|BYTES_SH(2));

-    Chain.hForward = cmsCreateTransformTHR(ContextID,

-        hLab, TYPE_Lab_DBL,

-        hGamut, dwFormat,

-        INTENT_RELATIVE_COLORIMETRIC,

-        cmsFLAGS_NOCACHE);

-

-    // Does create the backwards step

-    Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat,

-        hLab, TYPE_Lab_DBL,

-        INTENT_RELATIVE_COLORIMETRIC,

-        cmsFLAGS_NOCACHE);

-

-

-    // All ok?

-    if (Chain.hInput && Chain.hForward && Chain.hReverse) {

-

-        // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing

-        // dE when doing a transform back and forth on the colorimetric intent.

-

-        Gamut = cmsPipelineAlloc(ContextID, 3, 1);

-        if (Gamut != NULL) {

-

-            CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL);

-            if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) {

-                cmsPipelineFree(Gamut);

-                Gamut = NULL;

-            } 

-            else {

-                cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0);

-            }

-        }

-    }

-    else

-        Gamut = NULL;   // Didn't work...

-

-    // Free all needed stuff.

-    if (Chain.hInput)   cmsDeleteTransform(Chain.hInput);

-    if (Chain.hForward) cmsDeleteTransform(Chain.hForward);

-    if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);

-    if (hLab) cmsCloseProfile(hLab);

-

-    // And return computed hull

-    return Gamut;

-}

-

-// Total Area Coverage estimation ----------------------------------------------------------------

-

-typedef struct {

-    cmsUInt32Number  nOutputChans;

-    cmsHTRANSFORM    hRoundTrip;

-    cmsFloat32Number MaxTAC;

-    cmsFloat32Number MaxInput[cmsMAXCHANNELS];

-

-} cmsTACestimator;

-

-

-// This callback just accounts the maximum ink dropped in the given node. It does not populate any

-// memory, as the destination table is NULL. Its only purpose it to know the global maximum.

-static

-int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo)

-{

-    cmsTACestimator* bp = (cmsTACestimator*) Cargo;

-    cmsFloat32Number RoundTrip[cmsMAXCHANNELS];

-    cmsUInt32Number i;

-    cmsFloat32Number Sum;

-

-

-    // Evaluate the xform

-    cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);

-

-    // All all amounts of ink

-    for (Sum=0, i=0; i < bp ->nOutputChans; i++)

-            Sum += RoundTrip[i];

-

-    // If above maximum, keep track of input values

-    if (Sum > bp ->MaxTAC) {

-

-            bp ->MaxTAC = Sum;

-

-            for (i=0; i < bp ->nOutputChans; i++) {

-                bp ->MaxInput[i] = In[i];

-            }

-    }

-

-    return TRUE;

-

-    cmsUNUSED_PARAMETER(Out);

-}

-

-

-// Detect Total area coverage of the profile

-cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile)

-{

-    cmsTACestimator bp;

-    cmsUInt32Number dwFormatter;

-    cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS];

-    cmsHPROFILE hLab;

-    cmsContext ContextID = cmsGetProfileContextID(hProfile);

-

-    // TAC only works on output profiles

-    if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) {

-        return 0;

-    }

-

-    // Create a fake formatter for result

-    dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE);

-

-    bp.nOutputChans = T_CHANNELS(dwFormatter);

-    bp.MaxTAC = 0;    // Initial TAC is 0

-

-    //  for safety

-    if (bp.nOutputChans >= cmsMAXCHANNELS) return 0;

-

-    hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);

-    if (hLab == NULL) return 0;

-    // Setup a roundtrip on perceptual intent in output profile for TAC estimation

-    bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16,

-                                          hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);

-

-    cmsCloseProfile(hLab);

-    if (bp.hRoundTrip == NULL) return 0;

-

-    // For L* we only need black and white. For C* we need many points

-    GridPoints[0] = 6;

-    GridPoints[1] = 74;

-    GridPoints[2] = 74;

-

-

-    if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) {

-        bp.MaxTAC = 0;

-    }

-

-    cmsDeleteTransform(bp.hRoundTrip);

-

-    // Results in %

-    return bp.MaxTAC;

-}

-

-

-// Carefully,  clamp on CIELab space.

-

-cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab,

-                                   double amax, double amin,

-                                   double bmax, double bmin)

-{

-

-    // Whole Luma surface to zero

-

-    if (Lab -> L < 0) {

-

-        Lab-> L = Lab->a = Lab-> b = 0.0;

-        return FALSE;

-    }

-

-    // Clamp white, DISCARD HIGHLIGHTS. This is done

-    // in such way because icc spec doesn't allow the

-    // use of L>100 as a highlight means.

-

-    if (Lab->L > 100)

-        Lab -> L = 100;

-

-    // Check out gamut prism, on a, b faces

-

-    if (Lab -> a < amin || Lab->a > amax||

-        Lab -> b < bmin || Lab->b > bmax) {

-

-            cmsCIELCh LCh;

-            double h, slope;

-

-            // Falls outside a, b limits. Transports to LCh space,

-            // and then do the clipping

-

-

-            if (Lab -> a == 0.0) { // Is hue exactly 90?

-

-                // atan will not work, so clamp here

-                Lab -> b = Lab->b < 0 ? bmin : bmax;

-                return TRUE;

-            }

-

-            cmsLab2LCh(&LCh, Lab);

-

-            slope = Lab -> b / Lab -> a;

-            h = LCh.h;

-

-            // There are 4 zones

-

-            if ((h >= 0. && h < 45.) ||

-                (h >= 315 && h <= 360.)) {

-

-                    // clip by amax

-                    Lab -> a = amax;

-                    Lab -> b = amax * slope;

-            }

-            else

-                if (h >= 45. && h < 135.)

-                {

-                    // clip by bmax

-                    Lab -> b = bmax;

-                    Lab -> a = bmax / slope;

-                }

-                else

-                    if (h >= 135. && h < 225.) {

-                        // clip by amin

-                        Lab -> a = amin;

-                        Lab -> b = amin * slope;

-

-                    }

-                    else

-                        if (h >= 225. && h < 315.) {

-                            // clip by bmin

-                            Lab -> b = bmin;

-                            Lab -> a = bmin / slope;

-                        }

-                        else  {

-                            cmsSignalError(0, cmsERROR_RANGE, "Invalid angle");

-                            return FALSE;

-                        }

-

-    }

-

-    return TRUE;

-}

+//---------------------------------------------------------------------------------
+//
+//  Little Color Management System
+//  Copyright (c) 1998-2017 Marti Maria Saguer
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//---------------------------------------------------------------------------------
+//
+
+#include "lcms2_internal.h"
+
+
+// Auxiliary: append a Lab identity after the given sequence of profiles
+// and return the transform. Lab profile is closed, rest of profiles are kept open.
+cmsHTRANSFORM _cmsChain2Lab(cmsContext            ContextID,
+                            cmsUInt32Number        nProfiles,
+                            cmsUInt32Number        InputFormat,
+                            cmsUInt32Number        OutputFormat,
+                            const cmsUInt32Number  Intents[],
+                            const cmsHPROFILE      hProfiles[],
+                            const cmsBool          BPC[],
+                            const cmsFloat64Number AdaptationStates[],
+                            cmsUInt32Number        dwFlags)
+{
+    cmsHTRANSFORM xform;
+    cmsHPROFILE   hLab;
+    cmsHPROFILE   ProfileList[256];
+    cmsBool       BPCList[256];
+    cmsFloat64Number AdaptationList[256];
+    cmsUInt32Number IntentList[256];
+    cmsUInt32Number i;
+
+    // This is a rather big number and there is no need of dynamic memory
+    // since we are adding a profile, 254 + 1 = 255 and this is the limit
+    if (nProfiles > 254) return NULL;
+
+    // The output space
+    hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
+    if (hLab == NULL) return NULL;
+
+    // Create a copy of parameters
+    for (i=0; i < nProfiles; i++) {
+
+        ProfileList[i]    = hProfiles[i];
+        BPCList[i]        = BPC[i];
+        AdaptationList[i] = AdaptationStates[i];
+        IntentList[i]     = Intents[i];
+    }
+
+    // Place Lab identity at chain's end.
+    ProfileList[nProfiles]    = hLab;
+    BPCList[nProfiles]        = 0;
+    AdaptationList[nProfiles] = 1.0;
+    IntentList[nProfiles]     = INTENT_RELATIVE_COLORIMETRIC;
+
+    // Create the transform
+    xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList,
+                                       BPCList,
+                                       IntentList,
+                                       AdaptationList,
+                                       NULL, 0,
+                                       InputFormat,
+                                       OutputFormat,
+                                       dwFlags);
+
+    cmsCloseProfile(hLab);
+
+    return xform;
+}
+
+
+// Compute K -> L* relationship. Flags may include black point compensation. In this case,
+// the relationship is assumed from the profile with BPC to a black point zero.
+static
+cmsToneCurve* ComputeKToLstar(cmsContext            ContextID,
+                               cmsUInt32Number       nPoints,
+                               cmsUInt32Number       nProfiles,
+                               const cmsUInt32Number Intents[],
+                               const cmsHPROFILE     hProfiles[],
+                               const cmsBool         BPC[],
+                               const cmsFloat64Number AdaptationStates[],
+                               cmsUInt32Number dwFlags)
+{
+    cmsToneCurve* out = NULL;
+    cmsUInt32Number i;
+    cmsHTRANSFORM xform;
+    cmsCIELab Lab;
+    cmsFloat32Number cmyk[4];
+    cmsFloat32Number* SampledPoints;
+
+    xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
+    if (xform == NULL) return NULL;
+
+    SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number));
+    if (SampledPoints  == NULL) goto Error;
+
+    for (i=0; i < nPoints; i++) {
+
+        cmyk[0] = 0;
+        cmyk[1] = 0;
+        cmyk[2] = 0;
+        cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1));
+
+        cmsDoTransform(xform, cmyk, &Lab, 1);
+        SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation
+    }
+
+    out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints);
+
+Error:
+
+    cmsDeleteTransform(xform);
+    if (SampledPoints) _cmsFree(ContextID, SampledPoints);
+
+    return out;
+}
+
+
+// Compute Black tone curve on a CMYK -> CMYK transform. This is done by
+// using the proof direction on both profiles to find K->L* relationship
+// then joining both curves. dwFlags may include black point compensation.
+cmsToneCurve* _cmsBuildKToneCurve(cmsContext        ContextID,
+                                   cmsUInt32Number   nPoints,
+                                   cmsUInt32Number   nProfiles,
+                                   const cmsUInt32Number Intents[],
+                                   const cmsHPROFILE hProfiles[],
+                                   const cmsBool     BPC[],
+                                   const cmsFloat64Number AdaptationStates[],
+                                   cmsUInt32Number   dwFlags)
+{
+    cmsToneCurve *in, *out, *KTone;
+
+    // Make sure CMYK -> CMYK
+    if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
+        cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL;
+
+
+    // Make sure last is an output profile
+    if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL;
+
+    // Create individual curves. BPC works also as each K to L* is
+    // computed as a BPC to zero black point in case of L*
+    in  = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
+    if (in == NULL) return NULL;
+
+    out = ComputeKToLstar(ContextID, nPoints, 1,
+                            Intents + (nProfiles - 1),
+                            &hProfiles [nProfiles - 1],
+                            BPC + (nProfiles - 1),
+                            AdaptationStates + (nProfiles - 1),
+                            dwFlags);
+    if (out == NULL) {
+        cmsFreeToneCurve(in);
+        return NULL;
+    }
+
+    // Build the relationship. This effectively limits the maximum accuracy to 16 bits, but
+    // since this is used on black-preserving LUTs, we are not losing  accuracy in any case
+    KTone = cmsJoinToneCurve(ContextID, in, out, nPoints);
+
+    // Get rid of components
+    cmsFreeToneCurve(in); cmsFreeToneCurve(out);
+
+    // Something went wrong...
+    if (KTone == NULL) return NULL;
+
+    // Make sure it is monotonic
+    if (!cmsIsToneCurveMonotonic(KTone)) {
+        cmsFreeToneCurve(KTone);
+        return NULL;
+    }
+
+    return KTone;
+}
+
+
+// Gamut LUT Creation -----------------------------------------------------------------------------------------
+
+// Used by gamut & softproofing
+
+typedef struct {
+
+    cmsHTRANSFORM hInput;               // From whatever input color space. 16 bits to DBL
+    cmsHTRANSFORM hForward, hReverse;   // Transforms going from Lab to colorant and back
+    cmsFloat64Number Thereshold;        // The thereshold after which is considered out of gamut
+
+    } GAMUTCHAIN;
+
+// This sampler does compute gamut boundaries by comparing original
+// values with a transform going back and forth. Values above ERR_THERESHOLD
+// of maximum are considered out of gamut.
+
+#define ERR_THERESHOLD      5
+
+
+static
+int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+{
+    GAMUTCHAIN*  t = (GAMUTCHAIN* ) Cargo;
+    cmsCIELab LabIn1, LabOut1;
+    cmsCIELab LabIn2, LabOut2;
+    cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS];
+    cmsFloat64Number dE1, dE2, ErrorRatio;
+
+    // Assume in-gamut by default.
+    ErrorRatio = 1.0;
+
+    // Convert input to Lab
+    cmsDoTransform(t -> hInput, In, &LabIn1, 1);
+
+    // converts from PCS to colorant. This always
+    // does return in-gamut values,
+    cmsDoTransform(t -> hForward, &LabIn1, Proof, 1);
+
+    // Now, do the inverse, from colorant to PCS.
+    cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1);
+
+    memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab));
+
+    // Try again, but this time taking Check as input
+    cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1);
+    cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1);
+
+    // Take difference of direct value
+    dE1 = cmsDeltaE(&LabIn1, &LabOut1);
+
+    // Take difference of converted value
+    dE2 = cmsDeltaE(&LabIn2, &LabOut2);
+
+
+    // if dE1 is small and dE2 is small, value is likely to be in gamut
+    if (dE1 < t->Thereshold && dE2 < t->Thereshold)
+        Out[0] = 0;
+    else {
+
+        // if dE1 is small and dE2 is big, undefined. Assume in gamut
+        if (dE1 < t->Thereshold && dE2 > t->Thereshold)
+            Out[0] = 0;
+        else
+            // dE1 is big and dE2 is small, clearly out of gamut
+            if (dE1 > t->Thereshold && dE2 < t->Thereshold)
+                Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
+            else  {
+
+                // dE1 is big and dE2 is also big, could be due to perceptual mapping
+                // so take error ratio
+                if (dE2 == 0.0)
+                    ErrorRatio = dE1;
+                else
+                    ErrorRatio = dE1 / dE2;
+
+                if (ErrorRatio > t->Thereshold)
+                    Out[0] = (cmsUInt16Number)  _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
+                else
+                    Out[0] = 0;
+            }
+    }
+
+
+    return TRUE;
+}
+
+// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs
+// the dE obtained is then annotated on the LUT. Values truly out of gamut are clipped to dE = 0xFFFE
+// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well.
+//
+// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors,
+// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should.
+
+cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
+                                          cmsHPROFILE hProfiles[],
+                                          cmsBool  BPC[],
+                                          cmsUInt32Number Intents[],
+                                          cmsFloat64Number AdaptationStates[],
+                                          cmsUInt32Number nGamutPCSposition,
+                                          cmsHPROFILE hGamut)
+{
+    cmsHPROFILE hLab;
+    cmsPipeline* Gamut;
+    cmsStage* CLUT;
+    cmsUInt32Number dwFormat;
+    GAMUTCHAIN Chain;
+    cmsUInt32Number nChannels, nGridpoints;
+    cmsColorSpaceSignature ColorSpace;
+    cmsUInt32Number i;
+    cmsHPROFILE ProfileList[256];
+    cmsBool     BPCList[256];
+    cmsFloat64Number AdaptationList[256];
+    cmsUInt32Number IntentList[256];
+
+    memset(&Chain, 0, sizeof(GAMUTCHAIN));
+
+
+    if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) {
+        cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition);
+        return NULL;
+    }
+
+    hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
+    if (hLab == NULL) return NULL;
+
+
+    // The figure of merit. On matrix-shaper profiles, should be almost zero as
+    // the conversion is pretty exact. On LUT based profiles, different resolutions
+    // of input and output CLUT may result in differences.
+
+    if (cmsIsMatrixShaper(hGamut)) {
+
+        Chain.Thereshold = 1.0;
+    }
+    else {
+        Chain.Thereshold = ERR_THERESHOLD;
+    }
+
+
+    // Create a copy of parameters
+    for (i=0; i < nGamutPCSposition; i++) {
+        ProfileList[i]    = hProfiles[i];
+        BPCList[i]        = BPC[i];
+        AdaptationList[i] = AdaptationStates[i];
+        IntentList[i]     = Intents[i];
+    }
+
+    // Fill Lab identity
+    ProfileList[nGamutPCSposition] = hLab;
+    BPCList[nGamutPCSposition] = 0;
+    AdaptationList[nGamutPCSposition] = 1.0;
+    IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC;
+
+
+    ColorSpace  = cmsGetColorSpace(hGamut);
+
+    nChannels   = cmsChannelsOf(ColorSpace);
+    nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
+    dwFormat    = (CHANNELS_SH(nChannels)|BYTES_SH(2));
+
+    // 16 bits to Lab double
+    Chain.hInput = cmsCreateExtendedTransform(ContextID,
+        nGamutPCSposition + 1,
+        ProfileList,
+        BPCList,
+        IntentList,
+        AdaptationList,
+        NULL, 0,
+        dwFormat, TYPE_Lab_DBL,
+        cmsFLAGS_NOCACHE);
+
+
+    // Does create the forward step. Lab double to device
+    dwFormat    = (CHANNELS_SH(nChannels)|BYTES_SH(2));
+    Chain.hForward = cmsCreateTransformTHR(ContextID,
+        hLab, TYPE_Lab_DBL,
+        hGamut, dwFormat,
+        INTENT_RELATIVE_COLORIMETRIC,
+        cmsFLAGS_NOCACHE);
+
+    // Does create the backwards step
+    Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat,
+        hLab, TYPE_Lab_DBL,
+        INTENT_RELATIVE_COLORIMETRIC,
+        cmsFLAGS_NOCACHE);
+
+
+    // All ok?
+    if (Chain.hInput && Chain.hForward && Chain.hReverse) {
+
+        // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing
+        // dE when doing a transform back and forth on the colorimetric intent.
+
+        Gamut = cmsPipelineAlloc(ContextID, 3, 1);
+        if (Gamut != NULL) {
+
+            CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL);
+            if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) {
+                cmsPipelineFree(Gamut);
+                Gamut = NULL;
+            } 
+            else {
+                cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0);
+            }
+        }
+    }
+    else
+        Gamut = NULL;   // Didn't work...
+
+    // Free all needed stuff.
+    if (Chain.hInput)   cmsDeleteTransform(Chain.hInput);
+    if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
+    if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
+    if (hLab) cmsCloseProfile(hLab);
+
+    // And return computed hull
+    return Gamut;
+}
+
+// Total Area Coverage estimation ----------------------------------------------------------------
+
+typedef struct {
+    cmsUInt32Number  nOutputChans;
+    cmsHTRANSFORM    hRoundTrip;
+    cmsFloat32Number MaxTAC;
+    cmsFloat32Number MaxInput[cmsMAXCHANNELS];
+
+} cmsTACestimator;
+
+
+// This callback just accounts the maximum ink dropped in the given node. It does not populate any
+// memory, as the destination table is NULL. Its only purpose it to know the global maximum.
+static
+int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo)
+{
+    cmsTACestimator* bp = (cmsTACestimator*) Cargo;
+    cmsFloat32Number RoundTrip[cmsMAXCHANNELS];
+    cmsUInt32Number i;
+    cmsFloat32Number Sum;
+
+
+    // Evaluate the xform
+    cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
+
+    // All all amounts of ink
+    for (Sum=0, i=0; i < bp ->nOutputChans; i++)
+            Sum += RoundTrip[i];
+
+    // If above maximum, keep track of input values
+    if (Sum > bp ->MaxTAC) {
+
+            bp ->MaxTAC = Sum;
+
+            for (i=0; i < bp ->nOutputChans; i++) {
+                bp ->MaxInput[i] = In[i];
+            }
+    }
+
+    return TRUE;
+
+    cmsUNUSED_PARAMETER(Out);
+}
+
+
+// Detect Total area coverage of the profile
+cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile)
+{
+    cmsTACestimator bp;
+    cmsUInt32Number dwFormatter;
+    cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS];
+    cmsHPROFILE hLab;
+    cmsContext ContextID = cmsGetProfileContextID(hProfile);
+
+    // TAC only works on output profiles
+    if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) {
+        return 0;
+    }
+
+    // Create a fake formatter for result
+    dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE);
+
+    bp.nOutputChans = T_CHANNELS(dwFormatter);
+    bp.MaxTAC = 0;    // Initial TAC is 0
+
+    //  for safety
+    if (bp.nOutputChans >= cmsMAXCHANNELS) return 0;
+
+    hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
+    if (hLab == NULL) return 0;
+    // Setup a roundtrip on perceptual intent in output profile for TAC estimation
+    bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16,
+                                          hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
+
+    cmsCloseProfile(hLab);
+    if (bp.hRoundTrip == NULL) return 0;
+
+    // For L* we only need black and white. For C* we need many points
+    GridPoints[0] = 6;
+    GridPoints[1] = 74;
+    GridPoints[2] = 74;
+
+
+    if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) {
+        bp.MaxTAC = 0;
+    }
+
+    cmsDeleteTransform(bp.hRoundTrip);
+
+    // Results in %
+    return bp.MaxTAC;
+}
+
+
+// Carefully,  clamp on CIELab space.
+
+cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab,
+                                   double amax, double amin,
+                                   double bmax, double bmin)
+{
+
+    // Whole Luma surface to zero
+
+    if (Lab -> L < 0) {
+
+        Lab-> L = Lab->a = Lab-> b = 0.0;
+        return FALSE;
+    }
+
+    // Clamp white, DISCARD HIGHLIGHTS. This is done
+    // in such way because icc spec doesn't allow the
+    // use of L>100 as a highlight means.
+
+    if (Lab->L > 100)
+        Lab -> L = 100;
+
+    // Check out gamut prism, on a, b faces
+
+    if (Lab -> a < amin || Lab->a > amax||
+        Lab -> b < bmin || Lab->b > bmax) {
+
+            cmsCIELCh LCh;
+            double h, slope;
+
+            // Falls outside a, b limits. Transports to LCh space,
+            // and then do the clipping
+
+
+            if (Lab -> a == 0.0) { // Is hue exactly 90?
+
+                // atan will not work, so clamp here
+                Lab -> b = Lab->b < 0 ? bmin : bmax;
+                return TRUE;
+            }
+
+            cmsLab2LCh(&LCh, Lab);
+
+            slope = Lab -> b / Lab -> a;
+            h = LCh.h;
+
+            // There are 4 zones
+
+            if ((h >= 0. && h < 45.) ||
+                (h >= 315 && h <= 360.)) {
+
+                    // clip by amax
+                    Lab -> a = amax;
+                    Lab -> b = amax * slope;
+            }
+            else
+                if (h >= 45. && h < 135.)
+                {
+                    // clip by bmax
+                    Lab -> b = bmax;
+                    Lab -> a = bmax / slope;
+                }
+                else
+                    if (h >= 135. && h < 225.) {
+                        // clip by amin
+                        Lab -> a = amin;
+                        Lab -> b = amin * slope;
+
+                    }
+                    else
+                        if (h >= 225. && h < 315.) {
+                            // clip by bmin
+                            Lab -> b = bmin;
+                            Lab -> a = bmin / slope;
+                        }
+                        else  {
+                            cmsSignalError(0, cmsERROR_RANGE, "Invalid angle");
+                            return FALSE;
+                        }
+
+    }
+
+    return TRUE;
+}
diff --git a/third_party/lcms/src/cmshalf.c b/third_party/lcms/src/cmshalf.c
index cdd4e37..935273d 100644
--- a/third_party/lcms/src/cmshalf.c
+++ b/third_party/lcms/src/cmshalf.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -31,7 +31,7 @@
 // This code is inspired in the paper "Fast Half Float Conversions"
 // by Jeroen van der Zijp
 
-static cmsUInt32Number Mantissa[2048] = {
+static const cmsUInt32Number Mantissa[2048] = {
 
 0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000,
 0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000,
@@ -377,7 +377,7 @@
 0x387fc000, 0x387fe000
 };
 
-static cmsUInt16Number Offset[64] = {
+static const cmsUInt16Number Offset[64] = {
 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
@@ -391,7 +391,7 @@
 0x0400, 0x0400, 0x0400, 0x0400
 };
 
-static cmsUInt32Number Exponent[64] = {
+static const cmsUInt32Number Exponent[64] = {
 0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000,
 0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000,
 0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000,
@@ -405,7 +405,7 @@
 0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000
 };
 
-static cmsUInt16Number Base[512] = {
+static const cmsUInt16Number Base[512] = {
 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -460,7 +460,7 @@
 0xfc00, 0xfc00
 };
 
-static cmsUInt8Number  Shift[512] = {
+static const cmsUInt8Number  Shift[512] = {
 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c
index 60d6a0e..e44ab3e 100644
--- a/third_party/lcms/src/cmsintrp.c
+++ b/third_party/lcms/src/cmsintrp.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -29,6 +29,13 @@
 // This module incorporates several interpolation routines, for 1 to 8 channels on input and
 // up to 65535 channels on output. The user may change those by using the interpolation plug-in
 
+// Some people may want to compile as C++ with all warnings on, in this case make compiler silent
+#ifdef _MSC_VER
+#    if (_MSC_VER >= 1400)
+#       pragma warning( disable : 4365 )
+#    endif
+#endif
+
 // Interpolation routines by default
 static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags);
 
@@ -102,12 +109,12 @@
 // This function precalculates as many parameters as possible to speed up the interpolation.
 cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID,
                                            const cmsUInt32Number nSamples[],
-                                           int InputChan, int OutputChan,
+                                           cmsUInt32Number InputChan, cmsUInt32Number OutputChan,
                                            const void *Table,
                                            cmsUInt32Number dwFlags)
 {
     cmsInterpParams* p;
-    int i;
+    cmsUInt32Number i;
 
     // Check for maximum inputs
     if (InputChan > MAX_INPUT_DIMENSIONS) {
@@ -151,7 +158,8 @@
 
 
 // This one is a wrapper on the anterior, but assuming all directions have same number of nodes
-cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, int nSamples, int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags)
+cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, 
+                                         cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags)
 {
     int i;
     cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS];
@@ -215,7 +223,7 @@
 // To prevent out of bounds indexing
 cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) 
 {
-    return ((v < 0.0f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v);
+    return ((v < 1.0e-9f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v);
 }
 
 // Floating-point version of 1D interpolation
@@ -352,10 +360,10 @@
     y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0;
 
     X0 = p -> opta[1] * x0;
-    X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[1]);
+    X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[1]);
 
     Y0 = p -> opta[0] * y0;
-    Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[0]);
+    Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[0]);
 
     for (OutChan = 0; OutChan < TotalOut; OutChan++) {
 
@@ -464,18 +472,18 @@
     py = fclamp(Input[1]) * p->Domain[1];
     pz = fclamp(Input[2]) * p->Domain[2];
 
-    x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0;
-    y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0;
-    z0 = (int) _cmsQuickFloor(pz); fz = pz - (cmsFloat32Number) z0;
+    x0 = (int) floor(px); fx = px - (cmsFloat32Number) x0;  // We need full floor funcionality here
+    y0 = (int) floor(py); fy = py - (cmsFloat32Number) y0;
+    z0 = (int) floor(pz); fz = pz - (cmsFloat32Number) z0;
 
     X0 = p -> opta[2] * x0;
-    X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[2]);
+    X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]);
 
     Y0 = p -> opta[1] * y0;
-    Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[1]);
+    Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]);
 
     Z0 = p -> opta[0] * z0;
-    Z1 = Z0 + (Input[2] >= 1.0 ? 0 : p->opta[0]);
+    Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]);
 
     for (OutChan = 0; OutChan < TotalOut; OutChan++) {
 
@@ -608,19 +616,19 @@
     py = fclamp(Input[1]) * p->Domain[1];
     pz = fclamp(Input[2]) * p->Domain[2];
 
-    x0 = (int) _cmsQuickFloor(px); rx = (px - (cmsFloat32Number) x0);
-    y0 = (int) _cmsQuickFloor(py); ry = (py - (cmsFloat32Number) y0);
-    z0 = (int) _cmsQuickFloor(pz); rz = (pz - (cmsFloat32Number) z0);
+    x0 = (int) floor(px); rx = (px - (cmsFloat32Number) x0);  // We need full floor functionality here
+    y0 = (int) floor(py); ry = (py - (cmsFloat32Number) y0);
+    z0 = (int) floor(pz); rz = (pz - (cmsFloat32Number) z0);
 
 
     X0 = p -> opta[2] * x0;
-    X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[2]);
+    X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]);
 
     Y0 = p -> opta[1] * y0;
-    Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[1]);
+    Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]);
 
     Z0 = p -> opta[0] * z0;
-    Z1 = Z0 + (Input[2] >= 1.0 ? 0 : p->opta[0]);
+    Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]);
 
     for (OutChan=0; OutChan < TotalOut; OutChan++) {
 
@@ -923,13 +931,13 @@
                                 c3 = DENS(X0, Y0, Z1) - c0;
 
                             }
-                            else  {
+                            else {
                                 c1 = c2 = c3 = 0;
                             }
 
                             Rest = c1 * rx + c2 * ry + c3 * rz;
 
-                            Tmp1[OutChan] = (cmsUInt16Number) ( c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
+                            Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
     }
 
 
@@ -1028,7 +1036,7 @@
        rest = pk - (cmsFloat32Number) k0;
 
        K0 = p -> opta[3] * k0;
-       K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[3]);
+       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[3]);
 
        p1 = *p;
        memmove(&p1.Domain[0], &p ->Domain[1], 3*sizeof(cmsUInt32Number));
@@ -1115,7 +1123,7 @@
        rest = pk - (cmsFloat32Number) k0;
 
        K0 = p -> opta[4] * k0;
-       K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[4]);
+       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[4]);
 
        p1 = *p;
        memmove(&p1.Domain[0], &p ->Domain[1], 4*sizeof(cmsUInt32Number));
@@ -1202,7 +1210,7 @@
        rest = pk - (cmsFloat32Number) k0;
 
        K0 = p -> opta[5] * k0;
-       K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[5]);
+       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[5]);
 
        p1 = *p;
        memmove(&p1.Domain[0], &p ->Domain[1], 5*sizeof(cmsUInt32Number));
@@ -1287,7 +1295,7 @@
        rest = pk - (cmsFloat32Number) k0;
 
        K0 = p -> opta[6] * k0;
-       K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[6]);
+       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[6]);
 
        p1 = *p;
        memmove(&p1.Domain[0], &p ->Domain[1], 6*sizeof(cmsUInt32Number));
@@ -1372,7 +1380,7 @@
        rest = pk - (cmsFloat32Number) k0;
 
        K0 = p -> opta[7] * k0;
-       K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[7]);
+       K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[7]);
 
        p1 = *p;
        memmove(&p1.Domain[0], &p ->Domain[1], 7*sizeof(cmsUInt32Number));
diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
index cc5f890..1b4942c 100644
--- a/third_party/lcms/src/cmsio0.c
+++ b/third_party/lcms/src/cmsio0.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -337,21 +337,27 @@
     return TRUE;
 }
 
-// Returns file pointer position
+// Returns file pointer position or 0 on error, which is also a valid position.
 static
 cmsUInt32Number FileTell(cmsIOHANDLER* iohandler)
 {
-    return (cmsUInt32Number) ftell((FILE*)iohandler ->stream);
+    long t = ftell((FILE*)iohandler ->stream);
+    if (t == -1L) {
+        cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file");
+        return 0;
+    }
+
+    return (cmsUInt32Number)t;
 }
 
 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error
 static
 cmsBool  FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer)
 {
-       if (size == 0) return TRUE;  // We allow to write 0 bytes, but nothing is written
+    if (size == 0) return TRUE;  // We allow to write 0 bytes, but nothing is written
 
-       iohandler->UsedSpace += size;
-       return (fwrite(Buffer, size, 1, (FILE*) iohandler->stream) == 1);
+    iohandler->UsedSpace += size;
+    return (fwrite(Buffer, size, 1, (FILE*)iohandler->stream) == 1);
 }
 
 // Closes the file
@@ -527,7 +533,7 @@
     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
     if (Icc == NULL) return -1;
 
-    return  Icc->TagCount;
+    return  (cmsInt32Number) Icc->TagCount;
 }
 
 // Return the tag signature of a given tag number
@@ -545,9 +551,9 @@
 static
 int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig)
 {
-    cmsUInt32Number i;
+    int i;
 
-    for (i=0; i < Profile -> TagCount; i++) {
+    for (i=0; i < (int) Profile -> TagCount; i++) {
 
         if (sig == Profile -> TagNames[i])
             return i;
@@ -649,7 +655,7 @@
             return FALSE;
         }
 
-        *NewPos = Icc ->TagCount;
+        *NewPos = (int) Icc ->TagCount;
         Icc -> TagCount++;
     }
 
@@ -680,10 +686,10 @@
     cmsUInt8Number temp2;
 
     if (*pByte > 0x09) *pByte = (cmsUInt8Number) 0x09;
-    temp1 = *(pByte+1) & 0xf0;
-    temp2 = *(pByte+1) & 0x0f;
-    if (temp1 > 0x90) temp1 = 0x90;
-    if (temp2 > 0x09) temp2 = 0x09;
+    temp1 = (cmsUInt8Number) (*(pByte+1) & 0xf0);
+    temp2 = (cmsUInt8Number) (*(pByte+1) & 0x0f);
+    if (temp1 > 0x90U) temp1 = 0x90U;
+    if (temp2 > 0x09U) temp2 = 0x09U;
     *(pByte+1) = (cmsUInt8Number)(temp1 | temp2);
     *(pByte+2) = (cmsUInt8Number)0;
     *(pByte+3) = (cmsUInt8Number)0;
@@ -792,7 +798,7 @@
     cmsICCHeader Header;
     cmsUInt32Number i;
     cmsTagEntry Tag;
-    cmsInt32Number Count = 0;
+    cmsUInt32Number Count;
 
     Header.size        = _cmsAdjustEndianess32(UsedSpace);
     Header.cmmId       = _cmsAdjustEndianess32(lcmsSignature);
@@ -823,9 +829,9 @@
     Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent);
 
     // Illuminant is always D50
-    Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X));
-    Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));
-    Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));
+    Header.illuminant.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X));
+    Header.illuminant.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));
+    Header.illuminant.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));
 
     // Created by LittleCMS (that's me!)
     Header.creator      = _cmsAdjustEndianess32(lcmsSignature);
@@ -841,6 +847,7 @@
     // Saves Tag directory
 
     // Get true count
+    Count = 0;
     for (i=0;  i < Icc -> TagCount; i++) {
         if (Icc ->TagNames[i] != (cmsTagSignature) 0)
             Count++;
@@ -853,9 +860,9 @@
 
         if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue;   // It is just a placeholder
 
-        Tag.sig    = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]);
-        Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]);
-        Tag.size   = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]);
+        Tag.sig    = (cmsTagSignature) _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagNames[i]);
+        Tag.offset = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagOffsets[i]);
+        Tag.size   = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagSizes[i]);
 
         if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE;
     }
@@ -1164,7 +1171,7 @@
     NewIcc = (_cmsICCPROFILE*) hEmpty;
 
     // Ok, in this case const void* is casted to void* just because open IO handler
-    // shares read and writting modes. Don't abuse this feature!
+    // shares read and writing modes. Don't abuse this feature!
     NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r");
     if (NewIcc ->IOhandler == NULL) goto Error;
 
@@ -1460,7 +1467,7 @@
     // Was open in write mode?
     if (Icc ->IsWrite) {
 
-        Icc ->IsWrite = FALSE;      // Assure no further writting
+        Icc ->IsWrite = FALSE;      // Assure no further writing
         rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile);
     }
 
@@ -1558,6 +1565,8 @@
     Offset    = Icc -> TagOffsets[n];
     TagSize   = Icc -> TagSizes[n];
 
+    if (TagSize < 8) goto Error;
+
     // Seek to its location
     if (!io -> Seek(io, Offset))
         goto Error;
@@ -1580,8 +1589,8 @@
     if (BaseType == 0) goto Error;
 
     if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
-
-    TagSize  -= 8;                      // Alredy read by the type base logic
+   
+    TagSize  -= 8;       // Alredy read by the type base logic
 
     // Get type handler
     TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);
@@ -1616,6 +1625,7 @@
         _cmsTagSignature2String(String, sig);
         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
             String, TagDescriptor ->ElemCount, ElemCount);
+        goto Error;
     }
 
 
@@ -1770,7 +1780,7 @@
 // raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows
 // to write a tag as raw data and the read it as handled.
 
-cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize)
+cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize)
 {
     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
     void *Object;
@@ -1888,7 +1898,7 @@
 }
 
 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without
-// checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading
+// checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading
 // it as cooked without serializing does result into an error. If that is what you want, you will need to dump
 // the profile to memry or disk and then reopen it.
 cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size)
diff --git a/third_party/lcms/src/cmsio1.c b/third_party/lcms/src/cmsio1.c
index 4b12ae1..1343f2b 100644
--- a/third_party/lcms/src/cmsio1.c
+++ b/third_party/lcms/src/cmsio1.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -310,8 +310,8 @@
 
 // Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
 // is adjusted here in order to create a LUT that takes care of all those details.
-// We add intent = -1 as a way to read matrix shaper always, no matter of other LUT
-cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
+// We add intent = 0xffffffff as a way to read matrix shaper always, no matter of other LUT
+cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
 {
     cmsTagTypeSignature OriginalType;
     cmsTagSignature tag16;
@@ -341,8 +341,8 @@
     }
 
     // This is an attempt to reuse this function to retrieve the matrix-shaper as pipeline no
-    // matter other LUT are present and have precedence. Intent = -1 means just this.
-    if (Intent >= INTENT_PERCEPTUAL && Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
+    // matter other LUT are present and have precedence. Intent = 0xffffffff can be used for that.
+    if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
 
         tag16 = Device2PCS16[Intent];
         tagFloat = Device2PCSFloat[Intent];
@@ -586,7 +586,7 @@
 }
 
 // Create an output MPE LUT from agiven profile. Version mismatches are handled here
-cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
+cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
 {
     cmsTagTypeSignature OriginalType;
     cmsTagSignature tag16;
@@ -594,7 +594,7 @@
     cmsContext ContextID  = cmsGetProfileContextID(hProfile);
 
 
-    if (Intent >= INTENT_PERCEPTUAL && Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
+    if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
 
         tag16 = PCS2Device16[Intent];
         tagFloat = PCS2DeviceFloat[Intent];
@@ -670,8 +670,8 @@
 static
 cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
 {
-    cmsContext ContextID       = cmsGetProfileContextID(hProfile);
-    cmsPipeline* Lut           = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
+    cmsContext ContextID = cmsGetProfileContextID(hProfile);
+    cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat));
     cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
     cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
 
@@ -689,17 +689,17 @@
                 goto Error;
         }
 
-        if (PCS == cmsSigLabData)
+    if (PCS == cmsSigLabData)
+    {
+        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
+            goto Error;
+    }
+    else
+        if (PCS == cmsSigXYZData)
         {
-            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
+            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
                 goto Error;
         }
-        else
-            if (PCS == cmsSigXYZData)
-            {
-                if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
-                    goto Error;
-            }
 
     return Lut;
 Error:
@@ -709,7 +709,7 @@
 
 // This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
 // tag name here may default to AToB0
-cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
+cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
 {
     cmsPipeline* Lut;
     cmsTagTypeSignature OriginalType;
@@ -718,7 +718,7 @@
     cmsContext ContextID = cmsGetProfileContextID(hProfile);
 
 
-    if (Intent < INTENT_PERCEPTUAL || Intent > INTENT_ABSOLUTE_COLORIMETRIC)
+    if (Intent > INTENT_ABSOLUTE_COLORIMETRIC)
         return NULL;
 
     tag16 = Device2PCS16[Intent];
diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c
index 8d58159..1c1e18f 100644
--- a/third_party/lcms/src/cmslut.c
+++ b/third_party/lcms/src/cmslut.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -124,8 +124,8 @@
     mpe = Lut ->Elements;
     for (i=0; i < n; i++) {
 
-        // Get asked type
-        Type  = (cmsStageSignature)va_arg(args, cmsStageSignature);
+        // Get asked type. cmsStageSignature is promoted to int by compiler
+        Type  = (cmsStageSignature)va_arg(args, int);
         if (mpe ->Type != Type) {
 
             va_end(args);       // Mismatch. We are done.
@@ -292,7 +292,7 @@
 
 
 // Create a bunch of identity curves
-cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels)
+cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels)
 {
     cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL);
 
@@ -712,7 +712,7 @@
 }
 
 // Creates an MPE that just copies input to output
-cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan)
+cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan)
 {
     cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
     cmsStage* mpe ;
@@ -736,7 +736,7 @@
 
 
 // Quantize a value 0 <= i < MaxSamples to 0..0xffff
-cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples)
+cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples)
 {
     cmsFloat64Number x;
 
@@ -749,8 +749,9 @@
 // function on knots. returns TRUE if all ok, FALSE otherwise.
 cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags)
 {
-    int i, t, nTotalPoints, index, rest;
-    int nInputs, nOutputs;
+    int i, t, index, rest;
+    cmsUInt32Number nTotalPoints;
+    cmsUInt32Number nInputs, nOutputs;
     cmsUInt32Number* nSamples;
     cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
     _cmsStageCLutData* clut;
@@ -770,14 +771,17 @@
     if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE;
     if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;
 
+    memset(In, 0, sizeof(In));
+    memset(Out, 0, sizeof(Out));
+
     nTotalPoints = CubeSize(nSamples, nInputs);
     if (nTotalPoints == 0) return FALSE;
 
     index = 0;
-    for (i = 0; i < nTotalPoints; i++) {
+    for (i = 0; i < (int) nTotalPoints; i++) {
 
         rest = i;
-        for (t = nInputs-1; t >=0; --t) {
+        for (t = (int)nInputs - 1; t >= 0; --t) {
 
             cmsUInt32Number  Colorant = rest % nSamples[t];
 
@@ -787,7 +791,7 @@
         }
 
         if (clut ->Tab.T != NULL) {
-            for (t=0; t < nOutputs; t++)
+            for (t = 0; t < (int)nOutputs; t++)
                 Out[t] = clut->Tab.T[index + t];
         }
 
@@ -797,7 +801,7 @@
         if (!(dwFlags & SAMPLER_INSPECT)) {
 
             if (clut ->Tab.T != NULL) {
-                for (t=0; t < nOutputs; t++)
+                for (t=0; t < (int) nOutputs; t++)
                     clut->Tab.T[index + t] = Out[t];
             }
         }
@@ -808,11 +812,12 @@
     return TRUE;
 }
 
-// Same as anterior, but for floting point
+// Same as anterior, but for floating point
 cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags)
 {
-    int i, t, nTotalPoints, index, rest;
-    int nInputs, nOutputs;
+    int i, t, index, rest;
+    cmsUInt32Number nTotalPoints;
+    cmsUInt32Number nInputs, nOutputs;
     cmsUInt32Number* nSamples;
     cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
     _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;
@@ -830,10 +835,10 @@
     if (nTotalPoints == 0) return FALSE;
 
     index = 0;
-    for (i = 0; i < nTotalPoints; i++) {
+    for (i = 0; i < (int)nTotalPoints; i++) {
 
         rest = i;
-        for (t = nInputs-1; t >=0; --t) {
+        for (t = (int) nInputs-1; t >=0; --t) {
 
             cmsUInt32Number  Colorant = rest % nSamples[t];
 
@@ -843,7 +848,7 @@
         }
 
         if (clut ->Tab.TFloat != NULL) {
-            for (t=0; t < nOutputs; t++)
+            for (t=0; t < (int) nOutputs; t++)
                 Out[t] = clut->Tab.TFloat[index + t];
         }
 
@@ -853,7 +858,7 @@
         if (!(dwFlags & SAMPLER_INSPECT)) {
 
             if (clut ->Tab.TFloat != NULL) {
-                for (t=0; t < nOutputs; t++)
+                for (t=0; t < (int) nOutputs; t++)
                     clut->Tab.TFloat[index + t] = Out[t];
             }
         }
@@ -871,7 +876,8 @@
 cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
                                          cmsSAMPLER16 Sampler, void * Cargo)
 {
-    int i, t, nTotalPoints, rest;
+    int i, t, rest;
+    cmsUInt32Number nTotalPoints;
     cmsUInt16Number In[cmsMAXCHANNELS];
 
     if (nInputs >= cmsMAXCHANNELS) return FALSE;
@@ -879,10 +885,10 @@
     nTotalPoints = CubeSize(clutPoints, nInputs);
     if (nTotalPoints == 0) return FALSE;
 
-    for (i = 0; i < nTotalPoints; i++) {
+    for (i = 0; i < (int) nTotalPoints; i++) {
 
         rest = i;
-        for (t = nInputs-1; t >=0; --t) {
+        for (t = (int) nInputs-1; t >=0; --t) {
 
             cmsUInt32Number  Colorant = rest % clutPoints[t];
 
@@ -901,7 +907,8 @@
 cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
                                             cmsSAMPLERFLOAT Sampler, void * Cargo)
 {
-    int i, t, nTotalPoints, rest;
+    int i, t, rest;
+    cmsUInt32Number nTotalPoints;
     cmsFloat32Number In[cmsMAXCHANNELS];
 
     if (nInputs >= cmsMAXCHANNELS) return FALSE;
@@ -909,10 +916,10 @@
     nTotalPoints = CubeSize(clutPoints, nInputs);
     if (nTotalPoints == 0) return FALSE;
 
-    for (i = 0; i < nTotalPoints; i++) {
+    for (i = 0; i < (int) nTotalPoints; i++) {
 
         rest = i;
-        for (t = nInputs-1; t >=0; --t) {
+        for (t = (int) nInputs-1; t >=0; --t) {
 
             cmsUInt32Number  Colorant = rest % clutPoints[t];
 
@@ -992,7 +999,7 @@
             return NULL;
         }
 
-        // We need to map * (0xffff / 0xff00), thats same as (257 / 256)
+        // We need to map * (0xffff / 0xff00), that's same as (257 / 256)
         // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256);
         for (i=0; i < 257; i++)  {
 
@@ -1137,7 +1144,7 @@
        }
 }
 
-cmsStage*  _cmsStageClipNegatives(cmsContext ContextID, int nChannels)
+cmsStage*  _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels)
 {
        return _cmsStageAllocPlaceholder(ContextID, cmsSigClipNegativesElemType,
               nChannels, nChannels, Clipper, NULL, NULL, NULL);
@@ -1274,7 +1281,7 @@
 static
 cmsBool BlessLUT(cmsPipeline* lut)
 {
-    // We can set the input/ouput channels only if we have elements.
+    // We can set the input/output channels only if we have elements.
     if (lut ->Elements != NULL) {
 
         cmsStage* prev;
@@ -1301,9 +1308,10 @@
 
             next = next->Next;
             prev = prev->Next;
-        }
     }
-    return TRUE;
+}
+
+    return TRUE;    
 }
 
 
@@ -1357,10 +1365,7 @@
 }
 
 
-
-
 // LUT Creation & Destruction
-
 cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels)
 {
        cmsPipeline* NewLUT;
@@ -1372,7 +1377,6 @@
        NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline));
        if (NewLUT == NULL) return NULL;
 
-
        NewLUT -> InputChannels  = InputChannels;
        NewLUT -> OutputChannels = OutputChannels;
 
@@ -1536,7 +1540,7 @@
             return FALSE;
     }
 
-    return BlessLUT(lut);
+    return BlessLUT(lut);    
 }
 
 // Unlink an element and return the pointer to it
@@ -1618,7 +1622,7 @@
                 return FALSE;
     }
 
-    return BlessLUT(l1);
+    return BlessLUT(l1);    
 }
 
 
diff --git a/third_party/lcms/src/cmsmd5.c b/third_party/lcms/src/cmsmd5.c
index c7380ca..4b16ad4 100644
--- a/third_party/lcms/src/cmsmd5.c
+++ b/third_party/lcms/src/cmsmd5.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
diff --git a/third_party/lcms/src/cmsmtrx.c b/third_party/lcms/src/cmsmtrx.c
index d0e5461..a83d39d 100644
--- a/third_party/lcms/src/cmsmtrx.c
+++ b/third_party/lcms/src/cmsmtrx.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c
index 9ed4cad..9cfd228 100644
--- a/third_party/lcms/src/cmsnamed.c
+++ b/third_party/lcms/src/cmsnamed.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -127,7 +127,7 @@
     for (i=0; i < mlu ->UsedEntries; i++) {
 
         if (mlu ->Entries[i].Country  == CountryCode &&
-            mlu ->Entries[i].Language == LanguageCode) return i;
+            mlu ->Entries[i].Language == LanguageCode) return (int) i;
     }
 
     // Not found
@@ -178,31 +178,32 @@
     return TRUE;
 }
 
-// Convert from a 3-char code to a cmsUInt16Number. It is done inthis way because some
+// Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some
 // compilers don't properly align beginning of strings
 
 static
 cmsUInt16Number strTo16(const char str[3])
 {
-    cmsUInt16Number n = ((cmsUInt16Number) str[0] << 8) | str[1];
+    const cmsUInt8Number* ptr8 = (const cmsUInt8Number*)str;
+    cmsUInt16Number n = (cmsUInt16Number) (((cmsUInt16Number) ptr8[1] << 8) | ptr8[0]);
 
-    return n;  // Always big endian in this case
+    return _cmsAdjustEndianess16(n);
 }
 
 static
 void strFrom16(char str[3], cmsUInt16Number n)
 {
-    // Assiming this would be aligned
+    // Assuming this would be aligned
     union {
 
        cmsUInt16Number n;
-       char str[2];
+       cmsUInt8Number str[2];
        
     } c;
 
-    c.n = n;  // Always big endian in this case
+    c.n = _cmsAdjustEndianess16(n);  
 
-    str[0] = c.str[0]; str[1] = c.str[1]; str[2] = 0;
+    str[0] = (char) c.str[0]; str[1] = (char) c.str[1]; str[2] = (char) 0;
 
 }
 
@@ -325,7 +326,7 @@
                               cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
 {
     cmsUInt32Number i;
-    cmsInt32Number Best = -1;
+    int Best = -1;
     _cmsMLUentry* v;
 
     if (mlu == NULL) return NULL;
@@ -338,7 +339,7 @@
 
         if (v -> Language == LanguageCode) {
 
-            if (Best == -1) Best = i;
+            if (Best == -1) Best = (int) i;
 
             if (v -> Country == CountryCode) {
 
@@ -517,7 +518,11 @@
         size = v ->Allocated * 2;
 
     // Keep a maximum color lists can grow, 100K entries seems reasonable
-    if (size > 1024*100) return FALSE;
+    if (size > 1024 * 100) {
+        _cmsFree(v->ContextID, (void*) v->List);
+        v->List = NULL;
+        return FALSE;
+    }
 
     NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));
     if (NewPtr == NULL)
@@ -539,7 +544,7 @@
     v ->nColors   = 0;
     v ->ContextID  = ContextID;
 
-    while (v -> Allocated < n){
+    while (v -> Allocated < n) {
         if (!GrowNamedColorList(v)) {
             cmsFreeNamedColorList(v);
             return NULL;
@@ -603,10 +608,10 @@
     }
 
     for (i=0; i < NamedColorList ->ColorantCount; i++)
-        NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL? 0 : Colorant[i];
+        NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i];
 
     for (i=0; i < 3; i++)
-        NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? 0 : PCS[i];
+        NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i];
 
     if (Name != NULL) {
 
@@ -641,6 +646,7 @@
 
     if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE;
 
+    // strcpy instead of strncpy because many apps are using small buffers
     if (Name) strcpy(Name, NamedColorList->List[nColor].Name);
     if (Prefix) strcpy(Prefix, NamedColorList->Prefix);
     if (Suffix) strcpy(Suffix, NamedColorList->Suffix);
@@ -658,13 +664,14 @@
 // Search for a given color name (no prefix or suffix)
 cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)
 {
-    int i, n;
+    cmsUInt32Number i;
+    cmsUInt32Number n;
 
     if (NamedColorList == NULL) return -1;
     n = cmsNamedColorCount(NamedColorList);
     for (i=0; i < n; i++) {
         if (cmsstrcasecmp(Name,  NamedColorList->List[i].Name) == 0)
-            return i;
+            return (cmsInt32Number) i;
     }
 
     return -1;
@@ -693,7 +700,8 @@
     cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
 
     if (index >= NamedColorList-> nColors) {
-        cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
+        cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);
+        Out[0] = Out[1] = Out[2] = 0.0f;
     }
     else {
 
@@ -712,7 +720,10 @@
     cmsUInt32Number j;
 
     if (index >= NamedColorList-> nColors) {
-        cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
+        cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);
+        for (j = 0; j < NamedColorList->ColorantCount; j++)
+            Out[j] = 0.0f;
+
     }
     else {
         for (j=0; j < NamedColorList ->ColorantCount; j++)
diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
index abe26b9..8a11718 100644
--- a/third_party/lcms/src/cmsopt.c
+++ b/third_party/lcms/src/cmsopt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -49,8 +49,8 @@
     cmsContext ContextID;
 
     // Number of channels
-    int nInputs;
-    int nOutputs;
+    cmsUInt32Number nInputs;
+    cmsUInt32Number nOutputs;
 
     _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS];       // The maximum number of input channels is known in advance
     cmsInterpParams*  ParamsCurveIn16[MAX_INPUT_DIMENSIONS];
@@ -94,8 +94,8 @@
 
     cmsContext ContextID;
 
-    int nCurves;                  // Number of curves
-    int nElements;                // Elements in curves
+    cmsUInt32Number nCurves;      // Number of curves
+    cmsUInt32Number nElements;    // Elements in curves
     cmsUInt16Number** Curves;     // Points to a dynamically  allocated array
 
 } Curves16Data;
@@ -104,6 +104,39 @@
 // Simple optimizations ----------------------------------------------------------------------------------------------------------
 
 
+// Clamp a fixed point integer to signed 28 bits to avoid overflow in
+// calculations.  Clamp is intended for use with colorants, requiring one bit
+// for a colorant and another two bits to avoid overflow when combining the
+// colors.
+cmsINLINE cmsS1Fixed14Number _FixedClamp(cmsS1Fixed14Number n) {
+  const cmsS1Fixed14Number max_positive = 268435455;  // 0x0FFFFFFF;
+  const cmsS1Fixed14Number max_negative = -268435456; // 0xF0000000;
+  // Normally expect the provided number to be in the range [0..1] (but in
+  // fixed 1.14 format), so can perform a quick check for this typical case
+  // to reduce number of compares.
+  const cmsS1Fixed14Number typical_range_mask = 0xFFFF8000;
+
+  if (!(n & typical_range_mask))
+    return n;
+  if (n < max_negative)
+     return max_negative;
+  if (n > max_positive)
+    return max_positive;
+  return n;
+}
+
+// Perform one row of matrix multiply with translation for MatShaperEval16().
+cmsINLINE cmsInt64Number _MatShaperEvaluateRow(cmsS1Fixed14Number* mat,
+                                               cmsS1Fixed14Number off,
+                                               cmsS1Fixed14Number r,
+                                               cmsS1Fixed14Number g,
+                                               cmsS1Fixed14Number b) {
+  return ((cmsInt64Number)mat[0] * r +
+          (cmsInt64Number)mat[1] * g +
+          (cmsInt64Number)mat[2] * b +
+          off + 0x2000) >> 14;
+}
+
 // Remove an element in linked chain
 static
 void _RemoveElement(cmsStage** head)
@@ -216,7 +249,7 @@
                      // Multiply both matrices to get the result
                      _cmsMAT3per(&res, (cmsMAT3*)m2->Double, (cmsMAT3*)m1->Double);
 
-                     // Get the next in chain afer the matrices
+                     // Get the next in chain after the matrices
                      chain = (*pt2)->Next;
 
                      // Remove both matrices
@@ -305,7 +338,7 @@
     Prelin16Data* p16 = (Prelin16Data*) D;
     cmsUInt16Number  StageABC[MAX_INPUT_DIMENSIONS];
     cmsUInt16Number  StageDEF[cmsMAXCHANNELS];
-    int i;
+    cmsUInt32Number i;
 
     for (i=0; i < p16 ->nInputs; i++) {
 
@@ -350,15 +383,15 @@
 static
 Prelin16Data* PrelinOpt16alloc(cmsContext ContextID,
                                const cmsInterpParams* ColorMap,
-                               int nInputs, cmsToneCurve** In,
-                               int nOutputs, cmsToneCurve** Out )
+                               cmsUInt32Number nInputs, cmsToneCurve** In,
+                               cmsUInt32Number nOutputs, cmsToneCurve** Out )
 {
-    int i;
+    cmsUInt32Number i;
     Prelin16Data* p16 = (Prelin16Data*)_cmsMallocZero(ContextID, sizeof(Prelin16Data));
     if (p16 == NULL) return NULL;
 
     p16 ->nInputs = nInputs;
-    p16 -> nOutputs = nOutputs;
+    p16 ->nOutputs = nOutputs;
 
 
     for (i=0; i < nInputs; i++) {
@@ -406,7 +439,7 @@
 // Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for
 // almost any transform. We use floating point precision and then convert from floating point to 16 bits.
 static
-int XFormSampler16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+cmsInt32Number XFormSampler16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
 {
     cmsPipeline* Lut = (cmsPipeline*) Cargo;
     cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS];
@@ -453,7 +486,7 @@
 // is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels
 static
 cmsBool  PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
-                  int nChannelsOut, int nChannelsIn)
+                  cmsUInt32Number nChannelsOut, cmsUInt32Number nChannelsIn)
 {
     _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data;
     cmsInterpParams* p16  = Grid ->Params;
@@ -483,10 +516,10 @@
             ((pz - z0) != 0) ||
             ((pw - w0) != 0)) return FALSE; // Not on exact node
 
-        index = p16 -> opta[3] * x0 +
-                p16 -> opta[2] * y0 +
-                p16 -> opta[1] * z0 +
-                p16 -> opta[0] * w0;
+        index = (int) p16 -> opta[3] * x0 +
+                (int) p16 -> opta[2] * y0 +
+                (int) p16 -> opta[1] * z0 +
+                (int) p16 -> opta[0] * w0;
     }
     else
         if (nChannelsIn == 3) {
@@ -503,9 +536,9 @@
                 ((py - y0) != 0) ||
                 ((pz - z0) != 0)) return FALSE;  // Not on exact node
 
-            index = p16 -> opta[2] * x0 +
-                    p16 -> opta[1] * y0 +
-                    p16 -> opta[0] * z0;
+            index = (int) p16 -> opta[2] * x0 +
+                    (int) p16 -> opta[1] * y0 +
+                    (int) p16 -> opta[0] * z0;
         }
         else
             if (nChannelsIn == 1) {
@@ -516,24 +549,24 @@
                 
                 if (((px - x0) != 0)) return FALSE; // Not on exact node
 
-                index = p16 -> opta[0] * x0;
+                index = (int) p16 -> opta[0] * x0;
             }
             else {
                 cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
                 return FALSE;
             }
 
-            for (i=0; i < nChannelsOut; i++)
-                Grid -> Tab.T[index + i] = Value[i];
+            for (i = 0; i < (int) nChannelsOut; i++)
+                Grid->Tab.T[index + i] = Value[i];
 
             return TRUE;
 }
 
 // Auxiliary, to see if two values are equal or very different
 static
-cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] )
+cmsBool WhitesAreEqual(cmsUInt32Number n, cmsUInt16Number White1[], cmsUInt16Number White2[] )
 {
-    int i;
+    cmsUInt32Number i;
 
     for (i=0; i < n; i++) {
 
@@ -635,7 +668,7 @@
     cmsStage* mpe;
     cmsStage* CLUT;
     cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL;
-    int nGridPoints;
+    cmsUInt32Number nGridPoints;
     cmsColorSpaceSignature ColorSpace, OutputColorSpace;
     cmsStage *NewPreLin = NULL;
     cmsStage *NewPostLin = NULL;
@@ -647,8 +680,13 @@
     // This is a loosy optimization! does not apply in floating-point cases
     if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
 
-    ColorSpace       = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat));
-    OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat));
+    ColorSpace       = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat));
+    OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat));
+
+    // Color space must be specified
+    if (ColorSpace == (cmsColorSpaceSignature)0 ||
+        OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
+
     nGridPoints      = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
 
     // For empty LUTs, 2 points are enough
@@ -686,7 +724,7 @@
                     goto Error;
 
                 // Remove prelinearization. Since we have duplicated the curve
-                // in destination LUT, the sampling shoud be applied after this stage.
+                // in destination LUT, the sampling should be applied after this stage.
                 cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin);
             }
         }
@@ -718,14 +756,14 @@
                 if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin))
                     goto Error;
 
-                // In destination LUT, the sampling shoud be applied after this stage.
+                // In destination LUT, the sampling should be applied after this stage.
                 cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin);
             }
         }
     }
 
     // Now its time to do the sampling. We have to ignore pre/post linearization
-    // The source LUT whithout pre/post curves is passed as parameter.
+    // The source LUT without pre/post curves is passed as parameter.
     if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) {
 Error:
         // Ops, something went wrong, Restore stages
@@ -805,7 +843,7 @@
 {
     int BeginVal, EndVal;
     int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5);   // Cutoff at 2%
-    int AtEnd   = g ->nEntries - AtBegin - 1;                                  // And 98%
+    int AtEnd   = (int) g ->nEntries - AtBegin - 1;                                  // And 98%
     cmsFloat64Number Val, Slope, beta;
     int i;
 
@@ -866,9 +904,9 @@
 
 
         // Move to 0..1.0 in fixed domain
-        v1 = _cmsToFixedDomain(Input[0] * p -> Domain[0]);
-        v2 = _cmsToFixedDomain(Input[1] * p -> Domain[1]);
-        v3 = _cmsToFixedDomain(Input[2] * p -> Domain[2]);
+        v1 = _cmsToFixedDomain((int) (Input[0] * p -> Domain[0]));
+        v2 = _cmsToFixedDomain((int) (Input[1] * p -> Domain[1]));
+        v3 = _cmsToFixedDomain((int) (Input[2] * p -> Domain[2]));
 
         // Store the precalculated table of nodes
         p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1));
@@ -913,27 +951,27 @@
     cmsS15Fixed16Number    rx, ry, rz;
     cmsS15Fixed16Number    c0, c1, c2, c3, Rest;
     int                    OutChan;
-    register cmsS15Fixed16Number    X0, X1, Y0, Y1, Z0, Z1;
+    register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
     Prelin8Data* p8 = (Prelin8Data*) D;
     register const cmsInterpParams* p = p8 ->p;
-    int                    TotalOut = p -> nOutputs;
+    int                    TotalOut = (int) p -> nOutputs;
     const cmsUInt16Number* LutTable = (const cmsUInt16Number*) p->Table;
 
-    r = Input[0] >> 8;
-    g = Input[1] >> 8;
-    b = Input[2] >> 8;
+    r = (cmsUInt8Number) (Input[0] >> 8);
+    g = (cmsUInt8Number) (Input[1] >> 8);
+    b = (cmsUInt8Number) (Input[2] >> 8);
 
-    X0 = X1 = p8->X0[r];
-    Y0 = Y1 = p8->Y0[g];
-    Z0 = Z1 = p8->Z0[b];
+    X0 = X1 = (cmsS15Fixed16Number) p8->X0[r];
+    Y0 = Y1 = (cmsS15Fixed16Number) p8->Y0[g];
+    Z0 = Z1 = (cmsS15Fixed16Number) p8->Z0[b];
 
     rx = p8 ->rx[r];
     ry = p8 ->ry[g];
     rz = p8 ->rz[b];
 
-    X1 = X0 + ((rx == 0) ? 0 : p ->opta[2]);
-    Y1 = Y0 + ((ry == 0) ? 0 : p ->opta[1]);
-    Z1 = Z0 + ((rz == 0) ? 0 : p ->opta[0]);
+    X1 = X0 + (cmsS15Fixed16Number)((rx == 0) ? 0 :  p ->opta[2]);
+    Y1 = Y0 + (cmsS15Fixed16Number)((ry == 0) ? 0 :  p ->opta[1]);
+    Z1 = Z0 + (cmsS15Fixed16Number)((rz == 0) ? 0 :  p ->opta[0]);
 
 
     // These are the 6 Tetrahedral
@@ -986,9 +1024,8 @@
                                 c1 = c2 = c3 = 0;
                             }
 
-
                             Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
-                            Output[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest>>16))>>16);
+                            Output[OutChan] = (cmsUInt16Number) (c0 + ((Rest + (Rest >> 16)) >> 16));
 
     }
 }
@@ -1000,8 +1037,8 @@
 static
 cmsBool IsDegenerated(const cmsToneCurve* g)
 {
-    int i, Zeros = 0, Poles = 0;
-    int nEntries = g ->nEntries;
+    cmsUInt32Number i, Zeros = 0, Poles = 0;
+    cmsUInt32Number nEntries = g ->nEntries;
 
     for (i=0; i < nEntries; i++) {
 
@@ -1023,7 +1060,7 @@
 cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
 {
     cmsPipeline* OriginalLut;
-    int nGridPoints;
+    cmsUInt32Number nGridPoints;
     cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS];
     cmsUInt32Number t, i;
     cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS];
@@ -1061,8 +1098,13 @@
             if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
     }
 
-    ColorSpace       = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat));
-    OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat));
+    ColorSpace       = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat));
+    OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat));
+
+    // Color space must be specified
+    if (ColorSpace == (cmsColorSpaceSignature)0 ||
+        OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
+
     nGridPoints      = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
 
     // Empty gamma containers
@@ -1231,6 +1273,7 @@
     return FALSE;
 
     cmsUNUSED_PARAMETER(Intent);
+    cmsUNUSED_PARAMETER(lIsLinear);
 }
 
 
@@ -1240,7 +1283,7 @@
 void CurvesFree(cmsContext ContextID, void* ptr)
 {
      Curves16Data* Data = (Curves16Data*) ptr;
-     int i;
+     cmsUInt32Number i;
 
      for (i=0; i < Data -> nCurves; i++) {
 
@@ -1255,7 +1298,7 @@
 void* CurvesDup(cmsContext ContextID, const void* ptr)
 {
     Curves16Data* Data = (Curves16Data*)_cmsDupMem(ContextID, ptr, sizeof(Curves16Data));
-    int i;
+    cmsUInt32Number i;
 
     if (Data == NULL) return NULL;
 
@@ -1270,9 +1313,9 @@
 
 // Precomputes tables for 8-bit on input devicelink.
 static
-Curves16Data* CurvesAlloc(cmsContext ContextID, int nCurves, int nElements, cmsToneCurve** G)
+Curves16Data* CurvesAlloc(cmsContext ContextID, cmsUInt32Number nCurves, cmsUInt32Number nElements, cmsToneCurve** G)
 {
-    int i, j;
+    cmsUInt32Number i, j;
     Curves16Data* c16;
 
     c16 = (Curves16Data*)_cmsMallocZero(ContextID, sizeof(Curves16Data));
@@ -1282,7 +1325,10 @@
     c16 ->nElements = nElements;
 
     c16->Curves = (cmsUInt16Number**) _cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*));
-    if (c16 ->Curves == NULL) return NULL;
+    if (c16->Curves == NULL) {
+        _cmsFree(ContextID, c16);
+        return NULL;
+    }
 
     for (i=0; i < nCurves; i++) {
 
@@ -1298,7 +1344,7 @@
             return NULL;
         }
 
-        if (nElements == 256) {
+        if (nElements == 256U) {
 
             for (j=0; j < nElements; j++) {
 
@@ -1322,8 +1368,8 @@
                           register const void* D)
 {
     Curves16Data* Data = (Curves16Data*) D;
-    cmsUInt8Number x;
-    int i;
+    int x;
+    cmsUInt32Number i;
 
     for (i=0; i < Data ->nCurves; i++) {
 
@@ -1339,7 +1385,7 @@
                           register const void* D)
 {
     Curves16Data* Data = (Curves16Data*) D;
-    int i;
+    cmsUInt32Number i;
 
     for (i=0; i < Data ->nCurves; i++) {
          Out[i] = Data -> Curves[i][In[i]];
@@ -1454,6 +1500,7 @@
 
         // LUT optimizes to nothing. Set the identity LUT
         cmsStageFree(ObtainedCurves);
+        ObtainedCurves = NULL;
 
         if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels)))
             goto Error;
@@ -1513,29 +1560,30 @@
                      register const void* D)
 {
     MatShaper8Data* p = (MatShaper8Data*) D;
-    cmsS1Fixed14Number l1, l2, l3, r, g, b;
+    cmsS1Fixed14Number r, g, b;
+    cmsInt64Number l1, l2, l3;
     cmsUInt32Number ri, gi, bi;
 
     // In this case (and only in this case!) we can use this simplification since
     // In[] is assured to come from a 8 bit number. (a << 8 | a)
-    ri = In[0] & 0xFF;
-    gi = In[1] & 0xFF;
-    bi = In[2] & 0xFF;
+    ri = In[0] & 0xFFU;
+    gi = In[1] & 0xFFU;
+    bi = In[2] & 0xFFU;
 
     // Across first shaper, which also converts to 1.14 fixed point
-    r = p->Shaper1R[ri];
-    g = p->Shaper1G[gi];
-    b = p->Shaper1B[bi];
+    r = _FixedClamp(p->Shaper1R[ri]);
+    g = _FixedClamp(p->Shaper1G[gi]);
+    b = _FixedClamp(p->Shaper1B[bi]);
 
     // Evaluate the matrix in 1.14 fixed point
-    l1 =  (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14;
-    l2 =  (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14;
-    l3 =  (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14;
+    l1 = _MatShaperEvaluateRow(p->Mat[0], p->Off[0], r, g, b);
+    l2 = _MatShaperEvaluateRow(p->Mat[1], p->Off[1], r, g, b);
+    l3 = _MatShaperEvaluateRow(p->Mat[2], p->Off[2], r, g, b);
 
     // Now we have to clip to 0..1.0 range
-    ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384 : l1);
-    gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384 : l2);
-    bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384 : l3);
+    ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384U : (cmsUInt32Number) l1);
+    gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384U : (cmsUInt32Number) l2);
+    bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384U : (cmsUInt32Number) l3);
 
     // And across second shaper,
     Out[0] = p->Shaper2R[ri];
@@ -1546,7 +1594,7 @@
 
 // This table converts from 8 bits to 1.14 after applying the curve
 static
-cmsBool FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve)
+void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve)
 {
     int i;
     cmsFloat32Number R, y;
@@ -1555,17 +1603,17 @@
 
         R   = (cmsFloat32Number) (i / 255.0);
         y   = cmsEvalToneCurveFloat(Curve, R);
-        if (isinf(y))
-            return FALSE;
 
-        Table[i] = DOUBLE_TO_1FIXED14(y);
+        if (y < 131072.0)
+            Table[i] = DOUBLE_TO_1FIXED14(y);
+        else
+            Table[i] = 0x7fffffff;
     }
-    return TRUE;
 }
 
 // This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve
 static
-cmsBool FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput)
+void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput)
 {
     int i;
     cmsFloat32Number R, Val;
@@ -1574,8 +1622,12 @@
 
         R   = (cmsFloat32Number) (i / 16384.0);
         Val = cmsEvalToneCurveFloat(Curve, R);    // Val comes 0..1.0
-        if (isinf(Val))
-            return FALSE;
+
+        if (Val < 0)
+            Val = 0;
+
+        if (Val > 1.0)
+            Val = 1.0;
 
         if (Is8BitsOutput) {
 
@@ -1590,7 +1642,6 @@
         }
         else Table[i]  = _cmsQuickSaturateWord(Val * 65535.0);
     }
-    return TRUE;
 }
 
 // Compute the matrix-shaper structure
@@ -1608,21 +1659,15 @@
     p -> ContextID = Dest -> ContextID;
 
     // Precompute tables
-    if (!FillFirstShaper(p ->Shaper1R, Curve1[0]))
-        goto Error;
-    if (!FillFirstShaper(p ->Shaper1G, Curve1[1]))
-        goto Error;
-    if (!FillFirstShaper(p ->Shaper1B, Curve1[2]))
-        goto Error;
+    FillFirstShaper(p ->Shaper1R, Curve1[0]);
+    FillFirstShaper(p ->Shaper1G, Curve1[1]);
+    FillFirstShaper(p ->Shaper1B, Curve1[2]);
 
-    if (!FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits))
-        goto Error;
-    if (!FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits))
-        goto Error;
-    if (!FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits))
-        goto Error;
+    FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits);
+    FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits);
+    FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits);
 
-    // Convert matrix to nFixed14. Note that those values may take more than 16 bits as
+    // Convert matrix to nFixed14. Note that those values may take more than 16 bits 
     for (i=0; i < 3; i++) {
         for (j=0; j < 3; j++) {
             p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]);
@@ -1646,9 +1691,6 @@
     // Fill function pointers
     _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper);
     return TRUE;
-    Error:
-        _cmsFree(Dest->ContextID, p);
-        return FALSE;
 }
 
 //  8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast!
@@ -1761,8 +1803,7 @@
         *dwFlags |= cmsFLAGS_NOCACHE;
 
         // Setup the optimizarion routines
-        if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat))
-            goto Error;
+        SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat);
     }
 
     cmsPipelineFree(Src);
@@ -1888,7 +1929,7 @@
 // The entry point for LUT optimization
 cmsBool _cmsOptimizePipeline(cmsContext ContextID,
                              cmsPipeline**    PtrLut,
-                             int              Intent,
+                             cmsUInt32Number  Intent,
                              cmsUInt32Number* InputFormat,
                              cmsUInt32Number* OutputFormat,
                              cmsUInt32Number* dwFlags)
diff --git a/third_party/lcms/src/cmspack.c b/third_party/lcms/src/cmspack.c
index 6ed41da..84b0097 100644
--- a/third_party/lcms/src/cmspack.c
+++ b/third_party/lcms/src/cmspack.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -97,21 +97,21 @@
                                   register cmsUInt8Number* accum,
                                   register cmsUInt32Number Stride)
 {
-    int nChan      = T_CHANNELS(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->InputFormat);
-    int Reverse    = T_FLAVOR(info ->InputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    int Extra      = T_EXTRA(info -> InputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number nChan      = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap     = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
+    cmsUInt32Number Extra      = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsUInt16Number v;
-    int i;
+    cmsUInt32Number i;
 
     if (ExtraFirst) {
         accum += Extra;
     }
 
     for (i=0; i < nChan; i++) {
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         v = FROM_8_TO_16(*accum);
         v = Reverse ? REVERSE_FLAVOR_16(v) : v;
@@ -144,11 +144,11 @@
                                   register cmsUInt8Number* accum,
                                   register cmsUInt32Number Stride)
 {
-    int nChan     = T_CHANNELS(info -> InputFormat);
-    int DoSwap    = T_DOSWAP(info ->InputFormat);
-    int SwapFirst = T_SWAPFIRST(info ->InputFormat);
-    int Reverse   = T_FLAVOR(info ->InputFormat);
-    int i;
+    cmsUInt32Number nChan     = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap    = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->InputFormat);
+    cmsUInt32Number Reverse   = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number i;
     cmsUInt8Number* Init = accum;
 
     if (DoSwap ^ SwapFirst) {
@@ -157,7 +157,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
         cmsUInt16Number v = FROM_8_TO_16(*accum);
 
         wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v;
@@ -474,14 +474,14 @@
                                register cmsUInt8Number* accum,
                                register cmsUInt32Number Stride)
 {
-    int nChan       = T_CHANNELS(info -> InputFormat);
-    int SwapEndian  = T_ENDIAN16(info -> InputFormat);
-    int DoSwap      = T_DOSWAP(info ->InputFormat);
-    int Reverse     = T_FLAVOR(info ->InputFormat);
-    int SwapFirst   = T_SWAPFIRST(info -> InputFormat);
-    int Extra       = T_EXTRA(info -> InputFormat);
-    int ExtraFirst  = DoSwap ^ SwapFirst;
-    int i;
+   cmsUInt32Number nChan       = T_CHANNELS(info -> InputFormat);
+   cmsUInt32Number SwapEndian  = T_ENDIAN16(info -> InputFormat);
+   cmsUInt32Number DoSwap      = T_DOSWAP(info ->InputFormat);
+   cmsUInt32Number Reverse     = T_FLAVOR(info ->InputFormat);
+   cmsUInt32Number SwapFirst   = T_SWAPFIRST(info -> InputFormat);
+   cmsUInt32Number Extra       = T_EXTRA(info -> InputFormat);
+   cmsUInt32Number ExtraFirst  = DoSwap ^ SwapFirst;
+   cmsUInt32Number i;
 
     if (ExtraFirst) {
         accum += Extra * sizeof(cmsUInt16Number);
@@ -489,7 +489,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
         cmsUInt16Number v = *(cmsUInt16Number*) accum;
 
         if (SwapEndian)
@@ -523,11 +523,11 @@
                                   register cmsUInt8Number* accum,
                                   register cmsUInt32Number Stride)
 {
-    int nChan = T_CHANNELS(info -> InputFormat);
-    int DoSwap= T_DOSWAP(info ->InputFormat);
-    int Reverse= T_FLAVOR(info ->InputFormat);
-    int SwapEndian = T_ENDIAN16(info -> InputFormat);
-    int i;
+    cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap= T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse= T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat);
+    cmsUInt32Number i;
     cmsUInt8Number* Init = accum;
 
     if (DoSwap) {
@@ -536,7 +536,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
         cmsUInt16Number v = *(cmsUInt16Number*) accum;
 
         if (SwapEndian)
@@ -921,17 +921,17 @@
                                 register cmsUInt32Number Stride)
 {
 
-    int nChan      = T_CHANNELS(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->InputFormat);
-    int Reverse    = T_FLAVOR(info ->InputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    int Extra      = T_EXTRA(info -> InputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
-    int Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number nChan      = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap     = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
+    cmsUInt32Number Extra      = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
     cmsFloat64Number v;
     cmsUInt16Number  vi;
-    int i, start = 0;
-   cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0;
+    cmsUInt32Number i, start = 0;
+    cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0;
 
 
     if (ExtraFirst)
@@ -939,7 +939,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
             v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[(i + start) * Stride];
@@ -977,17 +977,17 @@
                                 register cmsUInt32Number Stride)
 {
 
-    int nChan      = T_CHANNELS(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->InputFormat);
-    int Reverse    = T_FLAVOR(info ->InputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    int Extra      = T_EXTRA(info -> InputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
-    int Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap   = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
+    cmsUInt32Number Extra   = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
     cmsFloat32Number v;
     cmsUInt16Number  vi;
-    int i, start = 0;
-   cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0;
+    cmsUInt32Number i, start = 0;
+    cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0;
 
 
     if (ExtraFirst)
@@ -995,7 +995,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
             v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride];
@@ -1054,15 +1054,15 @@
                                     cmsUInt32Number Stride)
 {
 
-    int nChan      = T_CHANNELS(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->InputFormat);
-    int Reverse    = T_FLAVOR(info ->InputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    int Extra      = T_EXTRA(info -> InputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
-    int Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap   = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
+    cmsUInt32Number Extra   = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
     cmsFloat32Number v;
-    int i, start = 0;
+    cmsUInt32Number i, start = 0;
     cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F;
 
 
@@ -1071,7 +1071,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
             v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride];
@@ -1106,15 +1106,15 @@
                                     cmsUInt32Number Stride)
 {
 
-    int nChan      = T_CHANNELS(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->InputFormat);
-    int Reverse    = T_FLAVOR(info ->InputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    int Extra      = T_EXTRA(info -> InputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
-    int Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap   = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
+    cmsUInt32Number Extra   = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
     cmsFloat64Number v;
-    int i, start = 0;
+    cmsUInt32Number i, start = 0;
     cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0;
 
 
@@ -1123,7 +1123,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
             v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[(i + start)  * Stride];
@@ -1277,15 +1277,15 @@
                              register cmsUInt8Number* output,
                              register cmsUInt32Number Stride)
 {
-    int nChan      = T_CHANNELS(info -> OutputFormat);
-    int DoSwap     = T_DOSWAP(info ->OutputFormat);
-    int Reverse    = T_FLAVOR(info ->OutputFormat);
-    int Extra      = T_EXTRA(info -> OutputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number nChan  = T_CHANNELS(info -> OutputFormat);
+    cmsUInt32Number DoSwap   = T_DOSWAP(info ->OutputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
+    cmsUInt32Number Extra   = T_EXTRA(info -> OutputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsUInt8Number* swap1;
     cmsUInt8Number v = 0;
-    int i;
+    cmsUInt32Number i;
 
     swap1 = output;
 
@@ -1295,7 +1295,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         v = FROM_16_TO_8(wOut[index]);
 
@@ -1329,16 +1329,16 @@
                              register cmsUInt8Number* output,
                              register cmsUInt32Number Stride)
 {
-    int nChan      = T_CHANNELS(info -> OutputFormat);
-    int SwapEndian = T_ENDIAN16(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->OutputFormat);
-    int Reverse    = T_FLAVOR(info ->OutputFormat);
-    int Extra      = T_EXTRA(info -> OutputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number nChan  = T_CHANNELS(info -> OutputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat);
+    cmsUInt32Number DoSwap   = T_DOSWAP(info ->OutputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
+    cmsUInt32Number Extra   = T_EXTRA(info -> OutputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsUInt16Number* swap1;
     cmsUInt16Number v = 0;
-    int i;
+    cmsUInt32Number i;
 
     swap1 = (cmsUInt16Number*) output;
 
@@ -1348,7 +1348,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         v = wOut[index];
 
@@ -1386,11 +1386,11 @@
                                 register cmsUInt8Number* output,
                                 register cmsUInt32Number Stride)
 {
-    int nChan     = T_CHANNELS(info -> OutputFormat);
-    int DoSwap    = T_DOSWAP(info ->OutputFormat);
-    int SwapFirst = T_SWAPFIRST(info ->OutputFormat);
-    int Reverse   = T_FLAVOR(info ->OutputFormat);
-    int i;
+    cmsUInt32Number nChan     = T_CHANNELS(info -> OutputFormat);
+    cmsUInt32Number DoSwap    = T_DOSWAP(info ->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->OutputFormat);
+    cmsUInt32Number Reverse   = T_FLAVOR(info ->OutputFormat);
+    cmsUInt32Number i;
     cmsUInt8Number* Init = output;
 
 
@@ -1401,7 +1401,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
         cmsUInt8Number v = FROM_16_TO_8(wOut[index]);
 
         *(cmsUInt8Number*)  output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v);
@@ -1420,11 +1420,11 @@
                                 register cmsUInt8Number* output,
                                 register cmsUInt32Number Stride)
 {
-    int nChan = T_CHANNELS(info -> OutputFormat);
-    int DoSwap = T_DOSWAP(info ->OutputFormat);
-    int Reverse= T_FLAVOR(info ->OutputFormat);
-    int SwapEndian = T_ENDIAN16(info -> OutputFormat);
-    int i;
+    cmsUInt32Number nChan      = T_CHANNELS(info -> OutputFormat);
+    cmsUInt32Number DoSwap     = T_DOSWAP(info ->OutputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat);
+    cmsUInt32Number i;
     cmsUInt8Number* Init = output;
     cmsUInt16Number v;
 
@@ -1434,7 +1434,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         v = wOut[index];
 
@@ -1794,9 +1794,9 @@
                                     register cmsUInt8Number* output,
                                     register cmsUInt32Number Stride)
 {
-    *output++ = (wOut[0] & 0xFF);
-    *output++ = (wOut[1] & 0xFF);
-    *output++ = (wOut[2] & 0xFF);
+    *output++ = (wOut[0] & 0xFFU);
+    *output++ = (wOut[1] & 0xFFU);
+    *output++ = (wOut[2] & 0xFFU);
 
     return output;
 
@@ -1826,9 +1826,9 @@
                                         register cmsUInt8Number* output,
                                         register cmsUInt32Number Stride)
 {
-    *output++ = (wOut[2] & 0xFF);
-    *output++ = (wOut[1] & 0xFF);
-    *output++ = (wOut[0] & 0xFF);
+    *output++ = (wOut[2] & 0xFFU);
+    *output++ = (wOut[1] & 0xFFU);
+    *output++ = (wOut[0] & 0xFFU);
 
     return output;
 
@@ -1917,9 +1917,9 @@
                                             register cmsUInt8Number* output,
                                             register cmsUInt32Number Stride)
 {
-    *output++ = (wOut[0] & 0xFF);
-    *output++ = (wOut[1] & 0xFF);
-    *output++ = (wOut[2] & 0xFF);
+    *output++ = (wOut[0] & 0xFFU);
+    *output++ = (wOut[1] & 0xFFU);
+    *output++ = (wOut[2] & 0xFFU);
     output++;
 
     return output;
@@ -1953,9 +1953,9 @@
                                                      register cmsUInt32Number Stride)
 {
     output++;
-    *output++ = (wOut[0] & 0xFF);
-    *output++ = (wOut[1] & 0xFF);
-    *output++ = (wOut[2] & 0xFF);
+    *output++ = (wOut[0] & 0xFFU);
+    *output++ = (wOut[1] & 0xFFU);
+    *output++ = (wOut[2] & 0xFFU);
 
     return output;
 
@@ -1987,9 +1987,9 @@
                                                 register cmsUInt32Number Stride)
 {
     output++;
-    *output++ = (wOut[2] & 0xFF);
-    *output++ = (wOut[1] & 0xFF);
-    *output++ = (wOut[0] & 0xFF);
+    *output++ = (wOut[2] & 0xFFU);
+    *output++ = (wOut[1] & 0xFFU);
+    *output++ = (wOut[0] & 0xFFU);
 
     return output;
 
@@ -2021,9 +2021,9 @@
                                                          register cmsUInt8Number* output,
                                                          register cmsUInt32Number Stride)
 {
-    *output++ = (wOut[2] & 0xFF);
-    *output++ = (wOut[1] & 0xFF);
-    *output++ = (wOut[0] & 0xFF);
+    *output++ = (wOut[2] & 0xFFU);
+    *output++ = (wOut[1] & 0xFFU);
+    *output++ = (wOut[0] & 0xFFU);
     output++;
 
     return output;
@@ -2379,24 +2379,24 @@
                                 register cmsUInt8Number* output,
                                 register cmsUInt32Number Stride)
 {
-    int nChan      = T_CHANNELS(info -> OutputFormat);
-    int DoSwap     = T_DOSWAP(info ->OutputFormat);
-    int Reverse    = T_FLAVOR(info ->OutputFormat);
-    int Extra      = T_EXTRA(info -> OutputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
-    int Planar     = T_PLANAR(info -> OutputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number nChan      = T_CHANNELS(info -> OutputFormat);
+    cmsUInt32Number DoSwap     = T_DOSWAP(info ->OutputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
+    cmsUInt32Number Extra      = T_EXTRA(info -> OutputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
+    cmsUInt32Number Planar     = T_PLANAR(info -> OutputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35 : 65535.0;
     cmsFloat64Number v = 0;
     cmsFloat64Number* swap1 = (cmsFloat64Number*) output;
-    int i, start = 0;
+    cmsUInt32Number i, start = 0;
 
     if (ExtraFirst)
         start = Extra;
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         v = (cmsFloat64Number) wOut[index] / maximum;
 
@@ -2430,24 +2430,24 @@
                                 register cmsUInt8Number* output,
                                 register cmsUInt32Number Stride)
 {
-       int nChan = T_CHANNELS(info->OutputFormat);
-       int DoSwap = T_DOSWAP(info->OutputFormat);
-       int Reverse = T_FLAVOR(info->OutputFormat);
-       int Extra = T_EXTRA(info->OutputFormat);
-       int SwapFirst = T_SWAPFIRST(info->OutputFormat);
-       int Planar = T_PLANAR(info->OutputFormat);
-       int ExtraFirst = DoSwap ^ SwapFirst;
+       cmsUInt32Number nChan      = T_CHANNELS(info->OutputFormat);
+       cmsUInt32Number DoSwap     = T_DOSWAP(info->OutputFormat);
+       cmsUInt32Number Reverse    = T_FLAVOR(info->OutputFormat);
+       cmsUInt32Number Extra      = T_EXTRA(info->OutputFormat);
+       cmsUInt32Number SwapFirst  = T_SWAPFIRST(info->OutputFormat);
+       cmsUInt32Number Planar     = T_PLANAR(info->OutputFormat);
+       cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
        cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 655.35 : 65535.0;
        cmsFloat64Number v = 0;
        cmsFloat32Number* swap1 = (cmsFloat32Number*)output;
-       int i, start = 0;
+       cmsUInt32Number i, start = 0;
 
        if (ExtraFirst)
               start = Extra;
 
        for (i = 0; i < nChan; i++) {
 
-              int index = DoSwap ? (nChan - i - 1) : i;
+              cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
               v = (cmsFloat64Number)wOut[index] / maximum;
 
@@ -2483,24 +2483,24 @@
                                     cmsUInt8Number* output,
                                     cmsUInt32Number Stride)
 {
-       int nChan = T_CHANNELS(info->OutputFormat);
-       int DoSwap = T_DOSWAP(info->OutputFormat);
-       int Reverse = T_FLAVOR(info->OutputFormat);
-       int Extra = T_EXTRA(info->OutputFormat);
-       int SwapFirst = T_SWAPFIRST(info->OutputFormat);
-       int Planar = T_PLANAR(info->OutputFormat);
-       int ExtraFirst = DoSwap ^ SwapFirst;
+       cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+       cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+       cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+       cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+       cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+       cmsUInt32Number Planar = T_PLANAR(info->OutputFormat);
+       cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
        cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 100.0 : 1.0;
        cmsFloat32Number* swap1 = (cmsFloat32Number*)output;
        cmsFloat64Number v = 0;
-       int i, start = 0;
+       cmsUInt32Number i, start = 0;
 
        if (ExtraFirst)
               start = Extra;
 
        for (i = 0; i < nChan; i++) {
 
-              int index = DoSwap ? (nChan - i - 1) : i;
+              cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
               v = wOut[index] * maximum;
 
@@ -2532,24 +2532,24 @@
                                     cmsUInt8Number* output,
                                     cmsUInt32Number Stride)
 {
-       int nChan = T_CHANNELS(info->OutputFormat);
-       int DoSwap = T_DOSWAP(info->OutputFormat);
-       int Reverse = T_FLAVOR(info->OutputFormat);
-       int Extra = T_EXTRA(info->OutputFormat);
-       int SwapFirst = T_SWAPFIRST(info->OutputFormat);
-       int Planar = T_PLANAR(info->OutputFormat);
-       int ExtraFirst = DoSwap ^ SwapFirst;
+       cmsUInt32Number nChan      = T_CHANNELS(info->OutputFormat);
+       cmsUInt32Number DoSwap     = T_DOSWAP(info->OutputFormat);
+       cmsUInt32Number Reverse    = T_FLAVOR(info->OutputFormat);
+       cmsUInt32Number Extra      = T_EXTRA(info->OutputFormat);
+       cmsUInt32Number SwapFirst  = T_SWAPFIRST(info->OutputFormat);
+       cmsUInt32Number Planar     = T_PLANAR(info->OutputFormat);
+       cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
        cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 100.0 : 1.0;
        cmsFloat64Number v = 0;
        cmsFloat64Number* swap1 = (cmsFloat64Number*)output;
-       int i, start = 0;
+       cmsUInt32Number i, start = 0;
 
        if (ExtraFirst)
               start = Extra;
 
        for (i = 0; i < nChan; i++) {
 
-              int index = DoSwap ? (nChan - i - 1) : i;
+              cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
               v = wOut[index] * maximum;
 
@@ -2706,15 +2706,15 @@
                                 register cmsUInt32Number Stride)
 {
 
-    int nChan      = T_CHANNELS(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->InputFormat);
-    int Reverse    = T_FLAVOR(info ->InputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    int Extra      = T_EXTRA(info -> InputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
-    int Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number nChan      = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap     = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
+    cmsUInt32Number Extra      = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
     cmsFloat32Number v;
-    int i, start = 0;
+    cmsUInt32Number i, start = 0;
     cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 655.35F : 65535.0F;
 
 
@@ -2723,7 +2723,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
             v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] );
@@ -2758,15 +2758,15 @@
                                     cmsUInt32Number Stride)
 {
 
-    int nChan      = T_CHANNELS(info -> InputFormat);
-    int DoSwap     = T_DOSWAP(info ->InputFormat);
-    int Reverse    = T_FLAVOR(info ->InputFormat);
-    int SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    int Extra      = T_EXTRA(info -> InputFormat);
-    int ExtraFirst = DoSwap ^ SwapFirst;
-    int Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number nChan      = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap     = T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
+    cmsUInt32Number Extra      = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
     cmsFloat32Number v;
-    int i, start = 0;
+    cmsUInt32Number i, start = 0;
     cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F;
 
 
@@ -2775,7 +2775,7 @@
 
     for (i=0; i < nChan; i++) {
 
-        int index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
             v =  _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] );
@@ -2808,24 +2808,24 @@
                                 register cmsUInt8Number* output,
                                 register cmsUInt32Number Stride)
 {
-       int nChan = T_CHANNELS(info->OutputFormat);
-       int DoSwap = T_DOSWAP(info->OutputFormat);
-       int Reverse = T_FLAVOR(info->OutputFormat);
-       int Extra = T_EXTRA(info->OutputFormat);
-       int SwapFirst = T_SWAPFIRST(info->OutputFormat);
-       int Planar = T_PLANAR(info->OutputFormat);
-       int ExtraFirst = DoSwap ^ SwapFirst;
+       cmsUInt32Number nChan      = T_CHANNELS(info->OutputFormat);
+       cmsUInt32Number DoSwap     = T_DOSWAP(info->OutputFormat);
+       cmsUInt32Number Reverse    = T_FLAVOR(info->OutputFormat);
+       cmsUInt32Number Extra      = T_EXTRA(info->OutputFormat);
+       cmsUInt32Number SwapFirst  = T_SWAPFIRST(info->OutputFormat);
+       cmsUInt32Number Planar     = T_PLANAR(info->OutputFormat);
+       cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
        cmsFloat32Number maximum = IsInkSpace(info->OutputFormat) ? 655.35F : 65535.0F;
        cmsFloat32Number v = 0;
        cmsUInt16Number* swap1 = (cmsUInt16Number*)output;
-       int i, start = 0;
+       cmsUInt32Number i, start = 0;
 
        if (ExtraFirst)
               start = Extra;
 
        for (i = 0; i < nChan; i++) {
 
-              int index = DoSwap ? (nChan - i - 1) : i;
+              cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
               v = (cmsFloat32Number)wOut[index] / maximum;
 
@@ -2859,24 +2859,24 @@
                                     cmsUInt8Number* output,
                                     cmsUInt32Number Stride)
 {
-       int nChan = T_CHANNELS(info->OutputFormat);
-       int DoSwap = T_DOSWAP(info->OutputFormat);
-       int Reverse = T_FLAVOR(info->OutputFormat);
-       int Extra = T_EXTRA(info->OutputFormat);
-       int SwapFirst = T_SWAPFIRST(info->OutputFormat);
-       int Planar = T_PLANAR(info->OutputFormat);
-       int ExtraFirst = DoSwap ^ SwapFirst;
+       cmsUInt32Number nChan      = T_CHANNELS(info->OutputFormat);
+       cmsUInt32Number DoSwap     = T_DOSWAP(info->OutputFormat);
+       cmsUInt32Number Reverse    = T_FLAVOR(info->OutputFormat);
+       cmsUInt32Number Extra      = T_EXTRA(info->OutputFormat);
+       cmsUInt32Number SwapFirst  = T_SWAPFIRST(info->OutputFormat);
+       cmsUInt32Number Planar     = T_PLANAR(info->OutputFormat);
+       cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
        cmsFloat32Number maximum = IsInkSpace(info->OutputFormat) ? 100.0F : 1.0F;
        cmsUInt16Number* swap1 = (cmsUInt16Number*)output;
        cmsFloat32Number v = 0;
-       int i, start = 0;
+       cmsUInt32Number i, start = 0;
 
        if (ExtraFirst)
               start = Extra;
 
        for (i = 0; i < nChan; i++) {
 
-              int index = DoSwap ? (nChan - i - 1) : i;
+           cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
               v = wOut[index] * maximum;
 
@@ -2907,7 +2907,7 @@
 // ----------------------------------------------------------------------------------------------------------------
 
 
-static cmsFormatters16 InputFormatters16[] = {
+static const cmsFormatters16 InputFormatters16[] = {
 
     //    Type                                          Mask                  Function
     //  ----------------------------   ------------------------------------  ----------------------------
@@ -2978,7 +2978,7 @@
 
 
 
-static cmsFormattersFloat InputFormattersFloat[] = {
+static const cmsFormattersFloat InputFormattersFloat[] = {
 
     //    Type                                          Mask                  Function
     //  ----------------------------   ------------------------------------  ----------------------------
@@ -3011,7 +3011,7 @@
 
     case CMS_PACK_FLAGS_16BITS: {
         for (i=0; i < sizeof(InputFormatters16) / sizeof(cmsFormatters16); i++) {
-            cmsFormatters16* f = InputFormatters16 + i;
+            const cmsFormatters16* f = InputFormatters16 + i;
 
             if ((dwInput & ~f ->Mask) == f ->Type) {
                 fr.Fmt16 = f ->Frm;
@@ -3023,7 +3023,7 @@
 
     case CMS_PACK_FLAGS_FLOAT: {
         for (i=0; i < sizeof(InputFormattersFloat) / sizeof(cmsFormattersFloat); i++) {
-            cmsFormattersFloat* f = InputFormattersFloat + i;
+            const cmsFormattersFloat* f = InputFormattersFloat + i;
 
             if ((dwInput & ~f ->Mask) == f ->Type) {
                 fr.FmtFloat = f ->Frm;
@@ -3041,7 +3041,7 @@
     return fr;
 }
 
-static cmsFormatters16 OutputFormatters16[] = {
+static const cmsFormatters16 OutputFormatters16[] = {
     //    Type                                          Mask                  Function
     //  ----------------------------   ------------------------------------  ----------------------------
 
@@ -3129,7 +3129,7 @@
 };
 
 
-static cmsFormattersFloat OutputFormattersFloat[] = {
+static const cmsFormattersFloat OutputFormattersFloat[] = {
     //    Type                                          Mask                                 Function
     //  ----------------------------   ---------------------------------------------------  ----------------------------
     {     TYPE_Lab_FLT,                                                ANYPLANAR|ANYEXTRA,   PackLabFloatFromFloat},
@@ -3147,8 +3147,6 @@
                              ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE,   PackHalfFromFloat },
 #endif
 
-
-
 };
 
 
@@ -3168,7 +3166,7 @@
      case CMS_PACK_FLAGS_16BITS: {
 
         for (i=0; i < sizeof(OutputFormatters16) / sizeof(cmsFormatters16); i++) {
-            cmsFormatters16* f = OutputFormatters16 + i;
+            const cmsFormatters16* f = OutputFormatters16 + i;
 
             if ((dwInput & ~f ->Mask) == f ->Type) {
                 fr.Fmt16 = f ->Frm;
@@ -3181,7 +3179,7 @@
     case CMS_PACK_FLAGS_FLOAT: {
 
         for (i=0; i < sizeof(OutputFormattersFloat) / sizeof(cmsFormattersFloat); i++) {
-            cmsFormattersFloat* f = OutputFormattersFloat + i;
+            const cmsFormattersFloat* f = OutputFormattersFloat + i;
 
             if ((dwInput & ~f ->Mask) == f ->Type) {
                 fr.FmtFloat = f ->Frm;
@@ -3321,7 +3319,7 @@
 // Return whatever given formatter refers to 8 bits
 cmsBool  _cmsFormatterIs8bit(cmsUInt32Number Type)
 {
-    int Bytes = T_BYTES(Type);
+    cmsUInt32Number Bytes = T_BYTES(Type);
 
     return (Bytes == 1);
 }
@@ -3331,9 +3329,9 @@
 {
 
     cmsColorSpaceSignature ColorSpace      = cmsGetColorSpace(hProfile);
-    cmsUInt32Number        ColorSpaceBits  = _cmsLCMScolorSpace(ColorSpace);
+    cmsUInt32Number        ColorSpaceBits  = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace);
     cmsUInt32Number        nOutputChans    = cmsChannelsOf(ColorSpace);
-    cmsUInt32Number        Float           = lIsFloat ? 1 : 0;
+    cmsUInt32Number        Float           = lIsFloat ? 1U : 0;
 
     // Create a fake formatter for result
     return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans);
@@ -3343,10 +3341,11 @@
 cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat)
 {
 
-    cmsColorSpaceSignature ColorSpace      = cmsGetPCS(hProfile);
-    int                    ColorSpaceBits  = _cmsLCMScolorSpace(ColorSpace);
-    cmsUInt32Number        nOutputChans    = cmsChannelsOf(ColorSpace);
-    cmsUInt32Number        Float           = lIsFloat ? 1 : 0;
+    cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
+
+    cmsUInt32Number ColorSpaceBits = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace);
+    cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace);
+    cmsUInt32Number Float = lIsFloat ? 1U : 0;
 
     // Create a fake formatter for result
     return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans);
diff --git a/third_party/lcms/src/cmspcs.c b/third_party/lcms/src/cmspcs.c
index 0cd8ecb..ea70484 100644
--- a/third_party/lcms/src/cmspcs.c
+++ b/third_party/lcms/src/cmspcs.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -656,9 +656,9 @@
 
 // This function returns a number of gridpoints to be used as LUT table. It assumes same number
 // of gripdpoints in all dimensions. Flags may override the choice.
-int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags)
+cmsUInt32Number _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags)
 {
-    int nChannels;
+    cmsUInt32Number nChannels;
 
     // Already specified?
     if (dwFlags & 0x00FF0000) {
@@ -802,7 +802,7 @@
        case PT_MCH14: return cmsSigMCHEData;
        case PT_MCH15: return cmsSigMCHFData;
 
-       default:  return (cmsColorSpaceSignature) (-1);
+       default:  return (cmsColorSpaceSignature) 0;
        }
 }
 
@@ -869,7 +869,7 @@
     case cmsSigMCHFData:
     case cmsSig15colorData:return PT_MCH15;
 
-    default:  return (cmsColorSpaceSignature) (-1);
+    default:  return (cmsColorSpaceSignature) 0;
     }
 }
 
diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c
index 80dd8d4..d54b4d1 100644
--- a/third_party/lcms/src/cmsplugin.c
+++ b/third_party/lcms/src/cmsplugin.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -172,19 +172,29 @@
 
     _cmsAssert(io != NULL);
 
-    if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
-            return FALSE;
+    if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
+        return FALSE;
 
     if (n != NULL) {
 
         tmp = _cmsAdjustEndianess32(tmp);
-        *n = *(cmsFloat32Number*) (void*) &tmp;
-        if (isnan(*n))
-            return FALSE;
+        *n = *(cmsFloat32Number*)(void*)&tmp;
+        
+        // Safeguard which covers against absurd values
+        if (*n > 1E+20 || *n < -1E+20) return FALSE;
+
+        #if defined(_MSC_VER) && _MSC_VER < 1800
+           return TRUE;
+        #elif defined (__BORLANDC__)
+           return TRUE;
+        #else
+
+           // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
+           return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
+        #endif        
     }
 
-    // fpclassify() required by C99
-    return (fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL);
+    return TRUE;
 }
 
 
@@ -197,7 +207,11 @@
     if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
             return FALSE;
 
-    if (n != NULL) _cmsAdjustEndianess64(n, &tmp);
+    if (n != NULL) {
+
+        _cmsAdjustEndianess64(n, &tmp);
+    }
+
     return TRUE;
 }
 
@@ -212,7 +226,7 @@
             return FALSE;
 
     if (n != NULL) {
-        *n = _cms15Fixed16toDouble(_cmsAdjustEndianess32(tmp));
+        *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
     }
 
     return TRUE;
@@ -229,9 +243,9 @@
 
     if (XYZ != NULL) {
 
-        XYZ->X = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.X));
-        XYZ->Y = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Y));
-        XYZ->Z = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Z));
+        XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
+        XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
+        XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
     }
     return TRUE;
 }
@@ -320,7 +334,7 @@
 
     _cmsAssert(io != NULL);
 
-    tmp = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(n));
+    tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
             return FALSE;
 
@@ -334,9 +348,9 @@
     _cmsAssert(io != NULL);
     _cmsAssert(XYZ != NULL);
 
-    xyz.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->X));
-    xyz.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Y));
-    xyz.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Z));
+    xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
+    xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
+    xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
 
     return io -> Write(io,  sizeof(cmsEncodedXYZNumber), &xyz);
 }
@@ -494,7 +508,7 @@
         return FALSE;   // Truncated, which is a fatal error for us
     }
 
-    rc = io ->Write(io, len, Buffer);
+    rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
 
     va_end(args);
 
@@ -754,6 +768,30 @@
     struct _cmsContext_struct* ctx;
     struct _cmsContext_struct  fakeContext;
         
+    // See the comments regarding locking in lcms2_internal.h
+    // for an explanation of why we need the following code.
+#ifdef CMS_IS_WINDOWS_
+#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+    {
+        static HANDLE _cmsWindowsInitMutex = NULL;
+        static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
+
+        if (*mutex == NULL)
+        {
+            HANDLE p = CreateMutex(NULL, FALSE, NULL);
+            if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL)
+                CloseHandle(p);
+        }
+        if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
+            return NULL;
+        if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL)
+            InitializeCriticalSection(&_cmsContextPoolHeadMutex);
+        if (*mutex == NULL || !ReleaseMutex(*mutex))
+            return NULL;
+    }
+#endif
+#endif
+
     _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
     
     fakeContext.chunks[UserPtr]     = UserData;
@@ -780,7 +818,7 @@
     ctx ->chunks[MemPlugin]   = &ctx->DefaultMemoryManager;
    
     // Now we can allocate the pool by using default memory manager
-    ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 32 pointers
+    ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 22 pointers
     if (ctx ->MemPool == NULL) {
 
          cmsDeleteContext(ctx);
diff --git a/third_party/lcms/src/cmsps2.c b/third_party/lcms/src/cmsps2.c
index 9635eaf..5802a14 100644
--- a/third_party/lcms/src/cmsps2.c
+++ b/third_party/lcms/src/cmsps2.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -400,7 +400,7 @@
 // Does write the intent
 
 static
-void EmitIntent(cmsIOHANDLER* m, int RenderingIntent)
+void EmitIntent(cmsIOHANDLER* m, cmsUInt32Number RenderingIntent)
 {
     const char *intent;
 
@@ -534,7 +534,7 @@
 // Compare gamma table
 
 static
-cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, int nEntries)
+cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nEntries)
 {
     return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;
 }
@@ -543,9 +543,9 @@
 // Does write a set of gamma curves
 
 static
-void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[])
+void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[])
 {
-    int i;
+    cmsUInt32Number i;
 
     for( i=0; i < n; i++ )
     {
@@ -768,7 +768,7 @@
 
 
 static
-int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXYZ* BlackPoint)
+int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint)
 {
     const char* PreMaj;
     const char* PostMaj;
@@ -828,14 +828,14 @@
 // Generates a curve from a gray profile
 
 static
-    cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
+cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent)
 {
     cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
     cmsHPROFILE hXYZ  = cmsCreateXYZProfile();
     cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
     int i;
 
-    if (Out != NULL) {
+    if (Out != NULL && xform != NULL) {
         for (i=0; i < 256; i++) {
 
             cmsUInt8Number Gray = (cmsUInt8Number) i;
@@ -847,8 +847,8 @@
         }
     }
 
-    cmsDeleteTransform(xform);
-    cmsCloseProfile(hXYZ);
+    if (xform) cmsDeleteTransform(xform);
+    if (hXYZ) cmsCloseProfile(hXYZ);
     return Out;
 }
 
@@ -858,7 +858,7 @@
 // a more perceptually uniform space... I do choose Lab.
 
 static
-int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
+int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
 {
     cmsHPROFILE hLab;
     cmsHTRANSFORM xform;
@@ -943,7 +943,6 @@
 
 
 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
-
 static
 int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
 {
@@ -969,17 +968,17 @@
 
             memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
 
-            for (i=0; i < 3; i++)
-                for (j=0; j < 3; j++)
+            for (i = 0; i < 3; i++)
+                for (j = 0; j < 3; j++)
                     Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
 
-            rc = EmitCIEBasedABC(m,  (cmsFloat64Number *) &Mat,
-                                _cmsStageGetPtrToCurveSet(Shaper),
-                                 &BlackPointAdaptedToD50);
+            rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat,
+                _cmsStageGetPtrToCurveSet(Shaper),
+                &BlackPointAdaptedToD50);
         }
-        else  {
+        else {
 
-            cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
+            cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
             return 0;
         }
 
@@ -992,12 +991,12 @@
 // This is a HP extension, and it works in Lab instead of XYZ
 
 static
-int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
+int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent)
 {
     cmsHTRANSFORM xform;
     cmsHPROFILE   hLab;
-    int i, nColors;
-    char ColorName[32];
+    cmsUInt32Number i, nColors;
+    char ColorName[cmsMAX_PATH];
     cmsNAMEDCOLORLIST* NamedColorList;
 
     hLab  = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
@@ -1275,20 +1274,20 @@
 // 8 bits.
 
 static
-int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
+int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
 {
     cmsHPROFILE hLab;
     cmsHTRANSFORM xform;
-    int i, nChannels;
+    cmsUInt32Number i, nChannels;
     cmsUInt32Number OutputFormat;
     _cmsTRANSFORM* v;
     cmsPipeline* DeviceLink;
     cmsHPROFILE Profiles[3];
     cmsCIEXYZ BlackPointAdaptedToD50;
-    cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
-    cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
+    cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
+    cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
     cmsUInt32Number InFrm = TYPE_Lab_16;
-    int RelativeEncodingIntent;
+    cmsUInt32Number RelativeEncodingIntent;
     cmsColorSpaceSignature ColorSpace;
 
 
@@ -1384,10 +1383,10 @@
 
 // Builds a ASCII string containing colorant list in 0..1.0 range
 static
-void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[])
+void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[])
 {
     char Buff[32];
-    int j;
+    cmsUInt32Number j;
 
     Colorant[0] = 0;
     if (nColorant > cmsMAXCHANNELS)
@@ -1409,12 +1408,12 @@
 // This is a HP extension.
 
 static
-int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags)
+int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
 {
     cmsHTRANSFORM xform;
-    int i, nColors, nColorant;
+    cmsUInt32Number i, nColors, nColorant;
     cmsUInt32Number OutputFormat;
-    char ColorName[32];
+    char ColorName[cmsMAX_PATH];
     char Colorant[128];
     cmsNAMEDCOLORLIST* NamedColorList;
 
diff --git a/third_party/lcms/src/cmssamp.c b/third_party/lcms/src/cmssamp.c
index a9997fa..1fc5f5d 100644
--- a/third_party/lcms/src/cmssamp.c
+++ b/third_party/lcms/src/cmssamp.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
diff --git a/third_party/lcms/src/cmssm.c b/third_party/lcms/src/cmssm.c
index 0f7cb7f..a0fdbc8 100644
--- a/third_party/lcms/src/cmssm.c
+++ b/third_party/lcms/src/cmssm.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
index 8b02f86..0c24da1 100644
--- a/third_party/lcms/src/cmstypes.c
+++ b/third_party/lcms/src/cmstypes.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -60,6 +60,11 @@
 // Helper macro to define a MPE handler. Callbacks do have a fixed naming convention
 #define TYPE_MPE_HANDLER(t, x)  { (t), READ_FN(x), WRITE_FN(x), GenericMPEdup, GenericMPEfree, NULL, 0 }
 
+// Infinites
+#define MINUS_INF   (-1E22F)
+#define PLUS_INF    (+1E22F)
+
+
 // Register a new type handler. This routine is shared between normal types and MPE. LinkedList points to the optional list head
 static
 cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos)
@@ -176,7 +181,8 @@
     cmsUInt32Number currentPosition;
 
     currentPosition = io->Tell(io);
-    // Verify there is enough space left to read two cmsUInt32Number items for Count items.
+
+    // Verify there is enough space left to read at least two cmsUInt32Number items for Count items.
     if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count)
         return FALSE;
 
@@ -238,7 +244,7 @@
 
     // Keep starting position of curve offsets
     DirectoryPos = io ->Tell(io);
-
+  
     // Write a fake directory to be filled latter on
     for (i=0; i < Count; i++) {
 
@@ -401,8 +407,8 @@
 static
 cmsBool  SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io)
 {
-    if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(x))) return FALSE;
-    if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(y))) return FALSE;
+    if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(x))) return FALSE;
+    if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(y))) return FALSE;
 
     return TRUE;
 }
@@ -961,14 +967,14 @@
     cmsBool  rc = FALSE;
     char Filler[68];
 
-    // Used below for writting zeroes
+    // Used below for writing zeroes
     memset(Filler, 0, sizeof(Filler));
 
     // Get the len of string
     len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
 
     // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data
-    //(see clause 4.1 for the definition of 'aligned'. Because the Unicode language
+    //(see clause 4.1 for the definition of "aligned"). Because the Unicode language
     // code and Unicode count immediately follow the ASCII description, their
     // alignment is not correct if the ASCII count is not a multiple of four. The
     // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and
@@ -1124,8 +1130,8 @@
                if (!NewGamma) return NULL;
 
                if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) {
-                 cmsFreeToneCurve(NewGamma);
-                 return NULL;
+                   cmsFreeToneCurve(NewGamma);
+                   return NULL;
                }
 
                *nItems = 1;
@@ -1186,7 +1192,7 @@
 // ********************************************************************************
 
 
-// Decide which curve type to use on writting
+// Decide which curve type to use on writing
 static
 cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data)
 {
@@ -1468,7 +1474,8 @@
         if (!_cmsReadUInt32Number(io, &Offset)) goto Error;
 
         // Check for overflow
-        if (Offset < (SizeOfHeader + 8)) goto Error;
+        if (Offset < (SizeOfHeader + 8)) goto Error;        
+        if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error;
 
         // True begin of the string
         BeginOfThisString = Offset - SizeOfHeader - 8;
@@ -1578,7 +1585,7 @@
 // Type cmsSigLut8Type
 // ********************************************************************************
 
-// Decide which LUT type to use on writting
+// Decide which LUT type to use on writing
 static
 cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data)
 {
@@ -1627,10 +1634,10 @@
 
 // Read 8 bit tables as gamma functions
 static
-cmsBool  Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels)
+cmsBool  Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, cmsUInt32Number nChannels)
 {
     cmsUInt8Number* Temp = NULL;
-    int i, j;
+    cmsUInt32Number i, j;
     cmsToneCurve* Tables[cmsMAXCHANNELS];
 
     if (nChannels > cmsMAXCHANNELS) return FALSE;
@@ -1821,8 +1828,10 @@
         _cmsFree(self ->ContextID, Temp);
         Temp = NULL;
 
-        if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T)))
+        if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) {
+            _cmsFree(self ->ContextID, T);
             goto Error;
+        }
         _cmsFree(self ->ContextID, T);
     }
 
@@ -1851,7 +1860,7 @@
     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
     _cmsStageMatrixData* MatMPE = NULL;
     _cmsStageCLutData* clut = NULL;
-    int clutPoints;
+    cmsUInt32Number clutPoints;
 
     // Disassemble the LUT into components.
     mpe = NewLUT -> Elements;
@@ -1971,9 +1980,10 @@
 
 // Read 16 bit tables as gamma functions
 static
-cmsBool  Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels, int nEntries)
+cmsBool  Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, 
+                                    cmsUInt32Number nChannels, cmsUInt32Number nEntries)
 {
-    int i;
+    cmsUInt32Number i;
     cmsToneCurve* Tables[cmsMAXCHANNELS];
 
     // Maybe an empty table? (this is a lcms extension)
@@ -2015,10 +2025,10 @@
 static
 cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables)
 {
-    int j;
+    cmsUInt32Number j;
     cmsUInt32Number i;
     cmsUInt16Number val;
-    int nEntries;
+    cmsUInt32Number nEntries;
 
     _cmsAssert(Tables != NULL);
 
@@ -2139,7 +2149,7 @@
     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
     _cmsStageMatrixData* MatMPE = NULL;
     _cmsStageCLutData* clut = NULL;
-    int i, InputChannels, OutputChannels, clutPoints;
+    cmsUInt32Number i, InputChannels, OutputChannels, clutPoints;
 
     // Disassemble the LUT into components.
     mpe = NewLUT -> Elements;
@@ -2325,7 +2335,8 @@
 //  V4 stuff. Read CLUT part for LutAtoB and LutBtoA
 
 static
-cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, int InputChannels, int OutputChannels)
+cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, 
+                   cmsUInt32Number Offset, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels)
 {
     cmsUInt8Number  gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension.
     cmsUInt32Number GridPoints[cmsMAXCHANNELS], i;
@@ -2362,8 +2373,8 @@
         for (i=0; i < Data ->nEntries; i++) {
 
             if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) {
-              cmsStageFree(CLUT);
-              return NULL;
+                cmsStageFree(CLUT);
+                return NULL;
             }
             Data ->Tab.T[i] = FROM_8_TO_16(v);
         }
@@ -2679,7 +2690,7 @@
 cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
     cmsPipeline* Lut = (cmsPipeline*) Ptr;
-    int inputChan, outputChan;
+    cmsUInt32Number inputChan, outputChan;
     cmsStage *A = NULL, *B = NULL, *M = NULL;
     cmsStage * Matrix = NULL;
     cmsStage * CLUT = NULL;
@@ -2727,7 +2738,7 @@
 
     if (CLUT != NULL) {
         offsetC = io ->Tell(io) - BaseOffset;
-        if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE;
+        if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE;
 
     }
     if (M != NULL) {
@@ -2867,7 +2878,7 @@
 cmsBool  Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
     cmsPipeline* Lut = (cmsPipeline*) Ptr;
-    int inputChan, outputChan;
+    cmsUInt32Number inputChan, outputChan;
     cmsStage *A = NULL, *B = NULL, *M = NULL;
     cmsStage *Matrix = NULL;
     cmsStage *CLUT = NULL;
@@ -2909,7 +2920,7 @@
 
     if (CLUT != NULL) {
         offsetC = io ->Tell(io) - BaseOffset;
-        if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE;
+        if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE;
 
     }
     if (M != NULL) {
@@ -2984,7 +2995,7 @@
 {
     cmsUInt32Number i, Count;
     cmsNAMEDCOLORLIST* List;
-    char Name[33];
+    char Name[34];
     cmsUInt16Number PCS[3];
 
 
@@ -3025,7 +3036,7 @@
 cmsBool  Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
-    int i, nColors;
+    cmsUInt32Number i, nColors;
 
     nColors = cmsNamedColorCount(NamedColorList);
 
@@ -3033,9 +3044,11 @@
 
     for (i=0; i < nColors; i++) {
 
-        char root[33];
+        char root[cmsMAX_PATH];
         cmsUInt16Number PCS[3];
 
+        memset(root, 0, sizeof(root));
+
         if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0;
         root[32] = 0;
 
@@ -3078,8 +3091,8 @@
 //The namedColor2Type is a count value and array of structures that provide color
 //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
 //device representation of the color are given. Both representations are 16-bit values.
-//The device representation corresponds to the header's 'color space of data' field.
-//This representation should be consistent with the 'number of device components'
+//The device representation corresponds to the header's "color space of data" field.
+//This representation should be consistent with the "number of device components"
 //field in the namedColor2Type. If this field is 0, device coordinates are not provided.
 //The PCS representation corresponds to the header's PCS field. The PCS representation
 //is always provided. Color names are fixed-length, 32-byte fields including null
@@ -3095,8 +3108,8 @@
     cmsUInt32Number      nDeviceCoords;  // Num of device coordinates
     char                 prefix[32];     // Prefix for each color name
     char                 suffix[32];     // Suffix for each color name
-    cmsNAMEDCOLORLIST*  v;
-    cmsUInt32Number i;
+    cmsNAMEDCOLORLIST*   v;
+    cmsUInt32Number      i;
 
 
     *nItems = 0;
@@ -3153,7 +3166,7 @@
     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
     char                prefix[33];     // Prefix for each color name
     char                suffix[33];     // Suffix for each color name
-    int i, nColors;
+    cmsUInt32Number     i, nColors;
 
     nColors = cmsNamedColorCount(NamedColorList);
 
@@ -3173,7 +3186,7 @@
 
        cmsUInt16Number PCS[3];
        cmsUInt16Number Colorant[cmsMAXCHANNELS];
-       char Root[33];
+       char Root[cmsMAX_PATH];
 
         if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0;
         Root[32] = 0;
@@ -3826,7 +3839,7 @@
 // ********************************************************************************
 //
 //This type represents a set of viewing condition parameters including:
-//CIE 'absolute'illuminant white point tristimulus values and CIE 'absolute'
+//CIE 'absolute' illuminant white point tristimulus values and CIE 'absolute'
 //surround tristimulus values.
 
 static
@@ -3927,7 +3940,7 @@
     cmsUInt16Number nSegments;
     cmsCurveSegment*  Segments;
     cmsToneCurve* Curve;
-    cmsFloat32Number PrevBreak = -1E22F;    // - infinite
+    cmsFloat32Number PrevBreak = MINUS_INF;    // - infinite
 
     // Take signature and channels for each element.
      if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL;
@@ -3952,7 +3965,7 @@
      }
 
      Segments[nSegments-1].x0 = PrevBreak;
-     Segments[nSegments-1].x1 = 1E22F;     // A big cmsFloat32Number number
+     Segments[nSegments-1].x1 = PLUS_INF;     // A big cmsFloat32Number number
 
      // Read segments
      for (i=0; i < nSegments; i++) {
@@ -4078,7 +4091,7 @@
     }
 
     _cmsFree(self ->ContextID, GammaTables);
-    *nItems = (mpe != NULL) ? 1 : 0;
+    *nItems = (mpe != NULL) ? 1U : 0;
     return mpe;
 
     cmsUNUSED_PARAMETER(SizeOfTag);
@@ -4214,7 +4227,7 @@
     if (InputChans >= cmsMAXCHANNELS) return NULL;
     if (OutputChans >= cmsMAXCHANNELS) return NULL;
 
-    nElems = InputChans * OutputChans;
+    nElems = (cmsUInt32Number) InputChans * OutputChans;
 
     Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number));
     if (Matrix == NULL) return NULL;
@@ -4318,22 +4331,22 @@
         goto Error;
 
     // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number
-    nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans;
+    nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? (cmsUInt32Number) MAX_INPUT_DIMENSIONS : InputChans;
 
     for (i = 0; i < nMaxGrids; i++) {
         if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
         GridPoints[i] = (cmsUInt32Number)Dimensions8[i];
     }
-
+    
     // Allocate the true CLUT
     mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL);
     if (mpe == NULL) goto Error;
 
-    // Read the data
+    // Read and sanitize the data
     clut = (_cmsStageCLutData*) mpe ->Data;
     for (i=0; i < clut ->nEntries; i++) {
 
-        if (!_cmsReadFloat32Number(io, &clut ->Tab.TFloat[i])) goto Error;
+        if (!_cmsReadFloat32Number(io, &clut->Tab.TFloat[i])) goto Error;       
     }
 
     *nItems = 1;
@@ -4470,15 +4483,19 @@
     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans);
     if (NewLUT == NULL) return NULL;
 
-    if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error;
+    if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error;    
     if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error;
 
+    // Check channel count
+    if (InputChans != NewLUT->InputChannels ||
+        OutputChans != NewLUT->OutputChannels) goto Error;
+
     // Success
     *nItems = 1;
     return NewLUT;
 
     // Error
-Error:
+Error:    
     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
     *nItems = 0;
     return NULL;
@@ -4493,7 +4510,7 @@
 cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
     cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos;
-    int inputChan, outputChan;
+    cmsUInt32Number inputChan, outputChan;
     cmsUInt32Number ElemCount;
     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before;
     cmsStageSignature ElementSig;
@@ -4541,7 +4558,7 @@
 
                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
 
-                 // An unknow element was found.
+                 // An unknown element was found.
                  cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String);
                  goto Error;
         }
@@ -5276,38 +5293,38 @@
 
 
 // This is the list of built-in types
-static _cmsTagTypeLinkedList SupportedTagTypes[] = {
+static const _cmsTagTypeLinkedList SupportedTagTypes[] = {
 
-{TYPE_HANDLER(cmsSigChromaticityType,          Chromaticity),        &SupportedTagTypes[1] },
-{TYPE_HANDLER(cmsSigColorantOrderType,         ColorantOrderType),   &SupportedTagTypes[2] },
-{TYPE_HANDLER(cmsSigS15Fixed16ArrayType,       S15Fixed16),          &SupportedTagTypes[3] },
-{TYPE_HANDLER(cmsSigU16Fixed16ArrayType,       U16Fixed16),          &SupportedTagTypes[4] },
-{TYPE_HANDLER(cmsSigTextType,                  Text),                &SupportedTagTypes[5] },
-{TYPE_HANDLER(cmsSigTextDescriptionType,       Text_Description),    &SupportedTagTypes[6] },
-{TYPE_HANDLER(cmsSigCurveType,                 Curve),               &SupportedTagTypes[7] },
-{TYPE_HANDLER(cmsSigParametricCurveType,       ParametricCurve),     &SupportedTagTypes[8] },
-{TYPE_HANDLER(cmsSigDateTimeType,              DateTime),            &SupportedTagTypes[9] },
-{TYPE_HANDLER(cmsSigLut8Type,                  LUT8),                &SupportedTagTypes[10] },
-{TYPE_HANDLER(cmsSigLut16Type,                 LUT16),               &SupportedTagTypes[11] },
-{TYPE_HANDLER(cmsSigColorantTableType,         ColorantTable),       &SupportedTagTypes[12] },
-{TYPE_HANDLER(cmsSigNamedColor2Type,           NamedColor),          &SupportedTagTypes[13] },
-{TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU),                 &SupportedTagTypes[14] },
-{TYPE_HANDLER(cmsSigProfileSequenceDescType,   ProfileSequenceDesc), &SupportedTagTypes[15] },
-{TYPE_HANDLER(cmsSigSignatureType,             Signature),           &SupportedTagTypes[16] },
-{TYPE_HANDLER(cmsSigMeasurementType,           Measurement),         &SupportedTagTypes[17] },
-{TYPE_HANDLER(cmsSigDataType,                  Data),                &SupportedTagTypes[18] },
-{TYPE_HANDLER(cmsSigLutAtoBType,               LUTA2B),              &SupportedTagTypes[19] },
-{TYPE_HANDLER(cmsSigLutBtoAType,               LUTB2A),              &SupportedTagTypes[20] },
-{TYPE_HANDLER(cmsSigUcrBgType,                 UcrBg),               &SupportedTagTypes[21] },
-{TYPE_HANDLER(cmsSigCrdInfoType,               CrdInfo),             &SupportedTagTypes[22] },
-{TYPE_HANDLER(cmsSigMultiProcessElementType,   MPE),                 &SupportedTagTypes[23] },
-{TYPE_HANDLER(cmsSigScreeningType,             Screening),           &SupportedTagTypes[24] },
-{TYPE_HANDLER(cmsSigViewingConditionsType,     ViewingConditions),   &SupportedTagTypes[25] },
-{TYPE_HANDLER(cmsSigXYZType,                   XYZ),                 &SupportedTagTypes[26] },
-{TYPE_HANDLER(cmsCorbisBrokenXYZtype,          XYZ),                 &SupportedTagTypes[27] },
-{TYPE_HANDLER(cmsMonacoBrokenCurveType,        Curve),               &SupportedTagTypes[28] },
-{TYPE_HANDLER(cmsSigProfileSequenceIdType,     ProfileSequenceId),   &SupportedTagTypes[29] },
-{TYPE_HANDLER(cmsSigDictType,                  Dictionary),          &SupportedTagTypes[30] },
+{TYPE_HANDLER(cmsSigChromaticityType,          Chromaticity),       (_cmsTagTypeLinkedList*) &SupportedTagTypes[1] },
+{TYPE_HANDLER(cmsSigColorantOrderType,         ColorantOrderType),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[2] },
+{TYPE_HANDLER(cmsSigS15Fixed16ArrayType,       S15Fixed16),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[3] },
+{TYPE_HANDLER(cmsSigU16Fixed16ArrayType,       U16Fixed16),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[4] },
+{TYPE_HANDLER(cmsSigTextType,                  Text),               (_cmsTagTypeLinkedList*) &SupportedTagTypes[5] },
+{TYPE_HANDLER(cmsSigTextDescriptionType,       Text_Description),   (_cmsTagTypeLinkedList*) &SupportedTagTypes[6] },
+{TYPE_HANDLER(cmsSigCurveType,                 Curve),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[7] },
+{TYPE_HANDLER(cmsSigParametricCurveType,       ParametricCurve),    (_cmsTagTypeLinkedList*) &SupportedTagTypes[8] },
+{TYPE_HANDLER(cmsSigDateTimeType,              DateTime),           (_cmsTagTypeLinkedList*) &SupportedTagTypes[9] },
+{TYPE_HANDLER(cmsSigLut8Type,                  LUT8),               (_cmsTagTypeLinkedList*) &SupportedTagTypes[10] },
+{TYPE_HANDLER(cmsSigLut16Type,                 LUT16),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[11] },
+{TYPE_HANDLER(cmsSigColorantTableType,         ColorantTable),      (_cmsTagTypeLinkedList*) &SupportedTagTypes[12] },
+{TYPE_HANDLER(cmsSigNamedColor2Type,           NamedColor),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[13] },
+{TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[14] },
+{TYPE_HANDLER(cmsSigProfileSequenceDescType,   ProfileSequenceDesc),(_cmsTagTypeLinkedList*) &SupportedTagTypes[15] },
+{TYPE_HANDLER(cmsSigSignatureType,             Signature),          (_cmsTagTypeLinkedList*) &SupportedTagTypes[16] },
+{TYPE_HANDLER(cmsSigMeasurementType,           Measurement),        (_cmsTagTypeLinkedList*) &SupportedTagTypes[17] },
+{TYPE_HANDLER(cmsSigDataType,                  Data),               (_cmsTagTypeLinkedList*) &SupportedTagTypes[18] },
+{TYPE_HANDLER(cmsSigLutAtoBType,               LUTA2B),             (_cmsTagTypeLinkedList*) &SupportedTagTypes[19] },
+{TYPE_HANDLER(cmsSigLutBtoAType,               LUTB2A),             (_cmsTagTypeLinkedList*) &SupportedTagTypes[20] },
+{TYPE_HANDLER(cmsSigUcrBgType,                 UcrBg),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[21] },
+{TYPE_HANDLER(cmsSigCrdInfoType,               CrdInfo),            (_cmsTagTypeLinkedList*) &SupportedTagTypes[22] },
+{TYPE_HANDLER(cmsSigMultiProcessElementType,   MPE),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[23] },
+{TYPE_HANDLER(cmsSigScreeningType,             Screening),          (_cmsTagTypeLinkedList*) &SupportedTagTypes[24] },
+{TYPE_HANDLER(cmsSigViewingConditionsType,     ViewingConditions),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[25] },
+{TYPE_HANDLER(cmsSigXYZType,                   XYZ),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[26] },
+{TYPE_HANDLER(cmsCorbisBrokenXYZtype,          XYZ),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[27] },
+{TYPE_HANDLER(cmsMonacoBrokenCurveType,        Curve),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] },
+{TYPE_HANDLER(cmsSigProfileSequenceIdType,     ProfileSequenceId),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] },
+{TYPE_HANDLER(cmsSigDictType,                  Dictionary),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] },
 {TYPE_HANDLER(cmsSigVcgtType,                  vcgt),                NULL }
 };
 
@@ -5399,7 +5416,7 @@
 {
     _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin);
 
-    return GetHandler(sig, ctx->TagTypes, SupportedTagTypes);
+    return GetHandler(sig, ctx->TagTypes, (_cmsTagTypeLinkedList*) SupportedTagTypes);
 }
 
 // ********************************************************************************
@@ -5414,7 +5431,7 @@
 
 } _cmsTagLinkedList;
 
-// This is the list of built-in tags
+// This is the list of built-in tags. The data of this list can be modified by plug-ins
 static _cmsTagLinkedList SupportedTags[] = {
 
     { cmsSigAToB0Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]},
@@ -5463,7 +5480,7 @@
     { cmsSigPreview2Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]},
 
     { cmsSigProfileDescriptionTag,  { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]},
-    { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]},
+    { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL},  &SupportedTags[34]},
     { cmsSigTechnologyTag,          { 1, 1, { cmsSigSignatureType }, NULL},  &SupportedTags[35]},
 
     { cmsSigColorimetricIntentImageStateTag,   { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]},
@@ -5500,10 +5517,10 @@
     { cmsSigVcgtTag,                { 1, 1, { cmsSigVcgtType},               NULL }, &SupportedTags[60]},
     { cmsSigMetaTag,                { 1, 1, { cmsSigDictType},               NULL }, &SupportedTags[61]},
     { cmsSigProfileSequenceIdTag,   { 1, 1, { cmsSigProfileSequenceIdType},  NULL }, &SupportedTags[62]},
+
     { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]},
     { cmsSigArgyllArtsTag,          { 9, 1, { cmsSigS15Fixed16ArrayType},    NULL}, NULL}
 
-
 };
 
 /*
diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c
index 9eff1f7..935effc 100644
--- a/third_party/lcms/src/cmsvirt.c
+++ b/third_party/lcms/src/cmsvirt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -291,7 +291,7 @@
 {
     cmsHPROFILE hICC;
     cmsPipeline* Pipeline;
-    int nChannels;
+    cmsUInt32Number nChannels;
 
     hICC = cmsCreateProfilePlaceholder(ContextID);
     if (!hICC)
@@ -397,7 +397,7 @@
     cmsHPROFILE hICC;
     cmsPipeline* LUT;
     cmsStage* CLUT;
-    int nChannels;
+    cmsUInt32Number nChannels;
 
     if (ColorSpace != cmsSigCmykData) {
         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
@@ -726,13 +726,13 @@
 // contrast, Saturation and white point displacement
 
 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
-    int nLUTPoints,
-    cmsFloat64Number Bright,
-    cmsFloat64Number Contrast,
-    cmsFloat64Number Hue,
-    cmsFloat64Number Saturation,
-    int TempSrc,
-    int TempDest)
+                                                       cmsUInt32Number nLUTPoints,
+                                                       cmsFloat64Number Bright,
+                                                       cmsFloat64Number Contrast,
+                                                       cmsFloat64Number Hue,
+                                                       cmsFloat64Number Saturation,
+                                                       cmsUInt32Number TempSrc,
+                                                       cmsUInt32Number TempDest)
 {
     cmsHPROFILE hICC;
     cmsPipeline* Pipeline;
@@ -740,7 +740,7 @@
     cmsCIExyY WhitePnt;
     cmsStage* CLUT;
     cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
-    int i;
+    cmsUInt32Number i;
 
     bchsw.Brightness = Bright;
     bchsw.Contrast   = Contrast;
@@ -778,7 +778,7 @@
 
     for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
     CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
-    if (CLUT == NULL) return NULL;
+    if (CLUT == NULL) goto Error;
 
 
     if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
@@ -811,13 +811,13 @@
 }
 
 
-CMSAPI cmsHPROFILE   CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
+CMSAPI cmsHPROFILE   CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,
                                                              cmsFloat64Number Bright,
                                                              cmsFloat64Number Contrast,
                                                              cmsFloat64Number Hue,
                                                              cmsFloat64Number Saturation,
-                                                             int TempSrc,
-                                                             int TempDest)
+                                                             cmsUInt32Number TempSrc,
+                                                             cmsUInt32Number TempDest)
 {
     return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
 }
@@ -830,8 +830,10 @@
     cmsHPROFILE hProfile;
     cmsPipeline* LUT = NULL;
     cmsStage* PostLin;
-    cmsToneCurve* EmptyTab;
+    cmsStage* OutLin;
+    cmsToneCurve* EmptyTab[3];
     cmsUInt16Number Zero[2] = { 0, 0 };
+    const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
 
     hProfile = cmsCreateProfilePlaceholder(ContextID);
     if (!hProfile)                          // can't allocate
@@ -842,22 +844,28 @@
     if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
 
 
-
     cmsSetDeviceClass(hProfile, cmsSigOutputClass);
     cmsSetColorSpace(hProfile,  cmsSigGrayData);
     cmsSetPCS(hProfile,         cmsSigLabData);
 
-    // An empty LUTs is all we need
-    LUT = cmsPipelineAlloc(ContextID, 1, 1);
+    // Create a valid ICC 4 structure
+    LUT = cmsPipelineAlloc(ContextID, 3, 1);
     if (LUT == NULL) goto Error;
-
-    EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
-    PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab);
-    cmsFreeToneCurve(EmptyTab);
+    
+    EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
+    PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
+    OutLin  = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
+    cmsFreeToneCurve(EmptyTab[0]);
 
     if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
         goto Error;
 
+    if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
+        goto Error;
+
+    if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin))
+        goto Error;
+
     if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
     if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
 
@@ -938,7 +946,7 @@
 {
     _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
     cmsHPROFILE hICC = NULL;
-    int i, nColors;
+    cmsUInt32Number i, nColors;
     cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
 
     // Create an empty placeholder
@@ -1055,7 +1063,7 @@
 {
     cmsHPROFILE hProfile = NULL;
     cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
-    cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut;
+    int ColorSpaceBitsIn, ColorSpaceBitsOut;
     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
     cmsPipeline* LUT = NULL;
     cmsStage* mpe;
diff --git a/third_party/lcms/src/cmswtpnt.c b/third_party/lcms/src/cmswtpnt.c
index c6b6125..9f90d6a 100644
--- a/third_party/lcms/src/cmswtpnt.c
+++ b/third_party/lcms/src/cmswtpnt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -76,7 +76,6 @@
         }
 
         // Obtain y(x)
-
         y = -3.000*(x*x) + 2.870*x - 0.275;
 
         // wave factors (not used, but here for futures extensions)
@@ -102,7 +101,7 @@
 
     } ISOTEMPERATURE;
 
-static ISOTEMPERATURE isotempdata[] = {
+static const ISOTEMPERATURE isotempdata[] = {
 //  {Mirek, Ut,       Vt,      Tt      }
     {0,     0.18006,  0.26352,  -0.24341},
     {10,    0.18066,  0.26589,  -0.25479},
@@ -266,7 +265,7 @@
 // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
 // This is just an approximation, I am not handling all the non-linear
 // aspects of the RGB to XYZ process, and assumming that the gamma correction
-// has transitive property in the tranformation chain.
+// has transitive property in the transformation chain.
 //
 // the alghoritm:
 //
diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c
index b3802f0..508117b 100644
--- a/third_party/lcms/src/cmsxform.c
+++ b/third_party/lcms/src/cmsxform.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -255,6 +255,8 @@
 
     strideIn = 0;
     strideOut = 0;
+    memset(fIn, 0, sizeof(fIn));
+    memset(fOut, 0, sizeof(fIn));
 
     for (i = 0; i < LineCount; i++) {
 
@@ -319,6 +321,7 @@
 
     strideIn = 0;
     strideOut = 0;
+    memset(fIn, 0, sizeof(fIn));
 
     for (i = 0; i < LineCount; i++) {
 
@@ -356,6 +359,7 @@
 
     strideIn = 0;
     strideOut = 0;
+    memset(wIn, 0, sizeof(wIn));
 
     for (i = 0; i < LineCount; i++) {
 
@@ -393,6 +397,8 @@
 
     strideIn = 0;
     strideOut = 0;
+    memset(wIn, 0, sizeof(wIn));
+    memset(wOut, 0, sizeof(wOut));
 
     for (i = 0; i < LineCount; i++) {
 
@@ -454,6 +460,8 @@
 
     strideIn = 0;
     strideOut = 0;
+    memset(wIn, 0, sizeof(wIn));
+    memset(wOut, 0, sizeof(wOut));
 
     for (i = 0; i < LineCount; i++) {
 
@@ -762,9 +770,9 @@
        // Allocate needed memory
        _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
        if (!p) {
-          cmsPipelineFree(lut);
-          return NULL;
-      }
+              cmsPipelineFree(lut);
+              return NULL;
+       }
 
        // Store the proposed pipeline
        p->Lut = lut;
@@ -797,7 +805,7 @@
                             p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
                             p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
 
-                            // Save the day?
+                            // Save the day? (Ignore the warning)
                             if (Plugin->OldXform) {
                                    p->OldXform = (_cmsTransformFn) p->xform;
                                    p->xform = _cmsTransform2toTransformAdaptor;
@@ -844,7 +852,7 @@
         }
         else {
 
-            int BytesPerPixelInput;
+            cmsUInt32Number BytesPerPixelInput;
 
             p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
             p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
@@ -894,13 +902,13 @@
 }
 
 static
-cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
+cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
 {
     cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
     cmsColorSpaceSignature PostColorSpace;
-    int i;
+    cmsUInt32Number i;
 
-    if (nProfiles <= 0) return FALSE;
+    if (nProfiles == 0) return FALSE;
     if (hProfiles[0] == NULL) return FALSE;
 
     *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
@@ -949,7 +957,7 @@
 static
 cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
 {
-    int Space1 = T_COLORSPACE(dwFormat);
+    int Space1 = (int) T_COLORSPACE(dwFormat);
     int Space2 = _cmsLCMScolorSpace(Check);
 
     if (Space1 == PT_ANY) return TRUE;
@@ -1205,7 +1213,7 @@
     hArray[0] = Input;
     hArray[1] = Output;
 
-    return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
+    return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
 }
 
 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
diff --git a/third_party/lcms/src/lcms2_internal.h b/third_party/lcms/src/lcms2_internal.h
index 115ac59..bd1c86b 100644
--- a/third_party/lcms/src/lcms2_internal.h
+++ b/third_party/lcms/src/lcms2_internal.h
@@ -1,7 +1,7 @@
 
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2016 Marti Maria Saguer
+//  Copyright (c) 1998-2017 Marti Maria Saguer
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the "Software"),
@@ -77,7 +77,7 @@
 // Maximum of channels for internal pipeline evaluation
 #define MAX_STAGE_CHANNELS  128
 
-// Unused parameter warning supression
+// Unused parameter warning suppression
 #define cmsUNUSED_PARAMETER(x) ((void)x)
 
 // The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999).
@@ -96,8 +96,16 @@
 # ifndef vsnprintf
 #       define vsnprintf  _vsnprintf
 # endif
-#endif
 
+/// Properly define some macros to accommodate
+/// older MSVC versions.
+# if _MSC_VER <= 1700
+        #include <float.h>
+        #define isnan _isnan
+        #define isinf(x) (!_finite((x)))
+# endif
+
+#endif
 
 // A fast way to convert from/to 16 <-> 8 bits
 #define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb))
@@ -185,6 +193,34 @@
 #include <windows.h>
 
 
+// The locking scheme in LCMS requires a single 'top level' mutex
+// to work. This is actually implemented on Windows as a
+// CriticalSection, because they are lighter weight. With
+// pthreads, this is statically inited. Unfortunately, windows
+// can't officially statically init critical sections.
+//
+// We can work around this in 2 ways.
+//
+// 1) We can use a proper mutex purely to protect the init
+// of the CriticalSection. This in turns requires us to protect
+// the Mutex creation, which we can do using the snappily
+// named InterlockedCompareExchangePointer API (present on
+// windows XP and above).
+//
+// 2) In cases where we want to work on pre-Windows XP, we
+// can use an even more horrible hack described below.
+//
+// So why wouldn't we always use 2)? Because not calling
+// the init function for a critical section means it fails
+// testing with ApplicationVerifier (and presumably similar
+// tools).
+//
+// We therefore default to 1, and people who want to be able
+// to run on pre-Windows XP boxes can build with:
+//     CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+// defined. This is automatically set for builds using
+// versions of MSVC that don't have this API available.
+//
 // From: http://locklessinc.com/articles/pthreads_on_windows/
 // The pthreads API has an initialization macro that has no correspondence to anything in 
 // the windows API. By investigating the internal definition of the critical section type, 
@@ -206,14 +242,30 @@
 
 typedef CRITICAL_SECTION _cmsMutex;
 
-#define CMS_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG) -1,-1,0,0,0,0}
-
 #ifdef _MSC_VER
 #    if (_MSC_VER >= 1800)
 #          pragma warning(disable : 26135)
 #    endif
 #endif
 
+#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+// If we are building with a version of MSVC smaller
+// than 1400 (i.e. before VS2005) then we don't have
+// the InterlockedCompareExchangePointer API, so use
+// the old version.
+#    ifdef _MSC_VER
+#       if _MSC_VER < 1400
+#          define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+#       endif
+#    endif
+#endif
+
+#ifdef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+#      define CMS_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG) -1,-1,0,0,0,0}
+#else
+#      define CMS_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG)NULL,-1,0,0,0,0}
+#endif
+
 cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
 {
 	EnterCriticalSection(m);
@@ -449,7 +501,7 @@
     void* chunks[MemoryClientMax];    // array of pointers to client chunks. Memory itself is hold in the suballocator. 
                                       // If NULL, then it reverts to global Context0
 
-    _cmsMemPluginChunkType DefaultMemoryManager;  // The allocators used for creating the context itself. Cannot be overriden
+    _cmsMemPluginChunkType DefaultMemoryManager;  // The allocators used for creating the context itself. Cannot be overridden
 };
 
 // Returns a pointer to a valid context structure, including the global one if id is zero. 
@@ -771,8 +823,8 @@
 
 // Interpolation ---------------------------------------------------------------------------------------------------------
 
-cmsInterpParams*     _cmsComputeInterpParams(cmsContext ContextID, int nSamples, int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags);
-cmsInterpParams*     _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags);
+cmsInterpParams*     _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags);
+cmsInterpParams*     _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags);
 void                 _cmsFreeInterpParams(cmsInterpParams* p);
 cmsBool              _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p);
 
@@ -831,13 +883,13 @@
 cmsStage*        _cmsStageAllocLabV2ToV4curves(cmsContext ContextID);
 cmsStage*        _cmsStageAllocLabV4ToV2(cmsContext ContextID);
 cmsStage*        _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS);
-cmsStage*        _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels);
-cmsStage*        _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan);
+cmsStage*        _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels);
+cmsStage*        _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan);
 cmsStage*        _cmsStageNormalizeFromLabFloat(cmsContext ContextID);
 cmsStage*        _cmsStageNormalizeFromXyzFloat(cmsContext ContextID);
 cmsStage*        _cmsStageNormalizeToLabFloat(cmsContext ContextID);
 cmsStage*        _cmsStageNormalizeToXyzFloat(cmsContext ContextID);
-cmsStage*        _cmsStageClipNegatives(cmsContext ContextID, int nChannels);
+cmsStage*        _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels);
 
 
 // For curve set only
@@ -872,9 +924,9 @@
 // Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy
 // of the LUTS, since ownership of original is up to the profile. The user should free allocated resources.
 
-cmsPipeline*      _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent);
-cmsPipeline*      _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent);
-cmsPipeline*      _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent);
+cmsPipeline*      _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
+cmsPipeline*      _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
+cmsPipeline*      _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent);
 
 // Special values
 cmsBool           _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile);
@@ -899,8 +951,8 @@
 
 // LUT optimization ------------------------------------------------------------------------------------------------
 
-cmsUInt16Number  _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples);
-int              _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags);
+cmsUInt16Number  _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples);
+cmsUInt32Number  _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags);
 
 cmsBool          _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
                                       cmsUInt16Number **White,
@@ -909,7 +961,7 @@
 
 cmsBool          _cmsOptimizePipeline(cmsContext ContextID,
                                       cmsPipeline**    Lut,
-                                      int              Intent,
+                                      cmsUInt32Number  Intent,
                                       cmsUInt32Number* InputFormat,
                                       cmsUInt32Number* OutputFormat,
                                       cmsUInt32Number* dwFlags );
diff --git a/third_party/libopenjpeg20/0000-use-colorspace.patch b/third_party/libopenjpeg20/0000-use-colorspace.patch
deleted file mode 100644
index 4d89eff..0000000
--- a/third_party/libopenjpeg20/0000-use-colorspace.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index f3ccb9974..882f1b649 100644
---- a/third_party/libopenjpeg20/jp2.c
-+++ b/third_party/libopenjpeg20/jp2.c
-@@ -1624,7 +1624,7 @@ OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
-             /* Part 1, I.5.3.4: Either both or none : */
-             if (!jp2->color.jp2_pclr->cmap) {
-                 opj_jp2_free_pclr(&(jp2->color));
--            } else {
-+            } else if (p_image->pdfium_use_colorspace) {
-                 if (!opj_jp2_apply_pclr(p_image, &(jp2->color), p_manager)) {
-                     return OPJ_FALSE;
-                 }
-diff --git a/third_party/libopenjpeg20/openjpeg.h b/third_party/libopenjpeg20/openjpeg.h
-index d571c516e..e8dbe4ff7 100644
---- a/third_party/libopenjpeg20/openjpeg.h
-+++ b/third_party/libopenjpeg20/openjpeg.h
-@@ -682,6 +682,10 @@ typedef struct opj_image {
-     OPJ_BYTE *icc_profile_buf;
-     /** size of ICC profile */
-     OPJ_UINT32 icc_profile_len;
-+
-+
-+    /** Whether to apply PCLR or not */
-+    OPJ_BOOL pdfium_use_colorspace;
- } opj_image_t;
- 
- 
diff --git a/third_party/libopenjpeg20/0003-dwt-decode.patch b/third_party/libopenjpeg20/0003-dwt-decode.patch
index 631219a..94d4b41 100644
--- a/third_party/libopenjpeg20/0003-dwt-decode.patch
+++ b/third_party/libopenjpeg20/0003-dwt-decode.patch
@@ -1,17 +1,18 @@
 diff --git a/third_party/libopenjpeg20/dwt.c b/third_party/libopenjpeg20/dwt.c
-index 83c148002..1455ee84a 100644
+index 5930d1c71..6512b1e4c 100644
 --- a/third_party/libopenjpeg20/dwt.c
 +++ b/third_party/libopenjpeg20/dwt.c
-@@ -62,8 +62,6 @@
- 
+@@ -63,9 +63,6 @@
  /** @defgroup DWT DWT - Implementation of a discrete wavelet transform */
  /*@{*/
+ 
 -#define OPJ_WS(i) v->mem[(i)*2]
 -#define OPJ_WD(i) v->mem[(1+(i)*2)]
- 
+-
  #ifdef __AVX2__
  /** Number of int32 values in a AVX2 register */
-@@ -81,6 +79,7 @@
+ #define VREG_INT_COUNT       8
+@@ -82,6 +79,7 @@
  
  typedef struct dwt_local {
      OPJ_INT32* mem;
@@ -19,7 +20,7 @@
      OPJ_INT32 dn;   /* number of elements in high pass band */
      OPJ_INT32 sn;   /* number of elements in low pass band */
      OPJ_INT32 cas;  /* 0 = start on even coord, 1 = start on odd coord */
-@@ -132,13 +131,14 @@ static void opj_dwt_deinterleave_v(OPJ_INT32 *a, OPJ_INT32 *b, OPJ_INT32 dn,
+@@ -133,13 +131,13 @@ static void opj_dwt_deinterleave_v(OPJ_INT32 *a, OPJ_INT32 *b, OPJ_INT32 dn,
  /**
  Forward 5-3 wavelet transform in 1-D
  */
@@ -34,11 +35,10 @@
 -                                  OPJ_INT32 cas);
 +static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_SIZE_T a_count,
 +    OPJ_INT32 dn, OPJ_INT32 sn, OPJ_INT32 cas);
-+
- 
  /**
  Explicit calculation of the Quantization Stepsizes
-@@ -149,14 +149,14 @@ static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
+ */
+@@ -149,14 +147,14 @@ static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
  Inverse wavelet transform in 2-D.
  */
  static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
@@ -56,7 +56,7 @@
  
  static OPJ_UINT32 opj_dwt_max_resolution(opj_tcd_resolution_t* OPJ_RESTRICT r,
          OPJ_UINT32 i);
-@@ -205,13 +205,20 @@ static void opj_v4dwt_decode_step2(opj_v4_t* l, opj_v4_t* w,
+@@ -205,13 +203,20 @@ static void opj_v4dwt_decode_step2(opj_v4_t* l, opj_v4_t* w,
  
  /*@}*/
  
@@ -83,7 +83,7 @@
  
  /* <summary>                                                              */
  /* This table contains the norms of the 5-3 wavelets for different bands. */
-@@ -344,8 +351,8 @@ static void opj_dwt_interleave_v(const opj_dwt_t* v, OPJ_INT32 *a, OPJ_INT32 x)
+@@ -344,8 +349,8 @@ static void opj_dwt_interleave_v(const opj_dwt_t* v, OPJ_INT32 *a, OPJ_INT32 x)
  /* <summary>                            */
  /* Forward 5-3 wavelet transform in 1-D. */
  /* </summary>                           */
@@ -94,7 +94,7 @@
  {
      OPJ_INT32 i;
  
-@@ -376,8 +383,8 @@ static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
+@@ -376,8 +381,8 @@ static void opj_dwt_encode_1(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
  /* <summary>                            */
  /* Inverse 5-3 wavelet transform in 1-D. */
  /* </summary>                           */
@@ -105,7 +105,7 @@
  {
      OPJ_INT32 i;
  
-@@ -406,7 +413,7 @@ static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
+@@ -406,7 +411,7 @@ static void opj_dwt_decode_1_(OPJ_INT32 *a, OPJ_INT32 dn, OPJ_INT32 sn,
  
  static void opj_dwt_decode_1(const opj_dwt_t *v)
  {
@@ -114,7 +114,7 @@
  }
  
  #endif /* STANDARD_SLOW_VERSION */
-@@ -1037,8 +1044,8 @@ static void opj_idwt53_v(const opj_dwt_t *dwt,
+@@ -1037,8 +1042,8 @@ static void opj_idwt53_v(const opj_dwt_t *dwt,
  /* <summary>                             */
  /* Forward 9-7 wavelet transform in 1-D. */
  /* </summary>                            */
@@ -125,7 +125,7 @@
  {
      OPJ_INT32 i;
      if (!cas) {
-@@ -1106,8 +1113,8 @@ static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
+@@ -1106,8 +1111,8 @@ static void opj_dwt_encode_stepsize(OPJ_INT32 stepsize, OPJ_INT32 numbps,
  /* <summary>                            */
  /* Forward 5-3 wavelet transform in 2-D. */
  /* </summary>                           */
@@ -136,7 +136,7 @@
  {
      OPJ_INT32 i, j, k;
      OPJ_INT32 *a = 00;
-@@ -1117,6 +1124,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
+@@ -1117,6 +1122,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
  
      OPJ_INT32 rw;           /* width of the resolution level computed   */
      OPJ_INT32 rh;           /* height of the resolution level computed  */
@@ -144,7 +144,7 @@
      OPJ_SIZE_T l_data_size;
  
      opj_tcd_resolution_t * l_cur_res = 0;
-@@ -1129,13 +1137,13 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
+@@ -1129,13 +1135,13 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
      l_cur_res = tilec->resolutions + l;
      l_last_res = l_cur_res - 1;
  
@@ -161,7 +161,7 @@
      bj = (OPJ_INT32*)opj_malloc(l_data_size);
      /* l_data_size is equal to 0 when numresolutions == 1 but bj is not used */
      /* in that case, so do not error out */
-@@ -1167,7 +1175,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
+@@ -1167,7 +1173,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
                  bj[k] = aj[k * w];
              }
  
@@ -170,7 +170,7 @@
  
              opj_dwt_deinterleave_v(bj, aj, dn, sn, w, cas_col);
          }
-@@ -1180,7 +1188,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
+@@ -1180,7 +1186,7 @@ static INLINE OPJ_BOOL opj_dwt_encode_procedure(opj_tcd_tilecomp_t * tilec,
              for (k = 0; k < rw; k++) {
                  bj[k] = aj[k];
              }
@@ -179,7 +179,7 @@
              opj_dwt_deinterleave_h(bj, aj, dn, sn, cas_row);
          }
  
-@@ -1379,7 +1387,7 @@ static void opj_dwt_decode_v_func(void* user_data, opj_tls_t* tls)
+@@ -1379,7 +1385,7 @@ static void opj_dwt_decode_v_func(void* user_data, opj_tls_t* tls)
  /* Inverse wavelet transform in 2-D.    */
  /* </summary>                           */
  static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
@@ -188,7 +188,7 @@
  {
      opj_dwt_t h;
      opj_dwt_t v;
-@@ -1401,22 +1409,23 @@ static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
+@@ -1401,22 +1407,23 @@ static OPJ_BOOL opj_dwt_decode_tile(opj_thread_pool_t* tp,
          return OPJ_TRUE;
      }
      num_threads = opj_thread_pool_get_thread_count(tp);
@@ -215,7 +215,7 @@
      v.mem = h.mem;
  
      while (--numres) {
-@@ -1594,7 +1603,8 @@ static void opj_dwt_interleave_partial_v(OPJ_INT32 *dest,
+@@ -1594,7 +1601,8 @@ static void opj_dwt_interleave_partial_v(OPJ_INT32 *dest,
      OPJ_UNUSED(ret);
  }
  
@@ -225,7 +225,7 @@
                                       OPJ_INT32 cas,
                                       OPJ_INT32 win_l_x0,
                                       OPJ_INT32 win_l_x1,
-@@ -1974,16 +1984,16 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
+@@ -1974,16 +1982,16 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
          opj_sparse_array_int32_free(sa);
          return OPJ_TRUE;
      }
@@ -245,7 +245,7 @@
      h.mem = (OPJ_INT32*)opj_aligned_32_malloc(h_mem_size);
      if (! h.mem) {
          /* FIXME event manager error callback */
-@@ -1991,6 +2001,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
+@@ -1991,6 +1999,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
          return OPJ_FALSE;
      }
  
@@ -253,7 +253,7 @@
      v.mem = h.mem;
  
      for (resno = 1; resno < numres; resno ++) {
-@@ -2101,7 +2112,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
+@@ -2101,7 +2110,7 @@ static OPJ_BOOL opj_dwt_decode_partial_tile(
                                               win_ll_x1,
                                               win_hl_x0,
                                               win_hl_x1);
diff --git a/third_party/libopenjpeg20/0005-jp2_apply_pclr.patch b/third_party/libopenjpeg20/0005-jp2_apply_pclr.patch
index c8caea8..2d45017 100644
--- a/third_party/libopenjpeg20/0005-jp2_apply_pclr.patch
+++ b/third_party/libopenjpeg20/0005-jp2_apply_pclr.patch
@@ -1,5 +1,5 @@
 diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index da4e05f82..c9562f705 100644
+index 8dc1ecbe6..61b3f5821 100644
 --- a/third_party/libopenjpeg20/jp2.c
 +++ b/third_party/libopenjpeg20/jp2.c
 @@ -1073,8 +1073,8 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
@@ -13,7 +13,7 @@
          }
  
          /* Palette mapping: */
-@@ -1102,19 +1102,19 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+@@ -1102,7 +1102,7 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
          pcol = cmap[i].pcol;
          src = old_comps[cmp].data;
          assert(src); /* verified above */
@@ -22,11 +22,7 @@
  
          /* Direct use: */
          if (cmap[i].mtyp == 0) {
--            assert(cmp == 0);
-+            assert( cmp == 0 ); // probably wrong.
-             dst = new_comps[i].data;
-             assert(dst);
-             for (j = 0; j < max; ++j) {
+@@ -1112,8 +1112,8 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
                  dst[j] = src[j];
              }
          } else {
diff --git a/third_party/libopenjpeg20/0019-tcd_init_tile.patch b/third_party/libopenjpeg20/0019-tcd_init_tile.patch
index fc5a3aa..8746eac 100644
--- a/third_party/libopenjpeg20/0019-tcd_init_tile.patch
+++ b/third_party/libopenjpeg20/0019-tcd_init_tile.patch
@@ -1,21 +1,14 @@
 diff --git a/third_party/libopenjpeg20/tcd.c b/third_party/libopenjpeg20/tcd.c
-index f0710cd14..35d15e3d1 100644
+index be3b84363..5757fd401 100644
 --- a/third_party/libopenjpeg20/tcd.c
 +++ b/third_party/libopenjpeg20/tcd.c
-@@ -1040,9 +1040,15 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
-                                                           cblkwidthexpn);
+@@ -1065,6 +1065,9 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
                      l_current_precinct->ch = (OPJ_UINT32)((brcblkyend - tlcblkystart) >>
                                                            cblkheightexpn);
--
+ 
 +                    if (l_current_precinct->cw && ((OPJ_UINT32)-1) / l_current_precinct->cw < l_current_precinct->ch) {
 +                        return OPJ_FALSE;
 +                    }
                      l_nb_code_blocks = l_current_precinct->cw * l_current_precinct->ch;
                      /*fprintf(stderr, "\t\t\t\t precinct_cw = %d x recinct_ch = %d\n",l_current_precinct->cw, l_current_precinct->ch);      */
-+
-+                    if (((OPJ_UINT32)-1) / (OPJ_UINT32)sizeof_block < l_nb_code_blocks) {
-+                        return OPJ_FALSE;
-+                    }
-                     l_nb_code_blocks_size = l_nb_code_blocks * (OPJ_UINT32)sizeof_block;
- 
-                     if (!l_current_precinct->cblks.blocks && (l_nb_code_blocks > 0U)) {
+                     if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof_block) <
diff --git a/third_party/libopenjpeg20/0022-jp2_apply_pclr_overflow.patch b/third_party/libopenjpeg20/0022-jp2_apply_pclr_overflow.patch
index f4f2ef5..c1773d4 100644
--- a/third_party/libopenjpeg20/0022-jp2_apply_pclr_overflow.patch
+++ b/third_party/libopenjpeg20/0022-jp2_apply_pclr_overflow.patch
@@ -1,8 +1,8 @@
 diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
-index 1fa607d66..78a2d22ff 100644
+index 8dc2b977f..3e23bc363 100644
 --- a/third_party/libopenjpeg20/jp2.c
 +++ b/third_party/libopenjpeg20/jp2.c
-@@ -1049,6 +1049,14 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+@@ -1058,6 +1058,14 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
      }
  
      old_comps = image->comps;
@@ -17,7 +17,7 @@
      new_comps = (opj_image_comp_t*)
                  opj_malloc(nr_channels * sizeof(opj_image_comp_t));
      if (!new_comps) {
-@@ -1093,21 +1101,27 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
+@@ -1102,20 +1110,26 @@ static OPJ_BOOL opj_jp2_apply_pclr(opj_image_t *image,
          cmp = cmap[i].cmp;
          pcol = cmap[i].pcol;
          src = old_comps[cmp].data;
@@ -37,7 +37,6 @@
 +
          /* Direct use: */
          if (cmap[i].mtyp == 0) {
-             assert( cmp == 0 ); // probably wrong.
 -            dst = new_comps[i].data;
 -            assert(dst);
              for (j = 0; j < max; ++j) {
diff --git a/third_party/libopenjpeg20/0035-opj_j2k_update_image_dimensions.patch b/third_party/libopenjpeg20/0036-opj_j2k_update_image_dimensions.patch
similarity index 100%
rename from third_party/libopenjpeg20/0035-opj_j2k_update_image_dimensions.patch
rename to third_party/libopenjpeg20/0036-opj_j2k_update_image_dimensions.patch
diff --git a/third_party/libopenjpeg20/0037-tcd_init_tile.patch b/third_party/libopenjpeg20/0037-tcd_init_tile.patch
new file mode 100644
index 0000000..e38a7ec
--- /dev/null
+++ b/third_party/libopenjpeg20/0037-tcd_init_tile.patch
@@ -0,0 +1,31 @@
+diff --git a/third_party/libopenjpeg20/tcd.c b/third_party/libopenjpeg20/tcd.c
+index 2ae211ef4..9e98f04ab 100644
+--- a/third_party/libopenjpeg20/tcd.c
++++ b/third_party/libopenjpeg20/tcd.c
+@@ -910,8 +910,24 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
+             /* p. 64, B.6, ISO/IEC FDIS15444-1 : 2000 (18 august 2000)  */
+             l_tl_prc_x_start = opj_int_floordivpow2(l_res->x0, (OPJ_INT32)l_pdx) << l_pdx;
+             l_tl_prc_y_start = opj_int_floordivpow2(l_res->y0, (OPJ_INT32)l_pdy) << l_pdy;
+-            l_br_prc_x_end = opj_int_ceildivpow2(l_res->x1, (OPJ_INT32)l_pdx) << l_pdx;
+-            l_br_prc_y_end = opj_int_ceildivpow2(l_res->y1, (OPJ_INT32)l_pdy) << l_pdy;
++            {
++                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->x1,
++                                  (OPJ_INT32)l_pdx)) << l_pdx;
++                if (tmp > (OPJ_UINT32)INT_MAX) {
++                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
++                    return OPJ_FALSE;
++                }
++                l_br_prc_x_end = (OPJ_INT32)tmp;
++            }
++            {
++                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->y1,
++                                  (OPJ_INT32)l_pdy)) << l_pdy;
++                if (tmp > (OPJ_UINT32)INT_MAX) {
++                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
++                    return OPJ_FALSE;
++                }
++                l_br_prc_y_end = (OPJ_INT32)tmp;
++            }
+             /*fprintf(stderr, "\t\t\tprc_x_start=%d, prc_y_start=%d, br_prc_x_end=%d, br_prc_y_end=%d \n", l_tl_prc_x_start, l_tl_prc_y_start, l_br_prc_x_end ,l_br_prc_y_end );*/
+ 
+             l_res->pw = (l_res->x0 == l_res->x1) ? 0U : (OPJ_UINT32)((
diff --git a/third_party/libopenjpeg20/CMakeLists.txt b/third_party/libopenjpeg20/CMakeLists.txt
deleted file mode 100644
index 800dc9a..0000000
--- a/third_party/libopenjpeg20/CMakeLists.txt
+++ /dev/null
@@ -1,111 +0,0 @@
-include_regular_expression("^.*$")
-
-#
-install( FILES  ${CMAKE_CURRENT_BINARY_DIR}/opj_config.h
- DESTINATION ${OPENJPEG_INSTALL_INCLUDE_DIR} COMPONENT Headers)
-
-include_directories(
-  ${OPENJPEG_BINARY_DIR}/src/lib/openjp2 # opj_config.h and opj_config_private.h
-)
-# Defines the source code for the library
-set(OPENJPEG_SRCS
-  ${CMAKE_CURRENT_SOURCE_DIR}/bio.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/cio.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/dwt.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/event.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/function_list.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/image.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/invert.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/j2k.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/jp2.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/mct.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/mqc.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/openjpeg.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/opj_clock.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/pi.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/raw.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/sparse_array.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/t1.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/t2.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/tcd.c
-  ${CMAKE_CURRENT_SOURCE_DIR}/tgt.c
-)
-if(BUILD_JPIP)
-  add_definitions(-DUSE_JPIP)
-  set(OPENJPEG_SRCS
-    ${OPENJPEG_SRCS}
-    ${CMAKE_CURRENT_SOURCE_DIR}/cidx_manager.c
-    ${CMAKE_CURRENT_SOURCE_DIR}/phix_manager.c
-    ${CMAKE_CURRENT_SOURCE_DIR}/ppix_manager.c
-    ${CMAKE_CURRENT_SOURCE_DIR}/thix_manager.c
-    ${CMAKE_CURRENT_SOURCE_DIR}/tpix_manager.c
-  )
-endif()
-
-# Build the library
-if(WIN32)
-  if(BUILD_SHARED_LIBS)
-    add_definitions(-DOPJ_EXPORTS)
-  else()
-    add_definitions(-DOPJ_STATIC)
-  endif()
-endif()
-add_library(${OPENJPEG_LIBRARY_NAME} ${OPENJPEG_SRCS})
-if(UNIX)
-  target_link_libraries(${OPENJPEG_LIBRARY_NAME} m)
-endif()
-set_target_properties(${OPENJPEG_LIBRARY_NAME} PROPERTIES ${OPENJPEG_LIBRARY_PROPERTIES})
-
-# Install library
-install(TARGETS ${OPENJPEG_LIBRARY_NAME}
-  EXPORT OpenJPEGTargets
-  RUNTIME DESTINATION ${OPENJPEG_INSTALL_BIN_DIR} COMPONENT Applications
-  LIBRARY DESTINATION ${OPENJPEG_INSTALL_LIB_DIR} COMPONENT Libraries
-  ARCHIVE DESTINATION ${OPENJPEG_INSTALL_LIB_DIR} COMPONENT Libraries
-)
-
-# Install includes files
-install(FILES openjpeg.h opj_stdint.h
-  DESTINATION ${OPENJPEG_INSTALL_INCLUDE_DIR} COMPONENT Headers
-)
-
-if(BUILD_DOC)
-# install man page of the library
-install(
-  FILES       ${OPENJPEG_SOURCE_DIR}/doc/man/man3/libopenjp2.3
-  DESTINATION ${OPENJPEG_INSTALL_MAN_DIR}/man3)
-endif()
-
-# internal utilities to generate t1_luts.h (part of the jp2 lib)
-# no need to install:
-add_executable(t1_generate_luts t1_generate_luts.c)
-if(UNIX)
-  target_link_libraries(t1_generate_luts m)
-endif()
-
-# Experimental option; let's how cppcheck performs
-# Implementation details:
-# I could not figure out how to easily upload a file to CDash. Instead simply
-# pretend cppcheck is part of the Build step. Technically cppcheck can even
-# output gcc formatted error/warning report
-# Another implementation detail: I could not redirect error to the error
-# catching mechanism something is busted in cmake 2.8.5, I had to use the
-# warning regex to catch them.
-if(OPENJPEG_CPPCHECK)
-  find_package(CPPCHECK REQUIRED)
-  foreach(f ${OPENJPEG_SRCS})
-    # cppcheck complains about too many configuration, pretend to be WIN32:
-    add_custom_command(TARGET ${OPENJPEG_LIBRARY_NAME}
-      COMMAND ${CPPCHECK_EXECUTABLE} -DWIN32 ${f})
-  endforeach()
-endif()
-
-if(OPJ_USE_DSYMUTIL)
-  if(BUILD_SHARED_LIBS)
-    GET_TARGET_PROPERTY(OPENJPEG_LIBRARY_LOCATION ${OPENJPEG_LIBRARY_NAME} LOCATION)
-    add_custom_command(TARGET ${OPENJPEG_LIBRARY_NAME} POST_BUILD
-    COMMAND "dsymutil" "${OPENJPEG_LIBRARY_LOCATION}"
-    COMMENT "dsymutil ${OPENJPEG_LIBRARY_LOCATION}"
-    DEPENDS ${OPENJPEG_LIBRARY_NAME})
-  endif()
-endif()
diff --git a/third_party/libopenjpeg20/README.pdfium b/third_party/libopenjpeg20/README.pdfium
index 1805000..2a13a61 100644
--- a/third_party/libopenjpeg20/README.pdfium
+++ b/third_party/libopenjpeg20/README.pdfium
@@ -1,6 +1,6 @@
 Name: OpenJPEG
 URL: http://www.openjpeg.org/
-Version: 2.3.0 (also update in opj_config*)
+Version: 2.3.1 (also update in opj_config*)
 Security Critical: yes
 License: 2-clause BSD
 
@@ -9,7 +9,6 @@
 
 Local Modifications:
 
-0000-use-colorspace.patch: Makes it possible to not call opj_jp2_apply_pclr().
 0003-dwt-decode.patch: Check array bounds for opj_dwt_decode_1() and friends.
 0005-jp2_apply_pclr.patch: Fix out of bounds access.
 0006-tcd_init_tile.patch: Fix a divide by zero bug in opj_tcd_init_tile().
@@ -27,4 +26,6 @@
 0026-use_opj_uint_ceildiv.patch: Remove (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)a, (OPJ_INT32) b).
 0033-undefined-shift-opj_t1_dec_clnpass.patch: fix undefined shifts originated from opj_t1_decode_cblk.
 0034-opj_malloc.patch: PDFium changes in opj_malloc.
-0035-opj_j2k_update_image_dimensions.patch: fix integer overflow.
+0035-opj_image_data_free.patch: Use the right free function in opj_jp2_apply_pclr.
+0036-opj_j2k_update_image_dimensions.patch: fix integer overflow.
+0037-tcd_init_tile.patch: Avoid integer overflow in opj_tcd_init_tile().
diff --git a/third_party/libopenjpeg20/dwt.c b/third_party/libopenjpeg20/dwt.c
index 1455ee8..6512b1e 100644
--- a/third_party/libopenjpeg20/dwt.c
+++ b/third_party/libopenjpeg20/dwt.c
@@ -138,8 +138,6 @@
 */
 static void opj_dwt_encode_1_real(OPJ_INT32 *a, OPJ_SIZE_T a_count,
     OPJ_INT32 dn, OPJ_INT32 sn, OPJ_INT32 cas);
-
-
 /**
 Explicit calculation of the Quantization Stepsizes
 */
@@ -981,7 +979,7 @@
 #if (defined(__SSE2__) || defined(__AVX2__))
         if (len > 1 && nb_cols == PARALLEL_COLS_53) {
             /* Same as below general case, except that thanks to SSE2/AVX2 */
-            /* we can efficently process 8/16 columns in parallel */
+            /* we can efficiently process 8/16 columns in parallel */
             opj_idwt53_v_cas0_mcols_SSE2_OR_AVX2(dwt->mem, sn, len, tiledp_col, stride);
             return;
         }
@@ -1024,7 +1022,7 @@
 #if (defined(__SSE2__) || defined(__AVX2__))
         if (len > 2 && nb_cols == PARALLEL_COLS_53) {
             /* Same as below general case, except that thanks to SSE2/AVX2 */
-            /* we can efficently process 8/16 columns in parallel */
+            /* we can efficiently process 8/16 columns in parallel */
             opj_idwt53_v_cas1_mcols_SSE2_OR_AVX2(dwt->mem, sn, len, tiledp_col, stride);
             return;
         }
@@ -2052,7 +2050,7 @@
         tr_hl_x0 = (OPJ_UINT32)tr->bands[0].x0;
         tr_lh_y0 = (OPJ_UINT32)tr->bands[1].y0;
 
-        /* Substract the origin of the bands for this tile, to the subwindow */
+        /* Subtract the origin of the bands for this tile, to the subwindow */
         /* of interest band coordinates, so as to get them relative to the */
         /* tile */
         win_ll_x0 = opj_uint_subs(win_ll_x0, tr_ll_x0);
@@ -2707,17 +2705,20 @@
     /* overflow check */
     if (l_data_size > (SIZE_MAX - 5U)) {
         /* FIXME event manager error callback */
+        opj_sparse_array_int32_free(sa);
         return OPJ_FALSE;
     }
     l_data_size += 5U;
     /* overflow check */
     if (l_data_size > (SIZE_MAX / sizeof(opj_v4_t))) {
         /* FIXME event manager error callback */
+        opj_sparse_array_int32_free(sa);
         return OPJ_FALSE;
     }
     h.wavelet = (opj_v4_t*) opj_aligned_malloc(l_data_size * sizeof(opj_v4_t));
     if (!h.wavelet) {
         /* FIXME event manager error callback */
+        opj_sparse_array_int32_free(sa);
         return OPJ_FALSE;
     }
     v.wavelet = h.wavelet;
@@ -2770,7 +2771,7 @@
         tr_hl_x0 = (OPJ_UINT32)tr->bands[0].x0;
         tr_lh_y0 = (OPJ_UINT32)tr->bands[1].y0;
 
-        /* Substract the origin of the bands for this tile, to the subwindow */
+        /* Subtract the origin of the bands for this tile, to the subwindow */
         /* of interest band coordinates, so as to get them relative to the */
         /* tile */
         win_ll_x0 = opj_uint_subs(win_ll_x0, tr_ll_x0);
diff --git a/third_party/libopenjpeg20/image.c b/third_party/libopenjpeg20/image.c
index 13bcb8e..fe37390 100644
--- a/third_party/libopenjpeg20/image.c
+++ b/third_party/libopenjpeg20/image.c
@@ -48,8 +48,8 @@
         image->color_space = clrspc;
         image->numcomps = numcmpts;
         /* allocate memory for the per-component information */
-        image->comps = (opj_image_comp_t*)opj_calloc(1,
-                       image->numcomps * sizeof(opj_image_comp_t));
+        image->comps = (opj_image_comp_t*)opj_calloc(image->numcomps,
+                       sizeof(opj_image_comp_t));
         if (!image->comps) {
             /* TODO replace with event manager, breaks API */
             /* fprintf(stderr,"Unable to allocate memory for image.\n"); */
diff --git a/third_party/libopenjpeg20/indexbox_manager.h b/third_party/libopenjpeg20/indexbox_manager.h
deleted file mode 100644
index 238114b..0000000
--- a/third_party/libopenjpeg20/indexbox_manager.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * $Id: indexbox_manager.h 897 2011-08-28 21:43:57Z Kaori.Hagihara@gmail.com $
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2003-2004, Yannick Verschueren
- * Copyright (c) 2010-2011, Kaori Hagihara
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*! \file
- *  \brief Modification of jpip.c from 2KAN indexer
- */
-
-#ifndef  INDEXBOX_MANAGER_H_
-# define INDEXBOX_MANAGER_H_
-
-#include "openjpeg.h"
-#include "j2k.h" /* needed to use jp2.h */
-#include "jp2.h"
-
-#define JPIP_CIDX 0x63696478   /* Codestream index                */
-#define JPIP_CPTR 0x63707472   /* Codestream Finder Box           */
-#define JPIP_MANF 0x6d616e66   /* Manifest Box                    */
-#define JPIP_FAIX 0x66616978   /* Fragment array Index box        */
-#define JPIP_MHIX 0x6d686978   /* Main Header Index Table         */
-#define JPIP_TPIX 0x74706978   /* Tile-part Index Table box       */
-#define JPIP_THIX 0x74686978   /* Tile header Index Table box     */
-#define JPIP_PPIX 0x70706978   /* Precinct Packet Index Table box */
-#define JPIP_PHIX 0x70686978   /* Packet Header index Table       */
-#define JPIP_FIDX 0x66696478   /* File Index                      */
-#define JPIP_FPTR 0x66707472   /* File Finder                     */
-#define JPIP_PRXY 0x70727879   /* Proxy boxes                     */
-#define JPIP_IPTR 0x69707472   /* Index finder box                */
-#define JPIP_PHLD 0x70686c64   /* Place holder                    */
-
-
-/*
- * Write tile-part Index table box (superbox)
- *
- * @param[in] coff      offset of j2k codestream
- * @param[in] cstr_info codestream information
- * @param[in] j2klen    length of j2k codestream
- * @param[in] cio       file output handle
- * @return              length of tpix box
- */
-int opj_write_tpix(int coff, opj_codestream_info_t cstr_info, int j2klen,
-                   opj_stream_private_t *cio,
-                   opj_event_mgr_t * p_manager);
-
-
-/*
- * Write tile header index table box (superbox)
- *
- * @param[in] coff      offset of j2k codestream
- * @param[in] cstr_info codestream information pointer
- * @param[in] cio       file output handle
- * @return              length of thix box
- */
-int opj_write_thix(int coff, opj_codestream_info_t cstr_info,
-                   opj_stream_private_t *cio, opj_event_mgr_t * p_manager);
-
-
-/*
- * Write precinct packet index table box (superbox)
- *
- * @param[in] coff      offset of j2k codestream
- * @param[in] cstr_info codestream information
- * @param[in] EPHused   true if EPH option used
- * @param[in] j2klen    length of j2k codestream
- * @param[in] cio       file output handle
- * @return              length of ppix box
- */
-int opj_write_ppix(int coff, opj_codestream_info_t cstr_info, OPJ_BOOL EPHused,
-                   int j2klen, opj_stream_private_t *cio,
-                   opj_event_mgr_t * p_manager);
-
-
-/*
- * Write packet header index table box (superbox)
- *
- * @param[in] coff      offset of j2k codestream
- * @param[in] cstr_info codestream information
- * @param[in] EPHused   true if EPH option used
- * @param[in] j2klen    length of j2k codestream
- * @param[in] cio       file output handle
- * @return              length of ppix box
- */
-int opj_write_phix(int coff, opj_codestream_info_t cstr_info, OPJ_BOOL EPHused,
-                   int j2klen, opj_stream_private_t *cio,
-                   opj_event_mgr_t * p_manager);
-
-/*
- * Write manifest box (box)
- *
- * @param[in] second number to be visited
- * @param[in] v      number of boxes
- * @param[in] box    box to be manifested
- * @param[in] cio    file output handle
- */
-
-void opj_write_manf(int second,
-                    int v,
-                    opj_jp2_box_t *box,
-                    opj_stream_private_t *cio,
-                    opj_event_mgr_t * p_manager);
-
-/*
- * Write main header index table (box)
- *
- * @param[in] coff offset of j2k codestream
- * @param[in] cstr_info codestream information
- * @param[in] cio  file output handle
- * @return         length of mainmhix box
- */
-int opj_write_mainmhix(int coff, opj_codestream_info_t cstr_info,
-                       opj_stream_private_t *cio,
-                       opj_event_mgr_t * p_manager);
-
-int opj_write_phixfaix(int coff, int compno, opj_codestream_info_t cstr_info,
-                       OPJ_BOOL EPHused, int j2klen, opj_stream_private_t *cio,
-                       opj_event_mgr_t * p_manager);
-
-int opj_write_ppixfaix(int coff, int compno, opj_codestream_info_t cstr_info,
-                       OPJ_BOOL EPHused, int j2klen, opj_stream_private_t *cio,
-                       opj_event_mgr_t * p_manager);
-
-int opj_write_tilemhix(int coff, opj_codestream_info_t cstr_info, int tileno,
-                       opj_stream_private_t *cio,
-                       opj_event_mgr_t * p_manager);
-
-int opj_write_tpixfaix(int coff, int compno, opj_codestream_info_t cstr_info,
-                       int j2klen, opj_stream_private_t *cio,
-                       opj_event_mgr_t * p_manager);
-
-#endif      /* !INDEXBOX_MANAGER_H_ */
diff --git a/third_party/libopenjpeg20/j2k.c b/third_party/libopenjpeg20/j2k.c
index cea6147..690b533 100644
--- a/third_party/libopenjpeg20/j2k.c
+++ b/third_party/libopenjpeg20/j2k.c
@@ -508,7 +508,7 @@
                                   opj_event_mgr_t * p_manager);
 
 /**
- * Reads a COD marker (Coding Styke defaults)
+ * Reads a COD marker (Coding style defaults)
  * @param       p_header_data   the data contained in the COD box.
  * @param       p_j2k                   the jpeg2000 codec.
  * @param       p_header_size   the size of the data contained in the COD marker.
@@ -1925,7 +1925,8 @@
     /* FIXME move it in a index structure included in p_j2k*/
     p_j2k->cstr_index->main_head_start = opj_stream_tell(p_stream) - 2;
 
-    opj_event_msg(p_manager, EVT_INFO, "Start to read j2k main header (%d).\n",
+    opj_event_msg(p_manager, EVT_INFO,
+                  "Start to read j2k main header (%" PRId64 ").\n",
                   p_j2k->cstr_index->main_head_start);
 
     /* Add the marker to the codestream index*/
@@ -2621,7 +2622,7 @@
 }
 
 /**
- * Reads a COD marker (Coding Styke defaults)
+ * Reads a COD marker (Coding style defaults)
  * @param       p_header_data   the data contained in the COD box.
  * @param       p_j2k                   the jpeg2000 codec.
  * @param       p_header_size   the size of the data contained in the COD marker.
@@ -2653,12 +2654,17 @@
             &l_cp->tcps[p_j2k->m_current_tile_number] :
             p_j2k->m_specific_param.m_decoder.m_default_tcp;
 
+#if 0
+    /* This check was added per https://github.com/uclouvain/openjpeg/commit/daed8cc9195555e101ab708a501af2dfe6d5e001 */
+    /* but this is no longer necessary to handle issue476.jp2 */
+    /* and this actually cause issues on legit files. See https://github.com/uclouvain/openjpeg/issues/1043 */
     /* Only one COD per tile */
     if (l_tcp->cod) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "COD marker already read. No more than one COD marker per tile.\n");
         return OPJ_FALSE;
     }
+#endif
     l_tcp->cod = 1;
 
     /* Make sure room is sufficient */
@@ -4085,7 +4091,12 @@
     /* preconditions */
     assert(p_tcp != 00);
     assert(p_manager != 00);
-    assert(p_tcp->ppt_buffer == NULL);
+
+    if (p_tcp->ppt_buffer != NULL) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "opj_j2k_merge_ppt() has already been called\n");
+        return OPJ_FALSE;
+    }
 
     if (p_tcp->ppt == 0U) {
         return OPJ_TRUE;
@@ -6419,7 +6430,9 @@
 
 OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads)
 {
-    if (opj_has_thread_support()) {
+    /* Currently we pass the thread-pool to the tcd, so we cannot re-set it */
+    /* afterwards */
+    if (opj_has_thread_support() && j2k->m_tcd == NULL) {
         opj_thread_pool_destroy(j2k->m_tp);
         j2k->m_tp = NULL;
         if (num_threads <= (OPJ_UINT32)INT_MAX) {
@@ -8836,7 +8849,10 @@
 
     /* Current marker is the EOC marker ?*/
     if (l_current_marker == J2K_MS_EOC) {
-        p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
+        if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_EOC) {
+            p_j2k->m_current_tile_number = 0;
+            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
+        }
     }
 
     /* FIXME DOC ???*/
diff --git a/third_party/libopenjpeg20/jp2.c b/third_party/libopenjpeg20/jp2.c
index 2374d45..1f61a23 100644
--- a/third_party/libopenjpeg20/jp2.c
+++ b/third_party/libopenjpeg20/jp2.c
@@ -1006,7 +1006,7 @@
                 if (!pcol_usage[i]) {
                     is_sane = 0U;
                     opj_event_msg(p_manager, EVT_WARNING,
-                                  "Component mapping seems wrong. Trying to correct.\n", i);
+                                  "Component mapping seems wrong. Trying to correct.\n");
                     break;
                 }
             }
@@ -1088,7 +1088,7 @@
 
         /* Palette mapping: */
         new_comps[i].data = (OPJ_INT32*)
-                            opj_image_data_alloc(old_comps[cmp].w * old_comps[cmp].h * sizeof(OPJ_INT32));
+                            opj_image_data_alloc(sizeof(OPJ_INT32) * old_comps[cmp].w * old_comps[cmp].h);
         if (!new_comps[i].data) {
             while (i > 0) {
                 -- i;
@@ -1125,7 +1125,6 @@
 
         /* Direct use: */
         if (cmap[i].mtyp == 0) {
-            assert( cmp == 0 ); // probably wrong.
             for (j = 0; j < max; ++j) {
                 dst[j] = src[j];
             }
@@ -1209,8 +1208,8 @@
         return OPJ_FALSE;
     }
 
-    entries = (OPJ_UINT32*) opj_malloc((size_t)nr_channels * nr_entries * sizeof(
-                                           OPJ_UINT32));
+    entries = (OPJ_UINT32*) opj_malloc(sizeof(OPJ_UINT32) * nr_channels *
+                                       nr_entries);
     if (!entries) {
         return OPJ_FALSE;
     }
@@ -1651,7 +1650,7 @@
             /* Part 1, I.5.3.4: Either both or none : */
             if (!jp2->color.jp2_pclr->cmap) {
                 opj_jp2_free_pclr(&(jp2->color));
-            } else if (p_image->pdfium_use_colorspace) {
+            } else {
                 if (!opj_jp2_apply_pclr(p_image, &(jp2->color), p_manager)) {
                     return OPJ_FALSE;
                 }
diff --git a/third_party/libopenjpeg20/openjpeg.h b/third_party/libopenjpeg20/openjpeg.h
index c119e8f..53a0e10 100644
--- a/third_party/libopenjpeg20/openjpeg.h
+++ b/third_party/libopenjpeg20/openjpeg.h
@@ -548,7 +548,7 @@
     /** Verbose mode */
     OPJ_BOOL m_verbose;
 
-    /** tile number ot the decoded tile*/
+    /** tile number of the decoded tile */
     OPJ_UINT32 tile_index;
     /** Nb of tile to decode */
     OPJ_UINT32 nb_tile_to_decode;
@@ -685,10 +685,6 @@
     OPJ_BYTE *icc_profile_buf;
     /** size of ICC profile */
     OPJ_UINT32 icc_profile_len;
-
-
-    /** Whether to apply PCLR or not */
-    OPJ_BOOL pdfium_use_colorspace;
 } opj_image_t;
 
 
@@ -1182,7 +1178,8 @@
         opj_stream_skip_fn p_function);
 
 /**
- * Sets the given function to be used as a seek function, the stream is then seekable.
+ * Sets the given function to be used as a seek function, the stream is then seekable,
+ * using SEEK_SET behavior.
  * @param       p_stream    the stream to modify
  * @param       p_function  the function to use a skip function.
 */
@@ -1317,6 +1314,9 @@
  * number, or "ALL_CPUS". If OPJ_NUM_THREADS is set and this function is called,
  * this function will override the behaviour of the environment variable.
  *
+ * Currently this function must be called after opj_setup_decoder() and
+ * before opj_read_header().
+ *
  * Note: currently only has effect on the decompressor.
  *
  * @param p_codec       decompressor handler
@@ -1385,7 +1385,7 @@
  * performance improvements when reading an image by chunks.
  *
  * @param   p_codec         the jpeg2000 codec.
- * @param   p_image         the decoded image previously setted by opj_read_header
+ * @param   p_image         the decoded image previously set by opj_read_header
  * @param   p_start_x       the left position of the rectangle to decode (in image coordinates).
  * @param   p_end_x         the right position of the rectangle to decode (in image coordinates).
  * @param   p_start_y       the up position of the rectangle to decode (in image coordinates).
diff --git a/third_party/libopenjpeg20/opj_config.h b/third_party/libopenjpeg20/opj_config.h
index 7384541..fda1f64 100644
--- a/third_party/libopenjpeg20/opj_config.h
+++ b/third_party/libopenjpeg20/opj_config.h
@@ -13,4 +13,4 @@
 /* Version number. */
 #define OPJ_VERSION_MAJOR 2
 #define OPJ_VERSION_MINOR 3
-#define OPJ_VERSION_BUILD 0
+#define OPJ_VERSION_BUILD 1
diff --git a/third_party/libopenjpeg20/opj_config.h.cmake.in b/third_party/libopenjpeg20/opj_config.h.cmake.in
deleted file mode 100644
index 5f762ca..0000000
--- a/third_party/libopenjpeg20/opj_config.h.cmake.in
+++ /dev/null
@@ -1,10 +0,0 @@
-/* create opj_config.h for CMake */
-#cmakedefine OPJ_HAVE_STDINT_H 		@OPJ_HAVE_STDINT_H@
-
-/*--------------------------------------------------------------------------*/
-/* OpenJPEG Versioning                                                      */
-
-/* Version number. */
-#define OPJ_VERSION_MAJOR @OPENJPEG_VERSION_MAJOR@
-#define OPJ_VERSION_MINOR @OPENJPEG_VERSION_MINOR@
-#define OPJ_VERSION_BUILD @OPENJPEG_VERSION_BUILD@
diff --git a/third_party/libopenjpeg20/opj_config_private.h b/third_party/libopenjpeg20/opj_config_private.h
index 81b449d..b6986f9 100644
--- a/third_party/libopenjpeg20/opj_config_private.h
+++ b/third_party/libopenjpeg20/opj_config_private.h
@@ -7,7 +7,7 @@
 /* create opj_config_private.h for CMake */
 #define OPJ_HAVE_INTTYPES_H 	1
 
-#define OPJ_PACKAGE_VERSION "2.3.0"
+#define OPJ_PACKAGE_VERSION "2.3.1"
 
 /* Not used by openjp2*/
 /*#define HAVE_MEMORY_H 1*/
diff --git a/third_party/libopenjpeg20/opj_intmath.h b/third_party/libopenjpeg20/opj_intmath.h
index ad13597..754b551 100644
--- a/third_party/libopenjpeg20/opj_intmath.h
+++ b/third_party/libopenjpeg20/opj_intmath.h
@@ -170,7 +170,7 @@
 static INLINE OPJ_UINT32  opj_uint_ceildiv(OPJ_UINT32  a, OPJ_UINT32  b)
 {
     assert(b);
-    return (a + b - 1) / b;
+    return (OPJ_UINT32)(((OPJ_UINT64)a + b - 1) / b);
 }
 
 /**
diff --git a/third_party/libopenjpeg20/pi.c b/third_party/libopenjpeg20/pi.c
index 256fe37..5f3d9ec 100644
--- a/third_party/libopenjpeg20/pi.c
+++ b/third_party/libopenjpeg20/pi.c
@@ -757,6 +757,9 @@
     /* position in x and y of tile */
     OPJ_UINT32 p, q;
 
+    /* non-corrected (in regard to image offset) tile offset */
+    OPJ_UINT32 l_tx0, l_ty0;
+
     /* preconditions */
     assert(p_cp != 00);
     assert(p_image != 00);
@@ -772,14 +775,14 @@
     q = p_tileno / p_cp->tw;
 
     /* find extent of tile */
-    *p_tx0 = opj_int_max((OPJ_INT32)(p_cp->tx0 + p * p_cp->tdx),
-                         (OPJ_INT32)p_image->x0);
-    *p_tx1 = opj_int_min((OPJ_INT32)(p_cp->tx0 + (p + 1) * p_cp->tdx),
-                         (OPJ_INT32)p_image->x1);
-    *p_ty0 = opj_int_max((OPJ_INT32)(p_cp->ty0 + q * p_cp->tdy),
-                         (OPJ_INT32)p_image->y0);
-    *p_ty1 = opj_int_min((OPJ_INT32)(p_cp->ty0 + (q + 1) * p_cp->tdy),
-                         (OPJ_INT32)p_image->y1);
+    l_tx0 = p_cp->tx0 + p *
+            p_cp->tdx; /* can't be greater than p_image->x1 so won't overflow */
+    *p_tx0 = (OPJ_INT32)opj_uint_max(l_tx0, p_image->x0);
+    *p_tx1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_tx0, p_cp->tdx), p_image->x1);
+    l_ty0 = p_cp->ty0 + q *
+            p_cp->tdy; /* can't be greater than p_image->y1 so won't overflow */
+    *p_ty0 = (OPJ_INT32)opj_uint_max(l_ty0, p_image->y0);
+    *p_ty1 = (OPJ_INT32)opj_uint_min(opj_uint_adds(l_ty0, p_cp->tdy), p_image->y1);
 
     /* max precision is 0 (can only grow) */
     *p_max_prec = 0;
diff --git a/third_party/libopenjpeg20/t1.c b/third_party/libopenjpeg20/t1.c
index 7674438..f6f7671 100644
--- a/third_party/libopenjpeg20/t1.c
+++ b/third_party/libopenjpeg20/t1.c
@@ -1618,8 +1618,8 @@
         cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
         cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
 
-        cblk->decoded_data = (OPJ_INT32*)opj_aligned_malloc(cblk_w * cblk_h * sizeof(
-                                 OPJ_INT32));
+        cblk->decoded_data = (OPJ_INT32*)opj_aligned_malloc(sizeof(OPJ_INT32) *
+                             cblk_w * cblk_h);
         if (cblk->decoded_data == NULL) {
             if (job->p_manager_mutex) {
                 opj_mutex_lock(job->p_manager_mutex);
@@ -1634,7 +1634,7 @@
             return;
         }
         /* Zero-init required */
-        memset(cblk->decoded_data, 0, cblk_w * cblk_h * sizeof(OPJ_INT32));
+        memset(cblk->decoded_data, 0, sizeof(OPJ_INT32) * cblk_w * cblk_h);
     } else if (cblk->decoded_data) {
         /* Not sure if that code path can happen, but better be */
         /* safe than sorry */
@@ -2168,9 +2168,18 @@
                         t1->data = tiledp;
                         t1->data_stride = tile_w;
                         if (tccp->qmfbid == 1) {
+                            /* Do multiplication on unsigned type, even if the
+                             * underlying type is signed, to avoid potential
+                             * int overflow on large value (the output will be
+                             * incorrect in such situation, but whatever...)
+                             * This assumes complement-to-2 signed integer
+                             * representation
+                             * Fixes https://github.com/uclouvain/openjpeg/issues/1053
+                             */
+                            OPJ_UINT32* OPJ_RESTRICT tiledp_u = (OPJ_UINT32*) tiledp;
                             for (j = 0; j < cblk_h; ++j) {
                                 for (i = 0; i < cblk_w; ++i) {
-                                    tiledp[tileIndex] *= (1 << T1_NMSEDEC_FRACBITS);
+                                    tiledp_u[tileIndex] <<= T1_NMSEDEC_FRACBITS;
                                     tileIndex++;
                                 }
                                 tileIndex += tileLineAdvance;
diff --git a/third_party/libopenjpeg20/t2.c b/third_party/libopenjpeg20/t2.c
index 6f956d1..9825118 100644
--- a/third_party/libopenjpeg20/t2.c
+++ b/third_party/libopenjpeg20/t2.c
@@ -667,7 +667,11 @@
     opj_tcd_resolution_t *res = &tilec->resolutions[resno];
 
     opj_bio_t *bio = 00;    /* BIO component */
+#ifdef ENABLE_EMPTY_PACKET_OPTIMIZATION
     OPJ_BOOL packet_empty = OPJ_TRUE;
+#else
+    OPJ_BOOL packet_empty = OPJ_FALSE;
+#endif
 
     /* <SOP 0xff91> */
     if (tcp->csty & J2K_CP_CSTY_SOP) {
@@ -728,6 +732,11 @@
     }
     opj_bio_init_enc(bio, c, length);
 
+#ifdef ENABLE_EMPTY_PACKET_OPTIMIZATION
+    /* WARNING: this code branch is disabled, since it has been reported that */
+    /* such packets cause decoding issues with cinema J2K hardware */
+    /* decoders: https://groups.google.com/forum/#!topic/openjpeg/M7M_fLX_Bco */
+
     /* Check if the packet is empty */
     /* Note: we could also skip that step and always write a packet header */
     band = res->bands;
@@ -755,10 +764,9 @@
             break;
         }
     }
-
+#endif
     opj_bio_write(bio, packet_empty ? 0 : 1, 1);           /* Empty header bit */
 
-
     /* Writing Packet header */
     band = res->bands;
     for (bandno = 0; !packet_empty &&
diff --git a/third_party/libopenjpeg20/tcd.c b/third_party/libopenjpeg20/tcd.c
index 7298fa3..9e98f04 100644
--- a/third_party/libopenjpeg20/tcd.c
+++ b/third_party/libopenjpeg20/tcd.c
@@ -910,8 +910,24 @@
             /* p. 64, B.6, ISO/IEC FDIS15444-1 : 2000 (18 august 2000)  */
             l_tl_prc_x_start = opj_int_floordivpow2(l_res->x0, (OPJ_INT32)l_pdx) << l_pdx;
             l_tl_prc_y_start = opj_int_floordivpow2(l_res->y0, (OPJ_INT32)l_pdy) << l_pdy;
-            l_br_prc_x_end = opj_int_ceildivpow2(l_res->x1, (OPJ_INT32)l_pdx) << l_pdx;
-            l_br_prc_y_end = opj_int_ceildivpow2(l_res->y1, (OPJ_INT32)l_pdy) << l_pdy;
+            {
+                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->x1,
+                                  (OPJ_INT32)l_pdx)) << l_pdx;
+                if (tmp > (OPJ_UINT32)INT_MAX) {
+                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
+                    return OPJ_FALSE;
+                }
+                l_br_prc_x_end = (OPJ_INT32)tmp;
+            }
+            {
+                OPJ_UINT32 tmp = ((OPJ_UINT32)opj_int_ceildivpow2(l_res->y1,
+                                  (OPJ_INT32)l_pdy)) << l_pdy;
+                if (tmp > (OPJ_UINT32)INT_MAX) {
+                    opj_event_msg(manager, EVT_ERROR, "Integer overflow\n");
+                    return OPJ_FALSE;
+                }
+                l_br_prc_y_end = (OPJ_INT32)tmp;
+            }
             /*fprintf(stderr, "\t\t\tprc_x_start=%d, prc_y_start=%d, br_prc_x_end=%d, br_prc_y_end=%d \n", l_tl_prc_x_start, l_tl_prc_y_start, l_br_prc_x_end ,l_br_prc_y_end );*/
 
             l_res->pw = (l_res->x0 == l_res->x1) ? 0U : (OPJ_UINT32)((
@@ -1069,13 +1085,16 @@
                                                           cblkwidthexpn);
                     l_current_precinct->ch = (OPJ_UINT32)((brcblkyend - tlcblkystart) >>
                                                           cblkheightexpn);
+
                     if (l_current_precinct->cw && ((OPJ_UINT32)-1) / l_current_precinct->cw < l_current_precinct->ch) {
                         return OPJ_FALSE;
                     }
                     l_nb_code_blocks = l_current_precinct->cw * l_current_precinct->ch;
                     /*fprintf(stderr, "\t\t\t\t precinct_cw = %d x recinct_ch = %d\n",l_current_precinct->cw, l_current_precinct->ch);      */
-
-                    if (((OPJ_UINT32)-1) / (OPJ_UINT32)sizeof_block < l_nb_code_blocks) {
+                    if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof_block) <
+                            l_nb_code_blocks) {
+                        opj_event_msg(manager, EVT_ERROR,
+                                      "Size of code block data exceeds system limits\n");
                         return OPJ_FALSE;
                     }
                     l_nb_code_blocks_size = l_nb_code_blocks * (OPJ_UINT32)sizeof_block;
diff --git a/third_party/libopenjpeg20/thread.c b/third_party/libopenjpeg20/thread.c
index af33c2c..f2fca2e 100644
--- a/third_party/libopenjpeg20/thread.c
+++ b/third_party/libopenjpeg20/thread.c
@@ -723,6 +723,8 @@
         tp->worker_threads[i].thread = opj_thread_create(opj_worker_thread_function,
                                        &(tp->worker_threads[i]));
         if (tp->worker_threads[i].thread == NULL) {
+            opj_mutex_destroy(tp->worker_threads[i].mutex);
+            opj_cond_destroy(tp->worker_threads[i].cond);
             tp->worker_threads_count = i;
             bRet = OPJ_FALSE;
             break;
@@ -732,7 +734,7 @@
     /* Wait all threads to be started */
     /* printf("waiting for all threads to be started\n"); */
     opj_mutex_lock(tp->mutex);
-    while (tp->waiting_worker_thread_count < num_threads) {
+    while (tp->waiting_worker_thread_count < tp->worker_threads_count) {
         opj_cond_wait(tp->cond, tp->mutex);
     }
     opj_mutex_unlock(tp->mutex);
diff --git a/third_party/libpng16/0003-check-errors-in-set-pcal.patch b/third_party/libpng16/0003-check-errors-in-set-pcal.patch
deleted file mode 100644
index 58e27ba..0000000
--- a/third_party/libpng16/0003-check-errors-in-set-pcal.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-diff --git a/pngset.c b/pngset.c
-index cccd9cd..83d6ce2 100644
---- a/pngset.c
-+++ b/pngset.c
-@@ -283,17 +283,29 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforpinfo_ptr,
- 
-    /* Check that the type matches the specification. */
-    if (type < 0 || type > 3)
--      png_error(png_ptr, "Invalid pCAL equation type");
-+   {
-+      png_chunk_report(png_ptr, "Invalid pCAL equation type",
-+            PNG_CHUNK_WRITE_ERROR);
-+      return;
-+   }
- 
-    if (nparams < 0 || nparams > 255)
--      png_error(png_ptr, "Invalid pCAL parameter count");
-+   {
-+      png_chunk_report(png_ptr, "Invalid pCAL parameter count",
-+            PNG_CHUNK_WRITE_ERROR);
-+      return;
-+   }
- 
-    /* Validate params[nparams] */
-    for (i=0; i<nparams; ++i)
-    {
-       if (params[i] == NULL ||
-           !png_check_fp_string(params[i], strlen(params[i])))
--         png_error(png_ptr, "Invalid format for pCAL parameter");
-+      {
-+         png_chunk_report(png_ptr, "Invalid format for pCAL parameter",
-+               PNG_CHUNK_WRITE_ERROR);
-+         return;
-+      }
-    }
- 
-    info_ptr->pcal_purpose = png_voidcast(png_charp,
-@@ -301,8 +313,8 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforpinfo_ptr,
- 
-    if (info_ptr->pcal_purpose == NULL)
-    {
--      png_warning(png_ptr, "Insufficient memory for pCAL purpose");
--
-+      png_chunk_report(png_ptr, "Insufficient memory for pCAL purpose",
-+            PNG_CHUNK_WRITE_ERROR);
-       return;
-    }
diff --git a/third_party/libpng16/0004-invalid-icc.patch b/third_party/libpng16/0004-invalid-icc.patch
deleted file mode 100644
index 0052c8e..0000000
--- a/third_party/libpng16/0004-invalid-icc.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-diff --git a/png.c b/png.c
-index 35e14f63d..01d8d9bae 100644
---- a/png.c
-+++ b/png.c
-@@ -1931,8 +1931,8 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
- static const png_byte D50_nCIEXYZ[12] =
-    { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d };
- 
--int /* PRIVATE */
--png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-+static int /* bool */
-+icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length)
- {
-    if (profile_length < 132)
-@@ -1942,6 +1942,40 @@ png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    return 1;
- }
- 
-+#ifdef PNG_READ_iCCP_SUPPORTED
-+int /* PRIVATE */
-+png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-+    png_const_charp name, png_uint_32 profile_length)
-+{
-+   if (!icc_check_length(png_ptr, colorspace, name, profile_length))
-+      return 0;
-+
-+   /* This needs to be here because the 'normal' check is in
-+    * png_decompress_chunk, yet this happens after the attempt to
-+    * png_malloc_base the required data.  We only need this on read; on write
-+    * the caller supplies the profile buffer so libpng doesn't allocate it.  See
-+    * the call to icc_check_length below (the write case).
-+    */
-+#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
-+      else if (png_ptr->user_chunk_malloc_max > 0 &&
-+               png_ptr->user_chunk_malloc_max < profile_length)
-+         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-+             "exceeds application limits");
-+#  elif PNG_USER_CHUNK_MALLOC_MAX > 0
-+      else if (PNG_USER_CHUNK_MALLOC_MAX < profile_length)
-+         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-+             "exceeds libpng limits");
-+#  else /* !SET_USER_LIMITS */
-+      /* This will get compiled out on all 32-bit and better systems. */
-+      else if (PNG_SIZE_MAX < profile_length)
-+         return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-+             "exceeds system limits");
-+#  endif /* !SET_USER_LIMITS */
-+
-+   return 1;
-+}
-+#endif /* READ_iCCP */
-+
- int /* PRIVATE */
- png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    png_const_charp name, png_uint_32 profile_length,
-@@ -2379,7 +2413,7 @@ png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,
-    if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
-       return 0;
- 
--   if (png_icc_check_length(png_ptr, colorspace, name, profile_length) != 0 &&
-+   if (icc_check_length(png_ptr, colorspace, name, profile_length) != 0 &&
-        png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,
-           color_type) != 0 &&
-        png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,
-diff --git a/pngpriv.h b/pngpriv.h
-index 9ea023fea..633671352 100644
---- a/pngpriv.h
-+++ b/pngpriv.h
-@@ -1541,9 +1541,11 @@ PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr,
-    /* The 'name' is used for information only */
- 
- /* Routines for checking parts of an ICC profile. */
-+#ifdef PNG_READ_iCCP_SUPPORTED
- PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_const_charp name,
-    png_uint_32 profile_length), PNG_EMPTY);
-+#endif /* READ_iCCP */
- PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,
-    png_colorspacerp colorspace, png_const_charp name,
-    png_uint_32 profile_length,
diff --git a/third_party/libpng16/LICENSE b/third_party/libpng16/LICENSE
new file mode 100644
index 0000000..e0c5b53
--- /dev/null
+++ b/third_party/libpng16/LICENSE
@@ -0,0 +1,134 @@
+COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
+=========================================
+
+PNG Reference Library License version 2
+---------------------------------------
+
+ * Copyright (c) 1995-2019 The PNG Reference Library Authors.
+ * Copyright (c) 2018-2019 Cosmin Truta.
+ * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
+ * Copyright (c) 1996-1997 Andreas Dilger.
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
+
+The software is supplied "as is", without warranty of any kind,
+express or implied, including, without limitation, the warranties
+of merchantability, fitness for a particular purpose, title, and
+non-infringement.  In no event shall the Copyright owners, or
+anyone distributing the software, be liable for any damages or
+other liability, whether in contract, tort or otherwise, arising
+from, out of, or in connection with the software, or the use or
+other dealings in the software, even if advised of the possibility
+of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute
+this software, or portions hereof, for any purpose, without fee,
+subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you
+    must not claim that you wrote the original software.  If you
+    use this software in a product, an acknowledgment in the product
+    documentation would be appreciated, but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must
+    not be misrepresented as being the original software.
+
+ 3. This Copyright notice may not be removed or altered from any
+    source or altered source distribution.
+
+
+PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35)
+-----------------------------------------------------------------------
+
+libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are
+Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are
+derived from libpng-1.0.6, and are distributed according to the same
+disclaimer and license as libpng-1.0.6 with the following individuals
+added to the list of Contributing Authors:
+
+    Simon-Pierre Cadieux
+    Eric S. Raymond
+    Mans Rullgard
+    Cosmin Truta
+    Gilles Vollant
+    James Yu
+    Mandar Sahastrabuddhe
+    Google Inc.
+    Vadim Barkov
+
+and with the following additions to the disclaimer:
+
+    There is no warranty against interference with your enjoyment of
+    the library or against infringement.  There is no warranty that our
+    efforts or the library will fulfill any of your particular purposes
+    or needs.  This library is provided with all faults, and the entire
+    risk of satisfactory quality, performance, accuracy, and effort is
+    with the user.
+
+Some files in the "contrib" directory and some configure-generated
+files that are distributed with libpng have other copyright owners, and
+are released under other open source licenses.
+
+libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
+libpng-0.96, and are distributed according to the same disclaimer and
+license as libpng-0.96, with the following individuals added to the
+list of Contributing Authors:
+
+    Tom Lane
+    Glenn Randers-Pehrson
+    Willem van Schaik
+
+libpng versions 0.89, June 1996, through 0.96, May 1997, are
+Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
+and are distributed according to the same disclaimer and license as
+libpng-0.88, with the following individuals added to the list of
+Contributing Authors:
+
+    John Bowler
+    Kevin Bracey
+    Sam Bushell
+    Magnus Holmgren
+    Greg Roelofs
+    Tom Tanner
+
+Some files in the "scripts" directory have other copyright owners,
+but are released under this license.
+
+libpng versions 0.5, May 1995, through 0.88, January 1996, are
+Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
+
+For the purposes of this copyright and license, "Contributing Authors"
+is defined as the following set of individuals:
+
+    Andreas Dilger
+    Dave Martindale
+    Guy Eric Schalnat
+    Paul Schmidt
+    Tim Wegner
+
+The PNG Reference Library is supplied "AS IS".  The Contributing
+Authors and Group 42, Inc. disclaim all warranties, expressed or
+implied, including, without limitation, the warranties of
+merchantability and of fitness for any purpose.  The Contributing
+Authors and Group 42, Inc. assume no liability for direct, indirect,
+incidental, special, exemplary, or consequential damages, which may
+result from the use of the PNG Reference Library, even if advised of
+the possibility of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+source code, or portions hereof, for any purpose, without fee, subject
+to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented.
+
+ 2. Altered versions must be plainly marked as such and must not
+    be misrepresented as being the original source.
+
+ 3. This Copyright notice may not be removed or altered from any
+    source or altered source distribution.
+
+The Contributing Authors and Group 42, Inc. specifically permit,
+without fee, and encourage the use of this source code as a component
+to supporting the PNG file format in commercial products.  If you use
+this source code in a product, acknowledgment is not required but would
+be appreciated.
diff --git a/third_party/libpng16/README.pdfium b/third_party/libpng16/README.pdfium
index faa5959..0d9c30c 100644
--- a/third_party/libpng16/README.pdfium
+++ b/third_party/libpng16/README.pdfium
@@ -1,6 +1,6 @@
 Name: libpng
 URL: http://libpng.org/
-Version: 1.6.22
+Version: 1.6.37
 Security Critical: yes
 License: libpng license
 License Android Compatible: yes
@@ -10,12 +10,10 @@
 
 Local Modifications:
 
-Local changes in Chromium's copy of libpng as of https://crrev.com/404379 - see
-README.chromium.
+Local changes in Chromium's copy of libpng as of
+e87a02987101e2dbe319a4aba6b52470f7624b4a - see README.chromium.
 
 pnglibconf.h: a copy of libpng's scripts/pnglibconf.h.prebuilt.
 pngprefix.h: manually-created redefinitions to avoid conflicts with Chromium.
 0000-build-config.patch: Local build configuration changes.
 0002-static-png-gt.patch: Unconditionally use static png_gt() in png.c to avoid compilation warning.
-0003-check-errors-in-set-pcal.patch: Backported github.com/glennrp/libpng/pull/135
-0004-invalid-icc.patch: Fix for large allocation for invalid ICC https://github.com/glennrp/libpng/commit/92a7c79db2c962d04006b35e2603ba9d5ce75541
diff --git a/third_party/libpng16/arm/arm_init.c b/third_party/libpng16/arm/arm_init.c
index 02df812..a34ecdb 100644
--- a/third_party/libpng16/arm/arm_init.c
+++ b/third_party/libpng16/arm/arm_init.c
@@ -1,14 +1,15 @@
 
 /* arm_init.c - NEON optimised filter functions
  *
+ * Copyright (c) 2018 Cosmin Truta
  * Copyright (c) 2014,2016 Glenn Randers-Pehrson
  * Written by Mans Rullgard, 2011.
- * Last changed in libpng 1.6.22 [May 26, 2016]
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
  * and license in png.h
  */
+
 /* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are
  * called.
  */
diff --git a/third_party/libpng16/arm/filter_neon.S b/third_party/libpng16/arm/filter_neon.S
index 3b061d6..2308aad 100644
--- a/third_party/libpng16/arm/filter_neon.S
+++ b/third_party/libpng16/arm/filter_neon.S
@@ -1,9 +1,9 @@
 
 /* filter_neon.S - NEON optimised filter functions
  *
- * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2014,2017 Glenn Randers-Pehrson
  * Written by Mans Rullgard, 2011.
- * Last changed in libpng 1.6.16 [December 22, 2014]
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -16,7 +16,7 @@
 #define PNG_VERSION_INFO_ONLY
 #include "../pngpriv.h"
 
-#if defined(__linux__) && defined(__ELF__)
+#if (defined(__linux__) || defined(__FreeBSD__)) && defined(__ELF__)
 .section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
 #endif
 
diff --git a/third_party/libpng16/arm/filter_neon_intrinsics.c b/third_party/libpng16/arm/filter_neon_intrinsics.c
index ea7e356..0647bfa 100644
--- a/third_party/libpng16/arm/filter_neon_intrinsics.c
+++ b/third_party/libpng16/arm/filter_neon_intrinsics.c
@@ -1,12 +1,11 @@
 
 /* filter_neon_intrinsics.c - NEON optimised filter functions
  *
+ * Copyright (c) 2018 Cosmin Truta
  * Copyright (c) 2014,2016 Glenn Randers-Pehrson
  * Written by James Yu <james.yu at linaro.org>, October 2013.
  * Based on filter_neon.S, written by Mans Rullgard, 2011.
  *
- * Last changed in libpng 1.6.22 [May 26, 2016]
- *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
  * and license in png.h
@@ -19,7 +18,11 @@
 /* This code requires -mfpu=neon on the command line: */
 #if PNG_ARM_NEON_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */
 
-#include <arm_neon.h>
+#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
+#  include <arm64_neon.h>
+#else
+#  include <arm_neon.h>
+#endif
 
 /* libpng row pointers are not necessarily aligned to any particular boundary,
  * however this code will only work with appropriate alignment.  arm/arm_init.c
@@ -33,6 +36,11 @@
  * 'type'.  This is written this way just to hide the GCC strict aliasing
  * warning; note that the code is safe because there never is an alias between
  * the input and output pointers.
+ *
+ * When compiling with MSVC ARM64, the png_ldr macro can't be passed directly
+ * to vst4_lane_u32, because of an internal compiler error inside MSVC.
+ * To avoid this compiler bug, we use a temporary variable (vdest_val) to store
+ * the result of png_ldr.
  */
 #define png_ldr(type,pointer)\
    (temp_pointer = png_ptr(type,pointer), *temp_pointer)
@@ -125,12 +133,15 @@
       uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp);
       uint8x8x4_t vrp = *vrpt;
       uint32x2x4_t *temp_pointer;
+      uint32x2x4_t vdest_val;
 
       vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
       vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]);
       vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]);
       vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]);
-      vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0);
+
+      vdest_val = png_ldr(uint32x2x4_t, &vdest);
+      vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
    }
 
    PNG_UNUSED(prev_row)
@@ -223,6 +234,7 @@
       uint8x8x4_t *vrpt, *vppt;
       uint8x8x4_t vrp, vpp;
       uint32x2x4_t *temp_pointer;
+      uint32x2x4_t vdest_val;
 
       vtmp = vld4_u32(png_ptr(uint32_t,rp));
       vrpt = png_ptr(uint8x8x4_t,&vtmp);
@@ -240,7 +252,8 @@
       vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]);
       vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
 
-      vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0);
+      vdest_val = png_ldr(uint32x2x4_t, &vdest);
+      vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
    }
 }
 
@@ -359,6 +372,7 @@
       uint8x8x4_t *vrpt, *vppt;
       uint8x8x4_t vrp, vpp;
       uint32x2x4_t *temp_pointer;
+      uint32x2x4_t vdest_val;
 
       vtmp = vld4_u32(png_ptr(uint32_t,rp));
       vrpt = png_ptr(uint8x8x4_t,&vtmp);
@@ -378,7 +392,8 @@
 
       vlast = vpp.val[3];
 
-      vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0);
+      vdest_val = png_ldr(uint32x2x4_t, &vdest);
+      vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
    }
 }
 
diff --git a/third_party/libpng16/arm/palette_neon_intrinsics.c b/third_party/libpng16/arm/palette_neon_intrinsics.c
new file mode 100644
index 0000000..8217d65
--- /dev/null
+++ b/third_party/libpng16/arm/palette_neon_intrinsics.c
@@ -0,0 +1,149 @@
+
+/* palette_neon_intrinsics.c - NEON optimised palette expansion functions
+ *
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 2017-2018 Arm Holdings. All rights reserved.
+ * Written by Richard Townsend <Richard.Townsend@arm.com>, February 2017.
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "../pngpriv.h"
+
+#if PNG_ARM_NEON_IMPLEMENTATION == 1
+
+#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
+#  include <arm64_neon.h>
+#else
+#  include <arm_neon.h>
+#endif
+
+/* Build an RGBA8 palette from the separate RGB and alpha palettes. */
+void
+png_riffle_palette_neon(png_structrp png_ptr)
+{
+   png_const_colorp palette = png_ptr->palette;
+   png_bytep riffled_palette = png_ptr->riffled_palette;
+   png_const_bytep trans_alpha = png_ptr->trans_alpha;
+   int num_trans = png_ptr->num_trans;
+   int i;
+
+   png_debug(1, "in png_riffle_palette_neon");
+
+   /* Initially black, opaque. */
+   uint8x16x4_t w = {{
+      vdupq_n_u8(0x00),
+      vdupq_n_u8(0x00),
+      vdupq_n_u8(0x00),
+      vdupq_n_u8(0xff),
+   }};
+
+   /* First, riffle the RGB colours into an RGBA8 palette.
+    * The alpha component is set to opaque for now.
+    */
+   for (i = 0; i < 256; i += 16)
+   {
+      uint8x16x3_t v = vld3q_u8((png_const_bytep)(palette + i));
+      w.val[0] = v.val[0];
+      w.val[1] = v.val[1];
+      w.val[2] = v.val[2];
+      vst4q_u8(riffled_palette + (i << 2), w);
+   }
+
+   /* Fix up the missing transparency values. */
+   for (i = 0; i < num_trans; i++)
+      riffled_palette[(i << 2) + 3] = trans_alpha[i];
+}
+
+/* Expands a palettized row into RGBA8. */
+int
+png_do_expand_palette_rgba8_neon(png_structrp png_ptr, png_row_infop row_info,
+    png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
+{
+   png_uint_32 row_width = row_info->width;
+   const png_uint_32 *riffled_palette =
+      (const png_uint_32 *)png_ptr->riffled_palette;
+   const png_int_32 pixels_per_chunk = 4;
+   int i;
+
+   png_debug(1, "in png_do_expand_palette_rgba8_neon");
+
+   if (row_width < pixels_per_chunk)
+      return 0;
+
+   /* This function originally gets the last byte of the output row.
+    * The NEON part writes forward from a given position, so we have
+    * to seek this back by 4 pixels x 4 bytes.
+    */
+   *ddp = *ddp - ((pixels_per_chunk * sizeof(png_uint_32)) - 1);
+
+   for (i = 0; i < row_width; i += pixels_per_chunk)
+   {
+      uint32x4_t cur;
+      png_bytep sp = *ssp - i, dp = *ddp - (i << 2);
+      cur = vld1q_dup_u32 (riffled_palette + *(sp - 3));
+      cur = vld1q_lane_u32(riffled_palette + *(sp - 2), cur, 1);
+      cur = vld1q_lane_u32(riffled_palette + *(sp - 1), cur, 2);
+      cur = vld1q_lane_u32(riffled_palette + *(sp - 0), cur, 3);
+      vst1q_u32((void *)dp, cur);
+   }
+   if (i != row_width)
+   {
+      /* Remove the amount that wasn't processed. */
+      i -= pixels_per_chunk;
+   }
+
+   /* Decrement output pointers. */
+   *ssp = *ssp - i;
+   *ddp = *ddp - (i << 2);
+   return i;
+}
+
+/* Expands a palettized row into RGB8. */
+int
+png_do_expand_palette_rgb8_neon(png_structrp png_ptr, png_row_infop row_info,
+    png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
+{
+   png_uint_32 row_width = row_info->width;
+   png_const_bytep palette = (png_const_bytep)png_ptr->palette;
+   const png_uint_32 pixels_per_chunk = 8;
+   int i;
+
+   png_debug(1, "in png_do_expand_palette_rgb8_neon");
+
+   if (row_width <= pixels_per_chunk)
+      return 0;
+
+   /* Seeking this back by 8 pixels x 3 bytes. */
+   *ddp = *ddp - ((pixels_per_chunk * sizeof(png_color)) - 1);
+
+   for (i = 0; i < row_width; i += pixels_per_chunk)
+   {
+      uint8x8x3_t cur;
+      png_bytep sp = *ssp - i, dp = *ddp - ((i << 1) + i);
+      cur = vld3_dup_u8(palette + sizeof(png_color) * (*(sp - 7)));
+      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 6)), cur, 1);
+      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 5)), cur, 2);
+      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 4)), cur, 3);
+      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 3)), cur, 4);
+      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 2)), cur, 5);
+      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 1)), cur, 6);
+      cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 0)), cur, 7);
+      vst3_u8((void *)dp, cur);
+   }
+
+   if (i != row_width)
+   {
+      /* Remove the amount that wasn't processed. */
+      i -= pixels_per_chunk;
+   }
+
+   /* Decrement output pointers. */
+   *ssp = *ssp - i;
+   *ddp = *ddp - ((i << 1) + i);
+   return i;
+}
+
+#endif /* PNG_ARM_NEON_IMPLEMENTATION */
diff --git a/third_party/libpng16/contrib/intel/INSTALL b/third_party/libpng16/contrib/intel/INSTALL
deleted file mode 100644
index cd5cdd9..0000000
--- a/third_party/libpng16/contrib/intel/INSTALL
+++ /dev/null
@@ -1,158 +0,0 @@
-Enabling SSE support
-
-Copyright (c) 2016 Google, Inc.
-Written by Mike Klein, Matt Sarett
-
-This INSTALL file written by Glenn Randers-Pehrson, 2016.
-
-If you have moved intel_init.c and filter_sse2_intrinsics.c to a different
-directory, be sure to update the '#include "../../pngpriv.h"' line in both
-files if necessary to point to the correct relative location of pngpriv.h
-with respect to the new location of those files.
-
-To enable SSE support in libpng, follow the instructions in I, II, or III,
-below:
-
-I. Using patched "configure" scripts:
-
-First, apply intel_sse.patch in your build directory.
-
-   patch -i contrib/intel/intel_sse.patch -p1
-
-Then, if you are not building in a new GIT clone, e.g., in a tar
-distribution, remove any existing pre-built configure scripts:
-
-   ./configure --enable-maintainer-mode
-   make maintainer-clean
-   ./autogen.sh --maintainer --clean
-
-Finally, configure libpng with -DPNG_INTEL_SSE in CPPFLAGS:
-
-   ./autogen.sh --maintainer
-   CPPFLAGS="-DPNG_INTEL_SSE" ./configure [options]
-   make CPPFLAGS="-DPNG_INTEL_SSE" [options]
-   make
-
-II. Using a custom makefile:
-
-If you are using a custom makefile makefile, you will have to update it
-manually to include contrib/intel/*.o in the dependencies, and to define
-PNG_INTEL_SSE.
-
-III. Using manually updated "configure" scripts:
-
-If you prefer, manually edit pngpriv.h, configure.ac, and Makefile.am,
-following the instructions below, then follow the instructions in
-section II of INSTALL in the main libpng directory, then configure libpng
-with -DPNG_INTEL_SSE in CPPFLAGS.
-
-1. Add the following code to configure.ac under HOST SPECIFIC OPTIONS
-directly beneath the section for ARM:
-
------------------cut----------------
-# INTEL
-# =====
-#
-# INTEL SSE (SIMD) support.
-
-AC_ARG_ENABLE([intel-sse],
-   AS_HELP_STRING([[[--enable-intel-sse]]],
-      [Enable Intel SSE optimizations: =no/off, yes/on:]
-      [no/off: disable the optimizations;]
-      [yes/on: enable the optimizations.]
-      [If not specified: determined by the compiler.]),
-   [case "$enableval" in
-      no|off)
-         # disable the default enabling:
-         AC_DEFINE([PNG_INTEL_SSE_OPT], [0],
-                   [Disable Intel SSE optimizations])
-         # Prevent inclusion of the assembler files below:
-         enable_intel_sse=no;;
-      yes|on)
-         AC_DEFINE([PNG_INTEL_SSE_OPT], [1],
-                   [Enable Intel SSE optimizations]);;
-      *)
-         AC_MSG_ERROR([--enable-intel-sse=${enable_intel_sse}: invalid value])
-   esac])
-
-# Add Intel specific files to all builds where the host_cpu is Intel ('x86*')
-# or where Intel optimizations were explicitly requested (this allows a
-# fallback if a future host CPU does not match 'x86*')
-AM_CONDITIONAL([PNG_INTEL_SSE],
-   [test "$enable_intel_sse" != 'no' &&
-    case "$host_cpu" in
-      i?86|x86_64) :;;
-      *)    test "$enable_intel_sse" != '';;
-    esac])
------------------cut----------------
-
-2. Add the following code to Makefile.am under HOST SPECIFIC OPTIONS
-directly beneath the "if PNG_ARM_NEON ... endif" statement:
-
------------------cut----------------
-if PNG_INTEL_SSE
-libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES += contrib/intel/intel_init.c\
-    contrib/intel/filter_sse2_intrinsics.c
-endif
------------------cut----------------
-
-3. Add the following lines to pngpriv.h, following the PNG_ARM_NEON_OPT
-code:
-
------------------cut----------------
-#ifndef PNG_INTEL_SSE_OPT
-#   ifdef PNG_INTEL_SSE
-      /* Only check for SSE if the build configuration has been modified to
-       * enable SSE optimizations.  This means that these optimizations will
-       * be off by default.  See contrib/intel for more details.
-       */
-#     if defined(__SSE4_1__) || defined(__AVX__) || defined(__SSSE3__) || \
-       defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
-       (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
-#         define PNG_INTEL_SSE_OPT 1
-#      endif
-#   endif
-#endif
-
-#if PNG_INTEL_SSE_OPT > 0
-#   ifndef PNG_INTEL_SSE_IMPLEMENTATION
-#      if defined(__SSE4_1__) || defined(__AVX__)
-          /* We are not actually using AVX, but checking for AVX is the best
-             way we can detect SSE4.1 and SSSE3 on MSVC.
-          */
-#         define PNG_INTEL_SSE_IMPLEMENTATION 3
-#      elif defined(__SSSE3__)
-#         define PNG_INTEL_SSE_IMPLEMENTATION 2
-#      elif defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
-       (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
-#         define PNG_INTEL_SSE_IMPLEMENTATION 1
-#      else
-#         define PNG_INTEL_SSE_IMPLEMENTATION 0
-#      endif
-#   endif
-
-#   if PNG_INTEL_SSE_IMPLEMENTATION > 0
-#      define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_sse2
-#   endif
-#endif
-
------------------cut----------------
-
-4. Add the following lines to pngpriv.h, following the prototype for
-png_read_filter_row_paeth4_neon:
-
------------------cut----------------
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-
------------------cut----------------
diff --git a/third_party/libpng16/contrib/intel/filter_sse2_intrinsics.c b/third_party/libpng16/contrib/intel/filter_sse2_intrinsics.c
deleted file mode 100644
index aea3f86..0000000
--- a/third_party/libpng16/contrib/intel/filter_sse2_intrinsics.c
+++ /dev/null
@@ -1,379 +0,0 @@
-
-/* filter_sse2_intrinsics.c - SSE2 optimized filter functions
- *
- * Copyright (c) 2016 Google, Inc.
- * Written by Mike Klein and Matt Sarett
- * Derived from arm/filter_neon_intrinsics.c, which was
- * Copyright (c) 2014,2016 Glenn Randers-Pehrson
- *
- * Last changed in libpng 1.6.22 [May 26, 2016]
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "../../pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-
-#if PNG_INTEL_SSE_IMPLEMENTATION > 0
-
-#include <immintrin.h>
-
-/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
- * They're positioned like this:
- *    prev:  c b
- *    row:   a d
- * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
- * whichever of a, b, or c is closest to p=a+b-c.
- */
-
-static __m128i load4(const void* p) {
-   return _mm_cvtsi32_si128(*(const int*)p);
-}
-
-static void store4(void* p, __m128i v) {
-   *(int*)p = _mm_cvtsi128_si32(v);
-}
-
-static __m128i load3(const void* p) {
-   /* We'll load 2 bytes, then 1 byte,
-    * then mask them together, and finally load into SSE.
-    */
-   const png_uint_16* p01 = p;
-   const png_byte*    p2  = (const png_byte*)(p01+1);
-
-   png_uint_32 v012 = (png_uint_32)(*p01)
-                    | (png_uint_32)(*p2) << 16;
-   return load4(&v012);
-}
-
-static void store3(void* p, __m128i v) {
-   /* We'll pull from SSE as a 32-bit int, then write
-    * its bottom two bytes, then its third byte.
-    */
-   png_uint_32 v012;
-   store4(&v012, v);
-
-   png_uint_16* p01 = p;
-   png_byte*    p2  = (png_byte*)(p01+1);
-   *p01 = v012;
-   *p2  = v012 >> 16;
-}
-
-void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Sub filter predicts each pixel as the previous pixel, a.
-    * There is no pixel to the left of the first pixel.  It's encoded directly.
-    * That works with our main loop if we just say that left pixel was zero.
-    */
-   png_debug(1, "in png_read_filter_row_sub3_sse2");
-   __m128i a, d = _mm_setzero_si128();
-
-   int rb = row_info->rowbytes;
-   while (rb >= 4) {
-      a = d; d = load4(row);
-      d = _mm_add_epi8(d, a);
-      store3(row, d);
-
-      row += 3;
-      rb  -= 3;
-   }
-   if (rb > 0) {
-      a = d; d = load3(row);
-      d = _mm_add_epi8(d, a);
-      store3(row, d);
-
-      row += 3;
-      rb  -= 3;
-   }
-}
-
-void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Sub filter predicts each pixel as the previous pixel, a.
-    * There is no pixel to the left of the first pixel.  It's encoded directly.
-    * That works with our main loop if we just say that left pixel was zero.
-    */
-   png_debug(1, "in png_read_filter_row_sub4_sse2");
-   __m128i a, d = _mm_setzero_si128();
-
-   int rb = row_info->rowbytes;
-   while (rb > 0) {
-      a = d; d = load4(row);
-      d = _mm_add_epi8(d, a);
-      store4(row, d);
-
-      row += 4;
-      rb  -= 4;
-   }
-}
-
-void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Avg filter predicts each pixel as the (truncated) average of a and b.
-    * There's no pixel to the left of the first pixel.  Luckily, it's
-    * predicted to be half of the pixel above it.  So again, this works
-    * perfectly with our loop if we make sure a starts at zero.
-    */
-   png_debug(1, "in png_read_filter_row_avg3_sse2");
-   const __m128i zero = _mm_setzero_si128();
-   __m128i    b;
-   __m128i a, d = zero;
-
-   int rb = row_info->rowbytes;
-   while (rb >= 4) {
-             b = load4(prev);
-      a = d; d = load4(row );
-
-      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
-      __m128i avg = _mm_avg_epu8(a,b);
-      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
-      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
-                                            _mm_set1_epi8(1)));
-      d = _mm_add_epi8(d, avg);
-      store3(row, d);
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-   if (rb > 0) {
-             b = load3(prev);
-      a = d; d = load3(row );
-
-      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
-      __m128i avg = _mm_avg_epu8(a,b);
-      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
-      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
-                                            _mm_set1_epi8(1)));
-
-      d = _mm_add_epi8(d, avg);
-      store3(row, d);
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-}
-
-void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* The Avg filter predicts each pixel as the (truncated) average of a and b.
-    * There's no pixel to the left of the first pixel.  Luckily, it's
-    * predicted to be half of the pixel above it.  So again, this works
-    * perfectly with our loop if we make sure a starts at zero.
-    */
-   png_debug(1, "in png_read_filter_row_avg4_sse2");
-   const __m128i zero = _mm_setzero_si128();
-   __m128i    b;
-   __m128i a, d = zero;
-
-   int rb = row_info->rowbytes;
-   while (rb > 0) {
-             b = load4(prev);
-      a = d; d = load4(row );
-
-      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
-      __m128i avg = _mm_avg_epu8(a,b);
-      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
-      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
-                                            _mm_set1_epi8(1)));
-
-      d = _mm_add_epi8(d, avg);
-      store4(row, d);
-
-      prev += 4;
-      row  += 4;
-      rb   -= 4;
-   }
-}
-
-/* Returns |x| for 16-bit lanes. */
-static __m128i abs_i16(__m128i x) {
-#if PNG_INTEL_SSE_IMPLEMENTATION >= 2
-   return _mm_abs_epi16(x);
-#else
-   /* Read this all as, return x<0 ? -x : x.
-   * To negate two's complement, you flip all the bits then add 1.
-    */
-   __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());
-
-   /* Flip negative lanes. */
-   x = _mm_xor_si128(x, is_negative);
-
-   /* +1 to negative lanes, else +0. */
-   x = _mm_add_epi16(x, _mm_srli_epi16(is_negative, 15));
-   return x;
-#endif
-}
-
-/* Bytewise c ? t : e. */
-static __m128i if_then_else(__m128i c, __m128i t, __m128i e) {
-#if PNG_INTEL_SSE_IMPLEMENTATION >= 3
-   return _mm_blendv_epi8(e,t,c);
-#else
-   return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));
-#endif
-}
-
-void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* Paeth tries to predict pixel d using the pixel to the left of it, a,
-    * and two pixels from the previous row, b and c:
-    *   prev: c b
-    *   row:  a d
-    * The Paeth function predicts d to be whichever of a, b, or c is nearest to
-    * p=a+b-c.
-    *
-    * The first pixel has no left context, and so uses an Up filter, p = b.
-    * This works naturally with our main loop's p = a+b-c if we force a and c
-    * to zero.
-    * Here we zero b and d, which become c and a respectively at the start of
-    * the loop.
-    */
-   png_debug(1, "in png_read_filter_row_paeth3_sse2");
-   const __m128i zero = _mm_setzero_si128();
-   __m128i c, b = zero,
-           a, d = zero;
-
-   int rb = row_info->rowbytes;
-   while (rb >= 4) {
-      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
-       * intermediates.
-       */
-      c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
-      a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
-
-      /* (p-a) == (a+b-c - a) == (b-c) */
-      __m128i pa = _mm_sub_epi16(b,c);
-
-      /* (p-b) == (a+b-c - b) == (a-c) */
-      __m128i pb = _mm_sub_epi16(a,c);
-
-      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
-      __m128i pc = _mm_add_epi16(pa,pb);
-
-      pa = abs_i16(pa);  /* |p-a| */
-      pb = abs_i16(pb);  /* |p-b| */
-      pc = abs_i16(pc);  /* |p-c| */
-
-      __m128i smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
-
-      /* Paeth breaks ties favoring a over b over c. */
-      __m128i nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
-                         if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
-                                                                     c));
-
-      /* Note `_epi8`: we need addition to wrap modulo 255. */
-      d = _mm_add_epi8(d, nearest);
-      store3(row, _mm_packus_epi16(d,d));
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-   if (rb > 0) {
-      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
-       * intermediates.
-       */
-      c = b; b = _mm_unpacklo_epi8(load3(prev), zero);
-      a = d; d = _mm_unpacklo_epi8(load3(row ), zero);
-
-      /* (p-a) == (a+b-c - a) == (b-c) */
-      __m128i pa = _mm_sub_epi16(b,c);
-
-      /* (p-b) == (a+b-c - b) == (a-c) */
-      __m128i pb = _mm_sub_epi16(a,c);
-
-      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
-      __m128i pc = _mm_add_epi16(pa,pb);
-
-      pa = abs_i16(pa);  /* |p-a| */
-      pb = abs_i16(pb);  /* |p-b| */
-      pc = abs_i16(pc);  /* |p-c| */
-
-      __m128i smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
-
-      /* Paeth breaks ties favoring a over b over c. */
-      __m128i nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
-                         if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
-                                                                     c));
-
-      /* Note `_epi8`: we need addition to wrap modulo 255. */
-      d = _mm_add_epi8(d, nearest);
-      store3(row, _mm_packus_epi16(d,d));
-
-      prev += 3;
-      row  += 3;
-      rb   -= 3;
-   }
-}
-
-void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev)
-{
-   /* Paeth tries to predict pixel d using the pixel to the left of it, a,
-    * and two pixels from the previous row, b and c:
-    *   prev: c b
-    *   row:  a d
-    * The Paeth function predicts d to be whichever of a, b, or c is nearest to
-    * p=a+b-c.
-    *
-    * The first pixel has no left context, and so uses an Up filter, p = b.
-    * This works naturally with our main loop's p = a+b-c if we force a and c
-    * to zero.
-    * Here we zero b and d, which become c and a respectively at the start of
-    * the loop.
-    */
-   png_debug(1, "in png_read_filter_row_paeth4_sse2");
-   const __m128i zero = _mm_setzero_si128();
-   __m128i c, b = zero,
-           a, d = zero;
-
-   int rb = row_info->rowbytes;
-   while (rb > 0) {
-      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
-       * intermediates.
-       */
-      c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
-      a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
-
-      /* (p-a) == (a+b-c - a) == (b-c) */
-      __m128i pa = _mm_sub_epi16(b,c);
-
-      /* (p-b) == (a+b-c - b) == (a-c) */
-      __m128i pb = _mm_sub_epi16(a,c);
-
-      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
-      __m128i pc = _mm_add_epi16(pa,pb);
-
-      pa = abs_i16(pa);  /* |p-a| */
-      pb = abs_i16(pb);  /* |p-b| */
-      pc = abs_i16(pc);  /* |p-c| */
-
-      __m128i smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
-
-      /* Paeth breaks ties favoring a over b over c. */
-      __m128i nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
-                         if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
-                                                                     c));
-
-      /* Note `_epi8`: we need addition to wrap modulo 255. */
-      d = _mm_add_epi8(d, nearest);
-      store4(row, _mm_packus_epi16(d,d));
-
-      prev += 4;
-      row  += 4;
-      rb   -= 4;
-   }
-}
-
-#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
-#endif /* READ */
diff --git a/third_party/libpng16/contrib/intel/intel_init.c b/third_party/libpng16/contrib/intel/intel_init.c
deleted file mode 100644
index 328e90e..0000000
--- a/third_party/libpng16/contrib/intel/intel_init.c
+++ /dev/null
@@ -1,54 +0,0 @@
-
-/* intel_init.c - SSE2 optimized filter functions
- *
- * Copyright (c) 2016 Google, Inc.
- * Written by Mike Klein and Matt Sarett
- * Derived from arm/arm_init.c, which was
- * Copyright (c) 2014,2016 Glenn Randers-Pehrson
- *
- * Last changed in libpng 1.6.22 [May 26, 2016]
- *
- * This code is released under the libpng license.
- * For conditions of distribution and use, see the disclaimer
- * and license in png.h
- */
-
-#include "../../pngpriv.h"
-
-#ifdef PNG_READ_SUPPORTED
-#if PNG_INTEL_SSE_IMPLEMENTATION > 0
-
-void
-png_init_filter_functions_sse2(png_structp pp, unsigned int bpp)
-{
-   /* The techniques used to implement each of these filters in SSE operate on
-    * one pixel at a time.
-    * So they generally speed up 3bpp images about 3x, 4bpp images about 4x.
-    * They can scale up to 6 and 8 bpp images and down to 2 bpp images,
-    * but they'd not likely have any benefit for 1bpp images.
-    * Most of these can be implemented using only MMX and 64-bit registers,
-    * but they end up a bit slower than using the equally-ubiquitous SSE2.
-   */
-   png_debug(1, "in png_init_filter_functions_sse2");
-   if (bpp == 3)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-         png_read_filter_row_paeth3_sse2;
-   }
-   else if (bpp == 4)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-          png_read_filter_row_paeth4_sse2;
-   }
-
-   /* No need optimize PNG_FILTER_VALUE_UP.  The compiler should
-    * autovectorize.
-    */
-}
-
-#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
-#endif /* PNG_READ_SUPPORTED */
diff --git a/third_party/libpng16/contrib/intel/intel_sse.patch b/third_party/libpng16/contrib/intel/intel_sse.patch
deleted file mode 100644
index d9d02bb..0000000
--- a/third_party/libpng16/contrib/intel/intel_sse.patch
+++ /dev/null
@@ -1,164 +0,0 @@
-diff --git libpng-1.6.22-orig/configure.ac libpng-1.6.22/configure.ac
---- libpng-1.6.22-orig/configure.ac	2016-05-25 18:59:10.000000000 -0400
-+++ libpng-1.6.22/configure.ac	2016-05-25 19:48:10.631751170 -0400
-@@ -341,16 +341,50 @@ AC_ARG_ENABLE([arm-neon],
- 
- AM_CONDITIONAL([PNG_ARM_NEON],
-    [test "$enable_arm_neon" != 'no' &&
-     case "$host_cpu" in
-       arm*|aarch64*) :;;
-       *)    test "$enable_arm_neon" != '';;
-     esac])
- 
-+# INTEL
-+# =====
-+#
-+# INTEL SSE (SIMD) support.
-+
-+AC_ARG_ENABLE([intel-sse],
-+   AS_HELP_STRING([[[--enable-intel-sse]]],
-+      [Enable Intel SSE optimizations: =no/off, yes/on:]
-+      [no/off: disable the optimizations;]
-+      [yes/on: enable the optimizations.]
-+      [If not specified: determined by the compiler.]),
-+   [case "$enableval" in
-+      no|off)
-+         # disable the default enabling:
-+         AC_DEFINE([PNG_INTEL_SSE_OPT], [0],
-+                   [Disable Intel SSE optimizations])
-+         # Prevent inclusion of the assembler files below:
-+         enable_intel_sse=no;;
-+      yes|on)
-+         AC_DEFINE([PNG_INTEL_SSE_OPT], [1],
-+                   [Enable Intel SSE optimizations]);;
-+      *)
-+         AC_MSG_ERROR([--enable-intel-sse=${enable_intel_sse}: invalid value])
-+   esac])
-+
-+# Add Intel specific files to all builds where the host_cpu is Intel ('x86*')
-+# or where Intel optimizations were explicitly requested (this allows a
-+# fallback if a future host CPU does not match 'x86*')
-+AM_CONDITIONAL([PNG_INTEL_SSE],
-+   [test "$enable_intel_sse" != 'no' &&
-+    case "$host_cpu" in
-+      i?86|x86_64) :;;
-+      *)    test "$enable_intel_sse" != '';;
-+    esac])
- AC_MSG_NOTICE([[Extra options for compiler: $PNG_COPTS]])
- 
- # Config files, substituting as above
- AC_CONFIG_FILES([Makefile libpng.pc:libpng.pc.in])
- AC_CONFIG_FILES([libpng-config:libpng-config.in],
-    [chmod +x libpng-config])
- 
- AC_OUTPUT
-diff --git libpng-1.6.22-orig/Makefile.am libpng-1.6.22/Makefile.am
---- libpng-1.6.22-orig/Makefile.am	2016-05-17 18:15:12.000000000 -0400
-+++ libpng-1.6.22/Makefile.am	2016-05-25 19:48:10.631751170 -0400
-@@ -89,16 +89,20 @@ libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SO
- 	pngset.c pngtrans.c pngwio.c pngwrite.c pngwtran.c pngwutil.c\
- 	png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h pngusr.dfa
- 
- if PNG_ARM_NEON
- libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES += arm/arm_init.c\
- 	arm/filter_neon.S arm/filter_neon_intrinsics.c
- endif
- 
-+if PNG_INTEL_SSE
-+libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES += contrib/intel/intel_init.c\
-+    contrib/intel/filter_sse2_intrinsics.c
-+endif
- nodist_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES = pnglibconf.h
- 
- libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_LDFLAGS = -no-undefined -export-dynamic \
- 	-version-number @PNGLIB_MAJOR@@PNGLIB_MINOR@:@PNGLIB_RELEASE@:0
- 
- if HAVE_LD_VERSION_SCRIPT
- #   Versioned symbols and restricted exports
- if HAVE_SOLARIS_LD
-diff --git libpng-1.6.22-orig/pngpriv.h libpng-1.6.22/pngpriv.h
---- libpng-1.6.22-orig/pngpriv.h	2016-05-25 18:59:10.000000000 -0400
-+++ libpng-1.6.22/pngpriv.h	2016-05-25 19:48:10.635751171 -0400
-@@ -177,16 +177,52 @@
- #  endif /* !PNG_ARM_NEON_IMPLEMENTATION */
- 
- #  ifndef PNG_ARM_NEON_IMPLEMENTATION
-       /* Use the intrinsics code by default. */
- #     define PNG_ARM_NEON_IMPLEMENTATION 1
- #  endif
- #endif /* PNG_ARM_NEON_OPT > 0 */
- 
-+#ifndef PNG_INTEL_SSE_OPT
-+#   ifdef PNG_INTEL_SSE
-+      /* Only check for SSE if the build configuration has been modified to
-+       * enable SSE optimizations.  This means that these optimizations will
-+       * be off by default.  See contrib/intel for more details.
-+       */
-+#     if defined(__SSE4_1__) || defined(__AVX__) || defined(__SSSE3__) || \
-+       defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
-+       (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
-+#         define PNG_INTEL_SSE_OPT 1
-+#      endif
-+#   endif
-+#endif
-+
-+#if PNG_INTEL_SSE_OPT > 0
-+#   ifndef PNG_INTEL_SSE_IMPLEMENTATION
-+#      if defined(__SSE4_1__) || defined(__AVX__)
-+          /* We are not actually using AVX, but checking for AVX is the best
-+             way we can detect SSE4.1 and SSSE3 on MSVC.
-+          */
-+#         define PNG_INTEL_SSE_IMPLEMENTATION 3
-+#      elif defined(__SSSE3__)
-+#         define PNG_INTEL_SSE_IMPLEMENTATION 2
-+#      elif defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
-+       (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
-+#         define PNG_INTEL_SSE_IMPLEMENTATION 1
-+#      else
-+#         define PNG_INTEL_SSE_IMPLEMENTATION 0
-+#      endif
-+#   endif
-+
-+#   if PNG_INTEL_SSE_IMPLEMENTATION > 0
-+#      define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_sse2
-+#   endif
-+#endif
-+
- /* Is this a build of a DLL where compilation of the object modules requires
-  * different preprocessor settings to those required for a simple library?  If
-  * so PNG_BUILD_DLL must be set.
-  *
-  * If libpng is used inside a DLL but that DLL does not export the libpng APIs
-  * PNG_BUILD_DLL must not be set.  To avoid the code below kicking in build a
-  * static library of libpng then link the DLL against that.
-  */
-@@ -1184,16 +1220,29 @@ PNG_INTERNAL_FUNCTION(void,png_read_filt
-     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
- PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop
-     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
- PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop
-     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
- PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
-     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
- 
-+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop
-+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop
-+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_sse2,(png_row_infop
-+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_sse2,(png_row_infop
-+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_sse2,(png_row_infop
-+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
-+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-+
- /* Choose the best filter to use and filter the row data */
- PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
-     png_row_infop row_info),PNG_EMPTY);
- 
- #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
- PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
-    png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
-    /* Read 'avail_out' bytes of data from the IDAT stream.  If the output buffer
diff --git a/third_party/libpng16/intel/filter_sse2_intrinsics.c b/third_party/libpng16/intel/filter_sse2_intrinsics.c
new file mode 100644
index 0000000..f52aaa8
--- /dev/null
+++ b/third_party/libpng16/intel/filter_sse2_intrinsics.c
@@ -0,0 +1,391 @@
+
+/* filter_sse2_intrinsics.c - SSE2 optimized filter functions
+ *
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2016-2017 Glenn Randers-Pehrson
+ * Written by Mike Klein and Matt Sarett
+ * Derived from arm/filter_neon_intrinsics.c
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "../pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+#if PNG_INTEL_SSE_IMPLEMENTATION > 0
+
+#include <immintrin.h>
+
+/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
+ * They're positioned like this:
+ *    prev:  c b
+ *    row:   a d
+ * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
+ * whichever of a, b, or c is closest to p=a+b-c.
+ */
+
+static __m128i load4(const void* p) {
+   int tmp;
+   memcpy(&tmp, p, sizeof(tmp));
+   return _mm_cvtsi32_si128(tmp);
+}
+
+static void store4(void* p, __m128i v) {
+   int tmp = _mm_cvtsi128_si32(v);
+   memcpy(p, &tmp, sizeof(int));
+}
+
+static __m128i load3(const void* p) {
+   png_uint_32 tmp = 0;
+   memcpy(&tmp, p, 3);
+   return _mm_cvtsi32_si128(tmp);
+}
+
+static void store3(void* p, __m128i v) {
+   int tmp = _mm_cvtsi128_si32(v);
+   memcpy(p, &tmp, 3);
+}
+
+void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev)
+{
+   /* The Sub filter predicts each pixel as the previous pixel, a.
+    * There is no pixel to the left of the first pixel.  It's encoded directly.
+    * That works with our main loop if we just say that left pixel was zero.
+    */
+   size_t rb;
+
+   __m128i a, d = _mm_setzero_si128();
+
+   png_debug(1, "in png_read_filter_row_sub3_sse2");
+
+   rb = row_info->rowbytes;
+   while (rb >= 4) {
+      a = d; d = load4(row);
+      d = _mm_add_epi8(d, a);
+      store3(row, d);
+
+      row += 3;
+      rb  -= 3;
+   }
+   if (rb > 0) {
+      a = d; d = load3(row);
+      d = _mm_add_epi8(d, a);
+      store3(row, d);
+
+      row += 3;
+      rb  -= 3;
+   }
+   PNG_UNUSED(prev)
+}
+
+void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev)
+{
+   /* The Sub filter predicts each pixel as the previous pixel, a.
+    * There is no pixel to the left of the first pixel.  It's encoded directly.
+    * That works with our main loop if we just say that left pixel was zero.
+    */
+   size_t rb;
+
+   __m128i a, d = _mm_setzero_si128();
+
+   png_debug(1, "in png_read_filter_row_sub4_sse2");
+
+   rb = row_info->rowbytes+4;
+   while (rb > 4) {
+      a = d; d = load4(row);
+      d = _mm_add_epi8(d, a);
+      store4(row, d);
+
+      row += 4;
+      rb  -= 4;
+   }
+   PNG_UNUSED(prev)
+}
+
+void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev)
+{
+   /* The Avg filter predicts each pixel as the (truncated) average of a and b.
+    * There's no pixel to the left of the first pixel.  Luckily, it's
+    * predicted to be half of the pixel above it.  So again, this works
+    * perfectly with our loop if we make sure a starts at zero.
+    */
+
+   size_t rb;
+
+   const __m128i zero = _mm_setzero_si128();
+
+   __m128i    b;
+   __m128i a, d = zero;
+
+   png_debug(1, "in png_read_filter_row_avg3_sse2");
+   rb = row_info->rowbytes;
+   while (rb >= 4) {
+      __m128i avg;
+             b = load4(prev);
+      a = d; d = load4(row );
+
+      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
+      avg = _mm_avg_epu8(a,b);
+      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
+      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
+                                            _mm_set1_epi8(1)));
+      d = _mm_add_epi8(d, avg);
+      store3(row, d);
+
+      prev += 3;
+      row  += 3;
+      rb   -= 3;
+   }
+   if (rb > 0) {
+      __m128i avg;
+             b = load3(prev);
+      a = d; d = load3(row );
+
+      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
+      avg = _mm_avg_epu8(a,b);
+      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
+      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
+                                            _mm_set1_epi8(1)));
+
+      d = _mm_add_epi8(d, avg);
+      store3(row, d);
+
+      prev += 3;
+      row  += 3;
+      rb   -= 3;
+   }
+}
+
+void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev)
+{
+   /* The Avg filter predicts each pixel as the (truncated) average of a and b.
+    * There's no pixel to the left of the first pixel.  Luckily, it's
+    * predicted to be half of the pixel above it.  So again, this works
+    * perfectly with our loop if we make sure a starts at zero.
+    */
+   size_t rb;
+   const __m128i zero = _mm_setzero_si128();
+   __m128i    b;
+   __m128i a, d = zero;
+
+   png_debug(1, "in png_read_filter_row_avg4_sse2");
+
+   rb = row_info->rowbytes+4;
+   while (rb > 4) {
+      __m128i avg;
+             b = load4(prev);
+      a = d; d = load4(row );
+
+      /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
+      avg = _mm_avg_epu8(a,b);
+      /* ...but we can fix it up by subtracting off 1 if it rounded up. */
+      avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
+                                            _mm_set1_epi8(1)));
+
+      d = _mm_add_epi8(d, avg);
+      store4(row, d);
+
+      prev += 4;
+      row  += 4;
+      rb   -= 4;
+   }
+}
+
+/* Returns |x| for 16-bit lanes. */
+static __m128i abs_i16(__m128i x) {
+#if PNG_INTEL_SSE_IMPLEMENTATION >= 2
+   return _mm_abs_epi16(x);
+#else
+   /* Read this all as, return x<0 ? -x : x.
+   * To negate two's complement, you flip all the bits then add 1.
+    */
+   __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());
+
+   /* Flip negative lanes. */
+   x = _mm_xor_si128(x, is_negative);
+
+   /* +1 to negative lanes, else +0. */
+   x = _mm_sub_epi16(x, is_negative);
+   return x;
+#endif
+}
+
+/* Bytewise c ? t : e. */
+static __m128i if_then_else(__m128i c, __m128i t, __m128i e) {
+#if PNG_INTEL_SSE_IMPLEMENTATION >= 3
+   return _mm_blendv_epi8(e,t,c);
+#else
+   return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));
+#endif
+}
+
+void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev)
+{
+   /* Paeth tries to predict pixel d using the pixel to the left of it, a,
+    * and two pixels from the previous row, b and c:
+    *   prev: c b
+    *   row:  a d
+    * The Paeth function predicts d to be whichever of a, b, or c is nearest to
+    * p=a+b-c.
+    *
+    * The first pixel has no left context, and so uses an Up filter, p = b.
+    * This works naturally with our main loop's p = a+b-c if we force a and c
+    * to zero.
+    * Here we zero b and d, which become c and a respectively at the start of
+    * the loop.
+    */
+   size_t rb;
+   const __m128i zero = _mm_setzero_si128();
+   __m128i c, b = zero,
+           a, d = zero;
+
+   png_debug(1, "in png_read_filter_row_paeth3_sse2");
+
+   rb = row_info->rowbytes;
+   while (rb >= 4) {
+      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
+       * intermediates.
+       */
+      __m128i pa,pb,pc,smallest,nearest;
+      c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
+      a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
+
+      /* (p-a) == (a+b-c - a) == (b-c) */
+   
+      pa = _mm_sub_epi16(b,c);
+
+      /* (p-b) == (a+b-c - b) == (a-c) */
+      pb = _mm_sub_epi16(a,c);
+
+      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
+      pc = _mm_add_epi16(pa,pb);
+
+      pa = abs_i16(pa);  /* |p-a| */
+      pb = abs_i16(pb);  /* |p-b| */
+      pc = abs_i16(pc);  /* |p-c| */
+
+      smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
+
+      /* Paeth breaks ties favoring a over b over c. */
+      nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
+                 if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
+                                                             c));
+
+      /* Note `_epi8`: we need addition to wrap modulo 255. */
+      d = _mm_add_epi8(d, nearest);
+      store3(row, _mm_packus_epi16(d,d));
+
+      prev += 3;
+      row  += 3;
+      rb   -= 3;
+   }
+   if (rb > 0) {
+      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
+       * intermediates.
+       */
+      __m128i pa,pb,pc,smallest,nearest;
+      c = b; b = _mm_unpacklo_epi8(load3(prev), zero);
+      a = d; d = _mm_unpacklo_epi8(load3(row ), zero);
+
+      /* (p-a) == (a+b-c - a) == (b-c) */
+      pa = _mm_sub_epi16(b,c);
+
+      /* (p-b) == (a+b-c - b) == (a-c) */
+      pb = _mm_sub_epi16(a,c);
+
+      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
+      pc = _mm_add_epi16(pa,pb);
+
+      pa = abs_i16(pa);  /* |p-a| */
+      pb = abs_i16(pb);  /* |p-b| */
+      pc = abs_i16(pc);  /* |p-c| */
+
+      smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
+
+      /* Paeth breaks ties favoring a over b over c. */
+      nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
+                         if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
+                                                                     c));
+
+      /* Note `_epi8`: we need addition to wrap modulo 255. */
+      d = _mm_add_epi8(d, nearest);
+      store3(row, _mm_packus_epi16(d,d));
+
+      prev += 3;
+      row  += 3;
+      rb   -= 3;
+   }
+}
+
+void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev)
+{
+   /* Paeth tries to predict pixel d using the pixel to the left of it, a,
+    * and two pixels from the previous row, b and c:
+    *   prev: c b
+    *   row:  a d
+    * The Paeth function predicts d to be whichever of a, b, or c is nearest to
+    * p=a+b-c.
+    *
+    * The first pixel has no left context, and so uses an Up filter, p = b.
+    * This works naturally with our main loop's p = a+b-c if we force a and c
+    * to zero.
+    * Here we zero b and d, which become c and a respectively at the start of
+    * the loop.
+    */
+   size_t rb;
+   const __m128i zero = _mm_setzero_si128();
+   __m128i pa,pb,pc,smallest,nearest;
+   __m128i c, b = zero,
+           a, d = zero;
+
+   png_debug(1, "in png_read_filter_row_paeth4_sse2");
+
+   rb = row_info->rowbytes+4;
+   while (rb > 4) {
+      /* It's easiest to do this math (particularly, deal with pc) with 16-bit
+       * intermediates.
+       */
+      c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
+      a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
+
+      /* (p-a) == (a+b-c - a) == (b-c) */
+      pa = _mm_sub_epi16(b,c);
+
+      /* (p-b) == (a+b-c - b) == (a-c) */
+      pb = _mm_sub_epi16(a,c);
+
+      /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
+      pc = _mm_add_epi16(pa,pb);
+
+      pa = abs_i16(pa);  /* |p-a| */
+      pb = abs_i16(pb);  /* |p-b| */
+      pc = abs_i16(pc);  /* |p-c| */
+
+      smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
+
+      /* Paeth breaks ties favoring a over b over c. */
+      nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
+                         if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
+                                                                     c));
+
+      /* Note `_epi8`: we need addition to wrap modulo 255. */
+      d = _mm_add_epi8(d, nearest);
+      store4(row, _mm_packus_epi16(d,d));
+
+      prev += 4;
+      row  += 4;
+      rb   -= 4;
+   }
+}
+
+#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
+#endif /* READ */
diff --git a/third_party/libpng16/intel/intel_init.c b/third_party/libpng16/intel/intel_init.c
new file mode 100644
index 0000000..2f8168b
--- /dev/null
+++ b/third_party/libpng16/intel/intel_init.c
@@ -0,0 +1,52 @@
+
+/* intel_init.c - SSE2 optimized filter functions
+ *
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2016-2017 Glenn Randers-Pehrson
+ * Written by Mike Klein and Matt Sarett, Google, Inc.
+ * Derived from arm/arm_init.c
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "../pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+#if PNG_INTEL_SSE_IMPLEMENTATION > 0
+
+void
+png_init_filter_functions_sse2(png_structp pp, unsigned int bpp)
+{
+   /* The techniques used to implement each of these filters in SSE operate on
+    * one pixel at a time.
+    * So they generally speed up 3bpp images about 3x, 4bpp images about 4x.
+    * They can scale up to 6 and 8 bpp images and down to 2 bpp images,
+    * but they'd not likely have any benefit for 1bpp images.
+    * Most of these can be implemented using only MMX and 64-bit registers,
+    * but they end up a bit slower than using the equally-ubiquitous SSE2.
+   */
+   png_debug(1, "in png_init_filter_functions_sse2");
+   if (bpp == 3)
+   {
+      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2;
+      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2;
+      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
+         png_read_filter_row_paeth3_sse2;
+   }
+   else if (bpp == 4)
+   {
+      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2;
+      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2;
+      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
+          png_read_filter_row_paeth4_sse2;
+   }
+
+   /* No need optimize PNG_FILTER_VALUE_UP.  The compiler should
+    * autovectorize.
+    */
+}
+
+#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
+#endif /* PNG_READ_SUPPORTED */
diff --git a/third_party/libpng16/png.c b/third_party/libpng16/png.c
index 01d8d9b..ddf7a2f 100644
--- a/third_party/libpng16/png.c
+++ b/third_party/libpng16/png.c
@@ -1,10 +1,10 @@
 
 /* png.c - location for general purpose libpng functions
  *
- * Last changed in libpng 1.6.19 [November 12, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -14,7 +14,27 @@
 #include "pngpriv.h"
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_22 Your_png_h_is_not_version_1_6_22;
+typedef png_libpng_version_1_6_37 Your_png_h_is_not_version_1_6_37;
+
+#ifdef __GNUC__
+/* The version tests may need to be added to, but the problem warning has
+ * consistently been fixed in GCC versions which obtain wide-spread release.
+ * The problem is that many versions of GCC rearrange comparison expressions in
+ * the optimizer in such a way that the results of the comparison will change
+ * if signed integer overflow occurs.  Such comparisons are not permitted in
+ * ANSI C90, however GCC isn't clever enough to work out that that do not occur
+ * below in png_ascii_from_fp and png_muldiv, so it produces a warning with
+ * -Wextra.  Unfortunately this is highly dependent on the optimizer and the
+ * machine architecture so the warning comes and goes unpredictably and is
+ * impossible to "fix", even were that a good idea.
+ */
+#if __GNUC__ == 7 && __GNUC_MINOR__ == 1
+#define GCC_STRICT_OVERFLOW 1
+#endif /* GNU 7.1.x */
+#endif /* GNU */
+#ifndef GCC_STRICT_OVERFLOW
+#define GCC_STRICT_OVERFLOW 0
+#endif
 
 /* Tells libpng that we have already handled the first "num_bytes" bytes
  * of the PNG file signature.  If the PNG data is embedded into another
@@ -51,7 +71,7 @@
  * PNG signature (this is the same behavior as strcmp, memcmp, etc).
  */
 int PNGAPI
-png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)
+png_sig_cmp(png_const_bytep sig, size_t start, size_t num_to_check)
 {
    png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
 
@@ -85,7 +105,7 @@
    if (items >= (~(png_alloc_size_t)0)/size)
    {
       png_warning (png_voidcast(png_structrp, png_ptr),
-         "Potential overflow in png_zalloc()");
+          "Potential overflow in png_zalloc()");
       return NULL;
    }
 
@@ -116,7 +136,7 @@
  * trouble of calculating it.
  */
 void /* PRIVATE */
-png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length)
+png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, size_t length)
 {
    int need_crc = 1;
 
@@ -172,10 +192,10 @@
 int
 png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
 {
-     /* Libpng versions 1.0.0 and later are binary compatible if the version
-      * string matches through the second '.'; we must recompile any
-      * applications that use any older library version.
-      */
+   /* Libpng versions 1.0.0 and later are binary compatible if the version
+    * string matches through the second '.'; we must recompile any
+    * applications that use any older library version.
+    */
 
    if (user_png_ver != NULL)
    {
@@ -297,7 +317,7 @@
          if (png_user_version_check(&create_struct, user_png_ver) != 0)
          {
             png_structrp png_ptr = png_voidcast(png_structrp,
-               png_malloc_warn(&create_struct, (sizeof *png_ptr)));
+                png_malloc_warn(&create_struct, (sizeof *png_ptr)));
 
             if (png_ptr != NULL)
             {
@@ -346,7 +366,7 @@
     * has always been done in 'example.c'.
     */
    info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr,
-      (sizeof *info_ptr)));
+       (sizeof *info_ptr)));
 
    if (info_ptr != NULL)
       memset(info_ptr, 0, (sizeof *info_ptr));
@@ -401,8 +421,8 @@
  * those cases where it does anything other than a memset.
  */
 PNG_FUNCTION(void,PNGAPI
-png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size),
-   PNG_DEPRECATED)
+png_info_init_3,(png_infopp ptr_ptr, size_t png_info_struct_size),
+    PNG_DEPRECATED)
 {
    png_inforp info_ptr = *ptr_ptr;
 
@@ -417,7 +437,7 @@
       /* The following line is why this API should not be used: */
       free(info_ptr);
       info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL,
-         (sizeof *info_ptr)));
+          (sizeof *info_ptr)));
       if (info_ptr == NULL)
          return;
       *ptr_ptr = info_ptr;
@@ -430,7 +450,7 @@
 /* The following API is not called internally */
 void PNGAPI
 png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr,
-   int freer, png_uint_32 mask)
+    int freer, png_uint_32 mask)
 {
    png_debug(1, "in png_data_freer");
 
@@ -449,7 +469,7 @@
 
 void PNGAPI
 png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,
-   int num)
+    int num)
 {
    png_debug(1, "in png_free_data");
 
@@ -458,7 +478,7 @@
 
 #ifdef PNG_TEXT_SUPPORTED
    /* Free text item num or (if num == -1) all text items */
-   if (info_ptr->text != 0 &&
+   if (info_ptr->text != NULL &&
        ((mask & PNG_FREE_TEXT) & info_ptr->free_me) != 0)
    {
       if (num != -1)
@@ -477,6 +497,7 @@
          png_free(png_ptr, info_ptr->text);
          info_ptr->text = NULL;
          info_ptr->num_text = 0;
+         info_ptr->max_text = 0;
       }
    }
 #endif
@@ -541,7 +562,7 @@
 
 #ifdef PNG_sPLT_SUPPORTED
    /* Free a given sPLT entry, or (if num == -1) all sPLT entries */
-   if (info_ptr->splt_palettes != 0 &&
+   if (info_ptr->splt_palettes != NULL &&
        ((mask & PNG_FREE_SPLT) & info_ptr->free_me) != 0)
    {
       if (num != -1)
@@ -571,7 +592,7 @@
 #endif
 
 #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-   if (info_ptr->unknown_chunks != 0 &&
+   if (info_ptr->unknown_chunks != NULL &&
        ((mask & PNG_FREE_UNKN) & info_ptr->free_me) != 0)
    {
       if (num != -1)
@@ -594,6 +615,26 @@
    }
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+   /* Free any eXIf entry */
+   if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0)
+   {
+# ifdef PNG_READ_eXIf_SUPPORTED
+      if (info_ptr->eXIf_buf)
+      {
+         png_free(png_ptr, info_ptr->eXIf_buf);
+         info_ptr->eXIf_buf = NULL;
+      }
+# endif
+      if (info_ptr->exif)
+      {
+         png_free(png_ptr, info_ptr->exif);
+         info_ptr->exif = NULL;
+      }
+      info_ptr->valid &= ~PNG_INFO_eXIf;
+   }
+#endif
+
 #ifdef PNG_hIST_SUPPORTED
    /* Free any hIST entry */
    if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0)
@@ -617,7 +658,7 @@
    /* Free any image bits attached to the info structure */
    if (((mask & PNG_FREE_ROWS) & info_ptr->free_me) != 0)
    {
-      if (info_ptr->row_pointers != 0)
+      if (info_ptr->row_pointers != NULL)
       {
          png_uint_32 row;
          for (row = 0; row < info_ptr->height; row++)
@@ -684,7 +725,7 @@
 void PNGAPI
 png_save_int_32(png_bytep buf, png_int_32 i)
 {
-   png_save_uint_32(buf, i);
+   png_save_uint_32(buf, (png_uint_32)i);
 }
 #  endif
 
@@ -695,7 +736,7 @@
 int PNGAPI
 png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime)
 {
-   static PNG_CONST char short_months[12][4] =
+   static const char short_months[12][4] =
         {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 
@@ -773,20 +814,14 @@
 #ifdef PNG_STRING_COPYRIGHT
    return PNG_STRING_COPYRIGHT
 #else
-#  ifdef __STDC__
    return PNG_STRING_NEWLINE \
-      "libpng version 1.6.22 - May 26, 2016" PNG_STRING_NEWLINE \
-      "Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson" \
+      "libpng version 1.6.37" PNG_STRING_NEWLINE \
+      "Copyright (c) 2018-2019 Cosmin Truta" PNG_STRING_NEWLINE \
+      "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
       PNG_STRING_NEWLINE \
       "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
       "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
       PNG_STRING_NEWLINE;
-#  else
-   return "libpng version 1.6.22 - May 26, 2016\
-      Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson\
-      Copyright (c) 1996-1997 Andreas Dilger\
-      Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
-#  endif
 #endif
 }
 
@@ -901,7 +936,7 @@
 
    /* The code is the fifth byte after each four byte string.  Historically this
     * code was always searched from the end of the list, this is no longer
-    * necessary because the 'set' routine handles duplicate entries correcty.
+    * necessary because the 'set' routine handles duplicate entries correctly.
     */
    do /* num_chunk_list > 0, so at least one */
    {
@@ -1033,7 +1068,7 @@
 #ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */
 static int
 png_colorspace_check_gamma(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_fixed_point gAMA, int from)
+    png_colorspacerp colorspace, png_fixed_point gAMA, int from)
    /* This is called to check a new gamma value against an existing one.  The
     * routine returns false if the new gamma value should not be written.
     *
@@ -1047,7 +1082,7 @@
    png_fixed_point gtest;
 
    if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-      (png_muldiv(&gtest, colorspace->gamma, PNG_FP_1, gAMA) == 0  ||
+       (png_muldiv(&gtest, colorspace->gamma, PNG_FP_1, gAMA) == 0  ||
       png_gamma_significant(gtest) != 0))
    {
       /* Either this is an sRGB image, in which case the calculated gamma
@@ -1059,7 +1094,7 @@
       if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2)
       {
          png_chunk_report(png_ptr, "gamma value does not match sRGB",
-            PNG_CHUNK_ERROR);
+             PNG_CHUNK_ERROR);
          /* Do not overwrite an sRGB value */
          return from == 2;
       }
@@ -1067,7 +1102,7 @@
       else /* sRGB tag not involved */
       {
          png_chunk_report(png_ptr, "gamma value does not match libpng estimate",
-            PNG_CHUNK_WARNING);
+             PNG_CHUNK_WARNING);
          return from == 1;
       }
    }
@@ -1077,10 +1112,10 @@
 
 void /* PRIVATE */
 png_colorspace_set_gamma(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_fixed_point gAMA)
+    png_colorspacerp colorspace, png_fixed_point gAMA)
 {
    /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't
-    * occur.  Since the fixed point representation is asymetrical it is
+    * occur.  Since the fixed point representation is asymmetrical it is
     * possible for 1/gamma to overflow the limit of 21474 and this means the
     * gamma value must be at least 5/100000 and hence at most 20000.0.  For
     * safety the limits here are a little narrower.  The values are 0.00016 to
@@ -1635,8 +1670,8 @@
 
 static int
 png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,
-   int preferred)
+    png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,
+    int preferred)
 {
    if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
       return 0;
@@ -1683,7 +1718,7 @@
 
 int /* PRIVATE */
 png_colorspace_set_chromaticities(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, const png_xy *xy, int preferred)
+    png_colorspacerp colorspace, const png_xy *xy, int preferred)
 {
    /* We must check the end points to ensure they are reasonable - in the past
     * color management systems have crashed as a result of getting bogus
@@ -1697,7 +1732,7 @@
    {
       case 0: /* success */
          return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ,
-            preferred);
+             preferred);
 
       case 1:
          /* We can't invert the chromaticities so we can't produce value XYZ
@@ -1720,7 +1755,7 @@
 
 int /* PRIVATE */
 png_colorspace_set_endpoints(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
+    png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
 {
    png_XYZ XYZ = *XYZ_in;
    png_xy xy;
@@ -1729,7 +1764,7 @@
    {
       case 0:
          return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ,
-            preferred);
+             preferred);
 
       case 1:
          /* End points are invalid. */
@@ -1786,7 +1821,7 @@
 
 static int
 png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
-   png_const_charp name, png_alloc_size_t value, png_const_charp reason)
+    png_const_charp name, png_alloc_size_t value, png_const_charp reason)
 {
    size_t pos;
    char message[196]; /* see below for calculation */
@@ -1811,8 +1846,8 @@
          char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/
 
          pos = png_safecat(message, (sizeof message), pos,
-            png_format_number(number, number+(sizeof number),
-               PNG_NUMBER_FORMAT_x, value));
+             png_format_number(number, number+(sizeof number),
+             PNG_NUMBER_FORMAT_x, value));
          pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/
       }
 #  endif
@@ -1826,7 +1861,7 @@
     * application errors the PNG won't be written.)
     */
    png_chunk_report(png_ptr, message,
-      (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
+       (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
 
    return 0;
 }
@@ -1835,7 +1870,7 @@
 #ifdef PNG_sRGB_SUPPORTED
 int /* PRIVATE */
 png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
-   int intent)
+    int intent)
 {
    /* sRGB sets known gamma, end points and (from the chunk) intent. */
    /* IMPORTANT: these are not necessarily the values found in an ICC profile
@@ -1872,12 +1907,12 @@
     */
    if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST)
       return png_icc_profile_error(png_ptr, colorspace, "sRGB",
-         (unsigned)intent, "invalid sRGB rendering intent");
+          (png_alloc_size_t)intent, "invalid sRGB rendering intent");
 
    if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 &&
-      colorspace->rendering_intent != intent)
+       colorspace->rendering_intent != intent)
       return png_icc_profile_error(png_ptr, colorspace, "sRGB",
-         (unsigned)intent, "inconsistent rendering intents");
+         (png_alloc_size_t)intent, "inconsistent rendering intents");
 
    if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0)
    {
@@ -1889,8 +1924,8 @@
     * warn but overwrite the value with the correct one.
     */
    if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 &&
-      !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
-         100))
+       !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
+       100))
       png_chunk_report(png_ptr, "cHRM chunk does not match sRGB",
          PNG_CHUNK_ERROR);
 
@@ -1898,7 +1933,7 @@
     * returns true when the 'from' argument corresponds to sRGB (2).
     */
    (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE,
-      2/*from sRGB*/);
+       2/*from sRGB*/);
 
    /* intent: bugs in GCC force 'int' to be used as the parameter type. */
    colorspace->rendering_intent = (png_uint_16)intent;
@@ -1933,12 +1968,11 @@
 
 static int /* bool */
 icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
-   png_const_charp name, png_uint_32 profile_length)
+    png_const_charp name, png_uint_32 profile_length)
 {
    if (profile_length < 132)
       return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-         "too short");
-
+          "too short");
    return 1;
 }
 
@@ -1978,8 +2012,8 @@
 
 int /* PRIVATE */
 png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
-   png_const_charp name, png_uint_32 profile_length,
-   png_const_bytep profile/* first 132 bytes only */, int color_type)
+    png_const_charp name, png_uint_32 profile_length,
+    png_const_bytep profile/* first 132 bytes only */, int color_type)
 {
    png_uint_32 temp;
 
@@ -1991,18 +2025,18 @@
    temp = png_get_uint_32(profile);
    if (temp != profile_length)
       return png_icc_profile_error(png_ptr, colorspace, name, temp,
-         "length does not match profile");
+          "length does not match profile");
 
    temp = (png_uint_32) (*(profile+8));
    if (temp > 3 && (profile_length & 3))
       return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
-         "invalid length");
+          "invalid length");
 
    temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */
    if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */
       profile_length < 132+12*temp) /* truncated tag table */
       return png_icc_profile_error(png_ptr, colorspace, name, temp,
-         "tag count too large");
+          "tag count too large");
 
    /* The 'intent' must be valid or we can't store it, ICC limits the intent to
     * 16 bits.
@@ -2010,14 +2044,14 @@
    temp = png_get_uint_32(profile+64);
    if (temp >= 0xffff) /* The ICC limit */
       return png_icc_profile_error(png_ptr, colorspace, name, temp,
-         "invalid rendering intent");
+          "invalid rendering intent");
 
    /* This is just a warning because the profile may be valid in future
     * versions.
     */
    if (temp >= PNG_sRGB_INTENT_LAST)
       (void)png_icc_profile_error(png_ptr, NULL, name, temp,
-         "intent outside defined range");
+          "intent outside defined range");
 
    /* At this point the tag table can't be checked because it hasn't necessarily
     * been loaded; however, various header fields can be checked.  These checks
@@ -2027,14 +2061,14 @@
     */
 
    /* Data checks (could be skipped).  These checks must be independent of the
-    * version number; however, the version number doesn't accomodate changes in
+    * version number; however, the version number doesn't accommodate changes in
     * the header fields (just the known tags and the interpretation of the
     * data.)
     */
    temp = png_get_uint_32(profile+36); /* signature 'ascp' */
    if (temp != 0x61637370)
       return png_icc_profile_error(png_ptr, colorspace, name, temp,
-         "invalid signature");
+          "invalid signature");
 
    /* Currently the PCS illuminant/adopted white point (the computational
     * white point) are required to be D50,
@@ -2045,7 +2079,7 @@
     */
    if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0)
       (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/,
-         "PCS illuminant is not D50");
+          "PCS illuminant is not D50");
 
    /* The PNG spec requires this:
     * "If the iCCP chunk is present, the image samples conform to the colour
@@ -2073,18 +2107,18 @@
       case 0x52474220: /* 'RGB ' */
          if ((color_type & PNG_COLOR_MASK_COLOR) == 0)
             return png_icc_profile_error(png_ptr, colorspace, name, temp,
-               "RGB color space not permitted on grayscale PNG");
+                "RGB color space not permitted on grayscale PNG");
          break;
 
       case 0x47524159: /* 'GRAY' */
          if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
             return png_icc_profile_error(png_ptr, colorspace, name, temp,
-               "Gray color space not permitted on RGB PNG");
+                "Gray color space not permitted on RGB PNG");
          break;
 
       default:
          return png_icc_profile_error(png_ptr, colorspace, name, temp,
-            "invalid ICC profile color space");
+             "invalid ICC profile color space");
    }
 
    /* It is up to the application to check that the profile class matches the
@@ -2109,7 +2143,7 @@
       case 0x61627374: /* 'abst' */
          /* May not be embedded in an image */
          return png_icc_profile_error(png_ptr, colorspace, name, temp,
-            "invalid embedded Abstract ICC profile");
+             "invalid embedded Abstract ICC profile");
 
       case 0x6c696e6b: /* 'link' */
          /* DeviceLink profiles cannot be interpreted in a non-device specific
@@ -2119,7 +2153,7 @@
           * PNG.
           */
          return png_icc_profile_error(png_ptr, colorspace, name, temp,
-            "unexpected DeviceLink ICC profile class");
+             "unexpected DeviceLink ICC profile class");
 
       case 0x6e6d636c: /* 'nmcl' */
          /* A NamedColor profile is also device specific, however it doesn't
@@ -2127,7 +2161,7 @@
           * certainly it will fail the tests below.
           */
          (void)png_icc_profile_error(png_ptr, NULL, name, temp,
-            "unexpected NamedColor ICC profile class");
+             "unexpected NamedColor ICC profile class");
          break;
 
       default:
@@ -2137,7 +2171,7 @@
           * understood profiles.
           */
          (void)png_icc_profile_error(png_ptr, NULL, name, temp,
-            "unrecognized ICC profile class");
+             "unrecognized ICC profile class");
          break;
    }
 
@@ -2153,7 +2187,7 @@
 
       default:
          return png_icc_profile_error(png_ptr, colorspace, name, temp,
-            "unexpected ICC PCS encoding");
+             "unexpected ICC PCS encoding");
    }
 
    return 1;
@@ -2161,8 +2195,8 @@
 
 int /* PRIVATE */
 png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
-   png_const_charp name, png_uint_32 profile_length,
-   png_const_bytep profile /* header plus whole tag table */)
+    png_const_charp name, png_uint_32 profile_length,
+    png_const_bytep profile /* header plus whole tag table */)
 {
    png_uint_32 tag_count = png_get_uint_32(profile+128);
    png_uint_32 itag;
@@ -2183,22 +2217,23 @@
        * being in range.  All defined tag types have an 8 byte header - a 4 byte
        * type signature then 0.
        */
-      if ((tag_start & 3) != 0)
-      {
-         /* CNHP730S.icc shipped with Microsoft Windows 64 violates this, it is
-          * only a warning here because libpng does not care about the
-          * alignment.
-          */
-         (void)png_icc_profile_error(png_ptr, NULL, name, tag_id,
-            "ICC profile tag start not a multiple of 4");
-      }
 
       /* This is a hard error; potentially it can cause read outside the
        * profile.
        */
       if (tag_start > profile_length || tag_length > profile_length - tag_start)
          return png_icc_profile_error(png_ptr, colorspace, name, tag_id,
-            "ICC profile tag outside profile");
+             "ICC profile tag outside profile");
+
+      if ((tag_start & 3) != 0)
+      {
+         /* CNHP730S.icc shipped with Microsoft Windows 64 violates this; it is
+          * only a warning here because libpng does not care about the
+          * alignment.
+          */
+         (void)png_icc_profile_error(png_ptr, NULL, name, tag_id,
+             "ICC profile tag start not a multiple of 4");
+      }
    }
 
    return 1; /* success, maybe with warnings */
@@ -2226,22 +2261,22 @@
     */
    /* adler32, crc32, MD5[4], intent, date, length, file-name */
    PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9,
-      PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
-      "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
+       PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
+       "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
 
    /* ICC sRGB v2 perceptual no black-compensation: */
    PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21,
-      PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
-      "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
+       PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
+       "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
 
    PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae,
-      PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
-      "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
+       PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
+       "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
 
    /* ICC sRGB v4 perceptual */
    PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812,
-      PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
-      "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
+       PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
+       "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
 
    /* The following profiles have no known MD5 checksum. If there is a match
     * on the (empty) MD5 the other fields are used to attempt a match and
@@ -2249,8 +2284,8 @@
     * which suggests that they were also made by Hewlett Packard.
     */
    PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce,
-      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
-      "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
+       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
+       "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
 
    /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not
     * match the D50 PCS illuminant in the header (it is in fact the D65 values,
@@ -2260,17 +2295,17 @@
     * chromaticAdaptationTag.
     */
    PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552,
-      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
-      "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
+       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
+       "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
 
    PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,
-      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
-      "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
+       PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
+       "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
 };
 
 static int
 png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
-   png_const_bytep profile, uLong adler)
+    png_const_bytep profile, uLong adler)
 {
    /* The quick check is to verify just the MD5 signature and trust the
     * rest of the data.  Because the profile has already been verified for
@@ -2354,7 +2389,7 @@
                       * which is made irrelevant by this error.
                       */
                      png_chunk_report(png_ptr, "known incorrect sRGB profile",
-                        PNG_CHUNK_ERROR);
+                         PNG_CHUNK_ERROR);
                   }
 
                   /* Warn that this being done; this isn't even an error since
@@ -2364,8 +2399,8 @@
                   else if (png_sRGB_checks[i].have_md5 == 0)
                   {
                      png_chunk_report(png_ptr,
-                        "out-of-date sRGB profile with no signature",
-                        PNG_CHUNK_WARNING);
+                         "out-of-date sRGB profile with no signature",
+                         PNG_CHUNK_WARNING);
                   }
 
                   return 1+png_sRGB_checks[i].is_broken;
@@ -2388,38 +2423,36 @@
 
    return 0; /* no match */
 }
-#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */
 
 void /* PRIVATE */
 png_icc_set_sRGB(png_const_structrp png_ptr,
-   png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
+    png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
 {
    /* Is this profile one of the known ICC sRGB profiles?  If it is, just set
     * the sRGB information.
     */
-#if PNG_sRGB_PROFILE_CHECKS >= 0
    if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0)
-#endif
       (void)png_colorspace_set_sRGB(png_ptr, colorspace,
          (int)/*already checked*/png_get_uint_32(profile+64));
 }
+#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */
 #endif /* sRGB */
 
 int /* PRIVATE */
 png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,
-   png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,
-   int color_type)
+    png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,
+    int color_type)
 {
    if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
       return 0;
 
    if (icc_check_length(png_ptr, colorspace, name, profile_length) != 0 &&
        png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,
-          color_type) != 0 &&
+           color_type) != 0 &&
        png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,
-          profile) != 0)
+           profile) != 0)
    {
-#     ifdef PNG_sRGB_SUPPORTED
+#     if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
          /* If no sRGB support, don't try storing sRGB information */
          png_icc_set_sRGB(png_ptr, colorspace, profile, 0);
 #     endif
@@ -2478,7 +2511,7 @@
          /* Check for an internal error. */
          if (r+g+b != 32768)
             png_error(png_ptr,
-               "internal error handling cHRM coefficients");
+                "internal error handling cHRM coefficients");
 
          else
          {
@@ -2505,7 +2538,7 @@
 static int /* PRIVATE */
 png_gt(size_t a, size_t b)
 {
-    return a > b;
+   return a > b;
 }
 #else
 #   define png_gt(a,b) ((a) > (b))
@@ -2513,9 +2546,9 @@
 
 void /* PRIVATE */
 png_check_IHDR(png_const_structrp png_ptr,
-   png_uint_32 width, png_uint_32 height, int bit_depth,
-   int color_type, int interlace_type, int compression_type,
-   int filter_type)
+    png_uint_32 width, png_uint_32 height, int bit_depth,
+    int color_type, int interlace_type, int compression_type,
+    int filter_type)
 {
    int error = 0;
 
@@ -2532,7 +2565,7 @@
       error = 1;
    }
 
-   if (png_gt(((width + 7) & (~7)),
+   if (png_gt(((width + 7) & (~7U)),
        ((PNG_SIZE_MAX
            - 48        /* big_row_buf hack */
            - 1)        /* filter byte */
@@ -2669,7 +2702,7 @@
 
 #if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
 /* ASCII to fp functions */
-/* Check an ASCII formated floating point value, see the more detailed
+/* Check an ASCII formatted floating point value, see the more detailed
  * comments in pngpriv.h
  */
 /* The following is used internally to preserve the sticky flags */
@@ -2677,11 +2710,11 @@
 #define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY))
 
 int /* PRIVATE */
-png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
-   png_size_tp whereami)
+png_check_fp_number(png_const_charp string, size_t size, int *statep,
+    png_size_tp whereami)
 {
    int state = *statep;
-   png_size_t i = *whereami;
+   size_t i = *whereami;
 
    while (i < size)
    {
@@ -2804,10 +2837,10 @@
 
 /* The same but for a complete string. */
 int
-png_check_fp_string(png_const_charp string, png_size_t size)
+png_check_fp_string(png_const_charp string, size_t size)
 {
    int        state=0;
-   png_size_t char_index=0;
+   size_t char_index=0;
 
    if (png_check_fp_number(string, size, &state, &char_index) != 0 &&
       (char_index == size || string[char_index] == 0))
@@ -2834,7 +2867,7 @@
    if (power < 0)
    {
       if (power < DBL_MIN_10_EXP) return 0;
-      recip = 1, power = -power;
+      recip = 1; power = -power;
    }
 
    if (power > 0)
@@ -2859,8 +2892,16 @@
 /* Function to format a floating point value in ASCII with a given
  * precision.
  */
+#if GCC_STRICT_OVERFLOW
+#pragma GCC diagnostic push
+/* The problem arises below with exp_b10, which can never overflow because it
+ * comes, originally, from frexp and is therefore limited to a range which is
+ * typically +/-710 (log2(DBL_MAX)/log2(DBL_MIN)).
+ */
+#pragma GCC diagnostic warning "-Wstrict-overflow=2"
+#endif /* GCC_STRICT_OVERFLOW */
 void /* PRIVATE */
-png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
+png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, size_t size,
     double fp, unsigned int precision)
 {
    /* We use standard functions from math.h, but not printf because
@@ -2912,7 +2953,9 @@
             double test = png_pow10(exp_b10+1);
 
             if (test <= DBL_MAX)
-               ++exp_b10, base = test;
+            {
+               ++exp_b10; base = test;
+            }
 
             else
                break;
@@ -2926,7 +2969,10 @@
           * test on DBL_MAX above.
           */
          fp /= base;
-         while (fp >= 1) fp /= 10, ++exp_b10;
+         while (fp >= 1)
+         {
+            fp /= 10; ++exp_b10;
+         }
 
          /* Because of the code above fp may, at this point, be
           * less than .1, this is ok because the code below can
@@ -2943,7 +2989,7 @@
              */
             if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */
             {
-               czero = -exp_b10; /* PLUS 2 digits: TOTAL 3 */
+               czero = 0U-exp_b10; /* PLUS 2 digits: TOTAL 3 */
                exp_b10 = 0;      /* Dot added below before first output. */
             }
             else
@@ -2977,7 +3023,7 @@
                      /* Rounding up to 10, handle that here. */
                      if (czero > 0)
                      {
-                        --czero, d = 1;
+                        --czero; d = 1;
                         if (cdigits == 0) --clead;
                      }
                      else
@@ -2991,7 +3037,7 @@
 
                            else if (ch == 46)
                            {
-                              ch = *--ascii, ++size;
+                              ch = *--ascii; ++size;
                               /* Advance exp_b10 to '1', so that the
                                * decimal point happens after the
                                * previous digit.
@@ -3018,7 +3064,9 @@
                               int ch = *--ascii;
 
                               if (ch == 46)
-                                 ++size, exp_b10 = 1;
+                              {
+                                 ++size; exp_b10 = 1;
+                              }
 
                               /* Else lost a leading zero, so 'exp_b10' is
                                * still ok at (-1)
@@ -3054,21 +3102,26 @@
                       */
                      if (exp_b10 != (-1))
                      {
-                        if (exp_b10 == 0) *ascii++ = 46, --size;
+                        if (exp_b10 == 0)
+                        {
+                           *ascii++ = 46; --size;
+                        }
                         /* PLUS 1: TOTAL 4 */
                         --exp_b10;
                      }
-                     *ascii++ = 48, --czero;
+                     *ascii++ = 48; --czero;
                   }
 
                   if (exp_b10 != (-1))
                   {
                      if (exp_b10 == 0)
-                        *ascii++ = 46, --size; /* counted above */
+                     {
+                        *ascii++ = 46; --size; /* counted above */
+                     }
 
                      --exp_b10;
                   }
-                  *ascii++ = (char)(48 + (int)d), ++cdigits;
+                  *ascii++ = (char)(48 + (int)d); ++cdigits;
                }
             }
             while (cdigits+czero < precision+clead && fp > DBL_MIN);
@@ -3076,11 +3129,11 @@
             /* The total output count (max) is now 4+precision */
 
             /* Check for an exponent, if we don't need one we are
-             * done and just need to terminate the string.  At
-             * this point exp_b10==(-1) is effectively if flag - it got
-             * to '-1' because of the decrement after outputting
-             * the decimal point above (the exponent required is
-             * *not* -1!)
+             * done and just need to terminate the string.  At this
+             * point, exp_b10==(-1) is effectively a flag: it got
+             * to '-1' because of the decrement, after outputting
+             * the decimal point above. (The exponent required is
+             * *not* -1.)
              */
             if (exp_b10 >= (-1) && exp_b10 <= 2)
             {
@@ -3091,7 +3144,7 @@
                 * zeros were *not* output, so this doesn't increase
                 * the output count.
                 */
-               while (--exp_b10 >= 0) *ascii++ = 48;
+               while (exp_b10-- > 0) *ascii++ = 48;
 
                *ascii = 0;
 
@@ -3109,7 +3162,7 @@
              */
             size -= cdigits;
 
-            *ascii++ = 69, --size;    /* 'E': PLUS 1 TOTAL 2+precision */
+            *ascii++ = 69; --size;    /* 'E': PLUS 1 TOTAL 2+precision */
 
             /* The following use of an unsigned temporary avoids ambiguities in
              * the signed arithmetic on exp_b10 and permits GCC at least to do
@@ -3120,12 +3173,12 @@
 
                if (exp_b10 < 0)
                {
-                  *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */
-                  uexp_b10 = -exp_b10;
+                  *ascii++ = 45; --size; /* '-': PLUS 1 TOTAL 3+precision */
+                  uexp_b10 = 0U-exp_b10;
                }
 
                else
-                  uexp_b10 = exp_b10;
+                  uexp_b10 = 0U+exp_b10;
 
                cdigits = 0;
 
@@ -3168,6 +3221,9 @@
    /* Here on buffer too small. */
    png_error(png_ptr, "ASCII conversion buffer too small");
 }
+#if GCC_STRICT_OVERFLOW
+#pragma GCC diagnostic pop
+#endif /* GCC_STRICT_OVERFLOW */
 
 #  endif /* FLOATING_POINT */
 
@@ -3176,7 +3232,7 @@
  */
 void /* PRIVATE */
 png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii,
-    png_size_t size, png_fixed_point fp)
+    size_t size, png_fixed_point fp)
 {
    /* Require space for 10 decimal digits, a decimal point, a minus sign and a
     * trailing \0, 13 characters:
@@ -3187,9 +3243,11 @@
 
       /* Avoid overflow here on the minimum integer. */
       if (fp < 0)
-         *ascii++ = 45, num = -fp;
+      {
+         *ascii++ = 45; num = (png_uint_32)(-fp);
+      }
       else
-         num = fp;
+         num = (png_uint_32)fp;
 
       if (num <= 0x80000000) /* else overflowed */
       {
@@ -3225,7 +3283,10 @@
                 * then ndigits digits to first:
                 */
                i = 5;
-               while (ndigits < i) *ascii++ = 48, --i;
+               while (ndigits < i)
+               {
+                  *ascii++ = 48; --i;
+               }
                while (ndigits >= first) *ascii++ = digits[--ndigits];
                /* Don't output the trailing zeros! */
             }
@@ -3276,6 +3337,15 @@
  * the nearest .00001).  Overflow and divide by zero are signalled in
  * the result, a boolean - true on success, false on overflow.
  */
+#if GCC_STRICT_OVERFLOW /* from above */
+/* It is not obvious which comparison below gets optimized in such a way that
+ * signed overflow would change the result; looking through the code does not
+ * reveal any tests which have the form GCC complains about, so presumably the
+ * optimizer is moving an add or subtract into the 'if' somewhere.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wstrict-overflow=2"
+#endif /* GCC_STRICT_OVERFLOW */
 int
 png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
     png_int_32 divisor)
@@ -3390,6 +3460,9 @@
 
    return 0;
 }
+#if GCC_STRICT_OVERFLOW
+#pragma GCC diagnostic pop
+#endif /* GCC_STRICT_OVERFLOW */
 #endif /* READ_GAMMA || INCH_CONVERSIONS */
 
 #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
@@ -3683,7 +3756,7 @@
  * of getting this accuracy in practice.
  *
  * To deal with this the following exp() function works out the exponent of the
- * frational part of the logarithm by using an accurate 32-bit value from the
+ * fractional part of the logarithm by using an accurate 32-bit value from the
  * top four fractional bits then multiplying in the remaining bits.
  */
 static const png_uint_32
@@ -3841,25 +3914,25 @@
 {
    if (value > 0 && value < 65535)
    {
-#     ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
-         /* The same (unsigned int)->(double) constraints apply here as above,
-          * however in this case the (unsigned int) to (int) conversion can
-          * overflow on an ANSI-C90 compliant system so the cast needs to ensure
-          * that this is not possible.
-          */
-         double r = floor(65535*pow((png_int_32)value/65535.,
-                     gamma_val*.00001)+.5);
-         return (png_uint_16)r;
-#     else
-         png_int_32 lg2 = png_log16bit(value);
-         png_fixed_point res;
+# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+      /* The same (unsigned int)->(double) constraints apply here as above,
+       * however in this case the (unsigned int) to (int) conversion can
+       * overflow on an ANSI-C90 compliant system so the cast needs to ensure
+       * that this is not possible.
+       */
+      double r = floor(65535*pow((png_int_32)value/65535.,
+          gamma_val*.00001)+.5);
+      return (png_uint_16)r;
+# else
+      png_int_32 lg2 = png_log16bit(value);
+      png_fixed_point res;
 
-         if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0)
-            return png_exp16bit(res);
+      if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0)
+         return png_exp16bit(res);
 
-         /* Overflow. */
-         value = 0;
-#     endif
+      /* Overflow. */
+      value = 0;
+# endif
    }
 
    return (png_uint_16)value;
@@ -3898,18 +3971,18 @@
  */
 static void
 png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable,
-   PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)
+    unsigned int shift, png_fixed_point gamma_val)
 {
    /* Various values derived from 'shift': */
-   PNG_CONST unsigned int num = 1U << (8U - shift);
+   unsigned int num = 1U << (8U - shift);
 #ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
    /* CSE the division and work round wacky GCC warnings (see the comments
     * in png_gamma_8bit_correct for where these come from.)
     */
-   PNG_CONST double fmax = 1./(((png_int_32)1 << (16U - shift))-1);
+   double fmax = 1.0 / (((png_int_32)1 << (16U - shift)) - 1);
 #endif
-   PNG_CONST unsigned int max = (1U << (16U - shift))-1U;
-   PNG_CONST unsigned int max_by_2 = 1U << (15U-shift);
+   unsigned int max = (1U << (16U - shift)) - 1U;
+   unsigned int max_by_2 = 1U << (15U - shift);
    unsigned int i;
 
    png_uint_16pp table = *ptable =
@@ -3975,10 +4048,10 @@
  */
 static void
 png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable,
-   PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)
+    unsigned int shift, png_fixed_point gamma_val)
 {
-   PNG_CONST unsigned int num = 1U << (8U - shift);
-   PNG_CONST unsigned int max = (1U << (16U - shift))-1U;
+   unsigned int num = 1U << (8U - shift);
+   unsigned int max = (1U << (16U - shift))-1U;
    unsigned int i;
    png_uint_32 last;
 
@@ -4043,7 +4116,7 @@
  */
 static void
 png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable,
-   PNG_CONST png_fixed_point gamma_val)
+    png_fixed_point gamma_val)
 {
    unsigned int i;
    png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256);
@@ -4123,131 +4196,133 @@
 void /* PRIVATE */
 png_build_gamma_table(png_structrp png_ptr, int bit_depth)
 {
-  png_debug(1, "in png_build_gamma_table");
+   png_debug(1, "in png_build_gamma_table");
 
-  /* Remove any existing table; this copes with multiple calls to
-   * png_read_update_info.  The warning is because building the gamma tables
-   * multiple times is a performance hit - it's harmless but the ability to call
-   * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible
-   * to warn if the app introduces such a hit.
-   */
-  if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL)
-  {
-    png_warning(png_ptr, "gamma table being rebuilt");
-    png_destroy_gamma_table(png_ptr);
-  }
+   /* Remove any existing table; this copes with multiple calls to
+    * png_read_update_info. The warning is because building the gamma tables
+    * multiple times is a performance hit - it's harmless but the ability to
+    * call png_read_update_info() multiple times is new in 1.5.6 so it seems
+    * sensible to warn if the app introduces such a hit.
+    */
+   if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL)
+   {
+      png_warning(png_ptr, "gamma table being rebuilt");
+      png_destroy_gamma_table(png_ptr);
+   }
 
-  if (bit_depth <= 8)
-  {
-     png_build_8bit_table(png_ptr, &png_ptr->gamma_table,
-         png_ptr->screen_gamma > 0 ?  png_reciprocal2(png_ptr->colorspace.gamma,
-         png_ptr->screen_gamma) : PNG_FP_1);
+   if (bit_depth <= 8)
+   {
+      png_build_8bit_table(png_ptr, &png_ptr->gamma_table,
+          png_ptr->screen_gamma > 0 ?
+          png_reciprocal2(png_ptr->colorspace.gamma,
+          png_ptr->screen_gamma) : PNG_FP_1);
 
 #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
    defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
    defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
-     if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
-     {
-        png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,
-            png_reciprocal(png_ptr->colorspace.gamma));
+      if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
+      {
+         png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,
+             png_reciprocal(png_ptr->colorspace.gamma));
 
-        png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1,
-            png_ptr->screen_gamma > 0 ?  png_reciprocal(png_ptr->screen_gamma) :
-            png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
-     }
+         png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1,
+             png_ptr->screen_gamma > 0 ?
+             png_reciprocal(png_ptr->screen_gamma) :
+             png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
+      }
 #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-  }
+   }
 #ifdef PNG_16BIT_SUPPORTED
-  else
-  {
-     png_byte shift, sig_bit;
+   else
+   {
+      png_byte shift, sig_bit;
 
-     if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
-     {
-        sig_bit = png_ptr->sig_bit.red;
+      if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+      {
+         sig_bit = png_ptr->sig_bit.red;
 
-        if (png_ptr->sig_bit.green > sig_bit)
-           sig_bit = png_ptr->sig_bit.green;
+         if (png_ptr->sig_bit.green > sig_bit)
+            sig_bit = png_ptr->sig_bit.green;
 
-        if (png_ptr->sig_bit.blue > sig_bit)
-           sig_bit = png_ptr->sig_bit.blue;
-     }
-     else
-        sig_bit = png_ptr->sig_bit.gray;
+         if (png_ptr->sig_bit.blue > sig_bit)
+            sig_bit = png_ptr->sig_bit.blue;
+      }
+      else
+         sig_bit = png_ptr->sig_bit.gray;
 
-     /* 16-bit gamma code uses this equation:
-      *
-      *   ov = table[(iv & 0xff) >> gamma_shift][iv >> 8]
-      *
-      * Where 'iv' is the input color value and 'ov' is the output value -
-      * pow(iv, gamma).
-      *
-      * Thus the gamma table consists of up to 256 256-entry tables.  The table
-      * is selected by the (8-gamma_shift) most significant of the low 8 bits of
-      * the color value then indexed by the upper 8 bits:
-      *
-      *   table[low bits][high 8 bits]
-      *
-      * So the table 'n' corresponds to all those 'iv' of:
-      *
-      *   <all high 8-bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>
-      *
-      */
-     if (sig_bit > 0 && sig_bit < 16U)
-        /* shift == insignificant bits */
-        shift = (png_byte)((16U - sig_bit) & 0xff);
+      /* 16-bit gamma code uses this equation:
+       *
+       *   ov = table[(iv & 0xff) >> gamma_shift][iv >> 8]
+       *
+       * Where 'iv' is the input color value and 'ov' is the output value -
+       * pow(iv, gamma).
+       *
+       * Thus the gamma table consists of up to 256 256-entry tables.  The table
+       * is selected by the (8-gamma_shift) most significant of the low 8 bits
+       * of the color value then indexed by the upper 8 bits:
+       *
+       *   table[low bits][high 8 bits]
+       *
+       * So the table 'n' corresponds to all those 'iv' of:
+       *
+       *   <all high 8-bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>
+       *
+       */
+      if (sig_bit > 0 && sig_bit < 16U)
+         /* shift == insignificant bits */
+         shift = (png_byte)((16U - sig_bit) & 0xff);
 
-     else
-        shift = 0; /* keep all 16 bits */
+      else
+         shift = 0; /* keep all 16 bits */
 
-     if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
-     {
-        /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively
-         * the significant bits in the *input* when the output will
-         * eventually be 8 bits.  By default it is 11.
-         */
-        if (shift < (16U - PNG_MAX_GAMMA_8))
-           shift = (16U - PNG_MAX_GAMMA_8);
-     }
+      if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
+      {
+         /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively
+          * the significant bits in the *input* when the output will
+          * eventually be 8 bits.  By default it is 11.
+          */
+         if (shift < (16U - PNG_MAX_GAMMA_8))
+            shift = (16U - PNG_MAX_GAMMA_8);
+      }
 
-     if (shift > 8U)
-        shift = 8U; /* Guarantees at least one table! */
+      if (shift > 8U)
+         shift = 8U; /* Guarantees at least one table! */
 
-     png_ptr->gamma_shift = shift;
+      png_ptr->gamma_shift = shift;
 
-     /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now
-      * PNG_COMPOSE).  This effectively smashed the background calculation for
-      * 16-bit output because the 8-bit table assumes the result will be reduced
-      * to 8 bits.
-      */
-     if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
-         png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
-         png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma,
-         png_ptr->screen_gamma) : PNG_FP_1);
+      /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now
+       * PNG_COMPOSE).  This effectively smashed the background calculation for
+       * 16-bit output because the 8-bit table assumes the result will be
+       * reduced to 8 bits.
+       */
+      if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
+          png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
+          png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma,
+          png_ptr->screen_gamma) : PNG_FP_1);
 
-     else
-         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,
-         png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma,
-         png_ptr->screen_gamma) : PNG_FP_1);
+      else
+          png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,
+          png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma,
+          png_ptr->screen_gamma) : PNG_FP_1);
 
 #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
    defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
    defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
-     if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
-     {
-        png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,
-            png_reciprocal(png_ptr->colorspace.gamma));
+      if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
+      {
+         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,
+             png_reciprocal(png_ptr->colorspace.gamma));
 
-        /* Notice that the '16 from 1' table should be full precision, however
-         * the lookup on this table still uses gamma_shift, so it can't be.
-         * TODO: fix this.
-         */
-        png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift,
-            png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
-            png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
-     }
+         /* Notice that the '16 from 1' table should be full precision, however
+          * the lookup on this table still uses gamma_shift, so it can't be.
+          * TODO: fix this.
+          */
+         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift,
+             png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
+             png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
+      }
 #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-  }
+   }
 #endif /* 16BIT */
 }
 #endif /* READ_GAMMA */
@@ -4260,13 +4335,13 @@
    if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT &&
       (option & 1) == 0)
    {
-      int mask = 3 << option;
-      int setting = (2 + (onoff != 0)) << option;
-      int current = png_ptr->options;
+      png_uint_32 mask = 3U << option;
+      png_uint_32 setting = (2U + (onoff != 0)) << option;
+      png_uint_32 current = png_ptr->options;
 
-      png_ptr->options = (png_byte)(((current & ~mask) | setting) & 0xff);
+      png_ptr->options = (png_uint_32)((current & ~mask) | setting);
 
-      return (current & mask) >> option;
+      return (int)(current & mask) >> option;
    }
 
    return PNG_OPTION_INVALID;
@@ -4278,7 +4353,7 @@
    defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
 /* sRGB conversion tables; these are machine generated with the code in
  * contrib/tools/makesRGB.c.  The actual sRGB transfer curve defined in the
- * specification (see the article at http://en.wikipedia.org/wiki/SRGB)
+ * specification (see the article at https://en.wikipedia.org/wiki/SRGB)
  * is used, not the gamma=1/2.2 approximation use elsewhere in libpng.
  * The sRGB to linear table is exact (to the nearest 16-bit linear fraction).
  * The inverse (linear to sRGB) table has accuracies as follows:
@@ -4514,8 +4589,7 @@
    if (image != NULL && image->opaque != NULL &&
       image->opaque->error_buf == NULL)
    {
-      /* Ignore errors here: */
-      (void)png_safe_execute(image, png_image_free_function, image);
+      png_image_free_function(image);
       image->opaque = NULL;
    }
 }
diff --git a/third_party/libpng16/png.h b/third_party/libpng16/png.h
index 08c039d..139eb0d 100644
--- a/third_party/libpng16/png.h
+++ b/third_party/libpng16/png.h
@@ -1,72 +1,105 @@
 
 /* png.h - header file for PNG reference library
  *
- * libpng version 1.6.22, May 26, 2016
+ * libpng version 1.6.37 - April 14, 2019
  *
- * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
- * This code is released under the libpng license (See LICENSE, below)
+ * This code is released under the libpng license. (See LICENSE, below.)
  *
  * Authors and maintainers:
  *   libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
  *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
- *   libpng versions 0.97, January 1998, through 1.6.22, May 26, 2016:
- *     Glenn Randers-Pehrson.
+ *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
+ *     Glenn Randers-Pehrson
+ *   libpng versions 1.6.36, December 2018, through 1.6.37, April 2019:
+ *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
 
 /*
- * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+ * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
+ * =========================================
  *
- * If you modify libpng you may insert additional notices immediately following
- * this sentence.
+ * PNG Reference Library License version 2
+ * ---------------------------------------
  *
- * This code is released under the libpng license.
+ *  * Copyright (c) 1995-2019 The PNG Reference Library Authors.
+ *  * Copyright (c) 2018-2019 Cosmin Truta.
+ *  * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
+ *  * Copyright (c) 1996-1997 Andreas Dilger.
+ *  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
- * Some files in the "contrib" directory and some configure-generated
- * files that are distributed with libpng have other copyright owners and
- * are released under other open source licenses.
+ * The software is supplied "as is", without warranty of any kind,
+ * express or implied, including, without limitation, the warranties
+ * of merchantability, fitness for a particular purpose, title, and
+ * non-infringement.  In no event shall the Copyright owners, or
+ * anyone distributing the software, be liable for any damages or
+ * other liability, whether in contract, tort or otherwise, arising
+ * from, out of, or in connection with the software, or the use or
+ * other dealings in the software, even if advised of the possibility
+ * of such damage.
  *
- * libpng versions 1.0.7, July 1, 2000 through 1.6.22, May 26, 2016 are
- * Copyright (c) 2000-2002, 2004, 2006-2016 Glenn Randers-Pehrson, are
+ * Permission is hereby granted to use, copy, modify, and distribute
+ * this software, or portions hereof, for any purpose, without fee,
+ * subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you
+ *     must not claim that you wrote the original software.  If you
+ *     use this software in a product, an acknowledgment in the product
+ *     documentation would be appreciated, but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must
+ *     not be misrepresented as being the original software.
+ *
+ *  3. This Copyright notice may not be removed or altered from any
+ *     source or altered source distribution.
+ *
+ *
+ * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35)
+ * -----------------------------------------------------------------------
+ *
+ * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are
+ * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are
  * derived from libpng-1.0.6, and are distributed according to the same
  * disclaimer and license as libpng-1.0.6 with the following individuals
  * added to the list of Contributing Authors:
  *
- *    Simon-Pierre Cadieux
- *    Eric S. Raymond
- *    Mans Rullgard
- *    Cosmin Truta
- *    Gilles Vollant
- *    James Yu
+ *     Simon-Pierre Cadieux
+ *     Eric S. Raymond
+ *     Mans Rullgard
+ *     Cosmin Truta
+ *     Gilles Vollant
+ *     James Yu
+ *     Mandar Sahastrabuddhe
+ *     Google Inc.
+ *     Vadim Barkov
  *
  * and with the following additions to the disclaimer:
  *
- *    There is no warranty against interference with your enjoyment of the
- *    library or against infringement.  There is no warranty that our
- *    efforts or the library will fulfill any of your particular purposes
- *    or needs.  This library is provided with all faults, and the entire
- *    risk of satisfactory quality, performance, accuracy, and effort is with
- *    the user.
+ *     There is no warranty against interference with your enjoyment of
+ *     the library or against infringement.  There is no warranty that our
+ *     efforts or the library will fulfill any of your particular purposes
+ *     or needs.  This library is provided with all faults, and the entire
+ *     risk of satisfactory quality, performance, accuracy, and effort is
+ *     with the user.
  *
- * Some files in the "contrib" directory have other copyright owners and
+ * Some files in the "contrib" directory and some configure-generated
+ * files that are distributed with libpng have other copyright owners, and
  * are released under other open source licenses.
  *
- *
  * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
  * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
  * libpng-0.96, and are distributed according to the same disclaimer and
- * license as libpng-0.96, with the following individuals added to the list
- * of Contributing Authors:
+ * license as libpng-0.96, with the following individuals added to the
+ * list of Contributing Authors:
  *
- *    Tom Lane
- *    Glenn Randers-Pehrson
- *    Willem van Schaik
- *
- * Some files in the "scripts" directory have different copyright owners
- * but are also released under this license.
+ *     Tom Lane
+ *     Glenn Randers-Pehrson
+ *     Willem van Schaik
  *
  * libpng versions 0.89, June 1996, through 0.96, May 1997, are
  * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
@@ -74,14 +107,14 @@
  * libpng-0.88, with the following individuals added to the list of
  * Contributing Authors:
  *
- *    John Bowler
- *    Kevin Bracey
- *    Sam Bushell
- *    Magnus Holmgren
- *    Greg Roelofs
- *    Tom Tanner
+ *     John Bowler
+ *     Kevin Bracey
+ *     Sam Bushell
+ *     Magnus Holmgren
+ *     Greg Roelofs
+ *     Tom Tanner
  *
- * Some files in the "scripts" directory have other copyright owners
+ * Some files in the "scripts" directory have other copyright owners,
  * but are released under this license.
  *
  * libpng versions 0.5, May 1995, through 0.88, January 1996, are
@@ -90,62 +123,49 @@
  * For the purposes of this copyright and license, "Contributing Authors"
  * is defined as the following set of individuals:
  *
- *    Andreas Dilger
- *    Dave Martindale
- *    Guy Eric Schalnat
- *    Paul Schmidt
- *    Tim Wegner
+ *     Andreas Dilger
+ *     Dave Martindale
+ *     Guy Eric Schalnat
+ *     Paul Schmidt
+ *     Tim Wegner
  *
- * The PNG Reference Library is supplied "AS IS".  The Contributing Authors
- * and Group 42, Inc. disclaim all warranties, expressed or implied,
- * including, without limitation, the warranties of merchantability and of
- * fitness for any purpose.  The Contributing Authors and Group 42, Inc.
- * assume no liability for direct, indirect, incidental, special, exemplary,
- * or consequential damages, which may result from the use of the PNG
- * Reference Library, even if advised of the possibility of such damage.
+ * The PNG Reference Library is supplied "AS IS".  The Contributing
+ * Authors and Group 42, Inc. disclaim all warranties, expressed or
+ * implied, including, without limitation, the warranties of
+ * merchantability and of fitness for any purpose.  The Contributing
+ * Authors and Group 42, Inc. assume no liability for direct, indirect,
+ * incidental, special, exemplary, or consequential damages, which may
+ * result from the use of the PNG Reference Library, even if advised of
+ * the possibility of such damage.
  *
  * Permission is hereby granted to use, copy, modify, and distribute this
  * source code, or portions hereof, for any purpose, without fee, subject
  * to the following restrictions:
  *
- *   1. The origin of this source code must not be misrepresented.
+ *  1. The origin of this source code must not be misrepresented.
  *
- *   2. Altered versions must be plainly marked as such and must not
- *      be misrepresented as being the original source.
+ *  2. Altered versions must be plainly marked as such and must not
+ *     be misrepresented as being the original source.
  *
- *   3. This Copyright notice may not be removed or altered from any
- *      source or altered source distribution.
+ *  3. This Copyright notice may not be removed or altered from any
+ *     source or altered source distribution.
  *
- * The Contributing Authors and Group 42, Inc. specifically permit, without
- * fee, and encourage the use of this source code as a component to
- * supporting the PNG file format in commercial products.  If you use this
- * source code in a product, acknowledgment is not required but would be
- * appreciated.
+ * The Contributing Authors and Group 42, Inc. specifically permit,
+ * without fee, and encourage the use of this source code as a component
+ * to supporting the PNG file format in commercial products.  If you use
+ * this source code in a product, acknowledgment is not required but would
+ * be appreciated.
  *
  * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE.
  *
- * TRADEMARK:
+ * TRADEMARK
+ * =========
  *
- * The name "libpng" has not been registered by the Copyright owner
+ * The name "libpng" has not been registered by the Copyright owners
  * as a trademark in any jurisdiction.  However, because libpng has
  * been distributed and maintained world-wide, continually since 1995,
- * the Copyright owner claims "common-law trademark protection" in any
+ * the Copyright owners claim "common-law trademark protection" in any
  * jurisdiction where common-law trademark is recognized.
- *
- * OSI CERTIFICATION:
- *
- * Libpng is OSI Certified Open Source Software.  OSI Certified Open Source is
- * a certification mark of the Open Source Initiative. OSI has not addressed
- * the additional disclaimers inserted at version 1.0.7.
- *
- * EXPORT CONTROL:
- *
- * The Copyright owner believes that the Export Control Classification
- * Number (ECCN) for libpng is EAR99, which means not subject to export
- * controls or International Traffic in Arms Regulations (ITAR) because
- * it is open source, publicly available software, that does not contain
- * any encryption software.  See the EAR, paragraphs 734.3(b)(3) and
- * 734.7(b).
  */
 
 /*
@@ -211,23 +231,25 @@
  *    1.0.7rc1-2               1    10007  2.1.0.7rc1-2 (binary compatible)
  *    1.0.7                    1    10007  (still compatible)
  *    ...
- *    1.0.19                  10    10019  10.so.0.19[.0]
+ *    1.0.69                  10    10069  10.so.0.69[.0]
  *    ...
- *    1.2.56                  13    10256  12.so.0.56[.0]
+ *    1.2.59                  13    10259  12.so.0.59[.0]
  *    ...
- *    1.5.27                  15    10527  15.so.15.27[.0]
+ *    1.4.20                  14    10420  14.so.0.20[.0]
  *    ...
- *    1.6.22                  16    10622  16.so.16.22[.0]
+ *    1.5.30                  15    10530  15.so.15.30[.0]
+ *    ...
+ *    1.6.37                  16    10637  16.so.16.37[.0]
  *
- *    Henceforth the source version will match the shared-library major
- *    and minor numbers; the shared-library major version number will be
- *    used for changes in backward compatibility, as it is intended.  The
- *    PNG_LIBPNG_VER macro, which is not used within libpng but is available
- *    for applications, is an unsigned integer of the form xyyzz corresponding
- *    to the source version x.y.z (leading zeros in y and z).  Beta versions
- *    were given the previous public release number plus a letter, until
- *    version 1.0.6j; from then on they were given the upcoming public
- *    release number plus "betaNN" or "rcNN".
+ *    Henceforth the source version will match the shared-library major and
+ *    minor numbers; the shared-library major version number will be used for
+ *    changes in backward compatibility, as it is intended.
+ *    The PNG_LIBPNG_VER macro, which is not used within libpng but is
+ *    available for applications, is an unsigned integer of the form XYYZZ
+ *    corresponding to the source version X.Y.Z (leading zeros in Y and Z).
+ *    Beta versions were given the previous public release number plus a
+ *    letter, until version 1.0.6j; from then on they were given the upcoming
+ *    public release number plus "betaNN" or "rcNN".
  *
  *    Binary incompatibility exists only when applications make direct access
  *    to the info_ptr or png_ptr members through png.h, and the compiled
@@ -237,65 +259,8 @@
  *    in binary compatibility (e.g., when a new feature is added).
  *
  * See libpng.txt or libpng.3 for more information.  The PNG specification
- * is available as a W3C Recommendation and as an ISO Specification,
- * <http://www.w3.org/TR/2003/REC-PNG-20031110/
- */
-
-/*
- * Y2K compliance in libpng:
- * =========================
- *
- *    May 26, 2016
- *
- *    Since the PNG Development group is an ad-hoc body, we can't make
- *    an official declaration.
- *
- *    This is your unofficial assurance that libpng from version 0.71 and
- *    upward through 1.6.22 are Y2K compliant.  It is my belief that
- *    earlier versions were also Y2K compliant.
- *
- *    Libpng only has two year fields.  One is a 2-byte unsigned integer
- *    that will hold years up to 65535.  The other, which is deprecated,
- *    holds the date in text format, and will hold years up to 9999.
- *
- *    The integer is
- *        "png_uint_16 year" in png_time_struct.
- *
- *    The string is
- *        "char time_buffer[29]" in png_struct.  This is no longer used
- *    in libpng-1.6.x and will be removed from libpng-1.7.0.
- *
- *    There are seven time-related functions:
- *        png.c: png_convert_to_rfc_1123_buffer() in png.c
- *          (formerly png_convert_to_rfc_1123() prior to libpng-1.5.x and
- *          png_convert_to_rfc_1152() in error prior to libpng-0.98)
- *        png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
- *        png_convert_from_time_t() in pngwrite.c
- *        png_get_tIME() in pngget.c
- *        png_handle_tIME() in pngrutil.c, called in pngread.c
- *        png_set_tIME() in pngset.c
- *        png_write_tIME() in pngwutil.c, called in pngwrite.c
- *
- *    All handle dates properly in a Y2K environment.  The
- *    png_convert_from_time_t() function calls gmtime() to convert from system
- *    clock time, which returns (year - 1900), which we properly convert to
- *    the full 4-digit year.  There is a possibility that libpng applications
- *    are not passing 4-digit years into the png_convert_to_rfc_1123_buffer()
- *    function, or that they are incorrectly passing only a 2-digit year
- *    instead of "year - 1900" into the png_convert_from_struct_tm() function,
- *    but this is not under our control.  The libpng documentation has always
- *    stated that it works with 4-digit years, and the APIs have been
- *    documented as such.
- *
- *    The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
- *    integer to hold the year, and can hold years as large as 65535.
- *
- *    zlib, upon which libpng depends, is also Y2K compliant.  It contains
- *    no date-related code.
- *
- *       Glenn Randers-Pehrson
- *       libpng maintainer
- *       PNG Development Group
+ * is available as a W3C Recommendation and as an ISO/IEC Standard; see
+ * <https://www.w3.org/TR/2003/REC-PNG-20031110/>
  */
 
 #ifndef PNG_H
@@ -313,9 +278,8 @@
  */
 
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.22"
-#define PNG_HEADER_VERSION_STRING \
-     " libpng version 1.6.22 - May 26, 2016\n"
+#define PNG_LIBPNG_VER_STRING "1.6.37"
+#define PNG_HEADER_VERSION_STRING " libpng version 1.6.37 - April 14, 2019\n"
 
 #define PNG_LIBPNG_VER_SONUM   16
 #define PNG_LIBPNG_VER_DLLNUM  16
@@ -323,12 +287,11 @@
 /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
 #define PNG_LIBPNG_VER_MAJOR   1
 #define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 22
+#define PNG_LIBPNG_VER_RELEASE 37
 
-/* This should match the numeric part of the final component of
- * PNG_LIBPNG_VER_STRING, omitting any leading zero:
+/* This should be zero for a public release, or non-zero for a
+ * development version.  [Deprecated]
  */
-
 #define PNG_LIBPNG_VER_BUILD  0
 
 /* Release Status */
@@ -348,26 +311,27 @@
 
 #define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE
 
-/* Careful here.  At one time, Guy wanted to use 082, but that would be octal.
- * We must not include leading zeros.
- * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only
- * version 1.0.0 was mis-numbered 100 instead of 10000).  From
- * version 1.0.1 it's    xxyyzz, where x=major, y=minor, z=release
+/* Careful here.  At one time, Guy wanted to use 082, but that
+ * would be octal.  We must not include leading zeros.
+ * Versions 0.7 through 1.0.0 were in the range 0 to 100 here
+ * (only version 1.0.0 was mis-numbered 100 instead of 10000).
+ * From version 1.0.1 it is:
+ * XXYYZZ, where XX=major, YY=minor, ZZ=release
  */
-#define PNG_LIBPNG_VER 10622 /* 1.6.22 */
+#define PNG_LIBPNG_VER 10637 /* 1.6.37 */
 
 /* Library configuration: these options cannot be changed after
  * the library has been built.
  */
 #ifndef PNGLCONF_H
-    /* If pnglibconf.h is missing, you can
-     * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
-     */
+/* If pnglibconf.h is missing, you can
+ * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
+ */
 #   include "pnglibconf.h"
 #endif
 
 #ifndef PNG_VERSION_INFO_ONLY
-   /* Machine specific configuration. */
+/* Machine specific configuration. */
 #  include "pngconf.h"
 #endif
 
@@ -464,7 +428,7 @@
 /* This triggers a compiler error in png.c, if png.c and png.h
  * do not agree upon the version number.
  */
-typedef char* png_libpng_version_1_6_22;
+typedef char* png_libpng_version_1_6_37;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
@@ -605,8 +569,8 @@
    png_charp key;          /* keyword, 1-79 character description of "text" */
    png_charp text;         /* comment, may be an empty string (ie "")
                               or a NULL pointer */
-   png_size_t text_length; /* length of the text string */
-   png_size_t itxt_length; /* length of the itxt string */
+   size_t text_length;     /* length of the text string */
+   size_t itxt_length;     /* length of the itxt string */
    png_charp lang;         /* language code, 0-79 characters
                               or a NULL pointer */
    png_charp lang_key;     /* keyword translated UTF-8 string, 0 or more
@@ -657,17 +621,17 @@
  */
 typedef struct png_unknown_chunk_t
 {
-    png_byte name[5]; /* Textual chunk name with '\0' terminator */
-    png_byte *data;   /* Data, should not be modified on read! */
-    png_size_t size;
+   png_byte name[5]; /* Textual chunk name with '\0' terminator */
+   png_byte *data;   /* Data, should not be modified on read! */
+   size_t size;
 
-    /* On write 'location' must be set using the flag values listed below.
-     * Notice that on read it is set by libpng however the values stored have
-     * more bits set than are listed below.  Always treat the value as a
-     * bitmask.  On write set only one bit - setting multiple bits may cause the
-     * chunk to be written in multiple places.
-     */
-    png_byte location; /* mode of operation at read time */
+   /* On write 'location' must be set using the flag values listed below.
+    * Notice that on read it is set by libpng however the values stored have
+    * more bits set than are listed below.  Always treat the value as a
+    * bitmask.  On write set only one bit - setting multiple bits may cause the
+    * chunk to be written in multiple places.
+    */
+   png_byte location; /* mode of operation at read time */
 }
 png_unknown_chunk;
 
@@ -684,7 +648,7 @@
 /* Maximum positive integer used in PNG is (2^31)-1 */
 #define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
 #define PNG_UINT_32_MAX ((png_uint_32)(-1))
-#define PNG_SIZE_MAX ((png_size_t)(-1))
+#define PNG_SIZE_MAX ((size_t)(-1))
 
 /* These are constants for fixed point values encoded in the
  * PNG specification manner (x100000)
@@ -781,6 +745,7 @@
 #define PNG_INFO_sPLT 0x2000U  /* ESR, 1.0.6 */
 #define PNG_INFO_sCAL 0x4000U  /* ESR, 1.0.6 */
 #define PNG_INFO_IDAT 0x8000U  /* ESR, 1.0.6 */
+#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */
 
 /* This is used for the transformation routines, as some of them
  * change these values for the row.  It also should enable using
@@ -789,7 +754,7 @@
 typedef struct png_row_info_struct
 {
    png_uint_32 width;    /* width of row */
-   png_size_t rowbytes;  /* number of bytes in row */
+   size_t rowbytes;      /* number of bytes in row */
    png_byte color_type;  /* color type of row */
    png_byte bit_depth;   /* bit depth of row */
    png_byte channels;    /* number of channels (1, 2, 3, or 4) */
@@ -808,7 +773,7 @@
  * expected to return the read data in the buffer.
  */
 typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp));
-typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, png_size_t));
+typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t));
 typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp));
 typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32,
     int));
@@ -945,8 +910,8 @@
  * signature, and non-zero otherwise.  Having num_to_check == 0 or
  * start > 7 will always fail (ie return non-zero).
  */
-PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, png_size_t start,
-    png_size_t num_to_check));
+PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start,
+    size_t num_to_check));
 
 /* Simple signature checking function.  This is the same as calling
  * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
@@ -965,11 +930,11 @@
     png_error_ptr warn_fn),
     PNG_ALLOCATED);
 
-PNG_EXPORT(6, png_size_t, png_get_compression_buffer_size,
+PNG_EXPORT(6, size_t, png_get_compression_buffer_size,
     (png_const_structrp png_ptr));
 
 PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
-    png_size_t size));
+    size_t size));
 
 /* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp
  * match up.
@@ -1022,7 +987,7 @@
 
 /* Write a PNG chunk - size, type, (optional) data, CRC. */
 PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep
-    chunk_name, png_const_bytep data, png_size_t length));
+    chunk_name, png_const_bytep data, size_t length));
 
 /* Write the start of a PNG chunk - length and chunk name. */
 PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr,
@@ -1030,7 +995,7 @@
 
 /* Write the data of a PNG chunk started with png_write_chunk_start(). */
 PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr,
-    png_const_bytep data, png_size_t length));
+    png_const_bytep data, size_t length));
 
 /* Finish a chunk started with png_write_chunk_start() (includes CRC). */
 PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr));
@@ -1044,7 +1009,7 @@
  * the API will be removed in the future.
  */
 PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr,
-    png_size_t png_info_struct_size), PNG_DEPRECATED);
+    size_t png_info_struct_size), PNG_DEPRECATED);
 
 /* Writes all the PNG information before the image. */
 PNG_EXPORT(20, void, png_write_info_before_PLTE,
@@ -1141,7 +1106,7 @@
  * corresponding composited pixel, and the color channels are unassociated
  * (not premultiplied).  The gamma encoded color channels must be scaled
  * according to the contribution and to do this it is necessary to undo
- * the encoding, scale the color values, perform the composition and reencode
+ * the encoding, scale the color values, perform the composition and re-encode
  * the values.  This is the 'PNG' mode.
  *
  * The alternative is to 'associate' the alpha with the color information by
@@ -1197,7 +1162,7 @@
  *
  * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC);
  *    In this case the output is assumed to be something like an sRGB conformant
- *    display preceeded by a power-law lookup table of power 1.45.  This is how
+ *    display preceded by a power-law lookup table of power 1.45.  This is how
  *    early Mac systems behaved.
  *
  * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR);
@@ -1244,7 +1209,7 @@
  *
  * When the default gamma of PNG files doesn't match the output gamma.
  *    If you have PNG files with no gamma information png_set_alpha_mode allows
- *    you to provide a default gamma, but it also sets the ouput gamma to the
+ *    you to provide a default gamma, but it also sets the output gamma to the
  *    matching value.  If you know your PNG files have a gamma that doesn't
  *    match the output you can take advantage of the fact that
  *    png_set_alpha_mode always sets the output gamma but only sets the PNG
@@ -1695,7 +1660,7 @@
  *           chunk will cause an error at this point unless it is to be saved.
  * positive: The chunk was handled, libpng will ignore/discard it.
  *
- * See "INTERACTION WTIH USER CHUNK CALLBACKS" below for important notes about
+ * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about
  * how this behavior will change in libpng 1.7
  */
 PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr,
@@ -1720,7 +1685,7 @@
 
 /* Function to be called when data becomes available */
 PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr,
-    png_inforp info_ptr, png_bytep buffer, png_size_t buffer_size));
+    png_inforp info_ptr, png_bytep buffer, size_t buffer_size));
 
 /* A function which may be called *only* within png_process_data to stop the
  * processing of any more data.  The function returns the number of bytes
@@ -1729,7 +1694,7 @@
  * 'save' is set to true the routine will first save all the pending data and
  * will always return 0.
  */
-PNG_EXPORT(219, png_size_t, png_process_data_pause, (png_structrp, int save));
+PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save));
 
 /* A function which may be called *only* outside (after) a call to
  * png_process_data.  It returns the number of bytes of data to skip in the
@@ -1793,7 +1758,8 @@
 #define PNG_FREE_PLTE 0x1000U
 #define PNG_FREE_TRNS 0x2000U
 #define PNG_FREE_TEXT 0x4000U
-#define PNG_FREE_ALL  0x7fffU
+#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */
+#define PNG_FREE_ALL  0xffffU
 #define PNG_FREE_MUL  0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
 
 #ifdef PNG_USER_MEM_SUPPORTED
@@ -1873,7 +1839,7 @@
     png_const_inforp info_ptr, png_uint_32 flag));
 
 /* Returns number of bytes needed to hold a transformed row. */
-PNG_EXPORT(111, png_size_t, png_get_rowbytes, (png_const_structrp png_ptr,
+PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr,
     png_const_inforp info_ptr));
 
 #ifdef PNG_INFO_IMAGE_SUPPORTED
@@ -2012,6 +1978,18 @@
     png_fixed_point int_blue_Z))
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_bytep *exif));
+PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_bytep exif));
+
+PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif));
+PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif));
+#endif
+
 #ifdef PNG_gAMA_SUPPORTED
 PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr,
     png_const_inforp info_ptr, double *file_gamma))
@@ -2030,9 +2008,6 @@
 #ifdef PNG_hIST_SUPPORTED
 PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr,
     png_inforp info_ptr, png_uint_16p *hist));
-#endif
-
-#ifdef PNG_hIST_SUPPORTED
 PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr,
     png_inforp info_ptr, png_const_uint_16p hist));
 #endif
@@ -2233,7 +2208,7 @@
  * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks
  * it simply resets the behavior to the libpng default.
  *
- * INTERACTION WTIH USER CHUNK CALLBACKS:
+ * INTERACTION WITH USER CHUNK CALLBACKS:
  * The per-chunk handling is always used when there is a png_user_chunk_ptr
  * callback and the callback returns 0; the chunk is then always stored *unless*
  * it is critical and the per-chunk setting is other than ALWAYS.  Notice that
@@ -2300,8 +2275,10 @@
  *    except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to
  *    be processed by libpng.
  */
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
 PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
     int keep, png_const_bytep chunk_list, int num_chunks));
+#endif /* HANDLE_AS_UNKNOWN */
 
 /* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned;
  * the result is therefore true (non-zero) if special handling is required,
@@ -2309,7 +2286,7 @@
  */
 PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr,
     png_const_bytep chunk_name));
-#endif
+#endif /* SET_UNKNOWN_CHUNKS */
 
 #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
 PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr,
@@ -2530,33 +2507,37 @@
 
  /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */
 
-#  define png_composite(composite, fg, alpha, bg)         \
-     { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \
-           * (png_uint_16)(alpha)                         \
-           + (png_uint_16)(bg)*(png_uint_16)(255          \
-           - (png_uint_16)(alpha)) + 128);                \
-       (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); }
+#  define png_composite(composite, fg, alpha, bg)        \
+   {                                                     \
+      png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \
+          * (png_uint_16)(alpha)                         \
+          + (png_uint_16)(bg)*(png_uint_16)(255          \
+          - (png_uint_16)(alpha)) + 128);                \
+      (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \
+   }
 
-#  define png_composite_16(composite, fg, alpha, bg)       \
-     { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg)  \
-           * (png_uint_32)(alpha)                          \
-           + (png_uint_32)(bg)*(65535                      \
-           - (png_uint_32)(alpha)) + 32768);               \
-       (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); }
+#  define png_composite_16(composite, fg, alpha, bg)     \
+   {                                                     \
+      png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \
+          * (png_uint_32)(alpha)                         \
+          + (png_uint_32)(bg)*(65535                     \
+          - (png_uint_32)(alpha)) + 32768);              \
+      (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \
+   }
 
 #else  /* Standard method using integer division */
 
-#  define png_composite(composite, fg, alpha, bg)                        \
-     (composite) =                                                       \
-         (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) +  \
-         (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \
-         127) / 255))
+#  define png_composite(composite, fg, alpha, bg)                      \
+   (composite) =                                                       \
+       (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) +  \
+       (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \
+       127) / 255))
 
-#  define png_composite_16(composite, fg, alpha, bg)                         \
-     (composite) =                                                           \
-         (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \
-         (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) +     \
-         32767) / 65535))
+#  define png_composite_16(composite, fg, alpha, bg)                       \
+   (composite) =                                                           \
+       (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \
+       (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) +     \
+       32767) / 65535))
 #endif /* READ_COMPOSITE_NODIV */
 
 #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
@@ -2592,38 +2573,38 @@
  * format for negative values, which is almost certainly true.
  */
 #  define PNG_get_uint_32(buf) \
-     (((png_uint_32)(*(buf)) << 24) + \
-      ((png_uint_32)(*((buf) + 1)) << 16) + \
-      ((png_uint_32)(*((buf) + 2)) << 8) + \
-      ((png_uint_32)(*((buf) + 3))))
+   (((png_uint_32)(*(buf)) << 24) + \
+    ((png_uint_32)(*((buf) + 1)) << 16) + \
+    ((png_uint_32)(*((buf) + 2)) << 8) + \
+    ((png_uint_32)(*((buf) + 3))))
 
    /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the
     * function) incorrectly returned a value of type png_uint_32.
     */
 #  define PNG_get_uint_16(buf) \
-     ((png_uint_16) \
-      (((unsigned int)(*(buf)) << 8) + \
-       ((unsigned int)(*((buf) + 1)))))
+   ((png_uint_16) \
+    (((unsigned int)(*(buf)) << 8) + \
+    ((unsigned int)(*((buf) + 1)))))
 
 #  define PNG_get_int_32(buf) \
-     ((png_int_32)((*(buf) & 0x80) \
-      ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \
-      : (png_int_32)png_get_uint_32(buf)))
+   ((png_int_32)((*(buf) & 0x80) \
+    ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \
+    : (png_int_32)png_get_uint_32(buf)))
 
-   /* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h,
-    * but defining a macro name prefixed with PNG_PREFIX.
-    */
+/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h,
+ * but defining a macro name prefixed with PNG_PREFIX.
+ */
 #  ifndef PNG_PREFIX
-#     define png_get_uint_32(buf) PNG_get_uint_32(buf)
-#     define png_get_uint_16(buf) PNG_get_uint_16(buf)
-#     define png_get_int_32(buf)  PNG_get_int_32(buf)
+#    define png_get_uint_32(buf) PNG_get_uint_32(buf)
+#    define png_get_uint_16(buf) PNG_get_uint_16(buf)
+#    define png_get_int_32(buf)  PNG_get_int_32(buf)
 #  endif
 #else
 #  ifdef PNG_PREFIX
-      /* No macros; revert to the (redefined) function */
-#     define PNG_get_uint_32 (png_get_uint_32)
-#     define PNG_get_uint_16 (png_get_uint_16)
-#     define PNG_get_int_32  (png_get_int_32)
+   /* No macros; revert to the (redefined) function */
+#    define PNG_get_uint_32 (png_get_uint_32)
+#    define PNG_get_uint_16 (png_get_uint_16)
+#    define PNG_get_int_32  (png_get_int_32)
 #  endif
 #endif
 
@@ -2646,7 +2627,7 @@
  * The simplified API hides the details of both libpng and the PNG file format
  * itself.  It allows PNG files to be read into a very limited number of
  * in-memory bitmap formats or to be written from the same formats.  If these
- * formats do not accomodate your needs then you can, and should, use the more
+ * formats do not accommodate your needs then you can, and should, use the more
  * sophisticated APIs above - these support a wide variety of in-memory formats
  * and a wide variety of sophisticated transformations to those formats as well
  * as a wide variety of APIs to manipulate ancillary information.
@@ -2752,7 +2733,7 @@
  *
  * When the simplified API needs to convert between sRGB and linear colorspaces,
  * the actual sRGB transfer curve defined in the sRGB specification (see the
- * article at http://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2
+ * article at <https://en.wikipedia.org/wiki/SRGB>) is used, not the gamma=1/2.2
  * approximation used elsewhere in libpng.
  *
  * When an alpha channel is present it is expected to denote pixel coverage
@@ -2807,6 +2788,8 @@
 #  define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */
 #endif
 
+#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */
+
 /* Commonly used formats have predefined macros.
  *
  * First the single byte (sRGB) formats:
@@ -2953,7 +2936,7 @@
  * 'flags' field of png_image.
  */
 #define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01
-   /* This indicates the the RGB values of the in-memory bitmap do not
+   /* This indicates that the RGB values of the in-memory bitmap do not
     * correspond to the red, green and blue end-points defined by sRGB.
     */
 
@@ -3006,7 +2989,7 @@
 #endif /* STDIO */
 
 PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
-   png_const_voidp memory, png_size_t size));
+   png_const_voidp memory, size_t size));
    /* The PNG header is read from the given memory buffer. */
 
 PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
@@ -3119,7 +3102,7 @@
     * than or equal to the original value.
     *
     * If the function returns false and *memory_bytes was not changed an error
-    * occured during write.  If *memory_bytes was changed, or is not 0 if
+    * occurred during write.  If *memory_bytes was changed, or is not 0 if
     * 'memory' was NULL, the write would have succeeded but for the memory
     * buffer being too small.  *memory_bytes contains the required number of
     * bytes and will be bigger that the original value.
@@ -3171,9 +3154,9 @@
 #define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\
    ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\
     (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\
-     12U+3U*(image).colormap_entries/*PLTE data*/+\
-     (((image).format&PNG_FORMAT_FLAG_ALPHA)?\
-      12U/*tRNS*/+(image).colormap_entries:0U):0U)+\
+    12U+3U*(image).colormap_entries/*PLTE data*/+\
+    (((image).format&PNG_FORMAT_FLAG_ALPHA)?\
+    12U/*tRNS*/+(image).colormap_entries:0U):0U)+\
     12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size))
    /* A helper for the following macro; if your compiler cannot handle the
     * following macro use this one with the result of
@@ -3203,7 +3186,7 @@
  * option and 'onoff' is 0 (off) or non-0 (on).  The value returned is given
  * by the PNG_OPTION_ defines below.
  *
- * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions,
+ * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions,
  *           are detected at run time, however sometimes it may be impossible
  *           to do this in user mode, in which case it is necessary to discover
  *           the capabilities in an OS specific way.  Such capabilities are
@@ -3221,7 +3204,14 @@
 #endif
 #define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */
 #define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */
-#define PNG_OPTION_NEXT  6 /* Next option - numbers must be even */
+#ifdef PNG_MIPS_MSA_API_SUPPORTED
+#  define PNG_MIPS_MSA   6 /* HARDWARE: MIPS Msa SIMD instructions supported */
+#endif
+#define PNG_IGNORE_ADLER32 8
+#ifdef PNG_POWERPC_VSX_API_SUPPORTED
+#  define PNG_POWERPC_VSX   10 /* HARDWARE: PowerPC VSX SIMD instructions supported */
+#endif
+#define PNG_OPTION_NEXT  12 /* Next option - numbers must be even */
 
 /* Return values: NOTE: there are four values and 'off' is *not* zero */
 #define PNG_OPTION_UNSET   0 /* Unset - defaults to off */
@@ -3245,7 +3235,7 @@
  * one to use is one more than this.)
  */
 #ifdef PNG_EXPORT_LAST_ORDINAL
-  PNG_EXPORT_LAST_ORDINAL(245);
+  PNG_EXPORT_LAST_ORDINAL(249);
 #endif
 
 #ifdef __cplusplus
diff --git a/third_party/libpng16/pngconf.h b/third_party/libpng16/pngconf.h
index 0cabe4b..927a769 100644
--- a/third_party/libpng16/pngconf.h
+++ b/third_party/libpng16/pngconf.h
@@ -1,11 +1,12 @@
 
-/* pngconf.h - machine configurable file for libpng
+/* pngconf.h - machine-configurable file for libpng
  *
- * libpng version 1.6.22, May 26, 2016
+ * libpng version 1.6.37
  *
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -57,14 +58,13 @@
 
 #endif /* PNG_BUILDING_SYMBOL_TABLE */
 
-/* Prior to 1.6.0 it was possible to turn off 'const' in declarations using
- * PNG_NO_CONST; this is no longer supported except for data declarations which
- * apparently still cause problems in 2011 on some compilers.
+/* Prior to 1.6.0, it was possible to turn off 'const' in declarations,
+ * using PNG_NO_CONST.  This is no longer supported.
  */
 #define PNG_CONST const /* backward compatibility only */
 
-/* This controls optimization of the reading of 16-bit and 32-bit values
- * from PNG files.  It can be set on a per-app-file basis - it
+/* This controls optimization of the reading of 16-bit and 32-bit
+ * values from PNG files.  It can be set on a per-app-file basis: it
  * just changes whether a macro is used when the function is called.
  * The library builder sets the default; if read functions are not
  * built into the library the macro implementation is forced on.
@@ -127,7 +127,7 @@
  *
  * These cases only differ if the operating system does not use the C
  * calling convention, at present this just means the above cases
- * (x86 DOS/Windows sytems) and, even then, this does not apply to
+ * (x86 DOS/Windows systems) and, even then, this does not apply to
  * Cygwin running on those systems.
  *
  * Note that the value must be defined in pnglibconf.h so that what
@@ -188,27 +188,27 @@
    * compatible with GCC or Visual C because of different calling conventions.
    */
 #  if PNG_API_RULE == 2
-    /* If this line results in an error, either because __watcall is not
-     * understood or because of a redefine just below you cannot use *this*
-     * build of the library with the compiler you are using.  *This* build was
-     * build using Watcom and applications must also be built using Watcom!
-     */
+   /* If this line results in an error, either because __watcall is not
+    * understood or because of a redefine just below you cannot use *this*
+    * build of the library with the compiler you are using.  *This* build was
+    * build using Watcom and applications must also be built using Watcom!
+    */
 #    define PNGCAPI __watcall
 #  endif
 
 #  if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))
 #    define PNGCAPI __cdecl
 #    if PNG_API_RULE == 1
-       /* If this line results in an error __stdcall is not understood and
-        * PNG_API_RULE should not have been set to '1'.
-        */
+   /* If this line results in an error __stdcall is not understood and
+    * PNG_API_RULE should not have been set to '1'.
+    */
 #      define PNGAPI __stdcall
 #    endif
 #  else
-    /* An older compiler, or one not detected (erroneously) above,
-     * if necessary override on the command line to get the correct
-     * variants for the compiler.
-     */
+   /* An older compiler, or one not detected (erroneously) above,
+    * if necessary override on the command line to get the correct
+    * variants for the compiler.
+    */
 #    ifndef PNGCAPI
 #      define PNGCAPI _cdecl
 #    endif
@@ -225,10 +225,10 @@
 
 #  if (defined(_MSC_VER) && _MSC_VER < 800) ||\
       (defined(__BORLANDC__) && __BORLANDC__ < 0x500)
-    /* older Borland and MSC
-     * compilers used '__export' and required this to be after
-     * the type.
-     */
+   /* older Borland and MSC
+    * compilers used '__export' and required this to be after
+    * the type.
+    */
 #    ifndef PNG_EXPORT_TYPE
 #      define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
 #    endif
@@ -244,9 +244,9 @@
 #  if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
 #    define PNGAPI _System
 #  else /* !Windows/x86 && !OS/2 */
-    /* Use the defaults, or define PNG*API on the command line (but
-     * this will have to be done for every compile!)
-     */
+   /* Use the defaults, or define PNG*API on the command line (but
+    * this will have to be done for every compile!)
+    */
 #  endif /* other system, !OS/2 */
 #endif /* !Windows/x86 */
 
@@ -267,7 +267,7 @@
  */
 #ifndef PNG_IMPEXP
 #  if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
-     /* This forces use of a DLL, disallowing static linking */
+   /* This forces use of a DLL, disallowing static linking */
 #    define PNG_IMPEXP PNG_DLL_IMPORT
 #  endif
 
@@ -340,7 +340,7 @@
    * less efficient code.
    */
 #  if defined(__clang__) && defined(__has_attribute)
-     /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
+   /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
 #    if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__)
 #      define PNG_USE_RESULT __attribute__((__warn_unused_result__))
 #    endif
@@ -507,16 +507,18 @@
 #  error "libpng requires a signed 32-bit (or more) type"
 #endif
 
-#if UINT_MAX > 4294967294
+#if UINT_MAX > 4294967294U
    typedef unsigned int png_uint_32;
-#elif ULONG_MAX > 4294967294
+#elif ULONG_MAX > 4294967294U
    typedef unsigned long int png_uint_32;
 #else
 #  error "libpng requires an unsigned 32-bit (or more) type"
 #endif
 
-/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however,
- * requires an ISOC90 compiler and relies on consistent behavior of sizeof.
+/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t.
+ * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant
+ * behavior of sizeof and ptrdiff_t are required.
+ * The legacy typedefs are provided here for backwards compatibility.
  */
 typedef size_t png_size_t;
 typedef ptrdiff_t png_ptrdiff_t;
@@ -537,13 +539,12 @@
 #  endif
 #endif
 
-/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no
- * smaller than png_uint_32.  Casts from png_size_t or png_uint_32 to
- * png_alloc_size_t are not necessary; in fact, it is recommended not to use
- * them at all so that the compiler can complain when something turns out to be
- * problematic.
+/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller
+ * than png_uint_32.  Casts from size_t or png_uint_32 to png_alloc_size_t are
+ * not necessary; in fact, it is recommended not to use them at all, so that
+ * the compiler can complain when something turns out to be problematic.
  *
- * Casts in the other direction (from png_alloc_size_t to png_size_t or
+ * Casts in the other direction (from png_alloc_size_t to size_t or
  * png_uint_32) should be explicitly applied; however, we do not expect to
  * encounter practical situations that require such conversions.
  *
@@ -553,7 +554,7 @@
 #ifdef PNG_SMALL_SIZE_T
    typedef png_uint_32 png_alloc_size_t;
 #else
-   typedef png_size_t png_alloc_size_t;
+   typedef size_t png_alloc_size_t;
 #endif
 
 /* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler
@@ -589,8 +590,8 @@
 typedef const char            * png_const_charp;
 typedef png_fixed_point       * png_fixed_point_p;
 typedef const png_fixed_point * png_const_fixed_point_p;
-typedef png_size_t            * png_size_tp;
-typedef const png_size_t      * png_const_size_tp;
+typedef size_t                * png_size_tp;
+typedef const size_t          * png_const_size_tp;
 
 #ifdef PNG_STDIO_SUPPORTED
 typedef FILE            * png_FILE_p;
diff --git a/third_party/libpng16/pngdebug.h b/third_party/libpng16/pngdebug.h
index 15a7ed0..00d5a45 100644
--- a/third_party/libpng16/pngdebug.h
+++ b/third_party/libpng16/pngdebug.h
@@ -1,10 +1,10 @@
 
 /* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
  *
- * Last changed in libpng 1.6.8 [December 19, 2013]
+ * Copyright (c) 2018 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
diff --git a/third_party/libpng16/pngerror.c b/third_party/libpng16/pngerror.c
index 3fc8092..ec3a709 100644
--- a/third_party/libpng16/pngerror.c
+++ b/third_party/libpng16/pngerror.c
@@ -1,10 +1,10 @@
 
 /* pngerror.c - stub functions for i/o and memory allocation
  *
- * Last changed in libpng 1.6.15 [November 20, 2014]
- * Copyright (c) 1998-2002,2004,2006-2014 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -26,7 +26,7 @@
 #ifdef PNG_WARNINGS_SUPPORTED
 static void /* PRIVATE */
 png_default_warning PNGARG((png_const_structrp png_ptr,
-   png_const_charp warning_message));
+    png_const_charp warning_message));
 #endif /* WARNINGS */
 
 /* This function is called whenever there is a fatal error.  This function
@@ -37,7 +37,7 @@
 #ifdef PNG_ERROR_TEXT_SUPPORTED
 PNG_FUNCTION(void,PNGAPI
 png_error,(png_const_structrp png_ptr, png_const_charp error_message),
-   PNG_NORETURN)
+    PNG_NORETURN)
 {
 #ifdef PNG_ERROR_NUMBERS_SUPPORTED
    char msg[16];
@@ -65,18 +65,18 @@
 
             else
                error_message += offset;
-      }
-
-      else
-      {
-         if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
-         {
-            msg[0] = '0';
-            msg[1] = '\0';
-            error_message = msg;
          }
-       }
-     }
+
+         else
+         {
+            if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
+            {
+               msg[0] = '0';
+               msg[1] = '\0';
+               error_message = msg;
+            }
+         }
+      }
    }
 #endif
    if (png_ptr != NULL && png_ptr->error_fn != NULL)
@@ -110,7 +110,7 @@
  */
 size_t
 png_safecat(png_charp buffer, size_t bufsize, size_t pos,
-   png_const_charp string)
+    png_const_charp string)
 {
    if (buffer != NULL && pos < bufsize)
    {
@@ -131,7 +131,7 @@
  */
 png_charp
 png_format_number(png_const_charp start, png_charp end, int format,
-   png_alloc_size_t number)
+    png_alloc_size_t number)
 {
    int count = 0;    /* number of digits output */
    int mincount = 1; /* minimum number required */
@@ -163,7 +163,7 @@
          case PNG_NUMBER_FORMAT_02u:
             /* Expects at least 2 digits. */
             mincount = 2;
-            /* FALL THROUGH */
+            /* FALLTHROUGH */
 
          case PNG_NUMBER_FORMAT_u:
             *--end = digits[number % 10];
@@ -173,7 +173,7 @@
          case PNG_NUMBER_FORMAT_02x:
             /* This format expects at least two digits */
             mincount = 2;
-            /* FALL THROUGH */
+            /* FALLTHROUGH */
 
          case PNG_NUMBER_FORMAT_x:
             *--end = digits[number & 0xf];
@@ -233,7 +233,7 @@
    }
    if (png_ptr != NULL && png_ptr->warning_fn != NULL)
       (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr),
-         warning_message + offset);
+          warning_message + offset);
    else
       png_default_warning(png_ptr, warning_message + offset);
 }
@@ -245,7 +245,7 @@
  */
 void
 png_warning_parameter(png_warning_parameters p, int number,
-   png_const_charp string)
+    png_const_charp string)
 {
    if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
       (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
@@ -253,7 +253,7 @@
 
 void
 png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
-   png_alloc_size_t value)
+    png_alloc_size_t value)
 {
    char buffer[PNG_NUMBER_BUFFER_SIZE];
    png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
@@ -261,7 +261,7 @@
 
 void
 png_warning_parameter_signed(png_warning_parameters p, int number, int format,
-   png_int_32 value)
+    png_int_32 value)
 {
    png_alloc_size_t u;
    png_charp str;
@@ -282,7 +282,7 @@
 
 void
 png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,
-   png_const_charp message)
+    png_const_charp message)
 {
    /* The internal buffer is just 192 bytes - enough for all our messages,
     * overflow doesn't happen because this code checks!  If someone figures
@@ -391,10 +391,10 @@
 void /* PRIVATE */
 png_app_warning(png_const_structrp png_ptr, png_const_charp error_message)
 {
-  if ((png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) != 0)
-     png_warning(png_ptr, error_message);
-  else
-     png_error(png_ptr, error_message);
+   if ((png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) != 0)
+      png_warning(png_ptr, error_message);
+   else
+      png_error(png_ptr, error_message);
 
 #  ifndef PNG_ERROR_TEXT_SUPPORTED
       PNG_UNUSED(error_message)
@@ -404,10 +404,10 @@
 void /* PRIVATE */
 png_app_error(png_const_structrp png_ptr, png_const_charp error_message)
 {
-  if ((png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) != 0)
-     png_warning(png_ptr, error_message);
-  else
-     png_error(png_ptr, error_message);
+   if ((png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) != 0)
+      png_warning(png_ptr, error_message);
+   else
+      png_error(png_ptr, error_message);
 
 #  ifndef PNG_ERROR_TEXT_SUPPORTED
       PNG_UNUSED(error_message)
@@ -425,7 +425,7 @@
  * if the character is invalid.
  */
 #define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
-static PNG_CONST char png_digit[16] = {
+static const char png_digit[16] = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F'
 };
@@ -478,7 +478,7 @@
 #if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
 PNG_FUNCTION(void,PNGAPI
 png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),
-   PNG_NORETURN)
+    PNG_NORETURN)
 {
    char msg[18+PNG_MAX_ERROR_TEXT];
    if (png_ptr == NULL)
@@ -573,7 +573,7 @@
 {
 #  define fixed_message "fixed point overflow in "
 #  define fixed_message_ln ((sizeof fixed_message)-1)
-   int  iin;
+   unsigned int  iin;
    char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];
    memcpy(msg, fixed_message, fixed_message_ln);
    iin = 0;
@@ -620,7 +620,7 @@
       else
       {
          png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *,
-            png_malloc_warn(png_ptr, jmp_buf_size));
+             png_malloc_warn(png_ptr, jmp_buf_size));
 
          if (png_ptr->jmp_buf_ptr == NULL)
             return NULL; /* new NULL return on OOM */
@@ -709,7 +709,7 @@
  */
 static PNG_FUNCTION(void /* PRIVATE */,
 png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
-   PNG_NORETURN)
+    PNG_NORETURN)
 {
 #ifdef PNG_CONSOLE_IO_SUPPORTED
 #ifdef PNG_ERROR_NUMBERS_SUPPORTED
@@ -883,9 +883,9 @@
     */
 PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI
 png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message),
-   PNG_NORETURN)
+    PNG_NORETURN)
 {
-   const png_const_structrp png_ptr = png_nonconst_ptr;
+   png_const_structrp png_ptr = png_nonconst_ptr;
    png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
 
    /* An error is always logged here, overwriting anything (typically a warning)
@@ -906,7 +906,7 @@
       /* Missing longjmp buffer, the following is to help debugging: */
       {
          size_t pos = png_safecat(image->message, (sizeof image->message), 0,
-            "bad longjmp: ");
+             "bad longjmp: ");
          png_safecat(image->message, (sizeof image->message), pos,
              error_message);
       }
@@ -920,7 +920,7 @@
 void /* PRIVATE */ PNGCBAPI
 png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)
 {
-   const png_const_structrp png_ptr = png_nonconst_ptr;
+   png_const_structrp png_ptr = png_nonconst_ptr;
    png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
 
    /* A warning is only logged if there is no prior warning or error. */
diff --git a/third_party/libpng16/pngget.c b/third_party/libpng16/pngget.c
index 14fc7be..5abf1ef 100644
--- a/third_party/libpng16/pngget.c
+++ b/third_party/libpng16/pngget.c
@@ -1,10 +1,10 @@
 
 /* pngget.c - retrieval of values from info struct
  *
- * Last changed in libpng 1.6.17 [March 26, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -26,7 +26,7 @@
    return(0);
 }
 
-png_size_t PNGAPI
+size_t PNGAPI
 png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr)
 {
    if (png_ptr != NULL && info_ptr != NULL)
@@ -338,7 +338,7 @@
    png_fixed_point result;
    if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127,
        5000) != 0)
-      return result;
+      return (png_uint_32)result;
 
    /* Overflow. */
    return 0;
@@ -367,7 +367,7 @@
 static png_fixed_point
 png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns)
 {
-   /* Convert from metres * 1,000,000 to inches * 100,000, meters to
+   /* Convert from meters * 1,000,000 to inches * 100,000, meters to
     * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127.
     * Notice that this can overflow - a warning is output and 0 is
     * returned.
@@ -486,7 +486,7 @@
 #ifdef PNG_bKGD_SUPPORTED
 png_uint_32 PNGAPI
 png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
-   png_color_16p *background)
+    png_color_16p *background)
 {
    if (png_ptr != NULL && info_ptr != NULL &&
        (info_ptr->valid & PNG_INFO_bKGD) != 0 &&
@@ -526,28 +526,28 @@
 
       if (white_x != NULL)
          *white_x = png_float(png_ptr,
-            info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
+             info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
       if (white_y != NULL)
          *white_y = png_float(png_ptr,
-            info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");
+             info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");
       if (red_x != NULL)
          *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx,
-            "cHRM red X");
+             "cHRM red X");
       if (red_y != NULL)
          *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy,
-            "cHRM red Y");
+             "cHRM red Y");
       if (green_x != NULL)
          *green_x = png_float(png_ptr,
-            info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");
+             info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");
       if (green_y != NULL)
          *green_y = png_float(png_ptr,
-            info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");
+             info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");
       if (blue_x != NULL)
          *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex,
-            "cHRM blue X");
+             "cHRM blue X");
       if (blue_y != NULL)
          *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey,
-            "cHRM blue Y");
+             "cHRM blue Y");
       return (PNG_INFO_cHRM);
    }
 
@@ -556,42 +556,42 @@
 
 png_uint_32 PNGAPI
 png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,
-   double *red_X, double *red_Y, double *red_Z, double *green_X,
-   double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
-   double *blue_Z)
+    double *red_X, double *red_Y, double *red_Z, double *green_X,
+    double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
+    double *blue_Z)
 {
    if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+       (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
    {
       png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");
 
       if (red_X != NULL)
          *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X,
-            "cHRM red X");
+             "cHRM red X");
       if (red_Y != NULL)
          *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y,
-            "cHRM red Y");
+             "cHRM red Y");
       if (red_Z != NULL)
          *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z,
-            "cHRM red Z");
+             "cHRM red Z");
       if (green_X != NULL)
          *green_X = png_float(png_ptr,
-            info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");
+             info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");
       if (green_Y != NULL)
          *green_Y = png_float(png_ptr,
-            info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");
+             info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");
       if (green_Z != NULL)
          *green_Z = png_float(png_ptr,
-            info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");
+             info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");
       if (blue_X != NULL)
          *blue_X = png_float(png_ptr,
-            info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");
+             info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");
       if (blue_Y != NULL)
          *blue_Y = png_float(png_ptr,
-            info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");
+             info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");
       if (blue_Z != NULL)
          *blue_Z = png_float(png_ptr,
-            info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
+             info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
       return (PNG_INFO_cHRM);
    }
 
@@ -681,8 +681,8 @@
    png_debug1(1, "in %s retrieval function", "gAMA");
 
    if (png_ptr != NULL && info_ptr != NULL &&
-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
-      file_gamma != NULL)
+       (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
+       file_gamma != NULL)
    {
       *file_gamma = info_ptr->colorspace.gamma;
       return (PNG_INFO_gAMA);
@@ -704,7 +704,7 @@
       file_gamma != NULL)
    {
       *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma,
-         "png_get_gAMA");
+          "png_get_gAMA");
       return (PNG_INFO_gAMA);
    }
 
@@ -741,8 +741,7 @@
 
    if (png_ptr != NULL && info_ptr != NULL &&
        (info_ptr->valid & PNG_INFO_iCCP) != 0 &&
-       name != NULL && compression_type != NULL && profile != NULL &&
-           proflen != NULL)
+       name != NULL && profile != NULL && proflen != NULL)
    {
       *name = info_ptr->iccp_name;
       *profile = info_ptr->iccp_profile;
@@ -750,11 +749,13 @@
       /* This is somewhat irrelevant since the profile data returned has
        * actually been uncompressed.
        */
-      *compression_type = PNG_COMPRESSION_TYPE_BASE;
+      if (compression_type != NULL)
+         *compression_type = PNG_COMPRESSION_TYPE_BASE;
       return (PNG_INFO_iCCP);
    }
 
    return (0);
+
 }
 #endif
 
@@ -773,6 +774,35 @@
 }
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+png_uint_32 PNGAPI
+png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_bytep *exif)
+{
+  png_warning(png_ptr, "png_get_eXIf does not work; use png_get_eXIf_1");
+  PNG_UNUSED(info_ptr)
+  PNG_UNUSED(exif)
+  return 0;
+}
+
+png_uint_32 PNGAPI
+png_get_eXIf_1(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *num_exif, png_bytep *exif)
+{
+   png_debug1(1, "in %s retrieval function", "eXIf");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_eXIf) != 0 && exif != NULL)
+   {
+      *num_exif = info_ptr->num_exif;
+      *exif = info_ptr->exif;
+      return (PNG_INFO_eXIf);
+   }
+
+   return (0);
+}
+#endif
+
 #ifdef PNG_hIST_SUPPORTED
 png_uint_32 PNGAPI
 png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
@@ -901,7 +931,7 @@
        */
       *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width");
       *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height),
-         "sCAL height");
+          "sCAL height");
       return (PNG_INFO_sCAL);
    }
 
@@ -1135,7 +1165,7 @@
 }
 #endif
 
-png_size_t PNGAPI
+size_t PNGAPI
 png_get_compression_buffer_size(png_const_structrp png_ptr)
 {
    if (png_ptr == NULL)
diff --git a/third_party/libpng16/pnginfo.h b/third_party/libpng16/pnginfo.h
index 361ed8b..1f98ded 100644
--- a/third_party/libpng16/pnginfo.h
+++ b/third_party/libpng16/pnginfo.h
@@ -1,10 +1,10 @@
 
 /* pnginfo.h - header file for PNG reference library
  *
- * Last changed in libpng 1.6.1 [March 28, 2013]
- * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2013,2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -55,10 +55,10 @@
 struct png_info_def
 {
    /* The following are necessary for every PNG file */
-   png_uint_32 width;  /* width of image in pixels (from IHDR) */
-   png_uint_32 height; /* height of image in pixels (from IHDR) */
-   png_uint_32 valid;  /* valid chunk data (see PNG_INFO_ below) */
-   png_size_t rowbytes; /* bytes needed to hold an untransformed row */
+   png_uint_32 width;       /* width of image in pixels (from IHDR) */
+   png_uint_32 height;      /* height of image in pixels (from IHDR) */
+   png_uint_32 valid;       /* valid chunk data (see PNG_INFO_ below) */
+   size_t rowbytes;         /* bytes needed to hold an untransformed row */
    png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
    png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
    png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
@@ -185,6 +185,14 @@
    png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+   int num_exif;  /* Added at libpng-1.6.31 */
+   png_bytep exif;
+# ifdef PNG_READ_eXIf_SUPPORTED
+   png_bytep eXIf_buf;  /* Added at libpng-1.6.32 */
+# endif
+#endif
+
 #ifdef PNG_hIST_SUPPORTED
    /* The hIST chunk contains the relative frequency or importance of the
     * various palette entries, so that a viewer can intelligently select a
@@ -239,7 +247,7 @@
    /* The sCAL chunk describes the actual physical dimensions of the
     * subject matter of the graphic.  The chunk contains a unit specification
     * a byte value, and two ASCII strings representing floating-point
-    * values.  The values are width and height corresponsing to one pixel
+    * values.  The values are width and height corresponding to one pixel
     * in the image.  Data values are valid if (valid & PNG_INFO_sCAL) is
     * non-zero.
     */
diff --git a/third_party/libpng16/pnglibconf.h b/third_party/libpng16/pnglibconf.h
index adf4238..308c606 100644
--- a/third_party/libpng16/pnglibconf.h
+++ b/third_party/libpng16/pnglibconf.h
@@ -1,8 +1,8 @@
-/* libpng 1.6.20 STANDARD API DEFINITION */
+/* libpng 1.6.22 CUSTOM API DEFINITION */
 
 /* pnglibconf.h - library build configuration */
 
-/* Libpng version 1.6.20 - December 3, 2015 */
+/* Libpng version 1.6.22 - May 29, 2016 */
 
 /* Copyright (c) 1998-2015 Glenn Randers-Pehrson */
 
@@ -11,11 +11,12 @@
 /* and license in png.h */
 
 /* pnglibconf.h */
-/* Machine generated file: DO NOT EDIT */
 /* Derived from: scripts/pnglibconf.dfa */
 #ifndef PNGLCONF_H
 #define PNGLCONF_H
-/* options */
+
+/* default options */
+/* These are PNG options that match the default in scripts/pnglibconf.dfa */
 #define PNG_16BIT_SUPPORTED
 #define PNG_ALIGNED_MEMORY_SUPPORTED
 /*#undef PNG_ARM_NEON_API_SUPPORTED*/
@@ -23,11 +24,7 @@
 #define PNG_BENIGN_ERRORS_SUPPORTED
 #define PNG_BENIGN_READ_ERRORS_SUPPORTED
 /*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
-#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
-#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
 #define PNG_COLORSPACE_SUPPORTED
-#define PNG_CONSOLE_IO_SUPPORTED
-#define PNG_CONVERT_tIME_SUPPORTED
 #define PNG_EASY_ACCESS_SUPPORTED
 /*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
 #define PNG_ERROR_TEXT_SUPPORTED
@@ -37,12 +34,8 @@
 #define PNG_FORMAT_AFIRST_SUPPORTED
 #define PNG_FORMAT_BGR_SUPPORTED
 #define PNG_GAMMA_SUPPORTED
-#define PNG_GET_PALETTE_MAX_SUPPORTED
 #define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-#define PNG_INCH_CONVERSIONS_SUPPORTED
 #define PNG_INFO_IMAGE_SUPPORTED
-#define PNG_IO_STATE_SUPPORTED
-#define PNG_MNG_FEATURES_SUPPORTED
 #define PNG_POINTER_INDEXING_SUPPORTED
 #define PNG_PROGRESSIVE_READ_SUPPORTED
 #define PNG_READ_16BIT_SUPPORTED
@@ -50,23 +43,17 @@
 #define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
 #define PNG_READ_BACKGROUND_SUPPORTED
 #define PNG_READ_BGR_SUPPORTED
-#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
 #define PNG_READ_COMPOSITE_NODIV_SUPPORTED
 #define PNG_READ_COMPRESSED_TEXT_SUPPORTED
 #define PNG_READ_EXPAND_16_SUPPORTED
 #define PNG_READ_EXPAND_SUPPORTED
 #define PNG_READ_FILLER_SUPPORTED
 #define PNG_READ_GAMMA_SUPPORTED
-#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
 #define PNG_READ_GRAY_TO_RGB_SUPPORTED
 #define PNG_READ_INTERLACING_SUPPORTED
 #define PNG_READ_INT_FUNCTIONS_SUPPORTED
-#define PNG_READ_INVERT_ALPHA_SUPPORTED
-#define PNG_READ_INVERT_SUPPORTED
-#define PNG_READ_OPT_PLTE_SUPPORTED
 #define PNG_READ_PACKSWAP_SUPPORTED
 #define PNG_READ_PACK_SUPPORTED
-#define PNG_READ_QUANTIZE_SUPPORTED
 #define PNG_READ_RGB_TO_GRAY_SUPPORTED
 #define PNG_READ_SCALE_16_TO_8_SUPPORTED
 #define PNG_READ_SHIFT_SUPPORTED
@@ -80,28 +67,17 @@
 #define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
 #define PNG_READ_USER_CHUNKS_SUPPORTED
 #define PNG_READ_USER_TRANSFORM_SUPPORTED
-#define PNG_READ_bKGD_SUPPORTED
 #define PNG_READ_cHRM_SUPPORTED
 #define PNG_READ_gAMA_SUPPORTED
-#define PNG_READ_hIST_SUPPORTED
 #define PNG_READ_iCCP_SUPPORTED
-#define PNG_READ_iTXt_SUPPORTED
-#define PNG_READ_oFFs_SUPPORTED
-#define PNG_READ_pCAL_SUPPORTED
-#define PNG_READ_pHYs_SUPPORTED
-#define PNG_READ_sBIT_SUPPORTED
-#define PNG_READ_sCAL_SUPPORTED
-#define PNG_READ_sPLT_SUPPORTED
 #define PNG_READ_sRGB_SUPPORTED
 #define PNG_READ_tEXt_SUPPORTED
-#define PNG_READ_tIME_SUPPORTED
 #define PNG_READ_tRNS_SUPPORTED
 #define PNG_READ_zTXt_SUPPORTED
 #define PNG_SAVE_INT_32_SUPPORTED
 #define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
 #define PNG_SEQUENTIAL_READ_SUPPORTED
 #define PNG_SETJMP_SUPPORTED
-#define PNG_SET_OPTION_SUPPORTED
 #define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
 #define PNG_SET_USER_LIMITS_SUPPORTED
 #define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
@@ -109,11 +85,11 @@
 #define PNG_SIMPLIFIED_READ_SUPPORTED
 #define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
 #define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
 #define PNG_SIMPLIFIED_WRITE_SUPPORTED
 #define PNG_STDIO_SUPPORTED
 #define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
 #define PNG_TEXT_SUPPORTED
-#define PNG_TIME_RFC1123_SUPPORTED
 #define PNG_UNKNOWN_CHUNKS_SUPPORTED
 #define PNG_USER_CHUNKS_SUPPORTED
 #define PNG_USER_LIMITS_SUPPORTED
@@ -124,19 +100,14 @@
 #define PNG_WRITE_16BIT_SUPPORTED
 #define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
 #define PNG_WRITE_BGR_SUPPORTED
-#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
 #define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
 #define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
 #define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
 #define PNG_WRITE_FILLER_SUPPORTED
 #define PNG_WRITE_FILTER_SUPPORTED
 #define PNG_WRITE_FLUSH_SUPPORTED
-#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
 #define PNG_WRITE_INTERLACING_SUPPORTED
 #define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
-#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
-#define PNG_WRITE_INVERT_SUPPORTED
-#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
 #define PNG_WRITE_PACKSWAP_SUPPORTED
 #define PNG_WRITE_PACK_SUPPORTED
 #define PNG_WRITE_SHIFT_SUPPORTED
@@ -148,42 +119,79 @@
 #define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
 #define PNG_WRITE_USER_TRANSFORM_SUPPORTED
 #define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
-#define PNG_WRITE_bKGD_SUPPORTED
 #define PNG_WRITE_cHRM_SUPPORTED
 #define PNG_WRITE_gAMA_SUPPORTED
-#define PNG_WRITE_hIST_SUPPORTED
 #define PNG_WRITE_iCCP_SUPPORTED
-#define PNG_WRITE_iTXt_SUPPORTED
-#define PNG_WRITE_oFFs_SUPPORTED
-#define PNG_WRITE_pCAL_SUPPORTED
-#define PNG_WRITE_pHYs_SUPPORTED
-#define PNG_WRITE_sBIT_SUPPORTED
-#define PNG_WRITE_sCAL_SUPPORTED
-#define PNG_WRITE_sPLT_SUPPORTED
 #define PNG_WRITE_sRGB_SUPPORTED
 #define PNG_WRITE_tEXt_SUPPORTED
-#define PNG_WRITE_tIME_SUPPORTED
 #define PNG_WRITE_tRNS_SUPPORTED
 #define PNG_WRITE_zTXt_SUPPORTED
-#define PNG_bKGD_SUPPORTED
 #define PNG_cHRM_SUPPORTED
 #define PNG_gAMA_SUPPORTED
-#define PNG_hIST_SUPPORTED
 #define PNG_iCCP_SUPPORTED
-#define PNG_iTXt_SUPPORTED
-#define PNG_oFFs_SUPPORTED
-#define PNG_pCAL_SUPPORTED
-#define PNG_pHYs_SUPPORTED
 #define PNG_sBIT_SUPPORTED
-#define PNG_sCAL_SUPPORTED
-#define PNG_sPLT_SUPPORTED
 #define PNG_sRGB_SUPPORTED
 #define PNG_tEXt_SUPPORTED
-#define PNG_tIME_SUPPORTED
 #define PNG_tRNS_SUPPORTED
 #define PNG_zTXt_SUPPORTED
 /* end of options */
-/* settings */
+
+/* chromium options */
+/* These are PNG options that chromium chooses to explicitly disable */
+/*#undef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED*/
+/*#undef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED*/
+/*#undef PNG_CONSOLE_IO_SUPPORTED*/
+/*#undef PNG_CONVERT_tIME_SUPPORTED*/
+/*#undef PNG_GET_PALETTE_MAX_SUPPORTED*/
+/*#undef PNG_INCH_CONVERSIONS_SUPPORTED*/
+/*#undef PNG_IO_STATE_SUPPORTED*/
+/*#undef PNG_MNG_FEATURES_SUPPORTED*/
+/*#undef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED*/
+/*#undef PNG_READ_GET_PALETTE_MAX_SUPPORTED*/
+/*#undef PNG_READ_INVERT_ALPHA_SUPPORTED*/
+/*#undef PNG_READ_INVERT_SUPPORTED*/
+/*#undef PNG_READ_OPT_PLTE_SUPPORTED*/
+/*#undef PNG_READ_QUANTIZE_SUPPORTED*/
+/*#undef PNG_READ_bKGD_SUPPORTED*/
+/*#undef PNG_READ_hIST_SUPPORTED*/
+/*#undef PNG_READ_iTXt_SUPPORTED*/
+/*#undef PNG_READ_oFFs_SUPPORTED*/
+/*#undef PNG_READ_pCAL_SUPPORTED*/
+/*#undef PNG_READ_pHYs_SUPPORTED*/
+/*#undef PNG_READ_sBIT_SUPPORTED*/
+/*#undef PNG_READ_sCAL_SUPPORTED*/
+/*#undef PNG_READ_sPLT_SUPPORTED*/
+/*#undef PNG_READ_tIME_SUPPORTED*/
+/*#undef PNG_SET_OPTION_SUPPORTED*/
+/*#undef PNG_TIME_RFC1123_SUPPORTED*/
+/*#undef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED*/
+/*#undef PNG_WRITE_GET_PALETTE_MAX_SUPPORTED*/
+/*#undef PNG_WRITE_INVERT_ALPHA_SUPPORTED*/
+/*#undef PNG_WRITE_INVERT_SUPPORTED*/
+/*#undef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED*/
+/*#undef PNG_WRITE_bKGD_SUPPORTED*/
+/*#undef PNG_WRITE_hIST_SUPPORTED*/
+/*#undef PNG_WRITE_iTXt_SUPPORTED*/
+/*#undef PNG_WRITE_oFFs_SUPPORTED*/
+/*#undef PNG_WRITE_pCAL_SUPPORTED*/
+/*#undef PNG_WRITE_pHYs_SUPPORTED*/
+/*#undef PNG_WRITE_sBIT_SUPPORTED*/
+/*#undef PNG_WRITE_sCAL_SUPPORTED*/
+/*#undef PNG_WRITE_sPLT_SUPPORTED*/
+/*#undef PNG_WRITE_tIME_SUPPORTED*/
+/*#undef PNG_bKGD_SUPPORTED*/
+/*#undef PNG_hIST_SUPPORTED*/
+/*#undef PNG_iTXt_SUPPORTED*/
+/*#undef PNG_oFFs_SUPPORTED*/
+/*#undef PNG_pCAL_SUPPORTED*/
+/*#undef PNG_pHYs_SUPPORTED*/
+/*#undef PNG_sCAL_SUPPORTED*/
+/*#undef PNG_sPLT_SUPPORTED*/
+/*#undef PNG_tIME_SUPPORTED*/
+/* end of chromium options */
+
+/* default settings */
+/* These are PNG settings that match the default in scripts/pnglibconf.dfa */
 #define PNG_API_RULE 0
 #define PNG_DEFAULT_READ_MACROS 1
 #define PNG_GAMMA_THRESHOLD_FIXED 5000
@@ -199,8 +207,6 @@
 #define PNG_QUANTIZE_RED_BITS 5
 #define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
 #define PNG_TEXT_Z_DEFAULT_STRATEGY 0
-#define PNG_USER_CHUNK_CACHE_MAX 1000
-#define PNG_USER_CHUNK_MALLOC_MAX 8000000
 #define PNG_USER_HEIGHT_MAX 1000000
 #define PNG_USER_WIDTH_MAX 1000000
 #define PNG_ZBUF_SIZE 8192
@@ -210,5 +216,21 @@
 #define PNG_Z_DEFAULT_STRATEGY 1
 #define PNG_sCAL_PRECISION 5
 #define PNG_sRGB_PROFILE_CHECKS 2
-/* end of settings */
+/* end of default settings */
+
+/* chromium settings */
+/* These are PNG setting that chromium has modified */
+/* crbug.com/117369 */
+#define PNG_USER_CHUNK_CACHE_MAX 128
+#define PNG_USER_CHUNK_MALLOC_MAX 4000000L
+/* end of chromium settings */
+
+/* pdfium prefixing */
+/*
+ * This is necessary to build multiple copies of libpng.  We need this while pdfium builds
+ * its own copy of libpng.
+ */
+#include "pngprefix.h"
+/* end of pdfium prefixing */
+
 #endif /* PNGLCONF_H */
diff --git a/third_party/libpng16/pngmem.c b/third_party/libpng16/pngmem.c
index cf815a6..726b7e0 100644
--- a/third_party/libpng16/pngmem.c
+++ b/third_party/libpng16/pngmem.c
@@ -1,10 +1,10 @@
 
 /* pngmem.c - stub functions for memory allocation
  *
- * Last changed in libpng 1.6.15 [November 20, 2014]
- * Copyright (c) 1998-2002,2004,2006-2014 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -69,7 +69,7 @@
  */
 PNG_FUNCTION(png_voidp /* PRIVATE */,
 png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
-   PNG_ALLOCATED)
+    PNG_ALLOCATED)
 {
    /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS
     * allocators have also been removed in 1.6.0, so any 16-bit system now has
@@ -110,9 +110,9 @@
  */
 static png_voidp
 png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
-   size_t element_size)
+    size_t element_size)
 {
-   png_alloc_size_t req = nelements; /* known to be > 0 */
+   png_alloc_size_t req = (png_alloc_size_t)nelements; /* known to be > 0 */
 
    if (req <= PNG_SIZE_MAX/element_size)
       return png_malloc_base(png_ptr, req * element_size);
@@ -123,7 +123,7 @@
 
 PNG_FUNCTION(png_voidp /* PRIVATE */,
 png_malloc_array,(png_const_structrp png_ptr, int nelements,
-   size_t element_size),PNG_ALLOCATED)
+    size_t element_size),PNG_ALLOCATED)
 {
    if (nelements <= 0 || element_size == 0)
       png_error(png_ptr, "internal error: array alloc");
@@ -133,7 +133,7 @@
 
 PNG_FUNCTION(png_voidp /* PRIVATE */,
 png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
-   int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
+    int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
 {
    /* These are internal errors: */
    if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
@@ -146,7 +146,7 @@
    if (add_elements <= INT_MAX - old_elements)
    {
       png_voidp new_array = png_malloc_array_checked(png_ptr,
-         old_elements+add_elements, element_size);
+          old_elements+add_elements, element_size);
 
       if (new_array != NULL)
       {
@@ -157,7 +157,7 @@
             memcpy(new_array, old_array, element_size*(unsigned)old_elements);
 
          memset((char*)new_array + element_size*(unsigned)old_elements, 0,
-            element_size*(unsigned)add_elements);
+             element_size*(unsigned)add_elements);
 
          return new_array;
       }
@@ -190,7 +190,7 @@
 #ifdef PNG_USER_MEM_SUPPORTED
 PNG_FUNCTION(png_voidp,PNGAPI
 png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size),
-   PNG_ALLOCATED PNG_DEPRECATED)
+    PNG_ALLOCATED PNG_DEPRECATED)
 {
    png_voidp ret;
 
@@ -213,7 +213,7 @@
  */
 PNG_FUNCTION(png_voidp,PNGAPI
 png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size),
-   PNG_ALLOCATED)
+    PNG_ALLOCATED)
 {
    if (png_ptr != NULL)
    {
diff --git a/third_party/libpng16/pngpread.c b/third_party/libpng16/pngpread.c
index a16fcbe..e283627 100644
--- a/third_party/libpng16/pngpread.c
+++ b/third_party/libpng16/pngpread.c
@@ -1,10 +1,10 @@
 
 /* pngpread.c - read a png file in push mode
  *
- * Last changed in libpng 1.6.18 [July 23, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -34,7 +34,7 @@
 
 void PNGAPI
 png_process_data(png_structrp png_ptr, png_inforp info_ptr,
-    png_bytep buffer, png_size_t buffer_size)
+    png_bytep buffer, size_t buffer_size)
 {
    if (png_ptr == NULL || info_ptr == NULL)
       return;
@@ -47,7 +47,7 @@
    }
 }
 
-png_size_t PNGAPI
+size_t PNGAPI
 png_process_data_pause(png_structrp png_ptr, int save)
 {
    if (png_ptr != NULL)
@@ -60,7 +60,7 @@
       else
       {
          /* This includes any pending saved bytes: */
-         png_size_t remaining = png_ptr->buffer_size;
+         size_t remaining = png_ptr->buffer_size;
          png_ptr->buffer_size = 0;
 
          /* So subtract the saved buffer size, unless all the data
@@ -77,11 +77,11 @@
 png_uint_32 PNGAPI
 png_process_data_skip(png_structrp png_ptr)
 {
-  /* TODO: Deprecate and remove this API.
-   * Somewhere the implementation of this seems to have been lost,
-   * or abandoned.  It was only to support some internal back-door access
-   * to png_struct) in libpng-1.4.x.
-   */
+/* TODO: Deprecate and remove this API.
+ * Somewhere the implementation of this seems to have been lost,
+ * or abandoned.  It was only to support some internal back-door access
+ * to png_struct) in libpng-1.4.x.
+ */
    png_app_warning(png_ptr,
 "png_process_data_skip is not implemented in any current version of libpng");
    return 0;
@@ -133,8 +133,8 @@
 void /* PRIVATE */
 png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr)
 {
-   png_size_t num_checked = png_ptr->sig_bytes, /* SAFE, does not exceed 8 */
-       num_to_check = 8 - num_checked;
+   size_t num_checked = png_ptr->sig_bytes; /* SAFE, does not exceed 8 */
+   size_t num_to_check = 8 - num_checked;
 
    if (png_ptr->buffer_size < num_to_check)
    {
@@ -189,6 +189,7 @@
       png_crc_read(png_ptr, chunk_tag, 4);
       png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
       png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+      png_check_chunk_length(png_ptr, png_ptr->push_length);
       png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
    }
 
@@ -410,14 +411,14 @@
    {
       PNG_PUSH_SAVE_BUFFER_IF_FULL
       png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length,
-         PNG_HANDLE_CHUNK_AS_DEFAULT);
+          PNG_HANDLE_CHUNK_AS_DEFAULT);
    }
 
    png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
 }
 
 void PNGCBAPI
-png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, size_t length)
 {
    png_bytep ptr;
 
@@ -427,7 +428,7 @@
    ptr = buffer;
    if (png_ptr->save_buffer_size != 0)
    {
-      png_size_t save_size;
+      size_t save_size;
 
       if (length < png_ptr->save_buffer_size)
          save_size = length;
@@ -444,7 +445,7 @@
    }
    if (length != 0 && png_ptr->current_buffer_size != 0)
    {
-      png_size_t save_size;
+      size_t save_size;
 
       if (length < png_ptr->current_buffer_size)
          save_size = length;
@@ -466,7 +467,7 @@
    {
       if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
       {
-         png_size_t i, istop;
+         size_t i, istop;
          png_bytep sp;
          png_bytep dp;
 
@@ -481,7 +482,7 @@
    if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
        png_ptr->save_buffer_max)
    {
-      png_size_t new_max;
+      size_t new_max;
       png_bytep old_buffer;
 
       if (png_ptr->save_buffer_size > PNG_SIZE_MAX -
@@ -493,7 +494,7 @@
       new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
       old_buffer = png_ptr->save_buffer;
       png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr,
-          (png_size_t)new_max);
+          (size_t)new_max);
 
       if (png_ptr->save_buffer == NULL)
       {
@@ -501,7 +502,10 @@
          png_error(png_ptr, "Insufficient memory for save_buffer");
       }
 
-      memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
+      if (old_buffer)
+         memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
+      else if (png_ptr->save_buffer_size)
+         png_error(png_ptr, "save_buffer error");
       png_free(png_ptr, old_buffer);
       png_ptr->save_buffer_max = new_max;
    }
@@ -518,7 +522,7 @@
 
 void /* PRIVATE */
 png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer,
-   png_size_t buffer_length)
+    size_t buffer_length)
 {
    png_ptr->current_buffer = buffer;
    png_ptr->current_buffer_size = buffer_length;
@@ -558,7 +562,7 @@
 
    if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0)
    {
-      png_size_t save_size = png_ptr->save_buffer_size;
+      size_t save_size = png_ptr->save_buffer_size;
       png_uint_32 idat_size = png_ptr->idat_size;
 
       /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
@@ -568,7 +572,7 @@
        * will break on either 16-bit or 64-bit platforms.
        */
       if (idat_size < save_size)
-         save_size = (png_size_t)idat_size;
+         save_size = (size_t)idat_size;
 
       else
          idat_size = (png_uint_32)save_size;
@@ -585,7 +589,7 @@
 
    if (png_ptr->idat_size != 0 && png_ptr->current_buffer_size != 0)
    {
-      png_size_t save_size = png_ptr->current_buffer_size;
+      size_t save_size = png_ptr->current_buffer_size;
       png_uint_32 idat_size = png_ptr->idat_size;
 
       /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
@@ -594,7 +598,7 @@
        * larger - this cannot overflow.
        */
       if (idat_size < save_size)
-         save_size = (png_size_t)idat_size;
+         save_size = (size_t)idat_size;
 
       else
          idat_size = (png_uint_32)save_size;
@@ -621,7 +625,7 @@
 
 void /* PRIVATE */
 png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer,
-   png_size_t buffer_length)
+    size_t buffer_length)
 {
    /* The caller checks for a non-zero buffer length. */
    if (!(buffer_length > 0) || buffer == NULL)
@@ -681,7 +685,12 @@
             png_warning(png_ptr, "Truncated compressed data in IDAT");
 
          else
-            png_error(png_ptr, "Decompression error in IDAT");
+         {
+            if (ret == Z_DATA_ERROR)
+               png_benign_error(png_ptr, "IDAT: ADLER32 checksum mismatch");
+            else
+               png_error(png_ptr, "Decompression error in IDAT");
+         }
 
          /* Skip the check on unprocessed input */
          return;
@@ -779,7 +788,7 @@
    {
       if (png_ptr->pass < 6)
          png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
-            png_ptr->transformations);
+             png_ptr->transformations);
 
       switch (png_ptr->pass)
       {
@@ -963,20 +972,20 @@
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
 
    /* Start of interlace block */
-   static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+   static const png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
 
    /* Offset to next interlace block */
-   static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+   static const png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
 
    /* Start of interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+   static const png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
 
    /* Offset to next interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+   static const png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
 
    /* Height of interlace block.  This is not currently used - if you need
     * it, uncomment it here and in png.h
-   static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+   static const png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
    */
 #endif
 
@@ -1041,7 +1050,7 @@
 {
    if (png_ptr->row_fn != NULL)
       (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
-         (int)png_ptr->pass);
+          (int)png_ptr->pass);
 }
 
 #ifdef PNG_READ_INTERLACING_SUPPORTED
diff --git a/third_party/libpng16/pngprefix.h b/third_party/libpng16/pngprefix.h
index 8eab668..228f42f 100644
--- a/third_party/libpng16/pngprefix.h
+++ b/third_party/libpng16/pngprefix.h
@@ -9,9 +9,453 @@
 // when this version of libpng and chromium's version of it are
 // both simultaneously present.
 
-#define png_get_uint_32 PDFIUM_png_get_uint_32
-#define png_get_uint_16 PDFIUM_png_get_uint_16
+#define png_access_version_number PDFIUM_png_access_version_number
+#define png_app_error PDFIUM_png_app_error
+#define png_app_warning PDFIUM_png_app_warning
+#define png_ascii_from_fixed PDFIUM_png_ascii_from_fixed
+#define png_ascii_from_fp PDFIUM_png_ascii_from_fp
+#define png_benign_error PDFIUM_png_benign_error
+#define png_build_gamma_table PDFIUM_png_build_gamma_table
+#define png_build_grayscale_palette PDFIUM_png_build_grayscale_palette
+#define png_calculate_crc PDFIUM_png_calculate_crc
+#define png_calloc PDFIUM_png_calloc
+#define png_check_IHDR PDFIUM_png_check_IHDR
+#define png_check_chunk_length PDFIUM_png_check_chunk_length
+#define png_check_chunk_name PDFIUM_png_check_chunk_name
+#define png_check_fp_number PDFIUM_png_check_fp_number
+#define png_check_fp_string PDFIUM_png_check_fp_string
+#define png_check_keyword PDFIUM_png_check_keyword
+#define png_chunk_benign_error PDFIUM_png_chunk_benign_error
+#define png_chunk_error PDFIUM_png_chunk_error
+#define png_chunk_report PDFIUM_png_chunk_report
+#define png_chunk_unknown_handling PDFIUM_png_chunk_unknown_handling
+#define png_chunk_warning PDFIUM_png_chunk_warning
+#define png_colorspace_set_ICC PDFIUM_png_colorspace_set_ICC
+#define png_colorspace_set_chromaticities PDFIUM_png_colorspace_set_chromaticities
+#define png_colorspace_set_endpoints PDFIUM_png_colorspace_set_endpoints
+#define png_colorspace_set_gamma PDFIUM_png_colorspace_set_gamma
+#define png_colorspace_set_rgb_coefficients PDFIUM_png_colorspace_set_rgb_coefficients
+#define png_colorspace_set_sRGB PDFIUM_png_colorspace_set_sRGB
+#define png_colorspace_sync PDFIUM_png_colorspace_sync
+#define png_colorspace_sync_info PDFIUM_png_colorspace_sync_info
+#define png_combine_row PDFIUM_png_combine_row
+#define png_compress_IDAT PDFIUM_png_compress_IDAT
+#define png_convert_from_struct_tm PDFIUM_png_convert_from_struct_tm
+#define png_convert_from_time_t PDFIUM_png_convert_from_time_t
+#define png_convert_to_rfc1123 PDFIUM_png_convert_to_rfc1123
+#define png_convert_to_rfc1123_buffer PDFIUM_png_convert_to_rfc1123_buffer
+#define png_crc_error PDFIUM_png_crc_error
+#define png_crc_finish PDFIUM_png_crc_finish
+#define png_crc_read PDFIUM_png_crc_read
+#define png_create_info_struct PDFIUM_png_create_info_struct
+#define png_create_png_struct PDFIUM_png_create_png_struct
+#define png_create_read_struct PDFIUM_png_create_read_struct
+#define png_create_read_struct_2 PDFIUM_png_create_read_struct_2
+#define png_create_write_struct PDFIUM_png_create_write_struct
+#define png_create_write_struct_2 PDFIUM_png_create_write_struct_2
+#define png_data_freer PDFIUM_png_data_freer
+#define png_default_flush PDFIUM_png_default_flush
+#define png_default_read_data PDFIUM_png_default_read_data
+#define png_default_write_data PDFIUM_png_default_write_data
+#define png_destroy_gamma_table PDFIUM_png_destroy_gamma_table
+#define png_destroy_info_struct PDFIUM_png_destroy_info_struct
+#define png_destroy_png_struct PDFIUM_png_destroy_png_struct
+#define png_destroy_read_struct PDFIUM_png_destroy_read_struct
+#define png_destroy_write_struct PDFIUM_png_destroy_write_struct
+#define png_do_bgr PDFIUM_png_do_bgr
+#define png_do_check_palette_indexes PDFIUM_png_do_check_palette_indexes
+#define png_do_chop PDFIUM_png_do_chop
+#define png_do_compose PDFIUM_png_do_compose
+#define png_do_encode_alpha PDFIUM_png_do_encode_alpha
+#define png_do_expand PDFIUM_png_do_expand
+#define png_do_expand_16 PDFIUM_png_do_expand_16
+#define png_do_expand_palette PDFIUM_png_do_expand_palette
+#define png_do_expand_palette_neon_rgb PDFIUM_png_do_expand_palette_neon_rgb
+#define png_do_expand_palette_neon_rgba PDFIUM_png_do_expand_palette_neon_rgba
+#define png_do_gamma PDFIUM_png_do_gamma
+#define png_do_gray_to_rgb PDFIUM_png_do_gray_to_rgb
+#define png_do_invert PDFIUM_png_do_invert
+#define png_do_pack PDFIUM_png_do_pack
+#define png_do_packswap PDFIUM_png_do_packswap
+#define png_do_quantize PDFIUM_png_do_quantize
+#define png_do_read_filler PDFIUM_png_do_read_filler
+#define png_do_read_interlace PDFIUM_png_do_read_interlace
+#define png_do_read_intrapixel PDFIUM_png_do_read_intrapixel
+#define png_do_read_invert_alpha PDFIUM_png_do_read_invert_alpha
+#define png_do_read_swap_alpha PDFIUM_png_do_read_swap_alpha
+#define png_do_read_transformations PDFIUM_png_do_read_transformations
+#define png_do_rgb_to_gray PDFIUM_png_do_rgb_to_gray
+#define png_do_scale_16_to_8 PDFIUM_png_do_scale_16_to_8
+#define png_do_shift PDFIUM_png_do_shift
+#define png_do_strip_channel PDFIUM_png_do_strip_channel
+#define png_do_swap PDFIUM_png_do_swap
+#define png_do_unpack PDFIUM_png_do_unpack
+#define png_do_unshift PDFIUM_png_do_unshift
+#define png_do_write_interlace PDFIUM_png_do_write_interlace
+#define png_do_write_intrapixel PDFIUM_png_do_write_intrapixel
+#define png_do_write_invert_alpha PDFIUM_png_do_write_invert_alpha
+#define png_do_write_swap_alpha PDFIUM_png_do_write_swap_alpha
+#define png_do_write_transformations PDFIUM_png_do_write_transformations
+#define png_error PDFIUM_png_error
+#define png_fixed PDFIUM_png_fixed
+#define png_fixed_error PDFIUM_png_fixed_error
+#define png_flush PDFIUM_png_flush
+#define png_format_number PDFIUM_png_format_number
+#define png_formatted_warning PDFIUM_png_formatted_warning
+#define png_free PDFIUM_png_free
+#define png_free_buffer_list PDFIUM_png_free_buffer_list
+#define png_free_data PDFIUM_png_free_data
+#define png_free_default PDFIUM_png_free_default
+#define png_free_jmpbuf PDFIUM_png_free_jmpbuf
+#define png_gamma_16bit_correct PDFIUM_png_gamma_16bit_correct
+#define png_gamma_8bit_correct PDFIUM_png_gamma_8bit_correct
+#define png_gamma_correct PDFIUM_png_gamma_correct
+#define png_gamma_significant PDFIUM_png_gamma_significant
+#define png_get_IHDR PDFIUM_png_get_IHDR
+#define png_get_PLTE PDFIUM_png_get_PLTE
+#define png_get_bKGD PDFIUM_png_get_bKGD
+#define png_get_bit_depth PDFIUM_png_get_bit_depth
+#define png_get_cHRM PDFIUM_png_get_cHRM
+#define png_get_cHRM_XYZ PDFIUM_png_get_cHRM_XYZ
+#define png_get_cHRM_XYZ_fixed PDFIUM_png_get_cHRM_XYZ_fixed
+#define png_get_cHRM_fixed PDFIUM_png_get_cHRM_fixed
+#define png_get_channels PDFIUM_png_get_channels
+#define png_get_chunk_cache_max PDFIUM_png_get_chunk_cache_max
+#define png_get_chunk_malloc_max PDFIUM_png_get_chunk_malloc_max
+#define png_get_color_type PDFIUM_png_get_color_type
+#define png_get_compression_buffer_size PDFIUM_png_get_compression_buffer_size
+#define png_get_compression_type PDFIUM_png_get_compression_type
+#define png_get_copyright PDFIUM_png_get_copyright
+#define png_get_current_pass_number PDFIUM_png_get_current_pass_number
+#define png_get_current_row_number PDFIUM_png_get_current_row_number
+#define png_get_eXIf PDFIUM_png_get_eXIf
+#define png_get_eXIf_1 PDFIUM_png_get_eXIf_1
+#define png_get_error_ptr PDFIUM_png_get_error_ptr
+#define png_get_filter_type PDFIUM_png_get_filter_type
+#define png_get_gAMA PDFIUM_png_get_gAMA
+#define png_get_gAMA_fixed PDFIUM_png_get_gAMA_fixed
+#define png_get_hIST PDFIUM_png_get_hIST
+#define png_get_header_ver PDFIUM_png_get_header_ver
+#define png_get_header_version PDFIUM_png_get_header_version
+#define png_get_iCCP PDFIUM_png_get_iCCP
+#define png_get_image_height PDFIUM_png_get_image_height
+#define png_get_image_width PDFIUM_png_get_image_width
 #define png_get_int_32 PDFIUM_png_get_int_32
-#define png_get_int_16 PDFIUM_png_get_int_16
+#define png_get_interlace_type PDFIUM_png_get_interlace_type
+#define png_get_io_chunk_type PDFIUM_png_get_io_chunk_type
+#define png_get_io_ptr PDFIUM_png_get_io_ptr
+#define png_get_io_state PDFIUM_png_get_io_state
+#define png_get_libpng_ver PDFIUM_png_get_libpng_ver
+#define png_get_mem_ptr PDFIUM_png_get_mem_ptr
+#define png_get_oFFs PDFIUM_png_get_oFFs
+#define png_get_pCAL PDFIUM_png_get_pCAL
+#define png_get_pHYs PDFIUM_png_get_pHYs
+#define png_get_pHYs_dpi PDFIUM_png_get_pHYs_dpi
+#define png_get_palette_max PDFIUM_png_get_palette_max
+#define png_get_pixel_aspect_ratio PDFIUM_png_get_pixel_aspect_ratio
+#define png_get_pixel_aspect_ratio_fixed PDFIUM_png_get_pixel_aspect_ratio_fixed
+#define png_get_pixels_per_inch PDFIUM_png_get_pixels_per_inch
+#define png_get_pixels_per_meter PDFIUM_png_get_pixels_per_meter
+#define png_get_progressive_ptr PDFIUM_png_get_progressive_ptr
+#define png_get_rgb_to_gray_status PDFIUM_png_get_rgb_to_gray_status
+#define png_get_rowbytes PDFIUM_png_get_rowbytes
+#define png_get_rows PDFIUM_png_get_rows
+#define png_get_sBIT PDFIUM_png_get_sBIT
+#define png_get_sCAL PDFIUM_png_get_sCAL
+#define png_get_sCAL_fixed PDFIUM_png_get_sCAL_fixed
+#define png_get_sCAL_s PDFIUM_png_get_sCAL_s
+#define png_get_sPLT PDFIUM_png_get_sPLT
+#define png_get_sRGB PDFIUM_png_get_sRGB
+#define png_get_signature PDFIUM_png_get_signature
+#define png_get_tIME PDFIUM_png_get_tIME
+#define png_get_tRNS PDFIUM_png_get_tRNS
+#define png_get_text PDFIUM_png_get_text
+#define png_get_uint_16 PDFIUM_png_get_uint_16
+#define png_get_uint_31 PDFIUM_png_get_uint_31
+#define png_get_uint_32 PDFIUM_png_get_uint_32
+#define png_get_unknown_chunks PDFIUM_png_get_unknown_chunks
+#define png_get_user_chunk_ptr PDFIUM_png_get_user_chunk_ptr
+#define png_get_user_height_max PDFIUM_png_get_user_height_max
+#define png_get_user_transform_ptr PDFIUM_png_get_user_transform_ptr
+#define png_get_user_width_max PDFIUM_png_get_user_width_max
+#define png_get_valid PDFIUM_png_get_valid
+#define png_get_x_offset_inches PDFIUM_png_get_x_offset_inches
+#define png_get_x_offset_inches_fixed PDFIUM_png_get_x_offset_inches_fixed
+#define png_get_x_offset_microns PDFIUM_png_get_x_offset_microns
+#define png_get_x_offset_pixels PDFIUM_png_get_x_offset_pixels
+#define png_get_x_pixels_per_inch PDFIUM_png_get_x_pixels_per_inch
+#define png_get_x_pixels_per_meter PDFIUM_png_get_x_pixels_per_meter
+#define png_get_y_offset_inches PDFIUM_png_get_y_offset_inches
+#define png_get_y_offset_inches_fixed PDFIUM_png_get_y_offset_inches_fixed
+#define png_get_y_offset_microns PDFIUM_png_get_y_offset_microns
+#define png_get_y_offset_pixels PDFIUM_png_get_y_offset_pixels
+#define png_get_y_pixels_per_inch PDFIUM_png_get_y_pixels_per_inch
+#define png_get_y_pixels_per_meter PDFIUM_png_get_y_pixels_per_meter
+#define png_handle_IEND PDFIUM_png_handle_IEND
+#define png_handle_IHDR PDFIUM_png_handle_IHDR
+#define png_handle_PLTE PDFIUM_png_handle_PLTE
+#define png_handle_as_unknown PDFIUM_png_handle_as_unknown
+#define png_handle_bKGD PDFIUM_png_handle_bKGD
+#define png_handle_cHRM PDFIUM_png_handle_cHRM
+#define png_handle_gAMA PDFIUM_png_handle_gAMA
+#define png_handle_hIST PDFIUM_png_handle_hIST
+#define png_handle_iCCP PDFIUM_png_handle_iCCP
+#define png_handle_iTXt PDFIUM_png_handle_iTXt
+#define png_handle_oFFs PDFIUM_png_handle_oFFs
+#define png_handle_pCAL PDFIUM_png_handle_pCAL
+#define png_handle_pHYs PDFIUM_png_handle_pHYs
+#define png_handle_sBIT PDFIUM_png_handle_sBIT
+#define png_handle_sCAL PDFIUM_png_handle_sCAL
+#define png_handle_sPLT PDFIUM_png_handle_sPLT
+#define png_handle_sRGB PDFIUM_png_handle_sRGB
+#define png_handle_tEXt PDFIUM_png_handle_tEXt
+#define png_handle_tIME PDFIUM_png_handle_tIME
+#define png_handle_tRNS PDFIUM_png_handle_tRNS
+#define png_handle_unknown PDFIUM_png_handle_unknown
+#define png_handle_zTXt PDFIUM_png_handle_zTXt
+#define png_icc_check_header PDFIUM_png_icc_check_header
+#define png_icc_check_length PDFIUM_png_icc_check_length
+#define png_icc_check_tag_table PDFIUM_png_icc_check_tag_table
+#define png_icc_set_sRGB PDFIUM_png_icc_set_sRGB
+#define png_image_begin_read_from_file PDFIUM_png_image_begin_read_from_file
+#define png_image_begin_read_from_memory PDFIUM_png_image_begin_read_from_memory
+#define png_image_begin_read_from_stdio PDFIUM_png_image_begin_read_from_stdio
+#define png_image_error PDFIUM_png_image_error
+#define png_image_finish_read PDFIUM_png_image_finish_read
+#define png_image_free PDFIUM_png_image_free
+#define png_image_write_to_file PDFIUM_png_image_write_to_file
+#define png_image_write_to_memory PDFIUM_png_image_write_to_memory
+#define png_image_write_to_stdio PDFIUM_png_image_write_to_stdio
+#define png_info_init_3 PDFIUM_png_info_init_3
+#define png_init_filter_functions_neon PDFIUM_png_init_filter_functions_neon
+#define png_init_filter_functions_sse2 PDFIUM_png_init_filter_functions_sse2
+#define png_init_io PDFIUM_png_init_io
+#define png_init_read_transformations PDFIUM_png_init_read_transformations
+#define png_longjmp PDFIUM_png_longjmp
+#define png_malloc PDFIUM_png_malloc
+#define png_malloc_array PDFIUM_png_malloc_array
+#define png_malloc_base PDFIUM_png_malloc_base
+#define png_malloc_default PDFIUM_png_malloc_default
+#define png_malloc_warn PDFIUM_png_malloc_warn
+#define png_muldiv PDFIUM_png_muldiv
+#define png_muldiv_warn PDFIUM_png_muldiv_warn
+#define png_permit_mng_features PDFIUM_png_permit_mng_features
+#define png_process_IDAT_data PDFIUM_png_process_IDAT_data
+#define png_process_data PDFIUM_png_process_data
+#define png_process_data_pause PDFIUM_png_process_data_pause
+#define png_process_data_skip PDFIUM_png_process_data_skip
+#define png_process_some_data PDFIUM_png_process_some_data
+#define png_progressive_combine_row PDFIUM_png_progressive_combine_row
+#define png_push_check_crc PDFIUM_png_push_check_crc
+#define png_push_crc_finish PDFIUM_png_push_crc_finish
+#define png_push_crc_skip PDFIUM_png_push_crc_skip
+#define png_push_fill_buffer PDFIUM_png_push_fill_buffer
+#define png_push_handle_iTXt PDFIUM_png_push_handle_iTXt
+#define png_push_handle_tEXt PDFIUM_png_push_handle_tEXt
+#define png_push_handle_unknown PDFIUM_png_push_handle_unknown
+#define png_push_handle_zTXt PDFIUM_png_push_handle_zTXt
+#define png_push_have_end PDFIUM_png_push_have_end
+#define png_push_have_info PDFIUM_png_push_have_info
+#define png_push_have_row PDFIUM_png_push_have_row
+#define png_push_process_row PDFIUM_png_push_process_row
+#define png_push_read_IDAT PDFIUM_png_push_read_IDAT
+#define png_push_read_chunk PDFIUM_png_push_read_chunk
+#define png_push_read_end PDFIUM_png_push_read_end
+#define png_push_read_iTXt PDFIUM_png_push_read_iTXt
+#define png_push_read_sig PDFIUM_png_push_read_sig
+#define png_push_read_tEXt PDFIUM_png_push_read_tEXt
+#define png_push_read_zTXt PDFIUM_png_push_read_zTXt
+#define png_push_restore_buffer PDFIUM_png_push_restore_buffer
+#define png_push_save_buffer PDFIUM_png_push_save_buffer
+#define png_read_IDAT_data PDFIUM_png_read_IDAT_data
+#define png_read_chunk_header PDFIUM_png_read_chunk_header
+#define png_read_data PDFIUM_png_read_data
+#define png_read_end PDFIUM_png_read_end
+#define png_read_filter_row PDFIUM_png_read_filter_row
+#define png_read_filter_row_avg3_neon PDFIUM_png_read_filter_row_avg3_neon
+#define png_read_filter_row_avg3_sse2 PDFIUM_png_read_filter_row_avg3_sse2
+#define png_read_filter_row_avg4_neon PDFIUM_png_read_filter_row_avg4_neon
+#define png_read_filter_row_avg4_sse2 PDFIUM_png_read_filter_row_avg4_sse2
+#define png_read_filter_row_paeth3_neon PDFIUM_png_read_filter_row_paeth3_neon
+#define png_read_filter_row_paeth3_sse2 PDFIUM_png_read_filter_row_paeth3_sse2
+#define png_read_filter_row_paeth4_neon PDFIUM_png_read_filter_row_paeth4_neon
+#define png_read_filter_row_paeth4_sse2 PDFIUM_png_read_filter_row_paeth4_sse2
+#define png_read_filter_row_sub3_neon PDFIUM_png_read_filter_row_sub3_neon
+#define png_read_filter_row_sub3_sse2 PDFIUM_png_read_filter_row_sub3_sse2
+#define png_read_filter_row_sub4_neon PDFIUM_png_read_filter_row_sub4_neon
+#define png_read_filter_row_sub4_sse2 PDFIUM_png_read_filter_row_sub4_sse2
+#define png_read_filter_row_up_neon PDFIUM_png_read_filter_row_up_neon
+#define png_read_finish_IDAT PDFIUM_png_read_finish_IDAT
+#define png_read_finish_row PDFIUM_png_read_finish_row
+#define png_read_image PDFIUM_png_read_image
+#define png_read_info PDFIUM_png_read_info
+#define png_read_png PDFIUM_png_read_png
+#define png_read_push_finish_row PDFIUM_png_read_push_finish_row
+#define png_read_row PDFIUM_png_read_row
+#define png_read_rows PDFIUM_png_read_rows
+#define png_read_sig PDFIUM_png_read_sig
+#define png_read_start_row PDFIUM_png_read_start_row
+#define png_read_transform_info PDFIUM_png_read_transform_info
+#define png_read_update_info PDFIUM_png_read_update_info
+#define png_realloc_array PDFIUM_png_realloc_array
+#define png_reciprocal PDFIUM_png_reciprocal
+#define png_reciprocal2 PDFIUM_png_reciprocal2
+#define png_reset_crc PDFIUM_png_reset_crc
+#define png_reset_zstream PDFIUM_png_reset_zstream
+#define png_riffle_palette_rgba PDFIUM_png_riffle_palette_rgba
+#define png_sRGB_base PDFIUM_png_sRGB_base
+#define png_sRGB_delta PDFIUM_png_sRGB_delta
+#define png_sRGB_table PDFIUM_png_sRGB_table
+#define png_safe_error PDFIUM_png_safe_error
+#define png_safe_execute PDFIUM_png_safe_execute
+#define png_safe_warning PDFIUM_png_safe_warning
+#define png_safecat PDFIUM_png_safecat
+#define png_save_int_32 PDFIUM_png_save_int_32
+#define png_save_uint_16 PDFIUM_png_save_uint_16
+#define png_save_uint_32 PDFIUM_png_save_uint_32
+#define png_set_IHDR PDFIUM_png_set_IHDR
+#define png_set_PLTE PDFIUM_png_set_PLTE
+#define png_set_add_alpha PDFIUM_png_set_add_alpha
+#define png_set_alpha_mode PDFIUM_png_set_alpha_mode
+#define png_set_alpha_mode_fixed PDFIUM_png_set_alpha_mode_fixed
+#define png_set_bKGD PDFIUM_png_set_bKGD
+#define png_set_background PDFIUM_png_set_background
+#define png_set_background_fixed PDFIUM_png_set_background_fixed
+#define png_set_benign_errors PDFIUM_png_set_benign_errors
+#define png_set_bgr PDFIUM_png_set_bgr
+#define png_set_cHRM PDFIUM_png_set_cHRM
+#define png_set_cHRM_XYZ PDFIUM_png_set_cHRM_XYZ
+#define png_set_cHRM_XYZ_fixed PDFIUM_png_set_cHRM_XYZ_fixed
+#define png_set_cHRM_fixed PDFIUM_png_set_cHRM_fixed
+#define png_set_check_for_invalid_index PDFIUM_png_set_check_for_invalid_index
+#define png_set_chunk_cache_max PDFIUM_png_set_chunk_cache_max
+#define png_set_chunk_malloc_max PDFIUM_png_set_chunk_malloc_max
+#define png_set_compression_buffer_size PDFIUM_png_set_compression_buffer_size
+#define png_set_compression_level PDFIUM_png_set_compression_level
+#define png_set_compression_mem_level PDFIUM_png_set_compression_mem_level
+#define png_set_compression_method PDFIUM_png_set_compression_method
+#define png_set_compression_strategy PDFIUM_png_set_compression_strategy
+#define png_set_compression_window_bits PDFIUM_png_set_compression_window_bits
+#define png_set_crc_action PDFIUM_png_set_crc_action
+#define png_set_eXIf PDFIUM_png_set_eXIf
+#define png_set_eXIf_1 PDFIUM_png_set_eXIf_1
+#define png_set_error_fn PDFIUM_png_set_error_fn
+#define png_set_expand PDFIUM_png_set_expand
+#define png_set_expand_16 PDFIUM_png_set_expand_16
+#define png_set_expand_gray_1_2_4_to_8 PDFIUM_png_set_expand_gray_1_2_4_to_8
+#define png_set_filler PDFIUM_png_set_filler
+#define png_set_filter PDFIUM_png_set_filter
+#define png_set_filter_heuristics PDFIUM_png_set_filter_heuristics
+#define png_set_filter_heuristics_fixed PDFIUM_png_set_filter_heuristics_fixed
+#define png_set_flush PDFIUM_png_set_flush
+#define png_set_gAMA PDFIUM_png_set_gAMA
+#define png_set_gAMA_fixed PDFIUM_png_set_gAMA_fixed
+#define png_set_gamma PDFIUM_png_set_gamma
+#define png_set_gamma_fixed PDFIUM_png_set_gamma_fixed
+#define png_set_gray_to_rgb PDFIUM_png_set_gray_to_rgb
+#define png_set_hIST PDFIUM_png_set_hIST
+#define png_set_iCCP PDFIUM_png_set_iCCP
+#define png_set_interlace_handling PDFIUM_png_set_interlace_handling
+#define png_set_invalid PDFIUM_png_set_invalid
+#define png_set_invert_alpha PDFIUM_png_set_invert_alpha
+#define png_set_invert_mono PDFIUM_png_set_invert_mono
+#define png_set_keep_unknown_chunks PDFIUM_png_set_keep_unknown_chunks
+#define png_set_longjmp_fn PDFIUM_png_set_longjmp_fn
+#define png_set_mem_fn PDFIUM_png_set_mem_fn
+#define png_set_oFFs PDFIUM_png_set_oFFs
+#define png_set_option PDFIUM_png_set_option
+#define png_set_pCAL PDFIUM_png_set_pCAL
+#define png_set_pHYs PDFIUM_png_set_pHYs
+#define png_set_packing PDFIUM_png_set_packing
+#define png_set_packswap PDFIUM_png_set_packswap
+#define png_set_palette_to_rgb PDFIUM_png_set_palette_to_rgb
+#define png_set_progressive_read_fn PDFIUM_png_set_progressive_read_fn
+#define png_set_quantize PDFIUM_png_set_quantize
+#define png_set_read_fn PDFIUM_png_set_read_fn
+#define png_set_read_status_fn PDFIUM_png_set_read_status_fn
+#define png_set_read_user_chunk_fn PDFIUM_png_set_read_user_chunk_fn
+#define png_set_read_user_transform_fn PDFIUM_png_set_read_user_transform_fn
+#define png_set_rgb_to_gray PDFIUM_png_set_rgb_to_gray
+#define png_set_rgb_to_gray_fixed PDFIUM_png_set_rgb_to_gray_fixed
+#define png_set_rows PDFIUM_png_set_rows
+#define png_set_sBIT PDFIUM_png_set_sBIT
+#define png_set_sCAL PDFIUM_png_set_sCAL
+#define png_set_sCAL_fixed PDFIUM_png_set_sCAL_fixed
+#define png_set_sCAL_s PDFIUM_png_set_sCAL_s
+#define png_set_sPLT PDFIUM_png_set_sPLT
+#define png_set_sRGB PDFIUM_png_set_sRGB
+#define png_set_sRGB_gAMA_and_cHRM PDFIUM_png_set_sRGB_gAMA_and_cHRM
+#define png_set_scale_16 PDFIUM_png_set_scale_16
+#define png_set_shift PDFIUM_png_set_shift
+#define png_set_sig_bytes PDFIUM_png_set_sig_bytes
+#define png_set_strip_16 PDFIUM_png_set_strip_16
+#define png_set_strip_alpha PDFIUM_png_set_strip_alpha
+#define png_set_swap PDFIUM_png_set_swap
+#define png_set_swap_alpha PDFIUM_png_set_swap_alpha
+#define png_set_tIME PDFIUM_png_set_tIME
+#define png_set_tRNS PDFIUM_png_set_tRNS
+#define png_set_tRNS_to_alpha PDFIUM_png_set_tRNS_to_alpha
+#define png_set_text PDFIUM_png_set_text
+#define png_set_text_2 PDFIUM_png_set_text_2
+#define png_set_text_compression_level PDFIUM_png_set_text_compression_level
+#define png_set_text_compression_mem_level PDFIUM_png_set_text_compression_mem_level
+#define png_set_text_compression_method PDFIUM_png_set_text_compression_method
+#define png_set_text_compression_strategy PDFIUM_png_set_text_compression_strategy
+#define png_set_text_compression_window_bits PDFIUM_png_set_text_compression_window_bits
+#define png_set_unknown_chunk_location PDFIUM_png_set_unknown_chunk_location
+#define png_set_unknown_chunks PDFIUM_png_set_unknown_chunks
+#define png_set_user_limits PDFIUM_png_set_user_limits
+#define png_set_user_transform_info PDFIUM_png_set_user_transform_info
+#define png_set_write_fn PDFIUM_png_set_write_fn
+#define png_set_write_status_fn PDFIUM_png_set_write_status_fn
+#define png_set_write_user_transform_fn PDFIUM_png_set_write_user_transform_fn
+#define png_sig_cmp PDFIUM_png_sig_cmp
+#define png_start_read_image PDFIUM_png_start_read_image
+#define png_user_version_check PDFIUM_png_user_version_check
+#define png_warning PDFIUM_png_warning
+#define png_warning_parameter PDFIUM_png_warning_parameter
+#define png_warning_parameter_signed PDFIUM_png_warning_parameter_signed
+#define png_warning_parameter_unsigned PDFIUM_png_warning_parameter_unsigned
+#define png_write_IEND PDFIUM_png_write_IEND
+#define png_write_IHDR PDFIUM_png_write_IHDR
+#define png_write_PLTE PDFIUM_png_write_PLTE
+#define png_write_bKGD PDFIUM_png_write_bKGD
+#define png_write_cHRM_fixed PDFIUM_png_write_cHRM_fixed
+#define png_write_chunk PDFIUM_png_write_chunk
+#define png_write_chunk_data PDFIUM_png_write_chunk_data
+#define png_write_chunk_end PDFIUM_png_write_chunk_end
+#define png_write_chunk_start PDFIUM_png_write_chunk_start
+#define png_write_data PDFIUM_png_write_data
+#define png_write_end PDFIUM_png_write_end
+#define png_write_find_filter PDFIUM_png_write_find_filter
+#define png_write_finish_row PDFIUM_png_write_finish_row
+#define png_write_flush PDFIUM_png_write_flush
+#define png_write_gAMA_fixed PDFIUM_png_write_gAMA_fixed
+#define png_write_hIST PDFIUM_png_write_hIST
+#define png_write_iCCP PDFIUM_png_write_iCCP
+#define png_write_iTXt PDFIUM_png_write_iTXt
+#define png_write_image PDFIUM_png_write_image
+#define png_write_info PDFIUM_png_write_info
+#define png_write_info_before_PLTE PDFIUM_png_write_info_before_PLTE
+#define png_write_oFFs PDFIUM_png_write_oFFs
+#define png_write_pCAL PDFIUM_png_write_pCAL
+#define png_write_pHYs PDFIUM_png_write_pHYs
+#define png_write_png PDFIUM_png_write_png
+#define png_write_row PDFIUM_png_write_row
+#define png_write_rows PDFIUM_png_write_rows
+#define png_write_sBIT PDFIUM_png_write_sBIT
+#define png_write_sCAL_s PDFIUM_png_write_sCAL_s
+#define png_write_sPLT PDFIUM_png_write_sPLT
+#define png_write_sRGB PDFIUM_png_write_sRGB
+#define png_write_sig PDFIUM_png_write_sig
+#define png_write_start_row PDFIUM_png_write_start_row
+#define png_write_tEXt PDFIUM_png_write_tEXt
+#define png_write_tIME PDFIUM_png_write_tIME
+#define png_write_tRNS PDFIUM_png_write_tRNS
+#define png_write_zTXt PDFIUM_png_write_zTXt
+#define png_zalloc PDFIUM_png_zalloc
+#define png_zfree PDFIUM_png_zfree
+#define png_zlib_inflate PDFIUM_png_zlib_inflate
+#define png_zstream_error PDFIUM_png_zstream_error
 
 #endif  // PNGPREFIX_H
diff --git a/third_party/libpng16/pngpriv.h b/third_party/libpng16/pngpriv.h
index 6336713..583c26f 100644
--- a/third_party/libpng16/pngpriv.h
+++ b/third_party/libpng16/pngpriv.h
@@ -1,10 +1,10 @@
 
 /* pngpriv.h - private declarations for use inside libpng
  *
- * Last changed in libpng 1.6.22 [May 26, 2016]
- * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -35,7 +35,9 @@
  * Windows/Visual Studio) there is no effect; the OS specific tests below are
  * still required (as of 2011-05-02.)
  */
-#define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
+#ifndef _POSIX_SOURCE
+# define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
+#endif
 
 #ifndef PNG_VERSION_INFO_ONLY
 /* Standard library headers not required by png.h: */
@@ -172,7 +174,10 @@
 #     else /* !defined __ARM_NEON__ */
          /* The 'intrinsics' code simply won't compile without this -mfpu=neon:
           */
-#        define PNG_ARM_NEON_IMPLEMENTATION 2
+#        if !defined(__aarch64__)
+            /* The assembler code currently does not work on ARM64 */
+#          define PNG_ARM_NEON_IMPLEMENTATION 2
+#        endif /* __aarch64__ */
 #     endif /* __ARM_NEON__ */
 #  endif /* !PNG_ARM_NEON_IMPLEMENTATION */
 
@@ -182,6 +187,22 @@
 #  endif
 #endif /* PNG_ARM_NEON_OPT > 0 */
 
+#ifndef PNG_MIPS_MSA_OPT
+#  if defined(__mips_msa) && (__mips_isa_rev >= 5) && defined(PNG_ALIGNED_MEMORY_SUPPORTED)
+#     define PNG_MIPS_MSA_OPT 2
+#  else
+#     define PNG_MIPS_MSA_OPT 0
+#  endif
+#endif
+
+#ifndef PNG_POWERPC_VSX_OPT
+#  if defined(__PPC64__) && defined(__ALTIVEC__) && defined(__VSX__)
+#     define PNG_POWERPC_VSX_OPT 2
+#  else
+#     define PNG_POWERPC_VSX_OPT 0
+#  endif
+#endif
+
 #ifndef PNG_INTEL_SSE_OPT
 #   ifdef PNG_INTEL_SSE
       /* Only check for SSE if the build configuration has been modified to
@@ -192,7 +213,11 @@
        defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
        (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
 #         define PNG_INTEL_SSE_OPT 1
+#      else
+#         define PNG_INTEL_SSE_OPT 0
 #      endif
+#   else
+#      define PNG_INTEL_SSE_OPT 0
 #   endif
 #endif
 
@@ -216,8 +241,36 @@
 #   if PNG_INTEL_SSE_IMPLEMENTATION > 0
 #      define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_sse2
 #   endif
+#else
+#   define PNG_INTEL_SSE_IMPLEMENTATION 0
 #endif
 
+#if PNG_MIPS_MSA_OPT > 0
+#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_msa
+#  ifndef PNG_MIPS_MSA_IMPLEMENTATION
+#     if defined(__mips_msa)
+#        if defined(__clang__)
+#        elif defined(__GNUC__)
+#           if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
+#              define PNG_MIPS_MSA_IMPLEMENTATION 2
+#           endif /* no GNUC support */
+#        endif /* __GNUC__ */
+#     else /* !defined __mips_msa */
+#        define PNG_MIPS_MSA_IMPLEMENTATION 2
+#     endif /* __mips_msa */
+#  endif /* !PNG_MIPS_MSA_IMPLEMENTATION */
+
+#  ifndef PNG_MIPS_MSA_IMPLEMENTATION
+#     define PNG_MIPS_MSA_IMPLEMENTATION 1
+#  endif
+#endif /* PNG_MIPS_MSA_OPT > 0 */
+
+#if PNG_POWERPC_VSX_OPT > 0
+#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_vsx
+#  define PNG_POWERPC_VSX_IMPLEMENTATION 1
+#endif
+
+
 /* Is this a build of a DLL where compilation of the object modules requires
  * different preprocessor settings to those required for a simple library?  If
  * so PNG_BUILD_DLL must be set.
@@ -410,25 +463,6 @@
 #  define png_fixed_error(s1,s2) png_err(s1)
 #endif
 
-/* C allows up-casts from (void*) to any pointer and (const void*) to any
- * pointer to a const object.  C++ regards this as a type error and requires an
- * explicit, static, cast and provides the static_cast<> rune to ensure that
- * const is not cast away.
- */
-#ifdef __cplusplus
-#  define png_voidcast(type, value) static_cast<type>(value)
-#  define png_constcast(type, value) const_cast<type>(value)
-#  define png_aligncast(type, value) \
-   static_cast<type>(static_cast<void*>(value))
-#  define png_aligncastconst(type, value) \
-   static_cast<type>(static_cast<const void*>(value))
-#else
-#  define png_voidcast(type, value) (value)
-#  define png_constcast(type, value) ((type)(value))
-#  define png_aligncast(type, value) ((void*)(value))
-#  define png_aligncastconst(type, value) ((const void*)(value))
-#endif /* __cplusplus */
-
 /* Some fixed point APIs are still required even if not exported because
  * they get used by the corresponding floating point APIs.  This magic
  * deals with this:
@@ -443,6 +477,35 @@
 /* Other defines specific to compilers can go here.  Try to keep
  * them inside an appropriate ifdef/endif pair for portability.
  */
+
+/* C allows up-casts from (void*) to any pointer and (const void*) to any
+ * pointer to a const object.  C++ regards this as a type error and requires an
+ * explicit, static, cast and provides the static_cast<> rune to ensure that
+ * const is not cast away.
+ */
+#ifdef __cplusplus
+#  define png_voidcast(type, value) static_cast<type>(value)
+#  define png_constcast(type, value) const_cast<type>(value)
+#  define png_aligncast(type, value) \
+   static_cast<type>(static_cast<void*>(value))
+#  define png_aligncastconst(type, value) \
+   static_cast<type>(static_cast<const void*>(value))
+#else
+#  define png_voidcast(type, value) (value)
+#  ifdef _WIN64
+#     ifdef __GNUC__
+         typedef unsigned long long png_ptruint;
+#     else
+         typedef unsigned __int64 png_ptruint;
+#     endif
+#  else
+      typedef unsigned long png_ptruint;
+#  endif
+#  define png_constcast(type, value) ((type)(png_ptruint)(const void*)(value))
+#  define png_aligncast(type, value) ((void*)(value))
+#  define png_aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
 #if defined(PNG_FLOATING_POINT_SUPPORTED) ||\
     defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)
    /* png.c requires the following ANSI-C constants if the conversion of
@@ -456,10 +519,10 @@
 
 #  if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
     defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
-     /* We need to check that <math.h> hasn't already been included earlier
-      * as it seems it doesn't agree with <fp.h>, yet we should really use
-      * <fp.h> if possible.
-      */
+   /* We need to check that <math.h> hasn't already been included earlier
+    * as it seems it doesn't agree with <fp.h>, yet we should really use
+    * <fp.h> if possible.
+    */
 #    if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
 #      include <fp.h>
 #    endif
@@ -467,9 +530,9 @@
 #    include <math.h>
 #  endif
 #  if defined(_AMIGA) && defined(__SASC) && defined(_M68881)
-     /* Amiga SAS/C: We must include builtin FPU functions when compiling using
-      * MATH=68881
-      */
+   /* Amiga SAS/C: We must include builtin FPU functions when compiling using
+    * MATH=68881
+    */
 #    include <m68881.h>
 #  endif
 #endif
@@ -540,7 +603,8 @@
 /* This implicitly assumes alignment is always to a power of 2. */
 #ifdef png_alignof
 #  define png_isaligned(ptr, type)\
-   ((((const char*)ptr-(const char*)0) & (png_alignof(type)-1)) == 0)
+   (((type)((const char*)ptr-(const char*)0) & \
+   (type)(png_alignof(type)-1)) == 0)
 #else
 #  define png_isaligned(ptr, type) 0
 #endif
@@ -557,92 +621,92 @@
  * are defined in png.h because they need to be visible to applications
  * that call png_set_unknown_chunk().
  */
-/* #define PNG_HAVE_IHDR            0x01 (defined in png.h) */
-/* #define PNG_HAVE_PLTE            0x02 (defined in png.h) */
-#define PNG_HAVE_IDAT               0x04
-/* #define PNG_AFTER_IDAT           0x08 (defined in png.h) */
-#define PNG_HAVE_IEND               0x10
-                   /*               0x20 (unused) */
-                   /*               0x40 (unused) */
-                   /*               0x80 (unused) */
-#define PNG_HAVE_CHUNK_HEADER      0x100
-#define PNG_WROTE_tIME             0x200
-#define PNG_WROTE_INFO_BEFORE_PLTE 0x400
-#define PNG_BACKGROUND_IS_GRAY     0x800
-#define PNG_HAVE_PNG_SIGNATURE    0x1000
-#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
-                   /*             0x4000 (unused) */
-#define PNG_IS_READ_STRUCT        0x8000 /* Else is a write struct */
+/* #define PNG_HAVE_IHDR            0x01U (defined in png.h) */
+/* #define PNG_HAVE_PLTE            0x02U (defined in png.h) */
+#define PNG_HAVE_IDAT               0x04U
+/* #define PNG_AFTER_IDAT           0x08U (defined in png.h) */
+#define PNG_HAVE_IEND               0x10U
+                   /*               0x20U (unused) */
+                   /*               0x40U (unused) */
+                   /*               0x80U (unused) */
+#define PNG_HAVE_CHUNK_HEADER      0x100U
+#define PNG_WROTE_tIME             0x200U
+#define PNG_WROTE_INFO_BEFORE_PLTE 0x400U
+#define PNG_BACKGROUND_IS_GRAY     0x800U
+#define PNG_HAVE_PNG_SIGNATURE    0x1000U
+#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */
+                   /*             0x4000U (unused) */
+#define PNG_IS_READ_STRUCT        0x8000U /* Else is a write struct */
 
 /* Flags for the transformations the PNG library does on the image data */
-#define PNG_BGR                 0x0001
-#define PNG_INTERLACE           0x0002
-#define PNG_PACK                0x0004
-#define PNG_SHIFT               0x0008
-#define PNG_SWAP_BYTES          0x0010
-#define PNG_INVERT_MONO         0x0020
-#define PNG_QUANTIZE            0x0040
-#define PNG_COMPOSE             0x0080     /* Was PNG_BACKGROUND */
-#define PNG_BACKGROUND_EXPAND   0x0100
-#define PNG_EXPAND_16           0x0200     /* Added to libpng 1.5.2 */
-#define PNG_16_TO_8             0x0400     /* Becomes 'chop' in 1.5.4 */
-#define PNG_RGBA                0x0800
-#define PNG_EXPAND              0x1000
-#define PNG_GAMMA               0x2000
-#define PNG_GRAY_TO_RGB         0x4000
-#define PNG_FILLER              0x8000
-#define PNG_PACKSWAP           0x10000
-#define PNG_SWAP_ALPHA         0x20000
-#define PNG_STRIP_ALPHA        0x40000
-#define PNG_INVERT_ALPHA       0x80000
-#define PNG_USER_TRANSFORM    0x100000
-#define PNG_RGB_TO_GRAY_ERR   0x200000
-#define PNG_RGB_TO_GRAY_WARN  0x400000
-#define PNG_RGB_TO_GRAY       0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */
-#define PNG_ENCODE_ALPHA      0x800000 /* Added to libpng-1.5.4 */
-#define PNG_ADD_ALPHA        0x1000000 /* Added to libpng-1.2.7 */
-#define PNG_EXPAND_tRNS      0x2000000 /* Added to libpng-1.2.9 */
-#define PNG_SCALE_16_TO_8    0x4000000 /* Added to libpng-1.5.4 */
-                       /*    0x8000000 unused */
-                       /*   0x10000000 unused */
-                       /*   0x20000000 unused */
-                       /*   0x40000000 unused */
+#define PNG_BGR                 0x0001U
+#define PNG_INTERLACE           0x0002U
+#define PNG_PACK                0x0004U
+#define PNG_SHIFT               0x0008U
+#define PNG_SWAP_BYTES          0x0010U
+#define PNG_INVERT_MONO         0x0020U
+#define PNG_QUANTIZE            0x0040U
+#define PNG_COMPOSE             0x0080U    /* Was PNG_BACKGROUND */
+#define PNG_BACKGROUND_EXPAND   0x0100U
+#define PNG_EXPAND_16           0x0200U    /* Added to libpng 1.5.2 */
+#define PNG_16_TO_8             0x0400U    /* Becomes 'chop' in 1.5.4 */
+#define PNG_RGBA                0x0800U
+#define PNG_EXPAND              0x1000U
+#define PNG_GAMMA               0x2000U
+#define PNG_GRAY_TO_RGB         0x4000U
+#define PNG_FILLER              0x8000U
+#define PNG_PACKSWAP           0x10000U
+#define PNG_SWAP_ALPHA         0x20000U
+#define PNG_STRIP_ALPHA        0x40000U
+#define PNG_INVERT_ALPHA       0x80000U
+#define PNG_USER_TRANSFORM    0x100000U
+#define PNG_RGB_TO_GRAY_ERR   0x200000U
+#define PNG_RGB_TO_GRAY_WARN  0x400000U
+#define PNG_RGB_TO_GRAY       0x600000U /* two bits, RGB_TO_GRAY_ERR|WARN */
+#define PNG_ENCODE_ALPHA      0x800000U /* Added to libpng-1.5.4 */
+#define PNG_ADD_ALPHA        0x1000000U /* Added to libpng-1.2.7 */
+#define PNG_EXPAND_tRNS      0x2000000U /* Added to libpng-1.2.9 */
+#define PNG_SCALE_16_TO_8    0x4000000U /* Added to libpng-1.5.4 */
+                       /*    0x8000000U unused */
+                       /*   0x10000000U unused */
+                       /*   0x20000000U unused */
+                       /*   0x40000000U unused */
 /* Flags for png_create_struct */
-#define PNG_STRUCT_PNG   0x0001
-#define PNG_STRUCT_INFO  0x0002
+#define PNG_STRUCT_PNG   0x0001U
+#define PNG_STRUCT_INFO  0x0002U
 
 /* Flags for the png_ptr->flags rather than declaring a byte for each one */
-#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001
-#define PNG_FLAG_ZSTREAM_INITIALIZED      0x0002 /* Added to libpng-1.6.0 */
-                                  /*      0x0004    unused */
-#define PNG_FLAG_ZSTREAM_ENDED            0x0008 /* Added to libpng-1.6.0 */
-                                  /*      0x0010    unused */
-                                  /*      0x0020    unused */
-#define PNG_FLAG_ROW_INIT                 0x0040
-#define PNG_FLAG_FILLER_AFTER             0x0080
-#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100
-#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200
-#define PNG_FLAG_CRC_CRITICAL_USE         0x0400
-#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800
-#define PNG_FLAG_ASSUME_sRGB              0x1000 /* Added to libpng-1.5.4 */
-#define PNG_FLAG_OPTIMIZE_ALPHA           0x2000 /* Added to libpng-1.5.4 */
-#define PNG_FLAG_DETECT_UNINITIALIZED     0x4000 /* Added to libpng-1.5.4 */
-/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000 */
-/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS      0x10000 */
-#define PNG_FLAG_LIBRARY_MISMATCH        0x20000
-#define PNG_FLAG_STRIP_ERROR_NUMBERS     0x40000
-#define PNG_FLAG_STRIP_ERROR_TEXT        0x80000
-#define PNG_FLAG_BENIGN_ERRORS_WARN     0x100000 /* Added to libpng-1.4.0 */
-#define PNG_FLAG_APP_WARNINGS_WARN      0x200000 /* Added to libpng-1.6.0 */
-#define PNG_FLAG_APP_ERRORS_WARN        0x400000 /* Added to libpng-1.6.0 */
-                                  /*    0x800000    unused */
-                                  /*   0x1000000    unused */
-                                  /*   0x2000000    unused */
-                                  /*   0x4000000    unused */
-                                  /*   0x8000000    unused */
-                                  /*  0x10000000    unused */
-                                  /*  0x20000000    unused */
-                                  /*  0x40000000    unused */
+#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001U
+#define PNG_FLAG_ZSTREAM_INITIALIZED      0x0002U /* Added to libpng-1.6.0 */
+                                  /*      0x0004U    unused */
+#define PNG_FLAG_ZSTREAM_ENDED            0x0008U /* Added to libpng-1.6.0 */
+                                  /*      0x0010U    unused */
+                                  /*      0x0020U    unused */
+#define PNG_FLAG_ROW_INIT                 0x0040U
+#define PNG_FLAG_FILLER_AFTER             0x0080U
+#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100U
+#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200U
+#define PNG_FLAG_CRC_CRITICAL_USE         0x0400U
+#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800U
+#define PNG_FLAG_ASSUME_sRGB              0x1000U /* Added to libpng-1.5.4 */
+#define PNG_FLAG_OPTIMIZE_ALPHA           0x2000U /* Added to libpng-1.5.4 */
+#define PNG_FLAG_DETECT_UNINITIALIZED     0x4000U /* Added to libpng-1.5.4 */
+/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000U */
+/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS      0x10000U */
+#define PNG_FLAG_LIBRARY_MISMATCH        0x20000U
+#define PNG_FLAG_STRIP_ERROR_NUMBERS     0x40000U
+#define PNG_FLAG_STRIP_ERROR_TEXT        0x80000U
+#define PNG_FLAG_BENIGN_ERRORS_WARN     0x100000U /* Added to libpng-1.4.0 */
+#define PNG_FLAG_APP_WARNINGS_WARN      0x200000U /* Added to libpng-1.6.0 */
+#define PNG_FLAG_APP_ERRORS_WARN        0x400000U /* Added to libpng-1.6.0 */
+                                  /*    0x800000U    unused */
+                                  /*   0x1000000U    unused */
+                                  /*   0x2000000U    unused */
+                                  /*   0x4000000U    unused */
+                                  /*   0x8000000U    unused */
+                                  /*  0x10000000U    unused */
+                                  /*  0x20000000U    unused */
+                                  /*  0x40000000U    unused */
 
 #define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
                                      PNG_FLAG_CRC_ANCILLARY_NOWARN)
@@ -673,8 +737,26 @@
 /* Added to libpng-1.2.6 JB */
 #define PNG_ROWBYTES(pixel_bits, width) \
     ((pixel_bits) >= 8 ? \
-    ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \
-    (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) )
+    ((size_t)(width) * (((size_t)(pixel_bits)) >> 3)) : \
+    (( ((size_t)(width) * ((size_t)(pixel_bits))) + 7) >> 3) )
+
+/* This returns the number of trailing bits in the last byte of a row, 0 if the
+ * last byte is completely full of pixels.  It is, in principle, (pixel_bits x
+ * width) % 8, but that would overflow for large 'width'.  The second macro is
+ * the same except that it returns the number of unused bits in the last byte;
+ * (8-TRAILBITS), but 0 when TRAILBITS is 0.
+ *
+ * NOTE: these macros are intended to be self-evidently correct and never
+ * overflow on the assumption that pixel_bits is in the range 0..255.  The
+ * arguments are evaluated only once and they can be signed (e.g. as a result of
+ * the integral promotions).  The result of the expression always has type
+ * (png_uint_32), however the compiler always knows it is in the range 0..7.
+ */
+#define PNG_TRAILBITS(pixel_bits, width) \
+    (((pixel_bits) * ((width) % (png_uint_32)8)) % 8)
+
+#define PNG_PADBITS(pixel_bits, width) \
+    ((8 - PNG_TRAILBITS(pixel_bits, width)) % 8)
 
 /* PNG_OUT_OF_RANGE returns true if value is outside the range
  * ideal-delta..ideal+delta.  Each argument is evaluated twice.
@@ -769,6 +851,7 @@
 #define png_PLTE PNG_U32( 80,  76,  84,  69)
 #define png_bKGD PNG_U32( 98,  75,  71,  68)
 #define png_cHRM PNG_U32( 99,  72,  82,  77)
+#define png_eXIf PNG_U32(101,  88,  73, 102) /* registered July 2017 */
 #define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
 #define png_gAMA PNG_U32(103,  65,  77,  65)
 #define png_gIFg PNG_U32(103,  73,  70, 103)
@@ -843,7 +926,7 @@
     * PNG files the -I directives must match.
     *
     * The most likely explanation is that you passed a -I in CFLAGS. This will
-    * not work; all the preprocessor directories and in particular all the -I
+    * not work; all the preprocessor directives and in particular all the -I
     * directives must be in CPPFLAGS.
     */
 #endif
@@ -972,15 +1055,15 @@
  */
 
 PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr,
-    png_bytep data, png_size_t length),PNG_EMPTY);
+    png_bytep data, size_t length),PNG_EMPTY);
 
 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
 PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr,
-    png_bytep buffer, png_size_t length),PNG_EMPTY);
+    png_bytep buffer, size_t length),PNG_EMPTY);
 #endif
 
 PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr,
-    png_bytep data, png_size_t length),PNG_EMPTY);
+    png_bytep data, size_t length),PNG_EMPTY);
 
 #ifdef PNG_WRITE_FLUSH_SUPPORTED
 #  ifdef PNG_STDIO_SUPPORTED
@@ -994,7 +1077,7 @@
 
 /* Write the "data" buffer to whatever output you are using */
 PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr,
-    png_const_bytep data, png_size_t length),PNG_EMPTY);
+    png_const_bytep data, size_t length),PNG_EMPTY);
 
 /* Read and check the PNG file signature */
 PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr,
@@ -1006,7 +1089,7 @@
 
 /* Read data from whatever input you are using into the "data" buffer */
 PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data,
-    png_size_t length),PNG_EMPTY);
+    size_t length),PNG_EMPTY);
 
 /* Read bytes into buf, and update png_ptr->crc */
 PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,
@@ -1024,7 +1107,7 @@
  * since this is the maximum buffer size we can specify.
  */
 PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,
-   png_const_bytep ptr, png_size_t length),PNG_EMPTY);
+   png_const_bytep ptr, size_t length),PNG_EMPTY);
 
 #ifdef PNG_WRITE_FLUSH_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
@@ -1061,7 +1144,7 @@
 #ifdef PNG_WRITE_cHRM_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr,
     const png_xy *xy), PNG_EMPTY);
-    /* The xy value must have been previously validated */
+   /* The xy value must have been previously validated */
 #endif
 
 #ifdef PNG_WRITE_sRGB_SUPPORTED
@@ -1069,6 +1152,11 @@
     int intent),PNG_EMPTY);
 #endif
 
+#ifdef PNG_WRITE_eXIf_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr,
+    png_bytep exif, int num_exif),PNG_EMPTY);
+#endif
+
 #ifdef PNG_WRITE_iCCP_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
    png_const_charp name, png_const_bytep profile), PNG_EMPTY);
@@ -1102,7 +1190,7 @@
 /* Chunks that have keywords */
 #ifdef PNG_WRITE_tEXt_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
-   png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
+   png_const_charp key, png_const_charp text, size_t text_len),PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_zTXt_SUPPORTED
@@ -1210,6 +1298,7 @@
 PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop
     row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY);
 
+#if PNG_ARM_NEON_OPT > 0
 PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info,
     png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop
@@ -1224,7 +1313,43 @@
     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+#endif
 
+#if PNG_MIPS_MSA_OPT > 0
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_msa,(png_row_infop row_info,
+    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_msa,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_msa,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_msa,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_msa,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_msa,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_msa,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+#endif
+
+#if PNG_POWERPC_VSX_OPT > 0
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_vsx,(png_row_infop row_info,
+    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_vsx,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_vsx,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_vsx,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_vsx,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_vsx,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_vsx,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+#endif
+
+#if PNG_INTEL_SSE_IMPLEMENTATION > 0
 PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop
     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop
@@ -1237,6 +1362,7 @@
     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
     row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+#endif
 
 /* Choose the best filter to use and filter the row data */
 PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
@@ -1264,7 +1390,7 @@
 /* Initialize the row buffers, etc. */
 PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
 
-#if PNG_ZLIB_VERNUM >= 0x1240
+#if ZLIB_VERNUM >= 0x1240
 PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush),
       PNG_EMPTY);
 #  define PNG_INFLATE(pp, flush) png_zlib_inflate(pp, flush)
@@ -1330,6 +1456,11 @@
     png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
 #endif
 
+#ifdef PNG_READ_eXIf_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
 #ifdef PNG_READ_gAMA_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr,
     png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
@@ -1405,9 +1536,12 @@
     png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
 #endif
 
-PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr,
+PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr,
     png_uint_32 chunk_name),PNG_EMPTY);
 
+PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr,
+    png_uint_32 chunk_length),PNG_EMPTY);
+
 PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr,
     png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY);
    /* This is the function that gets called for unknown chunks.  The 'keep'
@@ -1449,10 +1583,10 @@
 PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr),
     PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr,
-    png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);
+    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,
-    png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);
+    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),
     PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr,
@@ -1462,7 +1596,7 @@
 PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,
    png_inforp info_ptr),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr,
-     png_bytep row),PNG_EMPTY);
+    png_bytep row),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr,
     png_inforp info_ptr),PNG_EMPTY);
 PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,
@@ -1501,13 +1635,13 @@
 
 PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr,
     png_inforp info_ptr), PNG_EMPTY);
-    /* Synchronize the info 'valid' flags with the colorspace */
+   /* Synchronize the info 'valid' flags with the colorspace */
 
 PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr,
     png_inforp info_ptr), PNG_EMPTY);
-    /* Copy the png_struct colorspace to the info_struct and call the above to
-     * synchronize the flags.  Checks for NULL info_ptr and does nothing.
-     */
+   /* Copy the png_struct colorspace to the info_struct and call the above to
+    * synchronize the flags.  Checks for NULL info_ptr and does nothing.
+    */
 #endif
 
 /* Added at libpng version 1.4.0 */
@@ -1722,13 +1856,13 @@
 
 #ifdef PNG_FLOATING_POINT_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr,
-   png_charp ascii, png_size_t size, double fp, unsigned int precision),
+   png_charp ascii, size_t size, double fp, unsigned int precision),
    PNG_EMPTY);
 #endif /* FLOATING_POINT */
 
 #ifdef PNG_FIXED_POINT_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
-   png_charp ascii, png_size_t size, png_fixed_point fp),PNG_EMPTY);
+   png_charp ascii, size_t size, png_fixed_point fp),PNG_EMPTY);
 #endif /* FIXED_POINT */
 #endif /* sCAL */
 
@@ -1821,7 +1955,7 @@
  * the problem character.)  This has not been tested within libpng.
  */
 PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,
-   png_size_t size, int *statep, png_size_tp whereami),PNG_EMPTY);
+   size_t size, int *statep, png_size_tp whereami),PNG_EMPTY);
 
 /* This is the same but it checks a complete string and returns true
  * only if it just contains a floating point number.  As of 1.5.4 this
@@ -1830,7 +1964,7 @@
  * for negative or zero values using the sticky flag.
  */
 PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
-   png_size_t size),PNG_EMPTY);
+   size_t size),PNG_EMPTY);
 #endif /* pCAL || sCAL */
 
 #if defined(PNG_GAMMA_SUPPORTED) ||\
@@ -1905,7 +2039,7 @@
    png_voidp   error_buf;           /* Always a jmp_buf at present. */
 
    png_const_bytep memory;          /* Memory buffer. */
-   png_size_t      size;            /* Size of the memory buffer. */
+   size_t          size;            /* Size of the memory buffer. */
 
    unsigned int for_write       :1; /* Otherwise it is a read structure */
    unsigned int owned_file      :1; /* We own the file in io_ptr */
@@ -1964,15 +2098,48 @@
     * the builder of libpng passes the definition of PNG_FILTER_OPTIMIZATIONS in
     * CFLAGS in place of CPPFLAGS *and* uses symbol prefixing.
     */
+#  if PNG_ARM_NEON_OPT > 0
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,
    (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+#endif
+
+#if PNG_MIPS_MSA_OPT > 0
+PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_msa,
+   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+#endif
+
+#  if PNG_INTEL_SSE_IMPLEMENTATION > 0
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_sse2,
    (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+#  endif
 #endif
 
 PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr,
    png_const_charp key, png_bytep new_key), PNG_EMPTY);
 
+#if PNG_ARM_NEON_IMPLEMENTATION == 1
+PNG_INTERNAL_FUNCTION(void,
+                      png_riffle_palette_neon,
+                      (png_structrp),
+                      PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int,
+                      png_do_expand_palette_rgba8_neon,
+                      (png_structrp,
+                       png_row_infop,
+                       png_const_bytep,
+                       const png_bytepp,
+                       const png_bytepp),
+                      PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int,
+                      png_do_expand_palette_rgb8_neon,
+                      (png_structrp,
+                       png_row_infop,
+                       png_const_bytep,
+                       const png_bytepp,
+                       const png_bytepp),
+                      PNG_EMPTY);
+#endif
+
 /* Maintainer: Put new private prototypes here ^ */
 
 #include "pngdebug.h"
diff --git a/third_party/libpng16/pngread.c b/third_party/libpng16/pngread.c
index 0572c20..8fa7d9f 100644
--- a/third_party/libpng16/pngread.c
+++ b/third_party/libpng16/pngread.c
@@ -1,10 +1,10 @@
 
 /* pngread.c - read a PNG file
  *
- * Last changed in libpng 1.6.17 [March 26, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -28,10 +28,10 @@
 {
 #ifndef PNG_USER_MEM_SUPPORTED
    png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
-      error_fn, warn_fn, NULL, NULL, NULL);
+        error_fn, warn_fn, NULL, NULL, NULL);
 #else
    return png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
-       warn_fn, NULL, NULL, NULL);
+        warn_fn, NULL, NULL, NULL);
 }
 
 /* Alternate create PNG structure for reading, and allocate any memory
@@ -43,7 +43,7 @@
     png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
 {
    png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
-      error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
+       error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
 #endif /* USER_MEM */
 
    if (png_ptr != NULL)
@@ -175,6 +175,11 @@
          png_handle_cHRM(png_ptr, info_ptr, length);
 #endif
 
+#ifdef PNG_READ_eXIf_SUPPORTED
+      else if (chunk_name == png_eXIf)
+         png_handle_eXIf(png_ptr, info_ptr, length);
+#endif
+
 #ifdef PNG_READ_gAMA_SUPPORTED
       else if (chunk_name == png_gAMA)
          png_handle_gAMA(png_ptr, info_ptr, length);
@@ -252,7 +257,7 @@
 
       else
          png_handle_unknown(png_ptr, info_ptr, length,
-            PNG_HANDLE_CHUNK_AS_DEFAULT);
+             PNG_HANDLE_CHUNK_AS_DEFAULT);
    }
 }
 #endif /* SEQUENTIAL_READ */
@@ -279,7 +284,7 @@
       /* New in 1.6.0 this avoids the bug of doing the initializations twice */
       else
          png_app_error(png_ptr,
-            "png_read_update_info/png_start_read_image: duplicate call");
+             "png_read_update_info/png_start_read_image: duplicate call");
    }
 }
 
@@ -302,7 +307,7 @@
       /* New in 1.6.0 this avoids the bug of doing the initializations twice */
       else
          png_app_error(png_ptr,
-            "png_start_read_image/png_read_update_info: duplicate call");
+             "png_start_read_image/png_read_update_info: duplicate call");
    }
 }
 #endif /* SEQUENTIAL_READ */
@@ -359,9 +364,9 @@
 
          for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
          {
-            png_uint_32 s0   = (*(rp    ) << 8) | *(rp + 1);
-            png_uint_32 s1   = (*(rp + 2) << 8) | *(rp + 3);
-            png_uint_32 s2   = (*(rp + 4) << 8) | *(rp + 5);
+            png_uint_32 s0   = (png_uint_32)(*(rp    ) << 8) | *(rp + 1);
+            png_uint_32 s1   = (png_uint_32)(*(rp + 2) << 8) | *(rp + 3);
+            png_uint_32 s2   = (png_uint_32)(*(rp + 4) << 8) | *(rp + 5);
             png_uint_32 red  = (s0 + s1 + 65536) & 0xffff;
             png_uint_32 blue = (s2 + s1 + 65536) & 0xffff;
             *(rp    ) = (png_byte)((red >> 8) & 0xff);
@@ -534,13 +539,14 @@
       png_error(png_ptr, "Invalid attempt to read row data");
 
    /* Fill the row with IDAT data: */
+   png_ptr->row_buf[0]=255; /* to force error if no data was found */
    png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);
 
    if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
    {
       if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)
          png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,
-            png_ptr->prev_row + 1, png_ptr->row_buf[0]);
+             png_ptr->prev_row + 1, png_ptr->row_buf[0]);
       else
          png_error(png_ptr, "bad adaptive filter value");
    }
@@ -584,7 +590,7 @@
    {
       if (png_ptr->pass < 6)
          png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
-            png_ptr->transformations);
+             png_ptr->transformations);
 
       if (dsp_row != NULL)
          png_combine_row(png_ptr, dsp_row, 1/*display*/);
@@ -719,7 +725,7 @@
           * but the caller should do it!
           */
          png_warning(png_ptr, "Interlace handling should be turned on when "
-            "using png_read_image");
+             "using png_read_image");
          /* Make sure this is set correctly */
          png_ptr->num_rows = png_ptr->height;
       }
@@ -779,8 +785,8 @@
 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
    /* Report invalid palette index; added at libng-1.5.10 */
    if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-      png_ptr->num_palette_max > png_ptr->num_palette)
-     png_benign_error(png_ptr, "Read palette index exceeding num_palette");
+       png_ptr->num_palette_max > png_ptr->num_palette)
+      png_benign_error(png_ptr, "Read palette index exceeding num_palette");
 #endif
 
    do
@@ -842,6 +848,11 @@
          png_handle_cHRM(png_ptr, info_ptr, length);
 #endif
 
+#ifdef PNG_READ_eXIf_SUPPORTED
+      else if (chunk_name == png_eXIf)
+         png_handle_eXIf(png_ptr, info_ptr, length);
+#endif
+
 #ifdef PNG_READ_gAMA_SUPPORTED
       else if (chunk_name == png_gAMA)
          png_handle_gAMA(png_ptr, info_ptr, length);
@@ -919,7 +930,7 @@
 
       else
          png_handle_unknown(png_ptr, info_ptr, length,
-            PNG_HANDLE_CHUNK_AS_DEFAULT);
+             PNG_HANDLE_CHUNK_AS_DEFAULT);
    } while ((png_ptr->mode & PNG_HAVE_IEND) == 0);
 }
 #endif /* SEQUENTIAL_READ */
@@ -983,6 +994,12 @@
    png_ptr->chunk_list = NULL;
 #endif
 
+#if defined(PNG_READ_EXPAND_SUPPORTED) && \
+    defined(PNG_ARM_NEON_IMPLEMENTATION)
+   png_free(png_ptr, png_ptr->riffled_palette);
+   png_ptr->riffled_palette = NULL;
+#endif
+
    /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error
     * callbacks are still set at this point.  They are required to complete the
     * destruction of the png_struct itself.
@@ -1030,8 +1047,7 @@
 #ifdef PNG_INFO_IMAGE_SUPPORTED
 void PNGAPI
 png_read_png(png_structrp png_ptr, png_inforp info_ptr,
-                           int transforms,
-                           voidp params)
+    int transforms, voidp params)
 {
    if (png_ptr == NULL || info_ptr == NULL)
       return;
@@ -1307,7 +1323,7 @@
          if (info_ptr != NULL)
          {
             png_controlp control = png_voidcast(png_controlp,
-               png_malloc_warn(png_ptr, (sizeof *control)));
+                png_malloc_warn(png_ptr, (sizeof *control)));
 
             if (control != NULL)
             {
@@ -1394,7 +1410,9 @@
    png_structrp png_ptr = image->opaque->png_ptr;
    png_inforp info_ptr = image->opaque->info_ptr;
 
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
    png_set_benign_errors(png_ptr, 1/*warn*/);
+#endif
    png_read_info(png_ptr, info_ptr);
 
    /* Do this the fast way; just read directly out of png_struct. */
@@ -1432,7 +1450,7 @@
             break;
 
          case PNG_COLOR_TYPE_PALETTE:
-            cmap_entries = png_ptr->num_palette;
+            cmap_entries = (png_uint_32)png_ptr->num_palette;
             break;
 
          default:
@@ -1470,12 +1488,12 @@
 
       else
          return png_image_error(image,
-            "png_image_begin_read_from_stdio: invalid argument");
+             "png_image_begin_read_from_stdio: invalid argument");
    }
 
    else if (image != NULL)
       return png_image_error(image,
-         "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION");
+          "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION");
 
    return 0;
 }
@@ -1508,19 +1526,19 @@
 
       else
          return png_image_error(image,
-            "png_image_begin_read_from_file: invalid argument");
+             "png_image_begin_read_from_file: invalid argument");
    }
 
    else if (image != NULL)
       return png_image_error(image,
-         "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION");
+          "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION");
 
    return 0;
 }
 #endif /* STDIO */
 
 static void PNGCBAPI
-png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need)
+png_image_memory_read(png_structp png_ptr, png_bytep out, size_t need)
 {
    if (png_ptr != NULL)
    {
@@ -1531,7 +1549,7 @@
          if (cp != NULL)
          {
             png_const_bytep memory = cp->memory;
-            png_size_t size = cp->size;
+            size_t size = cp->size;
 
             if (memory != NULL && size >= need)
             {
@@ -1550,7 +1568,7 @@
 }
 
 int PNGAPI png_image_begin_read_from_memory(png_imagep image,
-   png_const_voidp memory, png_size_t size)
+    png_const_voidp memory, size_t size)
 {
    if (image != NULL && image->version == PNG_IMAGE_VERSION)
    {
@@ -1573,12 +1591,12 @@
 
       else
          return png_image_error(image,
-            "png_image_begin_read_from_memory: invalid argument");
+             "png_image_begin_read_from_memory: invalid argument");
    }
 
    else if (image != NULL)
       return png_image_error(image,
-         "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION");
+          "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION");
 
    return 0;
 }
@@ -1609,7 +1627,7 @@
     * errors (which are unfortunately quite common.)
     */
    {
-         static PNG_CONST png_byte chunks_to_process[] = {
+         static const png_byte chunks_to_process[] = {
             98,  75,  71,  68, '\0',  /* bKGD */
             99,  72,  82,  77, '\0',  /* cHRM */
            103,  65,  77,  65, '\0',  /* gAMA */
@@ -1624,12 +1642,12 @@
         * IHDR, PLTE, tRNS, IDAT, and IEND chunks.
         */
        png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER,
-         NULL, -1);
+           NULL, -1);
 
        /* But do not ignore image data handling chunks */
        png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT,
-         chunks_to_process, (int)/*SAFE*/(sizeof chunks_to_process)/5);
-    }
+           chunks_to_process, (int)/*SAFE*/(sizeof chunks_to_process)/5);
+   }
 }
 
 #  define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p)
@@ -1696,7 +1714,7 @@
 #ifdef __GNUC__
       default:
          png_error(display->image->opaque->png_ptr,
-            "unexpected encoding (internal error)");
+             "unexpected encoding (internal error)");
 #endif
    }
 
@@ -1705,8 +1723,8 @@
 
 static png_uint_32
 png_colormap_compose(png_image_read_control *display,
-   png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha,
-   png_uint_32 background, int encoding)
+    png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha,
+    png_uint_32 background, int encoding)
 {
    /* The file value is composed on the background, the background has the given
     * encoding and so does the result, the file is encoded with P_FILE and the
@@ -1742,14 +1760,14 @@
  */
 static void
 png_create_colormap_entry(png_image_read_control *display,
-   png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue,
-   png_uint_32 alpha, int encoding)
+    png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue,
+    png_uint_32 alpha, int encoding)
 {
    png_imagep image = display->image;
-   const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
-      P_LINEAR : P_sRGB;
-   const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 &&
-      (red != green || green != blue);
+   int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
+       P_LINEAR : P_sRGB;
+   int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 &&
+       (red != green || green != blue);
 
    if (ip > 255)
       png_error(image->opaque->png_ptr, "color-map index out of range");
@@ -1857,13 +1875,13 @@
    /* Store the value. */
    {
 #     ifdef PNG_FORMAT_AFIRST_SUPPORTED
-         const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
+         int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
             (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;
 #     else
 #        define afirst 0
 #     endif
 #     ifdef PNG_FORMAT_BGR_SUPPORTED
-         const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
+         int bgr = (image->format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
 #     else
 #        define bgr 0
 #     endif
@@ -1882,7 +1900,7 @@
          {
             case 4:
                entry[afirst ? 0 : 3] = (png_uint_16)alpha;
-               /* FALL THROUGH */
+               /* FALLTHROUGH */
 
             case 3:
                if (alpha < 65535)
@@ -1904,7 +1922,7 @@
 
             case 2:
                entry[1 ^ afirst] = (png_uint_16)alpha;
-               /* FALL THROUGH */
+               /* FALLTHROUGH */
 
             case 1:
                if (alpha < 65535)
@@ -1933,6 +1951,7 @@
          {
             case 4:
                entry[afirst ? 0 : 3] = (png_byte)alpha;
+               /* FALLTHROUGH */
             case 3:
                entry[afirst + (2 ^ bgr)] = (png_byte)blue;
                entry[afirst + 1] = (png_byte)green;
@@ -1941,6 +1960,7 @@
 
             case 2:
                entry[1 ^ afirst] = (png_byte)alpha;
+               /* FALLTHROUGH */
             case 1:
                entry[afirst] = (png_byte)green;
                break;
@@ -1967,7 +1987,7 @@
    for (i=0; i<256; ++i)
       png_create_colormap_entry(display, i, i, i, i, 255, P_FILE);
 
-   return i;
+   return (int)i;
 }
 
 static int
@@ -1978,7 +1998,7 @@
    for (i=0; i<256; ++i)
       png_create_colormap_entry(display, i, i, i, i, 255, P_sRGB);
 
-   return i;
+   return (int)i;
 }
 #define PNG_GRAY_COLORMAP_ENTRIES 256
 
@@ -2029,10 +2049,10 @@
 
       for (g=0; g<6; ++g)
          png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51,
-            P_sRGB);
+             P_sRGB);
    }
 
-   return i;
+   return (int)i;
 }
 
 #define PNG_GA_COLORMAP_ENTRIES 256
@@ -2053,11 +2073,11 @@
 
          for (b=0; b<6; ++b)
             png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255,
-               P_sRGB);
+                P_sRGB);
       }
    }
 
-   return i;
+   return (int)i;
 }
 
 #define PNG_RGB_COLORMAP_ENTRIES 216
@@ -2071,11 +2091,11 @@
 {
    png_image_read_control *display =
       png_voidcast(png_image_read_control*, argument);
-   const png_imagep image = display->image;
+   png_imagep image = display->image;
 
-   const png_structrp png_ptr = image->opaque->png_ptr;
-   const png_uint_32 output_format = image->format;
-   const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
+   png_structrp png_ptr = image->opaque->png_ptr;
+   png_uint_32 output_format = image->format;
+   int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
       P_LINEAR : P_sRGB;
 
    unsigned int cmap_entries;
@@ -2105,7 +2125,7 @@
 
       else if (display->background == NULL /* no way to remove it */)
          png_error(png_ptr,
-            "a background color must be supplied to remove alpha/transparency");
+             "background color must be supplied to remove alpha/transparency");
 
       /* Get a copy of the background color (this avoids repeating the checks
        * below.)  The encoding is 8-bit sRGB or 16-bit linear, depending on the
@@ -2200,7 +2220,7 @@
                 */
                if (i != trans)
                   png_create_colormap_entry(display, i, val, val, val, 255,
-                     P_FILE/*8-bit with file gamma*/);
+                      P_FILE/*8-bit with file gamma*/);
 
                /* Else this entry is transparent.  The colors don't matter if
                 * there is an alpha channel (back_alpha == 0), but it does no
@@ -2212,7 +2232,7 @@
                 */
                else
                   png_create_colormap_entry(display, i, back_r, back_g, back_b,
-                     back_alpha, output_encoding);
+                      back_alpha, output_encoding);
             }
 
             /* We need libpng to preserve the original encoding. */
@@ -2250,7 +2270,7 @@
             if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
                png_error(png_ptr, "gray[16] color-map: too few entries");
 
-            cmap_entries = make_gray_colormap(display);
+            cmap_entries = (unsigned int)make_gray_colormap(display);
 
             if (png_ptr->num_trans > 0)
             {
@@ -2277,7 +2297,7 @@
                          * matches.
                          */
                         png_create_colormap_entry(display, gray, back_g, back_g,
-                           back_g, 65535, P_LINEAR);
+                            back_g, 65535, P_LINEAR);
                      }
 
                      /* The background passed to libpng, however, must be the
@@ -2291,8 +2311,8 @@
                       * doesn't.
                       */
                      png_set_background_fixed(png_ptr, &c,
-                        PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                        0/*gamma: not used*/);
+                         PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                         0/*gamma: not used*/);
 
                      output_processing = PNG_CMAP_NONE;
                      break;
@@ -2322,7 +2342,7 @@
                 * background color at full precision.
                 */
                png_create_colormap_entry(display, 254, back_r, back_g, back_b,
-                  back_alpha, output_encoding);
+                   back_alpha, output_encoding);
             }
 
             else
@@ -2348,7 +2368,7 @@
             if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
                png_error(png_ptr, "gray+alpha color-map: too few entries");
 
-            cmap_entries = make_ga_colormap(display);
+            cmap_entries = (unsigned int)make_ga_colormap(display);
 
             background_index = PNG_CMAP_GA_BACKGROUND;
             output_processing = PNG_CMAP_GA;
@@ -2382,7 +2402,7 @@
                if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
                   png_error(png_ptr, "gray-alpha color-map: too few entries");
 
-               cmap_entries = make_gray_colormap(display);
+               cmap_entries = (unsigned int)make_gray_colormap(display);
 
                if (output_encoding == P_LINEAR)
                {
@@ -2390,7 +2410,7 @@
 
                   /* And make sure the corresponding palette entry matches. */
                   png_create_colormap_entry(display, gray, back_g, back_g,
-                     back_g, 65535, P_LINEAR);
+                      back_g, 65535, P_LINEAR);
                }
 
                /* The background passed to libpng, however, must be the sRGB
@@ -2400,8 +2420,8 @@
                c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
 
                png_set_background_fixed(png_ptr, &c,
-                  PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                  0/*gamma: not used*/);
+                   PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                   0/*gamma: not used*/);
 
                output_processing = PNG_CMAP_NONE;
             }
@@ -2421,7 +2441,7 @@
                {
                   png_uint_32 gray = (i * 256 + 115) / 231;
                   png_create_colormap_entry(display, i++, gray, gray, gray,
-                     255, P_sRGB);
+                      255, P_sRGB);
                }
 
                /* NOTE: this preserves the full precision of the application
@@ -2430,13 +2450,13 @@
                background_index = i;
                png_create_colormap_entry(display, i++, back_r, back_g, back_b,
 #ifdef __COVERITY__
-                 /* Coverity claims that output_encoding cannot be 2 (P_LINEAR)
-                  * here.
-                  */ 255U,
+                   /* Coverity claims that output_encoding
+                    * cannot be 2 (P_LINEAR) here.
+                    */ 255U,
 #else
-                  output_encoding == P_LINEAR ? 65535U : 255U,
+                    output_encoding == P_LINEAR ? 65535U : 255U,
 #endif
-                  output_encoding);
+                    output_encoding);
 
                /* For non-opaque input composite on the sRGB background - this
                 * requires inverting the encoding for each component.  The input
@@ -2474,9 +2494,9 @@
                      png_uint_32 gray = png_sRGB_table[g*51] * alpha;
 
                      png_create_colormap_entry(display, i++,
-                        PNG_sRGB_FROM_LINEAR(gray + back_rx),
-                        PNG_sRGB_FROM_LINEAR(gray + back_gx),
-                        PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, P_sRGB);
+                         PNG_sRGB_FROM_LINEAR(gray + back_rx),
+                         PNG_sRGB_FROM_LINEAR(gray + back_gx),
+                         PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, P_sRGB);
                   }
                }
 
@@ -2502,7 +2522,7 @@
              * png_set_tRNS_to_alpha before png_set_background_fixed.
              */
             png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1,
-               -1);
+                -1);
             data_encoding = P_sRGB;
 
             /* The output will now be one or two 8-bit gray or gray+alpha
@@ -2521,7 +2541,7 @@
                if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
                   png_error(png_ptr, "rgb[ga] color-map: too few entries");
 
-               cmap_entries = make_ga_colormap(display);
+               cmap_entries = (unsigned int)make_ga_colormap(display);
                background_index = PNG_CMAP_GA_BACKGROUND;
                output_processing = PNG_CMAP_GA;
             }
@@ -2547,12 +2567,12 @@
                   png_ptr->num_trans > 0) &&
                   png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0)
                {
-                  cmap_entries = make_gray_file_colormap(display);
+                  cmap_entries = (unsigned int)make_gray_file_colormap(display);
                   data_encoding = P_FILE;
                }
 
                else
-                  cmap_entries = make_gray_colormap(display);
+                  cmap_entries = (unsigned int)make_gray_colormap(display);
 
                /* But if the input has alpha or transparency it must be removed
                 */
@@ -2578,13 +2598,13 @@
                         gray = png_sRGB_table[gray]; /* now P_LINEAR */
 
                      gray = PNG_DIV257(png_gamma_16bit_correct(gray,
-                        png_ptr->colorspace.gamma)); /* now P_FILE */
+                         png_ptr->colorspace.gamma)); /* now P_FILE */
 
                      /* And make sure the corresponding palette entry contains
                       * exactly the required sRGB value.
                       */
                      png_create_colormap_entry(display, gray, back_g, back_g,
-                        back_g, 0/*unused*/, output_encoding);
+                         back_g, 0/*unused*/, output_encoding);
                   }
 
                   else if (output_encoding == P_LINEAR)
@@ -2609,8 +2629,8 @@
                    */
                   expand_tRNS = 1;
                   png_set_background_fixed(png_ptr, &c,
-                     PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                     0/*gamma: not used*/);
+                      PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                      0/*gamma: not used*/);
                }
 
                output_processing = PNG_CMAP_NONE;
@@ -2640,11 +2660,11 @@
                   if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)
                      png_error(png_ptr, "rgb+alpha color-map: too few entries");
 
-                  cmap_entries = make_rgb_colormap(display);
+                  cmap_entries = (unsigned int)make_rgb_colormap(display);
 
                   /* Add a transparent entry. */
                   png_create_colormap_entry(display, cmap_entries, 255, 255,
-                     255, 0, P_sRGB);
+                      255, 0, P_sRGB);
 
                   /* This is stored as the background index for the processing
                    * algorithm.
@@ -2665,7 +2685,7 @@
                          */
                         for (b=0; b<256; b = (b << 1) | 0x7f)
                            png_create_colormap_entry(display, cmap_entries++,
-                              r, g, b, 128, P_sRGB);
+                               r, g, b, 128, P_sRGB);
                      }
                   }
 
@@ -2689,10 +2709,10 @@
                   if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)
                      png_error(png_ptr, "rgb-alpha color-map: too few entries");
 
-                  cmap_entries = make_rgb_colormap(display);
+                  cmap_entries = (unsigned int)make_rgb_colormap(display);
 
                   png_create_colormap_entry(display, cmap_entries, back_r,
-                        back_g, back_b, 0/*unused*/, output_encoding);
+                      back_g, back_b, 0/*unused*/, output_encoding);
 
                   if (output_encoding == P_LINEAR)
                   {
@@ -2714,9 +2734,9 @@
                    * index.
                    */
                   if (memcmp((png_const_bytep)display->colormap +
-                        sample_size * cmap_entries,
-                     (png_const_bytep)display->colormap +
-                        sample_size * PNG_RGB_INDEX(r,g,b),
+                      sample_size * cmap_entries,
+                      (png_const_bytep)display->colormap +
+                          sample_size * PNG_RGB_INDEX(r,g,b),
                      sample_size) != 0)
                   {
                      /* The background color must be added. */
@@ -2734,13 +2754,13 @@
                             */
                            for (b=0; b<256; b = (b << 1) | 0x7f)
                               png_create_colormap_entry(display, cmap_entries++,
-                                 png_colormap_compose(display, r, P_sRGB, 128,
-                                    back_r, output_encoding),
-                                 png_colormap_compose(display, g, P_sRGB, 128,
-                                    back_g, output_encoding),
-                                 png_colormap_compose(display, b, P_sRGB, 128,
-                                    back_b, output_encoding),
-                                 0/*unused*/, output_encoding);
+                                  png_colormap_compose(display, r, P_sRGB, 128,
+                                      back_r, output_encoding),
+                                  png_colormap_compose(display, g, P_sRGB, 128,
+                                      back_g, output_encoding),
+                                  png_colormap_compose(display, b, P_sRGB, 128,
+                                      back_b, output_encoding),
+                                  0/*unused*/, output_encoding);
                         }
                      }
 
@@ -2758,8 +2778,8 @@
                      c.blue = (png_uint_16)back_b;
 
                      png_set_background_fixed(png_ptr, &c,
-                        PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                        0/*gamma: not used*/);
+                         PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                         0/*gamma: not used*/);
 
                      output_processing = PNG_CMAP_RGB;
                   }
@@ -2774,7 +2794,7 @@
                if (PNG_RGB_COLORMAP_ENTRIES > image->colormap_entries)
                   png_error(png_ptr, "rgb color-map: too few entries");
 
-               cmap_entries = make_rgb_colormap(display);
+               cmap_entries = (unsigned int)make_rgb_colormap(display);
                output_processing = PNG_CMAP_RGB;
             }
          }
@@ -2788,7 +2808,7 @@
             unsigned int num_trans = png_ptr->num_trans;
             png_const_bytep trans = num_trans > 0 ? png_ptr->trans_alpha : NULL;
             png_const_colorp colormap = png_ptr->palette;
-            const int do_background = trans != NULL &&
+            int do_background = trans != NULL &&
                (output_format & PNG_FORMAT_FLAG_ALPHA) == 0;
             unsigned int i;
 
@@ -2798,11 +2818,11 @@
 
             output_processing = PNG_CMAP_NONE;
             data_encoding = P_FILE; /* Don't change from color-map indices */
-            cmap_entries = png_ptr->num_palette;
+            cmap_entries = (unsigned int)png_ptr->num_palette;
             if (cmap_entries > 256)
                cmap_entries = 256;
 
-            if (cmap_entries > image->colormap_entries)
+            if (cmap_entries > (unsigned int)image->colormap_entries)
                png_error(png_ptr, "palette color-map: too few entries");
 
             for (i=0; i < cmap_entries; ++i)
@@ -2811,7 +2831,7 @@
                {
                   if (trans[i] == 0)
                      png_create_colormap_entry(display, i, back_r, back_g,
-                        back_b, 0, output_encoding);
+                         back_b, 0, output_encoding);
 
                   else
                   {
@@ -2819,22 +2839,22 @@
                       * on the sRGB color in 'back'.
                       */
                      png_create_colormap_entry(display, i,
-                        png_colormap_compose(display, colormap[i].red, P_FILE,
-                           trans[i], back_r, output_encoding),
-                        png_colormap_compose(display, colormap[i].green, P_FILE,
-                           trans[i], back_g, output_encoding),
-                        png_colormap_compose(display, colormap[i].blue, P_FILE,
-                           trans[i], back_b, output_encoding),
-                        output_encoding == P_LINEAR ? trans[i] * 257U :
-                           trans[i],
-                        output_encoding);
+                         png_colormap_compose(display, colormap[i].red,
+                             P_FILE, trans[i], back_r, output_encoding),
+                         png_colormap_compose(display, colormap[i].green,
+                             P_FILE, trans[i], back_g, output_encoding),
+                         png_colormap_compose(display, colormap[i].blue,
+                             P_FILE, trans[i], back_b, output_encoding),
+                         output_encoding == P_LINEAR ? trans[i] * 257U :
+                             trans[i],
+                         output_encoding);
                   }
                }
 
                else
                   png_create_colormap_entry(display, i, colormap[i].red,
-                     colormap[i].green, colormap[i].blue,
-                     i < num_trans ? trans[i] : 255U, P_FILE/*8-bit*/);
+                      colormap[i].green, colormap[i].blue,
+                      i < num_trans ? trans[i] : 255U, P_FILE/*8-bit*/);
             }
 
             /* The PNG data may have indices packed in fewer than 8 bits, it
@@ -2860,7 +2880,7 @@
       case P_sRGB:
          /* Change to 8-bit sRGB */
          png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_sRGB);
-         /* FALL THROUGH */
+         /* FALLTHROUGH */
 
       case P_FILE:
          if (png_ptr->bit_depth > 8)
@@ -2914,7 +2934,7 @@
          png_error(png_ptr, "bad background index (internal error)");
    }
 
-   display->colormap_processing = output_processing;
+   display->colormap_processing = (int)output_processing;
 
    return 1/*ok*/;
 }
@@ -2924,7 +2944,7 @@
 png_image_read_and_map(png_voidp argument)
 {
    png_image_read_control *display = png_voidcast(png_image_read_control*,
-      argument);
+       argument);
    png_imagep image = display->image;
    png_structrp png_ptr = image->opaque->png_ptr;
    int passes;
@@ -3061,7 +3081,7 @@
 
                      if (alpha >= 196)
                         *outrow = PNG_RGB_INDEX(inrow[0], inrow[1],
-                           inrow[2]);
+                            inrow[2]);
 
                      else if (alpha < 64)
                         *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND;
@@ -3113,7 +3133,7 @@
 png_image_read_colormapped(png_voidp argument)
 {
    png_image_read_control *display = png_voidcast(png_image_read_control*,
-      argument);
+       argument);
    png_imagep image = display->image;
    png_controlp control = image->opaque;
    png_structrp png_ptr = control->png_ptr;
@@ -3178,8 +3198,7 @@
             image->colormap_entries == 244 /* 216 + 1 + 27 */)
             break;
 
-         /* goto bad_output; */
-         /* FALL THROUGH */
+         goto bad_output;
 
       default:
       bad_output:
@@ -3223,14 +3242,14 @@
 
    else
    {
-      png_alloc_size_t row_bytes = display->row_bytes;
+      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
 
       while (--passes >= 0)
       {
          png_uint_32      y = image->height;
          png_bytep        row = png_voidcast(png_bytep, display->first_row);
 
-         while (y-- > 0)
+         for (; y > 0; --y)
          {
             png_read_row(png_ptr, row, NULL);
             row += row_bytes;
@@ -3246,7 +3265,7 @@
 png_image_read_composite(png_voidp argument)
 {
    png_image_read_control *display = png_voidcast(png_image_read_control*,
-      argument);
+       argument);
    png_imagep image = display->image;
    png_structrp png_ptr = image->opaque->png_ptr;
    int passes;
@@ -3373,7 +3392,7 @@
 png_image_read_background(png_voidp argument)
 {
    png_image_read_control *display = png_voidcast(png_image_read_control*,
-      argument);
+       argument);
    png_imagep image = display->image;
    png_structrp png_ptr = image->opaque->png_ptr;
    png_inforp info_ptr = image->opaque->info_ptr;
@@ -3433,8 +3452,7 @@
 
             for (pass = 0; pass < passes; ++pass)
             {
-               png_bytep        row = png_voidcast(png_bytep,
-                                                   display->first_row);
+               png_bytep row = png_voidcast(png_bytep, display->first_row);
                unsigned int     startx, stepx, stepy;
                png_uint_32      y;
 
@@ -3462,7 +3480,7 @@
                   for (; y<height; y += stepy)
                   {
                      png_bytep inrow = png_voidcast(png_bytep,
-                        display->local_row);
+                         display->local_row);
                      png_bytep outrow = first_row + y * step_row;
                      png_const_bytep end_row = outrow + width;
 
@@ -3507,7 +3525,7 @@
                   for (; y<height; y += stepy)
                   {
                      png_bytep inrow = png_voidcast(png_bytep,
-                        display->local_row);
+                         display->local_row);
                      png_bytep outrow = first_row + y * step_row;
                      png_const_bytep end_row = outrow + width;
 
@@ -3554,13 +3572,14 @@
           */
          {
             png_uint_16p first_row = png_voidcast(png_uint_16p,
-               display->first_row);
+                display->first_row);
             /* The division by two is safe because the caller passed in a
              * stride which was multiplied by 2 (below) to get row_bytes.
              */
             ptrdiff_t    step_row = display->row_bytes / 2;
-            int preserve_alpha = (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;
-            unsigned int outchannels = 1+preserve_alpha;
+            unsigned int preserve_alpha = (image->format &
+                PNG_FORMAT_FLAG_ALPHA) != 0;
+            unsigned int outchannels = 1U+preserve_alpha;
             int swap_alpha = 0;
 
 #           ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
@@ -3604,7 +3623,7 @@
 
                   /* Read the row, which is packed: */
                   png_read_row(png_ptr, png_voidcast(png_bytep,
-                     display->local_row), NULL);
+                      display->local_row), NULL);
                   inrow = png_voidcast(png_const_uint_16p, display->local_row);
 
                   /* Now do the pre-multiplication on each pixel in this row.
@@ -3653,7 +3672,7 @@
 png_image_read_direct(png_voidp argument)
 {
    png_image_read_control *display = png_voidcast(png_image_read_control*,
-      argument);
+       argument);
    png_imagep image = display->image;
    png_structrp png_ptr = image->opaque->png_ptr;
    png_inforp info_ptr = image->opaque->info_ptr;
@@ -3704,7 +3723,7 @@
                do_local_background = 1/*maybe*/;
 
             png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE,
-               PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
+                PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
          }
 
          change &= ~PNG_FORMAT_FLAG_COLOR;
@@ -3746,7 +3765,13 @@
          mode = PNG_ALPHA_PNG;
          output_gamma = PNG_DEFAULT_sRGB;
       }
-
+      
+      if ((change & PNG_FORMAT_FLAG_ASSOCIATED_ALPHA) != 0)
+      {
+         mode = PNG_ALPHA_OPTIMIZED;
+         change &= ~PNG_FORMAT_FLAG_ASSOCIATED_ALPHA;
+      }
+      
       /* If 'do_local_background' is set check for the presence of gamma
        * correction; this is part of the work-round for the libpng bug
        * described above.
@@ -3763,7 +3788,7 @@
           * final value.
           */
          if (png_muldiv(&gtest, output_gamma, png_ptr->colorspace.gamma,
-               PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0)
+             PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0)
             do_local_background = 0;
 
          else if (mode == PNG_ALPHA_STANDARD)
@@ -3826,8 +3851,8 @@
                 * pixels.
                 */
                png_set_background_fixed(png_ptr, &c,
-                  PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
-                  0/*gamma: not used*/);
+                   PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                   0/*gamma: not used*/);
             }
 
             else /* compose on row: implemented below. */
@@ -3927,7 +3952,7 @@
        */
       if (linear != 0)
       {
-         PNG_CONST png_uint_16 le = 0x0001;
+         png_uint_16 le = 0x0001;
 
          if ((*(png_const_bytep) & le) != 0)
             png_set_swap(png_ptr);
@@ -3972,6 +3997,10 @@
       else if (do_local_compose != 0) /* internal error */
          png_error(png_ptr, "png_image_read: alpha channel lost");
 
+      if ((format & PNG_FORMAT_FLAG_ASSOCIATED_ALPHA) != 0) {
+         info_format |= PNG_FORMAT_FLAG_ASSOCIATED_ALPHA;
+      }
+
       if (info_ptr->bit_depth == 16)
          info_format |= PNG_FORMAT_FLAG_LINEAR;
 
@@ -4057,14 +4086,14 @@
 
    else
    {
-      png_alloc_size_t row_bytes = display->row_bytes;
+      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
 
       while (--passes >= 0)
       {
          png_uint_32      y = image->height;
          png_bytep        row = png_voidcast(png_bytep, display->first_row);
 
-         while (y-- > 0)
+         for (; y > 0; --y)
          {
             png_read_row(png_ptr, row, NULL);
             row += row_bytes;
@@ -4077,7 +4106,7 @@
 
 int PNGAPI
 png_image_finish_read(png_imagep image, png_const_colorp background,
-   void *buffer, png_int_32 row_stride, void *colormap)
+    void *buffer, png_int_32 row_stride, void *colormap)
 {
    if (image != NULL && image->version == PNG_IMAGE_VERSION)
    {
@@ -4085,29 +4114,52 @@
        * original PNG format because it may not occur in the output PNG format
        * and libpng deals with the issues of reading the original.
        */
-      const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
+      unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
 
-      if (image->width <= 0x7FFFFFFFU/channels) /* no overflow */
+      /* The following checks just the 'row_stride' calculation to ensure it
+       * fits in a signed 32-bit value.  Because channels/components can be
+       * either 1 or 2 bytes in size the length of a row can still overflow 32
+       * bits; this is just to verify that the 'row_stride' argument can be
+       * represented.
+       */
+      if (image->width <= 0x7fffffffU/channels) /* no overflow */
       {
          png_uint_32 check;
-         const png_uint_32 png_row_stride = image->width * channels;
+         png_uint_32 png_row_stride = image->width * channels;
 
          if (row_stride == 0)
             row_stride = (png_int_32)/*SAFE*/png_row_stride;
 
          if (row_stride < 0)
-            check = -row_stride;
+            check = (png_uint_32)(-row_stride);
 
          else
-            check = row_stride;
+            check = (png_uint_32)row_stride;
 
+         /* This verifies 'check', the absolute value of the actual stride
+          * passed in and detects overflow in the application calculation (i.e.
+          * if the app did actually pass in a non-zero 'row_stride'.
+          */
          if (image->opaque != NULL && buffer != NULL && check >= png_row_stride)
          {
             /* Now check for overflow of the image buffer calculation; this
              * limits the whole image size to 32 bits for API compatibility with
              * the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro.
+             *
+             * The PNG_IMAGE_BUFFER_SIZE macro is:
+             *
+             *    (PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)*height*(row_stride))
+             *
+             * And the component size is always 1 or 2, so make sure that the
+             * number of *bytes* that the application is saying are available
+             * does actually fit into a 32-bit number.
+             *
+             * NOTE: this will be changed in 1.7 because PNG_IMAGE_BUFFER_SIZE
+             * will be changed to use png_alloc_size_t; bigger images can be
+             * accommodated on 64-bit systems.
              */
-            if (image->height <= 0xFFFFFFFF/png_row_stride)
+            if (image->height <=
+                0xffffffffU/PNG_IMAGE_PIXEL_COMPONENT_SIZE(image->format)/check)
             {
                if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||
                   (image->colormap_entries > 0 && colormap != NULL))
@@ -4127,15 +4179,16 @@
                    * all the setup has already been done.
                    */
                   if ((image->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
-                     result = png_safe_execute(image,
-                                    png_image_read_colormap, &display) &&
-                              png_safe_execute(image,
-                                    png_image_read_colormapped, &display);
+                     result =
+                         png_safe_execute(image,
+                             png_image_read_colormap, &display) &&
+                             png_safe_execute(image,
+                             png_image_read_colormapped, &display);
 
                   else
                      result =
                         png_safe_execute(image,
-                              png_image_read_direct, &display);
+                            png_image_read_direct, &display);
 
                   png_image_free(image);
                   return result;
@@ -4143,27 +4196,27 @@
 
                else
                   return png_image_error(image,
-                     "png_image_finish_read[color-map]: no color-map");
+                      "png_image_finish_read[color-map]: no color-map");
             }
 
             else
                return png_image_error(image,
-                  "png_image_finish_read: image too large");
+                   "png_image_finish_read: image too large");
          }
 
          else
             return png_image_error(image,
-               "png_image_finish_read: invalid argument");
+                "png_image_finish_read: invalid argument");
       }
 
       else
          return png_image_error(image,
-            "png_image_finish_read: row_stride too large");
+             "png_image_finish_read: row_stride too large");
    }
 
    else if (image != NULL)
       return png_image_error(image,
-         "png_image_finish_read: damaged PNG_IMAGE_VERSION");
+          "png_image_finish_read: damaged PNG_IMAGE_VERSION");
 
    return 0;
 }
diff --git a/third_party/libpng16/pngrio.c b/third_party/libpng16/pngrio.c
index 5101d54..7946358 100644
--- a/third_party/libpng16/pngrio.c
+++ b/third_party/libpng16/pngrio.c
@@ -1,10 +1,10 @@
 
 /* pngrio.c - functions for data input
  *
- * Last changed in libpng 1.6.17 [March 26, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -29,7 +29,7 @@
  * to read more than 64K on a 16-bit machine.
  */
 void /* PRIVATE */
-png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length)
+png_read_data(png_structrp png_ptr, png_bytep data, size_t length)
 {
    png_debug1(4, "reading %d bytes", (int)length);
 
@@ -47,14 +47,14 @@
  * than changing the library.
  */
 void PNGCBAPI
-png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+png_default_read_data(png_structp png_ptr, png_bytep data, size_t length)
 {
-   png_size_t check;
+   size_t check;
 
    if (png_ptr == NULL)
       return;
 
-   /* fread() returns 0 on error, so it is OK to store this in a png_size_t
+   /* fread() returns 0 on error, so it is OK to store this in a size_t
     * instead of an int, which is what fread() actually returns.
     */
    check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr));
@@ -85,7 +85,7 @@
  */
 void PNGAPI
 png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr,
-   png_rw_ptr read_data_fn)
+    png_rw_ptr read_data_fn)
 {
    if (png_ptr == NULL)
       return;
diff --git a/third_party/libpng16/pngrtran.c b/third_party/libpng16/pngrtran.c
index 3138147..786e0de 100644
--- a/third_party/libpng16/pngrtran.c
+++ b/third_party/libpng16/pngrtran.c
@@ -1,10 +1,10 @@
 
 /* pngrtran.c - transforms the data in a row for PNG readers
  *
- * Last changed in libpng 1.6.22 [May 26, 2016]
- * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -18,6 +18,17 @@
 
 #include "pngpriv.h"
 
+#ifdef PNG_ARM_NEON_IMPLEMENTATION
+#  if PNG_ARM_NEON_IMPLEMENTATION == 1
+#    define PNG_ARM_NEON_INTRINSICS_AVAILABLE
+#    if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
+#      include <arm64_neon.h>
+#    else
+#      include <arm_neon.h>
+#    endif
+#  endif
+#endif
+
 #ifdef PNG_READ_SUPPORTED
 
 /* Set the action on getting a CRC error for an ancillary or critical chunk. */
@@ -48,7 +59,8 @@
 
       case PNG_CRC_WARN_DISCARD:    /* Not a valid action for critical data */
          png_warning(png_ptr,
-            "Can't discard critical data on CRC error");
+             "Can't discard critical data on CRC error");
+         /* FALLTHROUGH */
       case PNG_CRC_ERROR_QUIT:                                /* Error/quit */
 
       case PNG_CRC_DEFAULT:
@@ -101,7 +113,7 @@
    {
       if ((png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
          png_app_error(png_ptr,
-            "invalid after png_start_read_image or png_read_update_info");
+             "invalid after png_start_read_image or png_read_update_info");
 
       else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0)
          png_app_error(png_ptr, "invalid before the PNG header has been read");
@@ -209,7 +221,7 @@
 #if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
 static png_fixed_point
 translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,
-   int is_screen)
+    int is_screen)
 {
    /* Check for flag values.  The main reason for having the old Mac value as a
     * flag is that it is pretty near impossible to work out what the correct
@@ -273,7 +285,7 @@
 #ifdef PNG_READ_ALPHA_MODE_SUPPORTED
 void PNGFAPI
 png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
-   png_fixed_point output_gamma)
+    png_fixed_point output_gamma)
 {
    int compose = 0;
    png_fixed_point file_gamma;
@@ -291,7 +303,7 @@
     * who use the inverse of the gamma value accidentally!  Since some of these
     * values are reasonable this may have to be changed:
     *
-    * 1.6.x: changed from 0.07..3 to 0.01..100 (to accomodate the optimal 16-bit
+    * 1.6.x: changed from 0.07..3 to 0.01..100 (to accommodate the optimal 16-bit
     * gamma of 36, and its reciprocal.)
     */
    if (output_gamma < 1000 || output_gamma > 10000000)
@@ -377,7 +389,7 @@
 
       if ((png_ptr->transformations & PNG_COMPOSE) != 0)
          png_error(png_ptr,
-            "conflicting calls to set alpha mode and background");
+             "conflicting calls to set alpha mode and background");
 
       png_ptr->transformations |= PNG_COMPOSE;
    }
@@ -388,7 +400,7 @@
 png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma)
 {
    png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,
-      output_gamma));
+       output_gamma));
 }
 #  endif
 #endif
@@ -429,7 +441,7 @@
       int i;
 
       png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
-          (png_uint_32)(num_palette * (sizeof (png_byte))));
+          (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))));
       for (i = 0; i < num_palette; i++)
          png_ptr->quantize_index[i] = (png_byte)i;
    }
@@ -446,7 +458,7 @@
 
          /* Initialize an array to sort colors */
          png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,
-             (png_uint_32)(num_palette * (sizeof (png_byte))));
+             (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))));
 
          /* Initialize the quantize_sort array */
          for (i = 0; i < num_palette; i++)
@@ -580,9 +592,11 @@
 
          /* Initialize palette index arrays */
          png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
-             (png_uint_32)(num_palette * (sizeof (png_byte))));
+             (png_alloc_size_t)((png_uint_32)num_palette *
+             (sizeof (png_byte))));
          png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
-             (png_uint_32)(num_palette * (sizeof (png_byte))));
+             (png_alloc_size_t)((png_uint_32)num_palette *
+             (sizeof (png_byte))));
 
          /* Initialize the sort array */
          for (i = 0; i < num_palette; i++)
@@ -591,7 +605,7 @@
             png_ptr->palette_to_index[i] = (png_byte)i;
          }
 
-         hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 *
+         hash = (png_dsortpp)png_calloc(png_ptr, (png_alloc_size_t)(769 *
              (sizeof (png_dsortp))));
 
          num_new_palette = num_palette;
@@ -622,7 +636,7 @@
                   {
 
                      t = (png_dsortp)png_malloc_warn(png_ptr,
-                         (png_uint_32)(sizeof (png_dsort)));
+                         (png_alloc_size_t)(sizeof (png_dsort)));
 
                      if (t == NULL)
                          break;
@@ -744,12 +758,12 @@
       int num_red = (1 << PNG_QUANTIZE_RED_BITS);
       int num_green = (1 << PNG_QUANTIZE_GREEN_BITS);
       int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS);
-      png_size_t num_entries = ((png_size_t)1 << total_bits);
+      size_t num_entries = ((size_t)1 << total_bits);
 
       png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,
-          (png_uint_32)(num_entries * (sizeof (png_byte))));
+          (png_alloc_size_t)(num_entries * (sizeof (png_byte))));
 
-      distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
+      distance = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)(num_entries *
           (sizeof (png_byte))));
 
       memset(distance, 0xff, num_entries * (sizeof (png_byte)));
@@ -802,7 +816,7 @@
 #ifdef PNG_READ_GAMMA_SUPPORTED
 void PNGFAPI
 png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
-   png_fixed_point file_gamma)
+    png_fixed_point file_gamma)
 {
    png_debug(1, "in png_set_gamma_fixed");
 
@@ -844,7 +858,7 @@
 png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma)
 {
    png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma),
-      convert_gamma_value(png_ptr, file_gamma));
+       convert_gamma_value(png_ptr, file_gamma));
 }
 #  endif /* FLOATING_POINT */
 #endif /* READ_GAMMA */
@@ -990,7 +1004,7 @@
        * that it just worked and get a memory overwrite.
        */
       png_error(png_ptr,
-        "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");
+          "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");
 
       /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */
    }
@@ -1017,7 +1031,7 @@
       {
          if (red >= 0 && green >= 0)
             png_app_warning(png_ptr,
-               "ignoring out of range rgb_to_gray coefficients");
+                "ignoring out of range rgb_to_gray coefficients");
 
          /* Use the defaults, from the cHRM chunk if set, else the historical
           * values which are close to the sRGB/HDTV/ITU-Rec 709 values.  See
@@ -1026,7 +1040,7 @@
           * something has already provided a default.
           */
          if (png_ptr->rgb_to_gray_red_coeff == 0 &&
-            png_ptr->rgb_to_gray_green_coeff == 0)
+             png_ptr->rgb_to_gray_green_coeff == 0)
          {
             png_ptr->rgb_to_gray_red_coeff   = 6968;
             png_ptr->rgb_to_gray_green_coeff = 23434;
@@ -1043,10 +1057,10 @@
 
 void PNGAPI
 png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,
-   double green)
+    double green)
 {
    png_set_rgb_to_gray_fixed(png_ptr, error_action,
-      png_fixed(png_ptr, red, "rgb to gray red coefficient"),
+       png_fixed(png_ptr, red, "rgb to gray red coefficient"),
       png_fixed(png_ptr, green, "rgb to gray green coefficient"));
 }
 #endif /* FLOATING POINT */
@@ -1168,20 +1182,20 @@
              png_ptr->palette[png_ptr->background.index].blue;
 
 #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
-        if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
-        {
-           if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0)
-           {
-              /* Invert the alpha channel (in tRNS) unless the pixels are
-               * going to be expanded, in which case leave it for later
-               */
-              int i, istop = png_ptr->num_trans;
+         if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
+         {
+            if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0)
+            {
+               /* Invert the alpha channel (in tRNS) unless the pixels are
+                * going to be expanded, in which case leave it for later
+                */
+               int i, istop = png_ptr->num_trans;
 
-              for (i=0; i<istop; i++)
-                 png_ptr->trans_alpha[i] = (png_byte)(255 -
-                    png_ptr->trans_alpha[i]);
-           }
-        }
+               for (i = 0; i < istop; i++)
+                  png_ptr->trans_alpha[i] =
+                      (png_byte)(255 - png_ptr->trans_alpha[i]);
+            }
+         }
 #endif /* READ_INVERT_ALPHA */
       }
    } /* background expand and (therefore) no alpha association. */
@@ -1253,7 +1267,7 @@
             default:
 
             case 8:
-               /* FALL THROUGH (Already 8 bits) */
+               /* FALLTHROUGH */ /*  (Already 8 bits) */
 
             case 16:
                /* Already a full 16 bits */
@@ -1303,7 +1317,7 @@
       {
          if (png_ptr->screen_gamma != 0) /* screen set too */
             gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma,
-               png_ptr->screen_gamma);
+                png_ptr->screen_gamma);
 
          else
             /* Assume the output matches the input; a long time default behavior
@@ -1314,7 +1328,7 @@
 
       else if (png_ptr->screen_gamma != 0)
          /* The converse - assume the file matches the screen, note that this
-          * perhaps undesireable default can (from 1.5.4) be changed by calling
+          * perhaps undesirable default can (from 1.5.4) be changed by calling
           * png_set_alpha_mode (even if the alpha handling mode isn't required
           * or isn't changed from the default.)
           */
@@ -1584,7 +1598,7 @@
           */
          if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
             png_warning(png_ptr,
-               "libpng does not support gamma+background+rgb_to_gray");
+                "libpng does not support gamma+background+rgb_to_gray");
 
          if ((png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) != 0)
          {
@@ -1620,13 +1634,13 @@
                   case PNG_BACKGROUND_GAMMA_FILE:
                      g = png_reciprocal(png_ptr->colorspace.gamma);
                      gs = png_reciprocal2(png_ptr->colorspace.gamma,
-                        png_ptr->screen_gamma);
+                         png_ptr->screen_gamma);
                      break;
 
                   case PNG_BACKGROUND_GAMMA_UNIQUE:
                      g = png_reciprocal(png_ptr->background_gamma);
                      gs = png_reciprocal2(png_ptr->background_gamma,
-                        png_ptr->screen_gamma);
+                         png_ptr->screen_gamma);
                      break;
                   default:
                      g = PNG_FP_1;    /* back_1 */
@@ -1654,11 +1668,11 @@
                if (png_gamma_significant(g) != 0)
                {
                   back_1.red = png_gamma_8bit_correct(png_ptr->background.red,
-                     g);
+                      g);
                   back_1.green = png_gamma_8bit_correct(
-                     png_ptr->background.green, g);
+                      png_ptr->background.green, g);
                   back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue,
-                     g);
+                      g);
                }
 
                else
@@ -1729,7 +1743,7 @@
                case PNG_BACKGROUND_GAMMA_FILE:
                   g = png_reciprocal(png_ptr->colorspace.gamma);
                   gs = png_reciprocal2(png_ptr->colorspace.gamma,
-                     png_ptr->screen_gamma);
+                      png_ptr->screen_gamma);
                   break;
 
                case PNG_BACKGROUND_GAMMA_UNIQUE:
@@ -1882,7 +1896,7 @@
 
       png_ptr->transformations &= ~PNG_SHIFT;
 
-      /* significant bits can be in the range 1 to 7 for a meaninful result, if
+      /* significant bits can be in the range 1 to 7 for a meaningful result, if
        * the number of significant bits is 0 then no shift is done (this is an
        * error condition which is silently ignored.)
        */
@@ -2148,9 +2162,9 @@
       {
          case 1:
          {
-            png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);
-            png_bytep dp = row + (png_size_t)row_width - 1;
-            png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07);
+            png_bytep sp = row + (size_t)((row_width - 1) >> 3);
+            png_bytep dp = row + (size_t)row_width - 1;
+            png_uint_32 shift = 7U - ((row_width + 7U) & 0x07);
             for (i = 0; i < row_width; i++)
             {
                *dp = (png_byte)((*sp >> shift) & 0x01);
@@ -2172,9 +2186,9 @@
          case 2:
          {
 
-            png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);
-            png_bytep dp = row + (png_size_t)row_width - 1;
-            png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+            png_bytep sp = row + (size_t)((row_width - 1) >> 2);
+            png_bytep dp = row + (size_t)row_width - 1;
+            png_uint_32 shift = ((3U - ((row_width + 3U) & 0x03)) << 1);
             for (i = 0; i < row_width; i++)
             {
                *dp = (png_byte)((*sp >> shift) & 0x03);
@@ -2195,9 +2209,9 @@
 
          case 4:
          {
-            png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);
-            png_bytep dp = row + (png_size_t)row_width - 1;
-            png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+            png_bytep sp = row + (size_t)((row_width - 1) >> 1);
+            png_bytep dp = row + (size_t)row_width - 1;
+            png_uint_32 shift = ((1U - ((row_width + 1U) & 0x01)) << 2);
             for (i = 0; i < row_width; i++)
             {
                *dp = (png_byte)((*sp >> shift) & 0x0f);
@@ -2460,95 +2474,94 @@
 static void
 png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
 {
+   png_uint_32 row_width = row_info->width;
+
    png_debug(1, "in png_do_read_swap_alpha");
 
+   if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
    {
-      png_uint_32 row_width = row_info->width;
-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      /* This converts from RGBA to ARGB */
+      if (row_info->bit_depth == 8)
       {
-         /* This converts from RGBA to ARGB */
-         if (row_info->bit_depth == 8)
+         png_bytep sp = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_byte save;
+         png_uint_32 i;
+
+         for (i = 0; i < row_width; i++)
          {
-            png_bytep sp = row + row_info->rowbytes;
-            png_bytep dp = sp;
-            png_byte save;
-            png_uint_32 i;
-
-            for (i = 0; i < row_width; i++)
-            {
-               save = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = save;
-            }
+            save = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = save;
          }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-         /* This converts from RRGGBBAA to AARRGGBB */
-         else
-         {
-            png_bytep sp = row + row_info->rowbytes;
-            png_bytep dp = sp;
-            png_byte save[2];
-            png_uint_32 i;
-
-            for (i = 0; i < row_width; i++)
-            {
-               save[0] = *(--sp);
-               save[1] = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = save[0];
-               *(--dp) = save[1];
-            }
-         }
-#endif
       }
 
-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+#ifdef PNG_READ_16BIT_SUPPORTED
+      /* This converts from RRGGBBAA to AARRGGBB */
+      else
       {
-         /* This converts from GA to AG */
-         if (row_info->bit_depth == 8)
-         {
-            png_bytep sp = row + row_info->rowbytes;
-            png_bytep dp = sp;
-            png_byte save;
-            png_uint_32 i;
+         png_bytep sp = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_byte save[2];
+         png_uint_32 i;
 
-            for (i = 0; i < row_width; i++)
-            {
-               save = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = save;
-            }
+         for (i = 0; i < row_width; i++)
+         {
+            save[0] = *(--sp);
+            save[1] = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = save[0];
+            *(--dp) = save[1];
          }
+      }
+#endif
+   }
+
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+   {
+      /* This converts from GA to AG */
+      if (row_info->bit_depth == 8)
+      {
+         png_bytep sp = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_byte save;
+         png_uint_32 i;
+
+         for (i = 0; i < row_width; i++)
+         {
+            save = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = save;
+         }
+      }
 
 #ifdef PNG_READ_16BIT_SUPPORTED
-         /* This converts from GGAA to AAGG */
-         else
-         {
-            png_bytep sp = row + row_info->rowbytes;
-            png_bytep dp = sp;
-            png_byte save[2];
-            png_uint_32 i;
+      /* This converts from GGAA to AAGG */
+      else
+      {
+         png_bytep sp = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_byte save[2];
+         png_uint_32 i;
 
-            for (i = 0; i < row_width; i++)
-            {
-               save[0] = *(--sp);
-               save[1] = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = *(--sp);
-               *(--dp) = save[0];
-               *(--dp) = save[1];
-            }
+         for (i = 0; i < row_width; i++)
+         {
+            save[0] = *(--sp);
+            save[1] = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = save[0];
+            *(--dp) = save[1];
          }
-#endif
       }
+#endif
    }
 }
 #endif
@@ -2678,8 +2691,8 @@
          if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
          {
             /* This changes the data from G to GX */
-            png_bytep sp = row + (png_size_t)row_width;
-            png_bytep dp =  sp + (png_size_t)row_width;
+            png_bytep sp = row + (size_t)row_width;
+            png_bytep dp =  sp + (size_t)row_width;
             for (i = 1; i < row_width; i++)
             {
                *(--dp) = lo_filler;
@@ -2694,8 +2707,8 @@
          else
          {
             /* This changes the data from G to XG */
-            png_bytep sp = row + (png_size_t)row_width;
-            png_bytep dp = sp  + (png_size_t)row_width;
+            png_bytep sp = row + (size_t)row_width;
+            png_bytep dp = sp  + (size_t)row_width;
             for (i = 0; i < row_width; i++)
             {
                *(--dp) = *(--sp);
@@ -2713,8 +2726,8 @@
          if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
          {
             /* This changes the data from GG to GGXX */
-            png_bytep sp = row + (png_size_t)row_width * 2;
-            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            png_bytep sp = row + (size_t)row_width * 2;
+            png_bytep dp = sp  + (size_t)row_width * 2;
             for (i = 1; i < row_width; i++)
             {
                *(--dp) = lo_filler;
@@ -2732,8 +2745,8 @@
          else
          {
             /* This changes the data from GG to XXGG */
-            png_bytep sp = row + (png_size_t)row_width * 2;
-            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            png_bytep sp = row + (size_t)row_width * 2;
+            png_bytep dp = sp  + (size_t)row_width * 2;
             for (i = 0; i < row_width; i++)
             {
                *(--dp) = *(--sp);
@@ -2755,8 +2768,8 @@
          if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
          {
             /* This changes the data from RGB to RGBX */
-            png_bytep sp = row + (png_size_t)row_width * 3;
-            png_bytep dp = sp  + (png_size_t)row_width;
+            png_bytep sp = row + (size_t)row_width * 3;
+            png_bytep dp = sp  + (size_t)row_width;
             for (i = 1; i < row_width; i++)
             {
                *(--dp) = lo_filler;
@@ -2773,8 +2786,8 @@
          else
          {
             /* This changes the data from RGB to XRGB */
-            png_bytep sp = row + (png_size_t)row_width * 3;
-            png_bytep dp = sp + (png_size_t)row_width;
+            png_bytep sp = row + (size_t)row_width * 3;
+            png_bytep dp = sp + (size_t)row_width;
             for (i = 0; i < row_width; i++)
             {
                *(--dp) = *(--sp);
@@ -2794,8 +2807,8 @@
          if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
          {
             /* This changes the data from RRGGBB to RRGGBBXX */
-            png_bytep sp = row + (png_size_t)row_width * 6;
-            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            png_bytep sp = row + (size_t)row_width * 6;
+            png_bytep dp = sp  + (size_t)row_width * 2;
             for (i = 1; i < row_width; i++)
             {
                *(--dp) = lo_filler;
@@ -2817,8 +2830,8 @@
          else
          {
             /* This changes the data from RRGGBB to XXRRGGBB */
-            png_bytep sp = row + (png_size_t)row_width * 6;
-            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            png_bytep sp = row + (size_t)row_width * 6;
+            png_bytep dp = sp  + (size_t)row_width * 2;
             for (i = 0; i < row_width; i++)
             {
                *(--dp) = *(--sp);
@@ -2859,8 +2872,8 @@
          if (row_info->bit_depth == 8)
          {
             /* This changes G to RGB */
-            png_bytep sp = row + (png_size_t)row_width - 1;
-            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            png_bytep sp = row + (size_t)row_width - 1;
+            png_bytep dp = sp  + (size_t)row_width * 2;
             for (i = 0; i < row_width; i++)
             {
                *(dp--) = *sp;
@@ -2872,8 +2885,8 @@
          else
          {
             /* This changes GG to RRGGBB */
-            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
-            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            png_bytep sp = row + (size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (size_t)row_width * 4;
             for (i = 0; i < row_width; i++)
             {
                *(dp--) = *sp;
@@ -2891,8 +2904,8 @@
          if (row_info->bit_depth == 8)
          {
             /* This changes GA to RGBA */
-            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
-            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            png_bytep sp = row + (size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (size_t)row_width * 2;
             for (i = 0; i < row_width; i++)
             {
                *(dp--) = *(sp--);
@@ -2905,8 +2918,8 @@
          else
          {
             /* This changes GGAA to RRGGBBAA */
-            png_bytep sp = row + (png_size_t)row_width * 4 - 1;
-            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            png_bytep sp = row + (size_t)row_width * 4 - 1;
+            png_bytep dp = sp  + (size_t)row_width * 4;
             for (i = 0; i < row_width; i++)
             {
                *(dp--) = *(sp--);
@@ -2934,7 +2947,7 @@
  * using the equation given in Poynton's ColorFAQ of 1998-01-04 at
  * <http://www.inforamp.net/~poynton/>  (THIS LINK IS DEAD June 2008 but
  * versions dated 1998 through November 2002 have been archived at
- * http://web.archive.org/web/20000816232553/http://www.inforamp.net/
+ * https://web.archive.org/web/20000816232553/www.inforamp.net/
  * ~poynton/notes/colour_and_gamma/ColorFAQ.txt )
  * Charles Poynton poynton at poynton.com
  *
@@ -2977,14 +2990,13 @@
  *  values this results in an implicit assumption that the original PNG RGB
  *  values were linear.
  *
- *  Other integer coefficents can be used via png_set_rgb_to_gray().  Because
+ *  Other integer coefficients can be used via png_set_rgb_to_gray().  Because
  *  the API takes just red and green coefficients the blue coefficient is
  *  calculated to make the sum 32768.  This will result in different rounding
  *  to that used above.
  */
 static int
 png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row)
-
 {
    int rgb_error = 0;
 
@@ -2993,12 +3005,11 @@
    if ((row_info->color_type & PNG_COLOR_MASK_PALETTE) == 0 &&
        (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
    {
-      PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
-      PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
-      PNG_CONST png_uint_32 bc = 32768 - rc - gc;
-      PNG_CONST png_uint_32 row_width = row_info->width;
-      PNG_CONST int have_alpha =
-         (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0;
+      png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
+      png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
+      png_uint_32 bc = 32768 - rc - gc;
+      png_uint_32 row_width = row_info->width;
+      int have_alpha = (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0;
 
       if (row_info->bit_depth == 8)
       {
@@ -3206,414 +3217,229 @@
 
    png_debug(1, "in png_do_compose");
 
+   switch (row_info->color_type)
    {
-      switch (row_info->color_type)
+      case PNG_COLOR_TYPE_GRAY:
       {
-         case PNG_COLOR_TYPE_GRAY:
+         switch (row_info->bit_depth)
          {
-            switch (row_info->bit_depth)
+            case 1:
             {
-               case 1:
+               sp = row;
+               shift = 7;
+               for (i = 0; i < row_width; i++)
                {
-                  sp = row;
-                  shift = 7;
-                  for (i = 0; i < row_width; i++)
+                  if ((png_uint_16)((*sp >> shift) & 0x01)
+                     == png_ptr->trans_color.gray)
                   {
-                     if ((png_uint_16)((*sp >> shift) & 0x01)
-                        == png_ptr->trans_color.gray)
-                     {
-                        unsigned int tmp = *sp & (0x7f7f >> (7 - shift));
-                        tmp |= png_ptr->background.gray << shift;
-                        *sp = (png_byte)(tmp & 0xff);
-                     }
-
-                     if (shift == 0)
-                     {
-                        shift = 7;
-                        sp++;
-                     }
-
-                     else
-                        shift--;
+                     unsigned int tmp = *sp & (0x7f7f >> (7 - shift));
+                     tmp |=
+                         (unsigned int)(png_ptr->background.gray << shift);
+                     *sp = (png_byte)(tmp & 0xff);
                   }
-                  break;
-               }
 
-               case 2:
-               {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-                  if (gamma_table != NULL)
+                  if (shift == 0)
                   {
-                     sp = row;
-                     shift = 6;
-                     for (i = 0; i < row_width; i++)
-                     {
-                        if ((png_uint_16)((*sp >> shift) & 0x03)
-                            == png_ptr->trans_color.gray)
-                        {
-                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
-                           tmp |= png_ptr->background.gray << shift;
-                           *sp = (png_byte)(tmp & 0xff);
-                        }
-
-                        else
-                        {
-                           unsigned int p = (*sp >> shift) & 0x03;
-                           unsigned int g = (gamma_table [p | (p << 2) |
-                               (p << 4) | (p << 6)] >> 6) & 0x03;
-                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
-                           tmp |= g << shift;
-                           *sp = (png_byte)(tmp & 0xff);
-                        }
-
-                        if (shift == 0)
-                        {
-                           shift = 6;
-                           sp++;
-                        }
-
-                        else
-                           shift -= 2;
-                     }
+                     shift = 7;
+                     sp++;
                   }
 
                   else
-#endif
-                  {
-                     sp = row;
-                     shift = 6;
-                     for (i = 0; i < row_width; i++)
-                     {
-                        if ((png_uint_16)((*sp >> shift) & 0x03)
-                            == png_ptr->trans_color.gray)
-                        {
-                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
-                           tmp |= png_ptr->background.gray << shift;
-                           *sp = (png_byte)(tmp & 0xff);
-                        }
-
-                        if (shift == 0)
-                        {
-                           shift = 6;
-                           sp++;
-                        }
-
-                        else
-                           shift -= 2;
-                     }
-                  }
-                  break;
+                     shift--;
                }
-
-               case 4:
-               {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-                  if (gamma_table != NULL)
-                  {
-                     sp = row;
-                     shift = 4;
-                     for (i = 0; i < row_width; i++)
-                     {
-                        if ((png_uint_16)((*sp >> shift) & 0x0f)
-                            == png_ptr->trans_color.gray)
-                        {
-                           unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
-                           tmp |= png_ptr->background.gray << shift;
-                           *sp = (png_byte)(tmp & 0xff);
-                        }
-
-                        else
-                        {
-                           unsigned int p = (*sp >> shift) & 0x0f;
-                           unsigned int g = (gamma_table[p | (p << 4)] >> 4) &
-                              0x0f;
-                           unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
-                           tmp |= g << shift;
-                           *sp = (png_byte)(tmp & 0xff);
-                        }
-
-                        if (shift == 0)
-                        {
-                           shift = 4;
-                           sp++;
-                        }
-
-                        else
-                           shift -= 4;
-                     }
-                  }
-
-                  else
-#endif
-                  {
-                     sp = row;
-                     shift = 4;
-                     for (i = 0; i < row_width; i++)
-                     {
-                        if ((png_uint_16)((*sp >> shift) & 0x0f)
-                            == png_ptr->trans_color.gray)
-                        {
-                           unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
-                           tmp |= png_ptr->background.gray << shift;
-                           *sp = (png_byte)(tmp & 0xff);
-                        }
-
-                        if (shift == 0)
-                        {
-                           shift = 4;
-                           sp++;
-                        }
-
-                        else
-                           shift -= 4;
-                     }
-                  }
-                  break;
-               }
-
-               case 8:
-               {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-                  if (gamma_table != NULL)
-                  {
-                     sp = row;
-                     for (i = 0; i < row_width; i++, sp++)
-                     {
-                        if (*sp == png_ptr->trans_color.gray)
-                           *sp = (png_byte)png_ptr->background.gray;
-
-                        else
-                           *sp = gamma_table[*sp];
-                     }
-                  }
-                  else
-#endif
-                  {
-                     sp = row;
-                     for (i = 0; i < row_width; i++, sp++)
-                     {
-                        if (*sp == png_ptr->trans_color.gray)
-                           *sp = (png_byte)png_ptr->background.gray;
-                     }
-                  }
-                  break;
-               }
-
-               case 16:
-               {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-                  if (gamma_16 != NULL)
-                  {
-                     sp = row;
-                     for (i = 0; i < row_width; i++, sp += 2)
-                     {
-                        png_uint_16 v;
-
-                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                        if (v == png_ptr->trans_color.gray)
-                        {
-                           /* Background is already in screen gamma */
-                           *sp = (png_byte)((png_ptr->background.gray >> 8)
-                                & 0xff);
-                           *(sp + 1) = (png_byte)(png_ptr->background.gray
-                                & 0xff);
-                        }
-
-                        else
-                        {
-                           v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                           *sp = (png_byte)((v >> 8) & 0xff);
-                           *(sp + 1) = (png_byte)(v & 0xff);
-                        }
-                     }
-                  }
-                  else
-#endif
-                  {
-                     sp = row;
-                     for (i = 0; i < row_width; i++, sp += 2)
-                     {
-                        png_uint_16 v;
-
-                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                        if (v == png_ptr->trans_color.gray)
-                        {
-                           *sp = (png_byte)((png_ptr->background.gray >> 8)
-                                & 0xff);
-                           *(sp + 1) = (png_byte)(png_ptr->background.gray
-                                & 0xff);
-                        }
-                     }
-                  }
-                  break;
-               }
-
-               default:
-                  break;
+               break;
             }
-            break;
-         }
 
-         case PNG_COLOR_TYPE_RGB:
-         {
-            if (row_info->bit_depth == 8)
+            case 2:
             {
 #ifdef PNG_READ_GAMMA_SUPPORTED
                if (gamma_table != NULL)
                {
                   sp = row;
-                  for (i = 0; i < row_width; i++, sp += 3)
+                  shift = 6;
+                  for (i = 0; i < row_width; i++)
                   {
-                     if (*sp == png_ptr->trans_color.red &&
-                         *(sp + 1) == png_ptr->trans_color.green &&
-                         *(sp + 2) == png_ptr->trans_color.blue)
+                     if ((png_uint_16)((*sp >> shift) & 0x03)
+                         == png_ptr->trans_color.gray)
                      {
-                        *sp = (png_byte)png_ptr->background.red;
-                        *(sp + 1) = (png_byte)png_ptr->background.green;
-                        *(sp + 2) = (png_byte)png_ptr->background.blue;
+                        unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
+                        tmp |=
+                           (unsigned int)png_ptr->background.gray << shift;
+                        *sp = (png_byte)(tmp & 0xff);
                      }
 
                      else
                      {
-                        *sp = gamma_table[*sp];
-                        *(sp + 1) = gamma_table[*(sp + 1)];
-                        *(sp + 2) = gamma_table[*(sp + 2)];
+                        unsigned int p = (*sp >> shift) & 0x03;
+                        unsigned int g = (gamma_table [p | (p << 2) |
+                            (p << 4) | (p << 6)] >> 6) & 0x03;
+                        unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
+                        tmp |= (unsigned int)(g << shift);
+                        *sp = (png_byte)(tmp & 0xff);
                      }
+
+                     if (shift == 0)
+                     {
+                        shift = 6;
+                        sp++;
+                     }
+
+                     else
+                        shift -= 2;
+                  }
+               }
+
+               else
+#endif
+               {
+                  sp = row;
+                  shift = 6;
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((png_uint_16)((*sp >> shift) & 0x03)
+                         == png_ptr->trans_color.gray)
+                     {
+                        unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
+                        tmp |=
+                            (unsigned int)png_ptr->background.gray << shift;
+                        *sp = (png_byte)(tmp & 0xff);
+                     }
+
+                     if (shift == 0)
+                     {
+                        shift = 6;
+                        sp++;
+                     }
+
+                     else
+                        shift -= 2;
+                  }
+               }
+               break;
+            }
+
+            case 4:
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_table != NULL)
+               {
+                  sp = row;
+                  shift = 4;
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((png_uint_16)((*sp >> shift) & 0x0f)
+                         == png_ptr->trans_color.gray)
+                     {
+                        unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
+                        tmp |=
+                           (unsigned int)(png_ptr->background.gray << shift);
+                        *sp = (png_byte)(tmp & 0xff);
+                     }
+
+                     else
+                     {
+                        unsigned int p = (*sp >> shift) & 0x0f;
+                        unsigned int g = (gamma_table[p | (p << 4)] >> 4) &
+                           0x0f;
+                        unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
+                        tmp |= (unsigned int)(g << shift);
+                        *sp = (png_byte)(tmp & 0xff);
+                     }
+
+                     if (shift == 0)
+                     {
+                        shift = 4;
+                        sp++;
+                     }
+
+                     else
+                        shift -= 4;
+                  }
+               }
+
+               else
+#endif
+               {
+                  sp = row;
+                  shift = 4;
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((png_uint_16)((*sp >> shift) & 0x0f)
+                         == png_ptr->trans_color.gray)
+                     {
+                        unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
+                        tmp |=
+                           (unsigned int)(png_ptr->background.gray << shift);
+                        *sp = (png_byte)(tmp & 0xff);
+                     }
+
+                     if (shift == 0)
+                     {
+                        shift = 4;
+                        sp++;
+                     }
+
+                     else
+                        shift -= 4;
+                  }
+               }
+               break;
+            }
+
+            case 8:
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_table != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp++)
+                  {
+                     if (*sp == png_ptr->trans_color.gray)
+                        *sp = (png_byte)png_ptr->background.gray;
+
+                     else
+                        *sp = gamma_table[*sp];
                   }
                }
                else
 #endif
                {
                   sp = row;
-                  for (i = 0; i < row_width; i++, sp += 3)
+                  for (i = 0; i < row_width; i++, sp++)
                   {
-                     if (*sp == png_ptr->trans_color.red &&
-                         *(sp + 1) == png_ptr->trans_color.green &&
-                         *(sp + 2) == png_ptr->trans_color.blue)
-                     {
-                        *sp = (png_byte)png_ptr->background.red;
-                        *(sp + 1) = (png_byte)png_ptr->background.green;
-                        *(sp + 2) = (png_byte)png_ptr->background.blue;
-                     }
+                     if (*sp == png_ptr->trans_color.gray)
+                        *sp = (png_byte)png_ptr->background.gray;
                   }
                }
+               break;
             }
-            else /* if (row_info->bit_depth == 16) */
+
+            case 16:
             {
 #ifdef PNG_READ_GAMMA_SUPPORTED
                if (gamma_16 != NULL)
                {
                   sp = row;
-                  for (i = 0; i < row_width; i++, sp += 6)
-                  {
-                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
-                         + *(sp + 3));
-
-                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
-                         + *(sp + 5));
-
-                     if (r == png_ptr->trans_color.red &&
-                         g == png_ptr->trans_color.green &&
-                         b == png_ptr->trans_color.blue)
-                     {
-                        /* Background is already in screen gamma */
-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                                & 0xff);
-                        *(sp + 3) = (png_byte)(png_ptr->background.green
-                                & 0xff);
-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                                & 0xff);
-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                     }
-
-                     else
-                     {
-                        png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                        *sp = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(v & 0xff);
-
-                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
-                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 3) = (png_byte)(v & 0xff);
-
-                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
-                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 5) = (png_byte)(v & 0xff);
-                     }
-                  }
-               }
-
-               else
-#endif
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 6)
-                  {
-                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
-                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
-                         + *(sp + 3));
-
-                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
-                         + *(sp + 5));
-
-                     if (r == png_ptr->trans_color.red &&
-                         g == png_ptr->trans_color.green &&
-                         b == png_ptr->trans_color.blue)
-                     {
-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                                & 0xff);
-                        *(sp + 3) = (png_byte)(png_ptr->background.green
-                                & 0xff);
-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                                & 0xff);
-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                     }
-                  }
-               }
-            }
-            break;
-         }
-
-         case PNG_COLOR_TYPE_GRAY_ALPHA:
-         {
-            if (row_info->bit_depth == 8)
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
-                   gamma_table != NULL)
-               {
-                  sp = row;
                   for (i = 0; i < row_width; i++, sp += 2)
                   {
-                     png_uint_16 a = *(sp + 1);
+                     png_uint_16 v;
 
-                     if (a == 0xff)
-                        *sp = gamma_table[*sp];
+                     v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
 
-                     else if (a == 0)
+                     if (v == png_ptr->trans_color.gray)
                      {
                         /* Background is already in screen gamma */
-                        *sp = (png_byte)png_ptr->background.gray;
+                        *sp = (png_byte)((png_ptr->background.gray >> 8)
+                             & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.gray
+                             & 0xff);
                      }
 
                      else
                      {
-                        png_byte v, w;
-
-                        v = gamma_to_1[*sp];
-                        png_composite(w, v, a, png_ptr->background_1.gray);
-                        if (optimize == 0)
-                           w = gamma_from_1[w];
-                        *sp = w;
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
                      }
                   }
                }
@@ -3623,298 +3449,486 @@
                   sp = row;
                   for (i = 0; i < row_width; i++, sp += 2)
                   {
-                     png_byte a = *(sp + 1);
+                     png_uint_16 v;
 
-                     if (a == 0)
-                        *sp = (png_byte)png_ptr->background.gray;
+                     v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
 
-                     else if (a < 0xff)
-                        png_composite(*sp, *sp, a, png_ptr->background.gray);
-                  }
-               }
-            }
-            else /* if (png_ptr->bit_depth == 16) */
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
-                   gamma_16_to_1 != NULL)
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 4)
-                  {
-                     png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
-                         + *(sp + 3));
-
-                     if (a == (png_uint_16)0xffff)
-                     {
-                        png_uint_16 v;
-
-                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                        *sp = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(v & 0xff);
-                     }
-
-                     else if (a == 0)
-                     {
-                        /* Background is already in screen gamma */
-                        *sp = (png_byte)((png_ptr->background.gray >> 8)
-                                & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
-                     }
-
-                     else
-                     {
-                        png_uint_16 g, v, w;
-
-                        g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
-                        png_composite_16(v, g, a, png_ptr->background_1.gray);
-                        if (optimize != 0)
-                           w = v;
-                        else
-                           w = gamma_16_from_1[(v & 0xff) >>
-                               gamma_shift][v >> 8];
-                        *sp = (png_byte)((w >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(w & 0xff);
-                     }
-                  }
-               }
-               else
-#endif
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 4)
-                  {
-                     png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
-                         + *(sp + 3));
-
-                     if (a == 0)
+                     if (v == png_ptr->trans_color.gray)
                      {
                         *sp = (png_byte)((png_ptr->background.gray >> 8)
-                                & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
-                     }
-
-                     else if (a < 0xffff)
-                     {
-                        png_uint_16 g, v;
-
-                        g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-                        png_composite_16(v, g, a, png_ptr->background.gray);
-                        *sp = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(v & 0xff);
+                             & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.gray
+                             & 0xff);
                      }
                   }
                }
+               break;
             }
-            break;
+
+            default:
+               break;
          }
-
-         case PNG_COLOR_TYPE_RGB_ALPHA:
-         {
-            if (row_info->bit_depth == 8)
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
-                   gamma_table != NULL)
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 4)
-                  {
-                     png_byte a = *(sp + 3);
-
-                     if (a == 0xff)
-                     {
-                        *sp = gamma_table[*sp];
-                        *(sp + 1) = gamma_table[*(sp + 1)];
-                        *(sp + 2) = gamma_table[*(sp + 2)];
-                     }
-
-                     else if (a == 0)
-                     {
-                        /* Background is already in screen gamma */
-                        *sp = (png_byte)png_ptr->background.red;
-                        *(sp + 1) = (png_byte)png_ptr->background.green;
-                        *(sp + 2) = (png_byte)png_ptr->background.blue;
-                     }
-
-                     else
-                     {
-                        png_byte v, w;
-
-                        v = gamma_to_1[*sp];
-                        png_composite(w, v, a, png_ptr->background_1.red);
-                        if (optimize == 0) w = gamma_from_1[w];
-                        *sp = w;
-
-                        v = gamma_to_1[*(sp + 1)];
-                        png_composite(w, v, a, png_ptr->background_1.green);
-                        if (optimize == 0) w = gamma_from_1[w];
-                        *(sp + 1) = w;
-
-                        v = gamma_to_1[*(sp + 2)];
-                        png_composite(w, v, a, png_ptr->background_1.blue);
-                        if (optimize == 0) w = gamma_from_1[w];
-                        *(sp + 2) = w;
-                     }
-                  }
-               }
-               else
-#endif
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 4)
-                  {
-                     png_byte a = *(sp + 3);
-
-                     if (a == 0)
-                     {
-                        *sp = (png_byte)png_ptr->background.red;
-                        *(sp + 1) = (png_byte)png_ptr->background.green;
-                        *(sp + 2) = (png_byte)png_ptr->background.blue;
-                     }
-
-                     else if (a < 0xff)
-                     {
-                        png_composite(*sp, *sp, a, png_ptr->background.red);
-
-                        png_composite(*(sp + 1), *(sp + 1), a,
-                            png_ptr->background.green);
-
-                        png_composite(*(sp + 2), *(sp + 2), a,
-                            png_ptr->background.blue);
-                     }
-                  }
-               }
-            }
-            else /* if (row_info->bit_depth == 16) */
-            {
-#ifdef PNG_READ_GAMMA_SUPPORTED
-               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
-                   gamma_16_to_1 != NULL)
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 8)
-                  {
-                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
-                         << 8) + (png_uint_16)(*(sp + 7)));
-
-                     if (a == (png_uint_16)0xffff)
-                     {
-                        png_uint_16 v;
-
-                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
-                        *sp = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(v & 0xff);
-
-                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
-                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 3) = (png_byte)(v & 0xff);
-
-                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
-                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 5) = (png_byte)(v & 0xff);
-                     }
-
-                     else if (a == 0)
-                     {
-                        /* Background is already in screen gamma */
-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                                & 0xff);
-                        *(sp + 3) = (png_byte)(png_ptr->background.green
-                                & 0xff);
-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                                & 0xff);
-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                     }
-
-                     else
-                     {
-                        png_uint_16 v, w;
-
-                        v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
-                        png_composite_16(w, v, a, png_ptr->background_1.red);
-                        if (optimize == 0)
-                           w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
-                                8];
-                        *sp = (png_byte)((w >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(w & 0xff);
-
-                        v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
-                        png_composite_16(w, v, a, png_ptr->background_1.green);
-                        if (optimize == 0)
-                           w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
-                                8];
-
-                        *(sp + 2) = (png_byte)((w >> 8) & 0xff);
-                        *(sp + 3) = (png_byte)(w & 0xff);
-
-                        v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
-                        png_composite_16(w, v, a, png_ptr->background_1.blue);
-                        if (optimize == 0)
-                           w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
-                                8];
-
-                        *(sp + 4) = (png_byte)((w >> 8) & 0xff);
-                        *(sp + 5) = (png_byte)(w & 0xff);
-                     }
-                  }
-               }
-
-               else
-#endif
-               {
-                  sp = row;
-                  for (i = 0; i < row_width; i++, sp += 8)
-                  {
-                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
-                         << 8) + (png_uint_16)(*(sp + 7)));
-
-                     if (a == 0)
-                     {
-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
-                                & 0xff);
-                        *(sp + 3) = (png_byte)(png_ptr->background.green
-                                & 0xff);
-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
-                                & 0xff);
-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
-                     }
-
-                     else if (a < 0xffff)
-                     {
-                        png_uint_16 v;
-
-                        png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-                        png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
-                            + *(sp + 3));
-                        png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
-                            + *(sp + 5));
-
-                        png_composite_16(v, r, a, png_ptr->background.red);
-                        *sp = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 1) = (png_byte)(v & 0xff);
-
-                        png_composite_16(v, g, a, png_ptr->background.green);
-                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 3) = (png_byte)(v & 0xff);
-
-                        png_composite_16(v, b, a, png_ptr->background.blue);
-                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
-                        *(sp + 5) = (png_byte)(v & 0xff);
-                     }
-                  }
-               }
-            }
-            break;
-         }
-
-         default:
-            break;
+         break;
       }
+
+      case PNG_COLOR_TYPE_RGB:
+      {
+         if (row_info->bit_depth == 8)
+         {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+            if (gamma_table != NULL)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 3)
+               {
+                  if (*sp == png_ptr->trans_color.red &&
+                      *(sp + 1) == png_ptr->trans_color.green &&
+                      *(sp + 2) == png_ptr->trans_color.blue)
+                  {
+                     *sp = (png_byte)png_ptr->background.red;
+                     *(sp + 1) = (png_byte)png_ptr->background.green;
+                     *(sp + 2) = (png_byte)png_ptr->background.blue;
+                  }
+
+                  else
+                  {
+                     *sp = gamma_table[*sp];
+                     *(sp + 1) = gamma_table[*(sp + 1)];
+                     *(sp + 2) = gamma_table[*(sp + 2)];
+                  }
+               }
+            }
+            else
+#endif
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 3)
+               {
+                  if (*sp == png_ptr->trans_color.red &&
+                      *(sp + 1) == png_ptr->trans_color.green &&
+                      *(sp + 2) == png_ptr->trans_color.blue)
+                  {
+                     *sp = (png_byte)png_ptr->background.red;
+                     *(sp + 1) = (png_byte)png_ptr->background.green;
+                     *(sp + 2) = (png_byte)png_ptr->background.blue;
+                  }
+               }
+            }
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+            if (gamma_16 != NULL)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 6)
+               {
+                  png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+                  png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                      + *(sp + 3));
+
+                  png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                      + *(sp + 5));
+
+                  if (r == png_ptr->trans_color.red &&
+                      g == png_ptr->trans_color.green &&
+                      b == png_ptr->trans_color.blue)
+                  {
+                     /* Background is already in screen gamma */
+                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                             & 0xff);
+                     *(sp + 3) = (png_byte)(png_ptr->background.green
+                             & 0xff);
+                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                             & 0xff);
+                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                  }
+
+                  else
+                  {
+                     png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                     *sp = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(v & 0xff);
+
+                     v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                     *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 3) = (png_byte)(v & 0xff);
+
+                     v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                     *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 5) = (png_byte)(v & 0xff);
+                  }
+               }
+            }
+
+            else
+#endif
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 6)
+               {
+                  png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+                  png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                      + *(sp + 3));
+
+                  png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                      + *(sp + 5));
+
+                  if (r == png_ptr->trans_color.red &&
+                      g == png_ptr->trans_color.green &&
+                      b == png_ptr->trans_color.blue)
+                  {
+                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                             & 0xff);
+                     *(sp + 3) = (png_byte)(png_ptr->background.green
+                             & 0xff);
+                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                             & 0xff);
+                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                  }
+               }
+            }
+         }
+         break;
+      }
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+      {
+         if (row_info->bit_depth == 8)
+         {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+            if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                gamma_table != NULL)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 2)
+               {
+                  png_uint_16 a = *(sp + 1);
+
+                  if (a == 0xff)
+                     *sp = gamma_table[*sp];
+
+                  else if (a == 0)
+                  {
+                     /* Background is already in screen gamma */
+                     *sp = (png_byte)png_ptr->background.gray;
+                  }
+
+                  else
+                  {
+                     png_byte v, w;
+
+                     v = gamma_to_1[*sp];
+                     png_composite(w, v, a, png_ptr->background_1.gray);
+                     if (optimize == 0)
+                        w = gamma_from_1[w];
+                     *sp = w;
+                  }
+               }
+            }
+            else
+#endif
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 2)
+               {
+                  png_byte a = *(sp + 1);
+
+                  if (a == 0)
+                     *sp = (png_byte)png_ptr->background.gray;
+
+                  else if (a < 0xff)
+                     png_composite(*sp, *sp, a, png_ptr->background.gray);
+               }
+            }
+         }
+         else /* if (png_ptr->bit_depth == 16) */
+         {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+            if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                gamma_16_to_1 != NULL)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 4)
+               {
+                  png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+                      + *(sp + 3));
+
+                  if (a == (png_uint_16)0xffff)
+                  {
+                     png_uint_16 v;
+
+                     v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                     *sp = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(v & 0xff);
+                  }
+
+                  else if (a == 0)
+                  {
+                     /* Background is already in screen gamma */
+                     *sp = (png_byte)((png_ptr->background.gray >> 8)
+                             & 0xff);
+                     *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
+                  }
+
+                  else
+                  {
+                     png_uint_16 g, v, w;
+
+                     g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                     png_composite_16(v, g, a, png_ptr->background_1.gray);
+                     if (optimize != 0)
+                        w = v;
+                     else
+                        w = gamma_16_from_1[(v & 0xff) >>
+                            gamma_shift][v >> 8];
+                     *sp = (png_byte)((w >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(w & 0xff);
+                  }
+               }
+            }
+            else
+#endif
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 4)
+               {
+                  png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+                      + *(sp + 3));
+
+                  if (a == 0)
+                  {
+                     *sp = (png_byte)((png_ptr->background.gray >> 8)
+                             & 0xff);
+                     *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
+                  }
+
+                  else if (a < 0xffff)
+                  {
+                     png_uint_16 g, v;
+
+                     g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                     png_composite_16(v, g, a, png_ptr->background.gray);
+                     *sp = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(v & 0xff);
+                  }
+               }
+            }
+         }
+         break;
+      }
+
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+      {
+         if (row_info->bit_depth == 8)
+         {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+            if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                gamma_table != NULL)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 4)
+               {
+                  png_byte a = *(sp + 3);
+
+                  if (a == 0xff)
+                  {
+                     *sp = gamma_table[*sp];
+                     *(sp + 1) = gamma_table[*(sp + 1)];
+                     *(sp + 2) = gamma_table[*(sp + 2)];
+                  }
+
+                  else if (a == 0)
+                  {
+                     /* Background is already in screen gamma */
+                     *sp = (png_byte)png_ptr->background.red;
+                     *(sp + 1) = (png_byte)png_ptr->background.green;
+                     *(sp + 2) = (png_byte)png_ptr->background.blue;
+                  }
+
+                  else
+                  {
+                     png_byte v, w;
+
+                     v = gamma_to_1[*sp];
+                     png_composite(w, v, a, png_ptr->background_1.red);
+                     if (optimize == 0) w = gamma_from_1[w];
+                     *sp = w;
+
+                     v = gamma_to_1[*(sp + 1)];
+                     png_composite(w, v, a, png_ptr->background_1.green);
+                     if (optimize == 0) w = gamma_from_1[w];
+                     *(sp + 1) = w;
+
+                     v = gamma_to_1[*(sp + 2)];
+                     png_composite(w, v, a, png_ptr->background_1.blue);
+                     if (optimize == 0) w = gamma_from_1[w];
+                     *(sp + 2) = w;
+                  }
+               }
+            }
+            else
+#endif
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 4)
+               {
+                  png_byte a = *(sp + 3);
+
+                  if (a == 0)
+                  {
+                     *sp = (png_byte)png_ptr->background.red;
+                     *(sp + 1) = (png_byte)png_ptr->background.green;
+                     *(sp + 2) = (png_byte)png_ptr->background.blue;
+                  }
+
+                  else if (a < 0xff)
+                  {
+                     png_composite(*sp, *sp, a, png_ptr->background.red);
+
+                     png_composite(*(sp + 1), *(sp + 1), a,
+                         png_ptr->background.green);
+
+                     png_composite(*(sp + 2), *(sp + 2), a,
+                         png_ptr->background.blue);
+                  }
+               }
+            }
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+            if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                gamma_16_to_1 != NULL)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 8)
+               {
+                  png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                      << 8) + (png_uint_16)(*(sp + 7)));
+
+                  if (a == (png_uint_16)0xffff)
+                  {
+                     png_uint_16 v;
+
+                     v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                     *sp = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(v & 0xff);
+
+                     v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                     *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 3) = (png_byte)(v & 0xff);
+
+                     v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                     *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 5) = (png_byte)(v & 0xff);
+                  }
+
+                  else if (a == 0)
+                  {
+                     /* Background is already in screen gamma */
+                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                             & 0xff);
+                     *(sp + 3) = (png_byte)(png_ptr->background.green
+                             & 0xff);
+                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                             & 0xff);
+                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                  }
+
+                  else
+                  {
+                     png_uint_16 v, w;
+
+                     v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                     png_composite_16(w, v, a, png_ptr->background_1.red);
+                     if (optimize == 0)
+                        w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
+                             8];
+                     *sp = (png_byte)((w >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(w & 0xff);
+
+                     v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                     png_composite_16(w, v, a, png_ptr->background_1.green);
+                     if (optimize == 0)
+                        w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
+                             8];
+
+                     *(sp + 2) = (png_byte)((w >> 8) & 0xff);
+                     *(sp + 3) = (png_byte)(w & 0xff);
+
+                     v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                     png_composite_16(w, v, a, png_ptr->background_1.blue);
+                     if (optimize == 0)
+                        w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
+                             8];
+
+                     *(sp + 4) = (png_byte)((w >> 8) & 0xff);
+                     *(sp + 5) = (png_byte)(w & 0xff);
+                  }
+               }
+            }
+
+            else
+#endif
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++, sp += 8)
+               {
+                  png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                      << 8) + (png_uint_16)(*(sp + 7)));
+
+                  if (a == 0)
+                  {
+                     *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                     *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                             & 0xff);
+                     *(sp + 3) = (png_byte)(png_ptr->background.green
+                             & 0xff);
+                     *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                             & 0xff);
+                     *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                  }
+
+                  else if (a < 0xffff)
+                  {
+                     png_uint_16 v;
+
+                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                         + *(sp + 3));
+                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                         + *(sp + 5));
+
+                     png_composite_16(v, r, a, png_ptr->background.red);
+                     *sp = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 1) = (png_byte)(v & 0xff);
+
+                     png_composite_16(v, g, a, png_ptr->background.green);
+                     *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 3) = (png_byte)(v & 0xff);
+
+                     png_composite_16(v, b, a, png_ptr->background.blue);
+                     *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                     *(sp + 5) = (png_byte)(v & 0xff);
+                  }
+               }
+            }
+         }
+         break;
+      }
+
+      default:
+         break;
    }
 }
 #endif /* READ_BACKGROUND || READ_ALPHA_MODE */
@@ -4138,12 +4152,11 @@
    {
       if (row_info->bit_depth == 8)
       {
-         PNG_CONST png_bytep table = png_ptr->gamma_from_1;
+         png_bytep table = png_ptr->gamma_from_1;
 
          if (table != NULL)
          {
-            PNG_CONST int step =
-               (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2;
+            int step = (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2;
 
             /* The alpha channel is the last component: */
             row += step - 1;
@@ -4157,13 +4170,12 @@
 
       else if (row_info->bit_depth == 16)
       {
-         PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1;
-         PNG_CONST int gamma_shift = png_ptr->gamma_shift;
+         png_uint_16pp table = png_ptr->gamma_16_from_1;
+         int gamma_shift = png_ptr->gamma_shift;
 
          if (table != NULL)
          {
-            PNG_CONST int step =
-               (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4;
+            int step = (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4;
 
             /* The alpha channel is the last component: */
             row += step - 2;
@@ -4194,8 +4206,9 @@
  * upon whether you supply trans and num_trans.
  */
 static void
-png_do_expand_palette(png_row_infop row_info, png_bytep row,
-   png_const_colorp palette, png_const_bytep trans_alpha, int num_trans)
+png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
+    png_bytep row, png_const_colorp palette, png_const_bytep trans_alpha,
+    int num_trans)
 {
    int shift, value;
    png_bytep sp, dp;
@@ -4212,8 +4225,8 @@
          {
             case 1:
             {
-               sp = row + (png_size_t)((row_width - 1) >> 3);
-               dp = row + (png_size_t)row_width - 1;
+               sp = row + (size_t)((row_width - 1) >> 3);
+               dp = row + (size_t)row_width - 1;
                shift = 7 - (int)((row_width + 7) & 0x07);
                for (i = 0; i < row_width; i++)
                {
@@ -4239,8 +4252,8 @@
 
             case 2:
             {
-               sp = row + (png_size_t)((row_width - 1) >> 2);
-               dp = row + (png_size_t)row_width - 1;
+               sp = row + (size_t)((row_width - 1) >> 2);
+               dp = row + (size_t)row_width - 1;
                shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
                for (i = 0; i < row_width; i++)
                {
@@ -4262,8 +4275,8 @@
 
             case 4:
             {
-               sp = row + (png_size_t)((row_width - 1) >> 1);
-               dp = row + (png_size_t)row_width - 1;
+               sp = row + (size_t)((row_width - 1) >> 1);
+               dp = row + (size_t)row_width - 1;
                shift = (int)((row_width & 0x01) << 2);
                for (i = 0; i < row_width; i++)
                {
@@ -4296,17 +4309,30 @@
          {
             if (num_trans > 0)
             {
-               sp = row + (png_size_t)row_width - 1;
-               dp = row + (png_size_t)(row_width << 2) - 1;
+               sp = row + (size_t)row_width - 1;
+               dp = row + ((size_t)row_width << 2) - 1;
 
-               for (i = 0; i < row_width; i++)
+               i = 0;
+#ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
+               if (png_ptr->riffled_palette != NULL)
+               {
+                  /* The RGBA optimization works with png_ptr->bit_depth == 8
+                   * but sometimes row_info->bit_depth has been changed to 8.
+                   * In these cases, the palette hasn't been riffled.
+                   */
+                  i = png_do_expand_palette_rgba8_neon(png_ptr, row_info, row,
+                      &sp, &dp);
+               }
+#else
+               PNG_UNUSED(png_ptr)
+#endif
+
+               for (; i < row_width; i++)
                {
                   if ((int)(*sp) >= num_trans)
                      *dp-- = 0xff;
-
                   else
                      *dp-- = trans_alpha[*sp];
-
                   *dp-- = palette[*sp].blue;
                   *dp-- = palette[*sp].green;
                   *dp-- = palette[*sp].red;
@@ -4321,10 +4347,17 @@
 
             else
             {
-               sp = row + (png_size_t)row_width - 1;
-               dp = row + (png_size_t)(row_width * 3) - 1;
+               sp = row + (size_t)row_width - 1;
+               dp = row + (size_t)(row_width * 3) - 1;
+               i = 0;
+#ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
+               i = png_do_expand_palette_rgb8_neon(png_ptr, row_info, row,
+                   &sp, &dp);
+#else
+               PNG_UNUSED(png_ptr)
+#endif
 
-               for (i = 0; i < row_width; i++)
+               for (; i < row_width; i++)
                {
                   *dp-- = palette[*sp].blue;
                   *dp-- = palette[*sp].green;
@@ -4357,195 +4390,130 @@
 
    png_debug(1, "in png_do_expand");
 
+   if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
    {
-      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      unsigned int gray = trans_color != NULL ? trans_color->gray : 0;
+
+      if (row_info->bit_depth < 8)
       {
-         unsigned int gray = trans_color != NULL ? trans_color->gray : 0;
-
-         if (row_info->bit_depth < 8)
+         switch (row_info->bit_depth)
          {
-            switch (row_info->bit_depth)
+            case 1:
             {
-               case 1:
-               {
-                  gray = (gray & 0x01) * 0xff;
-                  sp = row + (png_size_t)((row_width - 1) >> 3);
-                  dp = row + (png_size_t)row_width - 1;
-                  shift = 7 - (int)((row_width + 7) & 0x07);
-                  for (i = 0; i < row_width; i++)
-                  {
-                     if ((*sp >> shift) & 0x01)
-                        *dp = 0xff;
-
-                     else
-                        *dp = 0;
-
-                     if (shift == 7)
-                     {
-                        shift = 0;
-                        sp--;
-                     }
-
-                     else
-                        shift++;
-
-                     dp--;
-                  }
-                  break;
-               }
-
-               case 2:
-               {
-                  gray = (gray & 0x03) * 0x55;
-                  sp = row + (png_size_t)((row_width - 1) >> 2);
-                  dp = row + (png_size_t)row_width - 1;
-                  shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
-                  for (i = 0; i < row_width; i++)
-                  {
-                     value = (*sp >> shift) & 0x03;
-                     *dp = (png_byte)(value | (value << 2) | (value << 4) |
-                        (value << 6));
-                     if (shift == 6)
-                     {
-                        shift = 0;
-                        sp--;
-                     }
-
-                     else
-                        shift += 2;
-
-                     dp--;
-                  }
-                  break;
-               }
-
-               case 4:
-               {
-                  gray = (gray & 0x0f) * 0x11;
-                  sp = row + (png_size_t)((row_width - 1) >> 1);
-                  dp = row + (png_size_t)row_width - 1;
-                  shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
-                  for (i = 0; i < row_width; i++)
-                  {
-                     value = (*sp >> shift) & 0x0f;
-                     *dp = (png_byte)(value | (value << 4));
-                     if (shift == 4)
-                     {
-                        shift = 0;
-                        sp--;
-                     }
-
-                     else
-                        shift = 4;
-
-                     dp--;
-                  }
-                  break;
-               }
-
-               default:
-                  break;
-            }
-
-            row_info->bit_depth = 8;
-            row_info->pixel_depth = 8;
-            row_info->rowbytes = row_width;
-         }
-
-         if (trans_color != NULL)
-         {
-            if (row_info->bit_depth == 8)
-            {
-               gray = gray & 0xff;
-               sp = row + (png_size_t)row_width - 1;
-               dp = row + (png_size_t)(row_width << 1) - 1;
-
+               gray = (gray & 0x01) * 0xff;
+               sp = row + (size_t)((row_width - 1) >> 3);
+               dp = row + (size_t)row_width - 1;
+               shift = 7 - (int)((row_width + 7) & 0x07);
                for (i = 0; i < row_width; i++)
                {
-                  if ((*sp & 0xffU) == gray)
-                     *dp-- = 0;
+                  if ((*sp >> shift) & 0x01)
+                     *dp = 0xff;
 
                   else
-                     *dp-- = 0xff;
+                     *dp = 0;
 
-                  *dp-- = *sp--;
-               }
-            }
-
-            else if (row_info->bit_depth == 16)
-            {
-               unsigned int gray_high = (gray >> 8) & 0xff;
-               unsigned int gray_low = gray & 0xff;
-               sp = row + row_info->rowbytes - 1;
-               dp = row + (row_info->rowbytes << 1) - 1;
-               for (i = 0; i < row_width; i++)
-               {
-                  if ((*(sp - 1) & 0xffU) == gray_high &&
-                      (*(sp) & 0xffU) == gray_low)
+                  if (shift == 7)
                   {
-                     *dp-- = 0;
-                     *dp-- = 0;
+                     shift = 0;
+                     sp--;
                   }
 
                   else
-                  {
-                     *dp-- = 0xff;
-                     *dp-- = 0xff;
-                  }
+                     shift++;
 
-                  *dp-- = *sp--;
-                  *dp-- = *sp--;
+                  dp--;
                }
+               break;
             }
 
-            row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
-            row_info->channels = 2;
-            row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
-            row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
-               row_width);
+            case 2:
+            {
+               gray = (gray & 0x03) * 0x55;
+               sp = row + (size_t)((row_width - 1) >> 2);
+               dp = row + (size_t)row_width - 1;
+               shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x03;
+                  *dp = (png_byte)(value | (value << 2) | (value << 4) |
+                     (value << 6));
+                  if (shift == 6)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+
+                  else
+                     shift += 2;
+
+                  dp--;
+               }
+               break;
+            }
+
+            case 4:
+            {
+               gray = (gray & 0x0f) * 0x11;
+               sp = row + (size_t)((row_width - 1) >> 1);
+               dp = row + (size_t)row_width - 1;
+               shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x0f;
+                  *dp = (png_byte)(value | (value << 4));
+                  if (shift == 4)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+
+                  else
+                     shift = 4;
+
+                  dp--;
+               }
+               break;
+            }
+
+            default:
+               break;
          }
+
+         row_info->bit_depth = 8;
+         row_info->pixel_depth = 8;
+         row_info->rowbytes = row_width;
       }
-      else if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
-          trans_color != NULL)
+
+      if (trans_color != NULL)
       {
          if (row_info->bit_depth == 8)
          {
-            png_byte red = (png_byte)(trans_color->red & 0xff);
-            png_byte green = (png_byte)(trans_color->green & 0xff);
-            png_byte blue = (png_byte)(trans_color->blue & 0xff);
-            sp = row + (png_size_t)row_info->rowbytes - 1;
-            dp = row + (png_size_t)(row_width << 2) - 1;
+            gray = gray & 0xff;
+            sp = row + (size_t)row_width - 1;
+            dp = row + ((size_t)row_width << 1) - 1;
+
             for (i = 0; i < row_width; i++)
             {
-               if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)
+               if ((*sp & 0xffU) == gray)
                   *dp-- = 0;
 
                else
                   *dp-- = 0xff;
 
                *dp-- = *sp--;
-               *dp-- = *sp--;
-               *dp-- = *sp--;
             }
          }
+
          else if (row_info->bit_depth == 16)
          {
-            png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff);
-            png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff);
-            png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff);
-            png_byte red_low = (png_byte)(trans_color->red & 0xff);
-            png_byte green_low = (png_byte)(trans_color->green & 0xff);
-            png_byte blue_low = (png_byte)(trans_color->blue & 0xff);
+            unsigned int gray_high = (gray >> 8) & 0xff;
+            unsigned int gray_low = gray & 0xff;
             sp = row + row_info->rowbytes - 1;
-            dp = row + (png_size_t)(row_width << 3) - 1;
+            dp = row + (row_info->rowbytes << 1) - 1;
             for (i = 0; i < row_width; i++)
             {
-               if (*(sp - 5) == red_high &&
-                   *(sp - 4) == red_low &&
-                   *(sp - 3) == green_high &&
-                   *(sp - 2) == green_low &&
-                   *(sp - 1) == blue_high &&
-                   *(sp    ) == blue_low)
+               if ((*(sp - 1) & 0xffU) == gray_high &&
+                   (*(sp) & 0xffU) == gray_low)
                {
                   *dp-- = 0;
                   *dp-- = 0;
@@ -4559,18 +4527,81 @@
 
                *dp-- = *sp--;
                *dp-- = *sp--;
-               *dp-- = *sp--;
-               *dp-- = *sp--;
-               *dp-- = *sp--;
-               *dp-- = *sp--;
             }
          }
-         row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
-         row_info->channels = 4;
-         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
-         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+
+         row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+         row_info->channels = 2;
+         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+             row_width);
       }
    }
+   else if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
+       trans_color != NULL)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         png_byte red = (png_byte)(trans_color->red & 0xff);
+         png_byte green = (png_byte)(trans_color->green & 0xff);
+         png_byte blue = (png_byte)(trans_color->blue & 0xff);
+         sp = row + (size_t)row_info->rowbytes - 1;
+         dp = row + ((size_t)row_width << 2) - 1;
+         for (i = 0; i < row_width; i++)
+         {
+            if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)
+               *dp-- = 0;
+
+            else
+               *dp-- = 0xff;
+
+            *dp-- = *sp--;
+            *dp-- = *sp--;
+            *dp-- = *sp--;
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff);
+         png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff);
+         png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff);
+         png_byte red_low = (png_byte)(trans_color->red & 0xff);
+         png_byte green_low = (png_byte)(trans_color->green & 0xff);
+         png_byte blue_low = (png_byte)(trans_color->blue & 0xff);
+         sp = row + row_info->rowbytes - 1;
+         dp = row + ((size_t)row_width << 3) - 1;
+         for (i = 0; i < row_width; i++)
+         {
+            if (*(sp - 5) == red_high &&
+                *(sp - 4) == red_low &&
+                *(sp - 3) == green_high &&
+                *(sp - 2) == green_low &&
+                *(sp - 1) == blue_high &&
+                *(sp    ) == blue_low)
+            {
+               *dp-- = 0;
+               *dp-- = 0;
+            }
+
+            else
+            {
+               *dp-- = 0xff;
+               *dp-- = 0xff;
+            }
+
+            *dp-- = *sp--;
+            *dp-- = *sp--;
+            *dp-- = *sp--;
+            *dp-- = *sp--;
+            *dp-- = *sp--;
+            *dp-- = *sp--;
+         }
+      }
+      row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+      row_info->channels = 4;
+      row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+   }
 }
 #endif
 
@@ -4596,7 +4627,9 @@
       png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */
       png_byte *dp = sp + row_info->rowbytes;  /* destination, end + 1 */
       while (dp > sp)
-         dp[-2] = dp[-1] = *--sp, dp -= 2;
+      {
+         dp[-2] = dp[-1] = *--sp; dp -= 2;
+      }
 
       row_info->rowbytes *= 2;
       row_info->bit_depth = 16;
@@ -4738,7 +4771,19 @@
    {
       if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
       {
-         png_do_expand_palette(row_info, png_ptr->row_buf + 1,
+#ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
+         if ((png_ptr->num_trans > 0) && (png_ptr->bit_depth == 8))
+         {
+            if (png_ptr->riffled_palette == NULL)
+            {
+               /* Initialize the accelerated palette expansion. */
+               png_ptr->riffled_palette =
+                   (png_bytep)png_malloc(png_ptr, 256 * 4);
+               png_riffle_palette_neon(png_ptr);
+            }
+         }
+#endif
+         png_do_expand_palette(png_ptr, row_info, png_ptr->row_buf + 1,
              png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans);
       }
 
@@ -4750,8 +4795,7 @@
                 &(png_ptr->trans_color));
 
          else
-            png_do_expand(row_info, png_ptr->row_buf + 1,
-                NULL);
+            png_do_expand(row_info, png_ptr->row_buf + 1, NULL);
       }
    }
 #endif
@@ -4762,7 +4806,7 @@
        (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
        row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
       png_do_strip_channel(row_info, png_ptr->row_buf + 1,
-         0 /* at_start == false, because SWAP_ALPHA happens later */);
+          0 /* at_start == false, because SWAP_ALPHA happens later */);
 #endif
 
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
@@ -4975,7 +5019,7 @@
              (png_ptr,     /* png_ptr */
              row_info,     /* row_info: */
                 /*  png_uint_32 width;       width of row */
-                /*  png_size_t rowbytes;     number of bytes in row */
+                /*  size_t rowbytes;         number of bytes in row */
                 /*  png_byte color_type;     color type of pixels */
                 /*  png_byte bit_depth;      bit depth of samples */
                 /*  png_byte channels;       number of channels (1-4) */
diff --git a/third_party/libpng16/pngrutil.c b/third_party/libpng16/pngrutil.c
index c9747fc..3007414 100644
--- a/third_party/libpng16/pngrutil.c
+++ b/third_party/libpng16/pngrutil.c
@@ -1,10 +1,10 @@
 
 /* pngrutil.c - utilities to read a PNG file
  *
- * Last changed in libpng 1.6.20 [December 3, 2014]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -86,11 +86,11 @@
 {
    png_uint_32 uval = png_get_uint_32(buf);
    if ((uval & 0x80000000) == 0) /* non-negative */
-      return uval;
+      return (png_int_32)uval;
 
    uval = (uval ^ 0xffffffff) + 1;  /* 2's complement: -x = ~x+1 */
    if ((uval & 0x80000000) == 0) /* no overflow */
-       return -(png_int_32)uval;
+      return -(png_int_32)uval;
    /* The following has to be safe; this function only gets called on PNG data
     * and if we get here that data is invalid.  0 is the most safe value and
     * if not then an attacker would surely just generate a PNG with 0 instead.
@@ -102,7 +102,7 @@
 png_uint_16 (PNGAPI
 png_get_uint_16)(png_const_bytep buf)
 {
-   /* ANSI-C requires an int value to accomodate at least 16 bits so this
+   /* ANSI-C requires an int value to accommodate at least 16 bits so this
     * works and allows the compiler not to worry about possible narrowing
     * on 32-bit systems.  (Pre-ANSI systems did not make integers smaller
     * than 16 bits either.)
@@ -120,7 +120,7 @@
 void /* PRIVATE */
 png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
 {
-   png_size_t num_checked, num_to_check;
+   size_t num_checked, num_to_check;
 
    /* Exit if the user application does not expect a signature. */
    if (png_ptr->sig_bytes >= 8)
@@ -181,6 +181,9 @@
    /* Check to see if chunk name is valid. */
    png_check_chunk_name(png_ptr, png_ptr->chunk_name);
 
+   /* Check for too-large chunk length */
+   png_check_chunk_length(png_ptr, length);
+
 #ifdef PNG_IO_STATE_SUPPORTED
    png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;
 #endif
@@ -311,6 +314,7 @@
 
       if (buffer != NULL)
       {
+         memset(buffer, 0, new_size); /* just in case */
          png_ptr->read_buffer = buffer;
          png_ptr->read_buffer_size = new_size;
       }
@@ -370,11 +374,10 @@
     */
    {
       int ret; /* zlib return code */
-#if PNG_ZLIB_VERNUM >= 0x1240
+#if ZLIB_VERNUM >= 0x1240
+      int window_bits = 0;
 
 # if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_MAXIMUM_INFLATE_WINDOW)
-      int window_bits;
-
       if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) ==
           PNG_OPTION_ON)
       {
@@ -384,13 +387,11 @@
 
       else
       {
-         window_bits = 0;
          png_ptr->zstream_start = 1;
       }
-# else
-#   define window_bits 0
 # endif
-#endif
+
+#endif /* ZLIB_VERNUM >= 0x1240 */
 
       /* Set this for safety, just in case the previous owner left pointers to
        * memory allocations.
@@ -402,25 +403,32 @@
 
       if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
       {
-#if PNG_ZLIB_VERNUM < 0x1240
-         ret = inflateReset(&png_ptr->zstream);
-#else
+#if ZLIB_VERNUM >= 0x1240
          ret = inflateReset2(&png_ptr->zstream, window_bits);
+#else
+         ret = inflateReset(&png_ptr->zstream);
 #endif
       }
 
       else
       {
-#if PNG_ZLIB_VERNUM < 0x1240
-         ret = inflateInit(&png_ptr->zstream);
-#else
+#if ZLIB_VERNUM >= 0x1240
          ret = inflateInit2(&png_ptr->zstream, window_bits);
+#else
+         ret = inflateInit(&png_ptr->zstream);
 #endif
 
          if (ret == Z_OK)
             png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
       }
 
+#if ZLIB_VERNUM >= 0x1290 && \
+   defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_IGNORE_ADLER32)
+      if (((png_ptr->options >> PNG_IGNORE_ADLER32) & 3) == PNG_OPTION_ON)
+         /* Turn off validation of the ADLER32 checksum in IDAT chunks */
+         ret = inflateValidate(&png_ptr->zstream, 0);
+#endif
+
       if (ret == Z_OK)
          png_ptr->zowner = owner;
 
@@ -435,7 +443,7 @@
 #endif
 }
 
-#if PNG_ZLIB_VERNUM >= 0x1240
+#if ZLIB_VERNUM >= 0x1240
 /* Handle the start of the inflate stream if we called inflateInit2(strm,0);
  * in this case some zlib versions skip validation of the CINFO field and, in
  * certain circumstances, libpng may end up displaying an invalid image, in
@@ -461,6 +469,7 @@
 #endif /* Zlib >= 1.2.4 */
 
 #ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
+#if defined(PNG_READ_zTXt_SUPPORTED) || defined (PNG_READ_iTXt_SUPPORTED)
 /* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to
  * allow the caller to do multiple calls if required.  If the 'finish' flag is
  * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must
@@ -599,9 +608,9 @@
  */
 static int
 png_decompress_chunk(png_structrp png_ptr,
-   png_uint_32 chunklength, png_uint_32 prefix_size,
-   png_alloc_size_t *newlength /* must be initialized to the maximum! */,
-   int terminate /*add a '\0' to the end of the uncompressed data*/)
+    png_uint_32 chunklength, png_uint_32 prefix_size,
+    png_alloc_size_t *newlength /* must be initialized to the maximum! */,
+    int terminate /*add a '\0' to the end of the uncompressed data*/)
 {
    /* TODO: implement different limits for different types of chunk.
     *
@@ -638,8 +647,8 @@
          png_uint_32 lzsize = chunklength - prefix_size;
 
          ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
-            /* input: */ png_ptr->read_buffer + prefix_size, &lzsize,
-            /* output: */ NULL, newlength);
+             /* input: */ png_ptr->read_buffer + prefix_size, &lzsize,
+             /* output: */ NULL, newlength);
 
          if (ret == Z_STREAM_END)
          {
@@ -659,15 +668,17 @@
                 */
                png_alloc_size_t new_size = *newlength;
                png_alloc_size_t buffer_size = prefix_size + new_size +
-                  (terminate != 0);
+                   (terminate != 0);
                png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr,
-                  buffer_size));
+                   buffer_size));
 
                if (text != NULL)
                {
+                  memset(text, 0, buffer_size);
+
                   ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
-                     png_ptr->read_buffer + prefix_size, &lzsize,
-                     text + prefix_size, newlength);
+                      png_ptr->read_buffer + prefix_size, &lzsize,
+                      text + prefix_size, newlength);
 
                   if (ret == Z_STREAM_END)
                   {
@@ -712,7 +723,7 @@
                    * the extra space may otherwise be used as a Trojan Horse.
                    */
                   if (ret == Z_STREAM_END &&
-                     chunklength - prefix_size != lzsize)
+                      chunklength - prefix_size != lzsize)
                      png_chunk_benign_error(png_ptr, "extra compressed data");
                }
 
@@ -728,9 +739,7 @@
             {
                /* inflateReset failed, store the error message */
                png_zstream_error(png_ptr, ret);
-
-               if (ret == Z_STREAM_END)
-                  ret = PNG_UNEXPECTED_ZLIB_RETURN;
+               ret = PNG_UNEXPECTED_ZLIB_RETURN;
             }
          }
 
@@ -754,6 +763,7 @@
       return Z_MEM_ERROR;
    }
 }
+#endif /* READ_zTXt || READ_iTXt */
 #endif /* READ_COMPRESSED_TEXT */
 
 #ifdef PNG_READ_iCCP_SUPPORTED
@@ -762,8 +772,8 @@
  */
 static int
 png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
-   png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size,
-   int finish)
+    png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size,
+    int finish)
 {
    if (png_ptr->zowner == png_ptr->chunk_name)
    {
@@ -802,8 +812,8 @@
           * the available output is produced; this allows reading of truncated
           * streams.
           */
-         ret = PNG_INFLATE(png_ptr,
-            *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH));
+         ret = PNG_INFLATE(png_ptr, *chunk_bytes > 0 ?
+             Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH));
       }
       while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0));
 
@@ -821,7 +831,7 @@
       return Z_STREAM_ERROR;
    }
 }
-#endif
+#endif /* READ_iCCP */
 
 /* Read and check the IDHR chunk */
 
@@ -1009,7 +1019,7 @@
    if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
 #endif
    {
-      png_crc_finish(png_ptr, (int) length - num * 3);
+      png_crc_finish(png_ptr, (png_uint_32) (length - (unsigned int)num * 3));
    }
 
 #ifndef PNG_READ_OPT_PLTE_SUPPORTED
@@ -1292,7 +1302,7 @@
 
    png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
    (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy,
-      1/*prefer cHRM values*/);
+       1/*prefer cHRM values*/);
    png_colorspace_sync(png_ptr, info_ptr);
 }
 #endif
@@ -1371,11 +1381,13 @@
     * chunk is just ignored, so does not invalidate the color space.  An
     * alternative is to set the 'invalid' flags at the start of this routine
     * and only clear them in they were not set before and all the tests pass.
-    * The minimum 'deflate' stream is assumed to be just the 2 byte header and
-    * 4 byte checksum.  The keyword must be at least one character and there is
-    * a terminator (0) byte and the compression method.
     */
-   if (length < 9)
+
+   /* The keyword must be at least one character and there is a
+    * terminator (0) byte and the compression method byte, and the
+    * 'zlib' datastream is at least 11 bytes.
+    */
+   if (length < 14)
    {
       png_crc_finish(png_ptr, length);
       png_chunk_benign_error(png_ptr, "too short");
@@ -1407,6 +1419,16 @@
       png_crc_read(png_ptr, (png_bytep)keyword, read_length);
       length -= read_length;
 
+      /* The minimum 'zlib' stream is assumed to be just the 2 byte header,
+       * 5 bytes minimum 'deflate' stream, and the 4 byte checksum.
+       */
+      if (length < 11)
+      {
+         png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "too short");
+         return;
+      }
+
       keyword_length = 0;
       while (keyword_length < 80 && keyword_length < read_length &&
          keyword[keyword_length] != 0)
@@ -1425,53 +1447,52 @@
 
             if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK)
             {
-               Byte profile_header[132];
+               Byte profile_header[132]={0};
                Byte local_buffer[PNG_INFLATE_BUF_SIZE];
                png_alloc_size_t size = (sizeof profile_header);
 
                png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2);
                png_ptr->zstream.avail_in = read_length;
                (void)png_inflate_read(png_ptr, local_buffer,
-                  (sizeof local_buffer), &length, profile_header, &size,
-                  0/*finish: don't, because the output is too small*/);
+                   (sizeof local_buffer), &length, profile_header, &size,
+                   0/*finish: don't, because the output is too small*/);
 
                if (size == 0)
                {
                   /* We have the ICC profile header; do the basic header checks.
                    */
-                  const png_uint_32 profile_length =
-                     png_get_uint_32(profile_header);
+                  png_uint_32 profile_length = png_get_uint_32(profile_header);
 
                   if (png_icc_check_length(png_ptr, &png_ptr->colorspace,
-                     keyword, profile_length) != 0)
+                      keyword, profile_length) != 0)
                   {
                      /* The length is apparently ok, so we can check the 132
                       * byte header.
                       */
                      if (png_icc_check_header(png_ptr, &png_ptr->colorspace,
-                        keyword, profile_length, profile_header,
-                        png_ptr->color_type) != 0)
+                         keyword, profile_length, profile_header,
+                         png_ptr->color_type) != 0)
                      {
                         /* Now read the tag table; a variable size buffer is
                          * needed at this point, allocate one for the whole
                          * profile.  The header check has already validated
-                         * that none of these stuff will overflow.
+                         * that none of this stuff will overflow.
                          */
-                        const png_uint_32 tag_count = png_get_uint_32(
-                           profile_header+128);
+                        png_uint_32 tag_count =
+                           png_get_uint_32(profile_header + 128);
                         png_bytep profile = png_read_buffer(png_ptr,
-                           profile_length, 2/*silent*/);
+                            profile_length, 2/*silent*/);
 
                         if (profile != NULL)
                         {
                            memcpy(profile, profile_header,
-                              (sizeof profile_header));
+                               (sizeof profile_header));
 
                            size = 12 * tag_count;
 
                            (void)png_inflate_read(png_ptr, local_buffer,
-                              (sizeof local_buffer), &length,
-                              profile + (sizeof profile_header), &size, 0);
+                               (sizeof local_buffer), &length,
+                               profile + (sizeof profile_header), &size, 0);
 
                            /* Still expect a buffer error because we expect
                             * there to be some tag data!
@@ -1479,22 +1500,22 @@
                            if (size == 0)
                            {
                               if (png_icc_check_tag_table(png_ptr,
-                                 &png_ptr->colorspace, keyword, profile_length,
-                                 profile) != 0)
+                                  &png_ptr->colorspace, keyword, profile_length,
+                                  profile) != 0)
                               {
                                  /* The profile has been validated for basic
                                   * security issues, so read the whole thing in.
                                   */
                                  size = profile_length - (sizeof profile_header)
-                                    - 12 * tag_count;
+                                     - 12 * tag_count;
 
                                  (void)png_inflate_read(png_ptr, local_buffer,
-                                    (sizeof local_buffer), &length,
-                                    profile + (sizeof profile_header) +
-                                    12 * tag_count, &size, 1/*finish*/);
+                                     (sizeof local_buffer), &length,
+                                     profile + (sizeof profile_header) +
+                                     12 * tag_count, &size, 1/*finish*/);
 
                                  if (length > 0 && !(png_ptr->flags &
-                                       PNG_FLAG_BENIGN_ERRORS_WARN))
+                                     PNG_FLAG_BENIGN_ERRORS_WARN))
                                     errmsg = "extra compressed data";
 
                                  /* But otherwise allow extra data: */
@@ -1506,34 +1527,34 @@
                                         * keep going.
                                         */
                                        png_chunk_warning(png_ptr,
-                                          "extra compressed data");
+                                           "extra compressed data");
                                     }
 
                                     png_crc_finish(png_ptr, length);
                                     finished = 1;
 
-#                                   ifdef PNG_sRGB_SUPPORTED
+# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
                                     /* Check for a match against sRGB */
                                     png_icc_set_sRGB(png_ptr,
-                                       &png_ptr->colorspace, profile,
-                                       png_ptr->zstream.adler);
-#                                   endif
+                                        &png_ptr->colorspace, profile,
+                                        png_ptr->zstream.adler);
+# endif
 
                                     /* Steal the profile for info_ptr. */
                                     if (info_ptr != NULL)
                                     {
                                        png_free_data(png_ptr, info_ptr,
-                                          PNG_FREE_ICCP, 0);
+                                           PNG_FREE_ICCP, 0);
 
                                        info_ptr->iccp_name = png_voidcast(char*,
-                                          png_malloc_base(png_ptr,
-                                          keyword_length+1));
+                                           png_malloc_base(png_ptr,
+                                           keyword_length+1));
                                        if (info_ptr->iccp_name != NULL)
                                        {
                                           memcpy(info_ptr->iccp_name, keyword,
-                                             keyword_length+1);
+                                              keyword_length+1);
                                           info_ptr->iccp_proflen =
-                                             profile_length;
+                                              profile_length;
                                           info_ptr->iccp_profile = profile;
                                           png_ptr->read_buffer = NULL; /*steal*/
                                           info_ptr->free_me |= PNG_FREE_ICCP;
@@ -1562,19 +1583,11 @@
                                        return;
                                     }
                                  }
-
-                                 else if (size > 0)
-                                    errmsg = "truncated";
-
-#ifndef __COVERITY__
-                                 else
+                                 if (errmsg == NULL)
                                     errmsg = png_ptr->zstream.msg;
-#endif
                               }
-
                               /* else png_icc_check_tag_table output an error */
                            }
-
                            else /* profile truncated */
                               errmsg = png_ptr->zstream.msg;
                         }
@@ -1634,7 +1647,7 @@
    int entry_size, i;
    png_uint_32 skip = 0;
    png_uint_32 dl;
-   png_size_t max_dl;
+   size_t max_dl;
 
    png_debug(1, "in png_handle_sPLT");
 
@@ -1715,13 +1728,13 @@
    data_length = length - (png_uint_32)(entry_start - buffer);
 
    /* Integrity-check the data length */
-   if ((data_length % entry_size) != 0)
+   if ((data_length % (unsigned int)entry_size) != 0)
    {
       png_warning(png_ptr, "sPLT chunk has bad length");
       return;
    }
 
-   dl = (png_int_32)(data_length / entry_size);
+   dl = (png_uint_32)(data_length / (unsigned int)entry_size);
    max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry));
 
    if (dl > max_dl)
@@ -1730,10 +1743,10 @@
       return;
    }
 
-   new_palette.nentries = (png_int_32)(data_length / entry_size);
+   new_palette.nentries = (png_int_32)(data_length / (unsigned int)entry_size);
 
-   new_palette.entries = (png_sPLT_entryp)png_malloc_warn(
-       png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry)));
+   new_palette.entries = (png_sPLT_entryp)png_malloc_warn(png_ptr,
+       (png_alloc_size_t) new_palette.nentries * (sizeof (png_sPLT_entry)));
 
    if (new_palette.entries == NULL)
    {
@@ -1983,6 +1996,15 @@
 
    else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) /* GRAY */
    {
+      if (png_ptr->bit_depth <= 8)
+      {
+         if (buf[0] != 0 || buf[1] >= (unsigned int)(1 << png_ptr->bit_depth))
+         {
+            png_chunk_benign_error(png_ptr, "invalid gray level");
+            return;
+         }
+      }
+
       background.index = 0;
       background.red =
       background.green =
@@ -1992,6 +2014,15 @@
 
    else
    {
+      if (png_ptr->bit_depth <= 8)
+      {
+         if (buf[0] != 0 || buf[2] != 0 || buf[4] != 0)
+         {
+            png_chunk_benign_error(png_ptr, "invalid color");
+            return;
+         }
+      }
+
       background.index = 0;
       background.red = png_get_uint_16(buf);
       background.green = png_get_uint_16(buf + 2);
@@ -2003,6 +2034,69 @@
 }
 #endif
 
+#ifdef PNG_READ_eXIf_SUPPORTED
+void /* PRIVATE */
+png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   unsigned int i;
+
+   png_debug(1, "in png_handle_eXIf");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   if (length < 2)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "too short");
+      return;
+   }
+
+   else if (info_ptr == NULL || (info_ptr->valid & PNG_INFO_eXIf) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   info_ptr->free_me |= PNG_FREE_EXIF;
+
+   info_ptr->eXIf_buf = png_voidcast(png_bytep,
+             png_malloc_warn(png_ptr, length));
+
+   if (info_ptr->eXIf_buf == NULL)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return;
+   }
+
+   for (i = 0; i < length; i++)
+   {
+      png_byte buf[1];
+      png_crc_read(png_ptr, buf, 1);
+      info_ptr->eXIf_buf[i] = buf[0];
+      if (i == 1 && buf[0] != 'M' && buf[0] != 'I'
+                 && info_ptr->eXIf_buf[0] != buf[0])
+      {
+         png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "incorrect byte-order specifier");
+         png_free(png_ptr, info_ptr->eXIf_buf);
+         info_ptr->eXIf_buf = NULL;
+         return;
+      }
+   }
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   png_set_eXIf_1(png_ptr, info_ptr, length, info_ptr->eXIf_buf);
+
+   png_free(png_ptr, info_ptr->eXIf_buf);
+   info_ptr->eXIf_buf = NULL;
+}
+#endif
+
 #ifdef PNG_READ_hIST_SUPPORTED
 void /* PRIVATE */
 png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
@@ -2270,7 +2364,7 @@
    }
 
    png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
-      (png_charp)units, params);
+       (png_charp)units, params);
 
    png_free(png_ptr, params);
 }
@@ -2282,7 +2376,7 @@
 png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_bytep buffer;
-   png_size_t i;
+   size_t i;
    int state;
 
    png_debug(1, "in png_handle_sCAL");
@@ -2313,7 +2407,7 @@
    }
 
    png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
-      length + 1);
+       length + 1);
 
    buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
 
@@ -2352,7 +2446,7 @@
 
    else
    {
-      png_size_t heighti = i;
+      size_t heighti = i;
 
       state = 0;
       if (png_check_fp_number((png_const_charp)buffer, length,
@@ -2365,7 +2459,7 @@
       else
          /* This is the (only) success case. */
          png_set_sCAL_s(png_ptr, info_ptr, buffer[0],
-            (png_charp)buffer+1, (png_charp)buffer+heighti);
+             (png_charp)buffer+1, (png_charp)buffer+heighti);
    }
 }
 #endif
@@ -2465,8 +2559,8 @@
 
    if (buffer == NULL)
    {
-     png_chunk_benign_error(png_ptr, "out of memory");
-     return;
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return;
    }
 
    png_crc_read(png_ptr, buffer, length);
@@ -2531,6 +2625,9 @@
    if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
+   /* Note, "length" is sufficient here; we won't be adding
+    * a null terminator later.
+    */
    buffer = png_read_buffer(png_ptr, length, 2/*silent*/);
 
    if (buffer == NULL)
@@ -2573,27 +2670,32 @@
        * and text chunks.
        */
       if (png_decompress_chunk(png_ptr, length, keyword_length+2,
-         &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+          &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
       {
          png_text text;
 
-         /* It worked; png_ptr->read_buffer now looks like a tEXt chunk except
-          * for the extra compression type byte and the fact that it isn't
-          * necessarily '\0' terminated.
-          */
-         buffer = png_ptr->read_buffer;
-         buffer[uncompressed_length+(keyword_length+2)] = 0;
+         if (png_ptr->read_buffer == NULL)
+           errmsg="Read failure in png_handle_zTXt";
+         else
+         {
+            /* It worked; png_ptr->read_buffer now looks like a tEXt chunk
+             * except for the extra compression type byte and the fact that
+             * it isn't necessarily '\0' terminated.
+             */
+            buffer = png_ptr->read_buffer;
+            buffer[uncompressed_length+(keyword_length+2)] = 0;
 
-         text.compression = PNG_TEXT_COMPRESSION_zTXt;
-         text.key = (png_charp)buffer;
-         text.text = (png_charp)(buffer + keyword_length+2);
-         text.text_length = uncompressed_length;
-         text.itxt_length = 0;
-         text.lang = NULL;
-         text.lang_key = NULL;
+            text.compression = PNG_TEXT_COMPRESSION_zTXt;
+            text.key = (png_charp)buffer;
+            text.text = (png_charp)(buffer + keyword_length+2);
+            text.text_length = uncompressed_length;
+            text.itxt_length = 0;
+            text.lang = NULL;
+            text.lang_key = NULL;
 
-         if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
-            errmsg = "insufficient memory";
+            if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
+               errmsg = "insufficient memory";
+         }
       }
 
       else
@@ -2713,7 +2815,7 @@
           * iCCP and text chunks.
           */
          if (png_decompress_chunk(png_ptr, length, prefix_length,
-            &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+             &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
             buffer = png_ptr->read_buffer;
 
          else
@@ -2782,7 +2884,7 @@
    {
       PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name);
       /* The following is safe because of the PNG_SIZE_MAX init above */
-      png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/;
+      png_ptr->unknown_chunk.size = (size_t)length/*SAFE*/;
       /* 'mode' is a flag array, only the bottom four bits matter here */
       png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/;
 
@@ -2793,7 +2895,7 @@
       {
          /* Do a 'warn' here - it is handled below. */
          png_ptr->unknown_chunk.data = png_voidcast(png_bytep,
-            png_malloc_warn(png_ptr, length));
+             png_malloc_warn(png_ptr, length));
       }
    }
 
@@ -2818,7 +2920,7 @@
 /* Handle an unknown, or known but disabled, chunk */
 void /* PRIVATE */
 png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
-   png_uint_32 length, int keep)
+    png_uint_32 length, int keep)
 {
    int handled = 0; /* the chunk was handled */
 
@@ -2856,7 +2958,7 @@
       {
          /* Callback to user unknown chunk handler */
          int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr,
-            &png_ptr->unknown_chunk);
+             &png_ptr->unknown_chunk);
 
          /* ret is:
           * negative: An error occurred; png_chunk_error will be called.
@@ -2890,9 +2992,9 @@
                {
                   png_chunk_warning(png_ptr, "Saving unknown chunk:");
                   png_app_warning(png_ptr,
-                     "forcing save of an unhandled chunk;"
-                     " please call png_set_keep_unknown_chunks");
-                     /* with keep = PNG_HANDLE_CHUNK_IF_SAFE */
+                      "forcing save of an unhandled chunk;"
+                      " please call png_set_keep_unknown_chunks");
+                      /* with keep = PNG_HANDLE_CHUNK_IF_SAFE */
                }
 #              endif
                keep = PNG_HANDLE_CHUNK_IF_SAFE;
@@ -2969,7 +3071,7 @@
          case 2:
             png_ptr->user_chunk_cache_max = 1;
             png_chunk_benign_error(png_ptr, "no space in chunk cache");
-            /* FALL THROUGH */
+            /* FALLTHROUGH */
          case 1:
             /* NOTE: prior to 1.6.0 this case resulted in an unknown critical
              * chunk being skipped, now there will be a hard error below.
@@ -2978,14 +3080,14 @@
 
          default: /* not at limit */
             --(png_ptr->user_chunk_cache_max);
-            /* FALL THROUGH */
+            /* FALLTHROUGH */
          case 0: /* no limit */
 #  endif /* USER_LIMITS */
             /* Here when the limit isn't reached or when limits are compiled
              * out; store the chunk.
              */
             png_set_unknown_chunks(png_ptr, info_ptr,
-               &png_ptr->unknown_chunk, 1);
+                &png_ptr->unknown_chunk, 1);
             handled = 1;
 #  ifdef PNG_USER_LIMITS_SUPPORTED
             break;
@@ -3029,20 +3131,61 @@
  */
 
 void /* PRIVATE */
-png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name)
+png_check_chunk_name(png_const_structrp png_ptr, png_uint_32 chunk_name)
 {
    int i;
+   png_uint_32 cn=chunk_name;
 
    png_debug(1, "in png_check_chunk_name");
 
    for (i=1; i<=4; ++i)
    {
-      int c = chunk_name & 0xff;
+      int c = cn & 0xff;
 
       if (c < 65 || c > 122 || (c > 90 && c < 97))
          png_chunk_error(png_ptr, "invalid chunk type");
 
-      chunk_name >>= 8;
+      cn >>= 8;
+   }
+}
+
+void /* PRIVATE */
+png_check_chunk_length(png_const_structrp png_ptr, png_uint_32 length)
+{
+   png_alloc_size_t limit = PNG_UINT_31_MAX;
+
+# ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   if (png_ptr->user_chunk_malloc_max > 0 &&
+       png_ptr->user_chunk_malloc_max < limit)
+      limit = png_ptr->user_chunk_malloc_max;
+# elif PNG_USER_CHUNK_MALLOC_MAX > 0
+   if (PNG_USER_CHUNK_MALLOC_MAX < limit)
+      limit = PNG_USER_CHUNK_MALLOC_MAX;
+# endif
+   if (png_ptr->chunk_name == png_IDAT)
+   {
+      png_alloc_size_t idat_limit = PNG_UINT_31_MAX;
+      size_t row_factor =
+         (size_t)png_ptr->width
+         * (size_t)png_ptr->channels
+         * (png_ptr->bit_depth > 8? 2: 1)
+         + 1
+         + (png_ptr->interlaced? 6: 0);
+      if (png_ptr->height > PNG_UINT_32_MAX/row_factor)
+         idat_limit = PNG_UINT_31_MAX;
+      else
+         idat_limit = png_ptr->height * row_factor;
+      row_factor = row_factor > 32566? 32566 : row_factor;
+      idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */
+      idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX;
+      limit = limit < idat_limit? idat_limit : limit;
+   }
+
+   if (length > limit)
+   {
+      png_debug2(0," length = %lu, limit = %lu",
+         (unsigned long)length,(unsigned long)limit);
+      png_benign_error(png_ptr, "chunk data is too large");
    }
 }
 
@@ -3097,7 +3240,7 @@
 #     ifdef PNG_READ_PACKSWAP_SUPPORTED
       if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
          /* little-endian byte */
-         end_mask = 0xff << end_mask;
+         end_mask = (unsigned int)(0xff << end_mask);
 
       else /* big-endian byte */
 #     endif
@@ -3219,7 +3362,7 @@
          /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and
           * then pass:
           */
-         static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] =
+         static const png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] =
          {
             /* Little-endian byte masks for PACKSWAP */
             { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) },
@@ -3230,7 +3373,7 @@
          /* display_mask has only three entries for the odd passes, so index by
           * pass>>1.
           */
-         static PNG_CONST png_uint_32 display_mask[2][3][3] =
+         static const png_uint_32 display_mask[2][3][3] =
          {
             /* Little-endian byte masks for PACKSWAP */
             { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) },
@@ -3371,7 +3514,7 @@
                 */
                do
                {
-                  dp[0] = sp[0], dp[1] = sp[1];
+                  dp[0] = sp[0]; dp[1] = sp[1];
 
                   if (row_width <= bytes_to_jump)
                      return;
@@ -3392,7 +3535,7 @@
                 */
                for (;;)
                {
-                  dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2];
+                  dp[0] = sp[0]; dp[1] = sp[1]; dp[2] = sp[2];
 
                   if (row_width <= bytes_to_jump)
                      return;
@@ -3418,8 +3561,8 @@
                   /* Everything is aligned for png_uint_16 copies, but try for
                    * png_uint_32 first.
                    */
-                  if (png_isaligned(dp, png_uint_32) != 0 &&
-                      png_isaligned(sp, png_uint_32) != 0 &&
+                  if (png_isaligned(dp, png_uint_32) &&
+                      png_isaligned(sp, png_uint_32) &&
                       bytes_to_copy % (sizeof (png_uint_32)) == 0 &&
                       bytes_to_jump % (sizeof (png_uint_32)) == 0)
                   {
@@ -3539,11 +3682,11 @@
 #ifdef PNG_READ_INTERLACING_SUPPORTED
 void /* PRIVATE */
 png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
-   png_uint_32 transformations /* Because these may affect the byte layout */)
+    png_uint_32 transformations /* Because these may affect the byte layout */)
 {
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
    /* Offset to next interlace block */
-   static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+   static const unsigned int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
 
    png_debug(1, "in png_do_read_interlace");
    if (row != NULL && row_info != NULL)
@@ -3556,11 +3699,12 @@
       {
          case 1:
          {
-            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);
-            png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);
-            int sshift, dshift;
-            int s_start, s_end, s_inc;
-            int jstop = png_pass_inc[pass];
+            png_bytep sp = row + (size_t)((row_info->width - 1) >> 3);
+            png_bytep dp = row + (size_t)((final_width - 1) >> 3);
+            unsigned int sshift, dshift;
+            unsigned int s_start, s_end;
+            int s_inc;
+            int jstop = (int)png_pass_inc[pass];
             png_byte v;
             png_uint_32 i;
             int j;
@@ -3568,8 +3712,8 @@
 #ifdef PNG_READ_PACKSWAP_SUPPORTED
             if ((transformations & PNG_PACKSWAP) != 0)
             {
-                sshift = (int)((row_info->width + 7) & 0x07);
-                dshift = (int)((final_width + 7) & 0x07);
+                sshift = ((row_info->width + 7) & 0x07);
+                dshift = ((final_width + 7) & 0x07);
                 s_start = 7;
                 s_end = 0;
                 s_inc = -1;
@@ -3578,8 +3722,8 @@
             else
 #endif
             {
-                sshift = 7 - (int)((row_info->width + 7) & 0x07);
-                dshift = 7 - (int)((final_width + 7) & 0x07);
+                sshift = 7 - ((row_info->width + 7) & 0x07);
+                dshift = 7 - ((final_width + 7) & 0x07);
                 s_start = 0;
                 s_end = 7;
                 s_inc = 1;
@@ -3591,7 +3735,7 @@
                for (j = 0; j < jstop; j++)
                {
                   unsigned int tmp = *dp & (0x7f7f >> (7 - dshift));
-                  tmp |= v << dshift;
+                  tmp |= (unsigned int)(v << dshift);
                   *dp = (png_byte)(tmp & 0xff);
 
                   if (dshift == s_end)
@@ -3601,7 +3745,7 @@
                   }
 
                   else
-                     dshift += s_inc;
+                     dshift = (unsigned int)((int)dshift + s_inc);
                }
 
                if (sshift == s_end)
@@ -3611,7 +3755,7 @@
                }
 
                else
-                  sshift += s_inc;
+                  sshift = (unsigned int)((int)sshift + s_inc);
             }
             break;
          }
@@ -3620,16 +3764,17 @@
          {
             png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
             png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
-            int sshift, dshift;
-            int s_start, s_end, s_inc;
-            int jstop = png_pass_inc[pass];
+            unsigned int sshift, dshift;
+            unsigned int s_start, s_end;
+            int s_inc;
+            int jstop = (int)png_pass_inc[pass];
             png_uint_32 i;
 
 #ifdef PNG_READ_PACKSWAP_SUPPORTED
             if ((transformations & PNG_PACKSWAP) != 0)
             {
-               sshift = (int)(((row_info->width + 3) & 0x03) << 1);
-               dshift = (int)(((final_width + 3) & 0x03) << 1);
+               sshift = (((row_info->width + 3) & 0x03) << 1);
+               dshift = (((final_width + 3) & 0x03) << 1);
                s_start = 6;
                s_end = 0;
                s_inc = -2;
@@ -3638,8 +3783,8 @@
             else
 #endif
             {
-               sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1);
-               dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1);
+               sshift = ((3 - ((row_info->width + 3) & 0x03)) << 1);
+               dshift = ((3 - ((final_width + 3) & 0x03)) << 1);
                s_start = 0;
                s_end = 6;
                s_inc = 2;
@@ -3654,7 +3799,7 @@
                for (j = 0; j < jstop; j++)
                {
                   unsigned int tmp = *dp & (0x3f3f >> (6 - dshift));
-                  tmp |= v << dshift;
+                  tmp |= (unsigned int)(v << dshift);
                   *dp = (png_byte)(tmp & 0xff);
 
                   if (dshift == s_end)
@@ -3664,7 +3809,7 @@
                   }
 
                   else
-                     dshift += s_inc;
+                     dshift = (unsigned int)((int)dshift + s_inc);
                }
 
                if (sshift == s_end)
@@ -3674,25 +3819,26 @@
                }
 
                else
-                  sshift += s_inc;
+                  sshift = (unsigned int)((int)sshift + s_inc);
             }
             break;
          }
 
          case 4:
          {
-            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);
-            png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);
-            int sshift, dshift;
-            int s_start, s_end, s_inc;
+            png_bytep sp = row + (size_t)((row_info->width - 1) >> 1);
+            png_bytep dp = row + (size_t)((final_width - 1) >> 1);
+            unsigned int sshift, dshift;
+            unsigned int s_start, s_end;
+            int s_inc;
             png_uint_32 i;
-            int jstop = png_pass_inc[pass];
+            int jstop = (int)png_pass_inc[pass];
 
 #ifdef PNG_READ_PACKSWAP_SUPPORTED
             if ((transformations & PNG_PACKSWAP) != 0)
             {
-               sshift = (int)(((row_info->width + 1) & 0x01) << 2);
-               dshift = (int)(((final_width + 1) & 0x01) << 2);
+               sshift = (((row_info->width + 1) & 0x01) << 2);
+               dshift = (((final_width + 1) & 0x01) << 2);
                s_start = 4;
                s_end = 0;
                s_inc = -4;
@@ -3701,8 +3847,8 @@
             else
 #endif
             {
-               sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2);
-               dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2);
+               sshift = ((1 - ((row_info->width + 1) & 0x01)) << 2);
+               dshift = ((1 - ((final_width + 1) & 0x01)) << 2);
                s_start = 0;
                s_end = 4;
                s_inc = 4;
@@ -3716,7 +3862,7 @@
                for (j = 0; j < jstop; j++)
                {
                   unsigned int tmp = *dp & (0xf0f >> (4 - dshift));
-                  tmp |= v << dshift;
+                  tmp |= (unsigned int)(v << dshift);
                   *dp = (png_byte)(tmp & 0xff);
 
                   if (dshift == s_end)
@@ -3726,7 +3872,7 @@
                   }
 
                   else
-                     dshift += s_inc;
+                     dshift = (unsigned int)((int)dshift + s_inc);
                }
 
                if (sshift == s_end)
@@ -3736,21 +3882,21 @@
                }
 
                else
-                  sshift += s_inc;
+                  sshift = (unsigned int)((int)sshift + s_inc);
             }
             break;
          }
 
          default:
          {
-            png_size_t pixel_bytes = (row_info->pixel_depth >> 3);
+            size_t pixel_bytes = (row_info->pixel_depth >> 3);
 
-            png_bytep sp = row + (png_size_t)(row_info->width - 1)
+            png_bytep sp = row + (size_t)(row_info->width - 1)
                 * pixel_bytes;
 
-            png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;
+            png_bytep dp = row + (size_t)(final_width - 1) * pixel_bytes;
 
-            int jstop = png_pass_inc[pass];
+            int jstop = (int)png_pass_inc[pass];
             png_uint_32 i;
 
             for (i = 0; i < row_info->width; i++)
@@ -3783,10 +3929,10 @@
 
 static void
 png_read_filter_row_sub(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
+    png_const_bytep prev_row)
 {
-   png_size_t i;
-   png_size_t istop = row_info->rowbytes;
+   size_t i;
+   size_t istop = row_info->rowbytes;
    unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
    png_bytep rp = row + bpp;
 
@@ -3801,10 +3947,10 @@
 
 static void
 png_read_filter_row_up(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
+    png_const_bytep prev_row)
 {
-   png_size_t i;
-   png_size_t istop = row_info->rowbytes;
+   size_t i;
+   size_t istop = row_info->rowbytes;
    png_bytep rp = row;
    png_const_bytep pp = prev_row;
 
@@ -3817,13 +3963,13 @@
 
 static void
 png_read_filter_row_avg(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
+    png_const_bytep prev_row)
 {
-   png_size_t i;
+   size_t i;
    png_bytep rp = row;
    png_const_bytep pp = prev_row;
    unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
-   png_size_t istop = row_info->rowbytes - bpp;
+   size_t istop = row_info->rowbytes - bpp;
 
    for (i = 0; i < bpp; i++)
    {
@@ -3844,7 +3990,7 @@
 
 static void
 png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
+    png_const_bytep prev_row)
 {
    png_bytep rp_end = row + row_info->rowbytes;
    int a, c;
@@ -3878,7 +4024,10 @@
       /* Find the best predictor, the least of pa, pb, pc favoring the earlier
        * ones in the case of a tie.
        */
-      if (pb < pa) pa = pb, a = b;
+      if (pb < pa)
+      {
+         pa = pb; a = b;
+      }
       if (pc < pa) a = c;
 
       /* Calculate the current pixel in a, and move the previous row pixel to c
@@ -3892,9 +4041,9 @@
 
 static void
 png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row)
+    png_const_bytep prev_row)
 {
-   int bpp = (row_info->pixel_depth + 7) >> 3;
+   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
    png_bytep rp_end = row + bpp;
 
    /* Process the first pixel in the row completely (this is the same as 'up'
@@ -3907,7 +4056,7 @@
    }
 
    /* Remainder */
-   rp_end += row_info->rowbytes - bpp;
+   rp_end = rp_end + (row_info->rowbytes - bpp);
 
    while (row < rp_end)
    {
@@ -3930,7 +4079,10 @@
       pc = (p + pc) < 0 ? -(p + pc) : p + pc;
 #endif
 
-      if (pb < pa) pa = pb, a = b;
+      if (pb < pa)
+      {
+         pa = pb; a = b;
+      }
       if (pc < pa) a = c;
 
       a += *row;
@@ -3977,7 +4129,7 @@
 
 void /* PRIVATE */
 png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row,
-   png_const_bytep prev_row, int filter)
+    png_const_bytep prev_row, int filter)
 {
    /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define
     * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic
@@ -3995,7 +4147,7 @@
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 void /* PRIVATE */
 png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
-   png_alloc_size_t avail_out)
+    png_alloc_size_t avail_out)
 {
    /* Loop reading IDATs and decompressing the result into output[avail_out] */
    png_ptr->zstream.next_out = output;
@@ -4176,16 +4328,16 @@
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
 
    /* Start of interlace block */
-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
 
    /* Offset to next interlace block */
-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
 
    /* Start of interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
 
    /* Offset to next interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
 
    png_debug(1, "in png_read_finish_row");
    png_ptr->row_number++;
@@ -4241,19 +4393,19 @@
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
 
    /* Start of interlace block */
-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
 
    /* Offset to next interlace block */
-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
 
    /* Start of interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
 
    /* Offset to next interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
 
-   int max_pixel_depth;
-   png_size_t row_bytes;
+   unsigned int max_pixel_depth;
+   size_t row_bytes;
 
    png_debug(1, "in png_read_start_row");
 
@@ -4281,7 +4433,7 @@
       png_ptr->iwidth = png_ptr->width;
    }
 
-   max_pixel_depth = png_ptr->pixel_depth;
+   max_pixel_depth = (unsigned int)png_ptr->pixel_depth;
 
    /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpler set of
     * calculations to calculate the final pixel depth, then
@@ -4416,7 +4568,7 @@
 defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
    if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
    {
-      int user_pixel_depth = png_ptr->user_transform_depth *
+      unsigned int user_pixel_depth = png_ptr->user_transform_depth *
          png_ptr->user_transform_channels;
 
       if (user_pixel_depth > max_pixel_depth)
@@ -4438,7 +4590,7 @@
     * for safety's sake
     */
    row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) +
-       1 + ((max_pixel_depth + 7) >> 3);
+       1 + ((max_pixel_depth + 7) >> 3U);
 
 #ifdef PNG_MAX_MALLOC_64K
    if (row_bytes > (png_uint_32)65536L)
@@ -4447,42 +4599,42 @@
 
    if (row_bytes + 48 > png_ptr->old_big_row_buf_size)
    {
-     png_free(png_ptr, png_ptr->big_row_buf);
-     png_free(png_ptr, png_ptr->big_prev_row);
+      png_free(png_ptr, png_ptr->big_row_buf);
+      png_free(png_ptr, png_ptr->big_prev_row);
 
-     if (png_ptr->interlaced != 0)
-        png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
-            row_bytes + 48);
+      if (png_ptr->interlaced != 0)
+         png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
+             row_bytes + 48);
 
-     else
-        png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
+      else
+         png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
 
-     png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
+      png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
 
 #ifdef PNG_ALIGNED_MEMORY_SUPPORTED
-     /* Use 16-byte aligned memory for row_buf with at least 16 bytes
-      * of padding before and after row_buf; treat prev_row similarly.
-      * NOTE: the alignment is to the start of the pixels, one beyond the start
-      * of the buffer, because of the filter byte.  Prior to libpng 1.5.6 this
-      * was incorrect; the filter byte was aligned, which had the exact
-      * opposite effect of that intended.
-      */
-     {
-        png_bytep temp = png_ptr->big_row_buf + 32;
-        int extra = (int)((temp - (png_bytep)0) & 0x0f);
-        png_ptr->row_buf = temp - extra - 1/*filter byte*/;
+      /* Use 16-byte aligned memory for row_buf with at least 16 bytes
+       * of padding before and after row_buf; treat prev_row similarly.
+       * NOTE: the alignment is to the start of the pixels, one beyond the start
+       * of the buffer, because of the filter byte.  Prior to libpng 1.5.6 this
+       * was incorrect; the filter byte was aligned, which had the exact
+       * opposite effect of that intended.
+       */
+      {
+         png_bytep temp = png_ptr->big_row_buf + 32;
+         int extra = (int)((temp - (png_bytep)0) & 0x0f);
+         png_ptr->row_buf = temp - extra - 1/*filter byte*/;
 
-        temp = png_ptr->big_prev_row + 32;
-        extra = (int)((temp - (png_bytep)0) & 0x0f);
-        png_ptr->prev_row = temp - extra - 1/*filter byte*/;
-     }
+         temp = png_ptr->big_prev_row + 32;
+         extra = (int)((temp - (png_bytep)0) & 0x0f);
+         png_ptr->prev_row = temp - extra - 1/*filter byte*/;
+      }
 
 #else
-     /* Use 31 bytes of padding before and 17 bytes after row_buf. */
-     png_ptr->row_buf = png_ptr->big_row_buf + 31;
-     png_ptr->prev_row = png_ptr->big_prev_row + 31;
+      /* Use 31 bytes of padding before and 17 bytes after row_buf. */
+      png_ptr->row_buf = png_ptr->big_row_buf + 31;
+      png_ptr->prev_row = png_ptr->big_prev_row + 31;
 #endif
-     png_ptr->old_big_row_buf_size = row_bytes + 48;
+      png_ptr->old_big_row_buf_size = row_bytes + 48;
    }
 
 #ifdef PNG_MAX_MALLOC_64K
@@ -4507,7 +4659,7 @@
     * does not, so free the read buffer now regardless; the sequential reader
     * reallocates it on demand.
     */
-   if (png_ptr->read_buffer != 0)
+   if (png_ptr->read_buffer != NULL)
    {
       png_bytep buffer = png_ptr->read_buffer;
 
diff --git a/third_party/libpng16/pngset.c b/third_party/libpng16/pngset.c
index 1c51270..ec75dbe 100644
--- a/third_party/libpng16/pngset.c
+++ b/third_party/libpng16/pngset.c
@@ -1,10 +1,10 @@
 
 /* pngset.c - storage of image information into info struct
  *
- * Last changed in libpng 1.6.21 [January 15, 2016]
- * Copyright (c) 1998-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -104,14 +104,14 @@
     double green_x, double green_y, double blue_x, double blue_y)
 {
    png_set_cHRM_fixed(png_ptr, info_ptr,
-      png_fixed(png_ptr, white_x, "cHRM White X"),
-      png_fixed(png_ptr, white_y, "cHRM White Y"),
-      png_fixed(png_ptr, red_x, "cHRM Red X"),
-      png_fixed(png_ptr, red_y, "cHRM Red Y"),
-      png_fixed(png_ptr, green_x, "cHRM Green X"),
-      png_fixed(png_ptr, green_y, "cHRM Green Y"),
-      png_fixed(png_ptr, blue_x, "cHRM Blue X"),
-      png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
+       png_fixed(png_ptr, white_x, "cHRM White X"),
+       png_fixed(png_ptr, white_y, "cHRM White Y"),
+       png_fixed(png_ptr, red_x, "cHRM Red X"),
+       png_fixed(png_ptr, red_y, "cHRM Red Y"),
+       png_fixed(png_ptr, green_x, "cHRM Green X"),
+       png_fixed(png_ptr, green_y, "cHRM Green Y"),
+       png_fixed(png_ptr, blue_x, "cHRM Blue X"),
+       png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
 }
 
 void PNGAPI
@@ -120,20 +120,67 @@
     double blue_X, double blue_Y, double blue_Z)
 {
    png_set_cHRM_XYZ_fixed(png_ptr, info_ptr,
-      png_fixed(png_ptr, red_X, "cHRM Red X"),
-      png_fixed(png_ptr, red_Y, "cHRM Red Y"),
-      png_fixed(png_ptr, red_Z, "cHRM Red Z"),
-      png_fixed(png_ptr, green_X, "cHRM Green X"),
-      png_fixed(png_ptr, green_Y, "cHRM Green Y"),
-      png_fixed(png_ptr, green_Z, "cHRM Green Z"),
-      png_fixed(png_ptr, blue_X, "cHRM Blue X"),
-      png_fixed(png_ptr, blue_Y, "cHRM Blue Y"),
-      png_fixed(png_ptr, blue_Z, "cHRM Blue Z"));
+       png_fixed(png_ptr, red_X, "cHRM Red X"),
+       png_fixed(png_ptr, red_Y, "cHRM Red Y"),
+       png_fixed(png_ptr, red_Z, "cHRM Red Z"),
+       png_fixed(png_ptr, green_X, "cHRM Green X"),
+       png_fixed(png_ptr, green_Y, "cHRM Green Y"),
+       png_fixed(png_ptr, green_Z, "cHRM Green Z"),
+       png_fixed(png_ptr, blue_X, "cHRM Blue X"),
+       png_fixed(png_ptr, blue_Y, "cHRM Blue Y"),
+       png_fixed(png_ptr, blue_Z, "cHRM Blue Z"));
 }
 #  endif /* FLOATING_POINT */
 
 #endif /* cHRM */
 
+#ifdef PNG_eXIf_SUPPORTED
+void PNGAPI
+png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_bytep eXIf_buf)
+{
+  png_warning(png_ptr, "png_set_eXIf does not work; use png_set_eXIf_1");
+  PNG_UNUSED(info_ptr)
+  PNG_UNUSED(eXIf_buf)
+}
+
+void PNGAPI
+png_set_eXIf_1(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_32 num_exif, png_bytep eXIf_buf)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function", "eXIf");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (info_ptr->exif)
+   {
+      png_free(png_ptr, info_ptr->exif);
+      info_ptr->exif = NULL;
+   }
+
+   info_ptr->num_exif = num_exif;
+
+   info_ptr->exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr,
+       info_ptr->num_exif));
+
+   if (info_ptr->exif == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory for eXIf chunk data");
+      return;
+   }
+
+   info_ptr->free_me |= PNG_FREE_EXIF;
+
+   for (i = 0; i < (int) info_ptr->num_exif; i++)
+      info_ptr->exif[i] = eXIf_buf[i];
+
+   info_ptr->valid |= PNG_INFO_eXIf;
+}
+#endif /* eXIf */
+
 #ifdef PNG_gAMA_SUPPORTED
 void PNGFAPI
 png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
@@ -266,7 +313,7 @@
     png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type,
     int nparams, png_const_charp units, png_charpp params)
 {
-   png_size_t length;
+   size_t length;
    int i;
 
    png_debug1(1, "in %s storage function", "pCAL");
@@ -328,10 +375,10 @@
 
    length = strlen(units) + 1;
    png_debug1(3, "allocating units for info (%lu bytes)",
-     (unsigned long)length);
+       (unsigned long)length);
 
    info_ptr->pcal_units = png_voidcast(png_charp,
-      png_malloc_warn(png_ptr, length));
+       png_malloc_warn(png_ptr, length));
 
    if (info_ptr->pcal_units == NULL)
    {
@@ -343,7 +390,7 @@
    memcpy(info_ptr->pcal_units, units, length);
 
    info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
-       (png_size_t)((nparams + 1) * (sizeof (png_charp)))));
+       (size_t)(((unsigned int)nparams + 1) * (sizeof (png_charp)))));
 
    if (info_ptr->pcal_params == NULL)
    {
@@ -352,7 +399,8 @@
       return;
    }
 
-   memset(info_ptr->pcal_params, 0, (nparams + 1) * (sizeof (png_charp)));
+   memset(info_ptr->pcal_params, 0, ((unsigned int)nparams + 1) *
+       (sizeof (png_charp)));
 
    for (i = 0; i < nparams; i++)
    {
@@ -382,7 +430,7 @@
 png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,
     int unit, png_const_charp swidth, png_const_charp sheight)
 {
-   png_size_t lengthw = 0, lengthh = 0;
+   size_t lengthw = 0, lengthh = 0;
 
    png_debug1(1, "in %s storage function", "sCAL");
 
@@ -410,7 +458,7 @@
    png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw);
 
    info_ptr->scal_s_width = png_voidcast(png_charp,
-      png_malloc_warn(png_ptr, lengthw));
+       png_malloc_warn(png_ptr, lengthw));
 
    if (info_ptr->scal_s_width == NULL)
    {
@@ -426,7 +474,7 @@
    png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh);
 
    info_ptr->scal_s_height = png_voidcast(png_charp,
-      png_malloc_warn(png_ptr, lengthh));
+       png_malloc_warn(png_ptr, lengthh));
 
    if (info_ptr->scal_s_height == NULL)
    {
@@ -465,9 +513,9 @@
       char sheight[PNG_sCAL_MAX_DIGITS+1];
 
       png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width,
-         PNG_sCAL_PRECISION);
+          PNG_sCAL_PRECISION);
       png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height,
-         PNG_sCAL_PRECISION);
+          PNG_sCAL_PRECISION);
 
       png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
    }
@@ -575,7 +623,8 @@
        PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
 
    if (num_palette > 0)
-      memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color)));
+      memcpy(png_ptr->palette, palette, (unsigned int)num_palette *
+          (sizeof (png_color)));
    info_ptr->palette = png_ptr->palette;
    info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
 
@@ -642,7 +691,7 @@
 {
    png_charp new_iccp_name;
    png_bytep new_iccp_profile;
-   png_size_t length;
+   size_t length;
 
    png_debug1(1, "in %s storage function", "iCCP");
 
@@ -660,7 +709,7 @@
     */
    {
       int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name,
-         proflen, profile, info_ptr->color_type);
+          proflen, profile, info_ptr->color_type);
 
       png_colorspace_sync_info(png_ptr, info_ptr);
 
@@ -685,7 +734,7 @@
 
    memcpy(new_iccp_name, name, length);
    new_iccp_profile = png_voidcast(png_bytep,
-      png_malloc_warn(png_ptr, proflen));
+       png_malloc_warn(png_ptr, proflen));
 
    if (new_iccp_profile == NULL)
    {
@@ -760,14 +809,14 @@
           * the overflow checks.
           */
          new_text = png_voidcast(png_textp,png_realloc_array(png_ptr,
-            info_ptr->text, old_num_text, max_text-old_num_text,
-            sizeof *new_text));
+             info_ptr->text, old_num_text, max_text-old_num_text,
+             sizeof *new_text));
       }
 
       if (new_text == NULL)
       {
          png_chunk_report(png_ptr, "too many text chunks",
-            PNG_CHUNK_WRITE_ERROR);
+             PNG_CHUNK_WRITE_ERROR);
 
          return 1;
       }
@@ -795,7 +844,7 @@
           text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST)
       {
          png_chunk_report(png_ptr, "text compression mode is out of range",
-            PNG_CHUNK_WRITE_ERROR);
+             PNG_CHUNK_WRITE_ERROR);
          continue;
       }
 
@@ -827,7 +876,7 @@
 #  else /* iTXt */
       {
          png_chunk_report(png_ptr, "iTXt chunk not supported",
-            PNG_CHUNK_WRITE_ERROR);
+             PNG_CHUNK_WRITE_ERROR);
          continue;
       }
 #  endif
@@ -856,7 +905,7 @@
       if (textp->key == NULL)
       {
          png_chunk_report(png_ptr, "text chunk: out of memory",
-               PNG_CHUNK_WRITE_ERROR);
+             PNG_CHUNK_WRITE_ERROR);
 
          return 1;
       }
@@ -968,9 +1017,8 @@
        {
          /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */
           info_ptr->trans_alpha = png_voidcast(png_bytep,
-             png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
-
-          memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans);
+              png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
+          memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans);
        }
        png_ptr->trans_alpha = info_ptr->trans_alpha;
    }
@@ -989,7 +1037,7 @@
              trans_color->green > sample_max ||
              trans_color->blue > sample_max)))
             png_warning(png_ptr,
-               "tRNS chunk has out-of-range samples for bit_depth");
+                "tRNS chunk has out-of-range samples for bit_depth");
       }
 #endif
 
@@ -1031,8 +1079,8 @@
     * overflows.  Notice that the parameters are (int) and (size_t)
     */
    np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr,
-      info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries,
-      sizeof *np));
+       info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries,
+       sizeof *np));
 
    if (np == NULL)
    {
@@ -1050,7 +1098,7 @@
 
    do
    {
-      png_size_t length;
+      size_t length;
 
       /* Skip invalid input entries */
       if (entries->name == NULL || entries->entries == NULL)
@@ -1093,7 +1141,7 @@
        * checked it when doing the allocation.
        */
       memcpy(np->entries, entries->entries,
-         entries->nentries * sizeof (png_sPLT_entry));
+          (unsigned int)entries->nentries * sizeof (png_sPLT_entry));
 
       /* Note that 'continue' skips the advance of the out pointer and out
        * count, so an invalid entry is not added.
@@ -1101,8 +1149,9 @@
       info_ptr->valid |= PNG_INFO_sPLT;
       ++(info_ptr->splt_palettes_num);
       ++np;
+      ++entries;
    }
-   while (++entries, --nentries);
+   while (--nentries);
 
    if (nentries > 0)
       png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
@@ -1123,10 +1172,10 @@
    {
       /* Write struct, so unknown chunks come from the app */
       png_app_warning(png_ptr,
-         "png_set_unknown_chunks now expects a valid location");
+          "png_set_unknown_chunks now expects a valid location");
       /* Use the old behavior */
       location = (png_byte)(png_ptr->mode &
-         (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT));
+          (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT));
    }
 
    /* This need not be an internal error - if the app calls
@@ -1149,7 +1198,7 @@
 
 void PNGAPI
 png_set_unknown_chunks(png_const_structrp png_ptr,
-   png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
+    png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
 {
    png_unknown_chunkp np;
 
@@ -1188,13 +1237,13 @@
     * appropriate to read or write.
     */
    np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr,
-         info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns,
-         sizeof *np));
+       info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns,
+       sizeof *np));
 
    if (np == NULL)
    {
       png_chunk_report(png_ptr, "too many unknown chunks",
-         PNG_CHUNK_WRITE_ERROR);
+          PNG_CHUNK_WRITE_ERROR);
 
       return;
    }
@@ -1223,12 +1272,12 @@
       else
       {
          np->data = png_voidcast(png_bytep,
-            png_malloc_base(png_ptr, unknowns->size));
+             png_malloc_base(png_ptr, unknowns->size));
 
          if (np->data == NULL)
          {
             png_chunk_report(png_ptr, "unknown chunk: out of memory",
-               PNG_CHUNK_WRITE_ERROR);
+                PNG_CHUNK_WRITE_ERROR);
             /* But just skip storing the unknown chunk */
             continue;
          }
@@ -1262,7 +1311,7 @@
       {
          png_app_error(png_ptr, "invalid unknown chunk location");
          /* Fake out the pre 1.6.0 behavior: */
-         if ((location & PNG_HAVE_IDAT) != 0) /* undocumented! */
+         if (((unsigned int)location & PNG_HAVE_IDAT) != 0) /* undocumented! */
             location = PNG_AFTER_IDAT;
 
          else
@@ -1350,9 +1399,10 @@
       /* Ignore all unknown chunks and all chunks recognized by
        * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND
        */
-      static PNG_CONST png_byte chunks_to_ignore[] = {
+      static const png_byte chunks_to_ignore[] = {
          98,  75,  71,  68, '\0',  /* bKGD */
          99,  72,  82,  77, '\0',  /* cHRM */
+        101,  88,  73, 102, '\0',  /* eXIf */
         103,  65,  77,  65, '\0',  /* gAMA */
         104,  73,  83,  84, '\0',  /* hIST */
         105,  67,  67,  80, '\0',  /* iCCP */
@@ -1386,7 +1436,7 @@
          return;
       }
 
-      num_chunks = num_chunks_in;
+      num_chunks = (unsigned int)num_chunks_in;
    }
 
    old_num_chunks = png_ptr->num_chunk_list;
@@ -1435,7 +1485,7 @@
       for (i=0; i<num_chunks; ++i)
       {
          old_num_chunks = add_one_chunk(new_list, old_num_chunks,
-            chunk_list+5*i, keep);
+             chunk_list+5*i, keep);
       }
 
       /* Now remove any spurious 'default' entries. */
@@ -1513,62 +1563,62 @@
 #endif
 
 void PNGAPI
-png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size)
+png_set_compression_buffer_size(png_structrp png_ptr, size_t size)
 {
-    if (png_ptr == NULL)
-       return;
+   if (png_ptr == NULL)
+      return;
 
-    if (size == 0 || size > PNG_UINT_31_MAX)
-       png_error(png_ptr, "invalid compression buffer size");
+   if (size == 0 || size > PNG_UINT_31_MAX)
+      png_error(png_ptr, "invalid compression buffer size");
 
 #  ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-      if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
-      {
-         png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */
-         return;
-      }
+   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
+   {
+      png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */
+      return;
+   }
 #  endif
 
 #  ifdef PNG_WRITE_SUPPORTED
-      if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
+   if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
+   {
+      if (png_ptr->zowner != 0)
       {
-         if (png_ptr->zowner != 0)
-         {
-            png_warning(png_ptr,
-              "Compression buffer size cannot be changed because it is in use");
+         png_warning(png_ptr,
+             "Compression buffer size cannot be changed because it is in use");
 
-            return;
-         }
+         return;
+      }
 
 #ifndef __COVERITY__
-         /* Some compilers complain that this is always false.  However, it
-          * can be true when integer overflow happens.
-          */
-         if (size > ZLIB_IO_MAX)
-         {
-            png_warning(png_ptr,
-               "Compression buffer size limited to system maximum");
-            size = ZLIB_IO_MAX; /* must fit */
-         }
+      /* Some compilers complain that this is always false.  However, it
+       * can be true when integer overflow happens.
+       */
+      if (size > ZLIB_IO_MAX)
+      {
+         png_warning(png_ptr,
+             "Compression buffer size limited to system maximum");
+         size = ZLIB_IO_MAX; /* must fit */
+      }
 #endif
 
-         if (size < 6)
-         {
-            /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH
-             * if this is permitted.
-             */
-            png_warning(png_ptr,
-               "Compression buffer size cannot be reduced below 6");
+      if (size < 6)
+      {
+         /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH
+          * if this is permitted.
+          */
+         png_warning(png_ptr,
+             "Compression buffer size cannot be reduced below 6");
 
-            return;
-         }
-
-         if (png_ptr->zbuffer_size != size)
-         {
-            png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
-            png_ptr->zbuffer_size = (uInt)size;
-         }
+         return;
       }
+
+      if (png_ptr->zbuffer_size != size)
+      {
+         png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
+         png_ptr->zbuffer_size = (uInt)size;
+      }
+   }
 #  endif
 }
 
@@ -1576,7 +1626,7 @@
 png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask)
 {
    if (png_ptr != NULL && info_ptr != NULL)
-      info_ptr->valid &= ~mask;
+      info_ptr->valid &= (unsigned int)(~mask);
 }
 
 
@@ -1675,7 +1725,9 @@
 png_uint_32 /* PRIVATE */
 png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
 {
+#ifdef PNG_WARNINGS_SUPPORTED
    png_const_charp orig_key = key;
+#endif
    png_uint_32 key_len = 0;
    int bad_character = 0;
    int space = 1;
@@ -1693,14 +1745,16 @@
       png_byte ch = (png_byte)*key++;
 
       if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/))
-         *new_key++ = ch, ++key_len, space = 0;
+      {
+         *new_key++ = ch; ++key_len; space = 0;
+      }
 
       else if (space == 0)
       {
          /* A space or an invalid character when one wasn't seen immediately
           * before; output just a space.
           */
-         *new_key++ = 32, ++key_len, space = 1;
+         *new_key++ = 32; ++key_len; space = 1;
 
          /* If the character was not a space then it is invalid. */
          if (ch != 32)
@@ -1713,7 +1767,7 @@
 
    if (key_len > 0 && space != 0) /* trailing space */
    {
-      --key_len, --new_key;
+      --key_len; --new_key;
       if (bad_character == 0)
          bad_character = 32;
    }
@@ -1738,7 +1792,9 @@
 
       png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");
    }
-#endif /* WARNINGS */
+#else /* !WARNINGS */
+   PNG_UNUSED(png_ptr)
+#endif /* !WARNINGS */
 
    return key_len;
 }
diff --git a/third_party/libpng16/pngstruct.h b/third_party/libpng16/pngstruct.h
index c1f35ed..8bdc7ce 100644
--- a/third_party/libpng16/pngstruct.h
+++ b/third_party/libpng16/pngstruct.h
@@ -1,10 +1,10 @@
 
 /* pngstruct.h - header file for PNG reference library
  *
- * Last changed in libpng 1.6.18 [July 23, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -47,7 +47,7 @@
 /* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
  * can handle at once.  This type need be no larger than 16 bits (so maximum of
  * 65535), this define allows us to discover how big it is, but limited by the
- * maximuum for png_size_t.  The value can be overriden in a library build
+ * maximum for size_t.  The value can be overridden in a library build
  * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
  * lower value (e.g. 255 works).  A lower value may help memory usage (slightly)
  * and may even improve performance on some systems (and degrade it on others.)
@@ -214,7 +214,7 @@
    png_uint_32 height;        /* height of image in pixels */
    png_uint_32 num_rows;      /* number of rows in current pass */
    png_uint_32 usr_width;     /* width of row at start of write */
-   png_size_t rowbytes;       /* size of row in bytes */
+   size_t rowbytes;           /* size of row in bytes */
    png_uint_32 iwidth;        /* width of current interlaced row in pixels */
    png_uint_32 row_number;    /* current row in interlace pass */
    png_uint_32 chunk_name;    /* PNG_CHUNK() id of current chunk */
@@ -232,7 +232,7 @@
    png_bytep try_row;    /* buffer to save trial row when filtering */
    png_bytep tst_row;    /* buffer to save best trial row when filtering */
 #endif
-   png_size_t info_rowbytes;  /* Added in 1.5.4: cache of updated row bytes */
+   size_t info_rowbytes;      /* Added in 1.5.4: cache of updated row bytes */
 
    png_uint_32 idat_size;     /* current IDAT size for read */
    png_uint_32 crc;           /* current chunk CRC value */
@@ -249,7 +249,7 @@
    png_byte filter;           /* file filter type (always 0) */
    png_byte interlaced;       /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
    png_byte pass;             /* current interlace pass (0 - 6) */
-   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ below ) */
+   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ in png.h ) */
    png_byte color_type;       /* color type of file */
    png_byte bit_depth;        /* bit depth of file */
    png_byte usr_bit_depth;    /* bit depth of users row: write only */
@@ -263,7 +263,7 @@
                               /* pixel depth used for the row buffers */
    png_byte transformed_pixel_depth;
                               /* pixel depth after read/write transforms */
-#if PNG_ZLIB_VERNUM >= 0x1240
+#if ZLIB_VERNUM >= 0x1240
    png_byte zstream_start;    /* at start of an input zlib stream */
 #endif /* Zlib >= 1.2.4 */
 #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
@@ -307,7 +307,7 @@
 #endif
 
 #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
-   png_color_8 shift;         /* shift for significant bit tranformation */
+   png_color_8 shift;         /* shift for significant bit transformation */
 #endif
 
 #if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
@@ -328,10 +328,10 @@
    png_bytep current_buffer;         /* buffer for recently used data */
    png_uint_32 push_length;          /* size of current input chunk */
    png_uint_32 skip_length;          /* bytes to skip in input data */
-   png_size_t save_buffer_size;      /* amount of data now in save_buffer */
-   png_size_t save_buffer_max;       /* total size of save_buffer */
-   png_size_t buffer_size;           /* total amount of available input data */
-   png_size_t current_buffer_size;   /* amount of data now in current_buffer */
+   size_t save_buffer_size;          /* amount of data now in save_buffer */
+   size_t save_buffer_max;           /* total size of save_buffer */
+   size_t buffer_size;               /* total amount of available input data */
+   size_t current_buffer_size;       /* amount of data now in current_buffer */
    int process_mode;                 /* what push library is currently doing */
    int cur_palette;                  /* current push library palette index */
 
@@ -353,7 +353,7 @@
 
 /* Options */
 #ifdef PNG_SET_OPTION_SUPPORTED
-   png_byte options;           /* On/off state (up to 4 options) */
+   png_uint_32 options;           /* On/off state (up to 16 options) */
 #endif
 
 #if PNG_LIBPNG_VER < 10700
@@ -392,6 +392,12 @@
    /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */
 #endif
 
+/* New member added in libpng-1.6.36 */
+#if defined(PNG_READ_EXPAND_SUPPORTED) && \
+    defined(PNG_ARM_NEON_IMPLEMENTATION)
+   png_bytep riffled_palette; /* buffer for accelerated palette expansion */
+#endif
+
 /* New member added in libpng-1.0.4 (renamed in 1.0.9) */
 #if defined(PNG_MNG_FEATURES_SUPPORTED)
 /* Changed from png_byte to png_uint_32 at version 1.2.0 */
@@ -451,7 +457,7 @@
 #endif
 
 /* New member added in libpng-1.2.26 */
-  png_size_t old_big_row_buf_size;
+   size_t old_big_row_buf_size;
 
 #ifdef PNG_READ_SUPPORTED
 /* New member added in libpng-1.2.30 */
diff --git a/third_party/libpng16/pngtrans.c b/third_party/libpng16/pngtrans.c
index 7f8cc45..1100f46 100644
--- a/third_party/libpng16/pngtrans.c
+++ b/third_party/libpng16/pngtrans.c
@@ -1,10 +1,10 @@
 
 /* pngtrans.c - transforms the data in a row (used by both readers and writers)
  *
- * Last changed in libpng 1.6.18 [July 23, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -172,13 +172,14 @@
                    * size!
                    */
                   png_app_error(png_ptr,
-                     "png_set_filler is invalid for low bit depth gray output");
+                      "png_set_filler is invalid for"
+                      " low bit depth gray output");
                   return;
                }
 
             default:
                png_app_error(png_ptr,
-                  "png_set_filler: inappropriate color type");
+                   "png_set_filler: inappropriate color type");
                return;
          }
 #     else
@@ -268,8 +269,8 @@
    if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
    {
       png_bytep rp = row;
-      png_size_t i;
-      png_size_t istop = row_info->rowbytes;
+      size_t i;
+      size_t istop = row_info->rowbytes;
 
       for (i = 0; i < istop; i++)
       {
@@ -282,8 +283,8 @@
       row_info->bit_depth == 8)
    {
       png_bytep rp = row;
-      png_size_t i;
-      png_size_t istop = row_info->rowbytes;
+      size_t i;
+      size_t istop = row_info->rowbytes;
 
       for (i = 0; i < istop; i += 2)
       {
@@ -297,8 +298,8 @@
       row_info->bit_depth == 16)
    {
       png_bytep rp = row;
-      png_size_t i;
-      png_size_t istop = row_info->rowbytes;
+      size_t i;
+      size_t istop = row_info->rowbytes;
 
       for (i = 0; i < istop; i += 4)
       {
@@ -344,7 +345,7 @@
 #endif
 
 #if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-static PNG_CONST png_byte onebppswaptable[256] = {
+static const png_byte onebppswaptable[256] = {
    0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
    0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
    0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
@@ -379,7 +380,7 @@
    0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
 };
 
-static PNG_CONST png_byte twobppswaptable[256] = {
+static const png_byte twobppswaptable[256] = {
    0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
    0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
    0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
@@ -414,7 +415,7 @@
    0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
 };
 
-static PNG_CONST png_byte fourbppswaptable[256] = {
+static const png_byte fourbppswaptable[256] = {
    0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
    0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
    0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
@@ -513,11 +514,15 @@
          if (at_start != 0) /* Skip initial filler */
             ++sp;
          else          /* Skip initial channel and, for sp, the filler */
-            sp += 2, ++dp;
+         {
+            sp += 2; ++dp;
+         }
 
          /* For a 1 pixel wide image there is nothing to do */
          while (sp < ep)
-            *dp++ = *sp, sp += 2;
+         {
+            *dp++ = *sp; sp += 2;
+         }
 
          row_info->pixel_depth = 8;
       }
@@ -527,10 +532,14 @@
          if (at_start != 0) /* Skip initial filler */
             sp += 2;
          else          /* Skip initial channel and, for sp, the filler */
-            sp += 4, dp += 2;
+         {
+            sp += 4; dp += 2;
+         }
 
          while (sp < ep)
-            *dp++ = *sp++, *dp++ = *sp, sp += 3;
+         {
+            *dp++ = *sp++; *dp++ = *sp; sp += 3;
+         }
 
          row_info->pixel_depth = 16;
       }
@@ -553,11 +562,15 @@
          if (at_start != 0) /* Skip initial filler */
             ++sp;
          else          /* Skip initial channels and, for sp, the filler */
-            sp += 4, dp += 3;
+         {
+            sp += 4; dp += 3;
+         }
 
          /* Note that the loop adds 3 to dp and 4 to sp each time. */
          while (sp < ep)
-            *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2;
+         {
+            *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp; sp += 2;
+         }
 
          row_info->pixel_depth = 24;
       }
@@ -567,14 +580,16 @@
          if (at_start != 0) /* Skip initial filler */
             sp += 2;
          else          /* Skip initial channels and, for sp, the filler */
-            sp += 8, dp += 6;
+         {
+            sp += 8; dp += 6;
+         }
 
          while (sp < ep)
          {
             /* Copy 6 bytes, skip 2 */
-            *dp++ = *sp++, *dp++ = *sp++;
-            *dp++ = *sp++, *dp++ = *sp++;
-            *dp++ = *sp++, *dp++ = *sp, sp += 3;
+            *dp++ = *sp++; *dp++ = *sp++;
+            *dp++ = *sp++; *dp++ = *sp++;
+            *dp++ = *sp++; *dp++ = *sp; sp += 3;
          }
 
          row_info->pixel_depth = 48;
@@ -594,7 +609,7 @@
       return; /* The filler channel has gone already */
 
    /* Fix the rowbytes value. */
-   row_info->rowbytes = dp-row;
+   row_info->rowbytes = (size_t)(dp-row);
 }
 #endif
 
@@ -692,8 +707,8 @@
        * and this calculation is used because it avoids warnings that other
        * forms produced on either GCC or MSVC.
        */
-      int padding = (-row_info->pixel_depth * row_info->width) & 7;
-      png_bytep rp = png_ptr->row_buf + row_info->rowbytes;
+      int padding = PNG_PADBITS(row_info->pixel_depth, row_info->width);
+      png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1;
 
       switch (row_info->bit_depth)
       {
@@ -797,7 +812,7 @@
       (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
    {
       png_app_error(png_ptr,
-            "info change after png_start_read_image or png_read_update_info");
+          "info change after png_start_read_image or png_read_update_info");
       return;
    }
 #endif
diff --git a/third_party/libpng16/pngwio.c b/third_party/libpng16/pngwio.c
index 586c03b..10e919d 100644
--- a/third_party/libpng16/pngwio.c
+++ b/third_party/libpng16/pngwio.c
@@ -1,10 +1,10 @@
 
 /* pngwio.c - functions for data output
  *
- * Last changed in libpng 1.6.15 [November 20, 2014]
- * Copyright (c) 1998-2002,2004,2006-2014 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2014,2016,2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -30,12 +30,12 @@
  */
 
 void /* PRIVATE */
-png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length)
+png_write_data(png_structrp png_ptr, png_const_bytep data, size_t length)
 {
    /* NOTE: write_data_fn must not change the buffer! */
    if (png_ptr->write_data_fn != NULL )
       (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data),
-         length);
+          length);
 
    else
       png_error(png_ptr, "Call to NULL write function");
@@ -48,9 +48,9 @@
  * than changing the library.
  */
 void PNGCBAPI
-png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+png_default_write_data(png_structp png_ptr, png_bytep data, size_t length)
 {
-   png_size_t check;
+   size_t check;
 
    if (png_ptr == NULL)
       return;
diff --git a/third_party/libpng16/pngwrite.c b/third_party/libpng16/pngwrite.c
index 181a899..59377a4 100644
--- a/third_party/libpng16/pngwrite.c
+++ b/third_party/libpng16/pngwrite.c
@@ -1,10 +1,10 @@
 
 /* pngwrite.c - general routines to write a PNG file
  *
- * Last changed in libpng 1.6.19 [November 12, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018-2019 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -22,7 +22,7 @@
 /* Write out all the unknown chunks for the current given location */
 static void
 write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
-   unsigned int where)
+    unsigned int where)
 {
    if (info_ptr->unknown_chunks_num != 0)
    {
@@ -148,11 +148,11 @@
 #    ifdef PNG_WRITE_sRGB_SUPPORTED
                if ((info_ptr->valid & PNG_INFO_sRGB) != 0)
                   png_app_warning(png_ptr,
-                     "profile matches sRGB but writing iCCP instead");
+                      "profile matches sRGB but writing iCCP instead");
 #     endif
 
             png_write_iCCP(png_ptr, info_ptr->iccp_name,
-               info_ptr->iccp_profile);
+                info_ptr->iccp_profile);
          }
 #     ifdef PNG_WRITE_sRGB_SUPPORTED
          else
@@ -237,6 +237,11 @@
       png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
 #endif
 
+#ifdef PNG_WRITE_eXIf_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_eXIf) != 0)
+      png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif);
+#endif
+
 #ifdef PNG_WRITE_hIST_SUPPORTED
    if ((info_ptr->valid & PNG_INFO_hIST) != 0)
       png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
@@ -383,7 +388,7 @@
       for (i = 0; i < info_ptr->num_text; i++)
       {
          png_debug2(2, "Writing trailer text chunk %d, type %d", i,
-            info_ptr->text[i].compression);
+             info_ptr->text[i].compression);
          /* An internationalized chunk? */
          if (info_ptr->text[i].compression > 0)
          {
@@ -432,6 +437,12 @@
          }
       }
 #endif
+
+#ifdef PNG_WRITE_eXIf_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_eXIf) != 0)
+      png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif);
+#endif
+
 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
       write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);
 #endif
@@ -458,7 +469,7 @@
 
 #ifdef PNG_CONVERT_tIME_SUPPORTED
 void PNGAPI
-png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)
+png_convert_from_struct_tm(png_timep ptime, const struct tm * ttime)
 {
    png_debug(1, "in png_convert_from_struct_tm");
 
@@ -666,9 +677,9 @@
 
          for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
          {
-            png_uint_32 s0   = (*(rp    ) << 8) | *(rp + 1);
-            png_uint_32 s1   = (*(rp + 2) << 8) | *(rp + 3);
-            png_uint_32 s2   = (*(rp + 4) << 8) | *(rp + 5);
+            png_uint_32 s0   = (png_uint_32)(*(rp    ) << 8) | *(rp + 1);
+            png_uint_32 s1   = (png_uint_32)(*(rp + 2) << 8) | *(rp + 3);
+            png_uint_32 s2   = (png_uint_32)(*(rp + 4) << 8) | *(rp + 5);
             png_uint_32 red  = (png_uint_32)((s0 - s1) & 0xffffL);
             png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL);
             *(rp    ) = (png_byte)(red >> 8);
@@ -693,7 +704,7 @@
       return;
 
    png_debug2(1, "in png_write_row (row %u, pass %d)",
-      png_ptr->row_number, png_ptr->pass);
+       png_ptr->row_number, png_ptr->pass);
 
    /* Initialize transformations and other stuff if first time */
    if (png_ptr->row_number == 0 && png_ptr->pass == 0)
@@ -901,7 +912,7 @@
    if (png_ptr == NULL)
       return;
 
-   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
+   png_ptr->flush_dist = (nrows < 0 ? 0 : (png_uint_32)nrows);
 }
 
 /* Flush the current output buffers now */
@@ -1007,8 +1018,8 @@
          case 5:
          case 6:
          case 7: png_app_error(png_ptr, "Unknown row filter for method 0");
-            /* FALL THROUGH */
 #endif /* WRITE_FILTER */
+            /* FALLTHROUGH */
          case PNG_FILTER_VALUE_NONE:
             png_ptr->do_filter = PNG_FILTER_NONE; break;
 
@@ -1069,7 +1080,7 @@
              * is not available so the filter can't be used.  Just warn here.
              */
             png_app_warning(png_ptr,
-               "png_set_filter: UP/AVG/PAETH cannot be added after start");
+                "png_set_filter: UP/AVG/PAETH cannot be added after start");
             filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
          }
 
@@ -1095,13 +1106,13 @@
 
          if (png_ptr->try_row == NULL)
             png_ptr->try_row = png_voidcast(png_bytep,
-               png_malloc(png_ptr, buf_size));
+                png_malloc(png_ptr, buf_size));
 
          if (num_filters > 1)
          {
             if (png_ptr->tst_row == NULL)
                png_ptr->tst_row = png_voidcast(png_bytep,
-                  png_malloc(png_ptr, buf_size));
+                   png_malloc(png_ptr, buf_size));
          }
       }
       png_ptr->do_filter = (png_byte)filters;
@@ -1525,7 +1536,8 @@
        display->first_row);
    png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row);
    png_uint_16p row_end;
-   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
+   unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ?
+       3 : 1;
    int aindex = 0;
    png_uint_32 y = image->height;
 
@@ -1539,9 +1551,9 @@
          ++output_row;
       }
          else
-            aindex = channels;
+            aindex = (int)channels;
 #     else
-         aindex = channels;
+         aindex = (int)channels;
 #     endif
    }
 
@@ -1554,14 +1566,14 @@
     */
    row_end = output_row + image->width * (channels+1);
 
-   while (y-- > 0)
+   for (; y > 0; --y)
    {
       png_const_uint_16p in_ptr = input_row;
       png_uint_16p out_ptr = output_row;
 
       while (out_ptr < row_end)
       {
-         const png_uint_16 alpha = in_ptr[aindex];
+         png_uint_16 alpha = in_ptr[aindex];
          png_uint_32 reciprocal = 0;
          int c;
 
@@ -1575,7 +1587,7 @@
          if (alpha > 0 && alpha < 65535)
             reciprocal = ((0xffff<<15)+(alpha>>1))/alpha;
 
-         c = channels;
+         c = (int)channels;
          do /* always at least one channel */
          {
             png_uint_16 component = *in_ptr++;
@@ -1610,7 +1622,7 @@
       }
 
       png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
-      input_row += display->row_bytes/(sizeof (png_uint_16));
+      input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16));
    }
 
    return 1;
@@ -1624,11 +1636,11 @@
  * calculation can be done to 15 bits of accuracy; however, the output needs to
  * be scaled in the range 0..255*65535, so include that scaling here.
  */
-#   define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha)
+#   define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+((alpha)>>1))/(alpha))
 
 static png_byte
 png_unpremultiply(png_uint_32 component, png_uint_32 alpha,
-   png_uint_32 reciprocal/*from the above macro*/)
+    png_uint_32 reciprocal/*from the above macro*/)
 {
    /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0
     * is represented as some other value there is more likely to be a
@@ -1683,7 +1695,8 @@
        display->first_row);
    png_bytep output_row = png_voidcast(png_bytep, display->local_row);
    png_uint_32 y = image->height;
-   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
+   unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ?
+       3 : 1;
 
    if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
    {
@@ -1700,12 +1713,12 @@
 
       else
 #   endif
-      aindex = channels;
+      aindex = (int)channels;
 
       /* Use row_end in place of a loop counter: */
       row_end = output_row + image->width * (channels+1);
 
-      while (y-- > 0)
+      for (; y > 0; --y)
       {
          png_const_uint_16p in_ptr = input_row;
          png_bytep out_ptr = output_row;
@@ -1723,7 +1736,7 @@
             if (alphabyte > 0 && alphabyte < 255)
                reciprocal = UNP_RECIPROCAL(alpha);
 
-            c = channels;
+            c = (int)channels;
             do /* always at least one channel */
                *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
             while (--c > 0);
@@ -1735,7 +1748,7 @@
 
          png_write_row(png_ptr, png_voidcast(png_const_bytep,
              display->local_row));
-         input_row += display->row_bytes/(sizeof (png_uint_16));
+         input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16));
       } /* while y */
    }
 
@@ -1746,7 +1759,7 @@
        */
       png_bytep row_end = output_row + image->width * channels;
 
-      while (y-- > 0)
+      for (; y > 0; --y)
       {
          png_const_uint_16p in_ptr = input_row;
          png_bytep out_ptr = output_row;
@@ -1760,7 +1773,7 @@
          }
 
          png_write_row(png_ptr, output_row);
-         input_row += display->row_bytes/(sizeof (png_uint_16));
+         input_row += (png_uint_16)display->row_bytes/(sizeof (png_uint_16));
       }
    }
 
@@ -1770,25 +1783,25 @@
 static void
 png_image_set_PLTE(png_image_write_control *display)
 {
-   const png_imagep image = display->image;
+   png_imagep image = display->image;
    const void *cmap = display->colormap;
-   const int entries = image->colormap_entries > 256 ? 256 :
+   int entries = image->colormap_entries > 256 ? 256 :
        (int)image->colormap_entries;
 
    /* NOTE: the caller must check for cmap != NULL and entries != 0 */
-   const png_uint_32 format = image->format;
-   const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format);
+   png_uint_32 format = image->format;
+   unsigned int channels = PNG_IMAGE_SAMPLE_CHANNELS(format);
 
 #   if defined(PNG_FORMAT_BGR_SUPPORTED) &&\
       defined(PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED)
-      const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
+      int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
           (format & PNG_FORMAT_FLAG_ALPHA) != 0;
 #   else
 #     define afirst 0
 #   endif
 
 #   ifdef PNG_FORMAT_BGR_SUPPORTED
-      const int bgr = (format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
+      int bgr = (format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
 #   else
 #     define bgr 0
 #   endif
@@ -1809,7 +1822,7 @@
       {
          png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);
 
-         entry += i * channels;
+         entry += (unsigned int)i * channels;
 
          if ((channels & 1) != 0) /* no alpha */
          {
@@ -1848,16 +1861,16 @@
             if (channels >= 3) /* RGB */
             {
                palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)],
-                  alpha, reciprocal);
+                   alpha, reciprocal);
                palette[i].green = png_unpremultiply(entry[afirst + 1], alpha,
-                  reciprocal);
+                   reciprocal);
                palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha,
-                  reciprocal);
+                   reciprocal);
             }
 
             else /* gray */
                palette[i].blue = palette[i].red = palette[i].green =
-                  png_unpremultiply(entry[afirst], alpha, reciprocal);
+                   png_unpremultiply(entry[afirst], alpha, reciprocal);
          }
       }
 
@@ -1865,7 +1878,7 @@
       {
          png_const_bytep entry = png_voidcast(png_const_bytep, cmap);
 
-         entry += i * channels;
+         entry += (unsigned int)i * channels;
 
          switch (channels)
          {
@@ -1873,7 +1886,7 @@
                tRNS[i] = entry[afirst ? 0 : 3];
                if (tRNS[i] < 255)
                   num_trans = i+1;
-               /* FALL THROUGH */
+               /* FALLTHROUGH */
             case 3:
                palette[i].blue = entry[afirst + (2 ^ bgr)];
                palette[i].green = entry[afirst + 1];
@@ -1884,7 +1897,7 @@
                tRNS[i] = entry[1 ^ afirst];
                if (tRNS[i] < 255)
                   num_trans = i+1;
-               /* FALL THROUGH */
+               /* FALLTHROUGH */
             case 1:
                palette[i].blue = palette[i].red = palette[i].green =
                   entry[afirst];
@@ -1904,20 +1917,20 @@
 #   endif
 
    png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette,
-      entries);
+       entries);
 
    if (num_trans > 0)
       png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS,
-         num_trans, NULL);
+          num_trans, NULL);
 
-   image->colormap_entries = entries;
+   image->colormap_entries = (png_uint_32)entries;
 }
 
 static int
 png_image_write_main(png_voidp argument)
 {
    png_image_write_control *display = png_voidcast(png_image_write_control*,
-      argument);
+       argument);
    png_imagep image = display->image;
    png_structrp png_ptr = image->opaque->png_ptr;
    png_inforp info_ptr = image->opaque->info_ptr;
@@ -1927,7 +1940,7 @@
    int colormap = (format & PNG_FORMAT_FLAG_COLORMAP);
    int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */
    int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA);
-   int write_16bit = linear && !colormap && (display->convert_to_8bit == 0);
+   int write_16bit = linear && (display->convert_to_8bit == 0);
 
 #   ifdef PNG_BENIGN_ERRORS_SUPPORTED
       /* Make sure we error out on any bad situation */
@@ -1938,21 +1951,21 @@
     * and total image size to ensure that they are within the system limits.
     */
    {
-      const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
+      unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
 
-      if (image->width <= 0x7FFFFFFFU/channels) /* no overflow */
+      if (image->width <= 0x7fffffffU/channels) /* no overflow */
       {
          png_uint_32 check;
-         const png_uint_32 png_row_stride = image->width * channels;
+         png_uint_32 png_row_stride = image->width * channels;
 
          if (display->row_stride == 0)
             display->row_stride = (png_int_32)/*SAFE*/png_row_stride;
 
          if (display->row_stride < 0)
-            check = -display->row_stride;
+            check = (png_uint_32)(-display->row_stride);
 
          else
-            check = display->row_stride;
+            check = (png_uint_32)display->row_stride;
 
          if (check >= png_row_stride)
          {
@@ -1960,7 +1973,7 @@
              * limits the whole image size to 32 bits for API compatibility with
              * the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro.
              */
-            if (image->height > 0xFFFFFFFF/png_row_stride)
+            if (image->height > 0xffffffffU/png_row_stride)
                png_error(image->opaque->png_ptr, "memory image too large");
          }
 
@@ -1980,24 +1993,24 @@
          png_uint_32 entries = image->colormap_entries;
 
          png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
-            entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),
-            PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
-            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+             entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),
+             PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
+             PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
 
          png_image_set_PLTE(display);
       }
 
       else
          png_error(image->opaque->png_ptr,
-            "no color-map for color-mapped image");
+             "no color-map for color-mapped image");
    }
 
    else
       png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
-         write_16bit ? 16 : 8,
-         ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +
-         ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),
-         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+          write_16bit ? 16 : 8,
+          ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +
+          ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),
+          PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
 
    /* Counter-intuitively the data transformations must be called *after*
     * png_write_info, not before as in the read code, but the 'set' functions
@@ -2012,11 +2025,11 @@
 
       if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
          png_set_cHRM_fixed(png_ptr, info_ptr,
-            /* color      x       y */
-            /* white */ 31270, 32900,
-            /* red   */ 64000, 33000,
-            /* green */ 30000, 60000,
-            /* blue  */ 15000,  6000
+             /* color      x       y */
+             /* white */ 31270, 32900,
+             /* red   */ 64000, 33000,
+             /* green */ 30000, 60000,
+             /* blue  */ 15000,  6000
          );
    }
 
@@ -2039,7 +2052,7 @@
     */
    if (write_16bit != 0)
    {
-      PNG_CONST png_uint_16 le = 0x0001;
+      png_uint_16 le = 0x0001;
 
       if ((*(png_const_bytep) & le) != 0)
          png_set_swap(png_ptr);
@@ -2110,7 +2123,7 @@
        (colormap == 0 && display->convert_to_8bit != 0))
    {
       png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
-         png_get_rowbytes(png_ptr, info_ptr)));
+          png_get_rowbytes(png_ptr, info_ptr)));
       int result;
 
       display->local_row = row;
@@ -2136,7 +2149,7 @@
       ptrdiff_t row_bytes = display->row_bytes;
       png_uint_32 y = image->height;
 
-      while (y-- > 0)
+      for (; y > 0; --y)
       {
          png_write_row(png_ptr, row);
          row += row_bytes;
@@ -2149,12 +2162,11 @@
 
 
 static void (PNGCBAPI
-image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data,
-   png_size_t size)
+image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data, size_t size)
 {
    png_image_write_control *display = png_voidcast(png_image_write_control*,
-      png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/);
-   const png_alloc_size_t ob = display->output_bytes;
+       png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/);
+   png_alloc_size_t ob = display->output_bytes;
 
    /* Check for overflow; this should never happen: */
    if (size <= ((png_alloc_size_t)-1) - ob)
@@ -2184,22 +2196,22 @@
 png_image_write_memory(png_voidp argument)
 {
    png_image_write_control *display = png_voidcast(png_image_write_control*,
-      argument);
+       argument);
 
    /* The rest of the memory-specific init and write_main in an error protected
     * environment.  This case needs to use callbacks for the write operations
     * since libpng has no built in support for writing to memory.
     */
    png_set_write_fn(display->image->opaque->png_ptr, display/*io_ptr*/,
-         image_memory_write, image_memory_flush);
+       image_memory_write, image_memory_flush);
 
    return png_image_write_main(display);
 }
 
 int PNGAPI
 png_image_write_to_memory(png_imagep image, void *memory,
-   png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit,
-   const void *buffer, png_int_32 row_stride, const void *colormap)
+    png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit,
+    const void *buffer, png_int_32 row_stride, const void *colormap)
 {
    /* Write the image to the given buffer, or count the bytes if it is NULL */
    if (image != NULL && image->version == PNG_IMAGE_VERSION)
@@ -2251,12 +2263,12 @@
 
       else
          return png_image_error(image,
-            "png_image_write_to_memory: invalid argument");
+             "png_image_write_to_memory: invalid argument");
    }
 
    else if (image != NULL)
       return png_image_error(image,
-         "png_image_write_to_memory: incorrect PNG_IMAGE_VERSION");
+          "png_image_write_to_memory: incorrect PNG_IMAGE_VERSION");
 
    else
       return 0;
@@ -2265,7 +2277,7 @@
 #ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
 int PNGAPI
 png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
-   const void *buffer, png_int_32 row_stride, const void *colormap)
+    const void *buffer, png_int_32 row_stride, const void *colormap)
 {
    /* Write the image to the given (FILE*). */
    if (image != NULL && image->version == PNG_IMAGE_VERSION)
@@ -2301,12 +2313,12 @@
 
       else
          return png_image_error(image,
-            "png_image_write_to_stdio: invalid argument");
+             "png_image_write_to_stdio: invalid argument");
    }
 
    else if (image != NULL)
       return png_image_error(image,
-         "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");
+          "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");
 
    else
       return 0;
@@ -2314,8 +2326,8 @@
 
 int PNGAPI
 png_image_write_to_file(png_imagep image, const char *file_name,
-   int convert_to_8bit, const void *buffer, png_int_32 row_stride,
-   const void *colormap)
+    int convert_to_8bit, const void *buffer, png_int_32 row_stride,
+    const void *colormap)
 {
    /* Write the image to the named file. */
    if (image != NULL && image->version == PNG_IMAGE_VERSION)
@@ -2327,7 +2339,7 @@
          if (fp != NULL)
          {
             if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
-               row_stride, colormap) != 0)
+                row_stride, colormap) != 0)
             {
                int error; /* from fflush/fclose */
 
@@ -2368,12 +2380,12 @@
 
       else
          return png_image_error(image,
-            "png_image_write_to_file: invalid argument");
+             "png_image_write_to_file: invalid argument");
    }
 
    else if (image != NULL)
       return png_image_error(image,
-         "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");
+          "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");
 
    else
       return 0;
diff --git a/third_party/libpng16/pngwtran.c b/third_party/libpng16/pngwtran.c
index 038a2ef..49a13c1 100644
--- a/third_party/libpng16/pngwtran.c
+++ b/third_party/libpng16/pngwtran.c
@@ -1,10 +1,10 @@
 
 /* pngwtran.c - transforms the data in a row for PNG writers
  *
- * Last changed in libpng 1.6.18 [July 23, 2015]
- * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -177,7 +177,7 @@
    if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)
    {
       int shift_start[4], shift_dec[4];
-      int channels = 0;
+      unsigned int channels = 0;
 
       if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
       {
@@ -212,9 +212,9 @@
       if (row_info->bit_depth < 8)
       {
          png_bytep bp = row;
-         png_size_t i;
+         size_t i;
          unsigned int mask;
-         png_size_t row_bytes = row_info->rowbytes;
+         size_t row_bytes = row_info->rowbytes;
 
          if (bit_depth->gray == 1 && row_info->bit_depth == 2)
             mask = 0x55;
@@ -254,8 +254,7 @@
 
          for (i = 0; i < istop; i++, bp++)
          {
-
-            const unsigned int c = i%channels;
+            unsigned int c = i%channels;
             int j;
             unsigned int v, out;
 
@@ -283,7 +282,7 @@
 
          for (bp = row, i = 0; i < istop; i++)
          {
-            const unsigned int c = i%channels;
+            unsigned int c = i%channels;
             int j;
             unsigned int value, v;
 
@@ -514,7 +513,7 @@
              (png_ptr,  /* png_ptr */
              row_info,  /* row_info: */
                 /*  png_uint_32 width;       width of row */
-                /*  png_size_t rowbytes;     number of bytes in row */
+                /*  size_t rowbytes;         number of bytes in row */
                 /*  png_byte color_type;     color type of pixels */
                 /*  png_byte bit_depth;      bit depth of samples */
                 /*  png_byte channels;       number of channels (1-4) */
@@ -525,7 +524,7 @@
 #ifdef PNG_WRITE_FILLER_SUPPORTED
    if ((png_ptr->transformations & PNG_FILLER) != 0)
       png_do_strip_channel(row_info, png_ptr->row_buf + 1,
-         !(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
+          !(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
 #endif
 
 #ifdef PNG_WRITE_PACKSWAP_SUPPORTED
@@ -549,7 +548,7 @@
 #ifdef PNG_WRITE_SHIFT_SUPPORTED
    if ((png_ptr->transformations & PNG_SHIFT) != 0)
       png_do_shift(row_info, png_ptr->row_buf + 1,
-          &(png_ptr->shift));
+           &(png_ptr->shift));
 #endif
 
 #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
diff --git a/third_party/libpng16/pngwutil.c b/third_party/libpng16/pngwutil.c
index ca8a03a..16345e4 100644
--- a/third_party/libpng16/pngwutil.c
+++ b/third_party/libpng16/pngwutil.c
@@ -1,10 +1,10 @@
 
 /* pngwutil.c - utilities to write a PNG file
  *
- * Last changed in libpng 1.6.22 [May 26, 2016]
- * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
- * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
- * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
+ * Copyright (c) 1996-1997 Andreas Dilger
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -59,7 +59,7 @@
 
    /* Write the rest of the 8 byte signature */
    png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
-      (png_size_t)(8 - png_ptr->sig_bytes));
+       (size_t)(8 - png_ptr->sig_bytes));
 
    if (png_ptr->sig_bytes < 3)
       png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
@@ -124,8 +124,7 @@
  * given to png_write_chunk_header().
  */
 void PNGAPI
-png_write_chunk_data(png_structrp png_ptr, png_const_bytep data,
-    png_size_t length)
+png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, size_t length)
 {
    /* Write the data, and run the CRC over it */
    if (png_ptr == NULL)
@@ -160,7 +159,7 @@
    /* Write the crc in a single operation */
    png_save_uint_32(buf, png_ptr->crc);
 
-   png_write_data(png_ptr, buf, (png_size_t)4);
+   png_write_data(png_ptr, buf, 4);
 }
 
 /* Write a PNG chunk all at once.  The type is an array of ASCII characters
@@ -174,7 +173,7 @@
  */
 static void
 png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name,
-   png_const_bytep data, png_size_t length)
+    png_const_bytep data, size_t length)
 {
    if (png_ptr == NULL)
       return;
@@ -191,10 +190,10 @@
 /* This is the API that calls the internal function above. */
 void PNGAPI
 png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string,
-   png_const_bytep data, png_size_t length)
+    png_const_bytep data, size_t length)
 {
    png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data,
-      length);
+       length);
 }
 
 /* This is used below to find the size of an image to pass to png_deflate_claim,
@@ -291,7 +290,7 @@
 /* Initialize the compressor for the appropriate type of compression. */
 static int
 png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
-   png_alloc_size_t data_size)
+    png_alloc_size_t data_size)
 {
    if (png_ptr->zowner != 0)
    {
@@ -408,7 +407,7 @@
       png_ptr->zstream.avail_out = 0;
 
       /* Now initialize if required, setting the new parameters, otherwise just
-       * to a simple reset to the previous parameters.
+       * do a simple reset to the previous parameters.
        */
       if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
          ret = deflateReset(&png_ptr->zstream);
@@ -416,7 +415,7 @@
       else
       {
          ret = deflateInit2(&png_ptr->zstream, level, method, windowBits,
-            memLevel, strategy);
+             memLevel, strategy);
 
          if (ret == Z_OK)
             png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
@@ -477,7 +476,7 @@
 
 static void
 png_text_compress_init(compression_state *comp, png_const_bytep input,
-   png_alloc_size_t input_len)
+    png_alloc_size_t input_len)
 {
    comp->input = input;
    comp->input_len = input_len;
@@ -487,7 +486,7 @@
 /* Compress the data in the compression state input */
 static int
 png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
-   compression_state *comp, png_uint_32 prefix_len)
+    compression_state *comp, png_uint_32 prefix_len)
 {
    int ret;
 
@@ -579,7 +578,7 @@
 
          /* Compress the data */
          ret = deflate(&png_ptr->zstream,
-            input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
+             input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
 
          /* Claw back input data that was not consumed (because avail_in is
           * reset above every time round the loop).
@@ -675,6 +674,7 @@
     int interlace_type)
 {
    png_byte buf[13]; /* Buffer to store the IHDR info */
+   int is_invalid_depth;
 
    png_debug(1, "in png_write_IHDR");
 
@@ -700,11 +700,11 @@
          break;
 
       case PNG_COLOR_TYPE_RGB:
+         is_invalid_depth = (bit_depth != 8);
 #ifdef PNG_WRITE_16BIT_SUPPORTED
-         if (bit_depth != 8 && bit_depth != 16)
-#else
-         if (bit_depth != 8)
+         is_invalid_depth = (is_invalid_depth && bit_depth != 16);
 #endif
+         if (is_invalid_depth)
             png_error(png_ptr, "Invalid bit depth for RGB image");
 
          png_ptr->channels = 3;
@@ -726,18 +726,22 @@
          break;
 
       case PNG_COLOR_TYPE_GRAY_ALPHA:
-         if (bit_depth != 8 && bit_depth != 16)
+         is_invalid_depth = (bit_depth != 8);
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+         is_invalid_depth = (is_invalid_depth && bit_depth != 16);
+#endif
+         if (is_invalid_depth)
             png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
 
          png_ptr->channels = 2;
          break;
 
       case PNG_COLOR_TYPE_RGB_ALPHA:
+         is_invalid_depth = (bit_depth != 8);
 #ifdef PNG_WRITE_16BIT_SUPPORTED
-         if (bit_depth != 8 && bit_depth != 16)
-#else
-         if (bit_depth != 8)
+         is_invalid_depth = (is_invalid_depth && bit_depth != 16);
 #endif
+         if (is_invalid_depth)
             png_error(png_ptr, "Invalid bit depth for RGBA image");
 
          png_ptr->channels = 4;
@@ -815,7 +819,7 @@
    buf[12] = (png_byte)interlace_type;
 
    /* Write the chunk */
-   png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);
+   png_write_complete_chunk(png_ptr, png_IHDR, buf, 13);
 
    if ((png_ptr->do_filter) == PNG_NO_FILTERS)
    {
@@ -884,7 +888,7 @@
       buf[0] = pal_ptr->red;
       buf[1] = pal_ptr->green;
       buf[2] = pal_ptr->blue;
-      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+      png_write_chunk_data(png_ptr, buf, 3);
    }
 
 #else
@@ -898,7 +902,7 @@
       buf[0] = pal_ptr[i].red;
       buf[1] = pal_ptr[i].green;
       buf[2] = pal_ptr[i].blue;
-      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+      png_write_chunk_data(png_ptr, buf, 3);
    }
 
 #endif
@@ -925,7 +929,7 @@
  */
 void /* PRIVATE */
 png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
-   png_alloc_size_t input_len, int flush)
+    png_alloc_size_t input_len, int flush)
 {
    if (png_ptr->zowner != png_IDAT)
    {
@@ -937,7 +941,7 @@
       if (png_ptr->zbuffer_list == NULL)
       {
          png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
-            png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
+             png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
          png_ptr->zbuffer_list->next = NULL;
       }
 
@@ -998,7 +1002,8 @@
                optimize_cmf(data, png_image_size(png_ptr));
 #endif
 
-         png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+         if (size > 0)
+            png_write_complete_chunk(png_ptr, png_IDAT, data, size);
          png_ptr->mode |= PNG_HAVE_IDAT;
 
          png_ptr->zstream.next_out = data;
@@ -1044,7 +1049,8 @@
             optimize_cmf(data, png_image_size(png_ptr));
 #endif
 
-         png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+         if (size > 0)
+            png_write_complete_chunk(png_ptr, png_IDAT, data, size);
          png_ptr->zstream.avail_out = 0;
          png_ptr->zstream.next_out = NULL;
          png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
@@ -1068,7 +1074,7 @@
 {
    png_debug(1, "in png_write_IEND");
 
-   png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0);
+   png_write_complete_chunk(png_ptr, png_IEND, NULL, 0);
    png_ptr->mode |= PNG_HAVE_IEND;
 }
 
@@ -1083,7 +1089,7 @@
 
    /* file_gamma is saved in 1/100,000ths */
    png_save_uint_32(buf, (png_uint_32)file_gamma);
-   png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);
+   png_write_complete_chunk(png_ptr, png_gAMA, buf, 4);
 }
 #endif
 
@@ -1101,7 +1107,7 @@
           "Invalid sRGB rendering intent specified");
 
    buf[0]=(png_byte)srgb_intent;
-   png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1);
+   png_write_complete_chunk(png_ptr, png_sRGB, buf, 1);
 }
 #endif
 
@@ -1175,8 +1181,8 @@
    png_uint_32 name_len;
    png_byte new_name[80];
    png_byte entrybuf[10];
-   png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
-   png_size_t palette_size = entry_size * spalette->nentries;
+   size_t entry_size = (spalette->depth == 8 ? 6 : 10);
+   size_t palette_size = entry_size * (size_t)spalette->nentries;
    png_sPLT_entryp ep;
 #ifndef PNG_POINTER_INDEXING_SUPPORTED
    int i;
@@ -1193,10 +1199,9 @@
    png_write_chunk_header(png_ptr, png_sPLT,
        (png_uint_32)(name_len + 2 + palette_size));
 
-   png_write_chunk_data(png_ptr, (png_bytep)new_name,
-       (png_size_t)(name_len + 1));
+   png_write_chunk_data(png_ptr, (png_bytep)new_name, (size_t)(name_len + 1));
 
-   png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1);
+   png_write_chunk_data(png_ptr, &spalette->depth, 1);
 
    /* Loop through each palette entry, writing appropriately */
 #ifdef PNG_POINTER_INDEXING_SUPPORTED
@@ -1258,7 +1263,7 @@
 png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)
 {
    png_byte buf[4];
-   png_size_t size;
+   size_t size;
 
    png_debug(1, "in png_write_sBIT");
 
@@ -1358,7 +1363,7 @@
 
       /* Write the chunk out as it is */
       png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha,
-         (png_size_t)num_trans);
+          (size_t)num_trans);
    }
 
    else if (color_type == PNG_COLOR_TYPE_GRAY)
@@ -1373,7 +1378,7 @@
       }
 
       png_save_uint_16(buf, tran->gray);
-      png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2);
+      png_write_complete_chunk(png_ptr, png_tRNS, buf, 2);
    }
 
    else if (color_type == PNG_COLOR_TYPE_RGB)
@@ -1389,11 +1394,11 @@
 #endif
       {
          png_app_warning(png_ptr,
-           "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
+             "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
          return;
       }
 
-      png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6);
+      png_write_complete_chunk(png_ptr, png_tRNS, buf, 6);
    }
 
    else
@@ -1426,7 +1431,7 @@
       }
 
       buf[0] = back->index;
-      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);
+      png_write_complete_chunk(png_ptr, png_bKGD, buf, 1);
    }
 
    else if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
@@ -1441,12 +1446,13 @@
 #endif
       {
          png_warning(png_ptr,
-             "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
+             "Ignoring attempt to write 16-bit bKGD chunk "
+             "when bit_depth is 8");
 
          return;
       }
 
-      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6);
+      png_write_complete_chunk(png_ptr, png_bKGD, buf, 6);
    }
 
    else
@@ -1460,11 +1466,33 @@
       }
 
       png_save_uint_16(buf, back->gray);
-      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2);
+      png_write_complete_chunk(png_ptr, png_bKGD, buf, 2);
    }
 }
 #endif
 
+#ifdef PNG_WRITE_eXIf_SUPPORTED
+/* Write the Exif data */
+void /* PRIVATE */
+png_write_eXIf(png_structrp png_ptr, png_bytep exif, int num_exif)
+{
+   int i;
+   png_byte buf[1];
+
+   png_debug(1, "in png_write_eXIf");
+
+   png_write_chunk_header(png_ptr, png_eXIf, (png_uint_32)(num_exif));
+
+   for (i = 0; i < num_exif; i++)
+   {
+      buf[0] = exif[i];
+      png_write_chunk_data(png_ptr, buf, 1);
+   }
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
 #ifdef PNG_WRITE_hIST_SUPPORTED
 /* Write the histogram */
 void /* PRIVATE */
@@ -1489,7 +1517,7 @@
    for (i = 0; i < num_hist; i++)
    {
       png_save_uint_16(buf, hist[i]);
-      png_write_chunk_data(png_ptr, buf, (png_size_t)2);
+      png_write_chunk_data(png_ptr, buf, 2);
    }
 
    png_write_chunk_end(png_ptr);
@@ -1500,7 +1528,7 @@
 /* Write a tEXt chunk */
 void /* PRIVATE */
 png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
-    png_size_t text_len)
+    size_t text_len)
 {
    png_uint_32 key_len;
    png_byte new_key[80];
@@ -1571,7 +1599,7 @@
 
    /* Compute the compressed data; do it now for the length */
    png_text_compress_init(&comp, (png_const_bytep)text,
-      text == NULL ? 0 : strlen(text));
+       text == NULL ? 0 : strlen(text));
 
    if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
       png_error(png_ptr, png_ptr->zstream.msg);
@@ -1597,7 +1625,7 @@
     png_const_charp lang, png_const_charp lang_key, png_const_charp text)
 {
    png_uint_32 key_len, prefix_len;
-   png_size_t lang_len, lang_key_len;
+   size_t lang_len, lang_key_len;
    png_byte new_key[82];
    compression_state comp;
 
@@ -1707,7 +1735,7 @@
    png_save_int_32(buf + 4, y_offset);
    buf[8] = (png_byte)unit_type;
 
-   png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
+   png_write_complete_chunk(png_ptr, png_oFFs, buf, 9);
 }
 #endif
 #ifdef PNG_WRITE_pCAL_SUPPORTED
@@ -1718,7 +1746,7 @@
     png_charpp params)
 {
    png_uint_32 purpose_len;
-   png_size_t units_len, total_len;
+   size_t units_len, total_len;
    png_size_tp params_len;
    png_byte buf[10];
    png_byte new_purpose[80];
@@ -1742,7 +1770,7 @@
    total_len = purpose_len + units_len + 10;
 
    params_len = (png_size_tp)png_malloc(png_ptr,
-       (png_alloc_size_t)(nparams * (sizeof (png_size_t))));
+       (png_alloc_size_t)((png_alloc_size_t)nparams * (sizeof (size_t))));
 
    /* Find the length of each parameter, making sure we don't count the
     * null terminator for the last parameter.
@@ -1762,8 +1790,8 @@
    png_save_int_32(buf + 4, X1);
    buf[8] = (png_byte)type;
    buf[9] = (png_byte)nparams;
-   png_write_chunk_data(png_ptr, buf, (png_size_t)10);
-   png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);
+   png_write_chunk_data(png_ptr, buf, 10);
+   png_write_chunk_data(png_ptr, (png_const_bytep)units, (size_t)units_len);
 
    for (i = 0; i < nparams; i++)
    {
@@ -1782,7 +1810,7 @@
     png_const_charp height)
 {
    png_byte buf[64];
-   png_size_t wlen, hlen, total_len;
+   size_t wlen, hlen, total_len;
 
    png_debug(1, "in png_write_sCAL_s");
 
@@ -1823,7 +1851,7 @@
    png_save_uint_32(buf + 4, y_pixels_per_unit);
    buf[8] = (png_byte)unit_type;
 
-   png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);
+   png_write_complete_chunk(png_ptr, png_pHYs, buf, 9);
 }
 #endif
 
@@ -1853,7 +1881,7 @@
    buf[5] = mod_time->minute;
    buf[6] = mod_time->second;
 
-   png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7);
+   png_write_complete_chunk(png_ptr, png_tIME, buf, 7);
 }
 #endif
 
@@ -1865,16 +1893,16 @@
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
 
    /* Start of interlace block */
-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
 
    /* Offset to next interlace block */
-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
 
    /* Start of interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
 
    /* Offset to next interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
 #endif
 
    png_alloc_size_t buf_size;
@@ -1941,7 +1969,7 @@
     */
    if ((filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) != 0)
       png_ptr->prev_row = png_voidcast(png_bytep,
-         png_calloc(png_ptr, buf_size));
+          png_calloc(png_ptr, buf_size));
 #endif /* WRITE_FILTER */
 
 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
@@ -1980,16 +2008,16 @@
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
 
    /* Start of interlace block */
-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
 
    /* Offset to next interlace block */
-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
 
    /* Start of interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+   static const png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
 
    /* Offset to next interlace block in the y direction */
-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+   static const png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
 #endif
 
    png_debug(1, "in png_write_finish_row");
@@ -2043,8 +2071,8 @@
       {
          if (png_ptr->prev_row != NULL)
             memset(png_ptr->prev_row, 0,
-                (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
-                png_ptr->usr_bit_depth, png_ptr->width)) + 1);
+                PNG_ROWBYTES(png_ptr->usr_channels *
+                png_ptr->usr_bit_depth, png_ptr->width) + 1);
 
          return;
       }
@@ -2070,10 +2098,10 @@
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
 
    /* Start of interlace block */
-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+   static const png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
 
    /* Offset to next interlace block */
-   static PNG_CONST png_byte  png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+   static const png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
 
    png_debug(1, "in png_do_write_interlace");
 
@@ -2100,7 +2128,7 @@
             for (i = png_pass_start[pass]; i < row_width;
                i += png_pass_inc[pass])
             {
-               sp = row + (png_size_t)(i >> 3);
+               sp = row + (size_t)(i >> 3);
                value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
                d |= (value << shift);
 
@@ -2138,7 +2166,7 @@
             for (i = png_pass_start[pass]; i < row_width;
                i += png_pass_inc[pass])
             {
-               sp = row + (png_size_t)(i >> 2);
+               sp = row + (size_t)(i >> 2);
                value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
                d |= (value << shift);
 
@@ -2174,7 +2202,7 @@
             for (i = png_pass_start[pass]; i < row_width;
                 i += png_pass_inc[pass])
             {
-               sp = row + (png_size_t)(i >> 1);
+               sp = row + (size_t)(i >> 1);
                value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
                d |= (value << shift);
 
@@ -2200,7 +2228,7 @@
             png_bytep dp;
             png_uint_32 i;
             png_uint_32 row_width = row_info->width;
-            png_size_t pixel_bytes;
+            size_t pixel_bytes;
 
             /* Start at the beginning */
             dp = row;
@@ -2213,7 +2241,7 @@
                i += png_pass_inc[pass])
             {
                /* Find out where the original pixel is */
-               sp = row + (png_size_t)i * pixel_bytes;
+               sp = row + (size_t)i * pixel_bytes;
 
                /* Move the pixel */
                if (dp != sp)
@@ -2244,17 +2272,17 @@
  */
 static void /* PRIVATE */
 png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
-   png_size_t row_bytes);
+    size_t row_bytes);
 
 #ifdef PNG_WRITE_FILTER_SUPPORTED
-static png_size_t /* PRIVATE */
-png_setup_sub_row(png_structrp png_ptr, const png_uint_32 bpp,
-    const png_size_t row_bytes, const png_size_t lmins)
+static size_t /* PRIVATE */
+png_setup_sub_row(png_structrp png_ptr, png_uint_32 bpp,
+    size_t row_bytes, size_t lmins)
 {
    png_bytep rp, dp, lp;
-   png_size_t i;
-   png_size_t sum = 0;
-   int v;
+   size_t i;
+   size_t sum = 0;
+   unsigned int v;
 
    png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
 
@@ -2262,14 +2290,22 @@
         i++, rp++, dp++)
    {
       v = *dp = *rp;
+#ifdef PNG_USE_ABS
+      sum += 128 - abs((int)v - 128);
+#else
       sum += (v < 128) ? v : 256 - v;
+#endif
    }
 
    for (lp = png_ptr->row_buf + 1; i < row_bytes;
       i++, rp++, lp++, dp++)
    {
       v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+#ifdef PNG_USE_ABS
+      sum += 128 - abs((int)v - 128);
+#else
       sum += (v < 128) ? v : 256 - v;
+#endif
 
       if (sum > lmins)  /* We are already worse, don't continue. */
         break;
@@ -2279,11 +2315,11 @@
 }
 
 static void /* PRIVATE */
-png_setup_sub_row_only(png_structrp png_ptr, const png_uint_32 bpp,
-    const png_size_t row_bytes)
+png_setup_sub_row_only(png_structrp png_ptr, png_uint_32 bpp,
+    size_t row_bytes)
 {
    png_bytep rp, dp, lp;
-   png_size_t i;
+   size_t i;
 
    png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
 
@@ -2300,14 +2336,13 @@
    }
 }
 
-static png_size_t /* PRIVATE */
-png_setup_up_row(png_structrp png_ptr, const png_size_t row_bytes,
-    const png_size_t lmins)
+static size_t /* PRIVATE */
+png_setup_up_row(png_structrp png_ptr, size_t row_bytes, size_t lmins)
 {
    png_bytep rp, dp, pp;
-   png_size_t i;
-   png_size_t sum = 0;
-   int v;
+   size_t i;
+   size_t sum = 0;
+   unsigned int v;
 
    png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
 
@@ -2316,7 +2351,11 @@
        i++, rp++, pp++, dp++)
    {
       v = *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+#ifdef PNG_USE_ABS
+      sum += 128 - abs((int)v - 128);
+#else
       sum += (v < 128) ? v : 256 - v;
+#endif
 
       if (sum > lmins)  /* We are already worse, don't continue. */
         break;
@@ -2325,10 +2364,10 @@
    return (sum);
 }
 static void /* PRIVATE */
-png_setup_up_row_only(png_structrp png_ptr, const png_size_t row_bytes)
+png_setup_up_row_only(png_structrp png_ptr, size_t row_bytes)
 {
    png_bytep rp, dp, pp;
-   png_size_t i;
+   size_t i;
 
    png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
 
@@ -2340,23 +2379,27 @@
    }
 }
 
-static png_size_t /* PRIVATE */
-png_setup_avg_row(png_structrp png_ptr, const png_uint_32 bpp,
-      const png_size_t row_bytes, const png_size_t lmins)
+static size_t /* PRIVATE */
+png_setup_avg_row(png_structrp png_ptr, png_uint_32 bpp,
+    size_t row_bytes, size_t lmins)
 {
    png_bytep rp, dp, pp, lp;
    png_uint_32 i;
-   png_size_t sum = 0;
-   int v;
+   size_t sum = 0;
+   unsigned int v;
 
    png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
 
    for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-        pp = png_ptr->prev_row + 1; i < bpp; i++)
+       pp = png_ptr->prev_row + 1; i < bpp; i++)
    {
       v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
 
+#ifdef PNG_USE_ABS
+      sum += 128 - abs((int)v - 128);
+#else
       sum += (v < 128) ? v : 256 - v;
+#endif
    }
 
    for (lp = png_ptr->row_buf + 1; i < row_bytes; i++)
@@ -2364,7 +2407,11 @@
       v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
           & 0xff);
 
+#ifdef PNG_USE_ABS
+      sum += 128 - abs((int)v - 128);
+#else
       sum += (v < 128) ? v : 256 - v;
+#endif
 
       if (sum > lmins)  /* We are already worse, don't continue. */
         break;
@@ -2373,8 +2420,8 @@
    return (sum);
 }
 static void /* PRIVATE */
-png_setup_avg_row_only(png_structrp png_ptr, const png_uint_32 bpp,
-      const png_size_t row_bytes)
+png_setup_avg_row_only(png_structrp png_ptr, png_uint_32 bpp,
+    size_t row_bytes)
 {
    png_bytep rp, dp, pp, lp;
    png_uint_32 i;
@@ -2382,7 +2429,7 @@
    png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
 
    for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
-        pp = png_ptr->prev_row + 1; i < bpp; i++)
+       pp = png_ptr->prev_row + 1; i < bpp; i++)
    {
       *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
    }
@@ -2394,14 +2441,14 @@
    }
 }
 
-static png_size_t /* PRIVATE */
-png_setup_paeth_row(png_structrp png_ptr, const png_uint_32 bpp,
-    const png_size_t row_bytes, const png_size_t lmins)
+static size_t /* PRIVATE */
+png_setup_paeth_row(png_structrp png_ptr, png_uint_32 bpp,
+    size_t row_bytes, size_t lmins)
 {
    png_bytep rp, dp, pp, cp, lp;
-   png_size_t i;
-   png_size_t sum = 0;
-   int v;
+   size_t i;
+   size_t sum = 0;
+   unsigned int v;
 
    png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
 
@@ -2410,7 +2457,11 @@
    {
       v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
 
+#ifdef PNG_USE_ABS
+      sum += 128 - abs((int)v - 128);
+#else
       sum += (v < 128) ? v : 256 - v;
+#endif
    }
 
    for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
@@ -2439,7 +2490,11 @@
 
       v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
 
+#ifdef PNG_USE_ABS
+      sum += 128 - abs((int)v - 128);
+#else
       sum += (v < 128) ? v : 256 - v;
+#endif
 
       if (sum > lmins)  /* We are already worse, don't continue. */
         break;
@@ -2448,11 +2503,11 @@
    return (sum);
 }
 static void /* PRIVATE */
-png_setup_paeth_row_only(png_structrp png_ptr, const png_uint_32 bpp,
-    const png_size_t row_bytes)
+png_setup_paeth_row_only(png_structrp png_ptr, png_uint_32 bpp,
+    size_t row_bytes)
 {
    png_bytep rp, dp, pp, cp, lp;
-   png_size_t i;
+   size_t i;
 
    png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
 
@@ -2501,8 +2556,8 @@
    png_bytep row_buf;
    png_bytep best_row;
    png_uint_32 bpp;
-   png_size_t mins;
-   png_size_t row_bytes = row_info->rowbytes;
+   size_t mins;
+   size_t row_bytes = row_info->rowbytes;
 
    png_debug(1, "in png_write_find_filter");
 
@@ -2548,7 +2603,7 @@
       /* Overflow can occur in the calculation, just select the lowest set
        * filter.
        */
-      filter_to_do &= -filter_to_do;
+      filter_to_do &= 0U-filter_to_do;
    }
    else if ((filter_to_do & PNG_FILTER_NONE) != 0 &&
          filter_to_do != PNG_FILTER_NONE)
@@ -2557,15 +2612,19 @@
        * 'none' filter.
        */
       png_bytep rp;
-      png_size_t sum = 0;
-      png_size_t i;
-      int v;
+      size_t sum = 0;
+      size_t i;
+      unsigned int v;
 
       {
          for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
          {
             v = *rp;
+#ifdef PNG_USE_ABS
+            sum += 128 - abs((int)v - 128);
+#else
             sum += (v < 128) ? v : 256 - v;
+#endif
          }
       }
 
@@ -2582,8 +2641,8 @@
 
    else if ((filter_to_do & PNG_FILTER_SUB) != 0)
    {
-      png_size_t sum;
-      png_size_t lmins = mins;
+      size_t sum;
+      size_t lmins = mins;
 
       sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins);
 
@@ -2608,8 +2667,8 @@
 
    else if ((filter_to_do & PNG_FILTER_UP) != 0)
    {
-      png_size_t sum;
-      png_size_t lmins = mins;
+      size_t sum;
+      size_t lmins = mins;
 
       sum = png_setup_up_row(png_ptr, row_bytes, lmins);
 
@@ -2634,8 +2693,8 @@
 
    else if ((filter_to_do & PNG_FILTER_AVG) != 0)
    {
-      png_size_t sum;
-      png_size_t lmins = mins;
+      size_t sum;
+      size_t lmins = mins;
 
       sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins);
 
@@ -2652,7 +2711,7 @@
    }
 
    /* Paeth filter */
-   if ((filter_to_do == PNG_FILTER_PAETH) != 0)
+   if (filter_to_do == PNG_FILTER_PAETH)
    {
       png_setup_paeth_row_only(png_ptr, bpp, row_bytes);
       best_row = png_ptr->try_row;
@@ -2660,8 +2719,8 @@
 
    else if ((filter_to_do & PNG_FILTER_PAETH) != 0)
    {
-      png_size_t sum;
-      png_size_t lmins = mins;
+      size_t sum;
+      size_t lmins = mins;
 
       sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins);
 
@@ -2686,7 +2745,7 @@
 /* Do the actual writing of a previously filtered row. */
 static void
 png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
-   png_size_t full_row_length/*includes filter byte*/)
+    size_t full_row_length/*includes filter byte*/)
 {
    png_debug(1, "in png_write_filtered_row");
 
diff --git a/third_party/libtiff/0000-build-config.patch b/third_party/libtiff/0000-build-config.patch
index 2989607..4acd37e 100644
--- a/third_party/libtiff/0000-build-config.patch
+++ b/third_party/libtiff/0000-build-config.patch
@@ -68,7 +68,7 @@
 diff a/third_party/libtiff/tiffconf.h b/third_party/libtiff/tiffconf.h
 --- /dev/null
 +++ b/third_party/libtiff/tiffconf.h
-@@ -0,0 +1,239 @@
+@@ -0,0 +1,240 @@
 +/* libtiff/tiffconf.h.  Generated by configure.  */
 +/*
 +  Configuration defines for installed libtiff.
@@ -78,6 +78,7 @@
 +#ifndef _TIFFCONF_
 +#define _TIFFCONF_
 +
++#include "build/build_config.h"
 +#include "core/fxcrt/fx_system.h"
 +
 +//NOTE: The tiff codec requires an ANSI C compiler environment for building and 
@@ -100,7 +101,7 @@
 +//fx_system.h already include the string.h in ANSIC
 +
 +/* Define to 1 if you have the <search.h> header file. */
-+#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ && _MSC_VER >= 1900
++#if defined(OS_WIN)
 +// search.h is always available in VS 2015 and above, and may be
 +// available in earlier versions.
 +#define HAVE_SEARCH_H 1
diff --git a/third_party/libtiff/0006-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch b/third_party/libtiff/0006-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch
deleted file mode 100644
index 5d44fb1..0000000
--- a/third_party/libtiff/0006-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-diff --git a/core/fxcodec/codec/ccodec_tiffmodule.cpp b/core/fxcodec/codec/ccodec_tiffmodule.cpp
-index 09cfea4..20fda63 100644
---- a/core/fxcodec/codec/ccodec_tiffmodule.cpp
-+++ b/core/fxcodec/codec/ccodec_tiffmodule.cpp
-@@ -79,6 +79,10 @@ int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) {
-   return memcmp(ptr1, ptr2, (size_t)size);
- }
- 
-+int _TIFFIfMultiplicationOverflow(tmsize_t op1, tmsize_t op2) {
-+  return op1 > std::numeric_limits<tmsize_t>::max() / op2;
-+}
-+
- TIFFErrorHandler _TIFFwarningHandler = nullptr;
- TIFFErrorHandler _TIFFerrorHandler = nullptr;
- 
-diff --git a/third_party/libtiff/tif_aux.c b/third_party/libtiff/tif_aux.c
-index 927150a..3ce3680 100644
---- a/third_party/libtiff/tif_aux.c
-+++ b/third_party/libtiff/tif_aux.c
-@@ -69,7 +69,7 @@ _TIFFCheckRealloc(TIFF* tif, void* buffer,
- 	/*
- 	 * XXX: Check for integer overflow.
- 	 */
--	if (nmemb && elem_size && bytes / elem_size == nmemb)
-+	if (nmemb && elem_size && !_TIFFIfMultiplicationOverflow(nmemb, elem_size))
- 		cp = _TIFFrealloc(buffer, bytes);
- 
- 	if (cp == NULL) {
-diff --git a/third_party/libtiff/tiffio.h b/third_party/libtiff/tiffio.h
-index 038b670..056aed2 100644
---- a/third_party/libtiff/tiffio.h
-+++ b/third_party/libtiff/tiffio.h
-@@ -298,6 +298,7 @@ extern void _TIFFmemset(void* p, int v, tmsize_t c);
- extern void _TIFFmemcpy(void* d, const void* s, tmsize_t c);
- extern int _TIFFmemcmp(const void* p1, const void* p2, tmsize_t c);
- extern void _TIFFfree(void* p);
-+extern int _TIFFIfMultiplicationOverflow(tmsize_t op1, tmsize_t op2);
- 
- /*
- ** Stuff, related to tag handling and creating custom tags.
diff --git a/third_party/libtiff/0008-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch b/third_party/libtiff/0008-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch
deleted file mode 100644
index 3d494d8..0000000
--- a/third_party/libtiff/0008-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/third_party/libtiff/tif_aux.c b/third_party/libtiff/tif_aux.c
-index 3ce3680..bc4ea01 100644
---- a/third_party/libtiff/tif_aux.c
-+++ b/third_party/libtiff/tif_aux.c
-@@ -69,7 +69,7 @@ _TIFFCheckRealloc(TIFF* tif, void* buffer,
- 	/*
- 	 * XXX: Check for integer overflow.
- 	 */
--	if (nmemb && elem_size && !_TIFFIfMultiplicationOverflow(nmemb, elem_size))
-+	if (nmemb > 0 && elem_size > 0 && !_TIFFIfMultiplicationOverflow(nmemb, elem_size))
- 		cp = _TIFFrealloc(buffer, bytes);
- 
- 	if (cp == NULL) {
diff --git a/third_party/libtiff/0017-safe_skews_in_gtTileContig.patch b/third_party/libtiff/0017-safe_skews_in_gtTileContig.patch
index ad6d33d..b27bab5 100644
--- a/third_party/libtiff/0017-safe_skews_in_gtTileContig.patch
+++ b/third_party/libtiff/0017-safe_skews_in_gtTileContig.patch
@@ -1,5 +1,5 @@
 diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
-index 84cc1d1a7..82e19164e 100644
+index fc554ccab..fff3f7fde 100644
 --- a/third_party/libtiff/tif_getimage.c
 +++ b/third_party/libtiff/tif_getimage.c
 @@ -31,6 +31,7 @@
@@ -10,15 +10,15 @@
  
  static int gtTileContig(TIFFRGBAImage*, uint32*, uint32, uint32);
  static int gtTileSeparate(TIFFRGBAImage*, uint32*, uint32, uint32);
-@@ -629,6 +628,7 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+@@ -628,6 +629,7 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
      uint32 tw, th;
-     unsigned char* buf;
+     unsigned char* buf = NULL;
      int32 fromskew, toskew;
 +    int64 safeskew;
      uint32 nrow;
      int ret = 1, flip;
      uint32 this_tw, tocol;
-@@ -649,19 +647,37 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+@@ -648,19 +650,37 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
      flip = setorientation(img);
      if (flip & FLIP_VERTICALLY) {
  	    y = h - 1;
@@ -56,10 +56,10 @@
 +       return (0);
 +    }
 +    leftmost_toskew = safeskew;
-     for (row = 0; row < h; row += nrow)
+     for (row = 0; ret != 0 && row < h; row += nrow)
      {
          rowstoread = th - (row + img->row_offset) % th;
-@@ -704,9 +684,24 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+@@ -686,9 +706,24 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
  		/*
  		 * Rightmost tile is clipped on right side.
  		 */
diff --git a/third_party/libtiff/0025-upstream-OOM-gtTileContig.patch b/third_party/libtiff/0025-upstream-OOM-gtTileContig.patch
deleted file mode 100644
index d4d3d70..0000000
--- a/third_party/libtiff/0025-upstream-OOM-gtTileContig.patch
+++ /dev/null
@@ -1,460 +0,0 @@
-diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
-index 53c938a89..03c9a81fb 100644
---- a/third_party/libtiff/tif_getimage.c
-+++ b/third_party/libtiff/tif_getimage.c
-@@ -627,7 +627,7 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-     uint32 col, row, y, rowstoread;
-     tmsize_t pos;
-     uint32 tw, th;
--    unsigned char* buf;
-+    unsigned char* buf = NULL;
-     int32 fromskew, toskew;
-     int64 safeskew;
-     uint32 nrow;
-@@ -636,13 +636,14 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-     int32 this_toskew, leftmost_toskew;
-     int32 leftmost_fromskew;
-     uint32 leftmost_tw;
-+    tmsize_t bufsize;
- 
--    buf = (unsigned char*) _TIFFmalloc(TIFFTileSize(tif));
--    if (buf == 0) {
--               TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "No space for tile buffer");
--               return (0);
-+    bufsize = TIFFTileSize(tif);
-+    if (bufsize == 0) {
-+        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "No space for tile buffer");
-+        return (0);
-     }
--    _TIFFmemset(buf, 0, TIFFTileSize(tif));
-+
-     TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
-     TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
- 
-@@ -691,8 +692,9 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-        col = img->col_offset;
-        while (tocol < w)
-         {
--           if (TIFFReadTile(tif, buf, col,  
--                            row+img->row_offset, 0, 0)==(tmsize_t)(-1) && img->stoponerr)
-+           if (_TIFFReadTileAndAllocBuffer(tif, (void**) &buf, bufsize, col,
-+                            row+img->row_offset, 0, 0)==(tmsize_t)(-1) &&
-+                (buf == NULL || img->stoponerr))
-             {
-                 ret = 0;
-                 break;
-@@ -772,11 +774,11 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-        uint32 col, row, y, rowstoread;
-        tmsize_t pos;
-        uint32 tw, th;
--       unsigned char* buf;
--       unsigned char* p0;
--       unsigned char* p1;
--       unsigned char* p2;
--       unsigned char* pa;
-+       unsigned char* buf = NULL;
-+       unsigned char* p0 = NULL;
-+       unsigned char* p1 = NULL;
-+       unsigned char* p2 = NULL;
-+       unsigned char* pa = NULL;
-        tmsize_t tilesize;
-        tmsize_t bufsize;
-        int32 fromskew, toskew;
-@@ -795,16 +797,7 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-                TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in %s", "gtTileSeparate");
-                return (0);
-        }
--       buf = (unsigned char*) _TIFFmalloc(bufsize);
--       if (buf == 0) {
--               TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "No space for tile buffer");
--               return (0);
--       }
--       _TIFFmemset(buf, 0, bufsize);
--       p0 = buf;
--       p1 = p0 + tilesize;
--       p2 = p1 + tilesize;
--       pa = (alpha?(p2+tilesize):NULL);
-+
-        TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
-        TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
- 
-@@ -824,7 +817,6 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-           case PHOTOMETRIC_MINISBLACK:
-           case PHOTOMETRIC_PALETTE:
-             colorchannels = 1;
--            p2 = p1 = p0;
-             break;
- 
-           default:
-@@ -849,7 +841,30 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-                col = img->col_offset;
-                while (tocol < w)
-                {
--                       if (TIFFReadTile(tif, p0, col,  
-+                        if( buf == NULL )
-+                        {
-+                            if (_TIFFReadTileAndAllocBuffer(
-+                                    tif, (void**) &buf, bufsize, col,
-+                                    row+img->row_offset,0,0)==(tmsize_t)(-1)
-+                                && (buf == NULL || img->stoponerr))
-+                            {
-+                                    ret = 0;
-+                                    break;
-+                            }
-+                            p0 = buf;
-+                            if( colorchannels == 1 )
-+                            {
-+                                p2 = p1 = p0;
-+                                pa = (alpha?(p0+3*tilesize):NULL);
-+                            }
-+                            else
-+                            {
-+                                p1 = p0 + tilesize;
-+                                p2 = p1 + tilesize;
-+                                pa = (alpha?(p2+tilesize):NULL);
-+                            }
-+                        }
-+                       else if (TIFFReadTile(tif, p0, col,  
-                            row+img->row_offset,0,0)==(tmsize_t)(-1) && img->stoponerr)
-                        {
-                                ret = 0;
-@@ -940,13 +955,14 @@ gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-        tileContigRoutine put = img->put.contig;
-        uint32 row, y, nrow, nrowsub, rowstoread;
-        tmsize_t pos;
--       unsigned char* buf;
-+       unsigned char* buf = NULL;
-        uint32 rowsperstrip;
-        uint16 subsamplinghor,subsamplingver;
-        uint32 imagewidth = img->width;
-        tmsize_t scanline;
-        int32 fromskew, toskew;
-        int ret = 1, flip;
-+       tmsize_t maxstripsize;
- 
-        TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor, &subsamplingver);
-        if( subsamplingver == 0 ) {
-@@ -954,12 +970,7 @@ gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-                return (0);
-        }
- 
--       buf = (unsigned char*) _TIFFmalloc(TIFFStripSize(tif));
--       if (buf == 0) {
--               TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for strip buffer");
--               return (0);
--       }
--       _TIFFmemset(buf, 0, TIFFStripSize(tif));
-+       maxstripsize = TIFFStripSize(tif);
- 
-        flip = setorientation(img);
-        if (flip & FLIP_VERTICALLY) {
-@@ -981,11 +992,12 @@ gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-                nrowsub = nrow;
-                if ((nrowsub%subsamplingver)!=0)
-                        nrowsub+=subsamplingver-nrowsub%subsamplingver;
--               if (TIFFReadEncodedStrip(tif,
-+               if (_TIFFReadEncodedStripAndAllocBuffer(tif,
-                    TIFFComputeStrip(tif,row+img->row_offset, 0),
--                   buf,
-+                   (void**)(&buf),
-+                   maxstripsize,
-                    ((row + img->row_offset)%rowsperstrip + nrowsub) * scanline)==(tmsize_t)(-1)
--                   && img->stoponerr)
-+                   && (buf == NULL || img->stoponerr))
-                {
-                        ret = 0;
-                        break;
-@@ -1029,8 +1041,8 @@ gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
- {
-        TIFF* tif = img->tif;
-        tileSeparateRoutine put = img->put.separate;
--       unsigned char *buf;
--       unsigned char *p0, *p1, *p2, *pa;
-+       unsigned char *buf = NULL;
-+       unsigned char *p0 = NULL, *p1 = NULL, *p2 = NULL, *pa = NULL;
-        uint32 row, y, nrow, rowstoread;
-        tmsize_t pos;
-        tmsize_t scanline;
-@@ -1049,15 +1061,6 @@ gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-                TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in %s", "gtStripSeparate");
-                return (0);
-        }
--       p0 = buf = (unsigned char *)_TIFFmalloc(bufsize);
--       if (buf == 0) {
--               TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for tile buffer");
--               return (0);
--       }
--       _TIFFmemset(buf, 0, bufsize);
--       p1 = p0 + stripsize;
--       p2 = p1 + stripsize;
--       pa = (alpha?(p2+stripsize):NULL);
- 
-        flip = setorientation(img);
-        if (flip & FLIP_VERTICALLY) {
-@@ -1075,7 +1078,6 @@ gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-           case PHOTOMETRIC_MINISBLACK:
-           case PHOTOMETRIC_PALETTE:
-             colorchannels = 1;
--            p2 = p1 = p0;
-             break;
- 
-           default:
-@@ -1091,7 +1093,31 @@ gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-                rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
-                nrow = (row + rowstoread > h ? h - row : rowstoread);
-                offset_row = row + img->row_offset;
--               if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
-+                if( buf == NULL )
-+                {
-+                    if (_TIFFReadEncodedStripAndAllocBuffer(
-+                            tif, TIFFComputeStrip(tif, offset_row, 0),
-+                            (void**) &buf, bufsize,
-+                            ((row + img->row_offset)%rowsperstrip + nrow) * scanline)==(tmsize_t)(-1)
-+                        && (buf == NULL || img->stoponerr))
-+                    {
-+                            ret = 0;
-+                            break;
-+                    }
-+                    p0 = buf;
-+                    if( colorchannels == 1 )
-+                    {
-+                        p2 = p1 = p0;
-+                        pa = (alpha?(p0+3*stripsize):NULL);
-+                    }
-+                    else
-+                    {
-+                        p1 = p0 + stripsize;
-+                        p2 = p1 + stripsize;
-+                        pa = (alpha?(p2+stripsize):NULL);
-+                    }
-+                }
-+               else if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
-                    p0, ((row + img->row_offset)%rowsperstrip + nrow) * scanline)==(tmsize_t)(-1)
-                    && img->stoponerr)
-                {
-diff --git a/third_party/libtiff/tif_read.c b/third_party/libtiff/tif_read.c
-index cc4f5d2f6..ad0a778c0 100644
---- a/third_party/libtiff/tif_read.c
-+++ b/third_party/libtiff/tif_read.c
-@@ -442,18 +442,17 @@ TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample)
- }
- 
- /*
-- * Read a strip of data and decompress the specified
-- * amount into the user-supplied buffer.
-+ * Calculate the strip size according to the number of
-+ * rows in the strip (check for truncated last strip on any
-+ * of the separations).
-  */
--tmsize_t
--TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
-+static tmsize_t TIFFReadEncodedStripGetStripSize(TIFF* tif, uint32 strip, uint16* pplane)
- {
-        static const char module[] = "TIFFReadEncodedStrip";
-        TIFFDirectory *td = &tif->tif_dir;
-        uint32 rowsperstrip;
-        uint32 stripsperplane;
-        uint32 stripinplane;
--       uint16 plane;
-        uint32 rows;
-        tmsize_t stripsize;
-        if (!TIFFCheckRead(tif,0))
-@@ -465,23 +464,37 @@ TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
-                    (unsigned long)td->td_nstrips);
-                return((tmsize_t)(-1));
-        }
--       /*
--        * Calculate the strip size according to the number of
--        * rows in the strip (check for truncated last strip on any
--        * of the separations).
--        */
-+
-        rowsperstrip=td->td_rowsperstrip;
-        if (rowsperstrip>td->td_imagelength)
-                rowsperstrip=td->td_imagelength;
-        stripsperplane= TIFFhowmany_32_maxuint_compat(td->td_imagelength, rowsperstrip);
-        stripinplane=(strip%stripsperplane);
--       plane=(uint16)(strip/stripsperplane);
-+       if( pplane ) *pplane=(uint16)(strip/stripsperplane);
-        rows=td->td_imagelength-stripinplane*rowsperstrip;
-        if (rows>rowsperstrip)
-                rows=rowsperstrip;
-        stripsize=TIFFVStripSize(tif,rows);
-        if (stripsize==0)
-                return((tmsize_t)(-1));
-+    return stripsize;
-+}
-+
-+/*
-+ * Read a strip of data and decompress the specified
-+ * amount into the user-supplied buffer.
-+ */
-+tmsize_t
-+TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
-+{
-+  static const char module[] = "TIFFReadEncodedStrip";
-+  TIFFDirectory *td = &tif->tif_dir;
-+  tmsize_t stripsize;
-+  uint16 plane;
-+
-+  stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
-+  if (stripsize==((tmsize_t)(-1)))
-+      return((tmsize_t)(-1));
- 
-     /* shortcut to avoid an extra memcpy() */
-     if( td->td_compression == COMPRESSION_NONE &&
-@@ -510,6 +523,50 @@ TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
-        return(stripsize);
- }
- 
-+/* Variant of TIFFReadEncodedStrip() that does 
-+ * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillStrip() has
-+ *   suceeded. This avoid excessive memory allocation in case of truncated
-+ *   file.
-+ * * calls regular TIFFReadEncodedStrip() if *buf != NULL
-+ */
-+tmsize_t
-+_TIFFReadEncodedStripAndAllocBuffer(TIFF* tif, uint32 strip,
-+                                    void **buf, tmsize_t bufsizetoalloc,
-+                                    tmsize_t size_to_read)
-+{
-+    tmsize_t this_stripsize;
-+    uint16 plane;
-+
-+    if( *buf != NULL )
-+    {
-+        return TIFFReadEncodedStrip(tif, strip, *buf, size_to_read);
-+    }
-+
-+    this_stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
-+    if (this_stripsize==((tmsize_t)(-1)))
-+            return((tmsize_t)(-1));
-+
-+    if ((size_to_read!=(tmsize_t)(-1))&&(size_to_read<this_stripsize))
-+            this_stripsize=size_to_read;
-+    if (!TIFFFillStrip(tif,strip))
-+            return((tmsize_t)(-1));
-+
-+    *buf = _TIFFmalloc(bufsizetoalloc);
-+    if (*buf == NULL) {
-+            TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for strip buffer");
-+            return((tmsize_t)(-1));
-+    }
-+    _TIFFmemset(*buf, 0, bufsizetoalloc);
-+
-+    if ((*tif->tif_decodestrip)(tif,*buf,this_stripsize,plane)<=0)
-+            return((tmsize_t)(-1));
-+    (*tif->tif_postdecode)(tif,*buf,this_stripsize);
-+    return(this_stripsize);
-+
-+
-+}
-+
-+
- static tmsize_t
- TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size,
-     const char* module)
-@@ -939,6 +996,78 @@ TIFFReadEncodedTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size)
-                return ((tmsize_t)(-1));
- }
- 
-+/* Variant of TIFFReadTile() that does 
-+ * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillTile() has
-+ *   suceeded. This avoid excessive memory allocation in case of truncated
-+ *   file.
-+ * * calls regular TIFFReadEncodedTile() if *buf != NULL
-+ */
-+tmsize_t
-+_TIFFReadTileAndAllocBuffer(TIFF* tif,
-+                            void **buf, tmsize_t bufsizetoalloc,
-+                            uint32 x, uint32 y, uint32 z, uint16 s)
-+{
-+    if (!TIFFCheckRead(tif, 1) || !TIFFCheckTile(tif, x, y, z, s))
-+            return ((tmsize_t)(-1));
-+    return (_TIFFReadEncodedTileAndAllocBuffer(tif,
-+                                               TIFFComputeTile(tif, x, y, z, s),
-+                                               buf, bufsizetoalloc,
-+                                               (tmsize_t)(-1)));
-+}
-+
-+/* Variant of TIFFReadEncodedTile() that does 
-+ * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillTile() has
-+ *   suceeded. This avoid excessive memory allocation in case of truncated
-+ *   file.
-+ * * calls regular TIFFReadEncodedTile() if *buf != NULL
-+ */
-+tmsize_t
-+_TIFFReadEncodedTileAndAllocBuffer(TIFF* tif, uint32 tile,
-+                                    void **buf, tmsize_t bufsizetoalloc,
-+                                    tmsize_t size_to_read)
-+{
-+    static const char module[] = "_TIFFReadEncodedTileAndAllocBuffer";
-+    TIFFDirectory *td = &tif->tif_dir;
-+    tmsize_t tilesize = tif->tif_tilesize;
-+
-+    if( *buf != NULL )
-+    {
-+        return TIFFReadEncodedTile(tif, tile, *buf, size_to_read);
-+    }
-+
-+    if (!TIFFCheckRead(tif, 1))
-+            return ((tmsize_t)(-1));
-+    if (tile >= td->td_nstrips) {
-+            TIFFErrorExt(tif->tif_clientdata, module,
-+                "%lu: Tile out of range, max %lu",
-+                (unsigned long) tile, (unsigned long) td->td_nstrips);
-+            return ((tmsize_t)(-1));
-+    }
-+
-+    if (!TIFFFillTile(tif,tile))
-+            return((tmsize_t)(-1));
-+
-+    *buf = _TIFFmalloc(bufsizetoalloc);
-+    if (*buf == NULL) {
-+            TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
-+                         "No space for tile buffer");
-+            return((tmsize_t)(-1));
-+    }
-+    _TIFFmemset(*buf, 0, bufsizetoalloc);
-+
-+    if (size_to_read == (tmsize_t)(-1))
-+        size_to_read = tilesize;
-+    else if (size_to_read > tilesize)
-+        size_to_read = tilesize;
-+    if( (*tif->tif_decodetile)(tif,
-+        (uint8*) *buf, size_to_read, (uint16)(tile/td->td_stripsperimage))) {
-+        (*tif->tif_postdecode)(tif, (uint8*) *buf, size_to_read);
-+        return (size_to_read);
-+    } else
-+        return ((tmsize_t)(-1));
-+}
-+
-+
- static tmsize_t
- TIFFReadRawTile1(TIFF* tif, uint32 tile, void* buf, tmsize_t size, const char* module)
- {
-diff --git a/third_party/libtiff/tiffiop.h b/third_party/libtiff/tiffiop.h
-index 7e415c750..6fb47de5b 100644
---- a/third_party/libtiff/tiffiop.h
-+++ b/third_party/libtiff/tiffiop.h
-@@ -364,6 +364,20 @@ extern void* _TIFFCheckRealloc(TIFF*, void*, tmsize_t, tmsize_t, const char*);
- extern double _TIFFUInt64ToDouble(uint64);
- extern float _TIFFUInt64ToFloat(uint64);
- 
-+extern tmsize_t
-+_TIFFReadEncodedStripAndAllocBuffer(TIFF* tif, uint32 strip,
-+                                    void **buf, tmsize_t bufsizetoalloc,
-+                                    tmsize_t size_to_read);
-+extern tmsize_t
-+_TIFFReadEncodedTileAndAllocBuffer(TIFF* tif, uint32 tile,
-+                                    void **buf, tmsize_t bufsizetoalloc,
-+                                    tmsize_t size_to_read);
-+extern tmsize_t
-+_TIFFReadTileAndAllocBuffer(TIFF* tif,
-+                            void **buf, tmsize_t bufsizetoalloc,
-+                            uint32 x, uint32 y, uint32 z, uint16 s);
-+
-+
- extern int TIFFInitDumpMode(TIFF*, int);
- #ifdef PACKBITS_SUPPORT
- extern int TIFFInitPackBits(TIFF*, int);
diff --git a/third_party/libtiff/0026-upstream-null-dereference.patch b/third_party/libtiff/0026-upstream-null-dereference.patch
deleted file mode 100644
index 052645f..0000000
--- a/third_party/libtiff/0026-upstream-null-dereference.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
-index 03c9a81fb..d37f729c4 100644
---- a/third_party/libtiff/tif_getimage.c
-+++ b/third_party/libtiff/tif_getimage.c
-@@ -681,7 +681,7 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-        return (0);
-     }
-     leftmost_toskew = safeskew;
--    for (row = 0; row < h; row += nrow)
-+    for (row = 0; ret != 0 && row < h; row += nrow)
-     {
-         rowstoread = th - (row + img->row_offset) % th;
-        nrow = (row + rowstoread > h ? h - row : rowstoread);
-@@ -830,7 +830,7 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
-        leftmost_fromskew = img->col_offset % tw;
-        leftmost_tw = tw - leftmost_fromskew;
-        leftmost_toskew = toskew + leftmost_fromskew;
--       for (row = 0; row < h; row += nrow)
-+       for (row = 0; ret != 0 && row < h; row += nrow)
-        {
-                rowstoread = th - (row + img->row_offset) % th;
-                nrow = (row + rowstoread > h ? h - row : rowstoread);
diff --git a/third_party/libtiff/0028-nstrips-OOM.patch b/third_party/libtiff/0028-nstrips-OOM.patch
index a6db66e..97cc953 100644
--- a/third_party/libtiff/0028-nstrips-OOM.patch
+++ b/third_party/libtiff/0028-nstrips-OOM.patch
@@ -1,5 +1,5 @@
 diff --git a/third_party/libtiff/tif_dirread.c b/third_party/libtiff/tif_dirread.c
-index 772ebaf7d..ab938eac9 100644
+index 5e62e8131..521dbf0a4 100644
 --- a/third_party/libtiff/tif_dirread.c
 +++ b/third_party/libtiff/tif_dirread.c
 @@ -41,6 +41,7 @@
@@ -7,10 +7,10 @@
  #include "tiffiop.h"
  #include <float.h>
 +#include <limits.h>
+ #include <stdlib.h>
  
  #define IGNORE 0          /* tag placeholder used below */
- #define FAILED_FII    ((uint32) -1)
-@@ -3638,6 +3639,13 @@ TIFFReadDirectory(TIFF* tif)
+@@ -3743,6 +3744,13 @@ TIFFReadDirectory(TIFF* tif)
  		    isTiled(tif) ? "tiles" : "strips");
  		goto bad;
  	}
diff --git a/third_party/libtiff/0031-safe_size_ingtStripContig.patch b/third_party/libtiff/0031-safe_size_ingtStripContig.patch
new file mode 100644
index 0000000..e10bc34
--- /dev/null
+++ b/third_party/libtiff/0031-safe_size_ingtStripContig.patch
@@ -0,0 +1,48 @@
+diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
+index 23cb40951..2153f1348 100644
+--- a/third_party/libtiff/tif_getimage.c
++++ b/third_party/libtiff/tif_getimage.c
+@@ -962,6 +962,9 @@ gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+ 	int ret = 1, flip;
+         tmsize_t maxstripsize;
+ 
++	if ((tmsize_t)img->row_offset > TIFF_SSIZE_T_MAX || (size_t)h > (size_t)TIFF_SSIZE_T_MAX)
++		return (0);
++
+ 	TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor, &subsamplingver);
+ 	if( subsamplingver == 0 ) {
+ 		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Invalid vertical YCbCr subsampling");
+diff --git a/third_party/libtiff/tif_read.c b/third_party/libtiff/tif_read.c
+index e63810cc7..dd3002669 100644
+--- a/third_party/libtiff/tif_read.c
++++ b/third_party/libtiff/tif_read.c
+@@ -558,6 +558,8 @@ _TIFFReadEncodedStripAndAllocBuffer(TIFF* tif, uint32 strip,
+                                     void **buf, tmsize_t bufsizetoalloc,
+                                     tmsize_t size_to_read)
+ {
++    assert(size_to_read > 0);
++
+     tmsize_t this_stripsize;
+     uint16 plane;
+ 
+diff --git a/third_party/libtiff/tiffconf.h b/third_party/libtiff/tiffconf.h
+index 6292cc5cd..f57f6f709 100644
+--- a/third_party/libtiff/tiffconf.h
++++ b/third_party/libtiff/tiffconf.h
+@@ -132,13 +132,16 @@
+ 
+ #if defined(_WIN64)
+ #define TIFF_SSIZE_T signed __int64
++#define TIFF_SSIZE_T_MAX INT64_MAX
+ #else
+ #define TIFF_SSIZE_T signed int
++#define TIFF_SSIZE_T_MAX INT_MAX
+ #endif
+ 
+ #else
+ 
+ #define TIFF_SSIZE_T signed long
++#define TIFF_SSIZE_T_MAX LONG_MAX
+ 
+ #endif
+ 
diff --git a/third_party/libtiff/README.pdfium b/third_party/libtiff/README.pdfium
index a370a49..4c97a25 100644
--- a/third_party/libtiff/README.pdfium
+++ b/third_party/libtiff/README.pdfium
@@ -1,6 +1,6 @@
 Name: LibTIFF
 URL: http://www.simplesystems.org/libtiff/
-Version: 4.0.8
+Version: 4.1.0
 Security Critical: yes
 License: BSD
 
@@ -10,11 +10,7 @@
 Local Modifications:
 
 0000-build-config.patch: Local build configuration changes.
-0001-build-config.patch: Enable HAVE_SEARCH_H in tiffconf.h for VS 2015
-0006-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch: Fix a heap buffer overflow
-0008-HeapBufferOverflow-ChopUpSingleUncompressedStrip.patch: Fix a heap buffer overflow
 0017-safe_skews_in_gtTileContig.patch: return error if to/from skews overflow from int32.
-0025-upstream-OOM-gtTileContig: allocates the decoded buffer only after a first successful TIFFFillStrip.
-0026-upstream-null-dereference: properly evit when stoponerr is set and avoid null dereferences.
 0027-build-config.patch: #define variables so their value can be used by #if.
 0028-nstrips-OOM.patch: return error for excess number of tiles/strips.
+0031-safe_size_ingtStripContig.patch: return error if the size to read overflow from int32.
diff --git a/third_party/libtiff/t4.h b/third_party/libtiff/t4.h
index b908f54..fb0951a 100644
--- a/third_party/libtiff/t4.h
+++ b/third_party/libtiff/t4.h
@@ -1,5 +1,3 @@
-/* $Id: t4.h,v 1.3 2010-03-10 18:56:48 bfriesen Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_aux.c b/third_party/libtiff/tif_aux.c
index 74532c8..8188db5 100644
--- a/third_party/libtiff/tif_aux.c
+++ b/third_party/libtiff/tif_aux.c
@@ -1,5 +1,3 @@
-/* $Id: tif_aux.c,v 1.29 2016-11-11 20:45:53 erouault Exp $ */
-
 /*
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -32,31 +30,66 @@
 #include "tiffiop.h"
 #include "tif_predict.h"
 #include <math.h>
+#include <float.h>
 
 uint32
 _TIFFMultiply32(TIFF* tif, uint32 first, uint32 second, const char* where)
 {
-	uint32 bytes = first * second;
-
-	if (second && bytes / second != first) {
+	if (second && first > TIFF_UINT32_MAX / second) {
 		TIFFErrorExt(tif->tif_clientdata, where, "Integer overflow in %s", where);
-		bytes = 0;
+		return 0;
 	}
 
-	return bytes;
+	return first * second;
 }
 
 uint64
 _TIFFMultiply64(TIFF* tif, uint64 first, uint64 second, const char* where)
 {
-	uint64 bytes = first * second;
-
-	if (second && bytes / second != first) {
+	if (second && first > TIFF_UINT64_MAX / second) {
 		TIFFErrorExt(tif->tif_clientdata, where, "Integer overflow in %s", where);
-		bytes = 0;
+		return 0;
 	}
 
-	return bytes;
+	return first * second;
+}
+
+tmsize_t
+_TIFFMultiplySSize(TIFF* tif, tmsize_t first, tmsize_t second, const char* where)
+{
+    if( first <= 0 || second <= 0 )
+    {
+        if( tif != NULL && where != NULL )
+        {
+            TIFFErrorExt(tif->tif_clientdata, where,
+                        "Invalid argument to _TIFFMultiplySSize() in %s", where);
+        }
+        return 0;
+    }
+
+    if( first > TIFF_TMSIZE_T_MAX / second )
+    {
+        if( tif != NULL && where != NULL )
+        {
+            TIFFErrorExt(tif->tif_clientdata, where,
+                        "Integer overflow in %s", where);
+        }
+        return 0;
+    }
+    return first * second;
+}
+
+tmsize_t _TIFFCastUInt64ToSSize(TIFF* tif, uint64 val, const char* module)
+{
+    if( val > (uint64)TIFF_TMSIZE_T_MAX )
+    {
+        if( tif != NULL && module != NULL )
+        {
+            TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
+        }
+        return 0;
+    }
+    return (tmsize_t)val;
 }
 
 void*
@@ -64,13 +97,14 @@
 		  tmsize_t nmemb, tmsize_t elem_size, const char* what)
 {
 	void* cp = NULL;
-	tmsize_t bytes = nmemb * elem_size;
-
+        tmsize_t count = _TIFFMultiplySSize(tif, nmemb, elem_size, NULL);
 	/*
-	 * XXX: Check for integer overflow.
+	 * Check for integer overflow.
 	 */
-	if (nmemb > 0 && elem_size > 0 && !_TIFFIfMultiplicationOverflow(nmemb, elem_size))
-		cp = _TIFFrealloc(buffer, bytes);
+	if (count != 0)
+	{
+		cp = _TIFFrealloc(buffer, count);
+	}
 
 	if (cp == NULL) {
 		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
@@ -359,6 +393,22 @@
 	}
 }
 
+float _TIFFClampDoubleToFloat( double val )
+{
+    if( val > FLT_MAX )
+        return FLT_MAX;
+    if( val < -FLT_MAX )
+        return -FLT_MAX;
+    return (float)val;
+}
+
+int _TIFFSeekOK(TIFF* tif, toff_t off)
+{
+    /* Huge offsets, especially -1 / UINT64_MAX, can cause issues */
+    /* See http://bugzilla.maptools.org/show_bug.cgi?id=2726 */
+    return off <= (~(uint64)0)/2 && TIFFSeekFile(tif,off,SEEK_SET)==off;
+}
+
 /* vim: set ts=8 sts=8 sw=8 noet: */
 /*
  * Local Variables:
diff --git a/third_party/libtiff/tif_close.c b/third_party/libtiff/tif_close.c
index a0cb661..e4228df 100644
--- a/third_party/libtiff/tif_close.c
+++ b/third_party/libtiff/tif_close.c
@@ -1,5 +1,3 @@
-/* $Id: tif_close.c,v 1.21 2016-01-23 21:20:34 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_codec.c b/third_party/libtiff/tif_codec.c
index 7cb46f6..b6c04f0 100644
--- a/third_party/libtiff/tif_codec.c
+++ b/third_party/libtiff/tif_codec.c
@@ -1,5 +1,3 @@
-/* $Id: tif_codec.c,v 1.17 2015-08-19 02:31:04 bfriesen Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -72,6 +70,12 @@
 #ifndef LZMA_SUPPORT
 #define TIFFInitLZMA NotConfigured
 #endif
+#ifndef ZSTD_SUPPORT
+#define TIFFInitZSTD NotConfigured
+#endif
+#ifndef WEBP_SUPPORT
+#define TIFFInitWebP NotConfigured
+#endif
 
 /*
  * Compression schemes statically built into the library.
@@ -99,6 +103,8 @@
     { "SGILog",		COMPRESSION_SGILOG,	TIFFInitSGILog },
     { "SGILog24",	COMPRESSION_SGILOG24,	TIFFInitSGILog },
     { "LZMA",		COMPRESSION_LZMA,	TIFFInitLZMA },
+    { "ZSTD",		COMPRESSION_ZSTD,	TIFFInitZSTD },
+    { "WEBP",		COMPRESSION_WEBP,	TIFFInitWebP },
     { NULL,             0,                      NULL }
 };
 
diff --git a/third_party/libtiff/tif_color.c b/third_party/libtiff/tif_color.c
index 8b8418c..8fae40e 100644
--- a/third_party/libtiff/tif_color.c
+++ b/third_party/libtiff/tif_color.c
@@ -1,5 +1,3 @@
-/* $Id: tif_color.c,v 1.23 2017-05-13 18:17:34 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -168,7 +166,7 @@
 }
 
 /* 
- * Convert color value from the YCbCr space to CIE XYZ.
+ * Convert color value from the YCbCr space to RGB.
  * The colorspace conversion algorithm comes from the IJG v5a code;
  * see below for more information on how it works.
  */
@@ -275,10 +273,10 @@
       for (i = 0, x = -128; i < 256; i++, x++) {
 	    int32 Cr = (int32)CLAMPw(Code2V(x, refBlackWhite[4] - 128.0F,
 			    refBlackWhite[5] - 128.0F, 127),
-                            -128.0F * 64, 128.0F * 64);
+                            -128.0F * 32, 128.0F * 32);
 	    int32 Cb = (int32)CLAMPw(Code2V(x, refBlackWhite[2] - 128.0F,
 			    refBlackWhite[3] - 128.0F, 127),
-                            -128.0F * 64, 128.0F * 64);
+                            -128.0F * 32, 128.0F * 32);
 
 	    ycbcr->Cr_r_tab[i] = (int32)((D1*Cr + ONE_HALF)>>SHIFT);
 	    ycbcr->Cb_b_tab[i] = (int32)((D3*Cb + ONE_HALF)>>SHIFT);
@@ -286,7 +284,7 @@
 	    ycbcr->Cb_g_tab[i] = D4*Cb + ONE_HALF;
 	    ycbcr->Y_tab[i] =
 		    (int32)CLAMPw(Code2V(x + 128, refBlackWhite[0], refBlackWhite[1], 255),
-                                  -128.0F * 64, 128.0F * 64);
+                                  -128.0F * 32, 128.0F * 32);
       }
     }
 
diff --git a/third_party/libtiff/tif_compress.c b/third_party/libtiff/tif_compress.c
index b571d19..8130ef0 100644
--- a/third_party/libtiff/tif_compress.c
+++ b/third_party/libtiff/tif_compress.c
@@ -1,5 +1,3 @@
-/* $Id: tif_compress.c,v 1.25 2016-10-25 20:04:22 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_dir.c b/third_party/libtiff/tif_dir.c
index a883949..1e0a76c 100644
--- a/third_party/libtiff/tif_dir.c
+++ b/third_party/libtiff/tif_dir.c
@@ -1,5 +1,3 @@
-/* $Id: tif_dir.c,v 1.130 2017-05-17 21:54:05 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -31,7 +29,6 @@
  * (and also some miscellaneous stuff)
  */
 #include "tiffiop.h"
-#include <float.h>
 
 /*
  * These are used in the backwards compatibility code...
@@ -49,8 +46,8 @@
 		*vpp = 0;
 	}
 	if (vp) {
-		tmsize_t bytes = (tmsize_t)(nmemb * elem_size);
-		if (elem_size && bytes / elem_size == nmemb)
+		tmsize_t bytes = _TIFFMultiplySSize(NULL, nmemb, elem_size, NULL);
+		if (bytes)
 			*vpp = (void*) _TIFFmalloc(bytes);
 		if (*vpp)
 			_TIFFmemcpy(*vpp, vp, bytes);
@@ -90,13 +87,15 @@
  * Install extra samples information.
  */
 static int
-setExtraSamples(TIFFDirectory* td, va_list ap, uint32* v)
+setExtraSamples(TIFF* tif, va_list ap, uint32* v)
 {
 /* XXX: Unassociated alpha data == 999 is a known Corel Draw bug, see below */
 #define EXTRASAMPLE_COREL_UNASSALPHA 999 
 
 	uint16* va;
 	uint32 i;
+        TIFFDirectory* td = &tif->tif_dir;
+        static const char module[] = "setExtraSamples";
 
 	*v = (uint16) va_arg(ap, uint16_vap);
 	if ((uint16) *v > td->td_samplesperpixel)
@@ -118,6 +117,18 @@
 				return 0;
 		}
 	}
+
+        if ( td->td_transferfunction[0] != NULL && (td->td_samplesperpixel - *v > 1) &&
+                !(td->td_samplesperpixel - td->td_extrasamples > 1))
+        {
+                TIFFWarningExt(tif->tif_clientdata,module,
+                    "ExtraSamples tag value is changing, "
+                    "but TransferFunction was read with a different value. Cancelling it");
+                TIFFClrFieldBit(tif,FIELD_TRANSFERFUNCTION);
+                _TIFFfree(td->td_transferfunction[0]);
+                td->td_transferfunction[0] = NULL;
+        }
+
 	td->td_extrasamples = (uint16) *v;
 	_TIFFsetShortArray(&td->td_sampleinfo, va, td->td_extrasamples);
 	return 1;
@@ -155,15 +166,6 @@
 	return (0);
 }
 
-static float TIFFClampDoubleToFloat( double val )
-{
-    if( val > FLT_MAX )
-        return FLT_MAX;
-    if( val < -FLT_MAX )
-        return -FLT_MAX;
-    return (float)val;
-}
-
 static int
 _TIFFVSetField(TIFF* tif, uint32 tag, va_list ap)
 {
@@ -287,6 +289,18 @@
                 _TIFFfree(td->td_smaxsamplevalue);
                 td->td_smaxsamplevalue = NULL;
             }
+            /* Test if 3 transfer functions instead of just one are now needed
+               See http://bugzilla.maptools.org/show_bug.cgi?id=2820 */
+            if( td->td_transferfunction[0] != NULL && (v - td->td_extrasamples > 1) &&
+                !(td->td_samplesperpixel - td->td_extrasamples > 1))
+            {
+                    TIFFWarningExt(tif->tif_clientdata,module,
+                        "SamplesPerPixel tag value is changing, "
+                        "but TransferFunction was read with a different value. Cancelling it");
+                    TIFFClrFieldBit(tif,FIELD_TRANSFERFUNCTION);
+                    _TIFFfree(td->td_transferfunction[0]);
+                    td->td_transferfunction[0] = NULL;
+            }
         }
 		td->td_samplesperpixel = (uint16) v;
 		break;
@@ -322,13 +336,13 @@
         dblval = va_arg(ap, double);
         if( dblval < 0 )
             goto badvaluedouble;
-		td->td_xresolution = TIFFClampDoubleToFloat( dblval );
+		td->td_xresolution = _TIFFClampDoubleToFloat( dblval );
 		break;
 	case TIFFTAG_YRESOLUTION:
         dblval = va_arg(ap, double);
         if( dblval < 0 )
             goto badvaluedouble;
-		td->td_yresolution = TIFFClampDoubleToFloat( dblval );
+		td->td_yresolution = _TIFFClampDoubleToFloat( dblval );
 		break;
 	case TIFFTAG_PLANARCONFIG:
 		v = (uint16) va_arg(ap, uint16_vap);
@@ -337,10 +351,10 @@
 		td->td_planarconfig = (uint16) v;
 		break;
 	case TIFFTAG_XPOSITION:
-		td->td_xposition = TIFFClampDoubleToFloat( va_arg(ap, double) );
+		td->td_xposition = _TIFFClampDoubleToFloat( va_arg(ap, double) );
 		break;
 	case TIFFTAG_YPOSITION:
-		td->td_yposition = TIFFClampDoubleToFloat( va_arg(ap, double) );
+		td->td_yposition = _TIFFClampDoubleToFloat( va_arg(ap, double) );
 		break;
 	case TIFFTAG_RESOLUTIONUNIT:
 		v = (uint16) va_arg(ap, uint16_vap);
@@ -363,7 +377,7 @@
 		_TIFFsetShortArray(&td->td_colormap[2], va_arg(ap, uint16*), v32);
 		break;
 	case TIFFTAG_EXTRASAMPLES:
-		if (!setExtraSamples(td, ap, &v))
+		if (!setExtraSamples(tif, ap, &v))
 			goto badvalue;
 		break;
 	case TIFFTAG_MATTEING:
@@ -686,7 +700,7 @@
 				case TIFF_SRATIONAL:
 				case TIFF_FLOAT:
 					{
-						float v2 = TIFFClampDoubleToFloat(va_arg(ap, double));
+						float v2 = _TIFFClampDoubleToFloat(va_arg(ap, double));
 						_TIFFmemcpy(val, &v2, tv_size);
 					}
 					break;
@@ -863,15 +877,27 @@
 	const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY);
 	if( fip == NULL ) /* cannot happen since TIFFGetField() already checks it */
 	    return 0;
+
+	/*
+	 * We want to force the custom code to be used for custom
+	 * fields even if the tag happens to match a well known 
+	 * one - important for reinterpreted handling of standard
+	 * tag values in custom directories (i.e. EXIF) 
+	 */
+	if (fip->field_bit == FIELD_CUSTOM) {
+		standard_tag = 0;
+	}
 	
-        if( tag == TIFFTAG_NUMBEROFINKS )
+        if( standard_tag == TIFFTAG_NUMBEROFINKS )
         {
             int i;
             for (i = 0; i < td->td_customValueCount; i++) {
                 uint16 val;
                 TIFFTagValue *tv = td->td_customValues + i;
-                if (tv->info->field_tag != tag)
+                if (tv->info->field_tag != standard_tag)
                     continue;
+                if( tv->value == NULL )
+                    return 0;
                 val = *(uint16 *)tv->value;
                 /* Truncate to SamplesPerPixel, since the */
                 /* setting code for INKNAMES assume that there are SamplesPerPixel */
@@ -890,16 +916,6 @@
             return 0;
         }
 
-	/*
-	 * We want to force the custom code to be used for custom
-	 * fields even if the tag happens to match a well known 
-	 * one - important for reinterpreted handling of standard
-	 * tag values in custom directories (i.e. EXIF) 
-	 */
-	if (fip->field_bit == FIELD_CUSTOM) {
-		standard_tag = 0;
-	}
-
 	switch (standard_tag) {
 		case TIFFTAG_SUBFILETYPE:
 			*va_arg(ap, uint32*) = td->td_subfiletype;
@@ -1002,12 +1018,12 @@
 		case TIFFTAG_STRIPOFFSETS:
 		case TIFFTAG_TILEOFFSETS:
 			_TIFFFillStriles( tif );
-			*va_arg(ap, uint64**) = td->td_stripoffset;
+			*va_arg(ap, uint64**) = td->td_stripoffset_p;
 			break;
 		case TIFFTAG_STRIPBYTECOUNTS:
 		case TIFFTAG_TILEBYTECOUNTS:
 			_TIFFFillStriles( tif );
-			*va_arg(ap, uint64**) = td->td_stripbytecount;
+			*va_arg(ap, uint64**) = td->td_stripbytecount_p;
 			break;
 		case TIFFTAG_MATTEING:
 			*va_arg(ap, uint16*) =
@@ -1065,6 +1081,9 @@
 			if (td->td_samplesperpixel - td->td_extrasamples > 1) {
 				*va_arg(ap, uint16**) = td->td_transferfunction[1];
 				*va_arg(ap, uint16**) = td->td_transferfunction[2];
+			} else {
+				*va_arg(ap, uint16**) = NULL;
+				*va_arg(ap, uint16**) = NULL;
 			}
 			break;
 		case TIFFTAG_REFERENCEBLACKWHITE:
@@ -1263,8 +1282,9 @@
 	CleanupField(td_transferfunction[0]);
 	CleanupField(td_transferfunction[1]);
 	CleanupField(td_transferfunction[2]);
-	CleanupField(td_stripoffset);
-	CleanupField(td_stripbytecount);
+	CleanupField(td_stripoffset_p);
+	CleanupField(td_stripbytecount_p);
+        td->td_stripoffsetbyteallocsize = 0;
 	TIFFClrFieldBit(tif, FIELD_YCBCRSUBSAMPLING);
 	TIFFClrFieldBit(tif, FIELD_YCBCRPOSITIONING);
 
@@ -1277,10 +1297,8 @@
 	td->td_customValueCount = 0;
 	CleanupField(td_customValues);
 
-#if defined(DEFER_STRILE_LOAD)
         _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
         _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
-#endif        
 }
 #undef CleanupField
 
@@ -1368,7 +1386,9 @@
 	td->td_tilewidth = 0;
 	td->td_tilelength = 0;
 	td->td_tiledepth = 1;
+#ifdef STRIPBYTECOUNTSORTED_UNUSED
 	td->td_stripbytecountsorted = 1; /* Our own arrays always sorted. */  
+#endif
 	td->td_resolutionunit = RESUNIT_INCH;
 	td->td_sampleformat = SAMPLEFORMAT_UINT;
 	td->td_imagedepth = 1;
diff --git a/third_party/libtiff/tif_dir.h b/third_party/libtiff/tif_dir.h
index 6af5f3d..e7f0667 100644
--- a/third_party/libtiff/tif_dir.h
+++ b/third_party/libtiff/tif_dir.h
@@ -1,5 +1,3 @@
-/* $Id: tif_dir.h,v 1.54 2011-02-18 20:53:05 fwarmerdam Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -26,6 +24,10 @@
 
 #ifndef _TIFFDIR_
 #define	_TIFFDIR_
+
+#include "tiff.h"
+#include "tiffio.h"
+
 /*
  * ``Library-private'' Directory-related Definitions.
  */
@@ -56,6 +58,7 @@
 		uint32 toff_long;
 		uint64 toff_long8;
 	} tdir_offset;		/* either offset or the data itself if fits */
+	uint8  tdir_ignore;	/* flag status to ignore tag when parsing tags in tif_dirread.c */
 } TIFFDirEntry;
 
 /*
@@ -95,13 +98,14 @@
 	 * number of striles */
 	uint32  td_stripsperimage;  
 	uint32  td_nstrips;              /* size of offset & bytecount arrays */
-	uint64* td_stripoffset;
-	uint64* td_stripbytecount;
+	uint64* td_stripoffset_p;        /* should be accessed with TIFFGetStrileOffset */
+	uint64* td_stripbytecount_p;     /* should be accessed with TIFFGetStrileByteCount */
+        uint32  td_stripoffsetbyteallocsize; /* number of elements currently allocated for td_stripoffset/td_stripbytecount. Only used if TIFF_LAZYSTRILELOAD is set */
+#ifdef STRIPBYTECOUNTSORTED_UNUSED
 	int     td_stripbytecountsorted; /* is the bytecount array sorted ascending? */
-#if defined(DEFER_STRILE_LOAD)
+#endif
         TIFFDirEntry td_stripoffset_entry;    /* for deferred loading */
         TIFFDirEntry td_stripbytecount_entry; /* for deferred loading */
-#endif
 	uint16  td_nsubifd;
 	uint64* td_subifd;
 	/* YCbCr parameters */
@@ -116,6 +120,8 @@
 
 	int     td_customValueCount;
         TIFFTagValue *td_customValues;
+
+        unsigned char td_deferstrilearraywriting; /* see TIFFDeferStrileArrayWriting() */
 } TIFFDirectory;
 
 /*
@@ -291,6 +297,7 @@
 extern int _TIFFMergeFields(TIFF*, const TIFFField[], uint32);
 extern const TIFFField* _TIFFFindOrRegisterField(TIFF *, uint32, TIFFDataType);
 extern  TIFFField* _TIFFCreateAnonField(TIFF *, uint32, TIFFDataType);
+extern int _TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag);
 
 #if defined(__cplusplus)
 }
diff --git a/third_party/libtiff/tif_dirinfo.c b/third_party/libtiff/tif_dirinfo.c
index 23ad002..e1f6b23 100644
--- a/third_party/libtiff/tif_dirinfo.c
+++ b/third_party/libtiff/tif_dirinfo.c
@@ -1,5 +1,3 @@
-/* $Id: tif_dirinfo.c,v 1.126 2016-11-18 02:52:13 bfriesen Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -956,6 +954,122 @@
 	return 0;
 }
 
+int
+_TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag)
+{
+	/* Filter out non-codec specific tags */
+	switch (tag) {
+	    /* Shared tags */
+	    case TIFFTAG_PREDICTOR:
+	    /* JPEG tags */
+	    case TIFFTAG_JPEGTABLES:
+	    /* OJPEG tags */
+	    case TIFFTAG_JPEGIFOFFSET:
+	    case TIFFTAG_JPEGIFBYTECOUNT:
+	    case TIFFTAG_JPEGQTABLES:
+	    case TIFFTAG_JPEGDCTABLES:
+	    case TIFFTAG_JPEGACTABLES:
+	    case TIFFTAG_JPEGPROC:
+	    case TIFFTAG_JPEGRESTARTINTERVAL:
+	    /* CCITT* */
+	    case TIFFTAG_BADFAXLINES:
+	    case TIFFTAG_CLEANFAXDATA:
+	    case TIFFTAG_CONSECUTIVEBADFAXLINES:
+	    case TIFFTAG_GROUP3OPTIONS:
+	    case TIFFTAG_GROUP4OPTIONS:
+	    /* LERC */
+	    case TIFFTAG_LERC_PARAMETERS:
+		break;
+	    default:
+		return 1;
+	}
+	/* Check if codec specific tags are allowed for the current
+	 * compression scheme (codec) */
+	switch (tif->tif_dir.td_compression) {
+	    case COMPRESSION_LZW:
+		if (tag == TIFFTAG_PREDICTOR)
+		    return 1;
+		break;
+	    case COMPRESSION_PACKBITS:
+		/* No codec-specific tags */
+		break;
+	    case COMPRESSION_THUNDERSCAN:
+		/* No codec-specific tags */
+		break;
+	    case COMPRESSION_NEXT:
+		/* No codec-specific tags */
+		break;
+	    case COMPRESSION_JPEG:
+		if (tag == TIFFTAG_JPEGTABLES)
+		    return 1;
+		break;
+	    case COMPRESSION_OJPEG:
+		switch (tag) {
+		    case TIFFTAG_JPEGIFOFFSET:
+		    case TIFFTAG_JPEGIFBYTECOUNT:
+		    case TIFFTAG_JPEGQTABLES:
+		    case TIFFTAG_JPEGDCTABLES:
+		    case TIFFTAG_JPEGACTABLES:
+		    case TIFFTAG_JPEGPROC:
+		    case TIFFTAG_JPEGRESTARTINTERVAL:
+			return 1;
+		}
+		break;
+	    case COMPRESSION_CCITTRLE:
+	    case COMPRESSION_CCITTRLEW:
+	    case COMPRESSION_CCITTFAX3:
+	    case COMPRESSION_CCITTFAX4:
+		switch (tag) {
+		    case TIFFTAG_BADFAXLINES:
+		    case TIFFTAG_CLEANFAXDATA:
+		    case TIFFTAG_CONSECUTIVEBADFAXLINES:
+			return 1;
+		    case TIFFTAG_GROUP3OPTIONS:
+			if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX3)
+			    return 1;
+			break;
+		    case TIFFTAG_GROUP4OPTIONS:
+			if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4)
+			    return 1;
+			break;
+		}
+		break;
+	    case COMPRESSION_JBIG:
+		/* No codec-specific tags */
+		break;
+	    case COMPRESSION_DEFLATE:
+	    case COMPRESSION_ADOBE_DEFLATE:
+		if (tag == TIFFTAG_PREDICTOR)
+		    return 1;
+		break;
+	   case COMPRESSION_PIXARLOG:
+		if (tag == TIFFTAG_PREDICTOR)
+		    return 1;
+		break;
+	    case COMPRESSION_SGILOG:
+	    case COMPRESSION_SGILOG24:
+		/* No codec-specific tags */
+		break;
+	    case COMPRESSION_LZMA:
+		if (tag == TIFFTAG_PREDICTOR)
+		    return 1;
+		break;
+	    case COMPRESSION_ZSTD:
+		if (tag == TIFFTAG_PREDICTOR)
+		    return 1;
+		break;
+	    case COMPRESSION_LERC:
+		if (tag == TIFFTAG_LERC_PARAMETERS)
+		    return 1;
+		break;
+		  case COMPRESSION_WEBP:
+		if (tag == TIFFTAG_PREDICTOR)
+				return 1;
+		break;
+	}
+	return 0;
+}
+
 /* vim: set ts=8 sts=8 sw=8 noet: */
 
 /*
diff --git a/third_party/libtiff/tif_dirread.c b/third_party/libtiff/tif_dirread.c
index ab938ea..ceb166c 100644
--- a/third_party/libtiff/tif_dirread.c
+++ b/third_party/libtiff/tif_dirread.c
@@ -1,5 +1,3 @@
-/* $Id: tif_dirread.c,v 1.208 2017-04-27 15:46:22 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -31,9 +29,6 @@
  */
 
 /* Suggested pending improvements:
- * - add a field 'ignore' to the TIFFDirEntry structure, to flag status,
- *   eliminating current use of the IGNORE value, and therefore eliminating
- *   current irrational behaviour on tags with tag id code 0
  * - add a field 'field_info' to the TIFFDirEntry structure, and set that with
  *   the pointer to the appropriate TIFFField structure early on in
  *   TIFFReadDirectory, so as to eliminate current possibly repetitive lookup.
@@ -42,10 +37,15 @@
 #include "tiffiop.h"
 #include <float.h>
 #include <limits.h>
+#include <stdlib.h>
 
-#define IGNORE 0          /* tag placeholder used below */
 #define FAILED_FII    ((uint32) -1)
 
+/*
+ * Largest 64-bit signed integer value.
+ */
+#define TIFF_INT64_MAX ((int64)(TIFF_UINT64_MAX >> 1))
+
 #ifdef HAVE_IEEEFP
 # define TIFFCvtIEEEFloatToNative(tif, n, fp)
 # define TIFFCvtIEEEDoubleToNative(tif, n, dp)
@@ -166,7 +166,9 @@
 static int TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, uint32 nstrips, uint64** lpp);
 static int TIFFFetchSubjectDistance(TIFF*, TIFFDirEntry*);
 static void ChopUpSingleUncompressedStrip(TIFF*);
+static void TryChopUpUncompressedBigTiff(TIFF*);
 static uint64 TIFFReadUInt64(const uint8 *value);
+static int _TIFFGetMaxColorChannels(uint16 photometric);
 
 static int _TIFFFillStrilesInternal( TIFF *tif, int loadStripByteCount );
 
@@ -206,6 +208,7 @@
 	switch (direntry->tdir_type)
 	{
 		case TIFF_BYTE:
+		case TIFF_UNDEFINED:	/* Support to read TIFF_UNDEFINED with field_readcount==1 */
 			TIFFReadDirEntryCheckedByte(tif,direntry,value);
 			return(TIFFReadDirEntryErrOk);
 		case TIFF_SBYTE:
@@ -637,6 +640,8 @@
 				err=TIFFReadDirEntryCheckedDouble(tif,direntry,&m);
 				if (err!=TIFFReadDirEntryErrOk)
 					return(err);
+				if ((m > FLT_MAX) || (m < FLT_MIN))
+					return(TIFFReadDirEntryErrRange);
 				*value=(float)m;
 				return(TIFFReadDirEntryErrOk);
 			}
@@ -766,13 +771,80 @@
 	}
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryArray(TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize, void** value)
+
+#define INITIAL_THRESHOLD (1024 * 1024)
+#define THRESHOLD_MULTIPLIER 10
+#define MAX_THRESHOLD (THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * INITIAL_THRESHOLD)
+
+static enum TIFFReadDirEntryErr TIFFReadDirEntryDataAndRealloc(
+                    TIFF* tif, uint64 offset, tmsize_t size, void** pdest)
+{
+#if SIZEOF_SIZE_T == 8
+        tmsize_t threshold = INITIAL_THRESHOLD;
+#endif
+        tmsize_t already_read = 0;
+
+        assert( !isMapped(tif) );
+
+        if (!SeekOK(tif,offset))
+                return(TIFFReadDirEntryErrIo);
+
+        /* On 64 bit processes, read first a maximum of 1 MB, then 10 MB, etc */
+        /* so as to avoid allocating too much memory in case the file is too */
+        /* short. We could ask for the file size, but this might be */
+        /* expensive with some I/O layers (think of reading a gzipped file) */
+        /* Restrict to 64 bit processes, so as to avoid reallocs() */
+        /* on 32 bit processes where virtual memory is scarce.  */
+        while( already_read < size )
+        {
+            void* new_dest;
+            tmsize_t bytes_read;
+            tmsize_t to_read = size - already_read;
+#if SIZEOF_SIZE_T == 8
+            if( to_read >= threshold && threshold < MAX_THRESHOLD )
+            {
+                to_read = threshold;
+                threshold *= THRESHOLD_MULTIPLIER;
+            }
+#endif
+
+            new_dest = (uint8*) _TIFFrealloc(
+                            *pdest, already_read + to_read);
+            if( new_dest == NULL )
+            {
+                TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+                            "Failed to allocate memory for %s "
+                            "(%ld elements of %ld bytes each)",
+                            "TIFFReadDirEntryArray",
+                             (long) 1, (long) (already_read + to_read));
+                return TIFFReadDirEntryErrAlloc;
+            }
+            *pdest = new_dest;
+
+            bytes_read = TIFFReadFile(tif,
+                (char*)*pdest + already_read, to_read);
+            already_read += bytes_read;
+            if (bytes_read != to_read) {
+                return TIFFReadDirEntryErrIo;
+            }
+        }
+        return TIFFReadDirEntryErrOk;
+}
+
+static enum TIFFReadDirEntryErr TIFFReadDirEntryArrayWithLimit(
+    TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize,
+    void** value, uint64 maxcount)
 {
 	int typesize;
 	uint32 datasize;
 	void* data;
+        uint64 target_count64;
 	typesize=TIFFDataWidth(direntry->tdir_type);
-	if ((direntry->tdir_count==0)||(typesize==0))
+
+        target_count64 = (direntry->tdir_count > maxcount) ?
+                maxcount : direntry->tdir_count;
+
+	if ((target_count64==0)||(typesize==0))
 	{
 		*value=0;
 		return(TIFFReadDirEntryErrOk);
@@ -784,17 +856,30 @@
          * in either the current data type or the dest data type.  This also
          * avoids problems with overflow of tmsize_t on 32bit systems.
          */
-	if ((uint64)(2147483647/typesize)<direntry->tdir_count)
+	if ((uint64)(2147483647/typesize)<target_count64)
 		return(TIFFReadDirEntryErrSizesan);
-	if ((uint64)(2147483647/desttypesize)<direntry->tdir_count)
+	if ((uint64)(2147483647/desttypesize)<target_count64)
 		return(TIFFReadDirEntryErrSizesan);
 
-	*count=(uint32)direntry->tdir_count;
+	*count=(uint32)target_count64;
 	datasize=(*count)*typesize;
 	assert((tmsize_t)datasize>0);
-	data=_TIFFCheckMalloc(tif, *count, typesize, "ReadDirEntryArray");
-	if (data==0)
-		return(TIFFReadDirEntryErrAlloc);
+
+	if( isMapped(tif) && datasize > (uint32)tif->tif_size )
+		return TIFFReadDirEntryErrIo;
+
+	if( !isMapped(tif) &&
+		(((tif->tif_flags&TIFF_BIGTIFF) && datasize > 8) ||
+		(!(tif->tif_flags&TIFF_BIGTIFF) && datasize > 4)) )
+	{
+		data = NULL;
+	}
+	else
+	{
+		data=_TIFFCheckMalloc(tif, *count, typesize, "ReadDirEntryArray");
+		if (data==0)
+			return(TIFFReadDirEntryErrAlloc);
+	}
 	if (!(tif->tif_flags&TIFF_BIGTIFF))
 	{
 		if (datasize<=4)
@@ -805,7 +890,10 @@
 			uint32 offset = direntry->tdir_offset.toff_long;
 			if (tif->tif_flags&TIFF_SWAB)
 				TIFFSwabLong(&offset);
-			err=TIFFReadDirEntryData(tif,(uint64)offset,(tmsize_t)datasize,data);
+			if( isMapped(tif) )
+				err=TIFFReadDirEntryData(tif,(uint64)offset,(tmsize_t)datasize,data);
+			else
+				err=TIFFReadDirEntryDataAndRealloc(tif,(uint64)offset,(tmsize_t)datasize,&data);
 			if (err!=TIFFReadDirEntryErrOk)
 			{
 				_TIFFfree(data);
@@ -823,7 +911,10 @@
 			uint64 offset = direntry->tdir_offset.toff_long8;
 			if (tif->tif_flags&TIFF_SWAB)
 				TIFFSwabLong8(&offset);
-			err=TIFFReadDirEntryData(tif,offset,(tmsize_t)datasize,data);
+			if( isMapped(tif) )
+				err=TIFFReadDirEntryData(tif,(uint64)offset,(tmsize_t)datasize,data);
+			else
+				err=TIFFReadDirEntryDataAndRealloc(tif,(uint64)offset,(tmsize_t)datasize,&data);
 			if (err!=TIFFReadDirEntryErrOk)
 			{
 				_TIFFfree(data);
@@ -835,6 +926,12 @@
 	return(TIFFReadDirEntryErrOk);
 }
 
+static enum TIFFReadDirEntryErr TIFFReadDirEntryArray(TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize, void** value)
+{
+    return TIFFReadDirEntryArrayWithLimit(tif, direntry, count,
+                                          desttypesize, value, ~((uint64)0));
+}
+
 static enum TIFFReadDirEntryErr TIFFReadDirEntryByteArray(TIFF* tif, TIFFDirEntry* direntry, uint8** value)
 {
 	enum TIFFReadDirEntryErr err;
@@ -1864,7 +1961,8 @@
 	return(TIFFReadDirEntryErrOk);
 }
 
-static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value)
+static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8ArrayWithLimit(
+        TIFF* tif, TIFFDirEntry* direntry, uint64** value, uint64 maxcount)
 {
 	enum TIFFReadDirEntryErr err;
 	uint32 count;
@@ -1884,7 +1982,7 @@
 		default:
 			return(TIFFReadDirEntryErrType);
 	}
-	err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata);
+	err=TIFFReadDirEntryArrayWithLimit(tif,direntry,&count,8,&origdata,maxcount);
 	if ((err!=TIFFReadDirEntryErrOk)||(origdata==0))
 	{
 		*value=0;
@@ -2030,6 +2128,11 @@
 	return(TIFFReadDirEntryErrOk);
 }
 
+static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value)
+{
+    return TIFFReadDirEntryLong8ArrayWithLimit(tif, direntry, value, ~((uint64)0));
+}
+
 static enum TIFFReadDirEntryErr TIFFReadDirEntrySlong8Array(TIFF* tif, TIFFDirEntry* direntry, int64** value)
 {
 	enum TIFFReadDirEntryErr err;
@@ -2732,7 +2835,7 @@
 	if (direntry->tdir_count<(uint64)tif->tif_dir.td_samplesperpixel)
 		return(TIFFReadDirEntryErrCount);
 	err=TIFFReadDirEntryShortArray(tif,direntry,&m);
-	if (err!=TIFFReadDirEntryErrOk)
+	if (err!=TIFFReadDirEntryErrOk || m == NULL)
 		return(err);
 	na=m;
 	nb=tif->tif_dir.td_samplesperpixel;
@@ -3188,11 +3291,6 @@
 		return(TIFFReadDirEntryErrOk);
 }
 
-/*
- * Largest 32-bit unsigned integer value.
- */
-#define TIFF_UINT32_MAX 0xFFFFFFFFU
-
 static enum TIFFReadDirEntryErr
 TIFFReadDirEntryCheckRangeLongLong8(uint64 value)
 {
@@ -3211,8 +3309,6 @@
 		return(TIFFReadDirEntryErrOk);
 }
 
-#undef TIFF_UINT32_MAX
-
 static enum TIFFReadDirEntryErr
 TIFFReadDirEntryCheckRangeSlongLong(uint32 value)
 {
@@ -3278,11 +3374,6 @@
 		return(TIFFReadDirEntryErrOk);
 }
 
-/*
- * Largest 64-bit signed integer value.
- */
-#define TIFF_INT64_MAX ((int64)(((uint64) ~0) >> 1))
-
 static enum TIFFReadDirEntryErr
 TIFFReadDirEntryCheckRangeSlong8Long8(uint64 value)
 {
@@ -3292,8 +3383,6 @@
 		return(TIFFReadDirEntryErrOk);
 }
 
-#undef TIFF_INT64_MAX
-
 static enum TIFFReadDirEntryErr
 TIFFReadDirEntryData(TIFF* tif, uint64 offset, tmsize_t size, void* dest)
 {
@@ -3306,13 +3395,13 @@
 	} else {
 		size_t ma,mb;
 		ma=(size_t)offset;
+                if( (uint64)ma!=offset ||
+                    ma > (~(size_t)0) - (size_t)size )
+                {
+                    return TIFFReadDirEntryErrIo;
+                }
 		mb=ma+size;
-		if (((uint64)ma!=offset)
-		    || (mb < ma)
-		    || (mb - ma != (size_t) size)
-		    || (mb < (size_t)size)
-		    || (mb > (size_t)tif->tif_size)
-		    )
+		if (mb > (size_t)tif->tif_size)
 			return(TIFFReadDirEntryErrIo);
 		_TIFFmemcpy(dest,tif->tif_base+ma,size);
 	}
@@ -3407,6 +3496,78 @@
 }
 
 /*
+ * Return the maximum number of color channels specified for a given photometric
+ * type. 0 is returned if photometric type isn't supported or no default value
+ * is defined by the specification.
+ */
+static int _TIFFGetMaxColorChannels( uint16 photometric )
+{
+    switch (photometric) {
+	case PHOTOMETRIC_PALETTE:
+	case PHOTOMETRIC_MINISWHITE:
+	case PHOTOMETRIC_MINISBLACK:
+            return 1;
+	case PHOTOMETRIC_YCBCR:
+	case PHOTOMETRIC_RGB:
+	case PHOTOMETRIC_CIELAB:
+	case PHOTOMETRIC_LOGLUV:
+	case PHOTOMETRIC_ITULAB:
+	case PHOTOMETRIC_ICCLAB:
+            return 3;
+	case PHOTOMETRIC_SEPARATED:
+	case PHOTOMETRIC_MASK:
+            return 4;
+	case PHOTOMETRIC_LOGL:
+	case PHOTOMETRIC_CFA:
+	default:
+            return 0;
+    }
+}
+
+static int ByteCountLooksBad(TIFF* tif)
+{
+    /*
+        * Assume we have wrong StripByteCount value (in case
+        * of single strip) in following cases:
+        *   - it is equal to zero along with StripOffset;
+        *   - it is larger than file itself (in case of uncompressed
+        *     image);
+        *   - it is smaller than the size of the bytes per row
+        *     multiplied on the number of rows.  The last case should
+        *     not be checked in the case of writing new image,
+        *     because we may do not know the exact strip size
+        *     until the whole image will be written and directory
+        *     dumped out.
+        */
+    uint64 bytecount = TIFFGetStrileByteCount(tif, 0);
+    uint64 offset = TIFFGetStrileOffset(tif, 0);
+    uint64 filesize;
+
+    if( offset == 0 )
+        return 0;
+    if (bytecount == 0)
+        return 1;
+    if ( tif->tif_dir.td_compression != COMPRESSION_NONE )
+        return 0;
+    filesize = TIFFGetFileSize(tif);
+    if( offset <= filesize && bytecount > filesize - offset )
+        return 1;
+    if( tif->tif_mode == O_RDONLY )
+    {
+        uint64 scanlinesize = TIFFScanlineSize64(tif);
+        if( tif->tif_dir.td_imagelength > 0 &&
+            scanlinesize > TIFF_UINT64_MAX / tif->tif_dir.td_imagelength )
+        {
+            return 1;
+        }
+        if( bytecount < scanlinesize * tif->tif_dir.td_imagelength)
+            return 1;
+    }
+    return 0;
+}
+
+
+/*
  * Read the next TIFF directory from a file and convert it to the internal
  * format. We read directories sequentially.
  */
@@ -3422,6 +3583,7 @@
 	uint32 fii=FAILED_FII;
         toff_t nextdiroff;
     int bitspersample_read = FALSE;
+        int color_channels;
 
 	tif->tif_diroff=tif->tif_nextdiroff;
 	if (!TIFFCheckDirOffset(tif,tif->tif_nextdiroff))
@@ -3451,14 +3613,17 @@
 			uint16 nb;
 			for (na=ma+1, nb=mb+1; nb<dircount; na++, nb++)
 			{
-				if (ma->tdir_tag==na->tdir_tag)
-					na->tdir_tag=IGNORE;
+				if (ma->tdir_tag == na->tdir_tag) {
+					na->tdir_ignore = TRUE;
+				}
 			}
 		}
 	}
         
 	tif->tif_flags &= ~TIFF_BEENWRITING;    /* reset before new dir */
 	tif->tif_flags &= ~TIFF_BUF4WRITE;      /* reset before new dir */
+	tif->tif_flags &= ~TIFF_CHOPPEDUPARRAYS;
+
 	/* free any old stuff and reinit */
 	TIFFFreeDirectory(tif);
 	TIFFDefaultDirectory(tif);
@@ -3491,7 +3656,7 @@
 	{
 		if (!TIFFFetchNormalTag(tif,dp,0))
 			goto bad;
-		dp->tdir_tag=IGNORE;
+		dp->tdir_ignore = TRUE;
 	}
 	dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_COMPRESSION);
 	if (dp)
@@ -3514,7 +3679,7 @@
 		}
 		if (!TIFFSetField(tif,TIFFTAG_COMPRESSION,value))
 			goto bad;
-		dp->tdir_tag=IGNORE;
+		dp->tdir_ignore = TRUE;
 	}
 	else
 	{
@@ -3526,7 +3691,7 @@
 	 */
 	for (di=0, dp=dir; di<dircount; di++, dp++)
 	{
-		if (dp->tdir_tag!=IGNORE)
+		if (!dp->tdir_ignore)
 		{
 			TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
 			if (fii == FAILED_FII)
@@ -3534,8 +3699,8 @@
 				TIFFWarningExt(tif->tif_clientdata, module,
 				    "Unknown field with tag %d (0x%x) encountered",
 				    dp->tdir_tag,dp->tdir_tag);
-                                /* the following knowingly leaks the 
-                                   anonymous field structure */
+				/* the following knowingly leaks the 
+				   anonymous field structure */
 				if (!_TIFFMergeFields(tif,
 					_TIFFCreateAnonField(tif,
 						dp->tdir_tag,
@@ -3546,18 +3711,18 @@
 					    "Registering anonymous field with tag %d (0x%x) failed",
 					    dp->tdir_tag,
 					    dp->tdir_tag);
-					dp->tdir_tag=IGNORE;
+					dp->tdir_ignore = TRUE;
 				} else {
 					TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
 					assert(fii != FAILED_FII);
 				}
 			}
 		}
-		if (dp->tdir_tag!=IGNORE)
+		if (!dp->tdir_ignore)
 		{
 			fip=tif->tif_fields[fii];
 			if (fip->field_bit==FIELD_IGNORE)
-				dp->tdir_tag=IGNORE;
+				dp->tdir_ignore = TRUE;
 			else
 			{
 				switch (dp->tdir_tag)
@@ -3579,7 +3744,11 @@
 					case TIFFTAG_EXTRASAMPLES:
 						if (!TIFFFetchNormalTag(tif,dp,0))
 							goto bad;
-						dp->tdir_tag=IGNORE;
+						dp->tdir_ignore = TRUE;
+						break;
+					default:
+						if( !_TIFFCheckFieldIsValidForCodec(tif, dp->tdir_tag) )
+							dp->tdir_ignore = TRUE;
 						break;
 				}
 			}
@@ -3596,8 +3765,8 @@
 	if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG)&&
 	    (tif->tif_dir.td_planarconfig==PLANARCONFIG_SEPARATE))
 	{
-        if (!_TIFFFillStriles(tif))
-            goto bad;
+		if (!_TIFFFillStriles(tif))
+		    goto bad;
 		dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_STRIPOFFSETS);
 		if ((dp!=0)&&(dp->tdir_count==1))
 		{
@@ -3676,190 +3845,200 @@
 	 */
 	for (di=0, dp=dir; di<dircount; di++, dp++)
 	{
-		switch (dp->tdir_tag)
-		{
-			case IGNORE:
-				break;
-			case TIFFTAG_MINSAMPLEVALUE:
-			case TIFFTAG_MAXSAMPLEVALUE:
-			case TIFFTAG_BITSPERSAMPLE:
-			case TIFFTAG_DATATYPE:
-			case TIFFTAG_SAMPLEFORMAT:
-				/*
-				 * The MinSampleValue, MaxSampleValue, BitsPerSample
-				 * DataType and SampleFormat tags are supposed to be
-				 * written as one value/sample, but some vendors
-				 * incorrectly write one value only -- so we accept
-				 * that as well (yuck). Other vendors write correct
-				 * value for NumberOfSamples, but incorrect one for
-				 * BitsPerSample and friends, and we will read this
-				 * too.
-				 */
-				{
-					uint16 value;
-					enum TIFFReadDirEntryErr err;
-					err=TIFFReadDirEntryShort(tif,dp,&value);
-					if (err==TIFFReadDirEntryErrCount)
-						err=TIFFReadDirEntryPersampleShort(tif,dp,&value);
-					if (err!=TIFFReadDirEntryErrOk)
+		if (!dp->tdir_ignore) {
+			switch (dp->tdir_tag) 
+			{
+				case TIFFTAG_MINSAMPLEVALUE:
+				case TIFFTAG_MAXSAMPLEVALUE:
+				case TIFFTAG_BITSPERSAMPLE:
+				case TIFFTAG_DATATYPE:
+				case TIFFTAG_SAMPLEFORMAT:
+					/*
+					 * The MinSampleValue, MaxSampleValue, BitsPerSample
+					 * DataType and SampleFormat tags are supposed to be
+					 * written as one value/sample, but some vendors
+					 * incorrectly write one value only -- so we accept
+					 * that as well (yuck). Other vendors write correct
+					 * value for NumberOfSamples, but incorrect one for
+					 * BitsPerSample and friends, and we will read this
+					 * too.
+					 */
 					{
-						fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-						TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
-						goto bad;
-					}
-					if (!TIFFSetField(tif,dp->tdir_tag,value))
-						goto bad;
-                    if( dp->tdir_tag == TIFFTAG_BITSPERSAMPLE )
-                        bitspersample_read = TRUE;
-				}
-				break;
-			case TIFFTAG_SMINSAMPLEVALUE:
-			case TIFFTAG_SMAXSAMPLEVALUE:
-				{
-
-					double *data = NULL;
-					enum TIFFReadDirEntryErr err;
-					uint32 saved_flags;
-					int m;
-					if (dp->tdir_count != (uint64)tif->tif_dir.td_samplesperpixel)
-						err = TIFFReadDirEntryErrCount;
-					else
-						err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
-					if (err!=TIFFReadDirEntryErrOk)
-					{
-						fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-						TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
-						goto bad;
-					}
-					saved_flags = tif->tif_flags;
-					tif->tif_flags |= TIFF_PERSAMPLE;
-					m = TIFFSetField(tif,dp->tdir_tag,data);
-					tif->tif_flags = saved_flags;
-					_TIFFfree(data);
-					if (!m)
-						goto bad;
-				}
-				break;
-			case TIFFTAG_STRIPOFFSETS:
-			case TIFFTAG_TILEOFFSETS:
-#if defined(DEFER_STRILE_LOAD)
-                                _TIFFmemcpy( &(tif->tif_dir.td_stripoffset_entry),
-                                             dp, sizeof(TIFFDirEntry) );
-#else                          
-                                if( tif->tif_dir.td_stripoffset != NULL )
-                                {
-                                    TIFFErrorExt(tif->tif_clientdata, module,
-                                        "tif->tif_dir.td_stripoffset is "
-                                        "already allocated. Likely duplicated "
-                                        "StripOffsets/TileOffsets tag");
-                                    goto bad;
-                                }
-				if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripoffset))  
-					goto bad;
-#endif                                
-				break;
-			case TIFFTAG_STRIPBYTECOUNTS:
-			case TIFFTAG_TILEBYTECOUNTS:
-#if defined(DEFER_STRILE_LOAD)
-                                _TIFFmemcpy( &(tif->tif_dir.td_stripbytecount_entry),
-                                             dp, sizeof(TIFFDirEntry) );
-#else                          
-                                if( tif->tif_dir.td_stripbytecount != NULL )
-                                {
-                                    TIFFErrorExt(tif->tif_clientdata, module,
-                                        "tif->tif_dir.td_stripbytecount is "
-                                        "already allocated. Likely duplicated "
-                                        "StripByteCounts/TileByteCounts tag");
-                                    goto bad;
-                                }
-                                if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripbytecount))  
-					goto bad;
-#endif                                
-				break;
-			case TIFFTAG_COLORMAP:
-			case TIFFTAG_TRANSFERFUNCTION:
-				{
-					enum TIFFReadDirEntryErr err;
-					uint32 countpersample;
-					uint32 countrequired;
-					uint32 incrementpersample;
-					uint16* value=NULL;
-                    /* It would be dangerous to instantiate those tag values */
-                    /* since if td_bitspersample has not yet been read (due to */
-                    /* unordered tags), it could be read afterwards with a */
-                    /* values greater than the default one (1), which may cause */
-                    /* crashes in user code */
-                    if( !bitspersample_read )
-                    {
-                        fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-                        TIFFWarningExt(tif->tif_clientdata,module,
-                                       "Ignoring %s since BitsPerSample tag not found",
-                                       fip ? fip->field_name : "unknown tagname");
-                        continue;
-                    }
-					/* ColorMap or TransferFunction for high bit */
-					/* depths do not make much sense and could be */
-					/* used as a denial of service vector */
-					if (tif->tif_dir.td_bitspersample > 24)
-					{
-					    fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-					    TIFFWarningExt(tif->tif_clientdata,module,
-						"Ignoring %s because BitsPerSample=%d>24",
-						fip ? fip->field_name : "unknown tagname",
-						tif->tif_dir.td_bitspersample);
-					    continue;
-					}
-					countpersample=(1U<<tif->tif_dir.td_bitspersample);
-					if ((dp->tdir_tag==TIFFTAG_TRANSFERFUNCTION)&&(dp->tdir_count==(uint64)countpersample))
-					{
-						countrequired=countpersample;
-						incrementpersample=0;
-					}
-					else
-					{
-						countrequired=3*countpersample;
-						incrementpersample=countpersample;
-					}
-					if (dp->tdir_count!=(uint64)countrequired)
-						err=TIFFReadDirEntryErrCount;
-					else
-						err=TIFFReadDirEntryShortArray(tif,dp,&value);
-					if (err!=TIFFReadDirEntryErrOk)
-                    {
-						fip = TIFFFieldWithTag(tif,dp->tdir_tag);
-						TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",1);
-                    }
-					else
-					{
-						TIFFSetField(tif,dp->tdir_tag,value,value+incrementpersample,value+2*incrementpersample);
-						_TIFFfree(value);
-					}
-				}
-				break;
-/* BEGIN REV 4.0 COMPATIBILITY */
-			case TIFFTAG_OSUBFILETYPE:
-				{
-					uint16 valueo;
-					uint32 value;
-					if (TIFFReadDirEntryShort(tif,dp,&valueo)==TIFFReadDirEntryErrOk)
-					{
-						switch (valueo)
+						uint16 value;
+						enum TIFFReadDirEntryErr err;
+						err=TIFFReadDirEntryShort(tif,dp,&value);
+						if (err==TIFFReadDirEntryErrCount)
+							err=TIFFReadDirEntryPersampleShort(tif,dp,&value);
+						if (err!=TIFFReadDirEntryErrOk)
 						{
-							case OFILETYPE_REDUCEDIMAGE: value=FILETYPE_REDUCEDIMAGE; break;
-							case OFILETYPE_PAGE: value=FILETYPE_PAGE; break;
-							default: value=0; break;
+							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
+							TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
+							goto bad;
 						}
-						if (value!=0)
-							TIFFSetField(tif,TIFFTAG_SUBFILETYPE,value);
+						if (!TIFFSetField(tif,dp->tdir_tag,value))
+							goto bad;
+						if( dp->tdir_tag == TIFFTAG_BITSPERSAMPLE )
+						    bitspersample_read = TRUE;
 					}
-				}
-				break;
+					break;
+				case TIFFTAG_SMINSAMPLEVALUE:
+				case TIFFTAG_SMAXSAMPLEVALUE:
+					{
+
+						double *data = NULL;
+						enum TIFFReadDirEntryErr err;
+						uint32 saved_flags;
+						int m;
+						if (dp->tdir_count != (uint64)tif->tif_dir.td_samplesperpixel)
+							err = TIFFReadDirEntryErrCount;
+						else
+							err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
+						if (err!=TIFFReadDirEntryErrOk)
+						{
+							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
+							TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
+							goto bad;
+						}
+						saved_flags = tif->tif_flags;
+						tif->tif_flags |= TIFF_PERSAMPLE;
+						m = TIFFSetField(tif,dp->tdir_tag,data);
+						tif->tif_flags = saved_flags;
+						_TIFFfree(data);
+						if (!m)
+							goto bad;
+					}
+					break;
+				case TIFFTAG_STRIPOFFSETS:
+				case TIFFTAG_TILEOFFSETS:
+					_TIFFmemcpy( &(tif->tif_dir.td_stripoffset_entry),
+					   dp, sizeof(TIFFDirEntry) );
+					break;
+				case TIFFTAG_STRIPBYTECOUNTS:
+				case TIFFTAG_TILEBYTECOUNTS:
+					_TIFFmemcpy( &(tif->tif_dir.td_stripbytecount_entry),
+					   dp, sizeof(TIFFDirEntry) );
+					break;
+				case TIFFTAG_COLORMAP:
+				case TIFFTAG_TRANSFERFUNCTION:
+					{
+						enum TIFFReadDirEntryErr err;
+						uint32 countpersample;
+						uint32 countrequired;
+						uint32 incrementpersample;
+						uint16* value=NULL;
+						/* It would be dangerous to instantiate those tag values */
+						/* since if td_bitspersample has not yet been read (due to */
+						/* unordered tags), it could be read afterwards with a */
+						/* values greater than the default one (1), which may cause */
+						/* crashes in user code */
+						if( !bitspersample_read )
+						{
+							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
+							TIFFWarningExt(tif->tif_clientdata,module,
+								"Ignoring %s since BitsPerSample tag not found",
+								fip ? fip->field_name : "unknown tagname");
+							continue;
+						}
+						/* ColorMap or TransferFunction for high bit */
+						/* depths do not make much sense and could be */
+						/* used as a denial of service vector */
+						if (tif->tif_dir.td_bitspersample > 24)
+						{
+							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
+							TIFFWarningExt(tif->tif_clientdata,module,
+								"Ignoring %s because BitsPerSample=%d>24",
+								fip ? fip->field_name : "unknown tagname",
+								tif->tif_dir.td_bitspersample);
+							continue;
+						}
+						countpersample=(1U<<tif->tif_dir.td_bitspersample);
+						if ((dp->tdir_tag==TIFFTAG_TRANSFERFUNCTION)&&(dp->tdir_count==(uint64)countpersample))
+						{
+							countrequired=countpersample;
+							incrementpersample=0;
+						}
+						else
+						{
+							countrequired=3*countpersample;
+							incrementpersample=countpersample;
+						}
+						if (dp->tdir_count!=(uint64)countrequired)
+							err=TIFFReadDirEntryErrCount;
+						else
+							err=TIFFReadDirEntryShortArray(tif,dp,&value);
+						if (err!=TIFFReadDirEntryErrOk)
+						{
+							fip = TIFFFieldWithTag(tif,dp->tdir_tag);
+							TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",1);
+						}
+						else
+						{
+							TIFFSetField(tif,dp->tdir_tag,value,value+incrementpersample,value+2*incrementpersample);
+							_TIFFfree(value);
+						}
+					}
+					break;
+/* BEGIN REV 4.0 COMPATIBILITY */
+				case TIFFTAG_OSUBFILETYPE:
+					{
+						uint16 valueo;
+						uint32 value;
+						if (TIFFReadDirEntryShort(tif,dp,&valueo)==TIFFReadDirEntryErrOk)
+						{
+							switch (valueo)
+							{
+								case OFILETYPE_REDUCEDIMAGE: value=FILETYPE_REDUCEDIMAGE; break;
+								case OFILETYPE_PAGE: value=FILETYPE_PAGE; break;
+								default: value=0; break;
+							}
+							if (value!=0)
+								TIFFSetField(tif,TIFFTAG_SUBFILETYPE,value);
+						}
+					}
+					break;
 /* END REV 4.0 COMPATIBILITY */
-			default:
-				(void) TIFFFetchNormalTag(tif, dp, TRUE);
-				break;
-		}
-	}
+				default:
+					(void) TIFFFetchNormalTag(tif, dp, TRUE);
+					break;
+				}
+			} /* -- if (!dp->tdir_ignore) */
+		} /* -- for-loop -- */
+
+        if( tif->tif_mode == O_RDWR &&
+            tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
+            tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
+            tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
+            tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 )
+        {
+            /* Directory typically created with TIFFDeferStrileArrayWriting() */
+            TIFFSetupStrips(tif);
+        }
+        else if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) )
+        {
+            if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 )
+            {
+                if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripoffset_entry),
+                                         tif->tif_dir.td_nstrips,
+                                         &tif->tif_dir.td_stripoffset_p))
+                {
+                    goto bad;
+                }
+            }
+            if( tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 )
+            {
+                if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripbytecount_entry),
+                                         tif->tif_dir.td_nstrips,
+                                         &tif->tif_dir.td_stripbytecount_p))
+                {
+                    goto bad;
+                }
+            }
+        }
+
 	/*
 	 * OJPEG hack:
 	 * - If a) compression is OJPEG, and b) photometric tag is missing,
@@ -3929,6 +4108,37 @@
 			}
 		}
 	}
+
+	/*
+	 * Make sure all non-color channels are extrasamples.
+	 * If it's not the case, define them as such.
+	 */
+        color_channels = _TIFFGetMaxColorChannels(tif->tif_dir.td_photometric);
+        if (color_channels && tif->tif_dir.td_samplesperpixel - tif->tif_dir.td_extrasamples > color_channels) {
+                uint16 old_extrasamples;
+                uint16 *new_sampleinfo;
+
+                TIFFWarningExt(tif->tif_clientdata,module, "Sum of Photometric type-related "
+                    "color channels and ExtraSamples doesn't match SamplesPerPixel. "
+                    "Defining non-color channels as ExtraSamples.");
+
+                old_extrasamples = tif->tif_dir.td_extrasamples;
+                tif->tif_dir.td_extrasamples = (uint16) (tif->tif_dir.td_samplesperpixel - color_channels);
+
+                // sampleinfo should contain information relative to these new extra samples
+                new_sampleinfo = (uint16*) _TIFFcalloc(tif->tif_dir.td_extrasamples, sizeof(uint16));
+                if (!new_sampleinfo) {
+                    TIFFErrorExt(tif->tif_clientdata, module, "Failed to allocate memory for "
+                                "temporary new sampleinfo array (%d 16 bit elements)",
+                                tif->tif_dir.td_extrasamples);
+                    goto bad;
+                }
+
+                memcpy(new_sampleinfo, tif->tif_dir.td_sampleinfo, old_extrasamples * sizeof(uint16));
+                _TIFFsetShortArray(&tif->tif_dir.td_sampleinfo, new_sampleinfo, tif->tif_dir.td_extrasamples);
+                _TIFFfree(new_sampleinfo);
+        }
+
 	/*
 	 * Verify Palette image has a Colormap.
 	 */
@@ -3971,31 +4181,10 @@
 				"\"StripByteCounts\" field, calculating from imagelength");
 			if (EstimateStripByteCounts(tif, dir, dircount) < 0)
 			    goto bad;
-		/*
-		 * Assume we have wrong StripByteCount value (in case
-		 * of single strip) in following cases:
-		 *   - it is equal to zero along with StripOffset;
-		 *   - it is larger than file itself (in case of uncompressed
-		 *     image);
-		 *   - it is smaller than the size of the bytes per row
-		 *     multiplied on the number of rows.  The last case should
-		 *     not be checked in the case of writing new image,
-		 *     because we may do not know the exact strip size
-		 *     until the whole image will be written and directory
-		 *     dumped out.
-		 */
-		#define	BYTECOUNTLOOKSBAD \
-		    ( (tif->tif_dir.td_stripbytecount[0] == 0 && tif->tif_dir.td_stripoffset[0] != 0) || \
-		      (tif->tif_dir.td_compression == COMPRESSION_NONE && \
-		       tif->tif_dir.td_stripbytecount[0] > TIFFGetFileSize(tif) - tif->tif_dir.td_stripoffset[0]) || \
-		      (tif->tif_mode == O_RDONLY && \
-		       tif->tif_dir.td_compression == COMPRESSION_NONE && \
-		       tif->tif_dir.td_stripbytecount[0] < TIFFScanlineSize64(tif) * tif->tif_dir.td_imagelength) )
 
 		} else if (tif->tif_dir.td_nstrips == 1
-                           && _TIFFFillStriles(tif)
-			   && tif->tif_dir.td_stripoffset[0] != 0
-			   && BYTECOUNTLOOKSBAD) {
+                           && !(tif->tif_flags&TIFF_ISTILED)
+			   && ByteCountLooksBad(tif)) {
 			/*
 			 * XXX: Plexus (and others) sometimes give a value of
 			 * zero for a tag when they don't know what the
@@ -4007,13 +4196,13 @@
 			if(EstimateStripByteCounts(tif, dir, dircount) < 0)
 			    goto bad;
 
-#if !defined(DEFER_STRILE_LOAD)
-		} else if (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG
+		} else if (!(tif->tif_flags&TIFF_DEFERSTRILELOAD)
+			   && tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG
 			   && tif->tif_dir.td_nstrips > 2
 			   && tif->tif_dir.td_compression == COMPRESSION_NONE
-			   && tif->tif_dir.td_stripbytecount[0] != tif->tif_dir.td_stripbytecount[1]
-			   && tif->tif_dir.td_stripbytecount[0] != 0
-			   && tif->tif_dir.td_stripbytecount[1] != 0 ) {
+			   && TIFFGetStrileByteCount(tif, 0) != TIFFGetStrileByteCount(tif, 1)
+			   && TIFFGetStrileByteCount(tif, 0) != 0
+			   && TIFFGetStrileByteCount(tif, 1) != 0 ) {
 			/*
 			 * XXX: Some vendors fill StripByteCount array with
 			 * absolutely wrong values (it can be equal to
@@ -4028,7 +4217,6 @@
 			    "Wrong \"StripByteCounts\" field, ignoring and calculating from imagelength");
 			if (EstimateStripByteCounts(tif, dir, dircount) < 0)
 			    goto bad;
-#endif /* !defined(DEFER_STRILE_LOAD) */                        
 		}
 	}
 	if (dir)
@@ -4043,26 +4231,27 @@
 		else
 			tif->tif_dir.td_maxsamplevalue = (uint16)((1L<<tif->tif_dir.td_bitspersample)-1);
 	}
+
+#ifdef STRIPBYTECOUNTSORTED_UNUSED
 	/*
 	 * XXX: We can optimize checking for the strip bounds using the sorted
 	 * bytecounts array. See also comments for TIFFAppendToStrip()
 	 * function in tif_write.c.
 	 */
-#if !defined(DEFER_STRILE_LOAD)        
-	if (tif->tif_dir.td_nstrips > 1) {
+	if (!(tif->tif_flags&TIFF_DEFERSTRILELOAD) && tif->tif_dir.td_nstrips > 1) {
 		uint32 strip;
 
 		tif->tif_dir.td_stripbytecountsorted = 1;
 		for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) {
-			if (tif->tif_dir.td_stripoffset[strip - 1] >
-			    tif->tif_dir.td_stripoffset[strip]) {
+			if (TIFFGetStrileOffset(tif, strip - 1) >
+			    TIFFGetStrileOffset(tif, strip)) {
 				tif->tif_dir.td_stripbytecountsorted = 0;
 				break;
 			}
 		}
 	}
-#endif /* !defined(DEFER_STRILE_LOAD) */
-        
+#endif
+
 	/*
 	 * An opportunity for compression mode dependent tag fixup
 	 */
@@ -4081,11 +4270,20 @@
 	    (tif->tif_dir.td_nstrips==1)&&
 	    (tif->tif_dir.td_compression==COMPRESSION_NONE)&&  
 	    ((tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED))==TIFF_STRIPCHOP))
-    {
-        if ( !_TIFFFillStriles(tif) || !tif->tif_dir.td_stripbytecount )
-            return 0;
-		ChopUpSingleUncompressedStrip(tif);
-    }
+        {
+            ChopUpSingleUncompressedStrip(tif);
+        }
+
+        /* There are also uncompressed striped files with strips larger than */
+        /* 2 GB, which make them unfriendly with a lot of code. If possible, */
+        /* try to expose smaller "virtual" strips. */
+        if( tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG &&
+            tif->tif_dir.td_compression == COMPRESSION_NONE &&
+            (tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP &&
+            TIFFStripSize64(tif) > 0x7FFFFFFFUL )
+        {
+            TryChopUpUncompressedBigTiff(tif);
+        }
 
         /*
          * Clear the dirty directory flag. 
@@ -4237,17 +4435,17 @@
 				TIFFWarningExt(tif->tif_clientdata, module,
 				    "Registering anonymous field with tag %d (0x%x) failed",
 				    dp->tdir_tag, dp->tdir_tag);
-				dp->tdir_tag=IGNORE;
+				dp->tdir_ignore = TRUE;
 			} else {
 				TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
 				assert( fii != FAILED_FII );
 			}
 		}
-		if (dp->tdir_tag!=IGNORE)
+		if (!dp->tdir_ignore)
 		{
 			fip=tif->tif_fields[fii];
 			if (fip->field_bit==FIELD_IGNORE)
-				dp->tdir_tag=IGNORE;
+				dp->tdir_ignore = TRUE;
 			else
 			{
 				/* check data type */
@@ -4267,7 +4465,7 @@
 					TIFFWarningExt(tif->tif_clientdata, module,
 					    "Wrong data type %d for \"%s\"; tag ignored",
 					    dp->tdir_type,fip->field_name);
-					dp->tdir_tag=IGNORE;
+					dp->tdir_ignore = TRUE;
 				}
 				else
 				{
@@ -4281,21 +4479,21 @@
 						else
 							expected=(uint32)fip->field_readcount;
 						if (!CheckDirCount(tif,dp,expected))
-							dp->tdir_tag=IGNORE;
+							dp->tdir_ignore = TRUE;
 					}
 				}
 			}
-			switch (dp->tdir_tag)
-			{
-				case IGNORE:
-					break;
-				case EXIFTAG_SUBJECTDISTANCE:
-					(void) TIFFFetchSubjectDistance(tif,dp);
-					break;
-				default:
-					(void) TIFFFetchNormalTag(tif, dp, TRUE);
-					break;
-			}
+			if (!dp->tdir_ignore) {
+				switch (dp->tdir_tag) 
+				{
+					case EXIFTAG_SUBJECTDISTANCE:
+						(void)TIFFFetchSubjectDistance(tif, dp);
+						break;
+					default:
+						(void)TIFFFetchNormalTag(tif, dp, TRUE);
+						break;
+				}
+			} /*-- if (!dp->tdir_ignore) */
 		}
 	}
 	if (dir)
@@ -4328,12 +4526,12 @@
         if( !_TIFFFillStrilesInternal( tif, 0 ) )
             return -1;
 
-	if (td->td_stripbytecount)
-		_TIFFfree(td->td_stripbytecount);
-	td->td_stripbytecount = (uint64*)
+	if (td->td_stripbytecount_p)
+		_TIFFfree(td->td_stripbytecount_p);
+	td->td_stripbytecount_p = (uint64*)
 	    _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64),
 		"for \"StripByteCounts\" array");
-        if( td->td_stripbytecount == NULL )
+        if( td->td_stripbytecount_p == NULL )
             return -1;
 
 	if (td->td_compression != COMPRESSION_NONE) {
@@ -4357,6 +4555,8 @@
 				    dp->tdir_type);
 				return -1;
 			}
+			if( dp->tdir_count > TIFF_UINT64_MAX / typewidth )
+                            return -1;
 			datasize=(uint64)typewidth*dp->tdir_count;
 			if (!(tif->tif_flags&TIFF_BIGTIFF))
 			{
@@ -4368,13 +4568,19 @@
 				if (datasize<=8)
 					datasize=0;
 			}
+			if( space > TIFF_UINT64_MAX - datasize )
+                            return -1;
 			space+=datasize;
 		}
-		space = filesize - space;
+		if( filesize < space )
+                    /* we should perhaps return in error ? */
+                    space = filesize;
+                else
+                    space = filesize - space;
 		if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
 			space /= td->td_samplesperpixel;
 		for (strip = 0; strip < td->td_nstrips; strip++)
-			td->td_stripbytecount[strip] = space;
+			td->td_stripbytecount_p[strip] = space;
 		/*
 		 * This gross hack handles the case were the offset to
 		 * the last strip is past the place where we think the strip
@@ -4383,18 +4589,30 @@
 		 * of data in the strip and trim this number back accordingly.
 		 */
 		strip--;
-		if (td->td_stripoffset[strip]+td->td_stripbytecount[strip] > filesize)
-			td->td_stripbytecount[strip] = filesize - td->td_stripoffset[strip];
+                if (td->td_stripoffset_p[strip] > TIFF_UINT64_MAX - td->td_stripbytecount_p[strip])
+                    return -1;
+		if (td->td_stripoffset_p[strip]+td->td_stripbytecount_p[strip] > filesize) {
+                    if( td->td_stripoffset_p[strip] >= filesize ) {
+                        /* Not sure what we should in that case... */
+                        td->td_stripbytecount_p[strip] = 0;
+                    } else {
+                        td->td_stripbytecount_p[strip] = filesize - td->td_stripoffset_p[strip];
+                    }
+                }
 	} else if (isTiled(tif)) {
 		uint64 bytespertile = TIFFTileSize64(tif);
 
 		for (strip = 0; strip < td->td_nstrips; strip++)
-		    td->td_stripbytecount[strip] = bytespertile;
+		    td->td_stripbytecount_p[strip] = bytespertile;
 	} else {
 		uint64 rowbytes = TIFFScanlineSize64(tif);
 		uint32 rowsperstrip = td->td_imagelength/td->td_stripsperimage;
 		for (strip = 0; strip < td->td_nstrips; strip++)
-			td->td_stripbytecount[strip] = rowbytes * rowsperstrip;
+                {
+                    if( rowbytes > 0 && rowsperstrip > TIFF_UINT64_MAX / rowbytes )
+                        return -1;
+                    td->td_stripbytecount_p[strip] = rowbytes * rowsperstrip;
+                }
 	}
 	TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
 	if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP))
@@ -4588,12 +4806,13 @@
 		}
 	} else {
 		tmsize_t m;
-		tmsize_t off = (tmsize_t) tif->tif_diroff;
-		if ((uint64)off!=tif->tif_diroff)
+		tmsize_t off;
+		if (tif->tif_diroff > (uint64)TIFF_INT64_MAX)
 		{
 			TIFFErrorExt(tif->tif_clientdata,module,"Can not read TIFF directory count");
 			return(0);
 		}
+		off = (tmsize_t) tif->tif_diroff;
 
 		/*
 		 * Check for integer overflow when validating the dir_off,
@@ -4711,6 +4930,7 @@
 	mb=dir;
 	for (n=0; n<dircount16; n++)
 	{
+		mb->tdir_ignore = FALSE;
 		if (tif->tif_flags&TIFF_SWAB)
 			TIFFSwabShort((uint16*)ma);
 		mb->tdir_tag=*(uint16*)ma;
@@ -4725,6 +4945,7 @@
 				TIFFSwabLong((uint32*)ma);
 			mb->tdir_count=(uint64)(*(uint32*)ma);
 			ma+=sizeof(uint32);
+			mb->tdir_offset.toff_long8=0;
 			*(uint32*)(&mb->tdir_offset)=*(uint32*)ma;
 			ma+=sizeof(uint32);
 		}
@@ -4778,17 +4999,18 @@
 				err=TIFFReadDirEntryByteArray(tif,dp,&data);
 				if (err==TIFFReadDirEntryErrOk)
 				{
-					uint8* ma;
-					uint32 mb;
+					uint32 mb = 0;
 					int n;
-					ma=data;
-					mb=0;
-					while (mb<(uint32)dp->tdir_count)
+					if (data != NULL)
 					{
-						if (*ma==0)
-							break;
-						ma++;
-						mb++;
+					    uint8* ma = data;
+					    while (mb<(uint32)dp->tdir_count)
+					    {
+					            if (*ma==0)
+					                    break;
+					            ma++;
+					            mb++;
+					    }
 					}
 					if (mb+1<(uint32)dp->tdir_count)
 						TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" contains null byte in value; value incorrectly truncated during reading due to implementation limitations",fip->field_name);
@@ -5038,11 +5260,11 @@
 					if (err==TIFFReadDirEntryErrOk)
 					{
 						int m;
-                        if( dp->tdir_count > 0 && data[dp->tdir_count-1] != '\0' )
-                        {
-                            TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte. Forcing it to be null",fip->field_name);
-                            data[dp->tdir_count-1] = '\0';
-                        }
+						if( data != 0 && dp->tdir_count > 0 && data[dp->tdir_count-1] != '\0' )
+						{
+						    TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte. Forcing it to be null",fip->field_name);
+						    data[dp->tdir_count-1] = '\0';
+						}
 						m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data);
 						if (data!=0)
 							_TIFFfree(data);
@@ -5215,11 +5437,11 @@
 				if (err==TIFFReadDirEntryErrOk)
 				{
 					int m;
-                    if( dp->tdir_count > 0 && data[dp->tdir_count-1] != '\0' )
-                    {
-                        TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte. Forcing it to be null",fip->field_name);
-                        data[dp->tdir_count-1] = '\0';
-                    }
+					if( data != 0 && dp->tdir_count > 0 && data[dp->tdir_count-1] != '\0' )
+					{
+					    TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte. Forcing it to be null",fip->field_name);
+                                            data[dp->tdir_count-1] = '\0';
+					}
 					m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data);
 					if (data!=0)
 						_TIFFfree(data);
@@ -5437,28 +5659,39 @@
 	static const char module[] = "TIFFFetchStripThing";
 	enum TIFFReadDirEntryErr err;
 	uint64* data;
-	err=TIFFReadDirEntryLong8Array(tif,dir,&data);
+	err=TIFFReadDirEntryLong8ArrayWithLimit(tif,dir,&data,nstrips);
 	if (err!=TIFFReadDirEntryErrOk)
 	{
 		const TIFFField* fip = TIFFFieldWithTag(tif,dir->tdir_tag); 
 		TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0);
 		return(0);
 	}
-	if (dir->tdir_count!=(uint64)nstrips)
+	if (dir->tdir_count<(uint64)nstrips)
 	{
 		uint64* resizeddata;
+		const TIFFField* fip = TIFFFieldWithTag(tif,dir->tdir_tag);
+		const char* pszMax = getenv("LIBTIFF_STRILE_ARRAY_MAX_RESIZE_COUNT");
+		uint32 max_nstrips = 1000000;
+		if( pszMax )
+			max_nstrips = (uint32) atoi(pszMax);
+		TIFFReadDirEntryOutputErr(tif,TIFFReadDirEntryErrCount,
+		            module,
+		            fip ? fip->field_name : "unknown tagname",
+		            ( nstrips <= max_nstrips ) );
+
+		if( nstrips > max_nstrips )
+		{
+			_TIFFfree(data);
+			return(0);
+		}
+
 		resizeddata=(uint64*)_TIFFCheckMalloc(tif,nstrips,sizeof(uint64),"for strip array");
 		if (resizeddata==0) {
 			_TIFFfree(data);
 			return(0);
 		}
-		if (dir->tdir_count<(uint64)nstrips)
-		{
-			_TIFFmemcpy(resizeddata,data,(uint32)dir->tdir_count*sizeof(uint64));
-			_TIFFmemset(resizeddata+(uint32)dir->tdir_count,0,(nstrips-(uint32)dir->tdir_count)*sizeof(uint64));
-		}
-		else
-			_TIFFmemcpy(resizeddata,data,nstrips*sizeof(uint64));
+                _TIFFmemcpy(resizeddata,data,(uint32)dir->tdir_count*sizeof(uint64));
+                _TIFFmemset(resizeddata+(uint32)dir->tdir_count,0,(nstrips-(uint32)dir->tdir_count)*sizeof(uint64));
 		_TIFFfree(data);
 		data=resizeddata;
 	}
@@ -5506,7 +5739,7 @@
 			TIFFSwabArrayOfLong(m.i,2);
 		if (m.i[0]==0)
 			n=0.0;
-		else if (m.i[0]==0xFFFFFFFF)
+		else if (m.i[0]==0xFFFFFFFF || m.i[1]==0)
 			/*
 			 * XXX: Numerator 0xFFFFFFFF means that we have infinite
 			 * distance. Indicate that with a negative floating point
@@ -5524,6 +5757,75 @@
 	}
 }
 
+static void allocChoppedUpStripArrays(TIFF* tif, uint32 nstrips,
+                                      uint64 stripbytes, uint32 rowsperstrip)
+{
+    TIFFDirectory *td = &tif->tif_dir;
+    uint64 bytecount;
+    uint64 offset;
+    uint64 last_offset;
+    uint64 last_bytecount;
+    uint32 i;
+    uint64 *newcounts;
+    uint64 *newoffsets;
+
+    offset = TIFFGetStrileOffset(tif, 0);
+    last_offset = TIFFGetStrileOffset(tif, td->td_nstrips-1);
+    last_bytecount = TIFFGetStrileByteCount(tif, td->td_nstrips-1);
+    if( last_offset > TIFF_UINT64_MAX - last_bytecount ||
+        last_offset + last_bytecount < offset )
+    {
+        return;
+    }
+    bytecount = last_offset + last_bytecount - offset;
+
+    newcounts = (uint64*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint64),
+                            "for chopped \"StripByteCounts\" array");
+    newoffsets = (uint64*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint64),
+                            "for chopped \"StripOffsets\" array");
+    if (newcounts == NULL || newoffsets == NULL) {
+        /*
+        * Unable to allocate new strip information, give up and use
+        * the original one strip information.
+        */
+        if (newcounts != NULL)
+            _TIFFfree(newcounts);
+        if (newoffsets != NULL)
+            _TIFFfree(newoffsets);
+        return;
+    }
+
+    /*
+     * Fill the strip information arrays with new bytecounts and offsets
+     * that reflect the broken-up format.
+     */
+    for (i = 0; i < nstrips; i++)
+    {
+        if (stripbytes > bytecount)
+            stripbytes = bytecount;
+        newcounts[i] = stripbytes;
+        newoffsets[i] = stripbytes ? offset : 0;
+        offset += stripbytes;
+        bytecount -= stripbytes;
+    }
+
+    /*
+     * Replace old single strip info with multi-strip info.
+     */
+    td->td_stripsperimage = td->td_nstrips = nstrips;
+    TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
+
+    _TIFFfree(td->td_stripbytecount_p);
+    _TIFFfree(td->td_stripoffset_p);
+    td->td_stripbytecount_p = newcounts;
+    td->td_stripoffset_p = newoffsets;
+#ifdef STRIPBYTECOUNTSORTED_UNUSED
+    td->td_stripbytecountsorted = 1;
+#endif
+    tif->tif_flags |= TIFF_CHOPPEDUPARRAYS;
+}
+
+
 /*
  * Replace a single strip (tile) of uncompressed data by multiple strips
  * (tiles), each approximately STRIP_SIZE_DEFAULT bytes. This is useful for
@@ -5539,14 +5841,16 @@
 	uint32 rowblock;
 	uint64 rowblockbytes;
 	uint64 stripbytes;
-	uint32 strip;
 	uint32 nstrips;
 	uint32 rowsperstrip;
-	uint64* newcounts;
-	uint64* newoffsets;
 
-	bytecount = td->td_stripbytecount[0];
-	offset = td->td_stripoffset[0];
+	bytecount = TIFFGetStrileByteCount(tif, 0);
+        /* On a newly created file, just re-opened to be filled, we */
+        /* don't want strip chop to trigger as it is going to cause issues */
+        /* later ( StripOffsets and StripByteCounts improperly filled) . */
+        if( bytecount == 0 && tif->tif_mode != O_RDONLY )
+            return;
+	offset = TIFFGetStrileByteCount(tif, 0);
 	assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
 	if ((td->td_photometric == PHOTOMETRIC_YCBCR)&&
 	    (!isUpSampled(tif)))
@@ -5579,46 +5883,448 @@
         if( nstrips == 0 )
             return;
 
-	newcounts = (uint64*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint64),
-				"for chopped \"StripByteCounts\" array");
-	newoffsets = (uint64*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint64),
-				"for chopped \"StripOffsets\" array");
-	if (newcounts == NULL || newoffsets == NULL) {
-		/*
-		 * Unable to allocate new strip information, give up and use
-		 * the original one strip information.
-		 */
-		if (newcounts != NULL)
-			_TIFFfree(newcounts);
-		if (newoffsets != NULL)
-			_TIFFfree(newoffsets);
-		return;
-	}
-	/*
-	 * Fill the strip information arrays with new bytecounts and offsets
-	 * that reflect the broken-up format.
-	 */
-	for (strip = 0; strip < nstrips; strip++) {
-		if (stripbytes > bytecount)
-			stripbytes = bytecount;
-		newcounts[strip] = stripbytes;
-		newoffsets[strip] = stripbytes ? offset : 0;
-		offset += stripbytes;
-		bytecount -= stripbytes;
-	}
-	/*
-	 * Replace old single strip info with multi-strip info.
-	 */
-	td->td_stripsperimage = td->td_nstrips = nstrips;
-	TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
+        /* If we are going to allocate a lot of memory, make sure that the */
+        /* file is as big as needed */
+        if( tif->tif_mode == O_RDONLY &&
+            nstrips > 1000000 &&
+            (offset >= TIFFGetFileSize(tif) ||
+             stripbytes > (TIFFGetFileSize(tif) - offset) / (nstrips - 1)) )
+        {
+            return;
+        }
 
-	_TIFFfree(td->td_stripbytecount);
-	_TIFFfree(td->td_stripoffset);
-	td->td_stripbytecount = newcounts;
-	td->td_stripoffset = newoffsets;
-	td->td_stripbytecountsorted = 1;
+        allocChoppedUpStripArrays(tif, nstrips, stripbytes, rowsperstrip);
 }
 
+
+/*
+ * Replace a file with contiguous strips > 2 GB of uncompressed data by
+ * multiple smaller strips. This is useful for
+ * dealing with large images or for dealing with machines with a limited
+ * amount memory.
+ */
+static void TryChopUpUncompressedBigTiff( TIFF* tif )
+{
+    TIFFDirectory *td = &tif->tif_dir;
+    uint32 rowblock;
+    uint64 rowblockbytes;
+    uint32 i;
+    uint64 stripsize;
+    uint32 rowblocksperstrip;
+    uint32 rowsperstrip;
+    uint64 stripbytes;
+    uint32 nstrips;
+
+    stripsize = TIFFStripSize64(tif);
+
+    assert( tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG );
+    assert( tif->tif_dir.td_compression == COMPRESSION_NONE );
+    assert( (tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP );
+    assert( stripsize > 0x7FFFFFFFUL );
+
+    /* On a newly created file, just re-opened to be filled, we */
+    /* don't want strip chop to trigger as it is going to cause issues */
+    /* later ( StripOffsets and StripByteCounts improperly filled) . */
+    if( TIFFGetStrileByteCount(tif, 0) == 0 && tif->tif_mode != O_RDONLY )
+        return;
+
+    if ((td->td_photometric == PHOTOMETRIC_YCBCR)&&
+        (!isUpSampled(tif)))
+        rowblock = td->td_ycbcrsubsampling[1];
+    else
+        rowblock = 1;
+    rowblockbytes = TIFFVStripSize64(tif, rowblock);
+    if( rowblockbytes == 0 || rowblockbytes > 0x7FFFFFFFUL )
+    {
+        /* In case of file with gigantic width */
+        return;
+    }
+
+    /* Check that the strips are contiguous and of the expected size */
+    for( i = 0; i < td->td_nstrips; i++ )
+    {
+        if( i == td->td_nstrips - 1 )
+        {
+            if( TIFFGetStrileByteCount(tif, i) < TIFFVStripSize64(
+                    tif, td->td_imagelength - i * td->td_rowsperstrip ) )
+            {
+                return;
+            }
+        }
+        else
+        {
+            if( TIFFGetStrileByteCount(tif, i) != stripsize )
+            {
+                return;
+            }
+            if( i > 0 && TIFFGetStrileOffset(tif, i) !=
+                    TIFFGetStrileOffset(tif, i-1) + TIFFGetStrileByteCount(tif, i-1) )
+            {
+                return;
+            }
+        }
+    }
+
+    /* Aim for 512 MB strips (that will still be manageable by 32 bit builds */
+    rowblocksperstrip = (uint32) (512 * 1024 * 1024 / rowblockbytes);
+    if( rowblocksperstrip == 0 )
+        rowblocksperstrip = 1;
+    rowsperstrip = rowblocksperstrip * rowblock;
+    stripbytes = rowblocksperstrip * rowblockbytes;
+    assert( stripbytes <= 0x7FFFFFFFUL );
+
+    nstrips = TIFFhowmany_32(td->td_imagelength, rowsperstrip);
+    if( nstrips == 0 )
+        return;
+
+    /* If we are going to allocate a lot of memory, make sure that the */
+    /* file is as big as needed */
+    if( tif->tif_mode == O_RDONLY &&
+        nstrips > 1000000 )
+    {
+        uint64 last_offset = TIFFGetStrileOffset(tif, td->td_nstrips-1);
+        uint64 filesize = TIFFGetFileSize(tif);
+        uint64 last_bytecount = TIFFGetStrileByteCount(tif, td->td_nstrips-1);
+        if( last_offset > filesize ||
+            last_bytecount > filesize - last_offset )
+        {
+            return;
+        }
+    }
+
+    allocChoppedUpStripArrays(tif, nstrips, stripbytes, rowsperstrip);
+}
+
+
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
+static uint64 _TIFFUnsanitizedAddUInt64AndInt(uint64 a, int b)
+{
+    return a + b;
+}
+
+/* Read the value of [Strip|Tile]Offset or [Strip|Tile]ByteCount around
+ * strip/tile of number strile. Also fetch the neighbouring values using a
+ * 4096 byte page size.
+ */
+static
+int _TIFFPartialReadStripArray( TIFF* tif, TIFFDirEntry* dirent,
+                                int strile, uint64* panVals )
+{
+    static const char module[] = "_TIFFPartialReadStripArray";
+#define IO_CACHE_PAGE_SIZE 4096
+
+    size_t sizeofval;
+    const int bSwab = (tif->tif_flags & TIFF_SWAB) != 0;
+    int sizeofvalint;
+    uint64 nBaseOffset;
+    uint64 nOffset;
+    uint64 nOffsetStartPage;
+    uint64 nOffsetEndPage;
+    tmsize_t nToRead;
+    tmsize_t nRead;
+    uint64 nLastStripOffset;
+    int iStartBefore;
+    int i;
+    const uint32 arraySize = tif->tif_dir.td_stripoffsetbyteallocsize;
+    unsigned char buffer[2 * IO_CACHE_PAGE_SIZE];
+
+    assert( dirent->tdir_count > 4 );
+
+    if( dirent->tdir_type == TIFF_SHORT )
+    {
+        sizeofval = sizeof(uint16);
+    }
+    else if( dirent->tdir_type == TIFF_LONG )
+    {
+        sizeofval = sizeof(uint32);
+    }
+    else if( dirent->tdir_type == TIFF_LONG8 )
+    {
+        sizeofval = sizeof(uint64);
+    }
+    else
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                 "Invalid type for [Strip|Tile][Offset/ByteCount] tag");
+        panVals[strile] = 0;
+        return 0;
+    }
+    sizeofvalint = (int)(sizeofval);
+
+    if( tif->tif_flags&TIFF_BIGTIFF )
+    {
+        uint64 offset = dirent->tdir_offset.toff_long8;
+        if( bSwab )
+            TIFFSwabLong8(&offset);
+        nBaseOffset = offset;
+    }
+    else
+    {
+        uint32 offset = dirent->tdir_offset.toff_long;
+        if( bSwab )
+            TIFFSwabLong(&offset);
+        nBaseOffset = offset;
+    }
+    /* To avoid later unsigned integer overflows */
+    if( nBaseOffset > (uint64)TIFF_INT64_MAX )
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                 "Cannot read offset/size for strile %d", strile);
+        panVals[strile] = 0;
+        return 0;
+    }
+    nOffset = nBaseOffset + sizeofval * strile;
+    nOffsetStartPage =
+        (nOffset / IO_CACHE_PAGE_SIZE) * IO_CACHE_PAGE_SIZE;
+    nOffsetEndPage = nOffsetStartPage + IO_CACHE_PAGE_SIZE;
+
+    if( nOffset + sizeofval > nOffsetEndPage )
+        nOffsetEndPage += IO_CACHE_PAGE_SIZE;
+#undef IO_CACHE_PAGE_SIZE
+
+    nLastStripOffset = nBaseOffset + arraySize * sizeofval;
+    if( nLastStripOffset < nOffsetEndPage )
+        nOffsetEndPage = nLastStripOffset;
+    if( nOffsetStartPage >= nOffsetEndPage )
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                 "Cannot read offset/size for strile %d", strile);
+        panVals[strile] = 0;
+        return 0;
+    }
+    if (!SeekOK(tif,nOffsetStartPage))
+    {
+        panVals[strile] = 0;
+        return 0;
+    }
+
+    nToRead = (tmsize_t)(nOffsetEndPage - nOffsetStartPage);
+    nRead = TIFFReadFile(tif, buffer, nToRead);
+    if( nRead < nToRead )
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                 "Cannot read offset/size for strile around ~%d", strile);
+        return 0;
+    }
+    iStartBefore = -(int)((nOffset - nOffsetStartPage) / sizeofval);
+    if( strile + iStartBefore < 0 )
+        iStartBefore = -strile;
+    for( i = iStartBefore;
+         (uint32)(strile + i) < arraySize &&
+         _TIFFUnsanitizedAddUInt64AndInt(nOffset, (i + 1) * sizeofvalint) <= nOffsetEndPage;
+         ++i )
+    {
+        if( sizeofval == sizeof(uint16) )
+        {
+            uint16 val;
+            memcpy(&val,
+                   buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint,
+                   sizeof(val));
+            if( bSwab )
+                TIFFSwabShort(&val);
+            panVals[strile + i] = val;
+        }
+        else if( sizeofval == sizeof(uint32) )
+        {
+            uint32 val;
+            memcpy(&val,
+                   buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint,
+                   sizeof(val));
+            if( bSwab )
+                TIFFSwabLong(&val);
+            panVals[strile + i] = val;
+        }
+        else
+        {
+            uint64 val;
+            memcpy(&val,
+                   buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint,
+                   sizeof(val));
+            if( bSwab )
+                TIFFSwabLong8(&val);
+            panVals[strile + i] = val;
+        }
+    }
+    return 1;
+}
+
+static int _TIFFFetchStrileValue(TIFF* tif,
+                                 uint32 strile,
+                                 TIFFDirEntry* dirent,
+                                 uint64** parray)
+{
+    static const char module[] = "_TIFFFetchStrileValue";
+    TIFFDirectory *td = &tif->tif_dir;
+    if( strile >= dirent->tdir_count )
+    {
+        return 0;
+    }
+    if( strile >= td->td_stripoffsetbyteallocsize )
+    {
+        uint32 nStripArrayAllocBefore = td->td_stripoffsetbyteallocsize;
+        uint32 nStripArrayAllocNew;
+        uint64 nArraySize64;
+        size_t nArraySize;
+        uint64* offsetArray;
+        uint64* bytecountArray;
+
+        if( strile > 1000000 )
+        {
+            uint64 filesize = TIFFGetFileSize(tif);
+            /* Avoid excessive memory allocation attempt */
+            /* For such a big blockid we need at least a TIFF_LONG per strile */
+            /* for the offset array. */
+            if( strile > filesize / sizeof(uint32) )
+            {
+                TIFFErrorExt(tif->tif_clientdata, module, "File too short");
+                return 0;
+            }
+        }
+
+        if( td->td_stripoffsetbyteallocsize == 0 &&
+            td->td_nstrips < 1024 * 1024 )
+        {
+            nStripArrayAllocNew = td->td_nstrips;
+        }
+        else
+        {
+#define TIFF_MAX(a,b) (((a)>(b)) ? (a) : (b))
+#define TIFF_MIN(a,b) (((a)<(b)) ? (a) : (b))
+            nStripArrayAllocNew = TIFF_MAX(strile + 1, 1024U * 512U );
+            if( nStripArrayAllocNew < 0xFFFFFFFFU / 2  )
+                nStripArrayAllocNew *= 2;
+            nStripArrayAllocNew = TIFF_MIN(nStripArrayAllocNew, td->td_nstrips);
+        }
+        assert( strile < nStripArrayAllocNew );
+        nArraySize64 = (uint64)sizeof(uint64) * nStripArrayAllocNew;
+        nArraySize = (size_t)(nArraySize64);
+#if SIZEOF_SIZE_T == 4
+        if( nArraySize != nArraySize64 )
+        {
+            TIFFErrorExt(tif->tif_clientdata, module,
+                        "Cannot allocate strip offset and bytecount arrays");
+            return 0;
+        }
+#endif
+        offsetArray = (uint64*)(
+            _TIFFrealloc( td->td_stripoffset_p, nArraySize ) );
+        bytecountArray = (uint64*)(
+            _TIFFrealloc( td->td_stripbytecount_p, nArraySize ) );
+        if( offsetArray )
+            td->td_stripoffset_p = offsetArray;
+        if( bytecountArray )
+            td->td_stripbytecount_p = bytecountArray;
+        if( offsetArray && bytecountArray )
+        {
+            td->td_stripoffsetbyteallocsize = nStripArrayAllocNew;
+            /* Initialize new entries to ~0 / -1 */
+            memset(td->td_stripoffset_p + nStripArrayAllocBefore,
+                0xFF,
+                (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) * sizeof(uint64) );
+            memset(td->td_stripbytecount_p + nStripArrayAllocBefore,
+                0xFF,
+                (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) * sizeof(uint64) );
+        }
+        else
+        {
+            TIFFErrorExt(tif->tif_clientdata, module,
+                        "Cannot allocate strip offset and bytecount arrays");
+            _TIFFfree(td->td_stripoffset_p);
+            td->td_stripoffset_p = NULL;
+            _TIFFfree(td->td_stripbytecount_p);
+            td->td_stripbytecount_p = NULL;
+            td->td_stripoffsetbyteallocsize = 0;
+        }
+    }
+    if( *parray == NULL || strile >= td->td_stripoffsetbyteallocsize )
+        return 0;
+
+    if( ~((*parray)[strile]) == 0 )
+    {
+        if( !_TIFFPartialReadStripArray( tif, dirent, strile, *parray ) )
+        {
+            (*parray)[strile] = 0;
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static uint64 _TIFFGetStrileOffsetOrByteCountValue(TIFF *tif, uint32 strile,
+                                                   TIFFDirEntry* dirent,
+                                                   uint64** parray,
+                                                   int *pbErr)
+{
+    TIFFDirectory *td = &tif->tif_dir;
+    if( pbErr )
+        *pbErr = 0;
+    if( (tif->tif_flags&TIFF_DEFERSTRILELOAD) && !(tif->tif_flags&TIFF_CHOPPEDUPARRAYS) )
+    {
+        if( !(tif->tif_flags&TIFF_LAZYSTRILELOAD) ||
+            /* If the values may fit in the toff_long/toff_long8 member */
+            /* then use _TIFFFillStriles to simplify _TIFFFetchStrileValue */
+            dirent->tdir_count <= 4 )
+        {
+            if( !_TIFFFillStriles(tif) )
+            {
+                if( pbErr )
+                    *pbErr = 1;
+                /* Do not return, as we want this function to always */
+                /* return the same value if called several times with */
+                /* the same arguments */
+            }
+        }
+        else
+        {
+             if( !_TIFFFetchStrileValue(tif, strile, dirent, parray) )
+             {
+                if( pbErr )
+                    *pbErr = 1;
+                 return 0;
+             }
+        }
+    }
+    if( *parray == NULL || strile >= td->td_nstrips )
+    {
+        if( pbErr )
+            *pbErr = 1;
+        return 0;
+    }
+    return (*parray)[strile];
+}
+
+/* Return the value of the TileOffsets/StripOffsets array for the specified tile/strile */
+uint64 TIFFGetStrileOffset(TIFF *tif, uint32 strile)
+{
+    return TIFFGetStrileOffsetWithErr(tif, strile, NULL);
+}
+
+/* Return the value of the TileOffsets/StripOffsets array for the specified tile/strile */
+uint64 TIFFGetStrileOffsetWithErr(TIFF *tif, uint32 strile, int *pbErr)
+{
+    TIFFDirectory *td = &tif->tif_dir;
+    return _TIFFGetStrileOffsetOrByteCountValue(tif, strile,
+                               &(td->td_stripoffset_entry),
+                               &(td->td_stripoffset_p), pbErr);
+}
+
+/* Return the value of the TileByteCounts/StripByteCounts array for the specified tile/strile */
+uint64 TIFFGetStrileByteCount(TIFF *tif, uint32 strile)
+{
+    return TIFFGetStrileByteCountWithErr(tif, strile, NULL);
+}
+
+/* Return the value of the TileByteCounts/StripByteCounts array for the specified tile/strile */
+uint64 TIFFGetStrileByteCountWithErr(TIFF *tif, uint32 strile, int *pbErr)
+{
+    TIFFDirectory *td = &tif->tif_dir;
+    return _TIFFGetStrileOffsetOrByteCountValue(tif, strile,
+                               &(td->td_stripbytecount_entry),
+                               &(td->td_stripbytecount_p), pbErr);
+}
+
+
 int _TIFFFillStriles( TIFF *tif )
 {
     return _TIFFFillStrilesInternal( tif, 1 );
@@ -5626,51 +6332,64 @@
 
 static int _TIFFFillStrilesInternal( TIFF *tif, int loadStripByteCount )
 {
-#if defined(DEFER_STRILE_LOAD)
-        register TIFFDirectory *td = &tif->tif_dir;
-        int return_value = 1;
+    register TIFFDirectory *td = &tif->tif_dir;
+    int return_value = 1;
 
-        if( td->td_stripoffset != NULL )
-                return 1;
-
-        if( td->td_stripoffset_entry.tdir_count == 0 )
-                return 0;
-
-        if (!TIFFFetchStripThing(tif,&(td->td_stripoffset_entry),
-                                 td->td_nstrips,&td->td_stripoffset))
-        {
-                return_value = 0;
-        }
-
-        if (loadStripByteCount &&
-            !TIFFFetchStripThing(tif,&(td->td_stripbytecount_entry),
-                                 td->td_nstrips,&td->td_stripbytecount))
-        {
-                return_value = 0;
-        }
-
-        _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
-        _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
-
-	if (tif->tif_dir.td_nstrips > 1 && return_value == 1 ) {
-		uint32 strip;
-
-		tif->tif_dir.td_stripbytecountsorted = 1;
-		for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) {
-			if (tif->tif_dir.td_stripoffset[strip - 1] >
-			    tif->tif_dir.td_stripoffset[strip]) {
-				tif->tif_dir.td_stripbytecountsorted = 0;
-				break;
-			}
-		}
-	}
-
-        return return_value;
-#else /* !defined(DEFER_STRILE_LOAD) */
-        (void) tif;
-        (void) loadStripByteCount;
+    /* Do not do anything if TIFF_DEFERSTRILELOAD is not set */
+    if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) || (tif->tif_flags&TIFF_CHOPPEDUPARRAYS) != 0 )
         return 1;
-#endif 
+
+    if( tif->tif_flags&TIFF_LAZYSTRILELOAD )
+    {
+        /* In case of lazy loading, reload completely the arrays */
+        _TIFFfree(td->td_stripoffset_p);
+        _TIFFfree(td->td_stripbytecount_p);
+        td->td_stripoffset_p = NULL;
+        td->td_stripbytecount_p = NULL;
+        td->td_stripoffsetbyteallocsize = 0;
+        tif->tif_flags &= ~TIFF_LAZYSTRILELOAD;
+    }
+
+    /* If stripoffset array is already loaded, exit with success */
+    if( td->td_stripoffset_p != NULL )
+            return 1;
+
+    /* If tdir_count was cancelled, then we already got there, but in error */
+    if( td->td_stripoffset_entry.tdir_count == 0 )
+            return 0;
+
+    if (!TIFFFetchStripThing(tif,&(td->td_stripoffset_entry),
+                                td->td_nstrips,&td->td_stripoffset_p))
+    {
+            return_value = 0;
+    }
+
+    if (loadStripByteCount &&
+        !TIFFFetchStripThing(tif,&(td->td_stripbytecount_entry),
+                                td->td_nstrips,&td->td_stripbytecount_p))
+    {
+            return_value = 0;
+    }
+
+    _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
+    _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
+
+#ifdef STRIPBYTECOUNTSORTED_UNUSED
+    if (tif->tif_dir.td_nstrips > 1 && return_value == 1 ) {
+            uint32 strip;
+
+            tif->tif_dir.td_stripbytecountsorted = 1;
+            for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) {
+                    if (tif->tif_dir.td_stripoffset_p[strip - 1] >
+                        tif->tif_dir.td_stripoffset_p[strip]) {
+                            tif->tif_dir.td_stripbytecountsorted = 0;
+                            break;
+                    }
+            }
+    }
+#endif
+
+    return return_value;
 }
 
 
diff --git a/third_party/libtiff/tif_dirwrite.c b/third_party/libtiff/tif_dirwrite.c
index f733968..9e4d306 100644
--- a/third_party/libtiff/tif_dirwrite.c
+++ b/third_party/libtiff/tif_dirwrite.c
@@ -1,5 +1,3 @@
-/* $Id: tif_dirwrite.c,v 1.85 2017-01-11 16:09:02 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -30,7 +28,6 @@
  * Directory Write Support Routines.
  */
 #include "tiffiop.h"
-#include <float.h>
 
 #ifdef HAVE_IEEEFP
 #define TIFFCvtNativeToIEEEFloat(tif, n, fp)
@@ -185,6 +182,51 @@
 }
 
 /*
+ * This is an advanced writing function that must be used in a particular
+ * sequence, and generally together with TIFFForceStrileArrayWriting(),
+ * to make its intended effect. Its aim is to modify the location
+ * where the [Strip/Tile][Offsets/ByteCounts] arrays are located in the file.
+ * More precisely, when TIFFWriteCheck() will be called, the tag entries for
+ * those arrays will be written with type = count = offset = 0 as a temporary
+ * value.
+ *
+ * Its effect is only valid for the current directory, and before
+ * TIFFWriteDirectory() is first called, and  will be reset when
+ * changing directory.
+ *
+ * The typical sequence of calls is:
+ * TIFFOpen()
+ * [ TIFFCreateDirectory(tif) ]
+ * Set fields with calls to TIFFSetField(tif, ...)
+ * TIFFDeferStrileArrayWriting(tif)
+ * TIFFWriteCheck(tif, ...)
+ * TIFFWriteDirectory(tif)
+ * ... potentially create other directories and come back to the above directory
+ * TIFFForceStrileArrayWriting(tif): emit the arrays at the end of file
+ *
+ * Returns 1 in case of success, 0 otherwise.
+ */
+int TIFFDeferStrileArrayWriting(TIFF* tif)
+{
+    static const char module[] = "TIFFDeferStrileArrayWriting";
+    if (tif->tif_mode == O_RDONLY)
+    {
+        TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+                     "File opened in read-only mode");
+        return 0;
+    }
+    if( tif->tif_diroff != 0 )
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                     "Directory has already been written");
+        return 0;
+    }
+
+    tif->tif_dir.td_deferstrilearraywriting = TRUE;
+    return 1;
+}
+
+/*
  * Similar to TIFFWriteDirectory(), writes the directory out
  * but leaves all data structures in memory so that it can be
  * written again.  This will make a partially written TIFF file
@@ -195,7 +237,7 @@
 {
 	int rc;
 	/* Setup the strips arrays, if they haven't already been. */
-	if (tif->tif_dir.td_stripoffset == NULL)
+	if (tif->tif_dir.td_stripoffset_p == NULL)
 	    (void) TIFFSetupStrips(tif);
 	rc = TIFFWriteDirectorySec(tif,TRUE,FALSE,NULL);
 	(void) TIFFSetWriteOffset(tif, TIFFSeekFile(tif, 0, SEEK_END));
@@ -530,12 +572,12 @@
 			{
 				if (!isTiled(tif))
 				{
-					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount))
+					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount_p))
 						goto bad;
 				}
 				else
 				{
-					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount))
+					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount_p))
 						goto bad;
 				}
 			}
@@ -543,7 +585,7 @@
 			{
 				if (!isTiled(tif))
 				{
-                    /* td_stripoffset might be NULL in an odd OJPEG case. See
+                    /* td_stripoffset_p might be NULL in an odd OJPEG case. See
                      *  tif_dirread.c around line 3634.
                      * XXX: OJPEG hack.
                      * If a) compression is OJPEG, b) it's not a tiled TIFF,
@@ -554,13 +596,13 @@
                      * We can get here when using tiffset on such a file.
                      * See http://bugzilla.maptools.org/show_bug.cgi?id=2500
                     */
-                    if (tif->tif_dir.td_stripoffset != NULL &&
-                        !TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset))
+                    if (tif->tif_dir.td_stripoffset_p != NULL &&
+                        !TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset_p))
                         goto bad;
 				}
 				else
 				{
-					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset))
+					if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset_p))
 						goto bad;
 				}
 			}
@@ -697,8 +739,11 @@
 								}
 								break;
 							default:
-								assert(0);   /* we should never get here */
-								break;
+								TIFFErrorExt(tif->tif_clientdata,module,
+								            "Cannot write tag %d (%s)",
+								            TIFFFieldTag(o),
+                                                                            o->field_name ? o->field_name : "unknown");
+								goto bad;
 						}
 					}
 				}
@@ -821,7 +866,12 @@
 			TIFFDirEntry* nb;
 			for (na=0, nb=dir; ; na++, nb++)
 			{
-				assert(na<ndir);
+				if( na == ndir )
+                                {
+                                    TIFFErrorExt(tif->tif_clientdata,module,
+                                                 "Cannot find SubIFD tag");
+                                    goto bad;
+                                }
 				if (nb->tdir_tag==TIFFTAG_SUBIFD)
 					break;
 			}
@@ -940,15 +990,6 @@
 	return(0);
 }
 
-static float TIFFClampDoubleToFloat( double val )
-{
-    if( val > FLT_MAX )
-        return FLT_MAX;
-    if( val < -FLT_MAX )
-        return -FLT_MAX;
-    return (float)val;
-}
-
 static int8 TIFFClampDoubleToInt8( double val )
 {
     if( val > 127 )
@@ -1023,7 +1064,7 @@
 			if (tif->tif_dir.td_bitspersample<=32)
 			{
 				for (i = 0; i < count; ++i)
-					((float*)conv)[i] = TIFFClampDoubleToFloat(value[i]);
+					((float*)conv)[i] = _TIFFClampDoubleToFloat(value[i]);
 				ok = TIFFWriteDirectoryTagFloatArray(tif,ndir,dir,tag,count,(float*)conv);
 			}
 			else
@@ -1655,22 +1696,52 @@
 		return(TIFFWriteDirectoryTagCheckedLong(tif,ndir,dir,tag,value));
 }
 
+static int _WriteAsType(TIFF* tif, uint64 strile_size, uint64 uncompressed_threshold)
+{
+    const uint16 compression = tif->tif_dir.td_compression;
+    if ( compression == COMPRESSION_NONE )
+    {
+        return strile_size > uncompressed_threshold;
+    }
+    else if ( compression == COMPRESSION_JPEG ||
+              compression == COMPRESSION_LZW ||
+              compression == COMPRESSION_ADOBE_DEFLATE ||
+              compression == COMPRESSION_LZMA ||
+              compression == COMPRESSION_LERC ||
+              compression == COMPRESSION_ZSTD ||
+              compression == COMPRESSION_WEBP )
+    {
+        /* For a few select compression types, we assume that in the worst */
+        /* case the compressed size will be 10 times the uncompressed size */
+        /* This is overly pessismistic ! */
+        return strile_size >= uncompressed_threshold / 10;
+    }
+    return 1;
+}
+
+static int WriteAsLong8(TIFF* tif, uint64 strile_size)
+{
+    return _WriteAsType(tif, strile_size, 0xFFFFFFFFU);
+}
+
+static int WriteAsLong4(TIFF* tif, uint64 strile_size)
+{
+    return _WriteAsType(tif, strile_size, 0xFFFFU);
+}
+
 /************************************************************************/
 /*                TIFFWriteDirectoryTagLongLong8Array()                 */
 /*                                                                      */
-/*      Write out LONG8 array as LONG8 for BigTIFF or LONG for          */
-/*      Classic TIFF with some checking.                                */
+/*      Write out LONG8 array and write a SHORT/LONG/LONG8 depending    */
+/*      on strile size and Classic/BigTIFF mode.                        */
 /************************************************************************/
 
 static int
 TIFFWriteDirectoryTagLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value)
 {
     static const char module[] = "TIFFWriteDirectoryTagLongLong8Array";
-    uint64* ma;
-    uint32 mb;
-    uint32* p;
-    uint32* q;
     int o;
+    int write_aslong4;
 
     /* is this just a counting pass? */
     if (dir==NULL)
@@ -1679,37 +1750,105 @@
         return(1);
     }
 
-    /* We always write LONG8 for BigTIFF, no checking needed. */
-    if( tif->tif_flags&TIFF_BIGTIFF )
-        return TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir,
-                                                      tag,count,value);
-
-    /*
-    ** For classic tiff we want to verify everything is in range for LONG
-    ** and convert to long format.
-    */
-
-    p = _TIFFmalloc(count*sizeof(uint32));
-    if (p==NULL)
+    if( tif->tif_dir.td_deferstrilearraywriting )
     {
-        TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
-        return(0);
+        return TIFFWriteDirectoryTagData(tif, ndir, dir, tag, TIFF_NOTYPE, 0, 0, NULL);
     }
 
-    for (q=p, ma=value, mb=0; mb<count; ma++, mb++, q++)
+    if( tif->tif_flags&TIFF_BIGTIFF )
     {
-        if (*ma>0xFFFFFFFF)
+        int write_aslong8 = 1;
+        /* In the case of ByteCounts array, we may be able to write them on */
+        /* LONG if the strip/tilesize is not too big. */
+        /* Also do that for count > 1 in the case someone would want to create */
+        /* a single-strip file with a growing height, in which case using */
+        /* LONG8 will be safer. */
+        if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
         {
-            TIFFErrorExt(tif->tif_clientdata,module,
-                         "Attempt to write value larger than 0xFFFFFFFF in Classic TIFF file.");
-            _TIFFfree(p);
+            write_aslong8 = WriteAsLong8(tif, TIFFStripSize64(tif));
+        }
+        else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+        {
+            write_aslong8 = WriteAsLong8(tif, TIFFTileSize64(tif));
+        }
+        if( write_aslong8 )
+        {
+            return TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir,
+                                                        tag,count,value);
+        }
+    }
+
+    write_aslong4 = 1;
+    if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
+    {
+        write_aslong4 = WriteAsLong4(tif, TIFFStripSize64(tif));
+    }
+    else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+    {
+        write_aslong4 = WriteAsLong4(tif, TIFFTileSize64(tif));
+    }
+    if( write_aslong4 )
+    {
+        /*
+        ** For classic tiff we want to verify everything is in range for LONG
+        ** and convert to long format.
+        */
+
+        uint32* p = _TIFFmalloc(count*sizeof(uint32));
+        uint32* q;
+        uint64* ma;
+        uint32 mb;
+
+        if (p==NULL)
+        {
+            TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
             return(0);
         }
-        *q= (uint32)(*ma);
-    }
 
-    o=TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,count,p);
-    _TIFFfree(p);
+        for (q=p, ma=value, mb=0; mb<count; ma++, mb++, q++)
+        {
+            if (*ma>0xFFFFFFFF)
+            {
+                TIFFErrorExt(tif->tif_clientdata,module,
+                            "Attempt to write value larger than 0xFFFFFFFF in LONG array.");
+                _TIFFfree(p);
+                return(0);
+            }
+            *q= (uint32)(*ma);
+        }
+
+        o=TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,count,p);
+        _TIFFfree(p);
+    }
+    else
+    {
+        uint16* p = _TIFFmalloc(count*sizeof(uint16));
+        uint16* q;
+        uint64* ma;
+        uint32 mb;
+
+        if (p==NULL)
+        {
+            TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
+            return(0);
+        }
+
+        for (q=p, ma=value, mb=0; mb<count; ma++, mb++, q++)
+        {
+            if (*ma>0xFFFF)
+            {
+                /* Should not happen normally given the check we did before */
+                TIFFErrorExt(tif->tif_clientdata,module,
+                            "Attempt to write value larger than 0xFFFF in SHORT array.");
+                _TIFFfree(p);
+                return(0);
+            }
+            *q= (uint16)(*ma);
+        }
+
+        o=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,tag,count,p);
+        _TIFFfree(p);
+    }
 
     return(o);
 }
@@ -1887,12 +2026,14 @@
 		n=3;
 	if (n==3)
 	{
-		if (!_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[2],m*sizeof(uint16)))
+		if (tif->tif_dir.td_transferfunction[2] == NULL ||
+		    !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[2],m*sizeof(uint16)))
 			n=2;
 	}
 	if (n==2)
 	{
-		if (!_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[1],m*sizeof(uint16)))
+		if (tif->tif_dir.td_transferfunction[1] == NULL ||
+		    !_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[1],m*sizeof(uint16)))
 			n=1;
 	}
 	if (n==0)
@@ -1944,7 +2085,14 @@
 		for (p=0; p < tif->tif_dir.td_nsubifd; p++)
 		{
                         assert(pa != 0);
-			assert(*pa <= 0xFFFFFFFFUL);
+
+                        /* Could happen if an classicTIFF has a SubIFD of type LONG8 (which is illegal) */
+                        if( *pa > 0xFFFFFFFFUL)
+                        {
+                            TIFFErrorExt(tif->tif_clientdata,module,"Illegal value for SubIFD tag");
+                            _TIFFfree(o);
+                            return(0);
+                        }
 			*pb++=(uint32)(*pa++);
 		}
 		n=TIFFWriteDirectoryTagCheckedIfdArray(tif,ndir,dir,TIFFTAG_SUBIFD,tif->tif_dir.td_nsubifd,o);
@@ -2111,7 +2259,10 @@
 {
 	uint64 m;
 	assert(sizeof(uint64)==8);
-	assert(tif->tif_flags&TIFF_BIGTIFF);
+	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
+		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedLong8","LONG8 not allowed for ClassicTIFF");
+		return(0);
+	}
 	m=value;
 	if (tif->tif_flags&TIFF_SWAB)
 		TIFFSwabLong8(&m);
@@ -2124,7 +2275,10 @@
 {
 	assert(count<0x20000000);
 	assert(sizeof(uint64)==8);
-	assert(tif->tif_flags&TIFF_BIGTIFF);
+	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
+		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedLong8Array","LONG8 not allowed for ClassicTIFF");
+		return(0);
+	}
 	if (tif->tif_flags&TIFF_SWAB)
 		TIFFSwabArrayOfLong8(value,count);
 	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG8,count,count*8,value));
@@ -2136,7 +2290,10 @@
 {
 	int64 m;
 	assert(sizeof(int64)==8);
-	assert(tif->tif_flags&TIFF_BIGTIFF);
+	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
+		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedSlong8","SLONG8 not allowed for ClassicTIFF");
+		return(0);
+	}
 	m=value;
 	if (tif->tif_flags&TIFF_SWAB)
 		TIFFSwabLong8((uint64*)(&m));
@@ -2149,7 +2306,10 @@
 {
 	assert(count<0x20000000);
 	assert(sizeof(int64)==8);
-	assert(tif->tif_flags&TIFF_BIGTIFF);
+	if( !(tif->tif_flags&TIFF_BIGTIFF) ) {
+		TIFFErrorExt(tif->tif_clientdata,"TIFFWriteDirectoryTagCheckedSlong8Array","SLONG8 not allowed for ClassicTIFF");
+		return(0);
+	}
 	if (tif->tif_flags&TIFF_SWAB)
 		TIFFSwabArrayOfLong8((uint64*)value,count);
 	return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG8,count,count*8,value));
@@ -2403,7 +2563,12 @@
 	dir[m].tdir_count=count;
 	dir[m].tdir_offset.toff_long8 = 0;
 	if (datalength<=((tif->tif_flags&TIFF_BIGTIFF)?0x8U:0x4U))
-		_TIFFmemcpy(&dir[m].tdir_offset,data,datalength);
+        {
+            if( data && datalength )
+            {
+                _TIFFmemcpy(&dir[m].tdir_offset,data,datalength);
+            }
+        }
 	else
 	{
 		uint64 na,nb;
@@ -2796,12 +2961,59 @@
     }
 
 /* -------------------------------------------------------------------- */
+/*      When a dummy tag was written due to TIFFDeferStrileArrayWriting() */
+/* -------------------------------------------------------------------- */
+    if( entry_offset == 0 && entry_count == 0 && entry_type == 0 )
+    {
+        if( tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS )
+        {
+            entry_type = (tif->tif_flags&TIFF_BIGTIFF) ? TIFF_LONG8 : TIFF_LONG; 
+        }
+        else
+        {
+            int write_aslong8 = 1;
+            if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
+            {
+                write_aslong8 = WriteAsLong8(tif, TIFFStripSize64(tif));
+            }
+            else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+            {
+                write_aslong8 = WriteAsLong8(tif, TIFFTileSize64(tif));
+            }
+            if( write_aslong8 )
+            {
+                entry_type = TIFF_LONG8;
+            }
+            else
+            {
+                int write_aslong4 = 1;
+                if( count > 1 && tag == TIFFTAG_STRIPBYTECOUNTS )
+                {
+                    write_aslong4 = WriteAsLong4(tif, TIFFStripSize64(tif));
+                }
+                else if( count > 1 && tag == TIFFTAG_TILEBYTECOUNTS )
+                {
+                    write_aslong4 = WriteAsLong4(tif, TIFFTileSize64(tif));
+                }
+                if( write_aslong4 )
+                {
+                    entry_type = TIFF_LONG;
+                }
+                else
+                {
+                    entry_type = TIFF_SHORT;
+                }
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
 /*      What data type do we want to write this as?                     */
 /* -------------------------------------------------------------------- */
     if( TIFFDataWidth(in_datatype) == 8 && !(tif->tif_flags&TIFF_BIGTIFF) )
     {
         if( in_datatype == TIFF_LONG8 )
-            datatype = TIFF_LONG;
+            datatype = entry_type == TIFF_SHORT ? TIFF_SHORT : TIFF_LONG;
         else if( in_datatype == TIFF_SLONG8 )
             datatype = TIFF_SLONG;
         else if( in_datatype == TIFF_IFD8 )
@@ -2809,8 +3021,21 @@
         else
             datatype = in_datatype;
     }
-    else 
-        datatype = in_datatype;
+    else
+    {
+        if( in_datatype == TIFF_LONG8 &&
+            (entry_type == TIFF_SHORT || entry_type == TIFF_LONG ||
+             entry_type == TIFF_LONG8 ) )
+            datatype = entry_type;
+        else if( in_datatype == TIFF_SLONG8 &&
+            (entry_type == TIFF_SLONG || entry_type == TIFF_SLONG8 ) )
+            datatype = entry_type;
+        else if( in_datatype == TIFF_IFD8 &&
+            (entry_type == TIFF_IFD || entry_type == TIFF_IFD8 ) )
+            datatype = entry_type;
+        else
+            datatype = in_datatype;
+    }
 
 /* -------------------------------------------------------------------- */
 /*      Prepare buffer of actual data to write.  This includes          */
@@ -2859,6 +3084,29 @@
             }
         }
     }
+    else if( datatype == TIFF_SHORT && in_datatype == TIFF_LONG8 )
+    {
+	tmsize_t i;
+
+        for( i = 0; i < count; i++ )
+        {
+            ((uint16 *) buf_to_write)[i] =
+                (uint16) ((uint64 *) data)[i];
+            if( (uint64) ((uint16 *) buf_to_write)[i] != ((uint64 *) data)[i] )
+            {
+                _TIFFfree( buf_to_write );
+                TIFFErrorExt( tif->tif_clientdata, module,
+                              "Value exceeds 16bit range of output type." );
+                return 0;
+            }
+        }
+    }
+    else
+    {
+        TIFFErrorExt( tif->tif_clientdata, module,
+                      "Unhandled type conversion." );
+        return 0;
+    }
 
     if( TIFFDataWidth(datatype) > 1 && (tif->tif_flags&TIFF_SWAB) )
     {
@@ -2890,6 +3138,23 @@
         }
     }
 
+    if( (tag == TIFFTAG_TILEOFFSETS || tag == TIFFTAG_STRIPOFFSETS) &&
+        tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
+        tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 )
+    {
+        tif->tif_dir.td_stripoffset_entry.tdir_type = datatype;
+        tif->tif_dir.td_stripoffset_entry.tdir_count = count;
+    }
+    else if( (tag == TIFFTAG_TILEBYTECOUNTS || tag == TIFFTAG_STRIPBYTECOUNTS) &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+        tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 )
+    {
+        tif->tif_dir.td_stripbytecount_entry.tdir_type = datatype;
+        tif->tif_dir.td_stripbytecount_entry.tdir_count = count;
+    }
+
 /* -------------------------------------------------------------------- */
 /*      If the tag type, and count match, then we just write it out     */
 /*      over the old values without altering the directory entry at     */
@@ -2941,6 +3206,7 @@
 /*      Adjust the directory entry.                                     */
 /* -------------------------------------------------------------------- */
     entry_type = datatype;
+    entry_count = (uint64)count;
     memcpy( direntry_raw + 2, &entry_type, sizeof(uint16) );
     if (tif->tif_flags&TIFF_SWAB)
         TIFFSwabShort( (uint16 *) (direntry_raw + 2) );
diff --git a/third_party/libtiff/tif_dumpmode.c b/third_party/libtiff/tif_dumpmode.c
index a6a94c0..4a0b07f 100644
--- a/third_party/libtiff/tif_dumpmode.c
+++ b/third_party/libtiff/tif_dumpmode.c
@@ -1,5 +1,3 @@
-/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_dumpmode.c,v 1.15 2015-12-12 18:04:26 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_error.c b/third_party/libtiff/tif_error.c
index 0bc8b87..651168f 100644
--- a/third_party/libtiff/tif_error.c
+++ b/third_party/libtiff/tif_error.c
@@ -1,5 +1,3 @@
-/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_error.c,v 1.5 2010-03-10 18:56:48 bfriesen Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -51,24 +49,32 @@
 TIFFError(const char* module, const char* fmt, ...)
 {
 	va_list ap;
-	va_start(ap, fmt);
-	if (_TIFFerrorHandler)
+	if (_TIFFerrorHandler) {
+		va_start(ap, fmt);	
 		(*_TIFFerrorHandler)(module, fmt, ap);
-	if (_TIFFerrorHandlerExt)
+		va_end(ap);
+	}
+	if (_TIFFerrorHandlerExt) {
+		va_start(ap, fmt);
 		(*_TIFFerrorHandlerExt)(0, module, fmt, ap);
-	va_end(ap);
+		va_end(ap);
+	}
 }
 
 void
 TIFFErrorExt(thandle_t fd, const char* module, const char* fmt, ...)
 {
 	va_list ap;
-	va_start(ap, fmt);
-	if (_TIFFerrorHandler)
+	if (_TIFFerrorHandler) {
+		va_start(ap, fmt);
 		(*_TIFFerrorHandler)(module, fmt, ap);
-	if (_TIFFerrorHandlerExt)
+		va_end(ap);
+	}
+	if (_TIFFerrorHandlerExt) {
+		va_start(ap, fmt);
 		(*_TIFFerrorHandlerExt)(fd, module, fmt, ap);
-	va_end(ap);
+		va_end(ap);
+	}
 }
 
 /*
diff --git a/third_party/libtiff/tif_extension.c b/third_party/libtiff/tif_extension.c
index 39fab4c..87d3cfc 100644
--- a/third_party/libtiff/tif_extension.c
+++ b/third_party/libtiff/tif_extension.c
@@ -1,5 +1,3 @@
-/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_extension.c,v 1.8 2015-12-06 11:13:43 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_fax3.c b/third_party/libtiff/tif_fax3.c
index 087cedd..d11c968 100644
--- a/third_party/libtiff/tif_fax3.c
+++ b/third_party/libtiff/tif_fax3.c
@@ -1,5 +1,3 @@
-/* $Id: tif_fax3.c,v 1.80 2017-04-27 19:50:01 erouault Exp $ */
-
 /*
  * Copyright (c) 1990-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -1043,7 +1041,11 @@
 	for (;;) {
 		b2 = finddiff2(rp, b1, bits, PIXEL(rp,b1));
 		if (b2 >= a1) {
-			int32 d = b1 - a1;
+			/* Naive computation triggers -fsanitize=undefined,unsigned-integer-overflow */
+			/* although it is correct unless the difference between both is < 31 bit */
+			/* int32 d = b1 - a1; */
+			int32 d = (b1 >= a1 && b1 - a1 <= 3U) ? (int32)(b1 - a1):
+			          (b1 < a1 && a1 - b1 <= 3U) ? -(int32)(a1 - b1) : 0x7FFFFFFF;
 			if (!(-3 <= d && d <= 3)) {	/* horizontal mode */
 				a2 = finddiff2(bp, a1, bits, PIXEL(bp,a1));
 				putcode(tif, &horizcode);
diff --git a/third_party/libtiff/tif_fax3.h b/third_party/libtiff/tif_fax3.h
index 8a43505..abadcd9 100644
--- a/third_party/libtiff/tif_fax3.h
+++ b/third_party/libtiff/tif_fax3.h
@@ -1,5 +1,3 @@
-/* $Id: tif_fax3.h,v 1.13 2016-12-14 18:36:27 faxguy Exp $ */
-
 /*
  * Copyright (c) 1990-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_flush.c b/third_party/libtiff/tif_flush.c
index fd14e4c..f7fa207 100644
--- a/third_party/libtiff/tif_flush.c
+++ b/third_party/libtiff/tif_flush.c
@@ -1,5 +1,3 @@
-/* $Id: tif_flush.c,v 1.9 2010-03-31 06:40:10 fwarmerdam Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -47,36 +45,8 @@
         && !(tif->tif_flags & TIFF_DIRTYDIRECT) 
         && tif->tif_mode == O_RDWR )
     {
-        uint64  *offsets=NULL, *sizes=NULL;
-
-        if( TIFFIsTiled(tif) )
-        {
-            if( TIFFGetField( tif, TIFFTAG_TILEOFFSETS, &offsets ) 
-                && TIFFGetField( tif, TIFFTAG_TILEBYTECOUNTS, &sizes ) 
-                && _TIFFRewriteField( tif, TIFFTAG_TILEOFFSETS, TIFF_LONG8, 
-                                      tif->tif_dir.td_nstrips, offsets )
-                && _TIFFRewriteField( tif, TIFFTAG_TILEBYTECOUNTS, TIFF_LONG8, 
-                                      tif->tif_dir.td_nstrips, sizes ) )
-            {
-                tif->tif_flags &= ~TIFF_DIRTYSTRIP;
-                tif->tif_flags &= ~TIFF_BEENWRITING;
-                return 1;
-            }
-        }
-        else
-        {
-            if( TIFFGetField( tif, TIFFTAG_STRIPOFFSETS, &offsets ) 
-                && TIFFGetField( tif, TIFFTAG_STRIPBYTECOUNTS, &sizes ) 
-                && _TIFFRewriteField( tif, TIFFTAG_STRIPOFFSETS, TIFF_LONG8, 
-                                      tif->tif_dir.td_nstrips, offsets )
-                && _TIFFRewriteField( tif, TIFFTAG_STRIPBYTECOUNTS, TIFF_LONG8, 
-                                      tif->tif_dir.td_nstrips, sizes ) )
-            {
-                tif->tif_flags &= ~TIFF_DIRTYSTRIP;
-                tif->tif_flags &= ~TIFF_BEENWRITING;
-                return 1;
-            }
-        }
+        if( TIFFForceStrileArrayWriting(tif) )
+            return 1;
     }
 
     if ((tif->tif_flags & (TIFF_DIRTYDIRECT|TIFF_DIRTYSTRIP)) 
@@ -87,6 +57,92 @@
 }
 
 /*
+ * This is an advanced writing function that must be used in a particular
+ * sequence, and together with TIFFDeferStrileArrayWriting(),
+ * to make its intended effect. Its aim is to force the writing of
+ * the [Strip/Tile][Offsets/ByteCounts] arrays at the end of the file, when
+ * they have not yet been rewritten.
+ *
+ * The typical sequence of calls is:
+ * TIFFOpen()
+ * [ TIFFCreateDirectory(tif) ]
+ * Set fields with calls to TIFFSetField(tif, ...)
+ * TIFFDeferStrileArrayWriting(tif)
+ * TIFFWriteCheck(tif, ...)
+ * TIFFWriteDirectory(tif)
+ * ... potentially create other directories and come back to the above directory
+ * TIFFForceStrileArrayWriting(tif)
+ *
+ * Returns 1 in case of success, 0 otherwise.
+ */
+int TIFFForceStrileArrayWriting(TIFF* tif)
+{
+    static const char module[] = "TIFFForceStrileArrayWriting";
+    const int isTiled = TIFFIsTiled(tif);
+
+    if (tif->tif_mode == O_RDONLY)
+    {
+        TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+                     "File opened in read-only mode");
+        return 0;
+    }
+    if( tif->tif_diroff == 0 )
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                     "Directory has not yet been written");
+        return 0;
+    }
+    if( (tif->tif_flags & TIFF_DIRTYDIRECT) != 0 )
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                     "Directory has changes other than the strile arrays. "
+                     "TIFFRewriteDirectory() should be called instead");
+        return 0;
+    }
+
+    if( !(tif->tif_flags & TIFF_DIRTYSTRIP) )
+    {
+        if( !(tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
+             tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
+             tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
+             tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
+             tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
+             tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+             tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+             tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0) )
+        {
+            TIFFErrorExt(tif->tif_clientdata, module,
+                        "Function not called together with "
+                        "TIFFDeferStrileArrayWriting()");
+            return 0;
+        }
+
+        if (tif->tif_dir.td_stripoffset_p == NULL && !TIFFSetupStrips(tif))
+            return 0;
+    }
+
+    if( _TIFFRewriteField( tif,
+                           isTiled ? TIFFTAG_TILEOFFSETS :
+                                     TIFFTAG_STRIPOFFSETS,
+                           TIFF_LONG8,
+                           tif->tif_dir.td_nstrips,
+                           tif->tif_dir.td_stripoffset_p )
+        && _TIFFRewriteField( tif,
+                              isTiled ? TIFFTAG_TILEBYTECOUNTS :
+                                        TIFFTAG_STRIPBYTECOUNTS,
+                              TIFF_LONG8,
+                              tif->tif_dir.td_nstrips,
+                              tif->tif_dir.td_stripbytecount_p ) )
+    {
+        tif->tif_flags &= ~TIFF_DIRTYSTRIP;
+        tif->tif_flags &= ~TIFF_BEENWRITING;
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
  * Flush buffered data to the file.
  *
  * Frank Warmerdam'2000: I modified this to return 1 if TIFF_BEENWRITING
diff --git a/third_party/libtiff/tif_getimage.c b/third_party/libtiff/tif_getimage.c
index d37f729..3a86bcf 100644
--- a/third_party/libtiff/tif_getimage.c
+++ b/third_party/libtiff/tif_getimage.c
@@ -1,5 +1,3 @@
-/* $Id: tif_getimage.c,v 1.106 2017-05-20 11:29:02 erouault Exp $ */
-
 /*
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -139,7 +137,7 @@
 			/*
 			 * TODO: if at all meaningful and useful, make more complete
 			 * support check here, or better still, refactor to let supporting
-			 * code decide whether there is support and what meaningfull
+			 * code decide whether there is support and what meaningful
 			 * error to return
 			 */
 			break;
@@ -417,7 +415,7 @@
 			/*
 			 * TODO: if at all meaningful and useful, make more complete
 			 * support check here, or better still, refactor to let supporting
-			 * code decide whether there is support and what meaningfull
+			 * code decide whether there is support and what meaningful
 			 * error to return
 			 */
 			break;
@@ -792,9 +790,8 @@
 	uint32 leftmost_tw;
 
 	tilesize = TIFFTileSize(tif);  
-	bufsize = TIFFSafeMultiply(tmsize_t,alpha?4:3,tilesize);
+	bufsize = _TIFFMultiplySSize(tif, alpha?4:3,tilesize, "gtTileSeparate");
 	if (bufsize == 0) {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in %s", "gtTileSeparate");
 		return (0);
 	}
 
@@ -962,14 +959,17 @@
 	tmsize_t scanline;
 	int32 fromskew, toskew;
 	int ret = 1, flip;
-	tmsize_t maxstripsize;
+        tmsize_t maxstripsize;
+
+	if ((tmsize_t)img->row_offset > TIFF_SSIZE_T_MAX || (size_t)h > (size_t)TIFF_SSIZE_T_MAX)
+		return (0);
 
 	TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor, &subsamplingver);
 	if( subsamplingver == 0 ) {
 		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Invalid vertical YCbCr subsampling");
 		return (0);
 	}
-
+	
 	maxstripsize = TIFFStripSize(tif);
 
 	flip = setorientation(img);
@@ -987,16 +987,23 @@
 	fromskew = (w < imagewidth ? imagewidth - w : 0);
 	for (row = 0; row < h; row += nrow)
 	{
+		uint32 temp;
 		rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
 		nrow = (row + rowstoread > h ? h - row : rowstoread);
 		nrowsub = nrow;
 		if ((nrowsub%subsamplingver)!=0)
 			nrowsub+=subsamplingver-nrowsub%subsamplingver;
+		temp = (row + img->row_offset)%rowsperstrip + nrowsub;
+		if( scanline > 0 && temp > (size_t)(TIFF_TMSIZE_T_MAX / scanline) )
+		{
+			TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in gtStripContig");
+			return 0;
+		}
 		if (_TIFFReadEncodedStripAndAllocBuffer(tif,
 		    TIFFComputeStrip(tif,row+img->row_offset, 0),
 		    (void**)(&buf),
-		    maxstripsize,
-		    ((row + img->row_offset)%rowsperstrip + nrowsub) * scanline)==(tmsize_t)(-1)
+                    maxstripsize,
+		    temp * scanline)==(tmsize_t)(-1)
 		    && (buf == NULL || img->stoponerr))
 		{
 			ret = 0;
@@ -1056,9 +1063,8 @@
         uint16 colorchannels;
 
 	stripsize = TIFFStripSize(tif);  
-	bufsize = TIFFSafeMultiply(tmsize_t,alpha?4:3,stripsize);
+	bufsize = _TIFFMultiplySSize(tif,alpha?4:3,stripsize, "gtStripSeparate");
 	if (bufsize == 0) {
-		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in %s", "gtStripSeparate");
 		return (0);
 	}
 
@@ -1090,15 +1096,22 @@
 	fromskew = (w < imagewidth ? imagewidth - w : 0);
 	for (row = 0; row < h; row += nrow)
 	{
+                uint32 temp;
 		rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
 		nrow = (row + rowstoread > h ? h - row : rowstoread);
 		offset_row = row + img->row_offset;
+                temp = (row + img->row_offset)%rowsperstrip + nrow;
+                if( scanline > 0 && temp > (size_t)(TIFF_TMSIZE_T_MAX / scanline) )
+                {
+                        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in gtStripSeparate");
+                        return 0;
+                }
                 if( buf == NULL )
                 {
                     if (_TIFFReadEncodedStripAndAllocBuffer(
                             tif, TIFFComputeStrip(tif, offset_row, 0),
                             (void**) &buf, bufsize,
-                            ((row + img->row_offset)%rowsperstrip + nrow) * scanline)==(tmsize_t)(-1)
+                            temp * scanline)==(tmsize_t)(-1)
                         && (buf == NULL || img->stoponerr))
                     {
                             ret = 0;
@@ -1118,7 +1131,7 @@
                     }
                 }
 		else if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
-		    p0, ((row + img->row_offset)%rowsperstrip + nrow) * scanline)==(tmsize_t)(-1)
+		    p0, temp * scanline)==(tmsize_t)(-1)
 		    && img->stoponerr)
 		{
 			ret = 0;
@@ -1126,7 +1139,7 @@
 		}
 		if (colorchannels > 1 
                     && TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 1),
-                                            p1, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) == (tmsize_t)(-1)
+                                            p1, temp * scanline) == (tmsize_t)(-1)
 		    && img->stoponerr)
 		{
 			ret = 0;
@@ -1134,7 +1147,7 @@
 		}
 		if (colorchannels > 1 
                     && TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 2),
-                                            p2, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) == (tmsize_t)(-1)
+                                            p2, temp * scanline) == (tmsize_t)(-1)
 		    && img->stoponerr)
 		{
 			ret = 0;
@@ -1143,7 +1156,7 @@
 		if (alpha)
 		{
 			if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, colorchannels),
-			    pa, ((row + img->row_offset)%rowsperstrip + nrow) * scanline)==(tmsize_t)(-1)
+			    pa, temp * scanline)==(tmsize_t)(-1)
 			    && img->stoponerr)
 			{
 				ret = 0;
@@ -1273,8 +1286,8 @@
     int samplesperpixel = img->samplesperpixel;
 
     (void) y;
-    while (h-- > 0) {
-	for (x = w; x-- > 0;)
+    for( ; h > 0; --h) {
+	for (x = w; x > 0; --x)
         {
 	    *cp++ = PALmap[*pp][0];
             pp += samplesperpixel;
@@ -1293,7 +1306,7 @@
 
     (void) x; (void) y;
     fromskew /= 2;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	uint32* bw;
 	UNROLL2(w, bw = PALmap[*pp++], *cp++ = *bw++);
 	cp += toskew;
@@ -1310,7 +1323,7 @@
 
     (void) x; (void) y;
     fromskew /= 4;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	uint32* bw;
 	UNROLL4(w, bw = PALmap[*pp++], *cp++ = *bw++);
 	cp += toskew;
@@ -1327,7 +1340,7 @@
 
     (void) x; (void) y;
     fromskew /= 8;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	uint32* bw;
 	UNROLL8(w, bw = PALmap[*pp++], *cp++ = *bw++);
 	cp += toskew;
@@ -1344,8 +1357,8 @@
     uint32** BWmap = img->BWmap;
 
     (void) y;
-    while (h-- > 0) {
-	for (x = w; x-- > 0;)
+    for( ; h > 0; --h) {
+	for (x = w; x > 0; --x)
         {
 	    *cp++ = BWmap[*pp][0];
             pp += samplesperpixel;
@@ -1364,8 +1377,8 @@
     uint32** BWmap = img->BWmap;
 
     (void) y;
-    while (h-- > 0) {
-	for (x = w; x-- > 0;)
+    for( ; h > 0; --h) {
+	for (x = w; x > 0; --x)
         {
             *cp++ = BWmap[*pp][0] & ((uint32)*(pp+1) << 24 | ~A1);
             pp += samplesperpixel;
@@ -1384,10 +1397,10 @@
     uint32** BWmap = img->BWmap;
 
     (void) y;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
         uint16 *wp = (uint16 *) pp;
 
-	for (x = w; x-- > 0;)
+	for (x = w; x > 0; --x)
         {
             /* use high order byte of 16bit value */
 
@@ -1409,7 +1422,7 @@
 
     (void) x; (void) y;
     fromskew /= 8;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	uint32* bw;
 	UNROLL8(w, bw = BWmap[*pp++], *cp++ = *bw++);
 	cp += toskew;
@@ -1426,7 +1439,7 @@
 
     (void) x; (void) y;
     fromskew /= 4;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	uint32* bw;
 	UNROLL4(w, bw = BWmap[*pp++], *cp++ = *bw++);
 	cp += toskew;
@@ -1443,7 +1456,7 @@
 
     (void) x; (void) y;
     fromskew /= 2;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	uint32* bw;
 	UNROLL2(w, bw = BWmap[*pp++], *cp++ = *bw++);
 	cp += toskew;
@@ -1460,7 +1473,7 @@
 
     (void) x; (void) y;
     fromskew *= samplesperpixel;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	UNROLL8(w, NOP,
 	    *cp++ = PACK(pp[0], pp[1], pp[2]);
 	    pp += samplesperpixel);
@@ -1479,7 +1492,7 @@
 
     (void) x; (void) y;
     fromskew *= samplesperpixel;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	UNROLL8(w, NOP,
 	    *cp++ = PACK4(pp[0], pp[1], pp[2], pp[3]);
 	    pp += samplesperpixel);
@@ -1497,10 +1510,10 @@
 	int samplesperpixel = img->samplesperpixel;
 	(void) y;
 	fromskew *= samplesperpixel;
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		uint32 r, g, b, a;
 		uint8* m;
-		for (x = w; x-- > 0;) {
+		for (x = w; x > 0; --x) {
 			a = pp[3];
 			m = img->UaToAa+((size_t) a<<8);
 			r = m[pp[0]];
@@ -1523,8 +1536,8 @@
 	uint16 *wp = (uint16 *)pp;
 	(void) y;
 	fromskew *= samplesperpixel;
-	while (h-- > 0) {
-		for (x = w; x-- > 0;) {
+	for( ; h > 0; --h) {
+		for (x = w; x > 0; --x) {
 			*cp++ = PACK(img->Bitdepth16To8[wp[0]],
 			    img->Bitdepth16To8[wp[1]],
 			    img->Bitdepth16To8[wp[2]]);
@@ -1545,8 +1558,8 @@
 	uint16 *wp = (uint16 *)pp;
 	(void) y;
 	fromskew *= samplesperpixel;
-	while (h-- > 0) {
-		for (x = w; x-- > 0;) {
+	for( ; h > 0; --h) {
+		for (x = w; x > 0; --x) {
 			*cp++ = PACK4(img->Bitdepth16To8[wp[0]],
 			    img->Bitdepth16To8[wp[1]],
 			    img->Bitdepth16To8[wp[2]],
@@ -1568,10 +1581,10 @@
 	uint16 *wp = (uint16 *)pp;
 	(void) y;
 	fromskew *= samplesperpixel;
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		uint32 r,g,b,a;
 		uint8* m;
-		for (x = w; x-- > 0;) {
+		for (x = w; x > 0; --x) {
 			a = img->Bitdepth16To8[wp[3]];
 			m = img->UaToAa+((size_t) a<<8);
 			r = m[img->Bitdepth16To8[wp[0]]];
@@ -1597,7 +1610,7 @@
 
     (void) x; (void) y;
     fromskew *= samplesperpixel;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	UNROLL8(w, NOP,
 	    k = 255 - pp[3];
 	    r = (k*(255-pp[0]))/255;
@@ -1623,8 +1636,8 @@
 
     (void) y;
     fromskew *= samplesperpixel;
-    while (h-- > 0) {
-	for (x = w; x-- > 0;) {
+    for( ; h > 0; --h) {
+	for (x = w; x > 0; --x) {
 	    k = 255 - pp[3];
 	    r = (k*(255-pp[0]))/255;
 	    g = (k*(255-pp[1]))/255;
@@ -1653,7 +1666,7 @@
 DECLARESepPutFunc(putRGBseparate8bittile)
 {
     (void) img; (void) x; (void) y; (void) a;
-    while (h-- > 0) {
+    for( ; h > 0; --h) {
 	UNROLL8(w, NOP, *cp++ = PACK(*r++, *g++, *b++));
 	SKEW(r, g, b, fromskew);
 	cp += toskew;
@@ -1666,7 +1679,7 @@
 DECLARESepPutFunc(putRGBAAseparate8bittile)
 {
 	(void) img; (void) x; (void) y; 
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		UNROLL8(w, NOP, *cp++ = PACK4(*r++, *g++, *b++, *a++));
 		SKEW4(r, g, b, a, fromskew);
 		cp += toskew;
@@ -1679,9 +1692,9 @@
 DECLARESepPutFunc(putCMYKseparate8bittile)
 {
 	(void) img; (void) y;
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		uint32 rv, gv, bv, kv;
-		for (x = w; x-- > 0;) {
+		for (x = w; x > 0; --x) {
 			kv = 255 - *a++;
 			rv = (kv*(255-*r++))/255;
 			gv = (kv*(255-*g++))/255;
@@ -1699,10 +1712,10 @@
 DECLARESepPutFunc(putRGBUAseparate8bittile)
 {
 	(void) img; (void) y;
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		uint32 rv, gv, bv, av;
 		uint8* m;
-		for (x = w; x-- > 0;) {
+		for (x = w; x > 0; --x) {
 			av = *a++;
 			m = img->UaToAa+((size_t) av<<8);
 			rv = m[*r++];
@@ -1724,7 +1737,7 @@
 	uint16 *wg = (uint16*) g;
 	uint16 *wb = (uint16*) b;
 	(void) img; (void) y; (void) a;
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		for (x = 0; x < w; x++)
 			*cp++ = PACK(img->Bitdepth16To8[*wr++],
 			    img->Bitdepth16To8[*wg++],
@@ -1744,7 +1757,7 @@
 	uint16 *wb = (uint16*) b;
 	uint16 *wa = (uint16*) a;
 	(void) img; (void) y;
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		for (x = 0; x < w; x++)
 			*cp++ = PACK4(img->Bitdepth16To8[*wr++],
 			    img->Bitdepth16To8[*wg++],
@@ -1765,10 +1778,10 @@
 	uint16 *wb = (uint16*) b;
 	uint16 *wa = (uint16*) a;
 	(void) img; (void) y;
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		uint32 r2,g2,b2,a2;
 		uint8* m;
-		for (x = w; x-- > 0;) {
+		for (x = w; x > 0; --x) {
 			a2 = img->Bitdepth16To8[*wa++];
 			m = img->UaToAa+((size_t) a2<<8);
 			r2 = m[img->Bitdepth16To8[*wr++]];
@@ -1790,8 +1803,8 @@
 	uint32 r, g, b;
 	(void) y;
 	fromskew *= 3;
-	while (h-- > 0) {
-		for (x = w; x-- > 0;) {
+	for( ; h > 0; --h) {
+		for (x = w; x > 0; --x) {
 			TIFFCIELabToXYZ(img->cielab,
 					(unsigned char)pp[0],
 					(signed char)pp[1],
@@ -1904,7 +1917,7 @@
 
     (void) y;
     /* adjust fromskew */
-    fromskew = (fromskew * 18) / 4;
+    fromskew = (fromskew / 4) * (4*2+2);
     if ((h & 3) == 0 && (w & 3) == 0) {				        
         for (; h >= 4; h -= 4) {
             x = w>>2;
@@ -2007,7 +2020,7 @@
     int32 incr = 2*toskew+w;
 
     (void) y;
-    fromskew = (fromskew * 10) / 4;
+    fromskew = (fromskew / 4) * (4*2+2);
     if ((w & 3) == 0 && (h & 1) == 0) {
         for (; h >= 2; h -= 2) {
             x = w>>2;
@@ -2085,7 +2098,7 @@
 DECLAREContigPutFunc(putcontig8bitYCbCr41tile)
 {
     (void) y;
-    /* XXX adjust fromskew */
+    fromskew = (fromskew / 4) * (4*1+2);
     do {
 	x = w>>2;
 	while(x>0) {
@@ -2132,7 +2145,7 @@
 	uint32* cp2;
 	int32 incr = 2*toskew+w;
 	(void) y;
-	fromskew = (fromskew / 2) * 6;
+	fromskew = (fromskew / 2) * (2*2+2);
 	cp2 = cp+w+toskew;
 	while (h>=2) {
 		x = w;
@@ -2188,7 +2201,7 @@
 DECLAREContigPutFunc(putcontig8bitYCbCr21tile)
 {
 	(void) y;
-	fromskew = (fromskew * 4) / 2;
+	fromskew = (fromskew / 2) * (2*1+2);
 	do {
 		x = w>>1;
 		while(x>0) {
@@ -2227,7 +2240,7 @@
 	uint32* cp2;
 	int32 incr = 2*toskew+w;
 	(void) y;
-	fromskew = (fromskew / 2) * 4;
+	fromskew = (fromskew / 1) * (1 * 2 + 2);
 	cp2 = cp+w+toskew;
 	while (h>=2) {
 		x = w;
@@ -2263,7 +2276,7 @@
 DECLAREContigPutFunc(putcontig8bitYCbCr11tile)
 {
 	(void) y;
-	fromskew *= 3;
+	fromskew = (fromskew / 1) * (1 * 1 + 2);
 	do {
 		x = w; /* was x = w>>1; patched 2000/09/25 warmerda@home.com */
 		do {
@@ -2287,7 +2300,7 @@
 	(void) y;
 	(void) a;
 	/* TODO: naming of input vars is still off, change obfuscating declaration inside define, or resolve obfuscation */
-	while (h-- > 0) {
+	for( ; h > 0; --h) {
 		x = w;
 		do {
 			uint32 dr, dg, db;
@@ -2302,7 +2315,7 @@
 
 static int isInRefBlackWhiteRange(float f)
 {
-    return f >= (float)(-0x7FFFFFFF + 128) && f <= (float)0x7FFFFFFF;
+    return f > (float)(-0x7FFFFFFF + 128) && f < (float)0x7FFFFFFF;
 }
 
 static int
@@ -2367,6 +2380,13 @@
 	float   *whitePoint;
 	float   refWhite[3];
 
+	TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint);
+	if (whitePoint[1] == 0.0f ) {
+		TIFFErrorExt(img->tif->tif_clientdata, module,
+		    "Invalid value for WhitePoint tag.");
+		return NULL;
+        }
+
 	if (!img->cielab) {
 		img->cielab = (TIFFCIELabToRGB *)
 			_TIFFmalloc(sizeof(TIFFCIELabToRGB));
@@ -2377,7 +2397,6 @@
 		}
 	}
 
-	TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint);
 	refWhite[1] = 100.0F;
 	refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1];
 	refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1])
@@ -2988,7 +3007,7 @@
     if( !TIFFIsTiled( tif ) )
     {
 		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
-				  "Can't use TIFFReadRGBATile() with stripped file.");
+				  "Can't use TIFFReadRGBATile() with striped file.");
 		return (0);
     }
     
diff --git a/third_party/libtiff/tif_jpeg.c b/third_party/libtiff/tif_jpeg.c
index df06e03..3b1ed9f 100644
--- a/third_party/libtiff/tif_jpeg.c
+++ b/third_party/libtiff/tif_jpeg.c
@@ -1,5 +1,3 @@
-/* $Id: tif_jpeg.c,v 1.127 2017-01-31 13:02:27 erouault Exp $ */
-
 /*
  * Copyright (c) 1994-1997 Sam Leffler
  * Copyright (c) 1994-1997 Silicon Graphics, Inc.
@@ -28,6 +26,8 @@
 #define VC_EXTRALEAN
 
 #include "tiffiop.h"
+#include <stdlib.h>
+
 #ifdef JPEG_SUPPORT
 
 /*
@@ -47,6 +47,7 @@
 int TIFFFillStrip(TIFF* tif, uint32 strip);
 int TIFFFillTile(TIFF* tif, uint32 tile);
 int TIFFReInitJPEG_12( TIFF *tif, int scheme, int is_encode );
+int TIFFJPEGIsFullStripRequired_12(TIFF* tif);
 
 /* We undefine FAR to avoid conflict with JPEG definition */
 
@@ -73,7 +74,7 @@
    "JPEGLib: JPEG parameter struct mismatch: library thinks size is 432,
    caller expects 464"
 
-   For such users we wil fix the problem here. See install.doc file from
+   For such users we will fix the problem here. See install.doc file from
    the JPEG library distribution for details.
 */
 
@@ -153,6 +154,8 @@
 
 	jpeg_error_mgr	err;		/* libjpeg error manager */
 	JMP_BUF		exit_jmpbuf;	/* for catching libjpeg failures */
+	
+	struct jpeg_progress_mgr progress;
 	/*
 	 * The following two members could be a union, but
 	 * they're small enough that it's not worth the effort.
@@ -183,6 +186,7 @@
 	int		jpegtablesmode;	/* What to put in JPEGTables */
 
         int             ycbcrsampling_fetched;
+        int             max_allowed_scan_number;
 } JPEGState;
 
 #define	JState(tif)	((JPEGState*)(tif)->tif_data)
@@ -243,6 +247,33 @@
 	TIFFWarningExt(((JPEGState *) cinfo)->tif->tif_clientdata, "JPEGLib", "%s", buffer);
 }
 
+/* Avoid the risk of denial-of-service on crafted JPEGs with an insane */
+/* number of scans. */
+/* See http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf */
+static void
+TIFFjpeg_progress_monitor(j_common_ptr cinfo)
+{
+    JPEGState *sp = (JPEGState *) cinfo;	/* NB: cinfo assumed first */
+    if (cinfo->is_decompressor)
+    {
+        const int scan_no =
+            ((j_decompress_ptr)cinfo)->input_scan_number;
+        if (scan_no >= sp->max_allowed_scan_number)
+        {
+            TIFFErrorExt(((JPEGState *) cinfo)->tif->tif_clientdata, 
+                     "TIFFjpeg_progress_monitor",
+                     "Scan number %d exceeds maximum scans (%d). This limit "
+                     "can be raised through the LIBTIFF_JPEG_MAX_ALLOWED_SCAN_NUMBER "
+                     "environment variable.",
+                     scan_no, sp->max_allowed_scan_number);
+
+            jpeg_abort(cinfo);			/* clean up libjpeg state */
+            LONGJMP(sp->exit_jmpbuf, 1);		/* return to libtiff caller */
+        }
+    }
+}
+
+
 /*
  * Interface routines.  This layer of routines exists
  * primarily to limit side-effects from using setjmp.
@@ -345,8 +376,23 @@
 }
 
 static int
+TIFFjpeg_has_multiple_scans(JPEGState* sp)
+{
+	return CALLJPEG(sp, 0, jpeg_has_multiple_scans(&sp->cinfo.d));
+}
+
+static int
 TIFFjpeg_start_decompress(JPEGState* sp)
 {
+        const char* sz_max_allowed_scan_number;
+        /* progress monitor */
+        sp->cinfo.d.progress = &sp->progress;
+        sp->progress.progress_monitor = TIFFjpeg_progress_monitor;
+        sp->max_allowed_scan_number = 100;
+        sz_max_allowed_scan_number = getenv("LIBTIFF_JPEG_MAX_ALLOWED_SCAN_NUMBER");
+        if( sz_max_allowed_scan_number )
+            sp->max_allowed_scan_number = atoi(sz_max_allowed_scan_number);
+
 	return CALLVJPEG(sp, jpeg_start_decompress(&sp->cinfo.d));
 }
 
@@ -597,9 +643,8 @@
 }
 
 static void
-TIFFjpeg_data_src(JPEGState* sp, TIFF* tif)
+TIFFjpeg_data_src(JPEGState* sp)
 {
-	(void) tif;
 	sp->cinfo.d.src = &sp->src;
 	sp->src.init_source = std_init_source;
 	sp->src.fill_input_buffer = std_fill_input_buffer;
@@ -625,9 +670,9 @@
 }
 
 static void
-TIFFjpeg_tables_src(JPEGState* sp, TIFF* tif)
+TIFFjpeg_tables_src(JPEGState* sp)
 {
-	TIFFjpeg_data_src(sp, tif);
+	TIFFjpeg_data_src(sp);
 	sp->src.init_source = tables_init_source;
 }
 
@@ -743,12 +788,9 @@
 	 */
 	static const char module[] = "JPEGFixupTagsSubsampling";
 	struct JPEGFixupTagsSubsamplingData m;
+        uint64 fileoffset = TIFFGetStrileOffset(tif, 0);
 
-        _TIFFFillStriles( tif );
-        
-        if( tif->tif_dir.td_stripbytecount == NULL
-            || tif->tif_dir.td_stripoffset == NULL
-            || tif->tif_dir.td_stripbytecount[0] == 0 )
+        if( fileoffset == 0 )
         {
             /* Do not even try to check if the first strip/tile does not
                yet exist, as occurs when GDAL has created a new NULL file
@@ -767,9 +809,9 @@
 	}
 	m.buffercurrentbyte=NULL;
 	m.bufferbytesleft=0;
-	m.fileoffset=tif->tif_dir.td_stripoffset[0];
+	m.fileoffset=fileoffset;
 	m.filepositioned=0;
-	m.filebytesleft=tif->tif_dir.td_stripbytecount[0];
+	m.filebytesleft=TIFFGetStrileByteCount(tif, 0);
 	if (!JPEGFixupTagsSubsamplingSec(&m))
 		TIFFWarningExt(tif->tif_clientdata,module,
 		    "Unable to auto-correct subsampling values, likely corrupt JPEG compressed data in first strip/tile; auto-correcting skipped");
@@ -984,7 +1026,7 @@
 
 	/* Read JPEGTables if it is present */
 	if (TIFFFieldSet(tif,FIELD_JPEGTABLES)) {
-		TIFFjpeg_tables_src(sp, tif);
+		TIFFjpeg_tables_src(sp);
 		if(TIFFjpeg_read_header(sp,FALSE) != JPEG_HEADER_TABLES_ONLY) {
 			TIFFErrorExt(tif->tif_clientdata, "JPEGSetupDecode", "Bogus JPEGTables field");
 			return (0);
@@ -1006,11 +1048,47 @@
 	}
 
 	/* Set up for reading normal data */
-	TIFFjpeg_data_src(sp, tif);
+	TIFFjpeg_data_src(sp);
 	tif->tif_postdecode = _TIFFNoPostDecode; /* override byte swapping */
 	return (1);
 }
 
+/* Returns 1 if the full strip should be read, even when doing scanline per */
+/* scanline decoding. This happens when the JPEG stream uses multiple scans. */
+/* Currently only called in CHUNKY_STRIP_READ_SUPPORT mode through */
+/* scanline interface. */
+/* Only reads tif->tif_dir.td_bitspersample, tif->tif_rawdata and */
+/* tif->tif_rawcc members. */
+/* Can be called independently of the usual setup/predecode/decode states */
+int TIFFJPEGIsFullStripRequired(TIFF* tif)
+{
+    int ret;
+    JPEGState state;
+
+#if defined(JPEG_DUAL_MODE_8_12) && !defined(TIFFJPEGIsFullStripRequired)
+    if( tif->tif_dir.td_bitspersample == 12 )
+        return TIFFJPEGIsFullStripRequired_12( tif );
+#endif
+
+    memset(&state, 0, sizeof(JPEGState));
+    state.tif = tif;
+
+    TIFFjpeg_create_decompress(&state);
+
+    TIFFjpeg_data_src(&state);
+
+    if (TIFFjpeg_read_header(&state, TRUE) != JPEG_HEADER_OK)
+    {
+        TIFFjpeg_destroy(&state);
+        return (0);
+    }
+    ret = TIFFjpeg_has_multiple_scans(&state);
+
+    TIFFjpeg_destroy(&state);
+
+    return ret;
+}
+
 /*
  * Set up for decoding a strip or tile.
  */
@@ -1051,13 +1129,13 @@
 	/*
 	 * Check image parameters and set decompression parameters.
 	 */
-	segment_width = td->td_imagewidth;
-	segment_height = td->td_imagelength - tif->tif_row;
 	if (isTiled(tif)) {
                 segment_width = td->td_tilewidth;
                 segment_height = td->td_tilelength;
 		sp->bytesperline = TIFFTileRowSize(tif);
 	} else {
+		segment_width = td->td_imagewidth;
+		segment_height = td->td_imagelength - tif->tif_row;
 		if (segment_height > td->td_rowsperstrip)
 			segment_height = td->td_rowsperstrip;
 		sp->bytesperline = TIFFScanlineSize(tif);
@@ -1078,9 +1156,23 @@
 			       segment_width, segment_height,
 			       sp->cinfo.d.image_width,
 			       sp->cinfo.d.image_height);
-	} 
-	if (sp->cinfo.d.image_width > segment_width ||
-	    sp->cinfo.d.image_height > segment_height) {
+	}
+	if( sp->cinfo.d.image_width == segment_width &&
+	    sp->cinfo.d.image_height > segment_height &&
+	    tif->tif_row + segment_height == td->td_imagelength &&
+	    !isTiled(tif) ) {
+		/* Some files have a last strip, that should be truncated, */
+		/* but their JPEG codestream has still the maximum strip */
+		/* height. Warn about this as this is non compliant, but */
+		/* we can safely recover from that. */
+		TIFFWarningExt(tif->tif_clientdata, module,
+			     "JPEG strip size exceeds expected dimensions,"
+			     " expected %dx%d, got %dx%d",
+			     segment_width, segment_height,
+			     sp->cinfo.d.image_width, sp->cinfo.d.image_height);
+	}
+	else if (sp->cinfo.d.image_width > segment_width ||
+		 sp->cinfo.d.image_height > segment_height) {
 		/*
 		 * This case could be dangerous, if the strip or tile size has
 		 * been reported as less than the amount of data jpeg will
@@ -1113,6 +1205,47 @@
 		return (0);
 	}
 #endif
+
+        /* In some cases, libjpeg needs to allocate a lot of memory */
+        /* http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf */
+        if( TIFFjpeg_has_multiple_scans(sp) )
+        {
+            /* In this case libjpeg will need to allocate memory or backing */
+            /* store for all coefficients */
+            /* See call to jinit_d_coef_controller() from master_selection() */
+            /* in libjpeg */
+            toff_t nRequiredMemory = (toff_t)sp->cinfo.d.image_width *
+                                     sp->cinfo.d.image_height *
+                                     sp->cinfo.d.num_components *
+                                     ((td->td_bitspersample+7)/8);
+            /* BLOCK_SMOOTHING_SUPPORTED is generally defined, so we need */
+            /* to replicate the logic of jinit_d_coef_controller() */
+            if( sp->cinfo.d.progressive_mode )
+                nRequiredMemory *= 3;
+
+#ifndef TIFF_LIBJPEG_LARGEST_MEM_ALLOC
+#define TIFF_LIBJPEG_LARGEST_MEM_ALLOC (100 * 1024 * 1024)
+#endif
+
+            if( nRequiredMemory > TIFF_LIBJPEG_LARGEST_MEM_ALLOC &&
+                getenv("LIBTIFF_ALLOW_LARGE_LIBJPEG_MEM_ALLOC") == NULL )
+            {
+                    TIFFErrorExt(tif->tif_clientdata, module,
+                        "Reading this strip would require libjpeg to allocate "
+                        "at least %u bytes. "
+                        "This is disabled since above the %u threshold. "
+                        "You may override this restriction by defining the "
+                        "LIBTIFF_ALLOW_LARGE_LIBJPEG_MEM_ALLOC environment variable, "
+                        "or recompile libtiff by defining the "
+                        "TIFF_LIBJPEG_LARGEST_MEM_ALLOC macro to a value greater "
+                        "than %u",
+                        (unsigned)nRequiredMemory,
+                        (unsigned)TIFF_LIBJPEG_LARGEST_MEM_ALLOC,
+                        (unsigned)TIFF_LIBJPEG_LARGEST_MEM_ALLOC);
+                    return (0);
+            }
+        }
+
 	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
 		/* Component 0 should have expected sampling factors */
 		if (sp->cinfo.d.comp_info[0].h_samp_factor != sp->h_sampling ||
@@ -1374,10 +1507,18 @@
 {
 	JPEGState *sp = JState(tif);
 	tmsize_t nrows;
+        TIFFDirectory *td = &tif->tif_dir;
 	(void) s;
 
+        nrows = sp->cinfo.d.image_height;
+        /* For last strip, limit number of rows to its truncated height */
+        /* even if the codestream height is larger (which is not compliant, */
+        /* but that we tolerate) */
+        if( (uint32)nrows > td->td_imagelength - tif->tif_row && !isTiled(tif) )
+            nrows = td->td_imagelength - tif->tif_row;
+
 	/* data is expected to be read in multiples of a scanline */
-	if ( (nrows = sp->cinfo.d.image_height) != 0 ) {
+	if ( nrows != 0 ) {
 
 		/* Cb,Cr both have sampling factors 1, so this is correct */
 		JDIMENSION clumps_per_line = sp->cinfo.d.comp_info[1].downsampled_width;            
@@ -1430,7 +1571,7 @@
 					JSAMPLE *outptr = (JSAMPLE*)tmpbuf + clumpoffset;
 #else
 					JSAMPLE *outptr = (JSAMPLE*)buf + clumpoffset;
-					if (cc < (tmsize_t) (clumpoffset + samples_per_clump*(clumps_per_line-1) + hsamp)) {
+					if (cc < (tmsize_t)(clumpoffset + (tmsize_t)samples_per_clump*(clumps_per_line-1) + hsamp)) {
 						TIFFErrorExt(tif->tif_clientdata, "JPEGDecodeRaw",
 							     "application buffer not large enough for all data, possible subsampling issue");
 						return 0;
@@ -1990,8 +2131,8 @@
 	/* data is expected to be supplied in multiples of a clumpline */
 	/* a clumpline is equivalent to v_sampling desubsampled scanlines */
 	/* TODO: the following calculation of bytesperclumpline, should substitute calculation of sp->bytesperline, except that it is per v_sampling lines */
-	bytesperclumpline = (((sp->cinfo.c.image_width+sp->h_sampling-1)/sp->h_sampling)
-			     *(sp->h_sampling*sp->v_sampling+2)*sp->cinfo.c.data_precision+7)
+	bytesperclumpline = ((((tmsize_t)sp->cinfo.c.image_width+sp->h_sampling-1)/sp->h_sampling)
+			     *((tmsize_t)sp->h_sampling*sp->v_sampling+2)*sp->cinfo.c.data_precision+7)
 			    /8;
 
 	nrows = ( cc / bytesperclumpline ) * sp->v_sampling;
@@ -2318,12 +2459,22 @@
 #ifndef TIFF_JPEG_MAX_MEMORY_TO_USE
 #define TIFF_JPEG_MAX_MEMORY_TO_USE (10 * 1024 * 1024)
 #endif
-        /* Increase the max memory usable. This helps when creating files */
-        /* with "big" tile, without using libjpeg temporary files. */
-        /* For example a 512x512 tile with 3 bands */
-        /* requires 1.5 MB which is above libjpeg 1MB default */
-        if( sp->cinfo.c.mem->max_memory_to_use < TIFF_JPEG_MAX_MEMORY_TO_USE )
-            sp->cinfo.c.mem->max_memory_to_use = TIFF_JPEG_MAX_MEMORY_TO_USE;
+        /* libjpeg turbo 1.5.2 honours max_memory_to_use, but has no backing */
+        /* store implementation, so better not set max_memory_to_use ourselves. */
+        /* See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/162 */
+        if( sp->cinfo.c.mem->max_memory_to_use > 0 )
+        {
+            /* This is to address bug related in ticket GDAL #1795. */
+            if (getenv("JPEGMEM") == NULL)
+            {
+                /* Increase the max memory usable. This helps when creating files */
+                /* with "big" tile, without using libjpeg temporary files. */
+                /* For example a 512x512 tile with 3 bands */
+                /* requires 1.5 MB which is above libjpeg 1MB default */
+                if( sp->cinfo.c.mem->max_memory_to_use < TIFF_JPEG_MAX_MEMORY_TO_USE )
+                    sp->cinfo.c.mem->max_memory_to_use = TIFF_JPEG_MAX_MEMORY_TO_USE;
+            }
+        }
     }
 
     sp->cinfo_initialized = TRUE;
diff --git a/third_party/libtiff/tif_luv.c b/third_party/libtiff/tif_luv.c
index 59d0a74..6fe4858 100644
--- a/third_party/libtiff/tif_luv.c
+++ b/third_party/libtiff/tif_luv.c
@@ -1,5 +1,3 @@
-/* $Id: tif_luv.c,v 1.47 2017-05-14 10:17:27 erouault Exp $ */
-
 /*
  * Copyright (c) 1997 Greg Ward Larson
  * Copyright (c) 1997 Silicon Graphics, Inc.
@@ -215,7 +213,7 @@
 	bp = (unsigned char*) tif->tif_rawcp;
 	cc = tif->tif_rawcc;
 	/* get each byte string */
-	for (shft = 2*8; (shft -= 8) >= 0; ) {
+	for (shft = 8; shft >= 0; shft -=8) {
 		for (i = 0; i < npixels && cc > 0; ) {
 			if (*bp >= 128) {		/* run */
 				if( cc < 2 )
@@ -349,7 +347,7 @@
 	bp = (unsigned char*) tif->tif_rawcp;
 	cc = tif->tif_rawcc;
 	/* get each byte string */
-	for (shft = 4*8; (shft -= 8) >= 0; ) {
+	for (shft = 24; shft >= 0; shft -=8) {
 		for (i = 0; i < npixels && cc > 0; ) {
 			if (*bp >= 128) {		/* run */
 				if( cc < 2 )
@@ -467,7 +465,7 @@
 	/* compress each byte string */
 	op = tif->tif_rawcp;
 	occ = tif->tif_rawdatasize - tif->tif_rawcc;
-	for (shft = 2*8; (shft -= 8) >= 0; )
+	for (shft = 8; shft >= 0; shft -=8) {
 		for (i = 0; i < npixels; i += rc) {
 			if (occ < 4) {
 				tif->tif_rawcp = op;
@@ -522,6 +520,7 @@
 			} else
 				rc = 0;
 		}
+	}
 	tif->tif_rawcp = op;
 	tif->tif_rawcc = tif->tif_rawdatasize - occ;
 
@@ -618,7 +617,7 @@
 	/* compress each byte string */
 	op = tif->tif_rawcp;
 	occ = tif->tif_rawdatasize - tif->tif_rawcc;
-	for (shft = 4*8; (shft -= 8) >= 0; )
+	for (shft = 24; shft >= 0; shft -=8) {
 		for (i = 0; i < npixels; i += rc) {
 			if (occ < 4) {
 				tif->tif_rawcp = op;
@@ -673,6 +672,7 @@
 			} else
 				rc = 0;
 		}
+	}
 	tif->tif_rawcp = op;
 	tif->tif_rawcc = tif->tif_rawdatasize - occ;
 
@@ -742,9 +742,14 @@
 #undef exp2  /* Conflict with C'99 function */
 #define exp2(x)		exp(M_LN2*(x))
 
-#define itrunc(x,m)	((m)==SGILOGENCODE_NODITHER ? \
-				(int)(x) : \
-				(int)((x) + rand()*(1./RAND_MAX) - .5))
+static int itrunc(double x, int m)
+{
+    if( m == SGILOGENCODE_NODITHER )
+        return (int)x;
+    /* Silence CoverityScan warning about bad crypto function */
+    /* coverity[dont_call] */
+    return (int)(x + rand()*(1./RAND_MAX) - .5);
+}
 
 #if !LOGLUV_PUBLIC
 static
@@ -1264,16 +1269,10 @@
 	return (SGILOGDATAFMT_UNKNOWN);
 }
 
-
-#define TIFF_SIZE_T_MAX ((size_t) ~ ((size_t)0))
-#define TIFF_TMSIZE_T_MAX (tmsize_t)(TIFF_SIZE_T_MAX >> 1)
-
 static tmsize_t
 multiply_ms(tmsize_t m1, tmsize_t m2)
 {
-        if( m1 == 0 || m2 > TIFF_TMSIZE_T_MAX / m1 )
-            return 0;
-        return m1 * m2;
+        return _TIFFMultiplySSize(NULL, m1, m2, NULL);
 }
 
 static int
@@ -1314,7 +1313,7 @@
 	}
         if( isTiled(tif) )
             sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength);
-        else if( td->td_rowsperstrip != (uint32)-1 )
+        else if( td->td_rowsperstrip < td->td_imagelength )
             sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip);
         else
             sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_imagelength);
@@ -1416,8 +1415,10 @@
 	}
         if( isTiled(tif) )
             sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength);
-        else
+        else if( td->td_rowsperstrip < td->td_imagelength )
             sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip);
+        else
+            sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_imagelength);
 	if (multiply_ms(sp->tbuflen, sizeof (uint32)) == 0 ||
 	    (sp->tbuf = (uint8*) _TIFFmalloc(sp->tbuflen * sizeof (uint32))) == NULL) {
 		TIFFErrorExt(tif->tif_clientdata, module, "No space for SGILog translation buffer");
@@ -1505,7 +1506,7 @@
 	switch (td->td_photometric) {
 	case PHOTOMETRIC_LOGLUV:
 		if (!LogLuvInitState(tif))
-			break;
+			return (0);
 		if (td->td_compression == COMPRESSION_SGILOG24) {
 			tif->tif_encoderow = LogLuvEncode24;
 			switch (sp->user_datafmt) {
@@ -1538,7 +1539,7 @@
 		break;
 	case PHOTOMETRIC_LOGL:
 		if (!LogL16InitState(tif))
-			break;
+			return (0);
 		tif->tif_encoderow = LogL16Encode;  
 		switch (sp->user_datafmt) {
 		case SGILOGDATAFMT_FLOAT:
@@ -1554,7 +1555,7 @@
 		TIFFErrorExt(tif->tif_clientdata, module,
 		    "Inappropriate photometric interpretation %d for SGILog compression; %s",
 		    td->td_photometric, "must be either LogLUV or LogL");
-		break;
+		return (0);
 	}
 	sp->encoder_state = 1;
 	return (1);
diff --git a/third_party/libtiff/tif_lzw.c b/third_party/libtiff/tif_lzw.c
index 5f1acf8..21064f2 100644
--- a/third_party/libtiff/tif_lzw.c
+++ b/third_party/libtiff/tif_lzw.c
@@ -1,5 +1,3 @@
-/* $Id: tif_lzw.c,v 1.55 2017-05-17 09:38:58 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -135,6 +133,7 @@
 	long    dec_restart;		/* restart count */
 #ifdef LZW_CHECKEOS
 	uint64  dec_bitsleft;		/* available bits in raw data */
+	tmsize_t old_tif_rawcc;         /* value of tif_rawcc at the end of the previous TIFLZWDecode() call */
 #endif
 	decodeFunc dec_decode;		/* regular or backwards compatible */
 	code_t* dec_codep;		/* current recognized code */
@@ -248,6 +247,8 @@
 		/*
 		 * Zero-out the unused entries
                  */
+                /* Silence false positive */
+                /* coverity[overrun-buffer-arg] */
                  _TIFFmemset(&sp->dec_codetab[CODE_CLEAR], 0,
 			     (CODE_FIRST - CODE_CLEAR) * sizeof (code_t));
 	}
@@ -275,7 +276,8 @@
 	/*
 	 * Check for old bit-reversed codes.
 	 */
-	if (tif->tif_rawdata[0] == 0 && (tif->tif_rawdata[1] & 0x1)) {
+	if (tif->tif_rawcc >= 2 &&
+	    tif->tif_rawdata[0] == 0 && (tif->tif_rawdata[1] & 0x1)) {
 #ifdef LZW_COMPAT
 		if (!sp->dec_decode) {
 			TIFFWarningExt(tif->tif_clientdata, module,
@@ -319,6 +321,7 @@
 	sp->dec_nbitsmask = MAXCODE(BITS_MIN);
 #ifdef LZW_CHECKEOS
 	sp->dec_bitsleft = 0;
+        sp->old_tif_rawcc = 0;
 #endif
 	sp->dec_free_entp = sp->dec_codetab + CODE_FIRST;
 	/*
@@ -426,7 +429,7 @@
 
 	bp = (unsigned char *)tif->tif_rawcp;
 #ifdef LZW_CHECKEOS
-	sp->dec_bitsleft = (((uint64)tif->tif_rawcc) << 3);
+	sp->dec_bitsleft += (((uint64)tif->tif_rawcc - sp->old_tif_rawcc) << 3);
 #endif
 	nbits = sp->lzw_nbits;
 	nextdata = sp->lzw_nextdata;
@@ -554,6 +557,9 @@
 
 	tif->tif_rawcc -= (tmsize_t)( (uint8*) bp - tif->tif_rawcp );
 	tif->tif_rawcp = (uint8*) bp;
+#ifdef LZW_CHECKEOS
+	sp->old_tif_rawcc = tif->tif_rawcc;
+#endif
 	sp->lzw_nbits = (unsigned short) nbits;
 	sp->lzw_nextdata = nextdata;
 	sp->lzw_nextbits = nextbits;
@@ -603,6 +609,7 @@
 	char *tp;
 	unsigned char *bp;
 	int code, nbits;
+	int len;
 	long nextbits, nextdata, nbitsmask;
 	code_t *codep, *free_entp, *maxcodep, *oldcodep;
 
@@ -655,6 +662,9 @@
 	}
 
 	bp = (unsigned char *)tif->tif_rawcp;
+#ifdef LZW_CHECKEOS
+	sp->dec_bitsleft += (((uint64)tif->tif_rawcc - sp->old_tif_rawcc) << 3);
+#endif
 	nbits = sp->lzw_nbits;
 	nextdata = sp->lzw_nextdata;
 	nextbits = sp->lzw_nextbits;
@@ -751,20 +761,29 @@
 				}  while (--occ);
 				break;
 			}
-			assert(occ >= codep->length);
-			op += codep->length;
-			occ -= codep->length;
-			tp = op;
+			len = codep->length;
+			tp = op + len;
 			do {
-				*--tp = codep->value;
-			} while( (codep = codep->next) != NULL );
+				int t;
+				--tp;
+				t = codep->value;
+				codep = codep->next;
+				*tp = (char)t;
+			} while (codep && tp > op);
+			assert(occ >= len);
+			op += len;
+			occ -= len;
 		} else {
 			*op++ = (char)code;
 			occ--;
 		}
 	}
 
+	tif->tif_rawcc -= (tmsize_t)( (uint8*) bp - tif->tif_rawcp );
 	tif->tif_rawcp = (uint8*) bp;
+#ifdef LZW_CHECKEOS
+	sp->old_tif_rawcc = tif->tif_rawcc;
+#endif
 	sp->lzw_nbits = (unsigned short)nbits;
 	sp->lzw_nextdata = nextdata;
 	sp->lzw_nextbits = nextbits;
diff --git a/third_party/libtiff/tif_next.c b/third_party/libtiff/tif_next.c
index 0821178..0ba61ae 100644
--- a/third_party/libtiff/tif_next.c
+++ b/third_party/libtiff/tif_next.c
@@ -1,5 +1,3 @@
-/* $Id: tif_next.c,v 1.19 2016-09-04 21:32:56 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_ojpeg.c b/third_party/libtiff/tif_ojpeg.c
index 60e4eca..9982272 100644
--- a/third_party/libtiff/tif_ojpeg.c
+++ b/third_party/libtiff/tif_ojpeg.c
@@ -1,5 +1,3 @@
-/* $Id: tif_ojpeg.c,v 1.69 2017-04-27 17:29:26 erouault Exp $ */
-
 /* WARNING: The type of JPEG encapsulation defined by the TIFF Version 6.0
    specification is now totally obsolete and deprecated for new applications and
    images. This file was was created solely in order to read unconverted images
@@ -254,6 +252,7 @@
 typedef struct {
 	TIFF* tif;
         int decoder_ok;
+        int error_in_raw_data_decoding;
 	#ifndef LIBJPEG_ENCAP_EXTERNAL
 	JMP_BUF exit_jmpbuf;
 	#endif
@@ -689,7 +688,7 @@
 		if (OJPEGReadSecondarySos(tif,s)==0)
 			return(0);
 	}
-	if isTiled(tif)
+	if (isTiled(tif))
 		m=tif->tif_curtile;
 	else
 		m=tif->tif_curstrip;
@@ -753,6 +752,7 @@
 		}
 		m-=sp->subsampling_convert_clines-sp->subsampling_convert_state;
 		sp->subsampling_convert_state=0;
+                sp->error_in_raw_data_decoding=0;
 	}
 	while (m>=sp->subsampling_convert_clines)
 	{
@@ -803,6 +803,10 @@
             TIFFErrorExt(tif->tif_clientdata,module,"Cannot decode: decoder not correctly initialized");
             return 0;
         }
+        if( sp->error_in_raw_data_decoding )
+        {
+            return 0;
+        }
 	if (sp->libjpeg_jpeg_query_style==0)
 	{
 		if (OJPEGDecodeRaw(tif,buf,cc)==0)
@@ -842,8 +846,41 @@
 	{
 		if (sp->subsampling_convert_state==0)
 		{
-			if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0)
+			const jpeg_decompress_struct* cinfo = &sp->libjpeg_jpeg_decompress_struct;
+			int width = 0;
+			int last_col_width = 0;
+			int jpeg_bytes;
+			int expected_bytes;
+			int i;
+			if (cinfo->MCUs_per_row == 0)
+			{
+				sp->error_in_raw_data_decoding = 1;
+				return 0;
+			}
+			for (i = 0; i < cinfo->comps_in_scan; ++i)
+			{
+				const jpeg_component_info* info = cinfo->cur_comp_info[i];
+#if JPEG_LIB_VERSION >= 70
+				width += info->MCU_width * info->DCT_h_scaled_size;
+				last_col_width += info->last_col_width * info->DCT_h_scaled_size;
+#else
+				width += info->MCU_width * info->DCT_scaled_size;
+				last_col_width += info->last_col_width * info->DCT_scaled_size;
+#endif
+			}
+			jpeg_bytes = (cinfo->MCUs_per_row - 1) * width + last_col_width;
+			expected_bytes = sp->subsampling_convert_clinelenout * sp->subsampling_ver * sp->subsampling_hor;
+			if (jpeg_bytes != expected_bytes)
+			{
+				TIFFErrorExt(tif->tif_clientdata,module,"Inconsistent number of MCU in codestream");
+				sp->error_in_raw_data_decoding = 1;
 				return(0);
+			}
+			if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0)
+			{
+				sp->error_in_raw_data_decoding = 1;
+				return(0);
+			}
 		}
 		oy=sp->subsampling_convert_ybuf+sp->subsampling_convert_state*sp->subsampling_ver*sp->subsampling_convert_ylinelen;
 		ocb=sp->subsampling_convert_cbbuf+sp->subsampling_convert_state*sp->subsampling_convert_clinelen;
@@ -1001,7 +1038,6 @@
 	OJPEGState* sp=(OJPEGState*)tif->tif_data;
 	uint8 mh;
 	uint8 mv;
-        _TIFFFillStriles( tif );
         
 	assert(sp->subsamplingcorrect_done==0);
 	if ((tif->tif_dir.td_samplesperpixel!=3) || ((tif->tif_dir.td_photometric!=PHOTOMETRIC_YCBCR) &&
@@ -1057,7 +1093,7 @@
 	assert(sp->readheader_done==0);
 	sp->image_width=tif->tif_dir.td_imagewidth;
 	sp->image_length=tif->tif_dir.td_imagelength;
-	if isTiled(tif)
+	if (isTiled(tif))
 	{
 		sp->strile_width=tif->tif_dir.td_tilewidth;
 		sp->strile_length=tif->tif_dir.td_tilelength;
@@ -1093,6 +1129,12 @@
 	}
 	if (sp->strile_length<sp->image_length)
 	{
+		if (((sp->subsampling_hor!=1) && (sp->subsampling_hor!=2) && (sp->subsampling_hor!=4)) ||
+		    ((sp->subsampling_ver!=1) && (sp->subsampling_ver!=2) && (sp->subsampling_ver!=4)))
+		{
+			TIFFErrorExt(tif->tif_clientdata,module,"Invalid subsampling values");
+			return(0);
+		}
 		if (sp->strile_length%(sp->subsampling_ver*8)!=0)
 		{
 			TIFFErrorExt(tif->tif_clientdata,module,"Incompatible vertical subsampling and image strip/tile length");
@@ -1208,7 +1250,13 @@
 			sp->subsampling_convert_ybuflen=sp->subsampling_convert_ylinelen*sp->subsampling_convert_ylines;
 			sp->subsampling_convert_cbuflen=sp->subsampling_convert_clinelen*sp->subsampling_convert_clines;
 			sp->subsampling_convert_ycbcrbuflen=sp->subsampling_convert_ybuflen+2*sp->subsampling_convert_cbuflen;
-			sp->subsampling_convert_ycbcrbuf=_TIFFmalloc(sp->subsampling_convert_ycbcrbuflen);
+                        /* The calloc is not normally necessary, except in some edge/broken cases */
+                        /* for example for a tiled image of height 1 with a tile height of 1 and subsampling_hor=subsampling_ver=2 */
+                        /* In that case, libjpeg will only fill the 8 first lines of the 16 lines */
+                        /* See https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=16844 */
+                        /* Even if this case is allowed (?), its handling is broken because OJPEGPreDecode() should also likely */
+                        /* reset subsampling_convert_state to 0 when changing tile. */
+			sp->subsampling_convert_ycbcrbuf=_TIFFcalloc(1, sp->subsampling_convert_ycbcrbuflen);
 			if (sp->subsampling_convert_ycbcrbuf==0)
 			{
 				TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
@@ -1234,10 +1282,11 @@
 				*m++=sp->subsampling_convert_cbbuf+n*sp->subsampling_convert_clinelen;
 			for (n=0; n<sp->subsampling_convert_clines; n++)
 				*m++=sp->subsampling_convert_crbuf+n*sp->subsampling_convert_clinelen;
-			sp->subsampling_convert_clinelenout=((sp->strile_width+sp->subsampling_hor-1)/sp->subsampling_hor);
+			sp->subsampling_convert_clinelenout=sp->strile_width/sp->subsampling_hor + ((sp->strile_width % sp->subsampling_hor) != 0 ? 1 : 0);
 			sp->subsampling_convert_state=0;
+			sp->error_in_raw_data_decoding=0;
 			sp->bytes_per_line=sp->subsampling_convert_clinelenout*(sp->subsampling_ver*sp->subsampling_hor+2);
-			sp->lines_per_strile=((sp->strile_length+sp->subsampling_ver-1)/sp->subsampling_ver);
+			sp->lines_per_strile=sp->strile_length/sp->subsampling_ver + ((sp->strile_length % sp->subsampling_ver) != 0 ? 1 : 0);
 			sp->subsampling_convert_log=1;
 		}
 	}
@@ -1283,7 +1332,9 @@
 		}
 		else
 		{
-			if ((sp->jpeg_interchange_format_length==0) || (sp->jpeg_interchange_format+sp->jpeg_interchange_format_length>sp->file_size))
+			if ((sp->jpeg_interchange_format_length==0) ||
+                            (sp->jpeg_interchange_format > TIFF_UINT64_MAX - sp->jpeg_interchange_format_length) ||
+                            (sp->jpeg_interchange_format+sp->jpeg_interchange_format_length>sp->file_size))
 				sp->jpeg_interchange_format_length=sp->file_size-sp->jpeg_interchange_format;
 		}
 	}
@@ -2000,32 +2051,30 @@
 				sp->in_buffer_source=osibsStrile;
                                 break;
 			case osibsStrile:
-				if (!_TIFFFillStriles( sp->tif ) 
-				    || sp->tif->tif_dir.td_stripoffset == NULL
-				    || sp->tif->tif_dir.td_stripbytecount == NULL)
-					return 0;
-
 				if (sp->in_buffer_next_strile==sp->in_buffer_strile_count)
 					sp->in_buffer_source=osibsEof;
 				else
 				{
-					sp->in_buffer_file_pos=sp->tif->tif_dir.td_stripoffset[sp->in_buffer_next_strile];
+					int err = 0;
+					sp->in_buffer_file_pos=TIFFGetStrileOffsetWithErr(sp->tif, sp->in_buffer_next_strile, &err);
+					if( err )
+						return 0;
 					if (sp->in_buffer_file_pos!=0)
 					{
+						uint64 bytecount = TIFFGetStrileByteCountWithErr(sp->tif, sp->in_buffer_next_strile, &err);
+						if( err )
+							return 0;
 						if (sp->in_buffer_file_pos>=sp->file_size)
 							sp->in_buffer_file_pos=0;
-						else if (sp->tif->tif_dir.td_stripbytecount==NULL)
+						else if (bytecount==0)
 							sp->in_buffer_file_togo=sp->file_size-sp->in_buffer_file_pos;
 						else
 						{
-							if (sp->tif->tif_dir.td_stripbytecount == 0) {
-								TIFFErrorExt(sp->tif->tif_clientdata,sp->tif->tif_name,"Strip byte counts are missing");
-								return(0);
-							}
-							sp->in_buffer_file_togo=sp->tif->tif_dir.td_stripbytecount[sp->in_buffer_next_strile];
+							sp->in_buffer_file_togo=bytecount;
 							if (sp->in_buffer_file_togo==0)
 								sp->in_buffer_file_pos=0;
-							else if (sp->in_buffer_file_pos+sp->in_buffer_file_togo>sp->file_size)
+							else if (sp->in_buffer_file_pos > TIFF_UINT64_MAX - sp->in_buffer_file_togo || 
+                                                                sp->in_buffer_file_pos+sp->in_buffer_file_togo>sp->file_size)
 								sp->in_buffer_file_togo=sp->file_size-sp->in_buffer_file_pos;
 						}
 					}
diff --git a/third_party/libtiff/tif_open.c b/third_party/libtiff/tif_open.c
index a7279e1..3cb53d4 100644
--- a/third_party/libtiff/tif_open.c
+++ b/third_party/libtiff/tif_open.c
@@ -1,5 +1,3 @@
-/* $Id: tif_open.c,v 1.48 2016-11-20 22:29:47 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -133,6 +131,7 @@
 	if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) {
 		TIFFErrorExt(clientdata, module,
 		    "One of the client procedures is NULL pointer.");
+		_TIFFfree(tif);
 		goto bad2;
 	}
 	tif->tif_readproc = readproc;
@@ -183,6 +182,8 @@
 	 * 'h' read TIFF header only, do not load the first IFD
 	 * '4' ClassicTIFF for creating a file (default)
 	 * '8' BigTIFF for creating a file
+         * 'D' enable use of deferred strip/tile offset/bytecount array loading.
+         * 'O' on-demand loading of values instead of whole array loading (implies D)
 	 *
 	 * The use of the 'l' and 'b' flags is strongly discouraged.
 	 * These flags are provided solely because numerous vendors,
@@ -264,7 +265,22 @@
 				if (m&O_CREAT)
 					tif->tif_flags |= TIFF_BIGTIFF;
 				break;
+			case 'D':
+			        tif->tif_flags |= TIFF_DEFERSTRILELOAD;
+				break;
+			case 'O':
+				if( m == O_RDONLY )
+					tif->tif_flags |= (TIFF_LAZYSTRILELOAD | TIFF_DEFERSTRILELOAD);
+				break;
 		}
+
+#ifdef DEFER_STRILE_LOAD
+        /* Compatibility with old DEFER_STRILE_LOAD compilation flag */
+        /* Probably unneeded, since to the best of my knowledge (E. Rouault) */
+        /* GDAL was the only user of this, and will now use the new 'D' flag */
+        tif->tif_flags |= TIFF_DEFERSTRILELOAD;
+#endif
+
 	/*
 	 * Read in TIFF header.
 	 */
diff --git a/third_party/libtiff/tif_packbits.c b/third_party/libtiff/tif_packbits.c
index 18904b0..a8f29e8 100644
--- a/third_party/libtiff/tif_packbits.c
+++ b/third_party/libtiff/tif_packbits.c
@@ -1,5 +1,3 @@
-/* $Id: tif_packbits.c,v 1.26 2017-05-14 02:26:07 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_pixarlog.c b/third_party/libtiff/tif_pixarlog.c
index f226395..6264090 100644
--- a/third_party/libtiff/tif_pixarlog.c
+++ b/third_party/libtiff/tif_pixarlog.c
@@ -1,5 +1,3 @@
-/* $Id: tif_pixarlog.c,v 1.53 2017-05-17 09:53:06 erouault Exp $ */
-
 /*
  * Copyright (c) 1996-1997 Sam Leffler
  * Copyright (c) 1996 Pixar
@@ -636,20 +634,16 @@
 	return guess;
 }
 
-#define TIFF_SIZE_T_MAX ((size_t) ~ ((size_t)0))
-#define TIFF_TMSIZE_T_MAX (tmsize_t)(TIFF_SIZE_T_MAX >> 1)
-
 static tmsize_t
 multiply_ms(tmsize_t m1, tmsize_t m2)
 {
-        if( m1 == 0 || m2 > TIFF_TMSIZE_T_MAX / m1 )
-            return 0;
-        return m1 * m2;
+        return _TIFFMultiplySSize(NULL, m1, m2, NULL);
 }
 
 static tmsize_t
 add_ms(tmsize_t m1, tmsize_t m2)
 {
+        assert(m1 >= 0 && m2 >= 0);
 	/* if either input is zero, assume overflow already occurred */
 	if (m1 == 0 || m2 == 0)
 		return 0;
@@ -673,6 +667,7 @@
 	TIFFDirectory *td = &tif->tif_dir;
 	PixarLogState* sp = DecoderState(tif);
 	tmsize_t tbuf_size;
+        uint32 strip_height;
 
 	assert(sp != NULL);
 
@@ -682,6 +677,10 @@
 	if( (sp->state & PLSTATE_INIT) != 0 )
 		return 1;
 
+        strip_height = td->td_rowsperstrip;
+        if( strip_height > td->td_imagelength )
+            strip_height = td->td_imagelength;
+
 	/* Make sure no byte swapping happens on the data
 	 * after decompression. */
 	tif->tif_postdecode = _TIFFNoPostDecode;  
@@ -691,7 +690,7 @@
 	sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ?
 	    td->td_samplesperpixel : 1);
 	tbuf_size = multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth),
-				      td->td_rowsperstrip), sizeof(uint16));
+				      strip_height), sizeof(uint16));
 	/* add one more stride in case input ends mid-stride */
 	tbuf_size = add_ms(tbuf_size, sizeof(uint16) * sp->stride);
 	if (tbuf_size == 0)
@@ -814,9 +813,7 @@
 			TIFFErrorExt(tif->tif_clientdata, module,
 			    "Decoding error at scanline %lu, %s",
 			    (unsigned long) tif->tif_row, sp->stream.msg ? sp->stream.msg : "(null)");
-			if (inflateSync(&sp->stream) != Z_OK)
-				return (0);
-			continue;
+			return (0);
 		}
 		if (state != Z_OK) {
 			TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s",
@@ -1150,7 +1147,7 @@
 
 	llen = sp->stride * td->td_imagewidth;
     /* Check against the number of elements (of size uint16) of sp->tbuf */
-    if( n > (tmsize_t)(td->td_rowsperstrip * llen) )
+    if( n > ((tmsize_t)td->td_rowsperstrip * llen) )
     {
         TIFFErrorExt(tif->tif_clientdata, module,
                      "Too many input bytes provided");
diff --git a/third_party/libtiff/tif_predict.c b/third_party/libtiff/tif_predict.c
index 7a60a39..b775663 100644
--- a/third_party/libtiff/tif_predict.c
+++ b/third_party/libtiff/tif_predict.c
@@ -1,5 +1,3 @@
-/* $Id: tif_predict.c,v 1.43 2017-05-10 15:21:16 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -277,6 +275,7 @@
 /* - when storing into the byte stream, we explicitly mask with 0xff so */
 /*   as to make icc -check=conversions happy (not necessary by the standard) */
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 static int
 horAcc8(TIFF* tif, uint8* cp0, tmsize_t cc)
 {
@@ -344,6 +343,7 @@
         return horAcc16(tif, cp0, cc);
 }
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 static int
 horAcc16(TIFF* tif, uint8* cp0, tmsize_t cc)
 {
@@ -378,6 +378,7 @@
 	return horAcc32(tif, cp0, cc);
 }
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 static int
 horAcc32(TIFF* tif, uint8* cp0, tmsize_t cc)
 {
@@ -503,6 +504,7 @@
 		return 0;
 }
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 static int
 horDiff8(TIFF* tif, uint8* cp0, tmsize_t cc)
 {
@@ -556,6 +558,7 @@
 	return 1;
 }
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 static int
 horDiff16(TIFF* tif, uint8* cp0, tmsize_t cc)
 {
@@ -595,6 +598,7 @@
     return 1;
 }
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 static int
 horDiff32(TIFF* tif, uint8* cp0, tmsize_t cc)
 {
@@ -637,6 +641,7 @@
 /*
  * Floating point predictor differencing routine.
  */
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
 static int
 fpDiff(TIFF* tif, uint8* cp0, tmsize_t cc)
 {
diff --git a/third_party/libtiff/tif_predict.h b/third_party/libtiff/tif_predict.h
index 6c68e21..a326b9b 100644
--- a/third_party/libtiff/tif_predict.h
+++ b/third_party/libtiff/tif_predict.h
@@ -1,5 +1,3 @@
-/* $Id: tif_predict.h,v 1.9 2016-10-31 17:24:26 erouault Exp $ */
-
 /*
  * Copyright (c) 1995-1997 Sam Leffler
  * Copyright (c) 1995-1997 Silicon Graphics, Inc.
@@ -26,6 +24,10 @@
 
 #ifndef _TIFFPREDICT_
 #define	_TIFFPREDICT_
+
+#include "tiffio.h"
+#include "tiffiop.h"
+
 /*
  * ``Library-private'' Support for the Predictor Tag
  */
diff --git a/third_party/libtiff/tif_print.c b/third_party/libtiff/tif_print.c
index 24d4b98..a073794 100644
--- a/third_party/libtiff/tif_print.c
+++ b/third_party/libtiff/tif_print.c
@@ -1,5 +1,3 @@
-/* $Id: tif_print.c,v 1.65 2016-11-20 22:31:22 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -546,7 +544,7 @@
 				uint16 i;
 				fprintf(fd, "    %2ld: %5u",
 				    l, td->td_transferfunction[0][l]);
-				for (i = 1; i < td->td_samplesperpixel; i++)
+				for (i = 1; i < td->td_samplesperpixel - td->td_extrasamples && i < 3; i++)
 					fprintf(fd, " %5u",
 					    td->td_transferfunction[i][l]);
 				fputc('\n', fd);
@@ -654,8 +652,6 @@
 	if (tif->tif_tagmethods.printdir)
 		(*tif->tif_tagmethods.printdir)(tif, fd, flags);
 
-        _TIFFFillStriles( tif );
-        
 	if ((flags & TIFFPRINT_STRIPS) &&
 	    TIFFFieldSet(tif,FIELD_STRIPOFFSETS)) {
 		uint32 s;
@@ -667,13 +663,13 @@
 #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
 			fprintf(fd, "    %3lu: [%8I64u, %8I64u]\n",
 			    (unsigned long) s,
-			    (unsigned __int64) td->td_stripoffset[s],
-			    (unsigned __int64) td->td_stripbytecount[s]);
+			    (unsigned __int64) TIFFGetStrileOffset(tif, s),
+			    (unsigned __int64) TIFFGetStrileByteCount(tif, s));
 #else
 			fprintf(fd, "    %3lu: [%8llu, %8llu]\n",
 			    (unsigned long) s,
-			    (unsigned long long) td->td_stripoffset[s],
-			    (unsigned long long) td->td_stripbytecount[s]);
+			    (unsigned long long) TIFFGetStrileOffset(tif, s),
+			    (unsigned long long) TIFFGetStrileByteCount(tif, s));
 #endif
 	}
 }
diff --git a/third_party/libtiff/tif_read.c b/third_party/libtiff/tif_read.c
index ad0a778..d1acf33 100644
--- a/third_party/libtiff/tif_read.c
+++ b/third_party/libtiff/tif_read.c
@@ -1,5 +1,3 @@
-/* $Id: tif_read.c,v 1.59 2017-05-13 15:34:06 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -31,9 +29,6 @@
 #include "tiffiop.h"
 #include <stdio.h>
 
-#define TIFF_SIZE_T_MAX ((size_t) ~ ((size_t)0))
-#define TIFF_TMSIZE_T_MAX (tmsize_t)(TIFF_SIZE_T_MAX >> 1)
-
 int TIFFFillStrip(TIFF* tif, uint32 strip);
 int TIFFFillTile(TIFF* tif, uint32 tile);
 static int TIFFStartStrip(TIFF* tif, uint32 strip);
@@ -51,6 +46,8 @@
 #define THRESHOLD_MULTIPLIER 10
 #define MAX_THRESHOLD (THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * THRESHOLD_MULTIPLIER * INITIAL_THRESHOLD)
 
+#define TIFF_INT64_MAX ((((int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF)
+
 /* Read 'size' bytes in tif_rawdata buffer starting at offset 'rawdata_offset'
  * Returns 1 in case of success, 0 otherwise. */
 static int TIFFReadAndRealloc( TIFF* tif, tmsize_t size,
@@ -58,11 +55,27 @@
                                int is_strip, uint32 strip_or_tile,
                                const char* module )
 {
-#if SIZEOF_VOIDP == 8 || SIZEOF_SIZE_T == 8
+#if SIZEOF_SIZE_T == 8
         tmsize_t threshold = INITIAL_THRESHOLD;
 #endif
         tmsize_t already_read = 0;
 
+
+#if SIZEOF_SIZE_T != 8
+        /* On 32 bit processes, if the request is large enough, check against */
+        /* file size */
+        if( size > 1000 * 1000 * 1000 )
+        {
+            uint64 filesize = TIFFGetFileSize(tif);
+            if( (uint64)size >= filesize )
+            {
+                TIFFErrorExt(tif->tif_clientdata, module,
+                             "Chunk size requested is larger than file size.");
+                return 0;
+            }
+        }
+#endif
+
         /* On 64 bit processes, read first a maximum of 1 MB, then 10 MB, etc */
         /* so as to avoid allocating too much memory in case the file is too */
         /* short. We could ask for the file size, but this might be */
@@ -73,7 +86,7 @@
         {
             tmsize_t bytes_read;
             tmsize_t to_read = size - already_read;
-#if SIZEOF_VOIDP == 8 || SIZEOF_SIZE_T == 8
+#if SIZEOF_SIZE_T == 8
             if( to_read >= threshold && threshold < MAX_THRESHOLD &&
                 already_read + to_read + rawdata_offset > tif->tif_rawdatasize )
             {
@@ -105,6 +118,11 @@
                 }
                 tif->tif_rawdata = new_rawdata;
             }
+            if( tif->tif_rawdata == NULL )
+            {
+                /* should not happen in practice but helps CoverityScan */
+                return 0;
+            }
 
             bytes_read = TIFFReadFile(tif,
                 tif->tif_rawdata + rawdata_offset + already_read, to_read);
@@ -172,17 +190,14 @@
         tmsize_t to_read;
         tmsize_t read_ahead_mod;
         /* tmsize_t bytecountm; */
-        
-        if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount)
-            return 0;
-        
+
         /*
          * Expand raw data buffer, if needed, to hold data
          * strip coming from file (perhaps should set upper
          * bound on the size of a buffer we'll use?).
          */
 
-        /* bytecountm=(tmsize_t) td->td_stripbytecount[strip]; */
+        /* bytecountm=(tmsize_t) TIFFGetStrileByteCount(tif, strip); */
 
         /* Not completely sure where the * 2 comes from, but probably for */
         /* an exponentional growth strategy of tif_rawdatasize */
@@ -226,7 +241,7 @@
         /*
         ** Seek to the point in the file where more data should be read.
         */
-        read_offset = td->td_stripoffset[strip]
+        read_offset = TIFFGetStrileOffset(tif, strip)
                 + tif->tif_rawdataoff + tif->tif_rawdataloaded;
 
         if (!SeekOK(tif, read_offset)) {
@@ -243,10 +258,10 @@
                 to_read = read_ahead_mod - unused_data;
         else
                 to_read = tif->tif_rawdatasize - unused_data;
-        if( (uint64) to_read > td->td_stripbytecount[strip] 
+        if( (uint64) to_read > TIFFGetStrileByteCount(tif, strip)
             - tif->tif_rawdataoff - tif->tif_rawdataloaded )
         {
-                to_read = (tmsize_t) td->td_stripbytecount[strip]
+                to_read = (tmsize_t) TIFFGetStrileByteCount(tif, strip)
                         - tif->tif_rawdataoff - tif->tif_rawdataloaded;
         }
 
@@ -262,6 +277,7 @@
         tif->tif_rawdataoff = tif->tif_rawdataoff + tif->tif_rawdataloaded - unused_data ;
         tif->tif_rawdataloaded = unused_data + to_read;
 
+        tif->tif_rawcc = tif->tif_rawdataloaded;
         tif->tif_rawcp = tif->tif_rawdata;
                         
         if (!isFillOrder(tif, td->td_fillorder) &&
@@ -275,10 +291,28 @@
         ** restart the decoder.
         */
         if( restart )
-                return TIFFStartStrip(tif, strip);
+        {
+
+#ifdef JPEG_SUPPORT
+            /* A bit messy since breaks the codec abstraction. Ultimately */
+            /* there should be a function pointer for that, but it seems */
+            /* only JPEG is affected. */
+            /* For JPEG, if there are multiple scans (can generally be known */
+            /* with the  read_ahead used), we need to read the whole strip */
+            if( tif->tif_dir.td_compression==COMPRESSION_JPEG &&
+                (uint64)tif->tif_rawcc < TIFFGetStrileByteCount(tif, strip) )
+            {
+                if( TIFFJPEGIsFullStripRequired(tif) )
+                {
+                    return TIFFFillStrip(tif, strip);
+                }
+            }
+#endif
+
+            return TIFFStartStrip(tif, strip);
+        }
         else
         {
-                tif->tif_rawcc = tif->tif_rawdataloaded;
                 return 1;
         }
 }
@@ -325,10 +359,15 @@
          * read it a few lines at a time?
          */
 #if defined(CHUNKY_STRIP_READ_SUPPORT)
-        if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount)
-            return 0;
-        whole_strip = tif->tif_dir.td_stripbytecount[strip] < 10
+        whole_strip = TIFFGetStrileByteCount(tif, strip) < 10
                 || isMapped(tif);
+        if( td->td_compression == COMPRESSION_LERC ||
+            td->td_compression == COMPRESSION_JBIG )
+        {
+            /* Ideally plugins should have a way to declare they don't support
+             * chunk strip */
+            whole_strip = 1;
+        }
 #else
         whole_strip = 1;
 #endif
@@ -373,7 +412,7 @@
         else if( !whole_strip )
         {
                 if( ((tif->tif_rawdata + tif->tif_rawdataloaded) - tif->tif_rawcp) < read_ahead 
-                    && (uint64) tif->tif_rawdataoff+tif->tif_rawdataloaded < td->td_stripbytecount[strip] )
+                    && (uint64) tif->tif_rawdataoff+tif->tif_rawdataloaded < TIFFGetStrileByteCount(tif, strip) )
                 {
                         if( !TIFFFillStripPartial(tif,strip,read_ahead,0) )
                                 return 0;
@@ -477,7 +516,7 @@
 	stripsize=TIFFVStripSize(tif,rows);
 	if (stripsize==0)
 		return((tmsize_t)(-1));
-    return stripsize;
+	return stripsize;
 }
 
 /*
@@ -487,14 +526,14 @@
 tmsize_t
 TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
 {
-  static const char module[] = "TIFFReadEncodedStrip";
-  TIFFDirectory *td = &tif->tif_dir;
-  tmsize_t stripsize;
-  uint16 plane;
+	static const char module[] = "TIFFReadEncodedStrip";
+	TIFFDirectory *td = &tif->tif_dir;
+	tmsize_t stripsize;
+	uint16 plane;
 
-  stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
-  if (stripsize==((tmsize_t)(-1)))
-      return((tmsize_t)(-1));
+	stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
+	if (stripsize==((tmsize_t)(-1)))
+		return((tmsize_t)(-1));
 
     /* shortcut to avoid an extra memcpy() */
     if( td->td_compression == COMPRESSION_NONE &&
@@ -525,7 +564,7 @@
 
 /* Variant of TIFFReadEncodedStrip() that does 
  * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillStrip() has
- *   suceeded. This avoid excessive memory allocation in case of truncated
+ *   succeeded. This avoid excessive memory allocation in case of truncated
  *   file.
  * * calls regular TIFFReadEncodedStrip() if *buf != NULL
  */
@@ -534,6 +573,8 @@
                                     void **buf, tmsize_t bufsizetoalloc,
                                     tmsize_t size_to_read)
 {
+    assert(size_to_read > 0);
+
     tmsize_t this_stripsize;
     uint16 plane;
 
@@ -566,21 +607,15 @@
 
 }
 
-
 static tmsize_t
 TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size,
     const char* module)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-
-    if (!_TIFFFillStriles( tif ))
-        return ((tmsize_t)(-1));
-        
 	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
 	if (!isMapped(tif)) {
 		tmsize_t cc;
 
-		if (!SeekOK(tif, td->td_stripoffset[strip])) {
+		if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip))) {
 			TIFFErrorExt(tif->tif_clientdata, module,
 			    "Seek error at scanline %lu, strip %lu",
 			    (unsigned long) tif->tif_row, (unsigned long) strip);
@@ -606,8 +641,8 @@
 	} else {
 		tmsize_t ma = 0;
 		tmsize_t n;
-		if ((td->td_stripoffset[strip] > (uint64)TIFF_TMSIZE_T_MAX)||
-                    ((ma=(tmsize_t)td->td_stripoffset[strip])>tif->tif_size))
+		if ((TIFFGetStrileOffset(tif, strip) > (uint64)TIFF_TMSIZE_T_MAX)||
+                    ((ma=(tmsize_t)TIFFGetStrileOffset(tif, strip))>tif->tif_size))
                 {
                     n=0;
                 }
@@ -651,12 +686,10 @@
 TIFFReadRawStripOrTile2(TIFF* tif, uint32 strip_or_tile, int is_strip,
                         tmsize_t size, const char* module)
 {
-        TIFFDirectory *td = &tif->tif_dir;
-
         assert( !isMapped(tif) );
         assert((tif->tif_flags&TIFF_NOREADRAW)==0);
 
-        if (!SeekOK(tif, td->td_stripoffset[strip_or_tile])) {
+        if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip_or_tile))) {
             if( is_strip )
             {
                 TIFFErrorExt(tif->tif_clientdata, module,
@@ -692,7 +725,7 @@
 {
 	static const char module[] = "TIFFReadRawStrip";
 	TIFFDirectory *td = &tif->tif_dir;
-	uint64 bytecount;
+	uint64 bytecount64;
 	tmsize_t bytecountm;
 
 	if (!TIFFCheckRead(tif, 0))
@@ -710,31 +743,23 @@
 		    "Compression scheme does not support access to raw uncompressed data");
 		return ((tmsize_t)(-1));
 	}
-	bytecount = td->td_stripbytecount[strip];
-	if ((int64)bytecount <= 0) {
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "%I64u: Invalid strip byte count, strip %lu",
-			     (unsigned __int64) bytecount,
-			     (unsigned long) strip);
-#else
-		TIFFErrorExt(tif->tif_clientdata, module,
-			     "%llu: Invalid strip byte count, strip %lu",
-			     (unsigned long long) bytecount,
-			     (unsigned long) strip);
-#endif
-		return ((tmsize_t)(-1));
-	}
-	bytecountm = (tmsize_t)bytecount;
-	if ((uint64)bytecountm!=bytecount) {
-		TIFFErrorExt(tif->tif_clientdata, module, "Integer overflow");
-		return ((tmsize_t)(-1));
-	}
-	if (size != (tmsize_t)(-1) && size < bytecountm)
+	bytecount64 = TIFFGetStrileByteCount(tif, strip);
+	if (size != (tmsize_t)(-1) && (uint64)size <= bytecount64)
 		bytecountm = size;
+	else
+		bytecountm = _TIFFCastUInt64ToSSize(tif, bytecount64, module);
+	if( bytecountm == 0 ) {
+		return ((tmsize_t)(-1));
+	}
 	return (TIFFReadRawStrip1(tif, strip, buf, bytecountm, module));
 }
 
+TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
+static uint64 NoSantizeSubUInt64(uint64 a, uint64 b)
+{
+    return a - b;
+}
+
 /*
  * Read the specified strip and setup for decoding. The data buffer is
  * expanded, as necessary, to hold the strip's data.
@@ -745,13 +770,10 @@
 	static const char module[] = "TIFFFillStrip";
 	TIFFDirectory *td = &tif->tif_dir;
 
-        if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount)
-            return 0;
-
 	if ((tif->tif_flags&TIFF_NOREADRAW)==0)
 	{
-		uint64 bytecount = td->td_stripbytecount[strip];
-		if ((int64)bytecount <= 0) {
+		uint64 bytecount = TIFFGetStrileByteCount(tif, strip);
+		if( bytecount == 0 || bytecount > (uint64)TIFF_INT64_MAX ) {
 #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
 			TIFFErrorExt(tif->tif_clientdata, module,
 				"Invalid strip byte count %I64u, strip %lu",
@@ -778,7 +800,7 @@
 			    (bytecount - 4096) / 10 > (uint64)stripsize  )
 			{
 				uint64 newbytecount = (uint64)stripsize * 10 + 4096;
-				if( (int64)newbytecount >= 0 )
+				if( newbytecount == 0 || newbytecount > (uint64)TIFF_INT64_MAX )
 				{
 #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
 					TIFFWarningExt(tif->tif_clientdata, module,
@@ -798,6 +820,45 @@
 			}
 		}
 
+		if (isMapped(tif)) {
+			/*
+			 * We must check for overflow, potentially causing
+			 * an OOB read. Instead of simple
+			 *
+			 *  TIFFGetStrileOffset(tif, strip)+bytecount > tif->tif_size
+			 *
+			 * comparison (which can overflow) we do the following
+			 * two comparisons:
+			 */
+			if (bytecount > (uint64)tif->tif_size ||
+			    TIFFGetStrileOffset(tif, strip) > (uint64)tif->tif_size - bytecount) {
+				/*
+				 * This error message might seem strange, but
+				 * it's what would happen if a read were done
+				 * instead.
+				 */
+#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
+				TIFFErrorExt(tif->tif_clientdata, module,
+
+					"Read error on strip %lu; "
+					"got %I64u bytes, expected %I64u",
+					(unsigned long) strip,
+					(unsigned __int64) NoSantizeSubUInt64(tif->tif_size, TIFFGetStrileOffset(tif, strip)),
+					(unsigned __int64) bytecount);
+#else
+				TIFFErrorExt(tif->tif_clientdata, module,
+
+					"Read error on strip %lu; "
+					"got %llu bytes, expected %llu",
+					(unsigned long) strip,
+					(unsigned long long) NoSantizeSubUInt64(tif->tif_size, TIFFGetStrileOffset(tif, strip)),
+					(unsigned long long) bytecount);
+#endif
+				tif->tif_curstrip = NOSTRIP;
+				return (0);
+			}
+		}
+
 		if (isMapped(tif) &&
 		    (isFillOrder(tif, td->td_fillorder)
 		    || (tif->tif_flags & TIFF_NOBITREV))) {
@@ -818,44 +879,8 @@
 				tif->tif_rawdatasize = 0;
 			}
 			tif->tif_flags &= ~TIFF_MYBUFFER;
-			/*
-			 * We must check for overflow, potentially causing
-			 * an OOB read. Instead of simple
-			 *
-			 *  td->td_stripoffset[strip]+bytecount > tif->tif_size
-			 *
-			 * comparison (which can overflow) we do the following
-			 * two comparisons:
-			 */
-			if (bytecount > (uint64)tif->tif_size ||
-			    td->td_stripoffset[strip] > (uint64)tif->tif_size - bytecount) {
-				/*
-				 * This error message might seem strange, but
-				 * it's what would happen if a read were done
-				 * instead.
-				 */
-#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
-				TIFFErrorExt(tif->tif_clientdata, module,
-
-					"Read error on strip %lu; "
-					"got %I64u bytes, expected %I64u",
-					(unsigned long) strip,
-					(unsigned __int64) tif->tif_size - td->td_stripoffset[strip],
-					(unsigned __int64) bytecount);
-#else
-				TIFFErrorExt(tif->tif_clientdata, module,
-
-					"Read error on strip %lu; "
-					"got %llu bytes, expected %llu",
-					(unsigned long) strip,
-					(unsigned long long) tif->tif_size - td->td_stripoffset[strip],
-					(unsigned long long) bytecount);
-#endif
-				tif->tif_curstrip = NOSTRIP;
-				return (0);
-			}
 			tif->tif_rawdatasize = (tmsize_t)bytecount;
-			tif->tif_rawdata = tif->tif_base + (tmsize_t)td->td_stripoffset[strip];
+			tif->tif_rawdata = tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, strip);
                         tif->tif_rawdataoff = 0;
                         tif->tif_rawdataloaded = (tmsize_t) bytecount;
 
@@ -998,7 +1023,7 @@
 
 /* Variant of TIFFReadTile() that does 
  * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillTile() has
- *   suceeded. This avoid excessive memory allocation in case of truncated
+ *   succeeded. This avoid excessive memory allocation in case of truncated
  *   file.
  * * calls regular TIFFReadEncodedTile() if *buf != NULL
  */
@@ -1017,7 +1042,7 @@
 
 /* Variant of TIFFReadEncodedTile() that does 
  * * if *buf == NULL, *buf = _TIFFmalloc(bufsizetoalloc) only after TIFFFillTile() has
- *   suceeded. This avoid excessive memory allocation in case of truncated
+ *   succeeded. This avoid excessive memory allocation in case of truncated
  *   file.
  * * calls regular TIFFReadEncodedTile() if *buf != NULL
  */
@@ -1067,20 +1092,14 @@
         return ((tmsize_t)(-1));
 }
 
-
 static tmsize_t
 TIFFReadRawTile1(TIFF* tif, uint32 tile, void* buf, tmsize_t size, const char* module)
 {
-	TIFFDirectory *td = &tif->tif_dir;
-
-    if (!_TIFFFillStriles( tif ))
-        return ((tmsize_t)(-1));
-
 	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
 	if (!isMapped(tif)) {
 		tmsize_t cc;
 
-		if (!SeekOK(tif, td->td_stripoffset[tile])) {
+		if (!SeekOK(tif, TIFFGetStrileOffset(tif, tile))) {
 			TIFFErrorExt(tif->tif_clientdata, module,
 			    "Seek error at row %lu, col %lu, tile %lu",
 			    (unsigned long) tif->tif_row,
@@ -1110,9 +1129,9 @@
 	} else {
 		tmsize_t ma,mb;
 		tmsize_t n;
-		ma=(tmsize_t)td->td_stripoffset[tile];
+		ma=(tmsize_t)TIFFGetStrileOffset(tif, tile);
 		mb=ma+size;
-		if ((td->td_stripoffset[tile] > (uint64)TIFF_TMSIZE_T_MAX)||(ma>tif->tif_size))
+		if ((TIFFGetStrileOffset(tif, tile) > (uint64)TIFF_TMSIZE_T_MAX)||(ma>tif->tif_size))
 			n=0;
 		else if ((mb<ma)||(mb<size)||(mb>tif->tif_size))
 			n=tif->tif_size-ma;
@@ -1168,13 +1187,12 @@
 		"Compression scheme does not support access to raw uncompressed data");
 		return ((tmsize_t)(-1));
 	}
-	bytecount64 = td->td_stripbytecount[tile];
-	if (size != (tmsize_t)(-1) && (uint64)size < bytecount64)
-		bytecount64 = (uint64)size;
-	bytecountm = (tmsize_t)bytecount64;
-	if ((uint64)bytecountm!=bytecount64)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
+	bytecount64 = TIFFGetStrileByteCount(tif, tile);
+	if (size != (tmsize_t)(-1) && (uint64)size <= bytecount64)
+		bytecountm = size;
+	else
+		bytecountm = _TIFFCastUInt64ToSSize(tif, bytecount64, module);
+	if( bytecountm == 0 ) {
 		return ((tmsize_t)(-1));
 	}
 	return (TIFFReadRawTile1(tif, tile, buf, bytecountm, module));
@@ -1190,13 +1208,10 @@
 	static const char module[] = "TIFFFillTile";
 	TIFFDirectory *td = &tif->tif_dir;
 
-        if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount)
-            return 0;
-
 	if ((tif->tif_flags&TIFF_NOREADRAW)==0)
 	{
-		uint64 bytecount = td->td_stripbytecount[tile];
-		if ((int64)bytecount <= 0) {
+		uint64 bytecount = TIFFGetStrileByteCount(tif, tile);
+		if( bytecount == 0 || bytecount > (uint64)TIFF_INT64_MAX ) {
 #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
 			TIFFErrorExt(tif->tif_clientdata, module,
 				"%I64u: Invalid tile byte count, tile %lu",
@@ -1210,6 +1225,56 @@
 #endif
 			return (0);
 		}
+
+		/* To avoid excessive memory allocations: */
+		/* Byte count should normally not be larger than a number of */
+		/* times the uncompressed size plus some margin */
+                if( bytecount > 1024 * 1024 )
+                {
+			/* 10 and 4096 are just values that could be adjusted. */
+			/* Hopefully they are safe enough for all codecs */
+			tmsize_t stripsize = TIFFTileSize(tif);
+			if( stripsize != 0 &&
+			    (bytecount - 4096) / 10 > (uint64)stripsize  )
+			{
+				uint64 newbytecount = (uint64)stripsize * 10 + 4096;
+				if( newbytecount == 0 || newbytecount > (uint64)TIFF_INT64_MAX )
+				{
+#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__))
+					TIFFWarningExt(tif->tif_clientdata, module,
+					  "Too large tile byte count %I64u, tile %lu. Limiting to %I64u",
+					     (unsigned __int64) bytecount,
+					     (unsigned long) tile,
+					     (unsigned __int64) newbytecount);
+#else
+					TIFFErrorExt(tif->tif_clientdata, module,
+					  "Too large tile byte count %llu, tile %lu. Limiting to %llu",
+					     (unsigned long long) bytecount,
+					     (unsigned long) tile,
+					     (unsigned long long) newbytecount);
+#endif
+					bytecount = newbytecount;
+				}
+			}
+		}
+
+		if (isMapped(tif)) {
+			/*
+			 * We must check for overflow, potentially causing
+			 * an OOB read. Instead of simple
+			 *
+			 *  TIFFGetStrileOffset(tif, tile)+bytecount > tif->tif_size
+			 *
+			 * comparison (which can overflow) we do the following
+			 * two comparisons:
+			 */
+			if (bytecount > (uint64)tif->tif_size ||
+			    TIFFGetStrileOffset(tif, tile) > (uint64)tif->tif_size - bytecount) {
+				tif->tif_curtile = NOTILE;
+				return (0);
+			}
+		}
+
 		if (isMapped(tif) &&
 		    (isFillOrder(tif, td->td_fillorder)
 		     || (tif->tif_flags & TIFF_NOBITREV))) {
@@ -1230,23 +1295,10 @@
 				tif->tif_rawdatasize = 0;
 			}
 			tif->tif_flags &= ~TIFF_MYBUFFER;
-			/*
-			 * We must check for overflow, potentially causing
-			 * an OOB read. Instead of simple
-			 *
-			 *  td->td_stripoffset[tile]+bytecount > tif->tif_size
-			 *
-			 * comparison (which can overflow) we do the following
-			 * two comparisons:
-			 */
-			if (bytecount > (uint64)tif->tif_size ||
-			    td->td_stripoffset[tile] > (uint64)tif->tif_size - bytecount) {
-				tif->tif_curtile = NOTILE;
-				return (0);
-			}
+
 			tif->tif_rawdatasize = (tmsize_t)bytecount;
 			tif->tif_rawdata =
-				tif->tif_base + (tmsize_t)td->td_stripoffset[tile];
+				tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, tile);
                         tif->tif_rawdataoff = 0;
                         tif->tif_rawdataloaded = (tmsize_t) bytecount;
 			tif->tif_flags |= TIFF_BUFFERMMAP;
@@ -1305,7 +1357,8 @@
                         tif->tif_rawdataoff = 0;
                         tif->tif_rawdataloaded = bytecountm;
                         
-			if (!isFillOrder(tif, td->td_fillorder) &&
+			if (tif->tif_rawdata != NULL &&
+                            !isFillOrder(tif, td->td_fillorder) &&
 			    (tif->tif_flags & TIFF_NOBITREV) == 0)
 				TIFFReverseBits(tif->tif_rawdata,
                                                 tif->tif_rawdataloaded);
@@ -1372,9 +1425,6 @@
 {
 	TIFFDirectory *td = &tif->tif_dir;
 
-        if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount)
-            return 0;
-
 	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
 		if (!(*tif->tif_setupdecode)(tif))
 			return (0);
@@ -1395,7 +1445,7 @@
 		if( tif->tif_rawdataloaded > 0 )
 			tif->tif_rawcc = tif->tif_rawdataloaded;
 		else
-			tif->tif_rawcc = (tmsize_t)td->td_stripbytecount[strip];
+			tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, strip);
 	}
 	return ((*tif->tif_predecode)(tif,
 			(uint16)(strip / td->td_stripsperimage)));
@@ -1412,9 +1462,6 @@
 	TIFFDirectory *td = &tif->tif_dir;
         uint32 howmany32;
 
-        if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount)
-                return 0;
-
 	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
 		if (!(*tif->tif_setupdecode)(tif))
 			return (0);
@@ -1442,7 +1489,10 @@
 	else
 	{
 		tif->tif_rawcp = tif->tif_rawdata;
-		tif->tif_rawcc = (tmsize_t)td->td_stripbytecount[tile];
+		if( tif->tif_rawdataloaded > 0 )
+			tif->tif_rawcc = tif->tif_rawdataloaded;
+		else
+			tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, tile);
 	}
 	return ((*tif->tif_predecode)(tif,
 			(uint16)(tile/td->td_stripsperimage)));
@@ -1457,13 +1507,100 @@
 	}
 	if (tiles ^ isTiled(tif)) {
 		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, tiles ?
-		    "Can not read tiles from a stripped image" :
+		    "Can not read tiles from a striped image" :
 		    "Can not read scanlines from a tiled image");
 		return (0);
 	}
 	return (1);
 }
 
+/* Use the provided input buffer (inbuf, insize) and decompress it into
+ * (outbuf, outsize).
+ * This function replaces the use of TIFFReadEncodedStrip()/TIFFReadEncodedTile()
+ * when the user can provide the buffer for the input data, for example when
+ * he wants to avoid libtiff to read the strile offset/count values from the
+ * [Strip|Tile][Offsets/ByteCounts] array.
+ * inbuf content must be writable (if bit reversal is needed)
+ * Returns 1 in case of success, 0 otherwise.
+ */
+int      TIFFReadFromUserBuffer(TIFF* tif, uint32 strile,
+                                void* inbuf, tmsize_t insize,
+                                void* outbuf, tmsize_t outsize)
+{
+    static const char module[] = "TIFFReadFromUserBuffer";
+    TIFFDirectory *td = &tif->tif_dir;
+    int ret = 1;
+    uint32 old_tif_flags = tif->tif_flags;
+    tmsize_t old_rawdatasize = tif->tif_rawdatasize;
+    void* old_rawdata = tif->tif_rawdata;
+
+    if (tif->tif_mode == O_WRONLY) {
+        TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "File not open for reading");
+        return 0;
+    }
+    if (tif->tif_flags&TIFF_NOREADRAW)
+    {
+        TIFFErrorExt(tif->tif_clientdata, module,
+                "Compression scheme does not support access to raw uncompressed data");
+        return 0;
+    }
+
+    tif->tif_flags &= ~TIFF_MYBUFFER;
+    tif->tif_flags |= TIFF_BUFFERMMAP;
+    tif->tif_rawdatasize = insize;
+    tif->tif_rawdata = inbuf;
+    tif->tif_rawdataoff = 0;
+    tif->tif_rawdataloaded = insize;
+
+    if (!isFillOrder(tif, td->td_fillorder) &&
+        (tif->tif_flags & TIFF_NOBITREV) == 0)
+    {
+        TIFFReverseBits(inbuf, insize);
+    }
+
+    if( TIFFIsTiled(tif) )
+    {
+        if( !TIFFStartTile(tif, strile) ||
+            !(*tif->tif_decodetile)(tif, (uint8*) outbuf, outsize, 
+                                    (uint16)(strile/td->td_stripsperimage)) )
+        {
+            ret = 0;
+        }
+    }
+    else
+    {
+        uint32 rowsperstrip=td->td_rowsperstrip;
+        uint32 stripsperplane;
+        if (rowsperstrip>td->td_imagelength)
+            rowsperstrip=td->td_imagelength;
+        stripsperplane= TIFFhowmany_32_maxuint_compat(td->td_imagelength, rowsperstrip);
+        if( !TIFFStartStrip(tif, strile) ||
+            !(*tif->tif_decodestrip)(tif, (uint8*) outbuf, outsize, 
+                                     (uint16)(strile/stripsperplane)) )
+        {
+            ret = 0;
+        }
+    }
+    if( ret )
+    {
+        (*tif->tif_postdecode)(tif, (uint8*) outbuf, outsize);
+    }
+
+    if (!isFillOrder(tif, td->td_fillorder) &&
+        (tif->tif_flags & TIFF_NOBITREV) == 0)
+    {
+        TIFFReverseBits(inbuf, insize);
+    }
+
+    tif->tif_flags = old_tif_flags;
+    tif->tif_rawdatasize = old_rawdatasize;
+    tif->tif_rawdata = old_rawdata;
+    tif->tif_rawdataoff = 0;
+    tif->tif_rawdataloaded = 0;
+
+    return ret;
+}
+
 void
 _TIFFNoPostDecode(TIFF* tif, uint8* buf, tmsize_t cc)
 {
diff --git a/third_party/libtiff/tif_strip.c b/third_party/libtiff/tif_strip.c
index 6e9f2ef..c08c60a 100644
--- a/third_party/libtiff/tif_strip.c
+++ b/third_party/libtiff/tif_strip.c
@@ -1,5 +1,3 @@
-/* $Id: tif_strip.c,v 1.38 2016-12-03 11:02:15 erouault Exp $ */
-
 /*
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -131,15 +129,8 @@
 {
 	static const char module[] = "TIFFVStripSize";
 	uint64 m;
-	tmsize_t n;
 	m=TIFFVStripSize64(tif,nrows);
-	n=(tmsize_t)m;
-	if ((uint64)n!=m)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-		n=0;
-	}
-	return(n);
+        return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -149,8 +140,7 @@
 TIFFRawStripSize64(TIFF* tif, uint32 strip)
 {
 	static const char module[] = "TIFFRawStripSize64";
-	TIFFDirectory* td = &tif->tif_dir;
-	uint64 bytecount = td->td_stripbytecount[strip];
+	uint64 bytecount = TIFFGetStrileByteCount(tif, strip);
 
 	if (bytecount == 0)
 	{
@@ -213,15 +203,8 @@
 {
 	static const char module[] = "TIFFStripSize";
 	uint64 m;
-	tmsize_t n;
 	m=TIFFStripSize64(tif);
-	n=(tmsize_t)m;
-	if ((uint64)n!=m)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-		n=0;
-	}
-	return(n);
+	return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -332,14 +315,8 @@
 {
 	static const char module[] = "TIFFScanlineSize";
 	uint64 m;
-	tmsize_t n;
 	m=TIFFScanlineSize64(tif);
-	n=(tmsize_t)m;
-	if ((uint64)n!=m) {
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer arithmetic overflow");
-		n=0;
-	}
-	return(n);
+	return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -368,15 +345,8 @@
 {
 	static const char module[] = "TIFFRasterScanlineSize";
 	uint64 m;
-	tmsize_t n;
 	m=TIFFRasterScanlineSize64(tif);
-	n=(tmsize_t)m;
-	if ((uint64)n!=m)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer arithmetic overflow");
-		n=0;
-	}
-	return(n);
+	return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/third_party/libtiff/tif_swab.c b/third_party/libtiff/tif_swab.c
index 211dc57..b174ba6 100644
--- a/third_party/libtiff/tif_swab.c
+++ b/third_party/libtiff/tif_swab.c
@@ -1,5 +1,3 @@
-/* $Id: tif_swab.c,v 1.14 2016-09-04 21:32:56 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -31,7 +29,7 @@
  */
 #include "tiffiop.h"
 
-#ifndef TIFFSwabShort
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabShort)
 void
 TIFFSwabShort(uint16* wp)
 {
@@ -42,7 +40,7 @@
 }
 #endif
 
-#ifndef TIFFSwabLong
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabLong)
 void
 TIFFSwabLong(uint32* lp)
 {
@@ -54,7 +52,7 @@
 }
 #endif
 
-#ifndef TIFFSwabLong8
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabLong8)
 void
 TIFFSwabLong8(uint64* lp)
 {
@@ -68,7 +66,7 @@
 }
 #endif
 
-#ifndef TIFFSwabArrayOfShort
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfShort)
 void
 TIFFSwabArrayOfShort(register uint16* wp, tmsize_t n)
 {
@@ -84,7 +82,7 @@
 }
 #endif
 
-#ifndef TIFFSwabArrayOfTriples
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfTriples)
 void
 TIFFSwabArrayOfTriples(register uint8* tp, tmsize_t n)
 {
@@ -100,7 +98,7 @@
 }
 #endif
 
-#ifndef TIFFSwabArrayOfLong
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfLong)
 void
 TIFFSwabArrayOfLong(register uint32* lp, tmsize_t n)
 {
@@ -117,7 +115,7 @@
 }
 #endif
 
-#ifndef TIFFSwabArrayOfLong8
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfLong8)
 void
 TIFFSwabArrayOfLong8(register uint64* lp, tmsize_t n)
 {
@@ -136,7 +134,7 @@
 }
 #endif
 
-#ifndef TIFFSwabFloat
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabFloat)
 void
 TIFFSwabFloat(float* fp)
 {
@@ -148,7 +146,7 @@
 }
 #endif
 
-#ifndef TIFFSwabArrayOfFloat
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfFloat)
 void
 TIFFSwabArrayOfFloat(register float* fp, tmsize_t n)
 {
@@ -165,7 +163,7 @@
 }
 #endif
 
-#ifndef TIFFSwabDouble
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabDouble)
 void
 TIFFSwabDouble(double *dp)
 {
@@ -179,7 +177,7 @@
 }
 #endif
 
-#ifndef TIFFSwabArrayOfDouble
+#if defined(DISABLE_CHECK_TIFFSWABMACROS) || !defined(TIFFSwabArrayOfDouble)
 void
 TIFFSwabArrayOfDouble(double* dp, tmsize_t n)
 {
diff --git a/third_party/libtiff/tif_thunder.c b/third_party/libtiff/tif_thunder.c
index 183199d..db6383a 100644
--- a/third_party/libtiff/tif_thunder.c
+++ b/third_party/libtiff/tif_thunder.c
@@ -1,5 +1,3 @@
-/* $Id: tif_thunder.c,v 1.13 2016-09-04 21:32:56 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -124,17 +122,17 @@
 			break;
 		case THUNDER_2BITDELTAS:	/* 2-bit deltas */
 			if ((delta = ((n >> 4) & 3)) != DELTA2_SKIP)
-				SETPIXEL(op, lastpixel + twobitdeltas[delta]);
+				SETPIXEL(op, (unsigned)((int)lastpixel + twobitdeltas[delta]));
 			if ((delta = ((n >> 2) & 3)) != DELTA2_SKIP)
-				SETPIXEL(op, lastpixel + twobitdeltas[delta]);
+				SETPIXEL(op, (unsigned)((int)lastpixel + twobitdeltas[delta]));
 			if ((delta = (n & 3)) != DELTA2_SKIP)
-				SETPIXEL(op, lastpixel + twobitdeltas[delta]);
+				SETPIXEL(op, (unsigned)((int)lastpixel + twobitdeltas[delta]));
 			break;
 		case THUNDER_3BITDELTAS:	/* 3-bit deltas */
 			if ((delta = ((n >> 3) & 7)) != DELTA3_SKIP)
-				SETPIXEL(op, lastpixel + threebitdeltas[delta]);
+				SETPIXEL(op, (unsigned)((int)lastpixel + threebitdeltas[delta]));
 			if ((delta = (n & 7)) != DELTA3_SKIP)
-				SETPIXEL(op, lastpixel + threebitdeltas[delta]);
+				SETPIXEL(op, (unsigned)((int)lastpixel + threebitdeltas[delta]));
 			break;
 		case THUNDER_RAW:		/* raw data */
 			SETPIXEL(op, n);
diff --git a/third_party/libtiff/tif_tile.c b/third_party/libtiff/tif_tile.c
index 388e168..661cc77 100644
--- a/third_party/libtiff/tif_tile.c
+++ b/third_party/libtiff/tif_tile.c
@@ -1,5 +1,3 @@
-/* $Id: tif_tile.c,v 1.24 2015-06-07 22:35:40 bfriesen Exp $ */
-
 /*
  * Copyright (c) 1991-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -183,15 +181,8 @@
 {
 	static const char module[] = "TIFFTileRowSize";
 	uint64 m;
-	tmsize_t n;
 	m=TIFFTileRowSize64(tif);
-	n=(tmsize_t)m;
-	if ((uint64)n!=m)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-		n=0;
-	}
-	return(n);
+	return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -250,15 +241,8 @@
 {
 	static const char module[] = "TIFFVTileSize";
 	uint64 m;
-	tmsize_t n;
 	m=TIFFVTileSize64(tif,nrows);
-	n=(tmsize_t)m;
-	if ((uint64)n!=m)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-		n=0;
-	}
-	return(n);
+	return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
@@ -274,15 +258,8 @@
 {
 	static const char module[] = "TIFFTileSize";
 	uint64 m;
-	tmsize_t n;
 	m=TIFFTileSize64(tif);
-	n=(tmsize_t)m;
-	if ((uint64)n!=m)
-	{
-		TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow");
-		n=0;
-	}
-	return(n);
+	return _TIFFCastUInt64ToSSize(tif, m, module);
 }
 
 /*
diff --git a/third_party/libtiff/tif_version.c b/third_party/libtiff/tif_version.c
index f92c843..60875bb 100644
--- a/third_party/libtiff/tif_version.c
+++ b/third_party/libtiff/tif_version.c
@@ -1,4 +1,3 @@
-/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_version.c,v 1.3 2010-03-10 18:56:49 bfriesen Exp $ */
 /*
  * Copyright (c) 1992-1997 Sam Leffler
  * Copyright (c) 1992-1997 Silicon Graphics, Inc.
diff --git a/third_party/libtiff/tif_warning.c b/third_party/libtiff/tif_warning.c
index 423b636..c482785 100644
--- a/third_party/libtiff/tif_warning.c
+++ b/third_party/libtiff/tif_warning.c
@@ -1,5 +1,3 @@
-/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_warning.c,v 1.3 2010-03-10 18:56:49 bfriesen Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -51,24 +49,32 @@
 TIFFWarning(const char* module, const char* fmt, ...)
 {
 	va_list ap;
-	va_start(ap, fmt);
-	if (_TIFFwarningHandler)
+	if (_TIFFwarningHandler) {
+		va_start(ap, fmt);
 		(*_TIFFwarningHandler)(module, fmt, ap);
-	if (_TIFFwarningHandlerExt)
+		va_end(ap);
+	}
+	if (_TIFFwarningHandlerExt) {
+		va_start(ap, fmt);
 		(*_TIFFwarningHandlerExt)(0, module, fmt, ap);
-	va_end(ap);
+		va_end(ap);
+	}
 }
 
 void
 TIFFWarningExt(thandle_t fd, const char* module, const char* fmt, ...)
 {
 	va_list ap;
-	va_start(ap, fmt);
-	if (_TIFFwarningHandler)
+	if (_TIFFwarningHandler) {
+		va_start(ap, fmt);	
 		(*_TIFFwarningHandler)(module, fmt, ap);
-	if (_TIFFwarningHandlerExt)
+		va_end(ap);
+	}
+	if (_TIFFwarningHandlerExt) {
+		va_start(ap, fmt);
 		(*_TIFFwarningHandlerExt)(fd, module, fmt, ap);
-	va_end(ap);
+		va_end(ap);
+	}
 }
 
 
diff --git a/third_party/libtiff/tif_write.c b/third_party/libtiff/tif_write.c
index 4c216ec..33e803c 100644
--- a/third_party/libtiff/tif_write.c
+++ b/third_party/libtiff/tif_write.c
@@ -1,5 +1,3 @@
-/* $Id: tif_write.c,v 1.46 2016-12-03 21:57:44 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -130,10 +128,10 @@
 		tif->tif_rawcc = 0;
 		tif->tif_rawcp = tif->tif_rawdata;
 
-		if( td->td_stripbytecount[strip] > 0 )
+		if( td->td_stripbytecount_p[strip] > 0 )
 		{
 			/* if we are writing over existing tiles, zero length */
-			td->td_stripbytecount[strip] = 0;
+			td->td_stripbytecount_p[strip] = 0;
 
 			/* this forces TIFFAppendToStrip() to do a seek */
 			tif->tif_curoff = 0;
@@ -178,6 +176,32 @@
 	return (status);
 }
 
+/* Make sure that at the first attempt of rewriting a tile/strip, we will have */
+/* more bytes available in the output buffer than the previous byte count, */
+/* so that TIFFAppendToStrip() will detect the overflow when it is called the first */
+/* time if the new compressed tile is bigger than the older one. (GDAL #4771) */
+static int _TIFFReserveLargeEnoughWriteBuffer(TIFF* tif, uint32 strip_or_tile)
+{
+    TIFFDirectory *td = &tif->tif_dir;
+    if( td->td_stripbytecount_p[strip_or_tile] > 0 )
+    {
+        /* The +1 is to ensure at least one extra bytes */
+        /* The +4 is because the LZW encoder flushes 4 bytes before the limit */
+        uint64 safe_buffer_size = (uint64)(td->td_stripbytecount_p[strip_or_tile] + 1 + 4);
+        if( tif->tif_rawdatasize <= (tmsize_t)safe_buffer_size )
+        {
+            if( !(TIFFWriteBufferSetup(tif, NULL,
+                (tmsize_t)TIFFroundup_64(safe_buffer_size, 1024))) )
+                return 0;
+        }
+
+        /* Force TIFFAppendToStrip() to consider placing data at end
+            of file. */
+        tif->tif_curoff = 0;
+    }
+    return 1;
+}
+
 /*
  * Encode the supplied data and write it to the
  * specified strip.
@@ -224,6 +248,13 @@
         tif->tif_flags |= TIFF_BUF4WRITE;
 	tif->tif_curstrip = strip;
 
+	if( !_TIFFReserveLargeEnoughWriteBuffer(tif, strip) ) {
+            return ((tmsize_t)(-1));
+        }
+
+        tif->tif_rawcc = 0;
+        tif->tif_rawcp = tif->tif_rawdata;
+
         if (td->td_stripsperimage == 0) {
                 TIFFErrorExt(tif->tif_clientdata, module, "Zero strips per image");
                 return ((tmsize_t) -1);
@@ -236,27 +267,6 @@
 		tif->tif_flags |= TIFF_CODERSETUP;
 	}
 
-	if( td->td_stripbytecount[strip] > 0 )
-        {
-            /* Make sure that at the first attempt of rewriting the tile, we will have */
-            /* more bytes available in the output buffer than the previous byte count, */
-            /* so that TIFFAppendToStrip() will detect the overflow when it is called the first */
-            /* time if the new compressed tile is bigger than the older one. (GDAL #4771) */
-            if( tif->tif_rawdatasize <= (tmsize_t)td->td_stripbytecount[strip] )
-            {
-                if( !(TIFFWriteBufferSetup(tif, NULL,
-                    (tmsize_t)TIFFroundup_64((uint64)(td->td_stripbytecount[strip] + 1), 1024))) )
-                    return ((tmsize_t)(-1));
-            }
-
-	    /* Force TIFFAppendToStrip() to consider placing data at end
-               of file. */
-            tif->tif_curoff = 0;
-        }
-
-    tif->tif_rawcc = 0;
-    tif->tif_rawcp = tif->tif_rawdata;
-
 	tif->tif_flags &= ~TIFF_POSTENCODE;
 
     /* shortcut to avoid an extra memcpy() */
@@ -404,22 +414,8 @@
         tif->tif_flags |= TIFF_BUF4WRITE;
 	tif->tif_curtile = tile;
 
-	if( td->td_stripbytecount[tile] > 0 )
-        {
-            /* Make sure that at the first attempt of rewriting the tile, we will have */
-            /* more bytes available in the output buffer than the previous byte count, */
-            /* so that TIFFAppendToStrip() will detect the overflow when it is called the first */
-            /* time if the new compressed tile is bigger than the older one. (GDAL #4771) */
-            if( tif->tif_rawdatasize <= (tmsize_t) td->td_stripbytecount[tile] )
-            {
-                if( !(TIFFWriteBufferSetup(tif, NULL,
-                    (tmsize_t)TIFFroundup_64((uint64)(td->td_stripbytecount[tile] + 1), 1024))) )
-                    return ((tmsize_t)(-1));
-            }
-
-	    /* Force TIFFAppendToStrip() to consider placing data at end
-               of file. */
-            tif->tif_curoff = 0;
+        if( !_TIFFReserveLargeEnoughWriteBuffer(tif, tile) ) {
+            return ((tmsize_t)(-1));
         }
 
 	tif->tif_rawcc = 0;
@@ -539,18 +535,20 @@
 	td->td_nstrips = td->td_stripsperimage;
 	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
 		td->td_stripsperimage /= td->td_samplesperpixel;
-	td->td_stripoffset = (uint64 *)
-	    _TIFFmalloc(td->td_nstrips * sizeof (uint64));
-	td->td_stripbytecount = (uint64 *)
-	    _TIFFmalloc(td->td_nstrips * sizeof (uint64));
-	if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL)
+	td->td_stripoffset_p = (uint64 *)
+            _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64),
+                             "for \"StripOffsets\" array");
+	td->td_stripbytecount_p = (uint64 *)
+            _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64),
+                             "for \"StripByteCounts\" array");
+	if (td->td_stripoffset_p == NULL || td->td_stripbytecount_p == NULL)
 		return (0);
 	/*
 	 * Place data at the end-of-file
 	 * (by setting offsets to zero).
 	 */
-	_TIFFmemset(td->td_stripoffset, 0, td->td_nstrips*sizeof (uint64));
-	_TIFFmemset(td->td_stripbytecount, 0, td->td_nstrips*sizeof (uint64));
+	_TIFFmemset(td->td_stripoffset_p, 0, td->td_nstrips*sizeof (uint64));
+	_TIFFmemset(td->td_stripbytecount_p, 0, td->td_nstrips*sizeof (uint64));
 	TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
 	TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
 	return (1);
@@ -572,7 +570,7 @@
 	}
 	if (tiles ^ isTiled(tif)) {
 		TIFFErrorExt(tif->tif_clientdata, module, tiles ?
-		    "Can not write tiles to a stripped image" :
+		    "Can not write tiles to a striped image" :
 		    "Can not write scanlines to a tiled image");
 		return (0);
 	}
@@ -610,7 +608,7 @@
 			return (0);
 		}
 	}
-	if (tif->tif_dir.td_stripoffset == NULL && !TIFFSetupStrips(tif)) {
+	if (tif->tif_dir.td_stripoffset_p == NULL && !TIFFSetupStrips(tif)) {
 		tif->tif_dir.td_nstrips = 0;
 		TIFFErrorExt(tif->tif_clientdata, module, "No space for %s arrays",
 		    isTiled(tif) ? "tile" : "strip");
@@ -628,6 +626,20 @@
 	if (tif->tif_scanlinesize == 0)
 		return (0);
 	tif->tif_flags |= TIFF_BEENWRITING;
+
+        if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 &&
+            tif->tif_dir.td_stripoffset_entry.tdir_count == 0 &&
+            tif->tif_dir.td_stripoffset_entry.tdir_type == 0 &&
+            tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 &&
+            tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 &&
+            !(tif->tif_flags & TIFF_DIRTYDIRECT)  )
+        {
+            TIFFForceStrileArrayWriting(tif);
+        }
+
 	return (1);
 }
 
@@ -684,9 +696,9 @@
 	uint64* new_stripbytecount;
 
 	assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
-	new_stripoffset = (uint64*)_TIFFrealloc(td->td_stripoffset,
+	new_stripoffset = (uint64*)_TIFFrealloc(td->td_stripoffset_p,
 		(td->td_nstrips + delta) * sizeof (uint64));
-	new_stripbytecount = (uint64*)_TIFFrealloc(td->td_stripbytecount,
+	new_stripbytecount = (uint64*)_TIFFrealloc(td->td_stripbytecount_p,
 		(td->td_nstrips + delta) * sizeof (uint64));
 	if (new_stripoffset == NULL || new_stripbytecount == NULL) {
 		if (new_stripoffset)
@@ -697,11 +709,11 @@
 		TIFFErrorExt(tif->tif_clientdata, module, "No space to expand strip arrays");
 		return (0);
 	}
-	td->td_stripoffset = new_stripoffset;
-	td->td_stripbytecount = new_stripbytecount;
-	_TIFFmemset(td->td_stripoffset + td->td_nstrips,
+	td->td_stripoffset_p = new_stripoffset;
+	td->td_stripbytecount_p = new_stripbytecount;
+	_TIFFmemset(td->td_stripoffset_p + td->td_nstrips,
 		    0, delta*sizeof (uint64));
-	_TIFFmemset(td->td_stripbytecount + td->td_nstrips,
+	_TIFFmemset(td->td_stripbytecount_p + td->td_nstrips,
 		    0, delta*sizeof (uint64));
 	td->td_nstrips += delta;
         tif->tif_flags |= TIFF_DIRTYDIRECT;
@@ -720,12 +732,12 @@
 	uint64 m;
         int64 old_byte_count = -1;
 
-	if (td->td_stripoffset[strip] == 0 || tif->tif_curoff == 0) {
+	if (td->td_stripoffset_p[strip] == 0 || tif->tif_curoff == 0) {
             assert(td->td_nstrips > 0);
 
-            if( td->td_stripbytecount[strip] != 0 
-                && td->td_stripoffset[strip] != 0 
-                && td->td_stripbytecount[strip] >= (uint64) cc )
+            if( td->td_stripbytecount_p[strip] != 0 
+                && td->td_stripoffset_p[strip] != 0 
+                && td->td_stripbytecount_p[strip] >= (uint64) cc )
             {
                 /* 
                  * There is already tile data on disk, and the new tile
@@ -734,7 +746,7 @@
                  * more data to append to this strip before we are done
                  * depending on how we are getting called.
                  */
-                if (!SeekOK(tif, td->td_stripoffset[strip])) {
+                if (!SeekOK(tif, td->td_stripoffset_p[strip])) {
                     TIFFErrorExt(tif->tif_clientdata, module,
                                  "Seek error at scanline %lu",
                                  (unsigned long)tif->tif_row);
@@ -747,17 +759,17 @@
                  * Seek to end of file, and set that as our location to 
                  * write this strip.
                  */
-                td->td_stripoffset[strip] = TIFFSeekFile(tif, 0, SEEK_END);
+                td->td_stripoffset_p[strip] = TIFFSeekFile(tif, 0, SEEK_END);
                 tif->tif_flags |= TIFF_DIRTYSTRIP;
             }
 
-            tif->tif_curoff = td->td_stripoffset[strip];
+            tif->tif_curoff = td->td_stripoffset_p[strip];
 
             /*
              * We are starting a fresh strip/tile, so set the size to zero.
              */
-            old_byte_count = td->td_stripbytecount[strip];
-            td->td_stripbytecount[strip] = 0;
+            old_byte_count = td->td_stripbytecount_p[strip];
+            td->td_stripbytecount_p[strip] = 0;
 	}
 
 	m = tif->tif_curoff+cc;
@@ -774,9 +786,9 @@
 		    return (0);
 	}
 	tif->tif_curoff = m;
-	td->td_stripbytecount[strip] += cc;
+	td->td_stripbytecount_p[strip] += cc;
 
-        if( (int64) td->td_stripbytecount[strip] != old_byte_count )
+        if( (int64) td->td_stripbytecount_p[strip] != old_byte_count )
             tif->tif_flags |= TIFF_DIRTYSTRIP;
             
 	return (1);
diff --git a/third_party/libtiff/tif_zip.c b/third_party/libtiff/tif_zip.c
index 42943fb..c750773 100644
--- a/third_party/libtiff/tif_zip.c
+++ b/third_party/libtiff/tif_zip.c
@@ -1,5 +1,3 @@
-/* $Id: tif_zip.c,v 1.37 2017-05-10 15:21:16 erouault Exp $ */
-
 /*
  * Copyright (c) 1995-1997 Sam Leffler
  * Copyright (c) 1995-1997 Silicon Graphics, Inc.
@@ -126,7 +124,6 @@
 static int
 ZIPPreDecode(TIFF* tif, uint16 s)
 {
-	static const char module[] = "ZIPPreDecode";
 	ZIPState* sp = DecoderState(tif);
 
 	(void) s;
@@ -140,12 +137,7 @@
 	    we need to simplify this code to reflect a ZLib that is likely updated
 	    to deal with 8byte memory sizes, though this code will respond
 	    appropriately even before we simplify it */
-	sp->stream.avail_in = (uInt) tif->tif_rawcc;
-	if ((tmsize_t)sp->stream.avail_in != tif->tif_rawcc)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size");
-		return (0);
-	}
+	sp->stream.avail_in = (uint64)tif->tif_rawcc < 0xFFFFFFFFU ? (uInt) tif->tif_rawcc : 0xFFFFFFFFU;
 	return (inflateReset(&sp->stream) == Z_OK);
 }
 
@@ -160,46 +152,43 @@
 	assert(sp->state == ZSTATE_INIT_DECODE);
 
         sp->stream.next_in = tif->tif_rawcp;
-	sp->stream.avail_in = (uInt) tif->tif_rawcc;
         
 	sp->stream.next_out = op;
 	assert(sizeof(sp->stream.avail_out)==4);  /* if this assert gets raised,
 	    we need to simplify this code to reflect a ZLib that is likely updated
 	    to deal with 8byte memory sizes, though this code will respond
 	    appropriately even before we simplify it */
-	sp->stream.avail_out = (uInt) occ;
-	if ((tmsize_t)sp->stream.avail_out != occ)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size");
-		return (0);
-	}
 	do {
-		int state = inflate(&sp->stream, Z_PARTIAL_FLUSH);
+                int state;
+                uInt avail_in_before = (uint64)tif->tif_rawcc <= 0xFFFFFFFFU ? (uInt)tif->tif_rawcc : 0xFFFFFFFFU;
+                uInt avail_out_before = (uint64)occ < 0xFFFFFFFFU ? (uInt) occ : 0xFFFFFFFFU;
+                sp->stream.avail_in = avail_in_before;
+                sp->stream.avail_out = avail_out_before;
+		state = inflate(&sp->stream, Z_PARTIAL_FLUSH);
+		tif->tif_rawcc -= (avail_in_before - sp->stream.avail_in);
+                occ -= (avail_out_before - sp->stream.avail_out);
 		if (state == Z_STREAM_END)
 			break;
 		if (state == Z_DATA_ERROR) {
 			TIFFErrorExt(tif->tif_clientdata, module,
 			    "Decoding error at scanline %lu, %s",
 			     (unsigned long) tif->tif_row, SAFE_MSG(sp));
-			if (inflateSync(&sp->stream) != Z_OK)
-				return (0);
-			continue;
+			return (0);
 		}
 		if (state != Z_OK) {
 			TIFFErrorExt(tif->tif_clientdata, module, 
 				     "ZLib error: %s", SAFE_MSG(sp));
 			return (0);
 		}
-	} while (sp->stream.avail_out > 0);
-	if (sp->stream.avail_out != 0) {
+	} while (occ > 0);
+	if (occ != 0) {
 		TIFFErrorExt(tif->tif_clientdata, module,
 		    "Not enough data at scanline %lu (short " TIFF_UINT64_FORMAT " bytes)",
-		    (unsigned long) tif->tif_row, (TIFF_UINT64_T) sp->stream.avail_out);
+		    (unsigned long) tif->tif_row, (TIFF_UINT64_T) occ);
 		return (0);
 	}
 
         tif->tif_rawcp = sp->stream.next_in;
-        tif->tif_rawcc = sp->stream.avail_in;
 
 	return (1);
 }
@@ -231,7 +220,6 @@
 static int
 ZIPPreEncode(TIFF* tif, uint16 s)
 {
-	static const char module[] = "ZIPPreEncode";
 	ZIPState *sp = EncoderState(tif);
 
 	(void) s;
@@ -244,12 +232,7 @@
 	    we need to simplify this code to reflect a ZLib that is likely updated
 	    to deal with 8byte memory sizes, though this code will respond
 	    appropriately even before we simplify it */
-	sp->stream.avail_out = (uInt)tif->tif_rawdatasize;
-	if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size");
-		return (0);
-	}
+	sp->stream.avail_out = (uint64)tif->tif_rawdatasize <= 0xFFFFFFFFU ? (uInt)tif->tif_rawdatasize : 0xFFFFFFFFU;
 	return (deflateReset(&sp->stream) == Z_OK);
 }
 
@@ -271,13 +254,9 @@
 	    we need to simplify this code to reflect a ZLib that is likely updated
 	    to deal with 8byte memory sizes, though this code will respond
 	    appropriately even before we simplify it */
-	sp->stream.avail_in = (uInt) cc;
-	if ((tmsize_t)sp->stream.avail_in != cc)
-	{
-		TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size");
-		return (0);
-	}
 	do {
+                uInt avail_in_before = (uint64)cc <= 0xFFFFFFFFU ? (uInt)cc : 0xFFFFFFFFU;
+                sp->stream.avail_in = avail_in_before;
 		if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK) {
 			TIFFErrorExt(tif->tif_clientdata, module, 
 				     "Encoder error: %s",
@@ -288,9 +267,10 @@
 			tif->tif_rawcc = tif->tif_rawdatasize;
 			TIFFFlushData1(tif);
 			sp->stream.next_out = tif->tif_rawdata;
-			sp->stream.avail_out = (uInt) tif->tif_rawdatasize;  /* this is a safe typecast, as check is made already in ZIPPreEncode */
+			sp->stream.avail_out = (uint64)tif->tif_rawdatasize <= 0xFFFFFFFFU ? (uInt)tif->tif_rawdatasize : 0xFFFFFFFFU;
 		}
-	} while (sp->stream.avail_in > 0);
+		cc -= (avail_in_before - sp->stream.avail_in);
+	} while (cc > 0);
 	return (1);
 }
 
@@ -316,7 +296,7 @@
 				tif->tif_rawcc =  tif->tif_rawdatasize - sp->stream.avail_out;
 				TIFFFlushData1(tif);
 				sp->stream.next_out = tif->tif_rawdata;
-				sp->stream.avail_out = (uInt) tif->tif_rawdatasize;  /* this is a safe typecast, as check is made already in ZIPPreEncode */
+				sp->stream.avail_out = (uint64)tif->tif_rawdatasize <= 0xFFFFFFFFU ? (uInt)tif->tif_rawdatasize : 0xFFFFFFFFU;
 			}
 			break;
 		default:
diff --git a/third_party/libtiff/tiff.h b/third_party/libtiff/tiff.h
index fb39634..5b0a0c9 100644
--- a/third_party/libtiff/tiff.h
+++ b/third_party/libtiff/tiff.h
@@ -1,5 +1,3 @@
-/* $Id: tiff.h,v 1.70 2016-01-23 21:20:34 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -189,7 +187,11 @@
 #define     COMPRESSION_SGILOG		34676	/* SGI Log Luminance RLE */
 #define     COMPRESSION_SGILOG24	34677	/* SGI Log 24-bit packed */
 #define     COMPRESSION_JP2000          34712   /* Leadtools JPEG2000 */
+#define     COMPRESSION_LERC            34887   /* ESRI Lerc codec: https://github.com/Esri/lerc */
+/* compression codes 34887-34889 are reserved for ESRI */
 #define	    COMPRESSION_LZMA		34925	/* LZMA2 */
+#define	    COMPRESSION_ZSTD		50000	/* ZSTD: WARNING not registered in Adobe-maintained registry */
+#define	    COMPRESSION_WEBP		50001	/* WEBP: WARNING not registered in Adobe-maintained registry */
 #define	TIFFTAG_PHOTOMETRIC		262	/* photometric interpretation */
 #define	    PHOTOMETRIC_MINISWHITE	0	/* min value is white */
 #define	    PHOTOMETRIC_MINISBLACK	1	/* min value is black */
@@ -450,6 +452,8 @@
 /* tag 34929 is a private tag registered to FedEx */
 #define	TIFFTAG_FEDEX_EDR		34929	/* unknown use */
 #define TIFFTAG_INTEROPERABILITYIFD	40965	/* Pointer to Interoperability private directory */
+/* tags 50674 to 50677 are reserved for ESRI */
+#define TIFFTAG_LERC_PARAMETERS         50674   /* Stores LERC version and additional compression method */
 /* Adobe Digital Negative (DNG) format tags */
 #define TIFFTAG_DNGVERSION		50706	/* &DNG version number */
 #define TIFFTAG_DNGBACKWARDVERSION	50707	/* &DNG compatibility version */
@@ -603,6 +607,16 @@
 #define TIFFTAG_PERSAMPLE       65563	/* interface for per sample tags */
 #define     PERSAMPLE_MERGED        0	/* present as a single value */
 #define     PERSAMPLE_MULTI         1	/* present as multiple values */
+#define TIFFTAG_ZSTD_LEVEL      65564    /* ZSTD compression level */
+#define TIFFTAG_LERC_VERSION            65565 /* LERC version */
+#define     LERC_VERSION_2_4            4
+#define TIFFTAG_LERC_ADD_COMPRESSION    65566 /* LERC additional compression */
+#define     LERC_ADD_COMPRESSION_NONE    0
+#define     LERC_ADD_COMPRESSION_DEFLATE 1
+#define     LERC_ADD_COMPRESSION_ZSTD    2
+#define TIFFTAG_LERC_MAXZERROR          65567    /* LERC maximum error */
+#define TIFFTAG_WEBP_LEVEL		  65568	/* WebP compression level: WARNING not registered in Adobe-maintained registry */
+#define TIFFTAG_WEBP_LOSSLESS		65569	/* WebP lossless/lossy : WARNING not registered in Adobe-maintained registry */
 
 /*
  * EXIF tags
diff --git a/third_party/libtiff/tiffconf.h b/third_party/libtiff/tiffconf.h
index 50e458f..f57f6f7 100644
--- a/third_party/libtiff/tiffconf.h
+++ b/third_party/libtiff/tiffconf.h
@@ -7,10 +7,11 @@
 #ifndef _TIFFCONF_
 #define _TIFFCONF_
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_system.h"
 
 //NOTE: The tiff codec requires an ANSI C compiler environment for building and 
-//		presumes an ANSI C environment for use.
+//    presumes an ANSI C environment for use.
 
 # define HAVE_SYS_TYPES_H 1
 # define HAVE_FCNTL_H 1
@@ -29,7 +30,7 @@
 //fx_system.h already include the string.h in ANSIC
 
 /* Define to 1 if you have the <search.h> header file. */
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ && _MSC_VER >= 1900
+#if defined(OS_WIN)
 // search.h is always available in VS 2015 and above, and may be
 // available in earlier versions.
 #define HAVE_SEARCH_H 1
@@ -80,7 +81,7 @@
 /* Unsigned 32-bit type formatter */
 #define TIFF_UINT32_FORMAT "%u"
 
-#ifdef _MSC_VER		// windows
+#ifdef _MSC_VER   // windows
 
 /* Signed 64-bit type formatter */
 #define TIFF_INT64_FORMAT "%I64d"
@@ -94,9 +95,9 @@
 /* Unsigned 64-bit type */
 #define TIFF_UINT64_T unsigned __int64
 
-#else						// linux/unix
+#else           // linux/unix
 
-#if 0 //_FX_CPU_ == _FX_X64_	// linux/unix 64
+#if 0 //_FX_CPU_ == _FX_X64_  // linux/unix 64
 
 /* Signed 64-bit type formatter */
 #define TIFF_INT64_FORMAT "%ld"
@@ -107,7 +108,7 @@
 /* Signed 64-bit type */
 #define TIFF_INT64_T signed long
 
-#else						// linux/unix 32
+#else           // linux/unix 32
 
 /* Signed 64-bit type formatter */
 #define TIFF_INT64_FORMAT "%lld"
@@ -118,7 +119,7 @@
 /* Signed 64-bit type */
 #define TIFF_INT64_T signed long long
 
-#endif						// end _FX_CPU_
+#endif            // end _FX_CPU_
 
 /* Unsigned 64-bit type */
 #define TIFF_UINT64_T unsigned long long
@@ -131,13 +132,16 @@
 
 #if defined(_WIN64)
 #define TIFF_SSIZE_T signed __int64
+#define TIFF_SSIZE_T_MAX INT64_MAX
 #else
 #define TIFF_SSIZE_T signed int
+#define TIFF_SSIZE_T_MAX INT_MAX
 #endif
 
 #else
 
 #define TIFF_SSIZE_T signed long
+#define TIFF_SSIZE_T_MAX LONG_MAX
 
 #endif
 
@@ -201,7 +205,7 @@
 
 /* Support Old JPEG compresson (read contrib/ojpeg/README first! Compilation
    fails with unpatched IJG JPEG library) */
-#define  OJPEG_SUPPORT	1
+#define  OJPEG_SUPPORT  1
 
 /* Support Macintosh PackBits algorithm */
 #define PACKBITS_SUPPORT 1
diff --git a/third_party/libtiff/tiffio.h b/third_party/libtiff/tiffio.h
index f1d2fdc..198481d 100644
--- a/third_party/libtiff/tiffio.h
+++ b/third_party/libtiff/tiffio.h
@@ -1,5 +1,3 @@
-/* $Id: tiffio.h,v 1.94 2017-01-11 19:02:49 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -52,7 +50,7 @@
  *     promoted type (i.e. one of int, unsigned int, pointer,
  *     or double) and because we defined pseudo-tags that are
  *     outside the range of legal Aldus-assigned tags.
- * NB: tsize_t is int32 and not uint32 because some functions
+ * NB: tsize_t is signed and not unsigned because some functions
  *     return -1.
  * NB: toff_t is not off_t for many reasons; TIFFs max out at
  *     32-bit file offsets, and BigTIFF maxes out at 64-bit
@@ -299,7 +297,6 @@
 extern void _TIFFmemcpy(void* d, const void* s, tmsize_t c);
 extern int _TIFFmemcmp(const void* p1, const void* p2, tmsize_t c);
 extern void _TIFFfree(void* p);
-extern int _TIFFIfMultiplicationOverflow(tmsize_t op1, tmsize_t op2);
 
 /*
 ** Stuff, related to tag handling and creating custom tags.
@@ -414,6 +411,8 @@
 extern int TIFFWriteCustomDirectory(TIFF *, uint64 *);
 extern int TIFFCheckpointDirectory(TIFF *);
 extern int TIFFRewriteDirectory(TIFF *);
+extern int TIFFDeferStrileArrayWriting(TIFF *);
+extern int TIFFForceStrileArrayWriting(TIFF* );
 
 #if defined(c_plusplus) || defined(__cplusplus)
 extern void TIFFPrintDirectory(TIFF*, FILE*, long = 0);
@@ -471,6 +470,9 @@
 extern tmsize_t TIFFReadRawStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size);  
 extern tmsize_t TIFFReadEncodedTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size);  
 extern tmsize_t TIFFReadRawTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size);  
+extern int      TIFFReadFromUserBuffer(TIFF* tif, uint32 strile,
+                                       void* inbuf, tmsize_t insize,
+                                       void* outbuf, tmsize_t outsize);
 extern tmsize_t TIFFWriteEncodedStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc);
 extern tmsize_t TIFFWriteRawStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc);  
 extern tmsize_t TIFFWriteEncodedTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc);  
@@ -491,6 +493,11 @@
 extern void TIFFReverseBits(uint8* cp, tmsize_t n);
 extern const unsigned char* TIFFGetBitRevTable(int);
 
+extern uint64 TIFFGetStrileOffset(TIFF *tif, uint32 strile);
+extern uint64 TIFFGetStrileByteCount(TIFF *tif, uint32 strile);
+extern uint64 TIFFGetStrileOffsetWithErr(TIFF *tif, uint32 strile, int *pbErr);
+extern uint64 TIFFGetStrileByteCountWithErr(TIFF *tif, uint32 strile, int *pbErr);
+
 #ifdef LOGLUV_PUBLIC
 #define U_NEU		0.210526316
 #define V_NEU		0.473684211
diff --git a/third_party/libtiff/tiffiop.h b/third_party/libtiff/tiffiop.h
index 6fb47de..ba7b265 100644
--- a/third_party/libtiff/tiffiop.h
+++ b/third_party/libtiff/tiffiop.h
@@ -1,5 +1,3 @@
-/* $Id: tiffiop.h,v 1.90 2016-12-02 21:56:56 erouault Exp $ */
-
 /*
  * Copyright (c) 1988-1997 Sam Leffler
  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -71,12 +69,26 @@
 #endif
 
 #define    streq(a,b)      (strcmp(a,b) == 0)
+#define    strneq(a,b,n)   (strncmp(a,b,n) == 0)
 
 #ifndef TRUE
 #define	TRUE	1
 #define	FALSE	0
 #endif
 
+#define TIFF_SIZE_T_MAX ((size_t) ~ ((size_t)0))
+#define TIFF_TMSIZE_T_MAX (tmsize_t)(TIFF_SIZE_T_MAX >> 1)
+
+/*
+ * Largest 32-bit unsigned integer value.
+ */
+#define TIFF_UINT32_MAX 0xFFFFFFFFU
+
+/*
+ * Largest 64-bit unsigned integer value.
+ */
+#define TIFF_UINT64_MAX (((uint64)(TIFF_UINT32_MAX)) << 32 | TIFF_UINT32_MAX)
+
 typedef struct client_info {
     struct client_info *next;
     void *data;
@@ -127,6 +139,9 @@
         #define TIFF_DIRTYSTRIP 0x200000U /* stripoffsets/stripbytecount dirty*/
         #define TIFF_PERSAMPLE  0x400000U /* get/set per sample tags as arrays */
         #define TIFF_BUFFERMMAP 0x800000U /* read buffer (tif_rawdata) points into mmap() memory */
+        #define TIFF_DEFERSTRILELOAD 0x1000000U /* defer strip/tile offset/bytecount array loading. */
+        #define TIFF_LAZYSTRILELOAD  0x2000000U /* lazy/ondemand loading of strip/tile offset/bytecount values. Only used if TIFF_DEFERSTRILELOAD is set and in read-only mode */
+        #define TIFF_CHOPPEDUPARRAYS 0x4000000U /* set when allocChoppedUpStripArrays() has modified strip array */
 	uint64               tif_diroff;       /* file offset of current directory */
 	uint64               tif_nextdiroff;   /* file offset of following directory */
 	uint64*              tif_dirlist;      /* list of offsets to already seen directories to prevent IFD looping */
@@ -237,8 +252,7 @@
 	(TIFFReadFile((tif),(buf),(size))==(size))
 #endif
 #ifndef SeekOK
-#define SeekOK(tif, off) \
-	(TIFFSeekFile((tif),(off),SEEK_SET)==(off))
+#define SeekOK(tif, off) _TIFFSeekOK(tif, off)
 #endif
 #ifndef WriteOK
 #define WriteOK(tif, buf, size) \
@@ -259,7 +273,7 @@
 #define TIFFhowmany8_64(x) (((x)&0x07)?((uint64)(x)>>3)+1:(uint64)(x)>>3)
 #define TIFFroundup_64(x, y) (TIFFhowmany_64(x,y)*(y))
 
-/* Safe multiply which returns zero if there is an integer overflow */
+/* Safe multiply which returns zero if there is an *unsigned* integer overflow. This macro is not safe for *signed* integer types */
 #define TIFFSafeMultiply(t,v,m) ((((t)(m) != (t)0) && (((t)(((v)*(m))/(m))) == (t)(v))) ? (t)((v)*(m)) : (t)0)
 
 #define TIFFmax(A,B) ((A)>(B)?(A):(B))
@@ -314,6 +328,17 @@
 #define _TIFF_off_t off_t
 #endif
 
+#if defined(__has_attribute) && defined(__clang__)
+#if __has_attribute(no_sanitize)
+#define TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
+#else
+#define TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
+#endif
+#else
+#define TIFF_NOSANITIZE_UNSIGNED_INT_OVERFLOW
+#endif
+
+
 #if defined(__cplusplus)
 extern "C" {
 #endif
@@ -358,12 +383,16 @@
 
 extern uint32 _TIFFMultiply32(TIFF*, uint32, uint32, const char*);
 extern uint64 _TIFFMultiply64(TIFF*, uint64, uint64, const char*);
+extern tmsize_t _TIFFMultiplySSize(TIFF*, tmsize_t, tmsize_t, const char*);
+extern tmsize_t _TIFFCastUInt64ToSSize(TIFF*, uint64, const char*);
 extern void* _TIFFCheckMalloc(TIFF*, tmsize_t, tmsize_t, const char*);
 extern void* _TIFFCheckRealloc(TIFF*, void*, tmsize_t, tmsize_t, const char*);
 
 extern double _TIFFUInt64ToDouble(uint64);
 extern float _TIFFUInt64ToFloat(uint64);
 
+extern float _TIFFClampDoubleToFloat(double);
+
 extern tmsize_t
 _TIFFReadEncodedStripAndAllocBuffer(TIFF* tif, uint32 strip,
                                     void **buf, tmsize_t bufsizetoalloc,
@@ -376,7 +405,7 @@
 _TIFFReadTileAndAllocBuffer(TIFF* tif,
                             void **buf, tmsize_t bufsizetoalloc,
                             uint32 x, uint32 y, uint32 z, uint16 s);
-
+extern int _TIFFSeekOK(TIFF* tif, toff_t off);
 
 extern int TIFFInitDumpMode(TIFF*, int);
 #ifdef PACKBITS_SUPPORT
@@ -400,6 +429,7 @@
 #endif
 #ifdef JPEG_SUPPORT
 extern int TIFFInitJPEG(TIFF*, int);
+extern int TIFFJPEGIsFullStripRequired(TIFF*);
 #endif
 #ifdef JBIG_SUPPORT
 extern int TIFFInitJBIG(TIFF*, int);
@@ -416,6 +446,12 @@
 #ifdef LZMA_SUPPORT
 extern int TIFFInitLZMA(TIFF*, int);
 #endif
+#ifdef ZSTD_SUPPORT
+extern int TIFFInitZSTD(TIFF*, int);
+#endif
+#ifdef WEBP_SUPPORT
+extern int TIFFInitWebP(TIFF*, int);
+#endif
 #ifdef VMS
 extern const TIFFCodec _TIFFBuiltinCODECS[];
 #else
diff --git a/third_party/libtiff/tiffvers.h b/third_party/libtiff/tiffvers.h
index 890e433..aa3f613 100644
--- a/third_party/libtiff/tiffvers.h
+++ b/third_party/libtiff/tiffvers.h
@@ -1,4 +1,4 @@
-#define TIFFLIB_VERSION_STR "LIBTIFF, Version 4.0.8\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc."
+#define TIFFLIB_VERSION_STR "LIBTIFF, Version 4.1.0\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc."
 /*
  * This define can be used in code that requires
  * compilation-related definitions specific to a
@@ -6,4 +6,4 @@
  * version checking should be done based on the
  * string returned by TIFFGetVersion.
  */
-#define TIFFLIB_VERSION 20170521
+#define TIFFLIB_VERSION 20191103
diff --git a/third_party/pdfiumbigint.bp b/third_party/pdfiumbigint.bp
deleted file mode 100644
index fbd92a9..0000000
--- a/third_party/pdfiumbigint.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-cc_library_static {
-    name: "libpdfiumbigint",
-    defaults: ["pdfium-third-party"],
-
-    srcs: [
-        "bigint/BigInteger.cc",
-        "bigint/BigIntegerUtils.cc",
-        "bigint/BigUnsigned.cc",
-        "bigint/BigUnsignedInABase.cc",
-    ],
-}
diff --git a/third_party/pdfiumfx_agg.bp b/third_party/pdfiumfx_agg.bp
deleted file mode 100644
index f55fa67..0000000
--- a/third_party/pdfiumfx_agg.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfx_agg",
-    defaults: ["pdfium-third-party"],
-
-    cflags: [
-        "-Wno-unused-function"
-    ],
-
-    static_libs: [
-        "libpdfiumfxcrt",
-    ],
-
-    srcs: [
-        "agg23/agg_curves.cpp",
-        "agg23/agg_path_storage.cpp",
-        "agg23/agg_rasterizer_scanline_aa.cpp",
-        "agg23/agg_vcgen_dash.cpp",
-        "agg23/agg_vcgen_stroke.cpp",
-    ],
-}
diff --git a/third_party/pdfiumfx_libopenjpeg.bp b/third_party/pdfiumfx_libopenjpeg.bp
deleted file mode 100644
index 46b594d..0000000
--- a/third_party/pdfiumfx_libopenjpeg.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-cc_library_static {
-    name: "libpdfiumfx_libopenjpeg",
-    defaults: ["pdfium-third-party"],
-
-    srcs: [
-        "libopenjpeg20/bio.c",
-        "libopenjpeg20/cio.c",
-        "libopenjpeg20/dwt.c",
-        "libopenjpeg20/event.c",
-        "libopenjpeg20/function_list.c",
-        "libopenjpeg20/image.c",
-        "libopenjpeg20/invert.c",
-        "libopenjpeg20/j2k.c",
-        "libopenjpeg20/jp2.c",
-        "libopenjpeg20/mct.c",
-        "libopenjpeg20/mqc.c",
-        "libopenjpeg20/openjpeg.c",
-        "libopenjpeg20/opj_clock.c",
-        "libopenjpeg20/pi.c",
-        "libopenjpeg20/sparse_array.c",
-        "libopenjpeg20/t1.c",
-        "libopenjpeg20/t2.c",
-        "libopenjpeg20/tcd.c",
-        "libopenjpeg20/tgt.c",
-        "libopenjpeg20/thread.c",
-    ],
-}
diff --git a/third_party/pdfiumlcms2.bp b/third_party/pdfiumlcms2.bp
deleted file mode 100644
index d55287b..0000000
--- a/third_party/pdfiumlcms2.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-cc_library_static {
-    name: "libpdfiumlcms2",
-    defaults: ["pdfium-third-party"],
-
-    cflags: [
-        "-Wno-missing-braces",
-    ],
-
-    srcs: [
-        "lcms/src/cmsalpha.c",
-        "lcms/src/cmscam02.c",
-        "lcms/src/cmscgats.c",
-        "lcms/src/cmscnvrt.c",
-        "lcms/src/cmserr.c",
-        "lcms/src/cmsgamma.c",
-        "lcms/src/cmsgmt.c",
-        "lcms/src/cmshalf.c",
-        "lcms/src/cmsintrp.c",
-        "lcms/src/cmsio0.c",
-        "lcms/src/cmsio1.c",
-        "lcms/src/cmslut.c",
-        "lcms/src/cmsmd5.c",
-        "lcms/src/cmsmtrx.c",
-        "lcms/src/cmsnamed.c",
-        "lcms/src/cmsopt.c",
-        "lcms/src/cmspack.c",
-        "lcms/src/cmspcs.c",
-        "lcms/src/cmsplugin.c",
-        "lcms/src/cmsps2.c",
-        "lcms/src/cmssamp.c",
-        "lcms/src/cmssm.c",
-        "lcms/src/cmstypes.c",
-        "lcms/src/cmsvirt.c",
-        "lcms/src/cmswtpnt.c",
-        "lcms/src/cmsxform.c",
-    ],
-}
diff --git a/third_party/pdfiumpdfiumbase.bp b/third_party/pdfiumpdfiumbase.bp
deleted file mode 100644
index fc72eeb..0000000
--- a/third_party/pdfiumpdfiumbase.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-cc_library_static {
-    name: "libpdfiumpdfiumbase",
-    defaults: ["pdfium-third-party"],
-
-    srcs: [
-        "base/allocator/partition_allocator/address_space_randomization.cc",
-        "base/allocator/partition_allocator/page_allocator.cc",
-        "base/allocator/partition_allocator/partition_alloc.cc",
-        "base/allocator/partition_allocator/spin_lock.cc",
-    ],
-}
diff --git a/third_party/skia_shared/SkFloatToDecimal.cpp b/third_party/skia_shared/SkFloatToDecimal.cpp
new file mode 100644
index 0000000..774974e
--- /dev/null
+++ b/third_party/skia_shared/SkFloatToDecimal.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkFloatToDecimal.h"
+
+#include <cfloat>
+#include <climits>
+#include <cmath>
+
+//#include "SkTypes.h"
+#include <cassert>
+#define SkASSERT assert
+
+namespace pdfium {
+namespace skia {
+namespace {
+
+// Return pow(10.0, e), optimized for common cases.
+double pow10(int e) {
+    switch (e) {
+        case 0:  return 1.0;  // common cases
+        case 1:  return 10.0;
+        case 2:  return 100.0;
+        case 3:  return 1e+03;
+        case 4:  return 1e+04;
+        case 5:  return 1e+05;
+        case 6:  return 1e+06;
+        case 7:  return 1e+07;
+        case 8:  return 1e+08;
+        case 9:  return 1e+09;
+        case 10: return 1e+10;
+        case 11: return 1e+11;
+        case 12: return 1e+12;
+        case 13: return 1e+13;
+        case 14: return 1e+14;
+        case 15: return 1e+15;
+        default:
+            if (e > 15) {
+                double value = 1e+15;
+                while (e-- > 15) { value *= 10.0; }
+                return value;
+            } else {
+                SkASSERT(e < 0);
+                double value = 1.0;
+                while (e++ < 0) { value /= 10.0; }
+                return value;
+            }
+    }
+}
+
+}  // namespace
+
+/** Write a string into output, including a terminating '\0' (for
+    unit testing).  Return strlen(output) (for SkWStream::write) The
+    resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
+    sscanf(output, "%f", &x) will return the original value iff the
+    value is finite. This function accepts all possible input values.
+
+    Motivation: "PDF does not support [numbers] in exponential format
+    (such as 6.02e23)."  Otherwise, this function would rely on a
+    sprintf-type function from the standard library. */
+unsigned SkFloatToDecimal(float value, char output[kMaximumSkFloatToDecimalLength]) {
+    /* The longest result is -FLT_MIN.
+       We serialize it as "-.0000000000000000000000000000000000000117549435"
+       which has 48 characters plus a terminating '\0'. */
+
+    static_assert(kMaximumSkFloatToDecimalLength == 49, "");
+    // 3 = '-', '.', and '\0' characters.
+    // 9 = number of significant digits
+    // abs(FLT_MIN_10_EXP) = number of zeros in FLT_MIN
+    static_assert(kMaximumSkFloatToDecimalLength == 3 + 9 - FLT_MIN_10_EXP, "");
+
+    /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
+       most PDF rasterizers will use fixed-point scalars that lack the
+       dynamic range of floats.  Even if this is the case, I want to
+       serialize these (uncommon) very small and very large scalar
+       values with enough precision to allow a floating-point
+       rasterizer to read them in with perfect accuracy.
+       Experimentally, rasterizers such as pdfium do seem to benefit
+       from this.  Rasterizers that rely on fixed-point scalars should
+       gracefully ignore these values that they can not parse. */
+    char* output_ptr = &output[0];
+    const char* const end = &output[kMaximumSkFloatToDecimalLength - 1];
+    // subtract one to leave space for '\0'.
+
+    /* This function is written to accept any possible input value,
+       including non-finite values such as INF and NAN.  In that case,
+       we ignore value-correctness and output a syntacticly-valid
+       number. */
+    if (value == INFINITY) {
+        value = FLT_MAX;  // nearest finite float.
+    }
+    if (value == -INFINITY) {
+        value = -FLT_MAX;  // nearest finite float.
+    }
+    if (!std::isfinite(value) || value == 0.0f) {
+        // NAN is unsupported in PDF.  Always output a valid number.
+        // Also catch zero here, as a special case.
+        *output_ptr++ = '0';
+        *output_ptr = '\0';
+        return static_cast<unsigned>(output_ptr - output);
+    }
+    if (value < 0.0) {
+        *output_ptr++ = '-';
+        value = -value;
+    }
+    SkASSERT(value >= 0.0f);
+
+    int binaryExponent;
+    (void)std::frexp(value, &binaryExponent);
+    static const double kLog2 = 0.3010299956639812;  // log10(2.0);
+    int decimalExponent = static_cast<int>(std::floor(kLog2 * binaryExponent));
+    int decimalShift = decimalExponent - 8;
+    double power = pow10(-decimalShift);
+    SkASSERT(value * power <= (double)INT_MAX);
+    int d = static_cast<int>(value * power + 0.5);
+    // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
+    SkASSERT(d <= 999999999);
+    if (d > 167772159) {  // floor(pow(10,1+log10(1<<24)))
+       // need one fewer decimal digits for 24-bit precision.
+       decimalShift = decimalExponent - 7;
+       // SkASSERT(power * 0.1 = pow10(-decimalShift));
+       // recalculate to get rounding right.
+       d = static_cast<int>(value * (power * 0.1) + 0.5);
+       SkASSERT(d <= 99999999);
+    }
+    while (d % 10 == 0) {
+        d /= 10;
+        ++decimalShift;
+    }
+    SkASSERT(d > 0);
+    // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
+    unsigned char buffer[9]; // decimal value buffer.
+    int bufferIndex = 0;
+    do {
+        buffer[bufferIndex++] = d % 10;
+        d /= 10;
+    } while (d != 0);
+    SkASSERT(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0);
+    if (decimalShift >= 0) {
+        do {
+            --bufferIndex;
+            *output_ptr++ = '0' + buffer[bufferIndex];
+        } while (bufferIndex);
+        for (int i = 0; i < decimalShift; ++i) {
+            *output_ptr++ = '0';
+        }
+    } else {
+        int placesBeforeDecimal = bufferIndex + decimalShift;
+        if (placesBeforeDecimal > 0) {
+            while (placesBeforeDecimal-- > 0) {
+                --bufferIndex;
+                *output_ptr++ = '0' + buffer[bufferIndex];
+            }
+            *output_ptr++ = '.';
+        } else {
+            *output_ptr++ = '.';
+            int placesAfterDecimal = -placesBeforeDecimal;
+            while (placesAfterDecimal-- > 0) {
+                *output_ptr++ = '0';
+            }
+        }
+        while (bufferIndex > 0) {
+            --bufferIndex;
+            *output_ptr++ = '0' + buffer[bufferIndex];
+            if (output_ptr == end) {
+                break;  // denormalized: don't need extra precision.
+                // Note: denormalized numbers will not have the same number of
+                // significantDigits, but do not need them to round-trip.
+            }
+        }
+    }
+    SkASSERT(output_ptr <= end);
+    *output_ptr = '\0';
+    return static_cast<unsigned>(output_ptr - output);
+}
+}  // namespace skia
+}  // namespace pdfium
diff --git a/third_party/skia_shared/SkFloatToDecimal.h b/third_party/skia_shared/SkFloatToDecimal.h
new file mode 100644
index 0000000..376b093
--- /dev/null
+++ b/third_party/skia_shared/SkFloatToDecimal.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFloatToDecimal_DEFINED
+#define SkFloatToDecimal_DEFINED
+
+namespace pdfium {
+namespace skia {
+
+constexpr unsigned kMaximumSkFloatToDecimalLength = 49;
+
+/** \fn SkFloatToDecimal
+    Convert a float into a decimal string.
+
+    The resulting string will be in the form `[-]?([0-9]*\.)?[0-9]+` (It does
+    not use scientific notation.) and `sscanf(output, "%f", &x)` will return
+    the original value if the value is finite. This function accepts all
+    possible input values.
+
+    INFINITY and -INFINITY are rounded to FLT_MAX and -FLT_MAX.
+
+    NAN values are converted to 0.
+
+    This function will always add a terminating '\0' to the output.
+
+    @param value  Any floating-point number
+    @param output The buffer to write the string into.  Must be non-null.
+
+    @return strlen(output)
+*/
+unsigned SkFloatToDecimal(float value, char output[kMaximumSkFloatToDecimalLength]);
+
+}  // namespace skia
+}  // namespace pdfium
+
+#endif  // SkFloatToDecimal_DEFINED
diff --git a/third_party/yasm/BUILD.gn b/third_party/yasm/BUILD.gn
index 94dbf91..c6543a5 100644
--- a/third_party/yasm/BUILD.gn
+++ b/third_party/yasm/BUILD.gn
@@ -41,7 +41,11 @@
 
     # Don't define _DEBUG. Modest savings, but good for consistency.
     "//build/config:debug",
+
+    # Don't enable sanitizers for build tools. They slow down the overall build.
+    "//build/config/sanitizers:default_sanitizer_flags",
   ]
+
   configs_to_add += [
     "//build/config:release",
     "//build/config/compiler:optimize_max",
@@ -51,6 +55,12 @@
     # highbd_sad4d_sse2.asm on Windows this saves about 15 s.
     configs_to_delete += [ "//build/config/win:default_crt" ]
     configs_to_add += [ "//build/config/win:release_crt" ]
+
+    # Without no_default_deps, an implicit dependency on libc++ is added.
+    # libc++ may have been built referencing the debug CRT, but since we're
+    # explicitly using the release CRT, this would result in undefined symbol
+    # errors when linking, so we need to remove the implicit libc++ dependency.
+    no_default_deps = true
   }
 }
 
@@ -66,45 +76,38 @@
   import("//build/compiled_action.gni")
 
   config("yasm_config") {
+    configs = [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
     include_dirs = [
       "source/config/$host_os",
       "source/patched-yasm",
     ]
     defines = [ "HAVE_CONFIG_H" ]
-    if (is_posix) {
+    if (is_posix || is_fuchsia) {
       cflags = [ "-std=gnu99" ]
     }
   }
 
   executable("genmacro") {
-    sources = [
-      "source/patched-yasm/tools/genmacro/genmacro.c",
-    ]
+    sources = [ "source/patched-yasm/tools/genmacro/genmacro.c" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       ":yasm_config",
       "//build/config/compiler:no_chromium_code",
     ]
     deps = [
-      "//build/config:exe_and_shlib_deps",
-
       # Default manifest on Windows (a no-op elsewhere).
       "//build/win:default_exe_manifest",
     ]
   }
 
   executable("genmodule") {
-    sources = [
-      "source/patched-yasm/libyasm/genmodule.c",
-    ]
+    sources = [ "source/patched-yasm/libyasm/genmodule.c" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       ":yasm_config",
       "//build/config/compiler:no_chromium_code",
     ]
     deps = [
-      "//build/config:exe_and_shlib_deps",
-
       # Default manifest on Windows (a no-op elsewhere).
       "//build/win:default_exe_manifest",
     ]
@@ -128,7 +131,6 @@
 
     deps = [
       ":yasm_utils",
-      "//build/config:exe_and_shlib_deps",
 
       # Default manifest on Windows (a no-op elsewhere).
       "//build/win:default_exe_manifest",
@@ -155,34 +157,26 @@
   }
 
   executable("genstring") {
-    sources = [
-      "source/patched-yasm/genstring.c",
-    ]
+    sources = [ "source/patched-yasm/genstring.c" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       ":yasm_config",
       "//build/config/compiler:no_chromium_code",
     ]
     deps = [
-      "//build/config:exe_and_shlib_deps",
-
       # Default manifest on Windows (a no-op elsewhere).
       "//build/win:default_exe_manifest",
     ]
   }
 
   executable("genversion") {
-    sources = [
-      "source/patched-yasm/modules/preprocs/nasm/genversion.c",
-    ]
+    sources = [ "source/patched-yasm/modules/preprocs/nasm/genversion.c" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       ":yasm_config",
       "//build/config/compiler:no_chromium_code",
     ]
     deps = [
-      "//build/config:exe_and_shlib_deps",
-
       # Default manifest on Windows (a no-op elsewhere).
       "//build/win:default_exe_manifest",
     ]
@@ -225,8 +219,6 @@
       ":re2c_warnings",
     ]
     deps = [
-      "//build/config:exe_and_shlib_deps",
-
       # Default manifest on Windows (a no-op elsewhere).
       "//build/win:default_exe_manifest",
     ]
@@ -378,7 +370,6 @@
       ":generate_module",
       ":generate_version",
       ":yasm_utils",
-      "//build/config:exe_and_shlib_deps",
 
       # Default manifest on Windows (a no-op elsewhere).
       "//build/win:default_exe_manifest",
@@ -392,9 +383,7 @@
       "source/patched-yasm/modules/arch/x86/x86regtmod.gperf",
     ]
 
-    outputs = [
-      "$target_gen_dir/{{source_name_part}}.c",
-    ]
+    outputs = [ "$target_gen_dir/{{source_name_part}}.c" ]
     args = [
       "{{source}}",
       rebase_path(target_gen_dir, root_build_dir) + "/{{source_name_part}}.c",
@@ -415,9 +404,7 @@
       "source/patched-yasm/x86insn_nasm.gperf",
     ]
 
-    outputs = [
-      "$yasm_gen_include_dir/{{source_name_part}}.c",
-    ]
+    outputs = [ "$yasm_gen_include_dir/{{source_name_part}}.c" ]
     args = [
       "{{source}}",
       rebase_path(yasm_gen_include_dir, root_build_dir) +
@@ -446,49 +433,31 @@
   compile_macro("compile_nasm_macros") {
     # Output #included by
     #   source/patched-yasm/modules/preprocs/nasm/nasm-parser.c
-    sources = [
-      "source/patched-yasm/modules/parsers/nasm/nasm-std.mac",
-    ]
-    outputs = [
-      "$yasm_gen_include_dir/nasm-macros.c",
-    ]
+    sources = [ "source/patched-yasm/modules/parsers/nasm/nasm-std.mac" ]
+    outputs = [ "$yasm_gen_include_dir/nasm-macros.c" ]
     macro_varname = "nasm_standard_mac"
   }
 
   compile_macro("compile_nasm_version") {
     # Output #included by
     #   source/patched-yasm/modules/preprocs/nasm/nasm-preproc.c
-    sources = [
-      "$target_gen_dir/$version_file",
-    ]
-    outputs = [
-      "$yasm_gen_include_dir/nasm-version.c",
-    ]
+    sources = [ "$target_gen_dir/$version_file" ]
+    outputs = [ "$yasm_gen_include_dir/nasm-version.c" ]
     macro_varname = "nasm_version_mac"
-    deps = [
-      ":generate_version",
-    ]
+    deps = [ ":generate_version" ]
   }
 
   compile_macro("compile_win64_gas") {
     # Output #included by source/patched-yasm/frontends/yasm/yasm.c.
-    sources = [
-      "source/patched-yasm/modules/objfmts/coff/win64-gas.mac",
-    ]
-    outputs = [
-      "$yasm_gen_include_dir/win64-gas.c",
-    ]
+    sources = [ "source/patched-yasm/modules/objfmts/coff/win64-gas.mac" ]
+    outputs = [ "$yasm_gen_include_dir/win64-gas.c" ]
     macro_varname = "win64_gas_stdmac"
   }
 
   compile_macro("compile_win64_nasm") {
     # Output #included by source/patched-yasm/frontends/yasm/yasm.c.
-    sources = [
-      "source/patched-yasm/modules/objfmts/coff/win64-nasm.mac",
-    ]
-    outputs = [
-      "$yasm_gen_include_dir/win64-nasm.c",
-    ]
+    sources = [ "source/patched-yasm/modules/objfmts/coff/win64-nasm.mac" ]
+    outputs = [ "$yasm_gen_include_dir/win64-nasm.c" ]
     macro_varname = "win64_nasm_stdmac"
   }
 
@@ -498,9 +467,7 @@
       "source/patched-yasm/modules/parsers/gas/gas-token.re",
       "source/patched-yasm/modules/parsers/nasm/nasm-token.re",
     ]
-    outputs = [
-      "$target_gen_dir/{{source_name_part}}.c",
-    ]
+    outputs = [ "$target_gen_dir/{{source_name_part}}.c" ]
     args = [
       "-b",
       "-o",
@@ -512,12 +479,8 @@
   # This call doesn't fit into the re2c template above.
   compiled_action("compile_re2c_lc3b") {
     tool = ":re2c"
-    inputs = [
-      "source/patched-yasm/modules/arch/lc3b/lc3bid.re",
-    ]
-    outputs = [
-      "$target_gen_dir/lc3bid.c",
-    ]
+    inputs = [ "source/patched-yasm/modules/arch/lc3b/lc3bid.re" ]
+    outputs = [ "$target_gen_dir/lc3bid.c" ]
     args = [
       "-s",
       "-o",
@@ -530,12 +493,8 @@
     tool = ":genstring"
 
     # Output #included by source/patched-yasm/frontends/yasm/yasm.c.
-    inputs = [
-      "source/patched-yasm/COPYING",
-    ]
-    outputs = [
-      "$yasm_gen_include_dir/license.c",
-    ]
+    inputs = [ "source/patched-yasm/COPYING" ]
+    outputs = [ "$yasm_gen_include_dir/license.c" ]
     args = [
       "license_msg",
       rebase_path(outputs[0], root_build_dir),
@@ -549,9 +508,7 @@
       "source/patched-yasm/libyasm/module.in",
       config_makefile,
     ]
-    outputs = [
-      "$target_gen_dir/module.c",
-    ]
+    outputs = [ "$target_gen_dir/module.c" ]
     args = [
       rebase_path(inputs[0], root_build_dir),
       rebase_path(config_makefile, root_build_dir),
@@ -561,9 +518,7 @@
 
   compiled_action("generate_version") {
     tool = ":genversion"
-    outputs = [
-      "$target_gen_dir/$version_file",
-    ]
+    outputs = [ "$target_gen_dir/$version_file" ]
     args = [ rebase_path(outputs[0], root_build_dir) ]
   }
 }
diff --git a/third_party/yasm/CHROMIUM.diff b/third_party/yasm/CHROMIUM.diff
index 8f036f2..2ff0a6b 100644
--- a/third_party/yasm/CHROMIUM.diff
+++ b/third_party/yasm/CHROMIUM.diff
@@ -79,3 +79,24 @@
            return FALSE;
          }
  
+diff --git a/tools/re2c/parser.c b/tools/re2c/parser.c
+index 02d5c66..b3882af 100644
+--- a/tools/re2c/parser.c
++++ b/tools/re2c/parser.c
+@@ -226,15 +226,9 @@ void line_source(FILE *o, unsigned int line)
+ }
+ 
+ void parse(FILE *i, FILE *o){
+-    time_t now;
+-
+-    time(&now);
+-
+     peektok = NONE;
+ 
+-    fputs("/* Generated by re2c 0.9.1-C on ", o);
+-    fprintf(o, "%-24s", ctime(&now));
+-    fputs(" */\n", o); oline+=2;
++    fputs("/* Generated by re2c 0.9.1-C */\n", o); oline++;
+ 
+     in = Scanner_new(i);
+ 
diff --git a/third_party/yasm/run_yasm.py b/third_party/yasm/run_yasm.py
index cbd79cc..a257295 100644
--- a/third_party/yasm/run_yasm.py
+++ b/third_party/yasm/run_yasm.py
@@ -18,6 +18,7 @@
 """
 
 import argparse
+import os
 import sys
 import subprocess
 
@@ -30,6 +31,15 @@
 objfile = options.objfile
 depfile = objfile + '.d'
 
+# Set up environment for yasm.
+# Setting YASM_TEST_SUITE makes yasm output deterministic:
+# - the PE/COFF timestamp field is always 0 (this breaks link.exe /incremental,
+#   but we no longer user link.exe)
+# - in debug info, yasm identifies itself as "yasm HEAD" instead of e.g.
+#   "yasm 1.3.0" (we don't care much about this effect)
+# - in debug info, file paths are no longer absolute but relative to '.'
+os.environ['YASM_TEST_SUITE'] = '1'
+
 # Assemble.
 result_code = subprocess.call(sys.argv[1:])
 if result_code != 0:
diff --git a/third_party/yasm/yasm_assemble.gni b/third_party/yasm/yasm_assemble.gni
index 73d5548..f94fa6b 100644
--- a/third_party/yasm/yasm_assemble.gni
+++ b/third_party/yasm/yasm_assemble.gni
@@ -55,7 +55,7 @@
       "amd64",
     ]
   }
-} else if (is_posix) {
+} else if (is_posix || is_fuchsia) {
   if (current_cpu == "x86") {
     _yasm_flags = [
       "-felf32",
@@ -189,6 +189,9 @@
 
     sources = get_target_outputs(":$action_name")
 
+    # Do not publicize any header to remove build dependency.
+    public = []
+
     deps = [
       ":$action_name",
     ]
diff --git a/tools/modules/graph_modules.sh b/tools/modules/graph_modules.sh
new file mode 100755
index 0000000..e579c4b
--- /dev/null
+++ b/tools/modules/graph_modules.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright 2019 PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Generate a chart of dependencies and includes in "dot" format.
+# Invoke in pdfium/ top-level directory
+
+BUILD_DIR=out/Default
+
+function crunch {
+  echo '  edge [color=black,constraint=true]'
+  gn desc $BUILD_DIR $1 deps | grep -v '//:' | grep -v test | \
+      grep -v constants | grep -v samples | grep -v matches | \
+      sed "s|\\(.*\\)|  \"$1\" -> \"\\1\"|"
+  echo '  edge [color=red,constraint=false]'
+  gn desc $BUILD_DIR $1 allow_circular_includes_from | grep -v '//:' | \
+      grep -v test | grep -v samples | grep -v matches | \
+      grep -v 'how to display' | sed "s|\\(.*\\)|  \"\\1\" -> \"$1\"|"
+}
+
+TARGETS=`gn ls $BUILD_DIR | grep -v test | grep -v v8 | grep -v third_party | \
+             grep -v build | grep -v '//:'`
+
+echo 'digraph FRED {'
+echo '  node [shape=rectangle]'
+for TARGET in $TARGETS; do
+  crunch $TARGET
+done
+echo '}'
diff --git a/tools/ubsan/blacklist.txt b/tools/ubsan/blacklist.txt
new file mode 100644
index 0000000..29ebcf9
--- /dev/null
+++ b/tools/ubsan/blacklist.txt
@@ -0,0 +1,35 @@
+#############################################################################
+# UBSan blacklist.
+
+#############################################################################
+# YASM does some funny things that UBsan doesn't like.
+# https://crbug.com/489901
+src:*/third_party/yasm/*
+
+#############################################################################
+# V8 gives too many false positives. Ignore them for now.
+src:*/v8/*
+
+#############################################################################
+# Ignore system libraries.
+src:*/usr/*
+
+#############################################################################
+# ICU supressions. Mostly hash functions where integer overflow is OK.
+fun:*hashEntry*
+fun:*LocaleCacheKey*hashCode*
+fun:*google*protobuf*hash*
+fun:*(hash|Hash)*
+
+#############################################################################
+# Bounds blacklist.
+# Array at the end of struct pattern:
+# Maybe UBSan itself can be improved here?
+# e.g.
+# struct blah {
+#   int a;
+#   char foo[2]; // not actually 2
+# }
+src:*/third_party/icu/source/common/rbbi.cpp
+src:*/third_party/icu/source/common/rbbitblb.cpp
+src:*/third_party/icu/source/common/ucmndata.c
diff --git a/tools/ubsan/security_blacklist.txt b/tools/ubsan/security_blacklist.txt
new file mode 100644
index 0000000..e9bb1ed
--- /dev/null
+++ b/tools/ubsan/security_blacklist.txt
@@ -0,0 +1,56 @@
+# This black list is a merge of blacklist.txt and vptr_blacklist.txt.
+
+#############################################################################
+# UBSan blacklist.
+
+#############################################################################
+# YASM does some funny things that UBsan doesn't like.
+# https://crbug.com/489901
+src:*/third_party/yasm/*
+
+#############################################################################
+# V8 gives too many false positives. Ignore them for now.
+src:*/v8/*
+
+#############################################################################
+# Ignore system libraries.
+src:*/usr/*
+
+#############################################################################
+# ICU supressions. Mostly hash functions where integer overflow is OK.
+fun:*hashEntry*
+fun:*LocaleCacheKey*hashCode*
+fun:*google*protobuf*hash*
+fun:*(hash|Hash)*
+
+#############################################################################
+# Bounds blacklist.
+# Array at the end of struct pattern:
+# Maybe UBSan itself can be improved here?
+# e.g.
+# struct blah {
+#   int a;
+#   char foo[2]; // not actually 2
+# }
+src:*/third_party/icu/source/common/rbbi.cpp
+src:*/third_party/icu/source/common/rbbitblb.cpp
+src:*/third_party/icu/source/common/ucmndata.c
+
+#############################################################################
+# UBSan vptr blacklist.
+# Function and type based blacklisting use a mangled name, and it is especially
+# tricky to represent C++ types. For now, any possible changes by name manglings
+# are simply represented as wildcard expressions of regexp, and thus it might be
+# over-blacklisted.
+
+#############################################################################
+# UBSan seems to be emit false positives when virtual base classes are
+# involved, see e.g. crbug.com/448102.
+
+type:*v8*internal*OFStream*
+
+#############################################################################
+# UBsan goes into an infinite recursion when __dynamic_cast instrumented with
+# "vptr". See crbug.com/609786.
+
+src:*/third_party/libc\+\+abi/trunk/src/private_typeinfo.cpp
diff --git a/tools/ubsan/vptr_blacklist.txt b/tools/ubsan/vptr_blacklist.txt
new file mode 100644
index 0000000..ceb6813
--- /dev/null
+++ b/tools/ubsan/vptr_blacklist.txt
@@ -0,0 +1,18 @@
+#############################################################################
+# UBSan vptr blacklist.
+# Function and type based blacklisting use a mangled name, and it is especially
+# tricky to represent C++ types. For now, any possible changes by name manglings
+# are simply represented as wildcard expressions of regexp, and thus it might be
+# over-blacklisted.
+
+#############################################################################
+# UBSan seems to be emit false positives when virtual base classes are
+# involved, see e.g. crbug.com/448102.
+
+type:*v8*internal*OFStream*
+
+#############################################################################
+# UBsan goes into an infinite recursion when __dynamic_cast instrumented with
+# "vptr". See crbug.com/609786.
+
+src:*/third_party/libc\+\+abi/trunk/src/private_typeinfo.cpp
diff --git a/xfa/BUILD.gn b/xfa/BUILD.gn
new file mode 100644
index 0000000..df29737
--- /dev/null
+++ b/xfa/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../pdfium.gni")
+
+assert(pdf_enable_xfa)
+
+config("xfa_warnings") {
+  visibility = [ ":*" ]
+  if (is_posix && !is_clang) {  # When GCC.
+    cflags = [ "-Wno-strict-overflow" ]
+  }
+}
diff --git a/xfa/DEPS b/xfa/DEPS
index 2dd4ef3..dba1791 100644
--- a/xfa/DEPS
+++ b/xfa/DEPS
@@ -1,5 +1,7 @@
 include_rules = [
   '+core',
   '+fxbarcode',
-  '+third_party/bigint'
+
+  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
+  '-xfa/fwl',
 ]
diff --git a/xfa/fde/BUILD.gn b/xfa/fde/BUILD.gn
new file mode 100644
index 0000000..1806683
--- /dev/null
+++ b/xfa/fde/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fde") {
+  sources = [
+    "cfde_data.h",
+    "cfde_texteditengine.cpp",
+    "cfde_texteditengine.h",
+    "cfde_textout.cpp",
+    "cfde_textout.h",
+    "cfde_wordbreak_data.cpp",
+    "cfde_wordbreak_data.h",
+  ]
+  deps = [
+    "../../core/fxcrt",
+    "../../core/fxge",
+    "../fgas",
+    "../fgas/layout",
+  ]
+  configs += [
+    "../../:pdfium_core_config",
+    "../:xfa_warnings",
+  ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [ "cfde_texteditengine_unittest.cpp" ]
+  deps = [
+    ":fde",
+    "../../core/fxge",
+    "../../testing:unit_test_support",
+    "../fgas",
+  ]
+  pdfium_root_dir = "../../"
+}
diff --git a/xfa/fde/cfde_data.h b/xfa/fde/cfde_data.h
index d4ca63d..ad0740e 100644
--- a/xfa/fde/cfde_data.h
+++ b/xfa/fde/cfde_data.h
@@ -17,9 +17,8 @@
 };
 
 struct FDE_TextStyle {
-  FDE_TextStyle()
-      : single_line_(false), line_wrap_(false), last_line_height_(false) {}
-  ~FDE_TextStyle() {}
+  FDE_TextStyle() = default;
+  ~FDE_TextStyle() = default;
 
   void Reset() {
     single_line_ = false;
@@ -27,20 +26,9 @@
     last_line_height_ = false;
   }
 
-  bool single_line_;
-  bool line_wrap_;
-  bool last_line_height_;
-};
-
-struct FDE_TTOPIECE {
-  FDE_TTOPIECE();
-  FDE_TTOPIECE(const FDE_TTOPIECE& that);
-  ~FDE_TTOPIECE();
-
-  int32_t iStartChar;
-  int32_t iChars;
-  uint32_t dwCharStyles;
-  CFX_RectF rtPiece;
+  bool single_line_ = false;
+  bool line_wrap_ = false;
+  bool last_line_height_ = false;
 };
 
 #endif  // XFA_FDE_CFDE_DATA_H_
diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp
index d085a8d..957b6ff 100644
--- a/xfa/fde/cfde_texteditengine.cpp
+++ b/xfa/fde/cfde_texteditengine.cpp
@@ -8,7 +8,10 @@
 
 #include <algorithm>
 #include <limits>
+#include <utility>
 
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fde/cfde_wordbreak_data.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
@@ -19,7 +22,7 @@
 constexpr size_t kGapSize = 128;
 constexpr size_t kPageWidthMax = 0xffff;
 
-class InsertOperation : public CFDE_TextEditEngine::Operation {
+class InsertOperation final : public CFDE_TextEditEngine::Operation {
  public:
   InsertOperation(CFDE_TextEditEngine* engine,
                   size_t start_idx,
@@ -44,7 +47,7 @@
   WideString added_text_;
 };
 
-class DeleteOperation : public CFDE_TextEditEngine::Operation {
+class DeleteOperation final : public CFDE_TextEditEngine::Operation {
  public:
   DeleteOperation(CFDE_TextEditEngine* engine,
                   size_t start_idx,
@@ -69,7 +72,7 @@
   WideString removed_text_;
 };
 
-class ReplaceOperation : public CFDE_TextEditEngine::Operation {
+class ReplaceOperation final : public CFDE_TextEditEngine::Operation {
  public:
   ReplaceOperation(CFDE_TextEditEngine* engine,
                    size_t start_idx,
@@ -95,20 +98,6 @@
   DeleteOperation delete_op_;
 };
 
-bool CheckStateChangeForWordBreak(WordBreakProperty from,
-                                  WordBreakProperty to) {
-  ASSERT(static_cast<int>(from) < 13);
-
-  return !!(gs_FX_WordBreak_Table[static_cast<int>(from)] &
-            static_cast<uint16_t>(1 << static_cast<int>(to)));
-}
-
-WordBreakProperty GetWordBreakProperty(wchar_t wcCodePoint) {
-  uint8_t dwProperty = gs_FX_WordBreak_CodePointProperties[wcCodePoint >> 1];
-  return static_cast<WordBreakProperty>((wcCodePoint & 1) ? (dwProperty & 0x0F)
-                                                          : (dwProperty >> 4));
-}
-
 int GetBreakFlagsFor(WordBreakProperty current, WordBreakProperty next) {
   if (current == WordBreakProperty::kMidLetter) {
     if (next == WordBreakProperty::kALetter)
@@ -238,34 +227,59 @@
   text_out->SetStyles(style);
 
   size_t length = text.GetLength();
-  WideStringView temp(text.c_str(), length);
+  WideStringView temp = text.AsStringView();
 
   float vertical_height = line_spacing_ * visible_line_count_;
   size_t chars_exceeding_size = 0;
   // TODO(dsinclair): Can this get changed to a binary search?
   for (size_t i = 0; i < num_to_check; i++) {
-    // This does a lot of string copying ....
-    // TODO(dsinclair): make CalcLogicSize take a WideStringC instead.
-    text_out->CalcLogicSize(WideString(temp), text_rect);
-
+    text_out->CalcLogicSize(temp, &text_rect);
     if (limit_horizontal_area_ && text_rect.width <= available_width_)
       break;
     if (limit_vertical_area_ && text_rect.height <= vertical_height)
       break;
 
-    --length;
-    temp = temp.Mid(0, length);
     ++chars_exceeding_size;
+
+    --length;
+    temp = temp.First(length);
   }
 
   return chars_exceeding_size;
 }
 
 void CFDE_TextEditEngine::Insert(size_t idx,
-                                 const WideString& text,
+                                 const WideString& request_text,
                                  RecordOperation add_operation) {
-  if (idx > text_length_)
-    idx = text_length_;
+  WideString text = request_text;
+  if (text.IsEmpty())
+    return;
+
+  idx = std::min(idx, text_length_);
+
+  TextChange change;
+  change.selection_start = idx;
+  change.selection_end = idx;
+  change.text = text;
+  change.previous_text = GetText();
+  change.cancelled = false;
+
+  if (delegate_ && (add_operation != RecordOperation::kSkipRecord &&
+                    add_operation != RecordOperation::kSkipNotify)) {
+    delegate_->OnTextWillChange(&change);
+    if (change.cancelled)
+      return;
+
+    text = change.text;
+    idx = change.selection_start;
+
+    // Delegate extended the selection, so delete it before we insert.
+    if (change.selection_end != change.selection_start)
+      DeleteSelectedText(RecordOperation::kSkipRecord);
+
+    // Delegate may have changed text entirely, recheck.
+    idx = std::min(idx, text_length_);
+  }
 
   size_t length = text.GetLength();
   if (length == 0)
@@ -274,11 +288,23 @@
   // If we're going to be too big we insert what we can and notify the
   // delegate we've filled the text after the insert is done.
   bool exceeded_limit = false;
-  if (has_character_limit_ && text_length_ + length > character_limit_) {
-    exceeded_limit = true;
-    length = character_limit_ - text_length_;
-  }
 
+  // Currently we allow inserting a number of characters over the text limit if
+  // we're skipping notify. This means we're setting the formatted text into the
+  // engine. Otherwise, if you enter 123456789 for an SSN into a field
+  // with a 9 character limit and we reformat to 123-45-6789 we'll truncate
+  // the 89 when inserting into the text edit. See https://crbug.com/pdfium/1089
+  if (has_character_limit_ && text_length_ + length > character_limit_) {
+    if (add_operation == RecordOperation::kSkipNotify) {
+      // Raise the limit to allow subsequent changes to expanded text.
+      character_limit_ = text_length_ + length;
+    } else {
+      // Trucate the text to comply with the limit.
+      CHECK(text_length_ <= character_limit_);
+      length = character_limit_ - text_length_;
+      exceeded_limit = true;
+    }
+  }
   AdjustGap(idx, length);
 
   if (validation_enabled_ || limit_horizontal_area_ || limit_vertical_area_) {
@@ -341,7 +367,7 @@
     if (exceeded_limit)
       delegate_->NotifyTextFull();
 
-    delegate_->OnTextChanged(previous_text);
+    delegate_->OnTextChanged();
   }
 }
 
@@ -408,10 +434,9 @@
     return 0;
   --pos;
 
-  wchar_t ch = GetChar(pos);
   while (pos != 0) {
     // We want to be on the location just before the \r or \n
-    ch = GetChar(pos - 1);
+    wchar_t ch = GetChar(pos - 1);
     if (ch != '\r' && ch != '\n')
       break;
 
@@ -605,6 +630,7 @@
     return;
 
   has_character_limit_ = limit;
+  character_limit_ = std::max(character_limit_, text_length_);
   if (is_comb_text_)
     SetCombTextWidth();
 
@@ -617,7 +643,7 @@
 
   ClearOperationRecords();
 
-  character_limit_ = limit;
+  character_limit_ = std::max(limit, text_length_);
   if (is_comb_text_)
     SetCombTextWidth();
 
@@ -655,10 +681,6 @@
   is_dirty_ = true;
 }
 
-float CFDE_TextEditEngine::GetFontAscent() const {
-  return (static_cast<float>(font_->GetAscent()) * font_size_) / 1000;
-}
-
 void CFDE_TextEditEngine::SetAlignment(uint32_t alignment) {
   if (alignment == character_alignment_)
     return;
@@ -759,7 +781,7 @@
 
 WideString CFDE_TextEditEngine::GetSelectedText() const {
   if (!has_selection_)
-    return L"";
+    return WideString();
 
   WideString text;
   if (selection_.start_idx < gap_position_) {
@@ -793,7 +815,7 @@
 WideString CFDE_TextEditEngine::DeleteSelectedText(
     RecordOperation add_operation) {
   if (!has_selection_)
-    return L"";
+    return WideString();
 
   return Delete(selection_.start_idx, selection_.count, add_operation);
 }
@@ -802,7 +824,29 @@
                                        size_t length,
                                        RecordOperation add_operation) {
   if (start_idx >= text_length_)
-    return L"";
+    return WideString();
+
+  TextChange change;
+  change.text.clear();
+  change.cancelled = false;
+  if (delegate_ && (add_operation != RecordOperation::kSkipRecord &&
+                    add_operation != RecordOperation::kSkipNotify)) {
+    change.previous_text = GetText();
+    change.selection_start = start_idx;
+    change.selection_end = start_idx + length;
+
+    delegate_->OnTextWillChange(&change);
+    if (change.cancelled)
+      return WideString();
+
+    // Delegate may have changed the selection range.
+    start_idx = change.selection_start;
+    length = change.selection_end - change.selection_start;
+
+    // Delegate may have changed text entirely, recheck.
+    if (start_idx >= text_length_)
+      return WideString();
+  }
 
   length = std::min(length, text_length_ - start_idx);
   AdjustGap(start_idx + length, 0);
@@ -821,17 +865,40 @@
   gap_size_ += length;
 
   text_length_ -= length;
+  is_dirty_ = true;
   ClearSelection();
 
+  // The JS requested the insertion of text instead of just a deletion.
+  if (!change.text.IsEmpty())
+    Insert(start_idx, change.text, RecordOperation::kSkipRecord);
+
   if (delegate_)
-    delegate_->OnTextChanged(previous_text);
+    delegate_->OnTextChanged();
 
   return ret;
 }
 
-void CFDE_TextEditEngine::ReplaceSelectedText(const WideString& rep) {
-  size_t start_idx = selection_.start_idx;
+void CFDE_TextEditEngine::ReplaceSelectedText(const WideString& requested_rep) {
+  WideString rep = requested_rep;
 
+  if (delegate_) {
+    TextChange change;
+    change.selection_start = selection_.start_idx;
+    change.selection_end = selection_.start_idx + selection_.count;
+    change.text = rep;
+    change.previous_text = GetText();
+    change.cancelled = false;
+
+    delegate_->OnTextWillChange(&change);
+    if (change.cancelled)
+      return;
+
+    rep = change.text;
+    selection_.start_idx = change.selection_start;
+    selection_.count = change.selection_end - change.selection_start;
+  }
+
+  size_t start_idx = selection_.start_idx;
   WideString txt = DeleteSelectedText(RecordOperation::kSkipRecord);
   Insert(gap_position_, rep, RecordOperation::kSkipRecord);
 
@@ -899,14 +966,26 @@
 
   size_t start_it_idx = start_it->nStart;
   for (; start_it <= end_it; ++start_it) {
-    if (!start_it->rtPiece.Contains(point))
+    bool piece_contains_point_vertically =
+        (point.y >= start_it->rtPiece.top &&
+         point.y < start_it->rtPiece.bottom());
+    if (!piece_contains_point_vertically)
       continue;
 
     std::vector<CFX_RectF> rects = GetCharRects(*start_it);
     for (size_t i = 0; i < rects.size(); ++i) {
-      if (!rects[i].Contains(point))
+      bool character_contains_point_horizontally =
+          (point.x >= rects[i].left && point.x < rects[i].right());
+      if (!character_contains_point_horizontally)
         continue;
-      size_t pos = start_it->nStart + i;
+
+      // When clicking on the left half of a character, the cursor should be
+      // moved before it. If the click was on the right half of that character,
+      // move the cursor after it.
+      bool closer_to_left =
+          (point.x - rects[i].left < rects[i].right() - point.x);
+      int caret_pos = (closer_to_left ? i : i + 1);
+      size_t pos = start_it->nStart + caret_pos;
       if (pos >= text_length_)
         return text_length_;
 
@@ -920,6 +999,28 @@
       // TODO(dsinclair): Old code had a before flag set based on bidi?
       return pos;
     }
+
+    // Point is not within the horizontal range of any characters, it's
+    // afterwards. Return the position after the last character.
+    // The last line has nCount equal to the number of characters + 1 (sentinel
+    // character maybe?). Restrict to the text_length_ to account for that.
+    size_t pos = std::min(
+        static_cast<size_t>(start_it->nStart + start_it->nCount), text_length_);
+
+    // If the line is not the last one and it was broken right after a breaking
+    // whitespace (space or line break), the cursor should not be placed after
+    // the whitespace, but before it. If the cursor is moved after the
+    // whitespace, it goes to the beginning of the next line.
+    bool is_last_line = (std::next(start_it) == text_piece_info_.end());
+    if (!is_last_line && pos > 0) {
+      wchar_t previous_char = GetChar(pos - 1);
+      if (previous_char == L' ' || previous_char == L'\n' ||
+          previous_char == L'\r') {
+        --pos;
+      }
+    }
+
+    return pos;
   }
 
   if (start_it == text_piece_info_.end())
@@ -937,9 +1038,9 @@
   if (piece.nCount < 1)
     return std::vector<CFX_RectF>();
 
-  FX_TXTRUN tr;
+  CFX_TxtBreak::Run tr;
   tr.pEdtEngine = this;
-  tr.pIdentity = &piece;
+  tr.iStart = piece.nStart;
   tr.iLength = piece.nCount;
   tr.pFont = font_;
   tr.fFontSize = font_size_;
@@ -949,14 +1050,14 @@
   return text_break_.GetCharRects(&tr, false);
 }
 
-std::vector<FXTEXT_CHARPOS> CFDE_TextEditEngine::GetDisplayPos(
+std::vector<TextCharPos> CFDE_TextEditEngine::GetDisplayPos(
     const FDE_TEXTEDITPIECE& piece) {
   if (piece.nCount < 1)
-    return std::vector<FXTEXT_CHARPOS>();
+    return std::vector<TextCharPos>();
 
-  FX_TXTRUN tr;
+  CFX_TxtBreak::Run tr;
   tr.pEdtEngine = this;
-  tr.pIdentity = &piece;
+  tr.iStart = piece.nStart;
   tr.iLength = piece.nCount;
   tr.pFont = font_;
   tr.fFontSize = font_size_;
@@ -964,7 +1065,7 @@
   tr.dwCharStyles = piece.dwCharStyles;
   tr.pRect = &piece.rtPiece;
 
-  std::vector<FXTEXT_CHARPOS> data(text_break_.GetDisplayPos(&tr, nullptr));
+  std::vector<TextCharPos> data(text_break_.GetDisplayPos(&tr, nullptr));
   text_break_.GetDisplayPos(&tr, data.data());
   return data;
 }
@@ -1001,19 +1102,17 @@
       const CFX_BreakPiece* piece = text_break_.GetBreakPieceUnstable(i);
 
       FDE_TEXTEDITPIECE txtEdtPiece;
-      memset(&txtEdtPiece, 0, sizeof(FDE_TEXTEDITPIECE));
-
-      txtEdtPiece.nBidiLevel = piece->m_iBidiLevel;
-      txtEdtPiece.nCount = piece->GetLength();
-      txtEdtPiece.nStart = current_piece_start;
-      txtEdtPiece.dwCharStyles = piece->m_dwCharStyles;
-      if (FX_IsOdd(piece->m_iBidiLevel))
-        txtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;
-
       txtEdtPiece.rtPiece.left = piece->m_iStartPos / 20000.0f;
       txtEdtPiece.rtPiece.top = current_line_start;
       txtEdtPiece.rtPiece.width = piece->m_iWidth / 20000.0f;
       txtEdtPiece.rtPiece.height = line_spacing_;
+      txtEdtPiece.nStart = current_piece_start;
+      txtEdtPiece.nCount = piece->GetLength();
+      txtEdtPiece.nBidiLevel = piece->m_iBidiLevel;
+      txtEdtPiece.dwCharStyles = piece->m_dwCharStyles;
+      if (FX_IsOdd(piece->m_iBidiLevel))
+        txtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;
+
       text_piece_info_.push_back(txtEdtPiece);
 
       if (initialized_bounding_box) {
@@ -1037,13 +1136,7 @@
   if (IsAlignedRight() && bounds_smaller) {
     delta = available_width_ - contents_bounding_box_.width;
   } else if (IsAlignedCenter() && bounds_smaller) {
-    // TODO(dsinclair): Old code used CombText here and set the space to
-    // something unrelated to the available width .... Figure out if this is
-    // needed and what it should do.
-    // if (is_comb_text_) {
-    // } else {
     delta = (available_width_ - contents_bounding_box_.width) / 2.0f;
-    // }
   }
 
   if (delta != 0.0) {
@@ -1168,17 +1261,17 @@
   WordBreakProperty ePreType = WordBreakProperty::kNone;
   if (!IsEOF(!bPrev)) {
     Next(!bPrev);
-    ePreType = GetWordBreakProperty(GetChar());
+    ePreType = FX_GetWordBreakProperty(GetChar());
     Next(bPrev);
   }
 
-  WordBreakProperty eCurType = GetWordBreakProperty(GetChar());
+  WordBreakProperty eCurType = FX_GetWordBreakProperty(GetChar());
   bool bFirst = true;
   while (!IsEOF(bPrev)) {
     Next(bPrev);
 
-    WordBreakProperty eNextType = GetWordBreakProperty(GetChar());
-    bool wBreak = CheckStateChangeForWordBreak(eCurType, eNextType);
+    WordBreakProperty eNextType = FX_GetWordBreakProperty(GetChar());
+    bool wBreak = FX_CheckStateChangeForWordBreak(eCurType, eNextType);
     if (wBreak) {
       if (IsEOF(bPrev)) {
         Next(!bPrev);
@@ -1203,7 +1296,7 @@
         }
 
         Next(bPrev);
-        eNextType = GetWordBreakProperty(GetChar());
+        eNextType = FX_GetWordBreakProperty(GetChar());
         if (BreakFlagsChanged(nFlags, eNextType)) {
           Next(!bPrev);
           Next(!bPrev);
diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h
index 58f77ed..d68aa41 100644
--- a/xfa/fde/cfde_texteditengine.h
+++ b/xfa/fde/cfde_texteditengine.h
@@ -13,11 +13,11 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/fx_dib.h"
 #include "xfa/fgas/layout/cfx_txtbreak.h"
 
 class CFGAS_GEFont;
+class TextCharPos;
 
 struct FDE_TEXTEDITPIECE {
   FDE_TEXTEDITPIECE();
@@ -36,7 +36,7 @@
     default;
 inline FDE_TEXTEDITPIECE::~FDE_TEXTEDITPIECE() = default;
 
-class CFDE_TextEditEngine {
+class CFDE_TextEditEngine : public CFX_TxtBreak::Engine {
  public:
   class Iterator {
    public:
@@ -61,24 +61,34 @@
     virtual void Undo() const = 0;
   };
 
+  struct TextChange {
+    WideString text;
+    WideString previous_text;
+    size_t selection_start;
+    size_t selection_end;
+    bool cancelled;
+  };
+
   class Delegate {
    public:
     virtual ~Delegate() = default;
     virtual void NotifyTextFull() = 0;
     virtual void OnCaretChanged() = 0;
-    virtual void OnTextChanged(const WideString& prevText) = 0;
+    virtual void OnTextWillChange(TextChange* change) = 0;
+    virtual void OnTextChanged() = 0;
     virtual void OnSelChanged() = 0;
     virtual bool OnValidate(const WideString& wsText) = 0;
     virtual void SetScrollOffset(float fScrollOffset) = 0;
   };
 
-  enum class RecordOperation {
-    kInsertRecord,
-    kSkipRecord,
-  };
+  enum class RecordOperation { kInsertRecord, kSkipRecord, kSkipNotify };
 
   CFDE_TextEditEngine();
-  ~CFDE_TextEditEngine();
+  ~CFDE_TextEditEngine() override;
+
+  // CFX_TxtBreak::Engine:
+  wchar_t GetChar(size_t idx) const override;
+  size_t GetWidthOfChar(size_t idx) override;
 
   void SetDelegate(Delegate* delegate) { delegate_ = delegate; }
   void Clear();
@@ -103,7 +113,6 @@
   float GetFontSize() const { return font_size_; }
   void SetFontColor(FX_ARGB color) { font_color_ = color; }
   FX_ARGB GetFontColor() const { return font_color_; }
-  float GetFontAscent() const;
 
   void SetAlignment(uint32_t alignment);
   float GetLineSpace() const { return line_spacing_; }
@@ -152,9 +161,6 @@
 
   void Layout();
 
-  wchar_t GetChar(size_t idx) const;
-  // Non-const so we can force a Layout() if needed.
-  size_t GetWidthOfChar(size_t idx);
   // Non-const so we can force a Layout() if needed.
   size_t GetIndexForPoint(const CFX_PointF& point);
   // <start_idx, count>
@@ -165,15 +171,13 @@
   std::vector<CFX_RectF> GetCharacterRectsInRange(int32_t start_idx,
                                                   int32_t count);
 
-  CFX_TxtBreak* GetTextBreak() { return &text_break_; }
-
   const std::vector<FDE_TEXTEDITPIECE>& GetTextPieces() {
     // Force a layout if needed.
     Layout();
     return text_piece_info_;
   }
 
-  std::vector<FXTEXT_CHARPOS> GetDisplayPos(const FDE_TEXTEDITPIECE& info);
+  std::vector<TextCharPos> GetDisplayPos(const FDE_TEXTEDITPIECE& info);
 
   void SetMaxEditOperationsForTesting(size_t max);
 
@@ -209,8 +213,11 @@
   float line_spacing_;
   std::vector<WideString::CharType> content_;
   size_t text_length_;
+
+  // See e.g. https://en.wikipedia.org/wiki/Gap_buffer
   size_t gap_position_;
   size_t gap_size_;
+
   size_t available_width_;
   size_t character_limit_;
   size_t visible_line_count_;
diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp
index 522e61a..2089dff 100644
--- a/xfa/fde/cfde_texteditengine_unittest.cpp
+++ b/xfa/fde/cfde_texteditengine_unittest.cpp
@@ -4,14 +4,15 @@
 
 #include "xfa/fde/cfde_texteditengine.h"
 
+#include "core/fxge/text_char_pos.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
+#include "testing/xfa_unit_test_support.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 
 class CFDE_TextEditEngineTest : public testing::Test {
  public:
-  class Delegate : public CFDE_TextEditEngine::Delegate {
+  class Delegate final : public CFDE_TextEditEngine::Delegate {
    public:
     void Reset() {
       text_is_full = false;
@@ -21,7 +22,8 @@
     void NotifyTextFull() override { text_is_full = true; }
 
     void OnCaretChanged() override {}
-    void OnTextChanged(const WideString& prevText) override {}
+    void OnTextWillChange(CFDE_TextEditEngine::TextChange* change) override {}
+    void OnTextChanged() override {}
     void OnSelChanged() override {}
     bool OnValidate(const WideString& wsText) override {
       return !fail_validation;
@@ -38,7 +40,7 @@
   void SetUp() override {
     font_ =
         CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager());
-    ASSERT(font_.Get() != nullptr);
+    ASSERT_TRUE(font_.Get() != nullptr);
 
     engine_ = pdfium::MakeUnique<CFDE_TextEditEngine>();
     engine_->SetFont(font_);
@@ -159,6 +161,53 @@
   engine()->SetDelegate(nullptr);
 }
 
+TEST_F(CFDE_TextEditEngineTest, InsertToggleLimit) {
+  engine()->SetHasCharacterLimit(true);
+  engine()->Insert(0, L"Hello World");
+  engine()->SetCharacterLimit(5);
+  engine()->Insert(0, L"Not Inserted before ");
+  EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
+
+  engine()->SetHasCharacterLimit(false);
+  engine()->Insert(0, L"Inserted before ");
+  engine()->SetHasCharacterLimit(true);
+  engine()->Insert(0, L"Not Inserted before ");
+  EXPECT_STREQ(L"Inserted before Hello World", engine()->GetText().c_str());
+}
+
+TEST_F(CFDE_TextEditEngineTest, InsertSkipNotify) {
+  engine()->SetHasCharacterLimit(true);
+  engine()->SetCharacterLimit(8);
+  engine()->Insert(0, L"Hello");
+  engine()->Insert(5, L" World",
+                   CFDE_TextEditEngine::RecordOperation::kSkipNotify);
+  EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
+
+  engine()->Insert(0, L"Not inserted");
+  EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
+
+  engine()->Delete(5, 1);
+  EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str());
+
+  engine()->Insert(0, L"****");
+  EXPECT_STREQ(L"*HelloWorld", engine()->GetText().c_str());
+}
+
+TEST_F(CFDE_TextEditEngineTest, InsertGrowGap) {
+  engine()->Insert(0, L"||");
+  for (size_t i = 1; i < 1023; ++i) {
+    engine()->Insert(i, L"a");
+  }
+  WideString result = engine()->GetText();
+  ASSERT_EQ(result.GetLength(), 1024u);
+  EXPECT_EQ(result[0], L'|');
+  EXPECT_EQ(result[1], L'a');
+  EXPECT_EQ(result[2], L'a');
+  // ...
+  EXPECT_EQ(result[1022], L'a');
+  EXPECT_EQ(result[1023], L'|');
+}
+
 TEST_F(CFDE_TextEditEngineTest, Delete) {
   EXPECT_STREQ(L"", engine()->Delete(0, 50).c_str());
   EXPECT_STREQ(L"", engine()->GetText().c_str());
@@ -415,9 +464,41 @@
   engine()->Insert(0, L"Hello World");
   EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
+  EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
+  EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
   EXPECT_EQ(1U, engine()->GetIndexForPoint({10.0f, 5.0f}));
 }
 
+TEST_F(CFDE_TextEditEngineTest, GetIndexForPointLineWrap) {
+  engine()->SetFontSize(10.0f);
+  engine()->Insert(0,
+                   L"A text long enough to span multiple lines and test "
+                   L"getting indexes on multi-line edits.");
+  EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
+  EXPECT_EQ(87U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
+  EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
+  EXPECT_EQ(12U, engine()->GetIndexForPoint({1.0f, 10.0f}));
+  EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
+  EXPECT_EQ(2U, engine()->GetIndexForPoint({10.0f, 5.0f}));
+}
+
+TEST_F(CFDE_TextEditEngineTest, GetIndexForPointSpaceAtEnd) {
+  engine()->SetFontSize(10.0f);
+  engine()->Insert(0, L"Hello World ");
+  EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
+  EXPECT_EQ(12U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
+  EXPECT_EQ(12U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
+}
+
+TEST_F(CFDE_TextEditEngineTest, GetIndexForPointLineBreaks) {
+  engine()->SetFontSize(10.0f);
+  engine()->Insert(0, L"Hello\nWorld");
+  EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
+  EXPECT_EQ(5U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
+  EXPECT_EQ(6U, engine()->GetIndexForPoint({0.0f, 10.0f}));
+  EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
+}
+
 TEST_F(CFDE_TextEditEngineTest, BoundsForWordAt) {
   size_t start_idx;
   size_t count;
diff --git a/xfa/fde/cfde_textout.cpp b/xfa/fde/cfde_textout.cpp
index 19e7ae6..cd419bd 100644
--- a/xfa/fde/cfde_textout.cpp
+++ b/xfa/fde/cfde_textout.cpp
@@ -9,10 +9,15 @@
 #include <algorithm>
 #include <utility>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/cfx_substfont.h"
+#include "core/fxge/fx_font.h"
+#include "core/fxge/text_char_pos.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
@@ -36,84 +41,82 @@
 bool CFDE_TextOut::DrawString(CFX_RenderDevice* device,
                               FX_ARGB color,
                               const RetainPtr<CFGAS_GEFont>& pFont,
-                              FXTEXT_CHARPOS* pCharPos,
-                              int32_t iCount,
+                              pdfium::span<TextCharPos> pCharPos,
                               float fFontSize,
-                              const CFX_Matrix* pMatrix) {
-  ASSERT(pFont && pCharPos && iCount > 0);
+                              const CFX_Matrix& matrix) {
+  ASSERT(pFont);
+  ASSERT(!pCharPos.empty());
 
   CFX_Font* pFxFont = pFont->GetDevFont();
   if (FontStyleIsItalic(pFont->GetFontStyles()) && !pFxFont->IsItalic()) {
-    for (int32_t i = 0; i < iCount; ++i) {
-      static const float mc = 0.267949f;
-      float* pAM = pCharPos->m_AdjustMatrix;
-      pAM[2] = mc * pAM[0] + pAM[2];
-      pAM[3] = mc * pAM[1] + pAM[3];
-      ++pCharPos;
+    for (auto& pos : pCharPos) {
+      static constexpr float mc = 0.267949f;
+      pos.m_AdjustMatrix[2] += mc * pos.m_AdjustMatrix[0];
+      pos.m_AdjustMatrix[3] += mc * pos.m_AdjustMatrix[1];
     }
   }
 
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
   uint32_t dwFontStyle = pFont->GetFontStyles();
   CFX_Font FxFont;
   auto SubstFxFont = pdfium::MakeUnique<CFX_SubstFont>();
-  SubstFxFont->m_Weight = FontStyleIsBold(dwFontStyle) ? 700 : 400;
+  SubstFxFont->m_Weight = FontStyleIsForceBold(dwFontStyle) ? 700 : 400;
   SubstFxFont->m_ItalicAngle = FontStyleIsItalic(dwFontStyle) ? -12 : 0;
   SubstFxFont->m_WeightCJK = SubstFxFont->m_Weight;
   SubstFxFont->m_bItalicCJK = FontStyleIsItalic(dwFontStyle);
   FxFont.SetSubstFont(std::move(SubstFxFont));
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#endif
 
   RetainPtr<CFGAS_GEFont> pCurFont;
-  FXTEXT_CHARPOS* pCurCP = nullptr;
+  TextCharPos* pCurCP = nullptr;
   int32_t iCurCount = 0;
-  for (int32_t i = 0; i < iCount; ++i) {
+  for (auto& pos : pCharPos) {
     RetainPtr<CFGAS_GEFont> pSTFont =
-        pFont->GetSubstFont(static_cast<int32_t>(pCharPos->m_GlyphIndex));
-    pCharPos->m_GlyphIndex &= 0x00FFFFFF;
-    pCharPos->m_bFontStyle = false;
+        pFont->GetSubstFont(static_cast<int32_t>(pos.m_GlyphIndex));
+    pos.m_GlyphIndex &= 0x00FFFFFF;
+    pos.m_bFontStyle = false;
     if (pCurFont != pSTFont) {
       if (pCurFont) {
         pFxFont = pCurFont->GetDevFont();
 
         CFX_Font* font;
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
         FxFont.SetFace(pFxFont->GetFace());
+        FxFont.SetFontSpan(pFxFont->GetFontSpan());
         font = &FxFont;
 #else
         font = pFxFont;
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#endif
 
-        device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, pMatrix,
+        device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, matrix,
                                color, FXTEXT_CLEARTYPE);
       }
       pCurFont = pSTFont;
-      pCurCP = pCharPos;
+      pCurCP = &pos;
       iCurCount = 1;
     } else {
       ++iCurCount;
     }
-    ++pCharPos;
   }
 
   bool bRet = true;
   if (pCurFont && iCurCount) {
     pFxFont = pCurFont->GetDevFont();
     CFX_Font* font;
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
     FxFont.SetFace(pFxFont->GetFace());
+    FxFont.SetFontSpan(pFxFont->GetFontSpan());
     font = &FxFont;
 #else
     font = pFxFont;
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#endif
 
-    bRet = device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, pMatrix,
+    bRet = device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, matrix,
                                   color, FXTEXT_CLEARTYPE);
   }
-
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
-  FxFont.SetFace(nullptr);
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
+  device->Flush(false);
+#endif
 
   return bRet;
 }
@@ -125,21 +128,9 @@
 FDE_TTOPIECE::~FDE_TTOPIECE() = default;
 
 CFDE_TextOut::CFDE_TextOut()
-    : m_pTxtBreak(pdfium::MakeUnique<CFX_TxtBreak>()),
-      m_pFont(nullptr),
-      m_fFontSize(12.0f),
-      m_fLineSpace(m_fFontSize),
-      m_fLinePos(0.0f),
-      m_fTolerance(0.0f),
-      m_iAlignment(FDE_TextAlignment::kTopLeft),
-      m_TxtColor(0xFF000000),
-      m_dwTxtBkStyles(0),
-      m_ttoLines(5),
-      m_iCurLine(0),
-      m_iCurPiece(0),
-      m_iTotalLines(0) {}
+    : m_pTxtBreak(pdfium::MakeUnique<CFX_TxtBreak>()), m_ttoLines(5) {}
 
-CFDE_TextOut::~CFDE_TextOut() {}
+CFDE_TextOut::~CFDE_TextOut() = default;
 
 void CFDE_TextOut::SetFont(const RetainPtr<CFGAS_GEFont>& pFont) {
   ASSERT(pFont);
@@ -192,32 +183,33 @@
   m_pTxtBreak->SetLineBreakTolerance(m_fTolerance);
 }
 
-void CFDE_TextOut::CalcLogicSize(const WideString& str, CFX_SizeF& size) {
-  CFX_RectF rtText(0.0f, 0.0f, size.width, size.height);
-  CalcLogicSize(str, rtText);
-  size = rtText.Size();
+void CFDE_TextOut::CalcLogicSize(WideStringView str, CFX_SizeF* pSize) {
+  CFX_RectF rtText(0.0f, 0.0f, pSize->width, pSize->height);
+  CalcLogicSize(str, &rtText);
+  *pSize = rtText.Size();
 }
 
-void CFDE_TextOut::CalcLogicSize(const WideString& str, CFX_RectF& rect) {
+void CFDE_TextOut::CalcLogicSize(WideStringView str, CFX_RectF* pRect) {
   if (str.IsEmpty()) {
-    rect.width = 0.0f;
-    rect.height = 0.0f;
+    pRect->width = 0.0f;
+    pRect->height = 0.0f;
     return;
   }
 
-  ASSERT(m_pFont && m_fFontSize >= 1.0f);
+  ASSERT(m_pFont);
+  ASSERT(m_fFontSize >= 1.0f);
 
   if (!m_Styles.single_line_) {
-    if (rect.Width() < 1.0f)
-      rect.width = m_fFontSize * 1000.0f;
+    if (pRect->Width() < 1.0f)
+      pRect->width = m_fFontSize * 1000.0f;
 
-    m_pTxtBreak->SetLineWidth(rect.Width());
+    m_pTxtBreak->SetLineWidth(pRect->Width());
   }
 
   m_iTotalLines = 0;
   float fWidth = 0.0f;
   float fHeight = 0.0f;
-  float fStartPos = rect.right();
+  float fStartPos = pRect->right();
   CFX_BreakType dwBreakStatus = CFX_BreakType::None;
   bool break_char_is_set = false;
   for (const wchar_t& wch : str) {
@@ -227,32 +219,32 @@
     }
     dwBreakStatus = m_pTxtBreak->AppendChar(wch);
     if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus))
-      RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight);
+      RetrieveLineWidth(dwBreakStatus, &fStartPos, &fWidth, &fHeight);
   }
 
   dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph);
   if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus))
-    RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight);
+    RetrieveLineWidth(dwBreakStatus, &fStartPos, &fWidth, &fHeight);
 
   m_pTxtBreak->Reset();
-  float fInc = rect.Height() - fHeight;
+  float fInc = pRect->Height() - fHeight;
   if (TextAlignmentVerticallyCentered(m_iAlignment))
     fInc /= 2.0f;
   else if (IsTextAlignmentTop(m_iAlignment))
     fInc = 0.0f;
 
-  rect.left += fStartPos;
-  rect.top += fInc;
-  rect.width = std::min(fWidth, rect.Width());
-  rect.height = fHeight;
+  pRect->left += fStartPos;
+  pRect->top += fInc;
+  pRect->width = std::min(fWidth, pRect->Width());
+  pRect->height = fHeight;
   if (m_Styles.last_line_height_)
-    rect.height -= m_fLineSpace - m_fFontSize;
+    pRect->height -= m_fLineSpace - m_fFontSize;
 }
 
 bool CFDE_TextOut::RetrieveLineWidth(CFX_BreakType dwBreakStatus,
-                                     float& fStartPos,
-                                     float& fWidth,
-                                     float& fHeight) {
+                                     float* pStartPos,
+                                     float* pWidth,
+                                     float* pHeight) {
   if (CFX_BreakTypeNoneOrPiece(dwBreakStatus))
     return false;
 
@@ -261,27 +253,28 @@
   for (int32_t i = 0; i < m_pTxtBreak->CountBreakPieces(); i++) {
     const CFX_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i);
     fLineWidth += static_cast<float>(pPiece->m_iWidth) / 20000.0f;
-    fStartPos =
-        std::min(fStartPos, static_cast<float>(pPiece->m_iStartPos) / 20000.0f);
+    *pStartPos = std::min(*pStartPos,
+                          static_cast<float>(pPiece->m_iStartPos) / 20000.0f);
   }
   m_pTxtBreak->ClearBreakPieces();
 
   if (dwBreakStatus == CFX_BreakType::Paragraph)
     m_pTxtBreak->Reset();
   if (!m_Styles.line_wrap_ && dwBreakStatus == CFX_BreakType::Line) {
-    fWidth += fLineWidth;
+    *pWidth += fLineWidth;
   } else {
-    fWidth = std::max(fWidth, fLineWidth);
-    fHeight += fLineStep;
+    *pWidth = std::max(*pWidth, fLineWidth);
+    *pHeight += fLineStep;
   }
   ++m_iTotalLines;
   return true;
 }
 
 void CFDE_TextOut::DrawLogicText(CFX_RenderDevice* device,
-                                 const WideStringView& str,
+                                 WideStringView str,
                                  const CFX_RectF& rect) {
-  ASSERT(m_pFont && m_fFontSize >= 1.0f);
+  ASSERT(m_pFont);
+  ASSERT(m_fFontSize >= 1.0f);
 
   if (str.IsEmpty())
     return;
@@ -303,7 +296,7 @@
   CFX_RectF rtClip = m_Matrix.TransformRect(CFX_RectF());
   device->SaveState();
   if (rtClip.Width() > 0.0f && rtClip.Height() > 0.0f)
-    device->SetClip_Rect(rtClip);
+    device->SetClip_Rect(rtClip.GetOuterRect());
 
   for (auto& line : m_ttoLines) {
     int32_t iPieces = line.GetSize();
@@ -312,10 +305,11 @@
       if (!pPiece)
         continue;
 
-      int32_t iCount = GetDisplayPos(pPiece);
-      if (iCount > 0) {
-        CFDE_TextOut::DrawString(device, m_TxtColor, m_pFont, m_CharPos.data(),
-                                 iCount, m_fFontSize, &m_Matrix);
+      size_t szCount = GetDisplayPos(pPiece);
+      if (szCount > 0) {
+        CFDE_TextOut::DrawString(device, m_TxtColor, m_pFont,
+                                 {m_CharPos.data(), szCount}, m_fFontSize,
+                                 m_Matrix);
       }
     }
   }
@@ -343,7 +337,7 @@
       continue;
 
     bool bEndofLine =
-        RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect);
+        RetrievePieces(dwBreakStatus, false, rect, &iStartChar, &iPieceWidths);
     if (bEndofLine &&
         (m_Styles.line_wrap_ || dwBreakStatus == CFX_BreakType::Paragraph ||
          dwBreakStatus == CFX_BreakType::Page)) {
@@ -361,32 +355,32 @@
 
   dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph);
   if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus) && !bRet)
-    RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect);
+    RetrievePieces(dwBreakStatus, false, rect, &iStartChar, &iPieceWidths);
 
   m_pTxtBreak->ClearBreakPieces();
   m_pTxtBreak->Reset();
 }
 
 bool CFDE_TextOut::RetrievePieces(CFX_BreakType dwBreakStatus,
-                                  int32_t& iStartChar,
-                                  int32_t& iPieceWidths,
                                   bool bReload,
-                                  const CFX_RectF& rect) {
+                                  const CFX_RectF& rect,
+                                  int32_t* pStartChar,
+                                  int32_t* pPieceWidths) {
   float fLineStep = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;
   bool bNeedReload = false;
-  int32_t iLineWidth = FXSYS_round(rect.Width() * 20000.0f);
+  int32_t iLineWidth = FXSYS_roundf(rect.Width() * 20000.0f);
   int32_t iCount = m_pTxtBreak->CountBreakPieces();
   for (int32_t i = 0; i < iCount; i++) {
     const CFX_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i);
     int32_t iPieceChars = pPiece->GetLength();
-    int32_t iChar = iStartChar;
+    int32_t iChar = *pStartChar;
     int32_t iWidth = 0;
     int32_t j = 0;
     for (; j < iPieceChars; j++) {
       const CFX_Char* pTC = pPiece->GetChar(j);
       int32_t iCurCharWidth = pTC->m_iCharWidth > 0 ? pTC->m_iCharWidth : 0;
       if (m_Styles.single_line_ || !m_Styles.line_wrap_) {
-        if (iLineWidth - iPieceWidths - iWidth < iCurCharWidth) {
+        if (iLineWidth - *pPieceWidths - iWidth < iCurCharWidth) {
           bNeedReload = true;
           break;
         }
@@ -399,7 +393,7 @@
       m_ttoLines[m_iCurLine].SetNewReload(true);
     } else if (j > 0) {
       FDE_TTOPIECE ttoPiece;
-      ttoPiece.iStartChar = iStartChar;
+      ttoPiece.iStartChar = *pStartChar;
       ttoPiece.iChars = j;
       ttoPiece.dwCharStyles = pPiece->m_dwCharStyles;
       ttoPiece.rtPiece = CFX_RectF(
@@ -411,8 +405,8 @@
 
       AppendPiece(ttoPiece, bNeedReload, (bReload && i == iCount - 1));
     }
-    iStartChar += iPieceChars;
-    iPieceWidths += iWidth;
+    *pStartChar += iPieceChars;
+    *pPieceWidths += iWidth;
   }
   m_pTxtBreak->ClearBreakPieces();
 
@@ -458,24 +452,23 @@
 }
 
 void CFDE_TextOut::ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect) {
-  const wchar_t* pwsStr = m_wsText.c_str();
-  int32_t iPieceWidths = 0;
-
+  pdfium::span<const wchar_t> text_span = m_wsText.span();
   FDE_TTOPIECE* pPiece = pLine->GetPtrAt(0);
   int32_t iStartChar = pPiece->iStartChar;
   int32_t iPieceCount = pLine->GetSize();
+  int32_t iPieceWidths = 0;
   int32_t iPieceIndex = 0;
   CFX_BreakType dwBreakStatus = CFX_BreakType::None;
   m_fLinePos = pPiece->rtPiece.top;
   while (iPieceIndex < iPieceCount) {
-    int32_t iStar = iStartChar;
-    int32_t iEnd = pPiece->iChars + iStar;
-    while (iStar < iEnd) {
-      dwBreakStatus = m_pTxtBreak->AppendChar(*(pwsStr + iStar));
+    int32_t iStart = iStartChar;
+    int32_t iEnd = pPiece->iChars + iStart;
+    while (iStart < iEnd) {
+      dwBreakStatus = m_pTxtBreak->AppendChar(text_span[iStart]);
       if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus))
-        RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect);
+        RetrievePieces(dwBreakStatus, true, rect, &iStartChar, &iPieceWidths);
 
-      ++iStar;
+      ++iStart;
     }
     ++iPieceIndex;
     pPiece = pLine->GetPtrAt(iPieceIndex);
@@ -483,7 +476,7 @@
 
   dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph);
   if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus))
-    RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect);
+    RetrievePieces(dwBreakStatus, true, rect, &iStartChar, &iPieceWidths);
 
   m_pTxtBreak->Reset();
 }
@@ -512,13 +505,13 @@
   }
 }
 
-int32_t CFDE_TextOut::GetDisplayPos(FDE_TTOPIECE* pPiece) {
+size_t CFDE_TextOut::GetDisplayPos(FDE_TTOPIECE* pPiece) {
   ASSERT(pPiece->iChars >= 0);
 
   if (pdfium::CollectionSize<int32_t>(m_CharPos) < pPiece->iChars)
-    m_CharPos.resize(pPiece->iChars, FXTEXT_CHARPOS());
+    m_CharPos.resize(pPiece->iChars, TextCharPos());
 
-  FX_TXTRUN tr;
+  CFX_TxtBreak::Run tr;
   tr.wsStr = m_wsText + pPiece->iStartChar;
   tr.pWidths = &m_CharWidths[pPiece->iStartChar];
   tr.iLength = pPiece->iChars;
diff --git a/xfa/fde/cfde_textout.h b/xfa/fde/cfde_textout.h
index 6b6878e..bb65d28 100644
--- a/xfa/fde/cfde_textout.h
+++ b/xfa/fde/cfde_textout.h
@@ -11,26 +11,36 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/cfx_char.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/fx_dib.h"
+#include "third_party/base/span.h"
 #include "xfa/fde/cfde_data.h"
+#include "xfa/fgas/layout/cfx_char.h"
 
 class CFDE_RenderDevice;
 class CFGAS_GEFont;
 class CFX_RenderDevice;
 class CFX_TxtBreak;
+class TextCharPos;
+
+struct FDE_TTOPIECE {
+  FDE_TTOPIECE();
+  FDE_TTOPIECE(const FDE_TTOPIECE& that);
+  ~FDE_TTOPIECE();
+
+  int32_t iStartChar;
+  int32_t iChars;
+  uint32_t dwCharStyles;
+  CFX_RectF rtPiece;
+};
 
 class CFDE_TextOut {
  public:
   static bool DrawString(CFX_RenderDevice* device,
                          FX_ARGB color,
                          const RetainPtr<CFGAS_GEFont>& pFont,
-                         FXTEXT_CHARPOS* pCharPos,
-                         int32_t iCount,
+                         pdfium::span<TextCharPos> pCharPos,
                          float fFontSize,
-                         const CFX_Matrix* pMatrix);
+                         const CFX_Matrix& matrix);
 
   CFDE_TextOut();
   ~CFDE_TextOut();
@@ -44,10 +54,10 @@
   void SetMatrix(const CFX_Matrix& matrix) { m_Matrix = matrix; }
   void SetLineBreakTolerance(float fTolerance);
 
-  void CalcLogicSize(const WideString& str, CFX_SizeF& size);
-  void CalcLogicSize(const WideString& str, CFX_RectF& rect);
+  void CalcLogicSize(WideStringView str, CFX_SizeF* pSize);
+  void CalcLogicSize(WideStringView str, CFX_RectF* pRect);
   void DrawLogicText(CFX_RenderDevice* device,
-                     const WideStringView& str,
+                     WideStringView str,
                      const CFX_RectF& rect);
   int32_t GetTotalLines() const { return m_iTotalLines; }
 
@@ -71,40 +81,40 @@
   };
 
   bool RetrieveLineWidth(CFX_BreakType dwBreakStatus,
-                         float& fStartPos,
-                         float& fWidth,
-                         float& fHeight);
+                         float* pStartPos,
+                         float* pWidth,
+                         float* pHeight);
   void LoadText(const WideString& str, const CFX_RectF& rect);
 
   void Reload(const CFX_RectF& rect);
   void ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect);
   bool RetrievePieces(CFX_BreakType dwBreakStatus,
-                      int32_t& iStartChar,
-                      int32_t& iPieceWidths,
                       bool bReload,
-                      const CFX_RectF& rect);
+                      const CFX_RectF& rect,
+                      int32_t* pStartChar,
+                      int32_t* pPieceWidths);
   void AppendPiece(const FDE_TTOPIECE& ttoPiece, bool bNeedReload, bool bEnd);
   void DoAlignment(const CFX_RectF& rect);
-  int32_t GetDisplayPos(FDE_TTOPIECE* pPiece);
+  size_t GetDisplayPos(FDE_TTOPIECE* pPiece);
 
-  std::unique_ptr<CFX_TxtBreak> m_pTxtBreak;
+  std::unique_ptr<CFX_TxtBreak> const m_pTxtBreak;
   RetainPtr<CFGAS_GEFont> m_pFont;
-  float m_fFontSize;
-  float m_fLineSpace;
-  float m_fLinePos;
-  float m_fTolerance;
-  FDE_TextAlignment m_iAlignment;
+  float m_fFontSize = 12.0f;
+  float m_fLineSpace = 12.0f;
+  float m_fLinePos = 0.0f;
+  float m_fTolerance = 0.0f;
+  FDE_TextAlignment m_iAlignment = FDE_TextAlignment::kTopLeft;
   FDE_TextStyle m_Styles;
   std::vector<int32_t> m_CharWidths;
-  FX_ARGB m_TxtColor;
-  uint32_t m_dwTxtBkStyles;
+  FX_ARGB m_TxtColor = 0xFF000000;
+  uint32_t m_dwTxtBkStyles = 0;
   WideString m_wsText;
   CFX_Matrix m_Matrix;
   std::deque<CFDE_TTOLine> m_ttoLines;
-  int32_t m_iCurLine;
-  int32_t m_iCurPiece;
-  int32_t m_iTotalLines;
-  std::vector<FXTEXT_CHARPOS> m_CharPos;
+  int32_t m_iCurLine = 0;
+  int32_t m_iCurPiece = 0;
+  int32_t m_iTotalLines = 0;
+  std::vector<TextCharPos> m_CharPos;
 };
 
 #endif  // XFA_FDE_CFDE_TEXTOUT_H_
diff --git a/xfa/fde/cfde_wordbreak_data.cpp b/xfa/fde/cfde_wordbreak_data.cpp
index 3c4864b..9331253 100644
--- a/xfa/fde/cfde_wordbreak_data.cpp
+++ b/xfa/fde/cfde_wordbreak_data.cpp
@@ -6,115 +6,81 @@
 
 #include "xfa/fde/cfde_wordbreak_data.h"
 
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_system.h"
+
 namespace {
 
-enum WordBreakValue : uint16_t {
-  kWordBreakValueNone = 1 << 0,
-  kWordBreakValueCR = 1 << 1,
-  kWordBreakValueLF = 1 << 2,
-  kWordBreakValueNewLine = 1 << 3,
-  kWordBreakValueExtend = 1 << 4,
-  kWordBreakValueFormat = 1 << 5,
-  kWordBreakValueKataKana = 1 << 6,
-  kWordBreakValueALetter = 1 << 7,
-  kWordBreakValueMidLetter = 1 << 8,
-  kWordBreakValueMidNum = 1 << 9,
-  kWordBreakValueMidNumLet = 1 << 10,
-  kWordBreakValueNumeric = 1 << 11,
-  kWordBreakValueExtendNumLet = 1 << 12,
+enum WordBreakMask : uint16_t {
+  kWordBreakMaskNone = 1 << static_cast<int>(WordBreakProperty::kNone),
+  kWordBreakMaskCR = 1 << static_cast<int>(WordBreakProperty::kCR),
+  kWordBreakMaskLF = 1 << static_cast<int>(WordBreakProperty::kLF),
+  kWordBreakMaskNewLine = 1 << static_cast<int>(WordBreakProperty::kNewLine),
+  kWordBreakMaskExtend = 1 << static_cast<int>(WordBreakProperty::kExtend),
+  kWordBreakMaskFormat = 1 << static_cast<int>(WordBreakProperty::kFormat),
+  kWordBreakMaskKataKana = 1 << static_cast<int>(WordBreakProperty::kKataKana),
+  kWordBreakMaskALetter = 1 << static_cast<int>(WordBreakProperty::kALetter),
+  kWordBreakMaskMidLetter = 1
+                            << static_cast<int>(WordBreakProperty::kMidLetter),
+  kWordBreakMaskMidNum = 1 << static_cast<int>(WordBreakProperty::kMidNum),
+  kWordBreakMaskMidNumLet = 1
+                            << static_cast<int>(WordBreakProperty::kMidNumLet),
+  kWordBreakMaskNumeric = 1 << static_cast<int>(WordBreakProperty::kNumeric),
+  kWordBreakMaskExtendNumLet =
+      1 << static_cast<int>(WordBreakProperty::kExtendNumLet),
 };
 
-static_assert(kWordBreakValueNone ==
-                  (1 << static_cast<int>(WordBreakProperty::kNone)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueCR ==
-                  (1 << static_cast<int>(WordBreakProperty::kCR)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueLF ==
-                  (1 << static_cast<int>(WordBreakProperty::kLF)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueNewLine ==
-                  (1 << static_cast<int>(WordBreakProperty::kNewLine)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueExtend ==
-                  (1 << static_cast<int>(WordBreakProperty::kExtend)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueFormat ==
-                  (1 << static_cast<int>(WordBreakProperty::kFormat)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueKataKana ==
-                  (1 << static_cast<int>(WordBreakProperty::kKataKana)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueALetter ==
-                  (1 << static_cast<int>(WordBreakProperty::kALetter)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueMidLetter ==
-                  (1 << static_cast<int>(WordBreakProperty::kMidLetter)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueMidNum ==
-                  (1 << static_cast<int>(WordBreakProperty::kMidNum)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueMidNumLet ==
-                  (1 << static_cast<int>(WordBreakProperty::kMidNumLet)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueNumeric ==
-                  (1 << static_cast<int>(WordBreakProperty::kNumeric)),
-              "WordBreakValue must match");
-static_assert(kWordBreakValueExtendNumLet ==
-                  (1 << static_cast<int>(WordBreakProperty::kExtendNumLet)),
-              "WordBreakValue must match");
-
-}  // namespace
-
-const uint16_t gs_FX_WordBreak_Table[] = {
+const uint16_t kWordBreakTable[] = {
     // WordBreakProperty::kNone
     0xFFFF,
 
     // WordBreakProperty::kCR
-    static_cast<uint16_t>(~(kWordBreakValueLF | kWordBreakValueCR)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF | kWordBreakMaskCR)),
 
     // WordBreakProperty::kLF
-    static_cast<uint16_t>(~(kWordBreakValueLF)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF)),
 
     // WordBreakProperty::kNewLine
-    static_cast<uint16_t>(~(kWordBreakValueLF)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF)),
 
     // WordBreakProperty::kExtend
-    static_cast<uint16_t>(~(kWordBreakValueLF)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF)),
 
     // WordBreakPropery:: kFormat
-    static_cast<uint16_t>(~(kWordBreakValueLF)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF)),
 
     // WordBreakProperty::kKataKana
-    static_cast<uint16_t>(~(kWordBreakValueLF | kWordBreakValueKataKana |
-                            kWordBreakValueExtendNumLet)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF | kWordBreakMaskKataKana |
+                            kWordBreakMaskExtendNumLet)),
 
     // WordBreakProperty::kALetter
-    static_cast<uint16_t>(~(kWordBreakValueLF | kWordBreakValueALetter |
-                            kWordBreakValueNumeric |
-                            kWordBreakValueExtendNumLet)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF | kWordBreakMaskALetter |
+                            kWordBreakMaskNumeric |
+                            kWordBreakMaskExtendNumLet)),
 
     // WordBreakProperty::kMidLetter
-    static_cast<uint16_t>(~(kWordBreakValueLF)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF)),
 
     // WordBreakProperty::kMidNum
-    static_cast<uint16_t>(~(kWordBreakValueLF)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF)),
 
     // WordBreakProperty::kMidNumLet
-    static_cast<uint16_t>(~(kWordBreakValueLF)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF)),
 
     // WordBreakProperty::kNumeric
-    static_cast<uint16_t>(~(kWordBreakValueLF | kWordBreakValueALetter |
-                            kWordBreakValueNumeric |
-                            kWordBreakValueExtendNumLet)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF | kWordBreakMaskALetter |
+                            kWordBreakMaskNumeric |
+                            kWordBreakMaskExtendNumLet)),
 
     // WordBreakProperty::kExtendNumLet
-    static_cast<uint16_t>(~(kWordBreakValueLF | kWordBreakValueKataKana |
-                            kWordBreakValueALetter | kWordBreakValueNumeric |
-                            kWordBreakValueExtendNumLet)),
+    static_cast<uint16_t>(~(kWordBreakMaskLF | kWordBreakMaskKataKana |
+                            kWordBreakMaskALetter | kWordBreakMaskNumeric |
+                            kWordBreakMaskExtendNumLet)),
 };
 
-const uint8_t gs_FX_WordBreak_CodePointProperties[(0xFFFF - 1) / 2 + 1] = {
+// Table of |WordBreakProperty| for each of the possible uint16_t values,
+// packed as nibbles, with the low nibble first.
+const uint8_t kCodePointProperties[32768] = {
     0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x90, 0xA0,
     0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x89, 0x00, 0x00, 0x07, 0x77, 0x77, 0x77,
@@ -2847,3 +2813,22 @@
     0x00, 0x77, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 0x00, 0x00,
 };
+
+}  // namespace
+
+bool FX_CheckStateChangeForWordBreak(WordBreakProperty from,
+                                     WordBreakProperty to) {
+  ASSERT(static_cast<int>(from) < 13);
+  return !!(kWordBreakTable[static_cast<int>(from)] &
+            static_cast<uint16_t>(1 << static_cast<int>(to)));
+}
+
+WordBreakProperty FX_GetWordBreakProperty(wchar_t wcCodePoint) {
+  size_t index = static_cast<size_t>(wcCodePoint) / 2;
+  if (index >= FX_ArraySize(kCodePointProperties))
+    return WordBreakProperty::kNone;
+
+  uint8_t dwProperty = kCodePointProperties[index];
+  return static_cast<WordBreakProperty>((wcCodePoint & 1) ? (dwProperty & 0x0F)
+                                                          : (dwProperty >> 4));
+}
diff --git a/xfa/fde/cfde_wordbreak_data.h b/xfa/fde/cfde_wordbreak_data.h
index 28e26cc..1465f5c 100644
--- a/xfa/fde/cfde_wordbreak_data.h
+++ b/xfa/fde/cfde_wordbreak_data.h
@@ -10,6 +10,8 @@
 #include <stdint.h>
 
 enum class WordBreakProperty : uint8_t {
+  // Internal tables depend on constants computed from these values, so do
+  // not re-order.
   kNone = 0,
   kCR,
   kLF,
@@ -25,7 +27,8 @@
   kExtendNumLet,
 };
 
-extern const uint16_t gs_FX_WordBreak_Table[];
-extern const uint8_t gs_FX_WordBreak_CodePointProperties[];
+bool FX_CheckStateChangeForWordBreak(WordBreakProperty from,
+                                     WordBreakProperty to);
+WordBreakProperty FX_GetWordBreakProperty(wchar_t wcCodePoint);
 
 #endif  // XFA_FDE_CFDE_WORDBREAK_DATA_H_
diff --git a/xfa/fgas/BUILD.gn b/xfa/fgas/BUILD.gn
new file mode 100644
index 0000000..c4c68b2
--- /dev/null
+++ b/xfa/fgas/BUILD.gn
@@ -0,0 +1,61 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fgas") {
+  sources = [
+    "crt/cfgas_decimal.cpp",
+    "crt/cfgas_decimal.h",
+    "crt/cfgas_stringformatter.cpp",
+    "crt/cfgas_stringformatter.h",
+    "crt/locale_iface.h",
+    "crt/locale_mgr_iface.h",
+    "font/cfgas_defaultfontmanager.cpp",
+    "font/cfgas_defaultfontmanager.h",
+    "font/cfgas_fontmgr.cpp",
+    "font/cfgas_fontmgr.h",
+    "font/cfgas_gefont.cpp",
+    "font/cfgas_gefont.h",
+    "font/cfgas_pdffontmgr.cpp",
+    "font/cfgas_pdffontmgr.h",
+    "font/fgas_fontutils.cpp",
+    "font/fgas_fontutils.h",
+  ]
+  deps = [
+    "../../core/fpdfapi/font",
+    "../../core/fpdfapi/page",
+    "../../core/fpdfapi/parser",
+    "../../core/fxcrt",
+    "../../core/fxge",
+  ]
+  configs += [
+    "../../:pdfium_core_config",
+    "../:xfa_warnings",
+  ]
+  visibility = [ "../../*" ]
+
+  if (!is_win) {
+    sources += [
+      "font/cfx_fontsourceenum_file.cpp",
+      "font/cfx_fontsourceenum_file.h",
+    ]
+  }
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "crt/cfgas_decimal_unittest.cpp",
+    "crt/cfgas_stringformatter_unittest.cpp",
+  ]
+  deps = [
+    ":fgas",
+    "../../core/fpdfapi/page",
+    "../fxfa/parser",
+  ]
+  pdfium_root_dir = "../../"
+}
diff --git a/xfa/fgas/crt/cfgas_decimal.cpp b/xfa/fgas/crt/cfgas_decimal.cpp
new file mode 100644
index 0000000..6a8a759
--- /dev/null
+++ b/xfa/fgas/crt/cfgas_decimal.cpp
@@ -0,0 +1,451 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/crt/cfgas_decimal.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "core/fxcrt/fx_extension.h"
+
+#define FXMATH_DECIMAL_SCALELIMIT 0x1c
+#define FXMATH_DECIMAL_RSHIFT32BIT(x) ((x) >> 0x10 >> 0x10)
+#define FXMATH_DECIMAL_LSHIFT32BIT(x) ((x) << 0x10 << 0x10)
+
+namespace {
+
+inline uint8_t decimal_helper_div10(uint64_t& phi,
+                                    uint64_t& pmid,
+                                    uint64_t& plo) {
+  uint8_t retVal;
+  pmid += FXMATH_DECIMAL_LSHIFT32BIT(phi % 0xA);
+  phi /= 0xA;
+  plo += FXMATH_DECIMAL_LSHIFT32BIT(pmid % 0xA);
+  pmid /= 0xA;
+  retVal = plo % 0xA;
+  plo /= 0xA;
+  return retVal;
+}
+
+inline uint8_t decimal_helper_div10_any(uint64_t nums[], uint8_t numcount) {
+  uint8_t retVal = 0;
+  for (int i = numcount - 1; i > 0; i--) {
+    nums[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(nums[i] % 0xA);
+    nums[i] /= 0xA;
+  }
+  if (numcount) {
+    retVal = nums[0] % 0xA;
+    nums[0] /= 0xA;
+  }
+  return retVal;
+}
+
+inline void decimal_helper_mul10(uint64_t& phi, uint64_t& pmid, uint64_t& plo) {
+  plo *= 0xA;
+  pmid = pmid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(plo);
+  plo = (uint32_t)plo;
+  phi = phi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(pmid);
+  pmid = (uint32_t)pmid;
+}
+
+inline void decimal_helper_mul10_any(uint64_t nums[], uint8_t numcount) {
+  nums[0] *= 0xA;
+  for (int i = 1; i < numcount; i++) {
+    nums[i] = nums[i] * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(nums[i - 1]);
+    nums[i - 1] = (uint32_t)nums[i - 1];
+  }
+}
+
+inline void decimal_helper_normalize(uint64_t& phi,
+                                     uint64_t& pmid,
+                                     uint64_t& plo) {
+  phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
+  pmid = (uint32_t)pmid;
+  pmid += FXMATH_DECIMAL_RSHIFT32BIT(plo);
+  plo = (uint32_t)plo;
+  phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
+  pmid = (uint32_t)pmid;
+}
+
+inline void decimal_helper_normalize_any(uint64_t nums[], uint8_t len) {
+  for (int i = len - 2; i > 0; i--) {
+    nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
+    nums[i] = (uint32_t)nums[i];
+  }
+  for (int i = 0; i < len - 1; i++) {
+    nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
+    nums[i] = (uint32_t)nums[i];
+  }
+}
+
+inline int8_t decimal_helper_raw_compare_any(uint64_t a[],
+                                             uint8_t al,
+                                             uint64_t b[],
+                                             uint8_t bl) {
+  int8_t retVal = 0;
+  for (int i = std::max(al - 1, bl - 1); i >= 0; i--) {
+    uint64_t l = (i >= al ? 0 : a[i]), r = (i >= bl ? 0 : b[i]);
+    retVal += (l > r ? 1 : (l < r ? -1 : 0));
+    if (retVal)
+      return retVal;
+  }
+  return retVal;
+}
+
+inline void decimal_helper_dec_any(uint64_t a[], uint8_t al) {
+  for (int i = 0; i < al; i++) {
+    if (a[i]--)
+      return;
+  }
+}
+
+inline void decimal_helper_inc_any(uint64_t a[], uint8_t al) {
+  for (int i = 0; i < al; i++) {
+    a[i]++;
+    if ((uint32_t)a[i] == a[i])
+      return;
+    a[i] = 0;
+  }
+}
+
+inline void decimal_helper_raw_mul(uint64_t a[],
+                                   uint8_t al,
+                                   uint64_t b[],
+                                   uint8_t bl,
+                                   uint64_t c[],
+                                   uint8_t cl) {
+  ASSERT(al + bl <= cl);
+  for (int i = 0; i < cl; i++)
+    c[i] = 0;
+
+  for (int i = 0; i < al; i++) {
+    for (int j = 0; j < bl; j++) {
+      uint64_t m = (uint64_t)a[i] * b[j];
+      c[i + j] += (uint32_t)m;
+      c[i + j + 1] += FXMATH_DECIMAL_RSHIFT32BIT(m);
+    }
+  }
+  for (int i = 0; i < cl - 1; i++) {
+    c[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(c[i]);
+    c[i] = (uint32_t)c[i];
+  }
+  for (int i = 0; i < cl; i++)
+    c[i] = (uint32_t)c[i];
+}
+
+inline void decimal_helper_raw_div(uint64_t a[],
+                                   uint8_t al,
+                                   uint64_t b[],
+                                   uint8_t bl,
+                                   uint64_t c[],
+                                   uint8_t cl) {
+  for (int i = 0; i < cl; i++)
+    c[i] = 0;
+
+  uint64_t left[16] = {0};
+  uint64_t right[16] = {0};
+  left[0] = 0;
+  for (int i = 0; i < al; i++)
+    right[i] = a[i];
+
+  uint64_t tmp[16];
+  while (decimal_helper_raw_compare_any(left, al, right, al) <= 0) {
+    uint64_t cur[16];
+    for (int i = 0; i < al; i++)
+      cur[i] = left[i] + right[i];
+
+    for (int i = al - 1; i >= 0; i--) {
+      if (i)
+        cur[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(cur[i] % 2);
+      cur[i] /= 2;
+    }
+
+    decimal_helper_raw_mul(cur, al, b, bl, tmp, 16);
+    switch (decimal_helper_raw_compare_any(tmp, 16, a, al)) {
+      case -1:
+        for (int i = 0; i < 16; i++)
+          left[i] = cur[i];
+
+        left[0]++;
+        decimal_helper_normalize_any(left, al);
+        break;
+      case 1:
+        for (int i = 0; i < 16; i++)
+          right[i] = cur[i];
+        decimal_helper_dec_any(right, al);
+        break;
+      case 0:
+        for (int i = 0; i < std::min(al, cl); i++)
+          c[i] = cur[i];
+        return;
+    }
+  }
+  for (int i = 0; i < std::min(al, cl); i++)
+    c[i] = left[i];
+}
+
+inline bool decimal_helper_outofrange(uint64_t a[], uint8_t al, uint8_t goal) {
+  for (int i = goal; i < al; i++) {
+    if (a[i])
+      return true;
+  }
+  return false;
+}
+
+inline void decimal_helper_shrinkintorange(uint64_t a[],
+                                           uint8_t al,
+                                           uint8_t goal,
+                                           uint8_t& scale) {
+  bool bRoundUp = false;
+  while (scale != 0 && (scale > FXMATH_DECIMAL_SCALELIMIT ||
+                        decimal_helper_outofrange(a, al, goal))) {
+    bRoundUp = decimal_helper_div10_any(a, al) >= 5;
+    scale--;
+  }
+  if (bRoundUp) {
+    decimal_helper_normalize_any(a, goal);
+    decimal_helper_inc_any(a, goal);
+  }
+}
+
+inline void decimal_helper_truncate(uint64_t& phi,
+                                    uint64_t& pmid,
+                                    uint64_t& plo,
+                                    uint8_t& scale,
+                                    uint8_t minscale = 0) {
+  while (scale > minscale) {
+    uint64_t thi = phi, tmid = pmid, tlo = plo;
+    if (decimal_helper_div10(thi, tmid, tlo) != 0)
+      break;
+
+    phi = thi;
+    pmid = tmid;
+    plo = tlo;
+    scale--;
+  }
+}
+
+}  // namespace
+
+CFGAS_Decimal::CFGAS_Decimal() = default;
+
+CFGAS_Decimal::CFGAS_Decimal(uint64_t val)
+    : m_uMid(static_cast<uint32_t>(FXMATH_DECIMAL_RSHIFT32BIT(val))),
+      m_uLo(static_cast<uint32_t>(val)) {}
+
+CFGAS_Decimal::CFGAS_Decimal(uint32_t val)
+    : m_uLo(static_cast<uint32_t>(val)) {}
+
+CFGAS_Decimal::CFGAS_Decimal(uint32_t lo,
+                             uint32_t mid,
+                             uint32_t hi,
+                             bool neg,
+                             uint8_t scale)
+    : m_uHi(hi),
+      m_uMid(mid),
+      m_uLo(lo),
+      m_bNeg(neg && IsNotZero()),
+      m_uScale(scale > FXMATH_DECIMAL_SCALELIMIT ? 0 : scale) {}
+
+CFGAS_Decimal::CFGAS_Decimal(int32_t val) {
+  if (val >= 0) {
+    *this = CFGAS_Decimal(static_cast<uint32_t>(val));
+  } else if (val == std::numeric_limits<int32_t>::min()) {
+    *this = CFGAS_Decimal(static_cast<uint32_t>(val));
+    SetNegate();
+  } else {
+    *this = CFGAS_Decimal(static_cast<uint32_t>(-val));
+    SetNegate();
+  }
+}
+
+CFGAS_Decimal::CFGAS_Decimal(float val, uint8_t scale) {
+  float newval = fabs(val);
+  float divisor = powf(2.0, 64.0f);
+  uint64_t bottom64 = static_cast<uint64_t>(fmodf(newval, divisor));
+  uint64_t top64 = static_cast<uint64_t>(newval / divisor);
+  uint64_t plo = bottom64 & 0xFFFFFFFF;
+  uint64_t pmid = bottom64 >> 32;
+  uint64_t phi = top64 & 0xFFFFFFFF;
+
+  newval = fmodf(newval, 1.0f);
+  for (uint8_t iter = 0; iter < scale; iter++) {
+    decimal_helper_mul10(phi, pmid, plo);
+    newval *= 10;
+    plo += static_cast<uint64_t>(newval);
+    newval = fmodf(newval, 1.0f);
+  }
+
+  plo += FXSYS_roundf(newval);
+  decimal_helper_normalize(phi, pmid, plo);
+  m_uHi = static_cast<uint32_t>(phi);
+  m_uMid = static_cast<uint32_t>(pmid);
+  m_uLo = static_cast<uint32_t>(plo);
+  m_bNeg = val < 0 && IsNotZero();
+  m_uScale = scale;
+}
+
+CFGAS_Decimal::CFGAS_Decimal(WideStringView strObj) {
+  const wchar_t* str = strObj.unterminated_c_str();
+  const wchar_t* strBound = str + strObj.GetLength();
+  bool pointmet = false;
+  bool negmet = false;
+  uint8_t scale = 0;
+  m_uHi = 0;
+  m_uMid = 0;
+  m_uLo = 0;
+  while (str != strBound && *str == ' ')
+    str++;
+  if (str != strBound && *str == '-') {
+    negmet = 1;
+    str++;
+  } else if (str != strBound && *str == '+') {
+    str++;
+  }
+
+  while (str != strBound && (FXSYS_IsDecimalDigit(*str) || *str == '.') &&
+         scale < FXMATH_DECIMAL_SCALELIMIT) {
+    if (*str == '.') {
+      if (!pointmet)
+        pointmet = 1;
+    } else {
+      m_uHi = m_uHi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uMid * 0xA);
+      m_uMid = m_uMid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uLo * 0xA);
+      m_uLo = m_uLo * 0xA + (*str - '0');
+      if (pointmet)
+        scale++;
+    }
+    str++;
+  }
+  m_bNeg = negmet && IsNotZero();
+  m_uScale = scale;
+}
+
+WideString CFGAS_Decimal::ToWideString() const {
+  WideString retString;
+  WideString tmpbuf;
+  uint64_t phi = m_uHi;
+  uint64_t pmid = m_uMid;
+  uint64_t plo = m_uLo;
+  while (phi || pmid || plo)
+    tmpbuf += decimal_helper_div10(phi, pmid, plo) + '0';
+
+  uint8_t outputlen = (uint8_t)tmpbuf.GetLength();
+  uint8_t scale = m_uScale;
+  while (scale >= outputlen) {
+    tmpbuf += '0';
+    outputlen++;
+  }
+  if (m_bNeg && IsNotZero())
+    retString += '-';
+
+  for (uint8_t idx = 0; idx < outputlen; idx++) {
+    if (idx == (outputlen - scale) && scale != 0)
+      retString += '.';
+    retString += tmpbuf[outputlen - 1 - idx];
+  }
+  return retString;
+}
+
+float CFGAS_Decimal::ToFloat() const {
+  return static_cast<float>(ToDouble());
+}
+
+double CFGAS_Decimal::ToDouble() const {
+  double pow = (double)(1 << 16) * (1 << 16);
+  double base = static_cast<double>(m_uHi) * pow * pow +
+                static_cast<double>(m_uMid) * pow + static_cast<double>(m_uLo);
+  return (m_bNeg ? -1 : 1) * base * powf(10.0f, -m_uScale);
+}
+
+void CFGAS_Decimal::SetScale(uint8_t newscale) {
+  uint8_t oldscale = m_uScale;
+  if (oldscale == newscale)
+    return;
+
+  uint64_t phi = m_uHi;
+  uint64_t pmid = m_uMid;
+  uint64_t plo = m_uLo;
+  if (newscale > oldscale) {
+    for (uint8_t iter = 0; iter < newscale - oldscale; iter++)
+      decimal_helper_mul10(phi, pmid, plo);
+
+    m_uHi = static_cast<uint32_t>(phi);
+    m_uMid = static_cast<uint32_t>(pmid);
+    m_uLo = static_cast<uint32_t>(plo);
+    m_bNeg = m_bNeg && IsNotZero();
+    m_uScale = newscale;
+  } else {
+    uint64_t point5_hi = 0;
+    uint64_t point5_mid = 0;
+    uint64_t point5_lo = 5;
+    for (uint8_t iter = 0; iter < oldscale - newscale - 1; iter++)
+      decimal_helper_mul10(point5_hi, point5_mid, point5_lo);
+
+    phi += point5_hi;
+    pmid += point5_mid;
+    plo += point5_lo;
+    decimal_helper_normalize(phi, pmid, plo);
+    for (uint8_t iter = 0; iter < oldscale - newscale; iter++)
+      decimal_helper_div10(phi, pmid, plo);
+  }
+  m_uHi = static_cast<uint32_t>(phi);
+  m_uMid = static_cast<uint32_t>(pmid);
+  m_uLo = static_cast<uint32_t>(plo);
+  m_bNeg = m_bNeg && IsNotZero();
+  m_uScale = newscale;
+}
+
+void CFGAS_Decimal::SetNegate() {
+  if (IsNotZero())
+    m_bNeg = !m_bNeg;
+}
+
+CFGAS_Decimal CFGAS_Decimal::operator*(const CFGAS_Decimal& val) const {
+  uint64_t a[3] = {m_uLo, m_uMid, m_uHi},
+           b[3] = {val.m_uLo, val.m_uMid, val.m_uHi};
+  uint64_t c[6];
+  decimal_helper_raw_mul(a, 3, b, 3, c, 6);
+  bool neg = m_bNeg ^ val.m_bNeg;
+  uint8_t scale = m_uScale + val.m_uScale;
+  decimal_helper_shrinkintorange(c, 6, 3, scale);
+  return CFGAS_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
+                       static_cast<uint32_t>(c[2]), neg, scale);
+}
+
+CFGAS_Decimal CFGAS_Decimal::operator/(const CFGAS_Decimal& val) const {
+  if (!val.IsNotZero())
+    return CFGAS_Decimal();
+
+  bool neg = m_bNeg ^ val.m_bNeg;
+  uint64_t a[7] = {m_uLo, m_uMid, m_uHi},
+           b[3] = {val.m_uLo, val.m_uMid, val.m_uHi}, c[7] = {0};
+  uint8_t scale = 0;
+  if (m_uScale < val.m_uScale) {
+    for (int i = val.m_uScale - m_uScale; i > 0; i--)
+      decimal_helper_mul10_any(a, 7);
+  } else {
+    scale = m_uScale - val.m_uScale;
+  }
+
+  uint8_t minscale = scale;
+  if (!IsNotZero())
+    return CFGAS_Decimal(0, 0, 0, 0, minscale);
+
+  while (!a[6]) {
+    decimal_helper_mul10_any(a, 7);
+    scale++;
+  }
+
+  decimal_helper_div10_any(a, 7);
+  scale--;
+  decimal_helper_raw_div(a, 6, b, 3, c, 7);
+  decimal_helper_shrinkintorange(c, 6, 3, scale);
+  decimal_helper_truncate(c[2], c[1], c[0], scale, minscale);
+  return CFGAS_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
+                       static_cast<uint32_t>(c[2]), neg, scale);
+}
diff --git a/xfa/fgas/crt/cfgas_decimal.h b/xfa/fgas/crt/cfgas_decimal.h
new file mode 100644
index 0000000..4751c5b
--- /dev/null
+++ b/xfa/fgas/crt/cfgas_decimal.h
@@ -0,0 +1,47 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_CRT_CFGAS_DECIMAL_H_
+#define XFA_FGAS_CRT_CFGAS_DECIMAL_H_
+
+#include "core/fxcrt/fx_string.h"
+
+class CFGAS_Decimal {
+ public:
+  CFGAS_Decimal();
+  explicit CFGAS_Decimal(uint32_t val);
+  explicit CFGAS_Decimal(uint64_t val);
+  explicit CFGAS_Decimal(int32_t val);
+  CFGAS_Decimal(float val, uint8_t scale);
+  explicit CFGAS_Decimal(WideStringView str);
+
+  WideString ToWideString() const;
+  float ToFloat() const;
+  double ToDouble() const;
+
+  CFGAS_Decimal operator*(const CFGAS_Decimal& val) const;
+  CFGAS_Decimal operator/(const CFGAS_Decimal& val) const;
+
+  bool IsNotZero() const { return m_uHi || m_uMid || m_uLo; }
+  uint8_t GetScale() const { return m_uScale; }
+  void SetScale(uint8_t newScale);
+  void SetNegate();
+
+ private:
+  CFGAS_Decimal(uint32_t hi,
+                uint32_t mid,
+                uint32_t lo,
+                bool neg,
+                uint8_t scale);
+
+  uint32_t m_uHi = 0;
+  uint32_t m_uMid = 0;
+  uint32_t m_uLo = 0;
+  bool m_bNeg = false;
+  uint8_t m_uScale = 0;
+};
+
+#endif  // XFA_FGAS_CRT_CFGAS_DECIMAL_H_
diff --git a/xfa/fgas/crt/cfgas_decimal_unittest.cpp b/xfa/fgas/crt/cfgas_decimal_unittest.cpp
new file mode 100644
index 0000000..816045e
--- /dev/null
+++ b/xfa/fgas/crt/cfgas_decimal_unittest.cpp
@@ -0,0 +1,77 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fgas/crt/cfgas_decimal.h"
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CFGAS_Decimal, Empty) {
+  CFGAS_Decimal empty;
+  EXPECT_EQ(L"0", empty.ToWideString());
+  EXPECT_EQ(0.0f, empty.ToFloat());
+  EXPECT_EQ(0.0, empty.ToDouble());
+}
+
+TEST(CFGAS_Decimal, FromInt32) {
+  CFGAS_Decimal big(std::numeric_limits<int32_t>::max());
+  CFGAS_Decimal small(std::numeric_limits<int32_t>::min());
+  EXPECT_STREQ(L"2147483647", big.ToWideString().c_str());
+  EXPECT_STREQ(L"-2147483648", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromUint32) {
+  CFGAS_Decimal big(std::numeric_limits<uint32_t>::max());
+  CFGAS_Decimal small(std::numeric_limits<uint32_t>::min());
+  EXPECT_STREQ(L"4294967295", big.ToWideString().c_str());
+  EXPECT_STREQ(L"0", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromUint64) {
+  CFGAS_Decimal big(std::numeric_limits<uint64_t>::max());
+  CFGAS_Decimal small(std::numeric_limits<uint64_t>::min());
+  EXPECT_STREQ(L"18446744073709551615", big.ToWideString().c_str());
+  EXPECT_STREQ(L"0", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromFloat) {
+  WideString big = CFGAS_Decimal(powf(2.0f, 95.0f), 0).ToWideString();
+  WideString big_expected = L"39614081257132168796771975168";
+
+  // Precision may not be the same on all platforms.
+  EXPECT_EQ(big_expected.GetLength(), big.GetLength());
+  EXPECT_STREQ(big_expected.First(8).c_str(), big.First(8).c_str());
+
+  WideString tiny = CFGAS_Decimal(1e20f, 0).ToWideString();
+  WideString tiny_expected = L"100000000000000000000";
+  EXPECT_EQ(tiny_expected.GetLength(), tiny.GetLength());
+  EXPECT_STREQ(tiny_expected.First(8).c_str(), tiny.First(8).c_str());
+
+  WideString teeny = CFGAS_Decimal(1e14f, 4).ToWideString();
+  WideString teeny_expected = L"100000000000000.0000";
+  EXPECT_EQ(teeny_expected.GetLength(), teeny.GetLength());
+  EXPECT_STREQ(teeny_expected.First(8).c_str(), teeny.First(8).c_str());
+}
+
+TEST(CFGAS_Decimal, FromFloatFractional) {
+  WideString case1 = CFGAS_Decimal(123.456f, 10).ToWideString();
+  WideString case1_expected = L"123.4560000000";
+
+  // Precision may not be the same on all platforms.
+  EXPECT_EQ(case1_expected.GetLength(), case1.GetLength());
+  EXPECT_STREQ(case1_expected.First(8).c_str(), case1.First(8).c_str());
+}
+
+TEST(CFGAS_Decimal, FromString) {
+  CFGAS_Decimal big(L"100000000000000000000000000");
+  CFGAS_Decimal small(L"-1000000000000000000000000");
+  EXPECT_STREQ(L"100000000000000000000000000", big.ToWideString().c_str());
+  EXPECT_STREQ(L"-1000000000000000000000000", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromString28Digits) {
+  CFGAS_Decimal frac(L"32109876543210.0123456890123");
+  EXPECT_STREQ(L"32109876543210.0123456890123", frac.ToWideString().c_str());
+}
diff --git a/xfa/fgas/crt/cfgas_formatstring.cpp b/xfa/fgas/crt/cfgas_formatstring.cpp
deleted file mode 100644
index b8f6eee..0000000
--- a/xfa/fgas/crt/cfgas_formatstring.cpp
+++ /dev/null
@@ -1,2345 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/crt/cfgas_formatstring.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "core/fxcrt/cfx_decimal.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/xml/cxml_element.h"
-
-#define FX_LOCALECATEGORY_DateHash 0xbde9abde
-#define FX_LOCALECATEGORY_TimeHash 0x2d71b00f
-#define FX_LOCALECATEGORY_DateTimeHash 0x158c72ed
-#define FX_LOCALECATEGORY_NumHash 0x0b4ff870
-#define FX_LOCALECATEGORY_TextHash 0x2d08af85
-#define FX_LOCALECATEGORY_ZeroHash 0x568cb500
-#define FX_LOCALECATEGORY_NullHash 0x052931bb
-
-#define FX_NUMSTYLE_Percent 0x01
-#define FX_NUMSTYLE_Exponent 0x02
-#define FX_NUMSTYLE_DotVorv 0x04
-
-namespace {
-
-struct FX_LOCALESUBCATEGORYINFO {
-  uint32_t uHash;
-  const wchar_t* pName;
-  int32_t eSubCategory;
-};
-
-const FX_LOCALESUBCATEGORYINFO g_FXLocaleDateTimeSubCatData[] = {
-    {0x14da2125, L"default", FX_LOCALEDATETIMESUBCATEGORY_Default},
-    {0x9041d4b0, L"short", FX_LOCALEDATETIMESUBCATEGORY_Short},
-    {0xa084a381, L"medium", FX_LOCALEDATETIMESUBCATEGORY_Medium},
-    {0xcdce56b3, L"full", FX_LOCALEDATETIMESUBCATEGORY_Full},
-    {0xf6b4afb0, L"long", FX_LOCALEDATETIMESUBCATEGORY_Long},
-};
-const int32_t g_iFXLocaleDateTimeSubCatCount =
-    sizeof(g_FXLocaleDateTimeSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
-
-const FX_LOCALESUBCATEGORYINFO g_FXLocaleNumSubCatData[] = {
-    {0x46f95531, L"percent", FX_LOCALENUMPATTERN_Percent},
-    {0x4c4e8acb, L"currency", FX_LOCALENUMPATTERN_Currency},
-    {0x54034c2f, L"decimal", FX_LOCALENUMPATTERN_Decimal},
-    {0x7568e6ae, L"integer", FX_LOCALENUMPATTERN_Integer},
-};
-const int32_t g_iFXLocaleNumSubCatCount =
-    sizeof(g_FXLocaleNumSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
-
-struct FX_LOCALETIMEZONEINFO {
-  const wchar_t* name;
-  int16_t iHour;
-  int16_t iMinute;
-};
-
-const FX_LOCALETIMEZONEINFO g_FXLocaleTimeZoneData[] = {
-    {L"CDT", -5, 0}, {L"CST", -6, 0}, {L"EDT", -4, 0}, {L"EST", -5, 0},
-    {L"MDT", -6, 0}, {L"MST", -7, 0}, {L"PDT", -7, 0}, {L"PST", -8, 0},
-};
-
-const wchar_t gs_wsTimeSymbols[] = L"hHkKMSFAzZ";
-const wchar_t gs_wsDateSymbols[] = L"DJMEeGgYwW";
-const wchar_t gs_wsConstChars[] = L",-:/. ";
-
-int32_t ParseTimeZone(const wchar_t* pStr, int32_t iLen, FX_TIMEZONE* tz) {
-  tz->tzHour = 0;
-  tz->tzMinute = 0;
-  if (iLen < 0)
-    return 0;
-
-  int32_t iStart = 1;
-  int32_t iEnd = iStart + 2;
-  while (iStart < iLen && iStart < iEnd)
-    tz->tzHour = tz->tzHour * 10 + FXSYS_DecimalCharToInt(pStr[iStart++]);
-
-  if (iStart < iLen && pStr[iStart] == ':')
-    iStart++;
-
-  iEnd = iStart + 2;
-  while (iStart < iLen && iStart < iEnd)
-    tz->tzMinute = tz->tzMinute * 10 + FXSYS_DecimalCharToInt(pStr[iStart++]);
-
-  if (pStr[0] == '-')
-    tz->tzHour = -tz->tzHour;
-
-  return iStart;
-}
-
-int32_t ConvertHex(int32_t iKeyValue, wchar_t ch) {
-  if (FXSYS_isHexDigit(ch))
-    return iKeyValue * 16 + FXSYS_HexCharToInt(ch);
-  return iKeyValue;
-}
-
-WideString GetLiteralText(const wchar_t* pStrPattern,
-                          int32_t* iPattern,
-                          int32_t iLenPattern) {
-  WideString wsOutput;
-  if (pStrPattern[*iPattern] != '\'')
-    return wsOutput;
-
-  (*iPattern)++;
-  int32_t iQuote = 1;
-  while (*iPattern < iLenPattern) {
-    if (pStrPattern[*iPattern] == '\'') {
-      iQuote++;
-      if ((*iPattern + 1 >= iLenPattern) ||
-          ((pStrPattern[*iPattern + 1] != '\'') && (iQuote % 2 == 0))) {
-        break;
-      }
-      iQuote++;
-      (*iPattern)++;
-    } else if (pStrPattern[*iPattern] == '\\' &&
-               (*iPattern + 1 < iLenPattern) &&
-               pStrPattern[*iPattern + 1] == 'u') {
-      int32_t iKeyValue = 0;
-      *iPattern += 2;
-      int32_t i = 0;
-      while (*iPattern < iLenPattern && i++ < 4) {
-        wchar_t ch = pStrPattern[(*iPattern)++];
-        iKeyValue = ConvertHex(iKeyValue, ch);
-      }
-      if (iKeyValue != 0)
-        wsOutput += static_cast<wchar_t>(iKeyValue & 0x0000FFFF);
-
-      continue;
-    }
-    wsOutput += pStrPattern[(*iPattern)++];
-  }
-  return wsOutput;
-}
-
-WideString GetLiteralTextReverse(const wchar_t* pStrPattern,
-                                 int32_t* iPattern) {
-  WideString wsOutput;
-  if (pStrPattern[*iPattern] != '\'')
-    return wsOutput;
-
-  (*iPattern)--;
-  int32_t iQuote = 1;
-  while (*iPattern >= 0) {
-    if (pStrPattern[*iPattern] == '\'') {
-      iQuote++;
-      if (*iPattern - 1 >= 0 ||
-          ((pStrPattern[*iPattern - 1] != '\'') && (iQuote % 2 == 0))) {
-        break;
-      }
-      iQuote++;
-      (*iPattern)--;
-    } else if (pStrPattern[*iPattern] == '\\' &&
-               pStrPattern[*iPattern + 1] == 'u') {
-      (*iPattern)--;
-      int32_t iKeyValue = 0;
-      int32_t iLen = wsOutput.GetLength();
-      int32_t i = 1;
-      for (; i < iLen && i < 5; i++) {
-        wchar_t ch = wsOutput[i];
-        iKeyValue = ConvertHex(iKeyValue, ch);
-      }
-      if (iKeyValue != 0) {
-        wsOutput.Delete(0, i);
-        wsOutput = (wchar_t)(iKeyValue & 0x0000FFFF) + wsOutput;
-      }
-      continue;
-    }
-    wsOutput = pStrPattern[(*iPattern)--] + wsOutput;
-  }
-  return wsOutput;
-}
-
-bool GetNumericDotIndex(const WideString& wsNum,
-                        const WideString& wsDotSymbol,
-                        int32_t* iDotIndex) {
-  int32_t ccf = 0;
-  int32_t iLenf = wsNum.GetLength();
-  const wchar_t* pStr = wsNum.c_str();
-  int32_t iLenDot = wsDotSymbol.GetLength();
-  while (ccf < iLenf) {
-    if (pStr[ccf] == '\'') {
-      GetLiteralText(pStr, &ccf, iLenf);
-    } else if (ccf + iLenDot <= iLenf &&
-               !wcsncmp(pStr + ccf, wsDotSymbol.c_str(), iLenDot)) {
-      *iDotIndex = ccf;
-      return true;
-    }
-    ccf++;
-  }
-  auto result = wsNum.Find('.');
-  *iDotIndex = result.value_or(iLenf);
-  return result.has_value();
-}
-
-bool ExtractCountDigits(const wchar_t* str,
-                        int len,
-                        int count,
-                        int* cc,
-                        uint32_t* value) {
-  for (int i = count; i > 0; --i) {
-    if (*cc >= len)
-      return false;
-    if (!FXSYS_isDecimalDigit(str[*cc]))
-      return false;
-    *value = *value * 10 + FXSYS_DecimalCharToInt(str[(*cc)++]);
-  }
-  return true;
-}
-
-bool ExtractCountDigitsWithOptional(const wchar_t* str,
-                                    int len,
-                                    int count,
-                                    int* cc,
-                                    uint32_t* value) {
-  if (!ExtractCountDigits(str, len, count, cc, value))
-    return false;
-  ExtractCountDigits(str, len, 1, cc, value);
-  return true;
-}
-
-bool ParseLocaleDate(const WideString& wsDate,
-                     const WideString& wsDatePattern,
-                     IFX_Locale* pLocale,
-                     CFX_DateTime* datetime,
-                     int32_t* cc) {
-  uint32_t year = 1900;
-  uint32_t month = 1;
-  uint32_t day = 1;
-  int32_t ccf = 0;
-  const wchar_t* str = wsDate.c_str();
-  int32_t len = wsDate.GetLength();
-  const wchar_t* strf = wsDatePattern.c_str();
-  int32_t lenf = wsDatePattern.GetLength();
-  WideStringView wsDateSymbols(gs_wsDateSymbols);
-  while (*cc < len && ccf < lenf) {
-    if (strf[ccf] == '\'') {
-      WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
-      int32_t iLiteralLen = wsLiteral.GetLength();
-      if (*cc + iLiteralLen > len ||
-          wcsncmp(str + *cc, wsLiteral.c_str(), iLiteralLen)) {
-        return false;
-      }
-      *cc += iLiteralLen;
-      ccf++;
-      continue;
-    }
-    if (!wsDateSymbols.Contains(strf[ccf])) {
-      if (strf[ccf] != str[*cc])
-        return false;
-      (*cc)++;
-      ccf++;
-      continue;
-    }
-
-    WideString symbol;
-    symbol.Reserve(4);
-    symbol += strf[ccf++];
-    while (ccf < lenf && strf[ccf] == symbol[0])
-      symbol += strf[ccf++];
-
-    if (symbol == L"D" || symbol == L"DD") {
-      day = 0;
-      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &day))
-        return false;
-    } else if (symbol == L"J") {
-      uint32_t val = 0;
-      ExtractCountDigits(str, len, 3, cc, &val);
-    } else if (symbol == L"M" || symbol == L"MM") {
-      month = 0;
-      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &month))
-        return false;
-    } else if (symbol == L"MMM" || symbol == L"MMMM") {
-      for (uint16_t i = 0; i < 12; i++) {
-        WideString wsMonthName = pLocale->GetMonthName(i, symbol == L"MMM");
-        if (wsMonthName.IsEmpty())
-          continue;
-        if (!wcsncmp(wsMonthName.c_str(), str + *cc, wsMonthName.GetLength())) {
-          *cc += wsMonthName.GetLength();
-          month = i + 1;
-          break;
-        }
-      }
-    } else if (symbol == L"EEE" || symbol == L"EEEE") {
-      for (uint16_t i = 0; i < 7; i++) {
-        WideString wsDayName = pLocale->GetDayName(i, symbol == L"EEE");
-        if (wsDayName.IsEmpty())
-          continue;
-        if (!wcsncmp(wsDayName.c_str(), str + *cc, wsDayName.GetLength())) {
-          *cc += wsDayName.GetLength();
-          break;
-        }
-      }
-    } else if (symbol == L"YY" || symbol == L"YYYY") {
-      if (*cc + pdfium::base::checked_cast<int32_t>(symbol.GetLength()) > len)
-        return false;
-
-      year = 0;
-      if (!ExtractCountDigits(str, len, symbol.GetLength(), cc, &year))
-        return false;
-      if (symbol == L"YY") {
-        if (year <= 29)
-          year += 2000;
-        else
-          year += 1900;
-      }
-    } else if (symbol == L"G") {
-      *cc += 2;
-    } else if (symbol == L"JJJ" || symbol == L"E" || symbol == L"e" ||
-               symbol == L"w" || symbol == L"WW") {
-      *cc += symbol.GetLength();
-    }
-  }
-  if (*cc < len)
-    return false;
-
-  datetime->SetDate(year, month, day);
-  return !!(*cc);
-}
-
-void ResolveZone(FX_TIMEZONE tzDiff,
-                 IFX_Locale* pLocale,
-                 uint32_t* wHour,
-                 uint32_t* wMinute) {
-  int32_t iMinuteDiff = *wHour * 60 + *wMinute;
-  FX_TIMEZONE tzLocale = pLocale->GetTimeZone();
-  iMinuteDiff += tzLocale.tzHour * 60 +
-                 (tzLocale.tzHour < 0 ? -tzLocale.tzMinute : tzLocale.tzMinute);
-  iMinuteDiff -= tzDiff.tzHour * 60 +
-                 (tzDiff.tzHour < 0 ? -tzDiff.tzMinute : tzDiff.tzMinute);
-
-  iMinuteDiff %= 1440;
-  if (iMinuteDiff < 0)
-    iMinuteDiff += 1440;
-
-  *wHour = iMinuteDiff / 60;
-  *wMinute = iMinuteDiff % 60;
-}
-
-bool ParseLocaleTime(const WideString& wsTime,
-                     const WideString& wsTimePattern,
-                     IFX_Locale* pLocale,
-                     CFX_DateTime* datetime,
-                     int32_t* cc) {
-  uint32_t hour = 0;
-  uint32_t minute = 0;
-  uint32_t second = 0;
-  uint32_t millisecond = 0;
-  int32_t ccf = 0;
-  const wchar_t* str = wsTime.c_str();
-  int len = wsTime.GetLength();
-  const wchar_t* strf = wsTimePattern.c_str();
-  int lenf = wsTimePattern.GetLength();
-  bool bHasA = false;
-  bool bPM = false;
-  WideStringView wsTimeSymbols(gs_wsTimeSymbols);
-  while (*cc < len && ccf < lenf) {
-    if (strf[ccf] == '\'') {
-      WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
-      int32_t iLiteralLen = wsLiteral.GetLength();
-      if (*cc + iLiteralLen > len ||
-          wcsncmp(str + *cc, wsLiteral.c_str(), iLiteralLen)) {
-        return false;
-      }
-      *cc += iLiteralLen;
-      ccf++;
-      continue;
-    }
-    if (!wsTimeSymbols.Contains(strf[ccf])) {
-      if (strf[ccf] != str[*cc])
-        return false;
-      (*cc)++;
-      ccf++;
-      continue;
-    }
-
-    WideString symbol;
-    symbol.Reserve(4);
-    symbol += strf[ccf++];
-    while (ccf < lenf && strf[ccf] == symbol[0])
-      symbol += strf[ccf++];
-
-    if (symbol == L"k" || symbol == L"K" || symbol == L"h" || symbol == L"H") {
-      hour = 0;
-      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &hour))
-        return false;
-      if (symbol == L"K" && hour == 24)
-        hour = 0;
-    } else if (symbol == L"kk" || symbol == L"KK" || symbol == L"hh" ||
-               symbol == L"HH") {
-      hour = 0;
-      if (!ExtractCountDigits(str, len, 2, cc, &hour))
-        return false;
-      if (symbol == L"KK" && hour == 24)
-        hour = 0;
-    } else if (symbol == L"M") {
-      minute = 0;
-      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &minute))
-        return false;
-    } else if (symbol == L"MM") {
-      minute = 0;
-      if (!ExtractCountDigits(str, len, 2, cc, &minute))
-        return false;
-    } else if (symbol == L"S") {
-      second = 0;
-      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &second))
-        return false;
-    } else if (symbol == L"SS") {
-      second = 0;
-      if (!ExtractCountDigits(str, len, 2, cc, &second))
-        return false;
-    } else if (symbol == L"FFF") {
-      millisecond = 0;
-      if (!ExtractCountDigits(str, len, 3, cc, &millisecond))
-        return false;
-    } else if (symbol == L"A") {
-      WideString wsAM = pLocale->GetMeridiemName(true);
-      WideString wsPM = pLocale->GetMeridiemName(false);
-      if ((*cc + pdfium::base::checked_cast<int32_t>(wsAM.GetLength()) <=
-           len) &&
-          (WideStringView(str + *cc, wsAM.GetLength()) == wsAM)) {
-        *cc += wsAM.GetLength();
-        bHasA = true;
-      } else if ((*cc + pdfium::base::checked_cast<int32_t>(wsPM.GetLength()) <=
-                  len) &&
-                 (WideStringView(str + *cc, wsPM.GetLength()) == wsPM)) {
-        *cc += wsPM.GetLength();
-        bHasA = true;
-        bPM = true;
-      }
-    } else if (symbol == L"Z") {
-      if (*cc + 3 > len)
-        continue;
-
-      WideString tz(str[(*cc)++]);
-      tz += str[(*cc)++];
-      tz += str[(*cc)++];
-      if (tz == L"GMT") {
-        FX_TIMEZONE tzDiff;
-        tzDiff.tzHour = 0;
-        tzDiff.tzMinute = 0;
-        if (*cc < len && (str[*cc] == '-' || str[*cc] == '+'))
-          *cc += ParseTimeZone(str + *cc, len - *cc, &tzDiff);
-
-        ResolveZone(tzDiff, pLocale, &hour, &minute);
-      } else {
-        // Search the timezone list. There are only 8 of them, so linear scan.
-        for (size_t i = 0; i < FX_ArraySize(g_FXLocaleTimeZoneData); ++i) {
-          const FX_LOCALETIMEZONEINFO& info = g_FXLocaleTimeZoneData[i];
-          if (tz != info.name)
-            continue;
-
-          hour += info.iHour;
-          minute += info.iHour > 0 ? info.iMinute : -info.iMinute;
-          break;
-        }
-      }
-    } else if (symbol == L"z") {
-      if (str[*cc] != 'Z') {
-        FX_TIMEZONE tzDiff;
-        *cc += ParseTimeZone(str + *cc, len - *cc, &tzDiff);
-        ResolveZone(tzDiff, pLocale, &hour, &minute);
-      } else {
-        (*cc)++;
-      }
-    }
-  }
-  if (bHasA) {
-    if (bPM) {
-      hour += 12;
-      if (hour == 24)
-        hour = 12;
-    } else {
-      if (hour == 12)
-        hour = 0;
-    }
-  }
-  datetime->SetTime(hour, minute, second, millisecond);
-  return !!(*cc);
-}
-
-int32_t GetNumTrailingLimit(const WideString& wsFormat,
-                            int iDotPos,
-                            bool* bTrimTailZeros) {
-  if (iDotPos < 0)
-    return 0;
-
-  int32_t iCount = wsFormat.GetLength();
-  int32_t iTreading = 0;
-  for (iDotPos++; iDotPos < iCount; iDotPos++) {
-    wchar_t wc = wsFormat[iDotPos];
-    if (wc == L'z' || wc == L'9' || wc == 'Z') {
-      iTreading++;
-      *bTrimTailZeros = wc != L'9';
-    }
-  }
-  return iTreading;
-}
-
-bool IsLeapYear(uint32_t year) {
-  return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
-}
-
-bool MonthHas30Days(uint32_t month) {
-  return month == 4 || month == 6 || month == 9 || month == 11;
-}
-
-bool MonthHas31Days(uint32_t month) {
-  return month != 2 && !MonthHas30Days(month);
-}
-
-// |month| is 1-based. e.g. 1 means January.
-uint16_t GetSolarMonthDays(uint16_t year, uint16_t month) {
-  if (month == 2)
-    return FX_IsLeapYear(year) ? 29 : 28;
-
-  return MonthHas30Days(month) ? 30 : 31;
-}
-
-uint16_t GetWeekDay(uint16_t year, uint16_t month, uint16_t day) {
-  static const uint16_t month_day[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
-  uint16_t nDays =
-      (year - 1) % 7 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
-  nDays += month_day[month - 1] + day;
-  if (FX_IsLeapYear(year) && month > 2)
-    nDays++;
-  return nDays % 7;
-}
-
-uint16_t GetWeekOfMonth(uint16_t year, uint16_t month, uint16_t day) {
-  uint16_t week_day = GetWeekDay(year, month, 1);
-  uint16_t week_index = 0;
-  week_index += day / 7;
-  day = day % 7;
-  if (week_day + day > 7)
-    week_index++;
-  return week_index;
-}
-
-uint16_t GetWeekOfYear(uint16_t year, uint16_t month, uint16_t day) {
-  uint16_t nDays = 0;
-  for (uint16_t i = 1; i < month; i++)
-    nDays += GetSolarMonthDays(year, i);
-
-  nDays += day;
-  uint16_t week_day = GetWeekDay(year, 1, 1);
-  uint16_t week_index = 1;
-  week_index += nDays / 7;
-  nDays = nDays % 7;
-  if (week_day + nDays > 7)
-    week_index++;
-  return week_index;
-}
-
-WideString NumToString(size_t fmt_size, int32_t value) {
-  return WideString::Format(
-      fmt_size == 1 ? L"%d" : fmt_size == 2 ? L"%02d" : L"%03d", value);
-}
-
-WideString DateFormat(const WideString& wsDatePattern,
-                      IFX_Locale* pLocale,
-                      const CFX_DateTime& datetime) {
-  WideString wsResult;
-  int32_t year = datetime.GetYear();
-  uint8_t month = datetime.GetMonth();
-  uint8_t day = datetime.GetDay();
-  int32_t ccf = 0;
-  const wchar_t* strf = wsDatePattern.c_str();
-  int32_t lenf = wsDatePattern.GetLength();
-  WideStringView wsDateSymbols(gs_wsDateSymbols);
-  while (ccf < lenf) {
-    if (strf[ccf] == '\'') {
-      wsResult += GetLiteralText(strf, &ccf, lenf);
-      ccf++;
-      continue;
-    }
-    if (!wsDateSymbols.Contains(strf[ccf])) {
-      wsResult += strf[ccf++];
-      continue;
-    }
-
-    WideString symbol;
-    symbol.Reserve(4);
-    symbol += strf[ccf++];
-    while (ccf < lenf && strf[ccf] == symbol[0])
-      symbol += strf[ccf++];
-
-    if (symbol == L"D" || symbol == L"DD") {
-      wsResult += NumToString(symbol.GetLength(), day);
-    } else if (symbol == L"J" || symbol == L"JJJ") {
-      uint16_t nDays = 0;
-      for (int i = 1; i < month; i++)
-        nDays += GetSolarMonthDays(year, i);
-      nDays += day;
-      wsResult += NumToString(symbol.GetLength(), nDays);
-    } else if (symbol == L"M" || symbol == L"MM") {
-      wsResult += NumToString(symbol.GetLength(), month);
-    } else if (symbol == L"MMM" || symbol == L"MMMM") {
-      wsResult += pLocale->GetMonthName(month - 1, symbol == L"MMM");
-    } else if (symbol == L"E" || symbol == L"e") {
-      uint16_t wWeekDay = GetWeekDay(year, month, day);
-      wsResult += NumToString(
-          1, symbol == L"E" ? wWeekDay + 1 : (wWeekDay ? wWeekDay : 7));
-    } else if (symbol == L"EEE" || symbol == L"EEEE") {
-      wsResult +=
-          pLocale->GetDayName(GetWeekDay(year, month, day), symbol == L"EEE");
-    } else if (symbol == L"G") {
-      wsResult += pLocale->GetEraName(year > 0);
-    } else if (symbol == L"YY") {
-      wsResult += NumToString(2, year % 100);
-    } else if (symbol == L"YYYY") {
-      wsResult += NumToString(1, year);
-    } else if (symbol == L"w") {
-      wsResult += NumToString(1, GetWeekOfMonth(year, month, day));
-    } else if (symbol == L"WW") {
-      wsResult += NumToString(2, GetWeekOfYear(year, month, day));
-    }
-  }
-  return wsResult;
-}
-
-WideString TimeFormat(const WideString& wsTimePattern,
-                      IFX_Locale* pLocale,
-                      const CFX_DateTime& datetime) {
-  WideString wsResult;
-  uint8_t hour = datetime.GetHour();
-  uint8_t minute = datetime.GetMinute();
-  uint8_t second = datetime.GetSecond();
-  uint16_t millisecond = datetime.GetMillisecond();
-  int32_t ccf = 0;
-  const wchar_t* strf = wsTimePattern.c_str();
-  int32_t lenf = wsTimePattern.GetLength();
-  uint16_t wHour = hour;
-  bool bPM = false;
-  if (wsTimePattern.Contains('A')) {
-    if (wHour >= 12)
-      bPM = true;
-  }
-
-  WideStringView wsTimeSymbols(gs_wsTimeSymbols);
-  while (ccf < lenf) {
-    if (strf[ccf] == '\'') {
-      wsResult += GetLiteralText(strf, &ccf, lenf);
-      ccf++;
-      continue;
-    }
-    if (!wsTimeSymbols.Contains(strf[ccf])) {
-      wsResult += strf[ccf++];
-      continue;
-    }
-
-    WideString symbol;
-    symbol.Reserve(4);
-    symbol += strf[ccf++];
-    while (ccf < lenf && strf[ccf] == symbol[0])
-      symbol += strf[ccf++];
-
-    if (symbol == L"h" || symbol == L"hh") {
-      if (wHour > 12)
-        wHour -= 12;
-      wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 12 : wHour);
-    } else if (symbol == L"K" || symbol == L"KK") {
-      wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 24 : wHour);
-    } else if (symbol == L"k" || symbol == L"kk") {
-      if (wHour > 12)
-        wHour -= 12;
-      wsResult += NumToString(symbol.GetLength(), wHour);
-    } else if (symbol == L"H" || symbol == L"HH") {
-      wsResult += NumToString(symbol.GetLength(), wHour);
-    } else if (symbol == L"M" || symbol == L"MM") {
-      wsResult += NumToString(symbol.GetLength(), minute);
-    } else if (symbol == L"S" || symbol == L"SS") {
-      wsResult += NumToString(symbol.GetLength(), second);
-    } else if (symbol == L"FFF") {
-      wsResult += NumToString(3, millisecond);
-    } else if (symbol == L"A") {
-      wsResult += pLocale->GetMeridiemName(!bPM);
-    } else if (symbol == L"Z" || symbol == L"z") {
-      if (symbol == L"Z")
-        wsResult += L"GMT";
-
-      FX_TIMEZONE tz = pLocale->GetTimeZone();
-      if (tz.tzHour != 0 || tz.tzMinute != 0) {
-        wsResult += tz.tzHour < 0 ? L"-" : L"+";
-        wsResult +=
-            WideString::Format(L"%02d:%02d", abs(tz.tzHour), tz.tzMinute);
-      }
-    }
-  }
-  return wsResult;
-}
-
-WideString FormatDateTimeInternal(const CFX_DateTime& dt,
-                                  const WideString& wsDatePattern,
-                                  const WideString& wsTimePattern,
-                                  bool bDateFirst,
-                                  IFX_Locale* pLocale) {
-  WideString wsDateOut;
-  if (!wsDatePattern.IsEmpty())
-    wsDateOut = DateFormat(wsDatePattern, pLocale, dt);
-
-  WideString wsTimeOut;
-  if (!wsTimePattern.IsEmpty())
-    wsTimeOut = TimeFormat(wsTimePattern, pLocale, dt);
-
-  return bDateFirst ? wsDateOut + wsTimeOut : wsTimeOut + wsDateOut;
-}
-
-}  // namespace
-
-bool FX_DateFromCanonical(const WideString& wsDate, CFX_DateTime* datetime) {
-  const wchar_t* str = wsDate.c_str();
-  int len = wsDate.GetLength();
-  if (len > 10)
-    return false;
-
-  int cc = 0;
-  uint32_t year = 0;
-  if (!ExtractCountDigits(str, len, 4, &cc, &year))
-    return false;
-  if (year < 1900)
-    return false;
-  if (cc >= len) {
-    datetime->SetDate(year, 1, 1);
-    return true;
-  }
-
-  if (str[cc] == '-')
-    cc++;
-
-  uint32_t month = 0;
-  if (!ExtractCountDigits(str, len, 2, &cc, &month))
-    return false;
-  if (month > 12 || month < 1)
-    return false;
-  if (cc >= len) {
-    datetime->SetDate(year, month, 1);
-    return true;
-  }
-
-  if (str[cc] == '-')
-    cc++;
-
-  uint32_t day = 0;
-  if (!ExtractCountDigits(str, len, 2, &cc, &day))
-    return false;
-  if (day < 1)
-    return false;
-  if ((MonthHas31Days(month) && day > 31) ||
-      (MonthHas30Days(month) && day > 30)) {
-    return false;
-  }
-  if (month == 2 && day > (IsLeapYear(year) ? 29U : 28U))
-    return false;
-
-  datetime->SetDate(year, month, day);
-  return true;
-}
-
-bool FX_TimeFromCanonical(const WideStringView& wsTime,
-                          CFX_DateTime* datetime,
-                          IFX_Locale* pLocale) {
-  if (wsTime.GetLength() == 0)
-    return false;
-
-  const wchar_t* str = wsTime.unterminated_c_str();
-  int len = wsTime.GetLength();
-
-  int cc = 0;
-  uint32_t hour = 0;
-  if (!ExtractCountDigits(str, len, 2, &cc, &hour))
-    return false;
-  if (hour >= 24)
-    return false;
-  if (cc >= len) {
-    datetime->SetTime(hour, 0, 0, 0);
-    return true;
-  }
-
-  if (str[cc] == ':')
-    cc++;
-
-  uint32_t minute = 0;
-  if (!ExtractCountDigits(str, len, 2, &cc, &minute))
-    return false;
-  if (minute >= 60)
-    return false;
-
-  if (cc >= len) {
-    datetime->SetTime(hour, minute, 0, 0);
-    return true;
-  }
-
-  if (str[cc] == ':')
-    cc++;
-
-  uint32_t second = 0;
-  uint32_t millisecond = 0;
-  if (str[cc] != 'Z') {
-    if (!ExtractCountDigits(str, len, 2, &cc, &second))
-      return false;
-    if (second >= 60)
-      return false;
-    if (cc < len && str[cc] == '.') {
-      cc++;
-      if (!ExtractCountDigits(str, len, 3, &cc, &millisecond))
-        return false;
-    }
-  }
-
-  // Skip until we find a + or - for the time zone.
-  while (cc < len) {
-    if (str[cc] == '+' || str[cc] == '-')
-      break;
-    ++cc;
-  }
-
-  if (cc < len) {
-    FX_TIMEZONE tzDiff;
-    tzDiff.tzHour = 0;
-    tzDiff.tzMinute = 0;
-    if (str[cc] != 'Z')
-      cc += ParseTimeZone(str + cc, len - cc, &tzDiff);
-
-    ResolveZone(tzDiff, pLocale, &hour, &minute);
-  }
-
-  datetime->SetTime(hour, minute, second, millisecond);
-  return true;
-}
-
-CFGAS_FormatString::CFGAS_FormatString(CXFA_LocaleMgr* pLocaleMgr)
-    : m_pLocaleMgr(pLocaleMgr) {}
-
-CFGAS_FormatString::~CFGAS_FormatString() {}
-
-void CFGAS_FormatString::SplitFormatString(
-    const WideString& wsFormatString,
-    std::vector<WideString>* wsPatterns) {
-  int32_t iStrLen = wsFormatString.GetLength();
-  const wchar_t* pStr = wsFormatString.c_str();
-  const wchar_t* pToken = pStr;
-  const wchar_t* pEnd = pStr + iStrLen;
-  bool iQuote = false;
-  while (true) {
-    if (pStr >= pEnd) {
-      wsPatterns->push_back(WideString(pToken, pStr - pToken));
-      return;
-    }
-    if (*pStr == '\'') {
-      iQuote = !iQuote;
-    } else if (*pStr == L'|' && !iQuote) {
-      wsPatterns->push_back(WideString(pToken, pStr - pToken));
-      pToken = pStr + 1;
-    }
-    pStr++;
-  }
-}
-
-FX_LOCALECATEGORY CFGAS_FormatString::GetCategory(const WideString& wsPattern) {
-  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
-  int32_t ccf = 0;
-  int32_t iLenf = wsPattern.GetLength();
-  const wchar_t* pStr = wsPattern.c_str();
-  bool bBraceOpen = false;
-  WideStringView wsConstChars(gs_wsConstChars);
-  while (ccf < iLenf) {
-    if (pStr[ccf] == '\'') {
-      GetLiteralText(pStr, &ccf, iLenf);
-    } else if (!bBraceOpen && !wsConstChars.Contains(pStr[ccf])) {
-      WideString wsCategory(pStr[ccf]);
-      ccf++;
-      while (true) {
-        if (ccf == iLenf)
-          return eCategory;
-        if (pStr[ccf] == '.' || pStr[ccf] == '(')
-          break;
-        if (pStr[ccf] == '{') {
-          bBraceOpen = true;
-          break;
-        }
-        wsCategory += pStr[ccf];
-        ccf++;
-      }
-
-      uint32_t dwHash = FX_HashCode_GetW(wsCategory.AsStringView(), false);
-      if (dwHash == FX_LOCALECATEGORY_DateTimeHash)
-        return FX_LOCALECATEGORY_DateTime;
-      if (dwHash == FX_LOCALECATEGORY_TextHash)
-        return FX_LOCALECATEGORY_Text;
-      if (dwHash == FX_LOCALECATEGORY_NumHash)
-        return FX_LOCALECATEGORY_Num;
-      if (dwHash == FX_LOCALECATEGORY_ZeroHash)
-        return FX_LOCALECATEGORY_Zero;
-      if (dwHash == FX_LOCALECATEGORY_NullHash)
-        return FX_LOCALECATEGORY_Null;
-      if (dwHash == FX_LOCALECATEGORY_DateHash) {
-        if (eCategory == FX_LOCALECATEGORY_Time)
-          return FX_LOCALECATEGORY_DateTime;
-        eCategory = FX_LOCALECATEGORY_Date;
-      } else if (dwHash == FX_LOCALECATEGORY_TimeHash) {
-        if (eCategory == FX_LOCALECATEGORY_Date)
-          return FX_LOCALECATEGORY_DateTime;
-        eCategory = FX_LOCALECATEGORY_Time;
-      }
-    } else if (pStr[ccf] == '}') {
-      bBraceOpen = false;
-    }
-    ccf++;
-  }
-  return eCategory;
-}
-
-WideString CFGAS_FormatString::GetTextFormat(const WideString& wsPattern,
-                                             const WideStringView& wsCategory) {
-  int32_t ccf = 0;
-  int32_t iLenf = wsPattern.GetLength();
-  const wchar_t* pStr = wsPattern.c_str();
-  bool bBrackOpen = false;
-  WideStringView wsConstChars(gs_wsConstChars);
-  WideString wsPurgePattern;
-  while (ccf < iLenf) {
-    if (pStr[ccf] == '\'') {
-      int32_t iCurChar = ccf;
-      GetLiteralText(pStr, &ccf, iLenf);
-      wsPurgePattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
-    } else if (!bBrackOpen && !wsConstChars.Contains(pStr[ccf])) {
-      WideString wsSearchCategory(pStr[ccf]);
-      ccf++;
-      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
-             pStr[ccf] != '(') {
-        wsSearchCategory += pStr[ccf];
-        ccf++;
-      }
-      if (wsSearchCategory != wsCategory)
-        continue;
-
-      while (ccf < iLenf) {
-        if (pStr[ccf] == '(') {
-          ccf++;
-          // Skip over the encoding name.
-          while (ccf < iLenf && pStr[ccf] != ')')
-            ccf++;
-        } else if (pStr[ccf] == '{') {
-          bBrackOpen = true;
-          break;
-        }
-        ccf++;
-      }
-    } else if (pStr[ccf] != '}') {
-      wsPurgePattern += pStr[ccf];
-    }
-    ccf++;
-  }
-  if (!bBrackOpen)
-    wsPurgePattern = wsPattern;
-
-  return wsPurgePattern;
-}
-
-IFX_Locale* CFGAS_FormatString::GetNumericFormat(const WideString& wsPattern,
-                                                 int32_t* iDotIndex,
-                                                 uint32_t* dwStyle,
-                                                 WideString* wsPurgePattern) {
-  *dwStyle = 0;
-  IFX_Locale* pLocale = nullptr;
-  int32_t ccf = 0;
-  int32_t iLenf = wsPattern.GetLength();
-  const wchar_t* pStr = wsPattern.c_str();
-  bool bFindDot = false;
-  bool bBrackOpen = false;
-  WideStringView wsConstChars(gs_wsConstChars);
-  while (ccf < iLenf) {
-    if (pStr[ccf] == '\'') {
-      int32_t iCurChar = ccf;
-      GetLiteralText(pStr, &ccf, iLenf);
-      *wsPurgePattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
-    } else if (!bBrackOpen && !wsConstChars.Contains(pStr[ccf])) {
-      WideString wsCategory(pStr[ccf]);
-      ccf++;
-      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
-             pStr[ccf] != '(') {
-        wsCategory += pStr[ccf];
-        ccf++;
-      }
-      if (wsCategory != L"num") {
-        bBrackOpen = true;
-        ccf = 0;
-        continue;
-      }
-      while (ccf < iLenf) {
-        if (pStr[ccf] == '{') {
-          bBrackOpen = true;
-          break;
-        }
-        if (pStr[ccf] == '(') {
-          ccf++;
-          WideString wsLCID;
-          while (ccf < iLenf && pStr[ccf] != ')')
-            wsLCID += pStr[ccf++];
-
-          pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
-        } else if (pStr[ccf] == '.') {
-          WideString wsSubCategory;
-          ccf++;
-          while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{')
-            wsSubCategory += pStr[ccf++];
-
-          uint32_t dwSubHash =
-              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
-          FX_LOCALENUMSUBCATEGORY eSubCategory = FX_LOCALENUMPATTERN_Decimal;
-          for (int32_t i = 0; i < g_iFXLocaleNumSubCatCount; i++) {
-            if (g_FXLocaleNumSubCatData[i].uHash == dwSubHash) {
-              eSubCategory = (FX_LOCALENUMSUBCATEGORY)g_FXLocaleNumSubCatData[i]
-                                 .eSubCategory;
-              break;
-            }
-          }
-          if (!pLocale)
-            pLocale = m_pLocaleMgr->GetDefLocale();
-
-          ASSERT(pLocale);
-
-          wsSubCategory = pLocale->GetNumPattern(eSubCategory);
-          auto result = wsSubCategory.Find('.');
-          if (result.has_value() && result.value() != 0) {
-            *iDotIndex += wsPurgePattern->GetLength();
-            bFindDot = true;
-            *dwStyle |= FX_NUMSTYLE_DotVorv;
-          }
-          *wsPurgePattern += wsSubCategory;
-          if (eSubCategory == FX_LOCALENUMPATTERN_Percent)
-            *dwStyle |= FX_NUMSTYLE_Percent;
-
-          continue;
-        }
-        ccf++;
-      }
-    } else if (pStr[ccf] == 'E') {
-      *dwStyle |= FX_NUMSTYLE_Exponent;
-      *wsPurgePattern += pStr[ccf];
-    } else if (pStr[ccf] == '%') {
-      *dwStyle |= FX_NUMSTYLE_Percent;
-      *wsPurgePattern += pStr[ccf];
-    } else if (pStr[ccf] != '}') {
-      *wsPurgePattern += pStr[ccf];
-    }
-    if (!bFindDot) {
-      if (pStr[ccf] == '.' || pStr[ccf] == 'V' || pStr[ccf] == 'v') {
-        bFindDot = true;
-        *iDotIndex = wsPurgePattern->GetLength() - 1;
-        *dwStyle |= FX_NUMSTYLE_DotVorv;
-      }
-    }
-    ccf++;
-  }
-  if (!bFindDot)
-    *iDotIndex = wsPurgePattern->GetLength();
-  if (!pLocale)
-    pLocale = m_pLocaleMgr->GetDefLocale();
-  return pLocale;
-}
-
-bool CFGAS_FormatString::ParseText(const WideString& wsSrcText,
-                                   const WideString& wsPattern,
-                                   WideString* wsValue) {
-  wsValue->clear();
-  if (wsSrcText.IsEmpty() || wsPattern.IsEmpty())
-    return false;
-
-  WideString wsTextFormat = GetTextFormat(wsPattern, L"text");
-  if (wsTextFormat.IsEmpty())
-    return false;
-
-  int32_t iText = 0;
-  int32_t iPattern = 0;
-  const wchar_t* pStrText = wsSrcText.c_str();
-  int32_t iLenText = wsSrcText.GetLength();
-  const wchar_t* pStrPattern = wsTextFormat.c_str();
-  int32_t iLenPattern = wsTextFormat.GetLength();
-  while (iPattern < iLenPattern && iText < iLenText) {
-    switch (pStrPattern[iPattern]) {
-      case '\'': {
-        WideString wsLiteral =
-            GetLiteralText(pStrPattern, &iPattern, iLenPattern);
-        int32_t iLiteralLen = wsLiteral.GetLength();
-        if (iText + iLiteralLen > iLenText ||
-            wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
-          *wsValue = wsSrcText;
-          return false;
-        }
-        iText += iLiteralLen;
-        iPattern++;
-        break;
-      }
-      case 'A':
-        if (FXSYS_iswalpha(pStrText[iText])) {
-          *wsValue += pStrText[iText];
-          iText++;
-        }
-        iPattern++;
-        break;
-      case 'X':
-        *wsValue += pStrText[iText];
-        iText++;
-        iPattern++;
-        break;
-      case 'O':
-      case '0':
-        if (FXSYS_isDecimalDigit(pStrText[iText]) ||
-            FXSYS_iswalpha(pStrText[iText])) {
-          *wsValue += pStrText[iText];
-          iText++;
-        }
-        iPattern++;
-        break;
-      case '9':
-        if (FXSYS_isDecimalDigit(pStrText[iText])) {
-          *wsValue += pStrText[iText];
-          iText++;
-        }
-        iPattern++;
-        break;
-      default:
-        if (pStrPattern[iPattern] != pStrText[iText]) {
-          *wsValue = wsSrcText;
-          return false;
-        }
-        iPattern++;
-        iText++;
-        break;
-    }
-  }
-  return iPattern == iLenPattern && iText == iLenText;
-}
-
-bool CFGAS_FormatString::ParseNum(const WideString& wsSrcNum,
-                                  const WideString& wsPattern,
-                                  WideString* wsValue) {
-  wsValue->clear();
-  if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty())
-    return false;
-
-  int32_t dot_index_f = -1;
-  uint32_t dwFormatStyle = 0;
-  WideString wsNumFormat;
-  IFX_Locale* pLocale =
-      GetNumericFormat(wsPattern, &dot_index_f, &dwFormatStyle, &wsNumFormat);
-  if (!pLocale || wsNumFormat.IsEmpty())
-    return false;
-
-  int32_t iExponent = 0;
-  WideString wsDotSymbol =
-      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
-  WideString wsGroupSymbol =
-      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping);
-  int32_t iGroupLen = wsGroupSymbol.GetLength();
-  WideString wsMinus = pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus);
-  int32_t iMinusLen = wsMinus.GetLength();
-  const wchar_t* str = wsSrcNum.c_str();
-  int len = wsSrcNum.GetLength();
-  const wchar_t* strf = wsNumFormat.c_str();
-  int lenf = wsNumFormat.GetLength();
-  bool bHavePercentSymbol = false;
-  bool bNeg = false;
-  bool bReverseParse = false;
-  int32_t dot_index = 0;
-
-  // If we're looking for a '.', 'V' or 'v' and the input string does not
-  // have a dot index for one of those, then we disable parsing the decimal.
-  if (!GetNumericDotIndex(wsSrcNum, wsDotSymbol, &dot_index) &&
-      (dwFormatStyle & FX_NUMSTYLE_DotVorv))
-    bReverseParse = true;
-
-  // This parse is broken into two parts based on the '.' in the number
-  // (or 'V' or 'v'). |dot_index_f| is the location of the dot in the format and
-  // |dot_index| is the location of the dot in the number.
-  //
-  // This first while() starts at the '.' and walks backwards to the start of
-  // the number. The second while() walks from the dot forwards to the end of
-  // the decimal.
-
-  int ccf = dot_index_f - 1;
-  int cc = dot_index - 1;
-  while (ccf >= 0 && cc >= 0) {
-    switch (strf[ccf]) {
-      case '\'': {
-        WideString wsLiteral = GetLiteralTextReverse(strf, &ccf);
-        int32_t iLiteralLen = wsLiteral.GetLength();
-        cc -= iLiteralLen - 1;
-        if (cc < 0 || wcsncmp(str + cc, wsLiteral.c_str(), iLiteralLen))
-          return false;
-
-        cc--;
-        ccf--;
-        break;
-      }
-      case '9':
-        if (!FXSYS_isDecimalDigit(str[cc]))
-          return false;
-
-        wsValue->InsertAtFront(str[cc]);
-        cc--;
-        ccf--;
-        break;
-      case 'z':
-      case 'Z':
-        if (strf[ccf] == 'z' || str[cc] != ' ') {
-          if (FXSYS_isDecimalDigit(str[cc])) {
-            wsValue->InsertAtFront(str[cc]);
-            cc--;
-          }
-        } else {
-          cc--;
-        }
-        ccf--;
-        break;
-      case 'S':
-      case 's':
-        if (str[cc] == '+' || (strf[ccf] == 'S' && str[cc] == ' ')) {
-          cc--;
-        } else {
-          cc -= iMinusLen - 1;
-          if (cc < 0 || wcsncmp(str + cc, wsMinus.c_str(), iMinusLen))
-            return false;
-
-          cc--;
-          bNeg = true;
-        }
-        ccf--;
-        break;
-      case 'E': {
-        bool bExpSign = false;
-        while (cc >= 0) {
-          if (str[cc] == 'E' || str[cc] == 'e')
-            break;
-          if (FXSYS_isDecimalDigit(str[cc])) {
-            iExponent = iExponent + FXSYS_DecimalCharToInt(str[cc]) * 10;
-            cc--;
-            continue;
-          }
-          if (str[cc] == '+') {
-            cc--;
-            continue;
-          }
-          if (cc - iMinusLen + 1 > 0 && !wcsncmp(str + (cc - iMinusLen + 1),
-                                                 wsMinus.c_str(), iMinusLen)) {
-            bExpSign = true;
-            cc -= iMinusLen;
-            continue;
-          }
-
-          return false;
-        }
-        cc--;
-        iExponent = bExpSign ? -iExponent : iExponent;
-        ccf--;
-        break;
-      }
-      case '$': {
-        WideString wsSymbol =
-            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
-        int32_t iSymbolLen = wsSymbol.GetLength();
-        cc -= iSymbolLen - 1;
-        if (cc < 0 || wcsncmp(str + cc, wsSymbol.c_str(), iSymbolLen))
-          return false;
-
-        cc--;
-        ccf--;
-        break;
-      }
-      case 'r':
-      case 'R':
-        if (ccf - 1 >= 0 && ((strf[ccf] == 'R' && strf[ccf - 1] == 'C') ||
-                             (strf[ccf] == 'r' && strf[ccf - 1] == 'c'))) {
-          if (strf[ccf] == 'R' && str[cc] == ' ') {
-            cc -= 2;
-          } else if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
-            bNeg = true;
-            cc -= 2;
-          }
-          ccf -= 2;
-        } else {
-          ccf--;
-        }
-        break;
-      case 'b':
-      case 'B':
-        if (ccf - 1 >= 0 && ((strf[ccf] == 'B' && strf[ccf - 1] == 'D') ||
-                             (strf[ccf] == 'b' && strf[ccf - 1] == 'd'))) {
-          if (strf[ccf] == 'B' && str[cc] == ' ') {
-            cc -= 2;
-          } else if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
-            bNeg = true;
-            cc -= 2;
-          }
-          ccf -= 2;
-        } else {
-          ccf--;
-        }
-        break;
-      case '%': {
-        WideString wsSymbol =
-            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
-        int32_t iSysmbolLen = wsSymbol.GetLength();
-        cc -= iSysmbolLen - 1;
-        if (cc < 0 || wcsncmp(str + cc, wsSymbol.c_str(), iSysmbolLen))
-          return false;
-
-        cc--;
-        ccf--;
-        bHavePercentSymbol = true;
-        break;
-      }
-      case '.':
-      case 'V':
-      case 'v':
-      case '8':
-        return false;
-      case ',': {
-        if (cc >= 0) {
-          cc -= iGroupLen - 1;
-          if (cc >= 0 &&
-              wcsncmp(str + cc, wsGroupSymbol.c_str(), iGroupLen) == 0) {
-            cc--;
-          } else {
-            cc += iGroupLen - 1;
-          }
-        }
-        ccf--;
-        break;
-      }
-      case '(':
-      case ')':
-        if (str[cc] == strf[ccf])
-          bNeg = true;
-        else if (str[cc] != L' ')
-          return false;
-
-        cc--;
-        ccf--;
-        break;
-      default:
-        if (strf[ccf] != str[cc])
-          return false;
-
-        cc--;
-        ccf--;
-    }
-  }
-  if (cc >= 0) {
-    if (str[cc] == '-') {
-      bNeg = true;
-      cc--;
-    }
-    if (cc >= 0)
-      return false;
-  }
-  if (dot_index < len && (dwFormatStyle & FX_NUMSTYLE_DotVorv))
-    *wsValue += '.';
-  if (!bReverseParse) {
-    ccf = dot_index_f + 1;
-    cc = (dot_index == len) ? len : dot_index + 1;
-    while (cc < len && ccf < lenf) {
-      switch (strf[ccf]) {
-        case '\'': {
-          WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
-          int32_t iLiteralLen = wsLiteral.GetLength();
-          if (cc + iLiteralLen > len ||
-              wcsncmp(str + cc, wsLiteral.c_str(), iLiteralLen)) {
-            return false;
-          }
-          cc += iLiteralLen;
-          ccf++;
-          break;
-        }
-        case '9':
-          if (!FXSYS_isDecimalDigit(str[cc]))
-            return false;
-
-          *wsValue += str[cc];
-          cc++;
-          ccf++;
-          break;
-        case 'z':
-        case 'Z':
-          if (strf[ccf] == 'z' || str[cc] != ' ') {
-            if (FXSYS_isDecimalDigit(str[cc])) {
-              *wsValue += str[cc];
-              cc++;
-            }
-          } else {
-            cc++;
-          }
-          ccf++;
-          break;
-        case 'S':
-        case 's':
-          if (str[cc] == '+' || (strf[ccf] == 'S' && str[cc] == ' ')) {
-            cc++;
-          } else {
-            if (cc + iMinusLen > len ||
-                wcsncmp(str + cc, wsMinus.c_str(), iMinusLen)) {
-              return false;
-            }
-            bNeg = true;
-            cc += iMinusLen;
-          }
-          ccf++;
-          break;
-        case 'E': {
-          if (cc >= len || (str[cc] != 'E' && str[cc] != 'e'))
-            return false;
-
-          bool bExpSign = false;
-          cc++;
-          if (cc < len) {
-            if (str[cc] == '+') {
-              cc++;
-            } else if (str[cc] == '-') {
-              bExpSign = true;
-              cc++;
-            }
-          }
-          while (cc < len) {
-            if (!FXSYS_isDecimalDigit(str[cc]))
-              break;
-
-            iExponent = iExponent * 10 + FXSYS_DecimalCharToInt(str[cc]);
-            cc++;
-          }
-          iExponent = bExpSign ? -iExponent : iExponent;
-          ccf++;
-          break;
-        }
-        case '$': {
-          WideString wsSymbol =
-              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
-          int32_t iSymbolLen = wsSymbol.GetLength();
-          if (cc + iSymbolLen > len ||
-              wcsncmp(str + cc, wsSymbol.c_str(), iSymbolLen)) {
-            return false;
-          }
-          cc += iSymbolLen;
-          ccf++;
-          break;
-        }
-        case 'c':
-        case 'C':
-          if (ccf + 1 < lenf && ((strf[ccf] == 'C' && strf[ccf + 1] == 'R') ||
-                                 (strf[ccf] == 'c' && strf[ccf + 1] == 'r'))) {
-            if (strf[ccf] == 'C' && str[cc] == ' ') {
-              cc++;
-            } else if (str[cc] == 'C' && cc + 1 < len && str[cc + 1] == 'R') {
-              bNeg = true;
-              cc += 2;
-            }
-            ccf += 2;
-          }
-          break;
-        case 'd':
-        case 'D':
-          if (ccf + 1 < lenf && ((strf[ccf] == 'D' && strf[ccf + 1] == 'B') ||
-                                 (strf[ccf] == 'd' && strf[ccf + 1] == 'b'))) {
-            if (strf[ccf] == 'D' && str[cc] == ' ') {
-              cc++;
-            } else if (str[cc] == 'D' && cc + 1 < len && str[cc + 1] == 'B') {
-              bNeg = true;
-              cc += 2;
-            }
-            ccf += 2;
-          }
-          break;
-        case '.':
-        case 'V':
-        case 'v':
-          return false;
-        case '%': {
-          WideString wsSymbol =
-              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
-          int32_t iSysmbolLen = wsSymbol.GetLength();
-          if (cc + iSysmbolLen <= len &&
-              !wcsncmp(str + cc, wsSymbol.c_str(), iSysmbolLen)) {
-            cc += iSysmbolLen;
-          }
-          ccf++;
-          bHavePercentSymbol = true;
-        } break;
-        case '8': {
-          while (ccf < lenf && strf[ccf] == '8')
-            ccf++;
-
-          while (cc < len && FXSYS_isDecimalDigit(str[cc])) {
-            *wsValue += str[cc];
-            cc++;
-          }
-        } break;
-        case ',': {
-          if (cc + iGroupLen <= len &&
-              wcsncmp(str + cc, wsGroupSymbol.c_str(), iGroupLen) == 0) {
-            cc += iGroupLen;
-          }
-          ccf++;
-          break;
-        }
-        case '(':
-        case ')':
-          if (str[cc] == strf[ccf])
-            bNeg = true;
-          else if (str[cc] != L' ')
-            return false;
-
-          cc++;
-          ccf++;
-          break;
-        default:
-          if (strf[ccf] != str[cc])
-            return false;
-
-          cc++;
-          ccf++;
-      }
-    }
-    if (cc != len)
-      return false;
-  }
-  if (iExponent || bHavePercentSymbol) {
-    CFX_Decimal decimal = CFX_Decimal(wsValue->AsStringView());
-    if (iExponent) {
-      decimal = decimal *
-                CFX_Decimal(FXSYS_pow(10, static_cast<float>(iExponent)), 3);
-    }
-    if (bHavePercentSymbol)
-      decimal = decimal / CFX_Decimal(100);
-
-    *wsValue = decimal;
-  }
-  if (bNeg)
-    wsValue->InsertAtFront(L'-');
-
-  return true;
-}
-
-FX_DATETIMETYPE CFGAS_FormatString::GetDateTimeFormat(
-    const WideString& wsPattern,
-    IFX_Locale** pLocale,
-    WideString* wsDatePattern,
-    WideString* wsTimePattern) {
-  *pLocale = nullptr;
-  WideString wsTempPattern;
-  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
-  int32_t ccf = 0;
-  int32_t iLenf = wsPattern.GetLength();
-  const wchar_t* pStr = wsPattern.c_str();
-  int32_t iFindCategory = 0;
-  bool bBraceOpen = false;
-  WideStringView wsConstChars(gs_wsConstChars);
-  while (ccf < iLenf) {
-    if (pStr[ccf] == '\'') {
-      int32_t iCurChar = ccf;
-      GetLiteralText(pStr, &ccf, iLenf);
-      wsTempPattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
-    } else if (!bBraceOpen && iFindCategory != 3 &&
-               !wsConstChars.Contains(pStr[ccf])) {
-      WideString wsCategory(pStr[ccf]);
-      ccf++;
-      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
-             pStr[ccf] != '(') {
-        if (pStr[ccf] == 'T') {
-          *wsDatePattern = wsPattern.Left(ccf);
-          *wsTimePattern = wsPattern.Right(wsPattern.GetLength() - ccf);
-          wsTimePattern->SetAt(0, ' ');
-          if (!*pLocale)
-            *pLocale = m_pLocaleMgr->GetDefLocale();
-
-          return FX_DATETIMETYPE_DateTime;
-        }
-        wsCategory += pStr[ccf];
-        ccf++;
-      }
-      if (!(iFindCategory & 1) && wsCategory == L"date") {
-        iFindCategory |= 1;
-        eCategory = FX_LOCALECATEGORY_Date;
-        if (iFindCategory & 2)
-          iFindCategory = 4;
-      } else if (!(iFindCategory & 2) && wsCategory == L"time") {
-        iFindCategory |= 2;
-        eCategory = FX_LOCALECATEGORY_Time;
-      } else if (wsCategory == L"datetime") {
-        iFindCategory = 3;
-        eCategory = FX_LOCALECATEGORY_DateTime;
-      } else {
-        continue;
-      }
-      while (ccf < iLenf) {
-        if (pStr[ccf] == '{') {
-          bBraceOpen = true;
-          break;
-        }
-        if (pStr[ccf] == '(') {
-          ccf++;
-          WideString wsLCID;
-          while (ccf < iLenf && pStr[ccf] != ')')
-            wsLCID += pStr[ccf++];
-
-          *pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
-        } else if (pStr[ccf] == '.') {
-          WideString wsSubCategory;
-          ccf++;
-          while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{')
-            wsSubCategory += pStr[ccf++];
-
-          uint32_t dwSubHash =
-              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
-          FX_LOCALEDATETIMESUBCATEGORY eSubCategory =
-              FX_LOCALEDATETIMESUBCATEGORY_Medium;
-          for (int32_t i = 0; i < g_iFXLocaleDateTimeSubCatCount; i++) {
-            if (g_FXLocaleDateTimeSubCatData[i].uHash == dwSubHash) {
-              eSubCategory =
-                  (FX_LOCALEDATETIMESUBCATEGORY)g_FXLocaleDateTimeSubCatData[i]
-                      .eSubCategory;
-              break;
-            }
-          }
-          if (!*pLocale)
-            *pLocale = m_pLocaleMgr->GetDefLocale();
-          ASSERT(*pLocale);
-
-          switch (eCategory) {
-            case FX_LOCALECATEGORY_Date:
-              *wsDatePattern =
-                  wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
-              break;
-            case FX_LOCALECATEGORY_Time:
-              *wsTimePattern =
-                  wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
-              break;
-            case FX_LOCALECATEGORY_DateTime:
-              *wsDatePattern =
-                  wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
-              *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
-              break;
-            default:
-              break;
-          }
-          wsTempPattern.clear();
-          continue;
-        }
-        ccf++;
-      }
-    } else if (pStr[ccf] == '}') {
-      bBraceOpen = false;
-      if (!wsTempPattern.IsEmpty()) {
-        if (eCategory == FX_LOCALECATEGORY_Time)
-          *wsTimePattern = wsTempPattern;
-        else if (eCategory == FX_LOCALECATEGORY_Date)
-          *wsDatePattern = wsTempPattern;
-
-        wsTempPattern.clear();
-      }
-    } else {
-      wsTempPattern += pStr[ccf];
-    }
-    ccf++;
-  }
-
-  if (!wsTempPattern.IsEmpty()) {
-    if (eCategory == FX_LOCALECATEGORY_Date)
-      *wsDatePattern += wsTempPattern;
-    else
-      *wsTimePattern += wsTempPattern;
-  }
-  if (!*pLocale)
-    *pLocale = m_pLocaleMgr->GetDefLocale();
-  if (!iFindCategory) {
-    wsTimePattern->clear();
-    *wsDatePattern = wsPattern;
-  }
-  return (FX_DATETIMETYPE)iFindCategory;
-}
-
-bool CFGAS_FormatString::ParseDateTime(const WideString& wsSrcDateTime,
-                                       const WideString& wsPattern,
-                                       FX_DATETIMETYPE eDateTimeType,
-                                       CFX_DateTime* dtValue) {
-  dtValue->Reset();
-  if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty())
-    return false;
-
-  WideString wsDatePattern;
-  WideString wsTimePattern;
-  IFX_Locale* pLocale = nullptr;
-  FX_DATETIMETYPE eCategory =
-      GetDateTimeFormat(wsPattern, &pLocale, &wsDatePattern, &wsTimePattern);
-  if (!pLocale)
-    return false;
-  if (eCategory == FX_DATETIMETYPE_Unknown)
-    eCategory = eDateTimeType;
-  if (eCategory == FX_DATETIMETYPE_Unknown)
-    return false;
-  if (eCategory == FX_DATETIMETYPE_TimeDate) {
-    int32_t iStart = 0;
-    if (!ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
-                         &iStart)) {
-      return false;
-    }
-    if (!ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
-                         &iStart)) {
-      return false;
-    }
-  } else {
-    int32_t iStart = 0;
-    if ((eCategory & FX_DATETIMETYPE_Date) &&
-        !ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
-                         &iStart)) {
-      return false;
-    }
-    if ((eCategory & FX_DATETIMETYPE_Time) &&
-        !ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
-                         &iStart)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool CFGAS_FormatString::ParseZero(const WideString& wsSrcText,
-                                   const WideString& wsPattern) {
-  WideString wsTextFormat = GetTextFormat(wsPattern, L"zero");
-
-  int32_t iText = 0;
-  int32_t iPattern = 0;
-  const wchar_t* pStrText = wsSrcText.c_str();
-  int32_t iLenText = wsSrcText.GetLength();
-  const wchar_t* pStrPattern = wsTextFormat.c_str();
-  int32_t iLenPattern = wsTextFormat.GetLength();
-  while (iPattern < iLenPattern && iText < iLenText) {
-    if (pStrPattern[iPattern] == '\'') {
-      WideString wsLiteral =
-          GetLiteralText(pStrPattern, &iPattern, iLenPattern);
-      int32_t iLiteralLen = wsLiteral.GetLength();
-      if (iText + iLiteralLen > iLenText ||
-          wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
-        return false;
-      }
-      iText += iLiteralLen;
-      iPattern++;
-      continue;
-    }
-    if (pStrPattern[iPattern] != pStrText[iText])
-      return false;
-
-    iText++;
-    iPattern++;
-  }
-  return iPattern == iLenPattern && iText == iLenText;
-}
-
-bool CFGAS_FormatString::ParseNull(const WideString& wsSrcText,
-                                   const WideString& wsPattern) {
-  WideString wsTextFormat = GetTextFormat(wsPattern, L"null");
-
-  int32_t iText = 0;
-  int32_t iPattern = 0;
-  const wchar_t* pStrText = wsSrcText.c_str();
-  int32_t iLenText = wsSrcText.GetLength();
-  const wchar_t* pStrPattern = wsTextFormat.c_str();
-  int32_t iLenPattern = wsTextFormat.GetLength();
-  while (iPattern < iLenPattern && iText < iLenText) {
-    if (pStrPattern[iPattern] == '\'') {
-      WideString wsLiteral =
-          GetLiteralText(pStrPattern, &iPattern, iLenPattern);
-      int32_t iLiteralLen = wsLiteral.GetLength();
-      if (iText + iLiteralLen > iLenText ||
-          wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
-        return false;
-      }
-      iText += iLiteralLen;
-      iPattern++;
-      continue;
-    }
-    if (pStrPattern[iPattern] != pStrText[iText])
-      return false;
-
-    iText++;
-    iPattern++;
-  }
-  return iPattern == iLenPattern && iText == iLenText;
-}
-
-bool CFGAS_FormatString::FormatText(const WideString& wsSrcText,
-                                    const WideString& wsPattern,
-                                    WideString* wsOutput) {
-  if (wsPattern.IsEmpty())
-    return false;
-
-  int32_t iLenText = wsSrcText.GetLength();
-  if (iLenText == 0)
-    return false;
-
-  WideString wsTextFormat = GetTextFormat(wsPattern, L"text");
-
-  int32_t iText = 0;
-  int32_t iPattern = 0;
-  const wchar_t* pStrText = wsSrcText.c_str();
-  const wchar_t* pStrPattern = wsTextFormat.c_str();
-  int32_t iLenPattern = wsTextFormat.GetLength();
-  while (iPattern < iLenPattern) {
-    switch (pStrPattern[iPattern]) {
-      case '\'': {
-        *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
-        iPattern++;
-        break;
-      }
-      case 'A':
-        if (iText >= iLenText || !FXSYS_iswalpha(pStrText[iText]))
-          return false;
-
-        *wsOutput += pStrText[iText++];
-        iPattern++;
-        break;
-      case 'X':
-        if (iText >= iLenText)
-          return false;
-
-        *wsOutput += pStrText[iText++];
-        iPattern++;
-        break;
-      case 'O':
-      case '0':
-        if (iText >= iLenText || (!FXSYS_isDecimalDigit(pStrText[iText]) &&
-                                  !FXSYS_iswalpha(pStrText[iText]))) {
-          return false;
-        }
-        *wsOutput += pStrText[iText++];
-        iPattern++;
-        break;
-      case '9':
-        if (iText >= iLenText || !FXSYS_isDecimalDigit(pStrText[iText]))
-          return false;
-
-        *wsOutput += pStrText[iText++];
-        iPattern++;
-        break;
-      default:
-        *wsOutput += pStrPattern[iPattern++];
-        break;
-    }
-  }
-  return iText == iLenText;
-}
-
-bool CFGAS_FormatString::FormatStrNum(const WideStringView& wsInputNum,
-                                      const WideString& wsPattern,
-                                      WideString* wsOutput) {
-  if (wsInputNum.IsEmpty() || wsPattern.IsEmpty())
-    return false;
-
-  int32_t dot_index_f = -1;
-  uint32_t dwNumStyle = 0;
-  WideString wsNumFormat;
-  IFX_Locale* pLocale =
-      GetNumericFormat(wsPattern, &dot_index_f, &dwNumStyle, &wsNumFormat);
-  if (!pLocale || wsNumFormat.IsEmpty())
-    return false;
-
-  int32_t cc = 0, ccf = 0;
-  const wchar_t* strf = wsNumFormat.c_str();
-  int lenf = wsNumFormat.GetLength();
-  WideString wsSrcNum(wsInputNum);
-  wsSrcNum.TrimLeft('0');
-  if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.')
-    wsSrcNum.InsertAtFront('0');
-
-  CFX_Decimal decimal = CFX_Decimal(wsSrcNum.AsStringView());
-  if (dwNumStyle & FX_NUMSTYLE_Percent) {
-    decimal = decimal * CFX_Decimal(100);
-    wsSrcNum = decimal;
-  }
-
-  int32_t exponent = 0;
-  if (dwNumStyle & FX_NUMSTYLE_Exponent) {
-    int fixed_count = 0;
-    while (ccf < dot_index_f) {
-      switch (strf[ccf]) {
-        case '\'':
-          GetLiteralText(strf, &ccf, dot_index_f);
-          break;
-        case '9':
-        case 'z':
-        case 'Z':
-          fixed_count++;
-          break;
-      }
-      ccf++;
-    }
-
-    int threshold = 1;
-    while (fixed_count > 1) {
-      threshold *= 10;
-      fixed_count--;
-    }
-    if (decimal != CFX_Decimal(0)) {
-      if (decimal < CFX_Decimal(threshold)) {
-        decimal = decimal * CFX_Decimal(10);
-        exponent = -1;
-        while (decimal < CFX_Decimal(threshold)) {
-          decimal = decimal * CFX_Decimal(10);
-          exponent -= 1;
-        }
-      } else if (decimal > CFX_Decimal(threshold)) {
-        threshold *= 10;
-        while (decimal > CFX_Decimal(threshold)) {
-          decimal = decimal / CFX_Decimal(10);
-          exponent += 1;
-        }
-      }
-    }
-  }
-
-  bool bTrimTailZeros = false;
-  int32_t iTreading =
-      GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
-  int32_t scale = decimal.GetScale();
-  if (iTreading < scale) {
-    decimal.SetScale(iTreading);
-    wsSrcNum = decimal;
-  }
-  if (bTrimTailZeros && scale > 0 && iTreading > 0) {
-    wsSrcNum.TrimRight(L"0");
-    wsSrcNum.TrimRight(L".");
-  }
-
-  WideString wsGroupSymbol =
-      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping);
-  bool bNeg = false;
-  if (wsSrcNum[0] == '-') {
-    bNeg = true;
-    wsSrcNum.Delete(0, 1);
-  }
-
-  bool bAddNeg = false;
-  const wchar_t* str = wsSrcNum.c_str();
-  int len = wsSrcNum.GetLength();
-  auto dot_index = wsSrcNum.Find('.');
-  if (!dot_index.has_value())
-    dot_index = len;
-
-  ccf = dot_index_f - 1;
-  cc = dot_index.value() - 1;
-  while (ccf >= 0) {
-    switch (strf[ccf]) {
-      case '9':
-        if (cc >= 0) {
-          if (!FXSYS_isDecimalDigit(str[cc]))
-            return false;
-
-          wsOutput->InsertAtFront(str[cc]);
-          cc--;
-        } else {
-          wsOutput->InsertAtFront(L'0');
-        }
-        ccf--;
-        break;
-      case 'z':
-        if (cc >= 0) {
-          if (!FXSYS_isDecimalDigit(str[cc]))
-            return false;
-          if (str[0] != '0')
-            wsOutput->InsertAtFront(str[cc]);
-
-          cc--;
-        }
-        ccf--;
-        break;
-      case 'Z':
-        if (cc >= 0) {
-          if (!FXSYS_isDecimalDigit(str[cc]))
-            return false;
-
-          wsOutput->InsertAtFront(str[0] == '0' ? L' ' : str[cc]);
-          cc--;
-        } else {
-          wsOutput->InsertAtFront(L' ');
-        }
-        ccf--;
-        break;
-      case 'S':
-        if (bNeg) {
-          *wsOutput =
-              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
-          bAddNeg = true;
-        } else {
-          wsOutput->InsertAtFront(L' ');
-        }
-        ccf--;
-        break;
-      case 's':
-        if (bNeg) {
-          *wsOutput =
-              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
-          bAddNeg = true;
-        }
-        ccf--;
-        break;
-      case 'E': {
-        *wsOutput = WideString::Format(L"E%+d", exponent) + *wsOutput;
-        ccf--;
-        break;
-      }
-      case '$': {
-        *wsOutput =
-            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol) +
-            *wsOutput;
-        ccf--;
-        break;
-      }
-      case 'r':
-        if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
-          if (bNeg)
-            *wsOutput = L"CR" + *wsOutput;
-
-          ccf -= 2;
-          bAddNeg = true;
-        }
-        break;
-      case 'R':
-        if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
-          *wsOutput = bNeg ? L"CR" : L"  " + *wsOutput;
-          ccf -= 2;
-          bAddNeg = true;
-        }
-        break;
-      case 'b':
-        if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
-          if (bNeg)
-            *wsOutput = L"db" + *wsOutput;
-
-          ccf -= 2;
-          bAddNeg = true;
-        }
-        break;
-      case 'B':
-        if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
-          *wsOutput = bNeg ? L"DB" : L"  " + *wsOutput;
-          ccf -= 2;
-          bAddNeg = true;
-        }
-        break;
-      case '%': {
-        *wsOutput =
-            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent) + *wsOutput;
-        ccf--;
-        break;
-      }
-      case ',':
-        if (cc >= 0)
-          *wsOutput = wsGroupSymbol + *wsOutput;
-
-        ccf--;
-        break;
-      case '(':
-        wsOutput->InsertAtFront(bNeg ? L'(' : L' ');
-        bAddNeg = true;
-        ccf--;
-        break;
-      case ')':
-        wsOutput->InsertAtFront(bNeg ? L')' : L' ');
-        ccf--;
-        break;
-      case '\'':
-        *wsOutput = GetLiteralTextReverse(strf, &ccf) + *wsOutput;
-        ccf--;
-        break;
-      default:
-        wsOutput->InsertAtFront(strf[ccf]);
-        ccf--;
-    }
-  }
-
-  if (cc >= 0) {
-    int nPos = dot_index.value() % 3;
-    wsOutput->clear();
-    for (int32_t i = 0;
-         i < pdfium::base::checked_cast<int32_t>(dot_index.value()); i++) {
-      if (i % 3 == nPos && i != 0)
-        *wsOutput += wsGroupSymbol;
-      *wsOutput += wsSrcNum[i];
-    }
-    if (pdfium::base::checked_cast<int32_t>(dot_index.value()) < len) {
-      *wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
-      *wsOutput += wsSrcNum.Right(len - dot_index.value() - 1);
-    }
-    if (bNeg) {
-      *wsOutput =
-          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
-    }
-    return false;
-  }
-  if (dot_index_f ==
-      pdfium::base::checked_cast<int32_t>(wsNumFormat.GetLength())) {
-    if (!bAddNeg && bNeg) {
-      *wsOutput =
-          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
-    }
-    return true;
-  }
-
-  WideString wsDotSymbol =
-      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
-  if (strf[dot_index_f] == 'V') {
-    *wsOutput += wsDotSymbol;
-  } else if (strf[dot_index_f] == '.') {
-    if (pdfium::base::checked_cast<int32_t>(dot_index.value()) < len)
-      *wsOutput += wsDotSymbol;
-    else if (strf[dot_index_f + 1] == '9' || strf[dot_index_f + 1] == 'Z')
-      *wsOutput += wsDotSymbol;
-  }
-
-  ccf = dot_index_f + 1;
-  cc = dot_index.value() + 1;
-  while (ccf < lenf) {
-    switch (strf[ccf]) {
-      case '\'':
-        *wsOutput += GetLiteralText(strf, &ccf, lenf);
-        ccf++;
-        break;
-      case '9':
-        if (cc < len) {
-          if (!FXSYS_isDecimalDigit(str[cc]))
-            return false;
-
-          *wsOutput += str[cc];
-          cc++;
-        } else {
-          *wsOutput += L'0';
-        }
-        ccf++;
-        break;
-      case 'z':
-        if (cc < len) {
-          if (!FXSYS_isDecimalDigit(str[cc]))
-            return false;
-
-          *wsOutput += str[cc];
-          cc++;
-        }
-        ccf++;
-        break;
-      case 'Z':
-        if (cc < len) {
-          if (!FXSYS_isDecimalDigit(str[cc]))
-            return false;
-
-          *wsOutput += str[cc];
-          cc++;
-        } else {
-          *wsOutput += L'0';
-        }
-        ccf++;
-        break;
-      case 'E': {
-        *wsOutput += WideString::Format(L"E%+d", exponent);
-        ccf++;
-        break;
-      }
-      case '$':
-        *wsOutput +=
-            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
-        ccf++;
-        break;
-      case 'c':
-        if (ccf + 1 < lenf && strf[ccf + 1] == 'r') {
-          if (bNeg)
-            *wsOutput += L"CR";
-
-          ccf += 2;
-          bAddNeg = true;
-        }
-        break;
-      case 'C':
-        if (ccf + 1 < lenf && strf[ccf + 1] == 'R') {
-          *wsOutput += bNeg ? L"CR" : L"  ";
-          ccf += 2;
-          bAddNeg = true;
-        }
-        break;
-      case 'd':
-        if (ccf + 1 < lenf && strf[ccf + 1] == 'b') {
-          if (bNeg)
-            *wsOutput += L"db";
-
-          ccf += 2;
-          bAddNeg = true;
-        }
-        break;
-      case 'D':
-        if (ccf + 1 < lenf && strf[ccf + 1] == 'B') {
-          *wsOutput += bNeg ? L"DB" : L"  ";
-          ccf += 2;
-          bAddNeg = true;
-        }
-        break;
-      case '%':
-        *wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
-        ccf++;
-        break;
-      case '8':
-        while (ccf < lenf && strf[ccf] == '8')
-          ccf++;
-        while (cc < len && FXSYS_isDecimalDigit(str[cc])) {
-          *wsOutput += str[cc];
-          cc++;
-        }
-        break;
-      case ',':
-        *wsOutput += wsGroupSymbol;
-        ccf++;
-        break;
-      case '(':
-        *wsOutput += bNeg ? '(' : ' ';
-        bAddNeg = true;
-        ccf++;
-        break;
-      case ')':
-        *wsOutput += bNeg ? ')' : ' ';
-        ccf++;
-        break;
-      default:
-        ccf++;
-    }
-  }
-  if (!bAddNeg && bNeg) {
-    *wsOutput = pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) +
-                (*wsOutput)[0] + wsOutput->Right(wsOutput->GetLength() - 1);
-  }
-  return true;
-}
-
-bool CFGAS_FormatString::FormatNum(const WideString& wsSrcNum,
-                                   const WideString& wsPattern,
-                                   WideString* wsOutput) {
-  if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty())
-    return false;
-  return FormatStrNum(wsSrcNum.AsStringView(), wsPattern, wsOutput);
-}
-
-bool CFGAS_FormatString::FormatDateTime(const WideString& wsSrcDateTime,
-                                        const WideString& wsPattern,
-                                        FX_DATETIMETYPE eDateTimeType,
-                                        WideString* wsOutput) {
-  if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty())
-    return false;
-
-  WideString wsDatePattern;
-  WideString wsTimePattern;
-  IFX_Locale* pLocale = nullptr;
-  FX_DATETIMETYPE eCategory =
-      GetDateTimeFormat(wsPattern, &pLocale, &wsDatePattern, &wsTimePattern);
-  if (!pLocale)
-    return false;
-
-  if (eCategory == FX_DATETIMETYPE_Unknown) {
-    if (eDateTimeType == FX_DATETIMETYPE_Time) {
-      wsTimePattern = wsDatePattern;
-      wsDatePattern.clear();
-    }
-    eCategory = eDateTimeType;
-  }
-  if (eCategory == FX_DATETIMETYPE_Unknown)
-    return false;
-
-  CFX_DateTime dt;
-  auto iT = wsSrcDateTime.Find(L"T");
-  if (!iT.has_value()) {
-    if (eCategory == FX_DATETIMETYPE_Date &&
-        FX_DateFromCanonical(wsSrcDateTime, &dt)) {
-      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
-                                         pLocale);
-      return true;
-    }
-    if (eCategory == FX_DATETIMETYPE_Time &&
-        FX_TimeFromCanonical(wsSrcDateTime.AsStringView(), &dt, pLocale)) {
-      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
-                                         pLocale);
-      return true;
-    }
-  } else {
-    WideString wsSrcDate(wsSrcDateTime.c_str(), iT.value());
-    WideStringView wsSrcTime(wsSrcDateTime.c_str() + iT.value() + 1,
-                             wsSrcDateTime.GetLength() - iT.value() - 1);
-    if (wsSrcDate.IsEmpty() || wsSrcTime.IsEmpty())
-      return false;
-    if (FX_DateFromCanonical(wsSrcDate, &dt) &&
-        FX_TimeFromCanonical(wsSrcTime, &dt, pLocale)) {
-      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
-                                         eCategory != FX_DATETIMETYPE_TimeDate,
-                                         pLocale);
-      return true;
-    }
-  }
-  return false;
-}
-
-bool CFGAS_FormatString::FormatZero(const WideString& wsPattern,
-                                    WideString* wsOutput) {
-  if (wsPattern.IsEmpty())
-    return false;
-
-  WideString wsTextFormat = GetTextFormat(wsPattern, L"zero");
-  int32_t iPattern = 0;
-  const wchar_t* pStrPattern = wsTextFormat.c_str();
-  int32_t iLenPattern = wsTextFormat.GetLength();
-  while (iPattern < iLenPattern) {
-    if (pStrPattern[iPattern] == '\'') {
-      *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
-      iPattern++;
-    } else {
-      *wsOutput += pStrPattern[iPattern++];
-    }
-  }
-  return true;
-}
-
-bool CFGAS_FormatString::FormatNull(const WideString& wsPattern,
-                                    WideString* wsOutput) {
-  if (wsPattern.IsEmpty())
-    return false;
-
-  WideString wsTextFormat = GetTextFormat(wsPattern, L"null");
-  int32_t iPattern = 0;
-  const wchar_t* pStrPattern = wsTextFormat.c_str();
-  int32_t iLenPattern = wsTextFormat.GetLength();
-  while (iPattern < iLenPattern) {
-    if (pStrPattern[iPattern] == '\'') {
-      *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
-      iPattern++;
-      continue;
-    }
-    *wsOutput += pStrPattern[iPattern++];
-  }
-  return true;
-}
diff --git a/xfa/fgas/crt/cfgas_formatstring.h b/xfa/fgas/crt/cfgas_formatstring.h
deleted file mode 100644
index 5eae25d..0000000
--- a/xfa/fgas/crt/cfgas_formatstring.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_CRT_CFGAS_FORMATSTRING_H_
-#define XFA_FGAS_CRT_CFGAS_FORMATSTRING_H_
-
-#include <vector>
-
-#include "core/fxcrt/ifx_locale.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-
-bool FX_DateFromCanonical(const WideString& wsDate, CFX_DateTime* datetime);
-bool FX_TimeFromCanonical(const WideStringView& wsTime,
-                          CFX_DateTime* datetime,
-                          IFX_Locale* pLocale);
-
-class CFGAS_FormatString {
- public:
-  explicit CFGAS_FormatString(CXFA_LocaleMgr* pLocaleMgr);
-  ~CFGAS_FormatString();
-
-  void SplitFormatString(const WideString& wsFormatString,
-                         std::vector<WideString>* wsPatterns);
-  FX_LOCALECATEGORY GetCategory(const WideString& wsPattern);
-
-  bool ParseText(const WideString& wsSrcText,
-                 const WideString& wsPattern,
-                 WideString* wsValue);
-  bool ParseNum(const WideString& wsSrcNum,
-                const WideString& wsPattern,
-                WideString* wsValue);
-  bool ParseDateTime(const WideString& wsSrcDateTime,
-                     const WideString& wsPattern,
-                     FX_DATETIMETYPE eDateTimeType,
-                     CFX_DateTime* dtValue);
-  bool ParseZero(const WideString& wsSrcText, const WideString& wsPattern);
-  bool ParseNull(const WideString& wsSrcText, const WideString& wsPattern);
-
-  bool FormatText(const WideString& wsSrcText,
-                  const WideString& wsPattern,
-                  WideString* wsOutput);
-  bool FormatNum(const WideString& wsSrcNum,
-                 const WideString& wsPattern,
-                 WideString* wsOutput);
-  bool FormatDateTime(const WideString& wsSrcDateTime,
-                      const WideString& wsPattern,
-                      FX_DATETIMETYPE eDateTimeType,
-                      WideString* wsOutput);
-  bool FormatZero(const WideString& wsPattern, WideString* wsOutput);
-  bool FormatNull(const WideString& wsPattern, WideString* wsOutput);
-
- private:
-  WideString GetTextFormat(const WideString& wsPattern,
-                           const WideStringView& wsCategory);
-  IFX_Locale* GetNumericFormat(const WideString& wsPattern,
-                               int32_t* iDotIndex,
-                               uint32_t* dwStyle,
-                               WideString* wsPurgePattern);
-  bool FormatStrNum(const WideStringView& wsInputNum,
-                    const WideString& wsPattern,
-                    WideString* wsOutput);
-  FX_DATETIMETYPE GetDateTimeFormat(const WideString& wsPattern,
-                                    IFX_Locale** pLocale,
-                                    WideString* wsDatePattern,
-                                    WideString* wsTimePattern);
-
-  CXFA_LocaleMgr* m_pLocaleMgr;
-};
-
-#endif  // XFA_FGAS_CRT_CFGAS_FORMATSTRING_H_
diff --git a/xfa/fgas/crt/cfgas_formatstring_unittest.cpp b/xfa/fgas/crt/cfgas_formatstring_unittest.cpp
deleted file mode 100644
index 51c0010..0000000
--- a/xfa/fgas/crt/cfgas_formatstring_unittest.cpp
+++ /dev/null
@@ -1,661 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/crt/cfgas_formatstring.h"
-
-#include <time.h>
-
-#include <memory>
-
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "testing/fx_string_testhelpers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-
-class CFGAS_FormatStringTest : public testing::Test {
- public:
-  CFGAS_FormatStringTest() {
-    SetTZ("UTC");
-    CPDF_ModuleMgr::Get()->Init();
-  }
-
-  ~CFGAS_FormatStringTest() override { CPDF_ModuleMgr::Get()->Destroy(); }
-
-  void TearDown() override {
-    fmt_.reset();
-    mgr_.reset();
-  }
-
-  void SetTZ(const char* tz) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-    _putenv_s("TZ", tz);
-    _tzset();
-#else
-    setenv("TZ", tz, 1);
-    tzset();
-#endif
-  }
-
-  // Note, this re-creates the fmt on each call. If you need to multiple
-  // times store it locally.
-  CFGAS_FormatString* fmt(const WideString& locale) {
-    mgr_ = pdfium::MakeUnique<CXFA_LocaleMgr>(nullptr, locale);
-    fmt_ = pdfium::MakeUnique<CFGAS_FormatString>(mgr_.get());
-    return fmt_.get();
-  }
-
- protected:
-  std::unique_ptr<CXFA_LocaleMgr> mgr_;
-  std::unique_ptr<CFGAS_FormatString> fmt_;
-};
-
-// TODO(dsinclair): Looks like the formatter/parser does not handle the various
-// 'g' flags.
-TEST_F(CFGAS_FormatStringTest, DateFormat) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {
-      {L"en", L"2002-10-25", L"MMMM DD, YYYY", L"October 25, 2002"},
-      // Note, this is in the doc as 5 but it's wrong and should be 3 by the
-      // example in the Picture Clause Reference section.
-      {L"en", L"20040722", L"'Week of the month is' w",
-       L"Week of the month is 3"},
-      {L"en", L"20040722", L"e 'days after Sunday'", L"4 days after Sunday"},
-      {L"en", L"20040722", L"YYYY-'W'WW-e", L"2004-W30-4"},
-      {L"en", L"20040722", L"E 'days after Saturday'",
-       L"5 days after Saturday"},
-      {L"en", L"2000-01-01", L"EEE, 'the' D 'of' MMMM, YYYY",
-       L"Sat, the 1 of January, 2000"},
-      {L"en", L"2000-01-01", L"EEEE, 'the' D 'of' MMMM, YYYY",
-       L"Saturday, the 1 of January, 2000"},
-      {L"en", L"19991202", L"MM/D/YY", L"12/2/99"},
-      {L"en", L"19990110", L"MMM D, YYYY", L"Jan 10, 1999"},
-      {L"en", L"19990202", L"J", L"33"},
-      {L"en", L"19990202", L"JJJ", L"033"},
-      {L"en", L"19991231", L"J", L"365"},
-      {L"en", L"20001231", L"J", L"366"},
-      {L"en", L"19990501", L"J", L"121"},
-      {L"en", L"19990901", L"J", L"244"},
-      {L"en", L"19990228", L"J", L"59"},
-      {L"en", L"20000229", L"J", L"60"},
-      {L"en", L"21000501", L"J", L"121"},
-      {L"en", L"19990102", L"M", L"1"},
-      {L"en", L"19990102", L"MMM", L"Jan"},
-      {L"en", L"19990102", L"YYYY G", L"1999 AD"},
-      // Week 01 of the year is the week containing Jan 04.
-      // {L"en", L"19990102", L"WW", L"00"},  -- Returns 01 incorrectly
-      // {L"en", L"19990104", L"WW", L"01"},  -- Returns 02 incorrectly
-      // The ?*+ should format as whitespace.
-      // {L"en", L"19990104", L"YYYY?*+MM", L"1999   01"},
-      // {L"en", L"1999-07-16", L"date{DD/MM/YY} '('date{MMM DD, YYYY}')'",
-      //  L"16/07/99 (Jul 16, 1999)"},
-      {L"de_CH", L"20041030", L"D. MMMM YYYY", L"30. Oktober 2004"},
-      {L"fr_CA", L"20041030", L"D MMMM YYYY", L"30 octobre 2004"},
-      {L"en", L"2002-10-25", L"date(fr){DD MMMM, YYYY}", L"25 octobre, 2002"},
-      {L"en", L"2002-10-25", L"date(es){EEEE, D 'de' MMMM 'de' YYYY}",
-       L"viernes, 25 de octubre de 2002"},
-      // {L"en", L"2002-20-25", L"date.long(fr)()", L"25 octobre, 2002"},
-      // {L"ja", L"2003-11-03", L"gY/M/D", L"H15/11/3"},
-      // {L"ja", L"1989-01-08", L"ggY-M-D", L"\u5e731-1-8"},
-      // {L"ja", L"1989-11-03", L"gggYY/MM/DD", L"\u5e73\u621089/11/03"},
-  };
-  // Note, none of the full width date symbols are listed here
-  // as they are not supported. In theory there are the full width versions
-  // of DDD, DDDD, MMM, MMMM, E, e, gg, YYY, YYYYY.
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->FormatDateTime(tests[i].input, tests[i].pattern,
-                                     FX_DATETIMETYPE_Date, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, TimeFormat) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {{L"en", L"01:01:11", L"h:M A", L"1:1 AM"},
-               {L"en", L"13:01:11", L"h:M A", L"1:1 PM"},
-               {L"en", L"01:01:11", L"hh:MM:SS A", L"01:01:11 AM"},
-               {L"en", L"13:01:11", L"hh:MM:SS A", L"01:01:11 PM"},
-               {L"en", L"01:01:11", L"hh:MM:SS A Z", L"01:01:11 AM GMT-02:00"},
-               {L"en", L"01:01:11", L"hh:MM:SS A z", L"01:01:11 AM -02:00"},
-               // {L"en", L"01:01:11", L"hh:MM:SS A zz", L"01:01:11 AM GMT"},
-               // Should change ?*+ into ' ' when formatting.
-               // {L"en", L"01:01:11", L"hh:MM:SS?*+A", L"01:01:11   AM"},
-               {L"en", L"12:01:01", L"k:MM:SS", L"12:01:01"},
-               {L"en", L"14:01:01", L"k:MM:SS", L"2:01:01"},
-               {L"en", L"12:01:11", L"kk:MM", L"12:01"},
-               {L"en", L"14:01:11", L"kk:MM", L"02:01"},
-               {L"en", L"12:01:11 +04:30", L"kk:MM", L"05:31"},
-               {L"en", L"12:01:11", L"kk:MM A", L"12:01 PM"},
-               {L"en", L"00:01:01", L"H:M:S", L"0:1:1"},
-               {L"en", L"13:02:11", L"H:M:S", L"13:2:11"},
-               {L"en", L"00:01:11.001", L"HH:M:S.FFF", L"00:1:11.001"},
-               {L"en", L"13:02:11", L"HH:M", L"13:2"},
-               {L"en", L"00:01:11", L"K:M", L"24:1"},
-               {L"en", L"00:02:11", L"KK:M", L"24:2"},
-               {L"en", L"11:11:11", L"HH:MM:SS 'o''clock' A Z",
-                L"11:11:11 o'clock AM GMT-02:00"},
-               {L"en", L"14:30:59", L"h:MM A", L"2:30 PM"},
-               {L"en", L"14:30:59", L"HH:MM:SS A Z", L"14:30:59 PM GMT-02:00"}};
-  // Note, none of the full width time symbols are listed here
-  // as they are not supported. In theory there are the full
-  // width versions of kkk, kkkk, HHH, HHHH, KKK, KKKK, MMM, MMMM,
-  // SSS, SSSS plus 2 more that the spec apparently forgot to
-  // list the symbol.
-
-  // The z modifier only appends if the TZ is outside of +0
-  SetTZ("UTC+2");
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->FormatDateTime(tests[i].input, tests[i].pattern,
-                                     FX_DATETIMETYPE_Time, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-
-  SetTZ("UTC");
-}
-
-TEST_F(CFGAS_FormatStringTest, DateTimeFormat) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {
-      {L"en", L"1999-07-16T10:30Z",
-       L"'At' time{HH:MM Z} 'on' date{MMM DD, YYYY}",
-       L"At 10:30 GMT on Jul 16, 1999"},
-      {L"en", L"1999-07-16T10:30", L"'At' time{HH:MM} 'on' date{MMM DD, YYYY}",
-       L"At 10:30 on Jul 16, 1999"},
-      {L"en", L"1999-07-16T10:30Z",
-       L"time{'At' HH:MM Z} date{'on' MMM DD, YYYY}",
-       L"At 10:30 GMT on Jul 16, 1999"},
-      {L"en", L"1999-07-16T10:30Z",
-       L"time{'At 'HH:MM Z}date{' on 'MMM DD, YYYY}",
-       L"At 10:30 GMT on Jul 16, 1999"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->FormatDateTime(tests[i].input, tests[i].pattern,
-                                     FX_DATETIMETYPE_TimeDate, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, DateParse) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    CFX_DateTime output;
-  } tests[] = {
-      {L"en", L"12/2/99", L"MM/D/YY", CFX_DateTime(1999, 12, 2, 0, 0, 0, 0)},
-      {L"en", L"2/2/99", L"M/D/YY", CFX_DateTime(1999, 2, 2, 0, 0, 0, 0)},
-      {L"en", L"2/2/10", L"M/D/YY", CFX_DateTime(2010, 2, 2, 0, 0, 0, 0)},
-      {L"en", L"Jan 10, 1999", L"MMM D, YYYY",
-       CFX_DateTime(1999, 1, 10, 0, 0, 0, 0)},
-      {L"en", L"Jan 10, 1999 AD", L"MMM D, YYYY G",
-       CFX_DateTime(1999, 1, 10, 0, 0, 0, 0)},
-      // TODO(dsinclair): Should this be -2 instead of 2?
-      {L"en", L"Jan 10, 0002 BC", L"MMM D, YYYY G",
-       CFX_DateTime(2, 1, 10, 0, 0, 0, 0)},
-      {L"en", L"October 25, 2002", L"MMMM DD, YYYY",
-       CFX_DateTime(2002, 10, 25, 0, 0, 0, 0)},
-      // TODO(dsinclair): The J and JJJ are ignored during parsing when they
-      // could be turned back into a date.
-      {L"en", L"1999-33", L"YYYY-J", CFX_DateTime(1999, 1, 1, 0, 0, 0, 0)},
-      {L"en", L"1999-033", L"YYYY-JJJ", CFX_DateTime(1999, 1, 1, 0, 0, 0, 0)},
-      {L"de_CH", L"30. Oktober 2004", L"D. MMMM YYYY",
-       CFX_DateTime(2004, 10, 30, 0, 0, 0, 0)},
-      {L"fr_CA", L"30 octobre 2004", L"D MMMM YYYY",
-       CFX_DateTime(2004, 10, 30, 0, 0, 0, 0)},
-      {L"en", L"Saturday, the 1 of January, 2000",
-       L"EEEE, 'the' D 'of' MMMM, YYYY", CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
-      {L"en", L"Sat, the 1 of January, 2000", L"EEE, 'the' D 'of' MMMM, YYYY",
-       CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
-      {L"en", L"7, the 1 of January, 2000",  // 7 == Saturday as 1 == Sunday
-       L"E, 'the' D 'of' MMMM, YYYY", CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
-      {L"en", L"6, the 1 of January, 2000",  // 6 == Saturday as 1 == Monday
-       L"e, 'the' D 'of' MMMM, YYYY", CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
-      {L"en", L"2004-07-22 Week of the month is 3",
-       L"YYYY-MM-DD 'Week of the month is' w",
-       CFX_DateTime(2004, 7, 22, 0, 0, 0, 0)},
-      {L"en", L"2004-07-22 Week of the year is 03",
-       L"YYYY-MM-DD 'Week of the year is' WW",
-       CFX_DateTime(2004, 7, 22, 0, 0, 0, 0)}
-      // {L"ja", L"H15/11/3", L"gY/M/D", CFX_DateTime(2003, 11, 3, 0, 0, 0, 0)},
-      // {L"ja", L"\u5e731-1-8", L"ggY-M-D", CFX_DateTime(1989, 1, 8, 0, 0, 0,
-      // 0)}, {L"ja", L"\u5e73\u621089/11/03", L"gggYY/MM/DD",
-      //  CFX_DateTime(1989, 11, 3, 0, 0, 0, 0)},
-      // {L"ja", L"u337b99/01/08", L"\u0067\u0067YY/MM/DD",
-      //  CFX_DateTime(1999, 1, 8, 0, 0, 0, 0)}
-  };
-  // Note, none of the full width date symbols are listed here as they are
-  // not supported. In theory there are the full width versions of DDD,
-  // DDDD, MMM, MMMM, E, e, gg, YYY, YYYYY.
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    CFX_DateTime result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->ParseDateTime(tests[i].input, tests[i].pattern,
-                                    FX_DATETIMETYPE_Date, &result));
-    EXPECT_EQ(tests[i].output, result) << " TEST: " << i;
-  }
-}
-
-// TODO(dsinclair): GetDateTimeFormat is broken and doesn't allow just returning
-// a parsed Time. It will assume it's a Date. The method needs to be re-written.
-// TEST_F(CFGAS_FormatStringTest, TimeParse) {
-//   struct {
-//     const wchar_t* locale;
-//     const wchar_t* input;
-//     const wchar_t* pattern;
-//     CFX_DateTime output;
-//   } tests[] = {
-//       {L"en", L"18:00", L"HH:MM", CFX_DateTime(0, 0, 0, 18, 0, 0, 0)},
-//       {L"en", L"12.59 Uhr", L"H.MM 'Uhr'", CFX_DateTime(0, 0, 0, 12, 59, 0,
-//       0)}, {L"en", L"1:05:10 PM PST", L"h:MM:SS A Z",
-//        CFX_DateTime(0, 0, 0, 17, 05, 10, 0)}};
-//   // Note, none of the full width date symbols are listed here as they are
-//   // not supported. In theory there are the full width versions of kkk,
-//   // kkkk, HHH, HHHH, KKK, KKKK, MMM, MMMM, SSS, SSSS plus 2 more that the
-//   // spec apparently forgot to list the symbol.
-
-//   for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-//     CFX_DateTime result;
-//     EXPECT_TRUE(fmt(tests[i].locale)
-//                     ->ParseDateTime(tests[i].input, tests[i].pattern,
-//                                     FX_DATETIMETYPE_Time, &result));
-//     EXPECT_EQ(tests[i].output, result) << " TEST: " << i;
-//   }
-// }
-
-TEST_F(CFGAS_FormatStringTest, SplitFormatString) {
-  std::vector<WideString> results;
-  fmt(L"en")->SplitFormatString(
-      L"null{'No data'} | null{} | text{999*9999} | text{999*999*9999}",
-      &results);
-  EXPECT_EQ(4UL, results.size());
-
-  const wchar_t* patterns[] = {L"null{'No data'} ", L" null{} ",
-                               L" text{999*9999} ", L" text{999*999*9999}"};
-
-  for (size_t i = 0; i < results.size(); ++i) {
-    EXPECT_STREQ(patterns[i], results[i].c_str());
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, NumParse) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {
-      // {L"en", L"€100.00", L"num(en_GB){$z,zz9.99}", L"100"},
-      // {L"en", L"1050", L"99V99", L"10.50"},
-      // {L"en", L"3125", L"99V99", L"31.25"},
-      {L"en", L"12.345e3", L"99.999E", L"12345.000000"},
-      {L"en", L"12.345e+3", L"99.999E", L"12345.000000"},
-      {L"en", L"12.345E-2", L"99.999E", L"0.123450"},
-      // TODO(dsinclair): Returns 0.000?
-      // {L"en", L"12e-2", L"99E", L"0.12"},
-      {L"en", L"150", L"z999", L"150"},
-      {L"en", L"150.50$", L"zzz.zz$", L"150.50"},
-      {L"en", L"0150", L"z999", L"0150"},
-      {L"en", L"123CR", L"999cr", L"-123"},
-      {L"en", L"123", L"999cr", L"123"},
-      {L"en", L"123CR", L"999CR", L"-123"},
-      {L"en", L"123  ", L"999CR", L"123"},
-      {L"en", L"123DB", L"999db", L"-123"},
-      {L"en", L"123", L"999db", L"123"},
-      {L"en", L"123DB", L"999DB", L"-123"},
-      {L"en", L"123  ", L"999DB", L"123"},
-      {L"en", L"123.5CR", L"999.9cr", L"-123.5"},
-      {L"en", L"123.5", L"999.9cr", L"123.5"},
-      {L"en", L"123.5CR", L"999.9CR", L"-123.5"},
-      // {L"en", L"123.5  ", L"999.9CR", L"123.5"},
-      {L"en", L"123.5DB", L"999.9db", L"-123.5"},
-      {L"en", L"123.5", L"999.9db", L"123.5"},
-      {L"en", L"123.5DB", L"999.9DB", L"-123.5"},
-      // {L"en", L"123.5  ", L"999.9DB", L"123.5"},
-      {L"en", L"10.50", L"z,zz9.99", L"10.50"},
-      {L"en", L"3,125.00", L"z,zz9.99", L"3125.00"},
-      {L"en", L"$1,234.00", L"$z,zz9.99DB", L"1234.00"},
-      // TODO(dsinclair): Comes out as 1234 instead of -1234.
-      // {L"en", L"$,1234.00DB", L"$z,zz9.99DB", L"-1234.00"},
-      {L"en", L"1.234", L"zz9.zzz", L"1.234"},
-      {L"en", L"1 text", L"num{z 'text'}", L"1"},
-      {L"en", L"1.234 text", L"z.zzz 'text'", L"1.234"},
-      {L"en", L"  1.234", L"ZZ9.ZZZ", L"1.234"},
-      {L"en", L"12.345", L"zz9.zzz", L"12.345"},
-      {L"en", L" 12.345", L"ZZ9.ZZZ", L"12.345"},
-      {L"en", L"123.456", L"zz9.zzz", L"123.456"},
-      {L"en", L"123.456", L"ZZ9.ZZZ", L"123.456"},
-      {L"en", L"123.456-", L"ZZ9.ZZZS", L"-123.456"},
-      {L"en", L"123.456+", L"ZZ9.ZZZS", L"123.456"},
-      {L"en", L"123.456 ", L"ZZ9.ZZZS", L"123.456"},
-      {L"en", L"123.456-", L"ZZ9.ZZZS", L"-123.456"},
-      {L"en", L"123.456+", L"ZZ9.ZZZS", L"123.456"},
-      {L"en", L"123", L"zz9.zzz", L"123"},
-      {L"en", L"123.", L"ZZ9.ZZZ", L"123."},
-      {L"en", L"123.", L"zz9.zzz", L"123."},
-      {L"en", L"123.", L"ZZ9.ZZZ", L"123."},
-      {L"en", L"123.0", L"zz9.zzz", L"123.0"},
-      {L"en", L"123.0", L"ZZ9.ZZZ", L"123.0"},
-      {L"en", L"123.000", L"zz9.zzz", L"123.000"},
-      {L"en", L"123.000", L"ZZ9.ZZZ", L"123.000"},
-      {L"en", L"12,345.67", L"zzz,zz9.88888888", L"12345.67"},
-      {L"en", L"12,345.0000", L"zzz,zz9.88888888", L"12345.0000"},
-      {L"en", L"12,345.6789", L"zzz,zz9.8", L"12345.6789"},
-      {L"en", L"12,345.", L"zzz,zz9.8", L"12345."},
-      {L"en", L"123,456.000", L"zzz,zz9.8888", L"123456.000"},
-      {L"en", L"123,456.0", L"zzz,zz9.8888", L"123456.0"},
-      {L"en", L"123,456", L"zzz,zz9.8888", L"123456"},
-      {L"en", L"123,456", L"ZZZ,ZZ9.88", L"123456"},
-      {L"en", L"12,345.67", L"zzz,zz9.88888888", L"12345.67"},
-      {L"en", L"12,345.0000", L"zzz,zz9.88888888", L"12345.0000"},
-      {L"en", L"12,345.6789", L"zzz,zz9.8", L"12345.6789"},
-      {L"en", L"12,345.", L"zzz,zz9.8", L"12345."},
-      // TODO(dsinclair): Parses to 0
-      // {L"en", L"12%", L"zz9.%%", L".12"},
-      {L"en", L"1,234.50%", L"zzz,zz9.99%%", L"12.345"},
-      // {L"en", L"-00123", L"S999v99", L"-1.23"},
-      {L"en", L" 001.23", L"S999V99", L"001.23"},
-      // {L"en", L" 123.00", L"S999V99", L"123"},
-      {L"en", L"  12.30", L"SZZ9.99", L"12.30"},
-      {L"en", L"- 12.30", L"SZ99.99", L"-12.30"},
-      {L"en", L"123.00", L"szz9.99", L"123.00"},
-      {L"en", L"-123.00", L"szz9.99", L"-123.00"},
-      // {L"en", L"$  1,234.00  ", L"$ZZ,ZZ9.99CR", L"1234"},
-      // {L"en", L"$  1,234.00CR", L"$ZZ,ZZ9.99CR", L"-1234"},
-      // {L"en", L"$1,23400", L"$z,zz9.99DB", L"1234"},
-      {L"en", L"$1,234.00DB", L"$z,zz9.99DB", L"-1234.00"},
-      {L"en",
-       L"1\xA0"
-       L"234",
-       L"num(fr){z,zzz}", L"1234"},
-      // TODO(dsinclair): Parses to blank
-      // {L"en", L"1,234%", L"num.percent{}", L"12.34"},
-      // {L"en", L"1\xA0" L"234%%", L"num(fr).percent{}", L"12.34"},
-      // TODO(dsinclair): Parses to blank
-      // {L"en", L"1,234%", L"num{9,999%%}", L"12.34"},
-      {L"fr",
-       L"123\xA0"
-       L"456",
-       L"zzz,zzz", L"123456"},
-      {L"en", L"12%", L"zz%", L"0.12"},
-      {L"en", L"(123", L"(zzz", L"-123"},
-      {L"en", L"123)", L"zzz)", L"-123"},
-      {L"en", L"(123)", L"(zzz)", L"-123"},
-      {L"en", L"123 ", L"zzz)", L"123"},
-      {L"en", L" 123", L"(zzz", L"123"},
-      {L"en", L" 123 ", L"(zzz)", L"123"},
-      {L"en", L"123.5(", L"zzz.z(", L"-123.5"},
-      {L"en", L"123.5)", L"zzz.z)", L"-123.5"},
-      {L"en", L"123.5 ", L"zzz.z)", L"123.5"},
-      {L"en", L"123.5 ", L"zzz.z(", L"123.5"},
-      {L"en", L"123.545,4", L"zzz.zzz,z", L"123.5454"},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->ParseNum(tests[i].input, tests[i].pattern, &result))
-        << " TEST: " << i;
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, NumFormat) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {
-      {L"en", L"1.234", L"zz9.zzz", L"1.234"},
-      {L"en", L"1", L"num{z 'text'}", L"1 text"},
-      {L"en", L"1", L"num{'text' z}", L"text 1"},
-      {L"en", L"1.234", L"ZZ9.ZZZ", L"  1.234"},
-      {L"en", L"12.345", L"zz9.zzz", L"12.345"},
-      {L"en", L"12.345", L"ZZ9.ZZZ", L" 12.345"},
-      {L"en", L"123.456", L"zz9.zzz", L"123.456"},
-      {L"en", L"123.456", L"ZZ9.ZZZ", L"123.456"},
-      {L"en", L"123", L"zz9.zzz", L"123"},
-      {L"en", L"123", L"ZZ9.ZZZ", L"123.000"},
-      {L"en", L"123.", L"zz9.zzz", L"123."},
-      {L"en", L"123.", L"ZZ9.ZZZ", L"123.000"},
-      {L"en", L"123.0", L"zz9.zzz", L"123"},
-      {L"en", L"123.0", L"ZZ9.ZZZ", L"123.000"},
-      {L"en", L"123.000", L"zz9.zzz", L"123"},
-      {L"en", L"123.000", L"ZZ9.ZZZ", L"123.000"},
-      // {L"en", L"12345.67", L"zzz,zz9.88888888", L"12,345.67"},
-      // {L"en", L"12345.0000", L"zzz,zz9.88888888", L"12,345.0000"},
-      // {L"en", L"12345.6789", L"zzz,zz9.8", L"12,345.6789"},
-      // {L"en", L"12345.", L"zzz,zz9.8", L"12,345"},
-      // {L"en", L"123456.000", L"zzz,zz9.8888", L"123,456.000"},
-      // {L"en", L"123456.0", L"zzz,zz9.8888", L"123,456.0"},
-      {L"en", L"123456", L"zzz,zz9.8888", L"123,456"},
-      {L"en", L"123456", L"ZZZ,ZZ9.88", L"123,456"},
-      // {L"en", L"12345.67", L"zzz,zz9.88888888", L"12,345.67"},
-      // {L"en", L"12345.0000", L"zzz,zz9.88888888", L"12,345.0000"},
-      // {L"en", L"12345.6789", L"zzz,zz9.8", L"12,345.6789"},
-      // {L"en", L"12345.", L"zzz,zz9.8", L"12,345"},
-      // {L"en", L"12%%", L"zz9.%%", L"12%%"},
-      // {L"en", L"1,234.5%%", L"zzz,zz9.99%%", L"1,234.50%%"},
-      {L"en", L"-1.23", L"S999v99", L"-00123"},
-      {L"en", L"1.23", L"S999V99", L" 001.23"},
-      {L"en", L"123", L"S999V99", L" 123.00"},
-      {L"en", L"12.3", L"SZZ9.99", L"  12.30"},
-      {L"en", L"-12.3", L"SZ99.99", L"- 12.30"},
-      {L"en", L"123", L"szz9.99", L"123.00"},
-      {L"en", L"-123", L"szz9.99", L"-123.00"},
-      // {L"en", L"1234", L"$ZZ,ZZ9.99CR", L"$  1,234.00  "},
-      // {L"en", L"-1234", L"$ZZ,ZZ9.99CR", L"$  1,234.00CR"},
-      // {L"en", L"1234", L"$z,zz9.99DB", L"$1,234.00"},
-      {L"en", L"-1234", L"$z,zz9.99DB", L"$1,234.00DB"},
-      {L"en", L"12345", L"99.999E", L"12.345E+3"},
-      {L"en", L"12345", L"99999E", L"12345E+0"},
-      {L"en", L".12345", L"99.999E", L"12.345E-2"},
-      {L"en", L"12345", L"99,999", L"12,345"},
-      {L"en", L"1234", L"num(fr){z,zzz}",
-       L"1\xA0"
-       L"234"},
-      {L"en", L"12.34", L"num.percent{}", L"1,234%"},
-      {L"en", L"12.34", L"num(fr).percent{}",
-       L"1\xA0"
-       L"234%"},
-      // {L"en", L"12.34", L"num{9,999%%}", L"1,234%"},
-      {L"en", L"-123", L"zzzCR", L"123CR"},
-      {L"en", L"123", L"zzzCR", L"123  "},
-      {L"en", L"-123", L"zzzcr", L"123CR"},
-      {L"en", L"123", L"zzzcr", L"123"},
-      {L"en", L"123", L"zzz$", L"123$"},
-      {L"en", L"-123.5", L"zzz.zCR", L"123.5CR"},
-      {L"en", L"123.5", L"zzz.zCR", L"123.5  "},
-      {L"en", L"-123.5", L"zzz.zcr", L"123.5CR"},
-      {L"en", L"123.5", L"zzz.zcr", L"123.5"},
-
-      {L"en", L"-123.5", L"999.9db", L"123.5db"},
-      {L"en", L"123.5", L"999.9db", L"123.5"},
-      {L"en", L"-123.5", L"999.9DB", L"123.5DB"},
-      {L"en", L"123.5", L"999.9DB", L"123.5  "},
-
-      {L"en", L"-123", L"(zzz", L"(123"},
-      // {L"en", L"-123", L"zzz)", L"123)"},
-      {L"en", L"-123", L"(zzz)", L"(123)"},
-      {L"en", L"123", L"zzz)", L"123 "},
-      {L"en", L"123", L"(zzz", L" 123"},
-      {L"en", L"123", L"(zzz)", L" 123 "},
-      {L"en", L"-123.5", L"zzz.z(", L"123.5("},
-      // {L"en", L"-123.5", L"zzz.z)", L"123.5)"},
-      {L"en", L"123.5", L"zzz.z)", L"123.5 "},
-      {L"en", L"123.5", L"zzz.z(", L"123.5 "},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->FormatNum(tests[i].input, tests[i].pattern, &result))
-        << " TEST: " << i;
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, TextParse) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {// TODO(dsinclair) Missing support for the global modifiers:
-               //  ? - wildcard
-               //  * - zero or more whitespace
-               //  + - one or more whitespace
-               // {L"en", L"555-1212", L"text(th_TH){999*9999}", L"5551212"},
-               {L"en", L"ABC-1234-5", L"AAA-9999-X", L"ABC12345"},
-               {L"en", L"ABC-1234-D", L"AAA-9999-X", L"ABC1234D"},
-               {L"en", L"A1C-1234-D", L"OOO-9999-X", L"A1C1234D"},
-               {L"en", L"A1C-1234-D", L"000-9999-X", L"A1C1234D"},
-               {L"en", L"A1C-1234-D text", L"000-9999-X 'text'", L"A1C1234D"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->ParseText(tests[i].input, tests[i].pattern, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, InvalidTextParse) {
-  // Input does not match mask.
-  WideString result;
-  EXPECT_FALSE(fmt(L"en")->ParseText(L"123-4567-8", L"AAA-9999-X", &result));
-}
-
-TEST_F(CFGAS_FormatStringTest, TextFormat) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {
-      {L"en", L"K1S5K2", L"A9A 9A9", L"K1S 5K2"},
-      {L"en", L"K1S5K2", L"text(fr){A9A 9A9}", L"K1S 5K2"},
-      {L"en", L"6135551212", L"'+1 ('9\u002399') '999-9999",
-       L"+1 (6#13) 555-1212"},
-      {L"en", L"6135551212", L"999.999.9999", L"613.555.1212"},
-      {L"en", L"6135551212", L"999\u0023999\u002A9999", L"613#555*1212"},
-      {L"en", L"K1#5K2", L"00X OO9", L"K1# 5K2"},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)
-                    ->FormatText(tests[i].input, tests[i].pattern, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, NullParse) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-  } tests[] = {
-      {L"en", L"", L"null{}"}, {L"en", L"No data", L"null{'No data'}"},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(
-        fmt(tests[i].locale)->ParseNull(tests[i].input, tests[i].pattern))
-        << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, NullFormat) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {{L"en", L"null{'n/a'}", L"n/a"}, {L"en", L"null{}", L""}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(fmt(tests[i].locale)->FormatNull(tests[i].pattern, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, ZeroParse) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-  } tests[] = {{L"en", L"", L"zero{}"},
-               {L"en", L"9", L"zero{9}"},
-               {L"en", L"a", L"zero{'a'}"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(
-        fmt(tests[i].locale)->ParseZero(tests[i].input, tests[i].pattern))
-        << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, ZeroFormat) {
-  struct {
-    const wchar_t* locale;
-    const wchar_t* input;
-    const wchar_t* pattern;
-    const wchar_t* output;
-  } tests[] = {// TODO(dsinclair): The zero format can take a number specifier
-               // which we don't take into account.
-               // {L"en", L"", L"zero {9}", L""},
-               // {L"en", L"0", L"zero {9}", L"0"},
-               // {L"en", L"0.0", L"zero{9}", L"0"},
-               {L"en", L"0", L"zero{}", L""}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    WideString result;
-    EXPECT_TRUE(
-        fmt(tests[i].locale)
-            ->FormatZero(/* tests[i].input,*/ tests[i].pattern, &result));
-    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
-  }
-}
-
-TEST_F(CFGAS_FormatStringTest, GetCategory) {
-  CFGAS_FormatString* f = fmt(L"en");
-
-  EXPECT_EQ(FX_LOCALECATEGORY_Unknown, f->GetCategory(L"'just text'"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Null, f->GetCategory(L"null{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Zero, f->GetCategory(L"zero{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Num, f->GetCategory(L"num{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Text, f->GetCategory(L"text{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_DateTime, f->GetCategory(L"datetime{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Time, f->GetCategory(L"time{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Date, f->GetCategory(L"date{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_DateTime, f->GetCategory(L"time{} date{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_DateTime, f->GetCategory(L"date{} time{}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Num, f->GetCategory(L"num(en_GB){}"));
-  EXPECT_EQ(FX_LOCALECATEGORY_Date, f->GetCategory(L"date.long{}"));
-}
diff --git a/xfa/fgas/crt/cfgas_stringformatter.cpp b/xfa/fgas/crt/cfgas_stringformatter.cpp
new file mode 100644
index 0000000..1c640b9
--- /dev/null
+++ b/xfa/fgas/crt/cfgas_stringformatter.cpp
@@ -0,0 +1,2265 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/crt/cfgas_stringformatter.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fgas/crt/cfgas_decimal.h"
+
+// NOTE: Code uses the convention for backwards-looping with unsigned types
+// that exploits the well-defined behaviour for unsigned underflow (and hence
+// the standard x < size() can be used in all cases to validate indices).
+
+#define FX_LOCALECATEGORY_DateHash 0xbde9abde
+#define FX_LOCALECATEGORY_TimeHash 0x2d71b00f
+#define FX_LOCALECATEGORY_DateTimeHash 0x158c72ed
+#define FX_LOCALECATEGORY_NumHash 0x0b4ff870
+#define FX_LOCALECATEGORY_TextHash 0x2d08af85
+#define FX_LOCALECATEGORY_ZeroHash 0x568cb500
+#define FX_LOCALECATEGORY_NullHash 0x052931bb
+
+#define FX_NUMSTYLE_Percent 0x01
+#define FX_NUMSTYLE_Exponent 0x02
+#define FX_NUMSTYLE_DotVorv 0x04
+
+namespace {
+
+struct LocaleDateTimeSubCategoryWithHash {
+  uint32_t uHash;  // Hashed as wide string.
+  FX_LOCALEDATETIMESUBCATEGORY eSubCategory;
+};
+
+struct LocaleNumberSubCategoryWithHash {
+  uint32_t uHash;  // Hashed as wide string.
+  FX_LOCALENUMSUBCATEGORY eSubCategory;
+};
+
+#undef SUBC
+#define SUBC(a, b, c) a, c
+
+const LocaleDateTimeSubCategoryWithHash g_FXLocaleDateTimeSubCatData[] = {
+    {SUBC(0x14da2125, "default", FX_LOCALEDATETIMESUBCATEGORY_Default)},
+    {SUBC(0x9041d4b0, "short", FX_LOCALEDATETIMESUBCATEGORY_Short)},
+    {SUBC(0xa084a381, "medium", FX_LOCALEDATETIMESUBCATEGORY_Medium)},
+    {SUBC(0xcdce56b3, "full", FX_LOCALEDATETIMESUBCATEGORY_Full)},
+    {SUBC(0xf6b4afb0, "long", FX_LOCALEDATETIMESUBCATEGORY_Long)},
+};
+
+const LocaleNumberSubCategoryWithHash g_FXLocaleNumSubCatData[] = {
+    {SUBC(0x46f95531, "percent", FX_LOCALENUMPATTERN_Percent)},
+    {SUBC(0x4c4e8acb, "currency", FX_LOCALENUMPATTERN_Currency)},
+    {SUBC(0x54034c2f, "decimal", FX_LOCALENUMPATTERN_Decimal)},
+    {SUBC(0x7568e6ae, "integer", FX_LOCALENUMPATTERN_Integer)},
+};
+
+#undef SUBC
+
+struct FX_LOCALETIMEZONEINFO {
+  const wchar_t* name;
+  int16_t iHour;
+  int16_t iMinute;
+};
+
+const FX_LOCALETIMEZONEINFO g_FXLocaleTimeZoneData[] = {
+    {L"CDT", -5, 0}, {L"CST", -6, 0}, {L"EDT", -4, 0}, {L"EST", -5, 0},
+    {L"MDT", -6, 0}, {L"MST", -7, 0}, {L"PDT", -7, 0}, {L"PST", -8, 0},
+};
+
+const wchar_t kTimeSymbols[] = L"hHkKMSFAzZ";
+const wchar_t kDateSymbols[] = L"DJMEeGgYwW";
+const wchar_t kConstChars[] = L",-:/. ";
+
+size_t ParseTimeZone(pdfium::span<const wchar_t> spStr, FX_TIMEZONE* tz) {
+  tz->tzHour = 0;
+  tz->tzMinute = 0;
+  if (spStr.empty())
+    return 0;
+
+  // Keep index by 0 close to empty() check above for optimizer's sake.
+  const bool bNegative = (spStr[0] == '-');
+
+  size_t iStart = 1;
+  size_t iEnd = iStart + 2;
+  while (iStart < spStr.size() && iStart < iEnd)
+    tz->tzHour = tz->tzHour * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
+
+  if (iStart < spStr.size() && spStr[iStart] == ':')
+    iStart++;
+
+  iEnd = iStart + 2;
+  while (iStart < spStr.size() && iStart < iEnd)
+    tz->tzMinute = tz->tzMinute * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
+
+  if (bNegative)
+    tz->tzHour = -tz->tzHour;
+
+  return iStart;
+}
+
+int32_t ConvertHex(int32_t iKeyValue, wchar_t ch) {
+  if (FXSYS_IsHexDigit(ch))
+    return iKeyValue * 16 + FXSYS_HexCharToInt(ch);
+  return iKeyValue;
+}
+
+WideString GetLiteralText(pdfium::span<const wchar_t> spStrPattern,
+                          size_t* iPattern) {
+  WideString wsOutput;
+  if (*iPattern >= spStrPattern.size() || spStrPattern[*iPattern] != '\'')
+    return wsOutput;
+
+  (*iPattern)++;
+  int32_t iQuote = 1;
+  while (*iPattern < spStrPattern.size()) {
+    if (spStrPattern[*iPattern] == '\'') {
+      iQuote++;
+      if ((*iPattern + 1 >= spStrPattern.size()) ||
+          ((spStrPattern[*iPattern + 1] != '\'') && (iQuote % 2 == 0))) {
+        break;
+      }
+      iQuote++;
+      (*iPattern)++;
+    } else if (spStrPattern[*iPattern] == '\\' &&
+               (*iPattern + 1 < spStrPattern.size()) &&
+               spStrPattern[*iPattern + 1] == 'u') {
+      int32_t iKeyValue = 0;
+      *iPattern += 2;
+      for (int32_t i = 0; *iPattern < spStrPattern.size() && i < 4; ++i) {
+        wchar_t ch = spStrPattern[(*iPattern)++];
+        iKeyValue = ConvertHex(iKeyValue, ch);
+      }
+      if (iKeyValue != 0)
+        wsOutput += static_cast<wchar_t>(iKeyValue & 0x0000FFFF);
+
+      continue;
+    }
+    wsOutput += spStrPattern[(*iPattern)++];
+  }
+  return wsOutput;
+}
+
+WideString GetLiteralTextReverse(pdfium::span<const wchar_t> spStrPattern,
+                                 size_t* iPattern) {
+  WideString wsOutput;
+  if (*iPattern >= spStrPattern.size() || spStrPattern[*iPattern] != '\'')
+    return wsOutput;
+
+  (*iPattern)--;
+  int32_t iQuote = 1;
+
+  while (*iPattern < spStrPattern.size()) {
+    if (spStrPattern[*iPattern] == '\'') {
+      iQuote++;
+      if (*iPattern - 1 >= spStrPattern.size() ||
+          ((spStrPattern[*iPattern - 1] != '\'') && (iQuote % 2 == 0))) {
+        break;
+      }
+      iQuote++;
+      (*iPattern)--;
+    } else if (spStrPattern[*iPattern] == '\\' &&
+               *iPattern + 1 < spStrPattern.size() &&
+               spStrPattern[*iPattern + 1] == 'u') {
+      (*iPattern)--;
+      int32_t iKeyValue = 0;
+      int32_t iLen = wsOutput.GetLength();
+      int32_t i = 1;
+      for (; i < iLen && i < 5; i++) {
+        wchar_t ch = wsOutput[i];
+        iKeyValue = ConvertHex(iKeyValue, ch);
+      }
+      if (iKeyValue != 0) {
+        wsOutput.Delete(0, i);
+        wsOutput = (wchar_t)(iKeyValue & 0x0000FFFF) + wsOutput;
+      }
+      continue;
+    }
+    wsOutput = spStrPattern[(*iPattern)--] + wsOutput;
+  }
+  return wsOutput;
+}
+
+bool GetNumericDotIndex(const WideString& wsNum,
+                        const WideString& wsDotSymbol,
+                        size_t* iDotIndex) {
+  pdfium::span<const wchar_t> spNum = wsNum.span();
+  pdfium::span<const wchar_t> spDotSymbol = wsDotSymbol.span();
+  for (size_t ccf = 0; ccf < spNum.size(); ++ccf) {
+    if (spNum[ccf] == '\'') {
+      GetLiteralText(spNum, &ccf);
+      continue;
+    }
+    if (ccf + spDotSymbol.size() <= spNum.size() &&
+        wcsncmp(&spNum[ccf], spDotSymbol.data(), spDotSymbol.size()) == 0) {
+      *iDotIndex = ccf;
+      return true;
+    }
+  }
+  auto result = wsNum.Find('.');
+  *iDotIndex = result.value_or(spNum.size());
+  return result.has_value();
+}
+
+bool ExtractCountDigits(pdfium::span<const wchar_t> spStr,
+                        size_t count,
+                        size_t* cc,
+                        uint32_t* value) {
+  for (size_t i = 0; i < count; ++i) {
+    if (*cc >= spStr.size() || !FXSYS_IsDecimalDigit(spStr[*cc]))
+      return false;
+    *value = *value * 10 + FXSYS_DecimalCharToInt(spStr[(*cc)++]);
+  }
+  return true;
+}
+
+bool ExtractCountDigitsWithOptional(pdfium::span<const wchar_t> spStr,
+                                    int count,
+                                    size_t* cc,
+                                    uint32_t* value) {
+  if (!ExtractCountDigits(spStr, count, cc, value))
+    return false;
+  ExtractCountDigits(spStr, 1, cc, value);
+  return true;
+}
+
+bool ParseLocaleDate(const WideString& wsDate,
+                     const WideString& wsDatePattern,
+                     LocaleIface* pLocale,
+                     CFX_DateTime* datetime,
+                     size_t* cc) {
+  uint32_t year = 1900;
+  uint32_t month = 1;
+  uint32_t day = 1;
+  size_t ccf = 0;
+  pdfium::span<const wchar_t> spDate = wsDate.span();
+  pdfium::span<const wchar_t> spDatePattern = wsDatePattern.span();
+  while (*cc < spDate.size() && ccf < spDatePattern.size()) {
+    if (spDatePattern[ccf] == '\'') {
+      WideString wsLiteral = GetLiteralText(spDatePattern, &ccf);
+      int32_t iLiteralLen = wsLiteral.GetLength();
+      if (*cc + iLiteralLen > spDate.size() ||
+          wcsncmp(spDate.data() + *cc, wsLiteral.c_str(), iLiteralLen) != 0) {
+        return false;
+      }
+      *cc += iLiteralLen;
+      ccf++;
+      continue;
+    }
+    if (!pdfium::ContainsValue(kDateSymbols, spDatePattern[ccf])) {
+      if (spDatePattern[ccf] != spDate[*cc])
+        return false;
+      (*cc)++;
+      ccf++;
+      continue;
+    }
+
+    WideString symbol;
+    symbol.Reserve(4);
+    symbol += spDatePattern[ccf++];
+    while (ccf < spDatePattern.size() && spDatePattern[ccf] == symbol[0]) {
+      symbol += spDatePattern[ccf++];
+    }
+    if (symbol.EqualsASCII("D") || symbol.EqualsASCII("DD")) {
+      day = 0;
+      if (!ExtractCountDigitsWithOptional(spDate, 1, cc, &day))
+        return false;
+    } else if (symbol.EqualsASCII("J")) {
+      uint32_t val = 0;
+      ExtractCountDigits(spDate, 3, cc, &val);
+    } else if (symbol.EqualsASCII("M") || symbol.EqualsASCII("MM")) {
+      month = 0;
+      if (!ExtractCountDigitsWithOptional(spDate, 1, cc, &month))
+        return false;
+    } else if (symbol.EqualsASCII("MMM") || symbol.EqualsASCII("MMMM")) {
+      for (uint16_t i = 0; i < 12; i++) {
+        WideString wsMonthName =
+            pLocale->GetMonthName(i, symbol.EqualsASCII("MMM"));
+        if (wsMonthName.IsEmpty())
+          continue;
+        if (wcsncmp(wsMonthName.c_str(), spDate.data() + *cc,
+                    wsMonthName.GetLength()) == 0) {
+          *cc += wsMonthName.GetLength();
+          month = i + 1;
+          break;
+        }
+      }
+    } else if (symbol.EqualsASCII("EEE") || symbol.EqualsASCII("EEEE")) {
+      for (uint16_t i = 0; i < 7; i++) {
+        WideString wsDayName =
+            pLocale->GetDayName(i, symbol.EqualsASCII("EEE"));
+        if (wsDayName.IsEmpty())
+          continue;
+        if (wcsncmp(wsDayName.c_str(), spDate.data() + *cc,
+                    wsDayName.GetLength()) == 0) {
+          *cc += wsDayName.GetLength();
+          break;
+        }
+      }
+    } else if (symbol.EqualsASCII("YY") || symbol.EqualsASCII("YYYY")) {
+      if (*cc + symbol.GetLength() > spDate.size())
+        return false;
+
+      year = 0;
+      if (!ExtractCountDigits(spDate, symbol.GetLength(), cc, &year))
+        return false;
+      if (symbol.EqualsASCII("YY")) {
+        if (year <= 29)
+          year += 2000;
+        else
+          year += 1900;
+      }
+    } else if (symbol.EqualsASCII("G")) {
+      *cc += 2;
+    } else if (symbol.EqualsASCII("JJJ") || symbol.EqualsASCIINoCase("E") ||
+               symbol.EqualsASCII("w") || symbol.EqualsASCII("WW")) {
+      *cc += symbol.GetLength();
+    }
+  }
+  if (*cc < spDate.size())
+    return false;
+
+  datetime->SetDate(year, month, day);
+  return !!(*cc);
+}
+
+void ResolveZone(FX_TIMEZONE tzDiff,
+                 const LocaleIface* pLocale,
+                 uint32_t* wHour,
+                 uint32_t* wMinute) {
+  int32_t iMinuteDiff = *wHour * 60 + *wMinute;
+  FX_TIMEZONE tzLocale = pLocale->GetTimeZone();
+  iMinuteDiff += tzLocale.tzHour * 60 +
+                 (tzLocale.tzHour < 0 ? -tzLocale.tzMinute : tzLocale.tzMinute);
+  iMinuteDiff -= tzDiff.tzHour * 60 +
+                 (tzDiff.tzHour < 0 ? -tzDiff.tzMinute : tzDiff.tzMinute);
+
+  iMinuteDiff %= 1440;
+  if (iMinuteDiff < 0)
+    iMinuteDiff += 1440;
+
+  *wHour = iMinuteDiff / 60;
+  *wMinute = iMinuteDiff % 60;
+}
+
+bool ParseLocaleTime(const WideString& wsTime,
+                     const WideString& wsTimePattern,
+                     LocaleIface* pLocale,
+                     CFX_DateTime* datetime,
+                     size_t* cc) {
+  uint32_t hour = 0;
+  uint32_t minute = 0;
+  uint32_t second = 0;
+  uint32_t millisecond = 0;
+  size_t ccf = 0;
+  pdfium::span<const wchar_t> spTime = wsTime.span();
+  pdfium::span<const wchar_t> spTimePattern = wsTimePattern.span();
+  bool bHasA = false;
+  bool bPM = false;
+  while (*cc < spTime.size() && ccf < spTimePattern.size()) {
+    if (spTimePattern[ccf] == '\'') {
+      WideString wsLiteral = GetLiteralText(spTimePattern, &ccf);
+      int32_t iLiteralLen = wsLiteral.GetLength();
+      if (*cc + iLiteralLen > spTime.size() ||
+          wcsncmp(spTime.data() + *cc, wsLiteral.c_str(), iLiteralLen) != 0) {
+        return false;
+      }
+      *cc += iLiteralLen;
+      ccf++;
+      continue;
+    }
+    if (!pdfium::ContainsValue(kTimeSymbols, spTimePattern[ccf])) {
+      if (spTimePattern[ccf] != spTime[*cc])
+        return false;
+      (*cc)++;
+      ccf++;
+      continue;
+    }
+
+    WideString symbol;
+    symbol.Reserve(4);
+    symbol += spTimePattern[ccf++];
+    while (ccf < spTimePattern.size() && spTimePattern[ccf] == symbol[0])
+      symbol += spTimePattern[ccf++];
+
+    if (symbol.EqualsASCIINoCase("k") || symbol.EqualsASCIINoCase("h")) {
+      hour = 0;
+      if (!ExtractCountDigitsWithOptional(spTime, 1, cc, &hour))
+        return false;
+      if (symbol.EqualsASCII("K") && hour == 24)
+        hour = 0;
+    } else if (symbol.EqualsASCIINoCase("kk") ||
+               symbol.EqualsASCIINoCase("hh")) {
+      hour = 0;
+      if (!ExtractCountDigits(spTime, 2, cc, &hour))
+        return false;
+      if (symbol.EqualsASCII("KK") && hour == 24)
+        hour = 0;
+    } else if (symbol.EqualsASCII("M")) {
+      minute = 0;
+      if (!ExtractCountDigitsWithOptional(spTime, 1, cc, &minute))
+        return false;
+    } else if (symbol.EqualsASCII("MM")) {
+      minute = 0;
+      if (!ExtractCountDigits(spTime, 2, cc, &minute))
+        return false;
+    } else if (symbol.EqualsASCII("S")) {
+      second = 0;
+      if (!ExtractCountDigitsWithOptional(spTime, 1, cc, &second))
+        return false;
+    } else if (symbol.EqualsASCII("SS")) {
+      second = 0;
+      if (!ExtractCountDigits(spTime, 2, cc, &second))
+        return false;
+    } else if (symbol.EqualsASCII("FFF")) {
+      millisecond = 0;
+      if (!ExtractCountDigits(spTime, 3, cc, &millisecond))
+        return false;
+    } else if (symbol.EqualsASCII("A")) {
+      WideString wsAM = pLocale->GetMeridiemName(true);
+      WideString wsPM = pLocale->GetMeridiemName(false);
+      if (*cc + wsAM.GetLength() <= spTime.size() &&
+          WideStringView(spTime.data() + *cc, wsAM.GetLength()) == wsAM) {
+        *cc += wsAM.GetLength();
+        bHasA = true;
+      } else if (*cc + wsPM.GetLength() <= spTime.size() &&
+                 WideStringView(spTime.data() + *cc, wsPM.GetLength()) ==
+                     wsPM) {
+        *cc += wsPM.GetLength();
+        bHasA = true;
+        bPM = true;
+      }
+    } else if (symbol.EqualsASCII("Z")) {
+      if (*cc + 3 > spTime.size())
+        continue;
+
+      WideString tz(spTime[(*cc)++]);
+      tz += spTime[(*cc)++];
+      tz += spTime[(*cc)++];
+      if (tz.EqualsASCII("GMT")) {
+        FX_TIMEZONE tzDiff;
+        tzDiff.tzHour = 0;
+        tzDiff.tzMinute = 0;
+        if (*cc < spTime.size() && (spTime[*cc] == '-' || spTime[*cc] == '+')) {
+          *cc += ParseTimeZone(spTime.subspan(*cc), &tzDiff);
+        }
+        ResolveZone(tzDiff, pLocale, &hour, &minute);
+      } else {
+        // Search the timezone list. There are only 8 of them, so linear scan.
+        for (size_t i = 0; i < FX_ArraySize(g_FXLocaleTimeZoneData); ++i) {
+          const FX_LOCALETIMEZONEINFO& info = g_FXLocaleTimeZoneData[i];
+          if (tz != info.name)
+            continue;
+
+          hour += info.iHour;
+          minute += info.iHour > 0 ? info.iMinute : -info.iMinute;
+          break;
+        }
+      }
+    } else if (symbol.EqualsASCII("z")) {
+      if (spTime[*cc] != 'Z') {
+        FX_TIMEZONE tzDiff;
+        *cc += ParseTimeZone(spTime.subspan(*cc), &tzDiff);
+        ResolveZone(tzDiff, pLocale, &hour, &minute);
+      } else {
+        (*cc)++;
+      }
+    }
+  }
+  if (bHasA) {
+    if (bPM) {
+      hour += 12;
+      if (hour == 24)
+        hour = 12;
+    } else {
+      if (hour == 12)
+        hour = 0;
+    }
+  }
+  datetime->SetTime(hour, minute, second, millisecond);
+  return !!(*cc);
+}
+
+size_t GetNumTrailingLimit(const WideString& wsFormat,
+                           size_t iDotPos,
+                           bool* bTrimTailZeros) {
+  const pdfium::span<const wchar_t> spFormat = wsFormat.span();
+  size_t iTrailing = 0;
+  for (++iDotPos; iDotPos < spFormat.size(); ++iDotPos) {
+    wchar_t wc = spFormat[iDotPos];
+    if (wc == L'z' || wc == L'9' || wc == 'Z') {
+      iTrailing++;
+      *bTrimTailZeros = wc != L'9';
+    }
+  }
+  return iTrailing;
+}
+
+bool IsLeapYear(uint32_t year) {
+  return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
+}
+
+bool MonthHas30Days(uint32_t month) {
+  return month == 4 || month == 6 || month == 9 || month == 11;
+}
+
+bool MonthHas31Days(uint32_t month) {
+  return month != 2 && !MonthHas30Days(month);
+}
+
+// |month| is 1-based. e.g. 1 means January.
+uint16_t GetSolarMonthDays(uint16_t year, uint16_t month) {
+  if (month == 2)
+    return FX_IsLeapYear(year) ? 29 : 28;
+
+  return MonthHas30Days(month) ? 30 : 31;
+}
+
+uint16_t GetWeekDay(uint16_t year, uint16_t month, uint16_t day) {
+  static const uint8_t kMonthDay[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
+  uint16_t nDays =
+      (year - 1) % 7 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
+  nDays += kMonthDay[month - 1] + day;
+  if (FX_IsLeapYear(year) && month > 2)
+    nDays++;
+  return nDays % 7;
+}
+
+uint16_t GetWeekOfMonth(uint16_t year, uint16_t month, uint16_t day) {
+  uint16_t week_day = GetWeekDay(year, month, 1);
+  uint16_t week_index = 0;
+  week_index += day / 7;
+  day = day % 7;
+  if (week_day + day > 7)
+    week_index++;
+  return week_index;
+}
+
+uint16_t GetWeekOfYear(uint16_t year, uint16_t month, uint16_t day) {
+  uint16_t nDays = 0;
+  for (uint16_t i = 1; i < month; i++)
+    nDays += GetSolarMonthDays(year, i);
+
+  nDays += day;
+  uint16_t week_day = GetWeekDay(year, 1, 1);
+  uint16_t week_index = 1;
+  week_index += nDays / 7;
+  nDays = nDays % 7;
+  if (week_day + nDays > 7)
+    week_index++;
+  return week_index;
+}
+
+WideString NumToString(size_t fmt_size, int32_t value) {
+  return WideString::Format(
+      fmt_size == 1 ? L"%d" : fmt_size == 2 ? L"%02d" : L"%03d", value);
+}
+
+WideString DateFormat(const WideString& wsDatePattern,
+                      LocaleIface* pLocale,
+                      const CFX_DateTime& datetime) {
+  WideString wsResult;
+  int32_t year = datetime.GetYear();
+  uint8_t month = datetime.GetMonth();
+  uint8_t day = datetime.GetDay();
+  size_t ccf = 0;
+  pdfium::span<const wchar_t> spDatePattern = wsDatePattern.span();
+  while (ccf < spDatePattern.size()) {
+    if (spDatePattern[ccf] == '\'') {
+      wsResult += GetLiteralText(spDatePattern, &ccf);
+      ccf++;
+      continue;
+    }
+    if (!pdfium::ContainsValue(kDateSymbols, spDatePattern[ccf])) {
+      wsResult += spDatePattern[ccf++];
+      continue;
+    }
+    WideString symbol;
+    symbol.Reserve(4);
+    symbol += spDatePattern[ccf++];
+    while (ccf < spDatePattern.size() && spDatePattern[ccf] == symbol[0])
+      symbol += spDatePattern[ccf++];
+
+    if (symbol.EqualsASCII("D") || symbol.EqualsASCII("DD")) {
+      wsResult += NumToString(symbol.GetLength(), day);
+    } else if (symbol.EqualsASCII("J") || symbol.EqualsASCII("JJJ")) {
+      uint16_t nDays = 0;
+      for (int i = 1; i < month; i++)
+        nDays += GetSolarMonthDays(year, i);
+      nDays += day;
+      wsResult += NumToString(symbol.GetLength(), nDays);
+    } else if (symbol.EqualsASCII("M") || symbol.EqualsASCII("MM")) {
+      wsResult += NumToString(symbol.GetLength(), month);
+    } else if (symbol.EqualsASCII("MMM") || symbol.EqualsASCII("MMMM")) {
+      wsResult += pLocale->GetMonthName(month - 1, symbol.EqualsASCII("MMM"));
+    } else if (symbol.EqualsASCIINoCase("e")) {
+      uint16_t wWeekDay = GetWeekDay(year, month, day);
+      wsResult +=
+          NumToString(1, symbol.EqualsASCII("E") ? wWeekDay + 1
+                                                 : (wWeekDay ? wWeekDay : 7));
+    } else if (symbol.EqualsASCII("EEE") || symbol.EqualsASCII("EEEE")) {
+      wsResult += pLocale->GetDayName(GetWeekDay(year, month, day),
+                                      symbol.EqualsASCII("EEE"));
+    } else if (symbol.EqualsASCII("G")) {
+      wsResult += pLocale->GetEraName(year > 0);
+    } else if (symbol.EqualsASCII("YY")) {
+      wsResult += NumToString(2, year % 100);
+    } else if (symbol.EqualsASCII("YYYY")) {
+      wsResult += NumToString(1, year);
+    } else if (symbol.EqualsASCII("w")) {
+      wsResult += NumToString(1, GetWeekOfMonth(year, month, day));
+    } else if (symbol.EqualsASCII("WW")) {
+      wsResult += NumToString(2, GetWeekOfYear(year, month, day));
+    }
+  }
+  return wsResult;
+}
+
+WideString TimeFormat(const WideString& wsTimePattern,
+                      LocaleIface* pLocale,
+                      const CFX_DateTime& datetime) {
+  WideString wsResult;
+  uint8_t hour = datetime.GetHour();
+  uint8_t minute = datetime.GetMinute();
+  uint8_t second = datetime.GetSecond();
+  uint16_t millisecond = datetime.GetMillisecond();
+  size_t ccf = 0;
+  pdfium::span<const wchar_t> spTimePattern = wsTimePattern.span();
+  uint16_t wHour = hour;
+  bool bPM = false;
+  if (wsTimePattern.Contains('A')) {
+    if (wHour >= 12)
+      bPM = true;
+  }
+
+  while (ccf < spTimePattern.size()) {
+    if (spTimePattern[ccf] == '\'') {
+      wsResult += GetLiteralText(spTimePattern, &ccf);
+      ccf++;
+      continue;
+    }
+    if (!pdfium::ContainsValue(kTimeSymbols, spTimePattern[ccf])) {
+      wsResult += spTimePattern[ccf++];
+      continue;
+    }
+
+    WideString symbol;
+    symbol.Reserve(4);
+    symbol += spTimePattern[ccf++];
+    while (ccf < spTimePattern.size() && spTimePattern[ccf] == symbol[0])
+      symbol += spTimePattern[ccf++];
+
+    if (symbol.EqualsASCII("h") || symbol.EqualsASCII("hh")) {
+      if (wHour > 12)
+        wHour -= 12;
+      wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 12 : wHour);
+    } else if (symbol.EqualsASCII("K") || symbol.EqualsASCII("KK")) {
+      wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 24 : wHour);
+    } else if (symbol.EqualsASCII("k") || symbol.EqualsASCII("kk")) {
+      if (wHour > 12)
+        wHour -= 12;
+      wsResult += NumToString(symbol.GetLength(), wHour);
+    } else if (symbol.EqualsASCII("H") || symbol.EqualsASCII("HH")) {
+      wsResult += NumToString(symbol.GetLength(), wHour);
+    } else if (symbol.EqualsASCII("M") || symbol.EqualsASCII("MM")) {
+      wsResult += NumToString(symbol.GetLength(), minute);
+    } else if (symbol.EqualsASCII("S") || symbol.EqualsASCII("SS")) {
+      wsResult += NumToString(symbol.GetLength(), second);
+    } else if (symbol.EqualsASCII("FFF")) {
+      wsResult += NumToString(3, millisecond);
+    } else if (symbol.EqualsASCII("A")) {
+      wsResult += pLocale->GetMeridiemName(!bPM);
+    } else if (symbol.EqualsASCIINoCase("z")) {
+      if (symbol.EqualsASCII("Z"))
+        wsResult += L"GMT";
+      FX_TIMEZONE tz = pLocale->GetTimeZone();
+      if (tz.tzHour != 0 || tz.tzMinute != 0) {
+        wsResult += tz.tzHour < 0 ? L"-" : L"+";
+        wsResult +=
+            WideString::Format(L"%02d:%02d", abs(tz.tzHour), tz.tzMinute);
+      }
+    }
+  }
+  return wsResult;
+}
+
+WideString FormatDateTimeInternal(const CFX_DateTime& dt,
+                                  const WideString& wsDatePattern,
+                                  const WideString& wsTimePattern,
+                                  bool bDateFirst,
+                                  LocaleIface* pLocale) {
+  WideString wsDateOut;
+  if (!wsDatePattern.IsEmpty())
+    wsDateOut = DateFormat(wsDatePattern, pLocale, dt);
+
+  WideString wsTimeOut;
+  if (!wsTimePattern.IsEmpty())
+    wsTimeOut = TimeFormat(wsTimePattern, pLocale, dt);
+
+  return bDateFirst ? wsDateOut + wsTimeOut : wsTimeOut + wsDateOut;
+}
+
+}  // namespace
+
+bool FX_DateFromCanonical(pdfium::span<const wchar_t> spDate,
+                          CFX_DateTime* datetime) {
+  if (spDate.size() > 10)
+    return false;
+
+  size_t cc = 0;
+  uint32_t year = 0;
+  if (!ExtractCountDigits(spDate, 4, &cc, &year))
+    return false;
+  if (year < 1900)
+    return false;
+  if (cc >= spDate.size()) {
+    datetime->SetDate(year, 1, 1);
+    return true;
+  }
+
+  if (spDate[cc] == '-')
+    cc++;
+
+  uint32_t month = 0;
+  if (!ExtractCountDigits(spDate, 2, &cc, &month) || month < 1 || month > 12)
+    return false;
+
+  if (cc >= spDate.size()) {
+    datetime->SetDate(year, month, 1);
+    return true;
+  }
+
+  if (spDate[cc] == '-')
+    cc++;
+
+  uint32_t day = 0;
+  if (!ExtractCountDigits(spDate, 2, &cc, &day))
+    return false;
+  if (day < 1)
+    return false;
+  if ((MonthHas31Days(month) && day > 31) ||
+      (MonthHas30Days(month) && day > 30)) {
+    return false;
+  }
+  if (month == 2 && day > (IsLeapYear(year) ? 29U : 28U))
+    return false;
+
+  datetime->SetDate(year, month, day);
+  return true;
+}
+
+bool FX_TimeFromCanonical(const LocaleIface* pLocale,
+                          pdfium::span<const wchar_t> spTime,
+                          CFX_DateTime* datetime) {
+  if (spTime.empty())
+    return false;
+
+  size_t cc = 0;
+  uint32_t hour = 0;
+  if (!ExtractCountDigits(spTime, 2, &cc, &hour) || hour >= 24)
+    return false;
+
+  if (cc >= spTime.size()) {
+    datetime->SetTime(hour, 0, 0, 0);
+    return true;
+  }
+
+  if (spTime[cc] == ':')
+    cc++;
+
+  uint32_t minute = 0;
+  if (!ExtractCountDigits(spTime, 2, &cc, &minute) || minute >= 60)
+    return false;
+
+  if (cc >= spTime.size()) {
+    datetime->SetTime(hour, minute, 0, 0);
+    return true;
+  }
+
+  if (spTime[cc] == ':')
+    cc++;
+
+  uint32_t second = 0;
+  uint32_t millisecond = 0;
+  if (cc < spTime.size() && spTime[cc] != 'Z') {
+    if (!ExtractCountDigits(spTime, 2, &cc, &second) || second >= 60)
+      return false;
+
+    if (cc < spTime.size() && spTime[cc] == '.') {
+      cc++;
+      if (!ExtractCountDigits(spTime, 3, &cc, &millisecond))
+        return false;
+    }
+  }
+
+  // Skip until we find a + or - for the time zone.
+  while (cc < spTime.size()) {
+    if (spTime[cc] == '+' || spTime[cc] == '-')
+      break;
+    ++cc;
+  }
+
+  if (cc < spTime.size()) {
+    FX_TIMEZONE tzDiff;
+    tzDiff.tzHour = 0;
+    tzDiff.tzMinute = 0;
+    if (spTime[cc] != 'Z')
+      cc += ParseTimeZone(spTime.subspan(cc), &tzDiff);
+    ResolveZone(tzDiff, pLocale, &hour, &minute);
+  }
+
+  datetime->SetTime(hour, minute, second, millisecond);
+  return true;
+}
+
+CFGAS_StringFormatter::CFGAS_StringFormatter(LocaleMgrIface* pLocaleMgr,
+                                             const WideString& wsPattern)
+    : m_pLocaleMgr(pLocaleMgr),
+      m_wsPattern(wsPattern),
+      m_spPattern(m_wsPattern.span()) {}
+
+CFGAS_StringFormatter::~CFGAS_StringFormatter() = default;
+
+// static
+std::vector<WideString> CFGAS_StringFormatter::SplitOnBars(
+    const WideString& wsFormatString) {
+  std::vector<WideString> wsPatterns;
+  pdfium::span<const wchar_t> spFormatString = wsFormatString.span();
+  size_t index = 0;
+  size_t token = 0;
+  bool bQuote = false;
+  for (; index < spFormatString.size(); ++index) {
+    if (spFormatString[index] == '\'') {
+      bQuote = !bQuote;
+    } else if (spFormatString[index] == L'|' && !bQuote) {
+      wsPatterns.emplace_back(spFormatString.data() + token, index - token);
+      token = index + 1;
+    }
+  }
+  wsPatterns.emplace_back(spFormatString.data() + token, index - token);
+  return wsPatterns;
+}
+
+FX_LOCALECATEGORY CFGAS_StringFormatter::GetCategory() const {
+  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
+  size_t ccf = 0;
+  bool bBraceOpen = false;
+  while (ccf < m_spPattern.size()) {
+    if (m_spPattern[ccf] == '\'') {
+      GetLiteralText(m_spPattern, &ccf);
+    } else if (!bBraceOpen &&
+               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+      WideString wsCategory(m_spPattern[ccf]);
+      ccf++;
+      while (true) {
+        if (ccf >= m_spPattern.size())
+          return eCategory;
+        if (m_spPattern[ccf] == '.' || m_spPattern[ccf] == '(')
+          break;
+        if (m_spPattern[ccf] == '{') {
+          bBraceOpen = true;
+          break;
+        }
+        wsCategory += m_spPattern[ccf];
+        ccf++;
+      }
+      uint32_t dwHash = FX_HashCode_GetW(wsCategory.AsStringView(), false);
+      if (dwHash == FX_LOCALECATEGORY_DateTimeHash)
+        return FX_LOCALECATEGORY_DateTime;
+      if (dwHash == FX_LOCALECATEGORY_TextHash)
+        return FX_LOCALECATEGORY_Text;
+      if (dwHash == FX_LOCALECATEGORY_NumHash)
+        return FX_LOCALECATEGORY_Num;
+      if (dwHash == FX_LOCALECATEGORY_ZeroHash)
+        return FX_LOCALECATEGORY_Zero;
+      if (dwHash == FX_LOCALECATEGORY_NullHash)
+        return FX_LOCALECATEGORY_Null;
+      if (dwHash == FX_LOCALECATEGORY_DateHash) {
+        if (eCategory == FX_LOCALECATEGORY_Time)
+          return FX_LOCALECATEGORY_DateTime;
+        eCategory = FX_LOCALECATEGORY_Date;
+      } else if (dwHash == FX_LOCALECATEGORY_TimeHash) {
+        if (eCategory == FX_LOCALECATEGORY_Date)
+          return FX_LOCALECATEGORY_DateTime;
+        eCategory = FX_LOCALECATEGORY_Time;
+      }
+    } else if (m_spPattern[ccf] == '}') {
+      bBraceOpen = false;
+    }
+    ccf++;
+  }
+  return eCategory;
+}
+
+WideString CFGAS_StringFormatter::GetTextFormat(
+    WideStringView wsCategory) const {
+  size_t ccf = 0;
+  bool bBrackOpen = false;
+  WideString wsPurgePattern;
+  while (ccf < m_spPattern.size()) {
+    if (m_spPattern[ccf] == '\'') {
+      int32_t iCurChar = ccf;
+      GetLiteralText(m_spPattern, &ccf);
+      wsPurgePattern +=
+          WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
+    } else if (!bBrackOpen &&
+               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+      WideString wsSearchCategory(m_spPattern[ccf]);
+      ccf++;
+      while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
+             m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
+        wsSearchCategory += m_spPattern[ccf];
+        ccf++;
+      }
+      if (wsSearchCategory != wsCategory)
+        continue;
+
+      while (ccf < m_spPattern.size()) {
+        if (m_spPattern[ccf] == '(') {
+          ccf++;
+          // Skip over the encoding name.
+          while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
+            ccf++;
+        } else if (m_spPattern[ccf] == '{') {
+          bBrackOpen = true;
+          break;
+        }
+        ccf++;
+      }
+    } else if (m_spPattern[ccf] != '}') {
+      wsPurgePattern += m_spPattern[ccf];
+    }
+    ccf++;
+  }
+  if (!bBrackOpen)
+    wsPurgePattern = m_wsPattern;
+
+  return wsPurgePattern;
+}
+
+LocaleIface* CFGAS_StringFormatter::GetNumericFormat(
+    size_t* iDotIndex,
+    uint32_t* dwStyle,
+    WideString* wsPurgePattern) const {
+  *dwStyle = 0;
+  LocaleIface* pLocale = nullptr;
+  size_t ccf = 0;
+  bool bFindDot = false;
+  bool bBrackOpen = false;
+  while (ccf < m_spPattern.size()) {
+    if (m_spPattern[ccf] == '\'') {
+      int32_t iCurChar = ccf;
+      GetLiteralText(m_spPattern, &ccf);
+      *wsPurgePattern +=
+          WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
+    } else if (!bBrackOpen &&
+               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+      WideString wsCategory(m_spPattern[ccf]);
+      ccf++;
+      while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
+             m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
+        wsCategory += m_spPattern[ccf];
+        ccf++;
+      }
+      if (!wsCategory.EqualsASCII("num")) {
+        bBrackOpen = true;
+        ccf = 0;
+        continue;
+      }
+      while (ccf < m_spPattern.size()) {
+        if (m_spPattern[ccf] == '{') {
+          bBrackOpen = true;
+          break;
+        }
+        if (m_spPattern[ccf] == '(') {
+          ccf++;
+          WideString wsLCID;
+          while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
+            wsLCID += m_spPattern[ccf++];
+
+          pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
+        } else if (m_spPattern[ccf] == '.') {
+          WideString wsSubCategory;
+          ccf++;
+          while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
+                 m_spPattern[ccf] != '{') {
+            wsSubCategory += m_spPattern[ccf++];
+          }
+          uint32_t dwSubHash =
+              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
+          FX_LOCALENUMSUBCATEGORY eSubCategory = FX_LOCALENUMPATTERN_Decimal;
+          for (const auto& data : g_FXLocaleNumSubCatData) {
+            if (data.uHash == dwSubHash) {
+              eSubCategory = data.eSubCategory;
+              break;
+            }
+          }
+          if (!pLocale)
+            pLocale = m_pLocaleMgr->GetDefLocale();
+
+          ASSERT(pLocale);
+
+          wsSubCategory = pLocale->GetNumPattern(eSubCategory);
+          auto result = wsSubCategory.Find('.');
+          if (result.has_value() && result.value() != 0) {
+            if (!bFindDot)
+              *iDotIndex = wsPurgePattern->GetLength() + result.value();
+            bFindDot = true;
+            *dwStyle |= FX_NUMSTYLE_DotVorv;
+          }
+          *wsPurgePattern += wsSubCategory;
+          if (eSubCategory == FX_LOCALENUMPATTERN_Percent)
+            *dwStyle |= FX_NUMSTYLE_Percent;
+
+          continue;
+        }
+        ccf++;
+      }
+    } else if (m_spPattern[ccf] == 'E') {
+      *dwStyle |= FX_NUMSTYLE_Exponent;
+      *wsPurgePattern += m_spPattern[ccf];
+    } else if (m_spPattern[ccf] == '%') {
+      *dwStyle |= FX_NUMSTYLE_Percent;
+      *wsPurgePattern += m_spPattern[ccf];
+    } else if (m_spPattern[ccf] != '}') {
+      *wsPurgePattern += m_spPattern[ccf];
+    }
+    if (!bFindDot && ccf < m_spPattern.size() &&
+        (m_spPattern[ccf] == '.' || m_spPattern[ccf] == 'V' ||
+         m_spPattern[ccf] == 'v')) {
+      bFindDot = true;
+      *iDotIndex = wsPurgePattern->GetLength() - 1;
+      *dwStyle |= FX_NUMSTYLE_DotVorv;
+    }
+    ccf++;
+  }
+  if (!bFindDot)
+    *iDotIndex = wsPurgePattern->GetLength();
+  if (!pLocale)
+    pLocale = m_pLocaleMgr->GetDefLocale();
+  return pLocale;
+}
+
+bool CFGAS_StringFormatter::ParseText(const WideString& wsSrcText,
+                                      WideString* wsValue) const {
+  wsValue->clear();
+  if (wsSrcText.IsEmpty() || m_spPattern.empty())
+    return false;
+
+  WideString wsTextFormat = GetTextFormat(L"text");
+  if (wsTextFormat.IsEmpty())
+    return false;
+
+  pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
+  pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
+
+  size_t iText = 0;
+  size_t iPattern = 0;
+  while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
+    switch (spTextFormat[iPattern]) {
+      case '\'': {
+        WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
+        int32_t iLiteralLen = wsLiteral.GetLength();
+        if (iText + iLiteralLen > spSrcText.size() ||
+            wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen) !=
+                0) {
+          *wsValue = wsSrcText;
+          return false;
+        }
+        iText += iLiteralLen;
+        iPattern++;
+        break;
+      }
+      case 'A':
+        if (FXSYS_iswalpha(spSrcText[iText])) {
+          *wsValue += spSrcText[iText];
+          iText++;
+        }
+        iPattern++;
+        break;
+      case 'X':
+        *wsValue += spSrcText[iText];
+        iText++;
+        iPattern++;
+        break;
+      case 'O':
+      case '0':
+        if (FXSYS_IsDecimalDigit(spSrcText[iText]) ||
+            FXSYS_iswalpha(spSrcText[iText])) {
+          *wsValue += spSrcText[iText];
+          iText++;
+        }
+        iPattern++;
+        break;
+      case '9':
+        if (FXSYS_IsDecimalDigit(spSrcText[iText])) {
+          *wsValue += spSrcText[iText];
+          iText++;
+        }
+        iPattern++;
+        break;
+      default:
+        if (spTextFormat[iPattern] != spSrcText[iText]) {
+          *wsValue = wsSrcText;
+          return false;
+        }
+        iPattern++;
+        iText++;
+        break;
+    }
+  }
+  return iPattern == spTextFormat.size() && iText == spSrcText.size();
+}
+
+bool CFGAS_StringFormatter::ParseNum(const WideString& wsSrcNum,
+                                     WideString* wsValue) const {
+  wsValue->clear();
+  if (wsSrcNum.IsEmpty() || m_spPattern.empty())
+    return false;
+
+  size_t dot_index_f = m_spPattern.size();
+  uint32_t dwFormatStyle = 0;
+  WideString wsNumFormat;
+  LocaleIface* pLocale =
+      GetNumericFormat(&dot_index_f, &dwFormatStyle, &wsNumFormat);
+  if (!pLocale || wsNumFormat.IsEmpty())
+    return false;
+
+  int32_t iExponent = 0;
+  WideString wsDotSymbol = pLocale->GetDecimalSymbol();
+  WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
+  int32_t iGroupLen = wsGroupSymbol.GetLength();
+  WideString wsMinus = pLocale->GetMinusSymbol();
+  int32_t iMinusLen = wsMinus.GetLength();
+
+  pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
+  pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
+
+  bool bHavePercentSymbol = false;
+  bool bNeg = false;
+  bool bReverseParse = false;
+  size_t dot_index = 0;
+
+  // If we're looking for a '.', 'V' or 'v' and the input string does not
+  // have a dot index for one of those, then we disable parsing the decimal.
+  if (!GetNumericDotIndex(wsSrcNum, wsDotSymbol, &dot_index) &&
+      (dwFormatStyle & FX_NUMSTYLE_DotVorv))
+    bReverseParse = true;
+
+  // This parse is broken into two parts based on the '.' in the number
+  // (or 'V' or 'v'). |dot_index_f| is the location of the dot in the format and
+  // |dot_index| is the location of the dot in the number.
+  //
+  // This first while() starts at the '.' and walks backwards to the start of
+  // the number. The second while() walks from the dot forwards to the end of
+  // the decimal.
+
+  size_t cc = dot_index - 1;
+  size_t ccf = dot_index_f - 1;
+  while (ccf < spNumFormat.size() && cc < spSrcNum.size()) {
+    switch (spNumFormat[ccf]) {
+      case '\'': {
+        WideString wsLiteral = GetLiteralTextReverse(spNumFormat, &ccf);
+        int32_t iLiteralLen = wsLiteral.GetLength();
+        cc -= iLiteralLen - 1;
+        if (cc >= spSrcNum.size() ||
+            wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
+                0) {
+          return false;
+        }
+        cc--;
+        ccf--;
+        break;
+      }
+      case '9':
+        if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+          return false;
+
+        wsValue->InsertAtFront(spSrcNum[cc]);
+        cc--;
+        ccf--;
+        break;
+      case 'z':
+      case 'Z':
+        if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
+          if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
+            wsValue->InsertAtFront(spSrcNum[cc]);
+            cc--;
+          }
+        } else {
+          cc--;
+        }
+        ccf--;
+        break;
+      case 'S':
+      case 's':
+        if (spSrcNum[cc] == '+' ||
+            (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
+          cc--;
+        } else {
+          cc -= iMinusLen - 1;
+          if (cc >= spSrcNum.size() ||
+              wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) != 0) {
+            return false;
+          }
+          cc--;
+          bNeg = true;
+        }
+        ccf--;
+        break;
+      case 'E': {
+        iExponent = 0;
+        bool bExpSign = false;
+        while (cc < spSrcNum.size()) {
+          if (spSrcNum[cc] == 'E' || spSrcNum[cc] == 'e')
+            break;
+          if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
+            if (iExponent > std::numeric_limits<int>::max() / 10)
+              return false;
+            iExponent = iExponent + FXSYS_DecimalCharToInt(spSrcNum[cc]) * 10;
+            cc--;
+            continue;
+          }
+          if (spSrcNum[cc] == '+') {
+            cc--;
+            continue;
+          }
+          if (cc - iMinusLen + 1 <= spSrcNum.size() &&
+              wcsncmp(spSrcNum.data() + (cc - iMinusLen + 1), wsMinus.c_str(),
+                      iMinusLen) == 0) {
+            bExpSign = true;
+            cc -= iMinusLen;
+            continue;
+          }
+
+          return false;
+        }
+        cc--;
+        iExponent = bExpSign ? -iExponent : iExponent;
+        ccf--;
+        break;
+      }
+      case '$': {
+        WideString wsSymbol = pLocale->GetCurrencySymbol();
+        int32_t iSymbolLen = wsSymbol.GetLength();
+        cc -= iSymbolLen - 1;
+        if (cc >= spSrcNum.size() ||
+            wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
+          return false;
+        }
+        cc--;
+        ccf--;
+        break;
+      }
+      case 'r':
+      case 'R':
+        if (ccf - 1 < spNumFormat.size() &&
+            ((spNumFormat[ccf] == 'R' && spNumFormat[ccf - 1] == 'C') ||
+             (spNumFormat[ccf] == 'r' && spNumFormat[ccf - 1] == 'c'))) {
+          if (spNumFormat[ccf] == 'R' && spSrcNum[cc] == ' ') {
+            cc -= 2;
+          } else if (spSrcNum[cc] == 'R' && cc - 1 < spSrcNum.size() &&
+                     spSrcNum[cc - 1] == 'C') {
+            bNeg = true;
+            cc -= 2;
+          }
+          ccf -= 2;
+        } else {
+          ccf--;
+        }
+        break;
+      case 'b':
+      case 'B':
+        if (ccf - 1 < spNumFormat.size() &&
+            ((spNumFormat[ccf] == 'B' && spNumFormat[ccf - 1] == 'D') ||
+             (spNumFormat[ccf] == 'b' && spNumFormat[ccf - 1] == 'd'))) {
+          if (spNumFormat[ccf] == 'B' && spSrcNum[cc] == ' ') {
+            cc -= 2;
+          } else if (spSrcNum[cc] == 'B' && cc - 1 < spSrcNum.size() &&
+                     spSrcNum[cc - 1] == 'D') {
+            bNeg = true;
+            cc -= 2;
+          }
+          ccf -= 2;
+        } else {
+          ccf--;
+        }
+        break;
+      case '%': {
+        WideString wsSymbol = pLocale->GetPercentSymbol();
+        int32_t iSysmbolLen = wsSymbol.GetLength();
+        cc -= iSysmbolLen - 1;
+        if (cc >= spSrcNum.size() ||
+            wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSysmbolLen) != 0) {
+          return false;
+        }
+        cc--;
+        ccf--;
+        bHavePercentSymbol = true;
+        break;
+      }
+      case '.':
+      case 'V':
+      case 'v':
+      case '8':
+        return false;
+      case ',': {
+        if (cc < spSrcNum.size()) {
+          cc -= iGroupLen - 1;
+          if (cc < spSrcNum.size() &&
+              wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
+                  0) {
+            cc--;
+          } else {
+            cc += iGroupLen - 1;
+          }
+        }
+        ccf--;
+        break;
+      }
+      case '(':
+      case ')':
+        if (spSrcNum[cc] == spNumFormat[ccf])
+          bNeg = true;
+        else if (spSrcNum[cc] != L' ')
+          return false;
+
+        cc--;
+        ccf--;
+        break;
+      default:
+        if (spNumFormat[ccf] != spSrcNum[cc])
+          return false;
+
+        cc--;
+        ccf--;
+    }
+  }
+  if (cc < spSrcNum.size()) {
+    if (spSrcNum[cc] == '-') {
+      bNeg = true;
+      cc--;
+    }
+    if (cc < spSrcNum.size())
+      return false;
+  }
+  if ((dwFormatStyle & FX_NUMSTYLE_DotVorv) && dot_index < spSrcNum.size())
+    *wsValue += '.';
+
+  if (!bReverseParse) {
+    cc = (dot_index == spSrcNum.size()) ? spSrcNum.size() : dot_index + 1;
+    for (ccf = dot_index_f + 1;
+         cc < spSrcNum.size() && ccf < spNumFormat.size(); ++ccf) {
+      switch (spNumFormat[ccf]) {
+        case '\'': {
+          WideString wsLiteral = GetLiteralText(spNumFormat, &ccf);
+          int32_t iLiteralLen = wsLiteral.GetLength();
+          if (cc + iLiteralLen > spSrcNum.size() ||
+              wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
+                  0) {
+            return false;
+          }
+          cc += iLiteralLen;
+          break;
+        }
+        case '9':
+          if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+            return false;
+
+          *wsValue += spSrcNum[cc];
+          cc++;
+          break;
+        case 'z':
+        case 'Z':
+          if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
+            if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
+              *wsValue += spSrcNum[cc];
+              cc++;
+            }
+          } else {
+            cc++;
+          }
+          break;
+        case 'S':
+        case 's':
+          if (spSrcNum[cc] == '+' ||
+              (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
+            cc++;
+          } else {
+            if (cc + iMinusLen > spSrcNum.size() ||
+                wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) !=
+                    0) {
+              return false;
+            }
+            bNeg = true;
+            cc += iMinusLen;
+          }
+          break;
+        case 'E': {
+          if (cc >= spSrcNum.size() ||
+              (spSrcNum[cc] != 'E' && spSrcNum[cc] != 'e')) {
+            return false;
+          }
+          iExponent = 0;
+          bool bExpSign = false;
+          cc++;
+          if (cc < spSrcNum.size()) {
+            if (spSrcNum[cc] == '+') {
+              cc++;
+            } else if (spSrcNum[cc] == '-') {
+              bExpSign = true;
+              cc++;
+            }
+          }
+          while (cc < spSrcNum.size()) {
+            if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+              break;
+            int digit = FXSYS_DecimalCharToInt(spSrcNum[cc]);
+            if (iExponent > (std::numeric_limits<int>::max() - digit) / 10)
+              return false;
+            iExponent = iExponent * 10 + digit;
+            cc++;
+          }
+          iExponent = bExpSign ? -iExponent : iExponent;
+          break;
+        }
+        case '$': {
+          WideString wsSymbol = pLocale->GetCurrencySymbol();
+          int32_t iSymbolLen = wsSymbol.GetLength();
+          if (cc + iSymbolLen > spSrcNum.size() ||
+              wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) !=
+                  0) {
+            return false;
+          }
+          cc += iSymbolLen;
+          break;
+        }
+        case 'c':
+        case 'C':
+          if (ccf + 1 < spNumFormat.size() &&
+              ((spNumFormat[ccf] == 'C' && spNumFormat[ccf + 1] == 'R') ||
+               (spNumFormat[ccf] == 'c' && spNumFormat[ccf + 1] == 'r'))) {
+            if (spNumFormat[ccf] == 'C' && spSrcNum[cc] == ' ') {
+              cc++;
+            } else if (spSrcNum[cc] == 'C' && cc + 1 < spSrcNum.size() &&
+                       spSrcNum[cc + 1] == 'R') {
+              bNeg = true;
+              cc += 2;
+            }
+            ccf++;
+          }
+          break;
+        case 'd':
+        case 'D':
+          if (ccf + 1 < spNumFormat.size() &&
+              ((spNumFormat[ccf] == 'D' && spNumFormat[ccf + 1] == 'B') ||
+               (spNumFormat[ccf] == 'd' && spNumFormat[ccf + 1] == 'b'))) {
+            if (spNumFormat[ccf] == 'D' && spSrcNum[cc] == ' ') {
+              cc++;
+            } else if (spSrcNum[cc] == 'D' && cc + 1 < spSrcNum.size() &&
+                       spSrcNum[cc + 1] == 'B') {
+              bNeg = true;
+              cc += 2;
+            }
+            ccf++;
+          }
+          break;
+        case '.':
+        case 'V':
+        case 'v':
+          return false;
+        case '%': {
+          WideString wsSymbol = pLocale->GetPercentSymbol();
+          int32_t iSysmbolLen = wsSymbol.GetLength();
+          if (cc + iSysmbolLen <= spSrcNum.size() &&
+              wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSysmbolLen) ==
+                  0) {
+            cc += iSysmbolLen;
+          }
+          bHavePercentSymbol = true;
+        } break;
+        case '8': {
+          while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
+            ccf++;
+
+          while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
+            *wsValue += spSrcNum[cc];
+            cc++;
+          }
+        } break;
+        case ',': {
+          if (cc + iGroupLen <= spSrcNum.size() &&
+              wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
+                  0) {
+            cc += iGroupLen;
+          }
+          break;
+        }
+        case '(':
+        case ')':
+          if (spSrcNum[cc] == spNumFormat[ccf])
+            bNeg = true;
+          else if (spSrcNum[cc] != L' ')
+            return false;
+
+          cc++;
+          break;
+        default:
+          if (spNumFormat[ccf] != spSrcNum[cc])
+            return false;
+
+          cc++;
+      }
+    }
+    if (cc != spSrcNum.size())
+      return false;
+  }
+  if (iExponent || bHavePercentSymbol) {
+    CFGAS_Decimal decimal = CFGAS_Decimal(wsValue->AsStringView());
+    if (iExponent) {
+      decimal = decimal *
+                CFGAS_Decimal(FXSYS_pow(10, static_cast<float>(iExponent)), 3);
+    }
+    if (bHavePercentSymbol)
+      decimal = decimal / CFGAS_Decimal(100);
+
+    *wsValue = decimal.ToWideString();
+  }
+  if (bNeg)
+    wsValue->InsertAtFront(L'-');
+
+  return true;
+}
+
+FX_DATETIMETYPE CFGAS_StringFormatter::GetDateTimeFormat(
+    LocaleIface** pLocale,
+    WideString* wsDatePattern,
+    WideString* wsTimePattern) const {
+  *pLocale = nullptr;
+  WideString wsTempPattern;
+  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
+  size_t ccf = 0;
+  int32_t iFindCategory = 0;
+  bool bBraceOpen = false;
+  while (ccf < m_spPattern.size()) {
+    if (m_spPattern[ccf] == '\'') {
+      int32_t iCurChar = ccf;
+      GetLiteralText(m_spPattern, &ccf);
+      wsTempPattern +=
+          WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
+    } else if (!bBraceOpen && iFindCategory != 3 &&
+               !pdfium::ContainsValue(kConstChars, m_spPattern[ccf])) {
+      WideString wsCategory(m_spPattern[ccf]);
+      ccf++;
+      while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
+             m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
+        if (m_spPattern[ccf] == 'T') {
+          *wsDatePattern = m_wsPattern.First(ccf);
+          *wsTimePattern = m_wsPattern.Last(m_wsPattern.GetLength() - ccf);
+          wsTimePattern->SetAt(0, ' ');
+          if (!*pLocale)
+            *pLocale = m_pLocaleMgr->GetDefLocale();
+
+          return FX_DATETIMETYPE_DateTime;
+        }
+        wsCategory += m_spPattern[ccf];
+        ccf++;
+      }
+      if (!(iFindCategory & 1) && wsCategory.EqualsASCII("date")) {
+        iFindCategory |= 1;
+        eCategory = FX_LOCALECATEGORY_Date;
+        if (iFindCategory & 2)
+          iFindCategory = 4;
+      } else if (!(iFindCategory & 2) && wsCategory.EqualsASCII("time")) {
+        iFindCategory |= 2;
+        eCategory = FX_LOCALECATEGORY_Time;
+      } else if (wsCategory.EqualsASCII("datetime")) {
+        iFindCategory = 3;
+        eCategory = FX_LOCALECATEGORY_DateTime;
+      } else {
+        continue;
+      }
+      while (ccf < m_spPattern.size()) {
+        if (m_spPattern[ccf] == '{') {
+          bBraceOpen = true;
+          break;
+        }
+        if (m_spPattern[ccf] == '(') {
+          ccf++;
+          WideString wsLCID;
+          while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
+            wsLCID += m_spPattern[ccf++];
+
+          *pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
+        } else if (m_spPattern[ccf] == '.') {
+          WideString wsSubCategory;
+          ccf++;
+          while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
+                 m_spPattern[ccf] != '{')
+            wsSubCategory += m_spPattern[ccf++];
+
+          uint32_t dwSubHash =
+              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
+          FX_LOCALEDATETIMESUBCATEGORY eSubCategory =
+              FX_LOCALEDATETIMESUBCATEGORY_Medium;
+          for (const auto& data : g_FXLocaleDateTimeSubCatData) {
+            if (data.uHash == dwSubHash) {
+              eSubCategory = data.eSubCategory;
+              break;
+            }
+          }
+          if (!*pLocale)
+            *pLocale = m_pLocaleMgr->GetDefLocale();
+          ASSERT(*pLocale);
+
+          switch (eCategory) {
+            case FX_LOCALECATEGORY_Date:
+              *wsDatePattern =
+                  wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
+              break;
+            case FX_LOCALECATEGORY_Time:
+              *wsTimePattern =
+                  wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
+              break;
+            case FX_LOCALECATEGORY_DateTime:
+              *wsDatePattern =
+                  wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
+              *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
+              break;
+            default:
+              break;
+          }
+          wsTempPattern.clear();
+          continue;
+        }
+        ccf++;
+      }
+    } else if (m_spPattern[ccf] == '}') {
+      bBraceOpen = false;
+      if (!wsTempPattern.IsEmpty()) {
+        if (eCategory == FX_LOCALECATEGORY_Time)
+          *wsTimePattern = std::move(wsTempPattern);
+        else if (eCategory == FX_LOCALECATEGORY_Date)
+          *wsDatePattern = std::move(wsTempPattern);
+        else
+          wsTempPattern.clear();
+      }
+    } else {
+      wsTempPattern += m_spPattern[ccf];
+    }
+    ccf++;
+  }
+
+  if (!wsTempPattern.IsEmpty()) {
+    if (eCategory == FX_LOCALECATEGORY_Date)
+      *wsDatePattern += wsTempPattern;
+    else
+      *wsTimePattern += wsTempPattern;
+  }
+  if (!*pLocale)
+    *pLocale = m_pLocaleMgr->GetDefLocale();
+  if (!iFindCategory) {
+    wsTimePattern->clear();
+    *wsDatePattern = m_wsPattern;
+  }
+  return (FX_DATETIMETYPE)iFindCategory;
+}
+
+bool CFGAS_StringFormatter::ParseDateTime(const WideString& wsSrcDateTime,
+                                          FX_DATETIMETYPE eDateTimeType,
+                                          CFX_DateTime* dtValue) const {
+  dtValue->Reset();
+  if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
+    return false;
+
+  LocaleIface* pLocale = nullptr;
+  WideString wsDatePattern;
+  WideString wsTimePattern;
+  FX_DATETIMETYPE eCategory =
+      GetDateTimeFormat(&pLocale, &wsDatePattern, &wsTimePattern);
+  if (!pLocale)
+    return false;
+
+  if (eCategory == FX_DATETIMETYPE_Unknown)
+    eCategory = eDateTimeType;
+
+  size_t iStart = 0;
+  switch (eCategory) {
+    case FX_DATETIMETYPE_Date:
+      return ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
+                             &iStart);
+    case FX_DATETIMETYPE_Time:
+      return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
+                             &iStart);
+    case FX_DATETIMETYPE_DateTime:
+      return ParseLocaleDate(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
+                             &iStart) &&
+             ParseLocaleTime(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
+                             &iStart);
+    case FX_DATETIMETYPE_TimeDate:
+      return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
+                             &iStart) &&
+             ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
+                             &iStart);
+    case FX_DATETIMETYPE_Unknown:
+    default:
+      return false;
+  }
+}
+
+bool CFGAS_StringFormatter::ParseZero(const WideString& wsSrcText) const {
+  WideString wsTextFormat = GetTextFormat(L"zero");
+  pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
+  pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
+
+  size_t iText = 0;
+  size_t iPattern = 0;
+  while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
+    if (spTextFormat[iPattern] == '\'') {
+      WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
+      int32_t iLiteralLen = wsLiteral.GetLength();
+      if (iText + iLiteralLen > spSrcText.size() ||
+          wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
+        return false;
+      }
+      iText += iLiteralLen;
+      iPattern++;
+      continue;
+    }
+    if (spTextFormat[iPattern] != spSrcText[iText])
+      return false;
+
+    iText++;
+    iPattern++;
+  }
+  return iPattern == spTextFormat.size() && iText == spSrcText.size();
+}
+
+bool CFGAS_StringFormatter::ParseNull(const WideString& wsSrcText) const {
+  WideString wsTextFormat = GetTextFormat(L"null");
+  pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
+  pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
+
+  size_t iText = 0;
+  size_t iPattern = 0;
+  while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
+    if (spTextFormat[iPattern] == '\'') {
+      WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
+      int32_t iLiteralLen = wsLiteral.GetLength();
+      if (iText + iLiteralLen > spSrcText.size() ||
+          wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
+        return false;
+      }
+      iText += iLiteralLen;
+      iPattern++;
+      continue;
+    }
+    if (spTextFormat[iPattern] != spSrcText[iText])
+      return false;
+
+    iText++;
+    iPattern++;
+  }
+  return iPattern == spTextFormat.size() && iText == spSrcText.size();
+}
+
+bool CFGAS_StringFormatter::FormatText(const WideString& wsSrcText,
+                                       WideString* wsOutput) const {
+  if (wsSrcText.IsEmpty() || m_spPattern.empty())
+    return false;
+
+  WideString wsTextFormat = GetTextFormat(L"text");
+  pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
+  pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
+
+  size_t iText = 0;
+  size_t iPattern = 0;
+  while (iPattern < spTextFormat.size()) {
+    switch (spTextFormat[iPattern]) {
+      case '\'': {
+        *wsOutput += GetLiteralText(spTextFormat, &iPattern);
+        iPattern++;
+        break;
+      }
+      case 'A':
+        if (iText >= spSrcText.size() || !FXSYS_iswalpha(spSrcText[iText]))
+          return false;
+
+        *wsOutput += spSrcText[iText++];
+        iPattern++;
+        break;
+      case 'X':
+        if (iText >= spSrcText.size())
+          return false;
+
+        *wsOutput += spSrcText[iText++];
+        iPattern++;
+        break;
+      case 'O':
+      case '0':
+        if (iText >= spSrcText.size() ||
+            (!FXSYS_IsDecimalDigit(spSrcText[iText]) &&
+             !FXSYS_iswalpha(spSrcText[iText]))) {
+          return false;
+        }
+        *wsOutput += spSrcText[iText++];
+        iPattern++;
+        break;
+      case '9':
+        if (iText >= spSrcText.size() ||
+            !FXSYS_IsDecimalDigit(spSrcText[iText]))
+          return false;
+
+        *wsOutput += spSrcText[iText++];
+        iPattern++;
+        break;
+      default:
+        *wsOutput += spTextFormat[iPattern++];
+        break;
+    }
+  }
+  return iText == spSrcText.size();
+}
+
+bool CFGAS_StringFormatter::FormatNum(const WideString& wsInputNum,
+                                      WideString* wsOutput) const {
+  if (wsInputNum.IsEmpty() || m_spPattern.empty())
+    return false;
+
+  size_t dot_index_f = m_spPattern.size();
+  uint32_t dwNumStyle = 0;
+  WideString wsNumFormat;
+  LocaleIface* pLocale =
+      GetNumericFormat(&dot_index_f, &dwNumStyle, &wsNumFormat);
+  if (!pLocale || wsNumFormat.IsEmpty())
+    return false;
+
+  pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
+  WideString wsSrcNum = wsInputNum;
+  wsSrcNum.TrimLeft('0');
+  if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.')
+    wsSrcNum.InsertAtFront('0');
+
+  CFGAS_Decimal decimal = CFGAS_Decimal(wsSrcNum.AsStringView());
+  if (dwNumStyle & FX_NUMSTYLE_Percent) {
+    decimal = decimal * CFGAS_Decimal(100);
+    wsSrcNum = decimal.ToWideString();
+  }
+
+  int32_t exponent = 0;
+  if (dwNumStyle & FX_NUMSTYLE_Exponent) {
+    int fixed_count = 0;
+    for (size_t ccf = 0; ccf < dot_index_f; ++ccf) {
+      switch (spNumFormat[ccf]) {
+        case '\'':
+          GetLiteralText(spNumFormat, &ccf);
+          break;
+        case '9':
+        case 'z':
+        case 'Z':
+          fixed_count++;
+          break;
+      }
+    }
+
+    FX_SAFE_UINT32 threshold = 1;
+    while (fixed_count > 1) {
+      threshold *= 10;
+      fixed_count--;
+    }
+    if (!threshold.IsValid())
+      return false;
+
+    bool bAdjusted = false;
+    while (decimal.IsNotZero() &&
+           fabs(decimal.ToDouble()) < threshold.ValueOrDie()) {
+      decimal = decimal * CFGAS_Decimal(10);
+      --exponent;
+      bAdjusted = true;
+    }
+    if (!bAdjusted) {
+      threshold *= 10;
+      if (!threshold.IsValid())
+        return false;
+
+      while (decimal.IsNotZero() &&
+             fabs(decimal.ToDouble()) > threshold.ValueOrDie()) {
+        decimal = decimal / CFGAS_Decimal(10);
+        ++exponent;
+      }
+    }
+  }
+
+  bool bTrimTailZeros = false;
+  int32_t iTreading =
+      GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
+  int32_t scale = decimal.GetScale();
+  if (iTreading < scale) {
+    decimal.SetScale(iTreading);
+    wsSrcNum = decimal.ToWideString();
+  }
+  if (bTrimTailZeros && scale > 0 && iTreading > 0) {
+    wsSrcNum.TrimRight(L"0");
+    wsSrcNum.TrimRight(L".");
+  }
+
+  WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
+  bool bNeg = false;
+  if (wsSrcNum[0] == '-') {
+    bNeg = true;
+    wsSrcNum.Delete(0, 1);
+  }
+
+  bool bAddNeg = false;
+  pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
+  auto dot_index = wsSrcNum.Find('.');
+  if (!dot_index.has_value())
+    dot_index = spSrcNum.size();
+
+  size_t cc = dot_index.value() - 1;
+  for (size_t ccf = dot_index_f - 1; ccf < spNumFormat.size(); --ccf) {
+    switch (spNumFormat[ccf]) {
+      case '9':
+        if (cc < spSrcNum.size()) {
+          if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+            return false;
+          wsOutput->InsertAtFront(spSrcNum[cc]);
+          cc--;
+        } else {
+          wsOutput->InsertAtFront(L'0');
+        }
+        break;
+      case 'z':
+        if (cc < spSrcNum.size()) {
+          if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+            return false;
+          if (spSrcNum[0] != '0')
+            wsOutput->InsertAtFront(spSrcNum[cc]);
+          cc--;
+        }
+        break;
+      case 'Z':
+        if (cc < spSrcNum.size()) {
+          if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+            return false;
+          wsOutput->InsertAtFront(spSrcNum[0] == '0' ? L' ' : spSrcNum[cc]);
+          cc--;
+        } else {
+          wsOutput->InsertAtFront(L' ');
+        }
+        break;
+      case 'S':
+        if (bNeg) {
+          *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
+          bAddNeg = true;
+        } else {
+          wsOutput->InsertAtFront(L' ');
+        }
+        break;
+      case 's':
+        if (bNeg) {
+          *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
+          bAddNeg = true;
+        }
+        break;
+      case 'E':
+        *wsOutput = WideString::Format(L"E%+d", exponent) + *wsOutput;
+        break;
+      case '$':
+        *wsOutput = pLocale->GetCurrencySymbol() + *wsOutput;
+        break;
+      case 'r':
+        if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'c') {
+          if (bNeg)
+            *wsOutput = L"CR" + *wsOutput;
+          ccf--;
+          bAddNeg = true;
+        } else {
+          wsOutput->InsertAtFront('r');
+        }
+        break;
+      case 'R':
+        if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'C') {
+          *wsOutput = bNeg ? L"CR" : L"  " + *wsOutput;
+          ccf--;
+          bAddNeg = true;
+        } else {
+          wsOutput->InsertAtFront('R');
+        }
+        break;
+      case 'b':
+        if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'd') {
+          if (bNeg)
+            *wsOutput = L"db" + *wsOutput;
+          ccf--;
+          bAddNeg = true;
+        } else {
+          wsOutput->InsertAtFront('b');
+        }
+        break;
+      case 'B':
+        if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'D') {
+          *wsOutput = bNeg ? L"DB" : L"  " + *wsOutput;
+          ccf--;
+          bAddNeg = true;
+        } else {
+          wsOutput->InsertAtFront('B');
+        }
+        break;
+      case '%':
+        *wsOutput = pLocale->GetPercentSymbol() + *wsOutput;
+        break;
+      case ',':
+        if (cc < spSrcNum.size())
+          *wsOutput = wsGroupSymbol + *wsOutput;
+        break;
+      case '(':
+        wsOutput->InsertAtFront(bNeg ? L'(' : L' ');
+        bAddNeg = true;
+        break;
+      case ')':
+        wsOutput->InsertAtFront(bNeg ? L')' : L' ');
+        break;
+      case '\'':
+        *wsOutput = GetLiteralTextReverse(spNumFormat, &ccf) + *wsOutput;
+        break;
+      default:
+        wsOutput->InsertAtFront(spNumFormat[ccf]);
+        break;
+    }
+  }
+
+  if (cc < spSrcNum.size()) {
+    size_t nPos = dot_index.value() % 3;
+    wsOutput->clear();
+    for (size_t i = 0; i < dot_index.value(); i++) {
+      if (i % 3 == nPos && i != 0)
+        *wsOutput += wsGroupSymbol;
+      *wsOutput += wsSrcNum[i];
+    }
+    if (dot_index.value() < spSrcNum.size()) {
+      *wsOutput += pLocale->GetDecimalSymbol();
+      *wsOutput += wsSrcNum.Last(spSrcNum.size() - dot_index.value() - 1);
+    }
+    if (bNeg)
+      *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
+    return true;
+  }
+  if (dot_index_f == wsNumFormat.GetLength()) {
+    if (!bAddNeg && bNeg)
+      *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
+    return true;
+  }
+
+  WideString wsDotSymbol = pLocale->GetDecimalSymbol();
+  if (spNumFormat[dot_index_f] == 'V') {
+    *wsOutput += wsDotSymbol;
+  } else if (spNumFormat[dot_index_f] == '.') {
+    if (dot_index.value() < spSrcNum.size()) {
+      *wsOutput += wsDotSymbol;
+    } else if (dot_index_f + 1 < spNumFormat.size() &&
+               (spNumFormat[dot_index_f + 1] == '9' ||
+                spNumFormat[dot_index_f + 1] == 'Z')) {
+      *wsOutput += wsDotSymbol;
+    }
+  }
+
+  cc = dot_index.value() + 1;
+  for (size_t ccf = dot_index_f + 1; ccf < spNumFormat.size(); ++ccf) {
+    switch (spNumFormat[ccf]) {
+      case '\'':
+        *wsOutput += GetLiteralText(spNumFormat, &ccf);
+        break;
+      case '9':
+        if (cc < spSrcNum.size()) {
+          if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+            return false;
+          *wsOutput += spSrcNum[cc];
+          cc++;
+        } else {
+          *wsOutput += L'0';
+        }
+        break;
+      case 'z':
+        if (cc < spSrcNum.size()) {
+          if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+            return false;
+          *wsOutput += spSrcNum[cc];
+          cc++;
+        }
+        break;
+      case 'Z':
+        if (cc < spSrcNum.size()) {
+          if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
+            return false;
+          *wsOutput += spSrcNum[cc];
+          cc++;
+        } else {
+          *wsOutput += L'0';
+        }
+        break;
+      case 'E': {
+        *wsOutput += WideString::Format(L"E%+d", exponent);
+        break;
+      }
+      case '$':
+        *wsOutput += pLocale->GetCurrencySymbol();
+        break;
+      case 'c':
+        if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'r') {
+          if (bNeg)
+            *wsOutput += L"CR";
+          ccf++;
+          bAddNeg = true;
+        }
+        break;
+      case 'C':
+        if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'R') {
+          *wsOutput += bNeg ? L"CR" : L"  ";
+          ccf++;
+          bAddNeg = true;
+        }
+        break;
+      case 'd':
+        if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'b') {
+          if (bNeg)
+            *wsOutput += L"db";
+          ccf++;
+          bAddNeg = true;
+        }
+        break;
+      case 'D':
+        if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'B') {
+          *wsOutput += bNeg ? L"DB" : L"  ";
+          ccf++;
+          bAddNeg = true;
+        }
+        break;
+      case '%':
+        *wsOutput += pLocale->GetPercentSymbol();
+        break;
+      case '8':
+        while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
+          ccf++;
+        while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
+          *wsOutput += spSrcNum[cc];
+          cc++;
+        }
+        break;
+      case ',':
+        *wsOutput += wsGroupSymbol;
+        break;
+      case '(':
+        *wsOutput += bNeg ? '(' : ' ';
+        bAddNeg = true;
+        break;
+      case ')':
+        *wsOutput += bNeg ? ')' : ' ';
+        break;
+      default:
+        break;
+    }
+  }
+  if (!bAddNeg && bNeg)
+    *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
+
+  return true;
+}
+
+bool CFGAS_StringFormatter::FormatDateTime(const WideString& wsSrcDateTime,
+                                           FX_DATETIMETYPE eDateTimeType,
+                                           WideString* wsOutput) const {
+  if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
+    return false;
+
+  WideString wsDatePattern;
+  WideString wsTimePattern;
+  LocaleIface* pLocale = nullptr;
+  FX_DATETIMETYPE eCategory =
+      GetDateTimeFormat(&pLocale, &wsDatePattern, &wsTimePattern);
+  if (!pLocale)
+    return false;
+
+  if (eCategory == FX_DATETIMETYPE_Unknown) {
+    if (eDateTimeType == FX_DATETIMETYPE_Time) {
+      wsTimePattern = std::move(wsDatePattern);
+      wsDatePattern = WideString();
+    }
+    eCategory = eDateTimeType;
+    if (eCategory == FX_DATETIMETYPE_Unknown)
+      return false;
+  }
+
+  CFX_DateTime dt;
+  auto iT = wsSrcDateTime.Find(L"T");
+  if (!iT.has_value()) {
+    if (eCategory == FX_DATETIMETYPE_Date &&
+        FX_DateFromCanonical(wsSrcDateTime.span(), &dt)) {
+      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
+                                         pLocale);
+      return true;
+    }
+    if (eCategory == FX_DATETIMETYPE_Time &&
+        FX_TimeFromCanonical(pLocale, wsSrcDateTime.span(), &dt)) {
+      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
+                                         pLocale);
+      return true;
+    }
+  } else {
+    pdfium::span<const wchar_t> wsSrcDate =
+        wsSrcDateTime.span().first(iT.value());
+    pdfium::span<const wchar_t> wsSrcTime =
+        wsSrcDateTime.span().subspan(iT.value() + 1);
+    if (wsSrcDate.empty() || wsSrcTime.empty())
+      return false;
+
+    if (FX_DateFromCanonical(wsSrcDate, &dt) &&
+        FX_TimeFromCanonical(pLocale, wsSrcTime, &dt)) {
+      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
+                                         eCategory != FX_DATETIMETYPE_TimeDate,
+                                         pLocale);
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CFGAS_StringFormatter::FormatZero(WideString* wsOutput) const {
+  if (m_spPattern.empty())
+    return false;
+
+  WideString wsTextFormat = GetTextFormat(L"zero");
+  pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
+  for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
+    if (spTextFormat[iPattern] == '\'') {
+      *wsOutput += GetLiteralText(spTextFormat, &iPattern);
+      continue;
+    }
+    *wsOutput += spTextFormat[iPattern];
+  }
+  return true;
+}
+
+bool CFGAS_StringFormatter::FormatNull(WideString* wsOutput) const {
+  if (m_spPattern.empty())
+    return false;
+
+  WideString wsTextFormat = GetTextFormat(L"null");
+  pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
+  for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
+    if (spTextFormat[iPattern] == '\'') {
+      *wsOutput += GetLiteralText(spTextFormat, &iPattern);
+      continue;
+    }
+    *wsOutput += spTextFormat[iPattern];
+  }
+  return true;
+}
diff --git a/xfa/fgas/crt/cfgas_stringformatter.h b/xfa/fgas/crt/cfgas_stringformatter.h
new file mode 100644
index 0000000..afdeeb9
--- /dev/null
+++ b/xfa/fgas/crt/cfgas_stringformatter.h
@@ -0,0 +1,67 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_CRT_CFGAS_STRINGFORMATTER_H_
+#define XFA_FGAS_CRT_CFGAS_STRINGFORMATTER_H_
+
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/span.h"
+#include "xfa/fgas/crt/locale_iface.h"
+#include "xfa/fgas/crt/locale_mgr_iface.h"
+
+bool FX_DateFromCanonical(pdfium::span<const wchar_t> wsTime,
+                          CFX_DateTime* datetime);
+bool FX_TimeFromCanonical(const LocaleIface* pLocale,
+                          pdfium::span<const wchar_t> wsTime,
+                          CFX_DateTime* datetime);
+
+class CFGAS_StringFormatter {
+ public:
+  CFGAS_StringFormatter(LocaleMgrIface* pLocaleMgr,
+                        const WideString& wsPattern);
+  ~CFGAS_StringFormatter();
+
+  static std::vector<WideString> SplitOnBars(const WideString& wsFormatString);
+
+  FX_LOCALECATEGORY GetCategory() const;
+
+  bool ParseText(const WideString& wsSrcText,
+                 WideString* wsValue) const;
+  bool ParseNum(const WideString& wsSrcNum,
+                WideString* wsValue) const;
+  bool ParseDateTime(const WideString& wsSrcDateTime,
+                     FX_DATETIMETYPE eDateTimeType,
+                     CFX_DateTime* dtValue) const;
+  bool ParseZero(const WideString& wsSrcText) const;
+  bool ParseNull(const WideString& wsSrcText) const;
+
+  bool FormatText(const WideString& wsSrcText,
+                  WideString* wsOutput) const;
+  bool FormatNum(const WideString& wsSrcNum,
+                 WideString* wsOutput) const;
+  bool FormatDateTime(const WideString& wsSrcDateTime,
+                      FX_DATETIMETYPE eDateTimeType,
+                      WideString* wsOutput) const;
+  bool FormatZero(WideString* wsOutput) const;
+  bool FormatNull(WideString* wsOutput) const;
+
+ private:
+  WideString GetTextFormat(WideStringView wsCategory) const;
+  LocaleIface* GetNumericFormat(size_t* iDotIndex,
+                                uint32_t* dwStyle,
+                                WideString* wsPurgePattern) const;
+  FX_DATETIMETYPE GetDateTimeFormat(LocaleIface** pLocale,
+                                    WideString* wsDatePattern,
+                                    WideString* wsTimePattern) const;
+
+  UnownedPtr<LocaleMgrIface> const m_pLocaleMgr;
+  const WideString m_wsPattern;                   // keep pattern string alive.
+  const pdfium::span<const wchar_t> m_spPattern;  // span into |m_wsPattern|.
+};
+
+#endif  // XFA_FGAS_CRT_CFGAS_STRINGFORMATTER_H_
diff --git a/xfa/fgas/crt/cfgas_stringformatter_unittest.cpp b/xfa/fgas/crt/cfgas_stringformatter_unittest.cpp
new file mode 100644
index 0000000..8a9e60c
--- /dev/null
+++ b/xfa/fgas/crt/cfgas_stringformatter_unittest.cpp
@@ -0,0 +1,757 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/crt/cfgas_stringformatter.h"
+
+#include <time.h>
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "core/fpdfapi/page/cpdf_pagemodule.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+
+class CFGAS_StringFormatterTest : public testing::Test {
+ public:
+  CFGAS_StringFormatterTest() {
+    SetTZ("UTC");
+    CPDF_PageModule::Create();
+  }
+
+  ~CFGAS_StringFormatterTest() override { CPDF_PageModule::Destroy(); }
+
+  void TearDown() override {
+    fmt_.reset();
+    mgr_.reset();
+  }
+
+  void SetTZ(const char* tz) {
+#if defined(OS_WIN)
+    _putenv_s("TZ", tz);
+    _tzset();
+#else
+    setenv("TZ", tz, 1);
+    tzset();
+#endif
+  }
+
+  // Note, this re-creates the fmt on each call. If you need to multiple
+  // times store it locally.
+  CFGAS_StringFormatter* fmt(const WideString& locale,
+                             const WideString& pattern) {
+    fmt_.reset();  // Can't outlive |mgr_|.
+    mgr_ = pdfium::MakeUnique<CXFA_LocaleMgr>(nullptr, locale);
+    fmt_ = pdfium::MakeUnique<CFGAS_StringFormatter>(mgr_.get(), pattern);
+    return fmt_.get();
+  }
+
+ protected:
+  std::unique_ptr<CXFA_LocaleMgr> mgr_;
+  std::unique_ptr<CFGAS_StringFormatter> fmt_;
+};
+
+// TODO(dsinclair): Looks like the formatter/parser does not handle the various
+// 'g' flags.
+TEST_F(CFGAS_StringFormatterTest, DateFormat) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {
+      {L"en", L"2002-10-25", L"MMMM DD, YYYY", L"October 25, 2002"},
+      // Note, this is in the doc as 5 but it's wrong and should be 3 by the
+      // example in the Picture Clause Reference section.
+      {L"en", L"20040722", L"'Week of the month is' w",
+       L"Week of the month is 3"},
+      {L"en", L"20040722", L"e 'days after Sunday'", L"4 days after Sunday"},
+      {L"en", L"20040722", L"YYYY-'W'WW-e", L"2004-W30-4"},
+      {L"en", L"20040722", L"E 'days after Saturday'",
+       L"5 days after Saturday"},
+      {L"en", L"2000-01-01", L"EEE, 'the' D 'of' MMMM, YYYY",
+       L"Sat, the 1 of January, 2000"},
+      {L"en", L"2000-01-01", L"EEEE, 'the' D 'of' MMMM, YYYY",
+       L"Saturday, the 1 of January, 2000"},
+      {L"en", L"19991202", L"MM/D/YY", L"12/2/99"},
+      {L"en", L"19990110", L"MMM D, YYYY", L"Jan 10, 1999"},
+      {L"en", L"19990202", L"J", L"33"},
+      {L"en", L"19990202", L"JJJ", L"033"},
+      {L"en", L"19991231", L"J", L"365"},
+      {L"en", L"20001231", L"J", L"366"},
+      {L"en", L"19990501", L"J", L"121"},
+      {L"en", L"19990901", L"J", L"244"},
+      {L"en", L"19990228", L"J", L"59"},
+      {L"en", L"20000229", L"J", L"60"},
+      {L"en", L"21000501", L"J", L"121"},
+      {L"en", L"19990102", L"M", L"1"},
+      {L"en", L"19990102", L"MMM", L"Jan"},
+      {L"en", L"19990102", L"YYYY G", L"1999 AD"},
+      // Week 01 of the year is the week containing Jan 04.
+      // {L"en", L"19990102", L"WW", L"00"},  -- Returns 01 incorrectly
+      // {L"en", L"19990104", L"WW", L"01"},  -- Returns 02 incorrectly
+      // The ?*+ should format as whitespace.
+      // {L"en", L"19990104", L"YYYY?*+MM", L"1999   01"},
+      // {L"en", L"1999-07-16", L"date{DD/MM/YY} '('date{MMM DD, YYYY}')'",
+      //  L"16/07/99 (Jul 16, 1999)"},
+      {L"de_CH", L"20041030", L"D. MMMM YYYY", L"30. Oktober 2004"},
+      {L"fr_CA", L"20041030", L"D MMMM YYYY", L"30 octobre 2004"},
+      {L"en", L"2002-10-25", L"date(fr){DD MMMM, YYYY}", L"25 octobre, 2002"},
+      {L"en", L"2002-10-25", L"date(es){EEEE, D 'de' MMMM 'de' YYYY}",
+       L"viernes, 25 de octubre de 2002"},
+      // {L"en", L"2002-20-25", L"date.long(fr)()", L"25 octobre, 2002"},
+      // {L"ja", L"2003-11-03", L"gY/M/D", L"H15/11/3"},
+      // {L"ja", L"1989-01-08", L"ggY-M-D", L"\u5e731-1-8"},
+      // {L"ja", L"1989-11-03", L"gggYY/MM/DD", L"\u5e73\u621089/11/03"},
+  };
+  // Note, none of the full width date symbols are listed here
+  // as they are not supported. In theory there are the full width versions
+  // of DDD, DDDD, MMM, MMMM, E, e, gg, YYY, YYYYY.
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(
+        fmt(tests[i].locale, tests[i].pattern)
+            ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_Date, &result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, TimeFormat) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {{L"en", L"01:01:11", L"h:M A", L"1:1 AM"},
+               {L"en", L"13:01:11", L"h:M A", L"1:1 PM"},
+               {L"en", L"01:01:11", L"hh:MM:SS A", L"01:01:11 AM"},
+               {L"en", L"13:01:11", L"hh:MM:SS A", L"01:01:11 PM"},
+               {L"en", L"01:01:11", L"hh:MM:SS A Z", L"01:01:11 AM GMT-02:00"},
+               {L"en", L"01:01:11", L"hh:MM:SS A z", L"01:01:11 AM -02:00"},
+               // {L"en", L"01:01:11", L"hh:MM:SS A zz", L"01:01:11 AM GMT"},
+               // Should change ?*+ into ' ' when formatting.
+               // {L"en", L"01:01:11", L"hh:MM:SS?*+A", L"01:01:11   AM"},
+               {L"en", L"12:01:01", L"k:MM:SS", L"12:01:01"},
+               {L"en", L"14:01:01", L"k:MM:SS", L"2:01:01"},
+               {L"en", L"12:01:11", L"kk:MM", L"12:01"},
+               {L"en", L"14:01:11", L"kk:MM", L"02:01"},
+               {L"en", L"12:01:11 +04:30", L"kk:MM", L"05:31"},
+               {L"en", L"12:01:11", L"kk:MM A", L"12:01 PM"},
+               {L"en", L"00:01:01", L"H:M:S", L"0:1:1"},
+               {L"en", L"13:02:11", L"H:M:S", L"13:2:11"},
+               {L"en", L"00:01:11.001", L"HH:M:S.FFF", L"00:1:11.001"},
+               {L"en", L"13:02:11", L"HH:M", L"13:2"},
+               {L"en", L"00:01:11", L"K:M", L"24:1"},
+               {L"en", L"00:02:11", L"KK:M", L"24:2"},
+               {L"en", L"11:11:11", L"HH:MM:SS 'o''clock' A Z",
+                L"11:11:11 o'clock AM GMT-02:00"},
+               {L"en", L"14:30:59", L"h:MM A", L"2:30 PM"},
+               {L"en", L"14:30:59", L"HH:MM:SS A Z", L"14:30:59 PM GMT-02:00"}};
+  // Note, none of the full width time symbols are listed here
+  // as they are not supported. In theory there are the full
+  // width versions of kkk, kkkk, HHH, HHHH, KKK, KKKK, MMM, MMMM,
+  // SSS, SSSS plus 2 more that the spec apparently forgot to
+  // list the symbol.
+
+  // The z modifier only appends if the TZ is outside of +0
+  SetTZ("UTC+2");
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(
+        fmt(tests[i].locale, tests[i].pattern)
+            ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_Time, &result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+
+  SetTZ("UTC");
+}
+
+TEST_F(CFGAS_StringFormatterTest, DateTimeFormat) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {
+      {L"en", L"1999-07-16T10:30Z",
+       L"'At' time{HH:MM Z} 'on' date{MMM DD, YYYY}",
+       L"At 10:30 GMT on Jul 16, 1999"},
+      {L"en", L"1999-07-16T10:30", L"'At' time{HH:MM} 'on' date{MMM DD, YYYY}",
+       L"At 10:30 on Jul 16, 1999"},
+      {L"en", L"1999-07-16T10:30Z",
+       L"time{'At' HH:MM Z} date{'on' MMM DD, YYYY}",
+       L"At 10:30 GMT on Jul 16, 1999"},
+      {L"en", L"1999-07-16T10:30Z",
+       L"time{'At 'HH:MM Z}date{' on 'MMM DD, YYYY}",
+       L"At 10:30 GMT on Jul 16, 1999"},
+      {L"en", L"9111T1111:", L"MMM D, YYYYTh:MM:SS A",
+       L"Jan 1, 9111 11:11:00 AM"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
+                    ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_DateTime,
+                                     &result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, TimeDateFormat) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {
+      {L"en", L"1999-07-16T10:30Z",
+       L"'At' time{HH:MM Z} 'on' date{MMM DD, YYYY}",
+       L"At 10:30 GMT on Jul 16, 1999"},
+      {L"en", L"1999-07-16T10:30", L"'At' time{HH:MM} 'on' date{MMM DD, YYYY}",
+       L"At 10:30 on Jul 16, 1999"},
+      {L"en", L"1999-07-16T10:30Z",
+       L"time{'At' HH:MM Z} date{'on' MMM DD, YYYY}",
+       L"At 10:30 GMT on Jul 16, 1999"},
+      {L"en", L"1999-07-16T10:30Z",
+       L"time{'At 'HH:MM Z}date{' on 'MMM DD, YYYY}",
+       L"At 10:30 GMT on Jul 16, 1999"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
+                    ->FormatDateTime(tests[i].input, FX_DATETIMETYPE_TimeDate,
+                                     &result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, DateParse) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    CFX_DateTime output;
+  } tests[] = {
+      {L"en", L"12/2/99", L"MM/D/YY", CFX_DateTime(1999, 12, 2, 0, 0, 0, 0)},
+      {L"en", L"2/2/99", L"M/D/YY", CFX_DateTime(1999, 2, 2, 0, 0, 0, 0)},
+      {L"en", L"2/2/10", L"M/D/YY", CFX_DateTime(2010, 2, 2, 0, 0, 0, 0)},
+      {L"en", L"Jan 10, 1999", L"MMM D, YYYY",
+       CFX_DateTime(1999, 1, 10, 0, 0, 0, 0)},
+      {L"en", L"Jan 10, 1999 AD", L"MMM D, YYYY G",
+       CFX_DateTime(1999, 1, 10, 0, 0, 0, 0)},
+      // TODO(dsinclair): Should this be -2 instead of 2?
+      {L"en", L"Jan 10, 0002 BC", L"MMM D, YYYY G",
+       CFX_DateTime(2, 1, 10, 0, 0, 0, 0)},
+      {L"en", L"October 25, 2002", L"MMMM DD, YYYY",
+       CFX_DateTime(2002, 10, 25, 0, 0, 0, 0)},
+      // TODO(dsinclair): The J and JJJ are ignored during parsing when they
+      // could be turned back into a date.
+      {L"en", L"1999-33", L"YYYY-J", CFX_DateTime(1999, 1, 1, 0, 0, 0, 0)},
+      {L"en", L"1999-033", L"YYYY-JJJ", CFX_DateTime(1999, 1, 1, 0, 0, 0, 0)},
+      {L"de_CH", L"30. Oktober 2004", L"D. MMMM YYYY",
+       CFX_DateTime(2004, 10, 30, 0, 0, 0, 0)},
+      {L"fr_CA", L"30 octobre 2004", L"D MMMM YYYY",
+       CFX_DateTime(2004, 10, 30, 0, 0, 0, 0)},
+      {L"en", L"Saturday, the 1 of January, 2000",
+       L"EEEE, 'the' D 'of' MMMM, YYYY", CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
+      {L"en", L"Sat, the 1 of January, 2000", L"EEE, 'the' D 'of' MMMM, YYYY",
+       CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
+      {L"en", L"7, the 1 of January, 2000",  // 7 == Saturday as 1 == Sunday
+       L"E, 'the' D 'of' MMMM, YYYY", CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
+      {L"en", L"6, the 1 of January, 2000",  // 6 == Saturday as 1 == Monday
+       L"e, 'the' D 'of' MMMM, YYYY", CFX_DateTime(2000, 1, 1, 0, 0, 0, 0)},
+      {L"en", L"2004-07-22 Week of the month is 3",
+       L"YYYY-MM-DD 'Week of the month is' w",
+       CFX_DateTime(2004, 7, 22, 0, 0, 0, 0)},
+      {L"en", L"2004-07-22 Week of the year is 03",
+       L"YYYY-MM-DD 'Week of the year is' WW",
+       CFX_DateTime(2004, 7, 22, 0, 0, 0, 0)}
+      // {L"ja", L"H15/11/3", L"gY/M/D", CFX_DateTime(2003, 11, 3, 0, 0, 0, 0)},
+      // {L"ja", L"\u5e731-1-8", L"ggY-M-D", CFX_DateTime(1989, 1, 8, 0, 0, 0,
+      // 0)}, {L"ja", L"\u5e73\u621089/11/03", L"gggYY/MM/DD",
+      //  CFX_DateTime(1989, 11, 3, 0, 0, 0, 0)},
+      // {L"ja", L"u337b99/01/08", L"\u0067\u0067YY/MM/DD",
+      //  CFX_DateTime(1999, 1, 8, 0, 0, 0, 0)}
+  };
+  // Note, none of the full width date symbols are listed here as they are
+  // not supported. In theory there are the full width versions of DDD,
+  // DDDD, MMM, MMMM, E, e, gg, YYY, YYYYY.
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    CFX_DateTime result;
+    EXPECT_TRUE(
+        fmt(tests[i].locale, tests[i].pattern)
+            ->ParseDateTime(tests[i].input, FX_DATETIMETYPE_Date, &result));
+    EXPECT_EQ(tests[i].output, result) << " TEST: " << i;
+  }
+}
+
+// TODO(dsinclair): GetDateTimeFormat is broken and doesn't allow just returning
+// a parsed Time. It will assume it's a Date. The method needs to be re-written.
+// TEST_F(CFGAS_StringFormatterTest, TimeParse) {
+//   struct {
+//     const wchar_t* locale;
+//     const wchar_t* input;
+//     const wchar_t* pattern;
+//     CFX_DateTime output;
+//   } tests[] = {
+//       {L"en", L"18:00", L"HH:MM", CFX_DateTime(0, 0, 0, 18, 0, 0, 0)},
+//       {L"en", L"12.59 Uhr", L"H.MM 'Uhr'", CFX_DateTime(0, 0, 0, 12, 59, 0,
+//       0)}, {L"en", L"1:05:10 PM PST", L"h:MM:SS A Z",
+//        CFX_DateTime(0, 0, 0, 17, 05, 10, 0)}};
+//   // Note, none of the full width date symbols are listed here as they are
+//   // not supported. In theory there are the full width versions of kkk,
+//   // kkkk, HHH, HHHH, KKK, KKKK, MMM, MMMM, SSS, SSSS plus 2 more that the
+//   // spec apparently forgot to list the symbol.
+
+//   for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+//     CFX_DateTime result;
+//     EXPECT_TRUE(fmt(tests[i].locale)
+//                     ->ParseDateTime(tests[i].input, tests[i].pattern,
+//                                     FX_DATETIMETYPE_Time, &result));
+//     EXPECT_EQ(tests[i].output, result) << " TEST: " << i;
+//   }
+// }
+
+TEST_F(CFGAS_StringFormatterTest, SplitFormatString) {
+  std::vector<WideString> results = CFGAS_StringFormatter::SplitOnBars(L"");
+  EXPECT_EQ(1UL, results.size());
+  EXPECT_TRUE(results[0].IsEmpty());
+
+  results = CFGAS_StringFormatter::SplitOnBars(L"|");
+  EXPECT_EQ(2UL, results.size());
+  EXPECT_TRUE(results[0].IsEmpty());
+  EXPECT_TRUE(results[1].IsEmpty());
+
+  results = CFGAS_StringFormatter::SplitOnBars(
+      L"null{'No|data'} | null{} | text{999*9999} | text{999*999*9999}");
+  EXPECT_EQ(4UL, results.size());
+
+  const wchar_t* patterns[] = {L"null{'No|data'} ", L" null{} ",
+                               L" text{999*9999} ", L" text{999*999*9999}"};
+  for (size_t i = 0; i < results.size(); ++i) {
+    EXPECT_STREQ(patterns[i], results[i].c_str());
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, NumParse) {
+  struct TestCase {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  };
+
+  static const TestCase tests[] = {
+      // {L"en", L"€100.00", L"num(en_GB){$z,zz9.99}", L"100"},
+      // {L"en", L"1050", L"99V99", L"10.50"},
+      // {L"en", L"3125", L"99V99", L"31.25"},
+      {L"en", L"12.345e3", L"99.999E", L"12345.000000"},
+      {L"en", L"12.345e+3", L"99.999E", L"12345.000000"},
+      {L"en", L"12.345E-2", L"99.999E", L"0.123450"},
+      // TODO(dsinclair): Returns 0.000?
+      // {L"en", L"12e-2", L"99E", L"0.12"},
+      {L"en", L"150", L"z999", L"150"},
+      {L"en", L"150.50$", L"zzz.zz$", L"150.50"},
+      {L"en", L"0150", L"z999", L"0150"},
+      {L"en", L"123CR", L"999cr", L"-123"},
+      {L"en", L"123", L"999cr", L"123"},
+      {L"en", L"123CR", L"999CR", L"-123"},
+      {L"en", L"123  ", L"999CR", L"123"},
+      {L"en", L"123DB", L"999db", L"-123"},
+      {L"en", L"123", L"999db", L"123"},
+      {L"en", L"123DB", L"999DB", L"-123"},
+      {L"en", L"123  ", L"999DB", L"123"},
+      {L"en", L"123.5CR", L"999.9cr", L"-123.5"},
+      {L"en", L"123.5", L"999.9cr", L"123.5"},
+      {L"en", L"123.5CR", L"999.9CR", L"-123.5"},
+      // {L"en", L"123.5  ", L"999.9CR", L"123.5"},
+      {L"en", L"123.5DB", L"999.9db", L"-123.5"},
+      {L"en", L"123.5", L"999.9db", L"123.5"},
+      {L"en", L"123.5DB", L"999.9DB", L"-123.5"},
+      // {L"en", L"123.5  ", L"999.9DB", L"123.5"},
+      {L"en", L"10.50", L"z,zz9.99", L"10.50"},
+      {L"en", L"3,125.00", L"z,zz9.99", L"3125.00"},
+      {L"en", L"$1,234.00", L"$z,zz9.99DB", L"1234.00"},
+      // TODO(dsinclair): Comes out as 1234 instead of -1234.
+      // {L"en", L"$,1234.00DB", L"$z,zz9.99DB", L"-1234.00"},
+      {L"en", L"1.234", L"zz9.zzz", L"1.234"},
+      {L"en", L"1 text", L"num{z 'text'}", L"1"},
+      {L"en", L"1.234 text", L"z.zzz 'text'", L"1.234"},
+      {L"en", L"  1.234", L"ZZ9.ZZZ", L"1.234"},
+      {L"en", L"12.345", L"zz9.zzz", L"12.345"},
+      {L"en", L" 12.345", L"ZZ9.ZZZ", L"12.345"},
+      {L"en", L"123.456", L"zz9.zzz", L"123.456"},
+      {L"en", L"123.456", L"ZZ9.ZZZ", L"123.456"},
+      {L"en", L"123.456-", L"ZZ9.ZZZS", L"-123.456"},
+      {L"en", L"123.456+", L"ZZ9.ZZZS", L"123.456"},
+      {L"en", L"123.456 ", L"ZZ9.ZZZS", L"123.456"},
+      {L"en", L"123.456-", L"ZZ9.ZZZS", L"-123.456"},
+      {L"en", L"123.456+", L"ZZ9.ZZZS", L"123.456"},
+      {L"en", L"123", L"zz9.zzz", L"123"},
+      {L"en", L"123.", L"ZZ9.ZZZ", L"123."},
+      {L"en", L"123.", L"zz9.zzz", L"123."},
+      {L"en", L"123.", L"ZZ9.ZZZ", L"123."},
+      {L"en", L"123.0", L"zz9.zzz", L"123.0"},
+      {L"en", L"123.0", L"ZZ9.ZZZ", L"123.0"},
+      {L"en", L"123.000", L"zz9.zzz", L"123.000"},
+      {L"en", L"123.000", L"ZZ9.ZZZ", L"123.000"},
+      {L"en", L"12,345.67", L"zzz,zz9.88888888", L"12345.67"},
+      {L"en", L"12,345.0000", L"zzz,zz9.88888888", L"12345.0000"},
+      {L"en", L"12,345.6789", L"zzz,zz9.8", L"12345.6789"},
+      {L"en", L"12,345.", L"zzz,zz9.8", L"12345."},
+      {L"en", L"123,456.000", L"zzz,zz9.8888", L"123456.000"},
+      {L"en", L"123,456.0", L"zzz,zz9.8888", L"123456.0"},
+      {L"en", L"123,456", L"zzz,zz9.8888", L"123456"},
+      {L"en", L"123,456", L"ZZZ,ZZ9.88", L"123456"},
+      {L"en", L"12,345.67", L"zzz,zz9.88888888", L"12345.67"},
+      {L"en", L"12,345.0000", L"zzz,zz9.88888888", L"12345.0000"},
+      {L"en", L"12,345.6789", L"zzz,zz9.8", L"12345.6789"},
+      {L"en", L"12,345.", L"zzz,zz9.8", L"12345."},
+      // TODO(dsinclair): Parses to 0
+      // {L"en", L"12%", L"zz9.%%", L".12"},
+      {L"en", L"1,234.50%", L"zzz,zz9.99%%", L"12.345"},
+      // {L"en", L"-00123", L"S999v99", L"-1.23"},
+      {L"en", L" 001.23", L"S999V99", L"001.23"},
+      // {L"en", L" 123.00", L"S999V99", L"123"},
+      {L"en", L"  12.30", L"SZZ9.99", L"12.30"},
+      {L"en", L"- 12.30", L"SZ99.99", L"-12.30"},
+      {L"en", L"123.00", L"szz9.99", L"123.00"},
+      {L"en", L"-123.00", L"szz9.99", L"-123.00"},
+      // {L"en", L"$  1,234.00  ", L"$ZZ,ZZ9.99CR", L"1234"},
+      // {L"en", L"$  1,234.00CR", L"$ZZ,ZZ9.99CR", L"-1234"},
+      // {L"en", L"$1,23400", L"$z,zz9.99DB", L"1234"},
+      {L"en", L"$1,234.00DB", L"$z,zz9.99DB", L"-1234.00"},
+      {L"en",
+       L"1\xA0"
+       L"234",
+       L"num(fr){z,zzz}", L"1234"},
+      // TODO(dsinclair): Parses to blank
+      // {L"en", L"1,234%", L"num.percent{}", L"12.34"},
+      // {L"en", L"1\xA0" L"234%%", L"num(fr).percent{}", L"12.34"},
+      // TODO(dsinclair): Parses to blank
+      // {L"en", L"1,234%", L"num{9,999%%}", L"12.34"},
+      {L"fr",
+       L"123\xA0"
+       L"456",
+       L"zzz,zzz", L"123456"},
+      {L"en", L"12%", L"zz%", L"0.12"},
+      {L"en", L"(123", L"(zzz", L"-123"},
+      {L"en", L"123)", L"zzz)", L"-123"},
+      {L"en", L"(123)", L"(zzz)", L"-123"},
+      {L"en", L"123 ", L"zzz)", L"123"},
+      {L"en", L" 123", L"(zzz", L"123"},
+      {L"en", L" 123 ", L"(zzz)", L"123"},
+      {L"en", L"123.5(", L"zzz.z(", L"-123.5"},
+      {L"en", L"123.5)", L"zzz.z)", L"-123.5"},
+      {L"en", L"123.5 ", L"zzz.z)", L"123.5"},
+      {L"en", L"123.5 ", L"zzz.z(", L"123.5"},
+      {L"en", L"123.545,4", L"zzz.zzz,z", L"123.5454"},
+      // https://crbug.com/938724
+      {L"en", L"1", L" num.().().}", L"1"},
+  };
+
+  static const TestCase failures[] = {
+      // https://crbug.com/pdfium/1260
+      {L"en", L"..", L"VC", L"."},
+
+      // https://crbug.com/938626
+      {L"en", L"PDF", L"num( ", L"."},
+
+      // https://crbug.com/945836
+      {L"en", L"9.E99999999999", L"EdEE.E999", L""},
+
+      // https://crbug.com/947188
+      {L"en", L"-3.E98998998 ", L" 35EEEE.EE98", L""},
+  };
+
+  for (const auto& test : tests) {
+    WideString result;
+    EXPECT_TRUE(fmt(test.locale, test.pattern)->ParseNum(test.input, &result))
+        << " TEST: " << test.input << ", " << test.pattern;
+    EXPECT_STREQ(test.output, result.c_str())
+        << " TEST: " << test.input << ", " << test.pattern;
+  }
+
+  for (const auto& test : failures) {
+    WideString result;
+    EXPECT_FALSE(fmt(test.locale, test.pattern)->ParseNum(test.input, &result))
+        << " TEST: " << test.input << ", " << test.pattern;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, NumFormat) {
+  struct TestCase {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  };
+
+  static const TestCase tests[] = {
+      {L"en", L"1.234", L"zz9.zzz", L"1.234"},
+      {L"en", L"1", L"num{z 'text'}", L"1 text"},
+      {L"en", L"1", L"num{'text' z}", L"text 1"},
+      {L"en", L"1.234", L"ZZ9.ZZZ", L"  1.234"},
+      {L"en", L"12.345", L"zz9.zzz", L"12.345"},
+      {L"en", L"12.345", L"ZZ9.ZZZ", L" 12.345"},
+      {L"en", L"123.456", L"zz9.zzz", L"123.456"},
+      {L"en", L"123.456", L"ZZ9.ZZZ", L"123.456"},
+      {L"en", L"123", L"zz9.zzz", L"123"},
+      {L"en", L"123", L"ZZ9.ZZZ", L"123.000"},
+      {L"en", L"123.", L"zz9.zzz", L"123."},
+      {L"en", L"123.", L"ZZ9.ZZZ", L"123.000"},
+      {L"en", L"123.0", L"zz9.zzz", L"123"},
+      {L"en", L"123.0", L"ZZ9.ZZZ", L"123.000"},
+      {L"en", L"123.000", L"zz9.zzz", L"123"},
+      {L"en", L"123.000", L"ZZ9.ZZZ", L"123.000"},
+      // {L"en", L"12345.67", L"zzz,zz9.88888888", L"12,345.67"},
+      // {L"en", L"12345.0000", L"zzz,zz9.88888888", L"12,345.0000"},
+      // {L"en", L"12345.6789", L"zzz,zz9.8", L"12,345.6789"},
+      // {L"en", L"12345.", L"zzz,zz9.8", L"12,345"},
+      // {L"en", L"123456.000", L"zzz,zz9.8888", L"123,456.000"},
+      // {L"en", L"123456.0", L"zzz,zz9.8888", L"123,456.0"},
+      {L"en", L"123456", L"zzz,zz9.8888", L"123,456"},
+      {L"en", L"123456", L"ZZZ,ZZ9.88", L"123,456"},
+      // {L"en", L"12345.67", L"zzz,zz9.88888888", L"12,345.67"},
+      // {L"en", L"12345.0000", L"zzz,zz9.88888888", L"12,345.0000"},
+      // {L"en", L"12345.6789", L"zzz,zz9.8", L"12,345.6789"},
+      // {L"en", L"12345.", L"zzz,zz9.8", L"12,345"},
+      // {L"en", L"12%%", L"zz9.%%", L"12%%"},
+      // {L"en", L"1,234.5%%", L"zzz,zz9.99%%", L"1,234.50%%"},
+      {L"en", L"-1.23", L"S999v99", L"-00123"},
+      {L"en", L"1.23", L"S999V99", L" 001.23"},
+      {L"en", L"123", L"S999V99", L" 123.00"},
+      {L"en", L"12.3", L"SZZ9.99", L"  12.30"},
+      {L"en", L"-12.3", L"SZ99.99", L"- 12.30"},
+      {L"en", L"123", L"szz9.99", L"123.00"},
+      {L"en", L"-123", L"szz9.99", L"-123.00"},
+      // {L"en", L"1234", L"$ZZ,ZZ9.99CR", L"$  1,234.00  "},
+      // {L"en", L"-1234", L"$ZZ,ZZ9.99CR", L"$  1,234.00CR"},
+      // {L"en", L"1234", L"$z,zz9.99DB", L"$1,234.00"},
+      {L"en", L"-1234", L"$z,zz9.99DB", L"$1,234.00DB"},
+      {L"en", L"12345", L"99.999E", L"12.345E+3"},
+      {L"en", L"12345", L"99999E", L"12345E+0"},
+      {L"en", L".12345", L"99.999E", L"12.345E-2"},
+      {L"en", L"12345", L"99,999", L"12,345"},
+      {L"en", L"1234", L"num(fr){z,zzz}",
+       L"1\xA0"
+       L"234"},
+      {L"en", L"12.34", L"num.percent{}", L"1,234%"},
+      {L"en", L"12.34", L"num(fr).percent{}",
+       L"1\xA0"
+       L"234%"},
+      // {L"en", L"12.34", L"num{9,999%%}", L"1,234%"},
+      {L"en", L"-123", L"zzzCR", L"123CR"},
+      {L"en", L"123", L"zzzCR", L"123  "},
+      {L"en", L"-123", L"zzzcr", L"123CR"},
+      {L"en", L"123", L"zzzcr", L"123"},
+      {L"en", L"123", L"zzz$", L"123$"},
+      {L"en", L"-123.5", L"zzz.zCR", L"123.5CR"},
+      {L"en", L"123.5", L"zzz.zCR", L"123.5  "},
+      {L"en", L"-123.5", L"zzz.zcr", L"123.5CR"},
+      {L"en", L"123.5", L"zzz.zcr", L"123.5"},
+
+      {L"en", L"-123.5", L"999.9db", L"123.5db"},
+      {L"en", L"123.5", L"999.9db", L"123.5"},
+      {L"en", L"-123.5", L"999.9DB", L"123.5DB"},
+      {L"en", L"123.5", L"999.9DB", L"123.5  "},
+
+      {L"en", L"-123", L"(zzz", L"(123"},
+      // {L"en", L"-123", L"zzz)", L"123)"},
+      {L"en", L"-123", L"(zzz)", L"(123)"},
+      {L"en", L"123", L"zzz)", L"123 "},
+      {L"en", L"123", L"(zzz", L" 123"},
+      {L"en", L"123", L"(zzz)", L" 123 "},
+      {L"en", L"-123.5", L"zzz.z(", L"123.5("},
+      // {L"en", L"-123.5", L"zzz.z)", L"123.5)"},
+      {L"en", L"123.5", L"zzz.z)", L"123.5 "},
+      {L"en", L"123.5", L"zzz.z(", L"123.5 "},
+      // https://crbug.com/pdfium/1233
+      {L"en", L"1", L"r9", L"r1"},
+      {L"en", L"1", L"R9", L"R1"},
+      {L"en", L"1", L"b9", L"b1"},
+      {L"en", L"1", L"B9", L"B1"},
+      {L"en", L"1", L"9.c", L"1"},
+      {L"en", L"1", L"9.C", L"1"},
+      {L"en", L"1", L"9.d", L"1"},
+      {L"en", L"1", L"9.D", L"1"},
+      // https://crbug.com/pdfium/1244
+      {L"en", L"1", L"E", L"1"},
+      {L"en", L"0", L"E", L"0"},
+      {L"en", L"-1", L"E", L"-1"},
+      {L"en", L"900000000000000000000", L"E", L"900,000,000,000,000,000,000"},
+      // TODO(tsepez): next one seems wrong
+      // {L"en", L".000000000000000000009", L"E", L"9"},
+      // https://crbug.com/938724
+      {L"en", L"1", L"| num.().().", L"1"},
+      // https://crbug.com/942449
+      {L"en", L"1", L"9.", L"1"},
+  };
+
+  static const TestCase failures[] = {
+      // https://crbug.com/pdfium/1271
+      {L"en", L"1", L"num.{E", L""},
+  };
+
+  for (const auto& test : tests) {
+    WideString result;
+    EXPECT_TRUE(fmt(test.locale, test.pattern)->FormatNum(test.input, &result))
+        << " TEST: " << test.input << ", " << test.pattern;
+    EXPECT_STREQ(test.output, result.c_str())
+        << " TEST: " << test.input << ", " << test.pattern;
+  }
+
+  for (const auto& test : failures) {
+    WideString result;
+    EXPECT_FALSE(fmt(test.locale, test.pattern)->FormatNum(test.input, &result))
+        << " TEST: " << test.input << ", " << test.pattern;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, TextParse) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {// TODO(dsinclair) Missing support for the global modifiers:
+               //  ? - wildcard
+               //  * - zero or more whitespace
+               //  + - one or more whitespace
+               // {L"en", L"555-1212", L"text(th_TH){999*9999}", L"5551212"},
+               {L"en", L"ABC-1234-5", L"AAA-9999-X", L"ABC12345"},
+               {L"en", L"ABC-1234-D", L"AAA-9999-X", L"ABC1234D"},
+               {L"en", L"A1C-1234-D", L"OOO-9999-X", L"A1C1234D"},
+               {L"en", L"A1C-1234-D", L"000-9999-X", L"A1C1234D"},
+               {L"en", L"A1C-1234-D text", L"000-9999-X 'text'", L"A1C1234D"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
+                    ->ParseText(tests[i].input, &result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, InvalidTextParse) {
+  // Input does not match mask.
+  WideString result;
+  EXPECT_FALSE(fmt(L"en", L"AAA-9999-X")->ParseText(L"123-4567-8", &result));
+}
+
+TEST_F(CFGAS_StringFormatterTest, TextFormat) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {
+      {L"en", L"K1S5K2", L"A9A 9A9", L"K1S 5K2"},
+      {L"en", L"K1S5K2", L"text(fr){A9A 9A9}", L"K1S 5K2"},
+      {L"en", L"6135551212", L"'+1 ('9\u002399') '999-9999",
+       L"+1 (6#13) 555-1212"},
+      {L"en", L"6135551212", L"999.999.9999", L"613.555.1212"},
+      {L"en", L"6135551212", L"999\u0023999\u002A9999", L"613#555*1212"},
+      {L"en", L"K1#5K2", L"00X OO9", L"K1# 5K2"},
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)
+                    ->FormatText(tests[i].input, &result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, NullParse) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+  } tests[] = {
+      {L"en", L"", L"null{}"},
+      {L"en", L"No data", L"null{'No data'}"},
+  };
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(
+        fmt(tests[i].locale, tests[i].pattern)->ParseNull(tests[i].input))
+        << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, NullFormat) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {{L"en", L"null{'n/a'}", L"n/a"}, {L"en", L"null{}", L""}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)->FormatNull(&result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, ZeroParse) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+  } tests[] = {{L"en", L"", L"zero{}"},
+               {L"en", L"9", L"zero{9}"},
+               {L"en", L"a", L"zero{'a'}"}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    EXPECT_TRUE(
+        fmt(tests[i].locale, tests[i].pattern)->ParseZero(tests[i].input))
+        << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, ZeroFormat) {
+  struct {
+    const wchar_t* locale;
+    const wchar_t* input;
+    const wchar_t* pattern;
+    const wchar_t* output;
+  } tests[] = {// TODO(dsinclair): The zero format can take a number specifier
+               // which we don't take into account.
+               // {L"en", L"", L"zero {9}", L""},
+               // {L"en", L"0", L"zero {9}", L"0"},
+               // {L"en", L"0.0", L"zero{9}", L"0"},
+               {L"en", L"0", L"zero{}", L""}};
+
+  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
+    WideString result;
+    EXPECT_TRUE(fmt(tests[i].locale, tests[i].pattern)->FormatZero(&result));
+    EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i;
+  }
+}
+
+TEST_F(CFGAS_StringFormatterTest, GetCategory) {
+  EXPECT_EQ(FX_LOCALECATEGORY_Unknown,
+            fmt(L"en", L"'just text'")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Null, fmt(L"en", L"null{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Zero, fmt(L"en", L"zero{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Num, fmt(L"en", L"num{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Text, fmt(L"en", L"text{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_DateTime,
+            fmt(L"en", L"datetime{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Time, fmt(L"en", L"time{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Date, fmt(L"en", L"date{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_DateTime,
+            fmt(L"en", L"time{} date{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_DateTime,
+            fmt(L"en", L"date{} time{}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Num, fmt(L"en", L"num(en_GB){}")->GetCategory());
+  EXPECT_EQ(FX_LOCALECATEGORY_Date, fmt(L"en", L"date.long{}")->GetCategory());
+}
diff --git a/xfa/fgas/crt/locale_iface.h b/xfa/fgas/crt/locale_iface.h
new file mode 100644
index 0000000..b5aecdb
--- /dev/null
+++ b/xfa/fgas/crt/locale_iface.h
@@ -0,0 +1,70 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_CRT_LOCALE_IFACE_H_
+#define XFA_FGAS_CRT_LOCALE_IFACE_H_
+
+#include "core/fxcrt/cfx_datetime.h"
+#include "core/fxcrt/fx_string.h"
+
+enum FX_LOCALEDATETIMESUBCATEGORY {
+  FX_LOCALEDATETIMESUBCATEGORY_Default,
+  FX_LOCALEDATETIMESUBCATEGORY_Short,
+  FX_LOCALEDATETIMESUBCATEGORY_Medium,
+  FX_LOCALEDATETIMESUBCATEGORY_Full,
+  FX_LOCALEDATETIMESUBCATEGORY_Long,
+};
+
+enum FX_LOCALENUMSUBCATEGORY {
+  FX_LOCALENUMPATTERN_Percent,
+  FX_LOCALENUMPATTERN_Currency,
+  FX_LOCALENUMPATTERN_Decimal,
+  FX_LOCALENUMPATTERN_Integer,
+};
+
+enum FX_LOCALECATEGORY {
+  FX_LOCALECATEGORY_Unknown,
+  FX_LOCALECATEGORY_Date,
+  FX_LOCALECATEGORY_Time,
+  FX_LOCALECATEGORY_DateTime,
+  FX_LOCALECATEGORY_Num,
+  FX_LOCALECATEGORY_Text,
+  FX_LOCALECATEGORY_Zero,
+  FX_LOCALECATEGORY_Null,
+};
+
+enum FX_DATETIMETYPE {
+  FX_DATETIMETYPE_Unknown,
+  FX_DATETIMETYPE_Date,
+  FX_DATETIMETYPE_Time,
+  FX_DATETIMETYPE_DateTime,
+  FX_DATETIMETYPE_TimeDate,
+};
+
+class LocaleIface {
+ public:
+  virtual ~LocaleIface() = default;
+
+  virtual WideString GetName() const = 0;
+  virtual WideString GetDecimalSymbol() const = 0;
+  virtual WideString GetGroupingSymbol() const = 0;
+  virtual WideString GetPercentSymbol() const = 0;
+  virtual WideString GetMinusSymbol() const = 0;
+  virtual WideString GetCurrencySymbol() const = 0;
+  virtual WideString GetDateTimeSymbols() const = 0;
+  virtual WideString GetMonthName(int32_t nMonth, bool bAbbr) const = 0;
+  virtual WideString GetDayName(int32_t nWeek, bool bAbbr) const = 0;
+  virtual WideString GetMeridiemName(bool bAM) const = 0;
+  virtual FX_TIMEZONE GetTimeZone() const = 0;
+  virtual WideString GetEraName(bool bAD) const = 0;
+  virtual WideString GetDatePattern(
+      FX_LOCALEDATETIMESUBCATEGORY eType) const = 0;
+  virtual WideString GetTimePattern(
+      FX_LOCALEDATETIMESUBCATEGORY eType) const = 0;
+  virtual WideString GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const = 0;
+};
+
+#endif  // XFA_FGAS_CRT_LOCALE_IFACE_H_
diff --git a/xfa/fgas/crt/locale_mgr_iface.h b/xfa/fgas/crt/locale_mgr_iface.h
new file mode 100644
index 0000000..42f5ada
--- /dev/null
+++ b/xfa/fgas/crt/locale_mgr_iface.h
@@ -0,0 +1,22 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_CRT_LOCALE_MGR_IFACE_H_
+#define XFA_FGAS_CRT_LOCALE_MGR_IFACE_H_
+
+#include "core/fxcrt/fx_string.h"
+
+class LocaleIface;
+
+class LocaleMgrIface {
+ public:
+  virtual ~LocaleMgrIface() = default;
+
+  virtual LocaleIface* GetDefLocale() = 0;
+  virtual LocaleIface* GetLocaleByName(const WideString& wsLCID) = 0;
+};
+
+#endif  // XFA_FGAS_CRT_LOCALE_MGR_IFACE_H_
diff --git a/xfa/fgas/font/cfgas_defaultfontmanager.cpp b/xfa/fgas/font/cfgas_defaultfontmanager.cpp
index 4d0ff20..239dfa7 100644
--- a/xfa/fgas/font/cfgas_defaultfontmanager.cpp
+++ b/xfa/fgas/font/cfgas_defaultfontmanager.cpp
@@ -6,57 +6,58 @@
 
 #include "xfa/fgas/font/cfgas_defaultfontmanager.h"
 
+#include "core/fxge/fx_font.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 
-CFGAS_DefaultFontManager::CFGAS_DefaultFontManager() {}
-
-CFGAS_DefaultFontManager::~CFGAS_DefaultFontManager() {}
-
+// static
 RetainPtr<CFGAS_GEFont> CFGAS_DefaultFontManager::GetFont(
     CFGAS_FontMgr* pFontMgr,
-    const WideStringView& wsFontFamily,
+    WideStringView wsFontFamily,
     uint32_t dwFontStyles) {
   WideString wsFontName(wsFontFamily);
   RetainPtr<CFGAS_GEFont> pFont =
       pFontMgr->LoadFont(wsFontName.c_str(), dwFontStyles, 0xFFFF);
-  if (!pFont) {
-    const FGAS_FontInfo* pCurFont =
-        FGAS_FontInfoByFontName(wsFontName.AsStringView());
-    if (pCurFont && pCurFont->pReplaceFont) {
-      uint32_t dwStyle = 0;
-      // TODO(dsinclair): Why doesn't this check the other flags?
-      if (FontStyleIsBold(dwFontStyles))
-        dwStyle |= FXFONT_BOLD;
-      if (FontStyleIsItalic(dwFontStyles))
-        dwStyle |= FXFONT_ITALIC;
-
-      const wchar_t* pReplace = pCurFont->pReplaceFont;
-      int32_t iLength = wcslen(pReplace);
-      while (iLength > 0) {
-        const wchar_t* pNameText = pReplace;
-        while (*pNameText != L',' && iLength > 0) {
-          pNameText++;
-          iLength--;
-        }
-        WideString wsReplace = WideString(pReplace, pNameText - pReplace);
-        pFont = pFontMgr->LoadFont(wsReplace.c_str(), dwStyle, 0xFFFF);
-        if (pFont)
-          break;
-
-        iLength--;
-        pNameText++;
-        pReplace = pNameText;
-      }
-    }
-  }
   if (pFont)
-    m_CacheFonts.push_back(pFont);
+    return pFont;
+
+  const FGAS_FontInfo* pCurFont =
+      FGAS_FontInfoByFontName(wsFontName.AsStringView());
+  if (!pCurFont || !pCurFont->pReplaceFont)
+    return pFont;
+
+  uint32_t dwStyle = 0;
+  // TODO(dsinclair): Why doesn't this check the other flags?
+  if (FontStyleIsForceBold(dwFontStyles))
+    dwStyle |= FXFONT_FORCE_BOLD;
+  if (FontStyleIsItalic(dwFontStyles))
+    dwStyle |= FXFONT_ITALIC;
+
+  const char* pReplace = pCurFont->pReplaceFont;
+  int32_t iLength = strlen(pReplace);
+  while (iLength > 0) {
+    const char* pNameText = pReplace;
+    while (*pNameText != ',' && iLength > 0) {
+      pNameText++;
+      iLength--;
+    }
+    WideString wsReplace =
+        WideString::FromASCII(ByteStringView(pReplace, pNameText - pReplace));
+    pFont = pFontMgr->LoadFont(wsReplace.c_str(), dwStyle, 0xFFFF);
+    if (pFont)
+      break;
+
+    iLength--;
+    pNameText++;
+    pReplace = pNameText;
+  }
   return pFont;
 }
 
+// static
 RetainPtr<CFGAS_GEFont> CFGAS_DefaultFontManager::GetDefaultFont(
     CFGAS_FontMgr* pFontMgr,
-    const WideStringView& wsFontFamily,
+    WideStringView wsFontFamily,
     uint32_t dwFontStyles) {
   RetainPtr<CFGAS_GEFont> pFont =
       pFontMgr->LoadFont(L"Arial Narrow", dwFontStyles, 0xFFFF);
@@ -64,7 +65,5 @@
     pFont = pFontMgr->LoadFont(static_cast<const wchar_t*>(nullptr),
                                dwFontStyles, 0xFFFF);
   }
-  if (pFont)
-    m_CacheFonts.push_back(pFont);
   return pFont;
 }
diff --git a/xfa/fgas/font/cfgas_defaultfontmanager.h b/xfa/fgas/font/cfgas_defaultfontmanager.h
index 2afe9eb..ebf2f3c 100644
--- a/xfa/fgas/font/cfgas_defaultfontmanager.h
+++ b/xfa/fgas/font/cfgas_defaultfontmanager.h
@@ -7,26 +7,24 @@
 #ifndef XFA_FGAS_FONT_CFGAS_DEFAULTFONTMANAGER_H_
 #define XFA_FGAS_FONT_CFGAS_DEFAULTFONTMANAGER_H_
 
-#include <vector>
-
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "xfa/fgas/font/cfgas_gefont.h"
+
+class CFGAS_GEFont;
+class CFGAS_FontMgr;
 
 class CFGAS_DefaultFontManager {
  public:
-  CFGAS_DefaultFontManager();
-  ~CFGAS_DefaultFontManager();
-
-  RetainPtr<CFGAS_GEFont> GetFont(CFGAS_FontMgr* pFontMgr,
-                                  const WideStringView& wsFontFamily,
-                                  uint32_t dwFontStyles);
-  RetainPtr<CFGAS_GEFont> GetDefaultFont(CFGAS_FontMgr* pFontMgr,
-                                         const WideStringView& wsFontFamily,
+  static RetainPtr<CFGAS_GEFont> GetFont(CFGAS_FontMgr* pFontMgr,
+                                         WideStringView wsFontFamily,
                                          uint32_t dwFontStyles);
+  static RetainPtr<CFGAS_GEFont> GetDefaultFont(CFGAS_FontMgr* pFontMgr,
+                                                WideStringView wsFontFamily,
+                                                uint32_t dwFontStyles);
 
- private:
-  std::vector<RetainPtr<CFGAS_GEFont>> m_CacheFonts;
+  CFGAS_DefaultFontManager() = delete;
+  CFGAS_DefaultFontManager(const CFGAS_DefaultFontManager&) = delete;
+  CFGAS_DefaultFontManager& operator=(const CFGAS_DefaultFontManager&) = delete;
 };
 
 #endif  // XFA_FGAS_FONT_CFGAS_DEFAULTFONTMANAGER_H_
diff --git a/xfa/fgas/font/cfgas_fontmgr.cpp b/xfa/fgas/font/cfgas_fontmgr.cpp
index 862e929..96407f3 100644
--- a/xfa/fgas/font/cfgas_fontmgr.cpp
+++ b/xfa/fgas/font/cfgas_fontmgr.cpp
@@ -10,80 +10,49 @@
 #include <memory>
 #include <utility>
 
+#include "build/build_config.h"
 #include "core/fxcrt/cfx_memorystream.h"
 #include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/fx_font.h"
-#include "core/fxge/ifx_systemfontinfo.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
+#include "xfa/fgas/font/cfx_fontsourceenum_file.h"
+#endif
 
 namespace {
 
-struct FX_CHARSET_MAP {
-  uint16_t charset;
-  uint16_t codepage;
-};
+bool VerifyUnicode(const RetainPtr<CFGAS_GEFont>& pFont, wchar_t wcUnicode) {
+  RetainPtr<CFX_Face> pFace = pFont->GetDevFont()->GetFace();
+  if (!pFace)
+    return false;
 
-const FX_CHARSET_MAP g_FXCharset2CodePageTable[] = {
-    {FX_CHARSET_ANSI, FX_CODEPAGE_MSWin_WesternEuropean},
-    {FX_CHARSET_Default, FX_CODEPAGE_DefANSI},
-    {FX_CHARSET_Symbol, FX_CODEPAGE_Symbol},
-    {FX_CHARSET_MAC_Roman, FX_CODEPAGE_MAC_Roman},
-    {FX_CHARSET_MAC_ShiftJIS, FX_CODEPAGE_MAC_ShiftJIS},
-    {FX_CHARSET_MAC_Korean, FX_CODEPAGE_MAC_Korean},
-    {FX_CHARSET_MAC_ChineseSimplified, FX_CODEPAGE_MAC_ChineseSimplified},
-    {FX_CHARSET_MAC_ChineseTraditional, FX_CODEPAGE_MAC_ChineseTraditional},
-    {FX_CHARSET_MAC_Hebrew, FX_CODEPAGE_MAC_Hebrew},
-    {FX_CHARSET_MAC_Arabic, FX_CODEPAGE_MAC_Arabic},
-    {FX_CHARSET_MAC_Greek, FX_CODEPAGE_MAC_Greek},
-    {FX_CHARSET_MAC_Turkish, FX_CODEPAGE_MAC_Turkish},
-    {FX_CHARSET_MAC_Thai, FX_CODEPAGE_MAC_Thai},
-    {FX_CHARSET_MAC_EasternEuropean, FX_CODEPAGE_MAC_EasternEuropean},
-    {FX_CHARSET_MAC_Cyrillic, FX_CODEPAGE_MAC_Cyrillic},
-    {FX_CHARSET_ShiftJIS, FX_CODEPAGE_ShiftJIS},
-    {FX_CHARSET_Hangul, FX_CODEPAGE_Hangul},
-    {FX_CHARSET_Johab, FX_CODEPAGE_Johab},
-    {FX_CHARSET_ChineseSimplified, FX_CODEPAGE_ChineseSimplified},
-    {FX_CHARSET_ChineseTraditional, FX_CODEPAGE_ChineseTraditional},
-    {FX_CHARSET_MSWin_Greek, FX_CODEPAGE_MSWin_Greek},
-    {FX_CHARSET_MSWin_Turkish, FX_CODEPAGE_MSWin_Turkish},
-    {FX_CHARSET_MSWin_Vietnamese, FX_CODEPAGE_MSWin_Vietnamese},
-    {FX_CHARSET_MSWin_Hebrew, FX_CODEPAGE_MSWin_Hebrew},
-    {FX_CHARSET_MSWin_Arabic, FX_CODEPAGE_MSWin_Arabic},
-    {FX_CHARSET_MSWin_Baltic, FX_CODEPAGE_MSWin_Baltic},
-    {FX_CHARSET_MSWin_Cyrillic, FX_CODEPAGE_MSWin_Cyrillic},
-    {FX_CHARSET_Thai, FX_CODEPAGE_MSDOS_Thai},
-    {FX_CHARSET_MSWin_EasternEuropean, FX_CODEPAGE_MSWin_EasternEuropean},
-    {FX_CHARSET_US, FX_CODEPAGE_MSDOS_US},
-    {FX_CHARSET_OEM, FX_CODEPAGE_MSDOS_WesternEuropean},
-};
+  FXFT_FaceRec* pFaceRec = pFace->GetRec();
+  FT_CharMap charmap = FXFT_Get_Face_Charmap(pFaceRec);
+  if (FXFT_Select_Charmap(pFaceRec, FT_ENCODING_UNICODE) != 0)
+    return false;
 
-uint16_t GetCodePageFromCharset(uint8_t charset) {
-  int32_t iEnd = sizeof(g_FXCharset2CodePageTable) / sizeof(FX_CHARSET_MAP) - 1;
-  ASSERT(iEnd >= 0);
-
-  int32_t iStart = 0, iMid;
-  do {
-    iMid = (iStart + iEnd) / 2;
-    const FX_CHARSET_MAP& cp = g_FXCharset2CodePageTable[iMid];
-    if (charset == cp.charset)
-      return cp.codepage;
-    if (charset < cp.charset)
-      iEnd = iMid - 1;
-    else
-      iStart = iMid + 1;
-  } while (iStart <= iEnd);
-  return 0xFFFF;
+  if (FT_Get_Char_Index(pFaceRec, wcUnicode) == 0) {
+    FT_Set_Charmap(pFaceRec, charmap);
+    return false;
+  }
+  return true;
 }
 
+}  // namespace
+
+#if defined(OS_WIN)
+
+namespace {
+
 int32_t GetSimilarityScore(FX_FONTDESCRIPTOR const* pFont,
                            uint32_t dwFontStyles) {
   int32_t iValue = 0;
@@ -108,7 +77,7 @@
   const FX_FONTDESCRIPTOR* pBestFont = nullptr;
   int32_t iBestSimilar = 0;
   for (const auto& font : fonts) {
-    if (FontStyleIsBold(font.dwFontStyles) &&
+    if (FontStyleIsForceBold(font.dwFontStyles) &&
         FontStyleIsItalic(font.dwFontStyles)) {
       continue;
     }
@@ -122,7 +91,7 @@
     if (font.uCharSet == FX_CHARSET_Symbol)
       continue;
     if (pParams->wCodePage != 0xFFFF) {
-      if (GetCodePageFromCharset(font.uCharSet) != pParams->wCodePage)
+      if (FX_GetCodePageFromCharset(font.uCharSet) != pParams->wCodePage)
         continue;
     } else {
       if (pParams->dwUSB < 128) {
@@ -173,23 +142,20 @@
   const LOGFONTW& lf = ((LPENUMLOGFONTEXW)lpelfe)->elfLogFont;
   if (lf.lfFaceName[0] == L'@')
     return 1;
-  FX_FONTDESCRIPTOR* pFont = FX_Alloc(FX_FONTDESCRIPTOR, 1);
-  memset(pFont, 0, sizeof(FX_FONTDESCRIPTOR));
-  pFont->uCharSet = lf.lfCharSet;
-  pFont->dwFontStyles = GetGdiFontStyles(lf);
-  FXSYS_wcsncpy(pFont->wsFontFace, (const wchar_t*)lf.lfFaceName, 31);
-  pFont->wsFontFace[31] = 0;
-  memcpy(&pFont->FontSignature, &lpntme->ntmFontSig,
-         sizeof(lpntme->ntmFontSig));
-  reinterpret_cast<std::deque<FX_FONTDESCRIPTOR>*>(lParam)->push_back(*pFont);
-  FX_Free(pFont);
+  FX_FONTDESCRIPTOR font;
+  memset(&font, 0, sizeof(FX_FONTDESCRIPTOR));
+  font.uCharSet = lf.lfCharSet;
+  font.dwFontStyles = GetGdiFontStyles(lf);
+  FXSYS_wcsncpy(font.wsFontFace, (const wchar_t*)lf.lfFaceName, 31);
+  font.wsFontFace[31] = 0;
+  memcpy(&font.FontSignature, &lpntme->ntmFontSig, sizeof(lpntme->ntmFontSig));
+  reinterpret_cast<std::deque<FX_FONTDESCRIPTOR>*>(lParam)->push_back(font);
   return 1;
 }
 
-void EnumGdiFonts(std::deque<FX_FONTDESCRIPTOR>* fonts,
-                  const wchar_t* pwsFaceName,
-                  wchar_t wUnicode) {
-  HDC hDC = ::GetDC(nullptr);
+std::deque<FX_FONTDESCRIPTOR> EnumGdiFonts(const wchar_t* pwsFaceName,
+                                           wchar_t wUnicode) {
+  std::deque<FX_FONTDESCRIPTOR> fonts;
   LOGFONTW lfFind;
   memset(&lfFind, 0, sizeof(lfFind));
   lfFind.lfCharSet = DEFAULT_CHARSET;
@@ -197,24 +163,56 @@
     FXSYS_wcsncpy(lfFind.lfFaceName, pwsFaceName, 31);
     lfFind.lfFaceName[31] = 0;
   }
+  HDC hDC = ::GetDC(nullptr);
   EnumFontFamiliesExW(hDC, (LPLOGFONTW)&lfFind, (FONTENUMPROCW)GdiFontEnumProc,
-                      (LPARAM)fonts, 0);
+                      (LPARAM)&fonts, 0);
   ::ReleaseDC(nullptr, hDC);
+  return fonts;
 }
 
 }  // namespace
 
-CFGAS_FontMgr::CFGAS_FontMgr() : m_pEnumerator(EnumGdiFonts), m_FontFaces(100) {
-  if (m_pEnumerator)
-    m_pEnumerator(&m_FontFaces, nullptr, 0xFEFF);
-}
+CFGAS_FontMgr::CFGAS_FontMgr() : m_FontFaces(EnumGdiFonts(nullptr, 0xFEFF)) {}
 
-CFGAS_FontMgr::~CFGAS_FontMgr() {}
+CFGAS_FontMgr::~CFGAS_FontMgr() = default;
 
 bool CFGAS_FontMgr::EnumFonts() {
   return true;
 }
 
+RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByUnicodeImpl(
+    wchar_t wUnicode,
+    uint32_t dwFontStyles,
+    const wchar_t* pszFontFamily,
+    uint32_t dwHash,
+    uint16_t wCodePage,
+    uint16_t wBitField) {
+  const FX_FONTDESCRIPTOR* pFD = FindFont(pszFontFamily, dwFontStyles, false,
+                                          wCodePage, wBitField, wUnicode);
+  if (!pFD && pszFontFamily) {
+    pFD =
+        FindFont(nullptr, dwFontStyles, false, wCodePage, wBitField, wUnicode);
+  }
+  if (!pFD)
+    return nullptr;
+
+  uint16_t newCodePage = FX_GetCodePageFromCharset(pFD->uCharSet);
+  const wchar_t* pFontFace = pFD->wsFontFace;
+  RetainPtr<CFGAS_GEFont> pFont =
+      CFGAS_GEFont::LoadFont(pFontFace, dwFontStyles, newCodePage, this);
+  if (!pFont)
+    return nullptr;
+
+  pFont->SetLogicalFontStyle(dwFontStyles);
+  if (!VerifyUnicode(pFont, wUnicode)) {
+    m_FailedUnicodesSet.insert(wUnicode);
+    return nullptr;
+  }
+
+  m_Hash2Fonts[dwHash].push_back(pFont);
+  return pFont;
+}
+
 const FX_FONTDESCRIPTOR* CFGAS_FontMgr::FindFont(const wchar_t* pszFontFamily,
                                                  uint32_t dwFontStyles,
                                                  bool matchParagraphStyle,
@@ -229,15 +227,19 @@
   params.pwsFamily = pszFontFamily;
   params.dwFontStyles = dwFontStyles;
   params.matchParagraphStyle = matchParagraphStyle;
+
   const FX_FONTDESCRIPTOR* pDesc = MatchDefaultFont(&params, m_FontFaces);
   if (pDesc)
     return pDesc;
 
-  if (!pszFontFamily || !m_pEnumerator)
+  if (!pszFontFamily)
     return nullptr;
 
-  std::deque<FX_FONTDESCRIPTOR> namedFonts;
-  m_pEnumerator(&namedFonts, pszFontFamily, wUnicode);
+  // Use a named object to store the returned value of EnumGdiFonts() instead
+  // of using a temporary object. This can prevent use-after-free issues since
+  // pDesc may point to one of std::deque object's elements.
+  std::deque<FX_FONTDESCRIPTOR> namedFonts =
+      EnumGdiFonts(pszFontFamily, wUnicode);
   params.pwsFamily = nullptr;
   pDesc = MatchDefaultFont(&params, namedFonts);
   if (!pDesc)
@@ -251,21 +253,10 @@
   return &m_FontFaces.back();
 }
 
-#else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else  // defined(OS_WIN)
 
 namespace {
 
-constexpr const char* g_FontFolders[] = {
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-    "/usr/share/fonts", "/usr/share/X11/fonts/Type1",
-    "/usr/share/X11/fonts/TTF", "/usr/local/share/fonts",
-#elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-    "~/Library/Fonts", "/Library/Fonts", "/System/Library/Fonts",
-#elif _FX_PLATFORM_ == _FX_PLATFORM_ANDROID_
-    "/system/fonts",
-#endif
-};
-
 const uint16_t g_CodePages[] = {FX_CODEPAGE_MSWin_WesternEuropean,
                                 FX_CODEPAGE_MSWin_EasternEuropean,
                                 FX_CODEPAGE_MSWin_Cyrillic,
@@ -352,82 +343,9 @@
   return static_cast<uint16_t>(p[0] << 8 | p[1]);
 }
 
-struct FX_BIT2CHARSET {
-  uint16_t wBit;
-  uint16_t wCharset;
-};
-
-const FX_BIT2CHARSET g_FX_Bit2Charset[4][16] = {
-    {{1 << 0, FX_CHARSET_ANSI},
-     {1 << 1, FX_CHARSET_MSWin_EasternEuropean},
-     {1 << 2, FX_CHARSET_MSWin_Cyrillic},
-     {1 << 3, FX_CHARSET_MSWin_Greek},
-     {1 << 4, FX_CHARSET_MSWin_Turkish},
-     {1 << 5, FX_CHARSET_MSWin_Hebrew},
-     {1 << 6, FX_CHARSET_MSWin_Arabic},
-     {1 << 7, FX_CHARSET_MSWin_Baltic},
-     {1 << 8, FX_CHARSET_MSWin_Vietnamese},
-     {1 << 9, FX_CHARSET_Default},
-     {1 << 10, FX_CHARSET_Default},
-     {1 << 11, FX_CHARSET_Default},
-     {1 << 12, FX_CHARSET_Default},
-     {1 << 13, FX_CHARSET_Default},
-     {1 << 14, FX_CHARSET_Default},
-     {1 << 15, FX_CHARSET_Default}},
-    {{1 << 0, FX_CHARSET_Thai},
-     {1 << 1, FX_CHARSET_ShiftJIS},
-     {1 << 2, FX_CHARSET_ChineseSimplified},
-     {1 << 3, FX_CHARSET_Hangul},
-     {1 << 4, FX_CHARSET_ChineseTraditional},
-     {1 << 5, FX_CHARSET_Johab},
-     {1 << 6, FX_CHARSET_Default},
-     {1 << 7, FX_CHARSET_Default},
-     {1 << 8, FX_CHARSET_Default},
-     {1 << 9, FX_CHARSET_Default},
-     {1 << 10, FX_CHARSET_Default},
-     {1 << 11, FX_CHARSET_Default},
-     {1 << 12, FX_CHARSET_Default},
-     {1 << 13, FX_CHARSET_Default},
-     {1 << 14, FX_CHARSET_OEM},
-     {1 << 15, FX_CHARSET_Symbol}},
-    {{1 << 0, FX_CHARSET_Default},
-     {1 << 1, FX_CHARSET_Default},
-     {1 << 2, FX_CHARSET_Default},
-     {1 << 3, FX_CHARSET_Default},
-     {1 << 4, FX_CHARSET_Default},
-     {1 << 5, FX_CHARSET_Default},
-     {1 << 6, FX_CHARSET_Default},
-     {1 << 7, FX_CHARSET_Default},
-     {1 << 8, FX_CHARSET_Default},
-     {1 << 9, FX_CHARSET_Default},
-     {1 << 10, FX_CHARSET_Default},
-     {1 << 11, FX_CHARSET_Default},
-     {1 << 12, FX_CHARSET_Default},
-     {1 << 13, FX_CHARSET_Default},
-     {1 << 14, FX_CHARSET_Default},
-     {1 << 15, FX_CHARSET_Default}},
-    {{1 << 0, FX_CHARSET_Default},
-     {1 << 1, FX_CHARSET_Default},
-     {1 << 2, FX_CHARSET_Default},
-     {1 << 3, FX_CHARSET_Default},
-     {1 << 4, FX_CHARSET_Default},
-     {1 << 5, FX_CHARSET_Default},
-     {1 << 6, FX_CHARSET_Default},
-     {1 << 7, FX_CHARSET_Default},
-     {1 << 8, FX_CHARSET_Default},
-     {1 << 9, FX_CHARSET_Default},
-     {1 << 10, FX_CHARSET_Default},
-     {1 << 11, FX_CHARSET_Default},
-     {1 << 12, FX_CHARSET_Default},
-     {1 << 13, FX_CHARSET_Default},
-     {1 << 14, FX_CHARSET_Default},
-     {1 << 15, FX_CHARSET_US}}};
-
-constexpr wchar_t kFolderSeparator = L'/';
-
 extern "C" {
 
-unsigned long ftStreamRead(FXFT_Stream stream,
+unsigned long ftStreamRead(FXFT_StreamRec* stream,
                            unsigned long offset,
                            unsigned char* buffer,
                            unsigned long count) {
@@ -436,206 +354,129 @@
 
   IFX_SeekableReadStream* pFile =
       static_cast<IFX_SeekableReadStream*>(stream->descriptor.pointer);
-  if (!pFile->ReadBlock(buffer, offset, count))
+  if (!pFile->ReadBlockAtOffset(buffer, offset, count))
     return 0;
 
   return count;
 }
 
-void ftStreamClose(FXFT_Stream stream) {}
+void ftStreamClose(FXFT_StreamRec* stream) {}
 
-};  // extern "C"
+}  // extern "C"
 
-}  // namespace
+// TODO(thestig): Pass in |name_table| as a std::vector?
+std::vector<WideString> GetNames(const uint8_t* name_table) {
+  std::vector<WideString> results;
+  if (!name_table)
+    return results;
 
-CFX_FontDescriptor::CFX_FontDescriptor()
-    : m_nFaceIndex(0), m_dwFontStyles(0), m_dwUsb(), m_dwCsb() {}
+  const uint8_t* lpTable = name_table;
+  WideString wsFamily;
+  const uint8_t* sp = lpTable + 2;
+  const uint8_t* lpNameRecord = lpTable + 6;
+  uint16_t nNameCount = GetUInt16(sp);
+  const uint8_t* lpStr = lpTable + GetUInt16(sp + 2);
+  for (uint16_t j = 0; j < nNameCount; j++) {
+    uint16_t nNameID = GetUInt16(lpNameRecord + j * 12 + 6);
+    if (nNameID != 1)
+      continue;
 
-CFX_FontDescriptor::~CFX_FontDescriptor() {}
-
-CFX_FontSourceEnum_File::CFX_FontSourceEnum_File() {
-  for (size_t i = 0; i < FX_ArraySize(g_FontFolders); ++i)
-    m_FolderPaths.push_back(g_FontFolders[i]);
-}
-
-CFX_FontSourceEnum_File::~CFX_FontSourceEnum_File() {}
-
-ByteString CFX_FontSourceEnum_File::GetNextFile() {
-  FX_FileHandle* pCurHandle =
-      !m_FolderQueue.empty() ? m_FolderQueue.back().pFileHandle : nullptr;
-  if (!pCurHandle) {
-    if (m_FolderPaths.empty())
-      return "";
-    pCurHandle = FX_OpenFolder(m_FolderPaths.back().c_str());
-    HandleParentPath hpp;
-    hpp.pFileHandle = pCurHandle;
-    hpp.bsParentPath = m_FolderPaths.back();
-    m_FolderQueue.push_back(hpp);
-  }
-  ByteString bsName;
-  bool bFolder;
-  ByteString bsFolderSeparator =
-      ByteString::FromUnicode(WideString(kFolderSeparator));
-  while (true) {
-    if (!FX_GetNextFile(pCurHandle, &bsName, &bFolder)) {
-      FX_CloseFolder(pCurHandle);
-      if (!m_FolderQueue.empty())
-        m_FolderQueue.pop_back();
-      if (m_FolderQueue.empty()) {
-        if (!m_FolderPaths.empty())
-          m_FolderPaths.pop_back();
-        return !m_FolderPaths.empty() ? GetNextFile() : "";
+    uint16_t nPlatformID = GetUInt16(lpNameRecord + j * 12 + 0);
+    uint16_t nNameLength = GetUInt16(lpNameRecord + j * 12 + 8);
+    uint16_t nNameOffset = GetUInt16(lpNameRecord + j * 12 + 10);
+    wsFamily.clear();
+    if (nPlatformID != 1) {
+      for (uint16_t k = 0; k < nNameLength / 2; k++) {
+        wchar_t wcTemp = GetUInt16(lpStr + nNameOffset + k * 2);
+        wsFamily += wcTemp;
       }
-      pCurHandle = m_FolderQueue.back().pFileHandle;
+      results.push_back(wsFamily);
       continue;
     }
-    if (bsName == "." || bsName == "..")
-      continue;
-    if (bFolder) {
-      HandleParentPath hpp;
-      hpp.bsParentPath =
-          m_FolderQueue.back().bsParentPath + bsFolderSeparator + bsName;
-      hpp.pFileHandle = FX_OpenFolder(hpp.bsParentPath.c_str());
-      if (!hpp.pFileHandle)
-        continue;
-      m_FolderQueue.push_back(hpp);
-      pCurHandle = hpp.pFileHandle;
-      continue;
+    for (uint16_t k = 0; k < nNameLength; k++) {
+      wchar_t wcTemp = GetUInt8(lpStr + nNameOffset + k);
+      wsFamily += wcTemp;
     }
-    bsName = m_FolderQueue.back().bsParentPath + bsFolderSeparator + bsName;
-    break;
+    results.push_back(wsFamily);
   }
-  return bsName;
+  return results;
 }
 
-bool CFX_FontSourceEnum_File::HasStartPosition() {
-  m_wsNext = GetNextFile().UTF8Decode();
-  return m_wsNext.GetLength() != 0;
-}
-
-// <next exists, stream for next>
-std::pair<bool, RetainPtr<IFX_SeekableStream>>
-CFX_FontSourceEnum_File::GetNext() {
-  if (m_wsNext.GetLength() == 0)
-    return {false, nullptr};
-
-  auto stream = IFX_SeekableStream::CreateFromFilename(m_wsNext.c_str(),
-                                                       FX_FILEMODE_ReadOnly);
-  m_wsNext = GetNextFile().UTF8Decode();
-  return {true, stream};
-}
-
-CFGAS_FontMgr::CFGAS_FontMgr()
-    : m_pFontSource(pdfium::MakeUnique<CFX_FontSourceEnum_File>()) {}
-
-CFGAS_FontMgr::~CFGAS_FontMgr() {}
-
-bool CFGAS_FontMgr::EnumFontsFromFontMapper() {
-  CFX_FontMapper* pFontMapper =
-      CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper();
-  if (!pFontMapper)
-    return false;
-
-  IFX_SystemFontInfo* pSystemFontInfo = pFontMapper->GetSystemFontInfo();
-  if (!pSystemFontInfo)
-    return false;
-
-  pSystemFontInfo->EnumFontList(pFontMapper);
-  for (int32_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
-    RetainPtr<IFX_SeekableReadStream> pFontStream =
-        CreateFontStream(pFontMapper, pSystemFontInfo, i);
-    if (!pFontStream)
-      continue;
-
-    WideString wsFaceName =
-        WideString::FromLocal(pFontMapper->GetFaceName(i).c_str());
-    RegisterFaces(pFontStream, &wsFaceName);
+void GetUSBCSB(FXFT_FaceRec* pFace, uint32_t* USB, uint32_t* CSB) {
+  TT_OS2* pOS2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(pFace, ft_sfnt_os2));
+  if (!pOS2) {
+    USB[0] = 0;
+    USB[1] = 0;
+    USB[2] = 0;
+    USB[3] = 0;
+    CSB[0] = 0;
+    CSB[1] = 0;
+    return;
   }
-  return !m_InstalledFonts.empty();
+  USB[0] = pOS2->ulUnicodeRange1;
+  USB[1] = pOS2->ulUnicodeRange2;
+  USB[2] = pOS2->ulUnicodeRange3;
+  USB[3] = pOS2->ulUnicodeRange4;
+  CSB[0] = pOS2->ulCodePageRange1;
+  CSB[1] = pOS2->ulCodePageRange2;
 }
 
-bool CFGAS_FontMgr::EnumFontsFromFiles() {
-  CFX_GEModule::Get()->GetFontMgr()->InitFTLibrary();
-  if (!m_pFontSource->HasStartPosition())
-    return !m_InstalledFonts.empty();
+uint32_t GetFlags(FXFT_FaceRec* pFace) {
+  uint32_t flags = 0;
+  if (FXFT_Is_Face_Bold(pFace))
+    flags |= FXFONT_FORCE_BOLD;
+  if (FXFT_Is_Face_Italic(pFace))
+    flags |= FXFONT_ITALIC;
+  if (FT_IS_FIXED_WIDTH(pFace))
+    flags |= FXFONT_FIXED_PITCH;
 
-  bool has_next;
-  RetainPtr<IFX_SeekableStream> stream;
-  std::tie(has_next, stream) = m_pFontSource->GetNext();
-  while (has_next) {
-    if (stream)
-      RegisterFaces(stream, nullptr);
-    std::tie(has_next, stream) = m_pFontSource->GetNext();
+  TT_OS2* pOS2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(pFace, ft_sfnt_os2));
+  if (!pOS2)
+    return flags;
+
+  if (pOS2->ulCodePageRange1 & (1 << 31))
+    flags |= FXFONT_SYMBOLIC;
+  if (pOS2->panose[0] == 2) {
+    uint8_t uSerif = pOS2->panose[1];
+    if ((uSerif > 1 && uSerif < 10) || uSerif > 13)
+      flags |= FXFONT_SERIF;
   }
-  return !m_InstalledFonts.empty();
+  return flags;
 }
 
-bool CFGAS_FontMgr::EnumFonts() {
-  return EnumFontsFromFontMapper() || EnumFontsFromFiles();
+RetainPtr<IFX_SeekableReadStream> CreateFontStream(
+    CFX_FontMapper* pFontMapper,
+    uint32_t index) {
+  size_t dwFileSize = 0;
+  std::unique_ptr<uint8_t, FxFreeDeleter> pBuffer =
+      pFontMapper->RawBytesForIndex(index, &dwFileSize);
+  if (!pBuffer)
+    return nullptr;
+
+  return pdfium::MakeRetain<CFX_MemoryStream>(std::move(pBuffer), dwFileSize);
 }
 
-bool CFGAS_FontMgr::VerifyUnicode(CFX_FontDescriptor* pDesc,
-                                  wchar_t wcUnicode) {
-  RetainPtr<IFX_SeekableReadStream> pFileRead =
-      CreateFontStream(pDesc->m_wsFaceName.UTF8Encode());
-  if (!pFileRead)
-    return false;
-
-  FXFT_Face pFace = LoadFace(pFileRead, pDesc->m_nFaceIndex);
-  FT_Error retCharmap = FXFT_Select_Charmap(pFace, FXFT_ENCODING_UNICODE);
-  FT_Error retIndex = FXFT_Get_Char_Index(pFace, wcUnicode);
-  if (!pFace)
-    return false;
-
-  if (FXFT_Get_Face_External_Stream(pFace))
-    FXFT_Clear_Face_External_Stream(pFace);
-
-  FXFT_Done_Face(pFace);
-  return !retCharmap && retIndex;
-}
-
-RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::LoadFont(const WideString& wsFaceName,
-                                                int32_t iFaceIndex,
-                                                int32_t* pFaceCount) {
+RetainPtr<IFX_SeekableReadStream> CreateFontStream(
+    const ByteString& bsFaceName) {
   CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
   CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper();
-  if (!pFontMapper)
-    return nullptr;
+  pFontMapper->LoadInstalledFonts();
 
-  IFX_SystemFontInfo* pSystemFontInfo = pFontMapper->GetSystemFontInfo();
-  if (!pSystemFontInfo)
-    return nullptr;
-
-  RetainPtr<IFX_SeekableReadStream> pFontStream =
-      CreateFontStream(wsFaceName.UTF8Encode());
-  if (!pFontStream)
-    return nullptr;
-
-  auto pInternalFont = pdfium::MakeUnique<CFX_Font>();
-  if (!pInternalFont->LoadFile(pFontStream, iFaceIndex))
-    return nullptr;
-
-  RetainPtr<CFGAS_GEFont> pFont =
-      CFGAS_GEFont::LoadFont(std::move(pInternalFont), this);
-  if (!pFont)
-    return nullptr;
-
-  m_IFXFont2FileRead[pFont] = pFontStream;
-  if (pFaceCount)
-    *pFaceCount = pFont->GetDevFont()->GetFace()->num_faces;
-  return pFont;
+  for (int32_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
+    if (pFontMapper->GetFaceName(i) == bsFaceName)
+      return CreateFontStream(pFontMapper, i);
+  }
+  return nullptr;
 }
 
-FXFT_Face CFGAS_FontMgr::LoadFace(
+RetainPtr<CFX_Face> LoadFace(
     const RetainPtr<IFX_SeekableReadStream>& pFontStream,
     int32_t iFaceIndex) {
   if (!pFontStream)
     return nullptr;
 
   CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
-  pFontMgr->InitFTLibrary();
-
-  FXFT_Library library = pFontMgr->GetFTLibrary();
+  FXFT_LibraryRec* library = pFontMgr->GetFTLibrary();
   if (!library)
     return nullptr;
 
@@ -643,8 +484,8 @@
   // Ultimately, we want to change this to:
   //   FXFT_Stream ftStream = FX_Alloc(FXFT_StreamRec, 1);
   // https://bugs.chromium.org/p/pdfium/issues/detail?id=690
-  FXFT_Stream ftStream =
-      static_cast<FXFT_Stream>(ft_scalloc(sizeof(FXFT_StreamRec), 1));
+  FXFT_StreamRec* ftStream =
+      static_cast<FXFT_StreamRec*>(ft_scalloc(sizeof(FXFT_StreamRec), 1));
   memset(ftStream, 0, sizeof(FXFT_StreamRec));
   ftStream->base = nullptr;
   ftStream->descriptor.pointer = static_cast<void*>(pFontStream.Get());
@@ -653,83 +494,50 @@
   ftStream->read = ftStreamRead;
   ftStream->close = ftStreamClose;
 
-  FXFT_Open_Args ftArgs;
-  memset(&ftArgs, 0, sizeof(FXFT_Open_Args));
+  FT_Open_Args ftArgs;
+  memset(&ftArgs, 0, sizeof(FT_Open_Args));
   ftArgs.flags |= FT_OPEN_STREAM;
   ftArgs.stream = ftStream;
 
-  FXFT_Face pFace = nullptr;
-  if (FXFT_Open_Face(library, &ftArgs, iFaceIndex, &pFace)) {
+  RetainPtr<CFX_Face> pFace = CFX_Face::Open(library, &ftArgs, iFaceIndex);
+  if (!pFace) {
     ft_sfree(ftStream);
     return nullptr;
   }
-
-  FXFT_Set_Pixel_Sizes(pFace, 0, 64);
+  FT_Set_Pixel_Sizes(pFace->GetRec(), 0, 64);
   return pFace;
 }
 
-RetainPtr<IFX_SeekableReadStream> CFGAS_FontMgr::CreateFontStream(
-    CFX_FontMapper* pFontMapper,
-    IFX_SystemFontInfo* pSystemFontInfo,
-    uint32_t index) {
-  void* hFont = pSystemFontInfo->MapFont(
-      0, 0, FX_CHARSET_Default, 0, pFontMapper->GetFaceName(index).c_str());
-  if (!hFont)
-    return nullptr;
+bool VerifyUnicodeForFontDescriptor(CFX_FontDescriptor* pDesc,
+                                    wchar_t wcUnicode) {
+  RetainPtr<IFX_SeekableReadStream> pFileRead =
+      CreateFontStream(pDesc->m_wsFaceName.ToUTF8());
+  if (!pFileRead)
+    return false;
 
-  uint32_t dwFileSize = pSystemFontInfo->GetFontData(hFont, 0, nullptr, 0);
-  if (dwFileSize == 0)
-    return nullptr;
+  RetainPtr<CFX_Face> pFace = LoadFace(pFileRead, pDesc->m_nFaceIndex);
+  if (!pFace)
+    return false;
 
-  uint8_t* pBuffer = FX_Alloc(uint8_t, dwFileSize + 1);
-  dwFileSize = pSystemFontInfo->GetFontData(hFont, 0, pBuffer, dwFileSize);
+  FT_Error retCharmap =
+      FXFT_Select_Charmap(pFace->GetRec(), FT_ENCODING_UNICODE);
+  FT_Error retIndex = FT_Get_Char_Index(pFace->GetRec(), wcUnicode);
 
-  return pdfium::MakeRetain<CFX_MemoryStream>(pBuffer, dwFileSize, true);
+  if (FXFT_Get_Face_External_Stream(pFace->GetRec()))
+    FXFT_Clear_Face_External_Stream(pFace->GetRec());
+
+  return !retCharmap && retIndex;
 }
 
-RetainPtr<IFX_SeekableReadStream> CFGAS_FontMgr::CreateFontStream(
-    const ByteString& bsFaceName) {
-  CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr();
-  CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper();
-  if (!pFontMapper)
-    return nullptr;
-
-  IFX_SystemFontInfo* pSystemFontInfo = pFontMapper->GetSystemFontInfo();
-  if (!pSystemFontInfo)
-    return nullptr;
-
-  pSystemFontInfo->EnumFontList(pFontMapper);
-  for (int32_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
-    if (pFontMapper->GetFaceName(i) == bsFaceName)
-      return CreateFontStream(pFontMapper, pSystemFontInfo, i);
-  }
-  return nullptr;
+bool IsPartName(const WideString& name1, const WideString& name2) {
+  return name1.Contains(name2.AsStringView());
 }
 
-void CFGAS_FontMgr::MatchFonts(
-    std::vector<CFX_FontDescriptorInfo>* pMatchedFonts,
-    uint16_t wCodePage,
-    uint32_t dwFontStyles,
-    const WideString& FontName,
-    wchar_t wcUnicode) {
-  pMatchedFonts->clear();
-  for (const auto& pFont : m_InstalledFonts) {
-    int32_t nPenalty =
-        CalcPenalty(pFont.get(), wCodePage, dwFontStyles, FontName, wcUnicode);
-    if (nPenalty >= 0xffff)
-      continue;
-    pMatchedFonts->push_back({pFont.get(), nPenalty});
-    if (pMatchedFonts->size() == 0xffff)
-      break;
-  }
-  std::sort(pMatchedFonts->begin(), pMatchedFonts->end());
-}
-
-int32_t CFGAS_FontMgr::CalcPenalty(CFX_FontDescriptor* pInstalled,
-                                   uint16_t wCodePage,
-                                   uint32_t dwFontStyles,
-                                   const WideString& FontName,
-                                   wchar_t wcUnicode) {
+int32_t CalcPenalty(CFX_FontDescriptor* pInstalled,
+                    uint16_t wCodePage,
+                    uint32_t dwFontStyles,
+                    const WideString& FontName,
+                    wchar_t wcUnicode) {
   int32_t nPenalty = 30000;
   if (FontName.GetLength() != 0) {
     if (FontName != pInstalled->m_wsFaceName) {
@@ -745,11 +553,10 @@
     } else {
       nPenalty -= 30000;
     }
-    if (30000 == nPenalty &&
-        0 == IsPartName(pInstalled->m_wsFaceName, FontName)) {
+    if (nPenalty == 30000 && !IsPartName(pInstalled->m_wsFaceName, FontName)) {
       size_t i;
       for (i = 0; i < pInstalled->m_wsFamilyNames.size(); i++) {
-        if (IsPartName(pInstalled->m_wsFamilyNames[i], FontName) != 0)
+        if (IsPartName(pInstalled->m_wsFamilyNames[i], FontName))
           break;
       }
       if (i == pInstalled->m_wsFamilyNames.size())
@@ -761,7 +568,7 @@
     }
   }
   uint32_t dwStyleMask = pInstalled->m_dwFontStyles ^ dwFontStyles;
-  if (FontStyleIsBold(dwStyleMask))
+  if (FontStyleIsForceBold(dwStyleMask))
     nPenalty += 4500;
   if (FontStyleIsFixedPitch(dwStyleMask))
     nPenalty += 10000;
@@ -796,35 +603,155 @@
   return nPenalty;
 }
 
-void CFGAS_FontMgr::RegisterFace(FXFT_Face pFace, const WideString* pFaceName) {
-  if ((pFace->face_flags & FT_FACE_FLAG_SCALABLE) == 0)
+}  // namespace
+
+CFX_FontDescriptor::CFX_FontDescriptor()
+    : m_nFaceIndex(0), m_dwFontStyles(0), m_dwUsb(), m_dwCsb() {}
+
+CFX_FontDescriptor::~CFX_FontDescriptor() {}
+
+CFGAS_FontMgr::CFGAS_FontMgr()
+    : m_pFontSource(pdfium::MakeUnique<CFX_FontSourceEnum_File>()) {}
+
+CFGAS_FontMgr::~CFGAS_FontMgr() {}
+
+bool CFGAS_FontMgr::EnumFontsFromFontMapper() {
+  CFX_FontMapper* pFontMapper =
+      CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper();
+  pFontMapper->LoadInstalledFonts();
+
+  for (int32_t i = 0; i < pFontMapper->GetFaceSize(); ++i) {
+    RetainPtr<IFX_SeekableReadStream> pFontStream =
+        CreateFontStream(pFontMapper, i);
+    if (!pFontStream)
+      continue;
+
+    WideString wsFaceName =
+        WideString::FromDefANSI(pFontMapper->GetFaceName(i).AsStringView());
+    RegisterFaces(pFontStream, &wsFaceName);
+  }
+
+  return !m_InstalledFonts.empty();
+}
+
+bool CFGAS_FontMgr::EnumFontsFromFiles() {
+  m_pFontSource->GetNext();
+  while (m_pFontSource->HasNext()) {
+    RetainPtr<IFX_SeekableStream> stream = m_pFontSource->GetStream();
+    if (stream)
+      RegisterFaces(stream, nullptr);
+    m_pFontSource->GetNext();
+  }
+  return !m_InstalledFonts.empty();
+}
+
+bool CFGAS_FontMgr::EnumFonts() {
+  return EnumFontsFromFontMapper() || EnumFontsFromFiles();
+}
+
+RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByUnicodeImpl(
+    wchar_t wUnicode,
+    uint32_t dwFontStyles,
+    const wchar_t* pszFontFamily,
+    uint32_t dwHash,
+    uint16_t wCodePage,
+    uint16_t /* wBitField */) {
+  std::vector<CFX_FontDescriptorInfo>* sortedFontInfos =
+      m_Hash2CandidateList[dwHash].get();
+  if (!sortedFontInfos) {
+    auto pNewFonts = pdfium::MakeUnique<std::vector<CFX_FontDescriptorInfo>>();
+    sortedFontInfos = pNewFonts.get();
+    MatchFonts(sortedFontInfos, wCodePage, dwFontStyles,
+               WideString(pszFontFamily), wUnicode);
+    m_Hash2CandidateList[dwHash] = std::move(pNewFonts);
+  }
+  for (const auto& info : *sortedFontInfos) {
+    CFX_FontDescriptor* pDesc = info.pFont;
+    if (!VerifyUnicodeForFontDescriptor(pDesc, wUnicode))
+      continue;
+    RetainPtr<CFGAS_GEFont> pFont =
+        LoadFontInternal(pDesc->m_wsFaceName, pDesc->m_nFaceIndex);
+    if (!pFont)
+      continue;
+    pFont->SetLogicalFontStyle(dwFontStyles);
+    m_Hash2Fonts[dwHash].push_back(pFont);
+    return pFont;
+  }
+  if (!pszFontFamily)
+    m_FailedUnicodesSet.insert(wUnicode);
+  return nullptr;
+}
+
+RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::LoadFontInternal(
+    const WideString& wsFaceName,
+    int32_t iFaceIndex) {
+  RetainPtr<IFX_SeekableReadStream> pFontStream =
+      CreateFontStream(wsFaceName.ToUTF8());
+  if (!pFontStream)
+    return nullptr;
+
+  auto pInternalFont = pdfium::MakeUnique<CFX_Font>();
+  if (!pInternalFont->LoadFile(pFontStream, iFaceIndex))
+    return nullptr;
+
+  RetainPtr<CFGAS_GEFont> pFont =
+      CFGAS_GEFont::LoadFont(std::move(pInternalFont), this);
+  if (!pFont)
+    return nullptr;
+
+  m_IFXFont2FileRead[pFont] = pFontStream;
+  return pFont;
+}
+
+void CFGAS_FontMgr::MatchFonts(
+    std::vector<CFX_FontDescriptorInfo>* pMatchedFonts,
+    uint16_t wCodePage,
+    uint32_t dwFontStyles,
+    const WideString& FontName,
+    wchar_t wcUnicode) {
+  pMatchedFonts->clear();
+  for (const auto& pFont : m_InstalledFonts) {
+    int32_t nPenalty =
+        CalcPenalty(pFont.get(), wCodePage, dwFontStyles, FontName, wcUnicode);
+    if (nPenalty >= 0xffff)
+      continue;
+    pMatchedFonts->push_back({pFont.get(), nPenalty});
+    if (pMatchedFonts->size() == 0xffff)
+      break;
+  }
+  std::sort(pMatchedFonts->begin(), pMatchedFonts->end());
+}
+
+void CFGAS_FontMgr::RegisterFace(RetainPtr<CFX_Face> pFace,
+                                 const WideString* pFaceName) {
+  if ((pFace->GetRec()->face_flags & FT_FACE_FLAG_SCALABLE) == 0)
     return;
 
   auto pFont = pdfium::MakeUnique<CFX_FontDescriptor>();
-  pFont->m_dwFontStyles |= FXFT_Is_Face_Bold(pFace) ? FXFONT_BOLD : 0;
-  pFont->m_dwFontStyles |= FXFT_Is_Face_Italic(pFace) ? FXFONT_ITALIC : 0;
-  pFont->m_dwFontStyles |= GetFlags(pFace);
+  pFont->m_dwFontStyles |= GetFlags(pFace->GetRec());
 
-  std::vector<uint16_t> charsets = GetCharsets(pFace);
-  GetUSBCSB(pFace, pFont->m_dwUsb, pFont->m_dwCsb);
+  GetUSBCSB(pFace->GetRec(), pFont->m_dwUsb, pFont->m_dwCsb);
 
   FT_ULong dwTag;
   FT_ENC_TAG(dwTag, 'n', 'a', 'm', 'e');
 
   std::vector<uint8_t> table;
   unsigned long nLength = 0;
-  unsigned int error = FXFT_Load_Sfnt_Table(pFace, dwTag, 0, nullptr, &nLength);
+  unsigned int error =
+      FT_Load_Sfnt_Table(pFace->GetRec(), dwTag, 0, nullptr, &nLength);
   if (error == 0 && nLength != 0) {
     table.resize(nLength);
-    if (FXFT_Load_Sfnt_Table(pFace, dwTag, 0, table.data(), nullptr))
+    if (FT_Load_Sfnt_Table(pFace->GetRec(), dwTag, 0, table.data(), nullptr))
       table.clear();
   }
-  GetNames(table.empty() ? nullptr : table.data(), pFont->m_wsFamilyNames);
-  pFont->m_wsFamilyNames.push_back(ByteString(pFace->family_name).UTF8Decode());
+  pFont->m_wsFamilyNames = GetNames(table.empty() ? nullptr : table.data());
+  pFont->m_wsFamilyNames.push_back(
+      WideString::FromUTF8(pFace->GetRec()->family_name));
   pFont->m_wsFaceName =
-      pFaceName ? *pFaceName
-                : WideString::FromLocal(FXFT_Get_Postscript_Name(pFace));
-  pFont->m_nFaceIndex = pFace->face_index;
+      pFaceName
+          ? *pFaceName
+          : WideString::FromDefANSI(FT_Get_Postscript_Name(pFace->GetRec()));
+  pFont->m_nFaceIndex = pFace->GetRec()->face_index;
   m_InstalledFonts.push_back(std::move(pFont));
 }
 
@@ -834,119 +761,19 @@
   int32_t index = 0;
   int32_t num_faces = 0;
   do {
-    FXFT_Face pFace = LoadFace(pFontStream, index++);
+    RetainPtr<CFX_Face> pFace = LoadFace(pFontStream, index++);
     if (!pFace)
       continue;
     // All faces keep number of faces. It can be retrieved from any one face.
     if (num_faces == 0)
-      num_faces = pFace->num_faces;
+      num_faces = pFace->GetRec()->num_faces;
     RegisterFace(pFace, pFaceName);
-    if (FXFT_Get_Face_External_Stream(pFace))
-      FXFT_Clear_Face_External_Stream(pFace);
-    FXFT_Done_Face(pFace);
+    if (FXFT_Get_Face_External_Stream(pFace->GetRec()))
+      FXFT_Clear_Face_External_Stream(pFace->GetRec());
   } while (index < num_faces);
 }
 
-uint32_t CFGAS_FontMgr::GetFlags(FXFT_Face pFace) {
-  uint32_t flag = 0;
-  if (FT_IS_FIXED_WIDTH(pFace))
-    flag |= FXFONT_FIXED_PITCH;
-  TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(pFace, ft_sfnt_os2);
-  if (!pOS2)
-    return flag;
-
-  if (pOS2->ulCodePageRange1 & (1 << 31))
-    flag |= FXFONT_SYMBOLIC;
-  if (pOS2->panose[0] == 2) {
-    uint8_t uSerif = pOS2->panose[1];
-    if ((uSerif > 1 && uSerif < 10) || uSerif > 13)
-      flag |= FXFONT_SERIF;
-  }
-  return flag;
-}
-
-void CFGAS_FontMgr::GetNames(const uint8_t* name_table,
-                             std::vector<WideString>& Names) {
-  if (!name_table)
-    return;
-
-  uint8_t* lpTable = (uint8_t*)name_table;
-  WideString wsFamily;
-  uint8_t* sp = lpTable + 2;
-  uint8_t* lpNameRecord = lpTable + 6;
-  uint16_t nNameCount = GetUInt16(sp);
-  uint8_t* lpStr = lpTable + GetUInt16(sp + 2);
-  for (uint16_t j = 0; j < nNameCount; j++) {
-    uint16_t nNameID = GetUInt16(lpNameRecord + j * 12 + 6);
-    if (nNameID != 1)
-      continue;
-
-    uint16_t nPlatformID = GetUInt16(lpNameRecord + j * 12 + 0);
-    uint16_t nNameLength = GetUInt16(lpNameRecord + j * 12 + 8);
-    uint16_t nNameOffset = GetUInt16(lpNameRecord + j * 12 + 10);
-    wsFamily.clear();
-    if (nPlatformID != 1) {
-      for (uint16_t k = 0; k < nNameLength / 2; k++) {
-        wchar_t wcTemp = GetUInt16(lpStr + nNameOffset + k * 2);
-        wsFamily += wcTemp;
-      }
-      Names.push_back(wsFamily);
-      continue;
-    }
-    for (uint16_t k = 0; k < nNameLength; k++) {
-      wchar_t wcTemp = GetUInt8(lpStr + nNameOffset + k);
-      wsFamily += wcTemp;
-    }
-    Names.push_back(wsFamily);
-  }
-}
-
-std::vector<uint16_t> CFGAS_FontMgr::GetCharsets(FXFT_Face pFace) const {
-  std::vector<uint16_t> charsets;
-  TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(pFace, ft_sfnt_os2);
-  if (!pOS2) {
-    charsets.push_back(FX_CHARSET_Default);
-    return charsets;
-  }
-  uint16_t a[4] = {
-      pOS2->ulCodePageRange1 & 0xffff, (pOS2->ulCodePageRange1 >> 16) & 0xffff,
-      pOS2->ulCodePageRange2 & 0xffff, (pOS2->ulCodePageRange2 >> 16) & 0xffff};
-  for (int n = 0; n < 4; n++) {
-    for (int32_t i = 0; i < 16; i++) {
-      if ((a[n] & g_FX_Bit2Charset[n][i].wBit) != 0)
-        charsets.push_back(g_FX_Bit2Charset[n][i].wCharset);
-    }
-  }
-  return charsets;
-}
-
-void CFGAS_FontMgr::GetUSBCSB(FXFT_Face pFace, uint32_t* USB, uint32_t* CSB) {
-  TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(pFace, ft_sfnt_os2);
-  if (!pOS2) {
-    USB[0] = 0;
-    USB[1] = 0;
-    USB[2] = 0;
-    USB[3] = 0;
-    CSB[0] = 0;
-    CSB[1] = 0;
-    return;
-  }
-  USB[0] = pOS2->ulUnicodeRange1;
-  USB[1] = pOS2->ulUnicodeRange2;
-  USB[2] = pOS2->ulUnicodeRange3;
-  USB[3] = pOS2->ulUnicodeRange4;
-  CSB[0] = pOS2->ulCodePageRange1;
-  CSB[1] = pOS2->ulCodePageRange2;
-}
-
-int32_t CFGAS_FontMgr::IsPartName(const WideString& Name1,
-                                  const WideString& Name2) {
-  if (Name1.Contains(Name2.c_str()))
-    return 1;
-  return 0;
-}
-
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif  // defined(OS_WIN)
 
 RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByCodePage(
     uint16_t wCodePage,
@@ -955,11 +782,16 @@
   ByteString bsHash = ByteString::Format("%d, %d", wCodePage, dwFontStyles);
   bsHash += FX_UTF8Encode(WideStringView(pszFontFamily));
   uint32_t dwHash = FX_HashCode_GetA(bsHash.AsStringView(), false);
-  std::vector<RetainPtr<CFGAS_GEFont>>* pFontArray = &m_Hash2Fonts[dwHash];
-  if (!pFontArray->empty())
-    return (*pFontArray)[0];
+  auto* pFontVector = &m_Hash2Fonts[dwHash];
+  if (!pFontVector->empty()) {
+    for (auto iter = pFontVector->begin(); iter != pFontVector->end(); ++iter) {
+      if (*iter != nullptr)
+        return *iter;
+    }
+    return nullptr;
+  }
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   const FX_FONTDESCRIPTOR* pFD =
       FindFont(pszFontFamily, dwFontStyles, true, wCodePage, 999, 0);
   if (!pFD)
@@ -971,7 +803,7 @@
 
   RetainPtr<CFGAS_GEFont> pFont =
       CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, wCodePage, this);
-#else   // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else   // defined(OS_WIN)
   std::vector<CFX_FontDescriptorInfo>* sortedFontInfos =
       m_Hash2CandidateList[dwHash].get();
   if (!sortedFontInfos) {
@@ -986,14 +818,14 @@
 
   CFX_FontDescriptor* pDesc = (*sortedFontInfos)[0].pFont;
   RetainPtr<CFGAS_GEFont> pFont =
-      LoadFont(pDesc->m_wsFaceName, pDesc->m_nFaceIndex, nullptr);
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+      LoadFontInternal(pDesc->m_wsFaceName, pDesc->m_nFaceIndex);
+#endif  // defined(OS_WIN)
 
   if (!pFont)
     return nullptr;
 
   pFont->SetLogicalFontStyle(dwFontStyles);
-  pFontArray->push_back(pFont);
+  pFontVector->push_back(pFont);
   return pFont;
 }
 
@@ -1001,10 +833,8 @@
     wchar_t wUnicode,
     uint32_t dwFontStyles,
     const wchar_t* pszFontFamily) {
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
   if (pdfium::ContainsKey(m_FailedUnicodesSet, wUnicode))
     return nullptr;
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
 
   const FGAS_FONTUSB* x = FGAS_GetUnicodeBitField(wUnicode);
   uint16_t wCodePage = x ? x->wCodePage : 0xFFFF;
@@ -1018,80 +848,20 @@
   }
   bsHash += FX_UTF8Encode(WideStringView(pszFontFamily));
   uint32_t dwHash = FX_HashCode_GetA(bsHash.AsStringView(), false);
-  std::vector<RetainPtr<CFGAS_GEFont>>* pFonts = &m_Hash2Fonts[dwHash];
-  for (size_t i = 0; i < pFonts->size(); ++i) {
-    if (VerifyUnicode((*pFonts)[i], wUnicode))
-      return (*pFonts)[i];
+  std::vector<RetainPtr<CFGAS_GEFont>>& fonts = m_Hash2Fonts[dwHash];
+  for (auto& pFont : fonts) {
+    if (VerifyUnicode(pFont, wUnicode))
+      return pFont;
   }
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  const FX_FONTDESCRIPTOR* pFD = FindFont(pszFontFamily, dwFontStyles, false,
-                                          wCodePage, wBitField, wUnicode);
-  if (!pFD && pszFontFamily) {
-    pFD =
-        FindFont(nullptr, dwFontStyles, false, wCodePage, wBitField, wUnicode);
-  }
-  if (!pFD)
-    return nullptr;
 
-  uint16_t newCodePage = GetCodePageFromCharset(pFD->uCharSet);
-  const wchar_t* pFontFace = pFD->wsFontFace;
-  RetainPtr<CFGAS_GEFont> pFont =
-      CFGAS_GEFont::LoadFont(pFontFace, dwFontStyles, newCodePage, this);
-  if (!pFont)
-    return nullptr;
-
-  pFont->SetLogicalFontStyle(dwFontStyles);
-  pFonts->push_back(pFont);
-  return pFont;
-#else   // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  std::vector<CFX_FontDescriptorInfo>* sortedFontInfos =
-      m_Hash2CandidateList[dwHash].get();
-  if (!sortedFontInfos) {
-    auto pNewFonts = pdfium::MakeUnique<std::vector<CFX_FontDescriptorInfo>>();
-    sortedFontInfos = pNewFonts.get();
-    MatchFonts(sortedFontInfos, wCodePage, dwFontStyles,
-               WideString(pszFontFamily), wUnicode);
-    m_Hash2CandidateList[dwHash] = std::move(pNewFonts);
-  }
-  for (const auto& info : *sortedFontInfos) {
-    CFX_FontDescriptor* pDesc = info.pFont;
-    if (!VerifyUnicode(pDesc, wUnicode))
-      continue;
-    RetainPtr<CFGAS_GEFont> pFont =
-        LoadFont(pDesc->m_wsFaceName, pDesc->m_nFaceIndex, nullptr);
-    if (!pFont)
-      continue;
-    pFont->SetLogicalFontStyle(dwFontStyles);
-    pFonts->push_back(pFont);
-    return pFont;
-  }
-  if (!pszFontFamily)
-    m_FailedUnicodesSet.insert(wUnicode);
-  return nullptr;
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-}
-
-bool CFGAS_FontMgr::VerifyUnicode(const RetainPtr<CFGAS_GEFont>& pFont,
-                                  wchar_t wcUnicode) {
-  if (!pFont)
-    return false;
-
-  FXFT_Face pFace = pFont->GetDevFont()->GetFace();
-  FXFT_CharMap charmap = FXFT_Get_Face_Charmap(pFace);
-  if (FXFT_Select_Charmap(pFace, FXFT_ENCODING_UNICODE) != 0)
-    return false;
-
-  if (FXFT_Get_Char_Index(pFace, wcUnicode) == 0) {
-    FXFT_Set_Charmap(pFace, charmap);
-    return false;
-  }
-  return true;
+  return GetFontByUnicodeImpl(wUnicode, dwFontStyles, pszFontFamily, dwHash,
+                              wCodePage, wBitField);
 }
 
 RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::LoadFont(const wchar_t* pszFontFamily,
                                                 uint32_t dwFontStyles,
                                                 uint16_t wCodePage) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   ByteString bsHash = ByteString::Format("%d, %d", wCodePage, dwFontStyles);
   bsHash += FX_UTF8Encode(WideStringView(pszFontFamily));
   uint32_t dwHash = FX_HashCode_GetA(bsHash.AsStringView(), false);
@@ -1114,18 +884,18 @@
   pFont->SetLogicalFontStyle(dwFontStyles);
   pFontArray->push_back(pFont);
   return pFont;
-#else   // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else   // defined(OS_WIN)
   return GetFontByCodePage(wCodePage, dwFontStyles, pszFontFamily);
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif  // defined(OS_WIN)
 }
 
 void CFGAS_FontMgr::RemoveFont(const RetainPtr<CFGAS_GEFont>& pEFont) {
   if (!pEFont)
     return;
 
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
   m_IFXFont2FileRead.erase(pEFont);
-#endif  // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#endif
 
   auto iter = m_Hash2Fonts.begin();
   while (iter != m_Hash2Fonts.end()) {
diff --git a/xfa/fgas/font/cfgas_fontmgr.h b/xfa/fgas/font/cfgas_fontmgr.h
index 445b318..bc7768e 100644
--- a/xfa/fgas/font/cfgas_fontmgr.h
+++ b/xfa/fgas/font/cfgas_fontmgr.h
@@ -11,22 +11,22 @@
 #include <map>
 #include <memory>
 #include <set>
-#include <utility>
 #include <vector>
 
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
+#include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/observable.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/cfx_face.h"
 #include "core/fxge/fx_freetype.h"
-#include "core/fxge/ifx_systemfontinfo.h"
 #include "xfa/fgas/font/cfgas_pdffontmgr.h"
 
 class CFGAS_GEFont;
 class CFX_FontMapper;
 class CFX_FontSourceEnum_File;
+class IFX_SeekableReadStream;
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
 struct FX_FONTMATCHPARAMS {
   const wchar_t* pwsFamily;
   uint32_t dwFontStyles;
@@ -63,11 +63,7 @@
          wcscmp(left.wsFontFace, right.wsFontFace) == 0;
 }
 
-typedef void (*FX_LPEnumAllFonts)(std::deque<FX_FONTDESCRIPTOR>* fonts,
-                                  const wchar_t* pwsFaceName,
-                                  wchar_t wUnicode);
-
-#else  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else  // defined(OS_WIN)
 
 class CFX_FontDescriptor {
  public:
@@ -75,9 +71,9 @@
   ~CFX_FontDescriptor();
 
   int32_t m_nFaceIndex;
+  uint32_t m_dwFontStyles;
   WideString m_wsFaceName;
   std::vector<WideString> m_wsFamilyNames;
-  uint32_t m_dwFontStyles;
   uint32_t m_dwUsb[4];
   uint32_t m_dwCsb[2];
 };
@@ -98,35 +94,9 @@
   }
 };
 
-class CFX_FontSourceEnum_File {
- public:
-  CFX_FontSourceEnum_File();
-  ~CFX_FontSourceEnum_File();
+#endif  // defined(OS_WIN)
 
-  bool HasStartPosition();
-  std::pair<bool, RetainPtr<IFX_SeekableStream>> GetNext();
-
- private:
-  struct HandleParentPath {
-    HandleParentPath() = default;
-    HandleParentPath(const HandleParentPath& x) {
-      pFileHandle = x.pFileHandle;
-      bsParentPath = x.bsParentPath;
-    }
-    FX_FileHandle* pFileHandle;
-    ByteString bsParentPath;
-  };
-
-  ByteString GetNextFile();
-
-  WideString m_wsNext;
-  std::vector<HandleParentPath> m_FolderQueue;
-  std::vector<ByteString> m_FolderPaths;
-};
-
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-class CFGAS_FontMgr : public Observable<CFGAS_FontMgr> {
+class CFGAS_FontMgr final : public Observable {
  public:
   CFGAS_FontMgr();
   ~CFGAS_FontMgr();
@@ -145,7 +115,14 @@
   bool EnumFonts();
 
  private:
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+  RetainPtr<CFGAS_GEFont> GetFontByUnicodeImpl(wchar_t wUnicode,
+                                               uint32_t dwFontStyles,
+                                               const wchar_t* pszFontFamily,
+                                               uint32_t dwHash,
+                                               uint16_t wCodePage,
+                                               uint16_t wBitField);
+
+#if defined(OS_WIN)
   const FX_FONTDESCRIPTOR* FindFont(const wchar_t* pszFontFamily,
                                     uint32_t dwFontStyles,
                                     bool matchParagraphStyle,
@@ -153,54 +130,34 @@
                                     uint32_t dwUSB,
                                     wchar_t wUnicode);
 
-  FX_LPEnumAllFonts m_pEnumerator;
-  std::deque<FX_FONTDESCRIPTOR> m_FontFaces;
-#else   // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#else   // defined(OS_WIN)
   bool EnumFontsFromFontMapper();
   bool EnumFontsFromFiles();
-  void RegisterFace(FXFT_Face pFace, const WideString* pFaceName);
+  void RegisterFace(RetainPtr<CFX_Face> pFace, const WideString* pFaceName);
   void RegisterFaces(const RetainPtr<IFX_SeekableReadStream>& pFontStream,
                      const WideString* pFaceName);
-  void GetNames(const uint8_t* name_table, std::vector<WideString>& Names);
-  std::vector<uint16_t> GetCharsets(FXFT_Face pFace) const;
-  void GetUSBCSB(FXFT_Face pFace, uint32_t* USB, uint32_t* CSB);
-  uint32_t GetFlags(FXFT_Face pFace);
-  bool VerifyUnicode(CFX_FontDescriptor* pDesc, wchar_t wcUnicode);
-  int32_t IsPartName(const WideString& Name1, const WideString& Name2);
   void MatchFonts(std::vector<CFX_FontDescriptorInfo>* MatchedFonts,
                   uint16_t wCodePage,
                   uint32_t dwFontStyles,
                   const WideString& FontName,
-                  wchar_t wcUnicode = 0xFFFE);
-  int32_t CalcPenalty(CFX_FontDescriptor* pInstalled,
-                      uint16_t wCodePage,
-                      uint32_t dwFontStyles,
-                      const WideString& FontName,
-                      wchar_t wcUnicode = 0xFFFE);
-  RetainPtr<CFGAS_GEFont> LoadFont(const WideString& wsFaceName,
-                                   int32_t iFaceIndex,
-                                   int32_t* pFaceCount);
-  FXFT_Face LoadFace(const RetainPtr<IFX_SeekableReadStream>& pFontStream,
-                     int32_t iFaceIndex);
-  RetainPtr<IFX_SeekableReadStream> CreateFontStream(
-      CFX_FontMapper* pFontMapper,
-      IFX_SystemFontInfo* pSystemFontInfo,
-      uint32_t index);
-  RetainPtr<IFX_SeekableReadStream> CreateFontStream(
-      const ByteString& bsFaceName);
+                  wchar_t wcUnicode);
+  RetainPtr<CFGAS_GEFont> LoadFontInternal(const WideString& wsFaceName,
+                                           int32_t iFaceIndex);
+#endif  // defined(OS_WIN)
 
+  std::map<uint32_t, std::vector<RetainPtr<CFGAS_GEFont>>> m_Hash2Fonts;
+  std::set<wchar_t> m_FailedUnicodesSet;
+
+#if defined(OS_WIN)
+  std::deque<FX_FONTDESCRIPTOR> m_FontFaces;
+#else
   std::unique_ptr<CFX_FontSourceEnum_File> m_pFontSource;
   std::vector<std::unique_ptr<CFX_FontDescriptor>> m_InstalledFonts;
   std::map<uint32_t, std::unique_ptr<std::vector<CFX_FontDescriptorInfo>>>
       m_Hash2CandidateList;
   std::map<RetainPtr<CFGAS_GEFont>, RetainPtr<IFX_SeekableReadStream>>
       m_IFXFont2FileRead;
-  std::set<wchar_t> m_FailedUnicodesSet;
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-
-  bool VerifyUnicode(const RetainPtr<CFGAS_GEFont>& pFont, wchar_t wcUnicode);
-
-  std::map<uint32_t, std::vector<RetainPtr<CFGAS_GEFont>>> m_Hash2Fonts;
+#endif  // defined(OS_WIN)
 };
 
 #endif  // XFA_FGAS_FONT_CFGAS_FONTMGR_H_
diff --git a/xfa/fgas/font/cfgas_gefont.cpp b/xfa/fgas/font/cfgas_gefont.cpp
index 83b7ad6..5a7c625 100644
--- a/xfa/fgas/font/cfgas_gefont.cpp
+++ b/xfa/fgas/font/cfgas_gefont.cpp
@@ -9,11 +9,13 @@
 #include <memory>
 #include <utility>
 
+#include "build/build_config.h"
+#include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_substfont.h"
-#include "core/fxge/cfx_unicodeencoding.h"
 #include "core/fxge/cfx_unicodeencodingex.h"
+#include "core/fxge/fx_font.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 
@@ -22,25 +24,26 @@
                                                uint32_t dwFontStyles,
                                                uint16_t wCodePage,
                                                CFGAS_FontMgr* pFontMgr) {
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
-  if (!pFontMgr)
-    return nullptr;
-
-  return pFontMgr->GetFontByCodePage(wCodePage, dwFontStyles, pszFontFamily);
-#else
+#if defined(OS_WIN)
   auto pFont = pdfium::MakeRetain<CFGAS_GEFont>(pFontMgr);
   if (!pFont->LoadFontInternal(pszFontFamily, dwFontStyles, wCodePage))
     return nullptr;
   return pFont;
+#else
+  if (!pFontMgr)
+    return nullptr;
+  return pFontMgr->GetFontByCodePage(wCodePage, dwFontStyles, pszFontFamily);
 #endif
 }
 
 // static
-RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadFont(CFX_Font* pExternalFont,
-                                               CFGAS_FontMgr* pFontMgr) {
+RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadFont(
+    const RetainPtr<CPDF_Font>& pPDFFont,
+    CFGAS_FontMgr* pFontMgr) {
   auto pFont = pdfium::MakeRetain<CFGAS_GEFont>(pFontMgr);
-  if (!pFont->LoadFontInternal(pExternalFont))
+  if (!pFont->LoadFontInternal(pPDFFont))
     return nullptr;
+
   return pFont;
 }
 
@@ -54,21 +57,21 @@
   return pFont;
 }
 
-CFGAS_GEFont::CFGAS_GEFont(CFGAS_FontMgr* pFontMgr)
-    :
-      m_bUseLogFontStyle(false),
-      m_dwLogFontStyle(0),
-      m_pFont(nullptr),
-      m_bExternalFont(false),
-      m_pFontMgr(pFontMgr) {
+// static
+RetainPtr<CFGAS_GEFont> CFGAS_GEFont::LoadStockFont(
+    CPDF_Document* pDoc,
+    CFGAS_FontMgr* pMgr,
+    const ByteString& font_family) {
+  RetainPtr<CPDF_Font> stock_font =
+      CPDF_Font::GetStockFont(pDoc, font_family.AsStringView());
+  return stock_font ? CFGAS_GEFont::LoadFont(stock_font, pMgr) : nullptr;
 }
 
-CFGAS_GEFont::~CFGAS_GEFont() {
-  if (!m_bExternalFont)
-    delete m_pFont;
-}
+CFGAS_GEFont::CFGAS_GEFont(CFGAS_FontMgr* pFontMgr) : m_pFontMgr(pFontMgr) {}
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+CFGAS_GEFont::~CFGAS_GEFont() = default;
+
+#if defined(OS_WIN)
 bool CFGAS_GEFont::LoadFontInternal(const wchar_t* pszFontFamily,
                                     uint32_t dwFontStyles,
                                     uint16_t wCodePage) {
@@ -76,41 +79,42 @@
     return false;
   ByteString csFontFamily;
   if (pszFontFamily)
-    csFontFamily = ByteString::FromUnicode(pszFontFamily);
+    csFontFamily = WideString(pszFontFamily).ToDefANSI();
 
   int32_t iWeight =
-      FontStyleIsBold(dwFontStyles) ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL;
-  m_pFont = new CFX_Font;
-  if (FontStyleIsItalic(dwFontStyles) && FontStyleIsBold(dwFontStyles))
+      FontStyleIsForceBold(dwFontStyles) ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL;
+  m_pFont = pdfium::MakeUnique<CFX_Font>();
+  if (FontStyleIsItalic(dwFontStyles) && FontStyleIsForceBold(dwFontStyles))
     csFontFamily += ",BoldItalic";
-  else if (FontStyleIsBold(dwFontStyles))
+  else if (FontStyleIsForceBold(dwFontStyles))
     csFontFamily += ",Bold";
   else if (FontStyleIsItalic(dwFontStyles))
     csFontFamily += ",Italic";
 
   m_pFont->LoadSubst(csFontFamily, true, dwFontStyles, iWeight, 0, wCodePage,
                      false);
-  if (!m_pFont->GetFace())
-    return false;
-  return InitFont();
+  return m_pFont->GetFaceRec() && InitFont();
 }
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif  // defined(OS_WIN)
 
-bool CFGAS_GEFont::LoadFontInternal(CFX_Font* pExternalFont) {
+bool CFGAS_GEFont::LoadFontInternal(const RetainPtr<CPDF_Font>& pPDFFont) {
+  CFX_Font* pExternalFont = pPDFFont->GetFont();
   if (m_pFont || !pExternalFont)
     return false;
 
   m_pFont = pExternalFont;
-  m_bExternalFont = true;
-  return InitFont();
+  if (!InitFont())
+    return false;
+
+  m_pPDFFont = pPDFFont;  // Keep pPDFFont alive for the duration.
+  return true;
 }
 
 bool CFGAS_GEFont::LoadFontInternal(std::unique_ptr<CFX_Font> pInternalFont) {
   if (m_pFont || !pInternalFont)
     return false;
 
-  m_pFont = pInternalFont.release();
-  m_bExternalFont = false;
+  m_pFont = std::move(pInternalFont);
   return InitFont();
 }
 
@@ -121,71 +125,66 @@
   if (m_pFontEncoding)
     return true;
 
-  m_pFontEncoding = FX_CreateFontEncodingEx(m_pFont, FXFM_ENCODING_NONE);
+  m_pFontEncoding = FX_CreateFontEncodingEx(m_pFont.Get());
   return !!m_pFontEncoding;
 }
 
 WideString CFGAS_GEFont::GetFamilyName() const {
-  if (!m_pFont->GetSubstFont() ||
-      m_pFont->GetSubstFont()->m_Family.GetLength() == 0) {
-    return WideString::FromLocal(m_pFont->GetFamilyName().AsStringView());
-  }
-  return WideString::FromLocal(
-      m_pFont->GetSubstFont()->m_Family.AsStringView());
+  CFX_SubstFont* subst_font = m_pFont->GetSubstFont();
+  ByteString family_name = subst_font && !subst_font->m_Family.IsEmpty()
+                               ? subst_font->m_Family
+                               : m_pFont->GetFamilyName();
+  return WideString::FromDefANSI(family_name.AsStringView());
 }
 
 uint32_t CFGAS_GEFont::GetFontStyles() const {
   ASSERT(m_pFont);
-  if (m_bUseLogFontStyle)
-    return m_dwLogFontStyle;
+  if (m_dwLogFontStyle.has_value())
+    return m_dwLogFontStyle.value();
 
   uint32_t dwStyles = 0;
   auto* pSubstFont = m_pFont->GetSubstFont();
   if (pSubstFont) {
     if (pSubstFont->m_Weight == FXFONT_FW_BOLD)
-      dwStyles |= FXFONT_BOLD;
-    if (pSubstFont->m_bFlagItalic)
-      dwStyles |= FXFONT_ITALIC;
+      dwStyles |= FXFONT_FORCE_BOLD;
   } else {
     if (m_pFont->IsBold())
-      dwStyles |= FXFONT_BOLD;
+      dwStyles |= FXFONT_FORCE_BOLD;
     if (m_pFont->IsItalic())
       dwStyles |= FXFONT_ITALIC;
   }
   return dwStyles;
 }
 
-bool CFGAS_GEFont::GetCharWidth(wchar_t wUnicode, int32_t& iWidth) {
+bool CFGAS_GEFont::GetCharWidth(wchar_t wUnicode, int32_t* pWidth) {
   auto it = m_CharWidthMap.find(wUnicode);
-  iWidth = it != m_CharWidthMap.end() ? it->second : 0;
-  if (iWidth == 65535)
+  *pWidth = it != m_CharWidthMap.end() ? it->second : 0;
+  if (*pWidth == 65535)
     return false;
 
-  if (iWidth > 0)
+  if (*pWidth > 0)
     return true;
 
-  if (!m_pProvider || !m_pProvider->GetCharWidth(RetainPtr<CFGAS_GEFont>(this),
-                                                 wUnicode, &iWidth)) {
-    RetainPtr<CFGAS_GEFont> pFont;
-    int32_t iGlyph;
-    std::tie(iGlyph, pFont) = GetGlyphIndexAndFont(wUnicode, true);
-    if (iGlyph != 0xFFFF && pFont) {
-      if (pFont.Get() == this) {
-        iWidth = m_pFont->GetGlyphWidth(iGlyph);
-        if (iWidth < 0)
-          iWidth = -1;
-      } else if (pFont->GetCharWidth(wUnicode, iWidth)) {
-        return true;
-      }
-    } else {
-      iWidth = -1;
+  RetainPtr<CFGAS_GEFont> pFont;
+  int32_t iGlyph;
+  std::tie(iGlyph, pFont) = GetGlyphIndexAndFont(wUnicode, true);
+  if (iGlyph != 0xFFFF && pFont) {
+    if (pFont == this) {
+      *pWidth = m_pFont->GetGlyphWidth(iGlyph);
+      if (*pWidth < 0)
+        *pWidth = -1;
+    } else if (pFont->GetCharWidth(wUnicode, pWidth)) {
+      return true;
     }
+  } else {
+    *pWidth = -1;
   }
-  m_CharWidthMap[wUnicode] = iWidth;
-  return iWidth > 0;
+
+  m_CharWidthMap[wUnicode] = *pWidth;
+  return *pWidth > 0;
 }
 
-bool CFGAS_GEFont::GetCharBBox(wchar_t wUnicode, CFX_Rect* bbox) {
+bool CFGAS_GEFont::GetCharBBox(wchar_t wUnicode, FX_RECT* bbox) {
   auto it = m_BBoxMap.find(wUnicode);
   if (it != m_BBoxMap.end()) {
     *bbox = it->second;
@@ -202,25 +201,16 @@
     return pFont->GetCharBBox(wUnicode, bbox);
 
   FX_RECT rtBBox;
-  if (!m_pFont->GetGlyphBBox(iGlyph, rtBBox))
+  if (!m_pFont->GetGlyphBBox(iGlyph, &rtBBox))
     return false;
 
-  CFX_Rect rt(rtBBox.left, rtBBox.top, rtBBox.Width(), rtBBox.Height());
-  m_BBoxMap[wUnicode] = rt;
-  *bbox = rt;
+  m_BBoxMap[wUnicode] = rtBBox;
+  *bbox = rtBBox;
   return true;
 }
 
-bool CFGAS_GEFont::GetBBox(CFX_Rect* bbox) {
-  FX_RECT rt(0, 0, 0, 0);
-  if (!m_pFont->GetBBox(rt))
-    return false;
-
-  bbox->left = rt.left;
-  bbox->width = rt.Width();
-  bbox->top = rt.bottom;
-  bbox->height = -rt.Height();
-  return true;
+bool CFGAS_GEFont::GetBBox(FX_RECT* bbox) {
+  return m_pFont->GetBBox(bbox);
 }
 
 int32_t CFGAS_GEFont::GetGlyphIndex(wchar_t wUnicode) {
@@ -235,7 +225,7 @@
     bool bRecursive) {
   int32_t iGlyphIndex = m_pFontEncoding->GlyphFromCharCode(wUnicode);
   if (iGlyphIndex > 0)
-    return {iGlyphIndex, RetainPtr<CFGAS_GEFont>(this)};
+    return {iGlyphIndex, pdfium::WrapRetain(this)};
 
   const FGAS_FONTUSB* pFontUSB = FGAS_GetUnicodeBitField(wUnicode);
   if (!pFontUSB)
@@ -263,11 +253,11 @@
   WideString wsFamily = GetFamilyName();
   RetainPtr<CFGAS_GEFont> pFont =
       m_pFontMgr->GetFontByUnicode(wUnicode, GetFontStyles(), wsFamily.c_str());
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
+#if !defined(OS_WIN)
   if (!pFont)
     pFont = m_pFontMgr->GetFontByUnicode(wUnicode, GetFontStyles(), nullptr);
 #endif
-  if (!pFont || pFont.Get() == this)  // Avoids direct cycles below.
+  if (!pFont || pFont == this)  // Avoids direct cycles below.
     return {0xFFFF, nullptr};
 
   m_FontMapper[wUnicode] = pFont;
@@ -292,6 +282,6 @@
 RetainPtr<CFGAS_GEFont> CFGAS_GEFont::GetSubstFont(int32_t iGlyphIndex) {
   iGlyphIndex = static_cast<uint32_t>(iGlyphIndex) >> 24;
   if (iGlyphIndex == 0)
-    return RetainPtr<CFGAS_GEFont>(this);
+    return pdfium::WrapRetain(this);
   return m_SubstFonts[iGlyphIndex - 1];
 }
diff --git a/xfa/fgas/font/cfgas_gefont.h b/xfa/fgas/font/cfgas_gefont.h
index 646562d..7e021e1 100644
--- a/xfa/fgas/font/cfgas_gefont.h
+++ b/xfa/fgas/font/cfgas_gefont.h
@@ -12,18 +12,21 @@
 #include <utility>
 #include <vector>
 
-#include "core/fxcrt/fx_memory.h"
+#include "build/build_config.h"
+#include "core/fxcrt/fx_coordinates.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/base/optional.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
-#include "xfa/fgas/font/cfgas_pdffontmgr.h"
 
-class CFX_UnicodeEncoding;
+class CFX_Font;
+class CFX_UnicodeEncodingEx;
+class CPDF_Document;
+class CPDF_Font;
 
-class CFGAS_GEFont : public Retainable {
+class CFGAS_GEFont final : public Retainable {
  public:
-  template <typename T>
-  friend class RetainPtr;
   template <typename T, typename... Args>
   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
@@ -31,30 +34,29 @@
                                           uint32_t dwFontStyles,
                                           uint16_t wCodePage,
                                           CFGAS_FontMgr* pFontMgr);
-  static RetainPtr<CFGAS_GEFont> LoadFont(CFX_Font* pExternalFont,
+  static RetainPtr<CFGAS_GEFont> LoadFont(const RetainPtr<CPDF_Font>& pPDFFont,
                                           CFGAS_FontMgr* pFontMgr);
   static RetainPtr<CFGAS_GEFont> LoadFont(
       std::unique_ptr<CFX_Font> pInternalFont,
       CFGAS_FontMgr* pFontMgr);
 
+  static RetainPtr<CFGAS_GEFont> LoadStockFont(CPDF_Document* pDoc,
+                                               CFGAS_FontMgr* pMgr,
+                                               const ByteString& font_family);
+
   uint32_t GetFontStyles() const;
-  bool GetCharWidth(wchar_t wUnicode, int32_t& iWidth);
+  bool GetCharWidth(wchar_t wUnicode, int32_t* pWidth);
   int32_t GetGlyphIndex(wchar_t wUnicode);
   int32_t GetAscent() const;
   int32_t GetDescent() const;
 
-  bool GetCharBBox(wchar_t wUnicode, CFX_Rect* bbox);
-  bool GetBBox(CFX_Rect* bbox);
+  bool GetCharBBox(wchar_t wUnicode, FX_RECT* bbox);
+  bool GetBBox(FX_RECT* bbox);
 
   RetainPtr<CFGAS_GEFont> GetSubstFont(int32_t iGlyphIndex);
-  CFX_Font* GetDevFont() const { return m_pFont; }
-
-  void SetFontProvider(CFGAS_PDFFontMgr* pProvider) {
-    m_pProvider.Reset(pProvider);
-  }
+  CFX_Font* GetDevFont() const { return m_pFont.Get(); }
 
   void SetLogicalFontStyle(uint32_t dwLogFontStyle) {
-    m_bUseLogFontStyle = true;
     m_dwLogFontStyle = dwLogFontStyle;
   }
 
@@ -62,34 +64,27 @@
   explicit CFGAS_GEFont(CFGAS_FontMgr* pFontMgr);
   ~CFGAS_GEFont() override;
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   bool LoadFontInternal(const wchar_t* pszFontFamily,
                         uint32_t dwFontStyles,
                         uint16_t wCodePage);
   bool LoadFontInternal(const uint8_t* pBuffer, int32_t length);
-  bool LoadFontInternal(const RetainPtr<CFX_SeekableStreamProxy>& pFontStream,
-                        bool bSaveStream);
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#endif
   bool LoadFontInternal(std::unique_ptr<CFX_Font> pInternalFont);
-  bool LoadFontInternal(CFX_Font* pExternalFont);
+  bool LoadFontInternal(const RetainPtr<CPDF_Font>& pPDFFont);
   bool InitFont();
   std::pair<int32_t, RetainPtr<CFGAS_GEFont>> GetGlyphIndexAndFont(
       wchar_t wUnicode,
       bool bRecursive);
   WideString GetFamilyName() const;
 
-  bool m_bUseLogFontStyle;
-  uint32_t m_dwLogFontStyle;
-  CFX_Font* m_pFont;
-  bool m_bExternalFont;
-  RetainPtr<CFGAS_GEFont> m_pSrcFont;  // Only set by ctor, so no cycles.
-  CFGAS_FontMgr::ObservedPtr m_pFontMgr;
-  CFGAS_PDFFontMgr::ObservedPtr m_pProvider;
-  RetainPtr<CFX_SeekableStreamProxy> m_pStream;
-  RetainPtr<IFX_SeekableReadStream> m_pFileRead;
-  std::unique_ptr<CFX_UnicodeEncoding> m_pFontEncoding;
+  Optional<uint32_t> m_dwLogFontStyle;
+  RetainPtr<CPDF_Font> m_pPDFFont;  // Must come before |m_pFont|.
+  MaybeOwned<CFX_Font> m_pFont;     // Must come before |m_pFontEncoding|.
+  ObservedPtr<CFGAS_FontMgr> const m_pFontMgr;
+  std::unique_ptr<CFX_UnicodeEncodingEx> m_pFontEncoding;
   std::map<wchar_t, int32_t> m_CharWidthMap;
-  std::map<wchar_t, CFX_Rect> m_BBoxMap;
+  std::map<wchar_t, FX_RECT> m_BBoxMap;
   std::vector<RetainPtr<CFGAS_GEFont>> m_SubstFonts;
   std::map<wchar_t, RetainPtr<CFGAS_GEFont>> m_FontMapper;
 };
diff --git a/xfa/fgas/font/cfgas_pdffontmgr.cpp b/xfa/fgas/font/cfgas_pdffontmgr.cpp
index 4d34ac8..b342237 100644
--- a/xfa/fgas/font/cfgas_pdffontmgr.cpp
+++ b/xfa/fgas/font/cfgas_pdffontmgr.cpp
@@ -9,7 +9,10 @@
 #include <algorithm>
 
 #include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/cpdf_docpagedata.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxge/fx_font.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 
@@ -35,7 +38,6 @@
 RetainPtr<CFGAS_GEFont> CFGAS_PDFFontMgr::FindFont(const ByteString& strPsName,
                                                    bool bBold,
                                                    bool bItalic,
-                                                   CPDF_Font** pDstPDFFont,
                                                    bool bStrictMatch) {
   CPDF_Dictionary* pFontSetDict =
       m_pDoc->GetRoot()->GetDictFor("AcroForm")->GetDictFor("DR");
@@ -48,9 +50,12 @@
 
   ByteString name = strPsName;
   name.Remove(' ');
-  for (const auto& it : *pFontSetDict) {
+
+  auto* pData = CPDF_DocPageData::FromDocument(m_pDoc.Get());
+  CPDF_DictionaryLocker locker(pFontSetDict);
+  for (const auto& it : locker) {
     const ByteString& key = it.first;
-    CPDF_Object* pObj = it.second.get();
+    CPDF_Object* pObj = it.second.Get();
     if (!PsNameMatchDRFontName(name.AsStringView(), bBold, bItalic, key,
                                bStrictMatch)) {
       continue;
@@ -59,36 +64,30 @@
     if (!pFontDict || pFontDict->GetStringFor("Type") != "Font")
       return nullptr;
 
-    CPDF_Font* pPDFFont = m_pDoc->LoadFont(pFontDict);
-    if (!pPDFFont)
+    RetainPtr<CPDF_Font> pPDFFont = pData->GetFont(pFontDict);
+    if (!pPDFFont || !pPDFFont->IsEmbedded())
       return nullptr;
 
-    if (!pPDFFont->IsEmbedded()) {
-      *pDstPDFFont = pPDFFont;
-      return nullptr;
-    }
-    return CFGAS_GEFont::LoadFont(pPDFFont->GetFont(), m_pFontMgr.Get());
+    return CFGAS_GEFont::LoadFont(pPDFFont, m_pFontMgr.Get());
   }
   return nullptr;
 }
 
-RetainPtr<CFGAS_GEFont> CFGAS_PDFFontMgr::GetFont(
-    const WideStringView& wsFontFamily,
-    uint32_t dwFontStyles,
-    CPDF_Font** pPDFFont,
-    bool bStrictMatch) {
+RetainPtr<CFGAS_GEFont> CFGAS_PDFFontMgr::GetFont(WideStringView wsFontFamily,
+                                                  uint32_t dwFontStyles,
+                                                  bool bStrictMatch) {
   uint32_t dwHashCode = FX_HashCode_GetW(wsFontFamily, false);
   ByteString strKey = ByteString::Format("%u%u", dwHashCode, dwFontStyles);
   auto it = m_FontMap.find(strKey);
   if (it != m_FontMap.end())
     return it->second;
 
-  ByteString bsPsName = ByteString::FromUnicode(WideString(wsFontFamily));
-  bool bBold = FontStyleIsBold(dwFontStyles);
+  ByteString bsPsName = WideString(wsFontFamily).ToDefANSI();
+  bool bBold = FontStyleIsForceBold(dwFontStyles);
   bool bItalic = FontStyleIsItalic(dwFontStyles);
   ByteString strFontName = PsNameToFontName(bsPsName, bBold, bItalic);
   RetainPtr<CFGAS_GEFont> pFont =
-      FindFont(strFontName, bBold, bItalic, pPDFFont, bStrictMatch);
+      FindFont(strFontName, bBold, bItalic, bStrictMatch);
   if (pFont)
     m_FontMap[strKey] = pFont;
 
@@ -111,7 +110,7 @@
   return strPsName;
 }
 
-bool CFGAS_PDFFontMgr::PsNameMatchDRFontName(const ByteStringView& bsPsName,
+bool CFGAS_PDFFontMgr::PsNameMatchDRFontName(ByteStringView bsPsName,
                                              bool bBold,
                                              bool bItalic,
                                              const ByteString& bsDRFontName,
@@ -150,7 +149,7 @@
       return false;
 
     if (iDifferLength > 1) {
-      ByteString bsDRTailer = bsDRName.Right(iDifferLength);
+      ByteString bsDRTailer = bsDRName.Last(iDifferLength);
       if (bsDRTailer == "MT" || bsDRTailer == "PSMT" ||
           bsDRTailer == "Regular" || bsDRTailer == "Reg") {
         return true;
@@ -161,17 +160,17 @@
       bool bMatch = false;
       switch (bsPsName[iPsLen - 1]) {
         case 'L':
-          if (bsDRName.Right(5) == "Light")
+          if (bsDRName.Last(5) == "Light")
             bMatch = true;
 
           break;
         case 'R':
-          if (bsDRName.Right(7) == "Regular" || bsDRName.Right(3) == "Reg")
+          if (bsDRName.Last(7) == "Regular" || bsDRName.Last(3) == "Reg")
             bMatch = true;
 
           break;
         case 'M':
-          if (bsDRName.Right(5) == "Medium")
+          if (bsDRName.Last(5) == "Medium")
             bMatch = true;
           break;
         default:
@@ -182,23 +181,3 @@
   }
   return true;
 }
-
-bool CFGAS_PDFFontMgr::GetCharWidth(const RetainPtr<CFGAS_GEFont>& pFont,
-                                    wchar_t wUnicode,
-                                    int32_t* pWidth) {
-  if (wUnicode != 0x20)
-    return false;
-
-  auto it = m_FDE2PDFFont.find(pFont);
-  if (it == m_FDE2PDFFont.end())
-    return false;
-
-  CPDF_Font* pPDFFont = it->second;
-  *pWidth = pPDFFont->GetCharWidthF(pPDFFont->CharCodeFromUnicode(wUnicode));
-  return true;
-}
-
-void CFGAS_PDFFontMgr::SetFont(const RetainPtr<CFGAS_GEFont>& pFont,
-                               CPDF_Font* pPDFFont) {
-  m_FDE2PDFFont[pFont] = pPDFFont;
-}
diff --git a/xfa/fgas/font/cfgas_pdffontmgr.h b/xfa/fgas/font/cfgas_pdffontmgr.h
index a019990..f9c56fc 100644
--- a/xfa/fgas/font/cfgas_pdffontmgr.h
+++ b/xfa/fgas/font/cfgas_pdffontmgr.h
@@ -9,41 +9,32 @@
 
 #include <map>
 
-#include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/observable.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 
 class CFGAS_FontMgr;
 class CFGAS_GEFont;
 class CPDF_Document;
-class CPDF_Font;
 
-class CFGAS_PDFFontMgr : public Observable<CFGAS_PDFFontMgr> {
+class CFGAS_PDFFontMgr final : public Observable {
  public:
   explicit CFGAS_PDFFontMgr(CPDF_Document* pDoc, CFGAS_FontMgr* pFontMgr);
   ~CFGAS_PDFFontMgr();
 
-  void SetFont(const RetainPtr<CFGAS_GEFont>& pFont, CPDF_Font* pPDFFont);
-  RetainPtr<CFGAS_GEFont> GetFont(const WideStringView& wsFontFamily,
+  RetainPtr<CFGAS_GEFont> GetFont(WideStringView wsFontFamily,
                                   uint32_t dwFontStyles,
-                                  CPDF_Font** pPDFFont,
                                   bool bStrictMatch);
-  bool GetCharWidth(const RetainPtr<CFGAS_GEFont>& pFont,
-                    wchar_t wUnicode,
-                    int32_t* pWidth);
 
  private:
   RetainPtr<CFGAS_GEFont> FindFont(const ByteString& strFamilyName,
                                    bool bBold,
                                    bool bItalic,
-                                   CPDF_Font** pPDFFont,
                                    bool bStrictMatch);
   ByteString PsNameToFontName(const ByteString& strPsName,
                               bool bBold,
                               bool bItalic);
-  bool PsNameMatchDRFontName(const ByteStringView& bsPsName,
+  bool PsNameMatchDRFontName(ByteStringView bsPsName,
                              bool bBold,
                              bool bItalic,
                              const ByteString& bsDRFontName,
@@ -51,7 +42,6 @@
 
   UnownedPtr<CPDF_Document> const m_pDoc;
   UnownedPtr<CFGAS_FontMgr> const m_pFontMgr;
-  std::map<RetainPtr<CFGAS_GEFont>, CPDF_Font*> m_FDE2PDFFont;
   std::map<ByteString, RetainPtr<CFGAS_GEFont>> m_FontMap;
 };
 
diff --git a/xfa/fgas/font/cfx_fontsourceenum_file.cpp b/xfa/fgas/font/cfx_fontsourceenum_file.cpp
new file mode 100644
index 0000000..83283ec
--- /dev/null
+++ b/xfa/fgas/font/cfx_fontsourceenum_file.cpp
@@ -0,0 +1,97 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/font/cfx_fontsourceenum_file.h"
+
+#include <iterator>
+
+#include "build/build_config.h"
+
+namespace {
+
+constexpr char kFolderSeparator = '/';
+
+constexpr const char* kFontFolders[] = {
+#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+    "/usr/share/fonts",
+    "/usr/share/X11/fonts/Type1",
+    "/usr/share/X11/fonts/TTF",
+    "/usr/local/share/fonts",
+#elif defined(OS_MACOSX)
+    "~/Library/Fonts",
+    "/Library/Fonts",
+    "/System/Library/Fonts",
+#elif defined(OS_ANDROID)
+    "/system/fonts",
+#endif
+};
+
+}  // namespace
+
+CFX_FontSourceEnum_File::CFX_FontSourceEnum_File()
+    : m_FolderPaths(std::begin(kFontFolders), std::end(kFontFolders)) {}
+
+CFX_FontSourceEnum_File::~CFX_FontSourceEnum_File() = default;
+
+ByteString CFX_FontSourceEnum_File::GetNextFile() {
+  FX_FolderHandle* pCurHandle =
+      !m_FolderQueue.empty() ? m_FolderQueue.back().pFolderHandle : nullptr;
+  if (!pCurHandle) {
+    if (m_FolderPaths.empty())
+      return ByteString();
+    pCurHandle = FX_OpenFolder(m_FolderPaths.back().c_str());
+    HandleParentPath hpp;
+    hpp.pFolderHandle = pCurHandle;
+    hpp.bsParentPath = m_FolderPaths.back();
+    m_FolderQueue.push_back(hpp);
+  }
+  ByteString bsName;
+  bool bFolder;
+  while (true) {
+    if (!FX_GetNextFile(pCurHandle, &bsName, &bFolder)) {
+      FX_CloseFolder(pCurHandle);
+      if (!m_FolderQueue.empty())
+        m_FolderQueue.pop_back();
+      if (m_FolderQueue.empty()) {
+        if (!m_FolderPaths.empty())
+          m_FolderPaths.pop_back();
+        return !m_FolderPaths.empty() ? GetNextFile() : ByteString();
+      }
+      pCurHandle = m_FolderQueue.back().pFolderHandle;
+      continue;
+    }
+    if (bsName == "." || bsName == "..")
+      continue;
+    if (bFolder) {
+      HandleParentPath hpp;
+      hpp.bsParentPath =
+          m_FolderQueue.back().bsParentPath + kFolderSeparator + bsName;
+      hpp.pFolderHandle = FX_OpenFolder(hpp.bsParentPath.c_str());
+      if (!hpp.pFolderHandle)
+        continue;
+      m_FolderQueue.push_back(hpp);
+      pCurHandle = hpp.pFolderHandle;
+      continue;
+    }
+    bsName = m_FolderQueue.back().bsParentPath + kFolderSeparator + bsName;
+    break;
+  }
+  return bsName;
+}
+
+void CFX_FontSourceEnum_File::GetNext() {
+  m_wsNext = WideString::FromUTF8(GetNextFile().AsStringView());
+}
+
+bool CFX_FontSourceEnum_File::HasNext() const {
+  return !m_wsNext.IsEmpty();
+}
+
+RetainPtr<IFX_SeekableStream> CFX_FontSourceEnum_File::GetStream() const {
+  ASSERT(HasNext());
+  return IFX_SeekableStream::CreateFromFilename(m_wsNext.c_str(),
+                                                FX_FILEMODE_ReadOnly);
+}
diff --git a/xfa/fgas/font/cfx_fontsourceenum_file.h b/xfa/fgas/font/cfx_fontsourceenum_file.h
new file mode 100644
index 0000000..9e93a97
--- /dev/null
+++ b/xfa/fgas/font/cfx_fontsourceenum_file.h
@@ -0,0 +1,49 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_FONT_CFX_FONTSOURCEENUM_FILE_H_
+#define XFA_FGAS_FONT_CFX_FONTSOURCEENUM_FILE_H_
+
+#include <vector>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+
+#if defined(OS_WIN)
+#error "Not used on Windows"
+#endif
+
+class CFX_FontSourceEnum_File {
+ public:
+  CFX_FontSourceEnum_File();
+  ~CFX_FontSourceEnum_File();
+
+  void GetNext();
+  bool HasNext() const;
+  RetainPtr<IFX_SeekableStream> GetStream() const;
+
+ private:
+  struct HandleParentPath {
+    HandleParentPath() = default;
+    HandleParentPath(const HandleParentPath& x) {
+      pFolderHandle = x.pFolderHandle;
+      bsParentPath = x.bsParentPath;
+    }
+    FX_FolderHandle* pFolderHandle;
+    ByteString bsParentPath;
+  };
+
+  ByteString GetNextFile();
+
+  WideString m_wsNext;
+  std::vector<HandleParentPath> m_FolderQueue;
+  std::vector<ByteString> m_FolderPaths;
+};
+
+#endif  // XFA_FGAS_FONT_CFX_FONTSOURCEENUM_FILE_H_
diff --git a/xfa/fgas/font/fgas_fontutils.cpp b/xfa/fgas/font/fgas_fontutils.cpp
index 2cf56ce..cc6c922 100644
--- a/xfa/fgas/font/fgas_fontutils.cpp
+++ b/xfa/fgas/font/fgas_fontutils.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fgas/font/fgas_fontutils.h"
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
@@ -188,1706 +189,1687 @@
     {0xFFA0, 0xFFEF, 68, 0xFFFF},
 };
 
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
 const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, L"SimSun", L"Arial", 0, 936},
-    {0x01e4f102, L"YouYuan", L"Arial", 1, 936},
-    {0x030549dc, L"LiSu", L"Arial", 1, 936},
-    {0x032edd44, L"Simhei", L"Arial", 1, 936},
-    {0x03eac6fc, L"PoorRichard-Regular", L"Arial", 2, 1252},
-    {0x03ed90e6, L"Nina", L"Arial", 0, 1252},
-    {0x077b56b3, L"KingsoftPhoneticPlain", L"Arial", 0, 1252},
-    {0x078ed524, L"MicrosoftSansSerif", L"Arial", 0, 1252},
-    {0x089b18a9, L"Arial", L"Arial", 0, 1252},
-    {0x0b2cad72, L"MonotypeCorsiva", L"Arial", 8, 1252},
-    {0x0bb003e7, L"Kartika", L"Arial", 2, 1252},
-    {0x0bb469df, L"VinerHandITC", L"Arial", 8, 1252},
-    {0x0bc1a851, L"SegoeUI", L"Arial", 0, 1252},
-    {0x0c112ebd, L"KozukaGothicPro-VIM", L"Arial", 0, 1252},
-    {0x0cfcb9c1, L"AdobeThai", L"Kokila,Arial Narrow", 0, 847},
-    {0x0e7de0f9, L"Playbill", L"Arial", 0, 1252},
-    {0x0eff47c3, L"STHupo", L"Arial", 0, 936},
-    {0x107ad374, L"Constantia", L"Arial", 2, 1252},
-    {0x12194c2d, L"KunstlerScript", L"Arial", 8, 1252},
-    {0x135ef6a1, L"MinionProSmBd",
-     L"Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
-    {0x158c4049, L"Garamond", L"Arial", 2, 1252},
-    {0x160ecb24, L"STZhongsong", L"Arial", 0, 936},
-    {0x161ed07e, L"MSGothic", L"Arial", 1, 1252},
-    {0x171d1ed1, L"SnapITC-Regular", L"Arial", 0, 1252},
-    {0x18d1188f, L"Cambria", L"Arial", 2, 1252},
-    {0x18eaf350, L"ArialUnicodeMS", L"Arial", 0, 936},
-    {0x1a92d115, L"MingLiU", L"Arial", 1, 1252},
-    {0x1cc217c6, L"TrebuchetMS", L"Arial", 0, 1252},
-    {0x1d649596, L"BasemicTimes", L"Arial", 0, 1252},
-    {0x1e34ee60, L"BellMT", L"Arial", 2, 1252},
-    {0x1eb36945, L"CooperBlack", L"Arial", 2, 1252},
-    {0x1ef7787d, L"BatangChe", L"Arial", 1, 1252},
-    {0x20b3bd3a, L"BrushScriptMT", L"Arial", 8, 1252},
-    {0x220877aa, L"Candara", L"Arial", 0, 1252},
-    {0x22135007, L"FreestyleScript-Regular", L"Arial", 8, 1252},
-    {0x251059c3, L"Chiller", L"Arial", 0, 1252},
-    {0x25bed6dd, L"MSReferenceSansSerif", L"Arial", 0, 1252},
-    {0x28154c81, L"Parchment-Regular", L"Arial", 8, 1252},
-    {0x29711eb9, L"STLiti", L"Arial", 0, 936},
-    {0x2b1993b4, L"Basemic", L"Arial", 0, 1252},
-    {0x2b316339, L"NiagaraSolid-Reg", L"Arial", 0, 1252},
-    {0x2c147529, L"FootlightMTLight", L"Arial", 0, 1252},
-    {0x2c198928, L"HarlowSolid", L"Arial", 0, 1252},
-    {0x2c6ac6b2, L"LucidaBright", L"Arial", 2, 1252},
-    {0x2c9f38e2, L"KozukaMinchoPro-VIR", L"Arial", 0, 1252},
-    {0x2d5a47b0, L"STCaiyun", L"Arial", 0, 936},
-    {0x2def26bf, L"BernardMT-Condensed", L"Arial", 0, 1252},
-    {0x2fd8930b, L"KozukaMinchoPr6NR", L"Arial", 0, 1252},
-    {0x3115525a, L"FangSong_GB2312", L"Arial", 0, 1252},
-    {0x31327817, L"MyriadPro",
-     L"Calibri,Corbel,Candara,Cambria Math,Franklin Gothic Medium,Arial "
-     L"Narrow,Times New Roman",
+    {0x01d5d33e, "SimSun", "Arial", 0, 936},
+    {0x01e4f102, "YouYuan", "Arial", 1, 936},
+    {0x030549dc, "LiSu", "Arial", 1, 936},
+    {0x032edd44, "Simhei", "Arial", 1, 936},
+    {0x03eac6fc, "PoorRichard-Regular", "Arial", 2, 1252},
+    {0x03ed90e6, "Nina", "Arial", 0, 1252},
+    {0x077b56b3, "KingsoftPhoneticPlain", "Arial", 0, 1252},
+    {0x078ed524, "MicrosoftSansSerif", "Arial", 0, 1252},
+    {0x089b18a9, "Arial", "Arial", 0, 1252},
+    {0x0b2cad72, "MonotypeCorsiva", "Arial", 8, 1252},
+    {0x0bb003e7, "Kartika", "Arial", 2, 1252},
+    {0x0bb469df, "VinerHandITC", "Arial", 8, 1252},
+    {0x0bc1a851, "SegoeUI", "Arial", 0, 1252},
+    {0x0c112ebd, "KozukaGothicPro-VIM", "Arial", 0, 1252},
+    {0x0cfcb9c1, "AdobeThai", "Kokila,Arial Narrow", 0, 847},
+    {0x0e7de0f9, "Playbill", "Arial", 0, 1252},
+    {0x0eff47c3, "STHupo", "Arial", 0, 936},
+    {0x107ad374, "Constantia", "Arial", 2, 1252},
+    {0x12194c2d, "KunstlerScript", "Arial", 8, 1252},
+    {0x135ef6a1, "MinionProSmBd",
+     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
+    {0x158c4049, "Garamond", "Arial", 2, 1252},
+    {0x160ecb24, "STZhongsong", "Arial", 0, 936},
+    {0x161ed07e, "MSGothic", "Arial", 1, 1252},
+    {0x171d1ed1, "SnapITC-Regular", "Arial", 0, 1252},
+    {0x18d1188f, "Cambria", "Arial", 2, 1252},
+    {0x18eaf350, "ArialUnicodeMS", "Arial", 0, 936},
+    {0x1a92d115, "MingLiU", "Arial", 1, 1252},
+    {0x1cc217c6, "TrebuchetMS", "Arial", 0, 1252},
+    {0x1d649596, "BasemicTimes", "Arial", 0, 1252},
+    {0x1e34ee60, "BellMT", "Arial", 2, 1252},
+    {0x1eb36945, "CooperBlack", "Arial", 2, 1252},
+    {0x1ef7787d, "BatangChe", "Arial", 1, 1252},
+    {0x20b3bd3a, "BrushScriptMT", "Arial", 8, 1252},
+    {0x220877aa, "Candara", "Arial", 0, 1252},
+    {0x22135007, "FreestyleScript-Regular", "Arial", 8, 1252},
+    {0x251059c3, "Chiller", "Arial", 0, 1252},
+    {0x25bed6dd, "MSReferenceSansSerif", "Arial", 0, 1252},
+    {0x28154c81, "Parchment-Regular", "Arial", 8, 1252},
+    {0x29711eb9, "STLiti", "Arial", 0, 936},
+    {0x2b1993b4, "Basemic", "Arial", 0, 1252},
+    {0x2b316339, "NiagaraSolid-Reg", "Arial", 0, 1252},
+    {0x2c147529, "FootlightMTLight", "Arial", 0, 1252},
+    {0x2c198928, "HarlowSolid", "Arial", 0, 1252},
+    {0x2c6ac6b2, "LucidaBright", "Arial", 2, 1252},
+    {0x2c9f38e2, "KozukaMinchoPro-VIR", "Arial", 0, 1252},
+    {0x2d5a47b0, "STCaiyun", "Arial", 0, 936},
+    {0x2def26bf, "BernardMT-Condensed", "Arial", 0, 1252},
+    {0x2fd8930b, "KozukaMinchoPr6NR", "Arial", 0, 1252},
+    {0x3115525a, "FangSong_GB2312", "Arial", 0, 1252},
+    {0x31327817, "MyriadPro",
+     "Calibri,Corbel,Candara,Cambria Math,Franklin Gothic Medium,Arial "
+     "Narrow,Times New Roman",
      0, 1252},
-    {0x32244975, L"Helvetica", L"Arial", 0, 1252},
-    {0x32ac995c, L"Terminal", L"Arial", 0, 1252},
-    {0x338d648a, L"NiagaraEngraved-Reg", L"Arial", 0, 1252},
-    {0x33bb65f2, L"Sylfaen", L"Arial", 2, 1252},
-    {0x3402c30e, L"MSPMincho", L"Arial", 2, 1252},
-    {0x3412bf31, L"SimSun-PUA", L"Arial", 0, 936},
-    {0x36eb39b9, L"BerlinSansFB", L"Arial", 0, 1252},
-    {0x36f42055, L"UniversATT", L"Microsoft Sans Serif", 0, 1252},
-    {0x3864c4f6, L"HighTowerText", L"Arial", 2, 1252},
-    {0x3a257d03, L"FangSong_GB2312", L"Arial", 0, 1252},
-    {0x3cdae668, L"FreestyleScript", L"Arial", 8, 1252},
-    {0x3d55aed7, L"Jokerman", L"Arial", 0, 1252},
-    {0x3d5b4385, L"PMingLiU", L"Arial", 2, 1252},
-    {0x3d9b7669, L"EstrangeloEdessa", L"Arial", 0, 1252},
-    {0x3e532d74, L"FranklinGothicMedium", L"Arial", 0, 1252},
-    {0x3e6aa32d, L"NSimSun", L"Arial", 1, 936},
-    {0x3f6c36a8, L"Gautami", L"Arial", 0, 1252},
-    {0x3ff32662, L"Chiller-Regular", L"Arial", 0, 1252},
-    {0x409de312, L"ModernNo.20", L"Arial", 2, 1252},
-    {0x41443c5e, L"Georgia", L"Arial", 2, 1252},
-    {0x4160ade5, L"BellGothicStdBlack",
-     L"Arial,Arial Unicode MS,Book Antiqua,Dotum,Georgia", 0, 1252},
-    {0x421976c4, L"Modern-Regular", L"Arial", 2, 1252},
-    {0x422a7252, L"Stencil", L"Arial", 0, 1252},
-    {0x42c8554f, L"Fixedsys", L"Arial", 0, 1252},
-    {0x435cb41d, L"Roman", L"Arial", 0, 1252},
-    {0x47882383, L"CourierNew", L"Arial", 1, 1252},
-    {0x480a2338, L"BerlinSansFBDemi", L"Arial", 0, 1252},
-    {0x480bf7a4, L"CourierStd", L"Courier New,Verdana", 0, 1252},
-    {0x481ad6ed, L"VladimirScript", L"Arial", 8, 1252},
-    {0x4911577a, L"YouYuan", L"Arial", 1, 936},
-    {0x4a788d72, L"STXingkai", L"Arial", 0, 936},
-    {0x4bf88566, L"SegoeCondensed", L"Arial", 0, 1252},
-    {0x4ccf51a4, L"BerlinSansFB-Reg", L"Arial", 0, 1252},
-    {0x4ea967ce, L"GulimChe", L"Arial", 1, 1252},
-    {0x4f68bd79, L"LetterGothicStd", L"Courier New,Verdana", 0, 1252},
-    {0x51a0d0e6, L"KozukaGothicPr6NM", L"Arial", 0, 1252},
-    {0x531b3dea, L"BasemicSymbol", L"Arial", 0, 1252},
-    {0x5333fd39, L"CalifornianFB-Reg", L"Arial", 2, 1252},
-    {0x53561a54, L"FZYTK--GBK1-0", L"Arial", 0, 936},
-    {0x55e0dde6, L"LucidaSansTypewriter", L"Arial", 0, 1252},
-    {0x574d4d3d, L"AdobeArabic", L"Arial Narrow", 0, 1252},
-    {0x5792e759, L"STKaiti", L"Arial", 0, 936},
-    {0x5921978e, L"LucidaSansUnicode", L"Arial", 0, 1252},
-    {0x594e2da4, L"Vrinda", L"Arial", 0, 1252},
-    {0x59baa9a2, L"KaiTi_GB2312", L"Arial", 0, 1252},
-    {0x5cfedf4f, L"BaskOldFace", L"Arial", 0, 1252},
-    {0x5f97921c, L"AdobeMyungjoStdM",
-     L"Batang,Bookman Old Style,Consolas,STZhongsong", 0, 936},
-    {0x5fefbfad, L"Batang", L"Arial", 2, 1252},
-    {0x605342b9, L"DotumChe", L"Arial", 1, 1252},
-    {0x608c5f9a, L"KaiTi_GB2312", L"Arial", 0, 936},
-    {0x61efd0d1, L"MaturaMTScriptCapitals", L"Arial", 0, 1252},
-    {0x626608a9, L"MVBoli", L"Arial", 0, 1252},
-    {0x630501a3, L"SmallFonts", L"Arial", 0, 1252},
-    {0x65d0e2a9, L"FZYTK--GBK1-0", L"Arial", 0, 936},
-    {0x669f29e1, L"FZSTK--GBK1-0", L"Arial", 0, 936},
-    {0x673a9e5f, L"Tunga", L"Arial", 0, 1252},
-    {0x691aa4ce, L"NiagaraSolid", L"Arial", 0, 1252},
-    {0x696259b7, L"Corbel", L"Arial", 0, 1252},
-    {0x696ee9be, L"STXihei", L"Arial", 0, 936},
-    {0x6c59cf69, L"Dotum", L"Arial", 0, 1252},
-    {0x707fa561, L"Gungsuh", L"Arial", 2, 1252},
-    {0x71416bb2, L"ZWAdobeF", L"Arial", 0, 1252},
-    {0x71b41801, L"Verdana", L"Arial", 0, 1252},
-    {0x73f25e4c, L"PalatinoLinotype", L"Arial", 0, 1252},
-    {0x73f4d19f, L"NiagaraEngraved", L"Arial", 0, 1252},
-    {0x74001694, L"MyriadProBlack", L"Book Antiqua,Constantia,Dotum,Georgia", 0,
+    {0x32244975, "Helvetica", "Arial", 0, 1252},
+    {0x32ac995c, "Terminal", "Arial", 0, 1252},
+    {0x338d648a, "NiagaraEngraved-Reg", "Arial", 0, 1252},
+    {0x33bb65f2, "Sylfaen", "Arial", 2, 1252},
+    {0x3402c30e, "MSPMincho", "Arial", 2, 1252},
+    {0x3412bf31, "SimSun-PUA", "Arial", 0, 936},
+    {0x36eb39b9, "BerlinSansFB", "Arial", 0, 1252},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
+    {0x3864c4f6, "HighTowerText", "Arial", 2, 1252},
+    {0x3a257d03, "FangSong_GB2312", "Arial", 0, 1252},
+    {0x3cdae668, "FreestyleScript", "Arial", 8, 1252},
+    {0x3d55aed7, "Jokerman", "Arial", 0, 1252},
+    {0x3d5b4385, "PMingLiU", "Arial", 2, 1252},
+    {0x3d9b7669, "EstrangeloEdessa", "Arial", 0, 1252},
+    {0x3e532d74, "FranklinGothicMedium", "Arial", 0, 1252},
+    {0x3e6aa32d, "NSimSun", "Arial", 1, 936},
+    {0x3f6c36a8, "Gautami", "Arial", 0, 1252},
+    {0x3ff32662, "Chiller-Regular", "Arial", 0, 1252},
+    {0x409de312, "ModernNo.20", "Arial", 2, 1252},
+    {0x41443c5e, "Georgia", "Arial", 2, 1252},
+    {0x4160ade5, "BellGothicStdBlack",
+     "Arial,Arial Unicode MS,Book Antiqua,Dotum,Georgia", 0, 1252},
+    {0x421976c4, "Modern-Regular", "Arial", 2, 1252},
+    {0x422a7252, "Stencil", "Arial", 0, 1252},
+    {0x42c8554f, "Fixedsys", "Arial", 0, 1252},
+    {0x435cb41d, "Roman", "Arial", 0, 1252},
+    {0x47882383, "CourierNew", "Arial", 1, 1252},
+    {0x480a2338, "BerlinSansFBDemi", "Arial", 0, 1252},
+    {0x480bf7a4, "CourierStd", "Courier New,Verdana", 0, 1252},
+    {0x481ad6ed, "VladimirScript", "Arial", 8, 1252},
+    {0x4911577a, "YouYuan", "Arial", 1, 936},
+    {0x4a788d72, "STXingkai", "Arial", 0, 936},
+    {0x4bf88566, "SegoeCondensed", "Arial", 0, 1252},
+    {0x4ccf51a4, "BerlinSansFB-Reg", "Arial", 0, 1252},
+    {0x4ea967ce, "GulimChe", "Arial", 1, 1252},
+    {0x4f68bd79, "LetterGothicStd", "Courier New,Verdana", 0, 1252},
+    {0x51a0d0e6, "KozukaGothicPr6NM", "Arial", 0, 1252},
+    {0x531b3dea, "BasemicSymbol", "Arial", 0, 1252},
+    {0x5333fd39, "CalifornianFB-Reg", "Arial", 2, 1252},
+    {0x53561a54, "FZYTK--GBK1-0", "Arial", 0, 936},
+    {0x55e0dde6, "LucidaSansTypewriter", "Arial", 0, 1252},
+    {0x574d4d3d, "AdobeArabic", "Arial Narrow", 0, 1252},
+    {0x5792e759, "STKaiti", "Arial", 0, 936},
+    {0x5921978e, "LucidaSansUnicode", "Arial", 0, 1252},
+    {0x594e2da4, "Vrinda", "Arial", 0, 1252},
+    {0x59baa9a2, "KaiTi_GB2312", "Arial", 0, 1252},
+    {0x5cfedf4f, "BaskOldFace", "Arial", 0, 1252},
+    {0x5f97921c, "AdobeMyungjoStdM",
+     "Batang,Bookman Old Style,Consolas,STZhongsong", 0, 936},
+    {0x5fefbfad, "Batang", "Arial", 2, 1252},
+    {0x605342b9, "DotumChe", "Arial", 1, 1252},
+    {0x608c5f9a, "KaiTi_GB2312", "Arial", 0, 936},
+    {0x61efd0d1, "MaturaMTScriptCapitals", "Arial", 0, 1252},
+    {0x626608a9, "MVBoli", "Arial", 0, 1252},
+    {0x630501a3, "SmallFonts", "Arial", 0, 1252},
+    {0x65d0e2a9, "FZYTK--GBK1-0", "Arial", 0, 936},
+    {0x669f29e1, "FZSTK--GBK1-0", "Arial", 0, 936},
+    {0x673a9e5f, "Tunga", "Arial", 0, 1252},
+    {0x691aa4ce, "NiagaraSolid", "Arial", 0, 1252},
+    {0x696259b7, "Corbel", "Arial", 0, 1252},
+    {0x696ee9be, "STXihei", "Arial", 0, 936},
+    {0x6c59cf69, "Dotum", "Arial", 0, 1252},
+    {0x707fa561, "Gungsuh", "Arial", 2, 1252},
+    {0x71416bb2, "ZWAdobeF", "Arial", 0, 1252},
+    {0x71b41801, "Verdana", "Arial", 0, 1252},
+    {0x73f25e4c, "PalatinoLinotype", "Arial", 0, 1252},
+    {0x73f4d19f, "NiagaraEngraved", "Arial", 0, 1252},
+    {0x74001694, "MyriadProBlack", "Book Antiqua,Constantia,Dotum,Georgia", 0,
      1252},
-    {0x74b14d8f, L"Haettenschweiler", L"Arial", 0, 1252},
-    {0x74cb44ee, L"NSimSun", L"Arial", 1, 936},
-    {0x76b4d7ff, L"Shruti", L"Arial", 0, 1252},
-    {0x788b3533, L"Webdings", L"Arial", 6, 42},
-    {0x797dde99, L"MSSerif", L"Arial", 0, 1252},
-    {0x7a0f9e9e, L"MSMincho", L"Arial", 1, 1252},
-    {0x7b439caf, L"OldEnglishTextMT", L"Arial", 0, 1252},
-    {0x8213a433, L"LucidaSans-Typewriter", L"Arial", 0, 1252},
-    {0x82fec929, L"AdobeSongStdL",
-     L"Centaur,Calibri,STSong,Bell MT,Garamond,Times New Roman", 0, 936},
-    {0x83581825, L"Modern", L"Arial", 0, 1252},
-    {0x835a2823, L"Algerian", L"Arial", 0, 1252},
-    {0x83dab9f5, L"Script", L"Arial", 0, 1252},
-    {0x847b56da, L"Tahoma", L"Arial", 0, 1252},
-    {0x8a783cb2, L"SimSun-PUA", L"Arial", 0, 1252},
-    {0x8b5cac0e, L"Onyx", L"Arial", 0, 1252},
-    {0x8c6a499e, L"Gulim", L"Arial", 0, 1252},
-    {0x8e0af790, L"JuiceITC", L"Arial", 0, 1252},
-    {0x8e8d43b2, L"Centaur", L"Arial", 2, 1252},
-    {0x8ee4dcca, L"BookshelfSymbol7", L"Arial", 0, 1252},
-    {0x90794800, L"BellGothicStdLight", L"Bell MT,Calibri,Times New Roman", 0,
+    {0x74b14d8f, "Haettenschweiler", "Arial", 0, 1252},
+    {0x74cb44ee, "NSimSun", "Arial", 1, 936},
+    {0x76b4d7ff, "Shruti", "Arial", 0, 1252},
+    {0x788b3533, "Webdings", "Arial", 6, 42},
+    {0x797dde99, "MSSerif", "Arial", 0, 1252},
+    {0x7a0f9e9e, "MSMincho", "Arial", 1, 1252},
+    {0x7b439caf, "OldEnglishTextMT", "Arial", 0, 1252},
+    {0x8213a433, "LucidaSans-Typewriter", "Arial", 0, 1252},
+    {0x82fec929, "AdobeSongStd",
+     "Centaur,Calibri,STSong,Bell MT,Garamond,Times New Roman", 0, 936},
+    {0x83581825, "Modern", "Arial", 0, 1252},
+    {0x835a2823, "Algerian", "Arial", 0, 1252},
+    {0x83dab9f5, "Script", "Arial", 0, 1252},
+    {0x847b56da, "Tahoma", "Arial", 0, 1252},
+    {0x8a783cb2, "SimSun-PUA", "Arial", 0, 1252},
+    {0x8b5cac0e, "Onyx", "Arial", 0, 1252},
+    {0x8c6a499e, "Gulim", "Arial", 0, 1252},
+    {0x8e0af790, "JuiceITC", "Arial", 0, 1252},
+    {0x8e8d43b2, "Centaur", "Arial", 2, 1252},
+    {0x8ee4dcca, "BookshelfSymbol7", "Arial", 0, 1252},
+    {0x90794800, "BellGothicStdLight", "Bell MT,Calibri,Times New Roman", 0,
      1252},
-    {0x909b516a, L"Century", L"Arial", 2, 1252},
-    {0x92ae370d, L"MSOutlook", L"Arial", 4, 42},
-    {0x93c9fbf1, L"LucidaFax", L"Arial", 2, 1252},
-    {0x9565085e, L"BookAntiqua", L"Arial", 2, 1252},
-    {0x9856d95d, L"AdobeMingStdL", L"Arial,Arial Unicode MS,Cambria,BatangChe",
-     0, 949},
-    {0x9bbadd6b, L"ColonnaMT", L"Arial", 0, 1252},
-    {0x9cbd16a4, L"ShowcardGothic-Reg", L"Arial", 0, 1252},
-    {0x9d73008e, L"MSSansSerif", L"Arial", 0, 1252},
-    {0xa0607db1, L"GungsuhChe", L"Arial", 1, 1252},
-    {0xa0bcf6a1, L"LatinWide", L"Arial", 2, 1252},
-    {0xa1429b36, L"Symbol", L"Arial", 6, 42},
-    {0xa1fa5abc, L"Wingdings2", L"Arial", 6, 42},
-    {0xa1fa5abd, L"Wingdings3", L"Arial", 6, 42},
-    {0xa427bad4, L"InformalRoman-Regular", L"Arial", 8, 1252},
-    {0xa8b92ece, L"FZSTK--GBK1-0", L"Arial", 0, 936},
-    {0xa8d83ece, L"CalifornianFB", L"Arial", 2, 1252},
-    {0xaa3e082c, L"Kingsoft-Phonetic", L"Arial", 0, 1252},
-    {0xaa6bcabe, L"HarlowSolidItalic", L"Arial", 0, 1252},
-    {0xade5337c, L"MSUIGothic", L"Arial", 0, 1252},
-    {0xb08dd941, L"WideLatin", L"Arial", 2, 1252},
-    {0xb207f05d, L"PoorRichard", L"Arial", 2, 1252},
-    {0xb3bc492f, L"JuiceITC-Regular", L"Arial", 0, 1252},
-    {0xb5545399, L"Marlett", L"Arial", 4, 42},
-    {0xb5dd1ebb, L"BritannicBold", L"Arial", 0, 1252},
-    {0xb699c1c5, L"LucidaCalligraphy-Italic", L"Arial", 0, 1252},
-    {0xb725d629, L"TimesNewRoman", L"Arial", 2, 1252},
-    {0xb7eaebeb, L"AdobeHeitiStdR", L"Batang,Century,Dotum", 0, 936},
-    {0xbd29c486, L"BerlinSansFBDemi-Bold", L"Arial", 0, 1252},
-    {0xbe8a8db4, L"BookshelfSymbolSeven", L"Arial", 0, 1252},
-    {0xc16c0118, L"AdobeHebrew", L"Bell MT,Berlin Sans FB,Calibri", 0, 1252},
-    {0xc318b0af, L"MyriadProLight", L"Calibri,STFangsong,Times New Roman", 0,
+    {0x909b516a, "Century", "Arial", 2, 1252},
+    {0x92ae370d, "MSOutlook", "Arial", 4, 42},
+    {0x93c9fbf1, "LucidaFax", "Arial", 2, 1252},
+    {0x9565085e, "BookAntiqua", "Arial", 2, 1252},
+    {0x9856d95d, "AdobeMingStd", "Arial,Arial Unicode MS,Cambria,BatangChe", 0,
+     949},
+    {0x9bbadd6b, "ColonnaMT", "Arial", 0, 1252},
+    {0x9cbd16a4, "ShowcardGothic-Reg", "Arial", 0, 1252},
+    {0x9d73008e, "MSSansSerif", "Arial", 0, 1252},
+    {0xa0607db1, "GungsuhChe", "Arial", 1, 1252},
+    {0xa0bcf6a1, "LatinWide", "Arial", 2, 1252},
+    {0xa1429b36, "Symbol", "Arial", 6, 42},
+    {0xa1fa5abc, "Wingdings2", "Arial", 6, 42},
+    {0xa1fa5abd, "Wingdings3", "Arial", 6, 42},
+    {0xa427bad4, "InformalRoman-Regular", "Arial", 8, 1252},
+    {0xa8b92ece, "FZSTK--GBK1-0", "Arial", 0, 936},
+    {0xa8d83ece, "CalifornianFB", "Arial", 2, 1252},
+    {0xaa3e082c, "Kingsoft-Phonetic", "Arial", 0, 1252},
+    {0xaa6bcabe, "HarlowSolidItalic", "Arial", 0, 1252},
+    {0xade5337c, "MSUIGothic", "Arial", 0, 1252},
+    {0xb08dd941, "WideLatin", "Arial", 2, 1252},
+    {0xb207f05d, "PoorRichard", "Arial", 2, 1252},
+    {0xb3bc492f, "JuiceITC-Regular", "Arial", 0, 1252},
+    {0xb5545399, "Marlett", "Arial", 4, 42},
+    {0xb5dd1ebb, "BritannicBold", "Arial", 0, 1252},
+    {0xb699c1c5, "LucidaCalligraphy-Italic", "Arial", 0, 1252},
+    {0xb725d629, "TimesNewRoman", "Arial", 2, 1252},
+    {0xb7eaebeb, "AdobeHeitiStdR", "Batang,Century,Dotum", 0, 936},
+    {0xbd29c486, "BerlinSansFBDemi-Bold", "Arial", 0, 1252},
+    {0xbe8a8db4, "BookshelfSymbolSeven", "Arial", 0, 1252},
+    {0xc16c0118, "AdobeHebrew", "Bell MT,Berlin Sans FB,Calibri", 0, 1252},
+    {0xc318b0af, "MyriadProLight", "Calibri,STFangsong,Times New Roman", 0,
      1252},
-    {0xc65e5659, L"CambriaMath", L"Arial", 2, 1252},
-    {0xc75c8f05, L"LucidaConsole", L"Arial", 1, 1252},
-    {0xca7c35d6, L"Calibri", L"Arial", 0, 1252},
-    {0xcb053f53, L"MicrosoftYaHei", L"Arial", 0, 936},
-    {0xcb7190f9, L"Magneto-Bold", L"Arial", 0, 1252},
-    {0xcca00cc5, L"System", L"Arial", 0, 1252},
-    {0xccad6f76, L"Jokerman-Regular", L"Arial", 0, 1252},
-    {0xccc5818c, L"EuroSign", L"Arial", 0, 1252},
-    {0xcf3d7234, L"LucidaHandwriting-Italic", L"Arial", 0, 1252},
-    {0xcf7b8fdb, L"MinionPro",
-     L"Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
-    {0xcfe5755f, L"Simhei", L"Arial", 1, 936},
-    {0xd011f4ee, L"MSPGothic", L"Arial", 0, 1252},
-    {0xd060e7ef, L"Vivaldi", L"Arial", 8, 1252},
-    {0xd07edec1, L"FranklinGothic-Medium", L"Arial", 0, 1252},
-    {0xd107243f, L"SimSun", L"Arial", 0, 936},
-    {0xd1881562, L"ArialNarrow", L"Arial Narrow", 0, 1252},
-    {0xd22b7dce, L"BodoniMTPosterCompressed", L"Arial", 0, 1252},
-    {0xd22bfa60, L"ComicSansMS", L"Arial", 8, 1252},
-    {0xd3bd0e35, L"Bauhaus93", L"Arial", 0, 1252},
-    {0xd429ee7a, L"STFangsong", L"Arial", 0, 936},
-    {0xd6679c12, L"BernardMTCondensed", L"Arial", 0, 1252},
-    {0xd8e8a027, L"LucidaSans", L"Arial", 0, 1252},
-    {0xd9fe7761, L"HighTowerText-Reg", L"Arial", 2, 1252},
-    {0xda7e551e, L"STSong", L"Arial", 0, 936},
-    {0xdaa6842d, L"STZhongsong", L"Arial", 0, 936},
-    {0xdaaab93f, L"STFangsong", L"Arial", 0, 936},
-    {0xdaeb0713, L"STSong", L"Arial", 0, 936},
-    {0xdafedbef, L"STCaiyun", L"Arial", 0, 936},
-    {0xdb00a3d9, L"Broadway", L"Arial", 0, 1252},
-    {0xdb1f5ad4, L"STXinwei", L"Arial", 0, 936},
-    {0xdb326e7f, L"STKaiti", L"Arial", 0, 936},
-    {0xdb69595a, L"STHupo", L"Arial", 0, 936},
-    {0xdba0082c, L"STXihei", L"Arial", 0, 936},
-    {0xdbd0ab18, L"STXingkai", L"Arial", 0, 936},
-    {0xdc1a7db1, L"STLiti", L"Arial", 0, 936},
-    {0xdc33075f, L"KristenITC-Regular", L"Arial", 8, 1252},
-    {0xdcc7009c, L"Harrington", L"Arial", 0, 1252},
-    {0xdd712466, L"ArialBlack", L"Arial", 0, 1252},
-    {0xdde87b3e, L"Impact", L"Arial", 0, 1252},
-    {0xdf69fb32, L"SnapITC", L"Arial", 0, 1252},
-    {0xdf8b25e8, L"CenturyGothic", L"Arial", 0, 1252},
-    {0xe0f705c0, L"KristenITC", L"Arial", 8, 1252},
-    {0xe1427573, L"Raavi", L"Arial", 0, 1252},
-    {0xe2cea0cb, L"Magneto", L"Arial", 0, 1252},
-    {0xe36a9e17, L"Ravie", L"Arial", 0, 1252},
-    {0xe433f8e2, L"Parchment", L"Arial", 8, 1252},
-    {0xe43dff4a, L"Wingdings", L"Arial", 4, 42},
-    {0xe4e2c405, L"MTExtra", L"Arial", 6, 42},
-    {0xe618cc35, L"InformalRoman", L"Arial", 8, 1252},
-    {0xe6c27ffc, L"Mistral", L"Arial", 8, 1252},
-    {0xe7ebf4b9, L"Courier", L"Courier New", 0, 1252},
-    {0xe8bc4a9d, L"MSReferenceSpecialty", L"Arial", 0, 1252},
-    {0xe90fb013, L"TempusSansITC", L"Arial", 0, 1252},
-    {0xec637b42, L"Consolas", L"Verdana", 1, 1252},
-    {0xed3a683b, L"STXinwei", L"Arial", 0, 936},
-    {0xef264cd1, L"LucidaHandwriting", L"Arial", 0, 1252},
-    {0xf086bca2, L"BaskervilleOldFace", L"Arial", 0, 1252},
-    {0xf1028030, L"Mangal", L"Arial", 2, 1252},
-    {0xf1da7eb9, L"ShowcardGothic", L"Arial", 0, 1252},
-    {0xf210f06a, L"ArialMT", L"Arial", 0, 1252},
-    {0xf477f16a, L"Latha", L"Arial", 0, 1252},
-    {0xf616f3dd, L"LiSu", L"Arial", 1, 936},
-    {0xfa479aa6, L"MicrosoftYaHei", L"Arial", 0, 936},
-    {0xfcd19697, L"BookmanOldStyle", L"Arial", 0, 1252},
-    {0xfe209a82, L"LucidaCalligraphy", L"Arial", 0, 1252},
-    {0xfef135f8, L"AdobeHeitiStd-Regular", L"Batang,Century,Dotum", 0, 936},
+    {0xc65e5659, "CambriaMath", "Arial", 2, 1252},
+    {0xc75c8f05, "LucidaConsole", "Arial", 1, 1252},
+    {0xca7c35d6, "Calibri", "Arial", 0, 1252},
+    {0xcb053f53, "MicrosoftYaHei", "Arial", 0, 936},
+    {0xcb7190f9, "Magneto-Bold", "Arial", 0, 1252},
+    {0xcca00cc5, "System", "Arial", 0, 1252},
+    {0xccad6f76, "Jokerman-Regular", "Arial", 0, 1252},
+    {0xccc5818c, "EuroSign", "Arial", 0, 1252},
+    {0xcf3d7234, "LucidaHandwriting-Italic", "Arial", 0, 1252},
+    {0xcf7b8fdb, "MinionPro",
+     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
+    {0xcfe5755f, "Simhei", "Arial", 1, 936},
+    {0xd011f4ee, "MSPGothic", "Arial", 0, 1252},
+    {0xd060e7ef, "Vivaldi", "Arial", 8, 1252},
+    {0xd07edec1, "FranklinGothic-Medium", "Arial", 0, 1252},
+    {0xd107243f, "SimSun", "Arial", 0, 936},
+    {0xd1881562, "ArialNarrow", "Arial Narrow", 0, 1252},
+    {0xd22b7dce, "BodoniMTPosterCompressed", "Arial", 0, 1252},
+    {0xd22bfa60, "ComicSansMS", "Arial", 8, 1252},
+    {0xd3bd0e35, "Bauhaus93", "Arial", 0, 1252},
+    {0xd429ee7a, "STFangsong", "Arial", 0, 936},
+    {0xd6679c12, "BernardMTCondensed", "Arial", 0, 1252},
+    {0xd8e8a027, "LucidaSans", "Arial", 0, 1252},
+    {0xd9fe7761, "HighTowerText-Reg", "Arial", 2, 1252},
+    {0xda7e551e, "STSong", "Arial", 0, 936},
+    {0xdaa6842d, "STZhongsong", "Arial", 0, 936},
+    {0xdaaab93f, "STFangsong", "Arial", 0, 936},
+    {0xdaeb0713, "STSong", "Arial", 0, 936},
+    {0xdafedbef, "STCaiyun", "Arial", 0, 936},
+    {0xdb00a3d9, "Broadway", "Arial", 0, 1252},
+    {0xdb1f5ad4, "STXinwei", "Arial", 0, 936},
+    {0xdb326e7f, "STKaiti", "Arial", 0, 936},
+    {0xdb69595a, "STHupo", "Arial", 0, 936},
+    {0xdba0082c, "STXihei", "Arial", 0, 936},
+    {0xdbd0ab18, "STXingkai", "Arial", 0, 936},
+    {0xdc1a7db1, "STLiti", "Arial", 0, 936},
+    {0xdc33075f, "KristenITC-Regular", "Arial", 8, 1252},
+    {0xdcc7009c, "Harrington", "Arial", 0, 1252},
+    {0xdd712466, "ArialBlack", "Arial", 0, 1252},
+    {0xdde87b3e, "Impact", "Arial", 0, 1252},
+    {0xdf69fb32, "SnapITC", "Arial", 0, 1252},
+    {0xdf8b25e8, "CenturyGothic", "Arial", 0, 1252},
+    {0xe0f705c0, "KristenITC", "Arial", 8, 1252},
+    {0xe1427573, "Raavi", "Arial", 0, 1252},
+    {0xe2cea0cb, "Magneto", "Arial", 0, 1252},
+    {0xe36a9e17, "Ravie", "Arial", 0, 1252},
+    {0xe433f8e2, "Parchment", "Arial", 8, 1252},
+    {0xe43dff4a, "Wingdings", "Arial", 4, 42},
+    {0xe4e2c405, "MTExtra", "Arial", 6, 42},
+    {0xe618cc35, "InformalRoman", "Arial", 8, 1252},
+    {0xe6c27ffc, "Mistral", "Arial", 8, 1252},
+    {0xe7ebf4b9, "Courier", "Courier New", 0, 1252},
+    {0xe8bc4a9d, "MSReferenceSpecialty", "Arial", 0, 1252},
+    {0xe90fb013, "TempusSansITC", "Arial", 0, 1252},
+    {0xec637b42, "Consolas", "Verdana", 1, 1252},
+    {0xed3a683b, "STXinwei", "Arial", 0, 936},
+    {0xef264cd1, "LucidaHandwriting", "Arial", 0, 1252},
+    {0xf086bca2, "BaskervilleOldFace", "Arial", 0, 1252},
+    {0xf1028030, "Mangal", "Arial", 2, 1252},
+    {0xf1da7eb9, "ShowcardGothic", "Arial", 0, 1252},
+    {0xf210f06a, "ArialMT", "Arial", 0, 1252},
+    {0xf477f16a, "Latha", "Arial", 0, 1252},
+    {0xf616f3dd, "LiSu", "Arial", 1, 936},
+    {0xfa479aa6, "MicrosoftYaHei", "Arial", 0, 936},
+    {0xfcd19697, "BookmanOldStyle", "Arial", 0, 1252},
+    {0xfe209a82, "LucidaCalligraphy", "Arial", 0, 1252},
+    {0xfef135f8, "AdobeHeitiStd-Regular", "Batang,Century,Dotum", 0, 936},
 };
 #elif _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
 const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, L"SimSun",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     L"PL UMing TW MBE",
+    {0x01d5d33e, "SimSun",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE",
      0, 936},
-    {0x01e4f102, L"YouYuan",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     L"PL UMing TW MBE",
+    {0x01e4f102, "YouYuan",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE",
      1, 936},
-    {0x030549dc, L"LiSu",
-     L"WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
-     L"Mono,WenQuanYi Micro Hei",
+    {0x030549dc, "LiSu",
+     "WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
+     "Mono,WenQuanYi Micro Hei",
      1, 936},
-    {0x032edd44, L"Simhei",
-     L"WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
-     L"Mono,WenQuanYi Micro Hei",
+    {0x032edd44, "Simhei",
+     "WenQuanYi Zen Hei,WenQuanYi Zen Hei Sharp,WenQuanYi Zen Hei "
+     "Mono,WenQuanYi Micro Hei",
      1, 936},
-    {0x03eac6fc, L"PoorRichard-Regular", L"Droid Sans Japanese,FreeSerif", 2,
+    {0x03eac6fc, "PoorRichard-Regular", "Droid Sans Japanese,FreeSerif", 2,
      1252},
-    {0x03ed90e6, L"Nina", L"FreeSerif", 0, 1252},
-    {0x077b56b3, L"KingsoftPhoneticPlain",
-     L"Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans Thai,Droid Sans "
-     L"Armenian,Untitled1,utkal,Lohit Oriya",
+    {0x03ed90e6, "Nina", "FreeSerif", 0, 1252},
+    {0x077b56b3, "KingsoftPhoneticPlain",
+     "Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans Thai,Droid Sans "
+     "Armenian,Untitled1,utkal,Lohit Oriya",
      0, 1252},
-    {0x078ed524, L"MicrosoftSansSerif",
-     L"Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei", 0, 1252},
-    {0x089b18a9, L"Arial",
-     L"Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif,WenQuanYi Micro Hei",
+    {0x078ed524, "MicrosoftSansSerif",
+     "Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei", 0, 1252},
+    {0x089b18a9, "Arial",
+     "Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif,WenQuanYi Micro Hei",
      0, 1252},
-    {0x0b2cad72, L"MonotypeCorsiva", L"Droid Sans Japanese,FreeSerif", 8, 1252},
-    {0x0bb003e7, L"Kartika",
-     L"FreeSans,Liberation Sans,Liberation Sans Narrow,Nimbus Sans "
-     L"L,Garuda,FreeSerif,WenQuanYi Micro Hei",
+    {0x0b2cad72, "MonotypeCorsiva", "Droid Sans Japanese,FreeSerif", 8, 1252},
+    {0x0bb003e7, "Kartika",
+     "FreeSans,Liberation Sans,Liberation Sans Narrow,Nimbus Sans "
+     "L,Garuda,FreeSerif,WenQuanYi Micro Hei",
      2, 1252},
-    {0x0bb469df, L"VinerHandITC",
-     L"Droid Sans Japanese,Ubuntu,Liberation Sans,Liberation Serif", 8, 1252},
-    {0x0bc1a851, L"SegoeUI", L"Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x0c112ebd, L"KozukaGothicPro-VIM", L"FreeSerif", 0, 1252},
-    {0x0cfcb9c1, L"AdobeThai", L"Droid Sans Japanese,Waree", 0, 847},
-    {0x0e7de0f9, L"Playbill",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     L"Sans Ethiopic,Droid Sans Japanese,FreeSerif",
+    {0x0bb469df, "VinerHandITC",
+     "Droid Sans Japanese,Ubuntu,Liberation Sans,Liberation Serif", 8, 1252},
+    {0x0bc1a851, "SegoeUI", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
+    {0x0c112ebd, "KozukaGothicPro-VIM", "FreeSerif", 0, 1252},
+    {0x0cfcb9c1, "AdobeThai", "Droid Sans Japanese,Waree", 0, 847},
+    {0x0e7de0f9, "Playbill",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Ethiopic,Droid Sans Japanese,FreeSerif",
      0, 1252},
-    {0x0eff47c3, L"STHupo", L"AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+    {0x0eff47c3, "STHupo", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
      936},
-    {0x107ad374, L"Constantia",
-     L"Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei,Ubuntu", 2, 1252},
-    {0x12194c2d, L"KunstlerScript", L"Droid Sans Japanese,Liberation Serif", 8,
+    {0x107ad374, "Constantia",
+     "Droid Sans Japanese,FreeSerif,WenQuanYi Micro Hei,Ubuntu", 2, 1252},
+    {0x12194c2d, "KunstlerScript", "Droid Sans Japanese,Liberation Serif", 8,
      1252},
-    {0x135ef6a1, L"MinionProSmBd", L"Liberation Serif", 0, 1252},
-    {0x158c4049, L"Garamond",
-     L"Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 2, 1252},
-    {0x160ecb24, L"STZhongsong",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x135ef6a1, "MinionProSmBd", "Liberation Serif", 0, 1252},
+    {0x158c4049, "Garamond",
+     "Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 2, 1252},
+    {0x160ecb24, "STZhongsong",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0x161ed07e, L"MSGothic",
-     L"WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,AR PL "
-     L"UMing CN,AR PL UMing HK,AR PL UMing TW",
+    {0x161ed07e, "MSGothic",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,AR PL "
+     "UMing CN,AR PL UMing HK,AR PL UMing TW",
      1, 1252},
-    {0x171d1ed1, L"SnapITC-Regular",
-     L"Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Sans", 0,
+    {0x171d1ed1, "SnapITC-Regular",
+     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Sans", 0,
      1252},
-    {0x18d1188f, L"Cambria", L"Droid Sans Japanese,FreeSerif,FreeMono", 2,
-     1252},
-    {0x18eaf350, L"ArialUnicodeMS",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x18d1188f, "Cambria", "Droid Sans Japanese,FreeSerif,FreeMono", 2, 1252},
+    {0x18eaf350, "ArialUnicodeMS",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0x1a92d115, L"MingLiU",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x1a92d115, "MingLiU",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      1, 1252},
-    {0x1cc217c6, L"TrebuchetMS",
-     L"Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0, 1252},
-    {0x1d649596, L"BasemicTimes",
-     L"Liberation Serif,Times New Roman,Droid Sans Japanese,FreeSerif,Ubuntu",
-     0, 1252},
-    {0x1e34ee60, L"BellMT",
-     L"KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 2, 1252},
-    {0x1eb36945, L"CooperBlack",
-     L"KacstQurn,Droid Sans Japanese,FreeMono,Liberation Mono, WenQuanYi Micro "
-     L"Hei Mono",
+    {0x1cc217c6, "TrebuchetMS",
+     "Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0, 1252},
+    {0x1d649596, "BasemicTimes",
+     "Liberation Serif,Times New Roman,Droid Sans Japanese,FreeSerif,Ubuntu", 0,
+     1252},
+    {0x1e34ee60, "BellMT",
+     "KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 2, 1252},
+    {0x1eb36945, "CooperBlack",
+     "KacstQurn,Droid Sans Japanese,FreeMono,Liberation Mono, WenQuanYi Micro "
+     "Hei Mono",
      2, 1252},
-    {0x1ef7787d, L"BatangChe",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing "
-     L"TW,WenQuanYi Zen Hei,WenQuanYi Micro Hei",
+    {0x1ef7787d, "BatangChe",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing "
+     "TW,WenQuanYi Zen Hei,WenQuanYi Micro Hei",
      1, 1252},
-    {0x20b3bd3a, L"BrushScriptMT",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
-     L"Japanese,URW Chancery L,Liberation Sans",
+    {0x20b3bd3a, "BrushScriptMT",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
+     "Japanese,URW Chancery L,Liberation Sans",
      8, 1252},
-    {0x220877aa, L"Candara", L"Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x22135007, L"FreestyleScript-Regular",
-     L"KacstQurn,Droid Sans Japanese,Liberation Sans", 8, 1252},
-    {0x251059c3, L"Chiller",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
-     L"Japanese,Liberation Sans",
+    {0x220877aa, "Candara", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
+    {0x22135007, "FreestyleScript-Regular",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans", 8, 1252},
+    {0x251059c3, "Chiller",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
+     "Japanese,Liberation Sans",
      0, 1252},
-    {0x25bed6dd, L"MSReferenceSansSerif",
-     L"DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,AR PL UKai "
-     L"HK",
+    {0x25bed6dd, "MSReferenceSansSerif",
+     "DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,AR PL UKai "
+     "HK",
      0, 1252},
-    {0x28154c81, L"Parchment-Regular", L"Droid Sans Japanese,Liberation Sans",
-     8, 1252},
-    {0x29711eb9, L"STLiti", L"AR PL UKai HK", 0, 936},
-    {0x2b1993b4, L"Basemic",
-     L"Liberation Serif,Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x2b316339, L"NiagaraSolid-Reg", L"Droid Sans Japanese,Liberation Sans", 0,
+    {0x28154c81, "Parchment-Regular", "Droid Sans Japanese,Liberation Sans", 8,
      1252},
-    {0x2c147529, L"FootlightMTLight",
-     L"KacstQurn,Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x2c198928, L"HarlowSolid",
-     L"KacstQurn,Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x2c6ac6b2, L"LucidaBright",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     L"Sans Japanese,Liberation Sans",
+    {0x29711eb9, "STLiti", "AR PL UKai HK", 0, 936},
+    {0x2b1993b4, "Basemic",
+     "Liberation Serif,Droid Sans Japanese,Liberation Sans", 0, 1252},
+    {0x2b316339, "NiagaraSolid-Reg", "Droid Sans Japanese,Liberation Sans", 0,
+     1252},
+    {0x2c147529, "FootlightMTLight",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans", 0, 1252},
+    {0x2c198928, "HarlowSolid", "KacstQurn,Droid Sans Japanese,Liberation Sans",
+     0, 1252},
+    {0x2c6ac6b2, "LucidaBright",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,Liberation Sans",
      2, 1252},
-    {0x2c9f38e2, L"KozukaMinchoPro-VIR", L"DejaVu Sans", 0, 1252},
-    {0x2d5a47b0, L"STCaiyun", L"AR PL UKai HK", 0, 936},
-    {0x2def26bf, L"BernardMT-Condensed",
-     L"KacstQurn,Droid Sans Japanese,DejaVu Serif", 0, 1252},
-    {0x2fd8930b, L"KozukaMinchoPr6NR", L"DejaVu Serif", 0, 1252},
-    {0x3115525a, L"FangSong_GB2312",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x2c9f38e2, "KozukaMinchoPro-VIR", "DejaVu Sans", 0, 1252},
+    {0x2d5a47b0, "STCaiyun", "AR PL UKai HK", 0, 936},
+    {0x2def26bf, "BernardMT-Condensed",
+     "KacstQurn,Droid Sans Japanese,DejaVu Serif", 0, 1252},
+    {0x2fd8930b, "KozukaMinchoPr6NR", "DejaVu Serif", 0, 1252},
+    {0x3115525a, "FangSong_GB2312",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 1252},
-    {0x31327817, L"MyriadPro",
-     L"Ubuntu Condensed,Droid Sans Japanese, FreeSerif", 0, 1252},
-    {0x32244975, L"Helvetica",
-     L"Ubuntu,DejaVu Sans Condensed,Liberation Sans,Liberation Sans "
-     L"Narrow,Nimbus Sans L",
+    {0x31327817, "MyriadPro", "Ubuntu Condensed,Droid Sans Japanese, FreeSerif",
      0, 1252},
-    {0x32ac995c, L"Terminal", L"DejaVu Serif", 0, 1252},
-    {0x338d648a, L"NiagaraEngraved-Reg", L"Droid Sans Japanese,DejaVu Serif", 0,
+    {0x32244975, "Helvetica",
+     "Ubuntu,DejaVu Sans Condensed,Liberation Sans,Liberation Sans "
+     "Narrow,Nimbus Sans ",
+     0, 1252},
+    {0x32ac995c, "Terminal", "DejaVu Serif", 0, 1252},
+    {0x338d648a, "NiagaraEngraved-Reg", "Droid Sans Japanese,DejaVu Serif", 0,
      1252},
-    {0x33bb65f2, L"Sylfaen", L"Droid Sans Japanese,DejaVu Sans", 2, 1252},
-    {0x3402c30e, L"MSPMincho",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 2,
+    {0x33bb65f2, "Sylfaen", "Droid Sans Japanese,DejaVu Sans", 2, 1252},
+    {0x3402c30e, "MSPMincho",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 2,
      1252},
-    {0x3412bf31, L"SimSun-PUA",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing CN,AR PL UMing HK", 0,
+    {0x3412bf31, "SimSun-PUA",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing CN,AR PL UMing HK", 0,
      936},
-    {0x36eb39b9, L"BerlinSansFB",
-     L"Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0, 1252},
-    {0x36f42055, L"UniversATT", L"Microsoft Sans Serif", 0, 1252},
-    {0x3864c4f6, L"HighTowerText", L"Droid Sans Japanese,DejaVu Serif", 2,
-     1252},
-    {0x3a257d03, L"FangSong_GB2312",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei", 0, 1252},
-    {0x3c7d1d07, L"Garamond3LTStd",
-     L"Droid Sans Japanese,Ubuntu Condensed,DejaVu Sans Condensed,Liberation "
-     L"Serif,Ubuntu,FreeSerif",
+    {0x36eb39b9, "BerlinSansFB",
+     "Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0, 1252},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
+    {0x3864c4f6, "HighTowerText", "Droid Sans Japanese,DejaVu Serif", 2, 1252},
+    {0x3a257d03, "FangSong_GB2312", "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei",
+     0, 1252},
+    {0x3c7d1d07, "Garamond3LTStd",
+     "Droid Sans Japanese,Ubuntu Condensed,DejaVu Sans Condensed,Liberation "
+     "Serif,Ubuntu,FreeSerif",
      2, 1252},
-    {0x3cdae668, L"FreestyleScript",
-     L"KacstQurn,Droid Sans Japanese,DejaVu Sans", 8, 1252},
-    {0x3d55aed7, L"Jokerman", L"Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x3d5b4385, L"PMingLiU",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x3cdae668, "FreestyleScript", "KacstQurn,Droid Sans Japanese,DejaVu Sans",
+     8, 1252},
+    {0x3d55aed7, "Jokerman", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
+    {0x3d5b4385, "PMingLiU",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      2, 1252},
-    {0x3d9b7669, L"EstrangeloEdessa", L"Droid Sans Japanese,DejaVu Sans", 0,
+    {0x3d9b7669, "EstrangeloEdessa", "Droid Sans Japanese,DejaVu Sans", 0,
      1252},
-    {0x3e532d74, L"FranklinGothicMedium", L"Droid Sans Japanese,Ubuntu", 0,
-     1252},
-    {0x3e6aa32d, L"NSimSun",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x3e532d74, "FranklinGothicMedium", "Droid Sans Japanese,Ubuntu", 0, 1252},
+    {0x3e6aa32d, "NSimSun",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      1, 936},
-    {0x3f6c36a8, L"Gautami",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic, mry_KacstQurn,Droid Sans "
-     L"Japanese,FreeSans",
+    {0x3f6c36a8, "Gautami",
+     "Droid Arabic Naskh,Droid Sans Ethiopic, mry_KacstQurn,Droid Sans "
+     "Japanese,FreeSans",
      0, 1252},
-    {0x3ff32662, L"Chiller-Regular",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,FreeSans", 0, 1252},
-    {0x409de312, L"ModernNo.20",
-     L"KacstQurn,Droid Sans Japanese,Nimbus Sans L,Nimbus Sans L,FreeSans", 2,
+    {0x3ff32662, "Chiller-Regular",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,FreeSans", 0, 1252},
+    {0x409de312, "ModernNo.20",
+     "KacstQurn,Droid Sans Japanese,Nimbus Sans L,Nimbus Sans L,FreeSans", 2,
      1252},
-    {0x41443c5e, L"Georgia", L"Droid Sans Japanese,FreeSans", 2, 1252},
-    {0x4160ade5, L"BellGothicStdBlack", L"FreeSans", 0, 1252},
-    {0x421976c4, L"Modern-Regular", L"FreeSans", 2, 1252},
-    {0x422a7252, L"Stencil", L"Droid Sans Japanese,FreeSans,Liberation Sans", 0,
+    {0x41443c5e, "Georgia", "Droid Sans Japanese,FreeSans", 2, 1252},
+    {0x4160ade5, "BellGothicStdBlack", "FreeSans", 0, 1252},
+    {0x421976c4, "Modern-Regular", "FreeSans", 2, 1252},
+    {0x422a7252, "Stencil", "Droid Sans Japanese,FreeSans,Liberation Sans", 0,
      1252},
-    {0x42c8554f, L"Fixedsys", L"FreeSerif", 0, 1252},
-    {0x435cb41d, L"Roman", L"FreeSerif", 0, 1252},
-    {0x47882383, L"CourierNew",
-     L"FreeMono,WenQuanYi Micro Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL "
-     L"UKai TW,AR PL UKai TW MBE,DejaVu Sans",
+    {0x42c8554f, "Fixedsys", "FreeSerif", 0, 1252},
+    {0x435cb41d, "Roman", "FreeSerif", 0, 1252},
+    {0x47882383, "CourierNew",
+     "FreeMono,WenQuanYi Micro Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL "
+     "UKai TW,AR PL UKai TW MBE,DejaVu Sans",
      1, 1252},
-    {0x480a2338, L"BerlinSansFBDemi", L"Droid Sans Japanese,Liberation Serif",
-     0, 1252},
-    {0x480bf7a4, L"CourierStd", L"DejaVu Sans", 0, 1252},
-    {0x481ad6ed, L"VladimirScript", L"Droid Sans Japanese,DejaVu Serif", 8,
+    {0x480a2338, "BerlinSansFBDemi", "Droid Sans Japanese,Liberation Serif", 0,
      1252},
-    {0x4911577a, L"YouYuan",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
+    {0x480bf7a4, "CourierStd", "DejaVu Sans", 0, 1252},
+    {0x481ad6ed, "VladimirScript", "Droid Sans Japanese,DejaVu Serif", 8, 1252},
+    {0x4911577a, "YouYuan",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
      936},
-    {0x4a788d72, L"STXingkai", L"AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+    {0x4a788d72, "STXingkai", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
      936},
-    {0x4bf88566, L"SegoeCondensed", L"FreeSerif", 0, 1252},
-    {0x4ccf51a4, L"BerlinSansFB-Reg", L"Droid Sans Japanese,Liberation Serif",
-     0, 1252},
-    {0x4ea967ce, L"GulimChe",
-     L"WenQuanYi Zen Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL UKai TW,AR PL "
-     L"UKai TW MBE",
-     1, 1252},
-    {0x4f68bd79, L"LetterGothicStd",
-     L"FreeMono,Liberation Mono,Andale Mono,WenQuanYi Micro Hei Mono", 0, 1252},
-    {0x51a0d0e6, L"KozukaGothicPr6NM", L"FreeSerif", 0, 1252},
-    {0x531b3dea, L"BasemicSymbol", L"FreeSerif", 0, 1252},
-    {0x5333fd39, L"CalifornianFB-Reg",
-     L"Droid Sans Japanese,URW Chancery L,FreeSerif", 2, 1252},
-    {0x53561a54, L"FZYTK--GBK1-0",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0x55e0dde6, L"LucidaSansTypewriter",
-     L"Ubuntu Mono,DejaVu Sans Mono,Nimbus Mono L,Liberation Mono,Courier 10 "
-     L"Pitch,FreeMono",
-     0, 1252},
-    {0x574d4d3d, L"AdobeArabic", L"Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x5792e759, L"STKaiti", L"WenQuanYi Micro Hei Mono", 0, 936},
-    {0x5921978e, L"LucidaSansUnicode", L"Droid Sans Japanese,DejaVu Sans", 0,
+    {0x4bf88566, "SegoeCondensed", "FreeSerif", 0, 1252},
+    {0x4ccf51a4, "BerlinSansFB-Reg", "Droid Sans Japanese,Liberation Serif", 0,
      1252},
-    {0x594e2da4, L"Vrinda",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Arabic "
-     L"Naskh,mry_KacstQurn,Droid Sans Japanese,FreeSans,FreeSerif",
-     0, 1252},
-    {0x59baa9a2, L"KaiTi_GB2312",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
-     0, 1252},
-    {0x5cfedf4f, L"BaskOldFace",
-     L"KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 0, 1252},
-    {0x5e16ac91, L"TrajanPro",
-     L"Nimbus Sans L,AR PL UMing HK,AR PL UKai HK,AR PL UMing TW,AR PL UMing "
-     L"TW MBE,DejaVu Sans,DejaVu Serif",
-     0, 1252},
-    {0x5f388196, L"ITCLegacySansStdMedium",
-     L"Liberation Serif,FreeSerif,FreeSans,Ubuntu", 0, 1252},
-    {0x5f97921c, L"AdobeMyungjoStdM",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x4ea967ce, "GulimChe",
+     "WenQuanYi Zen Hei Mono,AR PL UKai CN,AR PL UKai HK,AR PL UKai TW,AR PL "
+     "UKai TW MBE",
+     1, 1252},
+    {0x4f68bd79, "LetterGothicStd",
+     "FreeMono,Liberation Mono,Andale Mono,WenQuanYi Micro Hei Mono", 0, 1252},
+    {0x51a0d0e6, "KozukaGothicPr6NM", "FreeSerif", 0, 1252},
+    {0x531b3dea, "BasemicSymbol", "FreeSerif", 0, 1252},
+    {0x5333fd39, "CalifornianFB-Reg",
+     "Droid Sans Japanese,URW Chancery L,FreeSerif", 2, 1252},
+    {0x53561a54, "FZYTK--GBK1-0",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0x5fefbfad, L"Batang",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x55e0dde6, "LucidaSansTypewriter",
+     "Ubuntu Mono,DejaVu Sans Mono,Nimbus Mono L,Liberation Mono,Courier 10 "
+     "Pitch,FreeMono",
+     0, 1252},
+    {0x574d4d3d, "AdobeArabic", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
+    {0x5792e759, "STKaiti", "WenQuanYi Micro Hei Mono", 0, 936},
+    {0x5921978e, "LucidaSansUnicode", "Droid Sans Japanese,DejaVu Sans", 0,
+     1252},
+    {0x594e2da4, "Vrinda",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Arabic "
+     "Naskh,mry_KacstQurn,Droid Sans Japanese,FreeSans,FreeSerif",
+     0, 1252},
+    {0x59baa9a2, "KaiTi_GB2312",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, 1252},
+    {0x5cfedf4f, "BaskOldFace",
+     "KacstQurn,Droid Sans Japanese,Ubuntu,Liberation Serif", 0, 1252},
+    {0x5e16ac91, "TrajanPro",
+     "Nimbus Sans L,AR PL UMing HK,AR PL UKai HK,AR PL UMing TW,AR PL UMing "
+     "TW MBE,DejaVu Sans,DejaVu Serif",
+     0, 1252},
+    {0x5f388196, "ITCLegacySansStdMedium",
+     "Liberation Serif,FreeSerif,FreeSans,Ubuntu", 0, 1252},
+    {0x5f97921c, "AdobeMyungjoStdM",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, 936},
+    {0x5fefbfad, "Batang",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      2, 1252},
-    {0x605342b9, L"DotumChe",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
+    {0x605342b9, "DotumChe",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
      1252},
-    {0x608c5f9a, L"KaiTi_GB2312",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x608c5f9a, "KaiTi_GB2312",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0x61efd0d1, L"MaturaMTScriptCapitals",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     L"Sans Japanese,DejaVu Serif,DejaVu Sans",
+    {0x61efd0d1, "MaturaMTScriptCapitals",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,DejaVu Serif,DejaVu Sans",
      0, 1252},
-    {0x626608a9, L"MVBoli",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     L"Ethiopic,Droid Sans Japanese,DejaVu Sans",
+    {0x626608a9, "MVBoli",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Ethiopic,Droid Sans Japanese,DejaVu Sans",
      0, 1252},
-    {0x630501a3, L"SmallFonts", L"DejaVu Serif", 0, 1252},
-    {0x65d0e2a9, L"FZYTK--GBK1-0",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x630501a3, "SmallFonts", "DejaVu Serif", 0, 1252},
+    {0x65d0e2a9, "FZYTK--GBK1-0",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0x669f29e1, L"FZSTK--GBK1-0",
-     L"AR PL UMing CN,AR PL UKai CN, AR PL UMing HK", 0, 936},
-    {0x673a9e5f, L"Tunga",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     L"Japanese,DejaVu Serif",
+    {0x669f29e1, "FZSTK--GBK1-0",
+     "AR PL UMing CN,AR PL UKai CN, AR PL UMing HK", 0, 936},
+    {0x673a9e5f, "Tunga",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,DejaVu Serif",
      0, 1252},
-    {0x691aa4ce, L"NiagaraSolid", L"Droid Sans Japanese,DejaVu Serif", 0, 1252},
-    {0x696259b7, L"Corbel", L"Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0x696ee9be, L"STXihei", L"WenQuanYi Micro Hei Mono", 0, 936},
-    {0x6c59cf69, L"Dotum", L"WenQuanYi Zen Hei Mono", 0, 1252},
-    {0x707fa561, L"Gungsuh", L"WenQuanYi Zen Hei Mono", 2, 1252},
-    {0x71416bb2, L"ZWAdobeF",
-     L"KacstArt,KacstBookm,KacstDecorative,KacstDigital,KacstFarsi,KacstLetter,"
-     L"KacstOffice,Dingbats,FreeSerif",
+    {0x691aa4ce, "NiagaraSolid", "Droid Sans Japanese,DejaVu Serif", 0, 1252},
+    {0x696259b7, "Corbel", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
+    {0x696ee9be, "STXihei", "WenQuanYi Micro Hei Mono", 0, 936},
+    {0x6c59cf69, "Dotum", "WenQuanYi Zen Hei Mono", 0, 1252},
+    {0x707fa561, "Gungsuh", "WenQuanYi Zen Hei Mono", 2, 1252},
+    {0x71416bb2, "ZWAdobeF",
+     "KacstArt,KacstBookm,KacstDecorative,KacstDigital,KacstFarsi,KacstLetter,"
+     "KacstOffice,Dingbats,FreeSerif",
      0, 1252},
-    {0x71b41801, L"Verdana",
-     L"DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,DejaVu Sans",
+    {0x71b41801, "Verdana",
+     "DejaVu Sans Condensed,Ubuntu Condensed,Droid Sans Japanese,DejaVu Sans",
      0, 1252},
-    {0x73f25e4c, L"PalatinoLinotype", L"Droid Sans Japanese,FreeSerif", 0,
+    {0x73f25e4c, "PalatinoLinotype", "Droid Sans Japanese,FreeSerif", 0, 1252},
+    {0x73f4d19f, "NiagaraEngraved", "Droid Sans Japanese,FreeSerif", 0, 1252},
+    {0x74001694, "MyriadProBlack", "Droid Sans Japanese,AR PL UKai HK", 0,
      1252},
-    {0x73f4d19f, L"NiagaraEngraved", L"Droid Sans Japanese,FreeSerif", 0, 1252},
-    {0x74001694, L"MyriadProBlack", L"Droid Sans Japanese,AR PL UKai HK", 0,
+    {0x74b14d8f, "Haettenschweiler", "Droid Sans Japanese,DejaVu Serif", 0,
      1252},
-    {0x74b14d8f, L"Haettenschweiler", L"Droid Sans Japanese,DejaVu Serif", 0,
-     1252},
-    {0x74cb44ee, L"NSimSun", L"WenQuanYi Zen Hei Mono", 1, 936},
-    {0x76b4d7ff, L"Shruti",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     L"Japanese,FreeSans",
+    {0x74cb44ee, "NSimSun", "WenQuanYi Zen Hei Mono", 1, 936},
+    {0x76b4d7ff, "Shruti",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,FreeSans",
      0, 1252},
-    {0x788b3533, L"Webdings", L"FreeSans", 6, 42},
-    {0x797dde99, L"MSSerif", L"FreeSans", 0, 1252},
-    {0x7a0f9e9e, L"MSMincho",
-     L"WenQuanYi Micro Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW",
-     1, 1252},
-    {0x7b439caf, L"OldEnglishTextMT",
-     L"Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
-    {0x8213a433, L"LucidaSans-Typewriter",
-     L"Ubuntu Mono,Liberation Mono,DejaVu Sans Mono", 0, 1252},
-    {0x82fec929, L"AdobeSongStdL",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x788b3533, "Webdings", "FreeSans", 6, 42},
+    {0x797dde99, "MSSerif", "FreeSans", 0, 1252},
+    {0x7a0f9e9e, "MSMincho",
+     "WenQuanYi Micro Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 1,
+     1252},
+    {0x7b439caf, "OldEnglishTextMT",
+     "Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
+    {0x8213a433, "LucidaSans-Typewriter",
+     "Ubuntu Mono,Liberation Mono,DejaVu Sans Mono", 0, 1252},
+    {0x82fec929, "AdobeSongStd",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0x83581825, L"Modern", L"FreeSans", 0, 1252},
-    {0x835a2823, L"Algerian",
-     L"KacstQurn,Droid Sans Japanese,FreeSans,Liberation Sans,Ubuntu", 0, 1252},
-    {0x83dab9f5, L"Script", L"FreeSans", 0, 1252},
-    {0x847b56da, L"Tahoma",
-     L"Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif", 0, 1252},
-    {0x8a783cb2, L"SimSun-PUA",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x83581825, "Modern", "FreeSans", 0, 1252},
+    {0x835a2823, "Algerian",
+     "KacstQurn,Droid Sans Japanese,FreeSans,Liberation Sans,Ubuntu", 0, 1252},
+    {0x83dab9f5, "Script", "FreeSans", 0, 1252},
+    {0x847b56da, "Tahoma",
+     "Droid Sans Japanese,DejaVu Sans Condensed,FreeSerif", 0, 1252},
+    {0x8a783cb2, "SimSun-PUA",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 1252},
-    {0x8b5cac0e, L"Onyx", L"Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x8c6a499e, L"Gulim",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x8b5cac0e, "Onyx", "Droid Sans Japanese,Liberation Sans", 0, 1252},
+    {0x8c6a499e, "Gulim",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 1252},
-    {0x8e0af790, L"JuiceITC", L"Droid Sans Japanese,Liberation Sans", 0, 1252},
-    {0x8e8d43b2, L"Centaur",
-     L"KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 2, 1252},
-    {0x8ee4dcca, L"BookshelfSymbol7", L"Liberation Sans", 0, 1252},
-    {0x90794800, L"BellGothicStdLight", L"Liberation Sans", 0, 1252},
-    {0x909b516a, L"Century",
-     L"Droid Sans Japanese,Liberation Sans,Liberation Mono,Liberation Serif", 2,
+    {0x8e0af790, "JuiceITC", "Droid Sans Japanese,Liberation Sans", 0, 1252},
+    {0x8e8d43b2, "Centaur",
+     "KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 2, 1252},
+    {0x8ee4dcca, "BookshelfSymbol7", "Liberation Sans", 0, 1252},
+    {0x90794800, "BellGothicStdLight", "Liberation Sans", 0, 1252},
+    {0x909b516a, "Century",
+     "Droid Sans Japanese,Liberation Sans,Liberation Mono,Liberation Serif", 2,
      1252},
-    {0x92ae370d, L"MSOutlook", L"Liberation Sans", 4, 42},
-    {0x93c9fbf1, L"LucidaFax",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans "
-     L"Ethiopic,mry_KacstQurn,Liberation Sans",
+    {0x92ae370d, "MSOutlook", "Liberation Sans", 4, 42},
+    {0x93c9fbf1, "LucidaFax",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans "
+     "Ethiopic,mry_KacstQurn,Liberation Sans",
      2, 1252},
-    {0x9565085e, L"BookAntiqua",
-     L"Droid Sans Japanese,Liberation Sans,Liberation Serif", 2, 1252},
-    {0x9856d95d, L"AdobeMingStdL", L"AR PL UMing HK", 0, 949},
-    {0x9bbadd6b, L"ColonnaMT",
-     L"KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 0, 1252},
-    {0x9cbd16a4, L"ShowcardGothic-Reg",
-     L"Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
-    {0x9d73008e, L"MSSansSerif", L"FreeSerif", 0, 1252},
-    {0xa0607db1, L"GungsuhChe",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0x9565085e, "BookAntiqua",
+     "Droid Sans Japanese,Liberation Sans,Liberation Serif", 2, 1252},
+    {0x9856d95d, "AdobeMingStd", "AR PL UMing HK", 0, 949},
+    {0x9bbadd6b, "ColonnaMT",
+     "KacstQurn,Droid Sans Japanese,Khmer OS,Khmer OS System", 0, 1252},
+    {0x9cbd16a4, "ShowcardGothic-Reg",
+     "Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
+    {0x9d73008e, "MSSansSerif", "FreeSerif", 0, 1252},
+    {0xa0607db1, "GungsuhChe",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      1, 1252},
-    {0xa0bcf6a1, L"LatinWide", L"FreeSerif", 2, 1252},
-    {0xa1429b36, L"Symbol", L"FreeSerif", 6, 42},
-    {0xa1fa5abc, L"Wingdings2", L"FreeSerif", 6, 42},
-    {0xa1fa5abd, L"Wingdings3", L"FreeSerif", 6, 42},
-    {0xa427bad4, L"InformalRoman-Regular",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     L"Japanese,FreeSerif",
+    {0xa0bcf6a1, "LatinWide", "FreeSerif", 2, 1252},
+    {0xa1429b36, "Symbol", "FreeSerif", 6, 42},
+    {0xa1fa5abc, "Wingdings2", "FreeSerif", 6, 42},
+    {0xa1fa5abd, "Wingdings3", "FreeSerif", 6, 42},
+    {0xa427bad4, "InformalRoman-Regular",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,FreeSerif",
      8, 1252},
-    {0xa8b92ece, L"FZSTK--GBK1-0", L"AR PL UMing CN", 0, 936},
-    {0xa8d83ece, L"CalifornianFB", L"Droid Sans Japanese,FreeSerif", 2, 1252},
-    {0xaa3e082c, L"Kingsoft-Phonetic",
-     L"Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans "
-     L"Thai,utkal,Kedage,Mallige,AR PL UKai CN",
+    {0xa8b92ece, "FZSTK--GBK1-0", "AR PL UMing CN", 0, 936},
+    {0xa8d83ece, "CalifornianFB", "Droid Sans Japanese,FreeSerif", 2, 1252},
+    {0xaa3e082c, "Kingsoft-Phonetic",
+     "Tibetan Machine Uni,LKLUG,Samyak Gujarati,Droid Sans "
+     "Thai,utkal,Kedage,Mallige,AR PL UKai CN",
      0, 1252},
-    {0xaa6bcabe, L"HarlowSolidItalic",
-     L"KacstQurn,Droid Sans Japanese,Liberation Serif", 0, 1252},
-    {0xade5337c, L"MSUIGothic",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0xaa6bcabe, "HarlowSolidItalic",
+     "KacstQurn,Droid Sans Japanese,Liberation Serif", 0, 1252},
+    {0xade5337c, "MSUIGothic",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 1252},
-    {0xb08dd941, L"WideLatin",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     L"Sans Japanese,Liberation Serif",
+    {0xb08dd941, "WideLatin",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,Liberation Serif",
      2, 1252},
-    {0xb12765e0, L"ITCLegacySansStdBook",
-     L"AR PL UMing HK,AR PL UKai HK,FreeSerif,Ubuntu,FreeSans", 0, 1252},
-    {0xb207f05d, L"PoorRichard", L"Droid Sans Japanese,Liberation Serif", 2,
+    {0xb12765e0, "ITCLegacySansStdBook",
+     "AR PL UMing HK,AR PL UKai HK,FreeSerif,Ubuntu,FreeSans", 0, 1252},
+    {0xb207f05d, "PoorRichard", "Droid Sans Japanese,Liberation Serif", 2,
      1252},
-    {0xb3bc492f, L"JuiceITC-Regular", L"Droid Sans Japanese,Liberation Serif",
-     0, 1252},
-    {0xb5545399, L"Marlett", L"Liberation Serif", 4, 42},
-    {0xb5dd1ebb, L"BritannicBold",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans "
-     L"Ethiopic,mry_KacstQurn,Liberation Serif",
-     0, 1252},
-    {0xb699c1c5, L"LucidaCalligraphy-Italic",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     L"Sans Japanese,DejaVu Serif",
-     0, 1252},
-    {0xb725d629, L"TimesNewRoman", L"Droid Sans Japanese,Liberation Sans", 2,
+    {0xb3bc492f, "JuiceITC-Regular", "Droid Sans Japanese,Liberation Serif", 0,
      1252},
-    {0xb7eaebeb, L"AdobeHeitiStdR",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0xb5545399, "Marlett", "Liberation Serif", 4, 42},
+    {0xb5dd1ebb, "BritannicBold",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans "
+     "Ethiopic,mry_KacstQurn,Liberation Serif",
+     0, 1252},
+    {0xb699c1c5, "LucidaCalligraphy-Italic",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,DejaVu Serif",
+     0, 1252},
+    {0xb725d629, "TimesNewRoman", "Droid Sans Japanese,Liberation Sans", 2,
+     1252},
+    {0xb7eaebeb, "AdobeHeitiStdR",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0xbd29c486, L"BerlinSansFBDemi-Bold", L"Droid Sans Japanese,DejaVu Serif",
+    {0xbd29c486, "BerlinSansFBDemi-Bold", "Droid Sans Japanese,DejaVu Serif", 0,
+     1252},
+    {0xbe8a8db4, "BookshelfSymbolSeven", "DejaVu Sans", 0, 1252},
+    {0xc16c0118, "AdobeHebrew", "Droid Sans Japanese,Ubuntu,Liberation Serif",
      0, 1252},
-    {0xbe8a8db4, L"BookshelfSymbolSeven", L"DejaVu Sans", 0, 1252},
-    {0xc16c0118, L"AdobeHebrew", L"Droid Sans Japanese,Ubuntu,Liberation Serif",
-     0, 1252},
-    {0xc318b0af, L"MyriadProLight",
-     L"Droid Sans Japanese,AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+    {0xc318b0af, "MyriadProLight",
+     "Droid Sans Japanese,AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0, 1252},
+    {0xc65e5659, "CambriaMath", "Droid Sans Japanese,FreeSerif,FreeMono", 2,
      1252},
-    {0xc65e5659, L"CambriaMath", L"Droid Sans Japanese,FreeSerif,FreeMono", 2,
+    {0xc75c8f05, "LucidaConsole",
+     "Liberation Mono,DejaVu Sans Mono,FreeMono,WenQuanYi Micro Hei Mono", 1,
      1252},
-    {0xc75c8f05, L"LucidaConsole",
-     L"Liberation Mono,DejaVu Sans Mono,FreeMono,WenQuanYi Micro Hei Mono", 1,
-     1252},
-    {0xca7c35d6, L"Calibri", L"Droid Sans Japanese,DejaVu Sans", 0, 1252},
-    {0xcb053f53, L"MicrosoftYaHei",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0xca7c35d6, "Calibri", "Droid Sans Japanese,DejaVu Sans", 0, 1252},
+    {0xcb053f53, "MicrosoftYaHei",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0xcb7190f9, L"Magneto-Bold",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     L"Japanese,DejaVu Serif",
+    {0xcb7190f9, "Magneto-Bold",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,DejaVu Serif",
      0, 1252},
-    {0xcca00cc5, L"System", L"DejaVu Sans", 0, 1252},
-    {0xccad6f76, L"Jokerman-Regular", L"Droid Sans Japanese,DejaVu Sans", 0,
+    {0xcca00cc5, "System", "DejaVu Sans", 0, 1252},
+    {0xccad6f76, "Jokerman-Regular", "Droid Sans Japanese,DejaVu Sans", 0,
      1252},
-    {0xccc5818c, L"EuroSign", L"DejaVu Serif", 0, 1252},
-    {0xcf3d7234, L"LucidaHandwriting-Italic",
-     L"Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Serif", 0,
+    {0xccc5818c, "EuroSign", "DejaVu Serif", 0, 1252},
+    {0xcf3d7234, "LucidaHandwriting-Italic",
+     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans L,DejaVu Serif", 0,
      1252},
-    {0xcf7b8fdb, L"MinionPro", L"DejaVu Sans", 0, 1252},
-    {0xcfe5755f, L"Simhei",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0xcf7b8fdb, "MinionPro", "DejaVu Sans", 0, 1252},
+    {0xcfe5755f, "Simhei",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      1, 936},
-    {0xd011f4ee, L"MSPGothic",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 0,
+    {0xd011f4ee, "MSPGothic",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW", 0,
      1252},
-    {0xd060e7ef, L"Vivaldi",
-     L"KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 8, 1252},
-    {0xd07edec1, L"FranklinGothic-Medium", L"Droid Sans Japanese,Ubuntu", 0,
+    {0xd060e7ef, "Vivaldi",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 8, 1252},
+    {0xd07edec1, "FranklinGothic-Medium", "Droid Sans Japanese,Ubuntu", 0,
      1252},
-    {0xd107243f, L"SimSun", L"WenQuanYi Zen Hei Mono", 0, 936},
-    {0xd1881562, L"ArialNarrow",
-     L"Liberation Sans Narrow,Droid Sans Japanese,FreeSerif", 0, 1252},
-    {0xd22b7dce, L"BodoniMTPosterCompressed",
-     L"Droid Sans Japanese,DejaVu Serif", 0, 1252},
-    {0xd22bfa60, L"ComicSansMS",
-     L"Droid Sans Japanese,FreeMono,Liberation Mono", 8, 1252},
-    {0xd3bd0e35, L"Bauhaus93",
-     L"KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
-    {0xd429ee7a, L"STFangsong", L"WenQuanYi Micro Hei Mono", 0, 936},
-    {0xd6679c12, L"BernardMTCondensed",
-     L"KacstQurn,Droid Sans Japanese,Nimbus Sans L,URW Chancery "
-     L"L,KacstOne,Liberation Sans",
+    {0xd107243f, "SimSun", "WenQuanYi Zen Hei Mono", 0, 936},
+    {0xd1881562, "ArialNarrow",
+     "Liberation Sans Narrow,Droid Sans Japanese,FreeSerif", 0, 1252},
+    {0xd22b7dce, "BodoniMTPosterCompressed", "Droid Sans Japanese,DejaVu Serif",
      0, 1252},
-    {0xd8e8a027, L"LucidaSans",
-     L"Liberation Sans Narrow,Nimbus Sans L,KacstQurn,Droid Arabic Naskh,Droid "
-     L"Sans Ethiopic,DejaVu Serif Condensed,Liberation Mono,Ubuntu",
-     0, 1252},
-    {0xd9fe7761, L"HighTowerText-Reg",
-     L"Droid Sans Japanese,Ubuntu,Liberation Serif", 2, 1252},
-    {0xda7e551e, L"STSong", L"WenQuanYi Micro Hei Mono", 0, 936},
-    {0xdaa6842d, L"STZhongsong",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
-     0, 936},
-    {0xdaaab93f, L"STFangsong",
-     L"WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     L"Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdaeb0713, L"STSong",
-     L"WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     L"Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdafedbef, L"STCaiyun", L"AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdb00a3d9, L"Broadway",
-     L"KacstQurn,Droid Sans Japanese,DejaVu Sans,FreeMono,Liberation Mono", 0,
-     1252},
-    {0xdb1f5ad4, L"STXinwei", L"AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdb326e7f, L"STKaiti",
-     L"WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     L"Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdb69595a, L"STHupo",
-     L"WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     L"Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdba0082c, L"STXihei",
-     L" WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
-     L"Hei,WenQuanYi Zen Hei Sharp",
-     0, 936},
-    {0xdbd0ab18, L"STXingkai", L"AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdc1a7db1, L"STLiti", L"AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
-     936},
-    {0xdc33075f, L"KristenITC-Regular",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
-     L"Condensed,Ubuntu,Liberation Sans",
+    {0xd22bfa60, "ComicSansMS", "Droid Sans Japanese,FreeMono,Liberation Mono",
      8, 1252},
-    {0xdcc7009c, L"Harrington",
-     L"KacstQurn,Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0,
-     1252},
-    {0xdd712466, L"ArialBlack",
-     L"Droid Sans Japanese,DejaVu Sans,DejaVu Serif,FreeMono", 0, 1252},
-    {0xdde87b3e, L"Impact", L"Droid Sans Japanese,DejaVu Serif", 0, 1252},
-    {0xdf69fb32, L"SnapITC",
-     L"Liberation Sans Narrow,Ubuntu Condensed,DejaVu Sans,DejaVu "
-     L"Serif,FreeMono",
+    {0xd3bd0e35, "Bauhaus93",
+     "KacstQurn,Droid Sans Japanese,Liberation Sans,Ubuntu", 0, 1252},
+    {0xd429ee7a, "STFangsong", "WenQuanYi Micro Hei Mono", 0, 936},
+    {0xd6679c12, "BernardMTCondensed",
+     "KacstQurn,Droid Sans Japanese,Nimbus Sans L,URW Chancery "
+     "L,KacstOne,Liberation Sans",
      0, 1252},
-    {0xdf8b25e8, L"CenturyGothic",
-     L"Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
+    {0xd8e8a027, "LucidaSans",
+     "Liberation Sans Narrow,Nimbus Sans L,KacstQurn,Droid Arabic Naskh,Droid "
+     "Sans Ethiopic,DejaVu Serif Condensed,Liberation Mono,Ubuntu",
+     0, 1252},
+    {0xd9fe7761, "HighTowerText-Reg",
+     "Droid Sans Japanese,Ubuntu,Liberation Serif", 2, 1252},
+    {0xda7e551e, "STSong", "WenQuanYi Micro Hei Mono", 0, 936},
+    {0xdaa6842d, "STZhongsong",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
+     0, 936},
+    {0xdaaab93f, "STFangsong",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, 936},
+    {0xdaeb0713, "STSong",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, 936},
+    {0xdafedbef, "STCaiyun", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     936},
+    {0xdb00a3d9, "Broadway",
+     "KacstQurn,Droid Sans Japanese,DejaVu Sans,FreeMono,Liberation Mono", 0,
      1252},
-    {0xe0f705c0, L"KristenITC",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
-     L"Condensed,Ubuntu,Liberation Sans",
+    {0xdb1f5ad4, "STXinwei", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     936},
+    {0xdb326e7f, "STKaiti",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, 936},
+    {0xdb69595a, "STHupo",
+     "WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, 936},
+    {0xdba0082c, "STXihei",
+     " WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei Mono,WenQuanYi Zen "
+     "Hei,WenQuanYi Zen Hei Sharp",
+     0, 936},
+    {0xdbd0ab18, "STXingkai", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     936},
+    {0xdc1a7db1, "STLiti", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+     936},
+    {0xdc33075f, "KristenITC-Regular",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
+     "Condensed,Ubuntu,Liberation Sans",
      8, 1252},
-    {0xe1427573, L"Raavi",
-     L"Droid Arabic Naskh,Droid Sans "
-     L"Ethiopic,mry_KacstQurn,FreeSerif,Liberation Serif,Khmer OS",
-     0, 1252},
-    {0xe2cea0cb, L"Magneto",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
-     L"Serif,DejaVu Serif Condensed,DejaVu Sans",
-     0, 1252},
-    {0xe36a9e17, L"Ravie",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
-     L"Serif,DejaVu Sans,FreeMono",
-     0, 1252},
-    {0xe433f8e2, L"Parchment", L"Droid Sans Japanese,DejaVu Serif", 8, 1252},
-    {0xe43dff4a, L"Wingdings", L"DejaVu Serif", 4, 42},
-    {0xe4e2c405, L"MTExtra", L"DejaVu Serif", 6, 42},
-    {0xe618cc35, L"InformalRoman",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
-     L"Japanese,Nimbus Sans L,DejaVu Sans Condensed,Ubuntu,Liberation Sans",
-     8, 1252},
-    {0xe6c27ffc, L"Mistral", L"Droid Sans Japanese,DejaVu Serif", 8, 1252},
-    {0xe7ebf4b9, L"Courier", L"DejaVu Sans,DejaVu Sans Condensed,FreeSerif", 0,
+    {0xdcc7009c, "Harrington",
+     "KacstQurn,Droid Sans Japanese,Liberation Serif,FreeSerif,Ubuntu", 0,
      1252},
-    {0xe8bc4a9d, L"MSReferenceSpecialty", L"DejaVu Serif", 0, 1252},
-    {0xe90fb013, L"TempusSansITC",
-     L"Droid Sans Japanese,Ubuntu,Liberation Serif,FreeSerif", 0, 1252},
-    {0xec637b42, L"Consolas",
-     L"DejaVu Sans Condensed,AR PL UKai CN,AR PL UKai HK,AR PL UKai "
-     L"TW,FreeSerif,FreeSans",
+    {0xdd712466, "ArialBlack",
+     "Droid Sans Japanese,DejaVu Sans,DejaVu Serif,FreeMono", 0, 1252},
+    {0xdde87b3e, "Impact", "Droid Sans Japanese,DejaVu Serif", 0, 1252},
+    {0xdf69fb32, "SnapITC",
+     "Liberation Sans Narrow,Ubuntu Condensed,DejaVu Sans,DejaVu "
+     "Serif,FreeMono",
+     0, 1252},
+    {0xdf8b25e8, "CenturyGothic",
+     "Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
+     1252},
+    {0xe0f705c0, "KristenITC",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu Sans "
+     "Condensed,Ubuntu,Liberation Sans",
+     8, 1252},
+    {0xe1427573, "Raavi",
+     "Droid Arabic Naskh,Droid Sans "
+     "Ethiopic,mry_KacstQurn,FreeSerif,Liberation Serif,Khmer OS",
+     0, 1252},
+    {0xe2cea0cb, "Magneto",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
+     "Serif,DejaVu Serif Condensed,DejaVu Sans",
+     0, 1252},
+    {0xe36a9e17, "Ravie",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,DejaVu "
+     "Serif,DejaVu Sans,FreeMono",
+     0, 1252},
+    {0xe433f8e2, "Parchment", "Droid Sans Japanese,DejaVu Serif", 8, 1252},
+    {0xe43dff4a, "Wingdings", "DejaVu Serif", 4, 42},
+    {0xe4e2c405, "MTExtra", "DejaVu Serif", 6, 42},
+    {0xe618cc35, "InformalRoman",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid Sans "
+     "Japanese,Nimbus Sans L,DejaVu Sans Condensed,Ubuntu,Liberation Sans",
+     8, 1252},
+    {0xe6c27ffc, "Mistral", "Droid Sans Japanese,DejaVu Serif", 8, 1252},
+    {0xe7ebf4b9, "Courier", "DejaVu Sans,DejaVu Sans Condensed,FreeSerif", 0,
+     1252},
+    {0xe8bc4a9d, "MSReferenceSpecialty", "DejaVu Serif", 0, 1252},
+    {0xe90fb013, "TempusSansITC",
+     "Droid Sans Japanese,Ubuntu,Liberation Serif,FreeSerif", 0, 1252},
+    {0xec637b42, "Consolas",
+     "DejaVu Sans Condensed,AR PL UKai CN,AR PL UKai HK,AR PL UKai "
+     "TW,FreeSerif,FreeSans",
      1, 1252},
-    {0xed3a683b, L"STXinwei", L"AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
+    {0xed3a683b, "STXinwei", "AR PL UKai HK,AR PL UMing HK,AR PL UKai CN", 0,
      936},
-    {0xef264cd1, L"LucidaHandwriting",
-     L"Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans "
-     L"L,KacstQurn,Liberation Mono",
+    {0xef264cd1, "LucidaHandwriting",
+     "Liberation Sans Narrow,Ubuntu Condensed,Nimbus Sans "
+     "L,KacstQurn,Liberation Mono",
      0, 1252},
-    {0xf086bca2, L"BaskervilleOldFace",
-     L"KacstQurn,Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0,
+    {0xf086bca2, "BaskervilleOldFace",
+     "KacstQurn,Droid Sans Japanese,Liberation Serif,Ubuntu,FreeSerif", 0,
      1252},
-    {0xf1028030, L"Mangal",
-     L"FreeSans,TSCu_Paranar,Garuda,Liberation Sans,Liberation Sans "
-     L"Narrow,Nimbus Sans L",
+    {0xf1028030, "Mangal",
+     "FreeSans,TSCu_Paranar,Garuda,Liberation Sans,Liberation Sans "
+     "Narrow,Nimbus Sans ",
      2, 1252},
-    {0xf1da7eb9, L"ShowcardGothic",
-     L"Droid Sans Japanese,DejaVu Serif Condensed,DejaVu Sans "
-     L"Condensed,Liberation Sans,Ubuntu",
+    {0xf1da7eb9, "ShowcardGothic",
+     "Droid Sans Japanese,DejaVu Serif Condensed,DejaVu Sans "
+     "Condensed,Liberation Sans,Ubuntu",
      0, 1252},
-    {0xf210f06a, L"ArialMT",
-     L"Liberation Sans,Liberation Sans Narrow,FreeSans,Nimbus Sans L,Khmer OS "
-     L"System,Khmer OS",
+    {0xf210f06a, "ArialMT",
+     "Liberation Sans,Liberation Sans Narrow,FreeSans,Nimbus Sans L,Khmer OS "
+     "System,Khmer OS",
      0, 1252},
-    {0xf477f16a, L"Latha",
-     L"Liberation Sans Narrow,Nimbus Sans L,Droid Arabic "
-     L"Naskh,mry_KacstQurn,FreeSerif,Nimbus Sans L",
+    {0xf477f16a, "Latha",
+     "Liberation Sans Narrow,Nimbus Sans L,Droid Arabic "
+     "Naskh,mry_KacstQurn,FreeSerif,Nimbus Sans ",
      0, 1252},
-    {0xf616f3dd, L"LiSu",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     L"PL UMing TW MBE",
+    {0xf616f3dd, "LiSu",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE",
      1, 936},
-    {0xfa479aa6, L"MicrosoftYaHei",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0xfa479aa6, "MicrosoftYaHei",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
-    {0xfcd19697, L"BookmanOldStyle",
-     L"Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
+    {0xfcd19697, "BookmanOldStyle",
+     "Droid Sans Japanese,Liberation Mono,Liberation Sans,Liberation Serif", 0,
      1252},
-    {0xfe209a82, L"LucidaCalligraphy",
-     L"KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
-     L"Sans Japanese,DejaVu Serif,DejaVu Sans,FreeMono",
+    {0xfe209a82, "LucidaCalligraphy",
+     "KacstQurn,Droid Arabic Naskh,Droid Sans Ethiopic,mry_KacstQurn,Droid "
+     "Sans Japanese,DejaVu Serif,DejaVu Sans,FreeMono",
      0, 1252},
-    {0xfef135f8, L"AdobeHeitiStd-Regular",
-     L"WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
-     L"Sharp,WenQuanYi Micro Hei",
+    {0xfef135f8, "AdobeHeitiStd-Regular",
+     "WenQuanYi Zen Hei Mono,WenQuanYi Zen Hei,WenQuanYi Zen Hei "
+     "Sharp,WenQuanYi Micro Hei",
      0, 936},
 };
-#elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#elif defined(OS_MACOSX)
 const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, L"SimSun", L"STHeiti,Heiti TC,STFangsong", 0, 936},
-    {0x01e4f102, L"YouYuan", L"STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0x030549dc, L"LiSu", L"STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0x032edd44, L"Simhei", L"STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0x03eac6fc, L"PoorRichard-Regular",
-     L"Noteworthy,Avenir Next Condensed,Impact", 2, 1252},
-    {0x03ed90e6, L"Nina", L"Microsoft Sans Serif", 0, 1252},
-    {0x077b56b3, L"KingsoftPhoneticPlain",
-     L"LastResort,Apple "
-     L"Chancery,STIXVariants,STIXSizeOneSym,STIXSizeOneSym,Apple Braille",
+    {0x01d5d33e, "SimSun", "STHeiti,Heiti TC,STFangsong", 0, 936},
+    {0x01e4f102, "YouYuan", "STHeiti,Heiti TC,STFangsong", 1, 936},
+    {0x030549dc, "LiSu", "STHeiti,Heiti TC,STFangsong", 1, 936},
+    {0x032edd44, "Simhei", "STHeiti,Heiti TC,STFangsong", 1, 936},
+    {0x03eac6fc, "PoorRichard-Regular",
+     "Noteworthy,Avenir Next Condensed,Impact", 2, 1252},
+    {0x03ed90e6, "Nina", "Microsoft Sans Serif", 0, 1252},
+    {0x077b56b3, "KingsoftPhoneticPlain",
+     "LastResort,Apple "
+     "Chancery,STIXVariants,STIXSizeOneSym,STIXSizeOneSym,Apple Braille",
      0, 1252},
-    {0x078ed524, L"MicrosoftSansSerif", L"Songti SC,Apple Symbols", 0, 1252},
-    {0x089b18a9, L"Arial",
-     L"Arial Unicode MS,Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x0b2cad72, L"MonotypeCorsiva", L"Arial Narrow,Impact", 8, 1252},
-    {0x0bb003e7, L"Kartika",
-     L"Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Damascus", 2, 1252},
-    {0x0bb469df, L"VinerHandITC", L"Comic Sans MS,Songti SC,STSong", 8, 1252},
-    {0x0bc1a851, L"SegoeUI", L"Apple Symbols", 0, 1252},
-    {0x0c112ebd, L"KozukaGothicPro-VIM", L"Microsoft Sans Serif,Apple Symbols",
+    {0x078ed524, "MicrosoftSansSerif", "Songti SC,Apple Symbols", 0, 1252},
+    {0x089b18a9, "Arial", "Arial Unicode MS,Microsoft Sans Serif,Apple Symbols",
      0, 1252},
-    {0x0cfcb9c1, L"AdobeThai", L"Avenir Next Condensed Ultra Light", 0, 847},
-    {0x0e7de0f9, L"Playbill", L"STIXNonUnicode", 0, 1252},
-    {0x0eff47c3, L"STHupo", L"Kaiti SC,Songti SC,STHeiti", 0, 936},
-    {0x107ad374, L"Constantia", L"Arial Unicode MS,Palatino,Baskerville", 2,
+    {0x0b2cad72, "MonotypeCorsiva", "Arial Narrow,Impact", 8, 1252},
+    {0x0bb003e7, "Kartika",
+     "Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Damascus", 2, 1252},
+    {0x0bb469df, "VinerHandITC", "Comic Sans MS,Songti SC,STSong", 8, 1252},
+    {0x0bc1a851, "SegoeUI", "Apple Symbols", 0, 1252},
+    {0x0c112ebd, "KozukaGothicPro-VIM", "Microsoft Sans Serif,Apple Symbols", 0,
      1252},
-    {0x12194c2d, L"KunstlerScript",
-     L"Avenir Next Condensed Demi Bold,Arial Narrow", 8, 1252},
-    {0x135ef6a1, L"MinionProSmBd", L"Microsoft Sans Serif,Apple Symbols", 0,
+    {0x0cfcb9c1, "AdobeThai", "Avenir Next Condensed Ultra Light", 0, 847},
+    {0x0e7de0f9, "Playbill", "STIXNonUnicode", 0, 1252},
+    {0x0eff47c3, "STHupo", "Kaiti SC,Songti SC,STHeiti", 0, 936},
+    {0x107ad374, "Constantia", "Arial Unicode MS,Palatino,Baskerville", 2,
      1252},
-    {0x158c4049, L"Garamond", L"Impact,Arial Narrow", 2, 1252},
-    {0x160ecb24, L"STZhongsong", L"STFangsong,Songti SC", 0, 936},
-    {0x161ed07e, L"MSGothic",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing "
-     L"TW,Microsoft Sans Serif,Apple Symbols",
+    {0x12194c2d, "KunstlerScript",
+     "Avenir Next Condensed Demi Bold,Arial Narrow", 8, 1252},
+    {0x135ef6a1, "MinionProSmBd", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x158c4049, "Garamond", "Impact,Arial Narrow", 2, 1252},
+    {0x160ecb24, "STZhongsong", "STFangsong,Songti SC", 0, 936},
+    {0x161ed07e, "MSGothic",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing "
+     "TW,Microsoft Sans Serif,Apple Symbols",
      1, 1252},
-    {0x171d1ed1, L"SnapITC-Regular", L"STHeiti,Arial Black", 0, 1252},
-    {0x18d1188f, L"Cambria", L"Arial Unicode MS", 2, 1252},
-    {0x18eaf350, L"ArialUnicodeMS", L"Microsoft Sans Serif,Apple Symbols", 0,
+    {0x171d1ed1, "SnapITC-Regular", "STHeiti,Arial Black", 0, 1252},
+    {0x18d1188f, "Cambria", "Arial Unicode MS", 2, 1252},
+    {0x18eaf350, "ArialUnicodeMS", "Microsoft Sans Serif,Apple Symbols", 0,
      936},
-    {0x1a92d115, L"MingLiU", L"Heiti SC,STHeiti", 1, 1252},
-    {0x1cc217c6, L"TrebuchetMS", L"Damascus,Impact,Arial Narrow", 0, 1252},
-    {0x1d649596, L"BasemicTimes", L"Liberation Serif,Impact,Arial Narrow", 0,
+    {0x1a92d115, "MingLiU", "Heiti SC,STHeiti", 1, 1252},
+    {0x1cc217c6, "TrebuchetMS", "Damascus,Impact,Arial Narrow", 0, 1252},
+    {0x1d649596, "BasemicTimes", "Liberation Serif,Impact,Arial Narrow", 0,
      1252},
-    {0x1e34ee60, L"BellMT",
-     L"Papyrus,STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 2, 1252},
-    {0x1eb36945, L"CooperBlack",
-     L"Marion,STIXNonUnicode,Arial Rounded MT Bold,Lucida Grande", 2, 1252},
-    {0x1ef7787d, L"BatangChe",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,,AR PL UMing HK,AR PL UMing TW,AR "
-     L"PL UMing TW MBE,Arial Unicode MS,Heiti TC",
+    {0x1e34ee60, "BellMT",
+     "Papyrus,STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 2, 1252},
+    {0x1eb36945, "CooperBlack",
+     "Marion,STIXNonUnicode,Arial Rounded MT Bold,Lucida Grande", 2, 1252},
+    {0x1ef7787d, "BatangChe",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE,Arial Unicode MS,Heiti TC",
      1, 1252},
-    {0x20b3bd3a, L"BrushScriptMT",
-     L"STIXNonUnicode,Damascus,Arial Narrow,Avenir Next Condensed,Cochin", 8,
+    {0x20b3bd3a, "BrushScriptMT",
+     "STIXNonUnicode,Damascus,Arial Narrow,Avenir Next Condensed,Cochin", 8,
      1252},
-    {0x220877aa, L"Candara", L"Cochin,Baskerville,Marion", 0, 1252},
-    {0x22135007, L"FreestyleScript-Regular",
-     L"STIXNonUnicode,Nadeem,Zapf Dingbats", 8, 1252},
-    {0x251059c3, L"Chiller",
-     L"Zapf Dingbats,Damascus,STIXNonUnicode,Papyrus,KufiStandardGK,Baghdad", 0,
+    {0x220877aa, "Candara", "Cochin,Baskerville,Marion", 0, 1252},
+    {0x22135007, "FreestyleScript-Regular",
+     "STIXNonUnicode,Nadeem,Zapf Dingbats", 8, 1252},
+    {0x251059c3, "Chiller",
+     "Zapf Dingbats,Damascus,STIXNonUnicode,Papyrus,KufiStandardGK,Baghdad", 0,
      1252},
-    {0x25bed6dd, L"MSReferenceSansSerif",
-     L"Tahoma,Apple Symbols,Apple LiGothic,Arial Unicode MS,Lucida "
-     L"Grande,Microsoft Sans Serif",
+    {0x25bed6dd, "MSReferenceSansSerif",
+     "Tahoma,Apple Symbols,Apple LiGothic,Arial Unicode MS,Lucida "
+     "Grande,Microsoft Sans Serif",
      0, 1252},
-    {0x28154c81, L"Parchment-Regular", L"Microsoft Sans Serif,Apple Symbols", 8,
+    {0x28154c81, "Parchment-Regular", "Microsoft Sans Serif,Apple Symbols", 8,
      1252},
-    {0x29711eb9, L"STLiti", L"Kaiti SC,Songti SC", 0, 936},
-    {0x2b1993b4, L"Basemic", L"Impact,Arial Narrow", 0, 1252},
-    {0x2b316339, L"NiagaraSolid-Reg", L"Microsoft Sans Serif,Apple Symbols", 0,
+    {0x29711eb9, "STLiti", "Kaiti SC,Songti SC", 0, 936},
+    {0x2b1993b4, "Basemic", "Impact,Arial Narrow", 0, 1252},
+    {0x2b316339, "NiagaraSolid-Reg", "Microsoft Sans Serif,Apple Symbols", 0,
      1252},
-    {0x2c147529, L"FootlightMTLight",
-     L"STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans,Noteworthy", 0, 1252},
-    {0x2c198928, L"HarlowSolid",
-     L"Avenir Medium,Avenir Next Medium,Arial Unicode MS", 0, 1252},
-    {0x2c6ac6b2, L"LucidaBright",
-     L"PT Sans Narrow,Papyrus,Damascus,STIXNonUnicode,Arial Rounded MT "
-     L"Bold,Comic Sans MS,Avenir Next",
+    {0x2c147529, "FootlightMTLight",
+     "STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans,Noteworthy", 0, 1252},
+    {0x2c198928, "HarlowSolid",
+     "Avenir Medium,Avenir Next Medium,Arial Unicode MS", 0, 1252},
+    {0x2c6ac6b2, "LucidaBright",
+     "PT Sans Narrow,Papyrus,Damascus,STIXNonUnicode,Arial Rounded MT "
+     "Bold,Comic Sans MS,Avenir Next",
      2, 1252},
-    {0x2c9f38e2, L"KozukaMinchoPro-VIR", L"Microsoft Sans Serif,Apple Symbols",
+    {0x2c9f38e2, "KozukaMinchoPro-VIR", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x2d5a47b0, "STCaiyun", "Kaiti SC,Songti SC", 0, 936},
+    {0x2def26bf, "BernardMT-Condensed",
+     "Impact,Avenir Next Condensed Demi Bold,American Typewriter", 0, 1252},
+    {0x2fd8930b, "KozukaMinchoPr6NR", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x3115525a, "FangSong_GB2312", "Hiragino Sans GB,STHeiti", 0, 1252},
+    {0x31327817, "MyriadPro", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x32244975, "Helvetica",
+     "Arial Narrow,Arial Unicode MS,Damascus,STIXNonUnicode", 0, 1252},
+    {0x32ac995c, "Terminal", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x338d648a, "NiagaraEngraved-Reg", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x33bb65f2, "Sylfaen", "Arial Unicode MS,Marion", 2, 1252},
+    {0x3402c30e, "MSPMincho", "Arial Unicode MS,Apple SD Gothic Neo", 2, 1252},
+    {0x3412bf31, "SimSun-PUA", "STHeiti,Heiti TC,STFangsong", 0, 936},
+    {0x36eb39b9, "BerlinSansFB", "American Typewriter,Impact", 0, 1252},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
+    {0x3864c4f6, "HighTowerText", "STIXGeneral,.Helvetica Neue Desk UI", 2,
+     1252},
+    {0x3a257d03, "FangSong_GB2312", "Hiragino Sans GB,STHeiti", 0, 1252},
+    {0x3cdae668, "FreestyleScript", "Nadeem,Zapf Dingbats,STIXNonUnicode", 8,
+     1252},
+    {0x3d55aed7, "Jokerman",
+     "Papyrus,Lucida Grande,Heiti TC,American Typewriter", 0, 1252},
+    {0x3d5b4385, "PMingLiU", "Heiti SC,STHeiti", 2, 1252},
+    {0x3d9b7669, "EstrangeloEdessa", "American Typewriter,Marion", 0, 1252},
+    {0x3e532d74, "FranklinGothicMedium", "Impact,Arial Narrow", 0, 1252},
+    {0x3e6aa32d, "NSimSun", "STHeiti,STFangsong", 1, 936},
+    {0x3f6c36a8, "Gautami",
+     "Damascus,STIXNonUnicode,STIXGeneral,American Typewriter", 0, 1252},
+    {0x3ff32662, "Chiller-Regular", "Papyrus,KufiStandardGK,Baghdad", 0, 1252},
+    {0x409de312, "ModernNo.20", "Avenir Next Condensed,Impact", 2, 1252},
+    {0x41443c5e, "Georgia", ".Helvetica Neue Desk UI,Arial Unicode MS", 2,
+     1252},
+    {0x4160ade5, "BellGothicStdBlack", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x421976c4, "Modern-Regular", "Impact", 2, 1252},
+    {0x422a7252, "Stencil", "STIXNonUnicode,Songti SC,Georgia,Baskerville", 0,
+     1252},
+    {0x42c8554f, "Fixedsys", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x435cb41d, "Roman", "Arial Narrow", 0, 1252},
+    {0x47882383, "CourierNew", "PCMyungjo,Osaka,Arial Unicode MS,Songti SC", 1,
+     1252},
+    {0x480a2338, "BerlinSansFBDemi",
+     "STIXNonUnicode,American Typewriter,Avenir Next Condensed Heavy", 0, 1252},
+    {0x480bf7a4, "CourierStd", "Courier New", 0, 1252},
+    {0x481ad6ed, "VladimirScript",
+     "STIXNonUnicode,Avenir Next Condensed,Impact", 8, 1252},
+    {0x4911577a, "YouYuan", "STHeiti,Heiti TC", 1, 936},
+    {0x4a788d72, "STXingkai", "Kaiti SC,Songti SC", 0, 936},
+    {0x4bf88566, "SegoeCondensed", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x4ccf51a4, "BerlinSansFB-Reg",
+     "STIXNonUnicode,American Typewriter,Impact", 0, 1252},
+    {0x4ea967ce, "GulimChe", "Arial Unicode MS,Heiti TC,STFangsong", 1, 1252},
+    {0x4f68bd79, "LetterGothicStd",
+     "Courier New,Andale Mono,Ayuthaya,PCMyungjo,Osaka", 0, 1252},
+    {0x51a0d0e6, "KozukaGothicPr6NM", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x531b3dea, "BasemicSymbol", "Microsoft Sans Serif,Apple Symbols", 0,
+     1252},
+    {0x5333fd39, "CalifornianFB-Reg",
+     "American Typewriter,Avenir Next Condensed,Impact", 2, 1252},
+    {0x53561a54, "FZYTK--GBK1-0", "STFangsong,Songti SC,STSong", 0, 936},
+    {0x55e0dde6, "LucidaSansTypewriter", "Menlo,Courier New,Andale Mono", 0,
+     1252},
+    {0x574d4d3d, "AdobeArabic", "Arial Narrow", 0, 1252},
+    {0x5792e759, "STKaiti", "Songti SC,Arial Unicode MS", 0, 936},
+    {0x5921978e, "LucidaSansUnicode", "Lucida Grande,Arial Unicode MS,Menlo", 0,
+     1252},
+    {0x594e2da4, "Vrinda", "Geeza Pro,Damascus,STIXGeneral,Gill Sans", 0, 1252},
+    {0x59baa9a2, "KaiTi_GB2312", "Hiragino Sans GB,STHeiti", 0, 1252},
+    {0x5cfedf4f, "BaskOldFace",
+     "Avenir Next Condensed Heavy,PT Sans,Avenir Next Condensed", 0, 1252},
+    {0x5e16ac91, "TrajanPro", "Arial Narrow,PT Sans Narrow,Damascus", 0, 1252},
+    {0x5f97921c, "AdobeMyungjoStdM",
+     "AppleMyungjo,AppleGothic,Arial Unicode MS", 0, 936},
+    {0x5fefbfad, "Batang", "Arial Unicode MS,Songti SC", 2, 1252},
+    {0x605342b9, "DotumChe", "Arial Unicode MS,Heiti TC", 1, 1252},
+    {0x608c5f9a, "KaiTi_GB2312", "Hiragino Sans GB,STHeiti,Heiti TC", 0, 936},
+    {0x61efd0d1, "MaturaMTScriptCapitals",
+     "Kokonor,Damascus,STIXNonUnicode,STHeiti,Arial Black,Avenir Next Heavy", 0,
+     1252},
+    {0x626608a9, "MVBoli",
+     "Apple Braille,Geeza Pro,Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x630501a3, "SmallFonts", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x65d0e2a9, "FZYTK--GBK1-0", "STFangsong,Songti SC,STSong", 0, 936},
+    {0x669f29e1, "FZSTK--GBK1-0", "STHeiti,Heiti TC", 0, 936},
+    {0x673a9e5f, "Tunga",
+     "Damascus,STIXNonUnicode,Avenir Next Condensed,Avenir Next Condensed "
+     "Ultra Light,Futura",
      0, 1252},
-    {0x2d5a47b0, L"STCaiyun", L"Kaiti SC,Songti SC", 0, 936},
-    {0x2def26bf, L"BernardMT-Condensed",
-     L"Impact,Avenir Next Condensed Demi Bold,American Typewriter", 0, 1252},
-    {0x2fd8930b, L"KozukaMinchoPr6NR", L"Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x3115525a, L"FangSong_GB2312", L"Hiragino Sans GB,STHeiti", 0, 1252},
-    {0x31327817, L"MyriadPro", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x32244975, L"Helvetica",
-     L"Arial Narrow,Arial Unicode MS,Damascus,STIXNonUnicode", 0, 1252},
-    {0x32ac995c, L"Terminal", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x338d648a, L"NiagaraEngraved-Reg", L"Microsoft Sans Serif,Apple Symbols",
-     0, 1252},
-    {0x33bb65f2, L"Sylfaen", L"Arial Unicode MS,Marion", 2, 1252},
-    {0x3402c30e, L"MSPMincho", L"Arial Unicode MS,Apple SD Gothic Neo", 2,
-     1252},
-    {0x3412bf31, L"SimSun-PUA", L"STHeiti,Heiti TC,STFangsong", 0, 936},
-    {0x36eb39b9, L"BerlinSansFB", L"American Typewriter,Impact", 0, 1252},
-    {0x36f42055, L"UniversATT", L"Microsoft Sans Serif", 0, 1252},
-    {0x3864c4f6, L"HighTowerText", L"STIXGeneral,.Helvetica Neue Desk UI", 2,
-     1252},
-    {0x3a257d03, L"FangSong_GB2312", L"Hiragino Sans GB,STHeiti", 0, 1252},
-    {0x3cdae668, L"FreestyleScript", L"Nadeem,Zapf Dingbats,STIXNonUnicode", 8,
-     1252},
-    {0x3d55aed7, L"Jokerman",
-     L"Papyrus,Lucida Grande,Heiti TC,American Typewriter", 0, 1252},
-    {0x3d5b4385, L"PMingLiU", L"Heiti SC,STHeiti", 2, 1252},
-    {0x3d9b7669, L"EstrangeloEdessa", L"American Typewriter,Marion", 0, 1252},
-    {0x3e532d74, L"FranklinGothicMedium", L"Impact,Arial Narrow", 0, 1252},
-    {0x3e6aa32d, L"NSimSun", L"STHeiti,STFangsong", 1, 936},
-    {0x3f6c36a8, L"Gautami",
-     L"Damascus,STIXNonUnicode,STIXGeneral,American Typewriter", 0, 1252},
-    {0x3ff32662, L"Chiller-Regular", L"Papyrus,KufiStandardGK,Baghdad", 0,
-     1252},
-    {0x409de312, L"ModernNo.20", L"Avenir Next Condensed,Impact", 2, 1252},
-    {0x41443c5e, L"Georgia", L".Helvetica Neue Desk UI,Arial Unicode MS", 2,
-     1252},
-    {0x4160ade5, L"BellGothicStdBlack", L"Microsoft Sans Serif,Apple Symbols",
-     0, 1252},
-    {0x421976c4, L"Modern-Regular", L"Impact", 2, 1252},
-    {0x422a7252, L"Stencil", L"STIXNonUnicode,Songti SC,Georgia,Baskerville", 0,
-     1252},
-    {0x42c8554f, L"Fixedsys", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x435cb41d, L"Roman", L"Arial Narrow", 0, 1252},
-    {0x47882383, L"CourierNew", L"PCMyungjo,Osaka,Arial Unicode MS,Songti SC",
-     1, 1252},
-    {0x480a2338, L"BerlinSansFBDemi",
-     L"STIXNonUnicode,American Typewriter,Avenir Next Condensed Heavy", 0,
-     1252},
-    {0x480bf7a4, L"CourierStd", L"Courier New", 0, 1252},
-    {0x481ad6ed, L"VladimirScript",
-     L"STIXNonUnicode,Avenir Next Condensed,Impact", 8, 1252},
-    {0x4911577a, L"YouYuan", L"STHeiti,Heiti TC", 1, 936},
-    {0x4a788d72, L"STXingkai", L"Kaiti SC,Songti SC", 0, 936},
-    {0x4bf88566, L"SegoeCondensed", L"Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x4ccf51a4, L"BerlinSansFB-Reg",
-     L"STIXNonUnicode,American Typewriter,Impact", 0, 1252},
-    {0x4ea967ce, L"GulimChe", L"Arial Unicode MS,Heiti TC,STFangsong", 1, 1252},
-    {0x4f68bd79, L"LetterGothicStd",
-     L"Courier New,Andale Mono,Ayuthaya,PCMyungjo,Osaka", 0, 1252},
-    {0x51a0d0e6, L"KozukaGothicPr6NM", L"Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x531b3dea, L"BasemicSymbol", L"Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x5333fd39, L"CalifornianFB-Reg",
-     L"American Typewriter,Avenir Next Condensed,Impact", 2, 1252},
-    {0x53561a54, L"FZYTK--GBK1-0", L"STFangsong,Songti SC,STSong", 0, 936},
-    {0x55e0dde6, L"LucidaSansTypewriter", L"Menlo,Courier New,Andale Mono", 0,
-     1252},
-    {0x574d4d3d, L"AdobeArabic", L"Arial Narrow", 0, 1252},
-    {0x5792e759, L"STKaiti", L"Songti SC,Arial Unicode MS", 0, 936},
-    {0x5921978e, L"LucidaSansUnicode", L"Lucida Grande,Arial Unicode MS,Menlo",
-     0, 1252},
-    {0x594e2da4, L"Vrinda", L"Geeza Pro,Damascus,STIXGeneral,Gill Sans", 0,
-     1252},
-    {0x59baa9a2, L"KaiTi_GB2312", L"Hiragino Sans GB,STHeiti", 0, 1252},
-    {0x5cfedf4f, L"BaskOldFace",
-     L"Avenir Next Condensed Heavy,PT Sans,Avenir Next Condensed", 0, 1252},
-    {0x5e16ac91, L"TrajanPro", L"Arial Narrow,PT Sans Narrow,Damascus", 0,
-     1252},
-    {0x5f97921c, L"AdobeMyungjoStdM",
-     L"AppleMyungjo,AppleGothic,Arial Unicode MS", 0, 936},
-    {0x5fefbfad, L"Batang", L"Arial Unicode MS,Songti SC", 2, 1252},
-    {0x605342b9, L"DotumChe", L"Arial Unicode MS,Heiti TC", 1, 1252},
-    {0x608c5f9a, L"KaiTi_GB2312", L"Hiragino Sans GB,STHeiti,Heiti TC", 0, 936},
-    {0x61efd0d1, L"MaturaMTScriptCapitals",
-     L"Kokonor,Damascus,STIXNonUnicode,STHeiti,Arial Black,Avenir Next Heavy",
-     0, 1252},
-    {0x626608a9, L"MVBoli",
-     L"Apple Braille,Geeza Pro,Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x630501a3, L"SmallFonts", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x65d0e2a9, L"FZYTK--GBK1-0", L"STFangsong,Songti SC,STSong", 0, 936},
-    {0x669f29e1, L"FZSTK--GBK1-0", L"STHeiti,Heiti TC", 0, 936},
-    {0x673a9e5f, L"Tunga",
-     L"Damascus,STIXNonUnicode,Avenir Next Condensed,Avenir Next Condensed "
-     L"Ultra Light,Futura",
-     0, 1252},
-    {0x691aa4ce, L"NiagaraSolid", L"Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x696259b7, L"Corbel", L"Cochin,Baskerville,Marion", 0, 1252},
-    {0x696ee9be, L"STXihei", L"STHeiti,Heiti TC,Songti SC,Arial Unicode MS", 0,
+    {0x691aa4ce, "NiagaraSolid", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x696259b7, "Corbel", "Cochin,Baskerville,Marion", 0, 1252},
+    {0x696ee9be, "STXihei", "STHeiti,Heiti TC,Songti SC,Arial Unicode MS", 0,
      936},
-    {0x6c59cf69, L"Dotum", L"Arial Unicode MS,Songti SC", 0, 1252},
-    {0x707fa561, L"Gungsuh", L"Arial Unicode MS,Heiti TC", 2, 1252},
-    {0x71416bb2, L"ZWAdobeF",
-     L"STIXSizeFourSym,STIXSizeThreeSym,STIXSizeTwoSym,STIXSizeOneSym", 0,
-     1252},
-    {0x71b41801, L"Verdana",
-     L"Tahoma,Marion,Apple Symbols,.Helvetica Neue Desk UI,Lucida "
-     L"Grande,Courier New",
+    {0x6c59cf69, "Dotum", "Arial Unicode MS,Songti SC", 0, 1252},
+    {0x707fa561, "Gungsuh", "Arial Unicode MS,Heiti TC", 2, 1252},
+    {0x71416bb2, "ZWAdobeF",
+     "STIXSizeFourSym,STIXSizeThreeSym,STIXSizeTwoSym,STIXSizeOneSym", 0, 1252},
+    {0x71b41801, "Verdana",
+     "Tahoma,Marion,Apple Symbols,.Helvetica Neue Desk UI,Lucida "
+     "Grande,Courier New",
      0, 1252},
-    {0x73f25e4c, L"PalatinoLinotype", L"Palatino,Arial Unicode MS", 0, 1252},
-    {0x73f4d19f, L"NiagaraEngraved", L"Microsoft Sans Serif,Apple Symbols", 0,
+    {0x73f25e4c, "PalatinoLinotype", "Palatino,Arial Unicode MS", 0, 1252},
+    {0x73f4d19f, "NiagaraEngraved", "Microsoft Sans Serif,Apple Symbols", 0,
      1252},
-    {0x74001694, L"MyriadProBlack", L"Palatino,Baskerville,Marion,Cochin", 0,
+    {0x74001694, "MyriadProBlack", "Palatino,Baskerville,Marion,Cochin", 0,
      1252},
-    {0x74b14d8f, L"Haettenschweiler", L"Microsoft Sans Serif,Apple Symbols", 0,
+    {0x74b14d8f, "Haettenschweiler", "Microsoft Sans Serif,Apple Symbols", 0,
      1252},
-    {0x74cb44ee, L"NSimSun", L"STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0x76b4d7ff, L"Shruti",
-     L"Damascus,STIXNonUnicode,Arial Unicode MS,American Typewriter", 0, 1252},
-    {0x788b3533, L"Webdings", L"Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0x797dde99, L"MSSerif", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x7a0f9e9e, L"MSMincho",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     L"PL UMing TW MBE,Arial Unicode MS,Apple SD Gothic Neo",
+    {0x74cb44ee, "NSimSun", "STHeiti,Heiti TC,STFangsong", 1, 936},
+    {0x76b4d7ff, "Shruti",
+     "Damascus,STIXNonUnicode,Arial Unicode MS,American Typewriter", 0, 1252},
+    {0x788b3533, "Webdings", "Microsoft Sans Serif,Apple Symbols", 6, 42},
+    {0x797dde99, "MSSerif", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x7a0f9e9e, "MSMincho",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE,Arial Unicode MS,Apple SD Gothic Neo",
      1, 1252},
-    {0x7b439caf, L"OldEnglishTextMT",
-     L"STIXNonUnicode,Arial Unicode MS,Baskerville,Avenir Next Medium", 0,
+    {0x7b439caf, "OldEnglishTextMT",
+     "STIXNonUnicode,Arial Unicode MS,Baskerville,Avenir Next Medium", 0, 1252},
+    {0x8213a433, "LucidaSans-Typewriter",
+     "Comic Sans MS,Avenir Next,Arial Rounded MT Bold", 0, 1252},
+    {0x82fec929, "AdobeSongStd", "Heiti TC,STHeiti", 0, 936},
+    {0x83581825, "Modern", "Avenir Next Condensed,Impact", 0, 1252},
+    {0x835a2823, "Algerian",
+     "STIXNonUnicode,Baskerville,Avenir Next Medium,American Typewriter", 0,
      1252},
-    {0x8213a433, L"LucidaSans-Typewriter",
-     L"Comic Sans MS,Avenir Next,Arial Rounded MT Bold", 0, 1252},
-    {0x82fec929, L"AdobeSongStdL", L"Heiti TC,STHeiti", 0, 936},
-    {0x83581825, L"Modern", L"Avenir Next Condensed,Impact", 0, 1252},
-    {0x835a2823, L"Algerian",
-     L"STIXNonUnicode,Baskerville,Avenir Next Medium,American Typewriter", 0,
+    {0x83dab9f5, "Script", "Arial Narrow", 0, 1252},
+    {0x847b56da, "Tahoma", "Songti SC,Apple Symbols", 0, 1252},
+    {0x8a783cb2, "SimSun-PUA", "STHeiti,Heiti TC,STFangsong", 0, 1252},
+    {0x8b5cac0e, "Onyx", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0x8c6a499e, "Gulim", "Arial Unicode MS,Songti SC", 0, 1252},
+    {0x8e0af790, "JuiceITC", "Nadeem,Al Bayan", 0, 1252},
+    {0x8e8d43b2, "Centaur", "Avenir Next Condensed,Noteworthy,Impact", 2, 1252},
+    {0x8ee4dcca, "BookshelfSymbol7", "Microsoft Sans Serif,Apple Symbols", 0,
      1252},
-    {0x83dab9f5, L"Script", L"Arial Narrow", 0, 1252},
-    {0x847b56da, L"Tahoma", L"Songti SC,Apple Symbols", 0, 1252},
-    {0x8a783cb2, L"SimSun-PUA", L"STHeiti,Heiti TC,STFangsong", 0, 1252},
-    {0x8b5cac0e, L"Onyx", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0x8c6a499e, L"Gulim", L"Arial Unicode MS,Songti SC", 0, 1252},
-    {0x8e0af790, L"JuiceITC", L"Nadeem,Al Bayan", 0, 1252},
-    {0x8e8d43b2, L"Centaur", L"Avenir Next Condensed,Noteworthy,Impact", 2,
+    {0x90794800, "BellGothicStdLight", "Microsoft Sans Serif,Apple Symbols", 0,
      1252},
-    {0x8ee4dcca, L"BookshelfSymbol7", L"Microsoft Sans Serif,Apple Symbols", 0,
-     1252},
-    {0x90794800, L"BellGothicStdLight", L"Microsoft Sans Serif,Apple Symbols",
-     0, 1252},
-    {0x909b516a, L"Century", L"Damascus,Andale Mono,Songti SC,Arial Unicode MS",
+    {0x909b516a, "Century", "Damascus,Andale Mono,Songti SC,Arial Unicode MS",
      2, 1252},
-    {0x92ae370d, L"MSOutlook", L"Microsoft Sans Serif,Apple Symbols", 4, 42},
-    {0x93c9fbf1, L"LucidaFax",
-     L"PT Sans Narrow,Papyrus,Kokonor,Geeza Pro,Arial Rounded MT Bold,Lucida "
-     L"Grande,Futura",
+    {0x92ae370d, "MSOutlook", "Microsoft Sans Serif,Apple Symbols", 4, 42},
+    {0x93c9fbf1, "LucidaFax",
+     "PT Sans Narrow,Papyrus,Kokonor,Geeza Pro,Arial Rounded MT Bold,Lucida "
+     "Grande,Futura",
      2, 1252},
-    {0x9565085e, L"BookAntiqua", L"Palatino,Microsoft Sans Serif,Apple Symbols",
+    {0x9565085e, "BookAntiqua", "Palatino,Microsoft Sans Serif,Apple Symbols",
      2, 1252},
-    {0x9856d95d, L"AdobeMingStdL", L"AHiragino Sans GB,Heiti TC,STHeiti", 0,
-     949},
-    {0x9bbadd6b, L"ColonnaMT", L"Noteworthy,Avenir Next Condensed,Impact", 0,
+    {0x9856d95d, "AdobeMingStd", "AHiragino Sans GB,Heiti TC,STHeiti", 0, 949},
+    {0x9bbadd6b, "ColonnaMT", "Noteworthy,Avenir Next Condensed,Impact", 0,
      1252},
-    {0x9cbd16a4, L"ShowcardGothic-Reg",
-     L"Arial Unicode MS,Georgia,American Typewriter", 0, 1252},
-    {0x9d73008e, L"MSSansSerif", L"Songti SC,Apple Symbols", 0, 1252},
-    {0xa0607db1, L"GungsuhChe",
-     L"WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
-     L"PL UMing TW MBE,Arial Unicode MS,Heiti TC,STFangsong",
+    {0x9cbd16a4, "ShowcardGothic-Reg",
+     "Arial Unicode MS,Georgia,American Typewriter", 0, 1252},
+    {0x9d73008e, "MSSansSerif", "Songti SC,Apple Symbols", 0, 1252},
+    {0xa0607db1, "GungsuhChe",
+     "WenQuanYi Zen Hei Mono,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR "
+     "PL UMing TW MBE,Arial Unicode MS,Heiti TC,STFangsong",
      1, 1252},
-    {0xa0bcf6a1, L"LatinWide", L"Zapfino,Arial Black,STHeiti", 2, 1252},
-    {0xa1429b36, L"Symbol", L"Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0xa1fa5abc, L"Wingdings2", L"Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0xa1fa5abd, L"Wingdings3", L"Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0xa427bad4, L"InformalRoman-Regular",
-     L"STIXNonUnicode,Arial Narrow,Avenir Next Condensed Demi Bold", 8, 1252},
-    {0xa8b92ece, L"FZSTK--GBK1-0", L"STHeiti,Heiti TC,STFangsong", 0, 936},
-    {0xa8d83ece, L"CalifornianFB",
-     L"American Typewriter,Avenir Next Condensed,Impact", 2, 1252},
-    {0xaa3e082c, L"Kingsoft-Phonetic",
-     L"STIXVariants,STIXSizeOneSym,Apple Braille", 0, 1252},
-    {0xaa6bcabe, L"HarlowSolidItalic",
-     L"STIXNonUnicode,Avenir Medium,Avenir Next Medium,Arial Unicode MS", 0,
+    {0xa0bcf6a1, "LatinWide", "Zapfino,Arial Black,STHeiti", 2, 1252},
+    {0xa1429b36, "Symbol", "Microsoft Sans Serif,Apple Symbols", 6, 42},
+    {0xa1fa5abc, "Wingdings2", "Microsoft Sans Serif,Apple Symbols", 6, 42},
+    {0xa1fa5abd, "Wingdings3", "Microsoft Sans Serif,Apple Symbols", 6, 42},
+    {0xa427bad4, "InformalRoman-Regular",
+     "STIXNonUnicode,Arial Narrow,Avenir Next Condensed Demi Bold", 8, 1252},
+    {0xa8b92ece, "FZSTK--GBK1-0", "STHeiti,Heiti TC,STFangsong", 0, 936},
+    {0xa8d83ece, "CalifornianFB",
+     "American Typewriter,Avenir Next Condensed,Impact", 2, 1252},
+    {0xaa3e082c, "Kingsoft-Phonetic",
+     "STIXVariants,STIXSizeOneSym,Apple Braille", 0, 1252},
+    {0xaa6bcabe, "HarlowSolidItalic",
+     "STIXNonUnicode,Avenir Medium,Avenir Next Medium,Arial Unicode MS", 0,
      1252},
-    {0xade5337c, L"MSUIGothic", L"Arial Unicode MS,Apple SD Gothic Neo", 0,
-     1252},
-    {0xb08dd941, L"WideLatin",
-     L"Marion,Papyrus,Nanum Pen Script,Zapf Dingbats,Damascus,Zapfino,Arial "
-     L"Black,STHeiti",
+    {0xade5337c, "MSUIGothic", "Arial Unicode MS,Apple SD Gothic Neo", 0, 1252},
+    {0xb08dd941, "WideLatin",
+     "Marion,Papyrus,Nanum Pen Script,Zapf Dingbats,Damascus,Zapfino,Arial "
+     "Black,STHeiti",
      2, 1252},
-    {0xb12765e0, L"ITCLegacySansStdBook",
-     L"LastResort,.Helvetica Neue Desk UI,Arial Unicode MS,Palatino", 0, 1252},
-    {0xb207f05d, L"PoorRichard", L"Noteworthy,Avenir Next Condensed,Impact", 2,
+    {0xb12765e0, "ITCLegacySansStdBook",
+     "LastResort,.Helvetica Neue Desk UI,Arial Unicode MS,Palatino", 0, 1252},
+    {0xb207f05d, "PoorRichard", "Noteworthy,Avenir Next Condensed,Impact", 2,
      1252},
-    {0xb3bc492f, L"JuiceITC-Regular", L"Nadeem,Al Bayan,STIXNonUnicode", 0,
+    {0xb3bc492f, "JuiceITC-Regular", "Nadeem,Al Bayan,STIXNonUnicode", 0, 1252},
+    {0xb5545399, "Marlett", "Microsoft Sans Serif,Apple Symbols", 4, 42},
+    {0xb5dd1ebb, "BritannicBold",
+     "Damascus,STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0, 1252},
+    {0xb699c1c5, "LucidaCalligraphy-Italic", "STHeiti,Arial Black", 0, 1252},
+    {0xb725d629, "TimesNewRoman", "Microsoft Sans Serif,Apple Symbols", 2,
      1252},
-    {0xb5545399, L"Marlett", L"Microsoft Sans Serif,Apple Symbols", 4, 42},
-    {0xb5dd1ebb, L"BritannicBold",
-     L"Damascus,STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0, 1252},
-    {0xb699c1c5, L"LucidaCalligraphy-Italic", L"STHeiti,Arial Black", 0, 1252},
-    {0xb725d629, L"TimesNewRoman", L"Microsoft Sans Serif,Apple Symbols", 2,
-     1252},
-    {0xb7eaebeb, L"AdobeHeitiStdR", L"Heiti TC,STHeiti", 0, 936},
-    {0xbd29c486, L"BerlinSansFBDemi-Bold",
-     L"American Typewriter,Avenir Next Condensed Heavy", 0, 1252},
-    {0xbe8a8db4, L"BookshelfSymbolSeven", L"Microsoft Sans Serif,Apple Symbols",
+    {0xb7eaebeb, "AdobeHeitiStdR", "Heiti TC,STHeiti", 0, 936},
+    {0xbd29c486, "BerlinSansFBDemi-Bold",
+     "American Typewriter,Avenir Next Condensed Heavy", 0, 1252},
+    {0xbe8a8db4, "BookshelfSymbolSeven", "Microsoft Sans Serif,Apple Symbols",
      0, 1252},
-    {0xc16c0118, L"AdobeHebrew",
-     L".Helvetica Neue Desk UI,Palatino,American Typewriter", 0, 1252},
-    {0xc318b0af, L"MyriadProLight", L"Palatino,Baskerville,Marion", 0, 1252},
-    {0xc65e5659, L"CambriaMath", L"Arial Unicode MS", 2, 1252},
-    {0xc75c8f05, L"LucidaConsole", L"Courier New,Menlo,Andale Mono", 1, 1252},
-    {0xca7c35d6, L"Calibri", L"Apple Symbols,HeadLineA", 0, 1252},
-    {0xcb053f53, L"MicrosoftYaHei", L"Arial Unicode MS", 0, 936},
-    {0xcb7190f9, L"Magneto-Bold", L"Lucida Grande", 0, 1252},
-    {0xcca00cc5, L"System", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0xccad6f76, L"Jokerman-Regular", L"Lucida Grande", 0, 1252},
-    {0xccc5818c, L"EuroSign", L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0xcf3d7234, L"LucidaHandwriting-Italic",
-     L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0xcf7b8fdb, L"MinionPro",
-     L"Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
-    {0xcfe5755f, L"Simhei", L"STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0xd011f4ee, L"MSPGothic", L"Arial Unicode MS,Apple SD Gothic Neo", 0,
+    {0xc16c0118, "AdobeHebrew",
+     ".Helvetica Neue Desk UI,Palatino,American Typewriter", 0, 1252},
+    {0xc318b0af, "MyriadProLight", "Palatino,Baskerville,Marion", 0, 1252},
+    {0xc65e5659, "CambriaMath", "Arial Unicode MS", 2, 1252},
+    {0xc75c8f05, "LucidaConsole", "Courier New,Menlo,Andale Mono", 1, 1252},
+    {0xca7c35d6, "Calibri", "Apple Symbols,HeadLineA", 0, 1252},
+    {0xcb053f53, "MicrosoftYaHei", "Arial Unicode MS", 0, 936},
+    {0xcb7190f9, "Magneto-Bold", "Lucida Grande", 0, 1252},
+    {0xcca00cc5, "System", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0xccad6f76, "Jokerman-Regular", "Lucida Grande", 0, 1252},
+    {0xccc5818c, "EuroSign", "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0xcf3d7234, "LucidaHandwriting-Italic",
+     "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0xcf7b8fdb, "MinionPro",
+     "Bell MT,Corbel,Times New Roman,Cambria,Berlin Sans FB", 0, 1252},
+    {0xcfe5755f, "Simhei", "STHeiti,Heiti TC,STFangsong", 1, 936},
+    {0xd011f4ee, "MSPGothic", "Arial Unicode MS,Apple SD Gothic Neo", 0, 1252},
+    {0xd060e7ef, "Vivaldi",
+     "STIXNonUnicode,Arial Unicode MS,Avenir Medium,Avenir Next Medium", 8,
      1252},
-    {0xd060e7ef, L"Vivaldi",
-     L"STIXNonUnicode,Arial Unicode MS,Avenir Medium,Avenir Next Medium", 8,
-     1252},
-    {0xd07edec1, L"FranklinGothic-Medium", L"Impact,Arial Narrow", 0, 1252},
-    {0xd107243f, L"SimSun", L"STHeiti,Heiti TC,STFangsong", 0, 936},
-    {0xd1881562, L"ArialNarrow", L"PT Sans Narrow,Apple Symbols", 0, 1252},
-    {0xd22b7dce, L"BodoniMTPosterCompressed",
-     L"Microsoft Sans Serif,Apple Symbols", 0, 1252},
-    {0xd22bfa60, L"ComicSansMS",
-     L"Damascus,Georgia,.Helvetica Neue Desk UI,Lucida Grande,Arial Unicode MS",
+    {0xd07edec1, "FranklinGothic-Medium", "Impact,Arial Narrow", 0, 1252},
+    {0xd107243f, "SimSun", "STHeiti,Heiti TC,STFangsong", 0, 936},
+    {0xd1881562, "ArialNarrow", "PT Sans Narrow,Apple Symbols", 0, 1252},
+    {0xd22b7dce, "BodoniMTPosterCompressed",
+     "Microsoft Sans Serif,Apple Symbols", 0, 1252},
+    {0xd22bfa60, "ComicSansMS",
+     "Damascus,Georgia,.Helvetica Neue Desk UI,Lucida Grande,Arial Unicode MS",
      8, 1252},
-    {0xd3bd0e35, L"Bauhaus93",
-     L"STIXNonUnicode,Arial Unicode MS,Avenir Next,Avenir", 0, 1252},
-    {0xd429ee7a, L"STFangsong", L"Songti SC,Arial Unicode MS", 0, 936},
-    {0xd6679c12, L"BernardMTCondensed",
-     L"Impact,Avenir Next Condensed Demi Bold", 0, 1252},
-    {0xd8e8a027, L"LucidaSans",
-     L"Arial Narrow,Khmer MN,Kokonor,Damascus,Microsoft Sans Serif,Apple "
-     L"Symbols",
+    {0xd3bd0e35, "Bauhaus93",
+     "STIXNonUnicode,Arial Unicode MS,Avenir Next,Avenir", 0, 1252},
+    {0xd429ee7a, "STFangsong", "Songti SC,Arial Unicode MS", 0, 936},
+    {0xd6679c12, "BernardMTCondensed", "Impact,Avenir Next Condensed Demi Bold",
      0, 1252},
-    {0xd9fe7761, L"HighTowerText-Reg",
-     L"STIXGeneral,.Helvetica Neue Desk UI,Trebuchet MS", 2, 1252},
-    {0xda7e551e, L"STSong", L"Arial Unicode MS", 0, 936},
-    {0xdaa6842d, L"STZhongsong", L"STFangsong,Songti SC,STSong", 0, 936},
-    {0xdaaab93f, L"STFangsong", L"Songti SC,Arial Unicode MS", 0, 936},
-    {0xdaeb0713, L"STSong", L"Songti SC,Arial Unicode MS", 0, 936},
-    {0xdafedbef, L"STCaiyun", L"Kaiti SC,Songti SC,STHeiti", 0, 936},
-    {0xdb00a3d9, L"Broadway",
-     L"Papyrus,STIXNonUnicode,Arial Black,Avenir Next Heavy,Heiti TC", 0, 1252},
-    {0xdb1f5ad4, L"STXinwei", L"Kaiti SC,Songti SC,STHeiti", 0, 936},
-    {0xdb326e7f, L"STKaiti", L"Songti SC,Arial Unicode MS", 0, 936},
-    {0xdb69595a, L"STHupo", L"Kaiti SC,Songti SC,STHeiti", 0, 936},
-    {0xdba0082c, L"STXihei", L"Songti SC,Arial Unicode MS", 0, 936},
-    {0xdbd0ab18, L"STXingkai", L"Kaiti SC,Songti SC", 0, 936},
-    {0xdc1a7db1, L"STLiti", L"Kaiti SC,Songti SC", 0, 936},
-    {0xdc33075f, L"KristenITC-Regular",
-     L"STIXNonUnicode,Damascus,Songti SC,STSong", 8, 1252},
-    {0xdcc7009c, L"Harrington",
-     L"STIXNonUnicode,Avenir Next Condensed Heavy,Noteworthy", 0, 1252},
-    {0xdd712466, L"ArialBlack", L"Geeza Pro,Damascus,Songti SC,STSong", 0,
+    {0xd8e8a027, "LucidaSans",
+     "Arial Narrow,Khmer MN,Kokonor,Damascus,Microsoft Sans Serif,Apple "
+     "Symbols",
+     0, 1252},
+    {0xd9fe7761, "HighTowerText-Reg",
+     "STIXGeneral,.Helvetica Neue Desk UI,Trebuchet MS", 2, 1252},
+    {0xda7e551e, "STSong", "Arial Unicode MS", 0, 936},
+    {0xdaa6842d, "STZhongsong", "STFangsong,Songti SC,STSong", 0, 936},
+    {0xdaaab93f, "STFangsong", "Songti SC,Arial Unicode MS", 0, 936},
+    {0xdaeb0713, "STSong", "Songti SC,Arial Unicode MS", 0, 936},
+    {0xdafedbef, "STCaiyun", "Kaiti SC,Songti SC,STHeiti", 0, 936},
+    {0xdb00a3d9, "Broadway",
+     "Papyrus,STIXNonUnicode,Arial Black,Avenir Next Heavy,Heiti TC", 0, 1252},
+    {0xdb1f5ad4, "STXinwei", "Kaiti SC,Songti SC,STHeiti", 0, 936},
+    {0xdb326e7f, "STKaiti", "Songti SC,Arial Unicode MS", 0, 936},
+    {0xdb69595a, "STHupo", "Kaiti SC,Songti SC,STHeiti", 0, 936},
+    {0xdba0082c, "STXihei", "Songti SC,Arial Unicode MS", 0, 936},
+    {0xdbd0ab18, "STXingkai", "Kaiti SC,Songti SC", 0, 936},
+    {0xdc1a7db1, "STLiti", "Kaiti SC,Songti SC", 0, 936},
+    {0xdc33075f, "KristenITC-Regular",
+     "STIXNonUnicode,Damascus,Songti SC,STSong", 8, 1252},
+    {0xdcc7009c, "Harrington",
+     "STIXNonUnicode,Avenir Next Condensed Heavy,Noteworthy", 0, 1252},
+    {0xdd712466, "ArialBlack", "Geeza Pro,Damascus,Songti SC,STSong", 0, 1252},
+    {0xdde87b3e, "Impact", "Arial Narrow,Marion", 0, 1252},
+    {0xdf69fb32, "SnapITC",
+     "Arial Narrow,PT Sans Narrow,Marion,STHeiti,Arial Black", 0, 1252},
+    {0xdf8b25e8, "CenturyGothic",
+     "Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0, 1252},
+    {0xe0f705c0, "KristenITC", "Songti SC,STSong", 8, 1252},
+    {0xe1427573, "Raavi",
+     "Damascus,STIXNonUnicode,Marion,Papyrus,Avenir Next Condensed "
+     "Heavy,American Typewriter",
+     0, 1252},
+    {0xe2cea0cb, "Magneto",
+     "STIXNonUnicode,Damascus,Geeza Pro,Lucida Grande,Georgia,Heiti TC", 0,
      1252},
-    {0xdde87b3e, L"Impact", L"Arial Narrow,Marion", 0, 1252},
-    {0xdf69fb32, L"SnapITC",
-     L"Arial Narrow,PT Sans Narrow,Marion,STHeiti,Arial Black", 0, 1252},
-    {0xdf8b25e8, L"CenturyGothic",
-     L"Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0, 1252},
-    {0xe0f705c0, L"KristenITC", L"Songti SC,STSong", 8, 1252},
-    {0xe1427573, L"Raavi",
-     L"Damascus,STIXNonUnicode,Marion,Papyrus,Avenir Next Condensed "
-     L"Heavy,American Typewriter",
+    {0xe36a9e17, "Ravie", "STHeiti,Arial Black", 0, 1252},
+    {0xe433f8e2, "Parchment", "Microsoft Sans Serif,Apple Symbols", 8, 1252},
+    {0xe43dff4a, "Wingdings", "Microsoft Sans Serif,Apple Symbols", 4, 42},
+    {0xe4e2c405, "MTExtra", "Microsoft Sans Serif,Apple Symbols", 6, 42},
+    {0xe618cc35, "InformalRoman", "Arial Narrow", 8, 1252},
+    {0xe6c27ffc, "Mistral", "Apple Symbols", 8, 1252},
+    {0xe7ebf4b9, "Courier", "Courier New", 0, 1252},
+    {0xe8bc4a9d, "MSReferenceSpecialty", "Microsoft Sans Serif,Apple Symbols",
      0, 1252},
-    {0xe2cea0cb, L"Magneto",
-     L"STIXNonUnicode,Damascus,Geeza Pro,Lucida Grande,Georgia,Heiti TC", 0,
-     1252},
-    {0xe36a9e17, L"Ravie", L"STHeiti,Arial Black", 0, 1252},
-    {0xe433f8e2, L"Parchment", L"Microsoft Sans Serif,Apple Symbols", 8, 1252},
-    {0xe43dff4a, L"Wingdings", L"Microsoft Sans Serif,Apple Symbols", 4, 42},
-    {0xe4e2c405, L"MTExtra", L"Microsoft Sans Serif,Apple Symbols", 6, 42},
-    {0xe618cc35, L"InformalRoman", L"Arial Narrow", 8, 1252},
-    {0xe6c27ffc, L"Mistral", L"Apple Symbols", 8, 1252},
-    {0xe7ebf4b9, L"Courier", L"Courier New", 0, 1252},
-    {0xe8bc4a9d, L"MSReferenceSpecialty", L"Microsoft Sans Serif,Apple Symbols",
-     0, 1252},
-    {0xe90fb013, L"TempusSansITC",
-     L"STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 0, 1252},
-    {0xec637b42, L"Consolas",
-     L"AR PL UKai CN,AR PL UKai HK,AR PL UKai TW,AR PL UKai TW MBE,AR PL UMing "
-     L"CN,AR PL UMing HK,Microsoft Sans Serif,Tahoma",
+    {0xe90fb013, "TempusSansITC",
+     "STIXNonUnicode,Microsoft Sans Serif,Avenir Light", 0, 1252},
+    {0xec637b42, "Consolas",
+     "AR PL UKai CN,AR PL UKai HK,AR PL UKai TW,AR PL UKai TW MBE,AR PL UMing "
+     "CN,AR PL UMing HK,Microsoft Sans Serif,Tahoma",
      1, 1252},
-    {0xed3a683b, L"STXinwei", L"Kaiti SC,Songti SC,", 0, 936},
-    {0xef264cd1, L"LucidaHandwriting",
-     L"Arial Narrow,Avenir Next Condensed Demi Bold,Avenir Next "
-     L"Condensed,Avenir Next Condensed Medium,STHeiti,Arial Black",
+    {0xed3a683b, "STXinwei", "Kaiti SC,Songti SC,", 0, 936},
+    {0xef264cd1, "LucidaHandwriting",
+     "Arial Narrow,Avenir Next Condensed Demi Bold,Avenir Next "
+     "Condensed,Avenir Next Condensed Medium,STHeiti,Arial Black",
      0, 1252},
-    {0xf086bca2, L"BaskervilleOldFace",
-     L"STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0, 1252},
-    {0xf1028030, L"Mangal",
-     L"Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Tahoma", 2, 1252},
-    {0xf1da7eb9, L"ShowcardGothic",
-     L"Papyrus,Arial Unicode MS,Georgia,American Typewriter", 0, 1252},
-    {0xf210f06a, L"ArialMT",
-     L"Arial Unicode MS,Arial Narrow,STIXNonUnicode,Damascus,Avenir Next "
-     L"Condensed Demi Bold,Avenir Next Condensed Medium,Avenir Next Condensed",
+    {0xf086bca2, "BaskervilleOldFace",
+     "STIXNonUnicode,Avenir Next Condensed Heavy,PT Sans", 0, 1252},
+    {0xf1028030, "Mangal",
+     "Arial Unicode MS,Microsoft Sans Serif,Arial Narrow,Tahoma", 2, 1252},
+    {0xf1da7eb9, "ShowcardGothic",
+     "Papyrus,Arial Unicode MS,Georgia,American Typewriter", 0, 1252},
+    {0xf210f06a, "ArialMT",
+     "Arial Unicode MS,Arial Narrow,STIXNonUnicode,Damascus,Avenir Next "
+     "Condensed Demi Bold,Avenir Next Condensed Medium,Avenir Next Condensed",
      0, 1252},
-    {0xf477f16a, L"Latha",
-     L"Arial Narrow,Damascus,STIXNonUnicode,American Typewriter", 0, 1252},
-    {0xf616f3dd, L"LiSu", L"STHeiti,Heiti TC,STFangsong", 1, 936},
-    {0xfa479aa6, L"MicrosoftYaHei", L"Arial Unicode MS", 0, 936},
-    {0xfcd19697, L"BookmanOldStyle",
-     L"Geeza Pro,Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0, 1252},
-    {0xfe209a82, L"LucidaCalligraphy",
-     L"Kokonor,Damascus,STIXNonUnicode,STHeiti,Arial Black", 0, 1252},
-    {0xfef135f8, L"AdobeHeitiStd-Regular", L"Heiti TC,STHeiti", 0, 936},
+    {0xf477f16a, "Latha",
+     "Arial Narrow,Damascus,STIXNonUnicode,American Typewriter", 0, 1252},
+    {0xf616f3dd, "LiSu", "STHeiti,Heiti TC,STFangsong", 1, 936},
+    {0xfa479aa6, "MicrosoftYaHei", "Arial Unicode MS", 0, 936},
+    {0xfcd19697, "BookmanOldStyle",
+     "Geeza Pro,Damascus,Andale Mono,Songti SC,Arial Unicode MS", 0, 1252},
+    {0xfe209a82, "LucidaCalligraphy",
+     "Kokonor,Damascus,STIXNonUnicode,STHeiti,Arial Black", 0, 1252},
+    {0xfef135f8, "AdobeHeitiStd-Regular", "Heiti TC,STHeiti", 0, 936},
 };
-#elif _FX_PLATFORM_ == _FX_PLATFORM_ANDROID_
+#elif defined(OS_ANDROID)
 const FGAS_FontInfo g_XFAFontsMap[] = {
-    {0x01d5d33e, L"SimSun", L"Droid Sans Fallback", 0, 936},
-    {0x01e4f102, L"YouYuan", L"Droid Sans Fallback", 1, 936},
-    {0x030549dc, L"LiSu", L"Droid Sans Fallback", 1, 936},
-    {0x032edd44, L"Simhei", L"Droid Sans Fallback", 1, 936},
-    {0x03eac6fc, L"PoorRichard-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback,Droid Arabic "
-     L"Naskh,Droid Sans Ethiopic",
+    {0x01d5d33e, "SimSun", "Droid Sans Fallback", 0, 936},
+    {0x01e4f102, "YouYuan", "Droid Sans Fallback", 1, 936},
+    {0x030549dc, "LiSu", "Droid Sans Fallback", 1, 936},
+    {0x032edd44, "Simhei", "Droid Sans Fallback", 1, 936},
+    {0x03eac6fc, "PoorRichard-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback,Droid Arabic "
+     "Naskh,Droid Sans Ethiopic",
      2, 1252},
-    {0x03ed90e6, L"Nina",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x077b56b3, L"KingsoftPhoneticPlain",
-     L"Droid Sans Thai,Droid Sans Armenian,Droid Arabic Naskh,Droid Sans "
-     L"Ethiopic,Droid Sans Fallback",
+    {0x03ed90e6, "Nina",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x077b56b3, "KingsoftPhoneticPlain",
+     "Droid Sans Thai,Droid Sans Armenian,Droid Arabic Naskh,Droid Sans "
+     "Ethiopic,Droid Sans Fallback",
      0, 1252},
-    {0x078ed524, L"MicrosoftSansSerif", L"Droid Sans Fallback", 0, 1252},
-    {0x089b18a9, L"Arial", L"Droid Sans Fallback", 0, 1252},
-    {0x0b2cad72, L"MonotypeCorsiva",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x0bb003e7, L"Kartika",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono",
+    {0x078ed524, "MicrosoftSansSerif", "Droid Sans Fallback", 0, 1252},
+    {0x089b18a9, "Arial", "Droid Sans Fallback", 0, 1252},
+    {0x0b2cad72, "MonotypeCorsiva",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0x0bb003e7, "Kartika",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono",
      2, 1252},
-    {0x0bb469df, L"VinerHandITC",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x0bc1a851, L"SegoeUI", L"Droid Sans Fallback", 0, 1252},
-    {0x0c112ebd, L"KozukaGothicPro-VIM",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x0cfcb9c1, L"AdobeThai",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 847},
-    {0x0e7de0f9, L"Playbill",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono",
+    {0x0bb469df, "VinerHandITC",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0x0bc1a851, "SegoeUI", "Droid Sans Fallback", 0, 1252},
+    {0x0c112ebd, "KozukaGothicPro-VIM",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x0cfcb9c1, "AdobeThai",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 847},
+    {0x0e7de0f9, "Playbill",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono",
      0, 1252},
-    {0x0eff47c3, L"STHupo", L"Droid Sans Fallback", 0, 936},
-    {0x107ad374, L"Constantia",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x12194c2d, L"KunstlerScript",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x135ef6a1, L"MinionProSmBd",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x158c4049, L"Garamond",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x160ecb24, L"STZhongsong", L"Droid Sans Fallback", 0, 936},
-    {0x161ed07e, L"MSGothic", L"Droid Sans Fallback", 1, 1252},
-    {0x171d1ed1, L"SnapITC-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x18d1188f, L"Cambria", L"Droid Sans Fallback", 2, 1252},
-    {0x18eaf350, L"ArialUnicodeMS", L"Droid Sans Fallback", 0, 936},
-    {0x1a92d115, L"MingLiU", L"Droid Sans Fallback", 1, 1252},
-    {0x1cc217c6, L"TrebuchetMS",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x1d649596, L"BasemicTimes",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x1e34ee60, L"BellMT",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x1eb36945, L"CooperBlack",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x1ef7787d, L"BatangChe", L"Droid Sans Fallback", 1, 1252},
-    {0x20b3bd3a, L"BrushScriptMT", L"Droid Arabic Naskh,Droid Sans Ethiopic", 8,
+    {0x0eff47c3, "STHupo", "Droid Sans Fallback", 0, 936},
+    {0x107ad374, "Constantia",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x12194c2d, "KunstlerScript",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0x135ef6a1, "MinionProSmBd",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x158c4049, "Garamond",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x160ecb24, "STZhongsong", "Droid Sans Fallback", 0, 936},
+    {0x161ed07e, "MSGothic", "Droid Sans Fallback", 1, 1252},
+    {0x171d1ed1, "SnapITC-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x18d1188f, "Cambria", "Droid Sans Fallback", 2, 1252},
+    {0x18eaf350, "ArialUnicodeMS", "Droid Sans Fallback", 0, 936},
+    {0x1a92d115, "MingLiU", "Droid Sans Fallback", 1, 1252},
+    {0x1cc217c6, "TrebuchetMS",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x1d649596, "BasemicTimes",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x1e34ee60, "BellMT",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x1eb36945, "CooperBlack",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x1ef7787d, "BatangChe", "Droid Sans Fallback", 1, 1252},
+    {0x20b3bd3a, "BrushScriptMT", "Droid Arabic Naskh,Droid Sans Ethiopic", 8,
      1252},
-    {0x220877aa, L"Candara",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x22135007, L"FreestyleScript-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x251059c3, L"Chiller",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0, 1252},
-    {0x25bed6dd, L"MSReferenceSansSerif", L"Droid Sans Fallback", 0, 1252},
-    {0x28154c81, L"Parchment-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x29711eb9, L"STLiti", L"Droid Sans Fallback", 0, 936},
-    {0x2b1993b4, L"Basemic",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x2b316339, L"NiagaraSolid-Reg",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x2c147529, L"FootlightMTLight",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x2c198928, L"HarlowSolid",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x2c6ac6b2, L"LucidaBright",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 2, 1252},
-    {0x2c9f38e2, L"KozukaMinchoPro-VIR",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x2d5a47b0, L"STCaiyun", L"Droid Sans Fallback", 0, 936},
-    {0x2def26bf, L"BernardMT-Condensed",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x2fd8930b, L"KozukaMinchoPr6NR",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x3115525a, L"FangSong_GB2312", L"Droid Sans Fallback", 0, 1252},
-    {0x31327817, L"MyriadPro",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x32244975, L"Helvetica",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0, 1252},
-    {0x32ac995c, L"Terminal",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x338d648a, L"NiagaraEngraved-Reg",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x33bb65f2, L"Sylfaen",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x3402c30e, L"MSPMincho", L"Droid Sans Fallback", 2, 1252},
-    {0x3412bf31, L"SimSun-PUA", L"Droid Sans Fallback", 0, 936},
-    {0x36eb39b9, L"BerlinSansFB",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x36f42055, L"UniversATT", L"Microsoft Sans Serif", 0, 1252},
-    {0x3864c4f6, L"HighTowerText",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x3a257d03, L"FangSong_GB2312", L"Droid Sans Fallback", 0, 1252},
-    {0x3cdae668, L"FreestyleScript",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x3d55aed7, L"Jokerman",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x3d5b4385, L"PMingLiU", L"Droid Sans Fallback", 2, 1252},
-    {0x3d9b7669, L"EstrangeloEdessa",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x3e532d74, L"FranklinGothicMedium",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x3e6aa32d, L"NSimSun", L"Droid Sans Fallback", 1, 936},
-    {0x3f6c36a8, L"Gautami",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono,Droid Sans Fallback",
+    {0x220877aa, "Candara",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x22135007, "FreestyleScript-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0x251059c3, "Chiller",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0, 1252},
+    {0x25bed6dd, "MSReferenceSansSerif", "Droid Sans Fallback", 0, 1252},
+    {0x28154c81, "Parchment-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0x29711eb9, "STLiti", "Droid Sans Fallback", 0, 936},
+    {0x2b1993b4, "Basemic",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x2b316339, "NiagaraSolid-Reg",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x2c147529, "FootlightMTLight",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x2c198928, "HarlowSolid",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x2c6ac6b2, "LucidaBright",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 2, 1252},
+    {0x2c9f38e2, "KozukaMinchoPro-VIR",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x2d5a47b0, "STCaiyun", "Droid Sans Fallback", 0, 936},
+    {0x2def26bf, "BernardMT-Condensed",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x2fd8930b, "KozukaMinchoPr6NR",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x3115525a, "FangSong_GB2312", "Droid Sans Fallback", 0, 1252},
+    {0x31327817, "MyriadPro",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x32244975, "Helvetica",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0, 1252},
+    {0x32ac995c, "Terminal",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x338d648a, "NiagaraEngraved-Reg",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x33bb65f2, "Sylfaen",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x3402c30e, "MSPMincho", "Droid Sans Fallback", 2, 1252},
+    {0x3412bf31, "SimSun-PUA", "Droid Sans Fallback", 0, 936},
+    {0x36eb39b9, "BerlinSansFB",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x36f42055, "UniversATT", "Microsoft Sans Serif", 0, 1252},
+    {0x3864c4f6, "HighTowerText",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x3a257d03, "FangSong_GB2312", "Droid Sans Fallback", 0, 1252},
+    {0x3cdae668, "FreestyleScript",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0x3d55aed7, "Jokerman",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x3d5b4385, "PMingLiU", "Droid Sans Fallback", 2, 1252},
+    {0x3d9b7669, "EstrangeloEdessa",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x3e532d74, "FranklinGothicMedium",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x3e6aa32d, "NSimSun", "Droid Sans Fallback", 1, 936},
+    {0x3f6c36a8, "Gautami",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono,Droid Sans Fallback",
      0, 1252},
-    {0x3ff32662, L"Chiller-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x409de312, L"ModernNo.20",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x41443c5e, L"Georgia",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x4160ade5, L"BellGothicStdBlack",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x421976c4, L"Modern-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x422a7252, L"Stencil",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x42c8554f, L"Fixedsys",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x435cb41d, L"Roman",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x47882383, L"CourierNew", L"Droid Sans Fallback", 1, 1252},
-    {0x480a2338, L"BerlinSansFBDemi",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x480bf7a4, L"CourierStd", L"Droid Sans Fallback", 0, 1252},
-    {0x481ad6ed, L"VladimirScript",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0x4911577a, L"YouYuan", L"Droid Sans Fallback", 1, 936},
-    {0x4a788d72, L"STXingkai", L"Droid Sans Fallback", 0, 936},
-    {0x4bf88566, L"SegoeCondensed",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x4ccf51a4, L"BerlinSansFB-Reg",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x4ea967ce, L"GulimChe", L"Droid Sans Fallback", 1, 1252},
-    {0x4f68bd79, L"LetterGothicStd",
-     L"Droid Sans Mono,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
-     L"Mono,Droid Serif,Droid Sans Fallback",
+    {0x3ff32662, "Chiller-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x409de312, "ModernNo.20",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x41443c5e, "Georgia",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x4160ade5, "BellGothicStdBlack",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x421976c4, "Modern-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x422a7252, "Stencil",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x42c8554f, "Fixedsys",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x435cb41d, "Roman",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x47882383, "CourierNew", "Droid Sans Fallback", 1, 1252},
+    {0x480a2338, "BerlinSansFBDemi",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x480bf7a4, "CourierStd", "Droid Sans Fallback", 0, 1252},
+    {0x481ad6ed, "VladimirScript",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0x4911577a, "YouYuan", "Droid Sans Fallback", 1, 936},
+    {0x4a788d72, "STXingkai", "Droid Sans Fallback", 0, 936},
+    {0x4bf88566, "SegoeCondensed",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x4ccf51a4, "BerlinSansFB-Reg",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x4ea967ce, "GulimChe", "Droid Sans Fallback", 1, 1252},
+    {0x4f68bd79, "LetterGothicStd",
+     "Droid Sans Mono,Droid Arabic Naskh,Droid Sans Ethiopic,Droid Sans "
+     "Mono,Droid Serif,Droid Sans Fallback",
      0, 1252},
-    {0x51a0d0e6, L"KozukaGothicPr6NM",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x531b3dea, L"BasemicSymbol",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x5333fd39, L"CalifornianFB-Reg",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x53561a54, L"FZYTK--GBK1-0", L"Droid Sans Fallback", 0, 936},
-    {0x55e0dde6, L"LucidaSansTypewriter",
-     L"Droid Sans Mono,Droid Arabic Naskh,Droid Sans Ethiopic", 0, 1252},
-    {0x574d4d3d, L"AdobeArabic",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x5792e759, L"STKaiti", L"Droid Sans Fallback", 0, 936},
-    {0x5921978e, L"LucidaSansUnicode", L"Droid Sans Fallback", 0, 1252},
-    {0x594e2da4, L"Vrinda",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono",
+    {0x51a0d0e6, "KozukaGothicPr6NM",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x531b3dea, "BasemicSymbol",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x5333fd39, "CalifornianFB-Reg",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x53561a54, "FZYTK--GBK1-0", "Droid Sans Fallback", 0, 936},
+    {0x55e0dde6, "LucidaSansTypewriter",
+     "Droid Sans Mono,Droid Arabic Naskh,Droid Sans Ethiopic", 0, 1252},
+    {0x574d4d3d, "AdobeArabic",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x5792e759, "STKaiti", "Droid Sans Fallback", 0, 936},
+    {0x5921978e, "LucidaSansUnicode", "Droid Sans Fallback", 0, 1252},
+    {0x594e2da4, "Vrinda",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono",
      0, 1252},
-    {0x59baa9a2, L"KaiTi_GB2312", L"Droid Sans Fallback", 0, 1252},
-    {0x5cfedf4f, L"BaskOldFace",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x5f97921c, L"AdobeMyungjoStdM", L"Droid Sans Fallback", 0, 936},
-    {0x5fefbfad, L"Batang", L"Droid Sans Fallback", 2, 1252},
-    {0x605342b9, L"DotumChe", L"Droid Sans Fallback", 1, 1252},
-    {0x608c5f9a, L"KaiTi_GB2312", L"Droid Sans Fallback", 0, 936},
-    {0x61efd0d1, L"MaturaMTScriptCapitals",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0x59baa9a2, "KaiTi_GB2312", "Droid Sans Fallback", 0, 1252},
+    {0x5cfedf4f, "BaskOldFace",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x5f97921c, "AdobeMyungjoStdM", "Droid Sans Fallback", 0, 936},
+    {0x5fefbfad, "Batang", "Droid Sans Fallback", 2, 1252},
+    {0x605342b9, "DotumChe", "Droid Sans Fallback", 1, 1252},
+    {0x608c5f9a, "KaiTi_GB2312", "Droid Sans Fallback", 0, 936},
+    {0x61efd0d1, "MaturaMTScriptCapitals",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      0, 1252},
-    {0x626608a9, L"MVBoli",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0x626608a9, "MVBoli",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      0, 1252},
-    {0x630501a3, L"SmallFonts",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x65d0e2a9, L"FZYTK--GBK1-0", L"Droid Sans Fallback", 0, 936},
-    {0x669f29e1, L"FZSTK--GBK1-0", L"Droid Sans Fallback", 0, 936},
-    {0x673a9e5f, L"Tunga",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono,Droid Sans Fallback",
+    {0x630501a3, "SmallFonts",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x65d0e2a9, "FZYTK--GBK1-0", "Droid Sans Fallback", 0, 936},
+    {0x669f29e1, "FZSTK--GBK1-0", "Droid Sans Fallback", 0, 936},
+    {0x673a9e5f, "Tunga",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono,Droid Sans Fallback",
      0, 1252},
-    {0x691aa4ce, L"NiagaraSolid",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x696259b7, L"Corbel",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x696ee9be, L"STXihei", L"Droid Sans Fallback", 0, 936},
-    {0x6c59cf69, L"Dotum", L"Droid Sans Fallback", 0, 1252},
-    {0x707fa561, L"Gungsuh", L"Droid Sans Fallback", 2, 1252},
-    {0x71416bb2, L"ZWAdobeF",
-     L"Droid Arabic Naskh,Droid Sans Armenian,Droid Sans Ethiopic,Droid Sans "
-     L"Georgian,Droid Sans Hebrew,Droid Sans Thai",
+    {0x691aa4ce, "NiagaraSolid",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x696259b7, "Corbel",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x696ee9be, "STXihei", "Droid Sans Fallback", 0, 936},
+    {0x6c59cf69, "Dotum", "Droid Sans Fallback", 0, 1252},
+    {0x707fa561, "Gungsuh", "Droid Sans Fallback", 2, 1252},
+    {0x71416bb2, "ZWAdobeF",
+     "Droid Arabic Naskh,Droid Sans Armenian,Droid Sans Ethiopic,Droid Sans "
+     "Georgian,Droid Sans Hebrew,Droid Sans Thai",
      0, 1252},
-    {0x71b41801, L"Verdana",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x73f25e4c, L"PalatinoLinotype", L"Droid Sans Fallback", 0, 1252},
-    {0x73f4d19f, L"NiagaraEngraved",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x74001694, L"MyriadProBlack", L"Book Antiqua,Constantia,Dotum,Georgia", 0,
+    {0x71b41801, "Verdana",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x73f25e4c, "PalatinoLinotype", "Droid Sans Fallback", 0, 1252},
+    {0x73f4d19f, "NiagaraEngraved",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x74001694, "MyriadProBlack", "Book Antiqua,Constantia,Dotum,Georgia", 0,
      1252},
-    {0x74b14d8f, L"Haettenschweiler",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x74cb44ee, L"NSimSun", L"Droid Sans Fallback", 1, 936},
-    {0x76b4d7ff, L"Shruti",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono",
+    {0x74b14d8f, "Haettenschweiler",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x74cb44ee, "NSimSun", "Droid Sans Fallback", 1, 936},
+    {0x76b4d7ff, "Shruti",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono",
      0, 1252},
-    {0x788b3533, L"Webdings",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
-    {0x797dde99, L"MSSerif",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x7a0f9e9e, L"MSMincho", L"Droid Sans Fallback", 1, 1252},
-    {0x7b439caf, L"OldEnglishTextMT",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x8213a433, L"LucidaSans-Typewriter",
-     L"Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 0, 1252},
-    {0x82fec929, L"AdobeSongStdL", L"Droid Sans Fallback", 0, 936},
-    {0x83581825, L"Modern",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x835a2823, L"Algerian",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x83dab9f5, L"Script",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x847b56da, L"Tahoma", L"Droid Sans Fallback", 0, 1252},
-    {0x8a783cb2, L"SimSun-PUA", L"Droid Sans Fallback", 0, 1252},
-    {0x8b5cac0e, L"Onyx",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x8c6a499e, L"Gulim", L"Droid Sans Fallback", 0, 1252},
-    {0x8e0af790, L"JuiceITC",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x8e8d43b2, L"Centaur",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x8ee4dcca, L"BookshelfSymbol7",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x90794800, L"BellGothicStdLight",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x909b516a, L"Century",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x92ae370d, L"MSOutlook",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
-    {0x93c9fbf1, L"LucidaFax",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0x788b3533, "Webdings",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+    {0x797dde99, "MSSerif",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x7a0f9e9e, "MSMincho", "Droid Sans Fallback", 1, 1252},
+    {0x7b439caf, "OldEnglishTextMT",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x8213a433, "LucidaSans-Typewriter",
+     "Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 0, 1252},
+    {0x82fec929, "AdobeSongStd", "Droid Sans Fallback", 0, 936},
+    {0x83581825, "Modern",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x835a2823, "Algerian",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x83dab9f5, "Script",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x847b56da, "Tahoma", "Droid Sans Fallback", 0, 1252},
+    {0x8a783cb2, "SimSun-PUA", "Droid Sans Fallback", 0, 1252},
+    {0x8b5cac0e, "Onyx",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x8c6a499e, "Gulim", "Droid Sans Fallback", 0, 1252},
+    {0x8e0af790, "JuiceITC",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x8e8d43b2, "Centaur",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x8ee4dcca, "BookshelfSymbol7",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x90794800, "BellGothicStdLight",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x909b516a, "Century",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x92ae370d, "MSOutlook",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
+    {0x93c9fbf1, "LucidaFax",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      2, 1252},
-    {0x9565085e, L"BookAntiqua",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0x9856d95d, L"AdobeMingStdL", L"Droid Sans Fallback", 0, 949},
-    {0x9bbadd6b, L"ColonnaMT",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0x9cbd16a4, L"ShowcardGothic-Reg",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0, 1252},
-    {0x9d73008e, L"MSSansSerif",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xa0607db1, L"GungsuhChe", L"Droid Sans Fallback", 1, 1252},
-    {0xa0bcf6a1, L"LatinWide",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0xa1429b36, L"Symbol",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
-    {0xa1fa5abc, L"Wingdings2",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
-    {0xa1fa5abd, L"Wingdings3",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
-    {0xa427bad4, L"InformalRoman-Regular",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic", 8, 1252},
-    {0xa8b92ece, L"FZSTK--GBK1-0", L"Droid Sans Fallback", 0, 936},
-    {0xa8d83ece, L"CalifornianFB",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0xaa3e082c, L"Kingsoft-Phonetic",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xaa6bcabe, L"HarlowSolidItalic",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xade5337c, L"MSUIGothic",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xb08dd941, L"WideLatin",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0x9565085e, "BookAntiqua",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0x9856d95d, "AdobeMingStd", "Droid Sans Fallback", 0, 949},
+    {0x9bbadd6b, "ColonnaMT",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0x9cbd16a4, "ShowcardGothic-Reg",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0, 1252},
+    {0x9d73008e, "MSSansSerif",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xa0607db1, "GungsuhChe", "Droid Sans Fallback", 1, 1252},
+    {0xa0bcf6a1, "LatinWide",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0xa1429b36, "Symbol",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+    {0xa1fa5abc, "Wingdings2",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+    {0xa1fa5abd, "Wingdings3",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+    {0xa427bad4, "InformalRoman-Regular",
+     "Droid Arabic Naskh,Droid Sans Ethiopic", 8, 1252},
+    {0xa8b92ece, "FZSTK--GBK1-0", "Droid Sans Fallback", 0, 936},
+    {0xa8d83ece, "CalifornianFB",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0xaa3e082c, "Kingsoft-Phonetic",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xaa6bcabe, "HarlowSolidItalic",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xade5337c, "MSUIGothic",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xb08dd941, "WideLatin",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      2, 1252},
-    {0xb207f05d, L"PoorRichard",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0xb3bc492f, L"JuiceITC-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xb5545399, L"Marlett",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
-    {0xb5dd1ebb, L"BritannicBold", L"Droid Arabic Naskh,Droid Sans Ethiopic", 0,
+    {0xb207f05d, "PoorRichard",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0xb3bc492f, "JuiceITC-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xb5545399, "Marlett",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
+    {0xb5dd1ebb, "BritannicBold", "Droid Arabic Naskh,Droid Sans Ethiopic", 0,
      1252},
-    {0xb699c1c5, L"LucidaCalligraphy-Italic",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xb725d629, L"TimesNewRoman", L"Droid Sans Fallback", 2, 1252},
-    {0xb7eaebeb, L"AdobeHeitiStdR", L"Droid Sans Fallback", 0, 936},
-    {0xbd29c486, L"BerlinSansFBDemi-Bold",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xbe8a8db4, L"BookshelfSymbolSeven",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xc16c0118, L"AdobeHebrew",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback,Droid Arabic "
-     L"Naskh,Droid Sans Ethiopic",
+    {0xb699c1c5, "LucidaCalligraphy-Italic",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xb725d629, "TimesNewRoman", "Droid Sans Fallback", 2, 1252},
+    {0xb7eaebeb, "AdobeHeitiStdR", "Droid Sans Fallback", 0, 936},
+    {0xbd29c486, "BerlinSansFBDemi-Bold",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xbe8a8db4, "BookshelfSymbolSeven",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xc16c0118, "AdobeHebrew",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback,Droid Arabic "
+     "Naskh,Droid Sans Ethiopic",
      0, 1252},
-    {0xc318b0af, L"MyriadProLight",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xc65e5659, L"CambriaMath", L"Droid Sans Fallback", 2, 1252},
-    {0xc75c8f05, L"LucidaConsole",
-     L"Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 1, 1252},
-    {0xca7c35d6, L"Calibri", L"Droid Sans Fallback", 0, 1252},
-    {0xcb053f53, L"MicrosoftYaHei", L"Droid Sans Fallback", 0, 936},
-    {0xcb7190f9, L"Magneto-Bold",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xcca00cc5, L"System",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xccad6f76, L"Jokerman-Regular",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xccc5818c, L"EuroSign",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xcf3d7234, L"LucidaHandwriting-Italic",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xcf7b8fdb, L"MinionPro",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xcfe5755f, L"Simhei", L"Droid Sans Fallback", 1, 936},
-    {0xd011f4ee, L"MSPGothic", L"Droid Sans Fallback", 0, 1252},
-    {0xd060e7ef, L"Vivaldi",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0xd07edec1, L"FranklinGothic-Medium",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xd107243f, L"SimSun", L"Droid Sans Fallback", 0, 936},
-    {0xd1881562, L"ArialNarrow",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xd22b7dce, L"BodoniMTPosterCompressed",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xd22bfa60, L"ComicSansMS", L"Droid Serif,Roboto,Droid Sans Fallback", 8,
+    {0xc318b0af, "MyriadProLight",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xc65e5659, "CambriaMath", "Droid Sans Fallback", 2, 1252},
+    {0xc75c8f05, "LucidaConsole",
+     "Droid Sans Mono,Droid Serif,Roboto,Droid Sans Fallback", 1, 1252},
+    {0xca7c35d6, "Calibri", "Droid Sans Fallback", 0, 1252},
+    {0xcb053f53, "MicrosoftYaHei", "Droid Sans Fallback", 0, 936},
+    {0xcb7190f9, "Magneto-Bold",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xcca00cc5, "System",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xccad6f76, "Jokerman-Regular",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xccc5818c, "EuroSign",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xcf3d7234, "LucidaHandwriting-Italic",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xcf7b8fdb, "MinionPro",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xcfe5755f, "Simhei", "Droid Sans Fallback", 1, 936},
+    {0xd011f4ee, "MSPGothic", "Droid Sans Fallback", 0, 1252},
+    {0xd060e7ef, "Vivaldi",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0xd07edec1, "FranklinGothic-Medium",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xd107243f, "SimSun", "Droid Sans Fallback", 0, 936},
+    {0xd1881562, "ArialNarrow",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xd22b7dce, "BodoniMTPosterCompressed",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xd22bfa60, "ComicSansMS", "Droid Serif,Roboto,Droid Sans Fallback", 8,
      1252},
-    {0xd3bd0e35, L"Bauhaus93",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xd429ee7a, L"STFangsong", L"Droid Sans Fallback", 0, 936},
-    {0xd6679c12, L"BernardMTCondensed",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xd8e8a027, L"LucidaSans",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0, 1252},
-    {0xd9fe7761, L"HighTowerText-Reg",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
-    {0xda7e551e, L"STSong", L"Droid Sans Fallback", 0, 936},
-    {0xdaa6842d, L"STZhongsong", L"Droid Sans Fallback", 0, 936},
-    {0xdaaab93f, L"STFangsong", L"Droid Sans Fallback", 0, 936},
-    {0xdaeb0713, L"STSong",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 936},
-    {0xdafedbef, L"STCaiyun", L"Droid Sans Fallback", 0, 936},
-    {0xdb00a3d9, L"Broadway",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xdb1f5ad4, L"STXinwei", L"Droid Sans Fallback", 0, 936},
-    {0xdb326e7f, L"STKaiti", L"Droid Sans Fallback", 0, 936},
-    {0xdb69595a, L"STHupo", L"Droid Sans Fallback", 0, 936},
-    {0xdba0082c, L"STXihei", L"Droid Sans Fallback", 0, 936},
-    {0xdbd0ab18, L"STXingkai", L"Droid Sans Fallback", 0, 936},
-    {0xdc1a7db1, L"STLiti", L"Droid Sans Fallback", 0, 936},
-    {0xdc33075f, L"KristenITC-Regular",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8, 1252},
-    {0xdcc7009c, L"Harrington",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xdd712466, L"ArialBlack",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xdde87b3e, L"Impact",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xdf69fb32, L"SnapITC",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0xd3bd0e35, "Bauhaus93",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xd429ee7a, "STFangsong", "Droid Sans Fallback", 0, 936},
+    {0xd6679c12, "BernardMTCondensed",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xd8e8a027, "LucidaSans",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 0, 1252},
+    {0xd9fe7761, "HighTowerText-Reg",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 2, 1252},
+    {0xda7e551e, "STSong", "Droid Sans Fallback", 0, 936},
+    {0xdaa6842d, "STZhongsong", "Droid Sans Fallback", 0, 936},
+    {0xdaaab93f, "STFangsong", "Droid Sans Fallback", 0, 936},
+    {0xdaeb0713, "STSong",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 936},
+    {0xdafedbef, "STCaiyun", "Droid Sans Fallback", 0, 936},
+    {0xdb00a3d9, "Broadway",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xdb1f5ad4, "STXinwei", "Droid Sans Fallback", 0, 936},
+    {0xdb326e7f, "STKaiti", "Droid Sans Fallback", 0, 936},
+    {0xdb69595a, "STHupo", "Droid Sans Fallback", 0, 936},
+    {0xdba0082c, "STXihei", "Droid Sans Fallback", 0, 936},
+    {0xdbd0ab18, "STXingkai", "Droid Sans Fallback", 0, 936},
+    {0xdc1a7db1, "STLiti", "Droid Sans Fallback", 0, 936},
+    {0xdc33075f, "KristenITC-Regular",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8, 1252},
+    {0xdcc7009c, "Harrington",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xdd712466, "ArialBlack",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xdde87b3e, "Impact",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xdf69fb32, "SnapITC",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      0, 1252},
-    {0xdf8b25e8, L"CenturyGothic",
-     L"Droid Serif,Roboto,Droid Serif,Droid Sans Mono", 0, 1252},
-    {0xe0f705c0, L"KristenITC",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8, 1252},
-    {0xe1427573, L"Raavi",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono",
+    {0xdf8b25e8, "CenturyGothic",
+     "Droid Serif,Roboto,Droid Serif,Droid Sans Mono", 0, 1252},
+    {0xe0f705c0, "KristenITC",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto", 8, 1252},
+    {0xe1427573, "Raavi",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono",
      0, 1252},
-    {0xe2cea0cb, L"Magneto",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0xe2cea0cb, "Magneto",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      0, 1252},
-    {0xe36a9e17, L"Ravie",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono",
+    {0xe36a9e17, "Ravie",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono",
      0, 1252},
-    {0xe433f8e2, L"Parchment",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0xe43dff4a, L"Wingdings",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
-    {0xe4e2c405, L"MTExtra",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
-    {0xe618cc35, L"InformalRoman",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 8, 1252},
-    {0xe6c27ffc, L"Mistral",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
-    {0xe7ebf4b9, L"Courier", L"Droid Sans Fallback", 0, 1252},
-    {0xe8bc4a9d, L"MSReferenceSpecialty",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xe90fb013, L"TempusSansITC",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xec637b42, L"Consolas", L"Droid Sans Fallback", 1, 1252},
-    {0xed3a683b, L"STXinwei", L"Droid Sans Fallback", 0, 936},
-    {0xef264cd1, L"LucidaHandwriting",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0xe433f8e2, "Parchment",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0xe43dff4a, "Wingdings",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 4, 42},
+    {0xe4e2c405, "MTExtra",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 6, 42},
+    {0xe618cc35, "InformalRoman",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 8, 1252},
+    {0xe6c27ffc, "Mistral",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 8, 1252},
+    {0xe7ebf4b9, "Courier", "Droid Sans Fallback", 0, 1252},
+    {0xe8bc4a9d, "MSReferenceSpecialty",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xe90fb013, "TempusSansITC",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xec637b42, "Consolas", "Droid Sans Fallback", 1, 1252},
+    {0xed3a683b, "STXinwei", "Droid Sans Fallback", 0, 936},
+    {0xef264cd1, "LucidaHandwriting",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      0, 1252},
-    {0xf086bca2, L"BaskervilleOldFace",
-     L"Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xf1028030, L"Mangal",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0xf086bca2, "BaskervilleOldFace",
+     "Roboto,Droid Serif,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xf1028030, "Mangal",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      2, 1252},
-    {0xf1da7eb9, L"ShowcardGothic",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0, 1252},
-    {0xf210f06a, L"ArialMT",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0, 1252},
-    {0xf477f16a, L"Latha",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
-     L"Mono",
+    {0xf1da7eb9, "ShowcardGothic",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallbac", 0, 1252},
+    {0xf210f06a, "ArialMT",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif", 0, 1252},
+    {0xf477f16a, "Latha",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Roboto,Droid Serif,Droid Sans "
+     "Mono",
      0, 1252},
-    {0xf616f3dd, L"LiSu", L"Droid Sans Fallback", 1, 936},
-    {0xfa479aa6, L"MicrosoftYaHei", L"Droid Sans Fallback", 0, 936},
-    {0xfcd19697, L"BookmanOldStyle",
-     L"Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
-    {0xfe209a82, L"LucidaCalligraphy",
-     L"Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
-     L"Mono",
+    {0xf616f3dd, "LiSu", "Droid Sans Fallback", 1, 936},
+    {0xfa479aa6, "MicrosoftYaHei", "Droid Sans Fallback", 0, 936},
+    {0xfcd19697, "BookmanOldStyle",
+     "Droid Serif,Roboto,Droid Sans Mono,Droid Sans Fallback", 0, 1252},
+    {0xfe209a82, "LucidaCalligraphy",
+     "Droid Arabic Naskh,Droid Sans Ethiopic,Droid Serif,Roboto,Droid Sans "
+     "Mono",
      0, 1252},
-    {0xfef135f8, L"AdobeHeitiStd-Regular", L"Droid Sans Fallback", 0, 936},
+    {0xfef135f8, "AdobeHeitiStd-Regular", "Droid Sans Fallback", 0, 936},
 };
 #endif
 
 }  // namespace
 
-const FGAS_FONTUSB* FGAS_GetUnicodeBitField(wchar_t wUnicode) {
-  int32_t iEnd = sizeof(g_FXGdiFontUSBTable) / sizeof(FGAS_FONTUSB) - 1;
-  ASSERT(iEnd >= 0);
-
-  int32_t iStart = 0;
-  int32_t iMid;
-  do {
-    iMid = (iStart + iEnd) / 2;
-    const FGAS_FONTUSB& usb = g_FXGdiFontUSBTable[iMid];
-    if (wUnicode < usb.wStartUnicode)
-      iEnd = iMid - 1;
-    else if (wUnicode > usb.wEndUnicode)
-      iStart = iMid + 1;
-    else
-      return &usb;
-  } while (iStart <= iEnd);
+const FGAS_FONTUSB* FGAS_GetUnicodeBitField(wchar_t unicode) {
+  // This search is trying to find the entry where the unicode character falls
+  // bewtween start and end. std::upper_bound needs to be used here instead of
+  // lower_bound, because they return the first value that meets the
+  // requirement, as though they are linearly searching. For lower_bound this
+  // means the first element less then the value, and for upper_bound this means
+  // the first element greater then the value. Since the entries are sorted in
+  // ascending order, the correct entry is the first one with an end greater,
+  // aka after, the value.
+  auto* result = std::upper_bound(
+      std::begin(g_FXGdiFontUSBTable), std::end(g_FXGdiFontUSBTable), unicode,
+      [](const wchar_t unicode, const FGAS_FONTUSB& iter) {
+        return iter.wEndUnicode > unicode;
+      });
+  if (result != std::end(g_FXGdiFontUSBTable) &&
+      result->wStartUnicode <= unicode && result->wEndUnicode >= unicode)
+    return result;
   return nullptr;
 }
 
-WideString FGAS_FontNameToEnglishName(const WideStringView& wsLocalName) {
+WideString FGAS_FontNameToEnglishName(WideStringView wsLocalName) {
   uint32_t dwLocalNameHash = FX_HashCode_GetW(wsLocalName, true);
   const FGAS_FontInfo* pEnd = g_XFAFontsMap + FX_ArraySize(g_XFAFontsMap);
   const FGAS_FontInfo* pFontInfo =
@@ -1896,11 +1878,11 @@
                          return entry.dwFontNameHash < hash;
                        });
   if (pFontInfo < pEnd && pFontInfo->dwFontNameHash == dwLocalNameHash)
-    return pFontInfo->pPsName;
+    return WideString::FromASCII(ByteStringView(pFontInfo->pPsName));
   return WideString(wsLocalName);
 }
 
-const FGAS_FontInfo* FGAS_FontInfoByFontName(const WideStringView& wsFontName) {
+const FGAS_FontInfo* FGAS_FontInfoByFontName(WideStringView wsFontName) {
   WideString wsFontNameTemp(wsFontName);
   wsFontNameTemp.Remove(L' ');
   uint32_t dwCurFontNameHash =
diff --git a/xfa/fgas/font/fgas_fontutils.h b/xfa/fgas/font/fgas_fontutils.h
index 3bfd939..91998d5 100644
--- a/xfa/fgas/font/fgas_fontutils.h
+++ b/xfa/fgas/font/fgas_fontutils.h
@@ -19,15 +19,15 @@
 const FGAS_FONTUSB* FGAS_GetUnicodeBitField(wchar_t wUnicode);
 
 struct FGAS_FontInfo {
-  uint32_t dwFontNameHash;
-  const wchar_t* pPsName;
-  const wchar_t* pReplaceFont;
+  uint32_t dwFontNameHash;   // WideString hash.
+  const char* pPsName;       // Raw, POD struct.
+  const char* pReplaceFont;  // Raw, POD struct.
   uint16_t dwStyles;
   uint16_t wCodePage;
 };
 
-WideString FGAS_FontNameToEnglishName(const WideStringView& wsLocalName);
+WideString FGAS_FontNameToEnglishName(WideStringView wsLocalName);
 
-const FGAS_FontInfo* FGAS_FontInfoByFontName(const WideStringView& wsFontName);
+const FGAS_FontInfo* FGAS_FontInfoByFontName(WideStringView wsFontName);
 
 #endif  // XFA_FGAS_FONT_FGAS_FONTUTILS_H_
diff --git a/xfa/fgas/layout/BUILD.gn b/xfa/fgas/layout/BUILD.gn
new file mode 100644
index 0000000..61d2e75
--- /dev/null
+++ b/xfa/fgas/layout/BUILD.gn
@@ -0,0 +1,60 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("layout") {
+  sources = [
+    "cfx_break.cpp",
+    "cfx_break.h",
+    "cfx_breakline.cpp",
+    "cfx_breakline.h",
+    "cfx_breakpiece.cpp",
+    "cfx_breakpiece.h",
+    "cfx_char.cpp",
+    "cfx_char.h",
+    "cfx_linkuserdata.cpp",
+    "cfx_linkuserdata.h",
+    "cfx_rtfbreak.cpp",
+    "cfx_rtfbreak.h",
+    "cfx_textpiece.cpp",
+    "cfx_textpiece.h",
+    "cfx_textuserdata.cpp",
+    "cfx_textuserdata.h",
+    "cfx_txtbreak.cpp",
+    "cfx_txtbreak.h",
+    "fx_arabic.cpp",
+    "fx_arabic.h",
+    "fx_linebreak.cpp",
+    "fx_linebreak.h",
+  ]
+  deps = [
+    "../:fgas",
+    "../../../core/fxcrt",
+    "../../../core/fxcrt/css",
+    "../../../core/fxge",
+  ]
+  configs += [
+    "../../../:pdfium_core_config",
+    "../../:xfa_warnings",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cfx_rtfbreak_unittest.cpp",
+    "cfx_txtbreak_unittest.cpp",
+  ]
+  deps = [
+    ":layout",
+    "../:fgas",
+    "../../../core/fxge",
+    "../../../testing:unit_test_support",
+  ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/xfa/fgas/layout/cfx_break.cpp b/xfa/fgas/layout/cfx_break.cpp
index 0f52a2d..6330a51 100644
--- a/xfa/fgas/layout/cfx_break.cpp
+++ b/xfa/fgas/layout/cfx_break.cpp
@@ -9,44 +9,22 @@
 #include <algorithm>
 #include <vector>
 
+#include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 
-namespace {
-
-const int kMinimumTabWidth = 160000;
-
-}  // namespace
+const float CFX_Break::kConversionFactor = 20000.0f;
+const int CFX_Break::kMinimumTabWidth = 160000;
 
 CFX_Break::CFX_Break(uint32_t dwLayoutStyles)
-    : m_eCharType(FX_CHARTYPE_Unknown),
-      m_bSingleLine(false),
-      m_bCombText(false),
-      m_dwIdentity(0),
-      m_dwLayoutStyles(dwLayoutStyles),
-      m_iLineStart(0),
-      m_iLineWidth(2000000),
-      m_wParagraphBreakChar(L'\n'),
-      m_iFontSize(240),
-      m_iTabWidth(720000),
-      m_iHorizontalScale(100),
-      m_iVerticalScale(100),
-      m_iTolerance(0),
-      m_iCharSpace(0),
-      m_iDefChar(0),
-      m_wDefChar(0xFEFF),
-      m_pFont(nullptr),
-      m_pCurLine(nullptr),
-      m_iReadyLineIndex(-1) {
-  m_pCurLine = &m_Line[0];
-}
+    : m_dwLayoutStyles(dwLayoutStyles), m_pCurLine(&m_Lines[0]) {}
 
-CFX_Break::~CFX_Break() {}
+CFX_Break::~CFX_Break() = default;
 
 void CFX_Break::Reset() {
-  m_eCharType = FX_CHARTYPE_Unknown;
-  m_Line[0].Clear();
-  m_Line[1].Clear();
+  m_eCharType = FX_CHARTYPE::kUnknown;
+  for (CFX_BreakLine& line : m_Lines)
+    line.Clear();
 }
 
 void CFX_Break::SetLayoutStyles(uint32_t dwLayoutStyles) {
@@ -80,61 +58,43 @@
 
   SetBreakStatus();
   m_pFont = pFont;
-  FontChanged();
 }
 
 void CFX_Break::SetFontSize(float fFontSize) {
-  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
+  int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
   if (m_iFontSize == iFontSize)
     return;
 
   SetBreakStatus();
   m_iFontSize = iFontSize;
-  FontChanged();
 }
 
 void CFX_Break::SetBreakStatus() {
   ++m_dwIdentity;
-  int32_t iCount = m_pCurLine->CountChars();
-  if (iCount < 1)
+  if (m_pCurLine->m_LineChars.empty())
     return;
 
-  CFX_Char* tc = m_pCurLine->GetChar(iCount - 1);
+  CFX_Char* tc = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
   if (tc->m_dwStatus == CFX_BreakType::None)
     tc->m_dwStatus = CFX_BreakType::Piece;
 }
 
-FX_CHARTYPE CFX_Break::GetUnifiedCharType(FX_CHARTYPE chartype) const {
-  return chartype >= FX_CHARTYPE_ArabicAlef ? FX_CHARTYPE_Arabic : chartype;
+bool CFX_Break::IsGreaterThanLineWidth(int32_t width) const {
+  FX_SAFE_INT32 line_width = m_iLineWidth;
+  line_width += m_iTolerance;
+  return line_width.IsValid() && width > line_width.ValueOrDie();
 }
 
-void CFX_Break::FontChanged() {
-  m_iDefChar = 0;
-  if (!m_pFont || m_wDefChar == 0xFEFF)
-    return;
-
-  m_pFont->GetCharWidth(m_wDefChar, m_iDefChar);
-  m_iDefChar *= m_iFontSize;
+FX_CHARTYPE CFX_Break::GetUnifiedCharType(FX_CHARTYPE chartype) const {
+  return chartype >= FX_CHARTYPE::kArabicAlef ? FX_CHARTYPE::kArabic : chartype;
 }
 
 void CFX_Break::SetTabWidth(float fTabWidth) {
   // Note, the use of max here was only done in the TxtBreak code. Leaving this
   // in for the RTFBreak code for consistency. If we see issues with tab widths
   // we may need to fix this.
-  m_iTabWidth = std::max(FXSYS_round(fTabWidth * 20000.0f), kMinimumTabWidth);
-}
-
-void CFX_Break::SetDefaultChar(wchar_t wch) {
-  m_wDefChar = wch;
-  m_iDefChar = 0;
-  if (m_wDefChar == 0xFEFF || !m_pFont)
-    return;
-
-  m_pFont->GetCharWidth(m_wDefChar, m_iDefChar);
-  if (m_iDefChar < 0)
-    m_iDefChar = 0;
-  else
-    m_iDefChar *= m_iFontSize;
+  m_iTabWidth =
+      std::max(FXSYS_roundf(fTabWidth * kConversionFactor), kMinimumTabWidth);
 }
 
 void CFX_Break::SetParagraphBreakChar(wchar_t wch) {
@@ -144,19 +104,19 @@
 }
 
 void CFX_Break::SetLineBreakTolerance(float fTolerance) {
-  m_iTolerance = FXSYS_round(fTolerance * 20000.0f);
+  m_iTolerance = FXSYS_roundf(fTolerance * kConversionFactor);
 }
 
 void CFX_Break::SetCharSpace(float fCharSpace) {
-  m_iCharSpace = FXSYS_round(fCharSpace * 20000.0f);
+  m_iCharSpace = FXSYS_roundf(fCharSpace * kConversionFactor);
 }
 
 void CFX_Break::SetLineBoundary(float fLineStart, float fLineEnd) {
   if (fLineStart > fLineEnd)
     return;
 
-  m_iLineStart = FXSYS_round(fLineStart * 20000.0f);
-  m_iLineWidth = FXSYS_round(fLineEnd * 20000.0f);
+  m_iLineStart = FXSYS_roundf(fLineStart * kConversionFactor);
+  m_iLineWidth = FXSYS_roundf(fLineEnd * kConversionFactor);
   m_pCurLine->m_iStart = std::min(m_pCurLine->m_iStart, m_iLineWidth);
   m_pCurLine->m_iStart = std::max(m_pCurLine->m_iStart, m_iLineStart);
 }
@@ -172,7 +132,7 @@
   while (iStart > -1) {
     CFX_Char* pTC = &tca[iStart--];
     if (((bRichText && pTC->m_iCharWidth < 0) || bOmitChar) &&
-        pTC->GetCharType() == FX_CHARTYPE_Combination) {
+        pTC->GetCharType() == FX_CHARTYPE::kCombination) {
       continue;
     }
     if (--index < 0)
@@ -183,20 +143,20 @@
 
 int32_t CFX_Break::CountBreakPieces() const {
   return HasLine() ? pdfium::CollectionSize<int32_t>(
-                         m_Line[m_iReadyLineIndex].m_LinePieces)
+                         m_Lines[m_iReadyLineIndex].m_LinePieces)
                    : 0;
 }
 
 const CFX_BreakPiece* CFX_Break::GetBreakPieceUnstable(int32_t index) const {
   if (!HasLine())
     return nullptr;
-  if (!pdfium::IndexInBounds(m_Line[m_iReadyLineIndex].m_LinePieces, index))
+  if (!pdfium::IndexInBounds(m_Lines[m_iReadyLineIndex].m_LinePieces, index))
     return nullptr;
-  return &m_Line[m_iReadyLineIndex].m_LinePieces[index];
+  return &m_Lines[m_iReadyLineIndex].m_LinePieces[index];
 }
 
 void CFX_Break::ClearBreakPieces() {
   if (HasLine())
-    m_Line[m_iReadyLineIndex].Clear();
+    m_Lines[m_iReadyLineIndex].Clear();
   m_iReadyLineIndex = -1;
 }
diff --git a/xfa/fgas/layout/cfx_break.h b/xfa/fgas/layout/cfx_break.h
index 789220d..4f0dcb3 100644
--- a/xfa/fgas/layout/cfx_break.h
+++ b/xfa/fgas/layout/cfx_break.h
@@ -10,6 +10,7 @@
 #include <stdint.h>
 
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fgas/layout/cfx_breakline.h"
 
 class CFGAS_GEFont;
@@ -50,44 +51,45 @@
 
   void SetCharSpace(float fCharSpace);
   void SetParagraphBreakChar(wchar_t wch);
-  void SetDefaultChar(wchar_t wch);
 
   int32_t CountBreakPieces() const;
   const CFX_BreakPiece* GetBreakPieceUnstable(int32_t index) const;
   void ClearBreakPieces();
 
   CFX_Char* GetLastChar(int32_t index, bool bOmitChar, bool bRichText) const;
+  const CFX_BreakLine* GetCurrentLineForTesting() const {
+    return m_pCurLine.Get();
+  }
 
  protected:
+  static const int kMinimumTabWidth;
+  static const float kConversionFactor;
+
   explicit CFX_Break(uint32_t dwLayoutStyles);
 
   void SetBreakStatus();
   bool HasLine() const { return m_iReadyLineIndex >= 0; }
+  bool IsGreaterThanLineWidth(int32_t width) const;
   FX_CHARTYPE GetUnifiedCharType(FX_CHARTYPE dwType) const;
 
-  FX_CHARTYPE m_eCharType;
-  bool m_bSingleLine;
-  bool m_bCombText;
-  uint32_t m_dwIdentity;
-  uint32_t m_dwLayoutStyles;
-  int32_t m_iLineStart;
-  int32_t m_iLineWidth;
-  wchar_t m_wParagraphBreakChar;
-  int32_t m_iFontSize;
-  int32_t m_iTabWidth;
-  int32_t m_iHorizontalScale;
-  int32_t m_iVerticalScale;
-  int32_t m_iTolerance;
-  int32_t m_iCharSpace;
-  int32_t m_iDefChar;
-  wchar_t m_wDefChar;
+  FX_CHARTYPE m_eCharType = FX_CHARTYPE::kUnknown;
+  bool m_bSingleLine = false;
+  bool m_bCombText = false;
+  uint32_t m_dwIdentity = 0;
+  uint32_t m_dwLayoutStyles = 0;
+  int32_t m_iLineStart = 0;
+  int32_t m_iLineWidth = 2000000;
+  wchar_t m_wParagraphBreakChar = L'\n';
+  int32_t m_iFontSize = 240;
+  int32_t m_iTabWidth = 720000;
+  int32_t m_iHorizontalScale = 100;
+  int32_t m_iVerticalScale = 100;
+  int32_t m_iTolerance = 0;
+  int32_t m_iCharSpace = 0;
   RetainPtr<CFGAS_GEFont> m_pFont;
-  CFX_BreakLine m_Line[2];
-  CFX_BreakLine* m_pCurLine;
-  int8_t m_iReadyLineIndex;
-
- private:
-  void FontChanged();
+  UnownedPtr<CFX_BreakLine> m_pCurLine;
+  int8_t m_iReadyLineIndex = -1;
+  CFX_BreakLine m_Lines[2];
 };
 
 #endif  // XFA_FGAS_LAYOUT_CFX_BREAK_H_
diff --git a/xfa/fgas/layout/cfx_breakline.cpp b/xfa/fgas/layout/cfx_breakline.cpp
index 0788603..02203c9 100644
--- a/xfa/fgas/layout/cfx_breakline.cpp
+++ b/xfa/fgas/layout/cfx_breakline.cpp
@@ -8,33 +8,15 @@
 
 #include "third_party/base/stl_util.h"
 
-CFX_BreakLine::CFX_BreakLine() : m_iStart(0), m_iWidth(0), m_iArabicChars(0) {}
+CFX_BreakLine::CFX_BreakLine() = default;
 
-CFX_BreakLine::~CFX_BreakLine() {}
-
-int32_t CFX_BreakLine::CountChars() const {
-  return pdfium::CollectionSize<int32_t>(m_LineChars);
-}
+CFX_BreakLine::~CFX_BreakLine() = default;
 
 CFX_Char* CFX_BreakLine::GetChar(int32_t index) {
   ASSERT(pdfium::IndexInBounds(m_LineChars, index));
   return &m_LineChars[index];
 }
 
-const CFX_Char* CFX_BreakLine::GetChar(int32_t index) const {
-  ASSERT(pdfium::IndexInBounds(m_LineChars, index));
-  return &m_LineChars[index];
-}
-
-int32_t CFX_BreakLine::CountPieces() const {
-  return pdfium::CollectionSize<int32_t>(m_LinePieces);
-}
-
-const CFX_BreakPiece* CFX_BreakLine::GetPiece(int32_t index) const {
-  ASSERT(index >= 0 && index < CountPieces());
-  return &m_LinePieces[index];
-}
-
 int32_t CFX_BreakLine::GetLineEnd() const {
   return m_iStart + m_iWidth;
 }
@@ -45,3 +27,12 @@
   m_iWidth = 0;
   m_iArabicChars = 0;
 }
+
+void CFX_BreakLine::IncrementArabicCharCount() {
+  ++m_iArabicChars;
+}
+
+void CFX_BreakLine::DecrementArabicCharCount() {
+  ASSERT(m_iArabicChars > 0);
+  --m_iArabicChars;
+}
diff --git a/xfa/fgas/layout/cfx_breakline.h b/xfa/fgas/layout/cfx_breakline.h
index 0b83ee6..c432b35 100644
--- a/xfa/fgas/layout/cfx_breakline.h
+++ b/xfa/fgas/layout/cfx_breakline.h
@@ -9,30 +9,30 @@
 
 #include <vector>
 
-#include "core/fxcrt/cfx_char.h"
 #include "xfa/fgas/layout/cfx_breakpiece.h"
+#include "xfa/fgas/layout/cfx_char.h"
 
 class CFX_BreakLine {
  public:
   CFX_BreakLine();
   ~CFX_BreakLine();
 
-  int32_t CountChars() const;
   CFX_Char* GetChar(int32_t index);
-  const CFX_Char* GetChar(int32_t index) const;
-
-  int32_t CountPieces() const;
-  const CFX_BreakPiece* GetPiece(int32_t index) const;
-
   int32_t GetLineEnd() const;
 
   void Clear();
 
+  void IncrementArabicCharCount();
+  void DecrementArabicCharCount();
+  bool HasArabicChar() const { return m_iArabicChars > 0; }
+
   std::vector<CFX_Char> m_LineChars;
   std::vector<CFX_BreakPiece> m_LinePieces;
-  int32_t m_iStart;
-  int32_t m_iWidth;
-  int32_t m_iArabicChars;
+  int32_t m_iStart = 0;
+  int32_t m_iWidth = 0;
+
+ private:
+  int32_t m_iArabicChars = 0;
 };
 
 #endif  // XFA_FGAS_LAYOUT_CFX_BREAKLINE_H_
diff --git a/xfa/fgas/layout/cfx_breakpiece.cpp b/xfa/fgas/layout/cfx_breakpiece.cpp
index 364c117..b0cc754 100644
--- a/xfa/fgas/layout/cfx_breakpiece.cpp
+++ b/xfa/fgas/layout/cfx_breakpiece.cpp
@@ -6,6 +6,8 @@
 
 #include "xfa/fgas/layout/cfx_breakpiece.h"
 
+#include "xfa/fgas/layout/cfx_textuserdata.h"
+
 CFX_BreakPiece::CFX_BreakPiece()
     : m_dwStatus(CFX_BreakType::Piece),
       m_iStartPos(0),
@@ -30,7 +32,9 @@
 }
 
 CFX_Char* CFX_BreakPiece::GetChar(int32_t index) const {
-  ASSERT(index >= 0 && index < m_iChars && m_pChars);
+  ASSERT(index >= 0);
+  ASSERT(index < m_iChars);
+  ASSERT(m_pChars);
   return &(*m_pChars)[m_iStartChar + index];
 }
 
diff --git a/xfa/fgas/layout/cfx_breakpiece.h b/xfa/fgas/layout/cfx_breakpiece.h
index 5dbc0e7..8ea7032 100644
--- a/xfa/fgas/layout/cfx_breakpiece.h
+++ b/xfa/fgas/layout/cfx_breakpiece.h
@@ -9,11 +9,12 @@
 
 #include <vector>
 
-#include "core/fxcrt/cfx_char.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fxfa/cxfa_textuserdata.h"
+#include "xfa/fgas/layout/cfx_char.h"
+
+class CFX_TextUserData;
 
 class CFX_BreakPiece {
  public:
@@ -41,7 +42,7 @@
   uint32_t m_dwIdentity;
   uint32_t m_dwCharStyles;
   UnownedPtr<std::vector<CFX_Char>> m_pChars;
-  RetainPtr<CXFA_TextUserData> m_pUserData;
+  RetainPtr<CFX_TextUserData> m_pUserData;
 };
 
 #endif  // XFA_FGAS_LAYOUT_CFX_BREAKPIECE_H_
diff --git a/xfa/fgas/layout/cfx_char.cpp b/xfa/fgas/layout/cfx_char.cpp
new file mode 100644
index 0000000..311aff4
--- /dev/null
+++ b/xfa/fgas/layout/cfx_char.cpp
@@ -0,0 +1,565 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/cfx_char.h"
+
+#include <algorithm>
+
+#include "core/fxcrt/fx_extension.h"
+
+namespace {
+
+#ifndef NDEBUG
+constexpr int32_t kBidiMaxLevel = 61;
+#endif  // NDEBUG
+
+#undef PACK_NIBBLES
+#define PACK_NIBBLES(hi, lo) \
+  ((static_cast<uint32_t>(hi) << 4) + static_cast<uint32_t>(lo))
+
+enum FX_BIDIWEAKSTATE : uint8_t {
+  FX_BWSxa = 0,
+  FX_BWSxr,
+  FX_BWSxl,
+  FX_BWSao,
+  FX_BWSro,
+  FX_BWSlo,
+  FX_BWSrt,
+  FX_BWSlt,
+  FX_BWScn,
+  FX_BWSra,
+  FX_BWSre,
+  FX_BWSla,
+  FX_BWSle,
+  FX_BWSac,
+  FX_BWSrc,
+  FX_BWSrs,
+  FX_BWSlc,
+  FX_BWSls,
+  FX_BWSret,
+  FX_BWSlet
+};
+
+// NOTE: Range of FX_BIDICLASS prevents encoding all possible values in this
+// manner, but the ones used manage to fit. Except that I suspect that 0xF
+// was intended to be used as a sentinel, even though it also means kRLE.
+// TODO(tsepez): pick a better representation.
+enum FX_BIDIWEAKACTION : uint16_t {
+  FX_BWAIX = 0x100,
+  FX_BWAXX = 0x0F,
+  FX_BWAxxx = 0xFF,
+  FX_BWAxIx = 0x100 + FX_BWAxxx,
+  FX_BWAxxN = PACK_NIBBLES(0x0F, FX_BIDICLASS::kON),
+  FX_BWAxxE = PACK_NIBBLES(0x0F, FX_BIDICLASS::kEN),
+  FX_BWAxxA = PACK_NIBBLES(0x0F, FX_BIDICLASS::kAN),
+  FX_BWAxxR = PACK_NIBBLES(0x0F, FX_BIDICLASS::kR),
+  FX_BWAxxL = PACK_NIBBLES(0x0F, FX_BIDICLASS::kL),
+  FX_BWANxx = PACK_NIBBLES(FX_BIDICLASS::kON, 0x0F),
+  FX_BWAAxx = PACK_NIBBLES(FX_BIDICLASS::kAN, 0x0F),
+  FX_BWAExE = PACK_NIBBLES(FX_BIDICLASS::kEN, FX_BIDICLASS::kEN),
+  FX_BWANIx = 0x100 + PACK_NIBBLES(FX_BIDICLASS::kON, 0x0F),
+  FX_BWANxN = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kON),
+  FX_BWANxR = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kR),
+  FX_BWANxE = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kEN),
+  FX_BWAAxA = PACK_NIBBLES(FX_BIDICLASS::kAN, FX_BIDICLASS::kAN),
+  FX_BWANxL = PACK_NIBBLES(FX_BIDICLASS::kON, FX_BIDICLASS::kL),
+  FX_BWALxL = PACK_NIBBLES(FX_BIDICLASS::kL, FX_BIDICLASS::kL),
+  FX_BWAxIL = 0x100 + PACK_NIBBLES(0x0F, FX_BIDICLASS::kL),
+  FX_BWAAxR = PACK_NIBBLES(FX_BIDICLASS::kAN, FX_BIDICLASS::kR),
+  FX_BWALxx = PACK_NIBBLES(FX_BIDICLASS::kL, 0x0F),
+};
+
+enum FX_BIDINEUTRALSTATE : uint8_t {
+  FX_BNSr = 0,
+  FX_BNSl,
+  FX_BNSrn,
+  FX_BNSln,
+  FX_BNSa,
+  FX_BNSna
+};
+
+enum FX_BIDINEUTRALACTION : uint16_t {
+  // For placeholders in table.
+  FX_BNAZero = 0,
+
+  // Other values.
+  FX_BNAnL = PACK_NIBBLES(0, FX_BIDICLASS::kL),
+  FX_BNAEn = PACK_NIBBLES(FX_BIDICLASS::kAN, 0),
+  FX_BNARn = PACK_NIBBLES(FX_BIDICLASS::kR, 0),
+  FX_BNALn = PACK_NIBBLES(FX_BIDICLASS::kL, 0),
+  FX_BNAIn = FX_BWAIX,
+  FX_BNALnL = PACK_NIBBLES(FX_BIDICLASS::kL, FX_BIDICLASS::kL),
+};
+#undef PACK_NIBBLES
+
+const FX_BIDICLASS kNTypes[] = {
+    FX_BIDICLASS::kN,   FX_BIDICLASS::kL,   FX_BIDICLASS::kR,
+    FX_BIDICLASS::kAN,  FX_BIDICLASS::kEN,  FX_BIDICLASS::kAL,
+    FX_BIDICLASS::kNSM, FX_BIDICLASS::kCS,  FX_BIDICLASS::kES,
+    FX_BIDICLASS::kET,  FX_BIDICLASS::kBN,  FX_BIDICLASS::kBN,
+    FX_BIDICLASS::kN,   FX_BIDICLASS::kB,   FX_BIDICLASS::kRLO,
+    FX_BIDICLASS::kRLE, FX_BIDICLASS::kLRO, FX_BIDICLASS::kLRE,
+    FX_BIDICLASS::kPDF, FX_BIDICLASS::kON,
+};
+
+const FX_BIDIWEAKSTATE kWeakStates[20][10] = {
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSxa,
+     FX_BWSao, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSxr,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSxl,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
+     FX_BWSao, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSrt,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlt,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWScn,
+     FX_BWSac, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSra,
+     FX_BWSrc, FX_BWSro, FX_BWSrt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSre,
+     FX_BWSrs, FX_BWSrs, FX_BWSret},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSla,
+     FX_BWSlc, FX_BWSlo, FX_BWSlt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSle,
+     FX_BWSls, FX_BWSls, FX_BWSlet},
+    {FX_BWSao, FX_BWSxl, FX_BWSxr, FX_BWScn, FX_BWScn, FX_BWSxa, FX_BWSao,
+     FX_BWSao, FX_BWSao, FX_BWSao},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSro,
+     FX_BWSro, FX_BWSro, FX_BWSrt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlo,
+     FX_BWSlo, FX_BWSlo, FX_BWSlt},
+    {FX_BWSro, FX_BWSxl, FX_BWSxr, FX_BWSra, FX_BWSre, FX_BWSxa, FX_BWSret,
+     FX_BWSro, FX_BWSro, FX_BWSret},
+    {FX_BWSlo, FX_BWSxl, FX_BWSxr, FX_BWSla, FX_BWSle, FX_BWSxa, FX_BWSlet,
+     FX_BWSlo, FX_BWSlo, FX_BWSlet},
+};
+
+const FX_BIDIWEAKACTION kWeakActions[20][10] = {
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
+     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxR, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
+     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxxN},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxN, FX_BWAxxN, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
+     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
+     FX_BWAxIx, FX_BWANxN, FX_BWANxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxA, FX_BWAxxR,
+     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxxN},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxE, FX_BWAxIx, FX_BWAxIx, FX_BWAxxE},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxA, FX_BWAxIx, FX_BWAxxN, FX_BWAxIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxL, FX_BWAxIx, FX_BWAxIx, FX_BWAxxL},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWAAxA, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANxN},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxE, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAExE, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWAAxx, FX_BWANxL, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWANxx, FX_BWALxL, FX_BWANxR,
+     FX_BWANxN, FX_BWANxN, FX_BWANxN, FX_BWANIx},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxE, FX_BWAxxR,
+     FX_BWAxxE, FX_BWAxxN, FX_BWAxxN, FX_BWAxxE},
+    {FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxx, FX_BWAxxL, FX_BWAxxR,
+     FX_BWAxxL, FX_BWAxxN, FX_BWAxxN, FX_BWAxxL},
+};
+
+const FX_BIDINEUTRALSTATE kNeutralStates[6][5] = {
+    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
+    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+    {FX_BNSrn, FX_BNSl, FX_BNSr, FX_BNSr, FX_BNSr},
+    {FX_BNSln, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+    {FX_BNSna, FX_BNSl, FX_BNSr, FX_BNSa, FX_BNSl},
+};
+
+const FX_BIDINEUTRALACTION kNeutralActions[6][5] = {
+    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAZero},
+    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAnL},
+    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNARn},
+    {FX_BNAIn, FX_BNALn, FX_BNAEn, FX_BNAEn, FX_BNALnL},
+    {FX_BNAIn, FX_BNAZero, FX_BNAZero, FX_BNAZero, FX_BNAnL},
+    {FX_BNAIn, FX_BNAEn, FX_BNARn, FX_BNARn, FX_BNAEn},
+};
+
+const uint8_t kAddLevel[2][4] = {
+    {0, 1, 2, 2},
+    {1, 0, 1, 1},
+};
+
+FX_BIDICLASS Direction(int32_t val) {
+  return FX_IsOdd(val) ? FX_BIDICLASS::kR : FX_BIDICLASS::kL;
+}
+
+FX_BIDICLASS GetDeferredType(int32_t val) {
+  return static_cast<FX_BIDICLASS>((val >> 4) & 0x0F);
+}
+
+FX_BIDICLASS GetResolvedType(int32_t val) {
+  return static_cast<FX_BIDICLASS>(val & 0x0F);
+}
+
+FX_BIDICLASS GetDeferredNeutrals(int32_t iAction, int32_t iLevel) {
+  FX_BIDICLASS eClass = GetDeferredType(iAction);
+  return eClass == FX_BIDICLASS::kAN ? Direction(iLevel) : eClass;
+}
+
+FX_BIDICLASS GetResolvedNeutrals(int32_t iAction) {
+  return GetResolvedType(iAction);
+}
+
+FX_BIDIWEAKSTATE GetWeakState(FX_BIDIWEAKSTATE eState, FX_BIDICLASS eClass) {
+  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kWeakStates));
+  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kWeakStates[0]));
+  return kWeakStates[static_cast<size_t>(eState)][static_cast<size_t>(eClass)];
+}
+
+FX_BIDIWEAKACTION GetWeakAction(FX_BIDIWEAKSTATE eState, FX_BIDICLASS eClass) {
+  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kWeakActions));
+  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kWeakActions[0]));
+  return kWeakActions[static_cast<size_t>(eState)][static_cast<size_t>(eClass)];
+}
+
+FX_BIDINEUTRALSTATE GetNeutralState(FX_BIDINEUTRALSTATE eState,
+                                    FX_BIDICLASS eClass) {
+  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kNeutralStates));
+  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kNeutralStates[0]));
+  return kNeutralStates[static_cast<size_t>(eState)]
+                       [static_cast<size_t>(eClass)];
+}
+
+FX_BIDINEUTRALACTION GetNeutralAction(FX_BIDINEUTRALSTATE eState,
+                                      FX_BIDICLASS eClass) {
+  ASSERT(static_cast<size_t>(eState) < FX_ArraySize(kNeutralActions));
+  ASSERT(static_cast<size_t>(eClass) < FX_ArraySize(kNeutralActions[0]));
+  return kNeutralActions[static_cast<size_t>(eState)]
+                        [static_cast<size_t>(eClass)];
+}
+
+void ReverseString(std::vector<CFX_Char>* chars, size_t iStart, size_t iCount) {
+  ASSERT(pdfium::IndexInBounds(*chars, iStart));
+  ASSERT(iStart + iCount <= chars->size());
+
+  std::reverse(chars->begin() + iStart, chars->begin() + iStart + iCount);
+}
+
+void SetDeferredRunClass(std::vector<CFX_Char>* chars,
+                         size_t iStart,
+                         size_t iCount,
+                         FX_BIDICLASS eValue) {
+  ASSERT(iStart <= chars->size());
+  ASSERT(iStart >= iCount);
+
+  size_t iLast = iStart - iCount;
+  for (size_t i = iStart; i > iLast; --i)
+    (*chars)[i - 1].m_iBidiClass = eValue;
+}
+
+void SetDeferredRunLevel(std::vector<CFX_Char>* chars,
+                         size_t iStart,
+                         size_t iCount,
+                         int32_t iValue) {
+  ASSERT(iStart <= chars->size());
+  ASSERT(iStart >= iCount);
+
+  size_t iLast = iStart - iCount;
+  for (size_t i = iStart; i > iLast; --i)
+    (*chars)[i - 1].m_iBidiLevel = static_cast<int16_t>(iValue);
+}
+
+void Classify(std::vector<CFX_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    CFX_Char& cur = (*chars)[i];
+    cur.m_iBidiClass = FX_GetBidiClass(cur.char_code());
+  }
+}
+
+void ClassifyWithTransform(std::vector<CFX_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    CFX_Char& cur = (*chars)[i];
+    cur.m_iBidiClass =
+        kNTypes[static_cast<size_t>(FX_GetBidiClass(cur.char_code()))];
+  }
+}
+
+void ResolveExplicit(std::vector<CFX_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i)
+    (*chars)[i].m_iBidiLevel = 0;
+}
+
+void ResolveWeak(std::vector<CFX_Char>* chars, size_t iCount) {
+  if (iCount <= 1)
+    return;
+  --iCount;
+
+  int32_t iLevelCur = 0;
+  size_t iNum = 0;
+  FX_BIDIWEAKSTATE eState = FX_BWSxl;
+  FX_BIDICLASS eClsCur;
+  FX_BIDICLASS eClsRun;
+  FX_BIDICLASS eClsNew;
+  size_t i = 0;
+  for (; i <= iCount; ++i) {
+    CFX_Char* pTC = &(*chars)[i];
+    eClsCur = pTC->m_iBidiClass;
+    if (eClsCur == FX_BIDICLASS::kBN) {
+      pTC->m_iBidiLevel = (int16_t)iLevelCur;
+      if (i == iCount && iLevelCur != 0) {
+        eClsCur = Direction(iLevelCur);
+        pTC->m_iBidiClass = eClsCur;
+      } else if (i < iCount) {
+        CFX_Char* pTCNext = &(*chars)[i + 1];
+        int32_t iLevelNext, iLevelNew;
+        eClsNew = pTCNext->m_iBidiClass;
+        iLevelNext = pTCNext->m_iBidiLevel;
+        if (eClsNew != FX_BIDICLASS::kBN && iLevelCur != iLevelNext) {
+          iLevelNew = std::max(iLevelNext, iLevelCur);
+          pTC->m_iBidiLevel = static_cast<int16_t>(iLevelNew);
+          eClsCur = Direction(iLevelNew);
+          pTC->m_iBidiClass = eClsCur;
+          iLevelCur = iLevelNext;
+        } else {
+          if (iNum > 0)
+            ++iNum;
+          continue;
+        }
+      } else {
+        if (iNum > 0)
+          ++iNum;
+        continue;
+      }
+    }
+    if (eClsCur > FX_BIDICLASS::kBN)
+      continue;
+
+    FX_BIDIWEAKACTION eAction = GetWeakAction(eState, eClsCur);
+    eClsRun = GetDeferredType(eAction);
+    if (eClsRun != static_cast<FX_BIDICLASS>(0xF) && iNum > 0) {
+      SetDeferredRunClass(chars, i, iNum, eClsRun);
+      iNum = 0;
+    }
+    eClsNew = GetResolvedType(eAction);
+    if (eClsNew != static_cast<FX_BIDICLASS>(0xF))
+      pTC->m_iBidiClass = eClsNew;
+    if (FX_BWAIX & eAction)
+      ++iNum;
+
+    eState = GetWeakState(eState, eClsCur);
+  }
+  if (iNum == 0)
+    return;
+
+  eClsCur = Direction(0);
+  eClsRun = GetDeferredType(GetWeakAction(eState, eClsCur));
+  if (eClsRun != static_cast<FX_BIDICLASS>(0xF))
+    SetDeferredRunClass(chars, i, iNum, eClsRun);
+}
+
+void ResolveNeutrals(std::vector<CFX_Char>* chars, size_t iCount) {
+  if (iCount <= 1)
+    return;
+  --iCount;
+
+  CFX_Char* pTC;
+  int32_t iLevel = 0;
+  size_t i = 0;
+  size_t iNum = 0;
+  FX_BIDINEUTRALSTATE eState = FX_BNSl;
+  FX_BIDICLASS eClsCur;
+  FX_BIDICLASS eClsRun;
+  FX_BIDICLASS eClsNew;
+  for (; i <= iCount; ++i) {
+    pTC = &(*chars)[i];
+    eClsCur = pTC->m_iBidiClass;
+    if (eClsCur == FX_BIDICLASS::kBN) {
+      if (iNum)
+        ++iNum;
+      continue;
+    }
+    if (eClsCur >= FX_BIDICLASS::kAL)
+      continue;
+
+    FX_BIDINEUTRALACTION eAction = GetNeutralAction(eState, eClsCur);
+    eClsRun = GetDeferredNeutrals(eAction, iLevel);
+    if (eClsRun != FX_BIDICLASS::kN && iNum > 0) {
+      SetDeferredRunClass(chars, i, iNum, eClsRun);
+      iNum = 0;
+    }
+
+    eClsNew = GetResolvedNeutrals(eAction);
+    if (eClsNew != FX_BIDICLASS::kN)
+      pTC->m_iBidiClass = eClsNew;
+    if (FX_BNAIn & eAction)
+      ++iNum;
+
+    eState = GetNeutralState(eState, eClsCur);
+    iLevel = pTC->m_iBidiLevel;
+  }
+  if (iNum == 0)
+    return;
+
+  eClsCur = Direction(iLevel);
+  eClsRun = GetDeferredNeutrals(GetNeutralAction(eState, eClsCur), iLevel);
+  if (eClsRun != FX_BIDICLASS::kN)
+    SetDeferredRunClass(chars, i, iNum, eClsRun);
+}
+
+void ResolveImplicit(std::vector<CFX_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    FX_BIDICLASS eCls = (*chars)[i].m_iBidiClass;
+    if (eCls == FX_BIDICLASS::kBN || eCls <= FX_BIDICLASS::kON ||
+        eCls >= FX_BIDICLASS::kAL) {
+      continue;
+    }
+    (*chars)[i].m_iBidiLevel += kAddLevel[FX_IsOdd((*chars)[i].m_iBidiLevel)]
+                                         [static_cast<size_t>(eCls) - 1];
+  }
+}
+
+void ResolveWhitespace(std::vector<CFX_Char>* chars, size_t iCount) {
+  if (iCount <= 1)
+    return;
+  iCount--;
+
+  int32_t iLevel = 0;
+  size_t i = 0;
+  size_t iNum = 0;
+  for (; i <= iCount; ++i) {
+    switch (static_cast<FX_BIDICLASS>((*chars)[i].m_iBidiClass)) {
+      case FX_BIDICLASS::kWS:
+        ++iNum;
+        break;
+      case FX_BIDICLASS::kRLE:
+      case FX_BIDICLASS::kLRE:
+      case FX_BIDICLASS::kLRO:
+      case FX_BIDICLASS::kRLO:
+      case FX_BIDICLASS::kPDF:
+      case FX_BIDICLASS::kBN:
+        (*chars)[i].m_iBidiLevel = static_cast<int16_t>(iLevel);
+        ++iNum;
+        break;
+      case FX_BIDICLASS::kS:
+      case FX_BIDICLASS::kB:
+        if (iNum > 0)
+          SetDeferredRunLevel(chars, i, iNum, 0);
+
+        (*chars)[i].m_iBidiLevel = 0;
+        iNum = 0;
+        break;
+      default:
+        iNum = 0;
+        break;
+    }
+    iLevel = (*chars)[i].m_iBidiLevel;
+  }
+  if (iNum > 0)
+    SetDeferredRunLevel(chars, i, iNum, 0);
+}
+
+size_t ReorderLevel(std::vector<CFX_Char>* chars,
+                    size_t iCount,
+                    int32_t iBaseLevel,
+                    size_t iStart,
+                    bool bReverse) {
+  ASSERT(iBaseLevel >= 0);
+  ASSERT(iBaseLevel <= kBidiMaxLevel);
+  ASSERT(iStart < iCount);
+
+  if (iCount < 1)
+    return 0;
+
+  bReverse = bReverse || FX_IsOdd(iBaseLevel);
+  size_t i = iStart;
+  for (; i < iCount; ++i) {
+    int32_t iLevel = (*chars)[i].m_iBidiLevel;
+    if (iLevel == iBaseLevel)
+      continue;
+    if (iLevel < iBaseLevel)
+      break;
+
+    i += ReorderLevel(chars, iCount, iBaseLevel + 1, i, bReverse) - 1;
+  }
+
+  size_t iNum = i - iStart;
+  if (bReverse && iNum > 1)
+    ReverseString(chars, iStart, iNum);
+
+  return iNum;
+}
+
+void Reorder(std::vector<CFX_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount;)
+    i += ReorderLevel(chars, iCount, 0, i, false);
+}
+
+void Position(std::vector<CFX_Char>* chars, size_t iCount) {
+  for (size_t i = 0; i < iCount; ++i) {
+    if ((*chars)[i].m_iBidiPos > iCount)
+      continue;
+
+    (*chars)[(*chars)[i].m_iBidiPos].m_iBidiOrder = i;
+  }
+}
+
+}  // namespace
+
+// static
+void CFX_Char::BidiLine(std::vector<CFX_Char>* chars, size_t iCount) {
+  ASSERT(iCount <= chars->size());
+  if (iCount < 2)
+    return;
+
+  ClassifyWithTransform(chars, iCount);
+  ResolveExplicit(chars, iCount);
+  ResolveWeak(chars, iCount);
+  ResolveNeutrals(chars, iCount);
+  ResolveImplicit(chars, iCount);
+  Classify(chars, iCount);
+  ResolveWhitespace(chars, iCount);
+  Reorder(chars, iCount);
+  Position(chars, iCount);
+}
+
+CFX_Char::CFX_Char(uint16_t wCharCode) : CFX_Char(wCharCode, 100, 100) {}
+
+CFX_Char::CFX_Char(uint16_t wCharCode,
+                   int32_t iHorizontalScale,
+                   int32_t iVerticalScale)
+    : m_wCharCode(wCharCode),
+      m_iHorizontalScale(iHorizontalScale),
+      m_iVerticalScale(iVerticalScale) {}
+
+CFX_Char::CFX_Char(const CFX_Char& other) = default;
+
+CFX_Char::~CFX_Char() = default;
+
+FX_CHARTYPE CFX_Char::GetCharType() const {
+  return FX_GetCharType(m_wCharCode);
+}
diff --git a/xfa/fgas/layout/cfx_char.h b/xfa/fgas/layout/cfx_char.h
new file mode 100644
index 0000000..c4bafcc
--- /dev/null
+++ b/xfa/fgas/layout/cfx_char.h
@@ -0,0 +1,56 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFX_CHAR_H_
+#define XFA_FGAS_LAYOUT_CFX_CHAR_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "core/fxcrt/fx_unicode.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "xfa/fgas/layout/cfx_textuserdata.h"
+#include "xfa/fgas/layout/fx_linebreak.h"
+
+enum class CFX_BreakType : uint8_t { None = 0, Piece, Line, Paragraph, Page };
+
+class CFX_Char {
+ public:
+  static void BidiLine(std::vector<CFX_Char>* chars, size_t iCount);
+
+  explicit CFX_Char(uint16_t wCharCode);
+  CFX_Char(uint16_t wCharCode,
+           int32_t iHorizontalScale,
+           int32_t iVerticalScale);
+  CFX_Char(const CFX_Char& other);
+  ~CFX_Char();
+
+  FX_CHARTYPE GetCharType() const;
+
+  uint16_t char_code() const { return m_wCharCode; }
+  int16_t horizonal_scale() const { return m_iHorizontalScale; }
+  int16_t vertical_scale() const { return m_iVerticalScale; }
+
+  CFX_BreakType m_dwStatus = CFX_BreakType::None;
+  FX_BIDICLASS m_iBidiClass = FX_BIDICLASS::kON;
+  FX_LINEBREAKTYPE m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
+  uint32_t m_dwCharStyles = 0;
+  int32_t m_iCharWidth = 0;
+  uint16_t m_iBidiLevel = 0;
+  uint16_t m_iBidiPos = 0;
+  uint16_t m_iBidiOrder = 0;
+  int32_t m_iFontSize = 0;
+  uint32_t m_dwIdentity = 0;
+  RetainPtr<CFX_TextUserData> m_pUserData;
+
+ private:
+  uint16_t m_wCharCode;
+  int32_t m_iHorizontalScale;
+  int32_t m_iVerticalScale;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFX_CHAR_H_
diff --git a/xfa/fgas/layout/cfx_linebreak.cpp b/xfa/fgas/layout/cfx_linebreak.cpp
deleted file mode 100644
index 1435248..0000000
--- a/xfa/fgas/layout/cfx_linebreak.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fgas/layout/cfx_linebreak.h"
-
-#include "core/fxcrt/fx_unicode.h"
-
-const FX_LINEBREAKTYPE gs_FX_LineBreak_PairTable[64][32] = {
-    {FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBCP, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBPB, FX_LBPB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBPB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBPB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBIB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBDB, FX_LBPB,
-     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBPB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
-     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
-     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
-     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
-     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
-};
diff --git a/xfa/fgas/layout/cfx_linebreak.h b/xfa/fgas/layout/cfx_linebreak.h
deleted file mode 100644
index 8ba022e..0000000
--- a/xfa/fgas/layout/cfx_linebreak.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FGAS_LAYOUT_CFX_LINEBREAK_H_
-#define XFA_FGAS_LAYOUT_CFX_LINEBREAK_H_
-
-#include "core/fxcrt/fx_system.h"
-
-enum FX_LINEBREAKTYPE : uint8_t {
-  FX_LBT_UNKNOWN = 0x00,
-  FX_LBT_DIRECT_BRK = 0x1A,
-  FX_LBT_INDIRECT_BRK = 0x2B,
-  FX_LBT_COM_INDIRECT_BRK = 0x3C,
-  FX_LBT_COM_PROHIBITED_BRK = 0x4D,
-  FX_LBT_PROHIBITED_BRK = 0x5E,
-  FX_LBT_HANGUL_SPACE_BRK = 0x6F,
-};
-
-#define FX_LBUN FX_LBT_UNKNOWN
-#define FX_LBDB FX_LBT_DIRECT_BRK
-#define FX_LBIB FX_LBT_INDIRECT_BRK
-#define FX_LBCB FX_LBT_COM_INDIRECT_BRK
-#define FX_LBCP FX_LBT_COM_PROHIBITED_BRK
-#define FX_LBPB FX_LBT_PROHIBITED_BRK
-#define FX_LBHS FX_LBT_HANGUL_SPACE_BRK
-
-extern const FX_LINEBREAKTYPE gs_FX_LineBreak_PairTable[64][32];
-
-#endif  // XFA_FGAS_LAYOUT_CFX_LINEBREAK_H_
diff --git a/xfa/fgas/layout/cfx_linkuserdata.cpp b/xfa/fgas/layout/cfx_linkuserdata.cpp
new file mode 100644
index 0000000..d452480
--- /dev/null
+++ b/xfa/fgas/layout/cfx_linkuserdata.cpp
@@ -0,0 +1,12 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/cfx_linkuserdata.h"
+
+CFX_LinkUserData::CFX_LinkUserData(const WideString& wsText)
+    : m_wsURLContent(wsText) {}
+
+CFX_LinkUserData::~CFX_LinkUserData() {}
diff --git a/xfa/fgas/layout/cfx_linkuserdata.h b/xfa/fgas/layout/cfx_linkuserdata.h
new file mode 100644
index 0000000..83b43a2
--- /dev/null
+++ b/xfa/fgas/layout/cfx_linkuserdata.h
@@ -0,0 +1,28 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFX_LINKUSERDATA_H_
+#define XFA_FGAS_LAYOUT_CFX_LINKUSERDATA_H_
+
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_LinkUserData final : public Retainable {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  const wchar_t* GetLinkURL() const { return m_wsURLContent.c_str(); }
+
+ private:
+  explicit CFX_LinkUserData(const WideString& wsText);
+  ~CFX_LinkUserData() override;
+
+  WideString m_wsURLContent;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFX_LINKUSERDATA_H_
diff --git a/xfa/fgas/layout/cfx_rtfbreak.cpp b/xfa/fgas/layout/cfx_rtfbreak.cpp
index 3ef0ef2..78d723b 100644
--- a/xfa/fgas/layout/cfx_rtfbreak.cpp
+++ b/xfa/fgas/layout/cfx_rtfbreak.cpp
@@ -8,12 +8,17 @@
 
 #include <algorithm>
 
-#include "core/fxcrt/fx_arabic.h"
-#include "core/fxcrt/fx_bidi.h"
-#include "core/fxge/cfx_renderdevice.h"
+#include "build/build_config.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/numerics/safe_math.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_linebreak.h"
+#include "xfa/fgas/layout/cfx_char.h"
+#include "xfa/fgas/layout/cfx_textpiece.h"
+#include "xfa/fgas/layout/cfx_textuserdata.h"
+#include "xfa/fgas/layout/fx_arabic.h"
+#include "xfa/fgas/layout/fx_linebreak.h"
 
 CFX_RTFBreak::CFX_RTFBreak(uint32_t dwLayoutStyles)
     : CFX_Break(dwLayoutStyles),
@@ -26,15 +31,15 @@
 CFX_RTFBreak::~CFX_RTFBreak() {}
 
 void CFX_RTFBreak::SetLineStartPos(float fLinePos) {
-  int32_t iLinePos = FXSYS_round(fLinePos * 20000.0f);
+  int32_t iLinePos = FXSYS_roundf(fLinePos * kConversionFactor);
   iLinePos = std::min(iLinePos, m_iLineWidth);
   iLinePos = std::max(iLinePos, m_iLineStart);
   m_pCurLine->m_iStart = iLinePos;
 }
 
 void CFX_RTFBreak::AddPositionedTab(float fTabPos) {
-  int32_t iTabPos =
-      std::min(FXSYS_round(fTabPos * 20000.0f) + m_iLineStart, m_iLineWidth);
+  int32_t iTabPos = std::min(
+      FXSYS_roundf(fTabPos * kConversionFactor) + m_iLineStart, m_iLineWidth);
   auto it = std::lower_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
                              iTabPos);
   if (it != m_PositionedTabs.end() && *it == iTabPos)
@@ -42,7 +47,7 @@
   m_PositionedTabs.insert(it, iTabPos);
 }
 
-void CFX_RTFBreak::SetUserData(const RetainPtr<CXFA_TextUserData>& pUserData) {
+void CFX_RTFBreak::SetUserData(const RetainPtr<CFX_TextUserData>& pUserData) {
   if (m_pUserData == pUserData)
     return;
 
@@ -50,10 +55,6 @@
   m_pUserData = pUserData;
 }
 
-int32_t CFX_RTFBreak::GetLastPositionedTab() const {
-  return m_PositionedTabs.empty() ? m_iLineStart : m_PositionedTabs.back();
-}
-
 bool CFX_RTFBreak::GetPositionedTab(int32_t* iTabPos) const {
   auto it = std::upper_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
                              *iTabPos);
@@ -65,11 +66,10 @@
 }
 
 CFX_BreakType CFX_RTFBreak::AppendChar(wchar_t wch) {
-  ASSERT(m_pFont && m_pCurLine);
+  ASSERT(m_pCurLine);
 
-  uint32_t dwProps = FX_GetUnicodeProperties(wch);
-  FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
-  m_pCurLine->m_LineChars.emplace_back(wch, dwProps, m_iHorizontalScale,
+  FX_CHARTYPE chartype = FX_GetCharType(wch);
+  m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
                                        m_iVerticalScale);
   CFX_Char* pCurChar = &m_pCurLine->m_LineChars.back();
   pCurChar->m_iFontSize = m_iFontSize;
@@ -77,40 +77,40 @@
   pCurChar->m_pUserData = m_pUserData;
 
   CFX_BreakType dwRet1 = CFX_BreakType::None;
-  if (chartype != FX_CHARTYPE_Combination &&
+  if (chartype != FX_CHARTYPE::kCombination &&
       GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
-      m_eCharType != FX_CHARTYPE_Unknown &&
-      m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance &&
-      (m_eCharType != FX_CHARTYPE_Space || chartype != FX_CHARTYPE_Control)) {
+      m_eCharType != FX_CHARTYPE::kUnknown &&
+      IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()) &&
+      (m_eCharType != FX_CHARTYPE::kSpace ||
+       chartype != FX_CHARTYPE::kControl)) {
     dwRet1 = EndBreak(CFX_BreakType::Line);
-    int32_t iCount = m_pCurLine->CountChars();
-    if (iCount > 0)
-      pCurChar = &m_pCurLine->m_LineChars[iCount - 1];
+    if (!m_pCurLine->m_LineChars.empty())
+      pCurChar = &m_pCurLine->m_LineChars.back();
   }
 
   CFX_BreakType dwRet2 = CFX_BreakType::None;
   switch (chartype) {
-    case FX_CHARTYPE_Tab:
+    case FX_CHARTYPE::kTab:
       AppendChar_Tab(pCurChar);
       break;
-    case FX_CHARTYPE_Control:
+    case FX_CHARTYPE::kControl:
       dwRet2 = AppendChar_Control(pCurChar);
       break;
-    case FX_CHARTYPE_Combination:
+    case FX_CHARTYPE::kCombination:
       AppendChar_Combination(pCurChar);
       break;
-    case FX_CHARTYPE_ArabicAlef:
-    case FX_CHARTYPE_ArabicSpecial:
-    case FX_CHARTYPE_ArabicDistortion:
-    case FX_CHARTYPE_ArabicNormal:
-    case FX_CHARTYPE_ArabicForm:
-    case FX_CHARTYPE_Arabic:
+    case FX_CHARTYPE::kArabicAlef:
+    case FX_CHARTYPE::kArabicSpecial:
+    case FX_CHARTYPE::kArabicDistortion:
+    case FX_CHARTYPE::kArabicNormal:
+    case FX_CHARTYPE::kArabicForm:
+    case FX_CHARTYPE::kArabic:
       dwRet2 = AppendChar_Arabic(pCurChar);
       break;
-    case FX_CHARTYPE_Unknown:
-    case FX_CHARTYPE_Space:
-    case FX_CHARTYPE_Numeric:
-    case FX_CHARTYPE_Normal:
+    case FX_CHARTYPE::kUnknown:
+    case FX_CHARTYPE::kSpace:
+    case FX_CHARTYPE::kNumeric:
+    case FX_CHARTYPE::kNormal:
     default:
       dwRet2 = AppendChar_Others(pCurChar);
       break;
@@ -121,21 +121,30 @@
 }
 
 void CFX_RTFBreak::AppendChar_Combination(CFX_Char* pCurChar) {
-  int32_t iCharWidth = 0;
-  if (!m_pFont->GetCharWidth(pCurChar->char_code(), iCharWidth))
-    iCharWidth = 0;
+  FX_SAFE_INT32 iCharWidth = 0;
+  int32_t iCharWidthOut;
+  if (m_pFont && m_pFont->GetCharWidth(pCurChar->char_code(), &iCharWidthOut))
+    iCharWidth = iCharWidthOut;
 
   iCharWidth *= m_iFontSize;
-  iCharWidth = iCharWidth * m_iHorizontalScale / 100;
+  iCharWidth *= m_iHorizontalScale;
+  iCharWidth /= 100;
   CFX_Char* pLastChar = GetLastChar(0, false, true);
-  if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE_Combination)
-    iCharWidth = -iCharWidth;
+  if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE::kCombination)
+    iCharWidth *= -1;
   else
-    m_eCharType = FX_CHARTYPE_Combination;
+    m_eCharType = FX_CHARTYPE::kCombination;
 
-  pCurChar->m_iCharWidth = iCharWidth;
-  if (iCharWidth > 0)
-    m_pCurLine->m_iWidth += iCharWidth;
+  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+  if (iCharWidthValid > 0) {
+    FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+    checked_width += iCharWidthValid;
+    if (!checked_width.IsValid())
+      return;
+
+    m_pCurLine->m_iWidth = checked_width.ValueOrDie();
+  }
 }
 
 void CFX_RTFBreak::AppendChar_Tab(CFX_Char* pCurChar) {
@@ -144,10 +153,18 @@
 
   int32_t& iLineWidth = m_pCurLine->m_iWidth;
   int32_t iCharWidth = iLineWidth;
-  if (GetPositionedTab(&iCharWidth))
-    iCharWidth -= iLineWidth;
-  else
-    iCharWidth = m_iTabWidth * (iLineWidth / m_iTabWidth + 1) - iLineWidth;
+  FX_SAFE_INT32 iSafeCharWidth;
+  if (GetPositionedTab(&iCharWidth)) {
+    iSafeCharWidth = iCharWidth;
+  } else {
+    // Tab width is >= 160000, so this part does not need to be checked.
+    ASSERT(m_iTabWidth >= kMinimumTabWidth);
+    iSafeCharWidth = iLineWidth / m_iTabWidth + 1;
+    iSafeCharWidth *= m_iTabWidth;
+  }
+  iSafeCharWidth -= iLineWidth;
+
+  iCharWidth = iSafeCharWidth.ValueOrDefault(0);
 
   pCurChar->m_iCharWidth = iCharWidth;
   iLineWidth += iCharWidth;
@@ -178,46 +195,74 @@
 }
 
 CFX_BreakType CFX_RTFBreak::AppendChar_Arabic(CFX_Char* pCurChar) {
+  m_pCurLine->IncrementArabicCharCount();
+
   CFX_Char* pLastChar = nullptr;
-  int32_t iCharWidth = 0;
   wchar_t wForm;
   bool bAlef = false;
-  if (m_eCharType >= FX_CHARTYPE_ArabicAlef &&
-      m_eCharType <= FX_CHARTYPE_ArabicDistortion) {
+  if (m_eCharType >= FX_CHARTYPE::kArabicAlef &&
+      m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
     pLastChar = GetLastChar(1, false, true);
     if (pLastChar) {
       m_pCurLine->m_iWidth -= pLastChar->m_iCharWidth;
       CFX_Char* pPrevChar = GetLastChar(2, false, true);
       wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
       bAlef = (wForm == 0xFEFF &&
-               pLastChar->GetCharType() == FX_CHARTYPE_ArabicAlef);
-      if (!m_pFont->GetCharWidth(wForm, iCharWidth) &&
-          !m_pFont->GetCharWidth(pLastChar->char_code(), iCharWidth)) {
-        iCharWidth = m_iDefChar;
+               pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
+      FX_SAFE_INT32 iCharWidth;
+      int32_t iCharWidthOut;
+      if (m_pFont &&
+          (m_pFont->GetCharWidth(wForm, &iCharWidthOut) ||
+           m_pFont->GetCharWidth(pLastChar->char_code(), &iCharWidthOut))) {
+        iCharWidth = iCharWidthOut;
+      } else {
+        iCharWidth = 0;
       }
 
       iCharWidth *= m_iFontSize;
-      iCharWidth = iCharWidth * m_iHorizontalScale / 100;
-      pLastChar->m_iCharWidth = iCharWidth;
-      m_pCurLine->m_iWidth += iCharWidth;
+      iCharWidth *= m_iHorizontalScale;
+      iCharWidth /= 100;
+
+      int iCharWidthValid = iCharWidth.ValueOrDefault(0);
+      pLastChar->m_iCharWidth = iCharWidthValid;
+
+      FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+      checked_width += iCharWidthValid;
+      if (!checked_width.IsValid())
+        return CFX_BreakType::None;
+
+      m_pCurLine->m_iWidth = checked_width.ValueOrDie();
       iCharWidth = 0;
     }
   }
 
   wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
                                       nullptr);
-  if (!m_pFont->GetCharWidth(wForm, iCharWidth) &&
-      !m_pFont->GetCharWidth(pCurChar->char_code(), iCharWidth)) {
-    iCharWidth = m_iDefChar;
+  FX_SAFE_INT32 iCharWidth;
+  int32_t iCharWidthOut;
+  if (m_pFont &&
+      (m_pFont->GetCharWidth(wForm, &iCharWidthOut) ||
+       m_pFont->GetCharWidth(pCurChar->char_code(), &iCharWidthOut))) {
+    iCharWidth = iCharWidthOut;
+  } else {
+    iCharWidth = 0;
   }
 
   iCharWidth *= m_iFontSize;
-  iCharWidth = iCharWidth * m_iHorizontalScale / 100;
-  pCurChar->m_iCharWidth = iCharWidth;
-  m_pCurLine->m_iWidth += iCharWidth;
-  m_pCurLine->m_iArabicChars++;
+  iCharWidth *= m_iHorizontalScale;
+  iCharWidth /= 100;
 
-  if (m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance)
+  int iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+
+  FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+  checked_width += iCharWidthValid;
+  if (!checked_width.IsValid())
+    return CFX_BreakType::None;
+
+  m_pCurLine->m_iWidth = checked_width.ValueOrDie();
+
+  if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()))
     return EndBreak(CFX_BreakType::Line);
   return CFX_BreakType::None;
 }
@@ -225,18 +270,29 @@
 CFX_BreakType CFX_RTFBreak::AppendChar_Others(CFX_Char* pCurChar) {
   FX_CHARTYPE chartype = pCurChar->GetCharType();
   wchar_t wForm = pCurChar->char_code();
-  int32_t iCharWidth = 0;
-  if (!m_pFont->GetCharWidth(wForm, iCharWidth))
-    iCharWidth = m_iDefChar;
+  FX_SAFE_INT32 iCharWidth;
+  int32_t iCharWidthOut;
+  if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
+    iCharWidth = iCharWidthOut;
+  else
+    iCharWidth = 0;
 
   iCharWidth *= m_iFontSize;
-  iCharWidth *= m_iHorizontalScale / 100;
+  iCharWidth *= m_iHorizontalScale;
+  iCharWidth /= 100;
   iCharWidth += m_iCharSpace;
 
-  pCurChar->m_iCharWidth = iCharWidth;
-  m_pCurLine->m_iWidth += iCharWidth;
-  if (chartype != FX_CHARTYPE_Space &&
-      m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance) {
+  int iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+
+  FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
+  checked_width += iCharWidthValid;
+  if (!checked_width.IsValid())
+    return CFX_BreakType::None;
+
+  m_pCurLine->m_iWidth = checked_width.ValueOrDie();
+  if (chartype != FX_CHARTYPE::kSpace &&
+      IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
     return EndBreak(CFX_BreakType::Line);
   }
   return CFX_BreakType::None;
@@ -253,25 +309,24 @@
   }
 
   if (HasLine()) {
-    if (!m_Line[m_iReadyLineIndex].m_LinePieces.empty()) {
-      if (dwStatus != CFX_BreakType::Piece)
-        m_Line[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
-      return m_Line[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
-    }
-    return CFX_BreakType::None;
+    if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
+      return CFX_BreakType::None;
+
+    if (dwStatus != CFX_BreakType::Piece)
+      m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
+    return m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
   }
 
-  int32_t iCount = m_pCurLine->CountChars();
-  if (iCount < 1)
+  if (m_pCurLine->m_LineChars.empty())
     return CFX_BreakType::None;
 
-  CFX_Char* tc = m_pCurLine->GetChar(iCount - 1);
+  CFX_Char* tc = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
   tc->m_dwStatus = dwStatus;
   if (dwStatus == CFX_BreakType::Piece)
     return dwStatus;
 
-  m_iReadyLineIndex = m_pCurLine == &m_Line[0] ? 0 : 1;
-  CFX_BreakLine* pNextLine = &m_Line[1 - m_iReadyLineIndex];
+  m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
+  CFX_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
   bool bAllChars = m_iAlignment == CFX_RTFLineAlignment::Justified ||
                    m_iAlignment == CFX_RTFLineAlignment::Distributed;
 
@@ -285,7 +340,7 @@
   m_pCurLine->m_iStart = m_iLineStart;
 
   CFX_Char* pTC = GetLastChar(0, false, true);
-  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE_Unknown;
+  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
   return dwStatus;
 }
 
@@ -293,15 +348,16 @@
                                       bool bAllChars,
                                       CFX_BreakType dwStatus) {
   bool bDone = false;
-  if (m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance) {
-    const CFX_Char* tc = m_pCurLine->GetChar(m_pCurLine->CountChars() - 1);
+  if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
+    const CFX_Char* tc =
+        m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
     switch (tc->GetCharType()) {
-      case FX_CHARTYPE_Tab:
-      case FX_CHARTYPE_Control:
-      case FX_CHARTYPE_Space:
+      case FX_CHARTYPE::kTab:
+      case FX_CHARTYPE::kControl:
+      case FX_CHARTYPE::kSpace:
         break;
       default:
-        SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
+        SplitTextLine(m_pCurLine.Get(), pNextLine, !m_bPagination && bAllChars);
         bDone = true;
         break;
     }
@@ -310,7 +366,7 @@
   if (!m_bPagination) {
     if (bAllChars && !bDone) {
       int32_t endPos = m_pCurLine->GetLineEnd();
-      GetBreakPos(m_pCurLine->m_LineChars, endPos, bAllChars, true);
+      GetBreakPos(m_pCurLine->m_LineChars, bAllChars, true, &endPos);
     }
     return false;
   }
@@ -320,7 +376,7 @@
   tp.m_pChars = &m_pCurLine->m_LineChars;
   bool bNew = true;
   uint32_t dwIdentity = static_cast<uint32_t>(-1);
-  int32_t iLast = m_pCurLine->CountChars() - 1;
+  int32_t iLast = pdfium::CollectionSize<int32_t>(m_pCurLine->m_LineChars) - 1;
   int32_t j = 0;
   for (int32_t i = 0; i <= iLast;) {
     const CFX_Char* pTC = pCurChars + i;
@@ -334,7 +390,7 @@
       tp.m_iVerticalScale = pTC->vertical_scale();
       dwIdentity = pTC->m_dwIdentity;
       tp.m_dwIdentity = dwIdentity;
-      tp.m_pUserData = pTC->m_pUserData.As<CXFA_TextUserData>();
+      tp.m_pUserData = pTC->m_pUserData;
       j = i;
       bNew = false;
     }
@@ -362,22 +418,19 @@
                                      CFX_BreakType dwStatus) {
   CFX_Char* pTC;
   std::vector<CFX_Char>& chars = m_pCurLine->m_LineChars;
-  int32_t iCount = m_pCurLine->CountChars();
-  if (!m_bPagination && m_pCurLine->m_iArabicChars > 0) {
-    ASSERT(iCount >= 0);
-
+  if (!m_bPagination && m_pCurLine->HasArabicChar()) {
     size_t iBidiNum = 0;
-    for (size_t i = 0; i < static_cast<size_t>(iCount); ++i) {
+    for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
       pTC = &chars[i];
       pTC->m_iBidiPos = static_cast<int32_t>(i);
-      if (pTC->GetCharType() != FX_CHARTYPE_Control)
+      if (pTC->GetCharType() != FX_CHARTYPE::kControl)
         iBidiNum = i;
       if (i == 0)
         pTC->m_iBidiLevel = 1;
     }
-    FX_BidiLine(&chars, iBidiNum + 1);
+    CFX_Char::BidiLine(&chars, iBidiNum + 1);
   } else {
-    for (int32_t i = 0; i < iCount; ++i) {
+    for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
       pTC = &chars[i];
       pTC->m_iBidiLevel = 0;
       pTC->m_iBidiPos = 0;
@@ -396,6 +449,7 @@
   uint32_t dwIdentity = static_cast<uint32_t>(-1);
   int32_t i = 0;
   int32_t j = 0;
+  int32_t iCount = pdfium::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
   while (i < iCount) {
     pTC = &chars[i];
     if (iBidiLevel < 0) {
@@ -409,7 +463,7 @@
       tp.m_iVerticalScale = pTC->vertical_scale();
       dwIdentity = pTC->m_dwIdentity;
       tp.m_dwIdentity = dwIdentity;
-      tp.m_pUserData = pTC->m_pUserData.As<CXFA_TextUserData>();
+      tp.m_pUserData = pTC->m_pUserData;
       tp.m_dwStatus = CFX_BreakType::Piece;
       ++i;
     } else if (iBidiLevel != pTC->m_iBidiLevel ||
@@ -465,13 +519,13 @@
     int32_t j = bArabic ? 0 : ttp.m_iChars - 1;
     while (j > -1 && j < ttp.m_iChars) {
       const CFX_Char* tc = ttp.GetChar(j);
-      if (tc->m_nBreakType == FX_LBT_DIRECT_BRK)
+      if (tc->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
         ++iGapChars;
 
       if (!bFind || !bAllChars) {
-        uint32_t dwCharType = tc->GetCharType();
-        if (dwCharType == FX_CHARTYPE_Space ||
-            dwCharType == FX_CHARTYPE_Control) {
+        FX_CHARTYPE dwCharType = tc->GetCharType();
+        if (dwCharType == FX_CHARTYPE::kSpace ||
+            dwCharType == FX_CHARTYPE::kControl) {
           if (!bFind) {
             int32_t iCharWidth = tc->m_iCharWidth;
             if (bAllChars && iCharWidth > 0)
@@ -503,9 +557,10 @@
 
       for (int32_t j = 0; j < ttp.m_iChars; ++j) {
         CFX_Char* tc = ttp.GetChar(j);
-        if (tc->m_nBreakType != FX_LBT_DIRECT_BRK || tc->m_iCharWidth < 0)
+        if (tc->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
+            tc->m_iCharWidth < 0) {
           continue;
-
+        }
         int32_t k = iOffset / iGapChars;
         tc->m_iCharWidth += k;
         ttp.m_iWidth += k;
@@ -528,9 +583,9 @@
 }
 
 int32_t CFX_RTFBreak::GetBreakPos(std::vector<CFX_Char>& tca,
-                                  int32_t& iEndPos,
                                   bool bAllChars,
-                                  bool bOnlyBrk) {
+                                  bool bOnlyBrk,
+                                  int32_t* pEndPos) {
   int32_t iLength = pdfium::CollectionSize<int32_t>(tca) - 1;
   if (iLength < 1)
     return iLength;
@@ -541,84 +596,82 @@
   int32_t iIndirectPos = -1;
   int32_t iLast = -1;
   int32_t iLastPos = -1;
-  if (iEndPos <= m_iLineWidth) {
+  if (*pEndPos <= m_iLineWidth) {
     if (!bAllChars)
       return iLength;
 
     iBreak = iLength;
-    iBreakPos = iEndPos;
+    iBreakPos = *pEndPos;
   }
 
   CFX_Char* pCharArray = tca.data();
   CFX_Char* pCur = pCharArray + iLength;
   --iLength;
   if (bAllChars)
-    pCur->m_nBreakType = FX_LBT_UNKNOWN;
+    pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
 
-  uint32_t nCodeProp = pCur->char_props();
-  uint32_t nNext = nCodeProp & 0x003F;
+  FX_BREAKPROPERTY nNext = FX_GetBreakProperty(pCur->char_code());
   int32_t iCharWidth = pCur->m_iCharWidth;
   if (iCharWidth > 0)
-    iEndPos -= iCharWidth;
+    *pEndPos -= iCharWidth;
 
   while (iLength >= 0) {
     pCur = pCharArray + iLength;
-    nCodeProp = pCur->char_props();
-    uint32_t nCur = nCodeProp & 0x003F;
+    FX_BREAKPROPERTY nCur = FX_GetBreakProperty(pCur->char_code());
     bool bNeedBreak = false;
     FX_LINEBREAKTYPE eType;
-    if (nCur == kBreakPropertyTB) {
+    if (nCur == FX_BREAKPROPERTY::kTB) {
       bNeedBreak = true;
-      eType = nNext == kBreakPropertyTB
-                  ? FX_LBT_PROHIBITED_BRK
-                  : gs_FX_LineBreak_PairTable[nCur][nNext];
+      eType = nNext == FX_BREAKPROPERTY::kTB
+                  ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
+                  : GetLineBreakTypeFromPair(nCur, nNext);
     } else {
-      if (nCur == kBreakPropertySpace)
+      if (nCur == FX_BREAKPROPERTY::kSP)
         bNeedBreak = true;
 
-      eType = nNext == kBreakPropertySpace
-                  ? FX_LBT_PROHIBITED_BRK
-                  : gs_FX_LineBreak_PairTable[nCur][nNext];
+      eType = nNext == FX_BREAKPROPERTY::kSP
+                  ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
+                  : GetLineBreakTypeFromPair(nCur, nNext);
     }
     if (bAllChars)
-      pCur->m_nBreakType = eType;
+      pCur->m_eLineBreakType = eType;
 
     if (!bOnlyBrk) {
       iCharWidth = pCur->m_iCharWidth;
-      if (iEndPos <= m_iLineWidth || bNeedBreak) {
-        if (eType == FX_LBT_DIRECT_BRK && iBreak < 0) {
+      if (*pEndPos <= m_iLineWidth || bNeedBreak) {
+        if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
           iBreak = iLength;
-          iBreakPos = iEndPos;
+          iBreakPos = *pEndPos;
           if (!bAllChars)
             return iLength;
-        } else if (eType == FX_LBT_INDIRECT_BRK && iIndirect < 0) {
+        } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
           iIndirect = iLength;
-          iIndirectPos = iEndPos;
+          iIndirectPos = *pEndPos;
         }
         if (iLast < 0) {
           iLast = iLength;
-          iLastPos = iEndPos;
+          iLastPos = *pEndPos;
         }
       }
       if (iCharWidth > 0)
-        iEndPos -= iCharWidth;
+        *pEndPos -= iCharWidth;
     }
-    nNext = nCodeProp & 0x003F;
+    nNext = nCur;
     --iLength;
   }
   if (bOnlyBrk)
     return 0;
 
   if (iBreak > -1) {
-    iEndPos = iBreakPos;
+    *pEndPos = iBreakPos;
     return iBreak;
   }
   if (iIndirect > -1) {
-    iEndPos = iIndirectPos;
+    *pEndPos = iIndirectPos;
     return iIndirect;
   }
   if (iLast > -1) {
-    iEndPos = iLastPos;
+    *pEndPos = iLastPos;
     return iLast;
   }
   return 0;
@@ -627,21 +680,22 @@
 void CFX_RTFBreak::SplitTextLine(CFX_BreakLine* pCurLine,
                                  CFX_BreakLine* pNextLine,
                                  bool bAllChars) {
-  ASSERT(pCurLine && pNextLine);
-  int32_t iCount = pCurLine->CountChars();
-  if (iCount < 2)
+  ASSERT(pCurLine);
+  ASSERT(pNextLine);
+
+  if (pCurLine->m_LineChars.size() < 2)
     return;
 
   int32_t iEndPos = pCurLine->GetLineEnd();
   std::vector<CFX_Char>& curChars = pCurLine->m_LineChars;
-  int32_t iCharPos = GetBreakPos(curChars, iEndPos, bAllChars, false);
+  int32_t iCharPos = GetBreakPos(curChars, bAllChars, false, &iEndPos);
   if (iCharPos < 0)
     iCharPos = 0;
 
   ++iCharPos;
-  if (iCharPos >= iCount) {
+  if (iCharPos >= pdfium::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
     pNextLine->Clear();
-    curChars[iCharPos - 1].m_nBreakType = FX_LBT_UNKNOWN;
+    curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
     return;
   }
 
@@ -651,30 +705,30 @@
   pNextLine->m_iStart = pCurLine->m_iStart;
   pNextLine->m_iWidth = pCurLine->GetLineEnd() - iEndPos;
   pCurLine->m_iWidth = iEndPos;
-  curChars[iCharPos - 1].m_nBreakType = FX_LBT_UNKNOWN;
+  curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
 
   for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
-    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE_ArabicAlef) {
-      pCurLine->m_iArabicChars--;
-      pNextLine->m_iArabicChars++;
+    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
+      pCurLine->DecrementArabicCharCount();
+      pNextLine->IncrementArabicCharCount();
     }
     pNextLine->m_LineChars[i].m_dwStatus = CFX_BreakType::None;
   }
 }
 
-int32_t CFX_RTFBreak::GetDisplayPos(const FX_RTFTEXTOBJ* pText,
-                                    FXTEXT_CHARPOS* pCharPos,
-                                    bool bCharCode) const {
-  if (!pText || pText->iLength < 1)
+size_t CFX_RTFBreak::GetDisplayPos(const CFX_TextPiece* pPiece,
+                                   std::vector<TextCharPos>* pCharPos) const {
+  ASSERT(pPiece->iChars > 0);
+  ASSERT(pPiece->pFont);
+
+  RetainPtr<CFGAS_GEFont> pFont = pPiece->pFont;
+  CFX_RectF rtText(pPiece->rtPiece);
+  bool bRTLPiece = FX_IsOdd(pPiece->iBidiLevel);
+  float fFontSize = pPiece->fFontSize;
+  int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
+  if (iFontSize == 0)
     return 0;
 
-  ASSERT(pText->pFont && pText->pRect);
-
-  RetainPtr<CFGAS_GEFont> pFont = pText->pFont;
-  CFX_RectF rtText(*pText->pRect);
-  bool bRTLPiece = FX_IsOdd(pText->iBidiLevel);
-  float fFontSize = pText->fFontSize;
-  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
   int32_t iAscent = pFont->GetAscent();
   int32_t iDescent = pFont->GetDescent();
   int32_t iMaxHeight = iAscent - iDescent;
@@ -684,106 +738,86 @@
   wchar_t wPrev = 0xFEFF;
   wchar_t wNext;
   float fX = rtText.left;
-  int32_t iHorScale = pText->iHorizontalScale;
-  int32_t iVerScale = pText->iVerticalScale;
+  int32_t iHorScale = pPiece->iHorScale;
+  int32_t iVerScale = pPiece->iVerScale;
   if (bRTLPiece)
     fX = rtText.right();
 
   float fY = rtText.top + fAscent;
-  int32_t iCount = 0;
-  for (int32_t i = 0; i < pText->iLength; ++i) {
-    wchar_t wch = pText->pStr[i];
-    int32_t iWidth = pText->pWidths[i];
-    uint32_t dwProps = FX_GetUnicodeProperties(wch);
-    uint32_t dwCharType = (dwProps & FX_CHARTYPEBITSMASK);
+  size_t szCount = 0;
+  for (int32_t i = 0; i < pPiece->iChars; ++i) {
+    TextCharPos& current_char_pos = (*pCharPos)[szCount];
+    wchar_t wch = pPiece->szText[i];
+    int32_t iWidth = pPiece->Widths[i];
+    FX_CHARTYPE dwCharType = FX_GetCharType(wch);
     if (iWidth == 0) {
-      if (dwCharType == FX_CHARTYPE_ArabicAlef)
+      if (dwCharType == FX_CHARTYPE::kArabicAlef)
         wPrev = 0xFEFF;
       continue;
     }
 
-    int32_t iCharWidth = abs(iWidth);
-    bool bEmptyChar =
-        (dwCharType >= FX_CHARTYPE_Tab && dwCharType <= FX_CHARTYPE_Control);
+    uint32_t iCharWidth = abs(iWidth);
+    const bool bEmptyChar = (dwCharType >= FX_CHARTYPE::kTab &&
+                             dwCharType <= FX_CHARTYPE::kControl);
     if (!bEmptyChar)
-      ++iCount;
+      ++szCount;
 
-    if (pCharPos) {
-      iCharWidth /= iFontSize;
-      wchar_t wForm = wch;
-      if (dwCharType >= FX_CHARTYPE_ArabicAlef) {
-        if (i + 1 < pText->iLength) {
-          wNext = pText->pStr[i + 1];
-          if (pText->pWidths[i + 1] < 0 && i + 2 < pText->iLength)
-            wNext = pText->pStr[i + 2];
-        } else {
-          wNext = 0xFEFF;
-        }
-        wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
-      } else if (bRTLPiece) {
-        wForm = FX_GetMirrorChar(wch, dwProps);
+    iCharWidth /= iFontSize;
+    wchar_t wForm = wch;
+    if (dwCharType >= FX_CHARTYPE::kArabicAlef) {
+      if (i + 1 < pPiece->iChars) {
+        wNext = pPiece->szText[i + 1];
+        if (pPiece->Widths[i + 1] < 0 && i + 2 < pPiece->iChars)
+          wNext = pPiece->szText[i + 2];
+      } else {
+        wNext = 0xFEFF;
       }
-      dwProps = FX_GetUnicodeProperties(wForm);
+      wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
+    } else if (bRTLPiece) {
+      wForm = FX_GetMirrorChar(wch);
+    }
 
-      if (!bEmptyChar) {
-        if (bCharCode) {
-          pCharPos->m_GlyphIndex = wch;
-        } else {
-          pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm);
-          if (pCharPos->m_GlyphIndex == 0xFFFF)
-            pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wch);
-        }
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
-        pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
+    if (!bEmptyChar) {
+      current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wForm);
+      if (current_char_pos.m_GlyphIndex == 0xFFFF)
+        current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wch);
+#if defined(OS_MACOSX)
+      current_char_pos.m_ExtGID = current_char_pos.m_GlyphIndex;
 #endif
-        pCharPos->m_FontCharWidth = iCharWidth;
-      }
+      current_char_pos.m_FontCharWidth = iCharWidth;
+    }
 
-      float fCharWidth = fFontSize * iCharWidth / 1000.0f;
-      if (bRTLPiece && dwCharType != FX_CHARTYPE_Combination)
-        fX -= fCharWidth;
+    float fCharWidth = fFontSize * iCharWidth / 1000.0f;
+    if (bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
+      fX -= fCharWidth;
 
-      if (!bEmptyChar)
-        pCharPos->m_Origin = CFX_PointF(fX, fY);
-      if (!bRTLPiece && dwCharType != FX_CHARTYPE_Combination)
-        fX += fCharWidth;
+    if (!bEmptyChar)
+      current_char_pos.m_Origin = CFX_PointF(fX, fY);
+    if (!bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
+      fX += fCharWidth;
 
-      if (!bEmptyChar) {
-        pCharPos->m_bGlyphAdjust = true;
-        pCharPos->m_AdjustMatrix[0] = -1;
-        pCharPos->m_AdjustMatrix[1] = 0;
-        pCharPos->m_AdjustMatrix[2] = 0;
-        pCharPos->m_AdjustMatrix[3] = 1;
-        pCharPos->m_Origin.y += fAscent * iVerScale / 100.0f;
-        pCharPos->m_Origin.y -= fAscent;
+    if (!bEmptyChar) {
+      current_char_pos.m_bGlyphAdjust = true;
+      current_char_pos.m_AdjustMatrix[0] = -1;
+      current_char_pos.m_AdjustMatrix[1] = 0;
+      current_char_pos.m_AdjustMatrix[2] = 0;
+      current_char_pos.m_AdjustMatrix[3] = 1;
+      current_char_pos.m_Origin.y += fAscent * iVerScale / 100.0f;
+      current_char_pos.m_Origin.y -= fAscent;
 
-        if (iHorScale != 100 || iVerScale != 100) {
-          pCharPos->m_AdjustMatrix[0] =
-              pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
-          pCharPos->m_AdjustMatrix[1] =
-              pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
-          pCharPos->m_AdjustMatrix[2] =
-              pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
-          pCharPos->m_AdjustMatrix[3] =
-              pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
-        }
-        ++pCharPos;
+      if (iHorScale != 100 || iVerScale != 100) {
+        current_char_pos.m_AdjustMatrix[0] =
+            current_char_pos.m_AdjustMatrix[0] * iHorScale / 100.0f;
+        current_char_pos.m_AdjustMatrix[1] =
+            current_char_pos.m_AdjustMatrix[1] * iHorScale / 100.0f;
+        current_char_pos.m_AdjustMatrix[2] =
+            current_char_pos.m_AdjustMatrix[2] * iVerScale / 100.0f;
+        current_char_pos.m_AdjustMatrix[3] =
+            current_char_pos.m_AdjustMatrix[3] * iVerScale / 100.0f;
       }
     }
     if (iWidth > 0)
       wPrev = wch;
   }
-  return iCount;
+  return szCount;
 }
-
-FX_RTFTEXTOBJ::FX_RTFTEXTOBJ()
-    : pFont(nullptr),
-      pRect(nullptr),
-      wLineBreakChar(L'\n'),
-      fFontSize(12.0f),
-      iLength(0),
-      iBidiLevel(0),
-      iHorizontalScale(100),
-      iVerticalScale(100) {}
-
-FX_RTFTEXTOBJ::~FX_RTFTEXTOBJ() {}
diff --git a/xfa/fgas/layout/cfx_rtfbreak.h b/xfa/fgas/layout/cfx_rtfbreak.h
index 67052d2..2a722b0 100644
--- a/xfa/fgas/layout/cfx_rtfbreak.h
+++ b/xfa/fgas/layout/cfx_rtfbreak.h
@@ -14,10 +14,11 @@
 #include "core/fxcrt/fx_unicode.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "xfa/fgas/layout/cfx_break.h"
-#include "xfa/fxfa/cxfa_textuserdata.h"
 
 class CFGAS_GEFont;
-class FXTEXT_CHARPOS;
+class CFX_TextUserData;
+class CFX_TextPiece;
+class TextCharPos;
 
 enum class CFX_RTFLineAlignment {
   Left = 0,
@@ -27,23 +28,7 @@
   Distributed
 };
 
-struct FX_RTFTEXTOBJ {
-  FX_RTFTEXTOBJ();
-  ~FX_RTFTEXTOBJ();
-
-  WideString pStr;
-  std::vector<int32_t> pWidths;
-  RetainPtr<CFGAS_GEFont> pFont;
-  const CFX_RectF* pRect;
-  wchar_t wLineBreakChar;
-  float fFontSize;
-  int32_t iLength;
-  int32_t iBidiLevel;
-  int32_t iHorizontalScale;
-  int32_t iVerticalScale;
-};
-
-class CFX_RTFBreak : public CFX_Break {
+class CFX_RTFBreak final : public CFX_Break {
  public:
   explicit CFX_RTFBreak(uint32_t dwLayoutStyles);
   ~CFX_RTFBreak() override;
@@ -51,33 +36,29 @@
   void SetLineStartPos(float fLinePos);
 
   void SetAlignment(CFX_RTFLineAlignment align) { m_iAlignment = align; }
-  void SetUserData(const RetainPtr<CXFA_TextUserData>& pUserData);
+  void SetUserData(const RetainPtr<CFX_TextUserData>& pUserData);
 
   void AddPositionedTab(float fTabPos);
 
   CFX_BreakType EndBreak(CFX_BreakType dwStatus);
 
-  int32_t GetDisplayPos(const FX_RTFTEXTOBJ* pText,
-                        FXTEXT_CHARPOS* pCharPos,
-                        bool bCharCode) const;
+  size_t GetDisplayPos(const CFX_TextPiece* pPiece,
+                       std::vector<TextCharPos>* pCharPos) const;
 
   CFX_BreakType AppendChar(wchar_t wch);
 
-  CFX_BreakLine* GetCurrentLineForTesting() const { return m_pCurLine; }
-
  private:
   void AppendChar_Combination(CFX_Char* pCurChar);
   void AppendChar_Tab(CFX_Char* pCurChar);
   CFX_BreakType AppendChar_Control(CFX_Char* pCurChar);
   CFX_BreakType AppendChar_Arabic(CFX_Char* pCurChar);
   CFX_BreakType AppendChar_Others(CFX_Char* pCurChar);
-  int32_t GetLastPositionedTab() const;
   bool GetPositionedTab(int32_t* iTabPos) const;
 
   int32_t GetBreakPos(std::vector<CFX_Char>& tca,
-                      int32_t& iEndPos,
                       bool bAllChars,
-                      bool bOnlyBrk);
+                      bool bOnlyBrk,
+                      int32_t* pEndPos);
   void SplitTextLine(CFX_BreakLine* pCurLine,
                      CFX_BreakLine* pNextLine,
                      bool bAllChars);
@@ -92,7 +73,7 @@
   bool m_bPagination;
   std::vector<int32_t> m_PositionedTabs;
   CFX_RTFLineAlignment m_iAlignment;
-  RetainPtr<CXFA_TextUserData> m_pUserData;
+  RetainPtr<CFX_TextUserData> m_pUserData;
 };
 
 #endif  // XFA_FGAS_LAYOUT_CFX_RTFBREAK_H_
diff --git a/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp b/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
index 5f24631..0775d7b 100644
--- a/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
+++ b/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
@@ -7,26 +7,29 @@
 #include "xfa/fgas/layout/cfx_rtfbreak.h"
 
 #include <memory>
+#include <utility>
 
+#include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
+#include "testing/xfa_unit_test_support.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/layout/cfx_char.h"
 
 class CFX_RTFBreakTest : public testing::Test {
  public:
   void SetUp() override {
     font_ =
         CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager());
-    ASSERT(font_.Get() != nullptr);
+    ASSERT_TRUE(font_.Get());
   }
 
-  std::unique_ptr<CFX_RTFBreak> CreateBreak(int32_t args) {
-    auto b = pdfium::MakeUnique<CFX_RTFBreak>(args);
-    b->SetFont(font_);
-    return b;
+  std::unique_ptr<CFX_RTFBreak> CreateBreak(uint32_t layout_styles) {
+    auto rtf_break = pdfium::MakeUnique<CFX_RTFBreak>(layout_styles);
+    rtf_break->SetFont(font_);
+    return rtf_break;
   }
 
  private:
@@ -37,38 +40,54 @@
 // and must be consumed before you get any more characters ....
 
 TEST_F(CFX_RTFBreakTest, AddChars) {
-  auto b = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
+  auto rtf_break = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
 
   WideString str(L"Input String.");
-  for (const auto& c : str)
-    EXPECT_EQ(CFX_BreakType::None, b->AppendChar(c));
+  for (wchar_t ch : str)
+    EXPECT_EQ(CFX_BreakType::None, rtf_break->AppendChar(ch));
 
-  EXPECT_EQ(CFX_BreakType::Paragraph, b->AppendChar(L'\n'));
-  ASSERT_EQ(1, b->CountBreakPieces());
-  EXPECT_EQ(str + L"\n", b->GetBreakPieceUnstable(0)->GetString());
+  EXPECT_EQ(CFX_BreakType::Paragraph, rtf_break->AppendChar(L'\n'));
+  ASSERT_EQ(1, rtf_break->CountBreakPieces());
+  EXPECT_EQ(str + L"\n", rtf_break->GetBreakPieceUnstable(0)->GetString());
 
-  b->ClearBreakPieces();
-  b->Reset();
-  EXPECT_EQ(0, b->GetCurrentLineForTesting()->GetLineEnd());
+  rtf_break->ClearBreakPieces();
+  rtf_break->Reset();
+  EXPECT_EQ(0, rtf_break->GetCurrentLineForTesting()->GetLineEnd());
 
   str = L"Second str.";
-  for (const auto& c : str)
-    EXPECT_EQ(CFX_BreakType::None, b->AppendChar(c));
+  for (wchar_t ch : str)
+    EXPECT_EQ(CFX_BreakType::None, rtf_break->AppendChar(ch));
 
   // Force the end of the break at the end of the string.
-  b->EndBreak(CFX_BreakType::Paragraph);
-  ASSERT_EQ(1, b->CountBreakPieces());
-  EXPECT_EQ(str, b->GetBreakPieceUnstable(0)->GetString());
+  rtf_break->EndBreak(CFX_BreakType::Paragraph);
+  ASSERT_EQ(1, rtf_break->CountBreakPieces());
+  EXPECT_EQ(str, rtf_break->GetBreakPieceUnstable(0)->GetString());
 }
 
 TEST_F(CFX_RTFBreakTest, ControlCharacters) {
-  auto b = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
-  EXPECT_EQ(CFX_BreakType::Line, b->AppendChar(L'\v'));
-  EXPECT_EQ(CFX_BreakType::Page, b->AppendChar(L'\f'));
-  // 0x2029 is the Paragraph Separator unicode character.
-  EXPECT_EQ(CFX_BreakType::Paragraph, b->AppendChar(0x2029));
-  EXPECT_EQ(CFX_BreakType::Paragraph, b->AppendChar(L'\n'));
+  auto rtf_break = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
+  EXPECT_EQ(CFX_BreakType::Line, rtf_break->AppendChar(L'\v'));
+  EXPECT_EQ(CFX_BreakType::Page, rtf_break->AppendChar(L'\f'));
+  const wchar_t kUnicodeParagraphSeparator = 0x2029;
+  EXPECT_EQ(CFX_BreakType::Paragraph,
+            rtf_break->AppendChar(kUnicodeParagraphSeparator));
+  EXPECT_EQ(CFX_BreakType::Paragraph, rtf_break->AppendChar(L'\n'));
 
-  ASSERT_EQ(1, b->CountBreakPieces());
-  EXPECT_EQ(L"\v", b->GetBreakPieceUnstable(0)->GetString());
+  ASSERT_EQ(1, rtf_break->CountBreakPieces());
+  EXPECT_EQ(L"\v", rtf_break->GetBreakPieceUnstable(0)->GetString());
+}
+
+TEST_F(CFX_RTFBreakTest, BidiLine) {
+  auto rtf_break = CreateBreak(FX_LAYOUTSTYLE_ExpandTab);
+  rtf_break->SetLineBreakTolerance(1);
+  rtf_break->SetFontSize(12);
+
+  WideString input = WideString::FromUTF8(ByteStringView("\xa\x0\xa\xa", 4));
+  for (wchar_t ch : input)
+    rtf_break->AppendChar(ch);
+
+  std::vector<CFX_Char> chars =
+      rtf_break->GetCurrentLineForTesting()->m_LineChars;
+  CFX_Char::BidiLine(&chars, chars.size());
+  EXPECT_EQ(3u, chars.size());
 }
diff --git a/xfa/fgas/layout/cfx_textpiece.cpp b/xfa/fgas/layout/cfx_textpiece.cpp
new file mode 100644
index 0000000..06f6cd6
--- /dev/null
+++ b/xfa/fgas/layout/cfx_textpiece.cpp
@@ -0,0 +1,13 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/cfx_textpiece.h"
+
+#include "xfa/fgas/font/cfgas_gefont.h"
+
+CFX_TextPiece::CFX_TextPiece() = default;
+
+CFX_TextPiece::~CFX_TextPiece() = default;
diff --git a/xfa/fgas/layout/cfx_textpiece.h b/xfa/fgas/layout/cfx_textpiece.h
new file mode 100644
index 0000000..409d200
--- /dev/null
+++ b/xfa/fgas/layout/cfx_textpiece.h
@@ -0,0 +1,34 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFX_TEXTPIECE_H_
+#define XFA_FGAS_LAYOUT_CFX_TEXTPIECE_H_
+
+#include <vector>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
+
+class CFGAS_GEFont;
+
+class CFX_TextPiece {
+ public:
+  CFX_TextPiece();
+  ~CFX_TextPiece();
+
+  WideString szText;
+  std::vector<int32_t> Widths;
+  int32_t iChars;
+  int32_t iHorScale;
+  int32_t iVerScale;
+  int32_t iBidiLevel;
+  float fFontSize;
+  CFX_RectF rtPiece;
+  RetainPtr<CFGAS_GEFont> pFont;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFX_TEXTPIECE_H_
diff --git a/xfa/fgas/layout/cfx_textuserdata.cpp b/xfa/fgas/layout/cfx_textuserdata.cpp
new file mode 100644
index 0000000..f5151bd
--- /dev/null
+++ b/xfa/fgas/layout/cfx_textuserdata.cpp
@@ -0,0 +1,23 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/cfx_textuserdata.h"
+
+#include "core/fxcrt/css/cfx_css.h"
+#include "core/fxcrt/css/cfx_csscomputedstyle.h"
+#include "core/fxcrt/css/cfx_cssstyleselector.h"
+#include "xfa/fgas/layout/cfx_linkuserdata.h"
+
+CFX_TextUserData::CFX_TextUserData(
+    const RetainPtr<CFX_CSSComputedStyle>& pStyle)
+    : m_pStyle(pStyle) {}
+
+CFX_TextUserData::CFX_TextUserData(
+    const RetainPtr<CFX_CSSComputedStyle>& pStyle,
+    const RetainPtr<CFX_LinkUserData>& pLinkData)
+    : m_pStyle(pStyle), m_pLinkData(pLinkData) {}
+
+CFX_TextUserData::~CFX_TextUserData() {}
diff --git a/xfa/fgas/layout/cfx_textuserdata.h b/xfa/fgas/layout/cfx_textuserdata.h
new file mode 100644
index 0000000..d5aaed9
--- /dev/null
+++ b/xfa/fgas/layout/cfx_textuserdata.h
@@ -0,0 +1,30 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_CFX_TEXTUSERDATA_H_
+#define XFA_FGAS_LAYOUT_CFX_TEXTUSERDATA_H_
+
+#include "core/fxcrt/retain_ptr.h"
+
+class CFX_CSSComputedStyle;
+class CFX_LinkUserData;
+
+class CFX_TextUserData final : public Retainable {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  RetainPtr<CFX_CSSComputedStyle> m_pStyle;
+  RetainPtr<CFX_LinkUserData> m_pLinkData;
+
+ private:
+  explicit CFX_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle);
+  CFX_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle,
+                   const RetainPtr<CFX_LinkUserData>& pLinkData);
+  ~CFX_TextUserData() override;
+};
+
+#endif  // XFA_FGAS_LAYOUT_CFX_TEXTUSERDATA_H_
diff --git a/xfa/fgas/layout/cfx_txtbreak.cpp b/xfa/fgas/layout/cfx_txtbreak.cpp
index b028c9b..4b87728 100644
--- a/xfa/fgas/layout/cfx_txtbreak.cpp
+++ b/xfa/fgas/layout/cfx_txtbreak.cpp
@@ -8,19 +8,20 @@
 
 #include <algorithm>
 
-#include "core/fxcrt/fx_arabic.h"
-#include "core/fxcrt/fx_bidi.h"
-#include "core/fxcrt/fx_memory.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fde/cfde_texteditengine.h"
+#include "build/build_config.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/text_char_pos.h"
+#include "third_party/base/stl_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_linebreak.h"
+#include "xfa/fgas/layout/cfx_char.h"
+#include "xfa/fgas/layout/fx_arabic.h"
+#include "xfa/fgas/layout/fx_linebreak.h"
 
 namespace {
 
-bool IsCtrlCode(wchar_t ch) {
-  uint32_t dwRet = (FX_GetUnicodeProperties(ch) & FX_CHARTYPEBITSMASK);
-  return dwRet == FX_CHARTYPE_Tab || dwRet == FX_CHARTYPE_Control;
+bool IsCtrlCode(wchar_t wch) {
+  FX_CHARTYPE dwRet = FX_GetCharType(wch);
+  return dwRet == FX_CHARTYPE::kTab || dwRet == FX_CHARTYPE::kControl;
 }
 
 }  // namespace
@@ -33,24 +34,24 @@
 CFX_TxtBreak::~CFX_TxtBreak() {}
 
 void CFX_TxtBreak::SetLineWidth(float fLineWidth) {
-  m_iLineWidth = FXSYS_round(fLineWidth * 20000.0f);
+  m_iLineWidth = FXSYS_roundf(fLineWidth * kConversionFactor);
   ASSERT(m_iLineWidth >= 20000);
 }
 
 void CFX_TxtBreak::SetAlignment(int32_t iAlignment) {
-  ASSERT(iAlignment >= CFX_TxtLineAlignment_Left &&
-         iAlignment <= CFX_TxtLineAlignment_Justified);
+  ASSERT(iAlignment >= CFX_TxtLineAlignment_Left);
+  ASSERT(iAlignment <= CFX_TxtLineAlignment_Justified);
   m_iAlignment = iAlignment;
 }
 
 void CFX_TxtBreak::SetCombWidth(float fCombWidth) {
-  m_iCombWidth = FXSYS_round(fCombWidth * 20000.0f);
+  m_iCombWidth = FXSYS_roundf(fCombWidth * kConversionFactor);
 }
 
 void CFX_TxtBreak::AppendChar_Combination(CFX_Char* pCurChar) {
   wchar_t wch = pCurChar->char_code();
   wchar_t wForm;
-  int32_t iCharWidth = 0;
+  FX_SAFE_INT32 iCharWidth = 0;
   pCurChar->m_iCharWidth = -1;
   if (m_bCombText) {
     iCharWidth = m_iCombWidth;
@@ -78,21 +79,27 @@
         pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
       }
     }
-    if (!m_pFont->GetCharWidth(wForm, iCharWidth))
+    int32_t iCharWidthOut;
+    if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
+      iCharWidth = iCharWidthOut;
+    else
       iCharWidth = 0;
 
     iCharWidth *= m_iFontSize;
-    iCharWidth = iCharWidth * m_iHorizontalScale / 100;
+    iCharWidth *= m_iHorizontalScale;
+    iCharWidth /= 100;
   }
-  pCurChar->m_iCharWidth = -iCharWidth;
+
+  iCharWidth *= -1;
+  pCurChar->m_iCharWidth = iCharWidth.ValueOrDefault(0);
 }
 
 void CFX_TxtBreak::AppendChar_Tab(CFX_Char* pCurChar) {
-  m_eCharType = FX_CHARTYPE_Tab;
+  m_eCharType = FX_CHARTYPE::kTab;
 }
 
 CFX_BreakType CFX_TxtBreak::AppendChar_Control(CFX_Char* pCurChar) {
-  m_eCharType = FX_CHARTYPE_Control;
+  m_eCharType = FX_CHARTYPE::kControl;
   CFX_BreakType dwRet = CFX_BreakType::None;
   if (!m_bSingleLine) {
     wchar_t wch = pCurChar->char_code();
@@ -122,52 +129,65 @@
   FX_CHARTYPE chartype = pCurChar->GetCharType();
   int32_t& iLineWidth = m_pCurLine->m_iWidth;
   wchar_t wForm;
-  int32_t iCharWidth = 0;
   CFX_Char* pLastChar = nullptr;
   bool bAlef = false;
-  if (!m_bCombText && m_eCharType >= FX_CHARTYPE_ArabicAlef &&
-      m_eCharType <= FX_CHARTYPE_ArabicDistortion) {
+  if (!m_bCombText && m_eCharType >= FX_CHARTYPE::kArabicAlef &&
+      m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
+    FX_SAFE_INT32 iCharWidth = 0;
     pLastChar = GetLastChar(1, true, false);
     if (pLastChar) {
+      if (pLastChar->m_iCharWidth > 0)
+        iLineWidth -= pLastChar->m_iCharWidth;
       iCharWidth = pLastChar->m_iCharWidth;
-      if (iCharWidth > 0)
-        iLineWidth -= iCharWidth;
 
       CFX_Char* pPrevChar = GetLastChar(2, true, false);
       wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
       bAlef = (wForm == 0xFEFF &&
-               pLastChar->GetCharType() == FX_CHARTYPE_ArabicAlef);
-      m_pFont->GetCharWidth(wForm, iCharWidth);
-
+               pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
+      if (m_pFont) {
+        int32_t iCharWidthOut = 0;
+        m_pFont->GetCharWidth(wForm, &iCharWidthOut);
+        iCharWidth = iCharWidthOut;
+      }
       if (wForm == 0xFEFF)
-        iCharWidth = m_iDefChar;
+        iCharWidth = 0;
 
       iCharWidth *= m_iFontSize;
-      iCharWidth = iCharWidth * m_iHorizontalScale / 100;
-      pLastChar->m_iCharWidth = iCharWidth;
-      iLineWidth += iCharWidth;
-      iCharWidth = 0;
+      iCharWidth *= m_iHorizontalScale;
+      iCharWidth /= 100;
+
+      int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
+      pLastChar->m_iCharWidth = iCharWidthValid;
+      iLineWidth += iCharWidthValid;
     }
   }
 
   m_eCharType = chartype;
   wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
                                       nullptr);
+  FX_SAFE_INT32 iCharWidth = 0;
   if (m_bCombText) {
     iCharWidth = m_iCombWidth;
   } else {
-    m_pFont->GetCharWidth(wForm, iCharWidth);
-
+    if (m_pFont) {
+      int32_t iCharWidthOut = 0;
+      m_pFont->GetCharWidth(wForm, &iCharWidthOut);
+      iCharWidth = iCharWidthOut;
+    }
     if (wForm == 0xFEFF)
-      iCharWidth = m_iDefChar;
+      iCharWidth = 0;
 
     iCharWidth *= m_iFontSize;
-    iCharWidth = iCharWidth * m_iHorizontalScale / 100;
+    iCharWidth *= m_iHorizontalScale;
+    iCharWidth /= 100;
   }
-  pCurChar->m_iCharWidth = iCharWidth;
-  iLineWidth += iCharWidth;
-  m_pCurLine->m_iArabicChars++;
-  if (!m_bSingleLine && iLineWidth > m_iLineWidth + m_iTolerance)
+
+  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+  iLineWidth += iCharWidthValid;
+
+  m_pCurLine->IncrementArabicCharCount();
+  if (!m_bSingleLine && IsGreaterThanLineWidth(iLineWidth))
     return EndBreak(CFX_BreakType::Line);
   return CFX_BreakType::None;
 }
@@ -175,7 +195,7 @@
 CFX_BreakType CFX_TxtBreak::AppendChar_Others(CFX_Char* pCurChar) {
   FX_CHARTYPE chartype = pCurChar->GetCharType();
   int32_t& iLineWidth = m_pCurLine->m_iWidth;
-  int32_t iCharWidth = 0;
+  FX_SAFE_INT32 iCharWidth = 0;
   m_eCharType = chartype;
   wchar_t wch = pCurChar->char_code();
   wchar_t wForm = wch;
@@ -183,18 +203,24 @@
   if (m_bCombText) {
     iCharWidth = m_iCombWidth;
   } else {
-    if (!m_pFont->GetCharWidth(wForm, iCharWidth))
-      iCharWidth = m_iDefChar;
+    int32_t iCharWidthOut;
+    if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
+      iCharWidth = iCharWidthOut;
+    else
+      iCharWidth = 0;
 
     iCharWidth *= m_iFontSize;
-    iCharWidth = iCharWidth * m_iHorizontalScale / 100;
+    iCharWidth *= m_iHorizontalScale;
+    iCharWidth /= 100;
   }
 
   iCharWidth += m_iCharSpace;
-  pCurChar->m_iCharWidth = iCharWidth;
-  iLineWidth += iCharWidth;
-  if (!m_bSingleLine && chartype != FX_CHARTYPE_Space &&
-      iLineWidth > m_iLineWidth + m_iTolerance) {
+
+  int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
+  pCurChar->m_iCharWidth = iCharWidthValid;
+  iLineWidth += iCharWidthValid;
+  if (!m_bSingleLine && chartype != FX_CHARTYPE::kSpace &&
+      IsGreaterThanLineWidth(iLineWidth)) {
     return EndBreak(CFX_BreakType::Line);
   }
 
@@ -202,23 +228,22 @@
 }
 
 CFX_BreakType CFX_TxtBreak::AppendChar(wchar_t wch) {
-  uint32_t dwProps = FX_GetUnicodeProperties(wch);
-  FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
-  m_pCurLine->m_LineChars.emplace_back(wch, dwProps, m_iHorizontalScale,
+  FX_CHARTYPE chartype = FX_GetCharType(wch);
+  m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
                                        m_iVerticalScale);
   CFX_Char* pCurChar = &m_pCurLine->m_LineChars.back();
   pCurChar->m_dwCharStyles = m_iAlignment | (1 << 8);
 
   CFX_BreakType dwRet1 = CFX_BreakType::None;
-  if (chartype != FX_CHARTYPE_Combination &&
+  if (chartype != FX_CHARTYPE::kCombination &&
       GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
-      m_eCharType != FX_CHARTYPE_Unknown &&
-      m_pCurLine->m_iWidth > m_iLineWidth + m_iTolerance && !m_bSingleLine &&
-      (m_eCharType != FX_CHARTYPE_Space || chartype != FX_CHARTYPE_Control)) {
+      m_eCharType != FX_CHARTYPE::kUnknown && !m_bSingleLine &&
+      IsGreaterThanLineWidth(m_pCurLine->m_iWidth) &&
+      (m_eCharType != FX_CHARTYPE::kSpace ||
+       chartype != FX_CHARTYPE::kControl)) {
     dwRet1 = EndBreak(CFX_BreakType::Line);
-    int32_t iCount = m_pCurLine->CountChars();
-    if (iCount > 0)
-      pCurChar = &m_pCurLine->m_LineChars[iCount - 1];
+    if (!m_pCurLine->m_LineChars.empty())
+      pCurChar = &m_pCurLine->m_LineChars.back();
   }
 
   CFX_BreakType dwRet2 = CFX_BreakType::None;
@@ -227,29 +252,30 @@
     // don't get matched as control characters so we go into AppendChar_other
     // and never detect the new paragraph ...
     dwRet2 = CFX_BreakType::Paragraph;
+    EndBreak(dwRet2);
   } else {
     switch (chartype) {
-      case FX_CHARTYPE_Tab:
+      case FX_CHARTYPE::kTab:
         AppendChar_Tab(pCurChar);
         break;
-      case FX_CHARTYPE_Control:
+      case FX_CHARTYPE::kControl:
         dwRet2 = AppendChar_Control(pCurChar);
         break;
-      case FX_CHARTYPE_Combination:
+      case FX_CHARTYPE::kCombination:
         AppendChar_Combination(pCurChar);
         break;
-      case FX_CHARTYPE_ArabicAlef:
-      case FX_CHARTYPE_ArabicSpecial:
-      case FX_CHARTYPE_ArabicDistortion:
-      case FX_CHARTYPE_ArabicNormal:
-      case FX_CHARTYPE_ArabicForm:
-      case FX_CHARTYPE_Arabic:
+      case FX_CHARTYPE::kArabicAlef:
+      case FX_CHARTYPE::kArabicSpecial:
+      case FX_CHARTYPE::kArabicDistortion:
+      case FX_CHARTYPE::kArabicNormal:
+      case FX_CHARTYPE::kArabicForm:
+      case FX_CHARTYPE::kArabic:
         dwRet2 = AppendChar_Arabic(pCurChar);
         break;
-      case FX_CHARTYPE_Unknown:
-      case FX_CHARTYPE_Space:
-      case FX_CHARTYPE_Numeric:
-      case FX_CHARTYPE_Normal:
+      case FX_CHARTYPE::kUnknown:
+      case FX_CHARTYPE::kSpace:
+      case FX_CHARTYPE::kNumeric:
+      case FX_CHARTYPE::kNormal:
       default:
         dwRet2 = AppendChar_Others(pCurChar);
         break;
@@ -260,28 +286,26 @@
 
 bool CFX_TxtBreak::EndBreak_SplitLine(CFX_BreakLine* pNextLine,
                                       bool bAllChars) {
-  int32_t iCount = m_pCurLine->CountChars();
   bool bDone = false;
   CFX_Char* pTC;
-  if (!m_bSingleLine && m_pCurLine->m_iWidth > m_iLineWidth + m_iTolerance) {
-    pTC = m_pCurLine->GetChar(iCount - 1);
+  if (!m_bSingleLine && IsGreaterThanLineWidth(m_pCurLine->m_iWidth)) {
+    pTC = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
     switch (pTC->GetCharType()) {
-      case FX_CHARTYPE_Tab:
-      case FX_CHARTYPE_Control:
-      case FX_CHARTYPE_Space:
+      case FX_CHARTYPE::kTab:
+      case FX_CHARTYPE::kControl:
+      case FX_CHARTYPE::kSpace:
         break;
       default:
-        SplitTextLine(m_pCurLine, pNextLine, bAllChars);
+        SplitTextLine(m_pCurLine.Get(), pNextLine, bAllChars);
         bDone = true;
         break;
     }
   }
 
-  iCount = m_pCurLine->CountChars();
   CFX_BreakPiece tp;
   if (bAllChars && !bDone) {
     int32_t iEndPos = m_pCurLine->m_iWidth;
-    GetBreakPos(m_pCurLine->m_LineChars, iEndPos, bAllChars, true);
+    GetBreakPos(&m_pCurLine->m_LineChars, bAllChars, true, &iEndPos);
   }
   return false;
 }
@@ -292,96 +316,12 @@
   FX_TPO tpo;
   CFX_Char* pTC;
   std::vector<CFX_Char>& chars = m_pCurLine->m_LineChars;
-  int32_t iCount = m_pCurLine->CountChars();
-  bool bDone = m_pCurLine->m_iArabicChars > 0;
-  if (bDone) {
-    ASSERT(iCount >= 0);
-
-    size_t iBidiNum = 0;
-    for (size_t i = 0; i < static_cast<size_t>(iCount); ++i) {
-      pTC = &chars[i];
-      pTC->m_iBidiPos = static_cast<int32_t>(i);
-      if (pTC->GetCharType() != FX_CHARTYPE_Control)
-        iBidiNum = i;
-      if (i == 0)
-        pTC->m_iBidiLevel = 1;
-    }
-    FX_BidiLine(&chars, iBidiNum + 1);
-  }
-
-  if (bDone) {
-    tp.m_dwStatus = CFX_BreakType::Piece;
-    tp.m_iStartPos = m_pCurLine->m_iStart;
-    tp.m_pChars = &m_pCurLine->m_LineChars;
-    int32_t iBidiLevel = -1;
-    int32_t iCharWidth;
-    int32_t i = 0;
-    int32_t j = -1;
-    while (i < iCount) {
-      pTC = &chars[i];
-      if (iBidiLevel < 0) {
-        iBidiLevel = pTC->m_iBidiLevel;
-        tp.m_iWidth = 0;
-        tp.m_iBidiLevel = iBidiLevel;
-        tp.m_iBidiPos = pTC->m_iBidiOrder;
-        tp.m_dwCharStyles = pTC->m_dwCharStyles;
-        tp.m_iHorizontalScale = pTC->horizonal_scale();
-        tp.m_iVerticalScale = pTC->vertical_scale();
-        tp.m_dwStatus = CFX_BreakType::Piece;
-      }
-      if (iBidiLevel != pTC->m_iBidiLevel ||
-          pTC->m_dwStatus != CFX_BreakType::None) {
-        if (iBidiLevel == pTC->m_iBidiLevel) {
-          tp.m_dwStatus = pTC->m_dwStatus;
-          iCharWidth = pTC->m_iCharWidth;
-          if (iCharWidth > 0)
-            tp.m_iWidth += iCharWidth;
-
-          i++;
-        }
-        tp.m_iChars = i - tp.m_iStartChar;
-        m_pCurLine->m_LinePieces.push_back(tp);
-        tp.m_iStartPos += tp.m_iWidth;
-        tp.m_iStartChar = i;
-        tpo.index = ++j;
-        tpo.pos = tp.m_iBidiPos;
-        tpos->push_back(tpo);
-        iBidiLevel = -1;
-      } else {
-        iCharWidth = pTC->m_iCharWidth;
-        if (iCharWidth > 0)
-          tp.m_iWidth += iCharWidth;
-
-        i++;
-      }
-    }
-    if (i > tp.m_iStartChar) {
-      tp.m_dwStatus = dwStatus;
-      tp.m_iChars = i - tp.m_iStartChar;
-      m_pCurLine->m_LinePieces.push_back(tp);
-      tpo.index = ++j;
-      tpo.pos = tp.m_iBidiPos;
-      tpos->push_back(tpo);
-    }
-    if (j > -1) {
-      if (j > 0) {
-        std::sort(tpos->begin(), tpos->end());
-        int32_t iStartPos = 0;
-        for (i = 0; i <= j; i++) {
-          tpo = (*tpos)[i];
-          CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
-          ttp.m_iStartPos = iStartPos;
-          iStartPos += ttp.m_iWidth;
-        }
-      }
-      m_pCurLine->m_LinePieces[j].m_dwStatus = dwStatus;
-    }
-  } else {
+  if (!m_pCurLine->HasArabicChar()) {
     tp.m_dwStatus = dwStatus;
     tp.m_iStartPos = m_pCurLine->m_iStart;
     tp.m_iWidth = m_pCurLine->m_iWidth;
     tp.m_iStartChar = 0;
-    tp.m_iChars = iCount;
+    tp.m_iChars = m_pCurLine->m_LineChars.size();
     tp.m_pChars = &m_pCurLine->m_LineChars;
     pTC = &chars[0];
     tp.m_dwCharStyles = pTC->m_dwCharStyles;
@@ -389,6 +329,86 @@
     tp.m_iVerticalScale = pTC->vertical_scale();
     m_pCurLine->m_LinePieces.push_back(tp);
     tpos->push_back({0, 0});
+    return;
+  }
+
+  size_t iBidiNum = 0;
+  for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
+    pTC = &chars[i];
+    pTC->m_iBidiPos = static_cast<int32_t>(i);
+    if (pTC->GetCharType() != FX_CHARTYPE::kControl)
+      iBidiNum = i;
+    if (i == 0)
+      pTC->m_iBidiLevel = 1;
+  }
+  CFX_Char::BidiLine(&chars, iBidiNum + 1);
+
+  tp.m_dwStatus = CFX_BreakType::Piece;
+  tp.m_iStartPos = m_pCurLine->m_iStart;
+  tp.m_pChars = &m_pCurLine->m_LineChars;
+  int32_t iBidiLevel = -1;
+  int32_t iCharWidth;
+  int32_t i = 0;
+  int32_t j = -1;
+  int32_t iCount = pdfium::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
+  while (i < iCount) {
+    pTC = &chars[i];
+    if (iBidiLevel < 0) {
+      iBidiLevel = pTC->m_iBidiLevel;
+      tp.m_iWidth = 0;
+      tp.m_iBidiLevel = iBidiLevel;
+      tp.m_iBidiPos = pTC->m_iBidiOrder;
+      tp.m_dwCharStyles = pTC->m_dwCharStyles;
+      tp.m_iHorizontalScale = pTC->horizonal_scale();
+      tp.m_iVerticalScale = pTC->vertical_scale();
+      tp.m_dwStatus = CFX_BreakType::Piece;
+    }
+    if (iBidiLevel != pTC->m_iBidiLevel ||
+        pTC->m_dwStatus != CFX_BreakType::None) {
+      if (iBidiLevel == pTC->m_iBidiLevel) {
+        tp.m_dwStatus = pTC->m_dwStatus;
+        iCharWidth = pTC->m_iCharWidth;
+        if (iCharWidth > 0)
+          tp.m_iWidth += iCharWidth;
+
+        i++;
+      }
+      tp.m_iChars = i - tp.m_iStartChar;
+      m_pCurLine->m_LinePieces.push_back(tp);
+      tp.m_iStartPos += tp.m_iWidth;
+      tp.m_iStartChar = i;
+      tpo.index = ++j;
+      tpo.pos = tp.m_iBidiPos;
+      tpos->push_back(tpo);
+      iBidiLevel = -1;
+    } else {
+      iCharWidth = pTC->m_iCharWidth;
+      if (iCharWidth > 0)
+        tp.m_iWidth += iCharWidth;
+
+      i++;
+    }
+  }
+  if (i > tp.m_iStartChar) {
+    tp.m_dwStatus = dwStatus;
+    tp.m_iChars = i - tp.m_iStartChar;
+    m_pCurLine->m_LinePieces.push_back(tp);
+    tpo.index = ++j;
+    tpo.pos = tp.m_iBidiPos;
+    tpos->push_back(tpo);
+  }
+  if (j > -1) {
+    if (j > 0) {
+      std::sort(tpos->begin(), tpos->end());
+      int32_t iStartPos = 0;
+      for (i = 0; i <= j; i++) {
+        tpo = (*tpos)[i];
+        CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
+        ttp.m_iStartPos = iStartPos;
+        iStartPos += ttp.m_iWidth;
+      }
+    }
+    m_pCurLine->m_LinePieces[j].m_dwStatus = dwStatus;
   }
 }
 
@@ -407,11 +427,12 @@
     int32_t j = bArabic ? 0 : ttp.m_iChars - 1;
     while (j > -1 && j < ttp.m_iChars) {
       const CFX_Char* pTC = ttp.GetChar(j);
-      if (pTC->m_nBreakType == FX_LBT_DIRECT_BRK)
+      if (pTC->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
         iGapChars++;
       if (!bFind || !bAllChars) {
         FX_CHARTYPE chartype = pTC->GetCharType();
-        if (chartype == FX_CHARTYPE_Space || chartype == FX_CHARTYPE_Control) {
+        if (chartype == FX_CHARTYPE::kSpace ||
+            chartype == FX_CHARTYPE::kControl) {
           if (!bFind && bAllChars && pTC->m_iCharWidth > 0)
             iNetWidth -= pTC->m_iCharWidth;
         } else {
@@ -437,18 +458,16 @@
       else
         ttp.m_iStartPos = iStart;
 
-      for (int32_t j = 0; j < ttp.m_iChars; j++) {
+      for (int32_t j = 0; j < ttp.m_iChars && iGapChars > 0; j++, iGapChars--) {
         CFX_Char* pTC = ttp.GetChar(j);
-        if (pTC->m_nBreakType != FX_LBT_DIRECT_BRK || pTC->m_iCharWidth < 0)
+        if (pTC->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
+            pTC->m_iCharWidth < 0) {
           continue;
-
+        }
         int32_t k = iOffset / iGapChars;
         pTC->m_iCharWidth += k;
         ttp.m_iWidth += k;
         iOffset -= k;
-        iGapChars--;
-        if (iGapChars < 1)
-          break;
       }
       iStart += ttp.m_iWidth;
     }
@@ -475,24 +494,23 @@
   }
 
   if (HasLine()) {
-    if (!m_Line[m_iReadyLineIndex].m_LinePieces.empty()) {
-      if (dwStatus != CFX_BreakType::Piece)
-        m_Line[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
-      return m_Line[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
-    }
-    return CFX_BreakType::None;
+    if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
+      return CFX_BreakType::None;
+
+    if (dwStatus != CFX_BreakType::Piece)
+      m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
+    return m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
   }
 
-  int32_t iCount = m_pCurLine->CountChars();
-  if (iCount < 1)
+  if (m_pCurLine->m_LineChars.empty())
     return CFX_BreakType::None;
 
-  m_pCurLine->GetChar(iCount - 1)->m_dwStatus = dwStatus;
+  m_pCurLine->m_LineChars.back().m_dwStatus = dwStatus;
   if (dwStatus == CFX_BreakType::Piece)
     return dwStatus;
 
-  m_iReadyLineIndex = m_pCurLine == &m_Line[0] ? 0 : 1;
-  CFX_BreakLine* pNextLine = &m_Line[1 - m_iReadyLineIndex];
+  m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
+  CFX_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
   bool bAllChars = m_iAlignment > CFX_TxtLineAlignment_Right;
   if (!EndBreak_SplitLine(pNextLine, bAllChars)) {
     std::deque<FX_TPO> tpos;
@@ -503,16 +521,17 @@
 
   m_pCurLine = pNextLine;
   CFX_Char* pTC = GetLastChar(0, false, false);
-  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE_Unknown;
+  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
 
   return dwStatus;
 }
 
-int32_t CFX_TxtBreak::GetBreakPos(std::vector<CFX_Char>& ca,
-                                  int32_t& iEndPos,
+int32_t CFX_TxtBreak::GetBreakPos(std::vector<CFX_Char>* pChars,
                                   bool bAllChars,
-                                  bool bOnlyBrk) {
-  int32_t iLength = pdfium::CollectionSize<int32_t>(ca) - 1;
+                                  bool bOnlyBrk,
+                                  int32_t* pEndPos) {
+  std::vector<CFX_Char>& chars = *pChars;
+  int32_t iLength = pdfium::CollectionSize<int32_t>(chars) - 1;
   if (iLength < 1)
     return iLength;
 
@@ -522,74 +541,71 @@
   int32_t iIndirectPos = -1;
   int32_t iLast = -1;
   int32_t iLastPos = -1;
-  if (m_bSingleLine || iEndPos <= m_iLineWidth) {
+  if (m_bSingleLine || *pEndPos <= m_iLineWidth) {
     if (!bAllChars)
       return iLength;
 
     iBreak = iLength;
-    iBreakPos = iEndPos;
+    iBreakPos = *pEndPos;
   }
 
   FX_LINEBREAKTYPE eType;
-  uint32_t nCodeProp;
-  uint32_t nCur;
-  uint32_t nNext;
-  CFX_Char* pCur = &ca[iLength--];
+  FX_BREAKPROPERTY nCur;
+  FX_BREAKPROPERTY nNext;
+  CFX_Char* pCur = &chars[iLength--];
   if (bAllChars)
-    pCur->m_nBreakType = FX_LBT_UNKNOWN;
+    pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
 
-  nCodeProp = pCur->char_props();
-  nNext = nCodeProp & 0x003F;
+  nNext = FX_GetBreakProperty(pCur->char_code());
   int32_t iCharWidth = pCur->m_iCharWidth;
   if (iCharWidth > 0)
-    iEndPos -= iCharWidth;
+    *pEndPos -= iCharWidth;
 
   while (iLength >= 0) {
-    pCur = &ca[iLength];
-    nCodeProp = pCur->char_props();
-    nCur = nCodeProp & 0x003F;
-    if (nNext == kBreakPropertySpace)
-      eType = FX_LBT_PROHIBITED_BRK;
+    pCur = &chars[iLength];
+    nCur = FX_GetBreakProperty(pCur->char_code());
+    if (nNext == FX_BREAKPROPERTY::kSP)
+      eType = FX_LINEBREAKTYPE::kPROHIBITED_BRK;
     else
-      eType = gs_FX_LineBreak_PairTable[nCur][nNext];
+      eType = GetLineBreakTypeFromPair(nCur, nNext);
     if (bAllChars)
-      pCur->m_nBreakType = static_cast<uint8_t>(eType);
+      pCur->m_eLineBreakType = eType;
     if (!bOnlyBrk) {
-      if (m_bSingleLine || iEndPos <= m_iLineWidth ||
-          nCur == kBreakPropertySpace) {
-        if (eType == FX_LBT_DIRECT_BRK && iBreak < 0) {
+      if (m_bSingleLine || *pEndPos <= m_iLineWidth ||
+          nCur == FX_BREAKPROPERTY::kSP) {
+        if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
           iBreak = iLength;
-          iBreakPos = iEndPos;
+          iBreakPos = *pEndPos;
           if (!bAllChars)
             return iLength;
-        } else if (eType == FX_LBT_INDIRECT_BRK && iIndirect < 0) {
+        } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
           iIndirect = iLength;
-          iIndirectPos = iEndPos;
+          iIndirectPos = *pEndPos;
         }
         if (iLast < 0) {
           iLast = iLength;
-          iLastPos = iEndPos;
+          iLastPos = *pEndPos;
         }
       }
       iCharWidth = pCur->m_iCharWidth;
       if (iCharWidth > 0)
-        iEndPos -= iCharWidth;
+        *pEndPos -= iCharWidth;
     }
-    nNext = nCodeProp & 0x003F;
+    nNext = nCur;
     iLength--;
   }
   if (bOnlyBrk)
     return 0;
   if (iBreak > -1) {
-    iEndPos = iBreakPos;
+    *pEndPos = iBreakPos;
     return iBreak;
   }
   if (iIndirect > -1) {
-    iEndPos = iIndirectPos;
+    *pEndPos = iIndirectPos;
     return iIndirect;
   }
   if (iLast > -1) {
-    iEndPos = iLastPos;
+    *pEndPos = iLastPos;
     return iLast;
   }
   return 0;
@@ -598,22 +614,23 @@
 void CFX_TxtBreak::SplitTextLine(CFX_BreakLine* pCurLine,
                                  CFX_BreakLine* pNextLine,
                                  bool bAllChars) {
-  ASSERT(pCurLine && pNextLine);
-  int32_t iCount = pCurLine->CountChars();
-  if (iCount < 2)
+  ASSERT(pCurLine);
+  ASSERT(pNextLine);
+
+  if (pCurLine->m_LineChars.size() < 2)
     return;
 
   int32_t iEndPos = pCurLine->m_iWidth;
   std::vector<CFX_Char>& curChars = pCurLine->m_LineChars;
-  int32_t iCharPos = GetBreakPos(curChars, iEndPos, bAllChars, false);
+  int32_t iCharPos = GetBreakPos(&curChars, bAllChars, false, &iEndPos);
   if (iCharPos < 0)
     iCharPos = 0;
 
   iCharPos++;
-  if (iCharPos >= iCount) {
+  if (iCharPos >= pdfium::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
     pNextLine->Clear();
     CFX_Char* pTC = &curChars[iCharPos - 1];
-    pTC->m_nBreakType = FX_LBT_UNKNOWN;
+    pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
     return;
   }
 
@@ -622,13 +639,12 @@
   curChars.erase(curChars.begin() + iCharPos, curChars.end());
   pCurLine->m_iWidth = iEndPos;
   CFX_Char* pTC = &curChars[iCharPos - 1];
-  pTC->m_nBreakType = FX_LBT_UNKNOWN;
-  iCount = pdfium::CollectionSize<int>(pNextLine->m_LineChars);
+  pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
   int32_t iWidth = 0;
-  for (int32_t i = 0; i < iCount; i++) {
-    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE_ArabicAlef) {
-      pCurLine->m_iArabicChars--;
-      pNextLine->m_iArabicChars++;
+  for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
+    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
+      pCurLine->DecrementArabicCharCount();
+      pNextLine->IncrementArabicCharCount();
     }
     iWidth += std::max(0, pNextLine->m_LineChars[i].m_iCharWidth);
     pNextLine->m_LineChars[i].m_dwStatus = CFX_BreakType::None;
@@ -642,12 +658,12 @@
   int32_t iWidth;
 };
 
-int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun,
-                                    FXTEXT_CHARPOS* pCharPos) const {
+size_t CFX_TxtBreak::GetDisplayPos(const Run* pTxtRun,
+                                   TextCharPos* pCharPos) const {
   if (!pTxtRun || pTxtRun->iLength < 1)
     return 0;
 
-  CFDE_TextEditEngine* pEngine = pTxtRun->pEdtEngine;
+  Engine* pEngine = pTxtRun->pEdtEngine;
   const wchar_t* pStr = pTxtRun->wsStr.c_str();
   int32_t* pWidths = pTxtRun->pWidths;
   int32_t iLength = pTxtRun->iLength - 1;
@@ -656,12 +672,12 @@
   CFX_RectF rtText(*pTxtRun->pRect);
   bool bRTLPiece = (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel) != 0;
   float fFontSize = pTxtRun->fFontSize;
-  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
+  int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
   int32_t iAscent = pFont->GetAscent();
   int32_t iDescent = pFont->GetDescent();
   int32_t iMaxHeight = iAscent - iDescent;
   float fFontHeight = fFontSize;
-  float fAscent = fFontHeight * (float)iAscent / (float)iMaxHeight;
+  float fAscent = fFontHeight * iAscent / iMaxHeight;
   float fX = rtText.left;
   float fY;
   float fCharWidth;
@@ -677,7 +693,7 @@
   fYBase = rtText.top + (rtText.height - fFontSize) / 2.0f;
   fY = fYBase + fAscent;
 
-  int32_t iCount = 0;
+  size_t szCount = 0;
   int32_t iNext = 0;
   wchar_t wPrev = 0xFEFF;
   wchar_t wNext = 0xFEFF;
@@ -686,32 +702,32 @@
   bool bShadda = false;
   bool bLam = false;
   for (int32_t i = 0; i <= iLength; i++) {
+    int32_t iAbsolute = i + pTxtRun->iStart;
     int32_t iWidth;
     wchar_t wch;
     if (pEngine) {
-      wch = pEngine->GetChar(i);
-      iWidth = pEngine->GetWidthOfChar(i);
+      wch = pEngine->GetChar(iAbsolute);
+      iWidth = pEngine->GetWidthOfChar(iAbsolute);
     } else {
       wch = *pStr++;
       iWidth = *pWidths++;
     }
 
-    uint32_t dwProps = FX_GetUnicodeProperties(wch);
-    FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
-    if (chartype == FX_CHARTYPE_ArabicAlef && iWidth == 0) {
+    FX_CHARTYPE chartype = FX_GetCharType(wch);
+    if (chartype == FX_CHARTYPE::kArabicAlef && iWidth == 0) {
       wPrev = 0xFEFF;
       wLast = wch;
       continue;
     }
 
-    if (chartype >= FX_CHARTYPE_ArabicAlef) {
+    if (chartype >= FX_CHARTYPE::kArabicAlef) {
       if (i < iLength) {
         if (pEngine) {
           iNext = i + 1;
           while (iNext <= iLength) {
-            wNext = pEngine->GetChar(iNext);
-            dwProps = FX_GetUnicodeProperties(wNext);
-            if ((dwProps & FX_CHARTYPEBITSMASK) != FX_CHARTYPE_Combination)
+            int32_t iNextAbsolute = iNext + pTxtRun->iStart;
+            wNext = pEngine->GetChar(iNextAbsolute);
+            if (FX_GetCharType(wNext) != FX_CHARTYPE::kCombination)
               break;
 
             iNext++;
@@ -726,8 +742,7 @@
               break;
 
             wNext = pStr[j];
-            dwProps = FX_GetUnicodeProperties(wNext);
-          } while ((dwProps & FX_CHARTYPEBITSMASK) == FX_CHARTYPE_Combination);
+          } while (FX_GetCharType(wNext) == FX_CHARTYPE::kCombination);
           if (i + j >= iLength)
             wNext = 0xFEFF;
         }
@@ -737,7 +752,7 @@
 
       wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
       bLam = (wPrev == 0x0644 && wch == 0x0644 && wNext == 0x0647);
-    } else if (chartype == FX_CHARTYPE_Combination) {
+    } else if (chartype == FX_CHARTYPE::kCombination) {
       wForm = wch;
       if (wch >= 0x064C && wch <= 0x0651) {
         if (bShadda) {
@@ -747,8 +762,10 @@
           wNext = 0xFEFF;
           if (pEngine) {
             iNext = i + 1;
-            if (iNext <= iLength)
-              wNext = pEngine->GetChar(iNext);
+            if (iNext <= iLength) {
+              int32_t iNextAbsolute = iNext + pTxtRun->iStart;
+              wNext = pEngine->GetChar(iNextAbsolute);
+            }
           } else {
             if (i < iLength)
               wNext = *pStr;
@@ -768,30 +785,29 @@
       } else {
         bShadda = false;
       }
-    } else if (chartype == FX_CHARTYPE_Numeric) {
+    } else if (chartype == FX_CHARTYPE::kNumeric) {
       wForm = wch;
     } else if (wch == L'.') {
       wForm = wch;
     } else if (wch == L',') {
       wForm = wch;
     } else if (bRTLPiece) {
-      wForm = FX_GetMirrorChar(wch, dwProps);
+      wForm = FX_GetMirrorChar(wch);
     } else {
       wForm = wch;
     }
-    if (chartype != FX_CHARTYPE_Combination)
+    if (chartype != FX_CHARTYPE::kCombination)
       bShadda = false;
-    if (chartype < FX_CHARTYPE_ArabicAlef)
+    if (chartype < FX_CHARTYPE::kArabicAlef)
       bLam = false;
 
-    dwProps = FX_GetUnicodeProperties(wForm);
     bool bEmptyChar =
-        (chartype >= FX_CHARTYPE_Tab && chartype <= FX_CHARTYPE_Control);
+        (chartype >= FX_CHARTYPE::kTab && chartype <= FX_CHARTYPE::kControl);
     if (wForm == 0xFEFF)
       bEmptyChar = true;
 
     int32_t iForms = bLam ? 3 : 1;
-    iCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
+    szCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
     if (!pCharPos) {
       if (iWidth > 0)
         wPrev = wch;
@@ -810,11 +826,11 @@
     if (bLam) {
       formChars[1].wForm = 0x0651;
       iCharWidth = 0;
-      pFont->GetCharWidth(0x0651, iCharWidth);
+      pFont->GetCharWidth(0x0651, &iCharWidth);
       formChars[1].iWidth = iCharWidth;
       formChars[2].wForm = 0x0670;
       iCharWidth = 0;
-      pFont->GetCharWidth(0x0670, iCharWidth);
+      pFont->GetCharWidth(0x0670, &iCharWidth);
       formChars[2].iWidth = iCharWidth;
     }
 
@@ -822,20 +838,22 @@
       wForm = (wchar_t)formChars[j].wForm;
       iCharWidth = formChars[j].iWidth;
       if (j > 0) {
-        chartype = FX_CHARTYPE_Combination;
+        chartype = FX_CHARTYPE::kCombination;
         wch = wForm;
         wLast = (wchar_t)formChars[j - 1].wForm;
       }
       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
         pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm);
-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+#if defined(OS_MACOSX)
         pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
 #endif
+        // TODO(npm): change widths in this method to unsigned to avoid implicit
+        // cast in the following line.
         pCharPos->m_FontCharWidth = iCharWidth;
       }
 
       fCharWidth = fFontSize * iCharWidth / 1000.0f;
-      if (bRTLPiece && chartype != FX_CHARTYPE_Combination)
+      if (bRTLPiece && chartype != FX_CHARTYPE::kCombination)
         fX -= fCharWidth;
 
       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
@@ -843,30 +861,27 @@
 
         if ((dwStyles & FX_LAYOUTSTYLE_CombText) != 0) {
           int32_t iFormWidth = iCharWidth;
-          pFont->GetCharWidth(wForm, iFormWidth);
+          pFont->GetCharWidth(wForm, &iFormWidth);
           float fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
           pCharPos->m_Origin.x += fOffset;
         }
 
-        if (chartype == FX_CHARTYPE_Combination) {
-          CFX_Rect rtBBox;
+        if (chartype == FX_CHARTYPE::kCombination) {
+          FX_RECT rtBBox;
           if (pFont->GetCharBBox(wForm, &rtBBox)) {
             pCharPos->m_Origin.y =
-                fYBase + fFontSize -
-                fFontSize * (float)rtBBox.height / (float)iMaxHeight;
+                fYBase + fFontSize - fFontSize * rtBBox.Height() / iMaxHeight;
           }
           if (wForm == wch && wLast != 0xFEFF) {
-            uint32_t dwLastProps = FX_GetUnicodeProperties(wLast);
-            if ((dwLastProps & FX_CHARTYPEBITSMASK) ==
-                FX_CHARTYPE_Combination) {
-              CFX_Rect rtBox;
+            if (FX_GetCharType(wLast) == FX_CHARTYPE::kCombination) {
+              FX_RECT rtBox;
               if (pFont->GetCharBBox(wLast, &rtBox))
-                pCharPos->m_Origin.y -= fFontSize * rtBox.height / iMaxHeight;
+                pCharPos->m_Origin.y -= fFontSize * rtBox.Height() / iMaxHeight;
             }
           }
         }
       }
-      if (!bRTLPiece && chartype != FX_CHARTYPE_Combination)
+      if (!bRTLPiece && chartype != FX_CHARTYPE::kCombination)
         fX += fCharWidth;
 
       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
@@ -893,32 +908,31 @@
       wPrev = static_cast<wchar_t>(formChars[0].wch);
     wLast = wch;
   }
-  return iCount;
+  return szCount;
 }
 
-std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const FX_TXTRUN* pTxtRun,
+std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const Run* pTxtRun,
                                                   bool bCharBBox) const {
   if (!pTxtRun || pTxtRun->iLength < 1)
     return std::vector<CFX_RectF>();
 
-  CFDE_TextEditEngine* pEngine = pTxtRun->pEdtEngine;
+  Engine* pEngine = pTxtRun->pEdtEngine;
   const wchar_t* pStr = pTxtRun->wsStr.c_str();
   int32_t* pWidths = pTxtRun->pWidths;
   int32_t iLength = pTxtRun->iLength;
   CFX_RectF rect(*pTxtRun->pRect);
   float fFontSize = pTxtRun->fFontSize;
-  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
   float fScale = fFontSize / 1000.0f;
   RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
   if (!pFont)
     bCharBBox = false;
 
-  CFX_Rect bbox;
+  FX_RECT bbox;
   if (bCharBBox)
     bCharBBox = pFont->GetBBox(&bbox);
 
   float fLeft = std::max(0.0f, bbox.left * fScale);
-  float fHeight = fabs(bbox.height * fScale);
+  float fHeight = fabs(bbox.Height() * fScale);
   bool bRTLPiece = !!(pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel);
   bool bSingleLine = !!(pTxtRun->dwStyles & FX_LAYOUTSTYLE_SingleLine);
   bool bCombText = !!(pTxtRun->dwStyles & FX_LAYOUTSTYLE_CombText);
@@ -929,23 +943,22 @@
 
   std::vector<CFX_RectF> rtArray(iLength);
   for (int32_t i = 0; i < iLength; i++) {
+    int32_t iAbsolute = i + pTxtRun->iStart;
     if (pEngine) {
-      wch = pEngine->GetChar(i);
-      iCharSize = pEngine->GetWidthOfChar(i);
+      wch = pEngine->GetChar(iAbsolute);
+      iCharSize = pEngine->GetWidthOfChar(iAbsolute);
     } else {
       wch = *pStr++;
       iCharSize = *pWidths++;
     }
-    fCharSize = static_cast<float>(iCharSize) / 20000.0f;
+    fCharSize = static_cast<float>(iCharSize) / kConversionFactor;
     bool bRet = (!bSingleLine && IsCtrlCode(wch));
     if (!(wch == L'\v' || wch == L'\f' || wch == 0x2028 || wch == 0x2029 ||
           wch == L'\n')) {
       bRet = false;
     }
-    if (bRet) {
-      iCharSize = iFontSize * 500;
+    if (bRet)
       fCharSize = fFontSize / 2.0f;
-    }
     rect.left = fStart;
     if (bRTLPiece) {
       rect.left -= fCharSize;
@@ -957,7 +970,7 @@
 
     if (bCharBBox && !bRet) {
       int32_t iCharWidth = 1000;
-      pFont->GetCharWidth(wch, iCharWidth);
+      pFont->GetCharWidth(wch, &iCharWidth);
       float fRTLeft = 0, fCharWidth = 0;
       if (iCharWidth > 0) {
         fCharWidth = iCharWidth * fScale;
@@ -979,20 +992,10 @@
   return rtArray;
 }
 
-FX_TXTRUN::FX_TXTRUN()
-    : pEdtEngine(nullptr),
-      pIdentity(nullptr),
-      pWidths(nullptr),
-      iLength(0),
-      pFont(nullptr),
-      fFontSize(12),
-      dwStyles(0),
-      iHorizontalScale(100),
-      iVerticalScale(100),
-      dwCharStyles(0),
-      pRect(nullptr),
-      bSkipSpace(true) {}
+CFX_TxtBreak::Engine::~Engine() = default;
 
-FX_TXTRUN::~FX_TXTRUN() {}
+CFX_TxtBreak::Run::Run() = default;
 
-FX_TXTRUN::FX_TXTRUN(const FX_TXTRUN& other) = default;
+CFX_TxtBreak::Run::~Run() = default;
+
+CFX_TxtBreak::Run::Run(const CFX_TxtBreak::Run& other) = default;
diff --git a/xfa/fgas/layout/cfx_txtbreak.h b/xfa/fgas/layout/cfx_txtbreak.h
index e6f8da0..7f3d4ac 100644
--- a/xfa/fgas/layout/cfx_txtbreak.h
+++ b/xfa/fgas/layout/cfx_txtbreak.h
@@ -8,17 +8,14 @@
 #define XFA_FGAS_LAYOUT_CFX_TXTBREAK_H_
 
 #include <deque>
-#include <memory>
 #include <vector>
 
-#include "core/fxcrt/cfx_char.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "xfa/fgas/layout/cfx_break.h"
+#include "xfa/fgas/layout/cfx_char.h"
 
-class CFDE_TextEditEngine;
 class CFGAS_GEFont;
-struct FDE_TEXTEDITPIECE;
+class TextCharPos;
 
 #define FX_TXTCHARSTYLE_ArabicShadda 0x0020
 #define FX_TXTCHARSTYLE_OddBidiLevel 0x0040
@@ -34,28 +31,36 @@
   return type == CFX_BreakType::None || type == CFX_BreakType::Piece;
 }
 
-struct FX_TXTRUN {
-  FX_TXTRUN();
-  FX_TXTRUN(const FX_TXTRUN& other);
-  ~FX_TXTRUN();
-
-  CFDE_TextEditEngine* pEdtEngine;
-  const FDE_TEXTEDITPIECE* pIdentity;
-  WideString wsStr;
-  int32_t* pWidths;
-  int32_t iLength;
-  RetainPtr<CFGAS_GEFont> pFont;
-  float fFontSize;
-  uint32_t dwStyles;
-  int32_t iHorizontalScale;
-  int32_t iVerticalScale;
-  uint32_t dwCharStyles;
-  const CFX_RectF* pRect;
-  bool bSkipSpace;
-};
-
-class CFX_TxtBreak : public CFX_Break {
+class CFX_TxtBreak final : public CFX_Break {
  public:
+  class Engine {
+   public:
+    virtual ~Engine();
+    virtual wchar_t GetChar(size_t idx) const = 0;
+    // Non-const so we can force a layout if needed.
+    virtual size_t GetWidthOfChar(size_t idx) = 0;
+  };
+
+  struct Run {
+    Run();
+    Run(const Run& other);
+    ~Run();
+
+    CFX_TxtBreak::Engine* pEdtEngine = nullptr;
+    WideString wsStr;
+    int32_t* pWidths = nullptr;
+    int32_t iStart = 0;
+    int32_t iLength = 0;
+    RetainPtr<CFGAS_GEFont> pFont;
+    float fFontSize = 12.0f;
+    uint32_t dwStyles = 0;
+    int32_t iHorizontalScale = 100;
+    int32_t iVerticalScale = 100;
+    uint32_t dwCharStyles = 0;
+    const CFX_RectF* pRect = nullptr;
+    bool bSkipSpace = true;
+  };
+
   CFX_TxtBreak();
   ~CFX_TxtBreak() override;
 
@@ -64,10 +69,8 @@
   void SetCombWidth(float fCombWidth);
   CFX_BreakType EndBreak(CFX_BreakType dwStatus);
 
-  int32_t GetDisplayPos(const FX_TXTRUN* pTxtRun,
-                        FXTEXT_CHARPOS* pCharPos) const;
-  std::vector<CFX_RectF> GetCharRects(const FX_TXTRUN* pTxtRun,
-                                      bool bCharBBox = false) const;
+  size_t GetDisplayPos(const Run* pTxtRun, TextCharPos* pCharPos) const;
+  std::vector<CFX_RectF> GetCharRects(const Run* pTxtRun, bool bCharBBox) const;
   CFX_BreakType AppendChar(wchar_t wch);
 
  private:
@@ -83,13 +86,13 @@
   void EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
                           bool bAllChars,
                           CFX_BreakType dwStatus);
-  int32_t GetBreakPos(std::vector<CFX_Char>& ca,
-                      int32_t& iEndPos,
-                      bool bAllChars = false,
-                      bool bOnlyBrk = false);
+  int32_t GetBreakPos(std::vector<CFX_Char>* pChars,
+                      bool bAllChars,
+                      bool bOnlyBrk,
+                      int32_t* pEndPos);
   void SplitTextLine(CFX_BreakLine* pCurLine,
                      CFX_BreakLine* pNextLine,
-                     bool bAllChars = false);
+                     bool bAllChars);
 
   int32_t m_iAlignment;
   int32_t m_iCombWidth;
diff --git a/xfa/fgas/layout/cfx_txtbreak_unittest.cpp b/xfa/fgas/layout/cfx_txtbreak_unittest.cpp
new file mode 100644
index 0000000..a3ae207
--- /dev/null
+++ b/xfa/fgas/layout/cfx_txtbreak_unittest.cpp
@@ -0,0 +1,49 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fgas/layout/cfx_txtbreak.h"
+
+#include <memory>
+#include <utility>
+
+#include "core/fxge/cfx_font.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_unit_test_support.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
+#include "xfa/fgas/layout/cfx_char.h"
+
+class CFX_TxtBreakTest : public testing::Test {
+ public:
+  void SetUp() override {
+    font_ =
+        CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager());
+    ASSERT_TRUE(font_.Get());
+  }
+
+  std::unique_ptr<CFX_TxtBreak> CreateBreak() {
+    auto txt_break = pdfium::MakeUnique<CFX_TxtBreak>();
+    txt_break->SetFont(font_);
+    return txt_break;
+  }
+
+ private:
+  RetainPtr<CFGAS_GEFont> font_;
+};
+
+TEST_F(CFX_TxtBreakTest, BidiLine) {
+  auto txt_break = CreateBreak();
+  txt_break->SetLineBreakTolerance(1);
+  txt_break->SetFontSize(12);
+
+  WideString input = WideString::FromUTF8(ByteStringView("\xa\x0\xa\xa", 4));
+  for (wchar_t ch : input)
+    txt_break->AppendChar(ch);
+
+  std::vector<CFX_Char> chars =
+      txt_break->GetCurrentLineForTesting()->m_LineChars;
+  CFX_Char::BidiLine(&chars, chars.size());
+  EXPECT_EQ(3u, chars.size());
+}
diff --git a/xfa/fgas/layout/fx_arabic.cpp b/xfa/fgas/layout/fx_arabic.cpp
new file mode 100644
index 0000000..886bb0d
--- /dev/null
+++ b/xfa/fgas/layout/fx_arabic.cpp
@@ -0,0 +1,231 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/fx_arabic.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_unicode.h"
+
+namespace {
+
+struct FX_ARBFORMTABLE {
+  uint16_t wIsolated;
+  uint16_t wFinal;
+  uint16_t wInitial;
+  uint16_t wMedial;
+};
+
+struct FX_ARAALEF {
+  uint16_t wAlef;
+  uint16_t wIsolated;
+};
+
+struct FX_ARASHADDA {
+  uint16_t wShadda;
+  uint16_t wIsolated;
+};
+
+const FX_ARBFORMTABLE g_FX_ArabicFormTables[] = {
+    {0xFE81, 0xFE82, 0xFE81, 0xFE82}, {0xFE83, 0xFE84, 0xFE83, 0xFE84},
+    {0xFE85, 0xFE86, 0xFE85, 0xFE86}, {0xFE87, 0xFE88, 0xFE87, 0xFE88},
+    {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}, {0xFE8D, 0xFE8E, 0xFE8D, 0xFE8E},
+    {0xFE8F, 0xFE90, 0xFE91, 0xFE92}, {0xFE93, 0xFE94, 0xFE93, 0xFE94},
+    {0xFE95, 0xFE96, 0xFE97, 0xFE98}, {0xFE99, 0xFE9A, 0xFE9B, 0xFE9C},
+    {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}, {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4},
+    {0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}, {0xFEA9, 0xFEAA, 0xFEA9, 0xFEAA},
+    {0xFEAB, 0xFEAC, 0xFEAB, 0xFEAC}, {0xFEAD, 0xFEAE, 0xFEAD, 0xFEAE},
+    {0xFEAF, 0xFEB0, 0xFEAF, 0xFEB0}, {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4},
+    {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}, {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC},
+    {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}, {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4},
+    {0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}, {0xFEC9, 0xFECA, 0xFECB, 0xFECC},
+    {0xFECD, 0xFECE, 0xFECF, 0xFED0}, {0x063B, 0x063B, 0x063B, 0x063B},
+    {0x063C, 0x063C, 0x063C, 0x063C}, {0x063D, 0x063D, 0x063D, 0x063D},
+    {0x063E, 0x063E, 0x063E, 0x063E}, {0x063F, 0x063F, 0x063F, 0x063F},
+    {0x0640, 0x0640, 0x0640, 0x0640}, {0xFED1, 0xFED2, 0xFED3, 0xFED4},
+    {0xFED5, 0xFED6, 0xFED7, 0xFED8}, {0xFED9, 0xFEDA, 0xFEDB, 0xFEDC},
+    {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}, {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4},
+    {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}, {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC},
+    {0xFEED, 0xFEEE, 0xFEED, 0xFEEE}, {0xFEEF, 0xFEF0, 0xFBFE, 0xFBFF},
+    {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}, {0x064B, 0x064B, 0x064B, 0x064B},
+    {0x064C, 0x064C, 0x064C, 0x064C}, {0x064D, 0x064D, 0x064D, 0x064D},
+    {0x064E, 0x064E, 0x064E, 0x064E}, {0x064F, 0x064F, 0x064F, 0x064F},
+    {0x0650, 0x0650, 0x0650, 0x0650}, {0x0651, 0x0651, 0x0651, 0x0651},
+    {0x0652, 0x0652, 0x0652, 0x0652}, {0x0653, 0x0653, 0x0653, 0x0653},
+    {0x0654, 0x0654, 0x0654, 0x0654}, {0x0655, 0x0655, 0x0655, 0x0655},
+    {0x0656, 0x0656, 0x0656, 0x0656}, {0x0657, 0x0657, 0x0657, 0x0657},
+    {0x0658, 0x0658, 0x0658, 0x0658}, {0x0659, 0x0659, 0x0659, 0x0659},
+    {0x065A, 0x065A, 0x065A, 0x065A}, {0x065B, 0x065B, 0x065B, 0x065B},
+    {0x065C, 0x065C, 0x065C, 0x065C}, {0x065D, 0x065D, 0x065D, 0x065D},
+    {0x065E, 0x065E, 0x065E, 0x065E}, {0x065F, 0x065F, 0x065F, 0x065F},
+    {0x0660, 0x0660, 0x0660, 0x0660}, {0x0661, 0x0661, 0x0661, 0x0661},
+    {0x0662, 0x0662, 0x0662, 0x0662}, {0x0663, 0x0663, 0x0663, 0x0663},
+    {0x0664, 0x0664, 0x0664, 0x0664}, {0x0665, 0x0665, 0x0665, 0x0665},
+    {0x0666, 0x0666, 0x0666, 0x0666}, {0x0667, 0x0667, 0x0667, 0x0667},
+    {0x0668, 0x0668, 0x0668, 0x0668}, {0x0669, 0x0669, 0x0669, 0x0669},
+    {0x066A, 0x066A, 0x066A, 0x066A}, {0x066B, 0x066B, 0x066B, 0x066B},
+    {0x066C, 0x066C, 0x066C, 0x066C}, {0x066D, 0x066D, 0x066D, 0x066D},
+    {0x066E, 0x066E, 0x066E, 0x066E}, {0x066F, 0x066F, 0x066F, 0x066F},
+    {0x0670, 0x0670, 0x0670, 0x0670}, {0xFB50, 0xFB51, 0xFB50, 0xFB51},
+    {0x0672, 0x0672, 0x0672, 0x0672}, {0x0673, 0x0673, 0x0673, 0x0673},
+    {0x0674, 0x0674, 0x0674, 0x0674}, {0x0675, 0x0675, 0x0675, 0x0675},
+    {0x0676, 0x0676, 0x0676, 0x0676}, {0x0677, 0x0677, 0x0677, 0x0677},
+    {0x0678, 0x0678, 0x0678, 0x0678}, {0xFB66, 0xFB67, 0xFB68, 0xFB69},
+    {0xFB5E, 0xFB5F, 0xFB60, 0xFB61}, {0xFB52, 0xFB53, 0xFB54, 0xFB55},
+    {0x067C, 0x067C, 0x067C, 0x067C}, {0x067D, 0x067D, 0x067D, 0x067D},
+    {0xFB56, 0xFB57, 0xFB58, 0xFB59}, {0xFB62, 0xFB63, 0xFB64, 0xFB65},
+    {0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}, {0x0681, 0x0681, 0x0681, 0x0681},
+    {0x0682, 0x0682, 0x0682, 0x0682}, {0xFB76, 0xFB77, 0xFB78, 0xFB79},
+    {0xFB72, 0xFB73, 0xFB74, 0xFB75}, {0x0685, 0x0685, 0x0685, 0x0685},
+    {0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}, {0xFB7E, 0xFB7F, 0xFB80, 0xFB81},
+    {0xFB88, 0xFB89, 0xFB88, 0xFB89}, {0x0689, 0x0689, 0x0689, 0x0689},
+    {0x068A, 0x068A, 0x068A, 0x068A}, {0x068B, 0x068B, 0x068B, 0x068B},
+    {0xFB84, 0xFB85, 0xFB84, 0xFB85}, {0xFB82, 0xFB83, 0xFB82, 0xFB83},
+    {0xFB86, 0xFB87, 0xFB86, 0xFB87}, {0x068F, 0x068F, 0x068F, 0x068F},
+    {0x0690, 0x0690, 0x0690, 0x0690}, {0xFB8C, 0xFB8D, 0xFB8C, 0xFB8D},
+    {0x0692, 0x0692, 0x0692, 0x0692}, {0x0693, 0x0693, 0x0693, 0x0693},
+    {0x0694, 0x0694, 0x0694, 0x0694}, {0x0695, 0x0695, 0x0695, 0x0695},
+    {0x0696, 0x0696, 0x0696, 0x0696}, {0x0697, 0x0697, 0x0697, 0x0697},
+    {0xFB8A, 0xFB8B, 0xFB8A, 0xFB8B}, {0x0699, 0x0699, 0x0699, 0x0699},
+    {0x069A, 0x069A, 0x069A, 0x069A}, {0x069B, 0x069B, 0x069B, 0x069B},
+    {0x069C, 0x069C, 0x069C, 0x069C}, {0x069D, 0x069D, 0x069D, 0x069D},
+    {0x069E, 0x069E, 0x069E, 0x069E}, {0x069F, 0x069F, 0x069F, 0x069F},
+    {0x06A0, 0x06A0, 0x06A0, 0x06A0}, {0x06A1, 0x06A1, 0x06A1, 0x06A1},
+    {0x06A2, 0x06A2, 0x06A2, 0x06A2}, {0x06A3, 0x06A3, 0x06A3, 0x06A3},
+    {0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}, {0x06A5, 0x06A5, 0x06A5, 0x06A5},
+    {0xFB6E, 0xFB6F, 0xFB70, 0xFB71}, {0x06A7, 0x06A7, 0x06A7, 0x06A7},
+    {0x06A8, 0x06A8, 0x06A8, 0x06A8}, {0xFB8E, 0xFB8F, 0xFB90, 0xFB91},
+    {0x06AA, 0x06AA, 0x06AA, 0x06AA}, {0x06AB, 0x06AB, 0x06AB, 0x06AB},
+    {0x06AC, 0x06AC, 0x06AC, 0x06AC}, {0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6},
+    {0x06AE, 0x06AE, 0x06AE, 0x06AE}, {0xFB92, 0xFB93, 0xFB94, 0xFB95},
+    {0x06B0, 0x06B0, 0x06B0, 0x06B0}, {0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D},
+    {0x06B2, 0x06B2, 0x06B2, 0x06B2}, {0xFB96, 0xFB97, 0xFB98, 0xFB99},
+    {0x06B4, 0x06B4, 0x06B4, 0x06B4}, {0x06B5, 0x06B5, 0x06B5, 0x06B5},
+    {0x06B6, 0x06B6, 0x06B6, 0x06B6}, {0x06B7, 0x06B7, 0x06B7, 0x06B7},
+    {0x06B8, 0x06B8, 0x06B8, 0x06B8}, {0x06B9, 0x06B9, 0x06B9, 0x06B9},
+    {0xFB9E, 0xFB9F, 0xFBE8, 0xFBE9}, {0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3},
+    {0x06BC, 0x06BC, 0x06BC, 0x06BC}, {0x06BD, 0x06BD, 0x06BD, 0x06BD},
+    {0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}, {0x06BF, 0x06BF, 0x06BF, 0x06BF},
+    {0xFBA4, 0xFBA5, 0xFBA4, 0xFBA5}, {0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9},
+    {0x06C2, 0x06C2, 0x06C2, 0x06C2}, {0x06C3, 0x06C3, 0x06C3, 0x06C3},
+    {0x06C4, 0x06C4, 0x06C4, 0x06C4}, {0xFBE0, 0xFBE1, 0xFBE0, 0xFBE1},
+    {0xFBD9, 0xFBDA, 0xFBD9, 0xFBDA}, {0xFBD7, 0xFBD8, 0xFBD7, 0xFBD8},
+    {0xFBDB, 0xFBDC, 0xFBDB, 0xFBDC}, {0xFBE2, 0xFBE3, 0xFBE2, 0xFBE3},
+    {0x06CA, 0x06CA, 0x06CA, 0x06CA}, {0xFBDE, 0xFBDF, 0xFBDE, 0xFBDF},
+    {0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}, {0x06CD, 0x06CD, 0x06CD, 0x06CD},
+    {0x06CE, 0x06CE, 0x06CE, 0x06CE}, {0x06CF, 0x06CF, 0x06CF, 0x06CF},
+    {0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}, {0x06D1, 0x06D1, 0x06D1, 0x06D1},
+    {0xFBAE, 0xFBAF, 0xFBAE, 0xFBAF}, {0xFBB0, 0xFBB1, 0xFBB0, 0xFBB1},
+    {0x06D4, 0x06D4, 0x06D4, 0x06D4}, {0x06D5, 0x06D5, 0x06D5, 0x06D5},
+};
+
+const FX_ARAALEF gs_FX_AlefTable[] = {
+    {0x0622, 0xFEF5},
+    {0x0623, 0xFEF7},
+    {0x0625, 0xFEF9},
+    {0x0627, 0xFEFB},
+};
+
+const FX_ARASHADDA gs_FX_ShaddaTable[] = {
+    {0x064C, 0xFC5E}, {0x064D, 0xFC5F}, {0x064E, 0xFC60},
+    {0x064F, 0xFC61}, {0x0650, 0xFC62},
+};
+
+const FX_ARBFORMTABLE* GetArabicFormTable(wchar_t unicode) {
+  if (unicode < 0x622 || unicode > 0x6d5)
+    return nullptr;
+  return g_FX_ArabicFormTables + unicode - 0x622;
+}
+
+const FX_ARBFORMTABLE* ParseChar(const CFX_Char* pTC,
+                                 wchar_t* wChar,
+                                 FX_CHARTYPE* eType) {
+  if (!pTC) {
+    *eType = FX_CHARTYPE::kUnknown;
+    *wChar = 0xFEFF;
+    return nullptr;
+  }
+
+  *eType = pTC->GetCharType();
+  *wChar = static_cast<wchar_t>(pTC->char_code());
+  const FX_ARBFORMTABLE* pFT = GetArabicFormTable(*wChar);
+  if (!pFT || *eType >= FX_CHARTYPE::kArabicNormal)
+    *eType = FX_CHARTYPE::kUnknown;
+
+  return pFT;
+}
+
+wchar_t GetArabicFromAlefTable(wchar_t alef) {
+  static const size_t s_iAlefCount = FX_ArraySize(gs_FX_AlefTable);
+  for (size_t iStart = 0; iStart < s_iAlefCount; iStart++) {
+    const FX_ARAALEF& v = gs_FX_AlefTable[iStart];
+    if (v.wAlef == alef)
+      return v.wIsolated;
+  }
+  return alef;
+}
+
+}  // namespace
+
+namespace pdfium {
+namespace arabic {
+
+wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next) {
+  CFX_Char c(wch);
+  CFX_Char p(prev);
+  CFX_Char n(next);
+  return GetFormChar(&c, &p, &n);
+}
+
+wchar_t GetFormChar(const CFX_Char* cur,
+                    const CFX_Char* prev,
+                    const CFX_Char* next) {
+  FX_CHARTYPE eCur;
+  wchar_t wCur;
+  const FX_ARBFORMTABLE* ft = ParseChar(cur, &wCur, &eCur);
+  if (eCur < FX_CHARTYPE::kArabicAlef || eCur >= FX_CHARTYPE::kArabicNormal)
+    return wCur;
+
+  FX_CHARTYPE ePrev;
+  wchar_t wPrev;
+  ParseChar(prev, &wPrev, &ePrev);
+  if (wPrev == 0x0644 && eCur == FX_CHARTYPE::kArabicAlef)
+    return 0xFEFF;
+
+  FX_CHARTYPE eNext;
+  wchar_t wNext;
+  ParseChar(next, &wNext, &eNext);
+  bool bAlef = (eNext == FX_CHARTYPE::kArabicAlef && wCur == 0x644);
+  if (ePrev < FX_CHARTYPE::kArabicAlef) {
+    if (bAlef)
+      return GetArabicFromAlefTable(wNext);
+    return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wIsolated : ft->wInitial;
+  }
+
+  if (bAlef) {
+    wCur = GetArabicFromAlefTable(wNext);
+    return (ePrev != FX_CHARTYPE::kArabicDistortion) ? wCur : ++wCur;
+  }
+
+  if (ePrev == FX_CHARTYPE::kArabicAlef || ePrev == FX_CHARTYPE::kArabicSpecial)
+    return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wIsolated : ft->wInitial;
+  return (eNext < FX_CHARTYPE::kArabicAlef) ? ft->wFinal : ft->wMedial;
+}
+
+}  // namespace arabic
+}  // namespace pdfium
+
+wchar_t FX_GetArabicFromShaddaTable(wchar_t shadda) {
+  static const size_t s_iShaddaCount = FX_ArraySize(gs_FX_ShaddaTable);
+  for (size_t iStart = 0; iStart < s_iShaddaCount; iStart++) {
+    const FX_ARASHADDA& v = gs_FX_ShaddaTable[iStart];
+    if (v.wShadda == shadda)
+      return v.wIsolated;
+  }
+  return shadda;
+}
diff --git a/xfa/fgas/layout/fx_arabic.h b/xfa/fgas/layout/fx_arabic.h
new file mode 100644
index 0000000..ca33aa5
--- /dev/null
+++ b/xfa/fgas/layout/fx_arabic.h
@@ -0,0 +1,26 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_FX_ARABIC_H_
+#define XFA_FGAS_LAYOUT_FX_ARABIC_H_
+
+#include "core/fxcrt/fx_system.h"
+#include "xfa/fgas/layout/cfx_char.h"
+
+namespace pdfium {
+namespace arabic {
+
+wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next);
+wchar_t GetFormChar(const CFX_Char* cur,
+                    const CFX_Char* prev,
+                    const CFX_Char* next);
+
+}  // namespace arabic
+}  // namespace pdfium
+
+wchar_t FX_GetArabicFromShaddaTable(wchar_t shadda);
+
+#endif  // XFA_FGAS_LAYOUT_FX_ARABIC_H_
diff --git a/xfa/fgas/layout/fx_linebreak.cpp b/xfa/fgas/layout/fx_linebreak.cpp
new file mode 100644
index 0000000..96821f1
--- /dev/null
+++ b/xfa/fgas/layout/fx_linebreak.cpp
@@ -0,0 +1,232 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fgas/layout/fx_linebreak.h"
+
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_unicode.h"
+
+namespace {
+
+#define FX_LBUN FX_LINEBREAKTYPE::kUNKNOWN
+#define FX_LBDB FX_LINEBREAKTYPE::kDIRECT_BRK
+#define FX_LBIB FX_LINEBREAKTYPE::kINDIRECT_BRK
+#define FX_LBCB FX_LINEBREAKTYPE::kCOM_INDIRECT_BRK
+#define FX_LBCP FX_LINEBREAKTYPE::kCOM_PROHIBITED_BRK
+#define FX_LBPB FX_LINEBREAKTYPE::kPROHIBITED_BRK
+#define FX_LBHS FX_LINEBREAKTYPE::kHANGUL_SPACE_BRK
+
+const FX_LINEBREAKTYPE gs_FX_LineBreak_PairTable[38][38] = {
+    {FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBPB, FX_LBPB, FX_LBPB, FX_LBCP, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBPB, FX_LBPB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBPB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBPB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBIB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBIB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBIB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBIB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBIB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBPB, FX_LBDB, FX_LBPB,
+     FX_LBDB, FX_LBIB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBPB, FX_LBPB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBPB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+    {FX_LBDB, FX_LBPB, FX_LBIB, FX_LBDB, FX_LBIB, FX_LBPB, FX_LBPB, FX_LBPB,
+     FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBDB, FX_LBIB, FX_LBIB,
+     FX_LBDB, FX_LBDB, FX_LBPB, FX_LBCB, FX_LBPB, FX_LBDB, FX_LBDB, FX_LBDB,
+     FX_LBDB, FX_LBDB, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN,
+     FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN, FX_LBUN},
+};
+
+#undef FX_LBUN
+#undef FX_LBDB
+#undef FX_LBIB
+#undef FX_LBCB
+#undef FX_LBCP
+#undef FX_LBPB
+#undef FX_LBHS
+
+}  // namespace
+
+FX_LINEBREAKTYPE GetLineBreakTypeFromPair(FX_BREAKPROPERTY curr_char,
+                                          FX_BREAKPROPERTY next_char) {
+  size_t row = static_cast<size_t>(curr_char);
+  size_t col = static_cast<size_t>(next_char);
+  ASSERT(row < FX_ArraySize(gs_FX_LineBreak_PairTable));
+  ASSERT(col < FX_ArraySize(gs_FX_LineBreak_PairTable[0]));
+  return gs_FX_LineBreak_PairTable[row][col];
+}
diff --git a/xfa/fgas/layout/fx_linebreak.h b/xfa/fgas/layout/fx_linebreak.h
new file mode 100644
index 0000000..ac46a35
--- /dev/null
+++ b/xfa/fgas/layout/fx_linebreak.h
@@ -0,0 +1,26 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FGAS_LAYOUT_FX_LINEBREAK_H_
+#define XFA_FGAS_LAYOUT_FX_LINEBREAK_H_
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_unicode.h"
+
+enum class FX_LINEBREAKTYPE : uint8_t {
+  kUNKNOWN = 0x00,
+  kDIRECT_BRK = 0x1A,
+  kINDIRECT_BRK = 0x2B,
+  kCOM_INDIRECT_BRK = 0x3C,
+  kCOM_PROHIBITED_BRK = 0x4D,
+  kPROHIBITED_BRK = 0x5E,
+  kHANGUL_SPACE_BRK = 0x6F,
+};
+
+FX_LINEBREAKTYPE GetLineBreakTypeFromPair(FX_BREAKPROPERTY curr_char,
+                                          FX_BREAKPROPERTY next_char);
+
+#endif  // XFA_FGAS_LAYOUT_FX_LINEBREAK_H_
diff --git a/xfa/fwl/BUILD.gn b/xfa/fwl/BUILD.gn
new file mode 100644
index 0000000..765704d
--- /dev/null
+++ b/xfa/fwl/BUILD.gn
@@ -0,0 +1,130 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fwl") {
+  sources = [
+    "cfwl_app.cpp",
+    "cfwl_app.h",
+    "cfwl_barcode.cpp",
+    "cfwl_barcode.h",
+    "cfwl_caret.cpp",
+    "cfwl_caret.h",
+    "cfwl_checkbox.cpp",
+    "cfwl_checkbox.h",
+    "cfwl_combobox.cpp",
+    "cfwl_combobox.h",
+    "cfwl_comboedit.cpp",
+    "cfwl_comboedit.h",
+    "cfwl_combolist.cpp",
+    "cfwl_combolist.h",
+    "cfwl_datetimeedit.cpp",
+    "cfwl_datetimeedit.h",
+    "cfwl_datetimepicker.cpp",
+    "cfwl_datetimepicker.h",
+    "cfwl_edit.cpp",
+    "cfwl_edit.h",
+    "cfwl_event.cpp",
+    "cfwl_event.h",
+    "cfwl_eventmouse.cpp",
+    "cfwl_eventmouse.h",
+    "cfwl_eventscroll.cpp",
+    "cfwl_eventscroll.h",
+    "cfwl_eventselectchanged.cpp",
+    "cfwl_eventselectchanged.h",
+    "cfwl_eventtarget.cpp",
+    "cfwl_eventtarget.h",
+    "cfwl_eventtextwillchange.cpp",
+    "cfwl_eventtextwillchange.h",
+    "cfwl_eventvalidate.cpp",
+    "cfwl_eventvalidate.h",
+    "cfwl_listbox.cpp",
+    "cfwl_listbox.h",
+    "cfwl_listitem.cpp",
+    "cfwl_listitem.h",
+    "cfwl_message.cpp",
+    "cfwl_message.h",
+    "cfwl_messagekey.cpp",
+    "cfwl_messagekey.h",
+    "cfwl_messagekillfocus.cpp",
+    "cfwl_messagekillfocus.h",
+    "cfwl_messagemouse.cpp",
+    "cfwl_messagemouse.h",
+    "cfwl_messagemousewheel.cpp",
+    "cfwl_messagemousewheel.h",
+    "cfwl_messagesetfocus.cpp",
+    "cfwl_messagesetfocus.h",
+    "cfwl_monthcalendar.cpp",
+    "cfwl_monthcalendar.h",
+    "cfwl_notedriver.cpp",
+    "cfwl_notedriver.h",
+    "cfwl_picturebox.cpp",
+    "cfwl_picturebox.h",
+    "cfwl_pushbutton.cpp",
+    "cfwl_pushbutton.h",
+    "cfwl_scrollbar.cpp",
+    "cfwl_scrollbar.h",
+    "cfwl_themebackground.h",
+    "cfwl_themepart.cpp",
+    "cfwl_themepart.h",
+    "cfwl_themetext.h",
+    "cfwl_widget.cpp",
+    "cfwl_widget.h",
+    "cfwl_widgetmgr.cpp",
+    "cfwl_widgetmgr.h",
+    "cfwl_widgetproperties.cpp",
+    "cfwl_widgetproperties.h",
+    "fwl_widgetdef.h",
+    "fwl_widgethit.h",
+    "ifwl_themeprovider.h",
+    "ifwl_widgetdelegate.h",
+    "theme/cfwl_barcodetp.cpp",
+    "theme/cfwl_barcodetp.h",
+    "theme/cfwl_carettp.cpp",
+    "theme/cfwl_carettp.h",
+    "theme/cfwl_checkboxtp.cpp",
+    "theme/cfwl_checkboxtp.h",
+    "theme/cfwl_comboboxtp.cpp",
+    "theme/cfwl_comboboxtp.h",
+    "theme/cfwl_datetimepickertp.cpp",
+    "theme/cfwl_datetimepickertp.h",
+    "theme/cfwl_edittp.cpp",
+    "theme/cfwl_edittp.h",
+    "theme/cfwl_listboxtp.cpp",
+    "theme/cfwl_listboxtp.h",
+    "theme/cfwl_monthcalendartp.cpp",
+    "theme/cfwl_monthcalendartp.h",
+    "theme/cfwl_pictureboxtp.cpp",
+    "theme/cfwl_pictureboxtp.h",
+    "theme/cfwl_pushbuttontp.cpp",
+    "theme/cfwl_pushbuttontp.h",
+    "theme/cfwl_scrollbartp.cpp",
+    "theme/cfwl_scrollbartp.h",
+    "theme/cfwl_utils.h",
+    "theme/cfwl_widgettp.cpp",
+    "theme/cfwl_widgettp.h",
+  ]
+  deps = [
+    "../../core/fxcrt",
+    "../../core/fxge",
+    "../../fxbarcode",
+    "../fde",
+    "../fgas",
+    "../fxgraphics",
+  ]
+  configs += [
+    "../../:pdfium_core_config",
+    "../:xfa_warnings",
+  ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "cfwl_edit_embeddertest.cpp" ]
+  pdfium_root_dir = "../../"
+}
diff --git a/xfa/fwl/README.md b/xfa/fwl/README.md
index 9285e89..6460ff1 100644
--- a/xfa/fwl/README.md
+++ b/xfa/fwl/README.md
@@ -5,8 +5,6 @@
 
 * CFWL_Widget
     * CFWL_Form
-        * CFWL_FormProxy
-            * CFWL_ComboBoxProxy
     * CFWL_Caret
     * CFWL_CheckBox
     * CFWL_ComboBox
@@ -48,7 +46,7 @@
     * CFWL_EventMouse
     * CFWL_EventScroll
     * CFWL_EventSelectChanged
-    * CFWL_EventTextChanged
+    * CFWL_EventTextWillChange
     * CFWL_EventValidate
 
 The widgets use IFWL_ThemeProvider for rendering everything, calling
diff --git a/xfa/fwl/cfwl_app.cpp b/xfa/fwl/cfwl_app.cpp
index 58ca93d..c3a06e3 100644
--- a/xfa/fwl/cfwl_app.cpp
+++ b/xfa/fwl/cfwl_app.cpp
@@ -10,13 +10,13 @@
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
-#include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
 
-CFWL_App::CFWL_App(CXFA_FFApp* pAdapter)
+CFWL_App::CFWL_App(AdapterIface* pAdapter)
     : m_pAdapterNative(pAdapter),
-      m_pWidgetMgr(pdfium::MakeUnique<CFWL_WidgetMgr>(pAdapter)),
+      m_pWidgetMgr(
+          pdfium::MakeUnique<CFWL_WidgetMgr>(pAdapter->GetWidgetMgrAdapter())),
       m_pNoteDriver(pdfium::MakeUnique<CFWL_NoteDriver>()) {
   ASSERT(m_pAdapterNative);
 }
 
-CFWL_App::~CFWL_App() {}
+CFWL_App::~CFWL_App() = default;
diff --git a/xfa/fwl/cfwl_app.h b/xfa/fwl/cfwl_app.h
index ed47149..a911ab2 100644
--- a/xfa/fwl/cfwl_app.h
+++ b/xfa/fwl/cfwl_app.h
@@ -10,12 +10,11 @@
 #include <memory>
 
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/timerhandler_iface.h"
+#include "xfa/fwl/cfwl_widgetmgr.h"
 
 class CFWL_NoteDriver;
 class CFWL_WidgetMgr;
-class CXFA_FFApp;
-class CXFA_FWLAdapterWidgetMgr;
-class CFWL_Widget;
 
 enum FWL_KeyFlag {
   FWL_KEYFLAG_Ctrl = 1 << 0,
@@ -29,15 +28,22 @@
 
 class CFWL_App {
  public:
-  explicit CFWL_App(CXFA_FFApp* pAdapter);
+  class AdapterIface {
+   public:
+    virtual ~AdapterIface() = default;
+    virtual CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() = 0;
+    virtual TimerHandlerIface* GetTimerHandler() = 0;
+  };
+
+  explicit CFWL_App(AdapterIface* pAdapter);
   ~CFWL_App();
 
-  CXFA_FFApp* GetAdapterNative() const { return m_pAdapterNative.Get(); }
+  AdapterIface* GetAdapterNative() const { return m_pAdapterNative.Get(); }
   CFWL_WidgetMgr* GetWidgetMgr() const { return m_pWidgetMgr.get(); }
   CFWL_NoteDriver* GetNoteDriver() const { return m_pNoteDriver.get(); }
 
  private:
-  UnownedPtr<CXFA_FFApp> const m_pAdapterNative;
+  UnownedPtr<AdapterIface> const m_pAdapterNative;
   std::unique_ptr<CFWL_WidgetMgr> m_pWidgetMgr;
   std::unique_ptr<CFWL_NoteDriver> m_pNoteDriver;
 };
diff --git a/xfa/fwl/cfwl_barcode.cpp b/xfa/fwl/cfwl_barcode.cpp
index f5d6ea9..008424d 100644
--- a/xfa/fwl/cfwl_barcode.cpp
+++ b/xfa/fwl/cfwl_barcode.cpp
@@ -8,21 +8,18 @@
 
 #include <utility>
 
+#include "fxbarcode/cfx_barcode.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_themepart.h"
-#include "xfa/fwl/cfx_barcode.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 #include "xfa/fwl/theme/cfwl_utils.h"
 
 CFWL_Barcode::CFWL_Barcode(const CFWL_App* app)
-    : CFWL_Edit(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr),
-      m_dwStatus(0),
-      m_type(BC_UNKNOWN),
-      m_dwAttributeMask(FWL_BCDATTRIBUTE_NONE) {}
+    : CFWL_Edit(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {}
 
-CFWL_Barcode::~CFWL_Barcode() {}
+CFWL_Barcode::~CFWL_Barcode() = default;
 
 FWL_Type CFWL_Barcode::GetClassID() const {
   return FWL_Type::Barcode;
@@ -44,7 +41,7 @@
     return;
   if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0) {
     GenerateBarcodeImageCache();
-    if (!m_pBarcodeEngine || (m_dwStatus & XFA_BCS_EncodeSuccess) == 0)
+    if (!m_pBarcodeEngine || m_eStatus != Status::kEncodeSuccess)
       return;
 
     CFX_Matrix mt;
@@ -64,31 +61,34 @@
 
   m_pBarcodeEngine.reset();
   m_type = type;
-  m_dwStatus = XFA_BCS_NeedUpdate;
+  m_eStatus = Status::kNeedUpdate;
 }
 
 void CFWL_Barcode::SetText(const WideString& wsText) {
   m_pBarcodeEngine.reset();
-  m_dwStatus = XFA_BCS_NeedUpdate;
+  m_eStatus = Status::kNeedUpdate;
   CFWL_Edit::SetText(wsText);
 }
 
+void CFWL_Barcode::SetTextSkipNotify(const WideString& wsText) {
+  m_pBarcodeEngine.reset();
+  m_eStatus = Status::kNeedUpdate;
+  CFWL_Edit::SetTextSkipNotify(wsText);
+}
+
 bool CFWL_Barcode::IsProtectedType() const {
   if (!m_pBarcodeEngine)
     return true;
 
   BC_TYPE tEngineType = m_pBarcodeEngine->GetType();
-  if (tEngineType == BC_QR_CODE || tEngineType == BC_PDF417 ||
-      tEngineType == BC_DATAMATRIX) {
-    return true;
-  }
-  return false;
+  return tEngineType == BC_QR_CODE || tEngineType == BC_PDF417 ||
+         tEngineType == BC_DATAMATRIX;
 }
 
 void CFWL_Barcode::OnProcessEvent(CFWL_Event* pEvent) {
-  if (pEvent->GetType() == CFWL_Event::Type::TextChanged) {
+  if (pEvent->GetType() == CFWL_Event::Type::TextWillChange) {
     m_pBarcodeEngine.reset();
-    m_dwStatus = XFA_BCS_NeedUpdate;
+    m_eStatus = Status::kNeedUpdate;
   }
   CFWL_Edit::OnProcessEvent(pEvent);
 }
@@ -149,16 +149,11 @@
   m_nECLevel = ecLevel;
 }
 
-void CFWL_Barcode::SetTruncated(bool truncated) {
-  m_dwAttributeMask |= FWL_BCDATTRIBUTE_TRUNCATED;
-  m_bTruncated = truncated;
-}
-
 void CFWL_Barcode::GenerateBarcodeImageCache() {
-  if ((m_dwStatus & XFA_BCS_NeedUpdate) == 0)
+  if (m_eStatus != Status::kNeedUpdate)
     return;
 
-  m_dwStatus = 0;
+  m_eStatus = Status::kNormal;
   CreateBarcodeEngine();
   if (!m_pBarcodeEngine)
     return;
@@ -167,12 +162,12 @@
   if (pTheme) {
     CFWL_ThemePart part;
     part.m_pWidget = this;
-    if (RetainPtr<CFGAS_GEFont> pFont = pTheme->GetFont(&part)) {
+    if (RetainPtr<CFGAS_GEFont> pFont = pTheme->GetFont(part)) {
       if (CFX_Font* pCXFont = pFont->GetDevFont())
         m_pBarcodeEngine->SetFont(pCXFont);
     }
-    m_pBarcodeEngine->SetFontSize(pTheme->GetFontSize(&part));
-    m_pBarcodeEngine->SetFontColor(pTheme->GetTextColor(&part));
+    m_pBarcodeEngine->SetFontSize(pTheme->GetFontSize(part));
+    m_pBarcodeEngine->SetFontColor(pTheme->GetTextColor(part));
   } else {
     m_pBarcodeEngine->SetFontSize(FWLTHEME_CAPACITY_FontSize);
   }
@@ -201,19 +196,15 @@
     m_pBarcodeEngine->SetEndChar(m_cEndChar);
   if (m_dwAttributeMask & FWL_BCDATTRIBUTE_ECLEVEL)
     m_pBarcodeEngine->SetErrorCorrectionLevel(m_nECLevel);
-  if (m_dwAttributeMask & FWL_BCDATTRIBUTE_TRUNCATED)
-    m_pBarcodeEngine->SetTruncated(m_bTruncated);
 
-  m_dwStatus = m_pBarcodeEngine->Encode(GetText().AsStringView())
-                   ? XFA_BCS_EncodeSuccess
-                   : 0;
+  m_eStatus = m_pBarcodeEngine->Encode(GetText().AsStringView())
+                  ? Status::kEncodeSuccess
+                  : Status::kNormal;
 }
 
 void CFWL_Barcode::CreateBarcodeEngine() {
   if (m_pBarcodeEngine || m_type == BC_UNKNOWN)
     return;
 
-  auto pBarcode = pdfium::MakeUnique<CFX_Barcode>();
-  if (pBarcode->Create(m_type))
-    m_pBarcodeEngine = std::move(pBarcode);
+  m_pBarcodeEngine = CFX_Barcode::Create(m_type);
 }
diff --git a/xfa/fwl/cfwl_barcode.h b/xfa/fwl/cfwl_barcode.h
index 2fc7960..d2cd716 100644
--- a/xfa/fwl/cfwl_barcode.h
+++ b/xfa/fwl/cfwl_barcode.h
@@ -11,15 +11,8 @@
 
 #include "fxbarcode/BC_Library.h"
 #include "xfa/fwl/cfwl_edit.h"
-#include "xfa/fwl/cfwl_scrollbar.h"
-#include "xfa/fwl/cfwl_widget.h"
 
-class CFWL_WidgetProperties;
 class CFX_Barcode;
-class CFWL_Widget;
-
-#define XFA_BCS_NeedUpdate 0x0001
-#define XFA_BCS_EncodeSuccess 0x0002
 
 enum FWL_BCDAttribute {
   FWL_BCDATTRIBUTE_NONE = 0,
@@ -34,10 +27,9 @@
   FWL_BCDATTRIBUTE_STARTCHAR = 1 << 8,
   FWL_BCDATTRIBUTE_ENDCHAR = 1 << 9,
   FWL_BCDATTRIBUTE_ECLEVEL = 1 << 10,
-  FWL_BCDATTRIBUTE_TRUNCATED = 1 << 11,
 };
 
-class CFWL_Barcode : public CFWL_Edit {
+class CFWL_Barcode final : public CFWL_Edit {
  public:
   explicit CFWL_Barcode(const CFWL_App* pApp);
   ~CFWL_Barcode() override;
@@ -50,6 +42,7 @@
 
   // CFWL_Edit
   void SetText(const WideString& wsText) override;
+  void SetTextSkipNotify(const WideString& wsText) override;
 
   void SetType(BC_TYPE type);
   bool IsProtectedType() const;
@@ -65,28 +58,32 @@
   void SetStartChar(char startChar);
   void SetEndChar(char endChar);
   void SetErrorCorrectionLevel(int32_t ecLevel);
-  void SetTruncated(bool truncated);
 
  private:
+  enum class Status : uint8_t {
+    kNormal,
+    kNeedUpdate,
+    kEncodeSuccess,
+  };
+
   void GenerateBarcodeImageCache();
   void CreateBarcodeEngine();
 
+  BC_TYPE m_type = BC_UNKNOWN;
+  BC_CHAR_ENCODING m_eCharEncoding = CHAR_ENCODING_UTF8;
+  BC_TEXT_LOC m_eTextLocation = BC_TEXT_LOC_NONE;
+  Status m_eStatus = Status::kNormal;
+  bool m_bCalChecksum = false;
+  bool m_bPrintChecksum = false;
+  char m_cStartChar = 0;
+  char m_cEndChar = 0;
+  int8_t m_nWideNarrowRatio = 1;
+  int32_t m_nModuleHeight = -1;
+  int32_t m_nModuleWidth = -1;
+  int32_t m_nDataLength = 0;
+  int32_t m_nECLevel = 0;
+  uint32_t m_dwAttributeMask = 0;
   std::unique_ptr<CFX_Barcode> m_pBarcodeEngine;
-  uint32_t m_dwStatus;
-  BC_TYPE m_type;
-  BC_CHAR_ENCODING m_eCharEncoding;
-  int32_t m_nModuleHeight;
-  int32_t m_nModuleWidth;
-  int32_t m_nDataLength;
-  bool m_bCalChecksum;
-  bool m_bPrintChecksum;
-  BC_TEXT_LOC m_eTextLocation;
-  int8_t m_nWideNarrowRatio;
-  char m_cStartChar;
-  char m_cEndChar;
-  int32_t m_nECLevel;
-  bool m_bTruncated;
-  uint32_t m_dwAttributeMask;
 };
 
 #endif  // XFA_FWL_CFWL_BARCODE_H_
diff --git a/xfa/fwl/cfwl_caret.cpp b/xfa/fwl/cfwl_caret.cpp
index cd85041..ef5bdcc 100644
--- a/xfa/fwl/cfwl_caret.cpp
+++ b/xfa/fwl/cfwl_caret.cpp
@@ -9,15 +9,15 @@
 #include <utility>
 
 #include "third_party/base/ptr_util.h"
+#include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_themebackground.h"
-#include "xfa/fwl/cfwl_timerinfo.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 namespace {
 
-const uint32_t kFrequency = 400;
+const uint32_t kBlinkPeriodMs = 600;
 
 constexpr int kStateHighlight = (1 << 0);
 
@@ -26,18 +26,11 @@
 CFWL_Caret::CFWL_Caret(const CFWL_App* app,
                        std::unique_ptr<CFWL_WidgetProperties> properties,
                        CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter),
-      m_pTimer(pdfium::MakeUnique<CFWL_Caret::Timer>(this)),
-      m_pTimerInfo(nullptr) {
+    : CFWL_Widget(app, std::move(properties), pOuter) {
   SetStates(kStateHighlight);
 }
 
-CFWL_Caret::~CFWL_Caret() {
-  if (m_pTimerInfo) {
-    m_pTimerInfo->StopTimer();
-    m_pTimerInfo = nullptr;
-  }
-}
+CFWL_Caret::~CFWL_Caret() = default;
 
 FWL_Type CFWL_Caret::GetClassID() const {
   return FWL_Type::Caret;
@@ -54,21 +47,19 @@
   if (!m_pProperties->m_pThemeProvider)
     return;
 
-  DrawCaretBK(pGraphics, m_pProperties->m_pThemeProvider, &matrix);
+  DrawCaretBK(pGraphics, m_pProperties->m_pThemeProvider.Get(), &matrix);
 }
 
 void CFWL_Caret::ShowCaret() {
-  if (m_pTimerInfo)
-    m_pTimerInfo->StopTimer();
-  m_pTimerInfo = m_pTimer->StartTimer(kFrequency, true);
+  m_pTimer = pdfium::MakeUnique<CFX_Timer>(
+      GetOwnerApp()->GetAdapterNative()->GetTimerHandler(), this,
+      kBlinkPeriodMs);
   RemoveStates(FWL_WGTSTATE_Invisible);
+  SetStates(kStateHighlight);
 }
 
 void CFWL_Caret::HideCaret() {
-  if (m_pTimerInfo) {
-    m_pTimerInfo->StopTimer();
-    m_pTimerInfo = nullptr;
-  }
+  m_pTimer.reset();
   SetStates(FWL_WGTSTATE_Invisible);
 }
 
@@ -86,7 +77,7 @@
   param.m_dwStates = CFWL_PartState_HightLight;
   if (pMatrix)
     param.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 void CFWL_Caret::OnProcessMessage(CFWL_Message* pMessage) {}
@@ -96,15 +87,12 @@
   DrawWidget(pGraphics, matrix);
 }
 
-CFWL_Caret::Timer::Timer(CFWL_Caret* pCaret) : CFWL_Timer(pCaret) {}
-
-void CFWL_Caret::Timer::Run(CFWL_TimerInfo* pTimerInfo) {
-  CFWL_Caret* pCaret = static_cast<CFWL_Caret*>(m_pWidget.Get());
-  if (!(pCaret->GetStates() & kStateHighlight))
-    pCaret->SetStates(kStateHighlight);
+void CFWL_Caret::OnTimerFired() {
+  if (!(GetStates() & kStateHighlight))
+    SetStates(kStateHighlight);
   else
-    pCaret->RemoveStates(kStateHighlight);
+    RemoveStates(kStateHighlight);
 
-  CFX_RectF rt = pCaret->GetWidgetRect();
-  pCaret->RepaintRect(CFX_RectF(0, 0, rt.width + 1, rt.height));
+  CFX_RectF rt = GetWidgetRect();
+  RepaintRect(CFX_RectF(0, 0, rt.width + 1, rt.height));
 }
diff --git a/xfa/fwl/cfwl_caret.h b/xfa/fwl/cfwl_caret.h
index 90d62d6..7f5dfdf 100644
--- a/xfa/fwl/cfwl_caret.h
+++ b/xfa/fwl/cfwl_caret.h
@@ -9,21 +9,21 @@
 
 #include <memory>
 
-#include "xfa/fwl/cfwl_timer.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 
 class CFWL_WidgetProperties;
 class CFWL_Widget;
 
-class CFWL_Caret : public CFWL_Widget {
+class CFWL_Caret final : public CFWL_Widget, public CFX_Timer::CallbackIface {
  public:
   CFWL_Caret(const CFWL_App* app,
              std::unique_ptr<CFWL_WidgetProperties> properties,
              CFWL_Widget* pOuter);
   ~CFWL_Caret() override;
 
-  // CFWL_Widget
+  // CFWL_Widget:
   FWL_Type GetClassID() const override;
   void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
@@ -31,25 +31,18 @@
                     const CFX_Matrix& matrix) override;
   void Update() override;
 
+  // CFX_Timer::CallbackIface:
+  void OnTimerFired() override;
+
   void ShowCaret();
   void HideCaret();
 
  private:
-  class Timer : public CFWL_Timer {
-   public:
-    explicit Timer(CFWL_Caret* pCaret);
-    ~Timer() override {}
-
-    void Run(CFWL_TimerInfo* hTimer) override;
-  };
-  friend class CFWL_Caret::Timer;
-
   void DrawCaretBK(CXFA_Graphics* pGraphics,
                    IFWL_ThemeProvider* pTheme,
                    const CFX_Matrix* pMatrix);
 
-  std::unique_ptr<CFWL_Caret::Timer> m_pTimer;
-  UnownedPtr<CFWL_TimerInfo> m_pTimerInfo;
+  std::unique_ptr<CFX_Timer> m_pTimer;
 };
 
 #endif  // XFA_FWL_CFWL_CARET_H_
diff --git a/xfa/fwl/cfwl_checkbox.cpp b/xfa/fwl/cfwl_checkbox.cpp
index 42b6cad..1342554 100644
--- a/xfa/fwl/cfwl_checkbox.cpp
+++ b/xfa/fwl/cfwl_checkbox.cpp
@@ -21,6 +21,7 @@
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themetext.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 namespace {
@@ -30,15 +31,8 @@
 }  // namespace
 
 CFWL_CheckBox::CFWL_CheckBox(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr),
-      m_iTTOAlign(FDE_TextAlignment::kCenter),
-      m_bBtnDown(false),
-      m_fBoxHeight(16.0f) {
-  m_dwTTOStyles.single_line_ = true;
-  m_rtClient.Reset();
-  m_rtBox.Reset();
-  m_rtCaption.Reset();
-  m_rtFocus.Reset();
+    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
+  m_TTOStyles.single_line_ = true;
 }
 
 CFWL_CheckBox::~CFWL_CheckBox() {}
@@ -65,14 +59,13 @@
                                const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
+
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
+  if (!pTheme)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
-  if (HasBorder()) {
-    DrawBorder(pGraphics, CFWL_Part::Border, m_pProperties->m_pThemeProvider,
-               matrix);
-  }
+  if (HasBorder())
+    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
 
   int32_t dwStates = GetPartStates();
 
@@ -84,12 +77,13 @@
   param.m_matrix.Concat(matrix);
   param.m_rtPart = m_rtClient;
   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
-    param.m_pData = &m_rtFocus;
-  pTheme->DrawBackground(&param);
+
+    param.m_pRtData = &m_rtFocus;
+  pTheme->DrawBackground(param);
 
   param.m_iPart = CFWL_Part::CheckBox;
   param.m_rtPart = m_rtBox;
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 
   CFWL_ThemeText textParam;
   textParam.m_pWidget = this;
@@ -99,9 +93,9 @@
   textParam.m_matrix.Concat(matrix);
   textParam.m_rtPart = m_rtCaption;
   textParam.m_wsText = L"Check box";
-  textParam.m_dwTTOStyles = m_dwTTOStyles;
+  textParam.m_dwTTOStyles = m_TTOStyles;
   textParam.m_iTTOAlign = m_iTTOAlign;
-  pTheme->DrawText(&textParam);
+  pTheme->DrawText(textParam);
 }
 
 void CFWL_CheckBox::SetCheckState(int32_t iCheck) {
@@ -122,9 +116,9 @@
 
 void CFWL_CheckBox::Layout() {
   m_pProperties->m_rtWidget.width =
-      FXSYS_round(m_pProperties->m_rtWidget.width);
+      FXSYS_roundf(m_pProperties->m_rtWidget.width);
   m_pProperties->m_rtWidget.height =
-      FXSYS_round(m_pProperties->m_rtWidget.height);
+      FXSYS_roundf(m_pProperties->m_rtWidget.height);
   m_rtClient = GetClientRect();
 
   float fTextLeft = m_rtClient.left + m_fBoxHeight;
@@ -133,11 +127,9 @@
                           m_rtClient.right() - fTextLeft, m_rtClient.height);
   m_rtCaption.Inflate(-kCaptionMargin, -kCaptionMargin);
 
-  CFX_RectF rtFocus(m_rtCaption.left, m_rtCaption.top, m_rtCaption.width,
-                    m_rtCaption.height);
-
-  CalcTextRect(L"Check box", m_pProperties->m_pThemeProvider, m_dwTTOStyles,
-               m_iTTOAlign, rtFocus);
+  CFX_RectF rtFocus = m_rtCaption;
+  CalcTextRect(L"Check box", m_pProperties->m_pThemeProvider.Get(), m_TTOStyles,
+               m_iTTOAlign, &rtFocus);
 
   m_rtFocus = CFX_RectF(m_rtCaption.TopLeft(),
                         std::max(m_rtCaption.width, rtFocus.width),
@@ -169,9 +161,8 @@
 
 void CFWL_CheckBox::UpdateTextOutStyles() {
   m_iTTOAlign = FDE_TextAlignment::kTopLeft;
-
-  m_dwTTOStyles.Reset();
-  m_dwTTOStyles.single_line_ = true;
+  m_TTOStyles.Reset();
+  m_TTOStyles.single_line_ = true;
 }
 
 void CFWL_CheckBox::NextStates() {
@@ -179,21 +170,6 @@
   if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CKB_RadioButton) {
     if ((m_pProperties->m_dwStates & FWL_STATE_CKB_CheckMask) ==
         FWL_STATE_CKB_Unchecked) {
-      CFWL_WidgetMgr* pWidgetMgr = GetOwnerApp()->GetWidgetMgr();
-      if (!pWidgetMgr->IsFormDisabled()) {
-        std::vector<CFWL_Widget*> radioarr =
-            pWidgetMgr->GetSameGroupRadioButton(this);
-        for (auto* pWidget : radioarr) {
-          CFWL_CheckBox* pCheckBox = static_cast<CFWL_CheckBox*>(pWidget);
-          if (pCheckBox != this &&
-              pCheckBox->GetStates() & FWL_STATE_CKB_Checked) {
-            pCheckBox->SetCheckState(0);
-            m_pWidgetMgr->RepaintWidget(
-                pCheckBox, CFX_RectF(0, 0, pCheckBox->GetWidgetRect().Size()));
-            break;
-          }
-        }
-      }
       m_pProperties->m_dwStates |= FWL_STATE_CKB_Checked;
     }
   } else {
@@ -261,8 +237,9 @@
     default:
       break;
   }
-
-  CFWL_Widget::OnProcessMessage(pMessage);
+  // Dst target could be |this|, continue only if not destroyed by above.
+  if (pMessage->GetDstTarget())
+    CFWL_Widget::OnProcessMessage(pMessage);
 }
 
 void CFWL_CheckBox::OnDrawWidget(CXFA_Graphics* pGraphics,
@@ -282,8 +259,6 @@
 void CFWL_CheckBox::OnLButtonDown() {
   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
     return;
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
-    SetFocus(true);
 
   m_bBtnDown = true;
   m_pProperties->m_dwStates &= ~FWL_STATE_CKB_Hovered;
@@ -351,10 +326,10 @@
 }
 
 void CFWL_CheckBox::OnKeyDown(CFWL_MessageKey* pMsg) {
-  if (pMsg->m_dwKeyCode == FWL_VKEY_Tab)
+  if (pMsg->m_dwKeyCode == XFA_FWL_VKEY_Tab)
     return;
-  if (pMsg->m_dwKeyCode == FWL_VKEY_Return ||
-      pMsg->m_dwKeyCode == FWL_VKEY_Space) {
+  if (pMsg->m_dwKeyCode == XFA_FWL_VKEY_Return ||
+      pMsg->m_dwKeyCode == XFA_FWL_VKEY_Space) {
     NextStates();
   }
 }
diff --git a/xfa/fwl/cfwl_checkbox.h b/xfa/fwl/cfwl_checkbox.h
index 9ae6590..0fa0236 100644
--- a/xfa/fwl/cfwl_checkbox.h
+++ b/xfa/fwl/cfwl_checkbox.h
@@ -7,8 +7,6 @@
 #ifndef XFA_FWL_CFWL_CHECKBOX_H_
 #define XFA_FWL_CFWL_CHECKBOX_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
@@ -33,7 +31,7 @@
 class CFWL_WidgetProperties;
 class CFWL_Widget;
 
-class CFWL_CheckBox : public CFWL_Widget {
+class CFWL_CheckBox final : public CFWL_Widget {
  public:
   explicit CFWL_CheckBox(const CFWL_App* pApp);
   ~CFWL_CheckBox() override;
@@ -66,10 +64,10 @@
   CFX_RectF m_rtBox;
   CFX_RectF m_rtCaption;
   CFX_RectF m_rtFocus;
-  FDE_TextStyle m_dwTTOStyles;
-  FDE_TextAlignment m_iTTOAlign;
-  bool m_bBtnDown;
-  float m_fBoxHeight;
+  FDE_TextStyle m_TTOStyles;
+  FDE_TextAlignment m_iTTOAlign = FDE_TextAlignment::kCenter;
+  bool m_bBtnDown = false;
+  float m_fBoxHeight = 16.0f;
 };
 
 #endif  // XFA_FWL_CFWL_CHECKBOX_H_
diff --git a/xfa/fwl/cfwl_combobox.cpp b/xfa/fwl/cfwl_combobox.cpp
index 143d797..9c5edf4 100644
--- a/xfa/fwl/cfwl_combobox.cpp
+++ b/xfa/fwl/cfwl_combobox.cpp
@@ -16,8 +16,6 @@
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_eventselectchanged.h"
-#include "xfa/fwl/cfwl_eventtextchanged.h"
-#include "xfa/fwl/cfwl_formproxy.h"
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fwl/cfwl_messagekey.h"
 #include "xfa/fwl/cfwl_messagekillfocus.h"
@@ -28,48 +26,22 @@
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/cfwl_themetext.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 CFWL_ComboBox::CFWL_ComboBox(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr),
-      m_pComboBoxProxy(nullptr),
-      m_bLButtonDown(false),
-      m_iCurSel(-1),
-      m_iBtnState(CFWL_PartState_Normal) {
-  m_rtClient.Reset();
-  m_rtBtn.Reset();
-  m_rtHandler.Reset();
-
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_InitComboList();
-    DisForm_InitComboEdit();
-    return;
-  }
-
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_pThemeProvider = m_pProperties->m_pThemeProvider;
-  prop->m_dwStyles |= FWL_WGTSTYLE_Border | FWL_WGTSTYLE_VScroll;
-  m_pListBox = pdfium::MakeUnique<CFWL_ComboList>(m_pOwnerApp.Get(),
-                                                  std::move(prop), this);
-
-  if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_CMB_DropDown) && !m_pEdit) {
-    m_pEdit = pdfium::MakeUnique<CFWL_ComboEdit>(
-        m_pOwnerApp.Get(), pdfium::MakeUnique<CFWL_WidgetProperties>(), this);
-    m_pEdit->SetOuter(this);
-  }
-  if (m_pEdit)
-    m_pEdit->SetParent(this);
-
-  SetStates(m_pProperties->m_dwStates);
+    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
+  InitComboList();
+  InitComboEdit();
 }
 
-CFWL_ComboBox::~CFWL_ComboBox() {}
+CFWL_ComboBox::~CFWL_ComboBox() = default;
 
 FWL_Type CFWL_ComboBox::GetClassID() const {
   return FWL_Type::ComboBox;
 }
 
-void CFWL_ComboBox::AddString(const WideStringView& wsText) {
+void CFWL_ComboBox::AddString(const WideString& wsText) {
   m_pListBox->AddString(wsText);
 }
 
@@ -83,118 +55,74 @@
 
 void CFWL_ComboBox::ModifyStylesEx(uint32_t dwStylesExAdded,
                                    uint32_t dwStylesExRemoved) {
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
-    return;
-  }
+  if (!m_pEdit)
+    InitComboEdit();
 
   bool bAddDropDown = !!(dwStylesExAdded & FWL_STYLEEXT_CMB_DropDown);
-  bool bRemoveDropDown = !!(dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown);
-  if (bAddDropDown && !m_pEdit) {
-    m_pEdit = pdfium::MakeUnique<CFWL_ComboEdit>(
-        m_pOwnerApp.Get(), pdfium::MakeUnique<CFWL_WidgetProperties>(),
-        nullptr);
-    m_pEdit->SetOuter(this);
-    m_pEdit->SetParent(this);
-  } else if (bRemoveDropDown && m_pEdit) {
-    m_pEdit->SetStates(FWL_WGTSTATE_Invisible);
-  }
+  bool bDelDropDown = !!(dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown);
+
+  dwStylesExRemoved &= ~FWL_STYLEEXT_CMB_DropDown;
+  m_pProperties->m_dwStyleExes |= FWL_STYLEEXT_CMB_DropDown;
+
+  if (bAddDropDown)
+    m_pEdit->ModifyStylesEx(0, FWL_STYLEEXT_EDT_ReadOnly);
+  else if (bDelDropDown)
+    m_pEdit->ModifyStylesEx(FWL_STYLEEXT_EDT_ReadOnly, 0);
   CFWL_Widget::ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
 }
 
 void CFWL_ComboBox::Update() {
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_Update();
+  if (m_iLock)
     return;
-  }
-  if (IsLocked())
-    return;
-
-  ResetTheme();
-  if (IsDropDownStyle() && m_pEdit)
+  if (m_pEdit)
     ResetEditAlignment();
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-
+  ResetTheme();
   Layout();
 }
 
 FWL_WidgetHit CFWL_ComboBox::HitTest(const CFX_PointF& point) {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_HitTest(point);
-  return CFWL_Widget::HitTest(point);
+  CFX_RectF rect(0, 0, m_pProperties->m_rtWidget.width - m_rtBtn.width,
+                 m_pProperties->m_rtWidget.height);
+  if (rect.Contains(point))
+    return FWL_WidgetHit::Edit;
+  if (m_rtBtn.Contains(point))
+    return FWL_WidgetHit::Client;
+  if (IsDropListVisible()) {
+    rect = m_pListBox->GetWidgetRect();
+    if (rect.Contains(point))
+      return FWL_WidgetHit::Client;
+  }
+  return FWL_WidgetHit::Unknown;
 }
 
 void CFWL_ComboBox::DrawWidget(CXFA_Graphics* pGraphics,
                                const CFX_Matrix& matrix) {
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_DrawWidget(pGraphics, &matrix);
-    return;
-  }
-
-  if (!pGraphics)
-    return;
-  if (!m_pProperties->m_pThemeProvider)
-    return;
-
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
-  if (HasBorder())
-    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
-
-  if (!IsDropDownStyle()) {
-    CFX_RectF rtTextBk(m_rtClient);
-    rtTextBk.width -= m_rtBtn.width;
-
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
+  pGraphics->SaveGraphState();
+  pGraphics->ConcatMatrix(&matrix);
+  if (!m_rtBtn.IsEmpty(0.1f)) {
     CFWL_ThemeBackground param;
     param.m_pWidget = this;
-    param.m_iPart = CFWL_Part::Background;
+    param.m_iPart = CFWL_Part::DropDownButton;
+    param.m_dwStates = m_iBtnState;
     param.m_pGraphics = pGraphics;
-    param.m_matrix.Concat(matrix);
-    param.m_rtPart = rtTextBk;
-
-    if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) {
-      param.m_dwStates = CFWL_PartState_Disabled;
-    } else if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) &&
-               (m_iCurSel >= 0)) {
-      param.m_dwStates = CFWL_PartState_Selected;
-    } else {
-      param.m_dwStates = CFWL_PartState_Normal;
-    }
-    pTheme->DrawBackground(&param);
-
-    if (m_iCurSel >= 0) {
-      if (!m_pListBox)
-        return;
-
-      CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel);
-
-      CFWL_ThemeText theme_text;
-      theme_text.m_pWidget = this;
-      theme_text.m_iPart = CFWL_Part::Caption;
-      theme_text.m_dwStates = m_iBtnState;
-      theme_text.m_pGraphics = pGraphics;
-      theme_text.m_matrix.Concat(matrix);
-      theme_text.m_rtPart = rtTextBk;
-      theme_text.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
-                                  ? CFWL_PartState_Selected
-                                  : CFWL_PartState_Normal;
-      theme_text.m_wsText = hItem ? hItem->GetText() : L"";
-      theme_text.m_dwTTOStyles.single_line_ = true;
-      theme_text.m_iTTOAlign = FDE_TextAlignment::kCenterLeft;
-      pTheme->DrawText(&theme_text);
-    }
+    param.m_rtPart = m_rtBtn;
+    pTheme->DrawBackground(param);
   }
+  pGraphics->RestoreGraphState();
 
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::DropDownButton;
-  param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-                         ? CFWL_PartState_Disabled
-                         : m_iBtnState;
-  param.m_pGraphics = pGraphics;
-  param.m_matrix.Concat(matrix);
-  param.m_rtPart = m_rtBtn;
-  pTheme->DrawBackground(&param);
+  if (m_pEdit) {
+    CFX_RectF rtEdit = m_pEdit->GetWidgetRect();
+    CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top);
+    mt.Concat(matrix);
+    m_pEdit->DrawWidget(pGraphics, mt);
+  }
+  if (m_pListBox && IsDropListVisible()) {
+    CFX_RectF rtList = m_pListBox->GetWidgetRect();
+    CFX_Matrix mt(1, 0, 0, 1, rtList.left, rtList.top);
+    mt.Concat(matrix);
+    m_pListBox->DrawWidget(pGraphics, mt);
+  }
 }
 
 void CFWL_ComboBox::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
@@ -211,7 +139,7 @@
 WideString CFWL_ComboBox::GetTextByIndex(int32_t iIndex) const {
   CFWL_ListItem* pItem = static_cast<CFWL_ListItem*>(
       m_pListBox->GetItem(m_pListBox.get(), iIndex));
-  return pItem ? pItem->GetText() : L"";
+  return pItem ? pItem->GetText() : WideString();
 }
 
 void CFWL_ComboBox::SetCurSel(int32_t iSel) {
@@ -222,7 +150,7 @@
       m_pEdit->SetText(WideString());
     } else {
       CFWL_ListItem* hItem = m_pListBox->GetItem(this, iSel);
-      m_pEdit->SetText(hItem ? hItem->GetText() : L"");
+      m_pEdit->SetText(hItem ? hItem->GetText() : WideString());
     }
     m_pEdit->Update();
   }
@@ -257,10 +185,10 @@
   if (m_pEdit)
     return m_pEdit->GetText();
   if (!m_pListBox)
-    return L"";
+    return WideString();
 
   CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel);
-  return hItem ? hItem->GetText() : L"";
+  return hItem ? hItem->GetText() : WideString();
 }
 
 void CFWL_ComboBox::OpenDropDownList(bool bActivate) {
@@ -268,9 +196,6 @@
 }
 
 CFX_RectF CFWL_ComboBox::GetBBox() const {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_GetBBox();
-
   CFX_RectF rect = m_pProperties->m_rtWidget;
   if (!m_pListBox || !IsDropListVisible())
     return rect;
@@ -287,69 +212,54 @@
     m_pEdit->ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
 }
 
-void CFWL_ComboBox::DrawStretchHandler(CXFA_Graphics* pGraphics,
-                                       const CFX_Matrix* pMatrix) {
-  CFWL_ThemeBackground param;
-  param.m_pGraphics = pGraphics;
-  param.m_iPart = CFWL_Part::StretchHandler;
-  param.m_dwStates = CFWL_PartState_Normal;
-  param.m_pWidget = this;
-  if (pMatrix)
-    param.m_matrix.Concat(*pMatrix);
-  param.m_rtPart = m_rtHandler;
-  m_pProperties->m_pThemeProvider->DrawBackground(&param);
-}
-
 void CFWL_ComboBox::ShowDropList(bool bActivate) {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_ShowDropList(bActivate);
   if (IsDropListVisible() == bActivate)
     return;
-  if (!m_pComboBoxProxy)
-    InitProxyForm();
 
-  m_pComboBoxProxy->Reset();
-  if (!bActivate) {
-    m_pComboBoxProxy->EndDoModal();
+  if (bActivate) {
+    CFWL_Event preEvent(CFWL_Event::Type::PreDropDown, this);
+    DispatchEvent(&preEvent);
+    if (!preEvent.GetSrcTarget())
+      return;
 
-    m_bLButtonDown = false;
-    m_pListBox->SetNotifyOwner(true);
-    SetFocus(true);
-    return;
+    CFWL_ComboList* pComboList = m_pListBox.get();
+    int32_t iItems = pComboList->CountItems(nullptr);
+    if (iItems < 1)
+      return;
+
+    ResetListItemAlignment();
+    pComboList->ChangeSelected(m_iCurSel);
+
+    float fItemHeight = pComboList->CalcItemHeight();
+    float fBorder = GetCXBorderSize();
+    float fPopupMin = 0.0f;
+    if (iItems > 3)
+      fPopupMin = fItemHeight * 3 + fBorder * 2;
+
+    float fPopupMax = fItemHeight * iItems + fBorder * 2;
+    CFX_RectF rtList(m_rtClient.left, 0, m_pProperties->m_rtWidget.width, 0);
+    GetPopupPos(fPopupMin, fPopupMax, m_pProperties->m_rtWidget, &rtList);
+
+    m_pListBox->SetWidgetRect(rtList);
+    m_pListBox->Update();
   }
 
-  m_pListBox->ChangeSelected(m_iCurSel);
-  ResetListItemAlignment();
+  if (bActivate) {
+    m_pListBox->RemoveStates(FWL_WGTSTATE_Invisible);
+    CFWL_Event postEvent(CFWL_Event::Type::PostDropDown, this);
+    DispatchEvent(&postEvent);
+  } else {
+    m_pListBox->SetStates(FWL_WGTSTATE_Invisible);
+  }
 
-  uint32_t dwStyleAdd = m_pProperties->m_dwStyleExes &
-                        (FWL_STYLEEXT_CMB_Sort | FWL_STYLEEXT_CMB_OwnerDraw);
-  m_pListBox->ModifyStylesEx(dwStyleAdd, 0);
-  m_rtList = m_pListBox->GetAutosizedWidgetRect();
-
-  CFX_RectF rtAnchor(0, 0, m_pProperties->m_rtWidget.width,
-                     m_pProperties->m_rtWidget.height);
-
-  m_rtList.width = std::max(m_rtList.width, m_rtClient.width);
-  m_rtProxy = m_rtList;
-
-  GetPopupPos(0, m_rtProxy.height, rtAnchor, m_rtProxy);
-
-  m_pComboBoxProxy->SetWidgetRect(m_rtProxy);
-  m_pComboBoxProxy->Update();
-  m_pListBox->SetWidgetRect(m_rtList);
-  m_pListBox->Update();
-
-  CFWL_Event ev(CFWL_Event::Type::PreDropDown, this);
-  DispatchEvent(&ev);
-
-  m_pListBox->SetFocus(true);
-  m_pComboBoxProxy->DoModal();
-  m_pListBox->SetFocus(false);
+  CFX_RectF rect = m_pListBox->GetWidgetRect();
+  rect.Inflate(2, 2);
+  RepaintRect(rect);
 }
 
 void CFWL_ComboBox::MatchEditText() {
   WideString wsText = m_pEdit->GetText();
-  int32_t iMatch = m_pListBox->MatchItem(wsText);
+  int32_t iMatch = m_pListBox->MatchItem(wsText.AsStringView());
   if (iMatch != m_iCurSel) {
     m_pListBox->ChangeSelected(iMatch);
     if (iMatch >= 0)
@@ -362,45 +272,52 @@
 
 void CFWL_ComboBox::SyncEditText(int32_t iListItem) {
   CFWL_ListItem* hItem = m_pListBox->GetItem(this, iListItem);
-  m_pEdit->SetText(hItem ? hItem->GetText() : L"");
+  m_pEdit->SetText(hItem ? hItem->GetText() : WideString());
   m_pEdit->Update();
   m_pEdit->SetSelected();
 }
 
 void CFWL_ComboBox::Layout() {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_Layout();
-
   m_rtClient = GetClientRect();
+  m_rtContent = m_rtClient;
   IFWL_ThemeProvider* theme = GetAvailableTheme();
   if (!theme)
     return;
 
+  float borderWidth = 1;
   float fBtn = theme->GetScrollBarWidth();
-  m_rtBtn = CFX_RectF(m_rtClient.right() - fBtn, m_rtClient.top, fBtn,
-                      m_rtClient.height);
+  if (!(GetStylesEx() & FWL_STYLEEXT_CMB_ReadOnly)) {
+    m_rtBtn =
+        CFX_RectF(m_rtClient.right() - fBtn, m_rtClient.top + borderWidth,
+                  fBtn - borderWidth, m_rtClient.height - 2 * borderWidth);
+  }
+
+  CFWL_ThemePart part;
+  part.m_pWidget = this;
+  CFX_RectF pUIMargin = theme->GetUIMargin(part);
+  m_rtContent.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
+                      pUIMargin.height);
+
   if (!IsDropDownStyle() || !m_pEdit)
     return;
 
-  CFX_RectF rtEdit(m_rtClient.left, m_rtClient.top, m_rtClient.width - fBtn,
-                   m_rtClient.height);
+  CFX_RectF rtEdit(m_rtContent.left, m_rtContent.top, m_rtContent.width - fBtn,
+                   m_rtContent.height);
   m_pEdit->SetWidgetRect(rtEdit);
 
   if (m_iCurSel >= 0) {
     CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel);
-    m_pEdit->LockUpdate();
-    m_pEdit->SetText(hItem ? hItem->GetText() : L"");
-    m_pEdit->UnlockUpdate();
+    ScopedUpdateLock update_lock(m_pEdit.get());
+    m_pEdit->SetText(hItem ? hItem->GetText() : WideString());
   }
   m_pEdit->Update();
 }
 
 void CFWL_ComboBox::ResetTheme() {
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
-  if (!pTheme) {
-    pTheme = GetAvailableTheme();
-    m_pProperties->m_pThemeProvider = pTheme;
-  }
+  if (!m_pProperties->m_pThemeProvider)
+    m_pProperties->m_pThemeProvider = GetAvailableTheme();
+
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
   if (m_pListBox && !m_pListBox->GetThemeProvider())
     m_pListBox->SetThemeProvider(pTheme);
   if (m_pEdit && !m_pEdit->GetThemeProvider())
@@ -483,25 +400,7 @@
   DispatchEvent(&ev);
 }
 
-void CFWL_ComboBox::InitProxyForm() {
-  if (m_pComboBoxProxy)
-    return;
-  if (!m_pListBox)
-    return;
-
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_pOwner = this;
-  prop->m_dwStyles = FWL_WGTSTYLE_Popup;
-  prop->m_dwStates = FWL_WGTSTATE_Invisible;
-
-  // TODO(dsinclair): Does this leak? I don't see a delete, but I'm not sure
-  // if the SetParent call is going to transfer ownership.
-  m_pComboBoxProxy = new CFWL_ComboBoxProxy(this, m_pOwnerApp.Get(),
-                                            std::move(prop), m_pListBox.get());
-  m_pListBox->SetParent(m_pComboBoxProxy);
-}
-
-void CFWL_ComboBox::DisForm_InitComboList() {
+void CFWL_ComboBox::InitComboList() {
   if (m_pListBox)
     return;
 
@@ -514,7 +413,7 @@
                                                   std::move(prop), this);
 }
 
-void CFWL_ComboBox::DisForm_InitComboEdit() {
+void CFWL_ComboBox::InitComboEdit() {
   if (m_pEdit)
     return;
 
@@ -527,370 +426,7 @@
   m_pEdit->SetOuter(this);
 }
 
-void CFWL_ComboBox::DisForm_ShowDropList(bool bActivate) {
-  if (DisForm_IsDropListVisible() == bActivate)
-    return;
-
-  if (bActivate) {
-    CFWL_Event preEvent(CFWL_Event::Type::PreDropDown, this);
-    DispatchEvent(&preEvent);
-
-    CFWL_ComboList* pComboList = m_pListBox.get();
-    int32_t iItems = pComboList->CountItems(nullptr);
-    if (iItems < 1)
-      return;
-
-    ResetListItemAlignment();
-    pComboList->ChangeSelected(m_iCurSel);
-
-    float fItemHeight = pComboList->CalcItemHeight();
-    float fBorder = GetBorderSize(true);
-    float fPopupMin = 0.0f;
-    if (iItems > 3)
-      fPopupMin = fItemHeight * 3 + fBorder * 2;
-
-    float fPopupMax = fItemHeight * iItems + fBorder * 2;
-    CFX_RectF rtList(m_rtClient.left, 0, m_pProperties->m_rtWidget.width, 0);
-    GetPopupPos(fPopupMin, fPopupMax, m_pProperties->m_rtWidget, rtList);
-
-    m_pListBox->SetWidgetRect(rtList);
-    m_pListBox->Update();
-  } else {
-    SetFocus(true);
-  }
-
-  if (bActivate) {
-    m_pListBox->RemoveStates(FWL_WGTSTATE_Invisible);
-    CFWL_Event postEvent(CFWL_Event::Type::PostDropDown, this);
-    DispatchEvent(&postEvent);
-  } else {
-    m_pListBox->SetStates(FWL_WGTSTATE_Invisible);
-  }
-
-  CFX_RectF rect = m_pListBox->GetWidgetRect();
-  rect.Inflate(2, 2);
-  RepaintRect(rect);
-}
-
-void CFWL_ComboBox::DisForm_ModifyStylesEx(uint32_t dwStylesExAdded,
-                                           uint32_t dwStylesExRemoved) {
-  if (!m_pEdit)
-    DisForm_InitComboEdit();
-
-  bool bAddDropDown = !!(dwStylesExAdded & FWL_STYLEEXT_CMB_DropDown);
-  bool bDelDropDown = !!(dwStylesExRemoved & FWL_STYLEEXT_CMB_DropDown);
-
-  dwStylesExRemoved &= ~FWL_STYLEEXT_CMB_DropDown;
-  m_pProperties->m_dwStyleExes |= FWL_STYLEEXT_CMB_DropDown;
-
-  if (bAddDropDown)
-    m_pEdit->ModifyStylesEx(0, FWL_STYLEEXT_EDT_ReadOnly);
-  else if (bDelDropDown)
-    m_pEdit->ModifyStylesEx(FWL_STYLEEXT_EDT_ReadOnly, 0);
-  CFWL_Widget::ModifyStylesEx(dwStylesExAdded, dwStylesExRemoved);
-}
-
-void CFWL_ComboBox::DisForm_Update() {
-  if (m_iLock)
-    return;
-  if (m_pEdit)
-    ResetEditAlignment();
-  ResetTheme();
-  Layout();
-}
-
-FWL_WidgetHit CFWL_ComboBox::DisForm_HitTest(const CFX_PointF& point) {
-  CFX_RectF rect(0, 0, m_pProperties->m_rtWidget.width - m_rtBtn.width,
-                 m_pProperties->m_rtWidget.height);
-  if (rect.Contains(point))
-    return FWL_WidgetHit::Edit;
-  if (m_rtBtn.Contains(point))
-    return FWL_WidgetHit::Client;
-  if (DisForm_IsDropListVisible()) {
-    rect = m_pListBox->GetWidgetRect();
-    if (rect.Contains(point))
-      return FWL_WidgetHit::Client;
-  }
-  return FWL_WidgetHit::Unknown;
-}
-
-void CFWL_ComboBox::DisForm_DrawWidget(CXFA_Graphics* pGraphics,
-                                       const CFX_Matrix* pMatrix) {
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
-  CFX_Matrix mtOrg;
-  if (pMatrix)
-    mtOrg = *pMatrix;
-
-  pGraphics->SaveGraphState();
-  pGraphics->ConcatMatrix(&mtOrg);
-  if (!m_rtBtn.IsEmpty(0.1f)) {
-    CFWL_ThemeBackground param;
-    param.m_pWidget = this;
-    param.m_iPart = CFWL_Part::DropDownButton;
-    param.m_dwStates = m_iBtnState;
-    param.m_pGraphics = pGraphics;
-    param.m_rtPart = m_rtBtn;
-    pTheme->DrawBackground(&param);
-  }
-  pGraphics->RestoreGraphState();
-
-  if (m_pEdit) {
-    CFX_RectF rtEdit = m_pEdit->GetWidgetRect();
-    CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top);
-    mt.Concat(mtOrg);
-    m_pEdit->DrawWidget(pGraphics, mt);
-  }
-  if (m_pListBox && DisForm_IsDropListVisible()) {
-    CFX_RectF rtList = m_pListBox->GetWidgetRect();
-    CFX_Matrix mt(1, 0, 0, 1, rtList.left, rtList.top);
-    mt.Concat(mtOrg);
-    m_pListBox->DrawWidget(pGraphics, mt);
-  }
-}
-
-CFX_RectF CFWL_ComboBox::DisForm_GetBBox() const {
-  CFX_RectF rect = m_pProperties->m_rtWidget;
-  if (!m_pListBox || !DisForm_IsDropListVisible())
-    return rect;
-
-  CFX_RectF rtList = m_pListBox->GetWidgetRect();
-  rtList.Offset(rect.left, rect.top);
-  rect.Union(rtList);
-  return rect;
-}
-
-void CFWL_ComboBox::DisForm_Layout() {
-  m_rtClient = GetClientRect();
-  m_rtContent = m_rtClient;
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  if (!theme)
-    return;
-
-  float borderWidth = 1;
-  float fBtn = theme->GetScrollBarWidth();
-  if (!(GetStylesEx() & FWL_STYLEEXT_CMB_ReadOnly)) {
-    m_rtBtn =
-        CFX_RectF(m_rtClient.right() - fBtn, m_rtClient.top + borderWidth,
-                  fBtn - borderWidth, m_rtClient.height - 2 * borderWidth);
-  }
-
-  CFWL_ThemePart part;
-  part.m_pWidget = this;
-  CFX_RectF pUIMargin = theme->GetUIMargin(&part);
-  m_rtContent.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
-                      pUIMargin.height);
-
-  if (!IsDropDownStyle() || !m_pEdit)
-    return;
-
-  CFX_RectF rtEdit(m_rtContent.left, m_rtContent.top, m_rtContent.width - fBtn,
-                   m_rtContent.height);
-  m_pEdit->SetWidgetRect(rtEdit);
-
-  if (m_iCurSel >= 0) {
-    CFWL_ListItem* hItem = m_pListBox->GetItem(this, m_iCurSel);
-    m_pEdit->LockUpdate();
-    m_pEdit->SetText(hItem ? hItem->GetText() : L"");
-    m_pEdit->UnlockUpdate();
-  }
-  m_pEdit->Update();
-}
-
 void CFWL_ComboBox::OnProcessMessage(CFWL_Message* pMessage) {
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_OnProcessMessage(pMessage);
-    return;
-  }
-  if (!pMessage)
-    return;
-
-  switch (pMessage->GetType()) {
-    case CFWL_Message::Type::SetFocus:
-      OnFocusChanged(pMessage, true);
-      break;
-    case CFWL_Message::Type::KillFocus:
-      OnFocusChanged(pMessage, false);
-      break;
-    case CFWL_Message::Type::Mouse: {
-      CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
-      switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
-          OnLButtonDown(pMsg);
-          break;
-        case FWL_MouseCommand::LeftButtonUp:
-          OnLButtonUp(pMsg);
-          break;
-        case FWL_MouseCommand::Move:
-          OnMouseMove(pMsg);
-          break;
-        case FWL_MouseCommand::Leave:
-          OnMouseLeave(pMsg);
-          break;
-        default:
-          break;
-      }
-      break;
-    }
-    case CFWL_Message::Type::Key:
-      OnKey(static_cast<CFWL_MessageKey*>(pMessage));
-      break;
-    default:
-      break;
-  }
-
-  CFWL_Widget::OnProcessMessage(pMessage);
-}
-
-void CFWL_ComboBox::OnProcessEvent(CFWL_Event* pEvent) {
-  CFWL_Event::Type type = pEvent->GetType();
-  if (type == CFWL_Event::Type::Scroll) {
-    CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
-    CFWL_EventScroll pScrollEv(this);
-    pScrollEv.m_iScrollCode = pScrollEvent->m_iScrollCode;
-    pScrollEv.m_fPos = pScrollEvent->m_fPos;
-    DispatchEvent(&pScrollEv);
-  } else if (type == CFWL_Event::Type::TextChanged) {
-    CFWL_Event pTemp(CFWL_Event::Type::EditChanged, this);
-    DispatchEvent(&pTemp);
-  }
-}
-
-void CFWL_ComboBox::OnDrawWidget(CXFA_Graphics* pGraphics,
-                                 const CFX_Matrix& matrix) {
-  DrawWidget(pGraphics, matrix);
-}
-
-void CFWL_ComboBox::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
-  if (bSet) {
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-    if (IsDropDownStyle() && pMsg->m_pSrcTarget != m_pListBox.get()) {
-      if (!m_pEdit)
-        return;
-      m_pEdit->SetSelected();
-      return;
-    }
-
-    RepaintRect(m_rtClient);
-    return;
-  }
-
-  m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
-  if (!IsDropDownStyle() || pMsg->m_pDstTarget == m_pListBox.get()) {
-    RepaintRect(m_rtClient);
-    return;
-  }
-  if (!m_pEdit)
-    return;
-
-  m_pEdit->FlagFocus(false);
-  m_pEdit->ClearSelected();
-}
-
-void CFWL_ComboBox::OnLButtonDown(CFWL_MessageMouse* pMsg) {
-  if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
-    return;
-
-  CFX_RectF& rtBtn = IsDropDownStyle() ? m_rtBtn : m_rtClient;
-  if (!rtBtn.Contains(pMsg->m_pos))
-    return;
-
-  if (IsDropDownStyle() && m_pEdit)
-    MatchEditText();
-
-  m_bLButtonDown = true;
-  m_iBtnState = CFWL_PartState_Pressed;
-  RepaintRect(m_rtClient);
-
-  ShowDropList(true);
-  m_iBtnState = CFWL_PartState_Normal;
-  RepaintRect(m_rtClient);
-}
-
-void CFWL_ComboBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
-  m_bLButtonDown = false;
-  if (m_rtBtn.Contains(pMsg->m_pos))
-    m_iBtnState = CFWL_PartState_Hovered;
-  else
-    m_iBtnState = CFWL_PartState_Normal;
-
-  RepaintRect(m_rtBtn);
-}
-
-void CFWL_ComboBox::OnMouseMove(CFWL_MessageMouse* pMsg) {
-  int32_t iOldState = m_iBtnState;
-  if (m_rtBtn.Contains(pMsg->m_pos)) {
-    m_iBtnState =
-        m_bLButtonDown ? CFWL_PartState_Pressed : CFWL_PartState_Hovered;
-  } else {
-    m_iBtnState = CFWL_PartState_Normal;
-  }
-  if ((iOldState != m_iBtnState) &&
-      !((m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) ==
-        FWL_WGTSTATE_Disabled)) {
-    RepaintRect(m_rtBtn);
-  }
-}
-
-void CFWL_ComboBox::OnMouseLeave(CFWL_MessageMouse* pMsg) {
-  if (!IsDropListVisible() &&
-      !((m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) ==
-        FWL_WGTSTATE_Disabled)) {
-    m_iBtnState = CFWL_PartState_Normal;
-    RepaintRect(m_rtBtn);
-  }
-}
-
-void CFWL_ComboBox::OnKey(CFWL_MessageKey* pMsg) {
-  uint32_t dwKeyCode = pMsg->m_dwKeyCode;
-  if (dwKeyCode == FWL_VKEY_Tab)
-    return;
-  if (pMsg->m_pDstTarget == this)
-    DoSubCtrlKey(pMsg);
-}
-
-void CFWL_ComboBox::DoSubCtrlKey(CFWL_MessageKey* pMsg) {
-  uint32_t dwKeyCode = pMsg->m_dwKeyCode;
-  const bool bUp = dwKeyCode == FWL_VKEY_Up;
-  const bool bDown = dwKeyCode == FWL_VKEY_Down;
-  if (bUp || bDown) {
-    int32_t iCount = m_pListBox->CountItems(nullptr);
-    if (iCount < 1)
-      return;
-
-    bool bMatchEqual = false;
-    int32_t iCurSel = m_iCurSel;
-    bool bDropDown = IsDropDownStyle();
-    if (bDropDown && m_pEdit) {
-      WideString wsText = m_pEdit->GetText();
-      iCurSel = m_pListBox->MatchItem(wsText);
-      if (iCurSel >= 0) {
-        CFWL_ListItem* hItem = m_pListBox->GetItem(this, iCurSel);
-        bMatchEqual = wsText == (hItem ? hItem->GetText() : L"");
-      }
-    }
-    if (iCurSel < 0) {
-      iCurSel = 0;
-    } else if (!bDropDown || bMatchEqual) {
-      if ((bUp && iCurSel == 0) || (bDown && iCurSel == iCount - 1))
-        return;
-      if (bUp)
-        iCurSel--;
-      else
-        iCurSel++;
-    }
-    m_iCurSel = iCurSel;
-    if (bDropDown && m_pEdit)
-      SyncEditText(m_iCurSel);
-    else
-      RepaintRect(m_rtClient);
-    return;
-  }
-
-  if (IsDropDownStyle())
-    m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
-}
-
-void CFWL_ComboBox::DisForm_OnProcessMessage(CFWL_Message* pMessage) {
   if (!pMessage)
     return;
 
@@ -898,12 +434,12 @@
   switch (pMessage->GetType()) {
     case CFWL_Message::Type::SetFocus: {
       backDefault = false;
-      DisForm_OnFocusChanged(pMessage, true);
+      OnFocusChanged(pMessage, true);
       break;
     }
     case CFWL_Message::Type::KillFocus: {
       backDefault = false;
-      DisForm_OnFocusChanged(pMessage, false);
+      OnFocusChanged(pMessage, false);
       break;
     }
     case CFWL_Message::Type::Mouse: {
@@ -911,7 +447,7 @@
       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
       switch (pMsg->m_dwCmd) {
         case FWL_MouseCommand::LeftButtonDown:
-          DisForm_OnLButtonDown(pMsg);
+          OnLButtonDown(pMsg);
           break;
         case FWL_MouseCommand::LeftButtonUp:
           OnLButtonUp(pMsg);
@@ -926,43 +462,71 @@
       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
       if (pKey->m_dwCmd == FWL_KeyCommand::KeyUp)
         break;
-      if (DisForm_IsDropListVisible() &&
-          pKey->m_dwCmd == FWL_KeyCommand::KeyDown) {
-        bool bListKey = pKey->m_dwKeyCode == FWL_VKEY_Up ||
-                        pKey->m_dwKeyCode == FWL_VKEY_Down ||
-                        pKey->m_dwKeyCode == FWL_VKEY_Return ||
-                        pKey->m_dwKeyCode == FWL_VKEY_Escape;
+      if (IsDropListVisible() && pKey->m_dwCmd == FWL_KeyCommand::KeyDown) {
+        bool bListKey = pKey->m_dwKeyCode == XFA_FWL_VKEY_Up ||
+                        pKey->m_dwKeyCode == XFA_FWL_VKEY_Down ||
+                        pKey->m_dwKeyCode == XFA_FWL_VKEY_Return ||
+                        pKey->m_dwKeyCode == XFA_FWL_VKEY_Escape;
         if (bListKey) {
           m_pListBox->GetDelegate()->OnProcessMessage(pMessage);
           break;
         }
       }
-      DisForm_OnKey(pKey);
+      OnKey(pKey);
       break;
     }
     default:
       break;
   }
-  if (backDefault)
+  // Dst target could be |this|, continue only if not destroyed by above.
+  if (backDefault && pMessage->GetDstTarget())
     CFWL_Widget::OnProcessMessage(pMessage);
 }
 
-void CFWL_ComboBox::DisForm_OnLButtonDown(CFWL_MessageMouse* pMsg) {
-  bool bDropDown = DisForm_IsDropListVisible();
+void CFWL_ComboBox::OnProcessEvent(CFWL_Event* pEvent) {
+  CFWL_Event::Type type = pEvent->GetType();
+  if (type == CFWL_Event::Type::Scroll) {
+    CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
+    CFWL_EventScroll pScrollEv(this);
+    pScrollEv.m_iScrollCode = pScrollEvent->m_iScrollCode;
+    pScrollEv.m_fPos = pScrollEvent->m_fPos;
+    DispatchEvent(&pScrollEv);
+  } else if (type == CFWL_Event::Type::TextWillChange) {
+    CFWL_Event pTemp(CFWL_Event::Type::EditChanged, this);
+    DispatchEvent(&pTemp);
+  }
+}
+
+void CFWL_ComboBox::OnDrawWidget(CXFA_Graphics* pGraphics,
+                                 const CFX_Matrix& matrix) {
+  DrawWidget(pGraphics, matrix);
+}
+
+void CFWL_ComboBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
+  if (m_rtBtn.Contains(pMsg->m_pos))
+    m_iBtnState = CFWL_PartState_Hovered;
+  else
+    m_iBtnState = CFWL_PartState_Normal;
+
+  RepaintRect(m_rtBtn);
+}
+
+void CFWL_ComboBox::OnLButtonDown(CFWL_MessageMouse* pMsg) {
+  bool bDropDown = IsDropListVisible();
   CFX_RectF& rtBtn = bDropDown ? m_rtBtn : m_rtClient;
   if (!rtBtn.Contains(pMsg->m_pos))
     return;
 
-  if (DisForm_IsDropListVisible()) {
-    DisForm_ShowDropList(false);
+  if (IsDropListVisible()) {
+    ShowDropList(false);
     return;
   }
   if (m_pEdit)
     MatchEditText();
-  DisForm_ShowDropList(true);
+  ShowDropList(true);
 }
 
-void CFWL_ComboBox::DisForm_OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
+void CFWL_ComboBox::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
   if (bSet) {
     m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
     if ((m_pEdit->GetStates() & FWL_WGTSTATE_Focused) == 0) {
@@ -971,16 +535,16 @@
     }
   } else {
     m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
-    DisForm_ShowDropList(false);
+    ShowDropList(false);
     CFWL_MessageKillFocus msg(m_pEdit.get());
     m_pEdit->GetDelegate()->OnProcessMessage(&msg);
   }
 }
 
-void CFWL_ComboBox::DisForm_OnKey(CFWL_MessageKey* pMsg) {
+void CFWL_ComboBox::OnKey(CFWL_MessageKey* pMsg) {
   uint32_t dwKeyCode = pMsg->m_dwKeyCode;
-  const bool bUp = dwKeyCode == FWL_VKEY_Up;
-  const bool bDown = dwKeyCode == FWL_VKEY_Down;
+  const bool bUp = dwKeyCode == XFA_FWL_VKEY_Up;
+  const bool bDown = dwKeyCode == XFA_FWL_VKEY_Down;
   if (bUp || bDown) {
     CFWL_ComboList* pComboList = m_pListBox.get();
     int32_t iCount = pComboList->CountItems(nullptr);
@@ -991,10 +555,10 @@
     int32_t iCurSel = m_iCurSel;
     if (m_pEdit) {
       WideString wsText = m_pEdit->GetText();
-      iCurSel = pComboList->MatchItem(wsText);
+      iCurSel = pComboList->MatchItem(wsText.AsStringView());
       if (iCurSel >= 0) {
         CFWL_ListItem* item = m_pListBox->GetSelItem(iCurSel);
-        bMatchEqual = wsText == (item ? item->GetText() : L"");
+        bMatchEqual = wsText == (item ? item->GetText() : WideString());
       }
     }
     if (iCurSel < 0) {
@@ -1014,3 +578,11 @@
   if (m_pEdit)
     m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
 }
+
+void CFWL_ComboBox::GetPopupPos(float fMinHeight,
+                                float fMaxHeight,
+                                const CFX_RectF& rtAnchor,
+                                CFX_RectF* pPopupRect) {
+  m_pWidgetMgr->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
+                                   pPopupRect);
+}
diff --git a/xfa/fwl/cfwl_combobox.h b/xfa/fwl/cfwl_combobox.h
index a9dc635..39c952c 100644
--- a/xfa/fwl/cfwl_combobox.h
+++ b/xfa/fwl/cfwl_combobox.h
@@ -9,17 +9,13 @@
 
 #include <memory>
 
-#include "xfa/fwl/cfwl_comboboxproxy.h"
 #include "xfa/fwl/cfwl_comboedit.h"
 #include "xfa/fwl/cfwl_combolist.h"
-#include "xfa/fwl/cfwl_form.h"
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fxgraphics/cxfa_graphics.h"
 
 class CFWL_WidgetProperties;
 class CFWL_ComboBox;
-class CFWL_ComboBoxProxy;
-class CFWL_FormProxy;
 class CFWL_ListBox;
 class CFWL_Widget;
 
@@ -39,7 +35,7 @@
 #define FWL_STYLEEXT_CMB_ListItemAlignMask (3L << 10)
 #define FWL_STYLEEXT_CMB_ReadOnly (1L << 13)
 
-class CFWL_ComboBox : public CFWL_Widget {
+class CFWL_ComboBox final : public CFWL_Widget {
  public:
   explicit CFWL_ComboBox(const CFWL_App* pApp);
   ~CFWL_ComboBox() override;
@@ -63,7 +59,7 @@
   int32_t GetCurSel() const { return m_iCurSel; }
   void SetCurSel(int32_t iSel);
 
-  void AddString(const WideStringView& wsText);
+  void AddString(const WideString& wsText);
   void RemoveAt(int32_t iIndex);
   void RemoveAll();
 
@@ -92,12 +88,6 @@
 
   CFX_RectF GetBBox() const;
   void EditModifyStylesEx(uint32_t dwStylesExAdded, uint32_t dwStylesExRemoved);
-
-  void DrawStretchHandler(CXFA_Graphics* pGraphics, const CFX_Matrix* pMatrix);
-  bool IsDropListVisible() const {
-    return m_pComboBoxProxy &&
-           !(m_pComboBoxProxy->GetStates() & FWL_WGTSTATE_Invisible);
-  }
   void ShowDropList(bool bActivate);
 
   CFWL_ComboEdit* GetComboEdit() const { return m_pEdit.get(); }
@@ -115,45 +105,26 @@
   void ResetTheme();
   void ResetEditAlignment();
   void ResetListItemAlignment();
-  void InitProxyForm();
-  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
-  void OnLButtonDown(CFWL_MessageMouse* pMsg);
+  void GetPopupPos(float fMinHeight,
+                   float fMaxHeight,
+                   const CFX_RectF& rtAnchor,
+                   CFX_RectF* pPopupRect);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
-  void OnMouseMove(CFWL_MessageMouse* pMsg);
-  void OnMouseLeave(CFWL_MessageMouse* pMsg);
-  void OnKey(CFWL_MessageKey* pMsg);
-  void DoSubCtrlKey(CFWL_MessageKey* pMsg);
 
-  void DisForm_InitComboList();
-  void DisForm_InitComboEdit();
-  void DisForm_ShowDropList(bool bActivate);
-  bool DisForm_IsDropListVisible() const {
-    return !(m_pListBox->GetStates() & FWL_WGTSTATE_Invisible);
-  }
-  void DisForm_ModifyStylesEx(uint32_t dwStylesExAdded,
-                              uint32_t dwStylesExRemoved);
-  void DisForm_Update();
-  FWL_WidgetHit DisForm_HitTest(const CFX_PointF& point);
-  void DisForm_DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix* pMatrix);
-  CFX_RectF DisForm_GetBBox() const;
-  void DisForm_Layout();
-  void DisForm_OnProcessMessage(CFWL_Message* pMessage);
-  void DisForm_OnLButtonDown(CFWL_MessageMouse* pMsg);
-  void DisForm_OnFocusChanged(CFWL_Message* pMsg, bool bSet);
-  void DisForm_OnKey(CFWL_MessageKey* pMsg);
+  void InitComboList();
+  void InitComboEdit();
+  bool IsDropListVisible() const { return m_pListBox->IsVisible(); }
+  void OnLButtonDown(CFWL_MessageMouse* pMsg);
+  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
+  void OnKey(CFWL_MessageKey* pMsg);
 
   CFX_RectF m_rtClient;
   CFX_RectF m_rtContent;
   CFX_RectF m_rtBtn;
-  CFX_RectF m_rtList;
-  CFX_RectF m_rtProxy;
-  CFX_RectF m_rtHandler;
   std::unique_ptr<CFWL_ComboEdit> m_pEdit;
   std::unique_ptr<CFWL_ComboList> m_pListBox;
-  CFWL_ComboBoxProxy* m_pComboBoxProxy;  // Can this be a unique_ptr?
-  bool m_bLButtonDown;
-  int32_t m_iCurSel;
-  int32_t m_iBtnState;
+  int32_t m_iCurSel = -1;
+  int32_t m_iBtnState = CFWL_PartState_Normal;
 };
 
 #endif  // XFA_FWL_CFWL_COMBOBOX_H_
diff --git a/xfa/fwl/cfwl_comboboxproxy.cpp b/xfa/fwl/cfwl_comboboxproxy.cpp
deleted file mode 100644
index d1db1c0..0000000
--- a/xfa/fwl/cfwl_comboboxproxy.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_comboboxproxy.h"
-
-#include <memory>
-#include <utility>
-
-#include "xfa/fwl/cfwl_app.h"
-#include "xfa/fwl/cfwl_combobox.h"
-#include "xfa/fwl/cfwl_messagekillfocus.h"
-#include "xfa/fwl/cfwl_messagemouse.h"
-#include "xfa/fwl/cfwl_notedriver.h"
-
-CFWL_ComboBoxProxy::CFWL_ComboBoxProxy(
-    CFWL_ComboBox* pComboBox,
-    const CFWL_App* app,
-    std::unique_ptr<CFWL_WidgetProperties> properties,
-    CFWL_Widget* pOuter)
-    : CFWL_FormProxy(app, std::move(properties), pOuter),
-      m_bLButtonDown(false),
-      m_bLButtonUpSelf(false),
-      m_pComboBox(pComboBox) {}
-
-CFWL_ComboBoxProxy::~CFWL_ComboBoxProxy() {}
-
-void CFWL_ComboBoxProxy::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-
-  switch (pMessage->GetType()) {
-    case CFWL_Message::Type::Mouse: {
-      CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
-      switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
-          OnLButtonDown(pMsg);
-          break;
-        case FWL_MouseCommand::LeftButtonUp:
-          OnLButtonUp(pMsg);
-          break;
-        default:
-          break;
-      }
-      break;
-    }
-    case CFWL_Message::Type::KillFocus:
-      OnFocusChanged(pMessage, false);
-      break;
-    case CFWL_Message::Type::SetFocus:
-      OnFocusChanged(pMessage, true);
-      break;
-    default:
-      break;
-  }
-  CFWL_Widget::OnProcessMessage(pMessage);
-}
-
-void CFWL_ComboBoxProxy::OnDrawWidget(CXFA_Graphics* pGraphics,
-                                      const CFX_Matrix& matrix) {
-  m_pComboBox->DrawStretchHandler(pGraphics, &matrix);
-}
-
-void CFWL_ComboBoxProxy::OnLButtonDown(CFWL_Message* pMessage) {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
-  if (CFX_RectF(0, 0, GetWidgetRect().Size()).Contains(pMsg->m_pos)) {
-    m_bLButtonDown = true;
-    pDriver->SetGrab(this, true);
-  } else {
-    m_bLButtonDown = false;
-    pDriver->SetGrab(this, false);
-    m_pComboBox->ShowDropList(false);
-  }
-}
-
-void CFWL_ComboBoxProxy::OnLButtonUp(CFWL_Message* pMessage) {
-  m_bLButtonDown = false;
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  pDriver->SetGrab(this, false);
-  if (!m_bLButtonUpSelf) {
-    m_bLButtonUpSelf = true;
-    return;
-  }
-
-  CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
-  if (!CFX_RectF(0, 0, GetWidgetRect().Size()).Contains(pMsg->m_pos) &&
-      m_pComboBox->IsDropListVisible()) {
-    m_pComboBox->ShowDropList(false);
-  }
-}
-
-void CFWL_ComboBoxProxy::OnFocusChanged(CFWL_Message* pMessage, bool bSet) {
-  if (bSet)
-    return;
-
-  CFWL_MessageKillFocus* pMsg = static_cast<CFWL_MessageKillFocus*>(pMessage);
-  if (!pMsg->m_pSetFocus)
-    m_pComboBox->ShowDropList(false);
-}
diff --git a/xfa/fwl/cfwl_comboboxproxy.h b/xfa/fwl/cfwl_comboboxproxy.h
deleted file mode 100644
index bb21707..0000000
--- a/xfa/fwl/cfwl_comboboxproxy.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_COMBOBOXPROXY_H_
-#define XFA_FWL_CFWL_COMBOBOXPROXY_H_
-
-#include <memory>
-
-#include "xfa/fwl/cfwl_formproxy.h"
-
-class CFWL_ComboBox;
-
-class CFWL_ComboBoxProxy : public CFWL_FormProxy {
- public:
-  CFWL_ComboBoxProxy(CFWL_ComboBox* pCombobBox,
-                     const CFWL_App* app,
-                     std::unique_ptr<CFWL_WidgetProperties> properties,
-                     CFWL_Widget* pOuter);
-  ~CFWL_ComboBoxProxy() override;
-
-  // CFWL_FormProxy
-  void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
-                    const CFX_Matrix& matrix) override;
-
-  void Reset() { m_bLButtonUpSelf = false; }
-
- private:
-  void OnLButtonDown(CFWL_Message* pMsg);
-  void OnLButtonUp(CFWL_Message* pMsg);
-  void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
-
-  bool m_bLButtonDown;
-  bool m_bLButtonUpSelf;
-  CFWL_ComboBox* m_pComboBox;
-};
-
-#endif  // XFA_FWL_CFWL_COMBOBOXPROXY_H_
diff --git a/xfa/fwl/cfwl_comboedit.cpp b/xfa/fwl/cfwl_comboedit.cpp
index 4fa1277..6ab081a 100644
--- a/xfa/fwl/cfwl_comboedit.cpp
+++ b/xfa/fwl/cfwl_comboedit.cpp
@@ -18,9 +18,10 @@
     std::unique_ptr<CFWL_WidgetProperties> properties,
     CFWL_Widget* pOuter)
     : CFWL_Edit(app, std::move(properties), pOuter) {
-  m_pOuter = static_cast<CFWL_ComboBox*>(pOuter);
 }
 
+CFWL_ComboEdit::~CFWL_ComboEdit() = default;
+
 void CFWL_ComboEdit::ClearSelected() {
   ClearSelection();
   RepaintRect(GetRTClient());
@@ -62,7 +63,6 @@
       if ((pMsg->m_dwCmd == FWL_MouseCommand::LeftButtonDown) &&
           ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)) {
         SetSelected();
-        m_pOuter->SetFocus(true);
       }
       break;
     }
diff --git a/xfa/fwl/cfwl_comboedit.h b/xfa/fwl/cfwl_comboedit.h
index efdabd3..68e6caa 100644
--- a/xfa/fwl/cfwl_comboedit.h
+++ b/xfa/fwl/cfwl_comboedit.h
@@ -15,11 +15,12 @@
 
 class CFWL_ComboBox;
 
-class CFWL_ComboEdit : public CFWL_Edit {
+class CFWL_ComboEdit final : public CFWL_Edit {
  public:
   CFWL_ComboEdit(const CFWL_App* app,
                  std::unique_ptr<CFWL_WidgetProperties> properties,
                  CFWL_Widget* pOuter);
+  ~CFWL_ComboEdit() override;
 
   // CFWL_Edit.
   void OnProcessMessage(CFWL_Message* pMessage) override;
@@ -27,9 +28,6 @@
   void ClearSelected();
   void SetSelected();
   void FlagFocus(bool bSet);
-
- private:
-  CFWL_ComboBox* m_pOuter;
 };
 
 #endif  // XFA_FWL_CFWL_COMBOEDIT_H_
diff --git a/xfa/fwl/cfwl_combolist.cpp b/xfa/fwl/cfwl_combolist.cpp
index 1180acf..d25f312 100644
--- a/xfa/fwl/cfwl_combolist.cpp
+++ b/xfa/fwl/cfwl_combolist.cpp
@@ -9,31 +9,31 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_comboedit.h"
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fwl/cfwl_messagekey.h"
 #include "xfa/fwl/cfwl_messagekillfocus.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 
 CFWL_ComboList::CFWL_ComboList(
     const CFWL_App* app,
     std::unique_ptr<CFWL_WidgetProperties> properties,
     CFWL_Widget* pOuter)
-    : CFWL_ListBox(app, std::move(properties), pOuter), m_bNotifyOwner(true) {
+    : CFWL_ListBox(app, std::move(properties), pOuter) {
   ASSERT(pOuter);
 }
 
-int32_t CFWL_ComboList::MatchItem(const WideString& wsMatch) {
+int32_t CFWL_ComboList::MatchItem(WideStringView wsMatch) {
   if (wsMatch.IsEmpty())
     return -1;
 
   int32_t iCount = CountItems(this);
   for (int32_t i = 0; i < iCount; i++) {
     CFWL_ListItem* hItem = GetItem(this, i);
-    WideString wsText = hItem ? hItem->GetText() : L"";
-    auto pos = wsText.Find(wsMatch.c_str());
+    WideString wsText = hItem ? hItem->GetText() : WideString();
+    auto pos = wsText.Find(wsMatch);
     if (pos.has_value() && pos.value() == 0)
       return i;
   }
@@ -122,8 +122,8 @@
 
   CFWL_MessageKillFocus* pKill = static_cast<CFWL_MessageKillFocus*>(pMsg);
   CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
-  if (pKill->m_pSetFocus == m_pOuter ||
-      pKill->m_pSetFocus == pOuter->GetComboEdit()) {
+  if (pKill->IsFocusedOnWidget(m_pOuter) ||
+      pKill->IsFocusedOnWidget(pOuter->GetComboEdit())) {
     pOuter->ShowDropList(false);
   }
 }
@@ -188,13 +188,13 @@
   if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown) {
     uint32_t dwKeyCode = pKey->m_dwKeyCode;
     switch (dwKeyCode) {
-      case FWL_VKEY_Return:
-      case FWL_VKEY_Escape: {
+      case XFA_FWL_VKEY_Return:
+      case XFA_FWL_VKEY_Escape: {
         pOuter->ShowDropList(false);
         return true;
       }
-      case FWL_VKEY_Up:
-      case FWL_VKEY_Down: {
+      case XFA_FWL_VKEY_Up:
+      case XFA_FWL_VKEY_Down: {
         OnDropListKeyDown(pKey);
         pOuter->ProcessSelChanged(false);
         return true;
@@ -208,7 +208,7 @@
     bPropagate = true;
   }
   if (bPropagate) {
-    pKey->m_pDstTarget = m_pOuter;
+    pKey->SetDstTarget(m_pOuter);
     pOuter->GetDelegate()->OnProcessMessage(pKey);
     return true;
   }
@@ -218,10 +218,10 @@
 void CFWL_ComboList::OnDropListKeyDown(CFWL_MessageKey* pKey) {
   uint32_t dwKeyCode = pKey->m_dwKeyCode;
   switch (dwKeyCode) {
-    case FWL_VKEY_Up:
-    case FWL_VKEY_Down:
-    case FWL_VKEY_Home:
-    case FWL_VKEY_End: {
+    case XFA_FWL_VKEY_Up:
+    case XFA_FWL_VKEY_Down:
+    case XFA_FWL_VKEY_Home:
+    case XFA_FWL_VKEY_End: {
       CFWL_ComboBox* pOuter = static_cast<CFWL_ComboBox*>(m_pOuter);
       CFWL_ListItem* hItem = GetItem(this, pOuter->GetCurrentSelection());
       hItem = GetListItem(hItem, dwKeyCode);
diff --git a/xfa/fwl/cfwl_combolist.h b/xfa/fwl/cfwl_combolist.h
index a4d5135..c275c72 100644
--- a/xfa/fwl/cfwl_combolist.h
+++ b/xfa/fwl/cfwl_combolist.h
@@ -13,7 +13,7 @@
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
 
-class CFWL_ComboList : public CFWL_ListBox {
+class CFWL_ComboList final : public CFWL_ListBox {
  public:
   CFWL_ComboList(const CFWL_App* app,
                  std::unique_ptr<CFWL_WidgetProperties> properties,
@@ -22,10 +22,8 @@
   // CFWL_ListBox.
   void OnProcessMessage(CFWL_Message* pMessage) override;
 
-  int32_t MatchItem(const WideString& wsMatch);
-
+  int32_t MatchItem(WideStringView wsMatch);
   void ChangeSelected(int32_t iSel);
-
   void SetNotifyOwner(bool notify) { m_bNotifyOwner = notify; }
 
  private:
@@ -37,7 +35,7 @@
   bool OnDropListKey(CFWL_MessageKey* pKey);
   void OnDropListKeyDown(CFWL_MessageKey* pKey);
 
-  bool m_bNotifyOwner;
+  bool m_bNotifyOwner = true;
 };
 
 #endif  // XFA_FWL_CFWL_COMBOLIST_H_
diff --git a/xfa/fwl/cfwl_datetimeedit.cpp b/xfa/fwl/cfwl_datetimeedit.cpp
index f1ddbb1..313e89a 100644
--- a/xfa/fwl/cfwl_datetimeedit.cpp
+++ b/xfa/fwl/cfwl_datetimeedit.cpp
@@ -9,7 +9,6 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
@@ -20,23 +19,10 @@
     CFWL_Widget* pOuter)
     : CFWL_Edit(app, std::move(properties), pOuter) {}
 
+CFWL_DateTimeEdit::~CFWL_DateTimeEdit() = default;
+
 void CFWL_DateTimeEdit::OnProcessMessage(CFWL_Message* pMessage) {
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_OnProcessMessage(pMessage);
-    return;
-  }
-
-  CFWL_Message::Type type = pMessage->GetType();
-  if (type == CFWL_Message::Type::SetFocus ||
-      type == CFWL_Message::Type::KillFocus) {
-    CFWL_Widget* pOuter = GetOuter();
-    pOuter->GetDelegate()->OnProcessMessage(pMessage);
-  }
-}
-
-void CFWL_DateTimeEdit::DisForm_OnProcessMessage(CFWL_Message* pMessage) {
-  if (!m_pWidgetMgr->IsFormDisabled() ||
-      pMessage->GetType() != CFWL_Message::Type::Mouse) {
+  if (pMessage->GetType() != CFWL_Message::Type::Mouse) {
     CFWL_Edit::OnProcessMessage(pMessage);
     return;
   }
diff --git a/xfa/fwl/cfwl_datetimeedit.h b/xfa/fwl/cfwl_datetimeedit.h
index 923ad05..e617ff4 100644
--- a/xfa/fwl/cfwl_datetimeedit.h
+++ b/xfa/fwl/cfwl_datetimeedit.h
@@ -13,17 +13,15 @@
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
 
-class CFWL_DateTimeEdit : public CFWL_Edit {
+class CFWL_DateTimeEdit final : public CFWL_Edit {
  public:
   CFWL_DateTimeEdit(const CFWL_App* app,
                     std::unique_ptr<CFWL_WidgetProperties> properties,
                     CFWL_Widget* pOuter);
+  ~CFWL_DateTimeEdit() override;
 
   // CFWL_Edit.
   void OnProcessMessage(CFWL_Message* pMessage) override;
-
- private:
-  void DisForm_OnProcessMessage(CFWL_Message* pMessage);
 };
 
 #endif  // XFA_FWL_CFWL_DATETIMEEDIT_H_
diff --git a/xfa/fwl/cfwl_datetimepicker.cpp b/xfa/fwl/cfwl_datetimepicker.cpp
index f3da454..11ea810 100644
--- a/xfa/fwl/cfwl_datetimepicker.cpp
+++ b/xfa/fwl/cfwl_datetimepicker.cpp
@@ -12,7 +12,6 @@
 #include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_eventselectchanged.h"
-#include "xfa/fwl/cfwl_formproxy.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_messagesetfocus.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -26,12 +25,7 @@
 
 }  // namespace
 CFWL_DateTimePicker::CFWL_DateTimePicker(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr),
-      m_iBtnState(1),
-      m_iYear(-1),
-      m_iMonth(-1),
-      m_iDay(-1),
-      m_bLBtnDown(false) {
+    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
   m_pProperties->m_dwStyleExes = FWL_STYLEEXT_DTP_ShortDateFormat;
 
   auto monthProp = pdfium::MakeUnique<CFWL_WidgetProperties>();
@@ -64,50 +58,42 @@
 }
 
 void CFWL_DateTimePicker::Update() {
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_Update();
-    return;
-  }
   if (m_iLock)
     return;
+
   if (!m_pProperties->m_pThemeProvider)
     m_pProperties->m_pThemeProvider = GetAvailableTheme();
-
-  m_pEdit->SetThemeProvider(m_pProperties->m_pThemeProvider);
+  m_pEdit->SetThemeProvider(m_pProperties->m_pThemeProvider.Get());
   m_rtClient = GetClientRect();
+  m_pEdit->SetWidgetRect(m_rtClient);
+  ResetEditAlignment();
+  m_pEdit->Update();
+  if (!m_pMonthCal->GetThemeProvider())
+    m_pMonthCal->SetThemeProvider(m_pProperties->m_pThemeProvider.Get());
 
   IFWL_ThemeProvider* theme = GetAvailableTheme();
   if (!theme)
     return;
 
-  float fBtn = theme->GetScrollBarWidth();
-  m_rtBtn = CFX_RectF(m_rtClient.right() - fBtn, m_rtClient.top, fBtn - 1,
-                      m_rtClient.height - 1);
-
-  CFX_RectF rtEdit(m_rtClient.left, m_rtClient.top, m_rtClient.width - fBtn,
-                   m_rtClient.height);
-  m_pEdit->SetWidgetRect(rtEdit);
-  ResetEditAlignment();
-  m_pEdit->Update();
-  if (!(m_pMonthCal->GetThemeProvider()))
-    m_pMonthCal->SetThemeProvider(m_pProperties->m_pThemeProvider);
-
+  m_fBtn = theme->GetScrollBarWidth();
   CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
   CFX_RectF rtPopUp(rtMonthCal.left, rtMonthCal.top + kDateTimePickerHeight,
                     rtMonthCal.width, rtMonthCal.height);
   m_pMonthCal->SetWidgetRect(rtPopUp);
   m_pMonthCal->Update();
-  return;
 }
 
 FWL_WidgetHit CFWL_DateTimePicker::HitTest(const CFX_PointF& point) {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_HitTest(point);
-  if (m_rtClient.Contains(point))
+  CFX_RectF rect(0, 0, m_pProperties->m_rtWidget.width,
+                 m_pProperties->m_rtWidget.height);
+  if (rect.Contains(point))
+    return FWL_WidgetHit::Edit;
+  if (NeedsToShowButton())
+    rect.width += m_fBtn;
+  if (rect.Contains(point))
     return FWL_WidgetHit::Client;
   if (IsMonthCalendarVisible()) {
-    CFX_RectF rect = m_pMonthCal->GetWidgetRect();
-    if (rect.Contains(point))
+    if (m_pMonthCal->GetWidgetRect().Contains(point))
       return FWL_WidgetHit::Client;
   }
   return FWL_WidgetHit::Unknown;
@@ -117,18 +103,30 @@
                                      const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
+
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
+  if (!pTheme)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
   if (HasBorder())
     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
   if (!m_rtBtn.IsEmpty())
     DrawDropDownButton(pGraphics, pTheme, &matrix);
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    DisForm_DrawWidget(pGraphics, &matrix);
-    return;
+
+  if (m_pEdit) {
+    CFX_RectF rtEdit = m_pEdit->GetWidgetRect();
+
+    CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top);
+    mt.Concat(matrix);
+    m_pEdit->DrawWidget(pGraphics, mt);
   }
+  if (!IsMonthCalendarVisible())
+    return;
+
+  CFX_RectF rtMonth = m_pMonthCal->GetWidgetRect();
+  CFX_Matrix mt(1, 0, 0, 1, rtMonth.left, rtMonth.top);
+  mt.Concat(matrix);
+  m_pMonthCal->DrawWidget(pGraphics, mt);
 }
 
 void CFWL_DateTimePicker::SetThemeProvider(IFWL_ThemeProvider* pTP) {
@@ -172,14 +170,17 @@
 }
 
 WideString CFWL_DateTimePicker::GetEditText() const {
-  return m_pEdit ? m_pEdit->GetText() : L"";
+  return m_pEdit ? m_pEdit->GetText() : WideString();
+}
+
+int32_t CFWL_DateTimePicker::GetEditTextLength() const {
+  return m_pEdit ? m_pEdit->GetTextLength() : 0;
 }
 
 CFX_RectF CFWL_DateTimePicker::GetBBox() const {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_GetBBox();
-
   CFX_RectF rect = m_pProperties->m_rtWidget;
+  if (NeedsToShowButton())
+    rect.width += m_fBtn;
   if (!IsMonthCalendarVisible())
     return rect;
 
@@ -205,61 +206,57 @@
   param.m_rtPart = m_rtBtn;
   if (pMatrix)
     param.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 WideString CFWL_DateTimePicker::FormatDateString(int32_t iYear,
                                                  int32_t iMonth,
                                                  int32_t iDay) {
-  if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_DTP_ShortDateFormat) ==
-      FWL_STYLEEXT_DTP_ShortDateFormat) {
+  if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_DTP_ShortDateFormat)
     return WideString::Format(L"%d-%d-%d", iYear, iMonth, iDay);
-  }
 
-  if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_DTP_LongDateFormat) ==
-      FWL_STYLEEXT_DTP_LongDateFormat) {
-    return WideString::Format(L"%d Year %d Month %d Day", iYear, iMonth, iDay);
-  }
-
-  return WideString();
+  return WideString::Format(L"%d Year %d Month %d Day", iYear, iMonth, iDay);
 }
 
 void CFWL_DateTimePicker::ShowMonthCalendar(bool bActivate) {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_ShowMonthCalendar(bActivate);
   if (IsMonthCalendarVisible() == bActivate)
     return;
-  if (!m_pForm)
-    InitProxyForm();
 
-  if (!bActivate) {
-    m_pForm->EndDoModal();
-    return;
+  if (bActivate) {
+    CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
+    float fPopupMin = rtMonthCal.height;
+    float fPopupMax = rtMonthCal.height;
+    CFX_RectF rtAnchor(m_pProperties->m_rtWidget);
+    rtAnchor.width = rtMonthCal.width;
+    rtMonthCal.left = m_rtClient.left;
+    rtMonthCal.top = rtAnchor.Height();
+    GetPopupPos(fPopupMin, fPopupMax, rtAnchor, &rtMonthCal);
+    m_pMonthCal->SetWidgetRect(rtMonthCal);
+    if (m_iYear > 0 && m_iMonth > 0 && m_iDay > 0)
+      m_pMonthCal->SetSelect(m_iYear, m_iMonth, m_iDay);
+    m_pMonthCal->Update();
   }
-
-  CFX_RectF rtMonth = m_pMonthCal->GetWidgetRect();
-
-  CFX_RectF rtAnchor(0, 0, m_pProperties->m_rtWidget.width,
-                     m_pProperties->m_rtWidget.height);
-  GetPopupPos(0, rtMonth.height, rtAnchor, rtMonth);
-  m_pForm->SetWidgetRect(rtMonth);
-
-  rtMonth.left = rtMonth.top = 0;
   if (bActivate)
     m_pMonthCal->RemoveStates(FWL_WGTSTATE_Invisible);
   else
     m_pMonthCal->SetStates(FWL_WGTSTATE_Invisible);
-  m_pMonthCal->SetWidgetRect(rtMonth);
-  m_pMonthCal->Update();
-  m_pForm->DoModal();
+
+  if (bActivate) {
+    CFWL_MessageSetFocus msg(m_pEdit.get(), m_pMonthCal.get());
+    m_pEdit->GetDelegate()->OnProcessMessage(&msg);
+  }
+
+  CFX_RectF rtInvalidate(0, 0, m_pProperties->m_rtWidget.width,
+                         m_pProperties->m_rtWidget.height);
+
+  CFX_RectF rtCal = m_pMonthCal->GetWidgetRect();
+  rtInvalidate.Union(rtCal);
+  rtInvalidate.Inflate(2, 2);
+  RepaintRect(rtInvalidate);
 }
 
 bool CFWL_DateTimePicker::IsMonthCalendarVisible() const {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_IsMonthCalendarVisible();
-  if (!m_pForm)
-    return false;
-  return !(m_pForm->GetStates() & FWL_WGTSTATE_Invisible);
+  return m_pMonthCal && m_pMonthCal->IsVisible();
 }
 
 void CFWL_DateTimePicker::ResetEditAlignment() {
@@ -322,150 +319,12 @@
   DispatchEvent(&ev);
 }
 
-void CFWL_DateTimePicker::InitProxyForm() {
-  if (m_pForm)
-    return;
-  if (!m_pMonthCal)
-    return;
-
-  auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
-  prop->m_dwStyles = FWL_WGTSTYLE_Popup;
-  prop->m_dwStates = FWL_WGTSTATE_Invisible;
-  prop->m_pOwner = this;
-
-  m_pForm = pdfium::MakeUnique<CFWL_FormProxy>(
-      m_pOwnerApp.Get(), std::move(prop), m_pMonthCal.get());
-  m_pMonthCal->SetParent(m_pForm.get());
-}
-
-bool CFWL_DateTimePicker::DisForm_IsMonthCalendarVisible() const {
-  if (!m_pMonthCal)
-    return false;
-  return !(m_pMonthCal->GetStates() & FWL_WGTSTATE_Invisible);
-}
-
-void CFWL_DateTimePicker::DisForm_ShowMonthCalendar(bool bActivate) {
-  if (IsMonthCalendarVisible() == bActivate)
-    return;
-
-  if (bActivate) {
-    CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
-    float fPopupMin = rtMonthCal.height;
-    float fPopupMax = rtMonthCal.height;
-    CFX_RectF rtAnchor(m_pProperties->m_rtWidget);
-    rtAnchor.width = rtMonthCal.width;
-    rtMonthCal.left = m_rtClient.left;
-    rtMonthCal.top = rtAnchor.Height();
-    GetPopupPos(fPopupMin, fPopupMax, rtAnchor, rtMonthCal);
-    m_pMonthCal->SetWidgetRect(rtMonthCal);
-    if (m_iYear > 0 && m_iMonth > 0 && m_iDay > 0)
-      m_pMonthCal->SetSelect(m_iYear, m_iMonth, m_iDay);
-    m_pMonthCal->Update();
-  }
-  if (bActivate)
-    m_pMonthCal->RemoveStates(FWL_WGTSTATE_Invisible);
-  else
-    m_pMonthCal->SetStates(FWL_WGTSTATE_Invisible);
-
-  if (bActivate) {
-    CFWL_MessageSetFocus msg(m_pEdit.get(), m_pMonthCal.get());
-    m_pEdit->GetDelegate()->OnProcessMessage(&msg);
-  }
-
-  CFX_RectF rtInvalidate(0, 0, m_pProperties->m_rtWidget.width,
-                         m_pProperties->m_rtWidget.height);
-
-  CFX_RectF rtCal = m_pMonthCal->GetWidgetRect();
-  rtInvalidate.Union(rtCal);
-  rtInvalidate.Inflate(2, 2);
-  RepaintRect(rtInvalidate);
-}
-
-FWL_WidgetHit CFWL_DateTimePicker::DisForm_HitTest(
-    const CFX_PointF& point) const {
-  CFX_RectF rect(0, 0, m_pProperties->m_rtWidget.width,
-                 m_pProperties->m_rtWidget.height);
-  if (rect.Contains(point))
-    return FWL_WidgetHit::Edit;
-  if (DisForm_IsNeedShowButton())
-    rect.width += m_fBtn;
-  if (rect.Contains(point))
-    return FWL_WidgetHit::Client;
-  if (IsMonthCalendarVisible()) {
-    if (m_pMonthCal->GetWidgetRect().Contains(point))
-      return FWL_WidgetHit::Client;
-  }
-  return FWL_WidgetHit::Unknown;
-}
-
-bool CFWL_DateTimePicker::DisForm_IsNeedShowButton() const {
+bool CFWL_DateTimePicker::NeedsToShowButton() const {
   return m_pProperties->m_dwStates & FWL_WGTSTATE_Focused ||
          m_pMonthCal->GetStates() & FWL_WGTSTATE_Focused ||
          m_pEdit->GetStates() & FWL_WGTSTATE_Focused;
 }
 
-void CFWL_DateTimePicker::DisForm_Update() {
-  if (m_iLock)
-    return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-
-  m_pEdit->SetThemeProvider(m_pProperties->m_pThemeProvider);
-  m_rtClient = GetClientRect();
-  m_pEdit->SetWidgetRect(m_rtClient);
-  ResetEditAlignment();
-  m_pEdit->Update();
-
-  if (!m_pMonthCal->GetThemeProvider())
-    m_pMonthCal->SetThemeProvider(m_pProperties->m_pThemeProvider);
-
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  if (!theme)
-    return;
-
-  m_fBtn = theme->GetScrollBarWidth();
-  CFX_RectF rtMonthCal = m_pMonthCal->GetAutosizedWidgetRect();
-  CFX_RectF rtPopUp(rtMonthCal.left, rtMonthCal.top + kDateTimePickerHeight,
-                    rtMonthCal.width, rtMonthCal.height);
-  m_pMonthCal->SetWidgetRect(rtPopUp);
-  m_pMonthCal->Update();
-}
-
-CFX_RectF CFWL_DateTimePicker::DisForm_GetBBox() const {
-  CFX_RectF rect = m_pProperties->m_rtWidget;
-  if (DisForm_IsNeedShowButton())
-    rect.width += m_fBtn;
-  if (!IsMonthCalendarVisible())
-    return rect;
-
-  CFX_RectF rtMonth = m_pMonthCal->GetWidgetRect();
-  rtMonth.Offset(m_pProperties->m_rtWidget.left, m_pProperties->m_rtWidget.top);
-  rect.Union(rtMonth);
-  return rect;
-}
-
-void CFWL_DateTimePicker::DisForm_DrawWidget(CXFA_Graphics* pGraphics,
-                                             const CFX_Matrix* pMatrix) {
-  if (!pGraphics)
-    return;
-  if (m_pEdit) {
-    CFX_RectF rtEdit = m_pEdit->GetWidgetRect();
-
-    CFX_Matrix mt(1, 0, 0, 1, rtEdit.left, rtEdit.top);
-    if (pMatrix)
-      mt.Concat(*pMatrix);
-    m_pEdit->DrawWidget(pGraphics, mt);
-  }
-  if (!IsMonthCalendarVisible())
-    return;
-
-  CFX_RectF rtMonth = m_pMonthCal->GetWidgetRect();
-  CFX_Matrix mt(1, 0, 0, 1, rtMonth.left, rtMonth.top);
-  if (pMatrix)
-    mt.Concat(*pMatrix);
-  m_pMonthCal->DrawWidget(pGraphics, mt);
-}
-
 void CFWL_DateTimePicker::OnProcessMessage(CFWL_Message* pMessage) {
   if (!pMessage)
     return;
@@ -507,8 +366,9 @@
     default:
       break;
   }
-
-  CFWL_Widget::OnProcessMessage(pMessage);
+  // Dst target could be |this|, continue only if not destroyed by above.
+  if (pMessage->GetDstTarget())
+    CFWL_Widget::OnProcessMessage(pMessage);
 }
 
 void CFWL_DateTimePicker::OnDrawWidget(CXFA_Graphics* pGraphics,
@@ -519,27 +379,34 @@
 void CFWL_DateTimePicker::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
   if (!pMsg)
     return;
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_OnFocusChanged(pMsg, bSet);
 
+  CFX_RectF rtInvalidate(m_rtBtn);
   if (bSet) {
-    m_pProperties->m_dwStates |= (FWL_WGTSTATE_Focused);
-    RepaintRect(m_rtClient);
+    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
+    if (m_pEdit && !(m_pEdit->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)) {
+      m_rtBtn = CFX_RectF(m_pProperties->m_rtWidget.width, 0, m_fBtn,
+                          m_pProperties->m_rtWidget.height - 1);
+    }
+    rtInvalidate = m_rtBtn;
+    pMsg->SetDstTarget(m_pEdit.get());
+    m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
   } else {
-    m_pProperties->m_dwStates &= ~(FWL_WGTSTATE_Focused);
-    RepaintRect(m_rtClient);
+    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
+    m_rtBtn = CFX_RectF();
+    if (IsMonthCalendarVisible())
+      ShowMonthCalendar(false);
+    if (m_pEdit->GetStates() & FWL_WGTSTATE_Focused) {
+      pMsg->SetSrcTarget(m_pEdit.get());
+      m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
+    }
   }
-  if (pMsg->m_pSrcTarget == m_pMonthCal.get() && IsMonthCalendarVisible()) {
-    ShowMonthCalendar(false);
-  }
-  RepaintRect(m_rtClient);
+  rtInvalidate.Inflate(2, 2);
+  RepaintRect(rtInvalidate);
 }
 
 void CFWL_DateTimePicker::OnLButtonDown(CFWL_MessageMouse* pMsg) {
   if (!pMsg)
     return;
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
-    SetFocus(true);
   if (!m_rtBtn.Contains(pMsg->m_pos))
     return;
 
@@ -578,29 +445,50 @@
   RepaintRect(m_rtBtn);
 }
 
-void CFWL_DateTimePicker::DisForm_OnFocusChanged(CFWL_Message* pMsg,
-                                                 bool bSet) {
-  CFX_RectF rtInvalidate(m_rtBtn);
-  if (bSet) {
-    m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
-    if (m_pEdit && !(m_pEdit->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)) {
-      m_rtBtn = CFX_RectF(m_pProperties->m_rtWidget.width, 0, m_fBtn,
-                          m_pProperties->m_rtWidget.height - 1);
-    }
-    rtInvalidate = m_rtBtn;
-    pMsg->m_pDstTarget = m_pEdit.get();
-    m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
-  } else {
-    m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
-    m_rtBtn.Reset();
+void CFWL_DateTimePicker::GetPopupPos(float fMinHeight,
+                                      float fMaxHeight,
+                                      const CFX_RectF& rtAnchor,
+                                      CFX_RectF* pPopupRect) {
+  m_pWidgetMgr->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
+                                   pPopupRect);
+}
 
-    if (DisForm_IsMonthCalendarVisible())
-      ShowMonthCalendar(false);
-    if (m_pEdit->GetStates() & FWL_WGTSTATE_Focused) {
-      pMsg->m_pSrcTarget = m_pEdit.get();
-      m_pEdit->GetDelegate()->OnProcessMessage(pMsg);
-    }
-  }
-  rtInvalidate.Inflate(2, 2);
-  RepaintRect(rtInvalidate);
+void CFWL_DateTimePicker::ClearText() {
+  m_pEdit->ClearText();
+}
+
+void CFWL_DateTimePicker::SelectAll() {
+  m_pEdit->SelectAll();
+}
+
+void CFWL_DateTimePicker::ClearSelection() {
+  m_pEdit->ClearSelection();
+}
+
+Optional<WideString> CFWL_DateTimePicker::Copy() {
+  return m_pEdit->Copy();
+}
+
+Optional<WideString> CFWL_DateTimePicker::Cut() {
+  return m_pEdit->Cut();
+}
+
+bool CFWL_DateTimePicker::Paste(const WideString& wsPaste) {
+  return m_pEdit->Paste(wsPaste);
+}
+
+bool CFWL_DateTimePicker::Undo() {
+  return m_pEdit->Undo();
+}
+
+bool CFWL_DateTimePicker::Redo() {
+  return m_pEdit->Redo();
+}
+
+bool CFWL_DateTimePicker::CanUndo() {
+  return m_pEdit->CanUndo();
+}
+
+bool CFWL_DateTimePicker::CanRedo() {
+  return m_pEdit->CanRedo();
 }
diff --git a/xfa/fwl/cfwl_datetimepicker.h b/xfa/fwl/cfwl_datetimepicker.h
index 6d53601..69520eb 100644
--- a/xfa/fwl/cfwl_datetimepicker.h
+++ b/xfa/fwl/cfwl_datetimepicker.h
@@ -16,22 +16,20 @@
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
 
-#define FWL_STYLEEXT_DTP_LongDateFormat 0
 #define FWL_STYLEEXT_DTP_ShortDateFormat (1L << 1)
-#define FWL_STYLEEXT_DTP_EditHNear 0
+#define FWL_STYLEEXT_DTP_EditHAlignMask (3L << 4)
+#define FWL_STYLEEXT_DTP_EditHNear (0L << 4)
 #define FWL_STYLEEXT_DTP_EditHCenter (1L << 4)
 #define FWL_STYLEEXT_DTP_EditHFar (2L << 4)
-#define FWL_STYLEEXT_DTP_EditVNear 0
+#define FWL_STYLEEXT_DTP_EditVAlignMask (3L << 6)
+#define FWL_STYLEEXT_DTP_EditVNear (0L << 6)
 #define FWL_STYLEEXT_DTP_EditVCenter (1L << 6)
 #define FWL_STYLEEXT_DTP_EditVFar (2L << 6)
 #define FWL_STYLEEXT_DTP_EditJustified (1L << 8)
-#define FWL_STYLEEXT_DTP_EditHAlignMask (3L << 4)
-#define FWL_STYLEEXT_DTP_EditVAlignMask (3L << 6)
 
 class CFWL_DateTimeEdit;
-class CFWL_FormProxy;
 
-class CFWL_DateTimePicker : public CFWL_Widget {
+class CFWL_DateTimePicker final : public CFWL_Widget {
  public:
   explicit CFWL_DateTimePicker(const CFWL_App* pApp);
   ~CFWL_DateTimePicker() override;
@@ -50,13 +48,24 @@
   void SetCurSel(int32_t iYear, int32_t iMonth, int32_t iDay);
 
   void SetEditText(const WideString& wsText);
+  int32_t GetEditTextLength() const;
   WideString GetEditText() const;
+  void ClearText();
 
+  void SelectAll();
+  void ClearSelection();
   bool HasSelection() const { return m_pEdit->HasSelection(); }
   // Returns <start, count> of the selection.
   std::pair<size_t, size_t> GetSelection() const {
     return m_pEdit->GetSelection();
   }
+  Optional<WideString> Copy();
+  Optional<WideString> Cut();
+  bool Paste(const WideString& wsPaste);
+  bool Undo();
+  bool Redo();
+  bool CanUndo();
+  bool CanRedo();
 
   CFX_RectF GetBBox() const;
   void SetEditLimit(int32_t nLimit) { m_pEdit->SetLimit(nLimit); }
@@ -66,41 +75,34 @@
   void ShowMonthCalendar(bool bActivate);
   void ProcessSelChanged(int32_t iYear, int32_t iMonth, int32_t iDay);
 
-  CFWL_FormProxy* GetFormProxy() const { return m_pForm.get(); }
-
  private:
   void DrawDropDownButton(CXFA_Graphics* pGraphics,
                           IFWL_ThemeProvider* pTheme,
                           const CFX_Matrix* pMatrix);
   WideString FormatDateString(int32_t iYear, int32_t iMonth, int32_t iDay);
   void ResetEditAlignment();
-  void InitProxyForm();
+  void GetPopupPos(float fMinHeight,
+                   float fMaxHeight,
+                   const CFX_RectF& rtAnchor,
+                   CFX_RectF* pPopupRect);
   void OnFocusChanged(CFWL_Message* pMsg, bool bSet);
   void OnLButtonDown(CFWL_MessageMouse* pMsg);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
   void OnMouseMove(CFWL_MessageMouse* pMsg);
   void OnMouseLeave(CFWL_MessageMouse* pMsg);
 
-  bool DisForm_IsMonthCalendarVisible() const;
-  void DisForm_ShowMonthCalendar(bool bActivate);
-  FWL_WidgetHit DisForm_HitTest(const CFX_PointF& point) const;
-  bool DisForm_IsNeedShowButton() const;
-  void DisForm_Update();
-  CFX_RectF DisForm_GetBBox() const;
-  void DisForm_DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix* pMatrix);
-  void DisForm_OnFocusChanged(CFWL_Message* pMsg, bool bSet);
+  bool NeedsToShowButton() const;
 
+  bool m_bLBtnDown = false;
+  int32_t m_iBtnState = 1;
+  int32_t m_iYear = -1;
+  int32_t m_iMonth = -1;
+  int32_t m_iDay = -1;
+  float m_fBtn = 0.0f;
   CFX_RectF m_rtBtn;
   CFX_RectF m_rtClient;
-  int32_t m_iBtnState;
-  int32_t m_iYear;
-  int32_t m_iMonth;
-  int32_t m_iDay;
-  bool m_bLBtnDown;
   std::unique_ptr<CFWL_DateTimeEdit> m_pEdit;
   std::unique_ptr<CFWL_MonthCalendar> m_pMonthCal;
-  std::unique_ptr<CFWL_FormProxy> m_pForm;
-  float m_fBtn;
 };
 
 #endif  // XFA_FWL_CFWL_DATETIMEPICKER_H_
diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp
index 960ec99..1f265b5 100644
--- a/xfa/fwl/cfwl_edit.cpp
+++ b/xfa/fwl/cfwl_edit.cpp
@@ -11,79 +11,50 @@
 #include <utility>
 #include <vector>
 
+#include "build/build_config.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/text_char_pos.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
-#include "xfa/fde/cfde_texteditengine.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_caret.h"
 #include "xfa/fwl/cfwl_event.h"
-#include "xfa/fwl/cfwl_eventcheckword.h"
-#include "xfa/fwl/cfwl_eventtextchanged.h"
+#include "xfa/fwl/cfwl_eventtextwillchange.h"
 #include "xfa/fwl/cfwl_eventvalidate.h"
 #include "xfa/fwl/cfwl_messagekey.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 #include "xfa/fwl/theme/cfwl_utils.h"
-#include "xfa/fxfa/cxfa_ffdoc.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
 namespace {
 
-const int kEditMargin = 3;
+constexpr int kEditMargin = 3;
 
-#if (_FX_OS_ == _FX_OS_MACOSX_)
+#if defined(OS_MACOSX)
 constexpr int kEditingModifier = FWL_KEYFLAG_Command;
 #else
 constexpr int kEditingModifier = FWL_KEYFLAG_Ctrl;
 #endif
 
-bool FxEditIsLatinWord(wchar_t c) {
-  return c == 0x2D || (c <= 0x005A && c >= 0x0041) ||
-         (c <= 0x007A && c >= 0x0061) || (c <= 0x02AF && c >= 0x00C0) ||
-         c == 0x0027;
-}
-
-void AddSquigglyPath(CXFA_GEPath* pPathData,
-                     float fStartX,
-                     float fEndX,
-                     float fY,
-                     float fStep) {
-  pPathData->MoveTo(CFX_PointF(fStartX, fY));
-  int i = 1;
-  for (float fx = fStartX + fStep; fx < fEndX; fx += fStep, ++i)
-    pPathData->LineTo(CFX_PointF(fx, fY + (i & 1) * fStep));
-}
-
 }  // namespace
 
 CFWL_Edit::CFWL_Edit(const CFWL_App* app,
                      std::unique_ptr<CFWL_WidgetProperties> properties,
                      CFWL_Widget* pOuter)
     : CFWL_Widget(app, std::move(properties), pOuter),
-      m_fVAlignOffset(0.0f),
-      m_fScrollOffsetX(0.0f),
-      m_fScrollOffsetY(0.0f),
-      m_bLButtonDown(false),
-      m_CursorPosition(0),
-      m_nLimit(-1),
-      m_fFontSize(0),
-      m_bSetRange(false),
-      m_iMax(0xFFFFFFF) {
-  m_rtClient.Reset();
-  m_rtEngine.Reset();
-  m_rtStatic.Reset();
-
-  InitCaret();
-  m_EdtEngine.SetDelegate(this);
+      m_pEditEngine(pdfium::MakeUnique<CFDE_TextEditEngine>()) {
+  m_pEditEngine->SetDelegate(this);
 }
 
 CFWL_Edit::~CFWL_Edit() {
+  m_pEditEngine->SetDelegate(nullptr);
   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
     HideCaret(nullptr);
 }
@@ -112,9 +83,9 @@
 CFX_RectF CFWL_Edit::GetAutosizedWidgetRect() {
   CFX_RectF rect;
 
-  if (m_EdtEngine.GetLength() > 0) {
+  if (m_pEditEngine->GetLength() > 0) {
     CFX_SizeF size = CalcTextSize(
-        m_EdtEngine.GetText(), m_pProperties->m_pThemeProvider,
+        m_pEditEngine->GetText(), m_pProperties->m_pThemeProvider.Get(),
         !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine));
     rect = CFX_RectF(0, 0, size);
   }
@@ -163,97 +134,18 @@
   return FWL_WidgetHit::Unknown;
 }
 
-void CFWL_Edit::AddSpellCheckObj(CXFA_GEPath& PathData,
-                                 int32_t nStart,
-                                 int32_t nCount,
-                                 float fOffSetX,
-                                 float fOffSetY) {
-  float fStep = m_EdtEngine.GetFontSize() / 16.0f;
-  float font_ascent = m_EdtEngine.GetFontAscent();
-
-  std::vector<CFX_RectF> rects =
-      m_EdtEngine.GetCharacterRectsInRange(nStart, nCount);
-  for (const auto& rect : rects) {
-    float fY = rect.top + font_ascent + fOffSetY;
-    float fStartX = rect.left + fOffSetX;
-    float fEndX = fStartX + rect.Width();
-
-    AddSquigglyPath(&PathData, fStartX, fEndX, fY, fStep);
-  }
-}
-
-void CFWL_Edit::DrawSpellCheck(CXFA_Graphics* pGraphics,
-                               const CFX_Matrix* pMatrix) {
-  pGraphics->SaveGraphState();
-  if (pMatrix)
-    pGraphics->ConcatMatrix(pMatrix);
-
-  CFWL_EventCheckWord checkWordEvent(this);
-  ByteString sLatinWord;
-  CXFA_GEPath pathSpell;
-  int32_t nStart = 0;
-  float fOffSetX = m_rtEngine.left - m_fScrollOffsetX;
-  float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset;
-  WideString wsSpell = GetText();
-  int32_t nContentLen = wsSpell.GetLength();
-  for (int i = 0; i < nContentLen; i++) {
-    if (FxEditIsLatinWord(wsSpell[i])) {
-      if (sLatinWord.IsEmpty())
-        nStart = i;
-      sLatinWord += (char)wsSpell[i];
-      continue;
-    }
-    checkWordEvent.bsWord = sLatinWord;
-    checkWordEvent.bCheckWord = true;
-    DispatchEvent(&checkWordEvent);
-
-    if (!sLatinWord.IsEmpty() && !checkWordEvent.bCheckWord) {
-      AddSpellCheckObj(pathSpell, nStart, sLatinWord.GetLength(), fOffSetX,
-                       fOffSetY);
-    }
-    sLatinWord.clear();
-  }
-
-  checkWordEvent.bsWord = sLatinWord;
-  checkWordEvent.bCheckWord = true;
-  DispatchEvent(&checkWordEvent);
-
-  if (!sLatinWord.IsEmpty() && !checkWordEvent.bCheckWord) {
-    AddSpellCheckObj(pathSpell, nStart, sLatinWord.GetLength(), fOffSetX,
-                     fOffSetY);
-  }
-  if (!pathSpell.IsEmpty()) {
-    CFX_RectF rtClip = m_rtEngine;
-    CFX_Matrix mt(1, 0, 0, 1, fOffSetX, fOffSetY);
-    if (pMatrix) {
-      rtClip = pMatrix->TransformRect(rtClip);
-      mt.Concat(*pMatrix);
-    }
-    pGraphics->SetClipRect(rtClip);
-    pGraphics->SetStrokeColor(CXFA_GEColor(0xFFFF0000));
-    pGraphics->SetLineWidth(0);
-    pGraphics->StrokePath(&pathSpell, nullptr);
-  }
-  pGraphics->RestoreGraphState();
-}
-
 void CFWL_Edit::DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
-    return;
+
   if (m_rtClient.IsEmpty())
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
-  if (!m_pWidgetMgr->IsFormDisabled())
-    DrawTextBk(pGraphics, pTheme, &matrix);
-  DrawContent(pGraphics, pTheme, &matrix);
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
+  if (!pTheme)
+    return;
 
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) &&
-      !(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly)) {
-    DrawSpellCheck(pGraphics, &matrix);
-  }
+  DrawContent(pGraphics, pTheme, &matrix);
   if (HasBorder())
     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
 }
@@ -271,36 +163,43 @@
 }
 
 void CFWL_Edit::SetText(const WideString& wsText) {
-  m_EdtEngine.Clear();
-  m_EdtEngine.Insert(0, wsText);
+  m_pEditEngine->Clear();
+  m_pEditEngine->Insert(0, wsText,
+                        CFDE_TextEditEngine::RecordOperation::kInsertRecord);
+}
+
+void CFWL_Edit::SetTextSkipNotify(const WideString& wsText) {
+  m_pEditEngine->Clear();
+  m_pEditEngine->Insert(0, wsText,
+                        CFDE_TextEditEngine::RecordOperation::kSkipNotify);
 }
 
 int32_t CFWL_Edit::GetTextLength() const {
-  return m_EdtEngine.GetLength();
+  return m_pEditEngine->GetLength();
 }
 
 WideString CFWL_Edit::GetText() const {
-  return m_EdtEngine.GetText();
+  return m_pEditEngine->GetText();
 }
 
 void CFWL_Edit::ClearText() {
-  m_EdtEngine.Clear();
+  m_pEditEngine->Clear();
 }
 
 void CFWL_Edit::SelectAll() {
-  m_EdtEngine.SelectAll();
+  m_pEditEngine->SelectAll();
 }
 
 bool CFWL_Edit::HasSelection() const {
-  return m_EdtEngine.HasSelection();
+  return m_pEditEngine->HasSelection();
 }
 
 std::pair<size_t, size_t> CFWL_Edit::GetSelection() const {
-  return m_EdtEngine.GetSelection();
+  return m_pEditEngine->GetSelection();
 }
 
 void CFWL_Edit::ClearSelection() {
-  return m_EdtEngine.ClearSelection();
+  return m_pEditEngine->ClearSelection();
 }
 
 int32_t CFWL_Edit::GetLimit() const {
@@ -311,54 +210,56 @@
   m_nLimit = nLimit;
 
   if (m_nLimit > 0) {
-    m_EdtEngine.SetHasCharacterLimit(true);
-    m_EdtEngine.SetCharacterLimit(nLimit);
+    m_pEditEngine->SetHasCharacterLimit(true);
+    m_pEditEngine->SetCharacterLimit(nLimit);
   } else {
-    m_EdtEngine.SetHasCharacterLimit(false);
+    m_pEditEngine->SetHasCharacterLimit(false);
   }
 }
 
 void CFWL_Edit::SetAliasChar(wchar_t wAlias) {
-  m_EdtEngine.SetAliasChar(wAlias);
+  m_pEditEngine->SetAliasChar(wAlias);
 }
 
 Optional<WideString> CFWL_Edit::Copy() {
-  if (!m_EdtEngine.HasSelection())
+  if (!m_pEditEngine->HasSelection())
     return {};
 
-  return {m_EdtEngine.GetSelectedText()};
+  return {m_pEditEngine->GetSelectedText()};
 }
 
 Optional<WideString> CFWL_Edit::Cut() {
-  if (!m_EdtEngine.HasSelection())
+  if (!m_pEditEngine->HasSelection())
     return {};
 
-  return {m_EdtEngine.DeleteSelectedText()};
+  WideString cut_text = m_pEditEngine->DeleteSelectedText();
+  UpdateCaret();
+  return {cut_text};
 }
 
 bool CFWL_Edit::Paste(const WideString& wsPaste) {
-  if (m_EdtEngine.HasSelection())
-    m_EdtEngine.ReplaceSelectedText(wsPaste);
+  if (m_pEditEngine->HasSelection())
+    m_pEditEngine->ReplaceSelectedText(wsPaste);
   else
-    m_EdtEngine.Insert(m_CursorPosition, wsPaste);
+    m_pEditEngine->Insert(m_CursorPosition, wsPaste);
 
   return true;
 }
 
 bool CFWL_Edit::Undo() {
-  return CanUndo() ? m_EdtEngine.Undo() : false;
+  return CanUndo() && m_pEditEngine->Undo();
 }
 
 bool CFWL_Edit::Redo() {
-  return CanRedo() ? m_EdtEngine.Redo() : false;
+  return CanRedo() && m_pEditEngine->Redo();
 }
 
 bool CFWL_Edit::CanUndo() {
-  return m_EdtEngine.CanUndo();
+  return m_pEditEngine->CanUndo();
 }
 
 bool CFWL_Edit::CanRedo() {
-  return m_EdtEngine.CanRedo();
+  return m_pEditEngine->CanRedo();
 }
 
 void CFWL_Edit::SetOuter(CFWL_Widget* pOuter) {
@@ -394,14 +295,26 @@
   }
 }
 
-void CFWL_Edit::OnTextChanged(const WideString& prevText) {
+void CFWL_Edit::OnTextWillChange(CFDE_TextEditEngine::TextChange* change) {
+  CFWL_EventTextWillChange event(this);
+  event.previous_text = change->previous_text;
+  event.change_text = change->text;
+  event.selection_start = change->selection_start;
+  event.selection_end = change->selection_end;
+  event.cancelled = false;
+
+  DispatchEvent(&event);
+
+  change->text = event.change_text;
+  change->selection_start = event.selection_start;
+  change->selection_end = event.selection_end;
+  change->cancelled = event.cancelled;
+}
+
+void CFWL_Edit::OnTextChanged() {
   if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VAlignMask)
     UpdateVAlignment();
 
-  CFWL_EventTextChanged event(this);
-  event.wsPrevText = prevText;
-  DispatchEvent(&event);
-
   LayoutScrollBar();
   RepaintRect(GetClientRect());
 }
@@ -411,10 +324,6 @@
 }
 
 bool CFWL_Edit::OnValidate(const WideString& wsText) {
-  CFWL_Widget* pDst = GetOuter();
-  if (!pDst)
-    pDst = this;
-
   CFWL_EventValidate event(this);
   event.wsInsert = wsText;
   event.bValidate = true;
@@ -442,7 +351,7 @@
   param.m_pGraphics = pGraphics;
   param.m_matrix = *pMatrix;
   param.m_rtPart = m_rtClient;
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 
   if (!IsShowScrollBar(true) || !IsShowScrollBar(false))
     return;
@@ -455,7 +364,7 @@
   param.m_bStaticBackground = true;
   param.m_bMaximize = true;
   param.m_rtPart = rtStatic;
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 void CFWL_Edit::DrawContent(CXFA_Graphics* pGraphics,
@@ -477,12 +386,12 @@
   }
 
   bool bShowSel = !!(m_pProperties->m_dwStates & FWL_WGTSTATE_Focused);
-  if (bShowSel && m_EdtEngine.HasSelection()) {
+  if (bShowSel && m_pEditEngine->HasSelection()) {
     size_t sel_start;
     size_t count;
-    std::tie(sel_start, count) = m_EdtEngine.GetSelection();
+    std::tie(sel_start, count) = m_pEditEngine->GetSelection();
     std::vector<CFX_RectF> rects =
-        m_EdtEngine.GetCharacterRectsInRange(sel_start, count);
+        m_pEditEngine->GetCharacterRectsInRange(sel_start, count);
 
     CXFA_GEPath path;
     for (auto& rect : rects) {
@@ -498,13 +407,10 @@
     param.m_pWidget = this;
     param.m_iPart = CFWL_Part::Background;
     param.m_pPath = &path;
-    pTheme->DrawBackground(&param);
+    pTheme->DrawBackground(param);
   }
 
   CFX_RenderDevice* pRenderDev = pGraphics->GetRenderDevice();
-  if (!pRenderDev)
-    return;
-
   RenderText(pRenderDev, rtClip, mt);
 
   if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText) {
@@ -526,7 +432,7 @@
     param.m_pWidget = this;
     param.m_iPart = CFWL_Part::CombTextLine;
     param.m_pPath = &path;
-    pTheme->DrawBackground(&param);
+    pTheme->DrawBackground(param);
   }
   pGraphics->RestoreGraphState();
 }
@@ -536,11 +442,11 @@
                            const CFX_Matrix& mt) {
   ASSERT(pRenderDev);
 
-  RetainPtr<CFGAS_GEFont> font = m_EdtEngine.GetFont();
+  RetainPtr<CFGAS_GEFont> font = m_pEditEngine->GetFont();
   if (!font)
     return;
 
-  pRenderDev->SetClip_Rect(clipRect);
+  pRenderDev->SetClip_Rect(clipRect.GetOuterRect());
 
   CFX_RectF rtDocClip = clipRect;
   if (rtDocClip.IsEmpty()) {
@@ -551,18 +457,17 @@
   }
   rtDocClip = mt.GetInverse().TransformRect(rtDocClip);
 
-  for (const FDE_TEXTEDITPIECE& info : m_EdtEngine.GetTextPieces()) {
+  for (const FDE_TEXTEDITPIECE& info : m_pEditEngine->GetTextPieces()) {
     // If this character is outside the clip, skip it.
     if (!rtDocClip.IntersectWith(info.rtPiece))
       continue;
 
-    std::vector<FXTEXT_CHARPOS> char_pos = m_EdtEngine.GetDisplayPos(info);
+    std::vector<TextCharPos> char_pos = m_pEditEngine->GetDisplayPos(info);
     if (char_pos.empty())
       continue;
 
-    CFDE_TextOut::DrawString(pRenderDev, m_EdtEngine.GetFontColor(), font,
-                             char_pos.data(), char_pos.size(),
-                             m_EdtEngine.GetFontSize(), &mt);
+    CFDE_TextOut::DrawString(pRenderDev, m_pEditEngine->GetFontColor(), font,
+                             char_pos, m_pEditEngine->GetFontSize(), mt);
   }
 }
 
@@ -572,13 +477,13 @@
 }
 
 void CFWL_Edit::UpdateEditParams() {
-  m_EdtEngine.SetAvailableWidth(m_rtEngine.width);
-  m_EdtEngine.SetCombText(
+  m_pEditEngine->SetAvailableWidth(m_rtEngine.width);
+  m_pEditEngine->SetCombText(
       !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText));
 
-  m_EdtEngine.EnableValidation(
+  m_pEditEngine->EnableValidation(
       !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Validate));
-  m_EdtEngine.EnablePasswordMode(
+  m_pEditEngine->EnablePasswordMode(
       !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Password));
 
   uint32_t alignment = 0;
@@ -606,22 +511,22 @@
     default:
       break;
   }
-  m_EdtEngine.SetAlignment(alignment);
+  m_pEditEngine->SetAlignment(alignment);
 
   bool auto_hscroll =
       !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoHScroll);
   if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) {
-    m_EdtEngine.EnableMultiLine(true);
-    m_EdtEngine.EnableLineWrap(!auto_hscroll);
-    m_EdtEngine.LimitVerticalScroll(
+    m_pEditEngine->EnableMultiLine(true);
+    m_pEditEngine->EnableLineWrap(!auto_hscroll);
+    m_pEditEngine->LimitVerticalScroll(
         (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll) == 0 &&
         (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoVScroll) == 0);
   } else {
-    m_EdtEngine.EnableMultiLine(false);
-    m_EdtEngine.EnableLineWrap(false);
-    m_EdtEngine.LimitVerticalScroll(false);
+    m_pEditEngine->EnableMultiLine(false);
+    m_pEditEngine->EnableLineWrap(false);
+    m_pEditEngine->LimitVerticalScroll(false);
   }
-  m_EdtEngine.LimitHorizontalScroll(!auto_hscroll);
+  m_pEditEngine->LimitHorizontalScroll(!auto_hscroll);
 
   IFWL_ThemeProvider* theme = GetAvailableTheme();
   CFWL_ThemePart part;
@@ -631,23 +536,23 @@
     m_fFontSize = FWLTHEME_CAPACITY_FontSize;
     return;
   }
-  m_fFontSize = theme->GetFontSize(&part);
+  m_fFontSize = theme->GetFontSize(part);
 
-  RetainPtr<CFGAS_GEFont> pFont = theme->GetFont(&part);
+  RetainPtr<CFGAS_GEFont> pFont = theme->GetFont(part);
   if (!pFont)
     return;
 
-  m_EdtEngine.SetFont(pFont);
-  m_EdtEngine.SetFontColor(theme->GetTextColor(&part));
-  m_EdtEngine.SetFontSize(m_fFontSize);
-  m_EdtEngine.SetLineSpace(theme->GetLineHeight(&part));
-  m_EdtEngine.SetTabWidth(m_fFontSize);
-  m_EdtEngine.SetVisibleLineCount(m_rtEngine.height /
-                                  theme->GetLineHeight(&part));
+  m_pEditEngine->SetFont(pFont);
+  m_pEditEngine->SetFontColor(theme->GetTextColor(part));
+  m_pEditEngine->SetFontSize(m_fFontSize);
+  m_pEditEngine->SetLineSpace(theme->GetLineHeight(part));
+  m_pEditEngine->SetTabWidth(m_fFontSize);
+  m_pEditEngine->SetVisibleLineCount(m_rtEngine.height /
+                                     theme->GetLineHeight(part));
 }
 
 void CFWL_Edit::UpdateEditLayout() {
-  m_EdtEngine.Layout();
+  m_pEditEngine->Layout();
 }
 
 bool CFWL_Edit::UpdateOffset() {
@@ -659,7 +564,7 @@
 
   const CFX_RectF& edit_bounds = m_rtEngine;
   if (edit_bounds.Contains(rtCaret)) {
-    CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox();
+    CFX_RectF contents_bounds = m_pEditEngine->GetContentsBoundingBox();
     contents_bounds.Offset(fOffSetX, fOffSetY);
     if (contents_bounds.right() < edit_bounds.right() && m_fScrollOffsetX > 0) {
       m_fScrollOffsetX += contents_bounds.right() - edit_bounds.right();
@@ -708,13 +613,13 @@
     CFWL_ThemePart part;
     part.m_pWidget = this;
 
-    CFX_SizeF pSpace = theme->GetSpaceAboveBelow(&part);
+    CFX_SizeF pSpace = theme->GetSpaceAboveBelow(part);
     fSpaceAbove = pSpace.width >= 0.1f ? pSpace.width : 0.0f;
     fSpaceBelow = pSpace.height >= 0.1f ? pSpace.height : 0.0f;
   }
 
   float fOffsetY = 0.0f;
-  CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox();
+  CFX_RectF contents_bounds = m_pEditEngine->GetContentsBoundingBox();
   if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VCenter) {
     fOffsetY = (m_rtEngine.height - contents_bounds.height) / 2.0f;
     if (fOffsetY < (fSpaceAbove + fSpaceBelow) / 2.0f &&
@@ -751,38 +656,36 @@
 }
 
 CFWL_ScrollBar* CFWL_Edit::UpdateScroll() {
-  bool bShowHorz =
-      m_pHorzScrollBar &&
-      ((m_pHorzScrollBar->GetStates() & FWL_WGTSTATE_Invisible) == 0);
-  bool bShowVert =
-      m_pVertScrollBar &&
-      ((m_pVertScrollBar->GetStates() & FWL_WGTSTATE_Invisible) == 0);
+  bool bShowHorz = m_pHorzScrollBar && m_pHorzScrollBar->IsVisible();
+  bool bShowVert = m_pVertScrollBar && m_pVertScrollBar->IsVisible();
   if (!bShowHorz && !bShowVert)
     return nullptr;
 
-  CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox();
+  CFX_RectF contents_bounds = m_pEditEngine->GetContentsBoundingBox();
   CFWL_ScrollBar* pRepaint = nullptr;
   if (bShowHorz) {
     CFX_RectF rtScroll = m_pHorzScrollBar->GetWidgetRect();
     if (rtScroll.width < contents_bounds.width) {
-      m_pHorzScrollBar->LockUpdate();
-      float fRange = contents_bounds.width - rtScroll.width;
-      m_pHorzScrollBar->SetRange(0.0f, fRange);
+      {
+        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
+        float fRange = contents_bounds.width - rtScroll.width;
+        m_pHorzScrollBar->SetRange(0.0f, fRange);
 
-      float fPos = pdfium::clamp(m_fScrollOffsetX, 0.0f, fRange);
-      m_pHorzScrollBar->SetPos(fPos);
-      m_pHorzScrollBar->SetTrackPos(fPos);
-      m_pHorzScrollBar->SetPageSize(rtScroll.width);
-      m_pHorzScrollBar->SetStepSize(rtScroll.width / 10);
-      m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Disabled);
-      m_pHorzScrollBar->UnlockUpdate();
+        float fPos = pdfium::clamp(m_fScrollOffsetX, 0.0f, fRange);
+        m_pHorzScrollBar->SetPos(fPos);
+        m_pHorzScrollBar->SetTrackPos(fPos);
+        m_pHorzScrollBar->SetPageSize(rtScroll.width);
+        m_pHorzScrollBar->SetStepSize(rtScroll.width / 10);
+        m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Disabled);
+      }
       m_pHorzScrollBar->Update();
       pRepaint = m_pHorzScrollBar.get();
     } else if ((m_pHorzScrollBar->GetStates() & FWL_WGTSTATE_Disabled) == 0) {
-      m_pHorzScrollBar->LockUpdate();
-      m_pHorzScrollBar->SetRange(0, -1);
-      m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Disabled);
-      m_pHorzScrollBar->UnlockUpdate();
+      {
+        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
+        m_pHorzScrollBar->SetRange(0, -1);
+        m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Disabled);
+      }
       m_pHorzScrollBar->Update();
       pRepaint = m_pHorzScrollBar.get();
     }
@@ -791,26 +694,28 @@
   if (bShowVert) {
     CFX_RectF rtScroll = m_pVertScrollBar->GetWidgetRect();
     if (rtScroll.height < contents_bounds.height) {
-      m_pVertScrollBar->LockUpdate();
-      float fStep = m_EdtEngine.GetLineSpace();
-      float fRange =
-          std::max(contents_bounds.height - m_rtEngine.height, fStep);
+      {
+        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
+        float fStep = m_pEditEngine->GetLineSpace();
+        float fRange =
+            std::max(contents_bounds.height - m_rtEngine.height, fStep);
 
-      m_pVertScrollBar->SetRange(0.0f, fRange);
-      float fPos = pdfium::clamp(m_fScrollOffsetY, 0.0f, fRange);
-      m_pVertScrollBar->SetPos(fPos);
-      m_pVertScrollBar->SetTrackPos(fPos);
-      m_pVertScrollBar->SetPageSize(rtScroll.height);
-      m_pVertScrollBar->SetStepSize(fStep);
-      m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Disabled);
-      m_pVertScrollBar->UnlockUpdate();
+        m_pVertScrollBar->SetRange(0.0f, fRange);
+        float fPos = pdfium::clamp(m_fScrollOffsetY, 0.0f, fRange);
+        m_pVertScrollBar->SetPos(fPos);
+        m_pVertScrollBar->SetTrackPos(fPos);
+        m_pVertScrollBar->SetPageSize(rtScroll.height);
+        m_pVertScrollBar->SetStepSize(fStep);
+        m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Disabled);
+      }
       m_pVertScrollBar->Update();
       pRepaint = m_pVertScrollBar.get();
     } else if ((m_pVertScrollBar->GetStates() & FWL_WGTSTATE_Disabled) == 0) {
-      m_pVertScrollBar->LockUpdate();
-      m_pVertScrollBar->SetRange(0, -1);
-      m_pVertScrollBar->SetStates(FWL_WGTSTATE_Disabled);
-      m_pVertScrollBar->UnlockUpdate();
+      {
+        ScopedUpdateLock update_lock(m_pHorzScrollBar.get());
+        m_pVertScrollBar->SetRange(0, -1);
+        m_pVertScrollBar->SetStates(FWL_WGTSTATE_Disabled);
+      }
       m_pVertScrollBar->Update();
       pRepaint = m_pVertScrollBar.get();
     }
@@ -832,7 +737,8 @@
 }
 
 bool CFWL_Edit::IsContentHeightOverflow() {
-  return m_EdtEngine.GetContentsBoundingBox().height > m_rtEngine.height + 1.0f;
+  return m_pEditEngine->GetContentsBoundingBox().height >
+         m_rtEngine.height + 1.0f;
 }
 
 void CFWL_Edit::Layout() {
@@ -846,12 +752,12 @@
   CFWL_ThemePart part;
   if (!m_pOuter) {
     part.m_pWidget = this;
-    CFX_RectF pUIMargin = theme->GetUIMargin(&part);
+    CFX_RectF pUIMargin = theme->GetUIMargin(part);
     m_rtEngine.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
                        pUIMargin.height);
   } else if (m_pOuter->GetClassID() == FWL_Type::DateTimePicker) {
     part.m_pWidget = m_pOuter;
-    CFX_RectF pUIMargin = theme->GetUIMargin(&part);
+    CFX_RectF pUIMargin = theme->GetUIMargin(part);
     m_rtEngine.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
                        pUIMargin.height);
   }
@@ -1008,17 +914,12 @@
     pRect->Offset(rtOuter.left, rtOuter.top);
   }
 
-  CXFA_FFWidget* pXFAWidget = pOuter->GetLayoutItem();
+  CFWL_Widget::AdapterIface* pXFAWidget = pOuter->GetAdapterIface();
   if (!pXFAWidget)
     return;
 
-  IXFA_DocEnvironment* pDocEnvironment =
-      pXFAWidget->GetDoc()->GetDocEnvironment();
-  if (!pDocEnvironment)
-    return;
-
   CFX_RectF rt = pXFAWidget->GetRotateMatrix().TransformRect(*pRect);
-  pDocEnvironment->DisplayCaret(pXFAWidget, true, &rt);
+  pXFAWidget->DisplayCaret(true, &rt);
 }
 
 void CFWL_Edit::HideCaret(CFX_RectF* pRect) {
@@ -1032,23 +933,18 @@
   while (pOuter->GetOuter())
     pOuter = pOuter->GetOuter();
 
-  CXFA_FFWidget* pXFAWidget = pOuter->GetLayoutItem();
+  CFWL_Widget::AdapterIface* pXFAWidget = pOuter->GetAdapterIface();
   if (!pXFAWidget)
     return;
 
-  IXFA_DocEnvironment* pDocEnvironment =
-      pXFAWidget->GetDoc()->GetDocEnvironment();
-  if (!pDocEnvironment)
-    return;
-
-  pDocEnvironment->DisplayCaret(pXFAWidget, false, pRect);
+  pXFAWidget->DisplayCaret(false, pRect);
 }
 
 bool CFWL_Edit::ValidateNumberChar(wchar_t cNum) {
   if (!m_bSetRange)
     return true;
 
-  WideString wsText = m_EdtEngine.GetText();
+  WideString wsText = m_pEditEngine->GetText();
   if (wsText.IsEmpty())
     return cNum != L'0';
 
@@ -1058,35 +954,48 @@
     return false;
 
   int32_t nLen = wsText.GetLength();
-  WideString l = wsText.Left(m_CursorPosition);
-  WideString r = wsText.Right(nLen - m_CursorPosition);
-  WideString wsNew = l + cNum + r;
+  WideString first = wsText.First(m_CursorPosition);
+  WideString last = wsText.Last(nLen - m_CursorPosition);
+  WideString wsNew = first + cNum + last;
   return wsNew.GetInteger() <= m_iMax;
 }
 
 void CFWL_Edit::InitCaret() {
-  m_pCaret.reset();
-  m_rtCaret = CFX_RectF();
+  if (m_pCaret)
+    return;
+
+  m_pCaret = pdfium::MakeUnique<CFWL_Caret>(
+      m_pOwnerApp.Get(), pdfium::MakeUnique<CFWL_WidgetProperties>(), this);
+  m_pCaret->SetParent(this);
+  m_pCaret->SetStates(m_pProperties->m_dwStates);
+  UpdateCursorRect();
 }
 
 void CFWL_Edit::UpdateCursorRect() {
-  int32_t bidi_level = 0;
-  m_rtCaret = CFX_RectF();
-  std::tie(bidi_level, m_rtCaret) =
-      m_EdtEngine.GetCharacterInfo(m_CursorPosition);
+  int32_t bidi_level;
+  if (m_pEditEngine->GetLength() > 0) {
+    std::tie(bidi_level, m_rtCaret) =
+        m_pEditEngine->GetCharacterInfo(m_CursorPosition);
+  } else {
+    bidi_level = 0;
+    m_rtCaret = CFX_RectF();
+  }
+
   // TODO(dsinclair): This should handle bidi level  ...
 
-  if (m_rtCaret.width == 0 && m_rtCaret.left > 1.0f)
-    m_rtCaret.left -= 1.0f;
-
   m_rtCaret.width = 1.0f;
+
+  // TODO(hnakashima): Handle correctly edits with empty text instead of using
+  // these defaults.
+  if (m_rtCaret.height == 0)
+    m_rtCaret.height = 8.0f;
 }
 
 void CFWL_Edit::SetCursorPosition(size_t position) {
   if (m_CursorPosition == position)
     return;
 
-  m_CursorPosition = position;
+  m_CursorPosition = std::min(position, m_pEditEngine->GetLength());
   UpdateCursorRect();
   OnCaretChanged();
 }
@@ -1136,14 +1045,16 @@
     default:
       break;
   }
-  CFWL_Widget::OnProcessMessage(pMessage);
+  // Dst target could be |this|, continue only if not destroyed by above.
+  if (pMessage->GetDstTarget())
+    CFWL_Widget::OnProcessMessage(pMessage);
 }
 
 void CFWL_Edit::OnProcessEvent(CFWL_Event* pEvent) {
   if (!pEvent || pEvent->GetType() != CFWL_Event::Type::Scroll)
     return;
 
-  CFWL_Widget* pSrcTarget = pEvent->m_pSrcTarget;
+  CFWL_Widget* pSrcTarget = pEvent->GetSrcTarget();
   if ((pSrcTarget == m_pVertScrollBar.get() && m_pVertScrollBar) ||
       (pSrcTarget == m_pHorzScrollBar.get() && m_pHorzScrollBar)) {
     CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
@@ -1158,10 +1069,8 @@
 }
 
 void CFWL_Edit::DoRButtonDown(CFWL_MessageMouse* pMsg) {
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
-    SetFocus(true);
-
-  m_CursorPosition = m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos));
+  SetCursorPosition(
+      m_pEditEngine->GetIndexForPoint(DeviceToEngine(pMsg->m_pos)));
 }
 
 void CFWL_Edit::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
@@ -1199,27 +1108,24 @@
   m_bLButtonDown = true;
   SetGrab(true);
 
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
-    SetFocus(true);
-
   bool bRepaint = false;
-  if (m_EdtEngine.HasSelection()) {
-    m_EdtEngine.ClearSelection();
+  if (m_pEditEngine->HasSelection()) {
+    m_pEditEngine->ClearSelection();
     bRepaint = true;
   }
 
   size_t index_at_click =
-      m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos));
+      m_pEditEngine->GetIndexForPoint(DeviceToEngine(pMsg->m_pos));
 
   if (index_at_click != m_CursorPosition &&
       !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift)) {
     size_t start = std::min(m_CursorPosition, index_at_click);
     size_t end = std::max(m_CursorPosition, index_at_click);
 
-    m_EdtEngine.SetSelection(start, end - start);
+    m_pEditEngine->SetSelection(start, end - start);
     bRepaint = true;
   } else {
-    m_CursorPosition = index_at_click;
+    SetCursorPosition(index_at_click);
   }
 
   if (bRepaint)
@@ -1232,12 +1138,13 @@
 }
 
 void CFWL_Edit::OnButtonDoubleClick(CFWL_MessageMouse* pMsg) {
-  size_t click_idx = m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos));
+  size_t click_idx =
+      m_pEditEngine->GetIndexForPoint(DeviceToEngine(pMsg->m_pos));
   size_t start_idx;
   size_t count;
-  std::tie(start_idx, count) = m_EdtEngine.BoundsForWordAt(click_idx);
+  std::tie(start_idx, count) = m_pEditEngine->BoundsForWordAt(click_idx);
 
-  m_EdtEngine.SetSelection(start_idx, count);
+  m_pEditEngine->SetSelection(start_idx, count);
   m_CursorPosition = start_idx + count;
   RepaintRect(m_rtEngine);
 }
@@ -1248,24 +1155,25 @@
     return;
 
   size_t old_cursor_pos = m_CursorPosition;
-  SetCursorPosition(m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos)));
+  SetCursorPosition(
+      m_pEditEngine->GetIndexForPoint(DeviceToEngine(pMsg->m_pos)));
   if (old_cursor_pos == m_CursorPosition)
     return;
 
-  size_t length = m_EdtEngine.GetLength();
+  size_t length = m_pEditEngine->GetLength();
   if (m_CursorPosition > length)
     SetCursorPosition(length);
 
   size_t sel_start = 0;
   size_t count = 0;
-  if (m_EdtEngine.HasSelection())
-    std::tie(sel_start, count) = m_EdtEngine.GetSelection();
+  if (m_pEditEngine->HasSelection())
+    std::tie(sel_start, count) = m_pEditEngine->GetSelection();
   else
     sel_start = old_cursor_pos;
 
   size_t start_pos = std::min(sel_start, m_CursorPosition);
   size_t end_pos = std::max(sel_start, m_CursorPosition);
-  m_EdtEngine.SetSelection(start_pos, end_pos - start_pos);
+  m_pEditEngine->SetSelection(start_pos, end_pos - start_pos);
 }
 
 void CFWL_Edit::OnKeyDown(CFWL_MessageKey* pMsg) {
@@ -1273,58 +1181,56 @@
   bool bCtrl = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl);
 
   size_t sel_start = m_CursorPosition;
-  if (m_EdtEngine.HasSelection()) {
+  if (m_pEditEngine->HasSelection()) {
     size_t start_idx;
     size_t count;
-    std::tie(start_idx, count) = m_EdtEngine.GetSelection();
+    std::tie(start_idx, count) = m_pEditEngine->GetSelection();
     sel_start = start_idx;
   }
 
   switch (pMsg->m_dwKeyCode) {
-    case FWL_VKEY_Left:
-      SetCursorPosition(m_EdtEngine.GetIndexLeft(m_CursorPosition));
+    case XFA_FWL_VKEY_Left:
+      SetCursorPosition(m_pEditEngine->GetIndexLeft(m_CursorPosition));
       break;
-    case FWL_VKEY_Right:
-      SetCursorPosition(m_EdtEngine.GetIndexRight(m_CursorPosition));
+    case XFA_FWL_VKEY_Right:
+      SetCursorPosition(m_pEditEngine->GetIndexRight(m_CursorPosition));
       break;
-    case FWL_VKEY_Up:
-      SetCursorPosition(m_EdtEngine.GetIndexUp(m_CursorPosition));
+    case XFA_FWL_VKEY_Up:
+      SetCursorPosition(m_pEditEngine->GetIndexUp(m_CursorPosition));
       break;
-    case FWL_VKEY_Down:
-      SetCursorPosition(m_EdtEngine.GetIndexDown(m_CursorPosition));
+    case XFA_FWL_VKEY_Down:
+      SetCursorPosition(m_pEditEngine->GetIndexDown(m_CursorPosition));
       break;
-    case FWL_VKEY_Home:
+    case XFA_FWL_VKEY_Home:
       SetCursorPosition(
-          bCtrl ? 0 : m_EdtEngine.GetIndexAtStartOfLine(m_CursorPosition));
+          bCtrl ? 0 : m_pEditEngine->GetIndexAtStartOfLine(m_CursorPosition));
       break;
-    case FWL_VKEY_End:
+    case XFA_FWL_VKEY_End:
       SetCursorPosition(
-          bCtrl ? m_EdtEngine.GetLength()
-                : m_EdtEngine.GetIndexAtEndOfLine(m_CursorPosition));
+          bCtrl ? m_pEditEngine->GetLength()
+                : m_pEditEngine->GetIndexAtEndOfLine(m_CursorPosition));
       break;
-    case FWL_VKEY_Delete: {
+    case XFA_FWL_VKEY_Delete: {
       if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly) ||
           (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) {
         break;
       }
 
-      if (m_CursorPosition > 0) {
-        SetCursorPosition(m_EdtEngine.GetIndexBefore(m_CursorPosition));
-        m_EdtEngine.Delete(m_CursorPosition, 1);
-      }
+      m_pEditEngine->Delete(m_CursorPosition, 1);
+      UpdateCaret();
       break;
     }
-    case FWL_VKEY_Insert:
-    case FWL_VKEY_F2:
-    case FWL_VKEY_Tab:
+    case XFA_FWL_VKEY_Insert:
+    case XFA_FWL_VKEY_F2:
+    case XFA_FWL_VKEY_Tab:
     default:
       break;
   }
 
   // Update the selection.
   if (bShift && sel_start != m_CursorPosition) {
-    m_EdtEngine.SetSelection(std::min(sel_start, m_CursorPosition),
-                             std::max(sel_start, m_CursorPosition));
+    m_pEditEngine->SetSelection(std::min(sel_start, m_CursorPosition),
+                                std::max(sel_start, m_CursorPosition));
     RepaintRect(m_rtEngine);
   }
 }
@@ -1337,41 +1243,32 @@
 
   wchar_t c = static_cast<wchar_t>(pMsg->m_dwKeyCode);
   switch (c) {
-    case FWL_VKEY_Back:
+    case L'\b':
       if (m_CursorPosition > 0) {
-        SetCursorPosition(m_EdtEngine.GetIndexBefore(m_CursorPosition));
-        m_EdtEngine.Delete(m_CursorPosition, 1);
+        SetCursorPosition(m_CursorPosition - 1);
+        m_pEditEngine->Delete(m_CursorPosition, 1);
+        UpdateCaret();
       }
       break;
-    case FWL_VKEY_NewLine:
-    case FWL_VKEY_Escape:
+    case L'\n':
+    case 27:   // Esc
+    case 127:  // Delete
       break;
-    case FWL_VKEY_Tab:
-      m_EdtEngine.Insert(m_CursorPosition, L"\t");
+    case L'\t':
+      m_pEditEngine->Insert(m_CursorPosition, L"\t");
       SetCursorPosition(m_CursorPosition + 1);
       break;
-    case FWL_VKEY_Return:
+    case L'\r':
       if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_WantReturn) {
-        m_EdtEngine.Insert(m_CursorPosition, L"\n");
+        m_pEditEngine->Insert(m_CursorPosition, L"\n");
         SetCursorPosition(m_CursorPosition + 1);
       }
       break;
     default: {
-      if (!m_pWidgetMgr->IsFormDisabled()) {
-        if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Number) {
-          if (((pMsg->m_dwKeyCode < FWL_VKEY_0) &&
-               (pMsg->m_dwKeyCode != 0x2E && pMsg->m_dwKeyCode != 0x2D)) ||
-              pMsg->m_dwKeyCode > FWL_VKEY_9) {
-            break;
-          }
-          if (!ValidateNumberChar(c))
-            break;
-        }
-      }
       if (pMsg->m_dwFlags & kEditingModifier)
         break;
 
-      m_EdtEngine.Insert(m_CursorPosition, WideString(c));
+      m_pEditEngine->Insert(m_CursorPosition, WideString(c));
       SetCursorPosition(m_CursorPosition + 1);
       break;
     }
diff --git a/xfa/fwl/cfwl_edit.h b/xfa/fwl/cfwl_edit.h
index 9c667f5..0242e5c 100644
--- a/xfa/fwl/cfwl_edit.h
+++ b/xfa/fwl/cfwl_edit.h
@@ -9,7 +9,6 @@
 
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "xfa/fde/cfde_texteditengine.h"
 #include "xfa/fwl/cfwl_event.h"
@@ -66,6 +65,7 @@
                     const CFX_Matrix& matrix) override;
 
   virtual void SetText(const WideString& wsText);
+  virtual void SetTextSkipNotify(const WideString& wsText);
 
   int32_t GetTextLength() const;
   WideString GetText() const;
@@ -93,7 +93,8 @@
   // CFDE_TextEditEngine::Delegate
   void NotifyTextFull() override;
   void OnCaretChanged() override;
-  void OnTextChanged(const WideString& prevText) override;
+  void OnTextWillChange(CFDE_TextEditEngine::TextChange* change) override;
+  void OnTextChanged() override;
   void OnSelChanged() override;
   bool OnValidate(const WideString& wsText) override;
   void SetScrollOffset(float fScrollOffset) override;
@@ -102,7 +103,7 @@
   void ShowCaret(CFX_RectF* pRect);
   void HideCaret(CFX_RectF* pRect);
   const CFX_RectF& GetRTClient() const { return m_rtClient; }
-  CFDE_TextEditEngine* GetTxtEdtEngine() { return &m_EdtEngine; }
+  CFDE_TextEditEngine* GetTxtEdtEngine() { return m_pEditEngine.get(); }
 
  private:
   void RenderText(CFX_RenderDevice* pRenderDev,
@@ -114,7 +115,6 @@
   void DrawContent(CXFA_Graphics* pGraphics,
                    IFWL_ThemeProvider* pTheme,
                    const CFX_Matrix* pMatrix);
-  void DrawSpellCheck(CXFA_Graphics* pGraphics, const CFX_Matrix* pMatrix);
 
   void UpdateEditEngine();
   void UpdateEditParams();
@@ -134,11 +134,6 @@
   bool ValidateNumberChar(wchar_t cNum);
   bool IsShowScrollBar(bool bVert);
   bool IsContentHeightOverflow();
-  void AddSpellCheckObj(CXFA_GEPath& PathData,
-                        int32_t nStart,
-                        int32_t nCount,
-                        float fOffSetX,
-                        float fOffSetY);
   void SetCursorPosition(size_t position);
   void UpdateCursorRect();
 
@@ -158,16 +153,16 @@
   CFX_RectF m_rtEngine;
   CFX_RectF m_rtStatic;
   CFX_RectF m_rtCaret;
-  float m_fVAlignOffset;
-  float m_fScrollOffsetX;
-  float m_fScrollOffsetY;
-  CFDE_TextEditEngine m_EdtEngine;
-  bool m_bLButtonDown;
-  size_t m_CursorPosition;
-  int32_t m_nLimit;
-  float m_fFontSize;
-  bool m_bSetRange;
-  int32_t m_iMax;
+  bool m_bLButtonDown = false;
+  bool m_bSetRange = false;
+  int32_t m_nLimit = -1;
+  int32_t m_iMax = 0xFFFFFFF;
+  float m_fVAlignOffset = 0.0f;
+  float m_fScrollOffsetX = 0.0f;
+  float m_fScrollOffsetY = 0.0f;
+  float m_fFontSize = 0.0f;
+  size_t m_CursorPosition = 0;
+  std::unique_ptr<CFDE_TextEditEngine> const m_pEditEngine;
   std::unique_ptr<CFWL_ScrollBar> m_pVertScrollBar;
   std::unique_ptr<CFWL_ScrollBar> m_pHorzScrollBar;
   std::unique_ptr<CFWL_Caret> m_pCaret;
diff --git a/xfa/fwl/cfwl_edit_embeddertest.cpp b/xfa/fwl/cfwl_edit_embeddertest.cpp
index 59a4ca5..9253293 100644
--- a/xfa/fwl/cfwl_edit_embeddertest.cpp
+++ b/xfa/fwl/cfwl_edit_embeddertest.cpp
@@ -8,13 +8,13 @@
 #include "testing/embedder_test.h"
 #include "testing/embedder_test_timer_handling_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
 
-class CFWLEditEmbeddertest : public EmbedderTest {
+class CFWLEditEmbedderTest : public XFAJSEmbedderTest {
  protected:
   void SetUp() override {
     EmbedderTest::SetUp();
     SetDelegate(&delegate_);
-    CreateAndInitializeFormPDF();
   }
 
   void TearDown() override {
@@ -22,8 +22,8 @@
     EmbedderTest::TearDown();
   }
 
-  void CreateAndInitializeFormPDF() {
-    EXPECT_TRUE(OpenDocument("xfa/email_recommended.pdf"));
+  void CreateAndInitializeFormPDF(const char* filename) {
+    EXPECT_TRUE(OpenDocument(filename));
     page_ = LoadPage(0);
     ASSERT_TRUE(page_);
   }
@@ -36,15 +36,13 @@
   EmbedderTestTimerHandlingDelegate delegate_;
 };
 
-TEST_F(CFWLEditEmbeddertest, Trivial) {
-  ASSERT_EQ(1u, delegate().GetAlerts().size());
-  auto alert = delegate().GetAlerts()[0];
-  EXPECT_STREQ(L"PDFium", alert.title.c_str());
-  EXPECT_STREQ(L"The value you entered for Text Field is invalid.",
-               alert.message.c_str());
+TEST_F(CFWLEditEmbedderTest, Trivial) {
+  CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
+  ASSERT_EQ(0u, delegate().GetAlerts().size());
 }
 
-TEST_F(CFWLEditEmbeddertest, LeftClickMouseSelection) {
+TEST_F(CFWLEditEmbedderTest, LeftClickMouseSelection) {
+  CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
   FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
   for (size_t i = 0; i < 10; ++i)
     FORM_OnChar(form_handle(), page(), 'a' + i, 0);
@@ -61,7 +59,14 @@
   EXPECT_STREQ(L"defgh", WideString::FromUTF16LE(buf, len).c_str());
 }
 
-TEST_F(CFWLEditEmbeddertest, DragMouseSelection) {
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_DragMouseSelection DISABLED_DragMouseSelection
+#else
+#define MAYBE_DragMouseSelection DragMouseSelection
+#endif
+TEST_F(CFWLEditEmbedderTest, MAYBE_DragMouseSelection) {
+  CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
   FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
   for (size_t i = 0; i < 10; ++i)
     FORM_OnChar(form_handle(), page(), 'a' + i, 0);
@@ -76,4 +81,162 @@
   unsigned short buf[128];
   unsigned long len = FORM_GetSelectedText(form_handle(), page(), &buf, 128);
   EXPECT_STREQ(L"defgh", WideString::FromUTF16LE(buf, len).c_str());
+
+  // TODO(hnakashima): This is incorrect. Visually 'abcdefgh' are selected.
+  const char kDraggedMD5[] = "f131526c8edd04e44de17b2647ec54c8";
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kDraggedMD5);
+  }
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_SimpleFill DISABLED_SimpleFill
+#else
+#define MAYBE_SimpleFill SimpleFill
+#endif
+TEST_F(CFWLEditEmbedderTest, MAYBE_SimpleFill) {
+  CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
+  const char kBlankMD5[] = "8dda78a3afaf9f7b5210eb81cacc4600";
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5);
+  }
+
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+  for (size_t i = 0; i < 10; ++i)
+    FORM_OnChar(form_handle(), page(), 'a' + i, 0);
+
+  const char kFilledMD5[] = "211e4e46eb347aa2bc7c425556d600b0";
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+  }
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_FillWithNewLineWithoutMultiline \
+  DISABLED_FillWithNewLineWithoutMultiline
+#else
+#define MAYBE_FillWithNewLineWithoutMultiline FillWithNewLineWithoutMultiline
+#endif
+TEST_F(CFWLEditEmbedderTest, MAYBE_FillWithNewLineWithoutMultiline) {
+  CreateAndInitializeFormPDF("xfa/email_recommended.pdf");
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+  for (size_t i = 0; i < 5; ++i)
+    FORM_OnChar(form_handle(), page(), 'a' + i, 0);
+  FORM_OnChar(form_handle(), page(), '\r', 0);
+  for (size_t i = 5; i < 10; ++i)
+    FORM_OnChar(form_handle(), page(), 'a' + i, 0);
+
+  const char kFilledMD5[] = "211e4e46eb347aa2bc7c425556d600b0";
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+  }
+}
+
+// Disabled due to flakiness.
+TEST_F(CFWLEditEmbedderTest, DISABLED_FillWithNewLineWithMultiline) {
+  CreateAndInitializeFormPDF("xfa/xfa_multiline_textfield.pdf");
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+
+  for (size_t i = 0; i < 5; ++i)
+    FORM_OnChar(form_handle(), page(), 'a' + i, 0);
+  FORM_OnChar(form_handle(), page(), '\r', 0);
+  for (size_t i = 5; i < 10; ++i)
+    FORM_OnChar(form_handle(), page(), 'a' + i, 0);
+
+  // Should look like:
+  // abcde
+  // fghij|
+  {
+#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+    const char kFilledMultilineMD5[] = "fc1f4d5fdb2c5755005fc525b0a60ec9";
+#else
+    const char kFilledMultilineMD5[] = "a5654e027d8b1667c20f3b86d1918003";
+#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMultilineMD5);
+  }
+
+  for (size_t i = 0; i < 4; ++i)
+    FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Left, 0);
+
+  // Should look like:
+  // abcde
+  // f|ghij
+
+  // Two backspaces is a workaround because left arrow does not behave well
+  // in the first character of a line. It skips back to the previous line.
+  for (size_t i = 0; i < 2; ++i)
+    FORM_OnChar(form_handle(), page(), '\b', 0);
+
+  // Should look like:
+  // abcde|ghij
+  {
+#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+    const char kMultilineBackspaceMD5[] = "8bb62a8100ff1e1cc113d4033e0d824e";
+#else
+    const char kMultilineBackspaceMD5[] = "a2f1dcab92bb1fb7c2f9ccc70100c989";
+#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kMultilineBackspaceMD5);
+  }
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_DateTimePickerTest DISABLED_DateTimePickerTest
+#else
+#define MAYBE_DateTimePickerTest DateTimePickerTest
+#endif
+TEST_F(CFWLEditEmbedderTest, MAYBE_DateTimePickerTest) {
+  CreateAndInitializeFormPDF("xfa/xfa_date_time_edit.pdf");
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+
+  const char kFilledMD5[] = "1036b8837a9dba75c6bd8f9347ae2eb2";
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+  }
+}
+
+TEST_F(CFWLEditEmbedderTest, ImageEditTest) {
+  CreateAndInitializeFormPDF("xfa/xfa_image_edit.pdf");
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+
+  const char kFilledMD5[] = "1940568c9ba33bac5d0b1ee9558c76b3";
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+  }
+}
+
+// TODO(crbug.com/pdfium/11): Fix this test and enable.
+#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
+#define MAYBE_ComboBoxTest DISABLED_ComboBoxTest
+#else
+#define MAYBE_ComboBoxTest ComboBoxTest
+#endif
+TEST_F(CFWLEditEmbedderTest, MAYBE_ComboBoxTest) {
+  CreateAndInitializeFormPDF("xfa/xfa_combobox.pdf");
+  FORM_OnLButtonDown(form_handle(), page(), 0, 115, 58);
+
+  const char kFilledMD5[] = "dad642ae8a5afce2591ffbcabbfc58dd";
+  {
+    ScopedFPDFBitmap page_bitmap =
+        RenderLoadedPageWithFlags(page(), FPDF_ANNOT);
+    CompareBitmap(page_bitmap.get(), 612, 792, kFilledMD5);
+  }
 }
diff --git a/xfa/fwl/cfwl_event.cpp b/xfa/fwl/cfwl_event.cpp
index 827eccc..5922bc1 100644
--- a/xfa/fwl/cfwl_event.cpp
+++ b/xfa/fwl/cfwl_event.cpp
@@ -6,15 +6,14 @@
 
 #include "xfa/fwl/cfwl_event.h"
 
-CFWL_Event::CFWL_Event(CFWL_Event::Type type)
-    : CFWL_Event(type, nullptr, nullptr) {}
+CFWL_Event::CFWL_Event(CFWL_Event::Type type) : m_type(type) {}
 
 CFWL_Event::CFWL_Event(Type type, CFWL_Widget* pSrcTarget)
-    : CFWL_Event(type, pSrcTarget, nullptr) {}
+    : m_type(type), m_pSrcTarget(pSrcTarget) {}
 
 CFWL_Event::CFWL_Event(Type type,
                        CFWL_Widget* pSrcTarget,
                        CFWL_Widget* pDstTarget)
-    : m_pSrcTarget(pSrcTarget), m_pDstTarget(pDstTarget), m_type(type) {}
+    : m_type(type), m_pSrcTarget(pSrcTarget), m_pDstTarget(pDstTarget) {}
 
-CFWL_Event::~CFWL_Event() {}
+CFWL_Event::~CFWL_Event() = default;
diff --git a/xfa/fwl/cfwl_event.h b/xfa/fwl/cfwl_event.h
index 01e3914..832c01f 100644
--- a/xfa/fwl/cfwl_event.h
+++ b/xfa/fwl/cfwl_event.h
@@ -7,20 +7,13 @@
 #ifndef XFA_FWL_CFWL_EVENT_H_
 #define XFA_FWL_CFWL_EVENT_H_
 
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "xfa/fwl/cfwl_messagekey.h"
-#include "xfa/fwl/cfwl_messagemouse.h"
-
-class CXFA_Graphics;
-class CFWL_Widget;
+#include "core/fxcrt/observed_ptr.h"
+#include "xfa/fwl/cfwl_widget.h"
 
 class CFWL_Event {
  public:
   enum class Type {
     CheckStateChanged,
-    CheckWord,
     Click,
     Close,
     EditChanged,
@@ -29,7 +22,7 @@
     PreDropDown,
     Scroll,
     SelectChanged,
-    TextChanged,
+    TextWillChange,
     TextFull,
     Validate
   };
@@ -40,12 +33,13 @@
   virtual ~CFWL_Event();
 
   Type GetType() const { return m_type; }
-
-  CFWL_Widget* m_pSrcTarget;
-  CFWL_Widget* m_pDstTarget;
+  CFWL_Widget* GetSrcTarget() const { return m_pSrcTarget.Get(); }
+  CFWL_Widget* GetDstTarget() const { return m_pDstTarget.Get(); }
 
  private:
-  Type m_type;
+  const Type m_type;
+  ObservedPtr<CFWL_Widget> const m_pSrcTarget;
+  ObservedPtr<CFWL_Widget> const m_pDstTarget;
 };
 
 #endif  // XFA_FWL_CFWL_EVENT_H_
diff --git a/xfa/fwl/cfwl_eventcheckword.cpp b/xfa/fwl/cfwl_eventcheckword.cpp
deleted file mode 100644
index fb0ce6e..0000000
--- a/xfa/fwl/cfwl_eventcheckword.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_eventcheckword.h"
-
-CFWL_EventCheckWord::CFWL_EventCheckWord(CFWL_Widget* pSrcTarget)
-    : CFWL_Event(CFWL_Event::Type::CheckWord, pSrcTarget) {}
-
-CFWL_EventCheckWord::~CFWL_EventCheckWord() {}
diff --git a/xfa/fwl/cfwl_eventcheckword.h b/xfa/fwl/cfwl_eventcheckword.h
deleted file mode 100644
index fafe3d3..0000000
--- a/xfa/fwl/cfwl_eventcheckword.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_EVENTCHECKWORD_H_
-#define XFA_FWL_CFWL_EVENTCHECKWORD_H_
-
-#include "xfa/fwl/cfwl_event.h"
-
-class CFWL_EventCheckWord : public CFWL_Event {
- public:
-  explicit CFWL_EventCheckWord(CFWL_Widget* pSrcTarget);
-  ~CFWL_EventCheckWord() override;
-
-  ByteString bsWord;
-  bool bCheckWord;
-};
-
-#endif  // XFA_FWL_CFWL_EVENTCHECKWORD_H_
diff --git a/xfa/fwl/cfwl_eventmouse.h b/xfa/fwl/cfwl_eventmouse.h
index a5caa93..e7982fb 100644
--- a/xfa/fwl/cfwl_eventmouse.h
+++ b/xfa/fwl/cfwl_eventmouse.h
@@ -8,14 +8,15 @@
 #define XFA_FWL_CFWL_EVENTMOUSE_H_
 
 #include "xfa/fwl/cfwl_event.h"
+#include "xfa/fwl/cfwl_messagemouse.h"
 
-class CFWL_EventMouse : public CFWL_Event {
+class CFWL_EventMouse final : public CFWL_Event {
  public:
   explicit CFWL_EventMouse(CFWL_Widget* pSrcTarget);
   CFWL_EventMouse(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
   ~CFWL_EventMouse() override;
 
-  FWL_MouseCommand m_dwCmd;
+  FWL_MouseCommand m_dwCmd = FWL_MouseCommand::LeftButtonDown;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTMOUSE_H_
diff --git a/xfa/fwl/cfwl_eventscroll.h b/xfa/fwl/cfwl_eventscroll.h
index a13eeef..78960c8 100644
--- a/xfa/fwl/cfwl_eventscroll.h
+++ b/xfa/fwl/cfwl_eventscroll.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fwl/cfwl_event.h"
 
-class CFWL_EventScroll : public CFWL_Event {
+class CFWL_EventScroll final : public CFWL_Event {
  public:
   enum class Code {
     None = 1,
@@ -27,8 +27,8 @@
   explicit CFWL_EventScroll(CFWL_Widget* pSrcTarget);
   ~CFWL_EventScroll() override;
 
-  Code m_iScrollCode;
-  float m_fPos;
+  Code m_iScrollCode = Code::None;
+  float m_fPos = 0.0f;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTSCROLL_H_
diff --git a/xfa/fwl/cfwl_eventselectchanged.h b/xfa/fwl/cfwl_eventselectchanged.h
index 30f1e2d..d4b68b3 100644
--- a/xfa/fwl/cfwl_eventselectchanged.h
+++ b/xfa/fwl/cfwl_eventselectchanged.h
@@ -9,18 +9,18 @@
 
 #include "xfa/fwl/cfwl_event.h"
 
-class CFWL_EventSelectChanged : public CFWL_Event {
+class CFWL_EventSelectChanged final : public CFWL_Event {
  public:
   explicit CFWL_EventSelectChanged(CFWL_Widget* pSrcTarget);
   ~CFWL_EventSelectChanged() override;
 
   // Used by ComboBox.
-  bool bLButtonUp;
+  bool bLButtonUp = false;
 
   // Used by DateTimePIcker
-  int32_t iYear;
-  int32_t iMonth;
-  int32_t iDay;
+  int32_t iYear = -1;
+  int32_t iMonth = -1;
+  int32_t iDay = -1;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTSELECTCHANGED_H_
diff --git a/xfa/fwl/cfwl_eventtarget.cpp b/xfa/fwl/cfwl_eventtarget.cpp
index 8a3b787..4b3d87c 100644
--- a/xfa/fwl/cfwl_eventtarget.cpp
+++ b/xfa/fwl/cfwl_eventtarget.cpp
@@ -10,7 +10,7 @@
 #include "xfa/fwl/ifwl_widgetdelegate.h"
 
 CFWL_EventTarget::CFWL_EventTarget(CFWL_Widget* pListener)
-    : m_pListener(pListener), m_bValid(true) {}
+    : m_pListener(pListener) {}
 
 CFWL_EventTarget::~CFWL_EventTarget() {}
 
@@ -23,7 +23,7 @@
   IFWL_WidgetDelegate* pDelegate = m_pListener->GetDelegate();
   if (!pDelegate)
     return false;
-  if (!m_widgets.empty() && m_widgets.count(pEvent->m_pSrcTarget) == 0)
+  if (!m_widgets.empty() && m_widgets.count(pEvent->GetSrcTarget()) == 0)
     return false;
 
   pDelegate->OnProcessEvent(pEvent);
diff --git a/xfa/fwl/cfwl_eventtarget.h b/xfa/fwl/cfwl_eventtarget.h
index b5a8bdb..f614319 100644
--- a/xfa/fwl/cfwl_eventtarget.h
+++ b/xfa/fwl/cfwl_eventtarget.h
@@ -26,9 +26,9 @@
   void FlagInvalid() { m_bValid = false; }
 
  private:
+  bool m_bValid = true;
+  CFWL_Widget* const m_pListener;
   std::set<CFWL_Widget*> m_widgets;
-  CFWL_Widget* m_pListener;
-  bool m_bValid;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTTARGET_H_
diff --git a/xfa/fwl/cfwl_eventtextchanged.cpp b/xfa/fwl/cfwl_eventtextchanged.cpp
deleted file mode 100644
index 439d99d..0000000
--- a/xfa/fwl/cfwl_eventtextchanged.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_eventtextchanged.h"
-
-CFWL_EventTextChanged::CFWL_EventTextChanged(CFWL_Widget* pSrcTarget)
-    : CFWL_Event(CFWL_Event::Type::TextChanged, pSrcTarget) {}
-
-CFWL_EventTextChanged::~CFWL_EventTextChanged() {}
diff --git a/xfa/fwl/cfwl_eventtextchanged.h b/xfa/fwl/cfwl_eventtextchanged.h
deleted file mode 100644
index 4494f08..0000000
--- a/xfa/fwl/cfwl_eventtextchanged.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_EVENTTEXTCHANGED_H_
-#define XFA_FWL_CFWL_EVENTTEXTCHANGED_H_
-
-#include "xfa/fwl/cfwl_event.h"
-
-class CFWL_EventTextChanged : public CFWL_Event {
- public:
-  explicit CFWL_EventTextChanged(CFWL_Widget* pSrcTarget);
-  ~CFWL_EventTextChanged() override;
-
-  WideString wsPrevText;
-};
-
-#endif  // XFA_FWL_CFWL_EVENTTEXTCHANGED_H_
diff --git a/xfa/fwl/cfwl_eventtextwillchange.cpp b/xfa/fwl/cfwl_eventtextwillchange.cpp
new file mode 100644
index 0000000..22b1100
--- /dev/null
+++ b/xfa/fwl/cfwl_eventtextwillchange.cpp
@@ -0,0 +1,12 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fwl/cfwl_eventtextwillchange.h"
+
+CFWL_EventTextWillChange::CFWL_EventTextWillChange(CFWL_Widget* pSrcTarget)
+    : CFWL_Event(CFWL_Event::Type::TextWillChange, pSrcTarget) {}
+
+CFWL_EventTextWillChange::~CFWL_EventTextWillChange() = default;
diff --git a/xfa/fwl/cfwl_eventtextwillchange.h b/xfa/fwl/cfwl_eventtextwillchange.h
new file mode 100644
index 0000000..a1bfe8c
--- /dev/null
+++ b/xfa/fwl/cfwl_eventtextwillchange.h
@@ -0,0 +1,24 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_
+#define XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_
+
+#include "xfa/fwl/cfwl_event.h"
+
+class CFWL_EventTextWillChange final : public CFWL_Event {
+ public:
+  explicit CFWL_EventTextWillChange(CFWL_Widget* pSrcTarget);
+  ~CFWL_EventTextWillChange() override;
+
+  WideString change_text;
+  WideString previous_text;
+  bool cancelled = false;
+  size_t selection_start = 0;
+  size_t selection_end = 0;
+};
+
+#endif  // XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_
diff --git a/xfa/fwl/cfwl_eventvalidate.h b/xfa/fwl/cfwl_eventvalidate.h
index b8feff1..26e3c7f 100644
--- a/xfa/fwl/cfwl_eventvalidate.h
+++ b/xfa/fwl/cfwl_eventvalidate.h
@@ -9,13 +9,13 @@
 
 #include "xfa/fwl/cfwl_event.h"
 
-class CFWL_EventValidate : public CFWL_Event {
+class CFWL_EventValidate final : public CFWL_Event {
  public:
   explicit CFWL_EventValidate(CFWL_Widget* pSrcTarget);
   ~CFWL_EventValidate() override;
 
+  bool bValidate = false;
   WideString wsInsert;
-  bool bValidate;
 };
 
 #endif  // XFA_FWL_CFWL_EVENTVALIDATE_H_
diff --git a/xfa/fwl/cfwl_form.cpp b/xfa/fwl/cfwl_form.cpp
deleted file mode 100644
index d1c1783..0000000
--- a/xfa/fwl/cfwl_form.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_form.h"
-
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
-#include "xfa/fde/cfde_textout.h"
-#include "xfa/fwl/cfwl_app.h"
-#include "xfa/fwl/cfwl_event.h"
-#include "xfa/fwl/cfwl_formproxy.h"
-#include "xfa/fwl/cfwl_messagemouse.h"
-#include "xfa/fwl/cfwl_notedriver.h"
-#include "xfa/fwl/cfwl_noteloop.h"
-#include "xfa/fwl/cfwl_themebackground.h"
-#include "xfa/fwl/cfwl_themepart.h"
-#include "xfa/fwl/cfwl_themetext.h"
-#include "xfa/fwl/cfwl_widgetmgr.h"
-#include "xfa/fwl/ifwl_themeprovider.h"
-
-CFWL_Form::CFWL_Form(const CFWL_App* app,
-                     std::unique_ptr<CFWL_WidgetProperties> properties,
-                     CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter),
-      m_pSubFocus(nullptr),
-      m_fCXBorder(0),
-      m_fCYBorder(0) {
-  m_rtRelative.Reset();
-  m_rtRestore.Reset();
-
-  RegisterForm();
-  RegisterEventTarget(nullptr);
-}
-
-CFWL_Form::~CFWL_Form() {
-  UnregisterEventTarget();
-  UnRegisterForm();
-}
-
-FWL_Type CFWL_Form::GetClassID() const {
-  return FWL_Type::Form;
-}
-
-bool CFWL_Form::IsInstance(const WideStringView& wsClass) const {
-  if (wsClass == WideStringView(FWL_CLASS_Form))
-    return true;
-  return CFWL_Widget::IsInstance(wsClass);
-}
-
-CFX_RectF CFWL_Form::GetClientRect() {
-  CFX_RectF rect = m_pProperties->m_rtWidget;
-  rect.Offset(-rect.left, -rect.top);
-  return rect;
-}
-
-void CFWL_Form::Update() {
-  if (m_iLock > 0)
-    return;
-  if (!m_pProperties->m_pThemeProvider)
-    m_pProperties->m_pThemeProvider = GetAvailableTheme();
-
-  Layout();
-}
-
-FWL_WidgetHit CFWL_Form::HitTest(const CFX_PointF& point) {
-  GetAvailableTheme();
-
-  CFX_RectF rtCap(m_fCYBorder, m_fCXBorder, -2 * m_fCYBorder, 0 - m_fCXBorder);
-  return rtCap.Contains(point) ? FWL_WidgetHit::Titlebar
-                               : FWL_WidgetHit::Client;
-}
-
-void CFWL_Form::DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) {
-  if (!pGraphics)
-    return;
-  if (!m_pProperties->m_pThemeProvider)
-    return;
-
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
-  DrawBackground(pGraphics, pTheme);
-
-#if _FX_OS_ == _FX_OS_MACOSX_
-  return;
-#endif
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_dwStates = CFWL_PartState_Normal;
-  param.m_pGraphics = pGraphics;
-  param.m_rtPart = m_rtRelative;
-  param.m_matrix.Concat(matrix);
-  if (m_pProperties->m_dwStyles & FWL_WGTSTYLE_Border) {
-    param.m_iPart = CFWL_Part::Border;
-    pTheme->DrawBackground(&param);
-  }
-}
-
-CFWL_Widget* CFWL_Form::DoModal() {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return nullptr;
-
-  CFWL_NoteDriver* pDriver = pApp->GetNoteDriver();
-  if (!pDriver)
-    return nullptr;
-
-  m_pNoteLoop = pdfium::MakeUnique<CFWL_NoteLoop>();
-  m_pNoteLoop->SetMainForm(this);
-
-  pDriver->PushNoteLoop(m_pNoteLoop.get());
-  RemoveStates(FWL_WGTSTATE_Invisible);
-  pDriver->Run();
-
-#if _FX_OS_ != _FX_OS_MACOSX_
-  pDriver->PopNoteLoop();
-#endif
-
-  m_pNoteLoop.reset();
-  return nullptr;
-}
-
-void CFWL_Form::EndDoModal() {
-  if (!m_pNoteLoop)
-    return;
-
-#if (_FX_OS_ == _FX_OS_MACOSX_)
-  m_pNoteLoop->EndModalLoop();
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  if (!pDriver)
-    return;
-
-  pDriver->PopNoteLoop();
-  SetStates(FWL_WGTSTATE_Invisible);
-#else
-  SetStates(FWL_WGTSTATE_Invisible);
-  m_pNoteLoop->EndModalLoop();
-#endif
-}
-
-void CFWL_Form::DrawBackground(CXFA_Graphics* pGraphics,
-                               IFWL_ThemeProvider* pTheme) {
-  CFWL_ThemeBackground param;
-  param.m_pWidget = this;
-  param.m_iPart = CFWL_Part::Background;
-  param.m_pGraphics = pGraphics;
-  param.m_rtPart = m_rtRelative;
-  param.m_rtPart.Deflate(m_fCYBorder, m_fCXBorder, m_fCYBorder, m_fCXBorder);
-  pTheme->DrawBackground(&param);
-}
-
-CFX_RectF CFWL_Form::GetEdgeRect() {
-  CFX_RectF rtEdge = m_rtRelative;
-  if (m_pProperties->m_dwStyles & FWL_WGTSTYLE_Border) {
-    float fCX = GetBorderSize(true);
-    float fCY = GetBorderSize(false);
-    rtEdge.Deflate(fCX, fCY, fCX, fCY);
-  }
-  return rtEdge;
-}
-
-void CFWL_Form::SetWorkAreaRect() {
-  m_rtRestore = m_pProperties->m_rtWidget;
-  CFWL_WidgetMgr* pWidgetMgr = GetOwnerApp()->GetWidgetMgr();
-  if (!pWidgetMgr)
-    return;
-  RepaintRect(m_rtRelative);
-}
-
-void CFWL_Form::Layout() {
-  m_rtRelative = GetRelativeRect();
-
-#if _FX_OS_ == _FX_OS_MACOSX_
-  IFWL_ThemeProvider* theme = GetAvailableTheme();
-  m_fCXBorder = theme ? theme->GetCXBorderSize() : 0.0f;
-  m_fCYBorder = theme ? theme->GetCYBorderSize() : 0.0f;
-#endif
-}
-
-void CFWL_Form::RegisterForm() {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  if (!pDriver)
-    return;
-
-  pDriver->RegisterForm(this);
-}
-
-void CFWL_Form::UnRegisterForm() {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  if (!pDriver)
-    return;
-
-  pDriver->UnRegisterForm(this);
-}
-
-void CFWL_Form::OnProcessMessage(CFWL_Message* pMessage) {
-#if _FX_OS_ == _FX_OS_MACOSX_
-  if (!pMessage)
-    return;
-
-  switch (pMessage->GetType()) {
-    case CFWL_Message::Type::Mouse: {
-      CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
-      switch (pMsg->m_dwCmd) {
-        case FWL_MouseCommand::LeftButtonDown:
-          OnLButtonDown(pMsg);
-          break;
-        case FWL_MouseCommand::LeftButtonUp:
-          OnLButtonUp(pMsg);
-          break;
-        default:
-          break;
-      }
-      break;
-    }
-    default:
-      break;
-  }
-#endif  // _FX_OS_ == _FX_OS_MACOSX_
-}
-
-void CFWL_Form::OnDrawWidget(CXFA_Graphics* pGraphics,
-                             const CFX_Matrix& matrix) {
-  DrawWidget(pGraphics, matrix);
-}
-
-void CFWL_Form::OnLButtonDown(CFWL_MessageMouse* pMsg) {
-  SetGrab(true);
-}
-
-void CFWL_Form::OnLButtonUp(CFWL_MessageMouse* pMsg) {
-  SetGrab(false);
-}
diff --git a/xfa/fwl/cfwl_form.h b/xfa/fwl/cfwl_form.h
deleted file mode 100644
index cac1747..0000000
--- a/xfa/fwl/cfwl_form.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_FORM_H_
-#define XFA_FWL_CFWL_FORM_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_system.h"
-#include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/cfwl_widgetproperties.h"
-
-#define FWL_CLASS_Form L"FWL_FORM"
-#define FWL_CLASS_FormProxy L"FWL_FORMPROXY"
-
-class CFWL_MessageMouse;
-class CFWL_NoteLoop;
-class CFWL_Widget;
-class IFWL_ThemeProvider;
-
-class CFWL_Form : public CFWL_Widget {
- public:
-  CFWL_Form(const CFWL_App* app,
-            std::unique_ptr<CFWL_WidgetProperties> properties,
-            CFWL_Widget* pOuter);
-  ~CFWL_Form() override;
-
-  // CFWL_Widget
-  FWL_Type GetClassID() const override;
-  bool IsInstance(const WideStringView& wsClass) const override;
-  CFX_RectF GetClientRect() override;
-  void Update() override;
-  FWL_WidgetHit HitTest(const CFX_PointF& point) override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
-  void OnProcessMessage(CFWL_Message* pMessage) override;
-  void OnDrawWidget(CXFA_Graphics* pGraphics,
-                    const CFX_Matrix& matrix) override;
-
-  CFWL_Widget* DoModal();
-  void EndDoModal();
-
-  CFWL_Widget* GetSubFocus() const { return m_pSubFocus; }
-  void SetSubFocus(CFWL_Widget* pWidget) { m_pSubFocus = pWidget; }
-
- private:
-  void DrawBackground(CXFA_Graphics* pGraphics, IFWL_ThemeProvider* pTheme);
-  CFX_RectF GetEdgeRect();
-  void SetWorkAreaRect();
-  void Layout();
-  void RegisterForm();
-  void UnRegisterForm();
-  void OnLButtonDown(CFWL_MessageMouse* pMsg);
-  void OnLButtonUp(CFWL_MessageMouse* pMsg);
-
-  CFX_RectF m_rtRestore;
-  CFX_RectF m_rtRelative;
-  std::unique_ptr<CFWL_NoteLoop> m_pNoteLoop;
-  CFWL_Widget* m_pSubFocus;
-  float m_fCXBorder;
-  float m_fCYBorder;
-};
-
-#endif  // XFA_FWL_CFWL_FORM_H_
diff --git a/xfa/fwl/cfwl_formproxy.cpp b/xfa/fwl/cfwl_formproxy.cpp
deleted file mode 100644
index d03c148..0000000
--- a/xfa/fwl/cfwl_formproxy.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_formproxy.h"
-
-#include <memory>
-#include <utility>
-
-#include "third_party/base/ptr_util.h"
-#include "xfa/fwl/cfwl_notedriver.h"
-
-CFWL_FormProxy::CFWL_FormProxy(
-    const CFWL_App* app,
-    std::unique_ptr<CFWL_WidgetProperties> properties,
-    CFWL_Widget* pOuter)
-    : CFWL_Form(app, std::move(properties), pOuter) {}
-
-CFWL_FormProxy::~CFWL_FormProxy() {}
-
-FWL_Type CFWL_FormProxy::GetClassID() const {
-  return FWL_Type::FormProxy;
-}
-
-bool CFWL_FormProxy::IsInstance(const WideStringView& wsClass) const {
-  if (wsClass == WideStringView(FWL_CLASS_FormProxy))
-    return true;
-  return CFWL_Form::IsInstance(wsClass);
-}
-
-void CFWL_FormProxy::Update() {}
-
-void CFWL_FormProxy::DrawWidget(CXFA_Graphics* pGraphics,
-                                const CFX_Matrix& matrix) {}
-
-void CFWL_FormProxy::OnProcessMessage(CFWL_Message* pMessage) {
-  m_pOuter->GetDelegate()->OnProcessMessage(pMessage);
-}
diff --git a/xfa/fwl/cfwl_formproxy.h b/xfa/fwl/cfwl_formproxy.h
deleted file mode 100644
index 498ff42..0000000
--- a/xfa/fwl/cfwl_formproxy.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_FORMPROXY_H_
-#define XFA_FWL_CFWL_FORMPROXY_H_
-
-#include <memory>
-
-#include "xfa/fwl/cfwl_form.h"
-
-class CFWL_WidgetProperties;
-
-class CFWL_FormProxy : public CFWL_Form {
- public:
-  CFWL_FormProxy(const CFWL_App* app,
-                 std::unique_ptr<CFWL_WidgetProperties> properties,
-                 CFWL_Widget* pOuter);
-  ~CFWL_FormProxy() override;
-
-  // CFWL_Widget
-  FWL_Type GetClassID() const override;
-  bool IsInstance(const WideStringView& wsClass) const override;
-  void Update() override;
-  void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
-  void OnProcessMessage(CFWL_Message* pMessage) override;
-};
-
-#endif  // XFA_FWL_CFWL_FORMPROXY_H_
diff --git a/xfa/fwl/cfwl_listbox.cpp b/xfa/fwl/cfwl_listbox.cpp
index 0ddb65b..b2b0f2e 100644
--- a/xfa/fwl/cfwl_listbox.cpp
+++ b/xfa/fwl/cfwl_listbox.cpp
@@ -20,6 +20,7 @@
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/cfwl_themetext.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 namespace {
@@ -31,16 +32,7 @@
 CFWL_ListBox::CFWL_ListBox(const CFWL_App* app,
                            std::unique_ptr<CFWL_WidgetProperties> properties,
                            CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter),
-      m_iTTOAligns(FDE_TextAlignment::kTopLeft),
-      m_hAnchor(nullptr),
-      m_fScorllBarWidth(0),
-      m_bLButtonDown(false),
-      m_pScrollBarTP(nullptr) {
-  m_rtClient.Reset();
-  m_rtConent.Reset();
-  m_rtStatic.Reset();
-}
+    : CFWL_Widget(app, std::move(properties), pOuter) {}
 
 CFWL_ListBox::~CFWL_ListBox() {}
 
@@ -69,7 +61,7 @@
       break;
     }
   }
-  m_dwTTOStyles.single_line_ = true;
+  m_TTOStyles.single_line_ = true;
   m_fScorllBarWidth = GetScrollWidth();
   CalcSize(false);
 }
@@ -94,10 +86,11 @@
                               const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
+
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
+  if (!pTheme)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
   pGraphics->SaveGraphState();
   if (HasBorder())
     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
@@ -177,17 +170,17 @@
                                          uint32_t dwKeyCode) {
   CFWL_ListItem* hRet = nullptr;
   switch (dwKeyCode) {
-    case FWL_VKEY_Up:
-    case FWL_VKEY_Down:
-    case FWL_VKEY_Home:
-    case FWL_VKEY_End: {
-      const bool bUp = dwKeyCode == FWL_VKEY_Up;
-      const bool bDown = dwKeyCode == FWL_VKEY_Down;
-      const bool bHome = dwKeyCode == FWL_VKEY_Home;
+    case XFA_FWL_VKEY_Up:
+    case XFA_FWL_VKEY_Down:
+    case XFA_FWL_VKEY_Home:
+    case XFA_FWL_VKEY_End: {
+      const bool bUp = dwKeyCode == XFA_FWL_VKEY_Up;
+      const bool bDown = dwKeyCode == XFA_FWL_VKEY_Down;
+      const bool bHome = dwKeyCode == XFA_FWL_VKEY_Home;
       int32_t iDstItem = -1;
       if (bUp || bDown) {
         int32_t index = GetItemIndex(this, pItem);
-        iDstItem = dwKeyCode == FWL_VKEY_Up ? index - 1 : index + 1;
+        iDstItem = dwKeyCode == XFA_FWL_VKEY_Up ? index - 1 : index + 1;
       } else if (bHome) {
         iDstItem = 0;
       } else {
@@ -365,11 +358,11 @@
   param.m_matrix.Concat(*pMatrix);
   param.m_rtPart = m_rtClient;
   if (IsShowScrollBar(false) && IsShowScrollBar(true))
-    param.m_pData = &m_rtStatic;
+    param.m_pRtData = &m_rtStatic;
   if (!IsEnabled())
     param.m_dwStates = CFWL_PartState_Disabled;
 
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 void CFWL_ListBox::DrawItems(CXFA_Graphics* pGraphics,
@@ -432,14 +425,14 @@
   bg_param.m_rtPart = rtItem;
   bg_param.m_bMaximize = true;
   CFX_RectF rtFocus(rtItem);
-  bg_param.m_pData = &rtFocus;
+  bg_param.m_pRtData = &rtFocus;
   if (m_pVertScrollBar && !m_pHorzScrollBar &&
       (dwPartStates & CFWL_PartState_Focused)) {
     bg_param.m_rtPart.left += 1;
     bg_param.m_rtPart.width -= (m_fScorllBarWidth + 1);
     rtFocus.Deflate(0.5, 0.5, 1 + m_fScorllBarWidth, 1);
   }
-  pTheme->DrawBackground(&bg_param);
+  pTheme->DrawBackground(bg_param);
 
   if (!pItem)
     return;
@@ -458,11 +451,11 @@
   textParam.m_pGraphics = pGraphics;
   textParam.m_matrix.Concat(*pMatrix);
   textParam.m_rtPart = rtText;
-  textParam.m_wsText = wsText;
-  textParam.m_dwTTOStyles = m_dwTTOStyles;
+  textParam.m_wsText = std::move(wsText);
+  textParam.m_dwTTOStyles = m_TTOStyles;
   textParam.m_iTTOAlign = m_iTTOAligns;
   textParam.m_bMaximize = true;
-  pTheme->DrawText(&textParam);
+  pTheme->DrawText(textParam);
 }
 
 CFX_SizeF CFWL_ListBox::CalcSize(bool bAutoSize) {
@@ -476,7 +469,7 @@
     CFWL_ThemePart part;
     part.m_pWidget = this;
     IFWL_ThemeProvider* theme = GetAvailableTheme();
-    CFX_RectF pUIMargin = theme ? theme->GetUIMargin(&part) : CFX_RectF();
+    CFX_RectF pUIMargin = theme ? theme->GetUIMargin(part) : CFX_RectF();
     m_rtConent.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
                        pUIMargin.height);
   }
@@ -599,8 +592,8 @@
     if (!pItem)
       continue;
 
-    CFX_SizeF sz =
-        CalcTextSize(pItem->GetText(), m_pProperties->m_pThemeProvider, false);
+    CFX_SizeF sz = CalcTextSize(pItem->GetText(),
+                                m_pProperties->m_pThemeProvider.Get(), false);
     fRet = std::max(fRet, sz.width);
   }
   return fRet;
@@ -615,7 +608,7 @@
   IFWL_ThemeProvider* theme = GetAvailableTheme();
   CFWL_ThemePart part;
   part.m_pWidget = this;
-  return (theme ? theme->GetFontSize(&part) : 20.0f) + 2 * kItemTextMargin;
+  return (theme ? theme->GetFontSize(part) : 20.0f) + 2 * kItemTextMargin;
 }
 
 void CFWL_ListBox::InitVerticalScrollBar() {
@@ -647,8 +640,9 @@
 bool CFWL_ListBox::IsShowScrollBar(bool bVert) {
   CFWL_ScrollBar* pScrollbar =
       bVert ? m_pVertScrollBar.get() : m_pHorzScrollBar.get();
-  if (!pScrollbar || (pScrollbar->GetStates() & FWL_WGTSTATE_Invisible))
+  if (!pScrollbar || !pScrollbar->IsVisible())
     return false;
+
   return !(m_pProperties->m_dwStyleExes &
            FWL_STYLEEXT_LTB_ShowScrollBarFocus) ||
          (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused);
@@ -693,7 +687,9 @@
     default:
       break;
   }
-  CFWL_Widget::OnProcessMessage(pMessage);
+  // Dst target could be |this|, continue only if not destroyed by above.
+  if (pMessage->GetDstTarget())
+    CFWL_Widget::OnProcessMessage(pMessage);
 }
 
 void CFWL_ListBox::OnProcessEvent(CFWL_Event* pEvent) {
@@ -702,7 +698,7 @@
   if (pEvent->GetType() != CFWL_Event::Type::Scroll)
     return;
 
-  CFWL_Widget* pSrcTarget = pEvent->m_pSrcTarget;
+  CFWL_Widget* pSrcTarget = pEvent->GetSrcTarget();
   if ((pSrcTarget == m_pVertScrollBar.get() && m_pVertScrollBar) ||
       (pSrcTarget == m_pHorzScrollBar.get() && m_pHorzScrollBar)) {
     CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
@@ -741,8 +737,6 @@
 
 void CFWL_ListBox::OnLButtonDown(CFWL_MessageMouse* pMsg) {
   m_bLButtonDown = true;
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
-    SetFocus(true);
 
   CFWL_ListItem* pItem = GetItemAtPoint(pMsg->m_pos);
   if (!pItem)
@@ -788,11 +782,11 @@
 void CFWL_ListBox::OnKeyDown(CFWL_MessageKey* pMsg) {
   uint32_t dwKeyCode = pMsg->m_dwKeyCode;
   switch (dwKeyCode) {
-    case FWL_VKEY_Tab:
-    case FWL_VKEY_Up:
-    case FWL_VKEY_Down:
-    case FWL_VKEY_Home:
-    case FWL_VKEY_End: {
+    case XFA_FWL_VKEY_Tab:
+    case XFA_FWL_VKEY_Up:
+    case XFA_FWL_VKEY_Down:
+    case XFA_FWL_VKEY_Home:
+    case XFA_FWL_VKEY_End: {
       CFWL_ListItem* pItem = GetFocusedItem();
       pItem = GetListItem(pItem, dwKeyCode);
       bool bShift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift);
@@ -907,9 +901,8 @@
   return it != m_ItemArray.end() ? it - m_ItemArray.begin() : -1;
 }
 
-CFWL_ListItem* CFWL_ListBox::AddString(const WideStringView& wsAdd) {
-  m_ItemArray.emplace_back(
-      pdfium::MakeUnique<CFWL_ListItem>(WideString(wsAdd)));
+CFWL_ListItem* CFWL_ListBox::AddString(const WideString& wsAdd) {
+  m_ItemArray.emplace_back(pdfium::MakeUnique<CFWL_ListItem>(wsAdd));
   return m_ItemArray.back().get();
 }
 
diff --git a/xfa/fwl/cfwl_listbox.h b/xfa/fwl/cfwl_listbox.h
index 4a7818c..178e49a 100644
--- a/xfa/fwl/cfwl_listbox.h
+++ b/xfa/fwl/cfwl_listbox.h
@@ -53,7 +53,7 @@
   CFWL_ListItem* GetItem(const CFWL_Widget* pWidget, int32_t nIndex) const;
   int32_t GetItemIndex(CFWL_Widget* pWidget, CFWL_ListItem* pItem);
 
-  CFWL_ListItem* AddString(const WideStringView& wsAdd);
+  CFWL_ListItem* AddString(const WideString& wsAdd);
   void RemoveAt(int32_t iIndex);
   void DeleteString(CFWL_ListItem* pItem);
   void DeleteAll();
@@ -122,13 +122,13 @@
   CFX_RectF m_rtConent;
   std::unique_ptr<CFWL_ScrollBar> m_pHorzScrollBar;
   std::unique_ptr<CFWL_ScrollBar> m_pVertScrollBar;
-  FDE_TextStyle m_dwTTOStyles;
-  FDE_TextAlignment m_iTTOAligns;
-  CFWL_ListItem* m_hAnchor;
-  float m_fItemHeight;
-  float m_fScorllBarWidth;
-  bool m_bLButtonDown;
-  IFWL_ThemeProvider* m_pScrollBarTP;
+  FDE_TextStyle m_TTOStyles;
+  FDE_TextAlignment m_iTTOAligns = FDE_TextAlignment::kTopLeft;
+  bool m_bLButtonDown = false;
+  float m_fItemHeight = 0.0f;
+  float m_fScorllBarWidth = 0.0f;
+  CFWL_ListItem* m_hAnchor = nullptr;
+  IFWL_ThemeProvider* m_pScrollBarTP = nullptr;
   std::vector<std::unique_ptr<CFWL_ListItem>> m_ItemArray;
 };
 
diff --git a/xfa/fwl/cfwl_listitem.cpp b/xfa/fwl/cfwl_listitem.cpp
index f8a7b75..edf1be3 100644
--- a/xfa/fwl/cfwl_listitem.cpp
+++ b/xfa/fwl/cfwl_listitem.cpp
@@ -6,9 +6,6 @@
 
 #include "xfa/fwl/cfwl_listitem.h"
 
-CFWL_ListItem::CFWL_ListItem(const WideString& text)
-    : m_dwStates(0), m_wsText(text) {
-  m_rtItem.Reset();
-}
+CFWL_ListItem::CFWL_ListItem(const WideString& text) : m_wsText(text) {}
 
 CFWL_ListItem::~CFWL_ListItem() {}
diff --git a/xfa/fwl/cfwl_listitem.h b/xfa/fwl/cfwl_listitem.h
index 62c3a98..a6afb22 100644
--- a/xfa/fwl/cfwl_listitem.h
+++ b/xfa/fwl/cfwl_listitem.h
@@ -24,8 +24,8 @@
   WideString GetText() const { return m_wsText; }
 
  private:
+  uint32_t m_dwStates = 0;
   CFX_RectF m_rtItem;
-  uint32_t m_dwStates;
   WideString m_wsText;
 };
 
diff --git a/xfa/fwl/cfwl_message.cpp b/xfa/fwl/cfwl_message.cpp
index 258fe62..f330a08 100644
--- a/xfa/fwl/cfwl_message.cpp
+++ b/xfa/fwl/cfwl_message.cpp
@@ -6,19 +6,9 @@
 
 #include "xfa/fwl/cfwl_message.h"
 
-CFWL_Message::CFWL_Message(CFWL_Message::Type type)
-    : CFWL_Message(type, nullptr, nullptr) {}
-
-CFWL_Message::CFWL_Message(Type type, CFWL_Widget* pSrcTarget)
-    : CFWL_Message(type, pSrcTarget, nullptr) {}
-
 CFWL_Message::CFWL_Message(Type type,
                            CFWL_Widget* pSrcTarget,
                            CFWL_Widget* pDstTarget)
-    : m_pSrcTarget(pSrcTarget), m_pDstTarget(pDstTarget), m_type(type) {}
+    : m_type(type), m_pSrcTarget(pSrcTarget), m_pDstTarget(pDstTarget) {}
 
-CFWL_Message::~CFWL_Message() {}
-
-std::unique_ptr<CFWL_Message> CFWL_Message::Clone() {
-  return nullptr;
-}
+CFWL_Message::~CFWL_Message() = default;
diff --git a/xfa/fwl/cfwl_message.h b/xfa/fwl/cfwl_message.h
index 99cf01b..69f7bf5 100644
--- a/xfa/fwl/cfwl_message.h
+++ b/xfa/fwl/cfwl_message.h
@@ -11,26 +11,30 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-
-class CFWL_Widget;
+#include "core/fxcrt/observed_ptr.h"
+#include "xfa/fwl/cfwl_widget.h"
 
 class CFWL_Message {
  public:
   enum class Type { Key, KillFocus, Mouse, MouseWheel, SetFocus };
 
-  explicit CFWL_Message(Type type);
-  CFWL_Message(Type type, CFWL_Widget* pSrcTarget);
-  CFWL_Message(Type type, CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
   virtual ~CFWL_Message();
 
-  virtual std::unique_ptr<CFWL_Message> Clone();
   Type GetType() const { return m_type; }
+  CFWL_Widget* GetSrcTarget() const { return m_pSrcTarget.Get(); }
+  CFWL_Widget* GetDstTarget() const { return m_pDstTarget.Get(); }
+  void SetSrcTarget(CFWL_Widget* pWidget) { m_pSrcTarget.Reset(pWidget); }
+  void SetDstTarget(CFWL_Widget* pWidget) { m_pDstTarget.Reset(pWidget); }
 
-  CFWL_Widget* m_pSrcTarget;
-  CFWL_Widget* m_pDstTarget;
+ protected:
+  CFWL_Message(Type type, CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
+  CFWL_Message(const CFWL_Message& that) = delete;
+  CFWL_Message& operator=(const CFWL_Message& that) = delete;
 
  private:
-  Type m_type;
+  const Type m_type;
+  ObservedPtr<CFWL_Widget> m_pSrcTarget;
+  ObservedPtr<CFWL_Widget> m_pDstTarget;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGE_H_
diff --git a/xfa/fwl/cfwl_messagekey.cpp b/xfa/fwl/cfwl_messagekey.cpp
index 38d4ea0..9abb0c1 100644
--- a/xfa/fwl/cfwl_messagekey.cpp
+++ b/xfa/fwl/cfwl_messagekey.cpp
@@ -10,12 +10,13 @@
 
 #include "third_party/base/ptr_util.h"
 
-CFWL_MessageKey::CFWL_MessageKey(CFWL_Widget* pSrcTarget,
-                                 CFWL_Widget* pDstTarget)
-    : CFWL_Message(CFWL_Message::Type::Key, pSrcTarget, pDstTarget) {}
+CFWL_MessageKey::CFWL_MessageKey(CFWL_Widget* pDstTarget,
+                                 FWL_KeyCommand cmd,
+                                 uint32_t flags,
+                                 uint32_t keycode)
+    : CFWL_Message(CFWL_Message::Type::Key, nullptr, pDstTarget),
+      m_dwCmd(cmd),
+      m_dwFlags(flags),
+      m_dwKeyCode(keycode) {}
 
-CFWL_MessageKey::~CFWL_MessageKey() {}
-
-std::unique_ptr<CFWL_Message> CFWL_MessageKey::Clone() {
-  return pdfium::MakeUnique<CFWL_MessageKey>(*this);
-}
+CFWL_MessageKey::~CFWL_MessageKey() = default;
diff --git a/xfa/fwl/cfwl_messagekey.h b/xfa/fwl/cfwl_messagekey.h
index bf430d3..b122647 100644
--- a/xfa/fwl/cfwl_messagekey.h
+++ b/xfa/fwl/cfwl_messagekey.h
@@ -13,17 +13,17 @@
 
 enum class FWL_KeyCommand { KeyDown, KeyUp, Char };
 
-class CFWL_MessageKey : public CFWL_Message {
+class CFWL_MessageKey final : public CFWL_Message {
  public:
-  CFWL_MessageKey(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
+  CFWL_MessageKey(CFWL_Widget* pDstTarget,
+                  FWL_KeyCommand cmd,
+                  uint32_t flags,
+                  uint32_t keycode);
   ~CFWL_MessageKey() override;
 
-  // CFWL_Message
-  std::unique_ptr<CFWL_Message> Clone() override;
-
-  uint32_t m_dwKeyCode;
-  uint32_t m_dwFlags;
-  FWL_KeyCommand m_dwCmd;
+  const FWL_KeyCommand m_dwCmd;
+  const uint32_t m_dwFlags;
+  const uint32_t m_dwKeyCode;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGEKEY_H_
diff --git a/xfa/fwl/cfwl_messagekillfocus.cpp b/xfa/fwl/cfwl_messagekillfocus.cpp
index 55247d1..15fe562 100644
--- a/xfa/fwl/cfwl_messagekillfocus.cpp
+++ b/xfa/fwl/cfwl_messagekillfocus.cpp
@@ -17,8 +17,4 @@
                                              CFWL_Widget* pDstTarget)
     : CFWL_Message(CFWL_Message::Type::KillFocus, pSrcTarget, pDstTarget) {}
 
-CFWL_MessageKillFocus::~CFWL_MessageKillFocus() {}
-
-std::unique_ptr<CFWL_Message> CFWL_MessageKillFocus::Clone() {
-  return pdfium::MakeUnique<CFWL_MessageKillFocus>(*this);
-}
+CFWL_MessageKillFocus::~CFWL_MessageKillFocus() = default;
diff --git a/xfa/fwl/cfwl_messagekillfocus.h b/xfa/fwl/cfwl_messagekillfocus.h
index d6ca799..18e64f9 100644
--- a/xfa/fwl/cfwl_messagekillfocus.h
+++ b/xfa/fwl/cfwl_messagekillfocus.h
@@ -9,18 +9,21 @@
 
 #include <memory>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_message.h"
 
-class CFWL_MessageKillFocus : public CFWL_Message {
+class CFWL_MessageKillFocus final : public CFWL_Message {
  public:
   explicit CFWL_MessageKillFocus(CFWL_Widget* pSrcTarget);
   CFWL_MessageKillFocus(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
   ~CFWL_MessageKillFocus() override;
 
-  // CFWL_Message
-  std::unique_ptr<CFWL_Message> Clone() override;
+  bool IsFocusedOnWidget(const CFWL_Widget* pWidget) const {
+    return pWidget == m_pSetFocus;
+  }
 
-  CFWL_Widget* m_pSetFocus;
+ private:
+  UnownedPtr<CFWL_Widget> m_pSetFocus;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGEKILLFOCUS_H_
diff --git a/xfa/fwl/cfwl_messagemouse.cpp b/xfa/fwl/cfwl_messagemouse.cpp
index 1d56b0f..395c9e6 100644
--- a/xfa/fwl/cfwl_messagemouse.cpp
+++ b/xfa/fwl/cfwl_messagemouse.cpp
@@ -10,14 +10,18 @@
 
 #include "third_party/base/ptr_util.h"
 
-CFWL_MessageMouse::CFWL_MessageMouse(CFWL_Widget* pSrcTarget,
-                                     CFWL_Widget* pDstTarget)
-    : CFWL_Message(CFWL_Message::Type::Mouse, pSrcTarget, pDstTarget) {}
+CFWL_MessageMouse::CFWL_MessageMouse(CFWL_Widget* pDstTarget,
+                                     FWL_MouseCommand cmd)
+    : CFWL_Message(CFWL_Message::Type::Mouse, nullptr, pDstTarget),
+      m_dwCmd(cmd) {}
 
-CFWL_MessageMouse::CFWL_MessageMouse(const CFWL_MessageMouse& other) = default;
+CFWL_MessageMouse::CFWL_MessageMouse(CFWL_Widget* pDstTarget,
+                                     FWL_MouseCommand cmd,
+                                     uint32_t flags,
+                                     CFX_PointF pos)
+    : CFWL_Message(CFWL_Message::Type::Mouse, nullptr, pDstTarget),
+      m_dwCmd(cmd),
+      m_dwFlags(flags),
+      m_pos(pos) {}
 
-CFWL_MessageMouse::~CFWL_MessageMouse() {}
-
-std::unique_ptr<CFWL_Message> CFWL_MessageMouse::Clone() {
-  return pdfium::MakeUnique<CFWL_MessageMouse>(*this);
-}
+CFWL_MessageMouse::~CFWL_MessageMouse() = default;
diff --git a/xfa/fwl/cfwl_messagemouse.h b/xfa/fwl/cfwl_messagemouse.h
index a2b0d39..10c298a 100644
--- a/xfa/fwl/cfwl_messagemouse.h
+++ b/xfa/fwl/cfwl_messagemouse.h
@@ -25,18 +25,18 @@
   Hover
 };
 
-class CFWL_MessageMouse : public CFWL_Message {
+class CFWL_MessageMouse final : public CFWL_Message {
  public:
-  CFWL_MessageMouse(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
-  CFWL_MessageMouse(const CFWL_MessageMouse& other);
+  CFWL_MessageMouse(CFWL_Widget* pDstTarget, FWL_MouseCommand cmd);
+  CFWL_MessageMouse(CFWL_Widget* pDstTarget,
+                    FWL_MouseCommand cmd,
+                    uint32_t flags,
+                    CFX_PointF pos);
   ~CFWL_MessageMouse() override;
 
-  // CFWL_Message
-  std::unique_ptr<CFWL_Message> Clone() override;
-
+  const FWL_MouseCommand m_dwCmd;
+  uint32_t m_dwFlags = 0;
   CFX_PointF m_pos;
-  uint32_t m_dwFlags;
-  FWL_MouseCommand m_dwCmd;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGEMOUSE_H_
diff --git a/xfa/fwl/cfwl_messagemousewheel.cpp b/xfa/fwl/cfwl_messagemousewheel.cpp
index 8996f65..3331179 100644
--- a/xfa/fwl/cfwl_messagemousewheel.cpp
+++ b/xfa/fwl/cfwl_messagemousewheel.cpp
@@ -10,15 +10,13 @@
 
 #include "third_party/base/ptr_util.h"
 
-CFWL_MessageMouseWheel::CFWL_MessageMouseWheel(CFWL_Widget* pSrcTarget,
-                                               CFWL_Widget* pDstTarget)
-    : CFWL_Message(CFWL_Message::Type::MouseWheel, pSrcTarget, pDstTarget) {}
+CFWL_MessageMouseWheel::CFWL_MessageMouseWheel(CFWL_Widget* pDstTarget,
+                                               uint32_t flags,
+                                               CFX_PointF pos,
+                                               CFX_PointF delta)
+    : CFWL_Message(CFWL_Message::Type::MouseWheel, nullptr, pDstTarget),
+      m_dwFlags(flags),
+      m_pos(pos),
+      m_delta(delta) {}
 
-CFWL_MessageMouseWheel::CFWL_MessageMouseWheel(const CFWL_MessageMouseWheel&) =
-    default;
-
-CFWL_MessageMouseWheel::~CFWL_MessageMouseWheel() {}
-
-std::unique_ptr<CFWL_Message> CFWL_MessageMouseWheel::Clone() {
-  return pdfium::MakeUnique<CFWL_MessageMouseWheel>(*this);
-}
+CFWL_MessageMouseWheel::~CFWL_MessageMouseWheel() = default;
diff --git a/xfa/fwl/cfwl_messagemousewheel.h b/xfa/fwl/cfwl_messagemousewheel.h
index f969b9a..9908951 100644
--- a/xfa/fwl/cfwl_messagemousewheel.h
+++ b/xfa/fwl/cfwl_messagemousewheel.h
@@ -12,18 +12,17 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "xfa/fwl/cfwl_message.h"
 
-class CFWL_MessageMouseWheel : public CFWL_Message {
+class CFWL_MessageMouseWheel final : public CFWL_Message {
  public:
-  CFWL_MessageMouseWheel(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
-  CFWL_MessageMouseWheel(const CFWL_MessageMouseWheel&);
+  CFWL_MessageMouseWheel(CFWL_Widget* pDstTarget,
+                         uint32_t flags,
+                         CFX_PointF pos,
+                         CFX_PointF delta);
   ~CFWL_MessageMouseWheel() override;
 
-  // CFWL_Message
-  std::unique_ptr<CFWL_Message> Clone() override;
-
+  const uint32_t m_dwFlags;
   CFX_PointF m_pos;
   CFX_PointF m_delta;
-  uint32_t m_dwFlags;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGEMOUSEWHEEL_H_
diff --git a/xfa/fwl/cfwl_messagesetfocus.cpp b/xfa/fwl/cfwl_messagesetfocus.cpp
index f7653c9..ec0c27a 100644
--- a/xfa/fwl/cfwl_messagesetfocus.cpp
+++ b/xfa/fwl/cfwl_messagesetfocus.cpp
@@ -14,8 +14,5 @@
                                            CFWL_Widget* pDstTarget)
     : CFWL_Message(CFWL_Message::Type::SetFocus, pSrcTarget, pDstTarget) {}
 
-CFWL_MessageSetFocus::~CFWL_MessageSetFocus() {}
+CFWL_MessageSetFocus::~CFWL_MessageSetFocus() = default;
 
-std::unique_ptr<CFWL_Message> CFWL_MessageSetFocus::Clone() {
-  return pdfium::MakeUnique<CFWL_MessageSetFocus>(*this);
-}
diff --git a/xfa/fwl/cfwl_messagesetfocus.h b/xfa/fwl/cfwl_messagesetfocus.h
index 05616fb..67e0035 100644
--- a/xfa/fwl/cfwl_messagesetfocus.h
+++ b/xfa/fwl/cfwl_messagesetfocus.h
@@ -11,13 +11,10 @@
 
 #include "xfa/fwl/cfwl_message.h"
 
-class CFWL_MessageSetFocus : public CFWL_Message {
+class CFWL_MessageSetFocus final : public CFWL_Message {
  public:
   CFWL_MessageSetFocus(CFWL_Widget* pSrcTarget, CFWL_Widget* pDstTarget);
   ~CFWL_MessageSetFocus() override;
-
-  // CFWL_Message
-  std::unique_ptr<CFWL_Message> Clone() override;
 };
 
 #endif  // XFA_FWL_CFWL_MESSAGESETFOCUS_H_
diff --git a/xfa/fwl/cfwl_monthcalendar.cpp b/xfa/fwl/cfwl_monthcalendar.cpp
index 08ed83f..76d9156 100644
--- a/xfa/fwl/cfwl_monthcalendar.cpp
+++ b/xfa/fwl/cfwl_monthcalendar.cpp
@@ -14,7 +14,6 @@
 #include "third_party/base/stl_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_datetimepicker.h"
-#include "xfa/fwl/cfwl_formproxy.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_themebackground.h"
@@ -22,7 +21,6 @@
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 #define MONTHCAL_HSEP_HEIGHT 1
-#define MONTHCAL_VSEP_WIDTH 1
 #define MONTHCAL_HMARGIN 3
 #define MONTHCAL_VMARGIN 2
 #define MONTHCAL_ROWS 9
@@ -32,54 +30,58 @@
 
 namespace {
 
-WideString GetCapacityForDay(IFWL_ThemeProvider* pTheme,
-                             CFWL_ThemePart& params,
-                             uint32_t day) {
-  ASSERT(day < 7);
-
-  if (day == 0)
-    return L"Sun";
-  if (day == 1)
-    return L"Mon";
-  if (day == 2)
-    return L"Tue";
-  if (day == 3)
-    return L"Wed";
-  if (day == 4)
-    return L"Thu";
-  if (day == 5)
-    return L"Fri";
-  return L"Sat";
+WideString GetAbbreviatedDayOfWeek(int day) {
+  switch (day) {
+    case 0:
+      return L"Sun";
+    case 1:
+      return L"Mon";
+    case 2:
+      return L"Tue";
+    case 3:
+      return L"Wed";
+    case 4:
+      return L"Thu";
+    case 5:
+      return L"Fri";
+    case 6:
+      return L"Sat";
+    default:
+      NOTREACHED();
+      return L"";
+  }
 }
 
-WideString GetCapacityForMonth(IFWL_ThemeProvider* pTheme,
-                               CFWL_ThemePart& params,
-                               uint32_t month) {
-  ASSERT(month < 12);
-
-  if (month == 0)
-    return L"January";
-  if (month == 1)
-    return L"February";
-  if (month == 2)
-    return L"March";
-  if (month == 3)
-    return L"April";
-  if (month == 4)
-    return L"May";
-  if (month == 5)
-    return L"June";
-  if (month == 6)
-    return L"July";
-  if (month == 7)
-    return L"August";
-  if (month == 8)
-    return L"September";
-  if (month == 9)
-    return L"October";
-  if (month == 10)
-    return L"November";
-  return L"December";
+WideString GetMonth(int month) {
+  switch (month) {
+    case 0:
+      return L"January";
+    case 1:
+      return L"February";
+    case 2:
+      return L"March";
+    case 3:
+      return L"April";
+    case 4:
+      return L"May";
+    case 5:
+      return L"June";
+    case 6:
+      return L"July";
+    case 7:
+      return L"August";
+    case 8:
+      return L"September";
+    case 9:
+      return L"October";
+    case 10:
+      return L"November";
+    case 11:
+      return L"December";
+    default:
+      NOTREACHED();
+      return L"";
+  }
 }
 
 }  // namespace
@@ -88,35 +90,9 @@
     const CFWL_App* app,
     std::unique_ptr<CFWL_WidgetProperties> properties,
     CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter),
-      m_bInitialized(false),
-      m_iCurYear(2011),
-      m_iCurMonth(1),
-      m_iYear(2011),
-      m_iMonth(1),
-      m_iDay(1),
-      m_iHovered(-1),
-      m_iLBtnPartStates(CFWL_PartState_Normal),
-      m_iRBtnPartStates(CFWL_PartState_Normal),
-      m_bFlag(false) {
-  m_rtHead.Reset();
-  m_rtWeek.Reset();
-  m_rtLBtn.Reset();
-  m_rtRBtn.Reset();
-  m_rtDates.Reset();
-  m_rtHSep.Reset();
-  m_rtHeadText.Reset();
-  m_rtToday.Reset();
-  m_rtTodayFlag.Reset();
-  m_rtClient.Reset();
-  m_rtWeekNum.Reset();
-  m_rtWeekNumSep.Reset();
-}
+    : CFWL_Widget(app, std::move(properties), pOuter) {}
 
-CFWL_MonthCalendar::~CFWL_MonthCalendar() {
-  ClearDateItem();
-  m_arrSelDays.clear();
-}
+CFWL_MonthCalendar::~CFWL_MonthCalendar() = default;
 
 FWL_Type CFWL_MonthCalendar::GetClassID() const {
   return FWL_Type::MonthCalendar;
@@ -150,10 +126,11 @@
                                     const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
+
   if (!m_pProperties->m_pThemeProvider)
     m_pProperties->m_pThemeProvider = GetAvailableTheme();
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
   if (HasBorder())
     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
 
@@ -161,7 +138,7 @@
   DrawHeadBK(pGraphics, pTheme, &matrix);
   DrawLButton(pGraphics, pTheme, &matrix);
   DrawRButton(pGraphics, pTheme, &matrix);
-  DrawSeperator(pGraphics, pTheme, &matrix);
+  DrawSeparator(pGraphics, pTheme, &matrix);
   DrawDatesInBK(pGraphics, pTheme, &matrix);
   DrawDatesInCircle(pGraphics, pTheme, &matrix);
   DrawCaption(pGraphics, pTheme, &matrix);
@@ -189,7 +166,7 @@
   params.m_rtPart = m_rtClient;
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&params);
+  pTheme->DrawBackground(params);
 }
 
 void CFWL_MonthCalendar::DrawHeadBK(CXFA_Graphics* pGraphics,
@@ -203,7 +180,7 @@
   params.m_rtPart = m_rtHead;
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&params);
+  pTheme->DrawBackground(params);
 }
 
 void CFWL_MonthCalendar::DrawLButton(CXFA_Graphics* pGraphics,
@@ -217,7 +194,7 @@
   params.m_rtPart = m_rtLBtn;
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&params);
+  pTheme->DrawBackground(params);
 }
 
 void CFWL_MonthCalendar::DrawRButton(CXFA_Graphics* pGraphics,
@@ -231,7 +208,7 @@
   params.m_rtPart = m_rtRBtn;
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&params);
+  pTheme->DrawBackground(params);
 }
 
 void CFWL_MonthCalendar::DrawCaption(CXFA_Graphics* pGraphics,
@@ -243,18 +220,18 @@
   textParam.m_dwStates = CFWL_PartState_Normal;
   textParam.m_pGraphics = pGraphics;
   textParam.m_wsText = GetHeadText(m_iCurYear, m_iCurMonth);
-  m_szHead =
-      CalcTextSize(textParam.m_wsText, m_pProperties->m_pThemeProvider, false);
+  m_szHead = CalcTextSize(textParam.m_wsText,
+                          m_pProperties->m_pThemeProvider.Get(), false);
   CalcHeadSize();
   textParam.m_rtPart = m_rtHeadText;
   textParam.m_dwTTOStyles.single_line_ = true;
   textParam.m_iTTOAlign = FDE_TextAlignment::kCenter;
   if (pMatrix)
     textParam.m_matrix.Concat(*pMatrix);
-  pTheme->DrawText(&textParam);
+  pTheme->DrawText(textParam);
 }
 
-void CFWL_MonthCalendar::DrawSeperator(CXFA_Graphics* pGraphics,
+void CFWL_MonthCalendar::DrawSeparator(CXFA_Graphics* pGraphics,
                                        IFWL_ThemeProvider* pTheme,
                                        const CFX_Matrix* pMatrix) {
   CFWL_ThemeBackground params;
@@ -265,7 +242,7 @@
   params.m_rtPart = m_rtHSep;
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&params);
+  pTheme->DrawBackground(params);
 }
 
 void CFWL_MonthCalendar::DrawDatesInBK(CXFA_Graphics* pGraphics,
@@ -290,10 +267,10 @@
       params.m_dwStates |= CFWL_PartState_Hovered;
     } else if (pDataInfo->dwStates & FWL_ITEMSTATE_MCD_Flag) {
       params.m_dwStates = CFWL_PartState_Flagged;
-      pTheme->DrawBackground(&params);
+      pTheme->DrawBackground(params);
     }
     params.m_rtPart = pDataInfo->rect;
-    pTheme->DrawBackground(&params);
+    pTheme->DrawBackground(params);
     params.m_dwStates = 0;
   }
 }
@@ -313,14 +290,14 @@
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
 
-  for (int32_t i = 0; i < 7; i++) {
+  for (int32_t i = 0; i < 7; ++i) {
     rtDayOfWeek =
         CFX_RectF(m_rtWeek.left + i * (m_szCell.width + MONTHCAL_HMARGIN * 2),
                   m_rtWeek.top, m_szCell);
 
     params.m_rtPart = rtDayOfWeek;
-    params.m_wsText = GetCapacityForDay(pTheme, params, i);
-    pTheme->DrawText(&params);
+    params.m_wsText = GetAbbreviatedDayOfWeek(i);
+    pTheme->DrawText(params);
   }
 }
 
@@ -333,17 +310,17 @@
   params.m_pGraphics = pGraphics;
   params.m_dwStates = CFWL_PartState_Normal;
   params.m_iTTOAlign = FDE_TextAlignment::kCenterLeft;
-  params.m_wsText = L"Today" + GetTodayText(m_iYear, m_iMonth, m_iDay);
+  params.m_wsText = GetTodayText(m_iYear, m_iMonth, m_iDay);
 
-  m_szToday =
-      CalcTextSize(params.m_wsText, m_pProperties->m_pThemeProvider, false);
+  m_szToday = CalcTextSize(params.m_wsText,
+                           m_pProperties->m_pThemeProvider.Get(), false);
   CalcTodaySize();
   params.m_rtPart = m_rtToday;
   params.m_dwTTOStyles.single_line_ = true;
 
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawText(&params);
+  pTheme->DrawText(params);
 }
 
 void CFWL_MonthCalendar::DrawDatesIn(CXFA_Graphics* pGraphics,
@@ -368,7 +345,7 @@
       params.m_dwStates |= CFWL_PartState_Hovered;
 
     params.m_dwTTOStyles.single_line_ = true;
-    pTheme->DrawText(&params);
+    pTheme->DrawText(params);
   }
 }
 
@@ -383,7 +360,7 @@
   params.m_iTTOAlign = FDE_TextAlignment::kCenter;
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawText(&params);
+  pTheme->DrawText(params);
 }
 
 void CFWL_MonthCalendar::DrawDatesInCircle(CXFA_Graphics* pGraphics,
@@ -407,22 +384,18 @@
   params.m_dwStates = CFWL_PartState_Normal;
   if (pMatrix)
     params.m_matrix.Concat(*pMatrix);
-  pTheme->DrawBackground(&params);
+  pTheme->DrawBackground(params);
 }
 
 CFX_SizeF CFWL_MonthCalendar::CalcSize() {
-  if (!m_pProperties->m_pThemeProvider)
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
+  if (!pTheme)
     return CFX_SizeF();
 
-  CFWL_ThemePart params;
-  params.m_pWidget = this;
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
   float fMaxWeekW = 0.0f;
   float fMaxWeekH = 0.0f;
-
-  for (uint32_t i = 0; i < 7; ++i) {
-    CFX_SizeF sz = CalcTextSize(GetCapacityForDay(pTheme, params, i),
-                                m_pProperties->m_pThemeProvider, false);
+  for (int i = 0; i < 7; ++i) {
+    CFX_SizeF sz = CalcTextSize(GetAbbreviatedDayOfWeek(i), pTheme, false);
     fMaxWeekW = (fMaxWeekW >= sz.width) ? fMaxWeekW : sz.width;
     fMaxWeekH = (fMaxWeekH >= sz.height) ? fMaxWeekH : sz.height;
   }
@@ -430,40 +403,37 @@
   float fDayMaxW = 0.0f;
   float fDayMaxH = 0.0f;
   for (int day = 10; day <= 31; day++) {
-    CFX_SizeF sz = CalcTextSize(WideString::Format(L"%d", day),
-                                m_pProperties->m_pThemeProvider, false);
+    CFX_SizeF sz = CalcTextSize(WideString::Format(L"%d", day), pTheme, false);
     fDayMaxW = (fDayMaxW >= sz.width) ? fDayMaxW : sz.width;
     fDayMaxH = (fDayMaxH >= sz.height) ? fDayMaxH : sz.height;
   }
-  m_szCell.width = float((fMaxWeekW >= fDayMaxW) ? (int)(fMaxWeekW + 0.5)
-                                                 : (int)(fDayMaxW + 0.5));
-  m_szCell.height = (fMaxWeekH >= fDayMaxH) ? fMaxWeekH : fDayMaxH;
+  m_szCell.width =
+      static_cast<int>(0.5 + (fMaxWeekW >= fDayMaxW ? fMaxWeekW : fDayMaxW));
+  m_szCell.height = fMaxWeekH >= fDayMaxH ? fMaxWeekH : fDayMaxH;
 
   CFX_SizeF fs;
   fs.width = m_szCell.width * MONTHCAL_COLUMNS +
              MONTHCAL_HMARGIN * MONTHCAL_COLUMNS * 2 +
              MONTHCAL_HEADER_BTN_HMARGIN * 2;
+
   float fMonthMaxW = 0.0f;
   float fMonthMaxH = 0.0f;
-
-  for (uint32_t i = 0; i < 12; ++i) {
-    CFX_SizeF sz = CalcTextSize(GetCapacityForMonth(pTheme, params, i),
-                                m_pProperties->m_pThemeProvider, false);
+  for (int i = 0; i < 12; ++i) {
+    CFX_SizeF sz = CalcTextSize(GetMonth(i), pTheme, false);
     fMonthMaxW = (fMonthMaxW >= sz.width) ? fMonthMaxW : sz.width;
     fMonthMaxH = (fMonthMaxH >= sz.height) ? fMonthMaxH : sz.height;
   }
 
-  CFX_SizeF szYear = CalcTextSize(GetHeadText(m_iYear, m_iMonth),
-                                  m_pProperties->m_pThemeProvider, false);
+  CFX_SizeF szYear =
+      CalcTextSize(GetHeadText(m_iYear, m_iMonth), pTheme, false);
   fMonthMaxH = std::max(fMonthMaxH, szYear.height);
   m_szHead = CFX_SizeF(fMonthMaxW + szYear.width, fMonthMaxH);
   fMonthMaxW =
       m_szHead.width + MONTHCAL_HEADER_BTN_HMARGIN * 2 + m_szCell.width * 2;
   fs.width = std::max(fs.width, fMonthMaxW);
 
-  WideString wsToday = GetTodayText(m_iYear, m_iMonth, m_iDay);
-  m_wsToday = L"Today" + wsToday;
-  m_szToday = CalcTextSize(wsToday, m_pProperties->m_pThemeProvider, false);
+  m_wsToday = GetTodayText(m_iYear, m_iMonth, m_iDay);
+  m_szToday = CalcTextSize(m_wsToday, pTheme, false);
   m_szToday.height = (m_szToday.height >= m_szCell.height) ? m_szToday.height
                                                            : m_szCell.height;
   fs.height = m_szCell.width + m_szCell.height * (MONTHCAL_ROWS - 2) +
@@ -552,11 +522,11 @@
 }
 
 void CFWL_MonthCalendar::InitDate() {
-  // TODO(dsinclair): These should pull the real today values instead of
-  // pretending it's 2011-01-01.
-  m_iYear = 2011;
-  m_iMonth = 1;
-  m_iDay = 1;
+  CFX_DateTime now = CFX_DateTime::Now();
+
+  m_iYear = now.GetYear();
+  m_iMonth = now.GetMonth();
+  m_iDay = now.GetDay();
   m_iCurYear = m_iYear;
   m_iCurMonth = m_iMonth;
 
@@ -672,7 +642,9 @@
 }
 
 WideString CFWL_MonthCalendar::GetHeadText(int32_t iYear, int32_t iMonth) {
-  ASSERT(iMonth > 0 && iMonth < 13);
+  ASSERT(iMonth > 0);
+  ASSERT(iMonth < 13);
+
   static const wchar_t* const pMonth[] = {L"January", L"February", L"March",
                                           L"April",   L"May",      L"June",
                                           L"July",    L"August",   L"September",
@@ -683,7 +655,7 @@
 WideString CFWL_MonthCalendar::GetTodayText(int32_t iYear,
                                             int32_t iMonth,
                                             int32_t iDay) {
-  return WideString::Format(L", %d/%d/%d", iDay, iMonth, iYear);
+  return WideString::Format(L"Today, %d/%d/%d", iDay, iMonth, iYear);
 }
 
 int32_t CFWL_MonthCalendar::GetDayAtPoint(const CFX_PointF& point) const {
@@ -738,7 +710,9 @@
     default:
       break;
   }
-  CFWL_Widget::OnProcessMessage(pMessage);
+  // Dst target could be |this|, continue only if not destroyed by above.
+  if (pMessage->GetDstTarget())
+    CFWL_Widget::OnProcessMessage(pMessage);
 }
 
 void CFWL_MonthCalendar::OnDrawWidget(CXFA_Graphics* pGraphics,
@@ -758,58 +732,10 @@
   } else if (m_rtToday.Contains(pMsg->m_pos)) {
     JumpToToday();
     RepaintRect(m_rtClient);
-  } else {
-    CFWL_DateTimePicker* pIPicker = static_cast<CFWL_DateTimePicker*>(m_pOuter);
-    if (pIPicker->IsMonthCalendarVisible())
-      m_bFlag = true;
   }
 }
 
 void CFWL_MonthCalendar::OnLButtonUp(CFWL_MessageMouse* pMsg) {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return DisForm_OnLButtonUp(pMsg);
-
-  if (m_rtLBtn.Contains(pMsg->m_pos)) {
-    m_iLBtnPartStates = 0;
-    RepaintRect(m_rtLBtn);
-    return;
-  }
-  if (m_rtRBtn.Contains(pMsg->m_pos)) {
-    m_iRBtnPartStates = 0;
-    RepaintRect(m_rtRBtn);
-    return;
-  }
-  if (m_rtToday.Contains(pMsg->m_pos))
-    return;
-
-  int32_t iOldSel = 0;
-  if (!m_arrSelDays.empty())
-    iOldSel = m_arrSelDays[0];
-
-  int32_t iCurSel = GetDayAtPoint(pMsg->m_pos);
-  CFWL_DateTimePicker* pIPicker = static_cast<CFWL_DateTimePicker*>(m_pOuter);
-  if (iCurSel > 0) {
-    DATEINFO* lpDatesInfo = m_arrDates[iCurSel - 1].get();
-    CFX_RectF rtInvalidate(lpDatesInfo->rect);
-    if (iOldSel > 0 && iOldSel <= pdfium::CollectionSize<int32_t>(m_arrDates)) {
-      lpDatesInfo = m_arrDates[iOldSel - 1].get();
-      rtInvalidate.Union(lpDatesInfo->rect);
-    }
-    AddSelDay(iCurSel);
-    if (!m_pOuter)
-      return;
-
-    pIPicker->ProcessSelChanged(m_iCurYear, m_iCurMonth, iCurSel);
-    pIPicker->ShowMonthCalendar(false);
-  } else if (m_bFlag &&
-             (!CFX_RectF(0, 0, pIPicker->GetFormProxy()->GetWidgetRect().Size())
-                   .Contains(pMsg->m_pos))) {
-    pIPicker->ShowMonthCalendar(false);
-  }
-  m_bFlag = false;
-}
-
-void CFWL_MonthCalendar::DisForm_OnLButtonUp(CFWL_MessageMouse* pMsg) {
   if (m_rtLBtn.Contains(pMsg->m_pos)) {
     m_iLBtnPartStates = 0;
     RepaintRect(m_rtLBtn);
diff --git a/xfa/fwl/cfwl_monthcalendar.h b/xfa/fwl/cfwl_monthcalendar.h
index 7b68600..a00ea0f 100644
--- a/xfa/fwl/cfwl_monthcalendar.h
+++ b/xfa/fwl/cfwl_monthcalendar.h
@@ -19,9 +19,8 @@
 #define FWL_ITEMSTATE_MCD_Selected (1L << 1)
 
 class CFWL_MessageMouse;
-class CFWL_Widget;
 
-class CFWL_MonthCalendar : public CFWL_Widget {
+class CFWL_MonthCalendar final : public CFWL_Widget {
  public:
   CFWL_MonthCalendar(const CFWL_App* app,
                      std::unique_ptr<CFWL_WidgetProperties> properties,
@@ -104,7 +103,7 @@
   void DrawCaption(CXFA_Graphics* pGraphics,
                    IFWL_ThemeProvider* pTheme,
                    const CFX_Matrix* pMatrix);
-  void DrawSeperator(CXFA_Graphics* pGraphics,
+  void DrawSeparator(CXFA_Graphics* pGraphics,
                      IFWL_ThemeProvider* pTheme,
                      const CFX_Matrix* pMatrix);
   void DrawDatesInBK(CXFA_Graphics* pGraphics,
@@ -146,11 +145,10 @@
   CFX_RectF GetDayRect(int32_t iDay);
   void OnLButtonDown(CFWL_MessageMouse* pMsg);
   void OnLButtonUp(CFWL_MessageMouse* pMsg);
-  void DisForm_OnLButtonUp(CFWL_MessageMouse* pMsg);
   void OnMouseMove(CFWL_MessageMouse* pMsg);
   void OnMouseLeave(CFWL_MessageMouse* pMsg);
 
-  bool m_bInitialized;
+  bool m_bInitialized = false;
   CFX_RectF m_rtHead;
   CFX_RectF m_rtWeek;
   CFX_RectF m_rtLBtn;
@@ -160,19 +158,17 @@
   CFX_RectF m_rtHeadText;
   CFX_RectF m_rtToday;
   CFX_RectF m_rtTodayFlag;
-  CFX_RectF m_rtWeekNum;
-  CFX_RectF m_rtWeekNumSep;
   WideString m_wsHead;
   WideString m_wsToday;
   std::vector<std::unique_ptr<DATEINFO>> m_arrDates;
-  int32_t m_iCurYear;
-  int32_t m_iCurMonth;
-  int32_t m_iYear;
-  int32_t m_iMonth;
-  int32_t m_iDay;
-  int32_t m_iHovered;
-  int32_t m_iLBtnPartStates;
-  int32_t m_iRBtnPartStates;
+  int32_t m_iCurYear = 2011;
+  int32_t m_iCurMonth = 1;
+  int32_t m_iYear = 2011;
+  int32_t m_iMonth = 1;
+  int32_t m_iDay = 1;
+  int32_t m_iHovered = -1;
+  int32_t m_iLBtnPartStates = CFWL_PartState_Normal;
+  int32_t m_iRBtnPartStates = CFWL_PartState_Normal;
   DATE m_dtMin;
   DATE m_dtMax;
   CFX_SizeF m_szHead;
@@ -180,7 +176,6 @@
   CFX_SizeF m_szToday;
   std::vector<int32_t> m_arrSelDays;
   CFX_RectF m_rtClient;
-  bool m_bFlag;
 };
 
 #endif  // XFA_FWL_CFWL_MONTHCALENDAR_H_
diff --git a/xfa/fwl/cfwl_notedriver.cpp b/xfa/fwl/cfwl_notedriver.cpp
index 9a19f4b..821dd5c 100644
--- a/xfa/fwl/cfwl_notedriver.cpp
+++ b/xfa/fwl/cfwl_notedriver.cpp
@@ -9,29 +9,23 @@
 #include <algorithm>
 #include <utility>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_eventtarget.h"
-#include "xfa/fwl/cfwl_form.h"
 #include "xfa/fwl/cfwl_messagekey.h"
 #include "xfa/fwl/cfwl_messagekillfocus.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_messagemousewheel.h"
 #include "xfa/fwl/cfwl_messagesetfocus.h"
-#include "xfa/fwl/cfwl_noteloop.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 
-CFWL_NoteDriver::CFWL_NoteDriver()
-    : m_pHover(nullptr),
-      m_pFocus(nullptr),
-      m_pGrab(nullptr),
-      m_pNoteLoop(pdfium::MakeUnique<CFWL_NoteLoop>()) {
-  PushNoteLoop(m_pNoteLoop.get());
-}
+CFWL_NoteDriver::CFWL_NoteDriver() = default;
 
-CFWL_NoteDriver::~CFWL_NoteDriver() {}
+CFWL_NoteDriver::~CFWL_NoteDriver() = default;
 
 void CFWL_NoteDriver::SendEvent(CFWL_Event* pNote) {
   for (const auto& pair : m_eventTargets) {
@@ -65,24 +59,11 @@
     it->second->FlagInvalid();
 }
 
-void CFWL_NoteDriver::PushNoteLoop(CFWL_NoteLoop* pNoteLoop) {
-  m_NoteLoopQueue.push_back(pNoteLoop);
-}
-
-CFWL_NoteLoop* CFWL_NoteDriver::PopNoteLoop() {
-  if (m_NoteLoopQueue.empty())
-    return nullptr;
-
-  CFWL_NoteLoop* p = m_NoteLoopQueue.back();
-  m_NoteLoopQueue.pop_back();
-  return p;
-}
-
 bool CFWL_NoteDriver::SetFocus(CFWL_Widget* pFocus) {
   if (m_pFocus == pFocus)
     return true;
 
-  CFWL_Widget* pPrev = m_pFocus;
+  CFWL_Widget* pPrev = m_pFocus.Get();
   m_pFocus = pFocus;
   if (pPrev) {
     if (IFWL_WidgetDelegate* pDelegate = pPrev->GetDelegate()) {
@@ -91,12 +72,6 @@
     }
   }
   if (pFocus) {
-    CFWL_Widget* pWidget =
-        pFocus->GetOwnerApp()->GetWidgetMgr()->GetSystemFormWidget(pFocus);
-    CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
-    if (pForm)
-      pForm->SetSubFocus(pFocus);
-
     if (IFWL_WidgetDelegate* pDelegate = pFocus->GetDelegate()) {
       CFWL_MessageSetFocus ms(nullptr, pFocus);
       pDelegate->OnProcessMessage(&ms);
@@ -105,17 +80,6 @@
   return true;
 }
 
-void CFWL_NoteDriver::Run() {
-#if _FX_OS_ == _FX_OS_LINUX_ || _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  for (;;) {
-    CFWL_NoteLoop* pTopLoop = GetTopLoop();
-    if (!pTopLoop || !pTopLoop->ContinueModal())
-      break;
-    UnqueueMessageAndProcess(pTopLoop);
-  }
-#endif  // _FX_OS_ == _FX_OS_LINUX_ || _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-}
-
 void CFWL_NoteDriver::NotifyTargetHide(CFWL_Widget* pNoteTarget) {
   if (m_pFocus == pNoteTarget)
     m_pFocus = nullptr;
@@ -134,62 +98,10 @@
     m_pGrab = nullptr;
 
   UnregisterEventTarget(pNoteTarget);
-
-  for (CFWL_Widget* pWidget : m_Forms) {
-    CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
-    if (!pForm)
-      continue;
-
-    CFWL_Widget* pSubFocus = pForm->GetSubFocus();
-    if (!pSubFocus)
-      return;
-
-    if (pSubFocus == pNoteTarget)
-      pForm->SetSubFocus(nullptr);
-  }
-}
-
-void CFWL_NoteDriver::RegisterForm(CFWL_Widget* pForm) {
-  if (!pForm || pdfium::ContainsValue(m_Forms, pForm))
-    return;
-
-  m_Forms.push_back(pForm);
-  if (m_Forms.size() == 1 && !m_NoteLoopQueue.empty() && m_NoteLoopQueue[0])
-    m_NoteLoopQueue[0]->SetMainForm(pForm);
-}
-
-void CFWL_NoteDriver::UnRegisterForm(CFWL_Widget* pForm) {
-  auto iter = std::find(m_Forms.begin(), m_Forms.end(), pForm);
-  if (iter != m_Forms.end())
-    m_Forms.erase(iter);
-}
-
-void CFWL_NoteDriver::QueueMessage(std::unique_ptr<CFWL_Message> pMessage) {
-  m_NoteQueue.push_back(std::move(pMessage));
-}
-
-void CFWL_NoteDriver::UnqueueMessageAndProcess(CFWL_NoteLoop* pNoteLoop) {
-  if (m_NoteQueue.empty())
-    return;
-
-  std::unique_ptr<CFWL_Message> pMessage = std::move(m_NoteQueue.front());
-  m_NoteQueue.pop_front();
-  if (!IsValidMessage(pMessage.get()))
-    return;
-
-  ProcessMessage(std::move(pMessage));
-}
-
-CFWL_NoteLoop* CFWL_NoteDriver::GetTopLoop() const {
-  return !m_NoteLoopQueue.empty() ? m_NoteLoopQueue.back() : nullptr;
 }
 
 void CFWL_NoteDriver::ProcessMessage(std::unique_ptr<CFWL_Message> pMessage) {
-  CFWL_WidgetMgr* pWidgetMgr =
-      pMessage->m_pDstTarget->GetOwnerApp()->GetWidgetMgr();
-  CFWL_Widget* pMessageForm = pWidgetMgr->IsFormDisabled()
-                                  ? pMessage->m_pDstTarget
-                                  : GetMessageForm(pMessage->m_pDstTarget);
+  CFWL_Widget* pMessageForm = pMessage->GetDstTarget();
   if (!pMessageForm)
     return;
 
@@ -231,7 +143,8 @@
     default:
       break;
   }
-  if (IFWL_WidgetDelegate* pDelegate = pMessage->m_pDstTarget->GetDelegate())
+  IFWL_WidgetDelegate* pDelegate = pMessage->GetDstTarget()->GetDelegate();
+  if (pDelegate)
     pDelegate->OnProcessMessage(pMessage);
 
   return true;
@@ -239,68 +152,33 @@
 
 bool CFWL_NoteDriver::DoSetFocus(CFWL_Message* pMessage,
                                  CFWL_Widget* pMessageForm) {
-  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
-  if (pWidgetMgr->IsFormDisabled()) {
-    m_pFocus = pMessage->m_pDstTarget;
-    return true;
-  }
-
-  CFWL_Widget* pWidget = pMessage->m_pDstTarget;
-  if (!pWidget)
-    return false;
-
-  CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
-  CFWL_Widget* pSubFocus = pForm->GetSubFocus();
-  if (pSubFocus && ((pSubFocus->GetStates() & FWL_WGTSTATE_Focused) == 0)) {
-    pMessage->m_pDstTarget = pSubFocus;
-    if (m_pFocus != pMessage->m_pDstTarget) {
-      m_pFocus = pMessage->m_pDstTarget;
-      return true;
-    }
-  }
-  return false;
+  m_pFocus = pMessage->GetDstTarget();
+  return true;
 }
 
 bool CFWL_NoteDriver::DoKillFocus(CFWL_Message* pMessage,
                                   CFWL_Widget* pMessageForm) {
-  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
-  if (pWidgetMgr->IsFormDisabled()) {
-    if (m_pFocus == pMessage->m_pDstTarget)
-      m_pFocus = nullptr;
-    return true;
-  }
-
-  CFWL_Form* pForm = static_cast<CFWL_Form*>(pMessage->m_pDstTarget);
-  if (!pForm)
-    return false;
-
-  CFWL_Widget* pSubFocus = pForm->GetSubFocus();
-  if (pSubFocus && (pSubFocus->GetStates() & FWL_WGTSTATE_Focused)) {
-    pMessage->m_pDstTarget = pSubFocus;
-    if (m_pFocus == pMessage->m_pDstTarget) {
-      m_pFocus = nullptr;
-      return true;
-    }
-  }
-  return false;
+  if (m_pFocus == pMessage->GetDstTarget())
+    m_pFocus = nullptr;
+  return true;
 }
 
 bool CFWL_NoteDriver::DoKey(CFWL_Message* pMessage, CFWL_Widget* pMessageForm) {
   CFWL_MessageKey* pMsg = static_cast<CFWL_MessageKey*>(pMessage);
-#if (_FX_OS_ != _FX_OS_MACOSX_)
+#if !defined(OS_MACOSX)
   if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown &&
-      pMsg->m_dwKeyCode == FWL_VKEY_Tab) {
+      pMsg->m_dwKeyCode == XFA_FWL_VKEY_Tab) {
     CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
-    CFWL_Widget* pForm = GetMessageForm(pMsg->m_pDstTarget);
-    CFWL_Widget* pFocus = m_pFocus;
-    if (m_pFocus && pWidgetMgr->GetSystemFormWidget(m_pFocus) != pForm)
+    CFWL_Widget* pForm = GetMessageForm(pMsg->GetDstTarget());
+    CFWL_Widget* pFocus = m_pFocus.Get();
+    if (m_pFocus && pWidgetMgr->GetSystemFormWidget(m_pFocus.Get()) != pForm)
       pFocus = nullptr;
 
-    bool bFind = false;
-    CFWL_Widget* pNextTabStop = pWidgetMgr->NextTab(pForm, pFocus, bFind);
-    if (!pNextTabStop) {
-      bFind = false;
-      pNextTabStop = pWidgetMgr->NextTab(pForm, nullptr, bFind);
+    CFWL_Widget* pNextTabStop = nullptr;
+    if (pForm) {
+      pNextTabStop = CFWL_WidgetMgr::NextTab(pForm, pFocus);
+      if (!pNextTabStop)
+        pNextTabStop = CFWL_WidgetMgr::NextTab(pForm, nullptr);
     }
     if (pNextTabStop == pFocus)
       return true;
@@ -310,20 +188,21 @@
   }
 #endif
 
-  if (!m_pFocus) {
-    if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown &&
-        pMsg->m_dwKeyCode == FWL_VKEY_Return) {
-      CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
-      CFWL_Widget* defButton = pWidgetMgr->GetDefaultButton(pMessageForm);
-      if (defButton) {
-        pMsg->m_pDstTarget = defButton;
-        return true;
-      }
-    }
-    return false;
+  if (m_pFocus) {
+    pMsg->SetDstTarget(m_pFocus.Get());
+    return true;
   }
-  pMsg->m_pDstTarget = m_pFocus;
-  return true;
+
+  if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown &&
+      pMsg->m_dwKeyCode == XFA_FWL_VKEY_Return) {
+    CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
+    CFWL_Widget* pDefButton = pWidgetMgr->GetDefaultButton(pMessageForm);
+    if (pDefButton) {
+      pMsg->SetDstTarget(pDefButton);
+      return true;
+    }
+  }
+  return false;
 }
 
 bool CFWL_NoteDriver::DoMouse(CFWL_Message* pMessage,
@@ -332,39 +211,34 @@
   if (pMsg->m_dwCmd == FWL_MouseCommand::Leave ||
       pMsg->m_dwCmd == FWL_MouseCommand::Hover ||
       pMsg->m_dwCmd == FWL_MouseCommand::Enter) {
-    return !!pMsg->m_pDstTarget;
+    return !!pMsg->GetDstTarget();
   }
-  if (pMsg->m_pDstTarget != pMessageForm)
-    pMsg->m_pos = pMsg->m_pDstTarget->TransformTo(pMessageForm, pMsg->m_pos);
+  if (pMsg->GetDstTarget() != pMessageForm)
+    pMsg->m_pos = pMsg->GetDstTarget()->TransformTo(pMessageForm, pMsg->m_pos);
   if (!DoMouseEx(pMsg, pMessageForm))
-    pMsg->m_pDstTarget = pMessageForm;
+    pMsg->SetDstTarget(pMessageForm);
   return true;
 }
 
 bool CFWL_NoteDriver::DoWheel(CFWL_Message* pMessage,
                               CFWL_Widget* pMessageForm) {
   CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
-  if (!pWidgetMgr)
-    return false;
-
   CFWL_MessageMouseWheel* pMsg = static_cast<CFWL_MessageMouseWheel*>(pMessage);
   CFWL_Widget* pDst = pWidgetMgr->GetWidgetAtPoint(pMessageForm, pMsg->m_pos);
   if (!pDst)
     return false;
 
   pMsg->m_pos = pMessageForm->TransformTo(pDst, pMsg->m_pos);
-  pMsg->m_pDstTarget = pDst;
+  pMsg->SetDstTarget(pDst);
   return true;
 }
 
 bool CFWL_NoteDriver::DoMouseEx(CFWL_Message* pMessage,
                                 CFWL_Widget* pMessageForm) {
   CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
-  if (!pWidgetMgr)
-    return false;
   CFWL_Widget* pTarget = nullptr;
   if (m_pGrab)
-    pTarget = m_pGrab;
+    pTarget = m_pGrab.Get();
 
   CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
   if (!pTarget)
@@ -374,21 +248,20 @@
   if (pTarget && pMessageForm != pTarget)
     pMsg->m_pos = pMessageForm->TransformTo(pTarget, pMsg->m_pos);
 
-  pMsg->m_pDstTarget = pTarget;
+  pMsg->SetDstTarget(pTarget);
   return true;
 }
 
 void CFWL_NoteDriver::MouseSecondary(CFWL_Message* pMessage) {
-  CFWL_Widget* pTarget = pMessage->m_pDstTarget;
+  CFWL_Widget* pTarget = pMessage->GetDstTarget();
   if (pTarget == m_pHover)
     return;
 
   CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
   if (m_pHover) {
-    CFWL_MessageMouse msLeave(nullptr, m_pHover);
-    msLeave.m_pos = pTarget->TransformTo(m_pHover, pMsg->m_pos);
-    msLeave.m_dwFlags = 0;
-    msLeave.m_dwCmd = FWL_MouseCommand::Leave;
+    CFWL_MessageMouse msLeave(
+        m_pHover.Get(), FWL_MouseCommand::Leave, 0,
+        pTarget->TransformTo(m_pHover.Get(), pMsg->m_pos));
     DispatchMessage(&msLeave, nullptr);
   }
   if (pTarget->GetClassID() == FWL_Type::Form) {
@@ -397,45 +270,16 @@
   }
   m_pHover = pTarget;
 
-  CFWL_MessageMouse msHover(nullptr, pTarget);
-  msHover.m_pos = pMsg->m_pos;
-  msHover.m_dwFlags = 0;
-  msHover.m_dwCmd = FWL_MouseCommand::Hover;
+  CFWL_MessageMouse msHover(pTarget, FWL_MouseCommand::Hover, 0, pMsg->m_pos);
   DispatchMessage(&msHover, nullptr);
 }
 
-bool CFWL_NoteDriver::IsValidMessage(CFWL_Message* pMessage) {
-  for (CFWL_NoteLoop* pNoteLoop : m_NoteLoopQueue) {
-    CFWL_Widget* pForm = pNoteLoop->GetForm();
-    if (pForm && pForm == pMessage->m_pDstTarget)
-      return true;
-  }
-  for (CFWL_Widget* pWidget : m_Forms) {
-    CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
-    if (pForm == pMessage->m_pDstTarget)
-      return true;
-  }
-  return false;
-}
-
 CFWL_Widget* CFWL_NoteDriver::GetMessageForm(CFWL_Widget* pDstTarget) {
-  if (m_NoteLoopQueue.empty())
+  if (!pDstTarget)
     return nullptr;
 
-  CFWL_Widget* pMessageForm = nullptr;
-  if (m_NoteLoopQueue.size() > 1)
-    pMessageForm = m_NoteLoopQueue.back()->GetForm();
-  else if (!pdfium::ContainsValue(m_Forms, pDstTarget))
-    pMessageForm = pDstTarget;
-
-  if (!pMessageForm && pDstTarget) {
-    CFWL_WidgetMgr* pWidgetMgr = pDstTarget->GetOwnerApp()->GetWidgetMgr();
-    if (!pWidgetMgr)
-      return nullptr;
-
-    pMessageForm = pWidgetMgr->GetSystemFormWidget(pDstTarget);
-  }
-  return pMessageForm;
+  CFWL_WidgetMgr* pWidgetMgr = pDstTarget->GetOwnerApp()->GetWidgetMgr();
+  return pWidgetMgr->GetSystemFormWidget(pDstTarget);
 }
 
 void CFWL_NoteDriver::ClearEventTargets() {
diff --git a/xfa/fwl/cfwl_notedriver.h b/xfa/fwl/cfwl_notedriver.h
index 0db482e..1de761b 100644
--- a/xfa/fwl/cfwl_notedriver.h
+++ b/xfa/fwl/cfwl_notedriver.h
@@ -7,17 +7,16 @@
 #ifndef XFA_FWL_CFWL_NOTEDRIVER_H_
 #define XFA_FWL_CFWL_NOTEDRIVER_H_
 
-#include <deque>
 #include <map>
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fxgraphics/cxfa_graphics.h"
 
 class CFWL_EventTarget;
-class CFWL_NoteLoop;
 class CFWL_TargetImp;
 class CFWL_Widget;
 
@@ -32,27 +31,15 @@
   void UnregisterEventTarget(CFWL_Widget* pListener);
   void ClearEventTargets();
 
-  CFWL_NoteLoop* GetTopLoop() const;
-  void PushNoteLoop(CFWL_NoteLoop* pNoteLoop);
-  CFWL_NoteLoop* PopNoteLoop();
-
-  CFWL_Widget* GetFocus() const { return m_pFocus; }
+  CFWL_Widget* GetFocus() const { return m_pFocus.Get(); }
   bool SetFocus(CFWL_Widget* pFocus);
   void SetGrab(CFWL_Widget* pGrab, bool bSet) {
     m_pGrab = bSet ? pGrab : nullptr;
   }
 
-  void Run();
-
   void NotifyTargetHide(CFWL_Widget* pNoteTarget);
   void NotifyTargetDestroy(CFWL_Widget* pNoteTarget);
-
-  void RegisterForm(CFWL_Widget* pForm);
-  void UnRegisterForm(CFWL_Widget* pForm);
-
   void ProcessMessage(std::unique_ptr<CFWL_Message> pMessage);
-  void QueueMessage(std::unique_ptr<CFWL_Message> pMessage);
-  void UnqueueMessageAndProcess(CFWL_NoteLoop* pNoteLoop);
 
  private:
   bool DispatchMessage(CFWL_Message* pMessage, CFWL_Widget* pMessageForm);
@@ -63,17 +50,12 @@
   bool DoWheel(CFWL_Message* pMsg, CFWL_Widget* pMessageForm);
   bool DoMouseEx(CFWL_Message* pMsg, CFWL_Widget* pMessageForm);
   void MouseSecondary(CFWL_Message* pMsg);
-  bool IsValidMessage(CFWL_Message* pMessage);
   CFWL_Widget* GetMessageForm(CFWL_Widget* pDstTarget);
 
-  std::vector<CFWL_Widget*> m_Forms;
-  std::deque<std::unique_ptr<CFWL_Message>> m_NoteQueue;
-  std::vector<CFWL_NoteLoop*> m_NoteLoopQueue;
   std::map<uint32_t, std::unique_ptr<CFWL_EventTarget>> m_eventTargets;
-  CFWL_Widget* m_pHover;
-  CFWL_Widget* m_pFocus;
-  CFWL_Widget* m_pGrab;
-  std::unique_ptr<CFWL_NoteLoop> m_pNoteLoop;
+  UnownedPtr<CFWL_Widget> m_pHover;
+  UnownedPtr<CFWL_Widget> m_pFocus;
+  UnownedPtr<CFWL_Widget> m_pGrab;
 };
 
 #endif  // XFA_FWL_CFWL_NOTEDRIVER_H_
diff --git a/xfa/fwl/cfwl_noteloop.cpp b/xfa/fwl/cfwl_noteloop.cpp
deleted file mode 100644
index ff35a1a..0000000
--- a/xfa/fwl/cfwl_noteloop.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_noteloop.h"
-
-CFWL_NoteLoop::CFWL_NoteLoop() : m_bContinueModal(true) {}
diff --git a/xfa/fwl/cfwl_noteloop.h b/xfa/fwl/cfwl_noteloop.h
deleted file mode 100644
index 57e1a14..0000000
--- a/xfa/fwl/cfwl_noteloop.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_NOTELOOP_H_
-#define XFA_FWL_CFWL_NOTELOOP_H_
-
-class CFWL_Widget;
-
-class CFWL_NoteLoop {
- public:
-  CFWL_NoteLoop();
-  ~CFWL_NoteLoop() {}
-
-  CFWL_Widget* GetForm() const { return m_pForm; }
-  bool ContinueModal() const { return m_bContinueModal; }
-  void EndModalLoop() { m_bContinueModal = false; }
-  void SetMainForm(CFWL_Widget* pForm) { m_pForm = pForm; }
-
- private:
-  CFWL_Widget* m_pForm;
-  bool m_bContinueModal;
-};
-
-#endif  // XFA_FWL_CFWL_NOTELOOP_H_
diff --git a/xfa/fwl/cfwl_picturebox.cpp b/xfa/fwl/cfwl_picturebox.cpp
index 637de94..6e90765 100644
--- a/xfa/fwl/cfwl_picturebox.cpp
+++ b/xfa/fwl/cfwl_picturebox.cpp
@@ -12,9 +12,6 @@
 
 CFWL_PictureBox::CFWL_PictureBox(const CFWL_App* app)
     : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {
-  m_rtClient.Reset();
-  m_rtImage.Reset();
-  m_matrix.SetIdentity();
 }
 
 CFWL_PictureBox::~CFWL_PictureBox() {}
diff --git a/xfa/fwl/cfwl_picturebox.h b/xfa/fwl/cfwl_picturebox.h
index 363db8a..11689d2 100644
--- a/xfa/fwl/cfwl_picturebox.h
+++ b/xfa/fwl/cfwl_picturebox.h
@@ -7,15 +7,13 @@
 #ifndef XFA_FWL_CFWL_PICTUREBOX_H_
 #define XFA_FWL_CFWL_PICTUREBOX_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
 
 class CFX_DIBitmap;
 class CFWL_Widget;
 
-class CFWL_PictureBox : public CFWL_Widget {
+class CFWL_PictureBox final : public CFWL_Widget {
  public:
   explicit CFWL_PictureBox(const CFWL_App* pApp);
   ~CFWL_PictureBox() override;
diff --git a/xfa/fwl/cfwl_pushbutton.cpp b/xfa/fwl/cfwl_pushbutton.cpp
index a912108..7806399 100644
--- a/xfa/fwl/cfwl_pushbutton.cpp
+++ b/xfa/fwl/cfwl_pushbutton.cpp
@@ -18,11 +18,11 @@
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themetext.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 CFWL_PushButton::CFWL_PushButton(const CFWL_App* app)
-    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr),
-      m_bBtnDown(false) {}
+    : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {}
 
 CFWL_PushButton::~CFWL_PushButton() {}
 
@@ -52,14 +52,14 @@
                                  const CFX_Matrix& matrix) {
   if (!pGraphics)
     return;
-  if (!m_pProperties->m_pThemeProvider)
+
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
+  if (!pTheme)
     return;
 
-  if (HasBorder()) {
-    DrawBorder(pGraphics, CFWL_Part::Border, m_pProperties->m_pThemeProvider,
-               matrix);
-  }
-  DrawBkground(pGraphics, m_pProperties->m_pThemeProvider, &matrix);
+  if (HasBorder())
+    DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
+  DrawBkground(pGraphics, pTheme, &matrix);
 }
 
 void CFWL_PushButton::DrawBkground(CXFA_Graphics* pGraphics,
@@ -74,8 +74,8 @@
     param.m_matrix.Concat(*pMatrix);
   param.m_rtPart = m_rtClient;
   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
-    param.m_pData = &m_rtCaption;
-  pTheme->DrawBackground(&param);
+    param.m_pRtData = &m_rtCaption;
+  pTheme->DrawBackground(param);
 }
 
 uint32_t CFWL_PushButton::GetPartStates() {
@@ -133,7 +133,9 @@
     default:
       break;
   }
-  CFWL_Widget::OnProcessMessage(pMessage);
+  // Dst target could be |this|, continue only if not destroyed by above.
+  if (pMessage->GetDstTarget())
+    CFWL_Widget::OnProcessMessage(pMessage);
 }
 
 void CFWL_PushButton::OnDrawWidget(CXFA_Graphics* pGraphics,
@@ -151,9 +153,6 @@
 }
 
 void CFWL_PushButton::OnLButtonDown(CFWL_MessageMouse* pMsg) {
-  if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
-    SetFocus(true);
-
   m_bBtnDown = true;
   m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
   m_pProperties->m_dwStates |= FWL_STATE_PSB_Pressed;
@@ -218,12 +217,14 @@
 }
 
 void CFWL_PushButton::OnKeyDown(CFWL_MessageKey* pMsg) {
-  if (pMsg->m_dwKeyCode != FWL_VKEY_Return)
+  if (pMsg->m_dwKeyCode != XFA_FWL_VKEY_Return)
     return;
 
   CFWL_EventMouse wmMouse(this);
   wmMouse.m_dwCmd = FWL_MouseCommand::LeftButtonUp;
   DispatchEvent(&wmMouse);
+  if (!wmMouse.GetSrcTarget())
+    return;
 
   CFWL_Event wmClick(CFWL_Event::Type::Click, this);
   DispatchEvent(&wmClick);
diff --git a/xfa/fwl/cfwl_pushbutton.h b/xfa/fwl/cfwl_pushbutton.h
index 5c509fb..925dbb6 100644
--- a/xfa/fwl/cfwl_pushbutton.h
+++ b/xfa/fwl/cfwl_pushbutton.h
@@ -7,8 +7,6 @@
 #ifndef XFA_FWL_CFWL_PUSHBUTTON_H_
 #define XFA_FWL_CFWL_PUSHBUTTON_H_
 
-#include <memory>
-
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
 
@@ -19,7 +17,7 @@
 class CFX_DIBitmap;
 class CFWL_Widget;
 
-class CFWL_PushButton : public CFWL_Widget {
+class CFWL_PushButton final : public CFWL_Widget {
  public:
   explicit CFWL_PushButton(const CFWL_App*);
   ~CFWL_PushButton() override;
@@ -46,9 +44,9 @@
   void OnMouseLeave(CFWL_MessageMouse* pMsg);
   void OnKeyDown(CFWL_MessageKey* pMsg);
 
+  bool m_bBtnDown = false;
   CFX_RectF m_rtClient;
   CFX_RectF m_rtCaption;
-  bool m_bBtnDown;
 };
 
 #endif  // XFA_FWL_CFWL_PUSHBUTTON_H_
diff --git a/xfa/fwl/cfwl_scrollbar.cpp b/xfa/fwl/cfwl_scrollbar.cpp
index f723124..acc144f 100644
--- a/xfa/fwl/cfwl_scrollbar.cpp
+++ b/xfa/fwl/cfwl_scrollbar.cpp
@@ -12,12 +12,12 @@
 
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
+#include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_messagemousewheel.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themepart.h"
-#include "xfa/fwl/cfwl_timerinfo.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
 
 #define FWL_SCROLLBAR_Elapse 500
@@ -32,34 +32,9 @@
     const CFWL_App* app,
     std::unique_ptr<CFWL_WidgetProperties> properties,
     CFWL_Widget* pOuter)
-    : CFWL_Widget(app, std::move(properties), pOuter),
-      m_pTimerInfo(nullptr),
-      m_fRangeMin(0),
-      m_fRangeMax(-1),
-      m_fPageSize(0),
-      m_fStepSize(0),
-      m_fPos(0),
-      m_fTrackPos(0),
-      m_iMinButtonState(CFWL_PartState_Normal),
-      m_iMaxButtonState(CFWL_PartState_Normal),
-      m_iThumbButtonState(CFWL_PartState_Normal),
-      m_iMinTrackState(CFWL_PartState_Normal),
-      m_iMaxTrackState(CFWL_PartState_Normal),
-      m_fLastTrackPos(0),
-      m_iMouseWheel(0),
-      m_bMouseDown(false),
-      m_fButtonLen(0),
-      m_bMinSize(false),
-      m_Timer(this) {
-  m_rtClient.Reset();
-  m_rtThumb.Reset();
-  m_rtMinBtn.Reset();
-  m_rtMaxBtn.Reset();
-  m_rtMinTrack.Reset();
-  m_rtMaxTrack.Reset();
-}
+    : CFWL_Widget(app, std::move(properties), pOuter) {}
 
-CFWL_ScrollBar::~CFWL_ScrollBar() {}
+CFWL_ScrollBar::~CFWL_ScrollBar() = default;
 
 FWL_Type CFWL_ScrollBar::GetClassID() const {
   return FWL_Type::ScrollBar;
@@ -81,7 +56,7 @@
   if (!m_pProperties->m_pThemeProvider)
     return;
 
-  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
+  IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
   if (HasBorder())
     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
   DrawTrack(pGraphics, pTheme, true, &matrix);
@@ -117,7 +92,7 @@
   param.m_pGraphics = pGraphics;
   param.m_matrix.Concat(*pMatrix);
   param.m_rtPart = bLower ? m_rtMinTrack : m_rtMaxTrack;
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 void CFWL_ScrollBar::DrawArrowBtn(CXFA_Graphics* pGraphics,
@@ -134,7 +109,7 @@
   param.m_matrix.Concat(*pMatrix);
   param.m_rtPart = bMinBtn ? m_rtMinBtn : m_rtMaxBtn;
   if (param.m_rtPart.height > 0 && param.m_rtPart.width > 0)
-    pTheme->DrawBackground(&param);
+    pTheme->DrawBackground(param);
 }
 
 void CFWL_ScrollBar::DrawThumb(CXFA_Graphics* pGraphics,
@@ -149,7 +124,7 @@
   param.m_pGraphics = pGraphics;
   param.m_matrix.Concat(*pMatrix);
   param.m_rtPart = m_rtThumb;
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 void CFWL_ScrollBar::Layout() {
@@ -380,12 +355,15 @@
   else
     DoMouseDown(4, m_rtMaxTrack, m_iMaxTrackState, point);
 
-  if (!SendEvent())
-    m_pTimerInfo = m_Timer.StartTimer(FWL_SCROLLBAR_Elapse, true);
+  if (!SendEvent()) {
+    m_pTimer = pdfium::MakeUnique<CFX_Timer>(
+        GetOwnerApp()->GetAdapterNative()->GetTimerHandler(), this,
+        FWL_SCROLLBAR_Elapse);
+  }
 }
 
 void CFWL_ScrollBar::OnLButtonUp(const CFX_PointF& point) {
-  m_pTimerInfo->StopTimer();
+  m_pTimer.reset();
   m_bMouseDown = false;
   DoMouseUp(0, m_rtMinBtn, m_iMinButtonState, point);
   DoMouseUp(1, m_rtThumb, m_iThumbButtonState, point);
@@ -482,13 +460,10 @@
   RepaintRect(rtItem);
 }
 
-CFWL_ScrollBar::Timer::Timer(CFWL_ScrollBar* pToolTip) : CFWL_Timer(pToolTip) {}
-
-void CFWL_ScrollBar::Timer::Run(CFWL_TimerInfo* pTimerInfo) {
-  CFWL_ScrollBar* pButton = static_cast<CFWL_ScrollBar*>(m_pWidget.Get());
-  if (pButton->m_pTimerInfo)
-    pButton->m_pTimerInfo->StopTimer();
-
-  if (!pButton->SendEvent())
-    pButton->m_pTimerInfo = StartTimer(0, true);
+void CFWL_ScrollBar::OnTimerFired() {
+  m_pTimer.reset();
+  if (!SendEvent()) {
+    m_pTimer = pdfium::MakeUnique<CFX_Timer>(
+        GetOwnerApp()->GetAdapterNative()->GetTimerHandler(), this, 0);
+  }
 }
diff --git a/xfa/fwl/cfwl_scrollbar.h b/xfa/fwl/cfwl_scrollbar.h
index b2523a6..c98479d 100644
--- a/xfa/fwl/cfwl_scrollbar.h
+++ b/xfa/fwl/cfwl_scrollbar.h
@@ -9,9 +9,10 @@
 
 #include <memory>
 
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_eventscroll.h"
-#include "xfa/fwl/cfwl_timer.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fwl/cfwl_widgetproperties.h"
 
@@ -20,14 +21,15 @@
 #define FWL_STYLEEXT_SCB_Horz (0L << 0)
 #define FWL_STYLEEXT_SCB_Vert (1L << 0)
 
-class CFWL_ScrollBar : public CFWL_Widget {
+class CFWL_ScrollBar final : public CFWL_Widget,
+                             public CFX_Timer::CallbackIface {
  public:
   CFWL_ScrollBar(const CFWL_App* app,
                  std::unique_ptr<CFWL_WidgetProperties> properties,
                  CFWL_Widget* pOuter);
   ~CFWL_ScrollBar() override;
 
-  // CFWL_Widget
+  // CFWL_Widget:
   FWL_Type GetClassID() const override;
   void Update() override;
   void DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override;
@@ -35,6 +37,9 @@
   void OnDrawWidget(CXFA_Graphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
+  // CFX_Timer::CallbackIface:
+  void OnTimerFired() override;
+
   void GetRange(float* fMin, float* fMax) const {
     ASSERT(fMin);
     ASSERT(fMax);
@@ -54,15 +59,6 @@
   void SetTrackPos(float fTrackPos);
 
  private:
-  class Timer : public CFWL_Timer {
-   public:
-    explicit Timer(CFWL_ScrollBar* pToolTip);
-    ~Timer() override {}
-
-    void Run(CFWL_TimerInfo* pTimerInfo) override;
-  };
-  friend class CFWL_ScrollBar::Timer;
-
   bool IsVertical() const {
     return !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_SCB_Vert);
   }
@@ -109,31 +105,30 @@
   void DoMouseLeave(int32_t iItem, const CFX_RectF& rtItem, int32_t& iState);
   void DoMouseHover(int32_t iItem, const CFX_RectF& rtItem, int32_t& iState);
 
-  CFWL_TimerInfo* m_pTimerInfo;
-  float m_fRangeMin;
-  float m_fRangeMax;
-  float m_fPageSize;
-  float m_fStepSize;
-  float m_fPos;
-  float m_fTrackPos;
-  int32_t m_iMinButtonState;
-  int32_t m_iMaxButtonState;
-  int32_t m_iThumbButtonState;
-  int32_t m_iMinTrackState;
-  int32_t m_iMaxTrackState;
-  float m_fLastTrackPos;
+  float m_fRangeMin = 0.0f;
+  float m_fRangeMax = -1.0f;
+  float m_fPageSize = 0.0f;
+  float m_fStepSize = 0.0f;
+  float m_fPos = 0.0f;
+  float m_fTrackPos = 0.0f;
+  int32_t m_iMinButtonState = CFWL_PartState_Normal;
+  int32_t m_iMaxButtonState = CFWL_PartState_Normal;
+  int32_t m_iThumbButtonState = CFWL_PartState_Normal;
+  int32_t m_iMinTrackState = CFWL_PartState_Normal;
+  int32_t m_iMaxTrackState = CFWL_PartState_Normal;
+  float m_fLastTrackPos = 0.0f;
   CFX_PointF m_cpTrackPoint;
-  int32_t m_iMouseWheel;
-  bool m_bMouseDown;
-  float m_fButtonLen;
-  bool m_bMinSize;
+  int32_t m_iMouseWheel = 0;
+  float m_fButtonLen = 0.0f;
+  bool m_bMouseDown = false;
+  bool m_bMinSize = false;
   CFX_RectF m_rtClient;
   CFX_RectF m_rtThumb;
   CFX_RectF m_rtMinBtn;
   CFX_RectF m_rtMaxBtn;
   CFX_RectF m_rtMinTrack;
   CFX_RectF m_rtMaxTrack;
-  CFWL_ScrollBar::Timer m_Timer;
+  std::unique_ptr<CFX_Timer> m_pTimer;
 };
 
 #endif  // XFA_FWL_CFWL_SCROLLBAR_H_
diff --git a/xfa/fwl/cfwl_themebackground.h b/xfa/fwl/cfwl_themebackground.h
index 85435aa..46df73c 100644
--- a/xfa/fwl/cfwl_themebackground.h
+++ b/xfa/fwl/cfwl_themebackground.h
@@ -7,23 +7,23 @@
 #ifndef XFA_FWL_CFWL_THEMEBACKGROUND_H_
 #define XFA_FWL_CFWL_THEMEBACKGROUND_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_themepart.h"
 
 class CXFA_Graphics;
 class CXFA_GEPath;
 
-class CFWL_ThemeBackground : public CFWL_ThemePart {
+class CFWL_ThemeBackground final : public CFWL_ThemePart {
  public:
   CFWL_ThemeBackground();
   ~CFWL_ThemeBackground();
 
-  CXFA_Graphics* m_pGraphics;
-  CXFA_GEPath* m_pPath;
+  UnownedPtr<CXFA_Graphics> m_pGraphics;
+  UnownedPtr<CXFA_GEPath> m_pPath;
 };
 
-inline CFWL_ThemeBackground::CFWL_ThemeBackground()
-    : m_pGraphics(nullptr), m_pPath(nullptr) {}
+inline CFWL_ThemeBackground::CFWL_ThemeBackground() = default;
 
-inline CFWL_ThemeBackground::~CFWL_ThemeBackground() {}
+inline CFWL_ThemeBackground::~CFWL_ThemeBackground() = default;
 
 #endif  // XFA_FWL_CFWL_THEMEBACKGROUND_H_
diff --git a/xfa/fwl/cfwl_themepart.cpp b/xfa/fwl/cfwl_themepart.cpp
index 25592f3..831e494 100644
--- a/xfa/fwl/cfwl_themepart.cpp
+++ b/xfa/fwl/cfwl_themepart.cpp
@@ -12,7 +12,4 @@
       m_dwStates(CFWL_PartState_Normal),
       m_bMaximize(false),
       m_bStaticBackground(false),
-      m_pData(nullptr) {
-  m_rtPart.Reset();
-  m_matrix.SetIdentity();
-}
+      m_pRtData(nullptr) {}
diff --git a/xfa/fwl/cfwl_themepart.h b/xfa/fwl/cfwl_themepart.h
index 1bb87e9..46be205 100644
--- a/xfa/fwl/cfwl_themepart.h
+++ b/xfa/fwl/cfwl_themepart.h
@@ -40,7 +40,6 @@
   MaximizeBox,
   NarrowCaption,
   RBtn,
-  StretchHandler,
   Thumb,
   ThumbBackArrow,
   ThumbForeArrow,
@@ -88,7 +87,7 @@
   uint32_t m_dwStates;
   bool m_bMaximize;
   bool m_bStaticBackground;
-  void* m_pData;
+  CFX_RectF* m_pRtData;
 };
 
 #endif  // XFA_FWL_CFWL_THEMEPART_H_
diff --git a/xfa/fwl/cfwl_themetext.h b/xfa/fwl/cfwl_themetext.h
index 91a3f38..f0cfaa7 100644
--- a/xfa/fwl/cfwl_themetext.h
+++ b/xfa/fwl/cfwl_themetext.h
@@ -11,14 +11,14 @@
 #include "core/fxcrt/fx_system.h"
 #include "xfa/fwl/cfwl_themepart.h"
 
-class CFWL_ThemeText : public CFWL_ThemePart {
+class CFWL_ThemeText final : public CFWL_ThemePart {
  public:
-  CFWL_ThemeText() : m_pGraphics(nullptr) {}
+  CFWL_ThemeText() = default;
 
+  FDE_TextAlignment m_iTTOAlign = FDE_TextAlignment::kTopLeft;
+  CXFA_Graphics* m_pGraphics = nullptr;
   WideString m_wsText;
   FDE_TextStyle m_dwTTOStyles;
-  FDE_TextAlignment m_iTTOAlign;
-  CXFA_Graphics* m_pGraphics;
 };
 
 #endif  // XFA_FWL_CFWL_THEMETEXT_H_
diff --git a/xfa/fwl/cfwl_timer.cpp b/xfa/fwl/cfwl_timer.cpp
deleted file mode 100644
index 2734e49..0000000
--- a/xfa/fwl/cfwl_timer.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_timer.h"
-
-#include "xfa/fwl/cfwl_app.h"
-#include "xfa/fwl/cfwl_timerinfo.h"
-#include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fwl/ifwl_adaptertimermgr.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
-
-CFWL_Timer::CFWL_Timer(CFWL_Widget* parent) : m_pWidget(parent) {}
-
-CFWL_Timer::~CFWL_Timer() {}
-
-CFWL_TimerInfo* CFWL_Timer::StartTimer(uint32_t dwElapse, bool bImmediately) {
-  const CFWL_App* pApp = m_pWidget->GetOwnerApp();
-  if (!pApp)
-    return nullptr;
-
-  CXFA_FFApp* pAdapterNative = pApp->GetAdapterNative();
-  if (!pAdapterNative)
-    return nullptr;
-
-  IFWL_AdapterTimerMgr* pAdapterTimerMgr = pAdapterNative->GetTimerMgr();
-  if (!pAdapterTimerMgr)
-    return nullptr;
-
-  CFWL_TimerInfo* pTimerInfo = nullptr;
-  pAdapterTimerMgr->Start(this, dwElapse, bImmediately, &pTimerInfo);
-  return pTimerInfo;
-}
diff --git a/xfa/fwl/cfwl_timer.h b/xfa/fwl/cfwl_timer.h
deleted file mode 100644
index da1b443..0000000
--- a/xfa/fwl/cfwl_timer.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFWL_TIMER_H_
-#define XFA_FWL_CFWL_TIMER_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CFWL_TimerInfo;
-class CFWL_Widget;
-
-class CFWL_Timer {
- public:
-  explicit CFWL_Timer(CFWL_Widget* parent);
-  virtual ~CFWL_Timer();
-
-  virtual void Run(CFWL_TimerInfo* hTimer) = 0;
-  CFWL_TimerInfo* StartTimer(uint32_t dwElapse, bool bImmediately);
-
- protected:
-  UnownedPtr<CFWL_Widget> m_pWidget;
-};
-
-#endif  // XFA_FWL_CFWL_TIMER_H_
diff --git a/xfa/fwl/cfwl_timerinfo.cpp b/xfa/fwl/cfwl_timerinfo.cpp
deleted file mode 100644
index ee4746a..0000000
--- a/xfa/fwl/cfwl_timerinfo.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfwl_timerinfo.h"
-
-#include "xfa/fwl/ifwl_adaptertimermgr.h"
-
-CFWL_TimerInfo::CFWL_TimerInfo(IFWL_AdapterTimerMgr* mgr) : m_pMgr(mgr) {
-  ASSERT(mgr);
-}
-
-CFWL_TimerInfo::~CFWL_TimerInfo() {}
-
-void CFWL_TimerInfo::StopTimer() {
-  m_pMgr->Stop(this);
-}
diff --git a/xfa/fwl/cfwl_timerinfo.h b/xfa/fwl/cfwl_timerinfo.h
deleted file mode 100644
index c58bcff..0000000
--- a/xfa/fwl/cfwl_timerinfo.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 XFA_FWL_CFWL_TIMERINFO_H_
-#define XFA_FWL_CFWL_TIMERINFO_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class IFWL_AdapterTimerMgr;
-
-class CFWL_TimerInfo {
- public:
-  explicit CFWL_TimerInfo(IFWL_AdapterTimerMgr* mgr);
-  virtual ~CFWL_TimerInfo();
-
-  void StopTimer();
-
- private:
-  UnownedPtr<IFWL_AdapterTimerMgr> m_pMgr;
-};
-
-#endif  // XFA_FWL_CFWL_TIMERINFO_H_
diff --git a/xfa/fwl/cfwl_widget.cpp b/xfa/fwl/cfwl_widget.cpp
index b9b04cf..39b706b 100644
--- a/xfa/fwl/cfwl_widget.cpp
+++ b/xfa/fwl/cfwl_widget.cpp
@@ -16,7 +16,6 @@
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_eventmouse.h"
-#include "xfa/fwl/cfwl_form.h"
 #include "xfa/fwl/cfwl_messagekey.h"
 #include "xfa/fwl/cfwl_messagekillfocus.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
@@ -28,9 +27,7 @@
 #include "xfa/fwl/cfwl_themetext.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
 #include "xfa/fwl/ifwl_themeprovider.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
 
-#define FWL_STYLEEXT_MNU_Vert (1L << 0)
 #define FWL_WGT_CalcHeight 2048
 #define FWL_WGT_CalcWidth 2048
 #define FWL_WGT_CalcMultiLineDefWidth 120.0f
@@ -41,29 +38,19 @@
     : m_pOwnerApp(app),
       m_pWidgetMgr(app->GetWidgetMgr()),
       m_pProperties(std::move(properties)),
-      m_pOuter(pOuter),
-      m_iLock(0),
-      m_pLayoutItem(nullptr),
-      m_nEventKey(0),
-      m_pDelegate(nullptr) {
+      m_pOuter(pOuter) {
   ASSERT(m_pWidgetMgr);
-
-  CFWL_Widget* pParent = m_pProperties->m_pParent;
-  m_pWidgetMgr->InsertWidget(pParent, this);
-  if (IsChild())
-    return;
-
-  CFWL_Widget* pOwner = m_pProperties->m_pOwner;
-  if (pOwner)
-    m_pWidgetMgr->SetOwner(pOwner, this);
+  ASSERT(m_pProperties);
+  m_pWidgetMgr->InsertWidget(m_pProperties->m_pParent, this);
 }
 
 CFWL_Widget::~CFWL_Widget() {
+  CHECK(!IsLocked());  // Prefer hard stop to UaF.
   NotifyDriver();
   m_pWidgetMgr->RemoveWidget(this);
 }
 
-bool CFWL_Widget::IsInstance(const WideStringView& wsClass) const {
+bool CFWL_Widget::IsForm() const {
   return false;
 }
 
@@ -76,10 +63,11 @@
 }
 
 void CFWL_Widget::InflateWidgetRect(CFX_RectF& rect) {
-  if (HasBorder()) {
-    float fBorder = GetBorderSize(true);
-    rect.Inflate(fBorder, fBorder);
-  }
+  if (!HasBorder())
+    return;
+
+  float fBorder = GetCXBorderSize();
+  rect.Inflate(fBorder, fBorder);
 }
 
 void CFWL_Widget::SetWidgetRect(const CFX_RectF& rect) {
@@ -95,10 +83,6 @@
   m_pWidgetMgr->SetParent(pParent, this);
 }
 
-uint32_t CFWL_Widget::GetStyles() const {
-  return m_pProperties->m_dwStyles;
-}
-
 void CFWL_Widget::ModifyStyles(uint32_t dwStylesAdded,
                                uint32_t dwStylesRemoved) {
   m_pProperties->m_dwStyles =
@@ -131,13 +115,13 @@
 
 void CFWL_Widget::SetStates(uint32_t dwStates) {
   m_pProperties->m_dwStates |= dwStates;
-  if (!(dwStates & FWL_WGTSTATE_Invisible))
+  if (IsVisible())
     return;
 
-  CFWL_NoteDriver* noteDriver =
-      static_cast<CFWL_NoteDriver*>(GetOwnerApp()->GetNoteDriver());
-  CFWL_WidgetMgr* widgetMgr = GetOwnerApp()->GetWidgetMgr();
+  CFWL_NoteDriver* noteDriver = GetOwnerApp()->GetNoteDriver();
   noteDriver->NotifyTargetHide(this);
+
+  CFWL_WidgetMgr* widgetMgr = GetOwnerApp()->GetWidgetMgr();
   CFWL_Widget* child = widgetMgr->GetFirstChildWidget(this);
   while (child) {
     noteDriver->NotifyTargetHide(child);
@@ -161,50 +145,18 @@
 
 CFX_PointF CFWL_Widget::TransformTo(CFWL_Widget* pWidget,
                                     const CFX_PointF& point) {
-  if (m_pWidgetMgr->IsFormDisabled()) {
-    CFX_SizeF szOffset;
-    if (IsParent(pWidget)) {
-      szOffset = GetOffsetFromParent(pWidget);
-    } else {
-      szOffset = pWidget->GetOffsetFromParent(this);
-      szOffset.width = -szOffset.width;
-      szOffset.height = -szOffset.height;
-    }
-    return point + CFX_PointF(szOffset.width, szOffset.height);
+  CFX_SizeF szOffset;
+  if (IsParent(pWidget)) {
+    szOffset = GetOffsetFromParent(pWidget);
+  } else {
+    szOffset = pWidget->GetOffsetFromParent(this);
+    szOffset.width = -szOffset.width;
+    szOffset.height = -szOffset.height;
   }
-
-  CFX_PointF ret = point;
-  CFWL_Widget* parent = GetParent();
-  if (parent)
-    ret = GetMatrix().Transform(ret + GetWidgetRect().TopLeft());
-
-  CFWL_Widget* form1 = m_pWidgetMgr->GetSystemFormWidget(this);
-  if (!form1)
-    return ret;
-
-  if (!pWidget)
-    return ret + form1->GetWidgetRect().TopLeft();
-
-  CFWL_Widget* form2 = m_pWidgetMgr->GetSystemFormWidget(pWidget);
-  if (!form2)
-    return ret;
-  if (form1 != form2) {
-    ret += form1->GetWidgetRect().TopLeft();
-    ret -= form2->GetWidgetRect().TopLeft();
-  }
-
-  parent = pWidget->GetParent();
-  if (!parent)
-    return ret;
-
-  return pWidget->GetMatrix().GetInverse().Transform(ret) -
-         pWidget->GetWidgetRect().TopLeft();
+  return point + CFX_PointF(szOffset.width, szOffset.height);
 }
 
-CFX_Matrix CFWL_Widget::GetMatrix() {
-  if (!m_pProperties)
-    return CFX_Matrix();
-
+CFX_Matrix CFWL_Widget::GetMatrix() const {
   CFWL_Widget* parent = GetParent();
   std::vector<CFWL_Widget*> parents;
   while (parent) {
@@ -213,28 +165,13 @@
   }
 
   CFX_Matrix matrix;
-  CFX_Matrix ctmOnParent;
-  CFX_RectF rect;
-  int32_t count = pdfium::CollectionSize<int32_t>(parents);
-  for (int32_t i = count - 2; i >= 0; i--) {
-    parent = parents[i];
-    if (parent->m_pProperties)
-      ctmOnParent.SetIdentity();
-    rect = parent->GetWidgetRect();
-    matrix.Concat(ctmOnParent, true);
-    matrix.Translate(rect.left, rect.top, true);
+  for (size_t i = parents.size(); i >= 2; i--) {
+    CFX_RectF rect = parents[i - 2]->GetWidgetRect();
+    matrix.TranslatePrepend(rect.left, rect.top);
   }
-  CFX_Matrix m;
-  m.SetIdentity();
-  matrix.Concat(m, true);
-  parents.clear();
   return matrix;
 }
 
-IFWL_ThemeProvider* CFWL_Widget::GetThemeProvider() const {
-  return m_pProperties->m_pThemeProvider;
-}
-
 void CFWL_Widget::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
   m_pProperties->m_pThemeProvider = pThemeProvider;
 }
@@ -248,7 +185,7 @@
 }
 
 bool CFWL_Widget::IsVisible() const {
-  return (m_pProperties->m_dwStates & FWL_WGTSTATE_Invisible) == 0;
+  return !(m_pProperties->m_dwStates & FWL_WGTSTATE_Invisible);
 }
 
 bool CFWL_Widget::IsOverLapper() const {
@@ -264,38 +201,44 @@
   return !!(m_pProperties->m_dwStyles & FWL_WGTSTYLE_Child);
 }
 
-CFX_RectF CFWL_Widget::GetEdgeRect() {
+CFWL_Widget* CFWL_Widget::GetOutmost() const {
+  CFWL_Widget* pOuter = const_cast<CFWL_Widget*>(this);
+  while (pOuter->GetOuter())
+    pOuter = pOuter->GetOuter();
+  return pOuter;
+}
+
+CFX_RectF CFWL_Widget::GetEdgeRect() const {
   CFX_RectF rtEdge(0, 0, m_pProperties->m_rtWidget.width,
                    m_pProperties->m_rtWidget.height);
-  if (HasBorder()) {
-    float fCX = GetBorderSize(true);
-    float fCY = GetBorderSize(false);
-    rtEdge.Deflate(fCX, fCY);
-  }
+  if (HasBorder())
+    rtEdge.Deflate(GetCXBorderSize(), GetCYBorderSize());
   return rtEdge;
 }
 
-float CFWL_Widget::GetBorderSize(bool bCX) {
+float CFWL_Widget::GetCXBorderSize() const {
   IFWL_ThemeProvider* theme = GetAvailableTheme();
-  if (!theme)
-    return 0.0f;
-  return bCX ? theme->GetCXBorderSize() : theme->GetCYBorderSize();
+  return theme ? theme->GetCXBorderSize() : 0.0f;
 }
 
-CFX_RectF CFWL_Widget::GetRelativeRect() {
+float CFWL_Widget::GetCYBorderSize() const {
+  IFWL_ThemeProvider* theme = GetAvailableTheme();
+  return theme ? theme->GetCYBorderSize() : 0.0f;
+}
+
+CFX_RectF CFWL_Widget::GetRelativeRect() const {
   return CFX_RectF(0, 0, m_pProperties->m_rtWidget.width,
                    m_pProperties->m_rtWidget.height);
 }
 
-IFWL_ThemeProvider* CFWL_Widget::GetAvailableTheme() {
+IFWL_ThemeProvider* CFWL_Widget::GetAvailableTheme() const {
   if (m_pProperties->m_pThemeProvider)
-    return m_pProperties->m_pThemeProvider;
+    return m_pProperties->m_pThemeProvider.Get();
 
-  CFWL_Widget* pUp = this;
+  const CFWL_Widget* pUp = this;
   do {
-    pUp = (pUp->GetStyles() & FWL_WGTSTYLE_Popup)
-              ? m_pWidgetMgr->GetOwnerWidget(pUp)
-              : m_pWidgetMgr->GetParentWidget(pUp);
+    pUp = pUp->IsPopup() ? m_pWidgetMgr->GetOwnerWidget(pUp)
+                         : m_pWidgetMgr->GetParentWidget(pUp);
     if (pUp) {
       IFWL_ThemeProvider* pRet = pUp->GetThemeProvider();
       if (pRet)
@@ -305,16 +248,6 @@
   return nullptr;
 }
 
-CFWL_Widget* CFWL_Widget::GetRootOuter() {
-  CFWL_Widget* pRet = m_pOuter;
-  if (!pRet)
-    return nullptr;
-
-  while (CFWL_Widget* pOuter = pRet->GetOuter())
-    pRet = pOuter;
-  return pRet;
-}
-
 CFX_SizeF CFWL_Widget::CalcTextSize(const WideString& wsText,
                                     IFWL_ThemeProvider* pTheme,
                                     bool bMultiLine) {
@@ -332,7 +265,7 @@
   calPart.m_iTTOAlign = FDE_TextAlignment::kTopLeft;
   float fWidth = bMultiLine ? FWL_WGT_CalcMultiLineDefWidth : FWL_WGT_CalcWidth;
   CFX_RectF rect(0, 0, fWidth, FWL_WGT_CalcHeight);
-  pTheme->CalcTextRect(&calPart, rect);
+  pTheme->CalcTextRect(calPart, &rect);
   return CFX_SizeF(rect.width, rect.height);
 }
 
@@ -340,160 +273,27 @@
                                IFWL_ThemeProvider* pTheme,
                                const FDE_TextStyle& dwTTOStyles,
                                FDE_TextAlignment iTTOAlign,
-                               CFX_RectF& rect) {
+                               CFX_RectF* pRect) {
   CFWL_ThemeText calPart;
   calPart.m_pWidget = this;
   calPart.m_wsText = wsText;
   calPart.m_dwTTOStyles = dwTTOStyles;
   calPart.m_iTTOAlign = iTTOAlign;
-  pTheme->CalcTextRect(&calPart, rect);
-}
-
-void CFWL_Widget::SetFocus(bool bFocus) {
-  if (m_pWidgetMgr->IsFormDisabled())
-    return;
-
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  if (!pDriver)
-    return;
-
-  CFWL_Widget* curFocus = pDriver->GetFocus();
-  if (bFocus && curFocus != this)
-    pDriver->SetFocus(this);
-  else if (!bFocus && curFocus == this)
-    pDriver->SetFocus(nullptr);
+  pTheme->CalcTextRect(calPart, pRect);
 }
 
 void CFWL_Widget::SetGrab(bool bSet) {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
+  CFWL_NoteDriver* pDriver = GetOwnerApp()->GetNoteDriver();
   pDriver->SetGrab(this, bSet);
 }
 
-void CFWL_Widget::GetPopupPos(float fMinHeight,
-                              float fMaxHeight,
-                              const CFX_RectF& rtAnchor,
-                              CFX_RectF& rtPopup) {
-  if (GetClassID() == FWL_Type::ComboBox) {
-    if (m_pWidgetMgr->IsFormDisabled()) {
-      m_pWidgetMgr->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
-                                       rtPopup);
-      return;
-    }
-    GetPopupPosComboBox(fMinHeight, fMaxHeight, rtAnchor, rtPopup);
-    return;
-  }
-  if (GetClassID() == FWL_Type::DateTimePicker &&
-      m_pWidgetMgr->IsFormDisabled()) {
-    m_pWidgetMgr->GetAdapterPopupPos(this, fMinHeight, fMaxHeight, rtAnchor,
-                                     rtPopup);
-    return;
-  }
-  GetPopupPosGeneral(fMinHeight, fMaxHeight, rtAnchor, rtPopup);
-}
-
-bool CFWL_Widget::GetPopupPosMenu(float fMinHeight,
-                                  float fMaxHeight,
-                                  const CFX_RectF& rtAnchor,
-                                  CFX_RectF& rtPopup) {
-  if (GetStylesEx() & FWL_STYLEEXT_MNU_Vert) {
-    bool bLeft = m_pProperties->m_rtWidget.left < 0;
-    float fRight = rtAnchor.right() + rtPopup.width;
-    CFX_PointF point = TransformTo(nullptr, CFX_PointF());
-    if (fRight + point.x > 0.0f || bLeft) {
-      rtPopup = CFX_RectF(rtAnchor.left - rtPopup.width, rtAnchor.top,
-                          rtPopup.width, rtPopup.height);
-    } else {
-      rtPopup = CFX_RectF(rtAnchor.right(), rtAnchor.top, rtPopup.width,
-                          rtPopup.height);
-    }
-    rtPopup.Offset(point.x, point.y);
-    return true;
-  }
-
-  float fBottom = rtAnchor.bottom() + rtPopup.height;
-  CFX_PointF point = TransformTo(nullptr, point);
-  if (fBottom + point.y > 0.0f) {
-    rtPopup = CFX_RectF(rtAnchor.left, rtAnchor.top - rtPopup.height,
-                        rtPopup.width, rtPopup.height);
-  } else {
-    rtPopup = CFX_RectF(rtAnchor.left, rtAnchor.bottom(), rtPopup.width,
-                        rtPopup.height);
-  }
-  rtPopup.Offset(point.x, point.y);
-  return true;
-}
-
-bool CFWL_Widget::GetPopupPosComboBox(float fMinHeight,
-                                      float fMaxHeight,
-                                      const CFX_RectF& rtAnchor,
-                                      CFX_RectF& rtPopup) {
-  float fPopHeight = rtPopup.height;
-  if (rtPopup.height > fMaxHeight)
-    fPopHeight = fMaxHeight;
-  else if (rtPopup.height < fMinHeight)
-    fPopHeight = fMinHeight;
-
-  float fWidth = std::max(rtAnchor.width, rtPopup.width);
-  float fBottom = rtAnchor.bottom() + fPopHeight;
-  CFX_PointF point = TransformTo(nullptr, CFX_PointF());
-  if (fBottom + point.y > 0.0f) {
-    rtPopup =
-        CFX_RectF(rtAnchor.left, rtAnchor.top - fPopHeight, fWidth, fPopHeight);
-  } else {
-    rtPopup = CFX_RectF(rtAnchor.left, rtAnchor.bottom(), fWidth, fPopHeight);
-  }
-
-  rtPopup.Offset(point.x, point.y);
-  return true;
-}
-
-bool CFWL_Widget::GetPopupPosGeneral(float fMinHeight,
-                                     float fMaxHeight,
-                                     const CFX_RectF& rtAnchor,
-                                     CFX_RectF& rtPopup) {
-  CFX_PointF point = TransformTo(nullptr, CFX_PointF());
-  if (rtAnchor.bottom() + point.y > 0.0f) {
-    rtPopup = CFX_RectF(rtAnchor.left, rtAnchor.top - rtPopup.height,
-                        rtPopup.width, rtPopup.height);
-  } else {
-    rtPopup = CFX_RectF(rtAnchor.left, rtAnchor.bottom(), rtPopup.width,
-                        rtPopup.height);
-  }
-  rtPopup.Offset(point.x, point.y);
-  return true;
-}
-
 void CFWL_Widget::RegisterEventTarget(CFWL_Widget* pEventSource) {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pNoteDriver = pApp->GetNoteDriver();
-  if (!pNoteDriver)
-    return;
-
+  CFWL_NoteDriver* pNoteDriver = GetOwnerApp()->GetNoteDriver();
   pNoteDriver->RegisterEventTarget(this, pEventSource);
 }
 
 void CFWL_Widget::UnregisterEventTarget() {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pNoteDriver = pApp->GetNoteDriver();
-  if (!pNoteDriver)
-    return;
-
+  CFWL_NoteDriver* pNoteDriver = GetOwnerApp()->GetNoteDriver();
   pNoteDriver->UnregisterEventTarget(this);
 }
 
@@ -502,21 +302,10 @@
     m_pOuter->GetDelegate()->OnProcessEvent(pEvent);
     return;
   }
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pNoteDriver = pApp->GetNoteDriver();
-  if (!pNoteDriver)
-    return;
+  CFWL_NoteDriver* pNoteDriver = GetOwnerApp()->GetNoteDriver();
   pNoteDriver->SendEvent(pEvent);
 }
 
-void CFWL_Widget::Repaint() {
-  RepaintRect(CFX_RectF(0, 0, m_pProperties->m_rtWidget.width,
-                        m_pProperties->m_rtWidget.height));
-}
-
 void CFWL_Widget::RepaintRect(const CFX_RectF& pRect) {
   m_pWidgetMgr->RepaintWidget(this, pRect);
 }
@@ -530,9 +319,9 @@
   param.m_iPart = iPartBk;
   param.m_pGraphics = pGraphics;
   if (pMatrix)
-    param.m_matrix.Concat(*pMatrix, true);
+    param.m_matrix = *pMatrix;
   param.m_rtPart = GetRelativeRect();
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 void CFWL_Widget::DrawBorder(CXFA_Graphics* pGraphics,
@@ -543,21 +332,13 @@
   param.m_pWidget = this;
   param.m_iPart = iPartBorder;
   param.m_pGraphics = pGraphics;
-  param.m_matrix.Concat(matrix, true);
+  param.m_matrix = matrix;
   param.m_rtPart = GetRelativeRect();
-  pTheme->DrawBackground(&param);
+  pTheme->DrawBackground(param);
 }
 
 void CFWL_Widget::NotifyDriver() {
-  const CFWL_App* pApp = GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  if (!pDriver)
-    return;
-
+  CFWL_NoteDriver* pDriver = GetOwnerApp()->GetNoteDriver();
   pDriver->NotifyTargetDestroy(this);
 }
 
@@ -566,9 +347,6 @@
     return CFX_SizeF();
 
   CFWL_WidgetMgr* pWidgetMgr = GetOwnerApp()->GetWidgetMgr();
-  if (!pWidgetMgr)
-    return CFX_SizeF();
-
   CFX_SizeF szRet(m_pProperties->m_rtWidget.left,
                   m_pProperties->m_rtWidget.top);
 
@@ -592,14 +370,13 @@
 }
 
 void CFWL_Widget::OnProcessMessage(CFWL_Message* pMessage) {
-  if (!pMessage->m_pDstTarget)
+  CFWL_Widget* pWidget = pMessage->GetDstTarget();
+  if (!pWidget)
     return;
 
-  CFWL_Widget* pWidget = pMessage->m_pDstTarget;
   switch (pMessage->GetType()) {
     case CFWL_Message::Type::Mouse: {
       CFWL_MessageMouse* pMsgMouse = static_cast<CFWL_MessageMouse*>(pMessage);
-
       CFWL_EventMouse evt(pWidget, pWidget);
       evt.m_dwCmd = pMsgMouse->m_dwCmd;
       pWidget->DispatchEvent(&evt);
@@ -614,3 +391,12 @@
 
 void CFWL_Widget::OnDrawWidget(CXFA_Graphics* pGraphics,
                                const CFX_Matrix& matrix) {}
+
+CFWL_Widget::ScopedUpdateLock::ScopedUpdateLock(CFWL_Widget* widget)
+    : widget_(widget) {
+  widget_->LockUpdate();
+}
+
+CFWL_Widget::ScopedUpdateLock::~ScopedUpdateLock() {
+  widget_->UnlockUpdate();
+}
diff --git a/xfa/fwl/cfwl_widget.h b/xfa/fwl/cfwl_widget.h
index 27cb458..a8112e8 100644
--- a/xfa/fwl/cfwl_widget.h
+++ b/xfa/fwl/cfwl_widget.h
@@ -11,14 +11,23 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fde/cfde_data.h"
-#include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_themepart.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
+#include "xfa/fwl/cfwl_widgetproperties.h"
 #include "xfa/fwl/fwl_widgethit.h"
 #include "xfa/fwl/ifwl_widgetdelegate.h"
 
+class CFWL_App;
+class CFWL_AppImp;
+class CFWL_Event;
+class CFWL_MessageKey;
+class CFWL_Widget;
+class CFWL_WidgetMgr;
+class IFWL_ThemeProvider;
+
 enum class FWL_Type {
   Unknown = 0,
 
@@ -39,21 +48,30 @@
   ToolTip
 };
 
-class CFWL_App;
-class CFWL_AppImp;
-class CFWL_MessageKey;
-class CFWL_Widget;
-class CFWL_WidgetMgr;
-class CFWL_WidgetProperties;
-class CXFA_FFWidget;
-class IFWL_ThemeProvider;
-
-class CFWL_Widget : public IFWL_WidgetDelegate {
+// NOTE: CFWL_Widget serves as its own delegate until replaced at runtime.
+class CFWL_Widget : public Observable, public IFWL_WidgetDelegate {
  public:
+  class AdapterIface {
+   public:
+    virtual ~AdapterIface() {}
+    virtual CFX_Matrix GetRotateMatrix() = 0;
+    virtual void DisplayCaret(bool bVisible, const CFX_RectF* pRtAnchor) = 0;
+    virtual void GetBorderColorAndThickness(FX_ARGB* cr, float* fWidth) = 0;
+  };
+
+  class ScopedUpdateLock {
+   public:
+    explicit ScopedUpdateLock(CFWL_Widget* widget);
+    ~ScopedUpdateLock();
+
+   private:
+    UnownedPtr<CFWL_Widget> const widget_;
+  };
+
   ~CFWL_Widget() override;
 
   virtual FWL_Type GetClassID() const = 0;
-  virtual bool IsInstance(const WideStringView& wsClass) const;
+  virtual bool IsForm() const;
   virtual CFX_RectF GetAutosizedWidgetRect();
   virtual CFX_RectF GetWidgetRect();
   virtual CFX_RectF GetClientRect();
@@ -78,23 +96,24 @@
 
   void SetParent(CFWL_Widget* pParent);
 
+  bool IsVisible() const;
+  bool IsOverLapper() const;
+  bool IsPopup() const;
+  bool IsChild() const;
+
   CFWL_Widget* GetOwner() { return m_pWidgetMgr->GetOwnerWidget(this); }
   CFWL_Widget* GetOuter() const { return m_pOuter; }
+  CFWL_Widget* GetOutmost() const;
 
-  uint32_t GetStyles() const;
   void ModifyStyles(uint32_t dwStylesAdded, uint32_t dwStylesRemoved);
   uint32_t GetStylesEx() const;
   uint32_t GetStates() const;
 
-  void LockUpdate() { m_iLock++; }
-  void UnlockUpdate() {
-    if (IsLocked())
-      m_iLock--;
-  }
-
   CFX_PointF TransformTo(CFWL_Widget* pWidget, const CFX_PointF& point);
-  CFX_Matrix GetMatrix();
-  IFWL_ThemeProvider* GetThemeProvider() const;
+  CFX_Matrix GetMatrix() const;
+  IFWL_ThemeProvider* GetThemeProvider() const {
+    return m_pProperties->m_pThemeProvider.Get();
+  }
 
   void SetDelegate(IFWL_WidgetDelegate* delegate) { m_pDelegate = delegate; }
   IFWL_WidgetDelegate* GetDelegate() {
@@ -108,12 +127,9 @@
   uint32_t GetEventKey() const { return m_nEventKey; }
   void SetEventKey(uint32_t key) { m_nEventKey = key; }
 
-  CXFA_FFWidget* GetLayoutItem() const { return m_pLayoutItem; }
-  void SetLayoutItem(CXFA_FFWidget* pItem) { m_pLayoutItem = pItem; }
-
-  void SetFocus(bool bFocus);
+  AdapterIface* GetAdapterIface() const { return m_pAdapterIface; }
+  void SetAdapterIface(AdapterIface* pItem) { m_pAdapterIface = pItem; }
   void RepaintRect(const CFX_RectF& pRect);
-  void Repaint();
 
  protected:
   CFWL_Widget(const CFWL_App* app,
@@ -123,10 +139,11 @@
   bool IsEnabled() const;
   bool IsLocked() const { return m_iLock > 0; }
   bool HasBorder() const;
-  CFX_RectF GetEdgeRect();
-  float GetBorderSize(bool bCX);
-  CFX_RectF GetRelativeRect();
-  IFWL_ThemeProvider* GetAvailableTheme();
+  CFX_RectF GetEdgeRect() const;
+  float GetCXBorderSize() const;
+  float GetCYBorderSize() const;
+  CFX_RectF GetRelativeRect() const;
+  IFWL_ThemeProvider* GetAvailableTheme() const;
   CFX_SizeF CalcTextSize(const WideString& wsText,
                          IFWL_ThemeProvider* pTheme,
                          bool bMultiLine);
@@ -134,12 +151,8 @@
                     IFWL_ThemeProvider* pTheme,
                     const FDE_TextStyle& dwTTOStyles,
                     FDE_TextAlignment iTTOAlign,
-                    CFX_RectF& rect);
+                    CFX_RectF* pRect);
   void SetGrab(bool bSet);
-  void GetPopupPos(float fMinHeight,
-                   float fMaxHeight,
-                   const CFX_RectF& rtAnchor,
-                   CFX_RectF& rtPopup);
   void RegisterEventTarget(CFWL_Widget* pEventSource);
   void UnregisterEventTarget();
   void DispatchEvent(CFWL_Event* pEvent);
@@ -152,29 +165,17 @@
   UnownedPtr<CFWL_WidgetMgr> const m_pWidgetMgr;
   std::unique_ptr<CFWL_WidgetProperties> m_pProperties;
   CFWL_Widget* m_pOuter;
-  int32_t m_iLock;
+  int32_t m_iLock = 0;
 
  private:
-  CFWL_Widget* GetParent() { return m_pWidgetMgr->GetParentWidget(this); }
-  CFX_SizeF GetOffsetFromParent(CFWL_Widget* pParent);
+  void LockUpdate() { m_iLock++; }
+  void UnlockUpdate() {
+    if (IsLocked())
+      m_iLock--;
+  }
 
-  bool IsVisible() const;
-  bool IsOverLapper() const;
-  bool IsPopup() const;
-  bool IsChild() const;
-  CFWL_Widget* GetRootOuter();
-  bool GetPopupPosMenu(float fMinHeight,
-                       float fMaxHeight,
-                       const CFX_RectF& rtAnchor,
-                       CFX_RectF& rtPopup);
-  bool GetPopupPosComboBox(float fMinHeight,
-                           float fMaxHeight,
-                           const CFX_RectF& rtAnchor,
-                           CFX_RectF& rtPopup);
-  bool GetPopupPosGeneral(float fMinHeight,
-                          float fMaxHeight,
-                          const CFX_RectF& rtAnchor,
-                          CFX_RectF& rtPopup);
+  CFWL_Widget* GetParent() const { return m_pWidgetMgr->GetParentWidget(this); }
+  CFX_SizeF GetOffsetFromParent(CFWL_Widget* pParent);
   void DrawBackground(CXFA_Graphics* pGraphics,
                       CFWL_Part iPartBk,
                       IFWL_ThemeProvider* pTheme,
@@ -182,8 +183,8 @@
   void NotifyDriver();
   bool IsParent(CFWL_Widget* pParent);
 
-  CXFA_FFWidget* m_pLayoutItem;
-  uint32_t m_nEventKey;
+  uint32_t m_nEventKey = 0;
+  AdapterIface* m_pAdapterIface = nullptr;
   UnownedPtr<IFWL_WidgetDelegate> m_pDelegate;
 };
 
diff --git a/xfa/fwl/cfwl_widgetmgr.cpp b/xfa/fwl/cfwl_widgetmgr.cpp
index 3082b18..cb41430 100644
--- a/xfa/fwl/cfwl_widgetmgr.cpp
+++ b/xfa/fwl/cfwl_widgetmgr.cpp
@@ -8,44 +8,39 @@
 
 #include <utility>
 
+#include "build/build_config.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_app.h"
-#include "xfa/fwl/cfwl_form.h"
+#include "xfa/fwl/cfwl_message.h"
 #include "xfa/fwl/cfwl_notedriver.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
-#include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
 
-namespace {
-
-const int kNeedRepaintHitPoints = 12;
-const int kNeedRepaintHitPiece = 3;
-
-struct FWL_NEEDREPAINTHITDATA {
-  CFX_PointF hitPoint;
-  bool bNotNeedRepaint;
-  bool bNotContainByDirty;
-};
-
-}  // namespace
-
-CFWL_WidgetMgr::CFWL_WidgetMgr(CXFA_FFApp* pAdapterNative)
-    : m_dwCapability(FWL_WGTMGR_DisableForm),
-      m_pAdapter(pAdapterNative->GetFWLAdapterWidgetMgr()) {
-  ASSERT(m_pAdapter);
+CFWL_WidgetMgr::CFWL_WidgetMgr(AdapterIface* pAdapterNative)
+    : m_pAdapter(pAdapterNative) {
   m_mapWidgetItem[nullptr] = pdfium::MakeUnique<Item>();
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  m_rtScreen.Reset();
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
 }
 
-CFWL_WidgetMgr::~CFWL_WidgetMgr() {}
+CFWL_WidgetMgr::~CFWL_WidgetMgr() = default;
 
-CFWL_Widget* CFWL_WidgetMgr::GetParentWidget(CFWL_Widget* pWidget) const {
+// static
+CFWL_Widget* CFWL_WidgetMgr::NextTab(CFWL_Widget* parent, CFWL_Widget* focus) {
+  CFWL_WidgetMgr* pMgr = parent->GetOwnerApp()->GetWidgetMgr();
+  CFWL_Widget* child = pMgr->GetFirstChildWidget(parent);
+  while (child) {
+    CFWL_Widget* bRet = NextTab(child, focus);
+    if (bRet)
+      return bRet;
+
+    child = pMgr->GetNextSiblingWidget(child);
+  }
+  return nullptr;
+}
+
+CFWL_Widget* CFWL_WidgetMgr::GetParentWidget(const CFWL_Widget* pWidget) const {
   Item* pItem = GetWidgetMgrItem(pWidget);
   return pItem && pItem->pParent ? pItem->pParent->pWidget : nullptr;
 }
 
-CFWL_Widget* CFWL_WidgetMgr::GetOwnerWidget(CFWL_Widget* pWidget) const {
+CFWL_Widget* CFWL_WidgetMgr::GetOwnerWidget(const CFWL_Widget* pWidget) const {
   Item* pItem = GetWidgetMgrItem(pWidget);
   return pItem && pItem->pOwner ? pItem->pOwner->pWidget : nullptr;
 }
@@ -142,29 +137,15 @@
 
 void CFWL_WidgetMgr::RepaintWidget(CFWL_Widget* pWidget,
                                    const CFX_RectF& rect) {
-  if (!m_pAdapter)
-    return;
-
   CFWL_Widget* pNative = pWidget;
   CFX_RectF transformedRect = rect;
-  if (IsFormDisabled()) {
-    CFWL_Widget* pOuter = pWidget->GetOuter();
-    while (pOuter) {
-      CFX_RectF rtTemp = pNative->GetWidgetRect();
-      transformedRect.left += rtTemp.left;
-      transformedRect.top += rtTemp.top;
-      pNative = pOuter;
-      pOuter = pOuter->GetOuter();
-    }
-  } else if (!IsAbleNative(pWidget)) {
-    pNative = GetSystemFormWidget(pWidget);
-    if (!pNative)
-      return;
-
-    CFX_PointF pos = pWidget->TransformTo(
-        pNative, CFX_PointF(transformedRect.left, transformedRect.top));
-    transformedRect.left = pos.x;
-    transformedRect.top = pos.y;
+  CFWL_Widget* pOuter = pWidget->GetOuter();
+  while (pOuter) {
+    CFX_RectF rtTemp = pNative->GetWidgetRect();
+    transformedRect.left += rtTemp.left;
+    transformedRect.top += rtTemp.top;
+    pNative = pOuter;
+    pOuter = pOuter->GetOuter();
   }
   AddRedrawCounts(pNative);
   m_pAdapter->RepaintWidget(pNative);
@@ -263,12 +244,10 @@
   if (!parent)
     return nullptr;
 
-  CFX_PointF pos;
   CFWL_Widget* child = GetLastChildWidget(parent);
   while (child) {
-    if ((child->GetStates() & FWL_WGTSTATE_Invisible) == 0) {
-      pos = parent->GetMatrix().GetInverse().Transform(point);
-
+    if (child->IsVisible()) {
+      CFX_PointF pos = parent->GetMatrix().GetInverse().Transform(point);
       CFX_RectF bounds = child->GetWidgetRect();
       if (bounds.Contains(pos)) {
         pos -= bounds.TopLeft();
@@ -280,56 +259,6 @@
   return parent;
 }
 
-CFWL_Widget* CFWL_WidgetMgr::NextTab(CFWL_Widget* parent,
-                                     CFWL_Widget* focus,
-                                     bool& bFind) {
-  CFWL_WidgetMgr* pMgr = parent->GetOwnerApp()->GetWidgetMgr();
-  CFWL_Widget* child = pMgr->GetFirstChildWidget(parent);
-  while (child) {
-    if (focus == child)
-      bFind = true;
-
-    CFWL_Widget* bRet = NextTab(child, focus, bFind);
-    if (bRet)
-      return bRet;
-
-    child = pMgr->GetNextSiblingWidget(child);
-  }
-  return nullptr;
-}
-
-int32_t CFWL_WidgetMgr::CountRadioButtonGroup(CFWL_Widget* pFirst) const {
-  int32_t iRet = 0;
-  CFWL_Widget* pChild = pFirst;
-  while (pChild) {
-    pChild = GetNextSiblingWidget(pChild);
-    ++iRet;
-  }
-  return iRet;
-}
-
-CFWL_Widget* CFWL_WidgetMgr::GetRadioButtonGroupHeader(
-    CFWL_Widget* pRadioButton) const {
-  CFWL_Widget* pNext = pRadioButton;
-  if (pNext && (pNext->GetStyles() & FWL_WGTSTYLE_Group))
-    return pNext;
-  return nullptr;
-}
-
-std::vector<CFWL_Widget*> CFWL_WidgetMgr::GetSameGroupRadioButton(
-    CFWL_Widget* pRadioButton) const {
-  CFWL_Widget* pFirst = GetFirstSiblingWidget(pRadioButton);
-  if (!pFirst)
-    pFirst = pRadioButton;
-
-  if (CountRadioButtonGroup(pFirst) < 2)
-    return std::vector<CFWL_Widget*>();
-
-  std::vector<CFWL_Widget*> group;
-  group.push_back(GetRadioButtonGroupHeader(pRadioButton));
-  return group;
-}
-
 CFWL_Widget* CFWL_WidgetMgr::GetDefaultButton(CFWL_Widget* pParent) const {
   if ((pParent->GetClassID() == FWL_Type::PushButton) &&
       (pParent->GetStates() & (1 << (FWL_WGTSTATE_MAX + 2)))) {
@@ -360,58 +289,35 @@
 }
 
 CFWL_WidgetMgr::Item* CFWL_WidgetMgr::GetWidgetMgrItem(
-    CFWL_Widget* pWidget) const {
+    const CFWL_Widget* pWidget) const {
   auto it = m_mapWidgetItem.find(pWidget);
-  return it != m_mapWidgetItem.end() ? static_cast<Item*>(it->second.get())
-                                     : nullptr;
+  return it != m_mapWidgetItem.end() ? it->second.get() : nullptr;
 }
 
 bool CFWL_WidgetMgr::IsAbleNative(CFWL_Widget* pWidget) const {
-  if (!pWidget)
-    return false;
-  if (!pWidget->IsInstance(FWL_CLASS_Form))
+  if (!pWidget || !pWidget->IsForm())
     return false;
 
-  uint32_t dwStyles = pWidget->GetStyles();
-  return ((dwStyles & FWL_WGTSTYLE_WindowTypeMask) ==
-          FWL_WGTSTYLE_OverLapper) ||
-         (dwStyles & FWL_WGTSTYLE_Popup);
+  return pWidget->IsOverLapper() || pWidget->IsPopup();
 }
 
 void CFWL_WidgetMgr::GetAdapterPopupPos(CFWL_Widget* pWidget,
                                         float fMinHeight,
                                         float fMaxHeight,
                                         const CFX_RectF& rtAnchor,
-                                        CFX_RectF& rtPopup) const {
-  m_pAdapter->GetPopupPos(pWidget, fMinHeight, fMaxHeight, rtAnchor, rtPopup);
+                                        CFX_RectF* pPopupRect) const {
+  m_pAdapter->GetPopupPos(pWidget, fMinHeight, fMaxHeight, rtAnchor,
+                          pPopupRect);
 }
 
-void CFWL_WidgetMgr::OnProcessMessageToForm(CFWL_Message* pMessage) {
-  if (!pMessage)
-    return;
-  if (!pMessage->m_pDstTarget)
+void CFWL_WidgetMgr::OnProcessMessageToForm(
+    std::unique_ptr<CFWL_Message> pMessage) {
+  CFWL_Widget* pDstWidget = pMessage->GetDstTarget();
+  if (!pDstWidget)
     return;
 
-  CFWL_Widget* pDstWidget = pMessage->m_pDstTarget;
-  const CFWL_App* pApp = pDstWidget->GetOwnerApp();
-  if (!pApp)
-    return;
-
-  CFWL_NoteDriver* pNoteDriver =
-      static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
-  if (!pNoteDriver)
-    return;
-
-  if (IsFormDisabled())
-    pNoteDriver->ProcessMessage(pMessage->Clone());
-  else
-    pNoteDriver->QueueMessage(pMessage->Clone());
-
-#if (_FX_OS_ == _FX_OS_MACOSX_)
-  CFWL_NoteLoop* pTopLoop = pNoteDriver->GetTopLoop();
-  if (pTopLoop)
-    pNoteDriver->UnqueueMessageAndProcess(pTopLoop);
-#endif
+  CFWL_NoteDriver* pNoteDriver = pDstWidget->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->ProcessMessage(std::move(pMessage));
 }
 
 void CFWL_WidgetMgr::OnDrawWidget(CFWL_Widget* pWidget,
@@ -423,26 +329,10 @@
   CFX_RectF clipCopy(0, 0, pWidget->GetWidgetRect().Size());
   CFX_RectF clipBounds;
 
-#if _FX_OS_ == _FX_OS_MACOSX_
-  if (IsFormDisabled()) {
-#endif  // _FX_OS_ == _FX_OS_MACOSX_
+  pWidget->GetDelegate()->OnDrawWidget(pGraphics, matrix);
+  clipBounds = pGraphics->GetClipRect();
+  clipCopy = clipBounds;
 
-    pWidget->GetDelegate()->OnDrawWidget(pGraphics, matrix);
-    clipBounds = pGraphics->GetClipRect();
-    clipCopy = clipBounds;
-
-#if _FX_OS_ == _FX_OS_MACOSX_
-  } else {
-    clipBounds = CFX_RectF(matrix.a, matrix.b, matrix.c, matrix.d);
-    // FIXME: const cast
-    CFX_Matrix* pMatrixHack = const_cast<CFX_Matrix*>(&matrix);
-    pMatrixHack->SetIdentity();
-    pWidget->GetDelegate()->OnDrawWidget(pGraphics, *pMatrixHack);
-  }
-#endif  // _FX_OS_ == _FX_OS_MACOSX_
-
-  if (!IsFormDisabled())
-    clipBounds.Intersect(pWidget->GetClientRect());
   if (!clipBounds.IsEmpty())
     DrawChild(pWidget, clipBounds, pGraphics, &matrix);
 
@@ -457,12 +347,11 @@
   if (!parent)
     return;
 
-  bool bFormDisable = IsFormDisabled();
   CFWL_Widget* pNextChild = GetFirstChildWidget(parent);
   while (pNextChild) {
     CFWL_Widget* child = pNextChild;
     pNextChild = GetNextSiblingWidget(child);
-    if (child->GetStates() & FWL_WGTSTATE_Invisible)
+    if (!child->IsVisible())
       continue;
 
     CFX_RectF rtWidget = child->GetWidgetRect();
@@ -471,131 +360,18 @@
 
     CFX_Matrix widgetMatrix;
     CFX_RectF clipBounds(rtWidget);
-    if (!bFormDisable)
-      widgetMatrix = child->GetMatrix();
     if (pMatrix)
       widgetMatrix.Concat(*pMatrix);
 
-    if (!bFormDisable) {
-      CFX_PointF pos = widgetMatrix.Transform(clipBounds.TopLeft());
-      clipBounds.left = pos.x;
-      clipBounds.top = pos.y;
-      clipBounds.Intersect(rtClip);
-      if (clipBounds.IsEmpty())
-        continue;
+    widgetMatrix.TranslatePrepend(rtWidget.left, rtWidget.top);
 
-      pGraphics->SaveGraphState();
-      pGraphics->SetClipRect(clipBounds);
-    }
-    widgetMatrix.Translate(rtWidget.left, rtWidget.top, true);
+    if (IFWL_WidgetDelegate* pDelegate = child->GetDelegate())
+      pDelegate->OnDrawWidget(pGraphics, widgetMatrix);
 
-    if (IFWL_WidgetDelegate* pDelegate = child->GetDelegate()) {
-      if (IsFormDisabled() || IsNeedRepaint(child, &widgetMatrix, rtClip))
-        pDelegate->OnDrawWidget(pGraphics, widgetMatrix);
-    }
-    if (!bFormDisable)
-      pGraphics->RestoreGraphState();
-
-    DrawChild(child, clipBounds, pGraphics,
-              bFormDisable ? &widgetMatrix : pMatrix);
-    child = GetNextSiblingWidget(child);
+    DrawChild(child, clipBounds, pGraphics, &widgetMatrix);
   }
 }
 
-bool CFWL_WidgetMgr::IsNeedRepaint(CFWL_Widget* pWidget,
-                                   CFX_Matrix* pMatrix,
-                                   const CFX_RectF& rtDirty) {
-  Item* pItem = GetWidgetMgrItem(pWidget);
-  if (pItem && pItem->iRedrawCounter > 0) {
-    pItem->iRedrawCounter = 0;
-    return true;
-  }
-
-  CFX_RectF rtWidget =
-      pMatrix->TransformRect(CFX_RectF(0, 0, pWidget->GetWidgetRect().Size()));
-  if (!rtWidget.IntersectWith(rtDirty))
-    return false;
-
-  CFWL_Widget* pChild =
-      pWidget->GetOwnerApp()->GetWidgetMgr()->GetFirstChildWidget(pWidget);
-  if (!pChild)
-    return true;
-
-  CFX_RectF rtChilds;
-  bool bChildIntersectWithDirty = false;
-  bool bOrginPtIntersectWidthChild = false;
-  bool bOrginPtIntersectWidthDirty = rtDirty.Contains(rtWidget.TopLeft());
-  static FWL_NEEDREPAINTHITDATA hitPoint[kNeedRepaintHitPoints];
-  memset(hitPoint, 0, sizeof(hitPoint));
-  float fxPiece = rtWidget.width / kNeedRepaintHitPiece;
-  float fyPiece = rtWidget.height / kNeedRepaintHitPiece;
-  hitPoint[2].hitPoint.x = hitPoint[6].hitPoint.x = rtWidget.left;
-  hitPoint[0].hitPoint.x = hitPoint[3].hitPoint.x = hitPoint[7].hitPoint.x =
-      hitPoint[10].hitPoint.x = fxPiece + rtWidget.left;
-  hitPoint[1].hitPoint.x = hitPoint[4].hitPoint.x = hitPoint[8].hitPoint.x =
-      hitPoint[11].hitPoint.x = fxPiece * 2 + rtWidget.left;
-  hitPoint[5].hitPoint.x = hitPoint[9].hitPoint.x =
-      rtWidget.width + rtWidget.left;
-  hitPoint[0].hitPoint.y = hitPoint[1].hitPoint.y = rtWidget.top;
-  hitPoint[2].hitPoint.y = hitPoint[3].hitPoint.y = hitPoint[4].hitPoint.y =
-      hitPoint[5].hitPoint.y = fyPiece + rtWidget.top;
-  hitPoint[6].hitPoint.y = hitPoint[7].hitPoint.y = hitPoint[8].hitPoint.y =
-      hitPoint[9].hitPoint.y = fyPiece * 2 + rtWidget.top;
-  hitPoint[10].hitPoint.y = hitPoint[11].hitPoint.y =
-      rtWidget.height + rtWidget.top;
-  do {
-    CFX_RectF rect = pChild->GetWidgetRect();
-    CFX_RectF r(rect.left + rtWidget.left, rect.top + rtWidget.top, rect.width,
-                rect.height);
-    if (r.IsEmpty())
-      continue;
-    if (r.Contains(rtDirty))
-      return false;
-    if (!bChildIntersectWithDirty && r.IntersectWith(rtDirty))
-      bChildIntersectWithDirty = true;
-    if (bOrginPtIntersectWidthDirty && !bOrginPtIntersectWidthChild)
-      bOrginPtIntersectWidthChild = rect.Contains(CFX_PointF(0, 0));
-
-    if (rtChilds.IsEmpty())
-      rtChilds = rect;
-    else if (!(pChild->GetStates() & FWL_WGTSTATE_Invisible))
-      rtChilds.Union(rect);
-
-    for (int32_t i = 0; i < kNeedRepaintHitPoints; i++) {
-      if (hitPoint[i].bNotContainByDirty || hitPoint[i].bNotNeedRepaint)
-        continue;
-      if (!rtDirty.Contains(hitPoint[i].hitPoint)) {
-        hitPoint[i].bNotContainByDirty = true;
-        continue;
-      }
-      if (r.Contains(hitPoint[i].hitPoint))
-        hitPoint[i].bNotNeedRepaint = true;
-    }
-    pChild =
-        pChild->GetOwnerApp()->GetWidgetMgr()->GetNextSiblingWidget(pChild);
-  } while (pChild);
-
-  if (!bChildIntersectWithDirty)
-    return true;
-  if (bOrginPtIntersectWidthDirty && !bOrginPtIntersectWidthChild)
-    return true;
-  if (rtChilds.IsEmpty())
-    return true;
-
-  int32_t repaintPoint = kNeedRepaintHitPoints;
-  for (int32_t i = 0; i < kNeedRepaintHitPoints; i++) {
-    if (hitPoint[i].bNotNeedRepaint)
-      repaintPoint--;
-  }
-  if (repaintPoint > 0)
-    return true;
-
-  rtChilds = pMatrix->TransformRect(rtChilds);
-  if (rtChilds.Contains(rtDirty) || rtChilds.Contains(rtWidget))
-    return false;
-  return true;
-}
-
 CFWL_WidgetMgr::Item::Item() : CFWL_WidgetMgr::Item(nullptr) {}
 
 CFWL_WidgetMgr::Item::Item(CFWL_Widget* widget)
@@ -605,12 +381,6 @@
       pPrevious(nullptr),
       pNext(nullptr),
       pWidget(widget),
-      iRedrawCounter(0)
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-      ,
-      bOutsideChanged(false)
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-{
-}
+      iRedrawCounter(0) {}
 
 CFWL_WidgetMgr::Item::~Item() {}
diff --git a/xfa/fwl/cfwl_widgetmgr.h b/xfa/fwl/cfwl_widgetmgr.h
index 153d0a9..a401e22 100644
--- a/xfa/fwl/cfwl_widgetmgr.h
+++ b/xfa/fwl/cfwl_widgetmgr.h
@@ -14,27 +14,36 @@
 #include "core/fxcrt/fx_system.h"
 #include "xfa/fxgraphics/cxfa_graphics.h"
 
-#define FWL_WGTMGR_DisableForm 0x00000002
-
 class CFWL_Message;
-class CXFA_FFApp;
-class CXFA_FWLAdapterWidgetMgr;
 class CXFA_Graphics;
 class CFX_Matrix;
 class CFWL_Widget;
 
 class CFWL_WidgetMgr {
  public:
-  explicit CFWL_WidgetMgr(CXFA_FFApp* pAdapterNative);
+  class AdapterIface {
+   public:
+    virtual ~AdapterIface() {}
+    virtual void RepaintWidget(CFWL_Widget* pWidget) = 0;
+    virtual bool GetPopupPos(CFWL_Widget* pWidget,
+                             float fMinHeight,
+                             float fMaxHeight,
+                             const CFX_RectF& rtAnchor,
+                             CFX_RectF* pPopupRect) = 0;
+  };
+
+  explicit CFWL_WidgetMgr(AdapterIface* pAdapterNative);
   ~CFWL_WidgetMgr();
 
-  void OnProcessMessageToForm(CFWL_Message* pMessage);
+  static CFWL_Widget* NextTab(CFWL_Widget* parent, CFWL_Widget* focus);
+
+  void OnProcessMessageToForm(std::unique_ptr<CFWL_Message> pMessage);
   void OnDrawWidget(CFWL_Widget* pWidget,
                     CXFA_Graphics* pGraphics,
                     const CFX_Matrix& matrix);
 
-  CFWL_Widget* GetParentWidget(CFWL_Widget* pWidget) const;
-  CFWL_Widget* GetOwnerWidget(CFWL_Widget* pWidget) const;
+  CFWL_Widget* GetParentWidget(const CFWL_Widget* pWidget) const;
+  CFWL_Widget* GetOwnerWidget(const CFWL_Widget* pWidget) const;
   CFWL_Widget* GetNextSiblingWidget(CFWL_Widget* pWidget) const;
   CFWL_Widget* GetFirstChildWidget(CFWL_Widget* pWidget) const;
   CFWL_Widget* GetSystemFormWidget(CFWL_Widget* pWidget) const;
@@ -48,23 +57,15 @@
 
   CFWL_Widget* GetWidgetAtPoint(CFWL_Widget* pParent,
                                 const CFX_PointF& point) const;
-  CFWL_Widget* NextTab(CFWL_Widget* parent, CFWL_Widget* focus, bool& bFind);
-
-  std::vector<CFWL_Widget*> GetSameGroupRadioButton(
-      CFWL_Widget* pRadioButton) const;
 
   CFWL_Widget* GetDefaultButton(CFWL_Widget* pParent) const;
   void AddRedrawCounts(CFWL_Widget* pWidget);
 
-  bool IsFormDisabled() const {
-    return !!(m_dwCapability & FWL_WGTMGR_DisableForm);
-  }
-
   void GetAdapterPopupPos(CFWL_Widget* pWidget,
                           float fMinHeight,
                           float fMaxHeight,
                           const CFX_RectF& rtAnchor,
-                          CFX_RectF& rtPopup) const;
+                          CFX_RectF* pPopupRect) const;
 
  private:
   class Item {
@@ -81,42 +82,24 @@
     CFWL_Widget* const pWidget;
     std::unique_ptr<CXFA_Graphics> pOffscreen;
     int32_t iRedrawCounter;
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-    bool bOutsideChanged;
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
   };
 
   CFWL_Widget* GetFirstSiblingWidget(CFWL_Widget* pWidget) const;
   CFWL_Widget* GetPriorSiblingWidget(CFWL_Widget* pWidget) const;
   CFWL_Widget* GetLastChildWidget(CFWL_Widget* pWidget) const;
-  Item* GetWidgetMgrItem(CFWL_Widget* pWidget) const;
+  Item* GetWidgetMgrItem(const CFWL_Widget* pWidget) const;
 
   void AppendWidget(CFWL_Widget* pWidget);
-
-  int32_t CountRadioButtonGroup(CFWL_Widget* pFirst) const;
-  CFWL_Widget* GetRadioButtonGroupHeader(CFWL_Widget* pRadioButton) const;
-
   void ResetRedrawCounts(CFWL_Widget* pWidget);
-
   void DrawChild(CFWL_Widget* pParent,
                  const CFX_RectF& rtClip,
                  CXFA_Graphics* pGraphics,
                  const CFX_Matrix* pMatrix);
-  CXFA_Graphics* DrawWidgetBefore(CFWL_Widget* pWidget,
-                                  CXFA_Graphics* pGraphics,
-                                  const CFX_Matrix* pMatrix);
-  bool IsNeedRepaint(CFWL_Widget* pWidget,
-                     CFX_Matrix* pMatrix,
-                     const CFX_RectF& rtDirty);
 
   bool IsAbleNative(CFWL_Widget* pWidget) const;
 
-  uint32_t m_dwCapability;
-  std::map<CFWL_Widget*, std::unique_ptr<Item>> m_mapWidgetItem;
-  UnownedPtr<CXFA_FWLAdapterWidgetMgr> const m_pAdapter;
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
-  CFX_RectF m_rtScreen;
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+  std::map<const CFWL_Widget*, std::unique_ptr<Item>> m_mapWidgetItem;
+  UnownedPtr<AdapterIface> const m_pAdapter;
 };
 
 #endif  // XFA_FWL_CFWL_WIDGETMGR_H_
diff --git a/xfa/fwl/cfwl_widgetproperties.cpp b/xfa/fwl/cfwl_widgetproperties.cpp
index fee957a..4dc3cd5 100644
--- a/xfa/fwl/cfwl_widgetproperties.cpp
+++ b/xfa/fwl/cfwl_widgetproperties.cpp
@@ -6,12 +6,6 @@
 
 #include "xfa/fwl/cfwl_widgetproperties.h"
 
-CFWL_WidgetProperties::CFWL_WidgetProperties()
-    : m_dwStyles(FWL_WGTSTYLE_Child),
-      m_dwStyleExes(0),
-      m_dwStates(0),
-      m_pThemeProvider(nullptr),
-      m_pParent(nullptr),
-      m_pOwner(nullptr) {}
+CFWL_WidgetProperties::CFWL_WidgetProperties() = default;
 
-CFWL_WidgetProperties::~CFWL_WidgetProperties() {}
+CFWL_WidgetProperties::~CFWL_WidgetProperties() = default;
diff --git a/xfa/fwl/cfwl_widgetproperties.h b/xfa/fwl/cfwl_widgetproperties.h
index 065acad..4148833 100644
--- a/xfa/fwl/cfwl_widgetproperties.h
+++ b/xfa/fwl/cfwl_widgetproperties.h
@@ -9,24 +9,23 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
-#include "xfa/fwl/cfwl_widget.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/fwl_widgetdef.h"
 
-class IFWL_ThemeProvider;
 class CFWL_Widget;
+class IFWL_ThemeProvider;
 
 class CFWL_WidgetProperties {
  public:
   CFWL_WidgetProperties();
   ~CFWL_WidgetProperties();
 
+  uint32_t m_dwStyles = FWL_WGTSTYLE_Child;
+  uint32_t m_dwStyleExes = 0;
+  uint32_t m_dwStates = 0;
   CFX_RectF m_rtWidget;
-  uint32_t m_dwStyles;
-  uint32_t m_dwStyleExes;
-  uint32_t m_dwStates;
-  IFWL_ThemeProvider* m_pThemeProvider;
-  CFWL_Widget* m_pParent;
-  CFWL_Widget* m_pOwner;
+  UnownedPtr<IFWL_ThemeProvider> m_pThemeProvider;
+  CFWL_Widget* m_pParent = nullptr;  // Raw, this class owned by node in tree.
 };
 
 #endif  // XFA_FWL_CFWL_WIDGETPROPERTIES_H_
diff --git a/xfa/fwl/cfx_barcode.cpp b/xfa/fwl/cfx_barcode.cpp
deleted file mode 100644
index 9d667c6..0000000
--- a/xfa/fwl/cfx_barcode.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fwl/cfx_barcode.h"
-
-#include <memory>
-
-#include "fxbarcode/cbc_codabar.h"
-#include "fxbarcode/cbc_code128.h"
-#include "fxbarcode/cbc_code39.h"
-#include "fxbarcode/cbc_codebase.h"
-#include "fxbarcode/cbc_datamatrix.h"
-#include "fxbarcode/cbc_ean13.h"
-#include "fxbarcode/cbc_ean8.h"
-#include "fxbarcode/cbc_pdf417i.h"
-#include "fxbarcode/cbc_qrcode.h"
-#include "fxbarcode/cbc_upca.h"
-#include "fxbarcode/utils.h"
-#include "third_party/base/ptr_util.h"
-
-namespace {
-
-std::unique_ptr<CBC_CodeBase> CreateBarCodeEngineObject(BC_TYPE type) {
-  switch (type) {
-    case BC_CODE39:
-      return pdfium::MakeUnique<CBC_Code39>();
-    case BC_CODABAR:
-      return pdfium::MakeUnique<CBC_Codabar>();
-    case BC_CODE128:
-      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_B);
-    case BC_CODE128_B:
-      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_B);
-    case BC_CODE128_C:
-      return pdfium::MakeUnique<CBC_Code128>(BC_CODE128_C);
-    case BC_EAN8:
-      return pdfium::MakeUnique<CBC_EAN8>();
-    case BC_UPCA:
-      return pdfium::MakeUnique<CBC_UPCA>();
-    case BC_EAN13:
-      return pdfium::MakeUnique<CBC_EAN13>();
-    case BC_QR_CODE:
-      return pdfium::MakeUnique<CBC_QRCode>();
-    case BC_PDF417:
-      return pdfium::MakeUnique<CBC_PDF417I>();
-    case BC_DATAMATRIX:
-      return pdfium::MakeUnique<CBC_DataMatrix>();
-    case BC_UNKNOWN:
-    default:
-      return nullptr;
-  }
-}
-
-}  // namespace
-
-CFX_Barcode::CFX_Barcode() {}
-
-CFX_Barcode::~CFX_Barcode() {}
-
-bool CFX_Barcode::Create(BC_TYPE type) {
-  m_pBCEngine = CreateBarCodeEngineObject(type);
-  return !!m_pBCEngine;
-}
-
-BC_TYPE CFX_Barcode::GetType() {
-  return m_pBCEngine ? m_pBCEngine->GetType() : BC_UNKNOWN;
-}
-
-bool CFX_Barcode::SetCharEncoding(BC_CHAR_ENCODING encoding) {
-  return m_pBCEngine ? m_pBCEngine->SetCharEncoding(encoding) : false;
-}
-
-bool CFX_Barcode::SetModuleHeight(int32_t moduleHeight) {
-  return m_pBCEngine ? m_pBCEngine->SetModuleHeight(moduleHeight) : false;
-}
-
-bool CFX_Barcode::SetModuleWidth(int32_t moduleWidth) {
-  return m_pBCEngine ? m_pBCEngine->SetModuleWidth(moduleWidth) : false;
-}
-
-bool CFX_Barcode::SetHeight(int32_t height) {
-  return m_pBCEngine ? m_pBCEngine->SetHeight(height) : false;
-}
-
-bool CFX_Barcode::SetWidth(int32_t width) {
-  return m_pBCEngine ? m_pBCEngine->SetWidth(width) : false;
-}
-
-bool CFX_Barcode::SetPrintChecksum(bool checksum) {
-  switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetPrintChecksum(checksum),
-                            true)
-                         : false;
-    default:
-      return false;
-  }
-}
-
-bool CFX_Barcode::SetDataLength(int32_t length) {
-  switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetDataLength(length),
-                            true)
-                         : false;
-    default:
-      return false;
-  }
-}
-
-bool CFX_Barcode::SetCalChecksum(bool state) {
-  switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetCalChecksum(state),
-                            true)
-                         : false;
-    default:
-      return false;
-  }
-}
-
-bool CFX_Barcode::SetFont(CFX_Font* pFont) {
-  switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine
-                 ? static_cast<CBC_OneCode*>(m_pBCEngine.get())->SetFont(pFont)
-                 : false;
-    default:
-      return false;
-  }
-}
-
-bool CFX_Barcode::SetFontSize(float size) {
-  switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetFontSize(size),
-                            true)
-                         : false;
-    default:
-      return false;
-  }
-}
-
-bool CFX_Barcode::SetFontColor(FX_ARGB color) {
-  switch (GetType()) {
-    case BC_CODE39:
-    case BC_CODABAR:
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-    case BC_EAN8:
-    case BC_EAN13:
-    case BC_UPCA:
-      return m_pBCEngine ? (static_cast<CBC_OneCode*>(m_pBCEngine.get())
-                                ->SetFontColor(color),
-                            true)
-                         : false;
-    default:
-      return false;
-  }
-}
-
-bool CFX_Barcode::SetTextLocation(BC_TEXT_LOC location) {
-  typedef bool (CBC_CodeBase::*memptrtype)(BC_TEXT_LOC);
-  memptrtype memptr = nullptr;
-  switch (GetType()) {
-    case BC_CODE39:
-      memptr = (memptrtype)&CBC_Code39::SetTextLocation;
-      break;
-    case BC_CODABAR:
-      memptr = (memptrtype)&CBC_Codabar::SetTextLocation;
-      break;
-    case BC_CODE128:
-    case BC_CODE128_B:
-    case BC_CODE128_C:
-      memptr = (memptrtype)&CBC_Code128::SetTextLocation;
-      break;
-    default:
-      break;
-  }
-  return m_pBCEngine && memptr ? (m_pBCEngine.get()->*memptr)(location) : false;
-}
-
-bool CFX_Barcode::SetWideNarrowRatio(int8_t ratio) {
-  typedef bool (CBC_CodeBase::*memptrtype)(int8_t);
-  memptrtype memptr = nullptr;
-  switch (GetType()) {
-    case BC_CODE39:
-      memptr = (memptrtype)&CBC_Code39::SetWideNarrowRatio;
-      break;
-    case BC_CODABAR:
-      memptr = (memptrtype)&CBC_Codabar::SetWideNarrowRatio;
-      break;
-    default:
-      break;
-  }
-  return m_pBCEngine && memptr ? (m_pBCEngine.get()->*memptr)(ratio) : false;
-}
-
-bool CFX_Barcode::SetStartChar(char start) {
-  typedef bool (CBC_CodeBase::*memptrtype)(char);
-  memptrtype memptr = nullptr;
-  switch (GetType()) {
-    case BC_CODABAR:
-      memptr = (memptrtype)&CBC_Codabar::SetStartChar;
-      break;
-    default:
-      break;
-  }
-  return m_pBCEngine && memptr ? (m_pBCEngine.get()->*memptr)(start) : false;
-}
-
-bool CFX_Barcode::SetEndChar(char end) {
-  typedef bool (CBC_CodeBase::*memptrtype)(char);
-  memptrtype memptr = nullptr;
-  switch (GetType()) {
-    case BC_CODABAR:
-      memptr = (memptrtype)&CBC_Codabar::SetEndChar;
-      break;
-    default:
-      break;
-  }
-  return m_pBCEngine && memptr ? (m_pBCEngine.get()->*memptr)(end) : false;
-}
-
-bool CFX_Barcode::SetErrorCorrectionLevel(int32_t level) {
-  typedef bool (CBC_CodeBase::*memptrtype)(int32_t);
-  memptrtype memptr = nullptr;
-  switch (GetType()) {
-    case BC_QR_CODE:
-      memptr = (memptrtype)&CBC_QRCode::SetErrorCorrectionLevel;
-      break;
-    case BC_PDF417:
-      memptr = (memptrtype)&CBC_PDF417I::SetErrorCorrectionLevel;
-      break;
-    default:
-      return false;
-  }
-  return m_pBCEngine && memptr ? (m_pBCEngine.get()->*memptr)(level) : false;
-}
-
-bool CFX_Barcode::SetTruncated(bool truncated) {
-  typedef void (CBC_CodeBase::*memptrtype)(bool);
-  memptrtype memptr = nullptr;
-  switch (GetType()) {
-    case BC_PDF417:
-      memptr = (memptrtype)&CBC_PDF417I::SetTruncated;
-      break;
-    default:
-      break;
-  }
-  return m_pBCEngine && memptr ? ((m_pBCEngine.get()->*memptr)(truncated), true)
-                               : false;
-}
-
-bool CFX_Barcode::Encode(const WideStringView& contents) {
-  return m_pBCEngine && m_pBCEngine->Encode(contents);
-}
-
-bool CFX_Barcode::RenderDevice(CFX_RenderDevice* device,
-                               const CFX_Matrix* matrix) {
-  return m_pBCEngine && m_pBCEngine->RenderDevice(device, matrix);
-}
diff --git a/xfa/fwl/cfx_barcode.h b/xfa/fwl/cfx_barcode.h
deleted file mode 100644
index eec4648..0000000
--- a/xfa/fwl/cfx_barcode.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_CFX_BARCODE_H_
-#define XFA_FWL_CFX_BARCODE_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxge/fx_dib.h"
-#include "fxbarcode/BC_Library.h"
-
-class CBC_CodeBase;
-class CFX_Font;
-class CFX_RenderDevice;
-class CFX_Matrix;
-
-class CFX_Barcode {
- public:
-  CFX_Barcode();
-  ~CFX_Barcode();
-
-  bool Create(BC_TYPE type);
-  BC_TYPE GetType();
-  bool Encode(const WideStringView& contents);
-
-  bool RenderDevice(CFX_RenderDevice* device, const CFX_Matrix* matrix);
-
-  bool SetCharEncoding(BC_CHAR_ENCODING encoding);
-
-  bool SetModuleHeight(int32_t moduleHeight);
-  bool SetModuleWidth(int32_t moduleWidth);
-
-  bool SetHeight(int32_t height);
-  bool SetWidth(int32_t width);
-
-  bool SetPrintChecksum(bool checksum);
-  bool SetDataLength(int32_t length);
-  bool SetCalChecksum(bool state);
-
-  bool SetFont(CFX_Font* pFont);
-  bool SetFontSize(float size);
-  bool SetFontColor(FX_ARGB color);
-
-  bool SetTextLocation(BC_TEXT_LOC location);
-
-  bool SetWideNarrowRatio(int8_t ratio);
-  bool SetStartChar(char start);
-  bool SetEndChar(char end);
-  bool SetErrorCorrectionLevel(int32_t level);
-  bool SetTruncated(bool truncated);
-
- private:
-  std::unique_ptr<CBC_CodeBase> m_pBCEngine;
-};
-
-#endif  // XFA_FWL_CFX_BARCODE_H_
diff --git a/xfa/fwl/cfx_barcode_unittest.cpp b/xfa/fwl/cfx_barcode_unittest.cpp
deleted file mode 100644
index fe8aee4..0000000
--- a/xfa/fwl/cfx_barcode_unittest.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "xfa/fwl/cfx_barcode.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_string.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
-#include "third_party/base/ptr_util.h"
-
-class BarcodeTest : public testing::Test {
- public:
-  void SetUp() override {
-    BC_Library_Init();
-    barcode_ = pdfium::MakeUnique<CFX_Barcode>();
-
-    auto device = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
-    auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-    if (bitmap->Create(640, 480, FXDIB_Rgb32))
-      bitmap_ = bitmap;
-    ASSERT_TRUE(bitmap_);
-    ASSERT_TRUE(device->Attach(bitmap_, false, nullptr, false));
-    device_ = std::move(device);
-  }
-
-  void TearDown() override {
-    bitmap_.Reset();
-    device_.reset();
-    barcode_.reset();
-    BC_Library_Destroy();
-  }
-
-  CFX_Barcode* barcode() const { return barcode_.get(); }
-
-  bool Create(BC_TYPE type) {
-    if (!barcode_->Create(type))
-      return false;
-
-    barcode_->SetModuleHeight(300);
-    barcode_->SetModuleWidth(420);
-    barcode_->SetHeight(298);
-    barcode_->SetWidth(418);
-    return true;
-  }
-
-  bool RenderDevice() {
-    return barcode_->RenderDevice(device_.get(), &matrix_);
-  }
-
-  std::string BitmapChecksum() {
-    return GenerateMD5Base16(bitmap_->GetBuffer(),
-                             bitmap_->GetPitch() * bitmap_->GetHeight());
-  }
-
- protected:
-  CFX_Matrix matrix_;
-  std::unique_ptr<CFX_Barcode> barcode_;
-  std::unique_ptr<CFX_RenderDevice> device_;
-  RetainPtr<CFX_DIBitmap> bitmap_;
-};
-
-TEST_F(BarcodeTest, Code39) {
-  EXPECT_TRUE(Create(BC_CODE39));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("cd4cd3f36da38ff58d9f621827018903", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, CodaBar) {
-  EXPECT_TRUE(Create(BC_CODABAR));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("481189dc4f86eddb8c42343c9b8ef1dd", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, Code128) {
-  EXPECT_TRUE(Create(BC_CODE128));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("11b21c178a9fd866d8be196c2103b263", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, Code128_B) {
-  EXPECT_TRUE(Create(BC_CODE128_B));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("11b21c178a9fd866d8be196c2103b263", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, Code128_C) {
-  EXPECT_TRUE(Create(BC_CODE128_C));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("6284ec8503d5a948c9518108da33cdd3", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, Ean8) {
-  EXPECT_TRUE(Create(BC_EAN8));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("22d85bcb02d48f48813f02a1cc9cfe8c", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, UPCA) {
-  EXPECT_TRUE(Create(BC_UPCA));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("cce41fc30852744c44b3353059b568b4", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, Ean13) {
-  EXPECT_TRUE(Create(BC_EAN13));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("187091ec1fd1830fc4d41d40a923d4fb", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, Pdf417) {
-  EXPECT_TRUE(Create(BC_PDF417));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("2bdb9b39f20c5763da6a0d7c7b1f6933", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, DataMatrix) {
-  EXPECT_TRUE(Create(BC_DATAMATRIX));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("5e5cd9a680b86fcd4ffd53ed36e3c980", BitmapChecksum());
-}
-
-TEST_F(BarcodeTest, QrCode) {
-  EXPECT_TRUE(Create(BC_QR_CODE));
-  EXPECT_TRUE(barcode()->Encode(L"clams"));
-  RenderDevice();
-  EXPECT_EQ("4751c6e0f67749fabe24f787128decee", BitmapChecksum());
-}
diff --git a/xfa/fwl/fwl_widgetdef.h b/xfa/fwl/fwl_widgetdef.h
index a85ba15..19eb508 100644
--- a/xfa/fwl/fwl_widgetdef.h
+++ b/xfa/fwl/fwl_widgetdef.h
@@ -21,177 +21,178 @@
 #define FWL_WGTSTATE_Invisible (1L << 5)
 #define FWL_WGTSTATE_MAX (6)
 
-#define FWL_VKEY_LButton 0x01
-#define FWL_VKEY_RButton 0x02
-#define FWL_VKEY_MButton 0x04
-#define FWL_VKEY_Back 0x08
-#define FWL_VKEY_Tab 0x09
-#define FWL_VKEY_NewLine 0x0A
-#define FWL_VKEY_Clear 0x0C
-#define FWL_VKEY_Return 0x0D
-#define FWL_VKEY_Shift 0x10
-#define FWL_VKEY_Control 0x11
-#define FWL_VKEY_Menu 0x12
-#define FWL_VKEY_Pause 0x13
-#define FWL_VKEY_Capital 0x14
-#define FWL_VKEY_Kana 0x15
-#define FWL_VKEY_Hangul 0x15
-#define FWL_VKEY_Junja 0x17
-#define FWL_VKEY_Final 0x18
-#define FWL_VKEY_Hanja 0x19
-#define FWL_VKEY_Kanji 0x19
-#define FWL_VKEY_Escape 0x1B
-#define FWL_VKEY_Convert 0x1C
-#define FWL_VKEY_NonConvert 0x1D
-#define FWL_VKEY_Accept 0x1E
-#define FWL_VKEY_ModeChange 0x1F
-#define FWL_VKEY_Space 0x20
-#define FWL_VKEY_Prior 0x21
-#define FWL_VKEY_Next 0x22
-#define FWL_VKEY_End 0x23
-#define FWL_VKEY_Home 0x24
-#define FWL_VKEY_Left 0x25
-#define FWL_VKEY_Up 0x26
-#define FWL_VKEY_Right 0x27
-#define FWL_VKEY_Down 0x28
-#define FWL_VKEY_Select 0x29
-#define FWL_VKEY_Print 0x2A
-#define FWL_VKEY_Execute 0x2B
-#define FWL_VKEY_Snapshot 0x2C
-#define FWL_VKEY_Insert 0x2D
-#define FWL_VKEY_Delete 0x2E
-#define FWL_VKEY_Help 0x2F
-#define FWL_VKEY_0 0x30
-#define FWL_VKEY_1 0x31
-#define FWL_VKEY_2 0x32
-#define FWL_VKEY_3 0x33
-#define FWL_VKEY_4 0x34
-#define FWL_VKEY_5 0x35
-#define FWL_VKEY_6 0x36
-#define FWL_VKEY_7 0x37
-#define FWL_VKEY_8 0x38
-#define FWL_VKEY_9 0x39
-#define FWL_VKEY_A 0x41
-#define FWL_VKEY_B 0x42
-#define FWL_VKEY_C 0x43
-#define FWL_VKEY_D 0x44
-#define FWL_VKEY_E 0x45
-#define FWL_VKEY_F 0x46
-#define FWL_VKEY_G 0x47
-#define FWL_VKEY_H 0x48
-#define FWL_VKEY_I 0x49
-#define FWL_VKEY_J 0x4A
-#define FWL_VKEY_K 0x4B
-#define FWL_VKEY_L 0x4C
-#define FWL_VKEY_M 0x4D
-#define FWL_VKEY_N 0x4E
-#define FWL_VKEY_O 0x4F
-#define FWL_VKEY_P 0x50
-#define FWL_VKEY_Q 0x51
-#define FWL_VKEY_R 0x52
-#define FWL_VKEY_S 0x53
-#define FWL_VKEY_T 0x54
-#define FWL_VKEY_U 0x55
-#define FWL_VKEY_V 0x56
-#define FWL_VKEY_W 0x57
-#define FWL_VKEY_X 0x58
-#define FWL_VKEY_Y 0x59
-#define FWL_VKEY_Z 0x5A
-#define FWL_VKEY_LWin 0x5B
-#define FWL_VKEY_Command 0x5B
-#define FWL_VKEY_RWin 0x5C
-#define FWL_VKEY_Apps 0x5D
-#define FWL_VKEY_Sleep 0x5F
-#define FWL_VKEY_NumPad0 0x60
-#define FWL_VKEY_NumPad1 0x61
-#define FWL_VKEY_NumPad2 0x62
-#define FWL_VKEY_NumPad3 0x63
-#define FWL_VKEY_NumPad4 0x64
-#define FWL_VKEY_NumPad5 0x65
-#define FWL_VKEY_NumPad6 0x66
-#define FWL_VKEY_NumPad7 0x67
-#define FWL_VKEY_NumPad8 0x68
-#define FWL_VKEY_NumPad9 0x69
-#define FWL_VKEY_Multiply 0x6A
-#define FWL_VKEY_Add 0x6B
-#define FWL_VKEY_Separator 0x6C
-#define FWL_VKEY_Subtract 0x6D
-#define FWL_VKEY_Decimal 0x6E
-#define FWL_VKEY_Divide 0x6F
-#define FWL_VKEY_F1 0x70
-#define FWL_VKEY_F2 0x71
-#define FWL_VKEY_F3 0x72
-#define FWL_VKEY_F4 0x73
-#define FWL_VKEY_F5 0x74
-#define FWL_VKEY_F6 0x75
-#define FWL_VKEY_F7 0x76
-#define FWL_VKEY_F8 0x77
-#define FWL_VKEY_F9 0x78
-#define FWL_VKEY_F10 0x79
-#define FWL_VKEY_F11 0x7A
-#define FWL_VKEY_F12 0x7B
-#define FWL_VKEY_F13 0x7C
-#define FWL_VKEY_F14 0x7D
-#define FWL_VKEY_F15 0x7E
-#define FWL_VKEY_F16 0x7F
-#define FWL_VKEY_F17 0x80
-#define FWL_VKEY_F18 0x81
-#define FWL_VKEY_F19 0x82
-#define FWL_VKEY_F20 0x83
-#define FWL_VKEY_F21 0x84
-#define FWL_VKEY_F22 0x85
-#define FWL_VKEY_F23 0x86
-#define FWL_VKEY_F24 0x87
-#define FWL_VKEY_NunLock 0x90
-#define FWL_VKEY_Scroll 0x91
-#define FWL_VKEY_LShift 0xA0
-#define FWL_VKEY_RShift 0xA1
-#define FWL_VKEY_LControl 0xA2
-#define FWL_VKEY_RControl 0xA3
-#define FWL_VKEY_LMenu 0xA4
-#define FWL_VKEY_RMenu 0xA5
-#define FWL_VKEY_BROWSER_Back 0xA6
-#define FWL_VKEY_BROWSER_Forward 0xA7
-#define FWL_VKEY_BROWSER_Refresh 0xA8
-#define FWL_VKEY_BROWSER_Stop 0xA9
-#define FWL_VKEY_BROWSER_Search 0xAA
-#define FWL_VKEY_BROWSER_Favorites 0xAB
-#define FWL_VKEY_BROWSER_Home 0xAC
-#define FWL_VKEY_VOLUME_Mute 0xAD
-#define FWL_VKEY_VOLUME_Down 0xAE
-#define FWL_VKEY_VOLUME_Up 0xAF
-#define FWL_VKEY_MEDIA_NEXT_Track 0xB0
-#define FWL_VKEY_MEDIA_PREV_Track 0xB1
-#define FWL_VKEY_MEDIA_Stop 0xB2
-#define FWL_VKEY_MEDIA_PLAY_Pause 0xB3
-#define FWL_VKEY_MEDIA_LAUNCH_Mail 0xB4
-#define FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select 0xB5
-#define FWL_VKEY_MEDIA_LAUNCH_APP1 0xB6
-#define FWL_VKEY_MEDIA_LAUNCH_APP2 0xB7
-#define FWL_VKEY_OEM_1 0xBA
-#define FWL_VKEY_OEM_Plus 0xBB
-#define FWL_VKEY_OEM_Comma 0xBC
-#define FWL_VKEY_OEM_Minus 0xBD
-#define FWL_VKEY_OEM_Period 0xBE
-#define FWL_VKEY_OEM_2 0xBF
-#define FWL_VKEY_OEM_3 0xC0
-#define FWL_VKEY_OEM_4 0xDB
-#define FWL_VKEY_OEM_5 0xDC
-#define FWL_VKEY_OEM_6 0xDD
-#define FWL_VKEY_OEM_7 0xDE
-#define FWL_VKEY_OEM_8 0xDF
-#define FWL_VKEY_OEM_102 0xE2
-#define FWL_VKEY_ProcessKey 0xE5
-#define FWL_VKEY_Packet 0xE7
-#define FWL_VKEY_Attn 0xF6
-#define FWL_VKEY_Crsel 0xF7
-#define FWL_VKEY_Exsel 0xF8
-#define FWL_VKEY_Ereof 0xF9
-#define FWL_VKEY_Play 0xFA
-#define FWL_VKEY_Zoom 0xFB
-#define FWL_VKEY_NoName 0xFC
-#define FWL_VKEY_PA1 0xFD
-#define FWL_VKEY_OEM_Clear 0xFE
-#define FWL_VKEY_Unknown 0
+// Same as enum FWL_VKEYCODE in public/fpdf_fwlevent.h, but duplicated to keep
+// xfa/fwl standalone.
+enum XFA_FWL_VKEYCODE {
+  XFA_FWL_VKEY_Back = 0x08,
+  XFA_FWL_VKEY_Tab = 0x09,
+  XFA_FWL_VKEY_NewLine = 0x0A,
+  XFA_FWL_VKEY_Clear = 0x0C,
+  XFA_FWL_VKEY_Return = 0x0D,
+  XFA_FWL_VKEY_Shift = 0x10,
+  XFA_FWL_VKEY_Control = 0x11,
+  XFA_FWL_VKEY_Menu = 0x12,
+  XFA_FWL_VKEY_Pause = 0x13,
+  XFA_FWL_VKEY_Capital = 0x14,
+  XFA_FWL_VKEY_Kana = 0x15,
+  XFA_FWL_VKEY_Hangul = 0x15,
+  XFA_FWL_VKEY_Junja = 0x17,
+  XFA_FWL_VKEY_Final = 0x18,
+  XFA_FWL_VKEY_Hanja = 0x19,
+  XFA_FWL_VKEY_Kanji = 0x19,
+  XFA_FWL_VKEY_Escape = 0x1B,
+  XFA_FWL_VKEY_Convert = 0x1C,
+  XFA_FWL_VKEY_NonConvert = 0x1D,
+  XFA_FWL_VKEY_Accept = 0x1E,
+  XFA_FWL_VKEY_ModeChange = 0x1F,
+  XFA_FWL_VKEY_Space = 0x20,
+  XFA_FWL_VKEY_Prior = 0x21,
+  XFA_FWL_VKEY_Next = 0x22,
+  XFA_FWL_VKEY_End = 0x23,
+  XFA_FWL_VKEY_Home = 0x24,
+  XFA_FWL_VKEY_Left = 0x25,
+  XFA_FWL_VKEY_Up = 0x26,
+  XFA_FWL_VKEY_Right = 0x27,
+  XFA_FWL_VKEY_Down = 0x28,
+  XFA_FWL_VKEY_Select = 0x29,
+  XFA_FWL_VKEY_Print = 0x2A,
+  XFA_FWL_VKEY_Execute = 0x2B,
+  XFA_FWL_VKEY_Snapshot = 0x2C,
+  XFA_FWL_VKEY_Insert = 0x2D,
+  XFA_FWL_VKEY_Delete = 0x2E,
+  XFA_FWL_VKEY_Help = 0x2F,
+  XFA_FWL_VKEY_0 = 0x30,
+  XFA_FWL_VKEY_1 = 0x31,
+  XFA_FWL_VKEY_2 = 0x32,
+  XFA_FWL_VKEY_3 = 0x33,
+  XFA_FWL_VKEY_4 = 0x34,
+  XFA_FWL_VKEY_5 = 0x35,
+  XFA_FWL_VKEY_6 = 0x36,
+  XFA_FWL_VKEY_7 = 0x37,
+  XFA_FWL_VKEY_8 = 0x38,
+  XFA_FWL_VKEY_9 = 0x39,
+  XFA_FWL_VKEY_A = 0x41,
+  XFA_FWL_VKEY_B = 0x42,
+  XFA_FWL_VKEY_C = 0x43,
+  XFA_FWL_VKEY_D = 0x44,
+  XFA_FWL_VKEY_E = 0x45,
+  XFA_FWL_VKEY_F = 0x46,
+  XFA_FWL_VKEY_G = 0x47,
+  XFA_FWL_VKEY_H = 0x48,
+  XFA_FWL_VKEY_I = 0x49,
+  XFA_FWL_VKEY_J = 0x4A,
+  XFA_FWL_VKEY_K = 0x4B,
+  XFA_FWL_VKEY_L = 0x4C,
+  XFA_FWL_VKEY_M = 0x4D,
+  XFA_FWL_VKEY_N = 0x4E,
+  XFA_FWL_VKEY_O = 0x4F,
+  XFA_FWL_VKEY_P = 0x50,
+  XFA_FWL_VKEY_Q = 0x51,
+  XFA_FWL_VKEY_R = 0x52,
+  XFA_FWL_VKEY_S = 0x53,
+  XFA_FWL_VKEY_T = 0x54,
+  XFA_FWL_VKEY_U = 0x55,
+  XFA_FWL_VKEY_V = 0x56,
+  XFA_FWL_VKEY_W = 0x57,
+  XFA_FWL_VKEY_X = 0x58,
+  XFA_FWL_VKEY_Y = 0x59,
+  XFA_FWL_VKEY_Z = 0x5A,
+  XFA_FWL_VKEY_LWin = 0x5B,
+  XFA_FWL_VKEY_Command = 0x5B,
+  XFA_FWL_VKEY_RWin = 0x5C,
+  XFA_FWL_VKEY_Apps = 0x5D,
+  XFA_FWL_VKEY_Sleep = 0x5F,
+  XFA_FWL_VKEY_NumPad0 = 0x60,
+  XFA_FWL_VKEY_NumPad1 = 0x61,
+  XFA_FWL_VKEY_NumPad2 = 0x62,
+  XFA_FWL_VKEY_NumPad3 = 0x63,
+  XFA_FWL_VKEY_NumPad4 = 0x64,
+  XFA_FWL_VKEY_NumPad5 = 0x65,
+  XFA_FWL_VKEY_NumPad6 = 0x66,
+  XFA_FWL_VKEY_NumPad7 = 0x67,
+  XFA_FWL_VKEY_NumPad8 = 0x68,
+  XFA_FWL_VKEY_NumPad9 = 0x69,
+  XFA_FWL_VKEY_Multiply = 0x6A,
+  XFA_FWL_VKEY_Add = 0x6B,
+  XFA_FWL_VKEY_Separator = 0x6C,
+  XFA_FWL_VKEY_Subtract = 0x6D,
+  XFA_FWL_VKEY_Decimal = 0x6E,
+  XFA_FWL_VKEY_Divide = 0x6F,
+  XFA_FWL_VKEY_F1 = 0x70,
+  XFA_FWL_VKEY_F2 = 0x71,
+  XFA_FWL_VKEY_F3 = 0x72,
+  XFA_FWL_VKEY_F4 = 0x73,
+  XFA_FWL_VKEY_F5 = 0x74,
+  XFA_FWL_VKEY_F6 = 0x75,
+  XFA_FWL_VKEY_F7 = 0x76,
+  XFA_FWL_VKEY_F8 = 0x77,
+  XFA_FWL_VKEY_F9 = 0x78,
+  XFA_FWL_VKEY_F10 = 0x79,
+  XFA_FWL_VKEY_F11 = 0x7A,
+  XFA_FWL_VKEY_F12 = 0x7B,
+  XFA_FWL_VKEY_F13 = 0x7C,
+  XFA_FWL_VKEY_F14 = 0x7D,
+  XFA_FWL_VKEY_F15 = 0x7E,
+  XFA_FWL_VKEY_F16 = 0x7F,
+  XFA_FWL_VKEY_F17 = 0x80,
+  XFA_FWL_VKEY_F18 = 0x81,
+  XFA_FWL_VKEY_F19 = 0x82,
+  XFA_FWL_VKEY_F20 = 0x83,
+  XFA_FWL_VKEY_F21 = 0x84,
+  XFA_FWL_VKEY_F22 = 0x85,
+  XFA_FWL_VKEY_F23 = 0x86,
+  XFA_FWL_VKEY_F24 = 0x87,
+  XFA_FWL_VKEY_NunLock = 0x90,
+  XFA_FWL_VKEY_Scroll = 0x91,
+  XFA_FWL_VKEY_LShift = 0xA0,
+  XFA_FWL_VKEY_RShift = 0xA1,
+  XFA_FWL_VKEY_LControl = 0xA2,
+  XFA_FWL_VKEY_RControl = 0xA3,
+  XFA_FWL_VKEY_LMenu = 0xA4,
+  XFA_FWL_VKEY_RMenu = 0xA5,
+  XFA_FWL_VKEY_BROWSER_Back = 0xA6,
+  XFA_FWL_VKEY_BROWSER_Forward = 0xA7,
+  XFA_FWL_VKEY_BROWSER_Refresh = 0xA8,
+  XFA_FWL_VKEY_BROWSER_Stop = 0xA9,
+  XFA_FWL_VKEY_BROWSER_Search = 0xAA,
+  XFA_FWL_VKEY_BROWSER_Favorites = 0xAB,
+  XFA_FWL_VKEY_BROWSER_Home = 0xAC,
+  XFA_FWL_VKEY_VOLUME_Mute = 0xAD,
+  XFA_FWL_VKEY_VOLUME_Down = 0xAE,
+  XFA_FWL_VKEY_VOLUME_Up = 0xAF,
+  XFA_FWL_VKEY_MEDIA_NEXT_Track = 0xB0,
+  XFA_FWL_VKEY_MEDIA_PREV_Track = 0xB1,
+  XFA_FWL_VKEY_MEDIA_Stop = 0xB2,
+  XFA_FWL_VKEY_MEDIA_PLAY_Pause = 0xB3,
+  XFA_FWL_VKEY_MEDIA_LAUNCH_Mail = 0xB4,
+  XFA_FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select = 0xB5,
+  XFA_FWL_VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
+  XFA_FWL_VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
+  XFA_FWL_VKEY_OEM_1 = 0xBA,
+  XFA_FWL_VKEY_OEM_Plus = 0xBB,
+  XFA_FWL_VKEY_OEM_Comma = 0xBC,
+  XFA_FWL_VKEY_OEM_Minus = 0xBD,
+  XFA_FWL_VKEY_OEM_Period = 0xBE,
+  XFA_FWL_VKEY_OEM_2 = 0xBF,
+  XFA_FWL_VKEY_OEM_3 = 0xC0,
+  XFA_FWL_VKEY_OEM_4 = 0xDB,
+  XFA_FWL_VKEY_OEM_5 = 0xDC,
+  XFA_FWL_VKEY_OEM_6 = 0xDD,
+  XFA_FWL_VKEY_OEM_7 = 0xDE,
+  XFA_FWL_VKEY_OEM_8 = 0xDF,
+  XFA_FWL_VKEY_OEM_102 = 0xE2,
+  XFA_FWL_VKEY_ProcessKey = 0xE5,
+  XFA_FWL_VKEY_Packet = 0xE7,
+  XFA_FWL_VKEY_Attn = 0xF6,
+  XFA_FWL_VKEY_Crsel = 0xF7,
+  XFA_FWL_VKEY_Exsel = 0xF8,
+  XFA_FWL_VKEY_Ereof = 0xF9,
+  XFA_FWL_VKEY_Play = 0xFA,
+  XFA_FWL_VKEY_Zoom = 0xFB,
+  XFA_FWL_VKEY_NoName = 0xFC,
+  XFA_FWL_VKEY_PA1 = 0xFD,
+  XFA_FWL_VKEY_OEM_Clear = 0xFE,
+  XFA_FWL_VKEY_Unknown = 0,
+};
 
 #endif  // XFA_FWL_FWL_WIDGETDEF_H_
diff --git a/xfa/fwl/ifwl_adaptertimermgr.h b/xfa/fwl/ifwl_adaptertimermgr.h
deleted file mode 100644
index 1f65589..0000000
--- a/xfa/fwl/ifwl_adaptertimermgr.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FWL_IFWL_ADAPTERTIMERMGR_H_
-#define XFA_FWL_IFWL_ADAPTERTIMERMGR_H_
-
-#include "xfa/fwl/cfwl_timer.h"
-
-class IFWL_AdapterTimerMgr {
- public:
-  virtual void Start(CFWL_Timer* pTimer,
-                     uint32_t dwElapse,
-                     bool bImmediately,
-                     CFWL_TimerInfo** pTimerInfo) = 0;
-  virtual void Stop(CFWL_TimerInfo* pTimerInfo) = 0;
-};
-
-#endif  // XFA_FWL_IFWL_ADAPTERTIMERMGR_H_
diff --git a/xfa/fwl/ifwl_themeprovider.h b/xfa/fwl/ifwl_themeprovider.h
index eba3085..5700d34 100644
--- a/xfa/fwl/ifwl_themeprovider.h
+++ b/xfa/fwl/ifwl_themeprovider.h
@@ -19,20 +19,23 @@
 
 class IFWL_ThemeProvider {
  public:
-  virtual ~IFWL_ThemeProvider() {}
+  virtual ~IFWL_ThemeProvider() = default;
 
-  virtual void DrawBackground(CFWL_ThemeBackground* pParams) = 0;
-  virtual void DrawText(CFWL_ThemeText* pParams) = 0;
-  virtual void CalcTextRect(CFWL_ThemeText* pParams, CFX_RectF& rect) = 0;
+  virtual void DrawBackground(const CFWL_ThemeBackground& pParams) = 0;
+  virtual void DrawText(const CFWL_ThemeText& pParams) = 0;
+  virtual void CalcTextRect(const CFWL_ThemeText& pParams,
+                            CFX_RectF* pRect) = 0;
   virtual float GetCXBorderSize() const = 0;
   virtual float GetCYBorderSize() const = 0;
-  virtual CFX_RectF GetUIMargin(CFWL_ThemePart* pThemePart) const = 0;
-  virtual float GetFontSize(CFWL_ThemePart* pThemePart) const = 0;
-  virtual RetainPtr<CFGAS_GEFont> GetFont(CFWL_ThemePart* pThemePart) const = 0;
-  virtual float GetLineHeight(CFWL_ThemePart* pThemePart) const = 0;
+  virtual CFX_RectF GetUIMargin(const CFWL_ThemePart& pThemePart) const = 0;
+  virtual float GetFontSize(const CFWL_ThemePart& pThemePart) const = 0;
+  virtual RetainPtr<CFGAS_GEFont> GetFont(
+      const CFWL_ThemePart& pThemePart) const = 0;
+  virtual float GetLineHeight(const CFWL_ThemePart& pThemePart) const = 0;
   virtual float GetScrollBarWidth() const = 0;
-  virtual FX_COLORREF GetTextColor(CFWL_ThemePart* pThemePart) const = 0;
-  virtual CFX_SizeF GetSpaceAboveBelow(CFWL_ThemePart* pThemePart) const = 0;
+  virtual FX_COLORREF GetTextColor(const CFWL_ThemePart& pThemePart) const = 0;
+  virtual CFX_SizeF GetSpaceAboveBelow(
+      const CFWL_ThemePart& pThemePart) const = 0;
 };
 
 #endif  // XFA_FWL_IFWL_THEMEPROVIDER_H_
diff --git a/xfa/fwl/ifwl_widgetdelegate.h b/xfa/fwl/ifwl_widgetdelegate.h
index 09bac9a..17b8933 100644
--- a/xfa/fwl/ifwl_widgetdelegate.h
+++ b/xfa/fwl/ifwl_widgetdelegate.h
@@ -16,7 +16,7 @@
 
 class IFWL_WidgetDelegate {
  public:
-  virtual ~IFWL_WidgetDelegate() {}
+  virtual ~IFWL_WidgetDelegate() = default;
 
   virtual void OnProcessMessage(CFWL_Message* pMessage) = 0;
   virtual void OnProcessEvent(CFWL_Event* pEvent) = 0;
diff --git a/xfa/fwl/theme/cfwl_barcodetp.cpp b/xfa/fwl/theme/cfwl_barcodetp.cpp
index cafdf17..222d008 100644
--- a/xfa/fwl/theme/cfwl_barcodetp.cpp
+++ b/xfa/fwl/theme/cfwl_barcodetp.cpp
@@ -14,20 +14,15 @@
 
 CFWL_BarcodeTP::~CFWL_BarcodeTP() {}
 
-void CFWL_BarcodeTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
-
-  switch (pParams->m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+void CFWL_BarcodeTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
+    case CFWL_Part::Border:
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
-    }
-    case CFWL_Part::Background: {
-      FillBackground(pParams->m_pGraphics, &pParams->m_rtPart,
-                     &pParams->m_matrix);
+    case CFWL_Part::Background:
+      FillBackground(pParams.m_pGraphics.Get(), pParams.m_rtPart,
+                     pParams.m_matrix);
       break;
-    }
     default:
       break;
   }
diff --git a/xfa/fwl/theme/cfwl_barcodetp.h b/xfa/fwl/theme/cfwl_barcodetp.h
index 42b41e7..8b88fcd 100644
--- a/xfa/fwl/theme/cfwl_barcodetp.h
+++ b/xfa/fwl/theme/cfwl_barcodetp.h
@@ -10,13 +10,13 @@
 #include "xfa/fwl/theme/cfwl_utils.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_BarcodeTP : public CFWL_WidgetTP {
+class CFWL_BarcodeTP final : public CFWL_WidgetTP {
  public:
   CFWL_BarcodeTP();
   ~CFWL_BarcodeTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 };
 
 #endif  // XFA_FWL_THEME_CFWL_BARCODETP_H_
diff --git a/xfa/fwl/theme/cfwl_carettp.cpp b/xfa/fwl/theme/cfwl_carettp.cpp
index 9735319..8d32a37 100644
--- a/xfa/fwl/theme/cfwl_carettp.cpp
+++ b/xfa/fwl/theme/cfwl_carettp.cpp
@@ -6,27 +6,25 @@
 
 #include "xfa/fwl/theme/cfwl_carettp.h"
 
+#include "core/fxge/render_defines.h"
 #include "xfa/fwl/cfwl_caret.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
-CFWL_CaretTP::CFWL_CaretTP() {}
-CFWL_CaretTP::~CFWL_CaretTP() {}
+CFWL_CaretTP::CFWL_CaretTP() = default;
 
-void CFWL_CaretTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
+CFWL_CaretTP::~CFWL_CaretTP() = default;
 
-  switch (pParams->m_iPart) {
+void CFWL_CaretTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
     case CFWL_Part::Background: {
-      if (!(pParams->m_dwStates & CFWL_PartState_HightLight))
+      if (!(pParams.m_dwStates & CFWL_PartState_HightLight))
         return;
 
-      DrawCaretBK(pParams->m_pGraphics, pParams->m_dwStates,
-                  &(pParams->m_rtPart), (CXFA_GEColor*)pParams->m_pData,
-                  &(pParams->m_matrix));
+      DrawCaretBK(pParams.m_pGraphics.Get(), pParams.m_dwStates,
+                  pParams.m_rtPart, pParams.m_matrix);
       break;
     }
     default:
@@ -36,16 +34,10 @@
 
 void CFWL_CaretTP::DrawCaretBK(CXFA_Graphics* pGraphics,
                                uint32_t dwStates,
-                               const CFX_RectF* pRect,
-                               CXFA_GEColor* crFill,
-                               CFX_Matrix* pMatrix) {
+                               const CFX_RectF& rect,
+                               const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rect = *pRect;
   path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-  if (crFill) {
-    pGraphics->SetFillColor(*crFill);
-  } else {
-    pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(255, 0, 0, 0)));
-  }
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+  pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(255, 0, 0, 0)));
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
 }
diff --git a/xfa/fwl/theme/cfwl_carettp.h b/xfa/fwl/theme/cfwl_carettp.h
index 8b88fee..a0f9dc5 100644
--- a/xfa/fwl/theme/cfwl_carettp.h
+++ b/xfa/fwl/theme/cfwl_carettp.h
@@ -9,20 +9,19 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_CaretTP : public CFWL_WidgetTP {
+class CFWL_CaretTP final : public CFWL_WidgetTP {
  public:
   CFWL_CaretTP();
   ~CFWL_CaretTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
- protected:
+ private:
   void DrawCaretBK(CXFA_Graphics* pGraphics,
                    uint32_t dwStates,
-                   const CFX_RectF* pRect,
-                   CXFA_GEColor* crFill,
-                   CFX_Matrix* pMatrix = nullptr);
+                   const CFX_RectF& rect,
+                   const CFX_Matrix& matrix);
 };
 
 #endif  // XFA_FWL_THEME_CFWL_CARETTP_H_
diff --git a/xfa/fwl/theme/cfwl_checkboxtp.cpp b/xfa/fwl/theme/cfwl_checkboxtp.cpp
index 82fdc1a..3436389 100644
--- a/xfa/fwl/theme/cfwl_checkboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_checkboxtp.cpp
@@ -7,6 +7,7 @@
 #include "xfa/fwl/theme/cfwl_checkboxtp.h"
 
 #include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/render_defines.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_checkbox.h"
@@ -18,7 +19,7 @@
 
 namespace {
 
-const int kSignPath = 100;
+constexpr int kSignPath = 100;
 
 CFX_PointF ScaleBezierPoint(const CFX_PointF& point) {
   CFX_PointF scaled_point(point);
@@ -29,50 +30,31 @@
 
 }  // namespace
 
-#define CHECKBOX_COLOR_BOXLT1 (ArgbEncode(255, 172, 168, 153))
-#define CHECKBOX_COLOR_BOXLT2 (ArgbEncode(255, 113, 111, 100))
-#define CHECKBOX_COLOR_BOXRB1 (ArgbEncode(255, 241, 239, 226))
-#define CHECKBOX_COLOR_BOXRB2 (ArgbEncode(255, 255, 255, 255))
-
-CFWL_CheckBoxTP::CFWL_CheckBoxTP() : m_pThemeData(new CKBThemeData) {
-  SetThemeData();
-}
+CFWL_CheckBoxTP::CFWL_CheckBoxTP() = default;
 
 CFWL_CheckBoxTP::~CFWL_CheckBoxTP() {
   if (m_pCheckPath)
     m_pCheckPath->Clear();
 }
 
-void CFWL_CheckBoxTP::Initialize() {
-  CFWL_WidgetTP::Initialize();
-  InitTTO();
-}
-
-void CFWL_CheckBoxTP::Finalize() {
-  FinalizeTTO();
-  CFWL_WidgetTP::Finalize();
-}
-
-void CFWL_CheckBoxTP::DrawText(CFWL_ThemeText* pParams) {
-  if (!m_pTextOut)
-    return;
-
-  m_pTextOut->SetTextColor(pParams->m_dwStates & CFWL_PartState_Disabled
+void CFWL_CheckBoxTP::DrawText(const CFWL_ThemeText& pParams) {
+  EnsureTTOInitialized();
+  m_pTextOut->SetTextColor(pParams.m_dwStates & CFWL_PartState_Disabled
                                ? FWLTHEME_CAPACITY_TextDisColor
                                : FWLTHEME_CAPACITY_TextColor);
   CFWL_WidgetTP::DrawText(pParams);
 }
 
 void CFWL_CheckBoxTP::DrawSignCheck(CXFA_Graphics* pGraphics,
-                                    const CFX_RectF* pRtSign,
+                                    const CFX_RectF& rtSign,
                                     FX_ARGB argbFill,
-                                    CFX_Matrix* pMatrix) {
+                                    const CFX_Matrix& matrix) {
   if (!m_pCheckPath)
-    InitCheckPath(pRtSign->width);
+    InitCheckPath(rtSign.width);
 
   CFX_Matrix mt;
-  mt.Translate(pRtSign->left, pRtSign->top);
-  mt.Concat(*pMatrix);
+  mt.Translate(rtSign.left, rtSign.top);
+  mt.Concat(matrix);
   pGraphics->SaveGraphState();
   pGraphics->SetFillColor(CXFA_GEColor(argbFill));
   pGraphics->FillPath(m_pCheckPath.get(), FXFILL_WINDING, &mt);
@@ -80,78 +62,77 @@
 }
 
 void CFWL_CheckBoxTP::DrawSignCircle(CXFA_Graphics* pGraphics,
-                                     const CFX_RectF* pRtSign,
+                                     const CFX_RectF& rtSign,
                                      FX_ARGB argbFill,
-                                     CFX_Matrix* pMatrix) {
+                                     const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  path.AddEllipse(*pRtSign);
+  path.AddEllipse(rtSign);
   pGraphics->SaveGraphState();
   pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   pGraphics->RestoreGraphState();
 }
 
 void CFWL_CheckBoxTP::DrawSignCross(CXFA_Graphics* pGraphics,
-                                    const CFX_RectF* pRtSign,
+                                    const CFX_RectF& rtSign,
                                     FX_ARGB argbFill,
-                                    CFX_Matrix* pMatrix) {
+                                    const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  float fRight = pRtSign->right();
-  float fBottom = pRtSign->bottom();
-  path.AddLine(pRtSign->TopLeft(), CFX_PointF(fRight, fBottom));
-  path.AddLine(CFX_PointF(pRtSign->left, fBottom),
-               CFX_PointF(fRight, pRtSign->top));
+  float fRight = rtSign.right();
+  float fBottom = rtSign.bottom();
+  path.AddLine(rtSign.TopLeft(), CFX_PointF(fRight, fBottom));
+  path.AddLine(CFX_PointF(rtSign.left, fBottom),
+               CFX_PointF(fRight, rtSign.top));
 
   pGraphics->SaveGraphState();
   pGraphics->SetStrokeColor(CXFA_GEColor(argbFill));
   pGraphics->SetLineWidth(1.0f);
-  pGraphics->StrokePath(&path, pMatrix);
+  pGraphics->StrokePath(&path, &matrix);
   pGraphics->RestoreGraphState();
 }
 
 void CFWL_CheckBoxTP::DrawSignDiamond(CXFA_Graphics* pGraphics,
-                                      const CFX_RectF* pRtSign,
+                                      const CFX_RectF& rtSign,
                                       FX_ARGB argbFill,
-                                      CFX_Matrix* pMatrix) {
+                                      const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  float fWidth = pRtSign->width;
-  float fHeight = pRtSign->height;
-  float fBottom = pRtSign->bottom();
-  path.MoveTo(CFX_PointF(pRtSign->left + fWidth / 2, pRtSign->top));
-  path.LineTo(CFX_PointF(pRtSign->left, pRtSign->top + fHeight / 2));
-  path.LineTo(CFX_PointF(pRtSign->left + fWidth / 2, fBottom));
-  path.LineTo(CFX_PointF(pRtSign->right(), pRtSign->top + fHeight / 2));
-  path.LineTo(CFX_PointF(pRtSign->left + fWidth / 2, pRtSign->top));
+  float fWidth = rtSign.width;
+  float fHeight = rtSign.height;
+  float fBottom = rtSign.bottom();
+  path.MoveTo(CFX_PointF(rtSign.left + fWidth / 2, rtSign.top));
+  path.LineTo(CFX_PointF(rtSign.left, rtSign.top + fHeight / 2));
+  path.LineTo(CFX_PointF(rtSign.left + fWidth / 2, fBottom));
+  path.LineTo(CFX_PointF(rtSign.right(), rtSign.top + fHeight / 2));
+  path.LineTo(CFX_PointF(rtSign.left + fWidth / 2, rtSign.top));
 
   pGraphics->SaveGraphState();
   pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   pGraphics->RestoreGraphState();
 }
 
 void CFWL_CheckBoxTP::DrawSignSquare(CXFA_Graphics* pGraphics,
-                                     const CFX_RectF* pRtSign,
+                                     const CFX_RectF& rtSign,
                                      FX_ARGB argbFill,
-                                     CFX_Matrix* pMatrix) {
+                                     const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  path.AddRectangle(pRtSign->left, pRtSign->top, pRtSign->width,
-                    pRtSign->height);
+  path.AddRectangle(rtSign.left, rtSign.top, rtSign.width, rtSign.height);
   pGraphics->SaveGraphState();
   pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   pGraphics->RestoreGraphState();
 }
 
 void CFWL_CheckBoxTP::DrawSignStar(CXFA_Graphics* pGraphics,
-                                   const CFX_RectF* pRtSign,
+                                   const CFX_RectF& rtSign,
                                    FX_ARGB argbFill,
-                                   CFX_Matrix* pMatrix) {
+                                   const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  float fBottom = pRtSign->bottom();
+  float fBottom = rtSign.bottom();
   float fRadius =
-      (pRtSign->top - fBottom) / (1 + static_cast<float>(cos(FX_PI / 5.0f)));
-  CFX_PointF ptCenter((pRtSign->left + pRtSign->right()) / 2.0f,
-                      (pRtSign->top + fBottom) / 2.0f);
+      (rtSign.top - fBottom) / (1 + static_cast<float>(cos(FX_PI / 5.0f)));
+  CFX_PointF ptCenter((rtSign.left + rtSign.right()) / 2.0f,
+                      (rtSign.top + fBottom) / 2.0f);
 
   CFX_PointF points[5];
   float fAngel = FX_PI / 10.0f;
@@ -173,48 +154,10 @@
   }
   pGraphics->SaveGraphState();
   pGraphics->SetFillColor(CXFA_GEColor(argbFill));
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   pGraphics->RestoreGraphState();
 }
 
-void CFWL_CheckBoxTP::SetThemeData() {
-  uint32_t* pData = (uint32_t*)&m_pThemeData->clrBoxBk;
-
-  *pData++ = 0;
-  *pData++ = 0;
-  *pData++ = ArgbEncode(255, 220, 220, 215),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 255, 240, 207),
-  *pData++ = ArgbEncode(255, 248, 179, 48),
-  *pData++ = ArgbEncode(255, 176, 176, 167),
-  *pData++ = ArgbEncode(255, 241, 239, 239),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 220, 220, 215),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 255, 240, 207),
-  *pData++ = ArgbEncode(255, 248, 179, 48),
-  *pData++ = ArgbEncode(255, 176, 176, 167),
-  *pData++ = ArgbEncode(255, 241, 239, 239),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 220, 220, 215),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 255, 240, 207),
-  *pData++ = ArgbEncode(255, 248, 179, 48),
-  *pData++ = ArgbEncode(255, 176, 176, 167),
-  *pData++ = ArgbEncode(255, 241, 239, 239),
-  *pData++ = ArgbEncode(255, 255, 255, 255),
-  *pData++ = ArgbEncode(255, 255, 255, 255);
-  m_pThemeData->clrSignBorderNormal = ArgbEncode(255, 28, 81, 128);
-  m_pThemeData->clrSignBorderDisable = ArgbEncode(255, 202, 200, 187);
-  m_pThemeData->clrSignCheck = ArgbEncode(255, 28, 81, 128);
-  m_pThemeData->clrSignNeutral = ArgbEncode(255, 28, 134, 26);
-  m_pThemeData->clrSignNeutralNormal = ArgbEncode(255, 114, 192, 113);
-  m_pThemeData->clrSignNeutralHover = ArgbEncode(255, 33, 161, 33);
-  m_pThemeData->clrSignNeutralPressed = ArgbEncode(255, 28, 134, 26);
-}
-
 void CFWL_CheckBoxTP::InitCheckPath(float fCheckLen) {
   if (!m_pCheckPath) {
     m_pCheckPath = pdfium::MakeUnique<CXFA_GEPath>();
@@ -269,14 +212,14 @@
   }
 }
 
-void CFWL_CheckBoxTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (pParams->m_iPart != CFWL_Part::CheckBox)
+void CFWL_CheckBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  if (pParams.m_iPart != CFWL_Part::CheckBox)
     return;
 
-  if ((pParams->m_dwStates & CFWL_PartState_Checked) ||
-      (pParams->m_dwStates & CFWL_PartState_Neutral)) {
-    DrawCheckSign(pParams->m_pWidget, pParams->m_pGraphics, pParams->m_rtPart,
-                  pParams->m_dwStates, &pParams->m_matrix);
+  if ((pParams.m_dwStates & CFWL_PartState_Checked) ||
+      (pParams.m_dwStates & CFWL_PartState_Neutral)) {
+    DrawCheckSign(pParams.m_pWidget, pParams.m_pGraphics.Get(),
+                  pParams.m_rtPart, pParams.m_dwStates, pParams.m_matrix);
   }
 }
 
@@ -284,7 +227,7 @@
                                     CXFA_Graphics* pGraphics,
                                     const CFX_RectF& pRtBox,
                                     int32_t iState,
-                                    CFX_Matrix* pMatrix) {
+                                    const CFX_Matrix& matrix) {
   CFX_RectF rtSign(pRtBox);
   uint32_t dwColor = iState & CFWL_PartState_Neutral ? 0xFFA9A9A9 : 0xFF000000;
 
@@ -292,22 +235,22 @@
   rtSign.Deflate(rtSign.width / 4, rtSign.height / 4);
   switch (dwStyle & FWL_STYLEEXT_CKB_SignShapeMask) {
     case FWL_STYLEEXT_CKB_SignShapeCheck:
-      DrawSignCheck(pGraphics, &rtSign, dwColor, pMatrix);
+      DrawSignCheck(pGraphics, rtSign, dwColor, matrix);
       break;
     case FWL_STYLEEXT_CKB_SignShapeCircle:
-      DrawSignCircle(pGraphics, &rtSign, dwColor, pMatrix);
+      DrawSignCircle(pGraphics, rtSign, dwColor, matrix);
       break;
     case FWL_STYLEEXT_CKB_SignShapeCross:
-      DrawSignCross(pGraphics, &rtSign, dwColor, pMatrix);
+      DrawSignCross(pGraphics, rtSign, dwColor, matrix);
       break;
     case FWL_STYLEEXT_CKB_SignShapeDiamond:
-      DrawSignDiamond(pGraphics, &rtSign, dwColor, pMatrix);
+      DrawSignDiamond(pGraphics, rtSign, dwColor, matrix);
       break;
     case FWL_STYLEEXT_CKB_SignShapeSquare:
-      DrawSignSquare(pGraphics, &rtSign, dwColor, pMatrix);
+      DrawSignSquare(pGraphics, rtSign, dwColor, matrix);
       break;
     case FWL_STYLEEXT_CKB_SignShapeStar:
-      DrawSignStar(pGraphics, &rtSign, dwColor, pMatrix);
+      DrawSignStar(pGraphics, rtSign, dwColor, matrix);
       break;
     default:
       break;
diff --git a/xfa/fwl/theme/cfwl_checkboxtp.h b/xfa/fwl/theme/cfwl_checkboxtp.h
index e373b38..e79a693 100644
--- a/xfa/fwl/theme/cfwl_checkboxtp.h
+++ b/xfa/fwl/theme/cfwl_checkboxtp.h
@@ -9,69 +9,53 @@
 
 #include <memory>
 
-#include "xfa/fwl/theme/cfwl_utils.h"
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_CheckBoxTP : public CFWL_WidgetTP {
+class CFWL_Widget;
+
+class CFWL_CheckBoxTP final : public CFWL_WidgetTP {
  public:
   CFWL_CheckBoxTP();
   ~CFWL_CheckBoxTP() override;
 
-  // CFWL_WidgeTP
-  void Initialize() override;
-  void Finalize() override;
-  void DrawText(CFWL_ThemeText* pParams) override;
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  // CFWL_WidgetTP
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
+  void DrawText(const CFWL_ThemeText& pParams) override;
 
- protected:
-  struct CKBThemeData {
-    FX_ARGB clrBoxBk[13][2];
-    FX_ARGB clrSignBorderNormal;
-    FX_ARGB clrSignBorderDisable;
-    FX_ARGB clrSignCheck;
-    FX_ARGB clrSignNeutral;
-    FX_ARGB clrSignNeutralNormal;
-    FX_ARGB clrSignNeutralHover;
-    FX_ARGB clrSignNeutralPressed;
-  };
-
+ private:
   void DrawCheckSign(CFWL_Widget* pWidget,
                      CXFA_Graphics* pGraphics,
                      const CFX_RectF& pRtBox,
                      int32_t iState,
-                     CFX_Matrix* pMatrix);
+                     const CFX_Matrix& matrix);
   void DrawSignCheck(CXFA_Graphics* pGraphics,
-                     const CFX_RectF* pRtSign,
+                     const CFX_RectF& rtSign,
                      FX_ARGB argbFill,
-                     CFX_Matrix* pMatrix);
+                     const CFX_Matrix& matrix);
   void DrawSignCircle(CXFA_Graphics* pGraphics,
-                      const CFX_RectF* pRtSign,
+                      const CFX_RectF& rtSign,
                       FX_ARGB argbFill,
-                      CFX_Matrix* pMatrix);
+                      const CFX_Matrix& matrix);
   void DrawSignCross(CXFA_Graphics* pGraphics,
-                     const CFX_RectF* pRtSign,
+                     const CFX_RectF& rtSign,
                      FX_ARGB argbFill,
-                     CFX_Matrix* pMatrix);
+                     const CFX_Matrix& matrix);
   void DrawSignDiamond(CXFA_Graphics* pGraphics,
-                       const CFX_RectF* pRtSign,
+                       const CFX_RectF& rtSign,
                        FX_ARGB argbFill,
-                       CFX_Matrix* pMatrix);
+                       const CFX_Matrix& matrix);
   void DrawSignSquare(CXFA_Graphics* pGraphics,
-                      const CFX_RectF* pRtSign,
+                      const CFX_RectF& rtSign,
                       FX_ARGB argbFill,
-                      CFX_Matrix* pMatrix);
+                      const CFX_Matrix& matrix);
   void DrawSignStar(CXFA_Graphics* pGraphics,
-                    const CFX_RectF* pRtSign,
+                    const CFX_RectF& rtSign,
                     FX_ARGB argbFill,
-                    CFX_Matrix* pMatrix);
+                    const CFX_Matrix& matrix);
 
   void InitCheckPath(float fCheckLen);
 
-  std::unique_ptr<CKBThemeData> m_pThemeData;
   std::unique_ptr<CXFA_GEPath> m_pCheckPath;
-
- private:
-  void SetThemeData();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_CHECKBOXTP_H_
diff --git a/xfa/fwl/theme/cfwl_comboboxtp.cpp b/xfa/fwl/theme/cfwl_comboboxtp.cpp
index 7695342..6cc842d 100644
--- a/xfa/fwl/theme/cfwl_comboboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_comboboxtp.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fwl/theme/cfwl_comboboxtp.h"
 
+#include "core/fxge/render_defines.h"
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
@@ -13,25 +14,22 @@
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
-CFWL_ComboBoxTP::CFWL_ComboBoxTP() {}
+CFWL_ComboBoxTP::CFWL_ComboBoxTP() = default;
 
-CFWL_ComboBoxTP::~CFWL_ComboBoxTP() {}
+CFWL_ComboBoxTP::~CFWL_ComboBoxTP() = default;
 
-void CFWL_ComboBoxTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
-
-  switch (pParams->m_iPart) {
+void CFWL_ComboBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
     case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Background: {
       CXFA_GEPath path;
-      CFX_RectF& rect = pParams->m_rtPart;
+      const CFX_RectF& rect = pParams.m_rtPart;
       path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
       FX_ARGB argb_color;
-      switch (pParams->m_dwStates) {
+      switch (pParams.m_dwStates) {
         case CFWL_PartState_Selected:
           argb_color = FWLTHEME_COLOR_BKSelected;
           break;
@@ -41,18 +39,14 @@
         default:
           argb_color = 0xFFFFFFFF;
       }
-      pParams->m_pGraphics->SaveGraphState();
-      pParams->m_pGraphics->SetFillColor(CXFA_GEColor(argb_color));
-      pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, &pParams->m_matrix);
-      pParams->m_pGraphics->RestoreGraphState();
+      pParams.m_pGraphics->SaveGraphState();
+      pParams.m_pGraphics->SetFillColor(CXFA_GEColor(argb_color));
+      pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &pParams.m_matrix);
+      pParams.m_pGraphics->RestoreGraphState();
       break;
     }
     case CFWL_Part::DropDownButton: {
-      DrawDropDownButton(pParams, pParams->m_dwStates, &pParams->m_matrix);
-      break;
-    }
-    case CFWL_Part::StretchHandler: {
-      DrawStrethHandler(pParams, 0, &pParams->m_matrix);
+      DrawDropDownButton(pParams, pParams.m_dwStates, pParams.m_matrix);
       break;
     }
     default:
@@ -60,20 +54,9 @@
   }
 }
 
-void CFWL_ComboBoxTP::DrawStrethHandler(CFWL_ThemeBackground* pParams,
-                                        uint32_t dwStates,
-                                        CFX_Matrix* pMatrix) {
-  CXFA_GEPath path;
-  path.AddRectangle(pParams->m_rtPart.left, pParams->m_rtPart.top,
-                    pParams->m_rtPart.width - 1, pParams->m_rtPart.height);
-  pParams->m_pGraphics->SetFillColor(
-      CXFA_GEColor(ArgbEncode(0xff, 0xff, 0, 0)));
-  pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, &pParams->m_matrix);
-}
-
-void CFWL_ComboBoxTP::DrawDropDownButton(CFWL_ThemeBackground* pParams,
+void CFWL_ComboBoxTP::DrawDropDownButton(const CFWL_ThemeBackground& pParams,
                                          uint32_t dwStates,
-                                         CFX_Matrix* pMatrix) {
+                                         const CFX_Matrix& matrix) {
   FWLTHEME_STATE eState = FWLTHEME_STATE_Normal;
   switch (dwStates) {
     case CFWL_PartState_Normal: {
@@ -95,6 +78,6 @@
     default:
       break;
   }
-  DrawArrowBtn(pParams->m_pGraphics, &pParams->m_rtPart,
-               FWLTHEME_DIRECTION_Down, eState, &pParams->m_matrix);
+  DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
+               FWLTHEME_DIRECTION_Down, eState, pParams.m_matrix);
 }
diff --git a/xfa/fwl/theme/cfwl_comboboxtp.h b/xfa/fwl/theme/cfwl_comboboxtp.h
index 183d3a0..b3a75a5 100644
--- a/xfa/fwl/theme/cfwl_comboboxtp.h
+++ b/xfa/fwl/theme/cfwl_comboboxtp.h
@@ -9,21 +9,18 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_ComboBoxTP : public CFWL_WidgetTP {
+class CFWL_ComboBoxTP final : public CFWL_WidgetTP {
  public:
   CFWL_ComboBoxTP();
   ~CFWL_ComboBoxTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
- protected:
-  void DrawDropDownButton(CFWL_ThemeBackground* pParams,
+ private:
+  void DrawDropDownButton(const CFWL_ThemeBackground& pParams,
                           uint32_t dwStates,
-                          CFX_Matrix* pMatrix);
-  void DrawStrethHandler(CFWL_ThemeBackground* pParams,
-                         uint32_t dwStates,
-                         CFX_Matrix* pMatrix);
+                          const CFX_Matrix& matrix);
 };
 
 #endif  // XFA_FWL_THEME_CFWL_COMBOBOXTP_H_
diff --git a/xfa/fwl/theme/cfwl_datetimepickertp.cpp b/xfa/fwl/theme/cfwl_datetimepickertp.cpp
index 8902ef9..a185952 100644
--- a/xfa/fwl/theme/cfwl_datetimepickertp.cpp
+++ b/xfa/fwl/theme/cfwl_datetimepickertp.cpp
@@ -13,27 +13,24 @@
 
 CFWL_DateTimePickerTP::~CFWL_DateTimePickerTP() {}
 
-void CFWL_DateTimePickerTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
-
-  switch (pParams->m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+void CFWL_DateTimePickerTP::DrawBackground(
+    const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
+    case CFWL_Part::Border:
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
-    }
-    case CFWL_Part::DropDownButton: {
-      DrawDropDownButton(pParams, &pParams->m_matrix);
+    case CFWL_Part::DropDownButton:
+      DrawDropDownButton(pParams, pParams.m_matrix);
       break;
-    }
     default:
       break;
   }
 }
 
-void CFWL_DateTimePickerTP::DrawDropDownButton(CFWL_ThemeBackground* pParams,
-                                               CFX_Matrix* pMatrix) {
-  uint32_t dwStates = pParams->m_dwStates;
+void CFWL_DateTimePickerTP::DrawDropDownButton(
+    const CFWL_ThemeBackground& pParams,
+    const CFX_Matrix& matrix) {
+  uint32_t dwStates = pParams.m_dwStates;
   dwStates &= 0x03;
   FWLTHEME_STATE eState = FWLTHEME_STATE_Normal;
   switch (eState & dwStates) {
@@ -56,7 +53,6 @@
     default:
       break;
   }
-  DrawArrowBtn(pParams->m_pGraphics, &pParams->m_rtPart,
-               FWLTHEME_DIRECTION_Down, eState, pMatrix);
+  DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
+               FWLTHEME_DIRECTION_Down, eState, matrix);
 }
-
diff --git a/xfa/fwl/theme/cfwl_datetimepickertp.h b/xfa/fwl/theme/cfwl_datetimepickertp.h
index 11536a3..e7163e0 100644
--- a/xfa/fwl/theme/cfwl_datetimepickertp.h
+++ b/xfa/fwl/theme/cfwl_datetimepickertp.h
@@ -9,16 +9,17 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_DateTimePickerTP : public CFWL_WidgetTP {
+class CFWL_DateTimePickerTP final : public CFWL_WidgetTP {
  public:
   CFWL_DateTimePickerTP();
   ~CFWL_DateTimePickerTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
- protected:
-  void DrawDropDownButton(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
+ private:
+  void DrawDropDownButton(const CFWL_ThemeBackground& pParams,
+                          const CFX_Matrix& matrix);
 };
 
 #endif  // XFA_FWL_THEME_CFWL_DATETIMEPICKERTP_H_
diff --git a/xfa/fwl/theme/cfwl_edittp.cpp b/xfa/fwl/theme/cfwl_edittp.cpp
index 599a4dd..cacdf09 100644
--- a/xfa/fwl/theme/cfwl_edittp.cpp
+++ b/xfa/fwl/theme/cfwl_edittp.cpp
@@ -6,77 +6,67 @@
 
 #include "xfa/fwl/theme/cfwl_edittp.h"
 
+#include "core/fxge/render_defines.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_fwltheme.h"
-#include "xfa/fxfa/parser/cxfa_border.h"
-#include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
-CFWL_EditTP::CFWL_EditTP() {}
+CFWL_EditTP::CFWL_EditTP() = default;
 
-CFWL_EditTP::~CFWL_EditTP() {}
+CFWL_EditTP::~CFWL_EditTP() = default;
 
-void CFWL_EditTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (CFWL_Part::CombTextLine == pParams->m_iPart) {
-    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
-    CXFA_Border* borderUI = pWidget->GetNode()->GetWidgetAcc()->GetUIBorder();
+void CFWL_EditTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  if (CFWL_Part::CombTextLine == pParams.m_iPart) {
+    CFWL_Widget::AdapterIface* pWidget =
+        pParams.m_pWidget->GetOutmost()->GetAdapterIface();
     FX_ARGB cr = 0xFF000000;
     float fWidth = 1.0f;
-    if (borderUI) {
-      CXFA_Edge* edge = borderUI->GetEdgeIfExists(0);
-      if (edge) {
-        cr = edge->GetColor();
-        fWidth = edge->GetThickness();
-      }
-    }
-    pParams->m_pGraphics->SetStrokeColor(CXFA_GEColor(cr));
-    pParams->m_pGraphics->SetLineWidth(fWidth);
-    pParams->m_pGraphics->StrokePath(pParams->m_pPath, &pParams->m_matrix);
+    pWidget->GetBorderColorAndThickness(&cr, &fWidth);
+    pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(cr));
+    pParams.m_pGraphics->SetLineWidth(fWidth);
+    pParams.m_pGraphics->StrokePath(pParams.m_pPath.Get(), &pParams.m_matrix);
     return;
   }
 
-  switch (pParams->m_iPart) {
+  switch (pParams.m_iPart) {
     case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Background: {
-      if (pParams->m_pPath) {
-        CXFA_Graphics* pGraphics = pParams->m_pGraphics;
+      if (pParams.m_pPath) {
+        CXFA_Graphics* pGraphics = pParams.m_pGraphics.Get();
         pGraphics->SaveGraphState();
         pGraphics->SetFillColor(CXFA_GEColor(FWLTHEME_COLOR_BKSelected));
-        pGraphics->FillPath(pParams->m_pPath, FXFILL_WINDING,
-                            &pParams->m_matrix);
+        pGraphics->FillPath(pParams.m_pPath.Get(), FXFILL_WINDING,
+                            &pParams.m_matrix);
         pGraphics->RestoreGraphState();
       } else {
         CXFA_GEPath path;
-        path.AddRectangle(pParams->m_rtPart.left, pParams->m_rtPart.top,
-                          pParams->m_rtPart.width, pParams->m_rtPart.height);
+        path.AddRectangle(pParams.m_rtPart.left, pParams.m_rtPart.top,
+                          pParams.m_rtPart.width, pParams.m_rtPart.height);
         CXFA_GEColor cr(FWLTHEME_COLOR_Background);
-        if (!pParams->m_bStaticBackground) {
-          if (pParams->m_dwStates & CFWL_PartState_Disabled)
+        if (!pParams.m_bStaticBackground) {
+          if (pParams.m_dwStates & CFWL_PartState_Disabled)
             cr = CXFA_GEColor(FWLTHEME_COLOR_EDGERB1);
-          else if (pParams->m_dwStates & CFWL_PartState_ReadOnly)
+          else if (pParams.m_dwStates & CFWL_PartState_ReadOnly)
             cr = CXFA_GEColor(ArgbEncode(255, 236, 233, 216));
           else
             cr = CXFA_GEColor(0xFFFFFFFF);
         }
-        pParams->m_pGraphics->SaveGraphState();
-        pParams->m_pGraphics->SetFillColor(cr);
-        pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING,
-                                       &pParams->m_matrix);
-        pParams->m_pGraphics->RestoreGraphState();
+        pParams.m_pGraphics->SaveGraphState();
+        pParams.m_pGraphics->SetFillColor(cr);
+        pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &pParams.m_matrix);
+        pParams.m_pGraphics->RestoreGraphState();
       }
       break;
     }
     case CFWL_Part::CombTextLine: {
-      pParams->m_pGraphics->SetStrokeColor(CXFA_GEColor(0xFF000000));
-      pParams->m_pGraphics->SetLineWidth(1.0f);
-      pParams->m_pGraphics->StrokePath(pParams->m_pPath, &pParams->m_matrix);
+      pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(0xFF000000));
+      pParams.m_pGraphics->SetLineWidth(1.0f);
+      pParams.m_pGraphics->StrokePath(pParams.m_pPath.Get(), &pParams.m_matrix);
       break;
     }
     default:
diff --git a/xfa/fwl/theme/cfwl_edittp.h b/xfa/fwl/theme/cfwl_edittp.h
index e1c6ac9..b5b58f3 100644
--- a/xfa/fwl/theme/cfwl_edittp.h
+++ b/xfa/fwl/theme/cfwl_edittp.h
@@ -9,13 +9,13 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_EditTP : public CFWL_WidgetTP {
+class CFWL_EditTP final : public CFWL_WidgetTP {
  public:
   CFWL_EditTP();
   ~CFWL_EditTP() override;
 
-  // CFWL_WidgeTTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  // CFWL_WidgetTP
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 };
 
 #endif  // XFA_FWL_THEME_CFWL_EDITTP_H_
diff --git a/xfa/fwl/theme/cfwl_listboxtp.cpp b/xfa/fwl/theme/cfwl_listboxtp.cpp
index 6b8aa76..28b5e34 100644
--- a/xfa/fwl/theme/cfwl_listboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_listboxtp.cpp
@@ -6,48 +6,48 @@
 
 #include "xfa/fwl/theme/cfwl_listboxtp.h"
 
+#include "build/build_config.h"
+#include "core/fxge/render_defines.h"
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
-CFWL_ListBoxTP::CFWL_ListBoxTP() {}
+CFWL_ListBoxTP::CFWL_ListBoxTP() = default;
 
-CFWL_ListBoxTP::~CFWL_ListBoxTP() {}
+CFWL_ListBoxTP::~CFWL_ListBoxTP() = default;
 
-void CFWL_ListBoxTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
-
-  switch (pParams->m_iPart) {
+void CFWL_ListBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
     case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Background: {
-      FillSoildRect(pParams->m_pGraphics, ArgbEncode(255, 255, 255, 255),
-                    &pParams->m_rtPart, &pParams->m_matrix);
-      if (pParams->m_pData) {
-        FillSoildRect(pParams->m_pGraphics, FWLTHEME_COLOR_Background,
-                      (CFX_RectF*)pParams->m_pData, &pParams->m_matrix);
+      FillSolidRect(pParams.m_pGraphics.Get(), ArgbEncode(255, 255, 255, 255),
+                    pParams.m_rtPart, pParams.m_matrix);
+      if (pParams.m_pRtData) {
+        FillSolidRect(pParams.m_pGraphics.Get(), FWLTHEME_COLOR_Background,
+                      *pParams.m_pRtData, pParams.m_matrix);
       }
       break;
     }
     case CFWL_Part::ListItem: {
-      DrawListBoxItem(pParams->m_pGraphics, pParams->m_dwStates,
-                      &pParams->m_rtPart, pParams->m_pData, &pParams->m_matrix);
+      DrawListBoxItem(pParams.m_pGraphics.Get(), pParams.m_dwStates,
+                      pParams.m_rtPart, pParams.m_pRtData, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Check: {
       uint32_t color = 0xFF000000;
-      if (pParams->m_dwStates == CFWL_PartState_Checked) {
+      if (pParams.m_dwStates == CFWL_PartState_Checked) {
         color = 0xFFFF0000;
-      } else if (pParams->m_dwStates == CFWL_PartState_Normal) {
+      } else if (pParams.m_dwStates == CFWL_PartState_Normal) {
         color = 0xFF0000FF;
       }
-      FillSoildRect(pParams->m_pGraphics, color, &pParams->m_rtPart,
-                    &pParams->m_matrix);
+      FillSolidRect(pParams.m_pGraphics.Get(), color, pParams.m_rtPart,
+                    pParams.m_matrix);
+      break;
     }
     default:
       break;
@@ -56,22 +56,22 @@
 
 void CFWL_ListBoxTP::DrawListBoxItem(CXFA_Graphics* pGraphics,
                                      uint32_t dwStates,
-                                     const CFX_RectF* prtItem,
-                                     void* pData,
-                                     CFX_Matrix* pMatrix) {
+                                     const CFX_RectF& rtItem,
+                                     const CFX_RectF* pData,
+                                     const CFX_Matrix& matrix) {
   if (dwStates & CFWL_PartState_Selected) {
     pGraphics->SaveGraphState();
     pGraphics->SetFillColor(CXFA_GEColor(FWLTHEME_COLOR_BKSelected));
-    CFX_RectF rt(*prtItem);
     CXFA_GEPath path;
-#if (_FX_OS_ == _FX_OS_MACOSX_)
-    path.AddRectangle(rt.left, rt.top, rt.width - 1, rt.height - 1);
+#if defined(OS_MACOSX)
+    path.AddRectangle(rtItem.left, rtItem.top, rtItem.width - 1,
+                      rtItem.height - 1);
 #else
-    path.AddRectangle(rt.left, rt.top, rt.width, rt.height);
+    path.AddRectangle(rtItem.left, rtItem.top, rtItem.width, rtItem.height);
 #endif
-    pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+    pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
     pGraphics->RestoreGraphState();
   }
-  if (dwStates & CFWL_PartState_Focused && pData)
-    DrawFocus(pGraphics, (CFX_RectF*)pData, pMatrix);
+  if ((dwStates & CFWL_PartState_Focused) && pData)
+    DrawFocus(pGraphics, *pData, matrix);
 }
diff --git a/xfa/fwl/theme/cfwl_listboxtp.h b/xfa/fwl/theme/cfwl_listboxtp.h
index 5dc45af..ee954b8 100644
--- a/xfa/fwl/theme/cfwl_listboxtp.h
+++ b/xfa/fwl/theme/cfwl_listboxtp.h
@@ -9,20 +9,20 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_ListBoxTP : public CFWL_WidgetTP {
+class CFWL_ListBoxTP final : public CFWL_WidgetTP {
  public:
   CFWL_ListBoxTP();
   ~CFWL_ListBoxTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
- protected:
+ private:
   void DrawListBoxItem(CXFA_Graphics* pGraphics,
                        uint32_t dwStates,
-                       const CFX_RectF* prtItem,
-                       void* pData = nullptr,
-                       CFX_Matrix* pMatrix = nullptr);
+                       const CFX_RectF& rtItem,
+                       const CFX_RectF* pData,
+                       const CFX_Matrix& matrix);
 };
 
 #endif  // XFA_FWL_THEME_CFWL_LISTBOXTP_H_
diff --git a/xfa/fwl/theme/cfwl_monthcalendartp.cpp b/xfa/fwl/theme/cfwl_monthcalendartp.cpp
index 6bb3f18..47ba930 100644
--- a/xfa/fwl/theme/cfwl_monthcalendartp.cpp
+++ b/xfa/fwl/theme/cfwl_monthcalendartp.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fwl/theme/cfwl_monthcalendartp.h"
 
+#include "core/fxge/render_defines.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fwl/cfwl_monthcalendar.h"
 #include "xfa/fwl/cfwl_themebackground.h"
@@ -15,69 +16,66 @@
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
-CFWL_MonthCalendarTP::CFWL_MonthCalendarTP() : m_pThemeData(new MCThemeData) {
-  SetThemeData();
-}
+namespace {
 
-CFWL_MonthCalendarTP::~CFWL_MonthCalendarTP() {}
+constexpr FX_ARGB kCaptionColor = ArgbEncode(0xff, 0, 153, 255);
+constexpr FX_ARGB kSeparatorColor = ArgbEncode(0xff, 141, 161, 239);
+constexpr FX_ARGB kDatesHoverBackgroundColor = ArgbEncode(0xff, 193, 211, 251);
+constexpr FX_ARGB kDatesSelectedBackgroundColor =
+    ArgbEncode(0xff, 173, 188, 239);
+constexpr FX_ARGB kDatesCircleColor = ArgbEncode(0xff, 103, 144, 209);
+constexpr FX_ARGB kBackgroundColor = ArgbEncode(0xff, 255, 255, 255);
 
-void CFWL_MonthCalendarTP::Initialize() {
-  CFWL_WidgetTP::Initialize();
-  InitTTO();
-}
+}  // namespace
 
-void CFWL_MonthCalendarTP::Finalize() {
-  FinalizeTTO();
-  CFWL_WidgetTP::Finalize();
-}
+CFWL_MonthCalendarTP::CFWL_MonthCalendarTP() = default;
 
-void CFWL_MonthCalendarTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
+CFWL_MonthCalendarTP::~CFWL_MonthCalendarTP() = default;
 
-  switch (pParams->m_iPart) {
+void CFWL_MonthCalendarTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
     case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Background: {
-      DrawTotalBK(pParams, &pParams->m_matrix);
+      DrawTotalBK(pParams, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Header: {
-      DrawHeadBk(pParams, &pParams->m_matrix);
+      DrawHeadBk(pParams, pParams.m_matrix);
       break;
     }
     case CFWL_Part::LBtn: {
-      FWLTHEME_STATE eState = GetState(pParams->m_dwStates);
-      DrawArrowBtn(pParams->m_pGraphics, &pParams->m_rtPart,
-                   FWLTHEME_DIRECTION_Left, eState, &pParams->m_matrix);
+      FWLTHEME_STATE eState = GetState(pParams.m_dwStates);
+      DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
+                   FWLTHEME_DIRECTION_Left, eState, pParams.m_matrix);
       break;
     }
     case CFWL_Part::RBtn: {
-      FWLTHEME_STATE eState = GetState(pParams->m_dwStates);
-      DrawArrowBtn(pParams->m_pGraphics, &pParams->m_rtPart,
-                   FWLTHEME_DIRECTION_Right, eState, &pParams->m_matrix);
+      FWLTHEME_STATE eState = GetState(pParams.m_dwStates);
+      DrawArrowBtn(pParams.m_pGraphics.Get(), pParams.m_rtPart,
+                   FWLTHEME_DIRECTION_Right, eState, pParams.m_matrix);
       break;
     }
     case CFWL_Part::HSeparator: {
-      DrawHSeperator(pParams, &pParams->m_matrix);
+      DrawHSeparator(pParams, pParams.m_matrix);
       break;
     }
     case CFWL_Part::DatesIn: {
-      DrawDatesInBK(pParams, &pParams->m_matrix);
+      DrawDatesInBK(pParams, pParams.m_matrix);
       break;
     }
     case CFWL_Part::TodayCircle: {
-      DrawTodayCircle(pParams, &pParams->m_matrix);
+      DrawTodayCircle(pParams, pParams.m_matrix);
       break;
     }
     case CFWL_Part::DateInCircle: {
-      DrawDatesInCircle(pParams, &pParams->m_matrix);
+      DrawDatesInCircle(pParams, pParams.m_matrix);
       break;
     }
     case CFWL_Part::WeekNumSep: {
-      DrawWeekNumSep(pParams, &pParams->m_matrix);
+      DrawWeekNumSep(pParams, pParams.m_matrix);
       break;
     }
     default:
@@ -85,62 +83,60 @@
   }
 }
 
-void CFWL_MonthCalendarTP::DrawText(CFWL_ThemeText* pParams) {
-  if (!m_pTextOut)
-    return;
-
-  if ((pParams->m_iPart == CFWL_Part::DatesIn) &&
-      !(pParams->m_dwStates & FWL_ITEMSTATE_MCD_Flag) &&
-      (pParams->m_dwStates &
+void CFWL_MonthCalendarTP::DrawText(const CFWL_ThemeText& pParams) {
+  EnsureTTOInitialized();
+  if ((pParams.m_iPart == CFWL_Part::DatesIn) &&
+      !(pParams.m_dwStates & FWL_ITEMSTATE_MCD_Flag) &&
+      (pParams.m_dwStates &
        (CFWL_PartState_Hovered | CFWL_PartState_Selected))) {
     m_pTextOut->SetTextColor(0xFFFFFFFF);
-  } else if (pParams->m_iPart == CFWL_Part::Caption) {
-    m_pTextOut->SetTextColor(m_pThemeData->clrCaption);
+  } else if (pParams.m_iPart == CFWL_Part::Caption) {
+    m_pTextOut->SetTextColor(kCaptionColor);
   } else {
     m_pTextOut->SetTextColor(0xFF000000);
   }
   CFWL_WidgetTP::DrawText(pParams);
 }
 
-void CFWL_MonthCalendarTP::DrawTotalBK(CFWL_ThemeBackground* pParams,
-                                       CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawTotalBK(const CFWL_ThemeBackground& pParams,
+                                       const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtTotal(pParams->m_rtPart);
+  CFX_RectF rtTotal(pParams.m_rtPart);
   path.AddRectangle(rtTotal.left, rtTotal.top, rtTotal.width, rtTotal.height);
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetFillColor(CXFA_GEColor(m_pThemeData->clrBK));
-  pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetFillColor(CXFA_GEColor(kBackgroundColor));
+  pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawHeadBk(CFWL_ThemeBackground* pParams,
-                                      CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawHeadBk(const CFWL_ThemeBackground& pParams,
+                                      const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtHead = pParams->m_rtPart;
+  CFX_RectF rtHead = pParams.m_rtPart;
   path.AddRectangle(rtHead.left, rtHead.top, rtHead.width, rtHead.height);
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetFillColor(CXFA_GEColor(m_pThemeData->clrBK));
-  pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetFillColor(CXFA_GEColor(kBackgroundColor));
+  pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawLButton(CFWL_ThemeBackground* pParams,
-                                       CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawLButton(const CFWL_ThemeBackground& pParams,
+                                       const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtLBtn = pParams->m_rtPart;
+  CFX_RectF rtLBtn = pParams.m_rtPart;
   path.AddRectangle(rtLBtn.left, rtLBtn.top, rtLBtn.width, rtLBtn.height);
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetStrokeColor(
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetStrokeColor(
       CXFA_GEColor(ArgbEncode(0xff, 205, 219, 243)));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  if (pParams->m_dwStates & CFWL_PartState_Pressed) {
-    pParams->m_pGraphics->SetFillColor(
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  if (pParams.m_dwStates & CFWL_PartState_Pressed) {
+    pParams.m_pGraphics->SetFillColor(
         CXFA_GEColor(ArgbEncode(0xff, 174, 198, 242)));
-    pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   } else {
-    pParams->m_pGraphics->SetFillColor(
+    pParams.m_pGraphics->SetFillColor(
         CXFA_GEColor(ArgbEncode(0xff, 227, 235, 249)));
-    pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   }
 
   path.Clear();
@@ -151,29 +147,29 @@
   path.LineTo(CFX_PointF(rtLBtn.left + rtLBtn.Width() / 3 * 2,
                          rtLBtn.bottom() - rtLBtn.height / 4));
 
-  pParams->m_pGraphics->SetStrokeColor(
+  pParams.m_pGraphics->SetStrokeColor(
       CXFA_GEColor(ArgbEncode(0xff, 50, 104, 205)));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawRButton(CFWL_ThemeBackground* pParams,
-                                       CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawRButton(const CFWL_ThemeBackground& pParams,
+                                       const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtRBtn = pParams->m_rtPart;
+  CFX_RectF rtRBtn = pParams.m_rtPart;
   path.AddRectangle(rtRBtn.left, rtRBtn.top, rtRBtn.width, rtRBtn.height);
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetStrokeColor(
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetStrokeColor(
       CXFA_GEColor(ArgbEncode(0xff, 205, 219, 243)));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  if (pParams->m_dwStates & CFWL_PartState_Pressed) {
-    pParams->m_pGraphics->SetFillColor(
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  if (pParams.m_dwStates & CFWL_PartState_Pressed) {
+    pParams.m_pGraphics->SetFillColor(
         CXFA_GEColor(ArgbEncode(0xff, 174, 198, 242)));
-    pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   } else {
-    pParams->m_pGraphics->SetFillColor(
+    pParams.m_pGraphics->SetFillColor(
         CXFA_GEColor(ArgbEncode(0xff, 227, 235, 249)));
-    pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   }
 
   path.Clear();
@@ -184,85 +180,81 @@
   path.LineTo(CFX_PointF(rtRBtn.left + rtRBtn.Width() / 3,
                          rtRBtn.bottom() - rtRBtn.height / 4));
 
-  pParams->m_pGraphics->SetStrokeColor(
+  pParams.m_pGraphics->SetStrokeColor(
       CXFA_GEColor(ArgbEncode(0xff, 50, 104, 205)));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawHSeperator(CFWL_ThemeBackground* pParams,
-                                          CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawHSeparator(const CFWL_ThemeBackground& pParams,
+                                          const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtHSep = pParams->m_rtPart;
+  CFX_RectF rtHSep = pParams.m_rtPart;
   path.MoveTo(CFX_PointF(rtHSep.left, rtHSep.top + rtHSep.height / 2));
   path.LineTo(CFX_PointF(rtHSep.right(), rtHSep.top + rtHSep.height / 2));
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(m_pThemeData->clrSeperator));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kSeparatorColor));
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawWeekNumSep(CFWL_ThemeBackground* pParams,
-                                          CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawWeekNumSep(const CFWL_ThemeBackground& pParams,
+                                          const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtWeekSep = pParams->m_rtPart;
+  CFX_RectF rtWeekSep = pParams.m_rtPart;
   path.MoveTo(rtWeekSep.TopLeft());
   path.LineTo(rtWeekSep.BottomLeft());
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(m_pThemeData->clrSeperator));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kSeparatorColor));
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawDatesInBK(CFWL_ThemeBackground* pParams,
-                                         CFX_Matrix* pMatrix) {
-  pParams->m_pGraphics->SaveGraphState();
-  if (pParams->m_dwStates & CFWL_PartState_Selected) {
+void CFWL_MonthCalendarTP::DrawDatesInBK(const CFWL_ThemeBackground& pParams,
+                                         const CFX_Matrix& matrix) {
+  pParams.m_pGraphics->SaveGraphState();
+  if (pParams.m_dwStates & CFWL_PartState_Selected) {
     CXFA_GEPath path;
-    CFX_RectF rtSelDay = pParams->m_rtPart;
+    CFX_RectF rtSelDay = pParams.m_rtPart;
     path.AddRectangle(rtSelDay.left, rtSelDay.top, rtSelDay.width,
                       rtSelDay.height);
-    pParams->m_pGraphics->SetFillColor(
-        CXFA_GEColor(m_pThemeData->clrDatesSelectedBK));
-    pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
-  } else if (pParams->m_dwStates & CFWL_PartState_Hovered) {
+    pParams.m_pGraphics->SetFillColor(
+        CXFA_GEColor(kDatesSelectedBackgroundColor));
+    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
+  } else if (pParams.m_dwStates & CFWL_PartState_Hovered) {
     CXFA_GEPath path;
-    CFX_RectF rtSelDay = pParams->m_rtPart;
+    CFX_RectF rtSelDay = pParams.m_rtPart;
     path.AddRectangle(rtSelDay.left, rtSelDay.top, rtSelDay.width,
                       rtSelDay.height);
-    pParams->m_pGraphics->SetFillColor(
-        CXFA_GEColor(m_pThemeData->clrDatesHoverBK));
-    pParams->m_pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+    pParams.m_pGraphics->SetFillColor(CXFA_GEColor(kDatesHoverBackgroundColor));
+    pParams.m_pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   }
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawDatesInCircle(CFWL_ThemeBackground* pParams,
-                                             CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawDatesInCircle(
+    const CFWL_ThemeBackground& pParams,
+    const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtSelDay = pParams->m_rtPart;
+  CFX_RectF rtSelDay = pParams.m_rtPart;
   path.AddRectangle(rtSelDay.left, rtSelDay.top, rtSelDay.width,
                     rtSelDay.height);
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(m_pThemeData->clrDatesCircle));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kDatesCircleColor));
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
-void CFWL_MonthCalendarTP::DrawTodayCircle(CFWL_ThemeBackground* pParams,
-                                           CFX_Matrix* pMatrix) {
+void CFWL_MonthCalendarTP::DrawTodayCircle(const CFWL_ThemeBackground& pParams,
+                                           const CFX_Matrix& matrix) {
   CXFA_GEPath path;
-  CFX_RectF rtTodayCircle = pParams->m_rtPart;
+  CFX_RectF rtTodayCircle = pParams.m_rtPart;
   path.AddRectangle(rtTodayCircle.left, rtTodayCircle.top, rtTodayCircle.width,
                     rtTodayCircle.height);
-  pParams->m_pGraphics->SaveGraphState();
-  pParams->m_pGraphics->SetStrokeColor(
-      CXFA_GEColor(m_pThemeData->clrDatesCircle));
-  pParams->m_pGraphics->StrokePath(&path, pMatrix);
-  pParams->m_pGraphics->RestoreGraphState();
+  pParams.m_pGraphics->SaveGraphState();
+  pParams.m_pGraphics->SetStrokeColor(CXFA_GEColor(kDatesCircleColor));
+  pParams.m_pGraphics->StrokePath(&path, &matrix);
+  pParams.m_pGraphics->RestoreGraphState();
 }
 
 FWLTHEME_STATE CFWL_MonthCalendarTP::GetState(uint32_t dwFWLStates) {
@@ -272,13 +264,3 @@
     return FWLTHEME_STATE_Pressed;
   return FWLTHEME_STATE_Normal;
 }
-
-void CFWL_MonthCalendarTP::SetThemeData() {
-  m_pThemeData->clrCaption = ArgbEncode(0xff, 0, 153, 255);
-  m_pThemeData->clrSeperator = ArgbEncode(0xff, 141, 161, 239);
-  m_pThemeData->clrDatesHoverBK = ArgbEncode(0xff, 193, 211, 251);
-  m_pThemeData->clrDatesSelectedBK = ArgbEncode(0xff, 173, 188, 239);
-  m_pThemeData->clrDatesCircle = ArgbEncode(0xff, 103, 144, 209);
-  m_pThemeData->clrToday = ArgbEncode(0xff, 0, 0, 0);
-  m_pThemeData->clrBK = ArgbEncode(0xff, 255, 255, 255);
-}
diff --git a/xfa/fwl/theme/cfwl_monthcalendartp.h b/xfa/fwl/theme/cfwl_monthcalendartp.h
index 6a1b9be..dd82cc1 100644
--- a/xfa/fwl/theme/cfwl_monthcalendartp.h
+++ b/xfa/fwl/theme/cfwl_monthcalendartp.h
@@ -7,48 +7,37 @@
 #ifndef XFA_FWL_THEME_CFWL_MONTHCALENDARTP_H_
 #define XFA_FWL_THEME_CFWL_MONTHCALENDARTP_H_
 
-#include <memory>
-
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_MonthCalendarTP : public CFWL_WidgetTP {
+class CFWL_MonthCalendarTP final : public CFWL_WidgetTP {
  public:
   CFWL_MonthCalendarTP();
   ~CFWL_MonthCalendarTP() override;
 
   // CFWL_WidgetTP
-  void Initialize() override;
-  void Finalize() override;
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
-  void DrawText(CFWL_ThemeText* pParams) override;
-
- protected:
-  struct MCThemeData {
-    FX_ARGB clrCaption;
-    FX_ARGB clrSeperator;
-    FX_ARGB clrDatesHoverBK;
-    FX_ARGB clrDatesSelectedBK;
-    FX_ARGB clrDatesCircle;
-    FX_ARGB clrToday;
-    FX_ARGB clrBK;
-  };
-
-  void DrawTotalBK(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawHeadBk(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawLButton(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawRButton(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawDatesInBK(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawDatesInCircle(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawTodayCircle(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawHSeperator(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  void DrawWeekNumSep(CFWL_ThemeBackground* pParams, CFX_Matrix* pMatrix);
-  FWLTHEME_STATE GetState(uint32_t dwFWLStates);
-
-  std::unique_ptr<MCThemeData> m_pThemeData;
-  WideString wsResource;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
+  void DrawText(const CFWL_ThemeText& pParams) override;
 
  private:
-  void SetThemeData();
+  void DrawTotalBK(const CFWL_ThemeBackground& pParams,
+                   const CFX_Matrix& matrix);
+  void DrawHeadBk(const CFWL_ThemeBackground& pParams,
+                  const CFX_Matrix& matrix);
+  void DrawLButton(const CFWL_ThemeBackground& pParams,
+                   const CFX_Matrix& matrix);
+  void DrawRButton(const CFWL_ThemeBackground& pParams,
+                   const CFX_Matrix& matrix);
+  void DrawDatesInBK(const CFWL_ThemeBackground& pParams,
+                     const CFX_Matrix& matrix);
+  void DrawDatesInCircle(const CFWL_ThemeBackground& pParams,
+                         const CFX_Matrix& matrix);
+  void DrawTodayCircle(const CFWL_ThemeBackground& pParams,
+                       const CFX_Matrix& matrix);
+  void DrawHSeparator(const CFWL_ThemeBackground& pParams,
+                      const CFX_Matrix& matrix);
+  void DrawWeekNumSep(const CFWL_ThemeBackground& pParams,
+                      const CFX_Matrix& matrix);
+  FWLTHEME_STATE GetState(uint32_t dwFWLStates);
 };
 
 #endif  // XFA_FWL_THEME_CFWL_MONTHCALENDARTP_H_
diff --git a/xfa/fwl/theme/cfwl_pictureboxtp.cpp b/xfa/fwl/theme/cfwl_pictureboxtp.cpp
index 27abbe2..39611bd 100644
--- a/xfa/fwl/theme/cfwl_pictureboxtp.cpp
+++ b/xfa/fwl/theme/cfwl_pictureboxtp.cpp
@@ -14,15 +14,11 @@
 
 CFWL_PictureBoxTP::~CFWL_PictureBoxTP() {}
 
-void CFWL_PictureBoxTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
-
-  switch (pParams->m_iPart) {
-    case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+void CFWL_PictureBoxTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
+    case CFWL_Part::Border:
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
-    }
     default:
       break;
   }
diff --git a/xfa/fwl/theme/cfwl_pictureboxtp.h b/xfa/fwl/theme/cfwl_pictureboxtp.h
index 4a118f2..4bb295a 100644
--- a/xfa/fwl/theme/cfwl_pictureboxtp.h
+++ b/xfa/fwl/theme/cfwl_pictureboxtp.h
@@ -9,13 +9,13 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_PictureBoxTP : public CFWL_WidgetTP {
+class CFWL_PictureBoxTP final : public CFWL_WidgetTP {
  public:
   CFWL_PictureBoxTP();
   ~CFWL_PictureBoxTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 };
 
 #endif  // XFA_FWL_THEME_CFWL_PICTUREBOXTP_H_
diff --git a/xfa/fwl/theme/cfwl_pushbuttontp.cpp b/xfa/fwl/theme/cfwl_pushbuttontp.cpp
index 3d3b1aa..45144b4 100644
--- a/xfa/fwl/theme/cfwl_pushbuttontp.cpp
+++ b/xfa/fwl/theme/cfwl_pushbuttontp.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fwl/theme/cfwl_pushbuttontp.h"
 
+#include "core/fxge/render_defines.h"
 #include "xfa/fwl/cfwl_pushbutton.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
@@ -21,14 +22,14 @@
 
 CFWL_PushButtonTP::~CFWL_PushButtonTP() {}
 
-void CFWL_PushButtonTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  switch (pParams->m_iPart) {
+void CFWL_PushButtonTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  switch (pParams.m_iPart) {
     case CFWL_Part::Border: {
-      DrawBorder(pParams->m_pGraphics, &pParams->m_rtPart, &pParams->m_matrix);
+      DrawBorder(pParams.m_pGraphics.Get(), pParams.m_rtPart, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Background: {
-      CFX_RectF& rect = pParams->m_rtPart;
+      const CFX_RectF& rect = pParams.m_rtPart;
       float fRight = rect.right();
       float fBottom = rect.bottom();
 
@@ -51,7 +52,7 @@
       CXFA_GEPath fillPath;
       fillPath.AddSubpath(&strokePath);
 
-      CXFA_Graphics* pGraphics = pParams->m_pGraphics;
+      CXFA_Graphics* pGraphics = pParams.m_pGraphics.Get();
       pGraphics->SaveGraphState();
 
       CFX_RectF rtInner(rect);
@@ -60,25 +61,22 @@
       fillPath.AddRectangle(rtInner.left, rtInner.top, rtInner.width,
                             rtInner.height);
 
-      int32_t iColor = GetColorID(pParams->m_dwStates);
-      DrawAxialShading(pGraphics, rect.left + PUSHBUTTON_SIZE_Corner, rect.top,
-                       rect.left + PUSHBUTTON_SIZE_Corner, rect.bottom(),
-                       m_pThemeData->clrStart[iColor],
-                       m_pThemeData->clrEnd[iColor], &fillPath,
-                       FXFILL_ALTERNATE, &pParams->m_matrix);
+      int32_t iColor = GetColorID(pParams.m_dwStates);
+      FillSolidRect(pGraphics, m_pThemeData->clrEnd[iColor], rect,
+                    pParams.m_matrix);
 
       pGraphics->SetStrokeColor(CXFA_GEColor(m_pThemeData->clrBorder[iColor]));
-      pGraphics->StrokePath(&strokePath, &pParams->m_matrix);
+      pGraphics->StrokePath(&strokePath, &pParams.m_matrix);
 
       fillPath.Clear();
       fillPath.AddRectangle(rtInner.left, rtInner.top, rtInner.width,
                             rtInner.height);
 
       pGraphics->SetFillColor(CXFA_GEColor(m_pThemeData->clrFill[iColor]));
-      pGraphics->FillPath(&fillPath, FXFILL_WINDING, &pParams->m_matrix);
-      if (pParams->m_dwStates & CFWL_PartState_Focused) {
+      pGraphics->FillPath(&fillPath, FXFILL_WINDING, &pParams.m_matrix);
+      if (pParams.m_dwStates & CFWL_PartState_Focused) {
         rtInner.Inflate(1, 1, 0, 0);
-        DrawFocus(pGraphics, &rtInner, &pParams->m_matrix);
+        DrawFocus(pGraphics, rtInner, pParams.m_matrix);
       }
       pGraphics->RestoreGraphState();
       break;
diff --git a/xfa/fwl/theme/cfwl_pushbuttontp.h b/xfa/fwl/theme/cfwl_pushbuttontp.h
index b2372bb..c3079dd 100644
--- a/xfa/fwl/theme/cfwl_pushbuttontp.h
+++ b/xfa/fwl/theme/cfwl_pushbuttontp.h
@@ -11,15 +11,15 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_PushButtonTP : public CFWL_WidgetTP {
+class CFWL_PushButtonTP final : public CFWL_WidgetTP {
  public:
   CFWL_PushButtonTP();
   ~CFWL_PushButtonTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
- protected:
+ private:
   struct PBThemeData {
     FX_ARGB clrBorder[5];
     FX_ARGB clrStart[5];
@@ -34,13 +34,10 @@
   void SetBackgroudColor(uint32_t* pData);
   void SetCaptionColor(uint32_t* pData);
   void SetCornerColor(uint32_t* pData);
-
   int32_t GetColorID(uint32_t dwStates) const;
+  void SetThemeData();
 
   std::unique_ptr<PBThemeData> m_pThemeData;
-
- private:
-  void SetThemeData();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_PUSHBUTTONTP_H_
diff --git a/xfa/fwl/theme/cfwl_scrollbartp.cpp b/xfa/fwl/theme/cfwl_scrollbartp.cpp
index d94eed0..55adc3e 100644
--- a/xfa/fwl/theme/cfwl_scrollbartp.cpp
+++ b/xfa/fwl/theme/cfwl_scrollbartp.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fwl/theme/cfwl_scrollbartp.h"
 
+#include "core/fxge/render_defines.h"
 #include "xfa/fwl/cfwl_scrollbar.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_widget.h"
@@ -25,45 +26,44 @@
 
 CFWL_ScrollBarTP::~CFWL_ScrollBarTP() {}
 
-void CFWL_ScrollBarTP::DrawBackground(CFWL_ThemeBackground* pParams) {
-  if (!pParams)
-    return;
-
-  CFWL_Widget* pWidget = pParams->m_pWidget;
+void CFWL_ScrollBarTP::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  CFWL_Widget* pWidget = pParams.m_pWidget;
   FWLTHEME_STATE eState = FWLTHEME_STATE_Normal;
-  if (pParams->m_dwStates & CFWL_PartState_Hovered)
+  if (pParams.m_dwStates & CFWL_PartState_Hovered)
     eState = FWLTHEME_STATE_Hover;
-  else if (pParams->m_dwStates & CFWL_PartState_Pressed)
+  else if (pParams.m_dwStates & CFWL_PartState_Pressed)
     eState = FWLTHEME_STATE_Pressed;
-  else if (pParams->m_dwStates & CFWL_PartState_Disabled)
+  else if (pParams.m_dwStates & CFWL_PartState_Disabled)
     eState = FWLTHEME_STATE_Disable;
 
-  CXFA_Graphics* pGraphics = pParams->m_pGraphics;
-  CFX_RectF* pRect = &pParams->m_rtPart;
+  CXFA_Graphics* pGraphics = pParams.m_pGraphics.Get();
   bool bVert = !!pWidget->GetStylesEx();
-  switch (pParams->m_iPart) {
+  switch (pParams.m_iPart) {
     case CFWL_Part::ForeArrow: {
-      DrawMaxMinBtn(pGraphics, pRect,
+      DrawMaxMinBtn(pGraphics, pParams.m_rtPart,
                     bVert ? FWLTHEME_DIRECTION_Up : FWLTHEME_DIRECTION_Left,
-                    eState, &pParams->m_matrix);
+                    eState, pParams.m_matrix);
       break;
     }
     case CFWL_Part::BackArrow: {
-      DrawMaxMinBtn(pGraphics, pRect,
+      DrawMaxMinBtn(pGraphics, pParams.m_rtPart,
                     bVert ? FWLTHEME_DIRECTION_Down : FWLTHEME_DIRECTION_Right,
-                    eState, &pParams->m_matrix);
+                    eState, pParams.m_matrix);
       break;
     }
     case CFWL_Part::Thumb: {
-      DrawThumbBtn(pGraphics, pRect, bVert, eState, true, &pParams->m_matrix);
+      DrawThumbBtn(pGraphics, pParams.m_rtPart, bVert, eState, true,
+                   pParams.m_matrix);
       break;
     }
     case CFWL_Part::LowerTrack: {
-      DrawTrack(pGraphics, pRect, bVert, eState, true, &pParams->m_matrix);
+      DrawTrack(pGraphics, pParams.m_rtPart, bVert, eState, true,
+                pParams.m_matrix);
       break;
     }
     case CFWL_Part::UpperTrack: {
-      DrawTrack(pGraphics, pRect, bVert, eState, false, &pParams->m_matrix);
+      DrawTrack(pGraphics, pParams.m_rtPart, bVert, eState, false,
+                pParams.m_matrix);
       break;
     }
     default:
@@ -72,63 +72,49 @@
 }
 
 void CFWL_ScrollBarTP::DrawThumbBtn(CXFA_Graphics* pGraphics,
-                                    const CFX_RectF* pRect,
+                                    const CFX_RectF& input_rect,
                                     bool bVert,
                                     FWLTHEME_STATE eState,
                                     bool bPawButton,
-                                    CFX_Matrix* pMatrix) {
+                                    const CFX_Matrix& matrix) {
   if (eState < FWLTHEME_STATE_Normal || eState > FWLTHEME_STATE_Disable)
     return;
 
-  CXFA_GEPath path;
-  CFX_RectF rect(*pRect);
-  if (bVert) {
+  CFX_RectF rect = input_rect;
+  if (bVert)
     rect.Deflate(1, 0);
-    if (rect.IsEmpty(0.1f))
-      return;
-
-    path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-    DrawAxialShading(pGraphics, rect.left, rect.top, rect.right(), rect.top,
-                     m_pThemeData->clrBtnBK[eState - 1][0],
-                     m_pThemeData->clrBtnBK[eState - 1][1], &path,
-                     FXFILL_WINDING, pMatrix);
-    pGraphics->SaveGraphState();
-    pGraphics->SetStrokeColor(
-        CXFA_GEColor(m_pThemeData->clrBtnBorder[eState - 1]));
-    pGraphics->StrokePath(&path, pMatrix);
-    pGraphics->RestoreGraphState();
-  } else {
+  else
     rect.Deflate(0, 1);
-    if (rect.IsEmpty(0.1f))
-      return;
 
-    path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
-    DrawAxialShading(pGraphics, rect.left, rect.top, rect.left, rect.bottom(),
-                     m_pThemeData->clrBtnBK[eState - 1][0],
-                     m_pThemeData->clrBtnBK[eState - 1][1], &path,
-                     FXFILL_WINDING, pMatrix);
-    pGraphics->SaveGraphState();
-    pGraphics->SetStrokeColor(
-        CXFA_GEColor(m_pThemeData->clrBtnBorder[eState - 1]));
-    pGraphics->StrokePath(&path, pMatrix);
-    pGraphics->RestoreGraphState();
-  }
+  if (rect.IsEmpty(0.1f))
+    return;
+
+  FillSolidRect(pGraphics, m_pThemeData->clrBtnBK[eState - 1][1], rect, matrix);
+
+  pGraphics->SaveGraphState();
+
+  CXFA_GEPath path;
+  path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
+  pGraphics->SetStrokeColor(
+      CXFA_GEColor(m_pThemeData->clrBtnBorder[eState - 1]));
+  pGraphics->StrokePath(&path, &matrix);
+  pGraphics->RestoreGraphState();
 }
 
 void CFWL_ScrollBarTP::DrawPaw(CXFA_Graphics* pGraphics,
-                               const CFX_RectF* pRect,
+                               const CFX_RectF& rect,
                                bool bVert,
                                FWLTHEME_STATE eState,
-                               CFX_Matrix* pMatrix) {
+                               const CFX_Matrix& matrix) {
   CXFA_GEPath path;
   if (bVert) {
     float fPawLen = kPawLength;
-    if (pRect->width / 2 <= fPawLen) {
-      fPawLen = (pRect->width - 6) / 2;
+    if (rect.width / 2 <= fPawLen) {
+      fPawLen = (rect.width - 6) / 2;
     }
 
-    float fX = pRect->left + pRect->width / 4;
-    float fY = pRect->top + pRect->height / 2;
+    float fX = rect.left + rect.width / 4;
+    float fY = rect.top + rect.height / 2;
     path.MoveTo(CFX_PointF(fX, fY - 4));
     path.LineTo(CFX_PointF(fX + fPawLen, fY - 4));
     path.MoveTo(CFX_PointF(fX, fY - 2));
@@ -157,15 +143,15 @@
     pGraphics->SetLineWidth(1);
     pGraphics->SetStrokeColor(
         CXFA_GEColor(m_pThemeData->clrPawColorDark[eState - 1]));
-    pGraphics->StrokePath(&path, pMatrix);
+    pGraphics->StrokePath(&path, &matrix);
   } else {
     float fPawLen = kPawLength;
-    if (pRect->height / 2 <= fPawLen) {
-      fPawLen = (pRect->height - 6) / 2;
+    if (rect.height / 2 <= fPawLen) {
+      fPawLen = (rect.height - 6) / 2;
     }
 
-    float fX = pRect->left + pRect->width / 2;
-    float fY = pRect->top + pRect->height / 4;
+    float fX = rect.left + rect.width / 2;
+    float fY = rect.top + rect.height / 4;
     path.MoveTo(CFX_PointF(fX - 4, fY));
     path.LineTo(CFX_PointF(fX - 4, fY + fPawLen));
     path.MoveTo(CFX_PointF(fX - 2, fY));
@@ -178,7 +164,7 @@
     pGraphics->SetLineWidth(1);
     pGraphics->SetStrokeColor(
         CXFA_GEColor(m_pThemeData->clrPawColorLight[eState - 1]));
-    pGraphics->StrokePath(&path, pMatrix);
+    pGraphics->StrokePath(&path, &matrix);
     fY++;
 
     path.Clear();
@@ -194,55 +180,49 @@
     pGraphics->SetLineWidth(1);
     pGraphics->SetStrokeColor(
         CXFA_GEColor(m_pThemeData->clrPawColorDark[eState - 1]));
-    pGraphics->StrokePath(&path, pMatrix);
+    pGraphics->StrokePath(&path, &matrix);
   }
 }
 
 void CFWL_ScrollBarTP::DrawTrack(CXFA_Graphics* pGraphics,
-                                 const CFX_RectF* pRect,
+                                 const CFX_RectF& rect,
                                  bool bVert,
                                  FWLTHEME_STATE eState,
                                  bool bLowerTrack,
-                                 CFX_Matrix* pMatrix) {
+                                 const CFX_Matrix& matrix) {
   if (eState < FWLTHEME_STATE_Normal || eState > FWLTHEME_STATE_Disable)
     return;
 
   pGraphics->SaveGraphState();
   CXFA_GEPath path;
-  float fRight = pRect->right();
-  float fBottom = pRect->bottom();
+  float fRight = rect.right();
+  float fBottom = rect.bottom();
   if (bVert) {
-    path.AddRectangle(pRect->left, pRect->top, 1, pRect->height);
-    path.AddRectangle(fRight - 1, pRect->top, 1, pRect->height);
+    path.AddRectangle(rect.left, rect.top, 1, rect.height);
+    path.AddRectangle(fRight - 1, rect.top, 1, rect.height);
   } else {
-    path.AddRectangle(pRect->left, pRect->top, pRect->width, 1);
-    path.AddRectangle(pRect->left, fBottom - 1, pRect->width, 1);
+    path.AddRectangle(rect.left, rect.top, rect.width, 1);
+    path.AddRectangle(rect.left, fBottom - 1, rect.width, 1);
   }
   pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(255, 238, 237, 229)));
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   path.Clear();
-  path.AddRectangle(pRect->left + 1, pRect->top, pRect->width - 2,
-                    pRect->height);
-  float x1 = bVert ? pRect->left + 1 : pRect->left;
-  float y1 = bVert ? pRect->top : pRect->top + 1;
-  float x2 = bVert ? fRight - 1 : pRect->left;
-  float y2 = bVert ? pRect->top : fBottom - 1;
+  path.AddRectangle(rect.left + 1, rect.top, rect.width - 2, rect.height);
   pGraphics->RestoreGraphState();
-  DrawAxialShading(pGraphics, x1, y1, x2, y2, m_pThemeData->clrTrackBKStart,
-                   m_pThemeData->clrTrackBKEnd, &path, FXFILL_WINDING, pMatrix);
+  FillSolidRect(pGraphics, m_pThemeData->clrTrackBKEnd, rect, matrix);
 }
 
 void CFWL_ScrollBarTP::DrawMaxMinBtn(CXFA_Graphics* pGraphics,
-                                     const CFX_RectF* pRect,
+                                     const CFX_RectF& rect,
                                      FWLTHEME_DIRECTION eDict,
                                      FWLTHEME_STATE eState,
-                                     CFX_Matrix* pMatrix) {
-  DrawTrack(pGraphics, pRect,
+                                     const CFX_Matrix& matrix) {
+  DrawTrack(pGraphics, rect,
             eDict == FWLTHEME_DIRECTION_Up || eDict == FWLTHEME_DIRECTION_Down,
-            eState, true, pMatrix);
-  CFX_RectF rtArrowBtn(*pRect);
+            eState, true, matrix);
+  CFX_RectF rtArrowBtn = rect;
   rtArrowBtn.Deflate(1, 1, 1, 1);
-  DrawArrowBtn(pGraphics, &rtArrowBtn, eDict, eState, pMatrix);
+  DrawArrowBtn(pGraphics, rtArrowBtn, eDict, eState, matrix);
 }
 
 void CFWL_ScrollBarTP::SetThemeData() {
diff --git a/xfa/fwl/theme/cfwl_scrollbartp.h b/xfa/fwl/theme/cfwl_scrollbartp.h
index eec372c..f07d102 100644
--- a/xfa/fwl/theme/cfwl_scrollbartp.h
+++ b/xfa/fwl/theme/cfwl_scrollbartp.h
@@ -11,51 +11,49 @@
 
 #include "xfa/fwl/theme/cfwl_widgettp.h"
 
-class CFWL_ScrollBarTP : public CFWL_WidgetTP {
+class CFWL_ScrollBarTP final : public CFWL_WidgetTP {
  public:
   CFWL_ScrollBarTP();
   ~CFWL_ScrollBarTP() override;
 
   // CFWL_WidgetTP
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
 
- protected:
+ private:
   struct SBThemeData {
+    FX_ARGB clrTrackBKStart;
+    FX_ARGB clrTrackBKEnd;
     FX_ARGB clrPawColorLight[4];
     FX_ARGB clrPawColorDark[4];
     FX_ARGB clrBtnBK[4][2];
     FX_ARGB clrBtnBorder[4];
-    FX_ARGB clrTrackBKStart;
-    FX_ARGB clrTrackBKEnd;
   };
 
   void DrawThumbBtn(CXFA_Graphics* pGraphics,
-                    const CFX_RectF* pRect,
+                    const CFX_RectF& rect,
                     bool bVert,
                     FWLTHEME_STATE eState,
-                    bool bPawButton = true,
-                    CFX_Matrix* pMatrix = nullptr);
+                    bool bPawButton,
+                    const CFX_Matrix& matrix);
   void DrawTrack(CXFA_Graphics* pGraphics,
-                 const CFX_RectF* pRect,
+                 const CFX_RectF& rect,
                  bool bVert,
                  FWLTHEME_STATE eState,
                  bool bLowerTrack,
-                 CFX_Matrix* pMatrix = nullptr);
+                 const CFX_Matrix& matrix);
   void DrawMaxMinBtn(CXFA_Graphics* pGraphics,
-                     const CFX_RectF* pRect,
+                     const CFX_RectF& rect,
                      FWLTHEME_DIRECTION eDict,
                      FWLTHEME_STATE eState,
-                     CFX_Matrix* pMatrix = nullptr);
+                     const CFX_Matrix& matrix);
   void DrawPaw(CXFA_Graphics* pGraphics,
-               const CFX_RectF* pRect,
+               const CFX_RectF& rect,
                bool bVert,
                FWLTHEME_STATE eState,
-               CFX_Matrix* pMatrix = nullptr);
+               const CFX_Matrix& matrix);
+  void SetThemeData();
 
   std::unique_ptr<SBThemeData> m_pThemeData;
-
- private:
-  void SetThemeData();
 };
 
 #endif  // XFA_FWL_THEME_CFWL_SCROLLBARTP_H_
diff --git a/xfa/fwl/theme/cfwl_widgettp.cpp b/xfa/fwl/theme/cfwl_widgettp.cpp
index 3113d24..27caf99 100644
--- a/xfa/fwl/theme/cfwl_widgettp.cpp
+++ b/xfa/fwl/theme/cfwl_widgettp.cpp
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "core/fxge/render_defines.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
@@ -23,38 +24,34 @@
 #include "xfa/fxgraphics/cxfa_gepath.h"
 #include "xfa/fxgraphics/cxfa_geshading.h"
 
-CFWL_WidgetTP::CFWL_WidgetTP()
-    : m_dwRefCount(1), m_pFDEFont(nullptr), m_pColorData(nullptr) {}
+namespace {
 
-CFWL_WidgetTP::~CFWL_WidgetTP() {}
+CFWL_FontManager* g_FontManager = nullptr;
 
-void CFWL_WidgetTP::Initialize() {}
+}  // namespace
 
-void CFWL_WidgetTP::Finalize() {
-  if (m_pTextOut)
-    FinalizeTTO();
-}
+CFWL_WidgetTP::CFWL_WidgetTP() = default;
 
-void CFWL_WidgetTP::DrawBackground(CFWL_ThemeBackground* pParams) {}
+CFWL_WidgetTP::~CFWL_WidgetTP() = default;
 
-void CFWL_WidgetTP::DrawText(CFWL_ThemeText* pParams) {
-  if (!m_pTextOut)
-    InitTTO();
+void CFWL_WidgetTP::DrawBackground(const CFWL_ThemeBackground& pParams) {}
 
-  int32_t iLen = pParams->m_wsText.GetLength();
+void CFWL_WidgetTP::DrawText(const CFWL_ThemeText& pParams) {
+  EnsureTTOInitialized();
+  int32_t iLen = pParams.m_wsText.GetLength();
   if (iLen <= 0)
     return;
 
-  CXFA_Graphics* pGraphics = pParams->m_pGraphics;
-  m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
-  m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
+  CXFA_Graphics* pGraphics = pParams.m_pGraphics;
+  m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
+  m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
 
-  CFX_Matrix* pMatrix = &pParams->m_matrix;
-  pMatrix->Concat(*pGraphics->GetMatrix());
-  m_pTextOut->SetMatrix(*pMatrix);
+  CFX_Matrix matrix = pParams.m_matrix;
+  matrix.Concat(*pGraphics->GetMatrix());
+  m_pTextOut->SetMatrix(matrix);
   m_pTextOut->DrawLogicText(pGraphics->GetRenderDevice(),
-                            WideStringView(pParams->m_wsText.c_str(), iLen),
-                            pParams->m_rtPart);
+                            WideStringView(pParams.m_wsText.c_str(), iLen),
+                            pParams.m_rtPart);
 }
 
 const RetainPtr<CFGAS_GEFont>& CFWL_WidgetTP::GetFont() const {
@@ -84,8 +81,7 @@
   m_pColorData->clrSign[3] = ArgbEncode(255, 128, 128, 128);
 }
 
-
-void CFWL_WidgetTP::InitTTO() {
+void CFWL_WidgetTP::EnsureTTOInitialized() {
   if (m_pTextOut)
     return;
 
@@ -96,97 +92,68 @@
   m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
 }
 
-void CFWL_WidgetTP::FinalizeTTO() {
-  m_pTextOut.reset();
-}
-
 void CFWL_WidgetTP::DrawBorder(CXFA_Graphics* pGraphics,
-                               const CFX_RectF* pRect,
-                               CFX_Matrix* pMatrix) {
-  if (!pGraphics || !pRect)
+                               const CFX_RectF& rect,
+                               const CFX_Matrix& matrix) {
+  if (!pGraphics)
     return;
 
   CXFA_GEPath path;
-  path.AddRectangle(pRect->left, pRect->top, pRect->width, pRect->height);
-  path.AddRectangle(pRect->left + 1, pRect->top + 1, pRect->width - 2,
-                    pRect->height - 2);
+  path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
+  path.AddRectangle(rect.left + 1, rect.top + 1, rect.width - 2,
+                    rect.height - 2);
   pGraphics->SaveGraphState();
   pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(255, 0, 0, 0)));
-  pGraphics->FillPath(&path, FXFILL_ALTERNATE, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_ALTERNATE, &matrix);
   pGraphics->RestoreGraphState();
 }
 
 void CFWL_WidgetTP::FillBackground(CXFA_Graphics* pGraphics,
-                                   const CFX_RectF* pRect,
-                                   CFX_Matrix* pMatrix) {
-  FillSoildRect(pGraphics, FWLTHEME_COLOR_Background, pRect, pMatrix);
+                                   const CFX_RectF& rect,
+                                   const CFX_Matrix& matrix) {
+  FillSolidRect(pGraphics, FWLTHEME_COLOR_Background, rect, matrix);
 }
 
-void CFWL_WidgetTP::FillSoildRect(CXFA_Graphics* pGraphics,
+void CFWL_WidgetTP::FillSolidRect(CXFA_Graphics* pGraphics,
                                   FX_ARGB fillColor,
-                                  const CFX_RectF* pRect,
-                                  CFX_Matrix* pMatrix) {
-  if (!pGraphics || !pRect)
+                                  const CFX_RectF& rect,
+                                  const CFX_Matrix& matrix) {
+  if (!pGraphics)
     return;
 
   CXFA_GEPath path;
-  path.AddRectangle(pRect->left, pRect->top, pRect->width, pRect->height);
+  path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
   pGraphics->SaveGraphState();
   pGraphics->SetFillColor(CXFA_GEColor(fillColor));
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
-  pGraphics->RestoreGraphState();
-}
-
-void CFWL_WidgetTP::DrawAxialShading(CXFA_Graphics* pGraphics,
-                                     float fx1,
-                                     float fy1,
-                                     float fx2,
-                                     float fy2,
-                                     FX_ARGB beginColor,
-                                     FX_ARGB endColor,
-                                     CXFA_GEPath* path,
-                                     int32_t fillMode,
-                                     CFX_Matrix* pMatrix) {
-  if (!pGraphics || !path)
-    return;
-
-  CFX_PointF begPoint(fx1, fy1);
-  CFX_PointF endPoint(fx2, fy2);
-  CXFA_GEShading shading(begPoint, endPoint, false, false, beginColor,
-                         endColor);
-  pGraphics->SaveGraphState();
-  pGraphics->SetFillColor(CXFA_GEColor(&shading));
-  pGraphics->FillPath(path, fillMode, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
   pGraphics->RestoreGraphState();
 }
 
 void CFWL_WidgetTP::DrawFocus(CXFA_Graphics* pGraphics,
-                              const CFX_RectF* pRect,
-                              CFX_Matrix* pMatrix) {
-  if (!pGraphics || !pRect)
+                              const CFX_RectF& rect,
+                              const CFX_Matrix& matrix) {
+  if (!pGraphics)
     return;
 
-  float DashPattern[2] = {1, 1};
   CXFA_GEPath path;
-  path.AddRectangle(pRect->left, pRect->top, pRect->width, pRect->height);
+  path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
   pGraphics->SaveGraphState();
   pGraphics->SetStrokeColor(CXFA_GEColor(0xFF000000));
-  pGraphics->SetLineDash(0.0f, DashPattern, 2);
-  pGraphics->StrokePath(&path, pMatrix);
+  static constexpr float kDashPattern[2] = {1, 1};
+  pGraphics->SetLineDash(0.0f, kDashPattern, FX_ArraySize(kDashPattern));
+  pGraphics->StrokePath(&path, &matrix);
   pGraphics->RestoreGraphState();
 }
 
 void CFWL_WidgetTP::DrawArrow(CXFA_Graphics* pGraphics,
-                              const CFX_RectF* pRect,
+                              const CFX_RectF& rect,
                               FWLTHEME_DIRECTION eDict,
                               FX_ARGB argSign,
-                              CFX_Matrix* pMatrix) {
+                              const CFX_Matrix& matrix) {
   bool bVert =
       (eDict == FWLTHEME_DIRECTION_Up || eDict == FWLTHEME_DIRECTION_Down);
-  float fLeft =
-      (float)(((pRect->width - (bVert ? 9 : 6)) / 2 + pRect->left) + 0.5);
-  float fTop =
-      (float)(((pRect->height - (bVert ? 6 : 9)) / 2 + pRect->top) + 0.5);
+  float fLeft = ((rect.width - (bVert ? 9 : 6)) / 2 + rect.left) + 0.5f;
+  float fTop = ((rect.height - (bVert ? 6 : 9)) / 2 + rect.top) + 0.5f;
   CXFA_GEPath path;
   switch (eDict) {
     case FWLTHEME_DIRECTION_Down: {
@@ -227,52 +194,44 @@
     }
   }
   pGraphics->SetFillColor(CXFA_GEColor(argSign));
-  pGraphics->FillPath(&path, FXFILL_WINDING, pMatrix);
+  pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
 }
 
 void CFWL_WidgetTP::DrawBtn(CXFA_Graphics* pGraphics,
-                            const CFX_RectF* pRect,
+                            const CFX_RectF& rect,
                             FWLTHEME_STATE eState,
-                            CFX_Matrix* pMatrix) {
+                            const CFX_Matrix& matrix) {
   InitializeArrowColorData();
+  FillSolidRect(pGraphics, m_pColorData->clrEnd[eState - 1], rect, matrix);
 
   CXFA_GEPath path;
-  float fRight = pRect->right();
-  float fBottom = pRect->bottom();
-  path.AddRectangle(pRect->left, pRect->top, pRect->width, pRect->height);
-  DrawAxialShading(pGraphics, pRect->left, pRect->top, fRight, fBottom,
-                   m_pColorData->clrStart[eState - 1],
-                   m_pColorData->clrEnd[eState - 1], &path, FXFILL_WINDING,
-                   pMatrix);
-
+  path.AddRectangle(rect.left, rect.top, rect.width, rect.height);
   pGraphics->SetStrokeColor(CXFA_GEColor(m_pColorData->clrBorder[eState - 1]));
-  pGraphics->StrokePath(&path, pMatrix);
+  pGraphics->StrokePath(&path, &matrix);
 }
 
 void CFWL_WidgetTP::DrawArrowBtn(CXFA_Graphics* pGraphics,
-                                 const CFX_RectF* pRect,
+                                 const CFX_RectF& rect,
                                  FWLTHEME_DIRECTION eDict,
                                  FWLTHEME_STATE eState,
-                                 CFX_Matrix* pMatrix) {
-  DrawBtn(pGraphics, pRect, eState, pMatrix);
-
+                                 const CFX_Matrix& matrix) {
+  DrawBtn(pGraphics, rect, eState, matrix);
   InitializeArrowColorData();
-  DrawArrow(pGraphics, pRect, eDict, m_pColorData->clrSign[eState - 1],
-            pMatrix);
+  DrawArrow(pGraphics, rect, eDict, m_pColorData->clrSign[eState - 1], matrix);
 }
 
 CFWL_FontData::CFWL_FontData() : m_dwStyles(0), m_dwCodePage(0) {}
 
 CFWL_FontData::~CFWL_FontData() {}
 
-bool CFWL_FontData::Equal(const WideStringView& wsFontFamily,
+bool CFWL_FontData::Equal(WideStringView wsFontFamily,
                           uint32_t dwFontStyles,
                           uint16_t wCodePage) {
   return m_wsFamily == wsFontFamily && m_dwStyles == dwFontStyles &&
          m_dwCodePage == wCodePage;
 }
 
-bool CFWL_FontData::LoadFont(const WideStringView& wsFontFamily,
+bool CFWL_FontData::LoadFont(WideStringView wsFontFamily,
                              uint32_t dwFontStyles,
                              uint16_t dwCodePage) {
   m_wsFamily = wsFontFamily;
@@ -294,26 +253,24 @@
   return m_pFont;
 }
 
-CFWL_FontManager* CFWL_FontManager::s_FontManager = nullptr;
 CFWL_FontManager* CFWL_FontManager::GetInstance() {
-  if (!s_FontManager)
-    s_FontManager = new CFWL_FontManager;
-  return s_FontManager;
+  if (!g_FontManager)
+    g_FontManager = new CFWL_FontManager;
+  return g_FontManager;
 }
 
 void CFWL_FontManager::DestroyInstance() {
-  delete s_FontManager;
-  s_FontManager = nullptr;
+  delete g_FontManager;
+  g_FontManager = nullptr;
 }
 
-CFWL_FontManager::CFWL_FontManager() {}
+CFWL_FontManager::CFWL_FontManager() = default;
 
-CFWL_FontManager::~CFWL_FontManager() {}
+CFWL_FontManager::~CFWL_FontManager() = default;
 
-RetainPtr<CFGAS_GEFont> CFWL_FontManager::FindFont(
-    const WideStringView& wsFontFamily,
-    uint32_t dwFontStyles,
-    uint16_t wCodePage) {
+RetainPtr<CFGAS_GEFont> CFWL_FontManager::FindFont(WideStringView wsFontFamily,
+                                                   uint32_t dwFontStyles,
+                                                   uint16_t wCodePage) {
   for (const auto& pData : m_FontsArray) {
     if (pData->Equal(wsFontFamily, dwFontStyles, wCodePage))
       return pData->GetFont();
@@ -326,6 +283,3 @@
   return m_FontsArray.back()->GetFont();
 }
 
-void FWLTHEME_Release() {
-  CFWL_FontManager::DestroyInstance();
-}
diff --git a/xfa/fwl/theme/cfwl_widgettp.h b/xfa/fwl/theme/cfwl_widgettp.h
index 7813c08..c4fa61a 100644
--- a/xfa/fwl/theme/cfwl_widgettp.h
+++ b/xfa/fwl/theme/cfwl_widgettp.h
@@ -20,23 +20,14 @@
 class CFGAS_FontMgr;
 class CFGAS_GEFont;
 class CFWL_ThemeBackground;
-class CFWL_ThemePart;
 class CFWL_ThemeText;
-class CFWL_Widget;
-
-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_
-class CFX_FontSourceEnum_File;
-#endif
 
 class CFWL_WidgetTP {
  public:
   virtual ~CFWL_WidgetTP();
 
-  virtual void Initialize();
-  virtual void Finalize();
-
-  virtual void DrawBackground(CFWL_ThemeBackground* pParams);
-  virtual void DrawText(CFWL_ThemeText* pParams);
+  virtual void DrawBackground(const CFWL_ThemeBackground& pParams);
+  virtual void DrawText(const CFWL_ThemeText& pParams);
 
   const RetainPtr<CFGAS_GEFont>& GetFont() const;
 
@@ -51,64 +42,50 @@
   CFWL_WidgetTP();
 
   void InitializeArrowColorData();
-  void InitTTO();
-  void FinalizeTTO();
+  void EnsureTTOInitialized();
 
   void DrawBorder(CXFA_Graphics* pGraphics,
-                  const CFX_RectF* pRect,
-                  CFX_Matrix* pMatrix = nullptr);
+                  const CFX_RectF& rect,
+                  const CFX_Matrix& matrix);
   void FillBackground(CXFA_Graphics* pGraphics,
-                      const CFX_RectF* pRect,
-                      CFX_Matrix* pMatrix = nullptr);
-  void FillSoildRect(CXFA_Graphics* pGraphics,
+                      const CFX_RectF& rect,
+                      const CFX_Matrix& matrix);
+  void FillSolidRect(CXFA_Graphics* pGraphics,
                      FX_ARGB fillColor,
-                     const CFX_RectF* pRect,
-                     CFX_Matrix* pMatrix = nullptr);
-  void DrawAxialShading(CXFA_Graphics* pGraphics,
-                        float fx1,
-                        float fy1,
-                        float fx2,
-                        float fy2,
-                        FX_ARGB beginColor,
-                        FX_ARGB endColor,
-                        CXFA_GEPath* path,
-                        int32_t fillMode = FXFILL_WINDING,
-                        CFX_Matrix* pMatrix = nullptr);
+                     const CFX_RectF& rect,
+                     const CFX_Matrix& matrix);
   void DrawFocus(CXFA_Graphics* pGraphics,
-                 const CFX_RectF* pRect,
-                 CFX_Matrix* pMatrix = nullptr);
+                 const CFX_RectF& rect,
+                 const CFX_Matrix& matrix);
   void DrawArrow(CXFA_Graphics* pGraphics,
-                 const CFX_RectF* pRect,
+                 const CFX_RectF& rect,
                  FWLTHEME_DIRECTION eDict,
                  FX_ARGB argSign,
-                 CFX_Matrix* pMatrix = nullptr);
+                 const CFX_Matrix& matrix);
   void DrawBtn(CXFA_Graphics* pGraphics,
-               const CFX_RectF* pRect,
+               const CFX_RectF& rect,
                FWLTHEME_STATE eState,
-               CFX_Matrix* pMatrix = nullptr);
+               const CFX_Matrix& matrix);
   void DrawArrowBtn(CXFA_Graphics* pGraphics,
-                    const CFX_RectF* pRect,
+                    const CFX_RectF& rect,
                     FWLTHEME_DIRECTION eDict,
                     FWLTHEME_STATE eState,
-                    CFX_Matrix* pMatrix = nullptr);
+                    const CFX_Matrix& matrix);
 
-  uint32_t m_dwRefCount;
   std::unique_ptr<CFDE_TextOut> m_pTextOut;
   RetainPtr<CFGAS_GEFont> m_pFDEFont;
   std::unique_ptr<CColorData> m_pColorData;
 };
 
-void FWLTHEME_Release();
-
-class CFWL_FontData {
+class CFWL_FontData final {
  public:
   CFWL_FontData();
-  virtual ~CFWL_FontData();
+  ~CFWL_FontData();
 
-  bool Equal(const WideStringView& wsFontFamily,
+  bool Equal(WideStringView wsFontFamily,
              uint32_t dwFontStyles,
              uint16_t wCodePage);
-  bool LoadFont(const WideStringView& wsFontFamily,
+  bool LoadFont(WideStringView wsFontFamily,
                 uint32_t dwFontStyles,
                 uint16_t wCodePage);
   RetainPtr<CFGAS_GEFont> GetFont() const;
@@ -121,20 +98,19 @@
   RetainPtr<CFGAS_GEFont> m_pFont;
 };
 
-class CFWL_FontManager {
+class CFWL_FontManager final {
  public:
   static CFWL_FontManager* GetInstance();
   static void DestroyInstance();
 
-  RetainPtr<CFGAS_GEFont> FindFont(const WideStringView& wsFontFamily,
+  RetainPtr<CFGAS_GEFont> FindFont(WideStringView wsFontFamily,
                                    uint32_t dwFontStyles,
                                    uint16_t dwCodePage);
 
- protected:
+ private:
   CFWL_FontManager();
-  virtual ~CFWL_FontManager();
+  ~CFWL_FontManager();
 
-  static CFWL_FontManager* s_FontManager;
   std::vector<std::unique_ptr<CFWL_FontData>> m_FontsArray;
 };
 
diff --git a/xfa/fxfa/BUILD.gn b/xfa/fxfa/BUILD.gn
new file mode 100644
index 0000000..8490abe
--- /dev/null
+++ b/xfa/fxfa/BUILD.gn
@@ -0,0 +1,134 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+import("../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fxfa") {
+  sources = [
+    "cxfa_eventparam.cpp",
+    "cxfa_eventparam.h",
+    "cxfa_ffapp.cpp",
+    "cxfa_ffapp.h",
+    "cxfa_ffarc.cpp",
+    "cxfa_ffarc.h",
+    "cxfa_ffbarcode.cpp",
+    "cxfa_ffbarcode.h",
+    "cxfa_ffcheckbutton.cpp",
+    "cxfa_ffcheckbutton.h",
+    "cxfa_ffcombobox.cpp",
+    "cxfa_ffcombobox.h",
+    "cxfa_ffdatetimeedit.cpp",
+    "cxfa_ffdatetimeedit.h",
+    "cxfa_ffdoc.cpp",
+    "cxfa_ffdoc.h",
+    "cxfa_ffdocview.cpp",
+    "cxfa_ffdocview.h",
+    "cxfa_ffdropdown.cpp",
+    "cxfa_ffdropdown.h",
+    "cxfa_ffexclgroup.cpp",
+    "cxfa_ffexclgroup.h",
+    "cxfa_fffield.cpp",
+    "cxfa_fffield.h",
+    "cxfa_ffimage.cpp",
+    "cxfa_ffimage.h",
+    "cxfa_ffimageedit.cpp",
+    "cxfa_ffimageedit.h",
+    "cxfa_ffline.cpp",
+    "cxfa_ffline.h",
+    "cxfa_fflistbox.cpp",
+    "cxfa_fflistbox.h",
+    "cxfa_ffnotify.cpp",
+    "cxfa_ffnotify.h",
+    "cxfa_ffnumericedit.cpp",
+    "cxfa_ffnumericedit.h",
+    "cxfa_ffpageview.cpp",
+    "cxfa_ffpageview.h",
+    "cxfa_ffpasswordedit.cpp",
+    "cxfa_ffpasswordedit.h",
+    "cxfa_ffpushbutton.cpp",
+    "cxfa_ffpushbutton.h",
+    "cxfa_ffrectangle.cpp",
+    "cxfa_ffrectangle.h",
+    "cxfa_ffsignature.cpp",
+    "cxfa_ffsignature.h",
+    "cxfa_fftext.cpp",
+    "cxfa_fftext.h",
+    "cxfa_fftextedit.cpp",
+    "cxfa_fftextedit.h",
+    "cxfa_ffwidget.cpp",
+    "cxfa_ffwidget.h",
+    "cxfa_ffwidgethandler.cpp",
+    "cxfa_ffwidgethandler.h",
+    "cxfa_fontmgr.cpp",
+    "cxfa_fontmgr.h",
+    "cxfa_fwladapterwidgetmgr.cpp",
+    "cxfa_fwladapterwidgetmgr.h",
+    "cxfa_fwltheme.cpp",
+    "cxfa_fwltheme.h",
+    "cxfa_imagerenderer.cpp",
+    "cxfa_imagerenderer.h",
+    "cxfa_loadercontext.cpp",
+    "cxfa_loadercontext.h",
+    "cxfa_pieceline.cpp",
+    "cxfa_pieceline.h",
+    "cxfa_readynodeiterator.cpp",
+    "cxfa_readynodeiterator.h",
+    "cxfa_rendercontext.cpp",
+    "cxfa_rendercontext.h",
+    "cxfa_textlayout.cpp",
+    "cxfa_textlayout.h",
+    "cxfa_textparsecontext.cpp",
+    "cxfa_textparsecontext.h",
+    "cxfa_textparser.cpp",
+    "cxfa_textparser.h",
+    "cxfa_textpiece.cpp",
+    "cxfa_textpiece.h",
+    "cxfa_textprovider.cpp",
+    "cxfa_textprovider.h",
+    "cxfa_texttabstopscontext.cpp",
+    "cxfa_texttabstopscontext.h",
+    "fxfa.h",
+    "fxfa_basic.h",
+  ]
+  deps = [
+    "../../core/fpdfapi/parser",
+    "../../core/fpdfdoc",
+    "../../core/fxcodec",
+    "../../core/fxcrt",
+    "../../core/fxcrt/css",
+    "../../core/fxge",
+    "../../fxbarcode",
+    "../../fxjs",
+    "../fde",
+    "../fgas",
+    "../fgas/layout",
+    "../fwl",
+    "../fxgraphics",
+    "layout",
+    "parser",
+  ]
+  allow_circular_includes_from = [
+    "../../fxjs",
+    "layout",
+    "parser",
+  ]
+  configs += [
+    "../../:pdfium_core_config",
+    "../:xfa_warnings",
+  ]
+  visibility = [ "../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cxfa_ffbarcode_unittest.cpp",
+    "cxfa_textparser_unittest.cpp",
+    "fxfa_basic_unittest.cpp",
+  ]
+  deps = [ ":fxfa" ]
+  pdfium_root_dir = "../../"
+}
diff --git a/xfa/fxfa/DEPS b/xfa/fxfa/DEPS
index 886ee8e..cd0aa8d 100644
--- a/xfa/fxfa/DEPS
+++ b/xfa/fxfa/DEPS
@@ -1,3 +1,6 @@
 include_rules = [
   '+fxjs',
+
+  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
+  '+xfa/fwl',
 ]
diff --git a/xfa/fxfa/README.md b/xfa/fxfa/README.md
index b554ce0..8845474 100644
--- a/xfa/fxfa/README.md
+++ b/xfa/fxfa/README.md
@@ -3,15 +3,15 @@
 The main hierarchy in this directory are the form elements:
 
 * CXFA_LayoutItem
-    * CXFA_ContentLayoutItem
+    * CXFA_ContainerLayoutItem
         * CXFA_FFPageView
+    * CXFA_ContentLayoutItem
         * CXFA_FFWidget
-            * CXFA_FFDraw
-                * CXFA_FFArc
-                * CXFA_FFImage
-                * CXFA_FFLine
-                * CXFA_FFRectangle
-                * CXFA_FFText
+            * CXFA_FFArc
+            * CXFA_FFImage
+            * CXFA_FFLine
+            * CXFA_FFRectangle
+            * CXFA_FFText
             * CXFA_FFExclGroup
             * CXFA_FFField
                 * CXFA_FFCheckButton
@@ -25,9 +25,6 @@
                     * CXFA_FFDateTimeEdit
                     * CXFA_FFNumericEdit
                     * CXFA_FFPasswordEdit
-            * CXFA_FFSubform
-
-CXFA_FFDraw is the base class for static elements like text and images.
 
 CXFA_FFField is the base class for widgets. It owns a lower level CFWL widget
 instance from xfa/fwl. The correspondence is:
diff --git a/xfa/fxfa/cxfa_eventparam.cpp b/xfa/fxfa/cxfa_eventparam.cpp
index bd6742f..c3f229d 100644
--- a/xfa/fxfa/cxfa_eventparam.cpp
+++ b/xfa/fxfa/cxfa_eventparam.cpp
@@ -8,39 +8,18 @@
 
 #include "xfa/fxfa/fxfa.h"
 
-CXFA_EventParam::CXFA_EventParam()
-    : m_pTarget(nullptr),
-      m_eType(XFA_EVENT_Unknown),
-      m_bCancelAction(false),
-      m_iCommitKey(0),
-      m_bKeyDown(false),
-      m_bModifier(false),
-      m_bReenter(false),
-      m_iSelEnd(0),
-      m_iSelStart(0),
-      m_bShift(false),
-      m_bIsFormReady(false) {}
-
-CXFA_EventParam::~CXFA_EventParam() {}
+CXFA_EventParam::CXFA_EventParam() = default;
 
 CXFA_EventParam::CXFA_EventParam(const CXFA_EventParam& other) = default;
 
-void CXFA_EventParam::Reset() {
-  m_wsChange.clear();
-  m_bCancelAction = false;
-  m_iCommitKey = 0;
-  m_wsFullText.clear();
-  m_bKeyDown = false;
-  m_bModifier = false;
-  m_wsNewContentType.clear();
-  m_wsNewText.clear();
-  m_wsPrevContentType.clear();
-  m_wsPrevText.clear();
-  m_bReenter = false;
-  m_iSelEnd = 0;
-  m_iSelStart = 0;
-  m_bShift = false;
-  m_wsSoapFaultCode.clear();
-  m_wsSoapFaultString.clear();
-  m_bIsFormReady = false;
+CXFA_EventParam::~CXFA_EventParam() = default;
+
+CXFA_EventParam& CXFA_EventParam::operator=(const CXFA_EventParam& other) =
+    default;
+
+CXFA_EventParam& CXFA_EventParam::operator=(CXFA_EventParam&& other) = default;
+
+WideString CXFA_EventParam::GetNewText() const {
+  return m_wsPrevText.First(m_iSelStart) + m_wsChange +
+         m_wsPrevText.Last(m_wsPrevText.GetLength() - m_iSelEnd);
 }
diff --git a/xfa/fxfa/cxfa_eventparam.h b/xfa/fxfa/cxfa_eventparam.h
index eafd76c..a670263 100644
--- a/xfa/fxfa/cxfa_eventparam.h
+++ b/xfa/fxfa/cxfa_eventparam.h
@@ -7,11 +7,13 @@
 #ifndef XFA_FXFA_CXFA_EVENTPARAM_H_
 #define XFA_FXFA_CXFA_EVENTPARAM_H_
 
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-class CXFA_WidgetAcc;
+class CXFA_Node;
 
-enum XFA_EVENTTYPE {
+enum XFA_EVENTTYPE : uint8_t {
   XFA_EVENT_Click,
   XFA_EVENT_Change,
   XFA_EVENT_DocClose,
@@ -48,31 +50,33 @@
 class CXFA_EventParam {
  public:
   CXFA_EventParam();
-  ~CXFA_EventParam();
   CXFA_EventParam(const CXFA_EventParam& other);
+  ~CXFA_EventParam();
 
-  void Reset();
+  CXFA_EventParam& operator=(const CXFA_EventParam& other);
+  CXFA_EventParam& operator=(CXFA_EventParam&& other);
 
-  CXFA_WidgetAcc* m_pTarget;
-  XFA_EVENTTYPE m_eType;
+  WideString GetNewText() const;
+
+  XFA_EVENTTYPE m_eType = XFA_EVENT_Unknown;
+  bool m_bCancelAction = false;
+  bool m_bKeyDown = false;
+  bool m_bModifier = false;
+  bool m_bReenter = false;
+  bool m_bShift = false;
+  bool m_bIsFormReady = false;
+  int32_t m_iCommitKey = 0;
+  int32_t m_iSelEnd = 0;
+  int32_t m_iSelStart = 0;
+  UnownedPtr<CXFA_Node> m_pTarget;
   WideString m_wsResult;
-  bool m_bCancelAction;
-  int32_t m_iCommitKey;
-  bool m_bKeyDown;
-  bool m_bModifier;
-  bool m_bReenter;
-  int32_t m_iSelEnd;
-  int32_t m_iSelStart;
-  bool m_bShift;
   WideString m_wsChange;
   WideString m_wsFullText;
   WideString m_wsNewContentType;
-  WideString m_wsNewText;
   WideString m_wsPrevContentType;
   WideString m_wsPrevText;
   WideString m_wsSoapFaultCode;
   WideString m_wsSoapFaultString;
-  bool m_bIsFormReady;
 };
 
 #endif  // XFA_FXFA_CXFA_EVENTPARAM_H_
diff --git a/xfa/fxfa/cxfa_ffapp.cpp b/xfa/fxfa/cxfa_ffapp.cpp
index 4cb9deb..656b559 100644
--- a/xfa/fxfa/cxfa_ffapp.cpp
+++ b/xfa/fxfa/cxfa_ffapp.cpp
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
@@ -22,6 +21,17 @@
 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
 #include "xfa/fxfa/cxfa_fwltheme.h"
 
+namespace {
+
+bool g_skipFontLoadForTesting = false;
+
+}  // namespace
+
+// static
+void CXFA_FFApp::SkipFontLoadForTesting(bool skip) {
+  g_skipFontLoadForTesting = skip;
+}
+
 CXFA_FFApp::CXFA_FFApp(IXFA_AppProvider* pProvider) : m_pProvider(pProvider) {
   // Ensure fully initialized before making an app based on |this|.
   m_pFWLApp = pdfium::MakeUnique<CFWL_App>(this);
@@ -29,53 +39,34 @@
 
 CXFA_FFApp::~CXFA_FFApp() {}
 
-std::unique_ptr<CXFA_FFDoc> CXFA_FFApp::CreateDoc(
-    IXFA_DocEnvironment* pDocEnvironment,
-    CPDF_Document* pPDFDoc) {
-  if (!pPDFDoc)
-    return nullptr;
-
-  auto pDoc = pdfium::MakeUnique<CXFA_FFDoc>(this, pDocEnvironment);
-  if (!pDoc->OpenDoc(pPDFDoc))
-    return nullptr;
-
-  return pDoc;
-}
-
-void CXFA_FFApp::SetDefaultFontMgr(
-    std::unique_ptr<CFGAS_DefaultFontManager> pFontMgr) {
-  if (!m_pFontMgr)
-    m_pFontMgr = pdfium::MakeUnique<CXFA_FontMgr>();
-  m_pFontMgr->SetDefFontMgr(std::move(pFontMgr));
-}
-
-CXFA_FontMgr* CXFA_FFApp::GetXFAFontMgr() const {
-  return m_pFontMgr.get();
-}
-
 CFGAS_FontMgr* CXFA_FFApp::GetFDEFontMgr() {
   if (!m_pFDEFontMgr) {
     m_pFDEFontMgr = pdfium::MakeUnique<CFGAS_FontMgr>();
-    if (!m_pFDEFontMgr->EnumFonts())
-      m_pFDEFontMgr = nullptr;
+    if (!g_skipFontLoadForTesting) {
+      if (!m_pFDEFontMgr->EnumFonts())
+        m_pFDEFontMgr = nullptr;
+    }
   }
   return m_pFDEFontMgr.get();
 }
 
-CXFA_FWLTheme* CXFA_FFApp::GetFWLTheme() {
-  if (!m_pFWLTheme)
-    m_pFWLTheme = pdfium::MakeUnique<CXFA_FWLTheme>(this);
+CXFA_FWLTheme* CXFA_FFApp::GetFWLTheme(CXFA_FFDoc* doc) {
+  if (!m_pFWLTheme) {
+    auto fwl_theme = pdfium::MakeUnique<CXFA_FWLTheme>(this);
+    if (fwl_theme->LoadCalendarFont(doc))
+      m_pFWLTheme = std::move(fwl_theme);
+  }
   return m_pFWLTheme.get();
 }
 
-CXFA_FWLAdapterWidgetMgr* CXFA_FFApp::GetFWLAdapterWidgetMgr() {
+CFWL_WidgetMgr::AdapterIface* CXFA_FFApp::GetWidgetMgrAdapter() {
   if (!m_pAdapterWidgetMgr)
     m_pAdapterWidgetMgr = pdfium::MakeUnique<CXFA_FWLAdapterWidgetMgr>();
   return m_pAdapterWidgetMgr.get();
 }
 
-IFWL_AdapterTimerMgr* CXFA_FFApp::GetTimerMgr() const {
-  return m_pProvider->GetTimerMgr();
+TimerHandlerIface* CXFA_FFApp::GetTimerHandler() {
+  return m_pProvider->GetTimerHandler();
 }
 
 void CXFA_FFApp::ClearEventTargets() {
diff --git a/xfa/fxfa/cxfa_ffapp.h b/xfa/fxfa/cxfa_ffapp.h
index 8ffd657..b01ba2f 100644
--- a/xfa/fxfa/cxfa_ffapp.h
+++ b/xfa/fxfa/cxfa_ffapp.h
@@ -8,44 +8,35 @@
 #define XFA_FXFA_CXFA_FFAPP_H_
 
 #include <memory>
-#include <vector>
 
-#include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_app.h"
+#include "xfa/fxfa/cxfa_fontmgr.h"
 #include "xfa/fxfa/fxfa.h"
 
-class CFGAS_DefaultFontManager;
 class CFGAS_FontMgr;
 class CFWL_WidgetMgr;
-class CPDF_Document;
-class CXFA_FFDocHandler;
-class CXFA_FontMgr;
 class CXFA_FWLAdapterWidgetMgr;
 class CXFA_FWLTheme;
-class IFWL_AdapterTimerMgr;
 
-class CXFA_FFApp {
+class CXFA_FFApp : public CFWL_App::AdapterIface {
  public:
+  static void SkipFontLoadForTesting(bool skip);
+
   explicit CXFA_FFApp(IXFA_AppProvider* pProvider);
-  ~CXFA_FFApp();
+  ~CXFA_FFApp() override;
 
-  std::unique_ptr<CXFA_FFDoc> CreateDoc(IXFA_DocEnvironment* pDocEnvironment,
-                                        CPDF_Document* pPDFDoc);
-  void SetDefaultFontMgr(std::unique_ptr<CFGAS_DefaultFontManager> pFontMgr);
+  // CFWL_App::AdapterIface:
+  CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() override;
+  TimerHandlerIface* GetTimerHandler() override;
 
-  CXFA_FWLAdapterWidgetMgr* GetFWLAdapterWidgetMgr();
   CFWL_WidgetMgr* GetFWLWidgetMgr() const { return m_pFWLApp->GetWidgetMgr(); }
-
   CFGAS_FontMgr* GetFDEFontMgr();
-  CXFA_FWLTheme* GetFWLTheme();
+  CXFA_FWLTheme* GetFWLTheme(CXFA_FFDoc* doc);
 
   IXFA_AppProvider* GetAppProvider() const { return m_pProvider.Get(); }
   const CFWL_App* GetFWLApp() const { return m_pFWLApp.get(); }
-  IFWL_AdapterTimerMgr* GetTimerMgr() const;
-  CXFA_FontMgr* GetXFAFontMgr() const;
+  CXFA_FontMgr* GetXFAFontMgr() { return &m_pFontMgr; }
 
   void ClearEventTargets();
 
@@ -56,14 +47,14 @@
   // font manager. The GEFont::LoadFont call takes the manager as a param and
   // stores it internally. When you destroy the GEFont it tries to unregister
   // from the font manager and if the default font manager was destroyed first
-  // get get a use-after-free. The m_pFWLTheme can try to cleanup a GEFont
+  // you get a use-after-free. The m_pFWLTheme can try to cleanup a GEFont
   // when it frees, so make sure it gets cleaned up first. That requires
   // m_pFWLApp to be cleaned up as well.
   //
   // TODO(dsinclair): The GEFont should have the FontMgr as the pointer instead
   // of the DEFFontMgr so this goes away. Bug 561.
   std::unique_ptr<CFGAS_FontMgr> m_pFDEFontMgr;
-  std::unique_ptr<CXFA_FontMgr> m_pFontMgr;
+  CXFA_FontMgr m_pFontMgr;
 
   std::unique_ptr<CXFA_FWLAdapterWidgetMgr> m_pAdapterWidgetMgr;
 
diff --git a/xfa/fxfa/cxfa_ffarc.cpp b/xfa/fxfa/cxfa_ffarc.cpp
index 64a4f70..5b26e92 100644
--- a/xfa/fxfa/cxfa_ffarc.cpp
+++ b/xfa/fxfa/cxfa_ffarc.cpp
@@ -9,14 +9,14 @@
 #include "xfa/fxfa/parser/cxfa_arc.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
 
-CXFA_FFArc::CXFA_FFArc(CXFA_Node* pNode) : CXFA_FFDraw(pNode) {}
+CXFA_FFArc::CXFA_FFArc(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
 CXFA_FFArc::~CXFA_FFArc() {}
 
 void CXFA_FFArc::RenderWidget(CXFA_Graphics* pGS,
                               const CFX_Matrix& matrix,
-                              uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                              HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CXFA_Value* value = m_pNode->GetFormValueIfExists();
@@ -25,11 +25,9 @@
 
   CFX_RectF rtArc = GetRectWithoutRotate();
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    XFA_RectWithoutMargin(rtArc, margin);
+  XFA_RectWithoutMargin(&rtArc, margin);
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
-
   DrawBorder(pGS, value->GetArcIfExists(), rtArc, mtRotate);
 }
diff --git a/xfa/fxfa/cxfa_ffarc.h b/xfa/fxfa/cxfa_ffarc.h
index f765119..022e25e 100644
--- a/xfa/fxfa/cxfa_ffarc.h
+++ b/xfa/fxfa/cxfa_ffarc.h
@@ -7,9 +7,9 @@
 #ifndef XFA_FXFA_CXFA_FFARC_H_
 #define XFA_FXFA_CXFA_FFARC_H_
 
-#include "xfa/fxfa/cxfa_ffdraw.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
 
-class CXFA_FFArc : public CXFA_FFDraw {
+class CXFA_FFArc final : public CXFA_FFWidget {
  public:
   explicit CXFA_FFArc(CXFA_Node* pnode);
   ~CXFA_FFArc() override;
@@ -17,7 +17,7 @@
   // CXFA_FFWidget
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
 };
 
 #endif  // XFA_FXFA_CXFA_FFARC_H_
diff --git a/xfa/fxfa/cxfa_ffbarcode.cpp b/xfa/fxfa/cxfa_ffbarcode.cpp
index 3ed57b9..1c541ec 100644
--- a/xfa/fxfa/cxfa_ffbarcode.cpp
+++ b/xfa/fxfa/cxfa_ffbarcode.cpp
@@ -17,81 +17,103 @@
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
+#include "xfa/fxfa/parser/cxfa_barcode.h"
 #include "xfa/fxfa/parser/cxfa_border.h"
 
 namespace {
 
 const BarCodeInfo g_BarCodeData[] = {
-    {0x7fb4a18, L"ean13", BarcodeType::ean13, BC_EAN13},
-    {0x8d13a3d, L"code11", BarcodeType::code11, BC_UNKNOWN},
-    {0x8d149a8, L"code49", BarcodeType::code49, BC_UNKNOWN},
-    {0x8d16347, L"code93", BarcodeType::code93, BC_UNKNOWN},
-    {0x91a92e2, L"upsMaxicode", BarcodeType::upsMaxicode, BC_UNKNOWN},
-    {0xa7d48dc, L"fim", BarcodeType::fim, BC_UNKNOWN},
-    {0xb359fe9, L"msi", BarcodeType::msi, BC_UNKNOWN},
-    {0x121f738c, L"code2Of5Matrix", BarcodeType::code2Of5Matrix, BC_UNKNOWN},
-    {0x15358616, L"ucc128", BarcodeType::ucc128, BC_UNKNOWN},
-    {0x1f4bfa05, L"rfid", BarcodeType::rfid, BC_UNKNOWN},
-    {0x1fda71bc, L"rss14Stacked", BarcodeType::rss14Stacked, BC_UNKNOWN},
-    {0x22065087, L"ean8add2", BarcodeType::ean8add2, BC_UNKNOWN},
-    {0x2206508a, L"ean8add5", BarcodeType::ean8add5, BC_UNKNOWN},
-    {0x2278366c, L"codabar", BarcodeType::codabar, BC_CODABAR},
-    {0x2a039a8d, L"telepen", BarcodeType::telepen, BC_UNKNOWN},
-    {0x323ed337, L"upcApwcd", BarcodeType::upcApwcd, BC_UNKNOWN},
-    {0x347a1846, L"postUSIMB", BarcodeType::postUSIMB, BC_UNKNOWN},
-    {0x391bb836, L"code128", BarcodeType::code128, BC_CODE128},
-    {0x398eddaf, L"dataMatrix", BarcodeType::dataMatrix, BC_DATAMATRIX},
-    {0x3cff60a8, L"upcEadd2", BarcodeType::upcEadd2, BC_UNKNOWN},
-    {0x3cff60ab, L"upcEadd5", BarcodeType::upcEadd5, BC_UNKNOWN},
-    {0x402cb188, L"code2Of5Standard", BarcodeType::code2Of5Standard,
+    {0x7fb4a18, "ean13", BarcodeType::ean13, BC_EAN13},
+    {0x8d13a3d, "code11", BarcodeType::code11, BC_UNKNOWN},
+    {0x8d149a8, "code49", BarcodeType::code49, BC_UNKNOWN},
+    {0x8d16347, "code93", BarcodeType::code93, BC_UNKNOWN},
+    {0x91a92e2, "upsMaxicode", BarcodeType::upsMaxicode, BC_UNKNOWN},
+    {0xa7d48dc, "fim", BarcodeType::fim, BC_UNKNOWN},
+    {0xb359fe9, "msi", BarcodeType::msi, BC_UNKNOWN},
+    {0x121f738c, "code2Of5Matrix", BarcodeType::code2Of5Matrix, BC_UNKNOWN},
+    {0x15358616, "ucc128", BarcodeType::ucc128, BC_UNKNOWN},
+    {0x1f4bfa05, "rfid", BarcodeType::rfid, BC_UNKNOWN},
+    {0x1fda71bc, "rss14Stacked", BarcodeType::rss14Stacked, BC_UNKNOWN},
+    {0x22065087, "ean8add2", BarcodeType::ean8add2, BC_UNKNOWN},
+    {0x2206508a, "ean8add5", BarcodeType::ean8add5, BC_UNKNOWN},
+    {0x2278366c, "codabar", BarcodeType::codabar, BC_CODABAR},
+    {0x2a039a8d, "telepen", BarcodeType::telepen, BC_UNKNOWN},
+    {0x323ed337, "upcApwcd", BarcodeType::upcApwcd, BC_UNKNOWN},
+    {0x347a1846, "postUSIMB", BarcodeType::postUSIMB, BC_UNKNOWN},
+    {0x391bb836, "code128", BarcodeType::code128, BC_CODE128},
+    {0x398eddaf, "dataMatrix", BarcodeType::dataMatrix, BC_DATAMATRIX},
+    {0x3cff60a8, "upcEadd2", BarcodeType::upcEadd2, BC_UNKNOWN},
+    {0x3cff60ab, "upcEadd5", BarcodeType::upcEadd5, BC_UNKNOWN},
+    {0x402cb188, "code2Of5Standard", BarcodeType::code2Of5Standard, BC_UNKNOWN},
+    {0x411764f7, "aztec", BarcodeType::aztec, BC_UNKNOWN},
+    {0x44d4e84c, "ean8", BarcodeType::ean8, BC_EAN8},
+    {0x48468902, "ucc128sscc", BarcodeType::ucc128sscc, BC_UNKNOWN},
+    {0x4880aea4, "upcAadd2", BarcodeType::upcAadd2, BC_UNKNOWN},
+    {0x4880aea7, "upcAadd5", BarcodeType::upcAadd5, BC_UNKNOWN},
+    {0x54f18256, "code2Of5Industrial", BarcodeType::code2Of5Industrial,
      BC_UNKNOWN},
-    {0x411764f7, L"aztec", BarcodeType::aztec, BC_UNKNOWN},
-    {0x44d4e84c, L"ean8", BarcodeType::ean8, BC_EAN8},
-    {0x48468902, L"ucc128sscc", BarcodeType::ucc128sscc, BC_UNKNOWN},
-    {0x4880aea4, L"upcAadd2", BarcodeType::upcAadd2, BC_UNKNOWN},
-    {0x4880aea7, L"upcAadd5", BarcodeType::upcAadd5, BC_UNKNOWN},
-    {0x54f18256, L"code2Of5Industrial", BarcodeType::code2Of5Industrial,
+    {0x58e15f25, "rss14Limited", BarcodeType::rss14Limited, BC_UNKNOWN},
+    {0x5c08d1b9, "postAUSReplyPaid", BarcodeType::postAUSReplyPaid, BC_UNKNOWN},
+    {0x5fa700bd, "rss14", BarcodeType::rss14, BC_UNKNOWN},
+    {0x631a7e35, "logmars", BarcodeType::logmars, BC_UNKNOWN},
+    {0x6a236236, "pdf417", BarcodeType::pdf417, BC_PDF417},
+    {0x6d098ece, "upcean2", BarcodeType::upcean2, BC_UNKNOWN},
+    {0x6d098ed1, "upcean5", BarcodeType::upcean5, BC_UNKNOWN},
+    {0x76b04eed, "code3Of9extended", BarcodeType::code3Of9extended, BC_UNKNOWN},
+    {0x7c7db84a, "maxicode", BarcodeType::maxicode, BC_UNKNOWN},
+    {0x8266f7f7, "ucc128random", BarcodeType::ucc128random, BC_UNKNOWN},
+    {0x83eca147, "postUSDPBC", BarcodeType::postUSDPBC, BC_UNKNOWN},
+    {0x8dd71de0, "postAUSStandard", BarcodeType::postAUSStandard, BC_UNKNOWN},
+    {0x98adad85, "plessey", BarcodeType::plessey, BC_UNKNOWN},
+    {0x9f84cce6, "ean13pwcd", BarcodeType::ean13pwcd, BC_UNKNOWN},
+    {0xb514fbe9, "upcA", BarcodeType::upcA, BC_UPCA},
+    {0xb514fbed, "upcE", BarcodeType::upcE, BC_UNKNOWN},
+    {0xb5c6a853, "ean13add2", BarcodeType::ean13add2, BC_UNKNOWN},
+    {0xb5c6a856, "ean13add5", BarcodeType::ean13add5, BC_UNKNOWN},
+    {0xb81fc512, "postUKRM4SCC", BarcodeType::postUKRM4SCC, BC_UNKNOWN},
+    {0xbad34b22, "code128SSCC", BarcodeType::code128SSCC, BC_UNKNOWN},
+    {0xbfbe0cf6, "postUS5Zip", BarcodeType::postUS5Zip, BC_UNKNOWN},
+    {0xc56618e8, "pdf417macro", BarcodeType::pdf417macro, BC_UNKNOWN},
+    {0xca730f8a, "code2Of5Interleaved", BarcodeType::code2Of5Interleaved,
      BC_UNKNOWN},
-    {0x58e15f25, L"rss14Limited", BarcodeType::rss14Limited, BC_UNKNOWN},
-    {0x5c08d1b9, L"postAUSReplyPaid", BarcodeType::postAUSReplyPaid,
-     BC_UNKNOWN},
-    {0x5fa700bd, L"rss14", BarcodeType::rss14, BC_UNKNOWN},
-    {0x631a7e35, L"logmars", BarcodeType::logmars, BC_UNKNOWN},
-    {0x6a236236, L"pdf417", BarcodeType::pdf417, BC_PDF417},
-    {0x6d098ece, L"upcean2", BarcodeType::upcean2, BC_UNKNOWN},
-    {0x6d098ed1, L"upcean5", BarcodeType::upcean5, BC_UNKNOWN},
-    {0x76b04eed, L"code3Of9extended", BarcodeType::code3Of9extended,
-     BC_UNKNOWN},
-    {0x7c7db84a, L"maxicode", BarcodeType::maxicode, BC_UNKNOWN},
-    {0x8266f7f7, L"ucc128random", BarcodeType::ucc128random, BC_UNKNOWN},
-    {0x83eca147, L"postUSDPBC", BarcodeType::postUSDPBC, BC_UNKNOWN},
-    {0x8dd71de0, L"postAUSStandard", BarcodeType::postAUSStandard, BC_UNKNOWN},
-    {0x98adad85, L"plessey", BarcodeType::plessey, BC_UNKNOWN},
-    {0x9f84cce6, L"ean13pwcd", BarcodeType::ean13pwcd, BC_UNKNOWN},
-    {0xb514fbe9, L"upcA", BarcodeType::upcA, BC_UPCA},
-    {0xb514fbed, L"upcE", BarcodeType::upcE, BC_UNKNOWN},
-    {0xb5c6a853, L"ean13add2", BarcodeType::ean13add2, BC_UNKNOWN},
-    {0xb5c6a856, L"ean13add5", BarcodeType::ean13add5, BC_UNKNOWN},
-    {0xb81fc512, L"postUKRM4SCC", BarcodeType::postUKRM4SCC, BC_UNKNOWN},
-    {0xbad34b22, L"code128SSCC", BarcodeType::code128SSCC, BC_UNKNOWN},
-    {0xbfbe0cf6, L"postUS5Zip", BarcodeType::postUS5Zip, BC_UNKNOWN},
-    {0xc56618e8, L"pdf417macro", BarcodeType::pdf417macro, BC_UNKNOWN},
-    {0xca730f8a, L"code2Of5Interleaved", BarcodeType::code2Of5Interleaved,
-     BC_UNKNOWN},
-    {0xd0097ac6, L"rss14Expanded", BarcodeType::rss14Expanded, BC_UNKNOWN},
-    {0xd25a0240, L"postAUSCust2", BarcodeType::postAUSCust2, BC_UNKNOWN},
-    {0xd25a0241, L"postAUSCust3", BarcodeType::postAUSCust3, BC_UNKNOWN},
-    {0xd53ed3e7, L"rss14Truncated", BarcodeType::rss14Truncated, BC_UNKNOWN},
-    {0xe72bcd57, L"code128A", BarcodeType::code128A, BC_UNKNOWN},
-    {0xe72bcd58, L"code128B", BarcodeType::code128B, BC_CODE128_B},
-    {0xe72bcd59, L"code128C", BarcodeType::code128C, BC_CODE128_C},
-    {0xee83c50f, L"rss14StackedOmni", BarcodeType::rss14StackedOmni,
-     BC_UNKNOWN},
-    {0xf2a18f7e, L"QRCode", BarcodeType::QRCode, BC_QR_CODE},
-    {0xfaeaf37f, L"postUSStandard", BarcodeType::postUSStandard, BC_UNKNOWN},
-    {0xfb48155c, L"code3Of9", BarcodeType::code3Of9, BC_CODE39},
+    {0xd0097ac6, "rss14Expanded", BarcodeType::rss14Expanded, BC_UNKNOWN},
+    {0xd25a0240, "postAUSCust2", BarcodeType::postAUSCust2, BC_UNKNOWN},
+    {0xd25a0241, "postAUSCust3", BarcodeType::postAUSCust3, BC_UNKNOWN},
+    {0xd53ed3e7, "rss14Truncated", BarcodeType::rss14Truncated, BC_UNKNOWN},
+    {0xe72bcd57, "code128A", BarcodeType::code128A, BC_UNKNOWN},
+    {0xe72bcd58, "code128B", BarcodeType::code128B, BC_CODE128_B},
+    {0xe72bcd59, "code128C", BarcodeType::code128C, BC_CODE128_C},
+    {0xee83c50f, "rss14StackedOmni", BarcodeType::rss14StackedOmni, BC_UNKNOWN},
+    {0xf2a18f7e, "QRCode", BarcodeType::QRCode, BC_QR_CODE},
+    {0xfaeaf37f, "postUSStandard", BarcodeType::postUSStandard, BC_UNKNOWN},
+    {0xfb48155c, "code3Of9", BarcodeType::code3Of9, BC_CODE39},
 };
 
+Optional<BC_CHAR_ENCODING> CharEncodingFromString(const WideString& value) {
+  if (value.CompareNoCase(L"UTF-16"))
+    return CHAR_ENCODING_UNICODE;
+  if (value.CompareNoCase(L"UTF-8"))
+    return CHAR_ENCODING_UTF8;
+  return {};
+}
+
+Optional<BC_TEXT_LOC> TextLocFromAttribute(XFA_AttributeValue value) {
+  switch (value) {
+    case XFA_AttributeValue::None:
+      return BC_TEXT_LOC_NONE;
+    case XFA_AttributeValue::Above:
+      return BC_TEXT_LOC_ABOVE;
+    case XFA_AttributeValue::Below:
+      return BC_TEXT_LOC_BELOW;
+    case XFA_AttributeValue::AboveEmbedded:
+      return BC_TEXT_LOC_ABOVEEMBED;
+    case XFA_AttributeValue::BelowEmbedded:
+      return BC_TEXT_LOC_BELOWEMBED;
+    default:
+      return {};
+  }
+}
+
 }  // namespace.
 
 // static
@@ -105,113 +127,117 @@
       FX_HashCode_GetW(wsName.AsStringView(), true),
       [](const BarCodeInfo& arg, uint32_t hash) { return arg.uHash < hash; });
 
-  if (it != std::end(g_BarCodeData) && wsName.AsStringView() == it->pName)
+  if (it != std::end(g_BarCodeData) && wsName.EqualsASCII(it->pName))
     return it;
 
   return nullptr;
 }
 
-CXFA_FFBarcode::CXFA_FFBarcode(CXFA_Node* pNode) : CXFA_FFTextEdit(pNode) {}
+CXFA_FFBarcode::CXFA_FFBarcode(CXFA_Node* pNode, CXFA_Barcode* barcode)
+    : CXFA_FFTextEdit(pNode), barcode_(barcode) {}
 
-CXFA_FFBarcode::~CXFA_FFBarcode() {}
+CXFA_FFBarcode::~CXFA_FFBarcode() = default;
 
 bool CXFA_FFBarcode::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNew = pdfium::MakeUnique<CFWL_Barcode>(GetFWLApp());
   CFWL_Barcode* pFWLBarcode = pNew.get();
-  m_pNormalWidget = std::move(pNew);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNew));
+  pFWLBarcode->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  m_pNormalWidget->LockUpdate();
+  CFWL_NoteDriver* pNoteDriver = pFWLBarcode->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pFWLBarcode, pFWLBarcode);
+  m_pOldDelegate = pFWLBarcode->GetDelegate();
+  pFWLBarcode->SetDelegate(this);
 
-  pFWLBarcode->SetText(
-      m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Display));
-  UpdateWidgetProperty();
-  m_pNormalWidget->UnlockUpdate();
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pFWLBarcode);
+    pFWLBarcode->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+    UpdateWidgetProperty();
+  }
+
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFBarcode::RenderWidget(CXFA_Graphics* pGS,
                                   const CFX_Matrix& matrix,
-                                  uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                  HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
-  DrawBorder(pGS, m_pNode->GetWidgetAcc()->GetUIBorder(), m_rtUI, mtRotate);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
   RenderCaption(pGS, &mtRotate);
-  CFX_RectF rtWidget = m_pNormalWidget->GetWidgetRect();
+  CFX_RectF rtWidget = GetNormalWidget()->GetWidgetRect();
 
   CFX_Matrix mt(1, 0, 0, 1, rtWidget.left, rtWidget.top);
   mt.Concat(mtRotate);
-  m_pNormalWidget->DrawWidget(pGS, mt);
+  GetNormalWidget()->DrawWidget(pGS, mt);
 }
 
 void CXFA_FFBarcode::UpdateWidgetProperty() {
   CXFA_FFTextEdit::UpdateWidgetProperty();
 
-  auto* node = GetNode();
-  const BarCodeInfo* info = GetBarcodeTypeByName(node->GetBarcodeType());
+  const BarCodeInfo* info = GetBarcodeTypeByName(barcode_->GetBarcodeType());
   if (!info)
     return;
 
-  auto* pBarCodeWidget = static_cast<CFWL_Barcode*>(m_pNormalWidget.get());
+  auto* pBarCodeWidget = static_cast<CFWL_Barcode*>(GetNormalWidget());
   pBarCodeWidget->SetType(info->eBCType);
 
-  Optional<BC_CHAR_ENCODING> encoding =
-      node->GetBarcodeAttribute_CharEncoding();
-  if (encoding)
-    pBarCodeWidget->SetCharEncoding(*encoding);
+  Optional<WideString> encoding_string = barcode_->GetCharEncoding();
+  if (encoding_string) {
+    Optional<BC_CHAR_ENCODING> encoding =
+        CharEncodingFromString(*encoding_string);
+    if (encoding)
+      pBarCodeWidget->SetCharEncoding(*encoding);
+  }
 
-  Optional<bool> calcChecksum = node->GetBarcodeAttribute_Checksum();
+  Optional<bool> calcChecksum = barcode_->GetChecksum();
   if (calcChecksum)
     pBarCodeWidget->SetCalChecksum(*calcChecksum);
 
-  Optional<int32_t> dataLen = node->GetBarcodeAttribute_DataLength();
+  Optional<int32_t> dataLen = barcode_->GetDataLength();
   if (dataLen)
     pBarCodeWidget->SetDataLength(*dataLen);
 
-  Optional<char> startChar = node->GetBarcodeAttribute_StartChar();
+  Optional<char> startChar = barcode_->GetStartChar();
   if (startChar)
     pBarCodeWidget->SetStartChar(*startChar);
 
-  Optional<char> endChar = node->GetBarcodeAttribute_EndChar();
+  Optional<char> endChar = barcode_->GetEndChar();
   if (endChar)
     pBarCodeWidget->SetEndChar(*endChar);
 
-  Optional<int32_t> ecLevel = node->GetBarcodeAttribute_ECLevel();
+  Optional<int32_t> ecLevel = barcode_->GetECLevel();
   if (ecLevel)
     pBarCodeWidget->SetErrorCorrectionLevel(*ecLevel);
 
-  Optional<int32_t> width = node->GetBarcodeAttribute_ModuleWidth();
+  Optional<int32_t> width = barcode_->GetModuleWidth();
   if (width)
     pBarCodeWidget->SetModuleWidth(*width);
 
-  Optional<int32_t> height = node->GetBarcodeAttribute_ModuleHeight();
+  Optional<int32_t> height = barcode_->GetModuleHeight();
   if (height)
     pBarCodeWidget->SetModuleHeight(*height);
 
-  Optional<bool> printCheck = node->GetBarcodeAttribute_PrintChecksum();
+  Optional<bool> printCheck = barcode_->GetPrintChecksum();
   if (printCheck)
     pBarCodeWidget->SetPrintChecksum(*printCheck);
 
-  Optional<BC_TEXT_LOC> textLoc = node->GetBarcodeAttribute_TextLocation();
-  if (textLoc)
-    pBarCodeWidget->SetTextLocation(*textLoc);
+  Optional<XFA_AttributeValue> text_attr = barcode_->GetTextLocation();
+  if (text_attr) {
+    Optional<BC_TEXT_LOC> textLoc = TextLocFromAttribute(*text_attr);
+    if (textLoc)
+      pBarCodeWidget->SetTextLocation(*textLoc);
+  }
 
-  Optional<bool> truncate = node->GetBarcodeAttribute_Truncate();
-  if (truncate)
-    pBarCodeWidget->SetTruncated(*truncate);
+  // Truncated is currently not a supported flag.
 
-  Optional<int8_t> ratio = node->GetBarcodeAttribute_WideNarrowRatio();
+  Optional<int8_t> ratio = barcode_->GetWideNarrowRatio();
   if (ratio)
     pBarCodeWidget->SetWideNarrowRatio(*ratio);
 
@@ -222,18 +248,14 @@
   }
 }
 
-bool CXFA_FFBarcode::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  auto* pBarCodeWidget = static_cast<CFWL_Barcode*>(m_pNormalWidget.get());
+bool CXFA_FFBarcode::AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                              const CFX_PointF& point,
+                                              FWL_MouseCommand command) {
+  auto* pBarCodeWidget = static_cast<CFWL_Barcode*>(GetNormalWidget());
   if (!pBarCodeWidget || pBarCodeWidget->IsProtectedType())
     return false;
-  if (!m_pNode->IsOpenAccess())
+  if (command == FWL_MouseCommand::LeftButtonDown && !m_pNode->IsOpenAccess())
     return false;
-  return CXFA_FFTextEdit::OnLButtonDown(dwFlags, point);
-}
 
-bool CXFA_FFBarcode::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  auto* pBarCodeWidget = static_cast<CFWL_Barcode*>(m_pNormalWidget.get());
-  if (!pBarCodeWidget || pBarCodeWidget->IsProtectedType())
-    return false;
-  return CXFA_FFTextEdit::OnRButtonDown(dwFlags, point);
+  return CXFA_FFTextEdit::AcceptsFocusOnButtonDown(dwFlags, point, command);
 }
diff --git a/xfa/fxfa/cxfa_ffbarcode.h b/xfa/fxfa/cxfa_ffbarcode.h
index 9398470..83e0884 100644
--- a/xfa/fxfa/cxfa_ffbarcode.h
+++ b/xfa/fxfa/cxfa_ffbarcode.h
@@ -7,6 +7,7 @@
 #ifndef XFA_FXFA_CXFA_FFBARCODE_H_
 #define XFA_FXFA_CXFA_FFBARCODE_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "fxbarcode/BC_Library.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_fftextedit.h"
@@ -77,27 +78,33 @@
 };
 
 struct BarCodeInfo {
-  uint32_t uHash;
-  const wchar_t* pName;
+  uint32_t uHash;     // |pName| hashed as if wide string.
+  const char* pName;  // Raw, POD struct.
   BarcodeType eName;
   BC_TYPE eBCType;
 };
 
-class CXFA_FFBarcode : public CXFA_FFTextEdit {
+class CXFA_Barcode;
+
+class CXFA_FFBarcode final : public CXFA_FFTextEdit {
  public:
   static const BarCodeInfo* GetBarcodeTypeByName(const WideString& wsName);
 
-  explicit CXFA_FFBarcode(CXFA_Node* pNode);
+  CXFA_FFBarcode(CXFA_Node* pNode, CXFA_Barcode* barcode);
   ~CXFA_FFBarcode() override;
 
   // CXFA_FFTextEdit
   bool LoadWidget() override;
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
   void UpdateWidgetProperty() override;
-  bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
+  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                const CFX_PointF& point,
+                                FWL_MouseCommand command) override;
+
+ private:
+  UnownedPtr<CXFA_Barcode> const barcode_;
 };
 
 #endif  // XFA_FXFA_CXFA_FFBARCODE_H_
diff --git a/xfa/fxfa/cxfa_ffbarcode_unittest.cpp b/xfa/fxfa/cxfa_ffbarcode_unittest.cpp
index 455b5a6..6236b10 100644
--- a/xfa/fxfa/cxfa_ffbarcode_unittest.cpp
+++ b/xfa/fxfa/cxfa_ffbarcode_unittest.cpp
@@ -5,7 +5,6 @@
 #include "xfa/fxfa/cxfa_ffbarcode.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
 
 TEST(XFA_FFBarcode, GetBarcodeTypeByName) {
   EXPECT_EQ(nullptr, CXFA_FFBarcode::GetBarcodeTypeByName(L""));
diff --git a/xfa/fxfa/cxfa_ffcheckbutton.cpp b/xfa/fxfa/cxfa_ffcheckbutton.cpp
index 89af82c..1d39e2f 100644
--- a/xfa/fxfa/cxfa_ffcheckbutton.cpp
+++ b/xfa/fxfa/cxfa_ffcheckbutton.cpp
@@ -21,66 +21,69 @@
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/parser/cxfa_border.h"
 #include "xfa/fxfa/parser/cxfa_caption.h"
+#include "xfa/fxfa/parser/cxfa_checkbutton.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
 
-CXFA_FFCheckButton::CXFA_FFCheckButton(CXFA_Node* pNode)
-    : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
+CXFA_FFCheckButton::CXFA_FFCheckButton(CXFA_Node* pNode,
+                                       CXFA_CheckButton* button)
+    : CXFA_FFField(pNode), button_(button) {}
 
-CXFA_FFCheckButton::~CXFA_FFCheckButton() {}
+CXFA_FFCheckButton::~CXFA_FFCheckButton() = default;
 
 bool CXFA_FFCheckButton::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNew = pdfium::MakeUnique<CFWL_CheckBox>(GetFWLApp());
   CFWL_CheckBox* pCheckBox = pNew.get();
-  m_pNormalWidget = std::move(pNew);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNew));
+  pCheckBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  if (m_pNode->GetWidgetAcc()->IsRadioButton())
+  CFWL_NoteDriver* pNoteDriver = pCheckBox->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pCheckBox, pCheckBox);
+  m_pOldDelegate = pCheckBox->GetDelegate();
+  pCheckBox->SetDelegate(this);
+  if (m_pNode->IsRadioButton())
     pCheckBox->ModifyStylesEx(FWL_STYLEEXT_CKB_RadioButton, 0xFFFFFFFF);
 
-  m_pNormalWidget->LockUpdate();
-  UpdateWidgetProperty();
-  SetFWLCheckState(m_pNode->GetWidgetAcc()->GetCheckState());
-  m_pNormalWidget->UnlockUpdate();
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pCheckBox);
+    UpdateWidgetProperty();
+    SetFWLCheckState(m_pNode->GetCheckState());
+  }
+
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFCheckButton::UpdateWidgetProperty() {
-  auto* pCheckBox = static_cast<CFWL_CheckBox*>(m_pNormalWidget.get());
+  auto* pCheckBox = static_cast<CFWL_CheckBox*>(GetNormalWidget());
   if (!pCheckBox)
     return;
 
-  pCheckBox->SetBoxSize(m_pNode->GetWidgetAcc()->GetCheckButtonSize());
+  pCheckBox->SetBoxSize(m_pNode->GetCheckButtonSize());
   uint32_t dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCross;
-  switch (m_pNode->GetWidgetAcc()->GetCheckButtonMark()) {
-    case XFA_AttributeEnum::Check:
+  switch (button_->GetMark()) {
+    case XFA_AttributeValue::Check:
       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCheck;
       break;
-    case XFA_AttributeEnum::Circle:
+    case XFA_AttributeValue::Circle:
       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCircle;
       break;
-    case XFA_AttributeEnum::Cross:
+    case XFA_AttributeValue::Cross:
       break;
-    case XFA_AttributeEnum::Diamond:
+    case XFA_AttributeValue::Diamond:
       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeDiamond;
       break;
-    case XFA_AttributeEnum::Square:
+    case XFA_AttributeValue::Square:
       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeSquare;
       break;
-    case XFA_AttributeEnum::Star:
+    case XFA_AttributeValue::Star:
       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeStar;
       break;
     default: {
-      if (m_pNode->GetWidgetAcc()->IsCheckButtonRound())
+      if (button_->IsRound())
         dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCircle;
     } break;
   }
-  if (m_pNode->GetWidgetAcc()->IsAllowNeutral())
+  if (button_->IsAllowNeutral())
     dwStyleEx |= FWL_STYLEEXT_CKB_3State;
 
   pCheckBox->ModifyStylesEx(
@@ -90,13 +93,12 @@
 bool CXFA_FFCheckButton::PerformLayout() {
   CXFA_FFWidget::PerformLayout();
 
-  float fCheckSize = m_pNode->GetWidgetAcc()->GetCheckButtonSize();
+  float fCheckSize = m_pNode->GetCheckButtonSize();
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
   CFX_RectF rtWidget = GetRectWithoutRotate();
-  if (margin)
-    XFA_RectWithoutMargin(rtWidget, margin);
+  XFA_RectWithoutMargin(&rtWidget, margin);
 
-  XFA_AttributeEnum iCapPlacement = XFA_AttributeEnum::Unknown;
+  XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown;
   float fCapReserve = 0;
   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
   if (caption && caption->IsVisible()) {
@@ -104,8 +106,8 @@
     iCapPlacement = caption->GetPlacementType();
     fCapReserve = caption->GetReserve();
     if (fCapReserve <= 0) {
-      if (iCapPlacement == XFA_AttributeEnum::Top ||
-          iCapPlacement == XFA_AttributeEnum::Bottom) {
+      if (iCapPlacement == XFA_AttributeValue::Top ||
+          iCapPlacement == XFA_AttributeValue::Bottom) {
         fCapReserve = rtWidget.height - fCheckSize;
       } else {
         fCapReserve = rtWidget.width - fCheckSize;
@@ -113,8 +115,8 @@
     }
   }
 
-  XFA_AttributeEnum iHorzAlign = XFA_AttributeEnum::Left;
-  XFA_AttributeEnum iVertAlign = XFA_AttributeEnum::Top;
+  XFA_AttributeValue iHorzAlign = XFA_AttributeValue::Left;
+  XFA_AttributeValue iVertAlign = XFA_AttributeValue::Top;
   CXFA_Para* para = m_pNode->GetParaIfExists();
   if (para) {
     iHorzAlign = para->GetHorizontalAlign();
@@ -124,74 +126,73 @@
   m_rtUI = rtWidget;
   CXFA_Margin* captionMargin = caption ? caption->GetMarginIfExists() : nullptr;
   switch (iCapPlacement) {
-    case XFA_AttributeEnum::Left: {
+    case XFA_AttributeValue::Left: {
       m_rtCaption.width = fCapReserve;
       CapLeftRightPlacement(captionMargin);
       m_rtUI.width -= fCapReserve;
       m_rtUI.left += fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Top: {
+    case XFA_AttributeValue::Top: {
       m_rtCaption.height = fCapReserve;
-      XFA_RectWithoutMargin(m_rtCaption, captionMargin);
+      XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
       m_rtUI.height -= fCapReserve;
       m_rtUI.top += fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Right: {
+    case XFA_AttributeValue::Right: {
       m_rtCaption.left = m_rtCaption.right() - fCapReserve;
       m_rtCaption.width = fCapReserve;
       CapLeftRightPlacement(captionMargin);
       m_rtUI.width -= fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Bottom: {
+    case XFA_AttributeValue::Bottom: {
       m_rtCaption.top = m_rtCaption.bottom() - fCapReserve;
       m_rtCaption.height = fCapReserve;
-      XFA_RectWithoutMargin(m_rtCaption, captionMargin);
+      XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
       m_rtUI.height -= fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Inline:
+    case XFA_AttributeValue::Inline:
       break;
     default:
-      iHorzAlign = XFA_AttributeEnum::Right;
+      iHorzAlign = XFA_AttributeValue::Right;
       break;
   }
 
-  if (iHorzAlign == XFA_AttributeEnum::Center)
+  if (iHorzAlign == XFA_AttributeValue::Center)
     m_rtUI.left += (m_rtUI.width - fCheckSize) / 2;
-  else if (iHorzAlign == XFA_AttributeEnum::Right)
+  else if (iHorzAlign == XFA_AttributeValue::Right)
     m_rtUI.left = m_rtUI.right() - fCheckSize;
 
-  if (iVertAlign == XFA_AttributeEnum::Middle)
+  if (iVertAlign == XFA_AttributeValue::Middle)
     m_rtUI.top += (m_rtUI.height - fCheckSize) / 2;
-  else if (iVertAlign == XFA_AttributeEnum::Bottom)
+  else if (iVertAlign == XFA_AttributeValue::Bottom)
     m_rtUI.top = m_rtUI.bottom() - fCheckSize;
 
   m_rtUI.width = fCheckSize;
   m_rtUI.height = fCheckSize;
   AddUIMargin(iCapPlacement);
   m_rtCheckBox = m_rtUI;
-  CXFA_Border* borderUI = m_pNode->GetWidgetAcc()->GetUIBorder();
+  CXFA_Border* borderUI = m_pNode->GetUIBorder();
   if (borderUI) {
     CXFA_Margin* borderMargin = borderUI->GetMarginIfExists();
-    if (borderMargin)
-      XFA_RectWithoutMargin(m_rtUI, borderMargin);
+    XFA_RectWithoutMargin(&m_rtUI, borderMargin);
   }
 
   m_rtUI.Normalize();
   LayoutCaption();
   SetFWLRect();
-  if (m_pNormalWidget)
-    m_pNormalWidget->Update();
+  if (GetNormalWidget())
+    GetNormalWidget()->Update();
 
   return true;
 }
 
 void CXFA_FFCheckButton::CapLeftRightPlacement(
     const CXFA_Margin* captionMargin) {
-  XFA_RectWithoutMargin(m_rtCaption, captionMargin);
+  XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
   if (m_rtCaption.height < 0)
     m_rtCaption.top += m_rtCaption.height;
   if (m_rtCaption.width < 0) {
@@ -200,15 +201,15 @@
   }
 }
 
-void CXFA_FFCheckButton::AddUIMargin(XFA_AttributeEnum iCapPlacement) {
-  CFX_RectF rtUIMargin = m_pNode->GetWidgetAcc()->GetUIMargin();
+void CXFA_FFCheckButton::AddUIMargin(XFA_AttributeValue iCapPlacement) {
+  CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
   m_rtUI.top -= rtUIMargin.top / 2 - rtUIMargin.height / 2;
 
   float fLeftAddRight = rtUIMargin.left + rtUIMargin.width;
   float fTopAddBottom = rtUIMargin.top + rtUIMargin.height;
   if (m_rtUI.width < fLeftAddRight) {
-    if (iCapPlacement == XFA_AttributeEnum::Right ||
-        iCapPlacement == XFA_AttributeEnum::Left) {
+    if (iCapPlacement == XFA_AttributeValue::Right ||
+        iCapPlacement == XFA_AttributeValue::Left) {
       m_rtUI.left -= fLeftAddRight - m_rtUI.width;
     } else {
       m_rtUI.left -= 2 * (fLeftAddRight - m_rtUI.width);
@@ -216,7 +217,7 @@
     m_rtUI.width += 2 * (fLeftAddRight - m_rtUI.width);
   }
   if (m_rtUI.height < fTopAddBottom) {
-    if (iCapPlacement == XFA_AttributeEnum::Right)
+    if (iCapPlacement == XFA_AttributeValue::Right)
       m_rtUI.left -= fTopAddBottom - m_rtUI.height;
 
     m_rtUI.top -= fTopAddBottom - m_rtUI.height;
@@ -226,40 +227,40 @@
 
 void CXFA_FFCheckButton::RenderWidget(CXFA_Graphics* pGS,
                                       const CFX_Matrix& matrix,
-                                      uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                      HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
-  DrawBorderWithFlag(pGS, m_pNode->GetWidgetAcc()->GetUIBorder(), m_rtUI,
-                     mtRotate, m_pNode->GetWidgetAcc()->IsCheckButtonRound());
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
+  DrawBorderWithFlag(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate,
+                     button_->IsRound());
   RenderCaption(pGS, &mtRotate);
-  DrawHighlight(pGS, &mtRotate, dwStatus,
-                m_pNode->GetWidgetAcc()->IsCheckButtonRound());
+  DrawHighlight(pGS, &mtRotate, highlight,
+                button_->IsRound() ? kRoundShape : kSquareShape);
   CFX_Matrix mt(1, 0, 0, 1, m_rtCheckBox.left, m_rtCheckBox.top);
   mt.Concat(mtRotate);
-  GetApp()->GetFWLWidgetMgr()->OnDrawWidget(m_pNormalWidget.get(), pGS, mt);
+  GetApp()->GetFWLWidgetMgr()->OnDrawWidget(GetNormalWidget(), pGS, mt);
 }
 
 bool CXFA_FFCheckButton::OnLButtonUp(uint32_t dwFlags,
                                      const CFX_PointF& point) {
-  if (!m_pNormalWidget || !IsButtonDown())
+  if (!GetNormalWidget() || !IsButtonDown())
     return false;
 
+  ObservedPtr<CXFA_FFCheckButton> pWatched(this);
   SetButtonDown(false);
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::LeftButtonUp;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::LeftButtonUp, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 XFA_CHECKSTATE CXFA_FFCheckButton::FWLState2XFAState() {
-  uint32_t dwState = m_pNormalWidget->GetStates();
+  uint32_t dwState = GetNormalWidget()->GetStates();
   if (dwState & FWL_STATE_CKB_Checked)
     return XFA_CHECKSTATE_On;
   if (dwState & FWL_STATE_CKB_Neutral)
@@ -269,31 +270,31 @@
 
 bool CXFA_FFCheckButton::CommitData() {
   XFA_CHECKSTATE eCheckState = FWLState2XFAState();
-  m_pNode->GetWidgetAcc()->SetCheckState(eCheckState, true);
+  m_pNode->SetCheckState(eCheckState, true);
   return true;
 }
 
 bool CXFA_FFCheckButton::IsDataChanged() {
   XFA_CHECKSTATE eCheckState = FWLState2XFAState();
-  return m_pNode->GetWidgetAcc()->GetCheckState() != eCheckState;
+  return m_pNode->GetCheckState() != eCheckState;
 }
 
 void CXFA_FFCheckButton::SetFWLCheckState(XFA_CHECKSTATE eCheckState) {
   if (eCheckState == XFA_CHECKSTATE_Neutral)
-    m_pNormalWidget->SetStates(FWL_STATE_CKB_Neutral);
+    GetNormalWidget()->SetStates(FWL_STATE_CKB_Neutral);
   else if (eCheckState == XFA_CHECKSTATE_On)
-    m_pNormalWidget->SetStates(FWL_STATE_CKB_Checked);
+    GetNormalWidget()->SetStates(FWL_STATE_CKB_Checked);
   else
-    m_pNormalWidget->RemoveStates(FWL_STATE_CKB_Checked);
+    GetNormalWidget()->RemoveStates(FWL_STATE_CKB_Checked);
 }
 
 bool CXFA_FFCheckButton::UpdateFWLData() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  XFA_CHECKSTATE eState = m_pNode->GetWidgetAcc()->GetCheckState();
+  XFA_CHECKSTATE eState = m_pNode->GetCheckState();
   SetFWLCheckState(eState);
-  m_pNormalWidget->Update();
+  GetNormalWidget()->Update();
   return true;
 }
 
@@ -307,29 +308,30 @@
     case CFWL_Event::Type::CheckStateChanged: {
       CXFA_EventParam eParam;
       eParam.m_eType = XFA_EVENT_Change;
-      eParam.m_wsNewText =
-          m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw);
+      eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
 
       CXFA_Node* exclNode = m_pNode->GetExclGroupIfExists();
       if (ProcessCommittedData()) {
-        eParam.m_pTarget = exclNode ? exclNode->GetWidgetAcc() : nullptr;
+        eParam.m_pTarget = exclNode;
         if (exclNode) {
-          m_pDocView->AddValidateWidget(exclNode->GetWidgetAcc());
-          m_pDocView->AddCalculateWidgetAcc(exclNode->GetWidgetAcc());
-          exclNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change,
+          m_pDocView->AddValidateNode(exclNode);
+          m_pDocView->AddCalculateNode(exclNode);
+          exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change,
                                  &eParam);
         }
-        eParam.m_pTarget = m_pNode->GetWidgetAcc();
-        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, &eParam);
+        eParam.m_pTarget = m_pNode.Get();
+        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change,
+                              &eParam);
       } else {
-        SetFWLCheckState(m_pNode->GetWidgetAcc()->GetCheckState());
+        SetFWLCheckState(m_pNode->GetCheckState());
       }
       if (exclNode) {
-        eParam.m_pTarget = exclNode->GetWidgetAcc();
-        exclNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Click, &eParam);
+        eParam.m_pTarget = exclNode;
+        exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click,
+                               &eParam);
       }
-      eParam.m_pTarget = m_pNode->GetWidgetAcc();
-      m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Click, &eParam);
+      eParam.m_pTarget = m_pNode.Get();
+      m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click, &eParam);
       break;
     }
     default:
diff --git a/xfa/fxfa/cxfa_ffcheckbutton.h b/xfa/fxfa/cxfa_ffcheckbutton.h
index f841d78..3e5a723 100644
--- a/xfa/fxfa/cxfa_ffcheckbutton.h
+++ b/xfa/fxfa/cxfa_ffcheckbutton.h
@@ -7,18 +7,22 @@
 #ifndef XFA_FXFA_CXFA_FFCHECKBUTTON_H_
 #define XFA_FXFA_CXFA_FFCHECKBUTTON_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_FFCheckButton : public CXFA_FFField {
+class CXFA_CheckButton;
+
+class CXFA_FFCheckButton final : public CXFA_FFField {
  public:
-  explicit CXFA_FFCheckButton(CXFA_Node* pNode);
+  CXFA_FFCheckButton(CXFA_Node* pNode, CXFA_CheckButton* button);
   ~CXFA_FFCheckButton() override;
 
   // CXFA_FFField
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
 
   bool LoadWidget() override;
   bool PerformLayout() override;
@@ -37,11 +41,12 @@
   bool CommitData() override;
   bool IsDataChanged() override;
   void CapLeftRightPlacement(const CXFA_Margin* captionMargin);
-  void AddUIMargin(XFA_AttributeEnum iCapPlacement);
+  void AddUIMargin(XFA_AttributeValue iCapPlacement);
   XFA_CHECKSTATE FWLState2XFAState();
 
-  IFWL_WidgetDelegate* m_pOldDelegate;
+  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
   CFX_RectF m_rtCheckBox;
+  UnownedPtr<CXFA_CheckButton> const button_;
 };
 
 #endif  // XFA_FXFA_CXFA_FFCHECKBUTTON_H_
diff --git a/xfa/fxfa/cxfa_ffcombobox.cpp b/xfa/fxfa/cxfa_ffcombobox.cpp
index 4c024fb..61fa659 100644
--- a/xfa/fxfa/cxfa_ffcombobox.cpp
+++ b/xfa/fxfa/cxfa_ffcombobox.cpp
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_combobox.h"
 #include "xfa/fwl/cfwl_eventselectchanged.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -22,61 +23,69 @@
   return static_cast<CFWL_ComboBox*>(widget);
 }
 
+const CFWL_ComboBox* ToComboBox(const CFWL_Widget* widget) {
+  return static_cast<const CFWL_ComboBox*>(widget);
+}
+
 }  // namespace
 
-CXFA_FFComboBox::CXFA_FFComboBox(CXFA_Node* pNode)
-    : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
+CXFA_FFComboBox::CXFA_FFComboBox(CXFA_Node* pNode) : CXFA_FFDropDown(pNode) {}
 
-CXFA_FFComboBox::~CXFA_FFComboBox() {}
+CXFA_FFComboBox::~CXFA_FFComboBox() = default;
 
-CFX_RectF CXFA_FFComboBox::GetBBox(uint32_t dwStatus, bool bDrawFocus) {
-  return bDrawFocus ? CFX_RectF() : CXFA_FFWidget::GetBBox(dwStatus);
+CXFA_FFComboBox* CXFA_FFComboBox::AsComboBox() {
+  return this;
+}
+
+CFX_RectF CXFA_FFComboBox::GetBBox(FocusOption focus) {
+  if (focus == kDrawFocus)
+    return CFX_RectF();
+  return CXFA_FFWidget::GetBBox(kDoNotDrawFocus);
 }
 
 bool CXFA_FFComboBox::PtInActiveRect(const CFX_PointF& point) {
-  auto* pComboBox = ToComboBox(m_pNormalWidget.get());
+  auto* pComboBox = ToComboBox(GetNormalWidget());
   return pComboBox && pComboBox->GetBBox().Contains(point);
 }
 
 bool CXFA_FFComboBox::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNew = pdfium::MakeUnique<CFWL_ComboBox>(GetFWLApp());
   CFWL_ComboBox* pComboBox = pNew.get();
-  m_pNormalWidget = std::move(pNew);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNew));
+  pComboBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  m_pNormalWidget->LockUpdate();
+  CFWL_NoteDriver* pNoteDriver = pComboBox->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pComboBox, pComboBox);
+  m_pOldDelegate = pComboBox->GetDelegate();
+  pComboBox->SetDelegate(this);
 
-  for (const auto& label : m_pNode->GetWidgetAcc()->GetChoiceListItems(false))
-    pComboBox->AddString(label.AsStringView());
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pComboBox);
+    for (const auto& label : m_pNode->GetChoiceListItems(false))
+      pComboBox->AddString(label);
 
-  std::vector<int32_t> iSelArray = m_pNode->GetWidgetAcc()->GetSelectedItems();
-  if (iSelArray.empty()) {
-    pComboBox->SetEditText(
-        m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw));
-  } else {
-    pComboBox->SetCurSel(iSelArray.front());
+    std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
+    if (iSelArray.empty())
+      pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
+    else
+      pComboBox->SetCurSel(iSelArray.front());
+
+    UpdateWidgetProperty();
   }
 
-  UpdateWidgetProperty();
-  m_pNormalWidget->UnlockUpdate();
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFComboBox::UpdateWidgetProperty() {
-  auto* pComboBox = ToComboBox(m_pNormalWidget.get());
+  auto* pComboBox = ToComboBox(GetNormalWidget());
   if (!pComboBox)
     return;
 
   uint32_t dwExtendedStyle = 0;
   uint32_t dwEditStyles = FWL_STYLEEXT_EDT_ReadOnly;
   dwExtendedStyle |= UpdateUIProperty();
-  if (m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry()) {
+  if (m_pNode->IsChoiceListAllowTextEntry()) {
     dwEditStyles &= ~FWL_STYLEEXT_EDT_ReadOnly;
     dwExtendedStyle |= FWL_STYLEEXT_CMB_DropDown;
   }
@@ -85,9 +94,9 @@
     dwExtendedStyle |= FWL_STYLEEXT_CMB_ReadOnly;
   }
   dwExtendedStyle |= GetAlignment();
-  m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
 
-  if (!m_pNode->GetWidgetAcc()->IsHorizontalScrollPolicyOff())
+  if (!m_pNode->IsHorizontalScrollPolicyOff())
     dwEditStyles |= FWL_STYLEEXT_EDT_AutoHScroll;
 
   pComboBox->EditModifyStylesEx(dwEditStyles, 0xFFFFFFFF);
@@ -102,44 +111,49 @@
 }
 
 bool CXFA_FFComboBox::OnKillFocus(CXFA_FFWidget* pNewWidget) {
+  ObservedPtr<CXFA_FFWidget> pWatched(this);
+  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
   if (!ProcessCommittedData())
     UpdateFWLData();
 
-  CXFA_FFField::OnKillFocus(pNewWidget);
-  return true;
+  return pWatched && pNewWatched &&
+         CXFA_FFField::OnKillFocus(pNewWatched.Get());
 }
 
 void CXFA_FFComboBox::OpenDropDownList() {
-  ToComboBox(m_pNormalWidget.get())->OpenDropDownList(true);
+  ToComboBox(GetNormalWidget())->OpenDropDownList(true);
 }
 
 bool CXFA_FFComboBox::CommitData() {
-  return m_pNode->GetWidgetAcc()->SetValue(XFA_VALUEPICTURE_Raw, m_wsNewValue);
+  return m_pNode->SetValue(XFA_VALUEPICTURE_Raw, m_wsNewValue);
 }
 
 bool CXFA_FFComboBox::IsDataChanged() {
-  auto* pFWLcombobox = ToComboBox(m_pNormalWidget.get());
-  WideString wsText = pFWLcombobox->GetEditText();
-  int32_t iCursel = pFWLcombobox->GetCurSel();
-  if (iCursel >= 0) {
-    WideString wsSel = pFWLcombobox->GetTextByIndex(iCursel);
-    if (wsSel == wsText)
-      wsText = m_pNode->GetWidgetAcc()
-                   ->GetChoiceListItem(iCursel, true)
-                   .value_or(L"");
-  }
-  if (m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw) == wsText)
+  WideString wsText = GetCurrentText();
+  if (m_pNode->GetValue(XFA_VALUEPICTURE_Raw) == wsText)
     return false;
 
-  m_wsNewValue = wsText;
+  m_wsNewValue = std::move(wsText);
   return true;
 }
 
 void CXFA_FFComboBox::FWLEventSelChange(CXFA_EventParam* pParam) {
   pParam->m_eType = XFA_EVENT_Change;
-  pParam->m_pTarget = m_pNode->GetWidgetAcc();
-  pParam->m_wsNewText = ToComboBox(m_pNormalWidget.get())->GetEditText();
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, pParam);
+  pParam->m_pTarget = m_pNode.Get();
+  pParam->m_wsPrevText = ToComboBox(GetNormalWidget())->GetEditText();
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, pParam);
+}
+
+WideString CXFA_FFComboBox::GetCurrentText() const {
+  auto* pFWLcombobox = ToComboBox(GetNormalWidget());
+  WideString wsText = pFWLcombobox->GetEditText();
+  int32_t iCursel = pFWLcombobox->GetCurSel();
+  if (iCursel >= 0) {
+    WideString wsSel = pFWLcombobox->GetTextByIndex(iCursel);
+    if (wsSel == wsText)
+      wsText = m_pNode->GetChoiceListItem(iCursel, true).value_or(L"");
+  }
+  return wsText;
 }
 
 uint32_t CXFA_FFComboBox::GetAlignment() {
@@ -149,18 +163,18 @@
 
   uint32_t dwExtendedStyle = 0;
   switch (para->GetHorizontalAlign()) {
-    case XFA_AttributeEnum::Center:
+    case XFA_AttributeValue::Center:
       dwExtendedStyle |=
           FWL_STYLEEXT_CMB_EditHCenter | FWL_STYLEEXT_CMB_ListItemCenterAlign;
       break;
-    case XFA_AttributeEnum::Justify:
+    case XFA_AttributeValue::Justify:
       dwExtendedStyle |= FWL_STYLEEXT_CMB_EditJustified;
       break;
-    case XFA_AttributeEnum::JustifyAll:
+    case XFA_AttributeValue::JustifyAll:
       break;
-    case XFA_AttributeEnum::Radix:
+    case XFA_AttributeValue::Radix:
       break;
-    case XFA_AttributeEnum::Right:
+    case XFA_AttributeValue::Right:
       break;
     default:
       dwExtendedStyle |=
@@ -169,10 +183,10 @@
   }
 
   switch (para->GetVerticalAlign()) {
-    case XFA_AttributeEnum::Middle:
+    case XFA_AttributeValue::Middle:
       dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVCenter;
       break;
-    case XFA_AttributeEnum::Bottom:
+    case XFA_AttributeValue::Bottom:
       dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVFar;
       break;
     default:
@@ -183,87 +197,88 @@
 }
 
 bool CXFA_FFComboBox::UpdateFWLData() {
-  auto* pComboBox = ToComboBox(m_pNormalWidget.get());
+  auto* pComboBox = ToComboBox(GetNormalWidget());
   if (!pComboBox)
     return false;
 
-  std::vector<int32_t> iSelArray = m_pNode->GetWidgetAcc()->GetSelectedItems();
+  std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
   if (!iSelArray.empty()) {
     pComboBox->SetCurSel(iSelArray.front());
   } else {
     pComboBox->SetCurSel(-1);
-    pComboBox->SetEditText(
-        m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw));
+    pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
   }
   pComboBox->Update();
   return true;
 }
 
 bool CXFA_FFComboBox::CanUndo() {
-  return m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry() &&
-         ToComboBox(m_pNormalWidget.get())->EditCanUndo();
+  return m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditCanUndo();
 }
 
 bool CXFA_FFComboBox::CanRedo() {
-  return m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry() &&
-         ToComboBox(m_pNormalWidget.get())->EditCanRedo();
+  return m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditCanRedo();
 }
 
 bool CXFA_FFComboBox::Undo() {
-  return m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry() &&
-         ToComboBox(m_pNormalWidget.get())->EditUndo();
+  return m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditUndo();
 }
 
 bool CXFA_FFComboBox::Redo() {
-  return m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry() &&
-         ToComboBox(m_pNormalWidget.get())->EditRedo();
+  return m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditRedo();
 }
 
 bool CXFA_FFComboBox::CanCopy() {
-  return ToComboBox(m_pNormalWidget.get())->EditCanCopy();
+  return ToComboBox(GetNormalWidget())->EditCanCopy();
 }
 
 bool CXFA_FFComboBox::CanCut() {
-  return m_pNode->IsOpenAccess() &&
-         m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry() &&
-         ToComboBox(m_pNormalWidget.get())->EditCanCut();
+  return m_pNode->IsOpenAccess() && m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditCanCut();
 }
 
 bool CXFA_FFComboBox::CanPaste() {
-  return m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry() &&
-         m_pNode->IsOpenAccess();
+  return m_pNode->IsChoiceListAllowTextEntry() && m_pNode->IsOpenAccess();
 }
 
 bool CXFA_FFComboBox::CanSelectAll() {
-  return ToComboBox(m_pNormalWidget.get())->EditCanSelectAll();
+  return ToComboBox(GetNormalWidget())->EditCanSelectAll();
 }
 
 Optional<WideString> CXFA_FFComboBox::Copy() {
-  return ToComboBox(m_pNormalWidget.get())->EditCopy();
+  return ToComboBox(GetNormalWidget())->EditCopy();
 }
 
 Optional<WideString> CXFA_FFComboBox::Cut() {
-  if (!m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry())
+  if (!m_pNode->IsChoiceListAllowTextEntry())
     return {};
 
-  return ToComboBox(m_pNormalWidget.get())->EditCut();
+  return ToComboBox(GetNormalWidget())->EditCut();
 }
 
 bool CXFA_FFComboBox::Paste(const WideString& wsPaste) {
-  return m_pNode->GetWidgetAcc()->IsChoiceListAllowTextEntry() &&
-         ToComboBox(m_pNormalWidget.get())->EditPaste(wsPaste);
+  return m_pNode->IsChoiceListAllowTextEntry() &&
+         ToComboBox(GetNormalWidget())->EditPaste(wsPaste);
 }
 
 void CXFA_FFComboBox::SelectAll() {
-  ToComboBox(m_pNormalWidget.get())->EditSelectAll();
+  ToComboBox(GetNormalWidget())->EditSelectAll();
 }
 
 void CXFA_FFComboBox::Delete() {
-  ToComboBox(m_pNormalWidget.get())->EditDelete();
+  ToComboBox(GetNormalWidget())->EditDelete();
 }
 
 void CXFA_FFComboBox::DeSelect() {
-  ToComboBox(m_pNormalWidget.get())->EditDeSelect();
+  ToComboBox(GetNormalWidget())->EditDeSelect();
+}
+
+WideString CXFA_FFComboBox::GetText() {
+  return GetCurrentText();
 }
 
 FormFieldType CXFA_FFComboBox::GetFormFieldType() {
@@ -271,56 +286,59 @@
 }
 
 void CXFA_FFComboBox::SetItemState(int32_t nIndex, bool bSelected) {
-  ToComboBox(m_pNormalWidget.get())->SetCurSel(bSelected ? nIndex : -1);
-  m_pNormalWidget->Update();
-  AddInvalidateRect();
+  ToComboBox(GetNormalWidget())->SetCurSel(bSelected ? nIndex : -1);
+  GetNormalWidget()->Update();
+  InvalidateRect();
 }
 
-void CXFA_FFComboBox::InsertItem(const WideStringView& wsLabel,
-                                 int32_t nIndex) {
-  ToComboBox(m_pNormalWidget.get())->AddString(wsLabel);
-  m_pNormalWidget->Update();
-  AddInvalidateRect();
+void CXFA_FFComboBox::InsertItem(const WideString& wsLabel, int32_t nIndex) {
+  ToComboBox(GetNormalWidget())->AddString(wsLabel);
+  GetNormalWidget()->Update();
+  InvalidateRect();
 }
 
 void CXFA_FFComboBox::DeleteItem(int32_t nIndex) {
   if (nIndex < 0)
-    ToComboBox(m_pNormalWidget.get())->RemoveAll();
+    ToComboBox(GetNormalWidget())->RemoveAll();
   else
-    ToComboBox(m_pNormalWidget.get())->RemoveAt(nIndex);
+    ToComboBox(GetNormalWidget())->RemoveAt(nIndex);
 
-  m_pNormalWidget->Update();
-  AddInvalidateRect();
+  GetNormalWidget()->Update();
+  InvalidateRect();
 }
 
 void CXFA_FFComboBox::OnTextChanged(CFWL_Widget* pWidget,
                                     const WideString& wsChanged) {
   CXFA_EventParam eParam;
-  eParam.m_wsPrevText = m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw);
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
   eParam.m_wsChange = wsChanged;
   FWLEventSelChange(&eParam);
 }
 
 void CXFA_FFComboBox::OnSelectChanged(CFWL_Widget* pWidget, bool bLButtonUp) {
+  ObservedPtr<CXFA_FFComboBox> watched(this);
   CXFA_EventParam eParam;
-  eParam.m_wsPrevText = m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw);
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
   FWLEventSelChange(&eParam);
-  if (m_pNode->GetWidgetAcc()->IsChoiceListCommitOnSelect() && bLButtonUp)
-    m_pDocView->SetFocusWidgetAcc(nullptr);
+  if (!watched)
+    return;
+
+  if (m_pNode->IsChoiceListCommitOnSelect() && bLButtonUp)
+    m_pDocView->SetFocusNode(nullptr);
 }
 
 void CXFA_FFComboBox::OnPreOpen(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_PreOpen;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::PreOpen, &eParam);
+  eParam.m_pTarget = m_pNode.Get();
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PreOpen, &eParam);
 }
 
 void CXFA_FFComboBox::OnPostOpen(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_PostOpen;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::PostOpen, &eParam);
+  eParam.m_pTarget = m_pNode.Get();
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PostOpen, &eParam);
 }
 
 void CXFA_FFComboBox::OnProcessMessage(CFWL_Message* pMessage) {
@@ -328,30 +346,32 @@
 }
 
 void CXFA_FFComboBox::OnProcessEvent(CFWL_Event* pEvent) {
+  ObservedPtr<CXFA_FFComboBox> watched(this);
   CXFA_FFField::OnProcessEvent(pEvent);
   switch (pEvent->GetType()) {
     case CFWL_Event::Type::SelectChanged: {
       auto* postEvent = static_cast<CFWL_EventSelectChanged*>(pEvent);
-      OnSelectChanged(m_pNormalWidget.get(), postEvent->bLButtonUp);
+      OnSelectChanged(GetNormalWidget(), postEvent->bLButtonUp);
       break;
     }
     case CFWL_Event::Type::EditChanged: {
       WideString wsChanged;
-      OnTextChanged(m_pNormalWidget.get(), wsChanged);
+      OnTextChanged(GetNormalWidget(), wsChanged);
       break;
     }
     case CFWL_Event::Type::PreDropDown: {
-      OnPreOpen(m_pNormalWidget.get());
+      OnPreOpen(GetNormalWidget());
       break;
     }
     case CFWL_Event::Type::PostDropDown: {
-      OnPostOpen(m_pNormalWidget.get());
+      OnPostOpen(GetNormalWidget());
       break;
     }
     default:
       break;
   }
-  m_pOldDelegate->OnProcessEvent(pEvent);
+  if (watched)
+    m_pOldDelegate->OnProcessEvent(pEvent);
 }
 
 void CXFA_FFComboBox::OnDrawWidget(CXFA_Graphics* pGraphics,
diff --git a/xfa/fxfa/cxfa_ffcombobox.h b/xfa/fxfa/cxfa_ffcombobox.h
index a20319e..3813b6e 100644
--- a/xfa/fxfa/cxfa_ffcombobox.h
+++ b/xfa/fxfa/cxfa_ffcombobox.h
@@ -7,19 +7,25 @@
 #ifndef XFA_FXFA_CXFA_FFCOMBOBOX_H_
 #define XFA_FXFA_CXFA_FFCOMBOBOX_H_
 
-#include "xfa/fxfa/cxfa_fffield.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/cxfa_ffdropdown.h"
 
-class CXFA_FFComboBox : public CXFA_FFField {
+class CXFA_EventParam;
+
+class CXFA_FFComboBox final : public CXFA_FFDropDown {
  public:
   explicit CXFA_FFComboBox(CXFA_Node* pNode);
   ~CXFA_FFComboBox() override;
 
+  // CXFA_FFDropDown:
+  CXFA_FFComboBox* AsComboBox() override;
+
   // CXFA_FFField
-  CFX_RectF GetBBox(uint32_t dwStatus, bool bDrawFocus = false) override;
+  CFX_RectF GetBBox(FocusOption focus) override;
   bool LoadWidget() override;
   void UpdateWidgetProperty() override;
   bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
+  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
   bool CanUndo() override;
   bool CanRedo() override;
   bool Undo() override;
@@ -35,6 +41,7 @@
   void SelectAll() override;
   void Delete() override;
   void DeSelect() override;
+  WideString GetText() override;
   FormFieldType GetFormFieldType() override;
 
   // IFWL_WidgetDelegate
@@ -43,18 +50,20 @@
   void OnDrawWidget(CXFA_Graphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
-  virtual void OpenDropDownList();
+  // CXFA_FFDropDown
+  void InsertItem(const WideString& wsLabel, int32_t nIndex) override;
+  void DeleteItem(int32_t nIndex) override;
+
+  void OpenDropDownList();
 
   void OnTextChanged(CFWL_Widget* pWidget, const WideString& wsChanged);
   void OnSelectChanged(CFWL_Widget* pWidget, bool bLButtonUp);
   void OnPreOpen(CFWL_Widget* pWidget);
   void OnPostOpen(CFWL_Widget* pWidget);
   void SetItemState(int32_t nIndex, bool bSelected);
-  void InsertItem(const WideStringView& wsLabel, int32_t nIndex);
-  void DeleteItem(int32_t nIndex);
 
  private:
-  // CXFA_FFField
+  // CXFA_FFField:
   bool PtInActiveRect(const CFX_PointF& point) override;
   bool CommitData() override;
   bool UpdateFWLData() override;
@@ -62,9 +71,10 @@
 
   uint32_t GetAlignment();
   void FWLEventSelChange(CXFA_EventParam* pParam);
+  WideString GetCurrentText() const;
 
   WideString m_wsNewValue;
-  IFWL_WidgetDelegate* m_pOldDelegate;
+  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
 };
 
 #endif  // XFA_FXFA_CXFA_FFCOMBOBOX_H_
diff --git a/xfa/fxfa/cxfa_ffdatetimeedit.cpp b/xfa/fxfa/cxfa_ffdatetimeedit.cpp
index d6bd096..d8d57f5 100644
--- a/xfa/fxfa/cxfa_ffdatetimeedit.cpp
+++ b/xfa/fxfa/cxfa_ffdatetimeedit.cpp
@@ -8,12 +8,14 @@
 
 #include <utility>
 
+#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_eventselectchanged.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_widget.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
@@ -22,80 +24,84 @@
 CXFA_FFDateTimeEdit::CXFA_FFDateTimeEdit(CXFA_Node* pNode)
     : CXFA_FFTextEdit(pNode) {}
 
-CXFA_FFDateTimeEdit::~CXFA_FFDateTimeEdit() {}
+CXFA_FFDateTimeEdit::~CXFA_FFDateTimeEdit() = default;
 
-CFX_RectF CXFA_FFDateTimeEdit::GetBBox(uint32_t dwStatus, bool bDrawFocus) {
-  if (bDrawFocus)
+CFWL_DateTimePicker* CXFA_FFDateTimeEdit::GetPickerWidget() {
+  return static_cast<CFWL_DateTimePicker*>(GetNormalWidget());
+}
+
+CFX_RectF CXFA_FFDateTimeEdit::GetBBox(FocusOption focus) {
+  if (focus == kDrawFocus)
     return CFX_RectF();
-  return CXFA_FFWidget::GetBBox(dwStatus);
+  return CXFA_FFWidget::GetBBox(kDoNotDrawFocus);
 }
 
 bool CXFA_FFDateTimeEdit::PtInActiveRect(const CFX_PointF& point) {
-  auto* pPicker = static_cast<CFWL_DateTimePicker*>(m_pNormalWidget.get());
+  CFWL_DateTimePicker* pPicker = GetPickerWidget();
   return pPicker && pPicker->GetBBox().Contains(point);
 }
 
 bool CXFA_FFDateTimeEdit::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNewPicker = pdfium::MakeUnique<CFWL_DateTimePicker>(GetFWLApp());
   CFWL_DateTimePicker* pWidget = pNewPicker.get();
-  m_pNormalWidget = std::move(pNewPicker);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNewPicker));
+  pWidget->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  m_pNormalWidget->LockUpdate();
+  CFWL_NoteDriver* pNoteDriver = pWidget->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pWidget, pWidget);
+  m_pOldDelegate = pWidget->GetDelegate();
+  pWidget->SetDelegate(this);
 
-  WideString wsText =
-      m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Display);
-  pWidget->SetEditText(wsText);
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pWidget);
+    WideString wsText = m_pNode->GetValue(XFA_VALUEPICTURE_Display);
+    pWidget->SetEditText(wsText);
 
-  CXFA_Value* value = m_pNode->GetFormValueIfExists();
-  if (value) {
-    switch (value->GetChildValueClassID()) {
-      case XFA_Element::Date: {
-        if (!wsText.IsEmpty()) {
-          CXFA_LocaleValue lcValue = XFA_GetLocaleValue(m_pNode.Get());
-          CFX_DateTime date = lcValue.GetDate();
-          if (date.IsSet())
-            pWidget->SetCurSel(date.GetYear(), date.GetMonth(), date.GetDay());
-        }
-      } break;
-      default:
-        break;
+    CXFA_Value* value = m_pNode->GetFormValueIfExists();
+    if (value) {
+      switch (value->GetChildValueClassID()) {
+        case XFA_Element::Date: {
+          if (!wsText.IsEmpty()) {
+            CXFA_LocaleValue lcValue = XFA_GetLocaleValue(m_pNode.Get());
+            CFX_DateTime date = lcValue.GetDate();
+            if (date.IsSet())
+              pWidget->SetCurSel(date.GetYear(), date.GetMonth(),
+                                 date.GetDay());
+          }
+        } break;
+        default:
+          break;
+      }
     }
+    UpdateWidgetProperty();
   }
-  UpdateWidgetProperty();
-  m_pNormalWidget->UnlockUpdate();
+
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFDateTimeEdit::UpdateWidgetProperty() {
-  CFWL_DateTimePicker* pWidget =
-      static_cast<CFWL_DateTimePicker*>(m_pNormalWidget.get());
-  if (!pWidget)
+  CFWL_DateTimePicker* pPicker = GetPickerWidget();
+  if (!pPicker)
     return;
 
   uint32_t dwExtendedStyle = FWL_STYLEEXT_DTP_ShortDateFormat;
   dwExtendedStyle |= UpdateUIProperty();
   dwExtendedStyle |= GetAlignment();
-  m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
 
   uint32_t dwEditStyles = 0;
-  Optional<int32_t> numCells = m_pNode->GetWidgetAcc()->GetNumberOfCells();
+  Optional<int32_t> numCells = m_pNode->GetNumberOfCells();
   if (numCells && *numCells > 0) {
     dwEditStyles |= FWL_STYLEEXT_EDT_CombText;
-    pWidget->SetEditLimit(*numCells);
+    pPicker->SetEditLimit(*numCells);
   }
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
     dwEditStyles |= FWL_STYLEEXT_EDT_ReadOnly;
-  if (!m_pNode->GetWidgetAcc()->IsHorizontalScrollPolicyOff())
+  if (!m_pNode->IsHorizontalScrollPolicyOff())
     dwEditStyles |= FWL_STYLEEXT_EDT_AutoHScroll;
 
-  pWidget->ModifyEditStylesEx(dwEditStyles, 0xFFFFFFFF);
+  pPicker->ModifyEditStylesEx(dwEditStyles, 0xFFFFFFFF);
 }
 
 uint32_t CXFA_FFDateTimeEdit::GetAlignment() {
@@ -105,16 +111,16 @@
 
   uint32_t dwExtendedStyle = 0;
   switch (para->GetHorizontalAlign()) {
-    case XFA_AttributeEnum::Center:
+    case XFA_AttributeValue::Center:
       dwExtendedStyle |= FWL_STYLEEXT_DTP_EditHCenter;
       break;
-    case XFA_AttributeEnum::Justify:
+    case XFA_AttributeValue::Justify:
       dwExtendedStyle |= FWL_STYLEEXT_DTP_EditJustified;
       break;
-    case XFA_AttributeEnum::JustifyAll:
-    case XFA_AttributeEnum::Radix:
+    case XFA_AttributeValue::JustifyAll:
+    case XFA_AttributeValue::Radix:
       break;
-    case XFA_AttributeEnum::Right:
+    case XFA_AttributeValue::Right:
       dwExtendedStyle |= FWL_STYLEEXT_DTP_EditHFar;
       break;
     default:
@@ -123,10 +129,10 @@
   }
 
   switch (para->GetVerticalAlign()) {
-    case XFA_AttributeEnum::Middle:
+    case XFA_AttributeValue::Middle:
       dwExtendedStyle |= FWL_STYLEEXT_DTP_EditVCenter;
       break;
-    case XFA_AttributeEnum::Bottom:
+    case XFA_AttributeValue::Bottom:
       dwExtendedStyle |= FWL_STYLEEXT_DTP_EditVFar;
       break;
     default:
@@ -137,80 +143,138 @@
 }
 
 bool CXFA_FFDateTimeEdit::CommitData() {
-  auto* pPicker = static_cast<CFWL_DateTimePicker*>(m_pNormalWidget.get());
-  if (!m_pNode->GetWidgetAcc()->SetValue(XFA_VALUEPICTURE_Edit,
-                                         pPicker->GetEditText())) {
+  CFWL_DateTimePicker* pPicker = GetPickerWidget();
+  if (!m_pNode->SetValue(XFA_VALUEPICTURE_Edit, pPicker->GetEditText()))
     return false;
-  }
 
-  m_pNode->GetWidgetAcc()->UpdateUIDisplay(GetDoc()->GetDocView(), this);
+  GetDoc()->GetDocView()->UpdateUIDisplay(m_pNode.Get(), this);
   return true;
 }
 
 bool CXFA_FFDateTimeEdit::UpdateFWLData() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
   XFA_VALUEPICTURE eType = XFA_VALUEPICTURE_Display;
   if (IsFocused())
     eType = XFA_VALUEPICTURE_Edit;
 
-  WideString wsText = m_pNode->GetWidgetAcc()->GetValue(eType);
-  auto* normalWidget = static_cast<CFWL_DateTimePicker*>(m_pNormalWidget.get());
-  normalWidget->SetEditText(wsText);
+  WideString wsText = m_pNode->GetValue(eType);
+  CFWL_DateTimePicker* pPicker = GetPickerWidget();
+  pPicker->SetEditText(wsText);
   if (IsFocused() && !wsText.IsEmpty()) {
     CXFA_LocaleValue lcValue = XFA_GetLocaleValue(m_pNode.Get());
     CFX_DateTime date = lcValue.GetDate();
     if (lcValue.IsValid()) {
       if (date.IsSet())
-        normalWidget->SetCurSel(date.GetYear(), date.GetMonth(), date.GetDay());
+        pPicker->SetCurSel(date.GetYear(), date.GetMonth(), date.GetDay());
     }
   }
-  m_pNormalWidget->Update();
+  GetNormalWidget()->Update();
   return true;
 }
 
 bool CXFA_FFDateTimeEdit::IsDataChanged() {
-  if (m_dwStatus & XFA_WidgetStatus_TextEditValueChanged)
+  if (GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_TextEditValueChanged))
     return true;
 
-  WideString wsText =
-      static_cast<CFWL_DateTimePicker*>(m_pNormalWidget.get())->GetEditText();
-  return m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Edit) != wsText;
+  WideString wsText = GetPickerWidget()->GetEditText();
+  return m_pNode->GetValue(XFA_VALUEPICTURE_Edit) != wsText;
 }
 
 void CXFA_FFDateTimeEdit::OnSelectChanged(CFWL_Widget* pWidget,
                                           int32_t iYear,
                                           int32_t iMonth,
                                           int32_t iDay) {
-  WideString wsPicture =
-      m_pNode->GetWidgetAcc()->GetPictureContent(XFA_VALUEPICTURE_Edit);
+  WideString wsPicture = m_pNode->GetPictureContent(XFA_VALUEPICTURE_Edit);
 
-  CXFA_LocaleValue date(XFA_VT_DATE, GetDoc()->GetXFADoc()->GetLocalMgr());
+  CXFA_LocaleValue date(XFA_VT_DATE, GetDoc()->GetXFADoc()->GetLocaleMgr());
   date.SetDate(CFX_DateTime(iYear, iMonth, iDay, 0, 0, 0, 0));
 
   WideString wsDate;
   date.FormatPatterns(wsDate, wsPicture, m_pNode->GetLocale(),
                       XFA_VALUEPICTURE_Edit);
 
-  auto* pDateTime = static_cast<CFWL_DateTimePicker*>(m_pNormalWidget.get());
-  pDateTime->SetEditText(wsDate);
-  pDateTime->Update();
+  CFWL_DateTimePicker* pPicker = GetPickerWidget();
+  pPicker->SetEditText(wsDate);
+  pPicker->Update();
   GetDoc()->GetDocEnvironment()->SetFocusWidget(GetDoc(), nullptr);
 
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Change;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  eParam.m_wsNewText = m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw);
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, &eParam);
+  eParam.m_pTarget = m_pNode.Get();
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
 }
 
 void CXFA_FFDateTimeEdit::OnProcessEvent(CFWL_Event* pEvent) {
   if (pEvent->GetType() == CFWL_Event::Type::SelectChanged) {
     auto* event = static_cast<CFWL_EventSelectChanged*>(pEvent);
-    OnSelectChanged(m_pNormalWidget.get(), event->iYear, event->iMonth,
+    OnSelectChanged(GetNormalWidget(), event->iYear, event->iMonth,
                     event->iDay);
     return;
   }
   CXFA_FFTextEdit::OnProcessEvent(pEvent);
 }
+
+bool CXFA_FFDateTimeEdit::CanUndo() {
+  return GetPickerWidget()->CanUndo();
+}
+
+bool CXFA_FFDateTimeEdit::CanRedo() {
+  return GetPickerWidget()->CanRedo();
+}
+
+bool CXFA_FFDateTimeEdit::Undo() {
+  return GetPickerWidget()->Undo();
+}
+
+bool CXFA_FFDateTimeEdit::Redo() {
+  return GetPickerWidget()->Redo();
+}
+
+bool CXFA_FFDateTimeEdit::CanCopy() {
+  return GetPickerWidget()->HasSelection();
+}
+
+bool CXFA_FFDateTimeEdit::CanCut() {
+  if (GetPickerWidget()->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)
+    return false;
+  return GetPickerWidget()->HasSelection();
+}
+
+bool CXFA_FFDateTimeEdit::CanPaste() {
+  return !(GetPickerWidget()->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly);
+}
+
+bool CXFA_FFDateTimeEdit::CanSelectAll() {
+  return GetPickerWidget()->GetEditTextLength() > 0;
+}
+
+Optional<WideString> CXFA_FFDateTimeEdit::Copy() {
+  return GetPickerWidget()->Copy();
+}
+
+Optional<WideString> CXFA_FFDateTimeEdit::Cut() {
+  return GetPickerWidget()->Cut();
+}
+
+bool CXFA_FFDateTimeEdit::Paste(const WideString& wsPaste) {
+  return GetPickerWidget()->Paste(wsPaste);
+}
+
+void CXFA_FFDateTimeEdit::SelectAll() {
+  GetPickerWidget()->SelectAll();
+}
+
+void CXFA_FFDateTimeEdit::Delete() {
+  GetPickerWidget()->ClearText();
+}
+
+void CXFA_FFDateTimeEdit::DeSelect() {
+  GetPickerWidget()->ClearSelection();
+}
+
+WideString CXFA_FFDateTimeEdit::GetText() {
+  return GetPickerWidget()->GetEditText();
+}
diff --git a/xfa/fxfa/cxfa_ffdatetimeedit.h b/xfa/fxfa/cxfa_ffdatetimeedit.h
index a549cbb..5ba6867 100644
--- a/xfa/fxfa/cxfa_ffdatetimeedit.h
+++ b/xfa/fxfa/cxfa_ffdatetimeedit.h
@@ -16,16 +16,17 @@
   XFA_DATETIMETYPE_DateAndTime
 };
 
+class CFWL_DateTimePicker;
 class CFWL_Event;
 class CFWL_Widget;
 
-class CXFA_FFDateTimeEdit : public CXFA_FFTextEdit {
+class CXFA_FFDateTimeEdit final : public CXFA_FFTextEdit {
  public:
   explicit CXFA_FFDateTimeEdit(CXFA_Node* pNode);
   ~CXFA_FFDateTimeEdit() override;
 
   // CXFA_FFTextEdit
-  CFX_RectF GetBBox(uint32_t dwStatus, bool bDrawFocus = false) override;
+  CFX_RectF GetBBox(FocusOption focus) override;
   bool LoadWidget() override;
   void UpdateWidgetProperty() override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
@@ -35,12 +36,31 @@
                        int32_t iMonth,
                        int32_t iDay);
 
+  // CXFA_FFWidget
+  bool CanUndo() override;
+  bool CanRedo() override;
+  bool Undo() override;
+  bool Redo() override;
+  bool CanCopy() override;
+  bool CanCut() override;
+  bool CanPaste() override;
+  bool CanSelectAll() override;
+  Optional<WideString> Copy() override;
+  Optional<WideString> Cut() override;
+  bool Paste(const WideString& wsPaste) override;
+  void SelectAll() override;
+  void Delete() override;
+  void DeSelect() override;
+  WideString GetText() override;
+
  private:
   bool PtInActiveRect(const CFX_PointF& point) override;
   bool CommitData() override;
   bool UpdateFWLData() override;
   bool IsDataChanged() override;
 
+  CFWL_DateTimePicker* GetPickerWidget();
+
   uint32_t GetAlignment();
 };
 
diff --git a/xfa/fxfa/cxfa_ffdoc.cpp b/xfa/fxfa/cxfa_ffdoc.cpp
index 4dfa78e..ac4b35f 100644
--- a/xfa/fxfa/cxfa_ffdoc.cpp
+++ b/xfa/fxfa/cxfa_ffdoc.cpp
@@ -10,264 +10,107 @@
 #include <memory>
 #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/fpdf_parser_decode.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
-#include "core/fxcrt/cfx_checksumcontext.h"
-#include "core/fxcrt/cfx_memorystream.h"
-#include "core/fxcrt/cfx_seekablemultistream.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
 #include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
+#include "xfa/fgas/font/cfgas_pdffontmgr.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_fontmgr.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_acrobat.h"
 #include "xfa/fxfa/parser/cxfa_acrobat7.h"
 #include "xfa/fxfa/parser/cxfa_dataexporter.h"
-#include "xfa/fxfa/parser/cxfa_dataimporter.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_document_parser.h"
 #include "xfa/fxfa/parser/cxfa_dynamicrender.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-namespace {
+FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI() = default;
+FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI(const FX_IMAGEDIB_AND_DPI& that) =
+    default;
 
-struct FX_BASE64DATA {
-  uint32_t data1 : 2;
-  uint32_t data2 : 6;
-  uint32_t data3 : 4;
-  uint32_t data4 : 4;
-  uint32_t data5 : 6;
-  uint32_t data6 : 2;
-  uint32_t data7 : 8;
-};
+FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI(const RetainPtr<CFX_DIBBase>& pDib,
+                                         int32_t xDpi,
+                                         int32_t yDpi)
+    : pDibSource(pDib), iImageXDpi(xDpi), iImageYDpi(yDpi) {}
 
-const uint8_t kStartValuesRemoved = 43;
-const uint8_t kDecoderMapSize = 80;
-const uint8_t g_FXBase64DecoderMap[kDecoderMapSize] = {
-    0x3E, 0xFF, 0xFF, 0xFF, 0x3F, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
-    0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,
-    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
-    0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
-    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
-    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
-    0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33,
-};
+FX_IMAGEDIB_AND_DPI::~FX_IMAGEDIB_AND_DPI() = default;
 
-uint8_t base64DecoderValue(uint8_t val) {
-  if (val < kStartValuesRemoved || val >= kStartValuesRemoved + kDecoderMapSize)
-    return 0xFF;
-  return g_FXBase64DecoderMap[val - kStartValuesRemoved];
+// static
+std::unique_ptr<CXFA_FFDoc> CXFA_FFDoc::CreateAndOpen(
+    CXFA_FFApp* pApp,
+    IXFA_DocEnvironment* pDocEnvironment,
+    CPDF_Document* pPDFDoc,
+    const RetainPtr<IFX_SeekableStream>& stream) {
+  ASSERT(pApp);
+  ASSERT(pDocEnvironment);
+  ASSERT(pPDFDoc);
+
+  // Use WrapUnique() to keep constructor private.
+  auto result =
+      pdfium::WrapUnique(new CXFA_FFDoc(pApp, pDocEnvironment, pPDFDoc));
+  if (!result->OpenDoc(stream))
+    return nullptr;
+
+  return result;
 }
 
-void Base64DecodePiece(const char src[4],
-                       int32_t iChars,
-                       FX_BASE64DATA& dst,
-                       int32_t& iBytes) {
-  ASSERT(iChars > 0 && iChars < 5);
-  iBytes = 1;
-  dst.data2 = base64DecoderValue(static_cast<uint8_t>(src[0]));
-  if (iChars > 1) {
-    uint8_t b = base64DecoderValue(static_cast<uint8_t>(src[1]));
-    dst.data1 = b >> 4;
-    dst.data4 = b;
-    if (iChars > 2) {
-      iBytes = 2;
-      b = base64DecoderValue(static_cast<uint8_t>(src[2]));
-      dst.data3 = b >> 2;
-      dst.data6 = b;
-      if (iChars > 3) {
-        iBytes = 3;
-        dst.data5 = base64DecoderValue(static_cast<uint8_t>(src[3]));
-      } else {
-        dst.data5 = 0;
-      }
-    } else {
-      dst.data3 = 0;
-    }
-  } else {
-    dst.data1 = 0;
-  }
-}
-
-int32_t Base64DecodeW(const wchar_t* pSrc, int32_t iSrcLen, uint8_t* pDst) {
-  ASSERT(pSrc);
-  if (iSrcLen < 1) {
-    return 0;
-  }
-  while (iSrcLen > 0 && pSrc[iSrcLen - 1] == '=') {
-    iSrcLen--;
-  }
-  if (iSrcLen < 1) {
-    return 0;
-  }
-  if (!pDst) {
-    int32_t iDstLen = iSrcLen / 4 * 3;
-    iSrcLen %= 4;
-    if (iSrcLen == 1) {
-      iDstLen += 1;
-    } else if (iSrcLen == 2) {
-      iDstLen += 1;
-    } else if (iSrcLen == 3) {
-      iDstLen += 2;
-    }
-    return iDstLen;
-  }
-  char srcData[4];
-  FX_BASE64DATA dstData;
-  int32_t iChars = 4, iBytes;
-  uint8_t* pDstEnd = pDst;
-  while (iSrcLen > 0) {
-    if (iSrcLen > 3) {
-      srcData[0] = (char)*pSrc++;
-      srcData[1] = (char)*pSrc++;
-      srcData[2] = (char)*pSrc++;
-      srcData[3] = (char)*pSrc++;
-      iSrcLen -= 4;
-    } else {
-      *((uint32_t*)&dstData) = 0;
-      *((uint32_t*)srcData) = 0;
-      srcData[0] = (char)*pSrc++;
-      if (iSrcLen > 1) {
-        srcData[1] = (char)*pSrc++;
-      }
-      if (iSrcLen > 2) {
-        srcData[2] = (char)*pSrc++;
-      }
-      iChars = iSrcLen;
-      iSrcLen = 0;
-    }
-    Base64DecodePiece(srcData, iChars, dstData, iBytes);
-    *pDstEnd++ = ((uint8_t*)&dstData)[0];
-    if (iBytes > 1) {
-      *pDstEnd++ = ((uint8_t*)&dstData)[1];
-    }
-    if (iBytes > 2) {
-      *pDstEnd++ = ((uint8_t*)&dstData)[2];
-    }
-  }
-  return pDstEnd - pDst;
-}
-
-}  // namespace
-
-CXFA_FFDoc::CXFA_FFDoc(CXFA_FFApp* pApp, IXFA_DocEnvironment* pDocEnvironment)
-    : m_pDocEnvironment(pDocEnvironment), m_pApp(pApp) {}
+CXFA_FFDoc::CXFA_FFDoc(CXFA_FFApp* pApp,
+                       IXFA_DocEnvironment* pDocEnvironment,
+                       CPDF_Document* pPDFDoc)
+    : m_pDocEnvironment(pDocEnvironment),
+      m_pApp(pApp),
+      m_pPDFDoc(pPDFDoc),
+      m_pNotify(pdfium::MakeUnique<CXFA_FFNotify>(this)),
+      m_pDocument(pdfium::MakeUnique<CXFA_Document>(
+          m_pNotify.get(),
+          pdfium::MakeUnique<CXFA_LayoutProcessor>())) {}
 
 CXFA_FFDoc::~CXFA_FFDoc() {
-  CloseDoc();
+  if (m_DocView) {
+    m_DocView->RunDocClose();
+    m_DocView.reset();
+  }
+  if (m_pDocument)
+    m_pDocument->ClearLayoutData();
+
+  m_pDocument.reset();
+  m_pXMLDoc.reset();
+  m_pNotify.reset();
+  m_pPDFFontMgr.reset();
+  m_HashToDibDpiMap.clear();
+  m_pApp->ClearEventTargets();
 }
 
-int32_t CXFA_FFDoc::StartLoad() {
-  m_pNotify = pdfium::MakeUnique<CXFA_FFNotify>(this);
-  m_pDocumentParser = pdfium::MakeUnique<CXFA_DocumentParser>(m_pNotify.get());
-  return m_pDocumentParser->StartParse(m_pStream, XFA_PacketType::Xdp);
-}
+bool CXFA_FFDoc::ParseDoc(const RetainPtr<IFX_SeekableStream>& stream) {
+  CXFA_DocumentParser parser(m_pDocument.get());
+  bool parsed = parser.Parse(stream, XFA_PacketType::Xdp);
 
-bool XFA_GetPDFContentsFromPDFXML(CFX_XMLNode* pPDFElement,
-                                  uint8_t*& pByteBuffer,
-                                  int32_t& iBufferSize) {
-  CFX_XMLElement* pDocumentElement = nullptr;
-  for (CFX_XMLNode* pXMLNode =
-           pPDFElement->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLNode; pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    if (pXMLNode->GetType() == FX_XMLNODE_Element) {
-      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-      WideString wsTagName = pXMLElement->GetName();
-      if (wsTagName == L"document") {
-        pDocumentElement = pXMLElement;
-        break;
-      }
-    }
-  }
-  if (!pDocumentElement) {
+  // We have to set the XML document before we return so that we can clean
+  // up in the OpenDoc method. If we don't, the XMLDocument will get free'd
+  // when this method returns and UnownedPtrs get unhappy.
+  m_pXMLDoc = parser.GetXMLDoc();
+
+  if (!parsed)
     return false;
-  }
-  CFX_XMLElement* pChunkElement = nullptr;
-  for (CFX_XMLNode* pXMLNode =
-           pDocumentElement->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLNode; pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    if (pXMLNode->GetType() == FX_XMLNODE_Element) {
-      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-      WideString wsTagName = pXMLElement->GetName();
-      if (wsTagName == L"chunk") {
-        pChunkElement = pXMLElement;
-        break;
-      }
-    }
-  }
-  if (!pChunkElement) {
-    return false;
-  }
-  WideString wsPDFContent = pChunkElement->GetTextData();
-  iBufferSize =
-      Base64DecodeW(wsPDFContent.c_str(), wsPDFContent.GetLength(), nullptr);
-  pByteBuffer = FX_Alloc(uint8_t, iBufferSize + 1);
-  pByteBuffer[iBufferSize] = '0';  // FIXME: I bet this is wrong.
-  Base64DecodeW(wsPDFContent.c_str(), wsPDFContent.GetLength(), pByteBuffer);
+
+  m_pDocument->SetRoot(parser.GetRootNode());
   return true;
 }
-void XFA_XPDPacket_MergeRootNode(CXFA_Node* pOriginRoot, CXFA_Node* pNewRoot) {
-  CXFA_Node* pChildNode = pNewRoot->GetFirstChild();
-  while (pChildNode) {
-    CXFA_Node* pOriginChild =
-        pOriginRoot->GetFirstChildByName(pChildNode->GetNameHash());
-    if (pOriginChild) {
-      pChildNode = pChildNode->GetNextSibling();
-    } else {
-      CXFA_Node* pNextSibling = pChildNode->GetNextSibling();
-      pNewRoot->RemoveChild(pChildNode, true);
-      pOriginRoot->InsertChild(pChildNode, nullptr);
-      pChildNode = pNextSibling;
-      pNextSibling = nullptr;
-    }
-  }
-}
-
-int32_t CXFA_FFDoc::DoLoad() {
-  int32_t iStatus = m_pDocumentParser->DoParse();
-  if (iStatus == XFA_PARSESTATUS_Done && !m_pPDFDoc)
-    return XFA_PARSESTATUS_SyntaxErr;
-  return iStatus;
-}
-
-void CXFA_FFDoc::StopLoad() {
-  m_pPDFFontMgr = pdfium::MakeUnique<CFGAS_PDFFontMgr>(
-      GetPDFDoc(), GetApp()->GetFDEFontMgr());
-
-  m_FormType = FormType::kXFAForeground;
-  CXFA_Node* pConfig = ToNode(
-      m_pDocumentParser->GetDocument()->GetXFAObject(XFA_HASHCODE_Config));
-  if (!pConfig)
-    return;
-
-  CXFA_Acrobat* pAcrobat =
-      pConfig->GetFirstChildByClass<CXFA_Acrobat>(XFA_Element::Acrobat);
-  if (!pAcrobat)
-    return;
-
-  CXFA_Acrobat7* pAcrobat7 =
-      pAcrobat->GetFirstChildByClass<CXFA_Acrobat7>(XFA_Element::Acrobat7);
-  if (!pAcrobat7)
-    return;
-
-  CXFA_DynamicRender* pDynamicRender =
-      pAcrobat7->GetFirstChildByClass<CXFA_DynamicRender>(
-          XFA_Element::DynamicRender);
-  if (!pDynamicRender)
-    return;
-
-  WideString wsType = pDynamicRender->JSObject()->GetContent(false);
-  if (wsType == L"required")
-    m_FormType = FormType::kXFAFull;
-}
 
 CXFA_FFDocView* CXFA_FFDoc::CreateDocView() {
   if (!m_DocView)
@@ -285,64 +128,49 @@
   return m_DocView.get();
 }
 
-bool CXFA_FFDoc::OpenDoc(CPDF_Document* pPDFDoc) {
-  if (!pPDFDoc)
+bool CXFA_FFDoc::OpenDoc(const RetainPtr<IFX_SeekableStream>& stream) {
+  if (!ParseDoc(stream))
     return false;
 
-  const CPDF_Dictionary* pRoot = pPDFDoc->GetRoot();
-  if (!pRoot)
+  CFGAS_FontMgr* mgr = GetApp()->GetFDEFontMgr();
+  if (!mgr)
     return false;
 
-  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
-  if (!pAcroForm)
-    return false;
+  // At this point we've got an XFA document and we want to always return
+  // true to signify the load succeeded.
+  m_pPDFFontMgr = pdfium::MakeUnique<CFGAS_PDFFontMgr>(GetPDFDoc(), mgr);
 
-  CPDF_Object* pElementXFA = pAcroForm->GetDirectObjectFor("XFA");
-  if (!pElementXFA)
-    return false;
+  m_FormType = FormType::kXFAForeground;
+  CXFA_Node* pConfig = ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Config));
+  if (!pConfig)
+    return true;
 
-  std::vector<CPDF_Stream*> xfaStreams;
-  if (pElementXFA->IsArray()) {
-    CPDF_Array* pXFAArray = (CPDF_Array*)pElementXFA;
-    for (size_t i = 0; i < pXFAArray->GetCount() / 2; i++) {
-      if (CPDF_Stream* pStream = pXFAArray->GetStreamAt(i * 2 + 1))
-        xfaStreams.push_back(pStream);
-    }
-  } else if (pElementXFA->IsStream()) {
-    xfaStreams.push_back((CPDF_Stream*)pElementXFA);
-  }
-  if (xfaStreams.empty())
-    return false;
+  CXFA_Acrobat* pAcrobat =
+      pConfig->GetFirstChildByClass<CXFA_Acrobat>(XFA_Element::Acrobat);
+  if (!pAcrobat)
+    return true;
 
-  m_pPDFDoc = pPDFDoc;
-  m_pStream = pdfium::MakeRetain<CFX_SeekableMultiStream>(xfaStreams);
+  CXFA_Acrobat7* pAcrobat7 =
+      pAcrobat->GetFirstChildByClass<CXFA_Acrobat7>(XFA_Element::Acrobat7);
+  if (!pAcrobat7)
+    return true;
+
+  CXFA_DynamicRender* pDynamicRender =
+      pAcrobat7->GetFirstChildByClass<CXFA_DynamicRender>(
+          XFA_Element::DynamicRender);
+  if (!pDynamicRender)
+    return true;
+
+  WideString wsType = pDynamicRender->JSObject()->GetContent(false);
+  if (wsType.EqualsASCII("required"))
+    m_FormType = FormType::kXFAFull;
+
   return true;
 }
 
-void CXFA_FFDoc::CloseDoc() {
-  if (m_DocView) {
-    m_DocView->RunDocClose();
-    m_DocView.reset();
-  }
-  CXFA_Document* doc =
-      m_pDocumentParser ? m_pDocumentParser->GetDocument() : nullptr;
-  if (doc)
-    doc->ClearLayoutData();
-
-  m_pDocumentParser.reset();
-  m_pNotify.reset();
-  m_pPDFFontMgr.reset();
-  m_HashToDibDpiMap.clear();
-  m_pApp->ClearEventTargets();
-}
-
-RetainPtr<CFX_DIBitmap> CXFA_FFDoc::GetPDFNamedImage(
-    const WideStringView& wsName,
-    int32_t& iImageXDpi,
-    int32_t& iImageYDpi) {
-  if (!m_pPDFDoc)
-    return nullptr;
-
+RetainPtr<CFX_DIBitmap> CXFA_FFDoc::GetPDFNamedImage(WideStringView wsName,
+                                                     int32_t& iImageXDpi,
+                                                     int32_t& iImageYDpi) {
   uint32_t dwHash = FX_HashCode_GetW(wsName, false);
   auto it = m_HashToDibDpiMap.find(dwHash);
   if (it != m_HashToDibDpiMap.end()) {
@@ -351,7 +179,7 @@
     return it->second.pDibSource.As<CFX_DIBitmap>();
   }
 
-  const CPDF_Dictionary* pRoot = m_pPDFDoc->GetRoot();
+  CPDF_Dictionary* pRoot = m_pPDFDoc->GetRoot();
   if (!pRoot)
     return nullptr;
 
@@ -383,9 +211,8 @@
   auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
   pAcc->LoadAllDataFiltered();
 
-  RetainPtr<IFX_SeekableStream> pImageFileRead =
-      pdfium::MakeRetain<CFX_MemoryStream>(
-          const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize(), false);
+  auto pImageFileRead =
+      pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(pAcc->GetSpan());
 
   RetainPtr<CFX_DIBitmap> pDibSource = XFA_LoadImageFromBuffer(
       pImageFileRead, FXCODEC_IMAGE_UNKNOWN, iImageXDpi, iImageYDpi);
@@ -394,23 +221,9 @@
 }
 
 bool CXFA_FFDoc::SavePackage(CXFA_Node* pNode,
-                             const RetainPtr<IFX_SeekableStream>& pFile,
-                             CFX_ChecksumContext* pCSContext) {
-  auto pExport = pdfium::MakeUnique<CXFA_DataExporter>(GetXFADoc());
-  if (!pNode)
-    return !!pExport->Export(pFile);
+                             const RetainPtr<IFX_SeekableStream>& pFile) {
+  ASSERT(pNode || GetXFADoc()->GetRoot());
 
-  ByteString bsChecksum;
-  if (pCSContext)
-    bsChecksum = pCSContext->GetChecksum();
-
-  return !!pExport->Export(
-      pFile, pNode, 0, bsChecksum.GetLength() ? bsChecksum.c_str() : nullptr);
-}
-
-bool CXFA_FFDoc::ImportData(const RetainPtr<IFX_SeekableStream>& pStream,
-                            bool bXDP) {
-  auto importer =
-      pdfium::MakeUnique<CXFA_DataImporter>(m_pDocumentParser->GetDocument());
-  return importer->ImportData(pStream);
+  CXFA_DataExporter exporter;
+  return exporter.Export(pFile, pNode ? pNode : GetXFADoc()->GetRoot());
 }
diff --git a/xfa/fxfa/cxfa_ffdoc.h b/xfa/fxfa/cxfa_ffdoc.h
index e183568..04d98bc 100644
--- a/xfa/fxfa/cxfa_ffdoc.h
+++ b/xfa/fxfa/cxfa_ffdoc.h
@@ -10,88 +10,82 @@
 #include <map>
 #include <memory>
 
+#include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
 
 class CFGAS_PDFFontMgr;
 class CFX_ChecksumContext;
+class CFX_DIBBase;
+class CFX_DIBitmap;
+class CFX_XMLDocument;
 class CPDF_Document;
 class CXFA_FFApp;
 class CXFA_FFNotify;
 class CXFA_FFDocView;
+class CXFA_LayoutProcessor;
 
 struct FX_IMAGEDIB_AND_DPI {
   FX_IMAGEDIB_AND_DPI();
   FX_IMAGEDIB_AND_DPI(const FX_IMAGEDIB_AND_DPI& that);
-  FX_IMAGEDIB_AND_DPI(const RetainPtr<CFX_DIBSource>& pDib,
+  FX_IMAGEDIB_AND_DPI(const RetainPtr<CFX_DIBBase>& pDib,
                       int32_t xDpi,
                       int32_t yDpi);
   ~FX_IMAGEDIB_AND_DPI();
 
-  RetainPtr<CFX_DIBSource> pDibSource;
+  RetainPtr<CFX_DIBBase> pDibSource;
   int32_t iImageXDpi;
   int32_t iImageYDpi;
 };
 
-inline FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI() = default;
-inline FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI(
-    const FX_IMAGEDIB_AND_DPI& that) = default;
-
-inline FX_IMAGEDIB_AND_DPI::FX_IMAGEDIB_AND_DPI(
-    const RetainPtr<CFX_DIBSource>& pDib,
-    int32_t xDpi,
-    int32_t yDpi)
-    : pDibSource(pDib), iImageXDpi(xDpi), iImageYDpi(yDpi) {}
-
-inline FX_IMAGEDIB_AND_DPI::~FX_IMAGEDIB_AND_DPI() = default;
-
 class CXFA_FFDoc {
  public:
-  CXFA_FFDoc(CXFA_FFApp* pApp, IXFA_DocEnvironment* pDocEnvironment);
+  static std::unique_ptr<CXFA_FFDoc> CreateAndOpen(
+      CXFA_FFApp* pApp,
+      IXFA_DocEnvironment* pDocEnvironment,
+      CPDF_Document* pPDFDoc,
+      const RetainPtr<IFX_SeekableStream>& stream);
+
   ~CXFA_FFDoc();
 
   IXFA_DocEnvironment* GetDocEnvironment() const {
     return m_pDocEnvironment.Get();
   }
   FormType GetFormType() const { return m_FormType; }
-
-  int32_t StartLoad();
-  int32_t DoLoad();
-  void StopLoad();
+  CFX_XMLDocument* GetXMLDocument() const { return m_pXMLDoc.get(); }
 
   CXFA_FFDocView* CreateDocView();
 
-  bool OpenDoc(CPDF_Document* pPDFDoc);
-  void CloseDoc();
-
-  CXFA_Document* GetXFADoc() const { return m_pDocumentParser->GetDocument(); }
+  CXFA_Document* GetXFADoc() const { return m_pDocument.get(); }
   CXFA_FFApp* GetApp() const { return m_pApp.Get(); }
   CPDF_Document* GetPDFDoc() const { return m_pPDFDoc.Get(); }
   CXFA_FFDocView* GetDocView(CXFA_LayoutProcessor* pLayout);
   CXFA_FFDocView* GetDocView();
-  RetainPtr<CFX_DIBitmap> GetPDFNamedImage(const WideStringView& wsName,
+  RetainPtr<CFX_DIBitmap> GetPDFNamedImage(WideStringView wsName,
                                            int32_t& iImageXDpi,
                                            int32_t& iImageYDpi);
   CFGAS_PDFFontMgr* GetPDFFontMgr() const { return m_pPDFFontMgr.get(); }
 
   bool SavePackage(CXFA_Node* pNode,
-                   const RetainPtr<IFX_SeekableStream>& pFile,
-                   CFX_ChecksumContext* pCSContext);
-  bool ImportData(const RetainPtr<IFX_SeekableStream>& pStream,
-                  bool bXDP = true);
+                   const RetainPtr<IFX_SeekableStream>& pFile);
 
  private:
+  CXFA_FFDoc(CXFA_FFApp* pApp,
+             IXFA_DocEnvironment* pDocEnvironment,
+             CPDF_Document* pPDFDoc);
+  bool OpenDoc(const RetainPtr<IFX_SeekableStream>& stream);
+  bool ParseDoc(const RetainPtr<IFX_SeekableStream>& stream);
+
   UnownedPtr<IXFA_DocEnvironment> const m_pDocEnvironment;
-  std::unique_ptr<CXFA_DocumentParser> m_pDocumentParser;
-  RetainPtr<IFX_SeekableStream> m_pStream;
   UnownedPtr<CXFA_FFApp> const m_pApp;
+  UnownedPtr<CPDF_Document> const m_pPDFDoc;
+  std::unique_ptr<CFX_XMLDocument> m_pXMLDoc;
   std::unique_ptr<CXFA_FFNotify> m_pNotify;
-  UnownedPtr<CPDF_Document> m_pPDFDoc;
-  std::map<uint32_t, FX_IMAGEDIB_AND_DPI> m_HashToDibDpiMap;
+  std::unique_ptr<CXFA_Document> m_pDocument;
   std::unique_ptr<CXFA_FFDocView> m_DocView;
   std::unique_ptr<CFGAS_PDFFontMgr> m_pPDFFontMgr;
+  std::map<uint32_t, FX_IMAGEDIB_AND_DPI> m_HashToDibDpiMap;
   FormType m_FormType = FormType::kXFAForeground;
 };
 
diff --git a/xfa/fxfa/cxfa_ffdocview.cpp b/xfa/fxfa/cxfa_ffdocview.cpp
index c4e5299..3a44122 100644
--- a/xfa/fxfa/cxfa_ffdocview.cpp
+++ b/xfa/fxfa/cxfa_ffdocview.cpp
@@ -6,8 +6,11 @@
 
 #include "xfa/fxfa/cxfa_ffdocview.h"
 
+#include <set>
+#include <utility>
+
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
@@ -15,7 +18,6 @@
 #include "xfa/fxfa/cxfa_ffbarcode.h"
 #include "xfa/fxfa/cxfa_ffcheckbutton.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
-#include "xfa/fxfa/cxfa_ffdraw.h"
 #include "xfa/fxfa/cxfa_ffexclgroup.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 #include "xfa/fxfa/cxfa_ffimage.h"
@@ -23,68 +25,57 @@
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffpushbutton.h"
 #include "xfa/fxfa/cxfa_ffsignature.h"
-#include "xfa/fxfa/cxfa_ffsubform.h"
 #include "xfa/fxfa/cxfa_fftext.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
+#include "xfa/fxfa/cxfa_readynodeiterator.h"
 #include "xfa/fxfa/cxfa_textprovider.h"
-#include "xfa/fxfa/cxfa_widgetacciterator.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_acrobat.h"
 #include "xfa/fxfa/parser/cxfa_binditems.h"
 #include "xfa/fxfa/parser/cxfa_calculate.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_pageset.h"
 #include "xfa/fxfa/parser/cxfa_present.h"
 #include "xfa/fxfa/parser/cxfa_subform.h"
 #include "xfa/fxfa/parser/cxfa_validate.h"
 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
 
-const XFA_AttributeEnum gs_EventActivity[] = {
-    XFA_AttributeEnum::Click,      XFA_AttributeEnum::Change,
-    XFA_AttributeEnum::DocClose,   XFA_AttributeEnum::DocReady,
-    XFA_AttributeEnum::Enter,      XFA_AttributeEnum::Exit,
-    XFA_AttributeEnum::Full,       XFA_AttributeEnum::IndexChange,
-    XFA_AttributeEnum::Initialize, XFA_AttributeEnum::MouseDown,
-    XFA_AttributeEnum::MouseEnter, XFA_AttributeEnum::MouseExit,
-    XFA_AttributeEnum::MouseUp,    XFA_AttributeEnum::PostExecute,
-    XFA_AttributeEnum::PostOpen,   XFA_AttributeEnum::PostPrint,
-    XFA_AttributeEnum::PostSave,   XFA_AttributeEnum::PostSign,
-    XFA_AttributeEnum::PostSubmit, XFA_AttributeEnum::PreExecute,
-    XFA_AttributeEnum::PreOpen,    XFA_AttributeEnum::PrePrint,
-    XFA_AttributeEnum::PreSave,    XFA_AttributeEnum::PreSign,
-    XFA_AttributeEnum::PreSubmit,  XFA_AttributeEnum::Ready,
-    XFA_AttributeEnum::Unknown,
+const XFA_AttributeValue gs_EventActivity[] = {
+    XFA_AttributeValue::Click,      XFA_AttributeValue::Change,
+    XFA_AttributeValue::DocClose,   XFA_AttributeValue::DocReady,
+    XFA_AttributeValue::Enter,      XFA_AttributeValue::Exit,
+    XFA_AttributeValue::Full,       XFA_AttributeValue::IndexChange,
+    XFA_AttributeValue::Initialize, XFA_AttributeValue::MouseDown,
+    XFA_AttributeValue::MouseEnter, XFA_AttributeValue::MouseExit,
+    XFA_AttributeValue::MouseUp,    XFA_AttributeValue::PostExecute,
+    XFA_AttributeValue::PostOpen,   XFA_AttributeValue::PostPrint,
+    XFA_AttributeValue::PostSave,   XFA_AttributeValue::PostSign,
+    XFA_AttributeValue::PostSubmit, XFA_AttributeValue::PreExecute,
+    XFA_AttributeValue::PreOpen,    XFA_AttributeValue::PrePrint,
+    XFA_AttributeValue::PreSave,    XFA_AttributeValue::PreSign,
+    XFA_AttributeValue::PreSubmit,  XFA_AttributeValue::Ready,
+    XFA_AttributeValue::Unknown,
 };
 
-CXFA_FFDocView::CXFA_FFDocView(CXFA_FFDoc* pDoc)
-    : m_bLayoutEvent(false),
-      m_pListFocusWidget(nullptr),
-      m_bInLayoutStatus(false),
-      m_pDoc(pDoc),
-      m_pXFADocLayout(nullptr),
-      m_iStatus(XFA_DOCVIEW_LAYOUTSTATUS_None),
-      m_iLock(0) {}
+CXFA_FFDocView::CXFA_FFDocView(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {}
 
-CXFA_FFDocView::~CXFA_FFDocView() {
-  DestroyDocView();
-}
+CXFA_FFDocView::~CXFA_FFDocView() = default;
 
 void CXFA_FFDocView::InitLayout(CXFA_Node* pNode) {
   RunBindItems();
-  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Initialize, false, true,
-                               nullptr);
-  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_IndexChange, false, true,
-                               nullptr);
+  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Initialize, false, true);
+  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_IndexChange, false, true);
 }
 
-int32_t CXFA_FFDocView::StartLayout(int32_t iStartPage) {
+int32_t CXFA_FFDocView::StartLayout() {
   m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_Start;
   m_pDoc->GetXFADoc()->DoProtoMerge();
   m_pDoc->GetXFADoc()->DoDataMerge();
   m_pXFADocLayout = GetXFALayout();
 
-  int32_t iStatus = m_pXFADocLayout->StartLayout();
+  int32_t iStatus = m_pXFADocLayout->StartLayout(false);
   if (iStatus < 0)
     return iStatus;
 
@@ -97,14 +88,13 @@
   InitCalculate(pRootItem);
   InitValidate(pRootItem);
 
-  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, true, true, nullptr);
+  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, true, true);
   m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_Start;
   return iStatus;
 }
 
 int32_t CXFA_FFDocView::DoLayout() {
-  int32_t iStatus = 100;
-  iStatus = m_pXFADocLayout->DoLayout();
+  int32_t iStatus = m_pXFADocLayout->DoLayout();
   if (iStatus != 100)
     return iStatus;
 
@@ -135,24 +125,19 @@
   InitCalculate(pPageSetNode);
   InitValidate(pPageSetNode);
 
-  ExecEventActivityByDeepFirst(pPageSetNode, XFA_EVENT_Ready, true, true,
-                               nullptr);
-  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true,
-                               nullptr);
-  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_DocReady, false, true,
-                               nullptr);
+  ExecEventActivityByDeepFirst(pPageSetNode, XFA_EVENT_Ready, true, true);
+  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true);
+  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_DocReady, false, true);
 
   RunCalculateWidgets();
   RunValidate();
 
-  if (RunLayout()) {
-    ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true,
-                                 nullptr);
-  }
+  if (RunLayout())
+    ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true);
 
-  m_CalculateAccs.clear();
-  if (m_pFocusAcc && !m_pFocusWidget)
-    SetFocusWidgetAcc(m_pFocusAcc.Get());
+  m_CalculateNodes.clear();
+  if (m_pFocusNode && !m_pFocusWidget)
+    SetFocusNode(m_pFocusNode.Get());
 
   m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_End;
 }
@@ -174,8 +159,9 @@
                            L"validation errors not reported.",
                            iRemain);
     }
-    pAppProvider->MsgBox(wsMsg, pAppProvider->GetAppTitle(), XFA_MBICON_Status,
-                         XFA_MB_OK);
+    pAppProvider->MsgBox(wsMsg, pAppProvider->GetAppTitle(),
+                         static_cast<uint32_t>(AlertIcon::kStatus),
+                         static_cast<uint32_t>(AlertButton::kOK));
   }
   m_arrNullTestMsg.clear();
 }
@@ -185,12 +171,13 @@
     return;
 
   LockUpdate();
-  for (CXFA_Node* pNode : m_NewAddedNodes) {
+  while (!m_NewAddedNodes.empty()) {
+    CXFA_Node* pNode = m_NewAddedNodes.front();
+    m_NewAddedNodes.pop_front();
     InitCalculate(pNode);
     InitValidate(pNode);
-    ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Ready, true, true, nullptr);
+    ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Ready, true, true);
   }
-  m_NewAddedNodes.clear();
 
   RunSubformIndexChange();
   RunCalculateWidgets();
@@ -202,11 +189,30 @@
     RunEventLayoutReady();
 
   m_bLayoutEvent = false;
-  m_CalculateAccs.clear();
-  RunInvalidate();
+  m_CalculateNodes.clear();
   UnlockUpdate();
 }
 
+void CXFA_FFDocView::UpdateUIDisplay(CXFA_Node* pNode, CXFA_FFWidget* pExcept) {
+  CXFA_FFWidget* pWidget = GetWidgetForNode(pNode);
+  CXFA_FFWidget* pNext = nullptr;
+  for (; pWidget; pWidget = pNext) {
+    pNext = pWidget->GetNextFFWidget();
+    if (pWidget == pExcept || !pWidget->IsLoaded() ||
+        (pNode->GetFFWidgetType() != XFA_FFWidgetType::kCheckButton &&
+         pWidget->IsFocused())) {
+      continue;
+    }
+    ObservedPtr<CXFA_FFWidget> pWatched(pWidget);
+    ObservedPtr<CXFA_FFWidget> pWatchedNext(pNext);
+    pWatched->UpdateFWLData();
+    if (pWatched)
+      pWatched->InvalidateRect();
+    if (!pWatchedNext)
+      break;
+  }
+}
+
 int32_t CXFA_FFDocView::CountPageViews() const {
   return m_pXFADocLayout ? m_pXFADocLayout->CountPages() : 0;
 }
@@ -214,37 +220,37 @@
 CXFA_FFPageView* CXFA_FFDocView::GetPageView(int32_t nIndex) const {
   if (!m_pXFADocLayout)
     return nullptr;
-  return static_cast<CXFA_FFPageView*>(m_pXFADocLayout->GetPage(nIndex));
+  auto* pPage = m_pXFADocLayout->GetPage(nIndex);
+  return pPage ? pPage->GetPageView() : nullptr;
 }
 
 CXFA_LayoutProcessor* CXFA_FFDocView::GetXFALayout() const {
-  return m_pDoc->GetXFADoc()->GetDocLayout();
+  return CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
 }
 
-bool CXFA_FFDocView::ResetSingleWidgetAccData(CXFA_WidgetAcc* pWidgetAcc) {
-  CXFA_Node* pNode = pWidgetAcc->GetNode();
+bool CXFA_FFDocView::ResetSingleNodeData(CXFA_Node* pNode) {
   XFA_Element eType = pNode->GetElementType();
   if (eType != XFA_Element::Field && eType != XFA_Element::ExclGroup)
     return false;
 
-  pWidgetAcc->ResetData();
-  pWidgetAcc->UpdateUIDisplay(this, nullptr);
+  pNode->ResetData();
+  UpdateUIDisplay(pNode, nullptr);
   CXFA_Validate* validate = pNode->GetValidateIfExists();
   if (!validate)
     return true;
 
-  AddValidateWidget(pWidgetAcc);
-  validate->SetFlag(XFA_NodeFlag_NeedsInitApp, false);
+  AddValidateNode(pNode);
+  validate->SetFlag(XFA_NodeFlag_NeedsInitApp);
   return true;
 }
 
-void CXFA_FFDocView::ResetWidgetAcc(CXFA_WidgetAcc* pWidgetAcc) {
+void CXFA_FFDocView::ResetNode(CXFA_Node* pNode) {
   m_bLayoutEvent = true;
   bool bChanged = false;
   CXFA_Node* pFormNode = nullptr;
-  if (pWidgetAcc) {
-    bChanged = ResetSingleWidgetAccData(pWidgetAcc);
-    pFormNode = pWidgetAcc->GetNode();
+  if (pNode) {
+    bChanged = ResetSingleNodeData(pNode);
+    pFormNode = pNode;
   } else {
     pFormNode = GetRootSubform();
   }
@@ -253,65 +259,19 @@
 
   if (pFormNode->GetElementType() != XFA_Element::Field &&
       pFormNode->GetElementType() != XFA_Element::ExclGroup) {
-    CXFA_WidgetAccIterator Iterator(pFormNode);
-    while (CXFA_WidgetAcc* pAcc = Iterator.MoveToNext()) {
-      bChanged |= ResetSingleWidgetAccData(pAcc);
-      if (pAcc->GetNode()->GetElementType() == XFA_Element::ExclGroup)
-        Iterator.SkipTree();
+    CXFA_ReadyNodeIterator it(pFormNode);
+    while (CXFA_Node* next_node = it.MoveToNext()) {
+      bChanged |= ResetSingleNodeData(next_node);
+      if (next_node->GetElementType() == XFA_Element::ExclGroup)
+        it.SkipTree();
     }
   }
   if (bChanged)
     m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc.Get());
 }
 
-int32_t CXFA_FFDocView::ProcessWidgetEvent(CXFA_EventParam* pParam,
-                                           CXFA_WidgetAcc* pWidgetAcc) {
-  if (!pParam)
-    return XFA_EVENTERROR_Error;
-
-  if (pParam->m_eType == XFA_EVENT_Validate) {
-    WideString wsValidateStr(L"preSubmit");
-    CXFA_Node* pConfigItem =
-        ToNode(m_pDoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Config));
-    if (pConfigItem) {
-      CXFA_Acrobat* pAcrobatNode =
-          pConfigItem->GetChild<CXFA_Acrobat>(0, XFA_Element::Acrobat, false);
-      CXFA_Validate* pValidateNode =
-          pAcrobatNode ? pAcrobatNode->GetChild<CXFA_Validate>(
-                             0, XFA_Element::Validate, false)
-                       : nullptr;
-      if (!pValidateNode) {
-        CXFA_Present* pPresentNode =
-            pConfigItem->GetChild<CXFA_Present>(0, XFA_Element::Present, false);
-        pValidateNode = pPresentNode ? pPresentNode->GetChild<CXFA_Validate>(
-                                           0, XFA_Element::Validate, false)
-                                     : nullptr;
-      }
-      if (pValidateNode)
-        wsValidateStr = pValidateNode->JSObject()->GetContent(false);
-    }
-
-    if (!wsValidateStr.Contains(L"preSubmit"))
-      return XFA_EVENTERROR_Success;
-  }
-
-  CXFA_Node* pNode = pWidgetAcc ? pWidgetAcc->GetNode() : nullptr;
-  if (!pNode) {
-    CXFA_Node* pRootItem =
-        ToNode(m_pDoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form));
-    if (!pRootItem)
-      return XFA_EVENTERROR_Error;
-
-    pNode = pRootItem->GetChild<CXFA_Node>(0, XFA_Element::Subform, false);
-  }
-
-  ExecEventActivityByDeepFirst(pNode, pParam->m_eType, pParam->m_bIsFormReady,
-                               true, nullptr);
-  return XFA_EVENTERROR_Success;
-}
-
 CXFA_FFWidget* CXFA_FFDocView::GetWidgetForNode(CXFA_Node* node) {
-  return static_cast<CXFA_FFWidget*>(GetXFALayout()->GetLayoutItem(node));
+  return GetFFWidget(ToContentLayoutItem(GetXFALayout()->GetLayoutItem(node)));
 }
 
 CXFA_FFWidgetHandler* CXFA_FFDocView::GetWidgetHandler() {
@@ -320,74 +280,60 @@
   return m_pWidgetHandler.get();
 }
 
-std::unique_ptr<CXFA_WidgetAccIterator>
-CXFA_FFDocView::CreateWidgetAccIterator() {
+std::unique_ptr<CXFA_ReadyNodeIterator>
+CXFA_FFDocView::CreateReadyNodeIterator() {
   CXFA_Subform* pFormRoot = GetRootSubform();
-  return pFormRoot ? pdfium::MakeUnique<CXFA_WidgetAccIterator>(pFormRoot)
+  return pFormRoot ? pdfium::MakeUnique<CXFA_ReadyNodeIterator>(pFormRoot)
                    : nullptr;
 }
 
-void CXFA_FFDocView::KillFocus() {
-  if (m_pFocusWidget &&
-      (m_pFocusWidget->GetStatus() & XFA_WidgetStatus_Focused)) {
-    m_pFocusWidget->OnKillFocus(nullptr);
-  }
-  m_pFocusAcc = nullptr;
-  m_pFocusWidget = nullptr;
-  m_pOldFocusWidget = nullptr;
-}
-
-bool CXFA_FFDocView::SetFocus(CXFA_FFWidget* hWidget) {
-  CXFA_FFWidget* pNewFocus = hWidget;
-  if (m_pOldFocusWidget == pNewFocus)
+bool CXFA_FFDocView::SetFocus(CXFA_FFWidget* pNewFocus) {
+  if (pNewFocus == m_pFocusWidget)
     return false;
 
-  CXFA_FFWidget* pOldFocus = m_pOldFocusWidget.Get();
-  m_pOldFocusWidget = pNewFocus;
-  if (pOldFocus) {
-    if (m_pFocusWidget != m_pOldFocusWidget &&
-        (pOldFocus->GetStatus() & XFA_WidgetStatus_Focused)) {
-      m_pFocusWidget = pOldFocus;
-      pOldFocus->OnKillFocus(pNewFocus);
-    } else if ((pOldFocus->GetStatus() & XFA_WidgetStatus_Visible)) {
-      if (!pOldFocus->IsLoaded())
-        pOldFocus->LoadWidget();
-
-      pOldFocus->OnSetFocus(m_pFocusWidget.Get());
-      m_pFocusWidget = pOldFocus;
-      pOldFocus->OnKillFocus(pNewFocus);
+  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewFocus);
+  if (m_pFocusWidget) {
+    CXFA_ContentLayoutItem* pItem = m_pFocusWidget->GetLayoutItem();
+    if (pItem->TestStatusBits(XFA_WidgetStatus_Visible) &&
+        !pItem->TestStatusBits(XFA_WidgetStatus_Focused)) {
+      if (!m_pFocusWidget->IsLoaded())
+        m_pFocusWidget->LoadWidget();
+      if (!m_pFocusWidget->OnSetFocus(m_pFocusWidget.Get()))
+        m_pFocusWidget.Reset();
     }
   }
-  if (m_pFocusWidget == m_pOldFocusWidget)
-    return false;
+  if (m_pFocusWidget) {
+    if (!m_pFocusWidget->OnKillFocus(pNewWatched.Get()))
+      return false;
+  }
 
-  pNewFocus = m_pOldFocusWidget.Get();
-  if (m_pListFocusWidget && pNewFocus == m_pListFocusWidget) {
-    m_pFocusAcc = nullptr;
-    m_pFocusWidget = nullptr;
-    m_pListFocusWidget = nullptr;
-    m_pOldFocusWidget = nullptr;
-    return false;
+  if (pNewWatched) {
+    if (pNewWatched->GetLayoutItem()->TestStatusBits(
+            XFA_WidgetStatus_Visible)) {
+      if (!pNewWatched->IsLoaded())
+        pNewWatched->LoadWidget();
+      if (!pNewWatched->OnSetFocus(m_pFocusWidget.Get()))
+        pNewWatched.Reset();
+    }
   }
-  if (pNewFocus && (pNewFocus->GetStatus() & XFA_WidgetStatus_Visible)) {
-    if (!pNewFocus->IsLoaded())
-      pNewFocus->LoadWidget();
-    pNewFocus->OnSetFocus(m_pFocusWidget.Get());
+  if (pNewWatched) {
+    CXFA_Node* node = pNewWatched->GetNode();
+    m_pFocusNode = node->IsWidgetReady() ? node : nullptr;
+    m_pFocusWidget.Reset(pNewWatched.Get());
+  } else {
+    m_pFocusNode.Reset();
+    m_pFocusWidget.Reset();
   }
-  m_pFocusAcc = pNewFocus ? pNewFocus->GetNode()->GetWidgetAcc() : nullptr;
-  m_pFocusWidget = pNewFocus;
-  m_pOldFocusWidget = m_pFocusWidget;
+
   return true;
 }
 
-void CXFA_FFDocView::SetFocusWidgetAcc(CXFA_WidgetAcc* pWidgetAcc) {
-  CXFA_FFWidget* pNewFocus = nullptr;
-  if (pWidgetAcc)
-    pNewFocus = GetWidgetForNode(pWidgetAcc->GetNode());
+void CXFA_FFDocView::SetFocusNode(CXFA_Node* node) {
+  CXFA_FFWidget* pNewFocus = node ? GetWidgetForNode(node) : nullptr;
   if (!SetFocus(pNewFocus))
     return;
 
-  m_pFocusAcc = pWidgetAcc;
+  m_pFocusNode = node;
   if (m_iStatus != XFA_DOCVIEW_LAYOUTSTATUS_End)
     return;
 
@@ -396,177 +342,136 @@
 }
 
 void CXFA_FFDocView::DeleteLayoutItem(CXFA_FFWidget* pWidget) {
-  if (m_pFocusAcc && m_pFocusAcc->GetNode() != pWidget->GetNode())
+  if (m_pFocusNode != pWidget->GetNode())
     return;
 
-  m_pFocusAcc = nullptr;
-  m_pFocusWidget = nullptr;
-  m_pOldFocusWidget = nullptr;
+  m_pFocusNode.Reset();
+  m_pFocusWidget.Reset();
 }
 
-static int32_t XFA_ProcessEvent(CXFA_FFDocView* pDocView,
-                                CXFA_WidgetAcc* pWidgetAcc,
-                                CXFA_EventParam* pParam) {
+static XFA_EventError XFA_ProcessEvent(CXFA_FFDocView* pDocView,
+                                       CXFA_Node* pNode,
+                                       CXFA_EventParam* pParam) {
   if (!pParam || pParam->m_eType == XFA_EVENT_Unknown)
-    return XFA_EVENTERROR_NotExist;
-  if (!pWidgetAcc)
-    return XFA_EVENTERROR_NotExist;
-
-  CXFA_Node* node = pWidgetAcc->GetNode();
-  if (node && node->GetElementType() == XFA_Element::Draw)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
+  if (pNode && pNode->GetElementType() == XFA_Element::Draw)
+    return XFA_EventError::kNotExist;
 
   switch (pParam->m_eType) {
     case XFA_EVENT_Calculate:
-      return node->ProcessCalculate(pDocView);
+      return pNode->ProcessCalculate(pDocView);
     case XFA_EVENT_Validate:
       if (pDocView->GetDoc()->GetDocEnvironment()->IsValidationsEnabled(
               pDocView->GetDoc())) {
-        return node->ProcessValidate(pDocView, 0x01);
+        return pNode->ProcessValidate(pDocView, 0x01);
       }
-      return XFA_EVENTERROR_Disabled;
+      return XFA_EventError::kDisabled;
     case XFA_EVENT_InitCalculate: {
-      CXFA_Calculate* calc = node->GetCalculateIfExists();
+      CXFA_Calculate* calc = pNode->GetCalculateIfExists();
       if (!calc)
-        return XFA_EVENTERROR_NotExist;
-      if (node->IsUserInteractive())
-        return XFA_EVENTERROR_Disabled;
+        return XFA_EventError::kNotExist;
+      if (pNode->IsUserInteractive())
+        return XFA_EventError::kDisabled;
 
-      return node->ExecuteScript(pDocView, calc->GetScriptIfExists(), pParam);
+      return pNode->ExecuteScript(pDocView, calc->GetScriptIfExists(), pParam);
     }
     default:
-      break;
+      return pNode->ProcessEvent(pDocView, gs_EventActivity[pParam->m_eType],
+                                 pParam);
   }
-
-  return node->ProcessEvent(pDocView, gs_EventActivity[pParam->m_eType],
-                            pParam);
 }
 
-int32_t CXFA_FFDocView::ExecEventActivityByDeepFirst(CXFA_Node* pFormNode,
-                                                     XFA_EVENTTYPE eEventType,
-                                                     bool bIsFormReady,
-                                                     bool bRecursive,
-                                                     CXFA_Node* pExclude) {
-  if (pFormNode == pExclude)
-    return XFA_EVENTERROR_NotExist;
+XFA_EventError CXFA_FFDocView::ExecEventActivityByDeepFirst(
+    CXFA_Node* pFormNode,
+    XFA_EVENTTYPE eEventType,
+    bool bIsFormReady,
+    bool bRecursive) {
+  if (!pFormNode)
+    return XFA_EventError::kNotExist;
 
   XFA_Element elementType = pFormNode->GetElementType();
   if (elementType == XFA_Element::Field) {
     if (eEventType == XFA_EVENT_IndexChange)
-      return XFA_EVENTERROR_NotExist;
+      return XFA_EventError::kNotExist;
 
-    CXFA_WidgetAcc* pWidgetAcc = pFormNode->GetWidgetAcc();
-    if (!pWidgetAcc)
-      return XFA_EVENTERROR_NotExist;
+    if (!pFormNode->IsWidgetReady())
+      return XFA_EventError::kNotExist;
 
     CXFA_EventParam eParam;
     eParam.m_eType = eEventType;
-    eParam.m_pTarget = pWidgetAcc;
+    eParam.m_pTarget = pFormNode;
     eParam.m_bIsFormReady = bIsFormReady;
-    return XFA_ProcessEvent(this, pWidgetAcc, &eParam);
+    return XFA_ProcessEvent(this, pFormNode, &eParam);
   }
 
-  int32_t iRet = XFA_EVENTERROR_NotExist;
+  XFA_EventError iRet = XFA_EventError::kNotExist;
   if (bRecursive) {
     for (CXFA_Node* pNode = pFormNode->GetFirstContainerChild(); pNode;
          pNode = pNode->GetNextContainerSibling()) {
       elementType = pNode->GetElementType();
       if (elementType != XFA_Element::Variables &&
           elementType != XFA_Element::Draw) {
-        iRet |= ExecEventActivityByDeepFirst(pNode, eEventType, bIsFormReady,
-                                             bRecursive, pExclude);
+        XFA_EventErrorAccumulate(
+            &iRet, ExecEventActivityByDeepFirst(pNode, eEventType, bIsFormReady,
+                                                bRecursive));
       }
     }
   }
-  CXFA_WidgetAcc* pWidgetAcc = pFormNode->GetWidgetAcc();
-  if (!pWidgetAcc)
+  if (!pFormNode->IsWidgetReady())
     return iRet;
 
   CXFA_EventParam eParam;
   eParam.m_eType = eEventType;
-  eParam.m_pTarget = pWidgetAcc;
+  eParam.m_pTarget = pFormNode;
   eParam.m_bIsFormReady = bIsFormReady;
-  iRet |= XFA_ProcessEvent(this, pWidgetAcc, &eParam);
 
+  XFA_EventErrorAccumulate(&iRet, XFA_ProcessEvent(this, pFormNode, &eParam));
   return iRet;
 }
 
 CXFA_FFWidget* CXFA_FFDocView::GetWidgetByName(const WideString& wsName,
                                                CXFA_FFWidget* pRefWidget) {
-  CXFA_WidgetAcc* pRefAcc =
-      pRefWidget ? pRefWidget->GetNode()->GetWidgetAcc() : nullptr;
-  CXFA_WidgetAcc* pAcc = GetWidgetAccByName(wsName, pRefAcc);
-  if (!pAcc)
-    return nullptr;
-  return GetWidgetForNode(pAcc->GetNode());
-}
-
-CXFA_WidgetAcc* CXFA_FFDocView::GetWidgetAccByName(
-    const WideString& wsName,
-    CXFA_WidgetAcc* pRefWidgetAcc) {
-  WideString wsExpression;
-  uint32_t dwStyle = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-                     XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
   CFXJSE_Engine* pScriptContext = m_pDoc->GetXFADoc()->GetScriptContext();
-  if (!pScriptContext)
-    return nullptr;
-
-  CXFA_Node* refNode = nullptr;
-  if (pRefWidgetAcc) {
-    refNode = pRefWidgetAcc->GetNode();
-    wsExpression = wsName;
-  } else {
-    wsExpression = L"$form." + wsName;
+  CXFA_Node* pRefNode = nullptr;
+  if (pRefWidget) {
+    CXFA_Node* node = pRefWidget->GetNode();
+    pRefNode = node->IsWidgetReady() ? node : nullptr;
   }
+  WideString wsExpression = (!pRefNode ? L"$form." : L"") + wsName;
 
   XFA_RESOLVENODE_RS resolveNodeRS;
-  if (!pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(),
-                                      &resolveNodeRS, dwStyle, nullptr)) {
+  constexpr uint32_t kStyle = XFA_RESOLVENODE_Children |
+                              XFA_RESOLVENODE_Properties |
+                              XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
+  if (!pScriptContext->ResolveObjects(pRefNode, wsExpression.AsStringView(),
+                                      &resolveNodeRS, kStyle, nullptr)) {
     return nullptr;
   }
 
   if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
     CXFA_Node* pNode = resolveNodeRS.objects.front()->AsNode();
-    if (pNode)
-      return pNode->GetWidgetAcc();
+    if (pNode && pNode->IsWidgetReady())
+      return GetWidgetForNode(pNode);
   }
   return nullptr;
 }
 
-void CXFA_FFDocView::OnPageEvent(CXFA_ContainerLayoutItem* pSender,
+void CXFA_FFDocView::OnPageEvent(CXFA_ViewLayoutItem* pSender,
                                  uint32_t dwEvent) {
-  CXFA_FFPageView* pFFPageView = static_cast<CXFA_FFPageView*>(pSender);
+  CXFA_FFPageView* pFFPageView = pSender ? pSender->GetPageView() : nullptr;
   m_pDoc->GetDocEnvironment()->PageViewEvent(pFFPageView, dwEvent);
 }
 
-
-void CXFA_FFDocView::AddInvalidateRect(CXFA_FFWidget* pWidget,
-                                       const CFX_RectF& rtInvalidate) {
-  AddInvalidateRect(pWidget->GetPageView(), rtInvalidate);
-}
-
-void CXFA_FFDocView::AddInvalidateRect(CXFA_FFPageView* pPageView,
-                                       const CFX_RectF& rtInvalidate) {
-  if (m_mapPageInvalidate[pPageView]) {
-    m_mapPageInvalidate[pPageView]->Union(rtInvalidate);
-    return;
-  }
-
-  m_mapPageInvalidate[pPageView] = pdfium::MakeUnique<CFX_RectF>(rtInvalidate);
-}
-
-void CXFA_FFDocView::RunInvalidate() {
-  for (const auto& pair : m_mapPageInvalidate)
-    m_pDoc->GetDocEnvironment()->InvalidateRect(pair.first, *pair.second);
-
-  m_mapPageInvalidate.clear();
+void CXFA_FFDocView::InvalidateRect(CXFA_FFPageView* pPageView,
+                                    const CFX_RectF& rtInvalidate) {
+  m_pDoc->GetDocEnvironment()->InvalidateRect(pPageView, rtInvalidate);
 }
 
 bool CXFA_FFDocView::RunLayout() {
   LockUpdate();
   m_bInLayoutStatus = true;
   if (!m_pXFADocLayout->IncrementLayout() &&
-      m_pXFADocLayout->StartLayout() < 100) {
+      m_pXFADocLayout->StartLayout(false) < 100) {
     m_pXFADocLayout->DoLayout();
     UnlockUpdate();
     m_bInLayoutStatus = false;
@@ -583,16 +488,19 @@
 }
 
 void CXFA_FFDocView::RunSubformIndexChange() {
-  for (CXFA_Node* pSubformNode : m_IndexChangedSubforms) {
-    if (!pSubformNode->GetWidgetAcc())
+  std::set<CXFA_Node*> seen;
+  while (!m_IndexChangedSubforms.empty()) {
+    CXFA_Node* pSubformNode = m_IndexChangedSubforms.front();
+    m_IndexChangedSubforms.pop_front();
+    bool bInserted = seen.insert(pSubformNode).second;
+    if (!bInserted || !pSubformNode->IsWidgetReady())
       continue;
 
     CXFA_EventParam eParam;
     eParam.m_eType = XFA_EVENT_IndexChange;
-    eParam.m_pTarget = pSubformNode->GetWidgetAcc();
-    pSubformNode->ProcessEvent(this, XFA_AttributeEnum::IndexChange, &eParam);
+    eParam.m_pTarget = pSubformNode;
+    pSubformNode->ProcessEvent(this, XFA_AttributeValue::IndexChange, &eParam);
   }
-  m_IndexChangedSubforms.clear();
 }
 
 void CXFA_FFDocView::AddNewFormNode(CXFA_Node* pNode) {
@@ -602,7 +510,8 @@
 
 void CXFA_FFDocView::AddIndexChangedSubform(CXFA_Node* pNode) {
   ASSERT(pNode->GetElementType() == XFA_Element::Subform);
-  m_IndexChangedSubforms.push_back(pNode);
+  if (!pdfium::ContainsValue(m_IndexChangedSubforms, pNode))
+    m_IndexChangedSubforms.push_back(pNode);
 }
 
 void CXFA_FFDocView::RunDocClose() {
@@ -611,24 +520,14 @@
   if (!pRootItem)
     return;
 
-  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_DocClose, false, true,
-                               nullptr);
+  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_DocClose, false, true);
 }
 
-void CXFA_FFDocView::DestroyDocView() {
-  ClearInvalidateList();
-  m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_None;
-  m_iLock = 0;
-  m_ValidateAccs.clear();
-  m_BindItems.clear();
-  m_CalculateAccs.clear();
-}
-
-void CXFA_FFDocView::AddCalculateWidgetAcc(CXFA_WidgetAcc* pWidgetAcc) {
-  CXFA_WidgetAcc* pCurrentAcc =
-      !m_CalculateAccs.empty() ? m_CalculateAccs.back() : nullptr;
-  if (pCurrentAcc != pWidgetAcc)
-    m_CalculateAccs.push_back(pWidgetAcc);
+void CXFA_FFDocView::AddCalculateNode(CXFA_Node* node) {
+  CXFA_Node* pCurrentNode =
+      !m_CalculateNodes.empty() ? m_CalculateNodes.back() : nullptr;
+  if (pCurrentNode != node)
+    m_CalculateNodes.push_back(node);
 }
 
 void CXFA_FFDocView::AddCalculateNodeNotify(CXFA_Node* pNodeChange) {
@@ -637,54 +536,56 @@
     return;
 
   for (auto* pResult : pGlobalData->m_Globals) {
-    if (!pResult->HasRemovedChildren())
-      AddCalculateWidgetAcc(pResult->GetWidgetAcc());
+    if (!pResult->HasRemovedChildren() && pResult->IsWidgetReady())
+      AddCalculateNode(pResult);
   }
 }
 
 size_t CXFA_FFDocView::RunCalculateRecursive(size_t index) {
-  while (index < m_CalculateAccs.size()) {
-    CXFA_Node* node = m_CalculateAccs[index]->GetNode();
+  while (index < m_CalculateNodes.size()) {
+    CXFA_Node* node = m_CalculateNodes[index];
 
     AddCalculateNodeNotify(node);
     size_t recurse = node->JSObject()->GetCalcRecursionCount() + 1;
     node->JSObject()->SetCalcRecursionCount(recurse);
     if (recurse > 11)
       break;
-    if (node->ProcessCalculate(this) == XFA_EVENTERROR_Success)
-      AddValidateWidget(node->GetWidgetAcc());
+    if (node->ProcessCalculate(this) == XFA_EventError::kSuccess &&
+        node->IsWidgetReady()) {
+      AddValidateNode(node);
+    }
 
     index = RunCalculateRecursive(++index);
   }
   return index;
 }
 
-int32_t CXFA_FFDocView::RunCalculateWidgets() {
+XFA_EventError CXFA_FFDocView::RunCalculateWidgets() {
   if (!m_pDoc->GetDocEnvironment()->IsCalculationsEnabled(m_pDoc.Get()))
-    return XFA_EVENTERROR_Disabled;
-  if (!m_CalculateAccs.empty())
+    return XFA_EventError::kDisabled;
+
+  if (!m_CalculateNodes.empty())
     RunCalculateRecursive(0);
 
-  for (CXFA_WidgetAcc* pCurAcc : m_CalculateAccs)
-    pCurAcc->GetNode()->JSObject()->SetCalcRecursionCount(0);
+  for (CXFA_Node* node : m_CalculateNodes)
+    node->JSObject()->SetCalcRecursionCount(0);
 
-  m_CalculateAccs.clear();
-  return XFA_EVENTERROR_Success;
+  m_CalculateNodes.clear();
+  return XFA_EventError::kSuccess;
 }
 
-void CXFA_FFDocView::AddValidateWidget(CXFA_WidgetAcc* pWidget) {
-  if (!pdfium::ContainsValue(m_ValidateAccs, pWidget))
-    m_ValidateAccs.push_back(pWidget);
+void CXFA_FFDocView::AddValidateNode(CXFA_Node* node) {
+  if (!pdfium::ContainsValue(m_ValidateNodes, node))
+    m_ValidateNodes.push_back(node);
 }
 
 void CXFA_FFDocView::InitCalculate(CXFA_Node* pNode) {
-  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_InitCalculate, false, true,
-                               nullptr);
+  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_InitCalculate, false, true);
 }
 
-void CXFA_FFDocView::ProcessValueChanged(CXFA_WidgetAcc* widgetAcc) {
-  AddValidateWidget(widgetAcc);
-  AddCalculateWidgetAcc(widgetAcc);
+void CXFA_FFDocView::ProcessValueChanged(CXFA_Node* node) {
+  AddValidateNode(node);
+  AddCalculateNode(node);
   RunCalculateWidgets();
   RunValidate();
 }
@@ -693,8 +594,8 @@
   if (!m_pDoc->GetDocEnvironment()->IsValidationsEnabled(m_pDoc.Get()))
     return false;
 
-  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Validate, false, true, nullptr);
-  m_ValidateAccs.clear();
+  ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Validate, false, true);
+  m_ValidateNodes.clear();
   return true;
 }
 
@@ -702,12 +603,12 @@
   if (!m_pDoc->GetDocEnvironment()->IsValidationsEnabled(m_pDoc.Get()))
     return false;
 
-  for (CXFA_WidgetAcc* pAcc : m_ValidateAccs) {
-    CXFA_Node* node = pAcc->GetNode();
+  while (!m_ValidateNodes.empty()) {
+    CXFA_Node* node = m_ValidateNodes.front();
+    m_ValidateNodes.pop_front();
     if (!node->HasRemovedChildren())
       node->ProcessValidate(this, 0);
   }
-  m_ValidateAccs.clear();
   return true;
 }
 
@@ -717,44 +618,46 @@
   if (!pRootItem)
     return false;
 
-  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true,
-                               nullptr);
+  ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true);
   RunLayout();
   return true;
 }
 
 void CXFA_FFDocView::RunBindItems() {
-  for (auto* item : m_BindItems) {
+  while (!m_BindItems.empty()) {
+    CXFA_BindItems* item = m_BindItems.front();
+    m_BindItems.pop_front();
     if (item->HasRemovedChildren())
       continue;
 
     CXFA_Node* pWidgetNode = item->GetParent();
-    CXFA_WidgetAcc* pAcc = pWidgetNode->GetWidgetAcc();
-    if (!pAcc)
+    if (!pWidgetNode || !pWidgetNode->IsWidgetReady())
       continue;
 
     CFXJSE_Engine* pScriptContext =
         pWidgetNode->GetDocument()->GetScriptContext();
     WideString wsRef = item->GetRef();
-    uint32_t dwStyle = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-                       XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent |
-                       XFA_RESOLVENODE_ALL;
+    constexpr uint32_t kStyle =
+        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
+        XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_ALL;
     XFA_RESOLVENODE_RS rs;
     pScriptContext->ResolveObjects(pWidgetNode, wsRef.AsStringView(), &rs,
-                                   dwStyle, nullptr);
-    pAcc->DeleteItem(-1, false, false);
+                                   kStyle, nullptr);
+    pWidgetNode->DeleteItem(-1, false, false);
     if (rs.dwFlags != XFA_ResolveNode_RSType_Nodes || rs.objects.empty())
       continue;
 
     WideString wsValueRef = item->GetValueRef();
     WideString wsLabelRef = item->GetLabelRef();
     const bool bUseValue = wsLabelRef.IsEmpty() || wsLabelRef == wsValueRef;
-    const bool bLabelUseContent = wsLabelRef.IsEmpty() || wsLabelRef == L"$";
-    const bool bValueUseContent = wsValueRef.IsEmpty() || wsValueRef == L"$";
+    const bool bLabelUseContent =
+        wsLabelRef.IsEmpty() || wsLabelRef.EqualsASCII("$");
+    const bool bValueUseContent =
+        wsValueRef.IsEmpty() || wsValueRef.EqualsASCII("$");
     WideString wsValue;
     WideString wsLabel;
     uint32_t uValueHash = FX_HashCode_GetW(wsValueRef.AsStringView(), false);
-    for (CXFA_Object* refObject : rs.objects) {
+    for (auto& refObject : rs.objects) {
       CXFA_Node* refNode = refObject->AsNode();
       if (!refNode)
         continue;
@@ -779,10 +682,9 @@
       } else {
         wsLabel = wsValue;
       }
-      pAcc->InsertItem(wsLabel, wsValue, false);
+      pWidgetNode->InsertItem(wsLabel, wsValue, false);
     }
   }
-  m_BindItems.clear();
 }
 
 void CXFA_FFDocView::SetChangeMark() {
diff --git a/xfa/fxfa/cxfa_ffdocview.h b/xfa/fxfa/cxfa_ffdocview.h
index 5c8f017..dbc5319 100644
--- a/xfa/fxfa/cxfa_ffdocview.h
+++ b/xfa/fxfa/cxfa_ffdocview.h
@@ -7,22 +7,26 @@
 #ifndef XFA_FXFA_CXFA_FFDOCVIEW_H_
 #define XFA_FXFA_CXFA_FFDOCVIEW_H_
 
-#include <map>
+#include <deque>
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/fxfa.h"
 
 class CXFA_BindItems;
-class CXFA_FFWidgetHandler;
 class CXFA_FFDoc;
-class CXFA_FFWidget;
+class CXFA_FFWidgetHandler;
+class CXFA_Node;
+class CXFA_ReadyNodeIterator;
 class CXFA_Subform;
-class CXFA_WidgetAccIterator;
+class CXFA_ViewLayoutItem;
 
-extern const XFA_AttributeEnum gs_EventActivity[];
+extern const XFA_AttributeValue gs_EventActivity[];
 enum XFA_DOCVIEW_LAYOUTSTATUS {
   XFA_DOCVIEW_LAYOUTSTATUS_None,
   XFA_DOCVIEW_LAYOUTSTATUS_Start,
@@ -45,74 +49,59 @@
   explicit CXFA_FFDocView(CXFA_FFDoc* pDoc);
   ~CXFA_FFDocView();
 
-  CXFA_FFDoc* GetDoc() { return m_pDoc.Get(); }
-  int32_t StartLayout(int32_t iStartPage = 0);
+  CXFA_FFDoc* GetDoc() const { return m_pDoc.Get(); }
+  int32_t StartLayout();
   int32_t DoLayout();
   void StopLayout();
   int32_t GetLayoutStatus() const { return m_iStatus; }
+
   void UpdateDocView();
+  void UpdateUIDisplay(CXFA_Node* pNode, CXFA_FFWidget* pExcept);
+
   int32_t CountPageViews() const;
   CXFA_FFPageView* GetPageView(int32_t nIndex) const;
 
-  void ResetWidgetAcc(CXFA_WidgetAcc* pWidgetAcc);
-  int32_t ProcessWidgetEvent(CXFA_EventParam* pParam,
-                             CXFA_WidgetAcc* pWidgetAcc);
+  void ResetNode(CXFA_Node* pNode);
   CXFA_FFWidgetHandler* GetWidgetHandler();
-  std::unique_ptr<CXFA_WidgetAccIterator> CreateWidgetAccIterator();
+  std::unique_ptr<CXFA_ReadyNodeIterator> CreateReadyNodeIterator();
   CXFA_FFWidget* GetFocusWidget() const { return m_pFocusWidget.Get(); }
-  void KillFocus();
-  bool SetFocus(CXFA_FFWidget* hWidget);
+  bool SetFocus(CXFA_FFWidget* pNewFocus);
   CXFA_FFWidget* GetWidgetForNode(CXFA_Node* node);
   CXFA_FFWidget* GetWidgetByName(const WideString& wsName,
                                  CXFA_FFWidget* pRefWidget);
-  CXFA_WidgetAcc* GetWidgetAccByName(const WideString& wsName,
-                                     CXFA_WidgetAcc* pRefWidgetAcc);
   CXFA_LayoutProcessor* GetXFALayout() const;
-  void OnPageEvent(CXFA_ContainerLayoutItem* pSender, uint32_t dwEvent);
+  void OnPageEvent(CXFA_ViewLayoutItem* pSender, uint32_t dwEvent);
   void LockUpdate() { m_iLock++; }
   void UnlockUpdate() { m_iLock--; }
-  bool IsUpdateLocked() { return m_iLock > 0; }
-  void ClearInvalidateList() { m_mapPageInvalidate.clear(); }
-  void AddInvalidateRect(CXFA_FFWidget* pWidget, const CFX_RectF& rtInvalidate);
-  void AddInvalidateRect(CXFA_FFPageView* pPageView,
-                         const CFX_RectF& rtInvalidate);
-  void RunInvalidate();
+  void InvalidateRect(CXFA_FFPageView* pPageView,
+                      const CFX_RectF& rtInvalidate);
   void RunDocClose();
-  void DestroyDocView();
 
-  void ProcessValueChanged(CXFA_WidgetAcc* widgetAcc);
-
-  bool InitValidate(CXFA_Node* pNode);
-  bool RunValidate();
-
+  void ProcessValueChanged(CXFA_Node* node);
   void SetChangeMark();
 
-  void AddValidateWidget(CXFA_WidgetAcc* pWidget);
+  void AddValidateNode(CXFA_Node* node);
   void AddCalculateNodeNotify(CXFA_Node* pNodeChange);
-  void AddCalculateWidgetAcc(CXFA_WidgetAcc* pWidgetAcc);
-  int32_t RunCalculateWidgets();
-  bool IsStaticNotify() {
-    return m_pDoc->GetFormType() == FormType::kXFAForeground;
-  }
+  void AddCalculateNode(CXFA_Node* node);
+
   bool RunLayout();
-  void RunSubformIndexChange();
   void AddNewFormNode(CXFA_Node* pNode);
   void AddIndexChangedSubform(CXFA_Node* pNode);
-  CXFA_WidgetAcc* GetFocusWidgetAcc() const { return m_pFocusAcc.Get(); }
-  void SetFocusWidgetAcc(CXFA_WidgetAcc* pWidgetAcc);
+  CXFA_Node* GetFocusNode() const { return m_pFocusNode.Get(); }
+  void SetFocusNode(CXFA_Node* pNode);
   void DeleteLayoutItem(CXFA_FFWidget* pWidget);
-  int32_t ExecEventActivityByDeepFirst(CXFA_Node* pFormNode,
-                                       XFA_EVENTTYPE eEventType,
-                                       bool bIsFormReady,
-                                       bool bRecursive,
-                                       CXFA_Node* pExclude);
+  XFA_EventError ExecEventActivityByDeepFirst(CXFA_Node* pFormNode,
+                                              XFA_EVENTTYPE eEventType,
+                                              bool bIsFormReady,
+                                              bool bRecursive);
 
   void AddBindItem(CXFA_BindItems* item) { m_BindItems.push_back(item); }
 
-  bool m_bLayoutEvent;
+  bool m_bLayoutEvent = false;
+  bool m_bInLayoutStatus = false;
   std::vector<WideString> m_arrNullTestMsg;
-  CXFA_FFWidget* m_pListFocusWidget;
-  bool m_bInLayoutStatus;
+
+  void ResetLayoutProcessor() { m_pXFADocLayout.Release(); }
 
  private:
   bool RunEventLayoutReady();
@@ -121,23 +110,27 @@
   void InitLayout(CXFA_Node* pNode);
   size_t RunCalculateRecursive(size_t index);
   void ShowNullTestMsg();
-  bool ResetSingleWidgetAccData(CXFA_WidgetAcc* pWidgetAcc);
+  bool ResetSingleNodeData(CXFA_Node* pNode);
   CXFA_Subform* GetRootSubform();
 
+  bool IsUpdateLocked() const { return m_iLock > 0; }
+  bool InitValidate(CXFA_Node* pNode);
+  bool RunValidate();
+  XFA_EventError RunCalculateWidgets();
+  void RunSubformIndexChange();
+
   UnownedPtr<CXFA_FFDoc> const m_pDoc;
   std::unique_ptr<CXFA_FFWidgetHandler> m_pWidgetHandler;
-  CXFA_LayoutProcessor* m_pXFADocLayout;  // Not owned.
-  UnownedPtr<CXFA_WidgetAcc> m_pFocusAcc;
-  UnownedPtr<CXFA_FFWidget> m_pFocusWidget;
-  UnownedPtr<CXFA_FFWidget> m_pOldFocusWidget;
-  std::map<CXFA_FFPageView*, std::unique_ptr<CFX_RectF>> m_mapPageInvalidate;
-  std::vector<CXFA_WidgetAcc*> m_ValidateAccs;
-  std::vector<CXFA_WidgetAcc*> m_CalculateAccs;
-  std::vector<CXFA_BindItems*> m_BindItems;
-  std::vector<CXFA_Node*> m_NewAddedNodes;
-  std::vector<CXFA_Node*> m_IndexChangedSubforms;
-  XFA_DOCVIEW_LAYOUTSTATUS m_iStatus;
-  int32_t m_iLock;
+  UnownedPtr<CXFA_LayoutProcessor> m_pXFADocLayout;
+  UnownedPtr<CXFA_Node> m_pFocusNode;
+  ObservedPtr<CXFA_FFWidget> m_pFocusWidget;
+  std::deque<CXFA_Node*> m_ValidateNodes;
+  std::vector<CXFA_Node*> m_CalculateNodes;
+  std::deque<CXFA_BindItems*> m_BindItems;
+  std::deque<CXFA_Node*> m_NewAddedNodes;
+  std::deque<CXFA_Node*> m_IndexChangedSubforms;
+  XFA_DOCVIEW_LAYOUTSTATUS m_iStatus = XFA_DOCVIEW_LAYOUTSTATUS_None;
+  int32_t m_iLock = 0;
 };
 
 #endif  // XFA_FXFA_CXFA_FFDOCVIEW_H_
diff --git a/xfa/fxfa/cxfa_ffdraw.cpp b/xfa/fxfa/cxfa_ffdraw.cpp
deleted file mode 100644
index 420bde1..0000000
--- a/xfa/fxfa/cxfa_ffdraw.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_ffdraw.h"
-
-#include "xfa/fxfa/cxfa_ffapp.h"
-#include "xfa/fxfa/cxfa_ffdoc.h"
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-
-CXFA_FFDraw::CXFA_FFDraw(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
-
-CXFA_FFDraw::~CXFA_FFDraw() {}
diff --git a/xfa/fxfa/cxfa_ffdraw.h b/xfa/fxfa/cxfa_ffdraw.h
deleted file mode 100644
index de9cfcf..0000000
--- a/xfa/fxfa/cxfa_ffdraw.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_FFDRAW_H_
-#define XFA_FXFA_CXFA_FFDRAW_H_
-
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-
-class CXFA_FFDraw : public CXFA_FFWidget {
- public:
-  explicit CXFA_FFDraw(CXFA_Node* pNode);
-  ~CXFA_FFDraw() override;
-};
-
-#endif  // XFA_FXFA_CXFA_FFDRAW_H_
diff --git a/xfa/fxfa/cxfa_ffdropdown.cpp b/xfa/fxfa/cxfa_ffdropdown.cpp
new file mode 100644
index 0000000..4289dc1
--- /dev/null
+++ b/xfa/fxfa/cxfa_ffdropdown.cpp
@@ -0,0 +1,19 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/cxfa_ffdropdown.h"
+
+CXFA_FFDropDown::CXFA_FFDropDown(CXFA_Node* node) : CXFA_FFField(node) {}
+
+CXFA_FFDropDown::~CXFA_FFDropDown() = default;
+
+CXFA_FFComboBox* CXFA_FFDropDown::AsComboBox() {
+  return nullptr;
+}
+
+CXFA_FFDropDown* CXFA_FFDropDown::AsDropDown() {
+  return this;
+}
diff --git a/xfa/fxfa/cxfa_ffdropdown.h b/xfa/fxfa/cxfa_ffdropdown.h
new file mode 100644
index 0000000..b1751b4
--- /dev/null
+++ b/xfa/fxfa/cxfa_ffdropdown.h
@@ -0,0 +1,34 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_CXFA_FFDROPDOWN_H_
+#define XFA_FXFA_CXFA_FFDROPDOWN_H_
+
+#include "core/fxcrt/widestring.h"
+#include "xfa/fxfa/cxfa_fffield.h"
+
+class CXFA_FFComboBox;
+
+class CXFA_FFDropDown : public CXFA_FFField {
+ public:
+  ~CXFA_FFDropDown() override;
+
+  // CXFA_FFField:
+  CXFA_FFDropDown* AsDropDown() override;
+
+  virtual void InsertItem(const WideString& wsLabel, int32_t nIndex) = 0;
+  virtual void DeleteItem(int32_t nIndex) = 0;
+  virtual CXFA_FFComboBox* AsComboBox();
+
+ protected:
+  explicit CXFA_FFDropDown(CXFA_Node* pNode);
+};
+
+inline CXFA_FFComboBox* ToComboBox(CXFA_FFDropDown* pDropDown) {
+  return pDropDown ? pDropDown->AsComboBox() : nullptr;
+}
+
+#endif  // XFA_FXFA_CXFA_FFDROPDOWN_H_
diff --git a/xfa/fxfa/cxfa_ffexclgroup.cpp b/xfa/fxfa/cxfa_ffexclgroup.cpp
index 318752b..dc0b4c7 100644
--- a/xfa/fxfa/cxfa_ffexclgroup.cpp
+++ b/xfa/fxfa/cxfa_ffexclgroup.cpp
@@ -17,12 +17,12 @@
 
 void CXFA_FFExclGroup::RenderWidget(CXFA_Graphics* pGS,
                                     const CFX_Matrix& matrix,
-                                    uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                    HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
 }
diff --git a/xfa/fxfa/cxfa_ffexclgroup.h b/xfa/fxfa/cxfa_ffexclgroup.h
index b904301..a14e071 100644
--- a/xfa/fxfa/cxfa_ffexclgroup.h
+++ b/xfa/fxfa/cxfa_ffexclgroup.h
@@ -10,7 +10,7 @@
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 
-class CXFA_FFExclGroup : public CXFA_FFWidget {
+class CXFA_FFExclGroup final : public CXFA_FFWidget {
  public:
   explicit CXFA_FFExclGroup(CXFA_Node* pNode);
   ~CXFA_FFExclGroup() override;
@@ -18,7 +18,7 @@
   // CXFA_FFWidget
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
 };
 
 #endif  // XFA_FXFA_CXFA_FFEXCLGROUP_H_
diff --git a/xfa/fxfa/cxfa_fffield.cpp b/xfa/fxfa/cxfa_fffield.cpp
index cd4359d..b031089 100644
--- a/xfa/fxfa/cxfa_fffield.cpp
+++ b/xfa/fxfa/cxfa_fffield.cpp
@@ -6,6 +6,11 @@
 
 #include "xfa/fxfa/cxfa_fffield.h"
 
+#include <algorithm>
+#include <utility>
+
+#include "core/fxge/render_defines.h"
+#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_eventmouse.h"
 #include "xfa/fwl/cfwl_messagekey.h"
@@ -15,6 +20,7 @@
 #include "xfa/fwl/cfwl_messagesetfocus.h"
 #include "xfa/fwl/cfwl_picturebox.h"
 #include "xfa/fwl/cfwl_widgetmgr.h"
+#include "xfa/fwl/fwl_widgetdef.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
@@ -31,68 +37,70 @@
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
-namespace {
-
-CXFA_FFField* ToField(CXFA_LayoutItem* widget) {
-  return static_cast<CXFA_FFField*>(widget);
-}
-
-}  // namespace
-
 CXFA_FFField::CXFA_FFField(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
-CXFA_FFField::~CXFA_FFField() {
-  CXFA_FFField::UnloadWidget();
+CXFA_FFField::~CXFA_FFField() = default;
+
+CXFA_FFDropDown* CXFA_FFField::AsDropDown() {
+  return nullptr;
 }
 
-CFX_RectF CXFA_FFField::GetBBox(uint32_t dwStatus, bool bDrawFocus) {
-  if (!bDrawFocus)
-    return CXFA_FFWidget::GetBBox(dwStatus);
+CXFA_FFField* CXFA_FFField::AsField() {
+  return this;
+}
 
-  XFA_Element type = m_pNode->GetWidgetAcc()->GetUIType();
-  if (type != XFA_Element::Button && type != XFA_Element::CheckButton &&
-      type != XFA_Element::ImageEdit && type != XFA_Element::Signature &&
-      type != XFA_Element::ChoiceList) {
-    return CFX_RectF();
+CFX_RectF CXFA_FFField::GetBBox(FocusOption focus) {
+  if (focus == kDoNotDrawFocus)
+    return CXFA_FFWidget::GetBBox(kDoNotDrawFocus);
+
+  switch (m_pNode->GetFFWidgetType()) {
+    case XFA_FFWidgetType::kButton:
+    case XFA_FFWidgetType::kCheckButton:
+    case XFA_FFWidgetType::kImageEdit:
+    case XFA_FFWidgetType::kSignature:
+    case XFA_FFWidgetType::kChoiceList:
+      return GetRotateMatrix().TransformRect(m_rtUI);
+    default:
+      return CFX_RectF();
   }
-
-  return GetRotateMatrix().TransformRect(m_rtUI);
 }
 
 void CXFA_FFField::RenderWidget(CXFA_Graphics* pGS,
                                 const CFX_Matrix& matrix,
-                                uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
-  DrawBorder(pGS, m_pNode->GetWidgetAcc()->GetUIBorder(), m_rtUI, mtRotate);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
   RenderCaption(pGS, &mtRotate);
-  DrawHighlight(pGS, &mtRotate, dwStatus, false);
+  DrawHighlight(pGS, &mtRotate, highlight, kSquareShape);
 
-  CFX_RectF rtWidget = m_pNormalWidget->GetWidgetRect();
+  CFX_RectF rtWidget = GetNormalWidget()->GetWidgetRect();
   CFX_Matrix mt(1, 0, 0, 1, rtWidget.left, rtWidget.top);
   mt.Concat(mtRotate);
-  GetApp()->GetFWLWidgetMgr()->OnDrawWidget(m_pNormalWidget.get(), pGS, mt);
+  GetApp()->GetFWLWidgetMgr()->OnDrawWidget(GetNormalWidget(), pGS, mt);
 }
 
 void CXFA_FFField::DrawHighlight(CXFA_Graphics* pGS,
                                  CFX_Matrix* pMatrix,
-                                 uint32_t dwStatus,
-                                 bool bEllipse) {
-  if (m_rtUI.IsEmpty() || !GetDoc()->GetXFADoc()->IsInteractive())
-    return;
-  if (!(dwStatus & XFA_WidgetStatus_Highlight) || !m_pNode->IsOpenAccess())
+                                 HighlightOption highlight,
+                                 ShapeOption shape) {
+  if (highlight == kNoHighlight)
     return;
 
+  if (m_rtUI.IsEmpty() || !GetDoc()->GetXFADoc()->IsInteractive() ||
+      !m_pNode->IsOpenAccess()) {
+    return;
+  }
   CXFA_FFDoc* pDoc = GetDoc();
   pGS->SetFillColor(
       CXFA_GEColor(pDoc->GetDocEnvironment()->GetHighlightColor(pDoc)));
   CXFA_GEPath path;
-  if (bEllipse)
+  if (shape == kRoundShape)
     path.AddEllipse(m_rtUI);
   else
     path.AddRectangle(m_rtUI.left, m_rtUI.top, m_rtUI.width, m_rtUI.height);
@@ -101,13 +109,13 @@
 }
 
 void CXFA_FFField::DrawFocus(CXFA_Graphics* pGS, CFX_Matrix* pMatrix) {
-  if (!(m_dwStatus & XFA_WidgetStatus_Focused))
+  if (!GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Focused))
     return;
 
   pGS->SetStrokeColor(CXFA_GEColor(0xFF000000));
 
-  float DashPattern[2] = {1, 1};
-  pGS->SetLineDash(0.0f, DashPattern, 2);
+  static constexpr float kDashPattern[2] = {1, 1};
+  pGS->SetLineDash(0.0f, kDashPattern, FX_ArraySize(kDashPattern));
   pGS->SetLineWidth(0);
 
   CXFA_GEPath path;
@@ -116,45 +124,53 @@
 }
 
 void CXFA_FFField::SetFWLThemeProvider() {
-  if (m_pNormalWidget)
-    m_pNormalWidget->SetThemeProvider(GetApp()->GetFWLTheme());
+  if (GetNormalWidget())
+    GetNormalWidget()->SetThemeProvider(GetApp()->GetFWLTheme(GetDoc()));
+}
+
+CFWL_Widget* CXFA_FFField::GetNormalWidget() {
+  return m_pNormalWidget.get();
+}
+
+const CFWL_Widget* CXFA_FFField::GetNormalWidget() const {
+  return m_pNormalWidget.get();
+}
+
+void CXFA_FFField::SetNormalWidget(std::unique_ptr<CFWL_Widget> widget) {
+  m_pNormalWidget = std::move(widget);
 }
 
 bool CXFA_FFField::IsLoaded() {
-  return m_pNormalWidget && CXFA_FFWidget::IsLoaded();
+  return GetNormalWidget() && CXFA_FFWidget::IsLoaded();
 }
 
 bool CXFA_FFField::LoadWidget() {
   SetFWLThemeProvider();
-  m_pNode->GetWidgetAcc()->LoadCaption(GetDoc());
+  m_pNode->LoadCaption(GetDoc());
   PerformLayout();
   return true;
 }
 
-void CXFA_FFField::UnloadWidget() {
-  m_pNormalWidget.reset();
-}
-
 void CXFA_FFField::SetEditScrollOffset() {
-  XFA_Element eType = m_pNode->GetWidgetAcc()->GetUIType();
-  if (eType != XFA_Element::TextEdit && eType != XFA_Element::NumericEdit &&
-      eType != XFA_Element::PasswordEdit) {
+  XFA_FFWidgetType eType = m_pNode->GetFFWidgetType();
+  if (eType != XFA_FFWidgetType::kTextEdit &&
+      eType != XFA_FFWidgetType::kNumericEdit &&
+      eType != XFA_FFWidgetType::kPasswordEdit) {
     return;
   }
 
   float fScrollOffset = 0;
-  CXFA_FFField* pPrev = ToField(GetPrev());
-  if (pPrev) {
-    CFX_RectF rtMargin = m_pNode->GetWidgetAcc()->GetUIMargin();
-    fScrollOffset = -rtMargin.top;
-  }
+  CXFA_ContentLayoutItem* pItem = GetLayoutItem()->GetPrev();
+  CXFA_FFField* pPrev = pItem ? ToField(pItem->GetFFWidget()) : nullptr;
+  if (pPrev)
+    fScrollOffset = -(m_pNode->GetUIMargin().top);
 
   while (pPrev) {
     fScrollOffset += pPrev->m_rtUI.height;
-    pPrev = ToField(pPrev->GetPrev());
+    pItem = pPrev->GetLayoutItem()->GetPrev();
+    pPrev = pItem ? ToField(pItem->GetFFWidget()) : nullptr;
   }
-  static_cast<CFWL_Edit*>(m_pNormalWidget.get())
-      ->SetScrollOffset(fScrollOffset);
+  static_cast<CFWL_Edit*>(GetNormalWidget())->SetScrollOffset(fScrollOffset);
 }
 
 bool CXFA_FFField::PerformLayout() {
@@ -163,8 +179,8 @@
   LayoutCaption();
   SetFWLRect();
   SetEditScrollOffset();
-  if (m_pNormalWidget)
-    m_pNormalWidget->Update();
+  if (GetNormalWidget())
+    GetNormalWidget()->Update();
   return true;
 }
 
@@ -172,7 +188,7 @@
   CFX_RectF rtWidget = GetRectWithoutRotate();
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
   if (margin) {
-    CXFA_LayoutItem* pItem = this;
+    CXFA_ContentLayoutItem* pItem = GetLayoutItem();
     float fLeftInset = margin->GetLeftInset();
     float fRightInset = margin->GetRightInset();
     float fTopInset = margin->GetTopInset();
@@ -189,18 +205,25 @@
     }
   }
 
-  XFA_AttributeEnum iCapPlacement = XFA_AttributeEnum::Unknown;
+  XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown;
   float fCapReserve = 0;
   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
   if (caption && !caption->IsHidden()) {
     iCapPlacement = caption->GetPlacementType();
-    if (iCapPlacement == XFA_AttributeEnum::Top && GetPrev()) {
-      m_rtCaption.Reset();
-    } else if (iCapPlacement == XFA_AttributeEnum::Bottom && GetNext()) {
-      m_rtCaption.Reset();
+    if ((iCapPlacement == XFA_AttributeValue::Top &&
+         GetLayoutItem()->GetPrev()) ||
+        (iCapPlacement == XFA_AttributeValue::Bottom &&
+         GetLayoutItem()->GetNext())) {
+      m_rtCaption = CFX_RectF();
     } else {
       fCapReserve = caption->GetReserve();
-      CXFA_LayoutItem* pItem = this;
+      if (iCapPlacement == XFA_AttributeValue::Top ||
+          iCapPlacement == XFA_AttributeValue::Bottom) {
+        fCapReserve = std::min(fCapReserve, rtWidget.height);
+      } else {
+        fCapReserve = std::min(fCapReserve, rtWidget.width);
+      }
+      CXFA_ContentLayoutItem* pItem = GetLayoutItem();
       if (!pItem->GetPrev() && !pItem->GetNext()) {
         m_rtCaption = rtWidget;
       } else {
@@ -211,17 +234,16 @@
           m_rtCaption.height += pItem->GetRect(false).Height();
           pItem = pItem->GetNext();
         }
-        XFA_RectWithoutMargin(m_rtCaption, margin);
+        XFA_RectWithoutMargin(&m_rtCaption, margin);
       }
 
-      CXFA_TextLayout* pCapTextLayout =
-          m_pNode->GetWidgetAcc()->GetCaptionTextLayout();
+      CXFA_TextLayout* pCapTextLayout = m_pNode->GetCaptionTextLayout();
       if (fCapReserve <= 0 && pCapTextLayout) {
         CFX_SizeF minSize;
         CFX_SizeF maxSize;
         CFX_SizeF size = pCapTextLayout->CalcSize(minSize, maxSize);
-        if (iCapPlacement == XFA_AttributeEnum::Top ||
-            iCapPlacement == XFA_AttributeEnum::Bottom) {
+        if (iCapPlacement == XFA_AttributeValue::Top ||
+            iCapPlacement == XFA_AttributeValue::Bottom) {
           fCapReserve = size.height;
         } else {
           fCapReserve = size.width;
@@ -233,56 +255,55 @@
   m_rtUI = rtWidget;
   CXFA_Margin* capMargin = caption ? caption->GetMarginIfExists() : nullptr;
   switch (iCapPlacement) {
-    case XFA_AttributeEnum::Left: {
+    case XFA_AttributeValue::Left: {
       m_rtCaption.width = fCapReserve;
       CapLeftRightPlacement(capMargin, rtWidget, iCapPlacement);
       m_rtUI.width -= fCapReserve;
       m_rtUI.left += fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Top: {
+    case XFA_AttributeValue::Top: {
       m_rtCaption.height = fCapReserve;
       CapTopBottomPlacement(capMargin, rtWidget, iCapPlacement);
       m_rtUI.top += fCapReserve;
       m_rtUI.height -= fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Right: {
+    case XFA_AttributeValue::Right: {
       m_rtCaption.left = m_rtCaption.right() - fCapReserve;
       m_rtCaption.width = fCapReserve;
       CapLeftRightPlacement(capMargin, rtWidget, iCapPlacement);
       m_rtUI.width -= fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Bottom: {
+    case XFA_AttributeValue::Bottom: {
       m_rtCaption.top = m_rtCaption.bottom() - fCapReserve;
       m_rtCaption.height = fCapReserve;
       CapTopBottomPlacement(capMargin, rtWidget, iCapPlacement);
       m_rtUI.height -= fCapReserve;
       break;
     }
-    case XFA_AttributeEnum::Inline:
+    case XFA_AttributeValue::Inline:
       break;
     default:
       break;
   }
 
-  CXFA_Border* borderUI = m_pNode->GetWidgetAcc()->GetUIBorder();
+  CXFA_Border* borderUI = m_pNode->GetUIBorder();
   if (borderUI) {
     CXFA_Margin* borderMargin = borderUI->GetMarginIfExists();
-    if (borderMargin)
-      XFA_RectWithoutMargin(m_rtUI, borderMargin);
+    XFA_RectWithoutMargin(&m_rtUI, borderMargin);
   }
   m_rtUI.Normalize();
 }
 
 void CXFA_FFField::CapTopBottomPlacement(const CXFA_Margin* margin,
                                          const CFX_RectF& rtWidget,
-                                         XFA_AttributeEnum iCapPlacement) {
-  CFX_RectF rtUIMargin = m_pNode->GetWidgetAcc()->GetUIMargin();
+                                         XFA_AttributeValue iCapPlacement) {
+  CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
   m_rtCaption.left += rtUIMargin.left;
   if (margin) {
-    XFA_RectWithoutMargin(m_rtCaption, margin);
+    XFA_RectWithoutMargin(&m_rtCaption, margin);
     if (m_rtCaption.height < 0)
       m_rtCaption.top += m_rtCaption.height;
   }
@@ -297,19 +318,19 @@
     m_rtCaption.top += rtUIMargin.top + rtUIMargin.height;
   } else if (fHeight > rtWidget.height) {
     m_rtUI.height += fHeight - rtWidget.height;
-    if (iCapPlacement == XFA_AttributeEnum::Bottom)
+    if (iCapPlacement == XFA_AttributeValue::Bottom)
       m_rtCaption.top += fHeight - rtWidget.height;
   }
 }
 
 void CXFA_FFField::CapLeftRightPlacement(const CXFA_Margin* margin,
                                          const CFX_RectF& rtWidget,
-                                         XFA_AttributeEnum iCapPlacement) {
-  CFX_RectF rtUIMargin = m_pNode->GetWidgetAcc()->GetUIMargin();
+                                         XFA_AttributeValue iCapPlacement) {
+  CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
   m_rtCaption.top += rtUIMargin.top;
   m_rtCaption.height -= rtUIMargin.top;
   if (margin) {
-    XFA_RectWithoutMargin(m_rtCaption, margin);
+    XFA_RectWithoutMargin(&m_rtCaption, margin);
     if (m_rtCaption.height < 0)
       m_rtCaption.top += m_rtCaption.height;
   }
@@ -318,7 +339,7 @@
   float fHeight = rtUIMargin.top + rtUIMargin.height;
   if (fWidth > rtWidget.width) {
     m_rtUI.width += fWidth - rtWidget.width;
-    if (iCapPlacement == XFA_AttributeEnum::Right)
+    if (iCapPlacement == XFA_AttributeValue::Right)
       m_rtCaption.left += fWidth - rtWidget.width;
   }
 
@@ -331,244 +352,241 @@
 }
 
 void CXFA_FFField::UpdateFWL() {
-  if (m_pNormalWidget)
-    m_pNormalWidget->Update();
+  if (GetNormalWidget())
+    GetNormalWidget()->Update();
 }
 
 uint32_t CXFA_FFField::UpdateUIProperty() {
-  CXFA_Node* pUiNode = m_pNode->GetWidgetAcc()->GetUIChild();
+  CXFA_Node* pUiNode = m_pNode->GetUIChildNode();
   if (pUiNode && pUiNode->GetElementType() == XFA_Element::DefaultUi)
     return FWL_STYLEEXT_EDT_ReadOnly;
   return 0;
 }
 
 void CXFA_FFField::SetFWLRect() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return;
 
   CFX_RectF rtUi = m_rtUI;
-  if (rtUi.width < 1.0)
-    rtUi.width = 1.0;
+  rtUi.width = std::max(rtUi.width, 1.0f);
   if (!GetDoc()->GetXFADoc()->IsInteractive()) {
     float fFontSize = m_pNode->GetFontSize();
-    if (rtUi.height < fFontSize)
-      rtUi.height = fFontSize;
+    rtUi.height = std::max(rtUi.height, fFontSize);
   }
-  m_pNormalWidget->SetWidgetRect(rtUi);
+  GetNormalWidget()->SetWidgetRect(rtUi);
 }
 
 bool CXFA_FFField::OnMouseEnter() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::Enter;
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::Enter));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnMouseExit() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::Leave;
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::Leave));
+
+  return !!pWatched;
 }
 
 CFX_PointF CXFA_FFField::FWLToClient(const CFX_PointF& point) {
-  return m_pNormalWidget ? point - m_pNormalWidget->GetWidgetRect().TopLeft()
-                         : point;
+  return GetNormalWidget()
+             ? point - GetNormalWidget()->GetWidgetRect().TopLeft()
+             : point;
 }
 
-bool CXFA_FFField::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNormalWidget)
+bool CXFA_FFField::AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                            const CFX_PointF& point,
+                                            FWL_MouseCommand command) {
+  if (!GetNormalWidget())
     return false;
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
     return false;
   if (!PtInActiveRect(point))
     return false;
 
-  SetButtonDown(true);
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::LeftButtonDown;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
   return true;
 }
 
+bool CXFA_FFField::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SetButtonDown(true);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::LeftButtonDown, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
+}
+
 bool CXFA_FFField::OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
   if (!IsButtonDown())
     return false;
 
+  ObservedPtr<CXFA_FFField> pWatched(this);
   SetButtonDown(false);
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::LeftButtonUp;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::LeftButtonUp, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnLButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::LeftButtonDblClk;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::LeftButtonDblClk, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::Move;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::Move, dwFlags, FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnMouseWheel(uint32_t dwFlags,
                                 int16_t zDelta,
                                 const CFX_PointF& point) {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  CFWL_MessageMouseWheel ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  ms.m_delta = CFX_PointF(zDelta, 0);
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouseWheel>(
+      GetNormalWidget(), dwFlags, FWLToClient(point), CFX_PointF(zDelta, 0)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNormalWidget)
-    return false;
-  if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
-    return false;
-  if (!PtInActiveRect(point))
-    return false;
-
+  ObservedPtr<CXFA_FFField> pWatched(this);
   SetButtonDown(true);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::RightButtonDown, dwFlags,
+      FWLToClient(point)));
 
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::RightButtonDown;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
   if (!IsButtonDown())
     return false;
 
+  ObservedPtr<CXFA_FFField> pWatched(this);
   SetButtonDown(false);
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::RightButtonUp;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::RightButtonUp, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::RightButtonDblClk;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::RightButtonDblClk, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnSetFocus(CXFA_FFWidget* pOldWidget) {
-  CXFA_FFWidget::OnSetFocus(pOldWidget);
-  if (!m_pNormalWidget)
+  if (!CXFA_FFWidget::OnSetFocus(pOldWidget))
     return false;
 
-  CFWL_MessageSetFocus ms(nullptr, m_pNormalWidget.get());
-  TranslateFWLMessage(&ms);
-  m_dwStatus |= XFA_WidgetStatus_Focused;
-  AddInvalidateRect();
-  return true;
+  if (!GetNormalWidget())
+    return false;
+
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(
+      pdfium::MakeUnique<CFWL_MessageSetFocus>(nullptr, GetNormalWidget()));
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
+  InvalidateRect();
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnKillFocus(CXFA_FFWidget* pNewWidget) {
-  if (!m_pNormalWidget)
-    return CXFA_FFWidget::OnKillFocus(pNewWidget);
-
-  CFWL_MessageKillFocus ms(nullptr, m_pNormalWidget.get());
-  TranslateFWLMessage(&ms);
-  m_dwStatus &= ~XFA_WidgetStatus_Focused;
-  AddInvalidateRect();
-  CXFA_FFWidget::OnKillFocus(pNewWidget);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
+  if (GetNormalWidget()) {
+    SendMessageToFWLWidget(
+        pdfium::MakeUnique<CFWL_MessageKillFocus>(nullptr, GetNormalWidget()));
+    GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_Focused);
+    InvalidateRect();
+  }
+  return pWatched && pNewWatched &&
+         CXFA_FFWidget::OnKillFocus(pNewWatched.Get());
 }
 
 bool CXFA_FFField::OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) {
-  if (!m_pNormalWidget || !GetDoc()->GetXFADoc()->IsInteractive())
+  if (!GetNormalWidget() || !GetDoc()->GetXFADoc()->IsInteractive())
     return false;
 
-  CFWL_MessageKey ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_KeyCommand::KeyDown;
-  ms.m_dwFlags = dwFlags;
-  ms.m_dwKeyCode = dwKeyCode;
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageKey>(
+      GetNormalWidget(), FWL_KeyCommand::KeyDown, dwFlags, dwKeyCode));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) {
-  if (!m_pNormalWidget || !GetDoc()->GetXFADoc()->IsInteractive())
+  if (!GetNormalWidget() || !GetDoc()->GetXFADoc()->IsInteractive())
     return false;
 
-  CFWL_MessageKey ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_KeyCommand::KeyUp;
-  ms.m_dwFlags = dwFlags;
-  ms.m_dwKeyCode = dwKeyCode;
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageKey>(
+      GetNormalWidget(), FWL_KeyCommand::KeyUp, dwFlags, dwKeyCode));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFField::OnChar(uint32_t dwChar, uint32_t dwFlags) {
   if (!GetDoc()->GetXFADoc()->IsInteractive())
     return false;
-  if (dwChar == FWL_VKEY_Tab)
+  if (dwChar == XFA_FWL_VKEY_Tab)
     return true;
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
   if (!m_pNode->IsOpenAccess())
     return false;
 
-  CFWL_MessageKey ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_KeyCommand::Char;
-  ms.m_dwFlags = dwFlags;
-  ms.m_dwKeyCode = dwChar;
-  TranslateFWLMessage(&ms);
-  return true;
+  ObservedPtr<CXFA_FFField> pWatched(this);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageKey>(
+      GetNormalWidget(), FWL_KeyCommand::Char, dwFlags, dwChar));
+
+  return !!pWatched;
 }
 
-FWL_WidgetHit CXFA_FFField::OnHitTest(const CFX_PointF& point) {
-  if (m_pNormalWidget &&
-      m_pNormalWidget->HitTest(FWLToClient(point)) != FWL_WidgetHit::Unknown) {
+FWL_WidgetHit CXFA_FFField::HitTest(const CFX_PointF& point) {
+  auto* pNorm = GetNormalWidget();
+  if (pNorm && pNorm->HitTest(FWLToClient(point)) != FWL_WidgetHit::Unknown)
     return FWL_WidgetHit::Client;
-  }
-
   if (!GetRectWithoutRotate().Contains(point))
     return FWL_WidgetHit::Unknown;
   if (m_rtCaption.Contains(point))
@@ -581,24 +599,21 @@
 }
 
 bool CXFA_FFField::PtInActiveRect(const CFX_PointF& point) {
-  return m_pNormalWidget && m_pNormalWidget->GetWidgetRect().Contains(point);
+  return GetNormalWidget() &&
+         GetNormalWidget()->GetWidgetRect().Contains(point);
 }
 
 void CXFA_FFField::LayoutCaption() {
-  CXFA_TextLayout* pCapTextLayout =
-      m_pNode->GetWidgetAcc()->GetCaptionTextLayout();
+  CXFA_TextLayout* pCapTextLayout = m_pNode->GetCaptionTextLayout();
   if (!pCapTextLayout)
     return;
 
-  float fHeight =
-      pCapTextLayout->Layout(CFX_SizeF(m_rtCaption.width, m_rtCaption.height));
-  if (m_rtCaption.height < fHeight)
-    m_rtCaption.height = fHeight;
+  float fHeight = pCapTextLayout->Layout(m_rtCaption.Size());
+  m_rtCaption.height = std::max(m_rtCaption.height, fHeight);
 }
 
 void CXFA_FFField::RenderCaption(CXFA_Graphics* pGS, CFX_Matrix* pMatrix) {
-  CXFA_TextLayout* pCapTextLayout =
-      m_pNode->GetWidgetAcc()->GetCaptionTextLayout();
+  CXFA_TextLayout* pCapTextLayout = m_pNode->GetCaptionTextLayout();
   if (!pCapTextLayout)
     return;
 
@@ -607,7 +622,7 @@
     return;
 
   if (!pCapTextLayout->IsLoaded())
-    pCapTextLayout->Layout(CFX_SizeF(m_rtCaption.width, m_rtCaption.height));
+    pCapTextLayout->Layout(m_rtCaption.Size());
 
   CFX_RectF rtClip = m_rtCaption;
   rtClip.Intersect(GetRectWithoutRotate());
@@ -625,95 +640,92 @@
     return false;
   if (!IsDataChanged())
     return false;
-  if (CalculateOverride() != 1)
-    return false;
-  if (!CommitData())
-    return false;
 
   m_pDocView->SetChangeMark();
-  m_pDocView->AddValidateWidget(m_pNode->GetWidgetAcc());
-  return true;
+  m_pDocView->AddValidateNode(m_pNode.Get());
+
+  if (CalculateOverride() != 1)
+    return false;
+  return CommitData();
 }
 
 int32_t CXFA_FFField::CalculateOverride() {
   CXFA_Node* exclNode = m_pNode->GetExclGroupIfExists();
-  if (!exclNode)
-    return CalculateWidgetAcc(m_pNode->GetWidgetAcc());
-
-  CXFA_WidgetAcc* pAcc = exclNode->GetWidgetAcc();
-  if (!pAcc)
-    return CalculateWidgetAcc(m_pNode->GetWidgetAcc());
-  if (CalculateWidgetAcc(pAcc) == 0)
+  if (!exclNode || !exclNode->IsWidgetReady())
+    return CalculateNode(m_pNode.Get());
+  if (CalculateNode(exclNode) == 0)
     return 0;
 
-  CXFA_Node* pNode = pAcc->GetExclGroupFirstMember();
+  CXFA_Node* pNode = exclNode->GetExclGroupFirstMember();
   if (!pNode)
     return 1;
 
-  CXFA_WidgetAcc* pWidgetAcc = nullptr;
   while (pNode) {
-    pWidgetAcc = pNode->GetWidgetAcc();
-    if (!pWidgetAcc)
+    if (!pNode->IsWidgetReady())
       return 1;
-    if (CalculateWidgetAcc(pWidgetAcc) == 0)
+    if (CalculateNode(pNode) == 0)
       return 0;
 
-    pNode = pWidgetAcc->GetExclGroupNextMember(pNode);
+    pNode = pNode->GetExclGroupNextMember(pNode);
   }
   return 1;
 }
 
-int32_t CXFA_FFField::CalculateWidgetAcc(CXFA_WidgetAcc* pAcc) {
-  CXFA_Calculate* calc = pAcc->GetNode()->GetCalculateIfExists();
+int32_t CXFA_FFField::CalculateNode(CXFA_Node* pNode) {
+  CXFA_Calculate* calc = pNode->GetCalculateIfExists();
   if (!calc)
     return 1;
 
   XFA_VERSION version = GetDoc()->GetXFADoc()->GetCurVersionMode();
   switch (calc->GetOverride()) {
-    case XFA_AttributeEnum::Error: {
+    case XFA_AttributeValue::Error: {
       if (version <= XFA_VERSION_204)
         return 1;
 
-      IXFA_AppProvider* pAppProvider = GetApp()->GetAppProvider();
+      IXFA_AppProvider* pAppProvider = GetAppProvider();
       if (pAppProvider) {
-        pAppProvider->MsgBox(L"You are not allowed to modify this field.",
-                             L"Calculate Override", XFA_MBICON_Warning,
-                             XFA_MB_OK);
+        pAppProvider->MsgBox(
+            WideString::FromASCII("You are not allowed to modify this field."),
+            WideString::FromASCII("Calculate Override"),
+            static_cast<uint32_t>(AlertIcon::kWarning),
+            static_cast<uint32_t>(AlertButton::kOK));
       }
       return 0;
     }
-    case XFA_AttributeEnum::Warning: {
+    case XFA_AttributeValue::Warning: {
       if (version <= XFA_VERSION_204) {
         CXFA_Script* script = calc->GetScriptIfExists();
-        if (!script)
-          return 1;
-        if (script->GetExpression().IsEmpty())
+        if (!script || script->GetExpression().IsEmpty())
           return 1;
       }
 
-      if (pAcc->GetNode()->IsUserInteractive())
+      if (pNode->IsUserInteractive())
         return 1;
 
-      IXFA_AppProvider* pAppProvider = GetApp()->GetAppProvider();
+      IXFA_AppProvider* pAppProvider = GetAppProvider();
       if (!pAppProvider)
         return 0;
 
       WideString wsMessage = calc->GetMessageText();
       if (!wsMessage.IsEmpty())
         wsMessage += L"\r\n";
+      wsMessage +=
+          WideString::FromASCII("Are you sure you want to modify this field?");
 
-      wsMessage += L"Are you sure you want to modify this field?";
-      if (pAppProvider->MsgBox(wsMessage, L"Calculate Override",
-                               XFA_MBICON_Warning, XFA_MB_YesNo) == XFA_IDYes) {
-        pAcc->GetNode()->SetFlag(XFA_NodeFlag_UserInteractive, false);
+      if (pAppProvider->MsgBox(wsMessage,
+                               WideString::FromASCII("Calculate Override"),
+                               static_cast<uint32_t>(AlertIcon::kWarning),
+                               static_cast<uint32_t>(AlertButton::kYesNo)) ==
+          static_cast<uint32_t>(AlertReturn::kYes)) {
+        pNode->SetFlag(XFA_NodeFlag_UserInteractive);
         return 1;
       }
       return 0;
     }
-    case XFA_AttributeEnum::Ignore:
+    case XFA_AttributeValue::Ignore:
       return 0;
-    case XFA_AttributeEnum::Disabled:
-      pAcc->GetNode()->SetFlag(XFA_NodeFlag_UserInteractive, false);
+    case XFA_AttributeValue::Disabled:
+      pNode->SetFlag(XFA_NodeFlag_UserInteractive);
       return 1;
     default:
       return 1;
@@ -728,8 +740,9 @@
   return false;
 }
 
-void CXFA_FFField::TranslateFWLMessage(CFWL_Message* pMessage) {
-  GetApp()->GetFWLWidgetMgr()->OnProcessMessageToForm(pMessage);
+void CXFA_FFField::SendMessageToFWLWidget(
+    std::unique_ptr<CFWL_Message> pMessage) {
+  GetApp()->GetFWLWidgetMgr()->OnProcessMessageToForm(std::move(pMessage));
 }
 
 void CXFA_FFField::OnProcessMessage(CFWL_Message* pMessage) {}
@@ -741,26 +754,26 @@
       if (event->m_dwCmd == FWL_MouseCommand::Enter) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseEnter;
-        eParam.m_pTarget = m_pNode->GetWidgetAcc();
-        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::MouseEnter,
+        eParam.m_pTarget = m_pNode.Get();
+        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseEnter,
                               &eParam);
       } else if (event->m_dwCmd == FWL_MouseCommand::Leave) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseExit;
-        eParam.m_pTarget = m_pNode->GetWidgetAcc();
-        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::MouseExit,
+        eParam.m_pTarget = m_pNode.Get();
+        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseExit,
                               &eParam);
       } else if (event->m_dwCmd == FWL_MouseCommand::LeftButtonDown) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseDown;
-        eParam.m_pTarget = m_pNode->GetWidgetAcc();
-        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::MouseDown,
+        eParam.m_pTarget = m_pNode.Get();
+        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseDown,
                               &eParam);
       } else if (event->m_dwCmd == FWL_MouseCommand::LeftButtonUp) {
         CXFA_EventParam eParam;
         eParam.m_eType = XFA_EVENT_MouseUp;
-        eParam.m_pTarget = m_pNode->GetWidgetAcc();
-        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::MouseUp,
+        eParam.m_pTarget = m_pNode.Get();
+        m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::MouseUp,
                               &eParam);
       }
       break;
@@ -768,8 +781,8 @@
     case CFWL_Event::Type::Click: {
       CXFA_EventParam eParam;
       eParam.m_eType = XFA_EVENT_Click;
-      eParam.m_pTarget = m_pNode->GetWidgetAcc();
-      m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Click, &eParam);
+      eParam.m_pTarget = m_pNode.Get();
+      m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click, &eParam);
       break;
     }
     default:
diff --git a/xfa/fxfa/cxfa_fffield.h b/xfa/fxfa/cxfa_fffield.h
index c2ba36c..dd7b914 100644
--- a/xfa/fxfa/cxfa_fffield.h
+++ b/xfa/fxfa/cxfa_fffield.h
@@ -14,23 +14,33 @@
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 
+class CXFA_FFDropDown;
+class CXFA_Node;
+
 #define XFA_MINUI_HEIGHT 4.32f
 #define XFA_DEFAULTUI_HEIGHT 2.0f
 
 class CXFA_FFField : public CXFA_FFWidget, public IFWL_WidgetDelegate {
  public:
+  enum ShapeOption { kSquareShape = 0, kRoundShape };
+
   explicit CXFA_FFField(CXFA_Node* pNode);
   ~CXFA_FFField() override;
 
-  // CXFA_FFWidget
-  CFX_RectF GetBBox(uint32_t dwStatus, bool bDrawFocus = false) override;
+  virtual CXFA_FFDropDown* AsDropDown();
+
+  // CXFA_FFWidget:
+  CXFA_FFField* AsField() override;
+  CFX_RectF GetBBox(FocusOption focus) override;
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
   bool IsLoaded() override;
   bool LoadWidget() override;
-  void UnloadWidget() override;
   bool PerformLayout() override;
+  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                const CFX_PointF& point,
+                                FWL_MouseCommand command) override;
   bool OnMouseEnter() override;
   bool OnMouseExit() override;
   bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
@@ -43,16 +53,15 @@
   bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) override;
-
-  bool OnSetFocus(CXFA_FFWidget* pOldWidget) override;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
+  bool OnSetFocus(CXFA_FFWidget* pOldWidget) override WARN_UNUSED_RESULT;
+  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
   bool OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) override;
   bool OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) override;
   bool OnChar(uint32_t dwChar, uint32_t dwFlags) override;
-  FWL_WidgetHit OnHitTest(const CFX_PointF& point) override;
   bool OnSetCursor(const CFX_PointF& point) override;
+  FWL_WidgetHit HitTest(const CFX_PointF& point) override;
 
-  // IFWL_WidgetDelegate
+  // IFWL_WidgetDelegate:
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
   void OnDrawWidget(CXFA_Graphics* pGraphics,
@@ -66,34 +75,42 @@
 
   virtual void SetFWLRect();
   void SetFWLThemeProvider();
-  CFWL_Widget* GetNormalWidget() { return m_pNormalWidget.get(); }
+  CFWL_Widget* GetNormalWidget();
+  const CFWL_Widget* GetNormalWidget() const;
+  void SetNormalWidget(std::unique_ptr<CFWL_Widget> widget);
   CFX_PointF FWLToClient(const CFX_PointF& point);
   void LayoutCaption();
   void RenderCaption(CXFA_Graphics* pGS, CFX_Matrix* pMatrix);
 
   int32_t CalculateOverride();
-  int32_t CalculateWidgetAcc(CXFA_WidgetAcc* pAcc);
+  int32_t CalculateNode(CXFA_Node* pNode);
   bool ProcessCommittedData();
   virtual bool CommitData();
   virtual bool IsDataChanged();
   void DrawHighlight(CXFA_Graphics* pGS,
                      CFX_Matrix* pMatrix,
-                     uint32_t dwStatus,
-                     bool bEllipse);
+                     HighlightOption highlight,
+                     ShapeOption shape);
   void DrawFocus(CXFA_Graphics* pGS, CFX_Matrix* pMatrix);
-  void TranslateFWLMessage(CFWL_Message* pMessage);
+  void SendMessageToFWLWidget(std::unique_ptr<CFWL_Message> pMessage);
   void CapPlacement();
   void CapTopBottomPlacement(const CXFA_Margin* margin,
                              const CFX_RectF& rtWidget,
-                             XFA_AttributeEnum iCapPlacement);
+                             XFA_AttributeValue iCapPlacement);
   void CapLeftRightPlacement(const CXFA_Margin* margin,
                              const CFX_RectF& rtWidget,
-                             XFA_AttributeEnum iCapPlacement);
+                             XFA_AttributeValue iCapPlacement);
   void SetEditScrollOffset();
 
-  std::unique_ptr<CFWL_Widget> m_pNormalWidget;
   CFX_RectF m_rtUI;
   CFX_RectF m_rtCaption;
+
+ private:
+  std::unique_ptr<CFWL_Widget> m_pNormalWidget;
 };
 
+inline CXFA_FFDropDown* ToDropDown(CXFA_FFField* field) {
+  return field ? field->AsDropDown() : nullptr;
+}
+
 #endif  // XFA_FXFA_CXFA_FFFIELD_H_
diff --git a/xfa/fxfa/cxfa_ffimage.cpp b/xfa/fxfa/cxfa_ffimage.cpp
index b4a0d32..eeb0e24 100644
--- a/xfa/fxfa/cxfa_ffimage.cpp
+++ b/xfa/fxfa/cxfa_ffimage.cpp
@@ -6,75 +6,63 @@
 
 #include "xfa/fxfa/cxfa_ffimage.h"
 
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
-#include "xfa/fxfa/cxfa_ffdraw.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/parser/cxfa_image.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
 
-CXFA_FFImage::CXFA_FFImage(CXFA_Node* pNode) : CXFA_FFDraw(pNode) {}
+CXFA_FFImage::CXFA_FFImage(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
 CXFA_FFImage::~CXFA_FFImage() {
-  CXFA_FFImage::UnloadWidget();
+  GetNode()->SetImageImage(nullptr);
 }
 
 bool CXFA_FFImage::IsLoaded() {
-  return !!GetNode()->GetWidgetAcc()->GetImageImage();
+  return !!GetNode()->GetImageImage();
 }
 
 bool CXFA_FFImage::LoadWidget() {
-  if (GetNode()->GetWidgetAcc()->GetImageImage())
+  if (GetNode()->GetImageImage())
     return true;
 
-  return GetNode()->GetWidgetAcc()->LoadImageImage(GetDoc())
-             ? CXFA_FFDraw::LoadWidget()
-             : false;
-}
-
-void CXFA_FFImage::UnloadWidget() {
-  GetNode()->GetWidgetAcc()->SetImageImage(nullptr);
+  return GetNode()->LoadImageImage(GetDoc()) && CXFA_FFWidget::LoadWidget();
 }
 
 void CXFA_FFImage::RenderWidget(CXFA_Graphics* pGS,
                                 const CFX_Matrix& matrix,
-                                uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
 
-  RetainPtr<CFX_DIBitmap> pDIBitmap =
-      GetNode()->GetWidgetAcc()->GetImageImage();
+  RetainPtr<CFX_DIBitmap> pDIBitmap = GetNode()->GetImageImage();
   if (!pDIBitmap)
     return;
 
   CFX_RectF rtImage = GetRectWithoutRotate();
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    XFA_RectWithoutMargin(rtImage, margin);
+  XFA_RectWithoutMargin(&rtImage, margin);
 
-  XFA_AttributeEnum iHorzAlign = XFA_AttributeEnum::Left;
-  XFA_AttributeEnum iVertAlign = XFA_AttributeEnum::Top;
+  XFA_AttributeValue iHorzAlign = XFA_AttributeValue::Left;
+  XFA_AttributeValue iVertAlign = XFA_AttributeValue::Top;
   CXFA_Para* para = m_pNode->GetParaIfExists();
   if (para) {
     iHorzAlign = para->GetHorizontalAlign();
     iVertAlign = para->GetVerticalAlign();
   }
 
-  int32_t iImageXDpi = 0;
-  int32_t iImageYDpi = 0;
-  m_pNode->GetWidgetAcc()->GetImageDpi(iImageXDpi, iImageYDpi);
-
   auto* value = m_pNode->GetFormValueIfExists();
   CXFA_Image* image = value ? value->GetImageIfExists() : nullptr;
   if (image) {
     XFA_DrawImage(pGS, rtImage, mtRotate, pDIBitmap, image->GetAspect(),
-                  iImageXDpi, iImageYDpi, iHorzAlign, iVertAlign);
+                  m_pNode->GetImageDpi(), iHorzAlign, iVertAlign);
   }
 }
diff --git a/xfa/fxfa/cxfa_ffimage.h b/xfa/fxfa/cxfa_ffimage.h
index c721b11..5d23f60 100644
--- a/xfa/fxfa/cxfa_ffimage.h
+++ b/xfa/fxfa/cxfa_ffimage.h
@@ -7,9 +7,9 @@
 #ifndef XFA_FXFA_CXFA_FFIMAGE_H_
 #define XFA_FXFA_CXFA_FFIMAGE_H_
 
-#include "xfa/fxfa/cxfa_ffdraw.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
 
-class CXFA_FFImage : public CXFA_FFDraw {
+class CXFA_FFImage final : public CXFA_FFWidget {
  public:
   explicit CXFA_FFImage(CXFA_Node* pNode);
   ~CXFA_FFImage() override;
@@ -17,10 +17,9 @@
   // CXFA_FFWidget
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
   bool IsLoaded() override;
   bool LoadWidget() override;
-  void UnloadWidget() override;
 };
 
 #endif  // XFA_FXFA_CXFA_FFIMAGE_H_
diff --git a/xfa/fxfa/cxfa_ffimageedit.cpp b/xfa/fxfa/cxfa_ffimageedit.cpp
index 0e97559..60f584a 100644
--- a/xfa/fxfa/cxfa_ffimageedit.cpp
+++ b/xfa/fxfa/cxfa_ffimageedit.cpp
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
@@ -23,65 +24,57 @@
 #include "xfa/fxfa/parser/cxfa_para.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
 
-CXFA_FFImageEdit::CXFA_FFImageEdit(CXFA_Node* pNode)
-    : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
+CXFA_FFImageEdit::CXFA_FFImageEdit(CXFA_Node* pNode) : CXFA_FFField(pNode) {}
 
 CXFA_FFImageEdit::~CXFA_FFImageEdit() {
-  CXFA_FFImageEdit::UnloadWidget();
+  m_pNode->SetImageEditImage(nullptr);
 }
 
 bool CXFA_FFImageEdit::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNew = pdfium::MakeUnique<CFWL_PictureBox>(GetFWLApp());
   CFWL_PictureBox* pPictureBox = pNew.get();
-  m_pNormalWidget = std::move(pNew);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNew));
+  pPictureBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
+  CFWL_NoteDriver* pNoteDriver = pPictureBox->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pPictureBox, pPictureBox);
   m_pOldDelegate = pPictureBox->GetDelegate();
   pPictureBox->SetDelegate(this);
 
   CXFA_FFField::LoadWidget();
-  if (!m_pNode->GetWidgetAcc()->GetImageEditImage())
+  if (!m_pNode->GetImageEditImage())
     UpdateFWLData();
 
   return true;
 }
 
-void CXFA_FFImageEdit::UnloadWidget() {
-  m_pNode->GetWidgetAcc()->SetImageEditImage(nullptr);
-  CXFA_FFField::UnloadWidget();
-}
-
 void CXFA_FFImageEdit::RenderWidget(CXFA_Graphics* pGS,
                                     const CFX_Matrix& matrix,
-                                    uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                    HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
-  DrawBorder(pGS, m_pNode->GetWidgetAcc()->GetUIBorder(), m_rtUI, mtRotate);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
   RenderCaption(pGS, &mtRotate);
-  RetainPtr<CFX_DIBitmap> pDIBitmap =
-      m_pNode->GetWidgetAcc()->GetImageEditImage();
+  RetainPtr<CFX_DIBitmap> pDIBitmap = m_pNode->GetImageEditImage();
   if (!pDIBitmap)
     return;
 
-  CFX_RectF rtImage = m_pNormalWidget->GetWidgetRect();
-  XFA_AttributeEnum iHorzAlign = XFA_AttributeEnum::Left;
-  XFA_AttributeEnum iVertAlign = XFA_AttributeEnum::Top;
+  CFX_RectF rtImage = GetNormalWidget()->GetWidgetRect();
+  XFA_AttributeValue iHorzAlign = XFA_AttributeValue::Left;
+  XFA_AttributeValue iVertAlign = XFA_AttributeValue::Top;
   CXFA_Para* para = m_pNode->GetParaIfExists();
   if (para) {
     iHorzAlign = para->GetHorizontalAlign();
     iVertAlign = para->GetVerticalAlign();
   }
 
-  XFA_AttributeEnum iAspect = XFA_AttributeEnum::Fit;
+  XFA_AttributeValue iAspect = XFA_AttributeValue::Fit;
   CXFA_Value* value = m_pNode->GetFormValueIfExists();
   if (value) {
     CXFA_Image* image = value->GetImageIfExists();
@@ -89,39 +82,44 @@
       iAspect = image->GetAspect();
   }
 
-  int32_t iImageXDpi = 0;
-  int32_t iImageYDpi = 0;
-  m_pNode->GetWidgetAcc()->GetImageEditDpi(iImageXDpi, iImageYDpi);
-  XFA_DrawImage(pGS, rtImage, mtRotate, pDIBitmap, iAspect, iImageXDpi,
-                iImageYDpi, iHorzAlign, iVertAlign);
+  XFA_DrawImage(pGS, rtImage, mtRotate, pDIBitmap, iAspect,
+                m_pNode->GetImageEditDpi(), iHorzAlign, iVertAlign);
 }
 
-bool CXFA_FFImageEdit::OnLButtonDown(uint32_t dwFlags,
-                                     const CFX_PointF& point) {
+bool CXFA_FFImageEdit::AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                                const CFX_PointF& point,
+                                                FWL_MouseCommand command) {
+  if (command != FWL_MouseCommand::LeftButtonDown)
+    return CXFA_FFField::AcceptsFocusOnButtonDown(dwFlags, point, command);
+
   if (!m_pNode->IsOpenAccess())
     return false;
   if (!PtInActiveRect(point))
     return false;
 
-  SetButtonDown(true);
-
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::LeftButtonDown;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
   return true;
 }
 
+bool CXFA_FFImageEdit::OnLButtonDown(uint32_t dwFlags,
+                                     const CFX_PointF& point) {
+  ObservedPtr<CXFA_FFImageEdit> pWatched(this);
+  SetButtonDown(true);
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::LeftButtonDown, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
+}
+
 void CXFA_FFImageEdit::SetFWLRect() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return;
 
-  CFX_RectF rtUIMargin = m_pNode->GetWidgetAcc()->GetUIMargin();
+  CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
   CFX_RectF rtImage(m_rtUI);
   rtImage.Deflate(rtUIMargin.left, rtUIMargin.top, rtUIMargin.width,
                   rtUIMargin.height);
-  m_pNormalWidget->SetWidgetRect(rtImage);
+  GetNormalWidget()->SetWidgetRect(rtImage);
 }
 
 bool CXFA_FFImageEdit::CommitData() {
@@ -129,8 +127,8 @@
 }
 
 bool CXFA_FFImageEdit::UpdateFWLData() {
-  m_pNode->GetWidgetAcc()->SetImageEditImage(nullptr);
-  m_pNode->GetWidgetAcc()->LoadImageEditImage(GetDoc());
+  m_pNode->SetImageEditImage(nullptr);
+  m_pNode->LoadImageEditImage(GetDoc());
   return true;
 }
 
diff --git a/xfa/fxfa/cxfa_ffimageedit.h b/xfa/fxfa/cxfa_ffimageedit.h
index a7ef6a8..f1233b3 100644
--- a/xfa/fxfa/cxfa_ffimageedit.h
+++ b/xfa/fxfa/cxfa_ffimageedit.h
@@ -7,9 +7,10 @@
 #ifndef XFA_FXFA_CXFA_FFIMAGEEDIT_H_
 #define XFA_FXFA_CXFA_FFIMAGEEDIT_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 
-class CXFA_FFImageEdit : public CXFA_FFField {
+class CXFA_FFImageEdit final : public CXFA_FFField {
  public:
   explicit CXFA_FFImageEdit(CXFA_Node* pNode);
   ~CXFA_FFImageEdit() override;
@@ -17,9 +18,11 @@
   // CXFA_FFField
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
   bool LoadWidget() override;
-  void UnloadWidget() override;
+  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                const CFX_PointF& point,
+                                FWL_MouseCommand command) override;
   bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
@@ -32,7 +35,7 @@
   bool UpdateFWLData() override;
   bool CommitData() override;
 
-  IFWL_WidgetDelegate* m_pOldDelegate;
+  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
 };
 
 #endif  // XFA_FXFA_CXFA_FFIMAGEEDIT_H_
diff --git a/xfa/fxfa/cxfa_ffline.cpp b/xfa/fxfa/cxfa_ffline.cpp
index 4937a0e..ec51040 100644
--- a/xfa/fxfa/cxfa_ffline.cpp
+++ b/xfa/fxfa/cxfa_ffline.cpp
@@ -15,11 +15,11 @@
 
 namespace {
 
-CFX_GraphStateData::LineCap LineCapToFXGE(XFA_AttributeEnum iLineCap) {
+CFX_GraphStateData::LineCap LineCapToFXGE(XFA_AttributeValue iLineCap) {
   switch (iLineCap) {
-    case XFA_AttributeEnum::Round:
+    case XFA_AttributeValue::Round:
       return CFX_GraphStateData::LineCapRound;
-    case XFA_AttributeEnum::Butt:
+    case XFA_AttributeValue::Butt:
       return CFX_GraphStateData::LineCapButt;
     default:
       break;
@@ -29,22 +29,23 @@
 
 }  // namespace
 
-CXFA_FFLine::CXFA_FFLine(CXFA_Node* pNode) : CXFA_FFDraw(pNode) {}
+CXFA_FFLine::CXFA_FFLine(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
 CXFA_FFLine::~CXFA_FFLine() {}
 
 void CXFA_FFLine::GetRectFromHand(CFX_RectF& rect,
-                                  XFA_AttributeEnum iHand,
+                                  XFA_AttributeValue iHand,
                                   float fLineWidth) {
   float fHalfWidth = fLineWidth / 2.0f;
   if (rect.height < 1.0f) {
     switch (iHand) {
-      case XFA_AttributeEnum::Left:
+      case XFA_AttributeValue::Left:
         rect.top -= fHalfWidth;
         break;
-      case XFA_AttributeEnum::Right:
+      case XFA_AttributeValue::Right:
         rect.top += fHalfWidth;
-      case XFA_AttributeEnum::Even:
+        break;
+      case XFA_AttributeValue::Even:
         break;
       default:
         NOTREACHED();
@@ -52,13 +53,13 @@
     }
   } else if (rect.width < 1.0f) {
     switch (iHand) {
-      case XFA_AttributeEnum::Left:
+      case XFA_AttributeValue::Left:
         rect.left += fHalfWidth;
         break;
-      case XFA_AttributeEnum::Right:
+      case XFA_AttributeValue::Right:
         rect.left += fHalfWidth;
         break;
-      case XFA_AttributeEnum::Even:
+      case XFA_AttributeValue::Even:
         break;
       default:
         NOTREACHED();
@@ -66,13 +67,13 @@
     }
   } else {
     switch (iHand) {
-      case XFA_AttributeEnum::Left:
+      case XFA_AttributeValue::Left:
         rect.Inflate(fHalfWidth, fHalfWidth);
         break;
-      case XFA_AttributeEnum::Right:
+      case XFA_AttributeValue::Right:
         rect.Deflate(fHalfWidth, fHalfWidth);
         break;
-      case XFA_AttributeEnum::Even:
+      case XFA_AttributeValue::Even:
         break;
       default:
         NOTREACHED();
@@ -83,8 +84,8 @@
 
 void CXFA_FFLine::RenderWidget(CXFA_Graphics* pGS,
                                const CFX_Matrix& matrix,
-                               uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                               HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CXFA_Value* value = m_pNode->GetFormValueIfExists();
@@ -93,9 +94,8 @@
 
   FX_ARGB lineColor = 0xFF000000;
   float fLineWidth = 1.0f;
-  XFA_AttributeEnum iStrokeType = XFA_AttributeEnum::Unknown;
-  XFA_AttributeEnum iCap = XFA_AttributeEnum::Unknown;
-
+  XFA_AttributeValue iStrokeType = XFA_AttributeValue::Unknown;
+  XFA_AttributeValue iCap = XFA_AttributeValue::Unknown;
   CXFA_Line* line = value->GetLineIfExists();
   if (line) {
     CXFA_Edge* edge = line->GetEdgeIfExists();
@@ -115,10 +115,9 @@
 
   CFX_RectF rtLine = GetRectWithoutRotate();
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    XFA_RectWithoutMargin(rtLine, margin);
+  XFA_RectWithoutMargin(&rtLine, margin);
 
-  GetRectFromHand(rtLine, line ? line->GetHand() : XFA_AttributeEnum::Left,
+  GetRectFromHand(rtLine, line ? line->GetHand() : XFA_AttributeValue::Left,
                   fLineWidth);
   CXFA_GEPath linePath;
   if (line && line->GetSlope() && rtLine.right() > 0.0f &&
diff --git a/xfa/fxfa/cxfa_ffline.h b/xfa/fxfa/cxfa_ffline.h
index 01f3fd3..62750d1 100644
--- a/xfa/fxfa/cxfa_ffline.h
+++ b/xfa/fxfa/cxfa_ffline.h
@@ -7,9 +7,9 @@
 #ifndef XFA_FXFA_CXFA_FFLINE_H_
 #define XFA_FXFA_CXFA_FFLINE_H_
 
-#include "xfa/fxfa/cxfa_ffdraw.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
 
-class CXFA_FFLine : public CXFA_FFDraw {
+class CXFA_FFLine final : public CXFA_FFWidget {
  public:
   explicit CXFA_FFLine(CXFA_Node* pNode);
   ~CXFA_FFLine() override;
@@ -17,11 +17,11 @@
   // CXFA_FFWidget
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
 
  private:
   void GetRectFromHand(CFX_RectF& rect,
-                       XFA_AttributeEnum iHand,
+                       XFA_AttributeValue iHand,
                        float fLineWidth);
 };
 
diff --git a/xfa/fxfa/cxfa_fflistbox.cpp b/xfa/fxfa/cxfa_fflistbox.cpp
index ce9198a..45162ee 100644
--- a/xfa/fxfa/cxfa_fflistbox.cpp
+++ b/xfa/fxfa/cxfa_fflistbox.cpp
@@ -10,6 +10,8 @@
 #include <utility>
 #include <vector>
 
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
 #include "xfa/fwl/cfwl_listbox.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_widget.h"
@@ -24,74 +26,75 @@
 
 }  // namespace
 
-CXFA_FFListBox::CXFA_FFListBox(CXFA_Node* pNode)
-    : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
+CXFA_FFListBox::CXFA_FFListBox(CXFA_Node* pNode) : CXFA_FFDropDown(pNode) {}
 
 CXFA_FFListBox::~CXFA_FFListBox() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return;
 
   CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->UnregisterEventTarget(m_pNormalWidget.get());
+      GetNormalWidget()->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->UnregisterEventTarget(GetNormalWidget());
 }
 
 bool CXFA_FFListBox::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNew = pdfium::MakeUnique<CFWL_ListBox>(
       GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
   CFWL_ListBox* pListBox = pNew.get();
   pListBox->ModifyStyles(FWL_WGTSTYLE_VScroll | FWL_WGTSTYLE_NoBackground,
                          0xFFFFFFFF);
-  m_pNormalWidget = std::move(pNew);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNew));
+  pListBox->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  m_pNormalWidget->LockUpdate();
+  CFWL_NoteDriver* pNoteDriver = pListBox->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pListBox, pListBox);
+  m_pOldDelegate = pListBox->GetDelegate();
+  pListBox->SetDelegate(this);
 
-  for (const auto& label : m_pNode->GetWidgetAcc()->GetChoiceListItems(false))
-    pListBox->AddString(label.AsStringView());
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pListBox);
+    for (const auto& label : m_pNode->GetChoiceListItems(false))
+      pListBox->AddString(label);
 
-  uint32_t dwExtendedStyle = FWL_STYLEEXT_LTB_ShowScrollBarFocus;
-  if (m_pNode->GetWidgetAcc()->IsChoiceListMultiSelect())
-    dwExtendedStyle |= FWL_STYLEEXT_LTB_MultiSelection;
+    uint32_t dwExtendedStyle = FWL_STYLEEXT_LTB_ShowScrollBarFocus;
+    if (m_pNode->IsChoiceListMultiSelect())
+      dwExtendedStyle |= FWL_STYLEEXT_LTB_MultiSelection;
 
-  dwExtendedStyle |= GetAlignment();
-  m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
-  for (int32_t selected : m_pNode->GetWidgetAcc()->GetSelectedItems())
-    pListBox->SetSelItem(pListBox->GetItem(nullptr, selected), true);
+    dwExtendedStyle |= GetAlignment();
+    pListBox->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+    for (int32_t selected : m_pNode->GetSelectedItems())
+      pListBox->SetSelItem(pListBox->GetItem(nullptr, selected), true);
+  }
 
-  m_pNormalWidget->UnlockUpdate();
   return CXFA_FFField::LoadWidget();
 }
 
 bool CXFA_FFListBox::OnKillFocus(CXFA_FFWidget* pNewFocus) {
+  ObservedPtr<CXFA_FFListBox> pWatched(this);
+  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewFocus);
   if (!ProcessCommittedData())
     UpdateFWLData();
 
-  CXFA_FFField::OnKillFocus(pNewFocus);
-  return true;
+  return pWatched && pNewWatched &&
+         CXFA_FFField::OnKillFocus(pNewWatched.Get());
 }
 
 bool CXFA_FFListBox::CommitData() {
-  auto* pListBox = ToListBox(m_pNormalWidget.get());
+  auto* pListBox = ToListBox(GetNormalWidget());
   std::vector<int32_t> iSelArray;
   int32_t iSels = pListBox->CountSelItems();
   for (int32_t i = 0; i < iSels; ++i)
     iSelArray.push_back(pListBox->GetSelIndex(i));
 
-  m_pNode->GetWidgetAcc()->SetSelectedItems(iSelArray, true, false, true);
+  m_pNode->SetSelectedItems(iSelArray, true, false, true);
   return true;
 }
 
 bool CXFA_FFListBox::IsDataChanged() {
-  std::vector<int32_t> iSelArray = m_pNode->GetWidgetAcc()->GetSelectedItems();
+  std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
   int32_t iOldSels = pdfium::CollectionSize<int32_t>(iSelArray);
-  auto* pListBox = ToListBox(m_pNormalWidget.get());
+  auto* pListBox = ToListBox(GetNormalWidget());
   int32_t iSels = pListBox->CountSelItems();
   if (iOldSels != iSels)
     return true;
@@ -111,16 +114,16 @@
 
   uint32_t dwExtendedStyle = 0;
   switch (para->GetHorizontalAlign()) {
-    case XFA_AttributeEnum::Center:
+    case XFA_AttributeValue::Center:
       dwExtendedStyle |= FWL_STYLEEXT_LTB_CenterAlign;
       break;
-    case XFA_AttributeEnum::Justify:
+    case XFA_AttributeValue::Justify:
       break;
-    case XFA_AttributeEnum::JustifyAll:
+    case XFA_AttributeValue::JustifyAll:
       break;
-    case XFA_AttributeEnum::Radix:
+    case XFA_AttributeValue::Radix:
       break;
-    case XFA_AttributeEnum::Right:
+    case XFA_AttributeValue::Right:
       dwExtendedStyle |= FWL_STYLEEXT_LTB_RightAlign;
       break;
     default:
@@ -131,11 +134,11 @@
 }
 
 bool CXFA_FFListBox::UpdateFWLData() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  auto* pListBox = ToListBox(m_pNormalWidget.get());
-  std::vector<int32_t> iSelArray = m_pNode->GetWidgetAcc()->GetSelectedItems();
+  auto* pListBox = ToListBox(GetNormalWidget());
+  std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
   std::vector<CFWL_ListItem*> selItemArray(iSelArray.size());
   std::transform(iSelArray.begin(), iSelArray.end(), selItemArray.begin(),
                  [pListBox](int32_t val) { return pListBox->GetSelItem(val); });
@@ -144,48 +147,40 @@
   for (CFWL_ListItem* pItem : selItemArray)
     pListBox->SetSelItem(pItem, true);
 
-  m_pNormalWidget->Update();
+  GetNormalWidget()->Update();
   return true;
 }
 
 void CXFA_FFListBox::OnSelectChanged(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Change;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  eParam.m_wsPrevText = m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw);
-
-  auto* pListBox = ToListBox(m_pNormalWidget.get());
-  int32_t iSels = pListBox->CountSelItems();
-  if (iSels > 0) {
-    CFWL_ListItem* item = pListBox->GetSelItem(0);
-    eParam.m_wsNewText = item ? item->GetText() : L"";
-  }
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, &eParam);
+  eParam.m_pTarget = m_pNode.Get();
+  eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
 }
 
 void CXFA_FFListBox::SetItemState(int32_t nIndex, bool bSelected) {
-  auto* pListBox = ToListBox(m_pNormalWidget.get());
+  auto* pListBox = ToListBox(GetNormalWidget());
   pListBox->SetSelItem(pListBox->GetSelItem(nIndex), bSelected);
-  m_pNormalWidget->Update();
-  AddInvalidateRect();
+  GetNormalWidget()->Update();
+  InvalidateRect();
 }
 
-void CXFA_FFListBox::InsertItem(const WideStringView& wsLabel, int32_t nIndex) {
-  WideString wsTemp(wsLabel);
-  ToListBox(m_pNormalWidget.get())->AddString(wsTemp.AsStringView());
-  m_pNormalWidget->Update();
-  AddInvalidateRect();
+void CXFA_FFListBox::InsertItem(const WideString& wsLabel, int32_t nIndex) {
+  ToListBox(GetNormalWidget())->AddString(wsLabel);
+  GetNormalWidget()->Update();
+  InvalidateRect();
 }
 
 void CXFA_FFListBox::DeleteItem(int32_t nIndex) {
-  auto* pListBox = ToListBox(m_pNormalWidget.get());
+  auto* pListBox = ToListBox(GetNormalWidget());
   if (nIndex < 0)
     pListBox->DeleteAll();
   else
     pListBox->DeleteString(pListBox->GetItem(nullptr, nIndex));
 
   pListBox->Update();
-  AddInvalidateRect();
+  InvalidateRect();
 }
 
 void CXFA_FFListBox::OnProcessMessage(CFWL_Message* pMessage) {
@@ -196,7 +191,7 @@
   CXFA_FFField::OnProcessEvent(pEvent);
   switch (pEvent->GetType()) {
     case CFWL_Event::Type::SelectChanged:
-      OnSelectChanged(m_pNormalWidget.get());
+      OnSelectChanged(GetNormalWidget());
       break;
     default:
       break;
diff --git a/xfa/fxfa/cxfa_fflistbox.h b/xfa/fxfa/cxfa_fflistbox.h
index 27f1cdb..896ee61 100644
--- a/xfa/fxfa/cxfa_fflistbox.h
+++ b/xfa/fxfa/cxfa_fflistbox.h
@@ -7,26 +7,29 @@
 #ifndef XFA_FXFA_CXFA_FFLISTBOX_H_
 #define XFA_FXFA_CXFA_FFLISTBOX_H_
 
-#include "xfa/fxfa/cxfa_fffield.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/cxfa_ffdropdown.h"
 
-class CXFA_FFListBox : public CXFA_FFField {
+class CXFA_FFListBox final : public CXFA_FFDropDown {
  public:
   explicit CXFA_FFListBox(CXFA_Node* pNode);
   ~CXFA_FFListBox() override;
 
-  // CXFA_FFField
+  // CXFA_FFField:
   bool LoadWidget() override;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
+  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
   void OnDrawWidget(CXFA_Graphics* pGraphics,
                     const CFX_Matrix& matrix) override;
   FormFieldType GetFormFieldType() override;
 
+  // CXFA_FFDropDown
+  void InsertItem(const WideString& wsLabel, int32_t nIndex) override;
+  void DeleteItem(int32_t nIndex) override;
+
   void OnSelectChanged(CFWL_Widget* pWidget);
   void SetItemState(int32_t nIndex, bool bSelected);
-  void InsertItem(const WideStringView& wsLabel, int32_t nIndex);
-  void DeleteItem(int32_t nIndex);
 
  private:
   bool CommitData() override;
@@ -35,7 +38,7 @@
 
   uint32_t GetAlignment();
 
-  IFWL_WidgetDelegate* m_pOldDelegate;
+  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
 };
 
 #endif  // XFA_FXFA_CXFA_FFLISTBOX_H_
diff --git a/xfa/fxfa/cxfa_ffnotify.cpp b/xfa/fxfa/cxfa_ffnotify.cpp
index 3f41620..fc70e6b 100644
--- a/xfa/fxfa/cxfa_ffnotify.cpp
+++ b/xfa/fxfa/cxfa_ffnotify.cpp
@@ -6,9 +6,9 @@
 
 #include "xfa/fxfa/cxfa_ffnotify.h"
 
-#include <memory>
 #include <utility>
 
+#include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffarc.h"
 #include "xfa/fxfa/cxfa_ffbarcode.h"
@@ -17,7 +17,6 @@
 #include "xfa/fxfa/cxfa_ffdatetimeedit.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
-#include "xfa/fxfa/cxfa_ffdraw.h"
 #include "xfa/fxfa/cxfa_ffexclgroup.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 #include "xfa/fxfa/cxfa_ffimage.h"
@@ -30,184 +29,169 @@
 #include "xfa/fxfa/cxfa_ffpushbutton.h"
 #include "xfa/fxfa/cxfa_ffrectangle.h"
 #include "xfa/fxfa/cxfa_ffsignature.h"
-#include "xfa/fxfa/cxfa_ffsubform.h"
 #include "xfa/fxfa/cxfa_fftext.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
 #include "xfa/fxfa/cxfa_textlayout.h"
 #include "xfa/fxfa/cxfa_textprovider.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_barcode.h"
 #include "xfa/fxfa/parser/cxfa_binditems.h"
+#include "xfa/fxfa/parser/cxfa_button.h"
+#include "xfa/fxfa/parser/cxfa_checkbutton.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-
-namespace {
-
-CXFA_FFListBox* ToListBox(CXFA_FFWidget* widget) {
-  return static_cast<CXFA_FFListBox*>(widget);
-}
-
-CXFA_FFComboBox* ToComboBox(CXFA_FFWidget* widget) {
-  return static_cast<CXFA_FFComboBox*>(widget);
-}
-
-}  // namespace
+#include "xfa/fxfa/parser/cxfa_passwordedit.h"
 
 CXFA_FFNotify::CXFA_FFNotify(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {}
 
 CXFA_FFNotify::~CXFA_FFNotify() {}
 
-void CXFA_FFNotify::OnPageEvent(CXFA_ContainerLayoutItem* pSender,
+void CXFA_FFNotify::OnPageEvent(CXFA_ViewLayoutItem* pSender,
                                 uint32_t dwEvent) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pSender->GetLayout());
   if (pDocView)
     pDocView->OnPageEvent(pSender, dwEvent);
 }
 
-void CXFA_FFNotify::OnWidgetListItemAdded(CXFA_WidgetAcc* pSender,
-                                          const wchar_t* pLabel,
-                                          const wchar_t* pValue,
+void CXFA_FFNotify::OnWidgetListItemAdded(CXFA_Node* pSender,
+                                          const WideString& wsLabel,
                                           int32_t iIndex) {
-  if (pSender->GetUIType() != XFA_Element::ChoiceList)
+  if (pSender->GetFFWidgetType() != XFA_FFWidgetType::kChoiceList)
     return;
 
-  CXFA_FFWidget* pWidget =
-      m_pDoc->GetDocView()->GetWidgetForNode(pSender->GetNode());
-  for (; pWidget; pWidget = pSender->GetNextWidget(pWidget)) {
-    if (pWidget->IsLoaded()) {
-      if (pSender->IsListBox())
-        ToListBox(pWidget)->InsertItem(pLabel, iIndex);
-      else
-        ToComboBox(pWidget)->InsertItem(pLabel, iIndex);
-    }
+  CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pSender);
+  for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
+    if (pWidget->IsLoaded())
+      ToDropDown(ToField(pWidget))->InsertItem(wsLabel, iIndex);
   }
 }
 
-void CXFA_FFNotify::OnWidgetListItemRemoved(CXFA_WidgetAcc* pSender,
+void CXFA_FFNotify::OnWidgetListItemRemoved(CXFA_Node* pSender,
                                             int32_t iIndex) {
-  if (pSender->GetUIType() != XFA_Element::ChoiceList)
+  if (pSender->GetFFWidgetType() != XFA_FFWidgetType::kChoiceList)
     return;
 
-  CXFA_FFWidget* pWidget =
-      m_pDoc->GetDocView()->GetWidgetForNode(pSender->GetNode());
-  for (; pWidget; pWidget = pSender->GetNextWidget(pWidget)) {
-    if (pWidget->IsLoaded()) {
-      if (pSender->IsListBox())
-        ToListBox(pWidget)->DeleteItem(iIndex);
-      else
-        ToComboBox(pWidget)->DeleteItem(iIndex);
-    }
+  CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pSender);
+  for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
+    if (pWidget->IsLoaded())
+      ToDropDown(ToField(pWidget))->DeleteItem(iIndex);
   }
 }
 
-CXFA_ContainerLayoutItem* CXFA_FFNotify::OnCreateContainerLayoutItem(
+std::unique_ptr<CXFA_FFPageView> CXFA_FFNotify::OnCreateViewLayoutItem(
     CXFA_Node* pNode) {
-  XFA_Element type = pNode->GetElementType();
-  ASSERT(type == XFA_Element::ContentArea || type == XFA_Element::PageArea);
+  if (pNode->GetElementType() != XFA_Element::PageArea)
+    return nullptr;
 
-  if (type == XFA_Element::PageArea) {
-    CXFA_LayoutProcessor* pLayout = m_pDoc->GetXFADoc()->GetDocLayout();
-    return new CXFA_FFPageView(m_pDoc->GetDocView(pLayout), pNode);
-  }
-  return new CXFA_ContainerLayoutItem(pNode);
+  auto* pLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
+  return pdfium::MakeUnique<CXFA_FFPageView>(m_pDoc->GetDocView(pLayout),
+                                             pNode);
 }
 
-CXFA_ContentLayoutItem* CXFA_FFNotify::OnCreateContentLayoutItem(
+std::unique_ptr<CXFA_FFWidget> CXFA_FFNotify::OnCreateContentLayoutItem(
     CXFA_Node* pNode) {
   ASSERT(pNode->GetElementType() != XFA_Element::ContentArea);
   ASSERT(pNode->GetElementType() != XFA_Element::PageArea);
 
   // We only need to create the widget for certain types of objects.
   if (!pNode->HasCreatedUIWidget())
-    return new CXFA_ContentLayoutItem(pNode);
+    return nullptr;
 
-  CXFA_FFWidget* pWidget;
-  switch (pNode->GetWidgetAcc()->GetUIType()) {
-    case XFA_Element::Barcode:
-      pWidget = new CXFA_FFBarcode(pNode);
+  std::unique_ptr<CXFA_FFWidget> pWidget;
+  switch (pNode->GetFFWidgetType()) {
+    case XFA_FFWidgetType::kBarcode: {
+      CXFA_Node* child = pNode->GetUIChildNode();
+      if (child->GetElementType() != XFA_Element::Barcode)
+        return nullptr;
+
+      pWidget = pdfium::MakeUnique<CXFA_FFBarcode>(
+          pNode, static_cast<CXFA_Barcode*>(child));
       break;
-    case XFA_Element::Button:
-      pWidget = new CXFA_FFPushButton(pNode);
+    }
+    case XFA_FFWidgetType::kButton: {
+      CXFA_Node* child = pNode->GetUIChildNode();
+      if (child->GetElementType() != XFA_Element::Button)
+        return nullptr;
+
+      pWidget = pdfium::MakeUnique<CXFA_FFPushButton>(
+          pNode, static_cast<CXFA_Button*>(child));
       break;
-    case XFA_Element::CheckButton:
-      pWidget = new CXFA_FFCheckButton(pNode);
+    }
+    case XFA_FFWidgetType::kCheckButton: {
+      CXFA_Node* child = pNode->GetUIChildNode();
+      if (child->GetElementType() != XFA_Element::CheckButton)
+        return nullptr;
+
+      pWidget = pdfium::MakeUnique<CXFA_FFCheckButton>(
+          pNode, static_cast<CXFA_CheckButton*>(child));
       break;
-    case XFA_Element::ChoiceList: {
-      if (pNode->GetWidgetAcc()->IsListBox())
-        pWidget = new CXFA_FFListBox(pNode);
+    }
+    case XFA_FFWidgetType::kChoiceList: {
+      if (pNode->IsListBox())
+        pWidget = pdfium::MakeUnique<CXFA_FFListBox>(pNode);
       else
-        pWidget = new CXFA_FFComboBox(pNode);
-    } break;
-    case XFA_Element::DateTimeEdit:
-      pWidget = new CXFA_FFDateTimeEdit(pNode);
+        pWidget = pdfium::MakeUnique<CXFA_FFComboBox>(pNode);
       break;
-    case XFA_Element::ImageEdit:
-      pWidget = new CXFA_FFImageEdit(pNode);
+    }
+    case XFA_FFWidgetType::kDateTimeEdit:
+      pWidget = pdfium::MakeUnique<CXFA_FFDateTimeEdit>(pNode);
       break;
-    case XFA_Element::NumericEdit:
-      pWidget = new CXFA_FFNumericEdit(pNode);
+    case XFA_FFWidgetType::kImageEdit:
+      pWidget = pdfium::MakeUnique<CXFA_FFImageEdit>(pNode);
       break;
-    case XFA_Element::PasswordEdit:
-      pWidget = new CXFA_FFPasswordEdit(pNode);
+    case XFA_FFWidgetType::kNumericEdit:
+      pWidget = pdfium::MakeUnique<CXFA_FFNumericEdit>(pNode);
       break;
-    case XFA_Element::Signature:
-      pWidget = new CXFA_FFSignature(pNode);
-      break;
-    case XFA_Element::TextEdit:
-      pWidget = new CXFA_FFTextEdit(pNode);
-      break;
-    case XFA_Element::Arc:
-      pWidget = new CXFA_FFArc(pNode);
-      break;
-    case XFA_Element::Line:
-      pWidget = new CXFA_FFLine(pNode);
-      break;
-    case XFA_Element::Rectangle:
-      pWidget = new CXFA_FFRectangle(pNode);
-      break;
-    case XFA_Element::Text:
-      pWidget = new CXFA_FFText(pNode);
-      break;
-    case XFA_Element::Image:
-      pWidget = new CXFA_FFImage(pNode);
-      break;
-    case XFA_Element::Draw:
-      pWidget = new CXFA_FFDraw(pNode);
-      break;
-    case XFA_Element::Subform:
-      pWidget = new CXFA_FFSubForm(pNode);
-      break;
-    case XFA_Element::ExclGroup:
-      pWidget = new CXFA_FFExclGroup(pNode);
-      break;
-    case XFA_Element::DefaultUi:
-    default:
-      pWidget = nullptr;
-      break;
-  }
+    case XFA_FFWidgetType::kPasswordEdit: {
+      CXFA_Node* child = pNode->GetUIChildNode();
+      if (child->GetElementType() != XFA_Element::PasswordEdit)
+        return nullptr;
 
-  if (pWidget) {
-    CXFA_LayoutProcessor* pLayout = m_pDoc->GetXFADoc()->GetDocLayout();
-    pWidget->SetDocView(m_pDoc->GetDocView(pLayout));
+      pWidget = pdfium::MakeUnique<CXFA_FFPasswordEdit>(
+          pNode, static_cast<CXFA_PasswordEdit*>(child));
+      break;
+    }
+    case XFA_FFWidgetType::kSignature:
+      pWidget = pdfium::MakeUnique<CXFA_FFSignature>(pNode);
+      break;
+    case XFA_FFWidgetType::kTextEdit:
+      pWidget = pdfium::MakeUnique<CXFA_FFTextEdit>(pNode);
+      break;
+    case XFA_FFWidgetType::kArc:
+      pWidget = pdfium::MakeUnique<CXFA_FFArc>(pNode);
+      break;
+    case XFA_FFWidgetType::kLine:
+      pWidget = pdfium::MakeUnique<CXFA_FFLine>(pNode);
+      break;
+    case XFA_FFWidgetType::kRectangle:
+      pWidget = pdfium::MakeUnique<CXFA_FFRectangle>(pNode);
+      break;
+    case XFA_FFWidgetType::kText:
+      pWidget = pdfium::MakeUnique<CXFA_FFText>(pNode);
+      break;
+    case XFA_FFWidgetType::kImage:
+      pWidget = pdfium::MakeUnique<CXFA_FFImage>(pNode);
+      break;
+    case XFA_FFWidgetType::kSubform:
+      pWidget = pdfium::MakeUnique<CXFA_FFWidget>(pNode);
+      break;
+    case XFA_FFWidgetType::kExclGroup:
+      pWidget = pdfium::MakeUnique<CXFA_FFExclGroup>(pNode);
+      break;
+    case XFA_FFWidgetType::kNone:
+      return nullptr;
   }
+  ASSERT(pWidget);
+  auto* pLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
+  pWidget->SetDocView(m_pDoc->GetDocView(pLayout));
   return pWidget;
 }
 
 void CXFA_FFNotify::StartFieldDrawLayout(CXFA_Node* pItem,
-                                         float& fCalcWidth,
-                                         float& fCalcHeight) {
-  CXFA_WidgetAcc* pAcc = pItem->GetWidgetAcc();
-  if (!pAcc)
-    return;
-
-  pAcc->StartWidgetLayout(m_pDoc.Get(), fCalcWidth, fCalcHeight);
-}
-
-bool CXFA_FFNotify::FindSplitPos(CXFA_Node* pItem,
-                                 int32_t iBlockIndex,
-                                 float& fCalcHeightPos) {
-  CXFA_WidgetAcc* pAcc = pItem->GetWidgetAcc();
-  return pAcc &&
-         pAcc->FindSplitPos(m_pDoc->GetDocView(), iBlockIndex, fCalcHeightPos);
+                                         float* pCalcWidth,
+                                         float* pCalcHeight) {
+  pItem->StartWidgetLayout(m_pDoc.Get(), pCalcWidth, pCalcHeight);
 }
 
 bool CXFA_FFNotify::RunScript(CXFA_Script* script, CXFA_Node* item) {
@@ -218,23 +202,21 @@
   CXFA_EventParam EventParam;
   EventParam.m_eType = XFA_EVENT_Unknown;
 
-  int32_t iRet;
+  XFA_EventError iRet;
   bool bRet;
   std::tie(iRet, bRet) = item->ExecuteBoolScript(pDocView, script, &EventParam);
-  return iRet == XFA_EVENTERROR_Success && bRet;
+  return iRet == XFA_EventError::kSuccess && bRet;
 }
 
-int32_t CXFA_FFNotify::ExecEventByDeepFirst(CXFA_Node* pFormNode,
-                                            XFA_EVENTTYPE eEventType,
-                                            bool bIsFormReady,
-                                            bool bRecursive,
-                                            CXFA_WidgetAcc* pExclude) {
+XFA_EventError CXFA_FFNotify::ExecEventByDeepFirst(CXFA_Node* pFormNode,
+                                                   XFA_EVENTTYPE eEventType,
+                                                   bool bIsFormReady,
+                                                   bool bRecursive) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
   if (!pDocView)
-    return XFA_EVENTERROR_NotExist;
-  return pDocView->ExecEventActivityByDeepFirst(
-      pFormNode, eEventType, bIsFormReady, bRecursive,
-      pExclude ? pExclude->GetNode() : nullptr);
+    return XFA_EventError::kNotExist;
+  return pDocView->ExecEventActivityByDeepFirst(pFormNode, eEventType,
+                                                bIsFormReady, bRecursive);
 }
 
 void CXFA_FFNotify::AddCalcValidate(CXFA_Node* pNode) {
@@ -242,20 +224,8 @@
   if (!pDocView)
     return;
 
-  CXFA_WidgetAcc* pWidgetAcc = pNode->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return;
-
-  pDocView->AddCalculateWidgetAcc(pWidgetAcc);
-  pDocView->AddValidateWidget(pWidgetAcc);
-}
-
-CXFA_FFDoc* CXFA_FFNotify::GetHDOC() {
-  return m_pDoc.Get();
-}
-
-IXFA_DocEnvironment* CXFA_FFNotify::GetDocEnvironment() const {
-  return m_pDoc->GetDocEnvironment();
+  pDocView->AddCalculateNode(pNode);
+  pDocView->AddValidateNode(pNode);
 }
 
 IXFA_AppProvider* CXFA_FFNotify::GetAppProvider() {
@@ -267,36 +237,47 @@
   return pDocView ? pDocView->GetWidgetHandler() : nullptr;
 }
 
-CXFA_FFWidget* CXFA_FFNotify::GetHWidget(CXFA_LayoutItem* pLayoutItem) {
-  return XFA_GetWidgetFromLayoutItem(pLayoutItem);
-}
+void CXFA_FFNotify::OpenDropDownList(CXFA_Node* pNode) {
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
+  CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
+  if (!pLayoutItem)
+    return;
 
-void CXFA_FFNotify::OpenDropDownList(CXFA_FFWidget* hWidget) {
-  if (hWidget->GetNode()->GetWidgetAcc()->GetUIType() !=
-      XFA_Element::ChoiceList)
+  CXFA_FFWidget* hWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem);
+  if (!hWidget)
+    return;
+
+  // SetFocusWidget() may destroy |hWidget| object by JS callback.
+  ObservedPtr<CXFA_FFWidget> pObservedWidget(hWidget);
+  CXFA_FFDoc* hDoc = GetHDOC();
+  hDoc->GetDocEnvironment()->SetFocusWidget(hDoc, hWidget);
+  if (!pObservedWidget)
+    return;
+
+  if (hWidget->GetNode()->GetFFWidgetType() != XFA_FFWidgetType::kChoiceList)
+    return;
+
+  if (!hWidget->IsLoaded())
+    return;
+
+  CXFA_FFDropDown* pDropDown = ToDropDown(ToField(hWidget));
+  CXFA_FFComboBox* pComboBox = ToComboBox(pDropDown);
+  if (!pComboBox)
     return;
 
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
   pDocView->LockUpdate();
-  ToComboBox(hWidget)->OpenDropDownList();
+  pComboBox->OpenDropDownList();
   pDocView->UnlockUpdate();
   pDocView->UpdateDocView();
 }
 
-WideString CXFA_FFNotify::GetCurrentDateTime() {
-  CFX_DateTime dataTime = CFX_DateTime::Now();
-  return WideString::Format(L"%d%02d%02dT%02d%02d%02d", dataTime.GetYear(),
-                            dataTime.GetMonth(), dataTime.GetDay(),
-                            dataTime.GetHour(), dataTime.GetMinute(),
-                            dataTime.GetSecond());
-}
-
-void CXFA_FFNotify::ResetData(CXFA_WidgetAcc* pWidgetAcc) {
+void CXFA_FFNotify::ResetData(CXFA_Node* pNode) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
   if (!pDocView)
     return;
 
-  pDocView->ResetWidgetAcc(pWidgetAcc);
+  pDocView->ResetNode(pNode);
 }
 
 int32_t CXFA_FFNotify::GetLayoutStatus() {
@@ -322,20 +303,14 @@
 
 CXFA_Node* CXFA_FFNotify::GetFocusWidgetNode() {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
-  if (!pDocView)
-    return nullptr;
-
-  CXFA_WidgetAcc* pAcc = pDocView->GetFocusWidgetAcc();
-  return pAcc ? pAcc->GetNode() : nullptr;
+  return pDocView ? pDocView->GetFocusNode() : nullptr;
 }
 
 void CXFA_FFNotify::SetFocusWidgetNode(CXFA_Node* pNode) {
   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
   if (!pDocView)
     return;
-
-  CXFA_WidgetAcc* pAcc = pNode ? pNode->GetWidgetAcc() : nullptr;
-  pDocView->SetFocusWidgetAcc(pAcc);
+  pDocView->SetFocusNode(pNode);
 }
 
 void CXFA_FFNotify::OnNodeReady(CXFA_Node* pNode) {
@@ -344,7 +319,7 @@
     return;
 
   if (pNode->HasCreatedUIWidget()) {
-    pNode->CreateWidgetAcc();
+    pNode->SetWidgetReady();
     return;
   }
 
@@ -353,7 +328,7 @@
       pDocView->AddBindItem(static_cast<CXFA_BindItems*>(pNode));
       break;
     case XFA_Element::Validate:
-      pNode->SetFlag(XFA_NodeFlag_NeedsInitApp, false);
+      pNode->SetFlag(XFA_NodeFlag_NeedsInitApp);
       break;
     default:
       break;
@@ -374,15 +349,10 @@
   if (pDocView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End)
     return;
 
-  CXFA_WidgetAcc* pWidgetAcc = pSender->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return;
-
-  CXFA_FFWidget* pWidget =
-      m_pDoc->GetDocView()->GetWidgetForNode(pWidgetAcc->GetNode());
-  for (; pWidget; pWidget = pWidgetAcc->GetNextWidget(pWidget)) {
+  CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pSender);
+  for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
     if (pWidget->IsLoaded())
-      pWidget->AddInvalidateRect();
+      pWidget->InvalidateRect();
   }
 }
 
@@ -402,15 +372,11 @@
 
   XFA_Element eType = pParentNode->GetElementType();
   bool bIsContainerNode = pParentNode->IsContainerNode();
-  CXFA_WidgetAcc* pWidgetAcc = pWidgetNode->GetWidgetAcc();
-  if (!pWidgetAcc)
-    return;
-
   bool bUpdateProperty = false;
   pDocView->SetChangeMark();
   switch (eType) {
     case XFA_Element::Caption: {
-      CXFA_TextLayout* pCapOut = pWidgetAcc->GetCaptionTextLayout();
+      CXFA_TextLayout* pCapOut = pWidgetNode->GetCaptionTextLayout();
       if (!pCapOut)
         return;
 
@@ -431,30 +397,33 @@
     pDocView->AddCalculateNodeNotify(pSender);
     if (eType == XFA_Element::Value || bIsContainerNode) {
       if (bIsContainerNode) {
-        pWidgetAcc->UpdateUIDisplay(m_pDoc->GetDocView(), nullptr);
-        pDocView->AddCalculateWidgetAcc(pWidgetAcc);
-        pDocView->AddValidateWidget(pWidgetAcc);
+        m_pDoc->GetDocView()->UpdateUIDisplay(pWidgetNode, nullptr);
+        pDocView->AddCalculateNode(pWidgetNode);
+        pDocView->AddValidateNode(pWidgetNode);
       } else if (pWidgetNode->GetParent()->GetElementType() ==
                  XFA_Element::ExclGroup) {
-        pWidgetAcc->UpdateUIDisplay(m_pDoc->GetDocView(), nullptr);
+        m_pDoc->GetDocView()->UpdateUIDisplay(pWidgetNode, nullptr);
       }
       return;
     }
   }
 
-  CXFA_FFWidget* pWidget =
-      m_pDoc->GetDocView()->GetWidgetForNode(pWidgetAcc->GetNode());
-  for (; pWidget; pWidget = pWidgetAcc->GetNextWidget(pWidget)) {
+  CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pWidgetNode);
+  for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
     if (!pWidget->IsLoaded())
       continue;
 
     if (bUpdateProperty)
       pWidget->UpdateWidgetProperty();
     pWidget->PerformLayout();
-    pWidget->AddInvalidateRect();
+    pWidget->InvalidateRect();
   }
 }
 
+void CXFA_FFNotify::OnContainerChanged(CXFA_Node* pNode) {
+  m_pDoc->GetXFADoc()->GetLayoutProcessor()->AddChangedContainer(pNode);
+}
+
 void CXFA_FFNotify::OnChildAdded(CXFA_Node* pSender) {
   if (!pSender->IsFormContainer())
     return;
@@ -503,8 +472,7 @@
       (dwStatus & (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) ==
           (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) {
     pWidget->SetPageView(pNewPageView);
-    m_pDoc->GetDocEnvironment()->WidgetPostAdd(
-        pWidget, pWidget->GetNode()->GetWidgetAcc());
+    m_pDoc->GetDocEnvironment()->WidgetPostAdd(pWidget);
   }
   if (pDocView->GetLayoutStatus() != XFA_DOCVIEW_LAYOUTSTATUS_End ||
       !(dwStatus & XFA_WidgetStatus_Visible)) {
@@ -516,7 +484,7 @@
   } else {
     pWidget->LoadWidget();
   }
-  pWidget->AddInvalidateRect();
+  pWidget->InvalidateRect();
 }
 
 void CXFA_FFNotify::OnLayoutItemRemoving(CXFA_LayoutProcessor* pLayout,
@@ -530,7 +498,6 @@
     return;
 
   pDocView->DeleteLayoutItem(pWidget);
-  m_pDoc->GetDocEnvironment()->WidgetPreRemove(
-      pWidget, pWidget->GetNode()->GetWidgetAcc());
-  pWidget->AddInvalidateRect();
+  m_pDoc->GetDocEnvironment()->WidgetPreRemove(pWidget);
+  pWidget->InvalidateRect();
 }
diff --git a/xfa/fxfa/cxfa_ffnotify.h b/xfa/fxfa/cxfa_ffnotify.h
index 924a060..52fae1d 100644
--- a/xfa/fxfa/cxfa_ffnotify.h
+++ b/xfa/fxfa/cxfa_ffnotify.h
@@ -7,25 +7,30 @@
 #ifndef XFA_FXFA_CXFA_FFNOTIFY_H_
 #define XFA_FXFA_CXFA_FFNOTIFY_H_
 
+#include <memory>
+
 #include "xfa/fxfa/cxfa_eventparam.h"
+#include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 
-class CXFA_FFWidgetHandler;
-class CXFA_ContainerLayoutItem;
 class CXFA_ContentLayoutItem;
+class CXFA_FFWidgetHandler;
+class CXFA_LayoutItem;
+class CXFA_LayoutProcessor;
+class CXFA_Script;
+class CXFA_ViewLayoutItem;
 
 class CXFA_FFNotify {
  public:
   explicit CXFA_FFNotify(CXFA_FFDoc* pDoc);
   ~CXFA_FFNotify();
 
-  void OnPageEvent(CXFA_ContainerLayoutItem* pSender, uint32_t dwEvent);
+  void OnPageEvent(CXFA_ViewLayoutItem* pSender, uint32_t dwEvent);
 
-  void OnWidgetListItemAdded(CXFA_WidgetAcc* pSender,
-                             const wchar_t* pLabel,
-                             const wchar_t* pValue,
+  void OnWidgetListItemAdded(CXFA_Node* pSender,
+                             const WideString& wsLabel,
                              int32_t iIndex);
-  void OnWidgetListItemRemoved(CXFA_WidgetAcc* pSender, int32_t iIndex);
+  void OnWidgetListItemRemoved(CXFA_Node* pSender, int32_t iIndex);
 
   // Node events
   void OnNodeReady(CXFA_Node* pNode);
@@ -34,11 +39,12 @@
                       XFA_Attribute eAttr,
                       CXFA_Node* pParentNode,
                       CXFA_Node* pWidgetNode);
+  void OnContainerChanged(CXFA_Node* pNode);
   void OnChildAdded(CXFA_Node* pSender);
   void OnChildRemoved();
 
-  CXFA_ContainerLayoutItem* OnCreateContainerLayoutItem(CXFA_Node* pNode);
-  CXFA_ContentLayoutItem* OnCreateContentLayoutItem(CXFA_Node* pNode);
+  std::unique_ptr<CXFA_FFPageView> OnCreateViewLayoutItem(CXFA_Node* pNode);
+  std::unique_ptr<CXFA_FFWidget> OnCreateContentLayoutItem(CXFA_Node* pNode);
 
   void OnLayoutItemAdded(CXFA_LayoutProcessor* pLayout,
                          CXFA_LayoutItem* pSender,
@@ -48,26 +54,19 @@
                             CXFA_LayoutItem* pSender);
 
   void StartFieldDrawLayout(CXFA_Node* pItem,
-                            float& fCalcWidth,
-                            float& fCalcHeight);
-  bool FindSplitPos(CXFA_Node* pItem,
-                    int32_t iBlockIndex,
-                    float& fCalcHeightPos);
+                            float* pCalcWidth,
+                            float* pCalcHeight);
   bool RunScript(CXFA_Script* pScript, CXFA_Node* pFormItem);
-  int32_t ExecEventByDeepFirst(CXFA_Node* pFormNode,
-                               XFA_EVENTTYPE eEventType,
-                               bool bIsFormReady = false,
-                               bool bRecursive = true,
-                               CXFA_WidgetAcc* pExclude = nullptr);
+  XFA_EventError ExecEventByDeepFirst(CXFA_Node* pFormNode,
+                                      XFA_EVENTTYPE eEventType,
+                                      bool bIsFormReady,
+                                      bool bRecursive);
   void AddCalcValidate(CXFA_Node* pNode);
-  CXFA_FFDoc* GetHDOC();
-  IXFA_DocEnvironment* GetDocEnvironment() const;
+  CXFA_FFDoc* GetHDOC() const { return m_pDoc.Get(); }
   IXFA_AppProvider* GetAppProvider();
   CXFA_FFWidgetHandler* GetWidgetHandler();
-  CXFA_FFWidget* GetHWidget(CXFA_LayoutItem* pLayoutItem);
-  void OpenDropDownList(CXFA_FFWidget* hWidget);
-  WideString GetCurrentDateTime();
-  void ResetData(CXFA_WidgetAcc* pWidgetAcc = nullptr);
+  void OpenDropDownList(CXFA_Node* pNode);
+  void ResetData(CXFA_Node* pNode);
   int32_t GetLayoutStatus();
   void RunNodeInitialize(CXFA_Node* pNode);
   void RunSubformIndexChange(CXFA_Node* pSubformNode);
diff --git a/xfa/fxfa/cxfa_ffnumericedit.cpp b/xfa/fxfa/cxfa_ffnumericedit.cpp
index b437bc2..8d1b3bb 100644
--- a/xfa/fxfa/cxfa_ffnumericedit.cpp
+++ b/xfa/fxfa/cxfa_ffnumericedit.cpp
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_eventvalidate.h"
 #include "xfa/fwl/cfwl_notedriver.h"
@@ -19,31 +20,32 @@
 CXFA_FFNumericEdit::CXFA_FFNumericEdit(CXFA_Node* pNode)
     : CXFA_FFTextEdit(pNode) {}
 
-CXFA_FFNumericEdit::~CXFA_FFNumericEdit() {}
+CXFA_FFNumericEdit::~CXFA_FFNumericEdit() = default;
 
 bool CXFA_FFNumericEdit::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNewEdit = pdfium::MakeUnique<CFWL_Edit>(
       GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
   CFWL_Edit* pWidget = pNewEdit.get();
-  m_pNormalWidget = std::move(pNewEdit);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNewEdit));
+  pWidget->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  m_pNormalWidget->LockUpdate();
+  CFWL_NoteDriver* pNoteDriver = pWidget->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pWidget, pWidget);
+  m_pOldDelegate = pWidget->GetDelegate();
+  pWidget->SetDelegate(this);
 
-  pWidget->SetText(m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Display));
-  UpdateWidgetProperty();
-  m_pNormalWidget->UnlockUpdate();
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pWidget);
+    pWidget->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+    UpdateWidgetProperty();
+  }
+
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFNumericEdit::UpdateWidgetProperty() {
-  CFWL_Edit* pWidget = static_cast<CFWL_Edit*>(m_pNormalWidget.get());
+  CFWL_Edit* pWidget = static_cast<CFWL_Edit*>(GetNormalWidget());
   if (!pWidget)
     return;
 
@@ -51,10 +53,10 @@
       FWL_STYLEEXT_EDT_ShowScrollbarFocus | FWL_STYLEEXT_EDT_OuterScrollbar |
       FWL_STYLEEXT_EDT_Validate | FWL_STYLEEXT_EDT_Number;
   dwExtendedStyle |= UpdateUIProperty();
-  if (!m_pNode->GetWidgetAcc()->IsHorizontalScrollPolicyOff())
+  if (!m_pNode->IsHorizontalScrollPolicyOff())
     dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoHScroll;
 
-  Optional<int32_t> numCells = m_pNode->GetWidgetAcc()->GetNumberOfCells();
+  Optional<int32_t> numCells = m_pNode->GetNumberOfCells();
   if (numCells && *numCells > 0) {
     dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
     pWidget->SetLimit(*numCells);
@@ -63,29 +65,27 @@
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
     dwExtendedStyle |= FWL_STYLEEXT_EDT_ReadOnly;
 
-  m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
 }
 
 void CXFA_FFNumericEdit::OnProcessEvent(CFWL_Event* pEvent) {
   if (pEvent->GetType() == CFWL_Event::Type::Validate) {
     CFWL_EventValidate* event = static_cast<CFWL_EventValidate*>(pEvent);
-    event->bValidate = OnValidate(m_pNormalWidget.get(), event->wsInsert);
+    event->bValidate = OnValidate(GetNormalWidget(), event->wsInsert);
     return;
   }
   CXFA_FFTextEdit::OnProcessEvent(pEvent);
 }
 
 bool CXFA_FFNumericEdit::OnValidate(CFWL_Widget* pWidget, WideString& wsText) {
-  WideString wsPattern =
-      m_pNode->GetWidgetAcc()->GetPictureContent(XFA_VALUEPICTURE_Edit);
+  WideString wsPattern = m_pNode->GetPictureContent(XFA_VALUEPICTURE_Edit);
   if (!wsPattern.IsEmpty())
     return true;
 
   WideString wsFormat;
   CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(m_pNode.Get());
-  widgetValue.GetNumericFormat(wsFormat,
-                               m_pNode->GetWidgetAcc()->GetLeadDigits(),
-                               m_pNode->GetWidgetAcc()->GetFracDigits());
+  widgetValue.GetNumericFormat(wsFormat, m_pNode->GetLeadDigits(),
+                               m_pNode->GetFracDigits());
   return widgetValue.ValidateNumericTemp(wsText, wsFormat,
                                          m_pNode->GetLocale());
 }
diff --git a/xfa/fxfa/cxfa_ffnumericedit.h b/xfa/fxfa/cxfa_ffnumericedit.h
index c413ac6..b2c39ce 100644
--- a/xfa/fxfa/cxfa_ffnumericedit.h
+++ b/xfa/fxfa/cxfa_ffnumericedit.h
@@ -12,9 +12,8 @@
 
 class CFWL_Event;
 class CFWL_Widget;
-class CXFA_WidgetAcc;
 
-class CXFA_FFNumericEdit : public CXFA_FFTextEdit {
+class CXFA_FFNumericEdit final : public CXFA_FFTextEdit {
  public:
   explicit CXFA_FFNumericEdit(CXFA_Node* pNode);
   ~CXFA_FFNumericEdit() override;
diff --git a/xfa/fxfa/cxfa_ffpageview.cpp b/xfa/fxfa/cxfa_ffpageview.cpp
index fe1fbb5..1915152 100644
--- a/xfa/fxfa/cxfa_ffpageview.cpp
+++ b/xfa/fxfa/cxfa_ffpageview.cpp
@@ -28,38 +28,39 @@
 namespace {
 
 CFX_Matrix GetPageMatrix(const CFX_RectF& docPageRect,
-                         const CFX_Rect& devicePageRect,
+                         const FX_RECT& devicePageRect,
                          int32_t iRotate,
                          uint32_t dwCoordinatesType) {
-  ASSERT(iRotate >= 0 && iRotate <= 3);
+  ASSERT(iRotate >= 0);
+  ASSERT(iRotate <= 3);
 
   bool bFlipX = (dwCoordinatesType & 0x01) != 0;
   bool bFlipY = (dwCoordinatesType & 0x02) != 0;
   CFX_Matrix m((bFlipX ? -1.0f : 1.0f), 0, 0, (bFlipY ? -1.0f : 1.0f), 0, 0);
   if (iRotate == 0 || iRotate == 2) {
-    m.a *= (float)devicePageRect.width / docPageRect.width;
-    m.d *= (float)devicePageRect.height / docPageRect.height;
+    m.a *= (float)devicePageRect.Width() / docPageRect.width;
+    m.d *= (float)devicePageRect.Height() / docPageRect.height;
   } else {
-    m.a *= (float)devicePageRect.height / docPageRect.width;
-    m.d *= (float)devicePageRect.width / docPageRect.height;
+    m.a *= (float)devicePageRect.Height() / docPageRect.width;
+    m.d *= (float)devicePageRect.Width() / docPageRect.height;
   }
   m.Rotate(iRotate * 1.57079632675f);
   switch (iRotate) {
     case 0:
-      m.e = bFlipX ? (float)devicePageRect.right() : (float)devicePageRect.left;
-      m.f = bFlipY ? (float)devicePageRect.bottom() : (float)devicePageRect.top;
+      m.e = bFlipX ? devicePageRect.right : devicePageRect.left;
+      m.f = bFlipY ? devicePageRect.bottom : devicePageRect.top;
       break;
     case 1:
-      m.e = bFlipY ? (float)devicePageRect.left : (float)devicePageRect.right();
-      m.f = bFlipX ? (float)devicePageRect.bottom() : (float)devicePageRect.top;
+      m.e = bFlipY ? devicePageRect.left : devicePageRect.right;
+      m.f = bFlipX ? devicePageRect.bottom : devicePageRect.top;
       break;
     case 2:
-      m.e = bFlipX ? (float)devicePageRect.left : (float)devicePageRect.right();
-      m.f = bFlipY ? (float)devicePageRect.top : (float)devicePageRect.bottom();
+      m.e = bFlipX ? devicePageRect.left : devicePageRect.right;
+      m.f = bFlipY ? devicePageRect.top : devicePageRect.bottom;
       break;
     case 3:
-      m.e = bFlipY ? (float)devicePageRect.right() : (float)devicePageRect.left;
-      m.f = bFlipX ? (float)devicePageRect.top : (float)devicePageRect.bottom();
+      m.e = bFlipY ? devicePageRect.right : devicePageRect.left;
+      m.f = bFlipX ? devicePageRect.top : devicePageRect.bottom;
       break;
     default:
       break;
@@ -70,23 +71,23 @@
 bool PageWidgetFilter(CXFA_FFWidget* pWidget,
                       uint32_t dwFilter,
                       bool bTraversal,
-                      bool bIgnorerelevant) {
+                      bool bIgnoreRelevant) {
   CXFA_Node* pNode = pWidget->GetNode();
 
-  if (!!(dwFilter & XFA_WidgetStatus_Focused) &&
+  if ((dwFilter & XFA_WidgetStatus_Focused) &&
       (!pNode || pNode->GetElementType() != XFA_Element::Field)) {
     return false;
   }
 
-  uint32_t dwStatus = pWidget->GetStatus();
-  if (bTraversal && (dwStatus & XFA_WidgetStatus_Disabled))
+  CXFA_ContentLayoutItem* pItem = pWidget->GetLayoutItem();
+  if (bTraversal && pItem->TestStatusBits(XFA_WidgetStatus_Disabled))
     return false;
-  if (bIgnorerelevant)
-    return !!(dwStatus & XFA_WidgetStatus_Visible);
+  if (bIgnoreRelevant)
+    return pItem->TestStatusBits(XFA_WidgetStatus_Visible);
 
   dwFilter &= (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable |
                XFA_WidgetStatus_Printable);
-  return (dwFilter & dwStatus) == dwFilter;
+  return pItem->TestStatusBits(dwFilter);
 }
 
 bool IsLayoutElement(XFA_Element eElement, bool bLayoutContainer) {
@@ -110,7 +111,7 @@
 }  // namespace
 
 CXFA_FFPageView::CXFA_FFPageView(CXFA_FFDocView* pDocView, CXFA_Node* pPageArea)
-    : CXFA_ContainerLayoutItem(pPageArea), m_pDocView(pDocView) {}
+    : m_pPageArea(pPageArea), m_pDocView(pDocView) {}
 
 CXFA_FFPageView::~CXFA_FFPageView() {}
 
@@ -119,32 +120,32 @@
 }
 
 CFX_RectF CXFA_FFPageView::GetPageViewRect() const {
-  return CFX_RectF(0, 0, GetPageSize());
+  return CFX_RectF(0, 0, GetLayoutItem()->GetPageSize());
 }
 
-CFX_Matrix CXFA_FFPageView::GetDisplayMatrix(const CFX_Rect& rtDisp,
+CFX_Matrix CXFA_FFPageView::GetDisplayMatrix(const FX_RECT& rtDisp,
                                              int32_t iRotate) const {
-  return GetPageMatrix(CFX_RectF(0, 0, GetPageSize()), rtDisp, iRotate, 0);
+  return GetPageMatrix(CFX_RectF(0, 0, GetLayoutItem()->GetPageSize()), rtDisp,
+                       iRotate, 0);
 }
 
-std::unique_ptr<IXFA_WidgetIterator> CXFA_FFPageView::CreateWidgetIterator(
-    uint32_t dwTraverseWay,
+std::unique_ptr<IXFA_WidgetIterator> CXFA_FFPageView::CreateFormWidgetIterator(
     uint32_t dwWidgetFilter) {
-  switch (dwTraverseWay) {
-    case XFA_TRAVERSEWAY_Tranvalse:
-      return pdfium::MakeUnique<CXFA_FFTabOrderPageWidgetIterator>(
-          this, dwWidgetFilter);
-    case XFA_TRAVERSEWAY_Form:
-      return pdfium::MakeUnique<CXFA_FFPageWidgetIterator>(this,
-                                                           dwWidgetFilter);
-  }
-  return nullptr;
+  return pdfium::MakeUnique<CXFA_FFPageWidgetIterator>(this, dwWidgetFilter);
+}
+
+std::unique_ptr<IXFA_WidgetIterator>
+CXFA_FFPageView::CreateTraverseWidgetIterator(uint32_t dwWidgetFilter) {
+  return pdfium::MakeUnique<CXFA_FFTabOrderPageWidgetIterator>(this,
+                                                               dwWidgetFilter);
 }
 
 CXFA_FFPageWidgetIterator::CXFA_FFPageWidgetIterator(CXFA_FFPageView* pPageView,
                                                      uint32_t dwFilter)
-    : m_pPageView(pPageView), m_dwFilter(dwFilter), m_sIterator(pPageView) {
-  m_bIgnorerelevant =
+    : m_pPageView(pPageView),
+      m_dwFilter(dwFilter),
+      m_sIterator(pPageView->GetLayoutItem()) {
+  m_bIgnoreRelevant =
       m_pPageView->GetDocView()->GetDoc()->GetXFADoc()->GetCurVersionMode() <
       XFA_VERSION_205;
 }
@@ -196,8 +197,8 @@
   return pLayoutItem ? XFA_GetWidgetFromLayoutItem(pLayoutItem) : nullptr;
 }
 
-bool CXFA_FFPageWidgetIterator::SetCurrentWidget(CXFA_FFWidget* hWidget) {
-  return hWidget && m_sIterator.SetCurrent(hWidget);
+bool CXFA_FFPageWidgetIterator::SetCurrentWidget(CXFA_FFWidget* pWidget) {
+  return pWidget && m_sIterator.SetCurrent(pWidget->GetLayoutItem());
 }
 
 CXFA_FFWidget* CXFA_FFPageWidgetIterator::GetWidget(
@@ -206,11 +207,11 @@
   if (!pWidget)
     return nullptr;
 
-  if (!PageWidgetFilter(pWidget, m_dwFilter, false, m_bIgnorerelevant))
+  if (!PageWidgetFilter(pWidget, m_dwFilter, false, m_bIgnoreRelevant))
     return nullptr;
 
   if (!pWidget->IsLoaded() &&
-      !!(pWidget->GetStatus() & XFA_WidgetStatus_Visible)) {
+      pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible)) {
     if (!pWidget->LoadWidget())
       return nullptr;
   }
@@ -231,7 +232,7 @@
     CXFA_FFPageView* pPageView,
     uint32_t dwFilter)
     : m_pPageView(pPageView), m_dwFilter(dwFilter), m_iCurWidget(-1) {
-  m_bIgnorerelevant =
+  m_bIgnoreRelevant =
       m_pPageView->GetDocView()->GetDoc()->GetXFADoc()->GetCurVersionMode() <
       XFA_VERSION_205;
   Reset();
@@ -247,10 +248,10 @@
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToFirst() {
   for (int32_t i = 0;
        i < pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
-                         m_bIgnorerelevant)) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
+                         m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget];
+      return m_TabOrderWidgetArray[m_iCurWidget].Get();
     }
   }
   return nullptr;
@@ -259,10 +260,10 @@
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToLast() {
   for (int32_t i = pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray) - 1;
        i >= 0; i--) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
-                         m_bIgnorerelevant)) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
+                         m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget];
+      return m_TabOrderWidgetArray[m_iCurWidget].Get();
     }
   }
   return nullptr;
@@ -271,10 +272,10 @@
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToNext() {
   for (int32_t i = m_iCurWidget + 1;
        i < pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
-                         m_bIgnorerelevant)) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
+                         m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget];
+      return m_TabOrderWidgetArray[m_iCurWidget].Get();
     }
   }
   m_iCurWidget = -1;
@@ -283,10 +284,10 @@
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToPrevious() {
   for (int32_t i = m_iCurWidget - 1; i >= 0; i--) {
-    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
-                         m_bIgnorerelevant)) {
+    if (PageWidgetFilter(m_TabOrderWidgetArray[i].Get(), m_dwFilter, true,
+                         m_bIgnoreRelevant)) {
       m_iCurWidget = i;
-      return m_TabOrderWidgetArray[m_iCurWidget];
+      return m_TabOrderWidgetArray[m_iCurWidget].Get();
     }
   }
   m_iCurWidget = -1;
@@ -294,7 +295,8 @@
 }
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetCurrentWidget() {
-  return m_iCurWidget >= 0 ? m_TabOrderWidgetArray[m_iCurWidget] : nullptr;
+  return m_iCurWidget >= 0 ? m_TabOrderWidgetArray[m_iCurWidget].Get()
+                           : nullptr;
 }
 
 bool CXFA_FFTabOrderPageWidgetIterator::SetCurrentWidget(
@@ -343,9 +345,9 @@
   while (pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray) <
          nWidgetCount) {
     if (!pdfium::ContainsValue(m_TabOrderWidgetArray, hWidget)) {
-      m_TabOrderWidgetArray.push_back(hWidget);
-      CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc();
-      if (pWidgetAcc->GetUIType() == XFA_Element::ExclGroup) {
+      m_TabOrderWidgetArray.emplace_back(hWidget);
+      CXFA_Node* pNode = hWidget->GetNode();
+      if (pNode->GetFFWidgetType() == XFA_FFWidgetType::kExclGroup) {
         auto it = std::find(SpaceOrderWidgetArray.begin(),
                             SpaceOrderWidgetArray.end(), hWidget);
         int32_t iWidgetIndex = it != SpaceOrderWidgetArray.end()
@@ -354,10 +356,10 @@
         while (true) {
           CXFA_FFWidget* radio =
               SpaceOrderWidgetArray[iWidgetIndex % nWidgetCount];
-          if (radio->GetNode()->GetExclGroupIfExists() != pWidgetAcc->GetNode())
+          if (radio->GetNode()->GetExclGroupIfExists() != pNode)
             break;
           if (!pdfium::ContainsValue(m_TabOrderWidgetArray, hWidget))
-            m_TabOrderWidgetArray.push_back(radio);
+            m_TabOrderWidgetArray.emplace_back(radio);
 
           iWidgetIndex++;
         }
@@ -378,41 +380,41 @@
 
 void CXFA_FFTabOrderPageWidgetIterator::OrderContainer(
     CXFA_LayoutItemIterator* sIterator,
-    CXFA_LayoutItem* pContainerItem,
+    CXFA_LayoutItem* pViewItem,
     CXFA_TabParam* pContainer,
-    bool& bCurrentItem,
-    bool& bContentArea,
-    bool bMarsterPage) {
+    bool* bCurrentItem,
+    bool* bContentArea,
+    bool bMasterPage) {
   std::vector<std::unique_ptr<CXFA_TabParam>> tabParams;
   CXFA_LayoutItem* pSearchItem = sIterator->MoveToNext();
   while (pSearchItem) {
     if (!pSearchItem->IsContentLayoutItem()) {
-      bContentArea = true;
+      *bContentArea = true;
       pSearchItem = sIterator->MoveToNext();
       continue;
     }
-    if (bMarsterPage && bContentArea) {
+    if (bMasterPage && *bContentArea) {
       break;
     }
-    if (bMarsterPage || bContentArea) {
+    if (bMasterPage || *bContentArea) {
       CXFA_FFWidget* hWidget = GetWidget(pSearchItem);
       if (!hWidget) {
         pSearchItem = sIterator->MoveToNext();
         continue;
       }
-      if (pContainerItem && (pSearchItem->GetParent() != pContainerItem)) {
-        bCurrentItem = true;
+      if (pViewItem && (pSearchItem->GetParent() != pViewItem)) {
+        *bCurrentItem = true;
         break;
       }
       tabParams.push_back(pdfium::MakeUnique<CXFA_TabParam>(hWidget));
       if (IsLayoutElement(pSearchItem->GetFormNode()->GetElementType(), true)) {
         OrderContainer(sIterator, pSearchItem, tabParams.back().get(),
-                       bCurrentItem, bContentArea, bMarsterPage);
+                       bCurrentItem, bContentArea, bMasterPage);
       }
     }
-    if (bCurrentItem) {
+    if (*bCurrentItem) {
       pSearchItem = sIterator->GetCurrent();
-      bCurrentItem = false;
+      *bCurrentItem = false;
     } else {
       pSearchItem = sIterator->MoveToNext();
     }
@@ -422,7 +424,7 @@
                const std::unique_ptr<CXFA_TabParam>& arg2) {
               const CFX_RectF& rt1 = arg1->GetWidget()->GetWidgetRect();
               const CFX_RectF& rt2 = arg2->GetWidget()->GetWidgetRect();
-              if (rt1.top - rt2.top >= XFA_FLOAT_PERCISION)
+              if (rt1.top - rt2.top >= kXFAWidgetPrecision)
                 return rt1.top < rt2.top;
               return rt1.left < rt2.left;
             });
@@ -432,11 +434,12 @@
 
 void CXFA_FFTabOrderPageWidgetIterator::CreateSpaceOrderWidgetArray(
     std::vector<CXFA_FFWidget*>* WidgetArray) {
-  CXFA_LayoutItemIterator sIterator(m_pPageView);
+  CXFA_LayoutItemIterator sIterator(m_pPageView->GetLayoutItem());
   auto pParam = pdfium::MakeUnique<CXFA_TabParam>(nullptr);
   bool bCurrentItem = false;
   bool bContentArea = false;
-  OrderContainer(&sIterator, nullptr, pParam.get(), bCurrentItem, bContentArea);
+  OrderContainer(&sIterator, nullptr, pParam.get(), &bCurrentItem,
+                 &bContentArea, false);
   WidgetArray->insert(WidgetArray->end(), pParam->GetChildren().begin(),
                       pParam->GetChildren().end());
 
@@ -444,22 +447,23 @@
   bCurrentItem = false;
   bContentArea = false;
   pParam->ClearChildren();
-  OrderContainer(&sIterator, nullptr, pParam.get(), bCurrentItem, bContentArea,
-                 true);
+  OrderContainer(&sIterator, nullptr, pParam.get(), &bCurrentItem,
+                 &bContentArea, true);
   WidgetArray->insert(WidgetArray->end(), pParam->GetChildren().begin(),
                       pParam->GetChildren().end());
 }
 
 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetWidget(
     CXFA_LayoutItem* pLayoutItem) {
-  if (CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem)) {
-    if (!pWidget->IsLoaded() &&
-        (pWidget->GetStatus() & XFA_WidgetStatus_Visible)) {
-      pWidget->LoadWidget();
-    }
-    return pWidget;
+  CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem);
+  if (!pWidget)
+    return nullptr;
+
+  if (!pWidget->IsLoaded() &&
+      pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible)) {
+    pWidget->LoadWidget();
   }
-  return nullptr;
+  return pWidget;
 }
 
 CXFA_TabParam::CXFA_TabParam(CXFA_FFWidget* pWidget) : m_pWidget(pWidget) {}
diff --git a/xfa/fxfa/cxfa_ffpageview.h b/xfa/fxfa/cxfa_ffpageview.h
index b33e25f..65c0b95 100644
--- a/xfa/fxfa/cxfa_ffpageview.h
+++ b/xfa/fxfa/cxfa_ffpageview.h
@@ -1,4 +1,4 @@
-// Copyrig 2014 PDFium Authors. All rights reserved.
+// 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.
 
@@ -10,35 +10,39 @@
 #include <memory>
 #include <vector>
 
-#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_layoutitem.h"
+#include "core/fxcrt/observed_ptr.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
 
 class CXFA_FFWidget;
 class CXFA_FFDocView;
 
-class CXFA_FFPageView : public CXFA_ContainerLayoutItem {
+class CXFA_FFPageView : public Observable {
  public:
   CXFA_FFPageView(CXFA_FFDocView* pDocView, CXFA_Node* pPageArea);
-  ~CXFA_FFPageView() override;
+  ~CXFA_FFPageView();
+
+  CXFA_ViewLayoutItem* GetLayoutItem() const { return m_pLayoutItem.Get(); }
+  void SetLayoutItem(CXFA_ViewLayoutItem* pItem) { m_pLayoutItem = pItem; }
 
   CXFA_FFDocView* GetDocView() const;
   CFX_RectF GetPageViewRect() const;
-  CFX_Matrix GetDisplayMatrix(const CFX_Rect& rtDisp, int32_t iRotate) const;
-  std::unique_ptr<IXFA_WidgetIterator> CreateWidgetIterator(
-      uint32_t dwTraverseWay,
+  CFX_Matrix GetDisplayMatrix(const FX_RECT& rtDisp, int32_t iRotate) const;
+
+  // These always return a non-null iterator.
+  std::unique_ptr<IXFA_WidgetIterator> CreateFormWidgetIterator(
+      uint32_t dwWidgetFilter);
+  std::unique_ptr<IXFA_WidgetIterator> CreateTraverseWidgetIterator(
       uint32_t dwWidgetFilter);
 
- protected:
+ private:
+  UnownedPtr<CXFA_Node> const m_pPageArea;
   UnownedPtr<CXFA_FFDocView> const m_pDocView;
+  UnownedPtr<CXFA_ViewLayoutItem> m_pLayoutItem;
 };
 
-using CXFA_LayoutItemIterator =
-    CXFA_NodeIteratorTemplate<CXFA_LayoutItem,
-                              CXFA_TraverseStrategy_LayoutItem>;
-
-class CXFA_FFPageWidgetIterator : public IXFA_WidgetIterator {
+class CXFA_FFPageWidgetIterator final : public IXFA_WidgetIterator {
  public:
   CXFA_FFPageWidgetIterator(CXFA_FFPageView* pPageView, uint32_t dwFilter);
   ~CXFA_FFPageWidgetIterator() override;
@@ -51,13 +55,13 @@
   CXFA_FFWidget* GetCurrentWidget() override;
   bool SetCurrentWidget(CXFA_FFWidget* hWidget) override;
 
- protected:
+ private:
   CXFA_FFWidget* GetWidget(CXFA_LayoutItem* pLayoutItem);
 
-  CXFA_FFPageView* m_pPageView;
-  CXFA_FFWidget* m_hCurWidget;
+  UnownedPtr<CXFA_FFPageView> m_pPageView;
+  UnownedPtr<CXFA_FFWidget> m_hCurWidget;
   uint32_t m_dwFilter;
-  bool m_bIgnorerelevant;
+  bool m_bIgnoreRelevant;
   CXFA_LayoutItemIterator m_sIterator;
 };
 
@@ -76,7 +80,7 @@
   std::vector<CXFA_FFWidget*> m_Children;
 };
 
-class CXFA_FFTabOrderPageWidgetIterator : public IXFA_WidgetIterator {
+class CXFA_FFTabOrderPageWidgetIterator final : public IXFA_WidgetIterator {
  public:
   CXFA_FFTabOrderPageWidgetIterator(CXFA_FFPageView* pPageView,
                                     uint32_t dwFilter);
@@ -90,7 +94,7 @@
   CXFA_FFWidget* GetCurrentWidget() override;
   bool SetCurrentWidget(CXFA_FFWidget* hWidget) override;
 
- protected:
+ private:
   CXFA_FFWidget* GetTraverseWidget(CXFA_FFWidget* pWidget);
   CXFA_FFWidget* FindWidgetByName(const WideString& wsWidgetName,
                                   CXFA_FFWidget* pRefWidget);
@@ -98,17 +102,17 @@
   void CreateSpaceOrderWidgetArray(std::vector<CXFA_FFWidget*>* WidgetArray);
   CXFA_FFWidget* GetWidget(CXFA_LayoutItem* pLayoutItem);
   void OrderContainer(CXFA_LayoutItemIterator* sIterator,
-                      CXFA_LayoutItem* pContainerItem,
+                      CXFA_LayoutItem* pViewItem,
                       CXFA_TabParam* pContainer,
-                      bool& bCurrentItem,
-                      bool& bContentArea,
-                      bool bMarsterPage = false);
+                      bool* bCurrentItem,
+                      bool* bContentArea,
+                      bool bMasterPage);
 
-  std::vector<CXFA_FFWidget*> m_TabOrderWidgetArray;
-  CXFA_FFPageView* m_pPageView;
+  std::vector<UnownedPtr<CXFA_FFWidget>> m_TabOrderWidgetArray;
+  UnownedPtr<CXFA_FFPageView> m_pPageView;
   uint32_t m_dwFilter;
   int32_t m_iCurWidget;
-  bool m_bIgnorerelevant;
+  bool m_bIgnoreRelevant;
 };
 
 #endif  // XFA_FXFA_CXFA_FFPAGEVIEW_H_
diff --git a/xfa/fxfa/cxfa_ffpasswordedit.cpp b/xfa/fxfa/cxfa_ffpasswordedit.cpp
index 15bd5fa..8019c5c 100644
--- a/xfa/fxfa/cxfa_ffpasswordedit.cpp
+++ b/xfa/fxfa/cxfa_ffpasswordedit.cpp
@@ -8,39 +8,43 @@
 
 #include <utility>
 
+#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_passwordedit.h"
 
-CXFA_FFPasswordEdit::CXFA_FFPasswordEdit(CXFA_Node* pNode)
-    : CXFA_FFTextEdit(pNode) {}
+CXFA_FFPasswordEdit::CXFA_FFPasswordEdit(CXFA_Node* pNode,
+                                         CXFA_PasswordEdit* password_node)
+    : CXFA_FFTextEdit(pNode), password_node_(password_node) {}
 
-CXFA_FFPasswordEdit::~CXFA_FFPasswordEdit() {}
+CXFA_FFPasswordEdit::~CXFA_FFPasswordEdit() = default;
 
 bool CXFA_FFPasswordEdit::LoadWidget() {
+  ASSERT(!IsLoaded());
   auto pNewEdit = pdfium::MakeUnique<CFWL_Edit>(
       GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
   CFWL_Edit* pWidget = pNewEdit.get();
-  m_pNormalWidget = std::move(pNewEdit);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNewEdit));
+  pWidget->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  m_pNormalWidget->LockUpdate();
+  CFWL_NoteDriver* pNoteDriver = pWidget->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pWidget, pWidget);
+  m_pOldDelegate = pWidget->GetDelegate();
+  pWidget->SetDelegate(this);
 
-  pWidget->SetText(m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Display));
-  UpdateWidgetProperty();
-  m_pNormalWidget->UnlockUpdate();
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pWidget);
+    pWidget->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+    UpdateWidgetProperty();
+  }
+
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFPasswordEdit::UpdateWidgetProperty() {
-  CFWL_Edit* pWidget = static_cast<CFWL_Edit*>(m_pNormalWidget.get());
+  CFWL_Edit* pWidget = static_cast<CFWL_Edit*>(GetNormalWidget());
   if (!pWidget)
     return;
 
@@ -49,14 +53,14 @@
                              FWL_STYLEEXT_EDT_Password;
   dwExtendedStyle |= UpdateUIProperty();
 
-  WideString password = m_pNode->GetWidgetAcc()->GetPasswordChar();
+  WideString password = password_node_->GetPasswordChar();
   if (!password.IsEmpty())
     pWidget->SetAliasChar(password[0]);
-  if (!m_pNode->GetWidgetAcc()->IsHorizontalScrollPolicyOff())
+  if (!m_pNode->IsHorizontalScrollPolicyOff())
     dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoHScroll;
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive())
     dwExtendedStyle |= FWL_STYLEEXT_EDT_ReadOnly;
 
   dwExtendedStyle |= GetAlignment();
-  m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
 }
diff --git a/xfa/fxfa/cxfa_ffpasswordedit.h b/xfa/fxfa/cxfa_ffpasswordedit.h
index da7279a..a96efa0 100644
--- a/xfa/fxfa/cxfa_ffpasswordedit.h
+++ b/xfa/fxfa/cxfa_ffpasswordedit.h
@@ -7,18 +7,22 @@
 #ifndef XFA_FXFA_CXFA_FFPASSWORDEDIT_H_
 #define XFA_FXFA_CXFA_FFPASSWORDEDIT_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/cxfa_fftextedit.h"
 
-class CXFA_WidgetAcc;
+class CXFA_PasswordEdit;
 
-class CXFA_FFPasswordEdit : public CXFA_FFTextEdit {
+class CXFA_FFPasswordEdit final : public CXFA_FFTextEdit {
  public:
-  explicit CXFA_FFPasswordEdit(CXFA_Node* pNode);
+  CXFA_FFPasswordEdit(CXFA_Node* pNode, CXFA_PasswordEdit* password_node);
   ~CXFA_FFPasswordEdit() override;
 
   // CXFA_FFTextEdit
   bool LoadWidget() override;
   void UpdateWidgetProperty() override;
+
+ private:
+  UnownedPtr<CXFA_PasswordEdit> const password_node_;
 };
 
 #endif  // XFA_FXFA_CXFA_FFPASSWORDEDIT_H_
diff --git a/xfa/fxfa/cxfa_ffpushbutton.cpp b/xfa/fxfa/cxfa_ffpushbutton.cpp
index fdf7512..7f2a06e 100644
--- a/xfa/fxfa/cxfa_ffpushbutton.cpp
+++ b/xfa/fxfa/cxfa_ffpushbutton.cpp
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "core/fxge/render_defines.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fwl/cfwl_pushbutton.h"
@@ -19,80 +20,72 @@
 #include "xfa/fxfa/cxfa_textlayout.h"
 #include "xfa/fxfa/cxfa_textprovider.h"
 #include "xfa/fxfa/parser/cxfa_border.h"
+#include "xfa/fxfa/parser/cxfa_button.h"
 #include "xfa/fxfa/parser/cxfa_caption.h"
 #include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
-CXFA_FFPushButton::CXFA_FFPushButton(CXFA_Node* pNode)
-    : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
+CXFA_FFPushButton::CXFA_FFPushButton(CXFA_Node* pNode, CXFA_Button* button)
+    : CXFA_FFField(pNode), button_(button) {}
 
-CXFA_FFPushButton::~CXFA_FFPushButton() {
-  CXFA_FFPushButton::UnloadWidget();
-}
+CXFA_FFPushButton::~CXFA_FFPushButton() = default;
 
 void CXFA_FFPushButton::RenderWidget(CXFA_Graphics* pGS,
                                      const CFX_Matrix& matrix,
-                                     uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                     HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
   RenderHighlightCaption(pGS, &mtRotate);
 
   CFX_RectF rtWidget = GetRectWithoutRotate();
   CFX_Matrix mt(1, 0, 0, 1, rtWidget.left, rtWidget.top);
   mt.Concat(mtRotate);
-  GetApp()->GetFWLWidgetMgr()->OnDrawWidget(m_pNormalWidget.get(), pGS, mt);
+  GetApp()->GetFWLWidgetMgr()->OnDrawWidget(GetNormalWidget(), pGS, mt);
 }
 
 bool CXFA_FFPushButton::LoadWidget() {
-  ASSERT(!m_pNormalWidget);
+  ASSERT(!IsLoaded());
   auto pNew = pdfium::MakeUnique<CFWL_PushButton>(GetFWLApp());
   CFWL_PushButton* pPushButton = pNew.get();
   m_pOldDelegate = pPushButton->GetDelegate();
   pPushButton->SetDelegate(this);
-  m_pNormalWidget = std::move(pNew);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNew));
+  pPushButton->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pNormalWidget->LockUpdate();
-  UpdateWidgetProperty();
-  LoadHighlightCaption();
-  m_pNormalWidget->UnlockUpdate();
+  CFWL_NoteDriver* pNoteDriver = pPushButton->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pPushButton, pPushButton);
+
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pPushButton);
+    UpdateWidgetProperty();
+    LoadHighlightCaption();
+  }
+
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFPushButton::UpdateWidgetProperty() {
   uint32_t dwStyleEx = 0;
-  switch (m_pNode->GetWidgetAcc()->GetButtonHighlight()) {
-    case XFA_AttributeEnum::Inverted:
+  switch (button_->GetHighlight()) {
+    case XFA_AttributeValue::Inverted:
       dwStyleEx = XFA_FWL_PSBSTYLEEXT_HiliteInverted;
       break;
-    case XFA_AttributeEnum::Outline:
+    case XFA_AttributeValue::Outline:
       dwStyleEx = XFA_FWL_PSBSTYLEEXT_HiliteOutLine;
       break;
-    case XFA_AttributeEnum::Push:
+    case XFA_AttributeValue::Push:
       dwStyleEx = XFA_FWL_PSBSTYLEEXT_HilitePush;
       break;
     default:
       break;
   }
-  m_pNormalWidget->ModifyStylesEx(dwStyleEx, 0xFFFFFFFF);
-}
-
-void CXFA_FFPushButton::UnloadWidget() {
-  m_pRolloverTextLayout.reset();
-  m_pDownTextLayout.reset();
-  m_pRollProvider.reset();
-  m_pDownProvider.reset();
-  CXFA_FFField::UnloadWidget();
+  GetNormalWidget()->ModifyStylesEx(dwStyleEx, 0xFFFFFFFF);
 }
 
 bool CXFA_FFPushButton::PerformLayout() {
@@ -101,27 +94,25 @@
 
   m_rtUI = rtWidget;
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    XFA_RectWithoutMargin(rtWidget, margin);
+  XFA_RectWithoutMargin(&rtWidget, margin);
 
   m_rtCaption = rtWidget;
 
   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
   CXFA_Margin* captionMargin = caption ? caption->GetMarginIfExists() : nullptr;
-  if (captionMargin)
-    XFA_RectWithoutMargin(m_rtCaption, captionMargin);
+  XFA_RectWithoutMargin(&m_rtCaption, captionMargin);
 
   LayoutHighlightCaption();
   SetFWLRect();
-  if (m_pNormalWidget)
-    m_pNormalWidget->Update();
+  if (GetNormalWidget())
+    GetNormalWidget()->Update();
 
   return true;
 }
 
 float CXFA_FFPushButton::GetLineWidth() {
   CXFA_Border* border = m_pNode->GetBorderIfExists();
-  if (border && border->GetPresence() == XFA_AttributeEnum::Visible) {
+  if (border && border->GetPresence() == XFA_AttributeValue::Visible) {
     CXFA_Edge* edge = border->GetEdgeIfExists(0);
     return edge ? edge->GetThickness() : 0;
   }
@@ -141,19 +132,19 @@
   if (!caption || caption->IsHidden())
     return;
 
-  if (m_pNode->GetWidgetAcc()->HasButtonRollover()) {
+  if (m_pNode->HasButtonRollover()) {
     if (!m_pRollProvider) {
       m_pRollProvider = pdfium::MakeUnique<CXFA_TextProvider>(
-          m_pNode->GetWidgetAcc(), XFA_TEXTPROVIDERTYPE_Rollover);
+          m_pNode.Get(), XFA_TEXTPROVIDERTYPE_Rollover);
     }
     m_pRolloverTextLayout =
         pdfium::MakeUnique<CXFA_TextLayout>(GetDoc(), m_pRollProvider.get());
   }
 
-  if (m_pNode->GetWidgetAcc()->HasButtonDown()) {
+  if (m_pNode->HasButtonDown()) {
     if (!m_pDownProvider) {
       m_pDownProvider = pdfium::MakeUnique<CXFA_TextProvider>(
-          m_pNode->GetWidgetAcc(), XFA_TEXTPROVIDERTYPE_Down);
+          m_pNode.Get(), XFA_TEXTPROVIDERTYPE_Down);
     }
     m_pDownTextLayout =
         pdfium::MakeUnique<CXFA_TextLayout>(GetDoc(), m_pDownProvider.get());
@@ -171,8 +162,7 @@
 
 void CXFA_FFPushButton::RenderHighlightCaption(CXFA_Graphics* pGS,
                                                CFX_Matrix* pMatrix) {
-  CXFA_TextLayout* pCapTextLayout =
-      m_pNode->GetWidgetAcc()->GetCaptionTextLayout();
+  CXFA_TextLayout* pCapTextLayout = m_pNode->GetCaptionTextLayout();
   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
   if (!caption || !caption->IsVisible())
     return;
@@ -186,7 +176,7 @@
     mt.Concat(*pMatrix);
   }
 
-  uint32_t dwState = m_pNormalWidget->GetStates();
+  uint32_t dwState = GetNormalWidget()->GetStates();
   if (m_pDownTextLayout && (dwState & FWL_STATE_PSB_Pressed) &&
       (dwState & FWL_STATE_PSB_Hovered)) {
     if (m_pDownTextLayout->DrawString(pRenderDevice, mt, rtClip, 0))
@@ -211,29 +201,30 @@
 
 void CXFA_FFPushButton::OnDrawWidget(CXFA_Graphics* pGraphics,
                                      const CFX_Matrix& matrix) {
-  if (m_pNormalWidget->GetStylesEx() & XFA_FWL_PSBSTYLEEXT_HiliteInverted) {
-    if ((m_pNormalWidget->GetStates() & FWL_STATE_PSB_Pressed) &&
-        (m_pNormalWidget->GetStates() & FWL_STATE_PSB_Hovered)) {
-      CFX_RectF rtFill(0, 0, m_pNormalWidget->GetWidgetRect().Size());
+  auto* pWidget = GetNormalWidget();
+  if (pWidget->GetStylesEx() & XFA_FWL_PSBSTYLEEXT_HiliteInverted) {
+    if ((pWidget->GetStates() & FWL_STATE_PSB_Pressed) &&
+        (pWidget->GetStates() & FWL_STATE_PSB_Hovered)) {
+      CFX_RectF rtFill(0, 0, pWidget->GetWidgetRect().Size());
       float fLineWith = GetLineWidth();
       rtFill.Deflate(fLineWith, fLineWith);
       CXFA_GEPath path;
       path.AddRectangle(rtFill.left, rtFill.top, rtFill.width, rtFill.height);
-      pGraphics->SetFillColor(CXFA_GEColor(FXARGB_MAKE(128, 128, 255, 255)));
+      pGraphics->SetFillColor(CXFA_GEColor(ArgbEncode(128, 128, 255, 255)));
       pGraphics->FillPath(&path, FXFILL_WINDING, &matrix);
     }
     return;
   }
 
-  if (m_pNormalWidget->GetStylesEx() & XFA_FWL_PSBSTYLEEXT_HiliteOutLine) {
-    if ((m_pNormalWidget->GetStates() & FWL_STATE_PSB_Pressed) &&
-        (m_pNormalWidget->GetStates() & FWL_STATE_PSB_Hovered)) {
+  if (pWidget->GetStylesEx() & XFA_FWL_PSBSTYLEEXT_HiliteOutLine) {
+    if ((pWidget->GetStates() & FWL_STATE_PSB_Pressed) &&
+        (pWidget->GetStates() & FWL_STATE_PSB_Hovered)) {
       float fLineWidth = GetLineWidth();
-      pGraphics->SetStrokeColor(CXFA_GEColor(FXARGB_MAKE(255, 128, 255, 255)));
+      pGraphics->SetStrokeColor(CXFA_GEColor(ArgbEncode(255, 128, 255, 255)));
       pGraphics->SetLineWidth(fLineWidth);
 
       CXFA_GEPath path;
-      CFX_RectF rect = m_pNormalWidget->GetWidgetRect();
+      CFX_RectF rect = pWidget->GetWidgetRect();
       path.AddRectangle(0, 0, rect.width, rect.height);
       pGraphics->StrokePath(&path, &matrix);
     }
diff --git a/xfa/fxfa/cxfa_ffpushbutton.h b/xfa/fxfa/cxfa_ffpushbutton.h
index 9b2a0ee..e4b34b5 100644
--- a/xfa/fxfa/cxfa_ffpushbutton.h
+++ b/xfa/fxfa/cxfa_ffpushbutton.h
@@ -9,25 +9,27 @@
 
 #include <memory>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 
 #define XFA_FWL_PSBSTYLEEXT_HiliteInverted (1L << 0)
 #define XFA_FWL_PSBSTYLEEXT_HilitePush (1L << 1)
 #define XFA_FWL_PSBSTYLEEXT_HiliteOutLine (1L << 2)
 
+class CXFA_Button;
+class CXFA_TextLayout;
 class CXFA_TextProvider;
 
-class CXFA_FFPushButton : public CXFA_FFField {
+class CXFA_FFPushButton final : public CXFA_FFField {
  public:
-  explicit CXFA_FFPushButton(CXFA_Node* pNode);
+  CXFA_FFPushButton(CXFA_Node* pNode, CXFA_Button* button);
   ~CXFA_FFPushButton() override;
 
   // CXFA_FFField
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
   bool LoadWidget() override;
-  void UnloadWidget() override;
   bool PerformLayout() override;
   void UpdateWidgetProperty() override;
   void OnProcessMessage(CFWL_Message* pMessage) override;
@@ -48,7 +50,8 @@
   std::unique_ptr<CXFA_TextLayout> m_pDownTextLayout;
   std::unique_ptr<CXFA_TextProvider> m_pRollProvider;
   std::unique_ptr<CXFA_TextProvider> m_pDownProvider;
-  IFWL_WidgetDelegate* m_pOldDelegate;
+  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
+  UnownedPtr<CXFA_Button> const button_;
 };
 
 #endif  // XFA_FXFA_CXFA_FFPUSHBUTTON_H_
diff --git a/xfa/fxfa/cxfa_ffrectangle.cpp b/xfa/fxfa/cxfa_ffrectangle.cpp
index 46c1009..ff5e576 100644
--- a/xfa/fxfa/cxfa_ffrectangle.cpp
+++ b/xfa/fxfa/cxfa_ffrectangle.cpp
@@ -9,14 +9,14 @@
 #include "xfa/fxfa/parser/cxfa_rectangle.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
 
-CXFA_FFRectangle::CXFA_FFRectangle(CXFA_Node* pNode) : CXFA_FFDraw(pNode) {}
+CXFA_FFRectangle::CXFA_FFRectangle(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
 CXFA_FFRectangle::~CXFA_FFRectangle() {}
 
 void CXFA_FFRectangle::RenderWidget(CXFA_Graphics* pGS,
                                     const CFX_Matrix& matrix,
-                                    uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                    HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CXFA_Value* value = m_pNode->GetFormValueIfExists();
@@ -25,11 +25,9 @@
 
   CFX_RectF rect = GetRectWithoutRotate();
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    XFA_RectWithoutMargin(rect, margin);
+  XFA_RectWithoutMargin(&rect, margin);
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
-
   DrawBorder(pGS, value->GetRectangleIfExists(), rect, mtRotate);
 }
diff --git a/xfa/fxfa/cxfa_ffrectangle.h b/xfa/fxfa/cxfa_ffrectangle.h
index 304c5c6..d78d5ff 100644
--- a/xfa/fxfa/cxfa_ffrectangle.h
+++ b/xfa/fxfa/cxfa_ffrectangle.h
@@ -7,9 +7,9 @@
 #ifndef XFA_FXFA_CXFA_FFRECTANGLE_H_
 #define XFA_FXFA_CXFA_FFRECTANGLE_H_
 
-#include "xfa/fxfa/cxfa_ffdraw.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
 
-class CXFA_FFRectangle : public CXFA_FFDraw {
+class CXFA_FFRectangle final : public CXFA_FFWidget {
  public:
   explicit CXFA_FFRectangle(CXFA_Node* pNode);
   ~CXFA_FFRectangle() override;
@@ -17,7 +17,7 @@
   // CXFA_FFWidget
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
 };
 
 #endif  // XFA_FXFA_CXFA_FFRECTANGLE_H_
diff --git a/xfa/fxfa/cxfa_ffsignature.cpp b/xfa/fxfa/cxfa_ffsignature.cpp
index 6e56b38..b138991 100644
--- a/xfa/fxfa/cxfa_ffsignature.cpp
+++ b/xfa/fxfa/cxfa_ffsignature.cpp
@@ -14,26 +14,27 @@
 
 CXFA_FFSignature::CXFA_FFSignature(CXFA_Node* pNode) : CXFA_FFField(pNode) {}
 
-CXFA_FFSignature::~CXFA_FFSignature() {}
+CXFA_FFSignature::~CXFA_FFSignature() = default;
 
 bool CXFA_FFSignature::LoadWidget() {
+  ASSERT(!IsLoaded());
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFSignature::RenderWidget(CXFA_Graphics* pGS,
                                     const CFX_Matrix& matrix,
-                                    uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                    HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
 
-  DrawBorder(pGS, m_pNode->GetWidgetAcc()->GetUIBorder(), m_rtUI, mtRotate);
+  DrawBorder(pGS, m_pNode->GetUIBorder(), m_rtUI, mtRotate);
   RenderCaption(pGS, &mtRotate);
-  DrawHighlight(pGS, &mtRotate, dwStatus, false);
+  DrawHighlight(pGS, &mtRotate, highlight, kSquareShape);
 }
 
 bool CXFA_FFSignature::OnMouseEnter() {
@@ -44,6 +45,12 @@
   return false;
 }
 
+bool CXFA_FFSignature::AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                                const CFX_PointF& point,
+                                                FWL_MouseCommand command) {
+  return false;
+}
+
 bool CXFA_FFSignature::OnLButtonDown(uint32_t dwFlags,
                                      const CFX_PointF& point) {
   return false;
@@ -94,12 +101,10 @@
   return false;
 }
 
-FWL_WidgetHit CXFA_FFSignature::OnHitTest(const CFX_PointF& point) {
-  if (m_pNormalWidget &&
-      m_pNormalWidget->HitTest(FWLToClient(point)) != FWL_WidgetHit::Unknown) {
+FWL_WidgetHit CXFA_FFSignature::HitTest(const CFX_PointF& point) {
+  auto* pNorm = GetNormalWidget();
+  if (pNorm && pNorm->HitTest(FWLToClient(point)) != FWL_WidgetHit::Unknown)
     return FWL_WidgetHit::Client;
-  }
-
   if (!GetRectWithoutRotate().Contains(point))
     return FWL_WidgetHit::Unknown;
   if (m_rtCaption.Contains(point))
diff --git a/xfa/fxfa/cxfa_ffsignature.h b/xfa/fxfa/cxfa_ffsignature.h
index bd09576..1aa00b6 100644
--- a/xfa/fxfa/cxfa_ffsignature.h
+++ b/xfa/fxfa/cxfa_ffsignature.h
@@ -17,8 +17,11 @@
   // CXFA_FFField
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
   bool LoadWidget() override;
+  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                const CFX_PointF& point,
+                                FWL_MouseCommand command) override;
   bool OnMouseEnter() override;
   bool OnMouseExit() override;
   bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
@@ -31,12 +34,11 @@
   bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point) override;
-
   bool OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) override;
   bool OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) override;
   bool OnChar(uint32_t dwChar, uint32_t dwFlags) override;
-  FWL_WidgetHit OnHitTest(const CFX_PointF& point) override;
   bool OnSetCursor(const CFX_PointF& point) override;
+  FWL_WidgetHit HitTest(const CFX_PointF& point) override;
   FormFieldType GetFormFieldType() override;
 };
 
diff --git a/xfa/fxfa/cxfa_ffsubform.cpp b/xfa/fxfa/cxfa_ffsubform.cpp
deleted file mode 100644
index 97a0750..0000000
--- a/xfa/fxfa/cxfa_ffsubform.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_ffsubform.h"
-
-#include "xfa/fxfa/cxfa_ffapp.h"
-#include "xfa/fxfa/cxfa_ffdoc.h"
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-
-CXFA_FFSubForm::CXFA_FFSubForm(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
-
-CXFA_FFSubForm::~CXFA_FFSubForm() {}
diff --git a/xfa/fxfa/cxfa_ffsubform.h b/xfa/fxfa/cxfa_ffsubform.h
deleted file mode 100644
index a69b571..0000000
--- a/xfa/fxfa/cxfa_ffsubform.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_FFSUBFORM_H_
-#define XFA_FXFA_CXFA_FFSUBFORM_H_
-
-#include "xfa/fxfa/cxfa_ffpageview.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-
-class CXFA_FFSubForm : public CXFA_FFWidget {
- public:
-  explicit CXFA_FFSubForm(CXFA_Node* pNode);
-  ~CXFA_FFSubForm() override;
-};
-
-#endif  // XFA_FXFA_CXFA_FFSUBFORM_H_
diff --git a/xfa/fxfa/cxfa_fftext.cpp b/xfa/fxfa/cxfa_fftext.cpp
index 7ae3cb6..f4ddbe0 100644
--- a/xfa/fxfa/cxfa_fftext.cpp
+++ b/xfa/fxfa/cxfa_fftext.cpp
@@ -6,36 +6,34 @@
 
 #include "xfa/fxfa/cxfa_fftext.h"
 
-#include "xfa/fwl/fwl_widgetdef.h"
+#include "xfa/fgas/layout/cfx_linkuserdata.h"
 #include "xfa/fwl/fwl_widgethit.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
-#include "xfa/fxfa/cxfa_ffdraw.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_linkuserdata.h"
 #include "xfa/fxfa/cxfa_pieceline.h"
 #include "xfa/fxfa/cxfa_textlayout.h"
 #include "xfa/fxfa/cxfa_textpiece.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
 #include "xfa/fxgraphics/cxfa_graphics.h"
 
-CXFA_FFText::CXFA_FFText(CXFA_Node* pNode) : CXFA_FFDraw(pNode) {}
+CXFA_FFText::CXFA_FFText(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}
 
 CXFA_FFText::~CXFA_FFText() {}
 
 void CXFA_FFText::RenderWidget(CXFA_Graphics* pGS,
                                const CFX_Matrix& matrix,
-                               uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                               HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CFX_Matrix mtRotate = GetRotateMatrix();
   mtRotate.Concat(matrix);
 
-  CXFA_FFWidget::RenderWidget(pGS, mtRotate, dwStatus);
+  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
 
-  CXFA_TextLayout* pTextLayout = m_pNode->GetWidgetAcc()->GetTextLayout();
+  CXFA_TextLayout* pTextLayout = m_pNode->GetTextLayout();
   if (!pTextLayout)
     return;
 
@@ -43,9 +41,9 @@
   CFX_RectF rtText = GetRectWithoutRotate();
   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
   if (margin) {
-    CXFA_LayoutItem* pItem = this;
+    CXFA_ContentLayoutItem* pItem = GetLayoutItem();
     if (!pItem->GetPrev() && !pItem->GetNext()) {
-      XFA_RectWithoutMargin(rtText, margin);
+      XFA_RectWithoutMargin(&rtText, margin);
     } else {
       float fTopInset = 0;
       float fBottomInset = 0;
@@ -62,24 +60,25 @@
   CFX_Matrix mt(1, 0, 0, 1, rtText.left, rtText.top);
   CFX_RectF rtClip = mtRotate.TransformRect(rtText);
   mt.Concat(mtRotate);
-  pTextLayout->DrawString(pRenderDevice, mt, rtClip, GetIndex());
+  pTextLayout->DrawString(pRenderDevice, mt, rtClip,
+                          GetLayoutItem()->GetIndex());
 }
 
 bool CXFA_FFText::IsLoaded() {
-  CXFA_TextLayout* pTextLayout = m_pNode->GetWidgetAcc()->GetTextLayout();
-  return pTextLayout && !pTextLayout->m_bHasBlock;
+  CXFA_TextLayout* pTextLayout = m_pNode->GetTextLayout();
+  return pTextLayout && !pTextLayout->HasBlock();
 }
 
 bool CXFA_FFText::PerformLayout() {
-  CXFA_FFDraw::PerformLayout();
-  CXFA_TextLayout* pTextLayout = m_pNode->GetWidgetAcc()->GetTextLayout();
+  CXFA_FFWidget::PerformLayout();
+  CXFA_TextLayout* pTextLayout = m_pNode->GetTextLayout();
   if (!pTextLayout)
     return false;
-  if (!pTextLayout->m_bHasBlock)
+  if (!pTextLayout->HasBlock())
     return true;
 
-  pTextLayout->m_Blocks.clear();
-  CXFA_LayoutItem* pItem = this;
+  pTextLayout->ClearBlocks();
+  CXFA_ContentLayoutItem* pItem = GetLayoutItem();
   if (!pItem->GetPrev() && !pItem->GetNext())
     return true;
 
@@ -96,11 +95,16 @@
     pTextLayout->ItemBlocks(rtText, pItem->GetIndex());
     pItem = pItem->GetNext();
   }
-  pTextLayout->m_bHasBlock = false;
+  pTextLayout->ResetHasBlock();
   return true;
 }
 
-bool CXFA_FFText::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
+bool CXFA_FFText::AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                           const CFX_PointF& point,
+                                           FWL_MouseCommand command) {
+  if (command != FWL_MouseCommand::LeftButtonDown)
+    return false;
+
   if (!GetRectWithoutRotate().Contains(point))
     return false;
 
@@ -108,6 +112,10 @@
   if (!wsURLContent)
     return false;
 
+  return true;
+}
+
+bool CXFA_FFText::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
   SetButtonDown(true);
   return true;
 }
@@ -130,7 +138,7 @@
   return true;
 }
 
-FWL_WidgetHit CXFA_FFText::OnHitTest(const CFX_PointF& point) {
+FWL_WidgetHit CXFA_FFText::HitTest(const CFX_PointF& point) {
   if (!GetRectWithoutRotate().Contains(point))
     return FWL_WidgetHit::Unknown;
   if (!GetLinkURLAtPoint(point))
@@ -139,7 +147,7 @@
 }
 
 const wchar_t* CXFA_FFText::GetLinkURLAtPoint(const CFX_PointF& point) {
-  CXFA_TextLayout* pTextLayout = m_pNode->GetWidgetAcc()->GetTextLayout();
+  CXFA_TextLayout* pTextLayout = m_pNode->GetTextLayout();
   if (!pTextLayout)
     return nullptr;
 
diff --git a/xfa/fxfa/cxfa_fftext.h b/xfa/fxfa/cxfa_fftext.h
index 8c32183..92eafb1 100644
--- a/xfa/fxfa/cxfa_fftext.h
+++ b/xfa/fxfa/cxfa_fftext.h
@@ -7,21 +7,24 @@
 #ifndef XFA_FXFA_CXFA_FFTEXT_H_
 #define XFA_FXFA_CXFA_FFTEXT_H_
 
-#include "xfa/fxfa/cxfa_ffdraw.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
 
-class CXFA_FFText : public CXFA_FFDraw {
+class CXFA_FFText final : public CXFA_FFWidget {
  public:
   explicit CXFA_FFText(CXFA_Node* pNode);
   ~CXFA_FFText() override;
 
   // CXFA_FFWidget
+  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                const CFX_PointF& point,
+                                FWL_MouseCommand command) override;
   bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) override;
-  FWL_WidgetHit OnHitTest(const CFX_PointF& point) override;
+  FWL_WidgetHit HitTest(const CFX_PointF& point) override;
   void RenderWidget(CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
-                    uint32_t dwStatus) override;
+                    HighlightOption highlight) override;
   bool IsLoaded() override;
   bool PerformLayout() override;
 
diff --git a/xfa/fxfa/cxfa_fftextedit.cpp b/xfa/fxfa/cxfa_fftextedit.cpp
index fad9a9b..7b1c93b 100644
--- a/xfa/fxfa/cxfa_fftextedit.cpp
+++ b/xfa/fxfa/cxfa_fftextedit.cpp
@@ -8,17 +8,19 @@
 
 #include <utility>
 
+#include "third_party/base/ptr_util.h"
 #include "xfa/fwl/cfwl_datetimepicker.h"
 #include "xfa/fwl/cfwl_edit.h"
-#include "xfa/fwl/cfwl_eventcheckword.h"
 #include "xfa/fwl/cfwl_eventtarget.h"
-#include "xfa/fwl/cfwl_eventtextchanged.h"
+#include "xfa/fwl/cfwl_eventtextwillchange.h"
 #include "xfa/fwl/cfwl_messagekillfocus.h"
 #include "xfa/fwl/cfwl_messagesetfocus.h"
 #include "xfa/fwl/cfwl_notedriver.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_ffdocview.h"
+#include "xfa/fxfa/parser/cxfa_barcode.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
 
@@ -30,41 +32,40 @@
 
 }  // namespace
 
-CXFA_FFTextEdit::CXFA_FFTextEdit(CXFA_Node* pNode)
-    : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
+CXFA_FFTextEdit::CXFA_FFTextEdit(CXFA_Node* pNode) : CXFA_FFField(pNode) {}
 
 CXFA_FFTextEdit::~CXFA_FFTextEdit() {
-  if (m_pNormalWidget) {
+  if (GetNormalWidget()) {
     CFWL_NoteDriver* pNoteDriver =
-        m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-    pNoteDriver->UnregisterEventTarget(m_pNormalWidget.get());
+        GetNormalWidget()->GetOwnerApp()->GetNoteDriver();
+    pNoteDriver->UnregisterEventTarget(GetNormalWidget());
   }
 }
 
 bool CXFA_FFTextEdit::LoadWidget() {
   auto pNewWidget = pdfium::MakeUnique<CFWL_Edit>(
       GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
+  ASSERT(!IsLoaded());
   CFWL_Edit* pFWLEdit = pNewWidget.get();
-  m_pNormalWidget = std::move(pNewWidget);
-  m_pNormalWidget->SetLayoutItem(this);
+  SetNormalWidget(std::move(pNewWidget));
+  pFWLEdit->SetAdapterIface(this);
 
-  CFWL_NoteDriver* pNoteDriver =
-      m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
-  pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
-                                   m_pNormalWidget.get());
-  m_pOldDelegate = m_pNormalWidget->GetDelegate();
-  m_pNormalWidget->SetDelegate(this);
-  m_pNormalWidget->LockUpdate();
-  UpdateWidgetProperty();
+  CFWL_NoteDriver* pNoteDriver = pFWLEdit->GetOwnerApp()->GetNoteDriver();
+  pNoteDriver->RegisterEventTarget(pFWLEdit, pFWLEdit);
+  m_pOldDelegate = pFWLEdit->GetDelegate();
+  pFWLEdit->SetDelegate(this);
 
-  pFWLEdit->SetText(
-      m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Display));
-  m_pNormalWidget->UnlockUpdate();
+  {
+    CFWL_Widget::ScopedUpdateLock update_lock(pFWLEdit);
+    UpdateWidgetProperty();
+    pFWLEdit->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
+  }
+
   return CXFA_FFField::LoadWidget();
 }
 
 void CXFA_FFTextEdit::UpdateWidgetProperty() {
-  CFWL_Edit* pWidget = static_cast<CFWL_Edit*>(m_pNormalWidget.get());
+  CFWL_Edit* pWidget = ToEdit(GetNormalWidget());
   if (!pWidget)
     return;
 
@@ -72,13 +73,13 @@
   uint32_t dwExtendedStyle =
       FWL_STYLEEXT_EDT_ShowScrollbarFocus | FWL_STYLEEXT_EDT_OuterScrollbar;
   dwExtendedStyle |= UpdateUIProperty();
-  if (m_pNode->GetWidgetAcc()->IsMultiLine()) {
+  if (m_pNode->IsMultiLine()) {
     dwExtendedStyle |= FWL_STYLEEXT_EDT_MultiLine | FWL_STYLEEXT_EDT_WantReturn;
-    if (!m_pNode->GetWidgetAcc()->IsVerticalScrollPolicyOff()) {
+    if (!m_pNode->IsVerticalScrollPolicyOff()) {
       dwStyle |= FWL_WGTSTYLE_VScroll;
       dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoVScroll;
     }
-  } else if (!m_pNode->GetWidgetAcc()->IsHorizontalScrollPolicyOff()) {
+  } else if (!m_pNode->IsHorizontalScrollPolicyOff()) {
     dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoHScroll;
   }
   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive()) {
@@ -88,11 +89,11 @@
 
   XFA_Element eType;
   int32_t iMaxChars;
-  std::tie(eType, iMaxChars) = m_pNode->GetWidgetAcc()->GetMaxChars();
+  std::tie(eType, iMaxChars) = m_pNode->GetMaxChars();
   if (eType == XFA_Element::ExData)
     iMaxChars = 0;
 
-  Optional<int32_t> numCells = m_pNode->GetWidgetAcc()->GetNumberOfCells();
+  Optional<int32_t> numCells = m_pNode->GetNumberOfCells();
   if (!numCells) {
     pWidget->SetLimit(iMaxChars);
   } else if (*numCells == 0) {
@@ -104,46 +105,54 @@
   }
 
   dwExtendedStyle |= GetAlignment();
-  m_pNormalWidget->ModifyStyles(dwStyle, 0xFFFFFFFF);
-  m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStyles(dwStyle, 0xFFFFFFFF);
+  GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
+}
+
+bool CXFA_FFTextEdit::AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                               const CFX_PointF& point,
+                                               FWL_MouseCommand command) {
+  if (command == FWL_MouseCommand::RightButtonDown && !m_pNode->IsOpenAccess())
+    return false;
+  if (!PtInActiveRect(point))
+    return false;
+
+  return true;
 }
 
 bool CXFA_FFTextEdit::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!PtInActiveRect(point))
-    return false;
+  ObservedPtr<CXFA_FFTextEdit> pWatched(this);
   if (!IsFocused()) {
-    m_dwStatus |= XFA_WidgetStatus_Focused;
+    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
     UpdateFWLData();
-    AddInvalidateRect();
-  }
+    if (!pWatched)
+      return false;
 
+    InvalidateRect();
+  }
   SetButtonDown(true);
-  CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get());
-  ms.m_dwCmd = FWL_MouseCommand::LeftButtonDown;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      GetNormalWidget(), FWL_MouseCommand::LeftButtonDown, dwFlags,
+      FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFTextEdit::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
-  if (!m_pNode->IsOpenAccess())
-    return false;
-  if (!PtInActiveRect(point))
-    return false;
+  ObservedPtr<CXFA_FFTextEdit> pWatched(this);
   if (!IsFocused()) {
-    m_dwStatus |= XFA_WidgetStatus_Focused;
+    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
     UpdateFWLData();
-    AddInvalidateRect();
-  }
+    if (!pWatched)
+      return false;
 
+    InvalidateRect();
+  }
   SetButtonDown(true);
-  CFWL_MessageMouse ms(nullptr, nullptr);
-  ms.m_dwCmd = FWL_MouseCommand::RightButtonDown;
-  ms.m_dwFlags = dwFlags;
-  ms.m_pos = FWLToClient(point);
-  TranslateFWLMessage(&ms);
-  return true;
+  SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
+      nullptr, FWL_MouseCommand::RightButtonDown, dwFlags, FWLToClient(point)));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFTextEdit::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
@@ -155,37 +164,57 @@
 }
 
 bool CXFA_FFTextEdit::OnSetFocus(CXFA_FFWidget* pOldWidget) {
-  m_dwStatus &= ~XFA_WidgetStatus_TextEditValueChanged;
+  ObservedPtr<CXFA_FFTextEdit> pWatched(this);
+  ObservedPtr<CXFA_FFWidget> pOldWatched(pOldWidget);
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_TextEditValueChanged);
   if (!IsFocused()) {
-    m_dwStatus |= XFA_WidgetStatus_Focused;
+    GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
     UpdateFWLData();
-    AddInvalidateRect();
+    if (!pWatched)
+      return false;
+
+    InvalidateRect();
   }
-  CXFA_FFWidget::OnSetFocus(pOldWidget);
-  CFWL_MessageSetFocus ms(nullptr, m_pNormalWidget.get());
-  TranslateFWLMessage(&ms);
-  return true;
+  if (!CXFA_FFWidget::OnSetFocus(pOldWatched.Get()))
+    return false;
+
+  SendMessageToFWLWidget(
+      pdfium::MakeUnique<CFWL_MessageSetFocus>(nullptr, GetNormalWidget()));
+
+  return !!pWatched;
 }
 
 bool CXFA_FFTextEdit::OnKillFocus(CXFA_FFWidget* pNewWidget) {
-  CFWL_MessageKillFocus ms(nullptr, m_pNormalWidget.get());
-  TranslateFWLMessage(&ms);
-  m_dwStatus &= ~XFA_WidgetStatus_Focused;
+  ObservedPtr<CXFA_FFWidget> pWatched(this);
+  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
+  SendMessageToFWLWidget(
+      pdfium::MakeUnique<CFWL_MessageKillFocus>(nullptr, GetNormalWidget()));
 
+  if (!pWatched)
+    return false;
+
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_Focused);
   SetEditScrollOffset();
   ProcessCommittedData();
   UpdateFWLData();
-  AddInvalidateRect();
-  CXFA_FFWidget::OnKillFocus(pNewWidget);
+  InvalidateRect();
+  if (!pWatched)
+    return false;
 
-  m_dwStatus &= ~XFA_WidgetStatus_TextEditValueChanged;
+  if (!CXFA_FFWidget::OnKillFocus(pNewWatched.Get()))
+    return false;
+
+  if (!pWatched)
+    return false;
+
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_TextEditValueChanged);
   return true;
 }
 
 bool CXFA_FFTextEdit::CommitData() {
-  WideString wsText = static_cast<CFWL_Edit*>(m_pNormalWidget.get())->GetText();
-  if (m_pNode->GetWidgetAcc()->SetValue(XFA_VALUEPICTURE_Edit, wsText)) {
-    m_pNode->GetWidgetAcc()->UpdateUIDisplay(GetDoc()->GetDocView(), this);
+  WideString wsText = ToEdit(GetNormalWidget())->GetText();
+  if (m_pNode->SetValue(XFA_VALUEPICTURE_Edit, wsText)) {
+    GetDoc()->GetDocView()->UpdateUIDisplay(m_pNode.Get(), this);
     return true;
   }
   ValidateNumberField(wsText);
@@ -193,23 +222,22 @@
 }
 
 void CXFA_FFTextEdit::ValidateNumberField(const WideString& wsText) {
-  CXFA_WidgetAcc* pAcc = GetNode()->GetWidgetAcc();
-  if (!pAcc || pAcc->GetUIType() != XFA_Element::NumericEdit)
+  if (GetNode()->GetFFWidgetType() != XFA_FFWidgetType::kNumericEdit)
     return;
 
-  IXFA_AppProvider* pAppProvider = GetApp()->GetAppProvider();
+  IXFA_AppProvider* pAppProvider = GetAppProvider();
   if (!pAppProvider)
     return;
 
-  WideString wsSomField = pAcc->GetNode()->GetSOMExpression();
-  pAppProvider->MsgBox(WideString::Format(L"%ls can not contain %ls",
-                                          wsText.c_str(), wsSomField.c_str()),
-                       pAppProvider->GetAppTitle(), XFA_MBICON_Error,
-                       XFA_MB_OK);
+  WideString wsSomField = GetNode()->GetSOMExpression();
+  pAppProvider->MsgBox(
+      wsText + WideString::FromASCII(" can not contain ") + wsSomField,
+      pAppProvider->GetAppTitle(), static_cast<uint32_t>(AlertIcon::kError),
+      static_cast<uint32_t>(AlertButton::kOK));
 }
 
 bool CXFA_FFTextEdit::IsDataChanged() {
-  return (m_dwStatus & XFA_WidgetStatus_TextEditValueChanged) != 0;
+  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_TextEditValueChanged);
 }
 
 uint32_t CXFA_FFTextEdit::GetAlignment() {
@@ -219,16 +247,16 @@
 
   uint32_t dwExtendedStyle = 0;
   switch (para->GetHorizontalAlign()) {
-    case XFA_AttributeEnum::Center:
+    case XFA_AttributeValue::Center:
       dwExtendedStyle |= FWL_STYLEEXT_EDT_HCenter;
       break;
-    case XFA_AttributeEnum::Justify:
+    case XFA_AttributeValue::Justify:
       dwExtendedStyle |= FWL_STYLEEXT_EDT_Justified;
       break;
-    case XFA_AttributeEnum::JustifyAll:
-    case XFA_AttributeEnum::Radix:
+    case XFA_AttributeValue::JustifyAll:
+    case XFA_AttributeValue::Radix:
       break;
-    case XFA_AttributeEnum::Right:
+    case XFA_AttributeValue::Right:
       dwExtendedStyle |= FWL_STYLEEXT_EDT_HFar;
       break;
     default:
@@ -237,10 +265,10 @@
   }
 
   switch (para->GetVerticalAlign()) {
-    case XFA_AttributeEnum::Middle:
+    case XFA_AttributeValue::Middle:
       dwExtendedStyle |= FWL_STYLEEXT_EDT_VCenter;
       break;
-    case XFA_AttributeEnum::Bottom:
+    case XFA_AttributeValue::Bottom:
       dwExtendedStyle |= FWL_STYLEEXT_EDT_VFar;
       break;
     default:
@@ -251,83 +279,77 @@
 }
 
 bool CXFA_FFTextEdit::UpdateFWLData() {
-  if (!m_pNormalWidget)
+  if (!GetNormalWidget())
     return false;
 
-  CFWL_Edit* pEdit = static_cast<CFWL_Edit*>(m_pNormalWidget.get());
+  CFWL_Edit* pEdit = ToEdit(GetNormalWidget());
   XFA_VALUEPICTURE eType = XFA_VALUEPICTURE_Display;
   if (IsFocused())
     eType = XFA_VALUEPICTURE_Edit;
 
   bool bUpdate = false;
-  if (m_pNode->GetWidgetAcc()->GetUIType() == XFA_Element::TextEdit &&
-      !m_pNode->GetWidgetAcc()->GetNumberOfCells()) {
+  if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kTextEdit &&
+      !m_pNode->GetNumberOfCells()) {
     XFA_Element elementType;
     int32_t iMaxChars;
-    std::tie(elementType, iMaxChars) = m_pNode->GetWidgetAcc()->GetMaxChars();
+    std::tie(elementType, iMaxChars) = m_pNode->GetMaxChars();
     if (elementType == XFA_Element::ExData)
       iMaxChars = eType == XFA_VALUEPICTURE_Edit ? iMaxChars : 0;
     if (pEdit->GetLimit() != iMaxChars) {
       pEdit->SetLimit(iMaxChars);
       bUpdate = true;
     }
-  } else if (m_pNode->GetWidgetAcc()->GetUIType() == XFA_Element::Barcode) {
+  } else if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kBarcode) {
     int32_t nDataLen = 0;
-    if (eType == XFA_VALUEPICTURE_Edit)
-      nDataLen = m_pNode->GetBarcodeAttribute_DataLength().value_or(0);
+    if (eType == XFA_VALUEPICTURE_Edit) {
+      nDataLen = static_cast<CXFA_Barcode*>(m_pNode->GetUIChildNode())
+                     ->GetDataLength()
+                     .value_or(0);
+    }
 
     pEdit->SetLimit(nDataLen);
     bUpdate = true;
   }
 
-  WideString wsText = m_pNode->GetWidgetAcc()->GetValue(eType);
+  WideString wsText = m_pNode->GetValue(eType);
   WideString wsOldText = pEdit->GetText();
   if (wsText != wsOldText || (eType == XFA_VALUEPICTURE_Edit && bUpdate)) {
-    pEdit->SetText(wsText);
+    pEdit->SetTextSkipNotify(wsText);
     bUpdate = true;
   }
   if (bUpdate)
-    m_pNormalWidget->Update();
+    GetNormalWidget()->Update();
 
   return true;
 }
 
-void CXFA_FFTextEdit::OnTextChanged(CFWL_Widget* pWidget,
-                                    const WideString& wsChanged,
-                                    const WideString& wsPrevText) {
-  m_dwStatus |= XFA_WidgetStatus_TextEditValueChanged;
+void CXFA_FFTextEdit::OnTextWillChange(CFWL_Widget* pWidget,
+                                       CFWL_EventTextWillChange* event) {
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_TextEditValueChanged);
+
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Change;
-  eParam.m_wsChange = wsChanged;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  eParam.m_wsPrevText = wsPrevText;
-  CFWL_Edit* pEdit = static_cast<CFWL_Edit*>(m_pNormalWidget.get());
-  if (m_pNode->GetWidgetAcc()->GetUIType() == XFA_Element::DateTimeEdit) {
-    CFWL_DateTimePicker* pDateTime = (CFWL_DateTimePicker*)pEdit;
-    eParam.m_wsNewText = pDateTime->GetEditText();
-    if (pDateTime->HasSelection()) {
-      size_t count;
-      std::tie(eParam.m_iSelStart, count) = pDateTime->GetSelection();
-      eParam.m_iSelEnd = eParam.m_iSelStart + count;
-    }
-  } else {
-    eParam.m_wsNewText = pEdit->GetText();
-    if (pEdit->HasSelection())
-      std::tie(eParam.m_iSelStart, eParam.m_iSelEnd) = pEdit->GetSelection();
-  }
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, &eParam);
+  eParam.m_wsChange = event->change_text;
+  eParam.m_pTarget = m_pNode.Get();
+  eParam.m_wsPrevText = event->previous_text;
+  eParam.m_iSelStart = static_cast<int32_t>(event->selection_start);
+  eParam.m_iSelEnd = static_cast<int32_t>(event->selection_end);
+
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
+
+  // Copy the data back out of the EventParam and into the TextChanged event so
+  // it can propagate back to the calling widget.
+  event->cancelled = eParam.m_bCancelAction;
+  event->change_text = std::move(eParam.m_wsChange);
+  event->selection_start = static_cast<size_t>(eParam.m_iSelStart);
+  event->selection_end = static_cast<size_t>(eParam.m_iSelEnd);
 }
 
 void CXFA_FFTextEdit::OnTextFull(CFWL_Widget* pWidget) {
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Full;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Full, &eParam);
-}
-
-bool CXFA_FFTextEdit::CheckWord(const ByteStringView& sWord) {
-  return sWord.IsEmpty() ||
-         m_pNode->GetWidgetAcc()->GetUIType() != XFA_Element::TextEdit;
+  eParam.m_pTarget = m_pNode.Get();
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Full, &eParam);
 }
 
 void CXFA_FFTextEdit::OnProcessMessage(CFWL_Message* pMessage) {
@@ -337,23 +359,13 @@
 void CXFA_FFTextEdit::OnProcessEvent(CFWL_Event* pEvent) {
   CXFA_FFField::OnProcessEvent(pEvent);
   switch (pEvent->GetType()) {
-    case CFWL_Event::Type::TextChanged: {
-      CFWL_EventTextChanged* event =
-          static_cast<CFWL_EventTextChanged*>(pEvent);
-      WideString wsChange;
-      OnTextChanged(m_pNormalWidget.get(), wsChange, event->wsPrevText);
+    case CFWL_Event::Type::TextWillChange:
+      OnTextWillChange(GetNormalWidget(),
+                       static_cast<CFWL_EventTextWillChange*>(pEvent));
       break;
-    }
-    case CFWL_Event::Type::TextFull: {
-      OnTextFull(m_pNormalWidget.get());
+    case CFWL_Event::Type::TextFull:
+      OnTextFull(GetNormalWidget());
       break;
-    }
-    case CFWL_Event::Type::CheckWord: {
-      WideString wstr(L"FWL_EVENT_DTP_SelectChanged");
-      CFWL_EventCheckWord* event = static_cast<CFWL_EventCheckWord*>(pEvent);
-      event->bCheckWord = CheckWord(event->bsWord.AsStringView());
-      break;
-    }
     default:
       break;
   }
@@ -366,62 +378,66 @@
 }
 
 bool CXFA_FFTextEdit::CanUndo() {
-  return ToEdit(m_pNormalWidget.get())->CanUndo();
+  return ToEdit(GetNormalWidget())->CanUndo();
 }
 
 bool CXFA_FFTextEdit::CanRedo() {
-  return ToEdit(m_pNormalWidget.get())->CanRedo();
+  return ToEdit(GetNormalWidget())->CanRedo();
 }
 
 bool CXFA_FFTextEdit::Undo() {
-  return ToEdit(m_pNormalWidget.get())->Undo();
+  return ToEdit(GetNormalWidget())->Undo();
 }
 
 bool CXFA_FFTextEdit::Redo() {
-  return ToEdit(m_pNormalWidget.get())->Redo();
+  return ToEdit(GetNormalWidget())->Redo();
 }
 
 bool CXFA_FFTextEdit::CanCopy() {
-  return ToEdit(m_pNormalWidget.get())->HasSelection();
+  return ToEdit(GetNormalWidget())->HasSelection();
 }
 
 bool CXFA_FFTextEdit::CanCut() {
-  if (ToEdit(m_pNormalWidget.get())->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)
+  if (ToEdit(GetNormalWidget())->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)
     return false;
-  return ToEdit(m_pNormalWidget.get())->HasSelection();
+  return ToEdit(GetNormalWidget())->HasSelection();
 }
 
 bool CXFA_FFTextEdit::CanPaste() {
-  return !(ToEdit(m_pNormalWidget.get())->GetStylesEx() &
+  return !(ToEdit(GetNormalWidget())->GetStylesEx() &
            FWL_STYLEEXT_EDT_ReadOnly);
 }
 
 bool CXFA_FFTextEdit::CanSelectAll() {
-  return ToEdit(m_pNormalWidget.get())->GetTextLength() > 0;
+  return ToEdit(GetNormalWidget())->GetTextLength() > 0;
 }
 
 Optional<WideString> CXFA_FFTextEdit::Copy() {
-  return ToEdit(m_pNormalWidget.get())->Copy();
+  return ToEdit(GetNormalWidget())->Copy();
 }
 
 Optional<WideString> CXFA_FFTextEdit::Cut() {
-  return ToEdit(m_pNormalWidget.get())->Cut();
+  return ToEdit(GetNormalWidget())->Cut();
 }
 
 bool CXFA_FFTextEdit::Paste(const WideString& wsPaste) {
-  return ToEdit(m_pNormalWidget.get())->Paste(wsPaste);
+  return ToEdit(GetNormalWidget())->Paste(wsPaste);
 }
 
 void CXFA_FFTextEdit::SelectAll() {
-  ToEdit(m_pNormalWidget.get())->SelectAll();
+  ToEdit(GetNormalWidget())->SelectAll();
 }
 
 void CXFA_FFTextEdit::Delete() {
-  ToEdit(m_pNormalWidget.get())->ClearText();
+  ToEdit(GetNormalWidget())->ClearText();
 }
 
 void CXFA_FFTextEdit::DeSelect() {
-  ToEdit(m_pNormalWidget.get())->ClearSelection();
+  ToEdit(GetNormalWidget())->ClearSelection();
+}
+
+WideString CXFA_FFTextEdit::GetText() {
+  return ToEdit(GetNormalWidget())->GetText();
 }
 
 FormFieldType CXFA_FFTextEdit::GetFormFieldType() {
diff --git a/xfa/fxfa/cxfa_fftextedit.h b/xfa/fxfa/cxfa_fftextedit.h
index e8edb46..09b9434 100644
--- a/xfa/fxfa/cxfa_fftextedit.h
+++ b/xfa/fxfa/cxfa_fftextedit.h
@@ -9,13 +9,14 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/cxfa_fffield.h"
 
 class CFWL_Event;
+class CFWL_EventTextWillChange;
 class CFWL_Widget;
 class CFX_Matrix;
 class CXFA_FFWidget;
-class CXFA_WidgetAcc;
 class IFWL_WidgetDelegate;
 
 class CXFA_FFTextEdit : public CXFA_FFField {
@@ -26,21 +27,21 @@
   // CXFA_FFField
   bool LoadWidget() override;
   void UpdateWidgetProperty() override;
+  bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                const CFX_PointF& point,
+                                FWL_MouseCommand command) override;
   bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) override;
   bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) override;
-  bool OnSetFocus(CXFA_FFWidget* pOldWidget) override;
-  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override;
+  bool OnSetFocus(CXFA_FFWidget* pOldWidget) override WARN_UNUSED_RESULT;
+  bool OnKillFocus(CXFA_FFWidget* pNewWidget) override WARN_UNUSED_RESULT;
   void OnProcessMessage(CFWL_Message* pMessage) override;
   void OnProcessEvent(CFWL_Event* pEvent) override;
   void OnDrawWidget(CXFA_Graphics* pGraphics,
                     const CFX_Matrix& matrix) override;
 
-  void OnTextChanged(CFWL_Widget* pWidget,
-                     const WideString& wsChanged,
-                     const WideString& wsPrevText);
+  void OnTextWillChange(CFWL_Widget* pWidget, CFWL_EventTextWillChange* change);
   void OnTextFull(CFWL_Widget* pWidget);
-  bool CheckWord(const ByteStringView& sWord);
 
   // CXFA_FFWidget
   bool CanUndo() override;
@@ -57,12 +58,13 @@
   void SelectAll() override;
   void Delete() override;
   void DeSelect() override;
+  WideString GetText() override;
   FormFieldType GetFormFieldType() override;
 
  protected:
   uint32_t GetAlignment();
 
-  IFWL_WidgetDelegate* m_pOldDelegate;
+  UnownedPtr<IFWL_WidgetDelegate> m_pOldDelegate;
 
  private:
   bool CommitData() override;
diff --git a/xfa/fxfa/cxfa_ffwidget.cpp b/xfa/fxfa/cxfa_ffwidget.cpp
index 71c45c7..881218c 100644
--- a/xfa/fxfa/cxfa_ffwidget.cpp
+++ b/xfa/fxfa/cxfa_ffwidget.cpp
@@ -12,24 +12,25 @@
 #include <utility>
 #include <vector>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fxcodec/codec/ccodec_progressivedecoder.h"
 #include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/cfx_memorystream.h"
+#include "core/fxcodec/progressivedecoder.h"
 #include "core/fxcrt/maybe_owned.h"
 #include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "xfa/fwl/fwl_widgethit.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffpageview.h"
+#include "xfa/fxfa/cxfa_ffwidgethandler.h"
 #include "xfa/fxfa/cxfa_imagerenderer.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_border.h"
 #include "xfa/fxfa/parser/cxfa_box.h"
+#include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxfa/parser/cxfa_image.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxgraphics/cxfa_graphics.h"
@@ -41,16 +42,23 @@
                               int32_t iBitsPerComponent) {
   FXDIB_Format dibFormat = FXDIB_Argb;
   switch (type) {
-    case FXCODEC_IMAGE_BMP:
     case FXCODEC_IMAGE_JPG:
-    case FXCODEC_IMAGE_TIF: {
+#ifdef PDF_ENABLE_XFA_BMP
+    case FXCODEC_IMAGE_BMP:
+#endif  // PDF_ENABLE_XFA_BMP
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+#endif  // PDF_ENABLE_XFA_TIFF
+    {
       dibFormat = FXDIB_Rgb32;
       int32_t bpp = iComponents * iBitsPerComponent;
       if (bpp <= 24) {
         dibFormat = FXDIB_Rgb;
       }
     } break;
+#ifdef PDF_ENABLE_XFA_PNG
     case FXCODEC_IMAGE_PNG:
+#endif  // PDF_ENABLE_XFA_PNG
     default:
       break;
   }
@@ -59,9 +67,7 @@
 
 bool IsFXCodecErrorStatus(FXCODEC_STATUS status) {
   return (status == FXCODEC_STATUS_ERROR ||
-#ifdef PDF_ENABLE_XFA
           status == FXCODEC_STATUS_ERR_MEMORY ||
-#endif  // PDF_ENABLE_XFA
           status == FXCODEC_STATUS_ERR_READ ||
           status == FXCODEC_STATUS_ERR_FLUSH ||
           status == FXCODEC_STATUS_ERR_FORMAT ||
@@ -74,22 +80,20 @@
                    const CFX_RectF& rtImage,
                    const CFX_Matrix& matrix,
                    const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                   XFA_AttributeEnum iAspect,
-                   int32_t iImageXDpi,
-                   int32_t iImageYDpi,
-                   XFA_AttributeEnum iHorzAlign,
-                   XFA_AttributeEnum iVertAlign) {
+                   XFA_AttributeValue iAspect,
+                   const CFX_Size& dpi,
+                   XFA_AttributeValue iHorzAlign,
+                   XFA_AttributeValue iVertAlign) {
   if (rtImage.IsEmpty())
     return;
   if (!pDIBitmap || !pDIBitmap->GetBuffer())
     return;
 
-  CFX_RectF rtFit(
-      rtImage.TopLeft(),
-      XFA_UnitPx2Pt((float)pDIBitmap->GetWidth(), (float)iImageXDpi),
-      XFA_UnitPx2Pt((float)pDIBitmap->GetHeight(), (float)iImageYDpi));
+  CFX_RectF rtFit(rtImage.TopLeft(),
+                  XFA_UnitPx2Pt(pDIBitmap->GetWidth(), dpi.width),
+                  XFA_UnitPx2Pt(pDIBitmap->GetHeight(), dpi.height));
   switch (iAspect) {
-    case XFA_AttributeEnum::Fit: {
+    case XFA_AttributeValue::Fit: {
       float f1 = rtImage.height / rtFit.height;
       float f2 = rtImage.width / rtFit.width;
       f1 = std::min(f1, f2);
@@ -97,35 +101,35 @@
       rtFit.width = rtFit.width * f1;
       break;
     }
-    case XFA_AttributeEnum::Height: {
+    case XFA_AttributeValue::Height: {
       float f1 = rtImage.height / rtFit.height;
       rtFit.height = rtImage.height;
       rtFit.width = f1 * rtFit.width;
       break;
     }
-    case XFA_AttributeEnum::None:
+    case XFA_AttributeValue::None:
       rtFit.height = rtImage.height;
       rtFit.width = rtImage.width;
       break;
-    case XFA_AttributeEnum::Width: {
+    case XFA_AttributeValue::Width: {
       float f1 = rtImage.width / rtFit.width;
       rtFit.width = rtImage.width;
       rtFit.height = rtFit.height * f1;
       break;
     }
-    case XFA_AttributeEnum::Actual:
+    case XFA_AttributeValue::Actual:
     default:
       break;
   }
 
-  if (iHorzAlign == XFA_AttributeEnum::Center)
+  if (iHorzAlign == XFA_AttributeValue::Center)
     rtFit.left += (rtImage.width - rtFit.width) / 2;
-  else if (iHorzAlign == XFA_AttributeEnum::Right)
+  else if (iHorzAlign == XFA_AttributeValue::Right)
     rtFit.left = rtImage.right() - rtFit.width;
 
-  if (iVertAlign == XFA_AttributeEnum::Middle)
+  if (iVertAlign == XFA_AttributeValue::Middle)
     rtFit.top += (rtImage.height - rtFit.height) / 2;
-  else if (iVertAlign == XFA_AttributeEnum::Bottom)
+  else if (iVertAlign == XFA_AttributeValue::Bottom)
     rtFit.top = rtImage.bottom() - rtImage.height;
 
   CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice();
@@ -152,8 +156,8 @@
     FXCODEC_IMAGE_TYPE type,
     int32_t& iImageXDpi,
     int32_t& iImageYDpi) {
-  CCodec_ModuleMgr* pCodecMgr = CPDF_ModuleMgr::Get()->GetCodecModule();
-  std::unique_ptr<CCodec_ProgressiveDecoder> pProgressiveDecoder =
+  auto* pCodecMgr = fxcodec::ModuleMgr::GetInstance();
+  std::unique_ptr<ProgressiveDecoder> pProgressiveDecoder =
       pCodecMgr->CreateProgressiveDecoder();
 
   CFX_DIBAttribute dibAttr;
@@ -212,42 +216,46 @@
   return pBitmap;
 }
 
-void XFA_RectWithoutMargin(CFX_RectF& rt, const CXFA_Margin* margin, bool bUI) {
+void XFA_RectWithoutMargin(CFX_RectF* rt, const CXFA_Margin* margin) {
   if (!margin)
     return;
 
-  rt.Deflate(margin->GetLeftInset(), margin->GetTopInset(),
-             margin->GetRightInset(), margin->GetBottomInset());
+  rt->Deflate(margin->GetLeftInset(), margin->GetTopInset(),
+              margin->GetRightInset(), margin->GetBottomInset());
 }
 
 CXFA_FFWidget* XFA_GetWidgetFromLayoutItem(CXFA_LayoutItem* pLayoutItem) {
-  if (pLayoutItem->GetFormNode()->HasCreatedUIWidget())
-    return static_cast<CXFA_FFWidget*>(pLayoutItem);
-  return nullptr;
+  if (!pLayoutItem->GetFormNode()->HasCreatedUIWidget())
+    return nullptr;
+
+  return GetFFWidget(ToContentLayoutItem(pLayoutItem));
 }
 
-CXFA_CalcData::CXFA_CalcData() : m_iRefCount(0) {}
+CXFA_CalcData::CXFA_CalcData() = default;
 
-CXFA_CalcData::~CXFA_CalcData() {}
+CXFA_CalcData::~CXFA_CalcData() = default;
 
-CXFA_FFWidget::CXFA_FFWidget(CXFA_Node* node)
-    : CXFA_ContentLayoutItem(node), m_pNode(node) {}
+CXFA_FFWidget::CXFA_FFWidget(CXFA_Node* node) : m_pNode(node) {}
 
-CXFA_FFWidget::~CXFA_FFWidget() {}
+CXFA_FFWidget::~CXFA_FFWidget() = default;
 
 const CFWL_App* CXFA_FFWidget::GetFWLApp() {
   return GetPageView()->GetDocView()->GetDoc()->GetApp()->GetFWLApp();
 }
 
+CXFA_FFWidget* CXFA_FFWidget::GetNextFFWidget() const {
+  return GetFFWidget(GetLayoutItem()->GetNext());
+}
+
 const CFX_RectF& CXFA_FFWidget::GetWidgetRect() const {
-  if ((m_dwStatus & XFA_WidgetStatus_RectCached) == 0)
+  if (!GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_RectCached))
     RecacheWidgetRect();
   return m_rtWidget;
 }
 
 const CFX_RectF& CXFA_FFWidget::RecacheWidgetRect() const {
-  m_dwStatus |= XFA_WidgetStatus_RectCached;
-  m_rtWidget = GetRect(false);
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_RectCached);
+  m_rtWidget = GetLayoutItem()->GetRect(false);
   return m_rtWidget;
 }
 
@@ -275,24 +283,25 @@
   return rtWidget;
 }
 
-uint32_t CXFA_FFWidget::GetStatus() {
-  return m_dwStatus;
-}
-
 void CXFA_FFWidget::ModifyStatus(uint32_t dwAdded, uint32_t dwRemoved) {
-  m_dwStatus = (m_dwStatus & ~dwRemoved) | dwAdded;
+  GetLayoutItem()->ClearStatusBits(dwRemoved);
+  GetLayoutItem()->SetStatusBits(dwAdded);
 }
 
-CFX_RectF CXFA_FFWidget::GetBBox(uint32_t dwStatus, bool bDrawFocus) {
-  if (bDrawFocus || !m_pPageView)
+CXFA_FFField* CXFA_FFWidget::AsField() {
+  return nullptr;
+}
+
+CFX_RectF CXFA_FFWidget::GetBBox(FocusOption focus) {
+  if (focus == kDrawFocus || !m_pPageView)
     return CFX_RectF();
   return m_pPageView->GetPageViewRect();
 }
 
 void CXFA_FFWidget::RenderWidget(CXFA_Graphics* pGS,
                                  const CFX_Matrix& matrix,
-                                 uint32_t dwStatus) {
-  if (!IsMatchVisibleStatus(dwStatus))
+                                 HighlightOption highlight) {
+  if (!HasVisibleStatus())
     return;
 
   CXFA_Border* border = m_pNode->GetBorderIfExists();
@@ -301,9 +310,7 @@
 
   CFX_RectF rtBorder = GetRectWithoutRotate();
   CXFA_Margin* margin = border->GetMarginIfExists();
-  if (margin)
-    XFA_RectWithoutMargin(rtBorder, margin);
-
+  XFA_RectWithoutMargin(&rtBorder, margin);
   rtBorder.Normalize();
   DrawBorder(pGS, border, rtBorder, matrix);
 }
@@ -317,8 +324,6 @@
   return true;
 }
 
-void CXFA_FFWidget::UnloadWidget() {}
-
 bool CXFA_FFWidget::PerformLayout() {
   RecacheWidgetRect();
   return true;
@@ -330,6 +335,22 @@
 
 void CXFA_FFWidget::UpdateWidgetProperty() {}
 
+bool CXFA_FFWidget::HasEventUnderHandler(XFA_EVENTTYPE eEventType,
+                                         CXFA_FFWidgetHandler* pHandler) {
+  CXFA_Node* pNode = GetNode();
+  return pNode->IsWidgetReady() && pHandler->HasEvent(pNode, eEventType);
+}
+
+bool CXFA_FFWidget::ProcessEventUnderHandler(CXFA_EventParam* params,
+                                             CXFA_FFWidgetHandler* pHandler) {
+  CXFA_Node* pNode = GetNode();
+  if (!pNode->IsWidgetReady())
+    return false;
+
+  params->m_pTarget = pNode;
+  return pHandler->ProcessEvent(pNode, params) == XFA_EventError::kSuccess;
+}
+
 void CXFA_FFWidget::DrawBorder(CXFA_Graphics* pGS,
                                CXFA_Box* box,
                                const CFX_RectF& rtBorder,
@@ -347,10 +368,10 @@
     box->Draw(pGS, rtBorder, matrix, forceRound);
 }
 
-void CXFA_FFWidget::AddInvalidateRect() {
-  CFX_RectF rtWidget = GetBBox(XFA_WidgetStatus_Focused);
+void CXFA_FFWidget::InvalidateRect() {
+  CFX_RectF rtWidget = GetBBox(kDoNotDrawFocus);
   rtWidget.Inflate(2, 2);
-  m_pDocView->AddInvalidateRect(m_pPageView, rtWidget);
+  m_pDocView->InvalidateRect(m_pPageView.Get(), rtWidget);
 }
 
 bool CXFA_FFWidget::OnMouseEnter() {
@@ -361,6 +382,12 @@
   return false;
 }
 
+bool CXFA_FFWidget::AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                             const CFX_PointF& point,
+                                             FWL_MouseCommand command) {
+  return false;
+}
+
 bool CXFA_FFWidget::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
   return false;
 }
@@ -396,28 +423,48 @@
 }
 
 bool CXFA_FFWidget::OnSetFocus(CXFA_FFWidget* pOldWidget) {
-  CXFA_FFWidget* pParent = GetParent();
+  // OnSetFocus event may remove this widget.
+  ObservedPtr<CXFA_FFWidget> pWatched(this);
+  CXFA_FFWidget* pParent = GetFFWidget(ToContentLayoutItem(GetParent()));
   if (pParent && !pParent->IsAncestorOf(pOldWidget)) {
-    pParent->OnSetFocus(pOldWidget);
+    if (!pParent->OnSetFocus(pOldWidget))
+      return false;
   }
-  m_dwStatus |= XFA_WidgetStatus_Focused;
+  if (!pWatched)
+    return false;
+
+  GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
+
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Enter;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Enter, &eParam);
-  return true;
+  eParam.m_pTarget = m_pNode.Get();
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Enter, &eParam);
+
+  return !!pWatched;
 }
 
 bool CXFA_FFWidget::OnKillFocus(CXFA_FFWidget* pNewWidget) {
-  m_dwStatus &= ~XFA_WidgetStatus_Focused;
+  // OnKillFocus event may remove these widgets.
+  ObservedPtr<CXFA_FFWidget> pWatched(this);
+  ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
+  GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_Focused);
   EventKillFocus();
-  if (pNewWidget) {
-    CXFA_FFWidget* pParent = GetParent();
-    if (pParent && !pParent->IsAncestorOf(pNewWidget)) {
-      pParent->OnKillFocus(pNewWidget);
-    }
+  if (!pWatched)
+    return false;
+
+  if (!pNewWidget)
+    return true;
+
+  if (!pNewWatched)
+    return false;
+
+  // OnKillFocus event may remove |pNewWidget|.
+  CXFA_FFWidget* pParent = GetFFWidget(ToContentLayoutItem(GetParent()));
+  if (pParent && !pParent->IsAncestorOf(pNewWidget)) {
+    if (!pParent->OnKillFocus(pNewWidget))
+      return false;
   }
-  return true;
+  return pWatched && pNewWatched;
 }
 
 bool CXFA_FFWidget::OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) {
@@ -432,7 +479,7 @@
   return false;
 }
 
-FWL_WidgetHit CXFA_FFWidget::OnHitTest(const CFX_PointF& point) {
+FWL_WidgetHit CXFA_FFWidget::HitTest(const CFX_PointF& point) {
   return FWL_WidgetHit::Unknown;
 }
 
@@ -498,20 +545,14 @@
 
 void CXFA_FFWidget::DeSelect() {}
 
+WideString CXFA_FFWidget::GetText() {
+  return WideString();
+}
+
 FormFieldType CXFA_FFWidget::GetFormFieldType() {
   return FormFieldType::kXFA;
 }
 
-void CXFA_FFWidget::GetSuggestWords(CFX_PointF pointf,
-                                    std::vector<ByteString>* pWords) {
-  pWords->clear();
-}
-
-bool CXFA_FFWidget::ReplaceSpellCheckWord(CFX_PointF pointf,
-                                          const ByteStringView& bsReplace) {
-  return false;
-}
-
 CFX_PointF CXFA_FFWidget::Rotate2Normal(const CFX_PointF& point) {
   CFX_Matrix mt = GetRotateMatrix();
   if (mt.IsIdentity())
@@ -556,23 +597,40 @@
   return mt;
 }
 
+void CXFA_FFWidget::DisplayCaret(bool bVisible, const CFX_RectF* pRtAnchor) {
+  IXFA_DocEnvironment* pDocEnvironment = GetDoc()->GetDocEnvironment();
+  if (!pDocEnvironment)
+    return;
+
+  pDocEnvironment->DisplayCaret(this, bVisible, pRtAnchor);
+}
+
+void CXFA_FFWidget::GetBorderColorAndThickness(FX_ARGB* cr, float* fWidth) {
+  ASSERT(GetNode()->IsWidgetReady());
+  CXFA_Border* borderUI = GetNode()->GetUIBorder();
+  if (!borderUI)
+    return;
+
+  CXFA_Edge* edge = borderUI->GetEdgeIfExists(0);
+  if (!edge)
+    return;
+
+  *cr = edge->GetColor();
+  *fWidth = edge->GetThickness();
+}
+
 bool CXFA_FFWidget::IsLayoutRectEmpty() {
   CFX_RectF rtLayout = GetRectWithoutRotate();
   return rtLayout.width < 0.1f && rtLayout.height < 0.1f;
 }
 
-CXFA_FFWidget* CXFA_FFWidget::GetParent() {
+CXFA_LayoutItem* CXFA_FFWidget::GetParent() {
   CXFA_Node* pParentNode = m_pNode->GetParent();
-  if (pParentNode) {
-    CXFA_WidgetAcc* pParentWidgetAcc =
-        static_cast<CXFA_WidgetAcc*>(pParentNode->GetWidgetAcc());
-    if (pParentWidgetAcc) {
-      CXFA_LayoutProcessor* layout = GetDocView()->GetXFALayout();
-      return static_cast<CXFA_FFWidget*>(
-          layout->GetLayoutItem(pParentWidgetAcc->GetNode()));
-    }
-  }
-  return nullptr;
+  if (!pParentNode)
+    return nullptr;
+
+  CXFA_LayoutProcessor* layout = GetDocView()->GetXFALayout();
+  return layout->GetLayoutItem(pParentNode);
 }
 
 bool CXFA_FFWidget::IsAncestorOf(CXFA_FFWidget* pWidget) {
@@ -593,14 +651,6 @@
   return GetWidgetRect().Contains(point);
 }
 
-CXFA_FFDocView* CXFA_FFWidget::GetDocView() {
-  return m_pDocView;
-}
-
-void CXFA_FFWidget::SetDocView(CXFA_FFDocView* pDocView) {
-  m_pDocView = pDocView;
-}
-
 CXFA_FFDoc* CXFA_FFWidget::GetDoc() {
   return m_pDocView->GetDoc();
 }
@@ -613,26 +663,30 @@
   return GetApp()->GetAppProvider();
 }
 
-bool CXFA_FFWidget::IsMatchVisibleStatus(uint32_t dwStatus) {
-  return !!(m_dwStatus & XFA_WidgetStatus_Visible);
+bool CXFA_FFWidget::HasVisibleStatus() const {
+  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible);
 }
 
 void CXFA_FFWidget::EventKillFocus() {
-  if (m_dwStatus & XFA_WidgetStatus_Access) {
-    m_dwStatus &= ~XFA_WidgetStatus_Access;
+  CXFA_ContentLayoutItem* pItem = GetLayoutItem();
+  if (pItem->TestStatusBits(XFA_WidgetStatus_Access)) {
+    pItem->ClearStatusBits(XFA_WidgetStatus_Access);
     return;
   }
   CXFA_EventParam eParam;
   eParam.m_eType = XFA_EVENT_Exit;
-  eParam.m_pTarget = m_pNode->GetWidgetAcc();
-  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Exit, &eParam);
+  eParam.m_pTarget = m_pNode.Get();
+  m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Exit, &eParam);
 }
 
 bool CXFA_FFWidget::IsButtonDown() {
-  return (m_dwStatus & XFA_WidgetStatus_ButtonDown) != 0;
+  return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_ButtonDown);
 }
 
 void CXFA_FFWidget::SetButtonDown(bool bSet) {
-  bSet ? m_dwStatus |= XFA_WidgetStatus_ButtonDown
-       : m_dwStatus &= ~XFA_WidgetStatus_ButtonDown;
+  CXFA_ContentLayoutItem* pItem = GetLayoutItem();
+  if (bSet)
+    pItem->SetStatusBits(XFA_WidgetStatus_ButtonDown);
+  else
+    pItem->ClearStatusBits(XFA_WidgetStatus_ButtonDown);
 }
diff --git a/xfa/fxfa/cxfa_ffwidget.h b/xfa/fxfa/cxfa_ffwidget.h
index 54aa191..4c362c1 100644
--- a/xfa/fxfa/cxfa_ffwidget.h
+++ b/xfa/fxfa/cxfa_ffwidget.h
@@ -11,35 +11,42 @@
 
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fxcodec/fx_codec_def.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "xfa/fwl/cfwl_app.h"
+#include "xfa/fwl/cfwl_messagemouse.h"
+#include "xfa/fwl/cfwl_widget.h"
+#include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/fxfa.h"
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
 
+class CFX_DIBitmap;
 class CXFA_Box;
-class CXFA_FFPageView;
-class CXFA_FFDocView;
-class CXFA_FFDoc;
 class CXFA_FFApp;
+class CXFA_FFDoc;
+class CXFA_FFDocView;
+class CXFA_FFField;
+class CXFA_FFPageView;
+class CXFA_FFWidgetHandler;
 class CXFA_Graphics;
 class CXFA_Image;
+class CXFA_Margin;
 enum class FWL_WidgetHit;
 
 inline float XFA_UnitPx2Pt(float fPx, float fDpi) {
   return fPx * 72.0f / fDpi;
 }
 
-#define XFA_FLOAT_PERCISION 0.001f
+constexpr float kXFAWidgetPrecision = 0.001f;
 
 void XFA_DrawImage(CXFA_Graphics* pGS,
                    const CFX_RectF& rtImage,
                    const CFX_Matrix& matrix,
                    const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                   XFA_AttributeEnum iAspect,
-                   int32_t iImageXDpi,
-                   int32_t iImageYDpi,
-                   XFA_AttributeEnum iHorzAlign = XFA_AttributeEnum::Left,
-                   XFA_AttributeEnum iVertAlign = XFA_AttributeEnum::Top);
+                   XFA_AttributeValue iAspect,
+                   const CFX_Size& dpi,
+                   XFA_AttributeValue iHorzAlign = XFA_AttributeValue::Left,
+                   XFA_AttributeValue iVertAlign = XFA_AttributeValue::Top);
 
 RetainPtr<CFX_DIBitmap> XFA_LoadImageFromBuffer(
     const RetainPtr<IFX_SeekableReadStream>& pImageFileRead,
@@ -47,9 +54,7 @@
     int32_t& iImageXDpi,
     int32_t& iImageYDpi);
 
-void XFA_RectWithoutMargin(CFX_RectF& rt,
-                           const CXFA_Margin* margin,
-                           bool bUI = false);
+void XFA_RectWithoutMargin(CFX_RectF* rt, const CXFA_Margin* margin);
 CXFA_FFWidget* XFA_GetWidgetFromLayoutItem(CXFA_LayoutItem* pLayoutItem);
 
 class CXFA_CalcData {
@@ -58,44 +63,66 @@
   ~CXFA_CalcData();
 
   std::vector<CXFA_Node*> m_Globals;
-  int32_t m_iRefCount;
 };
 
-class CXFA_FFWidget : public CXFA_ContentLayoutItem {
+class CXFA_FFWidget : public Observable, public CFWL_Widget::AdapterIface {
  public:
+  enum FocusOption { kDoNotDrawFocus = 0, kDrawFocus };
+  enum HighlightOption { kNoHighlight = 0, kHighlight };
+
   explicit CXFA_FFWidget(CXFA_Node* pNode);
   ~CXFA_FFWidget() override;
 
-  virtual CFX_RectF GetBBox(uint32_t dwStatus, bool bDrawFocus = false);
+  // CFWL_Widget::AdapterIface:
+  CFX_Matrix GetRotateMatrix() override;
+  void DisplayCaret(bool bVisible, const CFX_RectF* pRtAnchor) override;
+  void GetBorderColorAndThickness(FX_ARGB* cr, float* fWidth) override;
+
+  virtual CXFA_FFField* AsField();
+
+  virtual CFX_RectF GetBBox(FocusOption focus);
   virtual void RenderWidget(CXFA_Graphics* pGS,
                             const CFX_Matrix& matrix,
-                            uint32_t dwStatus);
+                            HighlightOption highlight);
   virtual bool IsLoaded();
   virtual bool LoadWidget();
-  virtual void UnloadWidget();
   virtual bool PerformLayout();
   virtual bool UpdateFWLData();
   virtual void UpdateWidgetProperty();
-  virtual bool OnMouseEnter();
-  virtual bool OnMouseExit();
-  virtual bool OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point);
-  virtual bool OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point);
-  virtual bool OnLButtonDblClk(uint32_t dwFlags, const CFX_PointF& point);
-  virtual bool OnMouseMove(uint32_t dwFlags, const CFX_PointF& point);
+  // |command| must be LeftButtonDown or RightButtonDown.
+  virtual bool AcceptsFocusOnButtonDown(uint32_t dwFlags,
+                                        const CFX_PointF& point,
+                                        FWL_MouseCommand command);
+
+  // Caution: Returning false from an On* method may mean |this| is destroyed.
+  virtual bool OnMouseEnter() WARN_UNUSED_RESULT;
+  virtual bool OnMouseExit() WARN_UNUSED_RESULT;
+  virtual bool OnLButtonDown(uint32_t dwFlags,
+                             const CFX_PointF& point) WARN_UNUSED_RESULT;
+  virtual bool OnLButtonUp(uint32_t dwFlags,
+                           const CFX_PointF& point) WARN_UNUSED_RESULT;
+  virtual bool OnLButtonDblClk(uint32_t dwFlags,
+                               const CFX_PointF& point) WARN_UNUSED_RESULT;
+  virtual bool OnMouseMove(uint32_t dwFlags,
+                           const CFX_PointF& point) WARN_UNUSED_RESULT;
   virtual bool OnMouseWheel(uint32_t dwFlags,
                             int16_t zDelta,
-                            const CFX_PointF& point);
-  virtual bool OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point);
-  virtual bool OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point);
-  virtual bool OnRButtonDblClk(uint32_t dwFlags, const CFX_PointF& point);
+                            const CFX_PointF& point) WARN_UNUSED_RESULT;
+  virtual bool OnRButtonDown(uint32_t dwFlags,
+                             const CFX_PointF& point) WARN_UNUSED_RESULT;
+  virtual bool OnRButtonUp(uint32_t dwFlags,
+                           const CFX_PointF& point) WARN_UNUSED_RESULT;
+  virtual bool OnRButtonDblClk(uint32_t dwFlags,
+                               const CFX_PointF& point) WARN_UNUSED_RESULT;
+  virtual bool OnSetFocus(CXFA_FFWidget* pOldWidget) WARN_UNUSED_RESULT;
+  virtual bool OnKillFocus(CXFA_FFWidget* pNewWidget) WARN_UNUSED_RESULT;
+  virtual bool OnKeyDown(uint32_t dwKeyCode,
+                         uint32_t dwFlags) WARN_UNUSED_RESULT;
+  virtual bool OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) WARN_UNUSED_RESULT;
+  virtual bool OnChar(uint32_t dwChar, uint32_t dwFlags) WARN_UNUSED_RESULT;
+  virtual bool OnSetCursor(const CFX_PointF& point) WARN_UNUSED_RESULT;
 
-  virtual bool OnSetFocus(CXFA_FFWidget* pOldWidget);
-  virtual bool OnKillFocus(CXFA_FFWidget* pNewWidget);
-  virtual bool OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags);
-  virtual bool OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags);
-  virtual bool OnChar(uint32_t dwChar, uint32_t dwFlags);
-  virtual FWL_WidgetHit OnHitTest(const CFX_PointF& point);
-  virtual bool OnSetCursor(const CFX_PointF& point);
+  virtual FWL_WidgetHit HitTest(const CFX_PointF& point);
   virtual bool CanUndo();
   virtual bool CanRedo();
   virtual bool Undo();
@@ -112,37 +139,40 @@
   virtual void SelectAll();
   virtual void Delete();
   virtual void DeSelect();
-
+  virtual WideString GetText();
   virtual FormFieldType GetFormFieldType();
 
-  // TODO(tsepez): Implement or remove.
-  void GetSuggestWords(CFX_PointF pointf, std::vector<ByteString>* pWords);
-  bool ReplaceSpellCheckWord(CFX_PointF pointf,
-                             const ByteStringView& bsReplace);
-
-  CXFA_FFPageView* GetPageView() const { return m_pPageView; }
+  CXFA_Node* GetNode() const { return m_pNode.Get(); }
+  CXFA_ContentLayoutItem* GetLayoutItem() const { return m_pLayoutItem.Get(); }
+  void SetLayoutItem(CXFA_ContentLayoutItem* pItem) { m_pLayoutItem = pItem; }
+  CXFA_FFPageView* GetPageView() const { return m_pPageView.Get(); }
   void SetPageView(CXFA_FFPageView* pPageView) { m_pPageView = pPageView; }
+  CXFA_FFDocView* GetDocView() const { return m_pDocView.Get(); }
+  void SetDocView(CXFA_FFDocView* pDocView) { m_pDocView = pDocView; }
+
+  CXFA_FFWidget* GetNextFFWidget() const;
   const CFX_RectF& GetWidgetRect() const;
   const CFX_RectF& RecacheWidgetRect() const;
-  uint32_t GetStatus();
   void ModifyStatus(uint32_t dwAdded, uint32_t dwRemoved);
 
-  CXFA_Node* GetNode() { return m_pNode.Get(); }
-
-  CXFA_FFDocView* GetDocView();
-  void SetDocView(CXFA_FFDocView* pDocView);
   CXFA_FFDoc* GetDoc();
   CXFA_FFApp* GetApp();
   IXFA_AppProvider* GetAppProvider();
-  void AddInvalidateRect();
-  bool IsFocused() const { return !!(m_dwStatus & XFA_WidgetStatus_Focused); }
+  void InvalidateRect();
+  bool IsFocused() const {
+    return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Focused);
+  }
   CFX_PointF Rotate2Normal(const CFX_PointF& point);
-  CFX_Matrix GetRotateMatrix();
   bool IsLayoutRectEmpty();
-  CXFA_FFWidget* GetParent();
+  CXFA_LayoutItem* GetParent();
   bool IsAncestorOf(CXFA_FFWidget* pWidget);
   const CFWL_App* GetFWLApp();
 
+  bool HasEventUnderHandler(XFA_EVENTTYPE eEventType,
+                            CXFA_FFWidgetHandler* pHandler);
+  bool ProcessEventUnderHandler(CXFA_EventParam* params,
+                                CXFA_FFWidgetHandler* pHandler);
+
  protected:
   virtual bool PtInActiveRect(const CFX_PointF& point);
 
@@ -157,15 +187,20 @@
                           bool forceRound);
 
   CFX_RectF GetRectWithoutRotate();
-  bool IsMatchVisibleStatus(uint32_t dwStatus);
+  bool HasVisibleStatus() const;
   void EventKillFocus();
   bool IsButtonDown();
   void SetButtonDown(bool bSet);
 
-  CXFA_FFDocView* m_pDocView = nullptr;
-  CXFA_FFPageView* m_pPageView = nullptr;
+  UnownedPtr<CXFA_ContentLayoutItem> m_pLayoutItem;
+  UnownedPtr<CXFA_FFDocView> m_pDocView;
+  UnownedPtr<CXFA_FFPageView> m_pPageView;
   UnownedPtr<CXFA_Node> const m_pNode;
   mutable CFX_RectF m_rtWidget;
 };
 
+inline CXFA_FFField* ToField(CXFA_FFWidget* widget) {
+  return widget ? widget->AsField() : nullptr;
+}
+
 #endif  // XFA_FXFA_CXFA_FFWIDGET_H_
diff --git a/xfa/fxfa/cxfa_ffwidget_type.h b/xfa/fxfa/cxfa_ffwidget_type.h
new file mode 100644
index 0000000..69fbcd2
--- /dev/null
+++ b/xfa/fxfa/cxfa_ffwidget_type.h
@@ -0,0 +1,31 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_CXFA_FFWIDGET_TYPE_H_
+#define XFA_FXFA_CXFA_FFWIDGET_TYPE_H_
+
+enum class XFA_FFWidgetType {
+  kNone = 0,
+  kBarcode,
+  kButton,
+  kCheckButton,
+  kChoiceList,
+  kDateTimeEdit,
+  kImageEdit,
+  kNumericEdit,
+  kPasswordEdit,
+  kSignature,
+  kTextEdit,
+  kArc,
+  kLine,
+  kRectangle,
+  kText,
+  kImage,
+  kSubform,
+  kExclGroup
+};
+
+#endif  // XFA_FXFA_CXFA_FFWIDGET_TYPE_H_
diff --git a/xfa/fxfa/cxfa_ffwidgethandler.cpp b/xfa/fxfa/cxfa_ffwidgethandler.cpp
index c124c91..aa5dc3a 100644
--- a/xfa/fxfa/cxfa_ffwidgethandler.cpp
+++ b/xfa/fxfa/cxfa_ffwidgethandler.cpp
@@ -14,9 +14,9 @@
 #include "xfa/fxfa/cxfa_fffield.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_calculate.h"
 #include "xfa/fxfa/parser/cxfa_checkbutton.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_ui.h"
@@ -47,10 +47,14 @@
                                          uint32_t dwFlags,
                                          const CFX_PointF& point) {
   m_pDocView->LockUpdate();
-  bool bRet = hWidget->OnLButtonDown(dwFlags, hWidget->Rotate2Normal(point));
-  if (bRet && m_pDocView->SetFocus(hWidget)) {
-    m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
-        m_pDocView->GetDoc(), hWidget);
+  bool bRet = hWidget->AcceptsFocusOnButtonDown(
+      dwFlags, hWidget->Rotate2Normal(point), FWL_MouseCommand::LeftButtonDown);
+  if (bRet) {
+    if (m_pDocView->SetFocus(hWidget)) {
+      m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
+          m_pDocView->GetDoc(), hWidget);
+    }
+    bRet = hWidget->OnLButtonDown(dwFlags, hWidget->Rotate2Normal(point));
   }
   m_pDocView->UnlockUpdate();
   m_pDocView->UpdateDocView();
@@ -72,7 +76,6 @@
                                            uint32_t dwFlags,
                                            const CFX_PointF& point) {
   bool bRet = hWidget->OnLButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
@@ -80,7 +83,6 @@
                                        uint32_t dwFlags,
                                        const CFX_PointF& point) {
   bool bRet = hWidget->OnMouseMove(dwFlags, hWidget->Rotate2Normal(point));
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
@@ -90,19 +92,22 @@
                                         const CFX_PointF& point) {
   bool bRet =
       hWidget->OnMouseWheel(dwFlags, zDelta, hWidget->Rotate2Normal(point));
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
 bool CXFA_FFWidgetHandler::OnRButtonDown(CXFA_FFWidget* hWidget,
                                          uint32_t dwFlags,
                                          const CFX_PointF& point) {
-  bool bRet = hWidget->OnRButtonDown(dwFlags, hWidget->Rotate2Normal(point));
-  if (bRet && m_pDocView->SetFocus(hWidget)) {
-    m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
-        m_pDocView->GetDoc(), hWidget);
+  bool bRet =
+      hWidget->AcceptsFocusOnButtonDown(dwFlags, hWidget->Rotate2Normal(point),
+                                        FWL_MouseCommand::RightButtonDown);
+  if (bRet) {
+    if (m_pDocView->SetFocus(hWidget)) {
+      m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
+          m_pDocView->GetDoc(), hWidget);
+    }
+    bRet = hWidget->OnRButtonDown(dwFlags, hWidget->Rotate2Normal(point));
   }
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
@@ -110,7 +115,6 @@
                                        uint32_t dwFlags,
                                        const CFX_PointF& point) {
   bool bRet = hWidget->OnRButtonUp(dwFlags, hWidget->Rotate2Normal(point));
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
@@ -118,7 +122,6 @@
                                            uint32_t dwFlags,
                                            const CFX_PointF& point) {
   bool bRet = hWidget->OnRButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
@@ -126,7 +129,6 @@
                                      uint32_t dwKeyCode,
                                      uint32_t dwFlags) {
   bool bRet = hWidget->OnKeyDown(dwKeyCode, dwFlags);
-  m_pDocView->RunInvalidate();
   m_pDocView->UpdateDocView();
   return bRet;
 }
@@ -135,7 +137,6 @@
                                    uint32_t dwKeyCode,
                                    uint32_t dwFlags) {
   bool bRet = hWidget->OnKeyUp(dwKeyCode, dwFlags);
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
@@ -143,10 +144,13 @@
                                   uint32_t dwChar,
                                   uint32_t dwFlags) {
   bool bRet = hWidget->OnChar(dwChar, dwFlags);
-  m_pDocView->RunInvalidate();
   return bRet;
 }
 
+WideString CXFA_FFWidgetHandler::GetText(CXFA_FFWidget* widget) {
+  return widget->GetText();
+}
+
 WideString CXFA_FFWidgetHandler::GetSelectedText(CXFA_FFWidget* widget) {
   if (!widget->CanCopy())
     return WideString();
@@ -162,11 +166,27 @@
   widget->Paste(text);
 }
 
-FWL_WidgetHit CXFA_FFWidgetHandler::OnHitTest(CXFA_FFWidget* hWidget,
-                                              const CFX_PointF& point) {
-  if (!(hWidget->GetStatus() & XFA_WidgetStatus_Visible))
+bool CXFA_FFWidgetHandler::CanUndo(CXFA_FFWidget* widget) {
+  return widget->CanUndo();
+}
+
+bool CXFA_FFWidgetHandler::CanRedo(CXFA_FFWidget* widget) {
+  return widget->CanRedo();
+}
+
+bool CXFA_FFWidgetHandler::Undo(CXFA_FFWidget* widget) {
+  return widget->Undo();
+}
+
+bool CXFA_FFWidgetHandler::Redo(CXFA_FFWidget* widget) {
+  return widget->Redo();
+}
+
+FWL_WidgetHit CXFA_FFWidgetHandler::HitTest(CXFA_FFWidget* pWidget,
+                                            const CFX_PointF& point) {
+  if (!pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_Visible))
     return FWL_WidgetHit::Unknown;
-  return hWidget->OnHitTest(hWidget->Rotate2Normal(point));
+  return pWidget->HitTest(pWidget->Rotate2Normal(point));
 }
 
 bool CXFA_FFWidgetHandler::OnSetCursor(CXFA_FFWidget* hWidget,
@@ -178,399 +198,62 @@
                                         CXFA_Graphics* pGS,
                                         const CFX_Matrix& matrix,
                                         bool bHighlight) {
-  hWidget->RenderWidget(pGS, matrix,
-                        bHighlight ? XFA_WidgetStatus_Highlight : 0);
+  hWidget->RenderWidget(
+      pGS, matrix,
+      bHighlight ? CXFA_FFWidget::kHighlight : CXFA_FFWidget::kNoHighlight);
 }
 
-bool CXFA_FFWidgetHandler::HasEvent(CXFA_WidgetAcc* pWidgetAcc,
+bool CXFA_FFWidgetHandler::HasEvent(CXFA_Node* pNode,
                                     XFA_EVENTTYPE eEventType) {
   if (eEventType == XFA_EVENT_Unknown)
     return false;
-  if (!pWidgetAcc)
-    return false;
-
-  CXFA_Node* node = pWidgetAcc->GetNode();
-  if (!node || node->GetElementType() == XFA_Element::Draw)
+  if (!pNode || pNode->GetElementType() == XFA_Element::Draw)
     return false;
 
   switch (eEventType) {
     case XFA_EVENT_Calculate: {
-      CXFA_Calculate* calc = node->GetCalculateIfExists();
+      CXFA_Calculate* calc = pNode->GetCalculateIfExists();
       return calc && calc->GetScriptIfExists();
     }
     case XFA_EVENT_Validate: {
-      CXFA_Validate* validate = node->GetValidateIfExists();
+      CXFA_Validate* validate = pNode->GetValidateIfExists();
       return validate && validate->GetScriptIfExists();
     }
     default:
       break;
   }
-  return !pWidgetAcc->GetEventByActivity(gs_EventActivity[eEventType], false)
+  return !pNode->GetEventByActivity(gs_EventActivity[eEventType], false)
               .empty();
 }
 
-int32_t CXFA_FFWidgetHandler::ProcessEvent(CXFA_WidgetAcc* pWidgetAcc,
-                                           CXFA_EventParam* pParam) {
+XFA_EventError CXFA_FFWidgetHandler::ProcessEvent(CXFA_Node* pNode,
+                                                  CXFA_EventParam* pParam) {
   if (!pParam || pParam->m_eType == XFA_EVENT_Unknown)
-    return XFA_EVENTERROR_NotExist;
-  if (!pWidgetAcc)
-    return XFA_EVENTERROR_NotExist;
-
-  CXFA_Node* node = pWidgetAcc->GetNode();
-  if (!node || node->GetElementType() == XFA_Element::Draw)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
+  if (!pNode || pNode->GetElementType() == XFA_Element::Draw)
+    return XFA_EventError::kNotExist;
 
   switch (pParam->m_eType) {
     case XFA_EVENT_Calculate:
-      return node->ProcessCalculate(m_pDocView);
+      return pNode->ProcessCalculate(m_pDocView.Get());
     case XFA_EVENT_Validate:
       if (m_pDocView->GetDoc()->GetDocEnvironment()->IsValidationsEnabled(
               m_pDocView->GetDoc())) {
-        return node->ProcessValidate(m_pDocView, 0);
+        return pNode->ProcessValidate(m_pDocView.Get(), 0);
       }
-      return XFA_EVENTERROR_Disabled;
+      return XFA_EventError::kDisabled;
     case XFA_EVENT_InitCalculate: {
-      CXFA_Calculate* calc = node->GetCalculateIfExists();
+      CXFA_Calculate* calc = pNode->GetCalculateIfExists();
       if (!calc)
-        return XFA_EVENTERROR_NotExist;
-      if (node->IsUserInteractive())
-        return XFA_EVENTERROR_Disabled;
-      return node->ExecuteScript(m_pDocView, calc->GetScriptIfExists(), pParam);
+        return XFA_EventError::kNotExist;
+      if (pNode->IsUserInteractive())
+        return XFA_EventError::kDisabled;
+      return pNode->ExecuteScript(m_pDocView.Get(), calc->GetScriptIfExists(),
+                                  pParam);
     }
     default:
       break;
   }
-  int32_t iRet =
-      node->ProcessEvent(m_pDocView, gs_EventActivity[pParam->m_eType], pParam);
-  return iRet;
-}
-
-CXFA_FFWidget* CXFA_FFWidgetHandler::CreateWidget(CXFA_FFWidget* hParent,
-                                                  XFA_WIDGETTYPE eType,
-                                                  CXFA_FFWidget* hBefore) {
-  CXFA_Node* pParentFormItem = hParent ? hParent->GetNode() : nullptr;
-  CXFA_Node* pBeforeFormItem = hBefore ? hBefore->GetNode() : nullptr;
-  CXFA_Node* pNewFormItem =
-      CreateWidgetFormItem(eType, pParentFormItem, pBeforeFormItem);
-  if (!pNewFormItem)
-    return nullptr;
-
-  CXFA_Node* templateNode = pNewFormItem->GetTemplateNodeIfExists();
-  if (!templateNode)
-    return nullptr;
-
-  templateNode->SetFlag(XFA_NodeFlag_Initialized, true);
-  pNewFormItem->SetFlag(XFA_NodeFlag_Initialized, true);
-  m_pDocView->RunLayout();
-  CXFA_LayoutItem* pLayout =
-      m_pDocView->GetXFALayout()->GetLayoutItem(pNewFormItem);
-  return static_cast<CXFA_FFWidget*>(pLayout);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateWidgetFormItem(
-    XFA_WIDGETTYPE eType,
-    CXFA_Node* pParent,
-    CXFA_Node* pBefore) const {
-  switch (eType) {
-    case XFA_WIDGETTYPE_Barcode:
-      return nullptr;
-    case XFA_WIDGETTYPE_PushButton:
-      return CreatePushButton(pParent, pBefore);
-    case XFA_WIDGETTYPE_CheckButton:
-      return CreateCheckButton(pParent, pBefore);
-    case XFA_WIDGETTYPE_ExcludeGroup:
-      return CreateExclGroup(pParent, pBefore);
-    case XFA_WIDGETTYPE_RadioButton:
-      return CreateRadioButton(pParent, pBefore);
-    case XFA_WIDGETTYPE_Arc:
-      return CreateArc(pParent, pBefore);
-    case XFA_WIDGETTYPE_Rectangle:
-      return CreateRectangle(pParent, pBefore);
-    case XFA_WIDGETTYPE_Image:
-      return CreateImage(pParent, pBefore);
-    case XFA_WIDGETTYPE_Line:
-      return CreateLine(pParent, pBefore);
-    case XFA_WIDGETTYPE_Text:
-      return CreateText(pParent, pBefore);
-    case XFA_WIDGETTYPE_DatetimeEdit:
-      return CreateDatetimeEdit(pParent, pBefore);
-    case XFA_WIDGETTYPE_DecimalField:
-      return CreateDecimalField(pParent, pBefore);
-    case XFA_WIDGETTYPE_NumericField:
-      return CreateNumericField(pParent, pBefore);
-    case XFA_WIDGETTYPE_Signature:
-      return CreateSignature(pParent, pBefore);
-    case XFA_WIDGETTYPE_TextEdit:
-      return CreateTextEdit(pParent, pBefore);
-    case XFA_WIDGETTYPE_DropdownList:
-      return CreateDropdownList(pParent, pBefore);
-    case XFA_WIDGETTYPE_ListBox:
-      return CreateListBox(pParent, pBefore);
-    case XFA_WIDGETTYPE_ImageField:
-      return CreateImageField(pParent, pBefore);
-    case XFA_WIDGETTYPE_PasswordEdit:
-      return CreatePasswordEdit(pParent, pBefore);
-    case XFA_WIDGETTYPE_Subform:
-      return CreateSubform(pParent, pBefore);
-    default:
-      return nullptr;
-  }
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreatePushButton(CXFA_Node* pParent,
-                                                  CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateField(XFA_Element::Button, pParent, pBefore);
-  CXFA_Node* pCaption = CreateCopyNode(XFA_Element::Caption, pField);
-  CXFA_Node* pValue = CreateCopyNode(XFA_Element::Value, pCaption);
-  CXFA_Node* pText = CreateCopyNode(XFA_Element::Text, pValue);
-  pText->JSObject()->SetContent(L"Button", L"Button", false, false, true);
-
-  CXFA_Node* pPara = CreateCopyNode(XFA_Element::Para, pCaption);
-  pPara->JSObject()->SetEnum(XFA_Attribute::VAlign, XFA_AttributeEnum::Middle,
-                             false);
-  pPara->JSObject()->SetEnum(XFA_Attribute::HAlign, XFA_AttributeEnum::Center,
-                             false);
-  CreateFontNode(pCaption);
-
-  CXFA_Node* pBorder = CreateCopyNode(XFA_Element::Border, pField);
-  pBorder->JSObject()->SetEnum(XFA_Attribute::Hand, XFA_AttributeEnum::Right,
-                               false);
-
-  CXFA_Node* pEdge = CreateCopyNode(XFA_Element::Edge, pBorder);
-  pEdge->JSObject()->SetEnum(XFA_Attribute::Stroke, XFA_AttributeEnum::Raised,
-                             false);
-
-  CXFA_Node* pFill = CreateCopyNode(XFA_Element::Fill, pBorder);
-  CXFA_Node* pColor = CreateCopyNode(XFA_Element::Color, pFill);
-  pColor->JSObject()->SetCData(XFA_Attribute::Value, L"212, 208, 200", false,
-                               false);
-
-  CXFA_Node* pBind = CreateCopyNode(XFA_Element::Bind, pField);
-  pBind->JSObject()->SetEnum(XFA_Attribute::Match, XFA_AttributeEnum::None,
-                             false);
-
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateCheckButton(CXFA_Node* pParent,
-                                                   CXFA_Node* pBefore) const {
-  return CreateField(XFA_Element::CheckButton, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateExclGroup(CXFA_Node* pParent,
-                                                 CXFA_Node* pBefore) const {
-  return CreateFormItem(XFA_Element::ExclGroup, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateRadioButton(CXFA_Node* pParent,
-                                                   CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateField(XFA_Element::CheckButton, pParent, pBefore);
-  CXFA_Ui* pUi = pField->GetFirstChildByClass<CXFA_Ui>(XFA_Element::Ui);
-  CXFA_CheckButton* pWidget =
-      pUi->GetFirstChildByClass<CXFA_CheckButton>(XFA_Element::CheckButton);
-  pWidget->JSObject()->SetEnum(XFA_Attribute::Shape, XFA_AttributeEnum::Round,
-                               false);
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateDatetimeEdit(CXFA_Node* pParent,
-                                                    CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateField(XFA_Element::DateTimeEdit, pParent, pBefore);
-  CreateValueNode(XFA_Element::Date, pField);
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateDecimalField(CXFA_Node* pParent,
-                                                    CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateNumericField(pParent, pBefore);
-  CreateValueNode(XFA_Element::Decimal, pField);
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateNumericField(CXFA_Node* pParent,
-                                                    CXFA_Node* pBefore) const {
-  return CreateField(XFA_Element::NumericEdit, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateSignature(CXFA_Node* pParent,
-                                                 CXFA_Node* pBefore) const {
-  return CreateField(XFA_Element::Signature, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateTextEdit(CXFA_Node* pParent,
-                                                CXFA_Node* pBefore) const {
-  return CreateField(XFA_Element::TextEdit, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateDropdownList(CXFA_Node* pParent,
-                                                    CXFA_Node* pBefore) const {
-  return CreateField(XFA_Element::ChoiceList, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateListBox(CXFA_Node* pParent,
-                                               CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateDropdownList(pParent, pBefore);
-  CXFA_Node* pUi = pField->GetFirstChild();
-  CXFA_Node* pListBox = pUi->GetFirstChild();
-  pListBox->JSObject()->SetEnum(XFA_Attribute::Open, XFA_AttributeEnum::Always,
-                                false);
-  pListBox->JSObject()->SetEnum(XFA_Attribute::CommitOn,
-                                XFA_AttributeEnum::Exit, false);
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateImageField(CXFA_Node* pParent,
-                                                  CXFA_Node* pBefore) const {
-  return CreateField(XFA_Element::ImageEdit, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreatePasswordEdit(CXFA_Node* pParent,
-                                                    CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateField(XFA_Element::PasswordEdit, pParent, pBefore);
-  CXFA_Node* pBind = CreateCopyNode(XFA_Element::Bind, pField);
-  pBind->JSObject()->SetEnum(XFA_Attribute::Match, XFA_AttributeEnum::None,
-                             false);
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateField(XFA_Element eElement,
-                                             CXFA_Node* pParent,
-                                             CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateFormItem(XFA_Element::Field, pParent, pBefore);
-  CreateCopyNode(eElement, CreateCopyNode(XFA_Element::Ui, pField));
-  CreateFontNode(pField);
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateArc(CXFA_Node* pParent,
-                                           CXFA_Node* pBefore) const {
-  return CreateDraw(XFA_Element::Arc, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateRectangle(CXFA_Node* pParent,
-                                                 CXFA_Node* pBefore) const {
-  return CreateDraw(XFA_Element::Rectangle, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateImage(CXFA_Node* pParent,
-                                             CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateDraw(XFA_Element::Image, pParent, pBefore);
-  CreateCopyNode(XFA_Element::ImageEdit,
-                 CreateCopyNode(XFA_Element::Ui, pField));
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateLine(CXFA_Node* pParent,
-                                            CXFA_Node* pBefore) const {
-  return CreateDraw(XFA_Element::Line, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateText(CXFA_Node* pParent,
-                                            CXFA_Node* pBefore) const {
-  CXFA_Node* pField = CreateDraw(XFA_Element::Text, pParent, pBefore);
-  CreateCopyNode(XFA_Element::TextEdit,
-                 CreateCopyNode(XFA_Element::Ui, pField));
-  CreateFontNode(pField);
-  return pField;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateDraw(XFA_Element eElement,
-                                            CXFA_Node* pParent,
-                                            CXFA_Node* pBefore) const {
-  CXFA_Node* pDraw = CreateFormItem(XFA_Element::Draw, pParent, pBefore);
-  CreateValueNode(eElement, pDraw);
-  return pDraw;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateSubform(CXFA_Node* pParent,
-                                               CXFA_Node* pBefore) const {
-  return CreateFormItem(XFA_Element::Subform, pParent, pBefore);
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateFormItem(XFA_Element eElement,
-                                                CXFA_Node* pParent,
-                                                CXFA_Node* pBefore) const {
-  if (!pParent)
-    return nullptr;
-
-  CXFA_Node* pTemplateParent = pParent->GetTemplateNodeIfExists();
-  if (!pTemplateParent)
-    return nullptr;
-
-  CXFA_Node* pNewFormItem = pTemplateParent->CloneTemplateToForm(false);
-  if (pParent)
-    pParent->InsertChild(pNewFormItem, pBefore);
-  return pNewFormItem;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateCopyNode(XFA_Element eElement,
-                                                CXFA_Node* pParent,
-                                                CXFA_Node* pBefore) const {
-  if (!pParent)
-    return nullptr;
-
-  CXFA_Node* pTemplateParent = pParent->GetTemplateNodeIfExists();
-  CXFA_Node* pNewNode =
-      CreateTemplateNode(eElement, pTemplateParent,
-                         pBefore ? pBefore->GetTemplateNodeIfExists() : nullptr)
-          ->Clone(false);
-  if (pParent)
-    pParent->InsertChild(pNewNode, pBefore);
-  return pNewNode;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateTemplateNode(XFA_Element eElement,
-                                                    CXFA_Node* pParent,
-                                                    CXFA_Node* pBefore) const {
-  CXFA_Document* pXFADoc = GetXFADoc();
-  CXFA_Node* pNewTemplateNode =
-      pXFADoc->CreateNode(XFA_PacketType::Template, eElement);
-  if (pParent)
-    pParent->InsertChild(pNewTemplateNode, pBefore);
-  return pNewTemplateNode;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateFontNode(CXFA_Node* pParent) const {
-  CXFA_Node* pFont = CreateCopyNode(XFA_Element::Font, pParent);
-  pFont->JSObject()->SetCData(XFA_Attribute::Typeface, L"Myriad Pro", false,
-                              false);
-  return pFont;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateMarginNode(CXFA_Node* pParent,
-                                                  uint32_t dwFlags,
-                                                  float fInsets[4]) const {
-  CXFA_Node* pMargin = CreateCopyNode(XFA_Element::Margin, pParent);
-  if (dwFlags & 0x01)
-    pMargin->JSObject()->SetMeasure(XFA_Attribute::LeftInset,
-                                    CXFA_Measurement(fInsets[0], XFA_Unit::Pt),
-                                    false);
-  if (dwFlags & 0x02)
-    pMargin->JSObject()->SetMeasure(XFA_Attribute::TopInset,
-                                    CXFA_Measurement(fInsets[1], XFA_Unit::Pt),
-                                    false);
-  if (dwFlags & 0x04)
-    pMargin->JSObject()->SetMeasure(XFA_Attribute::RightInset,
-                                    CXFA_Measurement(fInsets[2], XFA_Unit::Pt),
-                                    false);
-  if (dwFlags & 0x08)
-    pMargin->JSObject()->SetMeasure(XFA_Attribute::BottomInset,
-                                    CXFA_Measurement(fInsets[3], XFA_Unit::Pt),
-                                    false);
-  return pMargin;
-}
-
-CXFA_Node* CXFA_FFWidgetHandler::CreateValueNode(XFA_Element eValue,
-                                                 CXFA_Node* pParent) const {
-  CXFA_Node* pValue = CreateCopyNode(XFA_Element::Value, pParent);
-  CreateCopyNode(eValue, pValue);
-  return pValue;
-}
-
-CXFA_Document* CXFA_FFWidgetHandler::GetObjFactory() const {
-  return GetXFADoc();
-}
-
-CXFA_Document* CXFA_FFWidgetHandler::GetXFADoc() const {
-  return m_pDocView->GetDoc()->GetXFADoc();
+  return pNode->ProcessEvent(m_pDocView.Get(),
+                             gs_EventActivity[pParam->m_eType], pParam);
 }
diff --git a/xfa/fxfa/cxfa_ffwidgethandler.h b/xfa/fxfa/cxfa_ffwidgethandler.h
index 1ef8854..172a8c6 100644
--- a/xfa/fxfa/cxfa_ffwidgethandler.h
+++ b/xfa/fxfa/cxfa_ffwidgethandler.h
@@ -7,8 +7,7 @@
 #ifndef XFA_FXFA_CXFA_FFWIDGETHANDLER_H_
 #define XFA_FXFA_CXFA_FFWIDGETHANDLER_H_
 
-#include <vector>
-
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
@@ -22,10 +21,6 @@
   explicit CXFA_FFWidgetHandler(CXFA_FFDocView* pDocView);
   ~CXFA_FFWidgetHandler();
 
-  CXFA_FFWidget* CreateWidget(CXFA_FFWidget* hParent,
-                              XFA_WIDGETTYPE eType,
-                              CXFA_FFWidget* hBefore = nullptr);
-
   bool OnMouseEnter(CXFA_FFWidget* hWidget);
   bool OnMouseExit(CXFA_FFWidget* hWidget);
   bool OnLButtonDown(CXFA_FFWidget* hWidget,
@@ -54,70 +49,29 @@
                        uint32_t dwFlags,
                        const CFX_PointF& point);
 
+  WideString GetText(CXFA_FFWidget* widget);
   WideString GetSelectedText(CXFA_FFWidget* widget);
   void PasteText(CXFA_FFWidget* widget, const WideString& text);
 
+  bool CanUndo(CXFA_FFWidget* widget);
+  bool CanRedo(CXFA_FFWidget* widget);
+  bool Undo(CXFA_FFWidget* widget);
+  bool Redo(CXFA_FFWidget* widget);
+
   bool OnKeyDown(CXFA_FFWidget* hWidget, uint32_t dwKeyCode, uint32_t dwFlags);
   bool OnKeyUp(CXFA_FFWidget* hWidget, uint32_t dwKeyCode, uint32_t dwFlags);
   bool OnChar(CXFA_FFWidget* hWidget, uint32_t dwChar, uint32_t dwFlags);
-  FWL_WidgetHit OnHitTest(CXFA_FFWidget* hWidget, const CFX_PointF& point);
   bool OnSetCursor(CXFA_FFWidget* hWidget, const CFX_PointF& point);
+  FWL_WidgetHit HitTest(CXFA_FFWidget* pWidget, const CFX_PointF& point);
   void RenderWidget(CXFA_FFWidget* hWidget,
                     CXFA_Graphics* pGS,
                     const CFX_Matrix& matrix,
                     bool bHighlight);
-  bool HasEvent(CXFA_WidgetAcc* pWidgetAcc, XFA_EVENTTYPE eEventType);
-  int32_t ProcessEvent(CXFA_WidgetAcc* pWidgetAcc, CXFA_EventParam* pParam);
+  bool HasEvent(CXFA_Node* pNode, XFA_EVENTTYPE eEventType);
+  XFA_EventError ProcessEvent(CXFA_Node* pNode, CXFA_EventParam* pParam);
 
  private:
-  CXFA_Node* CreateWidgetFormItem(XFA_WIDGETTYPE eType,
-                                  CXFA_Node* pParent,
-                                  CXFA_Node* pBefore) const;
-
-  CXFA_Node* CreatePushButton(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateCheckButton(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateExclGroup(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateRadioButton(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateDatetimeEdit(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateDecimalField(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateNumericField(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateSignature(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateTextEdit(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateDropdownList(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateListBox(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateImageField(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreatePasswordEdit(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateField(XFA_Element eElement,
-                         CXFA_Node* pParent,
-                         CXFA_Node* pBefore) const;
-  CXFA_Node* CreateArc(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateRectangle(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateImage(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateLine(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateText(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateDraw(XFA_Element eElement,
-                        CXFA_Node* pParent,
-                        CXFA_Node* pBefore) const;
-
-  CXFA_Node* CreateSubform(CXFA_Node* pParent, CXFA_Node* pBefore) const;
-  CXFA_Node* CreateFormItem(XFA_Element eElement,
-                            CXFA_Node* pParent,
-                            CXFA_Node* pBefore) const;
-  CXFA_Node* CreateCopyNode(XFA_Element eElement,
-                            CXFA_Node* pParent,
-                            CXFA_Node* pBefore = nullptr) const;
-  CXFA_Node* CreateTemplateNode(XFA_Element eElement,
-                                CXFA_Node* pParent,
-                                CXFA_Node* pBefore) const;
-  CXFA_Node* CreateFontNode(CXFA_Node* pParent) const;
-  CXFA_Node* CreateMarginNode(CXFA_Node* pParent,
-                              uint32_t dwFlags,
-                              float fInsets[4]) const;
-  CXFA_Node* CreateValueNode(XFA_Element eValue, CXFA_Node* pParent) const;
-  CXFA_Document* GetObjFactory() const;
-  CXFA_Document* GetXFADoc() const;
-
-  CXFA_FFDocView* m_pDocView;
+  UnownedPtr<CXFA_FFDocView> m_pDocView;
 };
 
 #endif  //  XFA_FXFA_CXFA_FFWIDGETHANDLER_H_
diff --git a/xfa/fxfa/cxfa_fontmgr.cpp b/xfa/fxfa/cxfa_fontmgr.cpp
index 0956806..7259f6c 100644
--- a/xfa/fxfa/cxfa_fontmgr.cpp
+++ b/xfa/fxfa/cxfa_fontmgr.cpp
@@ -10,23 +10,23 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "xfa/fgas/font/cfgas_defaultfontmanager.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 
-CXFA_FontMgr::CXFA_FontMgr() {}
+CXFA_FontMgr::CXFA_FontMgr() = default;
 
-CXFA_FontMgr::~CXFA_FontMgr() {}
+CXFA_FontMgr::~CXFA_FontMgr() = default;
 
-RetainPtr<CFGAS_GEFont> CXFA_FontMgr::GetFont(
-    CXFA_FFDoc* hDoc,
-    const WideStringView& wsFontFamily,
-    uint32_t dwFontStyles) {
+RetainPtr<CFGAS_GEFont> CXFA_FontMgr::GetFont(CXFA_FFDoc* hDoc,
+                                              WideStringView wsFontFamily,
+                                              uint32_t dwFontStyles) {
   uint32_t dwHash = FX_HashCode_GetW(wsFontFamily, false);
   ByteString bsKey = ByteString::Format("%u%u%u", dwHash, dwFontStyles, 0xFFFF);
   auto iter = m_FontMap.find(bsKey);
@@ -34,43 +34,33 @@
     return iter->second;
 
   WideString wsEnglishName = FGAS_FontNameToEnglishName(wsFontFamily);
-
   CFGAS_PDFFontMgr* pMgr = hDoc->GetPDFFontMgr();
-  CPDF_Font* pPDFFont = nullptr;
   RetainPtr<CFGAS_GEFont> pFont;
   if (pMgr) {
-    pFont = pMgr->GetFont(wsEnglishName.AsStringView(), dwFontStyles, &pPDFFont,
-                          true);
+    pFont = pMgr->GetFont(wsEnglishName.AsStringView(), dwFontStyles, true);
     if (pFont)
       return pFont;
   }
-  if (!pFont && m_pDefFontMgr)
-    pFont = m_pDefFontMgr->GetFont(hDoc->GetApp()->GetFDEFontMgr(),
-                                   wsFontFamily, dwFontStyles);
-
+  if (!pFont) {
+    pFont = CFGAS_DefaultFontManager::GetFont(hDoc->GetApp()->GetFDEFontMgr(),
+                                              wsFontFamily, dwFontStyles);
+  }
   if (!pFont && pMgr) {
-    pPDFFont = nullptr;
-    pFont = pMgr->GetFont(wsEnglishName.AsStringView(), dwFontStyles, &pPDFFont,
-                          false);
+    pFont = pMgr->GetFont(wsEnglishName.AsStringView(), dwFontStyles, false);
     if (pFont)
       return pFont;
   }
-  if (!pFont && m_pDefFontMgr) {
-    pFont = m_pDefFontMgr->GetDefaultFont(hDoc->GetApp()->GetFDEFontMgr(),
-                                          wsFontFamily, dwFontStyles);
+  if (!pFont) {
+    pFont = CFGAS_DefaultFontManager::GetDefaultFont(
+        hDoc->GetApp()->GetFDEFontMgr(), wsFontFamily, dwFontStyles);
   }
-
-  if (pFont) {
-    if (pPDFFont) {
-      pMgr->SetFont(pFont, pPDFFont);
-      pFont->SetFontProvider(pMgr);
-    }
+  if (!pFont) {
+    pFont = CFGAS_GEFont::LoadStockFont(
+        hDoc->GetPDFDoc(), hDoc->GetApp()->GetFDEFontMgr(),
+        ByteString::Format("%ls", WideString(wsFontFamily).c_str()));
+  }
+  if (pFont)
     m_FontMap[bsKey] = pFont;
-  }
-  return pFont;
-}
 
-void CXFA_FontMgr::SetDefFontMgr(
-    std::unique_ptr<CFGAS_DefaultFontManager> pFontMgr) {
-  m_pDefFontMgr = std::move(pFontMgr);
+  return pFont;
 }
diff --git a/xfa/fxfa/cxfa_fontmgr.h b/xfa/fxfa/cxfa_fontmgr.h
index a940faf..2ad45dc 100644
--- a/xfa/fxfa/cxfa_fontmgr.h
+++ b/xfa/fxfa/cxfa_fontmgr.h
@@ -8,18 +8,11 @@
 #define XFA_FXFA_CXFA_FONTMGR_H_
 
 #include <map>
-#include <memory>
-#include <vector>
 
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "xfa/fgas/font/cfgas_defaultfontmanager.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
-#include "xfa/fgas/font/cfgas_pdffontmgr.h"
-#include "xfa/fxfa/fxfa.h"
+#include "core/fxcrt/fx_string.h"
 
-class CPDF_Font;
+class CFGAS_GEFont;
+class CXFA_FFDoc;
 
 class CXFA_FontMgr {
  public:
@@ -27,12 +20,10 @@
   ~CXFA_FontMgr();
 
   RetainPtr<CFGAS_GEFont> GetFont(CXFA_FFDoc* hDoc,
-                                  const WideStringView& wsFontFamily,
+                                  WideStringView wsFontFamily,
                                   uint32_t dwFontStyles);
-  void SetDefFontMgr(std::unique_ptr<CFGAS_DefaultFontManager> pFontMgr);
 
  private:
-  std::unique_ptr<CFGAS_DefaultFontManager> m_pDefFontMgr;
   std::map<ByteString, RetainPtr<CFGAS_GEFont>> m_FontMap;
 };
 
diff --git a/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp b/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp
index 6fdd553..a67bf08 100644
--- a/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp
+++ b/xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp
@@ -17,22 +17,22 @@
   if (!pWidget)
     return;
 
-  CXFA_FFWidget* pFFWidget = pWidget->GetLayoutItem();
+  auto* pFFWidget = static_cast<CXFA_FFWidget*>(pWidget->GetAdapterIface());
   if (!pFFWidget)
     return;
 
-  pFFWidget->AddInvalidateRect();
+  pFFWidget->InvalidateRect();
 }
 
 bool CXFA_FWLAdapterWidgetMgr::GetPopupPos(CFWL_Widget* pWidget,
                                            float fMinHeight,
                                            float fMaxHeight,
                                            const CFX_RectF& rtAnchor,
-                                           CFX_RectF& rtPopup) {
-  CXFA_FFWidget* pFFWidget = pWidget->GetLayoutItem();
+                                           CFX_RectF* pPopupRect) {
+  auto* pFFWidget = static_cast<CXFA_FFWidget*>(pWidget->GetAdapterIface());
   CFX_RectF rtRotateAnchor =
       pFFWidget->GetRotateMatrix().TransformRect(rtAnchor);
   pFFWidget->GetDoc()->GetDocEnvironment()->GetPopupPos(
-      pFFWidget, fMinHeight, fMaxHeight, rtRotateAnchor, rtPopup);
+      pFFWidget, fMinHeight, fMaxHeight, rtRotateAnchor, pPopupRect);
   return true;
 }
diff --git a/xfa/fxfa/cxfa_fwladapterwidgetmgr.h b/xfa/fxfa/cxfa_fwladapterwidgetmgr.h
index f5c4ce1..4a50c28 100644
--- a/xfa/fxfa/cxfa_fwladapterwidgetmgr.h
+++ b/xfa/fxfa/cxfa_fwladapterwidgetmgr.h
@@ -9,20 +9,21 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
+#include "xfa/fwl/cfwl_widgetmgr.h"
 
 class CFWL_Widget;
 
-class CXFA_FWLAdapterWidgetMgr {
+class CXFA_FWLAdapterWidgetMgr : public CFWL_WidgetMgr::AdapterIface {
  public:
   CXFA_FWLAdapterWidgetMgr();
-  ~CXFA_FWLAdapterWidgetMgr();
+  ~CXFA_FWLAdapterWidgetMgr() override;
 
-  void RepaintWidget(CFWL_Widget* pWidget);
+  void RepaintWidget(CFWL_Widget* pWidget) override;
   bool GetPopupPos(CFWL_Widget* pWidget,
                    float fMinHeight,
                    float fMaxHeight,
                    const CFX_RectF& rtAnchor,
-                   CFX_RectF& rtPopup);
+                   CFX_RectF* pPopupRect) override;
 };
 
 #endif  // XFA_FXFA_CXFA_FWLADAPTERWIDGETMGR_H_
diff --git a/xfa/fxfa/cxfa_fwltheme.cpp b/xfa/fxfa/cxfa_fwltheme.cpp
index 9209682..4c2c8da 100644
--- a/xfa/fxfa/cxfa_fwltheme.cpp
+++ b/xfa/fxfa/cxfa_fwltheme.cpp
@@ -7,6 +7,7 @@
 #include "xfa/fxfa/cxfa_fwltheme.h"
 
 #include "core/fxcrt/fx_codepage.h"
+#include "third_party/base/ptr_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fwl/cfwl_barcode.h"
@@ -22,6 +23,7 @@
 #include "xfa/fwl/cfwl_scrollbar.h"
 #include "xfa/fwl/cfwl_themebackground.h"
 #include "xfa/fwl/cfwl_themetext.h"
+#include "xfa/fwl/theme/cfwl_widgettp.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
@@ -35,15 +37,14 @@
 
 const float kLineHeight = 12.0f;
 
-}  // namespace
-
 CXFA_FFWidget* XFA_ThemeGetOuterWidget(CFWL_Widget* pWidget) {
-  CFWL_Widget* pOuter = pWidget;
-  while (pOuter && pOuter->GetOuter())
-    pOuter = pOuter->GetOuter();
-  return pOuter ? pOuter->GetLayoutItem() : nullptr;
+  CFWL_Widget* pOuter = pWidget ? pWidget->GetOutmost() : nullptr;
+  return pOuter ? static_cast<CXFA_FFWidget*>(pOuter->GetAdapterIface())
+                : nullptr;
 }
 
+}  // namespace
+
 CXFA_FWLTheme::CXFA_FWLTheme(CXFA_FFApp* pApp)
     : m_pCheckBoxTP(pdfium::MakeUnique<CFWL_CheckBoxTP>()),
       m_pListBoxTP(pdfium::MakeUnique<CFWL_ListBoxTP>()),
@@ -57,108 +58,105 @@
       m_pCaretTP(pdfium::MakeUnique<CFWL_CaretTP>()),
       m_pBarcodeTP(pdfium::MakeUnique<CFWL_BarcodeTP>()),
       m_pTextOut(pdfium::MakeUnique<CFDE_TextOut>()),
-      m_pCalendarFont(nullptr),
       m_pApp(pApp) {
-  m_Rect.Reset();
+}
 
+bool CXFA_FWLTheme::LoadCalendarFont(CXFA_FFDoc* doc) {
   for (size_t i = 0; !m_pCalendarFont && i < FX_ArraySize(g_FWLTheme_CalFonts);
        ++i) {
-    m_pCalendarFont = CFGAS_GEFont::LoadFont(g_FWLTheme_CalFonts[i], 0, 0,
-                                             m_pApp->GetFDEFontMgr());
-  }
-  if (!m_pCalendarFont) {
-    m_pCalendarFont = m_pApp->GetFDEFontMgr()->GetFontByCodePage(
-        FX_CODEPAGE_MSWin_WesternEuropean, 0, nullptr);
+    m_pCalendarFont =
+        m_pApp->GetXFAFontMgr()->GetFont(doc, g_FWLTheme_CalFonts[i], 0);
   }
 
-  ASSERT(m_pCalendarFont);
+  if (!m_pCalendarFont) {
+    CFGAS_FontMgr* font_mgr = m_pApp->GetFDEFontMgr();
+    if (font_mgr) {
+      m_pCalendarFont = font_mgr->GetFontByCodePage(
+          FX_CODEPAGE_MSWin_WesternEuropean, 0, nullptr);
+    }
+  }
+
+  return m_pCalendarFont != nullptr;
 }
 
 CXFA_FWLTheme::~CXFA_FWLTheme() {
   m_pTextOut.reset();
-  FWLTHEME_Release();
+  CFWL_FontManager::DestroyInstance();
 }
 
-void CXFA_FWLTheme::DrawBackground(CFWL_ThemeBackground* pParams) {
-  GetTheme(pParams->m_pWidget)->DrawBackground(pParams);
+void CXFA_FWLTheme::DrawBackground(const CFWL_ThemeBackground& pParams) {
+  GetTheme(pParams.m_pWidget)->DrawBackground(pParams);
 }
 
-void CXFA_FWLTheme::DrawText(CFWL_ThemeText* pParams) {
-  if (pParams->m_wsText.IsEmpty())
+void CXFA_FWLTheme::DrawText(const CFWL_ThemeText& pParams) {
+  if (pParams.m_wsText.IsEmpty())
     return;
 
-  if (pParams->m_pWidget->GetClassID() == FWL_Type::MonthCalendar) {
-    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
+  if (pParams.m_pWidget->GetClassID() == FWL_Type::MonthCalendar) {
+    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams.m_pWidget);
     if (!pWidget)
       return;
 
-    m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
-    m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
+    m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
+    m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
     m_pTextOut->SetFont(m_pCalendarFont);
     m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
     m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
-    if ((pParams->m_iPart == CFWL_Part::DatesIn) &&
-        !(pParams->m_dwStates & FWL_ITEMSTATE_MCD_Flag) &&
-        (pParams->m_dwStates &
+    if ((pParams.m_iPart == CFWL_Part::DatesIn) &&
+        !(pParams.m_dwStates & FWL_ITEMSTATE_MCD_Flag) &&
+        (pParams.m_dwStates &
          (CFWL_PartState_Hovered | CFWL_PartState_Selected))) {
-      m_pTextOut->SetTextColor(0xFFFFFFFF);
+      m_pTextOut->SetTextColor(0xFF888888);
     }
-    if (pParams->m_iPart == CFWL_Part::Caption)
+    if (pParams.m_iPart == CFWL_Part::Caption)
       m_pTextOut->SetTextColor(ArgbEncode(0xff, 0, 153, 255));
 
-    CXFA_Graphics* pGraphics = pParams->m_pGraphics;
+    CXFA_Graphics* pGraphics = pParams.m_pGraphics;
     CFX_RenderDevice* pRenderDevice = pGraphics->GetRenderDevice();
-    if (!pRenderDevice)
-      return;
-
-    CFX_Matrix mtPart = pParams->m_matrix;
+    CFX_Matrix mtPart = pParams.m_matrix;
     const CFX_Matrix* pMatrix = pGraphics->GetMatrix();
     if (pMatrix)
       mtPart.Concat(*pMatrix);
 
     m_pTextOut->SetMatrix(mtPart);
-    m_pTextOut->DrawLogicText(pRenderDevice, pParams->m_wsText.AsStringView(),
-                              pParams->m_rtPart);
+    m_pTextOut->DrawLogicText(pRenderDevice, pParams.m_wsText.AsStringView(),
+                              pParams.m_rtPart);
     return;
   }
-  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
+  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams.m_pWidget);
   if (!pWidget)
     return;
 
   CXFA_Node* pNode = pWidget->GetNode();
-  CXFA_Graphics* pGraphics = pParams->m_pGraphics;
+  CXFA_Graphics* pGraphics = pParams.m_pGraphics;
   CFX_RenderDevice* pRenderDevice = pGraphics->GetRenderDevice();
-  if (!pRenderDevice)
-    return;
-
-  m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
-  m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
-  m_pTextOut->SetFont(pNode->GetWidgetAcc()->GetFDEFont(pWidget->GetDoc()));
+  m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
+  m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
+  m_pTextOut->SetFont(pNode->GetFDEFont(pWidget->GetDoc()));
   m_pTextOut->SetFontSize(pNode->GetFontSize());
   m_pTextOut->SetTextColor(pNode->GetTextColor());
-  CFX_Matrix mtPart = pParams->m_matrix;
+  CFX_Matrix mtPart = pParams.m_matrix;
   const CFX_Matrix* pMatrix = pGraphics->GetMatrix();
   if (pMatrix)
     mtPart.Concat(*pMatrix);
 
   m_pTextOut->SetMatrix(mtPart);
-  m_pTextOut->DrawLogicText(pRenderDevice, pParams->m_wsText.AsStringView(),
-                            pParams->m_rtPart);
+  m_pTextOut->DrawLogicText(pRenderDevice, pParams.m_wsText.AsStringView(),
+                            pParams.m_rtPart);
 }
 
-CFX_RectF CXFA_FWLTheme::GetUIMargin(CFWL_ThemePart* pThemePart) const {
-  CFX_RectF rect;
-  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget);
+CFX_RectF CXFA_FWLTheme::GetUIMargin(const CFWL_ThemePart& pThemePart) const {
+  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget);
   if (!pWidget)
-    return rect;
+    return CFX_RectF();
 
-  CXFA_LayoutItem* pItem = pWidget;
-  CXFA_WidgetAcc* pWidgetAcc = pWidget->GetNode()->GetWidgetAcc();
-  rect = pWidgetAcc->GetUIMargin();
-  CXFA_Para* para = pWidgetAcc->GetNode()->GetParaIfExists();
+  CXFA_ContentLayoutItem* pItem = pWidget->GetLayoutItem();
+  CXFA_Node* pNode = pWidget->GetNode();
+  CFX_RectF rect = pNode->GetUIMargin();
+  CXFA_Para* para = pNode->GetParaIfExists();
   if (para) {
     rect.left += para->GetMarginLeft();
-    if (pWidgetAcc->IsMultiLine())
+    if (pNode->IsMultiLine())
       rect.width += para->GetMarginRight();
   }
   if (!pItem->GetPrev()) {
@@ -181,21 +179,21 @@
   return 1.0f;
 }
 
-float CXFA_FWLTheme::GetFontSize(CFWL_ThemePart* pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget))
+float CXFA_FWLTheme::GetFontSize(const CFWL_ThemePart& pThemePart) const {
+  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
     return pWidget->GetNode()->GetFontSize();
   return FWLTHEME_CAPACITY_FontSize;
 }
 
 RetainPtr<CFGAS_GEFont> CXFA_FWLTheme::GetFont(
-    CFWL_ThemePart* pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget))
-    return pWidget->GetNode()->GetWidgetAcc()->GetFDEFont(pWidget->GetDoc());
-  return GetTheme(pThemePart->m_pWidget)->GetFont();
+    const CFWL_ThemePart& pThemePart) const {
+  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
+    return pWidget->GetNode()->GetFDEFont(pWidget->GetDoc());
+  return GetTheme(pThemePart.m_pWidget)->GetFont();
 }
 
-float CXFA_FWLTheme::GetLineHeight(CFWL_ThemePart* pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget))
+float CXFA_FWLTheme::GetLineHeight(const CFWL_ThemePart& pThemePart) const {
+  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
     return pWidget->GetNode()->GetLineHeight();
   return kLineHeight;
 }
@@ -204,15 +202,17 @@
   return 9.0f;
 }
 
-FX_COLORREF CXFA_FWLTheme::GetTextColor(CFWL_ThemePart* pThemePart) const {
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget))
+FX_COLORREF CXFA_FWLTheme::GetTextColor(
+    const CFWL_ThemePart& pThemePart) const {
+  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget))
     return pWidget->GetNode()->GetTextColor();
   return FWLTHEME_CAPACITY_TextColor;
 }
 
-CFX_SizeF CXFA_FWLTheme::GetSpaceAboveBelow(CFWL_ThemePart* pThemePart) const {
+CFX_SizeF CXFA_FWLTheme::GetSpaceAboveBelow(
+    const CFWL_ThemePart& pThemePart) const {
   CFX_SizeF sizeAboveBelow;
-  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget)) {
+  if (CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart.m_pWidget)) {
     CXFA_Para* para = pWidget->GetNode()->GetParaIfExists();
     if (para) {
       sizeAboveBelow.width = para->GetSpaceAbove();
@@ -222,34 +222,32 @@
   return sizeAboveBelow;
 }
 
-void CXFA_FWLTheme::CalcTextRect(CFWL_ThemeText* pParams, CFX_RectF& rect) {
-  if (pParams->m_pWidget->GetClassID() == FWL_Type::MonthCalendar) {
-    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
-    if (!pWidget || !pParams || !m_pTextOut)
-      return;
+void CXFA_FWLTheme::CalcTextRect(const CFWL_ThemeText& pParams,
+                                 CFX_RectF* pRect) {
+  if (!m_pTextOut)
+    return;
 
-    m_pTextOut->SetFont(m_pCalendarFont);
-    m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
-    m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
-    m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
-    m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
-    m_pTextOut->CalcLogicSize(pParams->m_wsText, rect);
-  }
-
-  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
+  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams.m_pWidget);
   if (!pWidget)
     return;
 
+  if (pParams.m_pWidget->GetClassID() == FWL_Type::MonthCalendar) {
+    m_pTextOut->SetFont(m_pCalendarFont);
+    m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
+    m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
+    m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
+    m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
+    m_pTextOut->CalcLogicSize(pParams.m_wsText.AsStringView(), pRect);
+    return;
+  }
+
   CXFA_Node* pNode = pWidget->GetNode();
-  m_pTextOut->SetFont(pNode->GetWidgetAcc()->GetFDEFont(pWidget->GetDoc()));
+  m_pTextOut->SetFont(pNode->GetFDEFont(pWidget->GetDoc()));
   m_pTextOut->SetFontSize(pNode->GetFontSize());
   m_pTextOut->SetTextColor(pNode->GetTextColor());
-  if (!pParams)
-    return;
-
-  m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
-  m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
-  m_pTextOut->CalcLogicSize(pParams->m_wsText, rect);
+  m_pTextOut->SetAlignment(pParams.m_iTTOAlign);
+  m_pTextOut->SetStyles(pParams.m_dwTTOStyles);
+  m_pTextOut->CalcLogicSize(pParams.m_wsText.AsStringView(), pRect);
 }
 
 CFWL_WidgetTP* CXFA_FWLTheme::GetTheme(CFWL_Widget* pWidget) const {
diff --git a/xfa/fxfa/cxfa_fwltheme.h b/xfa/fxfa/cxfa_fwltheme.h
index bdab7cf..11f2584 100644
--- a/xfa/fxfa/cxfa_fwltheme.h
+++ b/xfa/fxfa/cxfa_fwltheme.h
@@ -29,19 +29,22 @@
   explicit CXFA_FWLTheme(CXFA_FFApp* pApp);
   ~CXFA_FWLTheme() override;
 
+  bool LoadCalendarFont(CXFA_FFDoc* doc);
+
   // IFWL_ThemeProvider:
-  void DrawBackground(CFWL_ThemeBackground* pParams) override;
-  void DrawText(CFWL_ThemeText* pParams) override;
-  void CalcTextRect(CFWL_ThemeText* pParams, CFX_RectF& rect) override;
+  void DrawBackground(const CFWL_ThemeBackground& pParams) override;
+  void DrawText(const CFWL_ThemeText& pParams) override;
+  void CalcTextRect(const CFWL_ThemeText& pParams, CFX_RectF* pRect) override;
   float GetCXBorderSize() const override;
   float GetCYBorderSize() const override;
-  CFX_RectF GetUIMargin(CFWL_ThemePart* pThemePart) const override;
-  float GetFontSize(CFWL_ThemePart* pThemePart) const override;
-  RetainPtr<CFGAS_GEFont> GetFont(CFWL_ThemePart* pThemePart) const override;
-  float GetLineHeight(CFWL_ThemePart* pThemePart) const override;
+  CFX_RectF GetUIMargin(const CFWL_ThemePart& pThemePart) const override;
+  float GetFontSize(const CFWL_ThemePart& pThemePart) const override;
+  RetainPtr<CFGAS_GEFont> GetFont(
+      const CFWL_ThemePart& pThemePart) const override;
+  float GetLineHeight(const CFWL_ThemePart& pThemePart) const override;
   float GetScrollBarWidth() const override;
-  FX_COLORREF GetTextColor(CFWL_ThemePart* pThemePart) const override;
-  CFX_SizeF GetSpaceAboveBelow(CFWL_ThemePart* pThemePart) const override;
+  FX_COLORREF GetTextColor(const CFWL_ThemePart& pThemePart) const override;
+  CFX_SizeF GetSpaceAboveBelow(const CFWL_ThemePart& pThemePart) const override;
 
  private:
   CFWL_WidgetTP* GetTheme(CFWL_Widget* pWidget) const;
@@ -64,6 +67,4 @@
   CFX_RectF m_Rect;
 };
 
-CXFA_FFWidget* XFA_ThemeGetOuterWidget(CFWL_Widget* pWidget);
-
 #endif  // XFA_FXFA_CXFA_FWLTHEME_H_
diff --git a/xfa/fxfa/cxfa_imagerenderer.cpp b/xfa/fxfa/cxfa_imagerenderer.cpp
index f6cb5e9..d3b66c8 100644
--- a/xfa/fxfa/cxfa_imagerenderer.cpp
+++ b/xfa/fxfa/cxfa_imagerenderer.cpp
@@ -7,25 +7,24 @@
 #include "xfa/fxfa/cxfa_imagerenderer.h"
 
 #include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/dib/cfx_dibsource.h"
+#include "core/fxge/dib/cfx_dibbase.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/cfx_imagerenderer.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
 #include "third_party/base/ptr_util.h"
 
-CXFA_ImageRenderer::CXFA_ImageRenderer(
-    CFX_RenderDevice* pDevice,
-    const RetainPtr<CFX_DIBSource>& pDIBSource,
-    const CFX_Matrix* pImage2Device)
-    : m_pDevice(pDevice),
-      m_ImageMatrix(*pImage2Device),
-      m_pDIBSource(pDIBSource) {}
+CXFA_ImageRenderer::CXFA_ImageRenderer(CFX_RenderDevice* pDevice,
+                                       const RetainPtr<CFX_DIBBase>& pDIBBase,
+                                       const CFX_Matrix* pImage2Device)
+    : m_pDevice(pDevice), m_ImageMatrix(*pImage2Device), m_pDIBBase(pDIBBase) {}
 
-CXFA_ImageRenderer::~CXFA_ImageRenderer() {}
+CXFA_ImageRenderer::~CXFA_ImageRenderer() = default;
 
 bool CXFA_ImageRenderer::Start() {
-  if (m_pDevice->StartDIBitsWithBlend(m_pDIBSource, 255, 0, &m_ImageMatrix,
-                                      FXDIB_INTERPOL, &m_DeviceHandle,
-                                      FXDIB_BLEND_NORMAL)) {
+  FXDIB_ResampleOptions options;
+  options.bInterpolateBilinear = true;
+  if (m_pDevice->StartDIBits(m_pDIBBase, 255, 0, m_ImageMatrix, options,
+                             &m_DeviceHandle)) {
     if (m_DeviceHandle) {
       m_Status = 3;
       return true;
@@ -38,11 +37,11 @@
   int dest_height = image_rect.Height();
   if ((fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) ||
       (fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) {
-    RetainPtr<CFX_DIBSource> pDib = m_pDIBSource;
-    if (m_pDIBSource->HasAlpha() &&
+    RetainPtr<CFX_DIBBase> pDib = m_pDIBBase;
+    if (m_pDIBBase->HasAlpha() &&
         !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_IMAGE) &&
         !(m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
-      m_pCloneConvert = m_pDIBSource->CloneConvert(FXDIB_Rgb);
+      m_pCloneConvert = m_pDIBBase->CloneConvert(FXDIB_Rgb);
       if (!m_pCloneConvert)
         return false;
 
@@ -52,7 +51,7 @@
     clip_box.Intersect(image_rect);
     m_Status = 2;
     m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
-        pDib, &m_ImageMatrix, FXDIB_INTERPOL, &clip_box);
+        pDib, m_ImageMatrix, options, &clip_box);
     return true;
   }
   if (m_ImageMatrix.a < 0)
@@ -62,17 +61,17 @@
   int dest_left, dest_top;
   dest_left = dest_width > 0 ? image_rect.left : image_rect.right;
   dest_top = dest_height > 0 ? image_rect.top : image_rect.bottom;
-  if (m_pDIBSource->IsOpaqueImage()) {
+  if (m_pDIBBase->IsOpaqueImage()) {
     if (m_pDevice->StretchDIBitsWithFlagsAndBlend(
-            m_pDIBSource, dest_left, dest_top, dest_width, dest_height,
-            FXDIB_INTERPOL, FXDIB_BLEND_NORMAL)) {
+            m_pDIBBase, dest_left, dest_top, dest_width, dest_height, options,
+            BlendMode::kNormal)) {
       return false;
     }
   }
-  if (m_pDIBSource->IsAlphaMask()) {
-    if (m_pDevice->StretchBitMaskWithFlags(m_pDIBSource, dest_left, dest_top,
+  if (m_pDIBBase->IsAlphaMask()) {
+    if (m_pDevice->StretchBitMaskWithFlags(m_pDIBBase, dest_left, dest_top,
                                            dest_width, dest_height, 0,
-                                           FXDIB_INTERPOL)) {
+                                           options)) {
       return false;
     }
   }
@@ -83,8 +82,8 @@
   FX_RECT dest_clip(
       dest_rect.left - image_rect.left, dest_rect.top - image_rect.top,
       dest_rect.right - image_rect.left, dest_rect.bottom - image_rect.top);
-  RetainPtr<CFX_DIBitmap> pStretched = m_pDIBSource->StretchTo(
-      dest_width, dest_height, FXDIB_INTERPOL, &dest_clip);
+  RetainPtr<CFX_DIBitmap> pStretched =
+      m_pDIBBase->StretchTo(dest_width, dest_height, options, &dest_clip);
   if (pStretched)
     CompositeDIBitmap(pStretched, dest_rect.left, dest_rect.top);
 
@@ -106,7 +105,7 @@
     } else {
       m_pDevice->SetDIBitsWithBlend(pBitmap, m_pTransformer->result().left,
                                     m_pTransformer->result().top,
-                                    FXDIB_BLEND_NORMAL);
+                                    BlendMode::kNormal);
     }
     return false;
   }
@@ -137,7 +136,7 @@
     if (pDIBitmap->IsAlphaMask())
       return;
 
-    m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, FXDIB_BLEND_NORMAL);
+    m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, BlendMode::kNormal);
     return;
   }
   if (!pDIBitmap->HasAlpha() ||
@@ -149,7 +148,8 @@
   if (!pCloneConvert)
     return;
 
-  CXFA_ImageRenderer imageRender(m_pDevice, pCloneConvert, &m_ImageMatrix);
+  CXFA_ImageRenderer imageRender(m_pDevice.Get(), pCloneConvert,
+                                 &m_ImageMatrix);
   if (!imageRender.Start())
     return;
 
diff --git a/xfa/fxfa/cxfa_imagerenderer.h b/xfa/fxfa/cxfa_imagerenderer.h
index 8ed9ab0..37e45d0 100644
--- a/xfa/fxfa/cxfa_imagerenderer.h
+++ b/xfa/fxfa/cxfa_imagerenderer.h
@@ -11,10 +11,10 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CFX_RenderDevice;
-class CFX_DIBSource;
+class CFX_DIBBase;
 class CFX_DIBitmap;
 class CFX_ImageTransformer;
 class CFX_ImageRenderer;
@@ -22,7 +22,7 @@
 class CXFA_ImageRenderer {
  public:
   CXFA_ImageRenderer(CFX_RenderDevice* pDevice,
-                     const RetainPtr<CFX_DIBSource>& pDIBSource,
+                     const RetainPtr<CFX_DIBBase>& pDIBBase,
                      const CFX_Matrix* pImage2Device);
   ~CXFA_ImageRenderer();
 
@@ -34,10 +34,10 @@
                          int left,
                          int top);
 
-  CFX_RenderDevice* m_pDevice;
+  UnownedPtr<CFX_RenderDevice> m_pDevice;
   int m_Status = 0;
   CFX_Matrix m_ImageMatrix;
-  RetainPtr<CFX_DIBSource> m_pDIBSource;
+  RetainPtr<CFX_DIBBase> m_pDIBBase;
   RetainPtr<CFX_DIBitmap> m_pCloneConvert;
   std::unique_ptr<CFX_ImageTransformer> m_pTransformer;
   std::unique_ptr<CFX_ImageRenderer> m_DeviceHandle;
diff --git a/xfa/fxfa/cxfa_linkuserdata.cpp b/xfa/fxfa/cxfa_linkuserdata.cpp
deleted file mode 100644
index c32b746..0000000
--- a/xfa/fxfa/cxfa_linkuserdata.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_linkuserdata.h"
-
-CXFA_LinkUserData::CXFA_LinkUserData(wchar_t* pszText)
-    : m_wsURLContent(pszText) {}
-
-CXFA_LinkUserData::~CXFA_LinkUserData() {}
diff --git a/xfa/fxfa/cxfa_linkuserdata.h b/xfa/fxfa/cxfa_linkuserdata.h
deleted file mode 100644
index 3d0f95e..0000000
--- a/xfa/fxfa/cxfa_linkuserdata.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_LINKUSERDATA_H_
-#define XFA_FXFA_CXFA_LINKUSERDATA_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-
-class CXFA_LinkUserData : public Retainable {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  const wchar_t* GetLinkURL() const { return m_wsURLContent.c_str(); }
-
- private:
-  explicit CXFA_LinkUserData(wchar_t* pszText);
-  ~CXFA_LinkUserData() override;
-
-  WideString m_wsURLContent;
-};
-
-#endif  // XFA_FXFA_CXFA_LINKUSERDATA_H_
diff --git a/xfa/fxfa/cxfa_loadercontext.cpp b/xfa/fxfa/cxfa_loadercontext.cpp
index 05ba9f1..5f66050 100644
--- a/xfa/fxfa/cxfa_loadercontext.cpp
+++ b/xfa/fxfa/cxfa_loadercontext.cpp
@@ -6,16 +6,8 @@
 
 #include "xfa/fxfa/cxfa_loadercontext.h"
 
-CXFA_LoaderContext::CXFA_LoaderContext()
-    : m_bSaveLineHeight(false),
-      m_fWidth(0),
-      m_fHeight(0),
-      m_fLastPos(0),
-      m_fStartLineOffset(0),
-      m_iChar(0),
-      m_iTotalLines(-1),
-      m_dwFlags(0),
-      m_pXMLNode(nullptr),
-      m_pNode(nullptr) {}
+#include "core/fxcrt/css/cfx_csscomputedstyle.h"
 
-CXFA_LoaderContext::~CXFA_LoaderContext() {}
+CXFA_LoaderContext::CXFA_LoaderContext() = default;
+
+CXFA_LoaderContext::~CXFA_LoaderContext() = default;
diff --git a/xfa/fxfa/cxfa_loadercontext.h b/xfa/fxfa/cxfa_loadercontext.h
index 06e3f11..ddcb909 100644
--- a/xfa/fxfa/cxfa_loadercontext.h
+++ b/xfa/fxfa/cxfa_loadercontext.h
@@ -9,31 +9,37 @@
 
 #include <vector>
 
-#include "core/fxcrt/css/cfx_csscomputedstyle.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 
+class CFX_CSSComputedStyle;
 class CFX_XMLNode;
 class CXFA_Node;
 
-class CXFA_LoaderContext {
- public:
+struct CXFA_BlockHeight {
+  size_t szBlockIndex;
+  float fHeight;
+};
+
+struct CXFA_LoaderContext {
   CXFA_LoaderContext();
   ~CXFA_LoaderContext();
 
-  bool m_bSaveLineHeight;
-  float m_fWidth;
-  float m_fHeight;
-  float m_fLastPos;
-  float m_fStartLineOffset;
-  int32_t m_iChar;
-  int32_t m_iLines;
-  int32_t m_iTotalLines;
-  uint32_t m_dwFlags;
-  CFX_XMLNode* m_pXMLNode;
-  CXFA_Node* m_pNode;
-  RetainPtr<CFX_CSSComputedStyle> m_pParentStyle;
-  std::vector<float> m_lineHeights;
-  std::vector<float> m_BlocksHeight;
+  bool bSaveLineHeight = false;
+  bool bFilterSpace = false;
+  float fWidth = 0;
+  float fHeight = 0;
+  float fLastPos = 0;
+  float fStartLineOffset = 0;
+  int32_t iChar = 0;
+  // TODO(thestig): Make this size_t?
+  int32_t iTotalLines = -1;
+  UnownedPtr<const CFX_XMLNode> pXMLNode;
+  UnownedPtr<CXFA_Node> pNode;
+  RetainPtr<CFX_CSSComputedStyle> pParentStyle;
+  std::vector<float> lineHeights;
+  std::vector<CXFA_BlockHeight> blockHeights;
 };
 
 #endif  // XFA_FXFA_CXFA_LOADERCONTEXT_H_
diff --git a/xfa/fxfa/cxfa_pieceline.h b/xfa/fxfa/cxfa_pieceline.h
index dc95e73..0e233ac 100644
--- a/xfa/fxfa/cxfa_pieceline.h
+++ b/xfa/fxfa/cxfa_pieceline.h
@@ -18,7 +18,7 @@
   ~CXFA_PieceLine();
 
   std::vector<std::unique_ptr<CXFA_TextPiece>> m_textPieces;
-  std::vector<int32_t> m_charCounts;
+  std::vector<size_t> m_charCounts;
 };
 
 #endif  // XFA_FXFA_CXFA_PIECELINE_H_
diff --git a/xfa/fxfa/cxfa_readynodeiterator.cpp b/xfa/fxfa/cxfa_readynodeiterator.cpp
new file mode 100644
index 0000000..728d9d6
--- /dev/null
+++ b/xfa/fxfa/cxfa_readynodeiterator.cpp
@@ -0,0 +1,31 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/cxfa_readynodeiterator.h"
+
+#include "xfa/fxfa/parser/cxfa_node.h"
+
+CXFA_ReadyNodeIterator::CXFA_ReadyNodeIterator(CXFA_Node* pTravelRoot)
+    : m_ContentIterator(pTravelRoot) {}
+
+CXFA_ReadyNodeIterator::~CXFA_ReadyNodeIterator() {}
+
+CXFA_Node* CXFA_ReadyNodeIterator::MoveToNext() {
+  CXFA_Node* pItem = m_pCurNode ? m_ContentIterator.MoveToNext()
+                                : m_ContentIterator.GetCurrent();
+  while (pItem) {
+    m_pCurNode = pItem->IsWidgetReady() ? pItem : nullptr;
+    if (m_pCurNode)
+      return m_pCurNode.Get();
+    pItem = m_ContentIterator.MoveToNext();
+  }
+  return nullptr;
+}
+
+void CXFA_ReadyNodeIterator::SkipTree() {
+  m_ContentIterator.SkipChildrenAndMoveToNext();
+  m_pCurNode = nullptr;
+}
diff --git a/xfa/fxfa/cxfa_readynodeiterator.h b/xfa/fxfa/cxfa_readynodeiterator.h
new file mode 100644
index 0000000..d3cd901
--- /dev/null
+++ b/xfa/fxfa/cxfa_readynodeiterator.h
@@ -0,0 +1,28 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_CXFA_READYNODEITERATOR_H_
+#define XFA_FXFA_CXFA_READYNODEITERATOR_H_
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
+
+class CXFA_Node;
+
+class CXFA_ReadyNodeIterator {
+ public:
+  explicit CXFA_ReadyNodeIterator(CXFA_Node* pTravelRoot);
+  ~CXFA_ReadyNodeIterator();
+
+  CXFA_Node* MoveToNext();
+  void SkipTree();
+
+ private:
+  CXFA_ContainerIterator m_ContentIterator;
+  UnownedPtr<CXFA_Node> m_pCurNode;
+};
+
+#endif  // XFA_FXFA_CXFA_READYNODEITERATOR_H_
diff --git a/xfa/fxfa/cxfa_rendercontext.cpp b/xfa/fxfa/cxfa_rendercontext.cpp
index 6667d46..106fcbe 100644
--- a/xfa/fxfa/cxfa_rendercontext.cpp
+++ b/xfa/fxfa/cxfa_rendercontext.cpp
@@ -8,29 +8,25 @@
 
 #include "xfa/fxfa/cxfa_ffpageview.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxgraphics/cxfa_graphics.h"
 
 CXFA_RenderContext::CXFA_RenderContext(CXFA_FFPageView* pPageView,
                                        const CFX_RectF& clipRect,
                                        const CFX_Matrix& matrix)
-    : m_pWidget(nullptr), m_matrix(matrix), m_rtClipRect(clipRect) {
-  matrix.GetInverse().TransformRect(m_rtClipRect);
+    : m_pWidgetIterator(pPageView->CreateFormWidgetIterator(
+          XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)),
+      m_pWidget(m_pWidgetIterator->MoveToNext()),
+      m_matrix(matrix),
+      m_rtClipRect(clipRect) {}
 
-  m_pWidgetIterator = pPageView->CreateWidgetIterator(
-      XFA_TRAVERSEWAY_Form,
-      XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable);
-  m_pWidget = m_pWidgetIterator->MoveToNext();
-}
-
-CXFA_RenderContext::~CXFA_RenderContext() {}
+CXFA_RenderContext::~CXFA_RenderContext() = default;
 
 void CXFA_RenderContext::DoRender(CXFA_Graphics* gs) {
   while (m_pWidget) {
-    CFX_RectF rtWidgetBox = m_pWidget->GetBBox(XFA_WidgetStatus_Visible);
-    rtWidgetBox.width += 1;
-    rtWidgetBox.height += 1;
+    CFX_RectF rtWidgetBox = m_pWidget->GetBBox(CXFA_FFWidget::kDoNotDrawFocus);
+    ++rtWidgetBox.width;
+    ++rtWidgetBox.height;
     if (rtWidgetBox.IntersectWith(m_rtClipRect))
-      m_pWidget->RenderWidget(gs, m_matrix, XFA_WidgetStatus_Highlight);
+      m_pWidget->RenderWidget(gs, m_matrix, CXFA_FFWidget::kHighlight);
 
     m_pWidget = m_pWidgetIterator->MoveToNext();
   }
diff --git a/xfa/fxfa/cxfa_rendercontext.h b/xfa/fxfa/cxfa_rendercontext.h
index 411830d..457c62f 100644
--- a/xfa/fxfa/cxfa_rendercontext.h
+++ b/xfa/fxfa/cxfa_rendercontext.h
@@ -9,9 +9,13 @@
 
 #include <memory>
 
-#include "xfa/fxfa/fxfa.h"
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CXFA_Graphics;
+class CXFA_FFPageView;
+class CXFA_FFWidget;
+class IXFA_WidgetIterator;
 
 class CXFA_RenderContext {
  public:
@@ -23,10 +27,10 @@
   void DoRender(CXFA_Graphics* gs);
 
  private:
-  std::unique_ptr<IXFA_WidgetIterator> m_pWidgetIterator;
-  CXFA_FFWidget* m_pWidget;
-  CFX_Matrix m_matrix;
-  CFX_RectF m_rtClipRect;
+  std::unique_ptr<IXFA_WidgetIterator> const m_pWidgetIterator;
+  UnownedPtr<CXFA_FFWidget> m_pWidget;
+  const CFX_Matrix m_matrix;
+  const CFX_RectF m_rtClipRect;
 };
 
 #endif  // XFA_FXFA_CXFA_RENDERCONTEXT_H_
diff --git a/xfa/fxfa/cxfa_textlayout.cpp b/xfa/fxfa/cxfa_textlayout.cpp
index ebb9e8a..45f682c 100644
--- a/xfa/fxfa/cxfa_textlayout.cpp
+++ b/xfa/fxfa/cxfa_textlayout.cpp
@@ -16,35 +16,59 @@
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/text_char_pos.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fxfa/cxfa_linkuserdata.h"
+#include "xfa/fgas/layout/cfx_linkuserdata.h"
+#include "xfa/fgas/layout/cfx_rtfbreak.h"
+#include "xfa/fgas/layout/cfx_textuserdata.h"
 #include "xfa/fxfa/cxfa_loadercontext.h"
 #include "xfa/fxfa/cxfa_pieceline.h"
 #include "xfa/fxfa/cxfa_textparsecontext.h"
 #include "xfa/fxfa/cxfa_textpiece.h"
 #include "xfa/fxfa/cxfa_textprovider.h"
 #include "xfa/fxfa/cxfa_texttabstopscontext.h"
-#include "xfa/fxfa/cxfa_textuserdata.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
 
-#define XFA_LOADERCNTXTFLG_FILTERSPACE 0x001
+namespace {
+
+constexpr float kHeightTolerance = 0.001f;
+
+void ProcessText(WideString* pText) {
+  int32_t iLen = pText->GetLength();
+  if (iLen == 0)
+    return;
+
+  int32_t iTrimLeft = 0;
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> psz = pText->GetBuffer(iLen);
+    wchar_t wPrev = 0;
+    for (int32_t i = 0; i < iLen; i++) {
+      wchar_t wch = psz[i];
+      if (wch < 0x20)
+        wch = 0x20;
+      if (wch == 0x20 && wPrev == 0x20)
+        continue;
+
+      wPrev = wch;
+      psz[iTrimLeft++] = wch;
+    }
+  }
+  pText->ReleaseBuffer(iTrimLeft);
+}
+
+}  // namespace
 
 CXFA_TextLayout::CXFA_TextLayout(CXFA_FFDoc* doc,
                                  CXFA_TextProvider* pTextProvider)
-    : m_bHasBlock(false),
-      m_pDoc(doc),
-      m_pTextProvider(pTextProvider),
-      m_pTextDataNode(nullptr),
-      m_bRichText(false),
-      m_iLines(0),
-      m_fMaxWidth(0),
-      m_bBlockContinue(true) {
+    : m_pDoc(doc), m_pTextProvider(pTextProvider) {
   ASSERT(m_pTextProvider);
 }
 
@@ -59,10 +83,7 @@
 }
 
 void CXFA_TextLayout::GetTextDataNode() {
-  if (!m_pTextProvider)
-    return;
-
-  CXFA_Node* pNode = m_pTextProvider->GetTextNode(m_bRichText);
+  CXFA_Node* pNode = m_pTextProvider->GetTextNode(&m_bRichText);
   if (pNode && m_bRichText)
     m_textParser.Reset();
 
@@ -77,20 +98,16 @@
   if (!pXMLRoot)
     return nullptr;
 
-  CFX_XMLNode* pXMLContainer = nullptr;
-  for (CFX_XMLNode* pXMLChild = pXMLRoot->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLChild;
-       pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    if (pXMLChild->GetType() == FX_XMLNODE_Element) {
-      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
-      WideString wsTag = pXMLElement->GetLocalTagName();
-      if (wsTag == L"body" || wsTag == L"html") {
-        pXMLContainer = pXMLChild;
-        break;
-      }
-    }
+  for (CFX_XMLNode* pXMLChild = pXMLRoot->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    CFX_XMLElement* pXMLElement = ToXMLElement(pXMLChild);
+    if (!pXMLElement)
+      continue;
+    WideString wsTag = pXMLElement->GetLocalTagName();
+    if (wsTag.EqualsASCII("body") || wsTag.EqualsASCII("html"))
+      return pXMLChild;
   }
-  return pXMLContainer;
+  return nullptr;
 }
 
 std::unique_ptr<CFX_RTFBreak> CXFA_TextLayout::CreateBreak(bool bDefault) {
@@ -100,7 +117,7 @@
 
   auto pBreak = pdfium::MakeUnique<CFX_RTFBreak>(dwStyle);
   pBreak->SetLineBreakTolerance(1);
-  pBreak->SetFont(m_textParser.GetFont(m_pDoc, m_pTextProvider, nullptr));
+  pBreak->SetFont(m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
   pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, nullptr));
   return pBreak;
 }
@@ -112,20 +129,20 @@
   if (para) {
     CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left;
     switch (para->GetHorizontalAlign()) {
-      case XFA_AttributeEnum::Center:
+      case XFA_AttributeValue::Center:
         iAlign = CFX_RTFLineAlignment::Center;
         break;
-      case XFA_AttributeEnum::Right:
+      case XFA_AttributeValue::Right:
         iAlign = CFX_RTFLineAlignment::Right;
         break;
-      case XFA_AttributeEnum::Justify:
+      case XFA_AttributeValue::Justify:
         iAlign = CFX_RTFLineAlignment::Justified;
         break;
-      case XFA_AttributeEnum::JustifyAll:
+      case XFA_AttributeValue::JustifyAll:
         iAlign = CFX_RTFLineAlignment::Distributed;
         break;
-      case XFA_AttributeEnum::Left:
-      case XFA_AttributeEnum::Radix:
+      case XFA_AttributeValue::Left:
+      case XFA_AttributeValue::Radix:
         break;
       default:
         NOTREACHED();
@@ -162,14 +179,15 @@
 
   float fFontSize = m_textParser.GetFontSize(m_pTextProvider, nullptr);
   m_pBreak->SetFontSize(fFontSize);
-  m_pBreak->SetFont(m_textParser.GetFont(m_pDoc, m_pTextProvider, nullptr));
+  m_pBreak->SetFont(
+      m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
 }
 
 void CXFA_TextLayout::InitBreak(CFX_CSSComputedStyle* pStyle,
                                 CFX_CSSDisplay eDisplay,
                                 float fLineWidth,
-                                CFX_XMLNode* pXMLNode,
+                                const CFX_XMLNode* pXMLNode,
                                 CFX_CSSComputedStyle* pParentStyle) {
   if (!pStyle) {
     InitBreak(fLineWidth);
@@ -242,7 +260,8 @@
   float fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle);
   m_pBreak->SetFontSize(fFontSize);
   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
-  m_pBreak->SetFont(m_textParser.GetFont(m_pDoc, m_pTextProvider, pStyle));
+  m_pBreak->SetFont(
+      m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, pStyle));
   m_pBreak->SetHorizontalScale(
       m_textParser.GetHorScale(m_pTextProvider, pStyle, pXMLNode));
   m_pBreak->SetVerticalScale(m_textParser.GetVerScale(m_pTextProvider, pStyle));
@@ -253,19 +272,19 @@
   if (!m_pLoader)
     return 0;
 
-  if (m_pLoader->m_lineHeights.empty() && m_pLoader->m_fWidth > 0) {
-    CFX_SizeF szMax(m_pLoader->m_fWidth, m_pLoader->m_fHeight);
-    m_pLoader->m_bSaveLineHeight = true;
-    m_pLoader->m_fLastPos = 0;
+  if (m_pLoader->lineHeights.empty() && m_pLoader->fWidth > 0) {
+    CFX_SizeF szMax(m_pLoader->fWidth, m_pLoader->fHeight);
+    m_pLoader->bSaveLineHeight = true;
+    m_pLoader->fLastPos = 0;
     CFX_SizeF szDef = CalcSize(szMax, szMax);
-    m_pLoader->m_bSaveLineHeight = false;
+    m_pLoader->bSaveLineHeight = false;
     return szDef.height;
   }
 
-  float fHeight = m_pLoader->m_fHeight;
+  float fHeight = m_pLoader->fHeight;
   if (fHeight < 0.1f) {
     fHeight = 0;
-    for (float value : m_pLoader->m_lineHeights)
+    for (float value : m_pLoader->lineHeights)
       fHeight += value;
   }
   return fHeight;
@@ -276,107 +295,119 @@
     m_pLoader = pdfium::MakeUnique<CXFA_LoaderContext>();
 
   if (fWidth < 0 ||
-      (m_pLoader->m_fWidth > -1 && fabs(fWidth - m_pLoader->m_fWidth) > 0)) {
-    m_pLoader->m_lineHeights.clear();
+      (m_pLoader->fWidth > -1 && fabs(fWidth - m_pLoader->fWidth) > 0)) {
+    m_pLoader->lineHeights.clear();
     m_Blocks.clear();
     Unload();
-    m_pLoader->m_fStartLineOffset = 0;
+    m_pLoader->fStartLineOffset = 0;
   }
-  m_pLoader->m_fWidth = fWidth;
+  m_pLoader->fWidth = fWidth;
 
   if (fWidth >= 0)
     return fWidth;
 
   CFX_SizeF szMax;
 
-  m_pLoader->m_bSaveLineHeight = true;
-  m_pLoader->m_fLastPos = 0;
+  m_pLoader->bSaveLineHeight = true;
+  m_pLoader->fLastPos = 0;
   CFX_SizeF szDef = CalcSize(szMax, szMax);
-  m_pLoader->m_bSaveLineHeight = false;
+  m_pLoader->bSaveLineHeight = false;
   return szDef.width;
 }
 
-float CXFA_TextLayout::DoLayout(int32_t iBlockIndex,
-                                float fCalcHeight,
-                                float fContentAreaHeight,
-                                float fTextHeight) {
+float CXFA_TextLayout::DoLayout(float fTextHeight) {
+  if (!m_pLoader)
+    return fTextHeight;
+
+  UpdateLoaderHeight(fTextHeight);
+  return fTextHeight;
+}
+
+float CXFA_TextLayout::DoSplitLayout(size_t szBlockIndex,
+                                     float fCalcHeight,
+                                     float fTextHeight) {
   if (!m_pLoader)
     return fCalcHeight;
 
-  int32_t iBlockCount = pdfium::CollectionSize<int32_t>(m_Blocks);
-  float fHeight = fTextHeight;
-  if (fHeight < 0)
-    fHeight = GetLayoutHeight();
+  UpdateLoaderHeight(fTextHeight);
 
-  m_pLoader->m_fHeight = fHeight;
-  if (fContentAreaHeight < 0)
+  if (fCalcHeight < 0)
     return fCalcHeight;
 
   m_bHasBlock = true;
-  if (iBlockCount == 0 && fHeight > 0) {
-    fHeight = fTextHeight - GetLayoutHeight();
+  if (m_Blocks.empty() && m_pLoader->fHeight > 0) {
+    float fHeight = fTextHeight - GetLayoutHeight();
     if (fHeight > 0) {
-      XFA_AttributeEnum iAlign = m_textParser.GetVAlign(m_pTextProvider);
-      if (iAlign == XFA_AttributeEnum::Middle)
+      XFA_AttributeValue iAlign = m_textParser.GetVAlign(m_pTextProvider);
+      if (iAlign == XFA_AttributeValue::Middle)
         fHeight /= 2.0f;
-      else if (iAlign != XFA_AttributeEnum::Bottom)
+      else if (iAlign != XFA_AttributeValue::Bottom)
         fHeight = 0;
-      m_pLoader->m_fStartLineOffset = fHeight;
+      m_pLoader->fStartLineOffset = fHeight;
     }
   }
 
-  float fLinePos = m_pLoader->m_fStartLineOffset;
-  int32_t iLineIndex = 0;
-  if (iBlockCount > 1) {
-    if (iBlockCount >= (iBlockIndex + 1) * 2) {
-      iLineIndex = m_Blocks[iBlockIndex * 2];
-    } else {
-      iLineIndex = m_Blocks[iBlockCount - 1] + m_Blocks[iBlockCount - 2];
-    }
-    if (!m_pLoader->m_BlocksHeight.empty()) {
-      for (int32_t i = 0; i < iBlockIndex; i++)
-        fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1];
+  float fLinePos = m_pLoader->fStartLineOffset;
+  size_t szLineIndex = 0;
+  if (!m_Blocks.empty()) {
+    if (szBlockIndex < m_Blocks.size())
+      szLineIndex = m_Blocks[szBlockIndex].szIndex;
+    else
+      szLineIndex = GetNextIndexFromLastBlockData();
+    for (size_t i = 0;
+         i < std::min(szBlockIndex, m_pLoader->blockHeights.size()); ++i) {
+      fLinePos -= m_pLoader->blockHeights[i].fHeight;
     }
   }
 
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_pLoader->m_lineHeights);
-  int32_t i = 0;
-  for (i = iLineIndex; i < iCount; i++) {
-    float fLineHeight = m_pLoader->m_lineHeights[i];
-    if (i == iLineIndex && fLineHeight - fContentAreaHeight > 0.001)
-      return 0;
+  if (szLineIndex >= m_pLoader->lineHeights.size())
+    return fCalcHeight;
 
-    if (fLinePos + fLineHeight - fContentAreaHeight > 0.001) {
-      if (iBlockCount >= (iBlockIndex + 1) * 2) {
-        m_Blocks[iBlockIndex * 2] = iLineIndex;
-        m_Blocks[iBlockIndex * 2 + 1] = i - iLineIndex;
-      } else {
-        m_Blocks.push_back(iLineIndex);
-        m_Blocks.push_back(i - iLineIndex);
-      }
-      if (i == iLineIndex) {
-        if (fCalcHeight <= fLinePos) {
-          if (pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight) >
-                  iBlockIndex * 2 &&
-              (m_pLoader->m_BlocksHeight[iBlockIndex * 2] == iBlockIndex)) {
-            m_pLoader->m_BlocksHeight[iBlockIndex * 2 + 1] = fCalcHeight;
-          } else {
-            m_pLoader->m_BlocksHeight.push_back((float)iBlockIndex);
-            m_pLoader->m_BlocksHeight.push_back(fCalcHeight);
-          }
-        }
-        return fCalcHeight;
-      }
+  if (m_pLoader->lineHeights[szLineIndex] - fCalcHeight > kHeightTolerance)
+    return 0;
+
+  for (size_t i = szLineIndex; i < m_pLoader->lineHeights.size(); ++i) {
+    float fLineHeight = m_pLoader->lineHeights[i];
+    if (fLinePos + fLineHeight - fCalcHeight <= kHeightTolerance) {
+      fLinePos += fLineHeight;
+      continue;
+    }
+
+    if (szBlockIndex < m_Blocks.size())
+      m_Blocks[szBlockIndex] = {szLineIndex, i - szLineIndex};
+    else
+      m_Blocks.push_back({szLineIndex, i - szLineIndex});
+
+    if (i != szLineIndex)
       return fLinePos;
+
+    if (fCalcHeight > fLinePos)
+      return fCalcHeight;
+
+    if (szBlockIndex < m_pLoader->blockHeights.size() &&
+        m_pLoader->blockHeights[szBlockIndex].szBlockIndex == szBlockIndex) {
+      m_pLoader->blockHeights[szBlockIndex].fHeight = fCalcHeight;
+    } else {
+      m_pLoader->blockHeights.push_back({szBlockIndex, fCalcHeight});
     }
-    fLinePos += fLineHeight;
+    return fCalcHeight;
   }
   return fCalcHeight;
 }
 
-int32_t CXFA_TextLayout::CountBlocks() const {
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_Blocks) / 2;
-  return iCount > 0 ? iCount : 1;
+size_t CXFA_TextLayout::CountBlocks() const {
+  size_t szCount = m_Blocks.size();
+  return szCount > 0 ? szCount : 1;
+}
+
+size_t CXFA_TextLayout::GetNextIndexFromLastBlockData() const {
+  return m_Blocks.back().szIndex + m_Blocks.back().szLength;
+}
+
+void CXFA_TextLayout::UpdateLoaderHeight(float fTextHeight) {
+  m_pLoader->fHeight = fTextHeight;
+  if (m_pLoader->fHeight < 0)
+    m_pLoader->fHeight = GetLayoutHeight();
 }
 
 CFX_SizeF CXFA_TextLayout::CalcSize(const CFX_SizeF& minSize,
@@ -389,7 +420,7 @@
   float fLinePos = 0;
   m_iLines = 0;
   m_fMaxWidth = 0;
-  Loader(width, fLinePos, false);
+  Loader(width, &fLinePos, false);
   if (fLinePos < 0.1f)
     fLinePos = m_textParser.GetFontSize(m_pTextProvider, nullptr);
 
@@ -404,53 +435,48 @@
   Unload();
   m_pBreak = CreateBreak(true);
   if (m_pLoader) {
-    m_pLoader->m_iTotalLines = -1;
-    m_pLoader->m_iChar = 0;
+    m_pLoader->iTotalLines = -1;
+    m_pLoader->iChar = 0;
   }
 
   m_iLines = 0;
   float fLinePos = 0;
-  Loader(size.width, fLinePos, true);
+  Loader(size.width, &fLinePos, true);
   UpdateAlign(size.height, fLinePos);
   m_pTabstopContext.reset();
   return fLinePos;
 }
 
-bool CXFA_TextLayout::Layout(int32_t iBlock) {
-  if (!m_pLoader || iBlock < 0 || iBlock >= CountBlocks())
-    return false;
-  if (m_pLoader->m_fWidth < 1)
+bool CXFA_TextLayout::LayoutInternal(size_t szBlockIndex) {
+  ASSERT(szBlockIndex < CountBlocks());
+
+  if (!m_pLoader || m_pLoader->fWidth < 1)
     return false;
 
-  m_pLoader->m_iTotalLines = -1;
+  m_pLoader->iTotalLines = -1;
   m_iLines = 0;
   float fLinePos = 0;
   CXFA_Node* pNode = nullptr;
-  CFX_SizeF szText(m_pLoader->m_fWidth, m_pLoader->m_fHeight);
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_Blocks);
-  int32_t iBlocksHeightCount =
-      pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight);
-  iBlocksHeightCount /= 2;
-  if (iBlock < iBlocksHeightCount)
+  CFX_SizeF szText(m_pLoader->fWidth, m_pLoader->fHeight);
+  if (szBlockIndex < m_pLoader->blockHeights.size())
     return true;
-  if (iBlock == iBlocksHeightCount) {
+  if (szBlockIndex == m_pLoader->blockHeights.size()) {
     Unload();
     m_pBreak = CreateBreak(true);
-    fLinePos = m_pLoader->m_fStartLineOffset;
-    for (int32_t i = 0; i < iBlocksHeightCount; i++)
-      fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1];
+    fLinePos = m_pLoader->fStartLineOffset;
+    for (size_t i = 0; i < m_pLoader->blockHeights.size(); ++i)
+      fLinePos -= m_pLoader->blockHeights[i].fHeight;
 
-    m_pLoader->m_iChar = 0;
-    if (iCount > 1)
-      m_pLoader->m_iTotalLines = m_Blocks[iBlock * 2 + 1];
+    m_pLoader->iChar = 0;
+    if (!m_Blocks.empty())
+      m_pLoader->iTotalLines = m_Blocks[szBlockIndex].szLength;
 
-    Loader(szText.width, fLinePos, true);
-    if (iCount == 0 && m_pLoader->m_fStartLineOffset < 0.1f)
+    Loader(szText.width, &fLinePos, true);
+    if (m_Blocks.empty() && m_pLoader->fStartLineOffset < 0.1f)
       UpdateAlign(szText.height, fLinePos);
   } else if (m_pTextDataNode) {
-    iBlock *= 2;
-    if (iBlock < iCount - 2)
-      m_pLoader->m_iTotalLines = m_Blocks[iBlock + 1];
+    if (!m_Blocks.empty() && szBlockIndex < m_Blocks.size() - 1)
+      m_pLoader->iTotalLines = m_Blocks[szBlockIndex].szLength;
 
     m_pBreak->Reset();
     if (m_bRichText) {
@@ -458,150 +484,130 @@
       if (!pContainerNode)
         return true;
 
-      CFX_XMLNode* pXMLNode = m_pLoader->m_pXMLNode;
+      const CFX_XMLNode* pXMLNode = m_pLoader->pXMLNode.Get();
       if (!pXMLNode)
         return true;
 
-      CFX_XMLNode* pSaveXMLNode = m_pLoader->m_pXMLNode;
-      for (; pXMLNode;
-           pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-        if (!LoadRichText(pXMLNode, szText.width, fLinePos,
-                          m_pLoader->m_pParentStyle, true, nullptr)) {
+      const CFX_XMLNode* pSaveXMLNode = pXMLNode;
+      for (; pXMLNode; pXMLNode = pXMLNode->GetNextSibling()) {
+        if (!LoadRichText(pXMLNode, szText.width, &fLinePos,
+                          m_pLoader->pParentStyle, true, nullptr, true, false,
+                          0)) {
           break;
         }
       }
       while (!pXMLNode) {
-        pXMLNode = pSaveXMLNode->GetNodeItem(CFX_XMLNode::Parent);
+        pXMLNode = pSaveXMLNode->GetParent();
         if (pXMLNode == pContainerNode)
           break;
-        if (!LoadRichText(pXMLNode, szText.width, fLinePos,
-                          m_pLoader->m_pParentStyle, true, nullptr, false)) {
+        if (!LoadRichText(pXMLNode, szText.width, &fLinePos,
+                          m_pLoader->pParentStyle, true, nullptr, false, false,
+                          0)) {
           break;
         }
         pSaveXMLNode = pXMLNode;
-        pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling);
+        pXMLNode = pXMLNode->GetNextSibling();
         if (!pXMLNode)
           continue;
-        for (; pXMLNode;
-             pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-          if (!LoadRichText(pXMLNode, szText.width, fLinePos,
-                            m_pLoader->m_pParentStyle, true, nullptr)) {
+        for (; pXMLNode; pXMLNode = pXMLNode->GetNextSibling()) {
+          if (!LoadRichText(pXMLNode, szText.width, &fLinePos,
+                            m_pLoader->pParentStyle, true, nullptr, true, false,
+                            0)) {
             break;
           }
         }
       }
     } else {
-      pNode = m_pLoader->m_pNode;
+      pNode = m_pLoader->pNode.Get();
       if (!pNode)
         return true;
-      LoadText(pNode, szText.width, fLinePos, true);
+      LoadText(pNode, szText.width, &fLinePos, true);
     }
   }
-  if (iBlock == iCount) {
+  if (szBlockIndex == m_Blocks.size()) {
     m_pTabstopContext.reset();
     m_pLoader.reset();
   }
   return true;
 }
 
-void CXFA_TextLayout::ItemBlocks(const CFX_RectF& rtText, int32_t iBlockIndex) {
+void CXFA_TextLayout::ItemBlocks(const CFX_RectF& rtText, size_t szBlockIndex) {
   if (!m_pLoader)
     return;
 
-  int32_t iCountHeight =
-      pdfium::CollectionSize<int32_t>(m_pLoader->m_lineHeights);
-  if (iCountHeight == 0)
+  if (m_pLoader->lineHeights.empty())
     return;
 
-  bool bEndItem = true;
-  int32_t iBlockCount = pdfium::CollectionSize<int32_t>(m_Blocks);
-  float fLinePos = m_pLoader->m_fStartLineOffset;
-  int32_t iLineIndex = 0;
-  if (iBlockIndex > 0) {
-    int32_t iBlockHeightCount =
-        pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight);
-    iBlockHeightCount /= 2;
-    if (iBlockHeightCount >= iBlockIndex) {
-      for (int32_t i = 0; i < iBlockIndex; i++)
-        fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1];
+  float fLinePos = m_pLoader->fStartLineOffset;
+  size_t szLineIndex = 0;
+  if (szBlockIndex > 0) {
+    if (szBlockIndex <= m_pLoader->blockHeights.size()) {
+      for (size_t i = 0; i < szBlockIndex; ++i)
+        fLinePos -= m_pLoader->blockHeights[i].fHeight;
     } else {
       fLinePos = 0;
     }
-    iLineIndex = m_Blocks[iBlockCount - 1] + m_Blocks[iBlockCount - 2];
+    szLineIndex = GetNextIndexFromLastBlockData();
   }
 
-  int32_t i = 0;
-  for (i = iLineIndex; i < iCountHeight; i++) {
-    float fLineHeight = m_pLoader->m_lineHeights[i];
-    if (fLinePos + fLineHeight - rtText.height > 0.001) {
-      m_Blocks.push_back(iLineIndex);
-      m_Blocks.push_back(i - iLineIndex);
-      bEndItem = false;
-      break;
+  size_t i;
+  for (i = szLineIndex; i < m_pLoader->lineHeights.size(); ++i) {
+    float fLineHeight = m_pLoader->lineHeights[i];
+    if (fLinePos + fLineHeight - rtText.height > kHeightTolerance) {
+      m_Blocks.push_back({szLineIndex, i - szLineIndex});
+      return;
     }
     fLinePos += fLineHeight;
   }
-  if (iCountHeight > 0 && (i - iLineIndex) > 0 && bEndItem) {
-    m_Blocks.push_back(iLineIndex);
-    m_Blocks.push_back(i - iLineIndex);
-  }
+  if (i > szLineIndex)
+    m_Blocks.push_back({szLineIndex, i - szLineIndex});
 }
 
 bool CXFA_TextLayout::DrawString(CFX_RenderDevice* pFxDevice,
-                                 const CFX_Matrix& tmDoc2Device,
+                                 const CFX_Matrix& mtDoc2Device,
                                  const CFX_RectF& rtClip,
-                                 int32_t iBlock) {
+                                 size_t szBlockIndex) {
   if (!pFxDevice)
     return false;
 
   pFxDevice->SaveState();
-  pFxDevice->SetClip_Rect(rtClip);
+  pFxDevice->SetClip_Rect(rtClip.GetOuterRect());
 
   if (m_pieceLines.empty()) {
-    int32_t iBlockCount = CountBlocks();
-    for (int32_t i = 0; i < iBlockCount; i++)
-      Layout(i);
+    size_t szBlockCount = CountBlocks();
+    for (size_t i = 0; i < szBlockCount; ++i)
+      LayoutInternal(i);
   }
 
-  FXTEXT_CHARPOS* pCharPos = nullptr;
-  int32_t iCharCount = 0;
-  int32_t iLineStart = 0;
-  int32_t iPieceLines = pdfium::CollectionSize<int32_t>(m_pieceLines);
-  int32_t iCount = pdfium::CollectionSize<int32_t>(m_Blocks);
-  if (iCount > 0) {
-    iBlock *= 2;
-    if (iBlock < iCount) {
-      iLineStart = m_Blocks[iBlock];
-      iPieceLines = m_Blocks[iBlock + 1];
+  std::vector<TextCharPos> char_pos(1);
+  size_t szLineStart = 0;
+  size_t szPieceLines = m_pieceLines.size();
+  if (!m_Blocks.empty()) {
+    if (szBlockIndex < m_Blocks.size()) {
+      szLineStart = m_Blocks[szBlockIndex].szIndex;
+      szPieceLines = m_Blocks[szBlockIndex].szLength;
     } else {
-      iPieceLines = 0;
+      szPieceLines = 0;
     }
   }
 
-  for (int32_t i = 0; i < iPieceLines; i++) {
-    if (i + iLineStart >= pdfium::CollectionSize<int32_t>(m_pieceLines))
+  for (size_t i = 0; i < szPieceLines; ++i) {
+    if (i + szLineStart >= m_pieceLines.size())
       break;
 
-    CXFA_PieceLine* pPieceLine = m_pieceLines[i + iLineStart].get();
-    int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces);
-    int32_t j = 0;
-    for (j = 0; j < iPieces; j++) {
+    CXFA_PieceLine* pPieceLine = m_pieceLines[i + szLineStart].get();
+    for (size_t j = 0; j < pPieceLine->m_textPieces.size(); ++j) {
       const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[j].get();
       int32_t iChars = pPiece->iChars;
-      if (iCharCount < iChars) {
-        FX_Free(pCharPos);
-        pCharPos = FX_Alloc(FXTEXT_CHARPOS, iChars);
-        iCharCount = iChars;
-      }
-      memset(pCharPos, 0, iCharCount * sizeof(FXTEXT_CHARPOS));
-      RenderString(pFxDevice, pPieceLine, j, pCharPos, tmDoc2Device);
+      if (pdfium::CollectionSize<int32_t>(char_pos) < iChars)
+        char_pos.resize(iChars);
+      RenderString(pFxDevice, pPieceLine, j, &char_pos, mtDoc2Device);
     }
-    for (j = 0; j < iPieces; j++)
-      RenderPath(pFxDevice, pPieceLine, j, pCharPos, tmDoc2Device);
+    for (size_t j = 0; j < pPieceLine->m_textPieces.size(); ++j)
+      RenderPath(pFxDevice, pPieceLine, j, &char_pos, mtDoc2Device);
   }
   pFxDevice->RestoreState(false);
-  FX_Free(pCharPos);
-  return iPieceLines > 0;
+  return szPieceLines > 0;
 }
 
 void CXFA_TextLayout::UpdateAlign(float fHeight, float fBottom) {
@@ -610,10 +616,10 @@
     return;
 
   switch (m_textParser.GetVAlign(m_pTextProvider)) {
-    case XFA_AttributeEnum::Middle:
+    case XFA_AttributeValue::Middle:
       fHeight /= 2.0f;
       break;
-    case XFA_AttributeEnum::Bottom:
+    case XFA_AttributeValue::Bottom:
       break;
     default:
       return;
@@ -625,32 +631,33 @@
   }
 }
 
-bool CXFA_TextLayout::Loader(float textWidth,
-                             float& fLinePos,
+void CXFA_TextLayout::Loader(float textWidth,
+                             float* pLinePos,
                              bool bSavePieces) {
   GetTextDataNode();
   if (!m_pTextDataNode)
-    return true;
+    return;
 
-  if (m_bRichText) {
-    CFX_XMLNode* pXMLContainer = GetXMLContainerNode();
-    if (pXMLContainer) {
-      if (!m_textParser.IsParsed())
-        m_textParser.DoParse(pXMLContainer, m_pTextProvider);
-
-      auto pRootStyle = m_textParser.CreateRootStyle(m_pTextProvider);
-      LoadRichText(pXMLContainer, textWidth, fLinePos, pRootStyle, bSavePieces,
-                   nullptr);
-    }
-  } else {
-    LoadText(m_pTextDataNode, textWidth, fLinePos, bSavePieces);
+  if (!m_bRichText) {
+    LoadText(m_pTextDataNode, textWidth, pLinePos, bSavePieces);
+    return;
   }
-  return true;
+
+  const CFX_XMLNode* pXMLContainer = GetXMLContainerNode();
+  if (!pXMLContainer)
+    return;
+
+  if (!m_textParser.IsParsed())
+    m_textParser.DoParse(pXMLContainer, m_pTextProvider);
+
+  auto pRootStyle = m_textParser.CreateRootStyle(m_pTextProvider);
+  LoadRichText(pXMLContainer, textWidth, pLinePos, pRootStyle, bSavePieces,
+               nullptr, true, false, 0);
 }
 
 void CXFA_TextLayout::LoadText(CXFA_Node* pNode,
                                float textWidth,
-                               float& fLinePos,
+                               float* pLinePos,
                                bool bSavePieces) {
   InitBreak(textWidth);
 
@@ -662,10 +669,10 @@
       fSpaceAbove = 0;
 
     switch (para->GetVerticalAlign()) {
-      case XFA_AttributeEnum::Top:
-      case XFA_AttributeEnum::Middle:
-      case XFA_AttributeEnum::Bottom: {
-        fLinePos += fSpaceAbove;
+      case XFA_AttributeValue::Top:
+      case XFA_AttributeValue::Middle:
+      case XFA_AttributeValue::Bottom: {
+        *pLinePos += fSpaceAbove;
         break;
       }
       default:
@@ -676,20 +683,20 @@
 
   WideString wsText = pNode->JSObject()->GetContent(false);
   wsText.TrimRight(L" ");
-  bool bRet = AppendChar(wsText, fLinePos, fSpaceAbove, bSavePieces);
+  bool bRet = AppendChar(wsText, pLinePos, fSpaceAbove, bSavePieces);
   if (bRet && m_pLoader)
-    m_pLoader->m_pNode = pNode;
+    m_pLoader->pNode = pNode;
   else
-    EndBreak(CFX_BreakType::Paragraph, fLinePos, bSavePieces);
+    EndBreak(CFX_BreakType::Paragraph, pLinePos, bSavePieces);
 }
 
 bool CXFA_TextLayout::LoadRichText(
-    CFX_XMLNode* pXMLNode,
+    const CFX_XMLNode* pXMLNode,
     float textWidth,
-    float& fLinePos,
+    float* pLinePos,
     const RetainPtr<CFX_CSSComputedStyle>& pParentStyle,
     bool bSavePieces,
-    RetainPtr<CXFA_LinkUserData> pLinkData,
+    RetainPtr<CFX_LinkUserData> pLinkData,
     bool bEndBreak,
     bool bIsOl,
     int32_t iLiCount) {
@@ -706,23 +713,22 @@
   if (bEndBreak) {
     bool bCurOl = false;
     bool bCurLi = false;
-    CFX_XMLElement* pElement = nullptr;
+    const CFX_XMLElement* pElement = nullptr;
     if (pContext) {
-      if (m_bBlockContinue ||
-          (m_pLoader && pXMLNode == m_pLoader->m_pXMLNode)) {
+      if (m_bBlockContinue || (m_pLoader && pXMLNode == m_pLoader->pXMLNode)) {
         m_bBlockContinue = true;
       }
-      if (pXMLNode->GetType() == FX_XMLNODE_Text) {
+      if (pXMLNode->GetType() == CFX_XMLNode::Type::kText) {
         bContentNode = true;
-      } else if (pXMLNode->GetType() == FX_XMLNODE_Element) {
-        pElement = static_cast<CFX_XMLElement*>(pXMLNode);
+      } else if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement) {
+        pElement = static_cast<const CFX_XMLElement*>(pXMLNode);
         wsName = pElement->GetLocalTagName();
       }
-      if (wsName == L"ol") {
+      if (wsName.EqualsASCII("ol")) {
         bIsOl = true;
         bCurOl = true;
       }
-      if (m_bBlockContinue || bContentNode == false) {
+      if (m_bBlockContinue || !bContentNode) {
         eDisplay = pContext->GetDisplay();
         if (eDisplay != CFX_CSSDisplay::Block &&
             eDisplay != CFX_CSSDisplay::Inline &&
@@ -736,23 +742,20 @@
         if ((eDisplay == CFX_CSSDisplay::Block ||
              eDisplay == CFX_CSSDisplay::ListItem) &&
             pStyle &&
-            (wsName.IsEmpty() || (wsName != L"body" && wsName != L"html" &&
-                                  wsName != L"ol" && wsName != L"ul"))) {
+            (wsName.IsEmpty() ||
+             !(wsName.EqualsASCII("body") || wsName.EqualsASCII("html") ||
+               wsName.EqualsASCII("ol") || wsName.EqualsASCII("ul")))) {
           const CFX_CSSRect* pRect = pStyle->GetMarginWidth();
           if (pRect) {
-            fLinePos += pRect->top.GetValue();
+            *pLinePos += pRect->top.GetValue();
             fSpaceBelow = pRect->bottom.GetValue();
           }
         }
 
-        if (wsName == L"a") {
-          ASSERT(pElement);
-          WideString wsLinkContent = pElement->GetString(L"href");
-          if (!wsLinkContent.IsEmpty()) {
-            pLinkData = pdfium::MakeRetain<CXFA_LinkUserData>(
-                wsLinkContent.GetBuffer(wsLinkContent.GetLength()));
-            wsLinkContent.ReleaseBuffer(wsLinkContent.GetLength());
-          }
+        if (wsName.EqualsASCII("a")) {
+          WideString wsLinkContent = pElement->GetAttribute(L"href");
+          if (!wsLinkContent.IsEmpty())
+            pLinkData = pdfium::MakeRetain<CFX_LinkUserData>(wsLinkContent);
         }
 
         int32_t iTabCount = m_textParser.CountTabs(
@@ -761,10 +764,10 @@
             bContentNode ? pParentStyle.Get() : pStyle.Get());
         WideString wsText;
         if (bContentNode && iTabCount == 0) {
-          wsText = static_cast<CFX_XMLText*>(pXMLNode)->GetText();
-        } else if (wsName == L"br") {
+          wsText = ToXMLText(pXMLNode)->GetText();
+        } else if (wsName.EqualsASCII("br")) {
           wsText = L'\n';
-        } else if (wsName == L"li") {
+        } else if (wsName.EqualsASCII("li")) {
           bCurLi = true;
           if (bIsOl)
             wsText = WideString::Format(L"%d.  ", iLiCount);
@@ -775,63 +778,61 @@
             while (iTabCount-- > 0)
               wsText += L'\t';
           } else {
-            m_textParser.GetEmbbedObj(m_pTextProvider, pXMLNode, wsText);
+            Optional<WideString> obj =
+                m_textParser.GetEmbeddedObj(m_pTextProvider, pXMLNode);
+            if (obj)
+              wsText = *obj;
           }
         }
 
         int32_t iLength = wsText.GetLength();
         if (iLength > 0 && bContentNode && !bSpaceRun)
-          ProcessText(wsText);
+          ProcessText(&wsText);
 
         if (m_pLoader) {
-          if (wsText.GetLength() > 0 &&
-              (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) {
+          if (wsText.GetLength() > 0 && m_pLoader->bFilterSpace) {
             wsText.TrimLeft(L" ");
           }
           if (CFX_CSSDisplay::Block == eDisplay) {
-            m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
+            m_pLoader->bFilterSpace = true;
           } else if (CFX_CSSDisplay::Inline == eDisplay &&
-                     (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) {
-            m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
-          } else if (wsText.GetLength() > 0 &&
-                     (0x20 == wsText[wsText.GetLength() - 1])) {
-            m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
+                     m_pLoader->bFilterSpace) {
+            m_pLoader->bFilterSpace = false;
+          } else if (wsText.GetLength() > 0 && wsText.Back() == 0x20) {
+            m_pLoader->bFilterSpace = true;
           } else if (wsText.GetLength() != 0) {
-            m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
+            m_pLoader->bFilterSpace = false;
           }
         }
 
         if (wsText.GetLength() > 0) {
-          if (!m_pLoader || m_pLoader->m_iChar == 0) {
-            auto pUserData = pdfium::MakeRetain<CXFA_TextUserData>(
+          if (!m_pLoader || m_pLoader->iChar == 0) {
+            auto pUserData = pdfium::MakeRetain<CFX_TextUserData>(
                 bContentNode ? pParentStyle : pStyle, pLinkData);
             m_pBreak->SetUserData(pUserData);
           }
 
-          if (AppendChar(wsText, fLinePos, 0, bSavePieces)) {
+          if (AppendChar(wsText, pLinePos, 0, bSavePieces)) {
             if (m_pLoader)
-              m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
-            if (IsEnd(bSavePieces)) {
-              if (m_pLoader && m_pLoader->m_iTotalLines > -1) {
-                m_pLoader->m_pXMLNode = pXMLNode;
-                m_pLoader->m_pParentStyle = pParentStyle;
-              }
-              return false;
+              m_pLoader->bFilterSpace = false;
+            if (!IsEnd(bSavePieces))
+              return true;
+            if (m_pLoader && m_pLoader->iTotalLines > -1) {
+              m_pLoader->pXMLNode = pXMLNode;
+              m_pLoader->pParentStyle = pParentStyle;
             }
-            return true;
+            return false;
           }
         }
       }
     }
 
-    for (CFX_XMLNode* pChildNode =
-             pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-         pChildNode;
-         pChildNode = pChildNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
+    for (CFX_XMLNode* pChildNode = pXMLNode->GetFirstChild(); pChildNode;
+         pChildNode = pChildNode->GetNextSibling()) {
       if (bCurOl)
         iLiCount++;
 
-      if (!LoadRichText(pChildNode, textWidth, fLinePos,
+      if (!LoadRichText(pChildNode, textWidth, pLinePos,
                         pContext ? pStyle : pParentStyle, bSavePieces,
                         pLinkData, true, bIsOl, iLiCount))
         return false;
@@ -839,10 +840,10 @@
 
     if (m_pLoader) {
       if (CFX_CSSDisplay::Block == eDisplay)
-        m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
+        m_pLoader->bFilterSpace = true;
     }
     if (bCurLi)
-      EndBreak(CFX_BreakType::Line, fLinePos, bSavePieces);
+      EndBreak(CFX_BreakType::Line, pLinePos, bSavePieces);
   } else {
     if (pContext)
       eDisplay = pContext->GetDisplay();
@@ -853,17 +854,16 @@
       CFX_BreakType dwStatus = (eDisplay == CFX_CSSDisplay::Block)
                                    ? CFX_BreakType::Paragraph
                                    : CFX_BreakType::Piece;
-      EndBreak(dwStatus, fLinePos, bSavePieces);
+      EndBreak(dwStatus, pLinePos, bSavePieces);
       if (eDisplay == CFX_CSSDisplay::Block) {
-        fLinePos += fSpaceBelow;
+        *pLinePos += fSpaceBelow;
         if (m_pTabstopContext)
           m_pTabstopContext->RemoveAll();
       }
       if (IsEnd(bSavePieces)) {
-        if (m_pLoader && m_pLoader->m_iTotalLines > -1) {
-          m_pLoader->m_pXMLNode =
-              pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling);
-          m_pLoader->m_pParentStyle = pParentStyle;
+        if (m_pLoader && m_pLoader->iTotalLines > -1) {
+          m_pLoader->pXMLNode = pXMLNode->GetNextSibling();
+          m_pLoader->pParentStyle = pParentStyle;
         }
         return false;
       }
@@ -873,13 +873,13 @@
 }
 
 bool CXFA_TextLayout::AppendChar(const WideString& wsText,
-                                 float& fLinePos,
+                                 float* pLinePos,
                                  float fSpaceAbove,
                                  bool bSavePieces) {
   CFX_BreakType dwStatus = CFX_BreakType::None;
   int32_t iChar = 0;
   if (m_pLoader)
-    iChar = m_pLoader->m_iChar;
+    iChar = m_pLoader->iChar;
 
   int32_t iLength = wsText.GetLength();
   for (int32_t i = iChar; i < iLength; i++) {
@@ -889,18 +889,18 @@
 
     dwStatus = m_pBreak->AppendChar(wch);
     if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece) {
-      AppendTextLine(dwStatus, fLinePos, bSavePieces);
+      AppendTextLine(dwStatus, pLinePos, bSavePieces, false);
       if (IsEnd(bSavePieces)) {
         if (m_pLoader)
-          m_pLoader->m_iChar = i;
+          m_pLoader->iChar = i;
         return true;
       }
       if (dwStatus == CFX_BreakType::Paragraph && m_bRichText)
-        fLinePos += fSpaceAbove;
+        *pLinePos += fSpaceAbove;
     }
   }
   if (m_pLoader)
-    m_pLoader->m_iChar = 0;
+    m_pLoader->iChar = 0;
 
   return false;
 }
@@ -908,39 +908,17 @@
 bool CXFA_TextLayout::IsEnd(bool bSavePieces) {
   if (!bSavePieces)
     return false;
-  if (m_pLoader && m_pLoader->m_iTotalLines > 0)
-    return m_iLines >= m_pLoader->m_iTotalLines;
+  if (m_pLoader && m_pLoader->iTotalLines > 0)
+    return m_iLines >= m_pLoader->iTotalLines;
   return false;
 }
 
-void CXFA_TextLayout::ProcessText(WideString& wsText) {
-  int32_t iLen = wsText.GetLength();
-  if (iLen == 0)
-    return;
-
-  wchar_t* psz = wsText.GetBuffer(iLen);
-  int32_t iTrimLeft = 0;
-  wchar_t wch = 0, wPrev = 0;
-  for (int32_t i = 0; i < iLen; i++) {
-    wch = psz[i];
-    if (wch < 0x20)
-      wch = 0x20;
-    if (wch == 0x20 && wPrev == 0x20)
-      continue;
-
-    wPrev = wch;
-    psz[iTrimLeft++] = wch;
-  }
-  wsText.ReleaseBuffer(iLen);
-  wsText = wsText.Left(iTrimLeft);
-}
-
 void CXFA_TextLayout::EndBreak(CFX_BreakType dwStatus,
-                               float& fLinePos,
+                               float* pLinePos,
                                bool bSavePieces) {
   dwStatus = m_pBreak->EndBreak(dwStatus);
   if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece)
-    AppendTextLine(dwStatus, fLinePos, bSavePieces, true);
+    AppendTextLine(dwStatus, pLinePos, bSavePieces, true);
 }
 
 void CXFA_TextLayout::DoTabstops(CFX_CSSComputedStyle* pStyle,
@@ -999,7 +977,7 @@
 }
 
 void CXFA_TextLayout::AppendTextLine(CFX_BreakType dwStatus,
-                                     float& fLinePos,
+                                     float* pLinePos,
                                      bool bSavePieces,
                                      bool bEndBreak) {
   int32_t iPieces = m_pBreak->CountBreakPieces();
@@ -1018,7 +996,7 @@
     int32_t i = 0;
     for (i = 0; i < iPieces; i++) {
       const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
-      CXFA_TextUserData* pUserData = pPiece->m_pUserData.Get();
+      CFX_TextUserData* pUserData = pPiece->m_pUserData.Get();
       if (pUserData)
         pStyle = pUserData->m_pStyle;
       float fVerScale = pPiece->m_iVerticalScale / 100.0f;
@@ -1035,11 +1013,13 @@
       m_textParser.GetLinethrough(m_pTextProvider, pStyle.Get(),
                                   pTP->iLineThrough);
       pTP->dwColor = m_textParser.GetColor(m_pTextProvider, pStyle.Get());
-      pTP->pFont = m_textParser.GetFont(m_pDoc, m_pTextProvider, pStyle.Get());
+      pTP->pFont =
+          m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, pStyle.Get());
       pTP->fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle.Get());
       pTP->rtPiece.left = pPiece->m_iStartPos / 20000.0f;
       pTP->rtPiece.width = pPiece->m_iWidth / 20000.0f;
-      pTP->rtPiece.height = (float)pPiece->m_iFontSize * fVerScale / 20.0f;
+      pTP->rtPiece.height =
+          static_cast<float>(pPiece->m_iFontSize) * fVerScale / 20.0f;
       float fBaseLineTemp =
           m_textParser.GetBaseline(m_pTextProvider, pStyle.Get());
       pTP->rtPiece.top = fBaseLineTemp;
@@ -1050,10 +1030,6 @@
         float fLineHeightTmp = fBaseLineTemp + pTP->rtPiece.height;
         if (fLineHeight < fLineHeightTmp)
           fLineHeight = fLineHeightTmp;
-        else
-          fBaseLineTemp = 0;
-      } else if (fBaseLine < -fBaseLineTemp) {
-        fBaseLine = -fBaseLineTemp;
       }
       fLineStep = std::max(fLineStep, fLineHeight);
       pTP->pLinkData = pUserData ? pUserData->m_pLinkData : nullptr;
@@ -1063,16 +1039,16 @@
     for (const auto& pTP : pPieceLine->m_textPieces) {
       float& fTop = pTP->rtPiece.top;
       float fBaseLineTemp = fTop;
-      fTop = fLinePos + fLineStep - pTP->rtPiece.height - fBaseLineTemp;
+      fTop = *pLinePos + fLineStep - pTP->rtPiece.height - fBaseLineTemp;
       fTop = std::max(0.0f, fTop);
     }
-    fLinePos += fLineStep + fBaseLine;
+    *pLinePos += fLineStep + fBaseLine;
   } else {
     float fLineStep = 0;
     float fLineWidth = 0;
     for (int32_t i = 0; i < iPieces; i++) {
       const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
-      CXFA_TextUserData* pUserData = pPiece->m_pUserData.Get();
+      CFX_TextUserData* pUserData = pPiece->m_pUserData.Get();
       if (pUserData)
         pStyle = pUserData->m_pStyle;
       float fVerScale = pPiece->m_iVerticalScale / 100.0f;
@@ -1081,7 +1057,8 @@
           m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale);
       if (fBaseLine > 0) {
         float fLineHeightTmp =
-            fBaseLine + (float)pPiece->m_iFontSize * fVerScale / 20.0f;
+            fBaseLine +
+            static_cast<float>(pPiece->m_iFontSize) * fVerScale / 20.0f;
         if (fLineHeight < fLineHeightTmp) {
           fLineHeight = fLineHeightTmp;
         }
@@ -1089,12 +1066,12 @@
       fLineStep = std::max(fLineStep, fLineHeight);
       fLineWidth += pPiece->m_iWidth / 20000.0f;
     }
-    fLinePos += fLineStep;
+    *pLinePos += fLineStep;
     m_fMaxWidth = std::max(m_fMaxWidth, fLineWidth);
-    if (m_pLoader && m_pLoader->m_bSaveLineHeight) {
-      float fHeight = fLinePos - m_pLoader->m_fLastPos;
-      m_pLoader->m_fLastPos = fLinePos;
-      m_pLoader->m_lineHeights.push_back(fHeight);
+    if (m_pLoader && m_pLoader->bSaveLineHeight) {
+      float fHeight = *pLinePos - m_pLoader->fLastPos;
+      m_pLoader->fLastPos = *pLinePos;
+      m_pLoader->lineHeights.push_back(fHeight);
     }
   }
 
@@ -1114,7 +1091,7 @@
           fSpaceBelow = 0;
 
         m_pBreak->SetLineStartPos(fStartPos);
-        fLinePos += fSpaceBelow;
+        *pLinePos += fSpaceBelow;
       }
     }
   }
@@ -1136,60 +1113,63 @@
 
 void CXFA_TextLayout::RenderString(CFX_RenderDevice* pDevice,
                                    CXFA_PieceLine* pPieceLine,
-                                   int32_t iPiece,
-                                   FXTEXT_CHARPOS* pCharPos,
-                                   const CFX_Matrix& tmDoc2Device) {
-  const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPiece].get();
-  int32_t iCount = GetDisplayPos(pPiece, pCharPos);
-  if (iCount > 0) {
-    CFDE_TextOut::DrawString(pDevice, pPiece->dwColor, pPiece->pFont, pCharPos,
-                             iCount, pPiece->fFontSize, &tmDoc2Device);
+                                   size_t szPiece,
+                                   std::vector<TextCharPos>* pCharPos,
+                                   const CFX_Matrix& mtDoc2Device) {
+  const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
+  size_t szCount = GetDisplayPos(pPiece, pCharPos);
+  if (szCount > 0) {
+    auto span = pdfium::make_span(pCharPos->data(), szCount);
+    CFDE_TextOut::DrawString(pDevice, pPiece->dwColor, pPiece->pFont, span,
+                             pPiece->fFontSize, mtDoc2Device);
   }
-  pPieceLine->m_charCounts.push_back(iCount);
+  pPieceLine->m_charCounts.push_back(szCount);
 }
 
 void CXFA_TextLayout::RenderPath(CFX_RenderDevice* pDevice,
                                  CXFA_PieceLine* pPieceLine,
-                                 int32_t iPiece,
-                                 FXTEXT_CHARPOS* pCharPos,
-                                 const CFX_Matrix& tmDoc2Device) {
-  CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPiece].get();
+                                 size_t szPiece,
+                                 std::vector<TextCharPos>* pCharPos,
+                                 const CFX_Matrix& mtDoc2Device) {
+  CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
   bool bNoUnderline = pPiece->iUnderline < 1 || pPiece->iUnderline > 2;
   bool bNoLineThrough = pPiece->iLineThrough < 1 || pPiece->iLineThrough > 2;
   if (bNoUnderline && bNoLineThrough)
     return;
 
   CFX_PathData path;
-  int32_t iChars = GetDisplayPos(pPiece, pCharPos);
-  if (iChars > 0) {
-    CFX_PointF pt1, pt2;
-    float fEndY = pCharPos[0].m_Origin.y + 1.05f;
-    if (pPiece->iPeriod == XFA_AttributeEnum::Word) {
+  size_t szChars = GetDisplayPos(pPiece, pCharPos);
+  if (szChars > 0) {
+    CFX_PointF pt1;
+    CFX_PointF pt2;
+    float fEndY = (*pCharPos)[0].m_Origin.y + 1.05f;
+    if (pPiece->iPeriod == XFA_AttributeValue::Word) {
       for (int32_t i = 0; i < pPiece->iUnderline; i++) {
-        for (int32_t j = 0; j < iChars; j++) {
-          pt1.x = pCharPos[j].m_Origin.x;
-          pt2.x =
-              pt1.x + pCharPos[j].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
+        for (size_t j = 0; j < szChars; j++) {
+          pt1.x = (*pCharPos)[j].m_Origin.x;
+          pt2.x = pt1.x +
+                  (*pCharPos)[j].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
           pt1.y = pt2.y = fEndY;
           path.AppendLine(pt1, pt2);
         }
         fEndY += 2.0f;
       }
     } else {
-      pt1.x = pCharPos[0].m_Origin.x;
-      pt2.x =
-          pCharPos[iChars - 1].m_Origin.x +
-          pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
+      pt1.x = (*pCharPos)[0].m_Origin.x;
+      pt2.x = (*pCharPos)[szChars - 1].m_Origin.x +
+              (*pCharPos)[szChars - 1].m_FontCharWidth * pPiece->fFontSize /
+                  1000.0f;
       for (int32_t i = 0; i < pPiece->iUnderline; i++) {
         pt1.y = pt2.y = fEndY;
         path.AppendLine(pt1, pt2);
         fEndY += 2.0f;
       }
     }
-    fEndY = pCharPos[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
-    pt1.x = pCharPos[0].m_Origin.x;
-    pt2.x = pCharPos[iChars - 1].m_Origin.x +
-            pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
+    fEndY = (*pCharPos)[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
+    pt1.x = (*pCharPos)[0].m_Origin.x;
+    pt2.x =
+        (*pCharPos)[szChars - 1].m_Origin.x +
+        (*pCharPos)[szChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
     for (int32_t i = 0; i < pPiece->iLineThrough; i++) {
       pt1.y = pt2.y = fEndY;
       path.AppendLine(pt1, pt2);
@@ -1197,59 +1177,61 @@
     }
   } else {
     if (bNoLineThrough &&
-        (bNoUnderline || pPiece->iPeriod != XFA_AttributeEnum::All)) {
+        (bNoUnderline || pPiece->iPeriod != XFA_AttributeValue::All)) {
       return;
     }
-    int32_t iCharsTmp = 0;
-    int32_t iPiecePrev = iPiece;
-    int32_t iPieceNext = iPiece;
-    while (iPiecePrev > 0) {
-      iPiecePrev--;
-      iCharsTmp = pPieceLine->m_charCounts[iPiecePrev];
-      if (iCharsTmp > 0)
+    bool bHasCount = false;
+    size_t szPiecePrev = szPiece;
+    size_t szPieceNext = szPiece;
+    while (szPiecePrev > 0) {
+      szPiecePrev--;
+      if (pPieceLine->m_charCounts[szPiecePrev] > 0) {
+        bHasCount = true;
         break;
+      }
     }
-    if (iCharsTmp == 0)
+    if (!bHasCount)
       return;
 
-    iCharsTmp = 0;
-    int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces);
-    while (iPieceNext < iPieces - 1) {
-      iPieceNext++;
-      iCharsTmp = pPieceLine->m_charCounts[iPieceNext];
-      if (iCharsTmp > 0)
+    bHasCount = false;
+    while (szPieceNext < pPieceLine->m_textPieces.size() - 1) {
+      szPieceNext++;
+      if (pPieceLine->m_charCounts[szPieceNext] > 0) {
+        bHasCount = true;
         break;
+      }
     }
-    if (iCharsTmp == 0)
+    if (!bHasCount)
       return;
 
     float fOrgX = 0.0f;
     float fEndX = 0.0f;
-    pPiece = pPieceLine->m_textPieces[iPiecePrev].get();
-    iChars = GetDisplayPos(pPiece, pCharPos);
-    if (iChars < 1)
+    pPiece = pPieceLine->m_textPieces[szPiecePrev].get();
+    szChars = GetDisplayPos(pPiece, pCharPos);
+    if (szChars < 1)
       return;
 
-    fOrgX = pCharPos[iChars - 1].m_Origin.x +
-            pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
-    pPiece = pPieceLine->m_textPieces[iPieceNext].get();
-    iChars = GetDisplayPos(pPiece, pCharPos);
-    if (iChars < 1)
+    fOrgX =
+        (*pCharPos)[szChars - 1].m_Origin.x +
+        (*pCharPos)[szChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
+    pPiece = pPieceLine->m_textPieces[szPieceNext].get();
+    szChars = GetDisplayPos(pPiece, pCharPos);
+    if (szChars < 1)
       return;
 
-    fEndX = pCharPos[0].m_Origin.x;
+    fEndX = (*pCharPos)[0].m_Origin.x;
     CFX_PointF pt1;
     CFX_PointF pt2;
     pt1.x = fOrgX;
     pt2.x = fEndX;
-    float fEndY = pCharPos[0].m_Origin.y + 1.05f;
+    float fEndY = (*pCharPos)[0].m_Origin.y + 1.05f;
     for (int32_t i = 0; i < pPiece->iUnderline; i++) {
       pt1.y = fEndY;
       pt2.y = fEndY;
       path.AppendLine(pt1, pt2);
       fEndY += 2.0f;
     }
-    fEndY = pCharPos[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
+    fEndY = (*pCharPos)[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
     for (int32_t i = 0; i < pPiece->iLineThrough; i++) {
       pt1.y = fEndY;
       pt2.y = fEndY;
@@ -1264,35 +1246,12 @@
   graphState.m_LineWidth = 1;
   graphState.m_MiterLimit = 10;
   graphState.m_DashPhase = 0;
-  pDevice->DrawPath(&path, &tmDoc2Device, &graphState, 0, pPiece->dwColor, 0);
+  pDevice->DrawPath(&path, &mtDoc2Device, &graphState, 0, pPiece->dwColor, 0);
 }
 
-int32_t CXFA_TextLayout::GetDisplayPos(const CXFA_TextPiece* pPiece,
-                                       FXTEXT_CHARPOS* pCharPos,
-                                       bool bCharCode) {
-  if (!pPiece)
+size_t CXFA_TextLayout::GetDisplayPos(const CXFA_TextPiece* pPiece,
+                                      std::vector<TextCharPos>* pCharPos) {
+  if (!pPiece || pPiece->iChars < 1)
     return 0;
-
-  FX_RTFTEXTOBJ tr;
-  if (!ToRun(pPiece, &tr))
-    return 0;
-  return m_pBreak->GetDisplayPos(&tr, pCharPos, bCharCode);
-}
-
-bool CXFA_TextLayout::ToRun(const CXFA_TextPiece* pPiece, FX_RTFTEXTOBJ* tr) {
-  int32_t iLength = pPiece->iChars;
-  if (iLength < 1)
-    return false;
-
-  tr->pStr = pPiece->szText;
-  tr->pFont = pPiece->pFont;
-  tr->pRect = &pPiece->rtPiece;
-  tr->pWidths = pPiece->Widths;
-  tr->iLength = iLength;
-  tr->fFontSize = pPiece->fFontSize;
-  tr->iBidiLevel = pPiece->iBidiLevel;
-  tr->wLineBreakChar = L'\n';
-  tr->iVerticalScale = pPiece->iVerScale;
-  tr->iHorizontalScale = pPiece->iHorScale;
-  return true;
+  return m_pBreak->GetDisplayPos(pPiece, pCharPos);
 }
diff --git a/xfa/fxfa/cxfa_textlayout.h b/xfa/fxfa/cxfa_textlayout.h
index 40b64a7..e47475a 100644
--- a/xfa/fxfa/cxfa_textlayout.h
+++ b/xfa/fxfa/cxfa_textlayout.h
@@ -13,51 +13,60 @@
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
-#include "xfa/fgas/layout/cfx_rtfbreak.h"
+#include "xfa/fgas/layout/cfx_char.h"
 #include "xfa/fxfa/cxfa_textparser.h"
 
 class CFDE_RenderDevice;
 class CFX_CSSComputedStyle;
-class CFX_RenderDevice;
 class CFX_RTFBreak;
+class CFX_RenderDevice;
 class CFX_XMLNode;
-class CXFA_LinkUserData;
-class CXFA_LoaderContext;
+class CFX_LinkUserData;
 class CXFA_Node;
 class CXFA_PieceLine;
 class CXFA_TextPiece;
 class CXFA_TextProvider;
 class CXFA_TextTabstopsContext;
+class TextCharPos;
+struct CXFA_LoaderContext;
+struct FX_RTFTEXTOBJ;
 
 class CXFA_TextLayout {
  public:
-  explicit CXFA_TextLayout(CXFA_FFDoc* doc, CXFA_TextProvider* pTextProvider);
+  CXFA_TextLayout(CXFA_FFDoc* doc, CXFA_TextProvider* pTextProvider);
   ~CXFA_TextLayout();
 
   float GetLayoutHeight();
   float StartLayout(float fWidth);
-  float DoLayout(int32_t iBlockIndex,
-                 float fCalcHeight,
-                 float fContentAreaHeight,
-                 float fTextHeight);
+  float DoLayout(float fTextHeight);
+  float DoSplitLayout(size_t szBlockIndex,
+                      float fCalcHeight,
+                      float fTextHeight);
   float Layout(const CFX_SizeF& size);
 
   CFX_SizeF CalcSize(const CFX_SizeF& minSize, const CFX_SizeF& maxSize);
-  void ItemBlocks(const CFX_RectF& rtText, int32_t iBlockIndex);
+  void ItemBlocks(const CFX_RectF& rtText, size_t szBlockIndex);
   bool DrawString(CFX_RenderDevice* pFxDevice,
-                  const CFX_Matrix& tmDoc2Device,
+                  const CFX_Matrix& mtDoc2Device,
                   const CFX_RectF& rtClip,
-                  int32_t iBlock);
+                  size_t szBlockIndex);
   bool IsLoaded() const { return !m_pieceLines.empty(); }
   void Unload();
+
   const std::vector<std::unique_ptr<CXFA_PieceLine>>* GetPieceLines() const {
     return &m_pieceLines;
   }
 
-  bool m_bHasBlock;
-  std::vector<int32_t> m_Blocks;
+  bool HasBlock() const { return m_bHasBlock; }
+  void ClearBlocks() { m_Blocks.clear(); }
+  void ResetHasBlock() { m_bHasBlock = false; }
 
  private:
+  struct BlockData {
+    size_t szIndex;
+    size_t szLength;
+  };
+
   void GetTextDataNode();
   CFX_XMLNode* GetXMLContainerNode();
   std::unique_ptr<CFX_RTFBreak> CreateBreak(bool bDefault);
@@ -65,64 +74,65 @@
   void InitBreak(CFX_CSSComputedStyle* pStyle,
                  CFX_CSSDisplay eDisplay,
                  float fLineWidth,
-                 CFX_XMLNode* pXMLNode,
+                 const CFX_XMLNode* pXMLNode,
                  CFX_CSSComputedStyle* pParentStyle);
-  bool Loader(float textWidth, float& fLinePos, bool bSavePieces);
+  void Loader(float textWidth, float* pLinePos, bool bSavePieces);
   void LoadText(CXFA_Node* pNode,
                 float textWidth,
-                float& fLinePos,
+                float* pLinePos,
                 bool bSavePieces);
-  bool LoadRichText(CFX_XMLNode* pXMLNode,
+  bool LoadRichText(const CFX_XMLNode* pXMLNode,
                     float textWidth,
-                    float& fLinePos,
+                    float* pLinePos,
                     const RetainPtr<CFX_CSSComputedStyle>& pParentStyle,
                     bool bSavePieces,
-                    RetainPtr<CXFA_LinkUserData> pLinkData,
-                    bool bEndBreak = true,
-                    bool bIsOl = false,
-                    int32_t iLiCount = 0);
+                    RetainPtr<CFX_LinkUserData> pLinkData,
+                    bool bEndBreak,
+                    bool bIsOl,
+                    int32_t iLiCount);
   bool AppendChar(const WideString& wsText,
-                  float& fLinePos,
+                  float* pLinePos,
                   float fSpaceAbove,
                   bool bSavePieces);
   void AppendTextLine(CFX_BreakType dwStatus,
-                      float& fLinePos,
+                      float* pLinePos,
                       bool bSavePieces,
-                      bool bEndBreak = false);
-  void EndBreak(CFX_BreakType dwStatus, float& fLinePos, bool bDefault);
+                      bool bEndBreak);
+  void EndBreak(CFX_BreakType dwStatus, float* pLinePos, bool bDefault);
   bool IsEnd(bool bSavePieces);
-  void ProcessText(WideString& wsText);
   void UpdateAlign(float fHeight, float fBottom);
   void RenderString(CFX_RenderDevice* pDevice,
                     CXFA_PieceLine* pPieceLine,
-                    int32_t iPiece,
-                    FXTEXT_CHARPOS* pCharPos,
-                    const CFX_Matrix& tmDoc2Device);
+                    size_t szPiece,
+                    std::vector<TextCharPos>* pCharPos,
+                    const CFX_Matrix& mtDoc2Device);
   void RenderPath(CFX_RenderDevice* pDevice,
                   CXFA_PieceLine* pPieceLine,
-                  int32_t iPiece,
-                  FXTEXT_CHARPOS* pCharPos,
-                  const CFX_Matrix& tmDoc2Device);
-  int32_t GetDisplayPos(const CXFA_TextPiece* pPiece,
-                        FXTEXT_CHARPOS* pCharPos,
-                        bool bCharCode = false);
-  bool ToRun(const CXFA_TextPiece* pPiece, FX_RTFTEXTOBJ* tr);
+                  size_t szPiece,
+                  std::vector<TextCharPos>* pCharPos,
+                  const CFX_Matrix& mtDoc2Device);
+  size_t GetDisplayPos(const CXFA_TextPiece* pPiece,
+                       std::vector<TextCharPos>* pCharPos);
   void DoTabstops(CFX_CSSComputedStyle* pStyle, CXFA_PieceLine* pPieceLine);
-  bool Layout(int32_t iBlock);
-  int32_t CountBlocks() const;
+  bool LayoutInternal(size_t szBlockIndex);
+  size_t CountBlocks() const;
+  size_t GetNextIndexFromLastBlockData() const;
+  void UpdateLoaderHeight(float fTextHeight);
 
-  CXFA_FFDoc* m_pDoc;
-  CXFA_TextProvider* m_pTextProvider;
-  CXFA_Node* m_pTextDataNode;
-  bool m_bRichText;
+  bool m_bHasBlock = false;
+  bool m_bRichText = false;
+  bool m_bBlockContinue = true;
+  int32_t m_iLines = 0;
+  float m_fMaxWidth = 0;
+  std::vector<BlockData> m_Blocks;
+  UnownedPtr<CXFA_FFDoc> const m_pDoc;
+  CXFA_TextProvider* const m_pTextProvider;  // Raw, owned by tree node.
+  CXFA_Node* m_pTextDataNode = nullptr;      // Raw, owned by tree node.
   std::unique_ptr<CFX_RTFBreak> m_pBreak;
   std::unique_ptr<CXFA_LoaderContext> m_pLoader;
-  int32_t m_iLines;
-  float m_fMaxWidth;
   CXFA_TextParser m_textParser;
   std::vector<std::unique_ptr<CXFA_PieceLine>> m_pieceLines;
   std::unique_ptr<CXFA_TextTabstopsContext> m_pTabstopContext;
-  bool m_bBlockContinue;
 };
 
 #endif  // XFA_FXFA_CXFA_TEXTLAYOUT_H_
diff --git a/xfa/fxfa/cxfa_textparsecontext.h b/xfa/fxfa/cxfa_textparsecontext.h
index 20d914b..a146d40 100644
--- a/xfa/fxfa/cxfa_textparsecontext.h
+++ b/xfa/fxfa/cxfa_textparsecontext.h
@@ -12,7 +12,6 @@
 
 #include "core/fxcrt/css/cfx_css.h"
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
-#include "third_party/base/stl_util.h"
 
 class CFX_CSSComputedStyle;
 
diff --git a/xfa/fxfa/cxfa_textparser.cpp b/xfa/fxfa/cxfa_textparser.cpp
index 07ba896..61b9a28 100644
--- a/xfa/fxfa/cxfa_textparser.cpp
+++ b/xfa/fxfa/cxfa_textparser.cpp
@@ -17,8 +17,10 @@
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxge/fx_font.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_fontmgr.h"
@@ -41,6 +43,18 @@
   Location,
 };
 
+WideString GetLowerCaseElementAttributeOrDefault(
+    const CFX_XMLElement* pElement,
+    const WideString& wsName,
+    const WideString& wsDefaultValue) {
+  WideString ws = pElement->GetAttribute(wsName);
+  if (ws.IsEmpty())
+    ws = wsDefaultValue;
+  else
+    ws.MakeLower();
+  return ws;
+}
+
 }  // namespace
 
 CXFA_TextParser::CXFA_TextParser()
@@ -74,20 +88,22 @@
 }
 
 std::unique_ptr<CFX_CSSStyleSheet> CXFA_TextParser::LoadDefaultSheetStyle() {
-  static const wchar_t s_pStyle[] =
-      L"html,body,ol,p,ul{display:block}"
-      L"li{display:list-item}"
-      L"ol,ul{padding-left:33px;margin:1.12em 0}"
-      L"ol{list-style-type:decimal}"
-      L"a{color:#0000ff;text-decoration:underline}"
-      L"b{font-weight:bolder}"
-      L"i{font-style:italic}"
-      L"sup{vertical-align:+15em;font-size:.66em}"
-      L"sub{vertical-align:-15em;font-size:.66em}";
-
+  static const char kStyle[] =
+      "html,body,ol,p,ul{display:block}"
+      "li{display:list-item}"
+      "ol,ul{padding-left:33px;margin:1.12em 0}"
+      "ol{list-style-type:decimal}"
+      "a{color:#0000ff;text-decoration:underline}"
+      "b{font-weight:bolder}"
+      "i{font-style:italic}"
+      "sup{vertical-align:+15em;font-size:.66em}"
+      "sub{vertical-align:-15em;font-size:.66em}";
+  WideString ws = WideString::FromASCII(kStyle);
   auto sheet = pdfium::MakeUnique<CFX_CSSStyleSheet>();
-  return sheet->LoadBuffer(s_pStyle, wcslen(s_pStyle)) ? std::move(sheet)
-                                                       : nullptr;
+  if (!sheet->LoadBuffer(ws.c_str(), ws.GetLength()))
+    return nullptr;
+
+  return sheet;
 }
 
 RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::CreateRootStyle(
@@ -104,20 +120,20 @@
     pStyle->SetTextIndent(indent);
     CFX_CSSTextAlign hAlign = CFX_CSSTextAlign::Left;
     switch (para->GetHorizontalAlign()) {
-      case XFA_AttributeEnum::Center:
+      case XFA_AttributeValue::Center:
         hAlign = CFX_CSSTextAlign::Center;
         break;
-      case XFA_AttributeEnum::Right:
+      case XFA_AttributeValue::Right:
         hAlign = CFX_CSSTextAlign::Right;
         break;
-      case XFA_AttributeEnum::Justify:
+      case XFA_AttributeValue::Justify:
         hAlign = CFX_CSSTextAlign::Justify;
         break;
-      case XFA_AttributeEnum::JustifyAll:
+      case XFA_AttributeValue::JustifyAll:
         hAlign = CFX_CSSTextAlign::JustifyAll;
         break;
-      case XFA_AttributeEnum::Left:
-      case XFA_AttributeEnum::Radix:
+      case XFA_AttributeValue::Left:
+      case XFA_AttributeValue::Radix:
         break;
       default:
         NOTREACHED();
@@ -180,7 +196,7 @@
 }
 
 RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::ComputeStyle(
-    CFX_XMLNode* pXMLNode,
+    const CFX_XMLNode* pXMLNode,
     CFX_CSSComputedStyle* pParentStyle) {
   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
   if (it == m_mapXMLNodeToParseContext.end())
@@ -203,7 +219,7 @@
   return pStyle;
 }
 
-void CXFA_TextParser::DoParse(CFX_XMLNode* pXMLContainer,
+void CXFA_TextParser::DoParse(const CFX_XMLNode* pXMLContainer,
                               CXFA_TextProvider* pTextProvider) {
   if (!pXMLContainer || !pTextProvider || m_bParsed)
     return;
@@ -214,7 +230,7 @@
   ParseRichText(pXMLContainer, pRootStyle.Get());
 }
 
-void CXFA_TextParser::ParseRichText(CFX_XMLNode* pXMLNode,
+void CXFA_TextParser::ParseRichText(const CFX_XMLNode* pXMLNode,
                                     CFX_CSSComputedStyle* pParentStyle) {
   if (!pXMLNode)
     return;
@@ -224,8 +240,8 @@
     return;
 
   RetainPtr<CFX_CSSComputedStyle> pNewStyle;
-  if ((tagProvider->GetTagName() != L"body") ||
-      (tagProvider->GetTagName() != L"html")) {
+  if (!(tagProvider->GetTagName().EqualsASCII("body") &&
+        tagProvider->GetTagName().EqualsASCII("html"))) {
     auto pTextContext = pdfium::MakeUnique<CXFA_TextParseContext>();
     CFX_CSSDisplay eDisplay = CFX_CSSDisplay::Inline;
     if (!tagProvider->m_bContent) {
@@ -245,9 +261,8 @@
     m_mapXMLNodeToParseContext[pXMLNode] = std::move(pTextContext);
   }
 
-  for (CFX_XMLNode* pXMLChild = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLChild;
-       pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
+  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
     ParseRichText(pXMLChild, pNewStyle.Get());
   }
 }
@@ -268,60 +283,56 @@
       0xb182eaae,  // body
       0xdb8ac455,  // html
   };
-  static const int32_t s_iCount = FX_ArraySize(s_XFATagName);
-
-  return std::binary_search(s_XFATagName, s_XFATagName + s_iCount,
+  return std::binary_search(std::begin(s_XFATagName), std::end(s_XFATagName),
                             FX_HashCode_GetW(wsName.AsStringView(), true));
 }
 
+// static
 std::unique_ptr<CXFA_TextParser::TagProvider> CXFA_TextParser::ParseTagInfo(
-    CFX_XMLNode* pXMLNode) {
+    const CFX_XMLNode* pXMLNode) {
   auto tagProvider = pdfium::MakeUnique<TagProvider>();
-
-  WideString wsName;
-  if (pXMLNode->GetType() == FX_XMLNODE_Element) {
-    CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-    wsName = pXMLElement->GetLocalTagName();
+  const CFX_XMLElement* pXMLElement = ToXMLElement(pXMLNode);
+  if (pXMLElement) {
+    WideString wsName = pXMLElement->GetLocalTagName();
     tagProvider->SetTagName(wsName);
     tagProvider->m_bTagAvailable = TagValidate(wsName);
-
-    WideString wsValue = pXMLElement->GetString(L"style");
+    WideString wsValue = pXMLElement->GetAttribute(L"style");
     if (!wsValue.IsEmpty())
       tagProvider->SetAttribute(L"style", wsValue);
-  } else if (pXMLNode->GetType() == FX_XMLNODE_Text) {
+
+    return tagProvider;
+  }
+  if (pXMLNode->GetType() == CFX_XMLNode::Type::kText) {
     tagProvider->m_bTagAvailable = true;
     tagProvider->m_bContent = true;
   }
   return tagProvider;
 }
 
-XFA_AttributeEnum CXFA_TextParser::GetVAlign(
+XFA_AttributeValue CXFA_TextParser::GetVAlign(
     CXFA_TextProvider* pTextProvider) const {
   CXFA_Para* para = pTextProvider->GetParaIfExists();
-  return para ? para->GetVerticalAlign() : XFA_AttributeEnum::Top;
+  return para ? para->GetVerticalAlign() : XFA_AttributeValue::Top;
 }
 
 float CXFA_TextParser::GetTabInterval(CFX_CSSComputedStyle* pStyle) const {
   WideString wsValue;
-  if (pStyle && pStyle->GetCustomStyle(L"tab-interval", wsValue))
+  if (pStyle && pStyle->GetCustomStyle(L"tab-interval", &wsValue))
     return CXFA_Measurement(wsValue.AsStringView()).ToUnit(XFA_Unit::Pt);
   return 36;
 }
 
 int32_t CXFA_TextParser::CountTabs(CFX_CSSComputedStyle* pStyle) const {
   WideString wsValue;
-  if (pStyle && pStyle->GetCustomStyle(L"xfa-tab-count", wsValue))
+  if (pStyle && pStyle->GetCustomStyle(L"xfa-tab-count", &wsValue))
     return wsValue.GetInteger();
   return 0;
 }
 
 bool CXFA_TextParser::IsSpaceRun(CFX_CSSComputedStyle* pStyle) const {
   WideString wsValue;
-  if (pStyle && pStyle->GetCustomStyle(L"xfa-spacerun", wsValue)) {
-    wsValue.MakeLower();
-    return wsValue == L"yes";
-  }
-  return false;
+  return pStyle && pStyle->GetCustomStyle(L"xfa-spacerun", &wsValue) &&
+         wsValue.EqualsASCIINoCase("yes");
 }
 
 RetainPtr<CFGAS_GEFont> CXFA_TextParser::GetFont(
@@ -334,9 +345,9 @@
   if (font) {
     wsFamily = font->GetTypeface();
     if (font->IsBold())
-      dwStyle |= FXFONT_BOLD;
+      dwStyle |= FXFONT_FORCE_BOLD;
     if (font->IsItalic())
-      dwStyle |= FXFONT_BOLD;
+      dwStyle |= FXFONT_FORCE_BOLD;
   }
 
   if (pStyle) {
@@ -346,7 +357,7 @@
 
     dwStyle = 0;
     if (pStyle->GetFontWeight() > FXFONT_FW_NORMAL)
-      dwStyle |= FXFONT_BOLD;
+      dwStyle |= FXFONT_FORCE_BOLD;
     if (pStyle->GetFontStyle() == CFX_CSSFontStyle::Italic)
       dwStyle |= FXFONT_ITALIC;
   }
@@ -366,10 +377,10 @@
 
 int32_t CXFA_TextParser::GetHorScale(CXFA_TextProvider* pTextProvider,
                                      CFX_CSSComputedStyle* pStyle,
-                                     CFX_XMLNode* pXMLNode) const {
+                                     const CFX_XMLNode* pXMLNode) const {
   if (pStyle) {
     WideString wsValue;
-    if (pStyle->GetCustomStyle(L"xfa-font-horizontal-scale", wsValue))
+    if (pStyle->GetCustomStyle(L"xfa-font-horizontal-scale", &wsValue))
       return wsValue.GetInteger();
 
     while (pXMLNode) {
@@ -378,11 +389,11 @@
         CXFA_TextParseContext* pContext = it->second.get();
         if (pContext && pContext->m_pParentStyle &&
             pContext->m_pParentStyle->GetCustomStyle(
-                L"xfa-font-horizontal-scale", wsValue)) {
+                L"xfa-font-horizontal-scale", &wsValue)) {
           return wsValue.GetInteger();
         }
       }
-      pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::Parent);
+      pXMLNode = pXMLNode->GetParent();
     }
   }
 
@@ -394,7 +405,7 @@
                                      CFX_CSSComputedStyle* pStyle) const {
   if (pStyle) {
     WideString wsValue;
-    if (pStyle->GetCustomStyle(L"xfa-font-vertical-scale", wsValue))
+    if (pStyle->GetCustomStyle(L"xfa-font-vertical-scale", &wsValue))
       return wsValue.GetInteger();
   }
 
@@ -405,9 +416,9 @@
 void CXFA_TextParser::GetUnderline(CXFA_TextProvider* pTextProvider,
                                    CFX_CSSComputedStyle* pStyle,
                                    int32_t& iUnderline,
-                                   XFA_AttributeEnum& iPeriod) const {
+                                   XFA_AttributeValue& iPeriod) const {
   iUnderline = 0;
-  iPeriod = XFA_AttributeEnum::All;
+  iPeriod = XFA_AttributeValue::All;
   CXFA_Font* font = pTextProvider->GetFontIfExists();
   if (!pStyle) {
     if (font) {
@@ -424,9 +435,9 @@
     iUnderline = 1;
 
   WideString wsValue;
-  if (pStyle->GetCustomStyle(L"underlinePeriod", wsValue)) {
-    if (wsValue == L"word")
-      iPeriod = XFA_AttributeEnum::Word;
+  if (pStyle->GetCustomStyle(L"underlinePeriod", &wsValue)) {
+    if (wsValue.EqualsASCII("word"))
+      iPeriod = XFA_AttributeValue::Word;
   } else if (font) {
     iPeriod = font->GetUnderlinePeriod();
   }
@@ -435,9 +446,11 @@
 void CXFA_TextParser::GetLinethrough(CXFA_TextProvider* pTextProvider,
                                      CFX_CSSComputedStyle* pStyle,
                                      int32_t& iLinethrough) const {
+  iLinethrough = 0;
   if (pStyle) {
     uint32_t dwDecoration = pStyle->GetTextDecoration();
-    iLinethrough = (dwDecoration & CFX_CSSTEXTDECORATION_LineThrough) ? 1 : 0;
+    if (dwDecoration & CFX_CSSTEXTDECORATION_LineThrough)
+      iLinethrough = 1;
     return;
   }
 
@@ -494,49 +507,38 @@
   return fLineHeight;
 }
 
-bool CXFA_TextParser::GetEmbbedObj(CXFA_TextProvider* pTextProvider,
-                                   CFX_XMLNode* pXMLNode,
-                                   WideString& wsValue) {
-  wsValue.clear();
+Optional<WideString> CXFA_TextParser::GetEmbeddedObj(
+    const CXFA_TextProvider* pTextProvider,
+    const CFX_XMLNode* pXMLNode) {
   if (!pXMLNode)
-    return false;
+    return {};
 
-  bool bRet = false;
-  if (pXMLNode->GetType() == FX_XMLNODE_Element) {
-    CFX_XMLElement* pElement = static_cast<CFX_XMLElement*>(pXMLNode);
-    WideString wsAttr = pElement->GetString(L"xfa:embed");
-    if (wsAttr.IsEmpty())
-      return false;
-    if (wsAttr[0] == L'#')
-      wsAttr.Delete(0);
+  const CFX_XMLElement* pElement = ToXMLElement(pXMLNode);
+  if (!pElement)
+    return {};
 
-    WideString ws = pElement->GetString(L"xfa:embedType");
-    if (ws.IsEmpty())
-      ws = L"som";
-    else
-      ws.MakeLower();
+  WideString wsAttr = pElement->GetAttribute(L"xfa:embed");
+  if (wsAttr.IsEmpty())
+    return {};
 
-    bool bURI = (ws == L"uri");
-    if (!bURI && ws != L"som")
-      return false;
+  if (wsAttr[0] == L'#')
+    wsAttr.Delete(0);
 
-    ws = pElement->GetString(L"xfa:embedMode");
-    if (ws.IsEmpty())
-      ws = L"formatted";
-    else
-      ws.MakeLower();
+  WideString ws =
+      GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedType", L"som");
+  if (!ws.EqualsASCII("uri"))
+    return {};
 
-    bool bRaw = (ws == L"raw");
-    if (!bRaw && ws != L"formatted")
-      return false;
+  ws = GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedMode",
+                                             L"formatted");
+  if (!(ws.EqualsASCII("raw") || ws.EqualsASCII("formatted")))
+    return {};
 
-    bRet = pTextProvider->GetEmbbedObj(bURI, bRaw, wsAttr, wsValue);
-  }
-  return bRet;
+  return pTextProvider->GetEmbeddedObj(wsAttr);
 }
 
 CXFA_TextParseContext* CXFA_TextParser::GetParseContextFromMap(
-    CFX_XMLNode* pXMLNode) {
+    const CFX_XMLNode* pXMLNode) {
   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
   return it != m_mapXMLNodeToParseContext.end() ? it->second.get() : nullptr;
 }
@@ -547,20 +549,18 @@
     return false;
 
   WideString wsValue;
-  if (!pStyle->GetCustomStyle(L"xfa-tab-stops", wsValue) &&
-      !pStyle->GetCustomStyle(L"tab-stops", wsValue)) {
+  if (!pStyle->GetCustomStyle(L"xfa-tab-stops", &wsValue) &&
+      !pStyle->GetCustomStyle(L"tab-stops", &wsValue)) {
     return false;
   }
 
-  int32_t iLength = wsValue.GetLength();
-  const wchar_t* pTabStops = wsValue.c_str();
-  int32_t iCur = 0;
-  int32_t iLast = 0;
+  pdfium::span<const wchar_t> spTabStops = wsValue.span();
+  size_t iCur = 0;
+  size_t iLast = 0;
   WideString wsAlign;
   TabStopStatus eStatus = TabStopStatus::None;
-  wchar_t ch;
-  while (iCur < iLength) {
-    ch = pTabStops[iCur];
+  while (iCur < spTabStops.size()) {
+    wchar_t ch = spTabStops[iCur];
     switch (eStatus) {
       case TabStopStatus::None:
         if (ch <= ' ') {
@@ -572,10 +572,10 @@
         break;
       case TabStopStatus::Alignment:
         if (ch == ' ') {
-          wsAlign = WideStringView(pTabStops + iLast, iCur - iLast);
+          wsAlign = WideStringView(spTabStops.subspan(iLast, iCur - iLast));
           eStatus = TabStopStatus::StartLeader;
           iCur++;
-          while (iCur < iLength && pTabStops[iCur] <= ' ')
+          while (iCur < spTabStops.size() && spTabStops[iCur] <= ' ')
             iCur++;
           iLast = iCur;
         } else {
@@ -587,8 +587,8 @@
           eStatus = TabStopStatus::Location;
         } else {
           int32_t iCount = 0;
-          while (iCur < iLength) {
-            ch = pTabStops[iCur];
+          while (iCur < spTabStops.size()) {
+            ch = spTabStops[iCur];
             iCur++;
             if (ch == '(') {
               iCount++;
@@ -598,7 +598,7 @@
                 break;
             }
           }
-          while (iCur < iLength && pTabStops[iCur] <= ' ')
+          while (iCur < spTabStops.size() && spTabStops[iCur] <= ' ')
             iCur++;
 
           iLast = iCur;
@@ -608,7 +608,8 @@
       case TabStopStatus::Location:
         if (ch == ' ') {
           uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringView(), true);
-          CXFA_Measurement ms(WideStringView(pTabStops + iLast, iCur - iLast));
+          CXFA_Measurement ms(
+              WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
           float fPos = ms.ToUnit(XFA_Unit::Pt);
           pTabstopContext->Append(dwHashCode, fPos);
           wsAlign.clear();
@@ -623,7 +624,8 @@
 
   if (!wsAlign.IsEmpty()) {
     uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringView(), true);
-    CXFA_Measurement ms(WideStringView(pTabStops + iLast, iCur - iLast));
+    CXFA_Measurement ms(
+        WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
     float fPos = ms.ToUnit(XFA_Unit::Pt);
     pTabstopContext->Append(dwHashCode, fPos);
   }
diff --git a/xfa/fxfa/cxfa_textparser.h b/xfa/fxfa/cxfa_textparser.h
index cd2856e..b1e5878 100644
--- a/xfa/fxfa/cxfa_textparser.h
+++ b/xfa/fxfa/cxfa_textparser.h
@@ -14,6 +14,7 @@
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/fx_dib.h"
+#include "third_party/base/optional.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CFGAS_GEFont;
@@ -32,17 +33,18 @@
   virtual ~CXFA_TextParser();
 
   void Reset();
-  void DoParse(CFX_XMLNode* pXMLContainer, CXFA_TextProvider* pTextProvider);
+  void DoParse(const CFX_XMLNode* pXMLContainer,
+               CXFA_TextProvider* pTextProvider);
 
   RetainPtr<CFX_CSSComputedStyle> CreateRootStyle(
       CXFA_TextProvider* pTextProvider);
   RetainPtr<CFX_CSSComputedStyle> ComputeStyle(
-      CFX_XMLNode* pXMLNode,
+      const CFX_XMLNode* pXMLNode,
       CFX_CSSComputedStyle* pParentStyle);
 
   bool IsParsed() const { return m_bParsed; }
 
-  XFA_AttributeEnum GetVAlign(CXFA_TextProvider* pTextProvider) const;
+  XFA_AttributeValue GetVAlign(CXFA_TextProvider* pTextProvider) const;
 
   float GetTabInterval(CFX_CSSComputedStyle* pStyle) const;
   int32_t CountTabs(CFX_CSSComputedStyle* pStyle) const;
@@ -59,14 +61,14 @@
 
   int32_t GetHorScale(CXFA_TextProvider* pTextProvider,
                       CFX_CSSComputedStyle* pStyle,
-                      CFX_XMLNode* pXMLNode) const;
+                      const CFX_XMLNode* pXMLNode) const;
   int32_t GetVerScale(CXFA_TextProvider* pTextProvider,
                       CFX_CSSComputedStyle* pStyle) const;
 
   void GetUnderline(CXFA_TextProvider* pTextProvider,
                     CFX_CSSComputedStyle* pStyle,
                     int32_t& iUnderline,
-                    XFA_AttributeEnum& iPeriod) const;
+                    XFA_AttributeValue& iPeriod) const;
   void GetLinethrough(CXFA_TextProvider* pTextProvider,
                       CFX_CSSComputedStyle* pStyle,
                       int32_t& iLinethrough) const;
@@ -79,10 +81,9 @@
                       bool bFirst,
                       float fVerScale) const;
 
-  bool GetEmbbedObj(CXFA_TextProvider* pTextProvider,
-                    CFX_XMLNode* pXMLNode,
-                    WideString& wsValue);
-  CXFA_TextParseContext* GetParseContextFromMap(CFX_XMLNode* pXMLNode);
+  Optional<WideString> GetEmbeddedObj(const CXFA_TextProvider* pTextProvider,
+                                      const CFX_XMLNode* pXMLNode);
+  CXFA_TextParseContext* GetParseContextFromMap(const CFX_XMLNode* pXMLNode);
 
  protected:
   bool TagValidate(const WideString& str) const;
@@ -112,9 +113,12 @@
     std::map<WideString, WideString> m_Attributes;
   };
 
+  // static
+  std::unique_ptr<TagProvider> ParseTagInfo(const CFX_XMLNode* pXMLNode);
+
   void InitCSSData(CXFA_TextProvider* pTextProvider);
-  void ParseRichText(CFX_XMLNode* pXMLNode, CFX_CSSComputedStyle* pParentStyle);
-  std::unique_ptr<TagProvider> ParseTagInfo(CFX_XMLNode* pXMLNode);
+  void ParseRichText(const CFX_XMLNode* pXMLNode,
+                     CFX_CSSComputedStyle* pParentStyle);
   std::unique_ptr<CFX_CSSStyleSheet> LoadDefaultSheetStyle();
   RetainPtr<CFX_CSSComputedStyle> CreateStyle(
       CFX_CSSComputedStyle* pParentStyle);
@@ -122,7 +126,7 @@
   bool m_bParsed;
   bool m_cssInitialized;
   std::unique_ptr<CFX_CSSStyleSelector> m_pSelector;
-  std::map<CFX_XMLNode*, std::unique_ptr<CXFA_TextParseContext>>
+  std::map<const CFX_XMLNode*, std::unique_ptr<CXFA_TextParseContext>>
       m_mapXMLNodeToParseContext;
 };
 
diff --git a/xfa/fxfa/cxfa_textparser_unittest.cpp b/xfa/fxfa/cxfa_textparser_unittest.cpp
index bd748cd..5198638 100644
--- a/xfa/fxfa/cxfa_textparser_unittest.cpp
+++ b/xfa/fxfa/cxfa_textparser_unittest.cpp
@@ -6,7 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CXFA_TestTextParser : public CXFA_TextParser {
+class CXFA_TestTextParser final : public CXFA_TextParser {
  public:
   CXFA_TestTextParser() : CXFA_TextParser() {}
 
diff --git a/xfa/fxfa/cxfa_textpiece.cpp b/xfa/fxfa/cxfa_textpiece.cpp
index d6a8d3a..363361a 100644
--- a/xfa/fxfa/cxfa_textpiece.cpp
+++ b/xfa/fxfa/cxfa_textpiece.cpp
@@ -6,9 +6,8 @@
 
 #include "xfa/fxfa/cxfa_textpiece.h"
 
-#include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fxfa/cxfa_linkuserdata.h"
+#include "xfa/fgas/layout/cfx_linkuserdata.h"
 
-CXFA_TextPiece::CXFA_TextPiece() {}
+CXFA_TextPiece::CXFA_TextPiece() = default;
 
-CXFA_TextPiece::~CXFA_TextPiece() {}
+CXFA_TextPiece::~CXFA_TextPiece() = default;
diff --git a/xfa/fxfa/cxfa_textpiece.h b/xfa/fxfa/cxfa_textpiece.h
index f3812f8..c9d524d 100644
--- a/xfa/fxfa/cxfa_textpiece.h
+++ b/xfa/fxfa/cxfa_textpiece.h
@@ -7,35 +7,23 @@
 #ifndef XFA_FXFA_CXFA_TEXTPIECE_H_
 #define XFA_FXFA_CXFA_TEXTPIECE_H_
 
-#include <vector>
-
-#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 "xfa/fgas/layout/cfx_textpiece.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-class CFGAS_GEFont;
-class CXFA_LinkUserData;
+class CFX_LinkUserData;
 
-class CXFA_TextPiece {
+class CXFA_TextPiece : public CFX_TextPiece {
  public:
   CXFA_TextPiece();
   ~CXFA_TextPiece();
 
-  WideString szText;
-  std::vector<int32_t> Widths;
-  int32_t iChars;
-  int32_t iHorScale;
-  int32_t iVerScale;
-  int32_t iBidiLevel;
   int32_t iUnderline;
-  XFA_AttributeEnum iPeriod;
   int32_t iLineThrough;
+  XFA_AttributeValue iPeriod;
   FX_ARGB dwColor;
-  float fFontSize;
-  CFX_RectF rtPiece;
-  RetainPtr<CFGAS_GEFont> pFont;
-  RetainPtr<CXFA_LinkUserData> pLinkData;
+  RetainPtr<CFX_LinkUserData> pLinkData;
 };
 
 #endif  // XFA_FXFA_CXFA_TEXTPIECE_H_
diff --git a/xfa/fxfa/cxfa_textprovider.cpp b/xfa/fxfa/cxfa_textprovider.cpp
index 55da281..7a17037 100644
--- a/xfa/fxfa/cxfa_textprovider.cpp
+++ b/xfa/fxfa/cxfa_textprovider.cpp
@@ -12,11 +12,9 @@
 
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
@@ -31,7 +29,6 @@
 #include "xfa/fxfa/parser/cxfa_caption.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
 #include "xfa/fxfa/parser/cxfa_items.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
@@ -39,13 +36,11 @@
 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-CXFA_Node* CXFA_TextProvider::GetTextNode(bool& bRichText) {
-  bRichText = false;
-
+CXFA_Node* CXFA_TextProvider::GetTextNode(bool* bRichText) {
+  *bRichText = false;
   if (m_eType == XFA_TEXTPROVIDERTYPE_Text) {
-    CXFA_Node* pElementNode = m_pWidgetAcc->GetNode();
     CXFA_Value* pValueNode =
-        pElementNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+        m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
     if (!pValueNode)
       return nullptr;
 
@@ -53,24 +48,23 @@
     if (pChildNode && pChildNode->GetElementType() == XFA_Element::ExData) {
       Optional<WideString> contentType = pChildNode->JSObject()->TryAttribute(
           XFA_Attribute::ContentType, false);
-      if (contentType && *contentType == L"text/html")
-        bRichText = true;
+      if (contentType.has_value() &&
+          contentType.value().EqualsASCII("text/html")) {
+        *bRichText = true;
+      }
     }
     return pChildNode;
   }
 
   if (m_eType == XFA_TEXTPROVIDERTYPE_Datasets) {
-    CXFA_Node* pBind = m_pWidgetAcc->GetNode()->GetBindData();
+    CXFA_Node* pBind = m_pNode->GetBindData();
     CFX_XMLNode* pXMLNode = pBind->GetXMLMappingNode();
-    ASSERT(pXMLNode);
-    for (CFX_XMLNode* pXMLChild =
-             pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-         pXMLChild;
-         pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-      if (pXMLChild->GetType() == FX_XMLNODE_Element) {
-        CFX_XMLElement* pElement = static_cast<CFX_XMLElement*>(pXMLChild);
-        if (XFA_RecognizeRichText(pElement))
-          bRichText = true;
+    for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+         pXMLChild = pXMLChild->GetNextSibling()) {
+      CFX_XMLElement* pElement = ToXMLElement(pXMLChild);
+      if (pElement && XFA_RecognizeRichText(pElement)) {
+        *bRichText = true;
+        break;
       }
     }
     return pBind;
@@ -78,8 +72,7 @@
 
   if (m_eType == XFA_TEXTPROVIDERTYPE_Caption) {
     CXFA_Caption* pCaptionNode =
-        m_pWidgetAcc->GetNode()->GetChild<CXFA_Caption>(0, XFA_Element::Caption,
-                                                        false);
+        m_pNode->GetChild<CXFA_Caption>(0, XFA_Element::Caption, false);
     if (!pCaptionNode)
       return nullptr;
 
@@ -92,23 +85,27 @@
     if (pChildNode && pChildNode->GetElementType() == XFA_Element::ExData) {
       Optional<WideString> contentType = pChildNode->JSObject()->TryAttribute(
           XFA_Attribute::ContentType, false);
-      if (contentType && *contentType == L"text/html")
-        bRichText = true;
+      if (contentType.has_value() &&
+          contentType.value().EqualsASCII("text/html")) {
+        *bRichText = true;
+      }
     }
     return pChildNode;
   }
 
-  CXFA_Items* pItemNode = m_pWidgetAcc->GetNode()->GetChild<CXFA_Items>(
-      0, XFA_Element::Items, false);
+  CXFA_Items* pItemNode =
+      m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
   if (!pItemNode)
     return nullptr;
 
   CXFA_Node* pNode = pItemNode->GetFirstChild();
   while (pNode) {
     WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
-    if (m_eType == XFA_TEXTPROVIDERTYPE_Rollover && wsName == L"rollover")
+    if (m_eType == XFA_TEXTPROVIDERTYPE_Rollover &&
+        wsName.EqualsASCII("rollover")) {
       return pNode;
-    if (m_eType == XFA_TEXTPROVIDERTYPE_Down && wsName == L"down")
+    }
+    if (m_eType == XFA_TEXTPROVIDERTYPE_Down && wsName.EqualsASCII("down"))
       return pNode;
 
     pNode = pNode->GetNextSibling();
@@ -118,45 +115,37 @@
 
 CXFA_Para* CXFA_TextProvider::GetParaIfExists() {
   if (m_eType == XFA_TEXTPROVIDERTYPE_Text)
-    return m_pWidgetAcc->GetNode()->GetParaIfExists();
+    return m_pNode->GetParaIfExists();
 
-  CXFA_Caption* pNode = m_pWidgetAcc->GetNode()->GetChild<CXFA_Caption>(
-      0, XFA_Element::Caption, false);
+  CXFA_Caption* pNode =
+      m_pNode->GetChild<CXFA_Caption>(0, XFA_Element::Caption, false);
   return pNode->GetChild<CXFA_Para>(0, XFA_Element::Para, false);
 }
 
 CXFA_Font* CXFA_TextProvider::GetFontIfExists() {
   if (m_eType == XFA_TEXTPROVIDERTYPE_Text)
-    return m_pWidgetAcc->GetNode()->GetFontIfExists();
+    return m_pNode->GetFontIfExists();
 
-  CXFA_Caption* pNode = m_pWidgetAcc->GetNode()->GetChild<CXFA_Caption>(
-      0, XFA_Element::Caption, false);
+  CXFA_Caption* pNode =
+      m_pNode->GetChild<CXFA_Caption>(0, XFA_Element::Caption, false);
   CXFA_Font* font = pNode->GetChild<CXFA_Font>(0, XFA_Element::Font, false);
-  return font ? font : m_pWidgetAcc->GetNode()->GetFontIfExists();
+  return font ? font : m_pNode->GetFontIfExists();
 }
 
-bool CXFA_TextProvider::IsCheckButtonAndAutoWidth() {
-  XFA_Element eType = m_pWidgetAcc->GetUIType();
-  if (eType != XFA_Element::CheckButton)
+bool CXFA_TextProvider::IsCheckButtonAndAutoWidth() const {
+  if (m_pNode->GetFFWidgetType() != XFA_FFWidgetType::kCheckButton)
     return false;
-  return !m_pWidgetAcc->GetNode()->TryWidth();
+  return !m_pNode->TryWidth();
 }
 
-bool CXFA_TextProvider::GetEmbbedObj(bool bURI,
-                                     bool bRaw,
-                                     const WideString& wsAttr,
-                                     WideString& wsValue) {
+Optional<WideString> CXFA_TextProvider::GetEmbeddedObj(
+    const WideString& wsAttr) const {
   if (m_eType != XFA_TEXTPROVIDERTYPE_Text)
-    return false;
+    return {};
 
-  if (!bURI)
-    return false;
-
-  CXFA_Node* pWidgetNode = m_pWidgetAcc->GetNode();
-  CXFA_Node* pParent = pWidgetNode->GetParent();
-  CXFA_Document* pDocument = pWidgetNode->GetDocument();
+  CXFA_Node* pParent = m_pNode->GetParent();
+  CXFA_Document* pDocument = m_pNode->GetDocument();
   CXFA_Node* pIDNode = nullptr;
-  CXFA_WidgetAcc* pEmbAcc = nullptr;
   if (pParent)
     pIDNode = pDocument->GetNodeByID(pParent, wsAttr.AsStringView());
 
@@ -165,12 +154,8 @@
         ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Form)),
         wsAttr.AsStringView());
   }
-  if (pIDNode)
-    pEmbAcc = pIDNode->GetWidgetAcc();
+  if (!pIDNode || !pIDNode->IsWidgetReady())
+    return {};
 
-  if (!pEmbAcc)
-    return false;
-
-  wsValue = pEmbAcc->GetValue(XFA_VALUEPICTURE_Display);
-  return true;
+  return pIDNode->GetValue(XFA_VALUEPICTURE_Display);
 }
diff --git a/xfa/fxfa/cxfa_textprovider.h b/xfa/fxfa/cxfa_textprovider.h
index 3431c9a..b4a9664 100644
--- a/xfa/fxfa/cxfa_textprovider.h
+++ b/xfa/fxfa/cxfa_textprovider.h
@@ -8,11 +8,12 @@
 #define XFA_FXFA_CXFA_TEXTPROVIDER_H_
 
 #include "core/fxcrt/fx_string.h"
+#include "third_party/base/optional.h"
 #include "xfa/fxfa/cxfa_textlayout.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
 
 class CXFA_Font;
 class CXFA_Node;
+class CXFA_Para;
 
 enum XFA_TEXTPROVIDERTYPE {
   XFA_TEXTPROVIDERTYPE_Text,
@@ -24,23 +25,20 @@
 
 class CXFA_TextProvider {
  public:
-  CXFA_TextProvider(CXFA_WidgetAcc* pWidgetAcc, XFA_TEXTPROVIDERTYPE eType)
-      : m_pWidgetAcc(pWidgetAcc), m_eType(eType) {
-    ASSERT(m_pWidgetAcc);
+  CXFA_TextProvider(CXFA_Node* pNode, XFA_TEXTPROVIDERTYPE eType)
+      : m_pNode(pNode), m_eType(eType) {
+    ASSERT(m_pNode);
   }
   ~CXFA_TextProvider() {}
 
-  CXFA_Node* GetTextNode(bool& bRichText);
+  CXFA_Node* GetTextNode(bool* bRichText);
   CXFA_Para* GetParaIfExists();
   CXFA_Font* GetFontIfExists();
-  bool IsCheckButtonAndAutoWidth();
-  bool GetEmbbedObj(bool bURI,
-                    bool bRaw,
-                    const WideString& wsAttr,
-                    WideString& wsValue);
+  bool IsCheckButtonAndAutoWidth() const;
+  Optional<WideString> GetEmbeddedObj(const WideString& wsAttr) const;
 
  private:
-  CXFA_WidgetAcc* m_pWidgetAcc;
+  CXFA_Node* m_pNode;  // Raw, this class owned by tree node.
   XFA_TEXTPROVIDERTYPE m_eType;
 };
 
diff --git a/xfa/fxfa/cxfa_textuserdata.cpp b/xfa/fxfa/cxfa_textuserdata.cpp
deleted file mode 100644
index 6c46978..0000000
--- a/xfa/fxfa/cxfa_textuserdata.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_textuserdata.h"
-
-#include "core/fxcrt/css/cfx_css.h"
-#include "core/fxcrt/css/cfx_csscomputedstyle.h"
-#include "core/fxcrt/css/cfx_cssstyleselector.h"
-#include "xfa/fxfa/cxfa_linkuserdata.h"
-
-CXFA_TextUserData::CXFA_TextUserData(
-    const RetainPtr<CFX_CSSComputedStyle>& pStyle)
-    : m_pStyle(pStyle) {}
-
-CXFA_TextUserData::CXFA_TextUserData(
-    const RetainPtr<CFX_CSSComputedStyle>& pStyle,
-    const RetainPtr<CXFA_LinkUserData>& pLinkData)
-    : m_pStyle(pStyle), m_pLinkData(pLinkData) {}
-
-CXFA_TextUserData::~CXFA_TextUserData() {}
diff --git a/xfa/fxfa/cxfa_textuserdata.h b/xfa/fxfa/cxfa_textuserdata.h
deleted file mode 100644
index c1af217..0000000
--- a/xfa/fxfa/cxfa_textuserdata.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_TEXTUSERDATA_H_
-#define XFA_FXFA_CXFA_TEXTUSERDATA_H_
-
-#include "core/fxcrt/retain_ptr.h"
-
-class CFX_CSSComputedStyle;
-class CXFA_LinkUserData;
-
-class CXFA_TextUserData : public Retainable {
- public:
-  template <typename T, typename... Args>
-  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
-
-  RetainPtr<CFX_CSSComputedStyle> m_pStyle;
-  RetainPtr<CXFA_LinkUserData> m_pLinkData;
-
- private:
-  explicit CXFA_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle);
-  CXFA_TextUserData(const RetainPtr<CFX_CSSComputedStyle>& pStyle,
-                    const RetainPtr<CXFA_LinkUserData>& pLinkData);
-  ~CXFA_TextUserData() override;
-};
-
-#endif  // XFA_FXFA_CXFA_TEXTUSERDATA_H_
diff --git a/xfa/fxfa/cxfa_widgetacc.cpp b/xfa/fxfa/cxfa_widgetacc.cpp
deleted file mode 100644
index ad4e08e..0000000
--- a/xfa/fxfa/cxfa_widgetacc.cpp
+++ /dev/null
@@ -1,2757 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_widgetacc.h"
-
-#include <algorithm>
-#include <tuple>
-#include <vector>
-
-#include "core/fxcrt/cfx_decimal.h"
-#include "core/fxcrt/cfx_memorystream.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fde/cfde_textout.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
-#include "xfa/fxfa/cxfa_ffdoc.h"
-#include "xfa/fxfa/cxfa_ffdocview.h"
-#include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
-#include "xfa/fxfa/cxfa_fontmgr.h"
-#include "xfa/fxfa/cxfa_textlayout.h"
-#include "xfa/fxfa/cxfa_textprovider.h"
-#include "xfa/fxfa/parser/cxfa_bind.h"
-#include "xfa/fxfa/parser/cxfa_border.h"
-#include "xfa/fxfa/parser/cxfa_calculate.h"
-#include "xfa/fxfa/parser/cxfa_caption.h"
-#include "xfa/fxfa/parser/cxfa_comb.h"
-#include "xfa/fxfa/parser/cxfa_decimal.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_event.h"
-#include "xfa/fxfa/parser/cxfa_font.h"
-#include "xfa/fxfa/parser/cxfa_format.h"
-#include "xfa/fxfa/parser/cxfa_image.h"
-#include "xfa/fxfa/parser/cxfa_items.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
-#include "xfa/fxfa/parser/cxfa_localevalue.h"
-#include "xfa/fxfa/parser/cxfa_margin.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_para.h"
-#include "xfa/fxfa/parser/cxfa_picture.h"
-#include "xfa/fxfa/parser/cxfa_script.h"
-#include "xfa/fxfa/parser/cxfa_stroke.h"
-#include "xfa/fxfa/parser/cxfa_ui.h"
-#include "xfa/fxfa/parser/cxfa_validate.h"
-#include "xfa/fxfa/parser/cxfa_value.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-class CXFA_WidgetLayoutData {
- public:
-  CXFA_WidgetLayoutData() : m_fWidgetHeight(-1) {}
-  virtual ~CXFA_WidgetLayoutData() {}
-
-  float m_fWidgetHeight;
-};
-
-namespace {
-
-constexpr uint8_t g_inv_base64[128] = {
-    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255,
-    255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
-    255, 255, 255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
-    25,  255, 255, 255, 255, 255, 255, 26,  27,  28,  29,  30,  31,  32,  33,
-    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
-    49,  50,  51,  255, 255, 255, 255, 255,
-};
-
-uint8_t* XFA_RemoveBase64Whitespace(const uint8_t* pStr, int32_t iLen) {
-  uint8_t* pCP;
-  int32_t i = 0, j = 0;
-  if (iLen == 0) {
-    iLen = strlen((char*)pStr);
-  }
-  pCP = FX_Alloc(uint8_t, iLen + 1);
-  for (; i < iLen; i++) {
-    if ((pStr[i] & 128) == 0) {
-      if (g_inv_base64[pStr[i]] != 0xFF || pStr[i] == '=') {
-        pCP[j++] = pStr[i];
-      }
-    }
-  }
-  pCP[j] = '\0';
-  return pCP;
-}
-
-int32_t XFA_Base64Decode(const char* pStr, uint8_t* pOutBuffer) {
-  if (!pStr) {
-    return 0;
-  }
-  uint8_t* pBuffer =
-      XFA_RemoveBase64Whitespace((uint8_t*)pStr, strlen((char*)pStr));
-  if (!pBuffer) {
-    return 0;
-  }
-  int32_t iLen = strlen((char*)pBuffer);
-  int32_t i = 0, j = 0;
-  uint32_t dwLimb = 0;
-  for (; i + 3 < iLen; i += 4) {
-    if (pBuffer[i] == '=' || pBuffer[i + 1] == '=' || pBuffer[i + 2] == '=' ||
-        pBuffer[i + 3] == '=') {
-      if (pBuffer[i] == '=' || pBuffer[i + 1] == '=') {
-        break;
-      }
-      if (pBuffer[i + 2] == '=') {
-        dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 6) |
-                 ((uint32_t)g_inv_base64[pBuffer[i + 1]]);
-        pOutBuffer[j] = (uint8_t)(dwLimb >> 4) & 0xFF;
-        j++;
-      } else {
-        dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 12) |
-                 ((uint32_t)g_inv_base64[pBuffer[i + 1]] << 6) |
-                 ((uint32_t)g_inv_base64[pBuffer[i + 2]]);
-        pOutBuffer[j] = (uint8_t)(dwLimb >> 10) & 0xFF;
-        pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 2) & 0xFF;
-        j += 2;
-      }
-    } else {
-      dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 18) |
-               ((uint32_t)g_inv_base64[pBuffer[i + 1]] << 12) |
-               ((uint32_t)g_inv_base64[pBuffer[i + 2]] << 6) |
-               ((uint32_t)g_inv_base64[pBuffer[i + 3]]);
-      pOutBuffer[j] = (uint8_t)(dwLimb >> 16) & 0xff;
-      pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 8) & 0xff;
-      pOutBuffer[j + 2] = (uint8_t)(dwLimb)&0xff;
-      j += 3;
-    }
-  }
-  FX_Free(pBuffer);
-  return j;
-}
-
-FXCODEC_IMAGE_TYPE XFA_GetImageType(const WideString& wsType) {
-  WideString wsContentType(wsType);
-  wsContentType.MakeLower();
-  if (wsContentType == L"image/jpg")
-    return FXCODEC_IMAGE_JPG;
-  if (wsContentType == L"image/png")
-    return FXCODEC_IMAGE_PNG;
-  if (wsContentType == L"image/gif")
-    return FXCODEC_IMAGE_GIF;
-  if (wsContentType == L"image/bmp")
-    return FXCODEC_IMAGE_BMP;
-  if (wsContentType == L"image/tif")
-    return FXCODEC_IMAGE_TIF;
-  return FXCODEC_IMAGE_UNKNOWN;
-}
-
-RetainPtr<CFX_DIBitmap> XFA_LoadImageData(CXFA_FFDoc* pDoc,
-                                          CXFA_Image* pImage,
-                                          bool& bNameImage,
-                                          int32_t& iImageXDpi,
-                                          int32_t& iImageYDpi) {
-  WideString wsHref = pImage->GetHref();
-  WideString wsImage = pImage->GetContent();
-  if (wsHref.IsEmpty() && wsImage.IsEmpty())
-    return nullptr;
-
-  FXCODEC_IMAGE_TYPE type = XFA_GetImageType(pImage->GetContentType());
-  ByteString bsContent;
-  uint8_t* pImageBuffer = nullptr;
-  RetainPtr<IFX_SeekableReadStream> pImageFileRead;
-  if (wsImage.GetLength() > 0) {
-    XFA_AttributeEnum iEncoding = pImage->GetTransferEncoding();
-    if (iEncoding == XFA_AttributeEnum::Base64) {
-      ByteString bsData = wsImage.UTF8Encode();
-      int32_t iLength = bsData.GetLength();
-      pImageBuffer = FX_Alloc(uint8_t, iLength);
-      int32_t iRead = XFA_Base64Decode(bsData.c_str(), pImageBuffer);
-      if (iRead > 0) {
-        pImageFileRead =
-            pdfium::MakeRetain<CFX_MemoryStream>(pImageBuffer, iRead, false);
-      }
-    } else {
-      bsContent = ByteString::FromUnicode(wsImage);
-      pImageFileRead = pdfium::MakeRetain<CFX_MemoryStream>(
-          const_cast<uint8_t*>(bsContent.raw_str()), bsContent.GetLength(),
-          false);
-    }
-  } else {
-    WideString wsURL = wsHref;
-    if (wsURL.Left(7) != L"http://" && wsURL.Left(6) != L"ftp://") {
-      RetainPtr<CFX_DIBitmap> pBitmap =
-          pDoc->GetPDFNamedImage(wsURL.AsStringView(), iImageXDpi, iImageYDpi);
-      if (pBitmap) {
-        bNameImage = true;
-        return pBitmap;
-      }
-    }
-    pImageFileRead = pDoc->GetDocEnvironment()->OpenLinkedFile(pDoc, wsURL);
-  }
-  if (!pImageFileRead) {
-    FX_Free(pImageBuffer);
-    return nullptr;
-  }
-  bNameImage = false;
-  RetainPtr<CFX_DIBitmap> pBitmap =
-      XFA_LoadImageFromBuffer(pImageFileRead, type, iImageXDpi, iImageYDpi);
-  FX_Free(pImageBuffer);
-  return pBitmap;
-}
-
-class CXFA_TextLayoutData : public CXFA_WidgetLayoutData {
- public:
-  CXFA_TextLayoutData() {}
-  ~CXFA_TextLayoutData() override {}
-
-  CXFA_TextLayout* GetTextLayout() const { return m_pTextLayout.get(); }
-  CXFA_TextProvider* GetTextProvider() const { return m_pTextProvider.get(); }
-
-  void LoadText(CXFA_FFDoc* doc, CXFA_WidgetAcc* pAcc) {
-    if (m_pTextLayout)
-      return;
-
-    m_pTextProvider =
-        pdfium::MakeUnique<CXFA_TextProvider>(pAcc, XFA_TEXTPROVIDERTYPE_Text);
-    m_pTextLayout =
-        pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pTextProvider.get());
-  }
-
- private:
-  std::unique_ptr<CXFA_TextLayout> m_pTextLayout;
-  std::unique_ptr<CXFA_TextProvider> m_pTextProvider;
-};
-
-class CXFA_ImageLayoutData : public CXFA_WidgetLayoutData {
- public:
-  CXFA_ImageLayoutData()
-      : m_bNamedImage(false), m_iImageXDpi(0), m_iImageYDpi(0) {}
-
-  ~CXFA_ImageLayoutData() override {}
-
-  bool LoadImageData(CXFA_FFDoc* doc, CXFA_WidgetAcc* pAcc) {
-    if (m_pDIBitmap)
-      return true;
-
-    CXFA_Value* value = pAcc->GetNode()->GetFormValueIfExists();
-    if (!value)
-      return false;
-
-    CXFA_Image* image = value->GetImageIfExists();
-    if (!image)
-      return false;
-
-    pAcc->SetImageImage(XFA_LoadImageData(doc, image, m_bNamedImage,
-                                          m_iImageXDpi, m_iImageYDpi));
-    return !!m_pDIBitmap;
-  }
-
-  RetainPtr<CFX_DIBitmap> m_pDIBitmap;
-  bool m_bNamedImage;
-  int32_t m_iImageXDpi;
-  int32_t m_iImageYDpi;
-};
-
-class CXFA_FieldLayoutData : public CXFA_WidgetLayoutData {
- public:
-  CXFA_FieldLayoutData() {}
-  ~CXFA_FieldLayoutData() override {}
-
-  bool LoadCaption(CXFA_FFDoc* doc, CXFA_WidgetAcc* pAcc) {
-    if (m_pCapTextLayout)
-      return true;
-    CXFA_Caption* caption = pAcc->GetNode()->GetCaptionIfExists();
-    if (!caption || caption->IsHidden())
-      return false;
-
-    m_pCapTextProvider = pdfium::MakeUnique<CXFA_TextProvider>(
-        pAcc, XFA_TEXTPROVIDERTYPE_Caption);
-    m_pCapTextLayout =
-        pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pCapTextProvider.get());
-    return true;
-  }
-
-  std::unique_ptr<CXFA_TextLayout> m_pCapTextLayout;
-  std::unique_ptr<CXFA_TextProvider> m_pCapTextProvider;
-  std::unique_ptr<CFDE_TextOut> m_pTextOut;
-  std::vector<float> m_FieldSplitArray;
-};
-
-class CXFA_TextEditData : public CXFA_FieldLayoutData {};
-
-class CXFA_ImageEditData : public CXFA_FieldLayoutData {
- public:
-  CXFA_ImageEditData()
-      : m_bNamedImage(false), m_iImageXDpi(0), m_iImageYDpi(0) {}
-
-  ~CXFA_ImageEditData() override {}
-
-  bool LoadImageData(CXFA_FFDoc* doc, CXFA_WidgetAcc* pAcc) {
-    if (m_pDIBitmap)
-      return true;
-
-    CXFA_Value* value = pAcc->GetNode()->GetFormValueIfExists();
-    if (!value)
-      return false;
-
-    CXFA_Image* image = value->GetImageIfExists();
-    if (!image)
-      return false;
-
-    pAcc->SetImageEditImage(XFA_LoadImageData(doc, image, m_bNamedImage,
-                                              m_iImageXDpi, m_iImageYDpi));
-    return !!m_pDIBitmap;
-  }
-
-  RetainPtr<CFX_DIBitmap> m_pDIBitmap;
-  bool m_bNamedImage;
-  int32_t m_iImageXDpi;
-  int32_t m_iImageYDpi;
-};
-
-float GetEdgeThickness(const std::vector<CXFA_Stroke*>& strokes,
-                       bool b3DStyle,
-                       int32_t nIndex) {
-  float fThickness = 0;
-
-  CXFA_Stroke* stroke = strokes[nIndex * 2 + 1];
-  if (stroke->IsVisible()) {
-    if (nIndex == 0)
-      fThickness += 2.5f;
-
-    fThickness += stroke->GetThickness() * (b3DStyle ? 4 : 2);
-  }
-  return fThickness;
-}
-
-bool SplitDateTime(const WideString& wsDateTime,
-                   WideString& wsDate,
-                   WideString& wsTime) {
-  wsDate = L"";
-  wsTime = L"";
-  if (wsDateTime.IsEmpty())
-    return false;
-
-  auto nSplitIndex = wsDateTime.Find('T');
-  if (!nSplitIndex.has_value())
-    nSplitIndex = wsDateTime.Find(' ');
-  if (!nSplitIndex.has_value())
-    return false;
-
-  wsDate = wsDateTime.Left(nSplitIndex.value());
-  if (!wsDate.IsEmpty()) {
-    if (!std::any_of(wsDate.begin(), wsDate.end(), std::iswdigit))
-      return false;
-  }
-  wsTime = wsDateTime.Right(wsDateTime.GetLength() - nSplitIndex.value() - 1);
-  if (!wsTime.IsEmpty()) {
-    if (!std::any_of(wsTime.begin(), wsTime.end(), std::iswdigit))
-      return false;
-  }
-  return true;
-}
-
-std::pair<XFA_Element, CXFA_Node*> CreateUIChild(CXFA_Node* pNode) {
-  XFA_Element eType = pNode->GetElementType();
-  XFA_Element eWidgetType = eType;
-  if (eType != XFA_Element::Field && eType != XFA_Element::Draw)
-    return {eWidgetType, nullptr};
-
-  eWidgetType = XFA_Element::Unknown;
-  XFA_Element eUIType = XFA_Element::Unknown;
-  auto* defValue =
-      pNode->JSObject()->GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
-  XFA_Element eValueType =
-      defValue ? defValue->GetChildValueClassID() : XFA_Element::Unknown;
-  switch (eValueType) {
-    case XFA_Element::Boolean:
-      eUIType = XFA_Element::CheckButton;
-      break;
-    case XFA_Element::Integer:
-    case XFA_Element::Decimal:
-    case XFA_Element::Float:
-      eUIType = XFA_Element::NumericEdit;
-      break;
-    case XFA_Element::ExData:
-    case XFA_Element::Text:
-      eUIType = XFA_Element::TextEdit;
-      eWidgetType = XFA_Element::Text;
-      break;
-    case XFA_Element::Date:
-    case XFA_Element::Time:
-    case XFA_Element::DateTime:
-      eUIType = XFA_Element::DateTimeEdit;
-      break;
-    case XFA_Element::Image:
-      eUIType = XFA_Element::ImageEdit;
-      eWidgetType = XFA_Element::Image;
-      break;
-    case XFA_Element::Arc:
-    case XFA_Element::Line:
-    case XFA_Element::Rectangle:
-      eUIType = XFA_Element::DefaultUi;
-      eWidgetType = eValueType;
-      break;
-    default:
-      break;
-  }
-
-  CXFA_Node* pUIChild = nullptr;
-  CXFA_Ui* pUI =
-      pNode->JSObject()->GetOrCreateProperty<CXFA_Ui>(0, XFA_Element::Ui);
-  CXFA_Node* pChild = pUI ? pUI->GetFirstChild() : nullptr;
-  for (; pChild; pChild = pChild->GetNextSibling()) {
-    XFA_Element eChildType = pChild->GetElementType();
-    if (eChildType == XFA_Element::Extras ||
-        eChildType == XFA_Element::Picture) {
-      continue;
-    }
-
-    auto node = CXFA_Node::Create(pChild->GetDocument(), XFA_Element::Ui,
-                                  XFA_PacketType::Form);
-    if (node && node->HasPropertyFlags(eChildType, XFA_PROPERTYFLAG_OneOf)) {
-      pUIChild = pChild;
-      break;
-    }
-  }
-
-  if (eType == XFA_Element::Draw) {
-    XFA_Element eDraw =
-        pUIChild ? pUIChild->GetElementType() : XFA_Element::Unknown;
-    switch (eDraw) {
-      case XFA_Element::TextEdit:
-        eWidgetType = XFA_Element::Text;
-        break;
-      case XFA_Element::ImageEdit:
-        eWidgetType = XFA_Element::Image;
-        break;
-      default:
-        eWidgetType = eWidgetType == XFA_Element::Unknown ? XFA_Element::Text
-                                                          : eWidgetType;
-        break;
-    }
-  } else {
-    if (pUIChild && pUIChild->GetElementType() == XFA_Element::DefaultUi) {
-      eWidgetType = XFA_Element::TextEdit;
-    } else {
-      eWidgetType =
-          pUIChild ? pUIChild->GetElementType()
-                   : (eUIType == XFA_Element::Unknown ? XFA_Element::TextEdit
-                                                      : eUIType);
-    }
-  }
-
-  if (!pUIChild) {
-    if (eUIType == XFA_Element::Unknown) {
-      eUIType = XFA_Element::TextEdit;
-      if (defValue) {
-        defValue->JSObject()->GetOrCreateProperty<CXFA_Text>(0,
-                                                             XFA_Element::Text);
-      }
-    }
-    return {eWidgetType,
-            pUI ? pUI->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eUIType)
-                : nullptr};
-  }
-
-  if (eUIType != XFA_Element::Unknown)
-    return {eWidgetType, pUIChild};
-
-  switch (pUIChild->GetElementType()) {
-    case XFA_Element::CheckButton: {
-      eValueType = XFA_Element::Text;
-      if (CXFA_Items* pItems =
-              pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false)) {
-        if (CXFA_Node* pItem =
-                pItems->GetChild<CXFA_Node>(0, XFA_Element::Unknown, false)) {
-          eValueType = pItem->GetElementType();
-        }
-      }
-      break;
-    }
-    case XFA_Element::DateTimeEdit:
-      eValueType = XFA_Element::DateTime;
-      break;
-    case XFA_Element::ImageEdit:
-      eValueType = XFA_Element::Image;
-      break;
-    case XFA_Element::NumericEdit:
-      eValueType = XFA_Element::Float;
-      break;
-    case XFA_Element::ChoiceList: {
-      eValueType = (pUIChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
-                    XFA_AttributeEnum::MultiSelect)
-                       ? XFA_Element::ExData
-                       : XFA_Element::Text;
-      break;
-    }
-    case XFA_Element::Barcode:
-    case XFA_Element::Button:
-    case XFA_Element::PasswordEdit:
-    case XFA_Element::Signature:
-    case XFA_Element::TextEdit:
-    default:
-      eValueType = XFA_Element::Text;
-      break;
-  }
-  if (defValue)
-    defValue->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eValueType);
-
-  return {eWidgetType, pUIChild};
-}
-
-}  // namespace
-
-CXFA_WidgetAcc::CXFA_WidgetAcc(CXFA_Node* pNode)
-    : m_bIsNull(true),
-      m_bPreNull(true),
-      m_pUiChildNode(nullptr),
-      m_eUIType(XFA_Element::Unknown),
-      m_pNode(pNode) {}
-
-CXFA_WidgetAcc::~CXFA_WidgetAcc() = default;
-
-void CXFA_WidgetAcc::ResetData() {
-  WideString wsValue;
-  XFA_Element eUIType = GetUIType();
-  switch (eUIType) {
-    case XFA_Element::ImageEdit: {
-      CXFA_Value* imageValue = m_pNode->GetDefaultValueIfExists();
-      CXFA_Image* image = imageValue ? imageValue->GetImageIfExists() : nullptr;
-      WideString wsContentType, wsHref;
-      if (image) {
-        wsValue = image->GetContent();
-        wsContentType = image->GetContentType();
-        wsHref = image->GetHref();
-      }
-      SetImageEdit(wsContentType, wsHref, wsValue);
-      break;
-    }
-    case XFA_Element::ExclGroup: {
-      CXFA_Node* pNextChild = m_pNode->GetFirstContainerChild();
-      while (pNextChild) {
-        CXFA_Node* pChild = pNextChild;
-        CXFA_WidgetAcc* pAcc = pChild->GetWidgetAcc();
-        if (!pAcc)
-          continue;
-
-        bool done = false;
-        if (wsValue.IsEmpty()) {
-          CXFA_Value* defValue = pAcc->GetNode()->GetDefaultValueIfExists();
-          if (defValue) {
-            wsValue = defValue->GetChildValueContent();
-            SetValue(XFA_VALUEPICTURE_Raw, wsValue);
-            pAcc->SetValue(XFA_VALUEPICTURE_Raw, wsValue);
-            done = true;
-          }
-        }
-        if (!done) {
-          CXFA_Items* pItems =
-              pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-          if (!pItems)
-            continue;
-
-          WideString itemText;
-          if (pItems->CountChildren(XFA_Element::Unknown, false) > 1) {
-            itemText =
-                pItems->GetChild<CXFA_Node>(1, XFA_Element::Unknown, false)
-                    ->JSObject()
-                    ->GetContent(false);
-          }
-          pAcc->SetValue(XFA_VALUEPICTURE_Raw, itemText);
-        }
-        pNextChild = pChild->GetNextContainerSibling();
-      }
-      break;
-    }
-    case XFA_Element::ChoiceList:
-      ClearAllSelections();
-    default: {
-      CXFA_Value* defValue = m_pNode->GetDefaultValueIfExists();
-      if (defValue)
-        wsValue = defValue->GetChildValueContent();
-
-      SetValue(XFA_VALUEPICTURE_Raw, wsValue);
-      break;
-    }
-  }
-}
-
-void CXFA_WidgetAcc::SetImageEdit(const WideString& wsContentType,
-                                  const WideString& wsHref,
-                                  const WideString& wsData) {
-  CXFA_Value* formValue = m_pNode->GetFormValueIfExists();
-  CXFA_Image* image = formValue ? formValue->GetImageIfExists() : nullptr;
-  if (image) {
-    image->SetContentType(WideString(wsContentType));
-    image->SetHref(wsHref);
-  }
-
-  m_pNode->JSObject()->SetContent(wsData, GetFormatDataValue(wsData), true,
-                                  false, true);
-
-  CXFA_Node* pBind = m_pNode->GetBindData();
-  if (!pBind) {
-    if (image)
-      image->SetTransferEncoding(XFA_AttributeEnum::Base64);
-    return;
-  }
-  pBind->JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false,
-                              false);
-  CXFA_Node* pHrefNode = pBind->GetFirstChild();
-  if (pHrefNode) {
-    pHrefNode->JSObject()->SetCData(XFA_Attribute::Value, wsHref, false, false);
-  } else {
-    CFX_XMLNode* pXMLNode = pBind->GetXMLMappingNode();
-    ASSERT(pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element);
-    static_cast<CFX_XMLElement*>(pXMLNode)->SetString(L"href", wsHref);
-  }
-}
-
-CXFA_FFWidget* CXFA_WidgetAcc::GetNextWidget(CXFA_FFWidget* pWidget) {
-  return static_cast<CXFA_FFWidget*>(pWidget->GetNext());
-}
-
-void CXFA_WidgetAcc::UpdateUIDisplay(CXFA_FFDocView* docView,
-                                     CXFA_FFWidget* pExcept) {
-  CXFA_FFWidget* pWidget = docView->GetWidgetForNode(m_pNode);
-  for (; pWidget; pWidget = GetNextWidget(pWidget)) {
-    if (pWidget == pExcept || !pWidget->IsLoaded() ||
-        (GetUIType() != XFA_Element::CheckButton && pWidget->IsFocused())) {
-      continue;
-    }
-    pWidget->UpdateFWLData();
-    pWidget->AddInvalidateRect();
-  }
-}
-
-void CXFA_WidgetAcc::CalcCaptionSize(CXFA_FFDoc* doc, CFX_SizeF& szCap) {
-  CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
-  if (!caption || !caption->IsVisible())
-    return;
-
-  LoadCaption(doc);
-
-  XFA_Element eUIType = GetUIType();
-  XFA_AttributeEnum iCapPlacement = caption->GetPlacementType();
-  float fCapReserve = caption->GetReserve();
-  const bool bVert = iCapPlacement == XFA_AttributeEnum::Top ||
-                     iCapPlacement == XFA_AttributeEnum::Bottom;
-  const bool bReserveExit = fCapReserve > 0.01;
-  CXFA_TextLayout* pCapTextLayout =
-      static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get())
-          ->m_pCapTextLayout.get();
-  if (pCapTextLayout) {
-    if (!bVert && eUIType != XFA_Element::Button)
-      szCap.width = fCapReserve;
-
-    CFX_SizeF minSize;
-    szCap = pCapTextLayout->CalcSize(minSize, szCap);
-    if (bReserveExit)
-      bVert ? szCap.height = fCapReserve : szCap.width = fCapReserve;
-  } else {
-    float fFontSize = 10.0f;
-    CXFA_Font* font = caption->GetFontIfExists();
-    if (font) {
-      fFontSize = font->GetFontSize();
-    } else {
-      CXFA_Font* widgetfont = m_pNode->GetFontIfExists();
-      if (widgetfont)
-        fFontSize = widgetfont->GetFontSize();
-    }
-
-    if (bVert) {
-      szCap.height = fCapReserve > 0 ? fCapReserve : fFontSize;
-    } else {
-      szCap.width = fCapReserve > 0 ? fCapReserve : 0;
-      szCap.height = fFontSize;
-    }
-  }
-
-  CXFA_Margin* captionMargin = caption->GetMarginIfExists();
-  if (!captionMargin)
-    return;
-
-  float fLeftInset = captionMargin->GetLeftInset();
-  float fTopInset = captionMargin->GetTopInset();
-  float fRightInset = captionMargin->GetRightInset();
-  float fBottomInset = captionMargin->GetBottomInset();
-  if (bReserveExit) {
-    bVert ? (szCap.width += fLeftInset + fRightInset)
-          : (szCap.height += fTopInset + fBottomInset);
-  } else {
-    szCap.width += fLeftInset + fRightInset;
-    szCap.height += fTopInset + fBottomInset;
-  }
-}
-
-bool CXFA_WidgetAcc::CalculateFieldAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) {
-  CFX_SizeF szCap;
-  CalcCaptionSize(doc, szCap);
-
-  CFX_RectF rtUIMargin = GetUIMargin();
-  size.width += rtUIMargin.left + rtUIMargin.width;
-  size.height += rtUIMargin.top + rtUIMargin.height;
-  if (szCap.width > 0 && szCap.height > 0) {
-    CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
-    XFA_AttributeEnum placement = caption ? caption->GetPlacementType()
-                                          : CXFA_Caption::kDefaultPlacementType;
-    switch (placement) {
-      case XFA_AttributeEnum::Left:
-      case XFA_AttributeEnum::Right:
-      case XFA_AttributeEnum::Inline: {
-        size.width += szCap.width;
-        size.height = std::max(size.height, szCap.height);
-      } break;
-      case XFA_AttributeEnum::Top:
-      case XFA_AttributeEnum::Bottom: {
-        size.height += szCap.height;
-        size.width = std::max(size.width, szCap.width);
-      }
-      default:
-        break;
-    }
-  }
-  return CalculateWidgetAutoSize(size);
-}
-
-bool CXFA_WidgetAcc::CalculateWidgetAutoSize(CFX_SizeF& size) {
-  CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin) {
-    size.width += margin->GetLeftInset() + margin->GetRightInset();
-    size.height += margin->GetTopInset() + margin->GetBottomInset();
-  }
-
-  CXFA_Para* para = m_pNode->GetParaIfExists();
-  if (para)
-    size.width += para->GetMarginLeft() + para->GetTextIndent();
-
-  Optional<float> width = m_pNode->TryWidth();
-  if (width) {
-    size.width = *width;
-  } else {
-    Optional<float> min = m_pNode->TryMinWidth();
-    if (min)
-      size.width = std::max(size.width, *min);
-
-    Optional<float> max = m_pNode->TryMaxWidth();
-    if (max && *max > 0)
-      size.width = std::min(size.width, *max);
-  }
-
-  Optional<float> height = m_pNode->TryHeight();
-  if (height) {
-    size.height = *height;
-  } else {
-    Optional<float> min = m_pNode->TryMinHeight();
-    if (min)
-      size.height = std::max(size.height, *min);
-
-    Optional<float> max = m_pNode->TryMaxHeight();
-    if (max && *max > 0)
-      size.height = std::min(size.height, *max);
-  }
-  return true;
-}
-
-void CXFA_WidgetAcc::CalculateTextContentSize(CXFA_FFDoc* doc,
-                                              CFX_SizeF& size) {
-  float fFontSize = m_pNode->GetFontSize();
-  WideString wsText = GetValue(XFA_VALUEPICTURE_Display);
-  if (wsText.IsEmpty()) {
-    size.height += fFontSize;
-    return;
-  }
-
-  wchar_t wcEnter = '\n';
-  wchar_t wsLast = wsText[wsText.GetLength() - 1];
-  if (wsLast == wcEnter)
-    wsText = wsText + wcEnter;
-
-  CXFA_FieldLayoutData* layoutData =
-      static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get());
-  if (!layoutData->m_pTextOut) {
-    layoutData->m_pTextOut = pdfium::MakeUnique<CFDE_TextOut>();
-    CFDE_TextOut* pTextOut = layoutData->m_pTextOut.get();
-    pTextOut->SetFont(GetFDEFont(doc));
-    pTextOut->SetFontSize(fFontSize);
-    pTextOut->SetLineBreakTolerance(fFontSize * 0.2f);
-    pTextOut->SetLineSpace(m_pNode->GetLineHeight());
-
-    FDE_TextStyle dwStyles;
-    dwStyles.last_line_height_ = true;
-    if (GetUIType() == XFA_Element::TextEdit && IsMultiLine())
-      dwStyles.line_wrap_ = true;
-
-    pTextOut->SetStyles(dwStyles);
-  }
-  layoutData->m_pTextOut->CalcLogicSize(wsText, size);
-}
-
-bool CXFA_WidgetAcc::CalculateTextEditAutoSize(CXFA_FFDoc* doc,
-                                               CFX_SizeF& size) {
-  if (size.width > 0) {
-    CFX_SizeF szOrz = size;
-    CFX_SizeF szCap;
-    CalcCaptionSize(doc, szCap);
-    bool bCapExit = szCap.width > 0.01 && szCap.height > 0.01;
-    XFA_AttributeEnum iCapPlacement = XFA_AttributeEnum::Unknown;
-    if (bCapExit) {
-      CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
-      iCapPlacement = caption ? caption->GetPlacementType()
-                              : CXFA_Caption::kDefaultPlacementType;
-      switch (iCapPlacement) {
-        case XFA_AttributeEnum::Left:
-        case XFA_AttributeEnum::Right:
-        case XFA_AttributeEnum::Inline: {
-          size.width -= szCap.width;
-        }
-        default:
-          break;
-      }
-    }
-    CFX_RectF rtUIMargin = GetUIMargin();
-    size.width -= rtUIMargin.left + rtUIMargin.width;
-    CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-    if (margin)
-      size.width -= margin->GetLeftInset() + margin->GetRightInset();
-
-    CalculateTextContentSize(doc, size);
-    size.height += rtUIMargin.top + rtUIMargin.height;
-    if (bCapExit) {
-      switch (iCapPlacement) {
-        case XFA_AttributeEnum::Left:
-        case XFA_AttributeEnum::Right:
-        case XFA_AttributeEnum::Inline: {
-          size.height = std::max(size.height, szCap.height);
-        } break;
-        case XFA_AttributeEnum::Top:
-        case XFA_AttributeEnum::Bottom: {
-          size.height += szCap.height;
-        }
-        default:
-          break;
-      }
-    }
-    size.width = szOrz.width;
-    return CalculateWidgetAutoSize(size);
-  }
-  CalculateTextContentSize(doc, size);
-  return CalculateFieldAutoSize(doc, size);
-}
-
-bool CXFA_WidgetAcc::CalculateCheckButtonAutoSize(CXFA_FFDoc* doc,
-                                                  CFX_SizeF& size) {
-  float fCheckSize = GetCheckButtonSize();
-  size = CFX_SizeF(fCheckSize, fCheckSize);
-  return CalculateFieldAutoSize(doc, size);
-}
-
-bool CXFA_WidgetAcc::CalculatePushButtonAutoSize(CXFA_FFDoc* doc,
-                                                 CFX_SizeF& size) {
-  CalcCaptionSize(doc, size);
-  return CalculateWidgetAutoSize(size);
-}
-
-CFX_SizeF CXFA_WidgetAcc::CalculateImageSize(float img_width,
-                                             float img_height,
-                                             float dpi_x,
-                                             float dpi_y) {
-  CFX_RectF rtImage(0, 0, XFA_UnitPx2Pt(img_width, dpi_x),
-                    XFA_UnitPx2Pt(img_height, dpi_y));
-
-  CFX_RectF rtFit;
-  Optional<float> width = m_pNode->TryWidth();
-  if (width) {
-    rtFit.width = *width;
-    GetWidthWithoutMargin(rtFit.width);
-  } else {
-    rtFit.width = rtImage.width;
-  }
-
-  Optional<float> height = m_pNode->TryHeight();
-  if (height) {
-    rtFit.height = *height;
-    GetHeightWithoutMargin(rtFit.height);
-  } else {
-    rtFit.height = rtImage.height;
-  }
-
-  return rtFit.Size();
-}
-
-bool CXFA_WidgetAcc::CalculateImageAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) {
-  if (!GetImageImage())
-    LoadImageImage(doc);
-
-  size.clear();
-  RetainPtr<CFX_DIBitmap> pBitmap = GetImageImage();
-  if (!pBitmap)
-    return CalculateWidgetAutoSize(size);
-
-  int32_t iImageXDpi = 0;
-  int32_t iImageYDpi = 0;
-  GetImageDpi(iImageXDpi, iImageYDpi);
-
-  size = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
-                            iImageXDpi, iImageYDpi);
-  return CalculateWidgetAutoSize(size);
-}
-
-bool CXFA_WidgetAcc::CalculateImageEditAutoSize(CXFA_FFDoc* doc,
-                                                CFX_SizeF& size) {
-  if (!GetImageEditImage())
-    LoadImageEditImage(doc);
-
-  size.clear();
-  RetainPtr<CFX_DIBitmap> pBitmap = GetImageEditImage();
-  if (!pBitmap)
-    return CalculateFieldAutoSize(doc, size);
-
-  int32_t iImageXDpi = 0;
-  int32_t iImageYDpi = 0;
-  GetImageEditDpi(iImageXDpi, iImageYDpi);
-
-  size = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
-                            iImageXDpi, iImageYDpi);
-  return CalculateFieldAutoSize(doc, size);
-}
-
-bool CXFA_WidgetAcc::LoadImageImage(CXFA_FFDoc* doc) {
-  InitLayoutData();
-  return static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get())
-      ->LoadImageData(doc, this);
-}
-
-bool CXFA_WidgetAcc::LoadImageEditImage(CXFA_FFDoc* doc) {
-  InitLayoutData();
-  return static_cast<CXFA_ImageEditData*>(m_pLayoutData.get())
-      ->LoadImageData(doc, this);
-}
-
-void CXFA_WidgetAcc::GetImageDpi(int32_t& iImageXDpi, int32_t& iImageYDpi) {
-  CXFA_ImageLayoutData* pData =
-      static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get());
-  iImageXDpi = pData->m_iImageXDpi;
-  iImageYDpi = pData->m_iImageYDpi;
-}
-
-void CXFA_WidgetAcc::GetImageEditDpi(int32_t& iImageXDpi, int32_t& iImageYDpi) {
-  CXFA_ImageEditData* pData =
-      static_cast<CXFA_ImageEditData*>(m_pLayoutData.get());
-  iImageXDpi = pData->m_iImageXDpi;
-  iImageYDpi = pData->m_iImageYDpi;
-}
-
-void CXFA_WidgetAcc::LoadText(CXFA_FFDoc* doc) {
-  InitLayoutData();
-  static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get())->LoadText(doc, this);
-}
-
-float CXFA_WidgetAcc::CalculateWidgetAutoWidth(float fWidthCalc) {
-  CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    fWidthCalc += margin->GetLeftInset() + margin->GetRightInset();
-
-  Optional<float> min = m_pNode->TryMinWidth();
-  if (min)
-    fWidthCalc = std::max(fWidthCalc, *min);
-
-  Optional<float> max = m_pNode->TryMaxWidth();
-  if (max && *max > 0)
-    fWidthCalc = std::min(fWidthCalc, *max);
-
-  return fWidthCalc;
-}
-
-float CXFA_WidgetAcc::GetWidthWithoutMargin(float fWidthCalc) {
-  CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    fWidthCalc -= margin->GetLeftInset() + margin->GetRightInset();
-  return fWidthCalc;
-}
-
-float CXFA_WidgetAcc::CalculateWidgetAutoHeight(float fHeightCalc) {
-  CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    fHeightCalc += margin->GetTopInset() + margin->GetBottomInset();
-
-  Optional<float> min = m_pNode->TryMinHeight();
-  if (min)
-    fHeightCalc = std::max(fHeightCalc, *min);
-
-  Optional<float> max = m_pNode->TryMaxHeight();
-  if (max && *max > 0)
-    fHeightCalc = std::min(fHeightCalc, *max);
-
-  return fHeightCalc;
-}
-
-float CXFA_WidgetAcc::GetHeightWithoutMargin(float fHeightCalc) {
-  CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-  if (margin)
-    fHeightCalc -= margin->GetTopInset() + margin->GetBottomInset();
-  return fHeightCalc;
-}
-
-void CXFA_WidgetAcc::StartWidgetLayout(CXFA_FFDoc* doc,
-                                       float& fCalcWidth,
-                                       float& fCalcHeight) {
-  InitLayoutData();
-
-  XFA_Element eUIType = GetUIType();
-  if (eUIType == XFA_Element::Text) {
-    m_pLayoutData->m_fWidgetHeight = m_pNode->TryHeight().value_or(-1);
-    StartTextLayout(doc, fCalcWidth, fCalcHeight);
-    return;
-  }
-  if (fCalcWidth > 0 && fCalcHeight > 0)
-    return;
-
-  m_pLayoutData->m_fWidgetHeight = -1;
-  float fWidth = 0;
-  if (fCalcWidth > 0 && fCalcHeight < 0) {
-    Optional<float> height = m_pNode->TryHeight();
-    if (height)
-      fCalcHeight = *height;
-    else
-      CalculateAccWidthAndHeight(doc, eUIType, fCalcWidth, fCalcHeight);
-
-    m_pLayoutData->m_fWidgetHeight = fCalcHeight;
-    return;
-  }
-  if (fCalcWidth < 0 && fCalcHeight < 0) {
-    Optional<float> height;
-    Optional<float> width = m_pNode->TryWidth();
-    if (width) {
-      fWidth = *width;
-
-      height = m_pNode->TryHeight();
-      if (height)
-        fCalcHeight = *height;
-    }
-    if (!width || !height)
-      CalculateAccWidthAndHeight(doc, eUIType, fWidth, fCalcHeight);
-
-    fCalcWidth = fWidth;
-  }
-  m_pLayoutData->m_fWidgetHeight = fCalcHeight;
-}
-
-void CXFA_WidgetAcc::CalculateAccWidthAndHeight(CXFA_FFDoc* doc,
-                                                XFA_Element eUIType,
-                                                float& fWidth,
-                                                float& fCalcHeight) {
-  CFX_SizeF sz(fWidth, m_pLayoutData->m_fWidgetHeight);
-  switch (eUIType) {
-    case XFA_Element::Barcode:
-    case XFA_Element::ChoiceList:
-    case XFA_Element::Signature:
-      CalculateFieldAutoSize(doc, sz);
-      break;
-    case XFA_Element::ImageEdit:
-      CalculateImageEditAutoSize(doc, sz);
-      break;
-    case XFA_Element::Button:
-      CalculatePushButtonAutoSize(doc, sz);
-      break;
-    case XFA_Element::CheckButton:
-      CalculateCheckButtonAutoSize(doc, sz);
-      break;
-    case XFA_Element::DateTimeEdit:
-    case XFA_Element::NumericEdit:
-    case XFA_Element::PasswordEdit:
-    case XFA_Element::TextEdit:
-      CalculateTextEditAutoSize(doc, sz);
-      break;
-    case XFA_Element::Image:
-      CalculateImageAutoSize(doc, sz);
-      break;
-    case XFA_Element::Arc:
-    case XFA_Element::Line:
-    case XFA_Element::Rectangle:
-    case XFA_Element::Subform:
-    case XFA_Element::ExclGroup:
-      CalculateWidgetAutoSize(sz);
-      break;
-    default:
-      break;
-  }
-  fWidth = sz.width;
-  m_pLayoutData->m_fWidgetHeight = sz.height;
-  fCalcHeight = sz.height;
-}
-
-bool CXFA_WidgetAcc::FindSplitPos(CXFA_FFDocView* docView,
-                                  int32_t iBlockIndex,
-                                  float& fCalcHeight) {
-  XFA_Element eUIType = GetUIType();
-  if (eUIType == XFA_Element::Subform)
-    return false;
-
-  if (eUIType != XFA_Element::Text && eUIType != XFA_Element::TextEdit &&
-      eUIType != XFA_Element::NumericEdit &&
-      eUIType != XFA_Element::PasswordEdit) {
-    fCalcHeight = 0;
-    return true;
-  }
-
-  float fTopInset = 0;
-  float fBottomInset = 0;
-  if (iBlockIndex == 0) {
-    CXFA_Margin* margin = m_pNode->GetMarginIfExists();
-    if (margin) {
-      fTopInset = margin->GetTopInset();
-      fBottomInset = margin->GetBottomInset();
-    }
-
-    CFX_RectF rtUIMargin = GetUIMargin();
-    fTopInset += rtUIMargin.top;
-    fBottomInset += rtUIMargin.width;
-  }
-  if (eUIType == XFA_Element::Text) {
-    float fHeight = fCalcHeight;
-    if (iBlockIndex == 0) {
-      fCalcHeight = fCalcHeight - fTopInset;
-      if (fCalcHeight < 0)
-        fCalcHeight = 0;
-    }
-
-    CXFA_TextLayout* pTextLayout =
-        static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get())->GetTextLayout();
-    fCalcHeight =
-        pTextLayout->DoLayout(iBlockIndex, fCalcHeight, fCalcHeight,
-                              m_pLayoutData->m_fWidgetHeight - fTopInset);
-    if (fCalcHeight != 0) {
-      if (iBlockIndex == 0)
-        fCalcHeight = fCalcHeight + fTopInset;
-      if (fabs(fHeight - fCalcHeight) < XFA_FLOAT_PERCISION)
-        return false;
-    }
-    return true;
-  }
-  XFA_AttributeEnum iCapPlacement = XFA_AttributeEnum::Unknown;
-  float fCapReserve = 0;
-  if (iBlockIndex == 0) {
-    CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
-    if (caption && !caption->IsHidden()) {
-      iCapPlacement = caption->GetPlacementType();
-      fCapReserve = caption->GetReserve();
-    }
-    if (iCapPlacement == XFA_AttributeEnum::Top &&
-        fCalcHeight < fCapReserve + fTopInset) {
-      fCalcHeight = 0;
-      return true;
-    }
-    if (iCapPlacement == XFA_AttributeEnum::Bottom &&
-        m_pLayoutData->m_fWidgetHeight - fCapReserve - fBottomInset) {
-      fCalcHeight = 0;
-      return true;
-    }
-    if (iCapPlacement != XFA_AttributeEnum::Top)
-      fCapReserve = 0;
-  }
-  CXFA_FieldLayoutData* pFieldData =
-      static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get());
-  int32_t iLinesCount = 0;
-  float fHeight = m_pLayoutData->m_fWidgetHeight;
-  if (GetValue(XFA_VALUEPICTURE_Display).IsEmpty()) {
-    iLinesCount = 1;
-  } else {
-    if (!pFieldData->m_pTextOut) {
-      // TODO(dsinclair): Inline fWidth when the 2nd param of
-      // CalculateAccWidthAndHeight isn't a ref-param.
-      float fWidth = m_pNode->TryWidth().value_or(0);
-      CalculateAccWidthAndHeight(docView->GetDoc(), eUIType, fWidth, fHeight);
-    }
-    iLinesCount = pFieldData->m_pTextOut->GetTotalLines();
-  }
-  std::vector<float>* pFieldArray = &pFieldData->m_FieldSplitArray;
-  int32_t iFieldSplitCount = pdfium::CollectionSize<int32_t>(*pFieldArray);
-  for (int32_t i = 0; i < iBlockIndex * 3; i += 3) {
-    iLinesCount -= (int32_t)(*pFieldArray)[i + 1];
-    fHeight -= (*pFieldArray)[i + 2];
-  }
-  if (iLinesCount == 0)
-    return false;
-
-  float fLineHeight = m_pNode->GetLineHeight();
-  float fFontSize = m_pNode->GetFontSize();
-  float fTextHeight = iLinesCount * fLineHeight - fLineHeight + fFontSize;
-  float fSpaceAbove = 0;
-  float fStartOffset = 0;
-  if (fHeight > 0.1f && iBlockIndex == 0) {
-    fStartOffset = fTopInset;
-    fHeight -= (fTopInset + fBottomInset);
-    CXFA_Para* para = m_pNode->GetParaIfExists();
-    if (para) {
-      fSpaceAbove = para->GetSpaceAbove();
-      float fSpaceBelow = para->GetSpaceBelow();
-      fHeight -= (fSpaceAbove + fSpaceBelow);
-      switch (para->GetVerticalAlign()) {
-        case XFA_AttributeEnum::Top:
-          fStartOffset += fSpaceAbove;
-          break;
-        case XFA_AttributeEnum::Middle:
-          fStartOffset += ((fHeight - fTextHeight) / 2 + fSpaceAbove);
-          break;
-        case XFA_AttributeEnum::Bottom:
-          fStartOffset += (fHeight - fTextHeight + fSpaceAbove);
-          break;
-        default:
-          NOTREACHED();
-          break;
-      }
-    }
-    if (fStartOffset < 0.1f)
-      fStartOffset = 0;
-  }
-  for (int32_t i = iBlockIndex - 1; iBlockIndex > 0 && i < iBlockIndex; i++) {
-    fStartOffset = (*pFieldArray)[i * 3] - (*pFieldArray)[i * 3 + 2];
-    if (fStartOffset < 0.1f)
-      fStartOffset = 0;
-  }
-  if (iFieldSplitCount / 3 == (iBlockIndex + 1))
-    (*pFieldArray)[0] = fStartOffset;
-  else
-    pFieldArray->push_back(fStartOffset);
-
-  XFA_VERSION version = docView->GetDoc()->GetXFADoc()->GetCurVersionMode();
-  bool bCanSplitNoContent = false;
-  XFA_AttributeEnum eLayoutMode = GetNode()
-                                      ->GetParent()
-                                      ->JSObject()
-                                      ->TryEnum(XFA_Attribute::Layout, true)
-                                      .value_or(XFA_AttributeEnum::Position);
-  if ((eLayoutMode == XFA_AttributeEnum::Position ||
-       eLayoutMode == XFA_AttributeEnum::Tb ||
-       eLayoutMode == XFA_AttributeEnum::Row ||
-       eLayoutMode == XFA_AttributeEnum::Table) &&
-      version > XFA_VERSION_208) {
-    bCanSplitNoContent = true;
-  }
-  if ((eLayoutMode == XFA_AttributeEnum::Tb ||
-       eLayoutMode == XFA_AttributeEnum::Row ||
-       eLayoutMode == XFA_AttributeEnum::Table) &&
-      version <= XFA_VERSION_208) {
-    if (fStartOffset < fCalcHeight) {
-      bCanSplitNoContent = true;
-    } else {
-      fCalcHeight = 0;
-      return true;
-    }
-  }
-  if (bCanSplitNoContent) {
-    if ((fCalcHeight - fTopInset - fSpaceAbove < fLineHeight)) {
-      fCalcHeight = 0;
-      return true;
-    }
-    if (fStartOffset + XFA_FLOAT_PERCISION >= fCalcHeight) {
-      if (iFieldSplitCount / 3 == (iBlockIndex + 1)) {
-        (*pFieldArray)[iBlockIndex * 3 + 1] = 0;
-        (*pFieldArray)[iBlockIndex * 3 + 2] = fCalcHeight;
-      } else {
-        pFieldArray->push_back(0);
-        pFieldArray->push_back(fCalcHeight);
-      }
-      return false;
-    }
-    if (fCalcHeight - fStartOffset < fLineHeight) {
-      fCalcHeight = fStartOffset;
-      if (iFieldSplitCount / 3 == (iBlockIndex + 1)) {
-        (*pFieldArray)[iBlockIndex * 3 + 1] = 0;
-        (*pFieldArray)[iBlockIndex * 3 + 2] = fCalcHeight;
-      } else {
-        pFieldArray->push_back(0);
-        pFieldArray->push_back(fCalcHeight);
-      }
-      return true;
-    }
-    float fTextNum =
-        fCalcHeight + XFA_FLOAT_PERCISION - fCapReserve - fStartOffset;
-    int32_t iLineNum =
-        (int32_t)((fTextNum + (fLineHeight - fFontSize)) / fLineHeight);
-    if (iLineNum >= iLinesCount) {
-      if (fCalcHeight - fStartOffset - fTextHeight >= fFontSize) {
-        if (iFieldSplitCount / 3 == (iBlockIndex + 1)) {
-          (*pFieldArray)[iBlockIndex * 3 + 1] = (float)iLinesCount;
-          (*pFieldArray)[iBlockIndex * 3 + 2] = fCalcHeight;
-        } else {
-          pFieldArray->push_back((float)iLinesCount);
-          pFieldArray->push_back(fCalcHeight);
-        }
-        return false;
-      }
-      if (fHeight - fStartOffset - fTextHeight < fFontSize) {
-        iLineNum -= 1;
-        if (iLineNum == 0) {
-          fCalcHeight = 0;
-          return true;
-        }
-      } else {
-        iLineNum = (int32_t)(fTextNum / fLineHeight);
-      }
-    }
-    if (iLineNum > 0) {
-      float fSplitHeight = iLineNum * fLineHeight + fCapReserve + fStartOffset;
-      if (iFieldSplitCount / 3 == (iBlockIndex + 1)) {
-        (*pFieldArray)[iBlockIndex * 3 + 1] = (float)iLineNum;
-        (*pFieldArray)[iBlockIndex * 3 + 2] = fSplitHeight;
-      } else {
-        pFieldArray->push_back((float)iLineNum);
-        pFieldArray->push_back(fSplitHeight);
-      }
-      if (fabs(fSplitHeight - fCalcHeight) < XFA_FLOAT_PERCISION)
-        return false;
-
-      fCalcHeight = fSplitHeight;
-      return true;
-    }
-  }
-  fCalcHeight = 0;
-  return true;
-}
-
-void CXFA_WidgetAcc::InitLayoutData() {
-  if (m_pLayoutData)
-    return;
-
-  switch (GetUIType()) {
-    case XFA_Element::Text:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_TextLayoutData>();
-      return;
-    case XFA_Element::TextEdit:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_TextEditData>();
-      return;
-    case XFA_Element::Image:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_ImageLayoutData>();
-      return;
-    case XFA_Element::ImageEdit:
-      m_pLayoutData = pdfium::MakeUnique<CXFA_ImageEditData>();
-      return;
-    default:
-      break;
-  }
-  if (m_pNode && m_pNode->GetElementType() == XFA_Element::Field) {
-    m_pLayoutData = pdfium::MakeUnique<CXFA_FieldLayoutData>();
-    return;
-  }
-  m_pLayoutData = pdfium::MakeUnique<CXFA_WidgetLayoutData>();
-}
-
-void CXFA_WidgetAcc::StartTextLayout(CXFA_FFDoc* doc,
-                                     float& fCalcWidth,
-                                     float& fCalcHeight) {
-  LoadText(doc);
-
-  CXFA_TextLayout* pTextLayout =
-      static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get())->GetTextLayout();
-  float fTextHeight = 0;
-  if (fCalcWidth > 0 && fCalcHeight > 0) {
-    float fWidth = GetWidthWithoutMargin(fCalcWidth);
-    pTextLayout->StartLayout(fWidth);
-    fTextHeight = fCalcHeight;
-    fTextHeight = GetHeightWithoutMargin(fTextHeight);
-    pTextLayout->DoLayout(0, fTextHeight, -1, fTextHeight);
-    return;
-  }
-  if (fCalcWidth > 0 && fCalcHeight < 0) {
-    float fWidth = GetWidthWithoutMargin(fCalcWidth);
-    pTextLayout->StartLayout(fWidth);
-  }
-
-  if (fCalcWidth < 0 && fCalcHeight < 0) {
-    Optional<float> width = m_pNode->TryWidth();
-    if (width) {
-      pTextLayout->StartLayout(GetWidthWithoutMargin(*width));
-      fCalcWidth = *width;
-    } else {
-      float fMaxWidth = CalculateWidgetAutoWidth(pTextLayout->StartLayout(-1));
-      pTextLayout->StartLayout(GetWidthWithoutMargin(fMaxWidth));
-      fCalcWidth = fMaxWidth;
-    }
-  }
-
-  if (m_pLayoutData->m_fWidgetHeight < 0) {
-    m_pLayoutData->m_fWidgetHeight = pTextLayout->GetLayoutHeight();
-    m_pLayoutData->m_fWidgetHeight =
-        CalculateWidgetAutoHeight(m_pLayoutData->m_fWidgetHeight);
-  }
-  fTextHeight = m_pLayoutData->m_fWidgetHeight;
-  fTextHeight = GetHeightWithoutMargin(fTextHeight);
-  pTextLayout->DoLayout(0, fTextHeight, -1, fTextHeight);
-  fCalcHeight = m_pLayoutData->m_fWidgetHeight;
-}
-
-bool CXFA_WidgetAcc::LoadCaption(CXFA_FFDoc* doc) {
-  InitLayoutData();
-  return static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get())
-      ->LoadCaption(doc, this);
-}
-
-CXFA_TextLayout* CXFA_WidgetAcc::GetCaptionTextLayout() {
-  return m_pLayoutData
-             ? static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get())
-                   ->m_pCapTextLayout.get()
-             : nullptr;
-}
-
-CXFA_TextLayout* CXFA_WidgetAcc::GetTextLayout() {
-  return m_pLayoutData
-             ? static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get())
-                   ->GetTextLayout()
-             : nullptr;
-}
-
-RetainPtr<CFX_DIBitmap> CXFA_WidgetAcc::GetImageImage() {
-  return m_pLayoutData
-             ? static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get())
-                   ->m_pDIBitmap
-             : nullptr;
-}
-
-RetainPtr<CFX_DIBitmap> CXFA_WidgetAcc::GetImageEditImage() {
-  return m_pLayoutData
-             ? static_cast<CXFA_ImageEditData*>(m_pLayoutData.get())
-                   ->m_pDIBitmap
-             : nullptr;
-}
-
-void CXFA_WidgetAcc::SetImageImage(const RetainPtr<CFX_DIBitmap>& newImage) {
-  CXFA_ImageLayoutData* pData =
-      static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get());
-  if (pData->m_pDIBitmap != newImage)
-    pData->m_pDIBitmap = newImage;
-}
-
-void CXFA_WidgetAcc::SetImageEditImage(
-    const RetainPtr<CFX_DIBitmap>& newImage) {
-  CXFA_ImageEditData* pData =
-      static_cast<CXFA_ImageEditData*>(m_pLayoutData.get());
-  if (pData->m_pDIBitmap != newImage)
-    pData->m_pDIBitmap = newImage;
-}
-
-RetainPtr<CFGAS_GEFont> CXFA_WidgetAcc::GetFDEFont(CXFA_FFDoc* doc) {
-  WideString wsFontName = L"Courier";
-  uint32_t dwFontStyle = 0;
-  CXFA_Font* font = m_pNode->GetFontIfExists();
-  if (font) {
-    if (font->IsBold())
-      dwFontStyle |= FXFONT_BOLD;
-    if (font->IsItalic())
-      dwFontStyle |= FXFONT_ITALIC;
-
-    wsFontName = font->GetTypeface();
-  }
-  return doc->GetApp()->GetXFAFontMgr()->GetFont(doc, wsFontName.AsStringView(),
-                                                 dwFontStyle);
-}
-
-CXFA_Node* CXFA_WidgetAcc::GetUIChild() {
-  if (m_eUIType == XFA_Element::Unknown)
-    std::tie(m_eUIType, m_pUiChildNode) = CreateUIChild(m_pNode);
-  return m_pUiChildNode;
-}
-
-XFA_Element CXFA_WidgetAcc::GetUIType() {
-  GetUIChild();
-  return m_eUIType;
-}
-
-bool CXFA_WidgetAcc::IsOpenAccess() const {
-  return m_pNode && m_pNode->IsOpenAccess();
-}
-
-std::vector<CXFA_Event*> CXFA_WidgetAcc::GetEventByActivity(
-    XFA_AttributeEnum iActivity,
-    bool bIsFormReady) {
-  std::vector<CXFA_Event*> events;
-  for (CXFA_Node* node : m_pNode->GetNodeList(0, XFA_Element::Event)) {
-    auto* event = static_cast<CXFA_Event*>(node);
-    if (event->GetActivity() == iActivity) {
-      if (iActivity == XFA_AttributeEnum::Ready) {
-        WideString wsRef = event->GetRef();
-        if (bIsFormReady) {
-          if (wsRef == WideStringView(L"$form"))
-            events.push_back(event);
-        } else {
-          if (wsRef == WideStringView(L"$layout"))
-            events.push_back(event);
-        }
-      } else {
-        events.push_back(event);
-      }
-    }
-  }
-  return events;
-}
-
-CXFA_Border* CXFA_WidgetAcc::GetUIBorder() {
-  CXFA_Node* pUIChild = GetUIChild();
-  return pUIChild ? pUIChild->JSObject()->GetProperty<CXFA_Border>(
-                        0, XFA_Element::Border)
-                  : nullptr;
-}
-
-CFX_RectF CXFA_WidgetAcc::GetUIMargin() {
-  CXFA_Node* pUIChild = GetUIChild();
-  CXFA_Margin* mgUI = nullptr;
-  if (pUIChild) {
-    mgUI =
-        pUIChild->JSObject()->GetProperty<CXFA_Margin>(0, XFA_Element::Margin);
-  }
-
-  if (!mgUI)
-    return CFX_RectF();
-
-  CXFA_Border* border = GetUIBorder();
-  if (border && border->GetPresence() != XFA_AttributeEnum::Visible)
-    return CFX_RectF();
-
-  Optional<float> left = mgUI->TryLeftInset();
-  Optional<float> top = mgUI->TryTopInset();
-  Optional<float> right = mgUI->TryRightInset();
-  Optional<float> bottom = mgUI->TryBottomInset();
-  if (border) {
-    bool bVisible = false;
-    float fThickness = 0;
-    XFA_AttributeEnum iType = XFA_AttributeEnum::Unknown;
-    std::tie(iType, bVisible, fThickness) = border->Get3DStyle();
-    if (!left || !top || !right || !bottom) {
-      std::vector<CXFA_Stroke*> strokes = border->GetStrokes();
-      if (!top)
-        top = GetEdgeThickness(strokes, bVisible, 0);
-      if (!right)
-        right = GetEdgeThickness(strokes, bVisible, 1);
-      if (!bottom)
-        bottom = GetEdgeThickness(strokes, bVisible, 2);
-      if (!left)
-        left = GetEdgeThickness(strokes, bVisible, 3);
-    }
-  }
-  return CFX_RectF(left.value_or(0.0), top.value_or(0.0), right.value_or(0.0),
-                   bottom.value_or(0.0));
-}
-
-XFA_AttributeEnum CXFA_WidgetAcc::GetButtonHighlight() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild)
-    return pUIChild->JSObject()->GetEnum(XFA_Attribute::Highlight);
-  return XFA_AttributeEnum::Inverted;
-}
-
-bool CXFA_WidgetAcc::HasButtonRollover() const {
-  CXFA_Items* pItems =
-      m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-  if (!pItems)
-    return false;
-
-  for (CXFA_Node* pText = pItems->GetFirstChild(); pText;
-       pText = pText->GetNextSibling()) {
-    if (pText->JSObject()->GetCData(XFA_Attribute::Name) == L"rollover")
-      return !pText->JSObject()->GetContent(false).IsEmpty();
-  }
-  return false;
-}
-
-bool CXFA_WidgetAcc::HasButtonDown() const {
-  CXFA_Items* pItems =
-      m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-  if (!pItems)
-    return false;
-
-  for (CXFA_Node* pText = pItems->GetFirstChild(); pText;
-       pText = pText->GetNextSibling()) {
-    if (pText->JSObject()->GetCData(XFA_Attribute::Name) == L"down")
-      return !pText->JSObject()->GetContent(false).IsEmpty();
-  }
-  return false;
-}
-
-bool CXFA_WidgetAcc::IsCheckButtonRound() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild)
-    return pUIChild->JSObject()->GetEnum(XFA_Attribute::Shape) ==
-           XFA_AttributeEnum::Round;
-  return false;
-}
-
-XFA_AttributeEnum CXFA_WidgetAcc::GetCheckButtonMark() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild)
-    return pUIChild->JSObject()->GetEnum(XFA_Attribute::Mark);
-  return XFA_AttributeEnum::Default;
-}
-
-bool CXFA_WidgetAcc::IsRadioButton() {
-  CXFA_Node* pParent = m_pNode->GetParent();
-  return pParent && pParent->GetElementType() == XFA_Element::ExclGroup;
-}
-
-float CXFA_WidgetAcc::GetCheckButtonSize() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild) {
-    return pUIChild->JSObject()
-        ->GetMeasure(XFA_Attribute::Size)
-        .ToUnit(XFA_Unit::Pt);
-  }
-  return CXFA_Measurement(10, XFA_Unit::Pt).ToUnit(XFA_Unit::Pt);
-}
-
-bool CXFA_WidgetAcc::IsAllowNeutral() {
-  CXFA_Node* pUIChild = GetUIChild();
-  return pUIChild &&
-         pUIChild->JSObject()->GetBoolean(XFA_Attribute::AllowNeutral);
-}
-
-XFA_CHECKSTATE CXFA_WidgetAcc::GetCheckState() {
-  WideString wsValue = m_pNode->GetRawValue();
-  if (wsValue.IsEmpty())
-    return XFA_CHECKSTATE_Off;
-
-  auto* pItems = m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-  if (!pItems)
-    return XFA_CHECKSTATE_Off;
-
-  CXFA_Node* pText = pItems->GetFirstChild();
-  int32_t i = 0;
-  while (pText) {
-    Optional<WideString> wsContent = pText->JSObject()->TryContent(false, true);
-    if (wsContent && *wsContent == wsValue)
-      return static_cast<XFA_CHECKSTATE>(i);
-
-    i++;
-    pText = pText->GetNextSibling();
-  }
-  return XFA_CHECKSTATE_Off;
-}
-
-void CXFA_WidgetAcc::SetCheckState(XFA_CHECKSTATE eCheckState, bool bNotify) {
-  CXFA_Node* node = m_pNode->GetExclGroupIfExists();
-  if (!node) {
-    CXFA_Items* pItems =
-        m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-    if (!pItems)
-      return;
-
-    int32_t i = -1;
-    CXFA_Node* pText = pItems->GetFirstChild();
-    WideString wsContent;
-    while (pText) {
-      i++;
-      if (i == eCheckState) {
-        wsContent = pText->JSObject()->GetContent(false);
-        break;
-      }
-      pText = pText->GetNextSibling();
-    }
-    if (m_pNode)
-      m_pNode->SyncValue(wsContent, bNotify);
-
-    return;
-  }
-
-  WideString wsValue;
-  if (eCheckState != XFA_CHECKSTATE_Off) {
-    if (CXFA_Items* pItems =
-            m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false)) {
-      CXFA_Node* pText = pItems->GetFirstChild();
-      if (pText)
-        wsValue = pText->JSObject()->GetContent(false);
-    }
-  }
-  CXFA_Node* pChild = node->GetFirstChild();
-  for (; pChild; pChild = pChild->GetNextSibling()) {
-    if (pChild->GetElementType() != XFA_Element::Field)
-      continue;
-
-    CXFA_Items* pItem =
-        pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-    if (!pItem)
-      continue;
-
-    CXFA_Node* pItemchild = pItem->GetFirstChild();
-    if (!pItemchild)
-      continue;
-
-    WideString text = pItemchild->JSObject()->GetContent(false);
-    WideString wsChildValue = text;
-    if (wsValue != text) {
-      pItemchild = pItemchild->GetNextSibling();
-      if (pItemchild)
-        wsChildValue = pItemchild->JSObject()->GetContent(false);
-      else
-        wsChildValue.clear();
-    }
-    pChild->SyncValue(wsChildValue, bNotify);
-  }
-  node->SyncValue(wsValue, bNotify);
-}
-
-CXFA_Node* CXFA_WidgetAcc::GetSelectedMember() {
-  CXFA_Node* pSelectedMember = nullptr;
-  WideString wsState = m_pNode->GetRawValue();
-  if (wsState.IsEmpty())
-    return pSelectedMember;
-
-  for (CXFA_Node* pNode = ToNode(m_pNode->GetFirstChild()); pNode;
-       pNode = pNode->GetNextSibling()) {
-    CXFA_WidgetAcc widgetData(pNode);
-    if (widgetData.GetCheckState() == XFA_CHECKSTATE_On) {
-      pSelectedMember = pNode;
-      break;
-    }
-  }
-  return pSelectedMember;
-}
-
-CXFA_Node* CXFA_WidgetAcc::SetSelectedMember(const WideStringView& wsName,
-                                             bool bNotify) {
-  uint32_t nameHash = FX_HashCode_GetW(wsName, false);
-  for (CXFA_Node* pNode = ToNode(m_pNode->GetFirstChild()); pNode;
-       pNode = pNode->GetNextSibling()) {
-    if (pNode->GetNameHash() == nameHash) {
-      CXFA_WidgetAcc widgetData(pNode);
-      widgetData.SetCheckState(XFA_CHECKSTATE_On, bNotify);
-      return pNode;
-    }
-  }
-  return nullptr;
-}
-
-void CXFA_WidgetAcc::SetSelectedMemberByValue(const WideStringView& wsValue,
-                                              bool bNotify,
-                                              bool bScriptModify,
-                                              bool bSyncData) {
-  WideString wsExclGroup;
-  for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode;
-       pNode = pNode->GetNextSibling()) {
-    if (pNode->GetElementType() != XFA_Element::Field)
-      continue;
-
-    CXFA_Items* pItem =
-        pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-    if (!pItem)
-      continue;
-
-    CXFA_Node* pItemchild = pItem->GetFirstChild();
-    if (!pItemchild)
-      continue;
-
-    WideString wsChildValue = pItemchild->JSObject()->GetContent(false);
-    if (wsValue != wsChildValue) {
-      pItemchild = pItemchild->GetNextSibling();
-      if (pItemchild)
-        wsChildValue = pItemchild->JSObject()->GetContent(false);
-      else
-        wsChildValue.clear();
-    } else {
-      wsExclGroup = wsValue;
-    }
-    pNode->JSObject()->SetContent(wsChildValue, wsChildValue, bNotify,
-                                  bScriptModify, false);
-  }
-  if (m_pNode) {
-    m_pNode->JSObject()->SetContent(wsExclGroup, wsExclGroup, bNotify,
-                                    bScriptModify, bSyncData);
-  }
-}
-
-CXFA_Node* CXFA_WidgetAcc::GetExclGroupFirstMember() {
-  CXFA_Node* pExcl = GetNode();
-  if (!pExcl)
-    return nullptr;
-
-  CXFA_Node* pNode = pExcl->GetFirstChild();
-  while (pNode) {
-    if (pNode->GetElementType() == XFA_Element::Field)
-      return pNode;
-
-    pNode = pNode->GetNextSibling();
-  }
-  return nullptr;
-}
-CXFA_Node* CXFA_WidgetAcc::GetExclGroupNextMember(CXFA_Node* pNode) {
-  if (!pNode)
-    return nullptr;
-
-  CXFA_Node* pNodeField = pNode->GetNextSibling();
-  while (pNodeField) {
-    if (pNodeField->GetElementType() == XFA_Element::Field)
-      return pNodeField;
-
-    pNodeField = pNodeField->GetNextSibling();
-  }
-  return nullptr;
-}
-
-bool CXFA_WidgetAcc::IsChoiceListCommitOnSelect() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild) {
-    return pUIChild->JSObject()->GetEnum(XFA_Attribute::CommitOn) ==
-           XFA_AttributeEnum::Select;
-  }
-  return true;
-}
-
-bool CXFA_WidgetAcc::IsChoiceListAllowTextEntry() {
-  CXFA_Node* pUIChild = GetUIChild();
-  return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::TextEntry);
-}
-
-bool CXFA_WidgetAcc::IsChoiceListMultiSelect() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild) {
-    return pUIChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
-           XFA_AttributeEnum::MultiSelect;
-  }
-  return false;
-}
-
-bool CXFA_WidgetAcc::IsListBox() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (!pUIChild)
-    return false;
-
-  XFA_AttributeEnum attr = pUIChild->JSObject()->GetEnum(XFA_Attribute::Open);
-  return attr == XFA_AttributeEnum::Always ||
-         attr == XFA_AttributeEnum::MultiSelect;
-}
-
-int32_t CXFA_WidgetAcc::CountChoiceListItems(bool bSaveValue) {
-  std::vector<CXFA_Node*> pItems;
-  int32_t iCount = 0;
-  for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode;
-       pNode = pNode->GetNextSibling()) {
-    if (pNode->GetElementType() != XFA_Element::Items)
-      continue;
-    iCount++;
-    pItems.push_back(pNode);
-    if (iCount == 2)
-      break;
-  }
-  if (iCount == 0)
-    return 0;
-
-  CXFA_Node* pItem = pItems[0];
-  if (iCount > 1) {
-    bool bItemOneHasSave =
-        pItems[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
-    bool bItemTwoHasSave =
-        pItems[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
-    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave)
-      pItem = pItems[1];
-  }
-  return pItem->CountChildren(XFA_Element::Unknown, false);
-}
-
-Optional<WideString> CXFA_WidgetAcc::GetChoiceListItem(int32_t nIndex,
-                                                       bool bSaveValue) {
-  std::vector<CXFA_Node*> pItemsArray;
-  int32_t iCount = 0;
-  for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode;
-       pNode = pNode->GetNextSibling()) {
-    if (pNode->GetElementType() != XFA_Element::Items)
-      continue;
-
-    ++iCount;
-    pItemsArray.push_back(pNode);
-    if (iCount == 2)
-      break;
-  }
-  if (iCount == 0)
-    return {};
-
-  CXFA_Node* pItems = pItemsArray[0];
-  if (iCount > 1) {
-    bool bItemOneHasSave =
-        pItemsArray[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
-    bool bItemTwoHasSave =
-        pItemsArray[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
-    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave)
-      pItems = pItemsArray[1];
-  }
-  if (!pItems)
-    return {};
-
-  CXFA_Node* pItem =
-      pItems->GetChild<CXFA_Node>(nIndex, XFA_Element::Unknown, false);
-  if (pItem)
-    return {pItem->JSObject()->GetContent(false)};
-  return {};
-}
-
-std::vector<WideString> CXFA_WidgetAcc::GetChoiceListItems(bool bSaveValue) {
-  std::vector<CXFA_Node*> items;
-  for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode && items.size() < 2;
-       pNode = pNode->GetNextSibling()) {
-    if (pNode->GetElementType() == XFA_Element::Items)
-      items.push_back(pNode);
-  }
-  if (items.empty())
-    return std::vector<WideString>();
-
-  CXFA_Node* pItem = items.front();
-  if (items.size() > 1) {
-    bool bItemOneHasSave =
-        items[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
-    bool bItemTwoHasSave =
-        items[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
-    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave)
-      pItem = items[1];
-  }
-
-  std::vector<WideString> wsTextArray;
-  for (CXFA_Node* pNode = pItem->GetFirstChild(); pNode;
-       pNode = pNode->GetNextSibling()) {
-    wsTextArray.emplace_back(pNode->JSObject()->GetContent(false));
-  }
-  return wsTextArray;
-}
-
-int32_t CXFA_WidgetAcc::CountSelectedItems() {
-  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
-  if (IsListBox() || !IsChoiceListAllowTextEntry())
-    return pdfium::CollectionSize<int32_t>(wsValueArray);
-
-  int32_t iSelected = 0;
-  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-  for (const auto& value : wsValueArray) {
-    if (pdfium::ContainsValue(wsSaveTextArray, value))
-      iSelected++;
-  }
-  return iSelected;
-}
-
-int32_t CXFA_WidgetAcc::GetSelectedItem(int32_t nIndex) {
-  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
-  if (!pdfium::IndexInBounds(wsValueArray, nIndex))
-    return -1;
-
-  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-  auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(),
-                      wsValueArray[nIndex]);
-  return it != wsSaveTextArray.end() ? it - wsSaveTextArray.begin() : -1;
-}
-
-std::vector<int32_t> CXFA_WidgetAcc::GetSelectedItems() {
-  std::vector<int32_t> iSelArray;
-  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
-  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-  for (const auto& value : wsValueArray) {
-    auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(), value);
-    if (it != wsSaveTextArray.end())
-      iSelArray.push_back(it - wsSaveTextArray.begin());
-  }
-  return iSelArray;
-}
-
-std::vector<WideString> CXFA_WidgetAcc::GetSelectedItemsValue() {
-  std::vector<WideString> wsSelTextArray;
-  WideString wsValue = m_pNode->GetRawValue();
-  if (IsChoiceListMultiSelect()) {
-    if (!wsValue.IsEmpty()) {
-      size_t iStart = 0;
-      size_t iLength = wsValue.GetLength();
-      auto iEnd = wsValue.Find(L'\n', iStart);
-      iEnd = (!iEnd.has_value()) ? iLength : iEnd;
-      while (iEnd >= iStart) {
-        wsSelTextArray.push_back(wsValue.Mid(iStart, iEnd.value() - iStart));
-        iStart = iEnd.value() + 1;
-        if (iStart >= iLength)
-          break;
-        iEnd = wsValue.Find(L'\n', iStart);
-        if (!iEnd.has_value())
-          wsSelTextArray.push_back(wsValue.Mid(iStart, iLength - iStart));
-      }
-    }
-  } else {
-    wsSelTextArray.push_back(wsValue);
-  }
-  return wsSelTextArray;
-}
-
-bool CXFA_WidgetAcc::GetItemState(int32_t nIndex) {
-  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-  return pdfium::IndexInBounds(wsSaveTextArray, nIndex) &&
-         pdfium::ContainsValue(GetSelectedItemsValue(),
-                               wsSaveTextArray[nIndex]);
-}
-
-void CXFA_WidgetAcc::SetItemState(int32_t nIndex,
-                                  bool bSelected,
-                                  bool bNotify,
-                                  bool bScriptModify,
-                                  bool bSyncData) {
-  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-  if (!pdfium::IndexInBounds(wsSaveTextArray, nIndex))
-    return;
-
-  int32_t iSel = -1;
-  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
-  auto it = std::find(wsValueArray.begin(), wsValueArray.end(),
-                      wsSaveTextArray[nIndex]);
-  if (it != wsValueArray.end())
-    iSel = it - wsValueArray.begin();
-
-  if (IsChoiceListMultiSelect()) {
-    if (bSelected) {
-      if (iSel < 0) {
-        WideString wsValue = m_pNode->GetRawValue();
-        if (!wsValue.IsEmpty()) {
-          wsValue += L"\n";
-        }
-        wsValue += wsSaveTextArray[nIndex];
-        m_pNode->JSObject()->SetContent(wsValue, wsValue, bNotify,
-                                        bScriptModify, bSyncData);
-      }
-    } else if (iSel >= 0) {
-      std::vector<int32_t> iSelArray = GetSelectedItems();
-      auto it = std::find(iSelArray.begin(), iSelArray.end(), nIndex);
-      if (it != iSelArray.end())
-        iSelArray.erase(it);
-      SetSelectedItems(iSelArray, bNotify, bScriptModify, bSyncData);
-    }
-  } else {
-    if (bSelected) {
-      if (iSel < 0) {
-        WideString wsSaveText = wsSaveTextArray[nIndex];
-        m_pNode->JSObject()->SetContent(wsSaveText,
-                                        GetFormatDataValue(wsSaveText), bNotify,
-                                        bScriptModify, bSyncData);
-      }
-    } else if (iSel >= 0) {
-      m_pNode->JSObject()->SetContent(WideString(), WideString(), bNotify,
-                                      bScriptModify, bSyncData);
-    }
-  }
-}
-
-void CXFA_WidgetAcc::SetSelectedItems(const std::vector<int32_t>& iSelArray,
-                                      bool bNotify,
-                                      bool bScriptModify,
-                                      bool bSyncData) {
-  WideString wsValue;
-  int32_t iSize = pdfium::CollectionSize<int32_t>(iSelArray);
-  if (iSize >= 1) {
-    std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
-    WideString wsItemValue;
-    for (int32_t i = 0; i < iSize; i++) {
-      wsItemValue = (iSize == 1) ? wsSaveTextArray[iSelArray[i]]
-                                 : wsSaveTextArray[iSelArray[i]] + L"\n";
-      wsValue += wsItemValue;
-    }
-  }
-  WideString wsFormat(wsValue);
-  if (!IsChoiceListMultiSelect())
-    wsFormat = GetFormatDataValue(wsValue);
-
-  m_pNode->JSObject()->SetContent(wsValue, wsFormat, bNotify, bScriptModify,
-                                  bSyncData);
-}
-
-void CXFA_WidgetAcc::ClearAllSelections() {
-  CXFA_Node* pBind = m_pNode->GetBindData();
-  if (!pBind || !IsChoiceListMultiSelect()) {
-    m_pNode->SyncValue(WideString(), false);
-    return;
-  }
-
-  while (CXFA_Node* pChildNode = pBind->GetFirstChild())
-    pBind->RemoveChild(pChildNode, true);
-}
-
-void CXFA_WidgetAcc::InsertItem(const WideString& wsLabel,
-                                const WideString& wsValue,
-                                bool bNotify) {
-  int32_t nIndex = -1;
-  WideString wsNewValue(wsValue);
-  if (wsNewValue.IsEmpty())
-    wsNewValue = wsLabel;
-
-  std::vector<CXFA_Node*> listitems;
-  for (CXFA_Node* pItem = m_pNode->GetFirstChild(); pItem;
-       pItem = pItem->GetNextSibling()) {
-    if (pItem->GetElementType() == XFA_Element::Items)
-      listitems.push_back(pItem);
-  }
-  if (listitems.empty()) {
-    CXFA_Node* pItems = m_pNode->CreateSamePacketNode(XFA_Element::Items);
-    m_pNode->InsertChild(-1, pItems);
-    InsertListTextItem(pItems, wsLabel, nIndex);
-    CXFA_Node* pSaveItems = m_pNode->CreateSamePacketNode(XFA_Element::Items);
-    m_pNode->InsertChild(-1, pSaveItems);
-    pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false);
-    InsertListTextItem(pSaveItems, wsNewValue, nIndex);
-  } else if (listitems.size() > 1) {
-    for (int32_t i = 0; i < 2; i++) {
-      CXFA_Node* pNode = listitems[i];
-      bool bHasSave = pNode->JSObject()->GetBoolean(XFA_Attribute::Save);
-      if (bHasSave)
-        InsertListTextItem(pNode, wsNewValue, nIndex);
-      else
-        InsertListTextItem(pNode, wsLabel, nIndex);
-    }
-  } else {
-    CXFA_Node* pNode = listitems[0];
-    pNode->JSObject()->SetBoolean(XFA_Attribute::Save, false, false);
-    pNode->JSObject()->SetEnum(XFA_Attribute::Presence,
-                               XFA_AttributeEnum::Visible, false);
-    CXFA_Node* pSaveItems = m_pNode->CreateSamePacketNode(XFA_Element::Items);
-    m_pNode->InsertChild(-1, pSaveItems);
-    pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false);
-    pSaveItems->JSObject()->SetEnum(XFA_Attribute::Presence,
-                                    XFA_AttributeEnum::Hidden, false);
-    CXFA_Node* pListNode = pNode->GetFirstChild();
-    int32_t i = 0;
-    while (pListNode) {
-      InsertListTextItem(pSaveItems, pListNode->JSObject()->GetContent(false),
-                         i);
-      ++i;
-
-      pListNode = pListNode->GetNextSibling();
-    }
-    InsertListTextItem(pNode, wsLabel, nIndex);
-    InsertListTextItem(pSaveItems, wsNewValue, nIndex);
-  }
-  if (!bNotify)
-    return;
-
-  m_pNode->GetDocument()->GetNotify()->OnWidgetListItemAdded(
-      this, wsLabel.c_str(), wsValue.c_str(), nIndex);
-}
-
-void CXFA_WidgetAcc::GetItemLabel(const WideStringView& wsValue,
-                                  WideString& wsLabel) {
-  int32_t iCount = 0;
-  std::vector<CXFA_Node*> listitems;
-  CXFA_Node* pItems = m_pNode->GetFirstChild();
-  for (; pItems; pItems = pItems->GetNextSibling()) {
-    if (pItems->GetElementType() != XFA_Element::Items)
-      continue;
-    iCount++;
-    listitems.push_back(pItems);
-  }
-
-  if (iCount <= 1) {
-    wsLabel = wsValue;
-    return;
-  }
-
-  CXFA_Node* pLabelItems = listitems[0];
-  bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save);
-  CXFA_Node* pSaveItems = nullptr;
-  if (bSave) {
-    pSaveItems = pLabelItems;
-    pLabelItems = listitems[1];
-  } else {
-    pSaveItems = listitems[1];
-  }
-  iCount = 0;
-
-  int32_t iSearch = -1;
-  for (CXFA_Node* pChildItem = pSaveItems->GetFirstChild(); pChildItem;
-       pChildItem = pChildItem->GetNextSibling()) {
-    if (pChildItem->JSObject()->GetContent(false) == wsValue) {
-      iSearch = iCount;
-      break;
-    }
-    iCount++;
-  }
-  if (iSearch < 0)
-    return;
-
-  CXFA_Node* pText =
-      pLabelItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false);
-  if (pText)
-    wsLabel = pText->JSObject()->GetContent(false);
-}
-
-WideString CXFA_WidgetAcc::GetItemValue(const WideStringView& wsLabel) {
-  int32_t iCount = 0;
-  std::vector<CXFA_Node*> listitems;
-  for (CXFA_Node* pItems = m_pNode->GetFirstChild(); pItems;
-       pItems = pItems->GetNextSibling()) {
-    if (pItems->GetElementType() != XFA_Element::Items)
-      continue;
-    iCount++;
-    listitems.push_back(pItems);
-  }
-  if (iCount <= 1)
-    return WideString(wsLabel);
-
-  CXFA_Node* pLabelItems = listitems[0];
-  bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save);
-  CXFA_Node* pSaveItems = nullptr;
-  if (bSave) {
-    pSaveItems = pLabelItems;
-    pLabelItems = listitems[1];
-  } else {
-    pSaveItems = listitems[1];
-  }
-  iCount = 0;
-
-  int32_t iSearch = -1;
-  WideString wsContent;
-  CXFA_Node* pChildItem = pLabelItems->GetFirstChild();
-  for (; pChildItem; pChildItem = pChildItem->GetNextSibling()) {
-    if (pChildItem->JSObject()->GetContent(false) == wsLabel) {
-      iSearch = iCount;
-      break;
-    }
-    iCount++;
-  }
-  if (iSearch < 0)
-    return L"";
-
-  CXFA_Node* pText =
-      pSaveItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false);
-  return pText ? pText->JSObject()->GetContent(false) : L"";
-}
-
-bool CXFA_WidgetAcc::DeleteItem(int32_t nIndex,
-                                bool bNotify,
-                                bool bScriptModify) {
-  bool bSetValue = false;
-  CXFA_Node* pItems = m_pNode->GetFirstChild();
-  for (; pItems; pItems = pItems->GetNextSibling()) {
-    if (pItems->GetElementType() != XFA_Element::Items)
-      continue;
-
-    if (nIndex < 0) {
-      while (CXFA_Node* pNode = pItems->GetFirstChild()) {
-        pItems->RemoveChild(pNode, true);
-      }
-    } else {
-      if (!bSetValue && pItems->JSObject()->GetBoolean(XFA_Attribute::Save)) {
-        SetItemState(nIndex, false, true, bScriptModify, true);
-        bSetValue = true;
-      }
-      int32_t i = 0;
-      CXFA_Node* pNode = pItems->GetFirstChild();
-      while (pNode) {
-        if (i == nIndex) {
-          pItems->RemoveChild(pNode, true);
-          break;
-        }
-        i++;
-        pNode = pNode->GetNextSibling();
-      }
-    }
-  }
-  if (bNotify)
-    m_pNode->GetDocument()->GetNotify()->OnWidgetListItemRemoved(this, nIndex);
-  return true;
-}
-
-bool CXFA_WidgetAcc::IsHorizontalScrollPolicyOff() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild) {
-    return pUIChild->JSObject()->GetEnum(XFA_Attribute::HScrollPolicy) ==
-           XFA_AttributeEnum::Off;
-  }
-  return false;
-}
-
-bool CXFA_WidgetAcc::IsVerticalScrollPolicyOff() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (pUIChild) {
-    return pUIChild->JSObject()->GetEnum(XFA_Attribute::VScrollPolicy) ==
-           XFA_AttributeEnum::Off;
-  }
-  return false;
-}
-
-Optional<int32_t> CXFA_WidgetAcc::GetNumberOfCells() {
-  CXFA_Node* pUIChild = GetUIChild();
-  if (!pUIChild)
-    return {};
-  if (CXFA_Comb* pNode =
-          pUIChild->GetChild<CXFA_Comb>(0, XFA_Element::Comb, false))
-    return {pNode->JSObject()->GetInteger(XFA_Attribute::NumberOfCells)};
-  return {};
-}
-
-WideString CXFA_WidgetAcc::GetPasswordChar() {
-  CXFA_Node* pUIChild = GetUIChild();
-  return pUIChild ? pUIChild->JSObject()->GetCData(XFA_Attribute::PasswordChar)
-                  : L"*";
-}
-
-bool CXFA_WidgetAcc::IsMultiLine() {
-  CXFA_Node* pUIChild = GetUIChild();
-  return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::MultiLine);
-}
-
-std::pair<XFA_Element, int32_t> CXFA_WidgetAcc::GetMaxChars() {
-  if (CXFA_Value* pNode =
-          m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false)) {
-    if (CXFA_Node* pChild = pNode->GetFirstChild()) {
-      switch (pChild->GetElementType()) {
-        case XFA_Element::Text:
-          return {XFA_Element::Text,
-                  pChild->JSObject()->GetInteger(XFA_Attribute::MaxChars)};
-        case XFA_Element::ExData: {
-          int32_t iMax =
-              pChild->JSObject()->GetInteger(XFA_Attribute::MaxLength);
-          return {XFA_Element::ExData, iMax < 0 ? 0 : iMax};
-        }
-        default:
-          break;
-      }
-    }
-  }
-  return {XFA_Element::Unknown, 0};
-}
-
-int32_t CXFA_WidgetAcc::GetFracDigits() {
-  CXFA_Value* pNode =
-      m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
-  if (!pNode)
-    return -1;
-
-  CXFA_Decimal* pChild =
-      pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
-  if (!pChild)
-    return -1;
-
-  return pChild->JSObject()
-      ->TryInteger(XFA_Attribute::FracDigits, true)
-      .value_or(-1);
-}
-
-int32_t CXFA_WidgetAcc::GetLeadDigits() {
-  CXFA_Value* pNode =
-      m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
-  if (!pNode)
-    return -1;
-
-  CXFA_Decimal* pChild =
-      pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
-  if (!pChild)
-    return -1;
-
-  return pChild->JSObject()
-      ->TryInteger(XFA_Attribute::LeadDigits, true)
-      .value_or(-1);
-}
-
-bool CXFA_WidgetAcc::SetValue(XFA_VALUEPICTURE eValueType,
-                              const WideString& wsValue) {
-  if (wsValue.IsEmpty()) {
-    if (m_pNode)
-      m_pNode->SyncValue(wsValue, true);
-    return true;
-  }
-
-  m_bPreNull = m_bIsNull;
-  m_bIsNull = false;
-  WideString wsNewText(wsValue);
-  WideString wsPicture = GetPictureContent(eValueType);
-  bool bValidate = true;
-  bool bSyncData = false;
-  CXFA_Node* pNode = GetUIChild();
-  if (!pNode)
-    return true;
-
-  XFA_Element eType = pNode->GetElementType();
-  if (!wsPicture.IsEmpty()) {
-    CXFA_LocaleMgr* pLocalMgr = m_pNode->GetDocument()->GetLocalMgr();
-    IFX_Locale* pLocale = GetLocale();
-    CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode());
-    bValidate =
-        widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture);
-    if (bValidate) {
-      widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsNewText,
-                                     wsPicture, pLocale, pLocalMgr);
-      wsNewText = widgetValue.GetValue();
-      if (eType == XFA_Element::NumericEdit)
-        wsNewText = NumericLimit(wsNewText, GetLeadDigits(), GetFracDigits());
-
-      bSyncData = true;
-    }
-  } else {
-    if (eType == XFA_Element::NumericEdit) {
-      if (wsNewText != L"0")
-        wsNewText = NumericLimit(wsNewText, GetLeadDigits(), GetFracDigits());
-
-      bSyncData = true;
-    }
-  }
-  if (eType != XFA_Element::NumericEdit || bSyncData) {
-    if (m_pNode)
-      m_pNode->SyncValue(wsNewText, true);
-  }
-
-  return bValidate;
-}
-
-WideString CXFA_WidgetAcc::GetPictureContent(XFA_VALUEPICTURE ePicture) {
-  if (ePicture == XFA_VALUEPICTURE_Raw)
-    return L"";
-
-  CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode());
-  switch (ePicture) {
-    case XFA_VALUEPICTURE_Display: {
-      if (CXFA_Format* pFormat =
-              m_pNode->GetChild<CXFA_Format>(0, XFA_Element::Format, false)) {
-        if (CXFA_Picture* pPicture = pFormat->GetChild<CXFA_Picture>(
-                0, XFA_Element::Picture, false)) {
-          Optional<WideString> picture =
-              pPicture->JSObject()->TryContent(false, true);
-          if (picture)
-            return *picture;
-        }
-      }
-
-      IFX_Locale* pLocale = GetLocale();
-      if (!pLocale)
-        return L"";
-
-      uint32_t dwType = widgetValue.GetType();
-      switch (dwType) {
-        case XFA_VT_DATE:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
-        case XFA_VT_TIME:
-          return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
-        case XFA_VT_DATETIME:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium) +
-                 L"T" +
-                 pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
-        case XFA_VT_DECIMAL:
-        case XFA_VT_FLOAT:
-        default:
-          return L"";
-      }
-    }
-    case XFA_VALUEPICTURE_Edit: {
-      CXFA_Ui* pUI = m_pNode->GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
-      if (pUI) {
-        if (CXFA_Picture* pPicture =
-                pUI->GetChild<CXFA_Picture>(0, XFA_Element::Picture, false)) {
-          Optional<WideString> picture =
-              pPicture->JSObject()->TryContent(false, true);
-          if (picture)
-            return *picture;
-        }
-      }
-
-      IFX_Locale* pLocale = GetLocale();
-      if (!pLocale)
-        return L"";
-
-      uint32_t dwType = widgetValue.GetType();
-      switch (dwType) {
-        case XFA_VT_DATE:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
-        case XFA_VT_TIME:
-          return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
-        case XFA_VT_DATETIME:
-          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short) +
-                 L"T" +
-                 pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
-        default:
-          return L"";
-      }
-    }
-    case XFA_VALUEPICTURE_DataBind: {
-      CXFA_Bind* bind = m_pNode->GetBindIfExists();
-      if (bind)
-        return bind->GetPicture();
-      break;
-    }
-    default:
-      break;
-  }
-  return L"";
-}
-
-IFX_Locale* CXFA_WidgetAcc::GetLocale() {
-  return m_pNode ? m_pNode->GetLocale() : nullptr;
-}
-
-WideString CXFA_WidgetAcc::GetValue(XFA_VALUEPICTURE eValueType) {
-  WideString wsValue = m_pNode->JSObject()->GetContent(false);
-
-  if (eValueType == XFA_VALUEPICTURE_Display)
-    GetItemLabel(wsValue.AsStringView(), wsValue);
-
-  WideString wsPicture = GetPictureContent(eValueType);
-  CXFA_Node* pNode = GetUIChild();
-  if (!pNode)
-    return wsValue;
-
-  switch (GetUIChild()->GetElementType()) {
-    case XFA_Element::ChoiceList: {
-      if (eValueType == XFA_VALUEPICTURE_Display) {
-        int32_t iSelItemIndex = GetSelectedItem(0);
-        if (iSelItemIndex >= 0) {
-          wsValue = GetChoiceListItem(iSelItemIndex, false).value_or(L"");
-          wsPicture.clear();
-        }
-      }
-    } break;
-    case XFA_Element::NumericEdit:
-      if (eValueType != XFA_VALUEPICTURE_Raw && wsPicture.IsEmpty()) {
-        IFX_Locale* pLocale = GetLocale();
-        if (eValueType == XFA_VALUEPICTURE_Display && pLocale)
-          wsValue = FormatNumStr(NormalizeNumStr(wsValue), pLocale);
-      }
-      break;
-    default:
-      break;
-  }
-  if (wsPicture.IsEmpty())
-    return wsValue;
-
-  if (IFX_Locale* pLocale = GetLocale()) {
-    CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode());
-    CXFA_LocaleMgr* pLocalMgr = m_pNode->GetDocument()->GetLocalMgr();
-    switch (widgetValue.GetType()) {
-      case XFA_VT_DATE: {
-        WideString wsDate, wsTime;
-        if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocalMgr);
-          if (date.FormatPatterns(wsValue, wsPicture, pLocale, eValueType))
-            return wsValue;
-        }
-        break;
-      }
-      case XFA_VT_TIME: {
-        WideString wsDate, wsTime;
-        if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocalMgr);
-          if (time.FormatPatterns(wsValue, wsPicture, pLocale, eValueType))
-            return wsValue;
-        }
-        break;
-      }
-      default:
-        break;
-    }
-    widgetValue.FormatPatterns(wsValue, wsPicture, pLocale, eValueType);
-  }
-  return wsValue;
-}
-
-WideString CXFA_WidgetAcc::GetNormalizeDataValue(const WideString& wsValue) {
-  if (wsValue.IsEmpty())
-    return L"";
-
-  WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
-  if (wsPicture.IsEmpty())
-    return wsValue;
-
-  ASSERT(GetNode());
-  CXFA_LocaleMgr* pLocalMgr = GetNode()->GetDocument()->GetLocalMgr();
-  IFX_Locale* pLocale = GetLocale();
-  CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode());
-  if (widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture)) {
-    widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsValue, wsPicture,
-                                   pLocale, pLocalMgr);
-    return widgetValue.GetValue();
-  }
-  return wsValue;
-}
-
-WideString CXFA_WidgetAcc::GetFormatDataValue(const WideString& wsValue) {
-  if (wsValue.IsEmpty())
-    return L"";
-
-  WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
-  if (wsPicture.IsEmpty())
-    return wsValue;
-
-  WideString wsFormattedValue = wsValue;
-  if (IFX_Locale* pLocale = GetLocale()) {
-    ASSERT(GetNode());
-    CXFA_Value* pNodeValue =
-        GetNode()->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
-    if (!pNodeValue)
-      return wsValue;
-
-    CXFA_Node* pValueChild = pNodeValue->GetFirstChild();
-    if (!pValueChild)
-      return wsValue;
-
-    int32_t iVTType = XFA_VT_NULL;
-    switch (pValueChild->GetElementType()) {
-      case XFA_Element::Decimal:
-        iVTType = XFA_VT_DECIMAL;
-        break;
-      case XFA_Element::Float:
-        iVTType = XFA_VT_FLOAT;
-        break;
-      case XFA_Element::Date:
-        iVTType = XFA_VT_DATE;
-        break;
-      case XFA_Element::Time:
-        iVTType = XFA_VT_TIME;
-        break;
-      case XFA_Element::DateTime:
-        iVTType = XFA_VT_DATETIME;
-        break;
-      case XFA_Element::Boolean:
-        iVTType = XFA_VT_BOOLEAN;
-        break;
-      case XFA_Element::Integer:
-        iVTType = XFA_VT_INTEGER;
-        break;
-      case XFA_Element::Text:
-        iVTType = XFA_VT_TEXT;
-        break;
-      default:
-        iVTType = XFA_VT_NULL;
-        break;
-    }
-    CXFA_LocaleMgr* pLocalMgr = GetNode()->GetDocument()->GetLocalMgr();
-    CXFA_LocaleValue widgetValue(iVTType, wsValue, pLocalMgr);
-    switch (widgetValue.GetType()) {
-      case XFA_VT_DATE: {
-        WideString wsDate, wsTime;
-        if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocalMgr);
-          if (date.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
-                                  XFA_VALUEPICTURE_DataBind)) {
-            return wsFormattedValue;
-          }
-        }
-        break;
-      }
-      case XFA_VT_TIME: {
-        WideString wsDate, wsTime;
-        if (SplitDateTime(wsValue, wsDate, wsTime)) {
-          CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocalMgr);
-          if (time.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
-                                  XFA_VALUEPICTURE_DataBind)) {
-            return wsFormattedValue;
-          }
-        }
-        break;
-      }
-      default:
-        break;
-    }
-    widgetValue.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
-                               XFA_VALUEPICTURE_DataBind);
-  }
-  return wsFormattedValue;
-}
-
-WideString CXFA_WidgetAcc::NormalizeNumStr(const WideString& wsValue) {
-  if (wsValue.IsEmpty())
-    return L"";
-
-  WideString wsOutput = wsValue;
-  wsOutput.TrimLeft('0');
-
-  if (!wsOutput.IsEmpty() && wsOutput.Contains('.') && GetFracDigits() != -1) {
-    wsOutput.TrimRight(L"0");
-    wsOutput.TrimRight(L".");
-  }
-  if (wsOutput.IsEmpty() || wsOutput[0] == '.')
-    wsOutput.InsertAtFront('0');
-
-  return wsOutput;
-}
-
-WideString CXFA_WidgetAcc::FormatNumStr(const WideString& wsValue,
-                                        IFX_Locale* pLocale) {
-  if (wsValue.IsEmpty())
-    return L"";
-
-  WideString wsSrcNum = wsValue;
-  WideString wsGroupSymbol =
-      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping);
-  bool bNeg = false;
-  if (wsSrcNum[0] == '-') {
-    bNeg = true;
-    wsSrcNum.Delete(0, 1);
-  }
-
-  auto dot_index = wsSrcNum.Find('.');
-  dot_index = !dot_index.has_value() ? wsSrcNum.GetLength() : dot_index;
-
-  if (dot_index.value() < 1)
-    return L"";
-
-  size_t nPos = dot_index.value() % 3;
-  WideString wsOutput;
-  for (size_t i = 0; i < dot_index.value(); i++) {
-    if (i % 3 == nPos && i != 0)
-      wsOutput += wsGroupSymbol;
-
-    wsOutput += wsSrcNum[i];
-  }
-  if (dot_index.value() < wsSrcNum.GetLength()) {
-    wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
-    wsOutput += wsSrcNum.Right(wsSrcNum.GetLength() - dot_index.value() - 1);
-  }
-  if (bNeg)
-    return pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + wsOutput;
-
-  return wsOutput;
-}
-
-void CXFA_WidgetAcc::InsertListTextItem(CXFA_Node* pItems,
-                                        const WideString& wsText,
-                                        int32_t nIndex) {
-  CXFA_Node* pText = pItems->CreateSamePacketNode(XFA_Element::Text);
-  pItems->InsertChild(nIndex, pText);
-  pText->JSObject()->SetContent(wsText, wsText, false, false, false);
-}
-
-WideString CXFA_WidgetAcc::NumericLimit(const WideString& wsValue,
-                                        int32_t iLead,
-                                        int32_t iTread) const {
-  if ((iLead == -1) && (iTread == -1))
-    return wsValue;
-
-  WideString wsRet;
-  int32_t iLead_ = 0, iTread_ = -1;
-  int32_t iCount = wsValue.GetLength();
-  if (iCount == 0)
-    return wsValue;
-
-  int32_t i = 0;
-  if (wsValue[i] == L'-') {
-    wsRet += L'-';
-    i++;
-  }
-  for (; i < iCount; i++) {
-    wchar_t wc = wsValue[i];
-    if (FXSYS_isDecimalDigit(wc)) {
-      if (iLead >= 0) {
-        iLead_++;
-        if (iLead_ > iLead)
-          return L"0";
-      } else if (iTread_ >= 0) {
-        iTread_++;
-        if (iTread_ > iTread) {
-          if (iTread != -1) {
-            CFX_Decimal wsDeci = CFX_Decimal(wsValue.AsStringView());
-            wsDeci.SetScale(iTread);
-            wsRet = wsDeci;
-          }
-          return wsRet;
-        }
-      }
-    } else if (wc == L'.') {
-      iTread_ = 0;
-      iLead = -1;
-    }
-    wsRet += wc;
-  }
-  return wsRet;
-}
diff --git a/xfa/fxfa/cxfa_widgetacc.h b/xfa/fxfa/cxfa_widgetacc.h
deleted file mode 100644
index f21ace9..0000000
--- a/xfa/fxfa/cxfa_widgetacc.h
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_WIDGETACC_H_
-#define XFA_FXFA_CXFA_WIDGETACC_H_
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#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/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
-#include "xfa/fxfa/fxfa_basic.h"
-
-enum XFA_CHECKSTATE {
-  XFA_CHECKSTATE_On = 0,
-  XFA_CHECKSTATE_Off = 1,
-  XFA_CHECKSTATE_Neutral = 2,
-};
-
-enum XFA_VALUEPICTURE {
-  XFA_VALUEPICTURE_Raw = 0,
-  XFA_VALUEPICTURE_Display,
-  XFA_VALUEPICTURE_Edit,
-  XFA_VALUEPICTURE_DataBind,
-};
-
-class CFGAS_GEFont;
-class CXFA_Bind;
-class CXFA_Border;
-class CXFA_Calculate;
-class CXFA_Caption;
-class CXFA_Event;
-class CXFA_EventParam;
-class CXFA_FFApp;
-class CXFA_FFDoc;
-class CXFA_FFDocView;
-class CXFA_FFWidget;
-class CXFA_Font;
-class CXFA_Margin;
-class CXFA_Node;
-class CXFA_Script;
-class CXFA_Para;
-class CXFA_TextLayout;
-class CXFA_Value;
-class CXFA_Validate;
-class CXFA_WidgetLayoutData;
-class IFX_Locale;
-
-class CXFA_WidgetAcc {
- public:
-  explicit CXFA_WidgetAcc(CXFA_Node* pNode);
-  ~CXFA_WidgetAcc();
-
-  void ResetData();
-
-  CXFA_FFWidget* GetNextWidget(CXFA_FFWidget* pWidget);
-  void StartWidgetLayout(CXFA_FFDoc* doc,
-                         float& fCalcWidth,
-                         float& fCalcHeight);
-  bool FindSplitPos(CXFA_FFDocView* docView,
-                    int32_t iBlockIndex,
-                    float& fCalcHeight);
-
-  bool LoadCaption(CXFA_FFDoc* doc);
-  CXFA_TextLayout* GetCaptionTextLayout();
-
-  void LoadText(CXFA_FFDoc* doc);
-  CXFA_TextLayout* GetTextLayout();
-
-  bool LoadImageImage(CXFA_FFDoc* doc);
-  bool LoadImageEditImage(CXFA_FFDoc* doc);
-  void GetImageDpi(int32_t& iImageXDpi, int32_t& iImageYDpi);
-  void GetImageEditDpi(int32_t& iImageXDpi, int32_t& iImageYDpi);
-
-  RetainPtr<CFX_DIBitmap> GetImageImage();
-  RetainPtr<CFX_DIBitmap> GetImageEditImage();
-  void SetImageEdit(const WideString& wsContentType,
-                    const WideString& wsHref,
-                    const WideString& wsData);
-  void SetImageImage(const RetainPtr<CFX_DIBitmap>& newImage);
-  void SetImageEditImage(const RetainPtr<CFX_DIBitmap>& newImage);
-  void UpdateUIDisplay(CXFA_FFDocView* docView, CXFA_FFWidget* pExcept);
-
-  RetainPtr<CFGAS_GEFont> GetFDEFont(CXFA_FFDoc* doc);
-
-  CXFA_Node* GetNode() const { return m_pNode; }
-
-  CXFA_Node* GetUIChild();
-  XFA_Element GetUIType();
-  CFX_RectF GetUIMargin();
-
-  bool IsOpenAccess() const;
-  bool IsListBox();
-  bool IsAllowNeutral();
-  bool IsRadioButton();
-  bool IsChoiceListAllowTextEntry();
-  bool IsMultiLine();
-
-  CXFA_Border* GetUIBorder();
-
-  std::vector<CXFA_Event*> GetEventByActivity(XFA_AttributeEnum iActivity,
-                                              bool bIsFormReady);
-
-  XFA_AttributeEnum GetButtonHighlight();
-  bool HasButtonRollover() const;
-  bool HasButtonDown() const;
-
-  bool IsCheckButtonRound();
-  XFA_AttributeEnum GetCheckButtonMark();
-  float GetCheckButtonSize();
-
-  XFA_CHECKSTATE GetCheckState();
-  void SetCheckState(XFA_CHECKSTATE eCheckState, bool bNotify);
-
-  CXFA_Node* GetSelectedMember();
-  CXFA_Node* SetSelectedMember(const WideStringView& wsName, bool bNotify);
-  void SetSelectedMemberByValue(const WideStringView& wsValue,
-                                bool bNotify,
-                                bool bScriptModify,
-                                bool bSyncData);
-
-  CXFA_Node* GetExclGroupFirstMember();
-  CXFA_Node* GetExclGroupNextMember(CXFA_Node* pNode);
-
-  int32_t CountChoiceListItems(bool bSaveValue);
-  Optional<WideString> GetChoiceListItem(int32_t nIndex, bool bSaveValue);
-  bool IsChoiceListMultiSelect();
-  bool IsChoiceListCommitOnSelect();
-  std::vector<WideString> GetChoiceListItems(bool bSaveValue);
-
-  int32_t CountSelectedItems();
-  int32_t GetSelectedItem(int32_t nIndex);
-  std::vector<int32_t> GetSelectedItems();
-  std::vector<WideString> GetSelectedItemsValue();
-  void SetSelectedItems(const std::vector<int32_t>& iSelArray,
-                        bool bNotify,
-                        bool bScriptModify,
-                        bool bSyncData);
-  void InsertItem(const WideString& wsLabel,
-                  const WideString& wsValue,
-                  bool bNotify);
-  bool DeleteItem(int32_t nIndex, bool bNotify, bool bScriptModify);
-  void ClearAllSelections();
-
-  bool GetItemState(int32_t nIndex);
-  void SetItemState(int32_t nIndex,
-                    bool bSelected,
-                    bool bNotify,
-                    bool bScriptModify,
-                    bool bSyncData);
-
-  WideString GetItemValue(const WideStringView& wsLabel);
-
-  bool IsHorizontalScrollPolicyOff();
-  bool IsVerticalScrollPolicyOff();
-  Optional<int32_t> GetNumberOfCells();
-
-  bool SetValue(XFA_VALUEPICTURE eValueType, const WideString& wsValue);
-  WideString GetValue(XFA_VALUEPICTURE eValueType);
-
-  WideString GetPictureContent(XFA_VALUEPICTURE ePicture);
-  IFX_Locale* GetLocale();
-
-  WideString GetNormalizeDataValue(const WideString& wsValue);
-  WideString GetFormatDataValue(const WideString& wsValue);
-  WideString NormalizeNumStr(const WideString& wsValue);
-
-  WideString GetPasswordChar();
-  std::pair<XFA_Element, int32_t> GetMaxChars();
-  int32_t GetFracDigits();
-  int32_t GetLeadDigits();
-
-  WideString NumericLimit(const WideString& wsValue,
-                          int32_t iLead,
-                          int32_t iTread) const;
-
-  bool IsPreNull() const { return m_bPreNull; }
-  void SetPreNull(bool val) { m_bPreNull = val; }
-  bool IsNull() const { return m_bIsNull; }
-  void SetIsNull(bool val) { m_bIsNull = val; }
-
- private:
-  void CalcCaptionSize(CXFA_FFDoc* doc, CFX_SizeF& szCap);
-  bool CalculateFieldAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size);
-  bool CalculateWidgetAutoSize(CFX_SizeF& size);
-  bool CalculateTextEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size);
-  bool CalculateCheckButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size);
-  bool CalculatePushButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size);
-  CFX_SizeF CalculateImageSize(float img_width,
-                               float img_height,
-                               float dpi_x,
-                               float dpi_y);
-  bool CalculateImageEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size);
-  bool CalculateImageAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size);
-  float CalculateWidgetAutoHeight(float fHeightCalc);
-  float CalculateWidgetAutoWidth(float fWidthCalc);
-  float GetWidthWithoutMargin(float fWidthCalc);
-  float GetHeightWithoutMargin(float fHeightCalc);
-  void CalculateTextContentSize(CXFA_FFDoc* doc, CFX_SizeF& size);
-  void CalculateAccWidthAndHeight(CXFA_FFDoc* doc,
-                                  XFA_Element eUIType,
-                                  float& fWidth,
-                                  float& fCalcHeight);
-  void InitLayoutData();
-  void StartTextLayout(CXFA_FFDoc* doc, float& fCalcWidth, float& fCalcHeight);
-
-  void InsertListTextItem(CXFA_Node* pItems,
-                          const WideString& wsText,
-                          int32_t nIndex);
-  WideString FormatNumStr(const WideString& wsValue, IFX_Locale* pLocale);
-  void GetItemLabel(const WideStringView& wsValue, WideString& wsLabel);
-
-  std::unique_ptr<CXFA_WidgetLayoutData> m_pLayoutData;
-  bool m_bIsNull;
-  bool m_bPreNull;
-  CXFA_Node* m_pUiChildNode;
-  XFA_Element m_eUIType;
-  CXFA_Node* m_pNode;
-};
-
-#endif  // XFA_FXFA_CXFA_WIDGETACC_H_
diff --git a/xfa/fxfa/cxfa_widgetacciterator.cpp b/xfa/fxfa/cxfa_widgetacciterator.cpp
deleted file mode 100644
index ece5604..0000000
--- a/xfa/fxfa/cxfa_widgetacciterator.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/cxfa_widgetacciterator.h"
-
-#include "xfa/fxfa/cxfa_widgetacc.h"
-
-CXFA_WidgetAccIterator::CXFA_WidgetAccIterator(CXFA_Node* pTravelRoot)
-    : m_ContentIterator(pTravelRoot), m_pCurWidgetAcc(nullptr) {}
-
-CXFA_WidgetAccIterator::~CXFA_WidgetAccIterator() {}
-
-CXFA_WidgetAcc* CXFA_WidgetAccIterator::MoveToNext() {
-  CXFA_Node* pItem = m_pCurWidgetAcc ? m_ContentIterator.MoveToNext()
-                                     : m_ContentIterator.GetCurrent();
-  while (pItem) {
-    m_pCurWidgetAcc = pItem->GetWidgetAcc();
-    if (m_pCurWidgetAcc)
-      return m_pCurWidgetAcc.Get();
-    pItem = m_ContentIterator.MoveToNext();
-  }
-  return nullptr;
-}
-
-void CXFA_WidgetAccIterator::SkipTree() {
-  m_ContentIterator.SkipChildrenAndMoveToNext();
-  m_pCurWidgetAcc = nullptr;
-}
diff --git a/xfa/fxfa/cxfa_widgetacciterator.h b/xfa/fxfa/cxfa_widgetacciterator.h
deleted file mode 100644
index 79860a4..0000000
--- a/xfa/fxfa/cxfa_widgetacciterator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_CXFA_WIDGETACCITERATOR_H_
-#define XFA_FXFA_CXFA_WIDGETACCITERATOR_H_
-
-#include "core/fxcrt/unowned_ptr.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
-
-class CXFA_Node;
-class CXFA_WidgetAcc;
-
-class CXFA_WidgetAccIterator {
- public:
-  explicit CXFA_WidgetAccIterator(CXFA_Node* pTravelRoot);
-  ~CXFA_WidgetAccIterator();
-
-  CXFA_WidgetAcc* MoveToNext();
-  void SkipTree();
-
- private:
-  CXFA_ContainerIterator m_ContentIterator;
-  UnownedPtr<CXFA_WidgetAcc> m_pCurWidgetAcc;
-};
-
-#endif  // XFA_FXFA_CXFA_WIDGETACCITERATOR_H_
diff --git a/xfa/fxfa/fm2js/BUILD.gn b/xfa/fxfa/fm2js/BUILD.gn
new file mode 100644
index 0000000..a6b2540
--- /dev/null
+++ b/xfa/fxfa/fm2js/BUILD.gn
@@ -0,0 +1,40 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fm2js") {
+  sources = [
+    "cxfa_fmexpression.cpp",
+    "cxfa_fmexpression.h",
+    "cxfa_fmlexer.cpp",
+    "cxfa_fmlexer.h",
+    "cxfa_fmparser.cpp",
+    "cxfa_fmparser.h",
+    "cxfa_fmsimpleexpression.cpp",
+    "cxfa_fmsimpleexpression.h",
+    "cxfa_fmtojavascriptdepth.cpp",
+    "cxfa_fmtojavascriptdepth.h",
+  ]
+  deps = [ "../../../core/fxcrt" ]
+  configs += [
+    "../../../:pdfium_core_config",
+    "../../:xfa_warnings",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cxfa_fmexpression_unittest.cpp",
+    "cxfa_fmlexer_unittest.cpp",
+    "cxfa_fmparser_unittest.cpp",
+    "cxfa_fmsimpleexpression_unittest.cpp",
+  ]
+  deps = [ ":fm2js" ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/xfa/fxfa/fm2js/DEPS b/xfa/fxfa/fm2js/DEPS
index 2be0352..e19a721 100644
--- a/xfa/fxfa/fm2js/DEPS
+++ b/xfa/fxfa/fm2js/DEPS
@@ -1,3 +1,6 @@
 include_rules = [
   '+third_party/icu',
+
+  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
+  '-xfa/fwl',
 ]
diff --git a/xfa/fxfa/fm2js/cxfa_fmexpression.cpp b/xfa/fxfa/fm2js/cxfa_fmexpression.cpp
index 759a875..a87ecca 100644
--- a/xfa/fxfa/fm2js/cxfa_fmexpression.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmexpression.cpp
@@ -14,515 +14,342 @@
 
 namespace {
 
-const wchar_t RUNTIMEBLOCKTEMPARRAY[] = L"pfm_ary";
-
-const wchar_t RUNTIMEBLOCKTEMPARRAYINDEX[] = L"pfm_ary_idx";
-
 const wchar_t kLessEqual[] = L" <= ";
 const wchar_t kGreaterEqual[] = L" >= ";
 const wchar_t kPlusEqual[] = L" += ";
 const wchar_t kMinusEqual[] = L" -= ";
 
+WideString IdentifierToName(WideStringView ident) {
+  if (ident.IsEmpty())
+    return WideString();
+  if (ident[0] != L'!')
+    return WideString(ident);
+  return L"pfm__excl__" + ident.Last(ident.GetLength() - 1);
+}
+
 }  // namespace
 
-CXFA_FMExpression::CXFA_FMExpression(uint32_t line)
-    : m_type(XFA_FM_EXPTYPE_UNKNOWN), m_line(line) {}
-
-CXFA_FMExpression::CXFA_FMExpression(uint32_t line, XFA_FM_EXPTYPE type)
-    : m_type(type), m_line(line) {}
-
-bool CXFA_FMExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
-}
-
-bool CXFA_FMExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
-}
+CXFA_FMExpression::CXFA_FMExpression() = default;
 
 CXFA_FMFunctionDefinition::CXFA_FMFunctionDefinition(
-    uint32_t line,
-    bool isGlobal,
-    const WideStringView& wsName,
+    WideStringView wsName,
     std::vector<WideStringView>&& arguments,
     std::vector<std::unique_ptr<CXFA_FMExpression>>&& expressions)
-    : CXFA_FMExpression(line, XFA_FM_EXPTYPE_FUNC),
+    : CXFA_FMExpression(),
       m_wsName(wsName),
       m_pArguments(std::move(arguments)),
-      m_pExpressions(std::move(expressions)),
-      m_isGlobal(isGlobal) {}
-
-CXFA_FMFunctionDefinition::~CXFA_FMFunctionDefinition() {}
-
-bool CXFA_FMFunctionDefinition::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  if (m_isGlobal && m_pExpressions.empty()) {
-    javascript << L"// comments only";
-    return !CXFA_IsTooBig(javascript);
-  }
-  if (m_isGlobal) {
-    javascript << L"(\n";
-  }
-  javascript << L"function ";
-  if (!m_wsName.IsEmpty() && m_wsName[0] == L'!') {
-    WideString tempName =
-        EXCLAMATION_IN_IDENTIFIER + m_wsName.Right(m_wsName.GetLength() - 1);
-    javascript << tempName;
-  } else {
-    javascript << m_wsName;
-  }
-  javascript << L"(";
-  bool bNeedComma = false;
-  for (const auto& identifier : m_pArguments) {
-    if (bNeedComma)
-      javascript << L", ";
-    if (identifier[0] == L'!') {
-      WideString tempIdentifier = EXCLAMATION_IN_IDENTIFIER +
-                                  identifier.Right(identifier.GetLength() - 1);
-      javascript << tempIdentifier;
-    } else {
-      javascript << identifier;
-    }
-    bNeedComma = true;
-  }
-  javascript << L")\n{\n";
-  javascript << L"var ";
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = null;\n";
-  for (const auto& expr : m_pExpressions) {
-    bool ret;
-    if (expr == m_pExpressions.back())
-      ret = expr->ToImpliedReturnJS(javascript);
-    else
-      ret = expr->ToJavaScript(javascript);
-
-    if (!ret)
-      return false;
-  }
-  javascript << L"return ";
-  if (m_isGlobal) {
-    javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-    javascript << L"(";
-    javascript << RUNTIMEFUNCTIONRETURNVALUE;
-    javascript << L")";
-  } else {
-    javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  }
-  javascript << L";\n}\n";
-  if (m_isGlobal) {
-    javascript << L").call(this);\n";
-  }
-  return !CXFA_IsTooBig(javascript);
+      m_pExpressions(std::move(expressions)) {
+  ASSERT(!wsName.IsEmpty());
 }
 
-bool CXFA_FMFunctionDefinition::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
+CXFA_FMFunctionDefinition::~CXFA_FMFunctionDefinition() = default;
+
+bool CXFA_FMFunctionDefinition::ToJavaScript(CFX_WideTextBuf* js,
+                                             ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
+    return false;
+
+  if (m_wsName.IsEmpty())
+    return false;
+
+  *js << "function " << IdentifierToName(m_wsName) << "(";
+  for (const auto& identifier : m_pArguments) {
+    if (identifier != m_pArguments.front())
+      *js << ", ";
+
+    *js << IdentifierToName(identifier);
+  }
+  *js << ") {\n";
+
+  *js << "var pfm_ret = null;\n";
+  for (const auto& expr : m_pExpressions) {
+    ReturnType ret_type = expr == m_pExpressions.back() ? ReturnType::kImplied
+                                                        : ReturnType::kInfered;
+    if (!expr->ToJavaScript(js, ret_type))
+      return false;
+  }
+
+  *js << "return pfm_ret;\n";
+  *js << "}\n";
+
+  return !CXFA_IsTooBig(js);
+}
+
+CXFA_FMAST::CXFA_FMAST(
+    std::vector<std::unique_ptr<CXFA_FMExpression>> expressions)
+    : expressions_(std::move(expressions)) {}
+
+CXFA_FMAST::~CXFA_FMAST() = default;
+
+bool CXFA_FMAST::ToJavaScript(CFX_WideTextBuf* js) {
+  if (expressions_.empty()) {
+    *js << "// comments only";
+    return !CXFA_IsTooBig(js);
+  }
+
+  *js << "(function() {\n";
+  *js << "let pfm_method_runner = function(obj, cb) {\n";
+  *js << "  if (pfm_rt.is_ary(obj)) {\n";
+  *js << "    let pfm_method_return = null;\n";
+  *js << "    for (var idx = obj.length -1; idx > 1; idx--) {\n";
+  *js << "      pfm_method_return = cb(obj[idx]);\n";
+  *js << "    }\n";
+  *js << "    return pfm_method_return;\n";
+  *js << "  }\n";
+  *js << "  return cb(obj);\n";
+  *js << "};\n";
+  *js << "var pfm_ret = null;\n";
+
+  for (const auto& expr : expressions_) {
+    ReturnType ret_type = expr == expressions_.back() ? ReturnType::kImplied
+                                                      : ReturnType::kInfered;
+    if (!expr->ToJavaScript(js, ret_type))
+      return false;
+  }
+
+  *js << "return pfm_rt.get_val(pfm_ret);\n";
+  *js << "}).call(this);";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMVarExpression::CXFA_FMVarExpression(
-    uint32_t line,
-    const WideStringView& wsName,
-    std::unique_ptr<CXFA_FMExpression> pInit)
-    : CXFA_FMExpression(line, XFA_FM_EXPTYPE_VAR),
-      m_wsName(wsName),
-      m_pInit(std::move(pInit)) {}
+    WideStringView wsName,
+    std::unique_ptr<CXFA_FMSimpleExpression> pInit)
+    : CXFA_FMExpression(), m_wsName(wsName), m_pInit(std::move(pInit)) {}
 
-CXFA_FMVarExpression::~CXFA_FMVarExpression() {}
+CXFA_FMVarExpression::~CXFA_FMVarExpression() = default;
 
-bool CXFA_FMVarExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMVarExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"var ";
-  WideString tempName(m_wsName);
-  if (m_wsName[0] == L'!') {
-    tempName =
-        EXCLAMATION_IN_IDENTIFIER + m_wsName.Right(m_wsName.GetLength() - 1);
-  }
-  javascript << tempName;
-  javascript << L" = ";
+  WideString tempName = IdentifierToName(m_wsName);
+  *js << "var " << tempName << " = ";
   if (m_pInit) {
-    if (!m_pInit->ToJavaScript(javascript))
+    if (!m_pInit->ToJavaScript(js, ReturnType::kInfered))
       return false;
-    javascript << tempName;
-    javascript << L" = ";
-    javascript << XFA_FM_EXPTypeToString(VARFILTER);
-    javascript << L"(";
-    javascript << tempName;
-    javascript << L");\n";
-  } else {
-    javascript << L"\"\";\n";
-  }
-  return !CXFA_IsTooBig(javascript);
-}
 
-bool CXFA_FMVarExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << L"var ";
-  WideString tempName(m_wsName);
-  if (m_wsName[0] == L'!') {
-    tempName =
-        EXCLAMATION_IN_IDENTIFIER + m_wsName.Right(m_wsName.GetLength() - 1);
-  }
-  javascript << tempName;
-  javascript << L" = ";
-  if (m_pInit) {
-    if (!m_pInit->ToJavaScript(javascript))
-      return false;
-    javascript << tempName;
-    javascript << L" = ";
-    javascript << XFA_FM_EXPTypeToString(VARFILTER);
-    javascript << L"(";
-    javascript << tempName;
-    javascript << L");\n";
+    *js << ";\n";
+    *js << tempName << " = pfm_rt.var_filter(" << tempName << ");\n";
   } else {
-    javascript << L"\"\";\n";
+    *js << "\"\";\n";
   }
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = ";
-  javascript << tempName;
-  javascript << L";\n";
-  return !CXFA_IsTooBig(javascript);
+
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = " << tempName << ";\n";
+
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMExpExpression::CXFA_FMExpExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pExpression)
-    : CXFA_FMExpression(line, XFA_FM_EXPTYPE_EXP),
-      m_pExpression(std::move(pExpression)) {}
+    : CXFA_FMExpression(), m_pExpression(std::move(pExpression)) {}
 
-CXFA_FMExpExpression::~CXFA_FMExpExpression() {}
+CXFA_FMExpExpression::~CXFA_FMExpExpression() = default;
 
-bool CXFA_FMExpExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMExpExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  bool ret = m_pExpression->ToJavaScript(javascript);
-  if (m_pExpression->GetOperatorToken() != TOKassign)
-    javascript << L";\n";
-  return ret;
-}
+  if (type == ReturnType::kInfered) {
+    bool ret = m_pExpression->ToJavaScript(js, ReturnType::kInfered);
+    if (m_pExpression->GetOperatorToken() != TOKassign)
+      *js << ";\n";
 
-bool CXFA_FMExpExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
+    return ret;
+  }
 
   if (m_pExpression->GetOperatorToken() == TOKassign)
-    return m_pExpression->ToImpliedReturnJS(javascript);
+    return m_pExpression->ToJavaScript(js, ReturnType::kImplied);
 
   if (m_pExpression->GetOperatorToken() == TOKstar ||
       m_pExpression->GetOperatorToken() == TOKdotstar ||
       m_pExpression->GetOperatorToken() == TOKdotscream ||
       m_pExpression->GetOperatorToken() == TOKdotdot ||
       m_pExpression->GetOperatorToken() == TOKdot) {
-    javascript << RUNTIMEFUNCTIONRETURNVALUE;
-    javascript << L" = ";
-    javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-    javascript << L"(";
-    if (!m_pExpression->ToJavaScript(javascript))
+    *js << "pfm_ret = pfm_rt.get_val(";
+    if (!m_pExpression->ToJavaScript(js, ReturnType::kInfered))
       return false;
-    javascript << L");\n";
-    return !CXFA_IsTooBig(javascript);
+
+    *js << ");\n";
+    return !CXFA_IsTooBig(js);
   }
 
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = ";
-  if (!m_pExpression->ToJavaScript(javascript))
+  *js << "pfm_ret = ";
+  if (!m_pExpression->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << L";\n";
-  return !CXFA_IsTooBig(javascript);
+
+  *js << ";\n";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMBlockExpression::CXFA_FMBlockExpression(
-    uint32_t line,
     std::vector<std::unique_ptr<CXFA_FMExpression>>&& pExpressionList)
-    : CXFA_FMExpression(line, XFA_FM_EXPTYPE_BLOCK),
-      m_ExpressionList(std::move(pExpressionList)) {}
+    : CXFA_FMExpression(), m_ExpressionList(std::move(pExpressionList)) {}
 
-CXFA_FMBlockExpression::~CXFA_FMBlockExpression() {}
+CXFA_FMBlockExpression::~CXFA_FMBlockExpression() = default;
 
-bool CXFA_FMBlockExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMBlockExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                          ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"{\n";
+  *js << "{\n";
   for (const auto& expr : m_ExpressionList) {
-    if (!expr->ToJavaScript(javascript))
-      return false;
+    if (type == ReturnType::kInfered) {
+      if (!expr->ToJavaScript(js, ReturnType::kInfered))
+        return false;
+    } else {
+      ReturnType ret_type = expr == m_ExpressionList.back()
+                                ? ReturnType::kImplied
+                                : ReturnType::kInfered;
+      if (!expr->ToJavaScript(js, ret_type))
+        return false;
+    }
   }
-  javascript << L"}\n";
-  return !CXFA_IsTooBig(javascript);
-}
+  *js << "}\n";
 
-bool CXFA_FMBlockExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << L"{\n";
-  for (const auto& expr : m_ExpressionList) {
-    bool ret;
-    if (expr == m_ExpressionList.back())
-      ret = expr->ToImpliedReturnJS(javascript);
-    else
-      ret = expr->ToJavaScript(javascript);
-
-    if (!ret)
-      return false;
-  }
-  javascript << L"}\n";
-  return !CXFA_IsTooBig(javascript);
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMDoExpression::CXFA_FMDoExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMExpression> pList)
-    : CXFA_FMExpression(line), m_pList(std::move(pList)) {}
+    : CXFA_FMExpression(), m_pList(std::move(pList)) {}
 
-CXFA_FMDoExpression::~CXFA_FMDoExpression() {}
+CXFA_FMDoExpression::~CXFA_FMDoExpression() = default;
 
-bool CXFA_FMDoExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMDoExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  return m_pList->ToJavaScript(javascript);
-}
-
-bool CXFA_FMDoExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  return m_pList->ToImpliedReturnJS(javascript);
+  return m_pList->ToJavaScript(js, type);
 }
 
 CXFA_FMIfExpression::CXFA_FMIfExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pExpression,
     std::unique_ptr<CXFA_FMExpression> pIfExpression,
+    std::vector<std::unique_ptr<CXFA_FMIfExpression>> pElseIfExpressions,
     std::unique_ptr<CXFA_FMExpression> pElseExpression)
-    : CXFA_FMExpression(line, XFA_FM_EXPTYPE_IF),
+    : CXFA_FMExpression(),
       m_pExpression(std::move(pExpression)),
       m_pIfExpression(std::move(pIfExpression)),
-      m_pElseExpression(std::move(pElseExpression)) {}
+      m_pElseIfExpressions(std::move(pElseIfExpressions)),
+      m_pElseExpression(std::move(pElseExpression)) {
+  ASSERT(m_pExpression);
+}
 
-CXFA_FMIfExpression::~CXFA_FMIfExpression() {}
+CXFA_FMIfExpression::~CXFA_FMIfExpression() = default;
 
-bool CXFA_FMIfExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMIfExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"if (";
-  if (m_pExpression) {
-    javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-    javascript << L"(";
-    if (!m_pExpression->ToJavaScript(javascript))
-      return false;
-    javascript << L")";
-  }
-  javascript << L")\n";
-  if (CXFA_IsTooBig(javascript))
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
+
+  *js << "if (pfm_rt.get_val(";
+  if (!m_pExpression->ToJavaScript(js, ReturnType::kInfered))
+    return false;
+  *js << "))\n";
+
+  if (CXFA_IsTooBig(js))
     return false;
 
   if (m_pIfExpression) {
-    if (!m_pIfExpression->ToJavaScript(javascript))
+    if (!m_pIfExpression->ToJavaScript(js, type))
       return false;
-    if (CXFA_IsTooBig(javascript))
+    if (CXFA_IsTooBig(js))
+      return false;
+  }
+
+  for (auto& expr : m_pElseIfExpressions) {
+    *js << "else ";
+    if (!expr->ToJavaScript(js, ReturnType::kInfered))
       return false;
   }
 
   if (m_pElseExpression) {
-    if (m_pElseExpression->GetExpType() == XFA_FM_EXPTYPE_IF) {
-      javascript << L"else\n";
-      javascript << L"{\n";
-      if (!m_pElseExpression->ToJavaScript(javascript))
-        return false;
-      javascript << L"}\n";
-    } else {
-      javascript << L"else\n";
-      if (!m_pElseExpression->ToJavaScript(javascript))
-        return false;
-    }
-  }
-  return !CXFA_IsTooBig(javascript);
-}
-
-bool CXFA_FMIfExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"if (";
-  if (m_pExpression) {
-    javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-    javascript << L"(";
-    if (!m_pExpression->ToJavaScript(javascript))
-      return false;
-    javascript << L")";
-  }
-  javascript << L")\n";
-  if (CXFA_IsTooBig(javascript))
-    return false;
-
-  if (m_pIfExpression) {
-    if (!m_pIfExpression->ToImpliedReturnJS(javascript))
-      return false;
-    if (CXFA_IsTooBig(javascript))
+    *js << "else ";
+    if (!m_pElseExpression->ToJavaScript(js, type))
       return false;
   }
-  if (m_pElseExpression) {
-    if (m_pElseExpression->GetExpType() == XFA_FM_EXPTYPE_IF) {
-      javascript << L"else\n";
-      javascript << L"{\n";
-      if (!m_pElseExpression->ToImpliedReturnJS(javascript))
-        return false;
-      javascript << L"}\n";
-    } else {
-      javascript << L"else\n";
-      if (!m_pElseExpression->ToImpliedReturnJS(javascript))
-        return false;
-    }
-  }
-  return !CXFA_IsTooBig(javascript);
-}
-
-CXFA_FMLoopExpression::~CXFA_FMLoopExpression() {}
-
-bool CXFA_FMLoopExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
-}
-
-bool CXFA_FMLoopExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMWhileExpression::CXFA_FMWhileExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pCondition,
     std::unique_ptr<CXFA_FMExpression> pExpression)
-    : CXFA_FMLoopExpression(line),
+    : CXFA_FMExpression(),
       m_pCondition(std::move(pCondition)),
       m_pExpression(std::move(pExpression)) {}
 
-CXFA_FMWhileExpression::~CXFA_FMWhileExpression() {}
+CXFA_FMWhileExpression::~CXFA_FMWhileExpression() = default;
 
-bool CXFA_FMWhileExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMWhileExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                          ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"while (";
-  if (!m_pCondition->ToJavaScript(javascript))
-    return false;
-  javascript << L")\n";
-  if (CXFA_IsTooBig(javascript))
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
+
+  *js << "while (";
+  if (!m_pCondition->ToJavaScript(js, ReturnType::kInfered))
     return false;
 
-  if (!m_pExpression->ToJavaScript(javascript))
+  *js << ")\n";
+  if (CXFA_IsTooBig(js))
     return false;
-  return !CXFA_IsTooBig(javascript);
+
+  if (!m_pExpression->ToJavaScript(js, type))
+    return false;
+
+  return !CXFA_IsTooBig(js);
 }
 
-bool CXFA_FMWhileExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
+CXFA_FMBreakExpression::CXFA_FMBreakExpression() : CXFA_FMExpression() {}
+
+CXFA_FMBreakExpression::~CXFA_FMBreakExpression() = default;
+
+bool CXFA_FMBreakExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                          ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"while (";
-  if (!m_pCondition->ToJavaScript(javascript))
-    return false;
-  javascript << L")\n";
-  if (CXFA_IsTooBig(javascript))
-    return false;
-
-  if (!m_pExpression->ToImpliedReturnJS(javascript))
-    return false;
-  return !CXFA_IsTooBig(javascript);
+  *js << "pfm_ret = 0;\nbreak;\n";
+  return !CXFA_IsTooBig(js);
 }
 
-CXFA_FMBreakExpression::CXFA_FMBreakExpression(uint32_t line)
-    : CXFA_FMExpression(line, XFA_FM_EXPTYPE_BREAK) {}
+CXFA_FMContinueExpression::CXFA_FMContinueExpression() : CXFA_FMExpression() {}
 
-CXFA_FMBreakExpression::~CXFA_FMBreakExpression() {}
+CXFA_FMContinueExpression::~CXFA_FMContinueExpression() = default;
 
-bool CXFA_FMBreakExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMContinueExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                             ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"break;\n";
-  return !CXFA_IsTooBig(javascript);
-}
-
-bool CXFA_FMBreakExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"break;\n";
-  return !CXFA_IsTooBig(javascript);
-}
-
-CXFA_FMContinueExpression::CXFA_FMContinueExpression(uint32_t line)
-    : CXFA_FMExpression(line, XFA_FM_EXPTYPE_CONTINUE) {}
-
-CXFA_FMContinueExpression::~CXFA_FMContinueExpression() {}
-
-bool CXFA_FMContinueExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"continue;\n";
-  return !CXFA_IsTooBig(javascript);
-}
-
-bool CXFA_FMContinueExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"continue;\n";
-  return !CXFA_IsTooBig(javascript);
+  *js << "pfm_ret = 0;\ncontinue;\n";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMForExpression::CXFA_FMForExpression(
-    uint32_t line,
-    const WideStringView& wsVariant,
+    WideStringView wsVariant,
     std::unique_ptr<CXFA_FMSimpleExpression> pAssignment,
     std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
     int32_t iDirection,
     std::unique_ptr<CXFA_FMSimpleExpression> pStep,
     std::unique_ptr<CXFA_FMExpression> pList)
-    : CXFA_FMLoopExpression(line),
+    : CXFA_FMExpression(),
       m_wsVariant(wsVariant),
       m_pAssignment(std::move(pAssignment)),
       m_pAccessor(std::move(pAccessor)),
@@ -530,242 +357,92 @@
       m_pStep(std::move(pStep)),
       m_pList(std::move(pList)) {}
 
-CXFA_FMForExpression::~CXFA_FMForExpression() {}
+CXFA_FMForExpression::~CXFA_FMForExpression() = default;
 
-bool CXFA_FMForExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMForExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"{\nvar ";
-  WideString tempVariant;
-  if (m_wsVariant[0] == L'!') {
-    tempVariant = EXCLAMATION_IN_IDENTIFIER +
-                  m_wsVariant.Right(m_wsVariant.GetLength() - 1);
-    javascript << tempVariant;
-  } else {
-    tempVariant = m_wsVariant;
-    javascript << m_wsVariant;
-  }
-  javascript << L" = null;\n";
-  javascript << L"for (";
-  javascript << tempVariant;
-  javascript << L" = ";
-  javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-  javascript << L"(";
-  if (!m_pAssignment->ToJavaScript(javascript))
-    return false;
-  javascript << L"); ";
-  javascript << tempVariant;
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
 
-  javascript << (m_bDirection ? kLessEqual : kGreaterEqual);
-  javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-  javascript << L"(";
-  if (!m_pAccessor->ToJavaScript(javascript))
-    return false;
-  javascript << L"); ";
-  javascript << tempVariant;
-  javascript << (m_bDirection ? kPlusEqual : kMinusEqual);
-  if (CXFA_IsTooBig(javascript))
-    return false;
+  *js << "{\n";
 
+  WideString tmpName = IdentifierToName(m_wsVariant);
+  *js << "var " << tmpName << " = null;\n";
+
+  *js << "for (" << tmpName << " = pfm_rt.get_val(";
+  if (!m_pAssignment->ToJavaScript(js, ReturnType::kInfered))
+    return false;
+  *js << "); ";
+
+  *js << tmpName << (m_bDirection ? kLessEqual : kGreaterEqual);
+  *js << "pfm_rt.get_val(";
+  if (!m_pAccessor->ToJavaScript(js, ReturnType::kInfered))
+    return false;
+  *js << "); ";
+
+  *js << tmpName << (m_bDirection ? kPlusEqual : kMinusEqual);
   if (m_pStep) {
-    javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-    javascript << L"(";
-    if (!m_pStep->ToJavaScript(javascript))
+    *js << "pfm_rt.get_val(";
+    if (!m_pStep->ToJavaScript(js, ReturnType::kInfered))
       return false;
-    javascript << L")";
+    *js << ")";
   } else {
-    javascript << L"1";
+    *js << "1";
   }
-  javascript << L")\n";
-  if (!m_pList->ToJavaScript(javascript))
-    return false;
-  javascript << L"}\n";
-  return !CXFA_IsTooBig(javascript);
-}
-
-bool CXFA_FMForExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  *js << ")\n";
+  if (CXFA_IsTooBig(js))
     return false;
 
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"{\nvar ";
-  WideString tempVariant;
-  if (m_wsVariant[0] == L'!') {
-    tempVariant = EXCLAMATION_IN_IDENTIFIER +
-                  m_wsVariant.Right(m_wsVariant.GetLength() - 1);
-    javascript << tempVariant;
-  } else {
-    tempVariant = m_wsVariant;
-    javascript << m_wsVariant;
-  }
-  javascript << L" = null;\n";
-  javascript << L"for (";
-  javascript << tempVariant;
-  javascript << L" = ";
-  javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-  javascript << L"(";
-  if (!m_pAssignment->ToJavaScript(javascript))
-    return false;
-  javascript << L"); ";
-  javascript << tempVariant;
-
-  javascript << (m_bDirection ? kLessEqual : kGreaterEqual);
-  javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-  javascript << L"(";
-  if (!m_pAccessor->ToJavaScript(javascript))
-    return false;
-  javascript << L"); ";
-  javascript << tempVariant;
-  javascript << L" += ";
-  javascript << (m_bDirection ? kPlusEqual : kMinusEqual);
-  if (CXFA_IsTooBig(javascript))
+  if (!m_pList->ToJavaScript(js, type))
     return false;
 
-  if (m_pStep) {
-    javascript << XFA_FM_EXPTypeToString(GETFMVALUE);
-    javascript << L"(";
-    if (!m_pStep->ToJavaScript(javascript))
-      return false;
-    javascript << L")";
-    if (CXFA_IsTooBig(javascript))
-      return false;
-  } else {
-    javascript << L"1";
-  }
-  javascript << L")\n";
-  if (!m_pList->ToImpliedReturnJS(javascript))
-    return false;
-  javascript << L"}\n";
-  return !CXFA_IsTooBig(javascript);
+  *js << "}\n";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMForeachExpression::CXFA_FMForeachExpression(
-    uint32_t line,
-    const WideStringView& wsIdentifier,
+    WideStringView wsIdentifier,
     std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pAccessors,
     std::unique_ptr<CXFA_FMExpression> pList)
-    : CXFA_FMLoopExpression(line),
+    : CXFA_FMExpression(),
       m_wsIdentifier(wsIdentifier),
       m_pAccessors(std::move(pAccessors)),
       m_pList(std::move(pList)) {}
 
-CXFA_FMForeachExpression::~CXFA_FMForeachExpression() {}
+CXFA_FMForeachExpression::~CXFA_FMForeachExpression() = default;
 
-bool CXFA_FMForeachExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMForeachExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                            ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"{\n";
-  javascript << L"var ";
-  if (m_wsIdentifier[0] == L'!') {
-    WideString tempIdentifier =
-        EXCLAMATION_IN_IDENTIFIER +
-        m_wsIdentifier.Right(m_wsIdentifier.GetLength() - 1);
-    javascript << tempIdentifier;
-  } else {
-    javascript << m_wsIdentifier;
-  }
-  javascript << L" = null;\n";
-  javascript << L"var ";
-  javascript << RUNTIMEBLOCKTEMPARRAY;
-  javascript << L" = ";
-  javascript << XFA_FM_EXPTypeToString(CONCATFMOBJECT);
-  javascript << L"(";
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = 0;\n";
 
+  *js << "{\n";
+
+  WideString tmpName = IdentifierToName(m_wsIdentifier);
+  *js << "var " << tmpName << " = null;\n";
+  *js << "var pfm_ary = pfm_rt.concat_obj(";
   for (const auto& expr : m_pAccessors) {
-    if (!expr->ToJavaScript(javascript))
+    if (!expr->ToJavaScript(js, ReturnType::kInfered))
       return false;
     if (expr != m_pAccessors.back())
-      javascript << L", ";
+      *js << ", ";
   }
-  javascript << L");\n";
-  javascript << L"var ";
-  javascript << RUNTIMEBLOCKTEMPARRAYINDEX;
-  javascript << (L" = 0;\n");
-  javascript << L"while(";
-  javascript << RUNTIMEBLOCKTEMPARRAYINDEX;
-  javascript << L" < ";
-  javascript << RUNTIMEBLOCKTEMPARRAY;
-  javascript << L".length)\n{\n";
-  if (m_wsIdentifier[0] == L'!') {
-    WideString tempIdentifier =
-        EXCLAMATION_IN_IDENTIFIER +
-        m_wsIdentifier.Right(m_wsIdentifier.GetLength() - 1);
-    javascript << tempIdentifier;
-  } else {
-    javascript << m_wsIdentifier;
-  }
-  javascript << L" = ";
-  javascript << RUNTIMEBLOCKTEMPARRAY;
-  javascript << L"[";
-  javascript << RUNTIMEBLOCKTEMPARRAYINDEX;
-  javascript << L"++];\n";
-  if (!m_pList->ToJavaScript(javascript))
-    return false;
-  javascript << L"}\n";
-  javascript << L"}\n";
-  return !CXFA_IsTooBig(javascript);
-}
+  *js << ");\n";
 
-bool CXFA_FMForeachExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  *js << "var pfm_ary_idx = 0;\n";
+  *js << "while(pfm_ary_idx < pfm_ary.length)\n{\n";
+  *js << tmpName << " = pfm_ary[pfm_ary_idx++];\n";
+  if (!m_pList->ToJavaScript(js, type))
     return false;
+  *js << "}\n";  // while
 
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = 0;\n";
-  javascript << L"{\n";
-  javascript << L"var ";
-  if (m_wsIdentifier[0] == L'!') {
-    WideString tempIdentifier =
-        EXCLAMATION_IN_IDENTIFIER +
-        m_wsIdentifier.Right(m_wsIdentifier.GetLength() - 1);
-    javascript << tempIdentifier;
-  } else {
-    javascript << m_wsIdentifier;
-  }
-  javascript << L" = null;\n";
-  javascript << L"var ";
-  javascript << RUNTIMEBLOCKTEMPARRAY;
-  javascript << L" = ";
-  javascript << XFA_FM_EXPTypeToString(CONCATFMOBJECT);
-  javascript << L"(";
-  for (const auto& expr : m_pAccessors) {
-    if (!expr->ToJavaScript(javascript))
-      return false;
-    if (expr != m_pAccessors.back())
-      javascript << L", ";
-  }
-  javascript << L");\n";
-  javascript << L"var ";
-  javascript << RUNTIMEBLOCKTEMPARRAYINDEX;
-  javascript << L" = 0;\n";
-  javascript << L"while(";
-  javascript << RUNTIMEBLOCKTEMPARRAYINDEX;
-  javascript << L" < ";
-  javascript << RUNTIMEBLOCKTEMPARRAY;
-  javascript << L".length)\n{\n";
-  if (m_wsIdentifier[0] == L'!') {
-    WideString tempIdentifier =
-        EXCLAMATION_IN_IDENTIFIER +
-        m_wsIdentifier.Right(m_wsIdentifier.GetLength() - 1);
-    javascript << tempIdentifier;
-  } else {
-    javascript << m_wsIdentifier;
-  }
-  javascript << L" = ";
-  javascript << RUNTIMEBLOCKTEMPARRAY;
-  javascript << L"[";
-  javascript << RUNTIMEBLOCKTEMPARRAYINDEX;
-  javascript << L"++];\n";
-  if (!m_pList->ToImpliedReturnJS(javascript))
-    return false;
-  javascript << L"}\n";
-  javascript << L"}\n";
-  return !CXFA_IsTooBig(javascript);
+  *js << "}\n";  // block
+  return !CXFA_IsTooBig(js);
 }
diff --git a/xfa/fxfa/fm2js/cxfa_fmexpression.h b/xfa/fxfa/fm2js/cxfa_fmexpression.h
index cfbba3a..2761ef5 100644
--- a/xfa/fxfa/fm2js/cxfa_fmexpression.h
+++ b/xfa/fxfa/fm2js/cxfa_fmexpression.h
@@ -12,170 +12,143 @@
 
 #include "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h"
 
-enum XFA_FM_EXPTYPE {
-  XFA_FM_EXPTYPE_UNKNOWN,
-  XFA_FM_EXPTYPE_FUNC,
-  XFA_FM_EXPTYPE_VAR,
-  XFA_FM_EXPTYPE_EXP,
-  XFA_FM_EXPTYPE_BLOCK,
-  XFA_FM_EXPTYPE_IF,
-  XFA_FM_EXPTYPE_BREAK,
-  XFA_FM_EXPTYPE_CONTINUE,
-};
-
 class CFX_WideTextBuf;
 
 class CXFA_FMExpression {
  public:
-  explicit CXFA_FMExpression(uint32_t line);
-  CXFA_FMExpression(uint32_t line, XFA_FM_EXPTYPE type);
-  virtual ~CXFA_FMExpression() {}
+  virtual ~CXFA_FMExpression() = default;
+  virtual bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) = 0;
 
-  virtual bool ToJavaScript(CFX_WideTextBuf& javascript);
-  virtual bool ToImpliedReturnJS(CFX_WideTextBuf&);
-  uint32_t GetLine() { return m_line; }
-  XFA_FM_EXPTYPE GetExpType() const { return m_type; }
-
- private:
-  XFA_FM_EXPTYPE m_type;
-  uint32_t m_line;
+ protected:
+  CXFA_FMExpression();
 };
 
-class CXFA_FMFunctionDefinition : public CXFA_FMExpression {
+class CXFA_FMFunctionDefinition final : public CXFA_FMExpression {
  public:
-  // Takes ownership of |arguments| and |expressions|.
   CXFA_FMFunctionDefinition(
-      uint32_t line,
-      bool isGlobal,
-      const WideStringView& wsName,
+      WideStringView wsName,
       std::vector<WideStringView>&& arguments,
       std::vector<std::unique_ptr<CXFA_FMExpression>>&& expressions);
   ~CXFA_FMFunctionDefinition() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsName;
   std::vector<WideStringView> m_pArguments;
   std::vector<std::unique_ptr<CXFA_FMExpression>> m_pExpressions;
-  bool m_isGlobal;
 };
 
-class CXFA_FMVarExpression : public CXFA_FMExpression {
+class CXFA_FMAST {
  public:
-  CXFA_FMVarExpression(uint32_t line,
-                       const WideStringView& wsName,
-                       std::unique_ptr<CXFA_FMExpression> pInit);
+  explicit CXFA_FMAST(
+      std::vector<std::unique_ptr<CXFA_FMExpression>> expressions);
+  ~CXFA_FMAST();
+
+  bool ToJavaScript(CFX_WideTextBuf* js);
+
+ private:
+  std::vector<std::unique_ptr<CXFA_FMExpression>> expressions_;
+};
+
+class CXFA_FMVarExpression final : public CXFA_FMExpression {
+ public:
+  CXFA_FMVarExpression(WideStringView wsName,
+                       std::unique_ptr<CXFA_FMSimpleExpression> pInit);
   ~CXFA_FMVarExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsName;
-  std::unique_ptr<CXFA_FMExpression> m_pInit;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pInit;
 };
 
-class CXFA_FMExpExpression : public CXFA_FMExpression {
+class CXFA_FMExpExpression final : public CXFA_FMExpression {
  public:
-  CXFA_FMExpExpression(uint32_t line,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExpression);
+  explicit CXFA_FMExpExpression(
+      std::unique_ptr<CXFA_FMSimpleExpression> pExpression);
   ~CXFA_FMExpExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   std::unique_ptr<CXFA_FMSimpleExpression> m_pExpression;
 };
 
-class CXFA_FMBlockExpression : public CXFA_FMExpression {
+class CXFA_FMBlockExpression final : public CXFA_FMExpression {
  public:
   CXFA_FMBlockExpression(
-      uint32_t line,
       std::vector<std::unique_ptr<CXFA_FMExpression>>&& pExpressionList);
   ~CXFA_FMBlockExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   std::vector<std::unique_ptr<CXFA_FMExpression>> m_ExpressionList;
 };
 
-class CXFA_FMDoExpression : public CXFA_FMExpression {
+class CXFA_FMDoExpression final : public CXFA_FMExpression {
  public:
-  CXFA_FMDoExpression(uint32_t line, std::unique_ptr<CXFA_FMExpression> pList);
+  explicit CXFA_FMDoExpression(std::unique_ptr<CXFA_FMExpression> pList);
   ~CXFA_FMDoExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   std::unique_ptr<CXFA_FMExpression> m_pList;
 };
 
-class CXFA_FMIfExpression : public CXFA_FMExpression {
+class CXFA_FMIfExpression final : public CXFA_FMExpression {
  public:
-  CXFA_FMIfExpression(uint32_t line,
-                      std::unique_ptr<CXFA_FMSimpleExpression> pExpression,
-                      std::unique_ptr<CXFA_FMExpression> pIfExpression,
-                      std::unique_ptr<CXFA_FMExpression> pElseExpression);
+  CXFA_FMIfExpression(
+      std::unique_ptr<CXFA_FMSimpleExpression> pExpression,
+      std::unique_ptr<CXFA_FMExpression> pIfExpression,
+      std::vector<std::unique_ptr<CXFA_FMIfExpression>> pElseIfExpressions,
+      std::unique_ptr<CXFA_FMExpression> pElseExpression);
   ~CXFA_FMIfExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   std::unique_ptr<CXFA_FMSimpleExpression> m_pExpression;
   std::unique_ptr<CXFA_FMExpression> m_pIfExpression;
+  std::vector<std::unique_ptr<CXFA_FMIfExpression>> m_pElseIfExpressions;
   std::unique_ptr<CXFA_FMExpression> m_pElseExpression;
 };
 
-class CXFA_FMLoopExpression : public CXFA_FMExpression {
+class CXFA_FMWhileExpression final : public CXFA_FMExpression {
  public:
-  explicit CXFA_FMLoopExpression(uint32_t line) : CXFA_FMExpression(line) {}
-  ~CXFA_FMLoopExpression() override;
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
-};
-
-class CXFA_FMWhileExpression : public CXFA_FMLoopExpression {
- public:
-  CXFA_FMWhileExpression(uint32_t line,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pCodition,
+  CXFA_FMWhileExpression(std::unique_ptr<CXFA_FMSimpleExpression> pCodition,
                          std::unique_ptr<CXFA_FMExpression> pExpression);
   ~CXFA_FMWhileExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   std::unique_ptr<CXFA_FMSimpleExpression> m_pCondition;
   std::unique_ptr<CXFA_FMExpression> m_pExpression;
 };
 
-class CXFA_FMBreakExpression : public CXFA_FMExpression {
+class CXFA_FMBreakExpression final : public CXFA_FMExpression {
  public:
-  explicit CXFA_FMBreakExpression(uint32_t line);
+  CXFA_FMBreakExpression();
   ~CXFA_FMBreakExpression() override;
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 };
 
-class CXFA_FMContinueExpression : public CXFA_FMExpression {
+class CXFA_FMContinueExpression final : public CXFA_FMExpression {
  public:
-  explicit CXFA_FMContinueExpression(uint32_t line);
+  CXFA_FMContinueExpression();
   ~CXFA_FMContinueExpression() override;
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 };
 
-class CXFA_FMForExpression : public CXFA_FMLoopExpression {
+class CXFA_FMForExpression final : public CXFA_FMExpression {
  public:
-  CXFA_FMForExpression(uint32_t line,
-                       const WideStringView& wsVariant,
+  CXFA_FMForExpression(WideStringView wsVariant,
                        std::unique_ptr<CXFA_FMSimpleExpression> pAssignment,
                        std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
                        int32_t iDirection,
@@ -183,8 +156,7 @@
                        std::unique_ptr<CXFA_FMExpression> pList);
   ~CXFA_FMForExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsVariant;
@@ -195,18 +167,16 @@
   std::unique_ptr<CXFA_FMExpression> m_pList;
 };
 
-class CXFA_FMForeachExpression : public CXFA_FMLoopExpression {
+class CXFA_FMForeachExpression final : public CXFA_FMExpression {
  public:
   // Takes ownership of |pAccessors|.
   CXFA_FMForeachExpression(
-      uint32_t line,
-      const WideStringView& wsIdentifier,
+      WideStringView wsIdentifier,
       std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pAccessors,
       std::unique_ptr<CXFA_FMExpression> pList);
   ~CXFA_FMForeachExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf&) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsIdentifier;
diff --git a/xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp
new file mode 100644
index 0000000..9d1a64c
--- /dev/null
+++ b/xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp
@@ -0,0 +1,68 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/fm2js/cxfa_fmexpression.h"
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/cfx_widetextbuf.h"
+#include "core/fxcrt/fx_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
+
+TEST(CXFA_FMExpressionTest, VarExpressionInitNull) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf accumulator;
+
+  CXFA_FMVarExpression(L"s", nullptr)
+      .ToJavaScript(&accumulator, ReturnType::kInfered);
+  EXPECT_STREQ(
+      LR"***(var s = "";
+)***",
+      accumulator.MakeString().c_str());
+}
+
+TEST(CXFA_FMExpressionTest, VarExpressionInitBlank) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf accumulator;
+
+  auto init = pdfium::MakeUnique<CXFA_FMStringExpression>(LR"("")");
+  CXFA_FMVarExpression(L"s", std::move(init))
+      .ToJavaScript(&accumulator, ReturnType::kInfered);
+  EXPECT_STREQ(
+      LR"***(var s = "";
+s = pfm_rt.var_filter(s);
+)***",
+      accumulator.MakeString().c_str());
+}
+
+TEST(CXFA_FMExpressionTest, VarExpressionInitString) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf accumulator;
+
+  auto init = pdfium::MakeUnique<CXFA_FMStringExpression>(LR"("foo")");
+  CXFA_FMVarExpression(L"s", std::move(init))
+      .ToJavaScript(&accumulator, ReturnType::kInfered);
+  EXPECT_STREQ(
+      LR"***(var s = "foo";
+s = pfm_rt.var_filter(s);
+)***",
+      accumulator.MakeString().c_str());
+}
+
+TEST(CXFA_FMExpressionTest, VarExpressionInitNumeric) {
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf accumulator;
+
+  auto init = pdfium::MakeUnique<CXFA_FMNumberExpression>(L"112");
+  CXFA_FMVarExpression(L"s", std::move(init))
+      .ToJavaScript(&accumulator, ReturnType::kInfered);
+  EXPECT_STREQ(
+      LR"***(var s = 112;
+s = pfm_rt.var_filter(s);
+)***",
+      accumulator.MakeString().c_str());
+}
diff --git a/xfa/fxfa/fm2js/cxfa_fmlexer.cpp b/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
index 675abc3..efb59f2 100644
--- a/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmlexer.cpp
@@ -1,4 +1,4 @@
-// Copright 2014 PDFium Authors. All rights reserved.
+// 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.
 
@@ -9,8 +9,6 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/icu/source/common/unicode/uchar.h"
 
 namespace {
 
@@ -20,14 +18,14 @@
 }
 
 bool IsIdentifierCharacter(wchar_t c) {
-  return u_isalnum(c) || c == 0x005F ||  // '_'
-         c == 0x0024;                    // '$'
+  return FXSYS_iswalnum(c) || c == 0x005F ||  // '_'
+         c == 0x0024;                         // '$'
 }
 
 bool IsInitialIdentifierCharacter(wchar_t c) {
-  return u_isalpha(c) || c == 0x005F ||  // '_'
-         c == 0x0024 ||                  // '$'
-         c == 0x0021;                    // '!'
+  return FXSYS_iswalpha(c) || c == 0x005F ||  // '_'
+         c == 0x0024 ||                       // '$'
+         c == 0x0021;                         // '!'
 }
 
 bool IsWhitespaceCharacter(wchar_t c) {
@@ -38,158 +36,125 @@
 }
 
 const XFA_FMKeyword keyWords[] = {
-    {TOKand, 0x00000026, L"&"},
-    {TOKlparen, 0x00000028, L"("},
-    {TOKrparen, 0x00000029, L")"},
-    {TOKmul, 0x0000002a, L"*"},
-    {TOKplus, 0x0000002b, L"+"},
-    {TOKcomma, 0x0000002c, L","},
-    {TOKminus, 0x0000002d, L"-"},
-    {TOKdot, 0x0000002e, L"."},
-    {TOKdiv, 0x0000002f, L"/"},
-    {TOKlt, 0x0000003c, L"<"},
-    {TOKassign, 0x0000003d, L"="},
-    {TOKgt, 0x0000003e, L">"},
-    {TOKlbracket, 0x0000005b, L"["},
-    {TOKrbracket, 0x0000005d, L"]"},
-    {TOKor, 0x0000007c, L"|"},
-    {TOKdotscream, 0x0000ec11, L".#"},
-    {TOKdotstar, 0x0000ec18, L".*"},
-    {TOKdotdot, 0x0000ec1c, L".."},
-    {TOKle, 0x000133f9, L"<="},
-    {TOKne, 0x000133fa, L"<>"},
-    {TOKeq, 0x0001391a, L"=="},
-    {TOKge, 0x00013e3b, L">="},
-    {TOKdo, 0x00020153, L"do"},
-    {TOKkseq, 0x00020676, L"eq"},
-    {TOKksge, 0x000210ac, L"ge"},
-    {TOKksgt, 0x000210bb, L"gt"},
-    {TOKif, 0x00021aef, L"if"},
-    {TOKin, 0x00021af7, L"in"},
-    {TOKksle, 0x00022a51, L"le"},
-    {TOKkslt, 0x00022a60, L"lt"},
-    {TOKksne, 0x00023493, L"ne"},
-    {TOKksor, 0x000239c1, L"or"},
-    {TOKnull, 0x052931bb, L"null"},
-    {TOKbreak, 0x05518c25, L"break"},
-    {TOKksand, 0x09f9db33, L"and"},
-    {TOKend, 0x0a631437, L"end"},
-    {TOKeof, 0x0a63195a, L"eof"},
-    {TOKfor, 0x0a7d67a7, L"for"},
-    {TOKnan, 0x0b4f91dd, L"nan"},
-    {TOKksnot, 0x0b4fd9b1, L"not"},
-    {TOKvar, 0x0c2203e9, L"var"},
-    {TOKthen, 0x2d5738cf, L"then"},
-    {TOKelse, 0x45f65ee9, L"else"},
-    {TOKexit, 0x4731d6ba, L"exit"},
-    {TOKdownto, 0x4caadc3b, L"downto"},
-    {TOKreturn, 0x4db8bd60, L"return"},
-    {TOKinfinity, 0x5c0a010a, L"infinity"},
-    {TOKendwhile, 0x5c64bff0, L"endwhile"},
-    {TOKforeach, 0x67e31f38, L"foreach"},
-    {TOKendfunc, 0x68f984a3, L"endfunc"},
-    {TOKelseif, 0x78253218, L"elseif"},
-    {TOKwhile, 0x84229259, L"while"},
-    {TOKendfor, 0x8ab49d7e, L"endfor"},
-    {TOKthrow, 0x8db05c94, L"throw"},
-    {TOKstep, 0xa7a7887c, L"step"},
-    {TOKupto, 0xb5155328, L"upto"},
-    {TOKcontinue, 0xc0340685, L"continue"},
-    {TOKfunc, 0xcdce60ec, L"func"},
-    {TOKendif, 0xe0e8fee6, L"endif"},
+    {TOKdo, "do"},
+    {TOKkseq, "eq"},
+    {TOKksge, "ge"},
+    {TOKksgt, "gt"},
+    {TOKif, "if"},
+    {TOKin, "in"},
+    {TOKksle, "le"},
+    {TOKkslt, "lt"},
+    {TOKksne, "ne"},
+    {TOKksor, "or"},
+    {TOKnull, "null"},
+    {TOKbreak, "break"},
+    {TOKksand, "and"},
+    {TOKend, "end"},
+    {TOKeof, "eof"},
+    {TOKfor, "for"},
+    {TOKnan, "nan"},
+    {TOKksnot, "not"},
+    {TOKvar, "var"},
+    {TOKthen, "then"},
+    {TOKelse, "else"},
+    {TOKexit, "exit"},
+    {TOKdownto, "downto"},
+    {TOKreturn, "return"},
+    {TOKinfinity, "infinity"},
+    {TOKendwhile, "endwhile"},
+    {TOKforeach, "foreach"},
+    {TOKendfunc, "endfunc"},
+    {TOKelseif, "elseif"},
+    {TOKwhile, "while"},
+    {TOKendfor, "endfor"},
+    {TOKthrow, "throw"},
+    {TOKstep, "step"},
+    {TOKupto, "upto"},
+    {TOKcontinue, "continue"},
+    {TOKfunc, "func"},
+    {TOKendif, "endif"},
 };
 
-const XFA_FM_TOKEN KEYWORD_START = TOKdo;
-const XFA_FM_TOKEN KEYWORD_END = TOKendif;
-
-const wchar_t* tokenStrings[] = {
-    L"TOKand",        L"TOKlparen",     L"TOKrparen",   L"TOKmul",
-    L"TOKplus",       L"TOKcomma",      L"TOKminus",    L"TOKdot",
-    L"TOKdiv",        L"TOKlt",         L"TOKassign",   L"TOKgt",
-    L"TOKlbracket",   L"TOKrbracket",   L"TOKor",       L"TOKdotscream",
-    L"TOKdotstar",    L"TOKdotdot",     L"TOKle",       L"TOKne",
-    L"TOKeq",         L"TOKge",         L"TOKdo",       L"TOKkseq",
-    L"TOKksge",       L"TOKksgt",       L"TOKif",       L"TOKin",
-    L"TOKksle",       L"TOKkslt",       L"TOKksne",     L"TOKksor",
-    L"TOKnull",       L"TOKbreak",      L"TOKksand",    L"TOKend",
-    L"TOKeof",        L"TOKfor",        L"TOKnan",      L"TOKksnot",
-    L"TOKvar",        L"TOKthen",       L"TOKelse",     L"TOKexit",
-    L"TOKdownto",     L"TOKreturn",     L"TOKinfinity", L"TOKendwhile",
-    L"TOKforeach",    L"TOKendfunc",    L"TOKelseif",   L"TOKwhile",
-    L"TOKendfor",     L"TOKthrow",      L"TOKstep",     L"TOKupto",
-    L"TOKcontinue",   L"TOKfunc",       L"TOKendif",    L"TOKstar",
-    L"TOKidentifier", L"TOKunderscore", L"TOKdollar",   L"TOKexclamation",
-    L"TOKcall",       L"TOKstring",     L"TOKnumber",   L"TOKreserver",
+#ifndef NDEBUG
+const char* const tokenStrings[] = {
+    "TOKand",        "TOKlparen",     "TOKrparen",   "TOKmul",
+    "TOKplus",       "TOKcomma",      "TOKminus",    "TOKdot",
+    "TOKdiv",        "TOKlt",         "TOKassign",   "TOKgt",
+    "TOKlbracket",   "TOKrbracket",   "TOKor",       "TOKdotscream",
+    "TOKdotstar",    "TOKdotdot",     "TOKle",       "TOKne",
+    "TOKeq",         "TOKge",         "TOKdo",       "TOKkseq",
+    "TOKksge",       "TOKksgt",       "TOKif",       "TOKin",
+    "TOKksle",       "TOKkslt",       "TOKksne",     "TOKksor",
+    "TOKnull",       "TOKbreak",      "TOKksand",    "TOKend",
+    "TOKeof",        "TOKfor",        "TOKnan",      "TOKksnot",
+    "TOKvar",        "TOKthen",       "TOKelse",     "TOKexit",
+    "TOKdownto",     "TOKreturn",     "TOKinfinity", "TOKendwhile",
+    "TOKforeach",    "TOKendfunc",    "TOKelseif",   "TOKwhile",
+    "TOKendfor",     "TOKthrow",      "TOKstep",     "TOKupto",
+    "TOKcontinue",   "TOKfunc",       "TOKendif",    "TOKstar",
+    "TOKidentifier", "TOKunderscore", "TOKdollar",   "TOKexclamation",
+    "TOKcall",       "TOKstring",     "TOKnumber",   "TOKreserver",
 };
+#endif  // NDEBUG
 
-XFA_FM_TOKEN TokenizeIdentifier(const WideStringView& str) {
-  uint32_t key = FX_HashCode_GetW(str, true);
-
-  const XFA_FMKeyword* end = std::begin(keyWords) + KEYWORD_END + 1;
+XFA_FM_TOKEN TokenizeIdentifier(WideStringView str) {
   const XFA_FMKeyword* result =
-      std::lower_bound(std::begin(keyWords) + KEYWORD_START, end, key,
-                       [](const XFA_FMKeyword& iter, const uint32_t& val) {
-                         return iter.m_hash < val;
-                       });
-  if (result != end && result->m_hash == key)
+      std::find_if(std::begin(keyWords), std::end(keyWords),
+                   [str](const XFA_FMKeyword& iter) {
+                     return str.EqualsASCII(iter.m_keyword);
+                   });
+  if (result != std::end(keyWords) && str.EqualsASCII(result->m_keyword))
     return result->m_type;
   return TOKidentifier;
 }
 
 }  // namespace
 
-CXFA_FMToken::CXFA_FMToken() : m_type(TOKreserver), m_line_num(1) {}
+CXFA_FMToken::CXFA_FMToken(XFA_FM_TOKEN token) : m_type(token) {}
 
-CXFA_FMToken::CXFA_FMToken(uint32_t line_num)
-    : m_type(TOKreserver), m_line_num(line_num) {}
+CXFA_FMToken::CXFA_FMToken() : CXFA_FMToken(TOKreserver) {}
 
-CXFA_FMToken::~CXFA_FMToken() {}
+CXFA_FMToken::CXFA_FMToken(const CXFA_FMToken&) = default;
 
+CXFA_FMToken::~CXFA_FMToken() = default;
+
+#ifndef NDEBUG
 WideString CXFA_FMToken::ToDebugString() const {
-  WideString str(L"type = ");
-  str += tokenStrings[m_type];
-  str += L", string = ";
+  WideString str = WideString::FromASCII("type = ");
+  str += WideString::FromASCII(tokenStrings[m_type]);
+  str += WideString::FromASCII(", string = ");
   str += m_string;
-  str += L", line_num = ";
-  str += std::to_wstring(m_line_num).c_str();
   return str;
 }
+#endif  // NDEBUG
 
-CXFA_FMLexer::CXFA_FMLexer(const WideStringView& wsFormCalc)
-    : m_cursor(wsFormCalc.unterminated_c_str()),
-      m_end(m_cursor + wsFormCalc.GetLength() - 1),
-      m_current_line(1),
-      m_lexer_error(false) {}
+CXFA_FMLexer::CXFA_FMLexer(WideStringView wsFormCalc)
+    : m_spInput(wsFormCalc.span()) {}
 
-CXFA_FMLexer::~CXFA_FMLexer() {}
+CXFA_FMLexer::~CXFA_FMLexer() = default;
 
-std::unique_ptr<CXFA_FMToken> CXFA_FMLexer::NextToken() {
-  if (m_lexer_error)
-    return nullptr;
+CXFA_FMToken CXFA_FMLexer::NextToken() {
+  if (m_bLexerError)
+    return CXFA_FMToken();
 
-  m_token = pdfium::MakeUnique<CXFA_FMToken>(m_current_line);
-  while (m_cursor <= m_end && *m_cursor) {
-    if (!IsFormCalcCharacter(*m_cursor)) {
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
       RaiseError();
-      return nullptr;
+      return CXFA_FMToken();
     }
 
-    switch (*m_cursor) {
+    switch (m_spInput[m_nCursor]) {
       case '\n':
-        ++m_current_line;
-        m_token->m_line_num = m_current_line;
-        ++m_cursor;
+        ++m_nCursor;
         break;
       case '\r':
-        ++m_cursor;
+        ++m_nCursor;
         break;
       case ';':
         AdvanceForComment();
         break;
       case '"':
-        m_token->m_type = TOKstring;
-        AdvanceForString();
-        return std::move(m_token);
+        return AdvanceForString();
       case '0':
       case '1':
       case '2':
@@ -200,257 +165,232 @@
       case '7':
       case '8':
       case '9':
-        m_token->m_type = TOKnumber;
-        AdvanceForNumber();
-        return std::move(m_token);
+        return AdvanceForNumber();
       case '=':
-        ++m_cursor;
-        if (m_cursor > m_end) {
-          m_token->m_type = TOKassign;
-          return std::move(m_token);
-        }
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return CXFA_FMToken(TOKassign);
 
-        if (!IsFormCalcCharacter(*m_cursor)) {
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
           RaiseError();
-          return nullptr;
+          return CXFA_FMToken();
         }
-        if (*m_cursor == '=') {
-          m_token->m_type = TOKeq;
-          ++m_cursor;
-        } else {
-          m_token->m_type = TOKassign;
+        if (m_spInput[m_nCursor] == '=') {
+          ++m_nCursor;
+          return CXFA_FMToken(TOKeq);
         }
-        return std::move(m_token);
+        return CXFA_FMToken(TOKassign);
       case '<':
-        ++m_cursor;
-        if (m_cursor > m_end) {
-          m_token->m_type = TOKlt;
-          return std::move(m_token);
-        }
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return CXFA_FMToken(TOKlt);
 
-        if (!IsFormCalcCharacter(*m_cursor)) {
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
           RaiseError();
-          return nullptr;
+          return CXFA_FMToken();
         }
-        if (*m_cursor == '=') {
-          m_token->m_type = TOKle;
-          ++m_cursor;
-        } else if (*m_cursor == '>') {
-          m_token->m_type = TOKne;
-          ++m_cursor;
-        } else {
-          m_token->m_type = TOKlt;
+        if (m_spInput[m_nCursor] == '=') {
+          ++m_nCursor;
+          return CXFA_FMToken(TOKle);
         }
-        return std::move(m_token);
+        if (m_spInput[m_nCursor] == '>') {
+          ++m_nCursor;
+          return CXFA_FMToken(TOKne);
+        }
+        return CXFA_FMToken(TOKlt);
       case '>':
-        ++m_cursor;
-        if (m_cursor > m_end) {
-          m_token->m_type = TOKgt;
-          return std::move(m_token);
-        }
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return CXFA_FMToken(TOKgt);
 
-        if (!IsFormCalcCharacter(*m_cursor)) {
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
           RaiseError();
-          return nullptr;
+          return CXFA_FMToken();
         }
-        if (*m_cursor == '=') {
-          m_token->m_type = TOKge;
-          ++m_cursor;
-        } else {
-          m_token->m_type = TOKgt;
+        if (m_spInput[m_nCursor] == '=') {
+          ++m_nCursor;
+          return CXFA_FMToken(TOKge);
         }
-        return std::move(m_token);
+        return CXFA_FMToken(TOKgt);
       case ',':
-        m_token->m_type = TOKcomma;
-        ++m_cursor;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKcomma);
       case '(':
-        m_token->m_type = TOKlparen;
-        ++m_cursor;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKlparen);
       case ')':
-        m_token->m_type = TOKrparen;
-        ++m_cursor;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKrparen);
       case '[':
-        m_token->m_type = TOKlbracket;
-        ++m_cursor;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKlbracket);
       case ']':
-        m_token->m_type = TOKrbracket;
-        ++m_cursor;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKrbracket);
       case '&':
-        ++m_cursor;
-        m_token->m_type = TOKand;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKand);
       case '|':
-        ++m_cursor;
-        m_token->m_type = TOKor;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKor);
       case '+':
-        ++m_cursor;
-        m_token->m_type = TOKplus;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKplus);
       case '-':
-        ++m_cursor;
-        m_token->m_type = TOKminus;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKminus);
       case '*':
-        ++m_cursor;
-        m_token->m_type = TOKmul;
-        return std::move(m_token);
+        ++m_nCursor;
+        return CXFA_FMToken(TOKmul);
       case '/': {
-        ++m_cursor;
-        if (m_cursor > m_end) {
-          m_token->m_type = TOKdiv;
-          return std::move(m_token);
-        }
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return CXFA_FMToken(TOKdiv);
 
-        if (!IsFormCalcCharacter(*m_cursor)) {
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
           RaiseError();
-          return nullptr;
+          return CXFA_FMToken();
         }
-        if (*m_cursor != '/') {
-          m_token->m_type = TOKdiv;
-          return std::move(m_token);
-        }
+        if (m_spInput[m_nCursor] != '/')
+          return CXFA_FMToken(TOKdiv);
+
         AdvanceForComment();
         break;
       }
       case '.':
-        ++m_cursor;
-        if (m_cursor > m_end) {
-          m_token->m_type = TOKdot;
-          return std::move(m_token);
-        }
+        ++m_nCursor;
+        if (m_nCursor >= m_spInput.size())
+          return CXFA_FMToken(TOKdot);
 
-        if (!IsFormCalcCharacter(*m_cursor)) {
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
           RaiseError();
-          return nullptr;
+          return CXFA_FMToken();
         }
 
-        if (*m_cursor == '.') {
-          m_token->m_type = TOKdotdot;
-          ++m_cursor;
-        } else if (*m_cursor == '*') {
-          m_token->m_type = TOKdotstar;
-          ++m_cursor;
-        } else if (*m_cursor == '#') {
-          m_token->m_type = TOKdotscream;
-          ++m_cursor;
-        } else if (*m_cursor <= '9' && *m_cursor >= '0') {
-          m_token->m_type = TOKnumber;
-          --m_cursor;
-          AdvanceForNumber();
-        } else {
-          m_token->m_type = TOKdot;
+        if (m_spInput[m_nCursor] == '.') {
+          ++m_nCursor;
+          return CXFA_FMToken(TOKdotdot);
         }
-        return std::move(m_token);
+        if (m_spInput[m_nCursor] == '*') {
+          ++m_nCursor;
+          return CXFA_FMToken(TOKdotstar);
+        }
+        if (m_spInput[m_nCursor] == '#') {
+          ++m_nCursor;
+          return CXFA_FMToken(TOKdotscream);
+        }
+        if (FXSYS_IsDecimalDigit(m_spInput[m_nCursor])) {
+          --m_nCursor;
+          return AdvanceForNumber();
+        }
+        return CXFA_FMToken(TOKdot);
       default:
-        if (IsWhitespaceCharacter(*m_cursor)) {
-          ++m_cursor;
+        if (IsWhitespaceCharacter(m_spInput[m_nCursor])) {
+          ++m_nCursor;
           break;
         }
-        if (!IsInitialIdentifierCharacter(*m_cursor)) {
+        if (!IsInitialIdentifierCharacter(m_spInput[m_nCursor])) {
           RaiseError();
-          return nullptr;
+          return CXFA_FMToken();
         }
-        AdvanceForIdentifier();
-        return std::move(m_token);
+        return AdvanceForIdentifier();
     }
   }
-
-  // If there isn't currently a token type then mark it EOF.
-  if (m_token->m_type == TOKreserver)
-    m_token->m_type = TOKeof;
-  return std::move(m_token);
+  return CXFA_FMToken(TOKeof);
 }
 
-void CXFA_FMLexer::AdvanceForNumber() {
+CXFA_FMToken CXFA_FMLexer::AdvanceForNumber() {
   // This will set end to the character after the end of the number.
-  wchar_t* end = nullptr;
-  if (m_cursor)
-    wcstod(const_cast<wchar_t*>(m_cursor), &end);
-  if (!end || FXSYS_iswalpha(*end)) {
-    RaiseError();
-    return;
+  int32_t used_length = 0;
+  if (m_nCursor < m_spInput.size()) {
+    FXSYS_wcstof(&m_spInput[m_nCursor], m_spInput.size() - m_nCursor,
+                 &used_length);
   }
-
-  m_token->m_string =
-      WideStringView(m_cursor, static_cast<size_t>(end - m_cursor));
-  m_cursor = end;
+  size_t end = m_nCursor + used_length;
+  if (used_length == 0 ||
+      (end < m_spInput.size() && FXSYS_iswalpha(m_spInput[end]))) {
+    RaiseError();
+    return CXFA_FMToken();
+  }
+  CXFA_FMToken token(TOKnumber);
+  token.m_string =
+      WideStringView(m_spInput.subspan(m_nCursor, end - m_nCursor));
+  m_nCursor = end;
+  return token;
 }
 
-void CXFA_FMLexer::AdvanceForString() {
-  const wchar_t* start = m_cursor;
-  ++m_cursor;
-  while (m_cursor <= m_end && *m_cursor) {
-    if (!IsFormCalcCharacter(*m_cursor))
+CXFA_FMToken CXFA_FMLexer::AdvanceForString() {
+  CXFA_FMToken token(TOKstring);
+  size_t start = m_nCursor;
+  ++m_nCursor;
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor]))
       break;
 
-    if (*m_cursor == '"') {
+    if (m_spInput[m_nCursor] == '"') {
       // Check for escaped "s, i.e. "".
-      ++m_cursor;
+      ++m_nCursor;
       // If the end of the input has been reached it was not escaped.
-      if (m_cursor > m_end) {
-        m_token->m_string =
-            WideStringView(start, static_cast<size_t>(m_cursor - start));
-        return;
+      if (m_nCursor >= m_spInput.size()) {
+        token.m_string =
+            WideStringView(m_spInput.subspan(start, m_nCursor - start));
+        return token;
       }
       // If the next character is not a " then the end of the string has been
       // found.
-      if (*m_cursor != '"') {
-        if (!IsFormCalcCharacter(*m_cursor)) {
+      if (m_spInput[m_nCursor] != '"') {
+        if (!IsFormCalcCharacter(m_spInput[m_nCursor]))
           break;
-        }
-        m_token->m_string = WideStringView(start, (m_cursor - start));
-        return;
+
+        token.m_string =
+            WideStringView(m_spInput.subspan(start, m_nCursor - start));
+        return token;
       }
     }
-    ++m_cursor;
+    ++m_nCursor;
   }
 
   // Didn't find the end of the string.
   RaiseError();
+  return CXFA_FMToken();
 }
 
-void CXFA_FMLexer::AdvanceForIdentifier() {
-  const wchar_t* start = m_cursor;
-  ++m_cursor;
-  while (m_cursor <= m_end && *m_cursor) {
-    if (!IsFormCalcCharacter(*m_cursor)) {
+CXFA_FMToken CXFA_FMLexer::AdvanceForIdentifier() {
+  size_t start = m_nCursor;
+  ++m_nCursor;
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
       RaiseError();
-      return;
+      return CXFA_FMToken();
     }
-
-    if (!IsIdentifierCharacter(*m_cursor)) {
+    if (!IsIdentifierCharacter(m_spInput[m_nCursor]))
       break;
-    }
-    ++m_cursor;
+
+    ++m_nCursor;
   }
-  m_token->m_string =
-      WideStringView(start, static_cast<size_t>(m_cursor - start));
-  m_token->m_type = TokenizeIdentifier(m_token->m_string);
+
+  WideStringView str =
+      WideStringView(m_spInput.subspan(start, m_nCursor - start));
+  CXFA_FMToken token(TokenizeIdentifier(str));
+  token.m_string = str;
+  return token;
 }
 
 void CXFA_FMLexer::AdvanceForComment() {
-  m_cursor++;
-  while (m_cursor <= m_end && *m_cursor) {
-    if (!IsFormCalcCharacter(*m_cursor)) {
+  ++m_nCursor;
+  while (!IsComplete() && m_spInput[m_nCursor]) {
+    if (!IsFormCalcCharacter(m_spInput[m_nCursor])) {
       RaiseError();
       return;
     }
-
-    if (*m_cursor == L'\r') {
-      ++m_cursor;
+    if (m_spInput[m_nCursor] == L'\r') {
+      ++m_nCursor;
       return;
     }
-    if (*m_cursor == L'\n') {
-      ++m_current_line;
-      ++m_cursor;
+    if (m_spInput[m_nCursor] == L'\n') {
+      ++m_nCursor;
       return;
     }
-    ++m_cursor;
+    ++m_nCursor;
   }
 }
diff --git a/xfa/fxfa/fm2js/cxfa_fmlexer.h b/xfa/fxfa/fm2js/cxfa_fmlexer.h
index b9764c5..2d9e5fc 100644
--- a/xfa/fxfa/fm2js/cxfa_fmlexer.h
+++ b/xfa/fxfa/fm2js/cxfa_fmlexer.h
@@ -7,9 +7,6 @@
 #ifndef XFA_FXFA_FM2JS_CXFA_FMLEXER_H_
 #define XFA_FXFA_FM2JS_CXFA_FMLEXER_H_
 
-#include <memory>
-#include <utility>
-
 #include "core/fxcrt/fx_string.h"
 
 enum XFA_FM_TOKEN {
@@ -85,50 +82,43 @@
 
 struct XFA_FMKeyword {
   XFA_FM_TOKEN m_type;
-  uint32_t m_hash;
-  const wchar_t* m_keyword;
+  const char* m_keyword;  // Raw, POD struct.
 };
 
 class CXFA_FMToken {
  public:
   CXFA_FMToken();
-  explicit CXFA_FMToken(uint32_t line_num);
+  explicit CXFA_FMToken(XFA_FM_TOKEN token);
+  CXFA_FMToken(const CXFA_FMToken&);
   ~CXFA_FMToken();
 
+#ifndef NDEBUG
   WideString ToDebugString() const;
+#endif  // NDEBUG
 
   WideStringView m_string;
   XFA_FM_TOKEN m_type;
-  uint32_t m_line_num;
 };
 
 class CXFA_FMLexer {
  public:
-  explicit CXFA_FMLexer(const WideStringView& wsFormcalc);
+  explicit CXFA_FMLexer(WideStringView wsFormcalc);
   ~CXFA_FMLexer();
 
-  std::unique_ptr<CXFA_FMToken> NextToken();
-
-  void SetCurrentLine(uint32_t line) { m_current_line = line; }
-  const wchar_t* GetPos() { return m_cursor; }
-  void SetPos(const wchar_t* pos) { m_cursor = pos; }
+  CXFA_FMToken NextToken();
+  bool IsComplete() const { return m_nCursor >= m_spInput.size(); }
 
  private:
-  void AdvanceForNumber();
-  void AdvanceForString();
-  void AdvanceForIdentifier();
+  CXFA_FMToken AdvanceForNumber();
+  CXFA_FMToken AdvanceForString();
+  CXFA_FMToken AdvanceForIdentifier();
   void AdvanceForComment();
 
-  void RaiseError() {
-    m_token.reset();
-    m_lexer_error = true;
-  }
+  void RaiseError() { m_bLexerError = true; }
 
-  const wchar_t* m_cursor;
-  const wchar_t* const m_end;
-  uint32_t m_current_line;
-  std::unique_ptr<CXFA_FMToken> m_token;
-  bool m_lexer_error;
+  pdfium::span<const wchar_t> m_spInput;
+  size_t m_nCursor = 0;
+  bool m_bLexerError = false;
 };
 
 #endif  // XFA_FXFA_FM2JS_CXFA_FMLEXER_H_
diff --git a/xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp
index a6ab871..3627e15 100644
--- a/xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp
@@ -7,91 +7,101 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
 
+TEST(CXFA_FMLexerTest, NullString) {
+  WideStringView null_string;
+  CXFA_FMLexer lexer(null_string);
+  CXFA_FMToken token = lexer.NextToken();
+  EXPECT_EQ(TOKeof, token.m_type);
+  EXPECT_TRUE(lexer.IsComplete());
+}
+
 TEST(CXFA_FMLexerTest, EmptyString) {
   CXFA_FMLexer lexer(L"");
-  std::unique_ptr<CXFA_FMToken> token = lexer.NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  CXFA_FMToken token = lexer.NextToken();
+  EXPECT_EQ(TOKeof, token.m_type);
+  EXPECT_TRUE(lexer.IsComplete());
 }
 
 TEST(CXFA_FMLexerTest, Numbers) {
   auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"-12");
-  std::unique_ptr<CXFA_FMToken> token = lexer->NextToken();
+  CXFA_FMToken token = lexer->NextToken();
   // TODO(dsinclair): Should this return -12 instead of two tokens?
-  EXPECT_EQ(TOKminus, token->m_type);
+  EXPECT_EQ(TOKminus, token.m_type);
   token = lexer->NextToken();
-  EXPECT_EQ(L"12", token->m_string);
+  EXPECT_EQ(L"12", token.m_string);
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"1.5362");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"1.5362", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"1.5362", token.m_string);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"0.875");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"0.875", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"0.875", token.m_string);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"5.56e-2");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"5.56e-2", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"5.56e-2", token.m_string);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"1.234E10");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"1.234E10", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"1.234E10", token.m_string);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123456789.012345678");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
+  EXPECT_EQ(TOKnumber, token.m_type);
   // TODO(dsinclair): This should round as per IEEE 64-bit values.
-  // EXPECT_EQ(L"123456789.01234567", token->m_string);
-  EXPECT_EQ(L"123456789.012345678", token->m_string);
+  // EXPECT_EQ(L"123456789.01234567", token.m_string);
+  EXPECT_EQ(L"123456789.012345678", token.m_string);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"99999999999999999");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
+  EXPECT_EQ(TOKnumber, token.m_type);
   // TODO(dsinclair): This is spec'd as rounding when > 16 significant digits
   // prior to the exponent.
-  // EXPECT_EQ(L"100000000000000000", token->m_string);
-  EXPECT_EQ(L"99999999999999999", token->m_string);
+  // EXPECT_EQ(L"100000000000000000", token.m_string);
+  EXPECT_EQ(L"99999999999999999", token.m_string);
+  EXPECT_TRUE(lexer->IsComplete());
 }
 
 // The quotes are stripped in CXFA_FMStringExpression::ToJavaScript.
 TEST(CXFA_FMLexerTest, Strings) {
   auto lexer =
       pdfium::MakeUnique<CXFA_FMLexer>(L"\"The cat jumped over the fence.\"");
-  std::unique_ptr<CXFA_FMToken> token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token->m_type);
-  EXPECT_EQ(L"\"The cat jumped over the fence.\"", token->m_string);
+  CXFA_FMToken token = lexer->NextToken();
+  EXPECT_EQ(TOKstring, token.m_type);
+  EXPECT_EQ(L"\"The cat jumped over the fence.\"", token.m_string);
 
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"\"\"");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token->m_type);
-  EXPECT_EQ(L"\"\"", token->m_string);
+  EXPECT_EQ(TOKstring, token.m_type);
+  EXPECT_EQ(L"\"\"", token.m_string);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(
       L"\"The message reads: \"\"Warning: Insufficient Memory\"\"\"");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token->m_type);
+  EXPECT_EQ(TOKstring, token.m_type);
   EXPECT_EQ(L"\"The message reads: \"\"Warning: Insufficient Memory\"\"\"",
-            token->m_string);
+            token.m_string);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(
       L"\"\\u0047\\u006f\\u0066\\u0069\\u0073\\u0068\\u0021\\u000d\\u000a\"");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token->m_type);
+  EXPECT_EQ(TOKstring, token.m_type);
   EXPECT_EQ(
       L"\"\\u0047\\u006f\\u0066\\u0069\\u0073\\u0068\\u0021\\u000d\\u000a\"",
-      token->m_string);
+      token.m_string);
+  EXPECT_TRUE(lexer->IsComplete());
 }
 
 // Note, 'this' is a keyword but is not matched by the lexer.
@@ -161,51 +171,53 @@
 
   for (size_t i = 0; i < FX_ArraySize(op); ++i) {
     auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(op[i].op);
-    std::unique_ptr<CXFA_FMToken> token = lexer->NextToken();
-    EXPECT_EQ(op[i].token, token->m_type);
+    CXFA_FMToken token = lexer->NextToken();
+    EXPECT_EQ(op[i].token, token.m_type);
+    EXPECT_TRUE(lexer->IsComplete());
   }
 }
 
 TEST(CXFA_FMLexerTest, Comments) {
   auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"// Empty.");
-  std::unique_ptr<CXFA_FMToken> token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  CXFA_FMToken token = lexer->NextToken();
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"//");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123 // Empty.\n\"str\"");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"123", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"123", token.m_string);
 
   token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token->m_type);
-  EXPECT_EQ(L"\"str\"", token->m_string);
+  EXPECT_EQ(TOKstring, token.m_type);
+  EXPECT_EQ(L"\"str\"", token.m_string);
 
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L";");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"; Empty.");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123 ;Empty.\n\"str\"");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"123", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"123", token.m_string);
 
   token = lexer->NextToken();
-  EXPECT_EQ(TOKstring, token->m_type);
-  EXPECT_EQ(L"\"str\"", token->m_string);
+  EXPECT_EQ(TOKstring, token.m_type);
+  EXPECT_EQ(L"\"str\"", token.m_string);
 
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
+  EXPECT_TRUE(lexer->IsComplete());
 }
 
 TEST(CXFA_FMLexerTest, ValidIdentifiers) {
@@ -213,43 +225,68 @@
       L"a", L"an_identifier", L"_ident", L"$ident", L"!ident", L"GetAddr"};
   for (const auto* ident : identifiers) {
     auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(ident);
-    std::unique_ptr<CXFA_FMToken> token = lexer->NextToken();
-    EXPECT_EQ(TOKidentifier, token->m_type);
-    EXPECT_EQ(ident, token->m_string);
+    CXFA_FMToken token = lexer->NextToken();
+    EXPECT_EQ(TOKidentifier, token.m_type);
+    EXPECT_EQ(ident, token.m_string);
+    EXPECT_TRUE(lexer->IsComplete());
   }
 }
 
 TEST(CXFA_FMLexerTest, InvalidIdentifiers) {
   auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"#a");
-  EXPECT_EQ(nullptr, lexer->NextToken());
+  auto token = lexer->NextToken();
+  EXPECT_EQ(TOKreserver, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"1a");
-  EXPECT_EQ(nullptr, lexer->NextToken());
+  token = lexer->NextToken();
+  EXPECT_EQ(TOKreserver, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"an@identifier");
-  EXPECT_NE(nullptr, lexer->NextToken());
-  EXPECT_EQ(nullptr, lexer->NextToken());
-  EXPECT_EQ(nullptr, lexer->NextToken());
+  token = lexer->NextToken();
+  EXPECT_NE(TOKreserver, token.m_type);
+  token = lexer->NextToken();
+  EXPECT_EQ(TOKreserver, token.m_type);
+  token = lexer->NextToken();
+  EXPECT_EQ(TOKreserver, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"_ident@");
-  EXPECT_NE(nullptr, lexer->NextToken());
-  EXPECT_EQ(nullptr, lexer->NextToken());
+  token = lexer->NextToken();
+  EXPECT_NE(TOKreserver, token.m_type);
+  token = lexer->NextToken();
+  EXPECT_EQ(TOKreserver, token.m_type);
+  EXPECT_FALSE(lexer->IsComplete());
 }
 
 TEST(CXFA_FMLexerTest, Whitespace) {
   auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(L" \t\xc\x9\xb");
-  std::unique_ptr<CXFA_FMToken> token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  CXFA_FMToken token = lexer->NextToken();
+  EXPECT_EQ(TOKeof, token.m_type);
 
   lexer = pdfium::MakeUnique<CXFA_FMLexer>(L"123 \t\xc\x9\xb 456");
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"123", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"123", token.m_string);
 
   token = lexer->NextToken();
-  EXPECT_EQ(TOKnumber, token->m_type);
-  EXPECT_EQ(L"456", token->m_string);
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"456", token.m_string);
 
   token = lexer->NextToken();
-  EXPECT_EQ(TOKeof, token->m_type);
+  EXPECT_EQ(TOKeof, token.m_type);
+  EXPECT_TRUE(lexer->IsComplete());
+}
+
+TEST(CXFA_FMLexerTest, NullData) {
+  auto lexer = pdfium::MakeUnique<CXFA_FMLexer>(
+      WideStringView(L"\x2d\x32\x00\x2d\x32", 5));
+  CXFA_FMToken token = lexer->NextToken();
+  EXPECT_EQ(TOKminus, token.m_type);
+
+  token = lexer->NextToken();
+  EXPECT_EQ(TOKnumber, token.m_type);
+  EXPECT_EQ(L"2", token.m_string);
+
+  token = lexer->NextToken();
+  EXPECT_EQ(TOKeof, token.m_type);
+  EXPECT_FALSE(lexer->IsComplete());
 }
diff --git a/xfa/fxfa/fm2js/cxfa_fmparser.cpp b/xfa/fxfa/fm2js/cxfa_fmparser.cpp
index 644fdf2..152b5a1 100644
--- a/xfa/fxfa/fm2js/cxfa_fmparser.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmparser.cpp
@@ -15,34 +15,42 @@
 
 namespace {
 
-const unsigned int kMaxAssignmentChainLength = 12;
-const unsigned int kMaxParseDepth = 1250;
+constexpr unsigned int kMaxParseDepth = 1250;
+constexpr unsigned int kMaxPostExpressions = 256;
+constexpr unsigned int kMaxExpressionListSize = 10000;
 
 }  // namespace
 
-CXFA_FMParser::CXFA_FMParser(const WideStringView& wsFormcalc)
-    : m_error(false), m_parse_depth(0), m_max_parse_depth(kMaxParseDepth) {
-  m_lexer = pdfium::MakeUnique<CXFA_FMLexer>(wsFormcalc);
+CXFA_FMParser::CXFA_FMParser(WideStringView wsFormcalc)
+    : m_lexer(pdfium::MakeUnique<CXFA_FMLexer>(wsFormcalc)),
+      m_error(false),
+      m_parse_depth(0),
+      m_max_parse_depth(kMaxParseDepth) {}
+
+CXFA_FMParser::~CXFA_FMParser() = default;
+
+std::unique_ptr<CXFA_FMAST> CXFA_FMParser::Parse() {
   m_token = m_lexer->NextToken();
-}
-
-CXFA_FMParser::~CXFA_FMParser() {}
-
-std::unique_ptr<CXFA_FMFunctionDefinition> CXFA_FMParser::Parse() {
-  auto expressions = ParseTopExpression();
   if (HasError())
     return nullptr;
 
-  std::vector<WideStringView> arguments;
-  return pdfium::MakeUnique<CXFA_FMFunctionDefinition>(
-      1, true, L"", std::move(arguments), std::move(expressions));
+  auto expressions = ParseExpressionList();
+  if (HasError())
+    return nullptr;
+
+  // We failed to parse all of the input so something has gone wrong.
+  if (!m_lexer->IsComplete())
+    return nullptr;
+
+  return pdfium::MakeUnique<CXFA_FMAST>(std::move(expressions));
 }
 
 bool CXFA_FMParser::NextToken() {
   if (HasError())
     return false;
+
   m_token = m_lexer->NextToken();
-  while (!HasError() && m_token->m_type == TOKreserver)
+  while (!HasError() && m_token.m_type == TOKreserver)
     m_token = m_lexer->NextToken();
   return !HasError();
 }
@@ -51,7 +59,7 @@
   if (HasError())
     return false;
 
-  if (m_token->m_type != op) {
+  if (m_token.m_type != op) {
     m_error = true;
     return false;
   }
@@ -63,103 +71,104 @@
 }
 
 std::vector<std::unique_ptr<CXFA_FMExpression>>
-CXFA_FMParser::ParseTopExpression() {
+CXFA_FMParser::ParseExpressionList() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return std::vector<std::unique_ptr<CXFA_FMExpression>>();
 
-  std::unique_ptr<CXFA_FMExpression> expr;
   std::vector<std::unique_ptr<CXFA_FMExpression>> expressions;
   while (!HasError()) {
-    if (m_token->m_type == TOKeof || m_token->m_type == TOKendfunc ||
-        m_token->m_type == TOKendif || m_token->m_type == TOKelseif ||
-        m_token->m_type == TOKelse || m_token->m_type == TOKreserver) {
-      return expressions;
-    }
-
-    expr = m_token->m_type == TOKfunc ? ParseFunction() : ParseExpression();
-    if (!expr) {
-      m_error = true;
+    if (m_token.m_type == TOKeof || m_token.m_type == TOKendfunc ||
+        m_token.m_type == TOKendif || m_token.m_type == TOKelseif ||
+        m_token.m_type == TOKelse || m_token.m_type == TOKendwhile ||
+        m_token.m_type == TOKendfor || m_token.m_type == TOKend ||
+        m_token.m_type == TOKendfunc || m_token.m_type == TOKreserver) {
       break;
     }
+
+    std::unique_ptr<CXFA_FMExpression> expr =
+        m_token.m_type == TOKfunc ? ParseFunction() : ParseExpression();
+    if (!expr) {
+      m_error = true;
+      return std::vector<std::unique_ptr<CXFA_FMExpression>>();
+    }
+
+    if (expressions.size() >= kMaxExpressionListSize) {
+      m_error = true;
+      return std::vector<std::unique_ptr<CXFA_FMExpression>>();
+    }
+
     expressions.push_back(std::move(expr));
   }
-  return std::vector<std::unique_ptr<CXFA_FMExpression>>();
+  return expressions;
 }
 
+// Func := 'func' Identifier '(' ParameterList ')' do ExpressionList 'endfunc'
+// ParamterList := (Not actually defined in the grammar) .....
+//                 (Identifier (',' Identifier)*)?
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseFunction() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
+  if (!CheckThenNext(TOKfunc))
+    return nullptr;
+  if (m_token.m_type != TOKidentifier)
+    return nullptr;
 
-  WideStringView ident;
-  std::vector<WideStringView> arguments;
-  std::vector<std::unique_ptr<CXFA_FMExpression>> expressions;
-  uint32_t line = m_token->m_line_num;
+  WideStringView ident = m_token.m_string;
   if (!NextToken())
     return nullptr;
-  if (m_token->m_type != TOKidentifier) {
-    m_error = true;
-    return nullptr;
-  } else {
-    ident = m_token->m_string;
-    if (!NextToken())
-      return nullptr;
-  }
   if (!CheckThenNext(TOKlparen))
     return nullptr;
-  if (m_token->m_type == TOKrparen) {
+
+  std::vector<WideStringView> arguments;
+  bool last_was_comma = false;
+  while (1) {
+    if (m_token.m_type == TOKrparen)
+      break;
+    if (m_token.m_type != TOKidentifier)
+      return nullptr;
+
+    last_was_comma = false;
+
+    arguments.push_back(m_token.m_string);
     if (!NextToken())
       return nullptr;
-  } else {
-    while (1) {
-      if (m_token->m_type != TOKidentifier) {
-        m_error = true;
-        return nullptr;
-      }
-      arguments.push_back(m_token->m_string);
-      if (!NextToken())
-        return nullptr;
-      if (m_token->m_type == TOKcomma) {
-        if (!NextToken())
-          return nullptr;
-        continue;
-      }
-      if (m_token->m_type == TOKrparen) {
-        if (!NextToken())
-          return nullptr;
-      } else {
-        if (!CheckThenNext(TOKrparen))
-          return nullptr;
-      }
-      break;
-    }
+    if (m_token.m_type != TOKcomma)
+      continue;
+
+    last_was_comma = true;
+    if (!NextToken())
+      return nullptr;
   }
+  if (last_was_comma || !CheckThenNext(TOKrparen))
+    return nullptr;
   if (!CheckThenNext(TOKdo))
     return nullptr;
-  if (m_token->m_type == TOKendfunc) {
-    if (!NextToken())
-      return nullptr;
-  } else {
-    expressions = ParseTopExpression();
-    if (!expressions.size() || !CheckThenNext(TOKendfunc))
-      return nullptr;
-  }
+
+  std::vector<std::unique_ptr<CXFA_FMExpression>> expressions;
+  if (m_token.m_type != TOKendfunc)
+    expressions = ParseExpressionList();
+
+  if (!CheckThenNext(TOKendfunc))
+    return nullptr;
 
   return pdfium::MakeUnique<CXFA_FMFunctionDefinition>(
-      line, false, ident, std::move(arguments), std::move(expressions));
+      ident, std::move(arguments), std::move(expressions));
 }
 
+// Expression := IfExpression | WhileExpression | ForExpression |
+//               ForEachExpression | AssignmentExpression |
+//               DeclarationExpression | SimpleExpression
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
   std::unique_ptr<CXFA_FMExpression> expr;
-  uint32_t line = m_token->m_line_num;
-  switch (m_token->m_type) {
+  switch (m_token.m_type) {
     case TOKvar:
-      expr = ParseVarExpression();
+      expr = ParseDeclarationExpression();
       break;
     case TOKnull:
     case TOKnumber:
@@ -187,104 +196,103 @@
       expr = ParseDoExpression();
       break;
     case TOKbreak:
-      expr = pdfium::MakeUnique<CXFA_FMBreakExpression>(line);
+      expr = pdfium::MakeUnique<CXFA_FMBreakExpression>();
       if (!NextToken())
         return nullptr;
       break;
     case TOKcontinue:
-      expr = pdfium::MakeUnique<CXFA_FMContinueExpression>(line);
+      expr = pdfium::MakeUnique<CXFA_FMContinueExpression>();
       if (!NextToken())
         return nullptr;
       break;
     default:
-      m_error = true;
       return nullptr;
   }
   return expr;
 }
 
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseVarExpression() {
+// Declaration := 'var' Variable | 'var' Variable '=' SimpleExpression |
+//           'Func' Identifier '(' ParameterList ')' do ExpressionList 'EndFunc'
+// TODO(dsinclair): We appear to be handling the 'func' case elsewhere.
+std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseDeclarationExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
   WideStringView ident;
-  uint32_t line = m_token->m_line_num;
   if (!NextToken())
     return nullptr;
-  if (m_token->m_type != TOKidentifier) {
-    m_error = true;
+  if (m_token.m_type != TOKidentifier)
     return nullptr;
-  }
 
-  ident = m_token->m_string;
+  ident = m_token.m_string;
   if (!NextToken())
     return nullptr;
 
-  std::unique_ptr<CXFA_FMExpression> expr;
-  if (m_token->m_type == TOKassign) {
+  std::unique_ptr<CXFA_FMSimpleExpression> expr;
+  if (m_token.m_type == TOKassign) {
     if (!NextToken())
       return nullptr;
 
-    expr = ParseExpExpression();
+    expr = ParseSimpleExpression();
     if (!expr)
       return nullptr;
   }
 
-  return pdfium::MakeUnique<CXFA_FMVarExpression>(line, ident, std::move(expr));
+  return pdfium::MakeUnique<CXFA_FMVarExpression>(ident, std::move(expr));
 }
 
+// SimpleExpression := LogicalOrExpression
 std::unique_ptr<CXFA_FMSimpleExpression>
 CXFA_FMParser::ParseSimpleExpression() {
   if (HasError())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
-  std::unique_ptr<CXFA_FMSimpleExpression> pExp1 = ParseLogicalOrExpression();
-  if (!pExp1)
-    return nullptr;
-  int level = 1;
-  while (m_token->m_type == TOKassign) {
-    if (!NextToken())
-      return nullptr;
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2 = ParseLogicalOrExpression();
-    if (!pExp2)
-      return nullptr;
-    if (level++ == kMaxAssignmentChainLength) {
-      m_error = true;
-      return nullptr;
-    }
-    pExp1 = pdfium::MakeUnique<CXFA_FMAssignExpression>(
-        line, TOKassign, std::move(pExp1), std::move(pExp2));
-  }
-  return pExp1;
+  return ParseLogicalOrExpression();
 }
 
+// Exp := SimpleExpression ( '=' SimpleExpression )?
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseExpExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
   std::unique_ptr<CXFA_FMSimpleExpression> pExp1 = ParseSimpleExpression();
   if (!pExp1)
     return nullptr;
-  return pdfium::MakeUnique<CXFA_FMExpExpression>(line, std::move(pExp1));
+
+  if (m_token.m_type == TOKassign) {
+    if (!NextToken())
+      return nullptr;
+
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2 = ParseSimpleExpression();
+    if (!pExp2)
+      return nullptr;
+
+    pExp1 = pdfium::MakeUnique<CXFA_FMAssignExpression>(
+        TOKassign, std::move(pExp1), std::move(pExp2));
+  }
+  return pdfium::MakeUnique<CXFA_FMExpExpression>(std::move(pExp1));
 }
 
+// LogicalOr := LogicalAndExpression |
+//              LogicalOrExpression LogicalOrOperator LogicalAndExpression
 std::unique_ptr<CXFA_FMSimpleExpression>
 CXFA_FMParser::ParseLogicalOrExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
   std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseLogicalAndExpression();
   if (!e1)
     return nullptr;
 
+  // TODO(dsinclair): Is this for() needed?
   for (;;) {
-    switch (m_token->m_type) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.m_type) {
       case TOKor:
       case TOKksor: {
         if (!NextToken())
@@ -296,7 +304,7 @@
           return nullptr;
 
         e1 = pdfium::MakeUnique<CXFA_FMLogicalOrExpression>(
-            line, TOKor, std::move(e1), std::move(e2));
+            TOKor, std::move(e1), std::move(e2));
         continue;
       }
       default:
@@ -307,19 +315,24 @@
   return e1;
 }
 
+// LogicalAnd := EqualityExpression |
+//               LogicalAndExpression LogicalAndOperator EqualityExpression
 std::unique_ptr<CXFA_FMSimpleExpression>
 CXFA_FMParser::ParseLogicalAndExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
   std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseEqualityExpression();
   if (!e1)
     return nullptr;
 
+  // TODO(dsinclair): Is this for() needed?
   for (;;) {
-    switch (m_token->m_type) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.m_type) {
       case TOKand:
       case TOKksand: {
         if (!NextToken())
@@ -330,7 +343,7 @@
           return nullptr;
 
         e1 = pdfium::MakeUnique<CXFA_FMLogicalAndExpression>(
-            line, TOKand, std::move(e1), std::move(e2));
+            TOKand, std::move(e1), std::move(e2));
         continue;
       }
       default:
@@ -341,43 +354,52 @@
   return e1;
 }
 
+// Equality := RelationExpression |
+//             EqualityExpression EqulaityOperator RelationalExpression
 std::unique_ptr<CXFA_FMSimpleExpression>
 CXFA_FMParser::ParseEqualityExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
   std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseRelationalExpression();
   if (!e1)
     return nullptr;
+
+  // TODO(dsinclair): Is this for() needed?
   for (;;) {
-    std::unique_ptr<CXFA_FMSimpleExpression> e2;
-    switch (m_token->m_type) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
+    switch (m_token.m_type) {
       case TOKeq:
-      case TOKkseq:
+      case TOKkseq: {
         if (!NextToken())
           return nullptr;
 
-        e2 = ParseRelationalExpression();
+        std::unique_ptr<CXFA_FMSimpleExpression> e2 =
+            ParseRelationalExpression();
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMEqualityExpression>(
-            line, TOKeq, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMEqualExpression>(TOKeq, std::move(e1),
+                                                        std::move(e2));
         continue;
+      }
       case TOKne:
-      case TOKksne:
+      case TOKksne: {
         if (!NextToken())
           return nullptr;
 
-        e2 = ParseRelationalExpression();
+        std::unique_ptr<CXFA_FMSimpleExpression> e2 =
+            ParseRelationalExpression();
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMEqualityExpression>(
-            line, TOKne, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMNotEqualExpression>(TOKne, std::move(e1),
+                                                           std::move(e2));
         continue;
+      }
       default:
         break;
     }
@@ -386,67 +408,72 @@
   return e1;
 }
 
+// Relational := AdditiveExpression |
+//               RelationalExpression RelationalOperator AdditiveExpression
 std::unique_ptr<CXFA_FMSimpleExpression>
 CXFA_FMParser::ParseRelationalExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
-  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseAddtiveExpression();
+  std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseAdditiveExpression();
   if (!e1)
     return nullptr;
 
+  // TODO(dsinclair): Is this for() needed?
   for (;;) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
     std::unique_ptr<CXFA_FMSimpleExpression> e2;
-    switch (m_token->m_type) {
+    switch (m_token.m_type) {
       case TOKlt:
       case TOKkslt:
         if (!NextToken())
           return nullptr;
 
-        e2 = ParseAddtiveExpression();
+        e2 = ParseAdditiveExpression();
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMRelationalExpression>(
-            line, TOKlt, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMLtExpression>(TOKlt, std::move(e1),
+                                                     std::move(e2));
         continue;
       case TOKgt:
       case TOKksgt:
         if (!NextToken())
           return nullptr;
 
-        e2 = ParseAddtiveExpression();
+        e2 = ParseAdditiveExpression();
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMRelationalExpression>(
-            line, TOKgt, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMGtExpression>(TOKgt, std::move(e1),
+                                                     std::move(e2));
         continue;
       case TOKle:
       case TOKksle:
         if (!NextToken())
           return nullptr;
 
-        e2 = ParseAddtiveExpression();
+        e2 = ParseAdditiveExpression();
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMRelationalExpression>(
-            line, TOKle, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMLeExpression>(TOKle, std::move(e1),
+                                                     std::move(e2));
         continue;
       case TOKge:
       case TOKksge:
         if (!NextToken())
           return nullptr;
 
-        e2 = ParseAddtiveExpression();
+        e2 = ParseAdditiveExpression();
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMRelationalExpression>(
-            line, TOKge, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMGeExpression>(TOKge, std::move(e1),
+                                                     std::move(e2));
         continue;
       default:
         break;
@@ -456,20 +483,25 @@
   return e1;
 }
 
+// Additive := MultiplicativeExpression |
+//             AdditiveExpression AdditiveOperator MultiplicativeExpression
 std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseAddtiveExpression() {
+CXFA_FMParser::ParseAdditiveExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
   std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseMultiplicativeExpression();
   if (!e1)
     return nullptr;
 
+  // TODO(dsinclair): Is this for() needed?
   for (;;) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
     std::unique_ptr<CXFA_FMSimpleExpression> e2;
-    switch (m_token->m_type) {
+    switch (m_token.m_type) {
       case TOKplus:
         if (!NextToken())
           return nullptr;
@@ -478,8 +510,8 @@
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMAdditiveExpression>(
-            line, TOKplus, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMPlusExpression>(TOKplus, std::move(e1),
+                                                       std::move(e2));
         continue;
       case TOKminus:
         if (!NextToken())
@@ -489,8 +521,8 @@
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMAdditiveExpression>(
-            line, TOKminus, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMMinusExpression>(TOKminus, std::move(e1),
+                                                        std::move(e2));
         continue;
       default:
         break;
@@ -500,20 +532,25 @@
   return e1;
 }
 
+// Multiplicative := UnaryExpression |
+//                 MultiplicateExpression MultiplicativeOperator UnaryExpression
 std::unique_ptr<CXFA_FMSimpleExpression>
 CXFA_FMParser::ParseMultiplicativeExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
   std::unique_ptr<CXFA_FMSimpleExpression> e1 = ParseUnaryExpression();
   if (!e1)
     return nullptr;
 
+  // TODO(dsinclair): Is this for() needed?
   for (;;) {
+    if (!IncrementParseDepthAndCheck())
+      return nullptr;
+
     std::unique_ptr<CXFA_FMSimpleExpression> e2;
-    switch (m_token->m_type) {
+    switch (m_token.m_type) {
       case TOKmul:
         if (!NextToken())
           return nullptr;
@@ -522,8 +559,8 @@
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMMultiplicativeExpression>(
-            line, TOKmul, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMMulExpression>(TOKmul, std::move(e1),
+                                                      std::move(e2));
         continue;
       case TOKdiv:
         if (!NextToken())
@@ -533,8 +570,8 @@
         if (!e2)
           return nullptr;
 
-        e1 = pdfium::MakeUnique<CXFA_FMMultiplicativeExpression>(
-            line, TOKdiv, std::move(e1), std::move(e2));
+        e1 = pdfium::MakeUnique<CXFA_FMDivExpression>(TOKdiv, std::move(e1),
+                                                      std::move(e2));
         continue;
       default:
         break;
@@ -544,14 +581,14 @@
   return e1;
 }
 
+// Unary := PrimaryExpression | UnaryOperator UnaryExpression
 std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseUnaryExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
   std::unique_ptr<CXFA_FMSimpleExpression> expr;
-  uint32_t line = m_token->m_line_num;
-  switch (m_token->m_type) {
+  switch (m_token.m_type) {
     case TOKplus:
       if (!NextToken())
         return nullptr;
@@ -560,7 +597,7 @@
       if (!expr)
         return nullptr;
 
-      expr = pdfium::MakeUnique<CXFA_FMPosExpression>(line, std::move(expr));
+      expr = pdfium::MakeUnique<CXFA_FMPosExpression>(std::move(expr));
       break;
     case TOKminus:
       if (!NextToken())
@@ -570,7 +607,7 @@
       if (!expr)
         return nullptr;
 
-      expr = pdfium::MakeUnique<CXFA_FMNegExpression>(line, std::move(expr));
+      expr = pdfium::MakeUnique<CXFA_FMNegExpression>(std::move(expr));
       break;
     case TOKksnot:
       if (!NextToken())
@@ -580,127 +617,101 @@
       if (!expr)
         return nullptr;
 
-      expr = pdfium::MakeUnique<CXFA_FMNotExpression>(line, std::move(expr));
+      expr = pdfium::MakeUnique<CXFA_FMNotExpression>(std::move(expr));
       break;
     default:
-      expr = ParsePrimaryExpression();
-      if (!expr)
-        return nullptr;
-      break;
+      return ParsePrimaryExpression();
   }
   return expr;
 }
 
+// Primary := Literal | FunctionCall | Accessor ('.*' )? |
+//           '(' SimpleExpression ')'
 std::unique_ptr<CXFA_FMSimpleExpression>
 CXFA_FMParser::ParsePrimaryExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  std::unique_ptr<CXFA_FMSimpleExpression> expr;
-  uint32_t line = m_token->m_line_num;
-  switch (m_token->m_type) {
-    case TOKnumber:
-      expr =
-          pdfium::MakeUnique<CXFA_FMNumberExpression>(line, m_token->m_string);
-      if (!NextToken())
-        return nullptr;
-      break;
-    case TOKstring:
-      expr =
-          pdfium::MakeUnique<CXFA_FMStringExpression>(line, m_token->m_string);
-      if (!NextToken())
-        return nullptr;
-      break;
+  std::unique_ptr<CXFA_FMSimpleExpression> expr = ParseLiteral();
+  if (expr)
+    return NextToken() ? std::move(expr) : nullptr;
+
+  switch (m_token.m_type) {
     case TOKidentifier: {
-      WideStringView wsIdentifier(m_token->m_string);
+      WideStringView wsIdentifier(m_token.m_string);
       if (!NextToken())
         return nullptr;
-      if (m_token->m_type == TOKlbracket) {
+      if (m_token.m_type == TOKlbracket) {
         std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
         if (!s)
           return nullptr;
 
         expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            line, nullptr, TOKdot, wsIdentifier, std::move(s));
+            nullptr, TOKdot, wsIdentifier, std::move(s));
         if (!expr)
           return nullptr;
         if (!NextToken())
           return nullptr;
       } else {
-        expr =
-            pdfium::MakeUnique<CXFA_FMIdentifierExpression>(line, wsIdentifier);
+        expr = pdfium::MakeUnique<CXFA_FMIdentifierExpression>(wsIdentifier);
       }
       break;
     }
-    case TOKif:
-      expr = pdfium::MakeUnique<CXFA_FMIdentifierExpression>(line,
-                                                             m_token->m_string);
-      if (!expr || !NextToken())
-        return nullptr;
-      break;
-    case TOKnull:
-      expr = pdfium::MakeUnique<CXFA_FMNullExpression>(line);
-      if (!expr || !NextToken())
-        return nullptr;
-      break;
     case TOKlparen:
       expr = ParseParenExpression();
       if (!expr)
         return nullptr;
       break;
     default:
-      m_error = true;
       return nullptr;
   }
-  expr = ParsePostExpression(std::move(expr));
-  if (!expr)
-    return nullptr;
-  return expr;
+  return ParsePostExpression(std::move(expr));
 }
 
+// Literal := String | Number | Null
+std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseLiteral() {
+  switch (m_token.m_type) {
+    case TOKnumber:
+      return pdfium::MakeUnique<CXFA_FMNumberExpression>(m_token.m_string);
+    case TOKstring:
+      return pdfium::MakeUnique<CXFA_FMStringExpression>(m_token.m_string);
+    case TOKnull:
+      return pdfium::MakeUnique<CXFA_FMNullExpression>();
+    default:
+      return nullptr;
+  }
+}
+
+// TODO(dsinclair): Make this match up to the grammar
+// I believe this is parsing the accessor ( '.' | '..' | '.#' )
 std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParsePostExpression(
     std::unique_ptr<CXFA_FMSimpleExpression> expr) {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  if (HasError())
-    return nullptr;
-
-  uint32_t line = m_token->m_line_num;
+  size_t expr_count = 0;
   while (1) {
-    switch (m_token->m_type) {
-      case TOKlparen: {
-        if (!NextToken())
-          return nullptr;
-        std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> expressions;
-        if (m_token->m_type != TOKrparen) {
-          while (m_token->m_type != TOKrparen) {
-            std::unique_ptr<CXFA_FMSimpleExpression> simple_expr =
-                ParseSimpleExpression();
-            if (!simple_expr)
-              return nullptr;
+    ++expr_count;
+    // Limit the number of expressions allowed in the post expression statement.
+    // If we don't do this then its possible to generate a stack overflow
+    // by having a very large number of things like .. expressions.
+    if (expr_count > kMaxPostExpressions)
+      return nullptr;
 
-            expressions.push_back(std::move(simple_expr));
-            if (m_token->m_type == TOKcomma) {
-              if (!NextToken())
-                return nullptr;
-            } else if (m_token->m_type == TOKeof ||
-                       m_token->m_type == TOKreserver) {
-              break;
-            }
-          }
-          if (m_token->m_type != TOKrparen) {
-            m_error = true;
-            return nullptr;
-          }
-        }
+    switch (m_token.m_type) {
+      case TOKlparen: {
+        std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
+            expressions = ParseArgumentList();
+        if (!expressions)
+          return nullptr;
+
         expr = pdfium::MakeUnique<CXFA_FMCallExpression>(
-            line, std::move(expr), std::move(expressions), false);
+            std::move(expr), std::move(*expressions), false);
         if (!NextToken())
           return nullptr;
-        if (m_token->m_type != TOKlbracket)
+        if (m_token.m_type != TOKlbracket)
           continue;
 
         std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
@@ -708,57 +719,33 @@
           return nullptr;
 
         expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            line, std::move(expr), TOKcall, L"", std::move(s));
+            std::move(expr), TOKcall, WideStringView(), std::move(s));
         break;
       }
       case TOKdot: {
         if (!NextToken())
           return nullptr;
-        if (m_token->m_type != TOKidentifier) {
-          m_error = true;
+        if (m_token.m_type != TOKidentifier)
           return nullptr;
-        }
-        WideStringView tempStr = m_token->m_string;
-        uint32_t tempLine = m_token->m_line_num;
+
+        WideStringView tempStr = m_token.m_string;
         if (!NextToken())
           return nullptr;
-        if (m_token->m_type == TOKlparen) {
-          std::unique_ptr<CXFA_FMSimpleExpression> pExpCall;
-          if (!NextToken())
+        if (m_token.m_type == TOKlparen) {
+          std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
+              expressions = ParseArgumentList();
+          if (!expressions)
             return nullptr;
 
-          std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> expressions;
-          if (m_token->m_type != TOKrparen) {
-            while (m_token->m_type != TOKrparen) {
-              std::unique_ptr<CXFA_FMSimpleExpression> exp =
-                  ParseSimpleExpression();
-              if (!exp)
-                return nullptr;
-
-              expressions.push_back(std::move(exp));
-              if (m_token->m_type == TOKcomma) {
-                if (!NextToken())
-                  return nullptr;
-              } else if (m_token->m_type == TOKeof ||
-                         m_token->m_type == TOKreserver) {
-                break;
-              }
-            }
-            if (m_token->m_type != TOKrparen) {
-              m_error = true;
-              return nullptr;
-            }
-          }
-          std::unique_ptr<CXFA_FMSimpleExpression> pIdentifier =
-              pdfium::MakeUnique<CXFA_FMIdentifierExpression>(tempLine,
-                                                              tempStr);
-          pExpCall = pdfium::MakeUnique<CXFA_FMCallExpression>(
-              line, std::move(pIdentifier), std::move(expressions), true);
+          auto pIdentifier =
+              pdfium::MakeUnique<CXFA_FMIdentifierExpression>(tempStr);
+          auto pExpCall = pdfium::MakeUnique<CXFA_FMCallExpression>(
+              std::move(pIdentifier), std::move(*expressions), true);
           expr = pdfium::MakeUnique<CXFA_FMMethodCallExpression>(
-              line, std::move(expr), std::move(pExpCall));
+              std::move(expr), std::move(pExpCall));
           if (!NextToken())
             return nullptr;
-          if (m_token->m_type != TOKlbracket)
+          if (m_token.m_type != TOKlbracket)
             continue;
 
           std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
@@ -766,83 +753,83 @@
             return nullptr;
 
           expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              line, std::move(expr), TOKcall, L"", std::move(s));
-        } else if (m_token->m_type == TOKlbracket) {
+              std::move(expr), TOKcall, WideStringView(), std::move(s));
+        } else if (m_token.m_type == TOKlbracket) {
           std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
           if (!s)
             return nullptr;
 
           expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              tempLine, std::move(expr), TOKdot, tempStr, std::move(s));
+              std::move(expr), TOKdot, tempStr, std::move(s));
         } else {
           std::unique_ptr<CXFA_FMSimpleExpression> s =
-              pdfium::MakeUnique<CXFA_FMIndexExpression>(
-                  tempLine, ACCESSOR_NO_INDEX, nullptr, false);
+              pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
+                                                         nullptr, false);
           expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              line, std::move(expr), TOKdot, tempStr, std::move(s));
+              std::move(expr), TOKdot, tempStr, std::move(s));
           continue;
         }
-      } break;
+        break;
+      }
       case TOKdotdot: {
         if (!NextToken())
           return nullptr;
-        if (m_token->m_type != TOKidentifier) {
-          m_error = true;
+        if (m_token.m_type != TOKidentifier)
           return nullptr;
-        }
-        WideStringView tempStr = m_token->m_string;
-        uint32_t tempLine = m_token->m_line_num;
+
+        WideStringView tempStr = m_token.m_string;
         if (!NextToken())
           return nullptr;
-        if (m_token->m_type == TOKlbracket) {
+        if (m_token.m_type == TOKlbracket) {
           std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
           if (!s)
             return nullptr;
 
           expr = pdfium::MakeUnique<CXFA_FMDotDotAccessorExpression>(
-              tempLine, std::move(expr), TOKdotdot, tempStr, std::move(s));
+              std::move(expr), TOKdotdot, tempStr, std::move(s));
         } else {
           std::unique_ptr<CXFA_FMSimpleExpression> s =
-              pdfium::MakeUnique<CXFA_FMIndexExpression>(
-                  tempLine, ACCESSOR_NO_INDEX, nullptr, false);
+              pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
+                                                         nullptr, false);
           expr = pdfium::MakeUnique<CXFA_FMDotDotAccessorExpression>(
-              line, std::move(expr), TOKdotdot, tempStr, std::move(s));
+              std::move(expr), TOKdotdot, tempStr, std::move(s));
           continue;
         }
-      } break;
+        break;
+      }
       case TOKdotscream: {
         if (!NextToken())
           return nullptr;
-        if (m_token->m_type != TOKidentifier) {
-          m_error = true;
+        if (m_token.m_type != TOKidentifier)
           return nullptr;
-        }
-        WideStringView tempStr = m_token->m_string;
-        uint32_t tempLine = m_token->m_line_num;
+
+        WideStringView tempStr = m_token.m_string;
         if (!NextToken())
           return nullptr;
-        if (m_token->m_type != TOKlbracket) {
+
+        if (m_token.m_type != TOKlbracket) {
           std::unique_ptr<CXFA_FMSimpleExpression> s =
-              pdfium::MakeUnique<CXFA_FMIndexExpression>(
-                  tempLine, ACCESSOR_NO_INDEX, nullptr, false);
+              pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
+                                                         nullptr, false);
           expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-              line, std::move(expr), TOKdotscream, tempStr, std::move(s));
+              std::move(expr), TOKdotscream, tempStr, std::move(s));
           continue;
         }
+
         std::unique_ptr<CXFA_FMSimpleExpression> s = ParseIndexExpression();
         if (!s)
           return nullptr;
 
         expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            tempLine, std::move(expr), TOKdotscream, tempStr, std::move(s));
+            std::move(expr), TOKdotscream, tempStr, std::move(s));
         break;
       }
       case TOKdotstar: {
         std::unique_ptr<CXFA_FMSimpleExpression> s =
-            pdfium::MakeUnique<CXFA_FMIndexExpression>(line, ACCESSOR_NO_INDEX,
+            pdfium::MakeUnique<CXFA_FMIndexExpression>(ACCESSOR_NO_INDEX,
                                                        nullptr, false);
         expr = pdfium::MakeUnique<CXFA_FMDotAccessorExpression>(
-            line, std::move(expr), TOKdotstar, L"*", std::move(s));
+            std::move(expr), TOKdotstar, L"*", std::move(s));
         break;
       }
       default:
@@ -854,49 +841,79 @@
   return expr;
 }
 
+// Argument lists are zero or more comma seperated simple expressions found
+// between '(' and ')'
+std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
+CXFA_FMParser::ParseArgumentList() {
+  if (m_token.m_type != TOKlparen || !NextToken())
+    return nullptr;
+
+  auto expressions = pdfium::MakeUnique<
+      std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>();
+  bool first_arg = true;
+  while (m_token.m_type != TOKrparen) {
+    if (first_arg) {
+      first_arg = false;
+    } else {
+      if (m_token.m_type != TOKcomma || !NextToken())
+        return nullptr;
+    }
+
+    std::unique_ptr<CXFA_FMSimpleExpression> exp = ParseSimpleExpression();
+    if (!exp)
+      return nullptr;
+
+    expressions->push_back(std::move(exp));
+    if (expressions->size() > kMaxPostExpressions)
+      return nullptr;
+  }
+
+  return expressions;
+}
+
+// Index := '[' ('*' | '+' SimpleExpression | '-' SimpleExpression) ']'
 std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseIndexExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
-
-  uint32_t line = m_token->m_line_num;
-  if (!NextToken())
+  if (!CheckThenNext(TOKlbracket))
     return nullptr;
 
-  std::unique_ptr<CXFA_FMSimpleExpression> s;
-  XFA_FM_AccessorIndex accessorIndex = ACCESSOR_NO_RELATIVEINDEX;
-  std::unique_ptr<CXFA_FMSimpleExpression> pExp;
-  if (m_token->m_type == TOKmul) {
-    pExp = pdfium::MakeUnique<CXFA_FMIndexExpression>(line, accessorIndex,
-                                                      std::move(s), true);
+  if (m_token.m_type == TOKmul) {
+    auto pExp = pdfium::MakeUnique<CXFA_FMIndexExpression>(
+        ACCESSOR_NO_RELATIVEINDEX, nullptr, true);
     if (!pExp || !NextToken())
       return nullptr;
-    if (m_token->m_type != TOKrbracket) {
-      m_error = true;
+
+    // TODO(dsinclair): This should CheckThenNext(TOKrbracket) but need to clean
+    // up the callsites.
+    if (m_token.m_type != TOKrbracket)
       return nullptr;
-    }
     return pExp;
   }
-  if (m_token->m_type == TOKplus) {
+
+  XFA_FM_AccessorIndex accessorIndex = ACCESSOR_NO_RELATIVEINDEX;
+  if (m_token.m_type == TOKplus) {
     accessorIndex = ACCESSOR_POSITIVE_INDEX;
     if (!NextToken())
       return nullptr;
-  } else if (m_token->m_type == TOKminus) {
+  } else if (m_token.m_type == TOKminus) {
     accessorIndex = ACCESSOR_NEGATIVE_INDEX;
     if (!NextToken())
       return nullptr;
   }
-  s = ParseSimpleExpression();
+
+  std::unique_ptr<CXFA_FMSimpleExpression> s = ParseSimpleExpression();
   if (!s)
     return nullptr;
-  if (m_token->m_type != TOKrbracket) {
-    m_error = true;
+  if (m_token.m_type != TOKrbracket)
     return nullptr;
-  }
-  return pdfium::MakeUnique<CXFA_FMIndexExpression>(line, accessorIndex,
-                                                    std::move(s), false);
+
+  return pdfium::MakeUnique<CXFA_FMIndexExpression>(accessorIndex, std::move(s),
+                                                    false);
 }
 
+// Paren := '(' SimpleExpression ')'
 std::unique_ptr<CXFA_FMSimpleExpression> CXFA_FMParser::ParseParenExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
@@ -904,214 +921,111 @@
 
   if (!CheckThenNext(TOKlparen))
     return nullptr;
-
-  if (m_token->m_type == TOKrparen) {
-    m_error = true;
+  if (m_token.m_type == TOKrparen)
     return nullptr;
-  }
 
-  uint32_t line = m_token->m_line_num;
-  std::unique_ptr<CXFA_FMSimpleExpression> pExp1 = ParseLogicalOrExpression();
+  std::unique_ptr<CXFA_FMSimpleExpression> pExp1 = ParseSimpleExpression();
   if (!pExp1)
     return nullptr;
 
-  int level = 1;
-  while (m_token->m_type == TOKassign) {
-    if (!NextToken())
-      return nullptr;
-
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2 = ParseLogicalOrExpression();
-    if (!pExp2)
-      return nullptr;
-    if (level++ == kMaxAssignmentChainLength) {
-      m_error = true;
-      return nullptr;
-    }
-
-    pExp1 = pdfium::MakeUnique<CXFA_FMAssignExpression>(
-        line, TOKassign, std::move(pExp1), std::move(pExp2));
-  }
   if (!CheckThenNext(TOKrparen))
     return nullptr;
   return pExp1;
 }
 
-std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseBlockExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  if (HasError())
-    return nullptr;
-
-  uint32_t line = m_token->m_line_num;
-  std::vector<std::unique_ptr<CXFA_FMExpression>> expressions;
-  while (1) {
-    std::unique_ptr<CXFA_FMExpression> expr;
-    switch (m_token->m_type) {
-      case TOKeof:
-      case TOKendif:
-      case TOKelseif:
-      case TOKelse:
-      case TOKendwhile:
-      case TOKendfor:
-      case TOKend:
-      case TOKendfunc:
-      case TOKreserver:
-        break;
-      case TOKfunc:
-        expr = ParseFunction();
-        if (!expr)
-          return nullptr;
-
-        expressions.push_back(std::move(expr));
-        continue;
-      default:
-        expr = ParseExpression();
-        if (!expr)
-          return nullptr;
-
-        expressions.push_back(std::move(expr));
-        continue;
-    }
-    break;
-  }
-  return pdfium::MakeUnique<CXFA_FMBlockExpression>(line,
-                                                    std::move(expressions));
-}
-
+// If := 'if' '(' SimpleExpression ')' 'then' ExpressionList
+//       ('elseif' '(' SimpleExpression ')' 'then' ExpressionList)*
+//       ('else' ExpressionList)?
+//       'endif'
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseIfExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
-  const wchar_t* pStartPos = m_lexer->GetPos();
-  if (!NextToken() || !CheckThenNext(TOKlparen))
+  if (!CheckThenNext(TOKif))
     return nullptr;
 
-  std::unique_ptr<CXFA_FMSimpleExpression> pExpression;
-  while (m_token->m_type != TOKrparen) {
-    pExpression = ParseSimpleExpression();
-    if (!pExpression)
-      return nullptr;
-    if (m_token->m_type != TOKcomma)
-      break;
-    if (!NextToken())
-      return nullptr;
-  }
-  if (!CheckThenNext(TOKrparen))
+  std::unique_ptr<CXFA_FMSimpleExpression> pCondition = ParseParenExpression();
+  if (!pCondition)
     return nullptr;
-  if (m_token->m_type != TOKthen) {
-    m_lexer->SetCurrentLine(line);
-    auto pNewToken = pdfium::MakeUnique<CXFA_FMToken>(line);
-    m_token = std::move(pNewToken);
-    m_token->m_type = TOKidentifier;
-    m_token->m_string = L"if";
-    m_lexer->SetPos(pStartPos);
-    return ParseExpExpression();
-  }
   if (!CheckThenNext(TOKthen))
     return nullptr;
 
-  std::unique_ptr<CXFA_FMExpression> pIfExpression = ParseBlockExpression();
-  if (!pIfExpression)
-    return nullptr;
+  auto pIfExpressions =
+      pdfium::MakeUnique<CXFA_FMBlockExpression>(ParseExpressionList());
+
+  std::vector<std::unique_ptr<CXFA_FMIfExpression>> pElseIfExpressions;
+  while (m_token.m_type == TOKelseif) {
+    if (!NextToken())
+      return nullptr;
+
+    auto elseIfCondition = ParseParenExpression();
+    if (!elseIfCondition)
+      return nullptr;
+    if (!CheckThenNext(TOKthen))
+      return nullptr;
+
+    auto elseIfExprs = ParseExpressionList();
+    pElseIfExpressions.push_back(pdfium::MakeUnique<CXFA_FMIfExpression>(
+        std::move(elseIfCondition),
+        pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(elseIfExprs)),
+        std::vector<std::unique_ptr<CXFA_FMIfExpression>>(), nullptr));
+  }
 
   std::unique_ptr<CXFA_FMExpression> pElseExpression;
-  switch (m_token->m_type) {
-    case TOKeof:
-    case TOKendif:
-      if (!CheckThenNext(TOKendif))
-        return nullptr;
-      break;
-    case TOKif:
-      pElseExpression = ParseIfExpression();
-      if (!pElseExpression || !CheckThenNext(TOKendif))
-        return nullptr;
-      break;
-    case TOKelseif:
-      pElseExpression = ParseIfExpression();
-      if (!pElseExpression)
-        return nullptr;
-      break;
-    case TOKelse:
-      if (!NextToken())
-        return nullptr;
-      pElseExpression = ParseBlockExpression();
-      if (!pElseExpression || !CheckThenNext(TOKendif))
-        return nullptr;
-      break;
-    default:
-      m_error = true;
+  if (m_token.m_type == TOKelse) {
+    if (!NextToken())
       return nullptr;
+
+    pElseExpression =
+        pdfium::MakeUnique<CXFA_FMBlockExpression>(ParseExpressionList());
   }
-  return pdfium::MakeUnique<CXFA_FMIfExpression>(line, std::move(pExpression),
-                                                 std::move(pIfExpression),
-                                                 std::move(pElseExpression));
+  if (!CheckThenNext(TOKendif))
+    return nullptr;
+
+  return pdfium::MakeUnique<CXFA_FMIfExpression>(
+      std::move(pCondition), std::move(pIfExpressions),
+      std::move(pElseIfExpressions), std::move(pElseExpression));
 }
 
+// While := 'while' '(' SimpleExpression ')' 'do' ExpressionList 'endwhile'
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseWhileExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
-
-  uint32_t line = m_token->m_line_num;
-  if (!NextToken())
+  if (!CheckThenNext(TOKwhile))
     return nullptr;
 
   std::unique_ptr<CXFA_FMSimpleExpression> pCondition = ParseParenExpression();
   if (!pCondition || !CheckThenNext(TOKdo))
     return nullptr;
 
-  std::unique_ptr<CXFA_FMExpression> pExpression = ParseBlockExpression();
-  if (!pExpression || !CheckThenNext(TOKendwhile))
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKendwhile))
     return nullptr;
-  return pdfium::MakeUnique<CXFA_FMWhileExpression>(line, std::move(pCondition),
-                                                    std::move(pExpression));
+
+  return pdfium::MakeUnique<CXFA_FMWhileExpression>(
+      std::move(pCondition),
+      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
 }
 
-std::unique_ptr<CXFA_FMSimpleExpression>
-CXFA_FMParser::ParseSubassignmentInForExpression() {
-  AutoRestorer<unsigned long> restorer(&m_parse_depth);
-  if (HasError() || !IncrementParseDepthAndCheck())
-    return nullptr;
-
-  if (HasError())
-    return nullptr;
-
-  if (m_token->m_type != TOKidentifier) {
-    m_error = true;
-    return nullptr;
-  }
-  std::unique_ptr<CXFA_FMSimpleExpression> expr = ParseSimpleExpression();
-  if (!expr)
-    return nullptr;
-  return expr;
-}
-
+// For := 'for' Assignment 'upto' Accessor ('step' SimpleExpression)?
+//            'do' ExpressionList 'endfor' |
+//         'for' Assignment 'downto' Accessor ('step' SimpleExpression)?
+//            'do' ExpressionList 'endfor'
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseForExpression() {
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
+  if (!CheckThenNext(TOKfor))
+    return nullptr;
+  if (m_token.m_type != TOKidentifier)
+    return nullptr;
 
-  WideStringView wsVariant;
-  uint32_t line = m_token->m_line_num;
+  WideStringView wsVariant = m_token.m_string;
   if (!NextToken())
     return nullptr;
-  if (m_token->m_type != TOKidentifier) {
-    m_error = true;
-    return nullptr;
-  }
-
-  wsVariant = m_token->m_string;
-  if (!NextToken())
-    return nullptr;
-  if (m_token->m_type != TOKassign) {
-    m_error = true;
-    return nullptr;
-  }
-  if (!NextToken())
+  if (!CheckThenNext(TOKassign))
     return nullptr;
 
   std::unique_ptr<CXFA_FMSimpleExpression> pAssignment =
@@ -1120,14 +1034,12 @@
     return nullptr;
 
   int32_t iDirection = 0;
-  if (m_token->m_type == TOKupto) {
+  if (m_token.m_type == TOKupto)
     iDirection = 1;
-  } else if (m_token->m_type == TOKdownto) {
+  else if (m_token.m_type == TOKdownto)
     iDirection = -1;
-  } else {
-    m_error = true;
+  else
     return nullptr;
-  }
 
   if (!NextToken())
     return nullptr;
@@ -1137,7 +1049,7 @@
     return nullptr;
 
   std::unique_ptr<CXFA_FMSimpleExpression> pStep;
-  if (m_token->m_type == TOKstep) {
+  if (m_token.m_type == TOKstep) {
     if (!NextToken())
       return nullptr;
     pStep = ParseSimpleExpression();
@@ -1147,85 +1059,80 @@
   if (!CheckThenNext(TOKdo))
     return nullptr;
 
-  std::unique_ptr<CXFA_FMExpression> pList = ParseBlockExpression();
-  if (!pList || !CheckThenNext(TOKendfor))
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKendfor))
     return nullptr;
 
-  std::unique_ptr<CXFA_FMExpression> expr;
-  if (!expr)
-    return nullptr;
   return pdfium::MakeUnique<CXFA_FMForExpression>(
-      line, wsVariant, std::move(pAssignment), std::move(pAccessor), iDirection,
-      std::move(pStep), std::move(pList));
+      wsVariant, std::move(pAssignment), std::move(pAccessor), iDirection,
+      std::move(pStep),
+      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
 }
 
+// Foreach := 'foreach' Identifier 'in' '(' ArgumentList ')'
+//            'do' ExpressionList 'endfor'
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseForeachExpression() {
+  if (m_token.m_type != TOKforeach)
+    return nullptr;
+
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
-
-  if (HasError())
+  if (!CheckThenNext(TOKforeach))
+    return nullptr;
+  if (m_token.m_type != TOKidentifier)
     return nullptr;
 
-  std::unique_ptr<CXFA_FMExpression> expr;
-  WideStringView wsIdentifier;
-  std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> pAccessors;
-  std::unique_ptr<CXFA_FMExpression> pList;
-  uint32_t line = m_token->m_line_num;
-  if (!NextToken())
-    return nullptr;
-  if (m_token->m_type != TOKidentifier) {
-    m_error = true;
-    return nullptr;
-  }
-
-  wsIdentifier = m_token->m_string;
+  WideStringView wsIdentifier = m_token.m_string;
   if (!NextToken() || !CheckThenNext(TOKin) || !CheckThenNext(TOKlparen))
     return nullptr;
-  if (m_token->m_type == TOKrparen) {
-    m_error = true;
-    return nullptr;
-  }
 
-  while (m_token->m_type != TOKrparen) {
+  std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> pArgumentList;
+  while (m_token.m_type != TOKrparen) {
     std::unique_ptr<CXFA_FMSimpleExpression> s = ParseSimpleExpression();
     if (!s)
       return nullptr;
 
-    pAccessors.push_back(std::move(s));
-    if (m_token->m_type != TOKcomma)
+    pArgumentList.push_back(std::move(s));
+    if (m_token.m_type != TOKcomma)
       break;
     if (!NextToken())
       return nullptr;
   }
-  if (!CheckThenNext(TOKrparen) || !CheckThenNext(TOKdo))
+  // We must have arguments.
+  if (pArgumentList.empty())
+    return nullptr;
+  if (!CheckThenNext(TOKrparen))
     return nullptr;
 
-  pList = ParseBlockExpression();
-  if (!pList || !CheckThenNext(TOKendfor))
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKendfor))
     return nullptr;
+
   return pdfium::MakeUnique<CXFA_FMForeachExpression>(
-      line, wsIdentifier, std::move(pAccessors), std::move(pList));
+      wsIdentifier, std::move(pArgumentList),
+      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
 }
 
+// Block := 'do' ExpressionList 'end'
 std::unique_ptr<CXFA_FMExpression> CXFA_FMParser::ParseDoExpression() {
+  if (m_token.m_type != TOKdo)
+    return nullptr;
+
   AutoRestorer<unsigned long> restorer(&m_parse_depth);
   if (HasError() || !IncrementParseDepthAndCheck())
     return nullptr;
-
-  if (HasError())
+  if (!CheckThenNext(TOKdo))
     return nullptr;
 
-  uint32_t line = m_token->m_line_num;
-  if (!NextToken())
+  auto exprs = ParseExpressionList();
+  if (!CheckThenNext(TOKend))
     return nullptr;
 
-  std::unique_ptr<CXFA_FMExpression> expr = ParseBlockExpression();
-  if (!expr || !CheckThenNext(TOKend))
-    return nullptr;
-  return pdfium::MakeUnique<CXFA_FMDoExpression>(line, std::move(expr));
+  return pdfium::MakeUnique<CXFA_FMDoExpression>(
+      pdfium::MakeUnique<CXFA_FMBlockExpression>(std::move(exprs)));
 }
 
 bool CXFA_FMParser::HasError() const {
-  return m_error || m_token == nullptr;
+  return m_error || m_token.m_type == TOKreserver;
 }
diff --git a/xfa/fxfa/fm2js/cxfa_fmparser.h b/xfa/fxfa/fm2js/cxfa_fmparser.h
index c536838..ad2b367 100644
--- a/xfa/fxfa/fm2js/cxfa_fmparser.h
+++ b/xfa/fxfa/fm2js/cxfa_fmparser.h
@@ -15,10 +15,10 @@
 
 class CXFA_FMParser {
  public:
-  explicit CXFA_FMParser(const WideStringView& wsFormcalc);
+  explicit CXFA_FMParser(WideStringView wsFormcalc);
   ~CXFA_FMParser();
 
-  std::unique_ptr<CXFA_FMFunctionDefinition> Parse();
+  std::unique_ptr<CXFA_FMAST> Parse();
   bool HasError() const;
 
   void SetMaxParseDepthForTest(unsigned long max_depth) {
@@ -30,12 +30,11 @@
   bool CheckThenNext(XFA_FM_TOKEN op);
   bool IncrementParseDepthAndCheck();
 
-  std::vector<std::unique_ptr<CXFA_FMExpression>> ParseTopExpression();
+  std::vector<std::unique_ptr<CXFA_FMExpression>> ParseExpressionList();
   std::unique_ptr<CXFA_FMExpression> ParseFunction();
   std::unique_ptr<CXFA_FMExpression> ParseExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseVarExpression();
+  std::unique_ptr<CXFA_FMExpression> ParseDeclarationExpression();
   std::unique_ptr<CXFA_FMExpression> ParseExpExpression();
-  std::unique_ptr<CXFA_FMExpression> ParseBlockExpression();
   std::unique_ptr<CXFA_FMExpression> ParseIfExpression();
   std::unique_ptr<CXFA_FMExpression> ParseWhileExpression();
   std::unique_ptr<CXFA_FMExpression> ParseForExpression();
@@ -43,21 +42,23 @@
   std::unique_ptr<CXFA_FMExpression> ParseDoExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseParenExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseSimpleExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseSubassignmentInForExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseLogicalOrExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseLogicalAndExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseEqualityExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseRelationalExpression();
-  std::unique_ptr<CXFA_FMSimpleExpression> ParseAddtiveExpression();
+  std::unique_ptr<CXFA_FMSimpleExpression> ParseAdditiveExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseMultiplicativeExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseUnaryExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParsePrimaryExpression();
   std::unique_ptr<CXFA_FMSimpleExpression> ParsePostExpression(
       std::unique_ptr<CXFA_FMSimpleExpression> e);
+  std::unique_ptr<std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>>
+  ParseArgumentList();
   std::unique_ptr<CXFA_FMSimpleExpression> ParseIndexExpression();
+  std::unique_ptr<CXFA_FMSimpleExpression> ParseLiteral();
 
   std::unique_ptr<CXFA_FMLexer> m_lexer;
-  std::unique_ptr<CXFA_FMToken> m_token;
+  CXFA_FMToken m_token;
   bool m_error;
   unsigned long m_parse_depth;
   unsigned long m_max_parse_depth;
diff --git a/xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp
index 8582649..4c5aa46 100644
--- a/xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmparser_unittest.cpp
@@ -8,109 +8,131 @@
 
 #include "core/fxcrt/cfx_widetextbuf.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
 
 TEST(CXFA_FMParserTest, Empty) {
   auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"");
-  std::unique_ptr<CXFA_FMFunctionDefinition> ast = parser->Parse();
-  ASSERT(ast != nullptr);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast);
   EXPECT_FALSE(parser->HasError());
 
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(buf));
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
   // TODO(dsinclair): This is a little weird .....
-  EXPECT_EQ(L"// comments only", buf.AsStringView());
+  EXPECT_STREQ(L"// comments only", buf.MakeString().c_str());
 }
 
 TEST(CXFA_FMParserTest, CommentOnlyIsError) {
   auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"; Just comment");
-  std::unique_ptr<CXFA_FMFunctionDefinition> ast = parser->Parse();
-  ASSERT(ast != nullptr);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast);
   // TODO(dsinclair): This isn't allowed per the spec.
   EXPECT_FALSE(parser->HasError());
   // EXPECT_TRUE(parser->HasError());
 
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(buf));
-  EXPECT_EQ(L"// comments only", buf.AsStringView());
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(L"// comments only", buf.MakeString().c_str());
 }
 
 TEST(CXFA_FMParserTest, CommentThenValue) {
   const wchar_t ret[] =
-      L"(\nfunction ()\n{\n"
-      L"var pfm_ret = null;\n"
-      L"pfm_ret = 12;\n"
-      L"return pfm_rt.get_val(pfm_ret);\n"
-      L"}\n).call(this);\n";
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = 12;
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
 
   auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"; Just comment\n12");
-  std::unique_ptr<CXFA_FMFunctionDefinition> ast = parser->Parse();
-  ASSERT(ast != nullptr);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast);
   EXPECT_FALSE(parser->HasError());
 
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(buf));
-  EXPECT_EQ(ret, buf.AsStringView());
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
 }
 
 TEST(CXFA_FMParserTest, Parse) {
   const wchar_t input[] =
-      L"$ = Avg (-3, 5, -6, 12, -13);\n"
-      L"$ = Avg (Table2..Row[*].Cell1);\n"
-      L"\n"
-      L"if ($ ne -1)then\n"
-      L"  border.fill.color.value = \"255,64,64\";\n"
-      L"else\n"
-      L"  border.fill.color.value = \"20,170,13\";\n"
-      L"endif\n"
-      L"\n"
-      L"$";
+      LR"***($ = Avg (-3, 5, -6, 12, -13);
+$ = Avg (Table2..Row[*].Cell1);
+if ($ ne -1)then
+  border.fill.color.value = "255,64,64";
+elseif ($ ne -2) then
+  border.fill.color.value = "128,128,128";
+else
+  border.fill.color.value = "20,170,13";
+endif
+$)***";
 
   const wchar_t ret[] =
-      L"(\nfunction ()\n{\n"
-      L"var pfm_ret = null;\n"
-      L"if (pfm_rt.is_obj(this))\n{\n"
-      L"pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.neg_op(3), 5, "
-      L"pfm_rt.neg_op(6), 12, pfm_rt.neg_op(13)));\n"
-      L"}\n"
-      L"if (pfm_rt.is_obj(this))\n{\n"
-      L"pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.dot_acc(pfm_rt.dotdot_acc("
-      L"Table2, \"Table2\", \"Row\", 1), \"\", \"Cell1\", 0, 0)));\n"
-      L"}\n"
-      L"if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(1))))\n{\n"
-      L"if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc("
-      L"border, \"border\", \"fill\", 0, 0), \"\", \"color\", 0, 0), \"\", "
-      L"\"value\", 0, 0)))\n{\n"
-      L"pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc("
-      L"pfm_rt.dot_acc(border, \"border\", \"fill\", 0, 0), \"\", "
-      L"\"color\", 0, 0), \"\", \"value\", 0, 0), \"255,64,64\");\n"
-      L"}\n"
-      L"}\nelse\n{\n"
-      L"if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc("
-      L"border, \"border\", \"fill\", 0, 0), \"\", \"color\", 0, 0), \"\", "
-      L"\"value\", 0, 0)))\n{\n"
-      L"pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc("
-      L"pfm_rt.dot_acc(border, \"border\", \"fill\", 0, 0), \"\", "
-      L"\"color\", 0, 0), \"\", \"value\", 0, 0), \"20,170,13\");\n"
-      L"}\n"
-      L"}\n"
-      L"pfm_ret = this;\n"
-      L"return pfm_rt.get_val(pfm_ret);\n"
-      L"}\n).call(this);\n";
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+if (pfm_rt.is_obj(this))
+{
+pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.neg_op(3), 5, pfm_rt.neg_op(6), 12, pfm_rt.neg_op(13)));
+}
+if (pfm_rt.is_obj(this))
+{
+pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.dot_acc(pfm_rt.dotdot_acc(Table2, "Table2", "Row", 1), "", "Cell1", 0, 0)));
+}
+if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(1))))
+{
+if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
+{
+pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "255,64,64");
+}
+}
+else if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(2))))
+{
+if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
+{
+pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "128,128,128");
+}
+}
+else {
+if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
+{
+pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "20,170,13");
+}
+}
+pfm_ret = this;
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
 
   auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
-  std::unique_ptr<CXFA_FMFunctionDefinition> ast = parser->Parse();
-  ASSERT(ast != nullptr);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast);
   EXPECT_FALSE(parser->HasError());
 
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf buf;
-  EXPECT_TRUE(ast->ToJavaScript(buf));
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
   EXPECT_EQ(ret, buf.AsStringView());
 }
 
@@ -123,9 +145,372 @@
 
 TEST(CFXA_FMParserTest, chromium752201) {
   auto parser = pdfium::MakeUnique<CXFA_FMParser>(
-      L"fTep a\n"
-      L".#\n"
-      L"fo@ =[=l");
+      LR"***(fTep a
+.#
+fo@ =[=l)***");
   EXPECT_EQ(nullptr, parser->Parse());
   EXPECT_TRUE(parser->HasError());
 }
+
+TEST(CXFA_FMParserTest, MultipleAssignmentIsNotAllowed) {
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(L"(a=(b=t))=u");
+
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(!ast);
+  EXPECT_TRUE(parser->HasError());
+}
+
+TEST(CXFA_FMParserTest, ParseFuncWithParams) {
+  const wchar_t input[] =
+      LR"***(func MyFunction(param1, param2) do
+  param1 * param2
+endfunc)***";
+
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+function MyFunction(param1, param2) {
+var pfm_ret = null;
+pfm_ret = pfm_rt.mul_op(param1, param2);
+return pfm_ret;
+}
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast);
+  EXPECT_FALSE(parser->HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseFuncWithoutParams) {
+  const wchar_t input[] =
+      LR"***(func MyFunction() do
+  42
+endfunc)***";
+
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+function MyFunction() {
+var pfm_ret = null;
+pfm_ret = 42;
+return pfm_ret;
+}
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast);
+  EXPECT_FALSE(parser->HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseFuncWithBadParamsList) {
+  const wchar_t input[] =
+      LR"***(func MyFunction(param1,) do
+  param1 * param2
+endfunc)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser->HasError());
+}
+
+TEST(CXFA_FMParserTest, ParseBadIfExpression) {
+  const wchar_t input[] = L"if ( then";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser->HasError());
+}
+
+TEST(CXFA_FMParserTest, ParseBadElseIfExpression) {
+  const wchar_t input[] =
+      LR"***(if ($ ne -1) then"
+elseif( then)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser->HasError());
+}
+
+TEST(CXFA_FMParserTest, ParseDepthWithWideTree) {
+  const wchar_t input[] = L"a <> b <> c <> d <> e <> f <> g <> h <> i <> j";
+
+  {
+    auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+    std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+    ASSERT_TRUE(ast);
+    EXPECT_TRUE(!parser->HasError());
+  }
+
+  {
+    auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+    parser->SetMaxParseDepthForTest(5);
+    std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+    ASSERT_TRUE(ast == nullptr);
+    EXPECT_TRUE(parser->HasError());
+  }
+}
+
+TEST(CXFA_FMParserTest, ParseCallSmall) {
+  const wchar_t input[] = L"i.f(O)";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(i, function(obj) {
+    return obj.f(pfm_rt.get_val(O));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  EXPECT_FALSE(parser->HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseCallBig) {
+  const wchar_t input[] = L"i.f(O.e(O.e(O)))";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(i, function(obj) {
+    return obj.f(pfm_rt.get_val((function() {
+  return pfm_method_runner(O, function(obj) {
+    return obj.e(pfm_rt.get_val((function() {
+  return pfm_method_runner(O, function(obj) {
+    return obj.e(pfm_rt.get_val(O));
+  });
+}).call(this)));
+  });
+}).call(this)));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  EXPECT_FALSE(parser->HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseVar) {
+  const wchar_t input[] = LR"(var s = "")";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+var s = "";
+s = pfm_rt.var_filter(s);
+pfm_ret = s;
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  EXPECT_FALSE(parser->HasError());
+
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseFunctionCallNoArguments) {
+  const wchar_t input[] = L"P.x()";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(P, function(obj) {
+    return obj.x();
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  EXPECT_FALSE(parser->HasError());
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseFunctionCallSingleArgument) {
+  const wchar_t input[] = L"P.x(foo)";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(P, function(obj) {
+    return obj.x(pfm_rt.get_jsobj(foo));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  EXPECT_FALSE(parser->HasError());
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseFunctionCallMultipleArguments) {
+  const wchar_t input[] = L"P.x(foo, bar, baz)";
+  const wchar_t ret[] =
+      LR"***((function() {
+let pfm_method_runner = function(obj, cb) {
+  if (pfm_rt.is_ary(obj)) {
+    let pfm_method_return = null;
+    for (var idx = obj.length -1; idx > 1; idx--) {
+      pfm_method_return = cb(obj[idx]);
+    }
+    return pfm_method_return;
+  }
+  return cb(obj);
+};
+var pfm_ret = null;
+pfm_ret = pfm_rt.get_val((function() {
+  return pfm_method_runner(P, function(obj) {
+    return obj.x(pfm_rt.get_jsobj(foo), pfm_rt.get_val(bar), pfm_rt.get_val(baz));
+  });
+}).call(this));
+return pfm_rt.get_val(pfm_ret);
+}).call(this);)***";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  EXPECT_FALSE(parser->HasError());
+  CXFA_FMToJavaScriptDepth::Reset();
+  CFX_WideTextBuf buf;
+  EXPECT_TRUE(ast->ToJavaScript(&buf));
+  EXPECT_STREQ(ret, buf.MakeString().c_str());
+}
+
+TEST(CXFA_FMParserTest, ParseFunctionCallMissingCommas) {
+  const wchar_t input[] = L"P.x(!foo!bar!baz)";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser->HasError());
+}
+
+TEST(CXFA_FMParserTest, ParseFunctionCallTrailingComma) {
+  const wchar_t input[] = L"P.x(foo,bar,baz,)";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser->HasError());
+}
+
+TEST(CXFA_FMParserTest, ParseFunctionCallExtraComma) {
+  const wchar_t input[] = L"P.x(foo,bar,,baz)";
+
+  auto parser = pdfium::MakeUnique<CXFA_FMParser>(input);
+  std::unique_ptr<CXFA_FMAST> ast = parser->Parse();
+  ASSERT_TRUE(ast == nullptr);
+  EXPECT_TRUE(parser->HasError());
+}
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
index 373ba91..e94f55a 100644
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
@@ -7,7 +7,6 @@
 #include "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h"
 
 #include <algorithm>
-#include <iostream>
 #include <utility>
 
 #include "core/fxcrt/autorestorer.h"
@@ -18,19 +17,6 @@
 
 namespace {
 
-// Indexed by XFA_FM_SimpleExpressionType
-const wchar_t* const gs_lpStrExpFuncName[] = {
-    L"pfm_rt.asgn_val_op", L"pfm_rt.log_or_op",  L"pfm_rt.log_and_op",
-    L"pfm_rt.eq_op",       L"pfm_rt.neq_op",     L"pfm_rt.lt_op",
-    L"pfm_rt.le_op",       L"pfm_rt.gt_op",      L"pfm_rt.ge_op",
-    L"pfm_rt.plus_op",     L"pfm_rt.minus_op",   L"pfm_rt.mul_op",
-    L"pfm_rt.div_op",      L"pfm_rt.pos_op",     L"pfm_rt.neg_op",
-    L"pfm_rt.log_not_op",  L"pfm_rt.",           L"pfm_rt.dot_acc",
-    L"pfm_rt.dotdot_acc",  L"pfm_rt.concat_obj", L"pfm_rt.is_obj",
-    L"pfm_rt.is_ary",      L"pfm_rt.get_val",    L"pfm_rt.get_jsobj",
-    L"pfm_rt.var_filter",
-};
-
 const wchar_t* const g_BuiltInFuncs[] = {
     L"Abs",          L"Apr",       L"At",       L"Avg",
     L"Ceil",         L"Choose",    L"Concat",   L"Count",
@@ -84,498 +70,331 @@
 
 }  // namespace
 
-WideStringView XFA_FM_EXPTypeToString(
-    XFA_FM_SimpleExpressionType simpleExpType) {
-  return gs_lpStrExpFuncName[simpleExpType];
-}
-
-CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(uint32_t line, XFA_FM_TOKEN op)
-    : m_line(line), m_op(op) {}
-
-bool CXFA_FMSimpleExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
-}
-
-bool CXFA_FMSimpleExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
-}
+CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op) : m_op(op) {}
 
 XFA_FM_TOKEN CXFA_FMSimpleExpression::GetOperatorToken() const {
   return m_op;
 }
 
-CXFA_FMNullExpression::CXFA_FMNullExpression(uint32_t line)
-    : CXFA_FMSimpleExpression(line, TOKnull) {}
+CXFA_FMNullExpression::CXFA_FMNullExpression()
+    : CXFA_FMSimpleExpression(TOKnull) {}
 
-bool CXFA_FMNullExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMNullExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"null";
-  return !CXFA_IsTooBig(javascript);
+  *js << "null";
+  return !CXFA_IsTooBig(js);
 }
 
-CXFA_FMNumberExpression::CXFA_FMNumberExpression(uint32_t line,
-                                                 WideStringView wsNumber)
-    : CXFA_FMSimpleExpression(line, TOKnumber), m_wsNumber(wsNumber) {}
+CXFA_FMNumberExpression::CXFA_FMNumberExpression(WideStringView wsNumber)
+    : CXFA_FMSimpleExpression(TOKnumber), m_wsNumber(wsNumber) {}
 
-CXFA_FMNumberExpression::~CXFA_FMNumberExpression() {}
+CXFA_FMNumberExpression::~CXFA_FMNumberExpression() = default;
 
-bool CXFA_FMNumberExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMNumberExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                           ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << m_wsNumber;
-  return !CXFA_IsTooBig(javascript);
+  *js << m_wsNumber;
+  return !CXFA_IsTooBig(js);
 }
 
-CXFA_FMStringExpression::CXFA_FMStringExpression(uint32_t line,
-                                                 WideStringView wsString)
-    : CXFA_FMSimpleExpression(line, TOKstring), m_wsString(wsString) {}
+CXFA_FMStringExpression::CXFA_FMStringExpression(WideStringView wsString)
+    : CXFA_FMSimpleExpression(TOKstring), m_wsString(wsString) {}
 
-CXFA_FMStringExpression::~CXFA_FMStringExpression() {}
+CXFA_FMStringExpression::~CXFA_FMStringExpression() = default;
 
-bool CXFA_FMStringExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMStringExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                           ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
   WideString tempStr(m_wsString);
   if (tempStr.GetLength() <= 2) {
-    javascript << tempStr;
-    return !CXFA_IsTooBig(javascript);
+    *js << tempStr;
+    return !CXFA_IsTooBig(js);
   }
-  javascript.AppendChar(L'\"');
+
+  *js << "\"";
   for (size_t i = 1; i < tempStr.GetLength() - 1; i++) {
     wchar_t oneChar = tempStr[i];
     switch (oneChar) {
       case L'\"':
-        i++;
-        javascript << L"\\\"";
+        ++i;
+        *js << "\\\"";
         break;
       case 0x0d:
         break;
       case 0x0a:
-        javascript << L"\\n";
+        *js << "\\n";
         break;
       default:
-        javascript.AppendChar(oneChar);
+        js->AppendChar(oneChar);
         break;
     }
   }
-  javascript.AppendChar(L'\"');
-  return !CXFA_IsTooBig(javascript);
+  *js << "\"";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMIdentifierExpression::CXFA_FMIdentifierExpression(
-    uint32_t line,
     WideStringView wsIdentifier)
-    : CXFA_FMSimpleExpression(line, TOKidentifier),
-      m_wsIdentifier(wsIdentifier) {}
+    : CXFA_FMSimpleExpression(TOKidentifier), m_wsIdentifier(wsIdentifier) {}
 
-CXFA_FMIdentifierExpression::~CXFA_FMIdentifierExpression() {}
+CXFA_FMIdentifierExpression::~CXFA_FMIdentifierExpression() = default;
 
-bool CXFA_FMIdentifierExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMIdentifierExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                               ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  WideString tempStr(m_wsIdentifier);
-  if (tempStr == L"$") {
-    tempStr = L"this";
-  } else if (tempStr == L"!") {
-    tempStr = L"xfa.datasets";
-  } else if (tempStr == L"$data") {
-    tempStr = L"xfa.datasets.data";
-  } else if (tempStr == L"$event") {
-    tempStr = L"xfa.event";
-  } else if (tempStr == L"$form") {
-    tempStr = L"xfa.form";
-  } else if (tempStr == L"$host") {
-    tempStr = L"xfa.host";
-  } else if (tempStr == L"$layout") {
-    tempStr = L"xfa.layout";
-  } else if (tempStr == L"$template") {
-    tempStr = L"xfa.template";
-  } else if (tempStr[0] == L'!') {
-    tempStr =
-        EXCLAMATION_IN_IDENTIFIER + tempStr.Right(tempStr.GetLength() - 1);
-  }
-  javascript << tempStr;
-  return !CXFA_IsTooBig(javascript);
-}
+  if (m_wsIdentifier.EqualsASCII("$"))
+    *js << "this";
+  else if (m_wsIdentifier.EqualsASCII("!"))
+    *js << "xfa.datasets";
+  else if (m_wsIdentifier.EqualsASCII("$data"))
+    *js << "xfa.datasets.data";
+  else if (m_wsIdentifier.EqualsASCII("$event"))
+    *js << "xfa.event";
+  else if (m_wsIdentifier.EqualsASCII("$form"))
+    *js << "xfa.form";
+  else if (m_wsIdentifier.EqualsASCII("$host"))
+    *js << "xfa.host";
+  else if (m_wsIdentifier.EqualsASCII("$layout"))
+    *js << "xfa.layout";
+  else if (m_wsIdentifier.EqualsASCII("$template"))
+    *js << "xfa.template";
+  else if (m_wsIdentifier[0] == L'!')
+    *js << "pfm__excl__" << m_wsIdentifier.Last(m_wsIdentifier.GetLength() - 1);
+  else
+    *js << m_wsIdentifier;
 
-CXFA_FMUnaryExpression::CXFA_FMUnaryExpression(
-    uint32_t line,
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMSimpleExpression(line, op), m_pExp(std::move(pExp)) {}
-
-CXFA_FMUnaryExpression::~CXFA_FMUnaryExpression() {}
-
-bool CXFA_FMUnaryExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
-}
-
-CXFA_FMBinExpression::CXFA_FMBinExpression(
-    uint32_t line,
-    XFA_FM_TOKEN op,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMSimpleExpression(line, op),
-      m_pExp1(std::move(pExp1)),
-      m_pExp2(std::move(pExp2)) {}
-
-CXFA_FMBinExpression::~CXFA_FMBinExpression() {}
-
-bool CXFA_FMBinExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  return !CXFA_IsTooBig(javascript) && depthManager.IsWithinMaxDepth();
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMAssignExpression::CXFA_FMAssignExpression(
-    uint32_t line,
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(line, op, std::move(pExp1), std::move(pExp2)) {}
+    : CXFA_FMSimpleExpression(op),
+      m_pExp1(std::move(pExp1)),
+      m_pExp2(std::move(pExp2)) {}
 
-bool CXFA_FMAssignExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+CXFA_FMAssignExpression::~CXFA_FMAssignExpression() = default;
+
+bool CXFA_FMAssignExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                           ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"if (";
-  javascript << gs_lpStrExpFuncName[ISFMOBJECT];
-  javascript << L"(";
   CFX_WideTextBuf tempExp1;
-  if (!m_pExp1->ToJavaScript(tempExp1))
+  if (!m_pExp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
     return false;
-  javascript << tempExp1;
-  javascript << L"))\n{\n";
-  javascript << gs_lpStrExpFuncName[ASSIGN];
-  javascript << L"(";
-  javascript << tempExp1;
-  javascript << L", ";
+
+  *js << "if (pfm_rt.is_obj(" << tempExp1 << "))\n{\n";
+  if (type == ReturnType::kImplied)
+    *js << "pfm_ret = ";
 
   CFX_WideTextBuf tempExp2;
-  if (!m_pExp2->ToJavaScript(tempExp2))
+  if (!m_pExp2->ToJavaScript(&tempExp2, ReturnType::kInfered))
     return false;
-  javascript << tempExp2;
-  javascript << L");\n}\n";
+
+  *js << "pfm_rt.asgn_val_op(" << tempExp1 << ", " << tempExp2 << ");\n}\n";
+
   if (m_pExp1->GetOperatorToken() == TOKidentifier &&
-      tempExp1.AsStringView() != L"this") {
-    javascript << L"else\n{\n";
-    javascript << tempExp1;
-    javascript << L" = ";
-    javascript << gs_lpStrExpFuncName[ASSIGN];
-    javascript << L"(";
-    javascript << tempExp1;
-    javascript << L", ";
-    javascript << tempExp2;
-    javascript << L");\n}\n";
+      !tempExp1.AsStringView().EqualsASCII("this")) {
+    *js << "else\n{\n";
+    if (type == ReturnType::kImplied)
+      *js << "pfm_ret = ";
+
+    *js << tempExp1 << " = pfm_rt.asgn_val_op";
+    *js << "(" << tempExp1 << ", " << tempExp2 << ");\n";
+    *js << "}\n";
   }
-  return !CXFA_IsTooBig(javascript);
+  return !CXFA_IsTooBig(js);
 }
 
-bool CXFA_FMAssignExpression::ToImpliedReturnJS(CFX_WideTextBuf& javascript) {
+CXFA_FMBinExpression::CXFA_FMBinExpression(
+    const WideString& opName,
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMSimpleExpression(op),
+      m_OpName(opName),
+      m_pExp1(std::move(pExp1)),
+      m_pExp2(std::move(pExp2)) {}
+
+CXFA_FMBinExpression::~CXFA_FMBinExpression() = default;
+
+bool CXFA_FMBinExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"if (";
-  javascript << gs_lpStrExpFuncName[ISFMOBJECT];
-  javascript << L"(";
-  CFX_WideTextBuf tempExp1;
-  if (!m_pExp1->ToJavaScript(tempExp1))
+  *js << "pfm_rt." << m_OpName << "(";
+  if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << tempExp1;
-  javascript << L"))\n{\n";
-  javascript << RUNTIMEFUNCTIONRETURNVALUE;
-  javascript << L" = ";
-  javascript << gs_lpStrExpFuncName[ASSIGN];
-  javascript << L"(";
-  javascript << tempExp1;
-  javascript << L", ";
-
-  CFX_WideTextBuf tempExp2;
-  if (!m_pExp2->ToJavaScript(tempExp2))
+  *js << ", ";
+  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << tempExp2;
-  javascript << L");\n}\n";
-  if (m_pExp1->GetOperatorToken() == TOKidentifier &&
-      tempExp1.AsStringView() != L"this") {
-    javascript << L"else\n{\n";
-    javascript << RUNTIMEFUNCTIONRETURNVALUE;
-    javascript << L" = ";
-    javascript << tempExp1;
-    javascript << L" = ";
-    javascript << gs_lpStrExpFuncName[ASSIGN];
-    javascript << L"(";
-    javascript << tempExp1;
-    javascript << L", ";
-    javascript << tempExp2;
-    javascript << L");\n}\n";
-  }
-  return !CXFA_IsTooBig(javascript);
+  *js << ")";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMLogicalOrExpression::CXFA_FMLogicalOrExpression(
-    uint32_t line,
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(line, op, std::move(pExp1), std::move(pExp2)) {}
-
-bool CXFA_FMLogicalOrExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << gs_lpStrExpFuncName[LOGICALOR];
-  javascript << L"(";
-  if (!m_pExp1->ToJavaScript(javascript))
-    return false;
-  javascript << L", ";
-  if (!m_pExp2->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
+    : CXFA_FMBinExpression(L"log_or_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
 
 CXFA_FMLogicalAndExpression::CXFA_FMLogicalAndExpression(
-    uint32_t line,
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(line, op, std::move(pExp1), std::move(pExp2)) {}
+    : CXFA_FMBinExpression(L"log_and_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
 
-bool CXFA_FMLogicalAndExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << gs_lpStrExpFuncName[LOGICALAND];
-  javascript << L"(";
-  if (!m_pExp1->ToJavaScript(javascript))
-    return false;
-  javascript << L", ";
-  if (!m_pExp2->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
-
-CXFA_FMEqualityExpression::CXFA_FMEqualityExpression(
-    uint32_t line,
+CXFA_FMEqualExpression::CXFA_FMEqualExpression(
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(line, op, std::move(pExp1), std::move(pExp2)) {}
+    : CXFA_FMBinExpression(L"eq_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
 
-bool CXFA_FMEqualityExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  switch (m_op) {
-    case TOKeq:
-    case TOKkseq:
-      javascript << gs_lpStrExpFuncName[EQUALITY];
-      break;
-    case TOKne:
-    case TOKksne:
-      javascript << gs_lpStrExpFuncName[NOTEQUALITY];
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  javascript << L"(";
-  if (!m_pExp1->ToJavaScript(javascript))
-    return false;
-  javascript << L", ";
-  if (!m_pExp2->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
-
-CXFA_FMRelationalExpression::CXFA_FMRelationalExpression(
-    uint32_t line,
+CXFA_FMNotEqualExpression::CXFA_FMNotEqualExpression(
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(line, op, std::move(pExp1), std::move(pExp2)) {}
+    : CXFA_FMBinExpression(L"neq_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
 
-bool CXFA_FMRelationalExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  switch (m_op) {
-    case TOKlt:
-    case TOKkslt:
-      javascript << gs_lpStrExpFuncName[LESS];
-      break;
-    case TOKgt:
-    case TOKksgt:
-      javascript << gs_lpStrExpFuncName[GREATER];
-      break;
-    case TOKle:
-    case TOKksle:
-      javascript << gs_lpStrExpFuncName[LESSEQUAL];
-      break;
-    case TOKge:
-    case TOKksge:
-      javascript << gs_lpStrExpFuncName[GREATEREQUAL];
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  javascript << L"(";
-  if (!m_pExp1->ToJavaScript(javascript))
-    return false;
-  javascript << L", ";
-  if (!m_pExp2->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
-
-CXFA_FMAdditiveExpression::CXFA_FMAdditiveExpression(
-    uint32_t line,
+CXFA_FMGtExpression::CXFA_FMGtExpression(
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(line, op, std::move(pExp1), std::move(pExp2)) {}
+    : CXFA_FMBinExpression(L"gt_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
 
-bool CXFA_FMAdditiveExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  switch (m_op) {
-    case TOKplus:
-      javascript << gs_lpStrExpFuncName[PLUS];
-      break;
-    case TOKminus:
-      javascript << gs_lpStrExpFuncName[MINUS];
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  javascript << L"(";
-  if (!m_pExp1->ToJavaScript(javascript))
-    return false;
-  javascript << L", ";
-  if (!m_pExp2->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
-
-CXFA_FMMultiplicativeExpression::CXFA_FMMultiplicativeExpression(
-    uint32_t line,
+CXFA_FMGeExpression::CXFA_FMGeExpression(
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMBinExpression(line, op, std::move(pExp1), std::move(pExp2)) {}
+    : CXFA_FMBinExpression(L"ge_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
 
-bool CXFA_FMMultiplicativeExpression::ToJavaScript(
-    CFX_WideTextBuf& javascript) {
+CXFA_FMLtExpression::CXFA_FMLtExpression(
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMBinExpression(L"lt_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
+
+CXFA_FMLeExpression::CXFA_FMLeExpression(
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMBinExpression(L"le_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
+
+CXFA_FMPlusExpression::CXFA_FMPlusExpression(
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMBinExpression(L"plus_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
+
+CXFA_FMMinusExpression::CXFA_FMMinusExpression(
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMBinExpression(L"minus_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
+
+CXFA_FMMulExpression::CXFA_FMMulExpression(
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMBinExpression(L"mul_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
+
+CXFA_FMDivExpression::CXFA_FMDivExpression(
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMBinExpression(L"div_op",
+                           op,
+                           std::move(pExp1),
+                           std::move(pExp2)) {}
+
+CXFA_FMUnaryExpression::CXFA_FMUnaryExpression(
+    const WideString& opName,
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp)
+    : CXFA_FMSimpleExpression(op), m_OpName(opName), m_pExp(std::move(pExp)) {}
+
+CXFA_FMUnaryExpression::~CXFA_FMUnaryExpression() = default;
+
+bool CXFA_FMUnaryExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                          ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  switch (m_op) {
-    case TOKmul:
-      javascript << gs_lpStrExpFuncName[MULTIPLE];
-      break;
-    case TOKdiv:
-      javascript << gs_lpStrExpFuncName[DIVIDE];
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  javascript << L"(";
-  if (!m_pExp1->ToJavaScript(javascript))
+  *js << "pfm_rt." << m_OpName.c_str() << "(";
+  if (!m_pExp->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << L", ";
-  if (!m_pExp2->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
+  *js << ")";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMPosExpression::CXFA_FMPosExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMUnaryExpression(line, TOKplus, std::move(pExp)) {}
-
-bool CXFA_FMPosExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << gs_lpStrExpFuncName[POSITIVE];
-  javascript << L"(";
-  if (!m_pExp->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
+    : CXFA_FMUnaryExpression(L"pos_op", TOKplus, std::move(pExp)) {}
 
 CXFA_FMNegExpression::CXFA_FMNegExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMUnaryExpression(line, TOKminus, std::move(pExp)) {}
-
-bool CXFA_FMNegExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << gs_lpStrExpFuncName[NEGATIVE];
-  javascript << L"(";
-  if (!m_pExp->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
+    : CXFA_FMUnaryExpression(L"neg_op", TOKminus, std::move(pExp)) {}
 
 CXFA_FMNotExpression::CXFA_FMNotExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp)
-    : CXFA_FMUnaryExpression(line, TOKksnot, std::move(pExp)) {}
-
-bool CXFA_FMNotExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
-  CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
-    return false;
-
-  javascript << gs_lpStrExpFuncName[NOT];
-  javascript << L"(";
-  if (!m_pExp->ToJavaScript(javascript))
-    return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
-}
+    : CXFA_FMUnaryExpression(L"log_not_op", TOKksnot, std::move(pExp)) {}
 
 CXFA_FMCallExpression::CXFA_FMCallExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp,
     std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pArguments,
     bool bIsSomMethod)
-    : CXFA_FMUnaryExpression(line, TOKcall, std::move(pExp)),
+    : CXFA_FMSimpleExpression(TOKcall),
+      m_pExp(std::move(pExp)),
       m_bIsSomMethod(bIsSomMethod),
       m_Arguments(std::move(pArguments)) {}
 
@@ -585,12 +404,12 @@
   if (funcName->GetLength() > g_BuiltInFuncsMaxLen)
     return false;
 
-  auto cmpFunc = [](const wchar_t* iter, const WideString& val) -> bool {
-    return val.CompareNoCase(iter) > 0;
-  };
   WideString str = funcName->MakeString();
   const wchar_t* const* pMatchResult = std::lower_bound(
-      std::begin(g_BuiltInFuncs), std::end(g_BuiltInFuncs), str, cmpFunc);
+      std::begin(g_BuiltInFuncs), std::end(g_BuiltInFuncs), str,
+      [](const wchar_t* iter, const WideString& val) -> bool {
+        return val.CompareNoCase(iter) > 0;
+      });
   if (pMatchResult != std::end(g_BuiltInFuncs) &&
       !str.CompareNoCase(*pMatchResult)) {
     funcName->Clear();
@@ -602,12 +421,11 @@
 
 uint32_t CXFA_FMCallExpression::IsMethodWithObjParam(
     const WideString& methodName) {
-  auto cmpFunc = [](const XFA_FMSOMMethod iter, const WideString& val) {
-    return val.Compare(iter.m_wsSomMethodName) > 0;
-  };
-  const XFA_FMSOMMethod* result =
-      std::lower_bound(std::begin(gs_FMSomMethods), std::end(gs_FMSomMethods),
-                       methodName, cmpFunc);
+  const XFA_FMSOMMethod* result = std::lower_bound(
+      std::begin(gs_FMSomMethods), std::end(gs_FMSomMethods), methodName,
+      [](const XFA_FMSOMMethod iter, const WideString& val) {
+        return val.Compare(iter.m_wsSomMethodName) > 0;
+      });
   if (result != std::end(gs_FMSomMethods) &&
       !methodName.Compare(result->m_wsSomMethodName)) {
     return result->m_dParameters;
@@ -615,280 +433,260 @@
   return 0;
 }
 
-bool CXFA_FMCallExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMCallExpression::ToJavaScript(CFX_WideTextBuf* js, ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
   CFX_WideTextBuf funcName;
-  if (!m_pExp->ToJavaScript(funcName))
+  if (!m_pExp->ToJavaScript(&funcName, ReturnType::kInfered))
     return false;
+
   if (m_bIsSomMethod) {
-    javascript << funcName;
-    javascript << L"(";
+    *js << funcName << "(";
     uint32_t methodPara = IsMethodWithObjParam(funcName.MakeString());
     if (methodPara > 0) {
       for (size_t i = 0; i < m_Arguments.size(); ++i) {
         // Currently none of our expressions use objects for a parameter over
         // the 6th. Make sure we don't overflow the shift when doing this
         // check. If we ever need more the 32 object params we can revisit.
-        if (i < 32 && (methodPara & (0x01 << i)) > 0) {
-          javascript << gs_lpStrExpFuncName[GETFMJSOBJ];
-        } else {
-          javascript << gs_lpStrExpFuncName[GETFMVALUE];
-        }
-        javascript << L"(";
-        const auto& expr = m_Arguments[i];
-        if (!expr->ToJavaScript(javascript))
+        *js << "pfm_rt.get_";
+        if (i < 32 && (methodPara & (0x01 << i)) > 0)
+          *js << "jsobj";
+        else
+          *js << "val";
+
+        *js << "(";
+        if (!m_Arguments[i]->ToJavaScript(js, ReturnType::kInfered))
           return false;
-        javascript << L")";
-        if (i + 1 < m_Arguments.size()) {
-          javascript << L", ";
-        }
+        *js << ")";
+        if (i + 1 < m_Arguments.size())
+          *js << ", ";
       }
     } else {
       for (const auto& expr : m_Arguments) {
-        javascript << gs_lpStrExpFuncName[GETFMVALUE];
-        javascript << L"(";
-        if (!expr->ToJavaScript(javascript))
+        *js << "pfm_rt.get_val(";
+        if (!expr->ToJavaScript(js, ReturnType::kInfered))
           return false;
-        javascript << L")";
+        *js << ")";
         if (expr != m_Arguments.back())
-          javascript << L", ";
+          *js << ", ";
       }
     }
-    javascript << L")";
+    *js << ")";
+    return !CXFA_IsTooBig(js);
+  }
+
+  bool isEvalFunc = false;
+  bool isExistsFunc = false;
+  if (!IsBuiltInFunc(&funcName)) {
+    // If a function is not a SomMethod or a built-in then the input was
+    // invalid, so failing. The scanner/lexer should catch this, but currently
+    // doesn't. This failure will bubble up to the top-level and cause the
+    // transpile to fail.
+    return false;
+  }
+
+  if (funcName.AsStringView().EqualsASCII("Eval")) {
+    isEvalFunc = true;
+    *js << "eval.call(this, pfm_rt.Translate";
   } else {
-    bool isEvalFunc = false;
-    bool isExistsFunc = false;
-    if (IsBuiltInFunc(&funcName)) {
-      if (funcName.AsStringView() == L"Eval") {
-        isEvalFunc = true;
-        javascript << L"eval.call(this, ";
-        javascript << gs_lpStrExpFuncName[CALL];
-        javascript << L"Translate";
-      } else if (funcName.AsStringView() == L"Exists") {
-        isExistsFunc = true;
-        javascript << gs_lpStrExpFuncName[CALL];
-        javascript << funcName;
-      } else {
-        javascript << gs_lpStrExpFuncName[CALL];
-        javascript << funcName;
-      }
+    if (funcName.AsStringView().EqualsASCII("Exists"))
+      isExistsFunc = true;
+
+    *js << "pfm_rt." << funcName;
+  }
+
+  *js << "(";
+  if (isExistsFunc) {
+    *js << "\n(\nfunction ()\n{\ntry\n{\n";
+    if (!m_Arguments.empty()) {
+      *js << "return ";
+      if (!m_Arguments[0]->ToJavaScript(js, ReturnType::kInfered))
+        return false;
+      *js << ";\n}\n";
     } else {
-      // If a function is not a SomMethod or a built-in then the input was
-      // invalid, so failing. The scanner/lexer should catch this, but currently
-      // doesn't. This failure will bubble up to the top-level and cause the
-      // transpile to fail.
-      return false;
+      *js << "return 0;\n}\n";
     }
-    javascript << L"(";
-    if (isExistsFunc) {
-      javascript << L"\n(\nfunction ()\n{\ntry\n{\n";
-      if (!m_Arguments.empty()) {
-        const auto& expr = m_Arguments[0];
-        javascript << L"return ";
-        if (!expr->ToJavaScript(javascript))
-          return false;
-        javascript << L";\n}\n";
-      } else {
-        javascript << L"return 0;\n}\n";
-      }
-      javascript << L"catch(accessExceptions)\n";
-      javascript << L"{\nreturn 0;\n}\n}\n).call(this)\n";
-    } else {
-      for (const auto& expr : m_Arguments) {
-        if (!expr->ToJavaScript(javascript))
-          return false;
-        if (expr != m_Arguments.back())
-          javascript << L", ";
-      }
-    }
-    javascript << L")";
-    if (isEvalFunc) {
-      javascript << L")";
+    *js << "catch(accessExceptions)\n";
+    *js << "{\nreturn 0;\n}\n}\n).call(this)\n";
+  } else {
+    for (const auto& expr : m_Arguments) {
+      if (!expr->ToJavaScript(js, ReturnType::kInfered))
+        return false;
+      if (expr != m_Arguments.back())
+        *js << ", ";
     }
   }
-  return !CXFA_IsTooBig(javascript);
+  *js << ")";
+  if (isEvalFunc)
+    *js << ")";
+
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMDotAccessorExpression::CXFA_FMDotAccessorExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
     XFA_FM_TOKEN op,
     WideStringView wsIdentifier,
     std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp)
-    : CXFA_FMBinExpression(line,
-                           op,
-                           std::move(pAccessor),
-                           std::move(pIndexExp)),
-      m_wsIdentifier(wsIdentifier) {}
+    : CXFA_FMSimpleExpression(op),
+      m_wsIdentifier(wsIdentifier),
+      m_pExp1(std::move(pAccessor)),
+      m_pExp2(std::move(pIndexExp)) {}
 
-CXFA_FMDotAccessorExpression::~CXFA_FMDotAccessorExpression() {}
+CXFA_FMDotAccessorExpression::~CXFA_FMDotAccessorExpression() = default;
 
-bool CXFA_FMDotAccessorExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+bool CXFA_FMDotAccessorExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                                ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << gs_lpStrExpFuncName[DOT];
-  javascript << L"(";
+  *js << "pfm_rt.dot_acc(";
+
   CFX_WideTextBuf tempExp1;
   if (m_pExp1) {
-    if (!m_pExp1->ToJavaScript(tempExp1))
+    if (!m_pExp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
       return false;
-    javascript << tempExp1;
+
+    *js << tempExp1;
   } else {
-    javascript << L"null";
+    *js << "null";
   }
-  javascript << L", ";
-  javascript << L"\"";
+  *js << ", \"";
 
   if (m_pExp1 && m_pExp1->GetOperatorToken() == TOKidentifier)
-    javascript << tempExp1;
-  javascript << L"\", ";
-  if (m_op == TOKdotscream) {
-    javascript << L"\"#";
-    javascript << m_wsIdentifier;
-    javascript << L"\", ";
-  } else if (m_op == TOKdotstar) {
-    javascript << L"\"*\", ";
-  } else if (m_op == TOKcall) {
-    javascript << L"\"\", ";
-  } else {
-    javascript << L"\"";
-    javascript << m_wsIdentifier;
-    javascript << L"\", ";
-  }
-  if (!m_pExp2->ToJavaScript(javascript))
+    *js << tempExp1;
+
+  *js << "\", ";
+  if (m_op == TOKdotscream)
+    *js << "\"#" << m_wsIdentifier << "\", ";
+  else if (m_op == TOKdotstar)
+    *js << "\"*\", ";
+  else if (m_op == TOKcall)
+    *js << "\"\", ";
+  else
+    *js << "\"" << m_wsIdentifier << "\", ";
+
+  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
+
+  *js << ")";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMIndexExpression::CXFA_FMIndexExpression(
-    uint32_t line,
     XFA_FM_AccessorIndex accessorIndex,
     std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp,
     bool bIsStarIndex)
-    : CXFA_FMUnaryExpression(line, TOKlbracket, std::move(pIndexExp)),
+    : CXFA_FMSimpleExpression(TOKlbracket),
+      m_pExp(std::move(pIndexExp)),
       m_accessorIndex(accessorIndex),
       m_bIsStarIndex(bIsStarIndex) {}
 
-bool CXFA_FMIndexExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+CXFA_FMIndexExpression::~CXFA_FMIndexExpression() = default;
+
+bool CXFA_FMIndexExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                          ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
   switch (m_accessorIndex) {
     case ACCESSOR_NO_INDEX:
-      javascript << L"0";
+      *js << "0";
       break;
     case ACCESSOR_NO_RELATIVEINDEX:
-      javascript << L"1";
+      *js << "1";
       break;
     case ACCESSOR_POSITIVE_INDEX:
-      javascript << L"2";
+      *js << "2";
       break;
     case ACCESSOR_NEGATIVE_INDEX:
-      javascript << L"3";
+      *js << "3";
       break;
     default:
-      javascript << L"0";
+      *js << "0";
   }
-  if (!m_bIsStarIndex) {
-    javascript << L", ";
-    if (m_pExp) {
-      if (!m_pExp->ToJavaScript(javascript))
-        return false;
-    } else {
-      javascript << L"0";
-    }
+  if (m_bIsStarIndex)
+    return !CXFA_IsTooBig(js);
+
+  *js << ", ";
+  if (m_pExp) {
+    if (!m_pExp->ToJavaScript(js, ReturnType::kInfered))
+      return false;
+  } else {
+    *js << "0";
   }
-  return !CXFA_IsTooBig(javascript);
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMDotDotAccessorExpression::CXFA_FMDotDotAccessorExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
     XFA_FM_TOKEN op,
     WideStringView wsIdentifier,
     std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp)
-    : CXFA_FMBinExpression(line,
-                           op,
-                           std::move(pAccessor),
-                           std::move(pIndexExp)),
-      m_wsIdentifier(wsIdentifier) {}
+    : CXFA_FMSimpleExpression(op),
+      m_wsIdentifier(wsIdentifier),
+      m_pExp1(std::move(pAccessor)),
+      m_pExp2(std::move(pIndexExp)) {}
 
-CXFA_FMDotDotAccessorExpression::~CXFA_FMDotDotAccessorExpression() {}
+CXFA_FMDotDotAccessorExpression::~CXFA_FMDotDotAccessorExpression() = default;
 
-bool CXFA_FMDotDotAccessorExpression::ToJavaScript(
-    CFX_WideTextBuf& javascript) {
+bool CXFA_FMDotDotAccessorExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                                   ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << gs_lpStrExpFuncName[DOTDOT];
-  javascript << L"(";
-  CFX_WideTextBuf tempExp1;
-  if (!m_pExp1->ToJavaScript(tempExp1))
+  *js << "pfm_rt.dotdot_acc(";
+  if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << tempExp1;
-  javascript << L", ";
-  javascript << L"\"";
+  *js << ", "
+      << "\"";
+  if (m_pExp1->GetOperatorToken() == TOKidentifier) {
+    if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
+      return false;
+  }
 
-  if (m_pExp1->GetOperatorToken() == TOKidentifier)
-    javascript << tempExp1;
-  javascript << L"\", ";
-  javascript << L"\"";
-  javascript << m_wsIdentifier;
-  javascript << L"\", ";
-  if (!m_pExp2->ToJavaScript(javascript))
+  *js << "\", \"" << m_wsIdentifier << "\", ";
+  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << L")";
-  return !CXFA_IsTooBig(javascript);
+  *js << ")";
+  return !CXFA_IsTooBig(js);
 }
 
 CXFA_FMMethodCallExpression::CXFA_FMMethodCallExpression(
-    uint32_t line,
     std::unique_ptr<CXFA_FMSimpleExpression> pAccessorExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pCallExp)
-    : CXFA_FMBinExpression(line,
-                           TOKdot,
-                           std::move(pAccessorExp1),
-                           std::move(pCallExp)) {}
+    : CXFA_FMSimpleExpression(TOKdot),
+      m_pExp1(std::move(pAccessorExp1)),
+      m_pExp2(std::move(pCallExp)) {}
 
-bool CXFA_FMMethodCallExpression::ToJavaScript(CFX_WideTextBuf& javascript) {
+CXFA_FMMethodCallExpression::~CXFA_FMMethodCallExpression() = default;
+
+bool CXFA_FMMethodCallExpression::ToJavaScript(CFX_WideTextBuf* js,
+                                               ReturnType type) {
   CXFA_FMToJavaScriptDepth depthManager;
-  if (CXFA_IsTooBig(javascript) || !depthManager.IsWithinMaxDepth())
+  if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
-  javascript << L"(\nfunction ()\n{\n";
-  javascript << L"var method_return_value = null;\n";
-  javascript << L"var accessor_object = ";
-  if (!m_pExp1->ToJavaScript(javascript))
+  CFX_WideTextBuf buf;
+  if (!m_pExp1->ToJavaScript(&buf, ReturnType::kInfered))
     return false;
-  javascript << L";\n";
-  javascript << L"if (";
-  javascript << gs_lpStrExpFuncName[ISFMARRAY];
-  javascript << L"(accessor_object))\n{\n";
-  javascript << L"for(var index = accessor_object.length - 1; index > 1; "
-                L"index--)\n{\n";
-  javascript << L"method_return_value = accessor_object[index].";
 
-  CFX_WideTextBuf tempExp2;
-  if (!m_pExp2->ToJavaScript(tempExp2))
+  *js << "(function() {\n";
+  *js << "  return pfm_method_runner(" << buf << ", function(obj) {\n";
+  *js << "    return obj.";
+  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
     return false;
-  javascript << tempExp2;
-  javascript << L";\n}\n}\n";
-  javascript << L"else\n{\nmethod_return_value = accessor_object.";
-  javascript << tempExp2;
-  javascript << L";\n}\n";
-  javascript << L"return method_return_value;\n";
-  javascript << L"}\n).call(this)";
-  return !CXFA_IsTooBig(javascript);
+  *js << ";\n";
+  *js << "  });\n";
+  *js << "}).call(this)";
+  return !CXFA_IsTooBig(js);
 }
 
-bool CXFA_IsTooBig(const CFX_WideTextBuf& javascript) {
-  return javascript.GetSize() >= 256 * 1024 * 1024;
+bool CXFA_IsTooBig(const CFX_WideTextBuf* js) {
+  return js->GetSize() >= 256 * 1024 * 1024;
 }
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
index 440b154..ae8f38e 100644
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
+++ b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
@@ -12,42 +12,6 @@
 
 #include "xfa/fxfa/fm2js/cxfa_fmlexer.h"
 
-#define RUNTIMEFUNCTIONRETURNVALUE L"pfm_ret"
-#define EXCLAMATION_IN_IDENTIFIER L"pfm__excl__"
-
-enum XFA_FM_SimpleExpressionType {
-  ASSIGN,
-  LOGICALOR,
-  LOGICALAND,
-  EQUALITY,
-  NOTEQUALITY,
-  LESS,
-  LESSEQUAL,
-  GREATER,
-  GREATEREQUAL,
-  PLUS,
-  MINUS,
-  MULTIPLE,
-  DIVIDE,
-  POSITIVE,
-  NEGATIVE,
-  NOT,
-  CALL,
-  DOT,
-  DOTDOT,
-  CONCATFMOBJECT,
-  ISFMOBJECT,
-  ISFMARRAY,
-  GETFMVALUE,
-  GETFMJSOBJ,
-  VARFILTER
-};
-
-class CFX_WideTextBuf;
-
-WideStringView XFA_FM_EXPTypeToString(
-    XFA_FM_SimpleExpressionType simpleExpType);
-
 enum XFA_FM_AccessorIndex {
   ACCESSOR_NO_INDEX,
   ACCESSOR_NO_RELATIVEINDEX,
@@ -55,185 +19,229 @@
   ACCESSOR_NEGATIVE_INDEX
 };
 
+enum class ReturnType { kImplied, kInfered };
+
+class CFX_WideTextBuf;
+
 class CXFA_FMSimpleExpression {
  public:
-  CXFA_FMSimpleExpression(uint32_t line, XFA_FM_TOKEN op);
-  virtual ~CXFA_FMSimpleExpression() {}
-  virtual bool ToJavaScript(CFX_WideTextBuf& javascript);
-  virtual bool ToImpliedReturnJS(CFX_WideTextBuf& javascript);
+  virtual ~CXFA_FMSimpleExpression() = default;
+  virtual bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) = 0;
 
   XFA_FM_TOKEN GetOperatorToken() const;
 
  protected:
-  uint32_t m_line;
+  explicit CXFA_FMSimpleExpression(XFA_FM_TOKEN op);
+
   const XFA_FM_TOKEN m_op;
 };
 
-class CXFA_FMNullExpression : public CXFA_FMSimpleExpression {
+class CXFA_FMNullExpression final : public CXFA_FMSimpleExpression {
  public:
-  explicit CXFA_FMNullExpression(uint32_t line);
+  CXFA_FMNullExpression();
   ~CXFA_FMNullExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 };
 
-class CXFA_FMNumberExpression : public CXFA_FMSimpleExpression {
+class CXFA_FMNumberExpression final : public CXFA_FMSimpleExpression {
  public:
-  CXFA_FMNumberExpression(uint32_t line, WideStringView wsNumber);
+  explicit CXFA_FMNumberExpression(WideStringView wsNumber);
   ~CXFA_FMNumberExpression() override;
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsNumber;
 };
 
-class CXFA_FMStringExpression : public CXFA_FMSimpleExpression {
+class CXFA_FMStringExpression final : public CXFA_FMSimpleExpression {
  public:
-  CXFA_FMStringExpression(uint32_t line, WideStringView wsString);
+  explicit CXFA_FMStringExpression(WideStringView wsString);
   ~CXFA_FMStringExpression() override;
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsString;
 };
 
-class CXFA_FMIdentifierExpression : public CXFA_FMSimpleExpression {
+class CXFA_FMIdentifierExpression final : public CXFA_FMSimpleExpression {
  public:
-  CXFA_FMIdentifierExpression(uint32_t line, WideStringView wsIdentifier);
+  explicit CXFA_FMIdentifierExpression(WideStringView wsIdentifier);
   ~CXFA_FMIdentifierExpression() override;
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsIdentifier;
 };
 
-class CXFA_FMUnaryExpression : public CXFA_FMSimpleExpression {
+class CXFA_FMAssignExpression final : public CXFA_FMSimpleExpression {
  public:
-  CXFA_FMUnaryExpression(uint32_t line,
-                         XFA_FM_TOKEN op,
-                         std::unique_ptr<CXFA_FMSimpleExpression> pExp);
-  ~CXFA_FMUnaryExpression() override;
+  CXFA_FMAssignExpression(XFA_FM_TOKEN op,
+                          std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                          std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMAssignExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
- protected:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp;
-};
-
-class CXFA_FMBinExpression : public CXFA_FMSimpleExpression {
- public:
-  CXFA_FMBinExpression(uint32_t line,
-                       XFA_FM_TOKEN op,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMBinExpression() override;
-
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-
- protected:
+ private:
   std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
   std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
-class CXFA_FMAssignExpression : public CXFA_FMBinExpression {
+class CXFA_FMBinExpression : public CXFA_FMSimpleExpression {
  public:
-  CXFA_FMAssignExpression(uint32_t line,
-                          XFA_FM_TOKEN op,
-                          std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                          std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMAssignExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
-  bool ToImpliedReturnJS(CFX_WideTextBuf& javascript) override;
+  ~CXFA_FMBinExpression() override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
+
+ protected:
+  CXFA_FMBinExpression(const WideString& opName,
+                       XFA_FM_TOKEN op,
+                       std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                       std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+
+ private:
+  WideString m_OpName;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
-class CXFA_FMLogicalOrExpression : public CXFA_FMBinExpression {
+class CXFA_FMLogicalOrExpression final : public CXFA_FMBinExpression {
  public:
-  CXFA_FMLogicalOrExpression(uint32_t line,
-                             XFA_FM_TOKEN op,
+  CXFA_FMLogicalOrExpression(XFA_FM_TOKEN op,
                              std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
                              std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
   ~CXFA_FMLogicalOrExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
 };
 
-class CXFA_FMLogicalAndExpression : public CXFA_FMBinExpression {
+class CXFA_FMLogicalAndExpression final : public CXFA_FMBinExpression {
  public:
-  CXFA_FMLogicalAndExpression(uint32_t line,
-                              XFA_FM_TOKEN op,
+  CXFA_FMLogicalAndExpression(XFA_FM_TOKEN op,
                               std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
                               std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
   ~CXFA_FMLogicalAndExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
 };
 
-class CXFA_FMEqualityExpression : public CXFA_FMBinExpression {
+class CXFA_FMEqualExpression final : public CXFA_FMBinExpression {
  public:
-  CXFA_FMEqualityExpression(uint32_t line,
-                            XFA_FM_TOKEN op,
+  CXFA_FMEqualExpression(XFA_FM_TOKEN op,
+                         std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                         std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMEqualExpression() override {}
+};
+
+class CXFA_FMNotEqualExpression final : public CXFA_FMBinExpression {
+ public:
+  CXFA_FMNotEqualExpression(XFA_FM_TOKEN op,
                             std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
                             std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMEqualityExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  ~CXFA_FMNotEqualExpression() override {}
 };
 
-class CXFA_FMRelationalExpression : public CXFA_FMBinExpression {
+class CXFA_FMGtExpression final : public CXFA_FMBinExpression {
  public:
-  CXFA_FMRelationalExpression(uint32_t line,
-                              XFA_FM_TOKEN op,
-                              std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                              std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMRelationalExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  CXFA_FMGtExpression(XFA_FM_TOKEN op,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMGtExpression() override {}
 };
 
-class CXFA_FMAdditiveExpression : public CXFA_FMBinExpression {
+class CXFA_FMGeExpression final : public CXFA_FMBinExpression {
  public:
-  CXFA_FMAdditiveExpression(uint32_t line,
-                            XFA_FM_TOKEN op,
-                            std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-                            std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMAdditiveExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  CXFA_FMGeExpression(XFA_FM_TOKEN op,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMGeExpression() override {}
 };
 
-class CXFA_FMMultiplicativeExpression : public CXFA_FMBinExpression {
+class CXFA_FMLtExpression final : public CXFA_FMBinExpression {
  public:
-  CXFA_FMMultiplicativeExpression(
-      uint32_t line,
-      XFA_FM_TOKEN op,
-      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
-      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
-  ~CXFA_FMMultiplicativeExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  CXFA_FMLtExpression(XFA_FM_TOKEN op,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMLtExpression() override {}
 };
 
-class CXFA_FMPosExpression : public CXFA_FMUnaryExpression {
+class CXFA_FMLeExpression final : public CXFA_FMBinExpression {
  public:
-  CXFA_FMPosExpression(uint32_t line,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp);
+  CXFA_FMLeExpression(XFA_FM_TOKEN op,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                      std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMLeExpression() override {}
+};
+
+class CXFA_FMPlusExpression final : public CXFA_FMBinExpression {
+ public:
+  CXFA_FMPlusExpression(XFA_FM_TOKEN op,
+                        std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                        std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMPlusExpression() override {}
+};
+
+class CXFA_FMMinusExpression final : public CXFA_FMBinExpression {
+ public:
+  CXFA_FMMinusExpression(XFA_FM_TOKEN op,
+                         std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                         std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMMinusExpression() override {}
+};
+
+class CXFA_FMMulExpression final : public CXFA_FMBinExpression {
+ public:
+  CXFA_FMMulExpression(XFA_FM_TOKEN op,
+                       std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                       std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMMulExpression() override {}
+};
+
+class CXFA_FMDivExpression final : public CXFA_FMBinExpression {
+ public:
+  CXFA_FMDivExpression(XFA_FM_TOKEN op,
+                       std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                       std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+  ~CXFA_FMDivExpression() override {}
+};
+
+class CXFA_FMUnaryExpression : public CXFA_FMSimpleExpression {
+ public:
+  ~CXFA_FMUnaryExpression() override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
+
+ protected:
+  CXFA_FMUnaryExpression(const WideString& opName,
+                         XFA_FM_TOKEN op,
+                         std::unique_ptr<CXFA_FMSimpleExpression> pExp);
+
+ private:
+  WideString m_OpName;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp;
+};
+
+class CXFA_FMPosExpression final : public CXFA_FMUnaryExpression {
+ public:
+  explicit CXFA_FMPosExpression(std::unique_ptr<CXFA_FMSimpleExpression> pExp);
   ~CXFA_FMPosExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
 };
 
-class CXFA_FMNegExpression : public CXFA_FMUnaryExpression {
+class CXFA_FMNegExpression final : public CXFA_FMUnaryExpression {
  public:
-  CXFA_FMNegExpression(uint32_t line,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp);
+  explicit CXFA_FMNegExpression(std::unique_ptr<CXFA_FMSimpleExpression> pExp);
   ~CXFA_FMNegExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
 };
 
-class CXFA_FMNotExpression : public CXFA_FMUnaryExpression {
+class CXFA_FMNotExpression final : public CXFA_FMUnaryExpression {
  public:
-  CXFA_FMNotExpression(uint32_t line,
-                       std::unique_ptr<CXFA_FMSimpleExpression> pExp);
+  explicit CXFA_FMNotExpression(std::unique_ptr<CXFA_FMSimpleExpression> pExp);
   ~CXFA_FMNotExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
 };
 
-class CXFA_FMCallExpression : public CXFA_FMUnaryExpression {
+class CXFA_FMCallExpression final : public CXFA_FMSimpleExpression {
  public:
   CXFA_FMCallExpression(
-      uint32_t line,
       std::unique_ptr<CXFA_FMSimpleExpression> pExp,
       std::vector<std::unique_ptr<CXFA_FMSimpleExpression>>&& pArguments,
       bool bIsSomMethod);
@@ -241,68 +249,77 @@
 
   bool IsBuiltInFunc(CFX_WideTextBuf* funcName);
   uint32_t IsMethodWithObjParam(const WideString& methodName);
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp;
   bool m_bIsSomMethod;
   std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> m_Arguments;
 };
 
-class CXFA_FMDotAccessorExpression : public CXFA_FMBinExpression {
+class CXFA_FMDotAccessorExpression final : public CXFA_FMSimpleExpression {
  public:
   CXFA_FMDotAccessorExpression(
-      uint32_t line,
       std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
       XFA_FM_TOKEN op,
       WideStringView wsIdentifier,
       std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp);
   ~CXFA_FMDotAccessorExpression() override;
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsIdentifier;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
-class CXFA_FMIndexExpression : public CXFA_FMUnaryExpression {
+class CXFA_FMIndexExpression final : public CXFA_FMSimpleExpression {
  public:
-  CXFA_FMIndexExpression(uint32_t line,
-                         XFA_FM_AccessorIndex accessorIndex,
+  CXFA_FMIndexExpression(XFA_FM_AccessorIndex accessorIndex,
                          std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp,
                          bool bIsStarIndex);
-  ~CXFA_FMIndexExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  ~CXFA_FMIndexExpression() override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp;
   XFA_FM_AccessorIndex m_accessorIndex;
   bool m_bIsStarIndex;
 };
 
-class CXFA_FMDotDotAccessorExpression : public CXFA_FMBinExpression {
+class CXFA_FMDotDotAccessorExpression final : public CXFA_FMSimpleExpression {
  public:
   CXFA_FMDotDotAccessorExpression(
-      uint32_t line,
       std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
       XFA_FM_TOKEN op,
       WideStringView wsIdentifier,
       std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp);
   ~CXFA_FMDotDotAccessorExpression() override;
 
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
 
  private:
   WideStringView m_wsIdentifier;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
-class CXFA_FMMethodCallExpression : public CXFA_FMBinExpression {
+class CXFA_FMMethodCallExpression final : public CXFA_FMSimpleExpression {
  public:
   CXFA_FMMethodCallExpression(
-      uint32_t line,
       std::unique_ptr<CXFA_FMSimpleExpression> pAccessorExp1,
       std::unique_ptr<CXFA_FMSimpleExpression> pCallExp);
-  ~CXFA_FMMethodCallExpression() override {}
-  bool ToJavaScript(CFX_WideTextBuf& javascript) override;
+  ~CXFA_FMMethodCallExpression() override;
+
+  bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
+
+ private:
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
-bool CXFA_IsTooBig(const CFX_WideTextBuf& javascript);
+bool CXFA_IsTooBig(const CFX_WideTextBuf* js);
 
 #endif  // XFA_FXFA_FM2JS_CXFA_FMSIMPLEEXPRESSION_H_
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp
index 96ccb71..198087c 100644
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp
@@ -10,23 +10,23 @@
 #include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/fm2js/cxfa_fmlexer.h"
 #include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
 
 TEST(FMCallExpressionTest, more_than_32_arguments) {
   // Use sign as it has 3 object parameters at positions 0, 5, and 6.
-  auto exp = pdfium::MakeUnique<CXFA_FMIdentifierExpression>(0, L"sign");
+  auto exp = pdfium::MakeUnique<CXFA_FMIdentifierExpression>(L"sign");
 
   std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> args;
   for (size_t i = 0; i < 50; i++)
-    args.push_back(pdfium::MakeUnique<CXFA_FMSimpleExpression>(0, TOKnan));
+    args.push_back(pdfium::MakeUnique<CXFA_FMNullExpression>());
 
   CXFA_FMToJavaScriptDepth::Reset();
-  CXFA_FMCallExpression callExp(0, std::move(exp), std::move(args), true);
+  CXFA_FMCallExpression callExp(std::move(exp), std::move(args), true);
+
   CFX_WideTextBuf js;
-  callExp.ToJavaScript(js);
+  callExp.ToJavaScript(&js, ReturnType::kInfered);
 
   // Generate the result javascript string.
   WideString result = L"sign(";
@@ -37,9 +37,9 @@
     result += L"pfm_rt.get_";
     // Object positions for sign() method.
     if (i == 0 || i == 5 || i == 6)
-      result += L"jsobj()";
+      result += L"jsobj(null)";
     else
-      result += L"val()";
+      result += L"val(null)";
   }
   result += L")";
 
@@ -49,21 +49,23 @@
 TEST(FMStringExpressionTest, Empty) {
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(1, WideStringView()).ToJavaScript(accumulator);
+  CXFA_FMStringExpression(L"").ToJavaScript(&accumulator, ReturnType::kInfered);
   EXPECT_EQ(L"", accumulator.AsStringView());
 }
 
 TEST(FMStringExpressionTest, Short) {
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(1, L"a").ToJavaScript(accumulator);
+  CXFA_FMStringExpression(L"a").ToJavaScript(&accumulator,
+                                             ReturnType::kInfered);
   EXPECT_EQ(L"a", accumulator.AsStringView());
 }
 
 TEST(FMStringExpressionTest, Medium) {
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(1, L".abcd.").ToJavaScript(accumulator);
+  CXFA_FMStringExpression(L".abcd.").ToJavaScript(&accumulator,
+                                                  ReturnType::kInfered);
   EXPECT_EQ(L"\"abcd\"", accumulator.AsStringView());
 }
 
@@ -71,14 +73,15 @@
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf accumulator;
   std::vector<WideStringView::UnsignedType> vec(140000, L'A');
-  CXFA_FMStringExpression(1, WideStringView(vec)).ToJavaScript(accumulator);
+  CXFA_FMStringExpression(WideStringView(vec))
+      .ToJavaScript(&accumulator, ReturnType::kInfered);
   EXPECT_EQ(140000u, accumulator.GetLength());
 }
 
 TEST(FMStringExpressionTest, Quoted) {
   CXFA_FMToJavaScriptDepth::Reset();
   CFX_WideTextBuf accumulator;
-  CXFA_FMStringExpression(1, L".Simon says \"\"run\"\".")
-      .ToJavaScript(accumulator);
+  CXFA_FMStringExpression(L".Simon says \"\"run\"\".")
+      .ToJavaScript(&accumulator, ReturnType::kInfered);
   EXPECT_EQ(L"\"Simon says \\\"run\\\"\"", accumulator.AsStringView());
 }
diff --git a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp b/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp
index 6312ba6..023516b 100644
--- a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.cpp
@@ -4,17 +4,7 @@
 
 #include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
 
-namespace {
-
-// Arbitarily picked by looking at how deep a translation got before hitting
-// the getting fuzzer memory limits. Should be larger then |kMaxParseDepth| in
-// cxfa_fmparser.cpp.
-const unsigned int kMaxDepth = 5000;
-
-}  // namespace
-
 unsigned long CXFA_FMToJavaScriptDepth::depth_ = 0;
-unsigned long CXFA_FMToJavaScriptDepth::max_depth_ = kMaxDepth;
 
 void CXFA_FMToJavaScriptDepth::Reset() {
   depth_ = 0;
diff --git a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h b/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h
index 14f87a6..f4cd1be 100644
--- a/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h
+++ b/xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h
@@ -10,13 +10,17 @@
   CXFA_FMToJavaScriptDepth() { depth_++; }
   ~CXFA_FMToJavaScriptDepth() { depth_--; }
 
-  bool IsWithinMaxDepth() const { return depth_ <= max_depth_; }
+  bool IsWithinMaxDepth() const { return depth_ <= kMaxDepth; }
 
   static void Reset();
 
  private:
+  // Arbitarily picked by looking at how deep a translation got before hitting
+  // the getting fuzzer memory limits. Should be larger then |kMaxParseDepth| in
+  // cxfa_fmparser.cpp.
+  const unsigned long kMaxDepth = 5000;
+
   static unsigned long depth_;
-  static unsigned long max_depth_;
 };
 
 #endif  // XFA_FXFA_FM2JS_CXFA_FMTOJAVASCRIPTDEPTH_H_
diff --git a/xfa/fxfa/fxfa.h b/xfa/fxfa/fxfa.h
index d42cd1b..2028cd3 100644
--- a/xfa/fxfa/fxfa.h
+++ b/xfa/fxfa/fxfa.h
@@ -7,30 +7,47 @@
 #ifndef XFA_FXFA_FXFA_H_
 #define XFA_FXFA_FXFA_H_
 
-#include <vector>
+#include <memory>
 
+#include "core/fxcrt/cfx_timer.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
+#include "core/fxge/fx_dib.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
+class CXFA_FFDoc;
 class CXFA_FFPageView;
+class CXFA_FFWidget;
 class CXFA_Submit;
-class CXFA_WidgetAcc;
-class IFWL_AdapterTimerMgr;
 class IFX_SeekableReadStream;
+class IJS_Runtime;
 
-#define XFA_MBICON_Error 0
-#define XFA_MBICON_Warning 1
-#define XFA_MBICON_Question 2
-#define XFA_MBICON_Status 3
-#define XFA_MB_OK 0
-#define XFA_MB_OKCancel 1
-#define XFA_MB_YesNo 2
-#define XFA_MB_YesNoCancel 3
-#define XFA_IDOK 1
-#define XFA_IDCancel 2
-#define XFA_IDNo 3
-#define XFA_IDYes 4
+// Note, values must match fpdf_formfill.h JSPLATFORM_ALERT_BUTTON_* flags.
+enum class AlertButton {
+  kDefault = 0,
+  kOK = 0,
+  kOKCancel = 1,
+  kYesNo = 2,
+  kYesNoCancel = 3,
+};
+
+// Note, values must match fpdf_formfill.h JSPLATFORM_ALERT_ICON_* flags.
+enum class AlertIcon {
+  kDefault = 0,
+  kError = 0,
+  kWarning = 1,
+  kQuestion = 2,
+  kStatus = 3,
+  kAsterisk = 4,
+};
+
+// Note, values must match fpdf_formfill.h JSPLATFORM_ALERT_RETURN_* flags.
+enum class AlertReturn {
+  kOK = 1,
+  kCancel = 2,
+  kNo = 3,
+  kYes = 4,
+};
 
 // Note, values must match fpdf_formfill.h FORMTYPE_* flags.
 enum class FormType {
@@ -40,29 +57,23 @@
   kXFAForeground = 3,
 };
 
-#define XFA_PARSESTATUS_StatusErr -3
-#define XFA_PARSESTATUS_StreamErr -2
-#define XFA_PARSESTATUS_SyntaxErr -1
-#define XFA_PARSESTATUS_Ready 0
-#define XFA_PARSESTATUS_Done 100
-
 #define XFA_PRINTOPT_ShowDialog 0x00000001
 #define XFA_PRINTOPT_CanCancel 0x00000002
 #define XFA_PRINTOPT_ShrinkPage 0x00000004
 #define XFA_PRINTOPT_AsImage 0x00000008
 #define XFA_PRINTOPT_ReverseOrder 0x00000010
 #define XFA_PRINTOPT_PrintAnnot 0x00000020
+
 #define XFA_PAGEVIEWEVENT_PostAdded 1
 #define XFA_PAGEVIEWEVENT_PostRemoved 3
 #define XFA_PAGEVIEWEVENT_StopLayout 4
 
-#define XFA_EVENTERROR_Success 1
-#define XFA_EVENTERROR_Error -1
-#define XFA_EVENTERROR_NotExist 0
-#define XFA_EVENTERROR_Disabled 2
-
-#define XFA_TRAVERSEWAY_Tranvalse 0x0001
-#define XFA_TRAVERSEWAY_Form 0x0002
+enum class XFA_EventError {
+  kError = -1,
+  kNotExist = 0,
+  kSuccess = 1,
+  kDisabled = 2,
+};
 
 enum XFA_WidgetStatus {
   XFA_WidgetStatus_None = 0,
@@ -71,42 +82,17 @@
   XFA_WidgetStatus_ButtonDown = 1 << 1,
   XFA_WidgetStatus_Disabled = 1 << 2,
   XFA_WidgetStatus_Focused = 1 << 3,
-  XFA_WidgetStatus_Highlight = 1 << 4,
-  XFA_WidgetStatus_Printable = 1 << 5,
-  XFA_WidgetStatus_RectCached = 1 << 6,
-  XFA_WidgetStatus_TextEditValueChanged = 1 << 7,
-  XFA_WidgetStatus_Viewable = 1 << 8,
-  XFA_WidgetStatus_Visible = 1 << 9
-};
-
-enum XFA_WIDGETTYPE {
-  XFA_WIDGETTYPE_Barcode,
-  XFA_WIDGETTYPE_PushButton,
-  XFA_WIDGETTYPE_CheckButton,
-  XFA_WIDGETTYPE_RadioButton,
-  XFA_WIDGETTYPE_DatetimeEdit,
-  XFA_WIDGETTYPE_DecimalField,
-  XFA_WIDGETTYPE_NumericField,
-  XFA_WIDGETTYPE_Signature,
-  XFA_WIDGETTYPE_TextEdit,
-  XFA_WIDGETTYPE_DropdownList,
-  XFA_WIDGETTYPE_ListBox,
-  XFA_WIDGETTYPE_ImageField,
-  XFA_WIDGETTYPE_PasswordEdit,
-  XFA_WIDGETTYPE_Arc,
-  XFA_WIDGETTYPE_Rectangle,
-  XFA_WIDGETTYPE_Image,
-  XFA_WIDGETTYPE_Line,
-  XFA_WIDGETTYPE_Text,
-  XFA_WIDGETTYPE_ExcludeGroup,
-  XFA_WIDGETTYPE_Subform,
-  XFA_WIDGETTYPE_Unknown,
+  XFA_WidgetStatus_Printable = 1 << 4,
+  XFA_WidgetStatus_RectCached = 1 << 5,
+  XFA_WidgetStatus_TextEditValueChanged = 1 << 6,
+  XFA_WidgetStatus_Viewable = 1 << 7,
+  XFA_WidgetStatus_Visible = 1 << 8
 };
 
 // Probably should be called IXFA_AppDelegate.
 class IXFA_AppProvider {
  public:
-  virtual ~IXFA_AppProvider() {}
+  virtual ~IXFA_AppProvider() = default;
 
   /**
    * Returns the language of the running host application. Such as zh_CN
@@ -145,9 +131,9 @@
    * user, refer to XFA_ID.
    */
   virtual int32_t MsgBox(const WideString& wsMessage,
-                         const WideString& wsTitle = L"",
-                         uint32_t dwIconType = 0,
-                         uint32_t dwButtonType = 0) = 0;
+                         const WideString& wsTitle,
+                         uint32_t dwIconType,
+                         uint32_t dwButtonType) = 0;
 
   /**
    * Get a response from the user.
@@ -158,9 +144,9 @@
    * @return A string containing the user's response.
    */
   virtual WideString Response(const WideString& wsQuestion,
-                              const WideString& wsTitle = L"",
-                              const WideString& wsDefaultAnswer = L"",
-                              bool bMask = true) = 0;
+                              const WideString& wsTitle,
+                              const WideString& wsDefaultAnswer,
+                              bool bMask) = 0;
 
   /**
    * Download something from somewhere.
@@ -204,31 +190,33 @@
                              const WideString& wsData,
                              const WideString& wsEncode) = 0;
 
-  virtual IFWL_AdapterTimerMgr* GetTimerMgr() = 0;
+  virtual TimerHandlerIface* GetTimerHandler() const = 0;
 };
 
 class IXFA_DocEnvironment {
  public:
-  virtual ~IXFA_DocEnvironment() {}
+  virtual ~IXFA_DocEnvironment() = default;
 
   virtual void SetChangeMark(CXFA_FFDoc* hDoc) = 0;
   virtual void InvalidateRect(CXFA_FFPageView* pPageView,
                               const CFX_RectF& rt) = 0;
+  // Show or hide caret.
   virtual void DisplayCaret(CXFA_FFWidget* hWidget,
                             bool bVisible,
                             const CFX_RectF* pRtAnchor) = 0;
+
   virtual bool GetPopupPos(CXFA_FFWidget* hWidget,
                            float fMinPopup,
                            float fMaxPopup,
                            const CFX_RectF& rtAnchor,
-                           CFX_RectF& rtPopup) = 0;
-  virtual bool PopupMenu(CXFA_FFWidget* hWidget, CFX_PointF ptPopup) = 0;
-  virtual void PageViewEvent(CXFA_FFPageView* pPageView, uint32_t dwFlags) = 0;
-  virtual void WidgetPostAdd(CXFA_FFWidget* hWidget,
-                             CXFA_WidgetAcc* pWidgetAcc) = 0;
-  virtual void WidgetPreRemove(CXFA_FFWidget* hWidget,
-                               CXFA_WidgetAcc* pWidgetAcc) = 0;
+                           CFX_RectF* pPopupRect) = 0;
+  virtual bool PopupMenu(CXFA_FFWidget* hWidget, const CFX_PointF& ptPopup) = 0;
 
+  // Specify dwFlags XFA_PAGEVIEWEVENT_Added, XFA_PAGEVIEWEVENT_Removing
+  virtual void PageViewEvent(CXFA_FFPageView* pPageView, uint32_t dwFlags) = 0;
+
+  virtual void WidgetPostAdd(CXFA_FFWidget* hWidget) = 0;
+  virtual void WidgetPreRemove(CXFA_FFWidget* hWidget) = 0;
   virtual int32_t CountPages(CXFA_FFDoc* hDoc) = 0;
   virtual int32_t GetCurrentPage(CXFA_FFDoc* hDoc) = 0;
   virtual void SetCurrentPage(CXFA_FFDoc* hDoc, int32_t iCurPage) = 0;
@@ -248,22 +236,19 @@
                      int32_t nEndPage,
                      uint32_t dwOptions) = 0;
   virtual FX_ARGB GetHighlightColor(CXFA_FFDoc* hDoc) = 0;
-
-  virtual bool Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) = 0;
-  virtual bool GetGlobalProperty(CXFA_FFDoc* hDoc,
-                                 const ByteStringView& szPropName,
-                                 CFXJSE_Value* pValue) = 0;
-  virtual bool SetGlobalProperty(CXFA_FFDoc* hDoc,
-                                 const ByteStringView& szPropName,
-                                 CFXJSE_Value* pValue) = 0;
+  virtual IJS_Runtime* GetIJSRuntime(CXFA_FFDoc* hDoc) const = 0;
   virtual RetainPtr<IFX_SeekableReadStream> OpenLinkedFile(
       CXFA_FFDoc* hDoc,
       const WideString& wsLink) = 0;
+
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
+  virtual bool Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) = 0;
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
 };
 
 class IXFA_WidgetIterator {
  public:
-  virtual ~IXFA_WidgetIterator() {}
+  virtual ~IXFA_WidgetIterator() = default;
 
   virtual void Reset() = 0;
   virtual CXFA_FFWidget* MoveToFirst() = 0;
diff --git a/xfa/fxfa/fxfa_basic.h b/xfa/fxfa/fxfa_basic.h
index 96b483d..333a61d 100644
--- a/xfa/fxfa/fxfa_basic.h
+++ b/xfa/fxfa/fxfa_basic.h
@@ -7,11 +7,11 @@
 #ifndef XFA_FXFA_FXFA_BASIC_H_
 #define XFA_FXFA_FXFA_BASIC_H_
 
-#include "fxjs/fxjse.h"
+#include <stdint.h>
 
-class CJX_Object;
 class CXFA_Measurement;
 enum class XFA_ObjectType;
+struct XFA_SCRIPTATTRIBUTEINFO;
 
 enum XFA_HashCode : uint32_t {
   XFA_HASHCODE_None = 0,
@@ -46,21 +46,10 @@
 };
 
 enum class XFA_PacketType : uint8_t {
-  User,
-  SourceSet,
-  Pdf,
-  Xdc,
-  Xdp,
-  Xmpmeta,
-  Xfdf,
-  Config,
-  LocaleSet,
-  Stylesheet,
-  Template,
-  Signature,
-  Datasets,
-  Form,
-  ConnectionSet,
+#undef PCKT____
+#define PCKT____(a, b, c, d, e, f) c,
+#include "xfa/fxfa/parser/packets.inc"
+#undef PCKT____
 };
 
 enum XFA_XDPPACKET {
@@ -95,837 +84,27 @@
   XFA_XDPPACKET_FLAGS_SUPPORTMANY = 16,
 };
 
-enum class XFA_AttributeEnum : uint32_t {
-  Asterisk,
-  Slash,
-  Backslash,
-  On,
-  Tb,
-  Up,
-  MetaData,
-  Delegate,
-  PostSubmit,
-  Name,
-  Cross,
-  Next,
-  None,
-  ShortEdge,
-  Checksum_1mod10_1mod11,
-  Height,
-  CrossDiagonal,
-  All,
-  Any,
-  ToRight,
-  MatchTemplate,
-  Dpl,
-  Invisible,
-  Fit,
-  Width,
-  PreSubmit,
-  Ipl,
-  FlateCompress,
-  Med,
-  Odd,
-  Off,
-  Pdf,
-  Row,
-  Top,
-  Xdp,
-  Xfd,
-  Xml,
-  Zip,
-  Zpl,
-  Visible,
-  Exclude,
-  MouseEnter,
-  Pair,
-  Filter,
-  MoveLast,
-  ExportAndImport,
-  Push,
-  Portrait,
-  Default,
-  StoredProc,
-  StayBOF,
-  StayEOF,
-  PostPrint,
-  UsCarrier,
-  Right,
-  PreOpen,
-  Actual,
-  Rest,
-  TopCenter,
-  StandardSymbol,
-  Initialize,
-  JustifyAll,
-  Normal,
-  Landscape,
-  NonInteractive,
-  MouseExit,
-  Minus,
-  DiagonalLeft,
-  SimplexPaginated,
-  Document,
-  Warning,
-  Auto,
-  Below,
-  BottomLeft,
-  BottomCenter,
-  Tcpl,
-  Text,
-  Grouping,
-  SecureSymbol,
-  PreExecute,
-  DocClose,
-  Keyset,
-  Vertical,
-  PreSave,
-  PreSign,
-  Bottom,
-  ToTop,
-  Verify,
-  First,
-  ContentArea,
-  Solid,
-  Pessimistic,
-  DuplexPaginated,
-  Round,
-  Remerge,
-  Ordered,
-  Percent,
-  Even,
-  Exit,
-  ToolTip,
-  OrderedOccurrence,
-  ReadOnly,
-  Currency,
-  Concat,
-  Thai,
-  Embossed,
-  Formdata,
-  Greek,
-  Decimal,
-  Select,
-  LongEdge,
-  Protected,
-  BottomRight,
-  Zero,
-  ForwardOnly,
-  DocReady,
-  Hidden,
-  Include,
-  Dashed,
-  MultiSelect,
-  Inactive,
-  Embed,
-  Static,
-  OnEntry,
-  Cyrillic,
-  NonBlank,
-  TopRight,
-  Hebrew,
-  TopLeft,
-  Center,
-  MoveFirst,
-  Diamond,
-  PageOdd,
-  Checksum_1mod10,
-  Korean,
-  AboveEmbedded,
-  ZipCompress,
-  Numeric,
-  Circle,
-  ToBottom,
-  Inverted,
-  Update,
-  Isoname,
-  Server,
-  Position,
-  MiddleCenter,
-  Optional,
-  UsePrinterSetting,
-  Outline,
-  IndexChange,
-  Change,
-  PageArea,
-  Once,
-  Only,
-  Open,
-  Caption,
-  Raised,
-  Justify,
-  RefAndDescendants,
-  Short,
-  PageFront,
-  Monospace,
-  Middle,
-  PrePrint,
-  Always,
-  Unknown,
-  ToLeft,
-  Above,
-  DashDot,
-  Gregorian,
-  Roman,
-  MouseDown,
-  Symbol,
-  PageEven,
-  Sign,
-  AddNew,
-  Star,
-  Optimistic,
-  Rl_tb,
-  MiddleRight,
-  Maintain,
-  Package,
-  SimplifiedChinese,
-  ToCenter,
-  Back,
-  Unspecified,
-  BatchOptimistic,
-  Bold,
-  Both,
-  Butt,
-  Client,
-  Checksum_2mod10,
-  ImageOnly,
-  Horizontal,
-  Dotted,
-  UserControl,
-  DiagonalRight,
-  ConsumeData,
-  Check,
-  Data,
-  Down,
-  SansSerif,
-  Inline,
-  TraditionalChinese,
-  Warn,
-  RefOnly,
-  InteractiveForms,
-  Word,
-  Unordered,
-  Required,
-  ImportOnly,
-  BelowEmbedded,
-  Japanese,
-  Full,
-  Rl_row,
-  Vietnamese,
-  EastEuropeanRoman,
-  MouseUp,
-  ExportOnly,
-  Clear,
-  Click,
-  Base64,
-  Close,
-  Host,
-  Global,
-  Blank,
-  Table,
-  Import,
-  Custom,
-  MiddleLeft,
-  PostExecute,
-  Radix,
-  PostOpen,
-  Enter,
-  Ignore,
-  Lr_tb,
-  Fantasy,
-  Italic,
-  Author,
-  ToEdge,
-  Choice,
-  Disabled,
-  CrossHatch,
-  DataRef,
-  DashDotDot,
-  Square,
-  Dynamic,
-  Manual,
-  Etched,
-  ValidationState,
-  Cursive,
-  Last,
-  Left,
-  Link,
-  Long,
-  InternationalCarrier,
-  PDF1_3,
-  PDF1_6,
-  Serif,
-  PostSave,
-  Ready,
-  PostSign,
-  Arabic,
-  Error,
-  Urlencoded,
-  Lowered
+enum class XFA_AttributeValue : uint16_t {
+#undef VALUE____
+#define VALUE____(a, b, c) c,
+#include "xfa/fxfa/parser/attribute_values.inc"
+#undef VALUE____
 };
 
-enum class XFA_Attribute : uint8_t {
-  H = 0,
-  W,
-  X,
-  Y,
-  Id,
-  To,
-  LineThrough,
-  HAlign,
-  Typeface,
-  BeforeTarget,
-  Name,
-  Next,
-  DataRowCount,
-  Break,
-  VScrollPolicy,
-  FontHorizontalScale,
-  TextIndent,
-  Context,
-  TrayOut,
-  Cap,
-  Max,
-  Min,
-  Ref,
-  Rid,
-  Url,
-  Use,
-  LeftInset,
-  Widows,
-  Level,
-  BottomInset,
-  OverflowTarget,
-  AllowMacro,
-  PagePosition,
-  ColumnWidths,
-  OverflowLeader,
-  Action,
-  NonRepudiation,
-  Rate,
-  AllowRichText,
-  Role,
-  OverflowTrailer,
-  Operation,
-  Timeout,
-  TopInset,
-  Access,
-  CommandType,
-  Format,
-  DataPrep,
-  WidgetData,
-  Abbr,
-  MarginRight,
-  DataDescription,
-  EncipherOnly,
-  KerningMode,
-  Rotate,
-  WordCharacterCount,
-  Type,
-  Reserve,
-  TextLocation,
-  Priority,
-  Underline,
-  ModuleWidth,
-  Hyphenate,
-  Listen,
-  Delimiter,
-  ContentType,
-  StartNew,
-  EofAction,
-  AllowNeutral,
-  Connection,
-  BaselineShift,
-  OverlinePeriod,
-  FracDigits,
-  Orientation,
-  TimeStamp,
-  PrintCheckDigit,
-  MarginLeft,
-  Stroke,
-  ModuleHeight,
-  TransferEncoding,
-  Usage,
-  Presence,
-  RadixOffset,
-  Preserve,
-  AliasNode,
-  MultiLine,
-  Version,
-  StartChar,
-  ScriptTest,
-  StartAngle,
-  CursorType,
-  DigitalSignature,
-  CodeType,
-  Output,
-  BookendTrailer,
-  ImagingBBox,
-  ExcludeInitialCap,
-  Force,
-  CrlSign,
-  Previous,
-  PushCharacterCount,
-  NullTest,
-  RunAt,
-  SpaceBelow,
-  SweepAngle,
-  NumberOfCells,
-  LetterSpacing,
-  LockType,
-  PasswordChar,
-  VAlign,
-  SourceBelow,
-  Inverted,
-  Mark,
-  MaxH,
-  MaxW,
-  Truncate,
-  MinH,
-  MinW,
-  Initial,
-  Mode,
-  Layout,
-  Server,
-  EmbedPDF,
-  OddOrEven,
-  TabDefault,
-  Contains,
-  RightInset,
-  MaxChars,
-  Open,
-  Relation,
-  WideNarrowRatio,
-  Relevant,
-  SignatureType,
-  LineThroughPeriod,
-  Shape,
-  TabStops,
-  OutputBelow,
-  Short,
-  FontVerticalScale,
-  Thickness,
-  CommitOn,
-  RemainCharacterCount,
-  KeyAgreement,
-  ErrorCorrectionLevel,
-  UpsMode,
-  MergeMode,
-  Circular,
-  PsName,
-  Trailer,
-  UnicodeRange,
-  ExecuteType,
-  DuplexImposition,
-  TrayIn,
-  BindingNode,
-  BofAction,
-  Save,
-  TargetType,
-  KeyEncipherment,
-  CredentialServerPolicy,
-  Size,
-  InitialNumber,
-  Slope,
-  CSpace,
-  ColSpan,
-  Binding,
-  Checksum,
-  CharEncoding,
-  Bind,
-  TextEntry,
-  Archive,
-  Uuid,
-  Posture,
-  After,
-  Orphans,
-  QualifiedName,
-  Usehref,
-  Locale,
-  Weight,
-  UnderlinePeriod,
-  Data,
-  Desc,
-  Numbered,
-  DataColumnCount,
-  Overline,
-  UrlPolicy,
-  AnchorType,
-  LabelRef,
-  BookendLeader,
-  MaxLength,
-  AccessKey,
-  CursorLocation,
-  DelayedOpen,
-  Target,
-  DataEncipherment,
-  AfterTarget,
-  Leader,
-  Picker,
-  From,
-  BaseProfile,
-  Aspect,
-  RowColumnRatio,
-  LineHeight,
-  Highlight,
-  ValueRef,
-  MaxEntries,
-  DataLength,
-  Activity,
-  Input,
-  Value,
-  BlankOrNotBlank,
-  AddRevocationInfo,
-  GenericFamily,
-  Hand,
-  Href,
-  TextEncoding,
-  LeadDigits,
-  Permissions,
-  SpaceAbove,
-  CodeBase,
-  Stock,
-  IsNull,
-  RestoreState,
-  ExcludeAllCaps,
-  FormatTest,
-  HScrollPolicy,
-  Join,
-  KeyCertSign,
-  Radius,
-  SourceAbove,
-  Override,
-  ClassId,
-  Disable,
-  Scope,
-  Match,
-  Placement,
-  Before,
-  WritingScript,
-  EndChar,
-  Lock,
-  Long,
-  Intact,
-  XdpContent,
-  DecipherOnly,
-  Unknown = 255,
-};
-
-enum class XFA_Element : int32_t {
+enum class XFA_Attribute : int16_t {
   Unknown = -1,
+#undef ATTR____
+#define ATTR____(a, b, c, d) c,
+#include "xfa/fxfa/parser/attributes.inc"
+#undef ATTR____
+};
 
-  Ps,
-  To,
-  Ui,
-  RecordSet,
-  SubsetBelow,
-  SubformSet,
-  AdobeExtensionLevel,
-  Typeface,
-  Break,
-  FontInfo,
-  NumberPattern,
-  DynamicRender,
-  PrintScaling,
-  CheckButton,
-  DatePatterns,
-  SourceSet,
-  Amd,
-  Arc,
-  Day,
-  Era,
-  Jog,
-  Log,
-  Map,
-  Mdp,
-  BreakBefore,
-  Oid,
-  Pcl,
-  Pdf,
-  Ref,
-  Uri,
-  Xdc,
-  Xdp,
-  Xfa,
-  Xsl,
-  Zpl,
-  Cache,
-  Margin,
-  KeyUsage,
-  Exclude,
-  ChoiceList,
-  Level,
-  LabelPrinter,
-  CalendarSymbols,
-  Para,
-  Part,
-  Pdfa,
-  Filter,
-  Present,
-  Pagination,
-  Encoding,
-  Event,
-  Whitespace,
-  DefaultUi,
-  DataModel,
-  Barcode,
-  TimePattern,
-  BatchOutput,
-  Enforce,
-  CurrencySymbols,
-  AddSilentPrint,
-  Rename,
-  Operation,
-  Typefaces,
-  SubjectDNs,
-  Issuers,
-  SignaturePseudoModel,
-  WsdlConnection,
-  Debug,
-  Delta,
-  EraNames,
-  ModifyAnnots,
-  StartNode,
-  Button,
-  Format,
-  Border,
-  Area,
-  Hyphenation,
-  Text,
-  Time,
-  Type,
-  Overprint,
-  Certificates,
-  EncryptionMethods,
-  SetProperty,
-  PrinterName,
-  StartPage,
-  PageOffset,
-  DateTime,
-  Comb,
-  Pattern,
-  IfEmpty,
-  SuppressBanner,
-  OutputBin,
-  Field,
-  Agent,
-  OutputXSL,
-  AdjustData,
-  AutoSave,
-  ContentArea,
-  EventPseudoModel,
-  WsdlAddress,
-  Solid,
-  DateTimeSymbols,
-  EncryptionLevel,
-  Edge,
-  Stipple,
-  Attributes,
-  VersionControl,
-  Meridiem,
-  ExclGroup,
-  ToolTip,
-  Compress,
-  Reason,
-  Execute,
-  ContentCopy,
-  DateTimeEdit,
-  Config,
-  Image,
-  SharpxHTML,
-  NumberOfCopies,
-  BehaviorOverride,
-  TimeStamp,
-  Month,
-  ViewerPreferences,
-  ScriptModel,
-  Decimal,
-  Subform,
-  Select,
-  Window,
-  LocaleSet,
-  Handler,
-  HostPseudoModel,
-  Presence,
-  Record,
-  Embed,
-  Version,
-  Command,
-  Copies,
-  Staple,
-  SubmitFormat,
-  Boolean,
-  Message,
-  Output,
-  PsMap,
-  ExcludeNS,
-  Assist,
-  Picture,
-  Traversal,
-  SilentPrint,
-  WebClient,
-  LayoutPseudoModel,
-  Producer,
-  Corner,
-  MsgId,
-  Color,
-  Keep,
-  Query,
-  Insert,
-  ImageEdit,
-  Validate,
-  DigestMethods,
-  NumberPatterns,
-  PageSet,
-  Integer,
-  SoapAddress,
-  Equate,
-  FormFieldFilling,
-  PageRange,
-  Update,
-  ConnectString,
-  Mode,
-  Layout,
-  Sharpxml,
-  XsdConnection,
-  Traverse,
-  Encodings,
-  Template,
-  Acrobat,
-  ValidationMessaging,
-  Signing,
-  DataWindow,
-  Script,
-  AddViewerPreferences,
-  AlwaysEmbed,
-  PasswordEdit,
-  NumericEdit,
-  EncryptionMethod,
-  Change,
-  PageArea,
-  SubmitUrl,
-  Oids,
-  Signature,
-  ADBE_JSConsole,
-  Caption,
-  Relevant,
-  FlipLabel,
-  ExData,
-  DayNames,
-  SoapAction,
-  DefaultTypeface,
-  Manifest,
-  Overflow,
-  Linear,
-  CurrencySymbol,
-  Delete,
-  Deltas,
-  DigestMethod,
-  InstanceManager,
-  EquateRange,
-  Medium,
-  TextEdit,
-  TemplateCache,
-  CompressObjectStream,
-  DataValue,
-  AccessibleContent,
-  TreeList,
-  IncludeXDPContent,
-  XmlConnection,
-  ValidateApprovalSignatures,
-  SignData,
-  Packets,
-  DatePattern,
-  DuplexOption,
-  Base,
-  Bind,
-  Compression,
-  User,
-  Rectangle,
-  EffectiveOutputPolicy,
-  ADBE_JSDebugger,
-  Acrobat7,
-  Interactive,
-  Locale,
-  CurrentPage,
-  Data,
-  Date,
-  Desc,
-  Encrypt,
-  Draw,
-  Encryption,
-  MeridiemNames,
-  Messaging,
-  Speak,
-  DataGroup,
-  Common,
-  Sharptext,
-  PaginationOverride,
-  Reasons,
-  SignatureProperties,
-  Threshold,
-  AppearanceFilter,
-  Fill,
-  Font,
-  Form,
-  MediumInfo,
-  Certificate,
-  Password,
-  RunScripts,
-  Trace,
-  Float,
-  RenderPolicy,
-  LogPseudoModel,
-  Destination,
-  Value,
-  Bookend,
-  ExObject,
-  OpenAction,
-  NeverEmbed,
-  BindItems,
-  Calculate,
-  Print,
-  Extras,
-  Proto,
-  DSigData,
-  Creator,
-  Connect,
-  Permissions,
-  ConnectionSet,
-  Submit,
-  Range,
-  Linearized,
-  Packet,
-  RootElement,
-  PlaintextMetadata,
-  NumberSymbols,
-  PrintHighQuality,
-  Driver,
-  IncrementalLoad,
-  SubjectDN,
-  CompressLogicalStructure,
-  IncrementalMerge,
-  Radial,
-  Variables,
-  TimePatterns,
-  EffectiveInputPolicy,
-  NameAttr,
-  Conformance,
-  Transform,
-  LockDocument,
-  BreakAfter,
-  Line,
-  List,
-  Source,
-  Occur,
-  PickTrayByPDFSize,
-  MonthNames,
-  Severity,
-  GroupParent,
-  DocumentAssembly,
-  NumberSymbol,
-  Tagged,
-  Items
+enum class XFA_Element : int16_t {
+  Unknown = -1,
+#undef ELEM____
+#define ELEM____(a, b, c, d) c,
+#include "xfa/fxfa/parser/elements.inc"
+#undef ELEM____
 };
 
 enum class XFA_AttributeType : uint8_t {
@@ -936,21 +115,9 @@
   Measure,
 };
 
-struct XFA_SCRIPTHIERARCHY {
-  uint16_t wAttributeStart;
-  uint16_t wAttributeCount;
-  int16_t wParentIndex;
-};
-
 #define XFA_PROPERTYFLAG_OneOf 0x01
 #define XFA_PROPERTYFLAG_DefaultOneOf 0x02
 
-struct XFA_AttributeEnumInfo {
-  uint32_t uHash;
-  const wchar_t* pName;
-  XFA_AttributeEnum eName;
-};
-
 enum class XFA_Unit : uint8_t {
   Percent = 0,
   Em,
@@ -964,20 +131,9 @@
   Unknown = 255,
 };
 
-typedef void (CJX_Object::*XFA_ATTRIBUTE_CALLBACK)(CFXJSE_Value* pValue,
-                                                   bool bSetting,
-                                                   XFA_Attribute eAttribute);
 enum class XFA_ScriptType : uint8_t {
   Basic,
   Object,
 };
 
-struct XFA_SCRIPTATTRIBUTEINFO {
-  uint32_t uHash;
-  const wchar_t* pName;
-  XFA_ATTRIBUTE_CALLBACK callback;
-  XFA_Attribute attribute;
-  XFA_ScriptType eValueType;
-};
-
 #endif  // XFA_FXFA_FXFA_BASIC_H_
diff --git a/xfa/fxfa/fxfa_basic_unittest.cpp b/xfa/fxfa/fxfa_basic_unittest.cpp
new file mode 100644
index 0000000..5f77708
--- /dev/null
+++ b/xfa/fxfa/fxfa_basic_unittest.cpp
@@ -0,0 +1,54 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/fxfa_basic.h"
+
+#include "core/fxcrt/bytestring.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void HashTestCase(uint32_t hash, const char* str, uint32_t* so_far) {
+  if (hash != 0xffffffffu) {
+    EXPECT_EQ(hash, FX_HashCode_GetAsIfW(str, false)) << str;
+    EXPECT_LT(*so_far, hash) << hash;
+  } else {
+    EXPECT_NE(hash, FX_HashCode_GetAsIfW(str, false)) << str;
+  }
+  *so_far = hash;
+}
+
+}  // namespace
+
+TEST(FXFABasic, PacketHashTest) {
+  uint32_t so_far = 0;
+#undef PCKT____
+#define PCKT____(a, b, c, d, e, f) HashTestCase(a, b, &so_far);
+#include "xfa/fxfa/parser/packets.inc"
+#undef PCKT____
+}
+
+TEST(FXFABasic, AttributeHashTest) {
+  uint32_t so_far = 0;
+#undef ATTR____
+#define ATTR____(a, b, c, d) HashTestCase(a, b, &so_far);
+#include "xfa/fxfa/parser/attributes.inc"
+#undef ATTR____
+}
+
+TEST(FXFABasic, ValueHashTest) {
+  uint32_t so_far = 0;
+#undef VALUE____
+#define VALUE____(a, b, c) HashTestCase(a, b, &so_far);
+#include "xfa/fxfa/parser/attribute_values.inc"
+#undef VALUE____
+}
+
+TEST(FXFABasic, ElementHashTest) {
+  uint32_t so_far = 0;
+#undef ELEM____
+#define ELEM____(a, b, c, d) HashTestCase(a, b, &so_far);
+#include "xfa/fxfa/parser/elements.inc"
+#undef ELEM____
+}
diff --git a/xfa/fxfa/layout/BUILD.gn b/xfa/fxfa/layout/BUILD.gn
new file mode 100644
index 0000000..6c9295f
--- /dev/null
+++ b/xfa/fxfa/layout/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2019 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("layout") {
+  sources = [
+    "cxfa_contentlayoutitem.cpp",
+    "cxfa_contentlayoutitem.h",
+    "cxfa_contentlayoutprocessor.cpp",
+    "cxfa_contentlayoutprocessor.h",
+    "cxfa_layoutitem.cpp",
+    "cxfa_layoutitem.h",
+    "cxfa_layoutprocessor.cpp",
+    "cxfa_layoutprocessor.h",
+    "cxfa_traversestrategy_layoutitem.h",
+    "cxfa_viewlayoutitem.cpp",
+    "cxfa_viewlayoutitem.h",
+    "cxfa_viewlayoutprocessor.cpp",
+    "cxfa_viewlayoutprocessor.h",
+  ]
+  deps = [
+    "../../../core/fxcrt",
+    "../../../fxjs",
+    "../parser",
+  ]
+  allow_circular_includes_from = [ "../../../fxjs" ]
+  configs += [
+    "../../../:pdfium_core_config",
+    "../../:xfa_warnings",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "cxfa_layoutitem_embeddertest.cpp" ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutitem.cpp b/xfa/fxfa/layout/cxfa_contentlayoutitem.cpp
new file mode 100644
index 0000000..250cd57
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_contentlayoutitem.cpp
@@ -0,0 +1,109 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
+
+#include <utility>
+
+#include "fxjs/xfa/cjx_object.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/parser/cxfa_margin.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+
+CXFA_ContentLayoutItem::CXFA_ContentLayoutItem(
+    CXFA_Node* pNode,
+    std::unique_ptr<CXFA_FFWidget> pWidget)
+    : CXFA_LayoutItem(pNode, kContentItem), m_pFFWidget(std::move(pWidget)) {
+  if (m_pFFWidget)
+    m_pFFWidget->SetLayoutItem(this);
+}
+
+CXFA_ContentLayoutItem::~CXFA_ContentLayoutItem() {
+  if (m_pFFWidget)
+    m_pFFWidget->SetLayoutItem(nullptr);
+
+  RemoveSelf();
+}
+
+CXFA_ContentLayoutItem* CXFA_ContentLayoutItem::GetFirst() {
+  CXFA_ContentLayoutItem* pCurNode = this;
+  while (auto* pPrev = pCurNode->GetPrev())
+    pCurNode = pPrev;
+
+  return pCurNode;
+}
+
+CXFA_ContentLayoutItem* CXFA_ContentLayoutItem::GetLast() {
+  CXFA_ContentLayoutItem* pCurNode = this;
+  while (auto* pNext = pCurNode->GetNext())
+    pCurNode = pNext;
+
+  return pCurNode;
+}
+
+void CXFA_ContentLayoutItem::InsertAfter(CXFA_ContentLayoutItem* pItem) {
+  CHECK_NE(this, pItem);
+  pItem->RemoveSelf();
+  pItem->m_pNext = m_pNext;
+  pItem->m_pPrev = this;
+  m_pNext = pItem;
+  if (pItem->m_pNext)
+    pItem->m_pNext->m_pPrev = pItem;
+}
+
+void CXFA_ContentLayoutItem::RemoveSelf() {
+  if (m_pNext)
+    m_pNext->m_pPrev = m_pPrev;
+  if (m_pPrev)
+    m_pPrev->m_pNext = m_pNext;
+}
+
+CFX_RectF CXFA_ContentLayoutItem::GetRect(bool bRelative) const {
+  CFX_PointF sPos = m_sPos;
+  CFX_SizeF sSize = m_sSize;
+  if (bRelative)
+    return CFX_RectF(sPos, sSize);
+
+  for (CXFA_LayoutItem* pLayoutItem = GetParent(); pLayoutItem;
+       pLayoutItem = pLayoutItem->GetParent()) {
+    if (CXFA_ContentLayoutItem* pContent = pLayoutItem->AsContentLayoutItem()) {
+      sPos += pContent->m_sPos;
+      CXFA_Margin* pMarginNode =
+          pContent->GetFormNode()->GetFirstChildByClass<CXFA_Margin>(
+              XFA_Element::Margin);
+      if (pMarginNode) {
+        sPos += CFX_PointF(pMarginNode->JSObject()->GetMeasureInUnit(
+                               XFA_Attribute::LeftInset, XFA_Unit::Pt),
+                           pMarginNode->JSObject()->GetMeasureInUnit(
+                               XFA_Attribute::TopInset, XFA_Unit::Pt));
+      }
+      continue;
+    }
+
+    if (pLayoutItem->GetFormNode()->GetElementType() ==
+        XFA_Element::ContentArea) {
+      sPos +=
+          CFX_PointF(pLayoutItem->GetFormNode()->JSObject()->GetMeasureInUnit(
+                         XFA_Attribute::X, XFA_Unit::Pt),
+                     pLayoutItem->GetFormNode()->JSObject()->GetMeasureInUnit(
+                         XFA_Attribute::Y, XFA_Unit::Pt));
+      break;
+    }
+    if (pLayoutItem->GetFormNode()->GetElementType() == XFA_Element::PageArea)
+      break;
+  }
+  return CFX_RectF(sPos, sSize);
+}
+
+size_t CXFA_ContentLayoutItem::GetIndex() const {
+  size_t szIndex = 0;
+  const CXFA_ContentLayoutItem* pCurNode = this;
+  while (auto* pPrev = pCurNode->GetPrev()) {
+    pCurNode = pPrev;
+    ++szIndex;
+  }
+  return szIndex;
+}
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutitem.h b/xfa/fxfa/layout/cxfa_contentlayoutitem.h
new file mode 100644
index 0000000..063f9a3
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_contentlayoutitem.h
@@ -0,0 +1,61 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTITEM_H_
+#define XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTITEM_H_
+
+#include <memory>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/layout/cxfa_layoutitem.h"
+
+class CXFA_FFWidget;
+
+class CXFA_ContentLayoutItem : public CXFA_LayoutItem {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  ~CXFA_ContentLayoutItem() override;
+
+  CXFA_FFWidget* GetFFWidget() { return m_pFFWidget.get(); }
+
+  CXFA_ContentLayoutItem* GetFirst();
+  CXFA_ContentLayoutItem* GetLast();
+  CXFA_ContentLayoutItem* GetPrev() const { return m_pPrev.Get(); }
+  CXFA_ContentLayoutItem* GetNext() const { return m_pNext.Get(); }
+  void InsertAfter(CXFA_ContentLayoutItem* pNext);
+
+  CFX_RectF GetRect(bool bRelative) const;
+  size_t GetIndex() const;
+
+  void SetStatusBits(uint32_t val) { m_dwStatus |= val; }
+  void ClearStatusBits(uint32_t val) { m_dwStatus &= ~val; }
+
+  // TRUE if all (not any) bits set in |val| are set in |m_dwStatus|.
+  bool TestStatusBits(uint32_t val) const { return (m_dwStatus & val) == val; }
+
+  CFX_PointF m_sPos;
+  CFX_SizeF m_sSize;
+
+ private:
+  CXFA_ContentLayoutItem(CXFA_Node* pNode,
+                         std::unique_ptr<CXFA_FFWidget> pFFWidget);
+
+  void RemoveSelf();
+
+  mutable uint32_t m_dwStatus = 0;
+  UnownedPtr<CXFA_ContentLayoutItem> m_pPrev;
+  UnownedPtr<CXFA_ContentLayoutItem> m_pNext;
+  std::unique_ptr<CXFA_FFWidget> const m_pFFWidget;
+};
+
+inline CXFA_FFWidget* GetFFWidget(CXFA_ContentLayoutItem* item) {
+  return item ? item->GetFFWidget() : nullptr;
+}
+
+#endif  // XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTITEM_H_
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
new file mode 100644
index 0000000..833ab99
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
@@ -0,0 +1,2812 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/compiler_specific.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_keep.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_margin.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
+#include "xfa/fxfa/parser/cxfa_occur.h"
+#include "xfa/fxfa/parser/cxfa_para.h"
+#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+namespace {
+
+std::vector<WideString> SeparateStringOnSpace(
+    pdfium::span<const wchar_t> spStr) {
+  std::vector<WideString> ret;
+  if (spStr.empty())
+    return ret;
+
+  size_t nPos = 0;
+  size_t nToken = 0;
+  while (nPos < spStr.size()) {
+    if (spStr[nPos] == L' ') {
+      ret.emplace_back(WideStringView(spStr.subspan(nToken, nPos - nToken)));
+      nToken = nPos + 1;
+    }
+    nPos++;
+  }
+  ret.emplace_back(WideStringView(spStr.subspan(nToken, nPos - nToken)));
+  return ret;
+}
+
+void UpdateWidgetSize(CXFA_ContentLayoutItem* pLayoutItem,
+                      float* pWidth,
+                      float* pHeight) {
+  CXFA_Node* pNode = pLayoutItem->GetFormNode();
+  switch (pNode->GetElementType()) {
+    case XFA_Element::Subform:
+    case XFA_Element::Area:
+    case XFA_Element::ExclGroup:
+    case XFA_Element::SubformSet: {
+      if (*pWidth < -kXFALayoutPrecision)
+        *pWidth = pLayoutItem->m_sSize.width;
+      if (*pHeight < -kXFALayoutPrecision)
+        *pHeight = pLayoutItem->m_sSize.height;
+      break;
+    }
+    case XFA_Element::Draw:
+    case XFA_Element::Field: {
+      pNode->GetDocument()->GetNotify()->StartFieldDrawLayout(pNode, pWidth,
+                                                              pHeight);
+      break;
+    }
+    default:
+      NOTREACHED();
+  }
+}
+
+CFX_SizeF CalculateContainerSpecifiedSize(CXFA_Node* pFormNode,
+                                          bool* bContainerWidthAutoSize,
+                                          bool* bContainerHeightAutoSize) {
+  *bContainerWidthAutoSize = true;
+  *bContainerHeightAutoSize = true;
+
+  XFA_Element eType = pFormNode->GetElementType();
+
+  CFX_SizeF containerSize;
+  if (eType == XFA_Element::Subform || eType == XFA_Element::ExclGroup) {
+    Optional<CXFA_Measurement> wValue =
+        pFormNode->JSObject()->TryMeasure(XFA_Attribute::W, false);
+    if (wValue && wValue->GetValue() > kXFALayoutPrecision) {
+      containerSize.width = wValue->ToUnit(XFA_Unit::Pt);
+      *bContainerWidthAutoSize = false;
+    }
+
+    Optional<CXFA_Measurement> hValue =
+        pFormNode->JSObject()->TryMeasure(XFA_Attribute::H, false);
+    if (hValue && hValue->GetValue() > kXFALayoutPrecision) {
+      containerSize.height = hValue->ToUnit(XFA_Unit::Pt);
+      *bContainerHeightAutoSize = false;
+    }
+  }
+
+  if (*bContainerWidthAutoSize && eType == XFA_Element::Subform) {
+    Optional<CXFA_Measurement> maxW =
+        pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxW, false);
+    if (maxW && maxW->GetValue() > kXFALayoutPrecision) {
+      containerSize.width = maxW->ToUnit(XFA_Unit::Pt);
+      *bContainerWidthAutoSize = false;
+    }
+
+    Optional<CXFA_Measurement> maxH =
+        pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxH, false);
+    if (maxH && maxH->GetValue() > kXFALayoutPrecision) {
+      containerSize.height = maxH->ToUnit(XFA_Unit::Pt);
+      *bContainerHeightAutoSize = false;
+    }
+  }
+  return containerSize;
+}
+
+CFX_SizeF CalculateContainerComponentSizeFromContentSize(
+    CXFA_Node* pFormNode,
+    bool bContainerWidthAutoSize,
+    float fContentCalculatedWidth,
+    bool bContainerHeightAutoSize,
+    float fContentCalculatedHeight,
+    const CFX_SizeF& currentContainerSize) {
+  CFX_SizeF componentSize = currentContainerSize;
+  CXFA_Margin* pMarginNode =
+      pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
+  if (bContainerWidthAutoSize) {
+    componentSize.width = fContentCalculatedWidth;
+    if (pMarginNode) {
+      Optional<CXFA_Measurement> leftInset =
+          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::LeftInset, false);
+      if (leftInset)
+        componentSize.width += leftInset->ToUnit(XFA_Unit::Pt);
+
+      Optional<CXFA_Measurement> rightInset =
+          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::RightInset, false);
+      if (rightInset)
+        componentSize.width += rightInset->ToUnit(XFA_Unit::Pt);
+    }
+  }
+
+  if (bContainerHeightAutoSize) {
+    componentSize.height = fContentCalculatedHeight;
+    if (pMarginNode) {
+      Optional<CXFA_Measurement> topInset =
+          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::TopInset, false);
+      if (topInset)
+        componentSize.height += topInset->ToUnit(XFA_Unit::Pt);
+
+      Optional<CXFA_Measurement> bottomInset =
+          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::BottomInset,
+                                              false);
+      if (bottomInset)
+        componentSize.height += bottomInset->ToUnit(XFA_Unit::Pt);
+    }
+  }
+  return componentSize;
+}
+
+CFX_FloatRect GetMarginInset(const CXFA_Margin* pMargin) {
+  CFX_FloatRect inset;
+  if (!pMargin)
+    return inset;
+
+  inset.left = pMargin->JSObject()->GetMeasureInUnit(XFA_Attribute::LeftInset,
+                                                     XFA_Unit::Pt);
+  inset.top = pMargin->JSObject()->GetMeasureInUnit(XFA_Attribute::TopInset,
+                                                    XFA_Unit::Pt);
+  inset.right = pMargin->JSObject()->GetMeasureInUnit(XFA_Attribute::RightInset,
+                                                      XFA_Unit::Pt);
+  inset.bottom = pMargin->JSObject()->GetMeasureInUnit(
+      XFA_Attribute::BottomInset, XFA_Unit::Pt);
+  return inset;
+}
+
+void RelocateTableRowCells(const RetainPtr<CXFA_ContentLayoutItem>& pLayoutRow,
+                           const std::vector<float>& rgSpecifiedColumnWidths,
+                           XFA_AttributeValue eLayout) {
+  bool bContainerWidthAutoSize = true;
+  bool bContainerHeightAutoSize = true;
+  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
+      pLayoutRow->GetFormNode(), &bContainerWidthAutoSize,
+      &bContainerHeightAutoSize);
+  CXFA_Margin* pMargin =
+      pLayoutRow->GetFormNode()->GetFirstChildByClass<CXFA_Margin>(
+          XFA_Element::Margin);
+  CFX_FloatRect inset = GetMarginInset(pMargin);
+  float fContentWidthLimit =
+      bContainerWidthAutoSize ? FLT_MAX
+                              : containerSize.width - inset.left - inset.right;
+  float fContentCurrentHeight =
+      pLayoutRow->m_sSize.height - inset.top - inset.bottom;
+  float fContentCalculatedWidth = 0;
+  float fContentCalculatedHeight = 0;
+  float fCurrentColX = 0;
+  int32_t nCurrentColIdx = 0;
+  bool bMetWholeRowCell = false;
+
+  for (CXFA_LayoutItem* pIter = pLayoutRow->GetFirstChild(); pIter;
+       pIter = pIter->GetNextSibling()) {
+    CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem();
+    if (!pLayoutChild)
+      continue;
+
+    int32_t nOriginalColSpan =
+        pLayoutChild->GetFormNode()->JSObject()->GetInteger(
+            XFA_Attribute::ColSpan);
+    if (nOriginalColSpan <= 0 && nOriginalColSpan != -1)
+      continue;
+
+    int32_t nColSpan = nOriginalColSpan;
+    float fColSpanWidth = 0;
+    if (nColSpan == -1 ||
+        nCurrentColIdx + nColSpan >
+            pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths)) {
+      nColSpan = pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths) -
+                 nCurrentColIdx;
+    }
+    for (int32_t i = 0; i < nColSpan; i++)
+      fColSpanWidth += rgSpecifiedColumnWidths[nCurrentColIdx + i];
+
+    if (nColSpan != nOriginalColSpan) {
+      fColSpanWidth = bMetWholeRowCell ? 0
+                                       : std::max(fColSpanWidth,
+                                                  pLayoutChild->m_sSize.height);
+    }
+    if (nOriginalColSpan == -1)
+      bMetWholeRowCell = true;
+
+    pLayoutChild->m_sPos = CFX_PointF(fCurrentColX, 0);
+    pLayoutChild->m_sSize.width = fColSpanWidth;
+    if (!pLayoutChild->GetFormNode()->PresenceRequiresSpace())
+      continue;
+
+    fCurrentColX += fColSpanWidth;
+    nCurrentColIdx += nColSpan;
+    float fNewHeight = bContainerHeightAutoSize ? -1 : fContentCurrentHeight;
+    UpdateWidgetSize(pLayoutChild, &fColSpanWidth, &fNewHeight);
+    pLayoutChild->m_sSize.height = fNewHeight;
+    if (bContainerHeightAutoSize) {
+      fContentCalculatedHeight =
+          std::max(fContentCalculatedHeight, pLayoutChild->m_sSize.height);
+    }
+  }
+
+  if (bContainerHeightAutoSize) {
+    for (CXFA_LayoutItem* pIter = pLayoutRow->GetFirstChild(); pIter;
+         pIter = pIter->GetNextSibling()) {
+      CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem();
+      if (!pLayoutChild)
+        continue;
+
+      UpdateWidgetSize(pLayoutChild, &pLayoutChild->m_sSize.width,
+                       &fContentCalculatedHeight);
+      float fOldChildHeight = pLayoutChild->m_sSize.height;
+      pLayoutChild->m_sSize.height = fContentCalculatedHeight;
+      CXFA_Para* pParaNode =
+          pLayoutChild->GetFormNode()->GetFirstChildByClass<CXFA_Para>(
+              XFA_Element::Para);
+      if (pParaNode && pLayoutChild->GetFirstChild()) {
+        float fOffHeight = fContentCalculatedHeight - fOldChildHeight;
+        XFA_AttributeValue eVType =
+            pParaNode->JSObject()->GetEnum(XFA_Attribute::VAlign);
+        switch (eVType) {
+          case XFA_AttributeValue::Middle:
+            fOffHeight = fOffHeight / 2;
+            break;
+          case XFA_AttributeValue::Bottom:
+            break;
+          case XFA_AttributeValue::Top:
+          default:
+            fOffHeight = 0;
+            break;
+        }
+        if (fOffHeight > 0) {
+          for (CXFA_LayoutItem* pInnerIter = pLayoutChild->GetFirstChild();
+               pInnerIter; pInnerIter = pInnerIter->GetNextSibling()) {
+            CXFA_ContentLayoutItem* pInnerChild =
+                pInnerIter->AsContentLayoutItem();
+            if (!pInnerChild)
+              continue;
+
+            pInnerChild->m_sPos.y += fOffHeight;
+          }
+        }
+      }
+    }
+  }
+
+  if (bContainerWidthAutoSize) {
+    float fChildSuppliedWidth = fCurrentColX;
+    if (fContentWidthLimit < FLT_MAX &&
+        fContentWidthLimit > fChildSuppliedWidth) {
+      fChildSuppliedWidth = fContentWidthLimit;
+    }
+    fContentCalculatedWidth =
+        std::max(fContentCalculatedWidth, fChildSuppliedWidth);
+  } else {
+    fContentCalculatedWidth = containerSize.width - inset.left - inset.right;
+  }
+
+  if (pLayoutRow->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout) ==
+      XFA_AttributeValue::Rl_row) {
+    for (CXFA_LayoutItem* pIter = pLayoutRow->GetFirstChild(); pIter;
+         pIter = pIter->GetNextSibling()) {
+      CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem();
+      if (!pLayoutChild)
+        continue;
+
+      pLayoutChild->m_sPos.x = fContentCalculatedWidth -
+                               pLayoutChild->m_sPos.x -
+                               pLayoutChild->m_sSize.width;
+    }
+  }
+  pLayoutRow->m_sSize = CalculateContainerComponentSizeFromContentSize(
+      pLayoutRow->GetFormNode(), bContainerWidthAutoSize,
+      fContentCalculatedWidth, bContainerHeightAutoSize,
+      fContentCalculatedHeight, containerSize);
+}
+
+XFA_AttributeValue GetLayout(CXFA_Node* pFormNode, bool* bRootForceTb) {
+  *bRootForceTb = false;
+  Optional<XFA_AttributeValue> layoutMode =
+      pFormNode->JSObject()->TryEnum(XFA_Attribute::Layout, false);
+  if (layoutMode)
+    return *layoutMode;
+
+  CXFA_Node* pParentNode = pFormNode->GetParent();
+  if (pParentNode && pParentNode->GetElementType() == XFA_Element::Form) {
+    *bRootForceTb = true;
+    return XFA_AttributeValue::Tb;
+  }
+  return XFA_AttributeValue::Position;
+}
+
+bool ExistContainerKeep(CXFA_Node* pCurNode, bool bPreFind) {
+  if (!pCurNode || !pCurNode->PresenceRequiresSpace())
+    return false;
+
+  CXFA_Node* pPreContainer = bPreFind ? pCurNode->GetPrevContainerSibling()
+                                      : pCurNode->GetNextContainerSibling();
+  if (!pPreContainer)
+    return false;
+
+  CXFA_Keep* pKeep =
+      pCurNode->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
+  if (pKeep) {
+    XFA_Attribute eKeepType = XFA_Attribute::Previous;
+    if (!bPreFind)
+      eKeepType = XFA_Attribute::Next;
+
+    Optional<XFA_AttributeValue> previous =
+        pKeep->JSObject()->TryEnum(eKeepType, false);
+    if (previous) {
+      if (*previous == XFA_AttributeValue::ContentArea ||
+          *previous == XFA_AttributeValue::PageArea) {
+        return true;
+      }
+    }
+  }
+
+  pKeep = pPreContainer->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
+  if (!pKeep)
+    return false;
+
+  XFA_Attribute eKeepType = XFA_Attribute::Next;
+  if (!bPreFind)
+    eKeepType = XFA_Attribute::Previous;
+
+  Optional<XFA_AttributeValue> next =
+      pKeep->JSObject()->TryEnum(eKeepType, false);
+  if (!next)
+    return false;
+  if (*next == XFA_AttributeValue::ContentArea ||
+      *next == XFA_AttributeValue::PageArea) {
+    return true;
+  }
+  return false;
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage> FindBreakNode(
+    CXFA_Node* pContainerNode,
+    bool bBreakBefore,
+    CXFA_Node** pCurActionNode) {
+  for (CXFA_Node* pBreakNode = pContainerNode; pBreakNode;
+       pBreakNode = pBreakNode->GetNextSibling()) {
+    XFA_Attribute eAttributeType =
+        bBreakBefore ? XFA_Attribute::Before : XFA_Attribute::After;
+
+    switch (pBreakNode->GetElementType()) {
+      case XFA_Element::BreakBefore: {
+        if (!bBreakBefore)
+          break;
+
+        *pCurActionNode = pBreakNode;
+        return CXFA_ContentLayoutProcessor::Stage::kBreakBefore;
+      }
+      case XFA_Element::BreakAfter: {
+        if (bBreakBefore)
+          break;
+
+        *pCurActionNode = pBreakNode;
+        return CXFA_ContentLayoutProcessor::Stage::kBreakAfter;
+      }
+      case XFA_Element::Break:
+        if (pBreakNode->JSObject()->GetEnum(eAttributeType) ==
+            XFA_AttributeValue::Auto) {
+          break;
+        }
+
+        *pCurActionNode = pBreakNode;
+        return bBreakBefore ? CXFA_ContentLayoutProcessor::Stage::kBreakBefore
+                            : CXFA_ContentLayoutProcessor::Stage::kBreakAfter;
+      default:
+        break;
+    }
+  }
+  return {};
+}
+
+void DeleteLayoutGeneratedNode(CXFA_Node* pGenerateNode) {
+  CXFA_FFNotify* pNotify = pGenerateNode->GetDocument()->GetNotify();
+  auto* pDocLayout =
+      CXFA_LayoutProcessor::FromDocument(pGenerateNode->GetDocument());
+  CXFA_NodeIterator sIterator(pGenerateNode);
+  for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
+       pNode = sIterator.MoveToNext()) {
+    RetainPtr<CXFA_ContentLayoutItem> pCurLayoutItem(
+        ToContentLayoutItem(pNode->JSObject()->GetLayoutItem()));
+    while (pCurLayoutItem) {
+      CXFA_ContentLayoutItem* pNextLayoutItem = pCurLayoutItem->GetNext();
+      pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem.Get());
+      pCurLayoutItem.Reset(pNextLayoutItem);
+    }
+  }
+  pGenerateNode->GetParent()->RemoveChildAndNotify(pGenerateNode, true);
+}
+
+uint8_t HAlignEnumToInt(XFA_AttributeValue eHAlign) {
+  switch (eHAlign) {
+    case XFA_AttributeValue::Center:
+      return 1;
+    case XFA_AttributeValue::Right:
+      return 2;
+    case XFA_AttributeValue::Left:
+    default:
+      return 0;
+  }
+}
+
+bool FindLayoutItemSplitPos(CXFA_ContentLayoutItem* pLayoutItem,
+                            float fCurVerticalOffset,
+                            float* fProposedSplitPos,
+                            bool* bAppChange,
+                            bool bCalculateMargin) {
+  CXFA_Node* pFormNode = pLayoutItem->GetFormNode();
+  if (*fProposedSplitPos <= fCurVerticalOffset + kXFALayoutPrecision ||
+      *fProposedSplitPos > fCurVerticalOffset + pLayoutItem->m_sSize.height -
+                               kXFALayoutPrecision) {
+    return false;
+  }
+
+  switch (pFormNode->GetIntact()) {
+    case XFA_AttributeValue::None: {
+      bool bAnyChanged = false;
+      CXFA_Document* pDocument = pFormNode->GetDocument();
+      CXFA_FFNotify* pNotify = pDocument->GetNotify();
+      float fCurTopMargin = 0, fCurBottomMargin = 0;
+      CXFA_Margin* pMarginNode =
+          pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
+      if (pMarginNode && bCalculateMargin) {
+        fCurTopMargin = pMarginNode->JSObject()->GetMeasureInUnit(
+            XFA_Attribute::TopInset, XFA_Unit::Pt);
+        fCurBottomMargin = pMarginNode->JSObject()->GetMeasureInUnit(
+            XFA_Attribute::BottomInset, XFA_Unit::Pt);
+      }
+      bool bChanged = true;
+      while (bChanged) {
+        bChanged = false;
+        {
+          Optional<float> fRelSplitPos = pFormNode->FindSplitPos(
+              pNotify->GetHDOC()->GetDocView(), pLayoutItem->GetIndex(),
+              *fProposedSplitPos - fCurVerticalOffset);
+          if (fRelSplitPos.has_value()) {
+            bAnyChanged = true;
+            bChanged = true;
+            *fProposedSplitPos = fCurVerticalOffset + fRelSplitPos.value();
+            *bAppChange = true;
+            if (*fProposedSplitPos <=
+                fCurVerticalOffset + kXFALayoutPrecision) {
+              return true;
+            }
+          }
+        }
+        float fRelSplitPos = *fProposedSplitPos - fCurBottomMargin;
+        for (CXFA_LayoutItem* pIter = pLayoutItem->GetFirstChild(); pIter;
+             pIter = pIter->GetNextSibling()) {
+          CXFA_ContentLayoutItem* pChildItem = pIter->AsContentLayoutItem();
+          if (!pChildItem)
+            continue;
+
+          float fChildOffset =
+              fCurVerticalOffset + fCurTopMargin + pChildItem->m_sPos.y;
+          bool bChange = false;
+          if (FindLayoutItemSplitPos(pChildItem, fChildOffset, &fRelSplitPos,
+                                     &bChange, bCalculateMargin)) {
+            if (fRelSplitPos - fChildOffset < kXFALayoutPrecision && bChange) {
+              *fProposedSplitPos = fRelSplitPos - fCurTopMargin;
+            } else {
+              *fProposedSplitPos = fRelSplitPos + fCurBottomMargin;
+            }
+            bAnyChanged = true;
+            bChanged = true;
+            if (*fProposedSplitPos <=
+                fCurVerticalOffset + kXFALayoutPrecision) {
+              return true;
+            }
+            if (bAnyChanged)
+              break;
+          }
+        }
+      }
+      return bAnyChanged;
+    }
+    case XFA_AttributeValue::ContentArea:
+    case XFA_AttributeValue::PageArea: {
+      *fProposedSplitPos = fCurVerticalOffset;
+      return true;
+    }
+    default:
+      return false;
+  }
+}
+
+CFX_PointF CalculatePositionedContainerPos(CXFA_Node* pNode,
+                                           const CFX_SizeF& size) {
+  XFA_AttributeValue eAnchorType =
+      pNode->JSObject()->GetEnum(XFA_Attribute::AnchorType);
+  int32_t nAnchorType = 0;
+  switch (eAnchorType) {
+    case XFA_AttributeValue::TopLeft:
+      nAnchorType = 0;
+      break;
+    case XFA_AttributeValue::TopCenter:
+      nAnchorType = 1;
+      break;
+    case XFA_AttributeValue::TopRight:
+      nAnchorType = 2;
+      break;
+    case XFA_AttributeValue::MiddleLeft:
+      nAnchorType = 3;
+      break;
+    case XFA_AttributeValue::MiddleCenter:
+      nAnchorType = 4;
+      break;
+    case XFA_AttributeValue::MiddleRight:
+      nAnchorType = 5;
+      break;
+    case XFA_AttributeValue::BottomLeft:
+      nAnchorType = 6;
+      break;
+    case XFA_AttributeValue::BottomCenter:
+      nAnchorType = 7;
+      break;
+    case XFA_AttributeValue::BottomRight:
+      nAnchorType = 8;
+      break;
+    default:
+      break;
+  }
+  static const uint8_t nNextPos[4][9] = {{0, 1, 2, 3, 4, 5, 6, 7, 8},
+                                         {6, 3, 0, 7, 4, 1, 8, 5, 2},
+                                         {8, 7, 6, 5, 4, 3, 2, 1, 0},
+                                         {2, 5, 8, 1, 4, 7, 0, 3, 6}};
+
+  CFX_PointF pos(
+      pNode->JSObject()->GetMeasureInUnit(XFA_Attribute::X, XFA_Unit::Pt),
+      pNode->JSObject()->GetMeasureInUnit(XFA_Attribute::Y, XFA_Unit::Pt));
+  int32_t nRotate =
+      XFA_MapRotation(pNode->JSObject()->GetInteger(XFA_Attribute::Rotate)) /
+      90;
+  int32_t nAbsoluteAnchorType = nNextPos[nRotate][nAnchorType];
+  switch (nAbsoluteAnchorType / 3) {
+    case 1:
+      pos.y -= size.height / 2;
+      break;
+    case 2:
+      pos.y -= size.height;
+      break;
+    default:
+      break;
+  }
+  switch (nAbsoluteAnchorType % 3) {
+    case 1:
+      pos.x -= size.width / 2;
+      break;
+    case 2:
+      pos.x -= size.width;
+      break;
+    default:
+      break;
+  }
+  return pos;
+}
+
+}  // namespace
+
+CXFA_ContentLayoutProcessor::CXFA_ContentLayoutProcessor(
+    CXFA_Node* pNode,
+    CXFA_ViewLayoutProcessor* pViewLayoutProcessor)
+    : m_pFormNode(pNode), m_pViewLayoutProcessor(pViewLayoutProcessor) {
+  ASSERT(GetFormNode());
+  ASSERT(GetFormNode()->IsContainerNode() ||
+         GetFormNode()->GetElementType() == XFA_Element::Form);
+  m_pOldLayoutItem.Reset(
+      ToContentLayoutItem(GetFormNode()->JSObject()->GetLayoutItem()));
+}
+
+CXFA_ContentLayoutProcessor::~CXFA_ContentLayoutProcessor() {}
+
+RetainPtr<CXFA_ContentLayoutItem>
+CXFA_ContentLayoutProcessor::CreateContentLayoutItem(CXFA_Node* pFormNode) {
+  if (!pFormNode)
+    return nullptr;
+
+  if (m_pOldLayoutItem) {
+    RetainPtr<CXFA_ContentLayoutItem> pLayoutItem = m_pOldLayoutItem;
+    m_pOldLayoutItem.Reset(m_pOldLayoutItem->GetNext());
+    return pLayoutItem;
+  }
+  CXFA_FFNotify* pNotify = pFormNode->GetDocument()->GetNotify();
+  auto pNewLayoutItem = pdfium::MakeRetain<CXFA_ContentLayoutItem>(
+      pFormNode, pNotify->OnCreateContentLayoutItem(pFormNode));
+
+  CXFA_ContentLayoutItem* pPrevLayoutItem =
+      ToContentLayoutItem(pFormNode->JSObject()->GetLayoutItem());
+  if (pPrevLayoutItem) {
+    pPrevLayoutItem->GetLast()->InsertAfter(pNewLayoutItem.Get());
+  } else {
+    pFormNode->JSObject()->SetLayoutItem(pNewLayoutItem.Get());
+  }
+  return pNewLayoutItem;
+}
+
+float CXFA_ContentLayoutProcessor::FindSplitPos(float fProposedSplitPos) {
+  ASSERT(m_pLayoutItem);
+  auto value = GetFormNode()->JSObject()->TryEnum(XFA_Attribute::Layout, true);
+  XFA_AttributeValue eLayout = value.value_or(XFA_AttributeValue::Position);
+  bool bCalculateMargin = eLayout != XFA_AttributeValue::Position;
+  while (fProposedSplitPos > kXFALayoutPrecision) {
+    bool bAppChange = false;
+    if (!FindLayoutItemSplitPos(m_pLayoutItem.Get(), 0, &fProposedSplitPos,
+                                &bAppChange, bCalculateMargin)) {
+      break;
+    }
+  }
+  return fProposedSplitPos;
+}
+
+void CXFA_ContentLayoutProcessor::SplitLayoutItem(
+    CXFA_ContentLayoutItem* pLayoutItem,
+    CXFA_ContentLayoutItem* pSecondParent,
+    float fSplitPos) {
+  float fCurTopMargin = 0;
+  float fCurBottomMargin = 0;
+  auto value = GetFormNode()->JSObject()->TryEnum(XFA_Attribute::Layout, true);
+  XFA_AttributeValue eLayout = value.value_or(XFA_AttributeValue::Position);
+  bool bCalculateMargin = true;
+  if (eLayout == XFA_AttributeValue::Position)
+    bCalculateMargin = false;
+
+  CXFA_Margin* pMarginNode =
+      pLayoutItem->GetFormNode()->GetFirstChildByClass<CXFA_Margin>(
+          XFA_Element::Margin);
+  if (pMarginNode && bCalculateMargin) {
+    fCurTopMargin = pMarginNode->JSObject()->GetMeasureInUnit(
+        XFA_Attribute::TopInset, XFA_Unit::Pt);
+    fCurBottomMargin = pMarginNode->JSObject()->GetMeasureInUnit(
+        XFA_Attribute::BottomInset, XFA_Unit::Pt);
+  }
+
+  RetainPtr<CXFA_ContentLayoutItem> pSecondLayoutItem;
+  if (m_pCurChildPreprocessor &&
+      m_pCurChildPreprocessor->GetFormNode() == pLayoutItem->GetFormNode()) {
+    pSecondLayoutItem = m_pCurChildPreprocessor->CreateContentLayoutItem(
+        pLayoutItem->GetFormNode());
+  } else {
+    pSecondLayoutItem = CreateContentLayoutItem(pLayoutItem->GetFormNode());
+  }
+  pSecondLayoutItem->m_sPos.x = pLayoutItem->m_sPos.x;
+  pSecondLayoutItem->m_sSize.width = pLayoutItem->m_sSize.width;
+  pSecondLayoutItem->m_sPos.y = 0;
+  pSecondLayoutItem->m_sSize.height = pLayoutItem->m_sSize.height - fSplitPos;
+  pLayoutItem->m_sSize.height -= pSecondLayoutItem->m_sSize.height;
+  if (pLayoutItem->GetFirstChild())
+    pSecondLayoutItem->m_sSize.height += fCurTopMargin;
+
+  bool bOrphanedItem = false;
+  if (pSecondParent) {
+    pSecondParent->AppendLastChild(pSecondLayoutItem);
+    if (fCurTopMargin > 0 && pLayoutItem->GetFirstChild()) {
+      pSecondParent->m_sSize.height += fCurTopMargin;
+      for (CXFA_LayoutItem* pParentIter = pSecondParent->GetParent();
+           pParentIter; pParentIter = pParentIter->GetParent()) {
+        CXFA_ContentLayoutItem* pContentItem =
+            pParentIter->AsContentLayoutItem();
+        if (!pContentItem)
+          continue;
+
+        pContentItem->m_sSize.height += fCurTopMargin;
+      }
+    }
+  } else if (pLayoutItem->GetParent()) {
+    pLayoutItem->GetParent()->InsertAfter(pSecondLayoutItem, pLayoutItem);
+  } else {
+    // Parentless |pLayoutitem| would like to have |pSecondLayoutItem| as a
+    // sibling, but that would violate the tree invariant. Instead, keep
+    // it an orphan and add it as a child of |pLayoutItem| after performing
+    // the split.
+    bOrphanedItem = true;
+  }
+
+  std::vector<RetainPtr<CXFA_ContentLayoutItem>> children;
+  while (auto* pFirst = ToContentLayoutItem(pLayoutItem->GetFirstChild())) {
+    children.emplace_back(pFirst);
+    pLayoutItem->RemoveChild(children.back());
+  }
+
+  float lHeightForKeep = 0;
+  float fAddMarginHeight = 0;
+  std::vector<RetainPtr<CXFA_ContentLayoutItem>> keepLayoutItems;
+  for (auto& pChildItem : children) {
+    if (fSplitPos <= fCurTopMargin + pChildItem->m_sPos.y + fCurBottomMargin +
+                         kXFALayoutPrecision) {
+      if (!ExistContainerKeep(pChildItem->GetFormNode(), true)) {
+        pChildItem->m_sPos.y -= fSplitPos - fCurBottomMargin;
+        pChildItem->m_sPos.y += lHeightForKeep;
+        pChildItem->m_sPos.y += fAddMarginHeight;
+        pSecondLayoutItem->AppendLastChild(pChildItem);
+        continue;
+      }
+      if (lHeightForKeep < kXFALayoutPrecision) {
+        for (auto& pPreItem : keepLayoutItems) {
+          pLayoutItem->RemoveChild(pPreItem);
+          pPreItem->m_sPos.y -= fSplitPos;
+          if (pPreItem->m_sPos.y < 0)
+            pPreItem->m_sPos.y = 0;
+          if (pPreItem->m_sPos.y + pPreItem->m_sSize.height > lHeightForKeep) {
+            pPreItem->m_sPos.y = lHeightForKeep;
+            lHeightForKeep += pPreItem->m_sSize.height;
+            pSecondLayoutItem->m_sSize.height += pPreItem->m_sSize.height;
+            if (pSecondParent)
+              pSecondParent->m_sSize.height += pPreItem->m_sSize.height;
+          }
+          pSecondLayoutItem->AppendLastChild(pPreItem);
+        }
+      }
+      pChildItem->m_sPos.y -= fSplitPos;
+      pChildItem->m_sPos.y += lHeightForKeep;
+      pChildItem->m_sPos.y += fAddMarginHeight;
+      pSecondLayoutItem->AppendLastChild(pChildItem);
+      continue;
+    }
+    if (fSplitPos + kXFALayoutPrecision >= fCurTopMargin + fCurBottomMargin +
+                                               pChildItem->m_sPos.y +
+                                               pChildItem->m_sSize.height) {
+      pLayoutItem->AppendLastChild(pChildItem);
+      if (ExistContainerKeep(pChildItem->GetFormNode(), false))
+        keepLayoutItems.push_back(pChildItem);
+      else
+        keepLayoutItems.clear();
+      continue;
+    }
+
+    float fOldHeight = pSecondLayoutItem->m_sSize.height;
+    SplitLayoutItem(
+        pChildItem.Get(), pSecondLayoutItem.Get(),
+        fSplitPos - fCurTopMargin - fCurBottomMargin - pChildItem->m_sPos.y);
+    fAddMarginHeight = pSecondLayoutItem->m_sSize.height - fOldHeight;
+    pLayoutItem->AppendLastChild(pChildItem);
+  }
+  if (bOrphanedItem)
+    pLayoutItem->AppendLastChild(pSecondLayoutItem);
+}
+
+void CXFA_ContentLayoutProcessor::SplitLayoutItem(float fSplitPos) {
+  ASSERT(m_pLayoutItem);
+  SplitLayoutItem(m_pLayoutItem.Get(), nullptr, fSplitPos);
+}
+
+RetainPtr<CXFA_ContentLayoutItem>
+CXFA_ContentLayoutProcessor::ExtractLayoutItem() {
+  RetainPtr<CXFA_ContentLayoutItem> pLayoutItem = m_pLayoutItem;
+  if (pLayoutItem) {
+    m_pLayoutItem.Reset(ToContentLayoutItem(pLayoutItem->GetNextSibling()));
+    pLayoutItem->RemoveSelfIfParented();
+  }
+  if (m_nCurChildNodeStage != Stage::kDone || !m_pOldLayoutItem)
+    return pLayoutItem;
+
+  CXFA_FFNotify* pNotify =
+      m_pOldLayoutItem->GetFormNode()->GetDocument()->GetNotify();
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(
+      m_pOldLayoutItem->GetFormNode()->GetDocument());
+
+  while (m_pOldLayoutItem) {
+    RetainPtr<CXFA_ContentLayoutItem> pToDeleteItem = m_pOldLayoutItem;
+    m_pOldLayoutItem.Reset(pToDeleteItem->GetNext());
+    if (pToDeleteItem == pLayoutItem)
+      break;
+    pNotify->OnLayoutItemRemoving(pDocLayout, pToDeleteItem.Get());
+    pToDeleteItem->RemoveSelfIfParented();
+  }
+  return pLayoutItem;
+}
+
+void CXFA_ContentLayoutProcessor::GotoNextContainerNodeSimple(
+    bool bUsePageBreak) {
+  m_nCurChildNodeStage = GotoNextContainerNode(
+      m_nCurChildNodeStage, bUsePageBreak, GetFormNode(), &m_pCurChildNode);
+}
+
+CXFA_ContentLayoutProcessor::Stage
+CXFA_ContentLayoutProcessor::GotoNextContainerNode(Stage nCurStage,
+                                                   bool bUsePageBreak,
+                                                   CXFA_Node* pParentContainer,
+                                                   CXFA_Node** pCurActionNode) {
+  CXFA_Node* pChildContainer = nullptr;
+  switch (nCurStage) {
+    case Stage::kBreakBefore:
+    case Stage::kBreakAfter: {
+      pChildContainer = (*pCurActionNode)->GetParent();
+      break;
+    }
+    case Stage::kKeep:
+    case Stage::kContainer:
+      pChildContainer = *pCurActionNode;
+      break;
+    default:
+      pChildContainer = nullptr;
+      break;
+  }
+
+  Optional<Stage> ret;
+  switch (nCurStage) {
+    case Stage::kKeep:
+      ret = HandleKeep(pChildContainer->GetFirstChild(), pCurActionNode);
+      if (ret.has_value())
+        return ret.value();
+      break;
+
+    case Stage::kNone:
+      *pCurActionNode = nullptr;
+      FALLTHROUGH;
+
+    case Stage::kBookendLeader:
+      ret = HandleBookendLeader(pParentContainer, pCurActionNode);
+      if (ret.has_value())
+        return ret.value();
+
+      *pCurActionNode = nullptr;
+      FALLTHROUGH;
+
+    case Stage::kBreakBefore:
+      ret = HandleBreakBefore(pChildContainer, pCurActionNode);
+      if (ret.has_value())
+        return ret.value();
+      break;
+
+    case Stage::kContainer:
+      *pCurActionNode = nullptr;
+      FALLTHROUGH;
+
+    case Stage::kBreakAfter:
+      ret = HandleBreakAfter(pChildContainer, pCurActionNode);
+      if (ret.has_value())
+        return ret.value();
+      break;
+
+    case Stage::kBookendTrailer:
+      ret = HandleBookendTrailer(pParentContainer, pCurActionNode);
+      if (ret.has_value())
+        return ret.value();
+      FALLTHROUGH;
+
+    default:
+      *pCurActionNode = nullptr;
+      return Stage::kDone;
+  }
+
+  ret = HandleCheckNextChildContainer(pParentContainer, pChildContainer,
+                                      pCurActionNode);
+  if (ret.has_value())
+    return ret.value();
+
+  *pCurActionNode = nullptr;
+  ret = HandleBookendTrailer(pParentContainer, pCurActionNode);
+  if (ret.has_value())
+    return ret.value();
+
+  *pCurActionNode = nullptr;
+  return Stage::kDone;
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::ProcessKeepNodesForCheckNext(
+    CXFA_Node** pCurActionNode,
+    CXFA_Node** pNextContainer,
+    bool* pLastKeepNode) {
+  const bool bCanSplit =
+      (*pNextContainer)->GetIntact() == XFA_AttributeValue::None;
+  const bool bNextKeep = ExistContainerKeep(*pNextContainer, false);
+
+  if (bNextKeep && !bCanSplit) {
+    if (!m_bIsProcessKeep && !m_bKeepBreakFinish) {
+      m_pKeepHeadNode = *pNextContainer;
+      m_bIsProcessKeep = true;
+    }
+    return {};
+  }
+
+  if (!m_bIsProcessKeep || !m_pKeepHeadNode) {
+    if (m_bKeepBreakFinish)
+      *pLastKeepNode = true;
+    m_bKeepBreakFinish = false;
+    return {};
+  }
+
+  m_pKeepTailNode = *pNextContainer;
+  if (m_bKeepBreakFinish) {
+    *pNextContainer = m_pKeepHeadNode;
+    ProcessKeepNodesEnd();
+    return {};
+  }
+
+  Optional<Stage> ret =
+      FindBreakNode((*pNextContainer)->GetFirstChild(), true, pCurActionNode);
+  if (!ret.has_value()) {
+    *pNextContainer = m_pKeepHeadNode;
+    ProcessKeepNodesEnd();
+    return {};
+  }
+
+  return ret;
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::ProcessKeepNodesForBreakBefore(
+    CXFA_Node** pCurActionNode,
+    CXFA_Node* pContainerNode) {
+  if (m_pKeepTailNode == pContainerNode) {
+    *pCurActionNode = m_pKeepHeadNode;
+    ProcessKeepNodesEnd();
+    return Stage::kContainer;
+  }
+
+  CXFA_Node* pBreakAfterNode = pContainerNode->GetFirstChild();
+  return FindBreakNode(pBreakAfterNode, false, pCurActionNode);
+}
+
+void CXFA_ContentLayoutProcessor::DoLayoutPageArea(
+    CXFA_ViewLayoutItem* pPageAreaLayoutItem) {
+  CXFA_Node* pFormNode = pPageAreaLayoutItem->GetFormNode();
+  CXFA_Node* pCurChildNode = nullptr;
+  CXFA_LayoutItem* pBeforeItem = nullptr;
+  for (Stage nCurChildNodeStage = GotoNextContainerNode(
+           Stage::kNone, false, pFormNode, &pCurChildNode);
+       pCurChildNode;
+       nCurChildNodeStage = GotoNextContainerNode(nCurChildNodeStage, false,
+                                                  pFormNode, &pCurChildNode)) {
+    if (nCurChildNodeStage != Stage::kContainer)
+      continue;
+    if (pCurChildNode->GetElementType() == XFA_Element::Variables)
+      continue;
+
+    auto pProcessor =
+        pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pCurChildNode, nullptr);
+    pProcessor->DoLayout(false, FLT_MAX, FLT_MAX);
+    if (!pProcessor->HasLayoutItem())
+      continue;
+
+    pProcessor->SetCurrentComponentPos(CalculatePositionedContainerPos(
+        pCurChildNode, pProcessor->GetCurrentComponentSize()));
+    RetainPtr<CXFA_LayoutItem> pProcessItem = pProcessor->ExtractLayoutItem();
+    if (!pBeforeItem)
+      pPageAreaLayoutItem->AppendFirstChild(pProcessItem);
+    else
+      pPageAreaLayoutItem->InsertAfter(pProcessItem, pBeforeItem);
+
+    pBeforeItem = pProcessItem.Get();
+  }
+
+  pBeforeItem = nullptr;
+  RetainPtr<CXFA_LayoutItem> pLayoutItem(pPageAreaLayoutItem->GetFirstChild());
+  while (pLayoutItem) {
+    if (!pLayoutItem->IsContentLayoutItem() ||
+        pLayoutItem->GetFormNode()->GetElementType() != XFA_Element::Draw) {
+      pLayoutItem.Reset(pLayoutItem->GetNextSibling());
+      continue;
+    }
+    if (pLayoutItem->GetFormNode()->GetElementType() != XFA_Element::Draw)
+      continue;
+
+    CXFA_LayoutItem* pNextLayoutItem = pLayoutItem->GetNextSibling();
+    pPageAreaLayoutItem->RemoveChild(pLayoutItem);
+    if (!pBeforeItem)
+      pPageAreaLayoutItem->AppendFirstChild(pLayoutItem);
+    else
+      pPageAreaLayoutItem->InsertAfter(pLayoutItem, pBeforeItem);
+
+    pBeforeItem = pLayoutItem.Get();
+    pLayoutItem.Reset(pNextLayoutItem);
+  }
+}
+
+void CXFA_ContentLayoutProcessor::DoLayoutPositionedContainer(
+    Context* pContext) {
+  if (m_pLayoutItem)
+    return;
+
+  m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
+  auto value = GetFormNode()->JSObject()->TryEnum(XFA_Attribute::Layout, true);
+  bool bIgnoreXY = value.value_or(XFA_AttributeValue::Position) !=
+                   XFA_AttributeValue::Position;
+  bool bContainerWidthAutoSize = true;
+  bool bContainerHeightAutoSize = true;
+  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
+      GetFormNode(), &bContainerWidthAutoSize, &bContainerHeightAutoSize);
+
+  float fContentCalculatedWidth = 0;
+  float fContentCalculatedHeight = 0;
+  float fHiddenContentCalculatedWidth = 0;
+  float fHiddenContentCalculatedHeight = 0;
+  if (!m_pCurChildNode)
+    GotoNextContainerNodeSimple(false);
+
+  int32_t iColIndex = 0;
+  for (; m_pCurChildNode; GotoNextContainerNodeSimple(false)) {
+    if (m_nCurChildNodeStage != Stage::kContainer)
+      continue;
+    if (m_pCurChildNode->GetElementType() == XFA_Element::Variables)
+      continue;
+
+    auto pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+        m_pCurChildNode, m_pViewLayoutProcessor.Get());
+    if (pContext && pContext->m_prgSpecifiedColumnWidths) {
+      int32_t iColSpan =
+          m_pCurChildNode->JSObject()->GetInteger(XFA_Attribute::ColSpan);
+      if (iColSpan <= pdfium::CollectionSize<int32_t>(
+                          *pContext->m_prgSpecifiedColumnWidths) -
+                          iColIndex) {
+        pContext->m_fCurColumnWidth = 0.0f;
+        if (iColSpan == -1) {
+          iColSpan = pdfium::CollectionSize<int32_t>(
+              *pContext->m_prgSpecifiedColumnWidths);
+        }
+        for (int32_t i = 0; iColIndex + i < iColSpan; ++i) {
+          pContext->m_fCurColumnWidth.value() +=
+              (*pContext->m_prgSpecifiedColumnWidths)[iColIndex + i];
+        }
+        if (pContext->m_fCurColumnWidth.value() == 0)
+          pContext->m_fCurColumnWidth.reset();
+
+        iColIndex += iColSpan >= 0 ? iColSpan : 0;
+      }
+    }
+
+    pProcessor->DoLayoutInternal(false, FLT_MAX, FLT_MAX, pContext);
+    if (!pProcessor->HasLayoutItem())
+      continue;
+
+    CFX_SizeF size = pProcessor->GetCurrentComponentSize();
+    bool bChangeParentSize = false;
+    if (m_pCurChildNode->PresenceRequiresSpace())
+      bChangeParentSize = true;
+
+    CFX_PointF absolutePos;
+    if (!bIgnoreXY)
+      absolutePos = CalculatePositionedContainerPos(m_pCurChildNode, size);
+
+    pProcessor->SetCurrentComponentPos(absolutePos);
+    if (bContainerWidthAutoSize) {
+      float fChildSuppliedWidth = absolutePos.x + size.width;
+      if (bChangeParentSize) {
+        fContentCalculatedWidth =
+            std::max(fContentCalculatedWidth, fChildSuppliedWidth);
+      } else {
+        if (fHiddenContentCalculatedWidth < fChildSuppliedWidth &&
+            m_pCurChildNode->GetElementType() != XFA_Element::Subform) {
+          fHiddenContentCalculatedWidth = fChildSuppliedWidth;
+        }
+      }
+    }
+
+    if (bContainerHeightAutoSize) {
+      float fChildSuppliedHeight = absolutePos.y + size.height;
+      if (bChangeParentSize) {
+        fContentCalculatedHeight =
+            std::max(fContentCalculatedHeight, fChildSuppliedHeight);
+      } else {
+        if (fHiddenContentCalculatedHeight < fChildSuppliedHeight &&
+            m_pCurChildNode->GetElementType() != XFA_Element::Subform) {
+          fHiddenContentCalculatedHeight = fChildSuppliedHeight;
+        }
+      }
+    }
+    m_pLayoutItem->AppendLastChild(pProcessor->ExtractLayoutItem());
+  }
+
+  XFA_VERSION eVersion = GetFormNode()->GetDocument()->GetCurVersionMode();
+  if (fContentCalculatedWidth == 0 && eVersion < XFA_VERSION_207)
+    fContentCalculatedWidth = fHiddenContentCalculatedWidth;
+  if (fContentCalculatedHeight == 0 && eVersion < XFA_VERSION_207)
+    fContentCalculatedHeight = fHiddenContentCalculatedHeight;
+
+  containerSize = CalculateContainerComponentSizeFromContentSize(
+      GetFormNode(), bContainerWidthAutoSize, fContentCalculatedWidth,
+      bContainerHeightAutoSize, fContentCalculatedHeight, containerSize);
+  SetCurrentComponentSize(containerSize);
+}
+
+void CXFA_ContentLayoutProcessor::DoLayoutTableContainer(
+    CXFA_Node* pLayoutNode) {
+  if (m_pLayoutItem)
+    return;
+  if (!pLayoutNode)
+    pLayoutNode = GetFormNode();
+
+  ASSERT(!m_pCurChildNode);
+
+  m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
+  bool bContainerWidthAutoSize = true;
+  bool bContainerHeightAutoSize = true;
+  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
+      GetFormNode(), &bContainerWidthAutoSize, &bContainerHeightAutoSize);
+  float fContentCalculatedWidth = 0;
+  float fContentCalculatedHeight = 0;
+  CXFA_Margin* pMarginNode =
+      GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
+  float fLeftInset = 0;
+  float fRightInset = 0;
+  if (pMarginNode) {
+    fLeftInset = pMarginNode->JSObject()->GetMeasureInUnit(
+        XFA_Attribute::LeftInset, XFA_Unit::Pt);
+    fRightInset = pMarginNode->JSObject()->GetMeasureInUnit(
+        XFA_Attribute::RightInset, XFA_Unit::Pt);
+  }
+
+  float fContentWidthLimit =
+      bContainerWidthAutoSize ? FLT_MAX
+                              : containerSize.width - fLeftInset - fRightInset;
+  WideString wsColumnWidths =
+      pLayoutNode->JSObject()->GetCData(XFA_Attribute::ColumnWidths);
+  if (!wsColumnWidths.IsEmpty()) {
+    for (auto& width : SeparateStringOnSpace(wsColumnWidths.span())) {
+      width.TrimLeft(L' ');
+      if (width.IsEmpty())
+        continue;
+
+      m_rgSpecifiedColumnWidths.push_back(
+          CXFA_Measurement(width.AsStringView()).ToUnit(XFA_Unit::Pt));
+    }
+  }
+
+  int32_t iSpecifiedColumnCount =
+      pdfium::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths);
+  Context layoutContext;
+  layoutContext.m_prgSpecifiedColumnWidths = &m_rgSpecifiedColumnWidths;
+  Context* pLayoutContext =
+      iSpecifiedColumnCount > 0 ? &layoutContext : nullptr;
+  if (!m_pCurChildNode)
+    GotoNextContainerNodeSimple(false);
+
+  for (; m_pCurChildNode; GotoNextContainerNodeSimple(false)) {
+    layoutContext.m_fCurColumnWidth.reset();
+    if (m_nCurChildNodeStage != Stage::kContainer)
+      continue;
+
+    auto pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+        m_pCurChildNode, m_pViewLayoutProcessor.Get());
+    pProcessor->DoLayoutInternal(false, FLT_MAX, FLT_MAX, pLayoutContext);
+    if (!pProcessor->HasLayoutItem())
+      continue;
+
+    m_pLayoutItem->AppendLastChild(pProcessor->ExtractLayoutItem());
+  }
+
+  int32_t iRowCount = 0;
+  int32_t iColCount = 0;
+  {
+    std::vector<CXFA_ContentLayoutItem*> rgRowItems;
+    std::vector<int32_t> rgRowItemsSpan;
+    std::vector<float> rgRowItemsWidth;
+    for (CXFA_LayoutItem* pIter = m_pLayoutItem->GetFirstChild(); pIter;
+         pIter = pIter->GetNextSibling()) {
+      CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem();
+      if (!pLayoutChild)
+        continue;
+      if (pLayoutChild->GetFormNode()->GetElementType() != XFA_Element::Subform)
+        continue;
+      if (!pLayoutChild->GetFormNode()->PresenceRequiresSpace())
+        continue;
+
+      XFA_AttributeValue eLayout =
+          pLayoutChild->GetFormNode()->JSObject()->GetEnum(
+              XFA_Attribute::Layout);
+      if (eLayout != XFA_AttributeValue::Row &&
+          eLayout != XFA_AttributeValue::Rl_row) {
+        continue;
+      }
+      CXFA_ContentLayoutItem* pRowLayoutCell =
+          ToContentLayoutItem(pLayoutChild->GetFirstChild());
+      if (pRowLayoutCell) {
+        rgRowItems.push_back(pRowLayoutCell);
+        int32_t iColSpan =
+            pRowLayoutCell->GetFormNode()->JSObject()->GetInteger(
+                XFA_Attribute::ColSpan);
+        rgRowItemsSpan.push_back(iColSpan);
+        rgRowItemsWidth.push_back(pRowLayoutCell->m_sSize.width);
+      }
+    }
+
+    iRowCount = pdfium::CollectionSize<int32_t>(rgRowItems);
+    iColCount = 0;
+    bool bMoreColumns = true;
+    while (bMoreColumns) {
+      bMoreColumns = false;
+      bool bAutoCol = false;
+      for (int32_t i = 0; i < iRowCount; i++) {
+        while (rgRowItems[i] &&
+               (rgRowItemsSpan[i] <= 0 ||
+                !rgRowItems[i]->GetFormNode()->PresenceRequiresSpace())) {
+          CXFA_ContentLayoutItem* pNewCell =
+              ToContentLayoutItem(rgRowItems[i]->GetNextSibling());
+          if (rgRowItemsSpan[i] < 0 &&
+              rgRowItems[i]->GetFormNode()->PresenceRequiresSpace()) {
+            pNewCell = nullptr;
+          }
+          rgRowItems[i] = pNewCell;
+          rgRowItemsSpan[i] =
+              pNewCell ? pNewCell->GetFormNode()->JSObject()->GetInteger(
+                             XFA_Attribute::ColSpan)
+                       : 0;
+          rgRowItemsWidth[i] = pNewCell ? pNewCell->m_sSize.width : 0;
+        }
+        CXFA_ContentLayoutItem* pCell = rgRowItems[i];
+        if (!pCell)
+          continue;
+
+        bMoreColumns = true;
+        if (rgRowItemsSpan[i] != 1)
+          continue;
+
+        if (iColCount >= iSpecifiedColumnCount) {
+          int32_t c =
+              iColCount + 1 -
+              pdfium::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths);
+          for (int32_t j = 0; j < c; j++)
+            m_rgSpecifiedColumnWidths.push_back(0);
+        }
+        if (m_rgSpecifiedColumnWidths[iColCount] < kXFALayoutPrecision)
+          bAutoCol = true;
+        if (bAutoCol &&
+            m_rgSpecifiedColumnWidths[iColCount] < rgRowItemsWidth[i]) {
+          m_rgSpecifiedColumnWidths[iColCount] = rgRowItemsWidth[i];
+        }
+      }
+
+      if (!bMoreColumns)
+        continue;
+
+      float fFinalColumnWidth = 0.0f;
+      if (pdfium::IndexInBounds(m_rgSpecifiedColumnWidths, iColCount))
+        fFinalColumnWidth = m_rgSpecifiedColumnWidths[iColCount];
+
+      for (int32_t i = 0; i < iRowCount; ++i) {
+        if (!rgRowItems[i])
+          continue;
+        --rgRowItemsSpan[i];
+        rgRowItemsWidth[i] -= fFinalColumnWidth;
+      }
+      ++iColCount;
+    }
+  }
+
+  float fCurrentRowY = 0;
+  for (CXFA_LayoutItem* pIter = m_pLayoutItem->GetFirstChild(); pIter;
+       pIter = pIter->GetNextSibling()) {
+    RetainPtr<CXFA_ContentLayoutItem> pLayoutChild(
+        pIter->AsContentLayoutItem());
+    if (!pLayoutChild || !pLayoutChild->GetFormNode()->PresenceRequiresSpace())
+      continue;
+
+    if (pLayoutChild->GetFormNode()->GetElementType() == XFA_Element::Subform) {
+      XFA_AttributeValue eSubformLayout =
+          pLayoutChild->GetFormNode()->JSObject()->GetEnum(
+              XFA_Attribute::Layout);
+      if (eSubformLayout == XFA_AttributeValue::Row ||
+          eSubformLayout == XFA_AttributeValue::Rl_row) {
+        RelocateTableRowCells(pLayoutChild, m_rgSpecifiedColumnWidths,
+                              eSubformLayout);
+      }
+    }
+
+    pLayoutChild->m_sPos.y = fCurrentRowY;
+    if (bContainerWidthAutoSize) {
+      pLayoutChild->m_sPos.x = 0;
+    } else {
+      switch (pLayoutChild->GetFormNode()->JSObject()->GetEnum(
+          XFA_Attribute::HAlign)) {
+        case XFA_AttributeValue::Center:
+          pLayoutChild->m_sPos.x =
+              (fContentWidthLimit - pLayoutChild->m_sSize.width) / 2;
+          break;
+        case XFA_AttributeValue::Right:
+          pLayoutChild->m_sPos.x =
+              fContentWidthLimit - pLayoutChild->m_sSize.width;
+          break;
+        case XFA_AttributeValue::Left:
+        default:
+          pLayoutChild->m_sPos.x = 0;
+          break;
+      }
+    }
+
+    if (bContainerWidthAutoSize) {
+      float fChildSuppliedWidth =
+          pLayoutChild->m_sPos.x + pLayoutChild->m_sSize.width;
+      if (fContentWidthLimit < FLT_MAX &&
+          fContentWidthLimit > fChildSuppliedWidth) {
+        fChildSuppliedWidth = fContentWidthLimit;
+      }
+      fContentCalculatedWidth =
+          std::max(fContentCalculatedWidth, fChildSuppliedWidth);
+    }
+    fCurrentRowY += pLayoutChild->m_sSize.height;
+  }
+
+  if (bContainerHeightAutoSize)
+    fContentCalculatedHeight = std::max(fContentCalculatedHeight, fCurrentRowY);
+
+  containerSize = CalculateContainerComponentSizeFromContentSize(
+      GetFormNode(), bContainerWidthAutoSize, fContentCalculatedWidth,
+      bContainerHeightAutoSize, fContentCalculatedHeight, containerSize);
+  SetCurrentComponentSize(containerSize);
+}
+
+bool CXFA_ContentLayoutProcessor::IsAddNewRowForTrailer(
+    CXFA_ContentLayoutItem* pTrailerItem) {
+  if (!pTrailerItem)
+    return false;
+
+  float fWidth = pTrailerItem->m_sSize.width;
+  XFA_AttributeValue eLayout =
+      GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout);
+  return eLayout == XFA_AttributeValue::Tb || m_fWidthLimit <= fWidth;
+}
+
+float CXFA_ContentLayoutProcessor::InsertKeepLayoutItems() {
+  if (m_ArrayKeepItems.empty())
+    return 0;
+
+  if (!m_pLayoutItem) {
+    m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
+    m_pLayoutItem->m_sSize.clear();
+  }
+
+  float fTotalHeight = 0;
+  for (auto iter = m_ArrayKeepItems.rbegin(); iter != m_ArrayKeepItems.rend();
+       iter++) {
+    AddLeaderAfterSplit(*iter);
+    fTotalHeight += (*iter)->m_sSize.height;
+  }
+  m_ArrayKeepItems.clear();
+
+  return fTotalHeight;
+}
+
+bool CXFA_ContentLayoutProcessor::ProcessKeepForSplit(
+    CXFA_ContentLayoutProcessor* pChildProcessor,
+    Result eRetValue,
+    std::vector<RetainPtr<CXFA_ContentLayoutItem>>* rgCurLineLayoutItem,
+    float* fContentCurRowAvailWidth,
+    float* fContentCurRowHeight,
+    float* fContentCurRowY,
+    bool* bAddedItemInRow,
+    bool* bForceEndPage,
+    Result* result) {
+  if (!pChildProcessor)
+    return false;
+
+  if (m_pCurChildNode->GetIntact() == XFA_AttributeValue::None &&
+      pChildProcessor->m_bHasAvailHeight)
+    return false;
+
+  if (!ExistContainerKeep(m_pCurChildNode, true))
+    return false;
+
+  CFX_SizeF childSize = pChildProcessor->GetCurrentComponentSize();
+  std::vector<RetainPtr<CXFA_ContentLayoutItem>> keepLayoutItems;
+  if (JudgePutNextPage(m_pLayoutItem.Get(), childSize.height,
+                       &keepLayoutItems)) {
+    m_ArrayKeepItems.clear();
+    for (auto& item : keepLayoutItems) {
+      m_pLayoutItem->RemoveChild(item);
+      *fContentCurRowY -= item->m_sSize.height;
+      m_ArrayKeepItems.push_back(item);
+    }
+    *bAddedItemInRow = true;
+    *bForceEndPage = true;
+    *result = Result::kPageFullBreak;
+    return true;
+  }
+
+  rgCurLineLayoutItem->push_back(pChildProcessor->ExtractLayoutItem());
+  *bAddedItemInRow = true;
+  *fContentCurRowAvailWidth -= childSize.width;
+  *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height);
+  *result = eRetValue;
+  return true;
+}
+
+bool CXFA_ContentLayoutProcessor::JudgePutNextPage(
+    CXFA_ContentLayoutItem* pParentLayoutItem,
+    float fChildHeight,
+    std::vector<RetainPtr<CXFA_ContentLayoutItem>>* pKeepItems) {
+  if (!pParentLayoutItem)
+    return false;
+
+  float fItemsHeight = 0;
+  for (CXFA_LayoutItem* pIter = pParentLayoutItem->GetFirstChild(); pIter;
+       pIter = pIter->GetNextSibling()) {
+    RetainPtr<CXFA_ContentLayoutItem> pChildLayoutItem(
+        pIter->AsContentLayoutItem());
+    if (!pChildLayoutItem)
+      continue;
+
+    if (ExistContainerKeep(pChildLayoutItem->GetFormNode(), false)) {
+      pKeepItems->push_back(pChildLayoutItem);
+      fItemsHeight += pChildLayoutItem->m_sSize.height;
+    } else {
+      pKeepItems->clear();
+      fItemsHeight = 0;
+    }
+  }
+  fItemsHeight += fChildHeight;
+  return m_pViewLayoutProcessor->GetNextAvailContentHeight(fItemsHeight);
+}
+
+void CXFA_ContentLayoutProcessor::ProcessUnUseBinds(CXFA_Node* pFormNode) {
+  if (!pFormNode)
+    return;
+
+  CXFA_NodeIterator sIterator(pFormNode);
+  for (CXFA_Node* pNode = sIterator.MoveToNext(); pNode;
+       pNode = sIterator.MoveToNext()) {
+    if (pNode->IsContainerNode()) {
+      CXFA_Node* pBindNode = pNode->GetBindData();
+      if (pBindNode) {
+        pBindNode->RemoveBindItem(pNode);
+        pNode->SetBindingNode(nullptr);
+      }
+    }
+    pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+  }
+}
+
+void CXFA_ContentLayoutProcessor::ProcessUnUseOverFlow(
+    CXFA_Node* pLeaderNode,
+    CXFA_Node* pTrailerNode,
+    const RetainPtr<CXFA_ContentLayoutItem>& pTrailerItem,
+    CXFA_Node* pFormNode) {
+  ProcessUnUseBinds(pLeaderNode);
+  ProcessUnUseBinds(pTrailerNode);
+  if (!pFormNode)
+    return;
+
+  if (pFormNode->GetElementType() == XFA_Element::Overflow ||
+      pFormNode->GetElementType() == XFA_Element::Break) {
+    pFormNode = pFormNode->GetParent();
+  }
+  if (pLeaderNode && pFormNode)
+    pFormNode->RemoveChildAndNotify(pLeaderNode, true);
+  if (pTrailerNode && pFormNode)
+    pFormNode->RemoveChildAndNotify(pTrailerNode, true);
+  if (pTrailerItem)
+    XFA_ReleaseLayoutItem(pTrailerItem);
+}
+
+CXFA_ContentLayoutProcessor::Result
+CXFA_ContentLayoutProcessor::DoLayoutFlowedContainer(
+    bool bUseBreakControl,
+    XFA_AttributeValue eFlowStrategy,
+    float fHeightLimit,
+    float fRealHeight,
+    Context* pContext,
+    bool bRootForceTb) {
+  m_bHasAvailHeight = true;
+  if (m_pCurChildPreprocessor)
+    m_pCurChildPreprocessor->m_ePreProcessRs = Result::kDone;
+
+  bool bContainerWidthAutoSize = true;
+  bool bContainerHeightAutoSize = true;
+  CFX_SizeF container_size = CalculateContainerSpecifiedSize(
+      GetFormNode(), &bContainerWidthAutoSize, &bContainerHeightAutoSize);
+  AdjustContainerSpecifiedSize(pContext, &container_size,
+                               &bContainerWidthAutoSize,
+                               &bContainerHeightAutoSize);
+
+  CXFA_Margin* pMargin =
+      GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
+  CFX_FloatRect inset = GetMarginInset(pMargin);
+  float fContentWidthLimit =
+      bContainerWidthAutoSize ? FLT_MAX
+                              : container_size.width - inset.left - inset.right;
+  float fAvailHeight = fHeightLimit - inset.top - inset.bottom;
+  if (fAvailHeight < 0)
+    m_bHasAvailHeight = false;
+
+  fRealHeight = fRealHeight - inset.top - inset.bottom;
+  CFX_SizeF calculated_size;
+  float fContentCurRowY = 0;
+  CXFA_ContentLayoutItem* pLastChild = nullptr;
+  if (m_pLayoutItem) {
+    pLastChild = FindLastContentLayoutItem(eFlowStrategy);
+    calculated_size = CalculateLayoutItemSize(pLastChild);
+    fContentCurRowY =
+        pLastChild ? pLastChild->m_sPos.y : calculated_size.height;
+  }
+
+  fContentCurRowY += InsertKeepLayoutItems();
+  if (m_nCurChildNodeStage == Stage::kNone)
+    GotoNextContainerNodeSimple(true);
+
+  fContentCurRowY += InsertPendingItems(GetFormNode());
+  if (m_pCurChildPreprocessor && m_nCurChildNodeStage == Stage::kContainer) {
+    if (ExistContainerKeep(m_pCurChildPreprocessor->GetFormNode(), false)) {
+      m_pKeepHeadNode = m_pCurChildNode;
+      m_bIsProcessKeep = true;
+      m_nCurChildNodeStage = Stage::kKeep;
+    }
+  }
+
+  bool bForceEndPage = false;
+  bool bBreakDone = false;
+  bool bIsManualBreak = false;
+  while (m_nCurChildNodeStage != Stage::kDone) {
+    float fContentCurRowHeight = 0;
+    float fContentCurRowAvailWidth = fContentWidthLimit;
+    m_fWidthLimit = fContentCurRowAvailWidth;
+    std::vector<RetainPtr<CXFA_ContentLayoutItem>> rgCurLineLayoutItems[3];
+    uint8_t uCurHAlignState =
+        (eFlowStrategy != XFA_AttributeValue::Rl_tb ? 0 : 2);
+    if (pLastChild) {
+      for (CXFA_LayoutItem* pNext = pLastChild; pNext;
+           pNext = pNext->GetNextSibling()) {
+        CXFA_ContentLayoutItem* pLayoutNext = pNext->AsContentLayoutItem();
+        if (!pLayoutNext)
+          continue;
+        if (!pLayoutNext->GetNextSibling() && m_pCurChildPreprocessor &&
+            m_pCurChildPreprocessor->GetFormNode() ==
+                pLayoutNext->GetFormNode()) {
+          if (m_pCurChildPreprocessor->m_pLayoutItem &&
+              m_pCurChildPreprocessor->m_pLayoutItem != pLayoutNext) {
+            pLayoutNext->InsertAfter(
+                m_pCurChildPreprocessor->m_pLayoutItem.Get());
+          }
+          m_pCurChildPreprocessor->m_pLayoutItem.Reset(pLayoutNext);
+          break;
+        }
+        uint8_t uHAlign =
+            HAlignEnumToInt(pLayoutNext->GetFormNode()->JSObject()->GetEnum(
+                XFA_Attribute::HAlign));
+        rgCurLineLayoutItems[uHAlign].emplace_back(pLayoutNext);
+        if (eFlowStrategy == XFA_AttributeValue::Lr_tb) {
+          if (uHAlign > uCurHAlignState)
+            uCurHAlignState = uHAlign;
+        } else if (uHAlign < uCurHAlignState) {
+          uCurHAlignState = uHAlign;
+        }
+        if (pLayoutNext->GetFormNode()->PresenceRequiresSpace()) {
+          if (pLayoutNext->m_sSize.height > fContentCurRowHeight)
+            fContentCurRowHeight = pLayoutNext->m_sSize.height;
+          fContentCurRowAvailWidth -= pLayoutNext->m_sSize.width;
+        }
+      }
+
+      RetainPtr<CXFA_ContentLayoutItem> pLayoutNextTemp(pLastChild);
+      while (pLayoutNextTemp) {
+        CXFA_ContentLayoutItem* pSaveLayoutNext =
+            ToContentLayoutItem(pLayoutNextTemp->GetNextSibling());
+        pLayoutNextTemp->RemoveSelfIfParented();
+        pLayoutNextTemp.Reset(pSaveLayoutNext);
+      }
+      pLastChild = nullptr;
+    }
+
+    while (m_pCurChildNode) {
+      std::unique_ptr<CXFA_ContentLayoutProcessor> pProcessor;
+      bool bAddedItemInRow = false;
+      fContentCurRowY += InsertPendingItems(GetFormNode());
+      switch (m_nCurChildNodeStage) {
+        case Stage::kKeep:
+        case Stage::kNone:
+          break;
+        case Stage::kBreakBefore: {
+          for (auto& item : m_ArrayKeepItems) {
+            m_pLayoutItem->RemoveChild(item);
+            calculated_size.height -= item->m_sSize.height;
+          }
+
+          if (!bUseBreakControl || !m_pViewLayoutProcessor)
+            break;
+
+          Optional<CXFA_ViewLayoutProcessor::BreakData> break_data =
+              m_pViewLayoutProcessor->ProcessBreakBefore(m_pCurChildNode);
+          if (!break_data.has_value() || !break_data.value().bCreatePage ||
+              GetFormNode()->GetElementType() == XFA_Element::Form) {
+            break;
+          }
+
+          CXFA_Node* pLeaderNode = break_data.value().pLeader;
+          CXFA_Node* pTrailerNode = break_data.value().pTrailer;
+          if (JudgeLeaderOrTrailerForOccur(pLeaderNode))
+            AddPendingNode(pLeaderNode, true);
+
+          if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) {
+            if (GetFormNode()->GetParent()->GetElementType() ==
+                    XFA_Element::Form &&
+                !m_pLayoutItem) {
+              AddPendingNode(pTrailerNode, true);
+            } else {
+              auto pTempProcessor =
+                  pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pTrailerNode,
+                                                                  nullptr);
+              InsertFlowedItem(
+                  pTempProcessor.get(), bContainerWidthAutoSize,
+                  bContainerHeightAutoSize, container_size.height,
+                  eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, false,
+                  FLT_MAX, FLT_MAX, fContentWidthLimit, &fContentCurRowY,
+                  &fContentCurRowAvailWidth, &fContentCurRowHeight,
+                  &bAddedItemInRow, &bForceEndPage, pContext, false);
+            }
+          }
+          GotoNextContainerNodeSimple(true);
+          bForceEndPage = true;
+          bIsManualBreak = true;
+          goto SuspendAndCreateNewRow;
+        }
+        case Stage::kBreakAfter: {
+          if (!bUseBreakControl || !m_pViewLayoutProcessor)
+            break;
+
+          Optional<CXFA_ViewLayoutProcessor::BreakData> break_data =
+              m_pViewLayoutProcessor->ProcessBreakAfter(m_pCurChildNode);
+          if (!break_data.has_value() ||
+              GetFormNode()->GetElementType() == XFA_Element::Form) {
+            break;
+          }
+
+          CXFA_Node* pLeaderNode = break_data.value().pLeader;
+          CXFA_Node* pTrailerNode = break_data.value().pTrailer;
+          bool bCreatePage = break_data.value().bCreatePage;
+          if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) {
+            auto pTempProcessor =
+                pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pTrailerNode,
+                                                                nullptr);
+            InsertFlowedItem(pTempProcessor.get(), bContainerWidthAutoSize,
+                             bContainerHeightAutoSize, container_size.height,
+                             eFlowStrategy, &uCurHAlignState,
+                             rgCurLineLayoutItems, false, FLT_MAX, FLT_MAX,
+                             fContentWidthLimit, &fContentCurRowY,
+                             &fContentCurRowAvailWidth, &fContentCurRowHeight,
+                             &bAddedItemInRow, &bForceEndPage, pContext, false);
+          }
+          if (!bCreatePage) {
+            if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) {
+              CalculateRowChildPosition(
+                  rgCurLineLayoutItems, eFlowStrategy, bContainerHeightAutoSize,
+                  bContainerWidthAutoSize, &calculated_size.width,
+                  &calculated_size.height, &fContentCurRowY,
+                  fContentCurRowHeight, fContentWidthLimit, false);
+              rgCurLineLayoutItems->clear();
+              auto pTempProcessor =
+                  pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(pLeaderNode,
+                                                                  nullptr);
+              InsertFlowedItem(
+                  pTempProcessor.get(), bContainerWidthAutoSize,
+                  bContainerHeightAutoSize, container_size.height,
+                  eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, false,
+                  FLT_MAX, FLT_MAX, fContentWidthLimit, &fContentCurRowY,
+                  &fContentCurRowAvailWidth, &fContentCurRowHeight,
+                  &bAddedItemInRow, &bForceEndPage, pContext, false);
+            }
+          } else {
+            if (JudgeLeaderOrTrailerForOccur(pLeaderNode))
+              AddPendingNode(pLeaderNode, true);
+          }
+
+          GotoNextContainerNodeSimple(true);
+          if (bCreatePage) {
+            bForceEndPage = true;
+            bIsManualBreak = true;
+            if (m_nCurChildNodeStage == Stage::kDone)
+              bBreakDone = true;
+          }
+          goto SuspendAndCreateNewRow;
+        }
+        case Stage::kBookendLeader: {
+          if (m_pCurChildPreprocessor) {
+            pProcessor = std::move(m_pCurChildPreprocessor);
+          } else if (m_pViewLayoutProcessor) {
+            CXFA_Node* pLeaderNode =
+                m_pViewLayoutProcessor->ProcessBookendLeader(m_pCurChildNode);
+            if (pLeaderNode) {
+              pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+                  pLeaderNode, m_pViewLayoutProcessor.Get());
+            }
+          }
+
+          if (pProcessor) {
+            if (InsertFlowedItem(
+                    pProcessor.get(), bContainerWidthAutoSize,
+                    bContainerHeightAutoSize, container_size.height,
+                    eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems,
+                    bUseBreakControl, fAvailHeight, fRealHeight,
+                    fContentWidthLimit, &fContentCurRowY,
+                    &fContentCurRowAvailWidth, &fContentCurRowHeight,
+                    &bAddedItemInRow, &bForceEndPage, pContext,
+                    false) != Result::kDone) {
+              goto SuspendAndCreateNewRow;
+            }
+            pProcessor.reset();
+          }
+          break;
+        }
+        case Stage::kBookendTrailer: {
+          if (m_pCurChildPreprocessor) {
+            pProcessor = std::move(m_pCurChildPreprocessor);
+          } else if (m_pViewLayoutProcessor) {
+            CXFA_Node* pTrailerNode =
+                m_pViewLayoutProcessor->ProcessBookendTrailer(m_pCurChildNode);
+            if (pTrailerNode) {
+              pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+                  pTrailerNode, m_pViewLayoutProcessor.Get());
+            }
+          }
+          if (pProcessor) {
+            if (InsertFlowedItem(
+                    pProcessor.get(), bContainerWidthAutoSize,
+                    bContainerHeightAutoSize, container_size.height,
+                    eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems,
+                    bUseBreakControl, fAvailHeight, fRealHeight,
+                    fContentWidthLimit, &fContentCurRowY,
+                    &fContentCurRowAvailWidth, &fContentCurRowHeight,
+                    &bAddedItemInRow, &bForceEndPage, pContext,
+                    false) != Result::kDone) {
+              goto SuspendAndCreateNewRow;
+            }
+            pProcessor.reset();
+          }
+          break;
+        }
+        case Stage::kContainer: {
+          ASSERT(m_pCurChildNode->IsContainerNode());
+          if (m_pCurChildNode->GetElementType() == XFA_Element::Variables)
+            break;
+          if (fContentCurRowY >= fHeightLimit + kXFALayoutPrecision &&
+              m_pCurChildNode->PresenceRequiresSpace()) {
+            bForceEndPage = true;
+            goto SuspendAndCreateNewRow;
+          }
+          if (!m_pCurChildNode->IsContainerNode())
+            break;
+
+          bool bNewRow = false;
+          if (m_pCurChildPreprocessor) {
+            pProcessor = std::move(m_pCurChildPreprocessor);
+            bNewRow = true;
+          } else {
+            pProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+                m_pCurChildNode, m_pViewLayoutProcessor.Get());
+          }
+
+          pProcessor->InsertPendingItems(m_pCurChildNode);
+          Result rs = InsertFlowedItem(
+              pProcessor.get(), bContainerWidthAutoSize,
+              bContainerHeightAutoSize, container_size.height, eFlowStrategy,
+              &uCurHAlignState, rgCurLineLayoutItems, bUseBreakControl,
+              fAvailHeight, fRealHeight, fContentWidthLimit, &fContentCurRowY,
+              &fContentCurRowAvailWidth, &fContentCurRowHeight,
+              &bAddedItemInRow, &bForceEndPage, pContext, bNewRow);
+          switch (rs) {
+            case Result::kManualBreak:
+              bIsManualBreak = true;
+              FALLTHROUGH;
+            case Result::kPageFullBreak:
+              bForceEndPage = true;
+              FALLTHROUGH;
+            case Result::kRowFullBreak:
+              goto SuspendAndCreateNewRow;
+            case Result::kDone:
+            default:
+              fContentCurRowY +=
+                  pProcessor->InsertPendingItems(m_pCurChildNode);
+              pProcessor.reset();
+              break;
+          }
+          break;
+        }
+        case Stage::kDone:
+          break;
+        default:
+          break;
+      }
+      GotoNextContainerNodeSimple(true);
+      if (bAddedItemInRow && eFlowStrategy == XFA_AttributeValue::Tb)
+        break;
+      continue;
+    SuspendAndCreateNewRow:
+      if (pProcessor)
+        m_pCurChildPreprocessor = std::move(pProcessor);
+      break;
+    }
+
+    CalculateRowChildPosition(rgCurLineLayoutItems, eFlowStrategy,
+                              bContainerHeightAutoSize, bContainerWidthAutoSize,
+                              &calculated_size.width, &calculated_size.height,
+                              &fContentCurRowY, fContentCurRowHeight,
+                              fContentWidthLimit, bRootForceTb);
+    m_fWidthLimit = fContentCurRowAvailWidth;
+    if (bForceEndPage)
+      break;
+  }
+
+  bool bRetValue =
+      m_nCurChildNodeStage == Stage::kDone && m_PendingNodes.empty();
+  if (bBreakDone)
+    bRetValue = false;
+
+  container_size = CalculateContainerComponentSizeFromContentSize(
+      GetFormNode(), bContainerWidthAutoSize, calculated_size.width,
+      bContainerHeightAutoSize, calculated_size.height, container_size);
+
+  if (container_size.height >= kXFALayoutPrecision || m_pLayoutItem ||
+      bRetValue) {
+    if (!m_pLayoutItem)
+      m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
+    container_size.height = std::max(container_size.height, 0.f);
+
+    SetCurrentComponentSize(container_size);
+    if (bForceEndPage)
+      m_fUsedSize = 0;
+    else
+      m_fUsedSize += m_pLayoutItem->m_sSize.height;
+  }
+
+  if (bRetValue)
+    return Result::kDone;
+  return bIsManualBreak ? Result::kManualBreak : Result::kPageFullBreak;
+}
+
+bool CXFA_ContentLayoutProcessor::CalculateRowChildPosition(
+    std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+    XFA_AttributeValue eFlowStrategy,
+    bool bContainerHeightAutoSize,
+    bool bContainerWidthAutoSize,
+    float* fContentCalculatedWidth,
+    float* fContentCalculatedHeight,
+    float* fContentCurRowY,
+    float fContentCurRowHeight,
+    float fContentWidthLimit,
+    bool bRootForceTb) {
+  int32_t nGroupLengths[3] = {0, 0, 0};
+  float fGroupWidths[3] = {0, 0, 0};
+  int32_t nTotalLength = 0;
+  for (int32_t i = 0; i < 3; i++) {
+    nGroupLengths[i] = pdfium::CollectionSize<int32_t>(rgCurLineLayoutItems[i]);
+    for (int32_t c = nGroupLengths[i], j = 0; j < c; j++) {
+      nTotalLength++;
+      if (rgCurLineLayoutItems[i][j]->GetFormNode()->PresenceRequiresSpace())
+        fGroupWidths[i] += rgCurLineLayoutItems[i][j]->m_sSize.width;
+    }
+  }
+  if (!nTotalLength) {
+    if (bContainerHeightAutoSize) {
+      *fContentCalculatedHeight =
+          std::min(*fContentCalculatedHeight, *fContentCurRowY);
+    }
+    return false;
+  }
+  if (!m_pLayoutItem)
+    m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
+
+  if (eFlowStrategy != XFA_AttributeValue::Rl_tb) {
+    float fCurPos;
+    fCurPos = 0;
+    for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) {
+      if (bRootForceTb) {
+        rgCurLineLayoutItems[0][j]->m_sPos = CalculatePositionedContainerPos(
+            rgCurLineLayoutItems[0][j]->GetFormNode(),
+            rgCurLineLayoutItems[0][j]->m_sSize);
+      } else {
+        rgCurLineLayoutItems[0][j]->m_sPos =
+            CFX_PointF(fCurPos, *fContentCurRowY);
+        if (rgCurLineLayoutItems[0][j]->GetFormNode()->PresenceRequiresSpace())
+          fCurPos += rgCurLineLayoutItems[0][j]->m_sSize.width;
+      }
+      m_pLayoutItem->AppendLastChild(rgCurLineLayoutItems[0][j]);
+      m_fLastRowWidth = fCurPos;
+    }
+    fCurPos = (fContentWidthLimit + fGroupWidths[0] - fGroupWidths[1] -
+               fGroupWidths[2]) /
+              2;
+    for (int32_t c = nGroupLengths[1], j = 0; j < c; j++) {
+      if (bRootForceTb) {
+        rgCurLineLayoutItems[1][j]->m_sPos = CalculatePositionedContainerPos(
+            rgCurLineLayoutItems[1][j]->GetFormNode(),
+            rgCurLineLayoutItems[1][j]->m_sSize);
+      } else {
+        rgCurLineLayoutItems[1][j]->m_sPos =
+            CFX_PointF(fCurPos, *fContentCurRowY);
+        if (rgCurLineLayoutItems[1][j]->GetFormNode()->PresenceRequiresSpace())
+          fCurPos += rgCurLineLayoutItems[1][j]->m_sSize.width;
+      }
+      m_pLayoutItem->AppendLastChild(rgCurLineLayoutItems[1][j]);
+      m_fLastRowWidth = fCurPos;
+    }
+    fCurPos = fContentWidthLimit - fGroupWidths[2];
+    for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) {
+      if (bRootForceTb) {
+        rgCurLineLayoutItems[2][j]->m_sPos = CalculatePositionedContainerPos(
+            rgCurLineLayoutItems[2][j]->GetFormNode(),
+            rgCurLineLayoutItems[2][j]->m_sSize);
+      } else {
+        rgCurLineLayoutItems[2][j]->m_sPos =
+            CFX_PointF(fCurPos, *fContentCurRowY);
+        if (rgCurLineLayoutItems[2][j]->GetFormNode()->PresenceRequiresSpace())
+          fCurPos += rgCurLineLayoutItems[2][j]->m_sSize.width;
+      }
+      m_pLayoutItem->AppendLastChild(rgCurLineLayoutItems[2][j]);
+      m_fLastRowWidth = fCurPos;
+    }
+  } else {
+    float fCurPos;
+    fCurPos = fGroupWidths[0];
+    for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) {
+      if (rgCurLineLayoutItems[0][j]->GetFormNode()->PresenceRequiresSpace())
+        fCurPos -= rgCurLineLayoutItems[0][j]->m_sSize.width;
+
+      rgCurLineLayoutItems[0][j]->m_sPos =
+          CFX_PointF(fCurPos, *fContentCurRowY);
+      m_pLayoutItem->AppendLastChild(rgCurLineLayoutItems[0][j]);
+      m_fLastRowWidth = fCurPos;
+    }
+    fCurPos = (fContentWidthLimit + fGroupWidths[0] + fGroupWidths[1] -
+               fGroupWidths[2]) /
+              2;
+    for (int32_t c = nGroupLengths[1], j = 0; j < c; j++) {
+      if (rgCurLineLayoutItems[1][j]->GetFormNode()->PresenceRequiresSpace())
+        fCurPos -= rgCurLineLayoutItems[1][j]->m_sSize.width;
+
+      rgCurLineLayoutItems[1][j]->m_sPos =
+          CFX_PointF(fCurPos, *fContentCurRowY);
+      m_pLayoutItem->AppendLastChild(rgCurLineLayoutItems[1][j]);
+      m_fLastRowWidth = fCurPos;
+    }
+    fCurPos = fContentWidthLimit;
+    for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) {
+      if (rgCurLineLayoutItems[2][j]->GetFormNode()->PresenceRequiresSpace())
+        fCurPos -= rgCurLineLayoutItems[2][j]->m_sSize.width;
+
+      rgCurLineLayoutItems[2][j]->m_sPos =
+          CFX_PointF(fCurPos, *fContentCurRowY);
+      m_pLayoutItem->AppendLastChild(rgCurLineLayoutItems[2][j]);
+      m_fLastRowWidth = fCurPos;
+    }
+  }
+  m_fLastRowY = *fContentCurRowY;
+  *fContentCurRowY += fContentCurRowHeight;
+  if (bContainerWidthAutoSize) {
+    float fChildSuppliedWidth = fGroupWidths[0];
+    if (fContentWidthLimit < FLT_MAX &&
+        fContentWidthLimit > fChildSuppliedWidth) {
+      fChildSuppliedWidth = fContentWidthLimit;
+    }
+    *fContentCalculatedWidth =
+        std::max(*fContentCalculatedWidth, fChildSuppliedWidth);
+  }
+  if (bContainerHeightAutoSize) {
+    *fContentCalculatedHeight =
+        std::max(*fContentCalculatedHeight, *fContentCurRowY);
+  }
+  return true;
+}
+
+CXFA_Node* CXFA_ContentLayoutProcessor::GetSubformSetParent(
+    CXFA_Node* pSubformSet) {
+  if (pSubformSet && pSubformSet->GetElementType() == XFA_Element::SubformSet) {
+    CXFA_Node* pParent = pSubformSet->GetParent();
+    while (pParent) {
+      if (pParent->GetElementType() != XFA_Element::SubformSet)
+        return pParent;
+      pParent = pParent->GetParent();
+    }
+  }
+  return pSubformSet;
+}
+
+void CXFA_ContentLayoutProcessor::DoLayoutField() {
+  if (m_pLayoutItem)
+    return;
+
+  ASSERT(!m_pCurChildNode);
+  m_pLayoutItem = CreateContentLayoutItem(GetFormNode());
+  if (!m_pLayoutItem)
+    return;
+
+  CXFA_Document* pDocument = GetFormNode()->GetDocument();
+  CXFA_FFNotify* pNotify = pDocument->GetNotify();
+  CFX_SizeF size(-1, -1);
+  pNotify->StartFieldDrawLayout(GetFormNode(), &size.width, &size.height);
+
+  int32_t nRotate = XFA_MapRotation(
+      GetFormNode()->JSObject()->GetInteger(XFA_Attribute::Rotate));
+  if (nRotate == 90 || nRotate == 270)
+    std::swap(size.width, size.height);
+
+  SetCurrentComponentSize(size);
+}
+
+CXFA_ContentLayoutProcessor::Result CXFA_ContentLayoutProcessor::DoLayout(
+    bool bUseBreakControl,
+    float fHeightLimit,
+    float fRealHeight) {
+  return DoLayoutInternal(bUseBreakControl, fHeightLimit, fRealHeight, nullptr);
+}
+
+CXFA_ContentLayoutProcessor::Result
+CXFA_ContentLayoutProcessor::DoLayoutInternal(bool bUseBreakControl,
+                                              float fHeightLimit,
+                                              float fRealHeight,
+                                              Context* pContext) {
+  switch (GetFormNode()->GetElementType()) {
+    case XFA_Element::Subform:
+    case XFA_Element::Area:
+    case XFA_Element::ExclGroup:
+    case XFA_Element::SubformSet: {
+      bool bRootForceTb = false;
+      CXFA_Node* pLayoutNode = GetSubformSetParent(GetFormNode());
+      XFA_AttributeValue eLayoutStrategy =
+          GetLayout(pLayoutNode, &bRootForceTb);
+      switch (eLayoutStrategy) {
+        case XFA_AttributeValue::Tb:
+        case XFA_AttributeValue::Lr_tb:
+        case XFA_AttributeValue::Rl_tb:
+          return DoLayoutFlowedContainer(bUseBreakControl, eLayoutStrategy,
+                                         fHeightLimit, fRealHeight, pContext,
+                                         bRootForceTb);
+        case XFA_AttributeValue::Position:
+        case XFA_AttributeValue::Row:
+        case XFA_AttributeValue::Rl_row:
+        default:
+          DoLayoutPositionedContainer(pContext);
+          m_nCurChildNodeStage = Stage::kDone;
+          return Result::kDone;
+        case XFA_AttributeValue::Table:
+          DoLayoutTableContainer(pLayoutNode);
+          m_nCurChildNodeStage = Stage::kDone;
+          return Result::kDone;
+      }
+    }
+    case XFA_Element::Draw:
+    case XFA_Element::Field:
+      DoLayoutField();
+      m_nCurChildNodeStage = Stage::kDone;
+      return Result::kDone;
+    case XFA_Element::ContentArea:
+    default:
+      return Result::kDone;
+  }
+}
+
+CFX_SizeF CXFA_ContentLayoutProcessor::GetCurrentComponentSize() {
+  return CFX_SizeF(m_pLayoutItem->m_sSize.width, m_pLayoutItem->m_sSize.height);
+}
+
+void CXFA_ContentLayoutProcessor::SetCurrentComponentPos(
+    const CFX_PointF& pos) {
+  m_pLayoutItem->m_sPos = pos;
+}
+
+void CXFA_ContentLayoutProcessor::SetCurrentComponentSize(
+    const CFX_SizeF& size) {
+  m_pLayoutItem->m_sSize = size;
+}
+
+bool CXFA_ContentLayoutProcessor::JudgeLeaderOrTrailerForOccur(
+    CXFA_Node* pFormNode) {
+  if (!pFormNode)
+    return false;
+
+  CXFA_Node* pTemplate = pFormNode->GetTemplateNodeIfExists();
+  if (!pTemplate)
+    pTemplate = pFormNode;
+
+  auto* pOccur =
+      pTemplate->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+  if (!pOccur)
+    return false;
+
+  int32_t iMax = pOccur->GetMax();
+  if (iMax < 0)
+    return true;
+
+  int32_t iCount = m_PendingNodesCount[pTemplate];
+  if (iCount >= iMax)
+    return false;
+
+  m_PendingNodesCount[pTemplate] = iCount + 1;
+  return true;
+}
+
+void CXFA_ContentLayoutProcessor::UpdatePendingItemLayout(
+    const RetainPtr<CXFA_ContentLayoutItem>& pLayoutItem) {
+  XFA_AttributeValue eLayout =
+      pLayoutItem->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout);
+  switch (eLayout) {
+    case XFA_AttributeValue::Row:
+    case XFA_AttributeValue::Rl_row:
+      RelocateTableRowCells(pLayoutItem, m_rgSpecifiedColumnWidths, eLayout);
+      break;
+    default:
+      break;
+  }
+}
+
+void CXFA_ContentLayoutProcessor::AddTrailerBeforeSplit(
+    float fSplitPos,
+    const RetainPtr<CXFA_ContentLayoutItem>& pTrailerLayoutItem,
+    bool bUseInherited) {
+  if (!pTrailerLayoutItem)
+    return;
+
+  float fHeight = pTrailerLayoutItem->m_sSize.height;
+  if (bUseInherited) {
+    float fNewSplitPos = 0;
+    if (fSplitPos - fHeight > kXFALayoutPrecision)
+      fNewSplitPos = FindSplitPos(fSplitPos - fHeight);
+    if (fNewSplitPos > kXFALayoutPrecision)
+      SplitLayoutItem(fNewSplitPos);
+    return;
+  }
+
+  UpdatePendingItemLayout(pTrailerLayoutItem);
+  CXFA_Margin* pMargin =
+      GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
+  CFX_FloatRect inset = GetMarginInset(pMargin);
+  if (!IsAddNewRowForTrailer(pTrailerLayoutItem.Get())) {
+    pTrailerLayoutItem->m_sPos.y = m_fLastRowY;
+    pTrailerLayoutItem->m_sPos.x = m_fLastRowWidth;
+    m_pLayoutItem->m_sSize.width += pTrailerLayoutItem->m_sSize.width;
+    m_pLayoutItem->AppendLastChild(pTrailerLayoutItem);
+    return;
+  }
+
+  float fNewSplitPos = 0;
+  if (fSplitPos - fHeight > kXFALayoutPrecision)
+    fNewSplitPos = FindSplitPos(fSplitPos - fHeight);
+
+  if (fNewSplitPos > kXFALayoutPrecision) {
+    SplitLayoutItem(fNewSplitPos);
+    pTrailerLayoutItem->m_sPos.y = fNewSplitPos - inset.top - inset.bottom;
+  } else {
+    pTrailerLayoutItem->m_sPos.y = fSplitPos - inset.top - inset.bottom;
+  }
+
+  switch (pTrailerLayoutItem->GetFormNode()->JSObject()->GetEnum(
+      XFA_Attribute::HAlign)) {
+    case XFA_AttributeValue::Right:
+      pTrailerLayoutItem->m_sPos.x = m_pLayoutItem->m_sSize.width -
+                                     inset.right -
+                                     pTrailerLayoutItem->m_sSize.width;
+      break;
+    case XFA_AttributeValue::Center:
+      pTrailerLayoutItem->m_sPos.x =
+          (m_pLayoutItem->m_sSize.width - inset.left - inset.right -
+           pTrailerLayoutItem->m_sSize.width) /
+          2;
+      break;
+    case XFA_AttributeValue::Left:
+    default:
+      pTrailerLayoutItem->m_sPos.x = inset.left;
+      break;
+  }
+  m_pLayoutItem->m_sSize.height += fHeight;
+  m_pLayoutItem->AppendLastChild(pTrailerLayoutItem);
+}
+
+void CXFA_ContentLayoutProcessor::AddLeaderAfterSplit(
+    const RetainPtr<CXFA_ContentLayoutItem>& pLeaderLayoutItem) {
+  UpdatePendingItemLayout(pLeaderLayoutItem);
+
+  CXFA_Margin* pMarginNode =
+      GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
+  float fLeftInset = 0;
+  float fRightInset = 0;
+  if (pMarginNode) {
+    fLeftInset = pMarginNode->JSObject()->GetMeasureInUnit(
+        XFA_Attribute::LeftInset, XFA_Unit::Pt);
+    fRightInset = pMarginNode->JSObject()->GetMeasureInUnit(
+        XFA_Attribute::RightInset, XFA_Unit::Pt);
+  }
+
+  float fHeight = pLeaderLayoutItem->m_sSize.height;
+  for (CXFA_LayoutItem* pIter = m_pLayoutItem->GetFirstChild(); pIter;
+       pIter = pIter->GetNextSibling()) {
+    CXFA_ContentLayoutItem* pContentItem = pIter->AsContentLayoutItem();
+    if (!pContentItem)
+      continue;
+
+    pContentItem->m_sPos.y += fHeight;
+  }
+  pLeaderLayoutItem->m_sPos.y = 0;
+
+  switch (pLeaderLayoutItem->GetFormNode()->JSObject()->GetEnum(
+      XFA_Attribute::HAlign)) {
+    case XFA_AttributeValue::Right:
+      pLeaderLayoutItem->m_sPos.x = m_pLayoutItem->m_sSize.width - fRightInset -
+                                    pLeaderLayoutItem->m_sSize.width;
+      break;
+    case XFA_AttributeValue::Center:
+      pLeaderLayoutItem->m_sPos.x =
+          (m_pLayoutItem->m_sSize.width - fLeftInset - fRightInset -
+           pLeaderLayoutItem->m_sSize.width) /
+          2;
+      break;
+    case XFA_AttributeValue::Left:
+    default:
+      pLeaderLayoutItem->m_sPos.x = fLeftInset;
+      break;
+  }
+  m_pLayoutItem->m_sSize.height += fHeight;
+  m_pLayoutItem->AppendLastChild(pLeaderLayoutItem);
+}
+
+void CXFA_ContentLayoutProcessor::AddPendingNode(CXFA_Node* pPendingNode,
+                                                 bool bBreakPending) {
+  m_PendingNodes.push_back(pPendingNode);
+  m_bBreakPending = bBreakPending;
+}
+
+float CXFA_ContentLayoutProcessor::InsertPendingItems(
+    CXFA_Node* pCurChildNode) {
+  float fTotalHeight = 0;
+  if (m_PendingNodes.empty())
+    return fTotalHeight;
+
+  if (!m_pLayoutItem) {
+    m_pLayoutItem = CreateContentLayoutItem(pCurChildNode);
+    m_pLayoutItem->m_sSize.clear();
+  }
+
+  while (!m_PendingNodes.empty()) {
+    auto pPendingProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+        m_PendingNodes.front(), nullptr);
+    m_PendingNodes.pop_front();
+    pPendingProcessor->DoLayout(false, FLT_MAX, FLT_MAX);
+    RetainPtr<CXFA_ContentLayoutItem> pPendingLayoutItem;
+    if (pPendingProcessor->HasLayoutItem())
+      pPendingLayoutItem = pPendingProcessor->ExtractLayoutItem();
+    if (pPendingLayoutItem) {
+      AddLeaderAfterSplit(pPendingLayoutItem);
+      if (m_bBreakPending)
+        fTotalHeight += pPendingLayoutItem->m_sSize.height;
+    }
+  }
+  return fTotalHeight;
+}
+
+CXFA_ContentLayoutProcessor::Result
+CXFA_ContentLayoutProcessor::InsertFlowedItem(
+    CXFA_ContentLayoutProcessor* pProcessor,
+    bool bContainerWidthAutoSize,
+    bool bContainerHeightAutoSize,
+    float fContainerHeight,
+    XFA_AttributeValue eFlowStrategy,
+    uint8_t* uCurHAlignState,
+    std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+    bool bUseBreakControl,
+    float fAvailHeight,
+    float fRealHeight,
+    float fContentWidthLimit,
+    float* fContentCurRowY,
+    float* fContentCurRowAvailWidth,
+    float* fContentCurRowHeight,
+    bool* bAddedItemInRow,
+    bool* bForceEndPage,
+    Context* pLayoutContext,
+    bool bNewRow) {
+  bool bTakeSpace = pProcessor->GetFormNode()->PresenceRequiresSpace();
+  uint8_t uHAlign = HAlignEnumToInt(
+      m_pCurChildNode->JSObject()->GetEnum(XFA_Attribute::HAlign));
+  if (bContainerWidthAutoSize)
+    uHAlign = 0;
+
+  if ((eFlowStrategy != XFA_AttributeValue::Rl_tb &&
+       uHAlign < *uCurHAlignState) ||
+      (eFlowStrategy == XFA_AttributeValue::Rl_tb &&
+       uHAlign > *uCurHAlignState)) {
+    return Result::kRowFullBreak;
+  }
+
+  *uCurHAlignState = uHAlign;
+  bool bIsOwnSplit =
+      pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None;
+  bool bUseRealHeight = bTakeSpace && bContainerHeightAutoSize && bIsOwnSplit &&
+                        pProcessor->GetFormNode()->GetParent()->GetIntact() ==
+                            XFA_AttributeValue::None;
+  bool bIsTransHeight = bTakeSpace;
+  if (bIsTransHeight && !bIsOwnSplit) {
+    bool bRootForceTb = false;
+    XFA_AttributeValue eLayoutStrategy =
+        GetLayout(pProcessor->GetFormNode(), &bRootForceTb);
+    if (eLayoutStrategy == XFA_AttributeValue::Lr_tb ||
+        eLayoutStrategy == XFA_AttributeValue::Rl_tb) {
+      bIsTransHeight = false;
+    }
+  }
+
+  bool bUseInherited = false;
+  Context layoutContext;
+  if (m_pViewLayoutProcessor) {
+    CXFA_Node* pOverflowNode =
+        m_pViewLayoutProcessor->QueryOverflow(GetFormNode());
+    if (pOverflowNode) {
+      layoutContext.m_pOverflowNode = pOverflowNode;
+      layoutContext.m_pOverflowProcessor = this;
+      pLayoutContext = &layoutContext;
+    }
+  }
+
+  Result eRetValue = Result::kDone;
+  if (!bNewRow || pProcessor->m_ePreProcessRs == Result::kDone) {
+    eRetValue = pProcessor->DoLayoutInternal(
+        bTakeSpace && bUseBreakControl,
+        bUseRealHeight ? fRealHeight - *fContentCurRowY : FLT_MAX,
+        bIsTransHeight ? fRealHeight - *fContentCurRowY : FLT_MAX,
+        pLayoutContext);
+    pProcessor->m_ePreProcessRs = eRetValue;
+  } else {
+    eRetValue = pProcessor->m_ePreProcessRs;
+    pProcessor->m_ePreProcessRs = Result::kDone;
+  }
+  if (!pProcessor->HasLayoutItem())
+    return eRetValue;
+
+  CFX_SizeF childSize = pProcessor->GetCurrentComponentSize();
+  if (bUseRealHeight && fRealHeight < kXFALayoutPrecision) {
+    fRealHeight = FLT_MAX;
+    fAvailHeight = FLT_MAX;
+  }
+  if (bTakeSpace &&
+      (childSize.width > *fContentCurRowAvailWidth + kXFALayoutPrecision) &&
+      (fContentWidthLimit - *fContentCurRowAvailWidth > kXFALayoutPrecision)) {
+    return Result::kRowFullBreak;
+  }
+
+  CXFA_Node* pOverflowLeaderNode = nullptr;
+  CXFA_Node* pOverflowTrailerNode = nullptr;
+  CXFA_Node* pFormNode = nullptr;
+  RetainPtr<CXFA_ContentLayoutItem> pTrailerLayoutItem;
+  bool bIsAddTrailerHeight = false;
+  if (m_pViewLayoutProcessor &&
+      pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None) {
+    pFormNode =
+        m_pViewLayoutProcessor->QueryOverflow(pProcessor->GetFormNode());
+    if (!pFormNode && pLayoutContext && pLayoutContext->m_pOverflowProcessor) {
+      pFormNode = pLayoutContext->m_pOverflowNode.Get();
+      bUseInherited = true;
+    }
+    Optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
+        m_pViewLayoutProcessor->ProcessOverflow(pFormNode, false);
+    if (overflow_data.has_value()) {
+      pOverflowLeaderNode = overflow_data.value().pLeader;
+      pOverflowTrailerNode = overflow_data.value().pTrailer;
+      if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowTrailerNode)) {
+        if (pOverflowTrailerNode) {
+          auto pOverflowLeaderProcessor =
+              pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+                  pOverflowTrailerNode, nullptr);
+          pOverflowLeaderProcessor->DoLayout(false, FLT_MAX, FLT_MAX);
+          pTrailerLayoutItem =
+              pOverflowLeaderProcessor->HasLayoutItem()
+                  ? pOverflowLeaderProcessor->ExtractLayoutItem()
+                  : nullptr;
+        }
+
+        bIsAddTrailerHeight =
+            bUseInherited
+                ? IsAddNewRowForTrailer(pTrailerLayoutItem.Get())
+                : pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem.Get());
+        if (bIsAddTrailerHeight) {
+          childSize.height += pTrailerLayoutItem->m_sSize.height;
+          bIsAddTrailerHeight = true;
+        }
+      }
+    }
+  }
+
+  if (!bTakeSpace ||
+      *fContentCurRowY + childSize.height <=
+          fAvailHeight + kXFALayoutPrecision ||
+      (!bContainerHeightAutoSize &&
+       m_fUsedSize + fAvailHeight + kXFALayoutPrecision >= fContainerHeight)) {
+    if (!bTakeSpace || eRetValue == Result::kDone) {
+      if (pProcessor->m_bUseInherited) {
+        if (pTrailerLayoutItem)
+          pProcessor->AddTrailerBeforeSplit(childSize.height,
+                                            pTrailerLayoutItem, false);
+        if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode))
+          pProcessor->AddPendingNode(pOverflowLeaderNode, false);
+
+        pProcessor->m_bUseInherited = false;
+      } else {
+        if (bIsAddTrailerHeight)
+          childSize.height -= pTrailerLayoutItem->m_sSize.height;
+
+        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
+                                         pOverflowTrailerNode,
+                                         pTrailerLayoutItem, pFormNode);
+      }
+
+      RetainPtr<CXFA_ContentLayoutItem> pChildLayoutItem =
+          pProcessor->ExtractLayoutItem();
+      if (ExistContainerKeep(pProcessor->GetFormNode(), false) &&
+          pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None) {
+        m_ArrayKeepItems.push_back(pChildLayoutItem);
+      } else {
+        m_ArrayKeepItems.clear();
+      }
+      rgCurLineLayoutItems[uHAlign].push_back(pChildLayoutItem);
+      *bAddedItemInRow = true;
+      if (bTakeSpace) {
+        *fContentCurRowAvailWidth -= childSize.width;
+        *fContentCurRowHeight =
+            std::max(*fContentCurRowHeight, childSize.height);
+      }
+      return Result::kDone;
+    }
+
+    if (eRetValue == Result::kPageFullBreak) {
+      if (pProcessor->m_bUseInherited) {
+        if (pTrailerLayoutItem) {
+          pProcessor->AddTrailerBeforeSplit(childSize.height,
+                                            pTrailerLayoutItem, false);
+        }
+        if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode))
+          pProcessor->AddPendingNode(pOverflowLeaderNode, false);
+
+        pProcessor->m_bUseInherited = false;
+      } else {
+        if (bIsAddTrailerHeight)
+          childSize.height -= pTrailerLayoutItem->m_sSize.height;
+
+        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
+                                         pOverflowTrailerNode,
+                                         pTrailerLayoutItem, pFormNode);
+      }
+    }
+    rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
+    *bAddedItemInRow = true;
+    *fContentCurRowAvailWidth -= childSize.width;
+    *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height);
+    return eRetValue;
+  }
+
+  Result eResult;
+  if (ProcessKeepForSplit(pProcessor, eRetValue, &rgCurLineLayoutItems[uHAlign],
+                          fContentCurRowAvailWidth, fContentCurRowHeight,
+                          fContentCurRowY, bAddedItemInRow, bForceEndPage,
+                          &eResult)) {
+    return eResult;
+  }
+
+  *bForceEndPage = true;
+  float fSplitPos = pProcessor->FindSplitPos(fAvailHeight - *fContentCurRowY);
+  if (fSplitPos > kXFALayoutPrecision) {
+    XFA_AttributeValue eLayout =
+        pProcessor->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout);
+    if (eLayout == XFA_AttributeValue::Tb && eRetValue == Result::kDone) {
+      pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
+                                       pOverflowTrailerNode, pTrailerLayoutItem,
+                                       pFormNode);
+      rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
+      *bAddedItemInRow = true;
+      if (bTakeSpace) {
+        *fContentCurRowAvailWidth -= childSize.width;
+        *fContentCurRowHeight =
+            std::max(*fContentCurRowHeight, childSize.height);
+      }
+      return Result::kPageFullBreak;
+    }
+
+    if (m_pViewLayoutProcessor && !pProcessor->m_bUseInherited &&
+        eRetValue != Result::kPageFullBreak) {
+      m_pViewLayoutProcessor->ProcessOverflow(pFormNode, true);
+    }
+    if (pTrailerLayoutItem && bIsAddTrailerHeight) {
+      pProcessor->AddTrailerBeforeSplit(fSplitPos, pTrailerLayoutItem,
+                                        bUseInherited);
+    } else {
+      pProcessor->SplitLayoutItem(fSplitPos);
+    }
+
+    if (bUseInherited) {
+      pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
+                                       pOverflowTrailerNode, pTrailerLayoutItem,
+                                       pFormNode);
+      m_bUseInherited = true;
+    } else {
+      CXFA_LayoutItem* firstChild = pProcessor->m_pLayoutItem->GetFirstChild();
+      if (firstChild && !firstChild->GetNextSibling() &&
+          firstChild->GetFormNode()->IsLayoutGeneratedNode()) {
+        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
+                                         pOverflowTrailerNode,
+                                         pTrailerLayoutItem, pFormNode);
+      } else if (pProcessor->JudgeLeaderOrTrailerForOccur(
+                     pOverflowLeaderNode)) {
+        pProcessor->AddPendingNode(pOverflowLeaderNode, false);
+      }
+    }
+
+    if (pProcessor->m_pLayoutItem->GetNextSibling()) {
+      childSize = pProcessor->GetCurrentComponentSize();
+      rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
+      *bAddedItemInRow = true;
+      if (bTakeSpace) {
+        *fContentCurRowAvailWidth -= childSize.width;
+        *fContentCurRowHeight =
+            std::max(*fContentCurRowHeight, childSize.height);
+      }
+    }
+    return Result::kPageFullBreak;
+  }
+
+  if (*fContentCurRowY <= kXFALayoutPrecision) {
+    childSize = pProcessor->GetCurrentComponentSize();
+    if (pProcessor->m_pViewLayoutProcessor->GetNextAvailContentHeight(
+            childSize.height)) {
+      if (m_pViewLayoutProcessor) {
+        if (!pFormNode && pLayoutContext)
+          pFormNode = pLayoutContext->m_pOverflowProcessor->GetFormNode();
+
+        m_pViewLayoutProcessor->ProcessOverflow(pFormNode, true);
+      }
+      if (bUseInherited) {
+        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
+                                         pOverflowTrailerNode,
+                                         pTrailerLayoutItem, pFormNode);
+        m_bUseInherited = true;
+      }
+      return Result::kPageFullBreak;
+    }
+
+    rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
+    *bAddedItemInRow = true;
+    if (bTakeSpace) {
+      *fContentCurRowAvailWidth -= childSize.width;
+      *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height);
+    }
+    if (eRetValue == Result::kDone)
+      *bForceEndPage = false;
+
+    return eRetValue;
+  }
+
+  XFA_AttributeValue eLayout =
+      pProcessor->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout);
+  if (pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None &&
+      eLayout == XFA_AttributeValue::Tb) {
+    if (m_pViewLayoutProcessor) {
+      Optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
+          m_pViewLayoutProcessor->ProcessOverflow(pFormNode, true);
+      if (overflow_data.has_value()) {
+        pOverflowLeaderNode = overflow_data.value().pLeader;
+        pOverflowTrailerNode = overflow_data.value().pTrailer;
+      }
+    }
+    if (pTrailerLayoutItem)
+      pProcessor->AddTrailerBeforeSplit(fSplitPos, pTrailerLayoutItem, false);
+    if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode))
+      pProcessor->AddPendingNode(pOverflowLeaderNode, false);
+
+    return Result::kPageFullBreak;
+  }
+
+  if (eRetValue != Result::kDone)
+    return Result::kPageFullBreak;
+
+  if (!pFormNode && pLayoutContext)
+    pFormNode = pLayoutContext->m_pOverflowProcessor->GetFormNode();
+  if (m_pViewLayoutProcessor) {
+    Optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data =
+        m_pViewLayoutProcessor->ProcessOverflow(pFormNode, true);
+    if (overflow_data.has_value()) {
+      pOverflowLeaderNode = overflow_data.value().pLeader;
+      pOverflowTrailerNode = overflow_data.value().pTrailer;
+    }
+  }
+  if (bUseInherited) {
+    pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, pOverflowTrailerNode,
+                                     pTrailerLayoutItem, pFormNode);
+    m_bUseInherited = true;
+  }
+  return Result::kPageFullBreak;
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::HandleKeep(CXFA_Node* pBreakAfterNode,
+                                        CXFA_Node** pCurActionNode) {
+  if (m_bKeepBreakFinish)
+    return {};
+  return FindBreakNode(pBreakAfterNode, false, pCurActionNode);
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::HandleBookendLeader(CXFA_Node* pParentContainer,
+                                                 CXFA_Node** pCurActionNode) {
+  for (CXFA_Node* pBookendNode = *pCurActionNode
+                                     ? (*pCurActionNode)->GetNextSibling()
+                                     : pParentContainer->GetFirstChild();
+       pBookendNode; pBookendNode = pBookendNode->GetNextSibling()) {
+    switch (pBookendNode->GetElementType()) {
+      case XFA_Element::Bookend:
+      case XFA_Element::Break:
+        *pCurActionNode = pBookendNode;
+        return Stage::kBookendLeader;
+      default:
+        break;
+    }
+  }
+  return {};
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::HandleBreakBefore(CXFA_Node* pChildContainer,
+                                               CXFA_Node** pCurActionNode) {
+  if (!*pCurActionNode)
+    return {};
+
+  CXFA_Node* pBreakBeforeNode = (*pCurActionNode)->GetNextSibling();
+  if (!m_bKeepBreakFinish) {
+    Optional<Stage> ret = FindBreakNode(pBreakBeforeNode, true, pCurActionNode);
+    if (ret.has_value())
+      return ret.value();
+  }
+  if (m_bIsProcessKeep)
+    return ProcessKeepNodesForBreakBefore(pCurActionNode, pChildContainer);
+
+  *pCurActionNode = pChildContainer;
+  return Stage::kContainer;
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::HandleBreakAfter(CXFA_Node* pChildContainer,
+                                              CXFA_Node** pCurActionNode) {
+  if (*pCurActionNode) {
+    CXFA_Node* pBreakAfterNode = (*pCurActionNode)->GetNextSibling();
+    return FindBreakNode(pBreakAfterNode, false, pCurActionNode);
+  }
+
+  CXFA_Node* pBreakAfterNode = pChildContainer->GetFirstChild();
+  return HandleKeep(pBreakAfterNode, pCurActionNode);
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::HandleCheckNextChildContainer(
+    CXFA_Node* pParentContainer,
+    CXFA_Node* pChildContainer,
+    CXFA_Node** pCurActionNode) {
+  CXFA_Node* pNextChildContainer =
+      pChildContainer ? pChildContainer->GetNextContainerSibling()
+                      : pParentContainer->GetFirstContainerChild();
+  while (pNextChildContainer && pNextChildContainer->IsLayoutGeneratedNode()) {
+    CXFA_Node* pSaveNode = pNextChildContainer;
+    pNextChildContainer = pNextChildContainer->GetNextContainerSibling();
+    if (pSaveNode->IsUnusedNode())
+      DeleteLayoutGeneratedNode(pSaveNode);
+  }
+  if (!pNextChildContainer)
+    return {};
+
+  bool bLastKeep = false;
+  Optional<Stage> ret = ProcessKeepNodesForCheckNext(
+      pCurActionNode, &pNextChildContainer, &bLastKeep);
+  if (ret.has_value())
+    return ret.value();
+
+  if (!m_bKeepBreakFinish && !bLastKeep) {
+    ret = FindBreakNode(pNextChildContainer->GetFirstChild(), true,
+                        pCurActionNode);
+    if (ret.has_value())
+      return ret.value();
+  }
+  *pCurActionNode = pNextChildContainer;
+  return m_bIsProcessKeep ? Stage::kKeep : Stage::kContainer;
+}
+
+Optional<CXFA_ContentLayoutProcessor::Stage>
+CXFA_ContentLayoutProcessor::HandleBookendTrailer(CXFA_Node* pParentContainer,
+                                                  CXFA_Node** pCurActionNode) {
+  for (CXFA_Node* pBookendNode = *pCurActionNode
+                                     ? (*pCurActionNode)->GetNextSibling()
+                                     : pParentContainer->GetFirstChild();
+       pBookendNode; pBookendNode = pBookendNode->GetNextSibling()) {
+    switch (pBookendNode->GetElementType()) {
+      case XFA_Element::Bookend:
+      case XFA_Element::Break:
+        *pCurActionNode = pBookendNode;
+        return Stage::kBookendTrailer;
+      default:
+        break;
+    }
+  }
+  return {};
+}
+
+void CXFA_ContentLayoutProcessor::ProcessKeepNodesEnd() {
+  m_bKeepBreakFinish = true;
+  m_pKeepHeadNode = nullptr;
+  m_pKeepTailNode = nullptr;
+  m_bIsProcessKeep = false;
+}
+
+void CXFA_ContentLayoutProcessor::AdjustContainerSpecifiedSize(
+    Context* pContext,
+    CFX_SizeF* pSize,
+    bool* pContainerWidthAutoSize,
+    bool* pContainerHeightAutoSize) {
+  if (pContext && pContext->m_fCurColumnWidth.has_value()) {
+    pSize->width = pContext->m_fCurColumnWidth.value();
+    *pContainerWidthAutoSize = false;
+  }
+  if (*pContainerHeightAutoSize)
+    return;
+
+  pSize->height -= m_fUsedSize;
+  CXFA_Node* pParentNode = GetFormNode()->GetParent();
+  bool bFocrTb = false;
+  if (!pParentNode ||
+      GetLayout(pParentNode, &bFocrTb) != XFA_AttributeValue::Row) {
+    return;
+  }
+
+  CXFA_Node* pChildContainer = GetFormNode()->GetFirstContainerChild();
+  if (!pChildContainer || !pChildContainer->GetNextContainerSibling())
+    return;
+
+  pSize->height = 0;
+  *pContainerHeightAutoSize = true;
+}
+
+CXFA_ContentLayoutItem* CXFA_ContentLayoutProcessor::FindLastContentLayoutItem(
+    XFA_AttributeValue eFlowStrategy) {
+  if (m_nCurChildNodeStage == Stage::kDone ||
+      eFlowStrategy == XFA_AttributeValue::Tb) {
+    return nullptr;
+  }
+
+  CXFA_ContentLayoutItem* pLastChild =
+      ToContentLayoutItem(m_pLayoutItem->GetFirstChild());
+  for (CXFA_LayoutItem* pNext = pLastChild; pNext;
+       pNext = pNext->GetNextSibling()) {
+    CXFA_ContentLayoutItem* pContentNext = pNext->AsContentLayoutItem();
+    if (pContentNext && pContentNext->m_sPos.y != pLastChild->m_sPos.y)
+      pLastChild = pContentNext;
+  }
+  return pLastChild;
+}
+
+CFX_SizeF CXFA_ContentLayoutProcessor::CalculateLayoutItemSize(
+    const CXFA_ContentLayoutItem* pLastChild) {
+  CFX_SizeF size;
+  for (CXFA_LayoutItem* pChild = m_pLayoutItem->GetFirstChild();
+       pChild != pLastChild; pChild = pChild->GetNextSibling()) {
+    CXFA_ContentLayoutItem* pLayout = pChild->AsContentLayoutItem();
+    if (!pLayout || !pLayout->GetFormNode()->PresenceRequiresSpace())
+      continue;
+
+    float fWidth = pLayout->m_sPos.x + pLayout->m_sSize.width;
+    float fHeight = pLayout->m_sPos.y + pLayout->m_sSize.height;
+    size.width = std::max(size.width, fWidth);
+    size.height = std::max(size.height, fHeight);
+  }
+  return size;
+}
+
+CXFA_ContentLayoutProcessor::Context::Context() = default;
+
+CXFA_ContentLayoutProcessor::Context::~Context() = default;
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.h b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.h
new file mode 100644
index 0000000..c4cfbe7
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.h
@@ -0,0 +1,230 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTPROCESSOR_H_
+#define XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTPROCESSOR_H_
+
+#include <float.h>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+constexpr float kXFALayoutPrecision = 0.0005f;
+
+class CXFA_ContentLayoutItem;
+class CXFA_ContentLayoutProcessor;
+class CXFA_LayoutProcessor;
+class CXFA_Node;
+class CXFA_ViewLayoutItem;
+class CXFA_ViewLayoutProcessor;
+
+class CXFA_ContentLayoutProcessor {
+ public:
+  enum class Result : uint8_t {
+    kDone,
+    kPageFullBreak,
+    kRowFullBreak,
+    kManualBreak,
+  };
+
+  enum class Stage : uint8_t {
+    kNone,
+    kBookendLeader,
+    kBreakBefore,
+    kKeep,
+    kContainer,
+    kBreakAfter,
+    kBookendTrailer,
+    kDone,
+  };
+
+  CXFA_ContentLayoutProcessor(CXFA_Node* pNode,
+                              CXFA_ViewLayoutProcessor* pViewLayoutProcessor);
+  ~CXFA_ContentLayoutProcessor();
+
+  Result DoLayout(bool bUseBreakControl, float fHeightLimit, float fRealHeight);
+  void DoLayoutPageArea(CXFA_ViewLayoutItem* pPageAreaLayoutItem);
+
+  CXFA_Node* GetFormNode() { return m_pFormNode; }
+  RetainPtr<CXFA_ContentLayoutItem> ExtractLayoutItem();
+
+ private:
+  class Context {
+   public:
+    Context();
+    ~Context();
+
+    Optional<float> m_fCurColumnWidth;
+    UnownedPtr<std::vector<float>> m_prgSpecifiedColumnWidths;
+    UnownedPtr<CXFA_ContentLayoutProcessor> m_pOverflowProcessor;
+    UnownedPtr<CXFA_Node> m_pOverflowNode;
+  };
+
+  Result DoLayoutInternal(bool bUseBreakControl,
+                          float fHeightLimit,
+                          float fRealHeight,
+                          Context* pContext);
+
+  CFX_SizeF GetCurrentComponentSize();
+  bool HasLayoutItem() const { return !!m_pLayoutItem; }
+  void SplitLayoutItem(float fSplitPos);
+  float FindSplitPos(float fProposedSplitPos);
+  bool ProcessKeepForSplit(
+      CXFA_ContentLayoutProcessor* pChildProcessor,
+      Result eRetValue,
+      std::vector<RetainPtr<CXFA_ContentLayoutItem>>* rgCurLineLayoutItem,
+      float* fContentCurRowAvailWidth,
+      float* fContentCurRowHeight,
+      float* fContentCurRowY,
+      bool* bAddedItemInRow,
+      bool* bForceEndPage,
+      Result* result);
+  void ProcessUnUseOverFlow(
+      CXFA_Node* pLeaderNode,
+      CXFA_Node* pTrailerNode,
+      const RetainPtr<CXFA_ContentLayoutItem>& pTrailerItem,
+      CXFA_Node* pFormNode);
+  bool IsAddNewRowForTrailer(CXFA_ContentLayoutItem* pTrailerItem);
+  bool JudgeLeaderOrTrailerForOccur(CXFA_Node* pFormNode);
+
+  RetainPtr<CXFA_ContentLayoutItem> CreateContentLayoutItem(
+      CXFA_Node* pFormNode);
+
+  void SetCurrentComponentPos(const CFX_PointF& pos);
+  void SetCurrentComponentSize(const CFX_SizeF& size);
+
+  void SplitLayoutItem(CXFA_ContentLayoutItem* pLayoutItem,
+                       CXFA_ContentLayoutItem* pSecondParent,
+                       float fSplitPos);
+  float InsertKeepLayoutItems();
+  bool CalculateRowChildPosition(
+      std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+      XFA_AttributeValue eFlowStrategy,
+      bool bContainerHeightAutoSize,
+      bool bContainerWidthAutoSize,
+      float* fContentCalculatedWidth,
+      float* fContentCalculatedHeight,
+      float* fContentCurRowY,
+      float fContentCurRowHeight,
+      float fContentWidthLimit,
+      bool bRootForceTb);
+  void ProcessUnUseBinds(CXFA_Node* pFormNode);
+  bool JudgePutNextPage(
+      CXFA_ContentLayoutItem* pParentLayoutItem,
+      float fChildHeight,
+      std::vector<RetainPtr<CXFA_ContentLayoutItem>>* pKeepItems);
+
+  void DoLayoutPositionedContainer(Context* pContext);
+  void DoLayoutTableContainer(CXFA_Node* pLayoutNode);
+  Result DoLayoutFlowedContainer(bool bUseBreakControl,
+                                 XFA_AttributeValue eFlowStrategy,
+                                 float fHeightLimit,
+                                 float fRealHeight,
+                                 Context* pContext,
+                                 bool bRootForceTb);
+  void DoLayoutField();
+
+  void GotoNextContainerNodeSimple(bool bUsePageBreak);
+  Stage GotoNextContainerNode(Stage nCurStage,
+                              bool bUsePageBreak,
+                              CXFA_Node* pParentContainer,
+                              CXFA_Node** pCurActionNode);
+
+  Optional<Stage> ProcessKeepNodesForCheckNext(CXFA_Node** pCurActionNode,
+                                               CXFA_Node** pNextContainer,
+                                               bool* pLastKeepNode);
+
+  Optional<Stage> ProcessKeepNodesForBreakBefore(CXFA_Node** pCurActionNode,
+                                                 CXFA_Node* pContainerNode);
+
+  CXFA_Node* GetSubformSetParent(CXFA_Node* pSubformSet);
+
+  void UpdatePendingItemLayout(
+      const RetainPtr<CXFA_ContentLayoutItem>& pLayoutItem);
+  void AddTrailerBeforeSplit(
+      float fSplitPos,
+      const RetainPtr<CXFA_ContentLayoutItem>& pTrailerLayoutItem,
+      bool bUseInherited);
+  void AddLeaderAfterSplit(
+      const RetainPtr<CXFA_ContentLayoutItem>& pLeaderLayoutItem);
+  void AddPendingNode(CXFA_Node* pPendingNode, bool bBreakPending);
+  float InsertPendingItems(CXFA_Node* pCurChildNode);
+  Result InsertFlowedItem(
+      CXFA_ContentLayoutProcessor* pProcessor,
+      bool bContainerWidthAutoSize,
+      bool bContainerHeightAutoSize,
+      float fContainerHeight,
+      XFA_AttributeValue eFlowStrategy,
+      uint8_t* uCurHAlignState,
+      std::vector<RetainPtr<CXFA_ContentLayoutItem>> (&rgCurLineLayoutItems)[3],
+      bool bUseBreakControl,
+      float fAvailHeight,
+      float fRealHeight,
+      float fContentWidthLimit,
+      float* fContentCurRowY,
+      float* fContentCurRowAvailWidth,
+      float* fContentCurRowHeight,
+      bool* bAddedItemInRow,
+      bool* bForceEndPage,
+      Context* pLayoutContext,
+      bool bNewRow);
+
+  Optional<Stage> HandleKeep(CXFA_Node* pBreakAfterNode,
+                             CXFA_Node** pCurActionNode);
+  Optional<Stage> HandleBookendLeader(CXFA_Node* pParentContainer,
+                                      CXFA_Node** pCurActionNode);
+  Optional<Stage> HandleBreakBefore(CXFA_Node* pChildContainer,
+                                    CXFA_Node** pCurActionNode);
+  Optional<Stage> HandleBreakAfter(CXFA_Node* pChildContainer,
+                                   CXFA_Node** pCurActionNode);
+  Optional<Stage> HandleCheckNextChildContainer(CXFA_Node* pParentContainer,
+                                                CXFA_Node* pChildContainer,
+                                                CXFA_Node** pCurActionNode);
+  Optional<Stage> HandleBookendTrailer(CXFA_Node* pParentContainer,
+                                       CXFA_Node** pCurActionNode);
+  void ProcessKeepNodesEnd();
+  void AdjustContainerSpecifiedSize(Context* pContext,
+                                    CFX_SizeF* pSize,
+                                    bool* pContainerWidthAutoSize,
+                                    bool* pContainerHeightAutoSize);
+  CXFA_ContentLayoutItem* FindLastContentLayoutItem(
+      XFA_AttributeValue eFlowStrategy);
+  CFX_SizeF CalculateLayoutItemSize(const CXFA_ContentLayoutItem* pLayoutChild);
+
+  Stage m_nCurChildNodeStage = Stage::kNone;
+  Result m_ePreProcessRs = Result::kDone;
+  bool m_bBreakPending = true;
+  bool m_bUseInherited = false;
+  bool m_bKeepBreakFinish = false;
+  bool m_bIsProcessKeep = false;
+  bool m_bHasAvailHeight = true;
+  float m_fUsedSize = 0;
+  float m_fLastRowWidth = 0;
+  float m_fLastRowY = 0;
+  float m_fWidthLimit = 0;
+  CXFA_Node* const m_pFormNode;
+  CXFA_Node* m_pCurChildNode = nullptr;
+  CXFA_Node* m_pKeepHeadNode = nullptr;
+  CXFA_Node* m_pKeepTailNode = nullptr;
+  RetainPtr<CXFA_ContentLayoutItem> m_pLayoutItem;
+  RetainPtr<CXFA_ContentLayoutItem> m_pOldLayoutItem;
+  UnownedPtr<CXFA_ViewLayoutProcessor> m_pViewLayoutProcessor;
+  std::vector<float> m_rgSpecifiedColumnWidths;
+  std::vector<RetainPtr<CXFA_ContentLayoutItem>> m_ArrayKeepItems;
+  std::list<CXFA_Node*> m_PendingNodes;
+  std::map<CXFA_Node*, int32_t> m_PendingNodesCount;
+  std::unique_ptr<CXFA_ContentLayoutProcessor> m_pCurChildPreprocessor;
+};
+
+#endif  // XFA_FXFA_LAYOUT_CXFA_CONTENTLAYOUTPROCESSOR_H_
diff --git a/xfa/fxfa/layout/cxfa_layoutitem.cpp b/xfa/fxfa/layout/cxfa_layoutitem.cpp
new file mode 100644
index 0000000..b4523c9
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_layoutitem.cpp
@@ -0,0 +1,77 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/layout/cxfa_layoutitem.h"
+
+#include <utility>
+
+#include "fxjs/xfa/cjx_object.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
+#include "xfa/fxfa/parser/cxfa_margin.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+
+void XFA_ReleaseLayoutItem(const RetainPtr<CXFA_LayoutItem>& pLayoutItem) {
+  RetainPtr<CXFA_LayoutItem> pNode(pLayoutItem->GetFirstChild());
+  while (pNode) {
+    RetainPtr<CXFA_LayoutItem> pNext(pNode->GetNextSibling());
+    XFA_ReleaseLayoutItem(pNode);
+    pNode = std::move(pNext);
+  }
+  CXFA_Document* pDocument = pLayoutItem->GetFormNode()->GetDocument();
+  CXFA_FFNotify* pNotify = pDocument->GetNotify();
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDocument);
+  pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem.Get());
+  if (pLayoutItem->GetFormNode()->GetElementType() == XFA_Element::PageArea) {
+    pNotify->OnPageEvent(ToViewLayoutItem(pLayoutItem.Get()),
+                         XFA_PAGEVIEWEVENT_PostRemoved);
+  }
+  pLayoutItem->RemoveSelfIfParented();
+}
+
+CXFA_LayoutItem::CXFA_LayoutItem(CXFA_Node* pNode, ItemType type)
+    : m_ItemType(type), m_pFormNode(pNode) {}
+
+CXFA_LayoutItem::~CXFA_LayoutItem() {
+  CHECK(!GetParent());
+  if (m_pFormNode) {
+    auto* pJSObj = m_pFormNode->JSObject();
+    if (pJSObj && pJSObj->GetLayoutItem() == this)
+      pJSObj->SetLayoutItem(nullptr);
+  }
+}
+
+CXFA_ViewLayoutItem* CXFA_LayoutItem::AsViewLayoutItem() {
+  return IsViewLayoutItem() ? static_cast<CXFA_ViewLayoutItem*>(this) : nullptr;
+}
+
+const CXFA_ViewLayoutItem* CXFA_LayoutItem::AsViewLayoutItem() const {
+  return IsViewLayoutItem() ? static_cast<const CXFA_ViewLayoutItem*>(this)
+                            : nullptr;
+}
+
+CXFA_ContentLayoutItem* CXFA_LayoutItem::AsContentLayoutItem() {
+  return IsContentLayoutItem() ? static_cast<CXFA_ContentLayoutItem*>(this)
+                               : nullptr;
+}
+
+const CXFA_ContentLayoutItem* CXFA_LayoutItem::AsContentLayoutItem() const {
+  return IsContentLayoutItem()
+             ? static_cast<const CXFA_ContentLayoutItem*>(this)
+             : nullptr;
+}
+
+const CXFA_ViewLayoutItem* CXFA_LayoutItem::GetPage() const {
+  for (CXFA_LayoutItem* pCurNode = const_cast<CXFA_LayoutItem*>(this); pCurNode;
+       pCurNode = pCurNode->GetParent()) {
+    if (pCurNode->m_pFormNode->GetElementType() == XFA_Element::PageArea)
+      return pCurNode->AsViewLayoutItem();
+  }
+  return nullptr;
+}
diff --git a/xfa/fxfa/layout/cxfa_layoutitem.h b/xfa/fxfa/layout/cxfa_layoutitem.h
new file mode 100644
index 0000000..034c145
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_layoutitem.h
@@ -0,0 +1,53 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_LAYOUT_CXFA_LAYOUTITEM_H_
+#define XFA_FXFA_LAYOUT_CXFA_LAYOUTITEM_H_
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/retained_tree_node.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+
+class CXFA_ContentLayoutItem;
+class CXFA_LayoutProcessor;
+class CXFA_ViewLayoutItem;
+
+class CXFA_LayoutItem : public RetainedTreeNode<CXFA_LayoutItem> {
+ public:
+  ~CXFA_LayoutItem() override;
+
+  bool IsViewLayoutItem() const { return m_ItemType == kViewItem; }
+  bool IsContentLayoutItem() const { return m_ItemType == kContentItem; }
+  CXFA_ViewLayoutItem* AsViewLayoutItem();
+  const CXFA_ViewLayoutItem* AsViewLayoutItem() const;
+  CXFA_ContentLayoutItem* AsContentLayoutItem();
+  const CXFA_ContentLayoutItem* AsContentLayoutItem() const;
+
+  const CXFA_ViewLayoutItem* GetPage() const;
+  CXFA_Node* GetFormNode() const { return m_pFormNode.Get(); }
+  void SetFormNode(CXFA_Node* pNode) { m_pFormNode = pNode; }
+
+ protected:
+  enum ItemType { kViewItem, kContentItem };
+  CXFA_LayoutItem(CXFA_Node* pNode, ItemType type);
+
+ private:
+  const ItemType m_ItemType;
+  UnownedPtr<CXFA_Node> m_pFormNode;
+};
+
+inline CXFA_ViewLayoutItem* ToViewLayoutItem(CXFA_LayoutItem* item) {
+  return item ? item->AsViewLayoutItem() : nullptr;
+}
+
+inline CXFA_ContentLayoutItem* ToContentLayoutItem(CXFA_LayoutItem* item) {
+  return item ? item->AsContentLayoutItem() : nullptr;
+}
+
+void XFA_ReleaseLayoutItem(const RetainPtr<CXFA_LayoutItem>& pLayoutItem);
+
+#endif  // XFA_FXFA_LAYOUT_CXFA_LAYOUTITEM_H_
diff --git a/xfa/fxfa/layout/cxfa_layoutitem_embeddertest.cpp b/xfa/fxfa/layout/cxfa_layoutitem_embeddertest.cpp
new file mode 100644
index 0000000..0ba54ef
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_layoutitem_embeddertest.cpp
@@ -0,0 +1,50 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CXFALayoutItemEmbedderTest : public EmbedderTest {};
+
+#if defined(LEAK_SANITIZER)
+
+// Leaks. See https://crbug.com/pdfium/1301
+#define MAYBE_Bug_1301 DISABLED_Bug_1301
+
+#else
+#define MAYBE_Bug_1301 Bug_1301
+#endif
+
+TEST_F(CXFALayoutItemEmbedderTest, Bug_1265) {
+  EXPECT_TRUE(OpenDocument("bug_1265.pdf"));
+  FPDF_PAGE page0 = LoadPage(0);
+  FPDF_PAGE page1 = LoadPage(1);
+  EXPECT_NE(nullptr, page0);
+  EXPECT_EQ(nullptr, page1);
+  UnloadPage(page0);
+}
+
+TEST_F(CXFALayoutItemEmbedderTest, MAYBE_Bug_1301) {
+  EXPECT_TRUE(OpenDocument("bug_1301.pdf"));
+  FPDF_PAGE page0 = LoadPage(0);
+  FPDF_PAGE page1 = LoadPage(1);
+  FPDF_PAGE page2 = LoadPage(2);
+  EXPECT_NE(nullptr, page0);
+  EXPECT_NE(nullptr, page1);
+  EXPECT_EQ(nullptr, page2);
+  UnloadPage(page0);
+  UnloadPage(page1);
+}
+
+TEST_F(CXFALayoutItemEmbedderTest, Bug_306123) {
+  EXPECT_TRUE(OpenDocument("bug_306123.pdf"));
+  FPDF_PAGE page0 = LoadPage(0);
+  FPDF_PAGE page1 = LoadPage(1);
+  FPDF_PAGE page2 = LoadPage(2);
+  EXPECT_NE(nullptr, page0);
+  EXPECT_NE(nullptr, page1);
+  EXPECT_EQ(nullptr, page2);
+  UnloadPage(page0);
+  UnloadPage(page1);
+}
diff --git a/xfa/fxfa/layout/cxfa_layoutprocessor.cpp b/xfa/fxfa/layout/cxfa_layoutprocessor.cpp
new file mode 100644
index 0000000..bb38fbe
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_layoutprocessor.cpp
@@ -0,0 +1,133 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
+
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_subform.h"
+#include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+// static
+CXFA_LayoutProcessor* CXFA_LayoutProcessor::FromDocument(
+    const CXFA_Document* pXFADoc) {
+  return static_cast<CXFA_LayoutProcessor*>(pXFADoc->GetLayoutProcessor());
+}
+
+CXFA_LayoutProcessor::CXFA_LayoutProcessor() = default;
+
+CXFA_LayoutProcessor::~CXFA_LayoutProcessor() = default;
+
+void CXFA_LayoutProcessor::SetForceRelayout(bool bForceRestart) {
+  m_bNeedLayout = bForceRestart;
+}
+
+int32_t CXFA_LayoutProcessor::StartLayout(bool bForceRestart) {
+  if (!bForceRestart && !NeedLayout())
+    return 100;
+
+  m_pContentLayoutProcessor.reset();
+  m_nProgressCounter = 0;
+  CXFA_Node* pFormPacketNode =
+      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form));
+  if (!pFormPacketNode)
+    return -1;
+
+  CXFA_Subform* pFormRoot =
+      pFormPacketNode->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
+  if (!pFormRoot)
+    return -1;
+
+  if (!m_pViewLayoutProcessor)
+    m_pViewLayoutProcessor = pdfium::MakeUnique<CXFA_ViewLayoutProcessor>(this);
+  if (!m_pViewLayoutProcessor->InitLayoutPage(pFormRoot))
+    return -1;
+
+  if (!m_pViewLayoutProcessor->PrepareFirstPage(pFormRoot))
+    return -1;
+
+  m_pContentLayoutProcessor = pdfium::MakeUnique<CXFA_ContentLayoutProcessor>(
+      pFormRoot, m_pViewLayoutProcessor.get());
+  m_nProgressCounter = 1;
+  return 0;
+}
+
+int32_t CXFA_LayoutProcessor::DoLayout() {
+  if (m_nProgressCounter < 1)
+    return -1;
+
+  CXFA_ContentLayoutProcessor::Result eStatus;
+  CXFA_Node* pFormNode = m_pContentLayoutProcessor->GetFormNode();
+  float fPosX =
+      pFormNode->JSObject()->GetMeasureInUnit(XFA_Attribute::X, XFA_Unit::Pt);
+  float fPosY =
+      pFormNode->JSObject()->GetMeasureInUnit(XFA_Attribute::Y, XFA_Unit::Pt);
+  do {
+    float fAvailHeight = m_pViewLayoutProcessor->GetAvailHeight();
+    eStatus =
+        m_pContentLayoutProcessor->DoLayout(true, fAvailHeight, fAvailHeight);
+    if (eStatus != CXFA_ContentLayoutProcessor::Result::kDone)
+      m_nProgressCounter++;
+
+    RetainPtr<CXFA_ContentLayoutItem> pLayoutItem =
+        m_pContentLayoutProcessor->ExtractLayoutItem();
+    if (pLayoutItem)
+      pLayoutItem->m_sPos = CFX_PointF(fPosX, fPosY);
+
+    m_pViewLayoutProcessor->SubmitContentItem(pLayoutItem, eStatus);
+  } while (eStatus != CXFA_ContentLayoutProcessor::Result::kDone);
+
+  if (eStatus == CXFA_ContentLayoutProcessor::Result::kDone) {
+    m_pViewLayoutProcessor->FinishPaginatedPageSets();
+    m_pViewLayoutProcessor->SyncLayoutData();
+    m_bNeedLayout = false;
+    m_rgChangedContainers.clear();
+  }
+  return 100 *
+         (eStatus == CXFA_ContentLayoutProcessor::Result::kDone
+              ? m_nProgressCounter
+              : m_nProgressCounter - 1) /
+         m_nProgressCounter;
+}
+
+bool CXFA_LayoutProcessor::IncrementLayout() {
+  if (m_bNeedLayout) {
+    StartLayout(true);
+    return DoLayout() == 100;
+  }
+  return m_rgChangedContainers.empty();
+}
+
+int32_t CXFA_LayoutProcessor::CountPages() const {
+  return m_pViewLayoutProcessor ? m_pViewLayoutProcessor->GetPageCount() : 0;
+}
+
+CXFA_ViewLayoutItem* CXFA_LayoutProcessor::GetPage(int32_t index) const {
+  return m_pViewLayoutProcessor ? m_pViewLayoutProcessor->GetPage(index)
+                                : nullptr;
+}
+
+CXFA_LayoutItem* CXFA_LayoutProcessor::GetLayoutItem(CXFA_Node* pFormItem) {
+  return pFormItem->JSObject()->GetLayoutItem();
+}
+
+void CXFA_LayoutProcessor::AddChangedContainer(CXFA_Node* pContainer) {
+  if (!pdfium::ContainsValue(m_rgChangedContainers, pContainer))
+    m_rgChangedContainers.push_back(pContainer);
+}
+
+bool CXFA_LayoutProcessor::NeedLayout() const {
+  return m_bNeedLayout || !m_rgChangedContainers.empty();
+}
diff --git a/xfa/fxfa/layout/cxfa_layoutprocessor.h b/xfa/fxfa/layout/cxfa_layoutprocessor.h
new file mode 100644
index 0000000..18dc208
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_layoutprocessor.h
@@ -0,0 +1,57 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_LAYOUT_CXFA_LAYOUTPROCESSOR_H_
+#define XFA_FXFA_LAYOUT_CXFA_LAYOUTPROCESSOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+
+class CXFA_ContentLayoutProcessor;
+class CXFA_LayoutItem;
+class CXFA_Node;
+class CXFA_ViewLayoutItem;
+class CXFA_ViewLayoutProcessor;
+
+class CXFA_LayoutProcessor : public CXFA_Document::LayoutProcessorIface {
+ public:
+  static CXFA_LayoutProcessor* FromDocument(const CXFA_Document* pXFADoc);
+
+  CXFA_LayoutProcessor();
+  ~CXFA_LayoutProcessor() override;
+
+  // CXFA_Document::LayoutProcessorIface:
+  void SetForceRelayout(bool bForceRestart) override;
+  void AddChangedContainer(CXFA_Node* pContainer) override;
+
+  int32_t StartLayout(bool bForceRestart);
+  int32_t DoLayout();
+  bool IncrementLayout();
+  int32_t CountPages() const;
+  CXFA_ViewLayoutItem* GetPage(int32_t index) const;
+  CXFA_LayoutItem* GetLayoutItem(CXFA_Node* pFormItem);
+  CXFA_ContentLayoutProcessor* GetRootContentLayoutProcessor() const {
+    return m_pContentLayoutProcessor.get();
+  }
+  CXFA_ViewLayoutProcessor* GetLayoutPageMgr() const {
+    return m_pViewLayoutProcessor.get();
+  }
+
+ private:
+  bool NeedLayout() const;
+
+  std::unique_ptr<CXFA_ViewLayoutProcessor> m_pViewLayoutProcessor;
+  std::unique_ptr<CXFA_ContentLayoutProcessor> m_pContentLayoutProcessor;
+  std::vector<CXFA_Node*> m_rgChangedContainers;
+  uint32_t m_nProgressCounter = 0;
+  bool m_bNeedLayout = true;
+};
+
+#endif  // XFA_FXFA_LAYOUT_CXFA_LAYOUTPROCESSOR_H_
diff --git a/xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h b/xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h
new file mode 100644
index 0000000..f71d708
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h
@@ -0,0 +1,32 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_LAYOUT_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
+#define XFA_FXFA_LAYOUT_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
+
+#include "core/fxcrt/retain_ptr.h"
+#include "xfa/fxfa/layout/cxfa_layoutitem.h"
+#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
+
+class CXFA_TraverseStrategy_LayoutItem {
+ public:
+  static CXFA_LayoutItem* GetFirstChild(CXFA_LayoutItem* pLayoutItem) {
+    return pLayoutItem->GetFirstChild();
+  }
+  static CXFA_LayoutItem* GetNextSibling(CXFA_LayoutItem* pLayoutItem) {
+    return pLayoutItem->GetNextSibling();
+  }
+  static CXFA_LayoutItem* GetParent(CXFA_LayoutItem* pLayoutItem) {
+    return pLayoutItem->GetParent();
+  }
+};
+
+using CXFA_LayoutItemIterator =
+    CXFA_NodeIteratorTemplate<CXFA_LayoutItem,
+                              CXFA_TraverseStrategy_LayoutItem,
+                              RetainPtr<CXFA_LayoutItem>>;
+
+#endif  // XFA_FXFA_LAYOUT_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutitem.cpp b/xfa/fxfa/layout/cxfa_viewlayoutitem.cpp
new file mode 100644
index 0000000..fcef2ab
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_viewlayoutitem.cpp
@@ -0,0 +1,61 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
+
+#include <utility>
+
+#include "fxjs/xfa/cjx_object.h"
+#include "xfa/fxfa/cxfa_ffpageview.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_medium.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+
+CXFA_ViewLayoutItem::CXFA_ViewLayoutItem(
+    CXFA_Node* pNode,
+    std::unique_ptr<CXFA_FFPageView> pPageView)
+    : CXFA_LayoutItem(pNode, kViewItem), m_pFFPageView(std::move(pPageView)) {
+  if (m_pFFPageView)
+    m_pFFPageView->SetLayoutItem(this);
+}
+
+CXFA_ViewLayoutItem::~CXFA_ViewLayoutItem() {
+  if (m_pFFPageView)
+    m_pFFPageView->SetLayoutItem(nullptr);
+}
+
+CXFA_LayoutProcessor* CXFA_ViewLayoutItem::GetLayout() const {
+  return CXFA_LayoutProcessor::FromDocument(GetFormNode()->GetDocument());
+}
+
+int32_t CXFA_ViewLayoutItem::GetPageIndex() const {
+  auto* pLayout =
+      CXFA_LayoutProcessor::FromDocument(GetFormNode()->GetDocument());
+  return pLayout->GetLayoutPageMgr()->GetPageIndex(this);
+}
+
+CFX_SizeF CXFA_ViewLayoutItem::GetPageSize() const {
+  CFX_SizeF size;
+  CXFA_Medium* pMedium =
+      GetFormNode()->GetFirstChildByClass<CXFA_Medium>(XFA_Element::Medium);
+  if (!pMedium)
+    return size;
+
+  size = CFX_SizeF(
+      pMedium->JSObject()->GetMeasureInUnit(XFA_Attribute::Short, XFA_Unit::Pt),
+      pMedium->JSObject()->GetMeasureInUnit(XFA_Attribute::Long, XFA_Unit::Pt));
+  if (pMedium->JSObject()->GetEnum(XFA_Attribute::Orientation) ==
+      XFA_AttributeValue::Landscape) {
+    size = CFX_SizeF(size.height, size.width);
+  }
+  return size;
+}
+
+CXFA_Node* CXFA_ViewLayoutItem::GetMasterPage() const {
+  return GetFormNode();
+}
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutitem.h b/xfa/fxfa/layout/cxfa_viewlayoutitem.h
new file mode 100644
index 0000000..1c9f77c
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_viewlayoutitem.h
@@ -0,0 +1,38 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTITEM_H_
+#define XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTITEM_H_
+
+#include <memory>
+
+#include "xfa/fxfa/layout/cxfa_layoutitem.h"
+
+class CXFA_FFPageView;
+
+class CXFA_ViewLayoutItem : public CXFA_LayoutItem {
+ public:
+  template <typename T, typename... Args>
+  friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+  ~CXFA_ViewLayoutItem() override;
+
+  CXFA_FFPageView* GetPageView() const { return m_pFFPageView.get(); }
+  CXFA_LayoutProcessor* GetLayout() const;
+  int32_t GetPageIndex() const;
+  CFX_SizeF GetPageSize() const;
+  CXFA_Node* GetMasterPage() const;
+
+  UnownedPtr<CXFA_Node> m_pOldSubform;
+
+ private:
+  CXFA_ViewLayoutItem(CXFA_Node* pNode,
+                      std::unique_ptr<CXFA_FFPageView> pPageView);
+
+  std::unique_ptr<CXFA_FFPageView> const m_pFFPageView;
+};
+
+#endif  // XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTITEM_H_
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp
new file mode 100644
index 0000000..dcfdd47
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp
@@ -0,0 +1,2005 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/layout/cxfa_viewlayoutprocessor.h"
+
+#include <utility>
+
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/cxfa_ffpageview.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h"
+#include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h"
+#include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
+#include "xfa/fxfa/parser/cxfa_contentarea.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_medium.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
+#include "xfa/fxfa/parser/cxfa_object.h"
+#include "xfa/fxfa/parser/cxfa_occur.h"
+#include "xfa/fxfa/parser/cxfa_pageset.h"
+#include "xfa/fxfa/parser/cxfa_subform.h"
+#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
+#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
+#include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
+#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+
+namespace {
+
+class TraverseStrategy_ViewLayoutItem {
+ public:
+  static CXFA_ViewLayoutItem* GetFirstChild(CXFA_ViewLayoutItem* pLayoutItem) {
+    for (CXFA_LayoutItem* pChildItem = pLayoutItem->GetFirstChild(); pChildItem;
+         pChildItem = pChildItem->GetNextSibling()) {
+      if (CXFA_ViewLayoutItem* pContainer = pChildItem->AsViewLayoutItem()) {
+        return pContainer;
+      }
+    }
+    return nullptr;
+  }
+
+  static CXFA_ViewLayoutItem* GetNextSibling(CXFA_ViewLayoutItem* pLayoutItem) {
+    for (CXFA_LayoutItem* pChildItem = pLayoutItem->GetNextSibling();
+         pChildItem; pChildItem = pChildItem->GetNextSibling()) {
+      if (CXFA_ViewLayoutItem* pContainer = pChildItem->AsViewLayoutItem()) {
+        return pContainer;
+      }
+    }
+    return nullptr;
+  }
+
+  static CXFA_ViewLayoutItem* GetParent(CXFA_ViewLayoutItem* pLayoutItem) {
+    return ToViewLayoutItem(pLayoutItem->GetParent());
+  }
+};
+
+using ViewLayoutItemIterator =
+    CXFA_NodeIteratorTemplate<CXFA_ViewLayoutItem,
+                              TraverseStrategy_ViewLayoutItem>;
+
+class TraverseStrategy_PageSet {
+ public:
+  static CXFA_ViewLayoutItem* GetFirstChild(CXFA_ViewLayoutItem* pLayoutItem) {
+    if (pLayoutItem->GetFormNode()->GetElementType() != XFA_Element::PageSet)
+      return nullptr;
+
+    for (CXFA_LayoutItem* pChildItem = pLayoutItem->GetFirstChild(); pChildItem;
+         pChildItem = pChildItem->GetNextSibling()) {
+      CXFA_ViewLayoutItem* pContainer = pChildItem->AsViewLayoutItem();
+      if (pContainer &&
+          pContainer->GetFormNode()->GetElementType() == XFA_Element::PageSet) {
+        return pContainer;
+      }
+    }
+    return nullptr;
+  }
+
+  static CXFA_ViewLayoutItem* GetNextSibling(CXFA_ViewLayoutItem* pLayoutItem) {
+    for (CXFA_LayoutItem* pChildItem = pLayoutItem->GetNextSibling();
+         pChildItem; pChildItem = pChildItem->GetNextSibling()) {
+      CXFA_ViewLayoutItem* pContainer = pChildItem->AsViewLayoutItem();
+      if (pContainer &&
+          pContainer->GetFormNode()->GetElementType() == XFA_Element::PageSet) {
+        return pContainer;
+      }
+    }
+    return nullptr;
+  }
+
+  static CXFA_ViewLayoutItem* GetParent(CXFA_ViewLayoutItem* pLayoutItem) {
+    return ToViewLayoutItem(pLayoutItem->GetParent());
+  }
+};
+
+using PageSetIterator =
+    CXFA_NodeIteratorTemplate<CXFA_ViewLayoutItem, TraverseStrategy_PageSet>;
+
+uint32_t GetRelevant(CXFA_Node* pFormItem, uint32_t dwParentRelvant) {
+  uint32_t dwRelevant = XFA_WidgetStatus_Viewable | XFA_WidgetStatus_Printable;
+  WideString wsRelevant =
+      pFormItem->JSObject()->GetCData(XFA_Attribute::Relevant);
+  if (!wsRelevant.IsEmpty()) {
+    if (wsRelevant.EqualsASCII("+print") || wsRelevant.EqualsASCII("print"))
+      dwRelevant &= ~XFA_WidgetStatus_Viewable;
+    else if (wsRelevant.EqualsASCII("-print"))
+      dwRelevant &= ~XFA_WidgetStatus_Printable;
+  }
+  if (!(dwParentRelvant & XFA_WidgetStatus_Viewable) &&
+      (dwRelevant != XFA_WidgetStatus_Viewable)) {
+    dwRelevant &= ~XFA_WidgetStatus_Viewable;
+  }
+  if (!(dwParentRelvant & XFA_WidgetStatus_Printable) &&
+      (dwRelevant != XFA_WidgetStatus_Printable)) {
+    dwRelevant &= ~XFA_WidgetStatus_Printable;
+  }
+  return dwRelevant;
+}
+
+void SyncContainer(CXFA_FFNotify* pNotify,
+                   CXFA_LayoutProcessor* pDocLayout,
+                   CXFA_LayoutItem* pViewItem,
+                   uint32_t dwRelevant,
+                   bool bVisible,
+                   int32_t nPageIndex) {
+  bool bVisibleItem = false;
+  uint32_t dwStatus = 0;
+  uint32_t dwRelevantContainer = 0;
+  if (bVisible) {
+    XFA_AttributeValue eAttributeValue =
+        pViewItem->GetFormNode()
+            ->JSObject()
+            ->TryEnum(XFA_Attribute::Presence, true)
+            .value_or(XFA_AttributeValue::Visible);
+    if (eAttributeValue == XFA_AttributeValue::Visible)
+      bVisibleItem = true;
+
+    dwRelevantContainer = GetRelevant(pViewItem->GetFormNode(), dwRelevant);
+    dwStatus =
+        (bVisibleItem ? XFA_WidgetStatus_Visible : 0) | dwRelevantContainer;
+  }
+  pNotify->OnLayoutItemAdded(pDocLayout, pViewItem, nPageIndex, dwStatus);
+  for (CXFA_LayoutItem* pChild = pViewItem->GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    if (pChild->IsContentLayoutItem()) {
+      SyncContainer(pNotify, pDocLayout, pChild, dwRelevantContainer,
+                    bVisibleItem, nPageIndex);
+    }
+  }
+}
+
+void ReorderLayoutItemToTail(const RetainPtr<CXFA_LayoutItem>& pLayoutItem) {
+  CXFA_LayoutItem* pParentLayoutItem = pLayoutItem->GetParent();
+  if (!pParentLayoutItem)
+    return;
+
+  pParentLayoutItem->RemoveChild(pLayoutItem);
+  pParentLayoutItem->AppendLastChild(pLayoutItem);
+}
+
+CXFA_Node* ResolveBreakTarget(CXFA_Node* pPageSetRoot,
+                              bool bNewExprStyle,
+                              WideString* pTargetAll) {
+  if (!pPageSetRoot)
+    return nullptr;
+
+  CXFA_Document* pDocument = pPageSetRoot->GetDocument();
+  if (pTargetAll->IsEmpty())
+    return nullptr;
+
+  pTargetAll->Trim();
+  int32_t iSplitIndex = 0;
+  bool bTargetAllFind = true;
+  while (iSplitIndex != -1) {
+    WideString wsExpr;
+    Optional<size_t> iSplitNextIndex = 0;
+    if (!bTargetAllFind) {
+      iSplitNextIndex = pTargetAll->Find(' ', iSplitIndex);
+      if (!iSplitNextIndex.has_value())
+        return nullptr;
+      wsExpr = pTargetAll->Substr(iSplitIndex,
+                                  iSplitNextIndex.value() - iSplitIndex);
+    } else {
+      wsExpr = *pTargetAll;
+    }
+    if (wsExpr.IsEmpty())
+      return nullptr;
+
+    bTargetAllFind = false;
+    if (wsExpr[0] == '#') {
+      CXFA_Node* pNode = pDocument->GetNodeByID(
+          ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Template)),
+          wsExpr.Last(wsExpr.GetLength() - 1).AsStringView());
+      if (pNode)
+        return pNode;
+    } else if (bNewExprStyle) {
+      WideString wsProcessedTarget = wsExpr;
+      if (wsExpr.First(4).EqualsASCII("som(") && wsExpr.Back() == L')')
+        wsProcessedTarget = wsExpr.Substr(4, wsExpr.GetLength() - 5);
+
+      XFA_RESOLVENODE_RS rs;
+      bool bRet = pDocument->GetScriptContext()->ResolveObjects(
+          pPageSetRoot, wsProcessedTarget.AsStringView(), &rs,
+          XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
+              XFA_RESOLVENODE_Attributes | XFA_RESOLVENODE_Siblings |
+              XFA_RESOLVENODE_Parent,
+          nullptr);
+      if (bRet && rs.objects.front()->IsNode())
+        return rs.objects.front()->AsNode();
+    }
+    iSplitIndex = iSplitNextIndex.value();
+  }
+  return nullptr;
+}
+
+void SetLayoutGeneratedNodeFlag(CXFA_Node* pNode) {
+  pNode->SetFlag(XFA_NodeFlag_LayoutGeneratedNode);
+  pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+}
+
+// Note: Returning nullptr is not the same as returning pdfium::nullopt.
+Optional<CXFA_ViewLayoutItem*> CheckContentAreaNotUsed(
+    CXFA_ViewLayoutItem* pPageAreaLayoutItem,
+    CXFA_Node* pContentArea) {
+  for (CXFA_LayoutItem* pChild = pPageAreaLayoutItem->GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    CXFA_ViewLayoutItem* pLayoutItem = pChild->AsViewLayoutItem();
+    if (pLayoutItem && pLayoutItem->GetFormNode() == pContentArea) {
+      if (!pLayoutItem->GetFirstChild())
+        return pLayoutItem;
+      return pdfium::nullopt;
+    }
+  }
+  return nullptr;
+}
+
+void SyncRemoveLayoutItem(CXFA_LayoutItem* pLayoutItem,
+                          CXFA_FFNotify* pNotify,
+                          CXFA_LayoutProcessor* pDocLayout) {
+  RetainPtr<CXFA_LayoutItem> pCurLayoutItem(pLayoutItem->GetFirstChild());
+  while (pCurLayoutItem) {
+    RetainPtr<CXFA_LayoutItem> pNextLayoutItem(
+        pCurLayoutItem->GetNextSibling());
+    SyncRemoveLayoutItem(pCurLayoutItem.Get(), pNotify, pDocLayout);
+    pCurLayoutItem = std::move(pNextLayoutItem);
+  }
+  pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
+  pLayoutItem->RemoveSelfIfParented();
+}
+
+bool RunBreakTestScript(CXFA_Script* pTestScript) {
+  WideString wsExpression = pTestScript->JSObject()->GetContent(false);
+  if (wsExpression.IsEmpty())
+    return true;
+  return pTestScript->GetDocument()->GetNotify()->RunScript(
+      pTestScript, pTestScript->GetContainerParent());
+}
+
+float CalculateLayoutItemHeight(const CXFA_LayoutItem* pItem) {
+  float fHeight = 0;
+  for (const CXFA_LayoutItem* pChild = pItem->GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    const CXFA_ContentLayoutItem* pContent = pChild->AsContentLayoutItem();
+    if (pContent)
+      fHeight += pContent->m_sSize.height;
+  }
+  return fHeight;
+}
+
+std::vector<float> GetHeightsForContentAreas(const CXFA_LayoutItem* pItem) {
+  std::vector<float> heights;
+  for (const CXFA_LayoutItem* pChild = pItem->GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    if (pChild->GetFormNode()->GetElementType() == XFA_Element::ContentArea)
+      heights.push_back(CalculateLayoutItemHeight(pChild));
+  }
+  return heights;
+}
+
+std::pair<size_t, CXFA_LayoutItem*> GetPageAreaCountAndLastPageAreaFromPageSet(
+    CXFA_ViewLayoutItem* pPageSetLayoutItem) {
+  size_t nCount = 0;
+  CXFA_LayoutItem* pLast = nullptr;
+  for (CXFA_LayoutItem* pPageAreaLayoutItem =
+           pPageSetLayoutItem->GetFirstChild();
+       pPageAreaLayoutItem;
+       pPageAreaLayoutItem = pPageAreaLayoutItem->GetNextSibling()) {
+    XFA_Element type = pPageAreaLayoutItem->GetFormNode()->GetElementType();
+    if (type != XFA_Element::PageArea)
+      continue;
+
+    ++nCount;
+    pLast = pPageAreaLayoutItem;
+  }
+  return {nCount, pLast};
+}
+
+bool ContentAreasFitInPageAreas(const CXFA_Node* pNode,
+                                const std::vector<float>& rgUsedHeights) {
+  size_t iCurContentAreaIndex = 0;
+  for (const CXFA_Node* pContentAreaNode = pNode->GetFirstChild();
+       pContentAreaNode;
+       pContentAreaNode = pContentAreaNode->GetNextSibling()) {
+    if (pContentAreaNode->GetElementType() != XFA_Element::ContentArea)
+      continue;
+
+    if (iCurContentAreaIndex >= rgUsedHeights.size())
+      return false;
+
+    const float fHeight = pContentAreaNode->JSObject()->GetMeasureInUnit(
+                              XFA_Attribute::H, XFA_Unit::Pt) +
+                          kXFALayoutPrecision;
+    if (rgUsedHeights[iCurContentAreaIndex] > fHeight)
+      return false;
+
+    ++iCurContentAreaIndex;
+  }
+  return true;
+}
+
+}  // namespace
+
+CXFA_ViewLayoutProcessor::CXFA_ViewRecord::CXFA_ViewRecord() = default;
+
+CXFA_ViewLayoutProcessor::CXFA_ViewRecord::~CXFA_ViewRecord() = default;
+
+CXFA_ViewLayoutProcessor::CXFA_ViewLayoutProcessor(
+    CXFA_LayoutProcessor* pLayoutProcessor)
+    : m_pLayoutProcessor(pLayoutProcessor),
+      m_CurrentViewRecordIter(m_ProposedViewRecords.end()) {}
+
+CXFA_ViewLayoutProcessor::~CXFA_ViewLayoutProcessor() {
+  ClearData();
+  RetainPtr<CXFA_LayoutItem> pLayoutItem(GetRootLayoutItem());
+  while (pLayoutItem) {
+    CXFA_LayoutItem* pNextLayout = pLayoutItem->GetNextSibling();
+    XFA_ReleaseLayoutItem(pLayoutItem);
+    pLayoutItem.Reset(pNextLayout);
+  }
+}
+
+bool CXFA_ViewLayoutProcessor::InitLayoutPage(CXFA_Node* pFormNode) {
+  PrepareLayout();
+  CXFA_Node* pTemplateNode = pFormNode->GetTemplateNodeIfExists();
+  if (!pTemplateNode)
+    return false;
+
+  m_pPageSetNode = pTemplateNode->JSObject()->GetOrCreateProperty<CXFA_PageSet>(
+      0, XFA_Element::PageSet);
+  ASSERT(m_pPageSetNode);
+
+  if (m_pPageSetRootLayoutItem) {
+    m_pPageSetRootLayoutItem->RemoveSelfIfParented();
+  } else {
+    m_pPageSetRootLayoutItem =
+        pdfium::MakeRetain<CXFA_ViewLayoutItem>(m_pPageSetNode, nullptr);
+  }
+  m_pPageSetCurLayoutItem = m_pPageSetRootLayoutItem;
+  m_pPageSetNode->JSObject()->SetLayoutItem(m_pPageSetRootLayoutItem.Get());
+
+  XFA_AttributeValue eRelation =
+      m_pPageSetNode->JSObject()->GetEnum(XFA_Attribute::Relation);
+  if (eRelation != XFA_AttributeValue::Unknown)
+    m_ePageSetMode = eRelation;
+
+  InitPageSetMap();
+  CXFA_Node* pPageArea = nullptr;
+  int32_t iCount = 0;
+  for (pPageArea = m_pPageSetNode->GetFirstChild(); pPageArea;
+       pPageArea = pPageArea->GetNextSibling()) {
+    if (pPageArea->GetElementType() != XFA_Element::PageArea)
+      continue;
+
+    iCount++;
+    if (pPageArea->GetFirstChildByClass<CXFA_ContentArea>(
+            XFA_Element::ContentArea)) {
+      return true;
+    }
+  }
+  if (iCount > 0)
+    return false;
+
+  CXFA_Document* pDocument = pTemplateNode->GetDocument();
+  pPageArea =
+      m_pPageSetNode->GetChild<CXFA_Node>(0, XFA_Element::PageArea, false);
+  if (!pPageArea) {
+    pPageArea = pDocument->CreateNode(m_pPageSetNode->GetPacketType(),
+                                      XFA_Element::PageArea);
+    if (!pPageArea)
+      return false;
+
+    m_pPageSetNode->InsertChildAndNotify(pPageArea, nullptr);
+    pPageArea->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+  }
+  CXFA_ContentArea* pContentArea =
+      pPageArea->GetChild<CXFA_ContentArea>(0, XFA_Element::ContentArea, false);
+  if (!pContentArea) {
+    pContentArea = static_cast<CXFA_ContentArea*>(pDocument->CreateNode(
+        pPageArea->GetPacketType(), XFA_Element::ContentArea));
+    if (!pContentArea)
+      return false;
+
+    pPageArea->InsertChildAndNotify(pContentArea, nullptr);
+    pContentArea->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pContentArea->JSObject()->SetMeasure(
+        XFA_Attribute::X, CXFA_Measurement(0.25f, XFA_Unit::In), false);
+    pContentArea->JSObject()->SetMeasure(
+        XFA_Attribute::Y, CXFA_Measurement(0.25f, XFA_Unit::In), false);
+    pContentArea->JSObject()->SetMeasure(
+        XFA_Attribute::W, CXFA_Measurement(8.0f, XFA_Unit::In), false);
+    pContentArea->JSObject()->SetMeasure(
+        XFA_Attribute::H, CXFA_Measurement(10.5f, XFA_Unit::In), false);
+  }
+  CXFA_Medium* pMedium =
+      pPageArea->GetChild<CXFA_Medium>(0, XFA_Element::Medium, false);
+  if (!pMedium) {
+    pMedium = static_cast<CXFA_Medium*>(
+        pDocument->CreateNode(pPageArea->GetPacketType(), XFA_Element::Medium));
+    if (!pContentArea)
+      return false;
+
+    pPageArea->InsertChildAndNotify(pMedium, nullptr);
+    pMedium->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+    pMedium->JSObject()->SetMeasure(
+        XFA_Attribute::Short, CXFA_Measurement(8.5f, XFA_Unit::In), false);
+    pMedium->JSObject()->SetMeasure(
+        XFA_Attribute::Long, CXFA_Measurement(11.0f, XFA_Unit::In), false);
+  }
+  return true;
+}
+
+bool CXFA_ViewLayoutProcessor::PrepareFirstPage(CXFA_Node* pRootSubform) {
+  bool bProBreakBefore = false;
+  const CXFA_Node* pBreakBeforeNode = nullptr;
+  while (pRootSubform) {
+    for (const CXFA_Node* pBreakNode = pRootSubform->GetFirstChild();
+         pBreakNode; pBreakNode = pBreakNode->GetNextSibling()) {
+      XFA_Element eType = pBreakNode->GetElementType();
+      if (eType == XFA_Element::BreakBefore ||
+          (eType == XFA_Element::Break &&
+           pBreakNode->JSObject()->GetEnum(XFA_Attribute::Before) !=
+               XFA_AttributeValue::Auto)) {
+        bProBreakBefore = true;
+        pBreakBeforeNode = pBreakNode;
+        break;
+      }
+    }
+    if (bProBreakBefore)
+      break;
+
+    bProBreakBefore = true;
+    pRootSubform =
+        pRootSubform->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
+    while (pRootSubform && !pRootSubform->PresenceRequiresSpace()) {
+      pRootSubform = pRootSubform->GetNextSameClassSibling<CXFA_Subform>(
+          XFA_Element::Subform);
+    }
+  }
+  if (pBreakBeforeNode) {
+    BreakData ret = ExecuteBreakBeforeOrAfter(pBreakBeforeNode, true);
+    if (ret.bCreatePage) {
+      ResetToFirstViewRecord();
+      return true;
+    }
+  }
+  return AppendNewPage(true);
+}
+
+bool CXFA_ViewLayoutProcessor::AppendNewPage(bool bFirstTemPage) {
+  if (m_CurrentViewRecordIter != GetTailPosition())
+    return true;
+
+  CXFA_Node* pPageNode = GetNextAvailPageArea(nullptr, nullptr, false, false);
+  if (!pPageNode)
+    return false;
+
+  if (bFirstTemPage && !HasCurrentViewRecord())
+    ResetToFirstViewRecord();
+  return !bFirstTemPage || HasCurrentViewRecord();
+}
+
+void CXFA_ViewLayoutProcessor::RemoveLayoutRecord(
+    CXFA_ViewRecord* pNewRecord,
+    CXFA_ViewRecord* pPrevRecord) {
+  if (!pNewRecord || !pPrevRecord)
+    return;
+  if (pNewRecord->pCurPageSet != pPrevRecord->pCurPageSet) {
+    pNewRecord->pCurPageSet->RemoveSelfIfParented();
+    return;
+  }
+  if (pNewRecord->pCurPageArea != pPrevRecord->pCurPageArea) {
+    pNewRecord->pCurPageArea->RemoveSelfIfParented();
+    return;
+  }
+  if (pNewRecord->pCurContentArea != pPrevRecord->pCurContentArea) {
+    pNewRecord->pCurContentArea->RemoveSelfIfParented();
+    return;
+  }
+}
+
+void CXFA_ViewLayoutProcessor::ReorderPendingLayoutRecordToTail(
+    CXFA_ViewRecord* pNewRecord,
+    CXFA_ViewRecord* pPrevRecord) {
+  if (!pNewRecord || !pPrevRecord)
+    return;
+  if (pNewRecord->pCurPageSet != pPrevRecord->pCurPageSet) {
+    ReorderLayoutItemToTail(pNewRecord->pCurPageSet);
+    return;
+  }
+  if (pNewRecord->pCurPageArea != pPrevRecord->pCurPageArea) {
+    ReorderLayoutItemToTail(pNewRecord->pCurPageArea);
+    return;
+  }
+  if (pNewRecord->pCurContentArea != pPrevRecord->pCurContentArea) {
+    ReorderLayoutItemToTail(pNewRecord->pCurContentArea);
+    return;
+  }
+}
+
+void CXFA_ViewLayoutProcessor::SubmitContentItem(
+    const RetainPtr<CXFA_ContentLayoutItem>& pContentLayoutItem,
+    CXFA_ContentLayoutProcessor::Result eStatus) {
+  if (pContentLayoutItem) {
+    if (!HasCurrentViewRecord())
+      return;
+
+    GetCurrentViewRecord()->pCurContentArea->AppendLastChild(
+        pContentLayoutItem);
+    m_bCreateOverFlowPage = false;
+  }
+
+  if (eStatus != CXFA_ContentLayoutProcessor::Result::kDone) {
+    if (eStatus == CXFA_ContentLayoutProcessor::Result::kPageFullBreak &&
+        m_CurrentViewRecordIter == GetTailPosition()) {
+      AppendNewPage(false);
+    }
+    m_CurrentViewRecordIter = GetTailPosition();
+    m_pCurPageArea = GetCurrentViewRecord()->pCurPageArea->GetFormNode();
+  }
+}
+
+float CXFA_ViewLayoutProcessor::GetAvailHeight() {
+  if (!HasCurrentViewRecord())
+    return 0.0f;
+
+  RetainPtr<CXFA_ViewLayoutItem> pLayoutItem =
+      GetCurrentViewRecord()->pCurContentArea;
+  if (!pLayoutItem || !pLayoutItem->GetFormNode())
+    return 0.0f;
+
+  float fAvailHeight = pLayoutItem->GetFormNode()->JSObject()->GetMeasureInUnit(
+      XFA_Attribute::H, XFA_Unit::Pt);
+  if (fAvailHeight >= kXFALayoutPrecision)
+    return fAvailHeight;
+  if (m_CurrentViewRecordIter == m_ProposedViewRecords.begin())
+    return 0.0f;
+  return FLT_MAX;
+}
+
+CXFA_ViewLayoutProcessor::CXFA_ViewRecord*
+CXFA_ViewLayoutProcessor::AppendNewRecord(
+    std::unique_ptr<CXFA_ViewRecord> pNewRecord) {
+  m_ProposedViewRecords.push_back(std::move(pNewRecord));
+  return m_ProposedViewRecords.back().get();
+}
+
+CXFA_ViewLayoutProcessor::CXFA_ViewRecord*
+CXFA_ViewLayoutProcessor::CreateViewRecord(CXFA_Node* pPageNode,
+                                           bool bCreateNew) {
+  ASSERT(pPageNode);
+  auto pNewRecord = pdfium::MakeUnique<CXFA_ViewRecord>();
+  if (!HasCurrentViewRecord()) {
+    CXFA_Node* pPageSet = pPageNode->GetParent();
+    if (pPageSet == m_pPageSetNode) {
+      pNewRecord->pCurPageSet = m_pPageSetRootLayoutItem;
+    } else {
+      auto pPageSetLayoutItem =
+          pdfium::MakeRetain<CXFA_ViewLayoutItem>(pPageSet, nullptr);
+      pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem.Get());
+      m_pPageSetRootLayoutItem->AppendLastChild(pPageSetLayoutItem);
+      pNewRecord->pCurPageSet = std::move(pPageSetLayoutItem);
+    }
+    return AppendNewRecord(std::move(pNewRecord));
+  }
+
+  if (!IsPageSetRootOrderedOccurrence()) {
+    *pNewRecord = *GetCurrentViewRecord();
+    return AppendNewRecord(std::move(pNewRecord));
+  }
+
+  CXFA_Node* pPageSet = pPageNode->GetParent();
+  if (!bCreateNew) {
+    if (pPageSet == m_pPageSetNode) {
+      pNewRecord->pCurPageSet = m_pPageSetCurLayoutItem;
+    } else {
+      RetainPtr<CXFA_ViewLayoutItem> pParentLayoutItem(
+          ToViewLayoutItem(pPageSet->JSObject()->GetLayoutItem()));
+      if (!pParentLayoutItem)
+        pParentLayoutItem = m_pPageSetCurLayoutItem;
+
+      pNewRecord->pCurPageSet = pParentLayoutItem;
+    }
+    return AppendNewRecord(std::move(pNewRecord));
+  }
+
+  CXFA_ViewLayoutItem* pParentPageSetLayout = nullptr;
+  if (pPageSet == GetCurrentViewRecord()->pCurPageSet->GetFormNode()) {
+    pParentPageSetLayout =
+        ToViewLayoutItem(GetCurrentViewRecord()->pCurPageSet->GetParent());
+  } else {
+    pParentPageSetLayout =
+        ToViewLayoutItem(pPageSet->GetParent()->JSObject()->GetLayoutItem());
+  }
+  auto pPageSetLayoutItem =
+      pdfium::MakeRetain<CXFA_ViewLayoutItem>(pPageSet, nullptr);
+  pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem.Get());
+  if (!pParentPageSetLayout) {
+    RetainPtr<CXFA_ViewLayoutItem> pPrePageSet(m_pPageSetRootLayoutItem);
+    while (pPrePageSet->GetNextSibling()) {
+      pPrePageSet.Reset(pPrePageSet->GetNextSibling()->AsViewLayoutItem());
+    }
+    if (pPrePageSet->GetParent()) {
+      pPrePageSet->GetParent()->InsertAfter(pPageSetLayoutItem,
+                                            pPrePageSet.Get());
+    }
+    m_pPageSetCurLayoutItem = pPageSetLayoutItem;
+  } else {
+    pParentPageSetLayout->AppendLastChild(pPageSetLayoutItem);
+  }
+  pNewRecord->pCurPageSet = pPageSetLayoutItem;
+  return AppendNewRecord(std::move(pNewRecord));
+}
+
+CXFA_ViewLayoutProcessor::CXFA_ViewRecord*
+CXFA_ViewLayoutProcessor::CreateViewRecordSimple() {
+  auto pNewRecord = pdfium::MakeUnique<CXFA_ViewRecord>();
+  if (HasCurrentViewRecord())
+    *pNewRecord = *GetCurrentViewRecord();
+  else
+    pNewRecord->pCurPageSet = m_pPageSetRootLayoutItem;
+  return AppendNewRecord(std::move(pNewRecord));
+}
+
+void CXFA_ViewLayoutProcessor::AddPageAreaLayoutItem(
+    CXFA_ViewRecord* pNewRecord,
+    CXFA_Node* pNewPageArea) {
+  RetainPtr<CXFA_ViewLayoutItem> pNewPageAreaLayoutItem;
+  if (pdfium::IndexInBounds(m_PageArray, m_nAvailPages)) {
+    RetainPtr<CXFA_ViewLayoutItem> pViewItem = m_PageArray[m_nAvailPages];
+    pViewItem->SetFormNode(pNewPageArea);
+    m_nAvailPages++;
+    pNewPageAreaLayoutItem = std::move(pViewItem);
+  } else {
+    CXFA_FFNotify* pNotify = pNewPageArea->GetDocument()->GetNotify();
+    auto pViewItem = pdfium::MakeRetain<CXFA_ViewLayoutItem>(
+        pNewPageArea, pNotify->OnCreateViewLayoutItem(pNewPageArea));
+    m_PageArray.push_back(pViewItem);
+    m_nAvailPages++;
+    pNotify->OnPageEvent(pViewItem.Get(), XFA_PAGEVIEWEVENT_PostRemoved);
+    pNewPageAreaLayoutItem = pViewItem;
+  }
+  pNewRecord->pCurPageSet->AppendLastChild(pNewPageAreaLayoutItem);
+  pNewRecord->pCurPageArea = pNewPageAreaLayoutItem;
+  pNewRecord->pCurContentArea = nullptr;
+}
+
+void CXFA_ViewLayoutProcessor::AddContentAreaLayoutItem(
+    CXFA_ViewRecord* pNewRecord,
+    CXFA_Node* pContentArea) {
+  if (!pContentArea) {
+    pNewRecord->pCurContentArea = nullptr;
+    return;
+  }
+  auto pNewViewLayoutItem =
+      pdfium::MakeRetain<CXFA_ViewLayoutItem>(pContentArea, nullptr);
+  pNewRecord->pCurPageArea->AppendLastChild(pNewViewLayoutItem);
+  pNewRecord->pCurContentArea = std::move(pNewViewLayoutItem);
+}
+
+void CXFA_ViewLayoutProcessor::FinishPaginatedPageSets() {
+  for (CXFA_ViewLayoutItem* pRootPageSetLayoutItem =
+           m_pPageSetRootLayoutItem.Get();
+       pRootPageSetLayoutItem; pRootPageSetLayoutItem = ToViewLayoutItem(
+                                   pRootPageSetLayoutItem->GetNextSibling())) {
+    PageSetIterator sIterator(pRootPageSetLayoutItem);
+    for (CXFA_ViewLayoutItem* pPageSetLayoutItem = sIterator.GetCurrent();
+         pPageSetLayoutItem; pPageSetLayoutItem = sIterator.MoveToNext()) {
+      XFA_AttributeValue ePageRelation =
+          pPageSetLayoutItem->GetFormNode()->JSObject()->GetEnum(
+              XFA_Attribute::Relation);
+      switch (ePageRelation) {
+        case XFA_AttributeValue::SimplexPaginated:
+        case XFA_AttributeValue::DuplexPaginated:
+          ProcessSimplexOrDuplexPageSets(
+              pPageSetLayoutItem,
+              ePageRelation == XFA_AttributeValue::SimplexPaginated);
+          break;
+        default:
+          ProcessLastPageSet();
+          break;
+      }
+    }
+  }
+}
+
+int32_t CXFA_ViewLayoutProcessor::GetPageCount() const {
+  return pdfium::CollectionSize<int32_t>(m_PageArray);
+}
+
+CXFA_ViewLayoutItem* CXFA_ViewLayoutProcessor::GetPage(int32_t index) const {
+  if (!pdfium::IndexInBounds(m_PageArray, index))
+    return nullptr;
+  return m_PageArray[index].Get();
+}
+
+int32_t CXFA_ViewLayoutProcessor::GetPageIndex(
+    const CXFA_ViewLayoutItem* pPage) const {
+  auto it = std::find(m_PageArray.begin(), m_PageArray.end(), pPage);
+  return it != m_PageArray.end() ? it - m_PageArray.begin() : -1;
+}
+
+bool CXFA_ViewLayoutProcessor::RunBreak(XFA_Element eBreakType,
+                                        XFA_AttributeValue eTargetType,
+                                        CXFA_Node* pTarget,
+                                        bool bStartNew) {
+  bool bRet = false;
+  switch (eTargetType) {
+    case XFA_AttributeValue::ContentArea:
+      if (pTarget && pTarget->GetElementType() != XFA_Element::ContentArea)
+        pTarget = nullptr;
+      if (ShouldGetNextPageArea(pTarget, bStartNew)) {
+        CXFA_Node* pPageArea = nullptr;
+        if (pTarget)
+          pPageArea = pTarget->GetParent();
+
+        pPageArea = GetNextAvailPageArea(pPageArea, pTarget, false, false);
+        bRet = !!pPageArea;
+      }
+      break;
+    case XFA_AttributeValue::PageArea:
+      if (pTarget && pTarget->GetElementType() != XFA_Element::PageArea)
+        pTarget = nullptr;
+      if (ShouldGetNextPageArea(pTarget, bStartNew)) {
+        CXFA_Node* pPageArea =
+            GetNextAvailPageArea(pTarget, nullptr, true, false);
+        bRet = !!pPageArea;
+      }
+      break;
+    case XFA_AttributeValue::PageOdd:
+      if (pTarget && pTarget->GetElementType() != XFA_Element::PageArea)
+        pTarget = nullptr;
+      break;
+    case XFA_AttributeValue::PageEven:
+      if (pTarget && pTarget->GetElementType() != XFA_Element::PageArea)
+        pTarget = nullptr;
+      break;
+    case XFA_AttributeValue::Auto:
+    default:
+      break;
+  }
+  return bRet;
+}
+
+bool CXFA_ViewLayoutProcessor::ShouldGetNextPageArea(CXFA_Node* pTarget,
+                                                     bool bStartNew) const {
+  return bStartNew || !pTarget || !HasCurrentViewRecord() ||
+         pTarget != GetCurrentViewRecord()->pCurPageArea->GetFormNode();
+}
+
+CXFA_ViewLayoutProcessor::BreakData
+CXFA_ViewLayoutProcessor::ExecuteBreakBeforeOrAfter(const CXFA_Node* pCurNode,
+                                                    bool bBefore) {
+  BreakData ret = {nullptr, nullptr, false};
+  XFA_Element eType = pCurNode->GetElementType();
+  switch (eType) {
+    case XFA_Element::BreakBefore:
+    case XFA_Element::BreakAfter: {
+      WideString wsBreakLeader;
+      WideString wsBreakTrailer;
+      CXFA_Node* pFormNode = pCurNode->GetContainerParent();
+      CXFA_Node* pContainer = pFormNode->GetTemplateNodeIfExists();
+      bool bStartNew =
+          pCurNode->JSObject()->GetInteger(XFA_Attribute::StartNew) != 0;
+      CXFA_Script* pScript =
+          pCurNode->GetFirstChildByClass<CXFA_Script>(XFA_Element::Script);
+      if (pScript && !RunBreakTestScript(pScript))
+        break;
+
+      WideString wsTarget =
+          pCurNode->JSObject()->GetCData(XFA_Attribute::Target);
+      CXFA_Node* pTarget = ResolveBreakTarget(m_pPageSetNode, true, &wsTarget);
+      wsBreakTrailer = pCurNode->JSObject()->GetCData(XFA_Attribute::Trailer);
+      wsBreakLeader = pCurNode->JSObject()->GetCData(XFA_Attribute::Leader);
+      ret.pLeader = ResolveBreakTarget(pContainer, true, &wsBreakLeader);
+      ret.pTrailer = ResolveBreakTarget(pContainer, true, &wsBreakTrailer);
+      if (RunBreak(eType,
+                   pCurNode->JSObject()->GetEnum(XFA_Attribute::TargetType),
+                   pTarget, bStartNew)) {
+        ret.bCreatePage = true;
+        break;
+      }
+      if (!m_ProposedViewRecords.empty() &&
+          m_CurrentViewRecordIter == m_ProposedViewRecords.begin() &&
+          eType == XFA_Element::BreakBefore) {
+        CXFA_Node* pParentNode = pFormNode->GetContainerParent();
+        if (!pParentNode ||
+            pFormNode != pParentNode->GetFirstContainerChild()) {
+          break;
+        }
+        pParentNode = pParentNode->GetParent();
+        if (!pParentNode ||
+            pParentNode->GetElementType() != XFA_Element::Form) {
+          break;
+        }
+        ret.bCreatePage = true;
+      }
+      break;
+    }
+    case XFA_Element::Break: {
+      bool bStartNew =
+          pCurNode->JSObject()->GetInteger(XFA_Attribute::StartNew) != 0;
+      WideString wsTarget = pCurNode->JSObject()->GetCData(
+          bBefore ? XFA_Attribute::BeforeTarget : XFA_Attribute::AfterTarget);
+      CXFA_Node* pTarget = ResolveBreakTarget(m_pPageSetNode, true, &wsTarget);
+      if (RunBreak(bBefore ? XFA_Element::BreakBefore : XFA_Element::BreakAfter,
+                   pCurNode->JSObject()->GetEnum(
+                       bBefore ? XFA_Attribute::Before : XFA_Attribute::After),
+                   pTarget, bStartNew)) {
+        ret.bCreatePage = true;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return ret;
+}
+
+Optional<CXFA_ViewLayoutProcessor::BreakData>
+CXFA_ViewLayoutProcessor::ProcessBreakBefore(const CXFA_Node* pBreakNode) {
+  return ProcessBreakBeforeOrAfter(pBreakNode, /*before=*/true);
+}
+
+Optional<CXFA_ViewLayoutProcessor::BreakData>
+CXFA_ViewLayoutProcessor::ProcessBreakAfter(const CXFA_Node* pBreakNode) {
+  return ProcessBreakBeforeOrAfter(pBreakNode, /*before=*/false);
+}
+
+Optional<CXFA_ViewLayoutProcessor::BreakData>
+CXFA_ViewLayoutProcessor::ProcessBreakBeforeOrAfter(const CXFA_Node* pBreakNode,
+                                                    bool bBefore) {
+  CXFA_Node* pFormNode = pBreakNode->GetContainerParent();
+  if (!pFormNode->PresenceRequiresSpace())
+    return pdfium::nullopt;
+
+  BreakData break_data = ExecuteBreakBeforeOrAfter(pBreakNode, bBefore);
+  CXFA_Document* pDocument = pBreakNode->GetDocument();
+  CXFA_Node* pDataScope = nullptr;
+  pFormNode = pFormNode->GetContainerParent();
+  if (break_data.pLeader) {
+    if (!break_data.pLeader->IsContainerNode())
+      return pdfium::nullopt;
+
+    pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
+    break_data.pLeader = pDocument->DataMerge_CopyContainer(
+        break_data.pLeader, pFormNode, pDataScope, true, true, true);
+    if (!break_data.pLeader)
+      return pdfium::nullopt;
+
+    pDocument->DataMerge_UpdateBindingRelations(break_data.pLeader);
+    SetLayoutGeneratedNodeFlag(break_data.pLeader);
+  }
+  if (break_data.pTrailer) {
+    if (!break_data.pTrailer->IsContainerNode())
+      return pdfium::nullopt;
+
+    if (!pDataScope)
+      pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
+
+    break_data.pTrailer = pDocument->DataMerge_CopyContainer(
+        break_data.pTrailer, pFormNode, pDataScope, true, true, true);
+    if (!break_data.pTrailer)
+      return pdfium::nullopt;
+
+    pDocument->DataMerge_UpdateBindingRelations(break_data.pTrailer);
+    SetLayoutGeneratedNodeFlag(break_data.pTrailer);
+  }
+  return break_data;
+}
+
+CXFA_Node* CXFA_ViewLayoutProcessor::ProcessBookendLeader(
+    const CXFA_Node* pBookendNode) {
+  return ProcessBookendLeaderOrTrailer(pBookendNode, /*leader=*/true);
+}
+
+CXFA_Node* CXFA_ViewLayoutProcessor::ProcessBookendTrailer(
+    const CXFA_Node* pBookendNode) {
+  return ProcessBookendLeaderOrTrailer(pBookendNode, /*leader=*/false);
+}
+
+CXFA_Node* CXFA_ViewLayoutProcessor::ProcessBookendLeaderOrTrailer(
+    const CXFA_Node* pBookendNode,
+    bool bLeader) {
+  CXFA_Node* pFormNode = pBookendNode->GetContainerParent();
+  CXFA_Node* pLeaderTemplate =
+      ResolveBookendLeaderOrTrailer(pBookendNode, bLeader);
+  if (!pLeaderTemplate)
+    return nullptr;
+
+  CXFA_Document* pDocument = pBookendNode->GetDocument();
+  CXFA_Node* pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
+  CXFA_Node* pBookendAppendNode = pDocument->DataMerge_CopyContainer(
+      pLeaderTemplate, pFormNode, pDataScope, true, true, true);
+  if (!pBookendAppendNode)
+    return nullptr;
+
+  pDocument->DataMerge_UpdateBindingRelations(pBookendAppendNode);
+  SetLayoutGeneratedNodeFlag(pBookendAppendNode);
+  return pBookendAppendNode;
+}
+
+bool CXFA_ViewLayoutProcessor::BreakOverflow(const CXFA_Node* pOverflowNode,
+                                             bool bCreatePage,
+                                             CXFA_Node** pLeaderTemplate,
+                                             CXFA_Node** pTrailerTemplate) {
+  CXFA_Node* pContainer =
+      pOverflowNode->GetContainerParent()->GetTemplateNodeIfExists();
+  if (pOverflowNode->GetElementType() == XFA_Element::Break) {
+    WideString wsOverflowLeader =
+        pOverflowNode->JSObject()->GetCData(XFA_Attribute::OverflowLeader);
+    WideString wsOverflowTarget =
+        pOverflowNode->JSObject()->GetCData(XFA_Attribute::OverflowTarget);
+    WideString wsOverflowTrailer =
+        pOverflowNode->JSObject()->GetCData(XFA_Attribute::OverflowTrailer);
+    if (wsOverflowTarget.IsEmpty() && wsOverflowLeader.IsEmpty() &&
+        wsOverflowTrailer.IsEmpty()) {
+      return false;
+    }
+
+    if (!wsOverflowTarget.IsEmpty() && bCreatePage && !m_bCreateOverFlowPage) {
+      CXFA_Node* pTarget =
+          ResolveBreakTarget(m_pPageSetNode, true, &wsOverflowTarget);
+      if (pTarget) {
+        m_bCreateOverFlowPage = true;
+        switch (pTarget->GetElementType()) {
+          case XFA_Element::PageArea:
+            RunBreak(XFA_Element::Overflow, XFA_AttributeValue::PageArea,
+                     pTarget, true);
+            break;
+          case XFA_Element::ContentArea:
+            RunBreak(XFA_Element::Overflow, XFA_AttributeValue::ContentArea,
+                     pTarget, true);
+            break;
+          default:
+            break;
+        }
+      }
+    }
+    if (!bCreatePage) {
+      *pLeaderTemplate =
+          ResolveBreakTarget(pContainer, true, &wsOverflowLeader);
+      *pTrailerTemplate =
+          ResolveBreakTarget(pContainer, true, &wsOverflowTrailer);
+    }
+    return true;
+  }
+
+  if (pOverflowNode->GetElementType() != XFA_Element::Overflow)
+    return false;
+
+  WideString wsOverflowTarget =
+      pOverflowNode->JSObject()->GetCData(XFA_Attribute::Target);
+  if (!wsOverflowTarget.IsEmpty() && bCreatePage && !m_bCreateOverFlowPage) {
+    CXFA_Node* pTarget =
+        ResolveBreakTarget(m_pPageSetNode, true, &wsOverflowTarget);
+    if (pTarget) {
+      m_bCreateOverFlowPage = true;
+      switch (pTarget->GetElementType()) {
+        case XFA_Element::PageArea:
+          RunBreak(XFA_Element::Overflow, XFA_AttributeValue::PageArea, pTarget,
+                   true);
+          break;
+        case XFA_Element::ContentArea:
+          RunBreak(XFA_Element::Overflow, XFA_AttributeValue::ContentArea,
+                   pTarget, true);
+          break;
+        default:
+          break;
+      }
+    }
+  }
+  if (!bCreatePage) {
+    WideString wsLeader =
+        pOverflowNode->JSObject()->GetCData(XFA_Attribute::Leader);
+    WideString wsTrailer =
+        pOverflowNode->JSObject()->GetCData(XFA_Attribute::Trailer);
+    *pLeaderTemplate = ResolveBreakTarget(pContainer, true, &wsLeader);
+    *pTrailerTemplate = ResolveBreakTarget(pContainer, true, &wsTrailer);
+  }
+  return true;
+}
+
+Optional<CXFA_ViewLayoutProcessor::OverflowData>
+CXFA_ViewLayoutProcessor::ProcessOverflow(CXFA_Node* pFormNode,
+                                          bool bCreatePage) {
+  if (!pFormNode)
+    return pdfium::nullopt;
+
+  CXFA_Node* pLeaderTemplate = nullptr;
+  CXFA_Node* pTrailerTemplate = nullptr;
+  bool bIsOverflowNode = pFormNode->GetElementType() == XFA_Element::Overflow ||
+                         pFormNode->GetElementType() == XFA_Element::Break;
+  OverflowData overflow_data{nullptr, nullptr};
+  for (CXFA_Node* pCurNode = bIsOverflowNode ? pFormNode
+                                             : pFormNode->GetFirstChild();
+       pCurNode; pCurNode = pCurNode->GetNextSibling()) {
+    if (BreakOverflow(pCurNode, bCreatePage, &pLeaderTemplate,
+                      &pTrailerTemplate)) {
+      if (bIsOverflowNode)
+        pFormNode = pCurNode->GetParent();
+
+      CXFA_Document* pDocument = pCurNode->GetDocument();
+      CXFA_Node* pDataScope = nullptr;
+      if (pLeaderTemplate) {
+        pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
+
+        overflow_data.pLeader = pDocument->DataMerge_CopyContainer(
+            pLeaderTemplate, pFormNode, pDataScope, true, true, true);
+        if (!overflow_data.pLeader)
+          return pdfium::nullopt;
+
+        pDocument->DataMerge_UpdateBindingRelations(overflow_data.pLeader);
+        SetLayoutGeneratedNodeFlag(overflow_data.pLeader);
+      }
+      if (pTrailerTemplate) {
+        if (!pDataScope)
+          pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
+
+        overflow_data.pTrailer = pDocument->DataMerge_CopyContainer(
+            pTrailerTemplate, pFormNode, pDataScope, true, true, true);
+        if (!overflow_data.pTrailer)
+          return pdfium::nullopt;
+
+        pDocument->DataMerge_UpdateBindingRelations(overflow_data.pTrailer);
+        SetLayoutGeneratedNodeFlag(overflow_data.pTrailer);
+      }
+      return overflow_data;
+    }
+    if (bIsOverflowNode)
+      break;
+  }
+  return pdfium::nullopt;
+}
+
+CXFA_Node* CXFA_ViewLayoutProcessor::ResolveBookendLeaderOrTrailer(
+    const CXFA_Node* pBookendNode,
+    bool bLeader) {
+  CXFA_Node* pContainer =
+      pBookendNode->GetContainerParent()->GetTemplateNodeIfExists();
+  if (pBookendNode->GetElementType() == XFA_Element::Break) {
+    WideString leader = pBookendNode->JSObject()->GetCData(
+        bLeader ? XFA_Attribute::BookendLeader : XFA_Attribute::BookendTrailer);
+    if (leader.IsEmpty())
+      return nullptr;
+    return ResolveBreakTarget(pContainer, false, &leader);
+  }
+
+  if (pBookendNode->GetElementType() != XFA_Element::Bookend)
+    return nullptr;
+
+  WideString leader = pBookendNode->JSObject()->GetCData(
+      bLeader ? XFA_Attribute::Leader : XFA_Attribute::Trailer);
+  return ResolveBreakTarget(pContainer, true, &leader);
+}
+
+bool CXFA_ViewLayoutProcessor::FindPageAreaFromPageSet(
+    CXFA_Node* pPageSet,
+    CXFA_Node* pStartChild,
+    CXFA_Node* pTargetPageArea,
+    CXFA_Node* pTargetContentArea,
+    bool bNewPage,
+    bool bQuery) {
+  if (!pPageSet && !pStartChild)
+    return false;
+
+  if (IsPageSetRootOrderedOccurrence()) {
+    return FindPageAreaFromPageSet_Ordered(pPageSet, pStartChild,
+                                           pTargetPageArea, pTargetContentArea,
+                                           bNewPage, bQuery);
+  }
+  XFA_AttributeValue ePreferredPosition = HasCurrentViewRecord()
+                                              ? XFA_AttributeValue::Rest
+                                              : XFA_AttributeValue::First;
+  return FindPageAreaFromPageSet_SimplexDuplex(
+      pPageSet, pStartChild, pTargetPageArea, pTargetContentArea, bNewPage,
+      bQuery, ePreferredPosition);
+}
+
+bool CXFA_ViewLayoutProcessor::FindPageAreaFromPageSet_Ordered(
+    CXFA_Node* pPageSet,
+    CXFA_Node* pStartChild,
+    CXFA_Node* pTargetPageArea,
+    CXFA_Node* pTargetContentArea,
+    bool bNewPage,
+    bool bQuery) {
+  int32_t iPageSetCount = 0;
+  if (!pStartChild && !bQuery) {
+    auto it = m_pPageSetMap.find(pPageSet);
+    if (it != m_pPageSetMap.end())
+      iPageSetCount = it->second;
+    int32_t iMax = -1;
+    CXFA_Node* pOccurNode =
+        pPageSet->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+    if (pOccurNode) {
+      Optional<int32_t> ret =
+          pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
+      if (ret)
+        iMax = *ret;
+    }
+    if (iMax >= 0 && iMax <= iPageSetCount)
+      return false;
+  }
+
+  bool bRes = false;
+  CXFA_Node* pCurrentNode =
+      pStartChild ? pStartChild->GetNextSibling() : pPageSet->GetFirstChild();
+  for (; pCurrentNode; pCurrentNode = pCurrentNode->GetNextSibling()) {
+    if (pCurrentNode->GetElementType() == XFA_Element::PageArea) {
+      if ((pTargetPageArea == pCurrentNode || !pTargetPageArea)) {
+        if (!pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
+                XFA_Element::ContentArea)) {
+          if (pTargetPageArea == pCurrentNode) {
+            CreateMinPageRecord(pCurrentNode, true, false);
+            pTargetPageArea = nullptr;
+          }
+          continue;
+        }
+        if (!bQuery) {
+          CXFA_ViewRecord* pNewRecord =
+              CreateViewRecord(pCurrentNode, !pStartChild);
+          AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
+          if (!pTargetContentArea) {
+            pTargetContentArea =
+                pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
+                    XFA_Element::ContentArea);
+          }
+          AddContentAreaLayoutItem(pNewRecord, pTargetContentArea);
+        }
+        m_pCurPageArea = pCurrentNode;
+        m_nCurPageCount = 1;
+        bRes = true;
+        break;
+      }
+      if (!bQuery)
+        CreateMinPageRecord(pCurrentNode, false, false);
+    } else if (pCurrentNode->GetElementType() == XFA_Element::PageSet) {
+      if (FindPageAreaFromPageSet_Ordered(pCurrentNode, nullptr,
+                                          pTargetPageArea, pTargetContentArea,
+                                          bNewPage, bQuery)) {
+        bRes = true;
+        break;
+      }
+      if (!bQuery)
+        CreateMinPageSetRecord(pCurrentNode, true);
+    }
+  }
+  if (!pStartChild && bRes && !bQuery)
+    m_pPageSetMap[pPageSet] = ++iPageSetCount;
+  return bRes;
+}
+
+bool CXFA_ViewLayoutProcessor::FindPageAreaFromPageSet_SimplexDuplex(
+    CXFA_Node* pPageSet,
+    CXFA_Node* pStartChild,
+    CXFA_Node* pTargetPageArea,
+    CXFA_Node* pTargetContentArea,
+    bool bNewPage,
+    bool bQuery,
+    XFA_AttributeValue ePreferredPosition) {
+  const XFA_AttributeValue eFallbackPosition = XFA_AttributeValue::Any;
+  CXFA_Node* pPreferredPageArea = nullptr;
+  CXFA_Node* pFallbackPageArea = nullptr;
+  CXFA_Node* pCurrentNode = nullptr;
+  if (!pStartChild || pStartChild->GetElementType() == XFA_Element::PageArea)
+    pCurrentNode = pPageSet->GetFirstChild();
+  else
+    pCurrentNode = pStartChild->GetNextSibling();
+
+  for (; pCurrentNode; pCurrentNode = pCurrentNode->GetNextSibling()) {
+    if (pCurrentNode->GetElementType() == XFA_Element::PageArea) {
+      if (!MatchPageAreaOddOrEven(pCurrentNode))
+        continue;
+
+      XFA_AttributeValue eCurPagePosition =
+          pCurrentNode->JSObject()->GetEnum(XFA_Attribute::PagePosition);
+      if (ePreferredPosition == XFA_AttributeValue::Last) {
+        if (eCurPagePosition != ePreferredPosition)
+          continue;
+        if (m_ePageSetMode == XFA_AttributeValue::SimplexPaginated ||
+            pCurrentNode->JSObject()->GetEnum(XFA_Attribute::OddOrEven) ==
+                XFA_AttributeValue::Any) {
+          pPreferredPageArea = pCurrentNode;
+          break;
+        }
+        CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
+        AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
+        AddContentAreaLayoutItem(
+            pNewRecord, pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
+                            XFA_Element::ContentArea));
+        return false;
+      }
+      if (ePreferredPosition == XFA_AttributeValue::Only) {
+        if (eCurPagePosition != ePreferredPosition)
+          continue;
+        if (m_ePageSetMode != XFA_AttributeValue::DuplexPaginated ||
+            pCurrentNode->JSObject()->GetEnum(XFA_Attribute::OddOrEven) ==
+                XFA_AttributeValue::Any) {
+          pPreferredPageArea = pCurrentNode;
+          break;
+        }
+        return false;
+      }
+      if ((pTargetPageArea == pCurrentNode || !pTargetPageArea)) {
+        if (!pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
+                XFA_Element::ContentArea)) {
+          if (pTargetPageArea == pCurrentNode) {
+            CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
+            AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
+            pTargetPageArea = nullptr;
+          }
+          continue;
+        }
+        if ((ePreferredPosition == XFA_AttributeValue::Rest &&
+             eCurPagePosition == XFA_AttributeValue::Any) ||
+            eCurPagePosition == ePreferredPosition) {
+          pPreferredPageArea = pCurrentNode;
+          break;
+        }
+        if (eCurPagePosition == eFallbackPosition && !pFallbackPageArea) {
+          pFallbackPageArea = pCurrentNode;
+        }
+      } else if (pTargetPageArea && !MatchPageAreaOddOrEven(pTargetPageArea)) {
+        CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
+        AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
+        AddContentAreaLayoutItem(
+            pNewRecord, pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
+                            XFA_Element::ContentArea));
+      }
+    } else if (pCurrentNode->GetElementType() == XFA_Element::PageSet) {
+      if (FindPageAreaFromPageSet_SimplexDuplex(
+              pCurrentNode, nullptr, pTargetPageArea, pTargetContentArea,
+              bNewPage, bQuery, ePreferredPosition)) {
+        break;
+      }
+    }
+  }
+
+  CXFA_Node* pCurPageArea = nullptr;
+  if (pPreferredPageArea)
+    pCurPageArea = pPreferredPageArea;
+  else if (pFallbackPageArea)
+    pCurPageArea = pFallbackPageArea;
+
+  if (!pCurPageArea)
+    return false;
+
+  if (!bQuery) {
+    CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
+    AddPageAreaLayoutItem(pNewRecord, pCurPageArea);
+    if (!pTargetContentArea) {
+      pTargetContentArea = pCurPageArea->GetFirstChildByClass<CXFA_ContentArea>(
+          XFA_Element::ContentArea);
+    }
+    AddContentAreaLayoutItem(pNewRecord, pTargetContentArea);
+  }
+  m_pCurPageArea = pCurPageArea;
+  return true;
+}
+
+bool CXFA_ViewLayoutProcessor::MatchPageAreaOddOrEven(CXFA_Node* pPageArea) {
+  if (m_ePageSetMode != XFA_AttributeValue::DuplexPaginated)
+    return true;
+
+  Optional<XFA_AttributeValue> ret =
+      pPageArea->JSObject()->TryEnum(XFA_Attribute::OddOrEven, true);
+  if (!ret || *ret == XFA_AttributeValue::Any)
+    return true;
+
+  int32_t iPageLast = GetPageCount() % 2;
+  return *ret == XFA_AttributeValue::Odd ? iPageLast == 0 : iPageLast == 1;
+}
+
+CXFA_Node* CXFA_ViewLayoutProcessor::GetNextAvailPageArea(
+    CXFA_Node* pTargetPageArea,
+    CXFA_Node* pTargetContentArea,
+    bool bNewPage,
+    bool bQuery) {
+  if (!m_pCurPageArea) {
+    FindPageAreaFromPageSet(m_pPageSetNode, nullptr, pTargetPageArea,
+                            pTargetContentArea, bNewPage, bQuery);
+    return m_pCurPageArea;
+  }
+
+  if (!pTargetPageArea || pTargetPageArea == m_pCurPageArea) {
+    if (!bNewPage && GetNextContentArea(pTargetContentArea))
+      return m_pCurPageArea;
+
+    if (IsPageSetRootOrderedOccurrence()) {
+      int32_t iMax = -1;
+      CXFA_Node* pOccurNode =
+          m_pCurPageArea->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+      if (pOccurNode) {
+        Optional<int32_t> ret =
+            pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
+        if (ret)
+          iMax = *ret;
+      }
+      if ((iMax < 0 || m_nCurPageCount < iMax)) {
+        if (!bQuery) {
+          CXFA_ViewRecord* pNewRecord = CreateViewRecord(m_pCurPageArea, false);
+          AddPageAreaLayoutItem(pNewRecord, m_pCurPageArea);
+          if (!pTargetContentArea) {
+            pTargetContentArea =
+                m_pCurPageArea->GetFirstChildByClass<CXFA_ContentArea>(
+                    XFA_Element::ContentArea);
+          }
+          AddContentAreaLayoutItem(pNewRecord, pTargetContentArea);
+        }
+        m_nCurPageCount++;
+        return m_pCurPageArea;
+      }
+    }
+  }
+
+  if (!bQuery && IsPageSetRootOrderedOccurrence())
+    CreateMinPageRecord(m_pCurPageArea, false, true);
+  if (FindPageAreaFromPageSet(m_pCurPageArea->GetParent(), m_pCurPageArea,
+                              pTargetPageArea, pTargetContentArea, bNewPage,
+                              bQuery)) {
+    return m_pCurPageArea;
+  }
+
+  CXFA_Node* pPageSet = m_pCurPageArea->GetParent();
+  while (pPageSet) {
+    if (FindPageAreaFromPageSet(pPageSet, nullptr, pTargetPageArea,
+                                pTargetContentArea, bNewPage, bQuery)) {
+      return m_pCurPageArea;
+    }
+    if (!bQuery && IsPageSetRootOrderedOccurrence())
+      CreateMinPageSetRecord(pPageSet, false);
+    if (FindPageAreaFromPageSet(nullptr, pPageSet, pTargetPageArea,
+                                pTargetContentArea, bNewPage, bQuery)) {
+      return m_pCurPageArea;
+    }
+    if (pPageSet == m_pPageSetNode)
+      break;
+
+    pPageSet = pPageSet->GetParent();
+  }
+  return nullptr;
+}
+
+bool CXFA_ViewLayoutProcessor::GetNextContentArea(CXFA_Node* pContentArea) {
+  CXFA_Node* pCurContentNode =
+      GetCurrentViewRecord()->pCurContentArea->GetFormNode();
+  if (!pContentArea) {
+    pContentArea = pCurContentNode->GetNextSameClassSibling<CXFA_ContentArea>(
+        XFA_Element::ContentArea);
+    if (!pContentArea)
+      return false;
+  } else {
+    if (pContentArea->GetParent() != m_pCurPageArea)
+      return false;
+
+    Optional<CXFA_ViewLayoutItem*> pContentAreaLayout = CheckContentAreaNotUsed(
+        GetCurrentViewRecord()->pCurPageArea.Get(), pContentArea);
+    if (!pContentAreaLayout.has_value())
+      return false;
+    if (pContentAreaLayout.value()) {
+      if (pContentAreaLayout.value()->GetFormNode() == pCurContentNode)
+        return false;
+
+      CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
+      pNewRecord->pCurContentArea.Reset(pContentAreaLayout.value());
+      return true;
+    }
+  }
+
+  CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
+  AddContentAreaLayoutItem(pNewRecord, pContentArea);
+  return true;
+}
+
+void CXFA_ViewLayoutProcessor::InitPageSetMap() {
+  if (!IsPageSetRootOrderedOccurrence())
+    return;
+
+  CXFA_NodeIterator sIterator(m_pPageSetNode);
+  for (CXFA_Node* pPageSetNode = sIterator.GetCurrent(); pPageSetNode;
+       pPageSetNode = sIterator.MoveToNext()) {
+    if (pPageSetNode->GetElementType() == XFA_Element::PageSet) {
+      XFA_AttributeValue eRelation =
+          pPageSetNode->JSObject()->GetEnum(XFA_Attribute::Relation);
+      if (eRelation == XFA_AttributeValue::OrderedOccurrence)
+        m_pPageSetMap[pPageSetNode] = 0;
+    }
+  }
+}
+
+int32_t CXFA_ViewLayoutProcessor::CreateMinPageRecord(CXFA_Node* pPageArea,
+                                                      bool bTargetPageArea,
+                                                      bool bCreateLast) {
+  if (!pPageArea)
+    return 0;
+
+  int32_t iMin = 0;
+  Optional<int32_t> ret;
+  CXFA_Node* pOccurNode =
+      pPageArea->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+  if (pOccurNode) {
+    ret = pOccurNode->JSObject()->TryInteger(XFA_Attribute::Min, false);
+    if (ret)
+      iMin = *ret;
+  }
+
+  if (!ret && !bTargetPageArea)
+    return iMin;
+
+  CXFA_Node* pContentArea = pPageArea->GetFirstChildByClass<CXFA_ContentArea>(
+      XFA_Element::ContentArea);
+  if (iMin < 1 && bTargetPageArea && !pContentArea)
+    iMin = 1;
+
+  int32_t i = 0;
+  if (bCreateLast)
+    i = m_nCurPageCount;
+
+  for (; i < iMin; i++) {
+    CXFA_ViewRecord* pNewRecord = CreateViewRecordSimple();
+    AddPageAreaLayoutItem(pNewRecord, pPageArea);
+    AddContentAreaLayoutItem(pNewRecord, pContentArea);
+  }
+  return iMin;
+}
+
+void CXFA_ViewLayoutProcessor::CreateMinPageSetRecord(CXFA_Node* pPageSet,
+                                                      bool bCreateAll) {
+  auto it = m_pPageSetMap.find(pPageSet);
+  if (it == m_pPageSetMap.end())
+    return;
+
+  int32_t iCurSetCount = it->second;
+  if (bCreateAll)
+    iCurSetCount = 0;
+
+  CXFA_Node* pOccurNode =
+      pPageSet->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+  if (!pOccurNode)
+    return;
+
+  Optional<int32_t> iMin =
+      pOccurNode->JSObject()->TryInteger(XFA_Attribute::Min, false);
+  if (!iMin || iCurSetCount >= *iMin)
+    return;
+
+  for (int32_t i = 0; i < *iMin - iCurSetCount; i++) {
+    for (CXFA_Node* node = pPageSet->GetFirstChild(); node;
+         node = node->GetNextSibling()) {
+      if (node->GetElementType() == XFA_Element::PageArea)
+        CreateMinPageRecord(node, false, false);
+      else if (node->GetElementType() == XFA_Element::PageSet)
+        CreateMinPageSetRecord(node, true);
+    }
+  }
+  m_pPageSetMap[pPageSet] = *iMin;
+}
+
+void CXFA_ViewLayoutProcessor::CreateNextMinRecord(CXFA_Node* pRecordNode) {
+  if (!pRecordNode)
+    return;
+
+  for (CXFA_Node* pCurrentNode = pRecordNode->GetNextSibling(); pCurrentNode;
+       pCurrentNode = pCurrentNode->GetNextSibling()) {
+    if (pCurrentNode->GetElementType() == XFA_Element::PageArea)
+      CreateMinPageRecord(pCurrentNode, false, false);
+    else if (pCurrentNode->GetElementType() == XFA_Element::PageSet)
+      CreateMinPageSetRecord(pCurrentNode, true);
+  }
+}
+
+void CXFA_ViewLayoutProcessor::ProcessLastPageSet() {
+  if (!m_pCurPageArea)
+    return;
+
+  CreateMinPageRecord(m_pCurPageArea, false, true);
+  CreateNextMinRecord(m_pCurPageArea);
+  CXFA_Node* pPageSet = m_pCurPageArea->GetParent();
+  while (pPageSet) {
+    CreateMinPageSetRecord(pPageSet, false);
+    if (pPageSet == m_pPageSetNode)
+      break;
+
+    CreateNextMinRecord(pPageSet);
+    pPageSet = pPageSet->GetParent();
+  }
+}
+
+bool CXFA_ViewLayoutProcessor::GetNextAvailContentHeight(float fChildHeight) {
+  CXFA_Node* pCurContentNode =
+      GetCurrentViewRecord()->pCurContentArea->GetFormNode();
+  if (!pCurContentNode)
+    return false;
+
+  pCurContentNode = pCurContentNode->GetNextSameClassSibling<CXFA_ContentArea>(
+      XFA_Element::ContentArea);
+  if (pCurContentNode) {
+    float fNextContentHeight = pCurContentNode->JSObject()->GetMeasureInUnit(
+        XFA_Attribute::H, XFA_Unit::Pt);
+    return fNextContentHeight > fChildHeight;
+  }
+
+  CXFA_Node* pPageNode = GetCurrentViewRecord()->pCurPageArea->GetFormNode();
+  CXFA_Node* pOccurNode =
+      pPageNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+  int32_t iMax = 0;
+  Optional<int32_t> ret;
+  if (pOccurNode) {
+    ret = pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
+    if (ret)
+      iMax = *ret;
+  }
+  if (ret) {
+    if (m_nCurPageCount == iMax) {
+      CXFA_Node* pSrcPage = m_pCurPageArea;
+      int32_t nSrcPageCount = m_nCurPageCount;
+      auto psSrcIter = GetTailPosition();
+      CXFA_Node* pNextPage =
+          GetNextAvailPageArea(nullptr, nullptr, false, true);
+      m_pCurPageArea = pSrcPage;
+      m_nCurPageCount = nSrcPageCount;
+      CXFA_ViewRecord* pPrevRecord = psSrcIter->get();
+      ++psSrcIter;
+      while (psSrcIter != m_ProposedViewRecords.end()) {
+        auto psSaveIter = psSrcIter++;
+        RemoveLayoutRecord(psSaveIter->get(), pPrevRecord);
+        m_ProposedViewRecords.erase(psSaveIter);
+      }
+      if (pNextPage) {
+        CXFA_Node* pContentArea =
+            pNextPage->GetFirstChildByClass<CXFA_ContentArea>(
+                XFA_Element::ContentArea);
+        if (pContentArea) {
+          float fNextContentHeight = pContentArea->JSObject()->GetMeasureInUnit(
+              XFA_Attribute::H, XFA_Unit::Pt);
+          if (fNextContentHeight > fChildHeight)
+            return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  CXFA_Node* pContentArea = pPageNode->GetFirstChildByClass<CXFA_ContentArea>(
+      XFA_Element::ContentArea);
+  if (!pContentArea)
+    return false;
+
+  float fNextContentHeight = pContentArea->JSObject()->GetMeasureInUnit(
+      XFA_Attribute::H, XFA_Unit::Pt);
+  return fNextContentHeight < kXFALayoutPrecision ||
+         fNextContentHeight > fChildHeight;
+}
+
+void CXFA_ViewLayoutProcessor::ClearData() {
+  if (!m_pPageSetNode)
+    return;
+
+  m_ProposedViewRecords.clear();
+  m_CurrentViewRecordIter = m_ProposedViewRecords.end();
+  m_pCurPageArea = nullptr;
+  m_nCurPageCount = 0;
+  m_bCreateOverFlowPage = false;
+  m_pPageSetMap.clear();
+}
+
+void CXFA_ViewLayoutProcessor::SaveLayoutItemChildren(
+    CXFA_LayoutItem* pParentLayoutItem) {
+  CXFA_Document* pDocument = m_pPageSetNode->GetDocument();
+  CXFA_FFNotify* pNotify = pDocument->GetNotify();
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDocument);
+  RetainPtr<CXFA_LayoutItem> pCurLayoutItem(pParentLayoutItem->GetFirstChild());
+  while (pCurLayoutItem) {
+    RetainPtr<CXFA_LayoutItem> pNextLayoutItem(
+        pCurLayoutItem->GetNextSibling());
+    if (pCurLayoutItem->IsContentLayoutItem()) {
+      if (pCurLayoutItem->GetFormNode()->HasRemovedChildren()) {
+        SyncRemoveLayoutItem(pCurLayoutItem.Get(), pNotify, pDocLayout);
+        pCurLayoutItem = std::move(pNextLayoutItem);
+        continue;
+      }
+      if (pCurLayoutItem->GetFormNode()->IsLayoutGeneratedNode())
+        pCurLayoutItem->GetFormNode()->SetNodeAndDescendantsUnused();
+    }
+    SaveLayoutItemChildren(pCurLayoutItem.Get());
+    pCurLayoutItem = std::move(pNextLayoutItem);
+  }
+}
+
+CXFA_Node* CXFA_ViewLayoutProcessor::QueryOverflow(CXFA_Node* pFormNode) {
+  for (CXFA_Node* pCurNode = pFormNode->GetFirstChild(); pCurNode;
+       pCurNode = pCurNode->GetNextSibling()) {
+    if (pCurNode->GetElementType() == XFA_Element::Break) {
+      WideString wsOverflowLeader =
+          pCurNode->JSObject()->GetCData(XFA_Attribute::OverflowLeader);
+      WideString wsOverflowTarget =
+          pCurNode->JSObject()->GetCData(XFA_Attribute::OverflowTarget);
+      WideString wsOverflowTrailer =
+          pCurNode->JSObject()->GetCData(XFA_Attribute::OverflowTrailer);
+
+      if (!wsOverflowLeader.IsEmpty() || !wsOverflowTrailer.IsEmpty() ||
+          !wsOverflowTarget.IsEmpty()) {
+        return pCurNode;
+      }
+      return nullptr;
+    }
+    if (pCurNode->GetElementType() == XFA_Element::Overflow)
+      return pCurNode;
+  }
+  return nullptr;
+}
+
+void CXFA_ViewLayoutProcessor::MergePageSetContents() {
+  CXFA_Document* pDocument = m_pPageSetNode->GetDocument();
+  pDocument->SetPendingNodesUnusedAndUnbound();
+
+  CXFA_FFNotify* pNotify = pDocument->GetNotify();
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDocument);
+  CXFA_ViewLayoutItem* pRootLayout = GetRootLayoutItem();
+
+  int32_t iIndex = 0;
+  for (; pRootLayout;
+       pRootLayout = ToViewLayoutItem(pRootLayout->GetNextSibling())) {
+    CXFA_Node* pPendingPageSet = nullptr;
+    ViewLayoutItemIterator iterator(pRootLayout);
+    CXFA_ViewLayoutItem* pRootPageSetViewItem = iterator.GetCurrent();
+    ASSERT(pRootPageSetViewItem->GetFormNode()->GetElementType() ==
+           XFA_Element::PageSet);
+    if (iIndex <
+        pdfium::CollectionSize<int32_t>(pDocument->m_pPendingPageSet)) {
+      pPendingPageSet = pDocument->m_pPendingPageSet[iIndex];
+      iIndex++;
+    }
+    if (!pPendingPageSet) {
+      if (pRootPageSetViewItem->GetFormNode()->GetPacketType() ==
+          XFA_PacketType::Template) {
+        pPendingPageSet =
+            pRootPageSetViewItem->GetFormNode()->CloneTemplateToForm(false);
+      } else {
+        pPendingPageSet = pRootPageSetViewItem->GetFormNode();
+      }
+    }
+    if (pRootPageSetViewItem->GetFormNode()->JSObject()->GetLayoutItem() ==
+        pRootPageSetViewItem) {
+      pRootPageSetViewItem->GetFormNode()->JSObject()->SetLayoutItem(nullptr);
+    }
+    pRootPageSetViewItem->SetFormNode(pPendingPageSet);
+    pPendingPageSet->ClearFlag(XFA_NodeFlag_UnusedNode);
+    for (CXFA_ViewLayoutItem* pViewItem = iterator.MoveToNext(); pViewItem;
+         pViewItem = iterator.MoveToNext()) {
+      CXFA_Node* pNode = pViewItem->GetFormNode();
+      if (pNode->GetPacketType() != XFA_PacketType::Template)
+        continue;
+
+      switch (pNode->GetElementType()) {
+        case XFA_Element::PageSet: {
+          CXFA_Node* pParentNode = pViewItem->GetParent()->GetFormNode();
+          CXFA_Node* pOldNode = pViewItem->GetFormNode();
+          CXFA_Node* pNewNode = XFA_NodeMerge_CloneOrMergeContainer(
+              pDocument, pParentNode, pOldNode, true, nullptr);
+          if (pOldNode != pNewNode) {
+            pOldNode->JSObject()->SetLayoutItem(nullptr);
+            pViewItem->SetFormNode(pNewNode);
+          }
+          break;
+        }
+        case XFA_Element::PageArea: {
+          CXFA_LayoutItem* pFormLayout = pViewItem;
+          CXFA_Node* pParentNode = pViewItem->GetParent()->GetFormNode();
+          bool bIsExistForm = true;
+          for (int32_t iLevel = 0; iLevel < 3; iLevel++) {
+            pFormLayout = pFormLayout->GetFirstChild();
+            if (iLevel == 2) {
+              while (pFormLayout &&
+                     !pFormLayout->GetFormNode()->PresenceRequiresSpace()) {
+                pFormLayout = pFormLayout->GetNextSibling();
+              }
+            }
+            if (!pFormLayout) {
+              bIsExistForm = false;
+              break;
+            }
+          }
+          if (bIsExistForm) {
+            CXFA_Node* pNewSubform = pFormLayout->GetFormNode();
+            if (pViewItem->m_pOldSubform &&
+                pViewItem->m_pOldSubform != pNewSubform) {
+              CXFA_Node* pExistingNode = XFA_DataMerge_FindFormDOMInstance(
+                  pDocument, pViewItem->GetFormNode()->GetElementType(),
+                  pViewItem->GetFormNode()->GetNameHash(), pParentNode);
+              CXFA_ContainerIterator sIterator(pExistingNode);
+              for (CXFA_Node* pIter = sIterator.GetCurrent(); pIter;
+                   pIter = sIterator.MoveToNext()) {
+                if (pIter->GetElementType() != XFA_Element::ContentArea) {
+                  RetainPtr<CXFA_LayoutItem> pLayoutItem(
+                      pIter->JSObject()->GetLayoutItem());
+                  if (pLayoutItem) {
+                    pNotify->OnLayoutItemRemoving(pDocLayout,
+                                                  pLayoutItem.Get());
+                    pLayoutItem->RemoveSelfIfParented();
+                  }
+                }
+              }
+              if (pExistingNode) {
+                pParentNode->RemoveChildAndNotify(pExistingNode, true);
+              }
+            }
+            pViewItem->m_pOldSubform = pNewSubform;
+          }
+          CXFA_Node* pOldNode = pViewItem->GetFormNode();
+          CXFA_Node* pNewNode = pDocument->DataMerge_CopyContainer(
+              pOldNode, pParentNode,
+              ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Record)), true, true,
+              true);
+          if (pOldNode != pNewNode) {
+            pOldNode->JSObject()->SetLayoutItem(nullptr);
+            pViewItem->SetFormNode(pNewNode);
+          }
+          break;
+        }
+        case XFA_Element::ContentArea: {
+          CXFA_Node* pParentNode = pViewItem->GetParent()->GetFormNode();
+          for (CXFA_Node* pChildNode = pParentNode->GetFirstChild(); pChildNode;
+               pChildNode = pChildNode->GetNextSibling()) {
+            if (pChildNode->GetTemplateNodeIfExists() !=
+                pViewItem->GetFormNode()) {
+              continue;
+            }
+            pViewItem->SetFormNode(pChildNode);
+            break;
+          }
+          break;
+        }
+        default:
+          break;
+      }
+    }
+    if (!pPendingPageSet->GetParent()) {
+      CXFA_Node* pNode = ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Form));
+      if (pNode) {
+        CXFA_Node* pFormToplevelSubform =
+            pNode->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
+        if (pFormToplevelSubform)
+          pFormToplevelSubform->InsertChildAndNotify(pPendingPageSet, nullptr);
+      }
+    }
+    pDocument->DataMerge_UpdateBindingRelations(pPendingPageSet);
+    pPendingPageSet->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+  }
+
+  CXFA_Node* pPageSet = GetRootLayoutItem()->GetFormNode();
+  while (pPageSet) {
+    CXFA_Node* pNextPageSet =
+        pPageSet->GetNextSameClassSibling<CXFA_PageSet>(XFA_Element::PageSet);
+    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
+        sIterator(pPageSet);
+    CXFA_Node* pNode = sIterator.GetCurrent();
+    while (pNode) {
+      if (pNode->IsUnusedNode()) {
+        if (pNode->IsContainerNode()) {
+          XFA_Element eType = pNode->GetElementType();
+          if (eType == XFA_Element::PageArea || eType == XFA_Element::PageSet) {
+            CXFA_ContainerIterator iteChild(pNode);
+            CXFA_Node* pChildNode = iteChild.MoveToNext();
+            for (; pChildNode; pChildNode = iteChild.MoveToNext()) {
+              RetainPtr<CXFA_LayoutItem> pLayoutItem(
+                  pChildNode->JSObject()->GetLayoutItem());
+              if (pLayoutItem) {
+                pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem.Get());
+                pLayoutItem->RemoveSelfIfParented();
+              }
+            }
+          } else if (eType != XFA_Element::ContentArea) {
+            RetainPtr<CXFA_LayoutItem> pLayoutItem(
+                pNode->JSObject()->GetLayoutItem());
+            if (pLayoutItem) {
+              pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem.Get());
+              pLayoutItem->RemoveSelfIfParented();
+            }
+          }
+          CXFA_Node* pNext = sIterator.SkipChildrenAndMoveToNext();
+          pNode->GetParent()->RemoveChildAndNotify(pNode, true);
+          pNode = pNext;
+        } else {
+          pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+          pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+          pNode = sIterator.MoveToNext();
+        }
+      } else {
+        pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+        pNode = sIterator.MoveToNext();
+      }
+    }
+    pPageSet = pNextPageSet;
+  }
+}
+
+void CXFA_ViewLayoutProcessor::LayoutPageSetContents() {
+  for (CXFA_ViewLayoutItem* pRootLayoutItem = GetRootLayoutItem();
+       pRootLayoutItem;
+       pRootLayoutItem = ToViewLayoutItem(pRootLayoutItem->GetNextSibling())) {
+    ViewLayoutItemIterator iterator(pRootLayoutItem);
+    for (CXFA_ViewLayoutItem* pViewItem = iterator.GetCurrent(); pViewItem;
+         pViewItem = iterator.MoveToNext()) {
+      XFA_Element type = pViewItem->GetFormNode()->GetElementType();
+      if (type != XFA_Element::PageArea)
+        continue;
+
+      m_pLayoutProcessor->GetRootContentLayoutProcessor()->DoLayoutPageArea(
+          pViewItem);
+    }
+  }
+}
+
+void CXFA_ViewLayoutProcessor::SyncLayoutData() {
+  MergePageSetContents();
+  LayoutPageSetContents();
+  CXFA_FFNotify* pNotify = m_pPageSetNode->GetDocument()->GetNotify();
+  int32_t nPageIdx = -1;
+  for (CXFA_ViewLayoutItem* pRootLayoutItem = GetRootLayoutItem();
+       pRootLayoutItem;
+       pRootLayoutItem = ToViewLayoutItem(pRootLayoutItem->GetNextSibling())) {
+    ViewLayoutItemIterator iteratorParent(pRootLayoutItem);
+    for (CXFA_ViewLayoutItem* pViewItem = iteratorParent.GetCurrent();
+         pViewItem; pViewItem = iteratorParent.MoveToNext()) {
+      XFA_Element type = pViewItem->GetFormNode()->GetElementType();
+      if (type != XFA_Element::PageArea)
+        continue;
+
+      nPageIdx++;
+      uint32_t dwRelevant =
+          XFA_WidgetStatus_Viewable | XFA_WidgetStatus_Printable;
+      CXFA_LayoutItemIterator iterator(pViewItem);
+      CXFA_LayoutItem* pChildLayoutItem = iterator.GetCurrent();
+      while (pChildLayoutItem) {
+        CXFA_ContentLayoutItem* pContentItem =
+            pChildLayoutItem->AsContentLayoutItem();
+        if (!pContentItem) {
+          pChildLayoutItem = iterator.MoveToNext();
+          continue;
+        }
+
+        XFA_AttributeValue presence =
+            pContentItem->GetFormNode()
+                ->JSObject()
+                ->TryEnum(XFA_Attribute::Presence, true)
+                .value_or(XFA_AttributeValue::Visible);
+        bool bVisible = presence == XFA_AttributeValue::Visible;
+        uint32_t dwRelevantChild =
+            GetRelevant(pContentItem->GetFormNode(), dwRelevant);
+        SyncContainer(pNotify, m_pLayoutProcessor, pContentItem,
+                      dwRelevantChild, bVisible, nPageIdx);
+        pChildLayoutItem = iterator.SkipChildrenAndMoveToNext();
+      }
+    }
+  }
+
+  int32_t nPage = pdfium::CollectionSize<int32_t>(m_PageArray);
+  for (int32_t i = nPage - 1; i >= m_nAvailPages; i--) {
+    RetainPtr<CXFA_ViewLayoutItem> pPage = m_PageArray[i];
+    m_PageArray.erase(m_PageArray.begin() + i);
+    pNotify->OnPageEvent(pPage.Get(), XFA_PAGEVIEWEVENT_PostRemoved);
+  }
+  ClearData();
+}
+
+void CXFA_ViewLayoutProcessor::PrepareLayout() {
+  m_pPageSetCurLayoutItem = nullptr;
+  m_ePageSetMode = XFA_AttributeValue::OrderedOccurrence;
+  m_nAvailPages = 0;
+  ClearData();
+  if (!m_pPageSetRootLayoutItem)
+    return;
+
+  RetainPtr<CXFA_ViewLayoutItem> pRootLayoutItem = m_pPageSetRootLayoutItem;
+  if (pRootLayoutItem &&
+      pRootLayoutItem->GetFormNode()->GetPacketType() == XFA_PacketType::Form) {
+    CXFA_Node* pPageSetFormNode = pRootLayoutItem->GetFormNode();
+    pRootLayoutItem->GetFormNode()->GetDocument()->m_pPendingPageSet.clear();
+    if (pPageSetFormNode->HasRemovedChildren()) {
+      XFA_ReleaseLayoutItem(pRootLayoutItem);
+      m_pPageSetRootLayoutItem = nullptr;
+      pRootLayoutItem = nullptr;
+      pPageSetFormNode = nullptr;
+      m_PageArray.clear();
+    }
+    while (pPageSetFormNode) {
+      CXFA_Node* pNextPageSet =
+          pPageSetFormNode->GetNextSameClassSibling<CXFA_PageSet>(
+              XFA_Element::PageSet);
+      pPageSetFormNode->GetParent()->RemoveChildAndNotify(pPageSetFormNode,
+                                                          false);
+      pRootLayoutItem->GetFormNode()
+          ->GetDocument()
+          ->m_pPendingPageSet.push_back(pPageSetFormNode);
+      pPageSetFormNode = pNextPageSet;
+    }
+  }
+  pRootLayoutItem = m_pPageSetRootLayoutItem;
+  CXFA_ViewLayoutItem* pNextLayout = nullptr;
+  for (; pRootLayoutItem; pRootLayoutItem.Reset(pNextLayout)) {
+    pNextLayout = ToViewLayoutItem(pRootLayoutItem->GetNextSibling());
+    SaveLayoutItemChildren(pRootLayoutItem.Get());
+    pRootLayoutItem->RemoveSelfIfParented();
+  }
+  m_pPageSetRootLayoutItem = nullptr;
+}
+
+void CXFA_ViewLayoutProcessor::ProcessSimplexOrDuplexPageSets(
+    CXFA_ViewLayoutItem* pPageSetLayoutItem,
+    bool bIsSimplex) {
+  size_t nPageAreaCount;
+  CXFA_LayoutItem* pLastPageAreaLayoutItem;
+  std::tie(nPageAreaCount, pLastPageAreaLayoutItem) =
+      GetPageAreaCountAndLastPageAreaFromPageSet(pPageSetLayoutItem);
+  if (!pLastPageAreaLayoutItem)
+    return;
+
+  if (!FindPageAreaFromPageSet_SimplexDuplex(
+          pPageSetLayoutItem->GetFormNode(), nullptr, nullptr, nullptr, true,
+          true,
+          nPageAreaCount == 1 ? XFA_AttributeValue::Only
+                              : XFA_AttributeValue::Last) &&
+      (nPageAreaCount == 1 &&
+       !FindPageAreaFromPageSet_SimplexDuplex(
+           pPageSetLayoutItem->GetFormNode(), nullptr, nullptr, nullptr, true,
+           true, XFA_AttributeValue::Last))) {
+    return;
+  }
+
+  CXFA_Node* pNode = m_pCurPageArea;
+  XFA_AttributeValue eCurChoice =
+      pNode->JSObject()->GetEnum(XFA_Attribute::PagePosition);
+  if (eCurChoice == XFA_AttributeValue::Last) {
+    XFA_AttributeValue eOddOrEven =
+        pNode->JSObject()->GetEnum(XFA_Attribute::OddOrEven);
+    XFA_AttributeValue eLastChoice =
+        pLastPageAreaLayoutItem->GetFormNode()->JSObject()->GetEnum(
+            XFA_Attribute::PagePosition);
+    if (eLastChoice == XFA_AttributeValue::First &&
+        (bIsSimplex || eOddOrEven != XFA_AttributeValue::Odd)) {
+      CXFA_ViewRecord* pRecord = CreateViewRecordSimple();
+      AddPageAreaLayoutItem(pRecord, pNode);
+      return;
+    }
+  }
+
+  std::vector<float> rgUsedHeights =
+      GetHeightsForContentAreas(pLastPageAreaLayoutItem);
+  if (ContentAreasFitInPageAreas(pNode, rgUsedHeights)) {
+    CXFA_LayoutItem* pChildLayoutItem =
+        pLastPageAreaLayoutItem->GetFirstChild();
+    CXFA_Node* pContentAreaNode = pNode->GetFirstChild();
+    pLastPageAreaLayoutItem->SetFormNode(pNode);
+    while (pChildLayoutItem && pContentAreaNode) {
+      if (pChildLayoutItem->GetFormNode()->GetElementType() !=
+          XFA_Element::ContentArea) {
+        pChildLayoutItem = pChildLayoutItem->GetNextSibling();
+        continue;
+      }
+      if (pContentAreaNode->GetElementType() != XFA_Element::ContentArea) {
+        pContentAreaNode = pContentAreaNode->GetNextSibling();
+        continue;
+      }
+      pChildLayoutItem->SetFormNode(pContentAreaNode);
+      pChildLayoutItem = pChildLayoutItem->GetNextSibling();
+      pContentAreaNode = pContentAreaNode->GetNextSibling();
+    }
+    return;
+  }
+
+  if (pNode->JSObject()->GetEnum(XFA_Attribute::PagePosition) ==
+      XFA_AttributeValue::Last) {
+    CXFA_ViewRecord* pRecord = CreateViewRecordSimple();
+    AddPageAreaLayoutItem(pRecord, pNode);
+  }
+}
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
new file mode 100644
index 0000000..e623285
--- /dev/null
+++ b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
@@ -0,0 +1,178 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTPROCESSOR_H_
+#define XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTPROCESSOR_H_
+
+#include <iterator>
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/retain_ptr.h"
+#include "third_party/base/optional.h"
+#include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h"
+
+class CXFA_LayoutItem;
+class CXFA_Node;
+
+class CXFA_ViewLayoutProcessor {
+ public:
+  struct BreakData {
+    CXFA_Node* pLeader;
+    CXFA_Node* pTrailer;
+    bool bCreatePage;
+  };
+
+  struct OverflowData {
+    CXFA_Node* pLeader;
+    CXFA_Node* pTrailer;
+  };
+
+  explicit CXFA_ViewLayoutProcessor(CXFA_LayoutProcessor* pLayoutProcessor);
+  ~CXFA_ViewLayoutProcessor();
+
+  bool InitLayoutPage(CXFA_Node* pFormNode);
+  bool PrepareFirstPage(CXFA_Node* pRootSubform);
+  float GetAvailHeight();
+  bool GetNextAvailContentHeight(float fChildHeight);
+  void SubmitContentItem(
+      const RetainPtr<CXFA_ContentLayoutItem>& pContentLayoutItem,
+      CXFA_ContentLayoutProcessor::Result eStatus);
+  void FinishPaginatedPageSets();
+  void SyncLayoutData();
+  int32_t GetPageCount() const;
+  CXFA_ViewLayoutItem* GetPage(int32_t index) const;
+  int32_t GetPageIndex(const CXFA_ViewLayoutItem* pPage) const;
+  CXFA_ViewLayoutItem* GetRootLayoutItem() const {
+    return m_pPageSetRootLayoutItem.Get();
+  }
+  Optional<BreakData> ProcessBreakBefore(const CXFA_Node* pBreakNode);
+  Optional<BreakData> ProcessBreakAfter(const CXFA_Node* pBreakNode);
+  Optional<OverflowData> ProcessOverflow(CXFA_Node* pFormNode,
+                                         bool bCreatePage);
+  CXFA_Node* QueryOverflow(CXFA_Node* pFormNode);
+  CXFA_Node* ProcessBookendLeader(const CXFA_Node* pBookendNode);
+  CXFA_Node* ProcessBookendTrailer(const CXFA_Node* pBookendNode);
+
+ private:
+  struct CXFA_ViewRecord {
+    CXFA_ViewRecord();
+    ~CXFA_ViewRecord();
+
+    RetainPtr<CXFA_ViewLayoutItem> pCurPageSet;
+    RetainPtr<CXFA_ViewLayoutItem> pCurPageArea;
+    RetainPtr<CXFA_ViewLayoutItem> pCurContentArea;
+  };
+
+  using RecordList = std::list<std::unique_ptr<CXFA_ViewRecord>>;
+
+  bool AppendNewPage(bool bFirstTemPage);
+  void ReorderPendingLayoutRecordToTail(CXFA_ViewRecord* pNewRecord,
+                                        CXFA_ViewRecord* pPrevRecord);
+  void RemoveLayoutRecord(CXFA_ViewRecord* pNewRecord,
+                          CXFA_ViewRecord* pPrevRecord);
+  bool HasCurrentViewRecord() const {
+    return m_CurrentViewRecordIter != m_ProposedViewRecords.end();
+  }
+  CXFA_ViewRecord* GetCurrentViewRecord() {
+    return m_CurrentViewRecordIter->get();
+  }
+  const CXFA_ViewRecord* GetCurrentViewRecord() const {
+    return m_CurrentViewRecordIter->get();
+  }
+  void ResetToFirstViewRecord() {
+    m_CurrentViewRecordIter = m_ProposedViewRecords.begin();
+  }
+  RecordList::iterator GetTailPosition() {
+    auto iter = m_ProposedViewRecords.end();
+    return !m_ProposedViewRecords.empty() ? std::prev(iter) : iter;
+  }
+  CXFA_ViewRecord* AppendNewRecord(std::unique_ptr<CXFA_ViewRecord> pNewRecord);
+  CXFA_ViewRecord* CreateViewRecord(CXFA_Node* pPageNode, bool bCreateNew);
+  CXFA_ViewRecord* CreateViewRecordSimple();
+  void AddPageAreaLayoutItem(CXFA_ViewRecord* pNewRecord,
+                             CXFA_Node* pNewPageArea);
+  void AddContentAreaLayoutItem(CXFA_ViewRecord* pNewRecord,
+                                CXFA_Node* pContentArea);
+  bool RunBreak(XFA_Element eBreakType,
+                XFA_AttributeValue eTargetType,
+                CXFA_Node* pTarget,
+                bool bStartNew);
+  bool ShouldGetNextPageArea(CXFA_Node* pTarget, bool bStartNew) const;
+  bool BreakOverflow(const CXFA_Node* pOverflowNode,
+                     bool bCreatePage,
+                     CXFA_Node** pLeaderTemplate,
+                     CXFA_Node** pTrailerTemplate);
+  CXFA_Node* ProcessBookendLeaderOrTrailer(const CXFA_Node* pBookendNode,
+                                           bool bLeader);
+  CXFA_Node* ResolveBookendLeaderOrTrailer(const CXFA_Node* pBookendNode,
+                                           bool bLeader);
+  Optional<BreakData> ProcessBreakBeforeOrAfter(const CXFA_Node* pBreakNode,
+                                                bool bBefore);
+  BreakData ExecuteBreakBeforeOrAfter(const CXFA_Node* pCurNode, bool bBefore);
+
+  int32_t CreateMinPageRecord(CXFA_Node* pPageArea,
+                              bool bTargetPageArea,
+                              bool bCreateLast);
+  void CreateMinPageSetRecord(CXFA_Node* pPageSet, bool bCreateAll);
+  void CreateNextMinRecord(CXFA_Node* pRecordNode);
+  bool FindPageAreaFromPageSet(CXFA_Node* pPageSet,
+                               CXFA_Node* pStartChild,
+                               CXFA_Node* pTargetPageArea,
+                               CXFA_Node* pTargetContentArea,
+                               bool bNewPage,
+                               bool bQuery);
+  bool FindPageAreaFromPageSet_Ordered(CXFA_Node* pPageSet,
+                                       CXFA_Node* pStartChild,
+                                       CXFA_Node* pTargetPageArea,
+                                       CXFA_Node* pTargetContentArea,
+                                       bool bNewPage,
+                                       bool bQuery);
+  bool FindPageAreaFromPageSet_SimplexDuplex(
+      CXFA_Node* pPageSet,
+      CXFA_Node* pStartChild,
+      CXFA_Node* pTargetPageArea,
+      CXFA_Node* pTargetContentArea,
+      bool bNewPage,
+      bool bQuery,
+      XFA_AttributeValue ePreferredPosition);
+  bool MatchPageAreaOddOrEven(CXFA_Node* pPageArea);
+  CXFA_Node* GetNextAvailPageArea(CXFA_Node* pTargetPageArea,
+                                  CXFA_Node* pTargetContentArea,
+                                  bool bNewPage,
+                                  bool bQuery);
+  bool GetNextContentArea(CXFA_Node* pTargetContentArea);
+  void InitPageSetMap();
+  void ProcessLastPageSet();
+  bool IsPageSetRootOrderedOccurrence() const {
+    return m_ePageSetMode == XFA_AttributeValue::OrderedOccurrence;
+  }
+  void ClearData();
+  void MergePageSetContents();
+  void LayoutPageSetContents();
+  void PrepareLayout();
+  void SaveLayoutItemChildren(CXFA_LayoutItem* pParentLayoutItem);
+  void ProcessSimplexOrDuplexPageSets(CXFA_ViewLayoutItem* pPageSetLayoutItem,
+                                      bool bIsSimplex);
+
+  CXFA_LayoutProcessor* m_pLayoutProcessor = nullptr;
+  CXFA_Node* m_pPageSetNode = nullptr;
+  RetainPtr<CXFA_ViewLayoutItem> m_pPageSetRootLayoutItem;
+  RetainPtr<CXFA_ViewLayoutItem> m_pPageSetCurLayoutItem;
+  RecordList m_ProposedViewRecords;
+  RecordList::iterator m_CurrentViewRecordIter;
+  CXFA_Node* m_pCurPageArea = nullptr;
+  int32_t m_nAvailPages = 0;
+  int32_t m_nCurPageCount = 0;
+  XFA_AttributeValue m_ePageSetMode = XFA_AttributeValue::OrderedOccurrence;
+  bool m_bCreateOverFlowPage = false;
+  std::map<CXFA_Node*, int32_t> m_pPageSetMap;
+  std::vector<RetainPtr<CXFA_ViewLayoutItem>> m_PageArray;
+};
+
+#endif  // XFA_FXFA_LAYOUT_CXFA_VIEWLAYOUTPROCESSOR_H_
diff --git a/xfa/fxfa/parser/BUILD.gn b/xfa/fxfa/parser/BUILD.gn
new file mode 100644
index 0000000..c6f6d55
--- /dev/null
+++ b/xfa/fxfa/parser/BUILD.gn
@@ -0,0 +1,721 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../../pdfium.gni")
+import("../../../testing/test.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("parser") {
+  sources = [
+    "cscript_datawindow.cpp",
+    "cscript_datawindow.h",
+    "cscript_eventpseudomodel.cpp",
+    "cscript_eventpseudomodel.h",
+    "cscript_hostpseudomodel.cpp",
+    "cscript_hostpseudomodel.h",
+    "cscript_layoutpseudomodel.cpp",
+    "cscript_layoutpseudomodel.h",
+    "cscript_logpseudomodel.cpp",
+    "cscript_logpseudomodel.h",
+    "cscript_signaturepseudomodel.cpp",
+    "cscript_signaturepseudomodel.h",
+    "cxfa_accessiblecontent.cpp",
+    "cxfa_accessiblecontent.h",
+    "cxfa_acrobat.cpp",
+    "cxfa_acrobat.h",
+    "cxfa_acrobat7.cpp",
+    "cxfa_acrobat7.h",
+    "cxfa_adbe_jsconsole.cpp",
+    "cxfa_adbe_jsconsole.h",
+    "cxfa_adbe_jsdebugger.cpp",
+    "cxfa_adbe_jsdebugger.h",
+    "cxfa_addsilentprint.cpp",
+    "cxfa_addsilentprint.h",
+    "cxfa_addviewerpreferences.cpp",
+    "cxfa_addviewerpreferences.h",
+    "cxfa_adjustdata.cpp",
+    "cxfa_adjustdata.h",
+    "cxfa_adobeextensionlevel.cpp",
+    "cxfa_adobeextensionlevel.h",
+    "cxfa_agent.cpp",
+    "cxfa_agent.h",
+    "cxfa_alwaysembed.cpp",
+    "cxfa_alwaysembed.h",
+    "cxfa_amd.cpp",
+    "cxfa_amd.h",
+    "cxfa_appearancefilter.cpp",
+    "cxfa_appearancefilter.h",
+    "cxfa_arc.cpp",
+    "cxfa_arc.h",
+    "cxfa_area.cpp",
+    "cxfa_area.h",
+    "cxfa_arraynodelist.cpp",
+    "cxfa_arraynodelist.h",
+    "cxfa_assist.cpp",
+    "cxfa_assist.h",
+    "cxfa_attachnodelist.cpp",
+    "cxfa_attachnodelist.h",
+    "cxfa_attributes.cpp",
+    "cxfa_attributes.h",
+    "cxfa_autosave.cpp",
+    "cxfa_autosave.h",
+    "cxfa_barcode.cpp",
+    "cxfa_barcode.h",
+    "cxfa_base.cpp",
+    "cxfa_base.h",
+    "cxfa_batchoutput.cpp",
+    "cxfa_batchoutput.h",
+    "cxfa_behavioroverride.cpp",
+    "cxfa_behavioroverride.h",
+    "cxfa_bind.cpp",
+    "cxfa_bind.h",
+    "cxfa_binditems.cpp",
+    "cxfa_binditems.h",
+    "cxfa_bookend.cpp",
+    "cxfa_bookend.h",
+    "cxfa_boolean.cpp",
+    "cxfa_boolean.h",
+    "cxfa_border.cpp",
+    "cxfa_border.h",
+    "cxfa_box.cpp",
+    "cxfa_box.h",
+    "cxfa_break.cpp",
+    "cxfa_break.h",
+    "cxfa_breakafter.cpp",
+    "cxfa_breakafter.h",
+    "cxfa_breakbefore.cpp",
+    "cxfa_breakbefore.h",
+    "cxfa_button.cpp",
+    "cxfa_button.h",
+    "cxfa_cache.cpp",
+    "cxfa_cache.h",
+    "cxfa_calculate.cpp",
+    "cxfa_calculate.h",
+    "cxfa_calendarsymbols.cpp",
+    "cxfa_calendarsymbols.h",
+    "cxfa_caption.cpp",
+    "cxfa_caption.h",
+    "cxfa_certificate.cpp",
+    "cxfa_certificate.h",
+    "cxfa_certificates.cpp",
+    "cxfa_certificates.h",
+    "cxfa_change.cpp",
+    "cxfa_change.h",
+    "cxfa_checkbutton.cpp",
+    "cxfa_checkbutton.h",
+    "cxfa_choicelist.cpp",
+    "cxfa_choicelist.h",
+    "cxfa_color.cpp",
+    "cxfa_color.h",
+    "cxfa_comb.cpp",
+    "cxfa_comb.h",
+    "cxfa_command.cpp",
+    "cxfa_command.h",
+    "cxfa_common.cpp",
+    "cxfa_common.h",
+    "cxfa_compress.cpp",
+    "cxfa_compress.h",
+    "cxfa_compression.cpp",
+    "cxfa_compression.h",
+    "cxfa_compresslogicalstructure.cpp",
+    "cxfa_compresslogicalstructure.h",
+    "cxfa_compressobjectstream.cpp",
+    "cxfa_compressobjectstream.h",
+    "cxfa_config.cpp",
+    "cxfa_config.h",
+    "cxfa_conformance.cpp",
+    "cxfa_conformance.h",
+    "cxfa_connect.cpp",
+    "cxfa_connect.h",
+    "cxfa_connectionset.cpp",
+    "cxfa_connectionset.h",
+    "cxfa_connectstring.cpp",
+    "cxfa_connectstring.h",
+    "cxfa_contentarea.cpp",
+    "cxfa_contentarea.h",
+    "cxfa_contentcopy.cpp",
+    "cxfa_contentcopy.h",
+    "cxfa_copies.cpp",
+    "cxfa_copies.h",
+    "cxfa_corner.cpp",
+    "cxfa_corner.h",
+    "cxfa_creator.cpp",
+    "cxfa_creator.h",
+    "cxfa_currencysymbol.cpp",
+    "cxfa_currencysymbol.h",
+    "cxfa_currencysymbols.cpp",
+    "cxfa_currencysymbols.h",
+    "cxfa_currentpage.cpp",
+    "cxfa_currentpage.h",
+    "cxfa_data.cpp",
+    "cxfa_data.h",
+    "cxfa_dataexporter.cpp",
+    "cxfa_dataexporter.h",
+    "cxfa_datagroup.cpp",
+    "cxfa_datagroup.h",
+    "cxfa_datamodel.cpp",
+    "cxfa_datamodel.h",
+    "cxfa_datavalue.cpp",
+    "cxfa_datavalue.h",
+    "cxfa_date.cpp",
+    "cxfa_date.h",
+    "cxfa_datepattern.cpp",
+    "cxfa_datepattern.h",
+    "cxfa_datepatterns.cpp",
+    "cxfa_datepatterns.h",
+    "cxfa_datetime.cpp",
+    "cxfa_datetime.h",
+    "cxfa_datetimeedit.cpp",
+    "cxfa_datetimeedit.h",
+    "cxfa_datetimesymbols.cpp",
+    "cxfa_datetimesymbols.h",
+    "cxfa_day.cpp",
+    "cxfa_day.h",
+    "cxfa_daynames.cpp",
+    "cxfa_daynames.h",
+    "cxfa_debug.cpp",
+    "cxfa_debug.h",
+    "cxfa_decimal.cpp",
+    "cxfa_decimal.h",
+    "cxfa_defaulttypeface.cpp",
+    "cxfa_defaulttypeface.h",
+    "cxfa_defaultui.cpp",
+    "cxfa_defaultui.h",
+    "cxfa_delete.cpp",
+    "cxfa_delete.h",
+    "cxfa_delta.cpp",
+    "cxfa_delta.h",
+    "cxfa_deltas.cpp",
+    "cxfa_deltas.h",
+    "cxfa_desc.cpp",
+    "cxfa_desc.h",
+    "cxfa_destination.cpp",
+    "cxfa_destination.h",
+    "cxfa_digestmethod.cpp",
+    "cxfa_digestmethod.h",
+    "cxfa_digestmethods.cpp",
+    "cxfa_digestmethods.h",
+    "cxfa_document.cpp",
+    "cxfa_document.h",
+    "cxfa_document_parser.cpp",
+    "cxfa_document_parser.h",
+    "cxfa_documentassembly.cpp",
+    "cxfa_documentassembly.h",
+    "cxfa_draw.cpp",
+    "cxfa_draw.h",
+    "cxfa_driver.cpp",
+    "cxfa_driver.h",
+    "cxfa_dsigdata.cpp",
+    "cxfa_dsigdata.h",
+    "cxfa_duplexoption.cpp",
+    "cxfa_duplexoption.h",
+    "cxfa_dynamicrender.cpp",
+    "cxfa_dynamicrender.h",
+    "cxfa_edge.cpp",
+    "cxfa_edge.h",
+    "cxfa_effectiveinputpolicy.cpp",
+    "cxfa_effectiveinputpolicy.h",
+    "cxfa_effectiveoutputpolicy.cpp",
+    "cxfa_effectiveoutputpolicy.h",
+    "cxfa_embed.cpp",
+    "cxfa_embed.h",
+    "cxfa_encoding.cpp",
+    "cxfa_encoding.h",
+    "cxfa_encodings.cpp",
+    "cxfa_encodings.h",
+    "cxfa_encrypt.cpp",
+    "cxfa_encrypt.h",
+    "cxfa_encryption.cpp",
+    "cxfa_encryption.h",
+    "cxfa_encryptionlevel.cpp",
+    "cxfa_encryptionlevel.h",
+    "cxfa_encryptionmethod.cpp",
+    "cxfa_encryptionmethod.h",
+    "cxfa_encryptionmethods.cpp",
+    "cxfa_encryptionmethods.h",
+    "cxfa_enforce.cpp",
+    "cxfa_enforce.h",
+    "cxfa_equate.cpp",
+    "cxfa_equate.h",
+    "cxfa_equaterange.cpp",
+    "cxfa_equaterange.h",
+    "cxfa_era.cpp",
+    "cxfa_era.h",
+    "cxfa_eranames.cpp",
+    "cxfa_eranames.h",
+    "cxfa_event.cpp",
+    "cxfa_event.h",
+    "cxfa_exclgroup.cpp",
+    "cxfa_exclgroup.h",
+    "cxfa_exclude.cpp",
+    "cxfa_exclude.h",
+    "cxfa_excludens.cpp",
+    "cxfa_excludens.h",
+    "cxfa_exdata.cpp",
+    "cxfa_exdata.h",
+    "cxfa_execute.cpp",
+    "cxfa_execute.h",
+    "cxfa_exobject.cpp",
+    "cxfa_exobject.h",
+    "cxfa_extras.cpp",
+    "cxfa_extras.h",
+    "cxfa_field.cpp",
+    "cxfa_field.h",
+    "cxfa_fill.cpp",
+    "cxfa_fill.h",
+    "cxfa_filter.cpp",
+    "cxfa_filter.h",
+    "cxfa_fliplabel.cpp",
+    "cxfa_fliplabel.h",
+    "cxfa_float.cpp",
+    "cxfa_float.h",
+    "cxfa_font.cpp",
+    "cxfa_font.h",
+    "cxfa_fontinfo.cpp",
+    "cxfa_fontinfo.h",
+    "cxfa_form.cpp",
+    "cxfa_form.h",
+    "cxfa_format.cpp",
+    "cxfa_format.h",
+    "cxfa_formfieldfilling.cpp",
+    "cxfa_formfieldfilling.h",
+    "cxfa_groupparent.cpp",
+    "cxfa_groupparent.h",
+    "cxfa_handler.cpp",
+    "cxfa_handler.h",
+    "cxfa_hyphenation.cpp",
+    "cxfa_hyphenation.h",
+    "cxfa_ifempty.cpp",
+    "cxfa_ifempty.h",
+    "cxfa_image.cpp",
+    "cxfa_image.h",
+    "cxfa_imageedit.cpp",
+    "cxfa_imageedit.h",
+    "cxfa_includexdpcontent.cpp",
+    "cxfa_includexdpcontent.h",
+    "cxfa_incrementalload.cpp",
+    "cxfa_incrementalload.h",
+    "cxfa_incrementalmerge.cpp",
+    "cxfa_incrementalmerge.h",
+    "cxfa_insert.cpp",
+    "cxfa_insert.h",
+    "cxfa_instancemanager.cpp",
+    "cxfa_instancemanager.h",
+    "cxfa_integer.cpp",
+    "cxfa_integer.h",
+    "cxfa_interactive.cpp",
+    "cxfa_interactive.h",
+    "cxfa_issuers.cpp",
+    "cxfa_issuers.h",
+    "cxfa_items.cpp",
+    "cxfa_items.h",
+    "cxfa_jog.cpp",
+    "cxfa_jog.h",
+    "cxfa_keep.cpp",
+    "cxfa_keep.h",
+    "cxfa_keyusage.cpp",
+    "cxfa_keyusage.h",
+    "cxfa_labelprinter.cpp",
+    "cxfa_labelprinter.h",
+    "cxfa_layout.cpp",
+    "cxfa_layout.h",
+    "cxfa_level.cpp",
+    "cxfa_level.h",
+    "cxfa_line.cpp",
+    "cxfa_line.h",
+    "cxfa_linear.cpp",
+    "cxfa_linear.h",
+    "cxfa_linearized.cpp",
+    "cxfa_linearized.h",
+    "cxfa_list.cpp",
+    "cxfa_list.h",
+    "cxfa_locale.cpp",
+    "cxfa_locale.h",
+    "cxfa_localemgr.cpp",
+    "cxfa_localemgr.h",
+    "cxfa_localeset.cpp",
+    "cxfa_localeset.h",
+    "cxfa_localevalue.cpp",
+    "cxfa_localevalue.h",
+    "cxfa_lockdocument.cpp",
+    "cxfa_lockdocument.h",
+    "cxfa_log.cpp",
+    "cxfa_log.h",
+    "cxfa_manifest.cpp",
+    "cxfa_manifest.h",
+    "cxfa_map.cpp",
+    "cxfa_map.h",
+    "cxfa_margin.cpp",
+    "cxfa_margin.h",
+    "cxfa_mdp.cpp",
+    "cxfa_mdp.h",
+    "cxfa_measurement.cpp",
+    "cxfa_measurement.h",
+    "cxfa_medium.cpp",
+    "cxfa_medium.h",
+    "cxfa_mediuminfo.cpp",
+    "cxfa_mediuminfo.h",
+    "cxfa_meridiem.cpp",
+    "cxfa_meridiem.h",
+    "cxfa_meridiemnames.cpp",
+    "cxfa_meridiemnames.h",
+    "cxfa_message.cpp",
+    "cxfa_message.h",
+    "cxfa_messaging.cpp",
+    "cxfa_messaging.h",
+    "cxfa_mode.cpp",
+    "cxfa_mode.h",
+    "cxfa_modifyannots.cpp",
+    "cxfa_modifyannots.h",
+    "cxfa_month.cpp",
+    "cxfa_month.h",
+    "cxfa_monthnames.cpp",
+    "cxfa_monthnames.h",
+    "cxfa_msgid.cpp",
+    "cxfa_msgid.h",
+    "cxfa_nameattr.cpp",
+    "cxfa_nameattr.h",
+    "cxfa_neverembed.cpp",
+    "cxfa_neverembed.h",
+    "cxfa_node.cpp",
+    "cxfa_node.h",
+    "cxfa_nodehelper.cpp",
+    "cxfa_nodehelper.h",
+    "cxfa_nodeiteratortemplate.h",
+    "cxfa_nodelocale.cpp",
+    "cxfa_nodelocale.h",
+    "cxfa_nodeowner.cpp",
+    "cxfa_nodeowner.h",
+    "cxfa_numberofcopies.cpp",
+    "cxfa_numberofcopies.h",
+    "cxfa_numberpattern.cpp",
+    "cxfa_numberpattern.h",
+    "cxfa_numberpatterns.cpp",
+    "cxfa_numberpatterns.h",
+    "cxfa_numbersymbol.cpp",
+    "cxfa_numbersymbol.h",
+    "cxfa_numbersymbols.cpp",
+    "cxfa_numbersymbols.h",
+    "cxfa_numericedit.cpp",
+    "cxfa_numericedit.h",
+    "cxfa_object.cpp",
+    "cxfa_object.h",
+    "cxfa_occur.cpp",
+    "cxfa_occur.h",
+    "cxfa_oid.cpp",
+    "cxfa_oid.h",
+    "cxfa_oids.cpp",
+    "cxfa_oids.h",
+    "cxfa_openaction.cpp",
+    "cxfa_openaction.h",
+    "cxfa_operation.cpp",
+    "cxfa_operation.h",
+    "cxfa_output.cpp",
+    "cxfa_output.h",
+    "cxfa_outputbin.cpp",
+    "cxfa_outputbin.h",
+    "cxfa_outputxsl.cpp",
+    "cxfa_outputxsl.h",
+    "cxfa_overflow.cpp",
+    "cxfa_overflow.h",
+    "cxfa_overprint.cpp",
+    "cxfa_overprint.h",
+    "cxfa_packet.cpp",
+    "cxfa_packet.h",
+    "cxfa_packets.cpp",
+    "cxfa_packets.h",
+    "cxfa_pagearea.cpp",
+    "cxfa_pagearea.h",
+    "cxfa_pageoffset.cpp",
+    "cxfa_pageoffset.h",
+    "cxfa_pagerange.cpp",
+    "cxfa_pagerange.h",
+    "cxfa_pageset.cpp",
+    "cxfa_pageset.h",
+    "cxfa_pagination.cpp",
+    "cxfa_pagination.h",
+    "cxfa_paginationoverride.cpp",
+    "cxfa_paginationoverride.h",
+    "cxfa_para.cpp",
+    "cxfa_para.h",
+    "cxfa_part.cpp",
+    "cxfa_part.h",
+    "cxfa_password.cpp",
+    "cxfa_password.h",
+    "cxfa_passwordedit.cpp",
+    "cxfa_passwordedit.h",
+    "cxfa_pattern.cpp",
+    "cxfa_pattern.h",
+    "cxfa_pcl.cpp",
+    "cxfa_pcl.h",
+    "cxfa_pdf.cpp",
+    "cxfa_pdf.h",
+    "cxfa_pdfa.cpp",
+    "cxfa_pdfa.h",
+    "cxfa_permissions.cpp",
+    "cxfa_permissions.h",
+    "cxfa_picktraybypdfsize.cpp",
+    "cxfa_picktraybypdfsize.h",
+    "cxfa_picture.cpp",
+    "cxfa_picture.h",
+    "cxfa_plaintextmetadata.cpp",
+    "cxfa_plaintextmetadata.h",
+    "cxfa_presence.cpp",
+    "cxfa_presence.h",
+    "cxfa_present.cpp",
+    "cxfa_present.h",
+    "cxfa_print.cpp",
+    "cxfa_print.h",
+    "cxfa_printername.cpp",
+    "cxfa_printername.h",
+    "cxfa_printhighquality.cpp",
+    "cxfa_printhighquality.h",
+    "cxfa_printscaling.cpp",
+    "cxfa_printscaling.h",
+    "cxfa_producer.cpp",
+    "cxfa_producer.h",
+    "cxfa_proto.cpp",
+    "cxfa_proto.h",
+    "cxfa_ps.cpp",
+    "cxfa_ps.h",
+    "cxfa_psmap.cpp",
+    "cxfa_psmap.h",
+    "cxfa_query.cpp",
+    "cxfa_query.h",
+    "cxfa_radial.cpp",
+    "cxfa_radial.h",
+    "cxfa_range.cpp",
+    "cxfa_range.h",
+    "cxfa_reason.cpp",
+    "cxfa_reason.h",
+    "cxfa_reasons.cpp",
+    "cxfa_reasons.h",
+    "cxfa_record.cpp",
+    "cxfa_record.h",
+    "cxfa_recordset.cpp",
+    "cxfa_recordset.h",
+    "cxfa_rectangle.cpp",
+    "cxfa_rectangle.h",
+    "cxfa_ref.cpp",
+    "cxfa_ref.h",
+    "cxfa_relevant.cpp",
+    "cxfa_relevant.h",
+    "cxfa_rename.cpp",
+    "cxfa_rename.h",
+    "cxfa_renderpolicy.cpp",
+    "cxfa_renderpolicy.h",
+    "cxfa_rootelement.cpp",
+    "cxfa_rootelement.h",
+    "cxfa_runscripts.cpp",
+    "cxfa_runscripts.h",
+    "cxfa_script.cpp",
+    "cxfa_script.h",
+    "cxfa_scriptmodel.cpp",
+    "cxfa_scriptmodel.h",
+    "cxfa_select.cpp",
+    "cxfa_select.h",
+    "cxfa_setproperty.cpp",
+    "cxfa_setproperty.h",
+    "cxfa_severity.cpp",
+    "cxfa_severity.h",
+    "cxfa_sharptext.cpp",
+    "cxfa_sharptext.h",
+    "cxfa_sharpxhtml.cpp",
+    "cxfa_sharpxhtml.h",
+    "cxfa_sharpxml.cpp",
+    "cxfa_sharpxml.h",
+    "cxfa_signature.cpp",
+    "cxfa_signature.h",
+    "cxfa_signatureproperties.cpp",
+    "cxfa_signatureproperties.h",
+    "cxfa_signdata.cpp",
+    "cxfa_signdata.h",
+    "cxfa_signing.cpp",
+    "cxfa_signing.h",
+    "cxfa_silentprint.cpp",
+    "cxfa_silentprint.h",
+    "cxfa_soapaction.cpp",
+    "cxfa_soapaction.h",
+    "cxfa_soapaddress.cpp",
+    "cxfa_soapaddress.h",
+    "cxfa_solid.cpp",
+    "cxfa_solid.h",
+    "cxfa_source.cpp",
+    "cxfa_source.h",
+    "cxfa_sourceset.cpp",
+    "cxfa_sourceset.h",
+    "cxfa_speak.cpp",
+    "cxfa_speak.h",
+    "cxfa_staple.cpp",
+    "cxfa_staple.h",
+    "cxfa_startnode.cpp",
+    "cxfa_startnode.h",
+    "cxfa_startpage.cpp",
+    "cxfa_startpage.h",
+    "cxfa_stipple.cpp",
+    "cxfa_stipple.h",
+    "cxfa_stroke.cpp",
+    "cxfa_stroke.h",
+    "cxfa_subform.cpp",
+    "cxfa_subform.h",
+    "cxfa_subformset.cpp",
+    "cxfa_subformset.h",
+    "cxfa_subjectdn.cpp",
+    "cxfa_subjectdn.h",
+    "cxfa_subjectdns.cpp",
+    "cxfa_subjectdns.h",
+    "cxfa_submit.cpp",
+    "cxfa_submit.h",
+    "cxfa_submitformat.cpp",
+    "cxfa_submitformat.h",
+    "cxfa_submiturl.cpp",
+    "cxfa_submiturl.h",
+    "cxfa_subsetbelow.cpp",
+    "cxfa_subsetbelow.h",
+    "cxfa_suppressbanner.cpp",
+    "cxfa_suppressbanner.h",
+    "cxfa_tagged.cpp",
+    "cxfa_tagged.h",
+    "cxfa_template.cpp",
+    "cxfa_template.h",
+    "cxfa_templatecache.cpp",
+    "cxfa_templatecache.h",
+    "cxfa_text.cpp",
+    "cxfa_text.h",
+    "cxfa_textedit.cpp",
+    "cxfa_textedit.h",
+    "cxfa_thisproxy.cpp",
+    "cxfa_thisproxy.h",
+    "cxfa_threshold.cpp",
+    "cxfa_threshold.h",
+    "cxfa_time.cpp",
+    "cxfa_time.h",
+    "cxfa_timepattern.cpp",
+    "cxfa_timepattern.h",
+    "cxfa_timepatterns.cpp",
+    "cxfa_timepatterns.h",
+    "cxfa_timestamp.cpp",
+    "cxfa_timestamp.h",
+    "cxfa_timezoneprovider.cpp",
+    "cxfa_timezoneprovider.h",
+    "cxfa_to.cpp",
+    "cxfa_to.h",
+    "cxfa_tooltip.cpp",
+    "cxfa_tooltip.h",
+    "cxfa_trace.cpp",
+    "cxfa_trace.h",
+    "cxfa_transform.cpp",
+    "cxfa_transform.h",
+    "cxfa_traversal.cpp",
+    "cxfa_traversal.h",
+    "cxfa_traverse.cpp",
+    "cxfa_traverse.h",
+    "cxfa_traversestrategy_xfacontainernode.h",
+    "cxfa_traversestrategy_xfanode.h",
+    "cxfa_treelist.cpp",
+    "cxfa_treelist.h",
+    "cxfa_type.cpp",
+    "cxfa_type.h",
+    "cxfa_typeface.cpp",
+    "cxfa_typeface.h",
+    "cxfa_typefaces.cpp",
+    "cxfa_typefaces.h",
+    "cxfa_ui.cpp",
+    "cxfa_ui.h",
+    "cxfa_update.cpp",
+    "cxfa_update.h",
+    "cxfa_uri.cpp",
+    "cxfa_uri.h",
+    "cxfa_user.cpp",
+    "cxfa_user.h",
+    "cxfa_validate.cpp",
+    "cxfa_validate.h",
+    "cxfa_validateapprovalsignatures.cpp",
+    "cxfa_validateapprovalsignatures.h",
+    "cxfa_validationmessaging.cpp",
+    "cxfa_validationmessaging.h",
+    "cxfa_value.cpp",
+    "cxfa_value.h",
+    "cxfa_variables.cpp",
+    "cxfa_variables.h",
+    "cxfa_version.cpp",
+    "cxfa_version.h",
+    "cxfa_versioncontrol.cpp",
+    "cxfa_versioncontrol.h",
+    "cxfa_viewerpreferences.cpp",
+    "cxfa_viewerpreferences.h",
+    "cxfa_webclient.cpp",
+    "cxfa_webclient.h",
+    "cxfa_whitespace.cpp",
+    "cxfa_whitespace.h",
+    "cxfa_window.cpp",
+    "cxfa_window.h",
+    "cxfa_wsdladdress.cpp",
+    "cxfa_wsdladdress.h",
+    "cxfa_wsdlconnection.cpp",
+    "cxfa_wsdlconnection.h",
+    "cxfa_xdc.cpp",
+    "cxfa_xdc.h",
+    "cxfa_xdp.cpp",
+    "cxfa_xdp.h",
+    "cxfa_xfa.cpp",
+    "cxfa_xfa.h",
+    "cxfa_xmlconnection.cpp",
+    "cxfa_xmlconnection.h",
+    "cxfa_xmllocale.cpp",
+    "cxfa_xmllocale.h",
+    "cxfa_xsdconnection.cpp",
+    "cxfa_xsdconnection.h",
+    "cxfa_xsl.cpp",
+    "cxfa_xsl.h",
+    "cxfa_zpl.cpp",
+    "cxfa_zpl.h",
+    "xfa_basic_data.cpp",
+    "xfa_basic_data.h",
+    "xfa_document_datamerger_imp.cpp",
+    "xfa_document_datamerger_imp.h",
+    "xfa_resolvenode_rs.h",
+    "xfa_utils.cpp",
+    "xfa_utils.h",
+  ]
+  deps = [
+    "../../../core/fxcodec",
+    "../../../core/fxcrt",
+    "../../../core/fxge",
+    "../../../fxjs",
+    "../../fde",
+    "../../fgas",
+    "../../fxgraphics",
+  ]
+  allow_circular_includes_from = [ "../../../fxjs" ]
+  configs += [
+    "../../../:pdfium_core_config",
+    "../../:xfa_warnings",
+  ]
+  visibility = [ "../../../*" ]
+}
+
+pdfium_unittest_source_set("unittests") {
+  sources = [
+    "cxfa_document_parser_unittest.cpp",
+    "cxfa_localevalue_unittest.cpp",
+    "cxfa_measurement_unittest.cpp",
+    "cxfa_node_unittest.cpp",
+    "cxfa_nodeiteratortemplate_unittest.cpp",
+    "cxfa_xmllocale_unittest.cpp",
+    "xfa_basic_data_unittest.cpp",
+    "xfa_utils_unittest.cpp",
+  ]
+  deps = [
+    ":parser",
+    "../../../fxjs",
+  ]
+  pdfium_root_dir = "../../../"
+}
+
+pdfium_embeddertest_source_set("embeddertests") {
+  sources = [ "cxfa_document_parser_embeddertest.cpp" ]
+  pdfium_root_dir = "../../../"
+}
diff --git a/xfa/fxfa/parser/DEPS b/xfa/fxfa/parser/DEPS
new file mode 100644
index 0000000..22337ef
--- /dev/null
+++ b/xfa/fxfa/parser/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  # xfa/fwl should be standalone. https://crbug.com/pdfium/507
+  '-xfa/fwl',
+]
+
diff --git a/xfa/fxfa/parser/attribute_values.inc b/xfa/fxfa/parser/attribute_values.inc
new file mode 100644
index 0000000..6cfdadf
--- /dev/null
+++ b/xfa/fxfa/parser/attribute_values.inc
@@ -0,0 +1,272 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+VALUE____(0x0000002au, "*", Asterisk)
+VALUE____(0x0000002fu, "/", Slash)
+VALUE____(0x0000005cu, "\\", Backslash)
+VALUE____(0x000239bdu, "on", On)
+VALUE____(0x00025356u, "tb", Tb)
+VALUE____(0x00025885u, "up", Up)
+VALUE____(0x0091b281u, "metaData", MetaData)
+VALUE____(0x01f8dedbu, "delegate", Delegate)
+VALUE____(0x02a6c55au, "postSubmit", PostSubmit)
+VALUE____(0x031b19c1u, "name", Name)
+VALUE____(0x0378a38au, "cross", Cross)
+VALUE____(0x03848b3fu, "next", Next)
+VALUE____(0x048b6670u, "none", None)
+VALUE____(0x051aafe5u, "shortEdge", ShortEdge)
+VALUE____(0x055264c4u, "1mod10_1mod11", Checksum_1mod10_1mod11)
+VALUE____(0x05a5c519u, "height", Height)
+VALUE____(0x089ce549u, "crossDiagonal", CrossDiagonal)
+VALUE____(0x09f9d0f9u, "all", All)
+VALUE____(0x09f9db48u, "any", Any)
+VALUE____(0x0a126261u, "toRight", ToRight)
+VALUE____(0x0a36de29u, "matchTemplate", MatchTemplate)
+VALUE____(0x0a48d040u, "dpl", Dpl)
+VALUE____(0x0a559c05u, "invisible", Invisible)
+VALUE____(0x0a7d48e3u, "fit", Fit)
+VALUE____(0x0a8a8f80u, "width", Width)
+VALUE____(0x0ab466bbu, "preSubmit", PreSubmit)
+VALUE____(0x0acc5785u, "ipl", Ipl)
+VALUE____(0x0afab0f8u, "flateCompress", FlateCompress)
+VALUE____(0x0b355816u, "med", Med)
+VALUE____(0x0b69ef77u, "odd", Odd)
+VALUE____(0x0b69f9bbu, "off", Off)
+VALUE____(0x0b843dbau, "pdf", Pdf)
+VALUE____(0x0bb912b8u, "row", Row)
+VALUE____(0x0bedaf33u, "top", Top)
+VALUE____(0x0c56afccu, "xdp", Xdp)
+VALUE____(0x0c56ba02u, "xfd", Xfd)
+VALUE____(0x0c56ddf1u, "xml", Xml)
+VALUE____(0x0c8b65f3u, "zip", Zip)
+VALUE____(0x0c8b89d6u, "zpl", Zpl)
+VALUE____(0x0f55d7eeu, "visible", Visible)
+VALUE____(0x0fe3596au, "exclude", Exclude)
+VALUE____(0x109d7ce7u, "mouseEnter", MouseEnter)
+VALUE____(0x10f1bc0cu, "pair", Pair)
+VALUE____(0x1154efe6u, "filter", Filter)
+VALUE____(0x125bc94bu, "moveLast", MoveLast)
+VALUE____(0x12e1f1f0u, "exportAndImport", ExportAndImport)
+VALUE____(0x13000c60u, "push", Push)
+VALUE____(0x138ee315u, "portrait", Portrait)
+VALUE____(0x14da2125u, "default", Default)
+VALUE____(0x157749a5u, "storedProc", StoredProc)
+VALUE____(0x16641198u, "stayBOF", StayBOF)
+VALUE____(0x16b2fc5bu, "stayEOF", StayEOF)
+VALUE____(0x17fad373u, "postPrint", PostPrint)
+VALUE____(0x193207d0u, "usCarrier", UsCarrier)
+VALUE____(0x193ade3eu, "right", Right)
+VALUE____(0x1bfc72d9u, "preOpen", PreOpen)
+VALUE____(0x1cc9317au, "actual", Actual)
+VALUE____(0x1f31df1eu, "rest", Rest)
+VALUE____(0x1fb1bf14u, "topCenter", TopCenter)
+VALUE____(0x207de667u, "standardSymbol", StandardSymbol)
+VALUE____(0x2196a452u, "initialize", Initialize)
+VALUE____(0x23bd40c7u, "justifyAll", JustifyAll)
+VALUE____(0x247cf3e9u, "normal", Normal)
+VALUE____(0x25aa946bu, "landscape", Landscape)
+VALUE____(0x2739b5c9u, "nonInteractive", NonInteractive)
+VALUE____(0x27410f03u, "mouseExit", MouseExit)
+VALUE____(0x2854e62cu, "minus", Minus)
+VALUE____(0x287e936au, "diagonalLeft", DiagonalLeft)
+VALUE____(0x2972a98fu, "simplexPaginated", SimplexPaginated)
+VALUE____(0x29d8225fu, "document", Document)
+VALUE____(0x2a9d3016u, "warning", Warning)
+VALUE____(0x2b35b6d9u, "auto", Auto)
+VALUE____(0x2c1653d9u, "below", Below)
+VALUE____(0x2c1f0540u, "bottomLeft", BottomLeft)
+VALUE____(0x2c44e816u, "bottomCenter", BottomCenter)
+VALUE____(0x2cd3e9f3u, "tcpl", Tcpl)
+VALUE____(0x2d08af85u, "text", Text)
+VALUE____(0x2dc478ebu, "grouping", Grouping)
+VALUE____(0x2ef3afddu, "secureSymbol", SecureSymbol)
+VALUE____(0x2f2dd29au, "preExecute", PreExecute)
+VALUE____(0x33c43decu, "docClose", DocClose)
+VALUE____(0x33f25bb5u, "keyset", Keyset)
+VALUE____(0x34e363dau, "vertical", Vertical)
+VALUE____(0x361fa1b6u, "preSave", PreSave)
+VALUE____(0x36f1c6d8u, "preSign", PreSign)
+VALUE____(0x399f02b5u, "bottom", Bottom)
+VALUE____(0x3b0ab096u, "toTop", ToTop)
+VALUE____(0x3c752495u, "verify", Verify)
+VALUE____(0x3ce05d68u, "first", First)
+VALUE____(0x3ecead94u, "contentArea", ContentArea)
+VALUE____(0x40623b5bu, "solid", Solid)
+VALUE____(0x42c6cd8du, "pessimistic", Pessimistic)
+VALUE____(0x43ddc6bfu, "duplexPaginated", DuplexPaginated)
+VALUE____(0x442f68c8u, "round", Round)
+VALUE____(0x45efb847u, "remerge", Remerge)
+VALUE____(0x46972265u, "ordered", Ordered)
+VALUE____(0x46f95531u, "percent", Percent)
+VALUE____(0x46fd25aeu, "even", Even)
+VALUE____(0x4731d6bau, "exit", Exit)
+VALUE____(0x4977356bu, "toolTip", ToolTip)
+VALUE____(0x49b980eeu, "orderedOccurrence", OrderedOccurrence)
+VALUE____(0x4a7e2dfeu, "readOnly", ReadOnly)
+VALUE____(0x4c4e8acbu, "currency", Currency)
+VALUE____(0x4dcf25f8u, "concat", Concat)
+VALUE____(0x4febb826u, "Thai", Thai)
+VALUE____(0x50ef95b2u, "embossed", Embossed)
+VALUE____(0x516e35ceu, "formdata", Formdata)
+VALUE____(0x52fa6f0eu, "Greek", Greek)
+VALUE____(0x54034c2fu, "decimal", Decimal)
+VALUE____(0x542c7300u, "select", Select)
+VALUE____(0x551f0ae5u, "longEdge", LongEdge)
+VALUE____(0x55520a8au, "protected", Protected)
+VALUE____(0x559f76f3u, "bottomRight", BottomRight)
+VALUE____(0x568cb500u, "zero", Zero)
+VALUE____(0x56bcecb7u, "forwardOnly", ForwardOnly)
+VALUE____(0x56bf456bu, "docReady", DocReady)
+VALUE____(0x573cb40cu, "hidden", Hidden)
+VALUE____(0x582e3424u, "include", Include)
+VALUE____(0x58a3dd29u, "dashed", Dashed)
+VALUE____(0x5955b22bu, "multiSelect", MultiSelect)
+VALUE____(0x598d5c53u, "inactive", Inactive)
+VALUE____(0x59c8f27du, "embed", Embed)
+VALUE____(0x5e7555e8u, "static", Static)
+VALUE____(0x606d4defu, "onEntry", OnEntry)
+VALUE____(0x6195eafbu, "Cyrillic", Cyrillic)
+VALUE____(0x6491b0f3u, "nonBlank", NonBlank)
+VALUE____(0x67bef031u, "topRight", TopRight)
+VALUE____(0x67df5ebdu, "Hebrew", Hebrew)
+VALUE____(0x6aea98beu, "topLeft", TopLeft)
+VALUE____(0x6c51afc1u, "center", Center)
+VALUE____(0x7145e6bfu, "moveFirst", MoveFirst)
+VALUE____(0x7375465cu, "diamond", Diamond)
+VALUE____(0x7461aef4u, "pageOdd", PageOdd)
+VALUE____(0x75f8aeb2u, "1mod10", Checksum_1mod10)
+VALUE____(0x76d708e0u, "Korean", Korean)
+VALUE____(0x789f14d7u, "aboveEmbedded", AboveEmbedded)
+VALUE____(0x792ea39fu, "zipCompress", ZipCompress)
+VALUE____(0x7a5b7193u, "numeric", Numeric)
+VALUE____(0x7abec0d2u, "circle", Circle)
+VALUE____(0x7afbba38u, "toBottom", ToBottom)
+VALUE____(0x7b95e661u, "inverted", Inverted)
+VALUE____(0x7baca2e3u, "update", Update)
+VALUE____(0x7eb5da2cu, "isoname", Isoname)
+VALUE____(0x7f6fd3d7u, "server", Server)
+VALUE____(0x814f82b5u, "position", Position)
+VALUE____(0x82deacf0u, "middleCenter", MiddleCenter)
+VALUE____(0x83a49dc6u, "optional", Optional)
+VALUE____(0x861a116fu, "usePrinterSetting", UsePrinterSetting)
+VALUE____(0x86701ce0u, "outline", Outline)
+VALUE____(0x8808385eu, "indexChange", IndexChange)
+VALUE____(0x891f4606u, "change", Change)
+VALUE____(0x89939f36u, "pageArea", PageArea)
+VALUE____(0x8b5c3b25u, "once", Once)
+VALUE____(0x8b5c6962u, "only", Only)
+VALUE____(0x8b90e1f2u, "open", Open)
+VALUE____(0x8bcfe96eu, "caption", Caption)
+VALUE____(0x8ce83ef8u, "raised", Raised)
+VALUE____(0x8d269caeu, "justify", Justify)
+VALUE____(0x8fd520dcu, "refAndDescendants", RefAndDescendants)
+VALUE____(0x9041d4b0u, "short", Short)
+VALUE____(0x90c94426u, "pageFront", PageFront)
+VALUE____(0x936beee5u, "monospace", Monospace)
+VALUE____(0x947fa00fu, "middle", Middle)
+VALUE____(0x9528a7b4u, "prePrint", PrePrint)
+VALUE____(0x959ab231u, "always", Always)
+VALUE____(0x96d61bf0u, "unknown", Unknown)
+VALUE____(0x997194eeu, "toLeft", ToLeft)
+VALUE____(0x9a83a3cdu, "above", Above)
+VALUE____(0x9d0d71c7u, "dashDot", DashDot)
+VALUE____(0x9df56f3eu, "gregorian", Gregorian)
+VALUE____(0x9f6723fdu, "Roman", Roman)
+VALUE____(0x9f693b21u, "mouseDown", MouseDown)
+VALUE____(0xa1429b36u, "symbol", Symbol)
+VALUE____(0xa5aa45cbu, "pageEven", PageEven)
+VALUE____(0xa68635f1u, "sign", Sign)
+VALUE____(0xa7315093u, "addNew", AddNew)
+VALUE____(0xa7a773fau, "star", Star)
+VALUE____(0xa7d57b45u, "optimistic", Optimistic)
+VALUE____(0xa8077321u, "rl-tb", Rl_tb)
+VALUE____(0xa8f1468du, "middleRight", MiddleRight)
+VALUE____(0xaa84a1f1u, "maintain", Maintain)
+VALUE____(0xab40b12cu, "package", Package)
+VALUE____(0xac8b4d85u, "SimplifiedChinese", SimplifiedChinese)
+VALUE____(0xadae6744u, "toCenter", ToCenter)
+VALUE____(0xb0129df1u, "back", Back)
+VALUE____(0xb0f088cfu, "unspecified", Unspecified)
+VALUE____(0xb1271067u, "batchOptimistic", BatchOptimistic)
+VALUE____(0xb18313a1u, "bold", Bold)
+VALUE____(0xb1833cadu, "both", Both)
+VALUE____(0xb221123fu, "butt", Butt)
+VALUE____(0xb40c36bfu, "client", Client)
+VALUE____(0xb56c7053u, "2mod10", Checksum_2mod10)
+VALUE____(0xb683a345u, "imageOnly", ImageOnly)
+VALUE____(0xb7732deau, "horizontal", Horizontal)
+VALUE____(0xb88652a4u, "dotted", Dotted)
+VALUE____(0xbb2f2880u, "userControl", UserControl)
+VALUE____(0xbbb79c5du, "diagonalRight", DiagonalRight)
+VALUE____(0xbd077154u, "consumeData", ConsumeData)
+VALUE____(0xbd3fb11eu, "check", Check)
+VALUE____(0xbde9abdau, "data", Data)
+VALUE____(0xbf5a02d8u, "down", Down)
+VALUE____(0xbf7450eeu, "sansSerif", SansSerif)
+VALUE____(0xc02d649fu, "inline", Inline)
+VALUE____(0xc11a9e3au, "TraditionalChinese", TraditionalChinese)
+VALUE____(0xc16169d8u, "warn", Warn)
+VALUE____(0xc16f071fu, "refOnly", RefOnly)
+VALUE____(0xc27c8ba5u, "interactiveForms", InteractiveForms)
+VALUE____(0xc2d1b15cu, "word", Word)
+VALUE____(0xc3621288u, "unordered", Unordered)
+VALUE____(0xc5251981u, "required", Required)
+VALUE____(0xc7088e7du, "importOnly", ImportOnly)
+VALUE____(0xc72cf0e3u, "belowEmbedded", BelowEmbedded)
+VALUE____(0xc819cf07u, "Japanese", Japanese)
+VALUE____(0xcdce56b3u, "full", Full)
+VALUE____(0xce0122e3u, "rl-row", Rl_row)
+VALUE____(0xcf7d71f1u, "Vietnamese", Vietnamese)
+VALUE____(0xcfde3e09u, "EastEuropeanRoman", EastEuropeanRoman)
+VALUE____(0xd576d08eu, "mouseUp", MouseUp)
+VALUE____(0xd7a92904u, "exportOnly", ExportOnly)
+VALUE____(0xd8ed1467u, "clear", Clear)
+VALUE____(0xd95657a6u, "click", Click)
+VALUE____(0xd96c7de5u, "base64", Base64)
+VALUE____(0xd9f47f36u, "close", Close)
+VALUE____(0xdb075bdeu, "host", Host)
+VALUE____(0xdb103411u, "global", Global)
+VALUE____(0xdb647188u, "blank", Blank)
+VALUE____(0xdb9be968u, "table", Table)
+VALUE____(0xdf590fbbu, "import", Import)
+VALUE____(0xe0e573fbu, "custom", Custom)
+VALUE____(0xe0ecc79au, "middleLeft", MiddleLeft)
+VALUE____(0xe1452019u, "postExecute", PostExecute)
+VALUE____(0xe1911d98u, "radix", Radix)
+VALUE____(0xe25fa7b8u, "postOpen", PostOpen)
+VALUE____(0xe28dce7eu, "enter", Enter)
+VALUE____(0xe2c44de4u, "ignore", Ignore)
+VALUE____(0xe2cd8c61u, "lr-tb", Lr_tb)
+VALUE____(0xe2da8336u, "fantasy", Fantasy)
+VALUE____(0xe31d5396u, "italic", Italic)
+VALUE____(0xe7ada113u, "author", Author)
+VALUE____(0xe8e7cc18u, "toEdge", ToEdge)
+VALUE____(0xe97aa98bu, "choice", Choice)
+VALUE____(0xeafd2a38u, "disabled", Disabled)
+VALUE____(0xeb2b7972u, "crossHatch", CrossHatch)
+VALUE____(0xeb2db2d7u, "dataRef", DataRef)
+VALUE____(0xec35dc6eu, "dashDotDot", DashDotDot)
+VALUE____(0xef85d351u, "square", Square)
+VALUE____(0xf2102445u, "dynamic", Dynamic)
+VALUE____(0xf272c7beu, "manual", Manual)
+VALUE____(0xf2bbb64du, "etched", Etched)
+VALUE____(0xf3b8fc6cu, "validationState", ValidationState)
+VALUE____(0xf42f2b81u, "cursive", Cursive)
+VALUE____(0xf54481d4u, "last", Last)
+VALUE____(0xf5ad782bu, "left", Left)
+VALUE____(0xf616da2eu, "link", Link)
+VALUE____(0xf6b4afb0u, "long", Long)
+VALUE____(0xf8636460u, "internationalCarrier", InternationalCarrier)
+VALUE____(0xf9fb37acu, "PDF1.3", PDF1_3)
+VALUE____(0xf9fb37afu, "PDF1.6", PDF1_6)
+VALUE____(0xfbce7f19u, "serif", Serif)
+VALUE____(0xfc82d695u, "postSave", PostSave)
+VALUE____(0xfcef86b5u, "ready", Ready)
+VALUE____(0xfd54fbb7u, "postSign", PostSign)
+VALUE____(0xfdc0aae2u, "Arabic", Arabic)
+VALUE____(0xfe06d2cau, "error", Error)
+VALUE____(0xfefc4885u, "urlencoded", Urlencoded)
+VALUE____(0xff795ad2u, "lowered", Lowered)
diff --git a/xfa/fxfa/parser/attributes.inc b/xfa/fxfa/parser/attributes.inc
new file mode 100644
index 0000000..7cc28e4
--- /dev/null
+++ b/xfa/fxfa/parser/attributes.inc
@@ -0,0 +1,318 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+ATTR____(0x00000068u, "h", H, Basic)
+ATTR____(0x00000077u, "w", W, Basic)
+ATTR____(0x00000078u, "x", X, Basic)
+ATTR____(0x00000079u, "y", Y, Basic)
+ATTR____(0x00020146u, "db", Db, Basic)
+ATTR____(0x00021aedu, "id", Id, Basic)
+ATTR____(0x000234a1u, "ns", Ns, Basic)
+ATTR____(0x00025363u, "to", To, Basic)
+ATTR____(0x00cb0ac9u, "lineThrough", LineThrough, Basic)
+ATTR____(0x02282c73u, "hAlign", HAlign, Basic)
+ATTR____(0x02c1c7f1u, "typeface", Typeface, Basic)
+ATTR____(0x03106c3au, "beforeTarget", BeforeTarget, Basic)
+ATTR____(0x031b19c1u, "name", Name, Basic)
+ATTR____(0x03848b3fu, "next", Next, Basic)
+ATTR____(0x043e349bu, "dataRowCount", DataRowCount, Basic)
+ATTR____(0x05518c25u, "break", Break, Basic)
+ATTR____(0x05ce6195u, "vScrollPolicy", VScrollPolicy, Basic)
+ATTR____(0x066c1ae9u, "validationsEnabled", ValidationsEnabled, Basic)
+ATTR____(0x08c74ae9u, "fontHorizontalScale", FontHorizontalScale, Basic)
+ATTR____(0x08d4f1c7u, "textIndent", TextIndent, Basic)
+ATTR____(0x097be91bu, "content", Content, Basic)
+ATTR____(0x097c1c65u, "context", Context, Object)
+ATTR____(0x09876578u, "trayOut", TrayOut, Basic)
+ATTR____(0x09f9d0f9u, "all", All, Object)
+ATTR____(0x0a2e3514u, "cap", Cap, Basic)
+ATTR____(0x0b3543a6u, "max", Max, Basic)
+ATTR____(0x0b356ca4u, "min", Min, Basic)
+ATTR____(0x0bb8df5du, "ref", Ref, Basic)
+ATTR____(0x0bb8f3dfu, "rid", Rid, Basic)
+ATTR____(0x0c080cd3u, "url", Url, Basic)
+ATTR____(0x0c0811edu, "use", Use, Basic)
+ATTR____(0x0cfea02eu, "leftInset", LeftInset, Basic)
+ATTR____(0x0d843798u, "fullText", FullText, Basic)
+ATTR____(0x0f23332fu, "errorText", ErrorText, Basic)
+ATTR____(0x0fb67185u, "recordsBefore", RecordsBefore, Basic)
+ATTR____(0x1026c59du, "widows", Widows, Basic)
+ATTR____(0x1059ec18u, "level", Level, Basic)
+ATTR____(0x1356caf8u, "bottomInset", BottomInset, Basic)
+ATTR____(0x13a08bdbu, "overflowTarget", OverflowTarget, Basic)
+ATTR____(0x1414d431u, "allowMacro", AllowMacro, Basic)
+ATTR____(0x14a32d52u, "pagePosition", PagePosition, Basic)
+ATTR____(0x14d04502u, "title", Title, Basic)
+ATTR____(0x1517dfa1u, "columnWidths", ColumnWidths, Basic)
+ATTR____(0x169134a1u, "overflowLeader", OverflowLeader, Basic)
+ATTR____(0x1abbd7e0u, "dataNode", DataNode, Object)
+ATTR____(0x1b6d1cf5u, "reenter", Reenter, Basic)
+ATTR____(0x1b8dce3eu, "action", Action, Basic)
+ATTR____(0x1e459b8fu, "nonRepudiation", NonRepudiation, Basic)
+ATTR____(0x1e6ffa9au, "prevContentType", PrevContentType, Basic)
+ATTR____(0x1ec8ab2cu, "rate", Rate, Basic)
+ATTR____(0x1ee2d24du, "instanceIndex", InstanceIndex, Basic)
+ATTR____(0x1ef3a64au, "allowRichText", AllowRichText, Basic)
+ATTR____(0x2038c9b2u, "role", Role, Basic)
+ATTR____(0x20914367u, "overflowTrailer", OverflowTrailer, Basic)
+ATTR____(0x21d5dfcbu, "currentRecordNumber", CurrentRecordNumber, Basic)
+ATTR____(0x226ca8f1u, "operation", Operation, Basic)
+ATTR____(0x24d85167u, "timeout", Timeout, Basic)
+ATTR____(0x25764436u, "topInset", TopInset, Basic)
+ATTR____(0x25839852u, "access", Access, Basic)
+ATTR____(0x25a3c206u, "soapFaultString", SoapFaultString, Basic)
+ATTR____(0x268b7ec1u, "commandType", CommandType, Basic)
+ATTR____(0x28dee6e9u, "format", Format, Basic)
+ATTR____(0x28e17e91u, "dataPrep", DataPrep, Basic)
+ATTR____(0x292b88feu, "widgetData", WidgetData, Basic)
+ATTR____(0x29418bb7u, "abbr", Abbr, Basic)
+ATTR____(0x2a82d99cu, "marginRight", MarginRight, Basic)
+ATTR____(0x2b5df51eu, "dataDescription", DataDescription, Basic)
+ATTR____(0x2bb3f470u, "encipherOnly", EncipherOnly, Basic)
+ATTR____(0x2cd79033u, "kerningMode", KerningMode, Basic)
+ATTR____(0x2d574d58u, "this", ThisValue, Object)
+ATTR____(0x2e00c007u, "newContentType", NewContentType, Basic)
+ATTR____(0x2ee7678fu, "rotate", Rotate, Basic)
+ATTR____(0x2f105f72u, "wordCharacterCount", WordCharacterCount, Basic)
+ATTR____(0x2f16a382u, "type", Type, Basic)
+ATTR____(0x312af044u, "recordsAfter", RecordsAfter, Basic)
+ATTR____(0x34ae103cu, "reserve", Reserve, Basic)
+ATTR____(0x3650557eu, "textLocation", TextLocation, Basic)
+ATTR____(0x392ae445u, "platform", Platform, Basic)
+ATTR____(0x39cdb0a2u, "priority", Priority, Basic)
+ATTR____(0x3a0273a6u, "underline", Underline, Basic)
+ATTR____(0x3b1ddd06u, "fillColor", FillColor, Basic)
+ATTR____(0x3b582286u, "moduleWidth", ModuleWidth, Basic)
+ATTR____(0x3d123c26u, "hyphenate", Hyphenate, Basic)
+ATTR____(0x3e7af94fu, "listen", Listen, Basic)
+ATTR____(0x4156ee3fu, "delimiter", Delimiter, Basic)
+ATTR____(0x42fed1fdu, "contentType", ContentType, Basic)
+ATTR____(0x453eaf38u, "startNew", StartNew, Basic)
+ATTR____(0x4570500fu, "modifier", Modifier, Basic)
+ATTR____(0x45a6daf8u, "eofAction", EofAction, Basic)
+ATTR____(0x47cfa43au, "allowNeutral", AllowNeutral, Basic)
+ATTR____(0x47d03490u, "connection", Connection, Basic)
+ATTR____(0x4873c601u, "baselineShift", BaselineShift, Basic)
+ATTR____(0x4b319767u, "overlinePeriod", OverlinePeriod, Basic)
+ATTR____(0x4b8bc840u, "fracDigits", FracDigits, Basic)
+ATTR____(0x4df15659u, "nodes", Nodes, Object)
+ATTR____(0x4ef3d02cu, "orientation", Orientation, Basic)
+ATTR____(0x4fdc3454u, "timeStamp", TimeStamp, Basic)
+ATTR____(0x50d1a9d1u, "model", Model, Object)
+ATTR____(0x50e2e33bu, "selEnd", SelEnd, Basic)
+ATTR____(0x52666f1cu, "printCheckDigit", PrintCheckDigit, Basic)
+ATTR____(0x534729c9u, "marginLeft", MarginLeft, Basic)
+ATTR____(0x5392ea58u, "stroke", Stroke, Basic)
+ATTR____(0x5404d6dfu, "moduleHeight", ModuleHeight, Basic)
+ATTR____(0x54c399e3u, "formattedValue", FormattedValue, Basic)
+ATTR____(0x54fa722cu, "transferEncoding", TransferEncoding, Basic)
+ATTR____(0x552d9ad5u, "usage", Usage, Basic)
+ATTR____(0x570ce835u, "presence", Presence, Basic)
+ATTR____(0x5739d1ffu, "radixOffset", RadixOffset, Basic)
+ATTR____(0x577682acu, "preserve", Preserve, Basic)
+ATTR____(0x57de87c2u, "prevText", PrevText, Basic)
+ATTR____(0x58be2870u, "aliasNode", AliasNode, Object)
+ATTR____(0x5a32e493u, "multiLine", MultiLine, Basic)
+ATTR____(0x5a3b375du, "borderColor", BorderColor, Basic)
+ATTR____(0x5a50e9e6u, "version", Version, Basic)
+ATTR____(0x5ab23b6cu, "startChar", StartChar, Basic)
+ATTR____(0x5b707a35u, "scriptTest", ScriptTest, Basic)
+ATTR____(0x5c054755u, "startAngle", StartAngle, Basic)
+ATTR____(0x5e936ed6u, "fontColor", FontColor, Basic)
+ATTR____(0x5ec958c0u, "cursorType", CursorType, Basic)
+ATTR____(0x5f760b50u, "digitalSignature", DigitalSignature, Basic)
+ATTR____(0x60a61eddu, "codeType", CodeType, Basic)
+ATTR____(0x60d4c8b1u, "output", Output, Basic)
+ATTR____(0x64110ab5u, "bookendTrailer", BookendTrailer, Basic)
+ATTR____(0x65e30c67u, "imagingBBox", ImagingBBox, Basic)
+ATTR____(0x66539c48u, "excludeInitialCap", ExcludeInitialCap, Basic)
+ATTR____(0x66642f8fu, "force", Force, Basic)
+ATTR____(0x66cb1eedu, "variation", Variation, Basic)
+ATTR____(0x6826c408u, "parentSubform", ParentSubform, Basic)
+ATTR____(0x69aa2292u, "crlSign", CrlSign, Basic)
+ATTR____(0x6a3405ddu, "previous", Previous, Basic)
+ATTR____(0x6a95c976u, "pushCharacterCount", PushCharacterCount, Basic)
+ATTR____(0x6aab37cbu, "isDefined", IsDefined, Basic)
+ATTR____(0x6b6ddcfbu, "nullTest", NullTest, Basic)
+ATTR____(0x6c0d9600u, "currentValue", CurrentValue, Basic)
+ATTR____(0x6cfa828au, "runAt", RunAt, Basic)
+ATTR____(0x6ea04e0au, "soapFaultCode", SoapFaultCode, Basic)
+ATTR____(0x6f544d49u, "count", Count, Basic)
+ATTR____(0x6f6556cfu, "newText", NewText, Basic)
+ATTR____(0x731e0665u, "spaceBelow", SpaceBelow, Basic)
+ATTR____(0x74788f8bu, "sweepAngle", SweepAngle, Basic)
+ATTR____(0x7717cbc4u, "language", Language, Basic)
+ATTR____(0x78a8d6cfu, "classAll", ClassAll, Object)
+ATTR____(0x78bff531u, "numberOfCells", NumberOfCells, Basic)
+ATTR____(0x79543055u, "letterSpacing", LetterSpacing, Basic)
+ATTR____(0x79975f2bu, "lockType", LockType, Basic)
+ATTR____(0x79b67434u, "mandatoryMessage", MandatoryMessage, Basic)
+ATTR____(0x7a0cc471u, "passwordChar", PasswordChar, Basic)
+ATTR____(0x7a7cc341u, "vAlign", VAlign, Basic)
+ATTR____(0x7b29630au, "sourceBelow", SourceBelow, Basic)
+ATTR____(0x7b95e661u, "inverted", Inverted, Basic)
+ATTR____(0x7c2fd80bu, "mark", Mark, Basic)
+ATTR____(0x7c2ff6aeu, "maxH", MaxH, Basic)
+ATTR____(0x7c2ff6bdu, "maxW", MaxW, Basic)
+ATTR____(0x7c732a66u, "truncate", Truncate, Basic)
+ATTR____(0x7d02356cu, "minH", MinH, Basic)
+ATTR____(0x7d02357bu, "minW", MinW, Basic)
+ATTR____(0x7d0b5fcau, "initial", Initial, Basic)
+ATTR____(0x7d9fd7c5u, "mode", Mode, Basic)
+ATTR____(0x7e7e845eu, "layout", Layout, Basic)
+ATTR____(0x7f6fd3d7u, "server", Server, Basic)
+ATTR____(0x824f21b7u, "embedPDF", EmbedPDF, Basic)
+ATTR____(0x8340ea66u, "oddOrEven", OddOrEven, Basic)
+ATTR____(0x836d4d7cu, "tabDefault", TabDefault, Basic)
+ATTR____(0x846599f8u, "transient", Transient, Basic)
+ATTR____(0x85fd6fafu, "mandatory", Mandatory, Basic)
+ATTR____(0x86698963u, "appType", AppType, Basic)
+ATTR____(0x8855805fu, "contains", Contains, Basic)
+ATTR____(0x891f4606u, "change", Change, Basic)
+ATTR____(0x8a692521u, "rightInset", RightInset, Basic)
+ATTR____(0x8af2e657u, "maxChars", MaxChars, Basic)
+ATTR____(0x8b90e1f2u, "open", Open, Basic)
+ATTR____(0x8c99377eu, "relation", Relation, Basic)
+ATTR____(0x8d181d61u, "wideNarrowRatio", WideNarrowRatio, Basic)
+ATTR____(0x8e1c2921u, "relevant", Relevant, Basic)
+ATTR____(0x8e29d794u, "signatureType", SignatureType, Basic)
+ATTR____(0x8ec6204cu, "lineThroughPeriod", LineThroughPeriod, Basic)
+ATTR____(0x8ed182d1u, "shape", Shape, Basic)
+ATTR____(0x8fa01790u, "tabStops", TabStops, Basic)
+ATTR____(0x8fa3c19eu, "shift", Shift, Basic)
+ATTR____(0x8fc36c0au, "outputBelow", OutputBelow, Basic)
+ATTR____(0x9041d4b0u, "short", Short, Basic)
+ATTR____(0x907c7719u, "fontVerticalScale", FontVerticalScale, Basic)
+ATTR____(0x942643f0u, "savedValue", SavedValue, Basic)
+ATTR____(0x94446dccu, "thickness", Thickness, Basic)
+ATTR____(0x94ff9e8du, "calculationsEnabled", CalculationsEnabled, Basic)
+ATTR____(0x957fa006u, "commitOn", CommitOn, Basic)
+ATTR____(0x964fb42eu, "formatMessage", FormatMessage, Basic)
+ATTR____(0x982bd892u, "remainCharacterCount", RemainCharacterCount, Basic)
+ATTR____(0x98fd4d81u, "keyAgreement", KeyAgreement, Basic)
+ATTR____(0x99800d7au, "errorCorrectionLevel", ErrorCorrectionLevel, Basic)
+ATTR____(0x9a63da3du, "upsMode", UpsMode, Basic)
+ATTR____(0x9cc17d75u, "mergeMode", MergeMode, Basic)
+ATTR____(0x9d833d75u, "circular", Circular, Basic)
+ATTR____(0x9d8ee204u, "psName", PsName, Basic)
+ATTR____(0x9dcc3ab3u, "trailer", Trailer, Basic)
+ATTR____(0x9f3e9510u, "instanceManager", InstanceManager, Object)
+ATTR____(0xa021b738u, "stateless", Stateless, Basic)
+ATTR____(0xa03cf627u, "rawValue", RawValue, Basic)
+ATTR____(0xa0933954u, "unicodeRange", UnicodeRange, Basic)
+ATTR____(0xa1b0d2f5u, "executeType", ExecuteType, Basic)
+ATTR____(0xa25a883du, "duplexImposition", DuplexImposition, Basic)
+ATTR____(0xa42ca1b7u, "trayIn", TrayIn, Basic)
+ATTR____(0xa433f001u, "bindingNode", BindingNode, Basic)
+ATTR____(0xa52682bdu, "{default}", DefaultValue, Basic)
+ATTR____(0xa5340ff5u, "bofAction", BofAction, Basic)
+ATTR____(0xa5b410cfu, "save", Save, Basic)
+ATTR____(0xa60dd202u, "length", Length, Basic)
+ATTR____(0xa6118c89u, "targetType", TargetType, Basic)
+ATTR____(0xa66404cbu, "keyEncipherment", KeyEncipherment, Basic)
+ATTR____(0xa6710262u, "credentialServerPolicy", CredentialServerPolicy, Basic)
+ATTR____(0xa686975bu, "size", Size, Basic)
+ATTR____(0xa85e74f3u, "initialNumber", InitialNumber, Basic)
+ATTR____(0xa9d9b2e1u, "keyDown", keyDown, Basic)
+ATTR____(0xabef37e3u, "slope", Slope, Basic)
+ATTR____(0xabfa6c4fu, "cSpace", CSpace, Basic)
+ATTR____(0xac06e2b0u, "colSpan", ColSpan, Basic)
+ATTR____(0xacb4823fu, "isContainer", IsContainer, Basic)
+ATTR____(0xadc4c77bu, "binding", Binding, Basic)
+ATTR____(0xaf754613u, "checksum", Checksum, Basic)
+ATTR____(0xb045fbc5u, "charEncoding", CharEncoding, Basic)
+ATTR____(0xb0e5485du, "bind", Bind, Basic)
+ATTR____(0xb12128b7u, "textEntry", TextEntry, Basic)
+ATTR____(0xb2c80857u, "className", ClassName, Basic)
+ATTR____(0xb373a862u, "archive", Archive, Basic)
+ATTR____(0xb598a1f7u, "uuid", Uuid, Basic)
+ATTR____(0xb5e49bf2u, "posture", Posture, Basic)
+ATTR____(0xb6b44172u, "after", After, Basic)
+ATTR____(0xb716467bu, "orphans", Orphans, Basic)
+ATTR____(0xbc0c4695u, "qualifiedName", QualifiedName, Basic)
+ATTR____(0xbc254332u, "usehref", Usehref, Basic)
+ATTR____(0xbc8fa350u, "locale", Locale, Basic)
+ATTR____(0xbcd44940u, "currentPage", CurrentPage, Basic)
+ATTR____(0xbd6e1d88u, "weight", Weight, Basic)
+ATTR____(0xbd96a0e9u, "underlinePeriod", UnderlinePeriod, Basic)
+ATTR____(0xbde9abdau, "data", Data, Basic)
+ATTR____(0xbe52dfbfu, "desc", Desc, Basic)
+ATTR____(0xbe9ba472u, "numbered", Numbered, Basic)
+ATTR____(0xbfc89db2u, "selStart", selStart, Basic)
+ATTR____(0xc035c6b1u, "dataColumnCount", DataColumnCount, Basic)
+ATTR____(0xc0ec9fa4u, "overline", Overline, Basic)
+ATTR____(0xc2ba0923u, "urlPolicy", UrlPolicy, Basic)
+ATTR____(0xc2bd40fdu, "anchorType", AnchorType, Basic)
+ATTR____(0xc32a5812u, "commitKey", CommitKey, Basic)
+ATTR____(0xc39a88bdu, "labelRef", LabelRef, Basic)
+ATTR____(0xc3c1442fu, "bookendLeader", BookendLeader, Basic)
+ATTR____(0xc4547a08u, "maxLength", MaxLength, Basic)
+ATTR____(0xc4fed09bu, "accessKey", AccessKey, Basic)
+ATTR____(0xc5762157u, "cursorLocation", CursorLocation, Basic)
+ATTR____(0xc860f30au, "delayedOpen", DelayedOpen, Basic)
+ATTR____(0xc8da4da7u, "target", Target, Basic)
+ATTR____(0xca5dc27cu, "dataEncipherment", DataEncipherment, Basic)
+ATTR____(0xcabfa3d0u, "validationMessage", ValidationMessage, Basic)
+ATTR____(0xcad6d8cau, "parent", Parent, Object)
+ATTR____(0xcb150479u, "afterTarget", AfterTarget, Basic)
+ATTR____(0xcbcaf66du, "leader", Leader, Basic)
+ATTR____(0xcca7897eu, "picker", Picker, Basic)
+ATTR____(0xcd7f7b54u, "from", From, Basic)
+ATTR____(0xcea5e62cu, "baseProfile", BaseProfile, Basic)
+ATTR____(0xd171b240u, "aspect", Aspect, Basic)
+ATTR____(0xd3c84d25u, "rowColumnRatio", RowColumnRatio, Basic)
+ATTR____(0xd4b01921u, "lineHeight", LineHeight, Basic)
+ATTR____(0xd4cc53f8u, "highlight", Highlight, Basic)
+ATTR____(0xd50f903au, "valueRef", ValueRef, Basic)
+ATTR____(0xd52482e0u, "maxEntries", MaxEntries, Basic)
+ATTR____(0xd5679c78u, "index", Index, Basic)
+ATTR____(0xd57c513cu, "dataLength", DataLength, Basic)
+ATTR____(0xd592b920u, "numPages", NumPages, Basic)
+ATTR____(0xd6128d8du, "activity", Activity, Basic)
+ATTR____(0xd6a39990u, "input", Input, Basic)
+ATTR____(0xd6e27f1du, "value", Value, Basic)
+ATTR____(0xd70798c2u, "blankOrNotBlank", BlankOrNotBlank, Basic)
+ATTR____(0xd861f8afu, "addRevocationInfo", AddRevocationInfo, Basic)
+ATTR____(0xd8624e04u, "cancelAction", cancelAction, Basic)
+ATTR____(0xd8f982bfu, "genericFamily", GenericFamily, Basic)
+ATTR____(0xd996fa9bu, "hand", Hand, Basic)
+ATTR____(0xdb55fec5u, "href", Href, Basic)
+ATTR____(0xdb5b4bceu, "classIndex", ClassIndex, Basic)
+ATTR____(0xdc75676cu, "textEncoding", TextEncoding, Basic)
+ATTR____(0xdcecd663u, "editValue", EditValue, Basic)
+ATTR____(0xde7f92bau, "leadDigits", LeadDigits, Basic)
+ATTR____(0xe07e5061u, "selectedIndex", SelectedIndex, Basic)
+ATTR____(0xe11a2cbcu, "permissions", Permissions, Basic)
+ATTR____(0xe18b5659u, "spaceAbove", SpaceAbove, Basic)
+ATTR____(0xe1a26b56u, "codeBase", CodeBase, Basic)
+ATTR____(0xe349d044u, "stock", Stock, Basic)
+ATTR____(0xe372ae97u, "isNull", IsNull, Basic)
+ATTR____(0xe4989adfu, "somExpression", SomExpression, Basic)
+ATTR____(0xe4c3a5e5u, "restoreState", RestoreState, Basic)
+ATTR____(0xe5c96d6au, "excludeAllCaps", ExcludeAllCaps, Basic)
+ATTR____(0xe64b1129u, "formatTest", FormatTest, Basic)
+ATTR____(0xe6f99487u, "hScrollPolicy", HScrollPolicy, Basic)
+ATTR____(0xe8dddf50u, "join", Join, Basic)
+ATTR____(0xe8f118a8u, "keyCertSign", KeyCertSign, Basic)
+ATTR____(0xe948b9a8u, "radius", Radius, Basic)
+ATTR____(0xe996b2feu, "sourceAbove", SourceAbove, Basic)
+ATTR____(0xea7090a0u, "override", Override, Basic)
+ATTR____(0xeb091003u, "classId", ClassId, Basic)
+ATTR____(0xeb511b54u, "disable", Disable, Basic)
+ATTR____(0xeda9017au, "scope", Scope, Basic)
+ATTR____(0xf197844du, "match", Match, Basic)
+ATTR____(0xf2009339u, "placement", Placement, Basic)
+ATTR____(0xf4ffce73u, "before", Before, Basic)
+ATTR____(0xf531b059u, "writingScript", WritingScript, Basic)
+ATTR____(0xf575ca75u, "endChar", EndChar, Basic)
+ATTR____(0xf65e34beu, "borderWidth", BorderWidth, Basic)
+ATTR____(0xf6b47749u, "lock", Lock, Basic)
+ATTR____(0xf6b4afb0u, "long", Long, Basic)
+ATTR____(0xf6b59543u, "intact", Intact, Basic)
+ATTR____(0xf889e747u, "xdpContent", XdpContent, Basic)
+ATTR____(0xfcef86b5u, "ready", Ready, Basic)
+ATTR____(0xfe612a5bu, "oneOfChild", OneOfChild, Object)
+ATTR____(0xfea53ec6u, "decipherOnly", DecipherOnly, Basic)
diff --git a/xfa/fxfa/parser/cscript_datawindow.cpp b/xfa/fxfa/parser/cscript_datawindow.cpp
index 0bf17e5..1328dba 100644
--- a/xfa/fxfa/parser/cscript_datawindow.cpp
+++ b/xfa/fxfa/parser/cscript_datawindow.cpp
@@ -16,7 +16,6 @@
     : CXFA_Object(pDocument,
                   XFA_ObjectType::Object,
                   XFA_Element::DataWindow,
-                  WideStringView(L"dataWindow"),
                   pdfium::MakeUnique<CJX_DataWindow>(this)) {}
 
 CScript_DataWindow::~CScript_DataWindow() {}
diff --git a/xfa/fxfa/parser/cscript_datawindow.h b/xfa/fxfa/parser/cscript_datawindow.h
index 819e8fe..ceed58e 100644
--- a/xfa/fxfa/parser/cscript_datawindow.h
+++ b/xfa/fxfa/parser/cscript_datawindow.h
@@ -7,17 +7,14 @@
 #ifndef XFA_FXFA_PARSER_CSCRIPT_DATAWINDOW_H_
 #define XFA_FXFA_PARSER_CSCRIPT_DATAWINDOW_H_
 
-#include "fxjs/xfa/cjx_datawindow.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
-class CScript_DataWindow : public CXFA_Object {
+class CXFA_Document;
+
+class CScript_DataWindow final : public CXFA_Object {
  public:
   explicit CScript_DataWindow(CXFA_Document* pDocument);
   ~CScript_DataWindow() override;
-
-  CJX_DataWindow* JSDataWindow() {
-    return static_cast<CJX_DataWindow*>(JSObject());
-  }
 };
 
 #endif  // XFA_FXFA_PARSER_CSCRIPT_DATAWINDOW_H_
diff --git a/xfa/fxfa/parser/cscript_eventpseudomodel.cpp b/xfa/fxfa/parser/cscript_eventpseudomodel.cpp
index da7fcb4..b4953af 100644
--- a/xfa/fxfa/parser/cscript_eventpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_eventpseudomodel.cpp
@@ -6,14 +6,13 @@
 
 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
 
-#include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/cjx_eventpseudomodel.h"
 #include "third_party/base/ptr_util.h"
 
 CScript_EventPseudoModel::CScript_EventPseudoModel(CXFA_Document* pDocument)
     : CXFA_Object(pDocument,
                   XFA_ObjectType::Object,
                   XFA_Element::EventPseudoModel,
-                  WideStringView(L"eventPseudoModel"),
                   pdfium::MakeUnique<CJX_EventPseudoModel>(this)) {}
 
-CScript_EventPseudoModel::~CScript_EventPseudoModel() {}
+CScript_EventPseudoModel::~CScript_EventPseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_eventpseudomodel.h b/xfa/fxfa/parser/cscript_eventpseudomodel.h
index efb4df0..8104162 100644
--- a/xfa/fxfa/parser/cscript_eventpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_eventpseudomodel.h
@@ -7,17 +7,14 @@
 #ifndef XFA_FXFA_PARSER_CSCRIPT_EVENTPSEUDOMODEL_H_
 #define XFA_FXFA_PARSER_CSCRIPT_EVENTPSEUDOMODEL_H_
 
-#include "fxjs/xfa/cjx_eventpseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
-class CScript_EventPseudoModel : public CXFA_Object {
+class CXFA_Document;
+
+class CScript_EventPseudoModel final : public CXFA_Object {
  public:
   explicit CScript_EventPseudoModel(CXFA_Document* pDocument);
   ~CScript_EventPseudoModel() override;
-
-  CJX_EventPseudoModel* JSEventPseudoModel() {
-    return static_cast<CJX_EventPseudoModel*>(JSObject());
-  }
 };
 
 #endif  // XFA_FXFA_PARSER_CSCRIPT_EVENTPSEUDOMODEL_H_
diff --git a/xfa/fxfa/parser/cscript_hostpseudomodel.cpp b/xfa/fxfa/parser/cscript_hostpseudomodel.cpp
index 8122aa3..4501633 100644
--- a/xfa/fxfa/parser/cscript_hostpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_hostpseudomodel.cpp
@@ -6,13 +6,13 @@
 
 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
 
+#include "fxjs/xfa/cjx_hostpseudomodel.h"
 #include "third_party/base/ptr_util.h"
 
 CScript_HostPseudoModel::CScript_HostPseudoModel(CXFA_Document* pDocument)
     : CXFA_Object(pDocument,
                   XFA_ObjectType::Object,
                   XFA_Element::HostPseudoModel,
-                  WideStringView(L"hostPseudoModel"),
                   pdfium::MakeUnique<CJX_HostPseudoModel>(this)) {}
 
-CScript_HostPseudoModel::~CScript_HostPseudoModel() {}
+CScript_HostPseudoModel::~CScript_HostPseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_hostpseudomodel.h b/xfa/fxfa/parser/cscript_hostpseudomodel.h
index 7e4a37d..c8af249 100644
--- a/xfa/fxfa/parser/cscript_hostpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_hostpseudomodel.h
@@ -7,19 +7,14 @@
 #ifndef XFA_FXFA_PARSER_CSCRIPT_HOSTPSEUDOMODEL_H_
 #define XFA_FXFA_PARSER_CSCRIPT_HOSTPSEUDOMODEL_H_
 
-#include "fxjs/xfa/cjx_hostpseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
 class CXFA_Document;
 
-class CScript_HostPseudoModel : public CXFA_Object {
+class CScript_HostPseudoModel final : public CXFA_Object {
  public:
   explicit CScript_HostPseudoModel(CXFA_Document* pDocument);
   ~CScript_HostPseudoModel() override;
-
-  CJX_HostPseudoModel* JSHostPseudoModel() {
-    return static_cast<CJX_HostPseudoModel*>(JSObject());
-  }
 };
 
 #endif  // XFA_FXFA_PARSER_CSCRIPT_HOSTPSEUDOMODEL_H_
diff --git a/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp b/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp
index 73033d5..4dfad07 100644
--- a/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_layoutpseudomodel.cpp
@@ -6,13 +6,13 @@
 
 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
 
+#include "fxjs/xfa/cjx_layoutpseudomodel.h"
 #include "third_party/base/ptr_util.h"
 
 CScript_LayoutPseudoModel::CScript_LayoutPseudoModel(CXFA_Document* pDocument)
     : CXFA_Object(pDocument,
                   XFA_ObjectType::Object,
                   XFA_Element::LayoutPseudoModel,
-                  WideStringView(L"layoutPseudoModel"),
                   pdfium::MakeUnique<CJX_LayoutPseudoModel>(this)) {}
 
-CScript_LayoutPseudoModel::~CScript_LayoutPseudoModel() {}
+CScript_LayoutPseudoModel::~CScript_LayoutPseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_layoutpseudomodel.h b/xfa/fxfa/parser/cscript_layoutpseudomodel.h
index d76013a..e88a6aa 100644
--- a/xfa/fxfa/parser/cscript_layoutpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_layoutpseudomodel.h
@@ -7,19 +7,14 @@
 #ifndef XFA_FXFA_PARSER_CSCRIPT_LAYOUTPSEUDOMODEL_H_
 #define XFA_FXFA_PARSER_CSCRIPT_LAYOUTPSEUDOMODEL_H_
 
-#include "fxjs/xfa/cjx_layoutpseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
-class CXFA_LayoutProcessor;
+class CXFA_Document;
 
-class CScript_LayoutPseudoModel : public CXFA_Object {
+class CScript_LayoutPseudoModel final : public CXFA_Object {
  public:
   explicit CScript_LayoutPseudoModel(CXFA_Document* pDocument);
   ~CScript_LayoutPseudoModel() override;
-
-  CJX_LayoutPseudoModel* JSLayoutPseudoModel() {
-    return static_cast<CJX_LayoutPseudoModel*>(JSObject());
-  }
 };
 
 #endif  // XFA_FXFA_PARSER_CSCRIPT_LAYOUTPSEUDOMODEL_H_
diff --git a/xfa/fxfa/parser/cscript_logpseudomodel.cpp b/xfa/fxfa/parser/cscript_logpseudomodel.cpp
index 1c2d802..185ac80 100644
--- a/xfa/fxfa/parser/cscript_logpseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_logpseudomodel.cpp
@@ -6,13 +6,13 @@
 
 #include "xfa/fxfa/parser/cscript_logpseudomodel.h"
 
+#include "fxjs/xfa/cjx_logpseudomodel.h"
 #include "third_party/base/ptr_util.h"
 
 CScript_LogPseudoModel::CScript_LogPseudoModel(CXFA_Document* pDocument)
     : CXFA_Object(pDocument,
                   XFA_ObjectType::Object,
                   XFA_Element::LogPseudoModel,
-                  WideStringView(L"logPseudoModel"),
                   pdfium::MakeUnique<CJX_LogPseudoModel>(this)) {}
 
 CScript_LogPseudoModel::~CScript_LogPseudoModel() {}
diff --git a/xfa/fxfa/parser/cscript_logpseudomodel.h b/xfa/fxfa/parser/cscript_logpseudomodel.h
index 9344f20..aa11bea 100644
--- a/xfa/fxfa/parser/cscript_logpseudomodel.h
+++ b/xfa/fxfa/parser/cscript_logpseudomodel.h
@@ -7,17 +7,14 @@
 #ifndef XFA_FXFA_PARSER_CSCRIPT_LOGPSEUDOMODEL_H_
 #define XFA_FXFA_PARSER_CSCRIPT_LOGPSEUDOMODEL_H_
 
-#include "fxjs/xfa/cjx_logpseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
-class CScript_LogPseudoModel : public CXFA_Object {
+class CXFA_Document;
+
+class CScript_LogPseudoModel final : public CXFA_Object {
  public:
   explicit CScript_LogPseudoModel(CXFA_Document* pDocument);
   ~CScript_LogPseudoModel() override;
-
-  CJX_LogPseudoModel* JSLogPseudoModel() {
-    return static_cast<CJX_LogPseudoModel*>(JSObject());
-  }
 };
 
 #endif  // XFA_FXFA_PARSER_CSCRIPT_LOGPSEUDOMODEL_H_
diff --git a/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp b/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp
index c5f4e64..1f92369 100644
--- a/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp
+++ b/xfa/fxfa/parser/cscript_signaturepseudomodel.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
 
+#include "fxjs/xfa/cjx_signaturepseudomodel.h"
 #include "third_party/base/ptr_util.h"
 
 CScript_SignaturePseudoModel::CScript_SignaturePseudoModel(
@@ -13,7 +14,6 @@
     : CXFA_Object(pDocument,
                   XFA_ObjectType::Object,
                   XFA_Element::SignaturePseudoModel,
-                  WideStringView(L"signaturePseudoModel"),
                   pdfium::MakeUnique<CJX_SignaturePseudoModel>(this)) {}
 
-CScript_SignaturePseudoModel::~CScript_SignaturePseudoModel() {}
+CScript_SignaturePseudoModel::~CScript_SignaturePseudoModel() = default;
diff --git a/xfa/fxfa/parser/cscript_signaturepseudomodel.h b/xfa/fxfa/parser/cscript_signaturepseudomodel.h
index 276c1df..532a767 100644
--- a/xfa/fxfa/parser/cscript_signaturepseudomodel.h
+++ b/xfa/fxfa/parser/cscript_signaturepseudomodel.h
@@ -7,17 +7,14 @@
 #ifndef XFA_FXFA_PARSER_CSCRIPT_SIGNATUREPSEUDOMODEL_H_
 #define XFA_FXFA_PARSER_CSCRIPT_SIGNATUREPSEUDOMODEL_H_
 
-#include "fxjs/xfa/cjx_signaturepseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
-class CScript_SignaturePseudoModel : public CXFA_Object {
+class CXFA_Document;
+
+class CScript_SignaturePseudoModel final : public CXFA_Object {
  public:
   explicit CScript_SignaturePseudoModel(CXFA_Document* pDocument);
   ~CScript_SignaturePseudoModel() override;
-
-  CJX_SignaturePseudoModel* JSSignaturePseudoModel() {
-    return static_cast<CJX_SignaturePseudoModel*>(JSObject());
-  }
 };
 
 #endif  // XFA_FXFA_PARSER_CSCRIPT_SIGNATUREPSEUDOMODEL_H_
diff --git a/xfa/fxfa/parser/cxfa_accessiblecontent.cpp b/xfa/fxfa/parser/cxfa_accessiblecontent.cpp
index 0a24615..4840fbb 100644
--- a/xfa/fxfa/parser/cxfa_accessiblecontent.cpp
+++ b/xfa/fxfa/parser/cxfa_accessiblecontent.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_accessiblecontent.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAccessibleContentAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"accessibleContent";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AccessibleContent,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAccessibleContentAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AccessibleContent::~CXFA_AccessibleContent() {}
+CXFA_AccessibleContent::~CXFA_AccessibleContent() = default;
diff --git a/xfa/fxfa/parser/cxfa_accessiblecontent.h b/xfa/fxfa/parser/cxfa_accessiblecontent.h
index 84ad8fc..2aea151 100644
--- a/xfa/fxfa/parser/cxfa_accessiblecontent.h
+++ b/xfa/fxfa/parser/cxfa_accessiblecontent.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AccessibleContent : public CXFA_Node {
+class CXFA_AccessibleContent final : public CXFA_Node {
  public:
   CXFA_AccessibleContent(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AccessibleContent() override;
diff --git a/xfa/fxfa/parser/cxfa_acrobat.cpp b/xfa/fxfa/parser/cxfa_acrobat.cpp
index 2327c08..301c056 100644
--- a/xfa/fxfa/parser/cxfa_acrobat.cpp
+++ b/xfa/fxfa/parser/cxfa_acrobat.cpp
@@ -6,21 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_acrobat.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kAcrobatPropertyData[] = {
     {XFA_Element::AutoSave, 1, 0},
     {XFA_Element::Validate, 1, 0},
     {XFA_Element::ValidateApprovalSignatures, 1, 0},
     {XFA_Element::Acrobat7, 1, 0},
     {XFA_Element::Common, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kAcrobatAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"acrobat";
+};
 
 }  // namespace
 
@@ -30,8 +32,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Acrobat,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kAcrobatPropertyData,
+                kAcrobatAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Acrobat::~CXFA_Acrobat() {}
+CXFA_Acrobat::~CXFA_Acrobat() = default;
diff --git a/xfa/fxfa/parser/cxfa_acrobat.h b/xfa/fxfa/parser/cxfa_acrobat.h
index 3701d59..5711178 100644
--- a/xfa/fxfa/parser/cxfa_acrobat.h
+++ b/xfa/fxfa/parser/cxfa_acrobat.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Acrobat : public CXFA_Node {
+class CXFA_Acrobat final : public CXFA_Node {
  public:
   CXFA_Acrobat(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Acrobat() override;
diff --git a/xfa/fxfa/parser/cxfa_acrobat7.cpp b/xfa/fxfa/parser/cxfa_acrobat7.cpp
index 089ab60..8b8afa4 100644
--- a/xfa/fxfa/parser/cxfa_acrobat7.cpp
+++ b/xfa/fxfa/parser/cxfa_acrobat7.cpp
@@ -6,17 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_acrobat7.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kAcrobat7PropertyData[] = {
     {XFA_Element::DynamicRender, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kAcrobat7AttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"acrobat7";
+};
 
 }  // namespace
 
@@ -26,8 +28,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Acrobat7,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kAcrobat7PropertyData,
+                kAcrobat7AttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Acrobat7::~CXFA_Acrobat7() {}
+CXFA_Acrobat7::~CXFA_Acrobat7() = default;
diff --git a/xfa/fxfa/parser/cxfa_acrobat7.h b/xfa/fxfa/parser/cxfa_acrobat7.h
index 579864a..8f64044 100644
--- a/xfa/fxfa/parser/cxfa_acrobat7.h
+++ b/xfa/fxfa/parser/cxfa_acrobat7.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Acrobat7 : public CXFA_Node {
+class CXFA_Acrobat7 final : public CXFA_Node {
  public:
   CXFA_Acrobat7(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Acrobat7() override;
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp b/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp
index af33636..cab9d62 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp
+++ b/xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp
@@ -6,26 +6,27 @@
 
 #include "xfa/fxfa/parser/cxfa_adbe_jsconsole.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kADBE_JSConsoleAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"ADBE_JSConsole";
+};
 
 }  // namespace
 
-CXFA_aDBE_JSConsole::CXFA_aDBE_JSConsole(CXFA_Document* doc,
+CXFA_ADBE_JSConsole::CXFA_ADBE_JSConsole(CXFA_Document* doc,
                                          XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ADBE_JSConsole,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kADBE_JSConsoleAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_aDBE_JSConsole::~CXFA_aDBE_JSConsole() {}
+CXFA_ADBE_JSConsole::~CXFA_ADBE_JSConsole() = default;
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsconsole.h b/xfa/fxfa/parser/cxfa_adbe_jsconsole.h
index 9fab6a1..2fef074 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsconsole.h
+++ b/xfa/fxfa/parser/cxfa_adbe_jsconsole.h
@@ -9,10 +9,10 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_aDBE_JSConsole : public CXFA_Node {
+class CXFA_ADBE_JSConsole final : public CXFA_Node {
  public:
-  CXFA_aDBE_JSConsole(CXFA_Document* doc, XFA_PacketType packet);
-  ~CXFA_aDBE_JSConsole() override;
+  CXFA_ADBE_JSConsole(CXFA_Document* doc, XFA_PacketType packet);
+  ~CXFA_ADBE_JSConsole() override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADBE_JSCONSOLE_H_
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp
index bcb62eb..01775e0 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp
+++ b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp
@@ -6,26 +6,27 @@
 
 #include "xfa/fxfa/parser/cxfa_adbe_jsdebugger.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kADBE_JSDebuggerAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"ADBE_JSDebugger";
+};
 
 }  // namespace
 
-CXFA_aDBE_JSDebugger::CXFA_aDBE_JSDebugger(CXFA_Document* doc,
+CXFA_ADBE_JSDebugger::CXFA_ADBE_JSDebugger(CXFA_Document* doc,
                                            XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ADBE_JSDebugger,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kADBE_JSDebuggerAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_aDBE_JSDebugger::~CXFA_aDBE_JSDebugger() {}
+CXFA_ADBE_JSDebugger::~CXFA_ADBE_JSDebugger() = default;
diff --git a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h
index ed821d4..34fcb0c 100644
--- a/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h
+++ b/xfa/fxfa/parser/cxfa_adbe_jsdebugger.h
@@ -9,10 +9,10 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_aDBE_JSDebugger : public CXFA_Node {
+class CXFA_ADBE_JSDebugger final : public CXFA_Node {
  public:
-  CXFA_aDBE_JSDebugger(CXFA_Document* doc, XFA_PacketType packet);
-  ~CXFA_aDBE_JSDebugger() override;
+  CXFA_ADBE_JSDebugger(CXFA_Document* doc, XFA_PacketType packet);
+  ~CXFA_ADBE_JSDebugger() override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ADBE_JSDEBUGGER_H_
diff --git a/xfa/fxfa/parser/cxfa_addsilentprint.cpp b/xfa/fxfa/parser/cxfa_addsilentprint.cpp
index 13f7f83..26b7315 100644
--- a/xfa/fxfa/parser/cxfa_addsilentprint.cpp
+++ b/xfa/fxfa/parser/cxfa_addsilentprint.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_addsilentprint.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAddSilentPrintAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"addSilentPrint";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AddSilentPrint,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAddSilentPrintAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AddSilentPrint::~CXFA_AddSilentPrint() {}
+CXFA_AddSilentPrint::~CXFA_AddSilentPrint() = default;
diff --git a/xfa/fxfa/parser/cxfa_addsilentprint.h b/xfa/fxfa/parser/cxfa_addsilentprint.h
index a251969..abfff47 100644
--- a/xfa/fxfa/parser/cxfa_addsilentprint.h
+++ b/xfa/fxfa/parser/cxfa_addsilentprint.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AddSilentPrint : public CXFA_Node {
+class CXFA_AddSilentPrint final : public CXFA_Node {
  public:
   CXFA_AddSilentPrint(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AddSilentPrint() override;
diff --git a/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp b/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp
index 3e3d060..d07fda2 100644
--- a/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp
+++ b/xfa/fxfa/parser/cxfa_addviewerpreferences.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_addviewerpreferences.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAddViewerPreferencesAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"addViewerPreferences";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AddViewerPreferences,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAddViewerPreferencesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AddViewerPreferences::~CXFA_AddViewerPreferences() {}
+CXFA_AddViewerPreferences::~CXFA_AddViewerPreferences() = default;
diff --git a/xfa/fxfa/parser/cxfa_addviewerpreferences.h b/xfa/fxfa/parser/cxfa_addviewerpreferences.h
index cf10868..62fd6e3 100644
--- a/xfa/fxfa/parser/cxfa_addviewerpreferences.h
+++ b/xfa/fxfa/parser/cxfa_addviewerpreferences.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AddViewerPreferences : public CXFA_Node {
+class CXFA_AddViewerPreferences final : public CXFA_Node {
  public:
   CXFA_AddViewerPreferences(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AddViewerPreferences() override;
diff --git a/xfa/fxfa/parser/cxfa_adjustdata.cpp b/xfa/fxfa/parser/cxfa_adjustdata.cpp
index 60a4f2c..6dbb3d4 100644
--- a/xfa/fxfa/parser/cxfa_adjustdata.cpp
+++ b/xfa/fxfa/parser/cxfa_adjustdata.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_adjustdata.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAdjustDataAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"adjustData";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::AdjustData,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAdjustDataAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AdjustData::~CXFA_AdjustData() {}
+CXFA_AdjustData::~CXFA_AdjustData() = default;
diff --git a/xfa/fxfa/parser/cxfa_adjustdata.h b/xfa/fxfa/parser/cxfa_adjustdata.h
index 3c004c2..463f2e6 100644
--- a/xfa/fxfa/parser/cxfa_adjustdata.h
+++ b/xfa/fxfa/parser/cxfa_adjustdata.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AdjustData : public CXFA_Node {
+class CXFA_AdjustData final : public CXFA_Node {
  public:
   CXFA_AdjustData(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AdjustData() override;
diff --git a/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp b/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp
index d253f8b..dce1399 100644
--- a/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp
+++ b/xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_adobeextensionlevel.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAdobeExtensionLevelAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"adobeExtensionLevel";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::AdobeExtensionLevel,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAdobeExtensionLevelAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AdobeExtensionLevel::~CXFA_AdobeExtensionLevel() {}
+CXFA_AdobeExtensionLevel::~CXFA_AdobeExtensionLevel() = default;
diff --git a/xfa/fxfa/parser/cxfa_adobeextensionlevel.h b/xfa/fxfa/parser/cxfa_adobeextensionlevel.h
index 91a830f..5e0a507 100644
--- a/xfa/fxfa/parser/cxfa_adobeextensionlevel.h
+++ b/xfa/fxfa/parser/cxfa_adobeextensionlevel.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AdobeExtensionLevel : public CXFA_Node {
+class CXFA_AdobeExtensionLevel final : public CXFA_Node {
  public:
   CXFA_AdobeExtensionLevel(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AdobeExtensionLevel() override;
diff --git a/xfa/fxfa/parser/cxfa_agent.cpp b/xfa/fxfa/parser/cxfa_agent.cpp
index 468b938..f55b7bb 100644
--- a/xfa/fxfa/parser/cxfa_agent.cpp
+++ b/xfa/fxfa/parser/cxfa_agent.cpp
@@ -6,15 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_agent.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAgentAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"agent";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Agent,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAgentAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Agent::~CXFA_Agent() {}
+CXFA_Agent::~CXFA_Agent() = default;
diff --git a/xfa/fxfa/parser/cxfa_agent.h b/xfa/fxfa/parser/cxfa_agent.h
index 65d826d..a84d2ac 100644
--- a/xfa/fxfa/parser/cxfa_agent.h
+++ b/xfa/fxfa/parser/cxfa_agent.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Agent : public CXFA_Node {
+class CXFA_Agent final : public CXFA_Node {
  public:
   CXFA_Agent(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Agent() override;
diff --git a/xfa/fxfa/parser/cxfa_alwaysembed.cpp b/xfa/fxfa/parser/cxfa_alwaysembed.cpp
index 13f9717..afbb4a0 100644
--- a/xfa/fxfa/parser/cxfa_alwaysembed.cpp
+++ b/xfa/fxfa/parser/cxfa_alwaysembed.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_alwaysembed.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAlwaysEmbedAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"alwaysEmbed";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::AlwaysEmbed,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAlwaysEmbedAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AlwaysEmbed::~CXFA_AlwaysEmbed() {}
+CXFA_AlwaysEmbed::~CXFA_AlwaysEmbed() = default;
diff --git a/xfa/fxfa/parser/cxfa_alwaysembed.h b/xfa/fxfa/parser/cxfa_alwaysembed.h
index 1780976..754fd89 100644
--- a/xfa/fxfa/parser/cxfa_alwaysembed.h
+++ b/xfa/fxfa/parser/cxfa_alwaysembed.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AlwaysEmbed : public CXFA_Node {
+class CXFA_AlwaysEmbed final : public CXFA_Node {
  public:
   CXFA_AlwaysEmbed(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AlwaysEmbed() override;
diff --git a/xfa/fxfa/parser/cxfa_amd.cpp b/xfa/fxfa/parser/cxfa_amd.cpp
index 7580304..0f6c9ef 100644
--- a/xfa/fxfa/parser/cxfa_amd.cpp
+++ b/xfa/fxfa/parser/cxfa_amd.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_amd.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAmdAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"amd";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Amd,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAmdAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Amd::~CXFA_Amd() {}
+CXFA_Amd::~CXFA_Amd() = default;
diff --git a/xfa/fxfa/parser/cxfa_amd.h b/xfa/fxfa/parser/cxfa_amd.h
index 951c6ca..6d6562c 100644
--- a/xfa/fxfa/parser/cxfa_amd.h
+++ b/xfa/fxfa/parser/cxfa_amd.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Amd : public CXFA_Node {
+class CXFA_Amd final : public CXFA_Node {
  public:
   CXFA_Amd(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Amd() override;
diff --git a/xfa/fxfa/parser/cxfa_appearancefilter.cpp b/xfa/fxfa/parser/cxfa_appearancefilter.cpp
index 9b3e9fb..6d076da 100644
--- a/xfa/fxfa/parser/cxfa_appearancefilter.cpp
+++ b/xfa/fxfa/parser/cxfa_appearancefilter.cpp
@@ -6,17 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_appearancefilter.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAppearanceFilterAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"appearanceFilter";
+};
 
 }  // namespace
 
@@ -27,8 +28,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeC,
                 XFA_Element::AppearanceFilter,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAppearanceFilterAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AppearanceFilter::~CXFA_AppearanceFilter() {}
+CXFA_AppearanceFilter::~CXFA_AppearanceFilter() = default;
diff --git a/xfa/fxfa/parser/cxfa_appearancefilter.h b/xfa/fxfa/parser/cxfa_appearancefilter.h
index 341232e..80b0ba4 100644
--- a/xfa/fxfa/parser/cxfa_appearancefilter.h
+++ b/xfa/fxfa/parser/cxfa_appearancefilter.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AppearanceFilter : public CXFA_Node {
+class CXFA_AppearanceFilter final : public CXFA_Node {
  public:
   CXFA_AppearanceFilter(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AppearanceFilter() override;
diff --git a/xfa/fxfa/parser/cxfa_arc.cpp b/xfa/fxfa/parser/cxfa_arc.cpp
index 9fb89a5..f9e2720 100644
--- a/xfa/fxfa/parser/cxfa_arc.cpp
+++ b/xfa/fxfa/parser/cxfa_arc.cpp
@@ -6,15 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_arc.h"
 
-#include "fxjs/xfa/cjx_arc.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Edge, 1, 0},
-                                                 {XFA_Element::Fill, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kArcPropertyData[] = {
+    {XFA_Element::Edge, 1, 0},
+    {XFA_Element::Fill, 1, 0},
+};
+
+const CXFA_Node::AttributeData kArcAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::StartAngle, XFA_AttributeType::Integer, (void*)0},
@@ -22,10 +24,8 @@
     {XFA_Attribute::Circular, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Hand, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Even},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"arc";
+     (void*)XFA_AttributeValue::Even},
+};
 
 }  // namespace
 
@@ -35,9 +35,8 @@
                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                XFA_ObjectType::Node,
                XFA_Element::Arc,
-               kPropertyData,
-               kAttributeData,
-               kName,
-               pdfium::MakeUnique<CJX_Arc>(this)) {}
+               kArcPropertyData,
+               kArcAttributeData,
+               pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Arc::~CXFA_Arc() {}
+CXFA_Arc::~CXFA_Arc() = default;
diff --git a/xfa/fxfa/parser/cxfa_arc.h b/xfa/fxfa/parser/cxfa_arc.h
index fd903aa..70d1b00 100644
--- a/xfa/fxfa/parser/cxfa_arc.h
+++ b/xfa/fxfa/parser/cxfa_arc.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_box.h"
 
-class CXFA_Arc : public CXFA_Box {
+class CXFA_Arc final : public CXFA_Box {
  public:
   CXFA_Arc(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Arc() override;
diff --git a/xfa/fxfa/parser/cxfa_area.cpp b/xfa/fxfa/parser/cxfa_area.cpp
index 7fd8aa6..16fa014 100644
--- a/xfa/fxfa/parser/cxfa_area.cpp
+++ b/xfa/fxfa/parser/cxfa_area.cpp
@@ -6,15 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_area.h"
 
-#include "fxjs/xfa/cjx_area.h"
+#include "fxjs/xfa/cjx_container.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Desc, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kAreaPropertyData[] = {
+    {XFA_Element::Desc, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kAreaAttributeData[] = {
     {XFA_Attribute::X, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Y, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
@@ -25,9 +27,7 @@
     {XFA_Attribute::ColSpan, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"area";
+};
 
 }  // namespace
 
@@ -38,9 +38,8 @@
           (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
           XFA_ObjectType::ContainerNode,
           XFA_Element::Area,
-          kPropertyData,
-          kAttributeData,
-          kName,
-          pdfium::MakeUnique<CJX_Area>(this)) {}
+          kAreaPropertyData,
+          kAreaAttributeData,
+          pdfium::MakeUnique<CJX_Container>(this)) {}
 
-CXFA_Area::~CXFA_Area() {}
+CXFA_Area::~CXFA_Area() = default;
diff --git a/xfa/fxfa/parser/cxfa_area.h b/xfa/fxfa/parser/cxfa_area.h
index 6568e73..cf1ff87 100644
--- a/xfa/fxfa/parser/cxfa_area.h
+++ b/xfa/fxfa/parser/cxfa_area.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Area : public CXFA_Node {
+class CXFA_Area final : public CXFA_Node {
  public:
   CXFA_Area(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Area() override;
diff --git a/xfa/fxfa/parser/cxfa_arraynodelist.cpp b/xfa/fxfa/parser/cxfa_arraynodelist.cpp
index 9eaf9a6..db36dca 100644
--- a/xfa/fxfa/parser/cxfa_arraynodelist.cpp
+++ b/xfa/fxfa/parser/cxfa_arraynodelist.cpp
@@ -6,46 +6,45 @@
 
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
 
+#include <utility>
 #include <vector>
 
-#include "third_party/base/stl_util.h"
-
 CXFA_ArrayNodeList::CXFA_ArrayNodeList(CXFA_Document* pDocument)
     : CXFA_TreeList(pDocument) {}
 
 CXFA_ArrayNodeList::~CXFA_ArrayNodeList() {}
 
-void CXFA_ArrayNodeList::SetArrayNodeList(
-    const std::vector<CXFA_Node*>& srcArray) {
+void CXFA_ArrayNodeList::SetArrayNodeList(std::vector<CXFA_Node*> srcArray) {
   if (!srcArray.empty())
-    m_array = srcArray;
+    m_array = std::move(srcArray);
 }
 
 size_t CXFA_ArrayNodeList::GetLength() {
   return m_array.size();
 }
 
-bool CXFA_ArrayNodeList::Append(CXFA_Node* pNode) {
+void CXFA_ArrayNodeList::Append(CXFA_Node* pNode) {
   m_array.push_back(pNode);
-  return true;
 }
 
 bool CXFA_ArrayNodeList::Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) {
   if (!pBeforeNode) {
     m_array.push_back(pNewNode);
-  } else {
-    auto it = std::find(m_array.begin(), m_array.end(), pBeforeNode);
-    if (it != m_array.end())
-      m_array.insert(it, pNewNode);
+    return true;
   }
+
+  auto it = std::find(m_array.begin(), m_array.end(), pBeforeNode);
+  if (it == m_array.end())
+    return false;
+
+  m_array.insert(it, pNewNode);
   return true;
 }
 
-bool CXFA_ArrayNodeList::Remove(CXFA_Node* pNode) {
+void CXFA_ArrayNodeList::Remove(CXFA_Node* pNode) {
   auto it = std::find(m_array.begin(), m_array.end(), pNode);
   if (it != m_array.end())
     m_array.erase(it);
-  return true;
 }
 
 CXFA_Node* CXFA_ArrayNodeList::Item(size_t index) {
diff --git a/xfa/fxfa/parser/cxfa_arraynodelist.h b/xfa/fxfa/parser/cxfa_arraynodelist.h
index 346b206..238c52c 100644
--- a/xfa/fxfa/parser/cxfa_arraynodelist.h
+++ b/xfa/fxfa/parser/cxfa_arraynodelist.h
@@ -14,19 +14,19 @@
 class CXFA_Document;
 class CXFA_Node;
 
-class CXFA_ArrayNodeList : public CXFA_TreeList {
+class CXFA_ArrayNodeList final : public CXFA_TreeList {
  public:
   explicit CXFA_ArrayNodeList(CXFA_Document* pDocument);
   ~CXFA_ArrayNodeList() override;
 
-  // From CXFA_TreeList.
+  // CXFA_TreeList:
   size_t GetLength() override;
-  bool Append(CXFA_Node* pNode) override;
+  void Append(CXFA_Node* pNode) override;
   bool Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) override;
-  bool Remove(CXFA_Node* pNode) override;
+  void Remove(CXFA_Node* pNode) override;
   CXFA_Node* Item(size_t iIndex) override;
 
-  void SetArrayNodeList(const std::vector<CXFA_Node*>& srcArray);
+  void SetArrayNodeList(std::vector<CXFA_Node*> srcArray);
 
  private:
   std::vector<CXFA_Node*> m_array;
diff --git a/xfa/fxfa/parser/cxfa_assist.cpp b/xfa/fxfa/parser/cxfa_assist.cpp
index 4286211..cac0bc6 100644
--- a/xfa/fxfa/parser/cxfa_assist.cpp
+++ b/xfa/fxfa/parser/cxfa_assist.cpp
@@ -6,22 +6,22 @@
 
 #include "xfa/fxfa/parser/cxfa_assist.h"
 
-#include "fxjs/xfa/cjx_assist.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::ToolTip, 1, 0},
-                                                 {XFA_Element::Speak, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kAssistPropertyData[] = {
+    {XFA_Element::ToolTip, 1, 0},
+    {XFA_Element::Speak, 1, 0},
+};
+
+const CXFA_Node::AttributeData kAssistAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Role, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"assist";
+};
 
 }  // namespace
 
@@ -31,9 +31,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Assist,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Assist>(this)) {}
+                kAssistPropertyData,
+                kAssistAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Assist::~CXFA_Assist() {}
+CXFA_Assist::~CXFA_Assist() = default;
diff --git a/xfa/fxfa/parser/cxfa_assist.h b/xfa/fxfa/parser/cxfa_assist.h
index d0e9020..77643cf 100644
--- a/xfa/fxfa/parser/cxfa_assist.h
+++ b/xfa/fxfa/parser/cxfa_assist.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Assist : public CXFA_Node {
+class CXFA_Assist final : public CXFA_Node {
  public:
   CXFA_Assist(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Assist() override;
diff --git a/xfa/fxfa/parser/cxfa_attachnodelist.cpp b/xfa/fxfa/parser/cxfa_attachnodelist.cpp
index f1fbfa7..6d8e5c3 100644
--- a/xfa/fxfa/parser/cxfa_attachnodelist.cpp
+++ b/xfa/fxfa/parser/cxfa_attachnodelist.cpp
@@ -11,9 +11,9 @@
 
 CXFA_AttachNodeList::CXFA_AttachNodeList(CXFA_Document* pDocument,
                                          CXFA_Node* pAttachNode)
-    : CXFA_TreeList(pDocument) {
-  m_pAttachNode = pAttachNode;
-}
+    : CXFA_TreeList(pDocument), m_pAttachNode(pAttachNode) {}
+
+CXFA_AttachNodeList::~CXFA_AttachNodeList() = default;
 
 size_t CXFA_AttachNodeList::GetLength() {
   return m_pAttachNode->CountChildren(
@@ -21,24 +21,28 @@
       m_pAttachNode->GetElementType() == XFA_Element::Subform);
 }
 
-bool CXFA_AttachNodeList::Append(CXFA_Node* pNode) {
+void CXFA_AttachNodeList::Append(CXFA_Node* pNode) {
   CXFA_Node* pParent = pNode->GetParent();
   if (pParent)
-    pParent->RemoveChild(pNode, true);
+    pParent->RemoveChildAndNotify(pNode, true);
 
-  return m_pAttachNode->InsertChild(pNode, nullptr);
+  m_pAttachNode->InsertChildAndNotify(pNode, nullptr);
 }
 
 bool CXFA_AttachNodeList::Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) {
+  if (pBeforeNode && pBeforeNode->GetParent() != m_pAttachNode)
+    return false;
+
   CXFA_Node* pParent = pNewNode->GetParent();
   if (pParent)
-    pParent->RemoveChild(pNewNode, true);
+    pParent->RemoveChildAndNotify(pNewNode, true);
 
-  return m_pAttachNode->InsertChild(pNewNode, pBeforeNode);
+  m_pAttachNode->InsertChildAndNotify(pNewNode, pBeforeNode);
+  return true;
 }
 
-bool CXFA_AttachNodeList::Remove(CXFA_Node* pNode) {
-  return m_pAttachNode->RemoveChild(pNode, true);
+void CXFA_AttachNodeList::Remove(CXFA_Node* pNode) {
+  m_pAttachNode->RemoveChildAndNotify(pNode, true);
 }
 
 CXFA_Node* CXFA_AttachNodeList::Item(size_t index) {
diff --git a/xfa/fxfa/parser/cxfa_attachnodelist.h b/xfa/fxfa/parser/cxfa_attachnodelist.h
index f0df2bb..2b20d70 100644
--- a/xfa/fxfa/parser/cxfa_attachnodelist.h
+++ b/xfa/fxfa/parser/cxfa_attachnodelist.h
@@ -12,19 +12,20 @@
 class CXFA_Document;
 class CXFA_Node;
 
-class CXFA_AttachNodeList : public CXFA_TreeList {
+class CXFA_AttachNodeList final : public CXFA_TreeList {
  public:
   CXFA_AttachNodeList(CXFA_Document* pDocument, CXFA_Node* pAttachNode);
+  ~CXFA_AttachNodeList() override;
 
-  // From CXFA_TreeList.
+  // CXFA_TreeList:
   size_t GetLength() override;
-  bool Append(CXFA_Node* pNode) override;
+  void Append(CXFA_Node* pNode) override;
   bool Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) override;
-  bool Remove(CXFA_Node* pNode) override;
+  void Remove(CXFA_Node* pNode) override;
   CXFA_Node* Item(size_t iIndex) override;
 
  private:
-  CXFA_Node* m_pAttachNode;
+  UnownedPtr<CXFA_Node> const m_pAttachNode;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_ATTACHNODELIST_H_
diff --git a/xfa/fxfa/parser/cxfa_attributes.cpp b/xfa/fxfa/parser/cxfa_attributes.cpp
index 80e58bd..aeaf8cf 100644
--- a/xfa/fxfa/parser/cxfa_attributes.cpp
+++ b/xfa/fxfa/parser/cxfa_attributes.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_attributes.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAttributesAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"attributes";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Attributes,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAttributesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Attributes::~CXFA_Attributes() {}
+CXFA_Attributes::~CXFA_Attributes() = default;
diff --git a/xfa/fxfa/parser/cxfa_attributes.h b/xfa/fxfa/parser/cxfa_attributes.h
index fd73da8..099ecea 100644
--- a/xfa/fxfa/parser/cxfa_attributes.h
+++ b/xfa/fxfa/parser/cxfa_attributes.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Attributes : public CXFA_Node {
+class CXFA_Attributes final : public CXFA_Node {
  public:
   CXFA_Attributes(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Attributes() override;
diff --git a/xfa/fxfa/parser/cxfa_autosave.cpp b/xfa/fxfa/parser/cxfa_autosave.cpp
index d77c252..978cd56 100644
--- a/xfa/fxfa/parser/cxfa_autosave.cpp
+++ b/xfa/fxfa/parser/cxfa_autosave.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_autosave.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kAutoSaveAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"autoSave";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::AutoSave,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kAutoSaveAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_AutoSave::~CXFA_AutoSave() {}
+CXFA_AutoSave::~CXFA_AutoSave() = default;
diff --git a/xfa/fxfa/parser/cxfa_autosave.h b/xfa/fxfa/parser/cxfa_autosave.h
index b58ccf1..5324351 100644
--- a/xfa/fxfa/parser/cxfa_autosave.h
+++ b/xfa/fxfa/parser/cxfa_autosave.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_AutoSave : public CXFA_Node {
+class CXFA_AutoSave final : public CXFA_Node {
  public:
   CXFA_AutoSave(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_AutoSave() override;
diff --git a/xfa/fxfa/parser/cxfa_barcode.cpp b/xfa/fxfa/parser/cxfa_barcode.cpp
index d692291..a7dbb71 100644
--- a/xfa/fxfa/parser/cxfa_barcode.cpp
+++ b/xfa/fxfa/parser/cxfa_barcode.cpp
@@ -6,20 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_barcode.h"
 
-#include "fxjs/xfa/cjx_barcode.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kBarcodeAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DataRowCount, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DataPrep, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::Type, XFA_AttributeType::CData, (void*)nullptr},
     {XFA_Attribute::TextLocation, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Below},
+     (void*)XFA_AttributeValue::Below},
     {XFA_Attribute::ModuleWidth, XFA_AttributeType::Measure, (void*)L"0.25mm"},
     {XFA_Attribute::PrintCheckDigit, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::ModuleHeight, XFA_AttributeType::Measure, (void*)L"5mm"},
@@ -28,18 +29,16 @@
     {XFA_Attribute::WideNarrowRatio, XFA_AttributeType::CData, (void*)L"3:1"},
     {XFA_Attribute::ErrorCorrectionLevel, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::UpsMode, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::UsCarrier},
+     (void*)XFA_AttributeValue::UsCarrier},
     {XFA_Attribute::Checksum, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::CharEncoding, XFA_AttributeType::CData, (void*)L"UTF-8"},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DataColumnCount, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::RowColumnRatio, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DataLength, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::EndChar, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"barcode";
+};
 
 }  // namespace
 
@@ -49,9 +48,126 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Barcode,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Barcode>(this)) {}
+                {},
+                kBarcodeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Barcode::~CXFA_Barcode() {}
+CXFA_Barcode::~CXFA_Barcode() = default;
+
+XFA_FFWidgetType CXFA_Barcode::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kBarcode;
+}
+
+WideString CXFA_Barcode::GetBarcodeType() {
+  return WideString(JSObject()->GetCData(XFA_Attribute::Type));
+}
+
+Optional<WideString> CXFA_Barcode::GetCharEncoding() {
+  return JSObject()->TryCData(XFA_Attribute::CharEncoding, true);
+}
+
+Optional<bool> CXFA_Barcode::GetChecksum() {
+  Optional<XFA_AttributeValue> checksum =
+      JSObject()->TryEnum(XFA_Attribute::Checksum, true);
+  if (!checksum)
+    return {};
+
+  switch (*checksum) {
+    case XFA_AttributeValue::None:
+      return {false};
+    case XFA_AttributeValue::Auto:
+      return {true};
+    case XFA_AttributeValue::Checksum_1mod10:
+    case XFA_AttributeValue::Checksum_1mod10_1mod11:
+    case XFA_AttributeValue::Checksum_2mod10:
+    default:
+      break;
+  }
+  return {};
+}
+
+Optional<int32_t> CXFA_Barcode::GetDataLength() {
+  Optional<WideString> wsDataLength =
+      JSObject()->TryCData(XFA_Attribute::DataLength, true);
+  if (!wsDataLength)
+    return {};
+
+  return {FXSYS_wtoi(wsDataLength->c_str())};
+}
+
+Optional<char> CXFA_Barcode::GetStartChar() {
+  Optional<WideString> wsStartEndChar =
+      JSObject()->TryCData(XFA_Attribute::StartChar, true);
+  if (!wsStartEndChar || wsStartEndChar->IsEmpty())
+    return {};
+
+  return {static_cast<char>((*wsStartEndChar)[0])};
+}
+
+Optional<char> CXFA_Barcode::GetEndChar() {
+  Optional<WideString> wsStartEndChar =
+      JSObject()->TryCData(XFA_Attribute::EndChar, true);
+  if (!wsStartEndChar || wsStartEndChar->IsEmpty())
+    return {};
+
+  return {static_cast<char>((*wsStartEndChar)[0])};
+}
+
+Optional<int32_t> CXFA_Barcode::GetECLevel() {
+  Optional<WideString> wsECLevel =
+      JSObject()->TryCData(XFA_Attribute::ErrorCorrectionLevel, true);
+  if (!wsECLevel)
+    return {};
+  return {FXSYS_wtoi(wsECLevel->c_str())};
+}
+
+Optional<int32_t> CXFA_Barcode::GetModuleWidth() {
+  Optional<CXFA_Measurement> moduleWidthHeight =
+      JSObject()->TryMeasure(XFA_Attribute::ModuleWidth, true);
+  if (!moduleWidthHeight)
+    return {};
+
+  return {static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt))};
+}
+
+Optional<int32_t> CXFA_Barcode::GetModuleHeight() {
+  Optional<CXFA_Measurement> moduleWidthHeight =
+      JSObject()->TryMeasure(XFA_Attribute::ModuleHeight, true);
+  if (!moduleWidthHeight)
+    return {};
+
+  return {static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt))};
+}
+
+Optional<bool> CXFA_Barcode::GetPrintChecksum() {
+  return JSObject()->TryBoolean(XFA_Attribute::PrintCheckDigit, true);
+}
+
+Optional<XFA_AttributeValue> CXFA_Barcode::GetTextLocation() {
+  return JSObject()->TryEnum(XFA_Attribute::TextLocation, true);
+}
+
+Optional<bool> CXFA_Barcode::GetTruncate() {
+  return JSObject()->TryBoolean(XFA_Attribute::Truncate, true);
+}
+
+Optional<int8_t> CXFA_Barcode::GetWideNarrowRatio() {
+  Optional<WideString> wsWideNarrowRatio =
+      JSObject()->TryCData(XFA_Attribute::WideNarrowRatio, true);
+  if (!wsWideNarrowRatio)
+    return {};
+
+  Optional<size_t> ptPos = wsWideNarrowRatio->Find(':');
+  if (!ptPos)
+    return {static_cast<int8_t>(FXSYS_wtoi(wsWideNarrowRatio->c_str()))};
+
+  int32_t fB = FXSYS_wtoi(
+      wsWideNarrowRatio->Last(wsWideNarrowRatio->GetLength() - (*ptPos + 1))
+          .c_str());
+  if (!fB)
+    return {0};
+
+  int32_t fA = FXSYS_wtoi(wsWideNarrowRatio->First(*ptPos).c_str());
+  float result = static_cast<float>(fA) / static_cast<float>(fB);
+  return {static_cast<int8_t>(result)};
+}
diff --git a/xfa/fxfa/parser/cxfa_barcode.h b/xfa/fxfa/parser/cxfa_barcode.h
index 469889b..219e8b3 100644
--- a/xfa/fxfa/parser/cxfa_barcode.h
+++ b/xfa/fxfa/parser/cxfa_barcode.h
@@ -7,12 +7,29 @@
 #ifndef XFA_FXFA_PARSER_CXFA_BARCODE_H_
 #define XFA_FXFA_PARSER_CXFA_BARCODE_H_
 
+#include "third_party/base/optional.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Barcode : public CXFA_Node {
+class CXFA_Barcode final : public CXFA_Node {
  public:
   CXFA_Barcode(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Barcode() override;
+
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+  WideString GetBarcodeType();
+  Optional<WideString> GetCharEncoding();
+  Optional<bool> GetChecksum();
+  Optional<int32_t> GetDataLength();
+  Optional<char> GetStartChar();
+  Optional<char> GetEndChar();
+  Optional<int32_t> GetECLevel();
+  Optional<int32_t> GetModuleWidth();
+  Optional<int32_t> GetModuleHeight();
+  Optional<bool> GetPrintChecksum();
+  Optional<XFA_AttributeValue> GetTextLocation();
+  Optional<bool> GetTruncate();
+  Optional<int8_t> GetWideNarrowRatio();
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BARCODE_H_
diff --git a/xfa/fxfa/parser/cxfa_base.cpp b/xfa/fxfa/parser/cxfa_base.cpp
index a82a79a..c5d0f88 100644
--- a/xfa/fxfa/parser/cxfa_base.cpp
+++ b/xfa/fxfa/parser/cxfa_base.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_base.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kBaseAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"base";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Base,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kBaseAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Base::~CXFA_Base() {}
+CXFA_Base::~CXFA_Base() = default;
diff --git a/xfa/fxfa/parser/cxfa_base.h b/xfa/fxfa/parser/cxfa_base.h
index 3974343..3f1c9ba 100644
--- a/xfa/fxfa/parser/cxfa_base.h
+++ b/xfa/fxfa/parser/cxfa_base.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Base : public CXFA_Node {
+class CXFA_Base final : public CXFA_Node {
  public:
   CXFA_Base(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Base() override;
diff --git a/xfa/fxfa/parser/cxfa_batchoutput.cpp b/xfa/fxfa/parser/cxfa_batchoutput.cpp
index 1678d6a..daec03f 100644
--- a/xfa/fxfa/parser/cxfa_batchoutput.cpp
+++ b/xfa/fxfa/parser/cxfa_batchoutput.cpp
@@ -6,16 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_batchoutput.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kBatchOutputAttributeData[] = {
     {XFA_Attribute::Format, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"batchOutput";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::BatchOutput,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kBatchOutputAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_BatchOutput::~CXFA_BatchOutput() {}
+CXFA_BatchOutput::~CXFA_BatchOutput() = default;
diff --git a/xfa/fxfa/parser/cxfa_batchoutput.h b/xfa/fxfa/parser/cxfa_batchoutput.h
index 9faa6cf..8d1de09 100644
--- a/xfa/fxfa/parser/cxfa_batchoutput.h
+++ b/xfa/fxfa/parser/cxfa_batchoutput.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_BatchOutput : public CXFA_Node {
+class CXFA_BatchOutput final : public CXFA_Node {
  public:
   CXFA_BatchOutput(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_BatchOutput() override;
diff --git a/xfa/fxfa/parser/cxfa_behavioroverride.cpp b/xfa/fxfa/parser/cxfa_behavioroverride.cpp
index addf8f1..86ea80b 100644
--- a/xfa/fxfa/parser/cxfa_behavioroverride.cpp
+++ b/xfa/fxfa/parser/cxfa_behavioroverride.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_behavioroverride.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kBehaviorOverrideAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"behaviorOverride";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::BehaviorOverride,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kBehaviorOverrideAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_BehaviorOverride::~CXFA_BehaviorOverride() {}
+CXFA_BehaviorOverride::~CXFA_BehaviorOverride() = default;
diff --git a/xfa/fxfa/parser/cxfa_behavioroverride.h b/xfa/fxfa/parser/cxfa_behavioroverride.h
index cb08211..d7dce1b 100644
--- a/xfa/fxfa/parser/cxfa_behavioroverride.h
+++ b/xfa/fxfa/parser/cxfa_behavioroverride.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_BehaviorOverride : public CXFA_Node {
+class CXFA_BehaviorOverride final : public CXFA_Node {
  public:
   CXFA_BehaviorOverride(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_BehaviorOverride() override;
diff --git a/xfa/fxfa/parser/cxfa_bind.cpp b/xfa/fxfa/parser/cxfa_bind.cpp
index d35a2cd..87f01e8 100644
--- a/xfa/fxfa/parser/cxfa_bind.cpp
+++ b/xfa/fxfa/parser/cxfa_bind.cpp
@@ -6,29 +6,29 @@
 
 #include "xfa/fxfa/parser/cxfa_bind.h"
 
-#include "fxjs/xfa/cjx_bind.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_picture.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Picture, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kBindPropertyData[] = {
+    {XFA_Element::Picture, 1, 0},
+};
+
+const CXFA_Node::AttributeData kBindAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ContentType, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TransferEncoding, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Match, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Once},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"bind";
+     (void*)XFA_AttributeValue::Once},
+};
 
 }  // namespace
 
@@ -39,15 +39,14 @@
                  XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Bind,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Bind>(this)) {}
+                kBindPropertyData,
+                kBindAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Bind::~CXFA_Bind() {}
+CXFA_Bind::~CXFA_Bind() = default;
 
 WideString CXFA_Bind::GetPicture() {
   CXFA_Picture* pPicture =
       GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
-  return pPicture ? pPicture->JSObject()->GetContent(false) : L"";
+  return pPicture ? pPicture->JSObject()->GetContent(false) : WideString();
 }
diff --git a/xfa/fxfa/parser/cxfa_bind.h b/xfa/fxfa/parser/cxfa_bind.h
index 1924cb6..f6331d6 100644
--- a/xfa/fxfa/parser/cxfa_bind.h
+++ b/xfa/fxfa/parser/cxfa_bind.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Bind : public CXFA_Node {
+class CXFA_Bind final : public CXFA_Node {
  public:
   CXFA_Bind(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Bind() override;
diff --git a/xfa/fxfa/parser/cxfa_binditems.cpp b/xfa/fxfa/parser/cxfa_binditems.cpp
index d7d72a7..44badb8 100644
--- a/xfa/fxfa/parser/cxfa_binditems.cpp
+++ b/xfa/fxfa/parser/cxfa_binditems.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_binditems.h"
 
-#include "fxjs/xfa/cjx_binditems.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kBindItemsAttributeData[] = {
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Connection, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::LabelRef, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ValueRef, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"bindItems";
+};
 
 }  // namespace
 
@@ -28,12 +26,11 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::BindItems,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_BindItems>(this)) {}
+                {},
+                kBindItemsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_BindItems::~CXFA_BindItems() {}
+CXFA_BindItems::~CXFA_BindItems() = default;
 
 WideString CXFA_BindItems::GetLabelRef() {
   return JSObject()->GetCData(XFA_Attribute::LabelRef);
diff --git a/xfa/fxfa/parser/cxfa_binditems.h b/xfa/fxfa/parser/cxfa_binditems.h
index f1a490d..34a4b67 100644
--- a/xfa/fxfa/parser/cxfa_binditems.h
+++ b/xfa/fxfa/parser/cxfa_binditems.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_BindItems : public CXFA_Node {
+class CXFA_BindItems final : public CXFA_Node {
  public:
   CXFA_BindItems(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_BindItems() override;
diff --git a/xfa/fxfa/parser/cxfa_bookend.cpp b/xfa/fxfa/parser/cxfa_bookend.cpp
index 6b172be..143a484 100644
--- a/xfa/fxfa/parser/cxfa_bookend.cpp
+++ b/xfa/fxfa/parser/cxfa_bookend.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_bookend.h"
 
-#include "fxjs/xfa/cjx_bookend.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kBookendAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Trailer, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Leader, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"bookend";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Bookend,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Bookend>(this)) {}
+                {},
+                kBookendAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Bookend::~CXFA_Bookend() {}
+CXFA_Bookend::~CXFA_Bookend() = default;
diff --git a/xfa/fxfa/parser/cxfa_bookend.h b/xfa/fxfa/parser/cxfa_bookend.h
index 82eb13c..1c68763 100644
--- a/xfa/fxfa/parser/cxfa_bookend.h
+++ b/xfa/fxfa/parser/cxfa_bookend.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Bookend : public CXFA_Node {
+class CXFA_Bookend final : public CXFA_Node {
  public:
   CXFA_Bookend(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Bookend() override;
diff --git a/xfa/fxfa/parser/cxfa_boolean.cpp b/xfa/fxfa/parser/cxfa_boolean.cpp
index f804919..919d679 100644
--- a/xfa/fxfa/parser/cxfa_boolean.cpp
+++ b/xfa/fxfa/parser/cxfa_boolean.cpp
@@ -11,14 +11,12 @@
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kBooleanAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"boolean";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                  XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Boolean,
-                nullptr,
-                kAttributeData,
-                kName,
+                {},
+                kBooleanAttributeData,
                 pdfium::MakeUnique<CJX_Boolean>(this)) {}
 
 CXFA_Boolean::~CXFA_Boolean() {}
diff --git a/xfa/fxfa/parser/cxfa_boolean.h b/xfa/fxfa/parser/cxfa_boolean.h
index 4121e36..60b8e5e 100644
--- a/xfa/fxfa/parser/cxfa_boolean.h
+++ b/xfa/fxfa/parser/cxfa_boolean.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Boolean : public CXFA_Node {
+class CXFA_Boolean final : public CXFA_Node {
  public:
   CXFA_Boolean(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Boolean() override;
diff --git a/xfa/fxfa/parser/cxfa_border.cpp b/xfa/fxfa/parser/cxfa_border.cpp
index eb87562..1d315ad 100644
--- a/xfa/fxfa/parser/cxfa_border.cpp
+++ b/xfa/fxfa/parser/cxfa_border.cpp
@@ -6,29 +6,29 @@
 
 #include "xfa/fxfa/parser/cxfa_border.h"
 
-#include "fxjs/xfa/cjx_border.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kBorderPropertyData[] = {
     {XFA_Element::Margin, 1, 0}, {XFA_Element::Edge, 4, 0},
     {XFA_Element::Corner, 4, 0}, {XFA_Element::Fill, 1, 0},
-    {XFA_Element::Extras, 1, 0}, {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kBorderAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Break, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Close},
+     (void*)XFA_AttributeValue::Close},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Hand, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Even},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"border";
+     (void*)XFA_AttributeValue::Even},
+};
 
 }  // namespace
 
@@ -38,9 +38,8 @@
                      (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                      XFA_ObjectType::Node,
                      XFA_Element::Border,
-                     kPropertyData,
-                     kAttributeData,
-                     kName,
-                     pdfium::MakeUnique<CJX_Border>(this)) {}
+                     kBorderPropertyData,
+                     kBorderAttributeData,
+                     pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Border::~CXFA_Border() {}
+CXFA_Border::~CXFA_Border() = default;
diff --git a/xfa/fxfa/parser/cxfa_border.h b/xfa/fxfa/parser/cxfa_border.h
index 24f3bb9..6071a77 100644
--- a/xfa/fxfa/parser/cxfa_border.h
+++ b/xfa/fxfa/parser/cxfa_border.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_rectangle.h"
 
-class CXFA_Border : public CXFA_Rectangle {
+class CXFA_Border final : public CXFA_Rectangle {
  public:
   CXFA_Border(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Border() override;
diff --git a/xfa/fxfa/parser/cxfa_box.cpp b/xfa/fxfa/parser/cxfa_box.cpp
index b814612..5ba01d3 100644
--- a/xfa/fxfa/parser/cxfa_box.cpp
+++ b/xfa/fxfa/parser/cxfa_box.cpp
@@ -24,10 +24,10 @@
 
 namespace {
 
-std::pair<XFA_AttributeEnum, CXFA_Stroke*> Style3D(
+std::pair<XFA_AttributeValue, CXFA_Stroke*> Style3D(
     const std::vector<CXFA_Stroke*>& strokes) {
   if (strokes.empty())
-    return {XFA_AttributeEnum::Unknown, nullptr};
+    return {XFA_AttributeValue::Unknown, nullptr};
 
   CXFA_Stroke* stroke = strokes[0];
   for (size_t i = 1; i < strokes.size(); i++) {
@@ -41,14 +41,14 @@
     break;
   }
 
-  XFA_AttributeEnum iType = stroke->GetStrokeType();
-  if (iType == XFA_AttributeEnum::Lowered ||
-      iType == XFA_AttributeEnum::Raised ||
-      iType == XFA_AttributeEnum::Etched ||
-      iType == XFA_AttributeEnum::Embossed) {
+  XFA_AttributeValue iType = stroke->GetStrokeType();
+  if (iType == XFA_AttributeValue::Lowered ||
+      iType == XFA_AttributeValue::Raised ||
+      iType == XFA_AttributeValue::Etched ||
+      iType == XFA_AttributeValue::Embossed) {
     return {iType, stroke};
   }
-  return {XFA_AttributeEnum::Unknown, stroke};
+  return {XFA_AttributeValue::Unknown, stroke};
 }
 
 CXFA_Rectangle* ToRectangle(CXFA_Box* box) {
@@ -62,9 +62,8 @@
                    uint32_t validPackets,
                    XFA_ObjectType oType,
                    XFA_Element eType,
-                   const PropertyData* properties,
-                   const AttributeData* attributes,
-                   const WideStringView& elementName,
+                   pdfium::span<const PropertyData> properties,
+                   pdfium::span<const AttributeData> attributes,
                    std::unique_ptr<CJX_Object> js_node)
     : CXFA_Node(pDoc,
                 ePacket,
@@ -73,19 +72,18 @@
                 eType,
                 properties,
                 attributes,
-                elementName,
                 std::move(js_node)) {}
 
 CXFA_Box::~CXFA_Box() = default;
 
-XFA_AttributeEnum CXFA_Box::GetHand() {
+XFA_AttributeValue CXFA_Box::GetHand() {
   return JSObject()->GetEnum(XFA_Attribute::Hand);
 }
 
-XFA_AttributeEnum CXFA_Box::GetPresence() {
+XFA_AttributeValue CXFA_Box::GetPresence() {
   return JSObject()
       ->TryEnum(XFA_Attribute::Presence, true)
-      .value_or(XFA_AttributeEnum::Visible);
+      .value_or(XFA_AttributeValue::Visible);
 }
 
 int32_t CXFA_Box::CountEdges() {
@@ -119,17 +117,17 @@
   return JSObject()->GetOrCreateProperty<CXFA_Fill>(0, XFA_Element::Fill);
 }
 
-std::tuple<XFA_AttributeEnum, bool, float> CXFA_Box::Get3DStyle() {
+std::tuple<XFA_AttributeValue, bool, float> CXFA_Box::Get3DStyle() {
   if (GetElementType() == XFA_Element::Arc)
-    return {XFA_AttributeEnum::Unknown, false, 0.0f};
+    return {XFA_AttributeValue::Unknown, false, 0.0f};
 
   std::vector<CXFA_Stroke*> strokes = GetStrokesInternal(true);
   CXFA_Stroke* stroke;
-  XFA_AttributeEnum iType;
+  XFA_AttributeValue iType;
 
   std::tie(iType, stroke) = Style3D(strokes);
-  if (iType == XFA_AttributeEnum::Unknown)
-    return {XFA_AttributeEnum::Unknown, false, 0.0f};
+  if (iType == XFA_AttributeValue::Unknown)
+    return {XFA_AttributeValue::Unknown, false, 0.0f};
 
   return {iType, stroke->IsVisible(), stroke->GetThickness()};
 }
@@ -184,7 +182,7 @@
                     const CFX_RectF& rtWidget,
                     const CFX_Matrix& matrix,
                     bool forceRound) {
-  if (GetPresence() != XFA_AttributeEnum::Visible)
+  if (GetPresence() != XFA_AttributeValue::Visible)
     return;
 
   XFA_Element eType = GetElementType();
@@ -224,13 +222,13 @@
     CXFA_Edge* edge = GetEdgeIfExists(0);
     float fThickness = std::fmax(0.0, edge ? edge->GetThickness() : 0);
     float fHalf = fThickness / 2;
-    XFA_AttributeEnum iHand = GetHand();
-    if (iHand == XFA_AttributeEnum::Left)
+    XFA_AttributeValue iHand = GetHand();
+    if (iHand == XFA_AttributeValue::Left)
       rtWidget.Inflate(fHalf, fHalf);
-    else if (iHand == XFA_AttributeEnum::Right)
+    else if (iHand == XFA_AttributeValue::Right)
       rtWidget.Deflate(fHalf, fHalf);
 
-    GetPathArcOrRounded(rtWidget, fillPath, forceRound);
+    GetPathArcOrRounded(rtWidget, forceRound, &fillPath);
   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
     ToRectangle(this)->GetFillPath(strokes, rtWidget, &fillPath);
   } else {
@@ -243,8 +241,8 @@
 }
 
 void CXFA_Box::GetPathArcOrRounded(CFX_RectF rtDraw,
-                                   CXFA_GEPath& fillPath,
-                                   bool forceRound) {
+                                   bool forceRound,
+                                   CXFA_GEPath* fillPath) {
   float a, b;
   a = rtDraw.width / 2.0f;
   b = rtDraw.height / 2.0f;
@@ -259,13 +257,13 @@
   Optional<int32_t> startAngle = GetStartAngle();
   Optional<int32_t> sweepAngle = GetSweepAngle();
   if (!startAngle && !sweepAngle) {
-    fillPath.AddEllipse(rtDraw);
+    fillPath->AddEllipse(rtDraw);
     return;
   }
 
-  fillPath.AddArc(rtDraw.TopLeft(), rtDraw.Size(),
-                  -startAngle.value_or(0) * FX_PI / 180.0f,
-                  -sweepAngle.value_or(360) * FX_PI / 180.0f);
+  fillPath->AddArc(rtDraw.TopLeft(), rtDraw.Size(),
+                   -startAngle.value_or(0) * FX_PI / 180.0f,
+                   -sweepAngle.value_or(360) * FX_PI / 180.0f);
 }
 
 void CXFA_Box::StrokeArcOrRounded(CXFA_Graphics* pGS,
@@ -278,10 +276,10 @@
 
   bool bVisible;
   float fThickness;
-  XFA_AttributeEnum i3DType;
+  XFA_AttributeValue i3DType;
   std::tie(i3DType, bVisible, fThickness) = Get3DStyle();
   bool lowered3d = false;
-  if (i3DType != XFA_AttributeEnum::Unknown) {
+  if (i3DType != XFA_AttributeValue::Unknown) {
     if (bVisible && fThickness >= 0.001f)
       lowered3d = true;
   }
@@ -291,10 +289,10 @@
     fHalf = 0;
   }
 
-  XFA_AttributeEnum iHand = GetHand();
-  if (iHand == XFA_AttributeEnum::Left) {
+  XFA_AttributeValue iHand = GetHand();
+  if (iHand == XFA_AttributeValue::Left) {
     rtWidget.Inflate(fHalf, fHalf);
-  } else if (iHand == XFA_AttributeEnum::Right) {
+  } else if (iHand == XFA_AttributeValue::Right) {
     rtWidget.Deflate(fHalf, fHalf);
   }
   if (!forceRound || !lowered3d) {
@@ -302,7 +300,7 @@
       return;
 
     CXFA_GEPath arcPath;
-    GetPathArcOrRounded(rtWidget, arcPath, forceRound);
+    GetPathArcOrRounded(rtWidget, forceRound, &arcPath);
     if (edge)
       edge->Stroke(&arcPath, pGS, matrix);
     return;
@@ -324,10 +322,6 @@
   rtWidget.width = a + a;
   rtWidget.height = b + b;
 
-  float startAngle = 0, sweepAngle = 360;
-  startAngle = startAngle * FX_PI / 180.0f;
-  sweepAngle = -sweepAngle * FX_PI / 180.0f;
-
   CXFA_GEPath arcPath;
   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FX_PI / 4.0f,
                  FX_PI);
diff --git a/xfa/fxfa/parser/cxfa_box.h b/xfa/fxfa/parser/cxfa_box.h
index 0831cd6..cbda771 100644
--- a/xfa/fxfa/parser/cxfa_box.h
+++ b/xfa/fxfa/parser/cxfa_box.h
@@ -25,8 +25,8 @@
  public:
   ~CXFA_Box() override;
 
-  XFA_AttributeEnum GetPresence();
-  std::tuple<XFA_AttributeEnum, bool, float> Get3DStyle();
+  XFA_AttributeValue GetPresence();
+  std::tuple<XFA_AttributeValue, bool, float> Get3DStyle();
 
   int32_t CountEdges();
   CXFA_Edge* GetEdgeIfExists(int32_t nIndex);
@@ -45,12 +45,11 @@
            uint32_t validPackets,
            XFA_ObjectType oType,
            XFA_Element eType,
-           const PropertyData* properties,
-           const AttributeData* attributes,
-           const WideStringView& elementName,
+           pdfium::span<const PropertyData> properties,
+           pdfium::span<const AttributeData> attributes,
            std::unique_ptr<CJX_Object> js_node);
 
-  XFA_AttributeEnum GetHand();
+  XFA_AttributeValue GetHand();
 
  private:
   bool IsCircular();
@@ -68,8 +67,8 @@
                           const CFX_Matrix& matrix,
                           bool forceRound);
   void GetPathArcOrRounded(CFX_RectF rtDraw,
-                           CXFA_GEPath& fillPath,
-                           bool forceRound);
+                           bool forceRound,
+                           CXFA_GEPath* fillPath);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BOX_H_
diff --git a/xfa/fxfa/parser/cxfa_break.cpp b/xfa/fxfa/parser/cxfa_break.cpp
index 3a83b1c..e02fa1e 100644
--- a/xfa/fxfa/parser/cxfa_break.cpp
+++ b/xfa/fxfa/parser/cxfa_break.cpp
@@ -6,14 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_break.h"
 
-#include "fxjs/xfa/cjx_break.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kBreakPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kBreakAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::BeforeTarget, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
@@ -23,15 +25,13 @@
     {XFA_Attribute::StartNew, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::BookendTrailer, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::After, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
+     (void*)XFA_AttributeValue::Auto},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::BookendLeader, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AfterTarget, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Before, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"break";
+     (void*)XFA_AttributeValue::Auto},
+};
 
 }  // namespace
 
@@ -41,9 +41,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Break,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Break>(this)) {}
+                kBreakPropertyData,
+                kBreakAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Break::~CXFA_Break() {}
+CXFA_Break::~CXFA_Break() = default;
diff --git a/xfa/fxfa/parser/cxfa_break.h b/xfa/fxfa/parser/cxfa_break.h
index c0d937c..4bf7dde 100644
--- a/xfa/fxfa/parser/cxfa_break.h
+++ b/xfa/fxfa/parser/cxfa_break.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Break : public CXFA_Node {
+class CXFA_Break final : public CXFA_Node {
  public:
   CXFA_Break(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Break() override;
diff --git a/xfa/fxfa/parser/cxfa_breakafter.cpp b/xfa/fxfa/parser/cxfa_breakafter.cpp
index 925b721..252a607 100644
--- a/xfa/fxfa/parser/cxfa_breakafter.cpp
+++ b/xfa/fxfa/parser/cxfa_breakafter.cpp
@@ -6,26 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_breakafter.h"
 
-#include "fxjs/xfa/cjx_breakafter.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Script, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kBreakAfterPropertyData[] = {
+    {XFA_Element::Script, 1, 0},
+};
+
+const CXFA_Node::AttributeData kBreakAfterAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::StartNew, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Trailer, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TargetType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
+     (void*)XFA_AttributeValue::Auto},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Target, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Leader, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"breakAfter";
+};
 
 }  // namespace
 
@@ -35,9 +35,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::BreakAfter,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_BreakAfter>(this)) {}
+                kBreakAfterPropertyData,
+                kBreakAfterAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_BreakAfter::~CXFA_BreakAfter() {}
+CXFA_BreakAfter::~CXFA_BreakAfter() = default;
diff --git a/xfa/fxfa/parser/cxfa_breakafter.h b/xfa/fxfa/parser/cxfa_breakafter.h
index 952b0c9..c607ef5 100644
--- a/xfa/fxfa/parser/cxfa_breakafter.h
+++ b/xfa/fxfa/parser/cxfa_breakafter.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_BreakAfter : public CXFA_Node {
+class CXFA_BreakAfter final : public CXFA_Node {
  public:
   CXFA_BreakAfter(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_BreakAfter() override;
diff --git a/xfa/fxfa/parser/cxfa_breakbefore.cpp b/xfa/fxfa/parser/cxfa_breakbefore.cpp
index c8dcade..0518ae1 100644
--- a/xfa/fxfa/parser/cxfa_breakbefore.cpp
+++ b/xfa/fxfa/parser/cxfa_breakbefore.cpp
@@ -6,26 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_breakbefore.h"
 
-#include "fxjs/xfa/cjx_breakbefore.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Script, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kBreakBeforePropertyData[] = {
+    {XFA_Element::Script, 1, 0},
+};
+
+const CXFA_Node::AttributeData kBreakBeforeAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::StartNew, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Trailer, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TargetType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
+     (void*)XFA_AttributeValue::Auto},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Target, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Leader, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"breakBefore";
+};
 
 }  // namespace
 
@@ -35,9 +35,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::BreakBefore,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_BreakBefore>(this)) {}
+                kBreakBeforePropertyData,
+                kBreakBeforeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_BreakBefore::~CXFA_BreakBefore() {}
+CXFA_BreakBefore::~CXFA_BreakBefore() = default;
diff --git a/xfa/fxfa/parser/cxfa_breakbefore.h b/xfa/fxfa/parser/cxfa_breakbefore.h
index 358e477..1b6dc63 100644
--- a/xfa/fxfa/parser/cxfa_breakbefore.h
+++ b/xfa/fxfa/parser/cxfa_breakbefore.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_BreakBefore : public CXFA_Node {
+class CXFA_BreakBefore final : public CXFA_Node {
  public:
   CXFA_BreakBefore(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_BreakBefore() override;
diff --git a/xfa/fxfa/parser/cxfa_button.cpp b/xfa/fxfa/parser/cxfa_button.cpp
index 2b62efb..4b35aad 100644
--- a/xfa/fxfa/parser/cxfa_button.cpp
+++ b/xfa/fxfa/parser/cxfa_button.cpp
@@ -6,22 +6,22 @@
 
 #include "xfa/fxfa/parser/cxfa_button.h"
 
-#include "fxjs/xfa/cjx_button.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kButtonPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kButtonAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Highlight, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Inverted},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"button";
+     (void*)XFA_AttributeValue::Inverted},
+};
 
 }  // namespace
 
@@ -31,9 +31,16 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Button,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Button>(this)) {}
+                kButtonPropertyData,
+                kButtonAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Button::~CXFA_Button() {}
+CXFA_Button::~CXFA_Button() = default;
+
+XFA_FFWidgetType CXFA_Button::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kButton;
+}
+
+XFA_AttributeValue CXFA_Button::GetHighlight() {
+  return JSObject()->GetEnum(XFA_Attribute::Highlight);
+}
diff --git a/xfa/fxfa/parser/cxfa_button.h b/xfa/fxfa/parser/cxfa_button.h
index 86ffb52..1eddd65 100644
--- a/xfa/fxfa/parser/cxfa_button.h
+++ b/xfa/fxfa/parser/cxfa_button.h
@@ -9,10 +9,14 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Button : public CXFA_Node {
+class CXFA_Button final : public CXFA_Node {
  public:
   CXFA_Button(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Button() override;
+
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+  XFA_AttributeValue GetHighlight();
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_BUTTON_H_
diff --git a/xfa/fxfa/parser/cxfa_cache.cpp b/xfa/fxfa/parser/cxfa_cache.cpp
index 74227a3..8b51c02 100644
--- a/xfa/fxfa/parser/cxfa_cache.cpp
+++ b/xfa/fxfa/parser/cxfa_cache.cpp
@@ -6,17 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_cache.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kCachePropertyData[] = {
     {XFA_Element::TemplateCache, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kCacheAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"cache";
+};
 
 }  // namespace
 
@@ -26,8 +28,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Cache,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kCachePropertyData,
+                kCacheAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
 CXFA_Cache::~CXFA_Cache() {}
diff --git a/xfa/fxfa/parser/cxfa_cache.h b/xfa/fxfa/parser/cxfa_cache.h
index e3907c6..430163a 100644
--- a/xfa/fxfa/parser/cxfa_cache.h
+++ b/xfa/fxfa/parser/cxfa_cache.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Cache : public CXFA_Node {
+class CXFA_Cache final : public CXFA_Node {
  public:
   CXFA_Cache(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Cache() override;
diff --git a/xfa/fxfa/parser/cxfa_calculate.cpp b/xfa/fxfa/parser/cxfa_calculate.cpp
index 3286787..ce79864 100644
--- a/xfa/fxfa/parser/cxfa_calculate.cpp
+++ b/xfa/fxfa/parser/cxfa_calculate.cpp
@@ -6,7 +6,7 @@
 
 #include "xfa/fxfa/parser/cxfa_calculate.h"
 
-#include "fxjs/xfa/cjx_calculate.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_message.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
@@ -14,19 +14,19 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Message, 1, 0},
-                                                 {XFA_Element::Script, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kCalculatePropertyData[] = {
+    {XFA_Element::Message, 1, 0},
+    {XFA_Element::Script, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kCalculateAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Override, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Error},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"calculate";
+     (void*)XFA_AttributeValue::Error},
+};
 
 }  // namespace
 
@@ -36,17 +36,16 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Calculate,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Calculate>(this)) {}
+                kCalculatePropertyData,
+                kCalculateAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Calculate::~CXFA_Calculate() {}
+CXFA_Calculate::~CXFA_Calculate() = default;
 
-XFA_AttributeEnum CXFA_Calculate::GetOverride() {
+XFA_AttributeValue CXFA_Calculate::GetOverride() {
   return JSObject()
       ->TryEnum(XFA_Attribute::Override, false)
-      .value_or(XFA_AttributeEnum::Error);
+      .value_or(XFA_AttributeValue::Error);
 }
 
 CXFA_Script* CXFA_Calculate::GetScriptIfExists() {
@@ -56,8 +55,8 @@
 WideString CXFA_Calculate::GetMessageText() {
   CXFA_Message* pNode = GetChild<CXFA_Message>(0, XFA_Element::Message, false);
   if (!pNode)
-    return L"";
+    return WideString();
 
   CXFA_Text* text = pNode->GetChild<CXFA_Text>(0, XFA_Element::Text, false);
-  return text ? text->GetContent() : L"";
+  return text ? text->GetContent() : WideString();
 }
diff --git a/xfa/fxfa/parser/cxfa_calculate.h b/xfa/fxfa/parser/cxfa_calculate.h
index a5be908..a3934ac 100644
--- a/xfa/fxfa/parser/cxfa_calculate.h
+++ b/xfa/fxfa/parser/cxfa_calculate.h
@@ -11,12 +11,12 @@
 
 class CXFA_Script;
 
-class CXFA_Calculate : public CXFA_Node {
+class CXFA_Calculate final : public CXFA_Node {
  public:
   CXFA_Calculate(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Calculate() override;
 
-  XFA_AttributeEnum GetOverride();
+  XFA_AttributeValue GetOverride();
   CXFA_Script* GetScriptIfExists();
   WideString GetMessageText();
 };
diff --git a/xfa/fxfa/parser/cxfa_calendarsymbols.cpp b/xfa/fxfa/parser/cxfa_calendarsymbols.cpp
index c9ad3b5..155a296 100644
--- a/xfa/fxfa/parser/cxfa_calendarsymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_calendarsymbols.cpp
@@ -6,20 +6,22 @@
 
 #include "xfa/fxfa/parser/cxfa_calendarsymbols.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kCalendarSymbolsPropertyData[] = {
     {XFA_Element::EraNames, 1, 0},
     {XFA_Element::DayNames, 2, 0},
     {XFA_Element::MeridiemNames, 1, 0},
     {XFA_Element::MonthNames, 2, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
-    {XFA_Attribute::Name, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Gregorian},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
+};
 
-constexpr wchar_t kName[] = L"calendarSymbols";
+const CXFA_Node::AttributeData kCalendarSymbolsAttributeData[] = {
+    {XFA_Attribute::Name, XFA_AttributeType::Enum,
+     (void*)XFA_AttributeValue::Gregorian},
+};
 
 }  // namespace
 
@@ -30,8 +32,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::CalendarSymbols,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kCalendarSymbolsPropertyData,
+                kCalendarSymbolsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_CalendarSymbols::~CXFA_CalendarSymbols() {}
+CXFA_CalendarSymbols::~CXFA_CalendarSymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_calendarsymbols.h b/xfa/fxfa/parser/cxfa_calendarsymbols.h
index 1b1f007..82ecde4 100644
--- a/xfa/fxfa/parser/cxfa_calendarsymbols.h
+++ b/xfa/fxfa/parser/cxfa_calendarsymbols.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_CalendarSymbols : public CXFA_Node {
+class CXFA_CalendarSymbols final : public CXFA_Node {
  public:
   CXFA_CalendarSymbols(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_CalendarSymbols() override;
diff --git a/xfa/fxfa/parser/cxfa_caption.cpp b/xfa/fxfa/parser/cxfa_caption.cpp
index a54605a..1fadb8d 100644
--- a/xfa/fxfa/parser/cxfa_caption.cpp
+++ b/xfa/fxfa/parser/cxfa_caption.cpp
@@ -6,7 +6,7 @@
 
 #include "xfa/fxfa/parser/cxfa_caption.h"
 
-#include "fxjs/xfa/cjx_caption.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
@@ -15,22 +15,22 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kCaptionPropertyData[] = {
     {XFA_Element::Margin, 1, 0}, {XFA_Element::Para, 1, 0},
     {XFA_Element::Font, 1, 0},   {XFA_Element::Value, 1, 0},
-    {XFA_Element::Extras, 1, 0}, {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kCaptionAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Reserve, XFA_AttributeType::Measure, (void*)L"-1un"},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Placement, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Left},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"caption";
+     (void*)XFA_AttributeValue::Left},
+};
 
 }  // namespace
 
@@ -40,34 +40,29 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Caption,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Caption>(this)) {}
+                kCaptionPropertyData,
+                kCaptionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Caption::~CXFA_Caption() {}
+CXFA_Caption::~CXFA_Caption() = default;
 
 bool CXFA_Caption::IsVisible() {
-  return JSObject()
-             ->TryEnum(XFA_Attribute::Presence, true)
-             .value_or(XFA_AttributeEnum::Visible) ==
-         XFA_AttributeEnum::Visible;
+  auto value = JSObject()->TryEnum(XFA_Attribute::Presence, true);
+  return !value.has_value() || value.value() == XFA_AttributeValue::Visible;
 }
 
 bool CXFA_Caption::IsHidden() {
-  return JSObject()
-             ->TryEnum(XFA_Attribute::Presence, true)
-             .value_or(XFA_AttributeEnum::Visible) == XFA_AttributeEnum::Hidden;
+  auto value = JSObject()->TryEnum(XFA_Attribute::Presence, true);
+  return !value.has_value() || value.value() == XFA_AttributeValue::Hidden;
 }
 
-XFA_AttributeEnum CXFA_Caption::GetPlacementType() {
-  return JSObject()
-      ->TryEnum(XFA_Attribute::Placement, true)
-      .value_or(XFA_AttributeEnum::Left);
+XFA_AttributeValue CXFA_Caption::GetPlacementType() {
+  auto value = JSObject()->TryEnum(XFA_Attribute::Placement, true);
+  return value.value_or(XFA_AttributeValue::Left);
 }
 
 float CXFA_Caption::GetReserve() const {
-  return JSObject()->GetMeasure(XFA_Attribute::Reserve).ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::Reserve, XFA_Unit::Pt);
 }
 
 CXFA_Margin* CXFA_Caption::GetMarginIfExists() {
diff --git a/xfa/fxfa/parser/cxfa_caption.h b/xfa/fxfa/parser/cxfa_caption.h
index c790787..9e7d38a 100644
--- a/xfa/fxfa/parser/cxfa_caption.h
+++ b/xfa/fxfa/parser/cxfa_caption.h
@@ -13,17 +13,17 @@
 class CXFA_Margin;
 class CXFA_Value;
 
-class CXFA_Caption : public CXFA_Node {
+class CXFA_Caption final : public CXFA_Node {
  public:
-  static constexpr XFA_AttributeEnum kDefaultPlacementType =
-      XFA_AttributeEnum::Left;
+  static constexpr XFA_AttributeValue kDefaultPlacementType =
+      XFA_AttributeValue::Left;
 
   CXFA_Caption(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Caption() override;
 
   bool IsVisible();
   bool IsHidden();
-  XFA_AttributeEnum GetPlacementType();
+  XFA_AttributeValue GetPlacementType();
   float GetReserve() const;
   CXFA_Margin* GetMarginIfExists();
   CXFA_Font* GetFontIfExists();
diff --git a/xfa/fxfa/parser/cxfa_certificate.cpp b/xfa/fxfa/parser/cxfa_certificate.cpp
index c3e8ec0..65b6941 100644
--- a/xfa/fxfa/parser/cxfa_certificate.cpp
+++ b/xfa/fxfa/parser/cxfa_certificate.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_certificate.h"
 
-#include "fxjs/xfa/cjx_certificate.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCertificateAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"certificate";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::TextNode,
                 XFA_Element::Certificate,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Certificate>(this)) {}
+                {},
+                kCertificateAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Certificate::~CXFA_Certificate() {}
+CXFA_Certificate::~CXFA_Certificate() = default;
diff --git a/xfa/fxfa/parser/cxfa_certificate.h b/xfa/fxfa/parser/cxfa_certificate.h
index 48106da..4923d57 100644
--- a/xfa/fxfa/parser/cxfa_certificate.h
+++ b/xfa/fxfa/parser/cxfa_certificate.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Certificate : public CXFA_Node {
+class CXFA_Certificate final : public CXFA_Node {
  public:
   CXFA_Certificate(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Certificate() override;
diff --git a/xfa/fxfa/parser/cxfa_certificates.cpp b/xfa/fxfa/parser/cxfa_certificates.cpp
index 0af8f30..42b025e 100644
--- a/xfa/fxfa/parser/cxfa_certificates.cpp
+++ b/xfa/fxfa/parser/cxfa_certificates.cpp
@@ -6,26 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_certificates.h"
 
-#include "fxjs/xfa/cjx_certificates.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kCertificatesPropertyData[] = {
     {XFA_Element::KeyUsage, 1, 0}, {XFA_Element::SubjectDNs, 1, 0},
     {XFA_Element::Issuers, 1, 0},  {XFA_Element::Signing, 1, 0},
-    {XFA_Element::Oids, 1, 0},     {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::Oids, 1, 0},
+};
+
+const CXFA_Node::AttributeData kCertificatesAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Url, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::CredentialServerPolicy, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::UrlPolicy, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"certificates";
+};
 
 }  // namespace
 
@@ -35,9 +35,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Certificates,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Certificates>(this)) {}
+                kCertificatesPropertyData,
+                kCertificatesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Certificates::~CXFA_Certificates() {}
+CXFA_Certificates::~CXFA_Certificates() = default;
diff --git a/xfa/fxfa/parser/cxfa_certificates.h b/xfa/fxfa/parser/cxfa_certificates.h
index a1bed73..6817d03 100644
--- a/xfa/fxfa/parser/cxfa_certificates.h
+++ b/xfa/fxfa/parser/cxfa_certificates.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Certificates : public CXFA_Node {
+class CXFA_Certificates final : public CXFA_Node {
  public:
   CXFA_Certificates(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Certificates() override;
diff --git a/xfa/fxfa/parser/cxfa_change.cpp b/xfa/fxfa/parser/cxfa_change.cpp
index 520c556..ae8da63 100644
--- a/xfa/fxfa/parser/cxfa_change.cpp
+++ b/xfa/fxfa/parser/cxfa_change.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_change.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kChangeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"change";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Change,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kChangeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Change::~CXFA_Change() {}
+CXFA_Change::~CXFA_Change() = default;
diff --git a/xfa/fxfa/parser/cxfa_change.h b/xfa/fxfa/parser/cxfa_change.h
index f39b92e..2fa4ba9 100644
--- a/xfa/fxfa/parser/cxfa_change.h
+++ b/xfa/fxfa/parser/cxfa_change.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Change : public CXFA_Node {
+class CXFA_Change final : public CXFA_Node {
  public:
   CXFA_Change(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Change() override;
diff --git a/xfa/fxfa/parser/cxfa_checkbutton.cpp b/xfa/fxfa/parser/cxfa_checkbutton.cpp
index 00f3993..903ce49 100644
--- a/xfa/fxfa/parser/cxfa_checkbutton.cpp
+++ b/xfa/fxfa/parser/cxfa_checkbutton.cpp
@@ -6,28 +6,28 @@
 
 #include "xfa/fxfa/parser/cxfa_checkbutton.h"
 
-#include "fxjs/xfa/cjx_checkbutton.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Margin, 1, 0},
-                                                 {XFA_Element::Border, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kCheckButtonPropertyData[] = {
+    {XFA_Element::Margin, 1, 0},
+    {XFA_Element::Border, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kCheckButtonAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AllowNeutral, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Mark, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Default},
+     (void*)XFA_AttributeValue::Default},
     {XFA_Attribute::Shape, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Square},
+     (void*)XFA_AttributeValue::Square},
     {XFA_Attribute::Size, XFA_AttributeType::Measure, (void*)L"10pt"},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"checkButton";
+};
 
 }  // namespace
 
@@ -37,9 +37,24 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::CheckButton,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_CheckButton>(this)) {}
+                kCheckButtonPropertyData,
+                kCheckButtonAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_CheckButton::~CXFA_CheckButton() {}
+CXFA_CheckButton::~CXFA_CheckButton() = default;
+
+XFA_FFWidgetType CXFA_CheckButton::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kCheckButton;
+}
+
+bool CXFA_CheckButton::IsRound() {
+  return JSObject()->GetEnum(XFA_Attribute::Shape) == XFA_AttributeValue::Round;
+}
+
+XFA_AttributeValue CXFA_CheckButton::GetMark() {
+  return JSObject()->GetEnum(XFA_Attribute::Mark);
+}
+
+bool CXFA_CheckButton::IsAllowNeutral() {
+  return JSObject()->GetBoolean(XFA_Attribute::AllowNeutral);
+}
diff --git a/xfa/fxfa/parser/cxfa_checkbutton.h b/xfa/fxfa/parser/cxfa_checkbutton.h
index dbfb4bb..3ca8b9e 100644
--- a/xfa/fxfa/parser/cxfa_checkbutton.h
+++ b/xfa/fxfa/parser/cxfa_checkbutton.h
@@ -9,10 +9,16 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_CheckButton : public CXFA_Node {
+class CXFA_CheckButton final : public CXFA_Node {
  public:
   CXFA_CheckButton(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_CheckButton() override;
+
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+
+  bool IsRound();
+  bool IsAllowNeutral();
+  XFA_AttributeValue GetMark();
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CHECKBUTTON_H_
diff --git a/xfa/fxfa/parser/cxfa_choicelist.cpp b/xfa/fxfa/parser/cxfa_choicelist.cpp
index 33f8683..63e9882 100644
--- a/xfa/fxfa/parser/cxfa_choicelist.cpp
+++ b/xfa/fxfa/parser/cxfa_choicelist.cpp
@@ -6,27 +6,27 @@
 
 #include "xfa/fxfa/parser/cxfa_choicelist.h"
 
-#include "fxjs/xfa/cjx_choicelist.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Margin, 1, 0},
-                                                 {XFA_Element::Border, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kChoiceListPropertyData[] = {
+    {XFA_Element::Margin, 1, 0},
+    {XFA_Element::Border, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kChoiceListAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Open, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::UserControl},
+     (void*)XFA_AttributeValue::UserControl},
     {XFA_Attribute::CommitOn, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Select},
+     (void*)XFA_AttributeValue::Select},
     {XFA_Attribute::TextEntry, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"choiceList";
+};
 
 }  // namespace
 
@@ -36,9 +36,19 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::ChoiceList,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_ChoiceList>(this)) {}
+                kChoiceListPropertyData,
+                kChoiceListAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ChoiceList::~CXFA_ChoiceList() {}
+CXFA_ChoiceList::~CXFA_ChoiceList() = default;
+
+XFA_Element CXFA_ChoiceList::GetValueNodeType() const {
+  return JSObject()->GetEnum(XFA_Attribute::Open) ==
+                 XFA_AttributeValue::MultiSelect
+             ? XFA_Element::ExData
+             : XFA_Element::Text;
+}
+
+XFA_FFWidgetType CXFA_ChoiceList::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kChoiceList;
+}
diff --git a/xfa/fxfa/parser/cxfa_choicelist.h b/xfa/fxfa/parser/cxfa_choicelist.h
index 9e94880..cff1463 100644
--- a/xfa/fxfa/parser/cxfa_choicelist.h
+++ b/xfa/fxfa/parser/cxfa_choicelist.h
@@ -9,10 +9,13 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ChoiceList : public CXFA_Node {
+class CXFA_ChoiceList final : public CXFA_Node {
  public:
   CXFA_ChoiceList(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ChoiceList() override;
+
+  XFA_Element GetValueNodeType() const override;
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_CHOICELIST_H_
diff --git a/xfa/fxfa/parser/cxfa_color.cpp b/xfa/fxfa/parser/cxfa_color.cpp
index 95c0ad3..477121b 100644
--- a/xfa/fxfa/parser/cxfa_color.cpp
+++ b/xfa/fxfa/parser/cxfa_color.cpp
@@ -6,22 +6,22 @@
 
 #include "xfa/fxfa/parser/cxfa_color.h"
 
-#include "fxjs/xfa/cjx_color.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kColorPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kColorAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::CSpace, XFA_AttributeType::CData, (void*)L"SRGB"},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Value, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"color";
+};
 
 }  // namespace
 
@@ -31,12 +31,11 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Color,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Color>(this)) {}
+                kColorPropertyData,
+                kColorAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Color::~CXFA_Color() {}
+CXFA_Color::~CXFA_Color() = default;
 
 FX_ARGB CXFA_Color::GetValue() {
   Optional<WideString> val = JSObject()->TryCData(XFA_Attribute::Value, false);
diff --git a/xfa/fxfa/parser/cxfa_color.h b/xfa/fxfa/parser/cxfa_color.h
index b15c9d8..d13a553 100644
--- a/xfa/fxfa/parser/cxfa_color.h
+++ b/xfa/fxfa/parser/cxfa_color.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Color : public CXFA_Node {
+class CXFA_Color final : public CXFA_Node {
  public:
   static constexpr FX_ARGB kBlackColor = 0xFF000000;
 
diff --git a/xfa/fxfa/parser/cxfa_comb.cpp b/xfa/fxfa/parser/cxfa_comb.cpp
index c427dde..4bd8ac5 100644
--- a/xfa/fxfa/parser/cxfa_comb.cpp
+++ b/xfa/fxfa/parser/cxfa_comb.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_comb.h"
 
-#include "fxjs/xfa/cjx_comb.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCombAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::NumberOfCells, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"comb";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Comb,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Comb>(this)) {}
+                {},
+                kCombAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
 CXFA_Comb::~CXFA_Comb() {}
diff --git a/xfa/fxfa/parser/cxfa_comb.h b/xfa/fxfa/parser/cxfa_comb.h
index 1f2b538..4193a9c 100644
--- a/xfa/fxfa/parser/cxfa_comb.h
+++ b/xfa/fxfa/parser/cxfa_comb.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Comb : public CXFA_Node {
+class CXFA_Comb final : public CXFA_Node {
  public:
   CXFA_Comb(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Comb() override;
diff --git a/xfa/fxfa/parser/cxfa_command.cpp b/xfa/fxfa/parser/cxfa_command.cpp
index ce385e1..4d1e283 100644
--- a/xfa/fxfa/parser/cxfa_command.cpp
+++ b/xfa/fxfa/parser/cxfa_command.cpp
@@ -6,25 +6,25 @@
 
 #include "xfa/fxfa/parser/cxfa_command.h"
 
-#include "fxjs/xfa/cjx_command.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Query, 1, 0},
-                                                 {XFA_Element::Insert, 1, 0},
-                                                 {XFA_Element::Update, 1, 0},
-                                                 {XFA_Element::Delete, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kCommandPropertyData[] = {
+    {XFA_Element::Query, 1, 0},
+    {XFA_Element::Insert, 1, 0},
+    {XFA_Element::Update, 1, 0},
+    {XFA_Element::Delete, 1, 0},
+};
+
+const CXFA_Node::AttributeData kCommandAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Timeout, XFA_AttributeType::Integer, (void*)30},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"command";
+};
 
 }  // namespace
 
@@ -34,9 +34,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Command,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Command>(this)) {}
+                kCommandPropertyData,
+                kCommandAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Command::~CXFA_Command() {}
+CXFA_Command::~CXFA_Command() = default;
diff --git a/xfa/fxfa/parser/cxfa_command.h b/xfa/fxfa/parser/cxfa_command.h
index 88b74f3..46f9854 100644
--- a/xfa/fxfa/parser/cxfa_command.h
+++ b/xfa/fxfa/parser/cxfa_command.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Command : public CXFA_Node {
+class CXFA_Command final : public CXFA_Node {
  public:
   CXFA_Command(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Command() override;
diff --git a/xfa/fxfa/parser/cxfa_common.cpp b/xfa/fxfa/parser/cxfa_common.cpp
index e95f02f..576234c 100644
--- a/xfa/fxfa/parser/cxfa_common.cpp
+++ b/xfa/fxfa/parser/cxfa_common.cpp
@@ -6,9 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_common.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kCommonPropertyData[] = {
     {XFA_Element::SuppressBanner, 1, 0},
     {XFA_Element::VersionControl, 1, 0},
     {XFA_Element::LocaleSet, 1, 0},
@@ -17,13 +20,12 @@
     {XFA_Element::Locale, 1, 0},
     {XFA_Element::Data, 1, 0},
     {XFA_Element::Messaging, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kCommonAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"common";
+};
 
 }  // namespace
 
@@ -33,8 +35,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Common,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kCommonPropertyData,
+                kCommonAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Common::~CXFA_Common() {}
+CXFA_Common::~CXFA_Common() = default;
diff --git a/xfa/fxfa/parser/cxfa_common.h b/xfa/fxfa/parser/cxfa_common.h
index d0ce0ea..7cab852 100644
--- a/xfa/fxfa/parser/cxfa_common.h
+++ b/xfa/fxfa/parser/cxfa_common.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Common : public CXFA_Node {
+class CXFA_Common final : public CXFA_Node {
  public:
   CXFA_Common(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Common() override;
diff --git a/xfa/fxfa/parser/cxfa_compress.cpp b/xfa/fxfa/parser/cxfa_compress.cpp
index 72a3b92..6b593e7 100644
--- a/xfa/fxfa/parser/cxfa_compress.cpp
+++ b/xfa/fxfa/parser/cxfa_compress.cpp
@@ -6,16 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_compress.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCompressAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Scope, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::ImageOnly},
+     (void*)XFA_AttributeValue::ImageOnly},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"compress";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Compress,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kCompressAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Compress::~CXFA_Compress() {}
+CXFA_Compress::~CXFA_Compress() = default;
diff --git a/xfa/fxfa/parser/cxfa_compress.h b/xfa/fxfa/parser/cxfa_compress.h
index bec1883..2b56390 100644
--- a/xfa/fxfa/parser/cxfa_compress.h
+++ b/xfa/fxfa/parser/cxfa_compress.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Compress : public CXFA_Node {
+class CXFA_Compress final : public CXFA_Node {
  public:
   CXFA_Compress(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Compress() override;
diff --git a/xfa/fxfa/parser/cxfa_compression.cpp b/xfa/fxfa/parser/cxfa_compression.cpp
index 3d2bfde..344101d 100644
--- a/xfa/fxfa/parser/cxfa_compression.cpp
+++ b/xfa/fxfa/parser/cxfa_compression.cpp
@@ -6,20 +6,22 @@
 
 #include "xfa/fxfa/parser/cxfa_compression.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kCompressionPropertyData[] = {
     {XFA_Element::Level, 1, 0},
     {XFA_Element::Type, 1, 0},
     {XFA_Element::CompressObjectStream, 1, 0},
     {XFA_Element::CompressLogicalStructure, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kCompressionAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"compression";
+};
 
 }  // namespace
 
@@ -29,8 +31,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Compression,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kCompressionPropertyData,
+                kCompressionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Compression::~CXFA_Compression() {}
+CXFA_Compression::~CXFA_Compression() = default;
diff --git a/xfa/fxfa/parser/cxfa_compression.h b/xfa/fxfa/parser/cxfa_compression.h
index f0e26cb..7a571ae 100644
--- a/xfa/fxfa/parser/cxfa_compression.h
+++ b/xfa/fxfa/parser/cxfa_compression.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Compression : public CXFA_Node {
+class CXFA_Compression final : public CXFA_Node {
  public:
   CXFA_Compression(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Compression() override;
diff --git a/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp b/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp
index a9c2baf..c9bcf6b 100644
--- a/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp
+++ b/xfa/fxfa/parser/cxfa_compresslogicalstructure.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_compresslogicalstructure.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCompressLogicalStructureAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"compressLogicalStructure";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CompressLogicalStructure,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kCompressLogicalStructureAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_CompressLogicalStructure::~CXFA_CompressLogicalStructure() {}
+CXFA_CompressLogicalStructure::~CXFA_CompressLogicalStructure() = default;
diff --git a/xfa/fxfa/parser/cxfa_compresslogicalstructure.h b/xfa/fxfa/parser/cxfa_compresslogicalstructure.h
index a8b5068..843eb60 100644
--- a/xfa/fxfa/parser/cxfa_compresslogicalstructure.h
+++ b/xfa/fxfa/parser/cxfa_compresslogicalstructure.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_CompressLogicalStructure : public CXFA_Node {
+class CXFA_CompressLogicalStructure final : public CXFA_Node {
  public:
   CXFA_CompressLogicalStructure(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_CompressLogicalStructure() override;
diff --git a/xfa/fxfa/parser/cxfa_compressobjectstream.cpp b/xfa/fxfa/parser/cxfa_compressobjectstream.cpp
index 49eaed0..ed643d0 100644
--- a/xfa/fxfa/parser/cxfa_compressobjectstream.cpp
+++ b/xfa/fxfa/parser/cxfa_compressobjectstream.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_compressobjectstream.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCompressObjectStreamAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"compressObjectStream";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CompressObjectStream,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kCompressObjectStreamAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_CompressObjectStream::~CXFA_CompressObjectStream() {}
+CXFA_CompressObjectStream::~CXFA_CompressObjectStream() = default;
diff --git a/xfa/fxfa/parser/cxfa_compressobjectstream.h b/xfa/fxfa/parser/cxfa_compressobjectstream.h
index 94e02ce..bce7c65 100644
--- a/xfa/fxfa/parser/cxfa_compressobjectstream.h
+++ b/xfa/fxfa/parser/cxfa_compressobjectstream.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_CompressObjectStream : public CXFA_Node {
+class CXFA_CompressObjectStream final : public CXFA_Node {
  public:
   CXFA_CompressObjectStream(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_CompressObjectStream() override;
diff --git a/xfa/fxfa/parser/cxfa_config.cpp b/xfa/fxfa/parser/cxfa_config.cpp
index 79232a2..540e967 100644
--- a/xfa/fxfa/parser/cxfa_config.cpp
+++ b/xfa/fxfa/parser/cxfa_config.cpp
@@ -6,18 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_config.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Present, 1, 0},
-                                                 {XFA_Element::Acrobat, 1, 0},
-                                                 {XFA_Element::Trace, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kConfigPropertyData[] = {
+    {XFA_Element::Present, 1, 0},
+    {XFA_Element::Acrobat, 1, 0},
+    {XFA_Element::Trace, 1, 0},
+};
+
+const CXFA_Node::AttributeData kConfigAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"config";
+};
 
 }  // namespace
 
@@ -27,8 +30,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Config,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kConfigPropertyData,
+                kConfigAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Config::~CXFA_Config() {}
+CXFA_Config::~CXFA_Config() = default;
diff --git a/xfa/fxfa/parser/cxfa_config.h b/xfa/fxfa/parser/cxfa_config.h
index 2ddb692..644c475 100644
--- a/xfa/fxfa/parser/cxfa_config.h
+++ b/xfa/fxfa/parser/cxfa_config.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Config : public CXFA_Node {
+class CXFA_Config final : public CXFA_Node {
  public:
   CXFA_Config(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Config() override;
diff --git a/xfa/fxfa/parser/cxfa_conformance.cpp b/xfa/fxfa/parser/cxfa_conformance.cpp
index f83c8ea..b9c8e34 100644
--- a/xfa/fxfa/parser/cxfa_conformance.cpp
+++ b/xfa/fxfa/parser/cxfa_conformance.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_conformance.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kConformanceAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"conformance";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Conformance,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kConformanceAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Conformance::~CXFA_Conformance() {}
+CXFA_Conformance::~CXFA_Conformance() = default;
diff --git a/xfa/fxfa/parser/cxfa_conformance.h b/xfa/fxfa/parser/cxfa_conformance.h
index 11a72e7..829bb1c 100644
--- a/xfa/fxfa/parser/cxfa_conformance.h
+++ b/xfa/fxfa/parser/cxfa_conformance.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Conformance : public CXFA_Node {
+class CXFA_Conformance final : public CXFA_Node {
  public:
   CXFA_Conformance(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Conformance() override;
diff --git a/xfa/fxfa/parser/cxfa_connect.cpp b/xfa/fxfa/parser/cxfa_connect.cpp
index 40648f4..c46debc 100644
--- a/xfa/fxfa/parser/cxfa_connect.cpp
+++ b/xfa/fxfa/parser/cxfa_connect.cpp
@@ -6,18 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_connect.h"
 
-#include "fxjs/xfa/cjx_connect.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kConnectPropertyData[] = {
     {XFA_Element::Picture, 1, 0},
     {XFA_Element::ConnectString, 1, 0},
     {XFA_Element::User, 1, 0},
     {XFA_Element::Password, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kConnectAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
@@ -25,12 +26,10 @@
     {XFA_Attribute::Timeout, XFA_AttributeType::Integer, (void*)15},
     {XFA_Attribute::Connection, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usage, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::ExportAndImport},
+     (void*)XFA_AttributeValue::ExportAndImport},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DelayedOpen, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"connect";
+};
 
 }  // namespace
 
@@ -41,9 +40,8 @@
                  XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Connect,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Connect>(this)) {}
+                kConnectPropertyData,
+                kConnectAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Connect::~CXFA_Connect() {}
+CXFA_Connect::~CXFA_Connect() = default;
diff --git a/xfa/fxfa/parser/cxfa_connect.h b/xfa/fxfa/parser/cxfa_connect.h
index ac91577..3d616ce 100644
--- a/xfa/fxfa/parser/cxfa_connect.h
+++ b/xfa/fxfa/parser/cxfa_connect.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Connect : public CXFA_Node {
+class CXFA_Connect final : public CXFA_Node {
  public:
   CXFA_Connect(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Connect() override;
diff --git a/xfa/fxfa/parser/cxfa_connectionset.cpp b/xfa/fxfa/parser/cxfa_connectionset.cpp
index 78f02e0..74585e6 100644
--- a/xfa/fxfa/parser/cxfa_connectionset.cpp
+++ b/xfa/fxfa/parser/cxfa_connectionset.cpp
@@ -9,12 +9,6 @@
 #include "fxjs/xfa/cjx_model.h"
 #include "third_party/base/ptr_util.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"connectionSet";
-
-}  // namespace
-
 CXFA_ConnectionSet::CXFA_ConnectionSet(CXFA_Document* doc,
                                        XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -22,9 +16,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::ConnectionSet,
-                nullptr,
-                nullptr,
-                kName,
+                {},
+                {},
                 pdfium::MakeUnique<CJX_Model>(this)) {}
 
-CXFA_ConnectionSet::~CXFA_ConnectionSet() {}
+CXFA_ConnectionSet::~CXFA_ConnectionSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_connectionset.h b/xfa/fxfa/parser/cxfa_connectionset.h
index 59316f7..74238e0 100644
--- a/xfa/fxfa/parser/cxfa_connectionset.h
+++ b/xfa/fxfa/parser/cxfa_connectionset.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ConnectionSet : public CXFA_Node {
+class CXFA_ConnectionSet final : public CXFA_Node {
  public:
   CXFA_ConnectionSet(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ConnectionSet() override;
diff --git a/xfa/fxfa/parser/cxfa_connectstring.cpp b/xfa/fxfa/parser/cxfa_connectstring.cpp
index 87eb70d..c5e37f4 100644
--- a/xfa/fxfa/parser/cxfa_connectstring.cpp
+++ b/xfa/fxfa/parser/cxfa_connectstring.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_connectstring.h"
 
-#include "fxjs/xfa/cjx_connectstring.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kConnectStringAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"connectString";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::ConnectString,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_ConnectString>(this)) {}
+                {},
+                kConnectStringAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_ConnectString::~CXFA_ConnectString() {}
+CXFA_ConnectString::~CXFA_ConnectString() = default;
diff --git a/xfa/fxfa/parser/cxfa_connectstring.h b/xfa/fxfa/parser/cxfa_connectstring.h
index 6c3a0b0..88dd7bc 100644
--- a/xfa/fxfa/parser/cxfa_connectstring.h
+++ b/xfa/fxfa/parser/cxfa_connectstring.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ConnectString : public CXFA_Node {
+class CXFA_ConnectString final : public CXFA_Node {
  public:
   CXFA_ConnectString(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ConnectString() override;
diff --git a/xfa/fxfa/parser/cxfa_containerlayoutitem.cpp b/xfa/fxfa/parser/cxfa_containerlayoutitem.cpp
deleted file mode 100644
index ee122b6..0000000
--- a/xfa/fxfa/parser/cxfa_containerlayoutitem.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
-
-#include "fxjs/xfa/cjx_object.h"
-#include "xfa/fxfa/parser/cxfa_layoutpagemgr.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
-#include "xfa/fxfa/parser/cxfa_medium.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-
-CXFA_ContainerLayoutItem::CXFA_ContainerLayoutItem(CXFA_Node* pNode)
-    : CXFA_LayoutItem(pNode, false), m_pOldSubform(nullptr) {}
-
-CXFA_LayoutProcessor* CXFA_ContainerLayoutItem::GetLayout() const {
-  return m_pFormNode->GetDocument()->GetLayoutProcessor();
-}
-
-int32_t CXFA_ContainerLayoutItem::GetPageIndex() const {
-  return m_pFormNode->GetDocument()
-      ->GetLayoutProcessor()
-      ->GetLayoutPageMgr()
-      ->GetPageIndex(this);
-}
-
-CFX_SizeF CXFA_ContainerLayoutItem::GetPageSize() const {
-  CFX_SizeF size;
-  CXFA_Medium* pMedium =
-      m_pFormNode->GetFirstChildByClass<CXFA_Medium>(XFA_Element::Medium);
-  if (!pMedium)
-    return size;
-
-  size = CFX_SizeF(pMedium->JSObject()
-                       ->GetMeasure(XFA_Attribute::Short)
-                       .ToUnit(XFA_Unit::Pt),
-                   pMedium->JSObject()
-                       ->GetMeasure(XFA_Attribute::Long)
-                       .ToUnit(XFA_Unit::Pt));
-  if (pMedium->JSObject()->GetEnum(XFA_Attribute::Orientation) ==
-      XFA_AttributeEnum::Landscape) {
-    size = CFX_SizeF(size.height, size.width);
-  }
-  return size;
-}
-
-CXFA_Node* CXFA_ContainerLayoutItem::GetMasterPage() const {
-  return m_pFormNode;
-}
diff --git a/xfa/fxfa/parser/cxfa_containerlayoutitem.h b/xfa/fxfa/parser/cxfa_containerlayoutitem.h
deleted file mode 100644
index 3c8a8af..0000000
--- a/xfa/fxfa/parser/cxfa_containerlayoutitem.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_CONTAINERLAYOUTITEM_H_
-#define XFA_FXFA_PARSER_CXFA_CONTAINERLAYOUTITEM_H_
-
-#include "xfa/fxfa/parser/cxfa_layoutitem.h"
-
-class CXFA_ContainerLayoutItem : public CXFA_LayoutItem {
- public:
-  explicit CXFA_ContainerLayoutItem(CXFA_Node* pNode);
-
-  CXFA_LayoutProcessor* GetLayout() const;
-  int32_t GetPageIndex() const;
-  CFX_SizeF GetPageSize() const;
-  CXFA_Node* GetMasterPage() const;
-
-  CXFA_Node* m_pOldSubform;
-};
-
-inline CXFA_ContainerLayoutItem* ToContainerLayoutItem(CXFA_LayoutItem* pItem) {
-  return pItem ? pItem->AsContainerLayoutItem() : nullptr;
-}
-
-#endif  // XFA_FXFA_PARSER_CXFA_CONTAINERLAYOUTITEM_H_
diff --git a/xfa/fxfa/parser/cxfa_contentarea.cpp b/xfa/fxfa/parser/cxfa_contentarea.cpp
index 48f52f7..8c62c0a 100644
--- a/xfa/fxfa/parser/cxfa_contentarea.cpp
+++ b/xfa/fxfa/parser/cxfa_contentarea.cpp
@@ -6,15 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_contentarea.h"
 
-#include "fxjs/xfa/cjx_contentarea.h"
+#include "fxjs/xfa/cjx_container.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Desc, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kContentAreaPropertyData[] = {
+    {XFA_Element::Desc, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kContentAreaAttributeData[] = {
     {XFA_Attribute::H, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::W, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::X, XFA_AttributeType::Measure, (void*)L"0in"},
@@ -24,9 +26,7 @@
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"contentArea";
+};
 
 }  // namespace
 
@@ -36,9 +36,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::ContentArea,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_ContentArea>(this)) {}
+                kContentAreaPropertyData,
+                kContentAreaAttributeData,
+                pdfium::MakeUnique<CJX_Container>(this)) {}
 
-CXFA_ContentArea::~CXFA_ContentArea() {}
+CXFA_ContentArea::~CXFA_ContentArea() = default;
diff --git a/xfa/fxfa/parser/cxfa_contentarea.h b/xfa/fxfa/parser/cxfa_contentarea.h
index 3c5ff60..43f6868 100644
--- a/xfa/fxfa/parser/cxfa_contentarea.h
+++ b/xfa/fxfa/parser/cxfa_contentarea.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ContentArea : public CXFA_Node {
+class CXFA_ContentArea final : public CXFA_Node {
  public:
   CXFA_ContentArea(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ContentArea() override;
diff --git a/xfa/fxfa/parser/cxfa_contentcopy.cpp b/xfa/fxfa/parser/cxfa_contentcopy.cpp
index a55d576..38fd27e 100644
--- a/xfa/fxfa/parser/cxfa_contentcopy.cpp
+++ b/xfa/fxfa/parser/cxfa_contentcopy.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_contentcopy.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kContentCopyAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"contentCopy";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ContentCopy,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kContentCopyAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ContentCopy::~CXFA_ContentCopy() {}
+CXFA_ContentCopy::~CXFA_ContentCopy() = default;
diff --git a/xfa/fxfa/parser/cxfa_contentcopy.h b/xfa/fxfa/parser/cxfa_contentcopy.h
index d394c74..5b32beb 100644
--- a/xfa/fxfa/parser/cxfa_contentcopy.h
+++ b/xfa/fxfa/parser/cxfa_contentcopy.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ContentCopy : public CXFA_Node {
+class CXFA_ContentCopy final : public CXFA_Node {
  public:
   CXFA_ContentCopy(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ContentCopy() override;
diff --git a/xfa/fxfa/parser/cxfa_contentlayoutitem.cpp b/xfa/fxfa/parser/cxfa_contentlayoutitem.cpp
deleted file mode 100644
index 1ee364b..0000000
--- a/xfa/fxfa/parser/cxfa_contentlayoutitem.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
-
-#include "fxjs/xfa/cjx_object.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-
-CXFA_ContentLayoutItem::CXFA_ContentLayoutItem(CXFA_Node* pNode)
-    : CXFA_LayoutItem(pNode, true),
-      m_pPrev(nullptr),
-      m_pNext(nullptr),
-      m_dwStatus(0) {}
-
-CXFA_ContentLayoutItem::~CXFA_ContentLayoutItem() {
-  if (m_pFormNode->JSObject()->GetLayoutItem() == this)
-    m_pFormNode->JSObject()->SetLayoutItem(nullptr);
-}
diff --git a/xfa/fxfa/parser/cxfa_contentlayoutitem.h b/xfa/fxfa/parser/cxfa_contentlayoutitem.h
deleted file mode 100644
index 500d3e4..0000000
--- a/xfa/fxfa/parser/cxfa_contentlayoutitem.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_CONTENTLAYOUTITEM_H_
-#define XFA_FXFA_PARSER_CXFA_CONTENTLAYOUTITEM_H_
-
-#include "xfa/fxfa/parser/cxfa_layoutitem.h"
-
-class CXFA_ContentLayoutItem : public CXFA_LayoutItem {
- public:
-  explicit CXFA_ContentLayoutItem(CXFA_Node* pNode);
-  ~CXFA_ContentLayoutItem() override;
-
-  CXFA_ContentLayoutItem* m_pPrev;
-  CXFA_ContentLayoutItem* m_pNext;
-  CFX_PointF m_sPos;
-  CFX_SizeF m_sSize;
-  mutable uint32_t m_dwStatus;
-};
-
-inline CXFA_ContentLayoutItem* ToContentLayoutItem(CXFA_LayoutItem* pItem) {
-  return pItem ? pItem->AsContentLayoutItem() : nullptr;
-}
-
-#endif  // XFA_FXFA_PARSER_CXFA_CONTENTLAYOUTITEM_H_
diff --git a/xfa/fxfa/parser/cxfa_copies.cpp b/xfa/fxfa/parser/cxfa_copies.cpp
index 4ddf07f..7b8ef0e 100644
--- a/xfa/fxfa/parser/cxfa_copies.cpp
+++ b/xfa/fxfa/parser/cxfa_copies.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_copies.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCopiesAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"copies";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Copies,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kCopiesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Copies::~CXFA_Copies() {}
+CXFA_Copies::~CXFA_Copies() = default;
diff --git a/xfa/fxfa/parser/cxfa_copies.h b/xfa/fxfa/parser/cxfa_copies.h
index 1ecd616..46497f9 100644
--- a/xfa/fxfa/parser/cxfa_copies.h
+++ b/xfa/fxfa/parser/cxfa_copies.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Copies : public CXFA_Node {
+class CXFA_Copies final : public CXFA_Node {
  public:
   CXFA_Copies(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Copies() override;
diff --git a/xfa/fxfa/parser/cxfa_corner.cpp b/xfa/fxfa/parser/cxfa_corner.cpp
index 0cd3ef4..3ffbc71 100644
--- a/xfa/fxfa/parser/cxfa_corner.cpp
+++ b/xfa/fxfa/parser/cxfa_corner.cpp
@@ -6,30 +6,30 @@
 
 #include "xfa/fxfa/parser/cxfa_corner.h"
 
-#include "fxjs/xfa/cjx_corner.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Color, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kCornerPropertyData[] = {
+    {XFA_Element::Color, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kCornerAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Stroke, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Solid},
+     (void*)XFA_AttributeValue::Solid},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::Inverted, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Thickness, XFA_AttributeType::Measure, (void*)L"0.5pt"},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Join, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Square},
+     (void*)XFA_AttributeValue::Square},
     {XFA_Attribute::Radius, XFA_AttributeType::Measure, (void*)L"0in"},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"corner";
+};
 
 }  // namespace
 
@@ -39,9 +39,8 @@
                   (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                   XFA_ObjectType::Node,
                   XFA_Element::Corner,
-                  kPropertyData,
-                  kAttributeData,
-                  kName,
-                  pdfium::MakeUnique<CJX_Corner>(this)) {}
+                  kCornerPropertyData,
+                  kCornerAttributeData,
+                  pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Corner::~CXFA_Corner() {}
+CXFA_Corner::~CXFA_Corner() = default;
diff --git a/xfa/fxfa/parser/cxfa_corner.h b/xfa/fxfa/parser/cxfa_corner.h
index 1042729..7dcaf32 100644
--- a/xfa/fxfa/parser/cxfa_corner.h
+++ b/xfa/fxfa/parser/cxfa_corner.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_stroke.h"
 
-class CXFA_Corner : public CXFA_Stroke {
+class CXFA_Corner final : public CXFA_Stroke {
  public:
   CXFA_Corner(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Corner() override;
diff --git a/xfa/fxfa/parser/cxfa_creator.cpp b/xfa/fxfa/parser/cxfa_creator.cpp
index 2e65068..e03b32a 100644
--- a/xfa/fxfa/parser/cxfa_creator.cpp
+++ b/xfa/fxfa/parser/cxfa_creator.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_creator.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCreatorAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"creator";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Creator,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kCreatorAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Creator::~CXFA_Creator() {}
+CXFA_Creator::~CXFA_Creator() = default;
diff --git a/xfa/fxfa/parser/cxfa_creator.h b/xfa/fxfa/parser/cxfa_creator.h
index c046883..437fab5 100644
--- a/xfa/fxfa/parser/cxfa_creator.h
+++ b/xfa/fxfa/parser/cxfa_creator.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Creator : public CXFA_Node {
+class CXFA_Creator final : public CXFA_Node {
  public:
   CXFA_Creator(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Creator() override;
diff --git a/xfa/fxfa/parser/cxfa_currencysymbol.cpp b/xfa/fxfa/parser/cxfa_currencysymbol.cpp
index de11f27..beca653 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbol.cpp
+++ b/xfa/fxfa/parser/cxfa_currencysymbol.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_currencysymbol.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCurrencySymbolAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Symbol},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"currencySymbol";
+     (void*)XFA_AttributeValue::Symbol},
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CurrencySymbol,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kCurrencySymbolAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_CurrencySymbol::~CXFA_CurrencySymbol() {}
+CXFA_CurrencySymbol::~CXFA_CurrencySymbol() = default;
diff --git a/xfa/fxfa/parser/cxfa_currencysymbol.h b/xfa/fxfa/parser/cxfa_currencysymbol.h
index 271f7fc..3d7b5bd 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbol.h
+++ b/xfa/fxfa/parser/cxfa_currencysymbol.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_CurrencySymbol : public CXFA_Node {
+class CXFA_CurrencySymbol final : public CXFA_Node {
  public:
   CXFA_CurrencySymbol(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_CurrencySymbol() override;
diff --git a/xfa/fxfa/parser/cxfa_currencysymbols.cpp b/xfa/fxfa/parser/cxfa_currencysymbols.cpp
index 4160be9..3b2e90c 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_currencysymbols.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_currencysymbols.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kCurrencySymbolsPropertyData[] = {
     {XFA_Element::CurrencySymbol, 3, 0},
-    {XFA_Element::Unknown, 0, 0}};
-
-constexpr wchar_t kName[] = L"currencySymbols";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::CurrencySymbols,
-                kPropertyData,
-                nullptr,
-                kName) {}
+                kCurrencySymbolsPropertyData,
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_CurrencySymbols::~CXFA_CurrencySymbols() {}
+CXFA_CurrencySymbols::~CXFA_CurrencySymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_currencysymbols.h b/xfa/fxfa/parser/cxfa_currencysymbols.h
index acd3f2a..2ce27cf 100644
--- a/xfa/fxfa/parser/cxfa_currencysymbols.h
+++ b/xfa/fxfa/parser/cxfa_currencysymbols.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_CurrencySymbols : public CXFA_Node {
+class CXFA_CurrencySymbols final : public CXFA_Node {
  public:
   CXFA_CurrencySymbols(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_CurrencySymbols() override;
diff --git a/xfa/fxfa/parser/cxfa_currentpage.cpp b/xfa/fxfa/parser/cxfa_currentpage.cpp
index be200e4..a98aa1c 100644
--- a/xfa/fxfa/parser/cxfa_currentpage.cpp
+++ b/xfa/fxfa/parser/cxfa_currentpage.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_currentpage.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kCurrentPageAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"currentPage";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::CurrentPage,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kCurrentPageAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_CurrentPage::~CXFA_CurrentPage() {}
+CXFA_CurrentPage::~CXFA_CurrentPage() = default;
diff --git a/xfa/fxfa/parser/cxfa_currentpage.h b/xfa/fxfa/parser/cxfa_currentpage.h
index d526b4a..5bba6a0 100644
--- a/xfa/fxfa/parser/cxfa_currentpage.h
+++ b/xfa/fxfa/parser/cxfa_currentpage.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_CurrentPage : public CXFA_Node {
+class CXFA_CurrentPage final : public CXFA_Node {
  public:
   CXFA_CurrentPage(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_CurrentPage() override;
diff --git a/xfa/fxfa/parser/cxfa_data.cpp b/xfa/fxfa/parser/cxfa_data.cpp
index 6fd66a9..5173147 100644
--- a/xfa/fxfa/parser/cxfa_data.cpp
+++ b/xfa/fxfa/parser/cxfa_data.cpp
@@ -6,21 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_data.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kDataPropertyData[] = {
     {XFA_Element::Uri, 1, 0},        {XFA_Element::Xsl, 1, 0},
     {XFA_Element::StartNode, 1, 0},  {XFA_Element::OutputXSL, 1, 0},
     {XFA_Element::AdjustData, 1, 0}, {XFA_Element::Attributes, 1, 0},
     {XFA_Element::Window, 1, 0},     {XFA_Element::Record, 1, 0},
     {XFA_Element::Range, 1, 0},      {XFA_Element::IncrementalLoad, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kDataAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"data";
+};
 
 }  // namespace
 
@@ -30,8 +32,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Data,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kDataPropertyData,
+                kDataAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Data::~CXFA_Data() {}
+CXFA_Data::~CXFA_Data() = default;
diff --git a/xfa/fxfa/parser/cxfa_data.h b/xfa/fxfa/parser/cxfa_data.h
index cb30f61..44cf40d 100644
--- a/xfa/fxfa/parser/cxfa_data.h
+++ b/xfa/fxfa/parser/cxfa_data.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Data : public CXFA_Node {
+class CXFA_Data final : public CXFA_Node {
  public:
   CXFA_Data(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Data() override;
diff --git a/xfa/fxfa/parser/cxfa_dataexporter.cpp b/xfa/fxfa/parser/cxfa_dataexporter.cpp
index 23fc178..50106ab 100644
--- a/xfa/fxfa/parser/cxfa_dataexporter.cpp
+++ b/xfa/fxfa/parser/cxfa_dataexporter.cpp
@@ -7,80 +7,56 @@
 #include "xfa/fxfa/parser/cxfa_dataexporter.h"
 
 #include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/xml/cfx_xmldoc.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "third_party/base/stl_util.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-CXFA_DataExporter::CXFA_DataExporter(CXFA_Document* pDocument)
-    : m_pDocument(pDocument) {
-  ASSERT(m_pDocument);
-}
+CXFA_DataExporter::CXFA_DataExporter() = default;
 
-CXFA_DataExporter::~CXFA_DataExporter() {}
+CXFA_DataExporter::~CXFA_DataExporter() = default;
 
-bool CXFA_DataExporter::Export(const RetainPtr<IFX_SeekableStream>& pWrite) {
-  return Export(pWrite, m_pDocument->GetRoot(), 0, nullptr);
-}
+bool CXFA_DataExporter::Export(const RetainPtr<IFX_SeekableStream>& pStream,
+                               CXFA_Node* pNode) {
+  ASSERT(pStream);
 
-bool CXFA_DataExporter::Export(const RetainPtr<IFX_SeekableStream>& pWrite,
-                               CXFA_Node* pNode,
-                               uint32_t dwFlag,
-                               const char* pChecksum) {
-  ASSERT(pWrite);
-  if (!pWrite)
+  if (!pStream)
     return false;
 
-  auto pStream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(pWrite, true);
-  pStream->SetCodePage(FX_CODEPAGE_UTF8);
-  return Export(pStream, pNode, dwFlag, pChecksum);
-}
-
-bool CXFA_DataExporter::Export(
-    const RetainPtr<CFX_SeekableStreamProxy>& pStream,
-    CXFA_Node* pNode,
-    uint32_t dwFlag,
-    const char* pChecksum) {
-  CFX_XMLDoc* pXMLDoc = m_pDocument->GetXMLDoc();
   if (pNode->IsModelNode()) {
     switch (pNode->GetPacketType()) {
       case XFA_PacketType::Xdp: {
         pStream->WriteString(
-            L"<xdp:xdp xmlns:xdp=\"http://ns.adobe.com/xdp/\">");
+            "<xdp:xdp xmlns:xdp=\"http://ns.adobe.com/xdp/\">");
         for (CXFA_Node* pChild = pNode->GetFirstChild(); pChild;
              pChild = pChild->GetNextSibling()) {
-          Export(pStream, pChild, dwFlag, pChecksum);
+          Export(pStream, pChild);
         }
-        pStream->WriteString(L"</xdp:xdp\n>");
+        pStream->WriteString("</xdp:xdp\n>");
         break;
       }
       case XFA_PacketType::Datasets: {
-        CFX_XMLElement* pElement =
-            static_cast<CFX_XMLElement*>(pNode->GetXMLMappingNode());
-        if (!pElement || pElement->GetType() != FX_XMLNODE_Element)
+        CFX_XMLElement* pElement = ToXMLElement(pNode->GetXMLMappingNode());
+        if (!pElement)
           return false;
 
         CXFA_Node* pDataNode = pNode->GetFirstChild();
         ASSERT(pDataNode);
         XFA_DataExporter_DealWithDataGroupNode(pDataNode);
-        pXMLDoc->SaveXMLNode(pStream, pElement);
+        pElement->Save(pStream);
         break;
       }
-      case XFA_PacketType::Form: {
-        XFA_DataExporter_RegenerateFormFile(pNode, pStream, pChecksum, false);
+      case XFA_PacketType::Form:
+        XFA_DataExporter_RegenerateFormFile(pNode, pStream, false);
         break;
-      }
       case XFA_PacketType::Template:
       default: {
-        CFX_XMLElement* pElement =
-            static_cast<CFX_XMLElement*>(pNode->GetXMLMappingNode());
-        if (!pElement || pElement->GetType() != FX_XMLNODE_Element)
+        CFX_XMLElement* pElement = ToXMLElement(pNode->GetXMLMappingNode());
+        if (!pElement)
           return false;
 
-        pXMLDoc->SaveXMLNode(pStream, pElement);
+        pElement->Save(pStream);
         break;
       }
     }
@@ -96,15 +72,14 @@
       break;
     }
   }
-  CFX_XMLElement* pElement =
-      static_cast<CFX_XMLElement*>(pExportNode->GetXMLMappingNode());
-  if (!pElement || pElement->GetType() != FX_XMLNODE_Element)
+  CFX_XMLElement* pElement = ToXMLElement(pExportNode->GetXMLMappingNode());
+  if (!pElement)
     return false;
 
   XFA_DataExporter_DealWithDataGroupNode(pExportNode);
-  pElement->SetString(L"xmlns:xfa", L"http://www.xfa.org/schema/xfa-data/1.0/");
-  pXMLDoc->SaveXMLNode(pStream, pElement);
+  pElement->SetAttribute(L"xmlns:xfa",
+                         L"http://www.xfa.org/schema/xfa-data/1.0/");
+  pElement->Save(pStream);
   pElement->RemoveAttribute(L"xmlns:xfa");
-
   return true;
 }
diff --git a/xfa/fxfa/parser/cxfa_dataexporter.h b/xfa/fxfa/parser/cxfa_dataexporter.h
index a2a55b3..6fe19d5 100644
--- a/xfa/fxfa/parser/cxfa_dataexporter.h
+++ b/xfa/fxfa/parser/cxfa_dataexporter.h
@@ -13,26 +13,13 @@
 class CXFA_Document;
 class CXFA_Node;
 class IFX_SeekableStream;
-class CFX_SeekableStreamProxy;
 
 class CXFA_DataExporter {
  public:
-  explicit CXFA_DataExporter(CXFA_Document* pDocument);
+  CXFA_DataExporter();
   ~CXFA_DataExporter();
 
-  bool Export(const RetainPtr<IFX_SeekableStream>& pWrite);
-  bool Export(const RetainPtr<IFX_SeekableStream>& pWrite,
-              CXFA_Node* pNode,
-              uint32_t dwFlag,
-              const char* pChecksum);
-
- private:
-  bool Export(const RetainPtr<CFX_SeekableStreamProxy>& pStream,
-              CXFA_Node* pNode,
-              uint32_t dwFlag,
-              const char* pChecksum);
-
-  UnownedPtr<CXFA_Document> const m_pDocument;
+  bool Export(const RetainPtr<IFX_SeekableStream>& pWrite, CXFA_Node* pNode);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATAEXPORTER_H_
diff --git a/xfa/fxfa/parser/cxfa_datagroup.cpp b/xfa/fxfa/parser/cxfa_datagroup.cpp
index 9b65146..bb5b5fb 100644
--- a/xfa/fxfa/parser/cxfa_datagroup.cpp
+++ b/xfa/fxfa/parser/cxfa_datagroup.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_datagroup.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDataGroupAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"dataGroup";
+};
 
 }  // namespace
 
@@ -22,8 +23,8 @@
                 XFA_XDPPACKET_Datasets,
                 XFA_ObjectType::Node,
                 XFA_Element::DataGroup,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDataGroupAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DataGroup::~CXFA_DataGroup() {}
+CXFA_DataGroup::~CXFA_DataGroup() = default;
diff --git a/xfa/fxfa/parser/cxfa_datagroup.h b/xfa/fxfa/parser/cxfa_datagroup.h
index 649b096..5a61704 100644
--- a/xfa/fxfa/parser/cxfa_datagroup.h
+++ b/xfa/fxfa/parser/cxfa_datagroup.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DataGroup : public CXFA_Node {
+class CXFA_DataGroup final : public CXFA_Node {
  public:
   CXFA_DataGroup(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DataGroup() override;
diff --git a/xfa/fxfa/parser/cxfa_dataimporter.cpp b/xfa/fxfa/parser/cxfa_dataimporter.cpp
deleted file mode 100644
index 9171b00..0000000
--- a/xfa/fxfa/parser/cxfa_dataimporter.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_dataimporter.h"
-
-#include <memory>
-
-#include "core/fxcrt/fx_stream.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/fxfa.h"
-#include "xfa/fxfa/fxfa_basic.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_simple_parser.h"
-
-CXFA_DataImporter::CXFA_DataImporter(CXFA_Document* pDocument)
-    : m_pDocument(pDocument) {
-  ASSERT(m_pDocument);
-}
-
-CXFA_DataImporter::~CXFA_DataImporter() {}
-
-bool CXFA_DataImporter::ImportData(
-    const RetainPtr<IFX_SeekableStream>& pDataDocument) {
-  auto pDataDocumentParser =
-      pdfium::MakeUnique<CXFA_SimpleParser>(m_pDocument.Get());
-  if (pDataDocumentParser->StartParse(
-          pDataDocument, XFA_PacketType::Datasets) != XFA_PARSESTATUS_Ready) {
-    return false;
-  }
-  if (pDataDocumentParser->DoParse() < XFA_PARSESTATUS_Done)
-    return false;
-
-  CXFA_Node* pImportDataRoot = pDataDocumentParser->GetRootNode();
-  if (!pImportDataRoot)
-    return false;
-
-  CXFA_Node* pDataModel =
-      ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Datasets));
-  if (!pDataModel)
-    return false;
-
-  CXFA_Node* pDataNode = ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Data));
-  if (pDataNode)
-    pDataModel->RemoveChild(pDataNode, true);
-
-  if (pImportDataRoot->GetElementType() == XFA_Element::DataModel) {
-    while (CXFA_Node* pChildNode = pImportDataRoot->GetFirstChild()) {
-      pImportDataRoot->RemoveChild(pChildNode, true);
-      pDataModel->InsertChild(pChildNode, nullptr);
-    }
-  } else {
-    CFX_XMLNode* pXMLNode = pImportDataRoot->GetXMLMappingNode();
-    CFX_XMLNode* pParentXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::Parent);
-    if (pParentXMLNode)
-      pParentXMLNode->RemoveChildNode(pXMLNode);
-    pDataModel->InsertChild(pImportDataRoot, nullptr);
-  }
-  m_pDocument->DoDataRemerge(false);
-  return true;
-}
diff --git a/xfa/fxfa/parser/cxfa_dataimporter.h b/xfa/fxfa/parser/cxfa_dataimporter.h
deleted file mode 100644
index ca5896e..0000000
--- a/xfa/fxfa/parser/cxfa_dataimporter.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_DATAIMPORTER_H_
-#define XFA_FXFA_PARSER_CXFA_DATAIMPORTER_H_
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/retain_ptr.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CXFA_Document;
-class IFX_SeekableStream;
-
-class CXFA_DataImporter {
- public:
-  explicit CXFA_DataImporter(CXFA_Document* pDocument);
-  ~CXFA_DataImporter();
-
-  bool ImportData(const RetainPtr<IFX_SeekableStream>& pDataDocument);
-
- private:
-  UnownedPtr<CXFA_Document> const m_pDocument;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_DATAIMPORTER_H_
diff --git a/xfa/fxfa/parser/cxfa_datamodel.cpp b/xfa/fxfa/parser/cxfa_datamodel.cpp
index 686ba4c..4b506c5 100644
--- a/xfa/fxfa/parser/cxfa_datamodel.cpp
+++ b/xfa/fxfa/parser/cxfa_datamodel.cpp
@@ -9,21 +9,14 @@
 #include "fxjs/xfa/cjx_model.h"
 #include "third_party/base/ptr_util.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"dataModel";
-
-}  // namespace
-
 CXFA_DataModel::CXFA_DataModel(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
                 XFA_XDPPACKET_Datasets,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::DataModel,
-                nullptr,
-                nullptr,
-                kName,
+                {},
+                {},
                 pdfium::MakeUnique<CJX_Model>(this)) {}
 
-CXFA_DataModel::~CXFA_DataModel() {}
+CXFA_DataModel::~CXFA_DataModel() = default;
diff --git a/xfa/fxfa/parser/cxfa_datamodel.h b/xfa/fxfa/parser/cxfa_datamodel.h
index f414ea3..8a1bd42 100644
--- a/xfa/fxfa/parser/cxfa_datamodel.h
+++ b/xfa/fxfa/parser/cxfa_datamodel.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DataModel : public CXFA_Node {
+class CXFA_DataModel final : public CXFA_Node {
  public:
   CXFA_DataModel(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DataModel() override;
diff --git a/xfa/fxfa/parser/cxfa_datavalue.cpp b/xfa/fxfa/parser/cxfa_datavalue.cpp
index e2ebaf0..6e541a4 100644
--- a/xfa/fxfa/parser/cxfa_datavalue.cpp
+++ b/xfa/fxfa/parser/cxfa_datavalue.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_datavalue.h"
 
-#include "fxjs/xfa/cjx_datavalue.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDataValueAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ContentType, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Contains, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Data},
+     (void*)XFA_AttributeValue::Data},
     {XFA_Attribute::Value, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::IsNull, XFA_AttributeType::Boolean, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"dataValue";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
                 XFA_XDPPACKET_Datasets,
                 XFA_ObjectType::Node,
                 XFA_Element::DataValue,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_DataValue>(this)) {}
+                {},
+                kDataValueAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DataValue::~CXFA_DataValue() {}
+CXFA_DataValue::~CXFA_DataValue() = default;
diff --git a/xfa/fxfa/parser/cxfa_datavalue.h b/xfa/fxfa/parser/cxfa_datavalue.h
index 8170aba..ee2c621 100644
--- a/xfa/fxfa/parser/cxfa_datavalue.h
+++ b/xfa/fxfa/parser/cxfa_datavalue.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DataValue : public CXFA_Node {
+class CXFA_DataValue final : public CXFA_Node {
  public:
   CXFA_DataValue(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DataValue() override;
diff --git a/xfa/fxfa/parser/cxfa_date.cpp b/xfa/fxfa/parser/cxfa_date.cpp
index 984ab32..9508e65 100644
--- a/xfa/fxfa/parser/cxfa_date.cpp
+++ b/xfa/fxfa/parser/cxfa_date.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_date.h"
 
-#include "fxjs/xfa/cjx_date.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDateAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"date";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Date,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Date>(this)) {}
+                {},
+                kDateAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Date::~CXFA_Date() {}
+CXFA_Date::~CXFA_Date() = default;
diff --git a/xfa/fxfa/parser/cxfa_date.h b/xfa/fxfa/parser/cxfa_date.h
index e5ebb25..3fb7ba7 100644
--- a/xfa/fxfa/parser/cxfa_date.h
+++ b/xfa/fxfa/parser/cxfa_date.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Date : public CXFA_Node {
+class CXFA_Date final : public CXFA_Node {
  public:
   CXFA_Date(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Date() override;
diff --git a/xfa/fxfa/parser/cxfa_datepattern.cpp b/xfa/fxfa/parser/cxfa_datepattern.cpp
index 237874b..9f4f635 100644
--- a/xfa/fxfa/parser/cxfa_datepattern.cpp
+++ b/xfa/fxfa/parser/cxfa_datepattern.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_datepattern.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDatePatternAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Med},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"datePattern";
+     (void*)XFA_AttributeValue::Med},
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DatePattern,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDatePatternAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DatePattern::~CXFA_DatePattern() {}
+CXFA_DatePattern::~CXFA_DatePattern() = default;
diff --git a/xfa/fxfa/parser/cxfa_datepattern.h b/xfa/fxfa/parser/cxfa_datepattern.h
index b661478..e67a61f 100644
--- a/xfa/fxfa/parser/cxfa_datepattern.h
+++ b/xfa/fxfa/parser/cxfa_datepattern.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DatePattern : public CXFA_Node {
+class CXFA_DatePattern final : public CXFA_Node {
  public:
   CXFA_DatePattern(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DatePattern() override;
diff --git a/xfa/fxfa/parser/cxfa_datepatterns.cpp b/xfa/fxfa/parser/cxfa_datepatterns.cpp
index 37a380d..9ac1922 100644
--- a/xfa/fxfa/parser/cxfa_datepatterns.cpp
+++ b/xfa/fxfa/parser/cxfa_datepatterns.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_datepatterns.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kDatePatternsPropertyData[] = {
     {XFA_Element::DatePattern, 4, 0},
-    {XFA_Element::Unknown, 0, 0}};
-
-constexpr wchar_t kName[] = L"datePatterns";
+};
 
 }  // namespace
 
@@ -22,8 +23,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::DatePatterns,
-                kPropertyData,
-                nullptr,
-                kName) {}
+                kDatePatternsPropertyData,
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DatePatterns::~CXFA_DatePatterns() {}
+CXFA_DatePatterns::~CXFA_DatePatterns() = default;
diff --git a/xfa/fxfa/parser/cxfa_datepatterns.h b/xfa/fxfa/parser/cxfa_datepatterns.h
index dfc55d0..6c2dca4 100644
--- a/xfa/fxfa/parser/cxfa_datepatterns.h
+++ b/xfa/fxfa/parser/cxfa_datepatterns.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DatePatterns : public CXFA_Node {
+class CXFA_DatePatterns final : public CXFA_Node {
  public:
   CXFA_DatePatterns(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DatePatterns() override;
diff --git a/xfa/fxfa/parser/cxfa_datetime.cpp b/xfa/fxfa/parser/cxfa_datetime.cpp
index 809e40c..313a469 100644
--- a/xfa/fxfa/parser/cxfa_datetime.cpp
+++ b/xfa/fxfa/parser/cxfa_datetime.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_datetime.h"
 
-#include "fxjs/xfa/cjx_datetime.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDateTimeAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"dateTime";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DateTime,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_DateTime>(this)) {}
+                {},
+                kDateTimeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DateTime::~CXFA_DateTime() {}
+CXFA_DateTime::~CXFA_DateTime() = default;
diff --git a/xfa/fxfa/parser/cxfa_datetime.h b/xfa/fxfa/parser/cxfa_datetime.h
index f5cd6f6..ca71daf 100644
--- a/xfa/fxfa/parser/cxfa_datetime.h
+++ b/xfa/fxfa/parser/cxfa_datetime.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DateTime : public CXFA_Node {
+class CXFA_DateTime final : public CXFA_Node {
  public:
   CXFA_DateTime(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DateTime() override;
diff --git a/xfa/fxfa/parser/cxfa_datetimeedit.cpp b/xfa/fxfa/parser/cxfa_datetimeedit.cpp
index 5c9a804..e64abbd 100644
--- a/xfa/fxfa/parser/cxfa_datetimeedit.cpp
+++ b/xfa/fxfa/parser/cxfa_datetimeedit.cpp
@@ -6,27 +6,27 @@
 
 #include "xfa/fxfa/parser/cxfa_datetimeedit.h"
 
-#include "fxjs/xfa/cjx_datetimeedit.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Margin, 1, 0},
-                                                 {XFA_Element::Border, 1, 0},
-                                                 {XFA_Element::Comb, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kDateTimeEditPropertyData[] = {
+    {XFA_Element::Margin, 1, 0},
+    {XFA_Element::Border, 1, 0},
+    {XFA_Element::Comb, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kDateTimeEditAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Picker, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Host},
+     (void*)XFA_AttributeValue::Host},
     {XFA_Attribute::HScrollPolicy, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"dateTimeEdit";
+     (void*)XFA_AttributeValue::Auto},
+};
 
 }  // namespace
 
@@ -36,9 +36,16 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::DateTimeEdit,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_DateTimeEdit>(this)) {}
+                kDateTimeEditPropertyData,
+                kDateTimeEditAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DateTimeEdit::~CXFA_DateTimeEdit() {}
+CXFA_DateTimeEdit::~CXFA_DateTimeEdit() = default;
+
+XFA_Element CXFA_DateTimeEdit::GetValueNodeType() const {
+  return XFA_Element::DateTime;
+}
+
+XFA_FFWidgetType CXFA_DateTimeEdit::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kDateTimeEdit;
+}
diff --git a/xfa/fxfa/parser/cxfa_datetimeedit.h b/xfa/fxfa/parser/cxfa_datetimeedit.h
index bac7879..de76e2a 100644
--- a/xfa/fxfa/parser/cxfa_datetimeedit.h
+++ b/xfa/fxfa/parser/cxfa_datetimeedit.h
@@ -9,10 +9,13 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DateTimeEdit : public CXFA_Node {
+class CXFA_DateTimeEdit final : public CXFA_Node {
  public:
   CXFA_DateTimeEdit(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DateTimeEdit() override;
+
+  XFA_Element GetValueNodeType() const override;
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DATETIMEEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_datetimesymbols.cpp b/xfa/fxfa/parser/cxfa_datetimesymbols.cpp
index 9f29666..159d821 100644
--- a/xfa/fxfa/parser/cxfa_datetimesymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_datetimesymbols.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_datetimesymbols.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"dateTimeSymbols";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_DateTimeSymbols::CXFA_DateTimeSymbols(CXFA_Document* doc,
                                            XFA_PacketType packet)
@@ -19,8 +16,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DateTimeSymbols,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DateTimeSymbols::~CXFA_DateTimeSymbols() {}
+CXFA_DateTimeSymbols::~CXFA_DateTimeSymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_datetimesymbols.h b/xfa/fxfa/parser/cxfa_datetimesymbols.h
index e2296d6..cfbe069 100644
--- a/xfa/fxfa/parser/cxfa_datetimesymbols.h
+++ b/xfa/fxfa/parser/cxfa_datetimesymbols.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DateTimeSymbols : public CXFA_Node {
+class CXFA_DateTimeSymbols final : public CXFA_Node {
  public:
   CXFA_DateTimeSymbols(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DateTimeSymbols() override;
diff --git a/xfa/fxfa/parser/cxfa_day.cpp b/xfa/fxfa/parser/cxfa_day.cpp
index c08b820..ffbeb2f 100644
--- a/xfa/fxfa/parser/cxfa_day.cpp
+++ b/xfa/fxfa/parser/cxfa_day.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_day.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"day";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_Day::CXFA_Day(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -18,8 +15,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Day,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Day::~CXFA_Day() {}
+CXFA_Day::~CXFA_Day() = default;
diff --git a/xfa/fxfa/parser/cxfa_day.h b/xfa/fxfa/parser/cxfa_day.h
index 8d04bf4..6fb2f45 100644
--- a/xfa/fxfa/parser/cxfa_day.h
+++ b/xfa/fxfa/parser/cxfa_day.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Day : public CXFA_Node {
+class CXFA_Day final : public CXFA_Node {
  public:
   CXFA_Day(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Day() override;
diff --git a/xfa/fxfa/parser/cxfa_daynames.cpp b/xfa/fxfa/parser/cxfa_daynames.cpp
index 47468e9..8a3b447 100644
--- a/xfa/fxfa/parser/cxfa_daynames.cpp
+++ b/xfa/fxfa/parser/cxfa_daynames.cpp
@@ -6,15 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_daynames.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Day, 7, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
-    {XFA_Attribute::Abbr, XFA_AttributeType::Boolean, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
+const CXFA_Node::PropertyData kDayNamesPropertyData[] = {
+    {XFA_Element::Day, 7, 0},
+};
 
-constexpr wchar_t kName[] = L"dayNames";
+const CXFA_Node::AttributeData kDayNamesAttributeData[] = {
+    {XFA_Attribute::Abbr, XFA_AttributeType::Boolean, (void*)0},
+};
 
 }  // namespace
 
@@ -24,8 +27,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::DayNames,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kDayNamesPropertyData,
+                kDayNamesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DayNames::~CXFA_DayNames() {}
+CXFA_DayNames::~CXFA_DayNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_daynames.h b/xfa/fxfa/parser/cxfa_daynames.h
index d566e50..229d69c 100644
--- a/xfa/fxfa/parser/cxfa_daynames.h
+++ b/xfa/fxfa/parser/cxfa_daynames.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DayNames : public CXFA_Node {
+class CXFA_DayNames final : public CXFA_Node {
  public:
   CXFA_DayNames(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DayNames() override;
diff --git a/xfa/fxfa/parser/cxfa_debug.cpp b/xfa/fxfa/parser/cxfa_debug.cpp
index 91cfd63..f4b6ac9 100644
--- a/xfa/fxfa/parser/cxfa_debug.cpp
+++ b/xfa/fxfa/parser/cxfa_debug.cpp
@@ -6,16 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_debug.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Uri, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kDebugPropertyData[] = {
+    {XFA_Element::Uri, 1, 0},
+};
+
+const CXFA_Node::AttributeData kDebugAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"debug";
+};
 
 }  // namespace
 
@@ -25,8 +28,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Debug,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kDebugPropertyData,
+                kDebugAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Debug::~CXFA_Debug() {}
+CXFA_Debug::~CXFA_Debug() = default;
diff --git a/xfa/fxfa/parser/cxfa_debug.h b/xfa/fxfa/parser/cxfa_debug.h
index e2e4ed1..1a254f0 100644
--- a/xfa/fxfa/parser/cxfa_debug.h
+++ b/xfa/fxfa/parser/cxfa_debug.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Debug : public CXFA_Node {
+class CXFA_Debug final : public CXFA_Node {
  public:
   CXFA_Debug(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Debug() override;
diff --git a/xfa/fxfa/parser/cxfa_decimal.cpp b/xfa/fxfa/parser/cxfa_decimal.cpp
index c887e8d..93e5e10 100644
--- a/xfa/fxfa/parser/cxfa_decimal.cpp
+++ b/xfa/fxfa/parser/cxfa_decimal.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_decimal.h"
 
-#include "fxjs/xfa/cjx_decimal.h"
+#include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDecimalAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::FracDigits, XFA_AttributeType::Integer, (void*)2},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::LeadDigits, XFA_AttributeType::Integer, (void*)-1},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"decimal";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Decimal,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Decimal>(this)) {}
+                {},
+                kDecimalAttributeData,
+                pdfium::MakeUnique<CJX_Object>(this)) {}
 
-CXFA_Decimal::~CXFA_Decimal() {}
+CXFA_Decimal::~CXFA_Decimal() = default;
diff --git a/xfa/fxfa/parser/cxfa_decimal.h b/xfa/fxfa/parser/cxfa_decimal.h
index da58896..668f61a 100644
--- a/xfa/fxfa/parser/cxfa_decimal.h
+++ b/xfa/fxfa/parser/cxfa_decimal.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Decimal : public CXFA_Node {
+class CXFA_Decimal final : public CXFA_Node {
  public:
   CXFA_Decimal(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Decimal() override;
diff --git a/xfa/fxfa/parser/cxfa_defaulttypeface.cpp b/xfa/fxfa/parser/cxfa_defaulttypeface.cpp
index 60da786..1d38e32 100644
--- a/xfa/fxfa/parser/cxfa_defaulttypeface.cpp
+++ b/xfa/fxfa/parser/cxfa_defaulttypeface.cpp
@@ -6,16 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_defaulttypeface.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDefaultTypefaceAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::WritingScript, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Asterisk},
+     (void*)XFA_AttributeValue::Asterisk},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"defaultTypeface";
+};
 
 }  // namespace
 
@@ -26,8 +27,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::DefaultTypeface,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDefaultTypefaceAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DefaultTypeface::~CXFA_DefaultTypeface() {}
+CXFA_DefaultTypeface::~CXFA_DefaultTypeface() = default;
diff --git a/xfa/fxfa/parser/cxfa_defaulttypeface.h b/xfa/fxfa/parser/cxfa_defaulttypeface.h
index 511ed8f..a241bf8 100644
--- a/xfa/fxfa/parser/cxfa_defaulttypeface.h
+++ b/xfa/fxfa/parser/cxfa_defaulttypeface.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DefaultTypeface : public CXFA_Node {
+class CXFA_DefaultTypeface final : public CXFA_Node {
  public:
   CXFA_DefaultTypeface(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DefaultTypeface() override;
diff --git a/xfa/fxfa/parser/cxfa_defaultui.cpp b/xfa/fxfa/parser/cxfa_defaultui.cpp
index a940084..26114b0 100644
--- a/xfa/fxfa/parser/cxfa_defaultui.cpp
+++ b/xfa/fxfa/parser/cxfa_defaultui.cpp
@@ -6,20 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_defaultui.h"
 
-#include "fxjs/xfa/cjx_defaultui.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kDefaultUiPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kDefaultUiAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"defaultUi";
+};
 
 }  // namespace
 
@@ -29,8 +29,12 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::DefaultUi,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kDefaultUiPropertyData,
+                kDefaultUiAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DefaultUi::~CXFA_DefaultUi() {}
+CXFA_DefaultUi::~CXFA_DefaultUi() = default;
+
+XFA_FFWidgetType CXFA_DefaultUi::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kTextEdit;
+}
diff --git a/xfa/fxfa/parser/cxfa_defaultui.h b/xfa/fxfa/parser/cxfa_defaultui.h
index ce14941..d09da0b 100644
--- a/xfa/fxfa/parser/cxfa_defaultui.h
+++ b/xfa/fxfa/parser/cxfa_defaultui.h
@@ -9,10 +9,12 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DefaultUi : public CXFA_Node {
+class CXFA_DefaultUi final : public CXFA_Node {
  public:
   CXFA_DefaultUi(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DefaultUi() override;
+
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DEFAULTUI_H_
diff --git a/xfa/fxfa/parser/cxfa_delete.cpp b/xfa/fxfa/parser/cxfa_delete.cpp
index 8477fa9..bf76b00 100644
--- a/xfa/fxfa/parser/cxfa_delete.cpp
+++ b/xfa/fxfa/parser/cxfa_delete.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_delete.h"
 
-#include "fxjs/xfa/cjx_delete.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDeleteAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"delete";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Delete,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Delete>(this)) {}
+                {},
+                kDeleteAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Delete::~CXFA_Delete() {}
+CXFA_Delete::~CXFA_Delete() = default;
diff --git a/xfa/fxfa/parser/cxfa_delete.h b/xfa/fxfa/parser/cxfa_delete.h
index 7c6058a..0ea6ab3 100644
--- a/xfa/fxfa/parser/cxfa_delete.h
+++ b/xfa/fxfa/parser/cxfa_delete.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Delete : public CXFA_Node {
+class CXFA_Delete final : public CXFA_Node {
  public:
   CXFA_Delete(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Delete() override;
diff --git a/xfa/fxfa/parser/cxfa_delta.cpp b/xfa/fxfa/parser/cxfa_delta.cpp
index 4b5bf77..c48b2b3 100644
--- a/xfa/fxfa/parser/cxfa_delta.cpp
+++ b/xfa/fxfa/parser/cxfa_delta.cpp
@@ -9,21 +9,14 @@
 #include "fxjs/xfa/cjx_delta.h"
 #include "third_party/base/ptr_util.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"delta";
-
-}  // namespace
-
 CXFA_Delta::CXFA_Delta(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
                 XFA_XDPPACKET_Form,
                 XFA_ObjectType::Object,
                 XFA_Element::Delta,
-                nullptr,
-                nullptr,
-                kName,
+                {},
+                {},
                 pdfium::MakeUnique<CJX_Delta>(this)) {}
 
-CXFA_Delta::~CXFA_Delta() {}
+CXFA_Delta::~CXFA_Delta() = default;
diff --git a/xfa/fxfa/parser/cxfa_delta.h b/xfa/fxfa/parser/cxfa_delta.h
index 693287b..40bfa56 100644
--- a/xfa/fxfa/parser/cxfa_delta.h
+++ b/xfa/fxfa/parser/cxfa_delta.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Delta : public CXFA_Node {
+class CXFA_Delta final : public CXFA_Node {
  public:
   CXFA_Delta(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Delta() override;
diff --git a/xfa/fxfa/parser/cxfa_deltas.cpp b/xfa/fxfa/parser/cxfa_deltas.cpp
index 4db0936..c55595c 100644
--- a/xfa/fxfa/parser/cxfa_deltas.cpp
+++ b/xfa/fxfa/parser/cxfa_deltas.cpp
@@ -6,10 +6,10 @@
 
 #include "xfa/fxfa/parser/cxfa_deltas.h"
 
-#include "fxjs/xfa/cjx_deltas.h"
+#include "fxjs/xfa/cjx_list.h"
 #include "third_party/base/ptr_util.h"
 
 CXFA_Deltas::CXFA_Deltas(CXFA_Document* doc)
-    : CXFA_List(doc, pdfium::MakeUnique<CJX_Deltas>(this)) {}
+    : CXFA_List(doc, pdfium::MakeUnique<CJX_List>(this)) {}
 
 CXFA_Deltas::~CXFA_Deltas() {}
diff --git a/xfa/fxfa/parser/cxfa_desc.cpp b/xfa/fxfa/parser/cxfa_desc.cpp
index 9cda06d..dc26ca4 100644
--- a/xfa/fxfa/parser/cxfa_desc.cpp
+++ b/xfa/fxfa/parser/cxfa_desc.cpp
@@ -11,20 +11,19 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kDescPropertyData[] = {
     {XFA_Element::Text, 1, 0},     {XFA_Element::Time, 1, 0},
     {XFA_Element::DateTime, 1, 0}, {XFA_Element::Image, 1, 0},
     {XFA_Element::Decimal, 1, 0},  {XFA_Element::Boolean, 1, 0},
     {XFA_Element::Integer, 1, 0},  {XFA_Element::ExData, 1, 0},
     {XFA_Element::Date, 1, 0},     {XFA_Element::Float, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kDescAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"desc";
+};
 
 }  // namespace
 
@@ -34,9 +33,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Desc,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kDescPropertyData,
+                kDescAttributeData,
                 pdfium::MakeUnique<CJX_Desc>(this)) {}
 
-CXFA_Desc::~CXFA_Desc() {}
+CXFA_Desc::~CXFA_Desc() = default;
diff --git a/xfa/fxfa/parser/cxfa_desc.h b/xfa/fxfa/parser/cxfa_desc.h
index 1811c63..11baa90 100644
--- a/xfa/fxfa/parser/cxfa_desc.h
+++ b/xfa/fxfa/parser/cxfa_desc.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Desc : public CXFA_Node {
+class CXFA_Desc final : public CXFA_Node {
  public:
   CXFA_Desc(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Desc() override;
diff --git a/xfa/fxfa/parser/cxfa_destination.cpp b/xfa/fxfa/parser/cxfa_destination.cpp
index 4e5d10b..b80dceb 100644
--- a/xfa/fxfa/parser/cxfa_destination.cpp
+++ b/xfa/fxfa/parser/cxfa_destination.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_destination.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDestinationAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"destination";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Destination,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDestinationAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Destination::~CXFA_Destination() {}
+CXFA_Destination::~CXFA_Destination() = default;
diff --git a/xfa/fxfa/parser/cxfa_destination.h b/xfa/fxfa/parser/cxfa_destination.h
index d9fc553..d31434f 100644
--- a/xfa/fxfa/parser/cxfa_destination.h
+++ b/xfa/fxfa/parser/cxfa_destination.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Destination : public CXFA_Node {
+class CXFA_Destination final : public CXFA_Node {
  public:
   CXFA_Destination(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Destination() override;
diff --git a/xfa/fxfa/parser/cxfa_digestmethod.cpp b/xfa/fxfa/parser/cxfa_digestmethod.cpp
index e74832e..70c781d 100644
--- a/xfa/fxfa/parser/cxfa_digestmethod.cpp
+++ b/xfa/fxfa/parser/cxfa_digestmethod.cpp
@@ -6,18 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_digestmethod.h"
 
-#include "fxjs/xfa/cjx_digestmethod.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDigestMethodAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"digestMethod";
+};
 
 }  // namespace
 
@@ -27,9 +25,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeC,
                 XFA_Element::DigestMethod,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_DigestMethod>(this)) {}
+                {},
+                kDigestMethodAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DigestMethod::~CXFA_DigestMethod() {}
+CXFA_DigestMethod::~CXFA_DigestMethod() = default;
diff --git a/xfa/fxfa/parser/cxfa_digestmethod.h b/xfa/fxfa/parser/cxfa_digestmethod.h
index 4ac22bb..777f46a 100644
--- a/xfa/fxfa/parser/cxfa_digestmethod.h
+++ b/xfa/fxfa/parser/cxfa_digestmethod.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DigestMethod : public CXFA_Node {
+class CXFA_DigestMethod final : public CXFA_Node {
  public:
   CXFA_DigestMethod(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DigestMethod() override;
diff --git a/xfa/fxfa/parser/cxfa_digestmethods.cpp b/xfa/fxfa/parser/cxfa_digestmethods.cpp
index 4993fd0..2a1d1a2 100644
--- a/xfa/fxfa/parser/cxfa_digestmethods.cpp
+++ b/xfa/fxfa/parser/cxfa_digestmethods.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_digestmethods.h"
 
-#include "fxjs/xfa/cjx_digestmethods.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDigestMethodsAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"digestMethods";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::DigestMethods,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_DigestMethods>(this)) {}
+                {},
+                kDigestMethodsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DigestMethods::~CXFA_DigestMethods() {}
+CXFA_DigestMethods::~CXFA_DigestMethods() = default;
diff --git a/xfa/fxfa/parser/cxfa_digestmethods.h b/xfa/fxfa/parser/cxfa_digestmethods.h
index 65d40fb..61dac97 100644
--- a/xfa/fxfa/parser/cxfa_digestmethods.h
+++ b/xfa/fxfa/parser/cxfa_digestmethods.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DigestMethods : public CXFA_Node {
+class CXFA_DigestMethods final : public CXFA_Node {
  public:
   CXFA_DigestMethods(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DigestMethods() override;
diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp
index 0675e35..1a2c5b9 100644
--- a/xfa/fxfa/parser/cxfa_document.cpp
+++ b/xfa/fxfa/parser/cxfa_document.cpp
@@ -6,8 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_document.h"
 
+#include <set>
+#include <utility>
+
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_engine.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/compiler_specific.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+#include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cscript_datawindow.h"
 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
@@ -15,26 +24,115 @@
 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
 #include "xfa/fxfa/parser/cscript_logpseudomodel.h"
 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
+#include "xfa/fxfa/parser/cxfa_bind.h"
 #include "xfa/fxfa/parser/cxfa_datagroup.h"
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
+#include "xfa/fxfa/parser/cxfa_exdata.h"
+#include "xfa/fxfa/parser/cxfa_form.h"
+#include "xfa/fxfa/parser/cxfa_image.h"
 #include "xfa/fxfa/parser/cxfa_interactive.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_items.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_occur.h"
+#include "xfa/fxfa/parser/cxfa_pageset.h"
 #include "xfa/fxfa/parser/cxfa_pdf.h"
 #include "xfa/fxfa/parser/cxfa_present.h"
+#include "xfa/fxfa/parser/cxfa_subform.h"
+#include "xfa/fxfa/parser/cxfa_template.h"
+#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
+#include "xfa/fxfa/parser/cxfa_value.h"
+#include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 namespace {
 
-constexpr const wchar_t kTemplateNS[] =
-    L"http://www.xfa.org/schema/xfa-template/";
+const wchar_t kTemplateNS[] = L"http://www.xfa.org/schema/xfa-template/";
 
-void MergeNodeRecurse(CXFA_Document* pDocument,
-                      CXFA_Node* pDestNodeParent,
-                      CXFA_Node* pProtoNode) {
+struct RecurseRecord {
+  CXFA_Node* pTemplateChild;
+  CXFA_Node* pDataChild;
+};
+
+class CXFA_TraverseStrategy_DDGroup {
+ public:
+  static CXFA_Node* GetFirstChild(CXFA_Node* pDDGroupNode) {
+    return pDDGroupNode->GetFirstChildByName(XFA_HASHCODE_Group);
+  }
+  static CXFA_Node* GetNextSibling(CXFA_Node* pDDGroupNode) {
+    return pDDGroupNode->GetNextSameNameSibling(XFA_HASHCODE_Group);
+  }
+  static CXFA_Node* GetParent(CXFA_Node* pDDGroupNode) {
+    return pDDGroupNode->GetParent();
+  }
+};
+
+void FormValueNode_MatchNoneCreateChild(CXFA_Node* pFormNode) {
+  ASSERT(pFormNode->IsWidgetReady());
+  // GetUIChildNode has the side effect of creating the UI child.
+  pFormNode->GetUIChildNode();
+}
+
+CXFA_Node* FormValueNode_CreateChild(CXFA_Node* pValueNode, XFA_Element iType) {
+  CXFA_Node* pChildNode = pValueNode->GetFirstChild();
+  if (!pChildNode) {
+    if (iType == XFA_Element::Unknown)
+      return nullptr;
+
+    pChildNode =
+        pValueNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, iType);
+  }
+  return pChildNode;
+}
+
+void FormValueNode_SetChildContent(CXFA_Node* pValueNode,
+                                   const WideString& wsContent,
+                                   XFA_Element iType) {
+  if (!pValueNode)
+    return;
+
+  ASSERT(pValueNode->GetPacketType() == XFA_PacketType::Form);
+  CXFA_Node* pChildNode = FormValueNode_CreateChild(pValueNode, iType);
+  if (!pChildNode)
+    return;
+
+  switch (pChildNode->GetObjectType()) {
+    case XFA_ObjectType::ContentNode: {
+      CXFA_Node* pContentRawDataNode = pChildNode->GetFirstChild();
+      if (!pContentRawDataNode) {
+        XFA_Element element = XFA_Element::Sharptext;
+        if (pChildNode->GetElementType() == XFA_Element::ExData) {
+          Optional<WideString> contentType =
+              pChildNode->JSObject()->TryAttribute(XFA_Attribute::ContentType,
+                                                   false);
+          if (contentType.has_value()) {
+            if (contentType.value().EqualsASCII("text/html"))
+              element = XFA_Element::SharpxHTML;
+            else if (contentType.value().EqualsASCII("text/xml"))
+              element = XFA_Element::Sharpxml;
+          }
+        }
+        pContentRawDataNode = pChildNode->CreateSamePacketNode(element);
+        pChildNode->InsertChildAndNotify(pContentRawDataNode, nullptr);
+      }
+      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
+                                                false, false);
+      break;
+    }
+    case XFA_ObjectType::NodeC:
+    case XFA_ObjectType::TextNode:
+    case XFA_ObjectType::NodeV: {
+      pChildNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent, false,
+                                       false);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void MergeNodeRecurse(CXFA_Node* pDestNodeParent, CXFA_Node* pProtoNode) {
   CXFA_Node* pExistingNode = nullptr;
   for (CXFA_Node* pFormChild = pDestNodeParent->GetFirstChild(); pFormChild;
        pFormChild = pFormChild->GetNextSibling()) {
@@ -51,29 +149,27 @@
     pExistingNode->SetTemplateNode(pProtoNode);
     for (CXFA_Node* pTemplateChild = pProtoNode->GetFirstChild();
          pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
-      MergeNodeRecurse(pDocument, pExistingNode, pTemplateChild);
+      MergeNodeRecurse(pExistingNode, pTemplateChild);
     }
     return;
   }
   CXFA_Node* pNewNode = pProtoNode->Clone(true);
   pNewNode->SetTemplateNode(pProtoNode);
-  pDestNodeParent->InsertChild(pNewNode, nullptr);
+  pDestNodeParent->InsertChildAndNotify(pNewNode, nullptr);
 }
 
-void MergeNode(CXFA_Document* pDocument,
-               CXFA_Node* pDestNode,
-               CXFA_Node* pProtoNode) {
+void MergeNode(CXFA_Node* pDestNode, CXFA_Node* pProtoNode) {
   {
     CXFA_NodeIterator sIterator(pDestNode);
     for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
          pNode = sIterator.MoveToNext()) {
-      pNode->SetFlag(XFA_NodeFlag_UnusedNode, true);
+      pNode->SetFlag(XFA_NodeFlag_UnusedNode);
     }
   }
   pDestNode->SetTemplateNode(pProtoNode);
   for (CXFA_Node* pTemplateChild = pProtoNode->GetFirstChild(); pTemplateChild;
        pTemplateChild = pTemplateChild->GetNextSibling()) {
-    MergeNodeRecurse(pDocument, pDestNode, pTemplateChild);
+    MergeNodeRecurse(pDestNode, pTemplateChild);
   }
   {
     CXFA_NodeIterator sIterator(pDestNode);
@@ -84,42 +180,1114 @@
   }
 }
 
+CXFA_Node* CloneOrMergeInstanceManager(CXFA_Document* pDocument,
+                                       CXFA_Node* pFormParent,
+                                       CXFA_Node* pTemplateNode,
+                                       std::vector<CXFA_Node*>* subforms) {
+  WideString wsSubformName =
+      pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
+  WideString wsInstMgrNodeName = L"_" + wsSubformName;
+  uint32_t dwInstNameHash =
+      FX_HashCode_GetW(wsInstMgrNodeName.AsStringView(), false);
+  CXFA_Node* pExistingNode = XFA_DataMerge_FindFormDOMInstance(
+      pDocument, XFA_Element::InstanceManager, dwInstNameHash, pFormParent);
+  if (pExistingNode) {
+    uint32_t dwNameHash = pTemplateNode->GetNameHash();
+    for (CXFA_Node* pNode = pExistingNode->GetNextSibling(); pNode;) {
+      XFA_Element eCurType = pNode->GetElementType();
+      if (eCurType == XFA_Element::InstanceManager)
+        break;
+
+      if ((eCurType != XFA_Element::Subform) &&
+          (eCurType != XFA_Element::SubformSet)) {
+        pNode = pNode->GetNextSibling();
+        continue;
+      }
+      if (dwNameHash != pNode->GetNameHash())
+        break;
+
+      CXFA_Node* pNextNode = pNode->GetNextSibling();
+      pFormParent->RemoveChildAndNotify(pNode, true);
+      subforms->push_back(pNode);
+      pNode = pNextNode;
+    }
+    pFormParent->RemoveChildAndNotify(pExistingNode, true);
+    pFormParent->InsertChildAndNotify(pExistingNode, nullptr);
+    pExistingNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+    pExistingNode->SetTemplateNode(pTemplateNode);
+    return pExistingNode;
+  }
+
+  CXFA_Node* pNewNode =
+      pDocument->CreateNode(XFA_PacketType::Form, XFA_Element::InstanceManager);
+  wsInstMgrNodeName =
+      L"_" + pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
+  pNewNode->JSObject()->SetCData(XFA_Attribute::Name, wsInstMgrNodeName, false,
+                                 false);
+  pFormParent->InsertChildAndNotify(pNewNode, nullptr);
+  pNewNode->SetTemplateNode(pTemplateNode);
+  return pNewNode;
+}
+
+void SortRecurseRecord(std::vector<RecurseRecord>* rgRecords,
+                       CXFA_Node* pDataScope,
+                       bool bChoiceMode) {
+  std::vector<RecurseRecord> rgResultRecord;
+  for (CXFA_Node* pNode = pDataScope->GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
+    auto it = std::find_if(rgRecords->begin(), rgRecords->end(),
+                           [pNode](const RecurseRecord& record) {
+                             return pNode == record.pDataChild;
+                           });
+    if (it != rgRecords->end()) {
+      rgResultRecord.push_back(*it);
+      rgRecords->erase(it);
+      if (bChoiceMode)
+        break;
+    }
+  }
+  if (rgResultRecord.empty())
+    return;
+
+  if (!bChoiceMode) {
+    rgResultRecord.insert(rgResultRecord.end(), rgRecords->begin(),
+                          rgRecords->end());
+  }
+  *rgRecords = rgResultRecord;
+}
+
+CXFA_Node* ScopeMatchGlobalBinding(CXFA_Node* pDataScope,
+                                   uint32_t dwNameHash,
+                                   XFA_Element eMatchDataNodeType,
+                                   bool bUpLevel) {
+  for (CXFA_Node *pCurDataScope = pDataScope, *pLastDataScope = nullptr;
+       pCurDataScope &&
+       pCurDataScope->GetPacketType() == XFA_PacketType::Datasets;
+       pLastDataScope = pCurDataScope,
+                 pCurDataScope = pCurDataScope->GetParent()) {
+    for (CXFA_Node* pDataChild = pCurDataScope->GetFirstChildByName(dwNameHash);
+         pDataChild;
+         pDataChild = pDataChild->GetNextSameNameSibling(dwNameHash)) {
+      if (pDataChild == pLastDataScope ||
+          (eMatchDataNodeType != XFA_Element::DataModel &&
+           pDataChild->GetElementType() != eMatchDataNodeType) ||
+          pDataChild->HasBindItem()) {
+        continue;
+      }
+      return pDataChild;
+    }
+
+    for (CXFA_DataGroup* pDataChild =
+             pCurDataScope->GetFirstChildByClass<CXFA_DataGroup>(
+                 XFA_Element::DataGroup);
+         pDataChild;
+         pDataChild = pDataChild->GetNextSameClassSibling<CXFA_DataGroup>(
+             XFA_Element::DataGroup)) {
+      CXFA_Node* pDataNode = ScopeMatchGlobalBinding(pDataChild, dwNameHash,
+                                                     eMatchDataNodeType, false);
+      if (pDataNode)
+        return pDataNode;
+    }
+    if (!bUpLevel)
+      break;
+  }
+  return nullptr;
+}
+
+CXFA_Node* FindGlobalDataNode(CXFA_Document* pDocument,
+                              const WideString& wsName,
+                              CXFA_Node* pDataScope,
+                              XFA_Element eMatchNodeType) {
+  if (wsName.IsEmpty())
+    return nullptr;
+
+  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
+  CXFA_Node* pBounded = pDocument->GetGlobalBinding(dwNameHash);
+  if (!pBounded) {
+    pBounded =
+        ScopeMatchGlobalBinding(pDataScope, dwNameHash, eMatchNodeType, true);
+    if (pBounded)
+      pDocument->RegisterGlobalBinding(dwNameHash, pBounded);
+  }
+  return pBounded;
+}
+
+CXFA_Node* FindOnceDataNode(const WideString& wsName,
+                            CXFA_Node* pDataScope,
+                            XFA_Element eMatchNodeType) {
+  if (wsName.IsEmpty())
+    return nullptr;
+
+  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
+  CXFA_Node* pLastDataScope = nullptr;
+  for (CXFA_Node* pCurDataScope = pDataScope;
+       pCurDataScope &&
+       pCurDataScope->GetPacketType() == XFA_PacketType::Datasets;
+       pCurDataScope = pCurDataScope->GetParent()) {
+    for (CXFA_Node* pDataChild = pCurDataScope->GetFirstChildByName(dwNameHash);
+         pDataChild;
+         pDataChild = pDataChild->GetNextSameNameSibling(dwNameHash)) {
+      if (pDataChild == pLastDataScope || pDataChild->HasBindItem() ||
+          (eMatchNodeType != XFA_Element::DataModel &&
+           pDataChild->GetElementType() != eMatchNodeType)) {
+        continue;
+      }
+      return pDataChild;
+    }
+    pLastDataScope = pCurDataScope;
+  }
+  return nullptr;
+}
+
+CXFA_Node* FindDataRefDataNode(CXFA_Document* pDocument,
+                               const WideString& wsRef,
+                               CXFA_Node* pDataScope,
+                               XFA_Element eMatchNodeType,
+                               CXFA_Node* pTemplateNode,
+                               bool bForceBind,
+                               bool bUpLevel) {
+  uint32_t dFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_BindNew;
+  if (bUpLevel || !wsRef.EqualsASCII("name"))
+    dFlags |= (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
+
+  XFA_RESOLVENODE_RS rs;
+  pDocument->GetScriptContext()->ResolveObjects(
+      pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
+  if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeAll ||
+      rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeMidAll ||
+      rs.objects.size() > 1) {
+    return pDocument->GetNotBindNode(rs.objects);
+  }
+
+  if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
+    CXFA_Object* pObject =
+        !rs.objects.empty() ? rs.objects.front().Get() : nullptr;
+    CXFA_Node* pNode = ToNode(pObject);
+    return (bForceBind || !pNode || !pNode->HasBindItem()) ? pNode : nullptr;
+  }
+  return nullptr;
+}
+
+CXFA_Node* FindMatchingDataNode(
+    CXFA_Document* pDocument,
+    CXFA_Node* pTemplateNode,
+    CXFA_Node* pDataScope,
+    bool& bAccessedDataDOM,
+    bool bForceBind,
+    CXFA_NodeIteratorTemplate<CXFA_Node,
+                              CXFA_TraverseStrategy_XFAContainerNode>*
+        pIterator,
+    bool& bSelfMatch,
+    XFA_AttributeValue& eBindMatch,
+    bool bUpLevel) {
+  CXFA_Node* pResult = nullptr;
+  CXFA_Node* pCurTemplateNode = pIterator->GetCurrent();
+  while (pCurTemplateNode) {
+    XFA_Element eMatchNodeType;
+    switch (pCurTemplateNode->GetElementType()) {
+      case XFA_Element::Subform:
+        eMatchNodeType = XFA_Element::DataGroup;
+        break;
+      case XFA_Element::Field: {
+        eMatchNodeType = XFA_FieldIsMultiListBox(pCurTemplateNode)
+                             ? XFA_Element::DataGroup
+                             : XFA_Element::DataValue;
+      } break;
+      case XFA_Element::ExclGroup:
+        eMatchNodeType = XFA_Element::DataValue;
+        break;
+      default:
+        pCurTemplateNode = pIterator->MoveToNext();
+        continue;
+    }
+
+    CXFA_Occur* pTemplateNodeOccur =
+        pCurTemplateNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+    if (pTemplateNodeOccur) {
+      int32_t iMin;
+      int32_t iMax;
+      int32_t iInit;
+      std::tie(iMin, iMax, iInit) = pTemplateNodeOccur->GetOccurInfo();
+      if (iMax == 0) {
+        pCurTemplateNode = pIterator->MoveToNext();
+        continue;
+      }
+    }
+
+    CXFA_Bind* pTemplateNodeBind =
+        pCurTemplateNode->GetFirstChildByClass<CXFA_Bind>(XFA_Element::Bind);
+    XFA_AttributeValue eMatch =
+        pTemplateNodeBind
+            ? pTemplateNodeBind->JSObject()->GetEnum(XFA_Attribute::Match)
+            : XFA_AttributeValue::Once;
+    eBindMatch = eMatch;
+    switch (eMatch) {
+      case XFA_AttributeValue::None:
+        pCurTemplateNode = pIterator->MoveToNext();
+        continue;
+      case XFA_AttributeValue::Global:
+        bAccessedDataDOM = true;
+        if (!bForceBind) {
+          pCurTemplateNode = pIterator->MoveToNext();
+          continue;
+        }
+        if (eMatchNodeType == XFA_Element::DataValue ||
+            (eMatchNodeType == XFA_Element::DataGroup &&
+             XFA_FieldIsMultiListBox(pTemplateNodeBind))) {
+          CXFA_Node* pGlobalBindNode = FindGlobalDataNode(
+              pDocument,
+              pCurTemplateNode->JSObject()->GetCData(XFA_Attribute::Name),
+              pDataScope, eMatchNodeType);
+          if (!pGlobalBindNode) {
+            pCurTemplateNode = pIterator->MoveToNext();
+            continue;
+          }
+          pResult = pGlobalBindNode;
+          break;
+        }
+        FALLTHROUGH;
+      case XFA_AttributeValue::Once: {
+        bAccessedDataDOM = true;
+        CXFA_Node* pOnceBindNode = FindOnceDataNode(
+            pCurTemplateNode->JSObject()->GetCData(XFA_Attribute::Name),
+            pDataScope, eMatchNodeType);
+        if (!pOnceBindNode) {
+          pCurTemplateNode = pIterator->MoveToNext();
+          continue;
+        }
+        pResult = pOnceBindNode;
+        break;
+      }
+      case XFA_AttributeValue::DataRef: {
+        bAccessedDataDOM = true;
+        CXFA_Node* pDataRefBindNode = FindDataRefDataNode(
+            pDocument,
+            pTemplateNodeBind->JSObject()->GetCData(XFA_Attribute::Ref),
+            pDataScope, eMatchNodeType, pTemplateNode, bForceBind, bUpLevel);
+        if (pDataRefBindNode &&
+            pDataRefBindNode->GetElementType() == eMatchNodeType) {
+          pResult = pDataRefBindNode;
+        }
+        if (!pResult) {
+          pCurTemplateNode = pIterator->SkipChildrenAndMoveToNext();
+          continue;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+    if (pCurTemplateNode == pTemplateNode && pResult)
+      bSelfMatch = true;
+    break;
+  }
+  return pResult;
+}
+
+void CreateDataBinding(CXFA_Node* pFormNode,
+                       CXFA_Node* pDataNode,
+                       bool bDataToForm) {
+  pFormNode->SetBindingNode(pDataNode);
+  pDataNode->AddBindItem(pFormNode);
+  XFA_Element eType = pFormNode->GetElementType();
+  if (eType != XFA_Element::Field && eType != XFA_Element::ExclGroup)
+    return;
+
+  ASSERT(pFormNode->IsWidgetReady());
+  auto* defValue = pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
+      0, XFA_Element::Value);
+  if (!bDataToForm) {
+    WideString wsValue;
+    switch (pFormNode->GetFFWidgetType()) {
+      case XFA_FFWidgetType::kImageEdit: {
+        CXFA_Image* image = defValue ? defValue->GetImageIfExists() : nullptr;
+        WideString wsContentType;
+        WideString wsHref;
+        if (image) {
+          wsValue = image->GetContent();
+          wsContentType = image->GetContentType();
+          wsHref = image->GetHref();
+        }
+        CFX_XMLElement* pXMLDataElement =
+            ToXMLElement(pDataNode->GetXMLMappingNode());
+        ASSERT(pXMLDataElement);
+        pDataNode->JSObject()->SetAttributeValue(
+            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+        pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
+                                        wsContentType, false, false);
+        if (!wsHref.IsEmpty())
+          pXMLDataElement->SetAttribute(L"href", wsHref);
+
+        break;
+      }
+      case XFA_FFWidgetType::kChoiceList:
+        wsValue = defValue ? defValue->GetChildValueContent() : WideString();
+        if (pFormNode->IsChoiceListMultiSelect()) {
+          std::vector<WideString> wsSelTextArray =
+              pFormNode->GetSelectedItemsValue();
+          if (!wsSelTextArray.empty()) {
+            for (const auto& text : wsSelTextArray) {
+              CXFA_Node* pValue =
+                  pDataNode->CreateSamePacketNode(XFA_Element::DataValue);
+              pValue->JSObject()->SetCData(XFA_Attribute::Name, L"value", false,
+                                           false);
+              pValue->CreateXMLMappingNode();
+              pDataNode->InsertChildAndNotify(pValue, nullptr);
+              pValue->JSObject()->SetCData(XFA_Attribute::Value, text, false,
+                                           false);
+            }
+          } else {
+            CFX_XMLElement* pElement =
+                ToXMLElement(pDataNode->GetXMLMappingNode());
+            pElement->SetAttribute(L"xfa:dataNode", L"dataGroup");
+          }
+        } else if (!wsValue.IsEmpty()) {
+          pDataNode->JSObject()->SetAttributeValue(
+              wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+        }
+        break;
+      case XFA_FFWidgetType::kCheckButton:
+        wsValue = defValue ? defValue->GetChildValueContent() : WideString();
+        if (wsValue.IsEmpty())
+          break;
+
+        pDataNode->JSObject()->SetAttributeValue(
+            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+        break;
+      case XFA_FFWidgetType::kExclGroup: {
+        CXFA_Node* pChecked = nullptr;
+        CXFA_Node* pChild = pFormNode->GetFirstChild();
+        for (; pChild; pChild = pChild->GetNextSibling()) {
+          if (pChild->GetElementType() != XFA_Element::Field)
+            continue;
+
+          auto* pValue =
+              pChild->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+          if (!pValue)
+            continue;
+
+          wsValue = pValue->GetChildValueContent();
+          if (wsValue.IsEmpty())
+            continue;
+
+          CXFA_Items* pItems =
+              pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+          if (!pItems)
+            continue;
+
+          CXFA_Node* pText = pItems->GetFirstChild();
+          if (!pText)
+            continue;
+
+          WideString wsContent = pText->JSObject()->GetContent(false);
+          if (wsContent == wsValue) {
+            pChecked = pChild;
+            pDataNode->JSObject()->SetAttributeValue(wsValue, wsValue, false,
+                                                     false);
+            pFormNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
+                                            false, false);
+            break;
+          }
+        }
+        if (!pChecked)
+          break;
+
+        pChild = pFormNode->GetFirstChild();
+        for (; pChild; pChild = pChild->GetNextSibling()) {
+          if (pChild == pChecked)
+            continue;
+          if (pChild->GetElementType() != XFA_Element::Field)
+            continue;
+
+          CXFA_Value* pValue =
+              pChild->JSObject()->GetOrCreateProperty<CXFA_Value>(
+                  0, XFA_Element::Value);
+          CXFA_Items* pItems =
+              pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+          CXFA_Node* pText = pItems ? pItems->GetFirstChild() : nullptr;
+          if (pText)
+            pText = pText->GetNextSibling();
+
+          WideString wsContent;
+          if (pText)
+            wsContent = pText->JSObject()->GetContent(false);
+
+          FormValueNode_SetChildContent(pValue, wsContent, XFA_Element::Text);
+        }
+        break;
+      }
+      case XFA_FFWidgetType::kNumericEdit: {
+        wsValue = defValue ? defValue->GetChildValueContent() : WideString();
+        if (wsValue.IsEmpty())
+          break;
+
+        wsValue = pFormNode->NormalizeNumStr(wsValue);
+        pDataNode->JSObject()->SetAttributeValue(
+            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+        CXFA_Value* pValue =
+            pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
+                0, XFA_Element::Value);
+        FormValueNode_SetChildContent(pValue, wsValue, XFA_Element::Float);
+        break;
+      }
+      default:
+        wsValue = defValue ? defValue->GetChildValueContent() : WideString();
+        if (wsValue.IsEmpty())
+          break;
+
+        pDataNode->JSObject()->SetAttributeValue(
+            wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
+        break;
+    }
+    return;
+  }
+
+  WideString wsXMLValue = pDataNode->JSObject()->GetContent(false);
+  WideString wsNormalizeValue = pFormNode->GetNormalizeDataValue(wsXMLValue);
+
+  pDataNode->JSObject()->SetAttributeValue(wsNormalizeValue, wsXMLValue, false,
+                                           false);
+  switch (pFormNode->GetFFWidgetType()) {
+    case XFA_FFWidgetType::kImageEdit: {
+      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
+                                    XFA_Element::Image);
+      CXFA_Image* image = defValue ? defValue->GetImageIfExists() : nullptr;
+      if (image) {
+        CFX_XMLElement* pXMLDataElement =
+            ToXMLElement(pDataNode->GetXMLMappingNode());
+        WideString wsContentType =
+            pXMLDataElement->GetAttribute(L"xfa:contentType");
+        if (!wsContentType.IsEmpty()) {
+          pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
+                                          wsContentType, false, false);
+          image->SetContentType(wsContentType);
+        }
+
+        WideString wsHref = pXMLDataElement->GetAttribute(L"href");
+        if (!wsHref.IsEmpty())
+          image->SetHref(wsHref);
+      }
+      break;
+    }
+    case XFA_FFWidgetType::kChoiceList:
+      if (pFormNode->IsChoiceListMultiSelect()) {
+        std::vector<CXFA_Node*> items = pDataNode->GetNodeListWithFilter(
+            XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties);
+        if (!items.empty()) {
+          bool single = items.size() == 1;
+          wsNormalizeValue.clear();
+
+          for (CXFA_Node* pNode : items) {
+            WideString wsItem = pNode->JSObject()->GetContent(false);
+            if (single)
+              wsItem += L"\n";
+
+            wsNormalizeValue += wsItem;
+          }
+          CXFA_ExData* exData =
+              defValue ? defValue->GetExDataIfExists() : nullptr;
+          if (exData)
+            exData->SetContentType(single ? L"text/plain" : L"text/xml");
+        }
+        FormValueNode_SetChildContent(defValue, wsNormalizeValue,
+                                      XFA_Element::ExData);
+      } else {
+        FormValueNode_SetChildContent(defValue, wsNormalizeValue,
+                                      XFA_Element::Text);
+      }
+      break;
+    case XFA_FFWidgetType::kExclGroup: {
+      pFormNode->SetSelectedMemberByValue(wsNormalizeValue.AsStringView(),
+                                          false, false, false);
+      break;
+    }
+    case XFA_FFWidgetType::kDateTimeEdit:
+      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
+                                    XFA_Element::DateTime);
+      break;
+    case XFA_FFWidgetType::kNumericEdit: {
+      WideString wsPicture =
+          pFormNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
+      if (wsPicture.IsEmpty())
+        wsNormalizeValue = pFormNode->NormalizeNumStr(wsNormalizeValue);
+
+      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
+                                    XFA_Element::Float);
+      break;
+    }
+    default:
+      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
+                                    XFA_Element::Text);
+      break;
+  }
+}
+
+CXFA_Node* MaybeCreateDataNode(CXFA_Document* pDocument,
+                               CXFA_Node* pDataParent,
+                               XFA_Element eNodeType,
+                               const WideString& wsName) {
+  if (!pDataParent)
+    return nullptr;
+
+  CXFA_Node* pParentDDNode = pDataParent->GetDataDescriptionNode();
+  if (!pParentDDNode) {
+    CXFA_Node* pDataNode =
+        pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
+    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
+    pDataNode->CreateXMLMappingNode();
+    pDataParent->InsertChildAndNotify(pDataNode, nullptr);
+    pDataNode->SetFlag(XFA_NodeFlag_Initialized);
+    return pDataNode;
+  }
+
+  CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_DDGroup> sIterator(
+      pParentDDNode);
+  for (CXFA_Node* pDDGroupNode = sIterator.GetCurrent(); pDDGroupNode;
+       pDDGroupNode = sIterator.MoveToNext()) {
+    if (pDDGroupNode != pParentDDNode) {
+      if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
+        continue;
+
+      Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
+      if (!ns.has_value() ||
+          !ns.value().EqualsASCII("http://ns.adobe.com/data-description/")) {
+        continue;
+      }
+    }
+
+    CXFA_Node* pDDNode =
+        pDDGroupNode->GetFirstChildByName(wsName.AsStringView());
+    if (!pDDNode)
+      continue;
+    if (pDDNode->GetElementType() != eNodeType)
+      break;
+
+    CXFA_Node* pDataNode =
+        pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
+    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
+    pDataNode->CreateXMLMappingNode();
+    if (eNodeType == XFA_Element::DataValue &&
+        pDDNode->JSObject()->GetEnum(XFA_Attribute::Contains) ==
+            XFA_AttributeValue::MetaData) {
+      pDataNode->JSObject()->SetEnum(XFA_Attribute::Contains,
+                                     XFA_AttributeValue::MetaData, false);
+    }
+    pDataParent->InsertChildAndNotify(pDataNode, nullptr);
+    pDataNode->SetDataDescriptionNode(pDDNode);
+    pDataNode->SetFlag(XFA_NodeFlag_Initialized);
+    return pDataNode;
+  }
+  return nullptr;
+}
+
+CXFA_Node* CopyContainer_Field(CXFA_Document* pDocument,
+                               CXFA_Node* pTemplateNode,
+                               CXFA_Node* pFormNode,
+                               CXFA_Node* pDataScope,
+                               bool bDataMerge,
+                               bool bUpLevel) {
+  CXFA_Node* pFieldNode = XFA_NodeMerge_CloneOrMergeContainer(
+      pDocument, pFormNode, pTemplateNode, false, nullptr);
+  ASSERT(pFieldNode);
+  for (CXFA_Node* pTemplateChildNode = pTemplateNode->GetFirstChild();
+       pTemplateChildNode;
+       pTemplateChildNode = pTemplateChildNode->GetNextSibling()) {
+    if (XFA_DataMerge_NeedGenerateForm(pTemplateChildNode, true)) {
+      XFA_NodeMerge_CloneOrMergeContainer(pDocument, pFieldNode,
+                                          pTemplateChildNode, true, nullptr);
+    } else if (pTemplateNode->GetElementType() == XFA_Element::ExclGroup &&
+               pTemplateChildNode->IsContainerNode()) {
+      if (pTemplateChildNode->GetElementType() == XFA_Element::Field) {
+        CopyContainer_Field(pDocument, pTemplateChildNode, pFieldNode, nullptr,
+                            false, true);
+      }
+    }
+  }
+  if (bDataMerge) {
+    bool bAccessedDataDOM = false;
+    bool bSelfMatch = false;
+    XFA_AttributeValue eBindMatch;
+    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
+        sNodeIter(pTemplateNode);
+    CXFA_Node* pDataNode = FindMatchingDataNode(
+        pDocument, pTemplateNode, pDataScope, bAccessedDataDOM, true,
+        &sNodeIter, bSelfMatch, eBindMatch, bUpLevel);
+    if (pDataNode)
+      CreateDataBinding(pFieldNode, pDataNode, true);
+  } else {
+    FormValueNode_MatchNoneCreateChild(pFieldNode);
+  }
+  return pFieldNode;
+}
+
+CXFA_Node* CopyContainer_SubformSet(CXFA_Document* pDocument,
+                                    CXFA_Node* pTemplateNode,
+                                    CXFA_Node* pFormParentNode,
+                                    CXFA_Node* pDataScope,
+                                    bool bOneInstance,
+                                    bool bDataMerge) {
+  XFA_Element eType = pTemplateNode->GetElementType();
+  CXFA_Node* pOccurNode = nullptr;
+  CXFA_Node* pFirstInstance = nullptr;
+  bool bUseInstanceManager =
+      pFormParentNode->GetElementType() != XFA_Element::Area;
+  CXFA_Node* pInstMgrNode = nullptr;
+  std::vector<CXFA_Node*> subformArray;
+  std::vector<CXFA_Node*>* pSearchArray = nullptr;
+  if (!bOneInstance &&
+      (eType == XFA_Element::SubformSet || eType == XFA_Element::Subform)) {
+    pInstMgrNode = bUseInstanceManager ? CloneOrMergeInstanceManager(
+                                             pDocument, pFormParentNode,
+                                             pTemplateNode, &subformArray)
+                                       : nullptr;
+    if (CXFA_Occur* pOccurTemplateNode =
+            pTemplateNode->GetFirstChildByClass<CXFA_Occur>(
+                XFA_Element::Occur)) {
+      pOccurNode = pInstMgrNode ? XFA_NodeMerge_CloneOrMergeContainer(
+                                      pDocument, pInstMgrNode,
+                                      pOccurTemplateNode, false, nullptr)
+                                : pOccurTemplateNode;
+    } else if (pInstMgrNode) {
+      pOccurNode =
+          pInstMgrNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
+      if (pOccurNode)
+        pOccurNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+    }
+    if (pInstMgrNode) {
+      pInstMgrNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pSearchArray = &subformArray;
+      if (pFormParentNode->GetElementType() == XFA_Element::PageArea) {
+        bOneInstance = true;
+        if (subformArray.empty())
+          pSearchArray = nullptr;
+      } else if (pTemplateNode->GetNameHash() == 0 && subformArray.empty()) {
+        pSearchArray = nullptr;
+      }
+    }
+  }
+
+  int32_t iMax = 1;
+  int32_t iInit = 1;
+  int32_t iMin = 1;
+  if (!bOneInstance && pOccurNode) {
+    std::tie(iMin, iMax, iInit) =
+        static_cast<CXFA_Occur*>(pOccurNode)->GetOccurInfo();
+  }
+
+  XFA_AttributeValue eRelation =
+      eType == XFA_Element::SubformSet
+          ? pTemplateNode->JSObject()->GetEnum(XFA_Attribute::Relation)
+          : XFA_AttributeValue::Ordered;
+  int32_t iCurRepeatIndex = 0;
+  XFA_AttributeValue eParentBindMatch = XFA_AttributeValue::None;
+  if (bDataMerge) {
+    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
+        sNodeIterator(pTemplateNode);
+    bool bAccessedDataDOM = false;
+    if (eType == XFA_Element::SubformSet || eType == XFA_Element::Area) {
+      sNodeIterator.MoveToNext();
+    } else {
+      std::map<CXFA_Node*, CXFA_Node*> subformMapArray;
+      std::vector<CXFA_Node*> nodeArray;
+      for (; iMax < 0 || iCurRepeatIndex < iMax; iCurRepeatIndex++) {
+        bool bSelfMatch = false;
+        XFA_AttributeValue eBindMatch = XFA_AttributeValue::None;
+        CXFA_Node* pDataNode = FindMatchingDataNode(
+            pDocument, pTemplateNode, pDataScope, bAccessedDataDOM, false,
+            &sNodeIterator, bSelfMatch, eBindMatch, true);
+        if (!pDataNode || sNodeIterator.GetCurrent() != pTemplateNode)
+          break;
+
+        eParentBindMatch = eBindMatch;
+        CXFA_Node* pSubformNode = XFA_NodeMerge_CloneOrMergeContainer(
+            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
+        if (!pFirstInstance)
+          pFirstInstance = pSubformNode;
+
+        CreateDataBinding(pSubformNode, pDataNode, true);
+        ASSERT(pSubformNode);
+        subformMapArray[pSubformNode] = pDataNode;
+        nodeArray.push_back(pSubformNode);
+      }
+
+      for (CXFA_Node* pSubform : nodeArray) {
+        CXFA_Node* pDataNode = nullptr;
+        auto it = subformMapArray.find(pSubform);
+        if (it != subformMapArray.end())
+          pDataNode = it->second;
+        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
+             pTemplateChild;
+             pTemplateChild = pTemplateChild->GetNextSibling()) {
+          if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
+                                             bUseInstanceManager)) {
+            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubform,
+                                                pTemplateChild, true, nullptr);
+          } else if (pTemplateChild->IsContainerNode()) {
+            pDocument->DataMerge_CopyContainer(pTemplateChild, pSubform,
+                                               pDataNode, false, true, false);
+          }
+        }
+      }
+      subformMapArray.clear();
+    }
+
+    for (; iMax < 0 || iCurRepeatIndex < iMax; iCurRepeatIndex++) {
+      bool bSelfMatch = false;
+      XFA_AttributeValue eBindMatch = XFA_AttributeValue::None;
+      if (!FindMatchingDataNode(pDocument, pTemplateNode, pDataScope,
+                                bAccessedDataDOM, false, &sNodeIterator,
+                                bSelfMatch, eBindMatch, true)) {
+        break;
+      }
+      if (eBindMatch == XFA_AttributeValue::DataRef &&
+          eParentBindMatch == XFA_AttributeValue::DataRef) {
+        break;
+      }
+
+      if (eRelation == XFA_AttributeValue::Choice ||
+          eRelation == XFA_AttributeValue::Unordered) {
+        CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
+            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
+        ASSERT(pSubformSetNode);
+        if (!pFirstInstance)
+          pFirstInstance = pSubformSetNode;
+
+        std::vector<RecurseRecord> rgItemMatchList;
+        std::vector<CXFA_Node*> rgItemUnmatchList;
+        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
+             pTemplateChild;
+             pTemplateChild = pTemplateChild->GetNextSibling()) {
+          if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
+                                             bUseInstanceManager)) {
+            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
+                                                pTemplateChild, true, nullptr);
+          } else if (pTemplateChild->IsContainerNode()) {
+            bSelfMatch = false;
+            eBindMatch = XFA_AttributeValue::None;
+            if (eRelation != XFA_AttributeValue::Ordered) {
+              CXFA_NodeIteratorTemplate<CXFA_Node,
+                                        CXFA_TraverseStrategy_XFAContainerNode>
+                  sChildIter(pTemplateChild);
+              CXFA_Node* pDataMatch = FindMatchingDataNode(
+                  pDocument, pTemplateChild, pDataScope, bAccessedDataDOM,
+                  false, &sChildIter, bSelfMatch, eBindMatch, true);
+              if (pDataMatch) {
+                RecurseRecord sNewRecord = {pTemplateChild, pDataMatch};
+                if (bSelfMatch)
+                  rgItemMatchList.insert(rgItemMatchList.begin(), sNewRecord);
+                else
+                  rgItemMatchList.push_back(sNewRecord);
+              } else {
+                rgItemUnmatchList.push_back(pTemplateChild);
+              }
+            } else {
+              rgItemUnmatchList.push_back(pTemplateChild);
+            }
+          }
+        }
+
+        switch (eRelation) {
+          case XFA_AttributeValue::Choice: {
+            ASSERT(!rgItemMatchList.empty());
+            SortRecurseRecord(&rgItemMatchList, pDataScope, true);
+            pDocument->DataMerge_CopyContainer(
+                rgItemMatchList.front().pTemplateChild, pSubformSetNode,
+                pDataScope, false, true, true);
+            break;
+          }
+          case XFA_AttributeValue::Unordered: {
+            if (!rgItemMatchList.empty()) {
+              SortRecurseRecord(&rgItemMatchList, pDataScope, false);
+              for (const auto& matched : rgItemMatchList) {
+                pDocument->DataMerge_CopyContainer(matched.pTemplateChild,
+                                                   pSubformSetNode, pDataScope,
+                                                   false, true, true);
+              }
+            }
+            for (auto* unmatched : rgItemUnmatchList) {
+              pDocument->DataMerge_CopyContainer(unmatched, pSubformSetNode,
+                                                 pDataScope, false, true, true);
+            }
+            break;
+          }
+          default:
+            break;
+        }
+      } else {
+        CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
+            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
+        ASSERT(pSubformSetNode);
+        if (!pFirstInstance)
+          pFirstInstance = pSubformSetNode;
+
+        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
+             pTemplateChild;
+             pTemplateChild = pTemplateChild->GetNextSibling()) {
+          if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
+                                             bUseInstanceManager)) {
+            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
+                                                pTemplateChild, true, nullptr);
+          } else if (pTemplateChild->IsContainerNode()) {
+            pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformSetNode,
+                                               pDataScope, false, true, true);
+          }
+        }
+      }
+    }
+
+    if (iCurRepeatIndex == 0 && !bAccessedDataDOM) {
+      int32_t iLimit = iMax;
+      if (pInstMgrNode && pTemplateNode->GetNameHash() == 0) {
+        iLimit = pdfium::CollectionSize<int32_t>(subformArray);
+        if (iLimit < iMin)
+          iLimit = iInit;
+      }
+
+      for (; (iLimit < 0 || iCurRepeatIndex < iLimit); iCurRepeatIndex++) {
+        if (pInstMgrNode) {
+          if (pSearchArray && pSearchArray->empty()) {
+            if (pTemplateNode->GetNameHash() != 0)
+              break;
+            pSearchArray = nullptr;
+          }
+        } else if (!XFA_DataMerge_FindFormDOMInstance(
+                       pDocument, pTemplateNode->GetElementType(),
+                       pTemplateNode->GetNameHash(), pFormParentNode)) {
+          break;
+        }
+        CXFA_Node* pSubformNode = XFA_NodeMerge_CloneOrMergeContainer(
+            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
+        ASSERT(pSubformNode);
+        if (!pFirstInstance)
+          pFirstInstance = pSubformNode;
+
+        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
+             pTemplateChild;
+             pTemplateChild = pTemplateChild->GetNextSibling()) {
+          if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
+                                             bUseInstanceManager)) {
+            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformNode,
+                                                pTemplateChild, true, nullptr);
+          } else if (pTemplateChild->IsContainerNode()) {
+            pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformNode,
+                                               pDataScope, false, true, true);
+          }
+        }
+      }
+    }
+  }
+
+  int32_t iMinimalLimit = iCurRepeatIndex == 0 ? iInit : iMin;
+  for (; iCurRepeatIndex < iMinimalLimit; iCurRepeatIndex++) {
+    CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
+        pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
+    ASSERT(pSubformSetNode);
+    if (!pFirstInstance)
+      pFirstInstance = pSubformSetNode;
+
+    bool bFound = false;
+    for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
+         pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
+      if (XFA_DataMerge_NeedGenerateForm(pTemplateChild, bUseInstanceManager)) {
+        XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
+                                            pTemplateChild, true, nullptr);
+      } else if (pTemplateChild->IsContainerNode()) {
+        if (bFound && eRelation == XFA_AttributeValue::Choice)
+          continue;
+
+        pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformSetNode,
+                                           pDataScope, false, bDataMerge, true);
+        bFound = true;
+      }
+    }
+  }
+  return pFirstInstance;
+}
+
+void UpdateBindingRelations(CXFA_Document* pDocument,
+                            CXFA_Node* pFormNode,
+                            CXFA_Node* pDataScope,
+                            bool bDataRef,
+                            bool bParentDataRef) {
+  bool bMatchRef = true;
+  XFA_Element eType = pFormNode->GetElementType();
+  CXFA_Node* pDataNode = pFormNode->GetBindData();
+  if (eType == XFA_Element::Subform || eType == XFA_Element::ExclGroup ||
+      eType == XFA_Element::Field) {
+    CXFA_Node* pTemplateNode = pFormNode->GetTemplateNodeIfExists();
+    CXFA_Bind* pTemplateNodeBind =
+        pTemplateNode
+            ? pTemplateNode->GetFirstChildByClass<CXFA_Bind>(XFA_Element::Bind)
+            : nullptr;
+    XFA_AttributeValue eMatch =
+        pTemplateNodeBind
+            ? pTemplateNodeBind->JSObject()->GetEnum(XFA_Attribute::Match)
+            : XFA_AttributeValue::Once;
+    switch (eMatch) {
+      case XFA_AttributeValue::None:
+        if (!bDataRef || bParentDataRef)
+          FormValueNode_MatchNoneCreateChild(pFormNode);
+        break;
+      case XFA_AttributeValue::Once:
+        if (!bDataRef || bParentDataRef) {
+          if (!pDataNode) {
+            if (pFormNode->GetNameHash() != 0 &&
+                pFormNode->JSObject()->GetEnum(XFA_Attribute::Scope) !=
+                    XFA_AttributeValue::None) {
+              XFA_Element eDataNodeType = (eType == XFA_Element::Subform ||
+                                           XFA_FieldIsMultiListBox(pFormNode))
+                                              ? XFA_Element::DataGroup
+                                              : XFA_Element::DataValue;
+              pDataNode = MaybeCreateDataNode(
+                  pDocument, pDataScope, eDataNodeType,
+                  WideString(
+                      pFormNode->JSObject()->GetCData(XFA_Attribute::Name)));
+              if (pDataNode)
+                CreateDataBinding(pFormNode, pDataNode, false);
+            }
+            if (!pDataNode)
+              FormValueNode_MatchNoneCreateChild(pFormNode);
+
+          } else {
+            CXFA_Node* pDataParent = pDataNode->GetParent();
+            if (pDataParent != pDataScope) {
+              ASSERT(pDataParent);
+              pDataParent->RemoveChildAndNotify(pDataNode, true);
+              pDataScope->InsertChildAndNotify(pDataNode, nullptr);
+            }
+          }
+        }
+        break;
+      case XFA_AttributeValue::Global:
+        if (!bDataRef || bParentDataRef) {
+          uint32_t dwNameHash = pFormNode->GetNameHash();
+          if (dwNameHash != 0 && !pDataNode) {
+            pDataNode = pDocument->GetGlobalBinding(dwNameHash);
+            if (!pDataNode) {
+              XFA_Element eDataNodeType = (eType == XFA_Element::Subform ||
+                                           XFA_FieldIsMultiListBox(pFormNode))
+                                              ? XFA_Element::DataGroup
+                                              : XFA_Element::DataValue;
+              CXFA_Node* pRecordNode =
+                  ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Record));
+              pDataNode = MaybeCreateDataNode(
+                  pDocument, pRecordNode, eDataNodeType,
+                  WideString(
+                      pFormNode->JSObject()->GetCData(XFA_Attribute::Name)));
+              if (pDataNode) {
+                CreateDataBinding(pFormNode, pDataNode, false);
+                pDocument->RegisterGlobalBinding(pFormNode->GetNameHash(),
+                                                 pDataNode);
+              }
+            } else {
+              CreateDataBinding(pFormNode, pDataNode, true);
+            }
+          }
+          if (!pDataNode)
+            FormValueNode_MatchNoneCreateChild(pFormNode);
+        }
+        break;
+      case XFA_AttributeValue::DataRef: {
+        bMatchRef = bDataRef;
+        bParentDataRef = true;
+        if (!pDataNode && bDataRef) {
+          WideString wsRef =
+              pTemplateNodeBind
+                  ? pTemplateNodeBind->JSObject()->GetCData(XFA_Attribute::Ref)
+                  : WideString();
+          uint32_t dFlags =
+              XFA_RESOLVENODE_Children | XFA_RESOLVENODE_CreateNode;
+          XFA_RESOLVENODE_RS rs;
+          pDocument->GetScriptContext()->ResolveObjects(
+              pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
+          CXFA_Object* pObject =
+              !rs.objects.empty() ? rs.objects.front().Get() : nullptr;
+          pDataNode = ToNode(pObject);
+          if (pDataNode) {
+            CreateDataBinding(pFormNode, pDataNode,
+                              rs.dwFlags == XFA_ResolveNode_RSType_ExistNodes);
+          } else {
+            FormValueNode_MatchNoneCreateChild(pFormNode);
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  if (bMatchRef &&
+      (eType == XFA_Element::Subform || eType == XFA_Element::SubformSet ||
+       eType == XFA_Element::Area || eType == XFA_Element::PageArea ||
+       eType == XFA_Element::PageSet)) {
+    for (CXFA_Node* pFormChild = pFormNode->GetFirstChild(); pFormChild;
+         pFormChild = pFormChild->GetNextSibling()) {
+      if (!pFormChild->IsContainerNode())
+        continue;
+      if (pFormChild->IsUnusedNode())
+        continue;
+
+      UpdateBindingRelations(pDocument, pFormChild,
+                             pDataNode ? pDataNode : pDataScope, bDataRef,
+                             bParentDataRef);
+    }
+  }
+}
+
+void UpdateDataRelation(CXFA_Node* pDataNode, CXFA_Node* pDataDescriptionNode) {
+  ASSERT(pDataDescriptionNode);
+  for (CXFA_Node* pDataChild = pDataNode->GetFirstChild(); pDataChild;
+       pDataChild = pDataChild->GetNextSibling()) {
+    uint32_t dwNameHash = pDataChild->GetNameHash();
+    if (!dwNameHash)
+      continue;
+
+    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_DDGroup>
+        sIterator(pDataDescriptionNode);
+    for (CXFA_Node* pDDGroupNode = sIterator.GetCurrent(); pDDGroupNode;
+         pDDGroupNode = sIterator.MoveToNext()) {
+      if (pDDGroupNode != pDataDescriptionNode) {
+        if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
+          continue;
+
+        Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
+        if (!ns.has_value() ||
+            !ns.value().EqualsASCII("http://ns.adobe.com/data-description/")) {
+          continue;
+        }
+      }
+
+      CXFA_Node* pDDNode = pDDGroupNode->GetFirstChildByName(dwNameHash);
+      if (!pDDNode)
+        continue;
+      if (pDDNode->GetElementType() != pDataChild->GetElementType())
+        break;
+
+      pDataChild->SetDataDescriptionNode(pDDNode);
+      UpdateDataRelation(pDataChild, pDDNode);
+      break;
+    }
+  }
+}
+
 }  // namespace
 
-CXFA_Document::CXFA_Document(CXFA_DocumentParser* pParser)
-    : m_pParser(pParser),
-      m_pRootNode(nullptr),
-      m_eCurVersionMode(XFA_VERSION_DEFAULT),
-      m_dwDocFlags(0) {
-  ASSERT(m_pParser);
+CXFA_Document::CXFA_Document(CXFA_FFNotify* notify,
+                             std::unique_ptr<LayoutProcessorIface> pLayout)
+    : CXFA_NodeOwner(),
+      notify_(notify),
+      m_pLayoutProcessor(std::move(pLayout)) {
+  if (m_pLayoutProcessor)
+    m_pLayoutProcessor->SetDocument(this);
 }
 
-CXFA_Document::~CXFA_Document() {
-  // Remove all the bindings before freeing the node as the ownership is wonky.
-  if (m_pRootNode)
-    m_pRootNode->ReleaseBindingNodes();
-
-  delete m_pRootNode;
-
-  for (CXFA_Node* pNode : m_PurgeNodes)
-    delete pNode;
-  m_PurgeNodes.clear();
-}
-
-CXFA_LayoutProcessor* CXFA_Document::GetLayoutProcessor() {
-  if (!m_pLayoutProcessor)
-    m_pLayoutProcessor = pdfium::MakeUnique<CXFA_LayoutProcessor>(this);
-  return m_pLayoutProcessor.get();
-}
-
-CXFA_LayoutProcessor* CXFA_Document::GetDocLayout() {
-  return GetLayoutProcessor();
-}
+CXFA_Document::~CXFA_Document() = default;
 
 void CXFA_Document::ClearLayoutData() {
   m_pLayoutProcessor.reset();
   m_pScriptContext.reset();
-  m_pLocalMgr.reset();
+  m_pLocaleMgr.reset();
   m_pScriptDataWindow.reset();
   m_pScriptEvent.reset();
   m_pScriptHost.reset();
@@ -128,22 +1296,6 @@
   m_pScriptSignature.reset();
 }
 
-void CXFA_Document::SetRoot(CXFA_Node* pNewRoot) {
-  if (m_pRootNode)
-    AddPurgeNode(m_pRootNode);
-
-  m_pRootNode = pNewRoot;
-  RemovePurgeNode(pNewRoot);
-}
-
-CFX_XMLDoc* CXFA_Document::GetXMLDoc() const {
-  return m_pParser->GetXMLDoc();
-}
-
-CXFA_FFNotify* CXFA_Document::GetNotify() const {
-  return m_pParser->GetNotify();
-}
-
 CXFA_Object* CXFA_Document::GetXFAObject(XFA_HashCode dwNodeNameHash) {
   switch (dwNodeNameHash) {
     case XFA_HASHCODE_Data: {
@@ -221,34 +1373,12 @@
                                      XFA_Element eElement) {
   if (eElement == XFA_Element::Unknown)
     return nullptr;
-
-  std::unique_ptr<CXFA_Node> pNode = CXFA_Node::Create(this, eElement, packet);
-  if (!pNode)
-    return nullptr;
-
-  // TODO(dsinclair): AddPrugeNode should take ownership of the pointer.
-  AddPurgeNode(pNode.get());
-  return pNode.release();
-}
-
-void CXFA_Document::AddPurgeNode(CXFA_Node* pNode) {
-  m_PurgeNodes.insert(pNode);
-}
-
-bool CXFA_Document::RemovePurgeNode(CXFA_Node* pNode) {
-  return !!m_PurgeNodes.erase(pNode);
-}
-
-void CXFA_Document::SetFlag(uint32_t dwFlag, bool bOn) {
-  if (bOn)
-    m_dwDocFlags |= dwFlag;
-  else
-    m_dwDocFlags &= ~dwFlag;
+  return AddOwnedNode(CXFA_Node::Create(this, eElement, packet));
 }
 
 bool CXFA_Document::IsInteractive() {
-  if (m_dwDocFlags & XFA_DOCFLAG_HasInteractive)
-    return !!(m_dwDocFlags & XFA_DOCFLAG_Interactive);
+  if (m_Interactive.has_value())
+    return m_Interactive.value();
 
   CXFA_Node* pConfig = ToNode(GetXFAObject(XFA_HASHCODE_Config));
   if (!pConfig)
@@ -265,36 +1395,31 @@
 
   CXFA_Interactive* pFormFiller =
       pPDF->GetChild<CXFA_Interactive>(0, XFA_Element::Interactive, false);
-  if (pFormFiller) {
-    m_dwDocFlags |= XFA_DOCFLAG_HasInteractive;
+  if (!pFormFiller)
+    return false;
 
-    WideString wsInteractive = pFormFiller->JSObject()->GetContent(false);
-    if (wsInteractive == L"1") {
-      m_dwDocFlags |= XFA_DOCFLAG_Interactive;
-      return true;
-    }
-  }
-  return false;
+  WideString wsInteractive = pFormFiller->JSObject()->GetContent(false);
+  bool bInteractive = wsInteractive.EqualsASCII("1");
+  m_Interactive = bInteractive;
+  return bInteractive;
 }
 
-CXFA_LocaleMgr* CXFA_Document::GetLocalMgr() {
-  if (!m_pLocalMgr) {
-    m_pLocalMgr = pdfium::MakeUnique<CXFA_LocaleMgr>(
+CXFA_LocaleMgr* CXFA_Document::GetLocaleMgr() {
+  if (!m_pLocaleMgr) {
+    m_pLocaleMgr = pdfium::MakeUnique<CXFA_LocaleMgr>(
         ToNode(GetXFAObject(XFA_HASHCODE_LocaleSet)),
         GetNotify()->GetAppProvider()->GetLanguage());
   }
-  return m_pLocalMgr.get();
+  return m_pLocaleMgr.get();
 }
 
-CFXJSE_Engine* CXFA_Document::InitScriptContext(v8::Isolate* pIsolate) {
+CFXJSE_Engine* CXFA_Document::InitScriptContext(CJS_Runtime* fxjs_runtime) {
   ASSERT(!m_pScriptContext);
-  m_pScriptContext = pdfium::MakeUnique<CFXJSE_Engine>(this, pIsolate);
+  m_pScriptContext = pdfium::MakeUnique<CFXJSE_Engine>(this, fxjs_runtime);
   return m_pScriptContext.get();
 }
 
-// We have to call |InitScriptContext| before any calls to |GetScriptContext|
-// or the context won't have an isolate set into it.
-CFXJSE_Engine* CXFA_Document::GetScriptContext() {
+CFXJSE_Engine* CXFA_Document::GetScriptContext() const {
   ASSERT(m_pScriptContext);
   return m_pScriptContext.get();
 }
@@ -302,21 +1427,24 @@
 XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber(
     const WideString& wsTemplateNS) {
   WideStringView wsTemplateURIPrefix(kTemplateNS);
-  size_t nPrefixLength = wsTemplateURIPrefix.GetLength();
-  if (WideStringView(wsTemplateNS.c_str(), wsTemplateNS.GetLength()) !=
-      wsTemplateURIPrefix) {
+  if (wsTemplateNS.GetLength() <= wsTemplateURIPrefix.GetLength())
     return XFA_VERSION_UNKNOWN;
-  }
-  auto nDotPos = wsTemplateNS.Find('.', nPrefixLength);
+
+  size_t prefixLength = wsTemplateURIPrefix.GetLength();
+  if (WideStringView(wsTemplateNS.c_str(), prefixLength) != wsTemplateURIPrefix)
+    return XFA_VERSION_UNKNOWN;
+
+  auto nDotPos = wsTemplateNS.Find('.', prefixLength);
   if (!nDotPos.has_value())
     return XFA_VERSION_UNKNOWN;
 
   int8_t iMajor = FXSYS_wtoi(
-      wsTemplateNS.Mid(nPrefixLength, nDotPos.value() - nPrefixLength).c_str());
+      wsTemplateNS.Substr(prefixLength, nDotPos.value() - prefixLength)
+          .c_str());
   int8_t iMinor =
       FXSYS_wtoi(wsTemplateNS
-                     .Mid(nDotPos.value() + 1,
-                          wsTemplateNS.GetLength() - nDotPos.value() - 2)
+                     .Substr(nDotPos.value() + 1,
+                             wsTemplateNS.GetLength() - nDotPos.value() - 2)
                      .c_str());
   XFA_VERSION eVersion = (XFA_VERSION)((int32_t)iMajor * 100 + iMinor);
   if (eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX)
@@ -326,8 +1454,12 @@
   return eVersion;
 }
 
+FormType CXFA_Document::GetFormType() const {
+  return GetNotify()->GetHDOC()->GetFormType();
+}
+
 CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot,
-                                      const WideStringView& wsID) {
+                                      WideStringView wsID) const {
   if (!pRoot || wsID.IsEmpty())
     return nullptr;
 
@@ -366,12 +1498,13 @@
   }
 
   for (CXFA_Node* pUseHrefNode : sUseNodes) {
+    // Must outlive the WideStringViews below.
+    WideString wsUseVal =
+        pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Usehref);
     WideStringView wsURI;
     WideStringView wsID;
     WideStringView wsSOM;
 
-    WideString wsUseVal =
-        pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Usehref);
     if (!wsUseVal.IsEmpty()) {
       auto uSharpPos = wsUseVal.Find('#');
       if (!uSharpPos.has_value()) {
@@ -399,8 +1532,7 @@
           wsSOM = WideStringView(wsUseVal.c_str(), wsUseVal.GetLength());
       }
     }
-
-    if (!wsURI.IsEmpty() && wsURI != L".")
+    if (!wsURI.IsEmpty() && !wsURI.EqualsASCII("."))
       continue;
 
     CXFA_Node* pProtoNode = nullptr;
@@ -409,10 +1541,12 @@
                         XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
                         XFA_RESOLVENODE_Siblings;
       XFA_RESOLVENODE_RS resolveNodeRS;
-      int32_t iRet = m_pScriptContext->ResolveObjects(
-          pUseHrefNode, wsSOM, &resolveNodeRS, dwFlag, nullptr);
-      if (iRet > 0 && resolveNodeRS.objects.front()->IsNode())
-        pProtoNode = resolveNodeRS.objects.front()->AsNode();
+      if (m_pScriptContext->ResolveObjects(pUseHrefNode, wsSOM, &resolveNodeRS,
+                                           dwFlag, nullptr)) {
+        auto* pFirstObject = resolveNodeRS.objects.front().Get();
+        if (pFirstObject && pFirstObject->IsNode())
+          pProtoNode = pFirstObject->AsNode();
+      }
     } else if (!wsID.IsEmpty()) {
       auto it = mIDMap.find(FX_HashCode_GetW(wsID, false));
       if (it == mIDMap.end())
@@ -422,6 +1556,275 @@
     if (!pProtoNode)
       continue;
 
-    MergeNode(this, pUseHrefNode, pProtoNode);
+    MergeNode(pUseHrefNode, pProtoNode);
   }
 }
+
+CXFA_Node* CXFA_Document::DataMerge_CopyContainer(CXFA_Node* pTemplateNode,
+                                                  CXFA_Node* pFormNode,
+                                                  CXFA_Node* pDataScope,
+                                                  bool bOneInstance,
+                                                  bool bDataMerge,
+                                                  bool bUpLevel) {
+  ASSERT(pTemplateNode->IsContainerNode());
+  switch (pTemplateNode->GetElementType()) {
+    case XFA_Element::Area:
+    case XFA_Element::PageArea:
+    case XFA_Element::Subform:
+    case XFA_Element::SubformSet:
+      return CopyContainer_SubformSet(this, pTemplateNode, pFormNode,
+                                      pDataScope, bOneInstance, bDataMerge);
+    case XFA_Element::ContentArea:
+    case XFA_Element::Draw:
+    case XFA_Element::ExclGroup:
+    case XFA_Element::Field:
+      return CopyContainer_Field(this, pTemplateNode, pFormNode, pDataScope,
+                                 bDataMerge, bUpLevel);
+    case XFA_Element::PageSet:
+    case XFA_Element::Variables:
+      return nullptr;
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
+}
+
+void CXFA_Document::DataMerge_UpdateBindingRelations(
+    CXFA_Node* pFormUpdateRoot) {
+  CXFA_Node* pDataScope =
+      XFA_DataMerge_FindDataScope(pFormUpdateRoot->GetParent());
+  if (!pDataScope)
+    return;
+
+  UpdateBindingRelations(this, pFormUpdateRoot, pDataScope, false, false);
+  UpdateBindingRelations(this, pFormUpdateRoot, pDataScope, true, false);
+}
+
+CXFA_Node* CXFA_Document::GetNotBindNode(
+    const std::vector<UnownedPtr<CXFA_Object>>& arrayObjects) const {
+  for (auto& pObject : arrayObjects) {
+    CXFA_Node* pNode = pObject->AsNode();
+    if (pNode && !pNode->HasBindItem())
+      return pNode;
+  }
+  return nullptr;
+}
+
+void CXFA_Document::DoDataMerge() {
+  CXFA_Node* pDatasetsRoot = ToNode(GetXFAObject(XFA_HASHCODE_Datasets));
+  if (!pDatasetsRoot) {
+    // Ownership will be passed in the AppendChild below to the XML tree.
+    auto* pDatasetsXMLNode =
+        notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
+            L"xfa:datasets");
+    pDatasetsXMLNode->SetAttribute(L"xmlns:xfa",
+                                   L"http://www.xfa.org/schema/xfa-data/1.0/");
+    pDatasetsRoot =
+        CreateNode(XFA_PacketType::Datasets, XFA_Element::DataModel);
+    pDatasetsRoot->JSObject()->SetCData(XFA_Attribute::Name, L"datasets", false,
+                                        false);
+
+    m_pRootNode->GetXMLMappingNode()->AppendLastChild(pDatasetsXMLNode);
+    m_pRootNode->InsertChildAndNotify(pDatasetsRoot, nullptr);
+    pDatasetsRoot->SetXMLMappingNode(pDatasetsXMLNode);
+  }
+
+  CXFA_Node* pDataRoot = nullptr;
+  CXFA_Node* pDDRoot = nullptr;
+  WideString wsDatasetsURI =
+      pDatasetsRoot->JSObject()->TryNamespace().value_or(WideString());
+  for (CXFA_Node* pChildNode = pDatasetsRoot->GetFirstChild(); pChildNode;
+       pChildNode = pChildNode->GetNextSibling()) {
+    if (pChildNode->GetElementType() != XFA_Element::DataGroup)
+      continue;
+
+    if (!pDDRoot && pChildNode->GetNameHash() == XFA_HASHCODE_DataDescription) {
+      Optional<WideString> namespaceURI =
+          pChildNode->JSObject()->TryNamespace();
+      if (!namespaceURI.has_value())
+        continue;
+      if (namespaceURI.value().EqualsASCII(
+              "http://ns.adobe.com/data-description/")) {
+        pDDRoot = pChildNode;
+      }
+    } else if (!pDataRoot && pChildNode->GetNameHash() == XFA_HASHCODE_Data) {
+      Optional<WideString> namespaceURI =
+          pChildNode->JSObject()->TryNamespace();
+      if (!namespaceURI)
+        continue;
+      if (*namespaceURI == wsDatasetsURI)
+        pDataRoot = pChildNode;
+    }
+    if (pDataRoot && pDDRoot)
+      break;
+  }
+
+  if (!pDataRoot) {
+    pDataRoot = CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup);
+    pDataRoot->JSObject()->SetCData(XFA_Attribute::Name, L"data", false, false);
+
+    auto* elem =
+        notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
+            L"xfa:data");
+    pDataRoot->SetXMLMappingNode(elem);
+    pDatasetsRoot->InsertChildAndNotify(pDataRoot, nullptr);
+  }
+
+  CXFA_DataGroup* pDataTopLevel =
+      pDataRoot->GetFirstChildByClass<CXFA_DataGroup>(XFA_Element::DataGroup);
+  uint32_t dwNameHash = pDataTopLevel ? pDataTopLevel->GetNameHash() : 0;
+  CXFA_Template* pTemplateRoot =
+      m_pRootNode->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
+  if (!pTemplateRoot)
+    return;
+
+  CXFA_Node* pTemplateChosen =
+      dwNameHash != 0 ? pTemplateRoot->GetFirstChildByName(dwNameHash)
+                      : nullptr;
+  if (!pTemplateChosen ||
+      pTemplateChosen->GetElementType() != XFA_Element::Subform) {
+    pTemplateChosen =
+        pTemplateRoot->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
+  }
+  if (!pTemplateChosen)
+    return;
+
+  CXFA_Form* pFormRoot =
+      m_pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
+  bool bEmptyForm = false;
+  if (!pFormRoot) {
+    bEmptyForm = true;
+    pFormRoot = static_cast<CXFA_Form*>(
+        CreateNode(XFA_PacketType::Form, XFA_Element::Form));
+    ASSERT(pFormRoot);
+    pFormRoot->JSObject()->SetCData(XFA_Attribute::Name, L"form", false, false);
+    m_pRootNode->InsertChildAndNotify(pFormRoot, nullptr);
+  } else {
+    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
+        sIterator(pFormRoot);
+    for (CXFA_Node* pNode = sIterator.MoveToNext(); pNode;
+         pNode = sIterator.MoveToNext()) {
+      pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+    }
+  }
+
+  CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
+      this, pFormRoot, pTemplateChosen, false, nullptr);
+  ASSERT(pSubformSetNode);
+  if (!pDataTopLevel) {
+    WideString wsFormName =
+        pSubformSetNode->JSObject()->GetCData(XFA_Attribute::Name);
+    WideString wsDataTopLevelName(wsFormName.IsEmpty() ? L"form" : wsFormName);
+
+    pDataTopLevel = static_cast<CXFA_DataGroup*>(
+        CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup));
+    pDataTopLevel->JSObject()->SetCData(XFA_Attribute::Name, wsDataTopLevelName,
+                                        false, false);
+
+    auto* elem =
+        notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
+            wsDataTopLevelName);
+    pDataTopLevel->SetXMLMappingNode(elem);
+
+    CXFA_Node* pBeforeNode = pDataRoot->GetFirstChild();
+    pDataRoot->InsertChildAndNotify(pDataTopLevel, pBeforeNode);
+  }
+
+  ASSERT(pDataTopLevel);
+  CreateDataBinding(pSubformSetNode, pDataTopLevel, true);
+  for (CXFA_Node* pTemplateChild = pTemplateChosen->GetFirstChild();
+       pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
+    if (XFA_DataMerge_NeedGenerateForm(pTemplateChild, true)) {
+      XFA_NodeMerge_CloneOrMergeContainer(this, pSubformSetNode, pTemplateChild,
+                                          true, nullptr);
+    } else if (pTemplateChild->IsContainerNode()) {
+      DataMerge_CopyContainer(pTemplateChild, pSubformSetNode, pDataTopLevel,
+                              false, true, true);
+    }
+  }
+  if (pDDRoot)
+    UpdateDataRelation(pDataRoot, pDDRoot);
+
+  DataMerge_UpdateBindingRelations(pSubformSetNode);
+  CXFA_PageSet* pPageSetNode =
+      pSubformSetNode->GetFirstChildByClass<CXFA_PageSet>(XFA_Element::PageSet);
+  while (pPageSetNode) {
+    m_pPendingPageSet.push_back(pPageSetNode);
+    CXFA_PageSet* pNextPageSetNode =
+        pPageSetNode->GetNextSameClassSibling<CXFA_PageSet>(
+            XFA_Element::PageSet);
+    pSubformSetNode->RemoveChildAndNotify(pPageSetNode, true);
+    pPageSetNode = pNextPageSetNode;
+  }
+
+  if (bEmptyForm)
+    return;
+
+  CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode> sIterator(
+      pFormRoot);
+  CXFA_Node* pNode = sIterator.MoveToNext();
+  while (pNode) {
+    if (pNode->IsUnusedNode()) {
+      if (pNode->IsContainerNode() ||
+          pNode->GetElementType() == XFA_Element::InstanceManager) {
+        CXFA_Node* pNext = sIterator.SkipChildrenAndMoveToNext();
+        pNode->GetParent()->RemoveChildAndNotify(pNode, true);
+        pNode = pNext;
+      } else {
+        pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
+        pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+        pNode = sIterator.MoveToNext();
+      }
+    } else {
+      pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pNode = sIterator.MoveToNext();
+    }
+  }
+}
+
+void CXFA_Document::DoDataRemerge(bool bDoDataMerge) {
+  CXFA_Node* pFormRoot = ToNode(GetXFAObject(XFA_HASHCODE_Form));
+  if (pFormRoot) {
+    while (CXFA_Node* pNode = pFormRoot->GetFirstChild())
+      pFormRoot->RemoveChildAndNotify(pNode, true);
+
+    pFormRoot->SetBindingNode(nullptr);
+  }
+  m_rgGlobalBinding.clear();
+
+  if (bDoDataMerge)
+    DoDataMerge();
+
+  GetLayoutProcessor()->SetForceRelayout(true);
+}
+
+CXFA_Node* CXFA_Document::GetGlobalBinding(uint32_t dwNameHash) {
+  auto it = m_rgGlobalBinding.find(dwNameHash);
+  return it != m_rgGlobalBinding.end() ? it->second : nullptr;
+}
+
+void CXFA_Document::RegisterGlobalBinding(uint32_t dwNameHash,
+                                          CXFA_Node* pDataNode) {
+  m_rgGlobalBinding[dwNameHash] = pDataNode;
+}
+
+void CXFA_Document::SetPendingNodesUnusedAndUnbound() {
+  for (CXFA_Node* pPageNode : m_pPendingPageSet) {
+    CXFA_NodeIterator sIterator(pPageNode);
+    for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
+         pNode = sIterator.MoveToNext()) {
+      if (pNode->IsContainerNode()) {
+        CXFA_Node* pBindNode = pNode->GetBindData();
+        if (pBindNode) {
+          pBindNode->RemoveBindItem(pNode);
+          pNode->SetBindingNode(nullptr);
+        }
+      }
+      pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+    }
+  }
+}
+
+CXFA_Document::LayoutProcessorIface::LayoutProcessorIface() = default;
+
+CXFA_Document::LayoutProcessorIface::~LayoutProcessorIface() = default;
diff --git a/xfa/fxfa/parser/cxfa_document.h b/xfa/fxfa/parser/cxfa_document.h
index bd576e6..ea80475 100644
--- a/xfa/fxfa/parser/cxfa_document.h
+++ b/xfa/fxfa/parser/cxfa_document.h
@@ -9,11 +9,13 @@
 
 #include <map>
 #include <memory>
-#include <set>
 #include <vector>
 
+#include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/optional.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_nodeowner.h"
 
 enum XFA_VERSION {
   XFA_VERSION_UNKNOWN = 0,
@@ -33,59 +35,71 @@
   XFA_VERSION_MAX = 400,
 };
 
-enum XFA_DocFlag {
-  XFA_DOCFLAG_StrictScoping = 0x0001,
-  XFA_DOCFLAG_HasInteractive = 0x0002,
-  XFA_DOCFLAG_Interactive = 0x0004,
-  XFA_DOCFLAG_Scripting = 0x0008
-};
-
-class CFX_XMLDoc;
 class CFXJSE_Engine;
+class CJS_Runtime;
 class CScript_DataWindow;
 class CScript_EventPseudoModel;
 class CScript_HostPseudoModel;
-class CScript_LogPseudoModel;
 class CScript_LayoutPseudoModel;
+class CScript_LogPseudoModel;
 class CScript_SignaturePseudoModel;
-class CXFA_ContainerLayoutItem;
-class CXFA_DocumentParser;
 class CXFA_FFNotify;
-class CXFA_LayoutItem;
-class CXFA_LayoutProcessor;
 class CXFA_Node;
 class CXFA_Object;
 
-class CXFA_Document {
+class CXFA_Document final : public CXFA_NodeOwner {
  public:
-  explicit CXFA_Document(CXFA_DocumentParser* pParser);
-  ~CXFA_Document();
+  class LayoutProcessorIface {
+   public:
+    LayoutProcessorIface();
+    virtual ~LayoutProcessorIface();
+    virtual void SetForceRelayout(bool enable) = 0;
+    virtual void AddChangedContainer(CXFA_Node* pContainer) = 0;
 
-  CFXJSE_Engine* InitScriptContext(v8::Isolate* pIsolate);
+    void SetDocument(CXFA_Document* pDocument) { m_pDocument = pDocument; }
+    CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+
+   private:
+    UnownedPtr<CXFA_Document> m_pDocument;
+  };
+
+  CXFA_Document(CXFA_FFNotify* notify,
+                std::unique_ptr<LayoutProcessorIface> pLayout);
+  ~CXFA_Document() override;
+
+  bool HasScriptContext() const { return !!m_pScriptContext; }
+  CFXJSE_Engine* InitScriptContext(CJS_Runtime* fxjs_runtime);
+
+  // Only safe to call in situations where the context is known to exist,
+  // and always returns non-NULL in those situations. In other words, we have
+  // to call InitScriptContext() first to avoid a situation where the context
+  // won't have an isolate set into it.
+  CFXJSE_Engine* GetScriptContext() const;
+
+  CXFA_FFNotify* GetNotify() const { return notify_.Get(); }
+  CXFA_LocaleMgr* GetLocaleMgr();
+  CXFA_Object* GetXFAObject(XFA_HashCode wsNodeNameHash);
+  CXFA_Node* GetNodeByID(CXFA_Node* pRoot, WideStringView wsID) const;
+  CXFA_Node* GetNotBindNode(
+      const std::vector<UnownedPtr<CXFA_Object>>& arrayNodes) const;
+
+  LayoutProcessorIface* GetLayoutProcessor() const {
+    return m_pLayoutProcessor.get();
+  }
 
   CXFA_Node* GetRoot() const { return m_pRootNode; }
+  void SetRoot(CXFA_Node* pNewRoot) { m_pRootNode = pNewRoot; }
 
-  CFX_XMLDoc* GetXMLDoc() const;
-  CXFA_FFNotify* GetNotify() const;
-  CXFA_LocaleMgr* GetLocalMgr();
-  CXFA_Object* GetXFAObject(XFA_HashCode wsNodeNameHash);
-  CXFA_Node* GetNodeByID(CXFA_Node* pRoot, const WideStringView& wsID);
-  CXFA_Node* GetNotBindNode(const std::vector<CXFA_Object*>& arrayNodes);
-  CXFA_LayoutProcessor* GetLayoutProcessor();
-  CXFA_LayoutProcessor* GetDocLayout();
-  CFXJSE_Engine* GetScriptContext();
+  bool is_strict_scoping() const { return m_bStrictScoping; }
+  void set_is_strict_scoping() { m_bStrictScoping = true; }
 
-  void SetRoot(CXFA_Node* pNewRoot);
-
-  void AddPurgeNode(CXFA_Node* pNode);
-  bool RemovePurgeNode(CXFA_Node* pNode);
-
-  bool HasFlag(uint32_t dwFlag) { return (m_dwDocFlags & dwFlag) == dwFlag; }
-  void SetFlag(uint32_t dwFlag, bool bOn);
+  bool is_scripting() const { return m_bScripting; }
+  void set_is_scripting() { m_bScripting = true; }
 
   bool IsInteractive();
   XFA_VERSION GetCurVersionMode() { return m_eCurVersionMode; }
   XFA_VERSION RecognizeXFAVersionNumber(const WideString& wsTemplateNS);
+  FormType GetFormType() const;
 
   CXFA_Node* CreateNode(XFA_PacketType packet, XFA_Element eElement);
 
@@ -102,24 +116,29 @@
 
   void ClearLayoutData();
 
-  std::map<uint32_t, CXFA_Node*> m_rgGlobalBinding;
+  CXFA_Node* GetGlobalBinding(uint32_t dwNameHash);
+  void RegisterGlobalBinding(uint32_t dwNameHash, CXFA_Node* pDataNode);
+  void SetPendingNodesUnusedAndUnbound();
+
   std::vector<CXFA_Node*> m_pPendingPageSet;
 
  private:
-  CXFA_DocumentParser* m_pParser;
-  CXFA_Node* m_pRootNode;
+  UnownedPtr<CXFA_FFNotify> const notify_;
+  CXFA_Node* m_pRootNode = nullptr;
+  std::map<uint32_t, CXFA_Node*> m_rgGlobalBinding;
   std::unique_ptr<CFXJSE_Engine> m_pScriptContext;
-  std::unique_ptr<CXFA_LayoutProcessor> m_pLayoutProcessor;
-  std::unique_ptr<CXFA_LocaleMgr> m_pLocalMgr;
+  std::unique_ptr<LayoutProcessorIface> m_pLayoutProcessor;
+  std::unique_ptr<CXFA_LocaleMgr> m_pLocaleMgr;
   std::unique_ptr<CScript_DataWindow> m_pScriptDataWindow;
   std::unique_ptr<CScript_EventPseudoModel> m_pScriptEvent;
   std::unique_ptr<CScript_HostPseudoModel> m_pScriptHost;
   std::unique_ptr<CScript_LogPseudoModel> m_pScriptLog;
   std::unique_ptr<CScript_LayoutPseudoModel> m_pScriptLayout;
   std::unique_ptr<CScript_SignaturePseudoModel> m_pScriptSignature;
-  std::set<CXFA_Node*> m_PurgeNodes;
-  XFA_VERSION m_eCurVersionMode;
-  uint32_t m_dwDocFlags;
+  XFA_VERSION m_eCurVersionMode = XFA_VERSION_DEFAULT;
+  Optional<bool> m_Interactive;
+  bool m_bStrictScoping = false;
+  bool m_bScripting = false;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DOCUMENT_H_
diff --git a/xfa/fxfa/parser/cxfa_document_parser.cpp b/xfa/fxfa/parser/cxfa_document_parser.cpp
index 6446ea3..e210b94 100644
--- a/xfa/fxfa/parser/cxfa_document_parser.cpp
+++ b/xfa/fxfa/parser/cxfa_document_parser.cpp
@@ -6,48 +6,1035 @@
 
 #include "xfa/fxfa/parser/cxfa_document_parser.h"
 
-#include "core/fxcrt/xml/cfx_xmldoc.h"
-#include "third_party/base/ptr_util.h"
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_widetextbuf.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/xml/cfx_xmlchardata.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlinstruction.h"
+#include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "core/fxcrt/xml/cfx_xmltext.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/logging.h"
+#include "third_party/base/optional.h"
 #include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_subform.h"
+#include "xfa/fxfa/parser/cxfa_template.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
 
-CXFA_DocumentParser::CXFA_DocumentParser(CXFA_FFNotify* pNotify)
-    : m_pNotify(pNotify) {}
+namespace {
 
-CXFA_DocumentParser::~CXFA_DocumentParser() {
+CFX_XMLNode* GetDocumentNode(CFX_XMLNode* pRootNode) {
+  for (CFX_XMLNode* pXMLNode = pRootNode->GetFirstChild(); pXMLNode;
+       pXMLNode = pXMLNode->GetNextSibling()) {
+    if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement)
+      return pXMLNode;
+  }
+  return nullptr;
 }
 
-int32_t CXFA_DocumentParser::StartParse(
-    const RetainPtr<IFX_SeekableStream>& pStream,
+bool MatchNodeName(CFX_XMLNode* pNode,
+                   WideStringView wsLocalTagName,
+                   WideStringView wsNamespaceURIPrefix,
+                   uint32_t eMatchFlags = XFA_XDPPACKET_FLAGS_NOMATCH) {
+  CFX_XMLElement* pElement = ToXMLElement(pNode);
+  if (!pElement)
+    return false;
+
+  WideString wsNodeStr = pElement->GetLocalTagName();
+  if (wsNodeStr != wsLocalTagName)
+    return false;
+
+  wsNodeStr = pElement->GetNamespaceURI();
+  if (eMatchFlags & XFA_XDPPACKET_FLAGS_NOMATCH)
+    return true;
+  if (eMatchFlags & XFA_XDPPACKET_FLAGS_PREFIXMATCH) {
+    return wsNodeStr.First(wsNamespaceURIPrefix.GetLength()) ==
+           wsNamespaceURIPrefix;
+  }
+
+  return wsNodeStr == wsNamespaceURIPrefix;
+}
+
+bool GetAttributeLocalName(WideStringView wsAttributeName,
+                           WideString& wsLocalAttrName) {
+  WideString wsAttrName(wsAttributeName);
+  auto pos = wsAttrName.Find(L':', 0);
+  if (!pos.has_value()) {
+    wsLocalAttrName = std::move(wsAttrName);
+    return false;
+  }
+  wsLocalAttrName = wsAttrName.Last(wsAttrName.GetLength() - pos.value() - 1);
+  return true;
+}
+
+bool ResolveAttribute(CFX_XMLElement* pElement,
+                      const WideString& wsAttrName,
+                      WideString& wsLocalAttrName,
+                      WideString& wsNamespaceURI) {
+  WideString wsNSPrefix;
+  if (GetAttributeLocalName(wsAttrName.AsStringView(), wsLocalAttrName)) {
+    wsNSPrefix = wsAttrName.First(wsAttrName.GetLength() -
+                                  wsLocalAttrName.GetLength() - 1);
+  }
+  if (wsLocalAttrName.EqualsASCII("xmlns") || wsNSPrefix.EqualsASCII("xmlns") ||
+      wsNSPrefix.EqualsASCII("xml")) {
+    return false;
+  }
+  if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
+                                                  &wsNamespaceURI)) {
+    wsNamespaceURI.clear();
+    return false;
+  }
+  return true;
+}
+
+Optional<WideString> FindAttributeWithNS(CFX_XMLElement* pElement,
+                                         WideStringView wsLocalAttributeName,
+                                         WideStringView wsNamespaceURIPrefix) {
+  WideString wsAttrNS;
+  for (auto it : pElement->GetAttributes()) {
+    auto pos = it.first.Find(L':', 0);
+    WideString wsNSPrefix;
+    if (!pos.has_value()) {
+      if (wsLocalAttributeName != it.first)
+        continue;
+    } else {
+      if (wsLocalAttributeName !=
+          it.first.Last(it.first.GetLength() - pos.value() - 1)) {
+        continue;
+      }
+      wsNSPrefix = it.first.First(pos.value());
+    }
+    if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
+                                                    &wsAttrNS) ||
+        wsAttrNS != wsNamespaceURIPrefix) {
+      continue;
+    }
+    return it.second;
+  }
+  return {};
+}
+
+CFX_XMLNode* GetDataSetsFromXDP(CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO datasets_packet =
+      XFA_GetPacketByIndex(XFA_PacketType::Datasets);
+  if (MatchNodeName(pXMLDocumentNode, datasets_packet.name, datasets_packet.uri,
+                    datasets_packet.flags)) {
+    return pXMLDocumentNode;
+  }
+  XFA_PACKETINFO xdp_packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
+  if (!MatchNodeName(pXMLDocumentNode, xdp_packet.name, xdp_packet.uri,
+                     xdp_packet.flags)) {
+    return nullptr;
+  }
+  for (CFX_XMLNode* pDatasetsNode = pXMLDocumentNode->GetFirstChild();
+       pDatasetsNode; pDatasetsNode = pDatasetsNode->GetNextSibling()) {
+    if (MatchNodeName(pDatasetsNode, datasets_packet.name, datasets_packet.uri,
+                      datasets_packet.flags)) {
+      return pDatasetsNode;
+    }
+  }
+  return nullptr;
+}
+
+bool IsStringAllWhitespace(WideString wsText) {
+  wsText.TrimRight(L"\x20\x9\xD\xA");
+  return wsText.IsEmpty();
+}
+
+void ConvertXMLToPlainText(CFX_XMLElement* pRootXMLNode, WideString& wsOutput) {
+  for (CFX_XMLNode* pXMLChild = pRootXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    switch (pXMLChild->GetType()) {
+      case CFX_XMLNode::Type::kElement: {
+        WideString wsTextData = ToXMLElement(pXMLChild)->GetTextData();
+        wsTextData += L"\n";
+        wsOutput += wsTextData;
+        break;
+      }
+      case CFX_XMLNode::Type::kText:
+      case CFX_XMLNode::Type::kCharData: {
+        WideString wsText = ToXMLText(pXMLChild)->GetText();
+        if (IsStringAllWhitespace(wsText))
+          continue;
+        wsOutput = std::move(wsText);
+        break;
+      }
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+}
+
+WideString GetPlainTextFromRichText(CFX_XMLNode* pXMLNode) {
+  if (!pXMLNode)
+    return WideString();
+
+  WideString wsPlainText;
+  switch (pXMLNode->GetType()) {
+    case CFX_XMLNode::Type::kElement: {
+      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
+      WideString wsTag = pXMLElement->GetLocalTagName();
+      uint32_t uTag = FX_HashCode_GetW(wsTag.AsStringView(), true);
+      if (uTag == 0x0001f714) {
+        wsPlainText += L"\n";
+      } else if (uTag == 0x00000070) {
+        if (!wsPlainText.IsEmpty()) {
+          wsPlainText += L"\n";
+        }
+      } else if (uTag == 0xa48ac63) {
+        if (!wsPlainText.IsEmpty() && wsPlainText.Back() != '\n') {
+          wsPlainText += L"\n";
+        }
+      }
+      break;
+    }
+    case CFX_XMLNode::Type::kText:
+    case CFX_XMLNode::Type::kCharData: {
+      WideString wsContent = ToXMLText(pXMLNode)->GetText();
+      wsPlainText += wsContent;
+      break;
+    }
+    default:
+      break;
+  }
+  for (CFX_XMLNode* pChildXML = pXMLNode->GetFirstChild(); pChildXML;
+       pChildXML = pChildXML->GetNextSibling()) {
+    wsPlainText += GetPlainTextFromRichText(pChildXML);
+  }
+
+  return wsPlainText;
+}
+
+}  // namespace
+
+bool XFA_RecognizeRichText(CFX_XMLElement* pRichTextXMLNode) {
+  return pRichTextXMLNode && pRichTextXMLNode->GetNamespaceURI().EqualsASCII(
+                                 "http://www.w3.org/1999/xhtml");
+}
+
+CXFA_DocumentParser::CXFA_DocumentParser(CXFA_Document* pFactory)
+    : m_pFactory(pFactory) {}
+
+CXFA_DocumentParser::~CXFA_DocumentParser() = default;
+
+bool CXFA_DocumentParser::Parse(
+    const RetainPtr<IFX_SeekableReadStream>& pStream,
     XFA_PacketType ePacketID) {
-  m_pDocument.reset();
-  m_nodeParser.CloseParser();
+  xml_doc_ = LoadXML(pStream);
+  if (!xml_doc_)
+    return false;
 
-  int32_t nRetStatus = m_nodeParser.StartParse(pStream, ePacketID);
-  if (nRetStatus == XFA_PARSESTATUS_Ready) {
-    m_pDocument = pdfium::MakeUnique<CXFA_Document>(this);
-    m_nodeParser.SetFactory(m_pDocument.get());
+  CFX_XMLNode* root = GetDocumentNode(xml_doc_->GetRoot());
+  if (!root)
+    return false;
+
+  m_pRootNode = ParseAsXDPPacket(root, ePacketID);
+  return !!m_pRootNode;
+}
+
+CFX_XMLNode* CXFA_DocumentParser::ParseXMLData(const ByteString& wsXML) {
+  auto pStream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(wsXML.raw_span());
+  xml_doc_ = LoadXML(pStream);
+  if (!xml_doc_)
+    return nullptr;
+  return GetDocumentNode(xml_doc_->GetRoot());
+}
+
+std::unique_ptr<CFX_XMLDocument> CXFA_DocumentParser::LoadXML(
+    const RetainPtr<IFX_SeekableReadStream>& pStream) {
+  ASSERT(pStream);
+
+  CFX_XMLParser parser(pStream);
+  std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
+  if (doc) {
+    doc->GetRoot()->InsertChildNode(doc->CreateNode<CFX_XMLInstruction>(L"xml"),
+                                    0);
   }
-  return nRetStatus;
+  return doc;
 }
 
-int32_t CXFA_DocumentParser::DoParse() {
-  int32_t nRetStatus = m_nodeParser.DoParse();
-  if (nRetStatus >= XFA_PARSESTATUS_Done) {
-    ASSERT(m_pDocument);
-    m_pDocument->SetRoot(m_nodeParser.GetRootNode());
+void CXFA_DocumentParser::ConstructXFANode(CXFA_Node* pXFANode,
+                                           CFX_XMLNode* pXMLNode) {
+  XFA_PacketType ePacketID = pXFANode->GetPacketType();
+  if (ePacketID == XFA_PacketType::Datasets) {
+    if (pXFANode->GetElementType() == XFA_Element::DataValue) {
+      for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+           pXMLChild = pXMLChild->GetNextSibling()) {
+        CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
+        if (eNodeType == CFX_XMLNode::Type::kInstruction)
+          continue;
+
+        if (eNodeType == CFX_XMLNode::Type::kElement) {
+          CXFA_Node* pXFAChild = m_pFactory->CreateNode(
+              XFA_PacketType::Datasets, XFA_Element::DataValue);
+          if (!pXFAChild)
+            return;
+
+          CFX_XMLElement* child = static_cast<CFX_XMLElement*>(pXMLChild);
+          WideString wsNodeStr = child->GetLocalTagName();
+          pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr, false,
+                                          false);
+          WideString wsChildValue = GetPlainTextFromRichText(child);
+          if (!wsChildValue.IsEmpty())
+            pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsChildValue,
+                                            false, false);
+
+          pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+          pXFAChild->SetXMLMappingNode(pXMLChild);
+          pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
+          break;
+        }
+      }
+      m_pRootNode = pXFANode;
+    } else {
+      m_pRootNode = DataLoader(pXFANode, pXMLNode, true);
+    }
+  } else if (pXFANode->IsContentNode()) {
+    ParseContentNode(pXFANode, pXMLNode, ePacketID);
+    m_pRootNode = pXFANode;
+  } else {
+    m_pRootNode = NormalLoader(pXFANode, pXMLNode, ePacketID, true);
   }
-  return nRetStatus;
 }
 
-CFX_XMLDoc* CXFA_DocumentParser::GetXMLDoc() const {
-  return m_nodeParser.GetXMLDoc();
+CXFA_Node* CXFA_DocumentParser::GetRootNode() const {
+  return m_pRootNode;
 }
 
-CXFA_FFNotify* CXFA_DocumentParser::GetNotify() const {
-  return m_pNotify.Get();
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
+                                                 XFA_PacketType ePacketID) {
+  switch (ePacketID) {
+    case XFA_PacketType::Xdp:
+      return ParseAsXDPPacket_XDP(pXMLDocumentNode);
+    case XFA_PacketType::Config:
+      return ParseAsXDPPacket_Config(pXMLDocumentNode);
+    case XFA_PacketType::Template:
+      return ParseAsXDPPacket_Template(pXMLDocumentNode);
+    case XFA_PacketType::Form:
+      return ParseAsXDPPacket_Form(pXMLDocumentNode);
+    case XFA_PacketType::Datasets:
+      return ParseAsXDPPacket_Data(pXMLDocumentNode);
+    case XFA_PacketType::Xdc:
+      return ParseAsXDPPacket_Xdc(pXMLDocumentNode);
+    case XFA_PacketType::LocaleSet:
+      return ParseAsXDPPacket_LocaleConnectionSourceSet(
+          pXMLDocumentNode, XFA_PacketType::LocaleSet, XFA_Element::LocaleSet);
+    case XFA_PacketType::ConnectionSet:
+      return ParseAsXDPPacket_LocaleConnectionSourceSet(
+          pXMLDocumentNode, XFA_PacketType::ConnectionSet,
+          XFA_Element::ConnectionSet);
+    case XFA_PacketType::SourceSet:
+      return ParseAsXDPPacket_LocaleConnectionSourceSet(
+          pXMLDocumentNode, XFA_PacketType::SourceSet, XFA_Element::SourceSet);
+    default:
+      return ParseAsXDPPacket_User(pXMLDocumentNode);
+  }
 }
 
-CXFA_Document* CXFA_DocumentParser::GetDocument() const {
-  return m_pDocument.get();
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_XDP(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
+    return nullptr;
+
+  CXFA_Node* pXFARootNode =
+      m_pFactory->CreateNode(XFA_PacketType::Xdp, XFA_Element::Xfa);
+  if (!pXFARootNode)
+    return nullptr;
+
+  m_pRootNode = pXFARootNode;
+  pXFARootNode->JSObject()->SetCData(XFA_Attribute::Name, L"xfa", false, false);
+
+  for (auto it : ToXMLElement(pXMLDocumentNode)->GetAttributes()) {
+    if (it.first.EqualsASCII("uuid")) {
+      pXFARootNode->JSObject()->SetCData(XFA_Attribute::Uuid, it.second, false,
+                                         false);
+    } else if (it.first.EqualsASCII("timeStamp")) {
+      pXFARootNode->JSObject()->SetCData(XFA_Attribute::TimeStamp, it.second,
+                                         false, false);
+    }
+  }
+
+  CFX_XMLNode* pXMLConfigDOMRoot = nullptr;
+  CXFA_Node* pXFAConfigDOMRoot = nullptr;
+  XFA_PACKETINFO config_packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
+  for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem;
+       pChildItem = pChildItem->GetNextSibling()) {
+    if (!MatchNodeName(pChildItem, config_packet.name, config_packet.uri,
+                       config_packet.flags)) {
+      continue;
+    }
+    // TODO(tsepez): make GetFirstChildByName() take a name.
+    uint32_t hash = FX_HashCode_GetW(config_packet.name, false);
+    if (pXFARootNode->GetFirstChildByName(hash))
+      return nullptr;
+
+    pXMLConfigDOMRoot = pChildItem;
+    pXFAConfigDOMRoot = ParseAsXDPPacket_Config(pXMLConfigDOMRoot);
+    if (pXFAConfigDOMRoot)
+      pXFARootNode->InsertChildAndNotify(pXFAConfigDOMRoot, nullptr);
+  }
+
+  CFX_XMLNode* pXMLDatasetsDOMRoot = nullptr;
+  CFX_XMLNode* pXMLFormDOMRoot = nullptr;
+  CFX_XMLNode* pXMLTemplateDOMRoot = nullptr;
+  for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem;
+       pChildItem = pChildItem->GetNextSibling()) {
+    CFX_XMLElement* pElement = ToXMLElement(pChildItem);
+    if (!pElement || pElement == pXMLConfigDOMRoot)
+      continue;
+
+    WideString wsPacketName = pElement->GetLocalTagName();
+    Optional<XFA_PACKETINFO> packet_info =
+        XFA_GetPacketByName(wsPacketName.AsStringView());
+    if (packet_info.has_value() && packet_info.value().uri &&
+        !MatchNodeName(pElement, packet_info.value().name,
+                       packet_info.value().uri, packet_info.value().flags)) {
+      packet_info = {};
+    }
+    XFA_PacketType ePacket = XFA_PacketType::User;
+    if (packet_info.has_value())
+      ePacket = packet_info.value().packet_type;
+    if (ePacket == XFA_PacketType::Xdp)
+      continue;
+    if (ePacket == XFA_PacketType::Datasets) {
+      if (pXMLDatasetsDOMRoot)
+        return nullptr;
+
+      pXMLDatasetsDOMRoot = pElement;
+    } else if (ePacket == XFA_PacketType::Form) {
+      if (pXMLFormDOMRoot)
+        return nullptr;
+
+      pXMLFormDOMRoot = pElement;
+    } else if (ePacket == XFA_PacketType::Template) {
+      // Found a duplicate template packet.
+      if (pXMLTemplateDOMRoot)
+        return nullptr;
+
+      CXFA_Node* pPacketNode = ParseAsXDPPacket_Template(pElement);
+      if (pPacketNode) {
+        pXMLTemplateDOMRoot = pElement;
+        pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+      }
+    } else {
+      CXFA_Node* pPacketNode = ParseAsXDPPacket(pElement, ePacket);
+      if (pPacketNode) {
+        if (packet_info.has_value() &&
+            (packet_info.value().flags & XFA_XDPPACKET_FLAGS_SUPPORTONE) &&
+            pXFARootNode->GetFirstChildByName(
+                FX_HashCode_GetW(packet_info.value().name, false))) {
+          return nullptr;
+        }
+        pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+      }
+    }
+  }
+
+  // No template is found.
+  if (!pXMLTemplateDOMRoot)
+    return nullptr;
+
+  if (pXMLDatasetsDOMRoot) {
+    CXFA_Node* pPacketNode =
+        ParseAsXDPPacket(pXMLDatasetsDOMRoot, XFA_PacketType::Datasets);
+    if (pPacketNode)
+      pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+  }
+  if (pXMLFormDOMRoot) {
+    CXFA_Node* pPacketNode =
+        ParseAsXDPPacket(pXMLFormDOMRoot, XFA_PacketType::Form);
+    if (pPacketNode)
+      pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr);
+  }
+
+  pXFARootNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pXFARootNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Config(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
+    return nullptr;
+
+  CXFA_Node* pNode =
+      m_pFactory->CreateNode(XFA_PacketType::Config, XFA_Element::Config);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
+  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Config, true))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Template(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Template);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
+    return nullptr;
+
+  CXFA_Node* pNode =
+      m_pFactory->CreateNode(XFA_PacketType::Template, XFA_Element::Template);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
+
+  CFX_XMLElement* pXMLDocumentElement = ToXMLElement(pXMLDocumentNode);
+  WideString wsNamespaceURI = pXMLDocumentElement->GetNamespaceURI();
+  if (wsNamespaceURI.IsEmpty())
+    wsNamespaceURI = pXMLDocumentElement->GetAttribute(L"xmlns:xfa");
+
+  pNode->GetDocument()->RecognizeXFAVersionNumber(wsNamespaceURI);
+
+  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Template, true))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Form(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Form);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
+    return nullptr;
+
+  CXFA_Node* pNode =
+      m_pFactory->CreateNode(XFA_PacketType::Form, XFA_Element::Form);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
+  CXFA_Template* pTemplateRoot =
+      m_pRootNode->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
+  CXFA_Subform* pTemplateChosen =
+      pTemplateRoot ? pTemplateRoot->GetFirstChildByClass<CXFA_Subform>(
+                          XFA_Element::Subform)
+                    : nullptr;
+  bool bUseAttribute = true;
+  if (pTemplateChosen &&
+      pTemplateChosen->JSObject()->GetEnum(XFA_Attribute::RestoreState) !=
+          XFA_AttributeValue::Auto) {
+    bUseAttribute = false;
+  }
+  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Form,
+                    bUseAttribute))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Data(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Datasets);
+  CFX_XMLNode* pDatasetsXMLNode = GetDataSetsFromXDP(pXMLDocumentNode);
+  if (pDatasetsXMLNode) {
+    CXFA_Node* pNode = m_pFactory->CreateNode(XFA_PacketType::Datasets,
+                                              XFA_Element::DataModel);
+    if (!pNode)
+      return nullptr;
+
+    pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
+    if (!DataLoader(pNode, pDatasetsXMLNode, false))
+      return nullptr;
+
+    pNode->SetXMLMappingNode(pDatasetsXMLNode);
+    return pNode;
+  }
+
+  CFX_XMLNode* pDataXMLNode = nullptr;
+  if (MatchNodeName(pXMLDocumentNode, L"data", packet.uri, packet.flags)) {
+    ToXMLElement(pXMLDocumentNode)->RemoveAttribute(L"xmlns:xfa");
+    pDataXMLNode = pXMLDocumentNode;
+  } else {
+    auto* pDataElement = xml_doc_->CreateNode<CFX_XMLElement>(L"xfa:data");
+    pXMLDocumentNode->RemoveSelfIfParented();
+
+    CFX_XMLElement* pElement = ToXMLElement(pXMLDocumentNode);
+    pElement->RemoveAttribute(L"xmlns:xfa");
+
+    // The node was either removed from the parent above, or already has no
+    // parent so we can take ownership.
+    pDataElement->AppendLastChild(pXMLDocumentNode);
+    pDataXMLNode = pDataElement;
+  }
+  if (!pDataXMLNode)
+    return nullptr;
+
+  CXFA_Node* pNode =
+      m_pFactory->CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup);
+  if (!pNode)
+    return nullptr;
+
+  WideString wsLocalName = ToXMLElement(pDataXMLNode)->GetLocalTagName();
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, wsLocalName, false, false);
+  if (!DataLoader(pNode, pDataXMLNode, true))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pDataXMLNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_LocaleConnectionSourceSet(
+    CFX_XMLNode* pXMLDocumentNode,
+    XFA_PacketType packet_type,
+    XFA_Element element) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(packet_type);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
+    return nullptr;
+
+  CXFA_Node* pNode = m_pFactory->CreateNode(packet_type, element);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
+  if (!NormalLoader(pNode, pXMLDocumentNode, packet_type, true))
+    return nullptr;
+
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Xdc(
+    CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdc);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
+    return nullptr;
+
+  CXFA_Node* pNode =
+      m_pFactory->CreateNode(XFA_PacketType::Xdc, XFA_Element::Xdc);
+  if (!pNode)
+    return nullptr;
+
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_User(
+    CFX_XMLNode* pXMLDocumentNode) {
+  CXFA_Node* pNode =
+      m_pFactory->CreateNode(XFA_PacketType::Xdp, XFA_Element::Packet);
+  if (!pNode)
+    return nullptr;
+
+  WideString wsName = ToXMLElement(pXMLDocumentNode)->GetLocalTagName();
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
+  pNode->SetXMLMappingNode(pXMLDocumentNode);
+  return pNode;
+}
+
+CXFA_Node* CXFA_DocumentParser::DataLoader(CXFA_Node* pXFANode,
+                                           CFX_XMLNode* pXMLDoc,
+                                           bool bDoTransform) {
+  ParseDataGroup(pXFANode, pXMLDoc, XFA_PacketType::Datasets);
+  return pXFANode;
+}
+
+CXFA_Node* CXFA_DocumentParser::NormalLoader(CXFA_Node* pXFANode,
+                                             CFX_XMLNode* pXMLDoc,
+                                             XFA_PacketType ePacketID,
+                                             bool bUseAttribute) {
+  constexpr size_t kMaxExecuteRecursion = 1000;
+  if (m_ExecuteRecursionDepth > kMaxExecuteRecursion)
+    return nullptr;
+  AutoRestorer<size_t> restorer(&m_ExecuteRecursionDepth);
+  ++m_ExecuteRecursionDepth;
+
+  bool bOneOfPropertyFound = false;
+  for (CFX_XMLNode* pXMLChild = pXMLDoc->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    switch (pXMLChild->GetType()) {
+      case CFX_XMLNode::Type::kElement: {
+        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
+        WideString wsTagName = pXMLElement->GetLocalTagName();
+        XFA_Element eType = XFA_GetElementByName(wsTagName.AsStringView());
+        if (eType == XFA_Element::Unknown)
+          continue;
+
+        if (pXFANode->HasPropertyFlags(
+                eType,
+                XFA_PROPERTYFLAG_OneOf | XFA_PROPERTYFLAG_DefaultOneOf)) {
+          if (bOneOfPropertyFound)
+            break;
+          bOneOfPropertyFound = true;
+        }
+
+        CXFA_Node* pXFAChild = m_pFactory->CreateNode(ePacketID, eType);
+        if (!pXFAChild)
+          return nullptr;
+        if (ePacketID == XFA_PacketType::Config) {
+          pXFAChild->JSObject()->SetAttribute(XFA_Attribute::Name,
+                                              wsTagName.AsStringView(), false);
+        }
+
+        bool IsNeedValue = true;
+        for (auto it : pXMLElement->GetAttributes()) {
+          WideString wsAttrName;
+          GetAttributeLocalName(it.first.AsStringView(), wsAttrName);
+          if (wsAttrName.EqualsASCII("nil") && it.second.EqualsASCII("true"))
+            IsNeedValue = false;
+
+          Optional<XFA_ATTRIBUTEINFO> attr =
+              XFA_GetAttributeByName(wsAttrName.AsStringView());
+          if (!attr.has_value())
+            continue;
+
+          if (!bUseAttribute && attr.value().attribute != XFA_Attribute::Name &&
+              attr.value().attribute != XFA_Attribute::Save) {
+            continue;
+          }
+          pXFAChild->JSObject()->SetAttribute(attr.value().attribute,
+                                              it.second.AsStringView(), false);
+        }
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        if (eType == XFA_Element::Validate || eType == XFA_Element::Locale) {
+          if (ePacketID == XFA_PacketType::Config)
+            ParseContentNode(pXFAChild, pXMLElement, ePacketID);
+          else
+            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
+
+          break;
+        }
+        switch (pXFAChild->GetObjectType()) {
+          case XFA_ObjectType::ContentNode:
+          case XFA_ObjectType::TextNode:
+          case XFA_ObjectType::NodeC:
+          case XFA_ObjectType::NodeV:
+            if (IsNeedValue)
+              ParseContentNode(pXFAChild, pXMLElement, ePacketID);
+            break;
+          default:
+            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
+            break;
+        }
+      } break;
+      case CFX_XMLNode::Type::kInstruction:
+        ParseInstruction(pXFANode, ToXMLInstruction(pXMLChild), ePacketID);
+        break;
+      default:
+        break;
+    }
+  }
+  return pXFANode;
+}
+
+void CXFA_DocumentParser::ParseContentNode(CXFA_Node* pXFANode,
+                                           CFX_XMLNode* pXMLNode,
+                                           XFA_PacketType ePacketID) {
+  XFA_Element element = XFA_Element::Sharptext;
+  if (pXFANode->GetElementType() == XFA_Element::ExData) {
+    WideString wsContentType =
+        pXFANode->JSObject()->GetCData(XFA_Attribute::ContentType);
+    if (wsContentType.EqualsASCII("text/html"))
+      element = XFA_Element::SharpxHTML;
+    else if (wsContentType.EqualsASCII("text/xml"))
+      element = XFA_Element::Sharpxml;
+  }
+  if (element == XFA_Element::SharpxHTML)
+    pXFANode->SetXMLMappingNode(pXMLNode);
+
+  WideString wsValue;
+  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
+    if (eNodeType == CFX_XMLNode::Type::kInstruction)
+      continue;
+
+    CFX_XMLElement* pElement = ToXMLElement(pXMLChild);
+    if (element == XFA_Element::SharpxHTML) {
+      if (!pElement)
+        break;
+      if (XFA_RecognizeRichText(pElement))
+        wsValue += GetPlainTextFromRichText(pElement);
+    } else if (element == XFA_Element::Sharpxml) {
+      if (!pElement)
+        break;
+      ConvertXMLToPlainText(pElement, wsValue);
+    } else {
+      if (pElement)
+        break;
+      CFX_XMLText* pText = ToXMLText(pXMLChild);
+      if (pText)
+        wsValue = pText->GetText();
+    }
+    break;
+  }
+  if (!wsValue.IsEmpty()) {
+    if (pXFANode->IsContentNode()) {
+      CXFA_Node* pContentRawDataNode =
+          m_pFactory->CreateNode(ePacketID, element);
+      ASSERT(pContentRawDataNode);
+      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsValue,
+                                                false, false);
+      pXFANode->InsertChildAndNotify(pContentRawDataNode, nullptr);
+    } else {
+      pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsValue, false,
+                                     false);
+    }
+  }
+}
+
+void CXFA_DocumentParser::ParseDataGroup(CXFA_Node* pXFANode,
+                                         CFX_XMLNode* pXMLNode,
+                                         XFA_PacketType ePacketID) {
+  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    switch (pXMLChild->GetType()) {
+      case CFX_XMLNode::Type::kElement: {
+        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
+        WideString wsNamespaceURI = pXMLElement->GetNamespaceURI();
+        if (wsNamespaceURI.EqualsASCII(
+                "http://www.xfa.com/schema/xfa-package/") ||
+            wsNamespaceURI.EqualsASCII(
+                "http://www.xfa.org/schema/xfa-package/") ||
+            wsNamespaceURI.EqualsASCII(
+                "http://www.w3.org/2001/XMLSchema-instance")) {
+          continue;
+        }
+
+        XFA_Element eNodeType = XFA_Element::DataModel;
+        if (eNodeType == XFA_Element::DataModel) {
+          Optional<WideString> wsDataNodeAttr =
+              FindAttributeWithNS(pXMLElement, L"dataNode",
+                                  L"http://www.xfa.org/schema/xfa-data/1.0/");
+          if (wsDataNodeAttr.has_value()) {
+            if (wsDataNodeAttr.value().EqualsASCII("dataGroup"))
+              eNodeType = XFA_Element::DataGroup;
+            else if (wsDataNodeAttr.value().EqualsASCII("dataValue"))
+              eNodeType = XFA_Element::DataValue;
+          }
+        }
+        if (eNodeType == XFA_Element::DataModel) {
+          Optional<WideString> wsContentType =
+              FindAttributeWithNS(pXMLElement, L"contentType",
+                                  L"http://www.xfa.org/schema/xfa-data/1.0/");
+          if (wsContentType.has_value() && !wsContentType.value().IsEmpty())
+            eNodeType = XFA_Element::DataValue;
+        }
+        if (eNodeType == XFA_Element::DataModel) {
+          for (CFX_XMLNode* pXMLDataChild = pXMLElement->GetFirstChild();
+               pXMLDataChild; pXMLDataChild = pXMLDataChild->GetNextSibling()) {
+            CFX_XMLElement* pElement = ToXMLElement(pXMLDataChild);
+            if (pElement && !XFA_RecognizeRichText(pElement)) {
+              eNodeType = XFA_Element::DataGroup;
+              break;
+            }
+          }
+        }
+        if (eNodeType == XFA_Element::DataModel)
+          eNodeType = XFA_Element::DataValue;
+
+        CXFA_Node* pXFAChild =
+            m_pFactory->CreateNode(XFA_PacketType::Datasets, eNodeType);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(
+            XFA_Attribute::Name, pXMLElement->GetLocalTagName(), false, false);
+        bool bNeedValue = true;
+
+        for (auto it : pXMLElement->GetAttributes()) {
+          WideString wsName;
+          WideString wsNS;
+          if (!ResolveAttribute(pXMLElement, it.first, wsName, wsNS)) {
+            continue;
+          }
+          if (wsName.EqualsASCII("nil") && it.second.EqualsASCII("true")) {
+            bNeedValue = false;
+            continue;
+          }
+          if (wsNS.EqualsASCII("http://www.xfa.com/schema/xfa-package/") ||
+              wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-package/") ||
+              wsNS.EqualsASCII("http://www.w3.org/2001/XMLSchema-instance") ||
+              wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-data/1.0/")) {
+            continue;
+          }
+          CXFA_Node* pXFAMetaData = m_pFactory->CreateNode(
+              XFA_PacketType::Datasets, XFA_Element::DataValue);
+          if (!pXFAMetaData)
+            return;
+
+          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Name, wsName, false,
+                                             false);
+          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::QualifiedName,
+                                             it.first, false, false);
+          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Value, it.second,
+                                             false, false);
+          pXFAMetaData->JSObject()->SetEnum(
+              XFA_Attribute::Contains, XFA_AttributeValue::MetaData, false);
+          pXFAChild->InsertChildAndNotify(pXFAMetaData, nullptr);
+          pXFAMetaData->SetXMLMappingNode(pXMLElement);
+          pXFAMetaData->SetFlag(XFA_NodeFlag_Initialized);
+        }
+
+        if (!bNeedValue)
+          pXMLElement->RemoveAttribute(L"xsi:nil");
+
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        if (eNodeType == XFA_Element::DataGroup)
+          ParseDataGroup(pXFAChild, pXMLElement, ePacketID);
+        else if (bNeedValue)
+          ParseDataValue(pXFAChild, pXMLChild, XFA_PacketType::Datasets);
+
+        pXFAChild->SetXMLMappingNode(pXMLElement);
+        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
+        continue;
+      }
+      case CFX_XMLNode::Type::kCharData:
+      case CFX_XMLNode::Type::kText: {
+        CFX_XMLText* pXMLText = ToXMLText(pXMLChild);
+        WideString wsText = pXMLText->GetText();
+        if (IsStringAllWhitespace(wsText))
+          continue;
+
+        CXFA_Node* pXFAChild = m_pFactory->CreateNode(XFA_PacketType::Datasets,
+                                                      XFA_Element::DataValue);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsText, false,
+                                        false);
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        pXFAChild->SetXMLMappingNode(pXMLText);
+        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
+        continue;
+      }
+      default:
+        continue;
+    }
+  }
+}
+
+void CXFA_DocumentParser::ParseDataValue(CXFA_Node* pXFANode,
+                                         CFX_XMLNode* pXMLNode,
+                                         XFA_PacketType ePacketID) {
+  CFX_WideTextBuf wsValueTextBuf;
+  CFX_WideTextBuf wsCurValueTextBuf;
+  bool bMarkAsCompound = false;
+  CFX_XMLNode* pXMLCurValueNode = nullptr;
+  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
+       pXMLChild = pXMLChild->GetNextSibling()) {
+    CFX_XMLNode::Type eNodeType = pXMLChild->GetType();
+    if (eNodeType == CFX_XMLNode::Type::kInstruction)
+      continue;
+
+    CFX_XMLText* pText = ToXMLText(pXMLChild);
+    if (pText) {
+      WideString wsText = pText->GetText();
+      if (!pXMLCurValueNode)
+        pXMLCurValueNode = pXMLChild;
+      wsCurValueTextBuf << wsText;
+      continue;
+    }
+    if (XFA_RecognizeRichText(ToXMLElement(pXMLChild))) {
+      WideString wsText = GetPlainTextFromRichText(ToXMLElement(pXMLChild));
+      if (!pXMLCurValueNode)
+        pXMLCurValueNode = pXMLChild;
+      wsCurValueTextBuf << wsText;
+      continue;
+    }
+    bMarkAsCompound = true;
+    if (pXMLCurValueNode) {
+      WideString wsCurValue = wsCurValueTextBuf.MakeString();
+      if (!wsCurValue.IsEmpty()) {
+        CXFA_Node* pXFAChild =
+            m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString(),
+                                        false, false);
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue, false,
+                                        false);
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
+        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
+        wsValueTextBuf << wsCurValue;
+        wsCurValueTextBuf.Clear();
+      }
+      pXMLCurValueNode = nullptr;
+    }
+    CXFA_Node* pXFAChild =
+        m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
+    if (!pXFAChild)
+      return;
+
+    WideString wsNodeStr = ToXMLElement(pXMLChild)->GetLocalTagName();
+    pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr, false,
+                                    false);
+    ParseDataValue(pXFAChild, pXMLChild, ePacketID);
+    pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+    pXFAChild->SetXMLMappingNode(pXMLChild);
+    pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
+    WideString wsCurValue =
+        pXFAChild->JSObject()->GetCData(XFA_Attribute::Value);
+    wsValueTextBuf << wsCurValue;
+  }
+
+  if (pXMLCurValueNode) {
+    WideString wsCurValue = wsCurValueTextBuf.MakeString();
+    if (!wsCurValue.IsEmpty()) {
+      if (bMarkAsCompound) {
+        CXFA_Node* pXFAChild =
+            m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
+        if (!pXFAChild)
+          return;
+
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString(),
+                                        false, false);
+        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue, false,
+                                        false);
+        pXFANode->InsertChildAndNotify(pXFAChild, nullptr);
+        pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
+        pXFAChild->SetFlag(XFA_NodeFlag_Initialized);
+      }
+      wsValueTextBuf << wsCurValue;
+      wsCurValueTextBuf.Clear();
+    }
+    pXMLCurValueNode = nullptr;
+  }
+  WideString wsNodeValue = wsValueTextBuf.MakeString();
+  pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsNodeValue, false,
+                                 false);
+}
+
+void CXFA_DocumentParser::ParseInstruction(CXFA_Node* pXFANode,
+                                           CFX_XMLInstruction* pXMLInstruction,
+                                           XFA_PacketType ePacketID) {
+  const std::vector<WideString>& target_data = pXMLInstruction->GetTargetData();
+  if (pXMLInstruction->IsOriginalXFAVersion()) {
+    if (target_data.size() > 1 &&
+        (pXFANode->GetDocument()->RecognizeXFAVersionNumber(target_data[0]) !=
+         XFA_VERSION_UNKNOWN) &&
+        target_data[1].EqualsASCII("v2.7-scripting:1")) {
+      pXFANode->GetDocument()->set_is_scripting();
+    }
+    return;
+  }
+  if (pXMLInstruction->IsAcrobat()) {
+    if (target_data.size() > 1 && target_data[0].EqualsASCII("JavaScript") &&
+        target_data[1].EqualsASCII("strictScoping")) {
+      pXFANode->GetDocument()->set_is_strict_scoping();
+    }
+  }
 }
diff --git a/xfa/fxfa/parser/cxfa_document_parser.h b/xfa/fxfa/parser/cxfa_document_parser.h
index 35d7160..a494b4f 100644
--- a/xfa/fxfa/parser/cxfa_document_parser.h
+++ b/xfa/fxfa/parser/cxfa_document_parser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// 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.
 
@@ -8,32 +8,75 @@
 #define XFA_FXFA_PARSER_CXFA_DOCUMENT_PARSER_H_
 
 #include <memory>
+#include <utility>
 
-#include "xfa/fxfa/parser/cxfa_simple_parser.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "xfa/fxfa/fxfa_basic.h"
 
-class CFX_XMLDoc;
+class CFX_XMLDocument;
+class CFX_XMLNode;
 class CXFA_Document;
-class CXFA_FFNotify;
-class CXFA_Notify;
-class IFX_SeekableStream;
+class CXFA_Node;
+class CFX_XMLInstruction;
+class IFX_SeekableReadStream;
 
 class CXFA_DocumentParser {
  public:
-  explicit CXFA_DocumentParser(CXFA_FFNotify* pNotify);
+  explicit CXFA_DocumentParser(CXFA_Document* pFactory);
   ~CXFA_DocumentParser();
 
-  int32_t StartParse(const RetainPtr<IFX_SeekableStream>& pStream,
-                     XFA_PacketType ePacketID);
-  int32_t DoParse();
+  bool Parse(const RetainPtr<IFX_SeekableReadStream>& pStream,
+             XFA_PacketType ePacketID);
 
-  CFX_XMLDoc* GetXMLDoc() const;
-  CXFA_FFNotify* GetNotify() const;
-  CXFA_Document* GetDocument() const;
+  CFX_XMLNode* ParseXMLData(const ByteString& wsXML);
+  void ConstructXFANode(CXFA_Node* pXFANode, CFX_XMLNode* pXMLNode);
+
+  std::unique_ptr<CFX_XMLDocument> GetXMLDoc() { return std::move(xml_doc_); }
+  CXFA_Node* GetRootNode() const;
 
  private:
-  UnownedPtr<CXFA_FFNotify> const m_pNotify;
-  std::unique_ptr<CXFA_Document> m_pDocument;
-  CXFA_SimpleParser m_nodeParser;
+  std::unique_ptr<CFX_XMLDocument> LoadXML(
+      const RetainPtr<IFX_SeekableReadStream>& pStream);
+
+  CXFA_Node* ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
+                              XFA_PacketType ePacketID);
+  CXFA_Node* ParseAsXDPPacket_XDP(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Config(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Template(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Form(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_Data(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_LocaleConnectionSourceSet(
+      CFX_XMLNode* pXMLDocumentNode,
+      XFA_PacketType packet_type,
+      XFA_Element element);
+  CXFA_Node* ParseAsXDPPacket_Xdc(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* ParseAsXDPPacket_User(CFX_XMLNode* pXMLDocumentNode);
+  CXFA_Node* NormalLoader(CXFA_Node* pXFANode,
+                          CFX_XMLNode* pXMLDoc,
+                          XFA_PacketType ePacketID,
+                          bool bUseAttribute);
+  CXFA_Node* DataLoader(CXFA_Node* pXFANode,
+                        CFX_XMLNode* pXMLDoc,
+                        bool bDoTransform);
+  void ParseContentNode(CXFA_Node* pXFANode,
+                        CFX_XMLNode* pXMLNode,
+                        XFA_PacketType ePacketID);
+  void ParseDataValue(CXFA_Node* pXFANode,
+                      CFX_XMLNode* pXMLNode,
+                      XFA_PacketType ePacketID);
+  void ParseDataGroup(CXFA_Node* pXFANode,
+                      CFX_XMLNode* pXMLNode,
+                      XFA_PacketType ePacketID);
+  void ParseInstruction(CXFA_Node* pXFANode,
+                        CFX_XMLInstruction* pXMLInstruction,
+                        XFA_PacketType ePacketID);
+
+  UnownedPtr<CXFA_Document> m_pFactory;
+  std::unique_ptr<CFX_XMLDocument> xml_doc_;
+  // TODO(dsinclair): Figure out who owns this.
+  CXFA_Node* m_pRootNode = nullptr;
+  size_t m_ExecuteRecursionDepth = 0;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_DOCUMENT_PARSER_H_
diff --git a/xfa/fxfa/parser/cxfa_document_parser_embeddertest.cpp b/xfa/fxfa/parser/cxfa_document_parser_embeddertest.cpp
new file mode 100644
index 0000000..129c8bc
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_document_parser_embeddertest.cpp
@@ -0,0 +1,22 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CXFASimpleParserEmbedderTest : public EmbedderTest {};
+
+TEST_F(CXFASimpleParserEmbedderTest, Bug_216) {
+  EXPECT_TRUE(OpenDocument("bug_216.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+  UnloadPage(page);
+}
+
+TEST_F(CXFASimpleParserEmbedderTest, Bug_709793) {
+  EXPECT_TRUE(OpenDocument("bug_709793.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_NE(nullptr, page);
+  UnloadPage(page);
+}
diff --git a/xfa/fxfa/parser/cxfa_document_parser_unittest.cpp b/xfa/fxfa/parser/cxfa_document_parser_unittest.cpp
new file mode 100644
index 0000000..625473a
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_document_parser_unittest.cpp
@@ -0,0 +1,124 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/cxfa_document_parser.h"
+
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+
+class CXFA_DocumentParserTest : public testing::Test {
+ public:
+  void SetUp() override {
+    doc_ = pdfium::MakeUnique<CXFA_Document>(nullptr, nullptr);
+    parser_ = pdfium::MakeUnique<CXFA_DocumentParser>(doc_.get());
+  }
+
+  void TearDown() override {
+    // Hold the XML tree until we cleanup the document.
+    std::unique_ptr<CFX_XMLDocument> doc = parser_->GetXMLDoc();
+    parser_ = nullptr;
+    doc_ = nullptr;
+  }
+
+  CXFA_Document* GetDoc() const { return doc_.get(); }
+  CXFA_DocumentParser* GetParser() const { return parser_.get(); }
+
+ private:
+  std::unique_ptr<CXFA_Document> doc_;
+  std::unique_ptr<CXFA_DocumentParser> parser_;
+};
+
+TEST_F(CXFA_DocumentParserTest, XMLInstructionsScriptOff) {
+  static const char kInput[] =
+      "<config>\n"
+      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
+      "v2.7-scripting:0 ?>\n"
+      "</config>";
+  EXPECT_FALSE(GetDoc()->is_scripting());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
+
+  CXFA_Node* root = GetParser()->GetRootNode();
+  ASSERT_TRUE(root);
+  EXPECT_FALSE(GetDoc()->is_scripting());
+}
+
+TEST_F(CXFA_DocumentParserTest, XMLInstructionsScriptOn) {
+  static const char kInput[] =
+      "<config>\n"
+      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
+      "v2.7-scripting:1 ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_scripting());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
+
+  CXFA_Node* root = GetParser()->GetRootNode();
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(GetDoc()->is_scripting());
+}
+
+TEST_F(CXFA_DocumentParserTest, XMLInstructionsStrictScope) {
+  static const char kInput[] =
+      "<config>"
+      "<?acrobat JavaScript strictScoping ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
+
+  CXFA_Node* root = GetParser()->GetRootNode();
+  ASSERT_TRUE(root);
+  EXPECT_TRUE(GetDoc()->is_strict_scoping());
+}
+
+TEST_F(CXFA_DocumentParserTest, XMLInstructionsStrictScopeBad) {
+  static const char kInput[] =
+      "<config>"
+      "<?acrobat JavaScript otherScoping ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
+
+  CXFA_Node* root = GetParser()->GetRootNode();
+  ASSERT_TRUE(root);
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+}
+
+TEST_F(CXFA_DocumentParserTest, MultipleXMLInstructions) {
+  static const char kInput[] =
+      "<config>"
+      "<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.7 "
+      "v2.7-scripting:1 ?>\n"
+      "<?acrobat JavaScript strictScoping ?>\n"
+      "</config>";
+
+  EXPECT_FALSE(GetDoc()->is_scripting());
+  EXPECT_FALSE(GetDoc()->is_strict_scoping());
+
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+      pdfium::as_bytes(pdfium::make_span(kInput)));
+  ASSERT_TRUE(GetParser()->Parse(stream, XFA_PacketType::Config));
+
+  CXFA_Node* root = GetParser()->GetRootNode();
+  ASSERT_TRUE(root);
+
+  EXPECT_TRUE(GetDoc()->is_scripting());
+  EXPECT_TRUE(GetDoc()->is_strict_scoping());
+}
diff --git a/xfa/fxfa/parser/cxfa_documentassembly.cpp b/xfa/fxfa/parser/cxfa_documentassembly.cpp
index 146071d..c6582e8 100644
--- a/xfa/fxfa/parser/cxfa_documentassembly.cpp
+++ b/xfa/fxfa/parser/cxfa_documentassembly.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_documentassembly.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDocumentAssemblyAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"documentAssembly";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DocumentAssembly,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDocumentAssemblyAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DocumentAssembly::~CXFA_DocumentAssembly() {}
+CXFA_DocumentAssembly::~CXFA_DocumentAssembly() = default;
diff --git a/xfa/fxfa/parser/cxfa_documentassembly.h b/xfa/fxfa/parser/cxfa_documentassembly.h
index c22d4a9..92cde65 100644
--- a/xfa/fxfa/parser/cxfa_documentassembly.h
+++ b/xfa/fxfa/parser/cxfa_documentassembly.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DocumentAssembly : public CXFA_Node {
+class CXFA_DocumentAssembly final : public CXFA_Node {
  public:
   CXFA_DocumentAssembly(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DocumentAssembly() override;
diff --git a/xfa/fxfa/parser/cxfa_draw.cpp b/xfa/fxfa/parser/cxfa_draw.cpp
index 8d0dd37..02387d7 100644
--- a/xfa/fxfa/parser/cxfa_draw.cpp
+++ b/xfa/fxfa/parser/cxfa_draw.cpp
@@ -11,29 +11,30 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kDrawPropertyData[] = {
     {XFA_Element::Ui, 1, 0},     {XFA_Element::Margin, 1, 0},
     {XFA_Element::Para, 1, 0},   {XFA_Element::Border, 1, 0},
     {XFA_Element::Assist, 1, 0}, {XFA_Element::Traversal, 1, 0},
     {XFA_Element::Keep, 1, 0},   {XFA_Element::Caption, 1, 0},
     {XFA_Element::Desc, 1, 0},   {XFA_Element::Font, 1, 0},
     {XFA_Element::Value, 1, 0},  {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kDrawAttributeData[] = {
     {XFA_Attribute::H, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::W, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::X, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Y, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Left},
+     (void*)XFA_AttributeValue::Left},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Rotate, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::VAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Top},
+     (void*)XFA_AttributeValue::Top},
     {XFA_Attribute::MaxH, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MaxW, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MinH, XFA_AttributeType::Measure, (void*)L"0in"},
@@ -43,10 +44,8 @@
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Locale, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AnchorType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::TopLeft},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"draw";
+     (void*)XFA_AttributeValue::TopLeft},
+};
 
 }  // namespace
 
@@ -56,9 +55,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Draw,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kDrawPropertyData,
+                kDrawAttributeData,
                 pdfium::MakeUnique<CJX_Draw>(this)) {}
 
-CXFA_Draw::~CXFA_Draw() {}
+CXFA_Draw::~CXFA_Draw() = default;
diff --git a/xfa/fxfa/parser/cxfa_draw.h b/xfa/fxfa/parser/cxfa_draw.h
index 603103b..c1b9006 100644
--- a/xfa/fxfa/parser/cxfa_draw.h
+++ b/xfa/fxfa/parser/cxfa_draw.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Draw : public CXFA_Node {
+class CXFA_Draw final : public CXFA_Node {
  public:
   CXFA_Draw(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Draw() override;
diff --git a/xfa/fxfa/parser/cxfa_driver.cpp b/xfa/fxfa/parser/cxfa_driver.cpp
index 883cbf6..215601c 100644
--- a/xfa/fxfa/parser/cxfa_driver.cpp
+++ b/xfa/fxfa/parser/cxfa_driver.cpp
@@ -6,18 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_driver.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::FontInfo, 1, 0},
-                                                 {XFA_Element::Xdc, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kDriverPropertyData[] = {
+    {XFA_Element::FontInfo, 1, 0},
+    {XFA_Element::Xdc, 1, 0},
+};
+
+const CXFA_Node::AttributeData kDriverAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"driver";
+};
 
 }  // namespace
 
@@ -27,8 +30,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Driver,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kDriverPropertyData,
+                kDriverAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Driver::~CXFA_Driver() {}
+CXFA_Driver::~CXFA_Driver() = default;
diff --git a/xfa/fxfa/parser/cxfa_driver.h b/xfa/fxfa/parser/cxfa_driver.h
index aee732e..f58e153 100644
--- a/xfa/fxfa/parser/cxfa_driver.h
+++ b/xfa/fxfa/parser/cxfa_driver.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Driver : public CXFA_Node {
+class CXFA_Driver final : public CXFA_Node {
  public:
   CXFA_Driver(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Driver() override;
diff --git a/xfa/fxfa/parser/cxfa_dsigdata.cpp b/xfa/fxfa/parser/cxfa_dsigdata.cpp
index 88d3e8e..19788db 100644
--- a/xfa/fxfa/parser/cxfa_dsigdata.cpp
+++ b/xfa/fxfa/parser/cxfa_dsigdata.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_dsigdata.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDSigDataAttributeData[] = {
     {XFA_Attribute::Value, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"dSigData";
+};
 
 }  // namespace
 
@@ -22,8 +23,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::DSigData,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDSigDataAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DSigData::~CXFA_DSigData() {}
+CXFA_DSigData::~CXFA_DSigData() = default;
diff --git a/xfa/fxfa/parser/cxfa_dsigdata.h b/xfa/fxfa/parser/cxfa_dsigdata.h
index 0cbaeb4..06e691c 100644
--- a/xfa/fxfa/parser/cxfa_dsigdata.h
+++ b/xfa/fxfa/parser/cxfa_dsigdata.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DSigData : public CXFA_Node {
+class CXFA_DSigData final : public CXFA_Node {
  public:
   CXFA_DSigData(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DSigData() override;
diff --git a/xfa/fxfa/parser/cxfa_duplexoption.cpp b/xfa/fxfa/parser/cxfa_duplexoption.cpp
index f8d4170..36feccd 100644
--- a/xfa/fxfa/parser/cxfa_duplexoption.cpp
+++ b/xfa/fxfa/parser/cxfa_duplexoption.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_duplexoption.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDuplexOptionAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"duplexOption";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DuplexOption,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDuplexOptionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DuplexOption::~CXFA_DuplexOption() {}
+CXFA_DuplexOption::~CXFA_DuplexOption() = default;
diff --git a/xfa/fxfa/parser/cxfa_duplexoption.h b/xfa/fxfa/parser/cxfa_duplexoption.h
index 69034ce..5bf8e50 100644
--- a/xfa/fxfa/parser/cxfa_duplexoption.h
+++ b/xfa/fxfa/parser/cxfa_duplexoption.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DuplexOption : public CXFA_Node {
+class CXFA_DuplexOption final : public CXFA_Node {
  public:
   CXFA_DuplexOption(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DuplexOption() override;
diff --git a/xfa/fxfa/parser/cxfa_dynamicrender.cpp b/xfa/fxfa/parser/cxfa_dynamicrender.cpp
index a60e192..5326cec 100644
--- a/xfa/fxfa/parser/cxfa_dynamicrender.cpp
+++ b/xfa/fxfa/parser/cxfa_dynamicrender.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_dynamicrender.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kDynamicRenderAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"dynamicRender";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::DynamicRender,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kDynamicRenderAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_DynamicRender::~CXFA_DynamicRender() {}
+CXFA_DynamicRender::~CXFA_DynamicRender() = default;
diff --git a/xfa/fxfa/parser/cxfa_dynamicrender.h b/xfa/fxfa/parser/cxfa_dynamicrender.h
index f7c54fa..78b75a7 100644
--- a/xfa/fxfa/parser/cxfa_dynamicrender.h
+++ b/xfa/fxfa/parser/cxfa_dynamicrender.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_DynamicRender : public CXFA_Node {
+class CXFA_DynamicRender final : public CXFA_Node {
  public:
   CXFA_DynamicRender(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_DynamicRender() override;
diff --git a/xfa/fxfa/parser/cxfa_edge.cpp b/xfa/fxfa/parser/cxfa_edge.cpp
index 65e599c..36ca7b9 100644
--- a/xfa/fxfa/parser/cxfa_edge.cpp
+++ b/xfa/fxfa/parser/cxfa_edge.cpp
@@ -6,28 +6,28 @@
 
 #include "xfa/fxfa/parser/cxfa_edge.h"
 
-#include "fxjs/xfa/cjx_edge.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Color, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kEdgePropertyData[] = {
+    {XFA_Element::Color, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kEdgeAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Cap, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Square},
+     (void*)XFA_AttributeValue::Square},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Stroke, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Solid},
+     (void*)XFA_AttributeValue::Solid},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::Thickness, XFA_AttributeType::Measure, (void*)L"0.5pt"},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"edge";
+};
 
 }  // namespace
 
@@ -37,9 +37,8 @@
                   (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                   XFA_ObjectType::Node,
                   XFA_Element::Edge,
-                  kPropertyData,
-                  kAttributeData,
-                  kName,
-                  pdfium::MakeUnique<CJX_Edge>(this)) {}
+                  kEdgePropertyData,
+                  kEdgeAttributeData,
+                  pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Edge::~CXFA_Edge() {}
+CXFA_Edge::~CXFA_Edge() = default;
diff --git a/xfa/fxfa/parser/cxfa_edge.h b/xfa/fxfa/parser/cxfa_edge.h
index 36fee55..79c7f14 100644
--- a/xfa/fxfa/parser/cxfa_edge.h
+++ b/xfa/fxfa/parser/cxfa_edge.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_stroke.h"
 
-class CXFA_Edge : public CXFA_Stroke {
+class CXFA_Edge final : public CXFA_Stroke {
  public:
   static constexpr FX_ARGB kDefaultColor = 0xFF000000;
 
diff --git a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp
index a2c5d0f..f2f210d 100644
--- a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp
+++ b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp
@@ -6,16 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_effectiveinputpolicy.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEffectiveInputPolicyAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"effectiveInputPolicy";
+};
 
 }  // namespace
 
@@ -26,8 +27,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::EffectiveInputPolicy,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEffectiveInputPolicyAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_EffectiveInputPolicy::~CXFA_EffectiveInputPolicy() {}
+CXFA_EffectiveInputPolicy::~CXFA_EffectiveInputPolicy() = default;
diff --git a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h
index 52a60a4..4f4fb8e 100644
--- a/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h
+++ b/xfa/fxfa/parser/cxfa_effectiveinputpolicy.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_EffectiveInputPolicy : public CXFA_Node {
+class CXFA_EffectiveInputPolicy final : public CXFA_Node {
  public:
   CXFA_EffectiveInputPolicy(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_EffectiveInputPolicy() override;
diff --git a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp
index e1ab370..43e07ff 100644
--- a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp
+++ b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.cpp
@@ -6,16 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEffectiveOutputPolicyAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"effectiveOutputPolicy";
+};
 
 }  // namespace
 
@@ -26,8 +27,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::EffectiveOutputPolicy,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEffectiveOutputPolicyAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_EffectiveOutputPolicy::~CXFA_EffectiveOutputPolicy() {}
+CXFA_EffectiveOutputPolicy::~CXFA_EffectiveOutputPolicy() = default;
diff --git a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h
index b2317d5..9b15a47 100644
--- a/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h
+++ b/xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_EffectiveOutputPolicy : public CXFA_Node {
+class CXFA_EffectiveOutputPolicy final : public CXFA_Node {
  public:
   CXFA_EffectiveOutputPolicy(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_EffectiveOutputPolicy() override;
diff --git a/xfa/fxfa/parser/cxfa_embed.cpp b/xfa/fxfa/parser/cxfa_embed.cpp
index f65fea4..0941b19 100644
--- a/xfa/fxfa/parser/cxfa_embed.cpp
+++ b/xfa/fxfa/parser/cxfa_embed.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_embed.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEmbedAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"embed";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Embed,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEmbedAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Embed::~CXFA_Embed() {}
+CXFA_Embed::~CXFA_Embed() = default;
diff --git a/xfa/fxfa/parser/cxfa_embed.h b/xfa/fxfa/parser/cxfa_embed.h
index d130840..8772b30 100644
--- a/xfa/fxfa/parser/cxfa_embed.h
+++ b/xfa/fxfa/parser/cxfa_embed.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Embed : public CXFA_Node {
+class CXFA_Embed final : public CXFA_Node {
  public:
   CXFA_Embed(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Embed() override;
diff --git a/xfa/fxfa/parser/cxfa_encoding.cpp b/xfa/fxfa/parser/cxfa_encoding.cpp
index 303221a..a9330d6 100644
--- a/xfa/fxfa/parser/cxfa_encoding.cpp
+++ b/xfa/fxfa/parser/cxfa_encoding.cpp
@@ -6,18 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_encoding.h"
 
-#include "fxjs/xfa/cjx_encoding.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEncodingAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"encoding";
+};
 
 }  // namespace
 
@@ -27,9 +25,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeC,
                 XFA_Element::Encoding,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Encoding>(this)) {}
+                {},
+                kEncodingAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Encoding::~CXFA_Encoding() {}
+CXFA_Encoding::~CXFA_Encoding() = default;
diff --git a/xfa/fxfa/parser/cxfa_encoding.h b/xfa/fxfa/parser/cxfa_encoding.h
index ad41b01..b98ea21 100644
--- a/xfa/fxfa/parser/cxfa_encoding.h
+++ b/xfa/fxfa/parser/cxfa_encoding.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Encoding : public CXFA_Node {
+class CXFA_Encoding final : public CXFA_Node {
  public:
   CXFA_Encoding(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Encoding() override;
diff --git a/xfa/fxfa/parser/cxfa_encodings.cpp b/xfa/fxfa/parser/cxfa_encodings.cpp
index c0df2d6..939cb53 100644
--- a/xfa/fxfa/parser/cxfa_encodings.cpp
+++ b/xfa/fxfa/parser/cxfa_encodings.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_encodings.h"
 
-#include "fxjs/xfa/cjx_encodings.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEncodingsAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"encodings";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Encodings,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Encodings>(this)) {}
+                {},
+                kEncodingsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Encodings::~CXFA_Encodings() {}
+CXFA_Encodings::~CXFA_Encodings() = default;
diff --git a/xfa/fxfa/parser/cxfa_encodings.h b/xfa/fxfa/parser/cxfa_encodings.h
index d3876de..3e8de09 100644
--- a/xfa/fxfa/parser/cxfa_encodings.h
+++ b/xfa/fxfa/parser/cxfa_encodings.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Encodings : public CXFA_Node {
+class CXFA_Encodings final : public CXFA_Node {
  public:
   CXFA_Encodings(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Encodings() override;
diff --git a/xfa/fxfa/parser/cxfa_encrypt.cpp b/xfa/fxfa/parser/cxfa_encrypt.cpp
index 6ddf941..baef9ad 100644
--- a/xfa/fxfa/parser/cxfa_encrypt.cpp
+++ b/xfa/fxfa/parser/cxfa_encrypt.cpp
@@ -11,18 +11,17 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kEncryptPropertyData[] = {
     {XFA_Element::Certificate, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kEncryptAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"encrypt";
+};
 
 }  // namespace
 
@@ -33,9 +32,8 @@
           (XFA_XDPPACKET_Template | XFA_XDPPACKET_Config | XFA_XDPPACKET_Form),
           XFA_ObjectType::ContentNode,
           XFA_Element::Encrypt,
-          kPropertyData,
-          kAttributeData,
-          kName,
+          kEncryptPropertyData,
+          kEncryptAttributeData,
           pdfium::MakeUnique<CJX_Encrypt>(this)) {}
 
-CXFA_Encrypt::~CXFA_Encrypt() {}
+CXFA_Encrypt::~CXFA_Encrypt() = default;
diff --git a/xfa/fxfa/parser/cxfa_encrypt.h b/xfa/fxfa/parser/cxfa_encrypt.h
index 4968db6..afb74b4 100644
--- a/xfa/fxfa/parser/cxfa_encrypt.h
+++ b/xfa/fxfa/parser/cxfa_encrypt.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Encrypt : public CXFA_Node {
+class CXFA_Encrypt final : public CXFA_Node {
  public:
   CXFA_Encrypt(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Encrypt() override;
diff --git a/xfa/fxfa/parser/cxfa_encryption.cpp b/xfa/fxfa/parser/cxfa_encryption.cpp
index 2a876fe..e730770 100644
--- a/xfa/fxfa/parser/cxfa_encryption.cpp
+++ b/xfa/fxfa/parser/cxfa_encryption.cpp
@@ -6,19 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_encryption.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kEncryptionPropertyData[] = {
     {XFA_Element::EncryptionLevel, 1, 0},
     {XFA_Element::Encrypt, 1, 0},
     {XFA_Element::Permissions, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kEncryptionAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"encryption";
+};
 
 }  // namespace
 
@@ -28,8 +30,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Encryption,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kEncryptionPropertyData,
+                kEncryptionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Encryption::~CXFA_Encryption() {}
+CXFA_Encryption::~CXFA_Encryption() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryption.h b/xfa/fxfa/parser/cxfa_encryption.h
index f2028ee..1bcf48f 100644
--- a/xfa/fxfa/parser/cxfa_encryption.h
+++ b/xfa/fxfa/parser/cxfa_encryption.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Encryption : public CXFA_Node {
+class CXFA_Encryption final : public CXFA_Node {
  public:
   CXFA_Encryption(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Encryption() override;
diff --git a/xfa/fxfa/parser/cxfa_encryptionlevel.cpp b/xfa/fxfa/parser/cxfa_encryptionlevel.cpp
index 099b035..b9a182f 100644
--- a/xfa/fxfa/parser/cxfa_encryptionlevel.cpp
+++ b/xfa/fxfa/parser/cxfa_encryptionlevel.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_encryptionlevel.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEncryptionLevelAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"encryptionLevel";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::EncryptionLevel,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEncryptionLevelAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_EncryptionLevel::~CXFA_EncryptionLevel() {}
+CXFA_EncryptionLevel::~CXFA_EncryptionLevel() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryptionlevel.h b/xfa/fxfa/parser/cxfa_encryptionlevel.h
index 1bb80ce..faf300b 100644
--- a/xfa/fxfa/parser/cxfa_encryptionlevel.h
+++ b/xfa/fxfa/parser/cxfa_encryptionlevel.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_EncryptionLevel : public CXFA_Node {
+class CXFA_EncryptionLevel final : public CXFA_Node {
  public:
   CXFA_EncryptionLevel(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_EncryptionLevel() override;
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethod.cpp b/xfa/fxfa/parser/cxfa_encryptionmethod.cpp
index e3402e2..4cad94c 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethod.cpp
+++ b/xfa/fxfa/parser/cxfa_encryptionmethod.cpp
@@ -6,15 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_encryptionmethod.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEncryptionMethodAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"encryptionMethod";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeC,
                 XFA_Element::EncryptionMethod,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEncryptionMethodAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_EncryptionMethod::~CXFA_EncryptionMethod() {}
+CXFA_EncryptionMethod::~CXFA_EncryptionMethod() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethod.h b/xfa/fxfa/parser/cxfa_encryptionmethod.h
index 2c86001..cae708f 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethod.h
+++ b/xfa/fxfa/parser/cxfa_encryptionmethod.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_EncryptionMethod : public CXFA_Node {
+class CXFA_EncryptionMethod final : public CXFA_Node {
  public:
   CXFA_EncryptionMethod(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_EncryptionMethod() override;
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethods.cpp b/xfa/fxfa/parser/cxfa_encryptionmethods.cpp
index e412031..806cd75 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethods.cpp
+++ b/xfa/fxfa/parser/cxfa_encryptionmethods.cpp
@@ -6,17 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_encryptionmethods.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEncryptionMethodsAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"encryptionMethods";
+};
 
 }  // namespace
 
@@ -27,8 +28,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::EncryptionMethods,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEncryptionMethodsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_EncryptionMethods::~CXFA_EncryptionMethods() {}
+CXFA_EncryptionMethods::~CXFA_EncryptionMethods() = default;
diff --git a/xfa/fxfa/parser/cxfa_encryptionmethods.h b/xfa/fxfa/parser/cxfa_encryptionmethods.h
index 9fcc0b5..825ee9f 100644
--- a/xfa/fxfa/parser/cxfa_encryptionmethods.h
+++ b/xfa/fxfa/parser/cxfa_encryptionmethods.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_EncryptionMethods : public CXFA_Node {
+class CXFA_EncryptionMethods final : public CXFA_Node {
  public:
   CXFA_EncryptionMethods(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_EncryptionMethods() override;
diff --git a/xfa/fxfa/parser/cxfa_enforce.cpp b/xfa/fxfa/parser/cxfa_enforce.cpp
index 59b6426..f8ce4a0 100644
--- a/xfa/fxfa/parser/cxfa_enforce.cpp
+++ b/xfa/fxfa/parser/cxfa_enforce.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_enforce.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEnforceAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"enforce";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Enforce,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEnforceAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Enforce::~CXFA_Enforce() {}
+CXFA_Enforce::~CXFA_Enforce() = default;
diff --git a/xfa/fxfa/parser/cxfa_enforce.h b/xfa/fxfa/parser/cxfa_enforce.h
index 3ccd8b8..ec7d745 100644
--- a/xfa/fxfa/parser/cxfa_enforce.h
+++ b/xfa/fxfa/parser/cxfa_enforce.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Enforce : public CXFA_Node {
+class CXFA_Enforce final : public CXFA_Node {
  public:
   CXFA_Enforce(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Enforce() override;
diff --git a/xfa/fxfa/parser/cxfa_equate.cpp b/xfa/fxfa/parser/cxfa_equate.cpp
index e927fbc..52a4108 100644
--- a/xfa/fxfa/parser/cxfa_equate.cpp
+++ b/xfa/fxfa/parser/cxfa_equate.cpp
@@ -6,17 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_equate.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEquateAttributeData[] = {
     {XFA_Attribute::To, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Force, XFA_AttributeType::Boolean, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::From, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"equate";
+};
 
 }  // namespace
 
@@ -26,8 +27,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Equate,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEquateAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Equate::~CXFA_Equate() {}
+CXFA_Equate::~CXFA_Equate() = default;
diff --git a/xfa/fxfa/parser/cxfa_equate.h b/xfa/fxfa/parser/cxfa_equate.h
index 0a28182..fc1f8d6 100644
--- a/xfa/fxfa/parser/cxfa_equate.h
+++ b/xfa/fxfa/parser/cxfa_equate.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Equate : public CXFA_Node {
+class CXFA_Equate final : public CXFA_Node {
  public:
   CXFA_Equate(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Equate() override;
diff --git a/xfa/fxfa/parser/cxfa_equaterange.cpp b/xfa/fxfa/parser/cxfa_equaterange.cpp
index 6f46a1f..4398c24 100644
--- a/xfa/fxfa/parser/cxfa_equaterange.cpp
+++ b/xfa/fxfa/parser/cxfa_equaterange.cpp
@@ -6,17 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_equaterange.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kEquateRangeAttributeData[] = {
     {XFA_Attribute::To, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::UnicodeRange, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::From, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"equateRange";
+};
 
 }  // namespace
 
@@ -26,8 +27,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::EquateRange,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kEquateRangeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_EquateRange::~CXFA_EquateRange() {}
+CXFA_EquateRange::~CXFA_EquateRange() = default;
diff --git a/xfa/fxfa/parser/cxfa_equaterange.h b/xfa/fxfa/parser/cxfa_equaterange.h
index 86c86b1..56f05e1 100644
--- a/xfa/fxfa/parser/cxfa_equaterange.h
+++ b/xfa/fxfa/parser/cxfa_equaterange.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_EquateRange : public CXFA_Node {
+class CXFA_EquateRange final : public CXFA_Node {
  public:
   CXFA_EquateRange(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_EquateRange() override;
diff --git a/xfa/fxfa/parser/cxfa_era.cpp b/xfa/fxfa/parser/cxfa_era.cpp
index 6051676..1cb8df2 100644
--- a/xfa/fxfa/parser/cxfa_era.cpp
+++ b/xfa/fxfa/parser/cxfa_era.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_era.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"era";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_Era::CXFA_Era(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -18,8 +15,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Era,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Era::~CXFA_Era() {}
+CXFA_Era::~CXFA_Era() = default;
diff --git a/xfa/fxfa/parser/cxfa_era.h b/xfa/fxfa/parser/cxfa_era.h
index a46a574..e0ecdf7 100644
--- a/xfa/fxfa/parser/cxfa_era.h
+++ b/xfa/fxfa/parser/cxfa_era.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Era : public CXFA_Node {
+class CXFA_Era final : public CXFA_Node {
  public:
   CXFA_Era(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Era() override;
diff --git a/xfa/fxfa/parser/cxfa_eranames.cpp b/xfa/fxfa/parser/cxfa_eranames.cpp
index 06bd2ca..391c5fa 100644
--- a/xfa/fxfa/parser/cxfa_eranames.cpp
+++ b/xfa/fxfa/parser/cxfa_eranames.cpp
@@ -6,12 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_eranames.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Era, 2, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-
-constexpr wchar_t kName[] = L"eraNames";
+const CXFA_Node::PropertyData kEraNamesPropertyData[] = {
+    {XFA_Element::Era, 2, 0},
+};
 
 }  // namespace
 
@@ -21,8 +23,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::EraNames,
-                kPropertyData,
-                nullptr,
-                kName) {}
+                kEraNamesPropertyData,
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_EraNames::~CXFA_EraNames() {}
+CXFA_EraNames::~CXFA_EraNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_eranames.h b/xfa/fxfa/parser/cxfa_eranames.h
index 4c8f788..468b658 100644
--- a/xfa/fxfa/parser/cxfa_eranames.h
+++ b/xfa/fxfa/parser/cxfa_eranames.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_EraNames : public CXFA_Node {
+class CXFA_EraNames final : public CXFA_Node {
  public:
   CXFA_EraNames(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_EraNames() override;
diff --git a/xfa/fxfa/parser/cxfa_event.cpp b/xfa/fxfa/parser/cxfa_event.cpp
index d4af2ff..0b109c1 100644
--- a/xfa/fxfa/parser/cxfa_event.cpp
+++ b/xfa/fxfa/parser/cxfa_event.cpp
@@ -6,33 +6,32 @@
 
 #include "xfa/fxfa/parser/cxfa_event.h"
 
-#include "fxjs/xfa/cjx_event.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
 #include "xfa/fxfa/parser/cxfa_submit.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kEventPropertyData[] = {
     {XFA_Element::Execute, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Script, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::SignData, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Extras, 1, 0},
     {XFA_Element::Submit, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kEventAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Listen, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::RefOnly},
+     (void*)XFA_AttributeValue::RefOnly},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Activity, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Click},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"event";
+     (void*)XFA_AttributeValue::Click},
+};
 
 }  // namespace
 
@@ -42,14 +41,13 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Event,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Event>(this)) {}
+                kEventPropertyData,
+                kEventAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Event::~CXFA_Event() {}
+CXFA_Event::~CXFA_Event() = default;
 
-XFA_AttributeEnum CXFA_Event::GetActivity() {
+XFA_AttributeValue CXFA_Event::GetActivity() {
   return JSObject()->GetEnum(XFA_Attribute::Activity);
 }
 
@@ -73,6 +71,8 @@
   return GetChild<CXFA_Script>(0, XFA_Element::Script, false);
 }
 
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
 CXFA_Submit* CXFA_Event::GetSubmitIfExists() {
   return GetChild<CXFA_Submit>(0, XFA_Element::Submit, false);
 }
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
diff --git a/xfa/fxfa/parser/cxfa_event.h b/xfa/fxfa/parser/cxfa_event.h
index a410805..73da7c2 100644
--- a/xfa/fxfa/parser/cxfa_event.h
+++ b/xfa/fxfa/parser/cxfa_event.h
@@ -12,15 +12,19 @@
 class CXFA_Script;
 class CXFA_Submit;
 
-class CXFA_Event : public CXFA_Node {
+class CXFA_Event final : public CXFA_Node {
  public:
   CXFA_Event(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Event() override;
 
-  XFA_AttributeEnum GetActivity();
+  XFA_AttributeValue GetActivity();
   XFA_Element GetEventType() const;
   CXFA_Script* GetScriptIfExists();
+
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
   CXFA_Submit* GetSubmitIfExists();
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
+
   WideString GetRef();
 };
 
diff --git a/xfa/fxfa/parser/cxfa_exclgroup.cpp b/xfa/fxfa/parser/cxfa_exclgroup.cpp
index a24dc36..f662224 100644
--- a/xfa/fxfa/parser/cxfa_exclgroup.cpp
+++ b/xfa/fxfa/parser/cxfa_exclgroup.cpp
@@ -11,44 +11,44 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kExclGroupPropertyData[] = {
     {XFA_Element::Margin, 1, 0},    {XFA_Element::Para, 1, 0},
     {XFA_Element::Border, 1, 0},    {XFA_Element::Assist, 1, 0},
     {XFA_Element::Traversal, 1, 0}, {XFA_Element::Validate, 1, 0},
     {XFA_Element::Caption, 1, 0},   {XFA_Element::Bind, 1, 0},
     {XFA_Element::Desc, 1, 0},      {XFA_Element::Calculate, 1, 0},
-    {XFA_Element::Extras, 1, 0},    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kExclGroupAttributeData[] = {
     {XFA_Attribute::H, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::W, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::X, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Y, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Left},
+     (void*)XFA_AttributeValue::Left},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Access, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Open},
+     (void*)XFA_AttributeValue::Open},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::VAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Top},
+     (void*)XFA_AttributeValue::Top},
     {XFA_Attribute::MaxH, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MaxW, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MinH, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MinW, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Layout, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Position},
+     (void*)XFA_AttributeValue::Position},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ColSpan, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AnchorType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::TopLeft},
+     (void*)XFA_AttributeValue::TopLeft},
     {XFA_Attribute::AccessKey, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"exclGroup";
+};
 
 }  // namespace
 
@@ -58,9 +58,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::ExclGroup,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kExclGroupPropertyData,
+                kExclGroupAttributeData,
                 pdfium::MakeUnique<CJX_ExclGroup>(this)) {}
 
-CXFA_ExclGroup::~CXFA_ExclGroup() {}
+CXFA_ExclGroup::~CXFA_ExclGroup() = default;
diff --git a/xfa/fxfa/parser/cxfa_exclgroup.h b/xfa/fxfa/parser/cxfa_exclgroup.h
index 5a3c8a3..390bb4a 100644
--- a/xfa/fxfa/parser/cxfa_exclgroup.h
+++ b/xfa/fxfa/parser/cxfa_exclgroup.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ExclGroup : public CXFA_Node {
+class CXFA_ExclGroup final : public CXFA_Node {
  public:
   CXFA_ExclGroup(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ExclGroup() override;
diff --git a/xfa/fxfa/parser/cxfa_exclude.cpp b/xfa/fxfa/parser/cxfa_exclude.cpp
index e07d92a..006831e 100644
--- a/xfa/fxfa/parser/cxfa_exclude.cpp
+++ b/xfa/fxfa/parser/cxfa_exclude.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_exclude.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kExcludeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"exclude";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Exclude,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kExcludeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Exclude::~CXFA_Exclude() {}
+CXFA_Exclude::~CXFA_Exclude() = default;
diff --git a/xfa/fxfa/parser/cxfa_exclude.h b/xfa/fxfa/parser/cxfa_exclude.h
index 0f78707..71f08fd 100644
--- a/xfa/fxfa/parser/cxfa_exclude.h
+++ b/xfa/fxfa/parser/cxfa_exclude.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Exclude : public CXFA_Node {
+class CXFA_Exclude final : public CXFA_Node {
  public:
   CXFA_Exclude(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Exclude() override;
diff --git a/xfa/fxfa/parser/cxfa_excludens.cpp b/xfa/fxfa/parser/cxfa_excludens.cpp
index b149e86..0d47359 100644
--- a/xfa/fxfa/parser/cxfa_excludens.cpp
+++ b/xfa/fxfa/parser/cxfa_excludens.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_excludens.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kExcludeNSAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"excludeNS";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::ExcludeNS,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kExcludeNSAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ExcludeNS::~CXFA_ExcludeNS() {}
+CXFA_ExcludeNS::~CXFA_ExcludeNS() = default;
diff --git a/xfa/fxfa/parser/cxfa_excludens.h b/xfa/fxfa/parser/cxfa_excludens.h
index c5cb548..9ce2e69 100644
--- a/xfa/fxfa/parser/cxfa_excludens.h
+++ b/xfa/fxfa/parser/cxfa_excludens.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ExcludeNS : public CXFA_Node {
+class CXFA_ExcludeNS final : public CXFA_Node {
  public:
   CXFA_ExcludeNS(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ExcludeNS() override;
diff --git a/xfa/fxfa/parser/cxfa_exdata.cpp b/xfa/fxfa/parser/cxfa_exdata.cpp
index cf57796..1c2e856 100644
--- a/xfa/fxfa/parser/cxfa_exdata.cpp
+++ b/xfa/fxfa/parser/cxfa_exdata.cpp
@@ -6,25 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_exdata.h"
 
-#include "fxjs/xfa/cjx_exdata.h"
+#include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kExDataAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Rid, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ContentType, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TransferEncoding, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::MaxLength, XFA_AttributeType::Integer, (void*)-1},
     {XFA_Attribute::Href, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"exData";
+};
 
 }  // namespace
 
@@ -34,12 +32,11 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ExData,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_ExData>(this)) {}
+                {},
+                kExDataAttributeData,
+                pdfium::MakeUnique<CJX_Object>(this)) {}
 
-CXFA_ExData::~CXFA_ExData() {}
+CXFA_ExData::~CXFA_ExData() = default;
 
 void CXFA_ExData::SetContentType(const WideString& wsContentType) {
   JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false, false);
diff --git a/xfa/fxfa/parser/cxfa_exdata.h b/xfa/fxfa/parser/cxfa_exdata.h
index 8eb3caf..8adf028 100644
--- a/xfa/fxfa/parser/cxfa_exdata.h
+++ b/xfa/fxfa/parser/cxfa_exdata.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ExData : public CXFA_Node {
+class CXFA_ExData final : public CXFA_Node {
  public:
   CXFA_ExData(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ExData() override;
diff --git a/xfa/fxfa/parser/cxfa_execute.cpp b/xfa/fxfa/parser/cxfa_execute.cpp
index 4196e8f..c89bd70 100644
--- a/xfa/fxfa/parser/cxfa_execute.cpp
+++ b/xfa/fxfa/parser/cxfa_execute.cpp
@@ -6,23 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_execute.h"
 
-#include "fxjs/xfa/cjx_execute.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kExecuteAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Connection, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::RunAt, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Client},
+     (void*)XFA_AttributeValue::Client},
     {XFA_Attribute::ExecuteType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Import},
+     (void*)XFA_AttributeValue::Import},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"execute";
+};
 
 }  // namespace
 
@@ -32,9 +30,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Execute,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Execute>(this)) {}
+                {},
+                kExecuteAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Execute::~CXFA_Execute() {}
+CXFA_Execute::~CXFA_Execute() = default;
diff --git a/xfa/fxfa/parser/cxfa_execute.h b/xfa/fxfa/parser/cxfa_execute.h
index 2d9f158..63fd18b 100644
--- a/xfa/fxfa/parser/cxfa_execute.h
+++ b/xfa/fxfa/parser/cxfa_execute.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Execute : public CXFA_Node {
+class CXFA_Execute final : public CXFA_Node {
  public:
   CXFA_Execute(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Execute() override;
diff --git a/xfa/fxfa/parser/cxfa_exobject.cpp b/xfa/fxfa/parser/cxfa_exobject.cpp
index fff1d2e..86a7409 100644
--- a/xfa/fxfa/parser/cxfa_exobject.cpp
+++ b/xfa/fxfa/parser/cxfa_exobject.cpp
@@ -6,14 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_exobject.h"
 
-#include "fxjs/xfa/cjx_exobject.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kExObjectPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kExObjectAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
@@ -22,9 +24,7 @@
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::CodeBase, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ClassId, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"exObject";
+};
 
 }  // namespace
 
@@ -34,9 +34,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::ExObject,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_ExObject>(this)) {}
+                kExObjectPropertyData,
+                kExObjectAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ExObject::~CXFA_ExObject() {}
+CXFA_ExObject::~CXFA_ExObject() = default;
diff --git a/xfa/fxfa/parser/cxfa_exobject.h b/xfa/fxfa/parser/cxfa_exobject.h
index b4d7de9..79ffdb9 100644
--- a/xfa/fxfa/parser/cxfa_exobject.h
+++ b/xfa/fxfa/parser/cxfa_exobject.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ExObject : public CXFA_Node {
+class CXFA_ExObject final : public CXFA_Node {
  public:
   CXFA_ExObject(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ExObject() override;
diff --git a/xfa/fxfa/parser/cxfa_extras.cpp b/xfa/fxfa/parser/cxfa_extras.cpp
index 4609e6f..b23a689 100644
--- a/xfa/fxfa/parser/cxfa_extras.cpp
+++ b/xfa/fxfa/parser/cxfa_extras.cpp
@@ -11,14 +11,12 @@
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kExtrasAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"extras";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                  XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Extras,
-                nullptr,
-                kAttributeData,
-                kName,
+                {},
+                kExtrasAttributeData,
                 pdfium::MakeUnique<CJX_Extras>(this)) {}
 
-CXFA_Extras::~CXFA_Extras() {}
+CXFA_Extras::~CXFA_Extras() = default;
diff --git a/xfa/fxfa/parser/cxfa_extras.h b/xfa/fxfa/parser/cxfa_extras.h
index 3b3c6b4..82108d0 100644
--- a/xfa/fxfa/parser/cxfa_extras.h
+++ b/xfa/fxfa/parser/cxfa_extras.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Extras : public CXFA_Node {
+class CXFA_Extras final : public CXFA_Node {
  public:
   CXFA_Extras(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Extras() override;
diff --git a/xfa/fxfa/parser/cxfa_field.cpp b/xfa/fxfa/parser/cxfa_field.cpp
index 56b8d59..e33dfa4 100644
--- a/xfa/fxfa/parser/cxfa_field.cpp
+++ b/xfa/fxfa/parser/cxfa_field.cpp
@@ -11,7 +11,7 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kFieldPropertyData[] = {
     {XFA_Element::Ui, 1, 0},        {XFA_Element::Margin, 1, 0},
     {XFA_Element::Para, 1, 0},      {XFA_Element::Format, 1, 0},
     {XFA_Element::Border, 1, 0},    {XFA_Element::Assist, 1, 0},
@@ -20,24 +20,26 @@
     {XFA_Element::Bind, 1, 0},      {XFA_Element::Desc, 1, 0},
     {XFA_Element::Font, 1, 0},      {XFA_Element::Value, 1, 0},
     {XFA_Element::Calculate, 1, 0}, {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Items, 2, 0},     {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::Items, 2, 0},
+};
+
+const CXFA_Node::AttributeData kFieldAttributeData[] = {
     {XFA_Attribute::H, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::W, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::X, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Y, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Left},
+     (void*)XFA_AttributeValue::Left},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Access, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Open},
+     (void*)XFA_AttributeValue::Open},
     {XFA_Attribute::Rotate, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::VAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Top},
+     (void*)XFA_AttributeValue::Top},
     {XFA_Attribute::MaxH, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MaxW, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MinH, XFA_AttributeType::Measure, (void*)L"0in"},
@@ -47,11 +49,9 @@
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Locale, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AnchorType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::TopLeft},
+     (void*)XFA_AttributeValue::TopLeft},
     {XFA_Attribute::AccessKey, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"field";
+};
 
 }  // namespace
 
@@ -61,9 +61,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Field,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kFieldPropertyData,
+                kFieldAttributeData,
                 pdfium::MakeUnique<CJX_Field>(this)) {}
 
-CXFA_Field::~CXFA_Field() {}
+CXFA_Field::~CXFA_Field() = default;
diff --git a/xfa/fxfa/parser/cxfa_field.h b/xfa/fxfa/parser/cxfa_field.h
index b2f7a05..e29cdd7 100644
--- a/xfa/fxfa/parser/cxfa_field.h
+++ b/xfa/fxfa/parser/cxfa_field.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Field : public CXFA_Node {
+class CXFA_Field final : public CXFA_Node {
  public:
   CXFA_Field(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Field() override;
diff --git a/xfa/fxfa/parser/cxfa_fill.cpp b/xfa/fxfa/parser/cxfa_fill.cpp
index 4cbd8f8..5bc8b02 100644
--- a/xfa/fxfa/parser/cxfa_fill.cpp
+++ b/xfa/fxfa/parser/cxfa_fill.cpp
@@ -6,7 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_fill.h"
 
-#include "fxjs/xfa/cjx_fill.h"
+#include "core/fxge/render_defines.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
 #include "xfa/fxfa/parser/cxfa_linear.h"
@@ -17,7 +18,7 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kFillPropertyData[] = {
     {XFA_Element::Pattern, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Solid, 1,
      XFA_PROPERTYFLAG_OneOf | XFA_PROPERTYFLAG_DefaultOneOf},
@@ -26,16 +27,15 @@
     {XFA_Element::Linear, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Extras, 1, 0},
     {XFA_Element::Radial, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kFillAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"fill";
+};
 
 }  // namespace
 
@@ -45,18 +45,17 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Fill,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Fill>(this)) {}
+                kFillPropertyData,
+                kFillAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Fill::~CXFA_Fill() {}
+CXFA_Fill::~CXFA_Fill() = default;
 
 bool CXFA_Fill::IsVisible() {
   return JSObject()
              ->TryEnum(XFA_Attribute::Presence, true)
-             .value_or(XFA_AttributeEnum::Visible) ==
-         XFA_AttributeEnum::Visible;
+             .value_or(XFA_AttributeValue::Visible) ==
+         XFA_AttributeValue::Visible;
 }
 
 void CXFA_Fill::SetColor(FX_ARGB color) {
diff --git a/xfa/fxfa/parser/cxfa_fill.h b/xfa/fxfa/parser/cxfa_fill.h
index 1ffdcdf..05fede9 100644
--- a/xfa/fxfa/parser/cxfa_fill.h
+++ b/xfa/fxfa/parser/cxfa_fill.h
@@ -18,7 +18,7 @@
 class CXFA_Radial;
 class CXFA_Stipple;
 
-class CXFA_Fill : public CXFA_Node {
+class CXFA_Fill final : public CXFA_Node {
  public:
   CXFA_Fill(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Fill() override;
diff --git a/xfa/fxfa/parser/cxfa_filter.cpp b/xfa/fxfa/parser/cxfa_filter.cpp
index ec6522d..fdd2960 100644
--- a/xfa/fxfa/parser/cxfa_filter.cpp
+++ b/xfa/fxfa/parser/cxfa_filter.cpp
@@ -6,27 +6,27 @@
 
 #include "xfa/fxfa/parser/cxfa_filter.h"
 
-#include "fxjs/xfa/cjx_filter.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kFilterPropertyData[] = {
     {XFA_Element::Mdp, 1, 0},           {XFA_Element::Certificates, 1, 0},
     {XFA_Element::TimeStamp, 1, 0},     {XFA_Element::Handler, 1, 0},
     {XFA_Element::DigestMethods, 1, 0}, {XFA_Element::Encodings, 1, 0},
     {XFA_Element::Reasons, 1, 0},       {XFA_Element::AppearanceFilter, 1, 0},
-    {XFA_Element::LockDocument, 1, 0},  {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::LockDocument, 1, 0},
+};
+
+const CXFA_Node::AttributeData kFilterAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Version, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AddRevocationInfo, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"filter";
+};
 
 }  // namespace
 
@@ -36,9 +36,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Filter,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Filter>(this)) {}
+                kFilterPropertyData,
+                kFilterAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Filter::~CXFA_Filter() {}
+CXFA_Filter::~CXFA_Filter() = default;
diff --git a/xfa/fxfa/parser/cxfa_filter.h b/xfa/fxfa/parser/cxfa_filter.h
index 2ac069a..033d97c 100644
--- a/xfa/fxfa/parser/cxfa_filter.h
+++ b/xfa/fxfa/parser/cxfa_filter.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Filter : public CXFA_Node {
+class CXFA_Filter final : public CXFA_Node {
  public:
   CXFA_Filter(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Filter() override;
diff --git a/xfa/fxfa/parser/cxfa_fliplabel.cpp b/xfa/fxfa/parser/cxfa_fliplabel.cpp
index 2227e8b..8780e87 100644
--- a/xfa/fxfa/parser/cxfa_fliplabel.cpp
+++ b/xfa/fxfa/parser/cxfa_fliplabel.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_fliplabel.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kFlipLabelAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"flipLabel";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::FlipLabel,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kFlipLabelAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_FlipLabel::~CXFA_FlipLabel() {}
+CXFA_FlipLabel::~CXFA_FlipLabel() = default;
diff --git a/xfa/fxfa/parser/cxfa_fliplabel.h b/xfa/fxfa/parser/cxfa_fliplabel.h
index dac0564..e3f8165 100644
--- a/xfa/fxfa/parser/cxfa_fliplabel.h
+++ b/xfa/fxfa/parser/cxfa_fliplabel.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_FlipLabel : public CXFA_Node {
+class CXFA_FlipLabel final : public CXFA_Node {
  public:
   CXFA_FlipLabel(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_FlipLabel() override;
diff --git a/xfa/fxfa/parser/cxfa_float.cpp b/xfa/fxfa/parser/cxfa_float.cpp
index 83e5749..a17341f 100644
--- a/xfa/fxfa/parser/cxfa_float.cpp
+++ b/xfa/fxfa/parser/cxfa_float.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_float.h"
 
-#include "fxjs/xfa/cjx_float.h"
+#include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kFloatAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"float";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Float,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Float>(this)) {}
+                {},
+                kFloatAttributeData,
+                pdfium::MakeUnique<CJX_Object>(this)) {}
 
-CXFA_Float::~CXFA_Float() {}
+CXFA_Float::~CXFA_Float() = default;
diff --git a/xfa/fxfa/parser/cxfa_float.h b/xfa/fxfa/parser/cxfa_float.h
index f93b3dd..a3fc1ae 100644
--- a/xfa/fxfa/parser/cxfa_float.h
+++ b/xfa/fxfa/parser/cxfa_float.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Float : public CXFA_Node {
+class CXFA_Float final : public CXFA_Node {
  public:
   CXFA_Float(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Float() override;
diff --git a/xfa/fxfa/parser/cxfa_font.cpp b/xfa/fxfa/parser/cxfa_font.cpp
index 694cb26..70e8426 100644
--- a/xfa/fxfa/parser/cxfa_font.cpp
+++ b/xfa/fxfa/parser/cxfa_font.cpp
@@ -6,17 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_font.h"
 
-#include "fxjs/xfa/cjx_font.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_fill.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Fill, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kFontPropertyData[] = {
+    {XFA_Element::Fill, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kFontAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::LineThrough, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::Typeface, XFA_AttributeType::CData, (void*)L"Courier"},
@@ -24,31 +26,29 @@
      (void*)L"100%"},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::KerningMode, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::Underline, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::BaselineShift, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::OverlinePeriod, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::All},
+     (void*)XFA_AttributeValue::All},
     {XFA_Attribute::LetterSpacing, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::LineThroughPeriod, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::All},
+     (void*)XFA_AttributeValue::All},
     {XFA_Attribute::FontVerticalScale, XFA_AttributeType::CData,
      (void*)L"100%"},
     {XFA_Attribute::PsName, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Size, XFA_AttributeType::Measure, (void*)L"10pt"},
     {XFA_Attribute::Posture, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Normal},
+     (void*)XFA_AttributeValue::Normal},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Weight, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Normal},
+     (void*)XFA_AttributeValue::Normal},
     {XFA_Attribute::UnderlinePeriod, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::All},
+     (void*)XFA_AttributeValue::All},
     {XFA_Attribute::Overline, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::GenericFamily, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Serif},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"font";
+     (void*)XFA_AttributeValue::Serif},
+};
 
 }  // namespace
 
@@ -59,17 +59,15 @@
           (XFA_XDPPACKET_Template | XFA_XDPPACKET_Config | XFA_XDPPACKET_Form),
           XFA_ObjectType::Node,
           XFA_Element::Font,
-          kPropertyData,
-          kAttributeData,
-          kName,
-          pdfium::MakeUnique<CJX_Font>(this)) {}
+          kFontPropertyData,
+          kFontAttributeData,
+          pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Font::~CXFA_Font() {}
+CXFA_Font::~CXFA_Font() = default;
 
 float CXFA_Font::GetBaselineShift() const {
-  return JSObject()
-      ->GetMeasure(XFA_Attribute::BaselineShift)
-      .ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::BaselineShift,
+                                      XFA_Unit::Pt);
 }
 
 float CXFA_Font::GetHorizontalScale() {
@@ -100,14 +98,14 @@
   return JSObject()->GetInteger(XFA_Attribute::Underline);
 }
 
-XFA_AttributeEnum CXFA_Font::GetUnderlinePeriod() {
+XFA_AttributeValue CXFA_Font::GetUnderlinePeriod() {
   return JSObject()
       ->TryEnum(XFA_Attribute::UnderlinePeriod, true)
-      .value_or(XFA_AttributeEnum::All);
+      .value_or(XFA_AttributeValue::All);
 }
 
 float CXFA_Font::GetFontSize() const {
-  return JSObject()->GetMeasure(XFA_Attribute::Size).ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::Size, XFA_Unit::Pt);
 }
 
 WideString CXFA_Font::GetTypeface() {
@@ -115,12 +113,12 @@
 }
 
 bool CXFA_Font::IsBold() {
-  return JSObject()->GetEnum(XFA_Attribute::Weight) == XFA_AttributeEnum::Bold;
+  return JSObject()->GetEnum(XFA_Attribute::Weight) == XFA_AttributeValue::Bold;
 }
 
 bool CXFA_Font::IsItalic() {
   return JSObject()->GetEnum(XFA_Attribute::Posture) ==
-         XFA_AttributeEnum::Italic;
+         XFA_AttributeValue::Italic;
 }
 
 void CXFA_Font::SetColor(FX_ARGB color) {
diff --git a/xfa/fxfa/parser/cxfa_font.h b/xfa/fxfa/parser/cxfa_font.h
index 346be76..e292ed5 100644
--- a/xfa/fxfa/parser/cxfa_font.h
+++ b/xfa/fxfa/parser/cxfa_font.h
@@ -10,7 +10,7 @@
 #include "core/fxge/fx_dib.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Font : public CXFA_Node {
+class CXFA_Font final : public CXFA_Node {
  public:
   CXFA_Font(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Font() override;
@@ -21,7 +21,7 @@
   float GetLetterSpacing();
   int32_t GetLineThrough();
   int32_t GetUnderline();
-  XFA_AttributeEnum GetUnderlinePeriod();
+  XFA_AttributeValue GetUnderlinePeriod();
   float GetFontSize() const;
   WideString GetTypeface();
 
diff --git a/xfa/fxfa/parser/cxfa_fontinfo.cpp b/xfa/fxfa/parser/cxfa_fontinfo.cpp
index ee1dc6e..8ec5e93 100644
--- a/xfa/fxfa/parser/cxfa_fontinfo.cpp
+++ b/xfa/fxfa/parser/cxfa_fontinfo.cpp
@@ -6,19 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_fontinfo.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kFontInfoPropertyData[] = {
     {XFA_Element::SubsetBelow, 1, 0},
     {XFA_Element::Map, 1, 0},
     {XFA_Element::Embed, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kFontInfoAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"fontInfo";
+};
 
 }  // namespace
 
@@ -28,8 +30,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::FontInfo,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kFontInfoPropertyData,
+                kFontInfoAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_FontInfo::~CXFA_FontInfo() {}
+CXFA_FontInfo::~CXFA_FontInfo() = default;
diff --git a/xfa/fxfa/parser/cxfa_fontinfo.h b/xfa/fxfa/parser/cxfa_fontinfo.h
index f9facd8..b798c55 100644
--- a/xfa/fxfa/parser/cxfa_fontinfo.h
+++ b/xfa/fxfa/parser/cxfa_fontinfo.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_FontInfo : public CXFA_Node {
+class CXFA_FontInfo final : public CXFA_Node {
  public:
   CXFA_FontInfo(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_FontInfo() override;
diff --git a/xfa/fxfa/parser/cxfa_form.cpp b/xfa/fxfa/parser/cxfa_form.cpp
index 38ce724..4586a29 100644
--- a/xfa/fxfa/parser/cxfa_form.cpp
+++ b/xfa/fxfa/parser/cxfa_form.cpp
@@ -11,11 +11,9 @@
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kFormAttributeData[] = {
     {XFA_Attribute::Checksum, XFA_AttributeType::CData, (void*)nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"form";
+};
 
 }  // namespace
 
@@ -25,9 +23,8 @@
                 XFA_XDPPACKET_Form,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Form,
-                nullptr,
-                kAttributeData,
-                kName,
+                {},
+                kFormAttributeData,
                 pdfium::MakeUnique<CJX_Form>(this)) {}
 
-CXFA_Form::~CXFA_Form() {}
+CXFA_Form::~CXFA_Form() = default;
diff --git a/xfa/fxfa/parser/cxfa_form.h b/xfa/fxfa/parser/cxfa_form.h
index 39d1d90..a0e2f32 100644
--- a/xfa/fxfa/parser/cxfa_form.h
+++ b/xfa/fxfa/parser/cxfa_form.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Form : public CXFA_Node {
+class CXFA_Form final : public CXFA_Node {
  public:
   CXFA_Form(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Form() override;
diff --git a/xfa/fxfa/parser/cxfa_format.cpp b/xfa/fxfa/parser/cxfa_format.cpp
index ebaaeb0..9e84a6f 100644
--- a/xfa/fxfa/parser/cxfa_format.cpp
+++ b/xfa/fxfa/parser/cxfa_format.cpp
@@ -6,21 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_format.h"
 
-#include "fxjs/xfa/cjx_format.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Picture, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kFormatPropertyData[] = {
+    {XFA_Element::Picture, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kFormatAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"format";
+};
 
 }  // namespace
 
@@ -30,9 +30,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Format,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Format>(this)) {}
+                kFormatPropertyData,
+                kFormatAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Format::~CXFA_Format() {}
+CXFA_Format::~CXFA_Format() = default;
diff --git a/xfa/fxfa/parser/cxfa_format.h b/xfa/fxfa/parser/cxfa_format.h
index 3e31716..7e74a1c 100644
--- a/xfa/fxfa/parser/cxfa_format.h
+++ b/xfa/fxfa/parser/cxfa_format.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Format : public CXFA_Node {
+class CXFA_Format final : public CXFA_Node {
  public:
   CXFA_Format(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Format() override;
diff --git a/xfa/fxfa/parser/cxfa_formfieldfilling.cpp b/xfa/fxfa/parser/cxfa_formfieldfilling.cpp
index 887e5cc..6dc5af4 100644
--- a/xfa/fxfa/parser/cxfa_formfieldfilling.cpp
+++ b/xfa/fxfa/parser/cxfa_formfieldfilling.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_formfieldfilling.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kFormFieldFillingAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"formFieldFilling";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::FormFieldFilling,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kFormFieldFillingAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_FormFieldFilling::~CXFA_FormFieldFilling() {}
+CXFA_FormFieldFilling::~CXFA_FormFieldFilling() = default;
diff --git a/xfa/fxfa/parser/cxfa_formfieldfilling.h b/xfa/fxfa/parser/cxfa_formfieldfilling.h
index 77f1b8f..f9e7447 100644
--- a/xfa/fxfa/parser/cxfa_formfieldfilling.h
+++ b/xfa/fxfa/parser/cxfa_formfieldfilling.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_FormFieldFilling : public CXFA_Node {
+class CXFA_FormFieldFilling final : public CXFA_Node {
  public:
   CXFA_FormFieldFilling(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_FormFieldFilling() override;
diff --git a/xfa/fxfa/parser/cxfa_groupparent.cpp b/xfa/fxfa/parser/cxfa_groupparent.cpp
index ca92d3e..8dd780c 100644
--- a/xfa/fxfa/parser/cxfa_groupparent.cpp
+++ b/xfa/fxfa/parser/cxfa_groupparent.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_groupparent.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kGroupParentAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"groupParent";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::GroupParent,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kGroupParentAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_GroupParent::~CXFA_GroupParent() {}
+CXFA_GroupParent::~CXFA_GroupParent() = default;
diff --git a/xfa/fxfa/parser/cxfa_groupparent.h b/xfa/fxfa/parser/cxfa_groupparent.h
index 7de296c..eb63a6a 100644
--- a/xfa/fxfa/parser/cxfa_groupparent.h
+++ b/xfa/fxfa/parser/cxfa_groupparent.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_GroupParent : public CXFA_Node {
+class CXFA_GroupParent final : public CXFA_Node {
  public:
   CXFA_GroupParent(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_GroupParent() override;
diff --git a/xfa/fxfa/parser/cxfa_handler.cpp b/xfa/fxfa/parser/cxfa_handler.cpp
index 80f0f81..88c6ac8 100644
--- a/xfa/fxfa/parser/cxfa_handler.cpp
+++ b/xfa/fxfa/parser/cxfa_handler.cpp
@@ -11,15 +11,13 @@
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kHandlerAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"handler";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::TextNode,
                 XFA_Element::Handler,
-                nullptr,
-                kAttributeData,
-                kName,
+                {},
+                kHandlerAttributeData,
                 pdfium::MakeUnique<CJX_Handler>(this)) {}
 
-CXFA_Handler::~CXFA_Handler() {}
+CXFA_Handler::~CXFA_Handler() = default;
diff --git a/xfa/fxfa/parser/cxfa_handler.h b/xfa/fxfa/parser/cxfa_handler.h
index e575c0b..1ab1c4d 100644
--- a/xfa/fxfa/parser/cxfa_handler.h
+++ b/xfa/fxfa/parser/cxfa_handler.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Handler : public CXFA_Node {
+class CXFA_Handler final : public CXFA_Node {
  public:
   CXFA_Handler(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Handler() override;
diff --git a/xfa/fxfa/parser/cxfa_hyphenation.cpp b/xfa/fxfa/parser/cxfa_hyphenation.cpp
index 0e835ed..41d77f4 100644
--- a/xfa/fxfa/parser/cxfa_hyphenation.cpp
+++ b/xfa/fxfa/parser/cxfa_hyphenation.cpp
@@ -6,9 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_hyphenation.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kHyphenationAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::WordCharacterCount, XFA_AttributeType::Integer, (void*)7},
@@ -18,9 +21,7 @@
     {XFA_Attribute::RemainCharacterCount, XFA_AttributeType::Integer, (void*)3},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ExcludeAllCaps, XFA_AttributeType::Boolean, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"hyphenation";
+};
 
 }  // namespace
 
@@ -30,8 +31,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Hyphenation,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kHyphenationAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Hyphenation::~CXFA_Hyphenation() {}
+CXFA_Hyphenation::~CXFA_Hyphenation() = default;
diff --git a/xfa/fxfa/parser/cxfa_hyphenation.h b/xfa/fxfa/parser/cxfa_hyphenation.h
index 2601c5f..77b49e9 100644
--- a/xfa/fxfa/parser/cxfa_hyphenation.h
+++ b/xfa/fxfa/parser/cxfa_hyphenation.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Hyphenation : public CXFA_Node {
+class CXFA_Hyphenation final : public CXFA_Node {
  public:
   CXFA_Hyphenation(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Hyphenation() override;
diff --git a/xfa/fxfa/parser/cxfa_ifempty.cpp b/xfa/fxfa/parser/cxfa_ifempty.cpp
index ae612ef..dc50a19 100644
--- a/xfa/fxfa/parser/cxfa_ifempty.cpp
+++ b/xfa/fxfa/parser/cxfa_ifempty.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_ifempty.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kIfEmptyAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"ifEmpty";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::IfEmpty,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kIfEmptyAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_IfEmpty::~CXFA_IfEmpty() {}
+CXFA_IfEmpty::~CXFA_IfEmpty() = default;
diff --git a/xfa/fxfa/parser/cxfa_ifempty.h b/xfa/fxfa/parser/cxfa_ifempty.h
index 3c3c1b4..401faa7 100644
--- a/xfa/fxfa/parser/cxfa_ifempty.h
+++ b/xfa/fxfa/parser/cxfa_ifempty.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_IfEmpty : public CXFA_Node {
+class CXFA_IfEmpty final : public CXFA_Node {
  public:
   CXFA_IfEmpty(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_IfEmpty() override;
diff --git a/xfa/fxfa/parser/cxfa_image.cpp b/xfa/fxfa/parser/cxfa_image.cpp
index 88ca977..fe84b11 100644
--- a/xfa/fxfa/parser/cxfa_image.cpp
+++ b/xfa/fxfa/parser/cxfa_image.cpp
@@ -6,25 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_image.h"
 
-#include "fxjs/xfa/cjx_image.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kImageAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ContentType, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TransferEncoding, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Base64},
+     (void*)XFA_AttributeValue::Base64},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Aspect, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Fit},
+     (void*)XFA_AttributeValue::Fit},
     {XFA_Attribute::Href, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"image";
+};
 
 }  // namespace
 
@@ -34,13 +32,13 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Image,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kImageAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Image::~CXFA_Image() {}
+CXFA_Image::~CXFA_Image() = default;
 
-XFA_AttributeEnum CXFA_Image::GetAspect() {
+XFA_AttributeValue CXFA_Image::GetAspect() {
   return JSObject()->GetEnum(XFA_Attribute::Aspect);
 }
 
@@ -52,8 +50,8 @@
   return JSObject()->TryCData(XFA_Attribute::Href, true).value_or(L"");
 }
 
-XFA_AttributeEnum CXFA_Image::GetTransferEncoding() {
-  return static_cast<XFA_AttributeEnum>(
+XFA_AttributeValue CXFA_Image::GetTransferEncoding() {
+  return static_cast<XFA_AttributeValue>(
       JSObject()->GetEnum(XFA_Attribute::TransferEncoding));
 }
 
@@ -69,7 +67,7 @@
   JSObject()->SetCData(XFA_Attribute::Href, wsHref, false, false);
 }
 
-void CXFA_Image::SetTransferEncoding(XFA_AttributeEnum iTransferEncoding) {
+void CXFA_Image::SetTransferEncoding(XFA_AttributeValue iTransferEncoding) {
   JSObject()->SetEnum(XFA_Attribute::TransferEncoding, iTransferEncoding,
                       false);
 }
diff --git a/xfa/fxfa/parser/cxfa_image.h b/xfa/fxfa/parser/cxfa_image.h
index 3670863..62bb93a 100644
--- a/xfa/fxfa/parser/cxfa_image.h
+++ b/xfa/fxfa/parser/cxfa_image.h
@@ -9,19 +9,19 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Image : public CXFA_Node {
+class CXFA_Image final : public CXFA_Node {
  public:
   CXFA_Image(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Image() override;
 
-  XFA_AttributeEnum GetAspect();
+  XFA_AttributeValue GetAspect();
   WideString GetContent();
 
   WideString GetHref();
   void SetHref(const WideString& wsHref);
 
-  XFA_AttributeEnum GetTransferEncoding();
-  void SetTransferEncoding(XFA_AttributeEnum iTransferEncoding);
+  XFA_AttributeValue GetTransferEncoding();
+  void SetTransferEncoding(XFA_AttributeValue iTransferEncoding);
 
   WideString GetContentType();
   void SetContentType(const WideString& wsContentType);
diff --git a/xfa/fxfa/parser/cxfa_imageedit.cpp b/xfa/fxfa/parser/cxfa_imageedit.cpp
index 6f21085..5a6447e 100644
--- a/xfa/fxfa/parser/cxfa_imageedit.cpp
+++ b/xfa/fxfa/parser/cxfa_imageedit.cpp
@@ -6,24 +6,24 @@
 
 #include "xfa/fxfa/parser/cxfa_imageedit.h"
 
-#include "fxjs/xfa/cjx_imageedit.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Margin, 1, 0},
-                                                 {XFA_Element::Border, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kImageEditPropertyData[] = {
+    {XFA_Element::Margin, 1, 0},
+    {XFA_Element::Border, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kImageEditAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Data, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Link},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"imageEdit";
+     (void*)XFA_AttributeValue::Link},
+};
 
 }  // namespace
 
@@ -33,9 +33,16 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::ImageEdit,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_ImageEdit>(this)) {}
+                kImageEditPropertyData,
+                kImageEditAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ImageEdit::~CXFA_ImageEdit() {}
+CXFA_ImageEdit::~CXFA_ImageEdit() = default;
+
+XFA_Element CXFA_ImageEdit::GetValueNodeType() const {
+  return XFA_Element::Image;
+}
+
+XFA_FFWidgetType CXFA_ImageEdit::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kImageEdit;
+}
diff --git a/xfa/fxfa/parser/cxfa_imageedit.h b/xfa/fxfa/parser/cxfa_imageedit.h
index 379750c..884c219 100644
--- a/xfa/fxfa/parser/cxfa_imageedit.h
+++ b/xfa/fxfa/parser/cxfa_imageedit.h
@@ -9,10 +9,13 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ImageEdit : public CXFA_Node {
+class CXFA_ImageEdit final : public CXFA_Node {
  public:
   CXFA_ImageEdit(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ImageEdit() override;
+
+  XFA_Element GetValueNodeType() const override;
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_IMAGEEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_includexdpcontent.cpp b/xfa/fxfa/parser/cxfa_includexdpcontent.cpp
index dc887d3..fb9f358 100644
--- a/xfa/fxfa/parser/cxfa_includexdpcontent.cpp
+++ b/xfa/fxfa/parser/cxfa_includexdpcontent.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_includexdpcontent.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kIncludeXDPContentAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"includeXDPContent";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::IncludeXDPContent,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kIncludeXDPContentAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_IncludeXDPContent::~CXFA_IncludeXDPContent() {}
+CXFA_IncludeXDPContent::~CXFA_IncludeXDPContent() = default;
diff --git a/xfa/fxfa/parser/cxfa_includexdpcontent.h b/xfa/fxfa/parser/cxfa_includexdpcontent.h
index 309a7c6..15c5bbe 100644
--- a/xfa/fxfa/parser/cxfa_includexdpcontent.h
+++ b/xfa/fxfa/parser/cxfa_includexdpcontent.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_IncludeXDPContent : public CXFA_Node {
+class CXFA_IncludeXDPContent final : public CXFA_Node {
  public:
   CXFA_IncludeXDPContent(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_IncludeXDPContent() override;
diff --git a/xfa/fxfa/parser/cxfa_incrementalload.cpp b/xfa/fxfa/parser/cxfa_incrementalload.cpp
index 43ba0cb..cedf559 100644
--- a/xfa/fxfa/parser/cxfa_incrementalload.cpp
+++ b/xfa/fxfa/parser/cxfa_incrementalload.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_incrementalload.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kIncrementalLoadAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"incrementalLoad";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::IncrementalLoad,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kIncrementalLoadAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_IncrementalLoad::~CXFA_IncrementalLoad() {}
+CXFA_IncrementalLoad::~CXFA_IncrementalLoad() = default;
diff --git a/xfa/fxfa/parser/cxfa_incrementalload.h b/xfa/fxfa/parser/cxfa_incrementalload.h
index 9ea8f29..253a2ae 100644
--- a/xfa/fxfa/parser/cxfa_incrementalload.h
+++ b/xfa/fxfa/parser/cxfa_incrementalload.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_IncrementalLoad : public CXFA_Node {
+class CXFA_IncrementalLoad final : public CXFA_Node {
  public:
   CXFA_IncrementalLoad(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_IncrementalLoad() override;
diff --git a/xfa/fxfa/parser/cxfa_incrementalmerge.cpp b/xfa/fxfa/parser/cxfa_incrementalmerge.cpp
index 659dd21..faf1148 100644
--- a/xfa/fxfa/parser/cxfa_incrementalmerge.cpp
+++ b/xfa/fxfa/parser/cxfa_incrementalmerge.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_incrementalmerge.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kIncrementalMergeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"incrementalMerge";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::IncrementalMerge,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kIncrementalMergeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_IncrementalMerge::~CXFA_IncrementalMerge() {}
+CXFA_IncrementalMerge::~CXFA_IncrementalMerge() = default;
diff --git a/xfa/fxfa/parser/cxfa_incrementalmerge.h b/xfa/fxfa/parser/cxfa_incrementalmerge.h
index c8e6554..4ec65af 100644
--- a/xfa/fxfa/parser/cxfa_incrementalmerge.h
+++ b/xfa/fxfa/parser/cxfa_incrementalmerge.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_IncrementalMerge : public CXFA_Node {
+class CXFA_IncrementalMerge final : public CXFA_Node {
  public:
   CXFA_IncrementalMerge(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_IncrementalMerge() override;
diff --git a/xfa/fxfa/parser/cxfa_insert.cpp b/xfa/fxfa/parser/cxfa_insert.cpp
index 998a7f8..ff98c51 100644
--- a/xfa/fxfa/parser/cxfa_insert.cpp
+++ b/xfa/fxfa/parser/cxfa_insert.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_insert.h"
 
-#include "fxjs/xfa/cjx_insert.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kInsertAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"insert";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Insert,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Insert>(this)) {}
+                {},
+                kInsertAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Insert::~CXFA_Insert() {}
+CXFA_Insert::~CXFA_Insert() = default;
diff --git a/xfa/fxfa/parser/cxfa_insert.h b/xfa/fxfa/parser/cxfa_insert.h
index 92579c3..e157a82 100644
--- a/xfa/fxfa/parser/cxfa_insert.h
+++ b/xfa/fxfa/parser/cxfa_insert.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Insert : public CXFA_Node {
+class CXFA_Insert final : public CXFA_Node {
  public:
   CXFA_Insert(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Insert() override;
diff --git a/xfa/fxfa/parser/cxfa_instancemanager.cpp b/xfa/fxfa/parser/cxfa_instancemanager.cpp
index 1ba5499..e0d2658 100644
--- a/xfa/fxfa/parser/cxfa_instancemanager.cpp
+++ b/xfa/fxfa/parser/cxfa_instancemanager.cpp
@@ -11,13 +11,13 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Occur, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
-    {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
+const CXFA_Node::PropertyData kInstanceManagerPropertyData[] = {
+    {XFA_Element::Occur, 1, 0},
+};
 
-constexpr wchar_t kName[] = L"instanceManager";
+const CXFA_Node::AttributeData kInstanceManagerAttributeData[] = {
+    {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
+};
 
 }  // namespace
 
@@ -28,9 +28,8 @@
                 XFA_XDPPACKET_Form,
                 XFA_ObjectType::Node,
                 XFA_Element::InstanceManager,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kInstanceManagerPropertyData,
+                kInstanceManagerAttributeData,
                 pdfium::MakeUnique<CJX_InstanceManager>(this)) {}
 
-CXFA_InstanceManager::~CXFA_InstanceManager() {}
+CXFA_InstanceManager::~CXFA_InstanceManager() = default;
diff --git a/xfa/fxfa/parser/cxfa_instancemanager.h b/xfa/fxfa/parser/cxfa_instancemanager.h
index 1910b92..3ace0e9 100644
--- a/xfa/fxfa/parser/cxfa_instancemanager.h
+++ b/xfa/fxfa/parser/cxfa_instancemanager.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_InstanceManager : public CXFA_Node {
+class CXFA_InstanceManager final : public CXFA_Node {
  public:
   CXFA_InstanceManager(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_InstanceManager() override;
diff --git a/xfa/fxfa/parser/cxfa_integer.cpp b/xfa/fxfa/parser/cxfa_integer.cpp
index bd263ab..cf48a25 100644
--- a/xfa/fxfa/parser/cxfa_integer.cpp
+++ b/xfa/fxfa/parser/cxfa_integer.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_integer.h"
 
-#include "fxjs/xfa/cjx_integer.h"
+#include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kIntegerAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"integer";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                  XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Integer,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Integer>(this)) {}
+                {},
+                kIntegerAttributeData,
+                pdfium::MakeUnique<CJX_Object>(this)) {}
 
-CXFA_Integer::~CXFA_Integer() {}
+CXFA_Integer::~CXFA_Integer() = default;
diff --git a/xfa/fxfa/parser/cxfa_integer.h b/xfa/fxfa/parser/cxfa_integer.h
index 2e5631d..ac94133 100644
--- a/xfa/fxfa/parser/cxfa_integer.h
+++ b/xfa/fxfa/parser/cxfa_integer.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Integer : public CXFA_Node {
+class CXFA_Integer final : public CXFA_Node {
  public:
   CXFA_Integer(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Integer() override;
diff --git a/xfa/fxfa/parser/cxfa_interactive.cpp b/xfa/fxfa/parser/cxfa_interactive.cpp
index e68d957..a489ab3 100644
--- a/xfa/fxfa/parser/cxfa_interactive.cpp
+++ b/xfa/fxfa/parser/cxfa_interactive.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_interactive.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kInteractiveAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"interactive";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Interactive,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kInteractiveAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
 CXFA_Interactive::~CXFA_Interactive() {}
diff --git a/xfa/fxfa/parser/cxfa_interactive.h b/xfa/fxfa/parser/cxfa_interactive.h
index 69f2918..12db0bf 100644
--- a/xfa/fxfa/parser/cxfa_interactive.h
+++ b/xfa/fxfa/parser/cxfa_interactive.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Interactive : public CXFA_Node {
+class CXFA_Interactive final : public CXFA_Node {
  public:
   CXFA_Interactive(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Interactive() override;
diff --git a/xfa/fxfa/parser/cxfa_issuers.cpp b/xfa/fxfa/parser/cxfa_issuers.cpp
index 44c70bc..0d9f5f2 100644
--- a/xfa/fxfa/parser/cxfa_issuers.cpp
+++ b/xfa/fxfa/parser/cxfa_issuers.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_issuers.h"
 
-#include "fxjs/xfa/cjx_issuers.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kIssuersAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"issuers";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Issuers,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Issuers>(this)) {}
+                {},
+                kIssuersAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Issuers::~CXFA_Issuers() {}
+CXFA_Issuers::~CXFA_Issuers() = default;
diff --git a/xfa/fxfa/parser/cxfa_issuers.h b/xfa/fxfa/parser/cxfa_issuers.h
index 6b94f15..11ba8b4 100644
--- a/xfa/fxfa/parser/cxfa_issuers.h
+++ b/xfa/fxfa/parser/cxfa_issuers.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Issuers : public CXFA_Node {
+class CXFA_Issuers final : public CXFA_Node {
  public:
   CXFA_Issuers(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Issuers() override;
diff --git a/xfa/fxfa/parser/cxfa_itemlayoutprocessor.cpp b/xfa/fxfa/parser/cxfa_itemlayoutprocessor.cpp
deleted file mode 100644
index 434d7ed..0000000
--- a/xfa/fxfa/parser/cxfa_itemlayoutprocessor.cpp
+++ /dev/null
@@ -1,2843 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_itemlayoutprocessor.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_keep.h"
-#include "xfa/fxfa/parser/cxfa_layoutcontext.h"
-#include "xfa/fxfa/parser/cxfa_layoutpagemgr.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/cxfa_margin.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
-#include "xfa/fxfa/parser/cxfa_occur.h"
-#include "xfa/fxfa/parser/cxfa_para.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-namespace {
-
-std::vector<WideString> SeparateStringW(const wchar_t* pStr,
-                                        int32_t iStrLen,
-                                        wchar_t delimiter) {
-  std::vector<WideString> ret;
-  if (!pStr)
-    return ret;
-  if (iStrLen < 0)
-    iStrLen = wcslen(pStr);
-
-  const wchar_t* pToken = pStr;
-  const wchar_t* pEnd = pStr + iStrLen;
-  while (true) {
-    if (pStr >= pEnd || delimiter == *pStr) {
-      ret.push_back(WideString(pToken, pStr - pToken));
-      pToken = pStr + 1;
-      if (pStr >= pEnd)
-        break;
-    }
-    pStr++;
-  }
-  return ret;
-}
-
-void UpdateWidgetSize(CXFA_ContentLayoutItem* pLayoutItem,
-                      float* fWidth,
-                      float* fHeight) {
-  CXFA_Node* pNode = pLayoutItem->m_pFormNode;
-  switch (pNode->GetElementType()) {
-    case XFA_Element::Subform:
-    case XFA_Element::Area:
-    case XFA_Element::ExclGroup:
-    case XFA_Element::SubformSet: {
-      if (*fWidth < -XFA_LAYOUT_FLOAT_PERCISION)
-        *fWidth = pLayoutItem->m_sSize.width;
-      if (*fHeight < -XFA_LAYOUT_FLOAT_PERCISION)
-        *fHeight = pLayoutItem->m_sSize.height;
-      break;
-    }
-    case XFA_Element::Draw:
-    case XFA_Element::Field: {
-      pNode->GetDocument()->GetNotify()->StartFieldDrawLayout(pNode, *fWidth,
-                                                              *fHeight);
-      break;
-    }
-    default:
-      NOTREACHED();
-  }
-}
-
-CFX_SizeF CalculateContainerSpecifiedSize(CXFA_Node* pFormNode,
-                                          bool* bContainerWidthAutoSize,
-                                          bool* bContainerHeightAutoSize) {
-  *bContainerWidthAutoSize = true;
-  *bContainerHeightAutoSize = true;
-
-  XFA_Element eType = pFormNode->GetElementType();
-
-  CFX_SizeF containerSize;
-  if (eType == XFA_Element::Subform || eType == XFA_Element::ExclGroup) {
-    Optional<CXFA_Measurement> wValue =
-        pFormNode->JSObject()->TryMeasure(XFA_Attribute::W, false);
-    if (wValue && wValue->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) {
-      containerSize.width = wValue->ToUnit(XFA_Unit::Pt);
-      *bContainerWidthAutoSize = false;
-    }
-
-    Optional<CXFA_Measurement> hValue =
-        pFormNode->JSObject()->TryMeasure(XFA_Attribute::H, false);
-    if (hValue && hValue->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) {
-      containerSize.height = hValue->ToUnit(XFA_Unit::Pt);
-      *bContainerHeightAutoSize = false;
-    }
-  }
-
-  if (*bContainerWidthAutoSize && eType == XFA_Element::Subform) {
-    Optional<CXFA_Measurement> maxW =
-        pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxW, false);
-    if (maxW && maxW->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) {
-      containerSize.width = maxW->ToUnit(XFA_Unit::Pt);
-      *bContainerWidthAutoSize = false;
-    }
-
-    Optional<CXFA_Measurement> maxH =
-        pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxH, false);
-    if (maxH && maxH->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) {
-      containerSize.height = maxH->ToUnit(XFA_Unit::Pt);
-      *bContainerHeightAutoSize = false;
-    }
-  }
-  return containerSize;
-}
-
-CFX_SizeF CalculateContainerComponentSizeFromContentSize(
-    CXFA_Node* pFormNode,
-    bool bContainerWidthAutoSize,
-    float fContentCalculatedWidth,
-    bool bContainerHeightAutoSize,
-    float fContentCalculatedHeight,
-    const CFX_SizeF& currentContainerSize) {
-  CFX_SizeF componentSize = currentContainerSize;
-  CXFA_Margin* pMarginNode =
-      pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
-  if (bContainerWidthAutoSize) {
-    componentSize.width = fContentCalculatedWidth;
-    if (pMarginNode) {
-      Optional<CXFA_Measurement> leftInset =
-          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::LeftInset, false);
-      if (leftInset)
-        componentSize.width += leftInset->ToUnit(XFA_Unit::Pt);
-
-      Optional<CXFA_Measurement> rightInset =
-          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::RightInset, false);
-      if (rightInset)
-        componentSize.width += rightInset->ToUnit(XFA_Unit::Pt);
-    }
-  }
-
-  if (bContainerHeightAutoSize) {
-    componentSize.height = fContentCalculatedHeight;
-    if (pMarginNode) {
-      Optional<CXFA_Measurement> topInset =
-          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::TopInset, false);
-      if (topInset)
-        componentSize.height += topInset->ToUnit(XFA_Unit::Pt);
-
-      Optional<CXFA_Measurement> bottomInset =
-          pMarginNode->JSObject()->TryMeasure(XFA_Attribute::BottomInset,
-                                              false);
-      if (bottomInset)
-        componentSize.height += bottomInset->ToUnit(XFA_Unit::Pt);
-    }
-  }
-  return componentSize;
-}
-
-void RelocateTableRowCells(CXFA_ContentLayoutItem* pLayoutRow,
-                           const std::vector<float>& rgSpecifiedColumnWidths,
-                           XFA_AttributeEnum eLayout) {
-  bool bContainerWidthAutoSize = true;
-  bool bContainerHeightAutoSize = true;
-  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
-      pLayoutRow->m_pFormNode, &bContainerWidthAutoSize,
-      &bContainerHeightAutoSize);
-  CXFA_Margin* pMarginNode =
-      pLayoutRow->m_pFormNode->GetFirstChildByClass<CXFA_Margin>(
-          XFA_Element::Margin);
-  float fLeftInset = 0;
-  float fTopInset = 0;
-  float fRightInset = 0;
-  float fBottomInset = 0;
-  if (pMarginNode) {
-    fLeftInset = pMarginNode->JSObject()
-                     ->GetMeasure(XFA_Attribute::LeftInset)
-                     .ToUnit(XFA_Unit::Pt);
-    fTopInset = pMarginNode->JSObject()
-                    ->GetMeasure(XFA_Attribute::TopInset)
-                    .ToUnit(XFA_Unit::Pt);
-    fRightInset = pMarginNode->JSObject()
-                      ->GetMeasure(XFA_Attribute::RightInset)
-                      .ToUnit(XFA_Unit::Pt);
-    fBottomInset = pMarginNode->JSObject()
-                       ->GetMeasure(XFA_Attribute::BottomInset)
-                       .ToUnit(XFA_Unit::Pt);
-  }
-
-  float fContentWidthLimit =
-      bContainerWidthAutoSize ? FLT_MAX
-                              : containerSize.width - fLeftInset - fRightInset;
-  float fContentCurrentHeight =
-      pLayoutRow->m_sSize.height - fTopInset - fBottomInset;
-  float fContentCalculatedWidth = 0;
-  float fContentCalculatedHeight = 0;
-  float fCurrentColX = 0;
-  int32_t nCurrentColIdx = 0;
-  bool bMetWholeRowCell = false;
-
-  for (auto* pLayoutChild =
-           static_cast<CXFA_ContentLayoutItem*>(pLayoutRow->m_pFirstChild);
-       pLayoutChild; pLayoutChild = static_cast<CXFA_ContentLayoutItem*>(
-                         pLayoutChild->m_pNextSibling)) {
-    int32_t nOriginalColSpan =
-        pLayoutChild->m_pFormNode->JSObject()->GetInteger(
-            XFA_Attribute::ColSpan);
-    int32_t nColSpan = nOriginalColSpan;
-    float fColSpanWidth = 0;
-    if (nColSpan == -1 ||
-        nCurrentColIdx + nColSpan >
-            pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths)) {
-      nColSpan = pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths) -
-                 nCurrentColIdx;
-    }
-    for (int32_t i = 0; i < nColSpan; i++)
-      fColSpanWidth += rgSpecifiedColumnWidths[nCurrentColIdx + i];
-
-    if (nColSpan != nOriginalColSpan) {
-      fColSpanWidth =
-          bMetWholeRowCell ? 0 : std::max(fColSpanWidth,
-                                          pLayoutChild->m_sSize.height);
-    }
-    if (nOriginalColSpan == -1)
-      bMetWholeRowCell = true;
-
-    pLayoutChild->m_sPos = CFX_PointF(fCurrentColX, 0);
-    pLayoutChild->m_sSize.width = fColSpanWidth;
-    if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutChild->m_pFormNode))
-      continue;
-
-    fCurrentColX += fColSpanWidth;
-    nCurrentColIdx += nColSpan;
-    float fNewHeight = bContainerHeightAutoSize ? -1 : fContentCurrentHeight;
-    UpdateWidgetSize(pLayoutChild, &fColSpanWidth, &fNewHeight);
-    pLayoutChild->m_sSize.height = fNewHeight;
-    if (bContainerHeightAutoSize) {
-      fContentCalculatedHeight =
-          std::max(fContentCalculatedHeight, pLayoutChild->m_sSize.height);
-    }
-  }
-
-  if (bContainerHeightAutoSize) {
-    for (CXFA_ContentLayoutItem* pLayoutChild =
-             (CXFA_ContentLayoutItem*)pLayoutRow->m_pFirstChild;
-         pLayoutChild;
-         pLayoutChild = (CXFA_ContentLayoutItem*)pLayoutChild->m_pNextSibling) {
-      UpdateWidgetSize(pLayoutChild, &pLayoutChild->m_sSize.width,
-                       &fContentCalculatedHeight);
-      float fOldChildHeight = pLayoutChild->m_sSize.height;
-      pLayoutChild->m_sSize.height = fContentCalculatedHeight;
-      CXFA_Para* pParaNode =
-          pLayoutChild->m_pFormNode->GetFirstChildByClass<CXFA_Para>(
-              XFA_Element::Para);
-      if (pParaNode && pLayoutChild->m_pFirstChild) {
-        float fOffHeight = fContentCalculatedHeight - fOldChildHeight;
-        XFA_AttributeEnum eVType =
-            pParaNode->JSObject()->GetEnum(XFA_Attribute::VAlign);
-        switch (eVType) {
-          case XFA_AttributeEnum::Middle:
-            fOffHeight = fOffHeight / 2;
-            break;
-          case XFA_AttributeEnum::Bottom:
-            break;
-          case XFA_AttributeEnum::Top:
-          default:
-            fOffHeight = 0;
-            break;
-        }
-        if (fOffHeight > 0) {
-          for (CXFA_ContentLayoutItem* pInnerLayoutChild =
-                   (CXFA_ContentLayoutItem*)pLayoutChild->m_pFirstChild;
-               pInnerLayoutChild;
-               pInnerLayoutChild =
-                   (CXFA_ContentLayoutItem*)pInnerLayoutChild->m_pNextSibling) {
-            pInnerLayoutChild->m_sPos.y += fOffHeight;
-          }
-        }
-      }
-    }
-  }
-
-  if (bContainerWidthAutoSize) {
-    float fChildSuppliedWidth = fCurrentColX;
-    if (fContentWidthLimit < FLT_MAX &&
-        fContentWidthLimit > fChildSuppliedWidth) {
-      fChildSuppliedWidth = fContentWidthLimit;
-    }
-    fContentCalculatedWidth =
-        std::max(fContentCalculatedWidth, fChildSuppliedWidth);
-  } else {
-    fContentCalculatedWidth = containerSize.width - fLeftInset - fRightInset;
-  }
-
-  if (pLayoutRow->m_pFormNode->JSObject()->GetEnum(XFA_Attribute::Layout) ==
-      XFA_AttributeEnum::Rl_row) {
-    for (CXFA_ContentLayoutItem* pLayoutChild =
-             (CXFA_ContentLayoutItem*)pLayoutRow->m_pFirstChild;
-         pLayoutChild;
-         pLayoutChild = (CXFA_ContentLayoutItem*)pLayoutChild->m_pNextSibling) {
-      pLayoutChild->m_sPos.x = fContentCalculatedWidth -
-                               pLayoutChild->m_sPos.x -
-                               pLayoutChild->m_sSize.width;
-    }
-  }
-  pLayoutRow->m_sSize = CalculateContainerComponentSizeFromContentSize(
-      pLayoutRow->m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth,
-      bContainerHeightAutoSize, fContentCalculatedHeight, containerSize);
-}
-
-void UpdatePendingItemLayout(CXFA_ItemLayoutProcessor* pProcessor,
-                             CXFA_ContentLayoutItem* pLayoutItem) {
-  XFA_AttributeEnum eLayout =
-      pLayoutItem->m_pFormNode->JSObject()->GetEnum(XFA_Attribute::Layout);
-  switch (eLayout) {
-    case XFA_AttributeEnum::Row:
-    case XFA_AttributeEnum::Rl_row:
-      RelocateTableRowCells(pLayoutItem, pProcessor->m_rgSpecifiedColumnWidths,
-                            eLayout);
-      break;
-    default:
-      break;
-  }
-}
-
-void AddTrailerBeforeSplit(CXFA_ItemLayoutProcessor* pProcessor,
-                           float fSplitPos,
-                           CXFA_ContentLayoutItem* pTrailerLayoutItem,
-                           bool bUseInherited) {
-  if (!pTrailerLayoutItem)
-    return;
-
-  float fHeight = pTrailerLayoutItem->m_sSize.height;
-  if (bUseInherited) {
-    float fNewSplitPos = 0;
-    if (fSplitPos - fHeight > XFA_LAYOUT_FLOAT_PERCISION)
-      fNewSplitPos = pProcessor->FindSplitPos(fSplitPos - fHeight);
-    if (fNewSplitPos > XFA_LAYOUT_FLOAT_PERCISION)
-      pProcessor->SplitLayoutItem(fNewSplitPos);
-    return;
-  }
-
-  UpdatePendingItemLayout(pProcessor, pTrailerLayoutItem);
-  CXFA_Margin* pMarginNode =
-      pProcessor->m_pFormNode->GetFirstChildByClass<CXFA_Margin>(
-          XFA_Element::Margin);
-  float fLeftInset = 0;
-  float fTopInset = 0;
-  float fRightInset = 0;
-  float fBottomInset = 0;
-  if (pMarginNode) {
-    fLeftInset = pMarginNode->JSObject()
-                     ->GetMeasure(XFA_Attribute::LeftInset)
-                     .ToUnit(XFA_Unit::Pt);
-    fTopInset = pMarginNode->JSObject()
-                    ->GetMeasure(XFA_Attribute::TopInset)
-                    .ToUnit(XFA_Unit::Pt);
-    fRightInset = pMarginNode->JSObject()
-                      ->GetMeasure(XFA_Attribute::RightInset)
-                      .ToUnit(XFA_Unit::Pt);
-    fBottomInset = pMarginNode->JSObject()
-                       ->GetMeasure(XFA_Attribute::BottomInset)
-                       .ToUnit(XFA_Unit::Pt);
-  }
-
-  if (!pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem)) {
-    pTrailerLayoutItem->m_sPos.y = pProcessor->m_fLastRowY;
-    pTrailerLayoutItem->m_sPos.x = pProcessor->m_fLastRowWidth;
-    pProcessor->m_pLayoutItem->m_sSize.width +=
-        pTrailerLayoutItem->m_sSize.width;
-    pProcessor->m_pLayoutItem->AddChild(pTrailerLayoutItem);
-    return;
-  }
-
-  float fNewSplitPos = 0;
-  if (fSplitPos - fHeight > XFA_LAYOUT_FLOAT_PERCISION)
-    fNewSplitPos = pProcessor->FindSplitPos(fSplitPos - fHeight);
-
-  if (fNewSplitPos > XFA_LAYOUT_FLOAT_PERCISION) {
-    pProcessor->SplitLayoutItem(fNewSplitPos);
-    pTrailerLayoutItem->m_sPos.y = fNewSplitPos - fTopInset - fBottomInset;
-  } else {
-    pTrailerLayoutItem->m_sPos.y = fSplitPos - fTopInset - fBottomInset;
-  }
-
-  switch (pTrailerLayoutItem->m_pFormNode->JSObject()->GetEnum(
-      XFA_Attribute::HAlign)) {
-    case XFA_AttributeEnum::Right:
-      pTrailerLayoutItem->m_sPos.x = pProcessor->m_pLayoutItem->m_sSize.width -
-                                     fRightInset -
-                                     pTrailerLayoutItem->m_sSize.width;
-      break;
-    case XFA_AttributeEnum::Center:
-      pTrailerLayoutItem->m_sPos.x =
-          (pProcessor->m_pLayoutItem->m_sSize.width - fLeftInset - fRightInset -
-           pTrailerLayoutItem->m_sSize.width) /
-          2;
-      break;
-    case XFA_AttributeEnum::Left:
-    default:
-      pTrailerLayoutItem->m_sPos.x = fLeftInset;
-      break;
-  }
-  pProcessor->m_pLayoutItem->m_sSize.height += fHeight;
-  pProcessor->m_pLayoutItem->AddChild(pTrailerLayoutItem);
-}
-
-void AddLeaderAfterSplit(CXFA_ItemLayoutProcessor* pProcessor,
-                         CXFA_ContentLayoutItem* pLeaderLayoutItem) {
-  UpdatePendingItemLayout(pProcessor, pLeaderLayoutItem);
-
-  CXFA_Margin* pMarginNode =
-      pProcessor->m_pFormNode->GetFirstChildByClass<CXFA_Margin>(
-          XFA_Element::Margin);
-  float fLeftInset = 0;
-  float fRightInset = 0;
-  if (pMarginNode) {
-    fLeftInset = pMarginNode->JSObject()
-                     ->GetMeasure(XFA_Attribute::LeftInset)
-                     .ToUnit(XFA_Unit::Pt);
-    fRightInset = pMarginNode->JSObject()
-                      ->GetMeasure(XFA_Attribute::RightInset)
-                      .ToUnit(XFA_Unit::Pt);
-  }
-
-  float fHeight = pLeaderLayoutItem->m_sSize.height;
-  for (CXFA_ContentLayoutItem* pChildItem =
-           (CXFA_ContentLayoutItem*)pProcessor->m_pLayoutItem->m_pFirstChild;
-       pChildItem;
-       pChildItem = (CXFA_ContentLayoutItem*)pChildItem->m_pNextSibling) {
-    pChildItem->m_sPos.y += fHeight;
-  }
-  pLeaderLayoutItem->m_sPos.y = 0;
-
-  switch (pLeaderLayoutItem->m_pFormNode->JSObject()->GetEnum(
-      XFA_Attribute::HAlign)) {
-    case XFA_AttributeEnum::Right:
-      pLeaderLayoutItem->m_sPos.x = pProcessor->m_pLayoutItem->m_sSize.width -
-                                    fRightInset -
-                                    pLeaderLayoutItem->m_sSize.width;
-      break;
-    case XFA_AttributeEnum::Center:
-      pLeaderLayoutItem->m_sPos.x =
-          (pProcessor->m_pLayoutItem->m_sSize.width - fLeftInset - fRightInset -
-           pLeaderLayoutItem->m_sSize.width) /
-          2;
-      break;
-    case XFA_AttributeEnum::Left:
-    default:
-      pLeaderLayoutItem->m_sPos.x = fLeftInset;
-      break;
-  }
-  pProcessor->m_pLayoutItem->m_sSize.height += fHeight;
-  pProcessor->m_pLayoutItem->AddChild(pLeaderLayoutItem);
-}
-
-void AddPendingNode(CXFA_ItemLayoutProcessor* pProcessor,
-                    CXFA_Node* pPendingNode,
-                    bool bBreakPending) {
-  pProcessor->m_PendingNodes.push_back(pPendingNode);
-  pProcessor->m_bBreakPending = bBreakPending;
-}
-
-float InsertPendingItems(CXFA_ItemLayoutProcessor* pProcessor,
-                         CXFA_Node* pCurChildNode) {
-  float fTotalHeight = 0;
-  if (pProcessor->m_PendingNodes.empty())
-    return fTotalHeight;
-
-  if (!pProcessor->m_pLayoutItem) {
-    pProcessor->m_pLayoutItem =
-        pProcessor->CreateContentLayoutItem(pCurChildNode);
-    pProcessor->m_pLayoutItem->m_sSize.clear();
-  }
-
-  while (!pProcessor->m_PendingNodes.empty()) {
-    auto pPendingProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-        pProcessor->m_PendingNodes.front(), nullptr);
-    pProcessor->m_PendingNodes.pop_front();
-    pPendingProcessor->DoLayout(false, FLT_MAX, FLT_MAX, nullptr);
-    CXFA_ContentLayoutItem* pPendingLayoutItem =
-        pPendingProcessor->HasLayoutItem()
-            ? pPendingProcessor->ExtractLayoutItem()
-            : nullptr;
-    if (pPendingLayoutItem) {
-      AddLeaderAfterSplit(pProcessor, pPendingLayoutItem);
-      if (pProcessor->m_bBreakPending)
-        fTotalHeight += pPendingLayoutItem->m_sSize.height;
-    }
-  }
-  return fTotalHeight;
-}
-
-XFA_AttributeEnum GetLayout(CXFA_Node* pFormNode, bool* bRootForceTb) {
-  *bRootForceTb = false;
-  Optional<XFA_AttributeEnum> layoutMode =
-      pFormNode->JSObject()->TryEnum(XFA_Attribute::Layout, false);
-  if (layoutMode)
-    return *layoutMode;
-
-  CXFA_Node* pParentNode = pFormNode->GetParent();
-  if (pParentNode && pParentNode->GetElementType() == XFA_Element::Form) {
-    *bRootForceTb = true;
-    return XFA_AttributeEnum::Tb;
-  }
-  return XFA_AttributeEnum::Position;
-}
-
-bool ExistContainerKeep(CXFA_Node* pCurNode, bool bPreFind) {
-  if (!pCurNode || !XFA_ItemLayoutProcessor_IsTakingSpace(pCurNode))
-    return false;
-
-  CXFA_Node* pPreContainer = bPreFind ? pCurNode->GetPrevContainerSibling()
-                                      : pCurNode->GetNextContainerSibling();
-  if (!pPreContainer)
-    return false;
-
-  CXFA_Keep* pKeep =
-      pCurNode->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
-  if (pKeep) {
-    XFA_Attribute eKeepType = XFA_Attribute::Previous;
-    if (!bPreFind)
-      eKeepType = XFA_Attribute::Next;
-
-    Optional<XFA_AttributeEnum> previous =
-        pKeep->JSObject()->TryEnum(eKeepType, false);
-    if (previous) {
-      if (*previous == XFA_AttributeEnum::ContentArea ||
-          *previous == XFA_AttributeEnum::PageArea) {
-        return true;
-      }
-    }
-  }
-
-  pKeep = pPreContainer->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
-  if (!pKeep)
-    return false;
-
-  XFA_Attribute eKeepType = XFA_Attribute::Next;
-  if (!bPreFind)
-    eKeepType = XFA_Attribute::Previous;
-
-  Optional<XFA_AttributeEnum> next =
-      pKeep->JSObject()->TryEnum(eKeepType, false);
-  if (!next)
-    return false;
-  if (*next == XFA_AttributeEnum::ContentArea ||
-      *next == XFA_AttributeEnum::PageArea) {
-    return true;
-  }
-  return false;
-}
-
-bool FindBreakNode(CXFA_Node* pContainerNode,
-                   CXFA_Node*& pCurActionNode,
-                   XFA_ItemLayoutProcessorStages* nCurStage,
-                   bool bBreakBefore) {
-  bool bFindRs = false;
-  for (CXFA_Node* pBreakNode = pContainerNode; pBreakNode;
-       pBreakNode = pBreakNode->GetNextSibling()) {
-    XFA_Attribute eAttributeType = XFA_Attribute::Before;
-    if (!bBreakBefore)
-      eAttributeType = XFA_Attribute::After;
-
-    switch (pBreakNode->GetElementType()) {
-      case XFA_Element::BreakBefore: {
-        if (bBreakBefore) {
-          pCurActionNode = pBreakNode;
-          *nCurStage = XFA_ItemLayoutProcessorStages::BreakBefore;
-          bFindRs = true;
-        }
-        break;
-      }
-      case XFA_Element::BreakAfter: {
-        if (!bBreakBefore) {
-          pCurActionNode = pBreakNode;
-          *nCurStage = XFA_ItemLayoutProcessorStages::BreakAfter;
-          bFindRs = true;
-        }
-        break;
-      }
-      case XFA_Element::Break:
-        if (pBreakNode->JSObject()->GetEnum(eAttributeType) !=
-            XFA_AttributeEnum::Auto) {
-          pCurActionNode = pBreakNode;
-          *nCurStage = XFA_ItemLayoutProcessorStages::BreakBefore;
-          if (!bBreakBefore)
-            *nCurStage = XFA_ItemLayoutProcessorStages::BreakAfter;
-
-          bFindRs = true;
-        }
-        break;
-      default:
-        break;
-    }
-    if (bFindRs)
-      break;
-  }
-  return bFindRs;
-}
-
-void DeleteLayoutGeneratedNode(CXFA_Node* pGenerateNode) {
-  CXFA_FFNotify* pNotify = pGenerateNode->GetDocument()->GetNotify();
-  CXFA_LayoutProcessor* pDocLayout =
-      pGenerateNode->GetDocument()->GetDocLayout();
-  CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode> sIterator(
-      pGenerateNode);
-  for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
-       pNode = sIterator.MoveToNext()) {
-    CXFA_ContentLayoutItem* pCurLayoutItem =
-        static_cast<CXFA_ContentLayoutItem*>(
-            pNode->JSObject()->GetLayoutItem());
-    CXFA_ContentLayoutItem* pNextLayoutItem = nullptr;
-    while (pCurLayoutItem) {
-      pNextLayoutItem = pCurLayoutItem->m_pNext;
-      pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem);
-      delete pCurLayoutItem;
-      pCurLayoutItem = pNextLayoutItem;
-    }
-  }
-  pGenerateNode->GetParent()->RemoveChild(pGenerateNode, true);
-}
-
-uint8_t HAlignEnumToInt(XFA_AttributeEnum eHAlign) {
-  switch (eHAlign) {
-    case XFA_AttributeEnum::Center:
-      return 1;
-    case XFA_AttributeEnum::Right:
-      return 2;
-    case XFA_AttributeEnum::Left:
-    default:
-      return 0;
-  }
-}
-
-XFA_ItemLayoutProcessorResult InsertFlowedItem(
-    CXFA_ItemLayoutProcessor* pThis,
-    CXFA_ItemLayoutProcessor* pProcessor,
-    bool bContainerWidthAutoSize,
-    bool bContainerHeightAutoSize,
-    float fContainerHeight,
-    XFA_AttributeEnum eFlowStrategy,
-    uint8_t* uCurHAlignState,
-    std::vector<CXFA_ContentLayoutItem*> (&rgCurLineLayoutItems)[3],
-    bool bUseBreakControl,
-    float fAvailHeight,
-    float fRealHeight,
-    float fContentWidthLimit,
-    float* fContentCurRowY,
-    float* fContentCurRowAvailWidth,
-    float* fContentCurRowHeight,
-    bool* bAddedItemInRow,
-    bool* bForceEndPage,
-    CXFA_LayoutContext* pLayoutContext,
-    bool bNewRow) {
-  bool bTakeSpace =
-      XFA_ItemLayoutProcessor_IsTakingSpace(pProcessor->m_pFormNode);
-  uint8_t uHAlign = HAlignEnumToInt(
-      pThis->m_pCurChildNode->JSObject()->GetEnum(XFA_Attribute::HAlign));
-  if (bContainerWidthAutoSize)
-    uHAlign = 0;
-
-  if ((eFlowStrategy != XFA_AttributeEnum::Rl_tb &&
-       uHAlign < *uCurHAlignState) ||
-      (eFlowStrategy == XFA_AttributeEnum::Rl_tb &&
-       uHAlign > *uCurHAlignState)) {
-    return XFA_ItemLayoutProcessorResult::RowFullBreak;
-  }
-
-  *uCurHAlignState = uHAlign;
-  bool bIsOwnSplit =
-      pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None;
-  bool bUseRealHeight = bTakeSpace && bContainerHeightAutoSize && bIsOwnSplit &&
-                        pProcessor->m_pFormNode->GetParent()->GetIntact() ==
-                            XFA_AttributeEnum::None;
-  bool bIsTransHeight = bTakeSpace;
-  if (bIsTransHeight && !bIsOwnSplit) {
-    bool bRootForceTb = false;
-    XFA_AttributeEnum eLayoutStrategy =
-        GetLayout(pProcessor->m_pFormNode, &bRootForceTb);
-    if (eLayoutStrategy == XFA_AttributeEnum::Lr_tb ||
-        eLayoutStrategy == XFA_AttributeEnum::Rl_tb) {
-      bIsTransHeight = false;
-    }
-  }
-
-  bool bUseInherited = false;
-  CXFA_LayoutContext layoutContext;
-  if (pThis->m_pPageMgr) {
-    CXFA_Node* pOverflowNode =
-        pThis->m_pPageMgr->QueryOverflow(pThis->m_pFormNode);
-    if (pOverflowNode) {
-      layoutContext.m_pOverflowNode = pOverflowNode;
-      layoutContext.m_pOverflowProcessor = pThis;
-      pLayoutContext = &layoutContext;
-    }
-  }
-
-  XFA_ItemLayoutProcessorResult eRetValue = XFA_ItemLayoutProcessorResult::Done;
-  if (!bNewRow ||
-      pProcessor->m_ePreProcessRs == XFA_ItemLayoutProcessorResult::Done) {
-    eRetValue = pProcessor->DoLayout(
-        bTakeSpace ? bUseBreakControl : false,
-        bUseRealHeight ? fRealHeight - *fContentCurRowY : FLT_MAX,
-        bIsTransHeight ? fRealHeight - *fContentCurRowY : FLT_MAX,
-        pLayoutContext);
-    pProcessor->m_ePreProcessRs = eRetValue;
-  } else {
-    eRetValue = pProcessor->m_ePreProcessRs;
-    pProcessor->m_ePreProcessRs = XFA_ItemLayoutProcessorResult::Done;
-  }
-  if (pProcessor->HasLayoutItem() == false)
-    return eRetValue;
-
-  CFX_SizeF childSize = pProcessor->GetCurrentComponentSize();
-  if (bUseRealHeight && fRealHeight < XFA_LAYOUT_FLOAT_PERCISION) {
-    fRealHeight = FLT_MAX;
-    fAvailHeight = FLT_MAX;
-  }
-  if (bTakeSpace && (childSize.width >
-                     *fContentCurRowAvailWidth + XFA_LAYOUT_FLOAT_PERCISION) &&
-      (fContentWidthLimit - *fContentCurRowAvailWidth >
-       XFA_LAYOUT_FLOAT_PERCISION)) {
-    return XFA_ItemLayoutProcessorResult::RowFullBreak;
-  }
-
-  CXFA_Node* pOverflowLeaderNode = nullptr;
-  CXFA_Node* pOverflowTrailerNode = nullptr;
-  CXFA_Node* pFormNode = nullptr;
-  CXFA_ContentLayoutItem* pTrailerLayoutItem = nullptr;
-  bool bIsAddTrailerHeight = false;
-  if (pThis->m_pPageMgr &&
-      pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None) {
-    pFormNode = pThis->m_pPageMgr->QueryOverflow(pProcessor->m_pFormNode);
-    if (!pFormNode && pLayoutContext && pLayoutContext->m_pOverflowProcessor) {
-      pFormNode = pLayoutContext->m_pOverflowNode;
-      bUseInherited = true;
-    }
-    if (pThis->m_pPageMgr->ProcessOverflow(pFormNode, pOverflowLeaderNode,
-                                           pOverflowTrailerNode, false,
-                                           false)) {
-      if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowTrailerNode)) {
-        if (pOverflowTrailerNode) {
-          auto pOverflowLeaderProcessor =
-              pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pOverflowTrailerNode,
-                                                           nullptr);
-          pOverflowLeaderProcessor->DoLayout(false, FLT_MAX, FLT_MAX, nullptr);
-          pTrailerLayoutItem =
-              pOverflowLeaderProcessor->HasLayoutItem()
-                  ? pOverflowLeaderProcessor->ExtractLayoutItem()
-                  : nullptr;
-        }
-
-        bIsAddTrailerHeight =
-            bUseInherited
-                ? pThis->IsAddNewRowForTrailer(pTrailerLayoutItem)
-                : pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem);
-        if (bIsAddTrailerHeight) {
-          childSize.height += pTrailerLayoutItem->m_sSize.height;
-          bIsAddTrailerHeight = true;
-        }
-      }
-    }
-  }
-
-  if (!bTakeSpace ||
-      *fContentCurRowY + childSize.height <=
-          fAvailHeight + XFA_LAYOUT_FLOAT_PERCISION ||
-      (!bContainerHeightAutoSize &&
-       pThis->m_fUsedSize + fAvailHeight + XFA_LAYOUT_FLOAT_PERCISION >=
-           fContainerHeight)) {
-    if (!bTakeSpace || eRetValue == XFA_ItemLayoutProcessorResult::Done) {
-      if (pProcessor->m_bUseInheriated) {
-        if (pTrailerLayoutItem)
-          AddTrailerBeforeSplit(pProcessor, childSize.height,
-                                pTrailerLayoutItem, false);
-        if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode))
-          AddPendingNode(pProcessor, pOverflowLeaderNode, false);
-
-        pProcessor->m_bUseInheriated = false;
-      } else {
-        if (bIsAddTrailerHeight)
-          childSize.height -= pTrailerLayoutItem->m_sSize.height;
-
-        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
-                                         pOverflowTrailerNode,
-                                         pTrailerLayoutItem, pFormNode);
-      }
-
-      CXFA_ContentLayoutItem* pChildLayoutItem =
-          pProcessor->ExtractLayoutItem();
-      if (ExistContainerKeep(pProcessor->m_pFormNode, false) &&
-          pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None) {
-        pThis->m_arrayKeepItems.push_back(pChildLayoutItem);
-      } else {
-        pThis->m_arrayKeepItems.clear();
-      }
-      rgCurLineLayoutItems[uHAlign].push_back(pChildLayoutItem);
-      *bAddedItemInRow = true;
-      if (bTakeSpace) {
-        *fContentCurRowAvailWidth -= childSize.width;
-        *fContentCurRowHeight =
-            std::max(*fContentCurRowHeight, childSize.height);
-      }
-      return XFA_ItemLayoutProcessorResult::Done;
-    }
-
-    if (eRetValue == XFA_ItemLayoutProcessorResult::PageFullBreak) {
-      if (pProcessor->m_bUseInheriated) {
-        if (pTrailerLayoutItem) {
-          AddTrailerBeforeSplit(pProcessor, childSize.height,
-                                pTrailerLayoutItem, false);
-        }
-        if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode))
-          AddPendingNode(pProcessor, pOverflowLeaderNode, false);
-
-        pProcessor->m_bUseInheriated = false;
-      } else {
-        if (bIsAddTrailerHeight)
-          childSize.height -= pTrailerLayoutItem->m_sSize.height;
-
-        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
-                                         pOverflowTrailerNode,
-                                         pTrailerLayoutItem, pFormNode);
-      }
-    }
-    rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
-    *bAddedItemInRow = true;
-    *fContentCurRowAvailWidth -= childSize.width;
-    *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height);
-    return eRetValue;
-  }
-
-  XFA_ItemLayoutProcessorResult eResult;
-  if (pThis->ProcessKeepForSplit(
-          pThis, pProcessor, eRetValue, &rgCurLineLayoutItems[uHAlign],
-          fContentCurRowAvailWidth, fContentCurRowHeight, fContentCurRowY,
-          bAddedItemInRow, bForceEndPage, &eResult)) {
-    return eResult;
-  }
-
-  *bForceEndPage = true;
-  float fSplitPos = pProcessor->FindSplitPos(fAvailHeight - *fContentCurRowY);
-  if (fSplitPos > XFA_LAYOUT_FLOAT_PERCISION) {
-    XFA_AttributeEnum eLayout =
-        pProcessor->m_pFormNode->JSObject()->GetEnum(XFA_Attribute::Layout);
-    if (eLayout == XFA_AttributeEnum::Tb &&
-        eRetValue == XFA_ItemLayoutProcessorResult::Done) {
-      pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
-                                       pOverflowTrailerNode, pTrailerLayoutItem,
-                                       pFormNode);
-      rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
-      *bAddedItemInRow = true;
-      if (bTakeSpace) {
-        *fContentCurRowAvailWidth -= childSize.width;
-        *fContentCurRowHeight =
-            std::max(*fContentCurRowHeight, childSize.height);
-      }
-      return XFA_ItemLayoutProcessorResult::PageFullBreak;
-    }
-
-    CXFA_Node* pTempLeaderNode = nullptr;
-    CXFA_Node* pTempTrailerNode = nullptr;
-    if (pThis->m_pPageMgr && !pProcessor->m_bUseInheriated &&
-        eRetValue != XFA_ItemLayoutProcessorResult::PageFullBreak) {
-      pThis->m_pPageMgr->ProcessOverflow(pFormNode, pTempLeaderNode,
-                                         pTempTrailerNode, false, true);
-    }
-    if (pTrailerLayoutItem && bIsAddTrailerHeight) {
-      AddTrailerBeforeSplit(pProcessor, fSplitPos, pTrailerLayoutItem,
-                            bUseInherited);
-    } else {
-      pProcessor->SplitLayoutItem(fSplitPos);
-    }
-
-    if (bUseInherited) {
-      pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
-                                       pOverflowTrailerNode, pTrailerLayoutItem,
-                                       pFormNode);
-      pThis->m_bUseInheriated = true;
-    } else {
-      CXFA_LayoutItem* firstChild = pProcessor->m_pLayoutItem->m_pFirstChild;
-      if (firstChild && !firstChild->m_pNextSibling &&
-          firstChild->m_pFormNode->IsLayoutGeneratedNode()) {
-        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
-                                         pOverflowTrailerNode,
-                                         pTrailerLayoutItem, pFormNode);
-      } else if (pProcessor->JudgeLeaderOrTrailerForOccur(
-                     pOverflowLeaderNode)) {
-        AddPendingNode(pProcessor, pOverflowLeaderNode, false);
-      }
-    }
-
-    if (pProcessor->m_pLayoutItem->m_pNextSibling) {
-      childSize = pProcessor->GetCurrentComponentSize();
-      rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
-      *bAddedItemInRow = true;
-      if (bTakeSpace) {
-        *fContentCurRowAvailWidth -= childSize.width;
-        *fContentCurRowHeight =
-            std::max(*fContentCurRowHeight, childSize.height);
-      }
-    }
-    return XFA_ItemLayoutProcessorResult::PageFullBreak;
-  }
-
-  if (*fContentCurRowY <= XFA_LAYOUT_FLOAT_PERCISION) {
-    childSize = pProcessor->GetCurrentComponentSize();
-    if (pProcessor->m_pPageMgr->GetNextAvailContentHeight(childSize.height)) {
-      CXFA_Node* pTempLeaderNode = nullptr;
-      CXFA_Node* pTempTrailerNode = nullptr;
-      if (pThis->m_pPageMgr) {
-        if (!pFormNode && pLayoutContext)
-          pFormNode = pLayoutContext->m_pOverflowProcessor->m_pFormNode;
-
-        pThis->m_pPageMgr->ProcessOverflow(pFormNode, pTempLeaderNode,
-                                           pTempTrailerNode, false, true);
-      }
-      if (bUseInherited) {
-        pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode,
-                                         pOverflowTrailerNode,
-                                         pTrailerLayoutItem, pFormNode);
-        pThis->m_bUseInheriated = true;
-      }
-      return XFA_ItemLayoutProcessorResult::PageFullBreak;
-    }
-
-    rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem());
-    *bAddedItemInRow = true;
-    if (bTakeSpace) {
-      *fContentCurRowAvailWidth -= childSize.width;
-      *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height);
-    }
-    if (eRetValue == XFA_ItemLayoutProcessorResult::Done)
-      *bForceEndPage = false;
-
-    return eRetValue;
-  }
-
-  XFA_AttributeEnum eLayout =
-      pProcessor->m_pFormNode->JSObject()->GetEnum(XFA_Attribute::Layout);
-  if (pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None &&
-      eLayout == XFA_AttributeEnum::Tb) {
-    if (pThis->m_pPageMgr) {
-      pThis->m_pPageMgr->ProcessOverflow(pFormNode, pOverflowLeaderNode,
-                                         pOverflowTrailerNode, false, true);
-    }
-    if (pTrailerLayoutItem)
-      AddTrailerBeforeSplit(pProcessor, fSplitPos, pTrailerLayoutItem, false);
-    if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode))
-      AddPendingNode(pProcessor, pOverflowLeaderNode, false);
-
-    return XFA_ItemLayoutProcessorResult::PageFullBreak;
-  }
-
-  if (eRetValue != XFA_ItemLayoutProcessorResult::Done)
-    return XFA_ItemLayoutProcessorResult::PageFullBreak;
-
-  if (!pFormNode && pLayoutContext)
-    pFormNode = pLayoutContext->m_pOverflowProcessor->m_pFormNode;
-  if (pThis->m_pPageMgr) {
-    pThis->m_pPageMgr->ProcessOverflow(pFormNode, pOverflowLeaderNode,
-                                       pOverflowTrailerNode, false, true);
-  }
-  if (bUseInherited) {
-    pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, pOverflowTrailerNode,
-                                     pTrailerLayoutItem, pFormNode);
-    pThis->m_bUseInheriated = true;
-  }
-  return XFA_ItemLayoutProcessorResult::PageFullBreak;
-}
-
-bool FindLayoutItemSplitPos(CXFA_ContentLayoutItem* pLayoutItem,
-                            float fCurVerticalOffset,
-                            float* fProposedSplitPos,
-                            bool* bAppChange,
-                            bool bCalculateMargin) {
-  CXFA_Node* pFormNode = pLayoutItem->m_pFormNode;
-  if (*fProposedSplitPos <= fCurVerticalOffset + XFA_LAYOUT_FLOAT_PERCISION ||
-      *fProposedSplitPos > fCurVerticalOffset + pLayoutItem->m_sSize.height -
-                               XFA_LAYOUT_FLOAT_PERCISION) {
-    return false;
-  }
-
-  switch (pFormNode->GetIntact()) {
-    case XFA_AttributeEnum::None: {
-      bool bAnyChanged = false;
-      CXFA_Document* pDocument = pFormNode->GetDocument();
-      CXFA_FFNotify* pNotify = pDocument->GetNotify();
-      float fCurTopMargin = 0, fCurBottomMargin = 0;
-      CXFA_Margin* pMarginNode =
-          pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
-      if (pMarginNode && bCalculateMargin) {
-        fCurTopMargin = pMarginNode->JSObject()
-                            ->GetMeasure(XFA_Attribute::TopInset)
-                            .ToUnit(XFA_Unit::Pt);
-        fCurBottomMargin = pMarginNode->JSObject()
-                               ->GetMeasure(XFA_Attribute::BottomInset)
-                               .ToUnit(XFA_Unit::Pt);
-      }
-      bool bChanged = true;
-      while (bChanged) {
-        bChanged = false;
-        {
-          float fRelSplitPos = *fProposedSplitPos - fCurVerticalOffset;
-          if (pNotify->FindSplitPos(pFormNode, pLayoutItem->GetIndex(),
-                                    fRelSplitPos)) {
-            bAnyChanged = true;
-            bChanged = true;
-            *fProposedSplitPos = fCurVerticalOffset + fRelSplitPos;
-            *bAppChange = true;
-            if (*fProposedSplitPos <=
-                fCurVerticalOffset + XFA_LAYOUT_FLOAT_PERCISION) {
-              return true;
-            }
-          }
-        }
-        float fRelSplitPos = *fProposedSplitPos - fCurBottomMargin;
-        for (CXFA_ContentLayoutItem* pChildItem =
-                 (CXFA_ContentLayoutItem*)pLayoutItem->m_pFirstChild;
-             pChildItem;
-             pChildItem = (CXFA_ContentLayoutItem*)pChildItem->m_pNextSibling) {
-          float fChildOffset =
-              fCurVerticalOffset + fCurTopMargin + pChildItem->m_sPos.y;
-          bool bChange = false;
-          if (FindLayoutItemSplitPos(pChildItem, fChildOffset, &fRelSplitPos,
-                                     &bChange, bCalculateMargin)) {
-            if (fRelSplitPos - fChildOffset < XFA_LAYOUT_FLOAT_PERCISION &&
-                bChange) {
-              *fProposedSplitPos = fRelSplitPos - fCurTopMargin;
-            } else {
-              *fProposedSplitPos = fRelSplitPos + fCurBottomMargin;
-            }
-            bAnyChanged = true;
-            bChanged = true;
-            if (*fProposedSplitPos <=
-                fCurVerticalOffset + XFA_LAYOUT_FLOAT_PERCISION) {
-              return true;
-            }
-            if (bAnyChanged)
-              break;
-          }
-        }
-      }
-      return bAnyChanged;
-    }
-    case XFA_AttributeEnum::ContentArea:
-    case XFA_AttributeEnum::PageArea: {
-      *fProposedSplitPos = fCurVerticalOffset;
-      return true;
-    }
-    default:
-      return false;
-  }
-}
-
-CFX_PointF CalculatePositionedContainerPos(CXFA_Node* pNode,
-                                           const CFX_SizeF& size) {
-  XFA_AttributeEnum eAnchorType =
-      pNode->JSObject()->GetEnum(XFA_Attribute::AnchorType);
-  int32_t nAnchorType = 0;
-  switch (eAnchorType) {
-    case XFA_AttributeEnum::TopLeft:
-      nAnchorType = 0;
-      break;
-    case XFA_AttributeEnum::TopCenter:
-      nAnchorType = 1;
-      break;
-    case XFA_AttributeEnum::TopRight:
-      nAnchorType = 2;
-      break;
-    case XFA_AttributeEnum::MiddleLeft:
-      nAnchorType = 3;
-      break;
-    case XFA_AttributeEnum::MiddleCenter:
-      nAnchorType = 4;
-      break;
-    case XFA_AttributeEnum::MiddleRight:
-      nAnchorType = 5;
-      break;
-    case XFA_AttributeEnum::BottomLeft:
-      nAnchorType = 6;
-      break;
-    case XFA_AttributeEnum::BottomCenter:
-      nAnchorType = 7;
-      break;
-    case XFA_AttributeEnum::BottomRight:
-      nAnchorType = 8;
-      break;
-    default:
-      break;
-  }
-  static const uint8_t nNextPos[4][9] = {{0, 1, 2, 3, 4, 5, 6, 7, 8},
-                                         {6, 3, 0, 7, 4, 1, 8, 5, 2},
-                                         {8, 7, 6, 5, 4, 3, 2, 1, 0},
-                                         {2, 5, 8, 1, 4, 7, 0, 3, 6}};
-
-  CFX_PointF pos(
-      pNode->JSObject()->GetMeasure(XFA_Attribute::X).ToUnit(XFA_Unit::Pt),
-      pNode->JSObject()->GetMeasure(XFA_Attribute::Y).ToUnit(XFA_Unit::Pt));
-  int32_t nRotate =
-      XFA_MapRotation(pNode->JSObject()->GetInteger(XFA_Attribute::Rotate)) /
-      90;
-  int32_t nAbsoluteAnchorType = nNextPos[nRotate][nAnchorType];
-  switch (nAbsoluteAnchorType / 3) {
-    case 1:
-      pos.y -= size.height / 2;
-      break;
-    case 2:
-      pos.y -= size.height;
-      break;
-    default:
-      break;
-  }
-  switch (nAbsoluteAnchorType % 3) {
-    case 1:
-      pos.x -= size.width / 2;
-      break;
-    case 2:
-      pos.x -= size.width;
-      break;
-    default:
-      break;
-  }
-  return pos;
-}
-
-}  // namespace
-
-CXFA_ItemLayoutProcessor::CXFA_ItemLayoutProcessor(CXFA_Node* pNode,
-                                                   CXFA_LayoutPageMgr* pPageMgr)
-    : m_pFormNode(pNode),
-      m_pLayoutItem(nullptr),
-      m_pCurChildNode(XFA_LAYOUT_INVALIDNODE),
-      m_fUsedSize(0),
-      m_pPageMgr(pPageMgr),
-      m_bBreakPending(true),
-      m_fLastRowWidth(0),
-      m_fLastRowY(0),
-      m_bUseInheriated(false),
-      m_ePreProcessRs(XFA_ItemLayoutProcessorResult::Done),
-      m_bKeepBreakFinish(false),
-      m_bIsProcessKeep(false),
-      m_pKeepHeadNode(nullptr),
-      m_pKeepTailNode(nullptr),
-      m_pOldLayoutItem(nullptr),
-      m_pCurChildPreprocessor(nullptr),
-      m_nCurChildNodeStage(XFA_ItemLayoutProcessorStages::None),
-      m_fWidthLimite(0),
-      m_bHasAvailHeight(true) {
-  ASSERT(m_pFormNode && (m_pFormNode->IsContainerNode() ||
-                         m_pFormNode->GetElementType() == XFA_Element::Form));
-  m_pOldLayoutItem = static_cast<CXFA_ContentLayoutItem*>(
-      m_pFormNode->JSObject()->GetLayoutItem());
-}
-
-CXFA_ItemLayoutProcessor::~CXFA_ItemLayoutProcessor() {}
-
-CXFA_ContentLayoutItem* CXFA_ItemLayoutProcessor::CreateContentLayoutItem(
-    CXFA_Node* pFormNode) {
-  if (!pFormNode)
-    return nullptr;
-
-  CXFA_ContentLayoutItem* pLayoutItem = nullptr;
-  if (m_pOldLayoutItem) {
-    pLayoutItem = m_pOldLayoutItem;
-    m_pOldLayoutItem = m_pOldLayoutItem->m_pNext;
-    return pLayoutItem;
-  }
-  pLayoutItem =
-      pFormNode->GetDocument()->GetNotify()->OnCreateContentLayoutItem(
-          pFormNode);
-  CXFA_ContentLayoutItem* pPrevLayoutItem =
-      static_cast<CXFA_ContentLayoutItem*>(
-          pFormNode->JSObject()->GetLayoutItem());
-  if (pPrevLayoutItem) {
-    while (pPrevLayoutItem->m_pNext)
-      pPrevLayoutItem = pPrevLayoutItem->m_pNext;
-
-    pPrevLayoutItem->m_pNext = pLayoutItem;
-    pLayoutItem->m_pPrev = pPrevLayoutItem;
-  } else {
-    pFormNode->JSObject()->SetLayoutItem(pLayoutItem);
-  }
-  return pLayoutItem;
-}
-
-float CXFA_ItemLayoutProcessor::FindSplitPos(float fProposedSplitPos) {
-  ASSERT(m_pLayoutItem);
-  XFA_AttributeEnum eLayout = m_pFormNode->JSObject()
-                                  ->TryEnum(XFA_Attribute::Layout, true)
-                                  .value_or(XFA_AttributeEnum::Position);
-  bool bCalculateMargin = eLayout != XFA_AttributeEnum::Position;
-  while (fProposedSplitPos > XFA_LAYOUT_FLOAT_PERCISION) {
-    bool bAppChange = false;
-    if (!FindLayoutItemSplitPos(m_pLayoutItem, 0, &fProposedSplitPos,
-                                &bAppChange, bCalculateMargin)) {
-      break;
-    }
-  }
-  return fProposedSplitPos;
-}
-
-void CXFA_ItemLayoutProcessor::SplitLayoutItem(
-    CXFA_ContentLayoutItem* pLayoutItem,
-    CXFA_ContentLayoutItem* pSecondParent,
-    float fSplitPos) {
-  float fCurTopMargin = 0, fCurBottomMargin = 0;
-  XFA_AttributeEnum eLayout = m_pFormNode->JSObject()
-                                  ->TryEnum(XFA_Attribute::Layout, true)
-                                  .value_or(XFA_AttributeEnum::Position);
-  bool bCalculateMargin = true;
-  if (eLayout == XFA_AttributeEnum::Position)
-    bCalculateMargin = false;
-
-  CXFA_Margin* pMarginNode =
-      pLayoutItem->m_pFormNode->GetFirstChildByClass<CXFA_Margin>(
-          XFA_Element::Margin);
-  if (pMarginNode && bCalculateMargin) {
-    fCurTopMargin = pMarginNode->JSObject()
-                        ->GetMeasure(XFA_Attribute::TopInset)
-                        .ToUnit(XFA_Unit::Pt);
-    fCurBottomMargin = pMarginNode->JSObject()
-                           ->GetMeasure(XFA_Attribute::BottomInset)
-                           .ToUnit(XFA_Unit::Pt);
-  }
-
-  CXFA_ContentLayoutItem* pSecondLayoutItem = nullptr;
-  if (m_pCurChildPreprocessor &&
-      m_pCurChildPreprocessor->m_pFormNode == pLayoutItem->m_pFormNode) {
-    pSecondLayoutItem = m_pCurChildPreprocessor->CreateContentLayoutItem(
-        pLayoutItem->m_pFormNode);
-  } else {
-    pSecondLayoutItem = CreateContentLayoutItem(pLayoutItem->m_pFormNode);
-  }
-  pSecondLayoutItem->m_sPos.x = pLayoutItem->m_sPos.x;
-  pSecondLayoutItem->m_sSize.width = pLayoutItem->m_sSize.width;
-  pSecondLayoutItem->m_sPos.y = 0;
-  pSecondLayoutItem->m_sSize.height = pLayoutItem->m_sSize.height - fSplitPos;
-  pLayoutItem->m_sSize.height -= pSecondLayoutItem->m_sSize.height;
-  if (pLayoutItem->m_pFirstChild)
-    pSecondLayoutItem->m_sSize.height += fCurTopMargin;
-
-  if (pSecondParent) {
-    pSecondParent->AddChild(pSecondLayoutItem);
-    if (fCurTopMargin > 0 && pLayoutItem->m_pFirstChild) {
-      pSecondParent->m_sSize.height += fCurTopMargin;
-      CXFA_ContentLayoutItem* pParentItem =
-          (CXFA_ContentLayoutItem*)pSecondParent->m_pParent;
-      while (pParentItem) {
-        pParentItem->m_sSize.height += fCurTopMargin;
-        pParentItem = (CXFA_ContentLayoutItem*)pParentItem->m_pParent;
-      }
-    }
-  } else {
-    pSecondLayoutItem->m_pParent = pLayoutItem->m_pParent;
-    pSecondLayoutItem->m_pNextSibling = pLayoutItem->m_pNextSibling;
-    pLayoutItem->m_pNextSibling = pSecondLayoutItem;
-  }
-
-  CXFA_ContentLayoutItem* pChildren =
-      (CXFA_ContentLayoutItem*)pLayoutItem->m_pFirstChild;
-  pLayoutItem->m_pFirstChild = nullptr;
-
-  float lHeightForKeep = 0;
-  float fAddMarginHeight = 0;
-  std::vector<CXFA_ContentLayoutItem*> keepLayoutItems;
-  for (CXFA_ContentLayoutItem *pChildItem = pChildren, *pChildNext = nullptr;
-       pChildItem; pChildItem = pChildNext) {
-    pChildNext = (CXFA_ContentLayoutItem*)pChildItem->m_pNextSibling;
-    pChildItem->m_pNextSibling = nullptr;
-    if (fSplitPos <= fCurTopMargin + pChildItem->m_sPos.y + fCurBottomMargin +
-                         XFA_LAYOUT_FLOAT_PERCISION) {
-      if (!ExistContainerKeep(pChildItem->m_pFormNode, true)) {
-        pChildItem->m_sPos.y -= fSplitPos - fCurBottomMargin;
-        pChildItem->m_sPos.y += lHeightForKeep;
-        pChildItem->m_sPos.y += fAddMarginHeight;
-        pSecondLayoutItem->AddChild(pChildItem);
-        continue;
-      }
-      if (lHeightForKeep < XFA_LAYOUT_FLOAT_PERCISION) {
-        for (auto* pPreItem : keepLayoutItems) {
-          pLayoutItem->RemoveChild(pPreItem);
-          pPreItem->m_sPos.y -= fSplitPos;
-          if (pPreItem->m_sPos.y < 0)
-            pPreItem->m_sPos.y = 0;
-          if (pPreItem->m_sPos.y + pPreItem->m_sSize.height > lHeightForKeep) {
-            pPreItem->m_sPos.y = lHeightForKeep;
-            lHeightForKeep += pPreItem->m_sSize.height;
-            pSecondLayoutItem->m_sSize.height += pPreItem->m_sSize.height;
-            if (pSecondParent)
-              pSecondParent->m_sSize.height += pPreItem->m_sSize.height;
-          }
-          pSecondLayoutItem->AddChild(pPreItem);
-        }
-      }
-      pChildItem->m_sPos.y -= fSplitPos;
-      pChildItem->m_sPos.y += lHeightForKeep;
-      pChildItem->m_sPos.y += fAddMarginHeight;
-      pSecondLayoutItem->AddChild(pChildItem);
-      continue;
-    }
-    if (fSplitPos + XFA_LAYOUT_FLOAT_PERCISION >=
-        fCurTopMargin + fCurBottomMargin + pChildItem->m_sPos.y +
-            pChildItem->m_sSize.height) {
-      pLayoutItem->AddChild(pChildItem);
-      if (ExistContainerKeep(pChildItem->m_pFormNode, false))
-        keepLayoutItems.push_back(pChildItem);
-      else
-        keepLayoutItems.clear();
-      continue;
-    }
-
-    float fOldHeight = pSecondLayoutItem->m_sSize.height;
-    SplitLayoutItem(
-        pChildItem, pSecondLayoutItem,
-        fSplitPos - fCurTopMargin - fCurBottomMargin - pChildItem->m_sPos.y);
-    fAddMarginHeight = pSecondLayoutItem->m_sSize.height - fOldHeight;
-    pLayoutItem->AddChild(pChildItem);
-  }
-}
-
-void CXFA_ItemLayoutProcessor::SplitLayoutItem(float fSplitPos) {
-  ASSERT(m_pLayoutItem);
-  SplitLayoutItem(m_pLayoutItem, nullptr, fSplitPos);
-}
-
-CXFA_ContentLayoutItem* CXFA_ItemLayoutProcessor::ExtractLayoutItem() {
-  CXFA_ContentLayoutItem* pLayoutItem = m_pLayoutItem;
-  if (pLayoutItem) {
-    m_pLayoutItem =
-        static_cast<CXFA_ContentLayoutItem*>(pLayoutItem->m_pNextSibling);
-    pLayoutItem->m_pNextSibling = nullptr;
-  }
-
-  if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Done ||
-      !ToContentLayoutItem(m_pOldLayoutItem)) {
-    return pLayoutItem;
-  }
-
-  if (m_pOldLayoutItem->m_pPrev)
-    m_pOldLayoutItem->m_pPrev->m_pNext = nullptr;
-
-  CXFA_FFNotify* pNotify =
-      m_pOldLayoutItem->m_pFormNode->GetDocument()->GetNotify();
-  CXFA_LayoutProcessor* pDocLayout =
-      m_pOldLayoutItem->m_pFormNode->GetDocument()->GetDocLayout();
-  CXFA_ContentLayoutItem* pOldLayoutItem = m_pOldLayoutItem;
-  while (pOldLayoutItem) {
-    CXFA_ContentLayoutItem* pNextOldLayoutItem = pOldLayoutItem->m_pNext;
-    pNotify->OnLayoutItemRemoving(pDocLayout, pOldLayoutItem);
-    if (pOldLayoutItem->m_pParent)
-      pOldLayoutItem->m_pParent->RemoveChild(pOldLayoutItem);
-
-    delete pOldLayoutItem;
-    pOldLayoutItem = pNextOldLayoutItem;
-  }
-  m_pOldLayoutItem = nullptr;
-  return pLayoutItem;
-}
-
-void CXFA_ItemLayoutProcessor::GotoNextContainerNode(
-    CXFA_Node*& pCurActionNode,
-    XFA_ItemLayoutProcessorStages& nCurStage,
-    CXFA_Node* pParentContainer,
-    bool bUsePageBreak) {
-  CXFA_Node* pEntireContainer = pParentContainer;
-  CXFA_Node* pChildContainer = XFA_LAYOUT_INVALIDNODE;
-  switch (nCurStage) {
-    case XFA_ItemLayoutProcessorStages::BreakBefore:
-    case XFA_ItemLayoutProcessorStages::BreakAfter: {
-      pChildContainer = pCurActionNode->GetParent();
-      break;
-    }
-    case XFA_ItemLayoutProcessorStages::Keep:
-    case XFA_ItemLayoutProcessorStages::Container:
-      pChildContainer = pCurActionNode;
-      break;
-    default:
-      pChildContainer = XFA_LAYOUT_INVALIDNODE;
-      break;
-  }
-
-  switch (nCurStage) {
-    case XFA_ItemLayoutProcessorStages::Keep: {
-      CXFA_Node* pBreakAfterNode = pChildContainer->GetFirstChild();
-      if (!m_bKeepBreakFinish &&
-          FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage, false)) {
-        return;
-      }
-      goto CheckNextChildContainer;
-    }
-    case XFA_ItemLayoutProcessorStages::None: {
-      pCurActionNode = XFA_LAYOUT_INVALIDNODE;
-      case XFA_ItemLayoutProcessorStages::BookendLeader:
-        for (CXFA_Node* pBookendNode = pCurActionNode == XFA_LAYOUT_INVALIDNODE
-                                           ? pEntireContainer->GetFirstChild()
-                                           : pCurActionNode->GetNextSibling();
-             pBookendNode; pBookendNode = pBookendNode->GetNextSibling()) {
-          switch (pBookendNode->GetElementType()) {
-            case XFA_Element::Bookend:
-            case XFA_Element::Break:
-              pCurActionNode = pBookendNode;
-              nCurStage = XFA_ItemLayoutProcessorStages::BookendLeader;
-              return;
-            default:
-              break;
-          }
-        }
-    }
-      {
-        pCurActionNode = XFA_LAYOUT_INVALIDNODE;
-        case XFA_ItemLayoutProcessorStages::BreakBefore:
-          if (pCurActionNode != XFA_LAYOUT_INVALIDNODE) {
-            CXFA_Node* pBreakBeforeNode = pCurActionNode->GetNextSibling();
-            if (!m_bKeepBreakFinish &&
-                FindBreakNode(pBreakBeforeNode, pCurActionNode, &nCurStage,
-                              true)) {
-              return;
-            }
-            if (m_bIsProcessKeep) {
-              if (ProcessKeepNodesForBreakBefore(pCurActionNode, nCurStage,
-                                                 pChildContainer)) {
-                return;
-              }
-              goto CheckNextChildContainer;
-            }
-            pCurActionNode = pChildContainer;
-            nCurStage = XFA_ItemLayoutProcessorStages::Container;
-            return;
-          }
-          goto CheckNextChildContainer;
-      }
-    case XFA_ItemLayoutProcessorStages::Container: {
-      pCurActionNode = XFA_LAYOUT_INVALIDNODE;
-      case XFA_ItemLayoutProcessorStages::BreakAfter: {
-        if (pCurActionNode == XFA_LAYOUT_INVALIDNODE) {
-          CXFA_Node* pBreakAfterNode = pChildContainer->GetFirstChild();
-          if (!m_bKeepBreakFinish &&
-              FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage,
-                            false)) {
-            return;
-          }
-        } else {
-          CXFA_Node* pBreakAfterNode = pCurActionNode->GetNextSibling();
-          if (FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage,
-                            false)) {
-            return;
-          }
-        }
-        goto CheckNextChildContainer;
-      }
-    }
-
-    CheckNextChildContainer : {
-      CXFA_Node* pNextChildContainer =
-          pChildContainer == XFA_LAYOUT_INVALIDNODE
-              ? pEntireContainer->GetFirstContainerChild()
-              : pChildContainer->GetNextContainerSibling();
-      while (pNextChildContainer &&
-             pNextChildContainer->IsLayoutGeneratedNode()) {
-        CXFA_Node* pSaveNode = pNextChildContainer;
-        pNextChildContainer = pNextChildContainer->GetNextContainerSibling();
-        if (pSaveNode->IsUnusedNode())
-          DeleteLayoutGeneratedNode(pSaveNode);
-      }
-      if (!pNextChildContainer)
-        goto NoMoreChildContainer;
-
-      bool bLastKeep = false;
-      if (ProcessKeepNodesForCheckNext(pCurActionNode, nCurStage,
-                                       pNextChildContainer, bLastKeep)) {
-        return;
-      }
-      if (!m_bKeepBreakFinish && !bLastKeep &&
-          FindBreakNode(pNextChildContainer->GetFirstChild(), pCurActionNode,
-                        &nCurStage, true)) {
-        return;
-      }
-      pCurActionNode = pNextChildContainer;
-      if (m_bIsProcessKeep)
-        nCurStage = XFA_ItemLayoutProcessorStages::Keep;
-      else
-        nCurStage = XFA_ItemLayoutProcessorStages::Container;
-      return;
-    }
-
-    NoMoreChildContainer : {
-      pCurActionNode = XFA_LAYOUT_INVALIDNODE;
-      case XFA_ItemLayoutProcessorStages::BookendTrailer:
-        for (CXFA_Node* pBookendNode = pCurActionNode == XFA_LAYOUT_INVALIDNODE
-                                           ? pEntireContainer->GetFirstChild()
-                                           : pCurActionNode->GetNextSibling();
-             pBookendNode; pBookendNode = pBookendNode->GetNextSibling()) {
-          switch (pBookendNode->GetElementType()) {
-            case XFA_Element::Bookend:
-            case XFA_Element::Break:
-              pCurActionNode = pBookendNode;
-              nCurStage = XFA_ItemLayoutProcessorStages::BookendTrailer;
-              return;
-            default:
-              break;
-          }
-        }
-    }
-    default:
-      pCurActionNode = nullptr;
-      nCurStage = XFA_ItemLayoutProcessorStages::Done;
-  }
-}
-
-bool CXFA_ItemLayoutProcessor::ProcessKeepNodesForCheckNext(
-    CXFA_Node*& pCurActionNode,
-    XFA_ItemLayoutProcessorStages& nCurStage,
-    CXFA_Node*& pNextContainer,
-    bool& bLastKeepNode) {
-  const bool bCanSplit = pNextContainer->GetIntact() == XFA_AttributeEnum::None;
-  bool bNextKeep = false;
-  if (ExistContainerKeep(pNextContainer, false))
-    bNextKeep = true;
-
-  if (bNextKeep && !bCanSplit) {
-    if (!m_bIsProcessKeep && !m_bKeepBreakFinish) {
-      m_pKeepHeadNode = pNextContainer;
-      m_bIsProcessKeep = true;
-    }
-    return false;
-  }
-
-  if (m_bIsProcessKeep && m_pKeepHeadNode) {
-    m_pKeepTailNode = pNextContainer;
-    if (!m_bKeepBreakFinish &&
-        FindBreakNode(pNextContainer->GetFirstChild(), pCurActionNode,
-                      &nCurStage, true)) {
-      return true;
-    }
-
-    pNextContainer = m_pKeepHeadNode;
-    m_bKeepBreakFinish = true;
-    m_pKeepHeadNode = nullptr;
-    m_pKeepTailNode = nullptr;
-    m_bIsProcessKeep = false;
-  } else {
-    if (m_bKeepBreakFinish)
-      bLastKeepNode = true;
-    m_bKeepBreakFinish = false;
-  }
-
-  return false;
-}
-
-bool CXFA_ItemLayoutProcessor::ProcessKeepNodesForBreakBefore(
-    CXFA_Node*& pCurActionNode,
-    XFA_ItemLayoutProcessorStages& nCurStage,
-    CXFA_Node* pContainerNode) {
-  if (m_pKeepTailNode == pContainerNode) {
-    pCurActionNode = m_pKeepHeadNode;
-    m_bKeepBreakFinish = true;
-    m_pKeepHeadNode = nullptr;
-    m_pKeepTailNode = nullptr;
-    m_bIsProcessKeep = false;
-    nCurStage = XFA_ItemLayoutProcessorStages::Container;
-    return true;
-  }
-
-  CXFA_Node* pBreakAfterNode = pContainerNode->GetFirstChild();
-  return FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage, false);
-}
-
-bool XFA_ItemLayoutProcessor_IsTakingSpace(CXFA_Node* pNode) {
-  XFA_AttributeEnum ePresence = pNode->JSObject()
-                                    ->TryEnum(XFA_Attribute::Presence, true)
-                                    .value_or(XFA_AttributeEnum::Visible);
-  return ePresence == XFA_AttributeEnum::Visible ||
-         ePresence == XFA_AttributeEnum::Invisible;
-}
-
-bool CXFA_ItemLayoutProcessor::IncrementRelayoutNode(
-    CXFA_LayoutProcessor* pLayoutProcessor,
-    CXFA_Node* pNode,
-    CXFA_Node* pParentNode) {
-  return false;
-}
-
-void CXFA_ItemLayoutProcessor::DoLayoutPageArea(
-    CXFA_ContainerLayoutItem* pPageAreaLayoutItem) {
-  CXFA_Node* pFormNode = pPageAreaLayoutItem->m_pFormNode;
-  CXFA_Node* pCurChildNode = XFA_LAYOUT_INVALIDNODE;
-  XFA_ItemLayoutProcessorStages nCurChildNodeStage =
-      XFA_ItemLayoutProcessorStages::None;
-  CXFA_LayoutItem* pBeforeItem = nullptr;
-  for (GotoNextContainerNode(pCurChildNode, nCurChildNodeStage, pFormNode,
-                             false);
-       pCurChildNode; GotoNextContainerNode(pCurChildNode, nCurChildNodeStage,
-                                            pFormNode, false)) {
-    if (nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Container)
-      continue;
-    if (pCurChildNode->GetElementType() == XFA_Element::Variables)
-      continue;
-
-    auto pProcessor =
-        pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pCurChildNode, nullptr);
-    pProcessor->DoLayout(false, FLT_MAX, FLT_MAX, nullptr);
-    if (!pProcessor->HasLayoutItem())
-      continue;
-
-    pProcessor->SetCurrentComponentPos(CalculatePositionedContainerPos(
-        pCurChildNode, pProcessor->GetCurrentComponentSize()));
-    CXFA_LayoutItem* pProcessItem = pProcessor->ExtractLayoutItem();
-    if (!pBeforeItem)
-      pPageAreaLayoutItem->AddHeadChild(pProcessItem);
-    else
-      pPageAreaLayoutItem->InsertChild(pBeforeItem, pProcessItem);
-
-    pBeforeItem = pProcessItem;
-  }
-
-  pBeforeItem = nullptr;
-  CXFA_LayoutItem* pLayoutItem = pPageAreaLayoutItem->m_pFirstChild;
-  while (pLayoutItem) {
-    if (!pLayoutItem->IsContentLayoutItem() ||
-        pLayoutItem->m_pFormNode->GetElementType() != XFA_Element::Draw) {
-      pLayoutItem = pLayoutItem->m_pNextSibling;
-      continue;
-    }
-    if (pLayoutItem->m_pFormNode->GetElementType() != XFA_Element::Draw)
-      continue;
-
-    CXFA_LayoutItem* pNextLayoutItem = pLayoutItem->m_pNextSibling;
-    pPageAreaLayoutItem->RemoveChild(pLayoutItem);
-    if (!pBeforeItem)
-      pPageAreaLayoutItem->AddHeadChild(pLayoutItem);
-    else
-      pPageAreaLayoutItem->InsertChild(pBeforeItem, pLayoutItem);
-
-    pBeforeItem = pLayoutItem;
-    pLayoutItem = pNextLayoutItem;
-  }
-}
-
-void CXFA_ItemLayoutProcessor::DoLayoutPositionedContainer(
-    CXFA_LayoutContext* pContext) {
-  if (m_pLayoutItem)
-    return;
-
-  m_pLayoutItem = CreateContentLayoutItem(m_pFormNode);
-  bool bIgnoreXY = (m_pFormNode->JSObject()
-                        ->TryEnum(XFA_Attribute::Layout, true)
-                        .value_or(XFA_AttributeEnum::Position) !=
-                    XFA_AttributeEnum::Position);
-  bool bContainerWidthAutoSize = true;
-  bool bContainerHeightAutoSize = true;
-  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
-      m_pFormNode, &bContainerWidthAutoSize, &bContainerHeightAutoSize);
-
-  float fContentCalculatedWidth = 0;
-  float fContentCalculatedHeight = 0;
-  float fHiddenContentCalculatedWidth = 0;
-  float fHiddenContentCalculatedHeight = 0;
-  if (m_pCurChildNode == XFA_LAYOUT_INVALIDNODE) {
-    GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode,
-                          false);
-  }
-
-  int32_t iColIndex = 0;
-  for (; m_pCurChildNode; GotoNextContainerNode(
-           m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, false)) {
-    if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Container)
-      continue;
-    if (m_pCurChildNode->GetElementType() == XFA_Element::Variables)
-      continue;
-
-    auto pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-        m_pCurChildNode, m_pPageMgr);
-    if (pContext && pContext->m_prgSpecifiedColumnWidths) {
-      int32_t iColSpan =
-          m_pCurChildNode->JSObject()->GetInteger(XFA_Attribute::ColSpan);
-      if (iColSpan <= pdfium::CollectionSize<int32_t>(
-                          *pContext->m_prgSpecifiedColumnWidths) -
-                          iColIndex) {
-        pContext->m_fCurColumnWidth = 0;
-        pContext->m_bCurColumnWidthAvaiable = true;
-        if (iColSpan == -1) {
-          iColSpan = pdfium::CollectionSize<int32_t>(
-              *pContext->m_prgSpecifiedColumnWidths);
-        }
-        for (int32_t i = 0; iColIndex + i < iColSpan; ++i) {
-          pContext->m_fCurColumnWidth +=
-              (*pContext->m_prgSpecifiedColumnWidths)[iColIndex + i];
-        }
-        if (pContext->m_fCurColumnWidth == 0)
-          pContext->m_bCurColumnWidthAvaiable = false;
-
-        iColIndex += iColSpan >= 0 ? iColSpan : 0;
-      }
-    }
-
-    pProcessor->DoLayout(false, FLT_MAX, FLT_MAX, pContext);
-    if (!pProcessor->HasLayoutItem())
-      continue;
-
-    CFX_SizeF size = pProcessor->GetCurrentComponentSize();
-    bool bChangeParentSize = false;
-    if (XFA_ItemLayoutProcessor_IsTakingSpace(m_pCurChildNode))
-      bChangeParentSize = true;
-
-    CFX_PointF absolutePos;
-    if (!bIgnoreXY)
-      absolutePos = CalculatePositionedContainerPos(m_pCurChildNode, size);
-
-    pProcessor->SetCurrentComponentPos(absolutePos);
-    if (bContainerWidthAutoSize) {
-      float fChildSuppliedWidth = absolutePos.x + size.width;
-      if (bChangeParentSize) {
-        fContentCalculatedWidth =
-            std::max(fContentCalculatedWidth, fChildSuppliedWidth);
-      } else {
-        if (fHiddenContentCalculatedWidth < fChildSuppliedWidth &&
-            m_pCurChildNode->GetElementType() != XFA_Element::Subform) {
-          fHiddenContentCalculatedWidth = fChildSuppliedWidth;
-        }
-      }
-    }
-
-    if (bContainerHeightAutoSize) {
-      float fChildSuppliedHeight = absolutePos.y + size.height;
-      if (bChangeParentSize) {
-        fContentCalculatedHeight =
-            std::max(fContentCalculatedHeight, fChildSuppliedHeight);
-      } else {
-        if (fHiddenContentCalculatedHeight < fChildSuppliedHeight &&
-            m_pCurChildNode->GetElementType() != XFA_Element::Subform) {
-          fHiddenContentCalculatedHeight = fChildSuppliedHeight;
-        }
-      }
-    }
-    m_pLayoutItem->AddChild(pProcessor->ExtractLayoutItem());
-  }
-
-  XFA_VERSION eVersion = m_pFormNode->GetDocument()->GetCurVersionMode();
-  if (fContentCalculatedWidth == 0 && eVersion < XFA_VERSION_207)
-    fContentCalculatedWidth = fHiddenContentCalculatedWidth;
-  if (fContentCalculatedHeight == 0 && eVersion < XFA_VERSION_207)
-    fContentCalculatedHeight = fHiddenContentCalculatedHeight;
-
-  containerSize = CalculateContainerComponentSizeFromContentSize(
-      m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth,
-      bContainerHeightAutoSize, fContentCalculatedHeight, containerSize);
-  SetCurrentComponentSize(containerSize);
-}
-
-void CXFA_ItemLayoutProcessor::DoLayoutTableContainer(CXFA_Node* pLayoutNode) {
-  if (m_pLayoutItem)
-    return;
-  if (!pLayoutNode)
-    pLayoutNode = m_pFormNode;
-
-  ASSERT(m_pCurChildNode == XFA_LAYOUT_INVALIDNODE);
-
-  m_pLayoutItem = CreateContentLayoutItem(m_pFormNode);
-  bool bContainerWidthAutoSize = true;
-  bool bContainerHeightAutoSize = true;
-  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
-      m_pFormNode, &bContainerWidthAutoSize, &bContainerHeightAutoSize);
-  float fContentCalculatedWidth = 0;
-  float fContentCalculatedHeight = 0;
-  CXFA_Margin* pMarginNode =
-      m_pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
-  float fLeftInset = 0;
-  float fRightInset = 0;
-  if (pMarginNode) {
-    fLeftInset = pMarginNode->JSObject()
-                     ->GetMeasure(XFA_Attribute::LeftInset)
-                     .ToUnit(XFA_Unit::Pt);
-    fRightInset = pMarginNode->JSObject()
-                      ->GetMeasure(XFA_Attribute::RightInset)
-                      .ToUnit(XFA_Unit::Pt);
-  }
-
-  float fContentWidthLimit =
-      bContainerWidthAutoSize ? FLT_MAX
-                              : containerSize.width - fLeftInset - fRightInset;
-  WideString wsColumnWidths =
-      pLayoutNode->JSObject()->GetCData(XFA_Attribute::ColumnWidths);
-  if (!wsColumnWidths.IsEmpty()) {
-    auto widths = SeparateStringW(wsColumnWidths.c_str(),
-                                  wsColumnWidths.GetLength(), L' ');
-    for (auto& width : widths) {
-      width.TrimLeft(L' ');
-      if (width.IsEmpty())
-        continue;
-
-      m_rgSpecifiedColumnWidths.push_back(
-          CXFA_Measurement(width.AsStringView()).ToUnit(XFA_Unit::Pt));
-    }
-  }
-
-  int32_t iSpecifiedColumnCount =
-      pdfium::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths);
-  CXFA_LayoutContext layoutContext;
-  layoutContext.m_prgSpecifiedColumnWidths = &m_rgSpecifiedColumnWidths;
-  CXFA_LayoutContext* pLayoutContext =
-      iSpecifiedColumnCount > 0 ? &layoutContext : nullptr;
-  if (m_pCurChildNode == XFA_LAYOUT_INVALIDNODE) {
-    GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode,
-                          false);
-  }
-
-  for (; m_pCurChildNode; GotoNextContainerNode(
-           m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, false)) {
-    layoutContext.m_bCurColumnWidthAvaiable = false;
-    layoutContext.m_fCurColumnWidth = 0;
-    if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Container)
-      continue;
-
-    auto pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-        m_pCurChildNode, m_pPageMgr);
-    pProcessor->DoLayout(false, FLT_MAX, FLT_MAX, pLayoutContext);
-    if (!pProcessor->HasLayoutItem())
-      continue;
-
-    m_pLayoutItem->AddChild(pProcessor->ExtractLayoutItem());
-  }
-
-  int32_t iRowCount = 0;
-  int32_t iColCount = 0;
-  {
-    std::vector<CXFA_ContentLayoutItem*> rgRowItems;
-    std::vector<int32_t> rgRowItemsSpan;
-    std::vector<float> rgRowItemsWidth;
-    for (auto* pLayoutChild =
-             static_cast<CXFA_ContentLayoutItem*>(m_pLayoutItem->m_pFirstChild);
-         pLayoutChild; pLayoutChild = static_cast<CXFA_ContentLayoutItem*>(
-                           pLayoutChild->m_pNextSibling)) {
-      if (pLayoutChild->m_pFormNode->GetElementType() != XFA_Element::Subform)
-        continue;
-      if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutChild->m_pFormNode))
-        continue;
-
-      XFA_AttributeEnum eLayout =
-          pLayoutChild->m_pFormNode->JSObject()->GetEnum(XFA_Attribute::Layout);
-      if (eLayout != XFA_AttributeEnum::Row &&
-          eLayout != XFA_AttributeEnum::Rl_row) {
-        continue;
-      }
-      if (CXFA_ContentLayoutItem* pRowLayoutCell =
-              (CXFA_ContentLayoutItem*)pLayoutChild->m_pFirstChild) {
-        rgRowItems.push_back(pRowLayoutCell);
-        int32_t iColSpan = pRowLayoutCell->m_pFormNode->JSObject()->GetInteger(
-            XFA_Attribute::ColSpan);
-        rgRowItemsSpan.push_back(iColSpan);
-        rgRowItemsWidth.push_back(pRowLayoutCell->m_sSize.width);
-      }
-    }
-
-    iRowCount = pdfium::CollectionSize<int32_t>(rgRowItems);
-    iColCount = 0;
-    bool bMoreColumns = true;
-    while (bMoreColumns) {
-      bMoreColumns = false;
-      bool bAutoCol = false;
-      for (int32_t i = 0; i < iRowCount; i++) {
-        while (rgRowItems[i] && (rgRowItemsSpan[i] <= 0 ||
-                                 !XFA_ItemLayoutProcessor_IsTakingSpace(
-                                     rgRowItems[i]->m_pFormNode))) {
-          CXFA_ContentLayoutItem* pNewCell =
-              (CXFA_ContentLayoutItem*)rgRowItems[i]->m_pNextSibling;
-          if (rgRowItemsSpan[i] < 0 && XFA_ItemLayoutProcessor_IsTakingSpace(
-                                           rgRowItems[i]->m_pFormNode)) {
-            pNewCell = nullptr;
-          }
-          rgRowItems[i] = pNewCell;
-          rgRowItemsSpan[i] =
-              pNewCell ? pNewCell->m_pFormNode->JSObject()->GetInteger(
-                             XFA_Attribute::ColSpan)
-                       : 0;
-          rgRowItemsWidth[i] = pNewCell ? pNewCell->m_sSize.width : 0;
-        }
-        CXFA_ContentLayoutItem* pCell = rgRowItems[i];
-        if (!pCell)
-          continue;
-
-        bMoreColumns = true;
-        if (rgRowItemsSpan[i] != 1)
-          continue;
-
-        if (iColCount >= iSpecifiedColumnCount) {
-          int32_t c = iColCount + 1 - pdfium::CollectionSize<int32_t>(
-                                          m_rgSpecifiedColumnWidths);
-          for (int32_t j = 0; j < c; j++)
-            m_rgSpecifiedColumnWidths.push_back(0);
-        }
-        if (m_rgSpecifiedColumnWidths[iColCount] < XFA_LAYOUT_FLOAT_PERCISION)
-          bAutoCol = true;
-        if (bAutoCol &&
-            m_rgSpecifiedColumnWidths[iColCount] < rgRowItemsWidth[i]) {
-          m_rgSpecifiedColumnWidths[iColCount] = rgRowItemsWidth[i];
-        }
-      }
-
-      if (!bMoreColumns)
-        continue;
-
-      float fFinalColumnWidth = 0.0f;
-      if (pdfium::IndexInBounds(m_rgSpecifiedColumnWidths, iColCount))
-        fFinalColumnWidth = m_rgSpecifiedColumnWidths[iColCount];
-
-      for (int32_t i = 0; i < iRowCount; ++i) {
-        if (!rgRowItems[i])
-          continue;
-        --rgRowItemsSpan[i];
-        rgRowItemsWidth[i] -= fFinalColumnWidth;
-      }
-      ++iColCount;
-    }
-  }
-
-  float fCurrentRowY = 0;
-  for (CXFA_ContentLayoutItem* pLayoutChild =
-           (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild;
-       pLayoutChild;
-       pLayoutChild = (CXFA_ContentLayoutItem*)pLayoutChild->m_pNextSibling) {
-    if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutChild->m_pFormNode))
-      continue;
-
-    if (pLayoutChild->m_pFormNode->GetElementType() == XFA_Element::Subform) {
-      XFA_AttributeEnum eSubformLayout =
-          pLayoutChild->m_pFormNode->JSObject()->GetEnum(XFA_Attribute::Layout);
-      if (eSubformLayout == XFA_AttributeEnum::Row ||
-          eSubformLayout == XFA_AttributeEnum::Rl_row) {
-        RelocateTableRowCells(pLayoutChild, m_rgSpecifiedColumnWidths,
-                              eSubformLayout);
-      }
-    }
-
-    pLayoutChild->m_sPos.y = fCurrentRowY;
-    if (bContainerWidthAutoSize) {
-      pLayoutChild->m_sPos.x = 0;
-    } else {
-      switch (pLayoutChild->m_pFormNode->JSObject()->GetEnum(
-          XFA_Attribute::HAlign)) {
-        case XFA_AttributeEnum::Center:
-          pLayoutChild->m_sPos.x =
-              (fContentWidthLimit - pLayoutChild->m_sSize.width) / 2;
-          break;
-        case XFA_AttributeEnum::Right:
-          pLayoutChild->m_sPos.x =
-              fContentWidthLimit - pLayoutChild->m_sSize.width;
-          break;
-        case XFA_AttributeEnum::Left:
-        default:
-          pLayoutChild->m_sPos.x = 0;
-          break;
-      }
-    }
-
-    if (bContainerWidthAutoSize) {
-      float fChildSuppliedWidth =
-          pLayoutChild->m_sPos.x + pLayoutChild->m_sSize.width;
-      if (fContentWidthLimit < FLT_MAX &&
-          fContentWidthLimit > fChildSuppliedWidth) {
-        fChildSuppliedWidth = fContentWidthLimit;
-      }
-      fContentCalculatedWidth =
-          std::max(fContentCalculatedWidth, fChildSuppliedWidth);
-    }
-    fCurrentRowY += pLayoutChild->m_sSize.height;
-  }
-
-  if (bContainerHeightAutoSize)
-    fContentCalculatedHeight = std::max(fContentCalculatedHeight, fCurrentRowY);
-
-  containerSize = CalculateContainerComponentSizeFromContentSize(
-      m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth,
-      bContainerHeightAutoSize, fContentCalculatedHeight, containerSize);
-  SetCurrentComponentSize(containerSize);
-}
-
-bool CXFA_ItemLayoutProcessor::IsAddNewRowForTrailer(
-    CXFA_ContentLayoutItem* pTrailerItem) {
-  if (!pTrailerItem)
-    return false;
-
-  float fWidth = pTrailerItem->m_sSize.width;
-  XFA_AttributeEnum eLayout =
-      m_pFormNode->JSObject()->GetEnum(XFA_Attribute::Layout);
-  return eLayout == XFA_AttributeEnum::Tb || m_fWidthLimite <= fWidth;
-}
-
-float CXFA_ItemLayoutProcessor::InsertKeepLayoutItems() {
-  if (m_arrayKeepItems.empty())
-    return 0;
-
-  if (!m_pLayoutItem) {
-    m_pLayoutItem = CreateContentLayoutItem(m_pFormNode);
-    m_pLayoutItem->m_sSize.clear();
-  }
-
-  float fTotalHeight = 0;
-  for (auto iter = m_arrayKeepItems.rbegin(); iter != m_arrayKeepItems.rend();
-       iter++) {
-    AddLeaderAfterSplit(this, *iter);
-    fTotalHeight += (*iter)->m_sSize.height;
-  }
-  m_arrayKeepItems.clear();
-
-  return fTotalHeight;
-}
-
-bool CXFA_ItemLayoutProcessor::ProcessKeepForSplit(
-    CXFA_ItemLayoutProcessor* pParentProcessor,
-    CXFA_ItemLayoutProcessor* pChildProcessor,
-    XFA_ItemLayoutProcessorResult eRetValue,
-    std::vector<CXFA_ContentLayoutItem*>* rgCurLineLayoutItem,
-    float* fContentCurRowAvailWidth,
-    float* fContentCurRowHeight,
-    float* fContentCurRowY,
-    bool* bAddedItemInRow,
-    bool* bForceEndPage,
-    XFA_ItemLayoutProcessorResult* result) {
-  if (!pParentProcessor || !pChildProcessor)
-    return false;
-
-  if (pParentProcessor->m_pCurChildNode->GetIntact() ==
-          XFA_AttributeEnum::None &&
-      pChildProcessor->m_bHasAvailHeight)
-    return false;
-
-  if (!ExistContainerKeep(pParentProcessor->m_pCurChildNode, true))
-    return false;
-
-  CFX_SizeF childSize = pChildProcessor->GetCurrentComponentSize();
-  std::vector<CXFA_ContentLayoutItem*> keepLayoutItems;
-  if (pParentProcessor->JudgePutNextPage(pParentProcessor->m_pLayoutItem,
-                                         childSize.height, &keepLayoutItems)) {
-    m_arrayKeepItems.clear();
-
-    for (auto* item : keepLayoutItems) {
-      pParentProcessor->m_pLayoutItem->RemoveChild(item);
-      *fContentCurRowY -= item->m_sSize.height;
-      m_arrayKeepItems.push_back(item);
-    }
-    *bAddedItemInRow = true;
-    *bForceEndPage = true;
-    *result = XFA_ItemLayoutProcessorResult::PageFullBreak;
-    return true;
-  }
-
-  rgCurLineLayoutItem->push_back(pChildProcessor->ExtractLayoutItem());
-  *bAddedItemInRow = true;
-  *fContentCurRowAvailWidth -= childSize.width;
-  *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height);
-  *result = eRetValue;
-
-  return true;
-}
-
-bool CXFA_ItemLayoutProcessor::JudgePutNextPage(
-    CXFA_ContentLayoutItem* pParentLayoutItem,
-    float fChildHeight,
-    std::vector<CXFA_ContentLayoutItem*>* pKeepItems) {
-  if (!pParentLayoutItem)
-    return false;
-
-  float fItemsHeight = 0;
-  for (CXFA_ContentLayoutItem* pChildLayoutItem =
-           (CXFA_ContentLayoutItem*)pParentLayoutItem->m_pFirstChild;
-       pChildLayoutItem;
-       pChildLayoutItem =
-           (CXFA_ContentLayoutItem*)pChildLayoutItem->m_pNextSibling) {
-    if (ExistContainerKeep(pChildLayoutItem->m_pFormNode, false)) {
-      pKeepItems->push_back(pChildLayoutItem);
-      fItemsHeight += pChildLayoutItem->m_sSize.height;
-    } else {
-      pKeepItems->clear();
-      fItemsHeight = 0;
-    }
-  }
-  fItemsHeight += fChildHeight;
-  return m_pPageMgr->GetNextAvailContentHeight(fItemsHeight);
-}
-
-void CXFA_ItemLayoutProcessor::ProcessUnUseBinds(CXFA_Node* pFormNode) {
-  if (!pFormNode)
-    return;
-
-  CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode> sIterator(
-      pFormNode);
-  for (CXFA_Node* pNode = sIterator.MoveToNext(); pNode;
-       pNode = sIterator.MoveToNext()) {
-    if (pNode->IsContainerNode()) {
-      CXFA_Node* pBindNode = pNode->GetBindData();
-      if (pBindNode) {
-        pBindNode->RemoveBindItem(pNode);
-        pNode->SetBindingNode(nullptr);
-      }
-    }
-    pNode->SetFlag(XFA_NodeFlag_UnusedNode, true);
-  }
-}
-
-void CXFA_ItemLayoutProcessor::ProcessUnUseOverFlow(
-    CXFA_Node* pLeaderNode,
-    CXFA_Node* pTrailerNode,
-    CXFA_ContentLayoutItem* pTrailerItem,
-    CXFA_Node* pFormNode) {
-  ProcessUnUseBinds(pLeaderNode);
-  ProcessUnUseBinds(pTrailerNode);
-  if (!pFormNode)
-    return;
-
-  if (pFormNode->GetElementType() == XFA_Element::Overflow ||
-      pFormNode->GetElementType() == XFA_Element::Break) {
-    pFormNode = pFormNode->GetParent();
-  }
-  if (pLeaderNode && pFormNode)
-    pFormNode->RemoveChild(pLeaderNode, true);
-  if (pTrailerNode && pFormNode)
-    pFormNode->RemoveChild(pTrailerNode, true);
-  if (pTrailerItem)
-    XFA_ReleaseLayoutItem(pTrailerItem);
-}
-
-XFA_ItemLayoutProcessorResult CXFA_ItemLayoutProcessor::DoLayoutFlowedContainer(
-    bool bUseBreakControl,
-    XFA_AttributeEnum eFlowStrategy,
-    float fHeightLimit,
-    float fRealHeight,
-    CXFA_LayoutContext* pContext,
-    bool bRootForceTb) {
-  m_bHasAvailHeight = true;
-  bool bBreakDone = false;
-  bool bContainerWidthAutoSize = true;
-  bool bContainerHeightAutoSize = true;
-  bool bForceEndPage = false;
-  bool bIsManualBreak = false;
-  if (m_pCurChildPreprocessor) {
-    m_pCurChildPreprocessor->m_ePreProcessRs =
-        XFA_ItemLayoutProcessorResult::Done;
-  }
-
-  CFX_SizeF containerSize = CalculateContainerSpecifiedSize(
-      m_pFormNode, &bContainerWidthAutoSize, &bContainerHeightAutoSize);
-  if (pContext && pContext->m_bCurColumnWidthAvaiable) {
-    bContainerWidthAutoSize = false;
-    containerSize.width = pContext->m_fCurColumnWidth;
-  }
-  if (!bContainerHeightAutoSize)
-    containerSize.height -= m_fUsedSize;
-
-  if (!bContainerHeightAutoSize) {
-    CXFA_Node* pParentNode = m_pFormNode->GetParent();
-    bool bFocrTb = false;
-    if (pParentNode &&
-        GetLayout(pParentNode, &bFocrTb) == XFA_AttributeEnum::Row) {
-      CXFA_Node* pChildContainer = m_pFormNode->GetFirstContainerChild();
-      if (pChildContainer && pChildContainer->GetNextContainerSibling()) {
-        containerSize.height = 0;
-        bContainerHeightAutoSize = true;
-      }
-    }
-  }
-
-  CXFA_Margin* pMarginNode =
-      m_pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin);
-  float fLeftInset = 0;
-  float fTopInset = 0;
-  float fRightInset = 0;
-  float fBottomInset = 0;
-  if (pMarginNode) {
-    fLeftInset = pMarginNode->JSObject()
-                     ->GetMeasure(XFA_Attribute::LeftInset)
-                     .ToUnit(XFA_Unit::Pt);
-    fTopInset = pMarginNode->JSObject()
-                    ->GetMeasure(XFA_Attribute::TopInset)
-                    .ToUnit(XFA_Unit::Pt);
-    fRightInset = pMarginNode->JSObject()
-                      ->GetMeasure(XFA_Attribute::RightInset)
-                      .ToUnit(XFA_Unit::Pt);
-    fBottomInset = pMarginNode->JSObject()
-                       ->GetMeasure(XFA_Attribute::BottomInset)
-                       .ToUnit(XFA_Unit::Pt);
-  }
-  float fContentWidthLimit =
-      bContainerWidthAutoSize ? FLT_MAX
-                              : containerSize.width - fLeftInset - fRightInset;
-  float fContentCalculatedWidth = 0;
-  float fContentCalculatedHeight = 0;
-  float fAvailHeight = fHeightLimit - fTopInset - fBottomInset;
-  if (fAvailHeight < 0)
-    m_bHasAvailHeight = false;
-
-  fRealHeight = fRealHeight - fTopInset - fBottomInset;
-  float fContentCurRowY = 0;
-  CXFA_ContentLayoutItem* pLayoutChild = nullptr;
-  if (m_pLayoutItem) {
-    if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Done &&
-        eFlowStrategy != XFA_AttributeEnum::Tb) {
-      pLayoutChild = (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild;
-      for (CXFA_ContentLayoutItem* pLayoutNext = pLayoutChild; pLayoutNext;
-           pLayoutNext = (CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling) {
-        if (pLayoutNext->m_sPos.y != pLayoutChild->m_sPos.y)
-          pLayoutChild = pLayoutNext;
-      }
-    }
-
-    for (CXFA_ContentLayoutItem* pLayoutTempChild =
-             (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild;
-         pLayoutTempChild != pLayoutChild;
-         pLayoutTempChild =
-             (CXFA_ContentLayoutItem*)pLayoutTempChild->m_pNextSibling) {
-      if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutTempChild->m_pFormNode))
-        continue;
-
-      fContentCalculatedWidth = std::max(
-          fContentCalculatedWidth,
-          pLayoutTempChild->m_sPos.x + pLayoutTempChild->m_sSize.width);
-      fContentCalculatedHeight = std::max(
-          fContentCalculatedHeight,
-          pLayoutTempChild->m_sPos.y + pLayoutTempChild->m_sSize.height);
-    }
-
-    if (pLayoutChild)
-      fContentCurRowY = pLayoutChild->m_sPos.y;
-    else
-      fContentCurRowY = fContentCalculatedHeight;
-  }
-
-  fContentCurRowY += InsertKeepLayoutItems();
-  if (m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::None) {
-    GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode,
-                          true);
-  }
-
-  fContentCurRowY += InsertPendingItems(this, m_pFormNode);
-  if (m_pCurChildPreprocessor &&
-      m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::Container) {
-    if (ExistContainerKeep(m_pCurChildPreprocessor->GetFormNode(), false)) {
-      m_pKeepHeadNode = m_pCurChildNode;
-      m_bIsProcessKeep = true;
-      m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Keep;
-    }
-  }
-
-  while (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Done) {
-    float fContentCurRowHeight = 0;
-    float fContentCurRowAvailWidth = fContentWidthLimit;
-    m_fWidthLimite = fContentCurRowAvailWidth;
-    std::vector<CXFA_ContentLayoutItem*> rgCurLineLayoutItems[3];
-    uint8_t uCurHAlignState =
-        (eFlowStrategy != XFA_AttributeEnum::Rl_tb ? 0 : 2);
-    if (pLayoutChild) {
-      for (CXFA_ContentLayoutItem* pLayoutNext = pLayoutChild; pLayoutNext;
-           pLayoutNext = (CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling) {
-        if (!pLayoutNext->m_pNextSibling && m_pCurChildPreprocessor &&
-            m_pCurChildPreprocessor->m_pFormNode == pLayoutNext->m_pFormNode) {
-          pLayoutNext->m_pNext = m_pCurChildPreprocessor->m_pLayoutItem;
-          m_pCurChildPreprocessor->m_pLayoutItem = pLayoutNext;
-          break;
-        }
-        uint8_t uHAlign =
-            HAlignEnumToInt(pLayoutNext->m_pFormNode->JSObject()->GetEnum(
-                XFA_Attribute::HAlign));
-        rgCurLineLayoutItems[uHAlign].push_back(pLayoutNext);
-        if (eFlowStrategy == XFA_AttributeEnum::Lr_tb) {
-          if (uHAlign > uCurHAlignState)
-            uCurHAlignState = uHAlign;
-        } else if (uHAlign < uCurHAlignState) {
-          uCurHAlignState = uHAlign;
-        }
-        if (XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutNext->m_pFormNode)) {
-          if (pLayoutNext->m_sSize.height > fContentCurRowHeight)
-            fContentCurRowHeight = pLayoutNext->m_sSize.height;
-          fContentCurRowAvailWidth -= pLayoutNext->m_sSize.width;
-        }
-      }
-
-      if ((CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild ==
-          pLayoutChild) {
-        m_pLayoutItem->m_pFirstChild = nullptr;
-      } else {
-        CXFA_ContentLayoutItem* pLayoutNext =
-            (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild;
-        for (; pLayoutNext;
-             pLayoutNext =
-                 (CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling) {
-          if ((CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling ==
-              pLayoutChild) {
-            pLayoutNext->m_pNextSibling = nullptr;
-            break;
-          }
-        }
-      }
-
-      CXFA_ContentLayoutItem* pLayoutNextTemp =
-          (CXFA_ContentLayoutItem*)pLayoutChild;
-      while (pLayoutNextTemp) {
-        pLayoutNextTemp->m_pParent = nullptr;
-        CXFA_ContentLayoutItem* pSaveLayoutNext =
-            (CXFA_ContentLayoutItem*)pLayoutNextTemp->m_pNextSibling;
-        pLayoutNextTemp->m_pNextSibling = nullptr;
-        pLayoutNextTemp = pSaveLayoutNext;
-      }
-      pLayoutChild = nullptr;
-    }
-
-    while (m_pCurChildNode) {
-      std::unique_ptr<CXFA_ItemLayoutProcessor> pProcessor;
-      bool bAddedItemInRow = false;
-      fContentCurRowY += InsertPendingItems(this, m_pFormNode);
-      switch (m_nCurChildNodeStage) {
-        case XFA_ItemLayoutProcessorStages::Keep:
-        case XFA_ItemLayoutProcessorStages::None:
-          break;
-        case XFA_ItemLayoutProcessorStages::BreakBefore: {
-          for (auto* item : m_arrayKeepItems) {
-            m_pLayoutItem->RemoveChild(item);
-            fContentCalculatedHeight -= item->m_sSize.height;
-          }
-
-          CXFA_Node* pLeaderNode = nullptr;
-          CXFA_Node* pTrailerNode = nullptr;
-          bool bCreatePage = false;
-          if (!bUseBreakControl || !m_pPageMgr ||
-              !m_pPageMgr->ProcessBreakBeforeOrAfter(m_pCurChildNode, true,
-                                                     pLeaderNode, pTrailerNode,
-                                                     bCreatePage) ||
-              m_pFormNode->GetElementType() == XFA_Element::Form ||
-              !bCreatePage) {
-            break;
-          }
-
-          if (JudgeLeaderOrTrailerForOccur(pLeaderNode))
-            AddPendingNode(this, pLeaderNode, true);
-
-          if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) {
-            if (m_pFormNode->GetParent()->GetElementType() ==
-                    XFA_Element::Form &&
-                !m_pLayoutItem) {
-              AddPendingNode(this, pTrailerNode, true);
-            } else {
-              auto pTempProcessor =
-                  pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pTrailerNode,
-                                                               nullptr);
-              InsertFlowedItem(
-                  this, pTempProcessor.get(), bContainerWidthAutoSize,
-                  bContainerHeightAutoSize, containerSize.height, eFlowStrategy,
-                  &uCurHAlignState, rgCurLineLayoutItems, false, FLT_MAX,
-                  FLT_MAX, fContentWidthLimit, &fContentCurRowY,
-                  &fContentCurRowAvailWidth, &fContentCurRowHeight,
-                  &bAddedItemInRow, &bForceEndPage, pContext, false);
-            }
-          }
-          GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage,
-                                m_pFormNode, true);
-          bForceEndPage = true;
-          bIsManualBreak = true;
-          goto SuspendAndCreateNewRow;
-        }
-        case XFA_ItemLayoutProcessorStages::BreakAfter: {
-          CXFA_Node* pLeaderNode = nullptr;
-          CXFA_Node* pTrailerNode = nullptr;
-          bool bCreatePage = false;
-          if (!bUseBreakControl || !m_pPageMgr ||
-              !m_pPageMgr->ProcessBreakBeforeOrAfter(m_pCurChildNode, false,
-                                                     pLeaderNode, pTrailerNode,
-                                                     bCreatePage) ||
-              m_pFormNode->GetElementType() == XFA_Element::Form) {
-            break;
-          }
-
-          if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) {
-            auto pTempProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-                pTrailerNode, nullptr);
-            InsertFlowedItem(
-                this, pTempProcessor.get(), bContainerWidthAutoSize,
-                bContainerHeightAutoSize, containerSize.height, eFlowStrategy,
-                &uCurHAlignState, rgCurLineLayoutItems, false, FLT_MAX, FLT_MAX,
-                fContentWidthLimit, &fContentCurRowY, &fContentCurRowAvailWidth,
-                &fContentCurRowHeight, &bAddedItemInRow, &bForceEndPage,
-                pContext, false);
-          }
-          if (!bCreatePage) {
-            if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) {
-              CalculateRowChildPosition(
-                  rgCurLineLayoutItems, eFlowStrategy, bContainerHeightAutoSize,
-                  bContainerWidthAutoSize, &fContentCalculatedWidth,
-                  &fContentCalculatedHeight, &fContentCurRowY,
-                  fContentCurRowHeight, fContentWidthLimit, false);
-              rgCurLineLayoutItems->clear();
-              auto pTempProcessor =
-                  pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pLeaderNode,
-                                                               nullptr);
-              InsertFlowedItem(
-                  this, pTempProcessor.get(), bContainerWidthAutoSize,
-                  bContainerHeightAutoSize, containerSize.height, eFlowStrategy,
-                  &uCurHAlignState, rgCurLineLayoutItems, false, FLT_MAX,
-                  FLT_MAX, fContentWidthLimit, &fContentCurRowY,
-                  &fContentCurRowAvailWidth, &fContentCurRowHeight,
-                  &bAddedItemInRow, &bForceEndPage, pContext, false);
-            }
-          } else {
-            if (JudgeLeaderOrTrailerForOccur(pLeaderNode))
-              AddPendingNode(this, pLeaderNode, true);
-          }
-
-          GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage,
-                                m_pFormNode, true);
-          if (bCreatePage) {
-            bForceEndPage = true;
-            bIsManualBreak = true;
-            if (m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::Done)
-              bBreakDone = true;
-          }
-          goto SuspendAndCreateNewRow;
-        }
-        case XFA_ItemLayoutProcessorStages::BookendLeader: {
-          CXFA_Node* pLeaderNode = nullptr;
-          if (m_pCurChildPreprocessor) {
-            pProcessor.reset(m_pCurChildPreprocessor);
-            m_pCurChildPreprocessor = nullptr;
-          } else if (m_pPageMgr &&
-                     m_pPageMgr->ProcessBookendLeaderOrTrailer(
-                         m_pCurChildNode, true, pLeaderNode)) {
-            pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-                pLeaderNode, m_pPageMgr);
-          }
-
-          if (pProcessor) {
-            if (InsertFlowedItem(
-                    this, pProcessor.get(), bContainerWidthAutoSize,
-                    bContainerHeightAutoSize, containerSize.height,
-                    eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems,
-                    bUseBreakControl, fAvailHeight, fRealHeight,
-                    fContentWidthLimit, &fContentCurRowY,
-                    &fContentCurRowAvailWidth, &fContentCurRowHeight,
-                    &bAddedItemInRow, &bForceEndPage, pContext,
-                    false) != XFA_ItemLayoutProcessorResult::Done) {
-              goto SuspendAndCreateNewRow;
-            } else {
-              pProcessor.reset();
-            }
-          }
-          break;
-        }
-        case XFA_ItemLayoutProcessorStages::BookendTrailer: {
-          CXFA_Node* pTrailerNode = nullptr;
-          if (m_pCurChildPreprocessor) {
-            pProcessor.reset(m_pCurChildPreprocessor);
-            m_pCurChildPreprocessor = nullptr;
-          } else if (m_pPageMgr &&
-                     m_pPageMgr->ProcessBookendLeaderOrTrailer(
-                         m_pCurChildNode, false, pTrailerNode)) {
-            pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-                pTrailerNode, m_pPageMgr);
-          }
-          if (pProcessor) {
-            if (InsertFlowedItem(
-                    this, pProcessor.get(), bContainerWidthAutoSize,
-                    bContainerHeightAutoSize, containerSize.height,
-                    eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems,
-                    bUseBreakControl, fAvailHeight, fRealHeight,
-                    fContentWidthLimit, &fContentCurRowY,
-                    &fContentCurRowAvailWidth, &fContentCurRowHeight,
-                    &bAddedItemInRow, &bForceEndPage, pContext,
-                    false) != XFA_ItemLayoutProcessorResult::Done) {
-              goto SuspendAndCreateNewRow;
-            } else {
-              pProcessor.reset();
-            }
-          }
-          break;
-        }
-        case XFA_ItemLayoutProcessorStages::Container: {
-          ASSERT(m_pCurChildNode->IsContainerNode());
-          if (m_pCurChildNode->GetElementType() == XFA_Element::Variables)
-            break;
-          if (fContentCurRowY >= fHeightLimit + XFA_LAYOUT_FLOAT_PERCISION &&
-              XFA_ItemLayoutProcessor_IsTakingSpace(m_pCurChildNode)) {
-            bForceEndPage = true;
-            goto SuspendAndCreateNewRow;
-          }
-          if (!m_pCurChildNode->IsContainerNode())
-            break;
-
-          bool bNewRow = false;
-          if (m_pCurChildPreprocessor) {
-            pProcessor.reset(m_pCurChildPreprocessor);
-            m_pCurChildPreprocessor = nullptr;
-            bNewRow = true;
-          } else {
-            pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-                m_pCurChildNode, m_pPageMgr);
-          }
-
-          InsertPendingItems(pProcessor.get(), m_pCurChildNode);
-          XFA_ItemLayoutProcessorResult rs = InsertFlowedItem(
-              this, pProcessor.get(), bContainerWidthAutoSize,
-              bContainerHeightAutoSize, containerSize.height, eFlowStrategy,
-              &uCurHAlignState, rgCurLineLayoutItems, bUseBreakControl,
-              fAvailHeight, fRealHeight, fContentWidthLimit, &fContentCurRowY,
-              &fContentCurRowAvailWidth, &fContentCurRowHeight,
-              &bAddedItemInRow, &bForceEndPage, pContext, bNewRow);
-          switch (rs) {
-            case XFA_ItemLayoutProcessorResult::ManualBreak:
-              bIsManualBreak = true;
-            case XFA_ItemLayoutProcessorResult::PageFullBreak:
-              bForceEndPage = true;
-            case XFA_ItemLayoutProcessorResult::RowFullBreak:
-              goto SuspendAndCreateNewRow;
-            case XFA_ItemLayoutProcessorResult::Done:
-            default:
-              fContentCurRowY +=
-                  InsertPendingItems(pProcessor.get(), m_pCurChildNode);
-              pProcessor.reset();
-          }
-          break;
-        }
-        case XFA_ItemLayoutProcessorStages::Done:
-          break;
-        default:
-          break;
-      }
-      GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode,
-                            true);
-      if (bAddedItemInRow && eFlowStrategy == XFA_AttributeEnum::Tb)
-        break;
-      continue;
-    SuspendAndCreateNewRow:
-      if (pProcessor)
-        m_pCurChildPreprocessor = pProcessor.release();
-      break;
-    }
-
-    CalculateRowChildPosition(
-        rgCurLineLayoutItems, eFlowStrategy, bContainerHeightAutoSize,
-        bContainerWidthAutoSize, &fContentCalculatedWidth,
-        &fContentCalculatedHeight, &fContentCurRowY, fContentCurRowHeight,
-        fContentWidthLimit, bRootForceTb);
-    m_fWidthLimite = fContentCurRowAvailWidth;
-    if (bForceEndPage)
-      break;
-  }
-
-  bool bRetValue =
-      m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::Done &&
-      m_PendingNodes.empty();
-  if (bBreakDone)
-    bRetValue = false;
-
-  containerSize = CalculateContainerComponentSizeFromContentSize(
-      m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth,
-      bContainerHeightAutoSize, fContentCalculatedHeight, containerSize);
-
-  if (containerSize.height >= XFA_LAYOUT_FLOAT_PERCISION || m_pLayoutItem ||
-      bRetValue) {
-    if (!m_pLayoutItem)
-      m_pLayoutItem = CreateContentLayoutItem(m_pFormNode);
-    containerSize.height = std::max(containerSize.height, 0.f);
-
-    SetCurrentComponentSize(containerSize);
-    if (bForceEndPage)
-      m_fUsedSize = 0;
-    else
-      m_fUsedSize += m_pLayoutItem->m_sSize.height;
-  }
-
-  return bRetValue
-             ? XFA_ItemLayoutProcessorResult::Done
-             : (bIsManualBreak ? XFA_ItemLayoutProcessorResult::ManualBreak
-                               : XFA_ItemLayoutProcessorResult::PageFullBreak);
-}
-
-bool CXFA_ItemLayoutProcessor::CalculateRowChildPosition(
-    std::vector<CXFA_ContentLayoutItem*> (&rgCurLineLayoutItems)[3],
-    XFA_AttributeEnum eFlowStrategy,
-    bool bContainerHeightAutoSize,
-    bool bContainerWidthAutoSize,
-    float* fContentCalculatedWidth,
-    float* fContentCalculatedHeight,
-    float* fContentCurRowY,
-    float fContentCurRowHeight,
-    float fContentWidthLimit,
-    bool bRootForceTb) {
-  int32_t nGroupLengths[3] = {0, 0, 0};
-  float fGroupWidths[3] = {0, 0, 0};
-  int32_t nTotalLength = 0;
-  for (int32_t i = 0; i < 3; i++) {
-    nGroupLengths[i] = pdfium::CollectionSize<int32_t>(rgCurLineLayoutItems[i]);
-    for (int32_t c = nGroupLengths[i], j = 0; j < c; j++) {
-      nTotalLength++;
-      if (XFA_ItemLayoutProcessor_IsTakingSpace(
-              rgCurLineLayoutItems[i][j]->m_pFormNode)) {
-        fGroupWidths[i] += rgCurLineLayoutItems[i][j]->m_sSize.width;
-      }
-    }
-  }
-  if (!nTotalLength) {
-    if (bContainerHeightAutoSize) {
-      *fContentCalculatedHeight =
-          std::min(*fContentCalculatedHeight, *fContentCurRowY);
-    }
-    return false;
-  }
-  if (!m_pLayoutItem)
-    m_pLayoutItem = CreateContentLayoutItem(m_pFormNode);
-
-  if (eFlowStrategy != XFA_AttributeEnum::Rl_tb) {
-    float fCurPos;
-    fCurPos = 0;
-    for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) {
-      if (bRootForceTb) {
-        rgCurLineLayoutItems[0][j]->m_sPos = CalculatePositionedContainerPos(
-            rgCurLineLayoutItems[0][j]->m_pFormNode,
-            rgCurLineLayoutItems[0][j]->m_sSize);
-      } else {
-        rgCurLineLayoutItems[0][j]->m_sPos =
-            CFX_PointF(fCurPos, *fContentCurRowY);
-        if (XFA_ItemLayoutProcessor_IsTakingSpace(
-                rgCurLineLayoutItems[0][j]->m_pFormNode)) {
-          fCurPos += rgCurLineLayoutItems[0][j]->m_sSize.width;
-        }
-      }
-      m_pLayoutItem->AddChild(rgCurLineLayoutItems[0][j]);
-      m_fLastRowWidth = fCurPos;
-    }
-    fCurPos = (fContentWidthLimit + fGroupWidths[0] - fGroupWidths[1] -
-               fGroupWidths[2]) /
-              2;
-    for (int32_t c = nGroupLengths[1], j = 0; j < c; j++) {
-      if (bRootForceTb) {
-        rgCurLineLayoutItems[1][j]->m_sPos = CalculatePositionedContainerPos(
-            rgCurLineLayoutItems[1][j]->m_pFormNode,
-            rgCurLineLayoutItems[1][j]->m_sSize);
-      } else {
-        rgCurLineLayoutItems[1][j]->m_sPos =
-            CFX_PointF(fCurPos, *fContentCurRowY);
-        if (XFA_ItemLayoutProcessor_IsTakingSpace(
-                rgCurLineLayoutItems[1][j]->m_pFormNode)) {
-          fCurPos += rgCurLineLayoutItems[1][j]->m_sSize.width;
-        }
-      }
-      m_pLayoutItem->AddChild(rgCurLineLayoutItems[1][j]);
-      m_fLastRowWidth = fCurPos;
-    }
-    fCurPos = fContentWidthLimit - fGroupWidths[2];
-    for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) {
-      if (bRootForceTb) {
-        rgCurLineLayoutItems[2][j]->m_sPos = CalculatePositionedContainerPos(
-            rgCurLineLayoutItems[2][j]->m_pFormNode,
-            rgCurLineLayoutItems[2][j]->m_sSize);
-      } else {
-        rgCurLineLayoutItems[2][j]->m_sPos =
-            CFX_PointF(fCurPos, *fContentCurRowY);
-        if (XFA_ItemLayoutProcessor_IsTakingSpace(
-                rgCurLineLayoutItems[2][j]->m_pFormNode)) {
-          fCurPos += rgCurLineLayoutItems[2][j]->m_sSize.width;
-        }
-      }
-      m_pLayoutItem->AddChild(rgCurLineLayoutItems[2][j]);
-      m_fLastRowWidth = fCurPos;
-    }
-  } else {
-    float fCurPos;
-    fCurPos = fGroupWidths[0];
-    for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) {
-      if (XFA_ItemLayoutProcessor_IsTakingSpace(
-              rgCurLineLayoutItems[0][j]->m_pFormNode)) {
-        fCurPos -= rgCurLineLayoutItems[0][j]->m_sSize.width;
-      }
-      rgCurLineLayoutItems[0][j]->m_sPos =
-          CFX_PointF(fCurPos, *fContentCurRowY);
-      m_pLayoutItem->AddChild(rgCurLineLayoutItems[0][j]);
-      m_fLastRowWidth = fCurPos;
-    }
-    fCurPos = (fContentWidthLimit + fGroupWidths[0] + fGroupWidths[1] -
-               fGroupWidths[2]) /
-              2;
-    for (int32_t c = nGroupLengths[1], j = 0; j < c; j++) {
-      if (XFA_ItemLayoutProcessor_IsTakingSpace(
-              rgCurLineLayoutItems[1][j]->m_pFormNode)) {
-        fCurPos -= rgCurLineLayoutItems[1][j]->m_sSize.width;
-      }
-      rgCurLineLayoutItems[1][j]->m_sPos =
-          CFX_PointF(fCurPos, *fContentCurRowY);
-      m_pLayoutItem->AddChild(rgCurLineLayoutItems[1][j]);
-      m_fLastRowWidth = fCurPos;
-    }
-    fCurPos = fContentWidthLimit;
-    for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) {
-      if (XFA_ItemLayoutProcessor_IsTakingSpace(
-              rgCurLineLayoutItems[2][j]->m_pFormNode)) {
-        fCurPos -= rgCurLineLayoutItems[2][j]->m_sSize.width;
-      }
-      rgCurLineLayoutItems[2][j]->m_sPos =
-          CFX_PointF(fCurPos, *fContentCurRowY);
-      m_pLayoutItem->AddChild(rgCurLineLayoutItems[2][j]);
-      m_fLastRowWidth = fCurPos;
-    }
-  }
-  m_fLastRowY = *fContentCurRowY;
-  *fContentCurRowY += fContentCurRowHeight;
-  if (bContainerWidthAutoSize) {
-    float fChildSuppliedWidth = fGroupWidths[0];
-    if (fContentWidthLimit < FLT_MAX &&
-        fContentWidthLimit > fChildSuppliedWidth) {
-      fChildSuppliedWidth = fContentWidthLimit;
-    }
-    *fContentCalculatedWidth =
-        std::max(*fContentCalculatedWidth, fChildSuppliedWidth);
-  }
-  if (bContainerHeightAutoSize) {
-    *fContentCalculatedHeight =
-        std::max(*fContentCalculatedHeight, *fContentCurRowY);
-  }
-  return true;
-}
-
-CXFA_Node* CXFA_ItemLayoutProcessor::GetSubformSetParent(
-    CXFA_Node* pSubformSet) {
-  if (pSubformSet && pSubformSet->GetElementType() == XFA_Element::SubformSet) {
-    CXFA_Node* pParent = pSubformSet->GetParent();
-    while (pParent) {
-      if (pParent->GetElementType() != XFA_Element::SubformSet)
-        return pParent;
-      pParent = pParent->GetParent();
-    }
-  }
-  return pSubformSet;
-}
-
-void CXFA_ItemLayoutProcessor::DoLayoutField() {
-  if (m_pLayoutItem)
-    return;
-
-  ASSERT(m_pCurChildNode == XFA_LAYOUT_INVALIDNODE);
-  m_pLayoutItem = CreateContentLayoutItem(m_pFormNode);
-  if (!m_pLayoutItem)
-    return;
-
-  CXFA_Document* pDocument = m_pFormNode->GetDocument();
-  CXFA_FFNotify* pNotify = pDocument->GetNotify();
-  CFX_SizeF size(-1, -1);
-  pNotify->StartFieldDrawLayout(m_pFormNode, size.width, size.height);
-
-  int32_t nRotate = XFA_MapRotation(
-      m_pFormNode->JSObject()->GetInteger(XFA_Attribute::Rotate));
-  if (nRotate == 90 || nRotate == 270)
-    std::swap(size.width, size.height);
-
-  SetCurrentComponentSize(size);
-}
-
-XFA_ItemLayoutProcessorResult CXFA_ItemLayoutProcessor::DoLayout(
-    bool bUseBreakControl,
-    float fHeightLimit,
-    float fRealHeight,
-    CXFA_LayoutContext* pContext) {
-  switch (m_pFormNode->GetElementType()) {
-    case XFA_Element::Subform:
-    case XFA_Element::Area:
-    case XFA_Element::ExclGroup:
-    case XFA_Element::SubformSet: {
-      bool bRootForceTb = false;
-      CXFA_Node* pLayoutNode = GetSubformSetParent(m_pFormNode);
-      XFA_AttributeEnum eLayoutStrategy = GetLayout(pLayoutNode, &bRootForceTb);
-      switch (eLayoutStrategy) {
-        case XFA_AttributeEnum::Tb:
-        case XFA_AttributeEnum::Lr_tb:
-        case XFA_AttributeEnum::Rl_tb:
-          return DoLayoutFlowedContainer(bUseBreakControl, eLayoutStrategy,
-                                         fHeightLimit, fRealHeight, pContext,
-                                         bRootForceTb);
-        case XFA_AttributeEnum::Position:
-        case XFA_AttributeEnum::Row:
-        case XFA_AttributeEnum::Rl_row:
-        default:
-          DoLayoutPositionedContainer(pContext);
-          m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Done;
-          return XFA_ItemLayoutProcessorResult::Done;
-        case XFA_AttributeEnum::Table:
-          DoLayoutTableContainer(pLayoutNode);
-          m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Done;
-          return XFA_ItemLayoutProcessorResult::Done;
-      }
-    }
-    case XFA_Element::Draw:
-    case XFA_Element::Field:
-      DoLayoutField();
-      m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Done;
-      return XFA_ItemLayoutProcessorResult::Done;
-    case XFA_Element::ContentArea:
-      return XFA_ItemLayoutProcessorResult::Done;
-    default:
-      return XFA_ItemLayoutProcessorResult::Done;
-  }
-}
-
-CFX_SizeF CXFA_ItemLayoutProcessor::GetCurrentComponentSize() {
-  return CFX_SizeF(m_pLayoutItem->m_sSize.width, m_pLayoutItem->m_sSize.height);
-}
-
-void CXFA_ItemLayoutProcessor::SetCurrentComponentPos(const CFX_PointF& pos) {
-  m_pLayoutItem->m_sPos = pos;
-}
-
-void CXFA_ItemLayoutProcessor::SetCurrentComponentSize(const CFX_SizeF& size) {
-  m_pLayoutItem->m_sSize = size;
-}
-
-bool CXFA_ItemLayoutProcessor::JudgeLeaderOrTrailerForOccur(
-    CXFA_Node* pFormNode) {
-  if (!pFormNode)
-    return false;
-
-  CXFA_Node* pTemplate = pFormNode->GetTemplateNodeIfExists();
-  if (!pTemplate)
-    pTemplate = pFormNode;
-
-  int32_t iMax =
-      pTemplate->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur)->GetMax();
-  if (iMax < 0)
-    return true;
-
-  int32_t iCount = m_PendingNodesCount[pTemplate];
-  if (iCount >= iMax)
-    return false;
-
-  m_PendingNodesCount[pTemplate] = iCount + 1;
-  return true;
-}
diff --git a/xfa/fxfa/parser/cxfa_itemlayoutprocessor.h b/xfa/fxfa/parser/cxfa_itemlayoutprocessor.h
deleted file mode 100644
index e178095..0000000
--- a/xfa/fxfa/parser/cxfa_itemlayoutprocessor.h
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_ITEMLAYOUTPROCESSOR_H_
-#define XFA_FXFA_PARSER_CXFA_ITEMLAYOUTPROCESSOR_H_
-
-#include <float.h>
-
-#include <list>
-#include <map>
-#include <tuple>
-#include <vector>
-
-#include "core/fxcrt/fx_coordinates.h"
-#include "xfa/fxfa/fxfa_basic.h"
-
-#define XFA_LAYOUT_INVALIDNODE ((CXFA_Node*)(intptr_t)-1)
-#define XFA_LAYOUT_FLOAT_PERCISION (0.0005f)
-
-class CXFA_ContainerLayoutItem;
-class CXFA_ContentLayoutItem;
-class CXFA_ItemLayoutProcessor;
-class CXFA_LayoutContext;
-class CXFA_LayoutPageMgr;
-class CXFA_LayoutProcessor;
-class CXFA_Node;
-
-enum class XFA_ItemLayoutProcessorResult {
-  Done,
-  PageFullBreak,
-  RowFullBreak,
-  ManualBreak,
-};
-
-enum class XFA_ItemLayoutProcessorStages {
-  None,
-  BookendLeader,
-  BreakBefore,
-  Keep,
-  Container,
-  BreakAfter,
-  BookendTrailer,
-  Done,
-};
-
-bool XFA_ItemLayoutProcessor_IsTakingSpace(CXFA_Node* pNode);
-
-class CXFA_ItemLayoutProcessor {
- public:
-  static bool IncrementRelayoutNode(CXFA_LayoutProcessor* pLayoutProcessor,
-                                    CXFA_Node* pNode,
-                                    CXFA_Node* pParentNode);
-
-  CXFA_ItemLayoutProcessor(CXFA_Node* pNode, CXFA_LayoutPageMgr* pPageMgr);
-  ~CXFA_ItemLayoutProcessor();
-
-  XFA_ItemLayoutProcessorResult DoLayout(bool bUseBreakControl,
-                                         float fHeightLimit,
-                                         float fRealHeight,
-                                         CXFA_LayoutContext* pContext);
-  void DoLayoutPageArea(CXFA_ContainerLayoutItem* pPageAreaLayoutItem);
-
-  CFX_SizeF GetCurrentComponentSize();
-  CXFA_Node* GetFormNode() { return m_pFormNode; }
-  bool HasLayoutItem() const { return !!m_pLayoutItem; }
-  CXFA_ContentLayoutItem* ExtractLayoutItem();
-  void SplitLayoutItem(float fSplitPos);
-
-  float FindSplitPos(float fProposedSplitPos);
-
-  bool ProcessKeepForSplit(
-      CXFA_ItemLayoutProcessor* pParentProcessor,
-      CXFA_ItemLayoutProcessor* pChildProcessor,
-      XFA_ItemLayoutProcessorResult eRetValue,
-      std::vector<CXFA_ContentLayoutItem*>* rgCurLineLayoutItem,
-      float* fContentCurRowAvailWidth,
-      float* fContentCurRowHeight,
-      float* fContentCurRowY,
-      bool* bAddedItemInRow,
-      bool* bForceEndPage,
-      XFA_ItemLayoutProcessorResult* result);
-  void ProcessUnUseOverFlow(CXFA_Node* pLeaderNode,
-                            CXFA_Node* pTrailerNode,
-                            CXFA_ContentLayoutItem* pTrailerItem,
-                            CXFA_Node* pFormNode);
-  bool IsAddNewRowForTrailer(CXFA_ContentLayoutItem* pTrailerItem);
-  bool JudgeLeaderOrTrailerForOccur(CXFA_Node* pFormNode);
-
-  CXFA_ContentLayoutItem* CreateContentLayoutItem(CXFA_Node* pFormNode);
-
-  CXFA_Node* m_pFormNode;
-  CXFA_ContentLayoutItem* m_pLayoutItem;
-  CXFA_Node* m_pCurChildNode;
-  float m_fUsedSize;
-  CXFA_LayoutPageMgr* m_pPageMgr;
-  std::list<CXFA_Node*> m_PendingNodes;
-  bool m_bBreakPending;
-  std::vector<float> m_rgSpecifiedColumnWidths;
-  std::vector<CXFA_ContentLayoutItem*> m_arrayKeepItems;
-  float m_fLastRowWidth;
-  float m_fLastRowY;
-  bool m_bUseInheriated;
-  XFA_ItemLayoutProcessorResult m_ePreProcessRs;
-
- private:
-  void SetCurrentComponentPos(const CFX_PointF& pos);
-  void SetCurrentComponentSize(const CFX_SizeF& size);
-
-  void SplitLayoutItem(CXFA_ContentLayoutItem* pLayoutItem,
-                       CXFA_ContentLayoutItem* pSecondParent,
-                       float fSplitPos);
-  float InsertKeepLayoutItems();
-  bool CalculateRowChildPosition(
-      std::vector<CXFA_ContentLayoutItem*> (&rgCurLineLayoutItems)[3],
-      XFA_AttributeEnum eFlowStrategy,
-      bool bContainerHeightAutoSize,
-      bool bContainerWidthAutoSize,
-      float* fContentCalculatedWidth,
-      float* fContentCalculatedHeight,
-      float* fContentCurRowY,
-      float fContentCurRowHeight,
-      float fContentWidthLimit,
-      bool bRootForceTb);
-  void ProcessUnUseBinds(CXFA_Node* pFormNode);
-  bool JudgePutNextPage(CXFA_ContentLayoutItem* pParentLayoutItem,
-                        float fChildHeight,
-                        std::vector<CXFA_ContentLayoutItem*>* pKeepItems);
-
-  void DoLayoutPositionedContainer(CXFA_LayoutContext* pContext);
-  void DoLayoutTableContainer(CXFA_Node* pLayoutNode);
-  XFA_ItemLayoutProcessorResult DoLayoutFlowedContainer(
-      bool bUseBreakControl,
-      XFA_AttributeEnum eFlowStrategy,
-      float fHeightLimit,
-      float fRealHeight,
-      CXFA_LayoutContext* pContext,
-      bool bRootForceTb);
-  void DoLayoutField();
-
-  void GotoNextContainerNode(CXFA_Node*& pCurActionNode,
-                             XFA_ItemLayoutProcessorStages& nCurStage,
-                             CXFA_Node* pParentContainer,
-                             bool bUsePageBreak);
-
-  bool ProcessKeepNodesForCheckNext(CXFA_Node*& pCurActionNode,
-                                    XFA_ItemLayoutProcessorStages& nCurStage,
-                                    CXFA_Node*& pNextContainer,
-                                    bool& bLastKeepNode);
-
-  bool ProcessKeepNodesForBreakBefore(CXFA_Node*& pCurActionNode,
-                                      XFA_ItemLayoutProcessorStages& nCurStage,
-                                      CXFA_Node* pContainerNode);
-
-  CXFA_Node* GetSubformSetParent(CXFA_Node* pSubformSet);
-
-  bool m_bKeepBreakFinish;
-  bool m_bIsProcessKeep;
-  CXFA_Node* m_pKeepHeadNode;
-  CXFA_Node* m_pKeepTailNode;
-  CXFA_ContentLayoutItem* m_pOldLayoutItem;
-  CXFA_ItemLayoutProcessor* m_pCurChildPreprocessor;
-  XFA_ItemLayoutProcessorStages m_nCurChildNodeStage;
-  std::map<CXFA_Node*, int32_t> m_PendingNodesCount;
-  float m_fWidthLimite;
-  bool m_bHasAvailHeight;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_ITEMLAYOUTPROCESSOR_H_
diff --git a/xfa/fxfa/parser/cxfa_items.cpp b/xfa/fxfa/parser/cxfa_items.cpp
index 06feddb..89fee2b 100644
--- a/xfa/fxfa/parser/cxfa_items.cpp
+++ b/xfa/fxfa/parser/cxfa_items.cpp
@@ -6,23 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_items.h"
 
-#include "fxjs/xfa/cjx_items.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kItemsAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::Save, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"items";
+};
 
 }  // namespace
 
@@ -32,9 +30,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Items,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Items>(this)) {}
+                {},
+                kItemsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Items::~CXFA_Items() {}
+CXFA_Items::~CXFA_Items() = default;
diff --git a/xfa/fxfa/parser/cxfa_items.h b/xfa/fxfa/parser/cxfa_items.h
index 586fabd..609b028 100644
--- a/xfa/fxfa/parser/cxfa_items.h
+++ b/xfa/fxfa/parser/cxfa_items.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Items : public CXFA_Node {
+class CXFA_Items final : public CXFA_Node {
  public:
   CXFA_Items(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Items() override;
diff --git a/xfa/fxfa/parser/cxfa_jog.cpp b/xfa/fxfa/parser/cxfa_jog.cpp
index 3735e2f..f979259 100644
--- a/xfa/fxfa/parser/cxfa_jog.cpp
+++ b/xfa/fxfa/parser/cxfa_jog.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_jog.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kJogAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"jog";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Jog,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kJogAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Jog::~CXFA_Jog() {}
+CXFA_Jog::~CXFA_Jog() = default;
diff --git a/xfa/fxfa/parser/cxfa_jog.h b/xfa/fxfa/parser/cxfa_jog.h
index 266ccd1..a40a488 100644
--- a/xfa/fxfa/parser/cxfa_jog.h
+++ b/xfa/fxfa/parser/cxfa_jog.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Jog : public CXFA_Node {
+class CXFA_Jog final : public CXFA_Node {
  public:
   CXFA_Jog(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Jog() override;
diff --git a/xfa/fxfa/parser/cxfa_keep.cpp b/xfa/fxfa/parser/cxfa_keep.cpp
index beb22fa..e500ee6 100644
--- a/xfa/fxfa/parser/cxfa_keep.cpp
+++ b/xfa/fxfa/parser/cxfa_keep.cpp
@@ -6,26 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_keep.h"
 
-#include "fxjs/xfa/cjx_keep.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kKeepPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kKeepAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Next, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Previous, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
+     (void*)XFA_AttributeValue::None},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Intact, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::None},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"keep";
+     (void*)XFA_AttributeValue::None},
+};
 
 }  // namespace
 
@@ -35,9 +35,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Keep,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Keep>(this)) {}
+                kKeepPropertyData,
+                kKeepAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Keep::~CXFA_Keep() {}
+CXFA_Keep::~CXFA_Keep() = default;
diff --git a/xfa/fxfa/parser/cxfa_keep.h b/xfa/fxfa/parser/cxfa_keep.h
index 7d53f96..0d4a2e0 100644
--- a/xfa/fxfa/parser/cxfa_keep.h
+++ b/xfa/fxfa/parser/cxfa_keep.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Keep : public CXFA_Node {
+class CXFA_Keep final : public CXFA_Node {
  public:
   CXFA_Keep(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Keep() override;
diff --git a/xfa/fxfa/parser/cxfa_keyusage.cpp b/xfa/fxfa/parser/cxfa_keyusage.cpp
index 13054be..dda5c10 100644
--- a/xfa/fxfa/parser/cxfa_keyusage.cpp
+++ b/xfa/fxfa/parser/cxfa_keyusage.cpp
@@ -6,18 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_keyusage.h"
 
-#include "fxjs/xfa/cjx_keyusage.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kKeyUsageAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::NonRepudiation, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::EncipherOnly, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::DigitalSignature, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::CrlSign, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::KeyAgreement, XFA_AttributeType::CData, nullptr},
@@ -26,9 +26,7 @@
     {XFA_Attribute::DataEncipherment, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::KeyCertSign, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DecipherOnly, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"keyUsage";
+};
 
 }  // namespace
 
@@ -38,9 +36,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::KeyUsage,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_KeyUsage>(this)) {}
+                {},
+                kKeyUsageAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_KeyUsage::~CXFA_KeyUsage() {}
+CXFA_KeyUsage::~CXFA_KeyUsage() = default;
diff --git a/xfa/fxfa/parser/cxfa_keyusage.h b/xfa/fxfa/parser/cxfa_keyusage.h
index b258074..3409e3f 100644
--- a/xfa/fxfa/parser/cxfa_keyusage.h
+++ b/xfa/fxfa/parser/cxfa_keyusage.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_KeyUsage : public CXFA_Node {
+class CXFA_KeyUsage final : public CXFA_Node {
  public:
   CXFA_KeyUsage(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_KeyUsage() override;
diff --git a/xfa/fxfa/parser/cxfa_labelprinter.cpp b/xfa/fxfa/parser/cxfa_labelprinter.cpp
index 55b04da..40f3616 100644
--- a/xfa/fxfa/parser/cxfa_labelprinter.cpp
+++ b/xfa/fxfa/parser/cxfa_labelprinter.cpp
@@ -6,22 +6,24 @@
 
 #include "xfa/fxfa/parser/cxfa_labelprinter.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kLabelPrinterPropertyData[] = {
     {XFA_Element::FontInfo, 1, 0},
     {XFA_Element::Xdc, 1, 0},
     {XFA_Element::BatchOutput, 1, 0},
     {XFA_Element::FlipLabel, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kLabelPrinterAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Zpl},
+     (void*)XFA_AttributeValue::Zpl},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"labelPrinter";
+};
 
 }  // namespace
 
@@ -31,8 +33,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::LabelPrinter,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kLabelPrinterPropertyData,
+                kLabelPrinterAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_LabelPrinter::~CXFA_LabelPrinter() {}
+CXFA_LabelPrinter::~CXFA_LabelPrinter() = default;
diff --git a/xfa/fxfa/parser/cxfa_labelprinter.h b/xfa/fxfa/parser/cxfa_labelprinter.h
index e3f5d3b..74a5e82 100644
--- a/xfa/fxfa/parser/cxfa_labelprinter.h
+++ b/xfa/fxfa/parser/cxfa_labelprinter.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_LabelPrinter : public CXFA_Node {
+class CXFA_LabelPrinter final : public CXFA_Node {
  public:
   CXFA_LabelPrinter(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_LabelPrinter() override;
diff --git a/xfa/fxfa/parser/cxfa_layout.cpp b/xfa/fxfa/parser/cxfa_layout.cpp
index bc2933f..12d6f49 100644
--- a/xfa/fxfa/parser/cxfa_layout.cpp
+++ b/xfa/fxfa/parser/cxfa_layout.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_layout.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kLayoutAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"layout";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Layout,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kLayoutAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Layout::~CXFA_Layout() {}
+CXFA_Layout::~CXFA_Layout() = default;
diff --git a/xfa/fxfa/parser/cxfa_layout.h b/xfa/fxfa/parser/cxfa_layout.h
index 93e069e..ad25efa 100644
--- a/xfa/fxfa/parser/cxfa_layout.h
+++ b/xfa/fxfa/parser/cxfa_layout.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Layout : public CXFA_Node {
+class CXFA_Layout final : public CXFA_Node {
  public:
   CXFA_Layout(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Layout() override;
diff --git a/xfa/fxfa/parser/cxfa_layoutcontext.h b/xfa/fxfa/parser/cxfa_layoutcontext.h
deleted file mode 100644
index a133ee1..0000000
--- a/xfa/fxfa/parser/cxfa_layoutcontext.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_LAYOUTCONTEXT_H_
-#define XFA_FXFA_PARSER_CXFA_LAYOUTCONTEXT_H_
-
-#include <vector>
-
-class CXFA_ItemLayoutProcess;
-class CXFA_Node;
-
-class CXFA_LayoutContext {
- public:
-  CXFA_LayoutContext()
-      : m_prgSpecifiedColumnWidths(nullptr),
-        m_fCurColumnWidth(0),
-        m_bCurColumnWidthAvaiable(false),
-        m_pOverflowProcessor(nullptr),
-        m_pOverflowNode(nullptr) {}
-  ~CXFA_LayoutContext() {}
-
-  std::vector<float>* m_prgSpecifiedColumnWidths;
-  float m_fCurColumnWidth;
-  bool m_bCurColumnWidthAvaiable;
-  CXFA_ItemLayoutProcessor* m_pOverflowProcessor;
-  CXFA_Node* m_pOverflowNode;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_LAYOUTCONTEXT_H_
diff --git a/xfa/fxfa/parser/cxfa_layoutitem.cpp b/xfa/fxfa/parser/cxfa_layoutitem.cpp
deleted file mode 100644
index 7fb921a..0000000
--- a/xfa/fxfa/parser/cxfa_layoutitem.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_layoutitem.h"
-
-#include "fxjs/xfa/cjx_object.h"
-#include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_margin.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-
-void XFA_ReleaseLayoutItem(CXFA_LayoutItem* pLayoutItem) {
-  CXFA_LayoutItem* pNode = pLayoutItem->m_pFirstChild;
-  CXFA_FFNotify* pNotify = pLayoutItem->m_pFormNode->GetDocument()->GetNotify();
-  CXFA_LayoutProcessor* pDocLayout =
-      pLayoutItem->m_pFormNode->GetDocument()->GetDocLayout();
-  while (pNode) {
-    CXFA_LayoutItem* pNext = pNode->m_pNextSibling;
-    pNode->m_pParent = nullptr;
-    pNotify->OnLayoutItemRemoving(pDocLayout, pNode);
-    XFA_ReleaseLayoutItem(pNode);
-    pNode = pNext;
-  }
-  pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
-  if (pLayoutItem->m_pFormNode->GetElementType() == XFA_Element::PageArea) {
-    pNotify->OnPageEvent(static_cast<CXFA_ContainerLayoutItem*>(pLayoutItem),
-                         XFA_PAGEVIEWEVENT_PostRemoved);
-  }
-  delete pLayoutItem;
-}
-
-CXFA_LayoutItem::CXFA_LayoutItem(CXFA_Node* pNode, bool bIsContentLayoutItem)
-    : m_pFormNode(pNode),
-      m_pParent(nullptr),
-      m_pNextSibling(nullptr),
-      m_pFirstChild(nullptr),
-      m_bIsContentLayoutItem(bIsContentLayoutItem) {}
-
-CXFA_LayoutItem::~CXFA_LayoutItem() {}
-
-CXFA_ContainerLayoutItem* CXFA_LayoutItem::AsContainerLayoutItem() {
-  return IsContainerLayoutItem() ? static_cast<CXFA_ContainerLayoutItem*>(this)
-                                 : nullptr;
-}
-
-CXFA_ContentLayoutItem* CXFA_LayoutItem::AsContentLayoutItem() {
-  return IsContentLayoutItem() ? static_cast<CXFA_ContentLayoutItem*>(this)
-                               : nullptr;
-}
-
-CXFA_ContainerLayoutItem* CXFA_LayoutItem::GetPage() const {
-  for (CXFA_LayoutItem* pCurNode = const_cast<CXFA_LayoutItem*>(this); pCurNode;
-       pCurNode = pCurNode->m_pParent) {
-    if (pCurNode->m_pFormNode->GetElementType() == XFA_Element::PageArea)
-      return static_cast<CXFA_ContainerLayoutItem*>(pCurNode);
-  }
-  return nullptr;
-}
-
-CFX_RectF CXFA_LayoutItem::GetRect(bool bRelative) const {
-  ASSERT(m_bIsContentLayoutItem);
-
-  auto* pThis = static_cast<const CXFA_ContentLayoutItem*>(this);
-  CFX_PointF sPos = pThis->m_sPos;
-  CFX_SizeF sSize = pThis->m_sSize;
-  if (bRelative)
-    return CFX_RectF(sPos, sSize);
-
-  for (CXFA_LayoutItem* pLayoutItem = pThis->m_pParent; pLayoutItem;
-       pLayoutItem = pLayoutItem->m_pParent) {
-    if (CXFA_ContentLayoutItem* pContent = pLayoutItem->AsContentLayoutItem()) {
-      sPos += pContent->m_sPos;
-      CXFA_Margin* pMarginNode =
-          pLayoutItem->m_pFormNode->GetFirstChildByClass<CXFA_Margin>(
-              XFA_Element::Margin);
-      if (pMarginNode) {
-        sPos += CFX_PointF(pMarginNode->JSObject()
-                               ->GetMeasure(XFA_Attribute::LeftInset)
-                               .ToUnit(XFA_Unit::Pt),
-                           pMarginNode->JSObject()
-                               ->GetMeasure(XFA_Attribute::TopInset)
-                               .ToUnit(XFA_Unit::Pt));
-      }
-      continue;
-    }
-
-    if (pLayoutItem->m_pFormNode->GetElementType() ==
-        XFA_Element::ContentArea) {
-      sPos += CFX_PointF(pLayoutItem->m_pFormNode->JSObject()
-                             ->GetMeasure(XFA_Attribute::X)
-                             .ToUnit(XFA_Unit::Pt),
-                         pLayoutItem->m_pFormNode->JSObject()
-                             ->GetMeasure(XFA_Attribute::Y)
-                             .ToUnit(XFA_Unit::Pt));
-      break;
-    }
-    if (pLayoutItem->m_pFormNode->GetElementType() == XFA_Element::PageArea)
-      break;
-  }
-  return CFX_RectF(sPos, sSize);
-}
-
-CXFA_LayoutItem* CXFA_LayoutItem::GetFirst() {
-  ASSERT(m_bIsContentLayoutItem);
-  CXFA_ContentLayoutItem* pCurNode = static_cast<CXFA_ContentLayoutItem*>(this);
-  while (pCurNode->m_pPrev)
-    pCurNode = pCurNode->m_pPrev;
-
-  return pCurNode;
-}
-
-const CXFA_LayoutItem* CXFA_LayoutItem::GetLast() const {
-  ASSERT(m_bIsContentLayoutItem);
-  const CXFA_ContentLayoutItem* pCurNode =
-      static_cast<const CXFA_ContentLayoutItem*>(this);
-  while (pCurNode->m_pNext)
-    pCurNode = pCurNode->m_pNext;
-
-  return pCurNode;
-}
-
-CXFA_LayoutItem* CXFA_LayoutItem::GetPrev() const {
-  ASSERT(m_bIsContentLayoutItem);
-
-  return static_cast<const CXFA_ContentLayoutItem*>(this)->m_pPrev;
-}
-
-CXFA_LayoutItem* CXFA_LayoutItem::GetNext() const {
-  ASSERT(m_bIsContentLayoutItem);
-  return static_cast<const CXFA_ContentLayoutItem*>(this)->m_pNext;
-}
-
-int32_t CXFA_LayoutItem::GetIndex() const {
-  ASSERT(m_bIsContentLayoutItem);
-  int32_t iIndex = 0;
-  const CXFA_ContentLayoutItem* pCurNode =
-      static_cast<const CXFA_ContentLayoutItem*>(this);
-  while (pCurNode->m_pPrev) {
-    pCurNode = pCurNode->m_pPrev;
-    ++iIndex;
-  }
-  return iIndex;
-}
-
-int32_t CXFA_LayoutItem::GetCount() const {
-  ASSERT(m_bIsContentLayoutItem);
-
-  int32_t iCount = GetIndex() + 1;
-  const CXFA_ContentLayoutItem* pCurNode =
-      static_cast<const CXFA_ContentLayoutItem*>(this);
-  while (pCurNode->m_pNext) {
-    pCurNode = pCurNode->m_pNext;
-    iCount++;
-  }
-  return iCount;
-}
-
-void CXFA_LayoutItem::AddChild(CXFA_LayoutItem* pChildItem) {
-  if (pChildItem->m_pParent)
-    pChildItem->m_pParent->RemoveChild(pChildItem);
-
-  pChildItem->m_pParent = this;
-  if (!m_pFirstChild) {
-    m_pFirstChild = pChildItem;
-    return;
-  }
-
-  CXFA_LayoutItem* pExistingChildItem = m_pFirstChild;
-  while (pExistingChildItem->m_pNextSibling)
-    pExistingChildItem = pExistingChildItem->m_pNextSibling;
-
-  pExistingChildItem->m_pNextSibling = pChildItem;
-}
-
-void CXFA_LayoutItem::AddHeadChild(CXFA_LayoutItem* pChildItem) {
-  if (pChildItem->m_pParent)
-    pChildItem->m_pParent->RemoveChild(pChildItem);
-
-  pChildItem->m_pParent = this;
-  if (!m_pFirstChild) {
-    m_pFirstChild = pChildItem;
-    return;
-  }
-
-  CXFA_LayoutItem* pExistingChildItem = m_pFirstChild;
-  m_pFirstChild = pChildItem;
-  m_pFirstChild->m_pNextSibling = pExistingChildItem;
-}
-
-void CXFA_LayoutItem::InsertChild(CXFA_LayoutItem* pBeforeItem,
-                                  CXFA_LayoutItem* pChildItem) {
-  if (pBeforeItem->m_pParent != this)
-    return;
-  if (pChildItem->m_pParent)
-    pChildItem->m_pParent = nullptr;
-
-  pChildItem->m_pParent = this;
-
-  CXFA_LayoutItem* pExistingChildItem = pBeforeItem->m_pNextSibling;
-  pBeforeItem->m_pNextSibling = pChildItem;
-  pChildItem->m_pNextSibling = pExistingChildItem;
-}
-
-void CXFA_LayoutItem::RemoveChild(CXFA_LayoutItem* pChildItem) {
-  if (pChildItem->m_pParent != this)
-    return;
-
-  if (m_pFirstChild == pChildItem) {
-    m_pFirstChild = pChildItem->m_pNextSibling;
-  } else {
-    CXFA_LayoutItem* pExistingChildItem = m_pFirstChild;
-    while (pExistingChildItem &&
-           pExistingChildItem->m_pNextSibling != pChildItem) {
-      pExistingChildItem = pExistingChildItem->m_pNextSibling;
-    }
-    if (pExistingChildItem)
-      pExistingChildItem->m_pNextSibling = pChildItem->m_pNextSibling;
-  }
-  pChildItem->m_pNextSibling = nullptr;
-  pChildItem->m_pParent = nullptr;
-}
diff --git a/xfa/fxfa/parser/cxfa_layoutitem.h b/xfa/fxfa/parser/cxfa_layoutitem.h
deleted file mode 100644
index 9c08860..0000000
--- a/xfa/fxfa/parser/cxfa_layoutitem.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_LAYOUTITEM_H_
-#define XFA_FXFA_PARSER_CXFA_LAYOUTITEM_H_
-
-#include "xfa/fxfa/parser/cxfa_document.h"
-
-class CXFA_ContainerLayoutItem;
-class CXFA_ContentLayoutItem;
-class CXFA_LayoutProcessor;
-
-void XFA_ReleaseLayoutItem(CXFA_LayoutItem* pLayoutItem);
-
-class CXFA_LayoutItem {
- public:
-  virtual ~CXFA_LayoutItem();
-
-  bool IsContainerLayoutItem() const { return !m_bIsContentLayoutItem; }
-  bool IsContentLayoutItem() const { return m_bIsContentLayoutItem; }
-  CXFA_ContainerLayoutItem* AsContainerLayoutItem();
-  CXFA_ContentLayoutItem* AsContentLayoutItem();
-
-  CXFA_ContainerLayoutItem* GetPage() const;
-  CXFA_Node* GetFormNode() const { return m_pFormNode; }
-  CFX_RectF GetRect(bool bRelative) const;
-
-  int32_t GetIndex() const;
-  int32_t GetCount() const;
-
-  CXFA_LayoutItem* GetParent() const { return m_pParent; }
-  CXFA_LayoutItem* GetFirst();
-  const CXFA_LayoutItem* GetLast() const;
-  CXFA_LayoutItem* GetPrev() const;
-  CXFA_LayoutItem* GetNext() const;
-
-  void AddChild(CXFA_LayoutItem* pChildItem);
-  void AddHeadChild(CXFA_LayoutItem* pChildItem);
-  void RemoveChild(CXFA_LayoutItem* pChildItem);
-  void InsertChild(CXFA_LayoutItem* pBeforeItem, CXFA_LayoutItem* pChildItem);
-
-  CXFA_Node* m_pFormNode;
-  CXFA_LayoutItem* m_pParent;
-  CXFA_LayoutItem* m_pNextSibling;
-  CXFA_LayoutItem* m_pFirstChild;
-
- protected:
-  CXFA_LayoutItem(CXFA_Node* pNode, bool bIsContentLayoutItem);
-
-  bool m_bIsContentLayoutItem;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_LAYOUTITEM_H_
diff --git a/xfa/fxfa/parser/cxfa_layoutpagemgr.cpp b/xfa/fxfa/parser/cxfa_layoutpagemgr.cpp
deleted file mode 100644
index 123cafb..0000000
--- a/xfa/fxfa/parser/cxfa_layoutpagemgr.cpp
+++ /dev/null
@@ -1,2014 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_layoutpagemgr.h"
-
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_contentarea.h"
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_itemlayoutprocessor.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
-#include "xfa/fxfa/parser/cxfa_medium.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
-#include "xfa/fxfa/parser/cxfa_occur.h"
-#include "xfa/fxfa/parser/cxfa_pageset.h"
-#include "xfa/fxfa/parser/cxfa_subform.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_contentareacontainerlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_layoutitem.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
-#include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-
-namespace {
-
-class PageSetContainerLayoutItem {
- public:
-  static CXFA_ContainerLayoutItem* GetFirstChild(
-      CXFA_ContainerLayoutItem* pLayoutItem) {
-    if (pLayoutItem->m_pFormNode->GetElementType() != XFA_Element::PageSet)
-      return nullptr;
-
-    CXFA_ContainerLayoutItem* pChildItem =
-        static_cast<CXFA_ContainerLayoutItem*>(pLayoutItem->m_pFirstChild);
-    while (pChildItem &&
-           pChildItem->m_pFormNode->GetElementType() != XFA_Element::PageSet) {
-      pChildItem =
-          static_cast<CXFA_ContainerLayoutItem*>(pChildItem->m_pNextSibling);
-    }
-    return pChildItem;
-  }
-
-  static CXFA_ContainerLayoutItem* GetNextSibling(
-      CXFA_ContainerLayoutItem* pLayoutItem) {
-    CXFA_ContainerLayoutItem* pChildItem =
-        static_cast<CXFA_ContainerLayoutItem*>(pLayoutItem->m_pNextSibling);
-    while (pChildItem &&
-           pChildItem->m_pFormNode->GetElementType() != XFA_Element::PageSet) {
-      pChildItem =
-          static_cast<CXFA_ContainerLayoutItem*>(pChildItem->m_pNextSibling);
-    }
-    return pChildItem;
-  }
-
-  static CXFA_ContainerLayoutItem* GetParent(
-      CXFA_ContainerLayoutItem* pLayoutItem) {
-    return static_cast<CXFA_ContainerLayoutItem*>(pLayoutItem->m_pParent);
-  }
-};
-
-uint32_t GetRelevant(CXFA_Node* pFormItem, uint32_t dwParentRelvant) {
-  uint32_t dwRelevant = XFA_WidgetStatus_Viewable | XFA_WidgetStatus_Printable;
-  WideString wsRelevant =
-      pFormItem->JSObject()->GetCData(XFA_Attribute::Relevant);
-  if (!wsRelevant.IsEmpty()) {
-    if (wsRelevant == L"+print" || wsRelevant == L"print")
-      dwRelevant &= ~XFA_WidgetStatus_Viewable;
-    else if (wsRelevant == L"-print")
-      dwRelevant &= ~XFA_WidgetStatus_Printable;
-  }
-
-  if (!(dwParentRelvant & XFA_WidgetStatus_Viewable) &&
-      (dwRelevant != XFA_WidgetStatus_Viewable)) {
-    dwRelevant &= ~XFA_WidgetStatus_Viewable;
-  }
-
-  if (!(dwParentRelvant & XFA_WidgetStatus_Printable) &&
-      (dwRelevant != XFA_WidgetStatus_Printable)) {
-    dwRelevant &= ~XFA_WidgetStatus_Printable;
-  }
-  return dwRelevant;
-}
-
-void SyncContainer(CXFA_FFNotify* pNotify,
-                   CXFA_LayoutProcessor* pDocLayout,
-                   CXFA_LayoutItem* pContainerItem,
-                   uint32_t dwRelevant,
-                   bool bVisible,
-                   int32_t nPageIndex) {
-  bool bVisibleItem = false;
-  uint32_t dwStatus = 0;
-  uint32_t dwRelevantContainer = 0;
-  if (bVisible) {
-    XFA_AttributeEnum eAttributeValue =
-        pContainerItem->m_pFormNode->JSObject()
-            ->TryEnum(XFA_Attribute::Presence, true)
-            .value_or(XFA_AttributeEnum::Visible);
-    if (eAttributeValue == XFA_AttributeEnum::Visible)
-      bVisibleItem = true;
-
-    dwRelevantContainer = GetRelevant(pContainerItem->m_pFormNode, dwRelevant);
-    dwStatus =
-        (bVisibleItem ? XFA_WidgetStatus_Visible : 0) | dwRelevantContainer;
-  }
-  pNotify->OnLayoutItemAdded(pDocLayout, pContainerItem, nPageIndex, dwStatus);
-  for (CXFA_LayoutItem* pChild = pContainerItem->m_pFirstChild; pChild;
-       pChild = pChild->m_pNextSibling) {
-    if (pChild->IsContentLayoutItem()) {
-      SyncContainer(pNotify, pDocLayout, pChild, dwRelevantContainer,
-                    bVisibleItem, nPageIndex);
-    }
-  }
-}
-
-void ReorderLayoutItemToTail(CXFA_ContainerLayoutItem* pLayoutItem) {
-  CXFA_ContainerLayoutItem* pParentLayoutItem =
-      static_cast<CXFA_ContainerLayoutItem*>(pLayoutItem->m_pParent);
-  if (!pParentLayoutItem)
-    return;
-
-  pParentLayoutItem->RemoveChild(pLayoutItem);
-  pParentLayoutItem->AddChild(pLayoutItem);
-}
-
-void RemoveLayoutItem(CXFA_ContainerLayoutItem* pLayoutItem) {
-  CXFA_ContainerLayoutItem* pParentLayoutItem =
-      static_cast<CXFA_ContainerLayoutItem*>(pLayoutItem->m_pParent);
-  if (!pParentLayoutItem)
-    return;
-
-  pParentLayoutItem->RemoveChild(pLayoutItem);
-}
-
-CXFA_Node* ResolveBreakTarget(CXFA_Node* pPageSetRoot,
-                              bool bNewExprStyle,
-                              WideString& wsTargetAll) {
-  if (!pPageSetRoot)
-    return nullptr;
-
-  CXFA_Document* pDocument = pPageSetRoot->GetDocument();
-  if (wsTargetAll.IsEmpty())
-    return nullptr;
-
-  wsTargetAll.Trim();
-  int32_t iSplitIndex = 0;
-  bool bTargetAllFind = true;
-  while (iSplitIndex != -1) {
-    WideString wsExpr;
-    Optional<size_t> iSplitNextIndex = 0;
-    if (!bTargetAllFind) {
-      iSplitNextIndex = wsTargetAll.Find(' ', iSplitIndex);
-      if (!iSplitNextIndex.has_value())
-        return nullptr;
-      wsExpr =
-          wsTargetAll.Mid(iSplitIndex, iSplitNextIndex.value() - iSplitIndex);
-    } else {
-      wsExpr = wsTargetAll;
-    }
-    if (wsExpr.IsEmpty())
-      return nullptr;
-
-    bTargetAllFind = false;
-    if (wsExpr[0] == '#') {
-      CXFA_Node* pNode = pDocument->GetNodeByID(
-          ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Template)),
-          wsExpr.Right(wsExpr.GetLength() - 1).AsStringView());
-      if (pNode)
-        return pNode;
-    } else if (bNewExprStyle) {
-      WideString wsProcessedTarget = wsExpr;
-      if (wsExpr.Left(4) == L"som(" && wsExpr.Last() == L')') {
-        wsProcessedTarget = wsExpr.Mid(4, wsExpr.GetLength() - 5);
-      }
-      XFA_RESOLVENODE_RS rs;
-      bool iRet = pDocument->GetScriptContext()->ResolveObjects(
-          pPageSetRoot, wsProcessedTarget.AsStringView(), &rs,
-          XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes | XFA_RESOLVENODE_Siblings |
-              XFA_RESOLVENODE_Parent,
-          nullptr);
-      if (iRet && rs.objects.front()->IsNode())
-        return rs.objects.front()->AsNode();
-    }
-    iSplitIndex = iSplitNextIndex.value();
-  }
-  return nullptr;
-}
-
-void SetLayoutGeneratedNodeFlag(CXFA_Node* pNode) {
-  pNode->SetFlag(XFA_NodeFlag_LayoutGeneratedNode, false);
-  pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
-}
-
-bool CheckContentAreaNotUsed(
-    CXFA_ContainerLayoutItem* pPageAreaLayoutItem,
-    CXFA_Node* pContentArea,
-    CXFA_ContainerLayoutItem*& pContentAreaLayoutItem) {
-  for (CXFA_ContainerLayoutItem* pLayoutItem =
-           static_cast<CXFA_ContainerLayoutItem*>(
-               pPageAreaLayoutItem->m_pFirstChild);
-       pLayoutItem; pLayoutItem = static_cast<CXFA_ContainerLayoutItem*>(
-                        pLayoutItem->m_pNextSibling)) {
-    if (pLayoutItem->m_pFormNode == pContentArea) {
-      if (!pLayoutItem->m_pFirstChild) {
-        pContentAreaLayoutItem = pLayoutItem;
-        return true;
-      }
-      return false;
-    }
-  }
-  return true;
-}
-
-void SyncRemoveLayoutItem(CXFA_LayoutItem* pParentLayoutItem,
-                          CXFA_FFNotify* pNotify,
-                          CXFA_LayoutProcessor* pDocLayout) {
-  CXFA_LayoutItem* pNextLayoutItem;
-  CXFA_LayoutItem* pCurLayoutItem = pParentLayoutItem->m_pFirstChild;
-  while (pCurLayoutItem) {
-    pNextLayoutItem = pCurLayoutItem->m_pNextSibling;
-    if (pCurLayoutItem->m_pFirstChild)
-      SyncRemoveLayoutItem(pCurLayoutItem, pNotify, pDocLayout);
-
-    pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem);
-    delete pCurLayoutItem;
-    pCurLayoutItem = pNextLayoutItem;
-  }
-}
-
-bool RunBreakTestScript(CXFA_Script* pTestScript) {
-  WideString wsExpression = pTestScript->JSObject()->GetContent(false);
-  if (wsExpression.IsEmpty())
-    return true;
-  return pTestScript->GetDocument()->GetNotify()->RunScript(
-      pTestScript, pTestScript->GetContainerParent());
-}
-
-}  // namespace
-
-class CXFA_ContainerRecord {
- public:
-  CXFA_ContainerRecord(CXFA_ContainerLayoutItem* pPageSet = nullptr,
-                       CXFA_ContainerLayoutItem* pPageArea = nullptr,
-                       CXFA_ContainerLayoutItem* pContentArea = nullptr)
-      : pCurPageSet(pPageSet),
-        pCurPageArea(pPageArea),
-        pCurContentArea(pContentArea) {}
-
-  CXFA_ContainerLayoutItem* pCurPageSet;
-  CXFA_ContainerLayoutItem* pCurPageArea;
-  CXFA_ContainerLayoutItem* pCurContentArea;
-};
-
-CXFA_LayoutPageMgr::CXFA_LayoutPageMgr(CXFA_LayoutProcessor* pLayoutProcessor)
-    : m_pLayoutProcessor(pLayoutProcessor),
-      m_pTemplatePageSetRoot(nullptr),
-      m_pPageSetLayoutItemRoot(nullptr),
-      m_pPageSetCurRoot(nullptr),
-      m_CurrentContainerRecordIter(m_ProposedContainerRecords.end()),
-      m_pCurPageArea(nullptr),
-      m_nAvailPages(0),
-      m_nCurPageCount(0),
-      m_ePageSetMode(XFA_AttributeEnum::OrderedOccurrence),
-      m_bCreateOverFlowPage(false) {}
-
-CXFA_LayoutPageMgr::~CXFA_LayoutPageMgr() {
-  ClearData();
-  CXFA_LayoutItem* pLayoutItem = GetRootLayoutItem();
-  CXFA_LayoutItem* pNextLayout = nullptr;
-  for (; pLayoutItem; pLayoutItem = pNextLayout) {
-    pNextLayout = pLayoutItem->m_pNextSibling;
-    XFA_ReleaseLayoutItem(pLayoutItem);
-  }
-}
-
-bool CXFA_LayoutPageMgr::InitLayoutPage(CXFA_Node* pFormNode) {
-  PrepareLayout();
-  CXFA_Node* pTemplateNode = pFormNode->GetTemplateNodeIfExists();
-  if (!pTemplateNode)
-    return false;
-
-  m_pTemplatePageSetRoot =
-      pTemplateNode->JSObject()->GetOrCreateProperty<CXFA_PageSet>(
-          0, XFA_Element::PageSet);
-  ASSERT(m_pTemplatePageSetRoot);
-
-  if (m_pPageSetLayoutItemRoot) {
-    m_pPageSetLayoutItemRoot->m_pParent = nullptr;
-    m_pPageSetLayoutItemRoot->m_pFirstChild = nullptr;
-    m_pPageSetLayoutItemRoot->m_pNextSibling = nullptr;
-    m_pPageSetLayoutItemRoot->m_pFormNode = m_pTemplatePageSetRoot;
-  } else {
-    m_pPageSetLayoutItemRoot =
-        new CXFA_ContainerLayoutItem(m_pTemplatePageSetRoot);
-  }
-  m_pPageSetCurRoot = m_pPageSetLayoutItemRoot;
-  m_pTemplatePageSetRoot->JSObject()->SetLayoutItem(m_pPageSetLayoutItemRoot);
-
-  XFA_AttributeEnum eRelation =
-      m_pTemplatePageSetRoot->JSObject()->GetEnum(XFA_Attribute::Relation);
-  if (eRelation != XFA_AttributeEnum::Unknown)
-    m_ePageSetMode = eRelation;
-
-  InitPageSetMap();
-  CXFA_Node* pPageArea = nullptr;
-  int32_t iCount = 0;
-  for (pPageArea = m_pTemplatePageSetRoot->GetFirstChild(); pPageArea;
-       pPageArea = pPageArea->GetNextSibling()) {
-    if (pPageArea->GetElementType() == XFA_Element::PageArea) {
-      iCount++;
-      if (pPageArea->GetFirstChildByClass<CXFA_ContentArea>(
-              XFA_Element::ContentArea))
-        return true;
-    }
-  }
-  if (iCount > 0)
-    return false;
-
-  CXFA_Document* pDocument = pTemplateNode->GetDocument();
-  pPageArea = m_pTemplatePageSetRoot->GetChild<CXFA_Node>(
-      0, XFA_Element::PageArea, false);
-  if (!pPageArea) {
-    pPageArea = pDocument->CreateNode(m_pTemplatePageSetRoot->GetPacketType(),
-                                      XFA_Element::PageArea);
-    if (!pPageArea)
-      return false;
-
-    m_pTemplatePageSetRoot->InsertChild(pPageArea, nullptr);
-    pPageArea->SetFlag(XFA_NodeFlag_Initialized, true);
-  }
-  CXFA_ContentArea* pContentArea =
-      pPageArea->GetChild<CXFA_ContentArea>(0, XFA_Element::ContentArea, false);
-  if (!pContentArea) {
-    pContentArea = static_cast<CXFA_ContentArea*>(pDocument->CreateNode(
-        pPageArea->GetPacketType(), XFA_Element::ContentArea));
-    if (!pContentArea)
-      return false;
-
-    pPageArea->InsertChild(pContentArea, nullptr);
-    pContentArea->SetFlag(XFA_NodeFlag_Initialized, true);
-    pContentArea->JSObject()->SetMeasure(
-        XFA_Attribute::X, CXFA_Measurement(0.25f, XFA_Unit::In), false);
-    pContentArea->JSObject()->SetMeasure(
-        XFA_Attribute::Y, CXFA_Measurement(0.25f, XFA_Unit::In), false);
-    pContentArea->JSObject()->SetMeasure(
-        XFA_Attribute::W, CXFA_Measurement(8.0f, XFA_Unit::In), false);
-    pContentArea->JSObject()->SetMeasure(
-        XFA_Attribute::H, CXFA_Measurement(10.5f, XFA_Unit::In), false);
-  }
-  CXFA_Medium* pMedium =
-      pPageArea->GetChild<CXFA_Medium>(0, XFA_Element::Medium, false);
-  if (!pMedium) {
-    pMedium = static_cast<CXFA_Medium*>(
-        pDocument->CreateNode(pPageArea->GetPacketType(), XFA_Element::Medium));
-    if (!pContentArea)
-      return false;
-
-    pPageArea->InsertChild(pMedium, nullptr);
-    pMedium->SetFlag(XFA_NodeFlag_Initialized, true);
-    pMedium->JSObject()->SetMeasure(
-        XFA_Attribute::Short, CXFA_Measurement(8.5f, XFA_Unit::In), false);
-    pMedium->JSObject()->SetMeasure(
-        XFA_Attribute::Long, CXFA_Measurement(11.0f, XFA_Unit::In), false);
-  }
-  return true;
-}
-
-bool CXFA_LayoutPageMgr::PrepareFirstPage(CXFA_Node* pRootSubform) {
-  bool bProBreakBefore = false;
-  CXFA_Node* pBreakBeforeNode = nullptr;
-  while (pRootSubform) {
-    for (CXFA_Node* pBreakNode = pRootSubform->GetFirstChild(); pBreakNode;
-         pBreakNode = pBreakNode->GetNextSibling()) {
-      XFA_Element eType = pBreakNode->GetElementType();
-      if (eType == XFA_Element::BreakBefore ||
-          (eType == XFA_Element::Break &&
-           pBreakNode->JSObject()->GetEnum(XFA_Attribute::Before) !=
-               XFA_AttributeEnum::Auto)) {
-        bProBreakBefore = true;
-        pBreakBeforeNode = pBreakNode;
-        break;
-      }
-    }
-    if (bProBreakBefore)
-      break;
-
-    bProBreakBefore = true;
-    pRootSubform =
-        pRootSubform->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
-    while (pRootSubform &&
-           !XFA_ItemLayoutProcessor_IsTakingSpace(pRootSubform)) {
-      pRootSubform = pRootSubform->GetNextSameClassSibling<CXFA_Subform>(
-          XFA_Element::Subform);
-    }
-  }
-  CXFA_Node* pLeader;
-  CXFA_Node* pTrailer;
-  if (pBreakBeforeNode &&
-      ExecuteBreakBeforeOrAfter(pBreakBeforeNode, true, pLeader, pTrailer)) {
-    m_CurrentContainerRecordIter = m_ProposedContainerRecords.begin();
-    return true;
-  }
-  return AppendNewPage(true);
-}
-
-bool CXFA_LayoutPageMgr::AppendNewPage(bool bFirstTemPage) {
-  if (m_CurrentContainerRecordIter != GetTailPosition())
-    return true;
-
-  CXFA_Node* pPageNode = GetNextAvailPageArea(nullptr);
-  if (!pPageNode)
-    return false;
-
-  if (bFirstTemPage &&
-      m_CurrentContainerRecordIter == m_ProposedContainerRecords.end()) {
-    m_CurrentContainerRecordIter = m_ProposedContainerRecords.begin();
-  }
-  return !bFirstTemPage ||
-         m_CurrentContainerRecordIter != m_ProposedContainerRecords.end();
-}
-
-void CXFA_LayoutPageMgr::RemoveLayoutRecord(CXFA_ContainerRecord* pNewRecord,
-                                            CXFA_ContainerRecord* pPrevRecord) {
-  if (!pNewRecord || !pPrevRecord)
-    return;
-  if (pNewRecord->pCurPageSet != pPrevRecord->pCurPageSet) {
-    RemoveLayoutItem(pNewRecord->pCurPageSet);
-    return;
-  }
-  if (pNewRecord->pCurPageArea != pPrevRecord->pCurPageArea) {
-    RemoveLayoutItem(pNewRecord->pCurPageArea);
-    return;
-  }
-  if (pNewRecord->pCurContentArea != pPrevRecord->pCurContentArea) {
-    RemoveLayoutItem(pNewRecord->pCurContentArea);
-    return;
-  }
-}
-
-void CXFA_LayoutPageMgr::ReorderPendingLayoutRecordToTail(
-    CXFA_ContainerRecord* pNewRecord,
-    CXFA_ContainerRecord* pPrevRecord) {
-  if (!pNewRecord || !pPrevRecord)
-    return;
-  if (pNewRecord->pCurPageSet != pPrevRecord->pCurPageSet) {
-    ReorderLayoutItemToTail(pNewRecord->pCurPageSet);
-    return;
-  }
-  if (pNewRecord->pCurPageArea != pPrevRecord->pCurPageArea) {
-    ReorderLayoutItemToTail(pNewRecord->pCurPageArea);
-    return;
-  }
-  if (pNewRecord->pCurContentArea != pPrevRecord->pCurContentArea) {
-    ReorderLayoutItemToTail(pNewRecord->pCurContentArea);
-    return;
-  }
-}
-
-void CXFA_LayoutPageMgr::SubmitContentItem(
-    CXFA_ContentLayoutItem* pContentLayoutItem,
-    XFA_ItemLayoutProcessorResult eStatus) {
-  if (pContentLayoutItem) {
-    GetCurrentContainerRecord()->pCurContentArea->AddChild(pContentLayoutItem);
-    m_bCreateOverFlowPage = false;
-  }
-
-  if (eStatus != XFA_ItemLayoutProcessorResult::Done) {
-    if (eStatus == XFA_ItemLayoutProcessorResult::PageFullBreak &&
-        m_CurrentContainerRecordIter == GetTailPosition()) {
-      AppendNewPage();
-    }
-    m_CurrentContainerRecordIter = GetTailPosition();
-    m_pCurPageArea = GetCurrentContainerRecord()->pCurPageArea->m_pFormNode;
-  }
-}
-
-float CXFA_LayoutPageMgr::GetAvailHeight() {
-  CXFA_ContainerLayoutItem* pLayoutItem =
-      GetCurrentContainerRecord()->pCurContentArea;
-  if (!pLayoutItem || !pLayoutItem->m_pFormNode)
-    return 0.0f;
-
-  float fAvailHeight = pLayoutItem->m_pFormNode->JSObject()
-                           ->GetMeasure(XFA_Attribute::H)
-                           .ToUnit(XFA_Unit::Pt);
-  if (fAvailHeight >= XFA_LAYOUT_FLOAT_PERCISION)
-    return fAvailHeight;
-  if (m_CurrentContainerRecordIter == m_ProposedContainerRecords.begin())
-    return 0.0f;
-  return FLT_MAX;
-}
-
-CXFA_ContainerRecord* CXFA_LayoutPageMgr::CreateContainerRecord(
-    CXFA_Node* pPageNode,
-    bool bCreateNew) {
-  CXFA_ContainerRecord* pNewRecord = new CXFA_ContainerRecord();
-  if (m_CurrentContainerRecordIter != m_ProposedContainerRecords.end()) {
-    if (!IsPageSetRootOrderedOccurrence() || !pPageNode) {
-      *pNewRecord = *GetCurrentContainerRecord();
-      m_ProposedContainerRecords.push_back(pNewRecord);
-      return pNewRecord;
-    }
-    CXFA_Node* pPageSet = pPageNode->GetParent();
-    if (!bCreateNew) {
-      if (pPageSet == m_pTemplatePageSetRoot) {
-        pNewRecord->pCurPageSet = m_pPageSetCurRoot;
-      } else {
-        CXFA_ContainerLayoutItem* pParentLayoutItem =
-            static_cast<CXFA_ContainerLayoutItem*>(
-                pPageSet->JSObject()->GetLayoutItem());
-        if (!pParentLayoutItem)
-          pParentLayoutItem = m_pPageSetCurRoot;
-
-        pNewRecord->pCurPageSet = pParentLayoutItem;
-      }
-    } else {
-      CXFA_ContainerLayoutItem* pParentPageSetLayout = nullptr;
-      if (pPageSet == GetCurrentContainerRecord()->pCurPageSet->m_pFormNode) {
-        pParentPageSetLayout = static_cast<CXFA_ContainerLayoutItem*>(
-            GetCurrentContainerRecord()->pCurPageSet->m_pParent);
-      } else {
-        pParentPageSetLayout = static_cast<CXFA_ContainerLayoutItem*>(
-            pPageSet->GetParent()->JSObject()->GetLayoutItem());
-      }
-      CXFA_ContainerLayoutItem* pPageSetLayoutItem =
-          new CXFA_ContainerLayoutItem(pPageSet);
-      pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem);
-      if (!pParentPageSetLayout) {
-        CXFA_ContainerLayoutItem* pPrePageSet = m_pPageSetLayoutItemRoot;
-        while (pPrePageSet->m_pNextSibling) {
-          pPrePageSet = static_cast<CXFA_ContainerLayoutItem*>(
-              pPrePageSet->m_pNextSibling);
-        }
-
-        pPrePageSet->m_pNextSibling = pPageSetLayoutItem;
-        m_pPageSetCurRoot = pPageSetLayoutItem;
-      } else {
-        pParentPageSetLayout->AddChild(pPageSetLayoutItem);
-      }
-      pNewRecord->pCurPageSet = pPageSetLayoutItem;
-    }
-  } else {
-    if (pPageNode) {
-      CXFA_Node* pPageSet = pPageNode->GetParent();
-      if (pPageSet == m_pTemplatePageSetRoot) {
-        pNewRecord->pCurPageSet = m_pPageSetLayoutItemRoot;
-      } else {
-        CXFA_ContainerLayoutItem* pPageSetLayoutItem =
-            new CXFA_ContainerLayoutItem(pPageSet);
-        pPageSet->JSObject()->SetLayoutItem(pPageSetLayoutItem);
-        m_pPageSetLayoutItemRoot->AddChild(pPageSetLayoutItem);
-        pNewRecord->pCurPageSet = pPageSetLayoutItem;
-      }
-    } else {
-      pNewRecord->pCurPageSet = m_pPageSetLayoutItemRoot;
-    }
-  }
-  m_ProposedContainerRecords.push_back(pNewRecord);
-  return pNewRecord;
-}
-
-void CXFA_LayoutPageMgr::AddPageAreaLayoutItem(CXFA_ContainerRecord* pNewRecord,
-                                               CXFA_Node* pNewPageArea) {
-  CXFA_ContainerLayoutItem* pNewPageAreaLayoutItem = nullptr;
-  if (pdfium::IndexInBounds(m_PageArray, m_nAvailPages)) {
-    CXFA_ContainerLayoutItem* pContainerItem = m_PageArray[m_nAvailPages];
-    pContainerItem->m_pFormNode = pNewPageArea;
-    m_nAvailPages++;
-    pNewPageAreaLayoutItem = pContainerItem;
-  } else {
-    CXFA_FFNotify* pNotify = pNewPageArea->GetDocument()->GetNotify();
-    auto* pContainerItem = pNotify->OnCreateContainerLayoutItem(pNewPageArea);
-    m_PageArray.push_back(pContainerItem);
-    m_nAvailPages++;
-    pNotify->OnPageEvent(pContainerItem, XFA_PAGEVIEWEVENT_PostRemoved);
-    pNewPageAreaLayoutItem = pContainerItem;
-  }
-  pNewRecord->pCurPageSet->AddChild(pNewPageAreaLayoutItem);
-  pNewRecord->pCurPageArea = pNewPageAreaLayoutItem;
-  pNewRecord->pCurContentArea = nullptr;
-}
-
-void CXFA_LayoutPageMgr::AddContentAreaLayoutItem(
-    CXFA_ContainerRecord* pNewRecord,
-    CXFA_Node* pContentArea) {
-  if (!pContentArea) {
-    pNewRecord->pCurContentArea = nullptr;
-    return;
-  }
-  CXFA_ContainerLayoutItem* pNewContentAreaLayoutItem =
-      new CXFA_ContainerLayoutItem(pContentArea);
-  ASSERT(pNewRecord->pCurPageArea);
-  pNewRecord->pCurPageArea->AddChild(pNewContentAreaLayoutItem);
-  pNewRecord->pCurContentArea = pNewContentAreaLayoutItem;
-}
-
-void CXFA_LayoutPageMgr::FinishPaginatedPageSets() {
-  CXFA_ContainerLayoutItem* pRootPageSetLayoutItem = m_pPageSetLayoutItemRoot;
-  for (; pRootPageSetLayoutItem;
-       pRootPageSetLayoutItem = static_cast<CXFA_ContainerLayoutItem*>(
-           pRootPageSetLayoutItem->m_pNextSibling)) {
-    CXFA_NodeIteratorTemplate<CXFA_ContainerLayoutItem,
-                              PageSetContainerLayoutItem>
-        sIterator(pRootPageSetLayoutItem);
-    for (CXFA_ContainerLayoutItem* pPageSetLayoutItem = sIterator.GetCurrent();
-         pPageSetLayoutItem; pPageSetLayoutItem = sIterator.MoveToNext()) {
-      XFA_AttributeEnum ePageRelation =
-          pPageSetLayoutItem->m_pFormNode->JSObject()->GetEnum(
-              XFA_Attribute::Relation);
-      switch (ePageRelation) {
-        case XFA_AttributeEnum::OrderedOccurrence:
-        default: { ProcessLastPageSet(); } break;
-        case XFA_AttributeEnum::SimplexPaginated:
-        case XFA_AttributeEnum::DuplexPaginated: {
-          CXFA_LayoutItem* pLastPageAreaLayoutItem = nullptr;
-          int32_t nPageAreaCount = 0;
-          for (CXFA_LayoutItem* pPageAreaLayoutItem =
-                   pPageSetLayoutItem->m_pFirstChild;
-               pPageAreaLayoutItem;
-               pPageAreaLayoutItem = pPageAreaLayoutItem->m_pNextSibling) {
-            if (pPageAreaLayoutItem->m_pFormNode->GetElementType() !=
-                XFA_Element::PageArea) {
-              continue;
-            }
-            nPageAreaCount++;
-            pLastPageAreaLayoutItem = pPageAreaLayoutItem;
-          }
-          if (!pLastPageAreaLayoutItem)
-            break;
-
-          if (!FindPageAreaFromPageSet_SimplexDuplex(
-                  pPageSetLayoutItem->m_pFormNode, nullptr, nullptr, nullptr,
-                  true, true,
-                  nPageAreaCount == 1 ? XFA_AttributeEnum::Only
-                                      : XFA_AttributeEnum::Last) &&
-              (nPageAreaCount == 1 &&
-               !FindPageAreaFromPageSet_SimplexDuplex(
-                   pPageSetLayoutItem->m_pFormNode, nullptr, nullptr, nullptr,
-                   true, true, XFA_AttributeEnum::Last))) {
-            break;
-          }
-          CXFA_Node* pNode = m_pCurPageArea;
-          XFA_AttributeEnum eCurChoice =
-              pNode->JSObject()->GetEnum(XFA_Attribute::PagePosition);
-          if (eCurChoice == XFA_AttributeEnum::Last) {
-            XFA_AttributeEnum eOddOrEven =
-                pNode->JSObject()->GetEnum(XFA_Attribute::OddOrEven);
-            XFA_AttributeEnum eLastChoice =
-                pLastPageAreaLayoutItem->m_pFormNode->JSObject()->GetEnum(
-                    XFA_Attribute::PagePosition);
-            if (eLastChoice == XFA_AttributeEnum::First &&
-                (ePageRelation == XFA_AttributeEnum::SimplexPaginated ||
-                 eOddOrEven != XFA_AttributeEnum::Odd)) {
-              CXFA_ContainerRecord* pRecord = CreateContainerRecord();
-              AddPageAreaLayoutItem(pRecord, pNode);
-              break;
-            }
-          }
-          bool bUsable = true;
-          std::vector<float> rgUsedHeights;
-          for (CXFA_LayoutItem* pChildLayoutItem =
-                   pLastPageAreaLayoutItem->m_pFirstChild;
-               pChildLayoutItem;
-               pChildLayoutItem = pChildLayoutItem->m_pNextSibling) {
-            if (pChildLayoutItem->m_pFormNode->GetElementType() !=
-                XFA_Element::ContentArea) {
-              continue;
-            }
-            float fUsedHeight = 0;
-            for (CXFA_LayoutItem* pContentChildLayoutItem =
-                     pChildLayoutItem->m_pFirstChild;
-                 pContentChildLayoutItem;
-                 pContentChildLayoutItem =
-                     pContentChildLayoutItem->m_pNextSibling) {
-              if (CXFA_ContentLayoutItem* pContent =
-                      pContentChildLayoutItem->AsContentLayoutItem()) {
-                fUsedHeight += pContent->m_sSize.height;
-              }
-            }
-            rgUsedHeights.push_back(fUsedHeight);
-          }
-          int32_t iCurContentAreaIndex = -1;
-          for (CXFA_Node* pContentAreaNode = pNode->GetFirstChild();
-               pContentAreaNode;
-               pContentAreaNode = pContentAreaNode->GetNextSibling()) {
-            if (pContentAreaNode->GetElementType() !=
-                XFA_Element::ContentArea) {
-              continue;
-            }
-            iCurContentAreaIndex++;
-            if (rgUsedHeights[iCurContentAreaIndex] >
-                pContentAreaNode->JSObject()
-                        ->GetMeasure(XFA_Attribute::H)
-                        .ToUnit(XFA_Unit::Pt) +
-                    XFA_LAYOUT_FLOAT_PERCISION) {
-              bUsable = false;
-              break;
-            }
-          }
-          if (bUsable) {
-            CXFA_LayoutItem* pChildLayoutItem =
-                pLastPageAreaLayoutItem->m_pFirstChild;
-            CXFA_Node* pContentAreaNode = pNode->GetFirstChild();
-            pLastPageAreaLayoutItem->m_pFormNode = pNode;
-            while (pChildLayoutItem && pContentAreaNode) {
-              if (pChildLayoutItem->m_pFormNode->GetElementType() !=
-                  XFA_Element::ContentArea) {
-                pChildLayoutItem = pChildLayoutItem->m_pNextSibling;
-                continue;
-              }
-              if (pContentAreaNode->GetElementType() !=
-                  XFA_Element::ContentArea) {
-                pContentAreaNode = pContentAreaNode->GetNextSibling();
-                continue;
-              }
-              pChildLayoutItem->m_pFormNode = pContentAreaNode;
-              pChildLayoutItem = pChildLayoutItem->m_pNextSibling;
-              pContentAreaNode = pContentAreaNode->GetNextSibling();
-            }
-          } else if (pNode->JSObject()->GetEnum(XFA_Attribute::PagePosition) ==
-                     XFA_AttributeEnum::Last) {
-            CXFA_ContainerRecord* pRecord = CreateContainerRecord();
-            AddPageAreaLayoutItem(pRecord, pNode);
-          }
-        } break;
-      }
-    }
-  }
-}
-
-int32_t CXFA_LayoutPageMgr::GetPageCount() const {
-  return pdfium::CollectionSize<int32_t>(m_PageArray);
-}
-
-CXFA_ContainerLayoutItem* CXFA_LayoutPageMgr::GetPage(int32_t index) const {
-  if (!pdfium::IndexInBounds(m_PageArray, index))
-    return nullptr;
-  return m_PageArray[index];
-}
-
-int32_t CXFA_LayoutPageMgr::GetPageIndex(
-    const CXFA_ContainerLayoutItem* pPage) const {
-  auto it = std::find(m_PageArray.begin(), m_PageArray.end(), pPage);
-  return it != m_PageArray.end() ? it - m_PageArray.begin() : -1;
-}
-
-bool CXFA_LayoutPageMgr::RunBreak(XFA_Element eBreakType,
-                                  XFA_AttributeEnum eTargetType,
-                                  CXFA_Node* pTarget,
-                                  bool bStartNew) {
-  bool bRet = false;
-  switch (eTargetType) {
-    case XFA_AttributeEnum::ContentArea:
-      if (pTarget && pTarget->GetElementType() != XFA_Element::ContentArea)
-        pTarget = nullptr;
-      if (!pTarget ||
-          m_CurrentContainerRecordIter == m_ProposedContainerRecords.end() ||
-          pTarget !=
-              GetCurrentContainerRecord()->pCurContentArea->m_pFormNode ||
-          bStartNew) {
-        CXFA_Node* pPageArea = nullptr;
-        if (pTarget)
-          pPageArea = pTarget->GetParent();
-
-        pPageArea = GetNextAvailPageArea(pPageArea, pTarget);
-        bRet = !!pPageArea;
-      }
-      break;
-    case XFA_AttributeEnum::PageArea:
-      if (pTarget && pTarget->GetElementType() != XFA_Element::PageArea)
-        pTarget = nullptr;
-      if (!pTarget ||
-          m_CurrentContainerRecordIter == m_ProposedContainerRecords.end() ||
-          pTarget != GetCurrentContainerRecord()->pCurPageArea->m_pFormNode ||
-          bStartNew) {
-        CXFA_Node* pPageArea = GetNextAvailPageArea(pTarget, nullptr, true);
-        bRet = !!pPageArea;
-      }
-      break;
-    case XFA_AttributeEnum::PageOdd:
-      if (pTarget && pTarget->GetElementType() != XFA_Element::PageArea)
-        pTarget = nullptr;
-      break;
-    case XFA_AttributeEnum::PageEven:
-      if (pTarget && pTarget->GetElementType() != XFA_Element::PageArea)
-        pTarget = nullptr;
-      break;
-    case XFA_AttributeEnum::Auto:
-    default:
-      break;
-  }
-  return bRet;
-}
-
-bool CXFA_LayoutPageMgr::ExecuteBreakBeforeOrAfter(
-    CXFA_Node* pCurNode,
-    bool bBefore,
-    CXFA_Node*& pBreakLeaderTemplate,
-    CXFA_Node*& pBreakTrailerTemplate) {
-  XFA_Element eType = pCurNode->GetElementType();
-  switch (eType) {
-    case XFA_Element::BreakBefore:
-    case XFA_Element::BreakAfter: {
-      WideString wsBreakLeader;
-      WideString wsBreakTrailer;
-      CXFA_Node* pFormNode = pCurNode->GetContainerParent();
-      CXFA_Node* pContainer = pFormNode->GetTemplateNodeIfExists();
-      bool bStartNew =
-          pCurNode->JSObject()->GetInteger(XFA_Attribute::StartNew) != 0;
-      CXFA_Script* pScript =
-          pCurNode->GetFirstChildByClass<CXFA_Script>(XFA_Element::Script);
-      if (pScript && !RunBreakTestScript(pScript))
-        return false;
-
-      WideString wsTarget =
-          pCurNode->JSObject()->GetCData(XFA_Attribute::Target);
-      CXFA_Node* pTarget =
-          ResolveBreakTarget(m_pTemplatePageSetRoot, true, wsTarget);
-      wsBreakTrailer = pCurNode->JSObject()->GetCData(XFA_Attribute::Trailer);
-      wsBreakLeader = pCurNode->JSObject()->GetCData(XFA_Attribute::Leader);
-      pBreakLeaderTemplate =
-          ResolveBreakTarget(pContainer, true, wsBreakLeader);
-      pBreakTrailerTemplate =
-          ResolveBreakTarget(pContainer, true, wsBreakTrailer);
-      if (RunBreak(eType,
-                   pCurNode->JSObject()->GetEnum(XFA_Attribute::TargetType),
-                   pTarget, bStartNew)) {
-        return true;
-      }
-      if (!m_ProposedContainerRecords.empty() &&
-          m_CurrentContainerRecordIter == m_ProposedContainerRecords.begin() &&
-          eType == XFA_Element::BreakBefore) {
-        CXFA_Node* pParentNode = pFormNode->GetContainerParent();
-        if (!pParentNode ||
-            pFormNode != pParentNode->GetFirstContainerChild()) {
-          break;
-        }
-        pParentNode = pParentNode->GetParent();
-        if (!pParentNode ||
-            pParentNode->GetElementType() != XFA_Element::Form) {
-          break;
-        }
-        return true;
-      }
-      break;
-    }
-    case XFA_Element::Break: {
-      bool bStartNew =
-          pCurNode->JSObject()->GetInteger(XFA_Attribute::StartNew) != 0;
-      WideString wsTarget = pCurNode->JSObject()->GetCData(
-          bBefore ? XFA_Attribute::BeforeTarget : XFA_Attribute::AfterTarget);
-      CXFA_Node* pTarget =
-          ResolveBreakTarget(m_pTemplatePageSetRoot, true, wsTarget);
-      if (RunBreak(bBefore ? XFA_Element::BreakBefore : XFA_Element::BreakAfter,
-                   pCurNode->JSObject()->GetEnum(
-                       bBefore ? XFA_Attribute::Before : XFA_Attribute::After),
-                   pTarget, bStartNew)) {
-        return true;
-      }
-      break;
-    }
-    default:
-      break;
-  }
-  return false;
-}
-
-bool CXFA_LayoutPageMgr::ProcessBreakBeforeOrAfter(
-    CXFA_Node* pBreakNode,
-    bool bBefore,
-    CXFA_Node*& pBreakLeaderNode,
-    CXFA_Node*& pBreakTrailerNode,
-    bool& bCreatePage) {
-  CXFA_Node* pLeaderTemplate = nullptr;
-  CXFA_Node* pTrailerTemplate = nullptr;
-  CXFA_Node* pFormNode = pBreakNode->GetContainerParent();
-  if (XFA_ItemLayoutProcessor_IsTakingSpace(pFormNode)) {
-    bCreatePage = ExecuteBreakBeforeOrAfter(pBreakNode, bBefore,
-                                            pLeaderTemplate, pTrailerTemplate);
-    CXFA_Document* pDocument = pBreakNode->GetDocument();
-    CXFA_Node* pDataScope = nullptr;
-    pFormNode = pFormNode->GetContainerParent();
-    if (pLeaderTemplate) {
-      if (!pDataScope)
-        pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
-
-      pBreakLeaderNode = pDocument->DataMerge_CopyContainer(
-          pLeaderTemplate, pFormNode, pDataScope, true, true, true);
-      pDocument->DataMerge_UpdateBindingRelations(pBreakLeaderNode);
-      SetLayoutGeneratedNodeFlag(pBreakLeaderNode);
-    }
-    if (pTrailerTemplate) {
-      if (!pDataScope)
-        pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
-
-      pBreakTrailerNode = pDocument->DataMerge_CopyContainer(
-          pTrailerTemplate, pFormNode, pDataScope, true, true, true);
-      pDocument->DataMerge_UpdateBindingRelations(pBreakTrailerNode);
-      SetLayoutGeneratedNodeFlag(pBreakTrailerNode);
-    }
-    return true;
-  }
-  return false;
-}
-
-bool CXFA_LayoutPageMgr::ProcessBookendLeaderOrTrailer(
-    CXFA_Node* pBookendNode,
-    bool bLeader,
-    CXFA_Node*& pBookendAppendNode) {
-  CXFA_Node* pLeaderTemplate = nullptr;
-  CXFA_Node* pFormNode = pBookendNode->GetContainerParent();
-  if (ResolveBookendLeaderOrTrailer(pBookendNode, bLeader, pLeaderTemplate)) {
-    CXFA_Document* pDocument = pBookendNode->GetDocument();
-    CXFA_Node* pDataScope = nullptr;
-    if (pLeaderTemplate) {
-      if (!pDataScope)
-        pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
-
-      pBookendAppendNode = pDocument->DataMerge_CopyContainer(
-          pLeaderTemplate, pFormNode, pDataScope, true, true, true);
-      pDocument->DataMerge_UpdateBindingRelations(pBookendAppendNode);
-      SetLayoutGeneratedNodeFlag(pBookendAppendNode);
-      return true;
-    }
-  }
-  return false;
-}
-
-CXFA_Node* CXFA_LayoutPageMgr::BreakOverflow(CXFA_Node* pOverflowNode,
-                                             CXFA_Node*& pLeaderTemplate,
-                                             CXFA_Node*& pTrailerTemplate,
-                                             bool bCreatePage) {
-  CXFA_Node* pContainer =
-      pOverflowNode->GetContainerParent()->GetTemplateNodeIfExists();
-  if (pOverflowNode->GetElementType() == XFA_Element::Break) {
-    WideString wsOverflowLeader =
-        pOverflowNode->JSObject()->GetCData(XFA_Attribute::OverflowLeader);
-    WideString wsOverflowTarget =
-        pOverflowNode->JSObject()->GetCData(XFA_Attribute::OverflowTarget);
-    WideString wsOverflowTrailer =
-        pOverflowNode->JSObject()->GetCData(XFA_Attribute::OverflowTrailer);
-    if (wsOverflowTarget.IsEmpty() && wsOverflowLeader.IsEmpty() &&
-        wsOverflowTrailer.IsEmpty()) {
-      return nullptr;
-    }
-
-    if (!wsOverflowTarget.IsEmpty() && bCreatePage && !m_bCreateOverFlowPage) {
-      CXFA_Node* pTarget =
-          ResolveBreakTarget(m_pTemplatePageSetRoot, true, wsOverflowTarget);
-      if (pTarget) {
-        m_bCreateOverFlowPage = true;
-        switch (pTarget->GetElementType()) {
-          case XFA_Element::PageArea:
-            RunBreak(XFA_Element::Overflow, XFA_AttributeEnum::PageArea,
-                     pTarget, true);
-            break;
-          case XFA_Element::ContentArea:
-            RunBreak(XFA_Element::Overflow, XFA_AttributeEnum::ContentArea,
-                     pTarget, true);
-            break;
-          default:
-            break;
-        }
-      }
-    }
-    if (!bCreatePage) {
-      pLeaderTemplate = ResolveBreakTarget(pContainer, true, wsOverflowLeader);
-      pTrailerTemplate =
-          ResolveBreakTarget(pContainer, true, wsOverflowTrailer);
-    }
-    return pOverflowNode;
-  }
-
-  if (pOverflowNode->GetElementType() != XFA_Element::Overflow)
-    return nullptr;
-
-  WideString wsOverflowTarget =
-      pOverflowNode->JSObject()->GetCData(XFA_Attribute::Target);
-  if (!wsOverflowTarget.IsEmpty() && bCreatePage && !m_bCreateOverFlowPage) {
-    CXFA_Node* pTarget =
-        ResolveBreakTarget(m_pTemplatePageSetRoot, true, wsOverflowTarget);
-    if (pTarget) {
-      m_bCreateOverFlowPage = true;
-      switch (pTarget->GetElementType()) {
-        case XFA_Element::PageArea:
-          RunBreak(XFA_Element::Overflow, XFA_AttributeEnum::PageArea, pTarget,
-                   true);
-          break;
-        case XFA_Element::ContentArea:
-          RunBreak(XFA_Element::Overflow, XFA_AttributeEnum::ContentArea,
-                   pTarget, true);
-          break;
-        default:
-          break;
-      }
-    }
-  }
-  if (!bCreatePage) {
-    WideString wsLeader =
-        pOverflowNode->JSObject()->GetCData(XFA_Attribute::Leader);
-    WideString wsTrailer =
-        pOverflowNode->JSObject()->GetCData(XFA_Attribute::Trailer);
-    pLeaderTemplate = ResolveBreakTarget(pContainer, true, wsLeader);
-    pTrailerTemplate = ResolveBreakTarget(pContainer, true, wsTrailer);
-  }
-  return pOverflowNode;
-}
-
-bool CXFA_LayoutPageMgr::ProcessOverflow(CXFA_Node* pFormNode,
-                                         CXFA_Node*& pLeaderNode,
-                                         CXFA_Node*& pTrailerNode,
-                                         bool bDataMerge,
-                                         bool bCreatePage) {
-  if (!pFormNode)
-    return false;
-
-  CXFA_Node* pLeaderTemplate = nullptr;
-  CXFA_Node* pTrailerTemplate = nullptr;
-  bool bIsOverflowNode = false;
-  if (pFormNode->GetElementType() == XFA_Element::Overflow ||
-      pFormNode->GetElementType() == XFA_Element::Break) {
-    bIsOverflowNode = true;
-  }
-  for (CXFA_Node* pCurNode = bIsOverflowNode ? pFormNode
-                                             : pFormNode->GetFirstChild();
-       pCurNode; pCurNode = pCurNode->GetNextSibling()) {
-    if (BreakOverflow(pCurNode, pLeaderTemplate, pTrailerTemplate,
-                      bCreatePage)) {
-      if (bIsOverflowNode)
-        pFormNode = pCurNode->GetParent();
-
-      CXFA_Document* pDocument = pCurNode->GetDocument();
-      CXFA_Node* pDataScope = nullptr;
-      if (pLeaderTemplate) {
-        if (!pDataScope)
-          pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
-
-        pLeaderNode = pDocument->DataMerge_CopyContainer(
-            pLeaderTemplate, pFormNode, pDataScope, true, true, true);
-        pDocument->DataMerge_UpdateBindingRelations(pLeaderNode);
-        SetLayoutGeneratedNodeFlag(pLeaderNode);
-      }
-      if (pTrailerTemplate) {
-        if (!pDataScope)
-          pDataScope = XFA_DataMerge_FindDataScope(pFormNode);
-
-        pTrailerNode = pDocument->DataMerge_CopyContainer(
-            pTrailerTemplate, pFormNode, pDataScope, true, true, true);
-        pDocument->DataMerge_UpdateBindingRelations(pTrailerNode);
-        SetLayoutGeneratedNodeFlag(pTrailerNode);
-      }
-      return true;
-    }
-    if (bIsOverflowNode) {
-      break;
-    }
-  }
-  return false;
-}
-
-bool CXFA_LayoutPageMgr::ResolveBookendLeaderOrTrailer(
-    CXFA_Node* pBookendNode,
-    bool bLeader,
-    CXFA_Node*& pBookendAppendTemplate) {
-  CXFA_Node* pContainer =
-      pBookendNode->GetContainerParent()->GetTemplateNodeIfExists();
-  if (pBookendNode->GetElementType() == XFA_Element::Break) {
-    WideString leader = pBookendNode->JSObject()->GetCData(
-        bLeader ? XFA_Attribute::BookendLeader : XFA_Attribute::BookendTrailer);
-    if (!leader.IsEmpty()) {
-      pBookendAppendTemplate = ResolveBreakTarget(pContainer, false, leader);
-      return true;
-    }
-    return false;
-  }
-
-  if (pBookendNode->GetElementType() == XFA_Element::Bookend) {
-    WideString leader = pBookendNode->JSObject()->GetCData(
-        bLeader ? XFA_Attribute::Leader : XFA_Attribute::Trailer);
-    pBookendAppendTemplate = ResolveBreakTarget(pContainer, true, leader);
-    return true;
-  }
-  return false;
-}
-
-bool CXFA_LayoutPageMgr::FindPageAreaFromPageSet(CXFA_Node* pPageSet,
-                                                 CXFA_Node* pStartChild,
-                                                 CXFA_Node* pTargetPageArea,
-                                                 CXFA_Node* pTargetContentArea,
-                                                 bool bNewPage,
-                                                 bool bQuery) {
-  if (!pPageSet && !pStartChild)
-    return false;
-
-  if (IsPageSetRootOrderedOccurrence()) {
-    return FindPageAreaFromPageSet_Ordered(pPageSet, pStartChild,
-                                           pTargetPageArea, pTargetContentArea,
-                                           bNewPage, bQuery);
-  }
-  XFA_AttributeEnum ePreferredPosition =
-      m_CurrentContainerRecordIter != m_ProposedContainerRecords.end()
-          ? XFA_AttributeEnum::Rest
-          : XFA_AttributeEnum::First;
-  return FindPageAreaFromPageSet_SimplexDuplex(
-      pPageSet, pStartChild, pTargetPageArea, pTargetContentArea, bNewPage,
-      bQuery, ePreferredPosition);
-}
-
-bool CXFA_LayoutPageMgr::FindPageAreaFromPageSet_Ordered(
-    CXFA_Node* pPageSet,
-    CXFA_Node* pStartChild,
-    CXFA_Node* pTargetPageArea,
-    CXFA_Node* pTargetContentArea,
-    bool bNewPage,
-    bool bQuery) {
-  int32_t iPageSetCount = 0;
-  if (!pStartChild && !bQuery) {
-    auto it = m_pPageSetMap.find(pPageSet);
-    if (it != m_pPageSetMap.end())
-      iPageSetCount = it->second;
-    int32_t iMax = -1;
-    CXFA_Node* pOccurNode =
-        pPageSet->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
-    if (pOccurNode) {
-      Optional<int32_t> ret =
-          pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
-      if (ret)
-        iMax = *ret;
-    }
-    if (iMax >= 0 && iMax <= iPageSetCount)
-      return false;
-  }
-
-  bool bRes = false;
-  CXFA_Node* pCurrentNode =
-      pStartChild ? pStartChild->GetNextSibling() : pPageSet->GetFirstChild();
-  for (; pCurrentNode; pCurrentNode = pCurrentNode->GetNextSibling()) {
-    if (pCurrentNode->GetElementType() == XFA_Element::PageArea) {
-      if ((pTargetPageArea == pCurrentNode || !pTargetPageArea)) {
-        if (!pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
-                XFA_Element::ContentArea)) {
-          if (pTargetPageArea == pCurrentNode) {
-            CreateMinPageRecord(pCurrentNode, true);
-            pTargetPageArea = nullptr;
-          }
-          continue;
-        }
-        if (!bQuery) {
-          CXFA_ContainerRecord* pNewRecord =
-              CreateContainerRecord(pCurrentNode, !pStartChild);
-          AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
-          if (!pTargetContentArea) {
-            pTargetContentArea =
-                pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
-                    XFA_Element::ContentArea);
-          }
-          AddContentAreaLayoutItem(pNewRecord, pTargetContentArea);
-        }
-        m_pCurPageArea = pCurrentNode;
-        m_nCurPageCount = 1;
-        bRes = true;
-        break;
-      }
-      if (!bQuery)
-        CreateMinPageRecord(pCurrentNode, false);
-    } else if (pCurrentNode->GetElementType() == XFA_Element::PageSet) {
-      if (FindPageAreaFromPageSet_Ordered(pCurrentNode, nullptr,
-                                          pTargetPageArea, pTargetContentArea,
-                                          bNewPage, bQuery)) {
-        bRes = true;
-        break;
-      }
-      if (!bQuery)
-        CreateMinPageSetRecord(pCurrentNode, true);
-    }
-  }
-  if (!pStartChild && bRes && !bQuery)
-    m_pPageSetMap[pPageSet] = ++iPageSetCount;
-  return bRes;
-}
-
-bool CXFA_LayoutPageMgr::FindPageAreaFromPageSet_SimplexDuplex(
-    CXFA_Node* pPageSet,
-    CXFA_Node* pStartChild,
-    CXFA_Node* pTargetPageArea,
-    CXFA_Node* pTargetContentArea,
-    bool bNewPage,
-    bool bQuery,
-    XFA_AttributeEnum ePreferredPosition) {
-  const XFA_AttributeEnum eFallbackPosition = XFA_AttributeEnum::Any;
-  CXFA_Node* pPreferredPageArea = nullptr;
-  CXFA_Node* pFallbackPageArea = nullptr;
-  CXFA_Node* pCurrentNode = nullptr;
-  if (!pStartChild || pStartChild->GetElementType() == XFA_Element::PageArea)
-    pCurrentNode = pPageSet->GetFirstChild();
-  else
-    pCurrentNode = pStartChild->GetNextSibling();
-
-  for (; pCurrentNode; pCurrentNode = pCurrentNode->GetNextSibling()) {
-    if (pCurrentNode->GetElementType() == XFA_Element::PageArea) {
-      if (!MatchPageAreaOddOrEven(pCurrentNode))
-        continue;
-
-      XFA_AttributeEnum eCurPagePosition =
-          pCurrentNode->JSObject()->GetEnum(XFA_Attribute::PagePosition);
-      if (ePreferredPosition == XFA_AttributeEnum::Last) {
-        if (eCurPagePosition != ePreferredPosition)
-          continue;
-        if (m_ePageSetMode == XFA_AttributeEnum::SimplexPaginated ||
-            pCurrentNode->JSObject()->GetEnum(XFA_Attribute::OddOrEven) ==
-                XFA_AttributeEnum::Any) {
-          pPreferredPageArea = pCurrentNode;
-          break;
-        }
-        CXFA_ContainerRecord* pNewRecord = CreateContainerRecord();
-        AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
-        AddContentAreaLayoutItem(
-            pNewRecord, pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
-                            XFA_Element::ContentArea));
-        pPreferredPageArea = pCurrentNode;
-        return false;
-      }
-      if (ePreferredPosition == XFA_AttributeEnum::Only) {
-        if (eCurPagePosition != ePreferredPosition)
-          continue;
-        if (m_ePageSetMode != XFA_AttributeEnum::DuplexPaginated ||
-            pCurrentNode->JSObject()->GetEnum(XFA_Attribute::OddOrEven) ==
-                XFA_AttributeEnum::Any) {
-          pPreferredPageArea = pCurrentNode;
-          break;
-        }
-        return false;
-      }
-      if ((pTargetPageArea == pCurrentNode || !pTargetPageArea)) {
-        if (!pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
-                XFA_Element::ContentArea)) {
-          if (pTargetPageArea == pCurrentNode) {
-            CXFA_ContainerRecord* pNewRecord = CreateContainerRecord();
-            AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
-            pTargetPageArea = nullptr;
-          }
-          continue;
-        }
-        if ((ePreferredPosition == XFA_AttributeEnum::Rest &&
-             eCurPagePosition == XFA_AttributeEnum::Any) ||
-            eCurPagePosition == ePreferredPosition) {
-          pPreferredPageArea = pCurrentNode;
-          break;
-        }
-        if (eCurPagePosition == eFallbackPosition && !pFallbackPageArea) {
-          pFallbackPageArea = pCurrentNode;
-        }
-      } else if (pTargetPageArea && !MatchPageAreaOddOrEven(pTargetPageArea)) {
-        CXFA_ContainerRecord* pNewRecord = CreateContainerRecord();
-        AddPageAreaLayoutItem(pNewRecord, pCurrentNode);
-        AddContentAreaLayoutItem(
-            pNewRecord, pCurrentNode->GetFirstChildByClass<CXFA_ContentArea>(
-                            XFA_Element::ContentArea));
-      }
-    } else if (pCurrentNode->GetElementType() == XFA_Element::PageSet) {
-      if (FindPageAreaFromPageSet_SimplexDuplex(
-              pCurrentNode, nullptr, pTargetPageArea, pTargetContentArea,
-              bNewPage, bQuery, ePreferredPosition)) {
-        break;
-      }
-    }
-  }
-
-  CXFA_Node* pCurPageArea = nullptr;
-  if (pPreferredPageArea)
-    pCurPageArea = pPreferredPageArea;
-  else if (pFallbackPageArea)
-    pCurPageArea = pFallbackPageArea;
-
-  if (!pCurPageArea)
-    return false;
-
-  if (!bQuery) {
-    CXFA_ContainerRecord* pNewRecord = CreateContainerRecord();
-    AddPageAreaLayoutItem(pNewRecord, pCurPageArea);
-    if (!pTargetContentArea) {
-      pTargetContentArea = pCurPageArea->GetFirstChildByClass<CXFA_ContentArea>(
-          XFA_Element::ContentArea);
-    }
-    AddContentAreaLayoutItem(pNewRecord, pTargetContentArea);
-  }
-  m_pCurPageArea = pCurPageArea;
-  return true;
-}
-
-bool CXFA_LayoutPageMgr::MatchPageAreaOddOrEven(CXFA_Node* pPageArea) {
-  if (m_ePageSetMode != XFA_AttributeEnum::DuplexPaginated)
-    return true;
-
-  Optional<XFA_AttributeEnum> ret =
-      pPageArea->JSObject()->TryEnum(XFA_Attribute::OddOrEven, true);
-  if (!ret || *ret == XFA_AttributeEnum::Any)
-    return true;
-
-  int32_t iPageLast = GetPageCount() % 2;
-  return *ret == XFA_AttributeEnum::Odd ? iPageLast == 0 : iPageLast == 1;
-}
-
-CXFA_Node* CXFA_LayoutPageMgr::GetNextAvailPageArea(
-    CXFA_Node* pTargetPageArea,
-    CXFA_Node* pTargetContentArea,
-    bool bNewPage,
-    bool bQuery) {
-  if (!m_pCurPageArea) {
-    FindPageAreaFromPageSet(m_pTemplatePageSetRoot, nullptr, pTargetPageArea,
-                            pTargetContentArea, bNewPage, bQuery);
-    ASSERT(m_pCurPageArea);
-    return m_pCurPageArea;
-  }
-
-  if (!pTargetPageArea || pTargetPageArea == m_pCurPageArea) {
-    if (!bNewPage && GetNextContentArea(pTargetContentArea))
-      return m_pCurPageArea;
-
-    if (IsPageSetRootOrderedOccurrence()) {
-      int32_t iMax = -1;
-      CXFA_Node* pOccurNode =
-          m_pCurPageArea->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
-      if (pOccurNode) {
-        Optional<int32_t> ret =
-            pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
-        if (ret)
-          iMax = *ret;
-      }
-      if ((iMax < 0 || m_nCurPageCount < iMax)) {
-        if (!bQuery) {
-          CXFA_ContainerRecord* pNewRecord =
-              CreateContainerRecord(m_pCurPageArea);
-          AddPageAreaLayoutItem(pNewRecord, m_pCurPageArea);
-          if (!pTargetContentArea) {
-            pTargetContentArea =
-                m_pCurPageArea->GetFirstChildByClass<CXFA_ContentArea>(
-                    XFA_Element::ContentArea);
-          }
-          AddContentAreaLayoutItem(pNewRecord, pTargetContentArea);
-        }
-        m_nCurPageCount++;
-        return m_pCurPageArea;
-      }
-    }
-  }
-
-  if (!bQuery && IsPageSetRootOrderedOccurrence())
-    CreateMinPageRecord(m_pCurPageArea, false, true);
-  if (FindPageAreaFromPageSet(m_pCurPageArea->GetParent(), m_pCurPageArea,
-                              pTargetPageArea, pTargetContentArea, bNewPage,
-                              bQuery)) {
-    return m_pCurPageArea;
-  }
-
-  CXFA_Node* pPageSet = m_pCurPageArea->GetParent();
-  while (true) {
-    if (FindPageAreaFromPageSet(pPageSet, nullptr, pTargetPageArea,
-                                pTargetContentArea, bNewPage, bQuery)) {
-      return m_pCurPageArea;
-    }
-    if (!bQuery && IsPageSetRootOrderedOccurrence())
-      CreateMinPageSetRecord(pPageSet);
-    if (FindPageAreaFromPageSet(nullptr, pPageSet, pTargetPageArea,
-                                pTargetContentArea, bNewPage, bQuery)) {
-      return m_pCurPageArea;
-    }
-    if (pPageSet == m_pTemplatePageSetRoot)
-      break;
-
-    pPageSet = pPageSet->GetParent();
-  }
-  return nullptr;
-}
-
-bool CXFA_LayoutPageMgr::GetNextContentArea(CXFA_Node* pContentArea) {
-  CXFA_Node* pCurContentNode =
-      GetCurrentContainerRecord()->pCurContentArea->m_pFormNode;
-  if (!pContentArea) {
-    pContentArea = pCurContentNode->GetNextSameClassSibling<CXFA_ContentArea>(
-        XFA_Element::ContentArea);
-    if (!pContentArea)
-      return false;
-  } else {
-    if (pContentArea->GetParent() != m_pCurPageArea)
-      return false;
-
-    CXFA_ContainerLayoutItem* pContentAreaLayout = nullptr;
-    if (!CheckContentAreaNotUsed(GetCurrentContainerRecord()->pCurPageArea,
-                                 pContentArea, pContentAreaLayout)) {
-      return false;
-    }
-    if (pContentAreaLayout) {
-      if (pContentAreaLayout->m_pFormNode != pCurContentNode) {
-        CXFA_ContainerRecord* pNewRecord = CreateContainerRecord();
-        pNewRecord->pCurContentArea = pContentAreaLayout;
-        return true;
-      }
-      return false;
-    }
-  }
-
-  CXFA_ContainerRecord* pNewRecord = CreateContainerRecord();
-  AddContentAreaLayoutItem(pNewRecord, pContentArea);
-  return true;
-}
-
-void CXFA_LayoutPageMgr::InitPageSetMap() {
-  if (!IsPageSetRootOrderedOccurrence())
-    return;
-
-  CXFA_NodeIterator sIterator(m_pTemplatePageSetRoot);
-  for (CXFA_Node* pPageSetNode = sIterator.GetCurrent(); pPageSetNode;
-       pPageSetNode = sIterator.MoveToNext()) {
-    if (pPageSetNode->GetElementType() == XFA_Element::PageSet) {
-      XFA_AttributeEnum eRelation =
-          pPageSetNode->JSObject()->GetEnum(XFA_Attribute::Relation);
-      if (eRelation == XFA_AttributeEnum::OrderedOccurrence)
-        m_pPageSetMap[pPageSetNode] = 0;
-    }
-  }
-}
-
-int32_t CXFA_LayoutPageMgr::CreateMinPageRecord(CXFA_Node* pPageArea,
-                                                bool bTargetPageArea,
-                                                bool bCreateLast) {
-  if (!pPageArea)
-    return 0;
-
-  int32_t iMin = 0;
-  Optional<int32_t> ret;
-  CXFA_Node* pOccurNode =
-      pPageArea->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
-  if (pOccurNode) {
-    ret = pOccurNode->JSObject()->TryInteger(XFA_Attribute::Min, false);
-    if (ret)
-      iMin = *ret;
-  }
-
-  if (!ret && !bTargetPageArea)
-    return iMin;
-
-  CXFA_Node* pContentArea = pPageArea->GetFirstChildByClass<CXFA_ContentArea>(
-      XFA_Element::ContentArea);
-  if (iMin < 1 && bTargetPageArea && !pContentArea)
-    iMin = 1;
-
-  int32_t i = 0;
-  if (bCreateLast)
-    i = m_nCurPageCount;
-
-  for (; i < iMin; i++) {
-    CXFA_ContainerRecord* pNewRecord = CreateContainerRecord();
-    AddPageAreaLayoutItem(pNewRecord, pPageArea);
-    AddContentAreaLayoutItem(pNewRecord, pContentArea);
-  }
-  return iMin;
-}
-
-void CXFA_LayoutPageMgr::CreateMinPageSetRecord(CXFA_Node* pPageSet,
-                                                bool bCreateAll) {
-  if (!pPageSet)
-    return;
-
-  auto it = m_pPageSetMap.find(pPageSet);
-  if (it == m_pPageSetMap.end())
-    return;
-
-  int32_t iCurSetCount = it->second;
-  if (bCreateAll)
-    iCurSetCount = 0;
-
-  CXFA_Node* pOccurNode =
-      pPageSet->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
-  if (!pOccurNode)
-    return;
-
-  Optional<int32_t> iMin =
-      pOccurNode->JSObject()->TryInteger(XFA_Attribute::Min, false);
-  if (!iMin || iCurSetCount >= *iMin)
-    return;
-
-  for (int32_t i = 0; i < *iMin - iCurSetCount; i++) {
-    for (CXFA_Node* node = pPageSet->GetFirstChild(); node;
-         node = node->GetNextSibling()) {
-      if (node->GetElementType() == XFA_Element::PageArea)
-        CreateMinPageRecord(node, false);
-      else if (node->GetElementType() == XFA_Element::PageSet)
-        CreateMinPageSetRecord(node, true);
-    }
-  }
-  m_pPageSetMap[pPageSet] = *iMin;
-}
-
-void CXFA_LayoutPageMgr::CreateNextMinRecord(CXFA_Node* pRecordNode) {
-  if (!pRecordNode)
-    return;
-
-  for (CXFA_Node* pCurrentNode = pRecordNode->GetNextSibling(); pCurrentNode;
-       pCurrentNode = pCurrentNode->GetNextSibling()) {
-    if (pCurrentNode->GetElementType() == XFA_Element::PageArea)
-      CreateMinPageRecord(pCurrentNode, false);
-    else if (pCurrentNode->GetElementType() == XFA_Element::PageSet)
-      CreateMinPageSetRecord(pCurrentNode, true);
-  }
-}
-
-void CXFA_LayoutPageMgr::ProcessLastPageSet() {
-  CreateMinPageRecord(m_pCurPageArea, false, true);
-  CreateNextMinRecord(m_pCurPageArea);
-  CXFA_Node* pPageSet = m_pCurPageArea->GetParent();
-  while (true) {
-    CreateMinPageSetRecord(pPageSet);
-    if (pPageSet == m_pTemplatePageSetRoot)
-      break;
-
-    CreateNextMinRecord(pPageSet);
-    pPageSet = pPageSet->GetParent();
-  }
-}
-
-bool CXFA_LayoutPageMgr::GetNextAvailContentHeight(float fChildHeight) {
-  CXFA_Node* pCurContentNode =
-      GetCurrentContainerRecord()->pCurContentArea->m_pFormNode;
-  if (!pCurContentNode)
-    return false;
-
-  pCurContentNode = pCurContentNode->GetNextSameClassSibling<CXFA_ContentArea>(
-      XFA_Element::ContentArea);
-  if (pCurContentNode) {
-    float fNextContentHeight = pCurContentNode->JSObject()
-                                   ->GetMeasure(XFA_Attribute::H)
-                                   .ToUnit(XFA_Unit::Pt);
-    return fNextContentHeight > fChildHeight;
-  }
-
-  CXFA_Node* pPageNode = GetCurrentContainerRecord()->pCurPageArea->m_pFormNode;
-  CXFA_Node* pOccurNode =
-      pPageNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
-  int32_t iMax = 0;
-  Optional<int32_t> ret;
-  if (pOccurNode) {
-    ret = pOccurNode->JSObject()->TryInteger(XFA_Attribute::Max, false);
-    if (ret)
-      iMax = *ret;
-  }
-  if (ret) {
-    if (m_nCurPageCount == iMax) {
-      CXFA_Node* pSrcPage = m_pCurPageArea;
-      int32_t nSrcPageCount = m_nCurPageCount;
-      auto psSrcIter = GetTailPosition();
-      CXFA_Node* pNextPage =
-          GetNextAvailPageArea(nullptr, nullptr, false, true);
-      m_pCurPageArea = pSrcPage;
-      m_nCurPageCount = nSrcPageCount;
-      CXFA_ContainerRecord* pPrevRecord = *psSrcIter++;
-      while (psSrcIter != m_ProposedContainerRecords.end()) {
-        auto psSaveIter = psSrcIter;
-        CXFA_ContainerRecord* pInsertRecord = *psSrcIter++;
-        RemoveLayoutRecord(pInsertRecord, pPrevRecord);
-        delete pInsertRecord;
-        m_ProposedContainerRecords.erase(psSaveIter);
-      }
-      if (pNextPage) {
-        CXFA_Node* pContentArea =
-            pNextPage->GetFirstChildByClass<CXFA_ContentArea>(
-                XFA_Element::ContentArea);
-        if (pContentArea) {
-          float fNextContentHeight = pContentArea->JSObject()
-                                         ->GetMeasure(XFA_Attribute::H)
-                                         .ToUnit(XFA_Unit::Pt);
-          if (fNextContentHeight > fChildHeight)
-            return true;
-        }
-      }
-      return false;
-    }
-  }
-
-  CXFA_Node* pContentArea = pPageNode->GetFirstChildByClass<CXFA_ContentArea>(
-      XFA_Element::ContentArea);
-  float fNextContentHeight = pContentArea->JSObject()
-                                 ->GetMeasure(XFA_Attribute::H)
-                                 .ToUnit(XFA_Unit::Pt);
-  if (fNextContentHeight < XFA_LAYOUT_FLOAT_PERCISION)
-    return true;
-  if (fNextContentHeight > fChildHeight)
-    return true;
-  return false;
-}
-
-void CXFA_LayoutPageMgr::ClearData() {
-  if (!m_pTemplatePageSetRoot)
-    return;
-
-  auto sPos = m_ProposedContainerRecords.begin();
-  while (sPos != m_ProposedContainerRecords.end()) {
-    CXFA_ContainerRecord* pRecord = *sPos++;
-    delete pRecord;
-  }
-  m_ProposedContainerRecords.clear();
-  m_CurrentContainerRecordIter = m_ProposedContainerRecords.end();
-  m_pCurPageArea = nullptr;
-  m_nCurPageCount = 0;
-  m_bCreateOverFlowPage = false;
-  m_pPageSetMap.clear();
-}
-
-void CXFA_LayoutPageMgr::SaveLayoutItem(CXFA_LayoutItem* pParentLayoutItem) {
-  CXFA_LayoutItem* pNextLayoutItem;
-  CXFA_LayoutItem* pCurLayoutItem = pParentLayoutItem->m_pFirstChild;
-  while (pCurLayoutItem) {
-    pNextLayoutItem = pCurLayoutItem->m_pNextSibling;
-    if (pCurLayoutItem->IsContentLayoutItem()) {
-      if (pCurLayoutItem->m_pFormNode->HasRemovedChildren()) {
-        CXFA_FFNotify* pNotify =
-            m_pTemplatePageSetRoot->GetDocument()->GetNotify();
-        CXFA_LayoutProcessor* pDocLayout =
-            m_pTemplatePageSetRoot->GetDocument()->GetDocLayout();
-        if (pCurLayoutItem->m_pFirstChild)
-          SyncRemoveLayoutItem(pCurLayoutItem, pNotify, pDocLayout);
-
-        pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem);
-        delete pCurLayoutItem;
-        pCurLayoutItem = pNextLayoutItem;
-        continue;
-      }
-
-      if (pCurLayoutItem->m_pFormNode->IsLayoutGeneratedNode()) {
-        CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
-            sIterator(pCurLayoutItem->m_pFormNode);
-        for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
-             pNode = sIterator.MoveToNext()) {
-          pNode->SetFlag(XFA_NodeFlag_UnusedNode, false);
-        }
-      }
-    }
-
-    if (pCurLayoutItem->m_pFirstChild)
-      SaveLayoutItem(pCurLayoutItem);
-
-    pCurLayoutItem->m_pParent = nullptr;
-    pCurLayoutItem->m_pNextSibling = nullptr;
-    pCurLayoutItem->m_pFirstChild = nullptr;
-    if (!pCurLayoutItem->IsContentLayoutItem() &&
-        pCurLayoutItem->m_pFormNode->GetElementType() !=
-            XFA_Element::PageArea) {
-      delete pCurLayoutItem;
-    }
-    pCurLayoutItem = pNextLayoutItem;
-  }
-}
-
-CXFA_Node* CXFA_LayoutPageMgr::QueryOverflow(CXFA_Node* pFormNode) {
-  for (CXFA_Node* pCurNode = pFormNode->GetFirstChild(); pCurNode;
-       pCurNode = pCurNode->GetNextSibling()) {
-    if (pCurNode->GetElementType() == XFA_Element::Break) {
-      WideString wsOverflowLeader =
-          pCurNode->JSObject()->GetCData(XFA_Attribute::OverflowLeader);
-      WideString wsOverflowTarget =
-          pCurNode->JSObject()->GetCData(XFA_Attribute::OverflowTarget);
-      WideString wsOverflowTrailer =
-          pCurNode->JSObject()->GetCData(XFA_Attribute::OverflowTrailer);
-
-      if (!wsOverflowLeader.IsEmpty() || !wsOverflowTrailer.IsEmpty() ||
-          !wsOverflowTarget.IsEmpty()) {
-        return pCurNode;
-      }
-      return nullptr;
-    }
-    if (pCurNode->GetElementType() == XFA_Element::Overflow)
-      return pCurNode;
-  }
-  return nullptr;
-}
-
-void CXFA_LayoutPageMgr::MergePageSetContents() {
-  CXFA_Document* pDocument = m_pTemplatePageSetRoot->GetDocument();
-  CXFA_FFNotify* pNotify = pDocument->GetNotify();
-  CXFA_LayoutProcessor* pDocLayout = pDocument->GetDocLayout();
-  CXFA_ContainerLayoutItem* pRootLayout = GetRootLayoutItem();
-  for (CXFA_Node* pPageNode : pDocument->m_pPendingPageSet) {
-    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
-        sIterator(pPageNode);
-    for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
-         pNode = sIterator.MoveToNext()) {
-      if (pNode->IsContainerNode()) {
-        CXFA_Node* pBindNode = pNode->GetBindData();
-        if (pBindNode) {
-          pBindNode->RemoveBindItem(pNode);
-          pNode->SetBindingNode(nullptr);
-        }
-      }
-      pNode->SetFlag(XFA_NodeFlag_UnusedNode, true);
-    }
-  }
-
-  int32_t iIndex = 0;
-  for (; pRootLayout; pRootLayout = static_cast<CXFA_ContainerLayoutItem*>(
-                          pRootLayout->m_pNextSibling)) {
-    CXFA_Node* pPendingPageSet = nullptr;
-    CXFA_NodeIteratorTemplate<
-        CXFA_ContainerLayoutItem,
-        CXFA_TraverseStrategy_ContentAreaContainerLayoutItem>
-        iterator(pRootLayout);
-    CXFA_ContainerLayoutItem* pRootPageSetContainerItem = iterator.GetCurrent();
-    ASSERT(pRootPageSetContainerItem->m_pFormNode->GetElementType() ==
-           XFA_Element::PageSet);
-    if (iIndex <
-        pdfium::CollectionSize<int32_t>(pDocument->m_pPendingPageSet)) {
-      pPendingPageSet = pDocument->m_pPendingPageSet[iIndex];
-      iIndex++;
-    }
-    if (!pPendingPageSet) {
-      if (pRootPageSetContainerItem->m_pFormNode->GetPacketType() ==
-          XFA_PacketType::Template) {
-        pPendingPageSet =
-            pRootPageSetContainerItem->m_pFormNode->CloneTemplateToForm(false);
-      } else {
-        pPendingPageSet = pRootPageSetContainerItem->m_pFormNode;
-      }
-    }
-    if (pRootPageSetContainerItem->m_pFormNode->JSObject()->GetLayoutItem() ==
-        pRootPageSetContainerItem) {
-      pRootPageSetContainerItem->m_pFormNode->JSObject()->SetLayoutItem(
-          nullptr);
-    }
-    pRootPageSetContainerItem->m_pFormNode = pPendingPageSet;
-    pPendingPageSet->ClearFlag(XFA_NodeFlag_UnusedNode);
-    for (CXFA_ContainerLayoutItem* pContainerItem = iterator.MoveToNext();
-         pContainerItem; pContainerItem = iterator.MoveToNext()) {
-      CXFA_Node* pNode = pContainerItem->m_pFormNode;
-      if (pNode->GetPacketType() != XFA_PacketType::Template)
-        continue;
-
-      switch (pNode->GetElementType()) {
-        case XFA_Element::PageSet: {
-          CXFA_Node* pParentNode = pContainerItem->m_pParent->m_pFormNode;
-          pContainerItem->m_pFormNode = XFA_NodeMerge_CloneOrMergeContainer(
-              pDocument, pParentNode, pContainerItem->m_pFormNode, true,
-              nullptr);
-          break;
-        }
-        case XFA_Element::PageArea: {
-          CXFA_LayoutItem* pFormLayout = pContainerItem;
-          CXFA_Node* pParentNode = pContainerItem->m_pParent->m_pFormNode;
-          bool bIsExistForm = true;
-          for (int32_t iLevel = 0; iLevel < 3; iLevel++) {
-            pFormLayout = pFormLayout->m_pFirstChild;
-            if (iLevel == 2) {
-              while (pFormLayout &&
-                     !XFA_ItemLayoutProcessor_IsTakingSpace(
-                         pFormLayout->m_pFormNode)) {
-                pFormLayout = pFormLayout->m_pNextSibling;
-              }
-            }
-            if (!pFormLayout) {
-              bIsExistForm = false;
-              break;
-            }
-          }
-          if (bIsExistForm) {
-            CXFA_Node* pNewSubform = pFormLayout->m_pFormNode;
-            if (pContainerItem->m_pOldSubform &&
-                pContainerItem->m_pOldSubform != pNewSubform) {
-              CXFA_Node* pExistingNode = XFA_DataMerge_FindFormDOMInstance(
-                  pDocument, pContainerItem->m_pFormNode->GetElementType(),
-                  pContainerItem->m_pFormNode->GetNameHash(), pParentNode);
-              CXFA_ContainerIterator sIterator(pExistingNode);
-              for (CXFA_Node* pIter = sIterator.GetCurrent(); pIter;
-                   pIter = sIterator.MoveToNext()) {
-                if (pIter->GetElementType() != XFA_Element::ContentArea) {
-                  CXFA_LayoutItem* pLayoutItem =
-                      pIter->JSObject()->GetLayoutItem();
-                  if (pLayoutItem) {
-                    pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
-                    delete pLayoutItem;
-                  }
-                }
-              }
-              if (pExistingNode) {
-                pParentNode->RemoveChild(pExistingNode, true);
-              }
-            }
-            pContainerItem->m_pOldSubform = pNewSubform;
-          }
-          pContainerItem->m_pFormNode = pDocument->DataMerge_CopyContainer(
-              pContainerItem->m_pFormNode, pParentNode,
-              ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Record)), true, true,
-              true);
-          break;
-        }
-        case XFA_Element::ContentArea: {
-          CXFA_Node* pParentNode = pContainerItem->m_pParent->m_pFormNode;
-          for (CXFA_Node* pChildNode = pParentNode->GetFirstChild(); pChildNode;
-               pChildNode = pChildNode->GetNextSibling()) {
-            if (pChildNode->GetTemplateNodeIfExists() !=
-                pContainerItem->m_pFormNode) {
-              continue;
-            }
-            pContainerItem->m_pFormNode = pChildNode;
-            break;
-          }
-          break;
-        }
-        default:
-          break;
-      }
-    }
-    if (!pPendingPageSet->GetParent()) {
-      CXFA_Node* pFormToplevelSubform =
-          pDocument->GetXFAObject(XFA_HASHCODE_Form)
-              ->AsNode()
-              ->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
-      pFormToplevelSubform->InsertChild(pPendingPageSet, nullptr);
-    }
-    pDocument->DataMerge_UpdateBindingRelations(pPendingPageSet);
-    pPendingPageSet->SetFlag(XFA_NodeFlag_Initialized, true);
-  }
-
-  CXFA_Node* pPageSet = GetRootLayoutItem()->m_pFormNode;
-  while (pPageSet) {
-    CXFA_Node* pNextPageSet =
-        pPageSet->GetNextSameClassSibling<CXFA_PageSet>(XFA_Element::PageSet);
-    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
-        sIterator(pPageSet);
-    CXFA_Node* pNode = sIterator.GetCurrent();
-    while (pNode) {
-      if (pNode->IsUnusedNode()) {
-        if (pNode->IsContainerNode()) {
-          XFA_Element eType = pNode->GetElementType();
-          if (eType == XFA_Element::PageArea || eType == XFA_Element::PageSet) {
-            CXFA_ContainerIterator iteChild(pNode);
-            CXFA_Node* pChildNode = iteChild.MoveToNext();
-            for (; pChildNode; pChildNode = iteChild.MoveToNext()) {
-              CXFA_LayoutItem* pLayoutItem =
-                  pChildNode->JSObject()->GetLayoutItem();
-              if (pLayoutItem) {
-                pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
-                delete pLayoutItem;
-              }
-            }
-          } else if (eType != XFA_Element::ContentArea) {
-            CXFA_LayoutItem* pLayoutItem = pNode->JSObject()->GetLayoutItem();
-            if (pLayoutItem) {
-              pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
-              delete pLayoutItem;
-            }
-          }
-          CXFA_Node* pNext = sIterator.SkipChildrenAndMoveToNext();
-          pNode->GetParent()->RemoveChild(pNode, true);
-          pNode = pNext;
-        } else {
-          pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
-          pNode->SetFlag(XFA_NodeFlag_Initialized, true);
-          pNode = sIterator.MoveToNext();
-        }
-      } else {
-        pNode->SetFlag(XFA_NodeFlag_Initialized, true);
-        pNode = sIterator.MoveToNext();
-      }
-    }
-    pPageSet = pNextPageSet;
-  }
-}
-
-void CXFA_LayoutPageMgr::LayoutPageSetContents() {
-  CXFA_ContainerLayoutItem* pRootLayoutItem = GetRootLayoutItem();
-  for (; pRootLayoutItem;
-       pRootLayoutItem = static_cast<CXFA_ContainerLayoutItem*>(
-           pRootLayoutItem->m_pNextSibling)) {
-    CXFA_NodeIteratorTemplate<
-        CXFA_ContainerLayoutItem,
-        CXFA_TraverseStrategy_ContentAreaContainerLayoutItem>
-        iterator(pRootLayoutItem);
-    for (CXFA_ContainerLayoutItem* pContainerItem = iterator.GetCurrent();
-         pContainerItem; pContainerItem = iterator.MoveToNext()) {
-      CXFA_Node* pNode = pContainerItem->m_pFormNode;
-      switch (pNode->GetElementType()) {
-        case XFA_Element::PageArea:
-          m_pLayoutProcessor->GetRootRootItemLayoutProcessor()
-              ->DoLayoutPageArea(pContainerItem);
-          break;
-        default:
-          break;
-      }
-    }
-  }
-}
-
-void CXFA_LayoutPageMgr::SyncLayoutData() {
-  MergePageSetContents();
-  LayoutPageSetContents();
-  CXFA_FFNotify* pNotify = m_pTemplatePageSetRoot->GetDocument()->GetNotify();
-  int32_t nPageIdx = -1;
-  CXFA_ContainerLayoutItem* pRootLayoutItem = GetRootLayoutItem();
-  for (; pRootLayoutItem;
-       pRootLayoutItem = static_cast<CXFA_ContainerLayoutItem*>(
-           pRootLayoutItem->m_pNextSibling)) {
-    CXFA_NodeIteratorTemplate<
-        CXFA_ContainerLayoutItem,
-        CXFA_TraverseStrategy_ContentAreaContainerLayoutItem>
-        iteratorParent(pRootLayoutItem);
-    for (CXFA_ContainerLayoutItem* pContainerItem = iteratorParent.GetCurrent();
-         pContainerItem; pContainerItem = iteratorParent.MoveToNext()) {
-      switch (pContainerItem->m_pFormNode->GetElementType()) {
-        case XFA_Element::PageArea: {
-          nPageIdx++;
-          uint32_t dwRelevant =
-              XFA_WidgetStatus_Viewable | XFA_WidgetStatus_Printable;
-          CXFA_NodeIteratorTemplate<CXFA_LayoutItem,
-                                    CXFA_TraverseStrategy_LayoutItem>
-              iterator(pContainerItem);
-          CXFA_LayoutItem* pChildLayoutItem = iterator.GetCurrent();
-          while (pChildLayoutItem) {
-            CXFA_ContentLayoutItem* pContentItem =
-                pChildLayoutItem->AsContentLayoutItem();
-            if (!pContentItem) {
-              pChildLayoutItem = iterator.MoveToNext();
-              continue;
-            }
-
-            XFA_AttributeEnum presence =
-                pContentItem->m_pFormNode->JSObject()
-                    ->TryEnum(XFA_Attribute::Presence, true)
-                    .value_or(XFA_AttributeEnum::Visible);
-            bool bVisible = presence == XFA_AttributeEnum::Visible;
-            uint32_t dwRelevantChild =
-                GetRelevant(pContentItem->m_pFormNode, dwRelevant);
-            SyncContainer(pNotify, m_pLayoutProcessor, pContentItem,
-                          dwRelevantChild, bVisible, nPageIdx);
-            pChildLayoutItem = iterator.SkipChildrenAndMoveToNext();
-          }
-          break;
-        }
-        default:
-          break;
-      }
-    }
-  }
-
-  int32_t nPage = pdfium::CollectionSize<int32_t>(m_PageArray);
-  for (int32_t i = nPage - 1; i >= m_nAvailPages; i--) {
-    CXFA_ContainerLayoutItem* pPage = m_PageArray[i];
-    m_PageArray.erase(m_PageArray.begin() + i);
-    pNotify->OnPageEvent(pPage, XFA_PAGEVIEWEVENT_PostRemoved);
-    delete pPage;
-  }
-  ClearData();
-}
-
-void XFA_ReleaseLayoutItem_NoPageArea(CXFA_LayoutItem* pLayoutItem) {
-  CXFA_LayoutItem *pNext, *pNode = pLayoutItem->m_pFirstChild;
-  while (pNode) {
-    pNext = pNode->m_pNextSibling;
-    pNode->m_pParent = nullptr;
-    XFA_ReleaseLayoutItem_NoPageArea(pNode);
-    pNode = pNext;
-  }
-  if (pLayoutItem->m_pFormNode->GetElementType() != XFA_Element::PageArea)
-    delete pLayoutItem;
-}
-
-void CXFA_LayoutPageMgr::PrepareLayout() {
-  m_pPageSetCurRoot = nullptr;
-  m_ePageSetMode = XFA_AttributeEnum::OrderedOccurrence;
-  m_nAvailPages = 0;
-  ClearData();
-  if (!m_pPageSetLayoutItemRoot)
-    return;
-
-  CXFA_ContainerLayoutItem* pRootLayoutItem = m_pPageSetLayoutItemRoot;
-  if (pRootLayoutItem &&
-      pRootLayoutItem->m_pFormNode->GetPacketType() == XFA_PacketType::Form) {
-    CXFA_Node* pPageSetFormNode = pRootLayoutItem->m_pFormNode;
-    pRootLayoutItem->m_pFormNode->GetDocument()->m_pPendingPageSet.clear();
-    if (pPageSetFormNode->HasRemovedChildren()) {
-      XFA_ReleaseLayoutItem(pRootLayoutItem);
-      m_pPageSetLayoutItemRoot = nullptr;
-      pRootLayoutItem = nullptr;
-      pPageSetFormNode = nullptr;
-      m_PageArray.clear();
-    }
-    while (pPageSetFormNode) {
-      CXFA_Node* pNextPageSet =
-          pPageSetFormNode->GetNextSameClassSibling<CXFA_PageSet>(
-              XFA_Element::PageSet);
-      pPageSetFormNode->GetParent()->RemoveChild(pPageSetFormNode, false);
-      pRootLayoutItem->m_pFormNode->GetDocument()->m_pPendingPageSet.push_back(
-          pPageSetFormNode);
-      pPageSetFormNode = pNextPageSet;
-    }
-  }
-  pRootLayoutItem = m_pPageSetLayoutItemRoot;
-  CXFA_ContainerLayoutItem* pNextLayout = nullptr;
-  for (; pRootLayoutItem; pRootLayoutItem = pNextLayout) {
-    pNextLayout =
-        static_cast<CXFA_ContainerLayoutItem*>(pRootLayoutItem->m_pNextSibling);
-    SaveLayoutItem(pRootLayoutItem);
-    delete pRootLayoutItem;
-  }
-  m_pPageSetLayoutItemRoot = nullptr;
-}
diff --git a/xfa/fxfa/parser/cxfa_layoutpagemgr.h b/xfa/fxfa/parser/cxfa_layoutpagemgr.h
deleted file mode 100644
index 42fa4e6..0000000
--- a/xfa/fxfa/parser/cxfa_layoutpagemgr.h
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_LAYOUTPAGEMGR_H_
-#define XFA_FXFA_PARSER_CXFA_LAYOUTPAGEMGR_H_
-
-#include <iterator>
-#include <list>
-#include <map>
-#include <vector>
-
-#include "xfa/fxfa/parser/cxfa_itemlayoutprocessor.h"
-
-class CXFA_ContainerRecord;
-class CXFA_LayoutItem;
-class CXFA_Node;
-
-class CXFA_LayoutPageMgr {
- public:
-  explicit CXFA_LayoutPageMgr(CXFA_LayoutProcessor* pLayoutProcessor);
-  ~CXFA_LayoutPageMgr();
-
-  bool InitLayoutPage(CXFA_Node* pFormNode);
-  bool PrepareFirstPage(CXFA_Node* pRootSubform);
-  float GetAvailHeight();
-  bool GetNextAvailContentHeight(float fChildHeight);
-  void SubmitContentItem(CXFA_ContentLayoutItem* pContentLayoutItem,
-                         XFA_ItemLayoutProcessorResult eStatus);
-  void FinishPaginatedPageSets();
-  void SyncLayoutData();
-  int32_t GetPageCount() const;
-  CXFA_ContainerLayoutItem* GetPage(int32_t index) const;
-  int32_t GetPageIndex(const CXFA_ContainerLayoutItem* pPage) const;
-  inline CXFA_ContainerLayoutItem* GetRootLayoutItem() const {
-    return m_pPageSetLayoutItemRoot;
-  }
-  bool ProcessBreakBeforeOrAfter(CXFA_Node* pBreakNode,
-                                 bool bBefore,
-                                 CXFA_Node*& pBreakLeaderNode,
-                                 CXFA_Node*& pBreakTrailerNode,
-                                 bool& bCreatePage);
-  bool ProcessOverflow(CXFA_Node* pFormNode,
-                       CXFA_Node*& pLeaderNode,
-                       CXFA_Node*& pTrailerNode,
-                       bool bDataMerge = false,
-                       bool bCreatePage = true);
-  CXFA_Node* QueryOverflow(CXFA_Node* pFormNode);
-  bool ProcessBookendLeaderOrTrailer(CXFA_Node* pBookendNode,
-                                     bool bLeader,
-                                     CXFA_Node*& pBookendAppendNode);
-
- private:
-  bool AppendNewPage(bool bFirstTemPage = false);
-  void ReorderPendingLayoutRecordToTail(CXFA_ContainerRecord* pNewRecord,
-                                        CXFA_ContainerRecord* pPrevRecord);
-  void RemoveLayoutRecord(CXFA_ContainerRecord* pNewRecord,
-                          CXFA_ContainerRecord* pPrevRecord);
-  CXFA_ContainerRecord* GetCurrentContainerRecord() {
-    return *m_CurrentContainerRecordIter;
-  }
-  std::list<CXFA_ContainerRecord*>::iterator GetTailPosition() {
-    auto iter = m_ProposedContainerRecords.end();
-    return !m_ProposedContainerRecords.empty() ? std::prev(iter) : iter;
-  }
-  CXFA_ContainerRecord* CreateContainerRecord(CXFA_Node* pPageNode = nullptr,
-                                              bool bCreateNew = false);
-  void AddPageAreaLayoutItem(CXFA_ContainerRecord* pNewRecord,
-                             CXFA_Node* pNewPageArea);
-  void AddContentAreaLayoutItem(CXFA_ContainerRecord* pNewRecord,
-                                CXFA_Node* pContentArea);
-  bool RunBreak(XFA_Element eBreakType,
-                XFA_AttributeEnum eTargetType,
-                CXFA_Node* pTarget,
-                bool bStartNew);
-  CXFA_Node* BreakOverflow(CXFA_Node* pOverflowNode,
-                           CXFA_Node*& pLeaderTemplate,
-                           CXFA_Node*& pTrailerTemplate,
-                           bool bCreatePage = true);
-  bool ResolveBookendLeaderOrTrailer(CXFA_Node* pBookendNode,
-                                     bool bLeader,
-                                     CXFA_Node*& pBookendAppendTemplate);
-  bool ExecuteBreakBeforeOrAfter(CXFA_Node* pCurNode,
-                                 bool bBefore,
-                                 CXFA_Node*& pBreakLeaderTemplate,
-                                 CXFA_Node*& pBreakTrailerTemplate);
-
-  int32_t CreateMinPageRecord(CXFA_Node* pPageArea,
-                              bool bTargetPageArea,
-                              bool bCreateLast = false);
-  void CreateMinPageSetRecord(CXFA_Node* pPageSet, bool bCreateAll = false);
-  void CreateNextMinRecord(CXFA_Node* pRecordNode);
-  bool FindPageAreaFromPageSet(CXFA_Node* pPageSet,
-                               CXFA_Node* pStartChild,
-                               CXFA_Node* pTargetPageArea = nullptr,
-                               CXFA_Node* pTargetContentArea = nullptr,
-                               bool bNewPage = false,
-                               bool bQuery = false);
-  bool FindPageAreaFromPageSet_Ordered(CXFA_Node* pPageSet,
-                                       CXFA_Node* pStartChild,
-                                       CXFA_Node* pTargetPageArea = nullptr,
-                                       CXFA_Node* pTargetContentArea = nullptr,
-                                       bool bNewPage = false,
-                                       bool bQuery = false);
-  bool FindPageAreaFromPageSet_SimplexDuplex(
-      CXFA_Node* pPageSet,
-      CXFA_Node* pStartChild,
-      CXFA_Node* pTargetPageArea = nullptr,
-      CXFA_Node* pTargetContentArea = nullptr,
-      bool bNewPage = false,
-      bool bQuery = false,
-      XFA_AttributeEnum ePreferredPosition = XFA_AttributeEnum::First);
-  bool MatchPageAreaOddOrEven(CXFA_Node* pPageArea);
-  CXFA_Node* GetNextAvailPageArea(CXFA_Node* pTargetPageArea,
-                                  CXFA_Node* pTargetContentArea = nullptr,
-                                  bool bNewPage = false,
-                                  bool bQuery = false);
-  bool GetNextContentArea(CXFA_Node* pTargetContentArea);
-  void InitPageSetMap();
-  void ProcessLastPageSet();
-  bool IsPageSetRootOrderedOccurrence() const {
-    return m_ePageSetMode == XFA_AttributeEnum::OrderedOccurrence;
-  }
-  void ClearData();
-  void MergePageSetContents();
-  void LayoutPageSetContents();
-  void PrepareLayout();
-  void SaveLayoutItem(CXFA_LayoutItem* pParentLayoutItem);
-
-  CXFA_LayoutProcessor* m_pLayoutProcessor;
-  CXFA_Node* m_pTemplatePageSetRoot;
-  CXFA_ContainerLayoutItem* m_pPageSetLayoutItemRoot;
-  CXFA_ContainerLayoutItem* m_pPageSetCurRoot;
-  std::list<CXFA_ContainerRecord*> m_ProposedContainerRecords;
-  std::list<CXFA_ContainerRecord*>::iterator m_CurrentContainerRecordIter;
-  CXFA_Node* m_pCurPageArea;
-  int32_t m_nAvailPages;
-  int32_t m_nCurPageCount;
-  XFA_AttributeEnum m_ePageSetMode;
-  bool m_bCreateOverFlowPage;
-  std::map<CXFA_Node*, int32_t> m_pPageSetMap;
-  std::vector<CXFA_ContainerLayoutItem*> m_PageArray;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_LAYOUTPAGEMGR_H_
diff --git a/xfa/fxfa/parser/cxfa_layoutprocessor.cpp b/xfa/fxfa/parser/cxfa_layoutprocessor.cpp
deleted file mode 100644
index 540e1c91..0000000
--- a/xfa/fxfa/parser/cxfa_layoutprocessor.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 "xfa/fxfa/parser/cxfa_layoutprocessor.h"
-
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_itemlayoutprocessor.h"
-#include "xfa/fxfa/parser/cxfa_layoutpagemgr.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_subform.h"
-#include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-CXFA_LayoutProcessor::CXFA_LayoutProcessor(CXFA_Document* pDocument)
-    : m_pDocument(pDocument), m_nProgressCounter(0), m_bNeedLayout(true) {}
-
-CXFA_LayoutProcessor::~CXFA_LayoutProcessor() {}
-
-CXFA_Document* CXFA_LayoutProcessor::GetDocument() const {
-  return m_pDocument.Get();
-}
-
-int32_t CXFA_LayoutProcessor::StartLayout(bool bForceRestart) {
-  if (!bForceRestart && !IsNeedLayout())
-    return 100;
-
-  m_pRootItemLayoutProcessor.reset();
-  m_nProgressCounter = 0;
-  CXFA_Node* pFormPacketNode =
-      ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Form));
-  if (!pFormPacketNode)
-    return -1;
-
-  CXFA_Subform* pFormRoot =
-      pFormPacketNode->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
-  if (!pFormRoot)
-    return -1;
-
-  if (!m_pLayoutPageMgr)
-    m_pLayoutPageMgr = pdfium::MakeUnique<CXFA_LayoutPageMgr>(this);
-  if (!m_pLayoutPageMgr->InitLayoutPage(pFormRoot))
-    return -1;
-
-  if (!m_pLayoutPageMgr->PrepareFirstPage(pFormRoot))
-    return -1;
-
-  m_pRootItemLayoutProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(
-      pFormRoot, m_pLayoutPageMgr.get());
-  m_nProgressCounter = 1;
-  return 0;
-}
-
-int32_t CXFA_LayoutProcessor::DoLayout() {
-  if (m_nProgressCounter < 1)
-    return -1;
-
-  XFA_ItemLayoutProcessorResult eStatus;
-  CXFA_Node* pFormNode = m_pRootItemLayoutProcessor->GetFormNode();
-  float fPosX =
-      pFormNode->JSObject()->GetMeasure(XFA_Attribute::X).ToUnit(XFA_Unit::Pt);
-  float fPosY =
-      pFormNode->JSObject()->GetMeasure(XFA_Attribute::Y).ToUnit(XFA_Unit::Pt);
-  do {
-    float fAvailHeight = m_pLayoutPageMgr->GetAvailHeight();
-    eStatus = m_pRootItemLayoutProcessor->DoLayout(true, fAvailHeight,
-                                                   fAvailHeight, nullptr);
-    if (eStatus != XFA_ItemLayoutProcessorResult::Done)
-      m_nProgressCounter++;
-
-    CXFA_ContentLayoutItem* pLayoutItem =
-        m_pRootItemLayoutProcessor->ExtractLayoutItem();
-    if (pLayoutItem)
-      pLayoutItem->m_sPos = CFX_PointF(fPosX, fPosY);
-
-    m_pLayoutPageMgr->SubmitContentItem(pLayoutItem, eStatus);
-  } while (eStatus != XFA_ItemLayoutProcessorResult::Done);
-
-  if (eStatus == XFA_ItemLayoutProcessorResult::Done) {
-    m_pLayoutPageMgr->FinishPaginatedPageSets();
-    m_pLayoutPageMgr->SyncLayoutData();
-    m_bNeedLayout = false;
-    m_rgChangedContainers.clear();
-  }
-  return 100 * (eStatus == XFA_ItemLayoutProcessorResult::Done
-                    ? m_nProgressCounter
-                    : m_nProgressCounter - 1) /
-         m_nProgressCounter;
-}
-
-bool CXFA_LayoutProcessor::IncrementLayout() {
-  if (m_bNeedLayout) {
-    StartLayout(true);
-    return DoLayout() == 100;
-  }
-  for (CXFA_Node* pNode : m_rgChangedContainers) {
-    CXFA_Node* pParentNode = pNode->GetContainerParent();
-    if (!pParentNode)
-      return false;
-    if (!CXFA_ItemLayoutProcessor::IncrementRelayoutNode(this, pNode,
-                                                         pParentNode)) {
-      return false;
-    }
-  }
-  m_rgChangedContainers.clear();
-  return true;
-}
-
-int32_t CXFA_LayoutProcessor::CountPages() const {
-  return m_pLayoutPageMgr ? m_pLayoutPageMgr->GetPageCount() : 0;
-}
-
-CXFA_ContainerLayoutItem* CXFA_LayoutProcessor::GetPage(int32_t index) const {
-  return m_pLayoutPageMgr ? m_pLayoutPageMgr->GetPage(index) : nullptr;
-}
-
-CXFA_LayoutItem* CXFA_LayoutProcessor::GetLayoutItem(CXFA_Node* pFormItem) {
-  return pFormItem->JSObject()->GetLayoutItem();
-}
-
-void CXFA_LayoutProcessor::AddChangedContainer(CXFA_Node* pContainer) {
-  if (!pdfium::ContainsValue(m_rgChangedContainers, pContainer))
-    m_rgChangedContainers.push_back(pContainer);
-}
-
-CXFA_ContainerLayoutItem* CXFA_LayoutProcessor::GetRootLayoutItem() const {
-  return m_pLayoutPageMgr ? m_pLayoutPageMgr->GetRootLayoutItem() : nullptr;
-}
-
-bool CXFA_LayoutProcessor::IsNeedLayout() {
-  return m_bNeedLayout || !m_rgChangedContainers.empty();
-}
diff --git a/xfa/fxfa/parser/cxfa_layoutprocessor.h b/xfa/fxfa/parser/cxfa_layoutprocessor.h
deleted file mode 100644
index e8391be..0000000
--- a/xfa/fxfa/parser/cxfa_layoutprocessor.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_LAYOUTPROCESSOR_H_
-#define XFA_FXFA_PARSER_CXFA_LAYOUTPROCESSOR_H_
-
-#include <memory>
-#include <vector>
-
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-
-class CXFA_ContainerLayoutItem;
-class CXFA_Document;
-class CXFA_ItemLayoutProcessor;
-class CXFA_LayoutItem;
-class CXFA_LayoutPageMgr;
-class CXFA_Node;
-
-class CXFA_LayoutProcessor {
- public:
-  explicit CXFA_LayoutProcessor(CXFA_Document* pDocument);
-  ~CXFA_LayoutProcessor();
-
-  CXFA_Document* GetDocument() const;
-  int32_t StartLayout(bool bForceRestart = false);
-  int32_t DoLayout();
-  bool IncrementLayout();
-  int32_t CountPages() const;
-  CXFA_ContainerLayoutItem* GetPage(int32_t index) const;
-  CXFA_LayoutItem* GetLayoutItem(CXFA_Node* pFormItem);
-  void AddChangedContainer(CXFA_Node* pContainer);
-  void SetForceReLayout(bool bForceRestart) { m_bNeedLayout = bForceRestart; }
-  CXFA_ContainerLayoutItem* GetRootLayoutItem() const;
-  CXFA_ItemLayoutProcessor* GetRootRootItemLayoutProcessor() const {
-    return m_pRootItemLayoutProcessor.get();
-  }
-  CXFA_LayoutPageMgr* GetLayoutPageMgr() const {
-    return m_pLayoutPageMgr.get();
-  }
-
- private:
-  bool IsNeedLayout();
-
-  UnownedPtr<CXFA_Document> const m_pDocument;
-  std::unique_ptr<CXFA_ItemLayoutProcessor> m_pRootItemLayoutProcessor;
-  std::unique_ptr<CXFA_LayoutPageMgr> m_pLayoutPageMgr;
-  std::vector<CXFA_Node*> m_rgChangedContainers;
-  uint32_t m_nProgressCounter;
-  bool m_bNeedLayout;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_LAYOUTPROCESSOR_H_
diff --git a/xfa/fxfa/parser/cxfa_level.cpp b/xfa/fxfa/parser/cxfa_level.cpp
index 7343805..b738081 100644
--- a/xfa/fxfa/parser/cxfa_level.cpp
+++ b/xfa/fxfa/parser/cxfa_level.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_level.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kLevelAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"level";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Level,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kLevelAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Level::~CXFA_Level() {}
+CXFA_Level::~CXFA_Level() = default;
diff --git a/xfa/fxfa/parser/cxfa_level.h b/xfa/fxfa/parser/cxfa_level.h
index abdd048..8cc03dc 100644
--- a/xfa/fxfa/parser/cxfa_level.h
+++ b/xfa/fxfa/parser/cxfa_level.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Level : public CXFA_Node {
+class CXFA_Level final : public CXFA_Node {
  public:
   CXFA_Level(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Level() override;
diff --git a/xfa/fxfa/parser/cxfa_line.cpp b/xfa/fxfa/parser/cxfa_line.cpp
index 4e656c2..2ac97b2 100644
--- a/xfa/fxfa/parser/cxfa_line.cpp
+++ b/xfa/fxfa/parser/cxfa_line.cpp
@@ -6,26 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_line.h"
 
-#include "fxjs/xfa/cjx_line.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_edge.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Edge, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kLinePropertyData[] = {
+    {XFA_Element::Edge, 1, 0},
+};
+
+const CXFA_Node::AttributeData kLineAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Slope, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Backslash},
+     (void*)XFA_AttributeValue::Backslash},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Hand, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Even},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"line";
+     (void*)XFA_AttributeValue::Even},
+};
 
 }  // namespace
 
@@ -35,19 +35,18 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Line,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Line>(this)) {}
+                kLinePropertyData,
+                kLineAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Line::~CXFA_Line() {}
+CXFA_Line::~CXFA_Line() = default;
 
-XFA_AttributeEnum CXFA_Line::GetHand() {
+XFA_AttributeValue CXFA_Line::GetHand() {
   return JSObject()->GetEnum(XFA_Attribute::Hand);
 }
 
 bool CXFA_Line::GetSlope() {
-  return JSObject()->GetEnum(XFA_Attribute::Slope) == XFA_AttributeEnum::Slash;
+  return JSObject()->GetEnum(XFA_Attribute::Slope) == XFA_AttributeValue::Slash;
 }
 
 CXFA_Edge* CXFA_Line::GetEdgeIfExists() {
diff --git a/xfa/fxfa/parser/cxfa_line.h b/xfa/fxfa/parser/cxfa_line.h
index 18faacd..53584fa 100644
--- a/xfa/fxfa/parser/cxfa_line.h
+++ b/xfa/fxfa/parser/cxfa_line.h
@@ -11,12 +11,12 @@
 
 class CXFA_Edge;
 
-class CXFA_Line : public CXFA_Node {
+class CXFA_Line final : public CXFA_Node {
  public:
   CXFA_Line(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Line() override;
 
-  XFA_AttributeEnum GetHand();
+  XFA_AttributeValue GetHand();
   bool GetSlope();
   CXFA_Edge* GetEdgeIfExists();
 };
diff --git a/xfa/fxfa/parser/cxfa_linear.cpp b/xfa/fxfa/parser/cxfa_linear.cpp
index 8506d24..b482d27 100644
--- a/xfa/fxfa/parser/cxfa_linear.cpp
+++ b/xfa/fxfa/parser/cxfa_linear.cpp
@@ -6,25 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_linear.h"
 
-#include "fxjs/xfa/cjx_linear.h"
+#include "core/fxge/render_defines.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
 #include "xfa/fxgraphics/cxfa_geshading.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Color, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kLinearPropertyData[] = {
+    {XFA_Element::Color, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kLinearAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::ToRight},
+     (void*)XFA_AttributeValue::ToRight},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"linear";
+};
 
 }  // namespace
 
@@ -34,17 +35,16 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Linear,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Linear>(this)) {}
+                kLinearPropertyData,
+                kLinearAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Linear::~CXFA_Linear() {}
+CXFA_Linear::~CXFA_Linear() = default;
 
-XFA_AttributeEnum CXFA_Linear::GetType() {
+XFA_AttributeValue CXFA_Linear::GetType() {
   return JSObject()
       ->TryEnum(XFA_Attribute::Type, true)
-      .value_or(XFA_AttributeEnum::ToRight);
+      .value_or(XFA_AttributeValue::ToRight);
 }
 
 CXFA_Color* CXFA_Linear::GetColorIfExists() {
@@ -62,19 +62,19 @@
   CFX_PointF ptStart;
   CFX_PointF ptEnd;
   switch (GetType()) {
-    case XFA_AttributeEnum::ToRight:
+    case XFA_AttributeValue::ToRight:
       ptStart = CFX_PointF(rtFill.left, rtFill.top);
       ptEnd = CFX_PointF(rtFill.right(), rtFill.top);
       break;
-    case XFA_AttributeEnum::ToBottom:
+    case XFA_AttributeValue::ToBottom:
       ptStart = CFX_PointF(rtFill.left, rtFill.top);
       ptEnd = CFX_PointF(rtFill.left, rtFill.bottom());
       break;
-    case XFA_AttributeEnum::ToLeft:
+    case XFA_AttributeValue::ToLeft:
       ptStart = CFX_PointF(rtFill.right(), rtFill.top);
       ptEnd = CFX_PointF(rtFill.left, rtFill.top);
       break;
-    case XFA_AttributeEnum::ToTop:
+    case XFA_AttributeValue::ToTop:
       ptStart = CFX_PointF(rtFill.left, rtFill.bottom());
       ptEnd = CFX_PointF(rtFill.left, rtFill.top);
       break;
diff --git a/xfa/fxfa/parser/cxfa_linear.h b/xfa/fxfa/parser/cxfa_linear.h
index bb55f80..0fa5e1a 100644
--- a/xfa/fxfa/parser/cxfa_linear.h
+++ b/xfa/fxfa/parser/cxfa_linear.h
@@ -14,9 +14,10 @@
 class CXFA_Color;
 class CXFA_Graphics;
 
-class CXFA_Linear : public CXFA_Node {
+class CXFA_Linear final : public CXFA_Node {
  public:
-  static constexpr XFA_AttributeEnum kDefaultType = XFA_AttributeEnum::ToRight;
+  static constexpr XFA_AttributeValue kDefaultType =
+      XFA_AttributeValue::ToRight;
 
   CXFA_Linear(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Linear() override;
@@ -28,7 +29,7 @@
             const CFX_Matrix& matrix);
 
  private:
-  XFA_AttributeEnum GetType();
+  XFA_AttributeValue GetType();
   CXFA_Color* GetColorIfExists();
 };
 
diff --git a/xfa/fxfa/parser/cxfa_linearized.cpp b/xfa/fxfa/parser/cxfa_linearized.cpp
index 0ae6b8d..6c2ffe1 100644
--- a/xfa/fxfa/parser/cxfa_linearized.cpp
+++ b/xfa/fxfa/parser/cxfa_linearized.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_linearized.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kLinearizedAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"linearized";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Linearized,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kLinearizedAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Linearized::~CXFA_Linearized() {}
+CXFA_Linearized::~CXFA_Linearized() = default;
diff --git a/xfa/fxfa/parser/cxfa_linearized.h b/xfa/fxfa/parser/cxfa_linearized.h
index df7846e..d2fe2d7 100644
--- a/xfa/fxfa/parser/cxfa_linearized.h
+++ b/xfa/fxfa/parser/cxfa_linearized.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Linearized : public CXFA_Node {
+class CXFA_Linearized final : public CXFA_Node {
  public:
   CXFA_Linearized(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Linearized() override;
diff --git a/xfa/fxfa/parser/cxfa_list.cpp b/xfa/fxfa/parser/cxfa_list.cpp
index 6f5fc29..6329cec 100644
--- a/xfa/fxfa/parser/cxfa_list.cpp
+++ b/xfa/fxfa/parser/cxfa_list.cpp
@@ -9,7 +9,7 @@
 #include <utility>
 
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_treelist.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
@@ -18,15 +18,13 @@
     : CXFA_List(pDocument,
                 XFA_ObjectType::List,
                 XFA_Element::List,
-                WideStringView(L"list"),
                 std::move(obj)) {}
 
 CXFA_List::CXFA_List(CXFA_Document* pDocument,
                      XFA_ObjectType objectType,
                      XFA_Element eType,
-                     const WideStringView& elementName,
                      std::unique_ptr<CJX_Object> obj)
-    : CXFA_Object(pDocument, objectType, eType, elementName, std::move(obj)) {
+    : CXFA_Object(pDocument, objectType, eType, std::move(obj)) {
   m_pDocument->GetScriptContext()->AddToCacheList(
       std::unique_ptr<CXFA_List>(this));
 }
diff --git a/xfa/fxfa/parser/cxfa_list.h b/xfa/fxfa/parser/cxfa_list.h
index de9406d..c114801 100644
--- a/xfa/fxfa/parser/cxfa_list.h
+++ b/xfa/fxfa/parser/cxfa_list.h
@@ -18,9 +18,9 @@
   ~CXFA_List() override;
 
   virtual size_t GetLength() = 0;
-  virtual bool Append(CXFA_Node* pNode) = 0;
+  virtual void Append(CXFA_Node* pNode) = 0;
   virtual bool Insert(CXFA_Node* pNewNode, CXFA_Node* pBeforeNode) = 0;
-  virtual bool Remove(CXFA_Node* pNode) = 0;
+  virtual void Remove(CXFA_Node* pNode) = 0;
   virtual CXFA_Node* Item(size_t iIndex) = 0;
 
  protected:
@@ -28,7 +28,6 @@
   CXFA_List(CXFA_Document* pDocument,
             XFA_ObjectType objectType,
             XFA_Element eType,
-            const WideStringView& elementName,
             std::unique_ptr<CJX_Object> obj);
 };
 
diff --git a/xfa/fxfa/parser/cxfa_locale.cpp b/xfa/fxfa/parser/cxfa_locale.cpp
index c601139..6c54fb8 100644
--- a/xfa/fxfa/parser/cxfa_locale.cpp
+++ b/xfa/fxfa/parser/cxfa_locale.cpp
@@ -6,21 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_locale.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kLocalePropertyData[] = {
     {XFA_Element::DatePatterns, 1, 0},    {XFA_Element::CalendarSymbols, 1, 0},
     {XFA_Element::CurrencySymbols, 1, 0}, {XFA_Element::Typefaces, 1, 0},
     {XFA_Element::DateTimeSymbols, 1, 0}, {XFA_Element::NumberPatterns, 1, 0},
     {XFA_Element::NumberSymbols, 1, 0},   {XFA_Element::TimePatterns, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kLocaleAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"locale";
+};
 
 }  // namespace
 
@@ -30,8 +32,8 @@
                 (XFA_XDPPACKET_Config | XFA_XDPPACKET_LocaleSet),
                 XFA_ObjectType::Node,
                 XFA_Element::Locale,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kLocalePropertyData,
+                kLocaleAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Locale::~CXFA_Locale() {}
+CXFA_Locale::~CXFA_Locale() = default;
diff --git a/xfa/fxfa/parser/cxfa_locale.h b/xfa/fxfa/parser/cxfa_locale.h
index 3a7e83d..67bee33 100644
--- a/xfa/fxfa/parser/cxfa_locale.h
+++ b/xfa/fxfa/parser/cxfa_locale.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Locale : public CXFA_Node {
+class CXFA_Locale final : public CXFA_Node {
  public:
   CXFA_Locale(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Locale() override;
diff --git a/xfa/fxfa/parser/cxfa_localemgr.cpp b/xfa/fxfa/parser/cxfa_localemgr.cpp
index bb769b6..b6c65a6 100644
--- a/xfa/fxfa/parser/cxfa_localemgr.cpp
+++ b/xfa/fxfa/parser/cxfa_localemgr.cpp
@@ -11,10 +11,8 @@
 #include <memory>
 #include <utility>
 
-#include "core/fpdfapi/cpdf_modulemgr.h"
-#include "core/fxcodec/codec/ccodec_flatemodule.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/xml/cxml_element.h"
+#include "core/fxcodec/flate/flatemodule.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_acrobat.h"
@@ -42,6 +40,8 @@
 #define FX_LANG_es_LA 0x080a
 #define FX_LANG_es_ES 0x0c0a
 
+namespace {
+
 // These arrays are the hex encoded XML strings which define the locale.
 // <locale name="en_US" desc="English(America)">
 //   <calendarSymbols name="gregorian">
@@ -1065,26 +1065,22 @@
     0xB3, 0x85, 0xFA, 0x59, 0x2A, 0x7A, 0xFF, 0x3D, 0xC4, 0x3F, 0xDE, 0xCB,
     0x8B, 0xC4};
 
-static std::unique_ptr<IFX_Locale> XFA_GetLocaleFromBuffer(const uint8_t* pBuf,
-                                                           int nBufLen) {
-  if (!pBuf || nBufLen <= 0)
+std::unique_ptr<LocaleIface> GetLocaleFromBuffer(
+    pdfium::span<const uint8_t> src_span) {
+  if (src_span.empty())
     return nullptr;
 
-  uint8_t* pOut = nullptr;
+  std::unique_ptr<uint8_t, FxFreeDeleter> output;
   uint32_t dwSize;
-  CCodec_ModuleMgr* pCodecMgr = CPDF_ModuleMgr::Get()->GetCodecModule();
-  pCodecMgr->GetFlateModule()->FlateOrLZWDecode(false, pBuf, nBufLen, true, 0,
-                                                0, 0, 0, 0, &pOut, &dwSize);
-  if (!pOut)
+  FlateModule::FlateOrLZWDecode(false, src_span, true, 0, 0, 0, 0, 0, &output,
+                                &dwSize);
+  if (!output)
     return nullptr;
 
-  std::unique_ptr<CXML_Element> pLocale = CXML_Element::Parse(pOut, dwSize);
-  FX_Free(pOut);
-  return pLocale ? pdfium::MakeUnique<CXFA_XMLLocale>(std::move(pLocale))
-                 : nullptr;
+  return CXFA_XMLLocale::Create(pdfium::make_span(output.get(), dwSize));
 }
 
-static uint16_t XFA_GetLanguage(WideString wsLanguage) {
+uint16_t GetLanguage(WideString wsLanguage) {
   if (wsLanguage.GetLength() < 2)
     return FX_LANG_en_US;
 
@@ -1127,28 +1123,25 @@
   return FX_LANG_en_US;
 }
 
+}  // namespace
+
 CXFA_LocaleMgr::CXFA_LocaleMgr(CXFA_Node* pLocaleSet, WideString wsDeflcid)
-    : m_dwLocaleFlags(0x00) {
-  m_dwDeflcid = XFA_GetLanguage(wsDeflcid);
-  if (pLocaleSet) {
-    CXFA_Node* pNodeLocale = pLocaleSet->GetFirstChild();
-    while (pNodeLocale) {
-      m_LocaleArray.push_back(pdfium::MakeUnique<CXFA_NodeLocale>(pNodeLocale));
-      pNodeLocale = pNodeLocale->GetNextSibling();
-    }
+    : m_pDefLocale(GetLocaleByName(wsDeflcid)),
+      m_dwDeflcid(GetLanguage(wsDeflcid)) {
+  if (!pLocaleSet)
+    return;
+
+  for (CXFA_Node* pNodeLocale = pLocaleSet->GetFirstChild(); pNodeLocale;
+       pNodeLocale = pNodeLocale->GetNextSibling()) {
+    m_LocaleArray.push_back(pdfium::MakeUnique<CXFA_NodeLocale>(pNodeLocale));
   }
-  m_pDefLocale = GetLocaleByName(wsDeflcid);
 }
 
 CXFA_LocaleMgr::~CXFA_LocaleMgr() {}
 
-uint16_t CXFA_LocaleMgr::GetDefLocaleID() const {
-  return m_dwDeflcid;
-}
-
-IFX_Locale* CXFA_LocaleMgr::GetDefLocale() {
+LocaleIface* CXFA_LocaleMgr::GetDefLocale() {
   if (m_pDefLocale)
-    return m_pDefLocale;
+    return m_pDefLocale.Get();
 
   if (!m_LocaleArray.empty())
     return m_LocaleArray[0].get();
@@ -1156,100 +1149,101 @@
   if (!m_XMLLocaleArray.empty())
     return m_XMLLocaleArray[0].get();
 
-  std::unique_ptr<IFX_Locale> locale(GetLocale(m_dwDeflcid));
+  std::unique_ptr<LocaleIface> locale(GetLocale(m_dwDeflcid));
   m_pDefLocale = locale.get();
   if (locale)
     m_XMLLocaleArray.push_back(std::move(locale));
 
-  return m_pDefLocale;
+  return m_pDefLocale.Get();
 }
 
-std::unique_ptr<IFX_Locale> CXFA_LocaleMgr::GetLocale(uint16_t lcid) {
+std::unique_ptr<LocaleIface> CXFA_LocaleMgr::GetLocale(uint16_t lcid) {
   switch (lcid) {
     case FX_LANG_zh_CN:
-      return XFA_GetLocaleFromBuffer(g_zhCN_Locale, sizeof(g_zhCN_Locale));
+      return GetLocaleFromBuffer(g_zhCN_Locale);
     case FX_LANG_zh_TW:
-      return XFA_GetLocaleFromBuffer(g_zhTW_Locale, sizeof(g_zhTW_Locale));
+      return GetLocaleFromBuffer(g_zhTW_Locale);
     case FX_LANG_zh_HK:
-      return XFA_GetLocaleFromBuffer(g_zhHK_Locale, sizeof(g_zhHK_Locale));
+      return GetLocaleFromBuffer(g_zhHK_Locale);
     case FX_LANG_ja_JP:
-      return XFA_GetLocaleFromBuffer(g_jaJP_Locale, sizeof(g_jaJP_Locale));
+      return GetLocaleFromBuffer(g_jaJP_Locale);
     case FX_LANG_ko_KR:
-      return XFA_GetLocaleFromBuffer(g_koKR_Locale, sizeof(g_koKR_Locale));
+      return GetLocaleFromBuffer(g_koKR_Locale);
     case FX_LANG_en_GB:
-      return XFA_GetLocaleFromBuffer(g_enGB_Locale, sizeof(g_enGB_Locale));
+      return GetLocaleFromBuffer(g_enGB_Locale);
     case FX_LANG_es_LA:
-      return XFA_GetLocaleFromBuffer(g_esLA_Locale, sizeof(g_esLA_Locale));
+      return GetLocaleFromBuffer(g_esLA_Locale);
     case FX_LANG_es_ES:
-      return XFA_GetLocaleFromBuffer(g_esES_Locale, sizeof(g_esES_Locale));
+      return GetLocaleFromBuffer(g_esES_Locale);
     case FX_LANG_de_DE:
-      return XFA_GetLocaleFromBuffer(g_deDE_Loacale, sizeof(g_deDE_Loacale));
+      return GetLocaleFromBuffer(g_deDE_Loacale);
     case FX_LANG_fr_FR:
-      return XFA_GetLocaleFromBuffer(g_frFR_Locale, sizeof(g_frFR_Locale));
+      return GetLocaleFromBuffer(g_frFR_Locale);
     case FX_LANG_it_IT:
-      return XFA_GetLocaleFromBuffer(g_itIT_Locale, sizeof(g_itIT_Locale));
+      return GetLocaleFromBuffer(g_itIT_Locale);
     case FX_LANG_pt_BR:
-      return XFA_GetLocaleFromBuffer(g_ptBR_Locale, sizeof(g_ptBR_Locale));
+      return GetLocaleFromBuffer(g_ptBR_Locale);
     case FX_LANG_nl_NL:
-      return XFA_GetLocaleFromBuffer(g_nlNL_Locale, sizeof(g_nlNL_Locale));
+      return GetLocaleFromBuffer(g_nlNL_Locale);
     case FX_LANG_ru_RU:
-      return XFA_GetLocaleFromBuffer(g_ruRU_Locale, sizeof(g_ruRU_Locale));
+      return GetLocaleFromBuffer(g_ruRU_Locale);
     case FX_LANG_en_US:
     default:
-      return XFA_GetLocaleFromBuffer(g_enUS_Locale, sizeof(g_enUS_Locale));
+      return GetLocaleFromBuffer(g_enUS_Locale);
   }
 }
 
-IFX_Locale* CXFA_LocaleMgr::GetLocaleByName(const WideString& wsLocaleName) {
+LocaleIface* CXFA_LocaleMgr::GetLocaleByName(const WideString& wsLocaleName) {
   for (size_t i = 0; i < m_LocaleArray.size(); i++) {
-    IFX_Locale* pLocale = m_LocaleArray[i].get();
+    LocaleIface* pLocale = m_LocaleArray[i].get();
     if (pLocale->GetName() == wsLocaleName)
       return pLocale;
   }
   if (wsLocaleName.GetLength() < 2)
     return nullptr;
   for (size_t i = 0; i < m_XMLLocaleArray.size(); i++) {
-    IFX_Locale* pLocale = m_XMLLocaleArray[i].get();
+    LocaleIface* pLocale = m_XMLLocaleArray[i].get();
     if (pLocale->GetName() == wsLocaleName)
       return pLocale;
   }
 
-  std::unique_ptr<IFX_Locale> pLocale(GetLocale(XFA_GetLanguage(wsLocaleName)));
-  IFX_Locale* pRetLocale = pLocale.get();
+  std::unique_ptr<LocaleIface> pLocale(GetLocale(GetLanguage(wsLocaleName)));
+  LocaleIface* pRetLocale = pLocale.get();
   if (pLocale)
     m_XMLLocaleArray.push_back(std::move(pLocale));
   return pRetLocale;
 }
 
-void CXFA_LocaleMgr::SetDefLocale(IFX_Locale* pLocale) {
+void CXFA_LocaleMgr::SetDefLocale(LocaleIface* pLocale) {
   m_pDefLocale = pLocale;
 }
 
-WideStringView CXFA_LocaleMgr::GetConfigLocaleName(CXFA_Node* pConfig) {
-  if (!(m_dwLocaleFlags & 0x01)) {
-    m_wsConfigLocale.clear();
-    if (pConfig) {
-      CXFA_Node* pChildfConfig =
-          pConfig->GetFirstChildByClass<CXFA_Acrobat>(XFA_Element::Acrobat);
-      if (!pChildfConfig) {
-        pChildfConfig =
-            pConfig->GetFirstChildByClass<CXFA_Present>(XFA_Element::Present);
-      }
-      CXFA_Common* pCommon =
-          pChildfConfig ? pChildfConfig->GetFirstChildByClass<CXFA_Common>(
-                              XFA_Element::Common)
-                        : nullptr;
-      CXFA_Locale* pLocale =
-          pCommon
-              ? pCommon->GetFirstChildByClass<CXFA_Locale>(XFA_Element::Locale)
-              : nullptr;
-      if (pLocale) {
-        m_wsConfigLocale = pLocale->JSObject()
-                               ->TryCData(XFA_Attribute::Value, false)
-                               .value_or(WideString());
-      }
-    }
-    m_dwLocaleFlags |= 0x01;
+WideString CXFA_LocaleMgr::GetConfigLocaleName(CXFA_Node* pConfig) {
+  if (m_hasSetLocaleName)
+    return m_wsConfigLocale;
+
+  m_hasSetLocaleName = true;
+  m_wsConfigLocale.clear();
+  if (!pConfig)
+    return m_wsConfigLocale;
+
+  CXFA_Node* pChildfConfig =
+      pConfig->GetFirstChildByClass<CXFA_Acrobat>(XFA_Element::Acrobat);
+  if (!pChildfConfig) {
+    pChildfConfig =
+        pConfig->GetFirstChildByClass<CXFA_Present>(XFA_Element::Present);
   }
-  return m_wsConfigLocale.AsStringView();
+  CXFA_Common* pCommon = pChildfConfig
+                             ? pChildfConfig->GetFirstChildByClass<CXFA_Common>(
+                                   XFA_Element::Common)
+                             : nullptr;
+  CXFA_Locale* pLocale =
+      pCommon ? pCommon->GetFirstChildByClass<CXFA_Locale>(XFA_Element::Locale)
+              : nullptr;
+  if (pLocale) {
+    m_wsConfigLocale = pLocale->JSObject()
+                           ->TryCData(XFA_Attribute::Value, false)
+                           .value_or(WideString());
+  }
+  return m_wsConfigLocale;
 }
diff --git a/xfa/fxfa/parser/cxfa_localemgr.h b/xfa/fxfa/parser/cxfa_localemgr.h
index d77a94a..9370988 100644
--- a/xfa/fxfa/parser/cxfa_localemgr.h
+++ b/xfa/fxfa/parser/cxfa_localemgr.h
@@ -10,34 +10,36 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/cfx_datetime.h"
-#include "core/fxcrt/ifx_locale.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "xfa/fgas/crt/locale_mgr_iface.h"
 
 class CXFA_Node;
-class IFX_Locale;
+class LocaleIface;
 
-class CXFA_LocaleMgr {
+class CXFA_LocaleMgr : public LocaleMgrIface {
  public:
   CXFA_LocaleMgr(CXFA_Node* pLocaleSet, WideString wsDeflcid);
-  ~CXFA_LocaleMgr();
+  ~CXFA_LocaleMgr() override;
 
-  uint16_t GetDefLocaleID() const;
-  IFX_Locale* GetDefLocale();
-  IFX_Locale* GetLocaleByName(const WideString& wsLocaleName);
+  LocaleIface* GetDefLocale() override;
+  LocaleIface* GetLocaleByName(const WideString& wsLocaleName) override;
 
-  void SetDefLocale(IFX_Locale* pLocale);
-  WideStringView GetConfigLocaleName(CXFA_Node* pConfig);
+  void SetDefLocale(LocaleIface* pLocale);
+  WideString GetConfigLocaleName(CXFA_Node* pConfig);
 
  private:
-  std::unique_ptr<IFX_Locale> GetLocale(uint16_t lcid);
+  std::unique_ptr<LocaleIface> GetLocale(uint16_t lcid);
 
-  std::vector<std::unique_ptr<IFX_Locale>> m_LocaleArray;
-  std::vector<std::unique_ptr<IFX_Locale>> m_XMLLocaleArray;
-  IFX_Locale* m_pDefLocale;  // owned by m_LocaleArray or m_XMLLocaleArray.
+  std::vector<std::unique_ptr<LocaleIface>> m_LocaleArray;
+  std::vector<std::unique_ptr<LocaleIface>> m_XMLLocaleArray;
+
+  // Owned by m_LocaleArray or m_XMLLocaleArray.
+  UnownedPtr<LocaleIface> m_pDefLocale;
+
   WideString m_wsConfigLocale;
   uint16_t m_dwDeflcid;
-  uint16_t m_dwLocaleFlags;
+  bool m_hasSetLocaleName = false;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LOCALEMGR_H_
diff --git a/xfa/fxfa/parser/cxfa_localeset.cpp b/xfa/fxfa/parser/cxfa_localeset.cpp
index b669e3b..527006c 100644
--- a/xfa/fxfa/parser/cxfa_localeset.cpp
+++ b/xfa/fxfa/parser/cxfa_localeset.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_localeset.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kLocaleSetAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"localeSet";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 (XFA_XDPPACKET_Config | XFA_XDPPACKET_LocaleSet),
                 XFA_ObjectType::ModelNode,
                 XFA_Element::LocaleSet,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kLocaleSetAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_LocaleSet::~CXFA_LocaleSet() {}
+CXFA_LocaleSet::~CXFA_LocaleSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_localeset.h b/xfa/fxfa/parser/cxfa_localeset.h
index 1d65ad1..dc6521a 100644
--- a/xfa/fxfa/parser/cxfa_localeset.h
+++ b/xfa/fxfa/parser/cxfa_localeset.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_LocaleSet : public CXFA_Node {
+class CXFA_LocaleSet final : public CXFA_Node {
  public:
   CXFA_LocaleSet(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_LocaleSet() override;
diff --git a/xfa/fxfa/parser/cxfa_localevalue.cpp b/xfa/fxfa/parser/cxfa_localevalue.cpp
index 09e3577..0663d38 100644
--- a/xfa/fxfa/parser/cxfa_localevalue.cpp
+++ b/xfa/fxfa/parser/cxfa_localevalue.cpp
@@ -6,12 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
 
+#include <cwchar>
+#include <utility>
 #include <vector>
 
 #include "core/fxcrt/fx_extension.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 #include "third_party/base/stl_util.h"
-#include "xfa/fgas/crt/cfgas_formatstring.h"
+#include "xfa/fgas/crt/cfgas_stringformatter.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
@@ -44,8 +47,8 @@
 bool ValueSplitDateTime(const WideString& wsDateTime,
                         WideString& wsDate,
                         WideString& wsTime) {
-  wsDate = L"";
-  wsTime = L"";
+  wsDate.clear();
+  wsTime.clear();
   if (wsDateTime.IsEmpty())
     return false;
 
@@ -55,21 +58,38 @@
   if (!nSplitIndex.has_value())
     return false;
 
-  wsDate = wsDateTime.Left(nSplitIndex.value());
-  wsTime = wsDateTime.Right(wsDateTime.GetLength() - nSplitIndex.value() - 1);
+  wsDate = wsDateTime.First(nSplitIndex.value());
+  wsTime = wsDateTime.Last(wsDateTime.GetLength() - nSplitIndex.value() - 1);
   return true;
 }
 
+class ScopedLocale {
+ public:
+  ScopedLocale(CXFA_LocaleMgr* pLocaleMgr, LocaleIface* pNewLocale)
+      : m_pLocaleMgr(pLocaleMgr),
+        m_pNewLocale(pNewLocale),
+        m_pOrigLocale(pNewLocale ? m_pLocaleMgr->GetDefLocale() : nullptr) {
+    if (m_pNewLocale)
+      m_pLocaleMgr->SetDefLocale(pNewLocale);
+  }
+
+  ~ScopedLocale() {
+    if (m_pNewLocale)
+      m_pLocaleMgr->SetDefLocale(m_pOrigLocale);
+  }
+
+  ScopedLocale(const ScopedLocale& that) = delete;
+  ScopedLocale& operator=(const ScopedLocale& that) = delete;
+
+ private:
+  UnownedPtr<CXFA_LocaleMgr> const m_pLocaleMgr;
+  LocaleIface* const m_pNewLocale;
+  LocaleIface* const m_pOrigLocale;
+};
+
 }  // namespace
 
-CXFA_LocaleValue::CXFA_LocaleValue()
-    : m_pLocaleMgr(nullptr), m_dwType(XFA_VT_NULL), m_bValid(true) {}
-
-CXFA_LocaleValue::CXFA_LocaleValue(const CXFA_LocaleValue& value)
-    : m_pLocaleMgr(value.m_pLocaleMgr),
-      m_wsValue(value.m_wsValue),
-      m_dwType(value.m_dwType),
-      m_bValid(value.m_bValid) {}
+CXFA_LocaleValue::CXFA_LocaleValue() = default;
 
 CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType, CXFA_LocaleMgr* pLocaleMgr)
     : m_pLocaleMgr(pLocaleMgr),
@@ -87,94 +107,88 @@
 CXFA_LocaleValue::CXFA_LocaleValue(uint32_t dwType,
                                    const WideString& wsValue,
                                    const WideString& wsFormat,
-                                   IFX_Locale* pLocale,
+                                   LocaleIface* pLocale,
                                    CXFA_LocaleMgr* pLocaleMgr)
     : m_pLocaleMgr(pLocaleMgr),
       m_dwType(dwType),
       m_bValid(ParsePatternValue(wsValue, wsFormat, pLocale)) {}
 
-CXFA_LocaleValue& CXFA_LocaleValue::operator=(const CXFA_LocaleValue& value) {
-  m_wsValue = value.m_wsValue;
-  m_dwType = value.m_dwType;
-  m_bValid = value.m_bValid;
-  m_pLocaleMgr = value.m_pLocaleMgr;
-  return *this;
-}
+CXFA_LocaleValue::CXFA_LocaleValue(const CXFA_LocaleValue& that) = default;
 
-CXFA_LocaleValue::~CXFA_LocaleValue() {}
+CXFA_LocaleValue& CXFA_LocaleValue::operator=(const CXFA_LocaleValue& that) =
+    default;
+
+CXFA_LocaleValue::~CXFA_LocaleValue() = default;
 
 bool CXFA_LocaleValue::ValidateValue(const WideString& wsValue,
                                      const WideString& wsPattern,
-                                     IFX_Locale* pLocale,
+                                     LocaleIface* pLocale,
                                      WideString* pMatchFormat) {
+  if (!m_pLocaleMgr)
+    return false;
+
+  ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
+  std::vector<WideString> wsPatterns =
+      CFGAS_StringFormatter::SplitOnBars(wsPattern);
+
   WideString wsOutput;
-  IFX_Locale* locale = m_pLocaleMgr->GetDefLocale();
-  if (pLocale)
-    m_pLocaleMgr->SetDefLocale(pLocale);
-
-  auto pFormat = pdfium::MakeUnique<CFGAS_FormatString>(m_pLocaleMgr);
-  std::vector<WideString> wsPatterns;
-  pFormat->SplitFormatString(wsPattern, &wsPatterns);
-
   bool bRet = false;
-  int32_t iCount = pdfium::CollectionSize<int32_t>(wsPatterns);
-  int32_t i = 0;
-  for (; i < iCount && !bRet; i++) {
-    WideString wsFormat = wsPatterns[i];
-    switch (ValueCategory(pFormat->GetCategory(wsFormat), m_dwType)) {
+  size_t i = 0;
+  for (; !bRet && i < wsPatterns.size(); i++) {
+    const WideString& wsFormat = wsPatterns[i];
+    auto pFormat =
+        pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
+    switch (ValueCategory(pFormat->GetCategory(), m_dwType)) {
       case FX_LOCALECATEGORY_Null:
-        bRet = pFormat->ParseNull(wsValue, wsFormat);
+        bRet = pFormat->ParseNull(wsValue);
         if (!bRet)
           bRet = wsValue.IsEmpty();
         break;
       case FX_LOCALECATEGORY_Zero:
-        bRet = pFormat->ParseZero(wsValue, wsFormat);
+        bRet = pFormat->ParseZero(wsValue);
         if (!bRet)
-          bRet = wsValue == L"0";
+          bRet = wsValue.EqualsASCII("0");
         break;
       case FX_LOCALECATEGORY_Num: {
         WideString fNum;
-        bRet = pFormat->ParseNum(wsValue, wsFormat, &fNum);
+        bRet = pFormat->ParseNum(wsValue, &fNum);
         if (!bRet)
-          bRet = pFormat->FormatNum(wsValue, wsFormat, &wsOutput);
+          bRet = pFormat->FormatNum(wsValue, &wsOutput);
         break;
       }
       case FX_LOCALECATEGORY_Text:
-        bRet = pFormat->ParseText(wsValue, wsFormat, &wsOutput);
+        bRet = pFormat->ParseText(wsValue, &wsOutput);
         wsOutput.clear();
         if (!bRet)
-          bRet = pFormat->FormatText(wsValue, wsFormat, &wsOutput);
+          bRet = pFormat->FormatText(wsValue, &wsOutput);
         break;
       case FX_LOCALECATEGORY_Date: {
         CFX_DateTime dt;
         bRet = ValidateCanonicalDate(wsValue, &dt);
         if (!bRet) {
-          bRet = pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Date,
-                                        &dt);
+          bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Date, &dt);
           if (!bRet) {
-            bRet = pFormat->FormatDateTime(wsValue, wsFormat,
-                                           FX_DATETIMETYPE_Date, &wsOutput);
+            bRet = pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_Date,
+                                           &wsOutput);
           }
         }
         break;
       }
       case FX_LOCALECATEGORY_Time: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Time,
-                                      &dt);
+        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Time, &dt);
         if (!bRet) {
-          bRet = pFormat->FormatDateTime(wsValue, wsFormat,
-                                         FX_DATETIMETYPE_Time, &wsOutput);
+          bRet =
+              pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_Time, &wsOutput);
         }
         break;
       }
       case FX_LOCALECATEGORY_DateTime: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, wsFormat,
-                                      FX_DATETIMETYPE_DateTime, &dt);
+        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_DateTime, &dt);
         if (!bRet) {
-          bRet = pFormat->FormatDateTime(wsValue, wsFormat,
-                                         FX_DATETIMETYPE_DateTime, &wsOutput);
+          bRet = pFormat->FormatDateTime(wsValue, FX_DATETIMETYPE_DateTime,
+                                         &wsOutput);
         }
         break;
       }
@@ -185,90 +199,16 @@
   }
   if (bRet && pMatchFormat)
     *pMatchFormat = wsPatterns[i - 1];
-  if (pLocale)
-    m_pLocaleMgr->SetDefLocale(locale);
-
   return bRet;
 }
 
 double CXFA_LocaleValue::GetDoubleNum() const {
-  if (m_bValid && (m_dwType == XFA_VT_BOOLEAN || m_dwType == XFA_VT_INTEGER ||
-                   m_dwType == XFA_VT_DECIMAL || m_dwType == XFA_VT_FLOAT)) {
-    int64_t nIntegral = 0;
-    uint32_t dwFractional = 0;
-    int32_t nExponent = 0;
-    int32_t cc = 0;
-    bool bNegative = false;
-    bool bExpSign = false;
-    const wchar_t* str = m_wsValue.c_str();
-    int len = m_wsValue.GetLength();
-    while (FXSYS_iswspace(str[cc]) && cc < len)
-      cc++;
-
-    if (cc >= len)
-      return 0;
-    if (str[0] == '+') {
-      cc++;
-    } else if (str[0] == '-') {
-      bNegative = true;
-      cc++;
-    }
-
-    int32_t nIntegralLen = 0;
-    while (cc < len) {
-      if (str[cc] == '.' || !FXSYS_isDecimalDigit(str[cc]) ||
-          nIntegralLen > 17) {
-        break;
-      }
-      nIntegral = nIntegral * 10 + str[cc] - '0';
-      cc++;
-      nIntegralLen++;
-    }
-
-    nIntegral = bNegative ? -nIntegral : nIntegral;
-    int32_t scale = 0;
-    double fraction = 0.0;
-    if (cc < len && str[cc] == '.') {
-      cc++;
-      while (cc < len) {
-        fraction += XFA_GetFractionalScale(scale) * (str[cc] - '0');
-        scale++;
-        cc++;
-        if (scale == XFA_GetMaxFractionalScale() ||
-            !FXSYS_isDecimalDigit(str[cc])) {
-          break;
-        }
-      }
-      dwFractional = static_cast<uint32_t>(fraction * 4294967296.0);
-    }
-    if (cc < len && (str[cc] == 'E' || str[cc] == 'e')) {
-      cc++;
-      if (cc < len) {
-        if (str[cc] == '+') {
-          cc++;
-        } else if (str[cc] == '-') {
-          bExpSign = true;
-          cc++;
-        }
-      }
-      while (cc < len) {
-        if (str[cc] == '.' || !FXSYS_isDecimalDigit(str[cc]))
-          break;
-
-        nExponent = nExponent * 10 + str[cc] - '0';
-        cc++;
-      }
-      nExponent = bExpSign ? -nExponent : nExponent;
-    }
-
-    double dValue = dwFractional / 4294967296.0;
-    dValue = nIntegral + (nIntegral >= 0 ? dValue : -dValue);
-    if (nExponent != 0)
-      dValue *= FXSYS_pow(10, static_cast<float>(nExponent));
-
-    return dValue;
+  if (!m_bValid || (m_dwType != XFA_VT_BOOLEAN && m_dwType != XFA_VT_INTEGER &&
+                    m_dwType != XFA_VT_DECIMAL && m_dwType != XFA_VT_FLOAT)) {
+    return 0;
   }
-  return 0;
+
+  return wcstod(m_wsValue.c_str(), nullptr);
 }
 
 CFX_DateTime CXFA_LocaleValue::GetDate() const {
@@ -276,7 +216,7 @@
     return CFX_DateTime();
 
   CFX_DateTime dt;
-  FX_DateFromCanonical(m_wsValue, &dt);
+  FX_DateFromCanonical(m_wsValue.span(), &dt);
   return dt;
 }
 
@@ -285,8 +225,7 @@
     return CFX_DateTime();
 
   CFX_DateTime dt;
-  FX_TimeFromCanonical(m_wsValue.AsStringView(), &dt,
-                       m_pLocaleMgr->GetDefLocale());
+  FX_TimeFromCanonical(m_pLocaleMgr->GetDefLocale(), m_wsValue.span(), &dt);
   return dt;
 }
 
@@ -318,15 +257,11 @@
 
 bool CXFA_LocaleValue::FormatPatterns(WideString& wsResult,
                                       const WideString& wsFormat,
-                                      IFX_Locale* pLocale,
+                                      LocaleIface* pLocale,
                                       XFA_VALUEPICTURE eValueType) const {
-  auto pFormat = pdfium::MakeUnique<CFGAS_FormatString>(m_pLocaleMgr);
-  std::vector<WideString> wsPatterns;
-  pFormat->SplitFormatString(wsFormat, &wsPatterns);
   wsResult.clear();
-  int32_t iCount = pdfium::CollectionSize<int32_t>(wsPatterns);
-  for (int32_t i = 0; i < iCount; i++) {
-    if (FormatSinglePattern(wsResult, wsPatterns[i], pLocale, eValueType))
+  for (const auto& pattern : CFGAS_StringFormatter::SplitOnBars(wsFormat)) {
+    if (FormatSinglePattern(wsResult, pattern, pLocale, eValueType))
       return true;
   }
   return false;
@@ -334,43 +269,44 @@
 
 bool CXFA_LocaleValue::FormatSinglePattern(WideString& wsResult,
                                            const WideString& wsFormat,
-                                           IFX_Locale* pLocale,
+                                           LocaleIface* pLocale,
                                            XFA_VALUEPICTURE eValueType) const {
-  IFX_Locale* locale = m_pLocaleMgr->GetDefLocale();
-  if (pLocale)
-    m_pLocaleMgr->SetDefLocale(pLocale);
+  if (!m_pLocaleMgr)
+    return false;
+
+  ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
 
   wsResult.clear();
   bool bRet = false;
-  auto pFormat = pdfium::MakeUnique<CFGAS_FormatString>(m_pLocaleMgr);
-  FX_LOCALECATEGORY eCategory =
-      ValueCategory(pFormat->GetCategory(wsFormat), m_dwType);
+  auto pFormat =
+      pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
+  FX_LOCALECATEGORY eCategory = ValueCategory(pFormat->GetCategory(), m_dwType);
   switch (eCategory) {
     case FX_LOCALECATEGORY_Null:
       if (m_wsValue.IsEmpty())
-        bRet = pFormat->FormatNull(wsFormat, &wsResult);
+        bRet = pFormat->FormatNull(&wsResult);
       break;
     case FX_LOCALECATEGORY_Zero:
-      if (m_wsValue == L"0")
-        bRet = pFormat->FormatZero(wsFormat, &wsResult);
+      if (m_wsValue.EqualsASCII("0"))
+        bRet = pFormat->FormatZero(&wsResult);
       break;
     case FX_LOCALECATEGORY_Num:
-      bRet = pFormat->FormatNum(m_wsValue, wsFormat, &wsResult);
+      bRet = pFormat->FormatNum(m_wsValue, &wsResult);
       break;
     case FX_LOCALECATEGORY_Text:
-      bRet = pFormat->FormatText(m_wsValue, wsFormat, &wsResult);
+      bRet = pFormat->FormatText(m_wsValue, &wsResult);
       break;
     case FX_LOCALECATEGORY_Date:
-      bRet = pFormat->FormatDateTime(m_wsValue, wsFormat, FX_DATETIMETYPE_Date,
-                                     &wsResult);
+      bRet =
+          pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_Date, &wsResult);
       break;
     case FX_LOCALECATEGORY_Time:
-      bRet = pFormat->FormatDateTime(m_wsValue, wsFormat, FX_DATETIMETYPE_Time,
-                                     &wsResult);
+      bRet =
+          pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_Time, &wsResult);
       break;
     case FX_LOCALECATEGORY_DateTime:
-      bRet = pFormat->FormatDateTime(m_wsValue, wsFormat,
-                                     FX_DATETIMETYPE_DateTime, &wsResult);
+      bRet = pFormat->FormatDateTime(m_wsValue, FX_DATETIMETYPE_DateTime,
+                                     &wsResult);
       break;
     default:
       wsResult = m_wsValue;
@@ -380,8 +316,6 @@
                 eValueType != XFA_VALUEPICTURE_Display)) {
     wsResult = m_wsValue;
   }
-  if (pLocale)
-    m_pLocaleMgr->SetDefLocale(locale);
 
   return bRet;
 }
@@ -430,65 +364,67 @@
 
 bool CXFA_LocaleValue::ValidateCanonicalDate(const WideString& wsDate,
                                              CFX_DateTime* unDate) {
-  static const uint16_t LastDay[12] = {31, 28, 31, 30, 31, 30,
-                                       31, 31, 30, 31, 30, 31};
+  static const uint8_t LastDay[12] = {31, 28, 31, 30, 31, 30,
+                                      31, 31, 30, 31, 30, 31};
   static const uint16_t wCountY = 4;
   static const uint16_t wCountM = 2;
   static const uint16_t wCountD = 2;
-  int nLen = wsDate.GetLength();
-  if (nLen < wCountY || nLen > wCountY + wCountM + wCountD + 2)
+  pdfium::span<const wchar_t> spDate = wsDate.span();
+  if (spDate.size() < wCountY ||
+      spDate.size() > wCountY + wCountM + wCountD + 2) {
     return false;
-
+  }
   const bool bSymbol = wsDate.Contains(0x2D);
   uint16_t wYear = 0;
   uint16_t wMonth = 0;
   uint16_t wDay = 0;
-  const wchar_t* pDate = wsDate.c_str();
-  int nIndex = 0;
-  int nStart = 0;
-  while (pDate[nIndex] != '\0' && nIndex < wCountY) {
-    if (!FXSYS_isDecimalDigit(pDate[nIndex]))
+  size_t nIndex = 0;
+  size_t nStart = 0;
+  while (nIndex < wCountY && spDate[nIndex] != '\0') {
+    if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
       return false;
 
-    wYear = (pDate[nIndex] - '0') + wYear * 10;
+    wYear = (spDate[nIndex] - '0') + wYear * 10;
     nIndex++;
   }
   if (bSymbol) {
-    if (pDate[nIndex] != 0x2D)
+    if (nIndex >= spDate.size() || spDate[nIndex] != 0x2D)
       return false;
     nIndex++;
   }
 
   nStart = nIndex;
-  while (pDate[nIndex] != '\0' && nIndex - nStart < wCountM && nIndex < nLen) {
-    if (!FXSYS_isDecimalDigit(pDate[nIndex]))
+  while (nIndex < spDate.size() && spDate[nIndex] != '\0' &&
+         nIndex - nStart < wCountM) {
+    if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
       return false;
 
-    wMonth = (pDate[nIndex] - '0') + wMonth * 10;
+    wMonth = (spDate[nIndex] - '0') + wMonth * 10;
     nIndex++;
   }
   if (bSymbol) {
-    if (pDate[nIndex] != 0x2D)
+    if (nIndex >= spDate.size() || spDate[nIndex] != 0x2D)
       return false;
     nIndex++;
   }
 
   nStart = nIndex;
-  while (pDate[nIndex] != '\0' && nIndex - nStart < wCountD && nIndex < nLen) {
-    if (!FXSYS_isDecimalDigit(pDate[nIndex]))
+  while (nIndex < spDate.size() && spDate[nIndex] != '\0' &&
+         nIndex - nStart < wCountD) {
+    if (!FXSYS_IsDecimalDigit(spDate[nIndex]))
       return false;
 
-    wDay = (pDate[nIndex] - '0') + wDay * 10;
+    wDay = (spDate[nIndex] - '0') + wDay * 10;
     nIndex++;
   }
-  if (nIndex != nLen)
+  if (nIndex != spDate.size())
     return false;
   if (wYear < 1900 || wYear > 2029)
     return false;
   if (wMonth < 1 || wMonth > 12)
-    return wMonth == 0 && nLen == wCountY;
+    return wMonth == 0 && spDate.size() == wCountY;
   if (wDay < 1)
-    return wDay == 0 && (nLen == wCountY + wCountM);
+    return wDay == 0 && spDate.size() == wCountY + wCountM;
   if (wMonth == 2) {
     if (wYear % 400 == 0 || (wYear % 100 != 0 && wYear % 4 == 0)) {
       if (wDay > 29)
@@ -506,8 +442,8 @@
 }
 
 bool CXFA_LocaleValue::ValidateCanonicalTime(const WideString& wsTime) {
-  int nLen = wsTime.GetLength();
-  if (nLen < 2)
+  pdfium::span<const wchar_t> spTime = wsTime.span();
+  if (spTime.size() < 2)
     return false;
 
   const uint16_t wCountH = 2;
@@ -519,128 +455,132 @@
   uint16_t wMinute = 0;
   uint16_t wSecond = 0;
   uint16_t wFraction = 0;
-  const wchar_t* pTime = wsTime.c_str();
-  int nIndex = 0;
-  int nStart = 0;
-  while (nIndex - nStart < wCountH && pTime[nIndex]) {
-    if (!FXSYS_isDecimalDigit(pTime[nIndex]))
+  size_t nIndex = 0;
+  size_t nStart = 0;
+  while (nIndex - nStart < wCountH && spTime[nIndex]) {
+    if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
       return false;
-    wHour = pTime[nIndex] - '0' + wHour * 10;
+    wHour = spTime[nIndex] - '0' + wHour * 10;
     nIndex++;
   }
   if (bSymbol) {
-    if (nIndex < nLen && pTime[nIndex] != ':')
+    if (nIndex < spTime.size() && spTime[nIndex] != ':')
       return false;
     nIndex++;
   }
 
   nStart = nIndex;
-  while (nIndex - nStart < wCountM && nIndex < nLen && pTime[nIndex]) {
-    if (!FXSYS_isDecimalDigit(pTime[nIndex]))
+  while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
+         nIndex - nStart < wCountM) {
+    if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
       return false;
-    wMinute = pTime[nIndex] - '0' + wMinute * 10;
+    wMinute = spTime[nIndex] - '0' + wMinute * 10;
     nIndex++;
   }
   if (bSymbol) {
-    if (nIndex < nLen && pTime[nIndex] != ':')
+    if (nIndex >= spTime.size() || spTime[nIndex] != ':')
       return false;
     nIndex++;
   }
   nStart = nIndex;
-  while (nIndex - nStart < wCountS && nIndex < nLen && pTime[nIndex]) {
-    if (!FXSYS_isDecimalDigit(pTime[nIndex]))
+  while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
+         nIndex - nStart < wCountS) {
+    if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
       return false;
-    wSecond = pTime[nIndex] - '0' + wSecond * 10;
+    wSecond = spTime[nIndex] - '0' + wSecond * 10;
     nIndex++;
   }
   auto pos = wsTime.Find('.');
   if (pos.has_value() && pos.value() != 0) {
-    if (pTime[nIndex] != '.')
+    if (nIndex >= spTime.size() || spTime[nIndex] != '.')
       return false;
     nIndex++;
     nStart = nIndex;
-    while (nIndex - nStart < wCountF && nIndex < nLen && pTime[nIndex]) {
-      if (!FXSYS_isDecimalDigit(pTime[nIndex]))
+    while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
+           nIndex - nStart < wCountF) {
+      if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
         return false;
-      wFraction = pTime[nIndex] - '0' + wFraction * 10;
+      wFraction = spTime[nIndex] - '0' + wFraction * 10;
       nIndex++;
     }
   }
-  if (nIndex < nLen) {
-    if (pTime[nIndex] == 'Z') {
+  if (nIndex < spTime.size()) {
+    if (spTime[nIndex] == 'Z') {
       nIndex++;
-    } else if (pTime[nIndex] == '-' || pTime[nIndex] == '+') {
+    } else if (spTime[nIndex] == '-' || spTime[nIndex] == '+') {
       int16_t nOffsetH = 0;
       int16_t nOffsetM = 0;
       nIndex++;
       nStart = nIndex;
-      while (nIndex - nStart < wCountH && nIndex < nLen && pTime[nIndex]) {
-        if (!FXSYS_isDecimalDigit(pTime[nIndex]))
+      while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
+             nIndex - nStart < wCountH) {
+        if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
           return false;
-        nOffsetH = pTime[nIndex] - '0' + nOffsetH * 10;
+        nOffsetH = spTime[nIndex] - '0' + nOffsetH * 10;
         nIndex++;
       }
       if (bSymbol) {
-        if (nIndex < nLen && pTime[nIndex] != ':')
+        if (nIndex >= spTime.size() || spTime[nIndex] != ':')
           return false;
         nIndex++;
       }
       nStart = nIndex;
-      while (nIndex - nStart < wCountM && nIndex < nLen && pTime[nIndex]) {
-        if (!FXSYS_isDecimalDigit(pTime[nIndex]))
+      while (nIndex < spTime.size() && spTime[nIndex] != '\0' &&
+             nIndex - nStart < wCountM) {
+        if (!FXSYS_IsDecimalDigit(spTime[nIndex]))
           return false;
-        nOffsetM = pTime[nIndex] - '0' + nOffsetM * 10;
+        nOffsetM = spTime[nIndex] - '0' + nOffsetM * 10;
         nIndex++;
       }
       if (nOffsetH > 12 || nOffsetM >= 60)
         return false;
     }
   }
-  return nIndex == nLen && wHour < 24 && wMinute < 60 && wSecond < 60 &&
-         wFraction <= 999;
+  return nIndex == spTime.size() && wHour < 24 && wMinute < 60 &&
+         wSecond < 60 && wFraction <= 999;
 }
 
 bool CXFA_LocaleValue::ParsePatternValue(const WideString& wsValue,
                                          const WideString& wsPattern,
-                                         IFX_Locale* pLocale) {
-  IFX_Locale* locale = m_pLocaleMgr->GetDefLocale();
-  if (pLocale)
-    m_pLocaleMgr->SetDefLocale(pLocale);
+                                         LocaleIface* pLocale) {
+  if (!m_pLocaleMgr)
+    return false;
 
-  auto pFormat = pdfium::MakeUnique<CFGAS_FormatString>(m_pLocaleMgr);
-  std::vector<WideString> wsPatterns;
-  pFormat->SplitFormatString(wsPattern, &wsPatterns);
+  std::vector<WideString> wsPatterns =
+      CFGAS_StringFormatter::SplitOnBars(wsPattern);
+
+  ScopedLocale scoped_locale(m_pLocaleMgr.Get(), pLocale);
   bool bRet = false;
-  int32_t iCount = pdfium::CollectionSize<int32_t>(wsPatterns);
-  for (int32_t i = 0; i < iCount && !bRet; i++) {
-    WideString wsFormat = wsPatterns[i];
-    switch (ValueCategory(pFormat->GetCategory(wsFormat), m_dwType)) {
+  for (size_t i = 0; !bRet && i < wsPatterns.size(); i++) {
+    const WideString& wsFormat = wsPatterns[i];
+    auto pFormat =
+        pdfium::MakeUnique<CFGAS_StringFormatter>(m_pLocaleMgr.Get(), wsFormat);
+    switch (ValueCategory(pFormat->GetCategory(), m_dwType)) {
       case FX_LOCALECATEGORY_Null:
-        bRet = pFormat->ParseNull(wsValue, wsFormat);
+        bRet = pFormat->ParseNull(wsValue);
         if (bRet)
           m_wsValue.clear();
         break;
       case FX_LOCALECATEGORY_Zero:
-        bRet = pFormat->ParseZero(wsValue, wsFormat);
+        bRet = pFormat->ParseZero(wsValue);
         if (bRet)
           m_wsValue = L"0";
         break;
       case FX_LOCALECATEGORY_Num: {
         WideString fNum;
-        bRet = pFormat->ParseNum(wsValue, wsFormat, &fNum);
+        bRet = pFormat->ParseNum(wsValue, &fNum);
         if (bRet)
-          m_wsValue = fNum;
+          m_wsValue = std::move(fNum);
         break;
       }
       case FX_LOCALECATEGORY_Text:
-        bRet = pFormat->ParseText(wsValue, wsFormat, &m_wsValue);
+        bRet = pFormat->ParseText(wsValue, &m_wsValue);
         break;
       case FX_LOCALECATEGORY_Date: {
         CFX_DateTime dt;
         bRet = ValidateCanonicalDate(wsValue, &dt);
         if (!bRet) {
-          bRet = pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Date,
-                                        &dt);
+          bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Date, &dt);
         }
         if (bRet)
           SetDate(dt);
@@ -648,16 +588,14 @@
       }
       case FX_LOCALECATEGORY_Time: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, wsFormat, FX_DATETIMETYPE_Time,
-                                      &dt);
+        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_Time, &dt);
         if (bRet)
           SetTime(dt);
         break;
       }
       case FX_LOCALECATEGORY_DateTime: {
         CFX_DateTime dt;
-        bRet = pFormat->ParseDateTime(wsValue, wsFormat,
-                                      FX_DATETIMETYPE_DateTime, &dt);
+        bRet = pFormat->ParseDateTime(wsValue, FX_DATETIMETYPE_DateTime, &dt);
         if (bRet)
           SetDateTime(dt);
         break;
@@ -671,9 +609,6 @@
   if (!bRet)
     m_wsValue = wsValue;
 
-  if (pLocale)
-    m_pLocaleMgr->SetDefLocale(locale);
-
   return bRet;
 }
 
@@ -681,33 +616,37 @@
                                         int32_t nIntLen,
                                         int32_t nDecLen) {
   ASSERT(wsFormat.IsEmpty());
-  ASSERT(nIntLen >= -1 && nDecLen >= -1);
+  ASSERT(nIntLen >= -1);
+  ASSERT(nDecLen >= -1);
 
   int32_t nTotalLen = (nIntLen >= 0 ? nIntLen : 2) + 1 +
                       (nDecLen >= 0 ? nDecLen : 2) + (nDecLen == 0 ? 0 : 1);
-  wchar_t* lpBuf = wsFormat.GetBuffer(nTotalLen);
-  int32_t nPos = 0;
-  lpBuf[nPos++] = L's';
+  {
+    // Span's lifetime must end before ReleaseBuffer() below.
+    pdfium::span<wchar_t> lpBuf = wsFormat.GetBuffer(nTotalLen);
+    int32_t nPos = 0;
+    lpBuf[nPos++] = L's';
 
-  if (nIntLen == -1) {
-    lpBuf[nPos++] = L'z';
-    lpBuf[nPos++] = L'*';
-  } else {
-    while (nIntLen) {
+    if (nIntLen == -1) {
       lpBuf[nPos++] = L'z';
-      nIntLen--;
+      lpBuf[nPos++] = L'*';
+    } else {
+      while (nIntLen) {
+        lpBuf[nPos++] = L'z';
+        nIntLen--;
+      }
     }
-  }
-  if (nDecLen != 0) {
-    lpBuf[nPos++] = L'.';
-  }
-  if (nDecLen == -1) {
-    lpBuf[nPos++] = L'z';
-    lpBuf[nPos++] = L'*';
-  } else {
-    while (nDecLen) {
+    if (nDecLen != 0) {
+      lpBuf[nPos++] = L'.';
+    }
+    if (nDecLen == -1) {
       lpBuf[nPos++] = L'z';
-      nDecLen--;
+      lpBuf[nPos++] = L'*';
+    } else {
+      while (nDecLen) {
+        lpBuf[nPos++] = L'z';
+        nDecLen--;
+      }
     }
   }
   wsFormat.ReleaseBuffer(nTotalLen);
@@ -715,16 +654,16 @@
 
 bool CXFA_LocaleValue::ValidateNumericTemp(const WideString& wsNumeric,
                                            const WideString& wsFormat,
-                                           IFX_Locale* pLocale) {
+                                           LocaleIface* pLocale) {
   if (wsFormat.IsEmpty() || wsNumeric.IsEmpty())
     return true;
 
-  const wchar_t* pNum = wsNumeric.c_str();
-  const wchar_t* pFmt = wsFormat.c_str();
+  pdfium::span<const wchar_t> spNum = wsNumeric.span();
+  pdfium::span<const wchar_t> spFmt = wsFormat.span();
   int32_t n = 0;
   int32_t nf = 0;
-  wchar_t c = pNum[n];
-  wchar_t cf = pFmt[nf];
+  wchar_t c = spNum[n];
+  wchar_t cf = spFmt[nf];
   if (cf == L's') {
     if (c == L'-' || c == L'+')
       ++n;
@@ -734,10 +673,10 @@
   bool bLimit = true;
   int32_t nCount = wsNumeric.GetLength();
   int32_t nCountFmt = wsFormat.GetLength();
-  while (n < nCount && (bLimit ? nf < nCountFmt : true) &&
-         FXSYS_isDecimalDigit(c = pNum[n])) {
-    if (bLimit == true) {
-      if ((cf = pFmt[nf]) == L'*')
+  while (n < nCount && (!bLimit || nf < nCountFmt) &&
+         FXSYS_IsDecimalDigit(c = spNum[n])) {
+    if (bLimit) {
+      if ((cf = spFmt[nf]) == L'*')
         bLimit = false;
       else if (cf == L'z')
         nf++;
@@ -751,18 +690,18 @@
   if (nf == nCountFmt)
     return false;
 
-  while (nf < nCountFmt && (cf = pFmt[nf]) != L'.') {
+  while (nf < nCountFmt && (cf = spFmt[nf]) != L'.') {
     ASSERT(cf == L'z' || cf == L'*');
     ++nf;
   }
 
   WideString wsDecimalSymbol;
   if (pLocale)
-    wsDecimalSymbol = pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
+    wsDecimalSymbol = pLocale->GetDecimalSymbol();
   else
     wsDecimalSymbol = WideString(L'.');
 
-  if (pFmt[nf] != L'.')
+  if (spFmt[nf] != L'.')
     return false;
   if (wsDecimalSymbol != WideStringView(c) && c != L'.')
     return false;
@@ -770,10 +709,10 @@
   ++nf;
   ++n;
   bLimit = true;
-  while (n < nCount && (bLimit ? nf < nCountFmt : true) &&
-         FXSYS_isDecimalDigit(c = pNum[n])) {
-    if (bLimit == true) {
-      if ((cf = pFmt[nf]) == L'*')
+  while (n < nCount && (!bLimit || nf < nCountFmt) &&
+         FXSYS_IsDecimalDigit(spNum[n])) {
+    if (bLimit) {
+      if ((cf = spFmt[nf]) == L'*')
         bLimit = false;
       else if (cf == L'z')
         nf++;
diff --git a/xfa/fxfa/parser/cxfa_localevalue.h b/xfa/fxfa/parser/cxfa_localevalue.h
index 60de601..2005905 100644
--- a/xfa/fxfa/parser/cxfa_localevalue.h
+++ b/xfa/fxfa/parser/cxfa_localevalue.h
@@ -9,9 +9,10 @@
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
-#include "xfa/fxfa/cxfa_widgetacc.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
 
-class IFX_Locale;
+class LocaleIface;
 class CFX_DateTime;
 class CXFA_LocaleMgr;
 
@@ -28,7 +29,6 @@
 class CXFA_LocaleValue {
  public:
   CXFA_LocaleValue();
-  CXFA_LocaleValue(const CXFA_LocaleValue& value);
   CXFA_LocaleValue(uint32_t dwType, CXFA_LocaleMgr* pLocaleMgr);
   CXFA_LocaleValue(uint32_t dwType,
                    const WideString& wsValue,
@@ -36,39 +36,40 @@
   CXFA_LocaleValue(uint32_t dwType,
                    const WideString& wsValue,
                    const WideString& wsFormat,
-                   IFX_Locale* pLocale,
+                   LocaleIface* pLocale,
                    CXFA_LocaleMgr* pLocaleMgr);
+  CXFA_LocaleValue(const CXFA_LocaleValue& that);
   ~CXFA_LocaleValue();
-  CXFA_LocaleValue& operator=(const CXFA_LocaleValue& value);
+
+  CXFA_LocaleValue& operator=(const CXFA_LocaleValue& that);
 
   bool ValidateValue(const WideString& wsValue,
                      const WideString& wsPattern,
-                     IFX_Locale* pLocale,
+                     LocaleIface* pLocale,
                      WideString* pMatchFormat);
 
   bool FormatPatterns(WideString& wsResult,
                       const WideString& wsFormat,
-                      IFX_Locale* pLocale,
+                      LocaleIface* pLocale,
                       XFA_VALUEPICTURE eValueType) const;
 
   void GetNumericFormat(WideString& wsFormat, int32_t nIntLen, int32_t nDecLen);
   bool ValidateNumericTemp(const WideString& wsNumeric,
                            const WideString& wsFormat,
-                           IFX_Locale* pLocale);
+                           LocaleIface* pLocale);
 
-  WideString GetValue() const { return m_wsValue; }
+  bool IsValid() const { return m_bValid; }
+  const WideString& GetValue() const { return m_wsValue; }
   uint32_t GetType() const { return m_dwType; }
   double GetDoubleNum() const;
   bool SetDate(const CFX_DateTime& d);
   CFX_DateTime GetDate() const;
   CFX_DateTime GetTime() const;
 
-  bool IsValid() const { return m_bValid; }
-
  private:
   bool FormatSinglePattern(WideString& wsResult,
                            const WideString& wsFormat,
-                           IFX_Locale* pLocale,
+                           LocaleIface* pLocale,
                            XFA_VALUEPICTURE eValueType) const;
   bool ValidateCanonicalValue(const WideString& wsValue, uint32_t dwVType);
   bool ValidateCanonicalDate(const WideString& wsDate, CFX_DateTime* unDate);
@@ -79,12 +80,12 @@
 
   bool ParsePatternValue(const WideString& wsValue,
                          const WideString& wsPattern,
-                         IFX_Locale* pLocale);
+                         LocaleIface* pLocale);
 
-  CXFA_LocaleMgr* m_pLocaleMgr;
+  UnownedPtr<CXFA_LocaleMgr> m_pLocaleMgr;
   WideString m_wsValue;
-  uint32_t m_dwType;
-  bool m_bValid;
+  uint32_t m_dwType = XFA_VT_NULL;
+  bool m_bValid = true;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_LOCALEVALUE_H_
diff --git a/xfa/fxfa/parser/cxfa_localevalue_unittest.cpp b/xfa/fxfa/parser/cxfa_localevalue_unittest.cpp
new file mode 100644
index 0000000..e69b058
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_localevalue_unittest.cpp
@@ -0,0 +1,36 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/cxfa_localevalue.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// We don't expect more precision than a float's worth from this code.
+float MakeDoubleNumAsFloat(const wchar_t* str) {
+  return static_cast<float>(
+      CXFA_LocaleValue(XFA_VT_FLOAT, str, nullptr).GetDoubleNum());
+}
+
+}  // namespace
+
+TEST(CXFALocaleValueTest, GetDoubleNum) {
+  EXPECT_EQ(0.0, MakeDoubleNumAsFloat(L""));
+  EXPECT_EQ(0.0, MakeDoubleNumAsFloat(L"0"));
+  EXPECT_EQ(0.0, MakeDoubleNumAsFloat(L"0."));
+  EXPECT_EQ(0.0, MakeDoubleNumAsFloat(L"0.0"));
+  EXPECT_EQ(0.0, MakeDoubleNumAsFloat(L"0.x"));
+  EXPECT_EQ(7.0, MakeDoubleNumAsFloat(L"7.x"));
+  EXPECT_FLOAT_EQ(0.54321f, MakeDoubleNumAsFloat(L".54321"));
+  EXPECT_FLOAT_EQ(0.54321f, MakeDoubleNumAsFloat(L"0.54321"));
+  EXPECT_FLOAT_EQ(0.54321f, MakeDoubleNumAsFloat(L"+0.54321"));
+  EXPECT_FLOAT_EQ(0.54321f, MakeDoubleNumAsFloat(L"   +0.54321"));
+  EXPECT_FLOAT_EQ(-0.54321f, MakeDoubleNumAsFloat(L"-.54321"));
+  EXPECT_FLOAT_EQ(-0.54321f, MakeDoubleNumAsFloat(L"-0.54321"));
+  EXPECT_FLOAT_EQ(-0.54321f, MakeDoubleNumAsFloat(L"  -0.54321"));
+  EXPECT_FLOAT_EQ(-0.054321f, MakeDoubleNumAsFloat(L"-0.54321e-1"));
+  EXPECT_FLOAT_EQ(-0.54321f, MakeDoubleNumAsFloat(L"-0.54321e0"));
+  EXPECT_FLOAT_EQ(-5.4321f, MakeDoubleNumAsFloat(L"-0.54321e1"));
+}
diff --git a/xfa/fxfa/parser/cxfa_lockdocument.cpp b/xfa/fxfa/parser/cxfa_lockdocument.cpp
index 7e65334..b6aa783 100644
--- a/xfa/fxfa/parser/cxfa_lockdocument.cpp
+++ b/xfa/fxfa/parser/cxfa_lockdocument.cpp
@@ -6,17 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_lockdocument.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kLockDocumentAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"lockDocument";
+};
 
 }  // namespace
 
@@ -26,8 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::LockDocument,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kLockDocumentAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_LockDocument::~CXFA_LockDocument() {}
+CXFA_LockDocument::~CXFA_LockDocument() = default;
diff --git a/xfa/fxfa/parser/cxfa_lockdocument.h b/xfa/fxfa/parser/cxfa_lockdocument.h
index 025031a..1328d34 100644
--- a/xfa/fxfa/parser/cxfa_lockdocument.h
+++ b/xfa/fxfa/parser/cxfa_lockdocument.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_LockDocument : public CXFA_Node {
+class CXFA_LockDocument final : public CXFA_Node {
  public:
   CXFA_LockDocument(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_LockDocument() override;
diff --git a/xfa/fxfa/parser/cxfa_log.cpp b/xfa/fxfa/parser/cxfa_log.cpp
index 38651cb..e3e6c91 100644
--- a/xfa/fxfa/parser/cxfa_log.cpp
+++ b/xfa/fxfa/parser/cxfa_log.cpp
@@ -6,19 +6,22 @@
 
 #include "xfa/fxfa/parser/cxfa_log.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::To, 1, 0},
-                                                 {XFA_Element::Uri, 1, 0},
-                                                 {XFA_Element::Mode, 1, 0},
-                                                 {XFA_Element::Threshold, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kLogPropertyData[] = {
+    {XFA_Element::To, 1, 0},
+    {XFA_Element::Uri, 1, 0},
+    {XFA_Element::Mode, 1, 0},
+    {XFA_Element::Threshold, 1, 0},
+};
+
+const CXFA_Node::AttributeData kLogAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"log";
+};
 
 }  // namespace
 
@@ -28,8 +31,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Log,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kLogPropertyData,
+                kLogAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Log::~CXFA_Log() {}
+CXFA_Log::~CXFA_Log() = default;
diff --git a/xfa/fxfa/parser/cxfa_log.h b/xfa/fxfa/parser/cxfa_log.h
index 17fd290..27173cd 100644
--- a/xfa/fxfa/parser/cxfa_log.h
+++ b/xfa/fxfa/parser/cxfa_log.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Log : public CXFA_Node {
+class CXFA_Log final : public CXFA_Node {
  public:
   CXFA_Log(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Log() override;
diff --git a/xfa/fxfa/parser/cxfa_manifest.cpp b/xfa/fxfa/parser/cxfa_manifest.cpp
index 32ec795..071ba3f 100644
--- a/xfa/fxfa/parser/cxfa_manifest.cpp
+++ b/xfa/fxfa/parser/cxfa_manifest.cpp
@@ -11,18 +11,18 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kManifestPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kManifestAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Action, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Include},
+     (void*)XFA_AttributeValue::Include},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"manifest";
+};
 
 }  // namespace
 
@@ -32,9 +32,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Manifest,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kManifestPropertyData,
+                kManifestAttributeData,
                 pdfium::MakeUnique<CJX_Manifest>(this)) {}
 
-CXFA_Manifest::~CXFA_Manifest() {}
+CXFA_Manifest::~CXFA_Manifest() = default;
diff --git a/xfa/fxfa/parser/cxfa_manifest.h b/xfa/fxfa/parser/cxfa_manifest.h
index a13cdac..d5ca05f 100644
--- a/xfa/fxfa/parser/cxfa_manifest.h
+++ b/xfa/fxfa/parser/cxfa_manifest.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Manifest : public CXFA_Node {
+class CXFA_Manifest final : public CXFA_Node {
  public:
   CXFA_Manifest(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Manifest() override;
diff --git a/xfa/fxfa/parser/cxfa_map.cpp b/xfa/fxfa/parser/cxfa_map.cpp
index 3e0c041..782cbc5 100644
--- a/xfa/fxfa/parser/cxfa_map.cpp
+++ b/xfa/fxfa/parser/cxfa_map.cpp
@@ -6,12 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_map.h"
 
-#include "fxjs/xfa/cjx_map.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kMapAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
@@ -20,9 +20,7 @@
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::From, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"map";
+};
 
 }  // namespace
 
@@ -32,9 +30,8 @@
                 (XFA_XDPPACKET_Config | XFA_XDPPACKET_SourceSet),
                 XFA_ObjectType::Node,
                 XFA_Element::Map,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Map>(this)) {}
+                {},
+                kMapAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Map::~CXFA_Map() {}
+CXFA_Map::~CXFA_Map() = default;
diff --git a/xfa/fxfa/parser/cxfa_map.h b/xfa/fxfa/parser/cxfa_map.h
index c49568f..f8e0797 100644
--- a/xfa/fxfa/parser/cxfa_map.h
+++ b/xfa/fxfa/parser/cxfa_map.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Map : public CXFA_Node {
+class CXFA_Map final : public CXFA_Node {
  public:
   CXFA_Map(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Map() override;
diff --git a/xfa/fxfa/parser/cxfa_margin.cpp b/xfa/fxfa/parser/cxfa_margin.cpp
index 265d7e9..5e58a88 100644
--- a/xfa/fxfa/parser/cxfa_margin.cpp
+++ b/xfa/fxfa/parser/cxfa_margin.cpp
@@ -6,14 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_margin.h"
 
-#include "fxjs/xfa/cjx_margin.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kMarginPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kMarginAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::LeftInset, XFA_AttributeType::Measure, (void*)L"0in"},
@@ -21,9 +23,7 @@
     {XFA_Attribute::TopInset, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::RightInset, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"margin";
+};
 
 }  // namespace
 
@@ -33,12 +33,11 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Margin,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Margin>(this)) {}
+                kMarginPropertyData,
+                kMarginAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Margin::~CXFA_Margin() {}
+CXFA_Margin::~CXFA_Margin() = default;
 
 float CXFA_Margin::GetLeftInset() const {
   return TryLeftInset().value_or(0);
diff --git a/xfa/fxfa/parser/cxfa_margin.h b/xfa/fxfa/parser/cxfa_margin.h
index e7f95b6..813bdfe 100644
--- a/xfa/fxfa/parser/cxfa_margin.h
+++ b/xfa/fxfa/parser/cxfa_margin.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Margin : public CXFA_Node {
+class CXFA_Margin final : public CXFA_Node {
  public:
   CXFA_Margin(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Margin() override;
diff --git a/xfa/fxfa/parser/cxfa_mdp.cpp b/xfa/fxfa/parser/cxfa_mdp.cpp
index b8fa1cd..3917bb6 100644
--- a/xfa/fxfa/parser/cxfa_mdp.cpp
+++ b/xfa/fxfa/parser/cxfa_mdp.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_mdp.h"
 
-#include "fxjs/xfa/cjx_mdp.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kMdpAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::SignatureType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Filter},
+     (void*)XFA_AttributeValue::Filter},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Permissions, XFA_AttributeType::Integer, (void*)2},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"mdp";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Mdp,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Mdp>(this)) {}
+                {},
+                kMdpAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Mdp::~CXFA_Mdp() {}
+CXFA_Mdp::~CXFA_Mdp() = default;
diff --git a/xfa/fxfa/parser/cxfa_mdp.h b/xfa/fxfa/parser/cxfa_mdp.h
index 0ae626d..79b1480 100644
--- a/xfa/fxfa/parser/cxfa_mdp.h
+++ b/xfa/fxfa/parser/cxfa_mdp.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Mdp : public CXFA_Node {
+class CXFA_Mdp final : public CXFA_Node {
  public:
   CXFA_Mdp(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Mdp() override;
diff --git a/xfa/fxfa/parser/cxfa_measurement.cpp b/xfa/fxfa/parser/cxfa_measurement.cpp
index d0534c9..3ce13e6 100644
--- a/xfa/fxfa/parser/cxfa_measurement.cpp
+++ b/xfa/fxfa/parser/cxfa_measurement.cpp
@@ -6,6 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 
+#include <cmath>
+
 #include "core/fxcrt/fx_extension.h"
 
 namespace {
@@ -18,7 +20,7 @@
 
 }  // namespace
 
-CXFA_Measurement::CXFA_Measurement(const WideStringView& wsMeasure) {
+CXFA_Measurement::CXFA_Measurement(WideStringView wsMeasure) {
   SetString(wsMeasure);
 }
 
@@ -30,20 +32,23 @@
   Set(fValue, eUnit);
 }
 
-void CXFA_Measurement::SetString(const WideStringView& wsMeasure) {
+void CXFA_Measurement::SetString(WideStringView wsMeasure) {
   if (wsMeasure.IsEmpty()) {
-    m_fValue = 0;
-    m_eUnit = XFA_Unit::Unknown;
+    Set(0, XFA_Unit::Unknown);
     return;
   }
 
+  if (wsMeasure[0] == L'=')
+    wsMeasure = wsMeasure.Last(wsMeasure.GetLength() - 1);
+
   int32_t iUsedLen = 0;
-  int32_t iOffset = (wsMeasure[0] == L'=') ? 1 : 0;
-  float fValue = FXSYS_wcstof(wsMeasure.unterminated_c_str() + iOffset,
-                              wsMeasure.GetLength() - iOffset, &iUsedLen);
-  XFA_Unit eUnit = GetUnitFromString(
-      wsMeasure.Right(wsMeasure.GetLength() - (iOffset + iUsedLen)));
-  Set(fValue, eUnit);
+  float fValue = FXSYS_wcstof(wsMeasure.unterminated_c_str(),
+                              wsMeasure.GetLength(), &iUsedLen);
+  if (!std::isfinite(fValue))
+    fValue = 0.0f;
+
+  wsMeasure = wsMeasure.Last(wsMeasure.GetLength() - iUsedLen);
+  Set(fValue, GetUnitFromString(wsMeasure));
 }
 
 WideString CXFA_Measurement::ToString() const {
@@ -128,22 +133,22 @@
 }
 
 // static
-XFA_Unit CXFA_Measurement::GetUnitFromString(const WideStringView& wsUnit) {
-  if (wsUnit == L"mm")
+XFA_Unit CXFA_Measurement::GetUnitFromString(WideStringView wsUnit) {
+  if (wsUnit.EqualsASCII("mm"))
     return XFA_Unit::Mm;
-  if (wsUnit == L"pt")
+  if (wsUnit.EqualsASCII("pt"))
     return XFA_Unit::Pt;
-  if (wsUnit == L"in")
+  if (wsUnit.EqualsASCII("in"))
     return XFA_Unit::In;
-  if (wsUnit == L"cm")
+  if (wsUnit.EqualsASCII("cm"))
     return XFA_Unit::Cm;
-  if (wsUnit == L"pc")
+  if (wsUnit.EqualsASCII("pc"))
     return XFA_Unit::Pc;
-  if (wsUnit == L"mp")
+  if (wsUnit.EqualsASCII("mp"))
     return XFA_Unit::Mp;
-  if (wsUnit == L"em")
+  if (wsUnit.EqualsASCII("em"))
     return XFA_Unit::Em;
-  if (wsUnit == L"%")
+  if (wsUnit.EqualsASCII("%"))
     return XFA_Unit::Percent;
   return XFA_Unit::Unknown;
 }
diff --git a/xfa/fxfa/parser/cxfa_measurement.h b/xfa/fxfa/parser/cxfa_measurement.h
index d83af24..b2cf2d5 100644
--- a/xfa/fxfa/parser/cxfa_measurement.h
+++ b/xfa/fxfa/parser/cxfa_measurement.h
@@ -13,11 +13,11 @@
 
 class CXFA_Measurement {
  public:
-  explicit CXFA_Measurement(const WideStringView& wsMeasure);
+  explicit CXFA_Measurement(WideStringView wsMeasure);
   CXFA_Measurement();
   CXFA_Measurement(float fValue, XFA_Unit eUnit);
 
-  static XFA_Unit GetUnitFromString(const WideStringView& wsUnit);
+  static XFA_Unit GetUnitFromString(WideStringView wsUnit);
 
   void Set(float fValue, XFA_Unit eUnit) {
     m_fValue = fValue;
@@ -31,7 +31,7 @@
   float ToUnit(XFA_Unit eUnit) const;
 
  private:
-  void SetString(const WideStringView& wsMeasure);
+  void SetString(WideStringView wsMeasure);
   bool ToUnitInternal(XFA_Unit eUnit, float* fValue) const;
 
   float m_fValue;
diff --git a/xfa/fxfa/parser/cxfa_measurement_unittest.cpp b/xfa/fxfa/parser/cxfa_measurement_unittest.cpp
new file mode 100644
index 0000000..7a84eb1
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_measurement_unittest.cpp
@@ -0,0 +1,46 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CXFAMeasurementTest, ToString) {
+  CXFA_Measurement measurement;
+
+  measurement.Set(0.1f, XFA_Unit::Percent);
+  EXPECT_STREQ(L"0.1%", measurement.ToString().c_str());
+  measurement.Set(1.0f, XFA_Unit::Em);
+  EXPECT_STREQ(L"1em", measurement.ToString().c_str());
+  measurement.Set(1.1f, XFA_Unit::Pt);
+  EXPECT_STREQ(L"1.1pt", measurement.ToString().c_str());
+  measurement.Set(1.0000001f, XFA_Unit::In);
+  EXPECT_STREQ(L"1.0000001in", measurement.ToString().c_str());
+  measurement.Set(1234.0f, XFA_Unit::Pc);
+  EXPECT_STREQ(L"1234pc", measurement.ToString().c_str());
+  measurement.Set(987654321.0123456789f, XFA_Unit::Cm);
+  EXPECT_STREQ(L"9.8765434e+08cm", measurement.ToString().c_str());
+  measurement.Set(0.0f, XFA_Unit::Mm);
+  EXPECT_STREQ(L"0mm", measurement.ToString().c_str());
+  measurement.Set(-2.0f, XFA_Unit::Mp);
+  EXPECT_STREQ(L"-2mp", measurement.ToString().c_str());
+}
+
+TEST(CXFAMeasurementTest, GetUnitFromString) {
+  EXPECT_EQ(XFA_Unit::Percent, CXFA_Measurement::GetUnitFromString(L"%"));
+  EXPECT_EQ(XFA_Unit::Em, CXFA_Measurement::GetUnitFromString(L"em"));
+  EXPECT_EQ(XFA_Unit::Pt, CXFA_Measurement::GetUnitFromString(L"pt"));
+  EXPECT_EQ(XFA_Unit::In, CXFA_Measurement::GetUnitFromString(L"in"));
+  EXPECT_EQ(XFA_Unit::Pc, CXFA_Measurement::GetUnitFromString(L"pc"));
+  EXPECT_EQ(XFA_Unit::Cm, CXFA_Measurement::GetUnitFromString(L"cm"));
+  EXPECT_EQ(XFA_Unit::Mm, CXFA_Measurement::GetUnitFromString(L"mm"));
+  EXPECT_EQ(XFA_Unit::Mp, CXFA_Measurement::GetUnitFromString(L"mp"));
+
+  EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L""));
+  EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L"foo"));
+  EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L"!"));
+  EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L"CM"));
+  EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L"Cm"));
+  EXPECT_EQ(XFA_Unit::Unknown, CXFA_Measurement::GetUnitFromString(L"cM"));
+}
diff --git a/xfa/fxfa/parser/cxfa_medium.cpp b/xfa/fxfa/parser/cxfa_medium.cpp
index 3fa494a..47dee1b 100644
--- a/xfa/fxfa/parser/cxfa_medium.cpp
+++ b/xfa/fxfa/parser/cxfa_medium.cpp
@@ -6,28 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_medium.h"
 
-#include "fxjs/xfa/cjx_medium.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kMediumAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TrayOut, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
+     (void*)XFA_AttributeValue::Auto},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Orientation, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Portrait},
+     (void*)XFA_AttributeValue::Portrait},
     {XFA_Attribute::ImagingBBox, XFA_AttributeType::CData, (void*)L"none"},
     {XFA_Attribute::Short, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::TrayIn, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
+     (void*)XFA_AttributeValue::Auto},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Stock, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Long, XFA_AttributeType::Measure, (void*)L"0in"},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"medium";
+};
 
 }  // namespace
 
@@ -37,9 +35,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Medium,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Medium>(this)) {}
+                {},
+                kMediumAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Medium::~CXFA_Medium() {}
+CXFA_Medium::~CXFA_Medium() = default;
diff --git a/xfa/fxfa/parser/cxfa_medium.h b/xfa/fxfa/parser/cxfa_medium.h
index b9b9c5d..49be83f 100644
--- a/xfa/fxfa/parser/cxfa_medium.h
+++ b/xfa/fxfa/parser/cxfa_medium.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Medium : public CXFA_Node {
+class CXFA_Medium final : public CXFA_Node {
  public:
   CXFA_Medium(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Medium() override;
diff --git a/xfa/fxfa/parser/cxfa_mediuminfo.cpp b/xfa/fxfa/parser/cxfa_mediuminfo.cpp
index 9a41f1d..cc17657 100644
--- a/xfa/fxfa/parser/cxfa_mediuminfo.cpp
+++ b/xfa/fxfa/parser/cxfa_mediuminfo.cpp
@@ -6,16 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_mediuminfo.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Map, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kMediumInfoPropertyData[] = {
+    {XFA_Element::Map, 1, 0},
+};
+
+const CXFA_Node::AttributeData kMediumInfoAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"mediumInfo";
+};
 
 }  // namespace
 
@@ -25,8 +28,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::MediumInfo,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kMediumInfoPropertyData,
+                kMediumInfoAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_MediumInfo::~CXFA_MediumInfo() {}
+CXFA_MediumInfo::~CXFA_MediumInfo() = default;
diff --git a/xfa/fxfa/parser/cxfa_mediuminfo.h b/xfa/fxfa/parser/cxfa_mediuminfo.h
index da100ea..65e58df 100644
--- a/xfa/fxfa/parser/cxfa_mediuminfo.h
+++ b/xfa/fxfa/parser/cxfa_mediuminfo.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_MediumInfo : public CXFA_Node {
+class CXFA_MediumInfo final : public CXFA_Node {
  public:
   CXFA_MediumInfo(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_MediumInfo() override;
diff --git a/xfa/fxfa/parser/cxfa_meridiem.cpp b/xfa/fxfa/parser/cxfa_meridiem.cpp
index 357ce2c..e250f2d 100644
--- a/xfa/fxfa/parser/cxfa_meridiem.cpp
+++ b/xfa/fxfa/parser/cxfa_meridiem.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_meridiem.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"meridiem";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_Meridiem::CXFA_Meridiem(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -18,8 +15,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Meridiem,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Meridiem::~CXFA_Meridiem() {}
+CXFA_Meridiem::~CXFA_Meridiem() = default;
diff --git a/xfa/fxfa/parser/cxfa_meridiem.h b/xfa/fxfa/parser/cxfa_meridiem.h
index 30bd4b0..40c956c 100644
--- a/xfa/fxfa/parser/cxfa_meridiem.h
+++ b/xfa/fxfa/parser/cxfa_meridiem.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Meridiem : public CXFA_Node {
+class CXFA_Meridiem final : public CXFA_Node {
  public:
   CXFA_Meridiem(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Meridiem() override;
diff --git a/xfa/fxfa/parser/cxfa_meridiemnames.cpp b/xfa/fxfa/parser/cxfa_meridiemnames.cpp
index dfb603e..70ede73 100644
--- a/xfa/fxfa/parser/cxfa_meridiemnames.cpp
+++ b/xfa/fxfa/parser/cxfa_meridiemnames.cpp
@@ -6,12 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_meridiemnames.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Meridiem, 2, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-
-constexpr wchar_t kName[] = L"meridiemNames";
+const CXFA_Node::PropertyData kMeridiemNamesPropertyData[] = {
+    {XFA_Element::Meridiem, 2, 0},
+};
 
 }  // namespace
 
@@ -22,8 +24,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::MeridiemNames,
-                kPropertyData,
-                nullptr,
-                kName) {}
+                kMeridiemNamesPropertyData,
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_MeridiemNames::~CXFA_MeridiemNames() {}
+CXFA_MeridiemNames::~CXFA_MeridiemNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_meridiemnames.h b/xfa/fxfa/parser/cxfa_meridiemnames.h
index 8cade21..00fbbb7 100644
--- a/xfa/fxfa/parser/cxfa_meridiemnames.h
+++ b/xfa/fxfa/parser/cxfa_meridiemnames.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_MeridiemNames : public CXFA_Node {
+class CXFA_MeridiemNames final : public CXFA_Node {
  public:
   CXFA_MeridiemNames(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_MeridiemNames() override;
diff --git a/xfa/fxfa/parser/cxfa_message.cpp b/xfa/fxfa/parser/cxfa_message.cpp
index fa16efd..809a1db 100644
--- a/xfa/fxfa/parser/cxfa_message.cpp
+++ b/xfa/fxfa/parser/cxfa_message.cpp
@@ -6,23 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_message.h"
 
-#include "fxjs/xfa/cjx_message.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::MsgId, 1, 0},
-                                                 {XFA_Element::Severity, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kMessagePropertyData[] = {
+    {XFA_Element::MsgId, 1, 0},
+    {XFA_Element::Severity, 1, 0},
+};
+
+const CXFA_Node::AttributeData kMessageAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"message";
+};
 
 }  // namespace
 
@@ -33,9 +33,8 @@
           (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
           XFA_ObjectType::Node,
           XFA_Element::Message,
-          kPropertyData,
-          kAttributeData,
-          kName,
-          pdfium::MakeUnique<CJX_Message>(this)) {}
+          kMessagePropertyData,
+          kMessageAttributeData,
+          pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Message::~CXFA_Message() {}
+CXFA_Message::~CXFA_Message() = default;
diff --git a/xfa/fxfa/parser/cxfa_message.h b/xfa/fxfa/parser/cxfa_message.h
index f172e06..81cb751 100644
--- a/xfa/fxfa/parser/cxfa_message.h
+++ b/xfa/fxfa/parser/cxfa_message.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Message : public CXFA_Node {
+class CXFA_Message final : public CXFA_Node {
  public:
   CXFA_Message(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Message() override;
diff --git a/xfa/fxfa/parser/cxfa_messaging.cpp b/xfa/fxfa/parser/cxfa_messaging.cpp
index 8ad77a4..4bb2b88 100644
--- a/xfa/fxfa/parser/cxfa_messaging.cpp
+++ b/xfa/fxfa/parser/cxfa_messaging.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_messaging.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kMessagingAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"messaging";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Messaging,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kMessagingAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Messaging::~CXFA_Messaging() {}
+CXFA_Messaging::~CXFA_Messaging() = default;
diff --git a/xfa/fxfa/parser/cxfa_messaging.h b/xfa/fxfa/parser/cxfa_messaging.h
index 48d16b3..86e16f3 100644
--- a/xfa/fxfa/parser/cxfa_messaging.h
+++ b/xfa/fxfa/parser/cxfa_messaging.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Messaging : public CXFA_Node {
+class CXFA_Messaging final : public CXFA_Node {
  public:
   CXFA_Messaging(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Messaging() override;
diff --git a/xfa/fxfa/parser/cxfa_mode.cpp b/xfa/fxfa/parser/cxfa_mode.cpp
index 6195f08..4e8e09e 100644
--- a/xfa/fxfa/parser/cxfa_mode.cpp
+++ b/xfa/fxfa/parser/cxfa_mode.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_mode.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kModeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"mode";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Mode,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kModeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Mode::~CXFA_Mode() {}
+CXFA_Mode::~CXFA_Mode() = default;
diff --git a/xfa/fxfa/parser/cxfa_mode.h b/xfa/fxfa/parser/cxfa_mode.h
index 1dfd426..46675db 100644
--- a/xfa/fxfa/parser/cxfa_mode.h
+++ b/xfa/fxfa/parser/cxfa_mode.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Mode : public CXFA_Node {
+class CXFA_Mode final : public CXFA_Node {
  public:
   CXFA_Mode(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Mode() override;
diff --git a/xfa/fxfa/parser/cxfa_modifyannots.cpp b/xfa/fxfa/parser/cxfa_modifyannots.cpp
index e349dbf..6ec1293 100644
--- a/xfa/fxfa/parser/cxfa_modifyannots.cpp
+++ b/xfa/fxfa/parser/cxfa_modifyannots.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_modifyannots.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kModifyAnnotsAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"modifyAnnots";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ModifyAnnots,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kModifyAnnotsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ModifyAnnots::~CXFA_ModifyAnnots() {}
+CXFA_ModifyAnnots::~CXFA_ModifyAnnots() = default;
diff --git a/xfa/fxfa/parser/cxfa_modifyannots.h b/xfa/fxfa/parser/cxfa_modifyannots.h
index 4e10921..c92b05b 100644
--- a/xfa/fxfa/parser/cxfa_modifyannots.h
+++ b/xfa/fxfa/parser/cxfa_modifyannots.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ModifyAnnots : public CXFA_Node {
+class CXFA_ModifyAnnots final : public CXFA_Node {
  public:
   CXFA_ModifyAnnots(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ModifyAnnots() override;
diff --git a/xfa/fxfa/parser/cxfa_month.cpp b/xfa/fxfa/parser/cxfa_month.cpp
index 687ef4a..8cc4481 100644
--- a/xfa/fxfa/parser/cxfa_month.cpp
+++ b/xfa/fxfa/parser/cxfa_month.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_month.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"month";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_Month::CXFA_Month(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -18,8 +15,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Month,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Month::~CXFA_Month() {}
+CXFA_Month::~CXFA_Month() = default;
diff --git a/xfa/fxfa/parser/cxfa_month.h b/xfa/fxfa/parser/cxfa_month.h
index 69361f5..f63095d 100644
--- a/xfa/fxfa/parser/cxfa_month.h
+++ b/xfa/fxfa/parser/cxfa_month.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Month : public CXFA_Node {
+class CXFA_Month final : public CXFA_Node {
  public:
   CXFA_Month(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Month() override;
diff --git a/xfa/fxfa/parser/cxfa_monthnames.cpp b/xfa/fxfa/parser/cxfa_monthnames.cpp
index 1b05b0d..dfb68d5 100644
--- a/xfa/fxfa/parser/cxfa_monthnames.cpp
+++ b/xfa/fxfa/parser/cxfa_monthnames.cpp
@@ -6,15 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_monthnames.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Month, 12, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
-    {XFA_Attribute::Abbr, XFA_AttributeType::Boolean, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
+const CXFA_Node::PropertyData kMonthNamesPropertyData[] = {
+    {XFA_Element::Month, 12, 0},
+};
 
-constexpr wchar_t kName[] = L"monthNames";
+const CXFA_Node::AttributeData kMonthNamesAttributeData[] = {
+    {XFA_Attribute::Abbr, XFA_AttributeType::Boolean, (void*)0},
+};
 
 }  // namespace
 
@@ -24,8 +27,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::MonthNames,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kMonthNamesPropertyData,
+                kMonthNamesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_MonthNames::~CXFA_MonthNames() {}
+CXFA_MonthNames::~CXFA_MonthNames() = default;
diff --git a/xfa/fxfa/parser/cxfa_monthnames.h b/xfa/fxfa/parser/cxfa_monthnames.h
index f95512b..cf0c331 100644
--- a/xfa/fxfa/parser/cxfa_monthnames.h
+++ b/xfa/fxfa/parser/cxfa_monthnames.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_MonthNames : public CXFA_Node {
+class CXFA_MonthNames final : public CXFA_Node {
  public:
   CXFA_MonthNames(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_MonthNames() override;
diff --git a/xfa/fxfa/parser/cxfa_msgid.cpp b/xfa/fxfa/parser/cxfa_msgid.cpp
index 626833b..107f635 100644
--- a/xfa/fxfa/parser/cxfa_msgid.cpp
+++ b/xfa/fxfa/parser/cxfa_msgid.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_msgid.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kMsgIdAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"msgId";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::MsgId,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kMsgIdAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_MsgId::~CXFA_MsgId() {}
+CXFA_MsgId::~CXFA_MsgId() = default;
diff --git a/xfa/fxfa/parser/cxfa_msgid.h b/xfa/fxfa/parser/cxfa_msgid.h
index 333085a..4c166d7 100644
--- a/xfa/fxfa/parser/cxfa_msgid.h
+++ b/xfa/fxfa/parser/cxfa_msgid.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_MsgId : public CXFA_Node {
+class CXFA_MsgId final : public CXFA_Node {
  public:
   CXFA_MsgId(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_MsgId() override;
diff --git a/xfa/fxfa/parser/cxfa_nameattr.cpp b/xfa/fxfa/parser/cxfa_nameattr.cpp
index dd42694..d29353f 100644
--- a/xfa/fxfa/parser/cxfa_nameattr.cpp
+++ b/xfa/fxfa/parser/cxfa_nameattr.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_nameattr.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kNameAttrAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"nameAttr";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::NameAttr,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kNameAttrAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NameAttr::~CXFA_NameAttr() {}
+CXFA_NameAttr::~CXFA_NameAttr() = default;
diff --git a/xfa/fxfa/parser/cxfa_nameattr.h b/xfa/fxfa/parser/cxfa_nameattr.h
index 05514c0..f346bf6 100644
--- a/xfa/fxfa/parser/cxfa_nameattr.h
+++ b/xfa/fxfa/parser/cxfa_nameattr.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NameAttr : public CXFA_Node {
+class CXFA_NameAttr final : public CXFA_Node {
  public:
   CXFA_NameAttr(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NameAttr() override;
diff --git a/xfa/fxfa/parser/cxfa_neverembed.cpp b/xfa/fxfa/parser/cxfa_neverembed.cpp
index 7ba64a7..18af282 100644
--- a/xfa/fxfa/parser/cxfa_neverembed.cpp
+++ b/xfa/fxfa/parser/cxfa_neverembed.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_neverembed.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kNeverEmbedAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"neverEmbed";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::NeverEmbed,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kNeverEmbedAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NeverEmbed::~CXFA_NeverEmbed() {}
+CXFA_NeverEmbed::~CXFA_NeverEmbed() = default;
diff --git a/xfa/fxfa/parser/cxfa_neverembed.h b/xfa/fxfa/parser/cxfa_neverembed.h
index 11ec928..70f90a3 100644
--- a/xfa/fxfa/parser/cxfa_neverembed.h
+++ b/xfa/fxfa/parser/cxfa_neverembed.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NeverEmbed : public CXFA_Node {
+class CXFA_NeverEmbed final : public CXFA_Node {
  public:
   CXFA_NeverEmbed(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NeverEmbed() override;
diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp
index ec70271..05c6061 100644
--- a/xfa/fxfa/parser/cxfa_node.cpp
+++ b/xfa/fxfa/parser/cxfa_node.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
+#include <algorithm>
 #include <map>
 #include <memory>
 #include <set>
@@ -13,53 +14,534 @@
 #include <vector>
 
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_decimal.h"
-#include "core/fxcrt/cfx_memorystream.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/fx_font.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/compiler_specific.h"
 #include "third_party/base/logging.h"
 #include "third_party/base/ptr_util.h"
+#include "third_party/base/span.h"
 #include "third_party/base/stl_util.h"
+#include "xfa/fde/cfde_textout.h"
+#include "xfa/fgas/crt/cfgas_decimal.h"
+#include "xfa/fgas/crt/locale_iface.h"
+#include "xfa/fgas/font/cfgas_fontmgr.h"
+#include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffapp.h"
 #include "xfa/fxfa/cxfa_ffdocview.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/cxfa_fontmgr.h"
+#include "xfa/fxfa/cxfa_textprovider.h"
+#include "xfa/fxfa/parser/cxfa_accessiblecontent.h"
+#include "xfa/fxfa/parser/cxfa_acrobat.h"
+#include "xfa/fxfa/parser/cxfa_acrobat7.h"
+#include "xfa/fxfa/parser/cxfa_adbe_jsconsole.h"
+#include "xfa/fxfa/parser/cxfa_adbe_jsdebugger.h"
+#include "xfa/fxfa/parser/cxfa_addsilentprint.h"
+#include "xfa/fxfa/parser/cxfa_addviewerpreferences.h"
+#include "xfa/fxfa/parser/cxfa_adjustdata.h"
+#include "xfa/fxfa/parser/cxfa_adobeextensionlevel.h"
+#include "xfa/fxfa/parser/cxfa_agent.h"
+#include "xfa/fxfa/parser/cxfa_alwaysembed.h"
+#include "xfa/fxfa/parser/cxfa_amd.h"
+#include "xfa/fxfa/parser/cxfa_appearancefilter.h"
+#include "xfa/fxfa/parser/cxfa_arc.h"
+#include "xfa/fxfa/parser/cxfa_area.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
+#include "xfa/fxfa/parser/cxfa_assist.h"
 #include "xfa/fxfa/parser/cxfa_attachnodelist.h"
+#include "xfa/fxfa/parser/cxfa_attributes.h"
+#include "xfa/fxfa/parser/cxfa_autosave.h"
+#include "xfa/fxfa/parser/cxfa_barcode.h"
+#include "xfa/fxfa/parser/cxfa_base.h"
+#include "xfa/fxfa/parser/cxfa_batchoutput.h"
+#include "xfa/fxfa/parser/cxfa_behavioroverride.h"
 #include "xfa/fxfa/parser/cxfa_bind.h"
+#include "xfa/fxfa/parser/cxfa_binditems.h"
+#include "xfa/fxfa/parser/cxfa_bookend.h"
+#include "xfa/fxfa/parser/cxfa_boolean.h"
 #include "xfa/fxfa/parser/cxfa_border.h"
+#include "xfa/fxfa/parser/cxfa_break.h"
+#include "xfa/fxfa/parser/cxfa_breakafter.h"
+#include "xfa/fxfa/parser/cxfa_breakbefore.h"
+#include "xfa/fxfa/parser/cxfa_button.h"
+#include "xfa/fxfa/parser/cxfa_cache.h"
 #include "xfa/fxfa/parser/cxfa_calculate.h"
+#include "xfa/fxfa/parser/cxfa_calendarsymbols.h"
 #include "xfa/fxfa/parser/cxfa_caption.h"
+#include "xfa/fxfa/parser/cxfa_certificate.h"
+#include "xfa/fxfa/parser/cxfa_certificates.h"
+#include "xfa/fxfa/parser/cxfa_change.h"
+#include "xfa/fxfa/parser/cxfa_checkbutton.h"
+#include "xfa/fxfa/parser/cxfa_choicelist.h"
+#include "xfa/fxfa/parser/cxfa_color.h"
+#include "xfa/fxfa/parser/cxfa_comb.h"
+#include "xfa/fxfa/parser/cxfa_command.h"
+#include "xfa/fxfa/parser/cxfa_common.h"
+#include "xfa/fxfa/parser/cxfa_compress.h"
+#include "xfa/fxfa/parser/cxfa_compression.h"
+#include "xfa/fxfa/parser/cxfa_compresslogicalstructure.h"
+#include "xfa/fxfa/parser/cxfa_compressobjectstream.h"
+#include "xfa/fxfa/parser/cxfa_config.h"
+#include "xfa/fxfa/parser/cxfa_conformance.h"
+#include "xfa/fxfa/parser/cxfa_connect.h"
+#include "xfa/fxfa/parser/cxfa_connectionset.h"
+#include "xfa/fxfa/parser/cxfa_connectstring.h"
+#include "xfa/fxfa/parser/cxfa_contentarea.h"
+#include "xfa/fxfa/parser/cxfa_contentcopy.h"
+#include "xfa/fxfa/parser/cxfa_copies.h"
+#include "xfa/fxfa/parser/cxfa_corner.h"
+#include "xfa/fxfa/parser/cxfa_creator.h"
+#include "xfa/fxfa/parser/cxfa_currencysymbol.h"
+#include "xfa/fxfa/parser/cxfa_currencysymbols.h"
+#include "xfa/fxfa/parser/cxfa_currentpage.h"
+#include "xfa/fxfa/parser/cxfa_data.h"
+#include "xfa/fxfa/parser/cxfa_datagroup.h"
+#include "xfa/fxfa/parser/cxfa_datamodel.h"
+#include "xfa/fxfa/parser/cxfa_datavalue.h"
+#include "xfa/fxfa/parser/cxfa_date.h"
+#include "xfa/fxfa/parser/cxfa_datepattern.h"
+#include "xfa/fxfa/parser/cxfa_datepatterns.h"
+#include "xfa/fxfa/parser/cxfa_datetime.h"
+#include "xfa/fxfa/parser/cxfa_datetimeedit.h"
+#include "xfa/fxfa/parser/cxfa_datetimesymbols.h"
+#include "xfa/fxfa/parser/cxfa_day.h"
+#include "xfa/fxfa/parser/cxfa_daynames.h"
+#include "xfa/fxfa/parser/cxfa_debug.h"
+#include "xfa/fxfa/parser/cxfa_decimal.h"
+#include "xfa/fxfa/parser/cxfa_defaulttypeface.h"
+#include "xfa/fxfa/parser/cxfa_defaultui.h"
+#include "xfa/fxfa/parser/cxfa_delete.h"
+#include "xfa/fxfa/parser/cxfa_delta.h"
+#include "xfa/fxfa/parser/cxfa_deltas.h"
+#include "xfa/fxfa/parser/cxfa_desc.h"
+#include "xfa/fxfa/parser/cxfa_destination.h"
+#include "xfa/fxfa/parser/cxfa_digestmethod.h"
+#include "xfa/fxfa/parser/cxfa_digestmethods.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_document_parser.h"
+#include "xfa/fxfa/parser/cxfa_documentassembly.h"
+#include "xfa/fxfa/parser/cxfa_draw.h"
+#include "xfa/fxfa/parser/cxfa_driver.h"
+#include "xfa/fxfa/parser/cxfa_dsigdata.h"
+#include "xfa/fxfa/parser/cxfa_duplexoption.h"
+#include "xfa/fxfa/parser/cxfa_dynamicrender.h"
+#include "xfa/fxfa/parser/cxfa_edge.h"
+#include "xfa/fxfa/parser/cxfa_effectiveinputpolicy.h"
+#include "xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h"
+#include "xfa/fxfa/parser/cxfa_embed.h"
+#include "xfa/fxfa/parser/cxfa_encoding.h"
+#include "xfa/fxfa/parser/cxfa_encodings.h"
+#include "xfa/fxfa/parser/cxfa_encrypt.h"
+#include "xfa/fxfa/parser/cxfa_encryption.h"
+#include "xfa/fxfa/parser/cxfa_encryptionlevel.h"
+#include "xfa/fxfa/parser/cxfa_encryptionmethod.h"
+#include "xfa/fxfa/parser/cxfa_encryptionmethods.h"
+#include "xfa/fxfa/parser/cxfa_enforce.h"
+#include "xfa/fxfa/parser/cxfa_equate.h"
+#include "xfa/fxfa/parser/cxfa_equaterange.h"
+#include "xfa/fxfa/parser/cxfa_era.h"
+#include "xfa/fxfa/parser/cxfa_eranames.h"
 #include "xfa/fxfa/parser/cxfa_event.h"
+#include "xfa/fxfa/parser/cxfa_exclgroup.h"
+#include "xfa/fxfa/parser/cxfa_exclude.h"
+#include "xfa/fxfa/parser/cxfa_excludens.h"
+#include "xfa/fxfa/parser/cxfa_exdata.h"
+#include "xfa/fxfa/parser/cxfa_execute.h"
+#include "xfa/fxfa/parser/cxfa_exobject.h"
+#include "xfa/fxfa/parser/cxfa_extras.h"
+#include "xfa/fxfa/parser/cxfa_field.h"
+#include "xfa/fxfa/parser/cxfa_fill.h"
+#include "xfa/fxfa/parser/cxfa_filter.h"
+#include "xfa/fxfa/parser/cxfa_fliplabel.h"
+#include "xfa/fxfa/parser/cxfa_float.h"
 #include "xfa/fxfa/parser/cxfa_font.h"
+#include "xfa/fxfa/parser/cxfa_fontinfo.h"
+#include "xfa/fxfa/parser/cxfa_form.h"
+#include "xfa/fxfa/parser/cxfa_format.h"
+#include "xfa/fxfa/parser/cxfa_formfieldfilling.h"
+#include "xfa/fxfa/parser/cxfa_groupparent.h"
+#include "xfa/fxfa/parser/cxfa_handler.h"
+#include "xfa/fxfa/parser/cxfa_hyphenation.h"
+#include "xfa/fxfa/parser/cxfa_ifempty.h"
+#include "xfa/fxfa/parser/cxfa_image.h"
+#include "xfa/fxfa/parser/cxfa_imageedit.h"
+#include "xfa/fxfa/parser/cxfa_includexdpcontent.h"
+#include "xfa/fxfa/parser/cxfa_incrementalload.h"
+#include "xfa/fxfa/parser/cxfa_incrementalmerge.h"
+#include "xfa/fxfa/parser/cxfa_insert.h"
+#include "xfa/fxfa/parser/cxfa_instancemanager.h"
+#include "xfa/fxfa/parser/cxfa_integer.h"
+#include "xfa/fxfa/parser/cxfa_interactive.h"
+#include "xfa/fxfa/parser/cxfa_issuers.h"
+#include "xfa/fxfa/parser/cxfa_items.h"
+#include "xfa/fxfa/parser/cxfa_jog.h"
 #include "xfa/fxfa/parser/cxfa_keep.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_keyusage.h"
+#include "xfa/fxfa/parser/cxfa_labelprinter.h"
+#include "xfa/fxfa/parser/cxfa_layout.h"
+#include "xfa/fxfa/parser/cxfa_level.h"
+#include "xfa/fxfa/parser/cxfa_line.h"
+#include "xfa/fxfa/parser/cxfa_linear.h"
+#include "xfa/fxfa/parser/cxfa_linearized.h"
+#include "xfa/fxfa/parser/cxfa_locale.h"
+#include "xfa/fxfa/parser/cxfa_localeset.h"
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
+#include "xfa/fxfa/parser/cxfa_lockdocument.h"
+#include "xfa/fxfa/parser/cxfa_log.h"
+#include "xfa/fxfa/parser/cxfa_manifest.h"
+#include "xfa/fxfa/parser/cxfa_map.h"
 #include "xfa/fxfa/parser/cxfa_margin.h"
+#include "xfa/fxfa/parser/cxfa_mdp.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_medium.h"
+#include "xfa/fxfa/parser/cxfa_mediuminfo.h"
+#include "xfa/fxfa/parser/cxfa_meridiem.h"
+#include "xfa/fxfa/parser/cxfa_meridiemnames.h"
+#include "xfa/fxfa/parser/cxfa_message.h"
+#include "xfa/fxfa/parser/cxfa_messaging.h"
+#include "xfa/fxfa/parser/cxfa_mode.h"
+#include "xfa/fxfa/parser/cxfa_modifyannots.h"
+#include "xfa/fxfa/parser/cxfa_month.h"
+#include "xfa/fxfa/parser/cxfa_monthnames.h"
+#include "xfa/fxfa/parser/cxfa_msgid.h"
+#include "xfa/fxfa/parser/cxfa_nameattr.h"
+#include "xfa/fxfa/parser/cxfa_neverembed.h"
 #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
+#include "xfa/fxfa/parser/cxfa_numberofcopies.h"
+#include "xfa/fxfa/parser/cxfa_numberpattern.h"
+#include "xfa/fxfa/parser/cxfa_numberpatterns.h"
+#include "xfa/fxfa/parser/cxfa_numbersymbol.h"
+#include "xfa/fxfa/parser/cxfa_numbersymbols.h"
+#include "xfa/fxfa/parser/cxfa_numericedit.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
+#include "xfa/fxfa/parser/cxfa_oid.h"
+#include "xfa/fxfa/parser/cxfa_oids.h"
+#include "xfa/fxfa/parser/cxfa_openaction.h"
+#include "xfa/fxfa/parser/cxfa_operation.h"
+#include "xfa/fxfa/parser/cxfa_output.h"
+#include "xfa/fxfa/parser/cxfa_outputbin.h"
+#include "xfa/fxfa/parser/cxfa_outputxsl.h"
+#include "xfa/fxfa/parser/cxfa_overflow.h"
+#include "xfa/fxfa/parser/cxfa_overprint.h"
+#include "xfa/fxfa/parser/cxfa_packet.h"
+#include "xfa/fxfa/parser/cxfa_packets.h"
+#include "xfa/fxfa/parser/cxfa_pagearea.h"
+#include "xfa/fxfa/parser/cxfa_pageoffset.h"
+#include "xfa/fxfa/parser/cxfa_pagerange.h"
+#include "xfa/fxfa/parser/cxfa_pageset.h"
+#include "xfa/fxfa/parser/cxfa_pagination.h"
+#include "xfa/fxfa/parser/cxfa_paginationoverride.h"
 #include "xfa/fxfa/parser/cxfa_para.h"
-#include "xfa/fxfa/parser/cxfa_simple_parser.h"
+#include "xfa/fxfa/parser/cxfa_part.h"
+#include "xfa/fxfa/parser/cxfa_password.h"
+#include "xfa/fxfa/parser/cxfa_passwordedit.h"
+#include "xfa/fxfa/parser/cxfa_pattern.h"
+#include "xfa/fxfa/parser/cxfa_pcl.h"
+#include "xfa/fxfa/parser/cxfa_pdf.h"
+#include "xfa/fxfa/parser/cxfa_pdfa.h"
+#include "xfa/fxfa/parser/cxfa_permissions.h"
+#include "xfa/fxfa/parser/cxfa_picktraybypdfsize.h"
+#include "xfa/fxfa/parser/cxfa_picture.h"
+#include "xfa/fxfa/parser/cxfa_plaintextmetadata.h"
+#include "xfa/fxfa/parser/cxfa_presence.h"
+#include "xfa/fxfa/parser/cxfa_present.h"
+#include "xfa/fxfa/parser/cxfa_print.h"
+#include "xfa/fxfa/parser/cxfa_printername.h"
+#include "xfa/fxfa/parser/cxfa_printhighquality.h"
+#include "xfa/fxfa/parser/cxfa_printscaling.h"
+#include "xfa/fxfa/parser/cxfa_producer.h"
+#include "xfa/fxfa/parser/cxfa_proto.h"
+#include "xfa/fxfa/parser/cxfa_ps.h"
+#include "xfa/fxfa/parser/cxfa_psmap.h"
+#include "xfa/fxfa/parser/cxfa_query.h"
+#include "xfa/fxfa/parser/cxfa_radial.h"
+#include "xfa/fxfa/parser/cxfa_range.h"
+#include "xfa/fxfa/parser/cxfa_reason.h"
+#include "xfa/fxfa/parser/cxfa_reasons.h"
+#include "xfa/fxfa/parser/cxfa_record.h"
+#include "xfa/fxfa/parser/cxfa_recordset.h"
+#include "xfa/fxfa/parser/cxfa_rectangle.h"
+#include "xfa/fxfa/parser/cxfa_ref.h"
+#include "xfa/fxfa/parser/cxfa_relevant.h"
+#include "xfa/fxfa/parser/cxfa_rename.h"
+#include "xfa/fxfa/parser/cxfa_renderpolicy.h"
+#include "xfa/fxfa/parser/cxfa_rootelement.h"
+#include "xfa/fxfa/parser/cxfa_runscripts.h"
+#include "xfa/fxfa/parser/cxfa_script.h"
+#include "xfa/fxfa/parser/cxfa_scriptmodel.h"
+#include "xfa/fxfa/parser/cxfa_select.h"
+#include "xfa/fxfa/parser/cxfa_setproperty.h"
+#include "xfa/fxfa/parser/cxfa_severity.h"
+#include "xfa/fxfa/parser/cxfa_sharptext.h"
+#include "xfa/fxfa/parser/cxfa_sharpxhtml.h"
+#include "xfa/fxfa/parser/cxfa_sharpxml.h"
+#include "xfa/fxfa/parser/cxfa_signature.h"
+#include "xfa/fxfa/parser/cxfa_signatureproperties.h"
+#include "xfa/fxfa/parser/cxfa_signdata.h"
+#include "xfa/fxfa/parser/cxfa_signing.h"
+#include "xfa/fxfa/parser/cxfa_silentprint.h"
+#include "xfa/fxfa/parser/cxfa_soapaction.h"
+#include "xfa/fxfa/parser/cxfa_soapaddress.h"
+#include "xfa/fxfa/parser/cxfa_solid.h"
+#include "xfa/fxfa/parser/cxfa_source.h"
+#include "xfa/fxfa/parser/cxfa_sourceset.h"
+#include "xfa/fxfa/parser/cxfa_speak.h"
+#include "xfa/fxfa/parser/cxfa_staple.h"
+#include "xfa/fxfa/parser/cxfa_startnode.h"
+#include "xfa/fxfa/parser/cxfa_startpage.h"
+#include "xfa/fxfa/parser/cxfa_stipple.h"
+#include "xfa/fxfa/parser/cxfa_stroke.h"
 #include "xfa/fxfa/parser/cxfa_subform.h"
+#include "xfa/fxfa/parser/cxfa_subformset.h"
+#include "xfa/fxfa/parser/cxfa_subjectdn.h"
+#include "xfa/fxfa/parser/cxfa_subjectdns.h"
+#include "xfa/fxfa/parser/cxfa_submit.h"
+#include "xfa/fxfa/parser/cxfa_submitformat.h"
+#include "xfa/fxfa/parser/cxfa_submiturl.h"
+#include "xfa/fxfa/parser/cxfa_subsetbelow.h"
+#include "xfa/fxfa/parser/cxfa_suppressbanner.h"
+#include "xfa/fxfa/parser/cxfa_tagged.h"
+#include "xfa/fxfa/parser/cxfa_template.h"
+#include "xfa/fxfa/parser/cxfa_templatecache.h"
+#include "xfa/fxfa/parser/cxfa_text.h"
+#include "xfa/fxfa/parser/cxfa_textedit.h"
+#include "xfa/fxfa/parser/cxfa_threshold.h"
+#include "xfa/fxfa/parser/cxfa_time.h"
+#include "xfa/fxfa/parser/cxfa_timepattern.h"
+#include "xfa/fxfa/parser/cxfa_timepatterns.h"
+#include "xfa/fxfa/parser/cxfa_timestamp.h"
+#include "xfa/fxfa/parser/cxfa_to.h"
+#include "xfa/fxfa/parser/cxfa_tooltip.h"
+#include "xfa/fxfa/parser/cxfa_trace.h"
+#include "xfa/fxfa/parser/cxfa_transform.h"
+#include "xfa/fxfa/parser/cxfa_traversal.h"
+#include "xfa/fxfa/parser/cxfa_traverse.h"
 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
+#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
+#include "xfa/fxfa/parser/cxfa_type.h"
+#include "xfa/fxfa/parser/cxfa_typeface.h"
+#include "xfa/fxfa/parser/cxfa_typefaces.h"
+#include "xfa/fxfa/parser/cxfa_ui.h"
+#include "xfa/fxfa/parser/cxfa_update.h"
+#include "xfa/fxfa/parser/cxfa_uri.h"
+#include "xfa/fxfa/parser/cxfa_user.h"
 #include "xfa/fxfa/parser/cxfa_validate.h"
+#include "xfa/fxfa/parser/cxfa_validateapprovalsignatures.h"
+#include "xfa/fxfa/parser/cxfa_validationmessaging.h"
 #include "xfa/fxfa/parser/cxfa_value.h"
+#include "xfa/fxfa/parser/cxfa_variables.h"
+#include "xfa/fxfa/parser/cxfa_version.h"
+#include "xfa/fxfa/parser/cxfa_versioncontrol.h"
+#include "xfa/fxfa/parser/cxfa_viewerpreferences.h"
+#include "xfa/fxfa/parser/cxfa_webclient.h"
+#include "xfa/fxfa/parser/cxfa_whitespace.h"
+#include "xfa/fxfa/parser/cxfa_window.h"
+#include "xfa/fxfa/parser/cxfa_wsdladdress.h"
+#include "xfa/fxfa/parser/cxfa_wsdlconnection.h"
+#include "xfa/fxfa/parser/cxfa_xdc.h"
+#include "xfa/fxfa/parser/cxfa_xdp.h"
+#include "xfa/fxfa/parser/cxfa_xfa.h"
+#include "xfa/fxfa/parser/cxfa_xmlconnection.h"
+#include "xfa/fxfa/parser/cxfa_xsdconnection.h"
+#include "xfa/fxfa/parser/cxfa_xsl.h"
+#include "xfa/fxfa/parser/cxfa_zpl.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
+class CXFA_FieldLayoutData;
+class CXFA_ImageEditData;
+class CXFA_ImageLayoutData;
+class CXFA_TextEditData;
+class CXFA_TextLayoutData;
+
 namespace {
 
 constexpr uint8_t kMaxExecuteRecursion = 2;
 
+constexpr uint8_t g_inv_base64[128] = {
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255,
+    255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
+    255, 255, 255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
+    25,  255, 255, 255, 255, 255, 255, 26,  27,  28,  29,  30,  31,  32,  33,
+    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
+    49,  50,  51,  255, 255, 255, 255, 255,
+};
+
+inline uint8_t GetInvBase64(uint8_t x) {
+  return (x & 128) == 0 ? g_inv_base64[x] : 255;
+}
+
+std::vector<uint8_t> XFA_RemoveBase64Whitespace(
+    pdfium::span<const uint8_t> spStr) {
+  std::vector<uint8_t> result;
+  result.reserve(spStr.size());
+  for (uint8_t ch : spStr) {
+    if (GetInvBase64(ch) != 255 || ch == '=')
+      result.push_back(ch);
+  }
+  return result;
+}
+
+std::vector<uint8_t> XFA_Base64Decode(const ByteString& bsStr) {
+  std::vector<uint8_t> result;
+  if (bsStr.IsEmpty())
+    return result;
+
+  std::vector<uint8_t> buffer = XFA_RemoveBase64Whitespace(bsStr.raw_span());
+  result.reserve(3 * (buffer.size() / 4));
+
+  uint32_t dwLimb = 0;
+  for (size_t i = 0; i + 3 < buffer.size(); i += 4) {
+    if (buffer[i] == '=' || buffer[i + 1] == '=' || buffer[i + 2] == '=' ||
+        buffer[i + 3] == '=') {
+      if (buffer[i] == '=' || buffer[i + 1] == '=') {
+        break;
+      }
+      if (buffer[i + 2] == '=') {
+        dwLimb = ((uint32_t)g_inv_base64[buffer[i]] << 6) |
+                 ((uint32_t)g_inv_base64[buffer[i + 1]]);
+        result.push_back((uint8_t)(dwLimb >> 4) & 0xFF);
+      } else {
+        dwLimb = ((uint32_t)g_inv_base64[buffer[i]] << 12) |
+                 ((uint32_t)g_inv_base64[buffer[i + 1]] << 6) |
+                 ((uint32_t)g_inv_base64[buffer[i + 2]]);
+        result.push_back((uint8_t)(dwLimb >> 10) & 0xFF);
+        result.push_back((uint8_t)(dwLimb >> 2) & 0xFF);
+      }
+    } else {
+      dwLimb = ((uint32_t)g_inv_base64[buffer[i]] << 18) |
+               ((uint32_t)g_inv_base64[buffer[i + 1]] << 12) |
+               ((uint32_t)g_inv_base64[buffer[i + 2]] << 6) |
+               ((uint32_t)g_inv_base64[buffer[i + 3]]);
+      result.push_back((uint8_t)(dwLimb >> 16) & 0xff);
+      result.push_back((uint8_t)(dwLimb >> 8) & 0xff);
+      result.push_back((uint8_t)(dwLimb)&0xff);
+    }
+  }
+  return result;
+}
+
+FXCODEC_IMAGE_TYPE XFA_GetImageType(const WideString& wsType) {
+  WideString wsContentType(wsType);
+  if (wsContentType.EqualsASCIINoCase("image/jpg"))
+    return FXCODEC_IMAGE_JPG;
+
+#ifdef PDF_ENABLE_XFA_BMP
+  if (wsContentType.EqualsASCIINoCase("image/bmp"))
+    return FXCODEC_IMAGE_BMP;
+#endif  // PDF_ENABLE_XFA_BMP
+
+#ifdef PDF_ENABLE_XFA_GIF
+  if (wsContentType.EqualsASCIINoCase("image/gif"))
+    return FXCODEC_IMAGE_GIF;
+#endif  // PDF_ENABLE_XFA_GIF
+
+#ifdef PDF_ENABLE_XFA_PNG
+  if (wsContentType.EqualsASCIINoCase("image/png"))
+    return FXCODEC_IMAGE_PNG;
+#endif  // PDF_ENABLE_XFA_PNG
+
+#ifdef PDF_ENABLE_XFA_TIFF
+  if (wsContentType.EqualsASCII("image/tif"))
+    return FXCODEC_IMAGE_TIFF;
+#endif  // PDF_ENABLE_XFA_TIFF
+
+  return FXCODEC_IMAGE_UNKNOWN;
+}
+
+RetainPtr<CFX_DIBitmap> XFA_LoadImageData(CXFA_FFDoc* pDoc,
+                                          CXFA_Image* pImage,
+                                          bool& bNameImage,
+                                          int32_t& iImageXDpi,
+                                          int32_t& iImageYDpi) {
+  WideString wsHref = pImage->GetHref();
+  WideString wsImage = pImage->GetContent();
+  if (wsHref.IsEmpty() && wsImage.IsEmpty())
+    return nullptr;
+
+  FXCODEC_IMAGE_TYPE type = XFA_GetImageType(pImage->GetContentType());
+  ByteString bsData;  // Must outlive |pImageFileRead|.
+  std::vector<uint8_t> buffer;  // Must outlive |pImageFileRead|.
+  RetainPtr<IFX_SeekableReadStream> pImageFileRead;
+  if (wsImage.GetLength() > 0) {
+    XFA_AttributeValue iEncoding = pImage->GetTransferEncoding();
+    if (iEncoding == XFA_AttributeValue::Base64) {
+      bsData = wsImage.ToUTF8();
+      buffer = XFA_Base64Decode(bsData);
+      if (!buffer.empty())
+        pImageFileRead = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(buffer);
+    } else {
+      bsData = wsImage.ToDefANSI();
+      pImageFileRead =
+          pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(bsData.raw_span());
+    }
+  } else {
+    WideString wsURL = wsHref;
+    if (!(wsURL.First(7).EqualsASCII("http://") ||
+          wsURL.First(6).EqualsASCII("ftp://"))) {
+      RetainPtr<CFX_DIBitmap> pBitmap =
+          pDoc->GetPDFNamedImage(wsURL.AsStringView(), iImageXDpi, iImageYDpi);
+      if (pBitmap) {
+        bNameImage = true;
+        return pBitmap;
+      }
+    }
+    pImageFileRead = pDoc->GetDocEnvironment()->OpenLinkedFile(pDoc, wsURL);
+  }
+  if (!pImageFileRead)
+    return nullptr;
+
+  bNameImage = false;
+  RetainPtr<CFX_DIBitmap> pBitmap =
+      XFA_LoadImageFromBuffer(pImageFileRead, type, iImageXDpi, iImageYDpi);
+  return pBitmap;
+}
+
+bool SplitDateTime(const WideString& wsDateTime,
+                   WideString& wsDate,
+                   WideString& wsTime) {
+  wsDate.clear();
+  wsTime.clear();
+  if (wsDateTime.IsEmpty())
+    return false;
+
+  auto nSplitIndex = wsDateTime.Find('T');
+  if (!nSplitIndex.has_value())
+    nSplitIndex = wsDateTime.Find(' ');
+  if (!nSplitIndex.has_value())
+    return false;
+
+  wsDate = wsDateTime.First(nSplitIndex.value());
+  if (!wsDate.IsEmpty()) {
+    if (!std::any_of(wsDate.begin(), wsDate.end(),
+                     [](wchar_t c) { return FXSYS_IsDecimalDigit(c); })) {
+      return false;
+    }
+  }
+  wsTime = wsDateTime.Last(wsDateTime.GetLength() - nSplitIndex.value() - 1);
+  if (!wsTime.IsEmpty()) {
+    if (!std::any_of(wsTime.begin(), wsTime.end(),
+                     [](wchar_t c) { return FXSYS_IsDecimalDigit(c); })) {
+      return false;
+    }
+  }
+  return true;
+}
+
 std::vector<CXFA_Node*> NodesSortedByDocumentIdx(
     const std::set<CXFA_Node*>& rgNodeSet) {
   if (rgNodeSet.empty())
@@ -141,8 +623,8 @@
           pBeforeNode = pLastNode->GetNextSibling();
         }
         for (auto* pCurNode : rgNodeArray1) {
-          pParentNode->RemoveChild(pCurNode, true);
-          pParentNode->InsertChild(pCurNode, pBeforeNode);
+          pParentNode->RemoveChildAndNotify(pCurNode, true);
+          pParentNode->InsertChildAndNotify(pCurNode, pBeforeNode);
         }
       }
     }
@@ -150,84 +632,336 @@
   }
 }
 
+float GetEdgeThickness(const std::vector<CXFA_Stroke*>& strokes,
+                       bool b3DStyle,
+                       int32_t nIndex) {
+  float fThickness = 0.0f;
+  CXFA_Stroke* stroke = strokes[nIndex * 2 + 1];
+  if (stroke->IsVisible()) {
+    if (nIndex == 0)
+      fThickness += 2.5f;
+
+    fThickness += stroke->GetThickness() * (b3DStyle ? 4 : 2);
+  }
+  return fThickness;
+}
+
+WideString FormatNumStr(const WideString& wsValue, LocaleIface* pLocale) {
+  if (wsValue.IsEmpty())
+    return WideString();
+
+  WideString wsSrcNum = wsValue;
+  WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
+  bool bNeg = false;
+  if (wsSrcNum[0] == '-') {
+    bNeg = true;
+    wsSrcNum.Delete(0, 1);
+  }
+
+  auto dot_index = wsSrcNum.Find('.');
+  dot_index = !dot_index.has_value() ? wsSrcNum.GetLength() : dot_index;
+
+  if (dot_index.value() < 1)
+    return WideString();
+
+  size_t nPos = dot_index.value() % 3;
+  WideString wsOutput;
+  for (size_t i = 0; i < dot_index.value(); i++) {
+    if (i % 3 == nPos && i != 0)
+      wsOutput += wsGroupSymbol;
+
+    wsOutput += wsSrcNum[i];
+  }
+  if (dot_index.value() < wsSrcNum.GetLength()) {
+    wsOutput += pLocale->GetDecimalSymbol();
+    wsOutput += wsSrcNum.Last(wsSrcNum.GetLength() - dot_index.value() - 1);
+  }
+  if (bNeg)
+    return pLocale->GetMinusSymbol() + wsOutput;
+
+  return wsOutput;
+}
+
+CXFA_Node* FindFirstSiblingNamedInList(CXFA_Node* parent,
+                                       uint32_t dwNameHash,
+                                       uint32_t dwFilter);
+CXFA_Node* FindFirstSiblingOfClassInList(CXFA_Node* parent,
+                                         XFA_Element element,
+                                         uint32_t dwFilter);
+
+CXFA_Node* FindFirstSiblingNamed(CXFA_Node* parent, uint32_t dwNameHash) {
+  CXFA_Node* result = FindFirstSiblingNamedInList(parent, dwNameHash,
+                                                  XFA_NODEFILTER_Properties);
+  if (result)
+    return result;
+
+  return FindFirstSiblingNamedInList(parent, dwNameHash,
+                                     XFA_NODEFILTER_Children);
+}
+
+CXFA_Node* FindFirstSiblingNamedInList(CXFA_Node* parent,
+                                       uint32_t dwNameHash,
+                                       uint32_t dwFilter) {
+  for (CXFA_Node* child : parent->GetNodeListWithFilter(dwFilter)) {
+    if (child->GetNameHash() == dwNameHash)
+      return child;
+
+    CXFA_Node* result = FindFirstSiblingNamed(child, dwNameHash);
+    if (result)
+      return result;
+  }
+  return nullptr;
+}
+
+CXFA_Node* FindFirstSiblingOfClass(CXFA_Node* parent, XFA_Element element) {
+  CXFA_Node* result =
+      FindFirstSiblingOfClassInList(parent, element, XFA_NODEFILTER_Properties);
+  if (result)
+    return result;
+
+  return FindFirstSiblingOfClassInList(parent, element,
+                                       XFA_NODEFILTER_Children);
+}
+
+CXFA_Node* FindFirstSiblingOfClassInList(CXFA_Node* parent,
+                                         XFA_Element element,
+                                         uint32_t dwFilter) {
+  for (CXFA_Node* child : parent->GetNodeListWithFilter(dwFilter)) {
+    if (child->GetElementType() == element)
+      return child;
+
+    CXFA_Node* result = FindFirstSiblingOfClass(child, element);
+    if (result)
+      return result;
+  }
+  return nullptr;
+}
+
+WideString GetNameExpressionSinglePath(CXFA_Node* pNode) {
+  const bool bIsProperty = pNode->IsProperty();
+  const bool bIsClassIndex =
+      pNode->IsUnnamed() ||
+      (bIsProperty && pNode->GetElementType() != XFA_Element::PageSet);
+  const wchar_t* pszFormat;
+  WideString ws;
+  if (bIsClassIndex) {
+    pszFormat = L"#%ls[%zu]";
+    ws = WideString::FromASCII(pNode->GetClassName());
+  } else {
+    pszFormat = L"%ls[%zu]";
+    ws = pNode->JSObject()->GetCData(XFA_Attribute::Name);
+    ws.Replace(L".", L"\\.");
+  }
+
+  return WideString::Format(pszFormat, ws.c_str(),
+                            pNode->GetIndex(bIsProperty, bIsClassIndex));
+}
+
+void TraverseSiblings(CXFA_Node* parent,
+                      uint32_t dwNameHash,
+                      std::vector<CXFA_Node*>* pSiblings,
+                      bool bIsClassName,
+                      bool bIsFindProperty) {
+  ASSERT(parent);
+  ASSERT(pSiblings);
+
+  if (bIsFindProperty) {
+    for (CXFA_Node* child :
+         parent->GetNodeListWithFilter(XFA_NODEFILTER_Properties)) {
+      if (bIsClassName) {
+        if (child->GetClassHashCode() == dwNameHash)
+          pSiblings->push_back(child);
+      } else {
+        if (child->GetNameHash() == dwNameHash) {
+          if (child->GetElementType() != XFA_Element::PageSet &&
+              child->GetElementType() != XFA_Element::Extras &&
+              child->GetElementType() != XFA_Element::Items) {
+            pSiblings->push_back(child);
+          }
+        }
+      }
+      if (child->IsUnnamed() &&
+          child->GetElementType() == XFA_Element::PageSet) {
+        TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName, false);
+      }
+    }
+    if (!pSiblings->empty())
+      return;
+  }
+  for (CXFA_Node* child :
+       parent->GetNodeListWithFilter(XFA_NODEFILTER_Children)) {
+    if (child->GetElementType() == XFA_Element::Variables)
+      continue;
+
+    if (bIsClassName) {
+      if (child->GetClassHashCode() == dwNameHash)
+        pSiblings->push_back(child);
+    } else {
+      if (child->GetNameHash() == dwNameHash)
+        pSiblings->push_back(child);
+    }
+
+    if (child->IsTransparent() &&
+        child->GetElementType() != XFA_Element::PageSet) {
+      TraverseSiblings(child, dwNameHash, pSiblings, bIsClassName, false);
+    }
+  }
+}
+
 }  // namespace
 
-// static
-WideString CXFA_Node::AttributeEnumToName(XFA_AttributeEnum item) {
-  return g_XFAEnumData[static_cast<int32_t>(item)].pName;
-}
+class CXFA_WidgetLayoutData {
+ public:
+  CXFA_WidgetLayoutData() = default;
+  virtual ~CXFA_WidgetLayoutData() = default;
 
-// static
-Optional<XFA_AttributeEnum> CXFA_Node::NameToAttributeEnum(
-    const WideStringView& name) {
-  if (name.IsEmpty())
-    return {};
+  virtual CXFA_FieldLayoutData* AsFieldLayoutData() { return nullptr; }
+  virtual CXFA_ImageLayoutData* AsImageLayoutData() { return nullptr; }
+  virtual CXFA_TextLayoutData* AsTextLayoutData() { return nullptr; }
 
-  auto* it = std::lower_bound(g_XFAEnumData, g_XFAEnumData + g_iXFAEnumCount,
-                              FX_HashCode_GetW(name, false),
-                              [](const XFA_AttributeEnumInfo& arg,
-                                 uint32_t hash) { return arg.uHash < hash; });
-  if (it != g_XFAEnumData + g_iXFAEnumCount && name == it->pName)
-    return {it->eName};
-  return {};
-}
+  float m_fWidgetHeight = -1.0f;
+};
+
+class CXFA_TextLayoutData final : public CXFA_WidgetLayoutData {
+ public:
+  CXFA_TextLayoutData() = default;
+  ~CXFA_TextLayoutData() override = default;
+
+  CXFA_TextLayoutData* AsTextLayoutData() override { return this; }
+
+  CXFA_TextLayout* GetTextLayout() const { return m_pTextLayout.get(); }
+  CXFA_TextProvider* GetTextProvider() const { return m_pTextProvider.get(); }
+
+  void LoadText(CXFA_FFDoc* doc, CXFA_Node* pNode) {
+    if (m_pTextLayout)
+      return;
+
+    m_pTextProvider =
+        pdfium::MakeUnique<CXFA_TextProvider>(pNode, XFA_TEXTPROVIDERTYPE_Text);
+    m_pTextLayout =
+        pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pTextProvider.get());
+  }
+
+ private:
+  std::unique_ptr<CXFA_TextLayout> m_pTextLayout;
+  std::unique_ptr<CXFA_TextProvider> m_pTextProvider;
+};
+
+class CXFA_ImageLayoutData final : public CXFA_WidgetLayoutData {
+ public:
+  CXFA_ImageLayoutData() = default;
+  ~CXFA_ImageLayoutData() override = default;
+
+  CXFA_ImageLayoutData* AsImageLayoutData() override { return this; }
+
+  bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) {
+    if (m_pDIBitmap)
+      return true;
+
+    CXFA_Value* value = pNode->GetFormValueIfExists();
+    if (!value)
+      return false;
+
+    CXFA_Image* image = value->GetImageIfExists();
+    if (!image)
+      return false;
+
+    pNode->SetImageImage(XFA_LoadImageData(doc, image, m_bNamedImage,
+                                           m_iImageXDpi, m_iImageYDpi));
+    return !!m_pDIBitmap;
+  }
+
+  bool m_bNamedImage = false;
+  int32_t m_iImageXDpi = 0;
+  int32_t m_iImageYDpi = 0;
+  RetainPtr<CFX_DIBitmap> m_pDIBitmap;
+};
+
+class CXFA_FieldLayoutData : public CXFA_WidgetLayoutData {
+ public:
+  CXFA_FieldLayoutData() = default;
+  ~CXFA_FieldLayoutData() override = default;
+
+  CXFA_FieldLayoutData* AsFieldLayoutData() override { return this; }
+
+  virtual CXFA_ImageEditData* AsImageEditData() { return nullptr; }
+  virtual CXFA_TextEditData* AsTextEditData() { return nullptr; }
+
+  bool LoadCaption(CXFA_FFDoc* doc, CXFA_Node* pNode) {
+    if (m_pCapTextLayout)
+      return true;
+    CXFA_Caption* caption = pNode->GetCaptionIfExists();
+    if (!caption || caption->IsHidden())
+      return false;
+
+    m_pCapTextProvider = pdfium::MakeUnique<CXFA_TextProvider>(
+        pNode, XFA_TEXTPROVIDERTYPE_Caption);
+    m_pCapTextLayout =
+        pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pCapTextProvider.get());
+    return true;
+  }
+
+  std::unique_ptr<CXFA_TextLayout> m_pCapTextLayout;
+  std::unique_ptr<CXFA_TextProvider> m_pCapTextProvider;
+  std::unique_ptr<CFDE_TextOut> m_pTextOut;
+  std::vector<float> m_FieldSplitArray;
+};
+
+class CXFA_TextEditData final : public CXFA_FieldLayoutData {
+ public:
+  CXFA_TextEditData() = default;
+  ~CXFA_TextEditData() override = default;
+
+  CXFA_TextEditData* AsTextEditData() override { return this; }
+};
+
+class CXFA_ImageEditData final : public CXFA_FieldLayoutData {
+ public:
+  CXFA_ImageEditData() = default;
+  ~CXFA_ImageEditData() override = default;
+
+  CXFA_ImageEditData* AsImageEditData() override { return this; }
+
+  bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) {
+    if (m_pDIBitmap)
+      return true;
+
+    CXFA_Value* value = pNode->GetFormValueIfExists();
+    if (!value)
+      return false;
+
+    CXFA_Image* image = value->GetImageIfExists();
+    if (!image)
+      return false;
+
+    pNode->SetImageEditImage(XFA_LoadImageData(doc, image, m_bNamedImage,
+                                               m_iImageXDpi, m_iImageYDpi));
+    return !!m_pDIBitmap;
+  }
+
+  bool m_bNamedImage = false;
+  int32_t m_iImageXDpi = 0;
+  int32_t m_iImageYDpi = 0;
+  RetainPtr<CFX_DIBitmap> m_pDIBitmap;
+};
 
 CXFA_Node::CXFA_Node(CXFA_Document* pDoc,
                      XFA_PacketType ePacket,
                      uint32_t validPackets,
                      XFA_ObjectType oType,
                      XFA_Element eType,
-                     const PropertyData* properties,
-                     const AttributeData* attributes,
-                     const WideStringView& elementName,
-                     std::unique_ptr<CJX_Object> js_node)
-    : CXFA_Object(pDoc, oType, eType, elementName, std::move(js_node)),
+                     pdfium::span<const PropertyData> properties,
+                     pdfium::span<const AttributeData> attributes,
+                     std::unique_ptr<CJX_Object> js_object)
+    : CXFA_Object(pDoc, oType, eType, std::move(js_object)),
       m_Properties(properties),
       m_Attributes(attributes),
       m_ValidPackets(validPackets),
-      m_pNext(nullptr),
-      m_pChild(nullptr),
-      m_pLastChild(nullptr),
-      m_pParent(nullptr),
-      m_pXMLNode(nullptr),
-      m_ePacket(ePacket),
-      m_uNodeFlags(XFA_NodeFlag_None),
-      m_dwNameHash(0),
-      m_pAuxNode(nullptr) {
+      m_ePacket(ePacket) {
   ASSERT(m_pDocument);
 }
 
-CXFA_Node::CXFA_Node(CXFA_Document* pDoc,
-                     XFA_PacketType ePacket,
-                     uint32_t validPackets,
-                     XFA_ObjectType oType,
-                     XFA_Element eType,
-                     const PropertyData* properties,
-                     const AttributeData* attributes,
-                     const WideStringView& elementName)
-    : CXFA_Node(pDoc,
-                ePacket,
-                validPackets,
-                oType,
-                eType,
-                properties,
-                attributes,
-                elementName,
-                pdfium::MakeUnique<CJX_Node>(this)) {}
-
-CXFA_Node::~CXFA_Node() {
-  ASSERT(!m_pParent);
-
-  CXFA_Node* pNode = m_pChild;
-  while (pNode) {
-    CXFA_Node* pNext = pNode->m_pNext;
-    pNode->m_pParent = nullptr;
-    delete pNode;
-    pNode = pNext;
-  }
-  if (m_pXMLNode && IsOwnXMLNode())
-    delete m_pXMLNode;
-}
+CXFA_Node::~CXFA_Node() = default;
 
 CXFA_Node* CXFA_Node::Clone(bool bRecursive) {
   CXFA_Node* pClone = m_pDocument->CreateNode(m_ePacket, m_elementType);
@@ -237,80 +971,69 @@
   JSObject()->MergeAllData(pClone);
   pClone->UpdateNameHash();
   if (IsNeedSavingXMLNode()) {
-    std::unique_ptr<CFX_XMLNode> pCloneXML;
+    CFX_XMLNode* pCloneXML;
     if (IsAttributeInXML()) {
       WideString wsName = JSObject()
                               ->TryAttribute(XFA_Attribute::Name, false)
                               .value_or(WideString());
-      auto pCloneXMLElement = pdfium::MakeUnique<CFX_XMLElement>(wsName);
-      WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
-      if (!wsValue.IsEmpty())
-        pCloneXMLElement->SetTextData(WideString(wsValue));
+      auto* pCloneXMLElement =
+          GetXMLDocument()->CreateNode<CFX_XMLElement>(wsName);
 
-      pCloneXML.reset(pCloneXMLElement.release());
+      WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
+      if (!wsValue.IsEmpty()) {
+        auto* text = GetXMLDocument()->CreateNode<CFX_XMLText>(wsValue);
+        pCloneXMLElement->AppendLastChild(text);
+      }
+
+      pCloneXML = pCloneXMLElement;
       pClone->JSObject()->SetEnum(XFA_Attribute::Contains,
-                                  XFA_AttributeEnum::Unknown, false);
+                                  XFA_AttributeValue::Unknown, false);
     } else {
-      pCloneXML = m_pXMLNode->Clone();
+      pCloneXML = xml_node_->Clone(GetXMLDocument());
     }
-    pClone->SetXMLMappingNode(pCloneXML.release());
-    pClone->SetFlag(XFA_NodeFlag_OwnXMLNode, false);
+    pClone->SetXMLMappingNode(pCloneXML);
   }
   if (bRecursive) {
     for (CXFA_Node* pChild = GetFirstChild(); pChild;
          pChild = pChild->GetNextSibling()) {
-      pClone->InsertChild(pChild->Clone(bRecursive), nullptr);
+      pClone->InsertChildAndNotify(pChild->Clone(bRecursive), nullptr);
     }
   }
-  pClone->SetFlag(XFA_NodeFlag_Initialized, true);
+  pClone->SetFlagAndNotify(XFA_NodeFlag_Initialized);
   pClone->SetBindingNode(nullptr);
   return pClone;
 }
 
-CXFA_Node* CXFA_Node::GetPrevSibling() const {
-  if (!m_pParent || m_pParent->m_pChild == this)
-    return nullptr;
-
-  for (CXFA_Node* pNode = m_pParent->m_pChild; pNode; pNode = pNode->m_pNext) {
-    if (pNode->m_pNext == this)
+CXFA_Node* CXFA_Node::GetNextContainerSibling() const {
+  for (auto* pNode = GetNextSibling(); pNode; pNode = pNode->GetNextSibling()) {
+    if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
       return pNode;
   }
   return nullptr;
 }
 
-CXFA_Node* CXFA_Node::GetNextContainerSibling() const {
-  CXFA_Node* pNode = m_pNext;
-  while (pNode && pNode->GetObjectType() != XFA_ObjectType::ContainerNode)
-    pNode = pNode->m_pNext;
-  return pNode;
-}
-
 CXFA_Node* CXFA_Node::GetPrevContainerSibling() const {
-  if (!m_pParent || m_pParent->m_pChild == this)
-    return nullptr;
-
-  CXFA_Node* container = nullptr;
-  for (CXFA_Node* pNode = m_pParent->m_pChild; pNode; pNode = pNode->m_pNext) {
+  for (auto* pNode = GetPrevSibling(); pNode; pNode = pNode->GetPrevSibling()) {
     if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
-      container = pNode;
-    if (pNode->m_pNext == this)
-      return container;
+      return pNode;
   }
   return nullptr;
 }
 
 CXFA_Node* CXFA_Node::GetFirstContainerChild() const {
-  CXFA_Node* pNode = m_pChild;
-  while (pNode && pNode->GetObjectType() != XFA_ObjectType::ContainerNode)
-    pNode = pNode->m_pNext;
-  return pNode;
+  for (auto* pNode = GetFirstChild(); pNode; pNode = pNode->GetNextSibling()) {
+    if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
+      return pNode;
+  }
+  return nullptr;
 }
 
 CXFA_Node* CXFA_Node::GetContainerParent() const {
-  CXFA_Node* pNode = m_pParent;
-  while (pNode && pNode->GetObjectType() != XFA_ObjectType::ContainerNode)
-    pNode = pNode->m_pParent;
-  return pNode;
+  for (auto* pNode = GetParent(); pNode; pNode = pNode->GetParent()) {
+    if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
+      return pNode;
+  }
+  return nullptr;
 }
 
 bool CXFA_Node::IsValidInPacket(XFA_PacketType packet) const {
@@ -319,15 +1042,10 @@
 
 const CXFA_Node::PropertyData* CXFA_Node::GetPropertyData(
     XFA_Element property) const {
-  if (m_Properties == nullptr)
-    return nullptr;
-
-  for (size_t i = 0;; ++i) {
-    const PropertyData* data = m_Properties + i;
-    if (data->property == XFA_Element::Unknown)
-      break;
-    if (data->property == property)
-      return data;
+  ASSERT(property != XFA_Element::Unknown);
+  for (const auto& prop : m_Properties) {
+    if (prop.property == property)
+      return &prop;
   }
   return nullptr;
 }
@@ -346,31 +1064,70 @@
   return data ? data->occurance_count : 0;
 }
 
-Optional<XFA_Element> CXFA_Node::GetFirstPropertyWithFlag(uint8_t flag) {
-  if (m_Properties == nullptr)
-    return {};
+std::pair<CXFA_Node*, int32_t> CXFA_Node::GetProperty(
+    int32_t index,
+    XFA_Element eProperty) const {
+  if (index < 0 || index >= PropertyOccuranceCount(eProperty))
+    return {nullptr, 0};
 
-  for (size_t i = 0;; ++i) {
-    const PropertyData* data = m_Properties + i;
-    if (data->property == XFA_Element::Unknown)
-      break;
-    if (data->flags & flag)
-      return {data->property};
+  int32_t iCount = 0;
+  for (CXFA_Node* pNode = GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
+    if (pNode->GetElementType() == eProperty) {
+      iCount++;
+      if (iCount > index)
+        return {pNode, iCount};
+    }
+  }
+  return {nullptr, iCount};
+}
+
+CXFA_Node* CXFA_Node::GetOrCreateProperty(int32_t index,
+                                          XFA_Element eProperty) {
+  if (index < 0 || index >= PropertyOccuranceCount(eProperty))
+    return nullptr;
+
+  int32_t iCount = 0;
+  CXFA_Node* node;
+  std::tie(node, iCount) = GetProperty(index, eProperty);
+  if (node)
+    return node;
+
+  if (HasPropertyFlags(eProperty, XFA_PROPERTYFLAG_OneOf)) {
+    for (CXFA_Node* pNode = GetFirstChild(); pNode;
+         pNode = pNode->GetNextSibling()) {
+      if (HasPropertyFlags(pNode->GetElementType(), XFA_PROPERTYFLAG_OneOf)) {
+        return nullptr;
+      }
+    }
+  }
+
+  CXFA_Node* pNewNode = nullptr;
+  for (; iCount <= index; ++iCount) {
+    pNewNode = GetDocument()->CreateNode(GetPacketType(), eProperty);
+    if (!pNewNode)
+      return nullptr;
+
+    InsertChildAndNotify(pNewNode, nullptr);
+    pNewNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+  }
+  return pNewNode;
+}
+
+Optional<XFA_Element> CXFA_Node::GetFirstPropertyWithFlag(uint8_t flag) const {
+  for (const auto& prop : m_Properties) {
+    if (prop.flags & flag)
+      return prop.property;
   }
   return {};
 }
 
 const CXFA_Node::AttributeData* CXFA_Node::GetAttributeData(
     XFA_Attribute attr) const {
-  if (m_Attributes == nullptr)
-    return nullptr;
-
-  for (size_t i = 0;; ++i) {
-    const AttributeData* cur_attr = &m_Attributes[i];
-    if (cur_attr->attribute == XFA_Attribute::Unknown)
-      break;
-    if (cur_attr->attribute == attr)
-      return cur_attr;
+  ASSERT(attr != XFA_Attribute::Unknown);
+  for (const auto& cur_attr : m_Attributes) {
+    if (cur_attr.attribute == attr)
+      return &cur_attr;
   }
   return nullptr;
 }
@@ -379,11 +1136,9 @@
   return !!GetAttributeData(attr);
 }
 
-// Note: This Method assumes that i is a valid index ....
 XFA_Attribute CXFA_Node::GetAttribute(size_t i) const {
-  if (m_Attributes == nullptr)
-    return XFA_Attribute::Unknown;
-  return m_Attributes[i].attribute;
+  return i < m_Attributes.size() ? m_Attributes[i].attribute
+                                 : XFA_Attribute::Unknown;
 }
 
 XFA_AttributeType CXFA_Node::GetAttributeType(XFA_Attribute type) const {
@@ -391,33 +1146,35 @@
   return data ? data->type : XFA_AttributeType::CData;
 }
 
-std::vector<CXFA_Node*> CXFA_Node::GetNodeList(uint32_t dwTypeFilter,
-                                               XFA_Element eTypeFilter) {
-  if (eTypeFilter != XFA_Element::Unknown) {
-    std::vector<CXFA_Node*> nodes;
-    for (CXFA_Node* pChild = m_pChild; pChild; pChild = pChild->m_pNext) {
-      if (pChild->GetElementType() == eTypeFilter)
-        nodes.push_back(pChild);
-    }
-    return nodes;
+std::vector<CXFA_Node*> CXFA_Node::GetNodeListForType(XFA_Element eTypeFilter) {
+  std::vector<CXFA_Node*> nodes;
+  for (CXFA_Node* pChild = GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    if (pChild->GetElementType() == eTypeFilter)
+      nodes.push_back(pChild);
   }
+  return nodes;
+}
 
+std::vector<CXFA_Node*> CXFA_Node::GetNodeListWithFilter(
+    uint32_t dwTypeFilter) {
+  std::vector<CXFA_Node*> nodes;
   if (dwTypeFilter == (XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties)) {
-    std::vector<CXFA_Node*> nodes;
-    for (CXFA_Node* pChild = m_pChild; pChild; pChild = pChild->m_pNext)
+    for (CXFA_Node* pChild = GetFirstChild(); pChild;
+         pChild = pChild->GetNextSibling())
       nodes.push_back(pChild);
     return nodes;
   }
 
   if (dwTypeFilter == 0)
-    return std::vector<CXFA_Node*>();
+    return nodes;
 
   bool bFilterChildren = !!(dwTypeFilter & XFA_NODEFILTER_Children);
   bool bFilterProperties = !!(dwTypeFilter & XFA_NODEFILTER_Properties);
   bool bFilterOneOfProperties = !!(dwTypeFilter & XFA_NODEFILTER_OneOfProperty);
-  std::vector<CXFA_Node*> nodes;
-  for (CXFA_Node* pChild = m_pChild; pChild; pChild = pChild->m_pNext) {
-    if (!HasProperty(pChild->GetElementType())) {
+  for (CXFA_Node* pChild = GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    if (HasProperty(pChild->GetElementType())) {
       if (bFilterProperties) {
         nodes.push_back(pChild);
       } else if (bFilterOneOfProperties &&
@@ -436,18 +1193,17 @@
 
   if (!bFilterOneOfProperties || !nodes.empty())
     return nodes;
-  if (m_Properties == nullptr)
-    return nodes;
 
   Optional<XFA_Element> property =
       GetFirstPropertyWithFlag(XFA_PROPERTYFLAG_DefaultOneOf);
-  if (!property)
+  if (!property.has_value())
     return nodes;
 
-  CXFA_Node* pNewNode = m_pDocument->CreateNode(GetPacketType(), *property);
+  CXFA_Node* pNewNode =
+      m_pDocument->CreateNode(GetPacketType(), property.value());
   if (pNewNode) {
-    InsertChild(pNewNode, nullptr);
-    pNewNode->SetFlag(XFA_NodeFlag_Initialized, true);
+    InsertChildAndNotify(pNewNode, nullptr);
+    pNewNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
     nodes.push_back(pNewNode);
   }
   return nodes;
@@ -455,7 +1211,10 @@
 
 CXFA_Node* CXFA_Node::CreateSamePacketNode(XFA_Element eType) {
   CXFA_Node* pNode = m_pDocument->CreateNode(m_ePacket, eType);
-  pNode->SetFlag(XFA_NodeFlag_Initialized, true);
+  if (!pNode)
+    return nullptr;
+
+  pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
   return pNode;
 }
 
@@ -472,10 +1231,11 @@
   if (bRecursive) {
     for (CXFA_Node* pChild = GetFirstChild(); pChild;
          pChild = pChild->GetNextSibling()) {
-      pClone->InsertChild(pChild->CloneTemplateToForm(bRecursive), nullptr);
+      pClone->InsertChildAndNotify(pChild->CloneTemplateToForm(bRecursive),
+                                   nullptr);
     }
   }
-  pClone->SetFlag(XFA_NodeFlag_Initialized, true);
+  pClone->SetFlagAndNotify(XFA_NodeFlag_Initialized);
   return pClone;
 }
 
@@ -492,17 +1252,13 @@
   return GetBindingNode();
 }
 
-std::vector<UnownedPtr<CXFA_Node>>* CXFA_Node::GetBindItems() {
-  return GetBindingNodes();
-}
-
 int32_t CXFA_Node::AddBindItem(CXFA_Node* pFormNode) {
   ASSERT(pFormNode);
 
   if (BindsFormItems()) {
     bool found = false;
-    for (auto& v : binding_nodes_) {
-      if (v.Get() == pFormNode) {
+    for (auto* v : binding_nodes_) {
+      if (v == pFormNode) {
         found = true;
         break;
       }
@@ -520,10 +1276,10 @@
   if (pOldFormItem == pFormNode)
     return 1;
 
-  std::vector<UnownedPtr<CXFA_Node>> items;
-  items.emplace_back(pOldFormItem);
-  items.emplace_back(pFormNode);
-  SetBindingNodes(std::move(items));
+  std::vector<CXFA_Node*> items;
+  items.push_back(pOldFormItem);
+  items.push_back(pFormNode);
+  binding_nodes_ = std::move(items);
 
   m_uNodeFlags |= XFA_NodeFlag_BindFormItems;
   return 2;
@@ -531,10 +1287,8 @@
 
 int32_t CXFA_Node::RemoveBindItem(CXFA_Node* pFormNode) {
   if (BindsFormItems()) {
-    auto it = std::find_if(binding_nodes_.begin(), binding_nodes_.end(),
-                           [&pFormNode](const UnownedPtr<CXFA_Node>& node) {
-                             return node.Get() == pFormNode;
-                           });
+    auto it =
+        std::find(binding_nodes_.begin(), binding_nodes_.end(), pFormNode);
     if (it != binding_nodes_.end())
       binding_nodes_.erase(it);
 
@@ -553,11 +1307,11 @@
   return 0;
 }
 
-bool CXFA_Node::HasBindItem() {
+bool CXFA_Node::HasBindItem() const {
   return GetPacketType() == XFA_PacketType::Datasets && GetBindingNode();
 }
 
-CXFA_WidgetAcc* CXFA_Node::GetContainerWidgetAcc() {
+CXFA_Node* CXFA_Node::GetContainerNode() {
   if (GetPacketType() != XFA_PacketType::Form)
     return nullptr;
   XFA_Element eType = GetElementType();
@@ -568,34 +1322,30 @@
     return nullptr;
 
   if (eType == XFA_Element::Field) {
-    CXFA_WidgetAcc* pFieldWidgetAcc = GetWidgetAcc();
-    if (pFieldWidgetAcc && pFieldWidgetAcc->IsChoiceListMultiSelect())
+    if (IsChoiceListMultiSelect())
       return nullptr;
 
-    WideString wsPicture;
-    if (pFieldWidgetAcc) {
-      wsPicture = pFieldWidgetAcc->GetPictureContent(XFA_VALUEPICTURE_DataBind);
-    }
+    WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
     if (!wsPicture.IsEmpty())
-      return pFieldWidgetAcc;
+      return this;
 
     CXFA_Node* pDataNode = GetBindData();
     if (!pDataNode)
       return nullptr;
-    pFieldWidgetAcc = nullptr;
-    for (const auto& pFormNode : *(pDataNode->GetBindItems())) {
+
+    CXFA_Node* pFieldNode = nullptr;
+    for (auto* pFormNode : pDataNode->GetBindItemsCopy()) {
       if (!pFormNode || pFormNode->HasRemovedChildren())
         continue;
-      pFieldWidgetAcc = pFormNode->GetWidgetAcc();
-      if (pFieldWidgetAcc) {
-        wsPicture =
-            pFieldWidgetAcc->GetPictureContent(XFA_VALUEPICTURE_DataBind);
-      }
+      pFieldNode = pFormNode->IsWidgetReady() ? pFormNode : nullptr;
+      if (pFieldNode)
+        wsPicture = pFieldNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
       if (!wsPicture.IsEmpty())
         break;
-      pFieldWidgetAcc = nullptr;
+
+      pFieldNode = nullptr;
     }
-    return pFieldWidgetAcc;
+    return pFieldNode;
   }
 
   CXFA_Node* pGrandNode = pParentNode ? pParentNode->GetParent() : nullptr;
@@ -611,24 +1361,27 @@
   }
   CXFA_Node* pParentOfValueNode =
       pValueNode ? pValueNode->GetParent() : nullptr;
-  return pParentOfValueNode ? pParentOfValueNode->GetContainerWidgetAcc()
-                            : nullptr;
+  return pParentOfValueNode ? pParentOfValueNode->GetContainerNode() : nullptr;
 }
 
-IFX_Locale* CXFA_Node::GetLocale() {
+LocaleIface* CXFA_Node::GetLocale() {
   Optional<WideString> localeName = GetLocaleName();
-  if (!localeName)
+  if (!localeName.has_value())
     return nullptr;
-  if (localeName.value() == L"ambient")
-    return GetDocument()->GetLocalMgr()->GetDefLocale();
-  return GetDocument()->GetLocalMgr()->GetLocaleByName(localeName.value());
+  if (localeName.value().EqualsASCII("ambient"))
+    return GetDocument()->GetLocaleMgr()->GetDefLocale();
+  return GetDocument()->GetLocaleMgr()->GetLocaleByName(localeName.value());
 }
 
 Optional<WideString> CXFA_Node::GetLocaleName() {
-  CXFA_Node* pForm = GetDocument()->GetXFAObject(XFA_HASHCODE_Form)->AsNode();
+  CXFA_Node* pForm = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form));
+  if (!pForm)
+    return {};
+
   CXFA_Subform* pTopSubform =
       pForm->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
-  ASSERT(pTopSubform);
+  if (!pTopSubform)
+    return {};
 
   CXFA_Node* pLocaleNode = this;
   do {
@@ -641,104 +1394,90 @@
   } while (pLocaleNode && pLocaleNode != pTopSubform);
 
   CXFA_Node* pConfig = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Config));
-  Optional<WideString> localeName = {
-      WideString(GetDocument()->GetLocalMgr()->GetConfigLocaleName(pConfig))};
-  if (localeName && !localeName->IsEmpty())
-    return localeName;
+  WideString wsLocaleName =
+      GetDocument()->GetLocaleMgr()->GetConfigLocaleName(pConfig);
+  if (!wsLocaleName.IsEmpty())
+    return wsLocaleName;
 
   if (pTopSubform) {
-    localeName =
+    Optional<WideString> localeName =
         pTopSubform->JSObject()->TryCData(XFA_Attribute::Locale, false);
     if (localeName)
       return localeName;
   }
 
-  IFX_Locale* pLocale = GetDocument()->GetLocalMgr()->GetDefLocale();
+  LocaleIface* pLocale = GetDocument()->GetLocaleMgr()->GetDefLocale();
   if (!pLocale)
     return {};
 
-  return {pLocale->GetName()};
+  return pLocale->GetName();
 }
 
-XFA_AttributeEnum CXFA_Node::GetIntact() {
+XFA_AttributeValue CXFA_Node::GetIntact() {
   CXFA_Keep* pKeep = GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
-  XFA_AttributeEnum eLayoutType = JSObject()
-                                      ->TryEnum(XFA_Attribute::Layout, true)
-                                      .value_or(XFA_AttributeEnum::Position);
+  auto layout = JSObject()->TryEnum(XFA_Attribute::Layout, true);
+  XFA_AttributeValue eLayoutType =
+      layout.value_or(XFA_AttributeValue::Position);
   if (pKeep) {
-    Optional<XFA_AttributeEnum> intact =
-        pKeep->JSObject()->TryEnum(XFA_Attribute::Intact, false);
-    if (intact) {
-      if (*intact == XFA_AttributeEnum::None &&
-          eLayoutType == XFA_AttributeEnum::Row &&
-          m_pDocument->GetCurVersionMode() < XFA_VERSION_208) {
-        CXFA_Node* pPreviewRow = GetPrevContainerSibling();
-        if (pPreviewRow &&
-            pPreviewRow->JSObject()->GetEnum(XFA_Attribute::Layout) ==
-                XFA_AttributeEnum::Row) {
-          Optional<XFA_AttributeEnum> value =
-              pKeep->JSObject()->TryEnum(XFA_Attribute::Previous, false);
-          if (value && (*value == XFA_AttributeEnum::ContentArea ||
-                        *value == XFA_AttributeEnum::PageArea)) {
-            return XFA_AttributeEnum::ContentArea;
-          }
-
-          CXFA_Keep* pNode =
-              pPreviewRow->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
-          Optional<XFA_AttributeEnum> ret;
-          if (pNode)
-            ret = pNode->JSObject()->TryEnum(XFA_Attribute::Next, false);
-          if (ret && (*ret == XFA_AttributeEnum::ContentArea ||
-                      *ret == XFA_AttributeEnum::PageArea)) {
-            return XFA_AttributeEnum::ContentArea;
-          }
-        }
-      }
+    Optional<XFA_AttributeValue> intact = GetIntactFromKeep(pKeep, eLayoutType);
+    if (intact)
       return *intact;
-    }
   }
 
   switch (GetElementType()) {
     case XFA_Element::Subform:
       switch (eLayoutType) {
-        case XFA_AttributeEnum::Position:
-        case XFA_AttributeEnum::Row:
-          return XFA_AttributeEnum::ContentArea;
+        case XFA_AttributeValue::Position:
+        case XFA_AttributeValue::Row:
+          return XFA_AttributeValue::ContentArea;
         default:
-          return XFA_AttributeEnum::None;
+          return XFA_AttributeValue::None;
       }
     case XFA_Element::Field: {
       CXFA_Node* parent = GetParent();
       if (!parent || parent->GetElementType() == XFA_Element::PageArea)
-        return XFA_AttributeEnum::ContentArea;
-      if (parent->GetIntact() != XFA_AttributeEnum::None)
-        return XFA_AttributeEnum::ContentArea;
+        return XFA_AttributeValue::ContentArea;
+      if (parent->GetIntact() != XFA_AttributeValue::None)
+        return XFA_AttributeValue::ContentArea;
 
-      XFA_AttributeEnum eParLayout = parent->JSObject()
-                                         ->TryEnum(XFA_Attribute::Layout, true)
-                                         .value_or(XFA_AttributeEnum::Position);
-      if (eParLayout == XFA_AttributeEnum::Position ||
-          eParLayout == XFA_AttributeEnum::Row ||
-          eParLayout == XFA_AttributeEnum::Table) {
-        return XFA_AttributeEnum::None;
+      auto value = parent->JSObject()->TryEnum(XFA_Attribute::Layout, true);
+      XFA_AttributeValue eParLayout =
+          value.value_or(XFA_AttributeValue::Position);
+      if (eParLayout == XFA_AttributeValue::Position ||
+          eParLayout == XFA_AttributeValue::Row ||
+          eParLayout == XFA_AttributeValue::Table) {
+        return XFA_AttributeValue::None;
       }
 
       XFA_VERSION version = m_pDocument->GetCurVersionMode();
-      if (eParLayout == XFA_AttributeEnum::Tb && version < XFA_VERSION_208) {
+      if (eParLayout == XFA_AttributeValue::Tb && version < XFA_VERSION_208) {
         Optional<CXFA_Measurement> measureH =
             JSObject()->TryMeasure(XFA_Attribute::H, false);
         if (measureH)
-          return XFA_AttributeEnum::ContentArea;
+          return XFA_AttributeValue::ContentArea;
       }
-      return XFA_AttributeEnum::None;
+      return XFA_AttributeValue::None;
     }
     case XFA_Element::Draw:
-      return XFA_AttributeEnum::ContentArea;
+      return XFA_AttributeValue::ContentArea;
     default:
-      return XFA_AttributeEnum::None;
+      return XFA_AttributeValue::None;
   }
 }
 
+WideString CXFA_Node::GetNameExpression() {
+  WideString wsName = GetNameExpressionSinglePath(this);
+  CXFA_Node* parent = GetParent();
+  while (parent) {
+    WideString wsParent = GetNameExpressionSinglePath(parent);
+    wsParent += L".";
+    wsParent += wsName;
+    wsName = std::move(wsParent);
+    parent = parent->GetParent();
+  }
+  return wsName;
+}
+
 CXFA_Node* CXFA_Node::GetDataDescriptionNode() {
   if (m_ePacket == XFA_PacketType::Datasets)
     return m_pAuxNode;
@@ -777,7 +1516,8 @@
 
 size_t CXFA_Node::CountChildren(XFA_Element eType, bool bOnlyChild) {
   size_t count = 0;
-  for (CXFA_Node* pNode = m_pChild; pNode; pNode = pNode->GetNextSibling()) {
+  for (CXFA_Node* pNode = GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
     if (pNode->GetElementType() != eType && eType != XFA_Element::Unknown)
       continue;
     if (bOnlyChild && HasProperty(pNode->GetElementType()))
@@ -789,9 +1529,10 @@
 
 CXFA_Node* CXFA_Node::GetChildInternal(size_t index,
                                        XFA_Element eType,
-                                       bool bOnlyChild) {
+                                       bool bOnlyChild) const {
   size_t count = 0;
-  for (CXFA_Node* pNode = m_pChild; pNode; pNode = pNode->GetNextSibling()) {
+  for (CXFA_Node* pNode = GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
     if (pNode->GetElementType() != eType && eType != XFA_Element::Unknown)
       continue;
     if (bOnlyChild && HasProperty(pNode->GetElementType()))
@@ -804,179 +1545,77 @@
   return nullptr;
 }
 
-int32_t CXFA_Node::InsertChild(int32_t index, CXFA_Node* pNode) {
-  ASSERT(!pNode->m_pNext);
-  pNode->m_pParent = this;
-  bool ret = m_pDocument->RemovePurgeNode(pNode);
-  ASSERT(ret);
-  (void)ret;  // Avoid unused variable warning.
+void CXFA_Node::InsertChildAndNotify(int32_t index, CXFA_Node* pNode) {
+  InsertChildAndNotify(pNode, GetNthChild(index));
+}
 
-  if (!m_pChild || index == 0) {
-    if (index > 0) {
-      return -1;
-    }
-    pNode->m_pNext = m_pChild;
-    m_pChild = pNode;
-    index = 0;
-  } else if (index < 0) {
-    m_pLastChild->m_pNext = pNode;
-  } else {
-    CXFA_Node* pPrev = m_pChild;
-    int32_t iCount = 0;
-    while (++iCount != index && pPrev->m_pNext) {
-      pPrev = pPrev->m_pNext;
-    }
-    if (index > 0 && index != iCount) {
-      return -1;
-    }
-    pNode->m_pNext = pPrev->m_pNext;
-    pPrev->m_pNext = pNode;
-    index = iCount;
-  }
-  if (!pNode->m_pNext) {
-    m_pLastChild = pNode;
-  }
-  ASSERT(m_pLastChild);
-  ASSERT(!m_pLastChild->m_pNext);
+void CXFA_Node::InsertChildAndNotify(CXFA_Node* pNode, CXFA_Node* pBeforeNode) {
+  CHECK(!pNode->GetParent());
+  CHECK(!pBeforeNode || pBeforeNode->GetParent() == this);
   pNode->ClearFlag(XFA_NodeFlag_HasRemovedChildren);
+  InsertBefore(pNode, pBeforeNode);
+
   CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
   if (pNotify)
     pNotify->OnChildAdded(this);
 
-  if (IsNeedSavingXMLNode() && pNode->m_pXMLNode) {
-    ASSERT(!pNode->m_pXMLNode->GetNodeItem(CFX_XMLNode::Parent));
-    m_pXMLNode->InsertChildNode(pNode->m_pXMLNode, index);
-    pNode->ClearFlag(XFA_NodeFlag_OwnXMLNode);
-  }
-  return index;
+  if (!IsNeedSavingXMLNode() || !pNode->xml_node_)
+    return;
+
+  ASSERT(!pNode->xml_node_->GetParent());
+  xml_node_->InsertBefore(pNode->xml_node_.Get(),
+                          pBeforeNode ? pBeforeNode->xml_node_.Get() : nullptr);
 }
 
-bool CXFA_Node::InsertChild(CXFA_Node* pNode, CXFA_Node* pBeforeNode) {
-  if (!pNode || pNode->m_pParent ||
-      (pBeforeNode && pBeforeNode->m_pParent != this)) {
-    NOTREACHED();
-    return false;
-  }
-  bool ret = m_pDocument->RemovePurgeNode(pNode);
-  ASSERT(ret);
-  (void)ret;  // Avoid unused variable warning.
+void CXFA_Node::RemoveChildAndNotify(CXFA_Node* pNode, bool bNotify) {
+  CHECK(pNode);
+  if (pNode->GetParent() != this)
+    return;
 
-  int32_t nIndex = -1;
-  pNode->m_pParent = this;
-  if (!m_pChild || pBeforeNode == m_pChild) {
-    pNode->m_pNext = m_pChild;
-    m_pChild = pNode;
-    nIndex = 0;
-  } else if (!pBeforeNode) {
-    pNode->m_pNext = m_pLastChild->m_pNext;
-    m_pLastChild->m_pNext = pNode;
-  } else {
-    nIndex = 1;
-    CXFA_Node* pPrev = m_pChild;
-    while (pPrev->m_pNext != pBeforeNode) {
-      pPrev = pPrev->m_pNext;
-      nIndex++;
-    }
-    pNode->m_pNext = pPrev->m_pNext;
-    pPrev->m_pNext = pNode;
-  }
-  if (!pNode->m_pNext) {
-    m_pLastChild = pNode;
-  }
-  ASSERT(m_pLastChild);
-  ASSERT(!m_pLastChild->m_pNext);
-  pNode->ClearFlag(XFA_NodeFlag_HasRemovedChildren);
-  CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
-  if (pNotify)
-    pNotify->OnChildAdded(this);
-
-  if (IsNeedSavingXMLNode() && pNode->m_pXMLNode) {
-    ASSERT(!pNode->m_pXMLNode->GetNodeItem(CFX_XMLNode::Parent));
-    m_pXMLNode->InsertChildNode(pNode->m_pXMLNode, nIndex);
-    pNode->ClearFlag(XFA_NodeFlag_OwnXMLNode);
-  }
-  return true;
-}
-
-CXFA_Node* CXFA_Node::Deprecated_GetPrevSibling() {
-  if (!m_pParent) {
-    return nullptr;
-  }
-  for (CXFA_Node* pSibling = m_pParent->m_pChild; pSibling;
-       pSibling = pSibling->m_pNext) {
-    if (pSibling->m_pNext == this) {
-      return pSibling;
-    }
-  }
-  return nullptr;
-}
-
-bool CXFA_Node::RemoveChild(CXFA_Node* pNode, bool bNotify) {
-  if (!pNode || pNode->m_pParent != this) {
-    NOTREACHED();
-    return false;
-  }
-  if (m_pChild == pNode) {
-    m_pChild = pNode->m_pNext;
-    if (m_pLastChild == pNode) {
-      m_pLastChild = pNode->m_pNext;
-    }
-    pNode->m_pNext = nullptr;
-    pNode->m_pParent = nullptr;
-  } else {
-    CXFA_Node* pPrev = pNode->Deprecated_GetPrevSibling();
-    pPrev->m_pNext = pNode->m_pNext;
-    if (m_pLastChild == pNode) {
-      m_pLastChild = pNode->m_pNext ? pNode->m_pNext : pPrev;
-    }
-    pNode->m_pNext = nullptr;
-    pNode->m_pParent = nullptr;
-  }
-  ASSERT(!m_pLastChild || !m_pLastChild->m_pNext);
+  pNode->SetFlag(XFA_NodeFlag_HasRemovedChildren);
+  TreeNode<CXFA_Node>::RemoveChild(pNode);
   OnRemoved(bNotify);
-  pNode->SetFlag(XFA_NodeFlag_HasRemovedChildren, true);
-  m_pDocument->AddPurgeNode(pNode);
-  if (IsNeedSavingXMLNode() && pNode->m_pXMLNode) {
-    if (pNode->IsAttributeInXML()) {
-      ASSERT(pNode->m_pXMLNode == m_pXMLNode &&
-             m_pXMLNode->GetType() == FX_XMLNODE_Element);
-      if (pNode->m_pXMLNode->GetType() == FX_XMLNODE_Element) {
-        CFX_XMLElement* pXMLElement =
-            static_cast<CFX_XMLElement*>(pNode->m_pXMLNode);
-        WideString wsAttributeName =
-            pNode->JSObject()->GetCData(XFA_Attribute::QualifiedName);
-        pXMLElement->RemoveAttribute(wsAttributeName.c_str());
-      }
 
-      WideString wsName = pNode->JSObject()
-                              ->TryAttribute(XFA_Attribute::Name, false)
-                              .value_or(WideString());
-      CFX_XMLElement* pNewXMLElement = new CFX_XMLElement(wsName);
-      WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
-      if (!wsValue.IsEmpty())
-        pNewXMLElement->SetTextData(WideString(wsValue));
+  if (!IsNeedSavingXMLNode() || !pNode->xml_node_)
+    return;
 
-      pNode->m_pXMLNode = pNewXMLElement;
-      pNode->JSObject()->SetEnum(XFA_Attribute::Contains,
-                                 XFA_AttributeEnum::Unknown, false);
-    } else {
-      m_pXMLNode->RemoveChildNode(pNode->m_pXMLNode);
-    }
-    pNode->SetFlag(XFA_NodeFlag_OwnXMLNode, false);
+  if (!pNode->IsAttributeInXML()) {
+    xml_node_->RemoveChild(pNode->xml_node_.Get());
+    return;
   }
-  return true;
+
+  ASSERT(pNode->xml_node_ == xml_node_);
+  CFX_XMLElement* pXMLElement = ToXMLElement(pNode->xml_node_.Get());
+  if (pXMLElement) {
+    WideString wsAttributeName =
+        pNode->JSObject()->GetCData(XFA_Attribute::QualifiedName);
+    pXMLElement->RemoveAttribute(wsAttributeName);
+  }
+
+  WideString wsName = pNode->JSObject()
+                          ->TryAttribute(XFA_Attribute::Name, false)
+                          .value_or(WideString());
+
+  auto* pNewXMLElement = GetXMLDocument()->CreateNode<CFX_XMLElement>(wsName);
+  WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
+  if (!wsValue.IsEmpty()) {
+    auto* text = GetXMLDocument()->CreateNode<CFX_XMLText>(wsValue);
+    pNewXMLElement->AppendLastChild(text);
+  }
+  pNode->xml_node_ = pNewXMLElement;
+  pNode->JSObject()->SetEnum(XFA_Attribute::Contains,
+                             XFA_AttributeValue::Unknown, false);
 }
 
-CXFA_Node* CXFA_Node::GetFirstChildByName(const WideStringView& wsName) const {
+CXFA_Node* CXFA_Node::GetFirstChildByName(WideStringView wsName) const {
   return GetFirstChildByName(FX_HashCode_GetW(wsName, false));
 }
 
 CXFA_Node* CXFA_Node::GetFirstChildByName(uint32_t dwNameHash) const {
   for (CXFA_Node* pNode = GetFirstChild(); pNode;
        pNode = pNode->GetNextSibling()) {
-    if (pNode->GetNameHash() == dwNameHash) {
+    if (pNode->GetNameHash() == dwNameHash)
       return pNode;
-    }
   }
   return nullptr;
 }
@@ -984,9 +1623,8 @@
 CXFA_Node* CXFA_Node::GetFirstChildByClassInternal(XFA_Element eType) const {
   for (CXFA_Node* pNode = GetFirstChild(); pNode;
        pNode = pNode->GetNextSibling()) {
-    if (pNode->GetElementType() == eType) {
+    if (pNode->GetElementType() == eType)
       return pNode;
-    }
   }
   return nullptr;
 }
@@ -994,51 +1632,89 @@
 CXFA_Node* CXFA_Node::GetNextSameNameSibling(uint32_t dwNameHash) const {
   for (CXFA_Node* pNode = GetNextSibling(); pNode;
        pNode = pNode->GetNextSibling()) {
-    if (pNode->GetNameHash() == dwNameHash) {
+    if (pNode->GetNameHash() == dwNameHash)
       return pNode;
-    }
   }
   return nullptr;
 }
 
 CXFA_Node* CXFA_Node::GetNextSameNameSiblingInternal(
-    const WideStringView& wsNodeName) const {
+    WideStringView wsNodeName) const {
   return GetNextSameNameSibling(FX_HashCode_GetW(wsNodeName, false));
 }
 
 CXFA_Node* CXFA_Node::GetNextSameClassSiblingInternal(XFA_Element eType) const {
   for (CXFA_Node* pNode = GetNextSibling(); pNode;
        pNode = pNode->GetNextSibling()) {
-    if (pNode->GetElementType() == eType) {
+    if (pNode->GetElementType() == eType)
       return pNode;
-    }
   }
   return nullptr;
 }
 
-int32_t CXFA_Node::GetNodeSameNameIndex() const {
-  CFXJSE_Engine* pScriptContext = m_pDocument->GetScriptContext();
-  if (!pScriptContext) {
-    return -1;
-  }
-  return pScriptContext->GetIndexByName(const_cast<CXFA_Node*>(this));
+CXFA_Node* CXFA_Node::GetOneChildNamed(WideStringView wsName) {
+  return FindFirstSiblingNamed(this, FX_HashCode_GetW(wsName, false));
 }
 
-int32_t CXFA_Node::GetNodeSameClassIndex() const {
-  CFXJSE_Engine* pScriptContext = m_pDocument->GetScriptContext();
-  if (!pScriptContext) {
-    return -1;
+CXFA_Node* CXFA_Node::GetOneChildOfClass(WideStringView wsClass) {
+  XFA_Element element = XFA_GetElementByName(wsClass);
+  if (element == XFA_Element::Unknown)
+    return nullptr;
+
+  return FindFirstSiblingOfClass(this, element);
+}
+
+std::vector<CXFA_Node*> CXFA_Node::GetSiblings(bool bIsClassName) {
+  std::vector<CXFA_Node*> siblings;
+  CXFA_Node* parent = GetParent();
+  if (!parent)
+    return siblings;
+  if (!parent->HasProperty(GetElementType())) {
+    parent = GetTransparentParent();
+    if (!parent)
+      return siblings;
   }
-  return pScriptContext->GetIndexByClassName(const_cast<CXFA_Node*>(this));
+
+  uint32_t dwNameHash = bIsClassName ? GetClassHashCode() : GetNameHash();
+  TraverseSiblings(parent, dwNameHash, &siblings, bIsClassName, true);
+  return siblings;
+}
+
+size_t CXFA_Node::GetIndex(bool bIsProperty, bool bIsClassIndex) {
+  CXFA_Node* parent = GetParent();
+  if (!parent)
+    return 0;
+
+  if (!bIsProperty) {
+    parent = GetTransparentParent();
+    if (!parent)
+      return 0;
+  }
+  uint32_t dwHashName = bIsClassIndex ? GetClassHashCode() : GetNameHash();
+  std::vector<CXFA_Node*> siblings;
+  TraverseSiblings(parent, dwHashName, &siblings, bIsClassIndex, true);
+  for (size_t i = 0; i < siblings.size(); ++i) {
+    if (siblings[i] == this)
+      return i;
+  }
+  return 0;
+}
+
+size_t CXFA_Node::GetIndexByName() {
+  return GetIndex(IsProperty(), /*bIsClassIndex=*/false);
+}
+
+size_t CXFA_Node::GetIndexByClassName() {
+  return GetIndex(IsProperty(), /*bIsClassIndex=*/true);
 }
 
 CXFA_Node* CXFA_Node::GetInstanceMgrOfSubform() {
   CXFA_Node* pInstanceMgr = nullptr;
   if (m_ePacket == XFA_PacketType::Form) {
     CXFA_Node* pParentNode = GetParent();
-    if (!pParentNode || pParentNode->GetElementType() == XFA_Element::Area) {
+    if (!pParentNode || pParentNode->GetElementType() == XFA_Element::Area)
       return pInstanceMgr;
-    }
+
     for (CXFA_Node* pNode = GetPrevSibling(); pNode;
          pNode = pNode->GetPrevSibling()) {
       XFA_Element eType = pNode->GetElementType();
@@ -1051,7 +1727,7 @@
         WideString wsInstName =
             pNode->JSObject()->GetCData(XFA_Attribute::Name);
         if (wsInstName.GetLength() > 0 && wsInstName[0] == '_' &&
-            wsInstName.Right(wsInstName.GetLength() - 1) == wsName) {
+            wsInstName.Last(wsInstName.GetLength() - 1) == wsName) {
           pInstanceMgr = pNode;
         }
         break;
@@ -1069,12 +1745,14 @@
   if (m_uNodeFlags & dwFlag)
     return true;
   if (dwFlag == XFA_NodeFlag_HasRemovedChildren)
-    return m_pParent && m_pParent->HasFlag(dwFlag);
+    return GetParent() && GetParent()->HasFlag(dwFlag);
   return false;
 }
 
-void CXFA_Node::SetFlag(uint32_t dwFlag, bool bNotify) {
-  if (dwFlag == XFA_NodeFlag_Initialized && bNotify && !IsInitialized()) {
+void CXFA_Node::SetFlagAndNotify(uint32_t dwFlag) {
+  ASSERT(dwFlag == XFA_NodeFlag_Initialized);
+
+  if (!IsInitialized()) {
     CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
     if (pNotify) {
       pNotify->OnNodeReady(this);
@@ -1083,26 +1761,20 @@
   m_uNodeFlags |= dwFlag;
 }
 
+void CXFA_Node::SetFlag(uint32_t dwFlag) {
+  m_uNodeFlags |= dwFlag;
+}
+
 void CXFA_Node::ClearFlag(uint32_t dwFlag) {
   m_uNodeFlags &= ~dwFlag;
 }
 
-void CXFA_Node::ReleaseBindingNodes() {
-  // Clear any binding nodes as we don't necessarily destruct in an order that
-  // makes sense.
-  for (auto& node : binding_nodes_)
-    node.Release();
-
-  for (CXFA_Node* pNode = m_pChild; pNode; pNode = pNode->m_pNext)
-    pNode->ReleaseBindingNodes();
-}
-
 bool CXFA_Node::IsAttributeInXML() {
   return JSObject()->GetEnum(XFA_Attribute::Contains) ==
-         XFA_AttributeEnum::MetaData;
+         XFA_AttributeValue::MetaData;
 }
 
-void CXFA_Node::OnRemoved(bool bNotify) {
+void CXFA_Node::OnRemoved(bool bNotify) const {
   if (!bNotify)
     return;
 
@@ -1117,17 +1789,16 @@
 }
 
 CFX_XMLNode* CXFA_Node::CreateXMLMappingNode() {
-  if (!m_pXMLNode) {
-    WideString wsTag(JSObject()->GetCData(XFA_Attribute::Name));
-    m_pXMLNode = new CFX_XMLElement(wsTag);
-    SetFlag(XFA_NodeFlag_OwnXMLNode, false);
+  if (!xml_node_) {
+    xml_node_ = GetXMLDocument()->CreateNode<CFX_XMLElement>(
+        JSObject()->GetCData(XFA_Attribute::Name));
   }
-  return m_pXMLNode;
+  return xml_node_.Get();
 }
 
-bool CXFA_Node::IsNeedSavingXMLNode() {
-  return m_pXMLNode && (GetPacketType() == XFA_PacketType::Datasets ||
-                        GetElementType() == XFA_Element::Xfa);
+bool CXFA_Node::IsNeedSavingXMLNode() const {
+  return xml_node_ && (GetPacketType() == XFA_PacketType::Datasets ||
+                       GetElementType() == XFA_Element::Xfa);
 }
 
 CXFA_Node* CXFA_Node::GetItemIfExists(int32_t iIndex) {
@@ -1146,7 +1817,7 @@
       WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
       WideString wsInstName = JSObject()->GetCData(XFA_Attribute::Name);
       if (wsInstName.GetLength() < 1 || wsInstName[0] != '_' ||
-          wsInstName.Right(wsInstName.GetLength() - 1) != wsName) {
+          wsInstName.Last(wsInstName.GetLength() - 1) != wsName) {
         return nullptr;
       }
       dwNameHash = pNode->GetNameHash();
@@ -1177,7 +1848,7 @@
       WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
       WideString wsInstName = JSObject()->GetCData(XFA_Attribute::Name);
       if (wsInstName.GetLength() < 1 || wsInstName[0] != '_' ||
-          wsInstName.Right(wsInstName.GetLength() - 1) != wsName) {
+          wsInstName.Last(wsInstName.GetLength() - 1) != wsName) {
         return iCount;
       }
       dwNameHash = pNode->GetNameHash();
@@ -1205,7 +1876,7 @@
 
     CXFA_Node* pNextSibling =
         iCount > 0 ? item->GetNextSibling() : GetNextSibling();
-    GetParent()->InsertChild(pNewInstance, pNextSibling);
+    GetParent()->InsertChildAndNotify(pNewInstance, pNextSibling);
     if (bMoveDataBindingNodes) {
       std::set<CXFA_Node*> sNew;
       std::set<CXFA_Node*> sAfter;
@@ -1240,7 +1911,7 @@
       return;
     }
 
-    GetParent()->InsertChild(pNewInstance, pBeforeInstance);
+    GetParent()->InsertChildAndNotify(pNewInstance, pBeforeInstance);
     if (bMoveDataBindingNodes) {
       std::set<CXFA_Node*> sNew;
       std::set<CXFA_Node*> sBefore;
@@ -1273,7 +1944,7 @@
 
 void CXFA_Node::RemoveItem(CXFA_Node* pRemoveInstance,
                            bool bRemoveDataBinding) {
-  GetParent()->RemoveChild(pRemoveInstance, true);
+  GetParent()->RemoveChildAndNotify(pRemoveInstance, true);
   if (!bRemoveDataBinding)
     return;
 
@@ -1287,7 +1958,7 @@
 
     if (pDataNode->RemoveBindItem(pFormNode) == 0) {
       if (CXFA_Node* pDataParent = pDataNode->GetParent()) {
-        pDataParent->RemoveChild(pDataNode, true);
+        pDataParent->RemoveChildAndNotify(pDataNode, true);
       }
     }
     pFormNode->SetBindingNode(nullptr);
@@ -1318,7 +1989,7 @@
       pTemplateNode, pFormParent, pDataScope, true, bDataMerge, true);
   if (pInstance) {
     pDocument->DataMerge_UpdateBindingRelations(pInstance);
-    pFormParent->RemoveChild(pInstance, true);
+    pFormParent->RemoveChildAndNotify(pInstance, true);
   }
   return pInstance;
 }
@@ -1355,12 +2026,12 @@
   return {WideString(static_cast<const wchar_t*>(*value))};
 }
 
-Optional<XFA_AttributeEnum> CXFA_Node::GetDefaultEnum(
+Optional<XFA_AttributeValue> CXFA_Node::GetDefaultEnum(
     XFA_Attribute attr) const {
   Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Enum);
   if (!value)
     return {};
-  return {static_cast<XFA_AttributeEnum>(reinterpret_cast<uintptr_t>(*value))};
+  return {static_cast<XFA_AttributeValue>(reinterpret_cast<uintptr_t>(*value))};
 }
 
 Optional<void*> CXFA_Node::GetDefaultValue(XFA_Attribute attr,
@@ -1375,10 +2046,6 @@
 
 void CXFA_Node::SendAttributeChangeMessage(XFA_Attribute eAttribute,
                                            bool bScriptModify) {
-  CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
-  if (!pLayoutPro)
-    return;
-
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
@@ -1417,9 +2084,8 @@
                                 pParentNode->GetParent());
       } else {
         CXFA_Node* pNode = pParentNode->GetParent();
-        if (pNode && pNode->GetElementType() == XFA_Element::Ui) {
+        if (pNode && pNode->GetElementType() == XFA_Element::Ui)
           pNotify->OnValueChanged(this, eAttribute, pNode, pNode->GetParent());
-        }
       }
       break;
     }
@@ -1474,7 +2140,7 @@
     case XFA_Element::Field:
     case XFA_Element::Subform:
     case XFA_Element::SubformSet:
-      pLayoutPro->AddChangedContainer(this);
+      pNotify->OnContainerChanged(this);
       pNotify->OnValueChanged(this, eAttribute, this, this);
       break;
     case XFA_Element::Sharptext:
@@ -1522,14 +2188,14 @@
     pParent = pParent->GetParent();
 
   if (pParent)
-    pLayoutPro->AddChangedContainer(pParent);
+    pNotify->OnContainerChanged(pParent);
 }
 
 void CXFA_Node::SyncValue(const WideString& wsValue, bool bNotify) {
   WideString wsFormatValue = wsValue;
-  CXFA_WidgetAcc* pContainerWidgetAcc = GetContainerWidgetAcc();
-  if (pContainerWidgetAcc)
-    wsFormatValue = pContainerWidgetAcc->GetFormatDataValue(wsValue);
+  CXFA_Node* pContainerNode = GetContainerNode();
+  if (pContainerNode)
+    wsFormatValue = pContainerNode->GetFormatDataValue(wsValue);
 
   JSObject()->SetContent(wsValue, wsFormatValue, bNotify, false, true);
 }
@@ -1538,7 +2204,7 @@
   return JSObject()->GetContent(false);
 }
 
-int32_t CXFA_Node::GetRotate() {
+int32_t CXFA_Node::GetRotate() const {
   Optional<int32_t> degrees =
       JSObject()->TryInteger(XFA_Attribute::Rotate, false);
   return degrees ? XFA_MapRotation(*degrees) / 90 * 90 : 0;
@@ -1594,10 +2260,10 @@
   return JSObject()->GetProperty<CXFA_Para>(0, XFA_Element::Para);
 }
 
-bool CXFA_Node::IsOpenAccess() {
+bool CXFA_Node::IsOpenAccess() const {
   for (auto* pNode = this; pNode; pNode = pNode->GetContainerParent()) {
-    XFA_AttributeEnum iAcc = pNode->JSObject()->GetEnum(XFA_Attribute::Access);
-    if (iAcc != XFA_AttributeEnum::Open)
+    XFA_AttributeValue iAcc = pNode->JSObject()->GetEnum(XFA_Attribute::Access);
+    if (iAcc != XFA_AttributeValue::Open)
       return false;
   }
   return true;
@@ -1631,6 +2297,49 @@
   return JSObject()->GetProperty<CXFA_Bind>(0, XFA_Element::Bind);
 }
 
+Optional<XFA_AttributeValue> CXFA_Node::GetIntactFromKeep(
+    const CXFA_Keep* pKeep,
+    XFA_AttributeValue eLayoutType) const {
+  Optional<XFA_AttributeValue> intact =
+      pKeep->JSObject()->TryEnum(XFA_Attribute::Intact, false);
+  if (!intact.has_value())
+    return {};
+
+  if (intact.value() != XFA_AttributeValue::None ||
+      eLayoutType != XFA_AttributeValue::Row ||
+      m_pDocument->GetCurVersionMode() >= XFA_VERSION_208) {
+    return intact;
+  }
+
+  CXFA_Node* pPreviewRow = GetPrevContainerSibling();
+  if (!pPreviewRow || pPreviewRow->JSObject()->GetEnum(XFA_Attribute::Layout) !=
+                          XFA_AttributeValue::Row) {
+    return intact;
+  }
+
+  Optional<XFA_AttributeValue> value =
+      pKeep->JSObject()->TryEnum(XFA_Attribute::Previous, false);
+  if (value && (*value == XFA_AttributeValue::ContentArea ||
+                *value == XFA_AttributeValue::PageArea)) {
+    return XFA_AttributeValue::ContentArea;
+  }
+
+  CXFA_Keep* pNode =
+      pPreviewRow->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
+  if (!pNode)
+    return intact;
+
+  Optional<XFA_AttributeValue> ret =
+      pNode->JSObject()->TryEnum(XFA_Attribute::Next, false);
+  if (!ret)
+    return intact;
+
+  return (*ret == XFA_AttributeValue::ContentArea ||
+          *ret == XFA_AttributeValue::PageArea)
+             ? XFA_AttributeValue::ContentArea
+             : intact;
+}
+
 Optional<float> CXFA_Node::TryWidth() {
   return JSObject()->TryMeasureAsFloat(XFA_Attribute::W);
 }
@@ -1662,275 +2371,300 @@
   return pExcl;
 }
 
-int32_t CXFA_Node::ProcessEvent(CXFA_FFDocView* docView,
-                                XFA_AttributeEnum iActivity,
-                                CXFA_EventParam* pEventParam) {
+XFA_EventError CXFA_Node::ProcessEvent(CXFA_FFDocView* pDocView,
+                                       XFA_AttributeValue iActivity,
+                                       CXFA_EventParam* pEventParam) {
   if (GetElementType() == XFA_Element::Draw)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
-  std::vector<CXFA_Event*> eventArray = GetWidgetAcc()->GetEventByActivity(
-      iActivity, pEventParam->m_bIsFormReady);
+  std::vector<CXFA_Event*> eventArray =
+      GetEventByActivity(iActivity, pEventParam->m_bIsFormReady);
   bool first = true;
-  int32_t iRet = XFA_EVENTERROR_NotExist;
+  XFA_EventError iRet = XFA_EventError::kNotExist;
   for (CXFA_Event* event : eventArray) {
-    int32_t result = ProcessEvent(docView, event, pEventParam);
-    if (first || result == XFA_EVENTERROR_Success)
+    XFA_EventError result =
+        ProcessEventInternal(pDocView, iActivity, event, pEventParam);
+    if (first || result == XFA_EventError::kSuccess)
       iRet = result;
     first = false;
   }
   return iRet;
 }
 
-int32_t CXFA_Node::ProcessEvent(CXFA_FFDocView* docView,
-                                CXFA_Event* event,
-                                CXFA_EventParam* pEventParam) {
+XFA_EventError CXFA_Node::ProcessEventInternal(CXFA_FFDocView* pDocView,
+                                               XFA_AttributeValue iActivity,
+                                               CXFA_Event* event,
+                                               CXFA_EventParam* pEventParam) {
   if (!event)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
   switch (event->GetEventType()) {
     case XFA_Element::Execute:
       break;
     case XFA_Element::Script:
-      return ExecuteScript(docView, event->GetScriptIfExists(), pEventParam);
+      if (iActivity == XFA_AttributeValue::DocClose) {
+        // Too late, scripting engine already gone.
+        return XFA_EventError::kNotExist;
+      }
+      return ExecuteScript(pDocView, event->GetScriptIfExists(), pEventParam);
     case XFA_Element::SignData:
       break;
     case XFA_Element::Submit: {
+// TODO(crbug.com/867485): Submit is disabled for now. Fix it and reenable this
+// code.
+#ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
       CXFA_Submit* submit = event->GetSubmitIfExists();
       if (!submit)
-        return XFA_EVENTERROR_NotExist;
-      return docView->GetDoc()->GetDocEnvironment()->Submit(docView->GetDoc(),
-                                                            submit);
+        return XFA_EventError::kNotExist;
+      return pDocView->GetDoc()->GetDocEnvironment()->Submit(pDocView->GetDoc(),
+                                                             submit);
+#else
+      return XFA_EventError::kDisabled;
+#endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
     }
     default:
       break;
   }
-  return XFA_EVENTERROR_NotExist;
+  return XFA_EventError::kNotExist;
 }
 
-int32_t CXFA_Node::ProcessCalculate(CXFA_FFDocView* docView) {
+XFA_EventError CXFA_Node::ProcessCalculate(CXFA_FFDocView* pDocView) {
   if (GetElementType() == XFA_Element::Draw)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
   CXFA_Calculate* calc = GetCalculateIfExists();
   if (!calc)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
   if (IsUserInteractive())
-    return XFA_EVENTERROR_Disabled;
+    return XFA_EventError::kDisabled;
 
   CXFA_EventParam EventParam;
   EventParam.m_eType = XFA_EVENT_Calculate;
-  int32_t iRet = ExecuteScript(docView, calc->GetScriptIfExists(), &EventParam);
-  if (iRet != XFA_EVENTERROR_Success)
+  XFA_EventError iRet =
+      ExecuteScript(pDocView, calc->GetScriptIfExists(), &EventParam);
+  if (iRet != XFA_EventError::kSuccess)
     return iRet;
 
   if (GetRawValue() != EventParam.m_wsResult) {
-    GetWidgetAcc()->SetValue(XFA_VALUEPICTURE_Raw, EventParam.m_wsResult);
-    GetWidgetAcc()->UpdateUIDisplay(docView, nullptr);
+    SetValue(XFA_VALUEPICTURE_Raw, EventParam.m_wsResult);
+    pDocView->UpdateUIDisplay(this, nullptr);
   }
-  return XFA_EVENTERROR_Success;
+  return XFA_EventError::kSuccess;
 }
 
-void CXFA_Node::ProcessScriptTestValidate(CXFA_FFDocView* docView,
+void CXFA_Node::ProcessScriptTestValidate(CXFA_FFDocView* pDocView,
                                           CXFA_Validate* validate,
-                                          int32_t iRet,
+                                          XFA_EventError iRet,
                                           bool bRetValue,
                                           bool bVersionFlag) {
-  if (iRet != XFA_EVENTERROR_Success)
+  if (iRet != XFA_EventError::kSuccess)
     return;
   if (bRetValue)
     return;
 
   IXFA_AppProvider* pAppProvider =
-      docView->GetDoc()->GetApp()->GetAppProvider();
+      pDocView->GetDoc()->GetApp()->GetAppProvider();
   if (!pAppProvider)
     return;
 
   WideString wsTitle = pAppProvider->GetAppTitle();
   WideString wsScriptMsg = validate->GetScriptMessageText();
-  if (validate->GetScriptTest() == XFA_AttributeEnum::Warning) {
+  if (validate->GetScriptTest() == XFA_AttributeValue::Warning) {
     if (IsUserInteractive())
       return;
     if (wsScriptMsg.IsEmpty())
       wsScriptMsg = GetValidateMessage(false, bVersionFlag);
 
     if (bVersionFlag) {
-      pAppProvider->MsgBox(wsScriptMsg, wsTitle, XFA_MBICON_Warning, XFA_MB_OK);
+      pAppProvider->MsgBox(wsScriptMsg, wsTitle,
+                           static_cast<uint32_t>(AlertIcon::kWarning),
+                           static_cast<uint32_t>(AlertButton::kOK));
       return;
     }
-    if (pAppProvider->MsgBox(wsScriptMsg, wsTitle, XFA_MBICON_Warning,
-                             XFA_MB_YesNo) == XFA_IDYes) {
-      SetFlag(XFA_NodeFlag_UserInteractive, false);
+    if (pAppProvider->MsgBox(wsScriptMsg, wsTitle,
+                             static_cast<uint32_t>(AlertIcon::kWarning),
+                             static_cast<uint32_t>(AlertButton::kYesNo)) ==
+        static_cast<uint32_t>(AlertReturn::kYes)) {
+      SetFlag(XFA_NodeFlag_UserInteractive);
     }
     return;
   }
 
   if (wsScriptMsg.IsEmpty())
     wsScriptMsg = GetValidateMessage(true, bVersionFlag);
-  pAppProvider->MsgBox(wsScriptMsg, wsTitle, XFA_MBICON_Error, XFA_MB_OK);
+  pAppProvider->MsgBox(wsScriptMsg, wsTitle,
+                       static_cast<uint32_t>(AlertIcon::kError),
+                       static_cast<uint32_t>(AlertButton::kOK));
 }
 
-int32_t CXFA_Node::ProcessFormatTestValidate(CXFA_FFDocView* docView,
-                                             CXFA_Validate* validate,
-                                             bool bVersionFlag) {
+XFA_EventError CXFA_Node::ProcessFormatTestValidate(CXFA_FFDocView* pDocView,
+                                                    CXFA_Validate* validate,
+                                                    bool bVersionFlag) {
+  WideString wsPicture = validate->GetPicture();
+  if (wsPicture.IsEmpty())
+    return XFA_EventError::kNotExist;
+
   WideString wsRawValue = GetRawValue();
-  if (!wsRawValue.IsEmpty()) {
-    WideString wsPicture = validate->GetPicture();
-    if (wsPicture.IsEmpty())
-      return XFA_EVENTERROR_NotExist;
+  if (wsRawValue.IsEmpty())
+    return XFA_EventError::kError;
 
-    IFX_Locale* pLocale = GetLocale();
-    if (!pLocale)
-      return XFA_EVENTERROR_NotExist;
+  LocaleIface* pLocale = GetLocale();
+  if (!pLocale)
+    return XFA_EventError::kNotExist;
 
-    CXFA_LocaleValue lcValue = XFA_GetLocaleValue(this);
-    if (!lcValue.ValidateValue(lcValue.GetValue(), wsPicture, pLocale,
-                               nullptr)) {
-      IXFA_AppProvider* pAppProvider =
-          docView->GetDoc()->GetApp()->GetAppProvider();
-      if (!pAppProvider)
-        return XFA_EVENTERROR_NotExist;
+  CXFA_LocaleValue lcValue = XFA_GetLocaleValue(this);
+  if (lcValue.ValidateValue(lcValue.GetValue(), wsPicture, pLocale, nullptr))
+    return XFA_EventError::kSuccess;
 
-      WideString wsFormatMsg = validate->GetFormatMessageText();
-      WideString wsTitle = pAppProvider->GetAppTitle();
-      if (validate->GetFormatTest() == XFA_AttributeEnum::Error) {
-        if (wsFormatMsg.IsEmpty())
-          wsFormatMsg = GetValidateMessage(true, bVersionFlag);
-        pAppProvider->MsgBox(wsFormatMsg, wsTitle, XFA_MBICON_Error, XFA_MB_OK);
-        return XFA_EVENTERROR_Success;
-      }
-      if (IsUserInteractive())
-        return XFA_EVENTERROR_NotExist;
-      if (wsFormatMsg.IsEmpty())
-        wsFormatMsg = GetValidateMessage(false, bVersionFlag);
+  IXFA_AppProvider* pAppProvider =
+      pDocView->GetDoc()->GetApp()->GetAppProvider();
+  if (!pAppProvider)
+    return XFA_EventError::kNotExist;
 
-      if (bVersionFlag) {
-        pAppProvider->MsgBox(wsFormatMsg, wsTitle, XFA_MBICON_Warning,
-                             XFA_MB_OK);
-        return XFA_EVENTERROR_Success;
-      }
-      if (pAppProvider->MsgBox(wsFormatMsg, wsTitle, XFA_MBICON_Warning,
-                               XFA_MB_YesNo) == XFA_IDYes) {
-        SetFlag(XFA_NodeFlag_UserInteractive, false);
-      }
-      return XFA_EVENTERROR_Success;
-    }
+  WideString wsFormatMsg = validate->GetFormatMessageText();
+  WideString wsTitle = pAppProvider->GetAppTitle();
+  if (validate->GetFormatTest() == XFA_AttributeValue::Error) {
+    if (wsFormatMsg.IsEmpty())
+      wsFormatMsg = GetValidateMessage(true, bVersionFlag);
+    pAppProvider->MsgBox(wsFormatMsg, wsTitle,
+                         static_cast<uint32_t>(AlertIcon::kError),
+                         static_cast<uint32_t>(AlertButton::kOK));
+    return XFA_EventError::kError;
   }
-  return XFA_EVENTERROR_NotExist;
+
+  if (wsFormatMsg.IsEmpty())
+    wsFormatMsg = GetValidateMessage(false, bVersionFlag);
+
+  if (bVersionFlag) {
+    pAppProvider->MsgBox(wsFormatMsg, wsTitle,
+                         static_cast<uint32_t>(AlertIcon::kWarning),
+                         static_cast<uint32_t>(AlertButton::kOK));
+    return XFA_EventError::kError;
+  }
+
+  if (pAppProvider->MsgBox(wsFormatMsg, wsTitle,
+                           static_cast<uint32_t>(AlertIcon::kWarning),
+                           static_cast<uint32_t>(AlertButton::kYesNo)) ==
+      static_cast<uint32_t>(AlertReturn::kYes)) {
+    SetFlag(XFA_NodeFlag_UserInteractive);
+  }
+
+  return XFA_EventError::kError;
 }
 
-int32_t CXFA_Node::ProcessNullTestValidate(CXFA_FFDocView* docView,
-                                           CXFA_Validate* validate,
-                                           int32_t iFlags,
-                                           bool bVersionFlag) {
-  if (!GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw).IsEmpty())
-    return XFA_EVENTERROR_Success;
-  if (GetWidgetAcc()->IsNull() && GetWidgetAcc()->IsPreNull())
-    return XFA_EVENTERROR_Success;
+XFA_EventError CXFA_Node::ProcessNullTestValidate(CXFA_FFDocView* pDocView,
+                                                  CXFA_Validate* validate,
+                                                  int32_t iFlags,
+                                                  bool bVersionFlag) {
+  if (!GetValue(XFA_VALUEPICTURE_Raw).IsEmpty())
+    return XFA_EventError::kSuccess;
+  if (m_bIsNull && m_bPreNull)
+    return XFA_EventError::kSuccess;
 
-  XFA_AttributeEnum eNullTest = validate->GetNullTest();
+  XFA_AttributeValue eNullTest = validate->GetNullTest();
   WideString wsNullMsg = validate->GetNullMessageText();
   if (iFlags & 0x01) {
-    int32_t iRet = XFA_EVENTERROR_Success;
-    if (eNullTest != XFA_AttributeEnum::Disabled)
-      iRet = XFA_EVENTERROR_Error;
+    XFA_EventError iRet = XFA_EventError::kSuccess;
+    if (eNullTest != XFA_AttributeValue::Disabled)
+      iRet = XFA_EventError::kError;
 
-    if (!wsNullMsg.IsEmpty()) {
-      if (eNullTest != XFA_AttributeEnum::Disabled) {
-        docView->m_arrNullTestMsg.push_back(wsNullMsg);
-        return XFA_EVENTERROR_Error;
-      }
-      return XFA_EVENTERROR_Success;
+    if (wsNullMsg.IsEmpty())
+      return iRet;
+
+    if (eNullTest != XFA_AttributeValue::Disabled) {
+      pDocView->m_arrNullTestMsg.push_back(wsNullMsg);
+      return XFA_EventError::kError;
     }
-    return iRet;
+    return XFA_EventError::kSuccess;
   }
   if (wsNullMsg.IsEmpty() && bVersionFlag &&
-      eNullTest != XFA_AttributeEnum::Disabled) {
-    return XFA_EVENTERROR_Error;
+      eNullTest != XFA_AttributeValue::Disabled) {
+    return XFA_EventError::kError;
   }
   IXFA_AppProvider* pAppProvider =
-      docView->GetDoc()->GetApp()->GetAppProvider();
+      pDocView->GetDoc()->GetApp()->GetAppProvider();
   if (!pAppProvider)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
   WideString wsCaptionName;
   WideString wsTitle = pAppProvider->GetAppTitle();
   switch (eNullTest) {
-    case XFA_AttributeEnum::Error: {
+    case XFA_AttributeValue::Error: {
       if (wsNullMsg.IsEmpty()) {
         wsCaptionName = GetValidateCaptionName(bVersionFlag);
-        wsNullMsg =
-            WideString::Format(L"%ls cannot be blank.", wsCaptionName.c_str());
+        wsNullMsg = wsCaptionName + L" cannot be blank.";
       }
-      pAppProvider->MsgBox(wsNullMsg, wsTitle, XFA_MBICON_Status, XFA_MB_OK);
-      return XFA_EVENTERROR_Error;
+      pAppProvider->MsgBox(wsNullMsg, wsTitle,
+                           static_cast<uint32_t>(AlertIcon::kStatus),
+                           static_cast<uint32_t>(AlertButton::kOK));
+      return XFA_EventError::kError;
     }
-    case XFA_AttributeEnum::Warning: {
+    case XFA_AttributeValue::Warning: {
       if (IsUserInteractive())
-        return true;
+        return XFA_EventError::kSuccess;
 
       if (wsNullMsg.IsEmpty()) {
         wsCaptionName = GetValidateCaptionName(bVersionFlag);
-        wsNullMsg = WideString::Format(
-            L"%ls cannot be blank. To ignore validations for %ls, click "
-            L"Ignore.",
-            wsCaptionName.c_str(), wsCaptionName.c_str());
+        wsNullMsg = wsCaptionName +
+                    L" cannot be blank. To ignore validations for " +
+                    wsCaptionName + L", click Ignore.";
       }
-      if (pAppProvider->MsgBox(wsNullMsg, wsTitle, XFA_MBICON_Warning,
-                               XFA_MB_YesNo) == XFA_IDYes) {
-        SetFlag(XFA_NodeFlag_UserInteractive, false);
+      if (pAppProvider->MsgBox(wsNullMsg, wsTitle,
+                               static_cast<uint32_t>(AlertIcon::kWarning),
+                               static_cast<uint32_t>(AlertButton::kYesNo)) ==
+          static_cast<uint32_t>(AlertReturn::kYes)) {
+        SetFlag(XFA_NodeFlag_UserInteractive);
       }
-      return XFA_EVENTERROR_Error;
+      return XFA_EventError::kError;
     }
-    case XFA_AttributeEnum::Disabled:
+    case XFA_AttributeValue::Disabled:
     default:
       break;
   }
-  return XFA_EVENTERROR_Success;
+  return XFA_EventError::kSuccess;
 }
 
-int32_t CXFA_Node::ProcessValidate(CXFA_FFDocView* docView, int32_t iFlags) {
+XFA_EventError CXFA_Node::ProcessValidate(CXFA_FFDocView* pDocView,
+                                          int32_t iFlags) {
   if (GetElementType() == XFA_Element::Draw)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
   CXFA_Validate* validate = GetValidateIfExists();
   if (!validate)
-    return XFA_EVENTERROR_NotExist;
+    return XFA_EventError::kNotExist;
 
   bool bInitDoc = validate->NeedsInitApp();
-  bool bStatus = docView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End;
-  int32_t iFormat = 0;
-  int32_t iRet = XFA_EVENTERROR_NotExist;
+  bool bStatus = pDocView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End;
+  XFA_EventError iFormat = XFA_EventError::kNotExist;
+  XFA_EventError iRet = XFA_EventError::kNotExist;
   CXFA_Script* script = validate->GetScriptIfExists();
   bool bRet = false;
   bool hasBoolResult = (bInitDoc || bStatus) && GetRawValue().IsEmpty();
   if (script) {
     CXFA_EventParam eParam;
     eParam.m_eType = XFA_EVENT_Validate;
-    eParam.m_pTarget = GetWidgetAcc();
-    std::tie(iRet, bRet) = ExecuteBoolScript(docView, script, &eParam);
+    eParam.m_pTarget = this;
+    std::tie(iRet, bRet) = ExecuteBoolScript(pDocView, script, &eParam);
   }
 
-  XFA_VERSION version = docView->GetDoc()->GetXFADoc()->GetCurVersionMode();
-  bool bVersionFlag = false;
-  if (version < XFA_VERSION_208)
-    bVersionFlag = true;
+  XFA_VERSION version = pDocView->GetDoc()->GetXFADoc()->GetCurVersionMode();
+  bool bVersionFlag = version < XFA_VERSION_208;
 
   if (bInitDoc) {
     validate->ClearFlag(XFA_NodeFlag_NeedsInitApp);
   } else {
-    iFormat = ProcessFormatTestValidate(docView, validate, bVersionFlag);
-    if (!bVersionFlag) {
-      bVersionFlag =
-          docView->GetDoc()->GetXFADoc()->HasFlag(XFA_DOCFLAG_Scripting);
-    }
-
-    iRet |= ProcessNullTestValidate(docView, validate, iFlags, bVersionFlag);
+    iFormat = ProcessFormatTestValidate(pDocView, validate, bVersionFlag);
+    if (!bVersionFlag)
+      bVersionFlag = pDocView->GetDoc()->GetXFADoc()->is_scripting();
+    XFA_EventErrorAccumulate(
+        &iRet,
+        ProcessNullTestValidate(pDocView, validate, iFlags, bVersionFlag));
   }
+  if (iFormat != XFA_EventError::kSuccess && hasBoolResult)
+    ProcessScriptTestValidate(pDocView, validate, iRet, bRet, bVersionFlag);
 
-  if (iFormat != XFA_EVENTERROR_Success && hasBoolResult)
-    ProcessScriptTestValidate(docView, validate, iRet, bRet, bVersionFlag);
-
-  return iRet | iFormat;
+  XFA_EventErrorAccumulate(&iRet, iFormat);
+  return iRet;
 }
 
 WideString CXFA_Node::GetValidateCaptionName(bool bVersionFlag) {
@@ -1955,50 +2689,46 @@
 WideString CXFA_Node::GetValidateMessage(bool bError, bool bVersionFlag) {
   WideString wsCaptionName = GetValidateCaptionName(bVersionFlag);
   if (bVersionFlag)
-    return WideString::Format(L"%ls validation failed", wsCaptionName.c_str());
-  if (bError) {
-    return WideString::Format(L"The value you entered for %ls is invalid.",
-                              wsCaptionName.c_str());
+    return wsCaptionName + L" validation failed";
+  WideString result =
+      L"The value you entered for " + wsCaptionName + L" is invalid.";
+  if (!bError) {
+    result +=
+        L" To ignore validations for " + wsCaptionName + L", click Ignore.";
   }
-  return WideString::Format(
-      L"The value you entered for %ls is invalid. To ignore "
-      L"validations for %ls, click Ignore.",
-      wsCaptionName.c_str(), wsCaptionName.c_str());
+  return result;
 }
 
-int32_t CXFA_Node::ExecuteScript(CXFA_FFDocView* docView,
-                                 CXFA_Script* script,
-                                 CXFA_EventParam* pEventParam) {
-  bool bRet;
-  int32_t iRet;
-  std::tie(iRet, bRet) = ExecuteBoolScript(docView, script, pEventParam);
-  return iRet;
+XFA_EventError CXFA_Node::ExecuteScript(CXFA_FFDocView* pDocView,
+                                        CXFA_Script* script,
+                                        CXFA_EventParam* pEventParam) {
+  return ExecuteBoolScript(pDocView, script, pEventParam).first;
 }
 
-std::pair<int32_t, bool> CXFA_Node::ExecuteBoolScript(
-    CXFA_FFDocView* docView,
+std::pair<XFA_EventError, bool> CXFA_Node::ExecuteBoolScript(
+    CXFA_FFDocView* pDocView,
     CXFA_Script* script,
     CXFA_EventParam* pEventParam) {
   if (m_ExecuteRecursionDepth > kMaxExecuteRecursion)
-    return {XFA_EVENTERROR_Success, false};
+    return {XFA_EventError::kSuccess, false};
 
   ASSERT(pEventParam);
   if (!script)
-    return {XFA_EVENTERROR_NotExist, false};
-  if (script->GetRunAt() == XFA_AttributeEnum::Server)
-    return {XFA_EVENTERROR_Disabled, false};
+    return {XFA_EventError::kNotExist, false};
+  if (script->GetRunAt() == XFA_AttributeValue::Server)
+    return {XFA_EventError::kDisabled, false};
 
   WideString wsExpression = script->GetExpression();
   if (wsExpression.IsEmpty())
-    return {XFA_EVENTERROR_NotExist, false};
+    return {XFA_EventError::kNotExist, false};
 
   CXFA_Script::Type eScriptType = script->GetContentType();
   if (eScriptType == CXFA_Script::Type::Unknown)
-    return {XFA_EVENTERROR_Success, false};
+    return {XFA_EventError::kSuccess, false};
 
-  CXFA_FFDoc* pDoc = docView->GetDoc();
+  CXFA_FFDoc* pDoc = pDocView->GetDoc();
   CFXJSE_Engine* pContext = pDoc->GetXFADoc()->GetScriptContext();
-  pContext->SetEventParam(*pEventParam);
+  pContext->SetEventParam(pEventParam);
   pContext->SetRunAtType(script->GetRunAt());
 
   std::vector<CXFA_Node*> refNodes;
@@ -2016,25 +2746,24 @@
                                pTmpRetValue.get(), this);
   }
 
-  int32_t iRet = XFA_EVENTERROR_Error;
+  XFA_EventError iRet = XFA_EventError::kError;
   if (bRet) {
-    iRet = XFA_EVENTERROR_Success;
+    iRet = XFA_EventError::kSuccess;
     if (pEventParam->m_eType == XFA_EVENT_Calculate ||
         pEventParam->m_eType == XFA_EVENT_InitCalculate) {
       if (!pTmpRetValue->IsUndefined()) {
         if (!pTmpRetValue->IsNull())
           pEventParam->m_wsResult = pTmpRetValue->ToWideString();
 
-        iRet = XFA_EVENTERROR_Success;
+        iRet = XFA_EventError::kSuccess;
       } else {
-        iRet = XFA_EVENTERROR_Error;
+        iRet = XFA_EventError::kError;
       }
       if (pEventParam->m_eType == XFA_EVENT_InitCalculate) {
-        if ((iRet == XFA_EVENTERROR_Success) &&
+        if ((iRet == XFA_EventError::kSuccess) &&
             (GetRawValue() != pEventParam->m_wsResult)) {
-          GetWidgetAcc()->SetValue(XFA_VALUEPICTURE_Raw,
-                                   pEventParam->m_wsResult);
-          docView->AddValidateWidget(GetWidgetAcc());
+          SetValue(XFA_VALUEPICTURE_Raw, pEventParam->m_wsResult);
+          pDocView->AddValidateNode(this);
         }
       }
       for (CXFA_Node* pRefNode : refNodes) {
@@ -2053,162 +2782,3228 @@
     }
   }
   pContext->SetNodesOfRunScript(nullptr);
+  pContext->SetEventParam(nullptr);
 
-  return {iRet, pTmpRetValue->IsBoolean() ? pTmpRetValue->ToBoolean() : false};
+  return {iRet, pTmpRetValue->IsBoolean() && pTmpRetValue->ToBoolean()};
 }
 
-WideString CXFA_Node::GetBarcodeType() {
-  CXFA_Node* pUIChild = GetWidgetAcc()->GetUIChild();
-  return pUIChild
-             ? WideString(pUIChild->JSObject()->GetCData(XFA_Attribute::Type))
-             : WideString();
+std::pair<XFA_FFWidgetType, CXFA_Ui*>
+CXFA_Node::CreateChildUIAndValueNodesIfNeeded() {
+  XFA_Element eType = GetElementType();
+  ASSERT(eType == XFA_Element::Field || eType == XFA_Element::Draw);
+
+  // Both Field and Draw have a UI property. We should always be able to
+  // retrieve or create the UI element. If we can't something is wrong.
+  CXFA_Ui* pUI = JSObject()->GetOrCreateProperty<CXFA_Ui>(0, XFA_Element::Ui);
+  ASSERT(pUI);
+
+  CXFA_Node* pUIChild = nullptr;
+  // Search through the children of the UI node to see if we have any of our
+  // One-Of entries. If so, that is the node associated with our UI.
+  for (CXFA_Node* pChild = pUI->GetFirstChild(); pChild;
+       pChild = pChild->GetNextSibling()) {
+    if (pUI->IsAOneOfChild(pChild)) {
+      pUIChild = pChild;
+      break;
+    }
+  }
+
+  XFA_FFWidgetType widget_type = XFA_FFWidgetType::kNone;
+  XFA_Element expected_ui_child_type = XFA_Element::Unknown;
+
+  // Both Field and Draw nodes have a Value child. So, we should either always
+  // have it, or always create it. If we don't get the Value child for some
+  // reason something has gone really wrong.
+  CXFA_Value* value =
+      JSObject()->GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
+  ASSERT(value);
+
+  // The Value nodes only have One-Of children. So, if we have a first child
+  // that child must be the type we want to use.
+  CXFA_Node* child = value->GetFirstChild();
+  if (child) {
+    switch (child->GetElementType()) {
+      case XFA_Element::Boolean:
+        expected_ui_child_type = XFA_Element::CheckButton;
+        break;
+      case XFA_Element::Integer:
+      case XFA_Element::Decimal:
+      case XFA_Element::Float:
+        expected_ui_child_type = XFA_Element::NumericEdit;
+        break;
+      case XFA_Element::ExData:
+      case XFA_Element::Text:
+        expected_ui_child_type = XFA_Element::TextEdit;
+        widget_type = XFA_FFWidgetType::kText;
+        break;
+      case XFA_Element::Date:
+      case XFA_Element::Time:
+      case XFA_Element::DateTime:
+        expected_ui_child_type = XFA_Element::DateTimeEdit;
+        break;
+      case XFA_Element::Image:
+        expected_ui_child_type = XFA_Element::ImageEdit;
+        widget_type = XFA_FFWidgetType::kImage;
+        break;
+      case XFA_Element::Arc:
+        expected_ui_child_type = XFA_Element::DefaultUi;
+        widget_type = XFA_FFWidgetType::kArc;
+        break;
+      case XFA_Element::Line:
+        expected_ui_child_type = XFA_Element::DefaultUi;
+        widget_type = XFA_FFWidgetType::kLine;
+        break;
+      case XFA_Element::Rectangle:
+        expected_ui_child_type = XFA_Element::DefaultUi;
+        widget_type = XFA_FFWidgetType::kRectangle;
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  if (eType == XFA_Element::Draw) {
+    if (pUIChild && pUIChild->GetElementType() == XFA_Element::TextEdit) {
+      widget_type = XFA_FFWidgetType::kText;
+    } else if (pUIChild &&
+               pUIChild->GetElementType() == XFA_Element::ImageEdit) {
+      widget_type = XFA_FFWidgetType::kImage;
+    } else if (widget_type == XFA_FFWidgetType::kNone) {
+      widget_type = XFA_FFWidgetType::kText;
+    }
+  } else if (eType == XFA_Element::Field) {
+    if (pUIChild && pUIChild->GetElementType() == XFA_Element::DefaultUi) {
+      widget_type = XFA_FFWidgetType::kTextEdit;
+    } else if (pUIChild) {
+      widget_type = pUIChild->GetDefaultFFWidgetType();
+    } else if (expected_ui_child_type == XFA_Element::Unknown) {
+      widget_type = XFA_FFWidgetType::kTextEdit;
+    }
+  } else {
+    NOTREACHED();
+  }
+
+  if (!pUIChild) {
+    if (expected_ui_child_type == XFA_Element::Unknown)
+      expected_ui_child_type = XFA_Element::TextEdit;
+    pUIChild = pUI->JSObject()->GetOrCreateProperty<CXFA_Node>(
+        0, expected_ui_child_type);
+  }
+
+  CreateValueNodeIfNeeded(value, pUIChild);
+  return {widget_type, pUI};
 }
 
-Optional<BC_CHAR_ENCODING> CXFA_Node::GetBarcodeAttribute_CharEncoding() {
-  Optional<WideString> wsCharEncoding =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryCData(
-          XFA_Attribute::CharEncoding, true);
-  if (!wsCharEncoding)
-    return {};
-  if (wsCharEncoding->CompareNoCase(L"UTF-16"))
-    return {CHAR_ENCODING_UNICODE};
-  if (wsCharEncoding->CompareNoCase(L"UTF-8"))
-    return {CHAR_ENCODING_UTF8};
-  return {};
+XFA_FFWidgetType CXFA_Node::GetDefaultFFWidgetType() const {
+  NOTREACHED();
+  return XFA_FFWidgetType::kNone;
 }
 
-Optional<bool> CXFA_Node::GetBarcodeAttribute_Checksum() {
-  Optional<XFA_AttributeEnum> checksum =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryEnum(XFA_Attribute::Checksum,
-                                                        true);
-  if (!checksum)
-    return {};
+CXFA_Node* CXFA_Node::CreateUINodeIfNeeded(CXFA_Ui* ui, XFA_Element type) {
+  return ui->JSObject()->GetOrCreateProperty<CXFA_Node>(0, type);
+}
 
-  switch (*checksum) {
-    case XFA_AttributeEnum::None:
-      return {false};
-    case XFA_AttributeEnum::Auto:
-      return {true};
-    case XFA_AttributeEnum::Checksum_1mod10:
-    case XFA_AttributeEnum::Checksum_1mod10_1mod11:
-    case XFA_AttributeEnum::Checksum_2mod10:
+void CXFA_Node::CreateValueNodeIfNeeded(CXFA_Value* value,
+                                        CXFA_Node* pUIChild) {
+  // Value nodes only have one child. If we have one already we're done.
+  if (value->GetFirstChild())
+    return;
+
+  // Create the Value node for our UI if needed.
+  XFA_Element valueType = pUIChild->GetValueNodeType();
+  if (pUIChild->GetElementType() == XFA_Element::CheckButton) {
+    CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+    if (pItems) {
+      CXFA_Node* pItem =
+          pItems->GetChild<CXFA_Node>(0, XFA_Element::Unknown, false);
+      if (pItem)
+        valueType = pItem->GetElementType();
+    }
+  }
+  value->JSObject()->GetOrCreateProperty<CXFA_Node>(0, valueType);
+}
+
+XFA_Element CXFA_Node::GetValueNodeType() const {
+  return XFA_Element::Text;
+}
+
+CXFA_Node* CXFA_Node::GetUIChildNode() {
+  ASSERT(HasCreatedUIWidget());
+
+  if (ff_widget_type_ != XFA_FFWidgetType::kNone)
+    return ui_ ? ui_->GetFirstChild() : nullptr;
+
+  XFA_Element type = GetElementType();
+  if (type == XFA_Element::Field || type == XFA_Element::Draw) {
+    std::tie(ff_widget_type_, ui_) = CreateChildUIAndValueNodesIfNeeded();
+  } else if (type == XFA_Element::Subform) {
+    ff_widget_type_ = XFA_FFWidgetType::kSubform;
+  } else if (type == XFA_Element::ExclGroup) {
+    ff_widget_type_ = XFA_FFWidgetType::kExclGroup;
+  } else {
+    NOTREACHED();
+  }
+  return ui_ ? ui_->GetFirstChild() : nullptr;
+}
+
+XFA_FFWidgetType CXFA_Node::GetFFWidgetType() {
+  GetUIChildNode();
+  return ff_widget_type_;
+}
+
+CXFA_Border* CXFA_Node::GetUIBorder() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  return pUIChild ? pUIChild->JSObject()->GetProperty<CXFA_Border>(
+                        0, XFA_Element::Border)
+                  : nullptr;
+}
+
+CFX_RectF CXFA_Node::GetUIMargin() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (!pUIChild)
+    return CFX_RectF();
+
+  CXFA_Margin* mgUI =
+      pUIChild->JSObject()->GetProperty<CXFA_Margin>(0, XFA_Element::Margin);
+  if (!mgUI)
+    return CFX_RectF();
+
+  CXFA_Border* border = GetUIBorder();
+  if (border && border->GetPresence() != XFA_AttributeValue::Visible)
+    return CFX_RectF();
+
+  Optional<float> left = mgUI->TryLeftInset();
+  Optional<float> top = mgUI->TryTopInset();
+  Optional<float> right = mgUI->TryRightInset();
+  Optional<float> bottom = mgUI->TryBottomInset();
+  if (border) {
+    bool bVisible = false;
+    float fThickness = 0;
+    XFA_AttributeValue iType = XFA_AttributeValue::Unknown;
+    std::tie(iType, bVisible, fThickness) = border->Get3DStyle();
+    if (!left || !top || !right || !bottom) {
+      std::vector<CXFA_Stroke*> strokes = border->GetStrokes();
+      if (!top)
+        top = GetEdgeThickness(strokes, bVisible, 0);
+      if (!right)
+        right = GetEdgeThickness(strokes, bVisible, 1);
+      if (!bottom)
+        bottom = GetEdgeThickness(strokes, bVisible, 2);
+      if (!left)
+        left = GetEdgeThickness(strokes, bVisible, 3);
+    }
+  }
+  return CFX_RectF(left.value_or(0.0), top.value_or(0.0), right.value_or(0.0),
+                   bottom.value_or(0.0));
+}
+
+std::vector<CXFA_Event*> CXFA_Node::GetEventByActivity(
+    XFA_AttributeValue iActivity,
+    bool bIsFormReady) {
+  std::vector<CXFA_Event*> events;
+  for (CXFA_Node* node : GetNodeListForType(XFA_Element::Event)) {
+    auto* event = static_cast<CXFA_Event*>(node);
+    if (event->GetActivity() != iActivity)
+      continue;
+
+    if (iActivity != XFA_AttributeValue::Ready) {
+      events.push_back(event);
+      continue;
+    }
+
+    WideString wsRef = event->GetRef();
+    if (bIsFormReady) {
+      if (wsRef == WideStringView(L"$form"))
+        events.push_back(event);
+      continue;
+    }
+
+    if (wsRef == WideStringView(L"$layout"))
+      events.push_back(event);
+  }
+  return events;
+}
+
+void CXFA_Node::ResetData() {
+  WideString wsValue;
+  switch (GetFFWidgetType()) {
+    case XFA_FFWidgetType::kImageEdit: {
+      CXFA_Value* imageValue = GetDefaultValueIfExists();
+      CXFA_Image* image = imageValue ? imageValue->GetImageIfExists() : nullptr;
+      WideString wsContentType, wsHref;
+      if (image) {
+        wsValue = image->GetContent();
+        wsContentType = image->GetContentType();
+        wsHref = image->GetHref();
+      }
+      SetImageEdit(wsContentType, wsHref, wsValue);
+      break;
+    }
+    case XFA_FFWidgetType::kExclGroup: {
+      CXFA_Node* pNextChild = GetFirstContainerChild();
+      while (pNextChild) {
+        CXFA_Node* pChild = pNextChild;
+        if (!pChild->IsWidgetReady())
+          continue;
+
+        bool done = false;
+        if (wsValue.IsEmpty()) {
+          CXFA_Value* defValue = pChild->GetDefaultValueIfExists();
+          if (defValue) {
+            wsValue = defValue->GetChildValueContent();
+            SetValue(XFA_VALUEPICTURE_Raw, wsValue);
+            pChild->SetValue(XFA_VALUEPICTURE_Raw, wsValue);
+            done = true;
+          }
+        }
+        if (!done) {
+          CXFA_Items* pItems =
+              pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+          if (!pItems)
+            continue;
+
+          WideString itemText;
+          if (pItems->CountChildren(XFA_Element::Unknown, false) > 1) {
+            itemText =
+                pItems->GetChild<CXFA_Node>(1, XFA_Element::Unknown, false)
+                    ->JSObject()
+                    ->GetContent(false);
+          }
+          pChild->SetValue(XFA_VALUEPICTURE_Raw, itemText);
+        }
+        pNextChild = pChild->GetNextContainerSibling();
+      }
+      break;
+    }
+    case XFA_FFWidgetType::kChoiceList:
+      ClearAllSelections();
+      FALLTHROUGH;
+    default: {
+      CXFA_Value* defValue = GetDefaultValueIfExists();
+      if (defValue)
+        wsValue = defValue->GetChildValueContent();
+
+      SetValue(XFA_VALUEPICTURE_Raw, wsValue);
+      break;
+    }
+  }
+}
+
+void CXFA_Node::SetImageEdit(const WideString& wsContentType,
+                             const WideString& wsHref,
+                             const WideString& wsData) {
+  CXFA_Value* formValue = GetFormValueIfExists();
+  CXFA_Image* image = formValue ? formValue->GetImageIfExists() : nullptr;
+  if (image) {
+    image->SetContentType(WideString(wsContentType));
+    image->SetHref(wsHref);
+  }
+
+  JSObject()->SetContent(wsData, GetFormatDataValue(wsData), true, false, true);
+
+  CXFA_Node* pBind = GetBindData();
+  if (!pBind) {
+    if (image)
+      image->SetTransferEncoding(XFA_AttributeValue::Base64);
+    return;
+  }
+  pBind->JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false,
+                              false);
+  CXFA_Node* pHrefNode = pBind->GetFirstChild();
+  if (pHrefNode) {
+    pHrefNode->JSObject()->SetCData(XFA_Attribute::Value, wsHref, false, false);
+    return;
+  }
+  CFX_XMLElement* pElement = ToXMLElement(pBind->GetXMLMappingNode());
+  pElement->SetAttribute(L"href", wsHref);
+}
+
+void CXFA_Node::CalcCaptionSize(CXFA_FFDoc* doc, CFX_SizeF* pszCap) {
+  CXFA_Caption* caption = GetCaptionIfExists();
+  if (!caption || !caption->IsVisible())
+    return;
+
+  LoadCaption(doc);
+
+  const float fCapReserve = caption->GetReserve();
+  const XFA_AttributeValue iCapPlacement = caption->GetPlacementType();
+  const bool bReserveExit = fCapReserve > 0.01;
+  const bool bVert = iCapPlacement == XFA_AttributeValue::Top ||
+                     iCapPlacement == XFA_AttributeValue::Bottom;
+  CXFA_TextLayout* pCapTextLayout =
+      m_pLayoutData->AsFieldLayoutData()->m_pCapTextLayout.get();
+  if (pCapTextLayout) {
+    if (!bVert && GetFFWidgetType() != XFA_FFWidgetType::kButton)
+      pszCap->width = fCapReserve;
+
+    CFX_SizeF minSize;
+    *pszCap = pCapTextLayout->CalcSize(minSize, *pszCap);
+    if (bReserveExit)
+      bVert ? pszCap->height = fCapReserve : pszCap->width = fCapReserve;
+  } else {
+    float fFontSize = 10.0f;
+    CXFA_Font* font = caption->GetFontIfExists();
+    if (font) {
+      fFontSize = font->GetFontSize();
+    } else {
+      CXFA_Font* widgetfont = GetFontIfExists();
+      if (widgetfont)
+        fFontSize = widgetfont->GetFontSize();
+    }
+
+    if (bVert) {
+      pszCap->height = fCapReserve > 0 ? fCapReserve : fFontSize;
+    } else {
+      pszCap->width = fCapReserve > 0 ? fCapReserve : 0;
+      pszCap->height = fFontSize;
+    }
+  }
+
+  CXFA_Margin* captionMargin = caption->GetMarginIfExists();
+  if (!captionMargin)
+    return;
+
+  float fLeftInset = captionMargin->GetLeftInset();
+  float fTopInset = captionMargin->GetTopInset();
+  float fRightInset = captionMargin->GetRightInset();
+  float fBottomInset = captionMargin->GetBottomInset();
+  if (bReserveExit) {
+    bVert ? (pszCap->width += fLeftInset + fRightInset)
+          : (pszCap->height += fTopInset + fBottomInset);
+  } else {
+    pszCap->width += fLeftInset + fRightInset;
+    pszCap->height += fTopInset + fBottomInset;
+  }
+}
+
+bool CXFA_Node::CalculateFieldAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
+  CFX_SizeF szCap;
+  CalcCaptionSize(doc, &szCap);
+
+  CFX_RectF rtUIMargin = GetUIMargin();
+  pSize->width += rtUIMargin.left + rtUIMargin.width;
+  pSize->height += rtUIMargin.top + rtUIMargin.height;
+  if (szCap.width > 0 && szCap.height > 0) {
+    CXFA_Caption* caption = GetCaptionIfExists();
+    XFA_AttributeValue placement = caption
+                                       ? caption->GetPlacementType()
+                                       : CXFA_Caption::kDefaultPlacementType;
+    switch (placement) {
+      case XFA_AttributeValue::Left:
+      case XFA_AttributeValue::Right:
+      case XFA_AttributeValue::Inline: {
+        pSize->width += szCap.width;
+        pSize->height = std::max(pSize->height, szCap.height);
+      } break;
+      case XFA_AttributeValue::Top:
+      case XFA_AttributeValue::Bottom: {
+        pSize->height += szCap.height;
+        pSize->width = std::max(pSize->width, szCap.width);
+        break;
+      }
+      default:
+        break;
+    }
+  }
+  return CalculateWidgetAutoSize(pSize);
+}
+
+bool CXFA_Node::CalculateWidgetAutoSize(CFX_SizeF* pSize) {
+  CXFA_Margin* margin = GetMarginIfExists();
+  if (margin) {
+    pSize->width += margin->GetLeftInset() + margin->GetRightInset();
+    pSize->height += margin->GetTopInset() + margin->GetBottomInset();
+  }
+
+  CXFA_Para* para = GetParaIfExists();
+  if (para)
+    pSize->width += para->GetMarginLeft() + para->GetTextIndent();
+
+  Optional<float> width = TryWidth();
+  if (width) {
+    pSize->width = *width;
+  } else {
+    Optional<float> min = TryMinWidth();
+    if (min)
+      pSize->width = std::max(pSize->width, *min);
+
+    Optional<float> max = TryMaxWidth();
+    if (max && *max > 0)
+      pSize->width = std::min(pSize->width, *max);
+  }
+
+  Optional<float> height = TryHeight();
+  if (height) {
+    pSize->height = *height;
+  } else {
+    Optional<float> min = TryMinHeight();
+    if (min)
+      pSize->height = std::max(pSize->height, *min);
+
+    Optional<float> max = TryMaxHeight();
+    if (max && *max > 0)
+      pSize->height = std::min(pSize->height, *max);
+  }
+  return true;
+}
+
+void CXFA_Node::CalculateTextContentSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
+  float fFontSize = GetFontSize();
+  WideString wsText = GetValue(XFA_VALUEPICTURE_Display);
+  if (wsText.IsEmpty()) {
+    pSize->height += fFontSize;
+    return;
+  }
+
+  if (wsText.Back() == L'\n')
+    wsText += L'\n';
+
+  CXFA_FieldLayoutData* layoutData = m_pLayoutData->AsFieldLayoutData();
+  if (!layoutData->m_pTextOut) {
+    layoutData->m_pTextOut = pdfium::MakeUnique<CFDE_TextOut>();
+    CFDE_TextOut* pTextOut = layoutData->m_pTextOut.get();
+    pTextOut->SetFont(GetFDEFont(doc));
+    pTextOut->SetFontSize(fFontSize);
+    pTextOut->SetLineBreakTolerance(fFontSize * 0.2f);
+    pTextOut->SetLineSpace(GetLineHeight());
+
+    FDE_TextStyle dwStyles;
+    dwStyles.last_line_height_ = true;
+    if (GetFFWidgetType() == XFA_FFWidgetType::kTextEdit && IsMultiLine())
+      dwStyles.line_wrap_ = true;
+
+    pTextOut->SetStyles(dwStyles);
+  }
+  layoutData->m_pTextOut->CalcLogicSize(wsText.AsStringView(), pSize);
+}
+
+bool CXFA_Node::CalculateTextEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
+  if (pSize->width > 0) {
+    CFX_SizeF szOrz = *pSize;
+    CFX_SizeF szCap;
+    CalcCaptionSize(doc, &szCap);
+    bool bCapExit = szCap.width > 0.01 && szCap.height > 0.01;
+    XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown;
+    if (bCapExit) {
+      CXFA_Caption* caption = GetCaptionIfExists();
+      iCapPlacement = caption ? caption->GetPlacementType()
+                              : CXFA_Caption::kDefaultPlacementType;
+      switch (iCapPlacement) {
+        case XFA_AttributeValue::Left:
+        case XFA_AttributeValue::Right:
+        case XFA_AttributeValue::Inline: {
+          pSize->width -= szCap.width;
+          break;
+        }
+        default:
+          break;
+      }
+    }
+    CFX_RectF rtUIMargin = GetUIMargin();
+    pSize->width -= rtUIMargin.left + rtUIMargin.width;
+    CXFA_Margin* margin = GetMarginIfExists();
+    if (margin)
+      pSize->width -= margin->GetLeftInset() + margin->GetRightInset();
+
+    CalculateTextContentSize(doc, pSize);
+    pSize->height += rtUIMargin.top + rtUIMargin.height;
+    if (bCapExit) {
+      switch (iCapPlacement) {
+        case XFA_AttributeValue::Left:
+        case XFA_AttributeValue::Right:
+        case XFA_AttributeValue::Inline: {
+          pSize->height = std::max(pSize->height, szCap.height);
+        } break;
+        case XFA_AttributeValue::Top:
+        case XFA_AttributeValue::Bottom: {
+          pSize->height += szCap.height;
+          break;
+        }
+        default:
+          break;
+      }
+    }
+    pSize->width = szOrz.width;
+    return CalculateWidgetAutoSize(pSize);
+  }
+  CalculateTextContentSize(doc, pSize);
+  return CalculateFieldAutoSize(doc, pSize);
+}
+
+bool CXFA_Node::CalculateCheckButtonAutoSize(CXFA_FFDoc* doc,
+                                             CFX_SizeF* pSize) {
+  float fCheckSize = GetCheckButtonSize();
+  *pSize = CFX_SizeF(fCheckSize, fCheckSize);
+  return CalculateFieldAutoSize(doc, pSize);
+}
+
+bool CXFA_Node::CalculatePushButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
+  CalcCaptionSize(doc, pSize);
+  return CalculateWidgetAutoSize(pSize);
+}
+
+CFX_SizeF CXFA_Node::CalculateImageSize(float img_width,
+                                        float img_height,
+                                        const CFX_Size& dpi) {
+  CFX_RectF rtImage(0, 0, XFA_UnitPx2Pt(img_width, dpi.width),
+                    XFA_UnitPx2Pt(img_height, dpi.height));
+
+  CFX_RectF rtFit;
+  Optional<float> width = TryWidth();
+  if (width) {
+    rtFit.width = *width;
+    GetWidthWithoutMargin(rtFit.width);
+  } else {
+    rtFit.width = rtImage.width;
+  }
+
+  Optional<float> height = TryHeight();
+  if (height) {
+    rtFit.height = *height;
+    GetHeightWithoutMargin(rtFit.height);
+  } else {
+    rtFit.height = rtImage.height;
+  }
+
+  return rtFit.Size();
+}
+
+bool CXFA_Node::CalculateImageAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
+  if (!GetImageImage())
+    LoadImageImage(doc);
+
+  pSize->clear();
+  RetainPtr<CFX_DIBitmap> pBitmap = GetImageImage();
+  if (!pBitmap)
+    return CalculateWidgetAutoSize(pSize);
+
+  *pSize = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
+                              GetImageDpi());
+  return CalculateWidgetAutoSize(pSize);
+}
+
+bool CXFA_Node::CalculateImageEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) {
+  if (!GetImageEditImage())
+    LoadImageEditImage(doc);
+
+  pSize->clear();
+  RetainPtr<CFX_DIBitmap> pBitmap = GetImageEditImage();
+  if (!pBitmap)
+    return CalculateFieldAutoSize(doc, pSize);
+
+  *pSize = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(),
+                              GetImageEditDpi());
+  return CalculateFieldAutoSize(doc, pSize);
+}
+
+bool CXFA_Node::LoadImageImage(CXFA_FFDoc* doc) {
+  InitLayoutData();
+  return m_pLayoutData->AsImageLayoutData()->LoadImageData(doc, this);
+}
+
+bool CXFA_Node::LoadImageEditImage(CXFA_FFDoc* doc) {
+  InitLayoutData();
+  return m_pLayoutData->AsFieldLayoutData()->AsImageEditData()->LoadImageData(
+      doc, this);
+}
+
+CFX_Size CXFA_Node::GetImageDpi() const {
+  CXFA_ImageLayoutData* pData = m_pLayoutData->AsImageLayoutData();
+  return CFX_Size(pData->m_iImageXDpi, pData->m_iImageYDpi);
+}
+
+CFX_Size CXFA_Node::GetImageEditDpi() const {
+  CXFA_ImageEditData* pData =
+      m_pLayoutData->AsFieldLayoutData()->AsImageEditData();
+  return CFX_Size(pData->m_iImageXDpi, pData->m_iImageYDpi);
+}
+
+float CXFA_Node::CalculateWidgetAutoWidth(float fWidthCalc) {
+  CXFA_Margin* margin = GetMarginIfExists();
+  if (margin)
+    fWidthCalc += margin->GetLeftInset() + margin->GetRightInset();
+
+  Optional<float> min = TryMinWidth();
+  if (min)
+    fWidthCalc = std::max(fWidthCalc, *min);
+
+  Optional<float> max = TryMaxWidth();
+  if (max && *max > 0)
+    fWidthCalc = std::min(fWidthCalc, *max);
+
+  return fWidthCalc;
+}
+
+float CXFA_Node::GetWidthWithoutMargin(float fWidthCalc) const {
+  CXFA_Margin* margin = GetMarginIfExists();
+  if (margin)
+    fWidthCalc -= margin->GetLeftInset() + margin->GetRightInset();
+  return fWidthCalc;
+}
+
+float CXFA_Node::CalculateWidgetAutoHeight(float fHeightCalc) {
+  CXFA_Margin* margin = GetMarginIfExists();
+  if (margin)
+    fHeightCalc += margin->GetTopInset() + margin->GetBottomInset();
+
+  Optional<float> min = TryMinHeight();
+  if (min)
+    fHeightCalc = std::max(fHeightCalc, *min);
+
+  Optional<float> max = TryMaxHeight();
+  if (max && *max > 0)
+    fHeightCalc = std::min(fHeightCalc, *max);
+
+  return fHeightCalc;
+}
+
+float CXFA_Node::GetHeightWithoutMargin(float fHeightCalc) const {
+  CXFA_Margin* margin = GetMarginIfExists();
+  if (margin)
+    fHeightCalc -= margin->GetTopInset() + margin->GetBottomInset();
+  return fHeightCalc;
+}
+
+void CXFA_Node::StartWidgetLayout(CXFA_FFDoc* doc,
+                                  float* pCalcWidth,
+                                  float* pCalcHeight) {
+  InitLayoutData();
+
+  if (GetFFWidgetType() == XFA_FFWidgetType::kText) {
+    m_pLayoutData->m_fWidgetHeight = TryHeight().value_or(-1);
+    StartTextLayout(doc, pCalcWidth, pCalcHeight);
+    return;
+  }
+  if (*pCalcWidth > 0 && *pCalcHeight > 0)
+    return;
+
+  m_pLayoutData->m_fWidgetHeight = -1;
+  float fWidth = 0;
+  if (*pCalcWidth > 0 && *pCalcHeight < 0) {
+    Optional<float> height = TryHeight();
+    if (height) {
+      *pCalcHeight = *height;
+    } else {
+      CFX_SizeF size = CalculateAccWidthAndHeight(doc, *pCalcWidth);
+      *pCalcWidth = size.width;
+      *pCalcHeight = size.height;
+    }
+
+    m_pLayoutData->m_fWidgetHeight = *pCalcHeight;
+    return;
+  }
+  if (*pCalcWidth < 0 && *pCalcHeight < 0) {
+    Optional<float> height;
+    Optional<float> width = TryWidth();
+    if (width) {
+      fWidth = *width;
+
+      height = TryHeight();
+      if (height)
+        *pCalcHeight = *height;
+    }
+    if (!width || !height) {
+      CFX_SizeF size = CalculateAccWidthAndHeight(doc, fWidth);
+      *pCalcWidth = size.width;
+      *pCalcHeight = size.height;
+    } else {
+      *pCalcWidth = fWidth;
+    }
+  }
+  m_pLayoutData->m_fWidgetHeight = *pCalcHeight;
+}
+
+CFX_SizeF CXFA_Node::CalculateAccWidthAndHeight(CXFA_FFDoc* doc, float fWidth) {
+  CFX_SizeF sz(fWidth, m_pLayoutData->m_fWidgetHeight);
+  switch (GetFFWidgetType()) {
+    case XFA_FFWidgetType::kBarcode:
+    case XFA_FFWidgetType::kChoiceList:
+    case XFA_FFWidgetType::kSignature:
+      CalculateFieldAutoSize(doc, &sz);
+      break;
+    case XFA_FFWidgetType::kImageEdit:
+      CalculateImageEditAutoSize(doc, &sz);
+      break;
+    case XFA_FFWidgetType::kButton:
+      CalculatePushButtonAutoSize(doc, &sz);
+      break;
+    case XFA_FFWidgetType::kCheckButton:
+      CalculateCheckButtonAutoSize(doc, &sz);
+      break;
+    case XFA_FFWidgetType::kDateTimeEdit:
+    case XFA_FFWidgetType::kNumericEdit:
+    case XFA_FFWidgetType::kPasswordEdit:
+    case XFA_FFWidgetType::kTextEdit:
+      CalculateTextEditAutoSize(doc, &sz);
+      break;
+    case XFA_FFWidgetType::kImage:
+      CalculateImageAutoSize(doc, &sz);
+      break;
+    case XFA_FFWidgetType::kArc:
+    case XFA_FFWidgetType::kLine:
+    case XFA_FFWidgetType::kRectangle:
+    case XFA_FFWidgetType::kSubform:
+    case XFA_FFWidgetType::kExclGroup:
+      CalculateWidgetAutoSize(&sz);
+      break;
+    case XFA_FFWidgetType::kText:
+    case XFA_FFWidgetType::kNone:
+      break;
+  }
+
+  m_pLayoutData->m_fWidgetHeight = sz.height;
+  return sz;
+}
+
+Optional<float> CXFA_Node::FindSplitPos(CXFA_FFDocView* pDocView,
+                                        size_t szBlockIndex,
+                                        float fCalcHeight) {
+  if (GetFFWidgetType() == XFA_FFWidgetType::kSubform)
+    return pdfium::nullopt;
+
+  switch (GetFFWidgetType()) {
+    case XFA_FFWidgetType::kText:
+    case XFA_FFWidgetType::kTextEdit:
+    case XFA_FFWidgetType::kNumericEdit:
+    case XFA_FFWidgetType::kPasswordEdit:
+      break;
+    default:
+      return 0.0f;
+  }
+
+  float fTopInset = 0;
+  float fBottomInset = 0;
+  if (szBlockIndex == 0) {
+    CXFA_Margin* margin = GetMarginIfExists();
+    if (margin) {
+      fTopInset = margin->GetTopInset();
+      fBottomInset = margin->GetBottomInset();
+    }
+
+    CFX_RectF rtUIMargin = GetUIMargin();
+    fTopInset += rtUIMargin.top;
+    fBottomInset += rtUIMargin.width;
+  }
+  if (GetFFWidgetType() == XFA_FFWidgetType::kText) {
+    float fHeight = fCalcHeight;
+    if (szBlockIndex == 0) {
+      fCalcHeight -= fTopInset;
+      fCalcHeight = std::max(fCalcHeight, 0.0f);
+    }
+    CXFA_TextLayout* pTextLayout =
+        m_pLayoutData->AsTextLayoutData()->GetTextLayout();
+    fCalcHeight = pTextLayout->DoSplitLayout(
+        szBlockIndex, fCalcHeight, m_pLayoutData->m_fWidgetHeight - fTopInset);
+    if (fCalcHeight != 0) {
+      if (szBlockIndex == 0)
+        fCalcHeight += fTopInset;
+      if (fabs(fHeight - fCalcHeight) < kXFAWidgetPrecision)
+        return pdfium::nullopt;
+    }
+    return fCalcHeight;
+  }
+
+  XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown;
+  float fCapReserve = 0;
+  if (szBlockIndex == 0) {
+    CXFA_Caption* caption = GetCaptionIfExists();
+    if (caption && !caption->IsHidden()) {
+      iCapPlacement = caption->GetPlacementType();
+      fCapReserve = caption->GetReserve();
+    }
+    if (iCapPlacement == XFA_AttributeValue::Top &&
+        fCalcHeight < fCapReserve + fTopInset) {
+      return 0.0f;
+    }
+    if (iCapPlacement == XFA_AttributeValue::Bottom &&
+        m_pLayoutData->m_fWidgetHeight - fCapReserve - fBottomInset) {
+      return 0.0f;
+    }
+    if (iCapPlacement != XFA_AttributeValue::Top)
+      fCapReserve = 0;
+  }
+  CXFA_FieldLayoutData* pFieldData = m_pLayoutData->AsFieldLayoutData();
+  int32_t iLinesCount = 0;
+  float fHeight = m_pLayoutData->m_fWidgetHeight;
+  if (GetValue(XFA_VALUEPICTURE_Display).IsEmpty()) {
+    iLinesCount = 1;
+  } else {
+    if (!pFieldData->m_pTextOut) {
+      CFX_SizeF size = CalculateAccWidthAndHeight(pDocView->GetDoc(),
+                                                  TryWidth().value_or(0));
+      fHeight = size.height;
+    }
+
+    iLinesCount = pFieldData->m_pTextOut->GetTotalLines();
+  }
+  std::vector<float>* pFieldArray = &pFieldData->m_FieldSplitArray;
+  size_t szFieldSplitCount = pFieldArray->size();
+  if (szFieldSplitCount < szBlockIndex * 3)
+    return pdfium::nullopt;
+
+  for (size_t i = 0; i < szBlockIndex * 3; i += 3) {
+    iLinesCount -= (int32_t)(*pFieldArray)[i + 1];
+    fHeight -= (*pFieldArray)[i + 2];
+  }
+  if (iLinesCount == 0)
+    return pdfium::nullopt;
+
+  float fLineHeight = GetLineHeight();
+  float fFontSize = GetFontSize();
+  float fTextHeight = iLinesCount * fLineHeight - fLineHeight + fFontSize;
+  float fSpaceAbove = 0;
+  float fStartOffset = 0;
+  if (fHeight > 0.1f && szBlockIndex == 0) {
+    fStartOffset = fTopInset;
+    fHeight -= (fTopInset + fBottomInset);
+    CXFA_Para* para = GetParaIfExists();
+    if (para) {
+      fSpaceAbove = para->GetSpaceAbove();
+      float fSpaceBelow = para->GetSpaceBelow();
+      fHeight -= (fSpaceAbove + fSpaceBelow);
+      switch (para->GetVerticalAlign()) {
+        case XFA_AttributeValue::Top:
+          fStartOffset += fSpaceAbove;
+          break;
+        case XFA_AttributeValue::Middle:
+          fStartOffset += ((fHeight - fTextHeight) / 2 + fSpaceAbove);
+          break;
+        case XFA_AttributeValue::Bottom:
+          fStartOffset += (fHeight - fTextHeight + fSpaceAbove);
+          break;
+        default:
+          NOTREACHED();
+          break;
+      }
+    }
+    if (fStartOffset < 0.1f)
+      fStartOffset = 0;
+  }
+  if (szBlockIndex > 0) {
+    size_t i = szBlockIndex - 1;
+    fStartOffset = (*pFieldArray)[i * 3] - (*pFieldArray)[i * 3 + 2];
+    if (fStartOffset < 0.1f)
+      fStartOffset = 0;
+  }
+  if (szFieldSplitCount / 3 == (szBlockIndex + 1))
+    (*pFieldArray)[0] = fStartOffset;
+  else
+    pFieldArray->push_back(fStartOffset);
+
+  XFA_VERSION version = pDocView->GetDoc()->GetXFADoc()->GetCurVersionMode();
+  bool bCanSplitNoContent = false;
+  auto value = GetParent()->JSObject()->TryEnum(XFA_Attribute::Layout, true);
+  XFA_AttributeValue eLayoutMode = value.value_or(XFA_AttributeValue::Position);
+  if ((eLayoutMode == XFA_AttributeValue::Position ||
+       eLayoutMode == XFA_AttributeValue::Tb ||
+       eLayoutMode == XFA_AttributeValue::Row ||
+       eLayoutMode == XFA_AttributeValue::Table) &&
+      version > XFA_VERSION_208) {
+    bCanSplitNoContent = true;
+  }
+  if ((eLayoutMode == XFA_AttributeValue::Tb ||
+       eLayoutMode == XFA_AttributeValue::Row ||
+       eLayoutMode == XFA_AttributeValue::Table) &&
+      version <= XFA_VERSION_208) {
+    if (fStartOffset >= fCalcHeight)
+      return 0.0f;
+
+    bCanSplitNoContent = true;
+  }
+  if (!bCanSplitNoContent ||
+      fCalcHeight - fTopInset - fSpaceAbove < fLineHeight) {
+    return 0.0f;
+  }
+
+  if (fStartOffset + kXFAWidgetPrecision >= fCalcHeight) {
+    if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
+      (*pFieldArray)[szBlockIndex * 3 + 1] = 0;
+      (*pFieldArray)[szBlockIndex * 3 + 2] = fCalcHeight;
+    } else {
+      pFieldArray->push_back(0);
+      pFieldArray->push_back(fCalcHeight);
+    }
+    return pdfium::nullopt;
+  }
+
+  if (fCalcHeight - fStartOffset < fLineHeight) {
+    fCalcHeight = fStartOffset;
+    if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
+      (*pFieldArray)[szBlockIndex * 3 + 1] = 0;
+      (*pFieldArray)[szBlockIndex * 3 + 2] = fCalcHeight;
+    } else {
+      pFieldArray->push_back(0);
+      pFieldArray->push_back(fCalcHeight);
+    }
+    return fCalcHeight;
+  }
+
+  float fTextNum =
+      fCalcHeight + kXFAWidgetPrecision - fCapReserve - fStartOffset;
+  int32_t iLineNum =
+      (int32_t)((fTextNum + (fLineHeight - fFontSize)) / fLineHeight);
+  if (iLineNum >= iLinesCount) {
+    if (fCalcHeight - fStartOffset - fTextHeight >= fFontSize) {
+      if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
+        (*pFieldArray)[szBlockIndex * 3 + 1] = iLinesCount;
+        (*pFieldArray)[szBlockIndex * 3 + 2] = fCalcHeight;
+      } else {
+        pFieldArray->push_back(iLinesCount);
+        pFieldArray->push_back(fCalcHeight);
+      }
+      return pdfium::nullopt;
+    }
+    if (fHeight - fStartOffset - fTextHeight < fFontSize) {
+      iLineNum -= 1;
+      if (iLineNum == 0)
+        return 0.0f;
+    } else {
+      iLineNum = (int32_t)(fTextNum / fLineHeight);
+    }
+  }
+  if (iLineNum <= 0)
+    return 0.0f;
+
+  float fSplitHeight = iLineNum * fLineHeight + fCapReserve + fStartOffset;
+  if (szFieldSplitCount / 3 == (szBlockIndex + 1)) {
+    (*pFieldArray)[szBlockIndex * 3 + 1] = iLineNum;
+    (*pFieldArray)[szBlockIndex * 3 + 2] = fSplitHeight;
+  } else {
+    pFieldArray->push_back(iLineNum);
+    pFieldArray->push_back(fSplitHeight);
+  }
+  if (fabs(fSplitHeight - fCalcHeight) < kXFAWidgetPrecision)
+    return pdfium::nullopt;
+  return fSplitHeight;
+}
+
+void CXFA_Node::InitLayoutData() {
+  if (m_pLayoutData)
+    return;
+
+  switch (GetFFWidgetType()) {
+    case XFA_FFWidgetType::kText:
+      m_pLayoutData = pdfium::MakeUnique<CXFA_TextLayoutData>();
+      return;
+    case XFA_FFWidgetType::kTextEdit:
+      m_pLayoutData = pdfium::MakeUnique<CXFA_TextEditData>();
+      return;
+    case XFA_FFWidgetType::kImage:
+      m_pLayoutData = pdfium::MakeUnique<CXFA_ImageLayoutData>();
+      return;
+    case XFA_FFWidgetType::kImageEdit:
+      m_pLayoutData = pdfium::MakeUnique<CXFA_ImageEditData>();
+      return;
     default:
       break;
   }
+  if (GetElementType() == XFA_Element::Field) {
+    m_pLayoutData = pdfium::MakeUnique<CXFA_FieldLayoutData>();
+    return;
+  }
+  m_pLayoutData = pdfium::MakeUnique<CXFA_WidgetLayoutData>();
+}
+
+void CXFA_Node::StartTextLayout(CXFA_FFDoc* doc,
+                                float* pCalcWidth,
+                                float* pCalcHeight) {
+  InitLayoutData();
+
+  CXFA_TextLayoutData* pTextLayoutData = m_pLayoutData->AsTextLayoutData();
+  pTextLayoutData->LoadText(doc, this);
+
+  CXFA_TextLayout* pTextLayout = pTextLayoutData->GetTextLayout();
+  float fTextHeight = 0;
+  if (*pCalcWidth > 0 && *pCalcHeight > 0) {
+    float fWidth = GetWidthWithoutMargin(*pCalcWidth);
+    pTextLayout->StartLayout(fWidth);
+    fTextHeight = *pCalcHeight;
+    fTextHeight = GetHeightWithoutMargin(fTextHeight);
+    pTextLayout->DoLayout(fTextHeight);
+    return;
+  }
+  if (*pCalcWidth > 0 && *pCalcHeight < 0) {
+    float fWidth = GetWidthWithoutMargin(*pCalcWidth);
+    pTextLayout->StartLayout(fWidth);
+  }
+  if (*pCalcWidth < 0 && *pCalcHeight < 0) {
+    Optional<float> width = TryWidth();
+    if (width) {
+      pTextLayout->StartLayout(GetWidthWithoutMargin(*width));
+      *pCalcWidth = *width;
+    } else {
+      float fMaxWidth = CalculateWidgetAutoWidth(pTextLayout->StartLayout(-1));
+      pTextLayout->StartLayout(GetWidthWithoutMargin(fMaxWidth));
+      *pCalcWidth = fMaxWidth;
+    }
+  }
+  if (m_pLayoutData->m_fWidgetHeight < 0) {
+    m_pLayoutData->m_fWidgetHeight = pTextLayout->GetLayoutHeight();
+    m_pLayoutData->m_fWidgetHeight =
+        CalculateWidgetAutoHeight(m_pLayoutData->m_fWidgetHeight);
+  }
+  fTextHeight = m_pLayoutData->m_fWidgetHeight;
+  fTextHeight = GetHeightWithoutMargin(fTextHeight);
+  pTextLayout->DoLayout(fTextHeight);
+  *pCalcHeight = m_pLayoutData->m_fWidgetHeight;
+}
+
+bool CXFA_Node::LoadCaption(CXFA_FFDoc* doc) {
+  InitLayoutData();
+  return m_pLayoutData->AsFieldLayoutData()->LoadCaption(doc, this);
+}
+
+CXFA_TextLayout* CXFA_Node::GetCaptionTextLayout() {
+  return m_pLayoutData
+             ? m_pLayoutData->AsFieldLayoutData()->m_pCapTextLayout.get()
+             : nullptr;
+}
+
+CXFA_TextLayout* CXFA_Node::GetTextLayout() {
+  return m_pLayoutData ? m_pLayoutData->AsTextLayoutData()->GetTextLayout()
+                       : nullptr;
+}
+
+RetainPtr<CFX_DIBitmap> CXFA_Node::GetImageImage() {
+  return m_pLayoutData ? m_pLayoutData->AsImageLayoutData()->m_pDIBitmap
+                       : nullptr;
+}
+
+RetainPtr<CFX_DIBitmap> CXFA_Node::GetImageEditImage() {
+  return m_pLayoutData ? m_pLayoutData->AsFieldLayoutData()
+                             ->AsImageEditData()
+                             ->m_pDIBitmap
+                       : nullptr;
+}
+
+void CXFA_Node::SetImageImage(const RetainPtr<CFX_DIBitmap>& newImage) {
+  CXFA_ImageLayoutData* pData = m_pLayoutData->AsImageLayoutData();
+  if (pData->m_pDIBitmap != newImage)
+    pData->m_pDIBitmap = newImage;
+}
+
+void CXFA_Node::SetImageEditImage(const RetainPtr<CFX_DIBitmap>& newImage) {
+  CXFA_ImageEditData* pData =
+      m_pLayoutData->AsFieldLayoutData()->AsImageEditData();
+  if (pData->m_pDIBitmap != newImage)
+    pData->m_pDIBitmap = newImage;
+}
+
+RetainPtr<CFGAS_GEFont> CXFA_Node::GetFDEFont(CXFA_FFDoc* doc) {
+  WideString wsFontName = L"Courier";
+  uint32_t dwFontStyle = 0;
+  CXFA_Font* font = GetFontIfExists();
+  if (font) {
+    if (font->IsBold())
+      dwFontStyle |= FXFONT_FORCE_BOLD;
+    if (font->IsItalic())
+      dwFontStyle |= FXFONT_ITALIC;
+
+    wsFontName = font->GetTypeface();
+  }
+  return doc->GetApp()->GetXFAFontMgr()->GetFont(doc, wsFontName.AsStringView(),
+                                                 dwFontStyle);
+}
+
+bool CXFA_Node::HasButtonRollover() {
+  CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+  if (!pItems)
+    return false;
+
+  for (CXFA_Node* pText = pItems->GetFirstChild(); pText;
+       pText = pText->GetNextSibling()) {
+    if (pText->JSObject()
+            ->GetCData(XFA_Attribute::Name)
+            .EqualsASCII("rollover")) {
+      return !pText->JSObject()->GetContent(false).IsEmpty();
+    }
+  }
+  return false;
+}
+
+bool CXFA_Node::HasButtonDown() {
+  CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+  if (!pItems)
+    return false;
+
+  for (CXFA_Node* pText = pItems->GetFirstChild(); pText;
+       pText = pText->GetNextSibling()) {
+    if (pText->JSObject()->GetCData(XFA_Attribute::Name).EqualsASCII("down")) {
+      return !pText->JSObject()->GetContent(false).IsEmpty();
+    }
+  }
+  return false;
+}
+
+bool CXFA_Node::IsRadioButton() {
+  CXFA_Node* pParent = GetParent();
+  return pParent && pParent->GetElementType() == XFA_Element::ExclGroup;
+}
+
+float CXFA_Node::GetCheckButtonSize() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (pUIChild) {
+    return pUIChild->JSObject()->GetMeasureInUnit(XFA_Attribute::Size,
+                                                  XFA_Unit::Pt);
+  }
+  return CXFA_Measurement(10, XFA_Unit::Pt).ToUnit(XFA_Unit::Pt);
+}
+
+XFA_CHECKSTATE CXFA_Node::GetCheckState() {
+  WideString wsValue = GetRawValue();
+  if (wsValue.IsEmpty())
+    return XFA_CHECKSTATE_Off;
+
+  auto* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+  if (!pItems)
+    return XFA_CHECKSTATE_Off;
+
+  CXFA_Node* pText = pItems->GetFirstChild();
+  int32_t i = 0;
+  while (pText) {
+    Optional<WideString> wsContent = pText->JSObject()->TryContent(false, true);
+    if (wsContent && *wsContent == wsValue)
+      return static_cast<XFA_CHECKSTATE>(i);
+
+    i++;
+    pText = pText->GetNextSibling();
+  }
+  return XFA_CHECKSTATE_Off;
+}
+
+void CXFA_Node::SetCheckState(XFA_CHECKSTATE eCheckState, bool bNotify) {
+  CXFA_Node* node = GetExclGroupIfExists();
+  if (!node) {
+    CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+    if (!pItems)
+      return;
+
+    int32_t i = -1;
+    CXFA_Node* pText = pItems->GetFirstChild();
+    WideString wsContent;
+    while (pText) {
+      i++;
+      if (i == eCheckState) {
+        wsContent = pText->JSObject()->GetContent(false);
+        break;
+      }
+      pText = pText->GetNextSibling();
+    }
+    SyncValue(wsContent, bNotify);
+
+    return;
+  }
+
+  WideString wsValue;
+  if (eCheckState != XFA_CHECKSTATE_Off) {
+    if (CXFA_Items* pItems =
+            GetChild<CXFA_Items>(0, XFA_Element::Items, false)) {
+      CXFA_Node* pText = pItems->GetFirstChild();
+      if (pText)
+        wsValue = pText->JSObject()->GetContent(false);
+    }
+  }
+  CXFA_Node* pChild = node->GetFirstChild();
+  for (; pChild; pChild = pChild->GetNextSibling()) {
+    if (pChild->GetElementType() != XFA_Element::Field)
+      continue;
+
+    CXFA_Items* pItem =
+        pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+    if (!pItem)
+      continue;
+
+    CXFA_Node* pItemchild = pItem->GetFirstChild();
+    if (!pItemchild)
+      continue;
+
+    WideString text = pItemchild->JSObject()->GetContent(false);
+    WideString wsChildValue = text;
+    if (wsValue != text) {
+      pItemchild = pItemchild->GetNextSibling();
+      if (pItemchild)
+        wsChildValue = pItemchild->JSObject()->GetContent(false);
+      else
+        wsChildValue.clear();
+    }
+    pChild->SyncValue(wsChildValue, bNotify);
+  }
+  node->SyncValue(wsValue, bNotify);
+}
+
+CXFA_Node* CXFA_Node::GetSelectedMember() {
+  CXFA_Node* pSelectedMember = nullptr;
+  WideString wsState = GetRawValue();
+  if (wsState.IsEmpty())
+    return pSelectedMember;
+
+  for (CXFA_Node* pNode = ToNode(GetFirstChild()); pNode;
+       pNode = pNode->GetNextSibling()) {
+    if (pNode->GetCheckState() == XFA_CHECKSTATE_On) {
+      pSelectedMember = pNode;
+      break;
+    }
+  }
+  return pSelectedMember;
+}
+
+CXFA_Node* CXFA_Node::SetSelectedMember(WideStringView wsName, bool bNotify) {
+  uint32_t nameHash = FX_HashCode_GetW(wsName, false);
+  for (CXFA_Node* pNode = ToNode(GetFirstChild()); pNode;
+       pNode = pNode->GetNextSibling()) {
+    if (pNode->GetNameHash() == nameHash) {
+      pNode->SetCheckState(XFA_CHECKSTATE_On, bNotify);
+      return pNode;
+    }
+  }
+  return nullptr;
+}
+
+void CXFA_Node::SetSelectedMemberByValue(WideStringView wsValue,
+                                         bool bNotify,
+                                         bool bScriptModify,
+                                         bool bSyncData) {
+  WideString wsExclGroup;
+  for (CXFA_Node* pNode = GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
+    if (pNode->GetElementType() != XFA_Element::Field)
+      continue;
+
+    CXFA_Items* pItem =
+        pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
+    if (!pItem)
+      continue;
+
+    CXFA_Node* pItemchild = pItem->GetFirstChild();
+    if (!pItemchild)
+      continue;
+
+    WideString wsChildValue = pItemchild->JSObject()->GetContent(false);
+    if (wsValue != wsChildValue) {
+      pItemchild = pItemchild->GetNextSibling();
+      if (pItemchild)
+        wsChildValue = pItemchild->JSObject()->GetContent(false);
+      else
+        wsChildValue.clear();
+    } else {
+      wsExclGroup = wsValue;
+    }
+    pNode->JSObject()->SetContent(wsChildValue, wsChildValue, bNotify,
+                                  bScriptModify, false);
+  }
+  JSObject()->SetContent(wsExclGroup, wsExclGroup, bNotify, bScriptModify,
+                         bSyncData);
+}
+
+CXFA_Node* CXFA_Node::GetExclGroupFirstMember() {
+  CXFA_Node* pNode = GetFirstChild();
+  while (pNode) {
+    if (pNode->GetElementType() == XFA_Element::Field)
+      return pNode;
+
+    pNode = pNode->GetNextSibling();
+  }
+  return nullptr;
+}
+
+CXFA_Node* CXFA_Node::GetExclGroupNextMember(CXFA_Node* pNode) {
+  if (!pNode)
+    return nullptr;
+
+  CXFA_Node* pNodeField = pNode->GetNextSibling();
+  while (pNodeField) {
+    if (pNodeField->GetElementType() == XFA_Element::Field)
+      return pNodeField;
+
+    pNodeField = pNodeField->GetNextSibling();
+  }
+  return nullptr;
+}
+
+bool CXFA_Node::IsChoiceListCommitOnSelect() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (pUIChild) {
+    return pUIChild->JSObject()->GetEnum(XFA_Attribute::CommitOn) ==
+           XFA_AttributeValue::Select;
+  }
+  return true;
+}
+
+bool CXFA_Node::IsChoiceListAllowTextEntry() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::TextEntry);
+}
+
+bool CXFA_Node::IsChoiceListMultiSelect() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (pUIChild) {
+    return pUIChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
+           XFA_AttributeValue::MultiSelect;
+  }
+  return false;
+}
+
+bool CXFA_Node::IsListBox() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (!pUIChild)
+    return false;
+
+  XFA_AttributeValue attr = pUIChild->JSObject()->GetEnum(XFA_Attribute::Open);
+  return attr == XFA_AttributeValue::Always ||
+         attr == XFA_AttributeValue::MultiSelect;
+}
+
+int32_t CXFA_Node::CountChoiceListItems(bool bSaveValue) {
+  std::vector<CXFA_Node*> pItems;
+  int32_t iCount = 0;
+  for (CXFA_Node* pNode = GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
+    if (pNode->GetElementType() != XFA_Element::Items)
+      continue;
+    iCount++;
+    pItems.push_back(pNode);
+    if (iCount == 2)
+      break;
+  }
+  if (iCount == 0)
+    return 0;
+
+  CXFA_Node* pItem = pItems[0];
+  if (iCount > 1) {
+    bool bItemOneHasSave =
+        pItems[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
+    bool bItemTwoHasSave =
+        pItems[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
+    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave)
+      pItem = pItems[1];
+  }
+  return pItem->CountChildren(XFA_Element::Unknown, false);
+}
+
+Optional<WideString> CXFA_Node::GetChoiceListItem(int32_t nIndex,
+                                                  bool bSaveValue) {
+  std::vector<CXFA_Node*> pItemsArray;
+  int32_t iCount = 0;
+  for (CXFA_Node* pNode = GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
+    if (pNode->GetElementType() != XFA_Element::Items)
+      continue;
+
+    ++iCount;
+    pItemsArray.push_back(pNode);
+    if (iCount == 2)
+      break;
+  }
+  if (iCount == 0)
+    return {};
+
+  CXFA_Node* pItems = pItemsArray[0];
+  if (iCount > 1) {
+    bool bItemOneHasSave =
+        pItemsArray[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
+    bool bItemTwoHasSave =
+        pItemsArray[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
+    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave)
+      pItems = pItemsArray[1];
+  }
+  if (!pItems)
+    return {};
+
+  CXFA_Node* pItem =
+      pItems->GetChild<CXFA_Node>(nIndex, XFA_Element::Unknown, false);
+  if (pItem)
+    return {pItem->JSObject()->GetContent(false)};
   return {};
 }
 
-Optional<int32_t> CXFA_Node::GetBarcodeAttribute_DataLength() {
-  Optional<WideString> wsDataLength =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryCData(
-          XFA_Attribute::DataLength, true);
-  if (!wsDataLength)
-    return {};
+std::vector<WideString> CXFA_Node::GetChoiceListItems(bool bSaveValue) {
+  std::vector<CXFA_Node*> items;
+  for (CXFA_Node* pNode = GetFirstChild(); pNode && items.size() < 2;
+       pNode = pNode->GetNextSibling()) {
+    if (pNode->GetElementType() == XFA_Element::Items)
+      items.push_back(pNode);
+  }
+  if (items.empty())
+    return std::vector<WideString>();
 
-  return {FXSYS_wtoi(wsDataLength->c_str())};
+  CXFA_Node* pItem = items.front();
+  if (items.size() > 1) {
+    bool bItemOneHasSave =
+        items[0]->JSObject()->GetBoolean(XFA_Attribute::Save);
+    bool bItemTwoHasSave =
+        items[1]->JSObject()->GetBoolean(XFA_Attribute::Save);
+    if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave)
+      pItem = items[1];
+  }
+
+  std::vector<WideString> wsTextArray;
+  for (CXFA_Node* pNode = pItem->GetFirstChild(); pNode;
+       pNode = pNode->GetNextSibling()) {
+    wsTextArray.emplace_back(pNode->JSObject()->GetContent(false));
+  }
+  return wsTextArray;
 }
 
-Optional<char> CXFA_Node::GetBarcodeAttribute_StartChar() {
-  Optional<WideString> wsStartEndChar =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryCData(
-          XFA_Attribute::StartChar, true);
-  if (!wsStartEndChar || wsStartEndChar->IsEmpty())
-    return {};
+int32_t CXFA_Node::CountSelectedItems() {
+  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
+  if (IsListBox() || !IsChoiceListAllowTextEntry())
+    return pdfium::CollectionSize<int32_t>(wsValueArray);
 
-  return {static_cast<char>((*wsStartEndChar)[0])};
+  int32_t iSelected = 0;
+  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
+  for (const auto& value : wsValueArray) {
+    if (pdfium::ContainsValue(wsSaveTextArray, value))
+      iSelected++;
+  }
+  return iSelected;
 }
 
-Optional<char> CXFA_Node::GetBarcodeAttribute_EndChar() {
-  Optional<WideString> wsStartEndChar =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryCData(XFA_Attribute::EndChar,
-                                                         true);
-  if (!wsStartEndChar || wsStartEndChar->IsEmpty())
-    return {};
+int32_t CXFA_Node::GetSelectedItem(int32_t nIndex) {
+  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
+  if (!pdfium::IndexInBounds(wsValueArray, nIndex))
+    return -1;
 
-  return {static_cast<char>((*wsStartEndChar)[0])};
+  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
+  auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(),
+                      wsValueArray[nIndex]);
+  return it != wsSaveTextArray.end() ? it - wsSaveTextArray.begin() : -1;
 }
 
-Optional<int32_t> CXFA_Node::GetBarcodeAttribute_ECLevel() {
-  Optional<WideString> wsECLevel =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryCData(
-          XFA_Attribute::ErrorCorrectionLevel, true);
-  if (!wsECLevel)
-    return {};
-  return {FXSYS_wtoi(wsECLevel->c_str())};
+std::vector<int32_t> CXFA_Node::GetSelectedItems() {
+  std::vector<int32_t> iSelArray;
+  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
+  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
+  for (const auto& value : wsValueArray) {
+    auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(), value);
+    if (it != wsSaveTextArray.end())
+      iSelArray.push_back(it - wsSaveTextArray.begin());
+  }
+  return iSelArray;
 }
 
-Optional<int32_t> CXFA_Node::GetBarcodeAttribute_ModuleWidth() {
-  Optional<CXFA_Measurement> moduleWidthHeight =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryMeasure(
-          XFA_Attribute::ModuleWidth, true);
-  if (!moduleWidthHeight)
-    return {};
+std::vector<WideString> CXFA_Node::GetSelectedItemsValue() {
+  WideString wsValue = GetRawValue();
+  if (IsChoiceListMultiSelect())
+    return fxcrt::Split(wsValue, L'\n');
 
-  return {static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt))};
+  std::vector<WideString> wsSelTextArray;
+  wsSelTextArray.push_back(wsValue);
+  return wsSelTextArray;
 }
 
-Optional<int32_t> CXFA_Node::GetBarcodeAttribute_ModuleHeight() {
-  Optional<CXFA_Measurement> moduleWidthHeight =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryMeasure(
-          XFA_Attribute::ModuleHeight, true);
-  if (!moduleWidthHeight)
-    return {};
-
-  return {static_cast<int32_t>(moduleWidthHeight->ToUnit(XFA_Unit::Pt))};
+bool CXFA_Node::GetItemState(int32_t nIndex) {
+  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
+  return pdfium::IndexInBounds(wsSaveTextArray, nIndex) &&
+         pdfium::ContainsValue(GetSelectedItemsValue(),
+                               wsSaveTextArray[nIndex]);
 }
 
-Optional<bool> CXFA_Node::GetBarcodeAttribute_PrintChecksum() {
-  return GetWidgetAcc()->GetUIChild()->JSObject()->TryBoolean(
-      XFA_Attribute::PrintCheckDigit, true);
+void CXFA_Node::SetItemState(int32_t nIndex,
+                             bool bSelected,
+                             bool bNotify,
+                             bool bScriptModify,
+                             bool bSyncData) {
+  std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
+  if (!pdfium::IndexInBounds(wsSaveTextArray, nIndex))
+    return;
+
+  int32_t iSel = -1;
+  std::vector<WideString> wsValueArray = GetSelectedItemsValue();
+  auto value_iter = std::find(wsValueArray.begin(), wsValueArray.end(),
+                              wsSaveTextArray[nIndex]);
+  if (value_iter != wsValueArray.end())
+    iSel = value_iter - wsValueArray.begin();
+
+  if (IsChoiceListMultiSelect()) {
+    if (bSelected) {
+      if (iSel < 0) {
+        WideString wsValue = GetRawValue();
+        if (!wsValue.IsEmpty()) {
+          wsValue += L"\n";
+        }
+        wsValue += wsSaveTextArray[nIndex];
+        JSObject()->SetContent(wsValue, wsValue, bNotify, bScriptModify,
+                               bSyncData);
+      }
+    } else if (iSel >= 0) {
+      std::vector<int32_t> iSelArray = GetSelectedItems();
+      auto selected_iter =
+          std::find(iSelArray.begin(), iSelArray.end(), nIndex);
+      if (selected_iter != iSelArray.end())
+        iSelArray.erase(selected_iter);
+      SetSelectedItems(iSelArray, bNotify, bScriptModify, bSyncData);
+    }
+  } else {
+    if (bSelected) {
+      if (iSel < 0) {
+        WideString wsSaveText = wsSaveTextArray[nIndex];
+        JSObject()->SetContent(wsSaveText, GetFormatDataValue(wsSaveText),
+                               bNotify, bScriptModify, bSyncData);
+      }
+    } else if (iSel >= 0) {
+      JSObject()->SetContent(WideString(), WideString(), bNotify, bScriptModify,
+                             bSyncData);
+    }
+  }
 }
 
-Optional<BC_TEXT_LOC> CXFA_Node::GetBarcodeAttribute_TextLocation() {
-  Optional<XFA_AttributeEnum> textLocation =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryEnum(
-          XFA_Attribute::TextLocation, true);
-  if (!textLocation)
-    return {};
+void CXFA_Node::SetSelectedItems(const std::vector<int32_t>& iSelArray,
+                                 bool bNotify,
+                                 bool bScriptModify,
+                                 bool bSyncData) {
+  WideString wsValue;
+  int32_t iSize = pdfium::CollectionSize<int32_t>(iSelArray);
+  if (iSize >= 1) {
+    std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true);
+    WideString wsItemValue;
+    for (int32_t i = 0; i < iSize; i++) {
+      wsItemValue = (iSize == 1) ? wsSaveTextArray[iSelArray[i]]
+                                 : wsSaveTextArray[iSelArray[i]] + L"\n";
+      wsValue += wsItemValue;
+    }
+  }
+  WideString wsFormat(wsValue);
+  if (!IsChoiceListMultiSelect())
+    wsFormat = GetFormatDataValue(wsValue);
 
-  switch (*textLocation) {
-    case XFA_AttributeEnum::None:
-      return {BC_TEXT_LOC_NONE};
-    case XFA_AttributeEnum::Above:
-      return {BC_TEXT_LOC_ABOVE};
-    case XFA_AttributeEnum::Below:
-      return {BC_TEXT_LOC_BELOW};
-    case XFA_AttributeEnum::AboveEmbedded:
-      return {BC_TEXT_LOC_ABOVEEMBED};
-    case XFA_AttributeEnum::BelowEmbedded:
-      return {BC_TEXT_LOC_BELOWEMBED};
+  JSObject()->SetContent(wsValue, wsFormat, bNotify, bScriptModify, bSyncData);
+}
+
+void CXFA_Node::ClearAllSelections() {
+  CXFA_Node* pBind = GetBindData();
+  if (!pBind || !IsChoiceListMultiSelect()) {
+    SyncValue(WideString(), false);
+    return;
+  }
+
+  while (CXFA_Node* pChildNode = pBind->GetFirstChild())
+    pBind->RemoveChildAndNotify(pChildNode, true);
+}
+
+void CXFA_Node::InsertItem(const WideString& wsLabel,
+                           const WideString& wsValue,
+                           bool bNotify) {
+  int32_t nIndex = -1;
+  WideString wsNewValue(wsValue);
+  if (wsNewValue.IsEmpty())
+    wsNewValue = wsLabel;
+
+  std::vector<CXFA_Node*> listitems;
+  for (CXFA_Node* pItem = GetFirstChild(); pItem;
+       pItem = pItem->GetNextSibling()) {
+    if (pItem->GetElementType() == XFA_Element::Items)
+      listitems.push_back(pItem);
+  }
+  if (listitems.empty()) {
+    CXFA_Node* pItems = CreateSamePacketNode(XFA_Element::Items);
+    InsertChildAndNotify(-1, pItems);
+    InsertListTextItem(pItems, wsLabel, nIndex);
+    CXFA_Node* pSaveItems = CreateSamePacketNode(XFA_Element::Items);
+    InsertChildAndNotify(-1, pSaveItems);
+    pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false);
+    InsertListTextItem(pSaveItems, wsNewValue, nIndex);
+  } else if (listitems.size() > 1) {
+    for (int32_t i = 0; i < 2; i++) {
+      CXFA_Node* pNode = listitems[i];
+      bool bHasSave = pNode->JSObject()->GetBoolean(XFA_Attribute::Save);
+      if (bHasSave)
+        InsertListTextItem(pNode, wsNewValue, nIndex);
+      else
+        InsertListTextItem(pNode, wsLabel, nIndex);
+    }
+  } else {
+    CXFA_Node* pNode = listitems[0];
+    pNode->JSObject()->SetBoolean(XFA_Attribute::Save, false, false);
+    pNode->JSObject()->SetEnum(XFA_Attribute::Presence,
+                               XFA_AttributeValue::Visible, false);
+    CXFA_Node* pSaveItems = CreateSamePacketNode(XFA_Element::Items);
+    InsertChildAndNotify(-1, pSaveItems);
+    pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false);
+    pSaveItems->JSObject()->SetEnum(XFA_Attribute::Presence,
+                                    XFA_AttributeValue::Hidden, false);
+    CXFA_Node* pListNode = pNode->GetFirstChild();
+    int32_t i = 0;
+    while (pListNode) {
+      InsertListTextItem(pSaveItems, pListNode->JSObject()->GetContent(false),
+                         i);
+      ++i;
+
+      pListNode = pListNode->GetNextSibling();
+    }
+    InsertListTextItem(pNode, wsLabel, nIndex);
+    InsertListTextItem(pSaveItems, wsNewValue, nIndex);
+  }
+  if (bNotify)
+    GetDocument()->GetNotify()->OnWidgetListItemAdded(this, wsLabel, nIndex);
+}
+
+WideString CXFA_Node::GetItemLabel(WideStringView wsValue) const {
+  std::vector<CXFA_Node*> listitems;
+  CXFA_Node* pItems = GetFirstChild();
+  for (; pItems; pItems = pItems->GetNextSibling()) {
+    if (pItems->GetElementType() != XFA_Element::Items)
+      continue;
+    listitems.push_back(pItems);
+  }
+
+  if (listitems.size() <= 1)
+    return WideString(wsValue);
+
+  CXFA_Node* pLabelItems = listitems[0];
+  bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save);
+  CXFA_Node* pSaveItems = nullptr;
+  if (bSave) {
+    pSaveItems = pLabelItems;
+    pLabelItems = listitems[1];
+  } else {
+    pSaveItems = listitems[1];
+  }
+
+  int32_t iCount = 0;
+  int32_t iSearch = -1;
+  for (CXFA_Node* pChildItem = pSaveItems->GetFirstChild(); pChildItem;
+       pChildItem = pChildItem->GetNextSibling()) {
+    if (pChildItem->JSObject()->GetContent(false) == wsValue) {
+      iSearch = iCount;
+      break;
+    }
+    iCount++;
+  }
+  if (iSearch < 0)
+    return WideString();
+
+  CXFA_Node* pText =
+      pLabelItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false);
+  return pText ? pText->JSObject()->GetContent(false) : WideString();
+}
+
+WideString CXFA_Node::GetItemValue(WideStringView wsLabel) {
+  int32_t iCount = 0;
+  std::vector<CXFA_Node*> listitems;
+  for (CXFA_Node* pItems = GetFirstChild(); pItems;
+       pItems = pItems->GetNextSibling()) {
+    if (pItems->GetElementType() != XFA_Element::Items)
+      continue;
+    iCount++;
+    listitems.push_back(pItems);
+  }
+  if (iCount <= 1)
+    return WideString(wsLabel);
+
+  CXFA_Node* pLabelItems = listitems[0];
+  bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save);
+  CXFA_Node* pSaveItems = nullptr;
+  if (bSave) {
+    pSaveItems = pLabelItems;
+    pLabelItems = listitems[1];
+  } else {
+    pSaveItems = listitems[1];
+  }
+  iCount = 0;
+
+  int32_t iSearch = -1;
+  WideString wsContent;
+  CXFA_Node* pChildItem = pLabelItems->GetFirstChild();
+  for (; pChildItem; pChildItem = pChildItem->GetNextSibling()) {
+    if (pChildItem->JSObject()->GetContent(false) == wsLabel) {
+      iSearch = iCount;
+      break;
+    }
+    iCount++;
+  }
+  if (iSearch < 0)
+    return WideString();
+
+  CXFA_Node* pText =
+      pSaveItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false);
+  return pText ? pText->JSObject()->GetContent(false) : WideString();
+}
+
+bool CXFA_Node::DeleteItem(int32_t nIndex, bool bNotify, bool bScriptModify) {
+  bool bSetValue = false;
+  CXFA_Node* pItems = GetFirstChild();
+  for (; pItems; pItems = pItems->GetNextSibling()) {
+    if (pItems->GetElementType() != XFA_Element::Items)
+      continue;
+
+    if (nIndex < 0) {
+      while (CXFA_Node* pNode = pItems->GetFirstChild()) {
+        pItems->RemoveChildAndNotify(pNode, true);
+      }
+    } else {
+      if (!bSetValue && pItems->JSObject()->GetBoolean(XFA_Attribute::Save)) {
+        SetItemState(nIndex, false, true, bScriptModify, true);
+        bSetValue = true;
+      }
+      int32_t i = 0;
+      CXFA_Node* pNode = pItems->GetFirstChild();
+      while (pNode) {
+        if (i == nIndex) {
+          pItems->RemoveChildAndNotify(pNode, true);
+          break;
+        }
+        i++;
+        pNode = pNode->GetNextSibling();
+      }
+    }
+  }
+  if (bNotify)
+    GetDocument()->GetNotify()->OnWidgetListItemRemoved(this, nIndex);
+  return true;
+}
+
+bool CXFA_Node::IsHorizontalScrollPolicyOff() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (pUIChild) {
+    return pUIChild->JSObject()->GetEnum(XFA_Attribute::HScrollPolicy) ==
+           XFA_AttributeValue::Off;
+  }
+  return false;
+}
+
+bool CXFA_Node::IsVerticalScrollPolicyOff() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (pUIChild) {
+    return pUIChild->JSObject()->GetEnum(XFA_Attribute::VScrollPolicy) ==
+           XFA_AttributeValue::Off;
+  }
+  return false;
+}
+
+Optional<int32_t> CXFA_Node::GetNumberOfCells() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  if (!pUIChild)
+    return {};
+  if (CXFA_Comb* pNode =
+          pUIChild->GetChild<CXFA_Comb>(0, XFA_Element::Comb, false))
+    return {pNode->JSObject()->GetInteger(XFA_Attribute::NumberOfCells)};
+  return {};
+}
+
+bool CXFA_Node::IsMultiLine() {
+  CXFA_Node* pUIChild = GetUIChildNode();
+  return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::MultiLine);
+}
+
+std::pair<XFA_Element, int32_t> CXFA_Node::GetMaxChars() {
+  if (CXFA_Value* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false)) {
+    if (CXFA_Node* pChild = pNode->GetFirstChild()) {
+      switch (pChild->GetElementType()) {
+        case XFA_Element::Text:
+          return {XFA_Element::Text,
+                  pChild->JSObject()->GetInteger(XFA_Attribute::MaxChars)};
+        case XFA_Element::ExData: {
+          int32_t iMax =
+              pChild->JSObject()->GetInteger(XFA_Attribute::MaxLength);
+          return {XFA_Element::ExData, iMax < 0 ? 0 : iMax};
+        }
+        default:
+          break;
+      }
+    }
+  }
+  return {XFA_Element::Unknown, 0};
+}
+
+int32_t CXFA_Node::GetFracDigits() {
+  CXFA_Value* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+  if (!pNode)
+    return -1;
+
+  CXFA_Decimal* pChild =
+      pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
+  if (!pChild)
+    return -1;
+
+  return pChild->JSObject()
+      ->TryInteger(XFA_Attribute::FracDigits, true)
+      .value_or(-1);
+}
+
+int32_t CXFA_Node::GetLeadDigits() {
+  CXFA_Value* pNode = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+  if (!pNode)
+    return -1;
+
+  CXFA_Decimal* pChild =
+      pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false);
+  if (!pChild)
+    return -1;
+
+  return pChild->JSObject()
+      ->TryInteger(XFA_Attribute::LeadDigits, true)
+      .value_or(-1);
+}
+
+bool CXFA_Node::SetValue(XFA_VALUEPICTURE eValueType,
+                         const WideString& wsValue) {
+  if (wsValue.IsEmpty()) {
+    SyncValue(wsValue, true);
+    return true;
+  }
+
+  SetPreNull(IsNull());
+  SetIsNull(false);
+
+  WideString wsNewText(wsValue);
+  WideString wsPicture = GetPictureContent(eValueType);
+  bool bValidate = true;
+  bool bSyncData = false;
+  CXFA_Node* pNode = GetUIChildNode();
+  if (!pNode)
+    return true;
+
+  XFA_Element eType = pNode->GetElementType();
+  if (!wsPicture.IsEmpty()) {
+    CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
+    LocaleIface* pLocale = GetLocale();
+    CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
+    bValidate =
+        widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture);
+    if (bValidate) {
+      widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsNewText,
+                                     wsPicture, pLocale, pLocaleMgr);
+      wsNewText = widgetValue.GetValue();
+      if (eType == XFA_Element::NumericEdit)
+        wsNewText = NumericLimit(wsNewText);
+
+      bSyncData = true;
+    }
+  } else if (eType == XFA_Element::NumericEdit) {
+    if (!wsNewText.EqualsASCII("0"))
+      wsNewText = NumericLimit(wsNewText);
+
+    bSyncData = true;
+  }
+  if (eType != XFA_Element::NumericEdit || bSyncData)
+    SyncValue(wsNewText, true);
+
+  return bValidate;
+}
+
+WideString CXFA_Node::GetPictureContent(XFA_VALUEPICTURE ePicture) {
+  if (ePicture == XFA_VALUEPICTURE_Raw)
+    return WideString();
+
+  CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
+  switch (ePicture) {
+    case XFA_VALUEPICTURE_Display: {
+      if (CXFA_Format* pFormat =
+              GetChild<CXFA_Format>(0, XFA_Element::Format, false)) {
+        if (CXFA_Picture* pPicture = pFormat->GetChild<CXFA_Picture>(
+                0, XFA_Element::Picture, false)) {
+          Optional<WideString> picture =
+              pPicture->JSObject()->TryContent(false, true);
+          if (picture)
+            return *picture;
+        }
+      }
+
+      LocaleIface* pLocale = GetLocale();
+      if (!pLocale)
+        return WideString();
+
+      uint32_t dwType = widgetValue.GetType();
+      switch (dwType) {
+        case XFA_VT_DATE:
+          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
+        case XFA_VT_TIME:
+          return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
+        case XFA_VT_DATETIME:
+          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium) +
+                 L"T" +
+                 pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium);
+        case XFA_VT_DECIMAL:
+        case XFA_VT_FLOAT:
+        default:
+          return WideString();
+      }
+    }
+    case XFA_VALUEPICTURE_Edit: {
+      CXFA_Ui* pUI = GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
+      if (pUI) {
+        if (CXFA_Picture* pPicture =
+                pUI->GetChild<CXFA_Picture>(0, XFA_Element::Picture, false)) {
+          Optional<WideString> picture =
+              pPicture->JSObject()->TryContent(false, true);
+          if (picture)
+            return *picture;
+        }
+      }
+
+      LocaleIface* pLocale = GetLocale();
+      if (!pLocale)
+        return WideString();
+
+      uint32_t dwType = widgetValue.GetType();
+      switch (dwType) {
+        case XFA_VT_DATE:
+          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
+        case XFA_VT_TIME:
+          return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
+        case XFA_VT_DATETIME:
+          return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short) +
+                 L"T" +
+                 pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short);
+        default:
+          return WideString();
+      }
+    }
+    case XFA_VALUEPICTURE_DataBind: {
+      CXFA_Bind* bind = GetBindIfExists();
+      if (bind)
+        return bind->GetPicture();
+      break;
+    }
     default:
       break;
   }
-  return {};
+  return WideString();
 }
 
-Optional<bool> CXFA_Node::GetBarcodeAttribute_Truncate() {
-  return GetWidgetAcc()->GetUIChild()->JSObject()->TryBoolean(
-      XFA_Attribute::Truncate, true);
+WideString CXFA_Node::GetValue(XFA_VALUEPICTURE eValueType) {
+  WideString wsValue = JSObject()->GetContent(false);
+
+  if (eValueType == XFA_VALUEPICTURE_Display)
+    wsValue = GetItemLabel(wsValue.AsStringView());
+
+  WideString wsPicture = GetPictureContent(eValueType);
+  CXFA_Node* pNode = GetUIChildNode();
+  if (!pNode)
+    return wsValue;
+
+  switch (pNode->GetElementType()) {
+    case XFA_Element::ChoiceList: {
+      if (eValueType == XFA_VALUEPICTURE_Display) {
+        int32_t iSelItemIndex = GetSelectedItem(0);
+        if (iSelItemIndex >= 0) {
+          wsValue =
+              GetChoiceListItem(iSelItemIndex, false).value_or(WideString());
+          wsPicture.clear();
+        }
+      }
+      break;
+    }
+    case XFA_Element::NumericEdit:
+      if (eValueType != XFA_VALUEPICTURE_Raw && wsPicture.IsEmpty()) {
+        LocaleIface* pLocale = GetLocale();
+        if (eValueType == XFA_VALUEPICTURE_Display && pLocale)
+          wsValue = FormatNumStr(NormalizeNumStr(wsValue), pLocale);
+      }
+      break;
+    default:
+      break;
+  }
+  if (wsPicture.IsEmpty())
+    return wsValue;
+
+  if (LocaleIface* pLocale = GetLocale()) {
+    CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
+    CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
+    switch (widgetValue.GetType()) {
+      case XFA_VT_DATE: {
+        WideString wsDate, wsTime;
+        if (SplitDateTime(wsValue, wsDate, wsTime)) {
+          CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocaleMgr);
+          if (date.FormatPatterns(wsValue, wsPicture, pLocale, eValueType))
+            return wsValue;
+        }
+        break;
+      }
+      case XFA_VT_TIME: {
+        WideString wsDate, wsTime;
+        if (SplitDateTime(wsValue, wsDate, wsTime)) {
+          CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocaleMgr);
+          if (time.FormatPatterns(wsValue, wsPicture, pLocale, eValueType))
+            return wsValue;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+    widgetValue.FormatPatterns(wsValue, wsPicture, pLocale, eValueType);
+  }
+  return wsValue;
 }
 
-Optional<int8_t> CXFA_Node::GetBarcodeAttribute_WideNarrowRatio() {
-  Optional<WideString> wsWideNarrowRatio =
-      GetWidgetAcc()->GetUIChild()->JSObject()->TryCData(
-          XFA_Attribute::WideNarrowRatio, true);
-  if (!wsWideNarrowRatio)
-    return {};
+WideString CXFA_Node::GetNormalizeDataValue(const WideString& wsValue) {
+  if (wsValue.IsEmpty())
+    return WideString();
 
-  Optional<size_t> ptPos = wsWideNarrowRatio->Find(':');
-  if (!ptPos)
-    return {static_cast<int8_t>(FXSYS_wtoi(wsWideNarrowRatio->c_str()))};
+  WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
+  if (wsPicture.IsEmpty())
+    return wsValue;
 
-  int32_t fB = FXSYS_wtoi(
-      wsWideNarrowRatio->Right(wsWideNarrowRatio->GetLength() - (*ptPos + 1))
-          .c_str());
-  if (!fB)
-    return {0};
+  CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
+  LocaleIface* pLocale = GetLocale();
+  CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(this);
+  if (widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture)) {
+    widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsValue, wsPicture,
+                                   pLocale, pLocaleMgr);
+    return widgetValue.GetValue();
+  }
+  return wsValue;
+}
 
-  int32_t fA = FXSYS_wtoi(wsWideNarrowRatio->Left(*ptPos).c_str());
-  float result = static_cast<float>(fA) / static_cast<float>(fB);
-  return {static_cast<int8_t>(result)};
+WideString CXFA_Node::GetFormatDataValue(const WideString& wsValue) {
+  if (wsValue.IsEmpty())
+    return WideString();
+
+  WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
+  if (wsPicture.IsEmpty())
+    return wsValue;
+
+  WideString wsFormattedValue = wsValue;
+  if (LocaleIface* pLocale = GetLocale()) {
+    CXFA_Value* pNodeValue = GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+    if (!pNodeValue)
+      return wsValue;
+
+    CXFA_Node* pValueChild = pNodeValue->GetFirstChild();
+    if (!pValueChild)
+      return wsValue;
+
+    int32_t iVTType = XFA_VT_NULL;
+    switch (pValueChild->GetElementType()) {
+      case XFA_Element::Decimal:
+        iVTType = XFA_VT_DECIMAL;
+        break;
+      case XFA_Element::Float:
+        iVTType = XFA_VT_FLOAT;
+        break;
+      case XFA_Element::Date:
+        iVTType = XFA_VT_DATE;
+        break;
+      case XFA_Element::Time:
+        iVTType = XFA_VT_TIME;
+        break;
+      case XFA_Element::DateTime:
+        iVTType = XFA_VT_DATETIME;
+        break;
+      case XFA_Element::Boolean:
+        iVTType = XFA_VT_BOOLEAN;
+        break;
+      case XFA_Element::Integer:
+        iVTType = XFA_VT_INTEGER;
+        break;
+      case XFA_Element::Text:
+        iVTType = XFA_VT_TEXT;
+        break;
+      default:
+        iVTType = XFA_VT_NULL;
+        break;
+    }
+    CXFA_LocaleMgr* pLocaleMgr = GetDocument()->GetLocaleMgr();
+    CXFA_LocaleValue widgetValue(iVTType, wsValue, pLocaleMgr);
+    switch (widgetValue.GetType()) {
+      case XFA_VT_DATE: {
+        WideString wsDate, wsTime;
+        if (SplitDateTime(wsValue, wsDate, wsTime)) {
+          CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocaleMgr);
+          if (date.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
+                                  XFA_VALUEPICTURE_DataBind)) {
+            return wsFormattedValue;
+          }
+        }
+        break;
+      }
+      case XFA_VT_TIME: {
+        WideString wsDate, wsTime;
+        if (SplitDateTime(wsValue, wsDate, wsTime)) {
+          CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocaleMgr);
+          if (time.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
+                                  XFA_VALUEPICTURE_DataBind)) {
+            return wsFormattedValue;
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+    widgetValue.FormatPatterns(wsFormattedValue, wsPicture, pLocale,
+                               XFA_VALUEPICTURE_DataBind);
+  }
+  return wsFormattedValue;
+}
+
+WideString CXFA_Node::NormalizeNumStr(const WideString& wsValue) {
+  if (wsValue.IsEmpty())
+    return WideString();
+
+  WideString wsOutput = wsValue;
+  wsOutput.TrimLeft('0');
+
+  if (!wsOutput.IsEmpty() && wsOutput.Contains('.') && GetFracDigits() != -1) {
+    wsOutput.TrimRight(L"0");
+    wsOutput.TrimRight(L".");
+  }
+  if (wsOutput.IsEmpty() || wsOutput[0] == '.')
+    wsOutput.InsertAtFront('0');
+
+  return wsOutput;
+}
+
+void CXFA_Node::InsertListTextItem(CXFA_Node* pItems,
+                                   const WideString& wsText,
+                                   int32_t nIndex) {
+  CXFA_Node* pText = pItems->CreateSamePacketNode(XFA_Element::Text);
+  pItems->InsertChildAndNotify(nIndex, pText);
+  pText->JSObject()->SetContent(wsText, wsText, false, false, false);
+}
+
+WideString CXFA_Node::NumericLimit(const WideString& wsValue) {
+  int32_t iLead = GetLeadDigits();
+  int32_t iTread = GetFracDigits();
+
+  if ((iLead == -1) && (iTread == -1))
+    return wsValue;
+
+  WideString wsRet;
+  int32_t iLead_ = 0, iTread_ = -1;
+  int32_t iCount = wsValue.GetLength();
+  if (iCount == 0)
+    return wsValue;
+
+  int32_t i = 0;
+  if (wsValue[i] == L'-') {
+    wsRet += L'-';
+    i++;
+  }
+  for (; i < iCount; i++) {
+    wchar_t wc = wsValue[i];
+    if (FXSYS_IsDecimalDigit(wc)) {
+      if (iLead >= 0) {
+        iLead_++;
+        if (iLead_ > iLead)
+          return L"0";
+      } else if (iTread_ >= 0) {
+        iTread_++;
+        if (iTread_ > iTread) {
+          if (iTread != -1) {
+            CFGAS_Decimal wsDeci = CFGAS_Decimal(wsValue.AsStringView());
+            wsDeci.SetScale(iTread);
+            wsRet = wsDeci.ToWideString();
+          }
+          return wsRet;
+        }
+      }
+    } else if (wc == L'.') {
+      iTread_ = 0;
+      iLead = -1;
+    }
+    wsRet += wc;
+  }
+  return wsRet;
+}
+
+bool CXFA_Node::IsTransparent() const {
+  XFA_Element type = GetElementType();
+  return type == XFA_Element::SubformSet || type == XFA_Element::Area ||
+         type == XFA_Element::Proto || (IsUnnamed() && IsContainerNode());
+}
+
+bool CXFA_Node::IsProperty() const {
+  CXFA_Node* parent = GetParent();
+  return parent && parent->HasProperty(GetElementType());
+}
+
+bool CXFA_Node::PresenceRequiresSpace() const {
+  auto value = JSObject()->TryEnum(XFA_Attribute::Presence, true);
+  XFA_AttributeValue ePresence = value.value_or(XFA_AttributeValue::Visible);
+  return ePresence == XFA_AttributeValue::Visible ||
+         ePresence == XFA_AttributeValue::Invisible;
+}
+
+void CXFA_Node::SetBindingNode(CXFA_Node* node) {
+  binding_nodes_.clear();
+  if (node)
+    binding_nodes_.emplace_back(node);
+}
+
+void CXFA_Node::SetNodeAndDescendantsUnused() {
+  CXFA_NodeIterator sIterator(this);
+  for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
+       pNode = sIterator.MoveToNext()) {
+    pNode->SetFlag(XFA_NodeFlag_UnusedNode);
+  }
+}
+
+void CXFA_Node::SetToXML(const WideString& value) {
+  auto* pNode = GetXMLMappingNode();
+  switch (pNode->GetType()) {
+    case CFX_XMLNode::Type::kElement: {
+      auto* elem = static_cast<CFX_XMLElement*>(pNode);
+      if (IsAttributeInXML()) {
+        elem->SetAttribute(JSObject()->GetCData(XFA_Attribute::QualifiedName),
+                           value);
+        return;
+      }
+
+      bool bDeleteChildren = true;
+      if (GetPacketType() == XFA_PacketType::Datasets) {
+        for (CXFA_Node* pChildDataNode = GetFirstChild(); pChildDataNode;
+             pChildDataNode = pChildDataNode->GetNextSibling()) {
+          if (pChildDataNode->HasBindItems()) {
+            bDeleteChildren = false;
+            break;
+          }
+        }
+      }
+      if (bDeleteChildren)
+        elem->RemoveAllChildren();
+
+      auto* text = GetXMLDocument()->CreateNode<CFX_XMLText>(value);
+      elem->AppendLastChild(text);
+      break;
+    }
+    case CFX_XMLNode::Type::kText:
+      ToXMLText(GetXMLMappingNode())->SetText(value);
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+CXFA_Node* CXFA_Node::GetTransparentParent() {
+  CXFA_Node* parent = GetParent();
+  while (parent) {
+    XFA_Element type = parent->GetElementType();
+    if (type == XFA_Element::Variables ||
+        (type != XFA_Element::SubformSet && !parent->IsUnnamed())) {
+      return parent;
+    }
+    parent = parent->GetParent();
+  }
+  return nullptr;
+}
+
+CFX_XMLDocument* CXFA_Node::GetXMLDocument() const {
+  return GetDocument()->GetNotify()->GetHDOC()->GetXMLDocument();
+}
+
+// static
+std::unique_ptr<CXFA_Node> CXFA_Node::Create(CXFA_Document* doc,
+                                             XFA_Element element,
+                                             XFA_PacketType packet) {
+  std::unique_ptr<CXFA_Node> node;
+  switch (element) {
+    case XFA_Element::Ps:
+      node = pdfium::MakeUnique<CXFA_Ps>(doc, packet);
+      break;
+    case XFA_Element::To:
+      node = pdfium::MakeUnique<CXFA_To>(doc, packet);
+      break;
+    case XFA_Element::Ui:
+      node = pdfium::MakeUnique<CXFA_Ui>(doc, packet);
+      break;
+    case XFA_Element::RecordSet:
+      node = pdfium::MakeUnique<CXFA_RecordSet>(doc, packet);
+      break;
+    case XFA_Element::SubsetBelow:
+      node = pdfium::MakeUnique<CXFA_SubsetBelow>(doc, packet);
+      break;
+    case XFA_Element::SubformSet:
+      node = pdfium::MakeUnique<CXFA_SubformSet>(doc, packet);
+      break;
+    case XFA_Element::AdobeExtensionLevel:
+      node = pdfium::MakeUnique<CXFA_AdobeExtensionLevel>(doc, packet);
+      break;
+    case XFA_Element::Typeface:
+      node = pdfium::MakeUnique<CXFA_Typeface>(doc, packet);
+      break;
+    case XFA_Element::Break:
+      node = pdfium::MakeUnique<CXFA_Break>(doc, packet);
+      break;
+    case XFA_Element::FontInfo:
+      node = pdfium::MakeUnique<CXFA_FontInfo>(doc, packet);
+      break;
+    case XFA_Element::NumberPattern:
+      node = pdfium::MakeUnique<CXFA_NumberPattern>(doc, packet);
+      break;
+    case XFA_Element::DynamicRender:
+      node = pdfium::MakeUnique<CXFA_DynamicRender>(doc, packet);
+      break;
+    case XFA_Element::PrintScaling:
+      node = pdfium::MakeUnique<CXFA_PrintScaling>(doc, packet);
+      break;
+    case XFA_Element::CheckButton:
+      node = pdfium::MakeUnique<CXFA_CheckButton>(doc, packet);
+      break;
+    case XFA_Element::DatePatterns:
+      node = pdfium::MakeUnique<CXFA_DatePatterns>(doc, packet);
+      break;
+    case XFA_Element::SourceSet:
+      node = pdfium::MakeUnique<CXFA_SourceSet>(doc, packet);
+      break;
+    case XFA_Element::Amd:
+      node = pdfium::MakeUnique<CXFA_Amd>(doc, packet);
+      break;
+    case XFA_Element::Arc:
+      node = pdfium::MakeUnique<CXFA_Arc>(doc, packet);
+      break;
+    case XFA_Element::Day:
+      node = pdfium::MakeUnique<CXFA_Day>(doc, packet);
+      break;
+    case XFA_Element::Era:
+      node = pdfium::MakeUnique<CXFA_Era>(doc, packet);
+      break;
+    case XFA_Element::Jog:
+      node = pdfium::MakeUnique<CXFA_Jog>(doc, packet);
+      break;
+    case XFA_Element::Log:
+      node = pdfium::MakeUnique<CXFA_Log>(doc, packet);
+      break;
+    case XFA_Element::Map:
+      node = pdfium::MakeUnique<CXFA_Map>(doc, packet);
+      break;
+    case XFA_Element::Mdp:
+      node = pdfium::MakeUnique<CXFA_Mdp>(doc, packet);
+      break;
+    case XFA_Element::BreakBefore:
+      node = pdfium::MakeUnique<CXFA_BreakBefore>(doc, packet);
+      break;
+    case XFA_Element::Oid:
+      node = pdfium::MakeUnique<CXFA_Oid>(doc, packet);
+      break;
+    case XFA_Element::Pcl:
+      node = pdfium::MakeUnique<CXFA_Pcl>(doc, packet);
+      break;
+    case XFA_Element::Pdf:
+      node = pdfium::MakeUnique<CXFA_Pdf>(doc, packet);
+      break;
+    case XFA_Element::Ref:
+      node = pdfium::MakeUnique<CXFA_Ref>(doc, packet);
+      break;
+    case XFA_Element::Uri:
+      node = pdfium::MakeUnique<CXFA_Uri>(doc, packet);
+      break;
+    case XFA_Element::Xdc:
+      node = pdfium::MakeUnique<CXFA_Xdc>(doc, packet);
+      break;
+    case XFA_Element::Xdp:
+      node = pdfium::MakeUnique<CXFA_Xdp>(doc, packet);
+      break;
+    case XFA_Element::Xfa:
+      node = pdfium::MakeUnique<CXFA_Xfa>(doc, packet);
+      break;
+    case XFA_Element::Xsl:
+      node = pdfium::MakeUnique<CXFA_Xsl>(doc, packet);
+      break;
+    case XFA_Element::Zpl:
+      node = pdfium::MakeUnique<CXFA_Zpl>(doc, packet);
+      break;
+    case XFA_Element::Cache:
+      node = pdfium::MakeUnique<CXFA_Cache>(doc, packet);
+      break;
+    case XFA_Element::Margin:
+      node = pdfium::MakeUnique<CXFA_Margin>(doc, packet);
+      break;
+    case XFA_Element::KeyUsage:
+      node = pdfium::MakeUnique<CXFA_KeyUsage>(doc, packet);
+      break;
+    case XFA_Element::Exclude:
+      node = pdfium::MakeUnique<CXFA_Exclude>(doc, packet);
+      break;
+    case XFA_Element::ChoiceList:
+      node = pdfium::MakeUnique<CXFA_ChoiceList>(doc, packet);
+      break;
+    case XFA_Element::Level:
+      node = pdfium::MakeUnique<CXFA_Level>(doc, packet);
+      break;
+    case XFA_Element::LabelPrinter:
+      node = pdfium::MakeUnique<CXFA_LabelPrinter>(doc, packet);
+      break;
+    case XFA_Element::CalendarSymbols:
+      node = pdfium::MakeUnique<CXFA_CalendarSymbols>(doc, packet);
+      break;
+    case XFA_Element::Para:
+      node = pdfium::MakeUnique<CXFA_Para>(doc, packet);
+      break;
+    case XFA_Element::Part:
+      node = pdfium::MakeUnique<CXFA_Part>(doc, packet);
+      break;
+    case XFA_Element::Pdfa:
+      node = pdfium::MakeUnique<CXFA_Pdfa>(doc, packet);
+      break;
+    case XFA_Element::Filter:
+      node = pdfium::MakeUnique<CXFA_Filter>(doc, packet);
+      break;
+    case XFA_Element::Present:
+      node = pdfium::MakeUnique<CXFA_Present>(doc, packet);
+      break;
+    case XFA_Element::Pagination:
+      node = pdfium::MakeUnique<CXFA_Pagination>(doc, packet);
+      break;
+    case XFA_Element::Encoding:
+      node = pdfium::MakeUnique<CXFA_Encoding>(doc, packet);
+      break;
+    case XFA_Element::Event:
+      node = pdfium::MakeUnique<CXFA_Event>(doc, packet);
+      break;
+    case XFA_Element::Whitespace:
+      node = pdfium::MakeUnique<CXFA_Whitespace>(doc, packet);
+      break;
+    case XFA_Element::DefaultUi:
+      node = pdfium::MakeUnique<CXFA_DefaultUi>(doc, packet);
+      break;
+    case XFA_Element::DataModel:
+      node = pdfium::MakeUnique<CXFA_DataModel>(doc, packet);
+      break;
+    case XFA_Element::Barcode:
+      node = pdfium::MakeUnique<CXFA_Barcode>(doc, packet);
+      break;
+    case XFA_Element::TimePattern:
+      node = pdfium::MakeUnique<CXFA_TimePattern>(doc, packet);
+      break;
+    case XFA_Element::BatchOutput:
+      node = pdfium::MakeUnique<CXFA_BatchOutput>(doc, packet);
+      break;
+    case XFA_Element::Enforce:
+      node = pdfium::MakeUnique<CXFA_Enforce>(doc, packet);
+      break;
+    case XFA_Element::CurrencySymbols:
+      node = pdfium::MakeUnique<CXFA_CurrencySymbols>(doc, packet);
+      break;
+    case XFA_Element::AddSilentPrint:
+      node = pdfium::MakeUnique<CXFA_AddSilentPrint>(doc, packet);
+      break;
+    case XFA_Element::Rename:
+      node = pdfium::MakeUnique<CXFA_Rename>(doc, packet);
+      break;
+    case XFA_Element::Operation:
+      node = pdfium::MakeUnique<CXFA_Operation>(doc, packet);
+      break;
+    case XFA_Element::Typefaces:
+      node = pdfium::MakeUnique<CXFA_Typefaces>(doc, packet);
+      break;
+    case XFA_Element::SubjectDNs:
+      node = pdfium::MakeUnique<CXFA_SubjectDNs>(doc, packet);
+      break;
+    case XFA_Element::Issuers:
+      node = pdfium::MakeUnique<CXFA_Issuers>(doc, packet);
+      break;
+    case XFA_Element::WsdlConnection:
+      node = pdfium::MakeUnique<CXFA_WsdlConnection>(doc, packet);
+      break;
+    case XFA_Element::Debug:
+      node = pdfium::MakeUnique<CXFA_Debug>(doc, packet);
+      break;
+    case XFA_Element::Delta:
+      node = pdfium::MakeUnique<CXFA_Delta>(doc, packet);
+      break;
+    case XFA_Element::EraNames:
+      node = pdfium::MakeUnique<CXFA_EraNames>(doc, packet);
+      break;
+    case XFA_Element::ModifyAnnots:
+      node = pdfium::MakeUnique<CXFA_ModifyAnnots>(doc, packet);
+      break;
+    case XFA_Element::StartNode:
+      node = pdfium::MakeUnique<CXFA_StartNode>(doc, packet);
+      break;
+    case XFA_Element::Button:
+      node = pdfium::MakeUnique<CXFA_Button>(doc, packet);
+      break;
+    case XFA_Element::Format:
+      node = pdfium::MakeUnique<CXFA_Format>(doc, packet);
+      break;
+    case XFA_Element::Border:
+      node = pdfium::MakeUnique<CXFA_Border>(doc, packet);
+      break;
+    case XFA_Element::Area:
+      node = pdfium::MakeUnique<CXFA_Area>(doc, packet);
+      break;
+    case XFA_Element::Hyphenation:
+      node = pdfium::MakeUnique<CXFA_Hyphenation>(doc, packet);
+      break;
+    case XFA_Element::Text:
+      node = pdfium::MakeUnique<CXFA_Text>(doc, packet);
+      break;
+    case XFA_Element::Time:
+      node = pdfium::MakeUnique<CXFA_Time>(doc, packet);
+      break;
+    case XFA_Element::Type:
+      node = pdfium::MakeUnique<CXFA_Type>(doc, packet);
+      break;
+    case XFA_Element::Overprint:
+      node = pdfium::MakeUnique<CXFA_Overprint>(doc, packet);
+      break;
+    case XFA_Element::Certificates:
+      node = pdfium::MakeUnique<CXFA_Certificates>(doc, packet);
+      break;
+    case XFA_Element::EncryptionMethods:
+      node = pdfium::MakeUnique<CXFA_EncryptionMethods>(doc, packet);
+      break;
+    case XFA_Element::SetProperty:
+      node = pdfium::MakeUnique<CXFA_SetProperty>(doc, packet);
+      break;
+    case XFA_Element::PrinterName:
+      node = pdfium::MakeUnique<CXFA_PrinterName>(doc, packet);
+      break;
+    case XFA_Element::StartPage:
+      node = pdfium::MakeUnique<CXFA_StartPage>(doc, packet);
+      break;
+    case XFA_Element::PageOffset:
+      node = pdfium::MakeUnique<CXFA_PageOffset>(doc, packet);
+      break;
+    case XFA_Element::DateTime:
+      node = pdfium::MakeUnique<CXFA_DateTime>(doc, packet);
+      break;
+    case XFA_Element::Comb:
+      node = pdfium::MakeUnique<CXFA_Comb>(doc, packet);
+      break;
+    case XFA_Element::Pattern:
+      node = pdfium::MakeUnique<CXFA_Pattern>(doc, packet);
+      break;
+    case XFA_Element::IfEmpty:
+      node = pdfium::MakeUnique<CXFA_IfEmpty>(doc, packet);
+      break;
+    case XFA_Element::SuppressBanner:
+      node = pdfium::MakeUnique<CXFA_SuppressBanner>(doc, packet);
+      break;
+    case XFA_Element::OutputBin:
+      node = pdfium::MakeUnique<CXFA_OutputBin>(doc, packet);
+      break;
+    case XFA_Element::Field:
+      node = pdfium::MakeUnique<CXFA_Field>(doc, packet);
+      break;
+    case XFA_Element::Agent:
+      node = pdfium::MakeUnique<CXFA_Agent>(doc, packet);
+      break;
+    case XFA_Element::OutputXSL:
+      node = pdfium::MakeUnique<CXFA_OutputXSL>(doc, packet);
+      break;
+    case XFA_Element::AdjustData:
+      node = pdfium::MakeUnique<CXFA_AdjustData>(doc, packet);
+      break;
+    case XFA_Element::AutoSave:
+      node = pdfium::MakeUnique<CXFA_AutoSave>(doc, packet);
+      break;
+    case XFA_Element::ContentArea:
+      node = pdfium::MakeUnique<CXFA_ContentArea>(doc, packet);
+      break;
+    case XFA_Element::WsdlAddress:
+      node = pdfium::MakeUnique<CXFA_WsdlAddress>(doc, packet);
+      break;
+    case XFA_Element::Solid:
+      node = pdfium::MakeUnique<CXFA_Solid>(doc, packet);
+      break;
+    case XFA_Element::DateTimeSymbols:
+      node = pdfium::MakeUnique<CXFA_DateTimeSymbols>(doc, packet);
+      break;
+    case XFA_Element::EncryptionLevel:
+      node = pdfium::MakeUnique<CXFA_EncryptionLevel>(doc, packet);
+      break;
+    case XFA_Element::Edge:
+      node = pdfium::MakeUnique<CXFA_Edge>(doc, packet);
+      break;
+    case XFA_Element::Stipple:
+      node = pdfium::MakeUnique<CXFA_Stipple>(doc, packet);
+      break;
+    case XFA_Element::Attributes:
+      node = pdfium::MakeUnique<CXFA_Attributes>(doc, packet);
+      break;
+    case XFA_Element::VersionControl:
+      node = pdfium::MakeUnique<CXFA_VersionControl>(doc, packet);
+      break;
+    case XFA_Element::Meridiem:
+      node = pdfium::MakeUnique<CXFA_Meridiem>(doc, packet);
+      break;
+    case XFA_Element::ExclGroup:
+      node = pdfium::MakeUnique<CXFA_ExclGroup>(doc, packet);
+      break;
+    case XFA_Element::ToolTip:
+      node = pdfium::MakeUnique<CXFA_ToolTip>(doc, packet);
+      break;
+    case XFA_Element::Compress:
+      node = pdfium::MakeUnique<CXFA_Compress>(doc, packet);
+      break;
+    case XFA_Element::Reason:
+      node = pdfium::MakeUnique<CXFA_Reason>(doc, packet);
+      break;
+    case XFA_Element::Execute:
+      node = pdfium::MakeUnique<CXFA_Execute>(doc, packet);
+      break;
+    case XFA_Element::ContentCopy:
+      node = pdfium::MakeUnique<CXFA_ContentCopy>(doc, packet);
+      break;
+    case XFA_Element::DateTimeEdit:
+      node = pdfium::MakeUnique<CXFA_DateTimeEdit>(doc, packet);
+      break;
+    case XFA_Element::Config:
+      node = pdfium::MakeUnique<CXFA_Config>(doc, packet);
+      break;
+    case XFA_Element::Image:
+      node = pdfium::MakeUnique<CXFA_Image>(doc, packet);
+      break;
+    case XFA_Element::SharpxHTML:
+      node = pdfium::MakeUnique<CXFA_SharpxHTML>(doc, packet);
+      break;
+    case XFA_Element::NumberOfCopies:
+      node = pdfium::MakeUnique<CXFA_NumberOfCopies>(doc, packet);
+      break;
+    case XFA_Element::BehaviorOverride:
+      node = pdfium::MakeUnique<CXFA_BehaviorOverride>(doc, packet);
+      break;
+    case XFA_Element::TimeStamp:
+      node = pdfium::MakeUnique<CXFA_TimeStamp>(doc, packet);
+      break;
+    case XFA_Element::Month:
+      node = pdfium::MakeUnique<CXFA_Month>(doc, packet);
+      break;
+    case XFA_Element::ViewerPreferences:
+      node = pdfium::MakeUnique<CXFA_ViewerPreferences>(doc, packet);
+      break;
+    case XFA_Element::ScriptModel:
+      node = pdfium::MakeUnique<CXFA_ScriptModel>(doc, packet);
+      break;
+    case XFA_Element::Decimal:
+      node = pdfium::MakeUnique<CXFA_Decimal>(doc, packet);
+      break;
+    case XFA_Element::Subform:
+      node = pdfium::MakeUnique<CXFA_Subform>(doc, packet);
+      break;
+    case XFA_Element::Select:
+      node = pdfium::MakeUnique<CXFA_Select>(doc, packet);
+      break;
+    case XFA_Element::Window:
+      node = pdfium::MakeUnique<CXFA_Window>(doc, packet);
+      break;
+    case XFA_Element::LocaleSet:
+      node = pdfium::MakeUnique<CXFA_LocaleSet>(doc, packet);
+      break;
+    case XFA_Element::Handler:
+      node = pdfium::MakeUnique<CXFA_Handler>(doc, packet);
+      break;
+    case XFA_Element::Presence:
+      node = pdfium::MakeUnique<CXFA_Presence>(doc, packet);
+      break;
+    case XFA_Element::Record:
+      node = pdfium::MakeUnique<CXFA_Record>(doc, packet);
+      break;
+    case XFA_Element::Embed:
+      node = pdfium::MakeUnique<CXFA_Embed>(doc, packet);
+      break;
+    case XFA_Element::Version:
+      node = pdfium::MakeUnique<CXFA_Version>(doc, packet);
+      break;
+    case XFA_Element::Command:
+      node = pdfium::MakeUnique<CXFA_Command>(doc, packet);
+      break;
+    case XFA_Element::Copies:
+      node = pdfium::MakeUnique<CXFA_Copies>(doc, packet);
+      break;
+    case XFA_Element::Staple:
+      node = pdfium::MakeUnique<CXFA_Staple>(doc, packet);
+      break;
+    case XFA_Element::SubmitFormat:
+      node = pdfium::MakeUnique<CXFA_SubmitFormat>(doc, packet);
+      break;
+    case XFA_Element::Boolean:
+      node = pdfium::MakeUnique<CXFA_Boolean>(doc, packet);
+      break;
+    case XFA_Element::Message:
+      node = pdfium::MakeUnique<CXFA_Message>(doc, packet);
+      break;
+    case XFA_Element::Output:
+      node = pdfium::MakeUnique<CXFA_Output>(doc, packet);
+      break;
+    case XFA_Element::PsMap:
+      node = pdfium::MakeUnique<CXFA_PsMap>(doc, packet);
+      break;
+    case XFA_Element::ExcludeNS:
+      node = pdfium::MakeUnique<CXFA_ExcludeNS>(doc, packet);
+      break;
+    case XFA_Element::Assist:
+      node = pdfium::MakeUnique<CXFA_Assist>(doc, packet);
+      break;
+    case XFA_Element::Picture:
+      node = pdfium::MakeUnique<CXFA_Picture>(doc, packet);
+      break;
+    case XFA_Element::Traversal:
+      node = pdfium::MakeUnique<CXFA_Traversal>(doc, packet);
+      break;
+    case XFA_Element::SilentPrint:
+      node = pdfium::MakeUnique<CXFA_SilentPrint>(doc, packet);
+      break;
+    case XFA_Element::WebClient:
+      node = pdfium::MakeUnique<CXFA_WebClient>(doc, packet);
+      break;
+    case XFA_Element::Producer:
+      node = pdfium::MakeUnique<CXFA_Producer>(doc, packet);
+      break;
+    case XFA_Element::Corner:
+      node = pdfium::MakeUnique<CXFA_Corner>(doc, packet);
+      break;
+    case XFA_Element::MsgId:
+      node = pdfium::MakeUnique<CXFA_MsgId>(doc, packet);
+      break;
+    case XFA_Element::Color:
+      node = pdfium::MakeUnique<CXFA_Color>(doc, packet);
+      break;
+    case XFA_Element::Keep:
+      node = pdfium::MakeUnique<CXFA_Keep>(doc, packet);
+      break;
+    case XFA_Element::Query:
+      node = pdfium::MakeUnique<CXFA_Query>(doc, packet);
+      break;
+    case XFA_Element::Insert:
+      node = pdfium::MakeUnique<CXFA_Insert>(doc, packet);
+      break;
+    case XFA_Element::ImageEdit:
+      node = pdfium::MakeUnique<CXFA_ImageEdit>(doc, packet);
+      break;
+    case XFA_Element::Validate:
+      node = pdfium::MakeUnique<CXFA_Validate>(doc, packet);
+      break;
+    case XFA_Element::DigestMethods:
+      node = pdfium::MakeUnique<CXFA_DigestMethods>(doc, packet);
+      break;
+    case XFA_Element::NumberPatterns:
+      node = pdfium::MakeUnique<CXFA_NumberPatterns>(doc, packet);
+      break;
+    case XFA_Element::PageSet:
+      node = pdfium::MakeUnique<CXFA_PageSet>(doc, packet);
+      break;
+    case XFA_Element::Integer:
+      node = pdfium::MakeUnique<CXFA_Integer>(doc, packet);
+      break;
+    case XFA_Element::SoapAddress:
+      node = pdfium::MakeUnique<CXFA_SoapAddress>(doc, packet);
+      break;
+    case XFA_Element::Equate:
+      node = pdfium::MakeUnique<CXFA_Equate>(doc, packet);
+      break;
+    case XFA_Element::FormFieldFilling:
+      node = pdfium::MakeUnique<CXFA_FormFieldFilling>(doc, packet);
+      break;
+    case XFA_Element::PageRange:
+      node = pdfium::MakeUnique<CXFA_PageRange>(doc, packet);
+      break;
+    case XFA_Element::Update:
+      node = pdfium::MakeUnique<CXFA_Update>(doc, packet);
+      break;
+    case XFA_Element::ConnectString:
+      node = pdfium::MakeUnique<CXFA_ConnectString>(doc, packet);
+      break;
+    case XFA_Element::Mode:
+      node = pdfium::MakeUnique<CXFA_Mode>(doc, packet);
+      break;
+    case XFA_Element::Layout:
+      node = pdfium::MakeUnique<CXFA_Layout>(doc, packet);
+      break;
+    case XFA_Element::Sharpxml:
+      node = pdfium::MakeUnique<CXFA_Sharpxml>(doc, packet);
+      break;
+    case XFA_Element::XsdConnection:
+      node = pdfium::MakeUnique<CXFA_XsdConnection>(doc, packet);
+      break;
+    case XFA_Element::Traverse:
+      node = pdfium::MakeUnique<CXFA_Traverse>(doc, packet);
+      break;
+    case XFA_Element::Encodings:
+      node = pdfium::MakeUnique<CXFA_Encodings>(doc, packet);
+      break;
+    case XFA_Element::Template:
+      node = pdfium::MakeUnique<CXFA_Template>(doc, packet);
+      break;
+    case XFA_Element::Acrobat:
+      node = pdfium::MakeUnique<CXFA_Acrobat>(doc, packet);
+      break;
+    case XFA_Element::ValidationMessaging:
+      node = pdfium::MakeUnique<CXFA_ValidationMessaging>(doc, packet);
+      break;
+    case XFA_Element::Signing:
+      node = pdfium::MakeUnique<CXFA_Signing>(doc, packet);
+      break;
+    case XFA_Element::Script:
+      node = pdfium::MakeUnique<CXFA_Script>(doc, packet);
+      break;
+    case XFA_Element::AddViewerPreferences:
+      node = pdfium::MakeUnique<CXFA_AddViewerPreferences>(doc, packet);
+      break;
+    case XFA_Element::AlwaysEmbed:
+      node = pdfium::MakeUnique<CXFA_AlwaysEmbed>(doc, packet);
+      break;
+    case XFA_Element::PasswordEdit:
+      node = pdfium::MakeUnique<CXFA_PasswordEdit>(doc, packet);
+      break;
+    case XFA_Element::NumericEdit:
+      node = pdfium::MakeUnique<CXFA_NumericEdit>(doc, packet);
+      break;
+    case XFA_Element::EncryptionMethod:
+      node = pdfium::MakeUnique<CXFA_EncryptionMethod>(doc, packet);
+      break;
+    case XFA_Element::Change:
+      node = pdfium::MakeUnique<CXFA_Change>(doc, packet);
+      break;
+    case XFA_Element::PageArea:
+      node = pdfium::MakeUnique<CXFA_PageArea>(doc, packet);
+      break;
+    case XFA_Element::SubmitUrl:
+      node = pdfium::MakeUnique<CXFA_SubmitUrl>(doc, packet);
+      break;
+    case XFA_Element::Oids:
+      node = pdfium::MakeUnique<CXFA_Oids>(doc, packet);
+      break;
+    case XFA_Element::Signature:
+      node = pdfium::MakeUnique<CXFA_Signature>(doc, packet);
+      break;
+    case XFA_Element::ADBE_JSConsole:
+      node = pdfium::MakeUnique<CXFA_ADBE_JSConsole>(doc, packet);
+      break;
+    case XFA_Element::Caption:
+      node = pdfium::MakeUnique<CXFA_Caption>(doc, packet);
+      break;
+    case XFA_Element::Relevant:
+      node = pdfium::MakeUnique<CXFA_Relevant>(doc, packet);
+      break;
+    case XFA_Element::FlipLabel:
+      node = pdfium::MakeUnique<CXFA_FlipLabel>(doc, packet);
+      break;
+    case XFA_Element::ExData:
+      node = pdfium::MakeUnique<CXFA_ExData>(doc, packet);
+      break;
+    case XFA_Element::DayNames:
+      node = pdfium::MakeUnique<CXFA_DayNames>(doc, packet);
+      break;
+    case XFA_Element::SoapAction:
+      node = pdfium::MakeUnique<CXFA_SoapAction>(doc, packet);
+      break;
+    case XFA_Element::DefaultTypeface:
+      node = pdfium::MakeUnique<CXFA_DefaultTypeface>(doc, packet);
+      break;
+    case XFA_Element::Manifest:
+      node = pdfium::MakeUnique<CXFA_Manifest>(doc, packet);
+      break;
+    case XFA_Element::Overflow:
+      node = pdfium::MakeUnique<CXFA_Overflow>(doc, packet);
+      break;
+    case XFA_Element::Linear:
+      node = pdfium::MakeUnique<CXFA_Linear>(doc, packet);
+      break;
+    case XFA_Element::CurrencySymbol:
+      node = pdfium::MakeUnique<CXFA_CurrencySymbol>(doc, packet);
+      break;
+    case XFA_Element::Delete:
+      node = pdfium::MakeUnique<CXFA_Delete>(doc, packet);
+      break;
+    case XFA_Element::DigestMethod:
+      node = pdfium::MakeUnique<CXFA_DigestMethod>(doc, packet);
+      break;
+    case XFA_Element::InstanceManager:
+      node = pdfium::MakeUnique<CXFA_InstanceManager>(doc, packet);
+      break;
+    case XFA_Element::EquateRange:
+      node = pdfium::MakeUnique<CXFA_EquateRange>(doc, packet);
+      break;
+    case XFA_Element::Medium:
+      node = pdfium::MakeUnique<CXFA_Medium>(doc, packet);
+      break;
+    case XFA_Element::TextEdit:
+      node = pdfium::MakeUnique<CXFA_TextEdit>(doc, packet);
+      break;
+    case XFA_Element::TemplateCache:
+      node = pdfium::MakeUnique<CXFA_TemplateCache>(doc, packet);
+      break;
+    case XFA_Element::CompressObjectStream:
+      node = pdfium::MakeUnique<CXFA_CompressObjectStream>(doc, packet);
+      break;
+    case XFA_Element::DataValue:
+      node = pdfium::MakeUnique<CXFA_DataValue>(doc, packet);
+      break;
+    case XFA_Element::AccessibleContent:
+      node = pdfium::MakeUnique<CXFA_AccessibleContent>(doc, packet);
+      break;
+    case XFA_Element::IncludeXDPContent:
+      node = pdfium::MakeUnique<CXFA_IncludeXDPContent>(doc, packet);
+      break;
+    case XFA_Element::XmlConnection:
+      node = pdfium::MakeUnique<CXFA_XmlConnection>(doc, packet);
+      break;
+    case XFA_Element::ValidateApprovalSignatures:
+      node = pdfium::MakeUnique<CXFA_ValidateApprovalSignatures>(doc, packet);
+      break;
+    case XFA_Element::SignData:
+      node = pdfium::MakeUnique<CXFA_SignData>(doc, packet);
+      break;
+    case XFA_Element::Packets:
+      node = pdfium::MakeUnique<CXFA_Packets>(doc, packet);
+      break;
+    case XFA_Element::DatePattern:
+      node = pdfium::MakeUnique<CXFA_DatePattern>(doc, packet);
+      break;
+    case XFA_Element::DuplexOption:
+      node = pdfium::MakeUnique<CXFA_DuplexOption>(doc, packet);
+      break;
+    case XFA_Element::Base:
+      node = pdfium::MakeUnique<CXFA_Base>(doc, packet);
+      break;
+    case XFA_Element::Bind:
+      node = pdfium::MakeUnique<CXFA_Bind>(doc, packet);
+      break;
+    case XFA_Element::Compression:
+      node = pdfium::MakeUnique<CXFA_Compression>(doc, packet);
+      break;
+    case XFA_Element::User:
+      node = pdfium::MakeUnique<CXFA_User>(doc, packet);
+      break;
+    case XFA_Element::Rectangle:
+      node = pdfium::MakeUnique<CXFA_Rectangle>(doc, packet);
+      break;
+    case XFA_Element::EffectiveOutputPolicy:
+      node = pdfium::MakeUnique<CXFA_EffectiveOutputPolicy>(doc, packet);
+      break;
+    case XFA_Element::ADBE_JSDebugger:
+      node = pdfium::MakeUnique<CXFA_ADBE_JSDebugger>(doc, packet);
+      break;
+    case XFA_Element::Acrobat7:
+      node = pdfium::MakeUnique<CXFA_Acrobat7>(doc, packet);
+      break;
+    case XFA_Element::Interactive:
+      node = pdfium::MakeUnique<CXFA_Interactive>(doc, packet);
+      break;
+    case XFA_Element::Locale:
+      node = pdfium::MakeUnique<CXFA_Locale>(doc, packet);
+      break;
+    case XFA_Element::CurrentPage:
+      node = pdfium::MakeUnique<CXFA_CurrentPage>(doc, packet);
+      break;
+    case XFA_Element::Data:
+      node = pdfium::MakeUnique<CXFA_Data>(doc, packet);
+      break;
+    case XFA_Element::Date:
+      node = pdfium::MakeUnique<CXFA_Date>(doc, packet);
+      break;
+    case XFA_Element::Desc:
+      node = pdfium::MakeUnique<CXFA_Desc>(doc, packet);
+      break;
+    case XFA_Element::Encrypt:
+      node = pdfium::MakeUnique<CXFA_Encrypt>(doc, packet);
+      break;
+    case XFA_Element::Draw:
+      node = pdfium::MakeUnique<CXFA_Draw>(doc, packet);
+      break;
+    case XFA_Element::Encryption:
+      node = pdfium::MakeUnique<CXFA_Encryption>(doc, packet);
+      break;
+    case XFA_Element::MeridiemNames:
+      node = pdfium::MakeUnique<CXFA_MeridiemNames>(doc, packet);
+      break;
+    case XFA_Element::Messaging:
+      node = pdfium::MakeUnique<CXFA_Messaging>(doc, packet);
+      break;
+    case XFA_Element::Speak:
+      node = pdfium::MakeUnique<CXFA_Speak>(doc, packet);
+      break;
+    case XFA_Element::DataGroup:
+      node = pdfium::MakeUnique<CXFA_DataGroup>(doc, packet);
+      break;
+    case XFA_Element::Common:
+      node = pdfium::MakeUnique<CXFA_Common>(doc, packet);
+      break;
+    case XFA_Element::Sharptext:
+      node = pdfium::MakeUnique<CXFA_Sharptext>(doc, packet);
+      break;
+    case XFA_Element::PaginationOverride:
+      node = pdfium::MakeUnique<CXFA_PaginationOverride>(doc, packet);
+      break;
+    case XFA_Element::Reasons:
+      node = pdfium::MakeUnique<CXFA_Reasons>(doc, packet);
+      break;
+    case XFA_Element::SignatureProperties:
+      node = pdfium::MakeUnique<CXFA_SignatureProperties>(doc, packet);
+      break;
+    case XFA_Element::Threshold:
+      node = pdfium::MakeUnique<CXFA_Threshold>(doc, packet);
+      break;
+    case XFA_Element::AppearanceFilter:
+      node = pdfium::MakeUnique<CXFA_AppearanceFilter>(doc, packet);
+      break;
+    case XFA_Element::Fill:
+      node = pdfium::MakeUnique<CXFA_Fill>(doc, packet);
+      break;
+    case XFA_Element::Font:
+      node = pdfium::MakeUnique<CXFA_Font>(doc, packet);
+      break;
+    case XFA_Element::Form:
+      node = pdfium::MakeUnique<CXFA_Form>(doc, packet);
+      break;
+    case XFA_Element::MediumInfo:
+      node = pdfium::MakeUnique<CXFA_MediumInfo>(doc, packet);
+      break;
+    case XFA_Element::Certificate:
+      node = pdfium::MakeUnique<CXFA_Certificate>(doc, packet);
+      break;
+    case XFA_Element::Password:
+      node = pdfium::MakeUnique<CXFA_Password>(doc, packet);
+      break;
+    case XFA_Element::RunScripts:
+      node = pdfium::MakeUnique<CXFA_RunScripts>(doc, packet);
+      break;
+    case XFA_Element::Trace:
+      node = pdfium::MakeUnique<CXFA_Trace>(doc, packet);
+      break;
+    case XFA_Element::Float:
+      node = pdfium::MakeUnique<CXFA_Float>(doc, packet);
+      break;
+    case XFA_Element::RenderPolicy:
+      node = pdfium::MakeUnique<CXFA_RenderPolicy>(doc, packet);
+      break;
+    case XFA_Element::Destination:
+      node = pdfium::MakeUnique<CXFA_Destination>(doc, packet);
+      break;
+    case XFA_Element::Value:
+      node = pdfium::MakeUnique<CXFA_Value>(doc, packet);
+      break;
+    case XFA_Element::Bookend:
+      node = pdfium::MakeUnique<CXFA_Bookend>(doc, packet);
+      break;
+    case XFA_Element::ExObject:
+      node = pdfium::MakeUnique<CXFA_ExObject>(doc, packet);
+      break;
+    case XFA_Element::OpenAction:
+      node = pdfium::MakeUnique<CXFA_OpenAction>(doc, packet);
+      break;
+    case XFA_Element::NeverEmbed:
+      node = pdfium::MakeUnique<CXFA_NeverEmbed>(doc, packet);
+      break;
+    case XFA_Element::BindItems:
+      node = pdfium::MakeUnique<CXFA_BindItems>(doc, packet);
+      break;
+    case XFA_Element::Calculate:
+      node = pdfium::MakeUnique<CXFA_Calculate>(doc, packet);
+      break;
+    case XFA_Element::Print:
+      node = pdfium::MakeUnique<CXFA_Print>(doc, packet);
+      break;
+    case XFA_Element::Extras:
+      node = pdfium::MakeUnique<CXFA_Extras>(doc, packet);
+      break;
+    case XFA_Element::Proto:
+      node = pdfium::MakeUnique<CXFA_Proto>(doc, packet);
+      break;
+    case XFA_Element::DSigData:
+      node = pdfium::MakeUnique<CXFA_DSigData>(doc, packet);
+      break;
+    case XFA_Element::Creator:
+      node = pdfium::MakeUnique<CXFA_Creator>(doc, packet);
+      break;
+    case XFA_Element::Connect:
+      node = pdfium::MakeUnique<CXFA_Connect>(doc, packet);
+      break;
+    case XFA_Element::Permissions:
+      node = pdfium::MakeUnique<CXFA_Permissions>(doc, packet);
+      break;
+    case XFA_Element::ConnectionSet:
+      node = pdfium::MakeUnique<CXFA_ConnectionSet>(doc, packet);
+      break;
+    case XFA_Element::Submit:
+      node = pdfium::MakeUnique<CXFA_Submit>(doc, packet);
+      break;
+    case XFA_Element::Range:
+      node = pdfium::MakeUnique<CXFA_Range>(doc, packet);
+      break;
+    case XFA_Element::Linearized:
+      node = pdfium::MakeUnique<CXFA_Linearized>(doc, packet);
+      break;
+    case XFA_Element::Packet:
+      node = pdfium::MakeUnique<CXFA_Packet>(doc, packet);
+      break;
+    case XFA_Element::RootElement:
+      node = pdfium::MakeUnique<CXFA_RootElement>(doc, packet);
+      break;
+    case XFA_Element::PlaintextMetadata:
+      node = pdfium::MakeUnique<CXFA_PlaintextMetadata>(doc, packet);
+      break;
+    case XFA_Element::NumberSymbols:
+      node = pdfium::MakeUnique<CXFA_NumberSymbols>(doc, packet);
+      break;
+    case XFA_Element::PrintHighQuality:
+      node = pdfium::MakeUnique<CXFA_PrintHighQuality>(doc, packet);
+      break;
+    case XFA_Element::Driver:
+      node = pdfium::MakeUnique<CXFA_Driver>(doc, packet);
+      break;
+    case XFA_Element::IncrementalLoad:
+      node = pdfium::MakeUnique<CXFA_IncrementalLoad>(doc, packet);
+      break;
+    case XFA_Element::SubjectDN:
+      node = pdfium::MakeUnique<CXFA_SubjectDN>(doc, packet);
+      break;
+    case XFA_Element::CompressLogicalStructure:
+      node = pdfium::MakeUnique<CXFA_CompressLogicalStructure>(doc, packet);
+      break;
+    case XFA_Element::IncrementalMerge:
+      node = pdfium::MakeUnique<CXFA_IncrementalMerge>(doc, packet);
+      break;
+    case XFA_Element::Radial:
+      node = pdfium::MakeUnique<CXFA_Radial>(doc, packet);
+      break;
+    case XFA_Element::Variables:
+      node = pdfium::MakeUnique<CXFA_Variables>(doc, packet);
+      break;
+    case XFA_Element::TimePatterns:
+      node = pdfium::MakeUnique<CXFA_TimePatterns>(doc, packet);
+      break;
+    case XFA_Element::EffectiveInputPolicy:
+      node = pdfium::MakeUnique<CXFA_EffectiveInputPolicy>(doc, packet);
+      break;
+    case XFA_Element::NameAttr:
+      node = pdfium::MakeUnique<CXFA_NameAttr>(doc, packet);
+      break;
+    case XFA_Element::Conformance:
+      node = pdfium::MakeUnique<CXFA_Conformance>(doc, packet);
+      break;
+    case XFA_Element::Transform:
+      node = pdfium::MakeUnique<CXFA_Transform>(doc, packet);
+      break;
+    case XFA_Element::LockDocument:
+      node = pdfium::MakeUnique<CXFA_LockDocument>(doc, packet);
+      break;
+    case XFA_Element::BreakAfter:
+      node = pdfium::MakeUnique<CXFA_BreakAfter>(doc, packet);
+      break;
+    case XFA_Element::Line:
+      node = pdfium::MakeUnique<CXFA_Line>(doc, packet);
+      break;
+    case XFA_Element::Source:
+      node = pdfium::MakeUnique<CXFA_Source>(doc, packet);
+      break;
+    case XFA_Element::Occur:
+      node = pdfium::MakeUnique<CXFA_Occur>(doc, packet);
+      break;
+    case XFA_Element::PickTrayByPDFSize:
+      node = pdfium::MakeUnique<CXFA_PickTrayByPDFSize>(doc, packet);
+      break;
+    case XFA_Element::MonthNames:
+      node = pdfium::MakeUnique<CXFA_MonthNames>(doc, packet);
+      break;
+    case XFA_Element::Severity:
+      node = pdfium::MakeUnique<CXFA_Severity>(doc, packet);
+      break;
+    case XFA_Element::GroupParent:
+      node = pdfium::MakeUnique<CXFA_GroupParent>(doc, packet);
+      break;
+    case XFA_Element::DocumentAssembly:
+      node = pdfium::MakeUnique<CXFA_DocumentAssembly>(doc, packet);
+      break;
+    case XFA_Element::NumberSymbol:
+      node = pdfium::MakeUnique<CXFA_NumberSymbol>(doc, packet);
+      break;
+    case XFA_Element::Tagged:
+      node = pdfium::MakeUnique<CXFA_Tagged>(doc, packet);
+      break;
+    case XFA_Element::Items:
+      node = pdfium::MakeUnique<CXFA_Items>(doc, packet);
+      break;
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
+  if (!node || !node->IsValidInPacket(packet))
+    return nullptr;
+  return node;
 }
diff --git a/xfa/fxfa/parser/cxfa_node.h b/xfa/fxfa/parser/cxfa_node.h
index c08a9af..097a6a1 100644
--- a/xfa/fxfa/parser/cxfa_node.h
+++ b/xfa/fxfa/parser/cxfa_node.h
@@ -7,17 +7,22 @@
 #ifndef XFA_FXFA_PARSER_CXFA_NODE_H_
 #define XFA_FXFA_PARSER_CXFA_NODE_H_
 
-#include <map>
 #include <memory>
 #include <utility>
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/tree_node.h"
 #include "core/fxge/fx_dib.h"
-#include "fxbarcode/BC_Library.h"
 #include "third_party/base/optional.h"
+#include "third_party/base/span.h"
+#include "xfa/fxfa/cxfa_ffwidget_type.h"
+#include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
+class CFGAS_GEFont;
+class CFX_DIBitmap;
+class CFX_XMLDocument;
 class CFX_XMLNode;
 class CXFA_Bind;
 class CXFA_Border;
@@ -25,21 +30,38 @@
 class CXFA_Caption;
 class CXFA_Event;
 class CXFA_EventParam;
+class CXFA_FFDoc;
 class CXFA_FFDocView;
 class CXFA_Font;
+class CXFA_Keep;
 class CXFA_Margin;
 class CXFA_Occur;
 class CXFA_Para;
 class CXFA_Script;
+class CXFA_TextLayout;
+class CXFA_Ui;
 class CXFA_Validate;
 class CXFA_Value;
-class CXFA_WidgetAcc;
-class IFX_Locale;
+class CXFA_WidgetLayoutData;
+class LocaleIface;
 
 #define XFA_NODEFILTER_Children 0x01
 #define XFA_NODEFILTER_Properties 0x02
 #define XFA_NODEFILTER_OneOfProperty 0x04
 
+enum XFA_CHECKSTATE {
+  XFA_CHECKSTATE_On = 0,
+  XFA_CHECKSTATE_Off = 1,
+  XFA_CHECKSTATE_Neutral = 2,
+};
+
+enum XFA_VALUEPICTURE {
+  XFA_VALUEPICTURE_Raw = 0,
+  XFA_VALUEPICTURE_Display,
+  XFA_VALUEPICTURE_Edit,
+  XFA_VALUEPICTURE_DataBind,
+};
+
 enum XFA_NodeFlag {
   XFA_NodeFlag_None = 0,
   XFA_NodeFlag_Initialized = 1 << 0,
@@ -47,13 +69,11 @@
   XFA_NodeFlag_NeedsInitApp = 1 << 2,
   XFA_NodeFlag_BindFormItems = 1 << 3,
   XFA_NodeFlag_UserInteractive = 1 << 4,
-  XFA_NodeFlag_SkipDataBinding = 1 << 5,
-  XFA_NodeFlag_OwnXMLNode = 1 << 6,
-  XFA_NodeFlag_UnusedNode = 1 << 7,
-  XFA_NodeFlag_LayoutGeneratedNode = 1 << 8
+  XFA_NodeFlag_UnusedNode = 1 << 5,
+  XFA_NodeFlag_LayoutGeneratedNode = 1 << 6
 };
 
-class CXFA_Node : public CXFA_Object {
+class CXFA_Node : public CXFA_Object, public TreeNode<CXFA_Node> {
  public:
   struct PropertyData {
     XFA_Element property;
@@ -67,37 +87,32 @@
     void* default_value;
   };
 
-#ifndef NDEBUG
-  static WideString ElementToName(XFA_Element elem);
-#endif  // NDEBUG
-
-  static WideString AttributeEnumToName(XFA_AttributeEnum item);
-  static Optional<XFA_AttributeEnum> NameToAttributeEnum(
-      const WideStringView& name);
-  static XFA_Attribute NameToAttribute(const WideStringView& name);
-  static WideString AttributeToName(XFA_Attribute attr);
-  static XFA_Element NameToElement(const WideString& name);
   static std::unique_ptr<CXFA_Node> Create(CXFA_Document* doc,
                                            XFA_Element element,
                                            XFA_PacketType packet);
 
   ~CXFA_Node() override;
 
-  bool IsValidInPacket(XFA_PacketType packet) const;
-
   bool HasProperty(XFA_Element property) const;
   bool HasPropertyFlags(XFA_Element property, uint8_t flags) const;
   uint8_t PropertyOccuranceCount(XFA_Element property) const;
 
+  std::pair<CXFA_Node*, int32_t> GetProperty(int32_t index,
+                                             XFA_Element eProperty) const;
+  CXFA_Node* GetOrCreateProperty(int32_t index, XFA_Element eProperty);
+
   void SendAttributeChangeMessage(XFA_Attribute eAttribute, bool bScriptModify);
 
   bool HasAttribute(XFA_Attribute attr) const;
-  XFA_Attribute GetAttribute(size_t i) const;
   XFA_AttributeType GetAttributeType(XFA_Attribute type) const;
 
+  // Note: returns XFA_Attribute::Unknown for invalid indicies.
+  XFA_Attribute GetAttribute(size_t i) const;
+
   XFA_PacketType GetPacketType() const { return m_ePacket; }
 
-  void SetFlag(uint32_t dwFlag, bool bNotify);
+  void SetFlag(uint32_t dwFlag);
+  void SetFlagAndNotify(uint32_t dwFlag);
   void ClearFlag(uint32_t dwFlag);
 
   CXFA_Node* CreateInstanceIfPossible(bool bDataMerge);
@@ -110,7 +125,6 @@
                   bool bMoveDataBindingNodes);
 
   bool IsInitialized() const { return HasFlag(XFA_NodeFlag_Initialized); }
-  bool IsOwnXMLNode() const { return HasFlag(XFA_NodeFlag_OwnXMLNode); }
   bool IsUserInteractive() const {
     return HasFlag(XFA_NodeFlag_UserInteractive);
   }
@@ -119,40 +133,26 @@
     return HasFlag(XFA_NodeFlag_LayoutGeneratedNode);
   }
 
-  void SetBindingNodes(std::vector<UnownedPtr<CXFA_Node>> nodes) {
-    binding_nodes_ = std::move(nodes);
-  }
-  std::vector<UnownedPtr<CXFA_Node>>* GetBindingNodes() {
-    return &binding_nodes_;
-  }
-  void SetBindingNode(CXFA_Node* node) {
-    binding_nodes_.clear();
-    if (node)
-      binding_nodes_.emplace_back(node);
-  }
-  CXFA_Node* GetBindingNode() const {
-    if (binding_nodes_.empty())
-      return nullptr;
-    return binding_nodes_[0].Get();
-  }
-  // TODO(dsinclair): This should not be needed. Nodes should get un-bound when
-  // they're deleted instead of us pointing to bad objects.
-  void ReleaseBindingNodes();
+  bool PresenceRequiresSpace() const;
+  void SetBindingNode(CXFA_Node* node);
+  void SetNodeAndDescendantsUnused();
 
-  bool BindsFormItems() const { return HasFlag(XFA_NodeFlag_BindFormItems); }
   bool HasRemovedChildren() const {
     return HasFlag(XFA_NodeFlag_HasRemovedChildren);
   }
-  bool NeedsInitApp() const { return HasFlag(XFA_NodeFlag_NeedsInitApp); }
 
   bool IsAttributeInXML();
   bool IsFormContainer() const {
     return m_ePacket == XFA_PacketType::Form && IsContainerNode();
   }
-  void SetXMLMappingNode(CFX_XMLNode* pXMLNode) { m_pXMLNode = pXMLNode; }
-  CFX_XMLNode* GetXMLMappingNode() const { return m_pXMLNode; }
+
+  void SetXMLMappingNode(CFX_XMLNode* node) { xml_node_ = node; }
+  CFX_XMLNode* GetXMLMappingNode() const { return xml_node_.Get(); }
   CFX_XMLNode* CreateXMLMappingNode();
-  bool IsNeedSavingXMLNode();
+  bool IsNeedSavingXMLNode() const;
+
+  void SetToXML(const WideString& value);
+
   uint32_t GetNameHash() const { return m_dwNameHash; }
   bool IsUnnamed() const { return m_dwNameHash == 0; }
   CXFA_Node* GetModelNode();
@@ -165,24 +165,19 @@
     return static_cast<T*>(GetChildInternal(index, eType, bOnlyChild));
   }
 
-  int32_t InsertChild(int32_t index, CXFA_Node* pNode);
-  bool InsertChild(CXFA_Node* pNode, CXFA_Node* pBeforeNode);
-  bool RemoveChild(CXFA_Node* pNode, bool bNotify);
+  void InsertChildAndNotify(int32_t index, CXFA_Node* pNode);
+  void InsertChildAndNotify(CXFA_Node* pNode, CXFA_Node* pBeforeNode);
+  void RemoveChildAndNotify(CXFA_Node* pNode, bool bNotify);
 
   CXFA_Node* Clone(bool bRecursive);
 
-  CXFA_Node* GetNextSibling() const { return m_pNext; }
-  CXFA_Node* GetPrevSibling() const;
-  CXFA_Node* GetFirstChild() const { return m_pChild; }
-  CXFA_Node* GetParent() const { return m_pParent; }
-
   CXFA_Node* GetNextContainerSibling() const;
   CXFA_Node* GetPrevContainerSibling() const;
   CXFA_Node* GetFirstContainerChild() const;
   CXFA_Node* GetContainerParent() const;
 
-  std::vector<CXFA_Node*> GetNodeList(uint32_t dwTypeFilter,
-                                      XFA_Element eTypeFilter);
+  std::vector<CXFA_Node*> GetNodeListForType(XFA_Element eTypeFilter);
+  std::vector<CXFA_Node*> GetNodeListWithFilter(uint32_t dwTypeFilter);
   CXFA_Node* CreateSamePacketNode(XFA_Element eType);
   CXFA_Node* CloneTemplateToForm(bool bRecursive);
   CXFA_Node* GetTemplateNodeIfExists() const;
@@ -190,16 +185,18 @@
   CXFA_Node* GetDataDescriptionNode();
   void SetDataDescriptionNode(CXFA_Node* pDataDescriptionNode);
   CXFA_Node* GetBindData();
-  std::vector<UnownedPtr<CXFA_Node>>* GetBindItems();
+  bool HasBindItems() const { return !binding_nodes_.empty(); }
+  std::vector<CXFA_Node*> GetBindItemsCopy() { return binding_nodes_; }
   int32_t AddBindItem(CXFA_Node* pFormNode);
   int32_t RemoveBindItem(CXFA_Node* pFormNode);
-  bool HasBindItem();
-  CXFA_WidgetAcc* GetContainerWidgetAcc();
-  IFX_Locale* GetLocale();
+  bool HasBindItem() const;
+  CXFA_Node* GetContainerNode();
+  LocaleIface* GetLocale();
   Optional<WideString> GetLocaleName();
-  XFA_AttributeEnum GetIntact();
+  XFA_AttributeValue GetIntact();
+  WideString GetNameExpression();
 
-  CXFA_Node* GetFirstChildByName(const WideStringView& wsNodeName) const;
+  CXFA_Node* GetFirstChildByName(WideStringView wsNodeName) const;
   CXFA_Node* GetFirstChildByName(uint32_t dwNodeNameHash) const;
   template <typename T>
   T* GetFirstChildByClass(XFA_Element eType) const {
@@ -207,7 +204,7 @@
   }
   CXFA_Node* GetNextSameNameSibling(uint32_t dwNodeNameHash) const;
   template <typename T>
-  T* GetNextSameNameSibling(const WideStringView& wsNodeName) const {
+  T* GetNextSameNameSibling(WideStringView wsNodeName) const {
     return static_cast<T*>(GetNextSameNameSiblingInternal(wsNodeName));
   }
   template <typename T>
@@ -215,28 +212,31 @@
     return static_cast<T*>(GetNextSameClassSiblingInternal(eType));
   }
 
-  int32_t GetNodeSameNameIndex() const;
-  int32_t GetNodeSameClassIndex() const;
-  CXFA_Node* GetInstanceMgrOfSubform();
+  CXFA_Node* GetOneChildNamed(WideStringView wsName);
+  CXFA_Node* GetOneChildOfClass(WideStringView wsClass);
 
-  CXFA_Occur* GetOccurIfExists();
+  std::vector<CXFA_Node*> GetSiblings(bool bIsClassName);
+  size_t GetIndex(bool bIsProperty, bool bIsClassIndex);
+  size_t GetIndexByName();
+  size_t GetIndexByClassName();
+
+  CXFA_Node* GetInstanceMgrOfSubform();
 
   Optional<bool> GetDefaultBoolean(XFA_Attribute attr) const;
   Optional<int32_t> GetDefaultInteger(XFA_Attribute attr) const;
   Optional<CXFA_Measurement> GetDefaultMeasurement(XFA_Attribute attr) const;
   Optional<WideString> GetDefaultCData(XFA_Attribute attr) const;
-  Optional<XFA_AttributeEnum> GetDefaultEnum(XFA_Attribute attr) const;
+  Optional<XFA_AttributeValue> GetDefaultEnum(XFA_Attribute attr) const;
 
-  void SyncValue(const WideString& wsValue, bool bNotify);
+  bool IsOpenAccess() const;
 
-  bool IsOpenAccess();
-
+  CXFA_Occur* GetOccurIfExists();
   CXFA_Border* GetBorderIfExists() const;
   CXFA_Border* GetOrCreateBorderIfPossible();
   CXFA_Caption* GetCaptionIfExists() const;
-
   CXFA_Font* GetFontIfExists() const;
   CXFA_Font* GetOrCreateFontIfPossible();
+
   float GetFontSize() const;
   FX_ARGB GetTextColor() const;
   float GetLineHeight() const;
@@ -247,52 +247,141 @@
   CXFA_Validate* GetValidateIfExists() const;
   CXFA_Validate* GetOrCreateValidateIfPossible();
 
-  CXFA_Value* GetDefaultValueIfExists();
   CXFA_Value* GetFormValueIfExists() const;
   WideString GetRawValue();
-  int32_t GetRotate();
 
-  CXFA_Bind* GetBindIfExists() const;
-
+  int32_t GetRotate() const;
   Optional<float> TryWidth();
-  Optional<float> TryHeight();
-  Optional<float> TryMinWidth();
-  Optional<float> TryMinHeight();
-  Optional<float> TryMaxWidth();
-  Optional<float> TryMaxHeight();
 
   CXFA_Node* GetExclGroupIfExists();
 
-  int32_t ProcessEvent(CXFA_FFDocView* docView,
-                       XFA_AttributeEnum iActivity,
-                       CXFA_EventParam* pEventParam);
-  int32_t ProcessEvent(CXFA_FFDocView* docView,
-                       CXFA_Event* event,
-                       CXFA_EventParam* pEventParam);
-  int32_t ProcessCalculate(CXFA_FFDocView* docView);
-  int32_t ProcessValidate(CXFA_FFDocView* docView, int32_t iFlags);
+  XFA_EventError ProcessEvent(CXFA_FFDocView* pDocView,
+                              XFA_AttributeValue iActivity,
+                              CXFA_EventParam* pEventParam);
+  XFA_EventError ProcessCalculate(CXFA_FFDocView* pDocView);
+  XFA_EventError ProcessValidate(CXFA_FFDocView* pDocView, int32_t iFlags);
+  XFA_EventError ExecuteScript(CXFA_FFDocView* pDocView,
+                               CXFA_Script* script,
+                               CXFA_EventParam* pEventParam);
+  std::pair<XFA_EventError, bool> ExecuteBoolScript(
+      CXFA_FFDocView* pDocView,
+      CXFA_Script* script,
+      CXFA_EventParam* pEventParam);
 
-  int32_t ExecuteScript(CXFA_FFDocView* docView,
-                        CXFA_Script* script,
-                        CXFA_EventParam* pEventParam);
-  std::pair<int32_t, bool> ExecuteBoolScript(CXFA_FFDocView* docView,
-                                             CXFA_Script* script,
-                                             CXFA_EventParam* pEventParam);
+  CXFA_Node* GetUIChildNode();
 
-  // TODO(dsinclair): Figure out how to move this to cxfa_barcode.
-  WideString GetBarcodeType();
-  Optional<BC_CHAR_ENCODING> GetBarcodeAttribute_CharEncoding();
-  Optional<bool> GetBarcodeAttribute_Checksum();
-  Optional<int32_t> GetBarcodeAttribute_DataLength();
-  Optional<char> GetBarcodeAttribute_StartChar();
-  Optional<char> GetBarcodeAttribute_EndChar();
-  Optional<int32_t> GetBarcodeAttribute_ECLevel();
-  Optional<int32_t> GetBarcodeAttribute_ModuleWidth();
-  Optional<int32_t> GetBarcodeAttribute_ModuleHeight();
-  Optional<bool> GetBarcodeAttribute_PrintChecksum();
-  Optional<BC_TEXT_LOC> GetBarcodeAttribute_TextLocation();
-  Optional<bool> GetBarcodeAttribute_Truncate();
-  Optional<int8_t> GetBarcodeAttribute_WideNarrowRatio();
+  // NOTE: value returned is often determined by child UI node, and
+  // can't be used to infer anything about this particual node itself.
+  XFA_FFWidgetType GetFFWidgetType();
+
+  CFX_RectF GetUIMargin();
+  CXFA_Border* GetUIBorder();
+
+  void SetPreNull(bool val) { m_bPreNull = val; }
+  bool IsNull() const { return m_bIsNull; }
+  void SetIsNull(bool val) { m_bIsNull = val; }
+
+  void SetWidgetReady() { is_widget_ready_ = true; }
+  bool IsWidgetReady() const { return is_widget_ready_; }
+  std::vector<CXFA_Event*> GetEventByActivity(XFA_AttributeValue iActivity,
+                                              bool bIsFormReady);
+
+  void ResetData();
+  void StartWidgetLayout(CXFA_FFDoc* doc,
+                         float* pCalcWidth,
+                         float* pCalcHeight);
+  Optional<float> FindSplitPos(CXFA_FFDocView* pDocView,
+                               size_t szBlockIndex,
+                               float fCalcHeight);
+
+  bool LoadCaption(CXFA_FFDoc* doc);
+  CXFA_TextLayout* GetCaptionTextLayout();
+  CXFA_TextLayout* GetTextLayout();
+
+  bool LoadImageImage(CXFA_FFDoc* doc);
+  bool LoadImageEditImage(CXFA_FFDoc* doc);
+  CFX_Size GetImageDpi() const;
+  CFX_Size GetImageEditDpi() const;
+
+  RetainPtr<CFX_DIBitmap> GetImageImage();
+  RetainPtr<CFX_DIBitmap> GetImageEditImage();
+  void SetImageImage(const RetainPtr<CFX_DIBitmap>& newImage);
+  void SetImageEditImage(const RetainPtr<CFX_DIBitmap>& newImage);
+
+  RetainPtr<CFGAS_GEFont> GetFDEFont(CXFA_FFDoc* doc);
+
+  bool IsListBox();
+  bool IsRadioButton();
+  bool IsMultiLine();
+
+  bool HasButtonRollover();
+  bool HasButtonDown();
+
+  float GetCheckButtonSize();
+
+  XFA_CHECKSTATE GetCheckState();
+  void SetCheckState(XFA_CHECKSTATE eCheckState, bool bNotify);
+
+  CXFA_Node* GetSelectedMember();
+  CXFA_Node* SetSelectedMember(WideStringView wsName, bool bNotify);
+  void SetSelectedMemberByValue(WideStringView wsValue,
+                                bool bNotify,
+                                bool bScriptModify,
+                                bool bSyncData);
+
+  CXFA_Node* GetExclGroupFirstMember();
+  CXFA_Node* GetExclGroupNextMember(CXFA_Node* pNode);
+
+  bool IsChoiceListAllowTextEntry();
+  int32_t CountChoiceListItems(bool bSaveValue);
+  Optional<WideString> GetChoiceListItem(int32_t nIndex, bool bSaveValue);
+  bool IsChoiceListMultiSelect();
+  bool IsChoiceListCommitOnSelect();
+  std::vector<WideString> GetChoiceListItems(bool bSaveValue);
+
+  int32_t CountSelectedItems();
+  int32_t GetSelectedItem(int32_t nIndex);
+  std::vector<int32_t> GetSelectedItems();
+  std::vector<WideString> GetSelectedItemsValue();
+  void SetSelectedItems(const std::vector<int32_t>& iSelArray,
+                        bool bNotify,
+                        bool bScriptModify,
+                        bool bSyncData);
+  void InsertItem(const WideString& wsLabel,
+                  const WideString& wsValue,
+                  bool bNotify);
+  bool DeleteItem(int32_t nIndex, bool bNotify, bool bScriptModify);
+  void ClearAllSelections();
+
+  bool GetItemState(int32_t nIndex);
+  void SetItemState(int32_t nIndex,
+                    bool bSelected,
+                    bool bNotify,
+                    bool bScriptModify,
+                    bool bSyncData);
+
+  WideString GetItemValue(WideStringView wsLabel);
+
+  bool IsHorizontalScrollPolicyOff();
+  bool IsVerticalScrollPolicyOff();
+  Optional<int32_t> GetNumberOfCells();
+
+  bool SetValue(XFA_VALUEPICTURE eValueType, const WideString& wsValue);
+  WideString GetValue(XFA_VALUEPICTURE eValueType);
+
+  WideString GetPictureContent(XFA_VALUEPICTURE ePicture);
+  WideString GetNormalizeDataValue(const WideString& wsValue);
+  WideString GetFormatDataValue(const WideString& wsValue);
+  WideString NormalizeNumStr(const WideString& wsValue);
+
+  std::pair<XFA_Element, int32_t> GetMaxChars();
+  int32_t GetFracDigits();
+  int32_t GetLeadDigits();
+
+  WideString NumericLimit(const WideString& wsValue);
+
+  bool IsTransparent() const;
+  bool IsProperty() const;
 
  protected:
   CXFA_Node(CXFA_Document* pDoc,
@@ -300,63 +389,117 @@
             uint32_t validPackets,
             XFA_ObjectType oType,
             XFA_Element eType,
-            const PropertyData* properties,
-            const AttributeData* attributes,
-            const WideStringView& elementName,
-            std::unique_ptr<CJX_Object> js_node);
-  CXFA_Node(CXFA_Document* pDoc,
-            XFA_PacketType ePacket,
-            uint32_t validPackets,
-            XFA_ObjectType oType,
-            XFA_Element eType,
-            const PropertyData* properties,
-            const AttributeData* attributes,
-            const WideStringView& elementName);
+            pdfium::span<const PropertyData> properties,
+            pdfium::span<const AttributeData> attributes,
+            std::unique_ptr<CJX_Object> js_object);
+
+  virtual XFA_Element GetValueNodeType() const;
+  virtual XFA_FFWidgetType GetDefaultFFWidgetType() const;
 
  private:
-  void ProcessScriptTestValidate(CXFA_FFDocView* docView,
+  void ProcessScriptTestValidate(CXFA_FFDocView* pDocView,
                                  CXFA_Validate* validate,
-                                 int32_t iRet,
+                                 XFA_EventError iRet,
                                  bool pRetValue,
                                  bool bVersionFlag);
-  int32_t ProcessFormatTestValidate(CXFA_FFDocView* docView,
-                                    CXFA_Validate* validate,
-                                    bool bVersionFlag);
-  int32_t ProcessNullTestValidate(CXFA_FFDocView* docView,
-                                  CXFA_Validate* validate,
-                                  int32_t iFlags,
-                                  bool bVersionFlag);
+  XFA_EventError ProcessFormatTestValidate(CXFA_FFDocView* pDocView,
+                                           CXFA_Validate* validate,
+                                           bool bVersionFlag);
+  XFA_EventError ProcessNullTestValidate(CXFA_FFDocView* pDocView,
+                                         CXFA_Validate* validate,
+                                         int32_t iFlags,
+                                         bool bVersionFlag);
   WideString GetValidateCaptionName(bool bVersionFlag);
   WideString GetValidateMessage(bool bError, bool bVersionFlag);
 
   bool HasFlag(XFA_NodeFlag dwFlag) const;
-  CXFA_Node* Deprecated_GetPrevSibling();
   const PropertyData* GetPropertyData(XFA_Element property) const;
   const AttributeData* GetAttributeData(XFA_Attribute attr) const;
-  Optional<XFA_Element> GetFirstPropertyWithFlag(uint8_t flag);
-  void OnRemoved(bool bNotify);
+  Optional<XFA_Element> GetFirstPropertyWithFlag(uint8_t flag) const;
+  void OnRemoved(bool bNotify) const;
   Optional<void*> GetDefaultValue(XFA_Attribute attr,
                                   XFA_AttributeType eType) const;
-  CXFA_Node* GetChildInternal(size_t index, XFA_Element eType, bool bOnlyChild);
+  CXFA_Node* GetChildInternal(size_t index,
+                              XFA_Element eType,
+                              bool bOnlyChild) const;
   CXFA_Node* GetFirstChildByClassInternal(XFA_Element eType) const;
-  CXFA_Node* GetNextSameNameSiblingInternal(
-      const WideStringView& wsNodeName) const;
+  CXFA_Node* GetNextSameNameSiblingInternal(WideStringView wsNodeName) const;
   CXFA_Node* GetNextSameClassSiblingInternal(XFA_Element eType) const;
+  void CalcCaptionSize(CXFA_FFDoc* doc, CFX_SizeF* pszCap);
+  bool CalculateFieldAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
+  bool CalculateWidgetAutoSize(CFX_SizeF* pSize);
+  bool CalculateTextEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
+  bool CalculateCheckButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
+  bool CalculatePushButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
+  CFX_SizeF CalculateImageSize(float img_width,
+                               float img_height,
+                               const CFX_Size& dpi);
+  bool CalculateImageEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
+  bool CalculateImageAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
+  float CalculateWidgetAutoHeight(float fHeightCalc);
+  float CalculateWidgetAutoWidth(float fWidthCalc);
+  float GetWidthWithoutMargin(float fWidthCalc) const;
+  float GetHeightWithoutMargin(float fHeightCalc) const;
+  void CalculateTextContentSize(CXFA_FFDoc* doc, CFX_SizeF* pSize);
+  CFX_SizeF CalculateAccWidthAndHeight(CXFA_FFDoc* doc, float fWidth);
+  void InitLayoutData();
+  void StartTextLayout(CXFA_FFDoc* doc, float* pCalcWidth, float* pCalcHeight);
 
-  const PropertyData* const m_Properties;
-  const AttributeData* const m_Attributes;
+  void InsertListTextItem(CXFA_Node* pItems,
+                          const WideString& wsText,
+                          int32_t nIndex);
+  WideString GetItemLabel(WideStringView wsValue) const;
+
+  std::pair<XFA_FFWidgetType, CXFA_Ui*> CreateChildUIAndValueNodesIfNeeded();
+  void CreateValueNodeIfNeeded(CXFA_Value* value, CXFA_Node* pUIChild);
+  CXFA_Node* CreateUINodeIfNeeded(CXFA_Ui* ui, XFA_Element type);
+  bool IsValidInPacket(XFA_PacketType packet) const;
+  void SetImageEdit(const WideString& wsContentType,
+                    const WideString& wsHref,
+                    const WideString& wsData);
+  CXFA_Node* GetBindingNode() const {
+    if (binding_nodes_.empty())
+      return nullptr;
+    return binding_nodes_[0];
+  }
+  bool BindsFormItems() const { return HasFlag(XFA_NodeFlag_BindFormItems); }
+  bool NeedsInitApp() const { return HasFlag(XFA_NodeFlag_NeedsInitApp); }
+  void SyncValue(const WideString& wsValue, bool bNotify);
+  CXFA_Value* GetDefaultValueIfExists();
+  CXFA_Bind* GetBindIfExists() const;
+  Optional<XFA_AttributeValue> GetIntactFromKeep(
+      const CXFA_Keep* pKeep,
+      XFA_AttributeValue eLayoutType) const;
+  CXFA_Node* GetTransparentParent();
+
+  Optional<float> TryHeight();
+  Optional<float> TryMinWidth();
+  Optional<float> TryMinHeight();
+  Optional<float> TryMaxWidth();
+  Optional<float> TryMaxHeight();
+  XFA_EventError ProcessEventInternal(CXFA_FFDocView* pDocView,
+                                      XFA_AttributeValue iActivity,
+                                      CXFA_Event* event,
+                                      CXFA_EventParam* pEventParam);
+
+  CFX_XMLDocument* GetXMLDocument() const;
+
+  const pdfium::span<const PropertyData> m_Properties;
+  const pdfium::span<const AttributeData> m_Attributes;
   const uint32_t m_ValidPackets;
-  CXFA_Node* m_pNext;
-  CXFA_Node* m_pChild;
-  CXFA_Node* m_pLastChild;
-  CXFA_Node* m_pParent;
-  CFX_XMLNode* m_pXMLNode;
+  UnownedPtr<CFX_XMLNode> xml_node_;
   const XFA_PacketType m_ePacket;
   uint8_t m_ExecuteRecursionDepth = 0;
-  uint16_t m_uNodeFlags;
-  uint32_t m_dwNameHash;
-  CXFA_Node* m_pAuxNode;
-  std::vector<UnownedPtr<CXFA_Node>> binding_nodes_;
+  uint16_t m_uNodeFlags = XFA_NodeFlag_None;
+  uint32_t m_dwNameHash = 0;
+  CXFA_Node* m_pAuxNode = nullptr;         // Raw, node tree cleanup order.
+  std::vector<CXFA_Node*> binding_nodes_;  // Raw, node tree cleanup order.
+  bool m_bIsNull = true;
+  bool m_bPreNull = true;
+  bool is_widget_ready_ = false;
+  std::unique_ptr<CXFA_WidgetLayoutData> m_pLayoutData;
+  CXFA_Ui* ui_ = nullptr;
+  XFA_FFWidgetType ff_widget_type_ = XFA_FFWidgetType::kNone;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NODE_H_
diff --git a/xfa/fxfa/parser/cxfa_node_statics.cpp b/xfa/fxfa/parser/cxfa_node_statics.cpp
deleted file mode 100644
index 93860c6..0000000
--- a/xfa/fxfa/parser/cxfa_node_statics.cpp
+++ /dev/null
@@ -1,2948 +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 <memory>
-
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/parser/cxfa_accessiblecontent.h"
-#include "xfa/fxfa/parser/cxfa_acrobat.h"
-#include "xfa/fxfa/parser/cxfa_acrobat7.h"
-#include "xfa/fxfa/parser/cxfa_adbe_jsconsole.h"
-#include "xfa/fxfa/parser/cxfa_adbe_jsdebugger.h"
-#include "xfa/fxfa/parser/cxfa_addsilentprint.h"
-#include "xfa/fxfa/parser/cxfa_addviewerpreferences.h"
-#include "xfa/fxfa/parser/cxfa_adjustdata.h"
-#include "xfa/fxfa/parser/cxfa_adobeextensionlevel.h"
-#include "xfa/fxfa/parser/cxfa_agent.h"
-#include "xfa/fxfa/parser/cxfa_alwaysembed.h"
-#include "xfa/fxfa/parser/cxfa_amd.h"
-#include "xfa/fxfa/parser/cxfa_appearancefilter.h"
-#include "xfa/fxfa/parser/cxfa_arc.h"
-#include "xfa/fxfa/parser/cxfa_area.h"
-#include "xfa/fxfa/parser/cxfa_assist.h"
-#include "xfa/fxfa/parser/cxfa_attributes.h"
-#include "xfa/fxfa/parser/cxfa_autosave.h"
-#include "xfa/fxfa/parser/cxfa_barcode.h"
-#include "xfa/fxfa/parser/cxfa_base.h"
-#include "xfa/fxfa/parser/cxfa_batchoutput.h"
-#include "xfa/fxfa/parser/cxfa_behavioroverride.h"
-#include "xfa/fxfa/parser/cxfa_bind.h"
-#include "xfa/fxfa/parser/cxfa_binditems.h"
-#include "xfa/fxfa/parser/cxfa_bookend.h"
-#include "xfa/fxfa/parser/cxfa_boolean.h"
-#include "xfa/fxfa/parser/cxfa_border.h"
-#include "xfa/fxfa/parser/cxfa_break.h"
-#include "xfa/fxfa/parser/cxfa_breakafter.h"
-#include "xfa/fxfa/parser/cxfa_breakbefore.h"
-#include "xfa/fxfa/parser/cxfa_button.h"
-#include "xfa/fxfa/parser/cxfa_cache.h"
-#include "xfa/fxfa/parser/cxfa_calculate.h"
-#include "xfa/fxfa/parser/cxfa_calendarsymbols.h"
-#include "xfa/fxfa/parser/cxfa_caption.h"
-#include "xfa/fxfa/parser/cxfa_certificate.h"
-#include "xfa/fxfa/parser/cxfa_certificates.h"
-#include "xfa/fxfa/parser/cxfa_change.h"
-#include "xfa/fxfa/parser/cxfa_checkbutton.h"
-#include "xfa/fxfa/parser/cxfa_choicelist.h"
-#include "xfa/fxfa/parser/cxfa_color.h"
-#include "xfa/fxfa/parser/cxfa_comb.h"
-#include "xfa/fxfa/parser/cxfa_command.h"
-#include "xfa/fxfa/parser/cxfa_common.h"
-#include "xfa/fxfa/parser/cxfa_compress.h"
-#include "xfa/fxfa/parser/cxfa_compression.h"
-#include "xfa/fxfa/parser/cxfa_compresslogicalstructure.h"
-#include "xfa/fxfa/parser/cxfa_compressobjectstream.h"
-#include "xfa/fxfa/parser/cxfa_config.h"
-#include "xfa/fxfa/parser/cxfa_conformance.h"
-#include "xfa/fxfa/parser/cxfa_connect.h"
-#include "xfa/fxfa/parser/cxfa_connectionset.h"
-#include "xfa/fxfa/parser/cxfa_connectstring.h"
-#include "xfa/fxfa/parser/cxfa_contentarea.h"
-#include "xfa/fxfa/parser/cxfa_contentcopy.h"
-#include "xfa/fxfa/parser/cxfa_copies.h"
-#include "xfa/fxfa/parser/cxfa_corner.h"
-#include "xfa/fxfa/parser/cxfa_creator.h"
-#include "xfa/fxfa/parser/cxfa_currencysymbol.h"
-#include "xfa/fxfa/parser/cxfa_currencysymbols.h"
-#include "xfa/fxfa/parser/cxfa_currentpage.h"
-#include "xfa/fxfa/parser/cxfa_data.h"
-#include "xfa/fxfa/parser/cxfa_datagroup.h"
-#include "xfa/fxfa/parser/cxfa_datamodel.h"
-#include "xfa/fxfa/parser/cxfa_datavalue.h"
-#include "xfa/fxfa/parser/cxfa_date.h"
-#include "xfa/fxfa/parser/cxfa_datepattern.h"
-#include "xfa/fxfa/parser/cxfa_datepatterns.h"
-#include "xfa/fxfa/parser/cxfa_datetime.h"
-#include "xfa/fxfa/parser/cxfa_datetimeedit.h"
-#include "xfa/fxfa/parser/cxfa_datetimesymbols.h"
-#include "xfa/fxfa/parser/cxfa_day.h"
-#include "xfa/fxfa/parser/cxfa_daynames.h"
-#include "xfa/fxfa/parser/cxfa_debug.h"
-#include "xfa/fxfa/parser/cxfa_decimal.h"
-#include "xfa/fxfa/parser/cxfa_defaulttypeface.h"
-#include "xfa/fxfa/parser/cxfa_defaultui.h"
-#include "xfa/fxfa/parser/cxfa_delete.h"
-#include "xfa/fxfa/parser/cxfa_delta.h"
-#include "xfa/fxfa/parser/cxfa_deltas.h"
-#include "xfa/fxfa/parser/cxfa_desc.h"
-#include "xfa/fxfa/parser/cxfa_destination.h"
-#include "xfa/fxfa/parser/cxfa_digestmethod.h"
-#include "xfa/fxfa/parser/cxfa_digestmethods.h"
-#include "xfa/fxfa/parser/cxfa_documentassembly.h"
-#include "xfa/fxfa/parser/cxfa_draw.h"
-#include "xfa/fxfa/parser/cxfa_driver.h"
-#include "xfa/fxfa/parser/cxfa_dsigdata.h"
-#include "xfa/fxfa/parser/cxfa_duplexoption.h"
-#include "xfa/fxfa/parser/cxfa_dynamicrender.h"
-#include "xfa/fxfa/parser/cxfa_edge.h"
-#include "xfa/fxfa/parser/cxfa_effectiveinputpolicy.h"
-#include "xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h"
-#include "xfa/fxfa/parser/cxfa_embed.h"
-#include "xfa/fxfa/parser/cxfa_encoding.h"
-#include "xfa/fxfa/parser/cxfa_encodings.h"
-#include "xfa/fxfa/parser/cxfa_encrypt.h"
-#include "xfa/fxfa/parser/cxfa_encryption.h"
-#include "xfa/fxfa/parser/cxfa_encryptionlevel.h"
-#include "xfa/fxfa/parser/cxfa_encryptionmethod.h"
-#include "xfa/fxfa/parser/cxfa_encryptionmethods.h"
-#include "xfa/fxfa/parser/cxfa_enforce.h"
-#include "xfa/fxfa/parser/cxfa_equate.h"
-#include "xfa/fxfa/parser/cxfa_equaterange.h"
-#include "xfa/fxfa/parser/cxfa_era.h"
-#include "xfa/fxfa/parser/cxfa_eranames.h"
-#include "xfa/fxfa/parser/cxfa_event.h"
-#include "xfa/fxfa/parser/cxfa_exclgroup.h"
-#include "xfa/fxfa/parser/cxfa_exclude.h"
-#include "xfa/fxfa/parser/cxfa_excludens.h"
-#include "xfa/fxfa/parser/cxfa_exdata.h"
-#include "xfa/fxfa/parser/cxfa_execute.h"
-#include "xfa/fxfa/parser/cxfa_exobject.h"
-#include "xfa/fxfa/parser/cxfa_extras.h"
-#include "xfa/fxfa/parser/cxfa_field.h"
-#include "xfa/fxfa/parser/cxfa_fill.h"
-#include "xfa/fxfa/parser/cxfa_filter.h"
-#include "xfa/fxfa/parser/cxfa_fliplabel.h"
-#include "xfa/fxfa/parser/cxfa_float.h"
-#include "xfa/fxfa/parser/cxfa_font.h"
-#include "xfa/fxfa/parser/cxfa_fontinfo.h"
-#include "xfa/fxfa/parser/cxfa_form.h"
-#include "xfa/fxfa/parser/cxfa_format.h"
-#include "xfa/fxfa/parser/cxfa_formfieldfilling.h"
-#include "xfa/fxfa/parser/cxfa_groupparent.h"
-#include "xfa/fxfa/parser/cxfa_handler.h"
-#include "xfa/fxfa/parser/cxfa_hyphenation.h"
-#include "xfa/fxfa/parser/cxfa_ifempty.h"
-#include "xfa/fxfa/parser/cxfa_image.h"
-#include "xfa/fxfa/parser/cxfa_imageedit.h"
-#include "xfa/fxfa/parser/cxfa_includexdpcontent.h"
-#include "xfa/fxfa/parser/cxfa_incrementalload.h"
-#include "xfa/fxfa/parser/cxfa_incrementalmerge.h"
-#include "xfa/fxfa/parser/cxfa_insert.h"
-#include "xfa/fxfa/parser/cxfa_instancemanager.h"
-#include "xfa/fxfa/parser/cxfa_integer.h"
-#include "xfa/fxfa/parser/cxfa_interactive.h"
-#include "xfa/fxfa/parser/cxfa_issuers.h"
-#include "xfa/fxfa/parser/cxfa_items.h"
-#include "xfa/fxfa/parser/cxfa_jog.h"
-#include "xfa/fxfa/parser/cxfa_keep.h"
-#include "xfa/fxfa/parser/cxfa_keyusage.h"
-#include "xfa/fxfa/parser/cxfa_labelprinter.h"
-#include "xfa/fxfa/parser/cxfa_layout.h"
-#include "xfa/fxfa/parser/cxfa_level.h"
-#include "xfa/fxfa/parser/cxfa_line.h"
-#include "xfa/fxfa/parser/cxfa_linear.h"
-#include "xfa/fxfa/parser/cxfa_linearized.h"
-#include "xfa/fxfa/parser/cxfa_locale.h"
-#include "xfa/fxfa/parser/cxfa_localeset.h"
-#include "xfa/fxfa/parser/cxfa_lockdocument.h"
-#include "xfa/fxfa/parser/cxfa_log.h"
-#include "xfa/fxfa/parser/cxfa_manifest.h"
-#include "xfa/fxfa/parser/cxfa_map.h"
-#include "xfa/fxfa/parser/cxfa_margin.h"
-#include "xfa/fxfa/parser/cxfa_mdp.h"
-#include "xfa/fxfa/parser/cxfa_medium.h"
-#include "xfa/fxfa/parser/cxfa_mediuminfo.h"
-#include "xfa/fxfa/parser/cxfa_meridiem.h"
-#include "xfa/fxfa/parser/cxfa_meridiemnames.h"
-#include "xfa/fxfa/parser/cxfa_message.h"
-#include "xfa/fxfa/parser/cxfa_messaging.h"
-#include "xfa/fxfa/parser/cxfa_mode.h"
-#include "xfa/fxfa/parser/cxfa_modifyannots.h"
-#include "xfa/fxfa/parser/cxfa_month.h"
-#include "xfa/fxfa/parser/cxfa_monthnames.h"
-#include "xfa/fxfa/parser/cxfa_msgid.h"
-#include "xfa/fxfa/parser/cxfa_nameattr.h"
-#include "xfa/fxfa/parser/cxfa_neverembed.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_numberofcopies.h"
-#include "xfa/fxfa/parser/cxfa_numberpattern.h"
-#include "xfa/fxfa/parser/cxfa_numberpatterns.h"
-#include "xfa/fxfa/parser/cxfa_numbersymbol.h"
-#include "xfa/fxfa/parser/cxfa_numbersymbols.h"
-#include "xfa/fxfa/parser/cxfa_numericedit.h"
-#include "xfa/fxfa/parser/cxfa_occur.h"
-#include "xfa/fxfa/parser/cxfa_oid.h"
-#include "xfa/fxfa/parser/cxfa_oids.h"
-#include "xfa/fxfa/parser/cxfa_openaction.h"
-#include "xfa/fxfa/parser/cxfa_operation.h"
-#include "xfa/fxfa/parser/cxfa_output.h"
-#include "xfa/fxfa/parser/cxfa_outputbin.h"
-#include "xfa/fxfa/parser/cxfa_outputxsl.h"
-#include "xfa/fxfa/parser/cxfa_overflow.h"
-#include "xfa/fxfa/parser/cxfa_overprint.h"
-#include "xfa/fxfa/parser/cxfa_packet.h"
-#include "xfa/fxfa/parser/cxfa_packets.h"
-#include "xfa/fxfa/parser/cxfa_pagearea.h"
-#include "xfa/fxfa/parser/cxfa_pageoffset.h"
-#include "xfa/fxfa/parser/cxfa_pagerange.h"
-#include "xfa/fxfa/parser/cxfa_pageset.h"
-#include "xfa/fxfa/parser/cxfa_pagination.h"
-#include "xfa/fxfa/parser/cxfa_paginationoverride.h"
-#include "xfa/fxfa/parser/cxfa_para.h"
-#include "xfa/fxfa/parser/cxfa_part.h"
-#include "xfa/fxfa/parser/cxfa_password.h"
-#include "xfa/fxfa/parser/cxfa_passwordedit.h"
-#include "xfa/fxfa/parser/cxfa_pattern.h"
-#include "xfa/fxfa/parser/cxfa_pcl.h"
-#include "xfa/fxfa/parser/cxfa_pdf.h"
-#include "xfa/fxfa/parser/cxfa_pdfa.h"
-#include "xfa/fxfa/parser/cxfa_permissions.h"
-#include "xfa/fxfa/parser/cxfa_picktraybypdfsize.h"
-#include "xfa/fxfa/parser/cxfa_picture.h"
-#include "xfa/fxfa/parser/cxfa_plaintextmetadata.h"
-#include "xfa/fxfa/parser/cxfa_presence.h"
-#include "xfa/fxfa/parser/cxfa_present.h"
-#include "xfa/fxfa/parser/cxfa_print.h"
-#include "xfa/fxfa/parser/cxfa_printername.h"
-#include "xfa/fxfa/parser/cxfa_printhighquality.h"
-#include "xfa/fxfa/parser/cxfa_printscaling.h"
-#include "xfa/fxfa/parser/cxfa_producer.h"
-#include "xfa/fxfa/parser/cxfa_proto.h"
-#include "xfa/fxfa/parser/cxfa_ps.h"
-#include "xfa/fxfa/parser/cxfa_psmap.h"
-#include "xfa/fxfa/parser/cxfa_query.h"
-#include "xfa/fxfa/parser/cxfa_radial.h"
-#include "xfa/fxfa/parser/cxfa_range.h"
-#include "xfa/fxfa/parser/cxfa_reason.h"
-#include "xfa/fxfa/parser/cxfa_reasons.h"
-#include "xfa/fxfa/parser/cxfa_record.h"
-#include "xfa/fxfa/parser/cxfa_recordset.h"
-#include "xfa/fxfa/parser/cxfa_rectangle.h"
-#include "xfa/fxfa/parser/cxfa_ref.h"
-#include "xfa/fxfa/parser/cxfa_relevant.h"
-#include "xfa/fxfa/parser/cxfa_rename.h"
-#include "xfa/fxfa/parser/cxfa_renderpolicy.h"
-#include "xfa/fxfa/parser/cxfa_rootelement.h"
-#include "xfa/fxfa/parser/cxfa_runscripts.h"
-#include "xfa/fxfa/parser/cxfa_script.h"
-#include "xfa/fxfa/parser/cxfa_scriptmodel.h"
-#include "xfa/fxfa/parser/cxfa_select.h"
-#include "xfa/fxfa/parser/cxfa_setproperty.h"
-#include "xfa/fxfa/parser/cxfa_severity.h"
-#include "xfa/fxfa/parser/cxfa_sharptext.h"
-#include "xfa/fxfa/parser/cxfa_sharpxhtml.h"
-#include "xfa/fxfa/parser/cxfa_sharpxml.h"
-#include "xfa/fxfa/parser/cxfa_signature.h"
-#include "xfa/fxfa/parser/cxfa_signatureproperties.h"
-#include "xfa/fxfa/parser/cxfa_signdata.h"
-#include "xfa/fxfa/parser/cxfa_signing.h"
-#include "xfa/fxfa/parser/cxfa_silentprint.h"
-#include "xfa/fxfa/parser/cxfa_soapaction.h"
-#include "xfa/fxfa/parser/cxfa_soapaddress.h"
-#include "xfa/fxfa/parser/cxfa_solid.h"
-#include "xfa/fxfa/parser/cxfa_source.h"
-#include "xfa/fxfa/parser/cxfa_sourceset.h"
-#include "xfa/fxfa/parser/cxfa_speak.h"
-#include "xfa/fxfa/parser/cxfa_staple.h"
-#include "xfa/fxfa/parser/cxfa_startnode.h"
-#include "xfa/fxfa/parser/cxfa_startpage.h"
-#include "xfa/fxfa/parser/cxfa_stipple.h"
-#include "xfa/fxfa/parser/cxfa_subform.h"
-#include "xfa/fxfa/parser/cxfa_subformset.h"
-#include "xfa/fxfa/parser/cxfa_subjectdn.h"
-#include "xfa/fxfa/parser/cxfa_subjectdns.h"
-#include "xfa/fxfa/parser/cxfa_submit.h"
-#include "xfa/fxfa/parser/cxfa_submitformat.h"
-#include "xfa/fxfa/parser/cxfa_submiturl.h"
-#include "xfa/fxfa/parser/cxfa_subsetbelow.h"
-#include "xfa/fxfa/parser/cxfa_suppressbanner.h"
-#include "xfa/fxfa/parser/cxfa_tagged.h"
-#include "xfa/fxfa/parser/cxfa_template.h"
-#include "xfa/fxfa/parser/cxfa_templatecache.h"
-#include "xfa/fxfa/parser/cxfa_text.h"
-#include "xfa/fxfa/parser/cxfa_textedit.h"
-#include "xfa/fxfa/parser/cxfa_threshold.h"
-#include "xfa/fxfa/parser/cxfa_time.h"
-#include "xfa/fxfa/parser/cxfa_timepattern.h"
-#include "xfa/fxfa/parser/cxfa_timepatterns.h"
-#include "xfa/fxfa/parser/cxfa_timestamp.h"
-#include "xfa/fxfa/parser/cxfa_to.h"
-#include "xfa/fxfa/parser/cxfa_tooltip.h"
-#include "xfa/fxfa/parser/cxfa_trace.h"
-#include "xfa/fxfa/parser/cxfa_transform.h"
-#include "xfa/fxfa/parser/cxfa_traversal.h"
-#include "xfa/fxfa/parser/cxfa_traverse.h"
-#include "xfa/fxfa/parser/cxfa_type.h"
-#include "xfa/fxfa/parser/cxfa_typeface.h"
-#include "xfa/fxfa/parser/cxfa_typefaces.h"
-#include "xfa/fxfa/parser/cxfa_ui.h"
-#include "xfa/fxfa/parser/cxfa_update.h"
-#include "xfa/fxfa/parser/cxfa_uri.h"
-#include "xfa/fxfa/parser/cxfa_user.h"
-#include "xfa/fxfa/parser/cxfa_validate.h"
-#include "xfa/fxfa/parser/cxfa_validateapprovalsignatures.h"
-#include "xfa/fxfa/parser/cxfa_validationmessaging.h"
-#include "xfa/fxfa/parser/cxfa_value.h"
-#include "xfa/fxfa/parser/cxfa_variables.h"
-#include "xfa/fxfa/parser/cxfa_version.h"
-#include "xfa/fxfa/parser/cxfa_versioncontrol.h"
-#include "xfa/fxfa/parser/cxfa_viewerpreferences.h"
-#include "xfa/fxfa/parser/cxfa_webclient.h"
-#include "xfa/fxfa/parser/cxfa_whitespace.h"
-#include "xfa/fxfa/parser/cxfa_window.h"
-#include "xfa/fxfa/parser/cxfa_wsdladdress.h"
-#include "xfa/fxfa/parser/cxfa_wsdlconnection.h"
-#include "xfa/fxfa/parser/cxfa_xdc.h"
-#include "xfa/fxfa/parser/cxfa_xdp.h"
-#include "xfa/fxfa/parser/cxfa_xfa.h"
-#include "xfa/fxfa/parser/cxfa_xmlconnection.h"
-#include "xfa/fxfa/parser/cxfa_xsdconnection.h"
-#include "xfa/fxfa/parser/cxfa_xsl.h"
-#include "xfa/fxfa/parser/cxfa_zpl.h"
-
-namespace {
-
-struct ElementNameInfo {
-  uint32_t hash;
-  XFA_Element element;
-} ElementNameToEnum[] = {
-    {0x23ee3 /* ps */, XFA_Element::Ps},
-    {0x25363 /* to */, XFA_Element::To},
-    {0x2587e /* ui */, XFA_Element::Ui},
-    {0x1c648b /* recordSet */, XFA_Element::RecordSet},
-    {0x171428f /* subsetBelow */, XFA_Element::SubsetBelow},
-    {0x1a0776a /* subformSet */, XFA_Element::SubformSet},
-    {0x2340d70 /* adobeExtensionLevel */, XFA_Element::AdobeExtensionLevel},
-    {0x2c1c7f1 /* typeface */, XFA_Element::Typeface},
-    {0x5518c25 /* break */, XFA_Element::Break},
-    {0x5fff523 /* fontInfo */, XFA_Element::FontInfo},
-    {0x653a227 /* numberPattern */, XFA_Element::NumberPattern},
-    {0x65b4a05 /* dynamicRender */, XFA_Element::DynamicRender},
-    {0x7e4362e /* printScaling */, XFA_Element::PrintScaling},
-    {0x7fe6d3a /* checkButton */, XFA_Element::CheckButton},
-    {0x80cf58f /* datePatterns */, XFA_Element::DatePatterns},
-    {0x811929d /* sourceSet */, XFA_Element::SourceSet},
-    {0x9f9d612 /* amd */, XFA_Element::Amd},
-    {0x9f9efb6 /* arc */, XFA_Element::Arc},
-    {0xa48835e /* day */, XFA_Element::Day},
-    {0xa6328b8 /* era */, XFA_Element::Era},
-    {0xae6a0a0 /* jog */, XFA_Element::Jog},
-    {0xb1b3d22 /* log */, XFA_Element::Log},
-    {0xb35439e /* map */, XFA_Element::Map},
-    {0xb355301 /* mdp */, XFA_Element::Mdp},
-    {0xb420438 /* breakBefore */, XFA_Element::BreakBefore},
-    {0xb6a091c /* oid */, XFA_Element::Oid},
-    {0xb84389f /* pcl */, XFA_Element::Pcl},
-    {0xb843dba /* pdf */, XFA_Element::Pdf},
-    {0xbb8df5d /* ref */, XFA_Element::Ref},
-    {0xc080cd0 /* uri */, XFA_Element::Uri},
-    {0xc56afbf /* xdc */, XFA_Element::Xdc},
-    {0xc56afcc /* xdp */, XFA_Element::Xdp},
-    {0xc56b9ff /* xfa */, XFA_Element::Xfa},
-    {0xc56fcb7 /* xsl */, XFA_Element::Xsl},
-    {0xc8b89d6 /* zpl */, XFA_Element::Zpl},
-    {0xc9bae94 /* cache */, XFA_Element::Cache},
-    {0xcb016be /* margin */, XFA_Element::Margin},
-    {0xe1378fe /* keyUsage */, XFA_Element::KeyUsage},
-    {0xfe3596a /* exclude */, XFA_Element::Exclude},
-    {0x10395ac7 /* choiceList */, XFA_Element::ChoiceList},
-    {0x1059ec18 /* level */, XFA_Element::Level},
-    {0x10874804 /* labelPrinter */, XFA_Element::LabelPrinter},
-    {0x10c40e03 /* calendarSymbols */, XFA_Element::CalendarSymbols},
-    {0x10f1ea24 /* para */, XFA_Element::Para},
-    {0x10f1ea37 /* part */, XFA_Element::Part},
-    {0x1140975b /* pdfa */, XFA_Element::Pdfa},
-    {0x1154efe6 /* filter */, XFA_Element::Filter},
-    {0x13f41de1 /* present */, XFA_Element::Present},
-    {0x1827e6ea /* pagination */, XFA_Element::Pagination},
-    {0x18463707 /* encoding */, XFA_Element::Encoding},
-    {0x185e41e2 /* event */, XFA_Element::Event},
-    {0x1adb142d /* whitespace */, XFA_Element::Whitespace},
-    {0x1f3f64c3 /* defaultUi */, XFA_Element::DefaultUi},
-    {0x204e87cb /* dataModel */, XFA_Element::DataModel},
-    {0x2057b350 /* barcode */, XFA_Element::Barcode},
-    {0x20596bad /* timePattern */, XFA_Element::TimePattern},
-    {0x210b74d3 /* batchOutput */, XFA_Element::BatchOutput},
-    {0x212ff0e2 /* enforce */, XFA_Element::Enforce},
-    {0x21d351b4 /* currencySymbols */, XFA_Element::CurrencySymbols},
-    {0x21db83c5 /* addSilentPrint */, XFA_Element::AddSilentPrint},
-    {0x22266258 /* rename */, XFA_Element::Rename},
-    {0x226ca8f1 /* operation */, XFA_Element::Operation},
-    {0x23e27b84 /* typefaces */, XFA_Element::Typefaces},
-    {0x23f4aa75 /* subjectDNs */, XFA_Element::SubjectDNs},
-    {0x240d5e8e /* issuers */, XFA_Element::Issuers},
-    {0x24a52f8a /* wsdlConnection */, XFA_Element::WsdlConnection},
-    {0x254ebd07 /* debug */, XFA_Element::Debug},
-    {0x2655c66a /* delta */, XFA_Element::Delta},
-    {0x26c0daec /* eraNames */, XFA_Element::EraNames},
-    {0x273ab03b /* modifyAnnots */, XFA_Element::ModifyAnnots},
-    {0x27875bb4 /* startNode */, XFA_Element::StartNode},
-    {0x285d0dbc /* button */, XFA_Element::Button},
-    {0x28dee6e9 /* format */, XFA_Element::Format},
-    {0x2a23349e /* border */, XFA_Element::Border},
-    {0x2ae67f19 /* area */, XFA_Element::Area},
-    {0x2c3c4c67 /* hyphenation */, XFA_Element::Hyphenation},
-    {0x2d08af85 /* text */, XFA_Element::Text},
-    {0x2d71b00f /* time */, XFA_Element::Time},
-    {0x2f16a382 /* type */, XFA_Element::Type},
-    {0x2fe057e9 /* overprint */, XFA_Element::Overprint},
-    {0x302aee16 /* certificates */, XFA_Element::Certificates},
-    {0x30b227df /* encryptionMethods */, XFA_Element::EncryptionMethods},
-    {0x32b900d1 /* setProperty */, XFA_Element::SetProperty},
-    {0x337d9e45 /* printerName */, XFA_Element::PrinterName},
-    {0x33edda4b /* startPage */, XFA_Element::StartPage},
-    {0x381943e4 /* pageOffset */, XFA_Element::PageOffset},
-    {0x382106cd /* dateTime */, XFA_Element::DateTime},
-    {0x386e7421 /* comb */, XFA_Element::Comb},
-    {0x390acd9e /* pattern */, XFA_Element::Pattern},
-    {0x3942163e /* ifEmpty */, XFA_Element::IfEmpty},
-    {0x39944a7b /* suppressBanner */, XFA_Element::SuppressBanner},
-    {0x3b3c3dca /* outputBin */, XFA_Element::OutputBin},
-    {0x3b8a4024 /* field */, XFA_Element::Field},
-    {0x3c15352f /* agent */, XFA_Element::Agent},
-    {0x3d7e8668 /* outputXSL */, XFA_Element::OutputXSL},
-    {0x3e1c91c5 /* adjustData */, XFA_Element::AdjustData},
-    {0x3e7a9408 /* autoSave */, XFA_Element::AutoSave},
-    {0x3ecead94 /* contentArea */, XFA_Element::ContentArea},
-    {0x3fadaec0 /* wsdlAddress */, XFA_Element::WsdlAddress},
-    {0x40623b5b /* solid */, XFA_Element::Solid},
-    {0x41f0bd76 /* dateTimeSymbols */, XFA_Element::DateTimeSymbols},
-    {0x444e7523 /* encryptionLevel */, XFA_Element::EncryptionLevel},
-    {0x4523af55 /* edge */, XFA_Element::Edge},
-    {0x45d5e3c1 /* stipple */, XFA_Element::Stipple},
-    {0x475e4e87 /* attributes */, XFA_Element::Attributes},
-    {0x487a8c87 /* versionControl */, XFA_Element::VersionControl},
-    {0x48e5248c /* meridiem */, XFA_Element::Meridiem},
-    {0x48f36719 /* exclGroup */, XFA_Element::ExclGroup},
-    {0x4977356b /* toolTip */, XFA_Element::ToolTip},
-    {0x499afecc /* compress */, XFA_Element::Compress},
-    {0x4a0c4948 /* reason */, XFA_Element::Reason},
-    {0x4bdcce13 /* execute */, XFA_Element::Execute},
-    {0x4c56b216 /* contentCopy */, XFA_Element::ContentCopy},
-    {0x4cc176d3 /* dateTimeEdit */, XFA_Element::DateTimeEdit},
-    {0x4e1e39b6 /* config */, XFA_Element::Config},
-    {0x4e2d6083 /* image */, XFA_Element::Image},
-    {0x4e814150 /* #xHTML */, XFA_Element::SharpxHTML},
-    {0x4f2388c1 /* numberOfCopies */, XFA_Element::NumberOfCopies},
-    {0x4f512e30 /* behaviorOverride */, XFA_Element::BehaviorOverride},
-    {0x4fdc3454 /* timeStamp */, XFA_Element::TimeStamp},
-    {0x51d90546 /* month */, XFA_Element::Month},
-    {0x523437e4 /* viewerPreferences */, XFA_Element::ViewerPreferences},
-    {0x53abc1c6 /* scriptModel */, XFA_Element::ScriptModel},
-    {0x54034c2f /* decimal */, XFA_Element::Decimal},
-    {0x54202c9e /* subform */, XFA_Element::Subform},
-    {0x542c7300 /* select */, XFA_Element::Select},
-    {0x5436d198 /* window */, XFA_Element::Window},
-    {0x5473b6dc /* localeSet */, XFA_Element::LocaleSet},
-    {0x56ae179e /* handler */, XFA_Element::Handler},
-    {0x570ce835 /* presence */, XFA_Element::Presence},
-    {0x5779d65f /* record */, XFA_Element::Record},
-    {0x59c8f27d /* embed */, XFA_Element::Embed},
-    {0x5a50e9e6 /* version */, XFA_Element::Version},
-    {0x5b8383df /* command */, XFA_Element::Command},
-    {0x5c43c6c3 /* copies */, XFA_Element::Copies},
-    {0x5e0c2c49 /* staple */, XFA_Element::Staple},
-    {0x5e5083dd /* submitFormat */, XFA_Element::SubmitFormat},
-    {0x5e8c5d20 /* boolean */, XFA_Element::Boolean},
-    {0x60490a85 /* message */, XFA_Element::Message},
-    {0x60d4c8b1 /* output */, XFA_Element::Output},
-    {0x61810081 /* psMap */, XFA_Element::PsMap},
-    {0x62bd904b /* excludeNS */, XFA_Element::ExcludeNS},
-    {0x669d4f77 /* assist */, XFA_Element::Assist},
-    {0x67334a1c /* picture */, XFA_Element::Picture},
-    {0x67fe7334 /* traversal */, XFA_Element::Traversal},
-    {0x6894589c /* silentPrint */, XFA_Element::SilentPrint},
-    {0x68a16bbd /* webClient */, XFA_Element::WebClient},
-    {0x6a4bc084 /* producer */, XFA_Element::Producer},
-    {0x6a9e04c9 /* corner */, XFA_Element::Corner},
-    {0x6ccd7274 /* msgId */, XFA_Element::MsgId},
-    {0x6e67921f /* color */, XFA_Element::Color},
-    {0x6ec217a5 /* keep */, XFA_Element::Keep},
-    {0x6eef1116 /* query */, XFA_Element::Query},
-    {0x7033bfd5 /* insert */, XFA_Element::Insert},
-    {0x704af389 /* imageEdit */, XFA_Element::ImageEdit},
-    {0x7233018a /* validate */, XFA_Element::Validate},
-    {0x72ba47b4 /* digestMethods */, XFA_Element::DigestMethods},
-    {0x72f2aa7a /* numberPatterns */, XFA_Element::NumberPatterns},
-    {0x74caed29 /* pageSet */, XFA_Element::PageSet},
-    {0x7568e6ae /* integer */, XFA_Element::Integer},
-    {0x76182db9 /* soapAddress */, XFA_Element::SoapAddress},
-    {0x773146c5 /* equate */, XFA_Element::Equate},
-    {0x77d449dd /* formFieldFilling */, XFA_Element::FormFieldFilling},
-    {0x7889d68a /* pageRange */, XFA_Element::PageRange},
-    {0x7baca2e3 /* update */, XFA_Element::Update},
-    {0x7ce89001 /* connectString */, XFA_Element::ConnectString},
-    {0x7d9fd7c5 /* mode */, XFA_Element::Mode},
-    {0x7e7e845e /* layout */, XFA_Element::Layout},
-    {0x7e845c34 /* #xml */, XFA_Element::Sharpxml},
-    {0x7fb341df /* xsdConnection */, XFA_Element::XsdConnection},
-    {0x7ffb51cc /* traverse */, XFA_Element::Traverse},
-    {0x80203b5a /* encodings */, XFA_Element::Encodings},
-    {0x803550fc /* template */, XFA_Element::Template},
-    {0x803d5bbc /* acrobat */, XFA_Element::Acrobat},
-    {0x821d6569 /* validationMessaging */, XFA_Element::ValidationMessaging},
-    {0x830e688f /* signing */, XFA_Element::Signing},
-    {0x83dab9f5 /* script */, XFA_Element::Script},
-    {0x8411ebcd /* addViewerPreferences */, XFA_Element::AddViewerPreferences},
-    {0x8777642e /* alwaysEmbed */, XFA_Element::AlwaysEmbed},
-    {0x877a6b39 /* passwordEdit */, XFA_Element::PasswordEdit},
-    {0x87e84c99 /* numericEdit */, XFA_Element::NumericEdit},
-    {0x8852cdec /* encryptionMethod */, XFA_Element::EncryptionMethod},
-    {0x891f4606 /* change */, XFA_Element::Change},
-    {0x89939f36 /* pageArea */, XFA_Element::PageArea},
-    {0x8a9d6247 /* submitUrl */, XFA_Element::SubmitUrl},
-    {0x8ad8b90f /* oids */, XFA_Element::Oids},
-    {0x8b036f32 /* signature */, XFA_Element::Signature},
-    {0x8b128efb /* ADBE_JSConsole */, XFA_Element::ADBE_JSConsole},
-    {0x8bcfe96e /* caption */, XFA_Element::Caption},
-    {0x8e1c2921 /* relevant */, XFA_Element::Relevant},
-    {0x8e3f0a4b /* flipLabel */, XFA_Element::FlipLabel},
-    {0x900280b7 /* exData */, XFA_Element::ExData},
-    {0x91e80352 /* dayNames */, XFA_Element::DayNames},
-    {0x93113b11 /* soapAction */, XFA_Element::SoapAction},
-    {0x938b09f6 /* defaultTypeface */, XFA_Element::DefaultTypeface},
-    {0x95b37897 /* manifest */, XFA_Element::Manifest},
-    {0x97b76b54 /* overflow */, XFA_Element::Overflow},
-    {0x9a57861b /* linear */, XFA_Element::Linear},
-    {0x9ad5a821 /* currencySymbol */, XFA_Element::CurrencySymbol},
-    {0x9c6471b3 /* delete */, XFA_Element::Delete},
-    {0x9deea61d /* deltas */, XFA_Element::Deltas},
-    {0x9e67de21 /* digestMethod */, XFA_Element::DigestMethod},
-    {0x9f3e9510 /* instanceManager */, XFA_Element::InstanceManager},
-    {0xa0799892 /* equateRange */, XFA_Element::EquateRange},
-    {0xa084a381 /* medium */, XFA_Element::Medium},
-    {0xa1211b8b /* textEdit */, XFA_Element::TextEdit},
-    {0xa17008f0 /* templateCache */, XFA_Element::TemplateCache},
-    {0xa4f7b88f /* compressObjectStream */, XFA_Element::CompressObjectStream},
-    {0xa65f5d17 /* dataValue */, XFA_Element::DataValue},
-    {0xa6caaa89 /* accessibleContent */, XFA_Element::AccessibleContent},
-    {0xa94cc00b /* includeXDPContent */, XFA_Element::IncludeXDPContent},
-    {0xa9b081a1 /* xmlConnection */, XFA_Element::XmlConnection},
-    {0xab2a3b74 /* validateApprovalSignatures */,
-     XFA_Element::ValidateApprovalSignatures},
-    {0xab8c5a2b /* signData */, XFA_Element::SignData},
-    {0xabaa2ceb /* packets */, XFA_Element::Packets},
-    {0xadba359c /* datePattern */, XFA_Element::DatePattern},
-    {0xae222b2b /* duplexOption */, XFA_Element::DuplexOption},
-    {0xb012effb /* base */, XFA_Element::Base},
-    {0xb0e5485d /* bind */, XFA_Element::Bind},
-    {0xb45d61b2 /* compression */, XFA_Element::Compression},
-    {0xb563f0ff /* user */, XFA_Element::User},
-    {0xb5848ad5 /* rectangle */, XFA_Element::Rectangle},
-    {0xb6dacb72 /* effectiveOutputPolicy */,
-     XFA_Element::EffectiveOutputPolicy},
-    {0xb7d7654d /* ADBE_JSDebugger */, XFA_Element::ADBE_JSDebugger},
-    {0xbab37f73 /* acrobat7 */, XFA_Element::Acrobat7},
-    {0xbc70081e /* interactive */, XFA_Element::Interactive},
-    {0xbc8fa350 /* locale */, XFA_Element::Locale},
-    {0xbcd44940 /* currentPage */, XFA_Element::CurrentPage},
-    {0xbde9abda /* data */, XFA_Element::Data},
-    {0xbde9abde /* date */, XFA_Element::Date},
-    {0xbe52dfbf /* desc */, XFA_Element::Desc},
-    {0xbf4b6405 /* encrypt */, XFA_Element::Encrypt},
-    {0xbfa87cce /* draw */, XFA_Element::Draw},
-    {0xc181ff4b /* encryption */, XFA_Element::Encryption},
-    {0xc1970f40 /* meridiemNames */, XFA_Element::MeridiemNames},
-    {0xc5ad9f5e /* messaging */, XFA_Element::Messaging},
-    {0xc69549f4 /* speak */, XFA_Element::Speak},
-    {0xc7743dc7 /* dataGroup */, XFA_Element::DataGroup},
-    {0xc7eb20e9 /* common */, XFA_Element::Common},
-    {0xc85d4528 /* #text */, XFA_Element::Sharptext},
-    {0xc861556a /* paginationOverride */, XFA_Element::PaginationOverride},
-    {0xc903dabb /* reasons */, XFA_Element::Reasons},
-    {0xc9a8127f /* signatureProperties */, XFA_Element::SignatureProperties},
-    {0xca010c2d /* threshold */, XFA_Element::Threshold},
-    {0xcb4c5e96 /* appearanceFilter */, XFA_Element::AppearanceFilter},
-    {0xcc92aba7 /* fill */, XFA_Element::Fill},
-    {0xcd308b77 /* font */, XFA_Element::Font},
-    {0xcd309ff4 /* form */, XFA_Element::Form},
-    {0xcebcca2d /* mediumInfo */, XFA_Element::MediumInfo},
-    {0xcfe0d643 /* certificate */, XFA_Element::Certificate},
-    {0xd012c033 /* password */, XFA_Element::Password},
-    {0xd01604bd /* runScripts */, XFA_Element::RunScripts},
-    {0xd1227e6f /* trace */, XFA_Element::Trace},
-    {0xd1532876 /* float */, XFA_Element::Float},
-    {0xd17a6c30 /* renderPolicy */, XFA_Element::RenderPolicy},
-    {0xd58aa962 /* destination */, XFA_Element::Destination},
-    {0xd6e27f1d /* value */, XFA_Element::Value},
-    {0xd7a14462 /* bookend */, XFA_Element::Bookend},
-    {0xd8c31254 /* exObject */, XFA_Element::ExObject},
-    {0xda6a8590 /* openAction */, XFA_Element::OpenAction},
-    {0xdab4fb7d /* neverEmbed */, XFA_Element::NeverEmbed},
-    {0xdb98475f /* bindItems */, XFA_Element::BindItems},
-    {0xdbfbe02e /* calculate */, XFA_Element::Calculate},
-    {0xdd7676ed /* print */, XFA_Element::Print},
-    {0xdde273d7 /* extras */, XFA_Element::Extras},
-    {0xde146b34 /* proto */, XFA_Element::Proto},
-    {0xdf059321 /* dSigData */, XFA_Element::DSigData},
-    {0xdfccf030 /* creator */, XFA_Element::Creator},
-    {0xdff78c6a /* connect */, XFA_Element::Connect},
-    {0xe11a2cbc /* permissions */, XFA_Element::Permissions},
-    {0xe14c801c /* connectionSet */, XFA_Element::ConnectionSet},
-    {0xe1c83a14 /* submit */, XFA_Element::Submit},
-    {0xe29821cd /* range */, XFA_Element::Range},
-    {0xe38d83c7 /* linearized */, XFA_Element::Linearized},
-    {0xe3aa2578 /* packet */, XFA_Element::Packet},
-    {0xe3aa860e /* rootElement */, XFA_Element::RootElement},
-    {0xe3e553fa /* plaintextMetadata */, XFA_Element::PlaintextMetadata},
-    {0xe3e6e4f2 /* numberSymbols */, XFA_Element::NumberSymbols},
-    {0xe3f067f6 /* printHighQuality */, XFA_Element::PrintHighQuality},
-    {0xe3fd078c /* driver */, XFA_Element::Driver},
-    {0xe48b34f2 /* incrementalLoad */, XFA_Element::IncrementalLoad},
-    {0xe550e7c2 /* subjectDN */, XFA_Element::SubjectDN},
-    {0xe6669a78 /* compressLogicalStructure */,
-     XFA_Element::CompressLogicalStructure},
-    {0xe7a7ea02 /* incrementalMerge */, XFA_Element::IncrementalMerge},
-    {0xe948530d /* radial */, XFA_Element::Radial},
-    {0xea8d6999 /* variables */, XFA_Element::Variables},
-    {0xeaa142c0 /* timePatterns */, XFA_Element::TimePatterns},
-    {0xeb943a71 /* effectiveInputPolicy */, XFA_Element::EffectiveInputPolicy},
-    {0xef04a2bc /* nameAttr */, XFA_Element::NameAttr},
-    {0xf07222ab /* conformance */, XFA_Element::Conformance},
-    {0xf0aaaadc /* transform */, XFA_Element::Transform},
-    {0xf1433e88 /* lockDocument */, XFA_Element::LockDocument},
-    {0xf54eb997 /* breakAfter */, XFA_Element::BreakAfter},
-    {0xf616da28 /* line */, XFA_Element::Line},
-    {0xf7055fb1 /* source */, XFA_Element::Source},
-    {0xf7eebe1c /* occur */, XFA_Element::Occur},
-    {0xf8d10d97 /* pickTrayByPDFSize */, XFA_Element::PickTrayByPDFSize},
-    {0xf8f19e3a /* monthNames */, XFA_Element::MonthNames},
-    {0xf984149b /* severity */, XFA_Element::Severity},
-    {0xf9bcb037 /* groupParent */, XFA_Element::GroupParent},
-    {0xfbc42fff /* documentAssembly */, XFA_Element::DocumentAssembly},
-    {0xfc78159f /* numberSymbol */, XFA_Element::NumberSymbol},
-    {0xfcbd606c /* tagged */, XFA_Element::Tagged},
-    {0xff063802 /* items */, XFA_Element::Items},
-};
-
-struct AttributeNameInfo {
-  uint32_t hash;
-  XFA_Attribute attribute;
-} AttributeNameInfoToEnum[] = {
-    {0x68 /* h */, XFA_Attribute::H},
-    {0x77 /* w */, XFA_Attribute::W},
-    {0x78 /* x */, XFA_Attribute::X},
-    {0x79 /* y */, XFA_Attribute::Y},
-    {0x21aed /* id */, XFA_Attribute::Id},
-    {0x25363 /* to */, XFA_Attribute::To},
-    {0xcb0ac9 /* lineThrough */, XFA_Attribute::LineThrough},
-    {0x2282c73 /* hAlign */, XFA_Attribute::HAlign},
-    {0x2c1c7f1 /* typeface */, XFA_Attribute::Typeface},
-    {0x3106c3a /* beforeTarget */, XFA_Attribute::BeforeTarget},
-    {0x31b19c1 /* name */, XFA_Attribute::Name},
-    {0x3848b3f /* next */, XFA_Attribute::Next},
-    {0x43e349b /* dataRowCount */, XFA_Attribute::DataRowCount},
-    {0x5518c25 /* break */, XFA_Attribute::Break},
-    {0x5ce6195 /* vScrollPolicy */, XFA_Attribute::VScrollPolicy},
-    {0x8c74ae9 /* fontHorizontalScale */, XFA_Attribute::FontHorizontalScale},
-    {0x8d4f1c7 /* textIndent */, XFA_Attribute::TextIndent},
-    {0x97c1c65 /* context */, XFA_Attribute::Context},
-    {0x9876578 /* trayOut */, XFA_Attribute::TrayOut},
-    {0xa2e3514 /* cap */, XFA_Attribute::Cap},
-    {0xb3543a6 /* max */, XFA_Attribute::Max},
-    {0xb356ca4 /* min */, XFA_Attribute::Min},
-    {0xbb8df5d /* ref */, XFA_Attribute::Ref},
-    {0xbb8f3df /* rid */, XFA_Attribute::Rid},
-    {0xc080cd3 /* url */, XFA_Attribute::Url},
-    {0xc0811ed /* use */, XFA_Attribute::Use},
-    {0xcfea02e /* leftInset */, XFA_Attribute::LeftInset},
-    {0x1026c59d /* widows */, XFA_Attribute::Widows},
-    {0x1059ec18 /* level */, XFA_Attribute::Level},
-    {0x1356caf8 /* bottomInset */, XFA_Attribute::BottomInset},
-    {0x13a08bdb /* overflowTarget */, XFA_Attribute::OverflowTarget},
-    {0x1414d431 /* allowMacro */, XFA_Attribute::AllowMacro},
-    {0x14a32d52 /* pagePosition */, XFA_Attribute::PagePosition},
-    {0x1517dfa1 /* columnWidths */, XFA_Attribute::ColumnWidths},
-    {0x169134a1 /* overflowLeader */, XFA_Attribute::OverflowLeader},
-    {0x1b8dce3e /* action */, XFA_Attribute::Action},
-    {0x1e459b8f /* nonRepudiation */, XFA_Attribute::NonRepudiation},
-    {0x1ec8ab2c /* rate */, XFA_Attribute::Rate},
-    {0x1ef3a64a /* allowRichText */, XFA_Attribute::AllowRichText},
-    {0x2038c9b2 /* role */, XFA_Attribute::Role},
-    {0x20914367 /* overflowTrailer */, XFA_Attribute::OverflowTrailer},
-    {0x226ca8f1 /* operation */, XFA_Attribute::Operation},
-    {0x24d85167 /* timeout */, XFA_Attribute::Timeout},
-    {0x25764436 /* topInset */, XFA_Attribute::TopInset},
-    {0x25839852 /* access */, XFA_Attribute::Access},
-    {0x268b7ec1 /* commandType */, XFA_Attribute::CommandType},
-    {0x28dee6e9 /* format */, XFA_Attribute::Format},
-    {0x28e17e91 /* dataPrep */, XFA_Attribute::DataPrep},
-    {0x292b88fe /* widgetData */, XFA_Attribute::WidgetData},
-    {0x29418bb7 /* abbr */, XFA_Attribute::Abbr},
-    {0x2a82d99c /* marginRight */, XFA_Attribute::MarginRight},
-    {0x2b5df51e /* dataDescription */, XFA_Attribute::DataDescription},
-    {0x2bb3f470 /* encipherOnly */, XFA_Attribute::EncipherOnly},
-    {0x2cd79033 /* kerningMode */, XFA_Attribute::KerningMode},
-    {0x2ee7678f /* rotate */, XFA_Attribute::Rotate},
-    {0x2f105f72 /* wordCharacterCount */, XFA_Attribute::WordCharacterCount},
-    {0x2f16a382 /* type */, XFA_Attribute::Type},
-    {0x34ae103c /* reserve */, XFA_Attribute::Reserve},
-    {0x3650557e /* textLocation */, XFA_Attribute::TextLocation},
-    {0x39cdb0a2 /* priority */, XFA_Attribute::Priority},
-    {0x3a0273a6 /* underline */, XFA_Attribute::Underline},
-    {0x3b582286 /* moduleWidth */, XFA_Attribute::ModuleWidth},
-    {0x3d123c26 /* hyphenate */, XFA_Attribute::Hyphenate},
-    {0x3e7af94f /* listen */, XFA_Attribute::Listen},
-    {0x4156ee3f /* delimiter */, XFA_Attribute::Delimiter},
-    {0x42fed1fd /* contentType */, XFA_Attribute::ContentType},
-    {0x453eaf38 /* startNew */, XFA_Attribute::StartNew},
-    {0x45a6daf8 /* eofAction */, XFA_Attribute::EofAction},
-    {0x47cfa43a /* allowNeutral */, XFA_Attribute::AllowNeutral},
-    {0x47d03490 /* connection */, XFA_Attribute::Connection},
-    {0x4873c601 /* baselineShift */, XFA_Attribute::BaselineShift},
-    {0x4b319767 /* overlinePeriod */, XFA_Attribute::OverlinePeriod},
-    {0x4b8bc840 /* fracDigits */, XFA_Attribute::FracDigits},
-    {0x4ef3d02c /* orientation */, XFA_Attribute::Orientation},
-    {0x4fdc3454 /* timeStamp */, XFA_Attribute::TimeStamp},
-    {0x52666f1c /* printCheckDigit */, XFA_Attribute::PrintCheckDigit},
-    {0x534729c9 /* marginLeft */, XFA_Attribute::MarginLeft},
-    {0x5392ea58 /* stroke */, XFA_Attribute::Stroke},
-    {0x5404d6df /* moduleHeight */, XFA_Attribute::ModuleHeight},
-    {0x54fa722c /* transferEncoding */, XFA_Attribute::TransferEncoding},
-    {0x552d9ad5 /* usage */, XFA_Attribute::Usage},
-    {0x570ce835 /* presence */, XFA_Attribute::Presence},
-    {0x5739d1ff /* radixOffset */, XFA_Attribute::RadixOffset},
-    {0x577682ac /* preserve */, XFA_Attribute::Preserve},
-    {0x58be2870 /* aliasNode */, XFA_Attribute::AliasNode},
-    {0x5a32e493 /* multiLine */, XFA_Attribute::MultiLine},
-    {0x5a50e9e6 /* version */, XFA_Attribute::Version},
-    {0x5ab23b6c /* startChar */, XFA_Attribute::StartChar},
-    {0x5b707a35 /* scriptTest */, XFA_Attribute::ScriptTest},
-    {0x5c054755 /* startAngle */, XFA_Attribute::StartAngle},
-    {0x5ec958c0 /* cursorType */, XFA_Attribute::CursorType},
-    {0x5f760b50 /* digitalSignature */, XFA_Attribute::DigitalSignature},
-    {0x60a61edd /* codeType */, XFA_Attribute::CodeType},
-    {0x60d4c8b1 /* output */, XFA_Attribute::Output},
-    {0x64110ab5 /* bookendTrailer */, XFA_Attribute::BookendTrailer},
-    {0x65e30c67 /* imagingBBox */, XFA_Attribute::ImagingBBox},
-    {0x66539c48 /* excludeInitialCap */, XFA_Attribute::ExcludeInitialCap},
-    {0x66642f8f /* force */, XFA_Attribute::Force},
-    {0x69aa2292 /* crlSign */, XFA_Attribute::CrlSign},
-    {0x6a3405dd /* previous */, XFA_Attribute::Previous},
-    {0x6a95c976 /* pushCharacterCount */, XFA_Attribute::PushCharacterCount},
-    {0x6b6ddcfb /* nullTest */, XFA_Attribute::NullTest},
-    {0x6cfa828a /* runAt */, XFA_Attribute::RunAt},
-    {0x731e0665 /* spaceBelow */, XFA_Attribute::SpaceBelow},
-    {0x74788f8b /* sweepAngle */, XFA_Attribute::SweepAngle},
-    {0x78bff531 /* numberOfCells */, XFA_Attribute::NumberOfCells},
-    {0x79543055 /* letterSpacing */, XFA_Attribute::LetterSpacing},
-    {0x79975f2b /* lockType */, XFA_Attribute::LockType},
-    {0x7a0cc471 /* passwordChar */, XFA_Attribute::PasswordChar},
-    {0x7a7cc341 /* vAlign */, XFA_Attribute::VAlign},
-    {0x7b29630a /* sourceBelow */, XFA_Attribute::SourceBelow},
-    {0x7b95e661 /* inverted */, XFA_Attribute::Inverted},
-    {0x7c2fd80b /* mark */, XFA_Attribute::Mark},
-    {0x7c2ff6ae /* maxH */, XFA_Attribute::MaxH},
-    {0x7c2ff6bd /* maxW */, XFA_Attribute::MaxW},
-    {0x7c732a66 /* truncate */, XFA_Attribute::Truncate},
-    {0x7d02356c /* minH */, XFA_Attribute::MinH},
-    {0x7d02357b /* minW */, XFA_Attribute::MinW},
-    {0x7d0b5fca /* initial */, XFA_Attribute::Initial},
-    {0x7d9fd7c5 /* mode */, XFA_Attribute::Mode},
-    {0x7e7e845e /* layout */, XFA_Attribute::Layout},
-    {0x7f6fd3d7 /* server */, XFA_Attribute::Server},
-    {0x824f21b7 /* embedPDF */, XFA_Attribute::EmbedPDF},
-    {0x8340ea66 /* oddOrEven */, XFA_Attribute::OddOrEven},
-    {0x836d4d7c /* tabDefault */, XFA_Attribute::TabDefault},
-    {0x8855805f /* contains */, XFA_Attribute::Contains},
-    {0x8a692521 /* rightInset */, XFA_Attribute::RightInset},
-    {0x8af2e657 /* maxChars */, XFA_Attribute::MaxChars},
-    {0x8b90e1f2 /* open */, XFA_Attribute::Open},
-    {0x8c99377e /* relation */, XFA_Attribute::Relation},
-    {0x8d181d61 /* wideNarrowRatio */, XFA_Attribute::WideNarrowRatio},
-    {0x8e1c2921 /* relevant */, XFA_Attribute::Relevant},
-    {0x8e29d794 /* signatureType */, XFA_Attribute::SignatureType},
-    {0x8ec6204c /* lineThroughPeriod */, XFA_Attribute::LineThroughPeriod},
-    {0x8ed182d1 /* shape */, XFA_Attribute::Shape},
-    {0x8fa01790 /* tabStops */, XFA_Attribute::TabStops},
-    {0x8fc36c0a /* outputBelow */, XFA_Attribute::OutputBelow},
-    {0x9041d4b0 /* short */, XFA_Attribute::Short},
-    {0x907c7719 /* fontVerticalScale */, XFA_Attribute::FontVerticalScale},
-    {0x94446dcc /* thickness */, XFA_Attribute::Thickness},
-    {0x957fa006 /* commitOn */, XFA_Attribute::CommitOn},
-    {0x982bd892 /* remainCharacterCount */,
-     XFA_Attribute::RemainCharacterCount},
-    {0x98fd4d81 /* keyAgreement */, XFA_Attribute::KeyAgreement},
-    {0x99800d7a /* errorCorrectionLevel */,
-     XFA_Attribute::ErrorCorrectionLevel},
-    {0x9a63da3d /* upsMode */, XFA_Attribute::UpsMode},
-    {0x9cc17d75 /* mergeMode */, XFA_Attribute::MergeMode},
-    {0x9d833d75 /* circular */, XFA_Attribute::Circular},
-    {0x9d8ee204 /* psName */, XFA_Attribute::PsName},
-    {0x9dcc3ab3 /* trailer */, XFA_Attribute::Trailer},
-    {0xa0933954 /* unicodeRange */, XFA_Attribute::UnicodeRange},
-    {0xa1b0d2f5 /* executeType */, XFA_Attribute::ExecuteType},
-    {0xa25a883d /* duplexImposition */, XFA_Attribute::DuplexImposition},
-    {0xa42ca1b7 /* trayIn */, XFA_Attribute::TrayIn},
-    {0xa433f001 /* bindingNode */, XFA_Attribute::BindingNode},
-    {0xa5340ff5 /* bofAction */, XFA_Attribute::BofAction},
-    {0xa5b410cf /* save */, XFA_Attribute::Save},
-    {0xa6118c89 /* targetType */, XFA_Attribute::TargetType},
-    {0xa66404cb /* keyEncipherment */, XFA_Attribute::KeyEncipherment},
-    {0xa6710262 /* credentialServerPolicy */,
-     XFA_Attribute::CredentialServerPolicy},
-    {0xa686975b /* size */, XFA_Attribute::Size},
-    {0xa85e74f3 /* initialNumber */, XFA_Attribute::InitialNumber},
-    {0xabef37e3 /* slope */, XFA_Attribute::Slope},
-    {0xabfa6c4f /* cSpace */, XFA_Attribute::CSpace},
-    {0xac06e2b0 /* colSpan */, XFA_Attribute::ColSpan},
-    {0xadc4c77b /* binding */, XFA_Attribute::Binding},
-    {0xaf754613 /* checksum */, XFA_Attribute::Checksum},
-    {0xb045fbc5 /* charEncoding */, XFA_Attribute::CharEncoding},
-    {0xb0e5485d /* bind */, XFA_Attribute::Bind},
-    {0xb12128b7 /* textEntry */, XFA_Attribute::TextEntry},
-    {0xb373a862 /* archive */, XFA_Attribute::Archive},
-    {0xb598a1f7 /* uuid */, XFA_Attribute::Uuid},
-    {0xb5e49bf2 /* posture */, XFA_Attribute::Posture},
-    {0xb6b44172 /* after */, XFA_Attribute::After},
-    {0xb716467b /* orphans */, XFA_Attribute::Orphans},
-    {0xbc0c4695 /* qualifiedName */, XFA_Attribute::QualifiedName},
-    {0xbc254332 /* usehref */, XFA_Attribute::Usehref},
-    {0xbc8fa350 /* locale */, XFA_Attribute::Locale},
-    {0xbd6e1d88 /* weight */, XFA_Attribute::Weight},
-    {0xbd96a0e9 /* underlinePeriod */, XFA_Attribute::UnderlinePeriod},
-    {0xbde9abda /* data */, XFA_Attribute::Data},
-    {0xbe52dfbf /* desc */, XFA_Attribute::Desc},
-    {0xbe9ba472 /* numbered */, XFA_Attribute::Numbered},
-    {0xc035c6b1 /* dataColumnCount */, XFA_Attribute::DataColumnCount},
-    {0xc0ec9fa4 /* overline */, XFA_Attribute::Overline},
-    {0xc2ba0923 /* urlPolicy */, XFA_Attribute::UrlPolicy},
-    {0xc2bd40fd /* anchorType */, XFA_Attribute::AnchorType},
-    {0xc39a88bd /* labelRef */, XFA_Attribute::LabelRef},
-    {0xc3c1442f /* bookendLeader */, XFA_Attribute::BookendLeader},
-    {0xc4547a08 /* maxLength */, XFA_Attribute::MaxLength},
-    {0xc4fed09b /* accessKey */, XFA_Attribute::AccessKey},
-    {0xc5762157 /* cursorLocation */, XFA_Attribute::CursorLocation},
-    {0xc860f30a /* delayedOpen */, XFA_Attribute::DelayedOpen},
-    {0xc8da4da7 /* target */, XFA_Attribute::Target},
-    {0xca5dc27c /* dataEncipherment */, XFA_Attribute::DataEncipherment},
-    {0xcb150479 /* afterTarget */, XFA_Attribute::AfterTarget},
-    {0xcbcaf66d /* leader */, XFA_Attribute::Leader},
-    {0xcca7897e /* picker */, XFA_Attribute::Picker},
-    {0xcd7f7b54 /* from */, XFA_Attribute::From},
-    {0xcea5e62c /* baseProfile */, XFA_Attribute::BaseProfile},
-    {0xd171b240 /* aspect */, XFA_Attribute::Aspect},
-    {0xd3c84d25 /* rowColumnRatio */, XFA_Attribute::RowColumnRatio},
-    {0xd4b01921 /* lineHeight */, XFA_Attribute::LineHeight},
-    {0xd4cc53f8 /* highlight */, XFA_Attribute::Highlight},
-    {0xd50f903a /* valueRef */, XFA_Attribute::ValueRef},
-    {0xd52482e0 /* maxEntries */, XFA_Attribute::MaxEntries},
-    {0xd57c513c /* dataLength */, XFA_Attribute::DataLength},
-    {0xd6128d8d /* activity */, XFA_Attribute::Activity},
-    {0xd6a39990 /* input */, XFA_Attribute::Input},
-    {0xd6e27f1d /* value */, XFA_Attribute::Value},
-    {0xd70798c2 /* blankOrNotBlank */, XFA_Attribute::BlankOrNotBlank},
-    {0xd861f8af /* addRevocationInfo */, XFA_Attribute::AddRevocationInfo},
-    {0xd8f982bf /* genericFamily */, XFA_Attribute::GenericFamily},
-    {0xd996fa9b /* hand */, XFA_Attribute::Hand},
-    {0xdb55fec5 /* href */, XFA_Attribute::Href},
-    {0xdc75676c /* textEncoding */, XFA_Attribute::TextEncoding},
-    {0xde7f92ba /* leadDigits */, XFA_Attribute::LeadDigits},
-    {0xe11a2cbc /* permissions */, XFA_Attribute::Permissions},
-    {0xe18b5659 /* spaceAbove */, XFA_Attribute::SpaceAbove},
-    {0xe1a26b56 /* codeBase */, XFA_Attribute::CodeBase},
-    {0xe349d044 /* stock */, XFA_Attribute::Stock},
-    {0xe372ae97 /* isNull */, XFA_Attribute::IsNull},
-    {0xe4c3a5e5 /* restoreState */, XFA_Attribute::RestoreState},
-    {0xe5c96d6a /* excludeAllCaps */, XFA_Attribute::ExcludeAllCaps},
-    {0xe64b1129 /* formatTest */, XFA_Attribute::FormatTest},
-    {0xe6f99487 /* hScrollPolicy */, XFA_Attribute::HScrollPolicy},
-    {0xe8dddf50 /* join */, XFA_Attribute::Join},
-    {0xe8f118a8 /* keyCertSign */, XFA_Attribute::KeyCertSign},
-    {0xe948b9a8 /* radius */, XFA_Attribute::Radius},
-    {0xe996b2fe /* sourceAbove */, XFA_Attribute::SourceAbove},
-    {0xea7090a0 /* override */, XFA_Attribute::Override},
-    {0xeb091003 /* classId */, XFA_Attribute::ClassId},
-    {0xeb511b54 /* disable */, XFA_Attribute::Disable},
-    {0xeda9017a /* scope */, XFA_Attribute::Scope},
-    {0xf197844d /* match */, XFA_Attribute::Match},
-    {0xf2009339 /* placement */, XFA_Attribute::Placement},
-    {0xf4ffce73 /* before */, XFA_Attribute::Before},
-    {0xf531b059 /* writingScript */, XFA_Attribute::WritingScript},
-    {0xf575ca75 /* endChar */, XFA_Attribute::EndChar},
-    {0xf6b47749 /* lock */, XFA_Attribute::Lock},
-    {0xf6b4afb0 /* long */, XFA_Attribute::Long},
-    {0xf6b59543 /* intact */, XFA_Attribute::Intact},
-    {0xf889e747 /* xdpContent */, XFA_Attribute::XdpContent},
-    {0xfea53ec6 /* decipherOnly */, XFA_Attribute::DecipherOnly},
-};
-
-}  // namespace
-
-// static
-XFA_Element CXFA_Node::NameToElement(const WideString& name) {
-  uint32_t hash = FX_HashCode_GetW(name.AsStringView(), false);
-  auto* elem = std::lower_bound(
-      std::begin(ElementNameToEnum), std::end(ElementNameToEnum), hash,
-      [](const ElementNameInfo& a, uint32_t hash) { return a.hash < hash; });
-  if (elem != std::end(ElementNameToEnum) && elem->hash == hash)
-    return elem->element;
-  return XFA_Element::Unknown;
-}
-
-// static
-XFA_Attribute CXFA_Node::NameToAttribute(const WideStringView& name) {
-  uint32_t hash = FX_HashCode_GetW(name, false);
-  auto* elem = std::lower_bound(
-      std::begin(AttributeNameInfoToEnum), std::end(AttributeNameInfoToEnum),
-      hash,
-      [](const AttributeNameInfo& a, uint32_t hash) { return a.hash < hash; });
-  if (elem != std::end(AttributeNameInfoToEnum) && elem->hash == hash)
-    return elem->attribute;
-  return XFA_Attribute::Unknown;
-}
-
-// static
-std::unique_ptr<CXFA_Node> CXFA_Node::Create(CXFA_Document* doc,
-                                             XFA_Element element,
-                                             XFA_PacketType packet) {
-  std::unique_ptr<CXFA_Node> node;
-  switch (element) {
-    case XFA_Element::Ps:
-      node = pdfium::MakeUnique<CXFA_Ps>(doc, packet);
-      break;
-    case XFA_Element::To:
-      node = pdfium::MakeUnique<CXFA_To>(doc, packet);
-      break;
-    case XFA_Element::Ui:
-      node = pdfium::MakeUnique<CXFA_Ui>(doc, packet);
-      break;
-    case XFA_Element::RecordSet:
-      node = pdfium::MakeUnique<CXFA_RecordSet>(doc, packet);
-      break;
-    case XFA_Element::SubsetBelow:
-      node = pdfium::MakeUnique<CXFA_SubsetBelow>(doc, packet);
-      break;
-    case XFA_Element::SubformSet:
-      node = pdfium::MakeUnique<CXFA_SubformSet>(doc, packet);
-      break;
-    case XFA_Element::AdobeExtensionLevel:
-      node = pdfium::MakeUnique<CXFA_AdobeExtensionLevel>(doc, packet);
-      break;
-    case XFA_Element::Typeface:
-      node = pdfium::MakeUnique<CXFA_Typeface>(doc, packet);
-      break;
-    case XFA_Element::Break:
-      node = pdfium::MakeUnique<CXFA_Break>(doc, packet);
-      break;
-    case XFA_Element::FontInfo:
-      node = pdfium::MakeUnique<CXFA_FontInfo>(doc, packet);
-      break;
-    case XFA_Element::NumberPattern:
-      node = pdfium::MakeUnique<CXFA_NumberPattern>(doc, packet);
-      break;
-    case XFA_Element::DynamicRender:
-      node = pdfium::MakeUnique<CXFA_DynamicRender>(doc, packet);
-      break;
-    case XFA_Element::PrintScaling:
-      node = pdfium::MakeUnique<CXFA_PrintScaling>(doc, packet);
-      break;
-    case XFA_Element::CheckButton:
-      node = pdfium::MakeUnique<CXFA_CheckButton>(doc, packet);
-      break;
-    case XFA_Element::DatePatterns:
-      node = pdfium::MakeUnique<CXFA_DatePatterns>(doc, packet);
-      break;
-    case XFA_Element::SourceSet:
-      node = pdfium::MakeUnique<CXFA_SourceSet>(doc, packet);
-      break;
-    case XFA_Element::Amd:
-      node = pdfium::MakeUnique<CXFA_Amd>(doc, packet);
-      break;
-    case XFA_Element::Arc:
-      node = pdfium::MakeUnique<CXFA_Arc>(doc, packet);
-      break;
-    case XFA_Element::Day:
-      node = pdfium::MakeUnique<CXFA_Day>(doc, packet);
-      break;
-    case XFA_Element::Era:
-      node = pdfium::MakeUnique<CXFA_Era>(doc, packet);
-      break;
-    case XFA_Element::Jog:
-      node = pdfium::MakeUnique<CXFA_Jog>(doc, packet);
-      break;
-    case XFA_Element::Log:
-      node = pdfium::MakeUnique<CXFA_Log>(doc, packet);
-      break;
-    case XFA_Element::Map:
-      node = pdfium::MakeUnique<CXFA_Map>(doc, packet);
-      break;
-    case XFA_Element::Mdp:
-      node = pdfium::MakeUnique<CXFA_Mdp>(doc, packet);
-      break;
-    case XFA_Element::BreakBefore:
-      node = pdfium::MakeUnique<CXFA_BreakBefore>(doc, packet);
-      break;
-    case XFA_Element::Oid:
-      node = pdfium::MakeUnique<CXFA_Oid>(doc, packet);
-      break;
-    case XFA_Element::Pcl:
-      node = pdfium::MakeUnique<CXFA_Pcl>(doc, packet);
-      break;
-    case XFA_Element::Pdf:
-      node = pdfium::MakeUnique<CXFA_Pdf>(doc, packet);
-      break;
-    case XFA_Element::Ref:
-      node = pdfium::MakeUnique<CXFA_Ref>(doc, packet);
-      break;
-    case XFA_Element::Uri:
-      node = pdfium::MakeUnique<CXFA_Uri>(doc, packet);
-      break;
-    case XFA_Element::Xdc:
-      node = pdfium::MakeUnique<CXFA_Xdc>(doc, packet);
-      break;
-    case XFA_Element::Xdp:
-      node = pdfium::MakeUnique<CXFA_Xdp>(doc, packet);
-      break;
-    case XFA_Element::Xfa:
-      node = pdfium::MakeUnique<CXFA_Xfa>(doc, packet);
-      break;
-    case XFA_Element::Xsl:
-      node = pdfium::MakeUnique<CXFA_Xsl>(doc, packet);
-      break;
-    case XFA_Element::Zpl:
-      node = pdfium::MakeUnique<CXFA_Zpl>(doc, packet);
-      break;
-    case XFA_Element::Cache:
-      node = pdfium::MakeUnique<CXFA_Cache>(doc, packet);
-      break;
-    case XFA_Element::Margin:
-      node = pdfium::MakeUnique<CXFA_Margin>(doc, packet);
-      break;
-    case XFA_Element::KeyUsage:
-      node = pdfium::MakeUnique<CXFA_KeyUsage>(doc, packet);
-      break;
-    case XFA_Element::Exclude:
-      node = pdfium::MakeUnique<CXFA_Exclude>(doc, packet);
-      break;
-    case XFA_Element::ChoiceList:
-      node = pdfium::MakeUnique<CXFA_ChoiceList>(doc, packet);
-      break;
-    case XFA_Element::Level:
-      node = pdfium::MakeUnique<CXFA_Level>(doc, packet);
-      break;
-    case XFA_Element::LabelPrinter:
-      node = pdfium::MakeUnique<CXFA_LabelPrinter>(doc, packet);
-      break;
-    case XFA_Element::CalendarSymbols:
-      node = pdfium::MakeUnique<CXFA_CalendarSymbols>(doc, packet);
-      break;
-    case XFA_Element::Para:
-      node = pdfium::MakeUnique<CXFA_Para>(doc, packet);
-      break;
-    case XFA_Element::Part:
-      node = pdfium::MakeUnique<CXFA_Part>(doc, packet);
-      break;
-    case XFA_Element::Pdfa:
-      node = pdfium::MakeUnique<CXFA_Pdfa>(doc, packet);
-      break;
-    case XFA_Element::Filter:
-      node = pdfium::MakeUnique<CXFA_Filter>(doc, packet);
-      break;
-    case XFA_Element::Present:
-      node = pdfium::MakeUnique<CXFA_Present>(doc, packet);
-      break;
-    case XFA_Element::Pagination:
-      node = pdfium::MakeUnique<CXFA_Pagination>(doc, packet);
-      break;
-    case XFA_Element::Encoding:
-      node = pdfium::MakeUnique<CXFA_Encoding>(doc, packet);
-      break;
-    case XFA_Element::Event:
-      node = pdfium::MakeUnique<CXFA_Event>(doc, packet);
-      break;
-    case XFA_Element::Whitespace:
-      node = pdfium::MakeUnique<CXFA_Whitespace>(doc, packet);
-      break;
-    case XFA_Element::DefaultUi:
-      node = pdfium::MakeUnique<CXFA_DefaultUi>(doc, packet);
-      break;
-    case XFA_Element::DataModel:
-      node = pdfium::MakeUnique<CXFA_DataModel>(doc, packet);
-      break;
-    case XFA_Element::Barcode:
-      node = pdfium::MakeUnique<CXFA_Barcode>(doc, packet);
-      break;
-    case XFA_Element::TimePattern:
-      node = pdfium::MakeUnique<CXFA_TimePattern>(doc, packet);
-      break;
-    case XFA_Element::BatchOutput:
-      node = pdfium::MakeUnique<CXFA_BatchOutput>(doc, packet);
-      break;
-    case XFA_Element::Enforce:
-      node = pdfium::MakeUnique<CXFA_Enforce>(doc, packet);
-      break;
-    case XFA_Element::CurrencySymbols:
-      node = pdfium::MakeUnique<CXFA_CurrencySymbols>(doc, packet);
-      break;
-    case XFA_Element::AddSilentPrint:
-      node = pdfium::MakeUnique<CXFA_AddSilentPrint>(doc, packet);
-      break;
-    case XFA_Element::Rename:
-      node = pdfium::MakeUnique<CXFA_Rename>(doc, packet);
-      break;
-    case XFA_Element::Operation:
-      node = pdfium::MakeUnique<CXFA_Operation>(doc, packet);
-      break;
-    case XFA_Element::Typefaces:
-      node = pdfium::MakeUnique<CXFA_Typefaces>(doc, packet);
-      break;
-    case XFA_Element::SubjectDNs:
-      node = pdfium::MakeUnique<CXFA_SubjectDNs>(doc, packet);
-      break;
-    case XFA_Element::Issuers:
-      node = pdfium::MakeUnique<CXFA_Issuers>(doc, packet);
-      break;
-    case XFA_Element::WsdlConnection:
-      node = pdfium::MakeUnique<CXFA_WsdlConnection>(doc, packet);
-      break;
-    case XFA_Element::Debug:
-      node = pdfium::MakeUnique<CXFA_Debug>(doc, packet);
-      break;
-    case XFA_Element::Delta:
-      node = pdfium::MakeUnique<CXFA_Delta>(doc, packet);
-      break;
-    case XFA_Element::EraNames:
-      node = pdfium::MakeUnique<CXFA_EraNames>(doc, packet);
-      break;
-    case XFA_Element::ModifyAnnots:
-      node = pdfium::MakeUnique<CXFA_ModifyAnnots>(doc, packet);
-      break;
-    case XFA_Element::StartNode:
-      node = pdfium::MakeUnique<CXFA_StartNode>(doc, packet);
-      break;
-    case XFA_Element::Button:
-      node = pdfium::MakeUnique<CXFA_Button>(doc, packet);
-      break;
-    case XFA_Element::Format:
-      node = pdfium::MakeUnique<CXFA_Format>(doc, packet);
-      break;
-    case XFA_Element::Border:
-      node = pdfium::MakeUnique<CXFA_Border>(doc, packet);
-      break;
-    case XFA_Element::Area:
-      node = pdfium::MakeUnique<CXFA_Area>(doc, packet);
-      break;
-    case XFA_Element::Hyphenation:
-      node = pdfium::MakeUnique<CXFA_Hyphenation>(doc, packet);
-      break;
-    case XFA_Element::Text:
-      node = pdfium::MakeUnique<CXFA_Text>(doc, packet);
-      break;
-    case XFA_Element::Time:
-      node = pdfium::MakeUnique<CXFA_Time>(doc, packet);
-      break;
-    case XFA_Element::Type:
-      node = pdfium::MakeUnique<CXFA_Type>(doc, packet);
-      break;
-    case XFA_Element::Overprint:
-      node = pdfium::MakeUnique<CXFA_Overprint>(doc, packet);
-      break;
-    case XFA_Element::Certificates:
-      node = pdfium::MakeUnique<CXFA_Certificates>(doc, packet);
-      break;
-    case XFA_Element::EncryptionMethods:
-      node = pdfium::MakeUnique<CXFA_EncryptionMethods>(doc, packet);
-      break;
-    case XFA_Element::SetProperty:
-      node = pdfium::MakeUnique<CXFA_SetProperty>(doc, packet);
-      break;
-    case XFA_Element::PrinterName:
-      node = pdfium::MakeUnique<CXFA_PrinterName>(doc, packet);
-      break;
-    case XFA_Element::StartPage:
-      node = pdfium::MakeUnique<CXFA_StartPage>(doc, packet);
-      break;
-    case XFA_Element::PageOffset:
-      node = pdfium::MakeUnique<CXFA_PageOffset>(doc, packet);
-      break;
-    case XFA_Element::DateTime:
-      node = pdfium::MakeUnique<CXFA_DateTime>(doc, packet);
-      break;
-    case XFA_Element::Comb:
-      node = pdfium::MakeUnique<CXFA_Comb>(doc, packet);
-      break;
-    case XFA_Element::Pattern:
-      node = pdfium::MakeUnique<CXFA_Pattern>(doc, packet);
-      break;
-    case XFA_Element::IfEmpty:
-      node = pdfium::MakeUnique<CXFA_IfEmpty>(doc, packet);
-      break;
-    case XFA_Element::SuppressBanner:
-      node = pdfium::MakeUnique<CXFA_SuppressBanner>(doc, packet);
-      break;
-    case XFA_Element::OutputBin:
-      node = pdfium::MakeUnique<CXFA_OutputBin>(doc, packet);
-      break;
-    case XFA_Element::Field:
-      node = pdfium::MakeUnique<CXFA_Field>(doc, packet);
-      break;
-    case XFA_Element::Agent:
-      node = pdfium::MakeUnique<CXFA_Agent>(doc, packet);
-      break;
-    case XFA_Element::OutputXSL:
-      node = pdfium::MakeUnique<CXFA_OutputXSL>(doc, packet);
-      break;
-    case XFA_Element::AdjustData:
-      node = pdfium::MakeUnique<CXFA_AdjustData>(doc, packet);
-      break;
-    case XFA_Element::AutoSave:
-      node = pdfium::MakeUnique<CXFA_AutoSave>(doc, packet);
-      break;
-    case XFA_Element::ContentArea:
-      node = pdfium::MakeUnique<CXFA_ContentArea>(doc, packet);
-      break;
-    case XFA_Element::WsdlAddress:
-      node = pdfium::MakeUnique<CXFA_WsdlAddress>(doc, packet);
-      break;
-    case XFA_Element::Solid:
-      node = pdfium::MakeUnique<CXFA_Solid>(doc, packet);
-      break;
-    case XFA_Element::DateTimeSymbols:
-      node = pdfium::MakeUnique<CXFA_DateTimeSymbols>(doc, packet);
-      break;
-    case XFA_Element::EncryptionLevel:
-      node = pdfium::MakeUnique<CXFA_EncryptionLevel>(doc, packet);
-      break;
-    case XFA_Element::Edge:
-      node = pdfium::MakeUnique<CXFA_Edge>(doc, packet);
-      break;
-    case XFA_Element::Stipple:
-      node = pdfium::MakeUnique<CXFA_Stipple>(doc, packet);
-      break;
-    case XFA_Element::Attributes:
-      node = pdfium::MakeUnique<CXFA_Attributes>(doc, packet);
-      break;
-    case XFA_Element::VersionControl:
-      node = pdfium::MakeUnique<CXFA_VersionControl>(doc, packet);
-      break;
-    case XFA_Element::Meridiem:
-      node = pdfium::MakeUnique<CXFA_Meridiem>(doc, packet);
-      break;
-    case XFA_Element::ExclGroup:
-      node = pdfium::MakeUnique<CXFA_ExclGroup>(doc, packet);
-      break;
-    case XFA_Element::ToolTip:
-      node = pdfium::MakeUnique<CXFA_ToolTip>(doc, packet);
-      break;
-    case XFA_Element::Compress:
-      node = pdfium::MakeUnique<CXFA_Compress>(doc, packet);
-      break;
-    case XFA_Element::Reason:
-      node = pdfium::MakeUnique<CXFA_Reason>(doc, packet);
-      break;
-    case XFA_Element::Execute:
-      node = pdfium::MakeUnique<CXFA_Execute>(doc, packet);
-      break;
-    case XFA_Element::ContentCopy:
-      node = pdfium::MakeUnique<CXFA_ContentCopy>(doc, packet);
-      break;
-    case XFA_Element::DateTimeEdit:
-      node = pdfium::MakeUnique<CXFA_DateTimeEdit>(doc, packet);
-      break;
-    case XFA_Element::Config:
-      node = pdfium::MakeUnique<CXFA_Config>(doc, packet);
-      break;
-    case XFA_Element::Image:
-      node = pdfium::MakeUnique<CXFA_Image>(doc, packet);
-      break;
-    case XFA_Element::SharpxHTML:
-      node = pdfium::MakeUnique<CXFA_SharpxHTML>(doc, packet);
-      break;
-    case XFA_Element::NumberOfCopies:
-      node = pdfium::MakeUnique<CXFA_NumberOfCopies>(doc, packet);
-      break;
-    case XFA_Element::BehaviorOverride:
-      node = pdfium::MakeUnique<CXFA_BehaviorOverride>(doc, packet);
-      break;
-    case XFA_Element::TimeStamp:
-      node = pdfium::MakeUnique<CXFA_TimeStamp>(doc, packet);
-      break;
-    case XFA_Element::Month:
-      node = pdfium::MakeUnique<CXFA_Month>(doc, packet);
-      break;
-    case XFA_Element::ViewerPreferences:
-      node = pdfium::MakeUnique<CXFA_ViewerPreferences>(doc, packet);
-      break;
-    case XFA_Element::ScriptModel:
-      node = pdfium::MakeUnique<CXFA_ScriptModel>(doc, packet);
-      break;
-    case XFA_Element::Decimal:
-      node = pdfium::MakeUnique<CXFA_Decimal>(doc, packet);
-      break;
-    case XFA_Element::Subform:
-      node = pdfium::MakeUnique<CXFA_Subform>(doc, packet);
-      break;
-    case XFA_Element::Select:
-      node = pdfium::MakeUnique<CXFA_Select>(doc, packet);
-      break;
-    case XFA_Element::Window:
-      node = pdfium::MakeUnique<CXFA_Window>(doc, packet);
-      break;
-    case XFA_Element::LocaleSet:
-      node = pdfium::MakeUnique<CXFA_LocaleSet>(doc, packet);
-      break;
-    case XFA_Element::Handler:
-      node = pdfium::MakeUnique<CXFA_Handler>(doc, packet);
-      break;
-    case XFA_Element::Presence:
-      node = pdfium::MakeUnique<CXFA_Presence>(doc, packet);
-      break;
-    case XFA_Element::Record:
-      node = pdfium::MakeUnique<CXFA_Record>(doc, packet);
-      break;
-    case XFA_Element::Embed:
-      node = pdfium::MakeUnique<CXFA_Embed>(doc, packet);
-      break;
-    case XFA_Element::Version:
-      node = pdfium::MakeUnique<CXFA_Version>(doc, packet);
-      break;
-    case XFA_Element::Command:
-      node = pdfium::MakeUnique<CXFA_Command>(doc, packet);
-      break;
-    case XFA_Element::Copies:
-      node = pdfium::MakeUnique<CXFA_Copies>(doc, packet);
-      break;
-    case XFA_Element::Staple:
-      node = pdfium::MakeUnique<CXFA_Staple>(doc, packet);
-      break;
-    case XFA_Element::SubmitFormat:
-      node = pdfium::MakeUnique<CXFA_SubmitFormat>(doc, packet);
-      break;
-    case XFA_Element::Boolean:
-      node = pdfium::MakeUnique<CXFA_Boolean>(doc, packet);
-      break;
-    case XFA_Element::Message:
-      node = pdfium::MakeUnique<CXFA_Message>(doc, packet);
-      break;
-    case XFA_Element::Output:
-      node = pdfium::MakeUnique<CXFA_Output>(doc, packet);
-      break;
-    case XFA_Element::PsMap:
-      node = pdfium::MakeUnique<CXFA_PsMap>(doc, packet);
-      break;
-    case XFA_Element::ExcludeNS:
-      node = pdfium::MakeUnique<CXFA_ExcludeNS>(doc, packet);
-      break;
-    case XFA_Element::Assist:
-      node = pdfium::MakeUnique<CXFA_Assist>(doc, packet);
-      break;
-    case XFA_Element::Picture:
-      node = pdfium::MakeUnique<CXFA_Picture>(doc, packet);
-      break;
-    case XFA_Element::Traversal:
-      node = pdfium::MakeUnique<CXFA_Traversal>(doc, packet);
-      break;
-    case XFA_Element::SilentPrint:
-      node = pdfium::MakeUnique<CXFA_SilentPrint>(doc, packet);
-      break;
-    case XFA_Element::WebClient:
-      node = pdfium::MakeUnique<CXFA_WebClient>(doc, packet);
-      break;
-    case XFA_Element::Producer:
-      node = pdfium::MakeUnique<CXFA_Producer>(doc, packet);
-      break;
-    case XFA_Element::Corner:
-      node = pdfium::MakeUnique<CXFA_Corner>(doc, packet);
-      break;
-    case XFA_Element::MsgId:
-      node = pdfium::MakeUnique<CXFA_MsgId>(doc, packet);
-      break;
-    case XFA_Element::Color:
-      node = pdfium::MakeUnique<CXFA_Color>(doc, packet);
-      break;
-    case XFA_Element::Keep:
-      node = pdfium::MakeUnique<CXFA_Keep>(doc, packet);
-      break;
-    case XFA_Element::Query:
-      node = pdfium::MakeUnique<CXFA_Query>(doc, packet);
-      break;
-    case XFA_Element::Insert:
-      node = pdfium::MakeUnique<CXFA_Insert>(doc, packet);
-      break;
-    case XFA_Element::ImageEdit:
-      node = pdfium::MakeUnique<CXFA_ImageEdit>(doc, packet);
-      break;
-    case XFA_Element::Validate:
-      node = pdfium::MakeUnique<CXFA_Validate>(doc, packet);
-      break;
-    case XFA_Element::DigestMethods:
-      node = pdfium::MakeUnique<CXFA_DigestMethods>(doc, packet);
-      break;
-    case XFA_Element::NumberPatterns:
-      node = pdfium::MakeUnique<CXFA_NumberPatterns>(doc, packet);
-      break;
-    case XFA_Element::PageSet:
-      node = pdfium::MakeUnique<CXFA_PageSet>(doc, packet);
-      break;
-    case XFA_Element::Integer:
-      node = pdfium::MakeUnique<CXFA_Integer>(doc, packet);
-      break;
-    case XFA_Element::SoapAddress:
-      node = pdfium::MakeUnique<CXFA_SoapAddress>(doc, packet);
-      break;
-    case XFA_Element::Equate:
-      node = pdfium::MakeUnique<CXFA_Equate>(doc, packet);
-      break;
-    case XFA_Element::FormFieldFilling:
-      node = pdfium::MakeUnique<CXFA_FormFieldFilling>(doc, packet);
-      break;
-    case XFA_Element::PageRange:
-      node = pdfium::MakeUnique<CXFA_PageRange>(doc, packet);
-      break;
-    case XFA_Element::Update:
-      node = pdfium::MakeUnique<CXFA_Update>(doc, packet);
-      break;
-    case XFA_Element::ConnectString:
-      node = pdfium::MakeUnique<CXFA_ConnectString>(doc, packet);
-      break;
-    case XFA_Element::Mode:
-      node = pdfium::MakeUnique<CXFA_Mode>(doc, packet);
-      break;
-    case XFA_Element::Layout:
-      node = pdfium::MakeUnique<CXFA_Layout>(doc, packet);
-      break;
-    case XFA_Element::Sharpxml:
-      node = pdfium::MakeUnique<CXFA_Sharpxml>(doc, packet);
-      break;
-    case XFA_Element::XsdConnection:
-      node = pdfium::MakeUnique<CXFA_XsdConnection>(doc, packet);
-      break;
-    case XFA_Element::Traverse:
-      node = pdfium::MakeUnique<CXFA_Traverse>(doc, packet);
-      break;
-    case XFA_Element::Encodings:
-      node = pdfium::MakeUnique<CXFA_Encodings>(doc, packet);
-      break;
-    case XFA_Element::Template:
-      node = pdfium::MakeUnique<CXFA_Template>(doc, packet);
-      break;
-    case XFA_Element::Acrobat:
-      node = pdfium::MakeUnique<CXFA_Acrobat>(doc, packet);
-      break;
-    case XFA_Element::ValidationMessaging:
-      node = pdfium::MakeUnique<CXFA_ValidationMessaging>(doc, packet);
-      break;
-    case XFA_Element::Signing:
-      node = pdfium::MakeUnique<CXFA_Signing>(doc, packet);
-      break;
-    case XFA_Element::Script:
-      node = pdfium::MakeUnique<CXFA_Script>(doc, packet);
-      break;
-    case XFA_Element::AddViewerPreferences:
-      node = pdfium::MakeUnique<CXFA_AddViewerPreferences>(doc, packet);
-      break;
-    case XFA_Element::AlwaysEmbed:
-      node = pdfium::MakeUnique<CXFA_AlwaysEmbed>(doc, packet);
-      break;
-    case XFA_Element::PasswordEdit:
-      node = pdfium::MakeUnique<CXFA_PasswordEdit>(doc, packet);
-      break;
-    case XFA_Element::NumericEdit:
-      node = pdfium::MakeUnique<CXFA_NumericEdit>(doc, packet);
-      break;
-    case XFA_Element::EncryptionMethod:
-      node = pdfium::MakeUnique<CXFA_EncryptionMethod>(doc, packet);
-      break;
-    case XFA_Element::Change:
-      node = pdfium::MakeUnique<CXFA_Change>(doc, packet);
-      break;
-    case XFA_Element::PageArea:
-      node = pdfium::MakeUnique<CXFA_PageArea>(doc, packet);
-      break;
-    case XFA_Element::SubmitUrl:
-      node = pdfium::MakeUnique<CXFA_SubmitUrl>(doc, packet);
-      break;
-    case XFA_Element::Oids:
-      node = pdfium::MakeUnique<CXFA_Oids>(doc, packet);
-      break;
-    case XFA_Element::Signature:
-      node = pdfium::MakeUnique<CXFA_Signature>(doc, packet);
-      break;
-    case XFA_Element::ADBE_JSConsole:
-      node = pdfium::MakeUnique<CXFA_aDBE_JSConsole>(doc, packet);
-      break;
-    case XFA_Element::Caption:
-      node = pdfium::MakeUnique<CXFA_Caption>(doc, packet);
-      break;
-    case XFA_Element::Relevant:
-      node = pdfium::MakeUnique<CXFA_Relevant>(doc, packet);
-      break;
-    case XFA_Element::FlipLabel:
-      node = pdfium::MakeUnique<CXFA_FlipLabel>(doc, packet);
-      break;
-    case XFA_Element::ExData:
-      node = pdfium::MakeUnique<CXFA_ExData>(doc, packet);
-      break;
-    case XFA_Element::DayNames:
-      node = pdfium::MakeUnique<CXFA_DayNames>(doc, packet);
-      break;
-    case XFA_Element::SoapAction:
-      node = pdfium::MakeUnique<CXFA_SoapAction>(doc, packet);
-      break;
-    case XFA_Element::DefaultTypeface:
-      node = pdfium::MakeUnique<CXFA_DefaultTypeface>(doc, packet);
-      break;
-    case XFA_Element::Manifest:
-      node = pdfium::MakeUnique<CXFA_Manifest>(doc, packet);
-      break;
-    case XFA_Element::Overflow:
-      node = pdfium::MakeUnique<CXFA_Overflow>(doc, packet);
-      break;
-    case XFA_Element::Linear:
-      node = pdfium::MakeUnique<CXFA_Linear>(doc, packet);
-      break;
-    case XFA_Element::CurrencySymbol:
-      node = pdfium::MakeUnique<CXFA_CurrencySymbol>(doc, packet);
-      break;
-    case XFA_Element::Delete:
-      node = pdfium::MakeUnique<CXFA_Delete>(doc, packet);
-      break;
-    case XFA_Element::DigestMethod:
-      node = pdfium::MakeUnique<CXFA_DigestMethod>(doc, packet);
-      break;
-    case XFA_Element::InstanceManager:
-      node = pdfium::MakeUnique<CXFA_InstanceManager>(doc, packet);
-      break;
-    case XFA_Element::EquateRange:
-      node = pdfium::MakeUnique<CXFA_EquateRange>(doc, packet);
-      break;
-    case XFA_Element::Medium:
-      node = pdfium::MakeUnique<CXFA_Medium>(doc, packet);
-      break;
-    case XFA_Element::TextEdit:
-      node = pdfium::MakeUnique<CXFA_TextEdit>(doc, packet);
-      break;
-    case XFA_Element::TemplateCache:
-      node = pdfium::MakeUnique<CXFA_TemplateCache>(doc, packet);
-      break;
-    case XFA_Element::CompressObjectStream:
-      node = pdfium::MakeUnique<CXFA_CompressObjectStream>(doc, packet);
-      break;
-    case XFA_Element::DataValue:
-      node = pdfium::MakeUnique<CXFA_DataValue>(doc, packet);
-      break;
-    case XFA_Element::AccessibleContent:
-      node = pdfium::MakeUnique<CXFA_AccessibleContent>(doc, packet);
-      break;
-    case XFA_Element::IncludeXDPContent:
-      node = pdfium::MakeUnique<CXFA_IncludeXDPContent>(doc, packet);
-      break;
-    case XFA_Element::XmlConnection:
-      node = pdfium::MakeUnique<CXFA_XmlConnection>(doc, packet);
-      break;
-    case XFA_Element::ValidateApprovalSignatures:
-      node = pdfium::MakeUnique<CXFA_ValidateApprovalSignatures>(doc, packet);
-      break;
-    case XFA_Element::SignData:
-      node = pdfium::MakeUnique<CXFA_SignData>(doc, packet);
-      break;
-    case XFA_Element::Packets:
-      node = pdfium::MakeUnique<CXFA_Packets>(doc, packet);
-      break;
-    case XFA_Element::DatePattern:
-      node = pdfium::MakeUnique<CXFA_DatePattern>(doc, packet);
-      break;
-    case XFA_Element::DuplexOption:
-      node = pdfium::MakeUnique<CXFA_DuplexOption>(doc, packet);
-      break;
-    case XFA_Element::Base:
-      node = pdfium::MakeUnique<CXFA_Base>(doc, packet);
-      break;
-    case XFA_Element::Bind:
-      node = pdfium::MakeUnique<CXFA_Bind>(doc, packet);
-      break;
-    case XFA_Element::Compression:
-      node = pdfium::MakeUnique<CXFA_Compression>(doc, packet);
-      break;
-    case XFA_Element::User:
-      node = pdfium::MakeUnique<CXFA_User>(doc, packet);
-      break;
-    case XFA_Element::Rectangle:
-      node = pdfium::MakeUnique<CXFA_Rectangle>(doc, packet);
-      break;
-    case XFA_Element::EffectiveOutputPolicy:
-      node = pdfium::MakeUnique<CXFA_EffectiveOutputPolicy>(doc, packet);
-      break;
-    case XFA_Element::ADBE_JSDebugger:
-      node = pdfium::MakeUnique<CXFA_aDBE_JSDebugger>(doc, packet);
-      break;
-    case XFA_Element::Acrobat7:
-      node = pdfium::MakeUnique<CXFA_Acrobat7>(doc, packet);
-      break;
-    case XFA_Element::Interactive:
-      node = pdfium::MakeUnique<CXFA_Interactive>(doc, packet);
-      break;
-    case XFA_Element::Locale:
-      node = pdfium::MakeUnique<CXFA_Locale>(doc, packet);
-      break;
-    case XFA_Element::CurrentPage:
-      node = pdfium::MakeUnique<CXFA_CurrentPage>(doc, packet);
-      break;
-    case XFA_Element::Data:
-      node = pdfium::MakeUnique<CXFA_Data>(doc, packet);
-      break;
-    case XFA_Element::Date:
-      node = pdfium::MakeUnique<CXFA_Date>(doc, packet);
-      break;
-    case XFA_Element::Desc:
-      node = pdfium::MakeUnique<CXFA_Desc>(doc, packet);
-      break;
-    case XFA_Element::Encrypt:
-      node = pdfium::MakeUnique<CXFA_Encrypt>(doc, packet);
-      break;
-    case XFA_Element::Draw:
-      node = pdfium::MakeUnique<CXFA_Draw>(doc, packet);
-      break;
-    case XFA_Element::Encryption:
-      node = pdfium::MakeUnique<CXFA_Encryption>(doc, packet);
-      break;
-    case XFA_Element::MeridiemNames:
-      node = pdfium::MakeUnique<CXFA_MeridiemNames>(doc, packet);
-      break;
-    case XFA_Element::Messaging:
-      node = pdfium::MakeUnique<CXFA_Messaging>(doc, packet);
-      break;
-    case XFA_Element::Speak:
-      node = pdfium::MakeUnique<CXFA_Speak>(doc, packet);
-      break;
-    case XFA_Element::DataGroup:
-      node = pdfium::MakeUnique<CXFA_DataGroup>(doc, packet);
-      break;
-    case XFA_Element::Common:
-      node = pdfium::MakeUnique<CXFA_Common>(doc, packet);
-      break;
-    case XFA_Element::Sharptext:
-      node = pdfium::MakeUnique<CXFA_Sharptext>(doc, packet);
-      break;
-    case XFA_Element::PaginationOverride:
-      node = pdfium::MakeUnique<CXFA_PaginationOverride>(doc, packet);
-      break;
-    case XFA_Element::Reasons:
-      node = pdfium::MakeUnique<CXFA_Reasons>(doc, packet);
-      break;
-    case XFA_Element::SignatureProperties:
-      node = pdfium::MakeUnique<CXFA_SignatureProperties>(doc, packet);
-      break;
-    case XFA_Element::Threshold:
-      node = pdfium::MakeUnique<CXFA_Threshold>(doc, packet);
-      break;
-    case XFA_Element::AppearanceFilter:
-      node = pdfium::MakeUnique<CXFA_AppearanceFilter>(doc, packet);
-      break;
-    case XFA_Element::Fill:
-      node = pdfium::MakeUnique<CXFA_Fill>(doc, packet);
-      break;
-    case XFA_Element::Font:
-      node = pdfium::MakeUnique<CXFA_Font>(doc, packet);
-      break;
-    case XFA_Element::Form:
-      node = pdfium::MakeUnique<CXFA_Form>(doc, packet);
-      break;
-    case XFA_Element::MediumInfo:
-      node = pdfium::MakeUnique<CXFA_MediumInfo>(doc, packet);
-      break;
-    case XFA_Element::Certificate:
-      node = pdfium::MakeUnique<CXFA_Certificate>(doc, packet);
-      break;
-    case XFA_Element::Password:
-      node = pdfium::MakeUnique<CXFA_Password>(doc, packet);
-      break;
-    case XFA_Element::RunScripts:
-      node = pdfium::MakeUnique<CXFA_RunScripts>(doc, packet);
-      break;
-    case XFA_Element::Trace:
-      node = pdfium::MakeUnique<CXFA_Trace>(doc, packet);
-      break;
-    case XFA_Element::Float:
-      node = pdfium::MakeUnique<CXFA_Float>(doc, packet);
-      break;
-    case XFA_Element::RenderPolicy:
-      node = pdfium::MakeUnique<CXFA_RenderPolicy>(doc, packet);
-      break;
-    case XFA_Element::Destination:
-      node = pdfium::MakeUnique<CXFA_Destination>(doc, packet);
-      break;
-    case XFA_Element::Value:
-      node = pdfium::MakeUnique<CXFA_Value>(doc, packet);
-      break;
-    case XFA_Element::Bookend:
-      node = pdfium::MakeUnique<CXFA_Bookend>(doc, packet);
-      break;
-    case XFA_Element::ExObject:
-      node = pdfium::MakeUnique<CXFA_ExObject>(doc, packet);
-      break;
-    case XFA_Element::OpenAction:
-      node = pdfium::MakeUnique<CXFA_OpenAction>(doc, packet);
-      break;
-    case XFA_Element::NeverEmbed:
-      node = pdfium::MakeUnique<CXFA_NeverEmbed>(doc, packet);
-      break;
-    case XFA_Element::BindItems:
-      node = pdfium::MakeUnique<CXFA_BindItems>(doc, packet);
-      break;
-    case XFA_Element::Calculate:
-      node = pdfium::MakeUnique<CXFA_Calculate>(doc, packet);
-      break;
-    case XFA_Element::Print:
-      node = pdfium::MakeUnique<CXFA_Print>(doc, packet);
-      break;
-    case XFA_Element::Extras:
-      node = pdfium::MakeUnique<CXFA_Extras>(doc, packet);
-      break;
-    case XFA_Element::Proto:
-      node = pdfium::MakeUnique<CXFA_Proto>(doc, packet);
-      break;
-    case XFA_Element::DSigData:
-      node = pdfium::MakeUnique<CXFA_DSigData>(doc, packet);
-      break;
-    case XFA_Element::Creator:
-      node = pdfium::MakeUnique<CXFA_Creator>(doc, packet);
-      break;
-    case XFA_Element::Connect:
-      node = pdfium::MakeUnique<CXFA_Connect>(doc, packet);
-      break;
-    case XFA_Element::Permissions:
-      node = pdfium::MakeUnique<CXFA_Permissions>(doc, packet);
-      break;
-    case XFA_Element::ConnectionSet:
-      node = pdfium::MakeUnique<CXFA_ConnectionSet>(doc, packet);
-      break;
-    case XFA_Element::Submit:
-      node = pdfium::MakeUnique<CXFA_Submit>(doc, packet);
-      break;
-    case XFA_Element::Range:
-      node = pdfium::MakeUnique<CXFA_Range>(doc, packet);
-      break;
-    case XFA_Element::Linearized:
-      node = pdfium::MakeUnique<CXFA_Linearized>(doc, packet);
-      break;
-    case XFA_Element::Packet:
-      node = pdfium::MakeUnique<CXFA_Packet>(doc, packet);
-      break;
-    case XFA_Element::RootElement:
-      node = pdfium::MakeUnique<CXFA_RootElement>(doc, packet);
-      break;
-    case XFA_Element::PlaintextMetadata:
-      node = pdfium::MakeUnique<CXFA_PlaintextMetadata>(doc, packet);
-      break;
-    case XFA_Element::NumberSymbols:
-      node = pdfium::MakeUnique<CXFA_NumberSymbols>(doc, packet);
-      break;
-    case XFA_Element::PrintHighQuality:
-      node = pdfium::MakeUnique<CXFA_PrintHighQuality>(doc, packet);
-      break;
-    case XFA_Element::Driver:
-      node = pdfium::MakeUnique<CXFA_Driver>(doc, packet);
-      break;
-    case XFA_Element::IncrementalLoad:
-      node = pdfium::MakeUnique<CXFA_IncrementalLoad>(doc, packet);
-      break;
-    case XFA_Element::SubjectDN:
-      node = pdfium::MakeUnique<CXFA_SubjectDN>(doc, packet);
-      break;
-    case XFA_Element::CompressLogicalStructure:
-      node = pdfium::MakeUnique<CXFA_CompressLogicalStructure>(doc, packet);
-      break;
-    case XFA_Element::IncrementalMerge:
-      node = pdfium::MakeUnique<CXFA_IncrementalMerge>(doc, packet);
-      break;
-    case XFA_Element::Radial:
-      node = pdfium::MakeUnique<CXFA_Radial>(doc, packet);
-      break;
-    case XFA_Element::Variables:
-      node = pdfium::MakeUnique<CXFA_Variables>(doc, packet);
-      break;
-    case XFA_Element::TimePatterns:
-      node = pdfium::MakeUnique<CXFA_TimePatterns>(doc, packet);
-      break;
-    case XFA_Element::EffectiveInputPolicy:
-      node = pdfium::MakeUnique<CXFA_EffectiveInputPolicy>(doc, packet);
-      break;
-    case XFA_Element::NameAttr:
-      node = pdfium::MakeUnique<CXFA_NameAttr>(doc, packet);
-      break;
-    case XFA_Element::Conformance:
-      node = pdfium::MakeUnique<CXFA_Conformance>(doc, packet);
-      break;
-    case XFA_Element::Transform:
-      node = pdfium::MakeUnique<CXFA_Transform>(doc, packet);
-      break;
-    case XFA_Element::LockDocument:
-      node = pdfium::MakeUnique<CXFA_LockDocument>(doc, packet);
-      break;
-    case XFA_Element::BreakAfter:
-      node = pdfium::MakeUnique<CXFA_BreakAfter>(doc, packet);
-      break;
-    case XFA_Element::Line:
-      node = pdfium::MakeUnique<CXFA_Line>(doc, packet);
-      break;
-    case XFA_Element::Source:
-      node = pdfium::MakeUnique<CXFA_Source>(doc, packet);
-      break;
-    case XFA_Element::Occur:
-      node = pdfium::MakeUnique<CXFA_Occur>(doc, packet);
-      break;
-    case XFA_Element::PickTrayByPDFSize:
-      node = pdfium::MakeUnique<CXFA_PickTrayByPDFSize>(doc, packet);
-      break;
-    case XFA_Element::MonthNames:
-      node = pdfium::MakeUnique<CXFA_MonthNames>(doc, packet);
-      break;
-    case XFA_Element::Severity:
-      node = pdfium::MakeUnique<CXFA_Severity>(doc, packet);
-      break;
-    case XFA_Element::GroupParent:
-      node = pdfium::MakeUnique<CXFA_GroupParent>(doc, packet);
-      break;
-    case XFA_Element::DocumentAssembly:
-      node = pdfium::MakeUnique<CXFA_DocumentAssembly>(doc, packet);
-      break;
-    case XFA_Element::NumberSymbol:
-      node = pdfium::MakeUnique<CXFA_NumberSymbol>(doc, packet);
-      break;
-    case XFA_Element::Tagged:
-      node = pdfium::MakeUnique<CXFA_Tagged>(doc, packet);
-      break;
-    case XFA_Element::Items:
-      node = pdfium::MakeUnique<CXFA_Items>(doc, packet);
-      break;
-    default:
-      NOTREACHED();
-      return nullptr;
-  }
-  if (!node || !node->IsValidInPacket(packet))
-    return nullptr;
-  return node;
-}
-
-// static
-WideString CXFA_Node::AttributeToName(XFA_Attribute attr) {
-  switch (attr) {
-    case XFA_Attribute::H:
-      return L"h";
-    case XFA_Attribute::W:
-      return L"w";
-    case XFA_Attribute::X:
-      return L"x";
-    case XFA_Attribute::Y:
-      return L"y";
-    case XFA_Attribute::Id:
-      return L"id";
-    case XFA_Attribute::To:
-      return L"to";
-    case XFA_Attribute::LineThrough:
-      return L"lineThrough";
-    case XFA_Attribute::HAlign:
-      return L"hAlign";
-    case XFA_Attribute::Typeface:
-      return L"typeface";
-    case XFA_Attribute::BeforeTarget:
-      return L"beforeTarget";
-    case XFA_Attribute::Name:
-      return L"name";
-    case XFA_Attribute::Next:
-      return L"next";
-    case XFA_Attribute::DataRowCount:
-      return L"dataRowCount";
-    case XFA_Attribute::Break:
-      return L"break";
-    case XFA_Attribute::VScrollPolicy:
-      return L"vScrollPolicy";
-    case XFA_Attribute::FontHorizontalScale:
-      return L"fontHorizontalScale";
-    case XFA_Attribute::TextIndent:
-      return L"textIndent";
-    case XFA_Attribute::Context:
-      return L"context";
-    case XFA_Attribute::TrayOut:
-      return L"trayOut";
-    case XFA_Attribute::Cap:
-      return L"cap";
-    case XFA_Attribute::Max:
-      return L"max";
-    case XFA_Attribute::Min:
-      return L"min";
-    case XFA_Attribute::Ref:
-      return L"ref";
-    case XFA_Attribute::Rid:
-      return L"rid";
-    case XFA_Attribute::Url:
-      return L"url";
-    case XFA_Attribute::Use:
-      return L"use";
-    case XFA_Attribute::LeftInset:
-      return L"leftInset";
-    case XFA_Attribute::Widows:
-      return L"widows";
-    case XFA_Attribute::Level:
-      return L"level";
-    case XFA_Attribute::BottomInset:
-      return L"bottomInset";
-    case XFA_Attribute::OverflowTarget:
-      return L"overflowTarget";
-    case XFA_Attribute::AllowMacro:
-      return L"allowMacro";
-    case XFA_Attribute::PagePosition:
-      return L"pagePosition";
-    case XFA_Attribute::ColumnWidths:
-      return L"columnWidths";
-    case XFA_Attribute::OverflowLeader:
-      return L"overflowLeader";
-    case XFA_Attribute::Action:
-      return L"action";
-    case XFA_Attribute::NonRepudiation:
-      return L"nonRepudiation";
-    case XFA_Attribute::Rate:
-      return L"rate";
-    case XFA_Attribute::AllowRichText:
-      return L"allowRichText";
-    case XFA_Attribute::Role:
-      return L"role";
-    case XFA_Attribute::OverflowTrailer:
-      return L"overflowTrailer";
-    case XFA_Attribute::Operation:
-      return L"operation";
-    case XFA_Attribute::Timeout:
-      return L"timeout";
-    case XFA_Attribute::TopInset:
-      return L"topInset";
-    case XFA_Attribute::Access:
-      return L"access";
-    case XFA_Attribute::CommandType:
-      return L"commandType";
-    case XFA_Attribute::Format:
-      return L"format";
-    case XFA_Attribute::DataPrep:
-      return L"dataPrep";
-    case XFA_Attribute::WidgetData:
-      return L"widgetData";
-    case XFA_Attribute::Abbr:
-      return L"abbr";
-    case XFA_Attribute::MarginRight:
-      return L"marginRight";
-    case XFA_Attribute::DataDescription:
-      return L"dataDescription";
-    case XFA_Attribute::EncipherOnly:
-      return L"encipherOnly";
-    case XFA_Attribute::KerningMode:
-      return L"kerningMode";
-    case XFA_Attribute::Rotate:
-      return L"rotate";
-    case XFA_Attribute::WordCharacterCount:
-      return L"wordCharacterCount";
-    case XFA_Attribute::Type:
-      return L"type";
-    case XFA_Attribute::Reserve:
-      return L"reserve";
-    case XFA_Attribute::TextLocation:
-      return L"textLocation";
-    case XFA_Attribute::Priority:
-      return L"priority";
-    case XFA_Attribute::Underline:
-      return L"underline";
-    case XFA_Attribute::ModuleWidth:
-      return L"moduleWidth";
-    case XFA_Attribute::Hyphenate:
-      return L"hyphenate";
-    case XFA_Attribute::Listen:
-      return L"listen";
-    case XFA_Attribute::Delimiter:
-      return L"delimiter";
-    case XFA_Attribute::ContentType:
-      return L"contentType";
-    case XFA_Attribute::StartNew:
-      return L"startNew";
-    case XFA_Attribute::EofAction:
-      return L"eofAction";
-    case XFA_Attribute::AllowNeutral:
-      return L"allowNeutral";
-    case XFA_Attribute::Connection:
-      return L"connection";
-    case XFA_Attribute::BaselineShift:
-      return L"baselineShift";
-    case XFA_Attribute::OverlinePeriod:
-      return L"overlinePeriod";
-    case XFA_Attribute::FracDigits:
-      return L"fracDigits";
-    case XFA_Attribute::Orientation:
-      return L"orientation";
-    case XFA_Attribute::TimeStamp:
-      return L"timeStamp";
-    case XFA_Attribute::PrintCheckDigit:
-      return L"printCheckDigit";
-    case XFA_Attribute::MarginLeft:
-      return L"marginLeft";
-    case XFA_Attribute::Stroke:
-      return L"stroke";
-    case XFA_Attribute::ModuleHeight:
-      return L"moduleHeight";
-    case XFA_Attribute::TransferEncoding:
-      return L"transferEncoding";
-    case XFA_Attribute::Usage:
-      return L"usage";
-    case XFA_Attribute::Presence:
-      return L"presence";
-    case XFA_Attribute::RadixOffset:
-      return L"radixOffset";
-    case XFA_Attribute::Preserve:
-      return L"preserve";
-    case XFA_Attribute::AliasNode:
-      return L"aliasNode";
-    case XFA_Attribute::MultiLine:
-      return L"multiLine";
-    case XFA_Attribute::Version:
-      return L"version";
-    case XFA_Attribute::StartChar:
-      return L"startChar";
-    case XFA_Attribute::ScriptTest:
-      return L"scriptTest";
-    case XFA_Attribute::StartAngle:
-      return L"startAngle";
-    case XFA_Attribute::CursorType:
-      return L"cursorType";
-    case XFA_Attribute::DigitalSignature:
-      return L"digitalSignature";
-    case XFA_Attribute::CodeType:
-      return L"codeType";
-    case XFA_Attribute::Output:
-      return L"output";
-    case XFA_Attribute::BookendTrailer:
-      return L"bookendTrailer";
-    case XFA_Attribute::ImagingBBox:
-      return L"imagingBBox";
-    case XFA_Attribute::ExcludeInitialCap:
-      return L"excludeInitialCap";
-    case XFA_Attribute::Force:
-      return L"force";
-    case XFA_Attribute::CrlSign:
-      return L"crlSign";
-    case XFA_Attribute::Previous:
-      return L"previous";
-    case XFA_Attribute::PushCharacterCount:
-      return L"pushCharacterCount";
-    case XFA_Attribute::NullTest:
-      return L"nullTest";
-    case XFA_Attribute::RunAt:
-      return L"runAt";
-    case XFA_Attribute::SpaceBelow:
-      return L"spaceBelow";
-    case XFA_Attribute::SweepAngle:
-      return L"sweepAngle";
-    case XFA_Attribute::NumberOfCells:
-      return L"numberOfCells";
-    case XFA_Attribute::LetterSpacing:
-      return L"letterSpacing";
-    case XFA_Attribute::LockType:
-      return L"lockType";
-    case XFA_Attribute::PasswordChar:
-      return L"passwordChar";
-    case XFA_Attribute::VAlign:
-      return L"vAlign";
-    case XFA_Attribute::SourceBelow:
-      return L"sourceBelow";
-    case XFA_Attribute::Inverted:
-      return L"inverted";
-    case XFA_Attribute::Mark:
-      return L"mark";
-    case XFA_Attribute::MaxH:
-      return L"maxH";
-    case XFA_Attribute::MaxW:
-      return L"maxW";
-    case XFA_Attribute::Truncate:
-      return L"truncate";
-    case XFA_Attribute::MinH:
-      return L"minH";
-    case XFA_Attribute::MinW:
-      return L"minW";
-    case XFA_Attribute::Initial:
-      return L"initial";
-    case XFA_Attribute::Mode:
-      return L"mode";
-    case XFA_Attribute::Layout:
-      return L"layout";
-    case XFA_Attribute::Server:
-      return L"server";
-    case XFA_Attribute::EmbedPDF:
-      return L"embedPDF";
-    case XFA_Attribute::OddOrEven:
-      return L"oddOrEven";
-    case XFA_Attribute::TabDefault:
-      return L"tabDefault";
-    case XFA_Attribute::Contains:
-      return L"contains";
-    case XFA_Attribute::RightInset:
-      return L"rightInset";
-    case XFA_Attribute::MaxChars:
-      return L"maxChars";
-    case XFA_Attribute::Open:
-      return L"open";
-    case XFA_Attribute::Relation:
-      return L"relation";
-    case XFA_Attribute::WideNarrowRatio:
-      return L"wideNarrowRatio";
-    case XFA_Attribute::Relevant:
-      return L"relevant";
-    case XFA_Attribute::SignatureType:
-      return L"signatureType";
-    case XFA_Attribute::LineThroughPeriod:
-      return L"lineThroughPeriod";
-    case XFA_Attribute::Shape:
-      return L"shape";
-    case XFA_Attribute::TabStops:
-      return L"tabStops";
-    case XFA_Attribute::OutputBelow:
-      return L"outputBelow";
-    case XFA_Attribute::Short:
-      return L"short";
-    case XFA_Attribute::FontVerticalScale:
-      return L"fontVerticalScale";
-    case XFA_Attribute::Thickness:
-      return L"thickness";
-    case XFA_Attribute::CommitOn:
-      return L"commitOn";
-    case XFA_Attribute::RemainCharacterCount:
-      return L"remainCharacterCount";
-    case XFA_Attribute::KeyAgreement:
-      return L"keyAgreement";
-    case XFA_Attribute::ErrorCorrectionLevel:
-      return L"errorCorrectionLevel";
-    case XFA_Attribute::UpsMode:
-      return L"upsMode";
-    case XFA_Attribute::MergeMode:
-      return L"mergeMode";
-    case XFA_Attribute::Circular:
-      return L"circular";
-    case XFA_Attribute::PsName:
-      return L"psName";
-    case XFA_Attribute::Trailer:
-      return L"trailer";
-    case XFA_Attribute::UnicodeRange:
-      return L"unicodeRange";
-    case XFA_Attribute::ExecuteType:
-      return L"executeType";
-    case XFA_Attribute::DuplexImposition:
-      return L"duplexImposition";
-    case XFA_Attribute::TrayIn:
-      return L"trayIn";
-    case XFA_Attribute::BindingNode:
-      return L"bindingNode";
-    case XFA_Attribute::BofAction:
-      return L"bofAction";
-    case XFA_Attribute::Save:
-      return L"save";
-    case XFA_Attribute::TargetType:
-      return L"targetType";
-    case XFA_Attribute::KeyEncipherment:
-      return L"keyEncipherment";
-    case XFA_Attribute::CredentialServerPolicy:
-      return L"credentialServerPolicy";
-    case XFA_Attribute::Size:
-      return L"size";
-    case XFA_Attribute::InitialNumber:
-      return L"initialNumber";
-    case XFA_Attribute::Slope:
-      return L"slope";
-    case XFA_Attribute::CSpace:
-      return L"cSpace";
-    case XFA_Attribute::ColSpan:
-      return L"colSpan";
-    case XFA_Attribute::Binding:
-      return L"binding";
-    case XFA_Attribute::Checksum:
-      return L"checksum";
-    case XFA_Attribute::CharEncoding:
-      return L"charEncoding";
-    case XFA_Attribute::Bind:
-      return L"bind";
-    case XFA_Attribute::TextEntry:
-      return L"textEntry";
-    case XFA_Attribute::Archive:
-      return L"archive";
-    case XFA_Attribute::Uuid:
-      return L"uuid";
-    case XFA_Attribute::Posture:
-      return L"posture";
-    case XFA_Attribute::After:
-      return L"after";
-    case XFA_Attribute::Orphans:
-      return L"orphans";
-    case XFA_Attribute::QualifiedName:
-      return L"qualifiedName";
-    case XFA_Attribute::Usehref:
-      return L"usehref";
-    case XFA_Attribute::Locale:
-      return L"locale";
-    case XFA_Attribute::Weight:
-      return L"weight";
-    case XFA_Attribute::UnderlinePeriod:
-      return L"underlinePeriod";
-    case XFA_Attribute::Data:
-      return L"data";
-    case XFA_Attribute::Desc:
-      return L"desc";
-    case XFA_Attribute::Numbered:
-      return L"numbered";
-    case XFA_Attribute::DataColumnCount:
-      return L"dataColumnCount";
-    case XFA_Attribute::Overline:
-      return L"overline";
-    case XFA_Attribute::UrlPolicy:
-      return L"urlPolicy";
-    case XFA_Attribute::AnchorType:
-      return L"anchorType";
-    case XFA_Attribute::LabelRef:
-      return L"labelRef";
-    case XFA_Attribute::BookendLeader:
-      return L"bookendLeader";
-    case XFA_Attribute::MaxLength:
-      return L"maxLength";
-    case XFA_Attribute::AccessKey:
-      return L"accessKey";
-    case XFA_Attribute::CursorLocation:
-      return L"cursorLocation";
-    case XFA_Attribute::DelayedOpen:
-      return L"delayedOpen";
-    case XFA_Attribute::Target:
-      return L"target";
-    case XFA_Attribute::DataEncipherment:
-      return L"dataEncipherment";
-    case XFA_Attribute::AfterTarget:
-      return L"afterTarget";
-    case XFA_Attribute::Leader:
-      return L"leader";
-    case XFA_Attribute::Picker:
-      return L"picker";
-    case XFA_Attribute::From:
-      return L"from";
-    case XFA_Attribute::BaseProfile:
-      return L"baseProfile";
-    case XFA_Attribute::Aspect:
-      return L"aspect";
-    case XFA_Attribute::RowColumnRatio:
-      return L"rowColumnRatio";
-    case XFA_Attribute::LineHeight:
-      return L"lineHeight";
-    case XFA_Attribute::Highlight:
-      return L"highlight";
-    case XFA_Attribute::ValueRef:
-      return L"valueRef";
-    case XFA_Attribute::MaxEntries:
-      return L"maxEntries";
-    case XFA_Attribute::DataLength:
-      return L"dataLength";
-    case XFA_Attribute::Activity:
-      return L"activity";
-    case XFA_Attribute::Input:
-      return L"input";
-    case XFA_Attribute::Value:
-      return L"value";
-    case XFA_Attribute::BlankOrNotBlank:
-      return L"blankOrNotBlank";
-    case XFA_Attribute::AddRevocationInfo:
-      return L"addRevocationInfo";
-    case XFA_Attribute::GenericFamily:
-      return L"genericFamily";
-    case XFA_Attribute::Hand:
-      return L"hand";
-    case XFA_Attribute::Href:
-      return L"href";
-    case XFA_Attribute::TextEncoding:
-      return L"textEncoding";
-    case XFA_Attribute::LeadDigits:
-      return L"leadDigits";
-    case XFA_Attribute::Permissions:
-      return L"permissions";
-    case XFA_Attribute::SpaceAbove:
-      return L"spaceAbove";
-    case XFA_Attribute::CodeBase:
-      return L"codeBase";
-    case XFA_Attribute::Stock:
-      return L"stock";
-    case XFA_Attribute::IsNull:
-      return L"isNull";
-    case XFA_Attribute::RestoreState:
-      return L"restoreState";
-    case XFA_Attribute::ExcludeAllCaps:
-      return L"excludeAllCaps";
-    case XFA_Attribute::FormatTest:
-      return L"formatTest";
-    case XFA_Attribute::HScrollPolicy:
-      return L"hScrollPolicy";
-    case XFA_Attribute::Join:
-      return L"join";
-    case XFA_Attribute::KeyCertSign:
-      return L"keyCertSign";
-    case XFA_Attribute::Radius:
-      return L"radius";
-    case XFA_Attribute::SourceAbove:
-      return L"sourceAbove";
-    case XFA_Attribute::Override:
-      return L"override";
-    case XFA_Attribute::ClassId:
-      return L"classId";
-    case XFA_Attribute::Disable:
-      return L"disable";
-    case XFA_Attribute::Scope:
-      return L"scope";
-    case XFA_Attribute::Match:
-      return L"match";
-    case XFA_Attribute::Placement:
-      return L"placement";
-    case XFA_Attribute::Before:
-      return L"before";
-    case XFA_Attribute::WritingScript:
-      return L"writingScript";
-    case XFA_Attribute::EndChar:
-      return L"endChar";
-    case XFA_Attribute::Lock:
-      return L"lock";
-    case XFA_Attribute::Long:
-      return L"long";
-    case XFA_Attribute::Intact:
-      return L"intact";
-    case XFA_Attribute::XdpContent:
-      return L"xdpContent";
-    case XFA_Attribute::DecipherOnly:
-      return L"decipherOnly";
-
-    default:
-      NOTREACHED();
-      break;
-  }
-  return L"";
-}
-
-#ifndef NDEBUG
-// static
-WideString CXFA_Node::ElementToName(XFA_Element attr) {
-  switch (attr) {
-    case XFA_Element::Ps:
-      return L"ps";
-    case XFA_Element::To:
-      return L"to";
-    case XFA_Element::Ui:
-      return L"ui";
-    case XFA_Element::RecordSet:
-      return L"recordSet";
-    case XFA_Element::SubsetBelow:
-      return L"subsetBelow";
-    case XFA_Element::SubformSet:
-      return L"subformSet";
-    case XFA_Element::AdobeExtensionLevel:
-      return L"adobeExtensionLevel";
-    case XFA_Element::Typeface:
-      return L"typeface";
-    case XFA_Element::Break:
-      return L"break";
-    case XFA_Element::FontInfo:
-      return L"fontInfo";
-    case XFA_Element::NumberPattern:
-      return L"numberPattern";
-    case XFA_Element::DynamicRender:
-      return L"dynamicRender";
-    case XFA_Element::PrintScaling:
-      return L"printScaling";
-    case XFA_Element::CheckButton:
-      return L"checkButton";
-    case XFA_Element::DatePatterns:
-      return L"datePatterns";
-    case XFA_Element::SourceSet:
-      return L"sourceSet";
-    case XFA_Element::Amd:
-      return L"amd";
-    case XFA_Element::Arc:
-      return L"arc";
-    case XFA_Element::Day:
-      return L"day";
-    case XFA_Element::Era:
-      return L"era";
-    case XFA_Element::Jog:
-      return L"jog";
-    case XFA_Element::Log:
-      return L"log";
-    case XFA_Element::Map:
-      return L"map";
-    case XFA_Element::Mdp:
-      return L"mdp";
-    case XFA_Element::BreakBefore:
-      return L"breakBefore";
-    case XFA_Element::Oid:
-      return L"oid";
-    case XFA_Element::Pcl:
-      return L"pcl";
-    case XFA_Element::Pdf:
-      return L"pdf";
-    case XFA_Element::Ref:
-      return L"ref";
-    case XFA_Element::Uri:
-      return L"uri";
-    case XFA_Element::Xdc:
-      return L"xdc";
-    case XFA_Element::Xdp:
-      return L"xdp";
-    case XFA_Element::Xfa:
-      return L"xfa";
-    case XFA_Element::Xsl:
-      return L"xsl";
-    case XFA_Element::Zpl:
-      return L"zpl";
-    case XFA_Element::Cache:
-      return L"cache";
-    case XFA_Element::Margin:
-      return L"margin";
-    case XFA_Element::KeyUsage:
-      return L"keyUsage";
-    case XFA_Element::Exclude:
-      return L"exclude";
-    case XFA_Element::ChoiceList:
-      return L"choiceList";
-    case XFA_Element::Level:
-      return L"level";
-    case XFA_Element::LabelPrinter:
-      return L"labelPrinter";
-    case XFA_Element::CalendarSymbols:
-      return L"calendarSymbols";
-    case XFA_Element::Para:
-      return L"para";
-    case XFA_Element::Part:
-      return L"part";
-    case XFA_Element::Pdfa:
-      return L"pdfa";
-    case XFA_Element::Filter:
-      return L"filter";
-    case XFA_Element::Present:
-      return L"present";
-    case XFA_Element::Pagination:
-      return L"pagination";
-    case XFA_Element::Encoding:
-      return L"encoding";
-    case XFA_Element::Event:
-      return L"event";
-    case XFA_Element::Whitespace:
-      return L"whitespace";
-    case XFA_Element::DefaultUi:
-      return L"defaultUi";
-    case XFA_Element::DataModel:
-      return L"dataModel";
-    case XFA_Element::Barcode:
-      return L"barcode";
-    case XFA_Element::TimePattern:
-      return L"timePattern";
-    case XFA_Element::BatchOutput:
-      return L"batchOutput";
-    case XFA_Element::Enforce:
-      return L"enforce";
-    case XFA_Element::CurrencySymbols:
-      return L"currencySymbols";
-    case XFA_Element::AddSilentPrint:
-      return L"addSilentPrint";
-    case XFA_Element::Rename:
-      return L"rename";
-    case XFA_Element::Operation:
-      return L"operation";
-    case XFA_Element::Typefaces:
-      return L"typefaces";
-    case XFA_Element::SubjectDNs:
-      return L"subjectDNs";
-    case XFA_Element::Issuers:
-      return L"issuers";
-    case XFA_Element::WsdlConnection:
-      return L"wsdlConnection";
-    case XFA_Element::Debug:
-      return L"debug";
-    case XFA_Element::Delta:
-      return L"delta";
-    case XFA_Element::EraNames:
-      return L"eraNames";
-    case XFA_Element::ModifyAnnots:
-      return L"modifyAnnots";
-    case XFA_Element::StartNode:
-      return L"startNode";
-    case XFA_Element::Button:
-      return L"button";
-    case XFA_Element::Format:
-      return L"format";
-    case XFA_Element::Border:
-      return L"border";
-    case XFA_Element::Area:
-      return L"area";
-    case XFA_Element::Hyphenation:
-      return L"hyphenation";
-    case XFA_Element::Text:
-      return L"text";
-    case XFA_Element::Time:
-      return L"time";
-    case XFA_Element::Type:
-      return L"type";
-    case XFA_Element::Overprint:
-      return L"overprint";
-    case XFA_Element::Certificates:
-      return L"certificates";
-    case XFA_Element::EncryptionMethods:
-      return L"encryptionMethods";
-    case XFA_Element::SetProperty:
-      return L"setProperty";
-    case XFA_Element::PrinterName:
-      return L"printerName";
-    case XFA_Element::StartPage:
-      return L"startPage";
-    case XFA_Element::PageOffset:
-      return L"pageOffset";
-    case XFA_Element::DateTime:
-      return L"dateTime";
-    case XFA_Element::Comb:
-      return L"comb";
-    case XFA_Element::Pattern:
-      return L"pattern";
-    case XFA_Element::IfEmpty:
-      return L"ifEmpty";
-    case XFA_Element::SuppressBanner:
-      return L"suppressBanner";
-    case XFA_Element::OutputBin:
-      return L"outputBin";
-    case XFA_Element::Field:
-      return L"field";
-    case XFA_Element::Agent:
-      return L"agent";
-    case XFA_Element::OutputXSL:
-      return L"outputXSL";
-    case XFA_Element::AdjustData:
-      return L"adjustData";
-    case XFA_Element::AutoSave:
-      return L"autoSave";
-    case XFA_Element::ContentArea:
-      return L"contentArea";
-    case XFA_Element::WsdlAddress:
-      return L"wsdlAddress";
-    case XFA_Element::Solid:
-      return L"solid";
-    case XFA_Element::DateTimeSymbols:
-      return L"dateTimeSymbols";
-    case XFA_Element::EncryptionLevel:
-      return L"encryptionLevel";
-    case XFA_Element::Edge:
-      return L"edge";
-    case XFA_Element::Stipple:
-      return L"stipple";
-    case XFA_Element::Attributes:
-      return L"attributes";
-    case XFA_Element::VersionControl:
-      return L"versionControl";
-    case XFA_Element::Meridiem:
-      return L"meridiem";
-    case XFA_Element::ExclGroup:
-      return L"exclGroup";
-    case XFA_Element::ToolTip:
-      return L"toolTip";
-    case XFA_Element::Compress:
-      return L"compress";
-    case XFA_Element::Reason:
-      return L"reason";
-    case XFA_Element::Execute:
-      return L"execute";
-    case XFA_Element::ContentCopy:
-      return L"contentCopy";
-    case XFA_Element::DateTimeEdit:
-      return L"dateTimeEdit";
-    case XFA_Element::Config:
-      return L"config";
-    case XFA_Element::Image:
-      return L"image";
-    case XFA_Element::SharpxHTML:
-      return L"#xHTML";
-    case XFA_Element::NumberOfCopies:
-      return L"numberOfCopies";
-    case XFA_Element::BehaviorOverride:
-      return L"behaviorOverride";
-    case XFA_Element::TimeStamp:
-      return L"timeStamp";
-    case XFA_Element::Month:
-      return L"month";
-    case XFA_Element::ViewerPreferences:
-      return L"viewerPreferences";
-    case XFA_Element::ScriptModel:
-      return L"scriptModel";
-    case XFA_Element::Decimal:
-      return L"decimal";
-    case XFA_Element::Subform:
-      return L"subform";
-    case XFA_Element::Select:
-      return L"select";
-    case XFA_Element::Window:
-      return L"window";
-    case XFA_Element::LocaleSet:
-      return L"localeSet";
-    case XFA_Element::Handler:
-      return L"handler";
-    case XFA_Element::Presence:
-      return L"presence";
-    case XFA_Element::Record:
-      return L"record";
-    case XFA_Element::Embed:
-      return L"embed";
-    case XFA_Element::Version:
-      return L"version";
-    case XFA_Element::Command:
-      return L"command";
-    case XFA_Element::Copies:
-      return L"copies";
-    case XFA_Element::Staple:
-      return L"staple";
-    case XFA_Element::SubmitFormat:
-      return L"submitFormat";
-    case XFA_Element::Boolean:
-      return L"boolean";
-    case XFA_Element::Message:
-      return L"message";
-    case XFA_Element::Output:
-      return L"output";
-    case XFA_Element::PsMap:
-      return L"psMap";
-    case XFA_Element::ExcludeNS:
-      return L"excludeNS";
-    case XFA_Element::Assist:
-      return L"assist";
-    case XFA_Element::Picture:
-      return L"picture";
-    case XFA_Element::Traversal:
-      return L"traversal";
-    case XFA_Element::SilentPrint:
-      return L"silentPrint";
-    case XFA_Element::WebClient:
-      return L"webClient";
-    case XFA_Element::Producer:
-      return L"producer";
-    case XFA_Element::Corner:
-      return L"corner";
-    case XFA_Element::MsgId:
-      return L"msgId";
-    case XFA_Element::Color:
-      return L"color";
-    case XFA_Element::Keep:
-      return L"keep";
-    case XFA_Element::Query:
-      return L"query";
-    case XFA_Element::Insert:
-      return L"insert";
-    case XFA_Element::ImageEdit:
-      return L"imageEdit";
-    case XFA_Element::Validate:
-      return L"validate";
-    case XFA_Element::DigestMethods:
-      return L"digestMethods";
-    case XFA_Element::NumberPatterns:
-      return L"numberPatterns";
-    case XFA_Element::PageSet:
-      return L"pageSet";
-    case XFA_Element::Integer:
-      return L"integer";
-    case XFA_Element::SoapAddress:
-      return L"soapAddress";
-    case XFA_Element::Equate:
-      return L"equate";
-    case XFA_Element::FormFieldFilling:
-      return L"formFieldFilling";
-    case XFA_Element::PageRange:
-      return L"pageRange";
-    case XFA_Element::Update:
-      return L"update";
-    case XFA_Element::ConnectString:
-      return L"connectString";
-    case XFA_Element::Mode:
-      return L"mode";
-    case XFA_Element::Layout:
-      return L"layout";
-    case XFA_Element::Sharpxml:
-      return L"#xml";
-    case XFA_Element::XsdConnection:
-      return L"xsdConnection";
-    case XFA_Element::Traverse:
-      return L"traverse";
-    case XFA_Element::Encodings:
-      return L"encodings";
-    case XFA_Element::Template:
-      return L"template";
-    case XFA_Element::Acrobat:
-      return L"acrobat";
-    case XFA_Element::ValidationMessaging:
-      return L"validationMessaging";
-    case XFA_Element::Signing:
-      return L"signing";
-    case XFA_Element::Script:
-      return L"script";
-    case XFA_Element::AddViewerPreferences:
-      return L"addViewerPreferences";
-    case XFA_Element::AlwaysEmbed:
-      return L"alwaysEmbed";
-    case XFA_Element::PasswordEdit:
-      return L"passwordEdit";
-    case XFA_Element::NumericEdit:
-      return L"numericEdit";
-    case XFA_Element::EncryptionMethod:
-      return L"encryptionMethod";
-    case XFA_Element::Change:
-      return L"change";
-    case XFA_Element::PageArea:
-      return L"pageArea";
-    case XFA_Element::SubmitUrl:
-      return L"submitUrl";
-    case XFA_Element::Oids:
-      return L"oids";
-    case XFA_Element::Signature:
-      return L"signature";
-    case XFA_Element::ADBE_JSConsole:
-      return L"ADBE_JSConsole";
-    case XFA_Element::Caption:
-      return L"caption";
-    case XFA_Element::Relevant:
-      return L"relevant";
-    case XFA_Element::FlipLabel:
-      return L"flipLabel";
-    case XFA_Element::ExData:
-      return L"exData";
-    case XFA_Element::DayNames:
-      return L"dayNames";
-    case XFA_Element::SoapAction:
-      return L"soapAction";
-    case XFA_Element::DefaultTypeface:
-      return L"defaultTypeface";
-    case XFA_Element::Manifest:
-      return L"manifest";
-    case XFA_Element::Overflow:
-      return L"overflow";
-    case XFA_Element::Linear:
-      return L"linear";
-    case XFA_Element::CurrencySymbol:
-      return L"currencySymbol";
-    case XFA_Element::Delete:
-      return L"delete";
-    case XFA_Element::Deltas:
-      return L"deltas";
-    case XFA_Element::DigestMethod:
-      return L"digestMethod";
-    case XFA_Element::InstanceManager:
-      return L"instanceManager";
-    case XFA_Element::EquateRange:
-      return L"equateRange";
-    case XFA_Element::Medium:
-      return L"medium";
-    case XFA_Element::TextEdit:
-      return L"textEdit";
-    case XFA_Element::TemplateCache:
-      return L"templateCache";
-    case XFA_Element::CompressObjectStream:
-      return L"compressObjectStream";
-    case XFA_Element::DataValue:
-      return L"dataValue";
-    case XFA_Element::AccessibleContent:
-      return L"accessibleContent";
-    case XFA_Element::IncludeXDPContent:
-      return L"includeXDPContent";
-    case XFA_Element::XmlConnection:
-      return L"xmlConnection";
-    case XFA_Element::ValidateApprovalSignatures:
-      return L"validateApprovalSignatures";
-    case XFA_Element::SignData:
-      return L"signData";
-    case XFA_Element::Packets:
-      return L"packets";
-    case XFA_Element::DatePattern:
-      return L"datePattern";
-    case XFA_Element::DuplexOption:
-      return L"duplexOption";
-    case XFA_Element::Base:
-      return L"base";
-    case XFA_Element::Bind:
-      return L"bind";
-    case XFA_Element::Compression:
-      return L"compression";
-    case XFA_Element::User:
-      return L"user";
-    case XFA_Element::Rectangle:
-      return L"rectangle";
-    case XFA_Element::EffectiveOutputPolicy:
-      return L"effectiveOutputPolicy";
-    case XFA_Element::ADBE_JSDebugger:
-      return L"ADBE_JSDebugger";
-    case XFA_Element::Acrobat7:
-      return L"acrobat7";
-    case XFA_Element::Interactive:
-      return L"interactive";
-    case XFA_Element::Locale:
-      return L"locale";
-    case XFA_Element::CurrentPage:
-      return L"currentPage";
-    case XFA_Element::Data:
-      return L"data";
-    case XFA_Element::Date:
-      return L"date";
-    case XFA_Element::Desc:
-      return L"desc";
-    case XFA_Element::Encrypt:
-      return L"encrypt";
-    case XFA_Element::Draw:
-      return L"draw";
-    case XFA_Element::Encryption:
-      return L"encryption";
-    case XFA_Element::MeridiemNames:
-      return L"meridiemNames";
-    case XFA_Element::Messaging:
-      return L"messaging";
-    case XFA_Element::Speak:
-      return L"speak";
-    case XFA_Element::DataGroup:
-      return L"dataGroup";
-    case XFA_Element::Common:
-      return L"common";
-    case XFA_Element::Sharptext:
-      return L"#text";
-    case XFA_Element::PaginationOverride:
-      return L"paginationOverride";
-    case XFA_Element::Reasons:
-      return L"reasons";
-    case XFA_Element::SignatureProperties:
-      return L"signatureProperties";
-    case XFA_Element::Threshold:
-      return L"threshold";
-    case XFA_Element::AppearanceFilter:
-      return L"appearanceFilter";
-    case XFA_Element::Fill:
-      return L"fill";
-    case XFA_Element::Font:
-      return L"font";
-    case XFA_Element::Form:
-      return L"form";
-    case XFA_Element::MediumInfo:
-      return L"mediumInfo";
-    case XFA_Element::Certificate:
-      return L"certificate";
-    case XFA_Element::Password:
-      return L"password";
-    case XFA_Element::RunScripts:
-      return L"runScripts";
-    case XFA_Element::Trace:
-      return L"trace";
-    case XFA_Element::Float:
-      return L"float";
-    case XFA_Element::RenderPolicy:
-      return L"renderPolicy";
-    case XFA_Element::Destination:
-      return L"destination";
-    case XFA_Element::Value:
-      return L"value";
-    case XFA_Element::Bookend:
-      return L"bookend";
-    case XFA_Element::ExObject:
-      return L"exObject";
-    case XFA_Element::OpenAction:
-      return L"openAction";
-    case XFA_Element::NeverEmbed:
-      return L"neverEmbed";
-    case XFA_Element::BindItems:
-      return L"bindItems";
-    case XFA_Element::Calculate:
-      return L"calculate";
-    case XFA_Element::Print:
-      return L"print";
-    case XFA_Element::Extras:
-      return L"extras";
-    case XFA_Element::Proto:
-      return L"proto";
-    case XFA_Element::DSigData:
-      return L"dSigData";
-    case XFA_Element::Creator:
-      return L"creator";
-    case XFA_Element::Connect:
-      return L"connect";
-    case XFA_Element::Permissions:
-      return L"permissions";
-    case XFA_Element::ConnectionSet:
-      return L"connectionSet";
-    case XFA_Element::Submit:
-      return L"submit";
-    case XFA_Element::Range:
-      return L"range";
-    case XFA_Element::Linearized:
-      return L"linearized";
-    case XFA_Element::Packet:
-      return L"packet";
-    case XFA_Element::RootElement:
-      return L"rootElement";
-    case XFA_Element::PlaintextMetadata:
-      return L"plaintextMetadata";
-    case XFA_Element::NumberSymbols:
-      return L"numberSymbols";
-    case XFA_Element::PrintHighQuality:
-      return L"printHighQuality";
-    case XFA_Element::Driver:
-      return L"driver";
-    case XFA_Element::IncrementalLoad:
-      return L"incrementalLoad";
-    case XFA_Element::SubjectDN:
-      return L"subjectDN";
-    case XFA_Element::CompressLogicalStructure:
-      return L"compressLogicalStructure";
-    case XFA_Element::IncrementalMerge:
-      return L"incrementalMerge";
-    case XFA_Element::Radial:
-      return L"radial";
-    case XFA_Element::Variables:
-      return L"variables";
-    case XFA_Element::TimePatterns:
-      return L"timePatterns";
-    case XFA_Element::EffectiveInputPolicy:
-      return L"effectiveInputPolicy";
-    case XFA_Element::NameAttr:
-      return L"nameAttr";
-    case XFA_Element::Conformance:
-      return L"conformance";
-    case XFA_Element::Transform:
-      return L"transform";
-    case XFA_Element::LockDocument:
-      return L"lockDocument";
-    case XFA_Element::BreakAfter:
-      return L"breakAfter";
-    case XFA_Element::Line:
-      return L"line";
-    case XFA_Element::Source:
-      return L"source";
-    case XFA_Element::Occur:
-      return L"occur";
-    case XFA_Element::PickTrayByPDFSize:
-      return L"pickTrayByPDFSize";
-    case XFA_Element::MonthNames:
-      return L"monthNames";
-    case XFA_Element::Severity:
-      return L"severity";
-    case XFA_Element::GroupParent:
-      return L"groupParent";
-    case XFA_Element::DocumentAssembly:
-      return L"documentAssembly";
-    case XFA_Element::NumberSymbol:
-      return L"numberSymbol";
-    case XFA_Element::Tagged:
-      return L"tagged";
-    case XFA_Element::Items:
-      return L"items";
-
-    default:
-      NOTREACHED();
-      break;
-  }
-  return L"";
-}
-#endif  // NDEBUG
diff --git a/xfa/fxfa/parser/cxfa_node_unittest.cpp b/xfa/fxfa/parser/cxfa_node_unittest.cpp
index dedef5d..620704f 100644
--- a/xfa/fxfa/parser/cxfa_node_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_node_unittest.cpp
@@ -4,24 +4,378 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
+#include "fxjs/xfa/cjx_node.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
 
-TEST(CXFA_NodeTest, NameToAttribute) {
-  EXPECT_EQ(XFA_Attribute::Unknown, CXFA_Node::NameToAttribute(L""));
-  EXPECT_EQ(XFA_Attribute::Unknown, CXFA_Node::NameToAttribute(L"nonesuch"));
-  EXPECT_EQ(XFA_Attribute::H, CXFA_Node::NameToAttribute(L"h"));
-  EXPECT_EQ(XFA_Attribute::Short, CXFA_Node::NameToAttribute(L"short"));
-  EXPECT_EQ(XFA_Attribute::DecipherOnly,
-            CXFA_Node::NameToAttribute(L"decipherOnly"));
+namespace {
+
+class TestNode final : public CXFA_Node {
+ public:
+  explicit TestNode(CXFA_Document* doc)
+      : CXFA_Node(doc,
+                  XFA_PacketType::Form,
+                  (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
+                  XFA_ObjectType::Node,
+                  XFA_Element::Node,
+                  {},
+                  {},
+                  pdfium::MakeUnique<CJX_Node>(this)) {}
+
+  ~TestNode() override = default;
+};
+
+}  // namespace
+
+class CXFANodeTest : public testing::Test {
+ public:
+  void SetUp() override {
+    doc_ = pdfium::MakeUnique<CXFA_Document>(nullptr, nullptr);
+    node_ = pdfium::MakeUnique<TestNode>(doc_.get());
+  }
+
+  void TearDown() override {
+    node_ = nullptr;
+    doc_ = nullptr;
+  }
+
+  CXFA_Document* GetDoc() const { return doc_.get(); }
+  CXFA_Node* GetNode() const { return node_.get(); }
+
+ private:
+  std::unique_ptr<CXFA_Document> doc_;
+  std::unique_ptr<TestNode> node_;
+};
+
+TEST_F(CXFANodeTest, InsertFirstChild) {
+  EXPECT_EQ(nullptr, GetNode()->GetFirstChild());
+  EXPECT_EQ(nullptr, GetNode()->GetLastChild());
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child);
+
+  EXPECT_EQ(GetNode(), child->GetParent());
+  EXPECT_EQ(child, GetNode()->GetFirstChild());
+  EXPECT_EQ(child, GetNode()->GetLastChild());
+  EXPECT_EQ(nullptr, child->GetPrevSibling());
+  EXPECT_EQ(nullptr, child->GetNextSibling());
 }
 
-TEST(CXFA_NodeTest, GetAttributeEnumByName) {
-  EXPECT_FALSE(!!CXFA_Node::NameToAttributeEnum(L""));
-  EXPECT_FALSE(!!CXFA_Node::NameToAttributeEnum(L"nonesuch"));
-  EXPECT_EQ(XFA_AttributeEnum::Asterisk, *CXFA_Node::NameToAttributeEnum(L"*"));
-  EXPECT_EQ(XFA_AttributeEnum::Visible,
-            *CXFA_Node::NameToAttributeEnum(L"visible"));
-  EXPECT_EQ(XFA_AttributeEnum::Lowered,
-            *CXFA_Node::NameToAttributeEnum(L"lowered"));
+TEST_F(CXFANodeTest, InsertChildByNegativeIndex) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child);
+
+  EXPECT_EQ(GetNode(), child->GetParent());
+  EXPECT_EQ(nullptr, child->GetNextSibling());
+  EXPECT_EQ(child0, child->GetPrevSibling());
+  EXPECT_EQ(child, child0->GetNextSibling());
+  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child, GetNode()->GetLastChild());
+}
+
+TEST_F(CXFANodeTest, InsertChildByIndex) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child2 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child2);
+
+  CXFA_Node* child3 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child3);
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(2, child);
+
+  EXPECT_EQ(GetNode(), child->GetParent());
+
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child1, child0->GetNextSibling());
+  EXPECT_EQ(child, child1->GetNextSibling());
+  EXPECT_EQ(child2, child->GetNextSibling());
+  EXPECT_EQ(child3, child2->GetNextSibling());
+  EXPECT_EQ(nullptr, child3->GetNextSibling());
+
+  EXPECT_EQ(child3, GetNode()->GetLastChild());
+  EXPECT_EQ(child2, child3->GetPrevSibling());
+  EXPECT_EQ(child, child2->GetPrevSibling());
+  EXPECT_EQ(child1, child->GetPrevSibling());
+  EXPECT_EQ(child0, child1->GetPrevSibling());
+  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+}
+
+TEST_F(CXFANodeTest, InsertChildIndexPastEnd) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(20, child);
+
+  EXPECT_EQ(GetNode(), child->GetParent());
+  EXPECT_EQ(nullptr, child->GetNextSibling());
+  EXPECT_EQ(child1, child->GetPrevSibling());
+  EXPECT_EQ(child, child1->GetNextSibling());
+
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child, GetNode()->GetLastChild());
+}
+
+TEST_F(CXFANodeTest, InsertFirstChildBeforeNullptr) {
+  EXPECT_EQ(nullptr, GetNode()->GetFirstChild());
+  EXPECT_EQ(nullptr, GetNode()->GetLastChild());
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(child, nullptr);
+  EXPECT_EQ(child, GetNode()->GetFirstChild());
+  EXPECT_EQ(child, GetNode()->GetLastChild());
+}
+
+TEST_F(CXFANodeTest, InsertBeforeWithNullBefore) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(child, nullptr);
+
+  EXPECT_EQ(GetNode(), child->GetParent());
+  EXPECT_EQ(nullptr, child->GetNextSibling());
+  EXPECT_EQ(child1, child->GetPrevSibling());
+  EXPECT_EQ(child, child1->GetNextSibling());
+
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child, GetNode()->GetLastChild());
+}
+
+TEST_F(CXFANodeTest, InsertBeforeFirstChild) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(child, child0);
+
+  EXPECT_EQ(GetNode(), child->GetParent());
+  EXPECT_EQ(child0, child->GetNextSibling());
+  EXPECT_EQ(nullptr, child->GetPrevSibling());
+  EXPECT_EQ(child, child0->GetPrevSibling());
+
+  EXPECT_EQ(child, GetNode()->GetFirstChild());
+  EXPECT_EQ(child1, GetNode()->GetLastChild());
+}
+
+TEST_F(CXFANodeTest, InsertBefore) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child2 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child2);
+
+  CXFA_Node* child3 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child3);
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(child, child2);
+
+  EXPECT_EQ(GetNode(), child->GetParent());
+  EXPECT_EQ(child2, child->GetNextSibling());
+  EXPECT_EQ(child1, child->GetPrevSibling());
+  EXPECT_EQ(child, child1->GetNextSibling());
+  EXPECT_EQ(child, child2->GetPrevSibling());
+
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child3, GetNode()->GetLastChild());
+}
+
+TEST_F(CXFANodeTest, RemoveOnlyChild) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child0, GetNode()->GetLastChild());
+
+  GetNode()->RemoveChildAndNotify(child0, false);
+  EXPECT_EQ(nullptr, GetNode()->GetFirstChild());
+  EXPECT_EQ(nullptr, GetNode()->GetLastChild());
+  EXPECT_EQ(nullptr, child0->GetParent());
+  EXPECT_EQ(nullptr, child0->GetNextSibling());
+  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+}
+
+TEST_F(CXFANodeTest, RemoveFirstChild) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child2 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child2);
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child2, GetNode()->GetLastChild());
+
+  GetNode()->RemoveChildAndNotify(child0, false);
+  EXPECT_EQ(child1, GetNode()->GetFirstChild());
+  EXPECT_EQ(child2, GetNode()->GetLastChild());
+  EXPECT_EQ(nullptr, child1->GetPrevSibling());
+  EXPECT_EQ(nullptr, child0->GetParent());
+  EXPECT_EQ(nullptr, child0->GetNextSibling());
+  EXPECT_EQ(nullptr, child0->GetPrevSibling());
+}
+
+TEST_F(CXFANodeTest, RemoveLastChild) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child2 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child2);
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child2, GetNode()->GetLastChild());
+
+  GetNode()->RemoveChildAndNotify(child2, false);
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child1, GetNode()->GetLastChild());
+  EXPECT_EQ(nullptr, child1->GetNextSibling());
+  EXPECT_EQ(nullptr, child2->GetParent());
+  EXPECT_EQ(nullptr, child2->GetNextSibling());
+  EXPECT_EQ(nullptr, child2->GetPrevSibling());
+}
+
+TEST_F(CXFANodeTest, RemoveChild) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child2 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child2);
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child2, GetNode()->GetLastChild());
+
+  GetNode()->RemoveChildAndNotify(child1, false);
+  EXPECT_EQ(child0, GetNode()->GetFirstChild());
+  EXPECT_EQ(child2, GetNode()->GetLastChild());
+  EXPECT_EQ(child2, child0->GetNextSibling());
+  EXPECT_EQ(child0, child2->GetPrevSibling());
+  EXPECT_EQ(nullptr, child1->GetParent());
+  EXPECT_EQ(nullptr, child1->GetNextSibling());
+  EXPECT_EQ(nullptr, child1->GetPrevSibling());
+}
+
+TEST_F(CXFANodeTest, InsertChildWithParent) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  child0->InsertChildAndNotify(-1, child1);
+
+  EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(0, child1), "");
+}
+
+TEST_F(CXFANodeTest, InsertNullChild) {
+  EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(0, nullptr), "");
+}
+
+TEST_F(CXFANodeTest, InsertBeforeWithNullChild) {
+  EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(nullptr, nullptr),
+                            "");
+}
+
+TEST_F(CXFANodeTest, InsertBeforeWithBeforeInAnotherParent) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  child0->InsertChildAndNotify(-1, child1);
+
+  CXFA_Node* child =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(child, child1), "");
+}
+
+TEST_F(CXFANodeTest, InsertBeforeWithNodeInAnotherParent) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  child0->InsertChildAndNotify(-1, child1);
+
+  EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(child1, nullptr),
+                            "");
+}
+
+TEST_F(CXFANodeTest, RemoveChildNullptr) {
+  EXPECT_DEATH_IF_SUPPORTED(GetNode()->RemoveChildAndNotify(nullptr, false),
+                            "");
+}
+
+TEST_F(CXFANodeTest, RemoveChildAnotherParent) {
+  CXFA_Node* child0 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  GetNode()->InsertChildAndNotify(-1, child0);
+
+  CXFA_Node* child1 =
+      GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui);
+  child0->InsertChildAndNotify(-1, child1);
+
+  GetNode()->RemoveChildAndNotify(child1, false);
+  EXPECT_EQ(child0, child1->GetParent());
 }
diff --git a/xfa/fxfa/parser/cxfa_nodehelper.cpp b/xfa/fxfa/parser/cxfa_nodehelper.cpp
index eea054a..dde90bc 100644
--- a/xfa/fxfa/parser/cxfa_nodehelper.cpp
+++ b/xfa/fxfa/parser/cxfa_nodehelper.cpp
@@ -6,287 +6,35 @@
 
 #include "xfa/fxfa/parser/cxfa_nodehelper.h"
 
+#include <utility>
+
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-CXFA_NodeHelper::CXFA_NodeHelper()
-    : m_eLastCreateType(XFA_Element::DataValue),
-      m_pCreateParent(nullptr),
-      m_iCreateCount(0),
-      m_iCreateFlag(XFA_ResolveNode_RSType_CreateNodeOne),
-      m_iCurAllStart(-1),
-      m_pAllStartParent(nullptr) {}
+CXFA_NodeHelper::CXFA_NodeHelper() = default;
 
-CXFA_NodeHelper::~CXFA_NodeHelper() {}
+CXFA_NodeHelper::~CXFA_NodeHelper() = default;
 
-CXFA_Node* CXFA_NodeHelper::ResolveNodes_GetOneChild(CXFA_Node* parent,
-                                                     const wchar_t* pwsName,
-                                                     bool bIsClassName) {
-  if (!parent)
-    return nullptr;
-
-  std::vector<CXFA_Node*> siblings;
-  uint32_t uNameHash = FX_HashCode_GetW(WideStringView(pwsName), false);
-  NodeAcc_TraverseAnySiblings(parent, uNameHash, &siblings, bIsClassName);
-  return !siblings.empty() ? siblings[0] : nullptr;
-}
-
-int32_t CXFA_NodeHelper::CountSiblings(CXFA_Node* pNode,
-                                       XFA_LOGIC_TYPE eLogicType,
-                                       std::vector<CXFA_Node*>* pSiblings,
-                                       bool bIsClassName) {
-  if (!pNode)
-    return 0;
-  CXFA_Node* parent = ResolveNodes_GetParent(pNode, XFA_LOGIC_NoTransparent);
-  if (!parent)
-    return 0;
-  if (!parent->HasProperty(pNode->GetElementType()) &&
-      eLogicType == XFA_LOGIC_Transparent) {
-    parent = ResolveNodes_GetParent(pNode, XFA_LOGIC_Transparent);
-    if (!parent)
-      return 0;
-  }
-  if (bIsClassName) {
-    return NodeAcc_TraverseSiblings(parent, pNode->GetClassHashCode(),
-                                    pSiblings, eLogicType, bIsClassName);
-  }
-  return NodeAcc_TraverseSiblings(parent, pNode->GetNameHash(), pSiblings,
-                                  eLogicType, bIsClassName);
-}
-
-int32_t CXFA_NodeHelper::NodeAcc_TraverseAnySiblings(
-    CXFA_Node* parent,
-    uint32_t dNameHash,
-    std::vector<CXFA_Node*>* pSiblings,
-    bool bIsClassName) {
-  if (!parent || !pSiblings)
-    return 0;
-
-  int32_t nCount = 0;
-  for (CXFA_Node* child :
-       parent->GetNodeList(XFA_NODEFILTER_Properties, XFA_Element::Unknown)) {
-    if (bIsClassName) {
-      if (child->GetClassHashCode() == dNameHash) {
-        pSiblings->push_back(child);
-        nCount++;
-      }
-    } else {
-      if (child->GetNameHash() == dNameHash) {
-        pSiblings->push_back(child);
-        nCount++;
-      }
-    }
-    if (nCount > 0)
-      return nCount;
-
-    nCount +=
-        NodeAcc_TraverseAnySiblings(child, dNameHash, pSiblings, bIsClassName);
-  }
-  for (CXFA_Node* child :
-       parent->GetNodeList(XFA_NODEFILTER_Children, XFA_Element::Unknown)) {
-    if (bIsClassName) {
-      if (child->GetClassHashCode() == dNameHash) {
-        pSiblings->push_back(child);
-        nCount++;
-      }
-    } else {
-      if (child->GetNameHash() == dNameHash) {
-        pSiblings->push_back(child);
-        nCount++;
-      }
-    }
-    if (nCount > 0)
-      return nCount;
-
-    nCount +=
-        NodeAcc_TraverseAnySiblings(child, dNameHash, pSiblings, bIsClassName);
-  }
-  return nCount;
-}
-
-int32_t CXFA_NodeHelper::NodeAcc_TraverseSiblings(
-    CXFA_Node* parent,
-    uint32_t dNameHash,
-    std::vector<CXFA_Node*>* pSiblings,
-    XFA_LOGIC_TYPE eLogicType,
-    bool bIsClassName,
-    bool bIsFindProperty) {
-  if (!parent || !pSiblings)
-    return 0;
-
-  int32_t nCount = 0;
-  if (bIsFindProperty) {
-    for (CXFA_Node* child :
-         parent->GetNodeList(XFA_NODEFILTER_Properties, XFA_Element::Unknown)) {
-      if (bIsClassName) {
-        if (child->GetClassHashCode() == dNameHash) {
-          pSiblings->push_back(child);
-          nCount++;
-        }
-      } else {
-        if (child->GetNameHash() == dNameHash) {
-          if (child->GetElementType() != XFA_Element::PageSet &&
-              child->GetElementType() != XFA_Element::Extras &&
-              child->GetElementType() != XFA_Element::Items) {
-            pSiblings->push_back(child);
-            nCount++;
-          }
-        }
-      }
-      if (child->IsUnnamed() &&
-          child->GetElementType() == XFA_Element::PageSet) {
-        nCount += NodeAcc_TraverseSiblings(child, dNameHash, pSiblings,
-                                           eLogicType, bIsClassName, false);
-      }
-    }
-    if (nCount > 0)
-      return nCount;
-  }
-  for (CXFA_Node* child :
-       parent->GetNodeList(XFA_NODEFILTER_Children, XFA_Element::Unknown)) {
-    if (child->GetElementType() == XFA_Element::Variables)
-      continue;
-
-    if (bIsClassName) {
-      if (child->GetClassHashCode() == dNameHash) {
-        pSiblings->push_back(child);
-        nCount++;
-      }
-    } else {
-      if (child->GetNameHash() == dNameHash) {
-        pSiblings->push_back(child);
-        nCount++;
-      }
-    }
-    if (eLogicType == XFA_LOGIC_NoTransparent)
-      continue;
-
-    if (NodeIsTransparent(child) &&
-        child->GetElementType() != XFA_Element::PageSet) {
-      nCount += NodeAcc_TraverseSiblings(child, dNameHash, pSiblings,
-                                         eLogicType, bIsClassName, false);
-    }
-  }
-  return nCount;
-}
-
-CXFA_Node* CXFA_NodeHelper::ResolveNodes_GetParent(CXFA_Node* pNode,
-                                                   XFA_LOGIC_TYPE eLogicType) {
-  if (!pNode) {
-    return nullptr;
-  }
-  if (eLogicType == XFA_LOGIC_NoTransparent) {
-    return pNode->GetParent();
-  }
-  CXFA_Node* parent;
-  CXFA_Node* node = pNode;
-  while (true) {
-    parent = ResolveNodes_GetParent(node);
-    if (!parent) {
-      break;
-    }
-    XFA_Element parentType = parent->GetElementType();
-    if ((!parent->IsUnnamed() && parentType != XFA_Element::SubformSet) ||
-        parentType == XFA_Element::Variables) {
-      break;
-    }
-    node = parent;
-  }
-  return parent;
-}
-
-int32_t CXFA_NodeHelper::GetIndex(CXFA_Node* pNode,
-                                  XFA_LOGIC_TYPE eLogicType,
-                                  bool bIsProperty,
-                                  bool bIsClassIndex) {
-  CXFA_Node* parent = ResolveNodes_GetParent(pNode, XFA_LOGIC_NoTransparent);
-  if (!parent) {
-    return 0;
-  }
-  if (!bIsProperty && eLogicType == XFA_LOGIC_Transparent) {
-    parent = ResolveNodes_GetParent(pNode, XFA_LOGIC_Transparent);
-    if (!parent) {
-      return 0;
-    }
-  }
-  uint32_t dwHashName = pNode->GetNameHash();
-  if (bIsClassIndex) {
-    dwHashName = pNode->GetClassHashCode();
-  }
-  std::vector<CXFA_Node*> siblings;
-  int32_t iSize = NodeAcc_TraverseSiblings(parent, dwHashName, &siblings,
-                                           eLogicType, bIsClassIndex);
-  for (int32_t i = 0; i < iSize; ++i) {
-    CXFA_Node* child = siblings[i];
-    if (child == pNode) {
-      return i;
-    }
-  }
-  return 0;
-}
-
-WideString CXFA_NodeHelper::GetNameExpression(CXFA_Node* refNode,
-                                              bool bIsAllPath,
-                                              XFA_LOGIC_TYPE eLogicType) {
-  WideString wsName;
-  if (bIsAllPath) {
-    wsName = GetNameExpression(refNode, false, eLogicType);
-    WideString wsParent;
-    CXFA_Node* parent =
-        ResolveNodes_GetParent(refNode, XFA_LOGIC_NoTransparent);
-    while (parent) {
-      wsParent = GetNameExpression(parent, false, eLogicType);
-      wsParent += L".";
-      wsParent += wsName;
-      wsName = wsParent;
-      parent = ResolveNodes_GetParent(parent, XFA_LOGIC_NoTransparent);
-    }
-    return wsName;
-  }
-
-  WideString ws;
-  bool bIsProperty = NodeIsProperty(refNode);
-  if (refNode->IsUnnamed() ||
-      (bIsProperty && refNode->GetElementType() != XFA_Element::PageSet)) {
-    ws = refNode->GetClassName();
-    return WideString::Format(L"#%ls[%d]", ws.c_str(),
-                              GetIndex(refNode, eLogicType, bIsProperty, true));
-  }
-  ws = refNode->JSObject()->GetCData(XFA_Attribute::Name);
-  ws.Replace(L".", L"\\.");
-  return WideString::Format(L"%ls[%d]", ws.c_str(),
-                            GetIndex(refNode, eLogicType, bIsProperty, false));
-}
-
-bool CXFA_NodeHelper::NodeIsTransparent(CXFA_Node* refNode) {
-  if (!refNode)
-    return false;
-
-  XFA_Element refNodeType = refNode->GetElementType();
-  return (refNode->IsUnnamed() && refNode->IsContainerNode()) ||
-         refNodeType == XFA_Element::SubformSet ||
-         refNodeType == XFA_Element::Area || refNodeType == XFA_Element::Proto;
-}
-
-bool CXFA_NodeHelper::CreateNode_ForCondition(WideString& wsCondition) {
-  int32_t iLen = wsCondition.GetLength();
+bool CXFA_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) {
+  size_t szLen = wsCondition.GetLength();
   WideString wsIndex(L"0");
   bool bAll = false;
-  if (iLen == 0) {
+  if (szLen == 0) {
     m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne;
     return false;
   }
   if (wsCondition[0] != '[')
     return false;
 
-  int32_t i = 1;
-  for (; i < iLen; ++i) {
+  size_t i = 1;
+  for (; i < szLen; ++i) {
     wchar_t ch = wsCondition[i];
     if (ch == ' ')
       continue;
@@ -300,44 +48,51 @@
     m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeAll;
   } else {
     m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne;
-    wsIndex = wsCondition.Mid(i, iLen - 1 - i);
+    wsIndex = wsCondition.Substr(i, szLen - 1 - i);
   }
-  int32_t iIndex = wsIndex.GetInteger();
-  m_iCreateCount = iIndex;
+  int32_t iCount = wsIndex.GetInteger();
+  if (iCount < 0)
+    return false;
+
+  m_iCreateCount = iCount;
   return true;
 }
 
-bool CXFA_NodeHelper::ResolveNodes_CreateNode(WideString wsName,
-                                              WideString wsCondition,
-                                              bool bLastNode,
-                                              CFXJSE_Engine* pScriptContext) {
-  if (!m_pCreateParent) {
+bool CXFA_NodeHelper::CreateNode(const WideString& wsName,
+                                 const WideString& wsCondition,
+                                 bool bLastNode,
+                                 CFXJSE_Engine* pScriptContext) {
+  if (!m_pCreateParent)
     return false;
-  }
+
+  WideStringView wsNameView = wsName.AsStringView();
   bool bIsClassName = false;
   bool bResult = false;
-  if (wsName[0] == '!') {
-    wsName = wsName.Right(wsName.GetLength() - 1);
+  if (!wsNameView.IsEmpty() && wsNameView[0] == '!') {
+    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
     m_pCreateParent = ToNode(
         pScriptContext->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
   }
-  if (wsName[0] == '#') {
+  if (!wsNameView.IsEmpty() && wsNameView[0] == '#') {
     bIsClassName = true;
-    wsName = wsName.Right(wsName.GetLength() - 1);
+    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
   }
-  if (m_iCreateCount == 0) {
-    CreateNode_ForCondition(wsCondition);
-  }
+  if (wsNameView.IsEmpty())
+    return false;
+
+  if (m_iCreateCount == 0)
+    CreateNodeForCondition(wsCondition);
+
   if (bIsClassName) {
-    XFA_Element eType = CXFA_Node::NameToElement(wsName);
+    XFA_Element eType = XFA_GetElementByName(wsNameView);
     if (eType == XFA_Element::Unknown)
       return false;
 
-    for (int32_t iIndex = 0; iIndex < m_iCreateCount; iIndex++) {
+    for (size_t i = 0; i < m_iCreateCount; ++i) {
       CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eType);
       if (pNewNode) {
-        m_pCreateParent->InsertChild(pNewNode, nullptr);
-        if (iIndex == m_iCreateCount - 1) {
+        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
+        if (i == m_iCreateCount - 1) {
           m_pCreateParent = pNewNode;
         }
         bResult = true;
@@ -348,23 +103,23 @@
     if (bLastNode) {
       eClassType = m_eLastCreateType;
     }
-    for (int32_t iIndex = 0; iIndex < m_iCreateCount; iIndex++) {
+    for (size_t i = 0; i < m_iCreateCount; ++i) {
       CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eClassType);
       if (pNewNode) {
-        pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name,
-                                           wsName.AsStringView(), false);
+        pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name, wsNameView,
+                                           false);
         pNewNode->CreateXMLMappingNode();
-        m_pCreateParent->InsertChild(pNewNode, nullptr);
-        if (iIndex == m_iCreateCount - 1) {
+        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
+        if (i == m_iCreateCount - 1) {
           m_pCreateParent = pNewNode;
         }
         bResult = true;
       }
     }
   }
-  if (!bResult) {
+  if (!bResult)
     m_pCreateParent = nullptr;
-  }
+
   return bResult;
 }
 
@@ -382,8 +137,3 @@
     m_eLastCreateType = XFA_Element::DataValue;
   }
 }
-
-bool CXFA_NodeHelper::NodeIsProperty(CXFA_Node* refNode) {
-  CXFA_Node* parent = ResolveNodes_GetParent(refNode, XFA_LOGIC_NoTransparent);
-  return parent && refNode && parent->HasProperty(refNode->GetElementType());
-}
diff --git a/xfa/fxfa/parser/cxfa_nodehelper.h b/xfa/fxfa/parser/cxfa_nodehelper.h
index e13387e..66e7e66 100644
--- a/xfa/fxfa/parser/cxfa_nodehelper.h
+++ b/xfa/fxfa/parser/cxfa_nodehelper.h
@@ -9,64 +9,31 @@
 
 #include <vector>
 
+#include "core/fxcrt/fx_string.h"
+#include "xfa/fxfa/fxfa_basic.h"
 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
 class CFXJSE_Engine;
-
-enum XFA_LOGIC_TYPE {
-  XFA_LOGIC_NoTransparent,
-  XFA_LOGIC_Transparent,
-};
+class CXFA_Node;
 
 class CXFA_NodeHelper {
  public:
   CXFA_NodeHelper();
   ~CXFA_NodeHelper();
 
-  CXFA_Node* ResolveNodes_GetOneChild(CXFA_Node* parent,
-                                      const wchar_t* pwsName,
-                                      bool bIsClassName = false);
-  CXFA_Node* ResolveNodes_GetParent(
-      CXFA_Node* pNode,
-      XFA_LOGIC_TYPE eLogicType = XFA_LOGIC_NoTransparent);
-
-  int32_t NodeAcc_TraverseSiblings(CXFA_Node* parent,
-                                   uint32_t dNameHash,
-                                   std::vector<CXFA_Node*>* pSiblings,
-                                   XFA_LOGIC_TYPE eLogicType,
-                                   bool bIsClassName = false,
-                                   bool bIsFindProperty = true);
-  int32_t NodeAcc_TraverseAnySiblings(CXFA_Node* parent,
-                                      uint32_t dNameHash,
-                                      std::vector<CXFA_Node*>* pSiblings,
-                                      bool bIsClassName = false);
-  int32_t CountSiblings(CXFA_Node* pNode,
-                        XFA_LOGIC_TYPE eLogicType,
-                        std::vector<CXFA_Node*>* pSiblings,
-                        bool bIsClassName = false);
-  int32_t GetIndex(CXFA_Node* pNode,
-                   XFA_LOGIC_TYPE eLogicType = XFA_LOGIC_NoTransparent,
-                   bool bIsProperty = false,
-                   bool bIsClassIndex = false);
-  WideString GetNameExpression(
-      CXFA_Node* refNode,
-      bool bIsAllPath,
-      XFA_LOGIC_TYPE eLogicType = XFA_LOGIC_NoTransparent);
-  bool NodeIsTransparent(CXFA_Node* refNode);
-  bool ResolveNodes_CreateNode(WideString wsName,
-                               WideString wsCondition,
-                               bool bLastNode,
-                               CFXJSE_Engine* pScriptContext);
-  bool CreateNode_ForCondition(WideString& wsCondition);
+  bool CreateNode(const WideString& wsName,
+                  const WideString& wsCondition,
+                  bool bLastNode,
+                  CFXJSE_Engine* pScriptContext);
+  bool CreateNodeForCondition(const WideString& wsCondition);
   void SetCreateNodeType(CXFA_Node* refNode);
-  bool NodeIsProperty(CXFA_Node* refNode);
 
-  XFA_Element m_eLastCreateType;
-  CXFA_Node* m_pCreateParent;
-  int32_t m_iCreateCount;
-  XFA_ResolveNode_RSType m_iCreateFlag;
-  int32_t m_iCurAllStart;
-  CXFA_Node* m_pAllStartParent;
+  XFA_Element m_eLastCreateType = XFA_Element::DataValue;
+  XFA_ResolveNode_RSType m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne;
+  size_t m_iCreateCount = 0;
+  int32_t m_iCurAllStart = -1;
+  UnownedPtr<CXFA_Node> m_pCreateParent;
+  UnownedPtr<CXFA_Node> m_pAllStartParent;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NODEHELPER_H_
diff --git a/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h b/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h
index 214f38e..8803efa 100644
--- a/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h
+++ b/xfa/fxfa/parser/cxfa_nodeiteratortemplate.h
@@ -7,14 +7,18 @@
 #ifndef XFA_FXFA_PARSER_CXFA_NODEITERATORTEMPLATE_H_
 #define XFA_FXFA_PARSER_CXFA_NODEITERATORTEMPLATE_H_
 
-template <class NodeType, class TraverseStrategy>
+#include "core/fxcrt/unowned_ptr.h"
+
+template <class NodeType,
+          class TraverseStrategy,
+          typename HolderType = UnownedPtr<NodeType>>
 class CXFA_NodeIteratorTemplate {
  public:
   explicit CXFA_NodeIteratorTemplate(NodeType* pRoot)
       : m_pRoot(pRoot), m_pCurrent(pRoot) {}
 
-  NodeType* GetRoot() const { return m_pRoot; }
-  NodeType* GetCurrent() const { return m_pCurrent; }
+  NodeType* GetRoot() const { return m_pRoot.Get(); }
+  NodeType* GetCurrent() const { return m_pCurrent.Get(); }
 
   void Reset() { m_pCurrent = m_pRoot; }
   bool SetCurrent(NodeType* pNode) {
@@ -22,7 +26,7 @@
       m_pCurrent = nullptr;
       return false;
     }
-    m_pCurrent = pNode;
+    m_pCurrent.Reset(pNode);
     return true;
   }
 
@@ -30,29 +34,27 @@
     if (!m_pRoot)
       return nullptr;
     if (!m_pCurrent) {
-      m_pCurrent = LastDescendant(m_pRoot);
-      return m_pCurrent;
+      m_pCurrent.Reset(LastDescendant(m_pRoot.Get()));
+      return m_pCurrent.Get();
     }
-    NodeType* pSibling = PreviousSiblingWithinSubtree(m_pCurrent);
+    NodeType* pSibling = PreviousSiblingWithinSubtree(m_pCurrent.Get());
     if (pSibling) {
-      m_pCurrent = LastDescendant(pSibling);
-      return m_pCurrent;
+      m_pCurrent.Reset(LastDescendant(pSibling));
+      return m_pCurrent.Get();
     }
-    NodeType* pParent = ParentWithinSubtree(m_pCurrent);
-    if (pParent) {
-      m_pCurrent = pParent;
-      return m_pCurrent;
-    }
-    return nullptr;
+    NodeType* pParent = ParentWithinSubtree(m_pCurrent.Get());
+    if (pParent)
+      m_pCurrent.Reset(pParent);
+    return pParent;
   }
 
   NodeType* MoveToNext() {
     if (!m_pRoot || !m_pCurrent)
       return nullptr;
-    NodeType* pChild = TraverseStrategy::GetFirstChild(m_pCurrent);
+    NodeType* pChild = TraverseStrategy::GetFirstChild(m_pCurrent.Get());
     if (pChild) {
-      m_pCurrent = pChild;
-      return m_pCurrent;
+      m_pCurrent.Reset(pChild);
+      return pChild;
     }
     return SkipChildrenAndMoveToNext();
   }
@@ -60,38 +62,32 @@
   NodeType* SkipChildrenAndMoveToNext() {
     if (!m_pRoot)
       return nullptr;
-    NodeType* pNode = m_pCurrent;
+    NodeType* pNode = m_pCurrent.Get();
     while (pNode) {
       NodeType* pSibling = NextSiblingWithinSubtree(pNode);
       if (pSibling) {
-        m_pCurrent = pSibling;
-        return m_pCurrent;
+        m_pCurrent.Reset(pSibling);
+        return pSibling;
       }
       pNode = ParentWithinSubtree(pNode);
     }
     m_pCurrent = nullptr;
-    return m_pCurrent;
+    return nullptr;
   }
 
  private:
   bool RootReachableFromNode(NodeType* pNode) {
-    if (!pNode)
-      return false;
-    if (pNode == m_pRoot)
-      return true;
-    return RootReachableFromNode(TraverseStrategy::GetParent(pNode));
+    return pNode && (pNode == m_pRoot ||
+                     RootReachableFromNode(TraverseStrategy::GetParent(pNode)));
   }
 
   NodeType* ParentWithinSubtree(NodeType* pNode) {
-    if (!pNode || pNode == m_pRoot)
-      return nullptr;
-    return TraverseStrategy::GetParent(pNode);
+    return pNode && pNode != m_pRoot ? TraverseStrategy::GetParent(pNode)
+                                     : nullptr;
   }
 
   NodeType* NextSiblingWithinSubtree(NodeType* pNode) {
-    if (pNode == m_pRoot)
-      return nullptr;
-    return TraverseStrategy::GetNextSibling(pNode);
+    return pNode != m_pRoot ? TraverseStrategy::GetNextSibling(pNode) : nullptr;
   }
 
   NodeType* PreviousSiblingWithinSubtree(NodeType* pNode) {
@@ -122,8 +118,8 @@
     return pChild ? LastDescendant(pChild) : pNode;
   }
 
-  NodeType* m_pRoot;
-  NodeType* m_pCurrent;
+  HolderType m_pRoot;
+  HolderType m_pCurrent;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NODEITERATORTEMPLATE_H_
diff --git a/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp b/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp
index 114bed0..aa4f72d 100644
--- a/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_nodeiteratortemplate_unittest.cpp
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 #include "third_party/base/ptr_util.h"
 
 class CXFA_NodeIteratorTemplateTest : public testing::Test {
diff --git a/xfa/fxfa/parser/cxfa_nodelocale.cpp b/xfa/fxfa/parser/cxfa_nodelocale.cpp
index a93452c..01d2dc5 100644
--- a/xfa/fxfa/parser/cxfa_nodelocale.cpp
+++ b/xfa/fxfa/parser/cxfa_nodelocale.cpp
@@ -8,7 +8,6 @@
 
 #include <utility>
 
-#include "core/fxcrt/xml/cxml_element.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "xfa/fxfa/parser/cxfa_calendarsymbols.h"
 #include "xfa/fxfa/parser/cxfa_datetimesymbols.h"
@@ -51,24 +50,24 @@
                         : nullptr);
 }
 
-WideString CXFA_NodeLocale::GetNumbericSymbol(FX_LOCALENUMSYMBOL eType) const {
-  switch (eType) {
-    case FX_LOCALENUMSYMBOL_Decimal:
-      return GetSymbol(XFA_Element::NumberSymbols, L"decimal");
-    case FX_LOCALENUMSYMBOL_Grouping:
-      return GetSymbol(XFA_Element::NumberSymbols, L"grouping");
-    case FX_LOCALENUMSYMBOL_Percent:
-      return GetSymbol(XFA_Element::NumberSymbols, L"percent");
-    case FX_LOCALENUMSYMBOL_Minus:
-      return GetSymbol(XFA_Element::NumberSymbols, L"minus");
-    case FX_LOCALENUMSYMBOL_Zero:
-      return GetSymbol(XFA_Element::NumberSymbols, L"zero");
-    case FX_LOCALENUMSYMBOL_CurrencySymbol:
-      return GetSymbol(XFA_Element::CurrencySymbols, L"symbol");
-    case FX_LOCALENUMSYMBOL_CurrencyName:
-      return GetSymbol(XFA_Element::CurrencySymbols, L"isoname");
-  }
-  return WideString();
+WideString CXFA_NodeLocale::GetDecimalSymbol() const {
+  return GetSymbol(XFA_Element::NumberSymbols, L"decimal");
+}
+
+WideString CXFA_NodeLocale::GetGroupingSymbol() const {
+  return GetSymbol(XFA_Element::NumberSymbols, L"grouping");
+}
+
+WideString CXFA_NodeLocale::GetPercentSymbol() const {
+  return GetSymbol(XFA_Element::NumberSymbols, L"percent");
+}
+
+WideString CXFA_NodeLocale::GetMinusSymbol() const {
+  return GetSymbol(XFA_Element::NumberSymbols, L"minus");
+}
+
+WideString CXFA_NodeLocale::GetCurrencySymbol() const {
+  return GetSymbol(XFA_Element::CurrencySymbols, L"symbol");
 }
 
 WideString CXFA_NodeLocale::GetDateTimeSymbols() const {
@@ -136,7 +135,7 @@
 }
 
 CXFA_Node* CXFA_NodeLocale::GetNodeByName(CXFA_Node* pParent,
-                                          const WideStringView& wsName) const {
+                                          WideStringView wsName) const {
   CXFA_Node* pChild = pParent ? pParent->GetFirstChild() : nullptr;
   while (pChild) {
     if (pChild->JSObject()->GetAttribute(XFA_Attribute::Name) == wsName)
@@ -148,7 +147,7 @@
 }
 
 WideString CXFA_NodeLocale::GetSymbol(XFA_Element eElement,
-                                      const WideStringView& symbol_type) const {
+                                      WideStringView symbol_type) const {
   CXFA_Node* pSymbols =
       m_pLocale ? m_pLocale->GetChild<CXFA_Node>(0, eElement, false) : nullptr;
   CXFA_Node* pSymbol = GetNodeByName(pSymbols, symbol_type);
@@ -165,8 +164,8 @@
   if (!pCalendar)
     return WideString();
 
-  CXFA_Node* pNode = pCalendar->GetFirstChildByClass<CXFA_Node>(eElement);
-  for (; pNode; pNode = pNode->GetNextSameClassSibling<CXFA_Node>(eElement)) {
+  for (CXFA_Node* pNode = pCalendar->GetFirstChildByClass<CXFA_Node>(eElement);
+       pNode; pNode = pNode->GetNextSameClassSibling<CXFA_Node>(eElement)) {
     if (pNode->JSObject()->GetBoolean(XFA_Attribute::Abbr) == bAbbr) {
       CXFA_Node* pSymbol =
           pNode->GetChild<CXFA_Node>(index, XFA_Element::Unknown, false);
diff --git a/xfa/fxfa/parser/cxfa_nodelocale.h b/xfa/fxfa/parser/cxfa_nodelocale.h
index 6f79e4a..0ada1a4 100644
--- a/xfa/fxfa/parser/cxfa_nodelocale.h
+++ b/xfa/fxfa/parser/cxfa_nodelocale.h
@@ -7,24 +7,25 @@
 #ifndef XFA_FXFA_PARSER_CXFA_NODELOCALE_H_
 #define XFA_FXFA_PARSER_CXFA_NODELOCALE_H_
 
-#include <memory>
-
-#include "core/fxcrt/ifx_locale.h"
+#include "xfa/fgas/crt/locale_iface.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CXFA_Node;
 
 WideString XFA_PatternToString(FX_LOCALENUMSUBCATEGORY category);
 
-class CXFA_NodeLocale : public IFX_Locale {
+class CXFA_NodeLocale final : public LocaleIface {
  public:
   explicit CXFA_NodeLocale(CXFA_Node* pLocale);
   ~CXFA_NodeLocale() override;
 
-  // IFX_Locale
+  // LocaleIface
   WideString GetName() const override;
-  WideString GetNumbericSymbol(FX_LOCALENUMSYMBOL eType) const override;
-
+  WideString GetDecimalSymbol() const override;
+  WideString GetGroupingSymbol() const override;
+  WideString GetPercentSymbol() const override;
+  WideString GetMinusSymbol() const override;
+  WideString GetCurrencySymbol() const override;
   WideString GetDateTimeSymbols() const override;
   WideString GetMonthName(int32_t nMonth, bool bAbbr) const override;
   WideString GetDayName(int32_t nWeek, bool bAbbr) const override;
@@ -37,10 +38,8 @@
   WideString GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const override;
 
  private:
-  CXFA_Node* GetNodeByName(CXFA_Node* pParent,
-                           const WideStringView& wsName) const;
-  WideString GetSymbol(XFA_Element eElement,
-                       const WideStringView& symbol_type) const;
+  CXFA_Node* GetNodeByName(CXFA_Node* pParent, WideStringView wsName) const;
+  WideString GetSymbol(XFA_Element eElement, WideStringView symbol_type) const;
   WideString GetCalendarSymbol(XFA_Element eElement,
                                int index,
                                bool bAbbr) const;
diff --git a/xfa/fxfa/parser/cxfa_nodeowner.cpp b/xfa/fxfa/parser/cxfa_nodeowner.cpp
new file mode 100644
index 0000000..9baf16c
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_nodeowner.cpp
@@ -0,0 +1,26 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/parser/cxfa_nodeowner.h"
+
+#include <utility>
+
+#include "xfa/fxfa/parser/cxfa_node.h"
+
+CXFA_NodeOwner::CXFA_NodeOwner() = default;
+
+CXFA_NodeOwner::~CXFA_NodeOwner() {
+  is_being_destroyed_ = true;
+}
+
+CXFA_Node* CXFA_NodeOwner::AddOwnedNode(std::unique_ptr<CXFA_Node> node) {
+  if (!node)
+    return nullptr;
+
+  CXFA_Node* ret = node.get();
+  nodes_.push_back(std::move(node));
+  return ret;
+}
diff --git a/xfa/fxfa/parser/cxfa_nodeowner.h b/xfa/fxfa/parser/cxfa_nodeowner.h
new file mode 100644
index 0000000..8f6abca
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_nodeowner.h
@@ -0,0 +1,29 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef XFA_FXFA_PARSER_CXFA_NODEOWNER_H_
+#define XFA_FXFA_PARSER_CXFA_NODEOWNER_H_
+
+#include <memory>
+#include <vector>
+
+class CXFA_Node;
+
+class CXFA_NodeOwner {
+ public:
+  virtual ~CXFA_NodeOwner();
+
+  CXFA_Node* AddOwnedNode(std::unique_ptr<CXFA_Node> node);
+  bool IsBeingDestroyed() const { return is_being_destroyed_; }
+
+ protected:
+  CXFA_NodeOwner();
+
+  bool is_being_destroyed_ = false;
+  std::vector<std::unique_ptr<CXFA_Node>> nodes_;
+};
+
+#endif  // XFA_FXFA_PARSER_CXFA_NODEOWNER_H_
diff --git a/xfa/fxfa/parser/cxfa_numberofcopies.cpp b/xfa/fxfa/parser/cxfa_numberofcopies.cpp
index 0978859..d66fd98 100644
--- a/xfa/fxfa/parser/cxfa_numberofcopies.cpp
+++ b/xfa/fxfa/parser/cxfa_numberofcopies.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_numberofcopies.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kNumberOfCopiesAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"numberOfCopies";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::NumberOfCopies,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kNumberOfCopiesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NumberOfCopies::~CXFA_NumberOfCopies() {}
+CXFA_NumberOfCopies::~CXFA_NumberOfCopies() = default;
diff --git a/xfa/fxfa/parser/cxfa_numberofcopies.h b/xfa/fxfa/parser/cxfa_numberofcopies.h
index 04bb599..3215bb3 100644
--- a/xfa/fxfa/parser/cxfa_numberofcopies.h
+++ b/xfa/fxfa/parser/cxfa_numberofcopies.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NumberOfCopies : public CXFA_Node {
+class CXFA_NumberOfCopies final : public CXFA_Node {
  public:
   CXFA_NumberOfCopies(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NumberOfCopies() override;
diff --git a/xfa/fxfa/parser/cxfa_numberpattern.cpp b/xfa/fxfa/parser/cxfa_numberpattern.cpp
index 6ee2698..964855c 100644
--- a/xfa/fxfa/parser/cxfa_numberpattern.cpp
+++ b/xfa/fxfa/parser/cxfa_numberpattern.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_numberpattern.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kNumberPatternAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Numeric},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"numberPattern";
+     (void*)XFA_AttributeValue::Numeric},
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::NumberPattern,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kNumberPatternAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NumberPattern::~CXFA_NumberPattern() {}
+CXFA_NumberPattern::~CXFA_NumberPattern() = default;
diff --git a/xfa/fxfa/parser/cxfa_numberpattern.h b/xfa/fxfa/parser/cxfa_numberpattern.h
index 0a4fabd..5c42d46 100644
--- a/xfa/fxfa/parser/cxfa_numberpattern.h
+++ b/xfa/fxfa/parser/cxfa_numberpattern.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NumberPattern : public CXFA_Node {
+class CXFA_NumberPattern final : public CXFA_Node {
  public:
   CXFA_NumberPattern(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NumberPattern() override;
diff --git a/xfa/fxfa/parser/cxfa_numberpatterns.cpp b/xfa/fxfa/parser/cxfa_numberpatterns.cpp
index a2f8373..1597a29 100644
--- a/xfa/fxfa/parser/cxfa_numberpatterns.cpp
+++ b/xfa/fxfa/parser/cxfa_numberpatterns.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_numberpatterns.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kNumberPatternsPropertyData[] = {
     {XFA_Element::NumberPattern, 4, 0},
-    {XFA_Element::Unknown, 0, 0}};
-
-constexpr wchar_t kName[] = L"numberPatterns";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::NumberPatterns,
-                kPropertyData,
-                nullptr,
-                kName) {}
+                kNumberPatternsPropertyData,
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NumberPatterns::~CXFA_NumberPatterns() {}
+CXFA_NumberPatterns::~CXFA_NumberPatterns() = default;
diff --git a/xfa/fxfa/parser/cxfa_numberpatterns.h b/xfa/fxfa/parser/cxfa_numberpatterns.h
index ef4f889..58c8cb6 100644
--- a/xfa/fxfa/parser/cxfa_numberpatterns.h
+++ b/xfa/fxfa/parser/cxfa_numberpatterns.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NumberPatterns : public CXFA_Node {
+class CXFA_NumberPatterns final : public CXFA_Node {
  public:
   CXFA_NumberPatterns(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NumberPatterns() override;
diff --git a/xfa/fxfa/parser/cxfa_numbersymbol.cpp b/xfa/fxfa/parser/cxfa_numbersymbol.cpp
index 74fdc1b..398ab17 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbol.cpp
+++ b/xfa/fxfa/parser/cxfa_numbersymbol.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_numbersymbol.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kNumberSymbolAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Decimal},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"numberSymbol";
+     (void*)XFA_AttributeValue::Decimal},
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::NumberSymbol,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kNumberSymbolAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NumberSymbol::~CXFA_NumberSymbol() {}
+CXFA_NumberSymbol::~CXFA_NumberSymbol() = default;
diff --git a/xfa/fxfa/parser/cxfa_numbersymbol.h b/xfa/fxfa/parser/cxfa_numbersymbol.h
index c164b03..404f366 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbol.h
+++ b/xfa/fxfa/parser/cxfa_numbersymbol.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NumberSymbol : public CXFA_Node {
+class CXFA_NumberSymbol final : public CXFA_Node {
  public:
   CXFA_NumberSymbol(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NumberSymbol() override;
diff --git a/xfa/fxfa/parser/cxfa_numbersymbols.cpp b/xfa/fxfa/parser/cxfa_numbersymbols.cpp
index 0d27f69..adc7234 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbols.cpp
+++ b/xfa/fxfa/parser/cxfa_numbersymbols.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_numbersymbols.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kNumberSymbolsPropertyData[] = {
     {XFA_Element::NumberSymbol, 5, 0},
-    {XFA_Element::Unknown, 0, 0}};
-
-constexpr wchar_t kName[] = L"numberSymbols";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::NumberSymbols,
-                kPropertyData,
-                nullptr,
-                kName) {}
+                kNumberSymbolsPropertyData,
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NumberSymbols::~CXFA_NumberSymbols() {}
+CXFA_NumberSymbols::~CXFA_NumberSymbols() = default;
diff --git a/xfa/fxfa/parser/cxfa_numbersymbols.h b/xfa/fxfa/parser/cxfa_numbersymbols.h
index 972df9f..37bb6fc 100644
--- a/xfa/fxfa/parser/cxfa_numbersymbols.h
+++ b/xfa/fxfa/parser/cxfa_numbersymbols.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NumberSymbols : public CXFA_Node {
+class CXFA_NumberSymbols final : public CXFA_Node {
  public:
   CXFA_NumberSymbols(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NumberSymbols() override;
diff --git a/xfa/fxfa/parser/cxfa_numericedit.cpp b/xfa/fxfa/parser/cxfa_numericedit.cpp
index 5a65c76..641fe2a 100644
--- a/xfa/fxfa/parser/cxfa_numericedit.cpp
+++ b/xfa/fxfa/parser/cxfa_numericedit.cpp
@@ -6,25 +6,25 @@
 
 #include "xfa/fxfa/parser/cxfa_numericedit.h"
 
-#include "fxjs/xfa/cjx_numericedit.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Margin, 1, 0},
-                                                 {XFA_Element::Border, 1, 0},
-                                                 {XFA_Element::Comb, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kNumericEditPropertyData[] = {
+    {XFA_Element::Margin, 1, 0},
+    {XFA_Element::Border, 1, 0},
+    {XFA_Element::Comb, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kNumericEditAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HScrollPolicy, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"numericEdit";
+     (void*)XFA_AttributeValue::Auto},
+};
 
 }  // namespace
 
@@ -34,9 +34,16 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::NumericEdit,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_NumericEdit>(this)) {}
+                kNumericEditPropertyData,
+                kNumericEditAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_NumericEdit::~CXFA_NumericEdit() {}
+CXFA_NumericEdit::~CXFA_NumericEdit() = default;
+
+XFA_Element CXFA_NumericEdit::GetValueNodeType() const {
+  return XFA_Element::Float;
+}
+
+XFA_FFWidgetType CXFA_NumericEdit::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kNumericEdit;
+}
diff --git a/xfa/fxfa/parser/cxfa_numericedit.h b/xfa/fxfa/parser/cxfa_numericedit.h
index d9cfd7e..145bf08 100644
--- a/xfa/fxfa/parser/cxfa_numericedit.h
+++ b/xfa/fxfa/parser/cxfa_numericedit.h
@@ -9,10 +9,13 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_NumericEdit : public CXFA_Node {
+class CXFA_NumericEdit final : public CXFA_Node {
  public:
   CXFA_NumericEdit(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_NumericEdit() override;
+
+  XFA_Element GetValueNodeType() const override;
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_NUMERICEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_object.cpp b/xfa/fxfa/parser/cxfa_object.cpp
index 7c73db0..6d730bf 100644
--- a/xfa/fxfa/parser/cxfa_object.cpp
+++ b/xfa/fxfa/parser/cxfa_object.cpp
@@ -9,35 +9,43 @@
 #include <utility>
 
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/xfa/cjx_object.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_thisproxy.h"
 #include "xfa/fxfa/parser/cxfa_treelist.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 
 CXFA_Object::CXFA_Object(CXFA_Document* pDocument,
                          XFA_ObjectType objectType,
                          XFA_Element elementType,
-                         const WideStringView& elementName,
                          std::unique_ptr<CJX_Object> jsObject)
-    : CFXJSE_HostObject(kXFA),
-      m_pDocument(pDocument),
+    : m_pDocument(pDocument),
       m_objectType(objectType),
       m_elementType(elementType),
-      m_elementNameHash(FX_HashCode_GetW(elementName, false)),
-      m_elementName(elementName),
+      m_elementName(XFA_ElementToName(elementType)),
+      m_elementNameHash(FX_HashCode_GetAsIfW(m_elementName, false)),
       m_pJSObject(std::move(jsObject)) {}
 
-CXFA_Object::~CXFA_Object() {}
+CXFA_Object::~CXFA_Object() {
+  if (!GetDocument()->IsBeingDestroyed() && GetDocument()->HasScriptContext())
+    GetDocument()->GetScriptContext()->RemoveJSBindingFromMap(this);
+}
+
+CXFA_Object* CXFA_Object::AsCXFAObject() {
+  return this;
+}
 
 WideString CXFA_Object::GetSOMExpression() {
-  CFXJSE_Engine* pScriptContext = m_pDocument->GetScriptContext();
-  if (!pScriptContext)
-    return WideString();
+  CXFA_Node* pNode = AsNode();
+  return pNode ? pNode->GetNameExpression() : WideString();
+}
 
-  return pScriptContext->GetSomExpression(ToNode(this));
+CXFA_List* CXFA_Object::AsList() {
+  return IsList() ? static_cast<CXFA_List*>(this) : nullptr;
 }
 
 CXFA_Node* CXFA_Object::AsNode() {
@@ -48,22 +56,22 @@
   return IsTreeList() ? static_cast<CXFA_TreeList*>(this) : nullptr;
 }
 
-const CXFA_Node* CXFA_Object::AsNode() const {
-  return IsNode() ? static_cast<const CXFA_Node*>(this) : nullptr;
+CXFA_ThisProxy* CXFA_Object::AsThisProxy() {
+  return IsThisProxy() ? static_cast<CXFA_ThisProxy*>(this) : nullptr;
 }
 
-const CXFA_TreeList* CXFA_Object::AsTreeList() const {
-  return IsTreeList() ? static_cast<const CXFA_TreeList*>(this) : nullptr;
-}
-
-void CXFA_Object::CreateWidgetAcc() {
-  acc_ = pdfium::MakeUnique<CXFA_WidgetAcc>(AsNode());
+CXFA_List* ToList(CXFA_Object* pObj) {
+  return pObj ? pObj->AsList() : nullptr;
 }
 
 CXFA_Node* ToNode(CXFA_Object* pObj) {
   return pObj ? pObj->AsNode() : nullptr;
 }
 
-const CXFA_Node* ToNode(const CXFA_Object* pObj) {
-  return pObj ? pObj->AsNode() : nullptr;
+CXFA_TreeList* ToTreeList(CXFA_Object* pObj) {
+  return pObj ? pObj->AsTreeList() : nullptr;
+}
+
+CXFA_ThisProxy* ToThisProxy(CXFA_Object* pObj) {
+  return pObj ? pObj->AsThisProxy() : nullptr;
 }
diff --git a/xfa/fxfa/parser/cxfa_object.h b/xfa/fxfa/parser/cxfa_object.h
index 5d5f31c..cfbcb13 100644
--- a/xfa/fxfa/parser/cxfa_object.h
+++ b/xfa/fxfa/parser/cxfa_object.h
@@ -10,7 +10,7 @@
 #include <memory>
 
 #include "core/fxcrt/fx_string.h"
-#include "fxjs/fxjse.h"
+#include "fxjs/xfa/fxjse.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 enum class XFA_ObjectType {
@@ -24,22 +24,30 @@
   TreeList,
   ContainerNode,
   ContentNode,
-  VariablesThis
+  ThisProxy,
 };
 
 class CJX_Object;
 class CXFA_Document;
+class CXFA_List;
 class CXFA_Node;
+class CXFA_ThisProxy;
 class CXFA_TreeList;
-class CXFA_WidgetAcc;
 
 class CXFA_Object : public CFXJSE_HostObject {
  public:
   ~CXFA_Object() override;
 
+  // CFXJSE_HostObject:
+  CXFA_Object* AsCXFAObject() override;
+
   CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
   XFA_ObjectType GetObjectType() const { return m_objectType; }
 
+  bool IsList() const {
+    return m_objectType == XFA_ObjectType::List ||
+           m_objectType == XFA_ObjectType::TreeList;
+  }
   bool IsNode() const {
     return m_objectType == XFA_ObjectType::Node ||
            m_objectType == XFA_ObjectType::NodeC ||
@@ -47,8 +55,7 @@
            m_objectType == XFA_ObjectType::ModelNode ||
            m_objectType == XFA_ObjectType::TextNode ||
            m_objectType == XFA_ObjectType::ContainerNode ||
-           m_objectType == XFA_ObjectType::ContentNode ||
-           m_objectType == XFA_ObjectType::VariablesThis;
+           m_objectType == XFA_ObjectType::ContentNode;
   }
   bool IsTreeList() const { return m_objectType == XFA_ObjectType::TreeList; }
   bool IsContentNode() const {
@@ -59,15 +66,12 @@
   }
   bool IsModelNode() const { return m_objectType == XFA_ObjectType::ModelNode; }
   bool IsNodeV() const { return m_objectType == XFA_ObjectType::NodeV; }
-  bool IsVariablesThis() const {
-    return m_objectType == XFA_ObjectType::VariablesThis;
-  }
+  bool IsThisProxy() const { return m_objectType == XFA_ObjectType::ThisProxy; }
 
+  CXFA_List* AsList();
   CXFA_Node* AsNode();
   CXFA_TreeList* AsTreeList();
-
-  const CXFA_Node* AsNode() const;
-  const CXFA_TreeList* AsTreeList() const;
+  CXFA_ThisProxy* AsThisProxy();
 
   CJX_Object* JSObject() { return m_pJSObject.get(); }
   const CJX_Object* JSObject() const { return m_pJSObject.get(); }
@@ -78,11 +82,9 @@
            m_elementType == XFA_Element::Subform ||
            m_elementType == XFA_Element::ExclGroup;
   }
-  void CreateWidgetAcc();
-  CXFA_WidgetAcc* GetWidgetAcc() { return acc_.get(); }
 
   XFA_Element GetElementType() const { return m_elementType; }
-  WideStringView GetClassName() const { return m_elementName; }
+  ByteStringView GetClassName() const { return m_elementName; }
   uint32_t GetClassHashCode() const { return m_elementNameHash; }
 
   WideString GetSOMExpression();
@@ -91,21 +93,20 @@
   CXFA_Object(CXFA_Document* pDocument,
               XFA_ObjectType objectType,
               XFA_Element eType,
-              const WideStringView& elementName,
               std::unique_ptr<CJX_Object> jsObject);
 
   UnownedPtr<CXFA_Document> const m_pDocument;
   const XFA_ObjectType m_objectType;
   const XFA_Element m_elementType;
-
+  const ByteStringView m_elementName;
   const uint32_t m_elementNameHash;
-  const WideStringView m_elementName;
-
   std::unique_ptr<CJX_Object> m_pJSObject;
-  std::unique_ptr<CXFA_WidgetAcc> acc_;
 };
 
+// Helper functions that permit nullptr arguments.
+CXFA_List* ToList(CXFA_Object* pObj);
 CXFA_Node* ToNode(CXFA_Object* pObj);
-const CXFA_Node* ToNode(const CXFA_Object* pObj);
+CXFA_TreeList* ToTreeList(CXFA_Object* pObj);
+CXFA_ThisProxy* ToThisProxy(CXFA_Object* pObj);
 
 #endif  // XFA_FXFA_PARSER_CXFA_OBJECT_H_
diff --git a/xfa/fxfa/parser/cxfa_occur.cpp b/xfa/fxfa/parser/cxfa_occur.cpp
index 7be3f70..ee68981 100644
--- a/xfa/fxfa/parser/cxfa_occur.cpp
+++ b/xfa/fxfa/parser/cxfa_occur.cpp
@@ -11,18 +11,18 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kOccurPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kOccurAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Max, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::Min, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Initial, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"occur";
+};
 
 }  // namespace
 
@@ -32,12 +32,11 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Occur,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kOccurPropertyData,
+                kOccurAttributeData,
                 pdfium::MakeUnique<CJX_Occur>(this)) {}
 
-CXFA_Occur::~CXFA_Occur() {}
+CXFA_Occur::~CXFA_Occur() = default;
 
 int32_t CXFA_Occur::GetMax() {
   Optional<int32_t> max = JSObject()->TryInteger(XFA_Attribute::Max, true);
diff --git a/xfa/fxfa/parser/cxfa_occur.h b/xfa/fxfa/parser/cxfa_occur.h
index 070c15c..58029a3 100644
--- a/xfa/fxfa/parser/cxfa_occur.h
+++ b/xfa/fxfa/parser/cxfa_occur.h
@@ -11,7 +11,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Occur : public CXFA_Node {
+class CXFA_Occur final : public CXFA_Node {
  public:
   static constexpr int32_t kDefaultMax = 1;
   static constexpr int32_t kDefaultMin = 1;
diff --git a/xfa/fxfa/parser/cxfa_oid.cpp b/xfa/fxfa/parser/cxfa_oid.cpp
index 7202e40..9032733 100644
--- a/xfa/fxfa/parser/cxfa_oid.cpp
+++ b/xfa/fxfa/parser/cxfa_oid.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_oid.h"
 
-#include "fxjs/xfa/cjx_oid.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kOidAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"oid";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::TextNode,
                 XFA_Element::Oid,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Oid>(this)) {}
+                {},
+                kOidAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Oid::~CXFA_Oid() {}
+CXFA_Oid::~CXFA_Oid() = default;
diff --git a/xfa/fxfa/parser/cxfa_oid.h b/xfa/fxfa/parser/cxfa_oid.h
index 45abf8b..c9ccb71 100644
--- a/xfa/fxfa/parser/cxfa_oid.h
+++ b/xfa/fxfa/parser/cxfa_oid.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Oid : public CXFA_Node {
+class CXFA_Oid final : public CXFA_Node {
  public:
   CXFA_Oid(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Oid() override;
diff --git a/xfa/fxfa/parser/cxfa_oids.cpp b/xfa/fxfa/parser/cxfa_oids.cpp
index 31d2a6d..64408ca 100644
--- a/xfa/fxfa/parser/cxfa_oids.cpp
+++ b/xfa/fxfa/parser/cxfa_oids.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_oids.h"
 
-#include "fxjs/xfa/cjx_oids.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kOidsAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"oids";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Oids,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Oids>(this)) {}
+                {},
+                kOidsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Oids::~CXFA_Oids() {}
+CXFA_Oids::~CXFA_Oids() = default;
diff --git a/xfa/fxfa/parser/cxfa_oids.h b/xfa/fxfa/parser/cxfa_oids.h
index cc8cd9c..f537601 100644
--- a/xfa/fxfa/parser/cxfa_oids.h
+++ b/xfa/fxfa/parser/cxfa_oids.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Oids : public CXFA_Node {
+class CXFA_Oids final : public CXFA_Node {
  public:
   CXFA_Oids(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Oids() override;
diff --git a/xfa/fxfa/parser/cxfa_openaction.cpp b/xfa/fxfa/parser/cxfa_openaction.cpp
index c50f433..b6264c2 100644
--- a/xfa/fxfa/parser/cxfa_openaction.cpp
+++ b/xfa/fxfa/parser/cxfa_openaction.cpp
@@ -6,17 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_openaction.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kOpenActionPropertyData[] = {
     {XFA_Element::Destination, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kOpenActionAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"openAction";
+};
 
 }  // namespace
 
@@ -26,8 +28,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::OpenAction,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kOpenActionPropertyData,
+                kOpenActionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_OpenAction::~CXFA_OpenAction() {}
+CXFA_OpenAction::~CXFA_OpenAction() = default;
diff --git a/xfa/fxfa/parser/cxfa_openaction.h b/xfa/fxfa/parser/cxfa_openaction.h
index d84eb29..2eff67c 100644
--- a/xfa/fxfa/parser/cxfa_openaction.h
+++ b/xfa/fxfa/parser/cxfa_openaction.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_OpenAction : public CXFA_Node {
+class CXFA_OpenAction final : public CXFA_Node {
  public:
   CXFA_OpenAction(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_OpenAction() override;
diff --git a/xfa/fxfa/parser/cxfa_operation.cpp b/xfa/fxfa/parser/cxfa_operation.cpp
index cc92d8c..dbd59ba 100644
--- a/xfa/fxfa/parser/cxfa_operation.cpp
+++ b/xfa/fxfa/parser/cxfa_operation.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_operation.h"
 
-#include "fxjs/xfa/cjx_operation.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kOperationAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Output, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Input, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"operation";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Operation,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Operation>(this)) {}
+                {},
+                kOperationAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Operation::~CXFA_Operation() {}
+CXFA_Operation::~CXFA_Operation() = default;
diff --git a/xfa/fxfa/parser/cxfa_operation.h b/xfa/fxfa/parser/cxfa_operation.h
index 454a354..4df63aa 100644
--- a/xfa/fxfa/parser/cxfa_operation.h
+++ b/xfa/fxfa/parser/cxfa_operation.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Operation : public CXFA_Node {
+class CXFA_Operation final : public CXFA_Node {
  public:
   CXFA_Operation(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Operation() override;
diff --git a/xfa/fxfa/parser/cxfa_output.cpp b/xfa/fxfa/parser/cxfa_output.cpp
index 124d7e5..c9e77de 100644
--- a/xfa/fxfa/parser/cxfa_output.cpp
+++ b/xfa/fxfa/parser/cxfa_output.cpp
@@ -6,18 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_output.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::To, 1, 0},
-                                                 {XFA_Element::Uri, 1, 0},
-                                                 {XFA_Element::Type, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kOutputPropertyData[] = {
+    {XFA_Element::To, 1, 0},
+    {XFA_Element::Uri, 1, 0},
+    {XFA_Element::Type, 1, 0},
+};
+
+const CXFA_Node::AttributeData kOutputAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"output";
+};
 
 }  // namespace
 
@@ -27,8 +30,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Output,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kOutputPropertyData,
+                kOutputAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Output::~CXFA_Output() {}
+CXFA_Output::~CXFA_Output() = default;
diff --git a/xfa/fxfa/parser/cxfa_output.h b/xfa/fxfa/parser/cxfa_output.h
index 2eb1391..45555be 100644
--- a/xfa/fxfa/parser/cxfa_output.h
+++ b/xfa/fxfa/parser/cxfa_output.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Output : public CXFA_Node {
+class CXFA_Output final : public CXFA_Node {
  public:
   CXFA_Output(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Output() override;
diff --git a/xfa/fxfa/parser/cxfa_outputbin.cpp b/xfa/fxfa/parser/cxfa_outputbin.cpp
index bb0ef2e..3b9174f 100644
--- a/xfa/fxfa/parser/cxfa_outputbin.cpp
+++ b/xfa/fxfa/parser/cxfa_outputbin.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_outputbin.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kOutputBinAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"outputBin";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::OutputBin,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kOutputBinAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_OutputBin::~CXFA_OutputBin() {}
+CXFA_OutputBin::~CXFA_OutputBin() = default;
diff --git a/xfa/fxfa/parser/cxfa_outputbin.h b/xfa/fxfa/parser/cxfa_outputbin.h
index 25a1ae0..7ab4540 100644
--- a/xfa/fxfa/parser/cxfa_outputbin.h
+++ b/xfa/fxfa/parser/cxfa_outputbin.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_OutputBin : public CXFA_Node {
+class CXFA_OutputBin final : public CXFA_Node {
  public:
   CXFA_OutputBin(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_OutputBin() override;
diff --git a/xfa/fxfa/parser/cxfa_outputxsl.cpp b/xfa/fxfa/parser/cxfa_outputxsl.cpp
index d56cc15..ab733bc 100644
--- a/xfa/fxfa/parser/cxfa_outputxsl.cpp
+++ b/xfa/fxfa/parser/cxfa_outputxsl.cpp
@@ -6,16 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_outputxsl.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Uri, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kOutputXSLPropertyData[] = {
+    {XFA_Element::Uri, 1, 0},
+};
+
+const CXFA_Node::AttributeData kOutputXSLAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"outputXSL";
+};
 
 }  // namespace
 
@@ -25,8 +28,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::OutputXSL,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kOutputXSLPropertyData,
+                kOutputXSLAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_OutputXSL::~CXFA_OutputXSL() {}
+CXFA_OutputXSL::~CXFA_OutputXSL() = default;
diff --git a/xfa/fxfa/parser/cxfa_outputxsl.h b/xfa/fxfa/parser/cxfa_outputxsl.h
index 4535523..361ea42 100644
--- a/xfa/fxfa/parser/cxfa_outputxsl.h
+++ b/xfa/fxfa/parser/cxfa_outputxsl.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_OutputXSL : public CXFA_Node {
+class CXFA_OutputXSL final : public CXFA_Node {
  public:
   CXFA_OutputXSL(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_OutputXSL() override;
diff --git a/xfa/fxfa/parser/cxfa_overflow.cpp b/xfa/fxfa/parser/cxfa_overflow.cpp
index 2589714..9ae517b 100644
--- a/xfa/fxfa/parser/cxfa_overflow.cpp
+++ b/xfa/fxfa/parser/cxfa_overflow.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_overflow.h"
 
-#include "fxjs/xfa/cjx_overflow.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kOverflowAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Trailer, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Target, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Leader, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"overflow";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Overflow,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Overflow>(this)) {}
+                {},
+                kOverflowAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Overflow::~CXFA_Overflow() {}
+CXFA_Overflow::~CXFA_Overflow() = default;
diff --git a/xfa/fxfa/parser/cxfa_overflow.h b/xfa/fxfa/parser/cxfa_overflow.h
index e6627ba..da2717a 100644
--- a/xfa/fxfa/parser/cxfa_overflow.h
+++ b/xfa/fxfa/parser/cxfa_overflow.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Overflow : public CXFA_Node {
+class CXFA_Overflow final : public CXFA_Node {
  public:
   CXFA_Overflow(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Overflow() override;
diff --git a/xfa/fxfa/parser/cxfa_overprint.cpp b/xfa/fxfa/parser/cxfa_overprint.cpp
index cf5ee4b..ee70ce5 100644
--- a/xfa/fxfa/parser/cxfa_overprint.cpp
+++ b/xfa/fxfa/parser/cxfa_overprint.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_overprint.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kOverprintAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"overprint";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Overprint,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kOverprintAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Overprint::~CXFA_Overprint() {}
+CXFA_Overprint::~CXFA_Overprint() = default;
diff --git a/xfa/fxfa/parser/cxfa_overprint.h b/xfa/fxfa/parser/cxfa_overprint.h
index a9ec241..e5e360d 100644
--- a/xfa/fxfa/parser/cxfa_overprint.h
+++ b/xfa/fxfa/parser/cxfa_overprint.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Overprint : public CXFA_Node {
+class CXFA_Overprint final : public CXFA_Node {
  public:
   CXFA_Overprint(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Overprint() override;
diff --git a/xfa/fxfa/parser/cxfa_packet.cpp b/xfa/fxfa/parser/cxfa_packet.cpp
index 6112271..96938ac 100644
--- a/xfa/fxfa/parser/cxfa_packet.cpp
+++ b/xfa/fxfa/parser/cxfa_packet.cpp
@@ -9,21 +9,14 @@
 #include "fxjs/xfa/cjx_packet.h"
 #include "third_party/base/ptr_util.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"packet";
-
-}  // namespace
-
 CXFA_Packet::CXFA_Packet(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
                 packet,
                 XFA_XDPPACKET_XDP,
                 XFA_ObjectType::NodeC,
                 XFA_Element::Packet,
-                nullptr,
-                nullptr,
-                kName,
+                {},
+                {},
                 pdfium::MakeUnique<CJX_Packet>(this)) {}
 
-CXFA_Packet::~CXFA_Packet() {}
+CXFA_Packet::~CXFA_Packet() = default;
diff --git a/xfa/fxfa/parser/cxfa_packet.h b/xfa/fxfa/parser/cxfa_packet.h
index 6bda61d..c94c74c 100644
--- a/xfa/fxfa/parser/cxfa_packet.h
+++ b/xfa/fxfa/parser/cxfa_packet.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Packet : public CXFA_Node {
+class CXFA_Packet final : public CXFA_Node {
  public:
   CXFA_Packet(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Packet() override;
diff --git a/xfa/fxfa/parser/cxfa_packets.cpp b/xfa/fxfa/parser/cxfa_packets.cpp
index 21b8b19..7981063 100644
--- a/xfa/fxfa/parser/cxfa_packets.cpp
+++ b/xfa/fxfa/parser/cxfa_packets.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_packets.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPacketsAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"packets";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Packets,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPacketsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Packets::~CXFA_Packets() {}
+CXFA_Packets::~CXFA_Packets() = default;
diff --git a/xfa/fxfa/parser/cxfa_packets.h b/xfa/fxfa/parser/cxfa_packets.h
index ebf29b6..5e0f408 100644
--- a/xfa/fxfa/parser/cxfa_packets.h
+++ b/xfa/fxfa/parser/cxfa_packets.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Packets : public CXFA_Node {
+class CXFA_Packets final : public CXFA_Node {
  public:
   CXFA_Packets(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Packets() override;
diff --git a/xfa/fxfa/parser/cxfa_pagearea.cpp b/xfa/fxfa/parser/cxfa_pagearea.cpp
index f82b344..cc48e89 100644
--- a/xfa/fxfa/parser/cxfa_pagearea.cpp
+++ b/xfa/fxfa/parser/cxfa_pagearea.cpp
@@ -6,33 +6,33 @@
 
 #include "xfa/fxfa/parser/cxfa_pagearea.h"
 
-#include "fxjs/xfa/cjx_pagearea.h"
+#include "fxjs/xfa/cjx_container.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Medium, 1, 0},
-                                                 {XFA_Element::Desc, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Occur, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kPageAreaPropertyData[] = {
+    {XFA_Element::Medium, 1, 0},
+    {XFA_Element::Desc, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Occur, 1, 0},
+};
+
+const CXFA_Node::AttributeData kPageAreaAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::PagePosition, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Any},
+     (void*)XFA_AttributeValue::Any},
     {XFA_Attribute::OddOrEven, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Any},
+     (void*)XFA_AttributeValue::Any},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::InitialNumber, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Numbered, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::BlankOrNotBlank, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Any},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pageArea";
+     (void*)XFA_AttributeValue::Any},
+};
 
 }  // namespace
 
@@ -42,9 +42,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::PageArea,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_PageArea>(this)) {}
+                kPageAreaPropertyData,
+                kPageAreaAttributeData,
+                pdfium::MakeUnique<CJX_Container>(this)) {}
 
-CXFA_PageArea::~CXFA_PageArea() {}
+CXFA_PageArea::~CXFA_PageArea() = default;
diff --git a/xfa/fxfa/parser/cxfa_pagearea.h b/xfa/fxfa/parser/cxfa_pagearea.h
index f3cab1e..8a8afaa 100644
--- a/xfa/fxfa/parser/cxfa_pagearea.h
+++ b/xfa/fxfa/parser/cxfa_pagearea.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PageArea : public CXFA_Node {
+class CXFA_PageArea final : public CXFA_Node {
  public:
   CXFA_PageArea(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PageArea() override;
diff --git a/xfa/fxfa/parser/cxfa_pageoffset.cpp b/xfa/fxfa/parser/cxfa_pageoffset.cpp
index 4671019..8054b4b 100644
--- a/xfa/fxfa/parser/cxfa_pageoffset.cpp
+++ b/xfa/fxfa/parser/cxfa_pageoffset.cpp
@@ -6,16 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_pageoffset.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPageOffsetAttributeData[] = {
     {XFA_Attribute::X, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Y, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pageOffset";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::PageOffset,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPageOffsetAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PageOffset::~CXFA_PageOffset() {}
+CXFA_PageOffset::~CXFA_PageOffset() = default;
diff --git a/xfa/fxfa/parser/cxfa_pageoffset.h b/xfa/fxfa/parser/cxfa_pageoffset.h
index a396c57..d090b11 100644
--- a/xfa/fxfa/parser/cxfa_pageoffset.h
+++ b/xfa/fxfa/parser/cxfa_pageoffset.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PageOffset : public CXFA_Node {
+class CXFA_PageOffset final : public CXFA_Node {
  public:
   CXFA_PageOffset(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PageOffset() override;
diff --git a/xfa/fxfa/parser/cxfa_pagerange.cpp b/xfa/fxfa/parser/cxfa_pagerange.cpp
index 5de7f5f..307b8dd 100644
--- a/xfa/fxfa/parser/cxfa_pagerange.cpp
+++ b/xfa/fxfa/parser/cxfa_pagerange.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_pagerange.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPageRangeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pageRange";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PageRange,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPageRangeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PageRange::~CXFA_PageRange() {}
+CXFA_PageRange::~CXFA_PageRange() = default;
diff --git a/xfa/fxfa/parser/cxfa_pagerange.h b/xfa/fxfa/parser/cxfa_pagerange.h
index 949276a..a0317b5 100644
--- a/xfa/fxfa/parser/cxfa_pagerange.h
+++ b/xfa/fxfa/parser/cxfa_pagerange.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PageRange : public CXFA_Node {
+class CXFA_PageRange final : public CXFA_Node {
  public:
   CXFA_PageRange(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PageRange() override;
diff --git a/xfa/fxfa/parser/cxfa_pageset.cpp b/xfa/fxfa/parser/cxfa_pageset.cpp
index 2fea618..1f08645 100644
--- a/xfa/fxfa/parser/cxfa_pageset.cpp
+++ b/xfa/fxfa/parser/cxfa_pageset.cpp
@@ -6,27 +6,27 @@
 
 #include "xfa/fxfa/parser/cxfa_pageset.h"
 
-#include "fxjs/xfa/cjx_pageset.h"
+#include "fxjs/xfa/cjx_container.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Occur, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kPageSetPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+    {XFA_Element::Occur, 1, 0},
+};
+
+const CXFA_Node::AttributeData kPageSetAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Relation, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::OrderedOccurrence},
+     (void*)XFA_AttributeValue::OrderedOccurrence},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DuplexImposition, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::LongEdge},
+     (void*)XFA_AttributeValue::LongEdge},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pageSet";
+};
 
 }  // namespace
 
@@ -36,9 +36,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::PageSet,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_PageSet>(this)) {}
+                kPageSetPropertyData,
+                kPageSetAttributeData,
+                pdfium::MakeUnique<CJX_Container>(this)) {}
 
-CXFA_PageSet::~CXFA_PageSet() {}
+CXFA_PageSet::~CXFA_PageSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_pageset.h b/xfa/fxfa/parser/cxfa_pageset.h
index 9cc3b17..da71581 100644
--- a/xfa/fxfa/parser/cxfa_pageset.h
+++ b/xfa/fxfa/parser/cxfa_pageset.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PageSet : public CXFA_Node {
+class CXFA_PageSet final : public CXFA_Node {
  public:
   CXFA_PageSet(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PageSet() override;
diff --git a/xfa/fxfa/parser/cxfa_pagination.cpp b/xfa/fxfa/parser/cxfa_pagination.cpp
index 99600ea..1c393a1 100644
--- a/xfa/fxfa/parser/cxfa_pagination.cpp
+++ b/xfa/fxfa/parser/cxfa_pagination.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_pagination.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPaginationAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pagination";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Pagination,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPaginationAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Pagination::~CXFA_Pagination() {}
+CXFA_Pagination::~CXFA_Pagination() = default;
diff --git a/xfa/fxfa/parser/cxfa_pagination.h b/xfa/fxfa/parser/cxfa_pagination.h
index a455ecb..9bbd820 100644
--- a/xfa/fxfa/parser/cxfa_pagination.h
+++ b/xfa/fxfa/parser/cxfa_pagination.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Pagination : public CXFA_Node {
+class CXFA_Pagination final : public CXFA_Node {
  public:
   CXFA_Pagination(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Pagination() override;
diff --git a/xfa/fxfa/parser/cxfa_paginationoverride.cpp b/xfa/fxfa/parser/cxfa_paginationoverride.cpp
index 8e8b6c6..0dfe7f2 100644
--- a/xfa/fxfa/parser/cxfa_paginationoverride.cpp
+++ b/xfa/fxfa/parser/cxfa_paginationoverride.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_paginationoverride.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPaginationOverrideAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"paginationOverride";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PaginationOverride,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPaginationOverrideAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PaginationOverride::~CXFA_PaginationOverride() {}
+CXFA_PaginationOverride::~CXFA_PaginationOverride() = default;
diff --git a/xfa/fxfa/parser/cxfa_paginationoverride.h b/xfa/fxfa/parser/cxfa_paginationoverride.h
index 9cc9528..4f0efba 100644
--- a/xfa/fxfa/parser/cxfa_paginationoverride.h
+++ b/xfa/fxfa/parser/cxfa_paginationoverride.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PaginationOverride : public CXFA_Node {
+class CXFA_PaginationOverride final : public CXFA_Node {
  public:
   CXFA_PaginationOverride(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PaginationOverride() override;
diff --git a/xfa/fxfa/parser/cxfa_para.cpp b/xfa/fxfa/parser/cxfa_para.cpp
index ad7190d..6907baf 100644
--- a/xfa/fxfa/parser/cxfa_para.cpp
+++ b/xfa/fxfa/parser/cxfa_para.cpp
@@ -6,19 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_para.h"
 
-#include "fxjs/xfa/cjx_para.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_measurement.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kParaPropertyData[] = {
     {XFA_Element::Hyphenation, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kParaAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Left},
+     (void*)XFA_AttributeValue::Left},
     {XFA_Attribute::TextIndent, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Widows, XFA_AttributeType::Integer, (void*)0},
@@ -28,16 +29,14 @@
     {XFA_Attribute::Preserve, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::SpaceBelow, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::VAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Top},
+     (void*)XFA_AttributeValue::Top},
     {XFA_Attribute::TabDefault, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TabStops, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Orphans, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::LineHeight, XFA_AttributeType::Measure, (void*)L"0pt"},
     {XFA_Attribute::SpaceAbove, XFA_AttributeType::Measure, (void*)L"0in"},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"para";
+};
 
 }  // namespace
 
@@ -47,47 +46,44 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Para,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Para>(this)) {}
+                kParaPropertyData,
+                kParaAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Para::~CXFA_Para() {}
+CXFA_Para::~CXFA_Para() = default;
 
-XFA_AttributeEnum CXFA_Para::GetHorizontalAlign() {
+XFA_AttributeValue CXFA_Para::GetHorizontalAlign() {
   return JSObject()
       ->TryEnum(XFA_Attribute::HAlign, true)
-      .value_or(XFA_AttributeEnum::Left);
+      .value_or(XFA_AttributeValue::Left);
 }
 
-XFA_AttributeEnum CXFA_Para::GetVerticalAlign() {
+XFA_AttributeValue CXFA_Para::GetVerticalAlign() {
   return JSObject()
       ->TryEnum(XFA_Attribute::VAlign, true)
-      .value_or(XFA_AttributeEnum::Top);
+      .value_or(XFA_AttributeValue::Top);
 }
 
 float CXFA_Para::GetLineHeight() {
-  return JSObject()->GetMeasure(XFA_Attribute::LineHeight).ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::LineHeight, XFA_Unit::Pt);
 }
 
 float CXFA_Para::GetMarginLeft() {
-  return JSObject()->GetMeasure(XFA_Attribute::MarginLeft).ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::MarginLeft, XFA_Unit::Pt);
 }
 
 float CXFA_Para::GetMarginRight() {
-  return JSObject()
-      ->GetMeasure(XFA_Attribute::MarginRight)
-      .ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::MarginRight, XFA_Unit::Pt);
 }
 
 float CXFA_Para::GetSpaceAbove() {
-  return JSObject()->GetMeasure(XFA_Attribute::SpaceAbove).ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::SpaceAbove, XFA_Unit::Pt);
 }
 
 float CXFA_Para::GetSpaceBelow() {
-  return JSObject()->GetMeasure(XFA_Attribute::SpaceBelow).ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::SpaceBelow, XFA_Unit::Pt);
 }
 
 float CXFA_Para::GetTextIndent() {
-  return JSObject()->GetMeasure(XFA_Attribute::TextIndent).ToUnit(XFA_Unit::Pt);
+  return JSObject()->GetMeasureInUnit(XFA_Attribute::TextIndent, XFA_Unit::Pt);
 }
diff --git a/xfa/fxfa/parser/cxfa_para.h b/xfa/fxfa/parser/cxfa_para.h
index afb48ce..735a199 100644
--- a/xfa/fxfa/parser/cxfa_para.h
+++ b/xfa/fxfa/parser/cxfa_para.h
@@ -9,13 +9,13 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Para : public CXFA_Node {
+class CXFA_Para final : public CXFA_Node {
  public:
   CXFA_Para(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Para() override;
 
-  XFA_AttributeEnum GetHorizontalAlign();
-  XFA_AttributeEnum GetVerticalAlign();
+  XFA_AttributeValue GetHorizontalAlign();
+  XFA_AttributeValue GetVerticalAlign();
   float GetLineHeight();
   float GetMarginLeft();
   float GetMarginRight();
diff --git a/xfa/fxfa/parser/cxfa_part.cpp b/xfa/fxfa/parser/cxfa_part.cpp
index d645571..d2acf53 100644
--- a/xfa/fxfa/parser/cxfa_part.cpp
+++ b/xfa/fxfa/parser/cxfa_part.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_part.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPartAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"part";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Part,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPartAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Part::~CXFA_Part() {}
+CXFA_Part::~CXFA_Part() = default;
diff --git a/xfa/fxfa/parser/cxfa_part.h b/xfa/fxfa/parser/cxfa_part.h
index 3db7639..99c2c59 100644
--- a/xfa/fxfa/parser/cxfa_part.h
+++ b/xfa/fxfa/parser/cxfa_part.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Part : public CXFA_Node {
+class CXFA_Part final : public CXFA_Node {
  public:
   CXFA_Part(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Part() override;
diff --git a/xfa/fxfa/parser/cxfa_password.cpp b/xfa/fxfa/parser/cxfa_password.cpp
index 3b3f719..057b818 100644
--- a/xfa/fxfa/parser/cxfa_password.cpp
+++ b/xfa/fxfa/parser/cxfa_password.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_password.h"
 
-#include "fxjs/xfa/cjx_password.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPasswordAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"password";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Password,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Password>(this)) {}
+                {},
+                kPasswordAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Password::~CXFA_Password() {}
+CXFA_Password::~CXFA_Password() = default;
diff --git a/xfa/fxfa/parser/cxfa_password.h b/xfa/fxfa/parser/cxfa_password.h
index 904f91f..a2cbca7 100644
--- a/xfa/fxfa/parser/cxfa_password.h
+++ b/xfa/fxfa/parser/cxfa_password.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Password : public CXFA_Node {
+class CXFA_Password final : public CXFA_Node {
  public:
   CXFA_Password(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Password() override;
diff --git a/xfa/fxfa/parser/cxfa_passwordedit.cpp b/xfa/fxfa/parser/cxfa_passwordedit.cpp
index e86cd13..f8f3c5e 100644
--- a/xfa/fxfa/parser/cxfa_passwordedit.cpp
+++ b/xfa/fxfa/parser/cxfa_passwordedit.cpp
@@ -6,25 +6,25 @@
 
 #include "xfa/fxfa/parser/cxfa_passwordedit.h"
 
-#include "fxjs/xfa/cjx_passwordedit.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Margin, 1, 0},
-                                                 {XFA_Element::Border, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kPasswordEditPropertyData[] = {
+    {XFA_Element::Margin, 1, 0},
+    {XFA_Element::Border, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kPasswordEditAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::PasswordChar, XFA_AttributeType::CData, (void*)L"*"},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HScrollPolicy, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"passwordEdit";
+     (void*)XFA_AttributeValue::Auto},
+};
 
 }  // namespace
 
@@ -34,8 +34,16 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::PasswordEdit,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kPasswordEditPropertyData,
+                kPasswordEditAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PasswordEdit::~CXFA_PasswordEdit() {}
+CXFA_PasswordEdit::~CXFA_PasswordEdit() = default;
+
+XFA_FFWidgetType CXFA_PasswordEdit::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kPasswordEdit;
+}
+
+WideString CXFA_PasswordEdit::GetPasswordChar() {
+  return JSObject()->GetCData(XFA_Attribute::PasswordChar);
+}
diff --git a/xfa/fxfa/parser/cxfa_passwordedit.h b/xfa/fxfa/parser/cxfa_passwordedit.h
index 2db05ba..fae812f 100644
--- a/xfa/fxfa/parser/cxfa_passwordedit.h
+++ b/xfa/fxfa/parser/cxfa_passwordedit.h
@@ -9,10 +9,13 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PasswordEdit : public CXFA_Node {
+class CXFA_PasswordEdit final : public CXFA_Node {
  public:
   CXFA_PasswordEdit(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PasswordEdit() override;
+
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
+  WideString GetPasswordChar();
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_PASSWORDEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_pattern.cpp b/xfa/fxfa/parser/cxfa_pattern.cpp
index 4cd32cc..e6f38aa 100644
--- a/xfa/fxfa/parser/cxfa_pattern.cpp
+++ b/xfa/fxfa/parser/cxfa_pattern.cpp
@@ -6,25 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_pattern.h"
 
-#include "fxjs/xfa/cjx_pattern.h"
+#include "core/fxge/render_defines.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
 #include "xfa/fxgraphics/cxfa_gepattern.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Color, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kPatternPropertyData[] = {
+    {XFA_Element::Color, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kPatternAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::CrossHatch},
+     (void*)XFA_AttributeValue::CrossHatch},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pattern";
+};
 
 }  // namespace
 
@@ -34,18 +35,17 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Pattern,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Pattern>(this)) {}
+                kPatternPropertyData,
+                kPatternAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Pattern::~CXFA_Pattern() {}
+CXFA_Pattern::~CXFA_Pattern() = default;
 
 CXFA_Color* CXFA_Pattern::GetColorIfExists() {
   return GetChild<CXFA_Color>(0, XFA_Element::Color, false);
 }
 
-XFA_AttributeEnum CXFA_Pattern::GetType() {
+XFA_AttributeValue CXFA_Pattern::GetType() {
   return JSObject()->GetEnum(XFA_Attribute::Type);
 }
 
@@ -59,19 +59,19 @@
 
   FX_HatchStyle iHatch = FX_HatchStyle::Cross;
   switch (GetType()) {
-    case XFA_AttributeEnum::CrossDiagonal:
+    case XFA_AttributeValue::CrossDiagonal:
       iHatch = FX_HatchStyle::DiagonalCross;
       break;
-    case XFA_AttributeEnum::DiagonalLeft:
+    case XFA_AttributeValue::DiagonalLeft:
       iHatch = FX_HatchStyle::ForwardDiagonal;
       break;
-    case XFA_AttributeEnum::DiagonalRight:
+    case XFA_AttributeValue::DiagonalRight:
       iHatch = FX_HatchStyle::BackwardDiagonal;
       break;
-    case XFA_AttributeEnum::Horizontal:
+    case XFA_AttributeValue::Horizontal:
       iHatch = FX_HatchStyle::Horizontal;
       break;
-    case XFA_AttributeEnum::Vertical:
+    case XFA_AttributeValue::Vertical:
       iHatch = FX_HatchStyle::Vertical;
       break;
     default:
diff --git a/xfa/fxfa/parser/cxfa_pattern.h b/xfa/fxfa/parser/cxfa_pattern.h
index 7533ac9..bf5c526 100644
--- a/xfa/fxfa/parser/cxfa_pattern.h
+++ b/xfa/fxfa/parser/cxfa_pattern.h
@@ -14,9 +14,10 @@
 class CXFA_Color;
 class CXFA_Graphics;
 
-class CXFA_Pattern : public CXFA_Node {
+class CXFA_Pattern final : public CXFA_Node {
  public:
-  static constexpr XFA_AttributeEnum kDefaultType = XFA_AttributeEnum::Unknown;
+  static constexpr XFA_AttributeValue kDefaultType =
+      XFA_AttributeValue::Unknown;
 
   CXFA_Pattern(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Pattern() override;
@@ -28,7 +29,7 @@
             const CFX_Matrix& matrix);
 
  private:
-  XFA_AttributeEnum GetType();
+  XFA_AttributeValue GetType();
   CXFA_Color* GetColorIfExists();
 };
 
diff --git a/xfa/fxfa/parser/cxfa_pcl.cpp b/xfa/fxfa/parser/cxfa_pcl.cpp
index 1f5e348..e55c80d 100644
--- a/xfa/fxfa/parser/cxfa_pcl.cpp
+++ b/xfa/fxfa/parser/cxfa_pcl.cpp
@@ -6,21 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_pcl.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kPclPropertyData[] = {
     {XFA_Element::FontInfo, 1, 0},   {XFA_Element::Jog, 1, 0},
     {XFA_Element::Xdc, 1, 0},        {XFA_Element::BatchOutput, 1, 0},
     {XFA_Element::PageOffset, 1, 0}, {XFA_Element::OutputBin, 1, 0},
     {XFA_Element::Staple, 1, 0},     {XFA_Element::MediumInfo, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kPclAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pcl";
+};
 
 }  // namespace
 
@@ -30,8 +32,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Pcl,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kPclPropertyData,
+                kPclAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Pcl::~CXFA_Pcl() {}
+CXFA_Pcl::~CXFA_Pcl() = default;
diff --git a/xfa/fxfa/parser/cxfa_pcl.h b/xfa/fxfa/parser/cxfa_pcl.h
index 9b78265..c9d94d6 100644
--- a/xfa/fxfa/parser/cxfa_pcl.h
+++ b/xfa/fxfa/parser/cxfa_pcl.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Pcl : public CXFA_Node {
+class CXFA_Pcl final : public CXFA_Node {
  public:
   CXFA_Pcl(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Pcl() override;
diff --git a/xfa/fxfa/parser/cxfa_pdf.cpp b/xfa/fxfa/parser/cxfa_pdf.cpp
index 800d393..499893c 100644
--- a/xfa/fxfa/parser/cxfa_pdf.cpp
+++ b/xfa/fxfa/parser/cxfa_pdf.cpp
@@ -6,9 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_pdf.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kPdfPropertyData[] = {
     {XFA_Element::AdobeExtensionLevel, 1, 0},
     {XFA_Element::FontInfo, 1, 0},
     {XFA_Element::Xdc, 1, 0},
@@ -28,14 +31,13 @@
     {XFA_Element::Creator, 1, 0},
     {XFA_Element::Linearized, 1, 0},
     {XFA_Element::Tagged, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kPdfAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pdf";
+};
 
 }  // namespace
 
@@ -45,8 +47,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Pdf,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kPdfPropertyData,
+                kPdfAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Pdf::~CXFA_Pdf() {}
+CXFA_Pdf::~CXFA_Pdf() = default;
diff --git a/xfa/fxfa/parser/cxfa_pdf.h b/xfa/fxfa/parser/cxfa_pdf.h
index d0925c8..00e4d38 100644
--- a/xfa/fxfa/parser/cxfa_pdf.h
+++ b/xfa/fxfa/parser/cxfa_pdf.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Pdf : public CXFA_Node {
+class CXFA_Pdf final : public CXFA_Node {
  public:
   CXFA_Pdf(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Pdf() override;
diff --git a/xfa/fxfa/parser/cxfa_pdfa.cpp b/xfa/fxfa/parser/cxfa_pdfa.cpp
index f41eb93..77d723f 100644
--- a/xfa/fxfa/parser/cxfa_pdfa.cpp
+++ b/xfa/fxfa/parser/cxfa_pdfa.cpp
@@ -6,20 +6,22 @@
 
 #include "xfa/fxfa/parser/cxfa_pdfa.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kPdfaPropertyData[] = {
     {XFA_Element::Amd, 1, 0},
     {XFA_Element::Part, 1, 0},
     {XFA_Element::IncludeXDPContent, 1, 0},
     {XFA_Element::Conformance, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kPdfaAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pdfa";
+};
 
 }  // namespace
 
@@ -29,8 +31,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Pdfa,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kPdfaPropertyData,
+                kPdfaAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Pdfa::~CXFA_Pdfa() {}
+CXFA_Pdfa::~CXFA_Pdfa() = default;
diff --git a/xfa/fxfa/parser/cxfa_pdfa.h b/xfa/fxfa/parser/cxfa_pdfa.h
index d0816a2..e43f025 100644
--- a/xfa/fxfa/parser/cxfa_pdfa.h
+++ b/xfa/fxfa/parser/cxfa_pdfa.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Pdfa : public CXFA_Node {
+class CXFA_Pdfa final : public CXFA_Node {
  public:
   CXFA_Pdfa(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Pdfa() override;
diff --git a/xfa/fxfa/parser/cxfa_permissions.cpp b/xfa/fxfa/parser/cxfa_permissions.cpp
index a56ac32..06e3a08 100644
--- a/xfa/fxfa/parser/cxfa_permissions.cpp
+++ b/xfa/fxfa/parser/cxfa_permissions.cpp
@@ -6,9 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_permissions.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kPermissionsPropertyData[] = {
     {XFA_Element::ModifyAnnots, 1, 0},
     {XFA_Element::ContentCopy, 1, 0},
     {XFA_Element::FormFieldFilling, 1, 0},
@@ -18,13 +21,12 @@
     {XFA_Element::PlaintextMetadata, 1, 0},
     {XFA_Element::PrintHighQuality, 1, 0},
     {XFA_Element::DocumentAssembly, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kPermissionsAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"permissions";
+};
 
 }  // namespace
 
@@ -34,8 +36,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Permissions,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kPermissionsPropertyData,
+                kPermissionsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Permissions::~CXFA_Permissions() {}
+CXFA_Permissions::~CXFA_Permissions() = default;
diff --git a/xfa/fxfa/parser/cxfa_permissions.h b/xfa/fxfa/parser/cxfa_permissions.h
index dca9018..79b3bf1 100644
--- a/xfa/fxfa/parser/cxfa_permissions.h
+++ b/xfa/fxfa/parser/cxfa_permissions.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Permissions : public CXFA_Node {
+class CXFA_Permissions final : public CXFA_Node {
  public:
   CXFA_Permissions(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Permissions() override;
diff --git a/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp b/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp
index b4e5ec2..3b2b7a3 100644
--- a/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp
+++ b/xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_picktraybypdfsize.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPickTrayByPDFSizeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"pickTrayByPDFSize";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PickTrayByPDFSize,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPickTrayByPDFSizeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PickTrayByPDFSize::~CXFA_PickTrayByPDFSize() {}
+CXFA_PickTrayByPDFSize::~CXFA_PickTrayByPDFSize() = default;
diff --git a/xfa/fxfa/parser/cxfa_picktraybypdfsize.h b/xfa/fxfa/parser/cxfa_picktraybypdfsize.h
index ef75e5c..3871552 100644
--- a/xfa/fxfa/parser/cxfa_picktraybypdfsize.h
+++ b/xfa/fxfa/parser/cxfa_picktraybypdfsize.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PickTrayByPDFSize : public CXFA_Node {
+class CXFA_PickTrayByPDFSize final : public CXFA_Node {
  public:
   CXFA_PickTrayByPDFSize(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PickTrayByPDFSize() override;
diff --git a/xfa/fxfa/parser/cxfa_picture.cpp b/xfa/fxfa/parser/cxfa_picture.cpp
index 4863a77..a1005ec 100644
--- a/xfa/fxfa/parser/cxfa_picture.cpp
+++ b/xfa/fxfa/parser/cxfa_picture.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_picture.h"
 
-#include "fxjs/xfa/cjx_picture.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPictureAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"picture";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
           (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
           XFA_ObjectType::ContentNode,
           XFA_Element::Picture,
-          nullptr,
-          kAttributeData,
-          kName,
-          pdfium::MakeUnique<CJX_Picture>(this)) {}
+          {},
+          kPictureAttributeData,
+          pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Picture::~CXFA_Picture() {}
+CXFA_Picture::~CXFA_Picture() = default;
diff --git a/xfa/fxfa/parser/cxfa_picture.h b/xfa/fxfa/parser/cxfa_picture.h
index 9fd2c55..304c7b9 100644
--- a/xfa/fxfa/parser/cxfa_picture.h
+++ b/xfa/fxfa/parser/cxfa_picture.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Picture : public CXFA_Node {
+class CXFA_Picture final : public CXFA_Node {
  public:
   CXFA_Picture(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Picture() override;
diff --git a/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp b/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp
index a5c51dd..75abb78 100644
--- a/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp
+++ b/xfa/fxfa/parser/cxfa_plaintextmetadata.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_plaintextmetadata.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPlaintextMetadataAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"plaintextMetadata";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PlaintextMetadata,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPlaintextMetadataAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PlaintextMetadata::~CXFA_PlaintextMetadata() {}
+CXFA_PlaintextMetadata::~CXFA_PlaintextMetadata() = default;
diff --git a/xfa/fxfa/parser/cxfa_plaintextmetadata.h b/xfa/fxfa/parser/cxfa_plaintextmetadata.h
index 03dce65..65e0ded 100644
--- a/xfa/fxfa/parser/cxfa_plaintextmetadata.h
+++ b/xfa/fxfa/parser/cxfa_plaintextmetadata.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PlaintextMetadata : public CXFA_Node {
+class CXFA_PlaintextMetadata final : public CXFA_Node {
  public:
   CXFA_PlaintextMetadata(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PlaintextMetadata() override;
diff --git a/xfa/fxfa/parser/cxfa_presence.cpp b/xfa/fxfa/parser/cxfa_presence.cpp
index f2389d9..99353a6 100644
--- a/xfa/fxfa/parser/cxfa_presence.cpp
+++ b/xfa/fxfa/parser/cxfa_presence.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_presence.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPresenceAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"presence";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Presence,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPresenceAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Presence::~CXFA_Presence() {}
+CXFA_Presence::~CXFA_Presence() = default;
diff --git a/xfa/fxfa/parser/cxfa_presence.h b/xfa/fxfa/parser/cxfa_presence.h
index a9b7766..1bd6e45 100644
--- a/xfa/fxfa/parser/cxfa_presence.h
+++ b/xfa/fxfa/parser/cxfa_presence.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Presence : public CXFA_Node {
+class CXFA_Presence final : public CXFA_Node {
  public:
   CXFA_Presence(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Presence() override;
diff --git a/xfa/fxfa/parser/cxfa_present.cpp b/xfa/fxfa/parser/cxfa_present.cpp
index 7552a79..e645f55 100644
--- a/xfa/fxfa/parser/cxfa_present.cpp
+++ b/xfa/fxfa/parser/cxfa_present.cpp
@@ -6,9 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_present.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kPresentPropertyData[] = {
     {XFA_Element::Xdp, 1, 0},
     {XFA_Element::Cache, 1, 0},
     {XFA_Element::Pagination, 1, 0},
@@ -23,13 +26,12 @@
     {XFA_Element::PaginationOverride, 1, 0},
     {XFA_Element::Destination, 1, 0},
     {XFA_Element::IncrementalMerge, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kPresentAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"present";
+};
 
 }  // namespace
 
@@ -39,8 +41,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Present,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kPresentPropertyData,
+                kPresentAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Present::~CXFA_Present() {}
+CXFA_Present::~CXFA_Present() = default;
diff --git a/xfa/fxfa/parser/cxfa_present.h b/xfa/fxfa/parser/cxfa_present.h
index 13b3c61..36db503 100644
--- a/xfa/fxfa/parser/cxfa_present.h
+++ b/xfa/fxfa/parser/cxfa_present.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Present : public CXFA_Node {
+class CXFA_Present final : public CXFA_Node {
  public:
   CXFA_Present(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Present() override;
diff --git a/xfa/fxfa/parser/cxfa_print.cpp b/xfa/fxfa/parser/cxfa_print.cpp
index f90186d..227af1e 100644
--- a/xfa/fxfa/parser/cxfa_print.cpp
+++ b/xfa/fxfa/parser/cxfa_print.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_print.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPrintAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"print";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Print,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPrintAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Print::~CXFA_Print() {}
+CXFA_Print::~CXFA_Print() = default;
diff --git a/xfa/fxfa/parser/cxfa_print.h b/xfa/fxfa/parser/cxfa_print.h
index 5f2513e..7f6fff9 100644
--- a/xfa/fxfa/parser/cxfa_print.h
+++ b/xfa/fxfa/parser/cxfa_print.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Print : public CXFA_Node {
+class CXFA_Print final : public CXFA_Node {
  public:
   CXFA_Print(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Print() override;
diff --git a/xfa/fxfa/parser/cxfa_printername.cpp b/xfa/fxfa/parser/cxfa_printername.cpp
index a5e6197..b434d26 100644
--- a/xfa/fxfa/parser/cxfa_printername.cpp
+++ b/xfa/fxfa/parser/cxfa_printername.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_printername.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPrinterNameAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"printerName";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PrinterName,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPrinterNameAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PrinterName::~CXFA_PrinterName() {}
+CXFA_PrinterName::~CXFA_PrinterName() = default;
diff --git a/xfa/fxfa/parser/cxfa_printername.h b/xfa/fxfa/parser/cxfa_printername.h
index 4f07885..a85d61e 100644
--- a/xfa/fxfa/parser/cxfa_printername.h
+++ b/xfa/fxfa/parser/cxfa_printername.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PrinterName : public CXFA_Node {
+class CXFA_PrinterName final : public CXFA_Node {
  public:
   CXFA_PrinterName(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PrinterName() override;
diff --git a/xfa/fxfa/parser/cxfa_printhighquality.cpp b/xfa/fxfa/parser/cxfa_printhighquality.cpp
index 8b1184f..38d16f5 100644
--- a/xfa/fxfa/parser/cxfa_printhighquality.cpp
+++ b/xfa/fxfa/parser/cxfa_printhighquality.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_printhighquality.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPrintHighQualityAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"printHighQuality";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PrintHighQuality,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPrintHighQualityAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PrintHighQuality::~CXFA_PrintHighQuality() {}
+CXFA_PrintHighQuality::~CXFA_PrintHighQuality() = default;
diff --git a/xfa/fxfa/parser/cxfa_printhighquality.h b/xfa/fxfa/parser/cxfa_printhighquality.h
index 6cd61125..6268489 100644
--- a/xfa/fxfa/parser/cxfa_printhighquality.h
+++ b/xfa/fxfa/parser/cxfa_printhighquality.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PrintHighQuality : public CXFA_Node {
+class CXFA_PrintHighQuality final : public CXFA_Node {
  public:
   CXFA_PrintHighQuality(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PrintHighQuality() override;
diff --git a/xfa/fxfa/parser/cxfa_printscaling.cpp b/xfa/fxfa/parser/cxfa_printscaling.cpp
index 8d68f68..a6641a3 100644
--- a/xfa/fxfa/parser/cxfa_printscaling.cpp
+++ b/xfa/fxfa/parser/cxfa_printscaling.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_printscaling.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kPrintScalingAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"printScaling";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::PrintScaling,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kPrintScalingAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PrintScaling::~CXFA_PrintScaling() {}
+CXFA_PrintScaling::~CXFA_PrintScaling() = default;
diff --git a/xfa/fxfa/parser/cxfa_printscaling.h b/xfa/fxfa/parser/cxfa_printscaling.h
index e21d427..76bf112 100644
--- a/xfa/fxfa/parser/cxfa_printscaling.h
+++ b/xfa/fxfa/parser/cxfa_printscaling.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PrintScaling : public CXFA_Node {
+class CXFA_PrintScaling final : public CXFA_Node {
  public:
   CXFA_PrintScaling(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PrintScaling() override;
diff --git a/xfa/fxfa/parser/cxfa_producer.cpp b/xfa/fxfa/parser/cxfa_producer.cpp
index 45253c7..07ed6e5 100644
--- a/xfa/fxfa/parser/cxfa_producer.cpp
+++ b/xfa/fxfa/parser/cxfa_producer.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_producer.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kProducerAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"producer";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Producer,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kProducerAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Producer::~CXFA_Producer() {}
+CXFA_Producer::~CXFA_Producer() = default;
diff --git a/xfa/fxfa/parser/cxfa_producer.h b/xfa/fxfa/parser/cxfa_producer.h
index 584826e..c40a334 100644
--- a/xfa/fxfa/parser/cxfa_producer.h
+++ b/xfa/fxfa/parser/cxfa_producer.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Producer : public CXFA_Node {
+class CXFA_Producer final : public CXFA_Node {
  public:
   CXFA_Producer(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Producer() override;
diff --git a/xfa/fxfa/parser/cxfa_proto.cpp b/xfa/fxfa/parser/cxfa_proto.cpp
index 6d103e7..c59ae13 100644
--- a/xfa/fxfa/parser/cxfa_proto.cpp
+++ b/xfa/fxfa/parser/cxfa_proto.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_proto.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"proto";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_Proto::CXFA_Proto(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -18,8 +15,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Proto,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Proto::~CXFA_Proto() {}
+CXFA_Proto::~CXFA_Proto() = default;
diff --git a/xfa/fxfa/parser/cxfa_proto.h b/xfa/fxfa/parser/cxfa_proto.h
index 8e1d84a..5af7ba2 100644
--- a/xfa/fxfa/parser/cxfa_proto.h
+++ b/xfa/fxfa/parser/cxfa_proto.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Proto : public CXFA_Node {
+class CXFA_Proto final : public CXFA_Node {
  public:
   CXFA_Proto(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Proto() override;
diff --git a/xfa/fxfa/parser/cxfa_ps.cpp b/xfa/fxfa/parser/cxfa_ps.cpp
index 1b2f532..35e5da3 100644
--- a/xfa/fxfa/parser/cxfa_ps.cpp
+++ b/xfa/fxfa/parser/cxfa_ps.cpp
@@ -6,21 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_ps.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kPsPropertyData[] = {
     {XFA_Element::FontInfo, 1, 0},  {XFA_Element::Jog, 1, 0},
     {XFA_Element::Xdc, 1, 0},       {XFA_Element::BatchOutput, 1, 0},
     {XFA_Element::OutputBin, 1, 0}, {XFA_Element::Compress, 1, 0},
     {XFA_Element::Staple, 1, 0},    {XFA_Element::MediumInfo, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kPsAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"ps";
+};
 
 }  // namespace
 
@@ -30,8 +32,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Ps,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kPsPropertyData,
+                kPsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Ps::~CXFA_Ps() {}
+CXFA_Ps::~CXFA_Ps() = default;
diff --git a/xfa/fxfa/parser/cxfa_ps.h b/xfa/fxfa/parser/cxfa_ps.h
index 5ef7e82..f51c510 100644
--- a/xfa/fxfa/parser/cxfa_ps.h
+++ b/xfa/fxfa/parser/cxfa_ps.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Ps : public CXFA_Node {
+class CXFA_Ps final : public CXFA_Node {
  public:
   CXFA_Ps(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Ps() override;
diff --git a/xfa/fxfa/parser/cxfa_psmap.cpp b/xfa/fxfa/parser/cxfa_psmap.cpp
index 0c0b122..12ca00b 100644
--- a/xfa/fxfa/parser/cxfa_psmap.cpp
+++ b/xfa/fxfa/parser/cxfa_psmap.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_psmap.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"psMap";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_PsMap::CXFA_PsMap(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -18,8 +15,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::PsMap,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_PsMap::~CXFA_PsMap() {}
+CXFA_PsMap::~CXFA_PsMap() = default;
diff --git a/xfa/fxfa/parser/cxfa_psmap.h b/xfa/fxfa/parser/cxfa_psmap.h
index 8297aaa..802e123 100644
--- a/xfa/fxfa/parser/cxfa_psmap.h
+++ b/xfa/fxfa/parser/cxfa_psmap.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_PsMap : public CXFA_Node {
+class CXFA_PsMap final : public CXFA_Node {
  public:
   CXFA_PsMap(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_PsMap() override;
diff --git a/xfa/fxfa/parser/cxfa_query.cpp b/xfa/fxfa/parser/cxfa_query.cpp
index 5e18565..7f2db0b 100644
--- a/xfa/fxfa/parser/cxfa_query.cpp
+++ b/xfa/fxfa/parser/cxfa_query.cpp
@@ -6,24 +6,24 @@
 
 #include "xfa/fxfa/parser/cxfa_query.h"
 
-#include "fxjs/xfa/cjx_query.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::RecordSet, 1, 0},
-                                                 {XFA_Element::Select, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kQueryPropertyData[] = {
+    {XFA_Element::RecordSet, 1, 0},
+    {XFA_Element::Select, 1, 0},
+};
+
+const CXFA_Node::AttributeData kQueryAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::CommandType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Unknown},
+     (void*)XFA_AttributeValue::Unknown},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"query";
+};
 
 }  // namespace
 
@@ -33,9 +33,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Query,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Query>(this)) {}
+                kQueryPropertyData,
+                kQueryAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Query::~CXFA_Query() {}
+CXFA_Query::~CXFA_Query() = default;
diff --git a/xfa/fxfa/parser/cxfa_query.h b/xfa/fxfa/parser/cxfa_query.h
index 548e7fc..7d62c68 100644
--- a/xfa/fxfa/parser/cxfa_query.h
+++ b/xfa/fxfa/parser/cxfa_query.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Query : public CXFA_Node {
+class CXFA_Query final : public CXFA_Node {
  public:
   CXFA_Query(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Query() override;
diff --git a/xfa/fxfa/parser/cxfa_radial.cpp b/xfa/fxfa/parser/cxfa_radial.cpp
index 3d024e7..4eb944f 100644
--- a/xfa/fxfa/parser/cxfa_radial.cpp
+++ b/xfa/fxfa/parser/cxfa_radial.cpp
@@ -8,25 +8,26 @@
 
 #include <utility>
 
-#include "fxjs/xfa/cjx_radial.h"
+#include "core/fxge/render_defines.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
 #include "xfa/fxgraphics/cxfa_geshading.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Color, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kRadialPropertyData[] = {
+    {XFA_Element::Color, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kRadialAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::ToEdge},
+     (void*)XFA_AttributeValue::ToEdge},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"radial";
+};
 
 }  // namespace
 
@@ -36,17 +37,15 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Radial,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Radial>(this)) {}
+                kRadialPropertyData,
+                kRadialAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Radial::~CXFA_Radial() {}
+CXFA_Radial::~CXFA_Radial() = default;
 
 bool CXFA_Radial::IsToEdge() {
-  return JSObject()
-             ->TryEnum(XFA_Attribute::Type, true)
-             .value_or(XFA_AttributeEnum::ToEdge) == XFA_AttributeEnum::ToEdge;
+  auto value = JSObject()->TryEnum(XFA_Attribute::Type, true);
+  return !value.has_value() || value.value() == XFA_AttributeValue::ToEdge;
 }
 
 CXFA_Color* CXFA_Radial::GetColorIfExists() {
diff --git a/xfa/fxfa/parser/cxfa_radial.h b/xfa/fxfa/parser/cxfa_radial.h
index b7ce95c..8fb30b5 100644
--- a/xfa/fxfa/parser/cxfa_radial.h
+++ b/xfa/fxfa/parser/cxfa_radial.h
@@ -14,7 +14,7 @@
 class CXFA_Color;
 class CXFA_Graphics;
 
-class CXFA_Radial : public CXFA_Node {
+class CXFA_Radial final : public CXFA_Node {
  public:
   CXFA_Radial(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Radial() override;
diff --git a/xfa/fxfa/parser/cxfa_range.cpp b/xfa/fxfa/parser/cxfa_range.cpp
index bd5f870..9f62b1d 100644
--- a/xfa/fxfa/parser/cxfa_range.cpp
+++ b/xfa/fxfa/parser/cxfa_range.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_range.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRangeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"range";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Range,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kRangeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Range::~CXFA_Range() {}
+CXFA_Range::~CXFA_Range() = default;
diff --git a/xfa/fxfa/parser/cxfa_range.h b/xfa/fxfa/parser/cxfa_range.h
index 23c8d1f..5d8920d 100644
--- a/xfa/fxfa/parser/cxfa_range.h
+++ b/xfa/fxfa/parser/cxfa_range.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Range : public CXFA_Node {
+class CXFA_Range final : public CXFA_Node {
  public:
   CXFA_Range(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Range() override;
diff --git a/xfa/fxfa/parser/cxfa_reason.cpp b/xfa/fxfa/parser/cxfa_reason.cpp
index 625ede9..8af61ba 100644
--- a/xfa/fxfa/parser/cxfa_reason.cpp
+++ b/xfa/fxfa/parser/cxfa_reason.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_reason.h"
 
-#include "fxjs/xfa/cjx_reason.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kReasonAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"reason";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::TextNode,
                 XFA_Element::Reason,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Reason>(this)) {}
+                {},
+                kReasonAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Reason::~CXFA_Reason() {}
+CXFA_Reason::~CXFA_Reason() = default;
diff --git a/xfa/fxfa/parser/cxfa_reason.h b/xfa/fxfa/parser/cxfa_reason.h
index 451fd82..1475637 100644
--- a/xfa/fxfa/parser/cxfa_reason.h
+++ b/xfa/fxfa/parser/cxfa_reason.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Reason : public CXFA_Node {
+class CXFA_Reason final : public CXFA_Node {
  public:
   CXFA_Reason(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Reason() override;
diff --git a/xfa/fxfa/parser/cxfa_reasons.cpp b/xfa/fxfa/parser/cxfa_reasons.cpp
index bd3d761..704f411 100644
--- a/xfa/fxfa/parser/cxfa_reasons.cpp
+++ b/xfa/fxfa/parser/cxfa_reasons.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_reasons.h"
 
-#include "fxjs/xfa/cjx_reasons.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kReasonsAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"reasons";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Reasons,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Reasons>(this)) {}
+                {},
+                kReasonsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Reasons::~CXFA_Reasons() {}
+CXFA_Reasons::~CXFA_Reasons() = default;
diff --git a/xfa/fxfa/parser/cxfa_reasons.h b/xfa/fxfa/parser/cxfa_reasons.h
index 57e9e09..673a9b2 100644
--- a/xfa/fxfa/parser/cxfa_reasons.h
+++ b/xfa/fxfa/parser/cxfa_reasons.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Reasons : public CXFA_Node {
+class CXFA_Reasons final : public CXFA_Node {
  public:
   CXFA_Reasons(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Reasons() override;
diff --git a/xfa/fxfa/parser/cxfa_record.cpp b/xfa/fxfa/parser/cxfa_record.cpp
index 5c5ded1..5c10f05 100644
--- a/xfa/fxfa/parser/cxfa_record.cpp
+++ b/xfa/fxfa/parser/cxfa_record.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_record.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRecordAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"record";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Record,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kRecordAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Record::~CXFA_Record() {}
+CXFA_Record::~CXFA_Record() = default;
diff --git a/xfa/fxfa/parser/cxfa_record.h b/xfa/fxfa/parser/cxfa_record.h
index bd2622d..de0d0c4 100644
--- a/xfa/fxfa/parser/cxfa_record.h
+++ b/xfa/fxfa/parser/cxfa_record.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Record : public CXFA_Node {
+class CXFA_Record final : public CXFA_Node {
  public:
   CXFA_Record(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Record() override;
diff --git a/xfa/fxfa/parser/cxfa_recordset.cpp b/xfa/fxfa/parser/cxfa_recordset.cpp
index a90c589..ad77695 100644
--- a/xfa/fxfa/parser/cxfa_recordset.cpp
+++ b/xfa/fxfa/parser/cxfa_recordset.cpp
@@ -6,30 +6,28 @@
 
 #include "xfa/fxfa/parser/cxfa_recordset.h"
 
-#include "fxjs/xfa/cjx_recordset.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRecordSetAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Max, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::EofAction, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::MoveLast},
+     (void*)XFA_AttributeValue::MoveLast},
     {XFA_Attribute::CursorType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::ForwardOnly},
+     (void*)XFA_AttributeValue::ForwardOnly},
     {XFA_Attribute::LockType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::ReadOnly},
+     (void*)XFA_AttributeValue::ReadOnly},
     {XFA_Attribute::BofAction, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::MoveFirst},
+     (void*)XFA_AttributeValue::MoveFirst},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::CursorLocation, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Client},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"recordSet";
+     (void*)XFA_AttributeValue::Client},
+};
 
 }  // namespace
 
@@ -39,9 +37,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::RecordSet,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_RecordSet>(this)) {}
+                {},
+                kRecordSetAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_RecordSet::~CXFA_RecordSet() {}
+CXFA_RecordSet::~CXFA_RecordSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_recordset.h b/xfa/fxfa/parser/cxfa_recordset.h
index c623516..a9895bd 100644
--- a/xfa/fxfa/parser/cxfa_recordset.h
+++ b/xfa/fxfa/parser/cxfa_recordset.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_RecordSet : public CXFA_Node {
+class CXFA_RecordSet final : public CXFA_Node {
  public:
   CXFA_RecordSet(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_RecordSet() override;
diff --git a/xfa/fxfa/parser/cxfa_rectangle.cpp b/xfa/fxfa/parser/cxfa_rectangle.cpp
index ac98df0..632141d 100644
--- a/xfa/fxfa/parser/cxfa_rectangle.cpp
+++ b/xfa/fxfa/parser/cxfa_rectangle.cpp
@@ -8,26 +8,27 @@
 
 #include <utility>
 
-#include "fxjs/xfa/cjx_rectangle.h"
+#include "core/fxge/render_defines.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_corner.h"
 #include "xfa/fxfa/parser/cxfa_stroke.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Edge, 4, 0},
-                                                 {XFA_Element::Corner, 4, 0},
-                                                 {XFA_Element::Fill, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kRectanglePropertyData[] = {
+    {XFA_Element::Edge, 4, 0},
+    {XFA_Element::Corner, 4, 0},
+    {XFA_Element::Fill, 1, 0},
+};
+
+const CXFA_Node::AttributeData kRectangleAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Hand, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Even},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"rectangle";
+     (void*)XFA_AttributeValue::Even},
+};
 
 }  // namespace
 
@@ -37,19 +38,17 @@
                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                XFA_ObjectType::Node,
                XFA_Element::Rectangle,
-               kPropertyData,
-               kAttributeData,
-               kName,
-               pdfium::MakeUnique<CJX_Rectangle>(this)) {}
+               kRectanglePropertyData,
+               kRectangleAttributeData,
+               pdfium::MakeUnique<CJX_Node>(this)) {}
 
 CXFA_Rectangle::CXFA_Rectangle(CXFA_Document* pDoc,
                                XFA_PacketType ePacket,
                                uint32_t validPackets,
                                XFA_ObjectType oType,
                                XFA_Element eType,
-                               const PropertyData* properties,
-                               const AttributeData* attributes,
-                               const WideStringView& elementName,
+                               pdfium::span<const PropertyData> properties,
+                               pdfium::span<const AttributeData> attributes,
                                std::unique_ptr<CJX_Object> js_node)
     : CXFA_Box(pDoc,
                ePacket,
@@ -58,7 +57,6 @@
                eType,
                properties,
                attributes,
-               elementName,
                std::move(js_node)) {}
 
 CXFA_Rectangle::~CXFA_Rectangle() {}
@@ -92,7 +90,7 @@
       stroke1 = strokes[0];
       if (stroke1->IsInverted())
         bSameStyles = false;
-      if (stroke1->GetJoinType() != XFA_AttributeEnum::Square)
+      if (stroke1->GetJoinType() != XFA_AttributeValue::Square)
         bSameStyles = false;
     }
   }
@@ -110,12 +108,12 @@
     float nx = 1.0f;
     float ny = 1.0f;
     CFX_PointF cp1, cp2;
-    auto* corner1 = static_cast<CXFA_Corner*>(strokes[i]);
-    auto* corner2 = static_cast<CXFA_Corner*>(strokes[(i + 2) % 8]);
+    CXFA_Stroke* corner1 = strokes[i];
+    CXFA_Stroke* corner2 = strokes[(i + 2) % 8];
     float fRadius1 = corner1->GetRadius();
     float fRadius2 = corner2->GetRadius();
     bool bInverted = corner1->IsInverted();
-    bool bRound = corner1->GetJoinType() == XFA_AttributeEnum::Round;
+    bool bRound = corner1->GetJoinType() == XFA_AttributeValue::Round;
     if (bRound) {
       sy = FX_PI / 2;
     }
@@ -214,36 +212,36 @@
   for (int32_t i = 1; i < 8; i += 2) {
     float fThickness = std::fmax(0.0, strokes[i]->GetThickness());
     float fHalf = fThickness / 2;
-    XFA_AttributeEnum iHand = GetHand();
+    XFA_AttributeValue iHand = GetHand();
     switch (i) {
       case 1:
-        if (iHand == XFA_AttributeEnum::Left) {
+        if (iHand == XFA_AttributeValue::Left) {
           rtWidget.top -= fHalf;
           rtWidget.height += fHalf;
-        } else if (iHand == XFA_AttributeEnum::Right) {
+        } else if (iHand == XFA_AttributeValue::Right) {
           rtWidget.top += fHalf;
           rtWidget.height -= fHalf;
         }
         break;
       case 3:
-        if (iHand == XFA_AttributeEnum::Left) {
+        if (iHand == XFA_AttributeValue::Left) {
           rtWidget.width += fHalf;
-        } else if (iHand == XFA_AttributeEnum::Right) {
+        } else if (iHand == XFA_AttributeValue::Right) {
           rtWidget.width -= fHalf;
         }
         break;
       case 5:
-        if (iHand == XFA_AttributeEnum::Left) {
+        if (iHand == XFA_AttributeValue::Left) {
           rtWidget.height += fHalf;
-        } else if (iHand == XFA_AttributeEnum::Right) {
+        } else if (iHand == XFA_AttributeValue::Right) {
           rtWidget.height -= fHalf;
         }
         break;
       case 7:
-        if (iHand == XFA_AttributeEnum::Left) {
+        if (iHand == XFA_AttributeValue::Left) {
           rtWidget.left -= fHalf;
           rtWidget.width += fHalf;
-        } else if (iHand == XFA_AttributeEnum::Right) {
+        } else if (iHand == XFA_AttributeValue::Right) {
           rtWidget.left += fHalf;
           rtWidget.width -= fHalf;
         }
@@ -259,23 +257,23 @@
                             const CFX_Matrix& matrix) {
   bool bVisible;
   float fThickness;
-  XFA_AttributeEnum i3DType;
+  XFA_AttributeValue i3DType;
   std::tie(i3DType, bVisible, fThickness) = Get3DStyle();
-  if (i3DType != XFA_AttributeEnum::Unknown) {
+  if (i3DType != XFA_AttributeValue::Unknown) {
     if (!bVisible || fThickness < 0.001f)
       return;
 
     switch (i3DType) {
-      case XFA_AttributeEnum::Lowered:
+      case XFA_AttributeValue::Lowered:
         StrokeLowered(pGS, rtWidget, fThickness, matrix);
         break;
-      case XFA_AttributeEnum::Raised:
+      case XFA_AttributeValue::Raised:
         StrokeRaised(pGS, rtWidget, fThickness, matrix);
         break;
-      case XFA_AttributeEnum::Etched:
+      case XFA_AttributeValue::Etched:
         StrokeEtched(pGS, rtWidget, fThickness, matrix);
         break;
-      case XFA_AttributeEnum::Embossed:
+      case XFA_AttributeValue::Embossed:
         StrokeEmbossed(pGS, rtWidget, fThickness, matrix);
         break;
       default:
@@ -312,7 +310,7 @@
       stroke1 = strokes[0];
       if (stroke1->IsInverted())
         bSameStyles = false;
-      if (stroke1->GetJoinType() != XFA_AttributeEnum::Square)
+      if (stroke1->GetJoinType() != XFA_AttributeValue::Square)
         bSameStyles = false;
     }
   }
@@ -445,17 +443,18 @@
                              int32_t nIndex,
                              bool bStart,
                              bool bCorner) {
-  ASSERT(nIndex >= 0 && nIndex < 8);
+  ASSERT(nIndex >= 0);
+  ASSERT(nIndex < 8);
 
   int32_t n = (nIndex & 1) ? nIndex - 1 : nIndex;
-  auto* corner1 = static_cast<CXFA_Corner*>(strokes[n]);
-  auto* corner2 = static_cast<CXFA_Corner*>(strokes[(n + 2) % 8]);
+  CXFA_Stroke* corner1 = strokes[n];
+  CXFA_Stroke* corner2 = strokes[(n + 2) % 8];
   float fRadius1 = bCorner ? corner1->GetRadius() : 0.0f;
   float fRadius2 = bCorner ? corner2->GetRadius() : 0.0f;
   bool bInverted = corner1->IsInverted();
   float offsetY = 0.0f;
   float offsetX = 0.0f;
-  bool bRound = corner1->GetJoinType() == XFA_AttributeEnum::Round;
+  bool bRound = corner1->GetJoinType() == XFA_AttributeValue::Round;
   float halfAfter = 0.0f;
   float halfBefore = 0.0f;
 
diff --git a/xfa/fxfa/parser/cxfa_rectangle.h b/xfa/fxfa/parser/cxfa_rectangle.h
index b117e66..6d17471 100644
--- a/xfa/fxfa/parser/cxfa_rectangle.h
+++ b/xfa/fxfa/parser/cxfa_rectangle.h
@@ -32,9 +32,8 @@
                  uint32_t validPackets,
                  XFA_ObjectType oType,
                  XFA_Element eType,
-                 const PropertyData* properties,
-                 const AttributeData* attributes,
-                 const WideStringView& elementName,
+                 pdfium::span<const PropertyData> properties,
+                 pdfium::span<const AttributeData> attributes,
                  std::unique_ptr<CJX_Object> js_node);
 
  private:
diff --git a/xfa/fxfa/parser/cxfa_ref.cpp b/xfa/fxfa/parser/cxfa_ref.cpp
index 0d7df44..fc4f59a 100644
--- a/xfa/fxfa/parser/cxfa_ref.cpp
+++ b/xfa/fxfa/parser/cxfa_ref.cpp
@@ -6,18 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_ref.h"
 
-#include "fxjs/xfa/cjx_ref.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRefAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"ref";
+};
 
 }  // namespace
 
@@ -27,9 +25,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::TextNode,
                 XFA_Element::Ref,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Ref>(this)) {}
+                {},
+                kRefAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Ref::~CXFA_Ref() {}
+CXFA_Ref::~CXFA_Ref() = default;
diff --git a/xfa/fxfa/parser/cxfa_ref.h b/xfa/fxfa/parser/cxfa_ref.h
index 630510f..5ab8e00 100644
--- a/xfa/fxfa/parser/cxfa_ref.h
+++ b/xfa/fxfa/parser/cxfa_ref.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Ref : public CXFA_Node {
+class CXFA_Ref final : public CXFA_Node {
  public:
   CXFA_Ref(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Ref() override;
diff --git a/xfa/fxfa/parser/cxfa_relevant.cpp b/xfa/fxfa/parser/cxfa_relevant.cpp
index c67ff1e..f6b8a52 100644
--- a/xfa/fxfa/parser/cxfa_relevant.cpp
+++ b/xfa/fxfa/parser/cxfa_relevant.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_relevant.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRelevantAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"relevant";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Relevant,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kRelevantAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Relevant::~CXFA_Relevant() {}
+CXFA_Relevant::~CXFA_Relevant() = default;
diff --git a/xfa/fxfa/parser/cxfa_relevant.h b/xfa/fxfa/parser/cxfa_relevant.h
index 2018c95..03283c3 100644
--- a/xfa/fxfa/parser/cxfa_relevant.h
+++ b/xfa/fxfa/parser/cxfa_relevant.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Relevant : public CXFA_Node {
+class CXFA_Relevant final : public CXFA_Node {
  public:
   CXFA_Relevant(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Relevant() override;
diff --git a/xfa/fxfa/parser/cxfa_rename.cpp b/xfa/fxfa/parser/cxfa_rename.cpp
index b0adbd7..f444554 100644
--- a/xfa/fxfa/parser/cxfa_rename.cpp
+++ b/xfa/fxfa/parser/cxfa_rename.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_rename.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRenameAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"rename";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Rename,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kRenameAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Rename::~CXFA_Rename() {}
+CXFA_Rename::~CXFA_Rename() = default;
diff --git a/xfa/fxfa/parser/cxfa_rename.h b/xfa/fxfa/parser/cxfa_rename.h
index 665c852..f50d852 100644
--- a/xfa/fxfa/parser/cxfa_rename.h
+++ b/xfa/fxfa/parser/cxfa_rename.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Rename : public CXFA_Node {
+class CXFA_Rename final : public CXFA_Node {
  public:
   CXFA_Rename(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Rename() override;
diff --git a/xfa/fxfa/parser/cxfa_renderpolicy.cpp b/xfa/fxfa/parser/cxfa_renderpolicy.cpp
index a8abdb3..6497eec 100644
--- a/xfa/fxfa/parser/cxfa_renderpolicy.cpp
+++ b/xfa/fxfa/parser/cxfa_renderpolicy.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_renderpolicy.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRenderPolicyAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"renderPolicy";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::RenderPolicy,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kRenderPolicyAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_RenderPolicy::~CXFA_RenderPolicy() {}
+CXFA_RenderPolicy::~CXFA_RenderPolicy() = default;
diff --git a/xfa/fxfa/parser/cxfa_renderpolicy.h b/xfa/fxfa/parser/cxfa_renderpolicy.h
index 24ddf46..4947289 100644
--- a/xfa/fxfa/parser/cxfa_renderpolicy.h
+++ b/xfa/fxfa/parser/cxfa_renderpolicy.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_RenderPolicy : public CXFA_Node {
+class CXFA_RenderPolicy final : public CXFA_Node {
  public:
   CXFA_RenderPolicy(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_RenderPolicy() override;
diff --git a/xfa/fxfa/parser/cxfa_rootelement.cpp b/xfa/fxfa/parser/cxfa_rootelement.cpp
index a9b8631..46d5aea 100644
--- a/xfa/fxfa/parser/cxfa_rootelement.cpp
+++ b/xfa/fxfa/parser/cxfa_rootelement.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_rootelement.h"
 
-#include "fxjs/xfa/cjx_rootelement.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRootElementAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"rootElement";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::RootElement,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_RootElement>(this)) {}
+                {},
+                kRootElementAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_RootElement::~CXFA_RootElement() {}
+CXFA_RootElement::~CXFA_RootElement() = default;
diff --git a/xfa/fxfa/parser/cxfa_rootelement.h b/xfa/fxfa/parser/cxfa_rootelement.h
index a995e8c..4a9218e 100644
--- a/xfa/fxfa/parser/cxfa_rootelement.h
+++ b/xfa/fxfa/parser/cxfa_rootelement.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_RootElement : public CXFA_Node {
+class CXFA_RootElement final : public CXFA_Node {
  public:
   CXFA_RootElement(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_RootElement() override;
diff --git a/xfa/fxfa/parser/cxfa_runscripts.cpp b/xfa/fxfa/parser/cxfa_runscripts.cpp
index 06c0d52..dbc6acc 100644
--- a/xfa/fxfa/parser/cxfa_runscripts.cpp
+++ b/xfa/fxfa/parser/cxfa_runscripts.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_runscripts.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kRunScriptsAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"runScripts";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::RunScripts,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kRunScriptsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_RunScripts::~CXFA_RunScripts() {}
+CXFA_RunScripts::~CXFA_RunScripts() = default;
diff --git a/xfa/fxfa/parser/cxfa_runscripts.h b/xfa/fxfa/parser/cxfa_runscripts.h
index d2ecaa0..2f80b0d 100644
--- a/xfa/fxfa/parser/cxfa_runscripts.h
+++ b/xfa/fxfa/parser/cxfa_runscripts.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_RunScripts : public CXFA_Node {
+class CXFA_RunScripts final : public CXFA_Node {
  public:
   CXFA_RunScripts(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_RunScripts() override;
diff --git a/xfa/fxfa/parser/cxfa_script.cpp b/xfa/fxfa/parser/cxfa_script.cpp
index 1e4eb7e..884f97d 100644
--- a/xfa/fxfa/parser/cxfa_script.cpp
+++ b/xfa/fxfa/parser/cxfa_script.cpp
@@ -11,25 +11,24 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kScriptPropertyData[] = {
     {XFA_Element::Exclude, 1, 0},
     {XFA_Element::CurrentPage, 1, 0},
     {XFA_Element::RunScripts, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kScriptAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ContentType, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::RunAt, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Client},
+     (void*)XFA_AttributeValue::Client},
     {XFA_Attribute::Binding, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"script";
+};
 
 }  // namespace
 
@@ -40,24 +39,25 @@
           (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
           XFA_ObjectType::ContentNode,
           XFA_Element::Script,
-          kPropertyData,
-          kAttributeData,
-          kName,
+          kScriptPropertyData,
+          kScriptAttributeData,
           pdfium::MakeUnique<CJX_Script>(this)) {}
 
-CXFA_Script::~CXFA_Script() {}
+CXFA_Script::~CXFA_Script() = default;
 
 CXFA_Script::Type CXFA_Script::GetContentType() {
   Optional<WideString> cData =
       JSObject()->TryCData(XFA_Attribute::ContentType, false);
-  if (!cData || *cData == L"application/x-formcalc")
+  if (!cData.has_value())
     return Type::Formcalc;
-  if (*cData == L"application/x-javascript")
+  if (cData.value().EqualsASCII("application/x-formcalc"))
+    return Type::Formcalc;
+  if (cData.value().EqualsASCII("application/x-javascript"))
     return Type::Javascript;
   return Type::Unknown;
 }
 
-XFA_AttributeEnum CXFA_Script::GetRunAt() {
+XFA_AttributeValue CXFA_Script::GetRunAt() {
   return JSObject()->GetEnum(XFA_Attribute::RunAt);
 }
 
diff --git a/xfa/fxfa/parser/cxfa_script.h b/xfa/fxfa/parser/cxfa_script.h
index 8ea7aac..f559284 100644
--- a/xfa/fxfa/parser/cxfa_script.h
+++ b/xfa/fxfa/parser/cxfa_script.h
@@ -10,7 +10,7 @@
 #include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Script : public CXFA_Node {
+class CXFA_Script final : public CXFA_Node {
  public:
   enum class Type {
     Formcalc = 0,
@@ -22,7 +22,7 @@
   ~CXFA_Script() override;
 
   Type GetContentType();
-  XFA_AttributeEnum GetRunAt();
+  XFA_AttributeValue GetRunAt();
   WideString GetExpression();
 };
 
diff --git a/xfa/fxfa/parser/cxfa_scriptmodel.cpp b/xfa/fxfa/parser/cxfa_scriptmodel.cpp
index 868dac5..8de4901 100644
--- a/xfa/fxfa/parser/cxfa_scriptmodel.cpp
+++ b/xfa/fxfa/parser/cxfa_scriptmodel.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_scriptmodel.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kScriptModelAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"scriptModel";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::ScriptModel,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kScriptModelAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ScriptModel::~CXFA_ScriptModel() {}
+CXFA_ScriptModel::~CXFA_ScriptModel() = default;
diff --git a/xfa/fxfa/parser/cxfa_scriptmodel.h b/xfa/fxfa/parser/cxfa_scriptmodel.h
index 40bc96e..a71073e 100644
--- a/xfa/fxfa/parser/cxfa_scriptmodel.h
+++ b/xfa/fxfa/parser/cxfa_scriptmodel.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ScriptModel : public CXFA_Node {
+class CXFA_ScriptModel final : public CXFA_Node {
  public:
   CXFA_ScriptModel(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ScriptModel() override;
diff --git a/xfa/fxfa/parser/cxfa_select.cpp b/xfa/fxfa/parser/cxfa_select.cpp
index 92eca07..04e51dc 100644
--- a/xfa/fxfa/parser/cxfa_select.cpp
+++ b/xfa/fxfa/parser/cxfa_select.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_select.h"
 
-#include "fxjs/xfa/cjx_select.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSelectAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"select";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Select,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Select>(this)) {}
+                {},
+                kSelectAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Select::~CXFA_Select() {}
+CXFA_Select::~CXFA_Select() = default;
diff --git a/xfa/fxfa/parser/cxfa_select.h b/xfa/fxfa/parser/cxfa_select.h
index 76651e0..82fc0d2 100644
--- a/xfa/fxfa/parser/cxfa_select.h
+++ b/xfa/fxfa/parser/cxfa_select.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Select : public CXFA_Node {
+class CXFA_Select final : public CXFA_Node {
  public:
   CXFA_Select(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Select() override;
diff --git a/xfa/fxfa/parser/cxfa_setproperty.cpp b/xfa/fxfa/parser/cxfa_setproperty.cpp
index 85d631b..a748d58 100644
--- a/xfa/fxfa/parser/cxfa_setproperty.cpp
+++ b/xfa/fxfa/parser/cxfa_setproperty.cpp
@@ -6,18 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_setproperty.h"
 
-#include "fxjs/xfa/cjx_setproperty.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSetPropertyAttributeData[] = {
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Connection, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Target, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"setProperty";
+};
 
 }  // namespace
 
@@ -27,9 +25,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::SetProperty,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SetProperty>(this)) {}
+                {},
+                kSetPropertyAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SetProperty::~CXFA_SetProperty() {}
+CXFA_SetProperty::~CXFA_SetProperty() = default;
diff --git a/xfa/fxfa/parser/cxfa_setproperty.h b/xfa/fxfa/parser/cxfa_setproperty.h
index 3f8f430..aab588b 100644
--- a/xfa/fxfa/parser/cxfa_setproperty.h
+++ b/xfa/fxfa/parser/cxfa_setproperty.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SetProperty : public CXFA_Node {
+class CXFA_SetProperty final : public CXFA_Node {
  public:
   CXFA_SetProperty(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SetProperty() override;
diff --git a/xfa/fxfa/parser/cxfa_severity.cpp b/xfa/fxfa/parser/cxfa_severity.cpp
index 53a91e8..78a0872 100644
--- a/xfa/fxfa/parser/cxfa_severity.cpp
+++ b/xfa/fxfa/parser/cxfa_severity.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_severity.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSeverityAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"severity";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Severity,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSeverityAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Severity::~CXFA_Severity() {}
+CXFA_Severity::~CXFA_Severity() = default;
diff --git a/xfa/fxfa/parser/cxfa_severity.h b/xfa/fxfa/parser/cxfa_severity.h
index 7114455..b2283c9 100644
--- a/xfa/fxfa/parser/cxfa_severity.h
+++ b/xfa/fxfa/parser/cxfa_severity.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Severity : public CXFA_Node {
+class CXFA_Severity final : public CXFA_Node {
  public:
   CXFA_Severity(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Severity() override;
diff --git a/xfa/fxfa/parser/cxfa_sharptext.cpp b/xfa/fxfa/parser/cxfa_sharptext.cpp
index 5300dfc..515e881 100644
--- a/xfa/fxfa/parser/cxfa_sharptext.cpp
+++ b/xfa/fxfa/parser/cxfa_sharptext.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_sharptext.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSharptextAttributeData[] = {
     {XFA_Attribute::Value, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"#text";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                  XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeV,
                 XFA_Element::Sharptext,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSharptextAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Sharptext::~CXFA_Sharptext() {}
+CXFA_Sharptext::~CXFA_Sharptext() = default;
diff --git a/xfa/fxfa/parser/cxfa_sharptext.h b/xfa/fxfa/parser/cxfa_sharptext.h
index 89a050b..f2a41e8 100644
--- a/xfa/fxfa/parser/cxfa_sharptext.h
+++ b/xfa/fxfa/parser/cxfa_sharptext.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Sharptext : public CXFA_Node {
+class CXFA_Sharptext final : public CXFA_Node {
  public:
   CXFA_Sharptext(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Sharptext() override;
diff --git a/xfa/fxfa/parser/cxfa_sharpxhtml.cpp b/xfa/fxfa/parser/cxfa_sharpxhtml.cpp
index 51829b7..6758b48 100644
--- a/xfa/fxfa/parser/cxfa_sharpxhtml.cpp
+++ b/xfa/fxfa/parser/cxfa_sharpxhtml.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_sharpxhtml.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSharpxHTMLAttributeData[] = {
     {XFA_Attribute::Value, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"#xHTML";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                  XFA_XDPPACKET_SourceSet | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeV,
                 XFA_Element::SharpxHTML,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSharpxHTMLAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SharpxHTML::~CXFA_SharpxHTML() {}
+CXFA_SharpxHTML::~CXFA_SharpxHTML() = default;
diff --git a/xfa/fxfa/parser/cxfa_sharpxhtml.h b/xfa/fxfa/parser/cxfa_sharpxhtml.h
index 3f6cf79..b73789d 100644
--- a/xfa/fxfa/parser/cxfa_sharpxhtml.h
+++ b/xfa/fxfa/parser/cxfa_sharpxhtml.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SharpxHTML : public CXFA_Node {
+class CXFA_SharpxHTML final : public CXFA_Node {
  public:
   CXFA_SharpxHTML(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SharpxHTML() override;
diff --git a/xfa/fxfa/parser/cxfa_sharpxml.cpp b/xfa/fxfa/parser/cxfa_sharpxml.cpp
index 6ce220e..0e066b1 100644
--- a/xfa/fxfa/parser/cxfa_sharpxml.cpp
+++ b/xfa/fxfa/parser/cxfa_sharpxml.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_sharpxml.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSharpxmlAttributeData[] = {
     {XFA_Attribute::Value, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"#xml";
+};
 
 }  // namespace
 
@@ -22,8 +23,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeV,
                 XFA_Element::Sharpxml,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSharpxmlAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Sharpxml::~CXFA_Sharpxml() {}
+CXFA_Sharpxml::~CXFA_Sharpxml() = default;
diff --git a/xfa/fxfa/parser/cxfa_sharpxml.h b/xfa/fxfa/parser/cxfa_sharpxml.h
index a065857..b2e467a 100644
--- a/xfa/fxfa/parser/cxfa_sharpxml.h
+++ b/xfa/fxfa/parser/cxfa_sharpxml.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Sharpxml : public CXFA_Node {
+class CXFA_Sharpxml final : public CXFA_Node {
  public:
   CXFA_Sharpxml(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Sharpxml() override;
diff --git a/xfa/fxfa/parser/cxfa_signature.cpp b/xfa/fxfa/parser/cxfa_signature.cpp
index baee044..d059808 100644
--- a/xfa/fxfa/parser/cxfa_signature.cpp
+++ b/xfa/fxfa/parser/cxfa_signature.cpp
@@ -6,24 +6,24 @@
 
 #include "xfa/fxfa/parser/cxfa_signature.h"
 
-#include "fxjs/xfa/cjx_signature.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kSignaturePropertyData[] = {
     {XFA_Element::Margin, 1, 0}, {XFA_Element::Filter, 1, 0},
     {XFA_Element::Border, 1, 0}, {XFA_Element::Manifest, 1, 0},
-    {XFA_Element::Extras, 1, 0}, {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kSignatureAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::PDF1_3},
+     (void*)XFA_AttributeValue::PDF1_3},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"signature";
+};
 
 }  // namespace
 
@@ -33,9 +33,12 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Signature,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Signature>(this)) {}
+                kSignaturePropertyData,
+                kSignatureAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Signature::~CXFA_Signature() {}
+CXFA_Signature::~CXFA_Signature() = default;
+
+XFA_FFWidgetType CXFA_Signature::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kSignature;
+}
diff --git a/xfa/fxfa/parser/cxfa_signature.h b/xfa/fxfa/parser/cxfa_signature.h
index f55ce5a..678e892 100644
--- a/xfa/fxfa/parser/cxfa_signature.h
+++ b/xfa/fxfa/parser/cxfa_signature.h
@@ -9,10 +9,12 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Signature : public CXFA_Node {
+class CXFA_Signature final : public CXFA_Node {
  public:
   CXFA_Signature(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Signature() override;
+
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_SIGNATURE_H_
diff --git a/xfa/fxfa/parser/cxfa_signatureproperties.cpp b/xfa/fxfa/parser/cxfa_signatureproperties.cpp
index c710cb4..63b2635 100644
--- a/xfa/fxfa/parser/cxfa_signatureproperties.cpp
+++ b/xfa/fxfa/parser/cxfa_signatureproperties.cpp
@@ -6,17 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_signatureproperties.h"
 
-#include "fxjs/xfa/cjx_signatureproperties.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSignaturePropertiesAttributeData[] = {
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"signatureProperties";
+};
 
 }  // namespace
 
@@ -27,9 +25,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::SignatureProperties,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SignatureProperties>(this)) {}
+                {},
+                kSignaturePropertiesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SignatureProperties::~CXFA_SignatureProperties() {}
+CXFA_SignatureProperties::~CXFA_SignatureProperties() = default;
diff --git a/xfa/fxfa/parser/cxfa_signatureproperties.h b/xfa/fxfa/parser/cxfa_signatureproperties.h
index 800b780..a66346e 100644
--- a/xfa/fxfa/parser/cxfa_signatureproperties.h
+++ b/xfa/fxfa/parser/cxfa_signatureproperties.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SignatureProperties : public CXFA_Node {
+class CXFA_SignatureProperties final : public CXFA_Node {
  public:
   CXFA_SignatureProperties(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SignatureProperties() override;
diff --git a/xfa/fxfa/parser/cxfa_signdata.cpp b/xfa/fxfa/parser/cxfa_signdata.cpp
index 796ae5c..3710325 100644
--- a/xfa/fxfa/parser/cxfa_signdata.cpp
+++ b/xfa/fxfa/parser/cxfa_signdata.cpp
@@ -6,25 +6,25 @@
 
 #include "xfa/fxfa/parser/cxfa_signdata.h"
 
-#include "fxjs/xfa/cjx_signdata.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Filter, 1, 0},
-                                                 {XFA_Element::Manifest, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kSignDataPropertyData[] = {
+    {XFA_Element::Filter, 1, 0},
+    {XFA_Element::Manifest, 1, 0},
+};
+
+const CXFA_Node::AttributeData kSignDataAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Operation, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Sign},
+     (void*)XFA_AttributeValue::Sign},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Target, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"signData";
+};
 
 }  // namespace
 
@@ -34,9 +34,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::SignData,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SignData>(this)) {}
+                kSignDataPropertyData,
+                kSignDataAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SignData::~CXFA_SignData() {}
+CXFA_SignData::~CXFA_SignData() = default;
diff --git a/xfa/fxfa/parser/cxfa_signdata.h b/xfa/fxfa/parser/cxfa_signdata.h
index 3ad39f5..e28460d 100644
--- a/xfa/fxfa/parser/cxfa_signdata.h
+++ b/xfa/fxfa/parser/cxfa_signdata.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SignData : public CXFA_Node {
+class CXFA_SignData final : public CXFA_Node {
  public:
   CXFA_SignData(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SignData() override;
diff --git a/xfa/fxfa/parser/cxfa_signing.cpp b/xfa/fxfa/parser/cxfa_signing.cpp
index 251e294..939d4f5 100644
--- a/xfa/fxfa/parser/cxfa_signing.cpp
+++ b/xfa/fxfa/parser/cxfa_signing.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_signing.h"
 
-#include "fxjs/xfa/cjx_signing.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSigningAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"signing";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Signing,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Signing>(this)) {}
+                {},
+                kSigningAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Signing::~CXFA_Signing() {}
+CXFA_Signing::~CXFA_Signing() = default;
diff --git a/xfa/fxfa/parser/cxfa_signing.h b/xfa/fxfa/parser/cxfa_signing.h
index 6df9a18..7c37403 100644
--- a/xfa/fxfa/parser/cxfa_signing.h
+++ b/xfa/fxfa/parser/cxfa_signing.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Signing : public CXFA_Node {
+class CXFA_Signing final : public CXFA_Node {
  public:
   CXFA_Signing(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Signing() override;
diff --git a/xfa/fxfa/parser/cxfa_silentprint.cpp b/xfa/fxfa/parser/cxfa_silentprint.cpp
index ca0cf9f..1a417aa 100644
--- a/xfa/fxfa/parser/cxfa_silentprint.cpp
+++ b/xfa/fxfa/parser/cxfa_silentprint.cpp
@@ -6,18 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_silentprint.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kSilentPrintPropertyData[] = {
     {XFA_Element::AddSilentPrint, 1, 0},
     {XFA_Element::PrinterName, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kSilentPrintAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"silentPrint";
+};
 
 }  // namespace
 
@@ -27,8 +29,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::SilentPrint,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kSilentPrintPropertyData,
+                kSilentPrintAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SilentPrint::~CXFA_SilentPrint() {}
+CXFA_SilentPrint::~CXFA_SilentPrint() = default;
diff --git a/xfa/fxfa/parser/cxfa_silentprint.h b/xfa/fxfa/parser/cxfa_silentprint.h
index 6ef5fcf..378084d 100644
--- a/xfa/fxfa/parser/cxfa_silentprint.h
+++ b/xfa/fxfa/parser/cxfa_silentprint.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SilentPrint : public CXFA_Node {
+class CXFA_SilentPrint final : public CXFA_Node {
  public:
   CXFA_SilentPrint(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SilentPrint() override;
diff --git a/xfa/fxfa/parser/cxfa_simple_parser.cpp b/xfa/fxfa/parser/cxfa_simple_parser.cpp
deleted file mode 100644
index 1864532..0000000
--- a/xfa/fxfa/parser/cxfa_simple_parser.cpp
+++ /dev/null
@@ -1,1264 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/cxfa_simple_parser.h"
-
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/cfx_checksumcontext.h"
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_codepage.h"
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/xml/cfx_xmlchardata.h"
-#include "core/fxcrt/xml/cfx_xmldoc.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "core/fxcrt/xml/cfx_xmlinstruction.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "core/fxcrt/xml/cfx_xmlparser.h"
-#include "core/fxcrt/xml/cfx_xmltext.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/fxfa.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_subform.h"
-#include "xfa/fxfa/parser/cxfa_template.h"
-#include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
-
-namespace {
-
-struct PacketInfo {
-  uint32_t hash;
-  const wchar_t* name;
-  XFA_PacketType packet_type;
-  const wchar_t* uri;
-  uint32_t flags;
-};
-const PacketInfo PacketData[] = {
-    {0x0, nullptr, XFA_PacketType::User, nullptr,
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTMANY},
-    {0x811929d, L"sourceSet", XFA_PacketType::SourceSet,
-     L"http://www.xfa.org/schema/xfa-source-set/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0xb843dba, L"pdf", XFA_PacketType::Pdf, L"http://ns.adobe.com/xdp/pdf/",
-     XFA_XDPPACKET_FLAGS_COMPLETEMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0xc56afbf, L"xdc", XFA_PacketType::Xdc, L"http://www.xfa.org/schema/xdc/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0xc56afcc, L"xdp", XFA_PacketType::Xdp, L"http://ns.adobe.com/xdp/",
-     XFA_XDPPACKET_FLAGS_COMPLETEMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0x132a8fbc, L"xmpmeta", XFA_PacketType::Xmpmeta,
-     L"http://ns.adobe.com/xmpmeta/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTMANY},
-    {0x48d004a8, L"xfdf", XFA_PacketType::Xfdf, L"http://ns.adobe.com/xfdf/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0x4e1e39b6, L"config", XFA_PacketType::Config,
-     L"http://www.xfa.org/schema/xci/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0x5473b6dc, L"localeSet", XFA_PacketType::LocaleSet,
-     L"http://www.xfa.org/schema/xfa-locale-set/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0x6038580a, L"stylesheet", XFA_PacketType::Stylesheet,
-     L"http://www.w3.org/1999/XSL/Transform",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTMANY},
-    {0x803550fc, L"template", XFA_PacketType::Template,
-     L"http://www.xfa.org/schema/xfa-template/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0x8b036f32, L"signature", XFA_PacketType::Signature,
-     L"http://www.w3.org/2000/09/xmldsig#",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0x99b95079, L"datasets", XFA_PacketType::Datasets,
-     L"http://www.xfa.org/schema/xfa-data/",
-     XFA_XDPPACKET_FLAGS_PREFIXMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0xcd309ff4, L"form", XFA_PacketType::Form,
-     L"http://www.xfa.org/schema/xfa-form/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-    {0xe14c801c, L"connectionSet", XFA_PacketType::ConnectionSet,
-     L"http://www.xfa.org/schema/xfa-connection-set/",
-     XFA_XDPPACKET_FLAGS_NOMATCH | XFA_XDPPACKET_FLAGS_SUPPORTONE},
-};
-
-const PacketInfo* GetPacketByIndex(XFA_PacketType ePacket) {
-  return PacketData + static_cast<uint8_t>(ePacket);
-}
-
-const PacketInfo* GetPacketByName(const WideStringView& wsName) {
-  if (wsName.IsEmpty())
-    return nullptr;
-
-  uint32_t hash = FX_HashCode_GetW(wsName, false);
-  auto* elem = std::lower_bound(
-      std::begin(PacketData), std::end(PacketData), hash,
-      [](const PacketInfo& a, uint32_t hash) { return a.hash < hash; });
-  if (elem != std::end(PacketData) && elem->hash == hash)
-    return elem;
-  return nullptr;
-}
-
-CFX_XMLNode* GetDocumentNode(CFX_XMLDoc* pXMLDoc,
-                             bool bVerifyWellFormness = false) {
-  if (!pXMLDoc)
-    return nullptr;
-
-  for (CFX_XMLNode* pXMLNode =
-           pXMLDoc->GetRoot()->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLNode; pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    if (pXMLNode->GetType() != FX_XMLNODE_Element)
-      continue;
-
-    if (!bVerifyWellFormness)
-      return pXMLNode;
-
-    for (CFX_XMLNode* pNextNode =
-             pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling);
-         pNextNode;
-         pNextNode = pNextNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-      if (pNextNode->GetType() == FX_XMLNODE_Element)
-        return nullptr;
-    }
-    return pXMLNode;
-  }
-  return nullptr;
-}
-
-WideString GetElementTagNamespaceURI(CFX_XMLElement* pElement) {
-  WideString wsNodeStr = pElement->GetNamespacePrefix();
-  WideString wsNamespaceURI;
-  if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNodeStr,
-                                                  &wsNamespaceURI)) {
-    return WideString();
-  }
-  return wsNamespaceURI;
-}
-
-bool MatchNodeName(CFX_XMLNode* pNode,
-                   const WideStringView& wsLocalTagName,
-                   const WideStringView& wsNamespaceURIPrefix,
-                   uint32_t eMatchFlags = XFA_XDPPACKET_FLAGS_NOMATCH) {
-  if (!pNode || pNode->GetType() != FX_XMLNODE_Element)
-    return false;
-
-  CFX_XMLElement* pElement = reinterpret_cast<CFX_XMLElement*>(pNode);
-  WideString wsNodeStr = pElement->GetLocalTagName();
-  if (wsNodeStr != wsLocalTagName)
-    return false;
-
-  wsNodeStr = GetElementTagNamespaceURI(pElement);
-  if (eMatchFlags & XFA_XDPPACKET_FLAGS_NOMATCH)
-    return true;
-  if (eMatchFlags & XFA_XDPPACKET_FLAGS_PREFIXMATCH) {
-    return wsNodeStr.Left(wsNamespaceURIPrefix.GetLength()) ==
-           wsNamespaceURIPrefix;
-  }
-
-  return wsNodeStr == wsNamespaceURIPrefix;
-}
-
-bool GetAttributeLocalName(const WideStringView& wsAttributeName,
-                           WideString& wsLocalAttrName) {
-  WideString wsAttrName(wsAttributeName);
-  auto pos = wsAttrName.Find(L':', 0);
-  if (!pos.has_value()) {
-    wsLocalAttrName = wsAttrName;
-    return false;
-  }
-  wsLocalAttrName = wsAttrName.Right(wsAttrName.GetLength() - pos.value() - 1);
-  return true;
-}
-
-bool ResolveAttribute(CFX_XMLElement* pElement,
-                      const WideString& wsAttrName,
-                      WideString& wsLocalAttrName,
-                      WideString& wsNamespaceURI) {
-  WideString wsNSPrefix;
-  if (GetAttributeLocalName(wsAttrName.AsStringView(), wsLocalAttrName)) {
-    wsNSPrefix = wsAttrName.Left(wsAttrName.GetLength() -
-                                 wsLocalAttrName.GetLength() - 1);
-  }
-  if (wsLocalAttrName == L"xmlns" || wsNSPrefix == L"xmlns" ||
-      wsNSPrefix == L"xml") {
-    return false;
-  }
-  if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
-                                                  &wsNamespaceURI)) {
-    wsNamespaceURI.clear();
-    return false;
-  }
-  return true;
-}
-
-bool FindAttributeWithNS(CFX_XMLElement* pElement,
-                         const WideStringView& wsLocalAttributeName,
-                         const WideStringView& wsNamespaceURIPrefix,
-                         WideString& wsValue,
-                         bool bMatchNSAsPrefix = false) {
-  if (!pElement)
-    return false;
-
-  WideString wsAttrNS;
-  for (auto it : pElement->GetAttributes()) {
-    auto pos = it.first.Find(L':', 0);
-    WideString wsNSPrefix;
-    if (!pos.has_value()) {
-      if (wsLocalAttributeName != it.first)
-        continue;
-    } else {
-      if (wsLocalAttributeName !=
-          it.first.Right(it.first.GetLength() - pos.value() - 1)) {
-        continue;
-      }
-      wsNSPrefix = it.first.Left(pos.value());
-    }
-
-    if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix,
-                                                    &wsAttrNS)) {
-      continue;
-    }
-    if (bMatchNSAsPrefix) {
-      if (wsAttrNS.Left(wsNamespaceURIPrefix.GetLength()) !=
-          wsNamespaceURIPrefix) {
-        continue;
-      }
-    } else {
-      if (wsAttrNS != wsNamespaceURIPrefix)
-        continue;
-    }
-    wsValue = it.second;
-    return true;
-  }
-  return false;
-}
-
-CFX_XMLNode* GetDataSetsFromXDP(CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* datasets_packet =
-      GetPacketByIndex(XFA_PacketType::Datasets);
-  if (MatchNodeName(pXMLDocumentNode, datasets_packet->name,
-                    datasets_packet->uri, datasets_packet->flags)) {
-    return pXMLDocumentNode;
-  }
-
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Xdp);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
-    return nullptr;
-  }
-
-  for (CFX_XMLNode* pDatasetsNode =
-           pXMLDocumentNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pDatasetsNode;
-       pDatasetsNode = pDatasetsNode->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    if (MatchNodeName(pDatasetsNode, datasets_packet->name,
-                      datasets_packet->uri, datasets_packet->flags)) {
-      return pDatasetsNode;
-    }
-  }
-  return nullptr;
-}
-
-bool IsStringAllWhitespace(WideString wsText) {
-  wsText.TrimRight(L"\x20\x9\xD\xA");
-  return wsText.IsEmpty();
-}
-
-void ConvertXMLToPlainText(CFX_XMLElement* pRootXMLNode, WideString& wsOutput) {
-  for (CFX_XMLNode* pXMLChild =
-           pRootXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLChild;
-       pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    switch (pXMLChild->GetType()) {
-      case FX_XMLNODE_Element: {
-        WideString wsTextData =
-            static_cast<CFX_XMLElement*>(pXMLChild)->GetTextData();
-        wsTextData += L"\n";
-        wsOutput += wsTextData;
-        break;
-      }
-      case FX_XMLNODE_Text:
-      case FX_XMLNODE_CharData: {
-        WideString wsText = static_cast<CFX_XMLText*>(pXMLChild)->GetText();
-        if (IsStringAllWhitespace(wsText))
-          continue;
-
-        wsOutput = wsText;
-        break;
-      }
-      default:
-        NOTREACHED();
-        break;
-    }
-  }
-}
-
-WideString GetPlainTextFromRichText(CFX_XMLNode* pXMLNode) {
-  if (!pXMLNode)
-    return L"";
-
-  WideString wsPlainText;
-  switch (pXMLNode->GetType()) {
-    case FX_XMLNODE_Element: {
-      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-      WideString wsTag = pXMLElement->GetLocalTagName();
-      uint32_t uTag = FX_HashCode_GetW(wsTag.AsStringView(), true);
-      if (uTag == 0x0001f714) {
-        wsPlainText += L"\n";
-      } else if (uTag == 0x00000070) {
-        if (!wsPlainText.IsEmpty()) {
-          wsPlainText += L"\n";
-        }
-      } else if (uTag == 0xa48ac63) {
-        if (!wsPlainText.IsEmpty() &&
-            wsPlainText[wsPlainText.GetLength() - 1] != '\n') {
-          wsPlainText += L"\n";
-        }
-      }
-      break;
-    }
-    case FX_XMLNODE_Text:
-    case FX_XMLNODE_CharData: {
-      WideString wsContent = static_cast<CFX_XMLText*>(pXMLNode)->GetText();
-      wsPlainText += wsContent;
-      break;
-    }
-    default:
-      break;
-  }
-  for (CFX_XMLNode* pChildXML = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pChildXML;
-       pChildXML = pChildXML->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    wsPlainText += GetPlainTextFromRichText(pChildXML);
-  }
-
-  return wsPlainText;
-}
-
-}  // namespace
-
-bool XFA_RecognizeRichText(CFX_XMLElement* pRichTextXMLNode) {
-  return pRichTextXMLNode && GetElementTagNamespaceURI(pRichTextXMLNode) ==
-                                 L"http://www.w3.org/1999/xhtml";
-}
-
-CXFA_SimpleParser::CXFA_SimpleParser() : m_bDocumentParser(true) {}
-
-CXFA_SimpleParser::CXFA_SimpleParser(CXFA_Document* pFactory)
-    : m_pFactory(pFactory), m_bDocumentParser(false) {}
-
-CXFA_SimpleParser::~CXFA_SimpleParser() {}
-
-void CXFA_SimpleParser::SetFactory(CXFA_Document* pFactory) {
-  ASSERT(m_bDocumentParser);
-  m_pFactory = pFactory;
-}
-
-int32_t CXFA_SimpleParser::StartParse(
-    const RetainPtr<IFX_SeekableStream>& pStream,
-    XFA_PacketType ePacketID) {
-  CloseParser();
-  m_pFileRead = pStream;
-  m_pStream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(pStream, false);
-  uint16_t wCodePage = m_pStream->GetCodePage();
-  if (wCodePage != FX_CODEPAGE_UTF16LE && wCodePage != FX_CODEPAGE_UTF16BE &&
-      wCodePage != FX_CODEPAGE_UTF8) {
-    m_pStream->SetCodePage(FX_CODEPAGE_UTF8);
-  }
-  m_pXMLDoc = pdfium::MakeUnique<CFX_XMLDoc>();
-  auto pNewParser =
-      pdfium::MakeUnique<CFX_XMLParser>(m_pXMLDoc->GetRoot(), m_pStream);
-  m_pXMLParser = pNewParser.get();
-  if (!m_pXMLDoc->LoadXML(std::move(pNewParser)))
-    return XFA_PARSESTATUS_StatusErr;
-
-  m_bParseStarted = true;
-  m_ePacketID = ePacketID;
-  return XFA_PARSESTATUS_Ready;
-}
-
-int32_t CXFA_SimpleParser::DoParse() {
-  if (!m_pXMLDoc || !m_bParseStarted)
-    return XFA_PARSESTATUS_StatusErr;
-
-  int32_t iRet = m_pXMLDoc->DoLoad();
-  if (iRet < 0)
-    return XFA_PARSESTATUS_SyntaxErr;
-  if (iRet < 100)
-    return iRet / 2;
-
-  m_pRootNode = ParseAsXDPPacket(GetDocumentNode(m_pXMLDoc.get()), m_ePacketID);
-  m_pXMLParser.Release();
-  m_pXMLDoc->CloseXML();
-  m_pStream.Reset();
-
-  if (!m_pRootNode)
-    return XFA_PARSESTATUS_StatusErr;
-
-  return XFA_PARSESTATUS_Done;
-}
-
-CFX_XMLNode* CXFA_SimpleParser::ParseXMLData(const ByteString& wsXML) {
-  CloseParser();
-  m_pXMLDoc = pdfium::MakeUnique<CFX_XMLDoc>();
-
-  auto pStream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
-      const_cast<uint8_t*>(wsXML.raw_str()), wsXML.GetLength());
-  auto pParser =
-      pdfium::MakeUnique<CFX_XMLParser>(m_pXMLDoc->GetRoot(), pStream);
-  pParser->m_dwCheckStatus = 0x03;
-  if (!m_pXMLDoc->LoadXML(std::move(pParser)))
-    return nullptr;
-
-  int32_t iRet = m_pXMLDoc->DoLoad();
-  if (iRet < 0 || iRet >= 100)
-    m_pXMLDoc->CloseXML();
-  return iRet < 100 ? nullptr : GetDocumentNode(m_pXMLDoc.get());
-}
-
-void CXFA_SimpleParser::ConstructXFANode(CXFA_Node* pXFANode,
-                                         CFX_XMLNode* pXMLNode) {
-  XFA_PacketType ePacketID = pXFANode->GetPacketType();
-  if (ePacketID == XFA_PacketType::Datasets) {
-    if (pXFANode->GetElementType() == XFA_Element::DataValue) {
-      for (CFX_XMLNode* pXMLChild =
-               pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-           pXMLChild;
-           pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-        FX_XMLNODETYPE eNodeType = pXMLChild->GetType();
-        if (eNodeType == FX_XMLNODE_Instruction)
-          continue;
-
-        if (eNodeType == FX_XMLNODE_Element) {
-          CXFA_Node* pXFAChild = m_pFactory->CreateNode(
-              XFA_PacketType::Datasets, XFA_Element::DataValue);
-          if (!pXFAChild)
-            return;
-
-          CFX_XMLElement* child = static_cast<CFX_XMLElement*>(pXMLChild);
-          WideString wsNodeStr = child->GetLocalTagName();
-          pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr, false,
-                                          false);
-          WideString wsChildValue = GetPlainTextFromRichText(child);
-          if (!wsChildValue.IsEmpty())
-            pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsChildValue,
-                                            false, false);
-
-          pXFANode->InsertChild(pXFAChild, nullptr);
-          pXFAChild->SetXMLMappingNode(pXMLChild);
-          pXFAChild->SetFlag(XFA_NodeFlag_Initialized, false);
-          break;
-        }
-      }
-      m_pRootNode = pXFANode;
-    } else {
-      m_pRootNode = DataLoader(pXFANode, pXMLNode, true);
-    }
-  } else if (pXFANode->IsContentNode()) {
-    ParseContentNode(pXFANode, pXMLNode, ePacketID);
-    m_pRootNode = pXFANode;
-  } else {
-    m_pRootNode = NormalLoader(pXFANode, pXMLNode, ePacketID, true);
-  }
-}
-
-CXFA_Node* CXFA_SimpleParser::GetRootNode() const {
-  return m_pRootNode;
-}
-
-CFX_XMLDoc* CXFA_SimpleParser::GetXMLDoc() const {
-  return m_pXMLDoc.get();
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
-                                               XFA_PacketType ePacketID) {
-  switch (ePacketID) {
-    case XFA_PacketType::Xdp:
-      return ParseAsXDPPacket_XDP(pXMLDocumentNode);
-    case XFA_PacketType::Config:
-      return ParseAsXDPPacket_Config(pXMLDocumentNode);
-    case XFA_PacketType::Template:
-      return ParseAsXDPPacket_Template(pXMLDocumentNode);
-    case XFA_PacketType::Form:
-      return ParseAsXDPPacket_Form(pXMLDocumentNode);
-    case XFA_PacketType::Datasets:
-      return ParseAsXDPPacket_Data(pXMLDocumentNode);
-    case XFA_PacketType::Xdc:
-      return ParseAsXDPPacket_Xdc(pXMLDocumentNode);
-    case XFA_PacketType::LocaleSet:
-      return ParseAsXDPPacket_LocaleConnectionSourceSet(
-          pXMLDocumentNode, XFA_PacketType::LocaleSet, XFA_Element::LocaleSet);
-    case XFA_PacketType::ConnectionSet:
-      return ParseAsXDPPacket_LocaleConnectionSourceSet(
-          pXMLDocumentNode, XFA_PacketType::ConnectionSet,
-          XFA_Element::ConnectionSet);
-    case XFA_PacketType::SourceSet:
-      return ParseAsXDPPacket_LocaleConnectionSourceSet(
-          pXMLDocumentNode, XFA_PacketType::SourceSet, XFA_Element::SourceSet);
-    default:
-      return ParseAsXDPPacket_User(pXMLDocumentNode);
-  }
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_XDP(
-    CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Xdp);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
-    return nullptr;
-  }
-
-  CXFA_Node* pXFARootNode =
-      m_pFactory->CreateNode(XFA_PacketType::Xdp, XFA_Element::Xfa);
-  if (!pXFARootNode)
-    return nullptr;
-
-  m_pRootNode = pXFARootNode;
-  pXFARootNode->JSObject()->SetCData(XFA_Attribute::Name, L"xfa", false, false);
-
-  CFX_XMLElement* pElement = static_cast<CFX_XMLElement*>(pXMLDocumentNode);
-  for (auto it : pElement->GetAttributes()) {
-    if (it.first == L"uuid")
-      pXFARootNode->JSObject()->SetCData(XFA_Attribute::Uuid, it.second, false,
-                                         false);
-    else if (it.first == L"timeStamp")
-      pXFARootNode->JSObject()->SetCData(XFA_Attribute::TimeStamp, it.second,
-                                         false, false);
-  }
-
-  CFX_XMLNode* pXMLConfigDOMRoot = nullptr;
-  CXFA_Node* pXFAConfigDOMRoot = nullptr;
-  for (CFX_XMLNode* pChildItem =
-           pXMLDocumentNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pChildItem;
-       pChildItem = pChildItem->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    const PacketInfo* pPacketInfo = GetPacketByIndex(XFA_PacketType::Config);
-    if (!MatchNodeName(pChildItem, pPacketInfo->name, pPacketInfo->uri,
-                       pPacketInfo->flags)) {
-      continue;
-    }
-    if (pXFARootNode->GetFirstChildByName(pPacketInfo->hash))
-      return nullptr;
-
-    pXMLConfigDOMRoot = pChildItem;
-    pXFAConfigDOMRoot = ParseAsXDPPacket_Config(pXMLConfigDOMRoot);
-    if (pXFAConfigDOMRoot)
-      pXFARootNode->InsertChild(pXFAConfigDOMRoot, nullptr);
-  }
-
-  CFX_XMLNode* pXMLDatasetsDOMRoot = nullptr;
-  CFX_XMLNode* pXMLFormDOMRoot = nullptr;
-  CFX_XMLNode* pXMLTemplateDOMRoot = nullptr;
-  for (CFX_XMLNode* pChildItem =
-           pXMLDocumentNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pChildItem;
-       pChildItem = pChildItem->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    if (!pChildItem || pChildItem->GetType() != FX_XMLNODE_Element)
-      continue;
-    if (pChildItem == pXMLConfigDOMRoot)
-      continue;
-
-    CFX_XMLElement* pElement = reinterpret_cast<CFX_XMLElement*>(pChildItem);
-    WideString wsPacketName = pElement->GetLocalTagName();
-    const PacketInfo* pPacketInfo =
-        GetPacketByName(wsPacketName.AsStringView());
-    if (pPacketInfo && pPacketInfo->uri) {
-      if (!MatchNodeName(pElement, pPacketInfo->name, pPacketInfo->uri,
-                         pPacketInfo->flags)) {
-        pPacketInfo = nullptr;
-      }
-    }
-    XFA_PacketType ePacket =
-        pPacketInfo ? pPacketInfo->packet_type : XFA_PacketType::User;
-    if (ePacket == XFA_PacketType::Xdp)
-      continue;
-    if (ePacket == XFA_PacketType::Datasets) {
-      if (pXMLDatasetsDOMRoot)
-        return nullptr;
-
-      pXMLDatasetsDOMRoot = pElement;
-    } else if (ePacket == XFA_PacketType::Form) {
-      if (pXMLFormDOMRoot)
-        return nullptr;
-
-      pXMLFormDOMRoot = pElement;
-    } else if (ePacket == XFA_PacketType::Template) {
-      // Found a duplicate template packet.
-      if (pXMLTemplateDOMRoot)
-        return nullptr;
-
-      CXFA_Node* pPacketNode = ParseAsXDPPacket(pElement, ePacket);
-      if (pPacketNode) {
-        pXMLTemplateDOMRoot = pElement;
-        pXFARootNode->InsertChild(pPacketNode, nullptr);
-      }
-    } else {
-      CXFA_Node* pPacketNode = ParseAsXDPPacket(pElement, ePacket);
-      if (pPacketNode) {
-        if (pPacketInfo &&
-            (pPacketInfo->flags & XFA_XDPPACKET_FLAGS_SUPPORTONE) &&
-            pXFARootNode->GetFirstChildByName(pPacketInfo->hash)) {
-          return nullptr;
-        }
-        pXFARootNode->InsertChild(pPacketNode, nullptr);
-      }
-    }
-  }
-
-  // No template is found.
-  if (!pXMLTemplateDOMRoot)
-    return nullptr;
-
-  if (pXMLDatasetsDOMRoot) {
-    CXFA_Node* pPacketNode =
-        ParseAsXDPPacket(pXMLDatasetsDOMRoot, XFA_PacketType::Datasets);
-    if (pPacketNode)
-      pXFARootNode->InsertChild(pPacketNode, nullptr);
-  }
-  if (pXMLFormDOMRoot) {
-    CXFA_Node* pPacketNode =
-        ParseAsXDPPacket(pXMLFormDOMRoot, XFA_PacketType::Form);
-    if (pPacketNode)
-      pXFARootNode->InsertChild(pPacketNode, nullptr);
-  }
-
-  pXFARootNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pXFARootNode;
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Config(
-    CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Config);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
-    return nullptr;
-  }
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Config, XFA_Element::Config);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
-  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Config, true))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Template(
-    CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Template);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
-    return nullptr;
-  }
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Template, XFA_Element::Template);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
-  if (m_bDocumentParser) {
-    CFX_XMLElement* pXMLDocumentElement =
-        static_cast<CFX_XMLElement*>(pXMLDocumentNode);
-    WideString wsNamespaceURI = pXMLDocumentElement->GetNamespaceURI();
-    if (wsNamespaceURI.IsEmpty())
-      wsNamespaceURI = pXMLDocumentElement->GetString(L"xmlns:xfa");
-
-    pNode->GetDocument()->RecognizeXFAVersionNumber(wsNamespaceURI);
-  }
-  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Template, true))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Form(
-    CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Form);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
-    return nullptr;
-  }
-
-  CFX_XMLElement* pXMLDocumentElement =
-      static_cast<CFX_XMLElement*>(pXMLDocumentNode);
-  WideString wsChecksum = pXMLDocumentElement->GetString(L"checksum");
-  if (wsChecksum.GetLength() != 28 || m_pXMLParser->m_dwCheckStatus != 0x03) {
-    return nullptr;
-  }
-
-  auto pChecksum = pdfium::MakeUnique<CFX_ChecksumContext>();
-  pChecksum->StartChecksum();
-  pChecksum->UpdateChecksum(m_pFileRead, m_pXMLParser->m_nStart[0],
-                            m_pXMLParser->m_nSize[0]);
-  pChecksum->UpdateChecksum(m_pFileRead, m_pXMLParser->m_nStart[1],
-                            m_pXMLParser->m_nSize[1]);
-  pChecksum->FinishChecksum();
-  ByteString bsCheck = pChecksum->GetChecksum();
-  if (bsCheck != wsChecksum.UTF8Encode())
-    return nullptr;
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Form, XFA_Element::Form);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
-  pNode->JSObject()->SetAttribute(XFA_Attribute::Checksum,
-                                  wsChecksum.AsStringView(), false);
-  CXFA_Template* pTemplateRoot =
-      m_pRootNode->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
-  CXFA_Subform* pTemplateChosen =
-      pTemplateRoot ? pTemplateRoot->GetFirstChildByClass<CXFA_Subform>(
-                          XFA_Element::Subform)
-                    : nullptr;
-  bool bUseAttribute = true;
-  if (pTemplateChosen &&
-      pTemplateChosen->JSObject()->GetEnum(XFA_Attribute::RestoreState) !=
-          XFA_AttributeEnum::Auto) {
-    bUseAttribute = false;
-  }
-  if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Form,
-                    bUseAttribute))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Data(
-    CFX_XMLNode* pXMLDocumentNode) {
-  CFX_XMLNode* pDatasetsXMLNode = GetDataSetsFromXDP(pXMLDocumentNode);
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Datasets);
-  if (pDatasetsXMLNode) {
-    CXFA_Node* pNode = m_pFactory->CreateNode(XFA_PacketType::Datasets,
-                                              XFA_Element::DataModel);
-    if (!pNode)
-      return nullptr;
-
-    pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false,
-                                false);
-    if (!DataLoader(pNode, pDatasetsXMLNode, false))
-      return nullptr;
-
-    pNode->SetXMLMappingNode(pDatasetsXMLNode);
-    return pNode;
-  }
-
-  CFX_XMLNode* pDataXMLNode = nullptr;
-  if (MatchNodeName(pXMLDocumentNode, L"data", packet->uri, packet->flags)) {
-    static_cast<CFX_XMLElement*>(pXMLDocumentNode)
-        ->RemoveAttribute(L"xmlns:xfa");
-    pDataXMLNode = pXMLDocumentNode;
-  } else {
-    CFX_XMLElement* pDataElement = new CFX_XMLElement(L"xfa:data");
-    CFX_XMLNode* pParentXMLNode =
-        pXMLDocumentNode->GetNodeItem(CFX_XMLNode::Parent);
-    if (pParentXMLNode)
-      pParentXMLNode->RemoveChildNode(pXMLDocumentNode);
-
-    ASSERT(pXMLDocumentNode->GetType() == FX_XMLNODE_Element);
-    if (pXMLDocumentNode->GetType() == FX_XMLNODE_Element) {
-      static_cast<CFX_XMLElement*>(pXMLDocumentNode)
-          ->RemoveAttribute(L"xmlns:xfa");
-    }
-    pDataElement->InsertChildNode(pXMLDocumentNode);
-    pDataXMLNode = pDataElement;
-  }
-
-  if (pDataXMLNode) {
-    CXFA_Node* pNode = m_pFactory->CreateNode(XFA_PacketType::Datasets,
-                                              XFA_Element::DataGroup);
-    if (!pNode) {
-      if (pDataXMLNode != pXMLDocumentNode)
-        delete pDataXMLNode;
-      return nullptr;
-    }
-    WideString wsLocalName =
-        static_cast<CFX_XMLElement*>(pDataXMLNode)->GetLocalTagName();
-    pNode->JSObject()->SetCData(XFA_Attribute::Name, wsLocalName, false, false);
-    if (!DataLoader(pNode, pDataXMLNode, true))
-      return nullptr;
-
-    pNode->SetXMLMappingNode(pDataXMLNode);
-    if (pDataXMLNode != pXMLDocumentNode)
-      pNode->SetFlag(XFA_NodeFlag_OwnXMLNode, false);
-    return pNode;
-  }
-  return nullptr;
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_LocaleConnectionSourceSet(
-    CFX_XMLNode* pXMLDocumentNode,
-    XFA_PacketType packet_type,
-    XFA_Element element) {
-  const PacketInfo* packet = GetPacketByIndex(packet_type);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
-    return nullptr;
-  }
-
-  CXFA_Node* pNode = m_pFactory->CreateNode(packet_type, element);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
-  if (!NormalLoader(pNode, pXMLDocumentNode, packet_type, true))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Xdc(
-    CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Xdc);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags))
-    return nullptr;
-
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Xdc, XFA_Element::Xdc);
-  if (!pNode)
-    return nullptr;
-
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_User(
-    CFX_XMLNode* pXMLDocumentNode) {
-  CXFA_Node* pNode =
-      m_pFactory->CreateNode(XFA_PacketType::Xdp, XFA_Element::Packet);
-  if (!pNode)
-    return nullptr;
-
-  WideString wsName =
-      static_cast<CFX_XMLElement*>(pXMLDocumentNode)->GetLocalTagName();
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
-  if (!UserPacketLoader(pNode, pXMLDocumentNode))
-    return nullptr;
-
-  pNode->SetXMLMappingNode(pXMLDocumentNode);
-  return pNode;
-}
-
-CXFA_Node* CXFA_SimpleParser::UserPacketLoader(CXFA_Node* pXFANode,
-                                               CFX_XMLNode* pXMLDoc) {
-  return pXFANode;
-}
-
-CXFA_Node* CXFA_SimpleParser::DataLoader(CXFA_Node* pXFANode,
-                                         CFX_XMLNode* pXMLDoc,
-                                         bool bDoTransform) {
-  ParseDataGroup(pXFANode, pXMLDoc, XFA_PacketType::Datasets);
-  return pXFANode;
-}
-
-CXFA_Node* CXFA_SimpleParser::NormalLoader(CXFA_Node* pXFANode,
-                                           CFX_XMLNode* pXMLDoc,
-                                           XFA_PacketType ePacketID,
-                                           bool bUseAttribute) {
-  bool bOneOfPropertyFound = false;
-  for (CFX_XMLNode* pXMLChild = pXMLDoc->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLChild;
-       pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    switch (pXMLChild->GetType()) {
-      case FX_XMLNODE_Element: {
-        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
-        WideString wsTagName = pXMLElement->GetLocalTagName();
-        XFA_Element eType = CXFA_Node::NameToElement(wsTagName);
-        if (eType == XFA_Element::Unknown)
-          continue;
-
-        if (pXFANode->HasPropertyFlags(
-                eType,
-                XFA_PROPERTYFLAG_OneOf | XFA_PROPERTYFLAG_DefaultOneOf)) {
-          if (bOneOfPropertyFound)
-            break;
-          bOneOfPropertyFound = true;
-        }
-
-        CXFA_Node* pXFAChild = m_pFactory->CreateNode(ePacketID, eType);
-        if (!pXFAChild)
-          return nullptr;
-        if (ePacketID == XFA_PacketType::Config) {
-          pXFAChild->JSObject()->SetAttribute(XFA_Attribute::Name,
-                                              wsTagName.AsStringView(), false);
-        }
-
-        bool IsNeedValue = true;
-        for (auto it : pXMLElement->GetAttributes()) {
-          WideString wsAttrName;
-          GetAttributeLocalName(it.first.AsStringView(), wsAttrName);
-          if (wsAttrName == L"nil" && it.second == L"true")
-            IsNeedValue = false;
-
-          XFA_Attribute attr =
-              CXFA_Node::NameToAttribute(wsAttrName.AsStringView());
-          if (attr == XFA_Attribute::Unknown)
-            continue;
-
-          if (!bUseAttribute && attr != XFA_Attribute::Name &&
-              attr != XFA_Attribute::Save) {
-            continue;
-          }
-          pXFAChild->JSObject()->SetAttribute(attr, it.second.AsStringView(),
-                                              false);
-        }
-        pXFANode->InsertChild(pXFAChild, nullptr);
-        if (eType == XFA_Element::Validate || eType == XFA_Element::Locale) {
-          if (ePacketID == XFA_PacketType::Config)
-            ParseContentNode(pXFAChild, pXMLElement, ePacketID);
-          else
-            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
-
-          break;
-        }
-        switch (pXFAChild->GetObjectType()) {
-          case XFA_ObjectType::ContentNode:
-          case XFA_ObjectType::TextNode:
-          case XFA_ObjectType::NodeC:
-          case XFA_ObjectType::NodeV:
-            if (IsNeedValue)
-              ParseContentNode(pXFAChild, pXMLElement, ePacketID);
-            break;
-          default:
-            NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute);
-            break;
-        }
-      } break;
-      case FX_XMLNODE_Instruction:
-        ParseInstruction(pXFANode, static_cast<CFX_XMLInstruction*>(pXMLChild),
-                         ePacketID);
-        break;
-      default:
-        break;
-    }
-  }
-  return pXFANode;
-}
-
-void CXFA_SimpleParser::ParseContentNode(CXFA_Node* pXFANode,
-                                         CFX_XMLNode* pXMLNode,
-                                         XFA_PacketType ePacketID) {
-  XFA_Element element = XFA_Element::Sharptext;
-  if (pXFANode->GetElementType() == XFA_Element::ExData) {
-    WideString wsContentType =
-        pXFANode->JSObject()->GetCData(XFA_Attribute::ContentType);
-    if (wsContentType == L"text/html")
-      element = XFA_Element::SharpxHTML;
-    else if (wsContentType == L"text/xml")
-      element = XFA_Element::Sharpxml;
-  }
-  if (element == XFA_Element::SharpxHTML)
-    pXFANode->SetXMLMappingNode(pXMLNode);
-
-  WideString wsValue;
-  for (CFX_XMLNode* pXMLChild = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLChild;
-       pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    FX_XMLNODETYPE eNodeType = pXMLChild->GetType();
-    if (eNodeType == FX_XMLNODE_Instruction)
-      continue;
-
-    if (element == XFA_Element::SharpxHTML) {
-      if (eNodeType != FX_XMLNODE_Element)
-        break;
-
-      if (XFA_RecognizeRichText(static_cast<CFX_XMLElement*>(pXMLChild)))
-        wsValue +=
-            GetPlainTextFromRichText(static_cast<CFX_XMLElement*>(pXMLChild));
-    } else if (element == XFA_Element::Sharpxml) {
-      if (eNodeType != FX_XMLNODE_Element)
-        break;
-
-      ConvertXMLToPlainText(static_cast<CFX_XMLElement*>(pXMLChild), wsValue);
-    } else {
-      if (eNodeType == FX_XMLNODE_Element)
-        break;
-      if (eNodeType == FX_XMLNODE_Text || eNodeType == FX_XMLNODE_CharData)
-        wsValue = static_cast<CFX_XMLText*>(pXMLChild)->GetText();
-    }
-    break;
-  }
-  if (!wsValue.IsEmpty()) {
-    if (pXFANode->IsContentNode()) {
-      CXFA_Node* pContentRawDataNode =
-          m_pFactory->CreateNode(ePacketID, element);
-      ASSERT(pContentRawDataNode);
-      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsValue,
-                                                false, false);
-      pXFANode->InsertChild(pContentRawDataNode, nullptr);
-    } else {
-      pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsValue, false,
-                                     false);
-    }
-  }
-}
-
-void CXFA_SimpleParser::ParseDataGroup(CXFA_Node* pXFANode,
-                                       CFX_XMLNode* pXMLNode,
-                                       XFA_PacketType ePacketID) {
-  for (CFX_XMLNode* pXMLChild = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLChild;
-       pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    switch (pXMLChild->GetType()) {
-      case FX_XMLNODE_Element: {
-        CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild);
-        {
-          WideString wsNamespaceURI = GetElementTagNamespaceURI(pXMLElement);
-          if (wsNamespaceURI == L"http://www.xfa.com/schema/xfa-package/" ||
-              wsNamespaceURI == L"http://www.xfa.org/schema/xfa-package/" ||
-              wsNamespaceURI == L"http://www.w3.org/2001/XMLSchema-instance") {
-            continue;
-          }
-        }
-
-        XFA_Element eNodeType = XFA_Element::DataModel;
-        if (eNodeType == XFA_Element::DataModel) {
-          WideString wsDataNodeAttr;
-          if (FindAttributeWithNS(pXMLElement, L"dataNode",
-                                  L"http://www.xfa.org/schema/xfa-data/1.0/",
-                                  wsDataNodeAttr)) {
-            if (wsDataNodeAttr == L"dataGroup")
-              eNodeType = XFA_Element::DataGroup;
-            else if (wsDataNodeAttr == L"dataValue")
-              eNodeType = XFA_Element::DataValue;
-          }
-        }
-        WideString wsContentType;
-        if (eNodeType == XFA_Element::DataModel) {
-          if (FindAttributeWithNS(pXMLElement, L"contentType",
-                                  L"http://www.xfa.org/schema/xfa-data/1.0/",
-                                  wsContentType)) {
-            if (!wsContentType.IsEmpty())
-              eNodeType = XFA_Element::DataValue;
-          }
-        }
-        if (eNodeType == XFA_Element::DataModel) {
-          for (CFX_XMLNode* pXMLDataChild =
-                   pXMLElement->GetNodeItem(CFX_XMLNode::FirstChild);
-               pXMLDataChild; pXMLDataChild = pXMLDataChild->GetNodeItem(
-                                  CFX_XMLNode::NextSibling)) {
-            if (pXMLDataChild->GetType() == FX_XMLNODE_Element) {
-              if (!XFA_RecognizeRichText(
-                      static_cast<CFX_XMLElement*>(pXMLDataChild))) {
-                eNodeType = XFA_Element::DataGroup;
-                break;
-              }
-            }
-          }
-        }
-        if (eNodeType == XFA_Element::DataModel)
-          eNodeType = XFA_Element::DataValue;
-
-        CXFA_Node* pXFAChild =
-            m_pFactory->CreateNode(XFA_PacketType::Datasets, eNodeType);
-        if (!pXFAChild)
-          return;
-
-        pXFAChild->JSObject()->SetCData(
-            XFA_Attribute::Name, pXMLElement->GetLocalTagName(), false, false);
-        bool bNeedValue = true;
-
-        for (auto it : pXMLElement->GetAttributes()) {
-          WideString wsName;
-          WideString wsNS;
-          if (!ResolveAttribute(pXMLElement, it.first, wsName, wsNS)) {
-            continue;
-          }
-          if (wsName == L"nil" && it.second == L"true") {
-            bNeedValue = false;
-            continue;
-          }
-          if (wsNS == L"http://www.xfa.com/schema/xfa-package/" ||
-              wsNS == L"http://www.xfa.org/schema/xfa-package/" ||
-              wsNS == L"http://www.w3.org/2001/XMLSchema-instance" ||
-              wsNS == L"http://www.xfa.org/schema/xfa-data/1.0/") {
-            continue;
-          }
-          CXFA_Node* pXFAMetaData = m_pFactory->CreateNode(
-              XFA_PacketType::Datasets, XFA_Element::DataValue);
-          if (!pXFAMetaData)
-            return;
-
-          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Name, wsName, false,
-                                             false);
-          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::QualifiedName,
-                                             it.first, false, false);
-          pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Value, it.second,
-                                             false, false);
-          pXFAMetaData->JSObject()->SetEnum(XFA_Attribute::Contains,
-                                            XFA_AttributeEnum::MetaData, false);
-          pXFAChild->InsertChild(pXFAMetaData, nullptr);
-          pXFAMetaData->SetXMLMappingNode(pXMLElement);
-          pXFAMetaData->SetFlag(XFA_NodeFlag_Initialized, false);
-        }
-
-        if (!bNeedValue) {
-          WideString wsNilName(L"xsi:nil");
-          pXMLElement->RemoveAttribute(wsNilName.c_str());
-        }
-        pXFANode->InsertChild(pXFAChild, nullptr);
-        if (eNodeType == XFA_Element::DataGroup)
-          ParseDataGroup(pXFAChild, pXMLElement, ePacketID);
-        else if (bNeedValue)
-          ParseDataValue(pXFAChild, pXMLChild, XFA_PacketType::Datasets);
-
-        pXFAChild->SetXMLMappingNode(pXMLElement);
-        pXFAChild->SetFlag(XFA_NodeFlag_Initialized, false);
-        continue;
-      }
-      case FX_XMLNODE_CharData:
-      case FX_XMLNODE_Text: {
-        CFX_XMLText* pXMLText = static_cast<CFX_XMLText*>(pXMLChild);
-        WideString wsText = pXMLText->GetText();
-        if (IsStringAllWhitespace(wsText))
-          continue;
-
-        CXFA_Node* pXFAChild = m_pFactory->CreateNode(XFA_PacketType::Datasets,
-                                                      XFA_Element::DataValue);
-        if (!pXFAChild)
-          return;
-
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsText, false,
-                                        false);
-        pXFANode->InsertChild(pXFAChild, nullptr);
-        pXFAChild->SetXMLMappingNode(pXMLText);
-        pXFAChild->SetFlag(XFA_NodeFlag_Initialized, false);
-        continue;
-      }
-      default:
-        continue;
-    }
-  }
-}
-
-void CXFA_SimpleParser::ParseDataValue(CXFA_Node* pXFANode,
-                                       CFX_XMLNode* pXMLNode,
-                                       XFA_PacketType ePacketID) {
-  CFX_WideTextBuf wsValueTextBuf;
-  CFX_WideTextBuf wsCurValueTextBuf;
-  bool bMarkAsCompound = false;
-  CFX_XMLNode* pXMLCurValueNode = nullptr;
-  for (CFX_XMLNode* pXMLChild = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
-       pXMLChild;
-       pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) {
-    FX_XMLNODETYPE eNodeType = pXMLChild->GetType();
-    if (eNodeType == FX_XMLNODE_Instruction)
-      continue;
-
-    if (eNodeType == FX_XMLNODE_Text || eNodeType == FX_XMLNODE_CharData) {
-      WideString wsText = static_cast<CFX_XMLText*>(pXMLChild)->GetText();
-      if (!pXMLCurValueNode)
-        pXMLCurValueNode = pXMLChild;
-
-      wsCurValueTextBuf << wsText;
-    } else if (XFA_RecognizeRichText(static_cast<CFX_XMLElement*>(pXMLChild))) {
-      WideString wsText =
-          GetPlainTextFromRichText(static_cast<CFX_XMLElement*>(pXMLChild));
-      if (!pXMLCurValueNode)
-        pXMLCurValueNode = pXMLChild;
-
-      wsCurValueTextBuf << wsText;
-    } else {
-      bMarkAsCompound = true;
-      if (pXMLCurValueNode) {
-        WideString wsCurValue = wsCurValueTextBuf.MakeString();
-        if (!wsCurValue.IsEmpty()) {
-          CXFA_Node* pXFAChild =
-              m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
-          if (!pXFAChild)
-            return;
-
-          pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, L"", false,
-                                          false);
-          pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue,
-                                          false, false);
-          pXFANode->InsertChild(pXFAChild, nullptr);
-          pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
-          pXFAChild->SetFlag(XFA_NodeFlag_Initialized, false);
-          wsValueTextBuf << wsCurValue;
-          wsCurValueTextBuf.Clear();
-        }
-        pXMLCurValueNode = nullptr;
-      }
-      CXFA_Node* pXFAChild =
-          m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
-      if (!pXFAChild)
-        return;
-
-      WideString wsNodeStr =
-          static_cast<CFX_XMLElement*>(pXMLChild)->GetLocalTagName();
-      pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr, false,
-                                      false);
-      ParseDataValue(pXFAChild, pXMLChild, ePacketID);
-      pXFANode->InsertChild(pXFAChild, nullptr);
-      pXFAChild->SetXMLMappingNode(pXMLChild);
-      pXFAChild->SetFlag(XFA_NodeFlag_Initialized, false);
-      WideString wsCurValue =
-          pXFAChild->JSObject()->GetCData(XFA_Attribute::Value);
-      wsValueTextBuf << wsCurValue;
-    }
-  }
-  if (pXMLCurValueNode) {
-    WideString wsCurValue = wsCurValueTextBuf.MakeString();
-    if (!wsCurValue.IsEmpty()) {
-      if (bMarkAsCompound) {
-        CXFA_Node* pXFAChild =
-            m_pFactory->CreateNode(ePacketID, XFA_Element::DataValue);
-        if (!pXFAChild)
-          return;
-
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, L"", false, false);
-        pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue, false,
-                                        false);
-        pXFANode->InsertChild(pXFAChild, nullptr);
-        pXFAChild->SetXMLMappingNode(pXMLCurValueNode);
-        pXFAChild->SetFlag(XFA_NodeFlag_Initialized, false);
-      }
-      wsValueTextBuf << wsCurValue;
-      wsCurValueTextBuf.Clear();
-    }
-    pXMLCurValueNode = nullptr;
-  }
-  WideString wsNodeValue = wsValueTextBuf.MakeString();
-  pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsNodeValue, false,
-                                 false);
-}
-
-void CXFA_SimpleParser::ParseInstruction(CXFA_Node* pXFANode,
-                                         CFX_XMLInstruction* pXMLInstruction,
-                                         XFA_PacketType ePacketID) {
-  if (!m_bDocumentParser)
-    return;
-
-  WideString wsTargetName = pXMLInstruction->GetName();
-  const std::vector<WideString>& target_data = pXMLInstruction->GetTargetData();
-  if (wsTargetName == L"originalXFAVersion") {
-    if (target_data.size() > 1 &&
-        (pXFANode->GetDocument()->RecognizeXFAVersionNumber(target_data[0]) !=
-         XFA_VERSION_UNKNOWN) &&
-        target_data[1] == L"v2.7-scripting:1") {
-      pXFANode->GetDocument()->SetFlag(XFA_DOCFLAG_Scripting, true);
-    }
-  } else if (wsTargetName == L"acrobat") {
-    if (target_data.size() > 1 && target_data[0] == L"JavaScript" &&
-        target_data[1] == L"strictScoping") {
-      pXFANode->GetDocument()->SetFlag(XFA_DOCFLAG_StrictScoping, true);
-    }
-  }
-}
-
-void CXFA_SimpleParser::CloseParser() {
-  m_pXMLDoc.reset();
-  m_pStream.Reset();
-}
diff --git a/xfa/fxfa/parser/cxfa_simple_parser.h b/xfa/fxfa/parser/cxfa_simple_parser.h
deleted file mode 100644
index 7a20de3..0000000
--- a/xfa/fxfa/parser/cxfa_simple_parser.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_SIMPLE_PARSER_H_
-#define XFA_FXFA_PARSER_CXFA_SIMPLE_PARSER_H_
-
-#include <memory>
-
-#include "xfa/fxfa/fxfa_basic.h"
-
-class CXFA_Document;
-class CXFA_Node;
-class CFX_XMLDoc;
-class CFX_XMLInstruction;
-class CFX_XMLNode;
-class CFX_XMLParser;
-class IFX_SeekableStream;
-class CFX_SeekableStreamProxy;
-
-class CXFA_SimpleParser {
- public:
-  CXFA_SimpleParser();
-  explicit CXFA_SimpleParser(CXFA_Document* pFactory);
-  ~CXFA_SimpleParser();
-
-  int32_t StartParse(const RetainPtr<IFX_SeekableStream>& pStream,
-                     XFA_PacketType ePacketID);
-  int32_t DoParse();
-  CFX_XMLNode* ParseXMLData(const ByteString& wsXML);
-  void ConstructXFANode(CXFA_Node* pXFANode, CFX_XMLNode* pXMLNode);
-  CXFA_Node* GetRootNode() const;
-  CFX_XMLDoc* GetXMLDoc() const;
-  void CloseParser();
-
-  // Called later for the ctor with no parameters.
-  void SetFactory(CXFA_Document* pFactory);
-
- private:
-  CXFA_Node* ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode,
-                              XFA_PacketType ePacketID);
-  CXFA_Node* ParseAsXDPPacket_XDP(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Config(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Template(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Form(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_Data(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_LocaleConnectionSourceSet(
-      CFX_XMLNode* pXMLDocumentNode,
-      XFA_PacketType packet_type,
-      XFA_Element element);
-  CXFA_Node* ParseAsXDPPacket_Xdc(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* ParseAsXDPPacket_User(CFX_XMLNode* pXMLDocumentNode);
-  CXFA_Node* NormalLoader(CXFA_Node* pXFANode,
-                          CFX_XMLNode* pXMLDoc,
-                          XFA_PacketType ePacketID,
-                          bool bUseAttribute);
-  CXFA_Node* DataLoader(CXFA_Node* pXFANode,
-                        CFX_XMLNode* pXMLDoc,
-                        bool bDoTransform);
-  CXFA_Node* UserPacketLoader(CXFA_Node* pXFANode, CFX_XMLNode* pXMLDoc);
-  void ParseContentNode(CXFA_Node* pXFANode,
-                        CFX_XMLNode* pXMLNode,
-                        XFA_PacketType ePacketID);
-  void ParseDataValue(CXFA_Node* pXFANode,
-                      CFX_XMLNode* pXMLNode,
-                      XFA_PacketType ePacketID);
-  void ParseDataGroup(CXFA_Node* pXFANode,
-                      CFX_XMLNode* pXMLNode,
-                      XFA_PacketType ePacketID);
-  void ParseInstruction(CXFA_Node* pXFANode,
-                        CFX_XMLInstruction* pXMLInstruction,
-                        XFA_PacketType ePacketID);
-
-  std::unique_ptr<CFX_XMLDoc> m_pXMLDoc;
-  UnownedPtr<CFX_XMLParser> m_pXMLParser;  // Owned by |m_pXMLDoc|
-  RetainPtr<CFX_SeekableStreamProxy> m_pStream;
-  RetainPtr<IFX_SeekableStream> m_pFileRead;
-  UnownedPtr<CXFA_Document> m_pFactory;
-  // TODO(dsinclair): Figure out who owns this.
-  CXFA_Node* m_pRootNode = nullptr;
-  XFA_PacketType m_ePacketID = XFA_PacketType::User;
-  bool m_bParseStarted = false;
-  const bool m_bDocumentParser;
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_SIMPLE_PARSER_H_
diff --git a/xfa/fxfa/parser/cxfa_simple_parser_embeddertest.cpp b/xfa/fxfa/parser/cxfa_simple_parser_embeddertest.cpp
deleted file mode 100644
index 174febf..0000000
--- a/xfa/fxfa/parser/cxfa_simple_parser_embeddertest.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/embedder_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class CXFASimpleParserEmbeddertest : public EmbedderTest {};
-
-TEST_F(CXFASimpleParserEmbeddertest, Bug_216) {
-  EXPECT_TRUE(OpenDocument("bug_216.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  UnloadPage(page);
-}
-
-TEST_F(CXFASimpleParserEmbeddertest, Bug_709793) {
-  EXPECT_TRUE(OpenDocument("bug_709793.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  EXPECT_NE(nullptr, page);
-  UnloadPage(page);
-}
diff --git a/xfa/fxfa/parser/cxfa_soapaction.cpp b/xfa/fxfa/parser/cxfa_soapaction.cpp
index b53a653..514c667 100644
--- a/xfa/fxfa/parser/cxfa_soapaction.cpp
+++ b/xfa/fxfa/parser/cxfa_soapaction.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_soapaction.h"
 
-#include "fxjs/xfa/cjx_soapaction.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSoapActionAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"soapAction";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::SoapAction,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SoapAction>(this)) {}
+                {},
+                kSoapActionAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_SoapAction::~CXFA_SoapAction() {}
+CXFA_SoapAction::~CXFA_SoapAction() = default;
diff --git a/xfa/fxfa/parser/cxfa_soapaction.h b/xfa/fxfa/parser/cxfa_soapaction.h
index b803eae..b7f7526 100644
--- a/xfa/fxfa/parser/cxfa_soapaction.h
+++ b/xfa/fxfa/parser/cxfa_soapaction.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SoapAction : public CXFA_Node {
+class CXFA_SoapAction final : public CXFA_Node {
  public:
   CXFA_SoapAction(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SoapAction() override;
diff --git a/xfa/fxfa/parser/cxfa_soapaddress.cpp b/xfa/fxfa/parser/cxfa_soapaddress.cpp
index 39f91a5..08fd142 100644
--- a/xfa/fxfa/parser/cxfa_soapaddress.cpp
+++ b/xfa/fxfa/parser/cxfa_soapaddress.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_soapaddress.h"
 
-#include "fxjs/xfa/cjx_soapaddress.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSoapAddressAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"soapAddress";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::SoapAddress,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SoapAddress>(this)) {}
+                {},
+                kSoapAddressAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_SoapAddress::~CXFA_SoapAddress() {}
+CXFA_SoapAddress::~CXFA_SoapAddress() = default;
diff --git a/xfa/fxfa/parser/cxfa_soapaddress.h b/xfa/fxfa/parser/cxfa_soapaddress.h
index b0feff4..130ddf3 100644
--- a/xfa/fxfa/parser/cxfa_soapaddress.h
+++ b/xfa/fxfa/parser/cxfa_soapaddress.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SoapAddress : public CXFA_Node {
+class CXFA_SoapAddress final : public CXFA_Node {
  public:
   CXFA_SoapAddress(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SoapAddress() override;
diff --git a/xfa/fxfa/parser/cxfa_solid.cpp b/xfa/fxfa/parser/cxfa_solid.cpp
index 4a20194..3ab0e1d 100644
--- a/xfa/fxfa/parser/cxfa_solid.cpp
+++ b/xfa/fxfa/parser/cxfa_solid.cpp
@@ -6,20 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_solid.h"
 
-#include "fxjs/xfa/cjx_solid.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kSolidPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kSolidAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"solid";
+};
 
 }  // namespace
 
@@ -29,9 +29,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Solid,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Solid>(this)) {}
+                kSolidPropertyData,
+                kSolidAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Solid::~CXFA_Solid() {}
+CXFA_Solid::~CXFA_Solid() = default;
diff --git a/xfa/fxfa/parser/cxfa_solid.h b/xfa/fxfa/parser/cxfa_solid.h
index ec1e2d0..28666ef 100644
--- a/xfa/fxfa/parser/cxfa_solid.h
+++ b/xfa/fxfa/parser/cxfa_solid.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Solid : public CXFA_Node {
+class CXFA_Solid final : public CXFA_Node {
  public:
   CXFA_Solid(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Solid() override;
diff --git a/xfa/fxfa/parser/cxfa_source.cpp b/xfa/fxfa/parser/cxfa_source.cpp
index 95d3736..eb7773c 100644
--- a/xfa/fxfa/parser/cxfa_source.cpp
+++ b/xfa/fxfa/parser/cxfa_source.cpp
@@ -11,16 +11,16 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Connect, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kSourcePropertyData[] = {
+    {XFA_Element::Connect, 1, 0},
+};
+
+const CXFA_Node::AttributeData kSourceAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"source";
+};
 
 }  // namespace
 
@@ -30,9 +30,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Source,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kSourcePropertyData,
+                kSourceAttributeData,
                 pdfium::MakeUnique<CJX_Source>(this)) {}
 
-CXFA_Source::~CXFA_Source() {}
+CXFA_Source::~CXFA_Source() = default;
diff --git a/xfa/fxfa/parser/cxfa_source.h b/xfa/fxfa/parser/cxfa_source.h
index af74be8..96af083 100644
--- a/xfa/fxfa/parser/cxfa_source.h
+++ b/xfa/fxfa/parser/cxfa_source.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Source : public CXFA_Node {
+class CXFA_Source final : public CXFA_Node {
  public:
   CXFA_Source(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Source() override;
diff --git a/xfa/fxfa/parser/cxfa_sourceset.cpp b/xfa/fxfa/parser/cxfa_sourceset.cpp
index 3deabc6..ba66cdf 100644
--- a/xfa/fxfa/parser/cxfa_sourceset.cpp
+++ b/xfa/fxfa/parser/cxfa_sourceset.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_sourceset.h"
 
-#include "fxjs/xfa/cjx_sourceset.h"
+#include "fxjs/xfa/cjx_model.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSourceSetAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"sourceSet";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::SourceSet,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SourceSet>(this)) {}
+                {},
+                kSourceSetAttributeData,
+                pdfium::MakeUnique<CJX_Model>(this)) {}
 
-CXFA_SourceSet::~CXFA_SourceSet() {}
+CXFA_SourceSet::~CXFA_SourceSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_sourceset.h b/xfa/fxfa/parser/cxfa_sourceset.h
index b07e12b..c31b94c 100644
--- a/xfa/fxfa/parser/cxfa_sourceset.h
+++ b/xfa/fxfa/parser/cxfa_sourceset.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SourceSet : public CXFA_Node {
+class CXFA_SourceSet final : public CXFA_Node {
  public:
   CXFA_SourceSet(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SourceSet() override;
diff --git a/xfa/fxfa/parser/cxfa_speak.cpp b/xfa/fxfa/parser/cxfa_speak.cpp
index ce05682..605b929 100644
--- a/xfa/fxfa/parser/cxfa_speak.cpp
+++ b/xfa/fxfa/parser/cxfa_speak.cpp
@@ -6,22 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_speak.h"
 
-#include "fxjs/xfa/cjx_speak.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSpeakAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Rid, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Priority, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Custom},
+     (void*)XFA_AttributeValue::Custom},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Disable, XFA_AttributeType::Boolean, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"speak";
+};
 
 }  // namespace
 
@@ -31,9 +29,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::TextNode,
                 XFA_Element::Speak,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Speak>(this)) {}
+                {},
+                kSpeakAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Speak::~CXFA_Speak() {}
+CXFA_Speak::~CXFA_Speak() = default;
diff --git a/xfa/fxfa/parser/cxfa_speak.h b/xfa/fxfa/parser/cxfa_speak.h
index 80ef54b..5497927 100644
--- a/xfa/fxfa/parser/cxfa_speak.h
+++ b/xfa/fxfa/parser/cxfa_speak.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Speak : public CXFA_Node {
+class CXFA_Speak final : public CXFA_Node {
  public:
   CXFA_Speak(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Speak() override;
diff --git a/xfa/fxfa/parser/cxfa_staple.cpp b/xfa/fxfa/parser/cxfa_staple.cpp
index 31736d1..436518c 100644
--- a/xfa/fxfa/parser/cxfa_staple.cpp
+++ b/xfa/fxfa/parser/cxfa_staple.cpp
@@ -6,16 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_staple.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kStapleAttributeData[] = {
     {XFA_Attribute::Mode, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::UsePrinterSetting},
+     (void*)XFA_AttributeValue::UsePrinterSetting},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"staple";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Staple,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kStapleAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Staple::~CXFA_Staple() {}
+CXFA_Staple::~CXFA_Staple() = default;
diff --git a/xfa/fxfa/parser/cxfa_staple.h b/xfa/fxfa/parser/cxfa_staple.h
index 9651fa4..44ef654 100644
--- a/xfa/fxfa/parser/cxfa_staple.h
+++ b/xfa/fxfa/parser/cxfa_staple.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Staple : public CXFA_Node {
+class CXFA_Staple final : public CXFA_Node {
  public:
   CXFA_Staple(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Staple() override;
diff --git a/xfa/fxfa/parser/cxfa_startnode.cpp b/xfa/fxfa/parser/cxfa_startnode.cpp
index f49b595..f415b29 100644
--- a/xfa/fxfa/parser/cxfa_startnode.cpp
+++ b/xfa/fxfa/parser/cxfa_startnode.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_startnode.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kStartNodeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"startNode";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::StartNode,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kStartNodeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_StartNode::~CXFA_StartNode() {}
+CXFA_StartNode::~CXFA_StartNode() = default;
diff --git a/xfa/fxfa/parser/cxfa_startnode.h b/xfa/fxfa/parser/cxfa_startnode.h
index 51815e5..b8bab06 100644
--- a/xfa/fxfa/parser/cxfa_startnode.h
+++ b/xfa/fxfa/parser/cxfa_startnode.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_StartNode : public CXFA_Node {
+class CXFA_StartNode final : public CXFA_Node {
  public:
   CXFA_StartNode(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_StartNode() override;
diff --git a/xfa/fxfa/parser/cxfa_startpage.cpp b/xfa/fxfa/parser/cxfa_startpage.cpp
index c52daf2..b8e31cd 100644
--- a/xfa/fxfa/parser/cxfa_startpage.cpp
+++ b/xfa/fxfa/parser/cxfa_startpage.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_startpage.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kStartPageAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"startPage";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::StartPage,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kStartPageAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_StartPage::~CXFA_StartPage() {}
+CXFA_StartPage::~CXFA_StartPage() = default;
diff --git a/xfa/fxfa/parser/cxfa_startpage.h b/xfa/fxfa/parser/cxfa_startpage.h
index 6262aa7..ae0ecee 100644
--- a/xfa/fxfa/parser/cxfa_startpage.h
+++ b/xfa/fxfa/parser/cxfa_startpage.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_StartPage : public CXFA_Node {
+class CXFA_StartPage final : public CXFA_Node {
  public:
   CXFA_StartPage(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_StartPage() override;
diff --git a/xfa/fxfa/parser/cxfa_stipple.cpp b/xfa/fxfa/parser/cxfa_stipple.cpp
index f8c059e..024d111 100644
--- a/xfa/fxfa/parser/cxfa_stipple.cpp
+++ b/xfa/fxfa/parser/cxfa_stipple.cpp
@@ -6,23 +6,24 @@
 
 #include "xfa/fxfa/parser/cxfa_stipple.h"
 
-#include "fxjs/xfa/cjx_stipple.h"
+#include "core/fxge/render_defines.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_color.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Color, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kStipplePropertyData[] = {
+    {XFA_Element::Color, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kStippleAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Rate, XFA_AttributeType::Integer, (void*)50},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"stipple";
+};
 
 }  // namespace
 
@@ -32,12 +33,11 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Stipple,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Stipple>(this)) {}
+                kStipplePropertyData,
+                kStippleAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Stipple::~CXFA_Stipple() {}
+CXFA_Stipple::~CXFA_Stipple() = default;
 
 CXFA_Color* CXFA_Stipple::GetColorIfExists() {
   return GetChild<CXFA_Color>(0, XFA_Element::Color, false);
@@ -60,10 +60,10 @@
   CXFA_Color* pColor = GetColorIfExists();
   FX_ARGB crColor = pColor ? pColor->GetValue() : CXFA_Color::kBlackColor;
 
-  int32_t a;
-  FX_COLORREF rgb;
-  std::tie(a, rgb) = ArgbToColorRef(crColor);
-  FX_ARGB cr = ArgbEncode(iRate * a / 100, rgb);
+  int32_t alpha;
+  FX_COLORREF colorref;
+  std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(crColor);
+  FX_ARGB cr = AlphaAndColorRefToArgb(iRate * alpha / 100, colorref);
 
   pGS->SaveGraphState();
   pGS->SetFillColor(CXFA_GEColor(cr));
diff --git a/xfa/fxfa/parser/cxfa_stipple.h b/xfa/fxfa/parser/cxfa_stipple.h
index 839534b..af5581f 100644
--- a/xfa/fxfa/parser/cxfa_stipple.h
+++ b/xfa/fxfa/parser/cxfa_stipple.h
@@ -14,7 +14,7 @@
 class CXFA_Color;
 class CXFA_Graphics;
 
-class CXFA_Stipple : public CXFA_Node {
+class CXFA_Stipple final : public CXFA_Node {
  public:
   static int32_t GetDefaultRate() { return 50; }
 
diff --git a/xfa/fxfa/parser/cxfa_stroke.cpp b/xfa/fxfa/parser/cxfa_stroke.cpp
index 9d5916a..65301c0 100644
--- a/xfa/fxfa/parser/cxfa_stroke.cpp
+++ b/xfa/fxfa/parser/cxfa_stroke.cpp
@@ -17,42 +17,42 @@
 #include "xfa/fxgraphics/cxfa_graphics.h"
 
 void XFA_StrokeTypeSetLineDash(CXFA_Graphics* pGraphics,
-                               XFA_AttributeEnum iStrokeType,
-                               XFA_AttributeEnum iCapType) {
+                               XFA_AttributeValue iStrokeType,
+                               XFA_AttributeValue iCapType) {
   switch (iStrokeType) {
-    case XFA_AttributeEnum::DashDot: {
+    case XFA_AttributeValue::DashDot: {
       float dashArray[] = {4, 1, 2, 1};
-      if (iCapType != XFA_AttributeEnum::Butt) {
+      if (iCapType != XFA_AttributeValue::Butt) {
         dashArray[1] = 2;
         dashArray[3] = 2;
       }
-      pGraphics->SetLineDash(0, dashArray, 4);
+      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
       break;
     }
-    case XFA_AttributeEnum::DashDotDot: {
+    case XFA_AttributeValue::DashDotDot: {
       float dashArray[] = {4, 1, 2, 1, 2, 1};
-      if (iCapType != XFA_AttributeEnum::Butt) {
+      if (iCapType != XFA_AttributeValue::Butt) {
         dashArray[1] = 2;
         dashArray[3] = 2;
         dashArray[5] = 2;
       }
-      pGraphics->SetLineDash(0, dashArray, 6);
+      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
       break;
     }
-    case XFA_AttributeEnum::Dashed: {
+    case XFA_AttributeValue::Dashed: {
       float dashArray[] = {5, 1};
-      if (iCapType != XFA_AttributeEnum::Butt)
+      if (iCapType != XFA_AttributeValue::Butt)
         dashArray[1] = 2;
 
-      pGraphics->SetLineDash(0, dashArray, 2);
+      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
       break;
     }
-    case XFA_AttributeEnum::Dotted: {
+    case XFA_AttributeValue::Dotted: {
       float dashArray[] = {2, 1};
-      if (iCapType != XFA_AttributeEnum::Butt)
+      if (iCapType != XFA_AttributeValue::Butt)
         dashArray[1] = 2;
 
-      pGraphics->SetLineDash(0, dashArray, 2);
+      pGraphics->SetLineDash(0, dashArray, FX_ArraySize(dashArray));
       break;
     }
     default:
@@ -66,9 +66,8 @@
                          uint32_t validPackets,
                          XFA_ObjectType oType,
                          XFA_Element eType,
-                         const PropertyData* properties,
-                         const AttributeData* attributes,
-                         const WideStringView& elementName,
+                         pdfium::span<const PropertyData> properties,
+                         pdfium::span<const AttributeData> attributes,
                          std::unique_ptr<CJX_Object> js_node)
     : CXFA_Node(pDoc,
                 ePacket,
@@ -77,23 +76,22 @@
                 eType,
                 properties,
                 attributes,
-                elementName,
                 std::move(js_node)) {}
 
 CXFA_Stroke::~CXFA_Stroke() = default;
 
 bool CXFA_Stroke::IsVisible() {
-  XFA_AttributeEnum presence = JSObject()
-                                   ->TryEnum(XFA_Attribute::Presence, true)
-                                   .value_or(XFA_AttributeEnum::Visible);
-  return presence == XFA_AttributeEnum::Visible;
+  XFA_AttributeValue presence = JSObject()
+                                    ->TryEnum(XFA_Attribute::Presence, true)
+                                    .value_or(XFA_AttributeValue::Visible);
+  return presence == XFA_AttributeValue::Visible;
 }
 
-XFA_AttributeEnum CXFA_Stroke::GetCapType() {
+XFA_AttributeValue CXFA_Stroke::GetCapType() {
   return JSObject()->GetEnum(XFA_Attribute::Cap);
 }
 
-XFA_AttributeEnum CXFA_Stroke::GetStrokeType() {
+XFA_AttributeValue CXFA_Stroke::GetStrokeType() {
   return JSObject()->GetEnum(XFA_Attribute::Stroke);
 }
 
@@ -134,7 +132,7 @@
                               false);
 }
 
-XFA_AttributeEnum CXFA_Stroke::GetJoinType() {
+XFA_AttributeValue CXFA_Stroke::GetJoinType() {
   return JSObject()->GetEnum(XFA_Attribute::Join);
 }
 
@@ -186,7 +184,7 @@
   pGS->SetLineWidth(fThickness);
   pGS->EnableActOnDash();
   pGS->SetLineCap(CFX_GraphStateData::LineCapButt);
-  XFA_StrokeTypeSetLineDash(pGS, GetStrokeType(), XFA_AttributeEnum::Butt);
+  XFA_StrokeTypeSetLineDash(pGS, GetStrokeType(), XFA_AttributeValue::Butt);
   pGS->SetStrokeColor(CXFA_GEColor(GetColor()));
   pGS->StrokePath(pPath, &matrix);
   pGS->RestoreGraphState();
diff --git a/xfa/fxfa/parser/cxfa_stroke.h b/xfa/fxfa/parser/cxfa_stroke.h
index ed41997..c87bc50 100644
--- a/xfa/fxfa/parser/cxfa_stroke.h
+++ b/xfa/fxfa/parser/cxfa_stroke.h
@@ -23,8 +23,8 @@
 class CXFA_Node;
 
 void XFA_StrokeTypeSetLineDash(CXFA_Graphics* pGraphics,
-                               XFA_AttributeEnum iStrokeType,
-                               XFA_AttributeEnum iCapType);
+                               XFA_AttributeValue iStrokeType,
+                               XFA_AttributeValue iCapType);
 
 class CXFA_Stroke : public CXFA_Node {
  public:
@@ -34,9 +34,9 @@
   bool IsVisible();
   bool IsInverted();
 
-  XFA_AttributeEnum GetCapType();
-  XFA_AttributeEnum GetStrokeType();
-  XFA_AttributeEnum GetJoinType();
+  XFA_AttributeValue GetCapType();
+  XFA_AttributeValue GetStrokeType();
+  XFA_AttributeValue GetJoinType();
   float GetRadius() const;
   float GetThickness() const;
 
@@ -56,9 +56,8 @@
               uint32_t validPackets,
               XFA_ObjectType oType,
               XFA_Element eType,
-              const PropertyData* properties,
-              const AttributeData* attributes,
-              const WideStringView& elementName,
+              pdfium::span<const PropertyData> properties,
+              pdfium::span<const AttributeData> attributes,
               std::unique_ptr<CJX_Object> js_node);
 };
 
diff --git a/xfa/fxfa/parser/cxfa_subform.cpp b/xfa/fxfa/parser/cxfa_subform.cpp
index 7bb7fa7..37d99ec 100644
--- a/xfa/fxfa/parser/cxfa_subform.cpp
+++ b/xfa/fxfa/parser/cxfa_subform.cpp
@@ -11,7 +11,7 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kSubformPropertyData[] = {
     {XFA_Element::Break, 1, 0},   {XFA_Element::Margin, 1, 0},
     {XFA_Element::Para, 1, 0},    {XFA_Element::Border, 1, 0},
     {XFA_Element::Assist, 1, 0},  {XFA_Element::Traversal, 1, 0},
@@ -20,46 +20,46 @@
     {XFA_Element::Bind, 1, 0},    {XFA_Element::Desc, 1, 0},
     {XFA_Element::Bookend, 1, 0}, {XFA_Element::Calculate, 1, 0},
     {XFA_Element::Extras, 1, 0},  {XFA_Element::Variables, 1, 0},
-    {XFA_Element::Occur, 1, 0},   {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::Occur, 1, 0},
+};
+
+const CXFA_Node::AttributeData kSubformAttributeData[] = {
     {XFA_Attribute::H, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::W, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::X, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Y, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Left},
+     (void*)XFA_AttributeValue::Left},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AllowMacro, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::ColumnWidths, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Access, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Open},
+     (void*)XFA_AttributeValue::Open},
     {XFA_Attribute::Presence, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Visible},
+     (void*)XFA_AttributeValue::Visible},
     {XFA_Attribute::VAlign, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Top},
+     (void*)XFA_AttributeValue::Top},
     {XFA_Attribute::MaxH, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MaxW, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MinH, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::MinW, XFA_AttributeType::Measure, (void*)L"0in"},
     {XFA_Attribute::Layout, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Position},
+     (void*)XFA_AttributeValue::Position},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::MergeMode, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::ConsumeData},
+     (void*)XFA_AttributeValue::ConsumeData},
     {XFA_Attribute::ColSpan, XFA_AttributeType::Integer, (void*)1},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Locale, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AnchorType, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::TopLeft},
+     (void*)XFA_AttributeValue::TopLeft},
     {XFA_Attribute::RestoreState, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Manual},
+     (void*)XFA_AttributeValue::Manual},
     {XFA_Attribute::Scope, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Name},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"subform";
+     (void*)XFA_AttributeValue::Name},
+};
 
 }  // namespace
 
@@ -69,9 +69,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Subform,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kSubformPropertyData,
+                kSubformAttributeData,
                 pdfium::MakeUnique<CJX_Subform>(this)) {}
 
-CXFA_Subform::~CXFA_Subform() {}
+CXFA_Subform::~CXFA_Subform() = default;
diff --git a/xfa/fxfa/parser/cxfa_subform.h b/xfa/fxfa/parser/cxfa_subform.h
index 00b2e17..3d16d10 100644
--- a/xfa/fxfa/parser/cxfa_subform.h
+++ b/xfa/fxfa/parser/cxfa_subform.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Subform : public CXFA_Node {
+class CXFA_Subform final : public CXFA_Node {
  public:
   CXFA_Subform(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Subform() override;
diff --git a/xfa/fxfa/parser/cxfa_subformset.cpp b/xfa/fxfa/parser/cxfa_subformset.cpp
index 0c9c3b0..d1f6ba5 100644
--- a/xfa/fxfa/parser/cxfa_subformset.cpp
+++ b/xfa/fxfa/parser/cxfa_subformset.cpp
@@ -6,27 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_subformset.h"
 
-#include "fxjs/xfa/cjx_subformset.h"
+#include "fxjs/xfa/cjx_container.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kSubformSetPropertyData[] = {
     {XFA_Element::Break, 1, 0},  {XFA_Element::Overflow, 1, 0},
     {XFA_Element::Desc, 1, 0},   {XFA_Element::Bookend, 1, 0},
     {XFA_Element::Extras, 1, 0}, {XFA_Element::Occur, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kSubformSetAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Relation, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Ordered},
+     (void*)XFA_AttributeValue::Ordered},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"subformSet";
+};
 
 }  // namespace
 
@@ -36,9 +35,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::SubformSet,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SubformSet>(this)) {}
+                kSubformSetPropertyData,
+                kSubformSetAttributeData,
+                pdfium::MakeUnique<CJX_Container>(this)) {}
 
-CXFA_SubformSet::~CXFA_SubformSet() {}
+CXFA_SubformSet::~CXFA_SubformSet() = default;
diff --git a/xfa/fxfa/parser/cxfa_subformset.h b/xfa/fxfa/parser/cxfa_subformset.h
index 8e0f3ea..12b9e9f 100644
--- a/xfa/fxfa/parser/cxfa_subformset.h
+++ b/xfa/fxfa/parser/cxfa_subformset.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SubformSet : public CXFA_Node {
+class CXFA_SubformSet final : public CXFA_Node {
  public:
   CXFA_SubformSet(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SubformSet() override;
diff --git a/xfa/fxfa/parser/cxfa_subjectdn.cpp b/xfa/fxfa/parser/cxfa_subjectdn.cpp
index 29ab2fa..1130739 100644
--- a/xfa/fxfa/parser/cxfa_subjectdn.cpp
+++ b/xfa/fxfa/parser/cxfa_subjectdn.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_subjectdn.h"
 
-#include "fxjs/xfa/cjx_subjectdn.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSubjectDNAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Delimiter, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"subjectDN";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::NodeC,
                 XFA_Element::SubjectDN,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SubjectDN>(this)) {}
+                {},
+                kSubjectDNAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SubjectDN::~CXFA_SubjectDN() {}
+CXFA_SubjectDN::~CXFA_SubjectDN() = default;
diff --git a/xfa/fxfa/parser/cxfa_subjectdn.h b/xfa/fxfa/parser/cxfa_subjectdn.h
index 0861d75..a4d94d7 100644
--- a/xfa/fxfa/parser/cxfa_subjectdn.h
+++ b/xfa/fxfa/parser/cxfa_subjectdn.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SubjectDN : public CXFA_Node {
+class CXFA_SubjectDN final : public CXFA_Node {
  public:
   CXFA_SubjectDN(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SubjectDN() override;
diff --git a/xfa/fxfa/parser/cxfa_subjectdns.cpp b/xfa/fxfa/parser/cxfa_subjectdns.cpp
index 8f714b0..524bc96 100644
--- a/xfa/fxfa/parser/cxfa_subjectdns.cpp
+++ b/xfa/fxfa/parser/cxfa_subjectdns.cpp
@@ -6,20 +6,18 @@
 
 #include "xfa/fxfa/parser/cxfa_subjectdns.h"
 
-#include "fxjs/xfa/cjx_subjectdns.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSubjectDNsAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"subjectDNs";
+};
 
 }  // namespace
 
@@ -29,9 +27,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::SubjectDNs,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_SubjectDNs>(this)) {}
+                {},
+                kSubjectDNsAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SubjectDNs::~CXFA_SubjectDNs() {}
+CXFA_SubjectDNs::~CXFA_SubjectDNs() = default;
diff --git a/xfa/fxfa/parser/cxfa_subjectdns.h b/xfa/fxfa/parser/cxfa_subjectdns.h
index e9d739c..03d770b 100644
--- a/xfa/fxfa/parser/cxfa_subjectdns.h
+++ b/xfa/fxfa/parser/cxfa_subjectdns.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SubjectDNs : public CXFA_Node {
+class CXFA_SubjectDNs final : public CXFA_Node {
  public:
   CXFA_SubjectDNs(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SubjectDNs() override;
diff --git a/xfa/fxfa/parser/cxfa_submit.cpp b/xfa/fxfa/parser/cxfa_submit.cpp
index e4558e8..82eed83 100644
--- a/xfa/fxfa/parser/cxfa_submit.cpp
+++ b/xfa/fxfa/parser/cxfa_submit.cpp
@@ -6,26 +6,26 @@
 
 #include "xfa/fxfa/parser/cxfa_submit.h"
 
-#include "fxjs/xfa/cjx_submit.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Encrypt, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kSubmitPropertyData[] = {
+    {XFA_Element::Encrypt, 1, 0},
+};
+
+const CXFA_Node::AttributeData kSubmitAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Format, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Xdp},
+     (void*)XFA_AttributeValue::Xdp},
     {XFA_Attribute::EmbedPDF, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Target, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::TextEncoding, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::XdpContent, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"submit";
+};
 
 }  // namespace
 
@@ -35,18 +35,17 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Submit,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Submit>(this)) {}
+                kSubmitPropertyData,
+                kSubmitAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Submit::~CXFA_Submit() {}
+CXFA_Submit::~CXFA_Submit() = default;
 
 bool CXFA_Submit::IsSubmitEmbedPDF() {
   return JSObject()->GetBoolean(XFA_Attribute::EmbedPDF);
 }
 
-XFA_AttributeEnum CXFA_Submit::GetSubmitFormat() {
+XFA_AttributeValue CXFA_Submit::GetSubmitFormat() {
   return JSObject()->GetEnum(XFA_Attribute::Format);
 }
 
diff --git a/xfa/fxfa/parser/cxfa_submit.h b/xfa/fxfa/parser/cxfa_submit.h
index 24d5f11..13cad86 100644
--- a/xfa/fxfa/parser/cxfa_submit.h
+++ b/xfa/fxfa/parser/cxfa_submit.h
@@ -10,13 +10,13 @@
 #include "core/fxcrt/widestring.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Submit : public CXFA_Node {
+class CXFA_Submit final : public CXFA_Node {
  public:
   CXFA_Submit(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Submit() override;
 
   bool IsSubmitEmbedPDF();
-  XFA_AttributeEnum GetSubmitFormat();
+  XFA_AttributeValue GetSubmitFormat();
   WideString GetSubmitTarget();
   WideString GetSubmitXDPContent();
 };
diff --git a/xfa/fxfa/parser/cxfa_submitformat.cpp b/xfa/fxfa/parser/cxfa_submitformat.cpp
index 30daf89..551b92b 100644
--- a/xfa/fxfa/parser/cxfa_submitformat.cpp
+++ b/xfa/fxfa/parser/cxfa_submitformat.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_submitformat.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSubmitFormatAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"submitFormat";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SubmitFormat,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSubmitFormatAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SubmitFormat::~CXFA_SubmitFormat() {}
+CXFA_SubmitFormat::~CXFA_SubmitFormat() = default;
diff --git a/xfa/fxfa/parser/cxfa_submitformat.h b/xfa/fxfa/parser/cxfa_submitformat.h
index adca1e9..bd6df26 100644
--- a/xfa/fxfa/parser/cxfa_submitformat.h
+++ b/xfa/fxfa/parser/cxfa_submitformat.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SubmitFormat : public CXFA_Node {
+class CXFA_SubmitFormat final : public CXFA_Node {
  public:
   CXFA_SubmitFormat(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SubmitFormat() override;
diff --git a/xfa/fxfa/parser/cxfa_submiturl.cpp b/xfa/fxfa/parser/cxfa_submiturl.cpp
index 591dd99..3212acb 100644
--- a/xfa/fxfa/parser/cxfa_submiturl.cpp
+++ b/xfa/fxfa/parser/cxfa_submiturl.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_submiturl.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSubmitUrlAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"submitUrl";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SubmitUrl,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSubmitUrlAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SubmitUrl::~CXFA_SubmitUrl() {}
+CXFA_SubmitUrl::~CXFA_SubmitUrl() = default;
diff --git a/xfa/fxfa/parser/cxfa_submiturl.h b/xfa/fxfa/parser/cxfa_submiturl.h
index 8d87db9..f1914db 100644
--- a/xfa/fxfa/parser/cxfa_submiturl.h
+++ b/xfa/fxfa/parser/cxfa_submiturl.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SubmitUrl : public CXFA_Node {
+class CXFA_SubmitUrl final : public CXFA_Node {
  public:
   CXFA_SubmitUrl(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SubmitUrl() override;
diff --git a/xfa/fxfa/parser/cxfa_subsetbelow.cpp b/xfa/fxfa/parser/cxfa_subsetbelow.cpp
index 90dbd06..ed179c9 100644
--- a/xfa/fxfa/parser/cxfa_subsetbelow.cpp
+++ b/xfa/fxfa/parser/cxfa_subsetbelow.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_subsetbelow.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSubsetBelowAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"subsetBelow";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SubsetBelow,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSubsetBelowAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SubsetBelow::~CXFA_SubsetBelow() {}
+CXFA_SubsetBelow::~CXFA_SubsetBelow() = default;
diff --git a/xfa/fxfa/parser/cxfa_subsetbelow.h b/xfa/fxfa/parser/cxfa_subsetbelow.h
index db912fe..230a563 100644
--- a/xfa/fxfa/parser/cxfa_subsetbelow.h
+++ b/xfa/fxfa/parser/cxfa_subsetbelow.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SubsetBelow : public CXFA_Node {
+class CXFA_SubsetBelow final : public CXFA_Node {
  public:
   CXFA_SubsetBelow(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SubsetBelow() override;
diff --git a/xfa/fxfa/parser/cxfa_suppressbanner.cpp b/xfa/fxfa/parser/cxfa_suppressbanner.cpp
index 46c42a5..3371170 100644
--- a/xfa/fxfa/parser/cxfa_suppressbanner.cpp
+++ b/xfa/fxfa/parser/cxfa_suppressbanner.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_suppressbanner.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kSuppressBannerAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"suppressBanner";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::SuppressBanner,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kSuppressBannerAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_SuppressBanner::~CXFA_SuppressBanner() {}
+CXFA_SuppressBanner::~CXFA_SuppressBanner() = default;
diff --git a/xfa/fxfa/parser/cxfa_suppressbanner.h b/xfa/fxfa/parser/cxfa_suppressbanner.h
index 79013a2..40057d0 100644
--- a/xfa/fxfa/parser/cxfa_suppressbanner.h
+++ b/xfa/fxfa/parser/cxfa_suppressbanner.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_SuppressBanner : public CXFA_Node {
+class CXFA_SuppressBanner final : public CXFA_Node {
  public:
   CXFA_SuppressBanner(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_SuppressBanner() override;
diff --git a/xfa/fxfa/parser/cxfa_tagged.cpp b/xfa/fxfa/parser/cxfa_tagged.cpp
index 0f46b62..940b12c 100644
--- a/xfa/fxfa/parser/cxfa_tagged.cpp
+++ b/xfa/fxfa/parser/cxfa_tagged.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_tagged.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTaggedAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"tagged";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Tagged,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kTaggedAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Tagged::~CXFA_Tagged() {}
+CXFA_Tagged::~CXFA_Tagged() = default;
diff --git a/xfa/fxfa/parser/cxfa_tagged.h b/xfa/fxfa/parser/cxfa_tagged.h
index 50b1ba4..ff54423 100644
--- a/xfa/fxfa/parser/cxfa_tagged.h
+++ b/xfa/fxfa/parser/cxfa_tagged.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Tagged : public CXFA_Node {
+class CXFA_Tagged final : public CXFA_Node {
  public:
   CXFA_Tagged(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Tagged() override;
diff --git a/xfa/fxfa/parser/cxfa_template.cpp b/xfa/fxfa/parser/cxfa_template.cpp
index 3f9afa1..bc5c444 100644
--- a/xfa/fxfa/parser/cxfa_template.cpp
+++ b/xfa/fxfa/parser/cxfa_template.cpp
@@ -11,19 +11,18 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kTemplatePropertyData[] = {
     {XFA_Element::Uri, 1, 0},       {XFA_Element::Xsl, 1, 0},
     {XFA_Element::StartPage, 1, 0}, {XFA_Element::Relevant, 1, 0},
     {XFA_Element::Base, 1, 0},      {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kTemplateAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::BaseProfile, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Full},
+     (void*)XFA_AttributeValue::Full},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"template";
+};
 
 }  // namespace
 
@@ -34,9 +33,8 @@
           (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
           XFA_ObjectType::ModelNode,
           XFA_Element::Template,
-          kPropertyData,
-          kAttributeData,
-          kName,
+          kTemplatePropertyData,
+          kTemplateAttributeData,
           pdfium::MakeUnique<CJX_Template>(this)) {}
 
-CXFA_Template::~CXFA_Template() {}
+CXFA_Template::~CXFA_Template() = default;
diff --git a/xfa/fxfa/parser/cxfa_template.h b/xfa/fxfa/parser/cxfa_template.h
index 34b035f..fa9b999 100644
--- a/xfa/fxfa/parser/cxfa_template.h
+++ b/xfa/fxfa/parser/cxfa_template.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Template : public CXFA_Node {
+class CXFA_Template final : public CXFA_Node {
  public:
   CXFA_Template(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Template() override;
diff --git a/xfa/fxfa/parser/cxfa_templatecache.cpp b/xfa/fxfa/parser/cxfa_templatecache.cpp
index e22ed65..bfc19b3 100644
--- a/xfa/fxfa/parser/cxfa_templatecache.cpp
+++ b/xfa/fxfa/parser/cxfa_templatecache.cpp
@@ -6,15 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_templatecache.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTemplateCacheAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::MaxEntries, XFA_AttributeType::Integer, (void*)5},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"templateCache";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::TemplateCache,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kTemplateCacheAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_TemplateCache::~CXFA_TemplateCache() {}
+CXFA_TemplateCache::~CXFA_TemplateCache() = default;
diff --git a/xfa/fxfa/parser/cxfa_templatecache.h b/xfa/fxfa/parser/cxfa_templatecache.h
index 45953fe..dd0136d 100644
--- a/xfa/fxfa/parser/cxfa_templatecache.h
+++ b/xfa/fxfa/parser/cxfa_templatecache.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_TemplateCache : public CXFA_Node {
+class CXFA_TemplateCache final : public CXFA_Node {
  public:
   CXFA_TemplateCache(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_TemplateCache() override;
diff --git a/xfa/fxfa/parser/cxfa_text.cpp b/xfa/fxfa/parser/cxfa_text.cpp
index e000493..bb921a0 100644
--- a/xfa/fxfa/parser/cxfa_text.cpp
+++ b/xfa/fxfa/parser/cxfa_text.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_text.h"
 
-#include "fxjs/xfa/cjx_text.h"
+#include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTextAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Rid, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::MaxChars, XFA_AttributeType::Integer, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"text";
+};
 
 }  // namespace
 
@@ -31,12 +29,11 @@
                  XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Text,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Text>(this)) {}
+                {},
+                kTextAttributeData,
+                pdfium::MakeUnique<CJX_Object>(this)) {}
 
-CXFA_Text::~CXFA_Text() {}
+CXFA_Text::~CXFA_Text() = default;
 
 WideString CXFA_Text::GetContent() {
   return JSObject()->GetContent(false);
diff --git a/xfa/fxfa/parser/cxfa_text.h b/xfa/fxfa/parser/cxfa_text.h
index a75aef2..811ce1a 100644
--- a/xfa/fxfa/parser/cxfa_text.h
+++ b/xfa/fxfa/parser/cxfa_text.h
@@ -10,7 +10,7 @@
 #include "core/fxcrt/fx_string.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Text : public CXFA_Node {
+class CXFA_Text final : public CXFA_Node {
  public:
   CXFA_Text(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Text() override;
diff --git a/xfa/fxfa/parser/cxfa_textedit.cpp b/xfa/fxfa/parser/cxfa_textedit.cpp
index 3b2767a..7ff47c7 100644
--- a/xfa/fxfa/parser/cxfa_textedit.cpp
+++ b/xfa/fxfa/parser/cxfa_textedit.cpp
@@ -6,29 +6,29 @@
 
 #include "xfa/fxfa/parser/cxfa_textedit.h"
 
-#include "fxjs/xfa/cjx_textedit.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Margin, 1, 0},
-                                                 {XFA_Element::Border, 1, 0},
-                                                 {XFA_Element::Comb, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kTextEditPropertyData[] = {
+    {XFA_Element::Margin, 1, 0},
+    {XFA_Element::Border, 1, 0},
+    {XFA_Element::Comb, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kTextEditAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::VScrollPolicy, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
+     (void*)XFA_AttributeValue::Auto},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::AllowRichText, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::MultiLine, XFA_AttributeType::Boolean, (void*)0},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::HScrollPolicy, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Auto},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"textEdit";
+     (void*)XFA_AttributeValue::Auto},
+};
 
 }  // namespace
 
@@ -38,9 +38,12 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::TextEdit,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_TextEdit>(this)) {}
+                kTextEditPropertyData,
+                kTextEditAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_TextEdit::~CXFA_TextEdit() {}
+CXFA_TextEdit::~CXFA_TextEdit() = default;
+
+XFA_FFWidgetType CXFA_TextEdit::GetDefaultFFWidgetType() const {
+  return XFA_FFWidgetType::kTextEdit;
+}
diff --git a/xfa/fxfa/parser/cxfa_textedit.h b/xfa/fxfa/parser/cxfa_textedit.h
index 972ede4..d4cafa4 100644
--- a/xfa/fxfa/parser/cxfa_textedit.h
+++ b/xfa/fxfa/parser/cxfa_textedit.h
@@ -9,10 +9,12 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_TextEdit : public CXFA_Node {
+class CXFA_TextEdit final : public CXFA_Node {
  public:
   CXFA_TextEdit(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_TextEdit() override;
+
+  XFA_FFWidgetType GetDefaultFFWidgetType() const override;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TEXTEDIT_H_
diff --git a/xfa/fxfa/parser/cxfa_thisproxy.cpp b/xfa/fxfa/parser/cxfa_thisproxy.cpp
index 314c98c..3654409 100644
--- a/xfa/fxfa/parser/cxfa_thisproxy.cpp
+++ b/xfa/fxfa/parser/cxfa_thisproxy.cpp
@@ -12,11 +12,10 @@
 
 CXFA_ThisProxy::CXFA_ThisProxy(CXFA_Node* pThisNode, CXFA_Node* pScriptNode)
     : CXFA_Object(pThisNode->GetDocument(),
-                  XFA_ObjectType::VariablesThis,
-                  XFA_Element::Unknown,
-                  WideStringView(),
+                  XFA_ObjectType::ThisProxy,
+                  XFA_Element::Object,
                   pdfium::MakeUnique<CJX_Object>(this)),
       m_pThisNode(pThisNode),
       m_pScriptNode(pScriptNode) {}
 
-CXFA_ThisProxy::~CXFA_ThisProxy() {}
+CXFA_ThisProxy::~CXFA_ThisProxy() = default;
diff --git a/xfa/fxfa/parser/cxfa_thisproxy.h b/xfa/fxfa/parser/cxfa_thisproxy.h
index 4bb0f5a..4ec6e4a 100644
--- a/xfa/fxfa/parser/cxfa_thisproxy.h
+++ b/xfa/fxfa/parser/cxfa_thisproxy.h
@@ -7,21 +7,22 @@
 #ifndef XFA_FXFA_PARSER_CXFA_THISPROXY_H_
 #define XFA_FXFA_PARSER_CXFA_THISPROXY_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 
 class CXFA_Node;
 
-class CXFA_ThisProxy : public CXFA_Object {
+class CXFA_ThisProxy final : public CXFA_Object {
  public:
   CXFA_ThisProxy(CXFA_Node* pThisNode, CXFA_Node* pScriptNode);
   ~CXFA_ThisProxy() override;
 
-  CXFA_Node* GetThisNode() const { return m_pThisNode; }
-  CXFA_Node* GetScriptNode() const { return m_pScriptNode; }
+  CXFA_Node* GetThisNode() const { return m_pThisNode.Get(); }
+  CXFA_Node* GetScriptNode() const { return m_pScriptNode.Get(); }
 
  private:
-  CXFA_Node* m_pThisNode;
-  CXFA_Node* m_pScriptNode;
+  UnownedPtr<CXFA_Node> m_pThisNode;
+  UnownedPtr<CXFA_Node> m_pScriptNode;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_THISPROXY_H_
diff --git a/xfa/fxfa/parser/cxfa_threshold.cpp b/xfa/fxfa/parser/cxfa_threshold.cpp
index 2047d52..3d1863a 100644
--- a/xfa/fxfa/parser/cxfa_threshold.cpp
+++ b/xfa/fxfa/parser/cxfa_threshold.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_threshold.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kThresholdAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"threshold";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Threshold,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kThresholdAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Threshold::~CXFA_Threshold() {}
+CXFA_Threshold::~CXFA_Threshold() = default;
diff --git a/xfa/fxfa/parser/cxfa_threshold.h b/xfa/fxfa/parser/cxfa_threshold.h
index 4bde01a..597b134 100644
--- a/xfa/fxfa/parser/cxfa_threshold.h
+++ b/xfa/fxfa/parser/cxfa_threshold.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Threshold : public CXFA_Node {
+class CXFA_Threshold final : public CXFA_Node {
  public:
   CXFA_Threshold(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Threshold() override;
diff --git a/xfa/fxfa/parser/cxfa_time.cpp b/xfa/fxfa/parser/cxfa_time.cpp
index f3a64ce..ea33713 100644
--- a/xfa/fxfa/parser/cxfa_time.cpp
+++ b/xfa/fxfa/parser/cxfa_time.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_time.h"
 
-#include "fxjs/xfa/cjx_time.h"
+#include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTimeAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"time";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Time,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Time>(this)) {}
+                {},
+                kTimeAttributeData,
+                pdfium::MakeUnique<CJX_Object>(this)) {}
 
-CXFA_Time::~CXFA_Time() {}
+CXFA_Time::~CXFA_Time() = default;
diff --git a/xfa/fxfa/parser/cxfa_time.h b/xfa/fxfa/parser/cxfa_time.h
index efc6d02..c189bc5 100644
--- a/xfa/fxfa/parser/cxfa_time.h
+++ b/xfa/fxfa/parser/cxfa_time.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Time : public CXFA_Node {
+class CXFA_Time final : public CXFA_Node {
  public:
   CXFA_Time(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Time() override;
diff --git a/xfa/fxfa/parser/cxfa_timepattern.cpp b/xfa/fxfa/parser/cxfa_timepattern.cpp
index 76e3a53..9264da2 100644
--- a/xfa/fxfa/parser/cxfa_timepattern.cpp
+++ b/xfa/fxfa/parser/cxfa_timepattern.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_timepattern.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTimePatternAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Med},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"timePattern";
+     (void*)XFA_AttributeValue::Med},
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::TimePattern,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kTimePatternAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_TimePattern::~CXFA_TimePattern() {}
+CXFA_TimePattern::~CXFA_TimePattern() = default;
diff --git a/xfa/fxfa/parser/cxfa_timepattern.h b/xfa/fxfa/parser/cxfa_timepattern.h
index f796047..7c150a9 100644
--- a/xfa/fxfa/parser/cxfa_timepattern.h
+++ b/xfa/fxfa/parser/cxfa_timepattern.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_TimePattern : public CXFA_Node {
+class CXFA_TimePattern final : public CXFA_Node {
  public:
   CXFA_TimePattern(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_TimePattern() override;
diff --git a/xfa/fxfa/parser/cxfa_timepatterns.cpp b/xfa/fxfa/parser/cxfa_timepatterns.cpp
index c3a8dde..1c92303 100644
--- a/xfa/fxfa/parser/cxfa_timepatterns.cpp
+++ b/xfa/fxfa/parser/cxfa_timepatterns.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_timepatterns.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kTimePatternsPropertyData[] = {
     {XFA_Element::TimePattern, 4, 0},
-    {XFA_Element::Unknown, 0, 0}};
-
-constexpr wchar_t kName[] = L"timePatterns";
+};
 
 }  // namespace
 
@@ -22,8 +23,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::TimePatterns,
-                kPropertyData,
-                nullptr,
-                kName) {}
+                kTimePatternsPropertyData,
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_TimePatterns::~CXFA_TimePatterns() {}
+CXFA_TimePatterns::~CXFA_TimePatterns() = default;
diff --git a/xfa/fxfa/parser/cxfa_timepatterns.h b/xfa/fxfa/parser/cxfa_timepatterns.h
index be9337d..13c05c8 100644
--- a/xfa/fxfa/parser/cxfa_timepatterns.h
+++ b/xfa/fxfa/parser/cxfa_timepatterns.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_TimePatterns : public CXFA_Node {
+class CXFA_TimePatterns final : public CXFA_Node {
  public:
   CXFA_TimePatterns(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_TimePatterns() override;
diff --git a/xfa/fxfa/parser/cxfa_timestamp.cpp b/xfa/fxfa/parser/cxfa_timestamp.cpp
index 1c86961..68f1c50 100644
--- a/xfa/fxfa/parser/cxfa_timestamp.cpp
+++ b/xfa/fxfa/parser/cxfa_timestamp.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_timestamp.h"
 
-#include "fxjs/xfa/cjx_timestamp.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTimeStampAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Type, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Optional},
+     (void*)XFA_AttributeValue::Optional},
     {XFA_Attribute::Server, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"timeStamp";
+};
 
 }  // namespace
 
@@ -30,8 +28,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::TimeStamp,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kTimeStampAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_TimeStamp::~CXFA_TimeStamp() {}
+CXFA_TimeStamp::~CXFA_TimeStamp() = default;
diff --git a/xfa/fxfa/parser/cxfa_timestamp.h b/xfa/fxfa/parser/cxfa_timestamp.h
index 7db73e9..6a6c1ac 100644
--- a/xfa/fxfa/parser/cxfa_timestamp.h
+++ b/xfa/fxfa/parser/cxfa_timestamp.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_TimeStamp : public CXFA_Node {
+class CXFA_TimeStamp final : public CXFA_Node {
  public:
   CXFA_TimeStamp(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_TimeStamp() override;
diff --git a/xfa/fxfa/parser/cxfa_timezoneprovider.cpp b/xfa/fxfa/parser/cxfa_timezoneprovider.cpp
index ff5ecce..0469563 100644
--- a/xfa/fxfa/parser/cxfa_timezoneprovider.cpp
+++ b/xfa/fxfa/parser/cxfa_timezoneprovider.cpp
@@ -8,10 +8,12 @@
 
 #include <time.h>
 
+#include "build/build_config.h"
+
 static bool g_bProviderTimeZoneSet = false;
 
 CXFA_TimeZoneProvider::CXFA_TimeZoneProvider() {
-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
+#if defined(OS_WIN)
   if (!g_bProviderTimeZoneSet) {
     g_bProviderTimeZoneSet = true;
     _tzset();
diff --git a/xfa/fxfa/parser/cxfa_to.cpp b/xfa/fxfa/parser/cxfa_to.cpp
index a30c015..cb400c0 100644
--- a/xfa/fxfa/parser/cxfa_to.cpp
+++ b/xfa/fxfa/parser/cxfa_to.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_to.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kToAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"to";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::To,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kToAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_To::~CXFA_To() {}
+CXFA_To::~CXFA_To() = default;
diff --git a/xfa/fxfa/parser/cxfa_to.h b/xfa/fxfa/parser/cxfa_to.h
index f418f59..8510bc2 100644
--- a/xfa/fxfa/parser/cxfa_to.h
+++ b/xfa/fxfa/parser/cxfa_to.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_To : public CXFA_Node {
+class CXFA_To final : public CXFA_Node {
  public:
   CXFA_To(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_To() override;
diff --git a/xfa/fxfa/parser/cxfa_tooltip.cpp b/xfa/fxfa/parser/cxfa_tooltip.cpp
index 04f27c9..2fecedd 100644
--- a/xfa/fxfa/parser/cxfa_tooltip.cpp
+++ b/xfa/fxfa/parser/cxfa_tooltip.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_tooltip.h"
 
-#include "fxjs/xfa/cjx_tooltip.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kToolTipAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Rid, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"toolTip";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::TextNode,
                 XFA_Element::ToolTip,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_ToolTip>(this)) {}
+                {},
+                kToolTipAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_ToolTip::~CXFA_ToolTip() {}
+CXFA_ToolTip::~CXFA_ToolTip() = default;
diff --git a/xfa/fxfa/parser/cxfa_tooltip.h b/xfa/fxfa/parser/cxfa_tooltip.h
index 0b13282..f46ca62 100644
--- a/xfa/fxfa/parser/cxfa_tooltip.h
+++ b/xfa/fxfa/parser/cxfa_tooltip.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ToolTip : public CXFA_Node {
+class CXFA_ToolTip final : public CXFA_Node {
  public:
   CXFA_ToolTip(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ToolTip() override;
diff --git a/xfa/fxfa/parser/cxfa_trace.cpp b/xfa/fxfa/parser/cxfa_trace.cpp
index 7de01fc..741805d 100644
--- a/xfa/fxfa/parser/cxfa_trace.cpp
+++ b/xfa/fxfa/parser/cxfa_trace.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_trace.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTraceAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"trace";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Trace,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kTraceAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Trace::~CXFA_Trace() {}
+CXFA_Trace::~CXFA_Trace() = default;
diff --git a/xfa/fxfa/parser/cxfa_trace.h b/xfa/fxfa/parser/cxfa_trace.h
index dec95da..1d76f73 100644
--- a/xfa/fxfa/parser/cxfa_trace.h
+++ b/xfa/fxfa/parser/cxfa_trace.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Trace : public CXFA_Node {
+class CXFA_Trace final : public CXFA_Node {
  public:
   CXFA_Trace(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Trace() override;
diff --git a/xfa/fxfa/parser/cxfa_transform.cpp b/xfa/fxfa/parser/cxfa_transform.cpp
index 5bfa476..e00ddc3 100644
--- a/xfa/fxfa/parser/cxfa_transform.cpp
+++ b/xfa/fxfa/parser/cxfa_transform.cpp
@@ -6,20 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_transform.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kTransformPropertyData[] = {
     {XFA_Element::Whitespace, 1, 0},  {XFA_Element::Rename, 1, 0},
     {XFA_Element::IfEmpty, 1, 0},     {XFA_Element::Presence, 1, 0},
     {XFA_Element::Picture, 1, 0},     {XFA_Element::NameAttr, 1, 0},
-    {XFA_Element::GroupParent, 1, 0}, {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+    {XFA_Element::GroupParent, 1, 0},
+};
+
+const CXFA_Node::AttributeData kTransformAttributeData[] = {
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"transform";
+};
 
 }  // namespace
 
@@ -29,8 +32,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Transform,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kTransformPropertyData,
+                kTransformAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Transform::~CXFA_Transform() {}
+CXFA_Transform::~CXFA_Transform() = default;
diff --git a/xfa/fxfa/parser/cxfa_transform.h b/xfa/fxfa/parser/cxfa_transform.h
index fde54e6..6409c6c 100644
--- a/xfa/fxfa/parser/cxfa_transform.h
+++ b/xfa/fxfa/parser/cxfa_transform.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Transform : public CXFA_Node {
+class CXFA_Transform final : public CXFA_Node {
  public:
   CXFA_Transform(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Transform() override;
diff --git a/xfa/fxfa/parser/cxfa_traversal.cpp b/xfa/fxfa/parser/cxfa_traversal.cpp
index b5ffd3f..ad367e9 100644
--- a/xfa/fxfa/parser/cxfa_traversal.cpp
+++ b/xfa/fxfa/parser/cxfa_traversal.cpp
@@ -6,20 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_traversal.h"
 
-#include "fxjs/xfa/cjx_traversal.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kTraversalPropertyData[] = {
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kTraversalAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"traversal";
+};
 
 }  // namespace
 
@@ -29,9 +29,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Traversal,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Traversal>(this)) {}
+                kTraversalPropertyData,
+                kTraversalAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Traversal::~CXFA_Traversal() {}
+CXFA_Traversal::~CXFA_Traversal() = default;
diff --git a/xfa/fxfa/parser/cxfa_traversal.h b/xfa/fxfa/parser/cxfa_traversal.h
index 47422b2..154211b 100644
--- a/xfa/fxfa/parser/cxfa_traversal.h
+++ b/xfa/fxfa/parser/cxfa_traversal.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Traversal : public CXFA_Node {
+class CXFA_Traversal final : public CXFA_Node {
  public:
   CXFA_Traversal(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Traversal() override;
diff --git a/xfa/fxfa/parser/cxfa_traverse.cpp b/xfa/fxfa/parser/cxfa_traverse.cpp
index d771b37..7afc59f 100644
--- a/xfa/fxfa/parser/cxfa_traverse.cpp
+++ b/xfa/fxfa/parser/cxfa_traverse.cpp
@@ -6,24 +6,24 @@
 
 #include "xfa/fxfa/parser/cxfa_traverse.h"
 
-#include "fxjs/xfa/cjx_traverse.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Script, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kTraversePropertyData[] = {
+    {XFA_Element::Script, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+
+const CXFA_Node::AttributeData kTraverseAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Ref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Operation, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Next},
+     (void*)XFA_AttributeValue::Next},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"traverse";
+};
 
 }  // namespace
 
@@ -33,9 +33,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Traverse,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Traverse>(this)) {}
+                kTraversePropertyData,
+                kTraverseAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
 CXFA_Traverse::~CXFA_Traverse() {}
diff --git a/xfa/fxfa/parser/cxfa_traverse.h b/xfa/fxfa/parser/cxfa_traverse.h
index de33cb7..dec5b89 100644
--- a/xfa/fxfa/parser/cxfa_traverse.h
+++ b/xfa/fxfa/parser/cxfa_traverse.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Traverse : public CXFA_Node {
+class CXFA_Traverse final : public CXFA_Node {
  public:
   CXFA_Traverse(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Traverse() override;
diff --git a/xfa/fxfa/parser/cxfa_traversestrategy_contentareacontainerlayoutitem.h b/xfa/fxfa/parser/cxfa_traversestrategy_contentareacontainerlayoutitem.h
deleted file mode 100644
index e0a584e..0000000
--- a/xfa/fxfa/parser/cxfa_traversestrategy_contentareacontainerlayoutitem.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_CONTENTAREACONTAINERLAYOUTITEM_H_
-#define XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_CONTENTAREACONTAINERLAYOUTITEM_H_
-
-#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
-
-class CXFA_TraverseStrategy_ContentAreaContainerLayoutItem {
- public:
-  static CXFA_ContainerLayoutItem* GetFirstChild(
-      CXFA_ContainerLayoutItem* pLayoutItem) {
-    for (CXFA_LayoutItem* pChildItem = pLayoutItem->m_pFirstChild; pChildItem;
-         pChildItem = pChildItem->m_pNextSibling) {
-      if (CXFA_ContainerLayoutItem* pContainer =
-              pChildItem->AsContainerLayoutItem()) {
-        return pContainer;
-      }
-    }
-    return nullptr;
-  }
-
-  static CXFA_ContainerLayoutItem* GetNextSibling(
-      CXFA_ContainerLayoutItem* pLayoutItem) {
-    for (CXFA_LayoutItem* pChildItem = pLayoutItem->m_pNextSibling; pChildItem;
-         pChildItem = pChildItem->m_pNextSibling) {
-      if (CXFA_ContainerLayoutItem* pContainer =
-              pChildItem->AsContainerLayoutItem()) {
-        return pContainer;
-      }
-    }
-    return nullptr;
-  }
-
-  static CXFA_ContainerLayoutItem* GetParent(
-      CXFA_ContainerLayoutItem* pLayoutItem) {
-    return static_cast<CXFA_ContainerLayoutItem*>(pLayoutItem->m_pParent);
-  }
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_CONTENTAREACONTAINERLAYOUTITEM_H_
diff --git a/xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h b/xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h
deleted file mode 100644
index de0d52a..0000000
--- a/xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_CONTENTLAYOUTITEM_H_
-#define XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_CONTENTLAYOUTITEM_H_
-
-#include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
-
-class CXFA_TraverseStrategy_ContentLayoutItem {
- public:
-  static CXFA_ContentLayoutItem* GetFirstChild(
-      CXFA_ContentLayoutItem* pLayoutItem) {
-    return static_cast<CXFA_ContentLayoutItem*>(pLayoutItem->m_pFirstChild);
-  }
-
-  static CXFA_ContentLayoutItem* GetNextSibling(
-      CXFA_ContentLayoutItem* pLayoutItem) {
-    return static_cast<CXFA_ContentLayoutItem*>(pLayoutItem->m_pNextSibling);
-  }
-
-  static CXFA_ContentLayoutItem* GetParent(
-      CXFA_ContentLayoutItem* pLayoutItem) {
-    return static_cast<CXFA_ContentLayoutItem*>(pLayoutItem->m_pParent);
-  }
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_CONTENTLAYOUTITEM_H_
diff --git a/xfa/fxfa/parser/cxfa_traversestrategy_layoutitem.h b/xfa/fxfa/parser/cxfa_traversestrategy_layoutitem.h
deleted file mode 100644
index 7b39826..0000000
--- a/xfa/fxfa/parser/cxfa_traversestrategy_layoutitem.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
-#define XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
-
-#include "xfa/fxfa/parser/cxfa_layoutitem.h"
-
-class CXFA_TraverseStrategy_LayoutItem {
- public:
-  static CXFA_LayoutItem* GetFirstChild(CXFA_LayoutItem* pLayoutItem) {
-    return pLayoutItem->m_pFirstChild;
-  }
-  static CXFA_LayoutItem* GetNextSibling(CXFA_LayoutItem* pLayoutItem) {
-    return pLayoutItem->m_pNextSibling;
-  }
-  static CXFA_LayoutItem* GetParent(CXFA_LayoutItem* pLayoutItem) {
-    return pLayoutItem->m_pParent;
-  }
-};
-
-#endif  // XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_LAYOUTITEM_H_
diff --git a/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h b/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h
index 3ca632a..d867f9f 100644
--- a/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h
+++ b/xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h
@@ -23,8 +23,8 @@
   }
 };
 
-typedef CXFA_NodeIteratorTemplate<CXFA_Node,
-                                  CXFA_TraverseStrategy_XFAContainerNode>
-    CXFA_ContainerIterator;
+using CXFA_ContainerIterator =
+    CXFA_NodeIteratorTemplate<CXFA_Node,
+                              CXFA_TraverseStrategy_XFAContainerNode>;
 
 #endif  // XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_XFACONTAINERNODE_H_
diff --git a/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h b/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h
index fc98d18..90f39a8 100644
--- a/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h
+++ b/xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h
@@ -22,7 +22,7 @@
   }
 };
 
-typedef CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
-    CXFA_NodeIterator;
+using CXFA_NodeIterator =
+    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>;
 
 #endif  // XFA_FXFA_PARSER_CXFA_TRAVERSESTRATEGY_XFANODE_H_
diff --git a/xfa/fxfa/parser/cxfa_treelist.cpp b/xfa/fxfa/parser/cxfa_treelist.cpp
index 5db9ecb..2dbec60 100644
--- a/xfa/fxfa/parser/cxfa_treelist.cpp
+++ b/xfa/fxfa/parser/cxfa_treelist.cpp
@@ -9,23 +9,19 @@
 #include <memory>
 
 #include "core/fxcrt/fx_extension.h"
-#include "fxjs/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_treelist.h"
-#include "third_party/base/numerics/safe_conversions.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_list.h"
+#include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 
 CXFA_TreeList::CXFA_TreeList(CXFA_Document* pDocument)
     : CXFA_List(pDocument,
                 XFA_ObjectType::TreeList,
                 XFA_Element::TreeList,
-                WideStringView(L"treeList"),
                 pdfium::MakeUnique<CJX_TreeList>(this)) {}
 
-CXFA_TreeList::~CXFA_TreeList() {}
+CXFA_TreeList::~CXFA_TreeList() = default;
 
-CXFA_Node* CXFA_TreeList::NamedItem(const WideStringView& wsName) {
+CXFA_Node* CXFA_TreeList::NamedItem(WideStringView wsName) {
   uint32_t dwHashCode = FX_HashCode_GetW(wsName, false);
   size_t count = GetLength();
   for (size_t i = 0; i < count; i++) {
diff --git a/xfa/fxfa/parser/cxfa_treelist.h b/xfa/fxfa/parser/cxfa_treelist.h
index 3c65ca5..a194e32 100644
--- a/xfa/fxfa/parser/cxfa_treelist.h
+++ b/xfa/fxfa/parser/cxfa_treelist.h
@@ -7,8 +7,7 @@
 #ifndef XFA_FXFA_PARSER_CXFA_TREELIST_H_
 #define XFA_FXFA_PARSER_CXFA_TREELIST_H_
 
-#include "fxjs/xfa/cjx_treelist.h"
-#include "xfa/fxfa/fxfa_basic.h"
+#include "core/fxcrt/fx_string.h"
 #include "xfa/fxfa/parser/cxfa_list.h"
 
 class CXFA_Node;
@@ -18,7 +17,7 @@
   explicit CXFA_TreeList(CXFA_Document* pDocument);
   ~CXFA_TreeList() override;
 
-  CXFA_Node* NamedItem(const WideStringView& wsName);
+  CXFA_Node* NamedItem(WideStringView wsName);
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_TREELIST_H_
diff --git a/xfa/fxfa/parser/cxfa_type.cpp b/xfa/fxfa/parser/cxfa_type.cpp
index ac1c193..ba36158 100644
--- a/xfa/fxfa/parser/cxfa_type.cpp
+++ b/xfa/fxfa/parser/cxfa_type.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_type.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTypeAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"type";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Type,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kTypeAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Type::~CXFA_Type() {}
+CXFA_Type::~CXFA_Type() = default;
diff --git a/xfa/fxfa/parser/cxfa_type.h b/xfa/fxfa/parser/cxfa_type.h
index 5e7d467..ef696fd 100644
--- a/xfa/fxfa/parser/cxfa_type.h
+++ b/xfa/fxfa/parser/cxfa_type.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Type : public CXFA_Node {
+class CXFA_Type final : public CXFA_Node {
  public:
   CXFA_Type(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Type() override;
diff --git a/xfa/fxfa/parser/cxfa_typeface.cpp b/xfa/fxfa/parser/cxfa_typeface.cpp
index 3bf69bd..0d2c2dc 100644
--- a/xfa/fxfa/parser/cxfa_typeface.cpp
+++ b/xfa/fxfa/parser/cxfa_typeface.cpp
@@ -6,13 +6,14 @@
 
 #include "xfa/fxfa/parser/cxfa_typeface.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kTypefaceAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"typeface";
+};
 
 }  // namespace
 
@@ -22,8 +23,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Typeface,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kTypefaceAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Typeface::~CXFA_Typeface() {}
+CXFA_Typeface::~CXFA_Typeface() = default;
diff --git a/xfa/fxfa/parser/cxfa_typeface.h b/xfa/fxfa/parser/cxfa_typeface.h
index cbc90c4..7b07cae 100644
--- a/xfa/fxfa/parser/cxfa_typeface.h
+++ b/xfa/fxfa/parser/cxfa_typeface.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Typeface : public CXFA_Node {
+class CXFA_Typeface final : public CXFA_Node {
  public:
   CXFA_Typeface(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Typeface() override;
diff --git a/xfa/fxfa/parser/cxfa_typefaces.cpp b/xfa/fxfa/parser/cxfa_typefaces.cpp
index 525919d..dfc35e0 100644
--- a/xfa/fxfa/parser/cxfa_typefaces.cpp
+++ b/xfa/fxfa/parser/cxfa_typefaces.cpp
@@ -6,11 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_typefaces.h"
 
-namespace {
-
-constexpr wchar_t kName[] = L"typefaces";
-
-}  // namespace
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
 
 CXFA_Typefaces::CXFA_Typefaces(CXFA_Document* doc, XFA_PacketType packet)
     : CXFA_Node(doc,
@@ -18,8 +15,8 @@
                 XFA_XDPPACKET_LocaleSet,
                 XFA_ObjectType::Node,
                 XFA_Element::Typefaces,
-                nullptr,
-                nullptr,
-                kName) {}
+                {},
+                {},
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Typefaces::~CXFA_Typefaces() {}
+CXFA_Typefaces::~CXFA_Typefaces() = default;
diff --git a/xfa/fxfa/parser/cxfa_typefaces.h b/xfa/fxfa/parser/cxfa_typefaces.h
index fa4b43f..b65c57d 100644
--- a/xfa/fxfa/parser/cxfa_typefaces.h
+++ b/xfa/fxfa/parser/cxfa_typefaces.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Typefaces : public CXFA_Node {
+class CXFA_Typefaces final : public CXFA_Node {
  public:
   CXFA_Typefaces(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Typefaces() override;
diff --git a/xfa/fxfa/parser/cxfa_ui.cpp b/xfa/fxfa/parser/cxfa_ui.cpp
index 883e79a..1630a3d 100644
--- a/xfa/fxfa/parser/cxfa_ui.cpp
+++ b/xfa/fxfa/parser/cxfa_ui.cpp
@@ -6,12 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_ui.h"
 
-#include "fxjs/xfa/cjx_ui.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kUiPropertyData[] = {
     {XFA_Element::CheckButton, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::ChoiceList, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::DefaultUi, 1, XFA_PROPERTYFLAG_OneOf},
@@ -24,16 +24,14 @@
     {XFA_Element::NumericEdit, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Signature, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::TextEdit, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::ExObject, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Extras, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kUiAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"ui";
+};
 
 }  // namespace
 
@@ -43,9 +41,18 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Ui,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Ui>(this)) {}
+                kUiPropertyData,
+                kUiAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Ui::~CXFA_Ui() {}
+CXFA_Ui::~CXFA_Ui() = default;
+
+bool CXFA_Ui::IsAOneOfChild(CXFA_Node* child) const {
+  for (auto& prop : kUiPropertyData) {
+    if (prop.property != child->GetElementType())
+      continue;
+    if (!!(prop.flags & XFA_PROPERTYFLAG_OneOf))
+      return true;
+  }
+  return false;
+}
diff --git a/xfa/fxfa/parser/cxfa_ui.h b/xfa/fxfa/parser/cxfa_ui.h
index 0824d6b..d479e95 100644
--- a/xfa/fxfa/parser/cxfa_ui.h
+++ b/xfa/fxfa/parser/cxfa_ui.h
@@ -9,10 +9,12 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Ui : public CXFA_Node {
+class CXFA_Ui final : public CXFA_Node {
  public:
   CXFA_Ui(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Ui() override;
+
+  bool IsAOneOfChild(CXFA_Node* child) const;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_UI_H_
diff --git a/xfa/fxfa/parser/cxfa_update.cpp b/xfa/fxfa/parser/cxfa_update.cpp
index 986e110..6150ff5 100644
--- a/xfa/fxfa/parser/cxfa_update.cpp
+++ b/xfa/fxfa/parser/cxfa_update.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_update.h"
 
-#include "fxjs/xfa/cjx_update.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kUpdateAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"update";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::Update,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Update>(this)) {}
+                {},
+                kUpdateAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Update::~CXFA_Update() {}
+CXFA_Update::~CXFA_Update() = default;
diff --git a/xfa/fxfa/parser/cxfa_update.h b/xfa/fxfa/parser/cxfa_update.h
index 9e652ea..3e6bb6c 100644
--- a/xfa/fxfa/parser/cxfa_update.h
+++ b/xfa/fxfa/parser/cxfa_update.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Update : public CXFA_Node {
+class CXFA_Update final : public CXFA_Node {
  public:
   CXFA_Update(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Update() override;
diff --git a/xfa/fxfa/parser/cxfa_uri.cpp b/xfa/fxfa/parser/cxfa_uri.cpp
index 96cc3ec..79d1ea6 100644
--- a/xfa/fxfa/parser/cxfa_uri.cpp
+++ b/xfa/fxfa/parser/cxfa_uri.cpp
@@ -6,21 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_uri.h"
 
-#include "fxjs/xfa/cjx_uri.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kUriAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"uri";
+};
 
 }  // namespace
 
@@ -30,9 +28,8 @@
                 (XFA_XDPPACKET_Config | XFA_XDPPACKET_ConnectionSet),
                 XFA_ObjectType::TextNode,
                 XFA_Element::Uri,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Uri>(this)) {}
+                {},
+                kUriAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_Uri::~CXFA_Uri() {}
+CXFA_Uri::~CXFA_Uri() = default;
diff --git a/xfa/fxfa/parser/cxfa_uri.h b/xfa/fxfa/parser/cxfa_uri.h
index bb31596..96252fc 100644
--- a/xfa/fxfa/parser/cxfa_uri.h
+++ b/xfa/fxfa/parser/cxfa_uri.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Uri : public CXFA_Node {
+class CXFA_Uri final : public CXFA_Node {
  public:
   CXFA_Uri(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Uri() override;
diff --git a/xfa/fxfa/parser/cxfa_user.cpp b/xfa/fxfa/parser/cxfa_user.cpp
index 09ec608..fb36d34 100644
--- a/xfa/fxfa/parser/cxfa_user.cpp
+++ b/xfa/fxfa/parser/cxfa_user.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_user.h"
 
-#include "fxjs/xfa/cjx_user.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kUserAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"user";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_SourceSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::User,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_User>(this)) {}
+                {},
+                kUserAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_User::~CXFA_User() {}
+CXFA_User::~CXFA_User() = default;
diff --git a/xfa/fxfa/parser/cxfa_user.h b/xfa/fxfa/parser/cxfa_user.h
index 3aad173..eca1a37 100644
--- a/xfa/fxfa/parser/cxfa_user.h
+++ b/xfa/fxfa/parser/cxfa_user.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_User : public CXFA_Node {
+class CXFA_User final : public CXFA_Node {
  public:
   CXFA_User(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_User() override;
diff --git a/xfa/fxfa/parser/cxfa_validate.cpp b/xfa/fxfa/parser/cxfa_validate.cpp
index 868e805..d65b170 100644
--- a/xfa/fxfa/parser/cxfa_validate.cpp
+++ b/xfa/fxfa/parser/cxfa_validate.cpp
@@ -6,35 +6,36 @@
 
 #include "xfa/fxfa/parser/cxfa_validate.h"
 
+#include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "fxjs/xfa/cjx_validate.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_message.h"
 #include "xfa/fxfa/parser/cxfa_picture.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Message, 1, 0},
-                                                 {XFA_Element::Picture, 1, 0},
-                                                 {XFA_Element::Script, 1, 0},
-                                                 {XFA_Element::Extras, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kValidatePropertyData[] = {
+    {XFA_Element::Message, 1, 0},
+    {XFA_Element::Picture, 1, 0},
+    {XFA_Element::Script, 1, 0},
+    {XFA_Element::Extras, 1, 0},
+};
+const CXFA_Node::AttributeData kValidateAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::ScriptTest, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Error},
+     (void*)XFA_AttributeValue::Error},
     {XFA_Attribute::NullTest, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Disabled},
+     (void*)XFA_AttributeValue::Disabled},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::FormatTest, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Warning},
+     (void*)XFA_AttributeValue::Warning},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
+};
 
-constexpr wchar_t kName[] = L"validate";
 constexpr wchar_t kFormatTest[] = L"formatTest";
 constexpr wchar_t kNullTest[] = L"nullTest";
 constexpr wchar_t kScriptTest[] = L"scriptTest";
@@ -48,29 +49,28 @@
           (XFA_XDPPACKET_Config | XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
           XFA_ObjectType::ContentNode,
           XFA_Element::Validate,
-          kPropertyData,
-          kAttributeData,
-          kName,
-          pdfium::MakeUnique<CJX_Validate>(this)) {}
+          kValidatePropertyData,
+          kValidateAttributeData,
+          pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Validate::~CXFA_Validate() {}
+CXFA_Validate::~CXFA_Validate() = default;
 
-XFA_AttributeEnum CXFA_Validate::GetFormatTest() {
+XFA_AttributeValue CXFA_Validate::GetFormatTest() {
   return JSObject()->GetEnum(XFA_Attribute::FormatTest);
 }
 
 void CXFA_Validate::SetNullTest(const WideString& wsValue) {
-  Optional<XFA_AttributeEnum> item =
-      CXFA_Node::NameToAttributeEnum(wsValue.AsStringView());
+  Optional<XFA_AttributeValue> item =
+      XFA_GetAttributeValueByName(wsValue.AsStringView());
   JSObject()->SetEnum(XFA_Attribute::NullTest,
-                      item ? *item : XFA_AttributeEnum::Disabled, false);
+                      item ? *item : XFA_AttributeValue::Disabled, false);
 }
 
-XFA_AttributeEnum CXFA_Validate::GetNullTest() {
+XFA_AttributeValue CXFA_Validate::GetNullTest() {
   return JSObject()->GetEnum(XFA_Attribute::NullTest);
 }
 
-XFA_AttributeEnum CXFA_Validate::GetScriptTest() {
+XFA_AttributeValue CXFA_Validate::GetScriptTest() {
   return JSObject()->GetEnum(XFA_Attribute::ScriptTest);
 }
 
@@ -78,7 +78,7 @@
   CXFA_Message* pNode =
       JSObject()->GetProperty<CXFA_Message>(0, XFA_Element::Message);
   if (!pNode)
-    return L"";
+    return WideString();
 
   for (CXFA_Node* pItemNode = pNode->GetFirstChild(); pItemNode;
        pItemNode = pItemNode->GetNextSibling()) {
@@ -89,7 +89,7 @@
     if (wsName.IsEmpty() || wsName == wsMessageType)
       return pItemNode->JSObject()->GetContent(false);
   }
-  return L"";
+  return WideString();
 }
 
 void CXFA_Validate::SetFormatMessageText(const WideString& wsMessage) {
@@ -137,7 +137,7 @@
   }
 
   CXFA_Node* pTextNode = pNode->CreateSamePacketNode(XFA_Element::Text);
-  pNode->InsertChild(pTextNode, nullptr);
+  pNode->InsertChildAndNotify(pTextNode, nullptr);
   pTextNode->JSObject()->SetCData(XFA_Attribute::Name, wsMessageType, false,
                                   false);
   pTextNode->JSObject()->SetContent(wsMessage, wsMessage, false, false, true);
@@ -145,7 +145,7 @@
 
 WideString CXFA_Validate::GetPicture() {
   CXFA_Picture* pNode = GetChild<CXFA_Picture>(0, XFA_Element::Picture, false);
-  return pNode ? pNode->JSObject()->GetContent(false) : L"";
+  return pNode ? pNode->JSObject()->GetContent(false) : WideString();
 }
 
 CXFA_Script* CXFA_Validate::GetScriptIfExists() {
diff --git a/xfa/fxfa/parser/cxfa_validate.h b/xfa/fxfa/parser/cxfa_validate.h
index a601308..0018cf9 100644
--- a/xfa/fxfa/parser/cxfa_validate.h
+++ b/xfa/fxfa/parser/cxfa_validate.h
@@ -11,22 +11,22 @@
 
 class CXFA_Script;
 
-class CXFA_Validate : public CXFA_Node {
+class CXFA_Validate final : public CXFA_Node {
  public:
   CXFA_Validate(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Validate() override;
 
-  XFA_AttributeEnum GetFormatTest();
+  XFA_AttributeValue GetFormatTest();
   WideString GetFormatMessageText();
   void SetFormatMessageText(const WideString& wsMessage);
 
-  XFA_AttributeEnum GetNullTest();
+  XFA_AttributeValue GetNullTest();
   void SetNullTest(const WideString& wsValue);
 
   WideString GetNullMessageText();
   void SetNullMessageText(const WideString& wsMessage);
 
-  XFA_AttributeEnum GetScriptTest();
+  XFA_AttributeValue GetScriptTest();
   WideString GetScriptMessageText();
   void SetScriptMessageText(const WideString& wsMessage);
 
diff --git a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp
index f51131e..d0aa5fd 100644
--- a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp
+++ b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_validateapprovalsignatures.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kValidateApprovalSignaturesAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"validateApprovalSignatures";
+};
 
 }  // namespace
 
@@ -25,8 +26,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::ValidateApprovalSignatures,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kValidateApprovalSignaturesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ValidateApprovalSignatures::~CXFA_ValidateApprovalSignatures() {}
+CXFA_ValidateApprovalSignatures::~CXFA_ValidateApprovalSignatures() = default;
diff --git a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h
index d7dac98..0504168 100644
--- a/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h
+++ b/xfa/fxfa/parser/cxfa_validateapprovalsignatures.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ValidateApprovalSignatures : public CXFA_Node {
+class CXFA_ValidateApprovalSignatures final : public CXFA_Node {
  public:
   CXFA_ValidateApprovalSignatures(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ValidateApprovalSignatures() override;
diff --git a/xfa/fxfa/parser/cxfa_validationmessaging.cpp b/xfa/fxfa/parser/cxfa_validationmessaging.cpp
index 196c98c..687f37c 100644
--- a/xfa/fxfa/parser/cxfa_validationmessaging.cpp
+++ b/xfa/fxfa/parser/cxfa_validationmessaging.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_validationmessaging.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kValidationMessagingAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"validationMessaging";
+};
 
 }  // namespace
 
@@ -24,8 +25,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::ValidationMessaging,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kValidationMessagingAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ValidationMessaging::~CXFA_ValidationMessaging() {}
+CXFA_ValidationMessaging::~CXFA_ValidationMessaging() = default;
diff --git a/xfa/fxfa/parser/cxfa_validationmessaging.h b/xfa/fxfa/parser/cxfa_validationmessaging.h
index 081b24c..1131788 100644
--- a/xfa/fxfa/parser/cxfa_validationmessaging.h
+++ b/xfa/fxfa/parser/cxfa_validationmessaging.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ValidationMessaging : public CXFA_Node {
+class CXFA_ValidationMessaging final : public CXFA_Node {
  public:
   CXFA_ValidationMessaging(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ValidationMessaging() override;
diff --git a/xfa/fxfa/parser/cxfa_value.cpp b/xfa/fxfa/parser/cxfa_value.cpp
index 2cd0dd9..979eabc 100644
--- a/xfa/fxfa/parser/cxfa_value.cpp
+++ b/xfa/fxfa/parser/cxfa_value.cpp
@@ -6,8 +6,8 @@
 
 #include "xfa/fxfa/parser/cxfa_value.h"
 
+#include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "fxjs/xfa/cjx_value.h"
 #include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_arc.h"
 #include "xfa/fxfa/parser/cxfa_exdata.h"
@@ -17,7 +17,7 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kValuePropertyData[] = {
     {XFA_Element::Arc, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Text, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Time, 1, XFA_PROPERTYFLAG_OneOf},
@@ -31,16 +31,15 @@
     {XFA_Element::Date, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Float, 1, XFA_PROPERTYFLAG_OneOf},
     {XFA_Element::Line, 1, XFA_PROPERTYFLAG_OneOf},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kValueAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Relevant, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Override, XFA_AttributeType::Boolean, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"value";
+};
 
 }  // namespace
 
@@ -50,12 +49,11 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::Node,
                 XFA_Element::Value,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Value>(this)) {}
+                kValuePropertyData,
+                kValueAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Value::~CXFA_Value() {}
+CXFA_Value::~CXFA_Value() = default;
 
 XFA_Element CXFA_Value::GetChildValueClassID() const {
   CXFA_Node* pNode = GetFirstChild();
@@ -64,41 +62,49 @@
 
 WideString CXFA_Value::GetChildValueContent() const {
   CXFA_Node* pNode = GetFirstChild();
-  return pNode ? pNode->JSObject()->TryContent(false, true).value_or(L"") : L"";
+  return pNode
+             ? pNode->JSObject()->TryContent(false, true).value_or(WideString())
+             : WideString();
 }
 
 CXFA_Arc* CXFA_Value::GetArcIfExists() const {
   CXFA_Node* node = GetFirstChild();
-  ASSERT(!node || node->GetElementType() == XFA_Element::Arc);
+  if (!node || node->GetElementType() != XFA_Element::Arc)
+    return nullptr;
   return static_cast<CXFA_Arc*>(node);
 }
 
 CXFA_Line* CXFA_Value::GetLineIfExists() const {
   CXFA_Node* node = GetFirstChild();
-  ASSERT(!node || node->GetElementType() == XFA_Element::Line);
+  if (!node || node->GetElementType() != XFA_Element::Line)
+    return nullptr;
   return static_cast<CXFA_Line*>(node);
 }
 
 CXFA_Rectangle* CXFA_Value::GetRectangleIfExists() const {
   CXFA_Node* node = GetFirstChild();
-  ASSERT(!node || node->GetElementType() == XFA_Element::Rectangle);
+  if (!node || node->GetElementType() != XFA_Element::Rectangle)
+    return nullptr;
   return static_cast<CXFA_Rectangle*>(node);
 }
 
 CXFA_Text* CXFA_Value::GetTextIfExists() const {
   CXFA_Node* node = GetFirstChild();
-  ASSERT(!node || node->GetElementType() == XFA_Element::Text);
+  if (!node || node->GetElementType() != XFA_Element::Text)
+    return nullptr;
   return static_cast<CXFA_Text*>(node);
 }
 
 CXFA_ExData* CXFA_Value::GetExDataIfExists() const {
   CXFA_Node* node = GetFirstChild();
-  ASSERT(!node || node->GetElementType() == XFA_Element::ExData);
+  if (!node || node->GetElementType() != XFA_Element::ExData)
+    return nullptr;
   return static_cast<CXFA_ExData*>(node);
 }
 
 CXFA_Image* CXFA_Value::GetImageIfExists() const {
   CXFA_Node* node = GetFirstChild();
-  ASSERT(!node || node->GetElementType() == XFA_Element::Image);
+  if (!node || node->GetElementType() != XFA_Element::Image)
+    return nullptr;
   return static_cast<CXFA_Image*>(node);
 }
diff --git a/xfa/fxfa/parser/cxfa_value.h b/xfa/fxfa/parser/cxfa_value.h
index 47aeefe..ccd4c2c 100644
--- a/xfa/fxfa/parser/cxfa_value.h
+++ b/xfa/fxfa/parser/cxfa_value.h
@@ -17,7 +17,7 @@
 class CXFA_Line;
 class CXFA_Rectangle;
 
-class CXFA_Value : public CXFA_Node {
+class CXFA_Value final : public CXFA_Node {
  public:
   CXFA_Value(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Value() override;
diff --git a/xfa/fxfa/parser/cxfa_variables.cpp b/xfa/fxfa/parser/cxfa_variables.cpp
index 1e3e751..5974b9e 100644
--- a/xfa/fxfa/parser/cxfa_variables.cpp
+++ b/xfa/fxfa/parser/cxfa_variables.cpp
@@ -6,18 +6,16 @@
 
 #include "xfa/fxfa/parser/cxfa_variables.h"
 
-#include "fxjs/xfa/cjx_variables.h"
+#include "fxjs/xfa/cjx_container.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kVariablesAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"variables";
+};
 
 }  // namespace
 
@@ -27,9 +25,8 @@
                 (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
                 XFA_ObjectType::ContainerNode,
                 XFA_Element::Variables,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_Variables>(this)) {}
+                {},
+                kVariablesAttributeData,
+                pdfium::MakeUnique<CJX_Container>(this)) {}
 
-CXFA_Variables::~CXFA_Variables() {}
+CXFA_Variables::~CXFA_Variables() = default;
diff --git a/xfa/fxfa/parser/cxfa_variables.h b/xfa/fxfa/parser/cxfa_variables.h
index 4c4cb4e..b9f6d72 100644
--- a/xfa/fxfa/parser/cxfa_variables.h
+++ b/xfa/fxfa/parser/cxfa_variables.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Variables : public CXFA_Node {
+class CXFA_Variables final : public CXFA_Node {
  public:
   CXFA_Variables(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Variables() override;
diff --git a/xfa/fxfa/parser/cxfa_version.cpp b/xfa/fxfa/parser/cxfa_version.cpp
index 5266f44..651d498 100644
--- a/xfa/fxfa/parser/cxfa_version.cpp
+++ b/xfa/fxfa/parser/cxfa_version.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_version.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kVersionAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"version";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::ContentNode,
                 XFA_Element::Version,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kVersionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Version::~CXFA_Version() {}
+CXFA_Version::~CXFA_Version() = default;
diff --git a/xfa/fxfa/parser/cxfa_version.h b/xfa/fxfa/parser/cxfa_version.h
index 34462fb..4b4f425 100644
--- a/xfa/fxfa/parser/cxfa_version.h
+++ b/xfa/fxfa/parser/cxfa_version.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Version : public CXFA_Node {
+class CXFA_Version final : public CXFA_Node {
  public:
   CXFA_Version(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Version() override;
diff --git a/xfa/fxfa/parser/cxfa_versioncontrol.cpp b/xfa/fxfa/parser/cxfa_versioncontrol.cpp
index 9cca84a..c72b877 100644
--- a/xfa/fxfa/parser/cxfa_versioncontrol.cpp
+++ b/xfa/fxfa/parser/cxfa_versioncontrol.cpp
@@ -6,19 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_versioncontrol.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kVersionControlAttributeData[] = {
     {XFA_Attribute::SourceBelow, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Update},
+     (void*)XFA_AttributeValue::Update},
     {XFA_Attribute::OutputBelow, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Warn},
+     (void*)XFA_AttributeValue::Warn},
     {XFA_Attribute::SourceAbove, XFA_AttributeType::Enum,
-     (void*)XFA_AttributeEnum::Warn},
+     (void*)XFA_AttributeValue::Warn},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"versionControl";
+};
 
 }  // namespace
 
@@ -29,8 +30,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::VersionControl,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kVersionControlAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_VersionControl::~CXFA_VersionControl() {}
+CXFA_VersionControl::~CXFA_VersionControl() = default;
diff --git a/xfa/fxfa/parser/cxfa_versioncontrol.h b/xfa/fxfa/parser/cxfa_versioncontrol.h
index 4688f55..733b817 100644
--- a/xfa/fxfa/parser/cxfa_versioncontrol.h
+++ b/xfa/fxfa/parser/cxfa_versioncontrol.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_VersionControl : public CXFA_Node {
+class CXFA_VersionControl final : public CXFA_Node {
  public:
   CXFA_VersionControl(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_VersionControl() override;
diff --git a/xfa/fxfa/parser/cxfa_viewerpreferences.cpp b/xfa/fxfa/parser/cxfa_viewerpreferences.cpp
index 1b80b47..4952c65 100644
--- a/xfa/fxfa/parser/cxfa_viewerpreferences.cpp
+++ b/xfa/fxfa/parser/cxfa_viewerpreferences.cpp
@@ -6,9 +6,12 @@
 
 #include "xfa/fxfa/parser/cxfa_viewerpreferences.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kViewerPreferencesPropertyData[] = {
     {XFA_Element::PrintScaling, 1, 0},
     {XFA_Element::Enforce, 1, 0},
     {XFA_Element::NumberOfCopies, 1, 0},
@@ -18,13 +21,12 @@
     {XFA_Element::DuplexOption, 1, 0},
     {XFA_Element::ADBE_JSDebugger, 1, 0},
     {XFA_Element::PickTrayByPDFSize, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kViewerPreferencesAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"viewerPreferences";
+};
 
 }  // namespace
 
@@ -35,8 +37,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::ViewerPreferences,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kViewerPreferencesPropertyData,
+                kViewerPreferencesAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_ViewerPreferences::~CXFA_ViewerPreferences() {}
+CXFA_ViewerPreferences::~CXFA_ViewerPreferences() = default;
diff --git a/xfa/fxfa/parser/cxfa_viewerpreferences.h b/xfa/fxfa/parser/cxfa_viewerpreferences.h
index bf831ee..229061f 100644
--- a/xfa/fxfa/parser/cxfa_viewerpreferences.h
+++ b/xfa/fxfa/parser/cxfa_viewerpreferences.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_ViewerPreferences : public CXFA_Node {
+class CXFA_ViewerPreferences final : public CXFA_Node {
  public:
   CXFA_ViewerPreferences(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_ViewerPreferences() override;
diff --git a/xfa/fxfa/parser/cxfa_webclient.cpp b/xfa/fxfa/parser/cxfa_webclient.cpp
index 0123560..66844fc 100644
--- a/xfa/fxfa/parser/cxfa_webclient.cpp
+++ b/xfa/fxfa/parser/cxfa_webclient.cpp
@@ -6,18 +6,21 @@
 
 #include "xfa/fxfa/parser/cxfa_webclient.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::FontInfo, 1, 0},
-                                                 {XFA_Element::Xdc, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kWebClientPropertyData[] = {
+    {XFA_Element::FontInfo, 1, 0},
+    {XFA_Element::Xdc, 1, 0},
+};
+
+const CXFA_Node::AttributeData kWebClientAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"webClient";
+};
 
 }  // namespace
 
@@ -27,8 +30,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::WebClient,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kWebClientPropertyData,
+                kWebClientAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_WebClient::~CXFA_WebClient() {}
+CXFA_WebClient::~CXFA_WebClient() = default;
diff --git a/xfa/fxfa/parser/cxfa_webclient.h b/xfa/fxfa/parser/cxfa_webclient.h
index b778689..41da046 100644
--- a/xfa/fxfa/parser/cxfa_webclient.h
+++ b/xfa/fxfa/parser/cxfa_webclient.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_WebClient : public CXFA_Node {
+class CXFA_WebClient final : public CXFA_Node {
  public:
   CXFA_WebClient(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_WebClient() override;
diff --git a/xfa/fxfa/parser/cxfa_whitespace.cpp b/xfa/fxfa/parser/cxfa_whitespace.cpp
index 6359b86..d8bdc62 100644
--- a/xfa/fxfa/parser/cxfa_whitespace.cpp
+++ b/xfa/fxfa/parser/cxfa_whitespace.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_whitespace.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kWhitespaceAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"whitespace";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Whitespace,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kWhitespaceAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Whitespace::~CXFA_Whitespace() {}
+CXFA_Whitespace::~CXFA_Whitespace() = default;
diff --git a/xfa/fxfa/parser/cxfa_whitespace.h b/xfa/fxfa/parser/cxfa_whitespace.h
index 62dde48..096a2da 100644
--- a/xfa/fxfa/parser/cxfa_whitespace.h
+++ b/xfa/fxfa/parser/cxfa_whitespace.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Whitespace : public CXFA_Node {
+class CXFA_Whitespace final : public CXFA_Node {
  public:
   CXFA_Whitespace(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Whitespace() override;
diff --git a/xfa/fxfa/parser/cxfa_window.cpp b/xfa/fxfa/parser/cxfa_window.cpp
index b1485f6..c1dd78d 100644
--- a/xfa/fxfa/parser/cxfa_window.cpp
+++ b/xfa/fxfa/parser/cxfa_window.cpp
@@ -6,14 +6,15 @@
 
 #include "xfa/fxfa/parser/cxfa_window.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kWindowAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"window";
+};
 
 }  // namespace
 
@@ -23,8 +24,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::NodeV,
                 XFA_Element::Window,
-                nullptr,
-                kAttributeData,
-                kName) {}
+                {},
+                kWindowAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Window::~CXFA_Window() {}
+CXFA_Window::~CXFA_Window() = default;
diff --git a/xfa/fxfa/parser/cxfa_window.h b/xfa/fxfa/parser/cxfa_window.h
index 852bc68..fc2fcb7 100644
--- a/xfa/fxfa/parser/cxfa_window.h
+++ b/xfa/fxfa/parser/cxfa_window.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Window : public CXFA_Node {
+class CXFA_Window final : public CXFA_Node {
  public:
   CXFA_Window(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Window() override;
diff --git a/xfa/fxfa/parser/cxfa_wsdladdress.cpp b/xfa/fxfa/parser/cxfa_wsdladdress.cpp
index 239bb66..9c35cac 100644
--- a/xfa/fxfa/parser/cxfa_wsdladdress.cpp
+++ b/xfa/fxfa/parser/cxfa_wsdladdress.cpp
@@ -6,19 +6,17 @@
 
 #include "xfa/fxfa/parser/cxfa_wsdladdress.h"
 
-#include "fxjs/xfa/cjx_wsdladdress.h"
+#include "fxjs/xfa/cjx_textnode.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kWsdlAddressAttributeData[] = {
     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"wsdlAddress";
+};
 
 }  // namespace
 
@@ -28,9 +26,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::TextNode,
                 XFA_Element::WsdlAddress,
-                nullptr,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_WsdlAddress>(this)) {}
+                {},
+                kWsdlAddressAttributeData,
+                pdfium::MakeUnique<CJX_TextNode>(this)) {}
 
-CXFA_WsdlAddress::~CXFA_WsdlAddress() {}
+CXFA_WsdlAddress::~CXFA_WsdlAddress() = default;
diff --git a/xfa/fxfa/parser/cxfa_wsdladdress.h b/xfa/fxfa/parser/cxfa_wsdladdress.h
index 83c965c..e65bd6f 100644
--- a/xfa/fxfa/parser/cxfa_wsdladdress.h
+++ b/xfa/fxfa/parser/cxfa_wsdladdress.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_WsdlAddress : public CXFA_Node {
+class CXFA_WsdlAddress final : public CXFA_Node {
  public:
   CXFA_WsdlAddress(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_WsdlAddress() override;
diff --git a/xfa/fxfa/parser/cxfa_wsdlconnection.cpp b/xfa/fxfa/parser/cxfa_wsdlconnection.cpp
index 6159f49..188255f 100644
--- a/xfa/fxfa/parser/cxfa_wsdlconnection.cpp
+++ b/xfa/fxfa/parser/cxfa_wsdlconnection.cpp
@@ -11,20 +11,19 @@
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kWsdlConnectionPropertyData[] = {
     {XFA_Element::Operation, 1, 0},
     {XFA_Element::WsdlAddress, 1, 0},
     {XFA_Element::SoapAddress, 1, 0},
     {XFA_Element::SoapAction, 1, 0},
     {XFA_Element::EffectiveOutputPolicy, 1, 0},
     {XFA_Element::EffectiveInputPolicy, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kWsdlConnectionAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DataDescription, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"wsdlConnection";
+};
 
 }  // namespace
 
@@ -35,9 +34,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::WsdlConnection,
-                kPropertyData,
-                kAttributeData,
-                kName,
+                kWsdlConnectionPropertyData,
+                kWsdlConnectionAttributeData,
                 pdfium::MakeUnique<CJX_WsdlConnection>(this)) {}
 
-CXFA_WsdlConnection::~CXFA_WsdlConnection() {}
+CXFA_WsdlConnection::~CXFA_WsdlConnection() = default;
diff --git a/xfa/fxfa/parser/cxfa_wsdlconnection.h b/xfa/fxfa/parser/cxfa_wsdlconnection.h
index 6c349cf..b2238fd 100644
--- a/xfa/fxfa/parser/cxfa_wsdlconnection.h
+++ b/xfa/fxfa/parser/cxfa_wsdlconnection.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_WsdlConnection : public CXFA_Node {
+class CXFA_WsdlConnection final : public CXFA_Node {
  public:
   CXFA_WsdlConnection(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_WsdlConnection() override;
diff --git a/xfa/fxfa/parser/cxfa_xdc.cpp b/xfa/fxfa/parser/cxfa_xdc.cpp
index daaebc8..84198da 100644
--- a/xfa/fxfa/parser/cxfa_xdc.cpp
+++ b/xfa/fxfa/parser/cxfa_xdc.cpp
@@ -6,17 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_xdc.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Uri, 1, 0},
-                                                 {XFA_Element::Xsl, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kXdcPropertyData[] = {
+    {XFA_Element::Uri, 1, 0},
+    {XFA_Element::Xsl, 1, 0},
+};
+
+const CXFA_Node::AttributeData kXdcAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"xdc";
+};
 
 }  // namespace
 
@@ -26,8 +29,8 @@
                 (XFA_XDPPACKET_Config | XFA_XDPPACKET_Xdc),
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Xdc,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kXdcPropertyData,
+                kXdcAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Xdc::~CXFA_Xdc() {}
+CXFA_Xdc::~CXFA_Xdc() = default;
diff --git a/xfa/fxfa/parser/cxfa_xdc.h b/xfa/fxfa/parser/cxfa_xdc.h
index 0eb43d5..81cbb57 100644
--- a/xfa/fxfa/parser/cxfa_xdc.h
+++ b/xfa/fxfa/parser/cxfa_xdc.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Xdc : public CXFA_Node {
+class CXFA_Xdc final : public CXFA_Node {
  public:
   CXFA_Xdc(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Xdc() override;
diff --git a/xfa/fxfa/parser/cxfa_xdp.cpp b/xfa/fxfa/parser/cxfa_xdp.cpp
index f100ae8..f3dc76c 100644
--- a/xfa/fxfa/parser/cxfa_xdp.cpp
+++ b/xfa/fxfa/parser/cxfa_xdp.cpp
@@ -6,16 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_xdp.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Packets, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kXdpPropertyData[] = {
+    {XFA_Element::Packets, 1, 0},
+};
+
+const CXFA_Node::AttributeData kXdpAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"xdp";
+};
 
 }  // namespace
 
@@ -25,8 +28,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Xdp,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kXdpPropertyData,
+                kXdpAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Xdp::~CXFA_Xdp() {}
+CXFA_Xdp::~CXFA_Xdp() = default;
diff --git a/xfa/fxfa/parser/cxfa_xdp.h b/xfa/fxfa/parser/cxfa_xdp.h
index 38b450e..16ad5d8 100644
--- a/xfa/fxfa/parser/cxfa_xdp.h
+++ b/xfa/fxfa/parser/cxfa_xdp.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Xdp : public CXFA_Node {
+class CXFA_Xdp final : public CXFA_Node {
  public:
   CXFA_Xdp(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Xdp() override;
diff --git a/xfa/fxfa/parser/cxfa_xfa.cpp b/xfa/fxfa/parser/cxfa_xfa.cpp
index 7eab898..22fdd35 100644
--- a/xfa/fxfa/parser/cxfa_xfa.cpp
+++ b/xfa/fxfa/parser/cxfa_xfa.cpp
@@ -11,12 +11,10 @@
 
 namespace {
 
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::AttributeData kXfaAttributeData[] = {
     {XFA_Attribute::TimeStamp, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Uuid, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"xfa";
+};
 
 }  // namespace
 
@@ -26,9 +24,8 @@
                 XFA_XDPPACKET_XDP,
                 XFA_ObjectType::ModelNode,
                 XFA_Element::Xfa,
-                nullptr,
-                kAttributeData,
-                kName,
+                {},
+                kXfaAttributeData,
                 pdfium::MakeUnique<CJX_Xfa>(this)) {}
 
-CXFA_Xfa::~CXFA_Xfa() {}
+CXFA_Xfa::~CXFA_Xfa() = default;
diff --git a/xfa/fxfa/parser/cxfa_xfa.h b/xfa/fxfa/parser/cxfa_xfa.h
index 137b601..d7d33df 100644
--- a/xfa/fxfa/parser/cxfa_xfa.h
+++ b/xfa/fxfa/parser/cxfa_xfa.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Xfa : public CXFA_Node {
+class CXFA_Xfa final : public CXFA_Node {
  public:
   CXFA_Xfa(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Xfa() override;
diff --git a/xfa/fxfa/parser/cxfa_xmlconnection.cpp b/xfa/fxfa/parser/cxfa_xmlconnection.cpp
index df05771..17be7bc 100644
--- a/xfa/fxfa/parser/cxfa_xmlconnection.cpp
+++ b/xfa/fxfa/parser/cxfa_xmlconnection.cpp
@@ -6,19 +6,19 @@
 
 #include "xfa/fxfa/parser/cxfa_xmlconnection.h"
 
-#include "fxjs/xfa/cjx_xmlconnection.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Uri, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kXmlConnectionPropertyData[] = {
+    {XFA_Element::Uri, 1, 0},
+};
+
+const CXFA_Node::AttributeData kXmlConnectionAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DataDescription, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"xmlConnection";
+};
 
 }  // namespace
 
@@ -29,9 +29,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::XmlConnection,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_XmlConnection>(this)) {}
+                kXmlConnectionPropertyData,
+                kXmlConnectionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_XmlConnection::~CXFA_XmlConnection() {}
+CXFA_XmlConnection::~CXFA_XmlConnection() = default;
diff --git a/xfa/fxfa/parser/cxfa_xmlconnection.h b/xfa/fxfa/parser/cxfa_xmlconnection.h
index f9158c6..33cc038 100644
--- a/xfa/fxfa/parser/cxfa_xmlconnection.h
+++ b/xfa/fxfa/parser/cxfa_xmlconnection.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_XmlConnection : public CXFA_Node {
+class CXFA_XmlConnection final : public CXFA_Node {
  public:
   CXFA_XmlConnection(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_XmlConnection() override;
diff --git a/xfa/fxfa/parser/cxfa_xmllocale.cpp b/xfa/fxfa/parser/cxfa_xmllocale.cpp
index 659c7bb..c7a6f47 100644
--- a/xfa/fxfa/parser/cxfa_xmllocale.cpp
+++ b/xfa/fxfa/parser/cxfa_xmllocale.cpp
@@ -8,94 +8,109 @@
 
 #include <utility>
 
-#include "core/fxcrt/xml/cxml_content.h"
-#include "core/fxcrt/xml/cxml_element.h"
+#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "third_party/base/ptr_util.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_nodelocale.h"
 #include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-CXFA_XMLLocale::CXFA_XMLLocale(std::unique_ptr<CXML_Element> pLocaleData)
-    : m_pLocaleData(std::move(pLocaleData)) {}
+namespace {
+
+constexpr wchar_t kNumberSymbols[] = L"numberSymbols";
+constexpr wchar_t kNumberSymbol[] = L"numberSymbol";
+constexpr wchar_t kCurrencySymbols[] = L"currencySymbols";
+constexpr wchar_t kCurrencySymbol[] = L"currencySymbol";
+
+}  // namespace
+
+// static
+std::unique_ptr<CXFA_XMLLocale> CXFA_XMLLocale::Create(
+    pdfium::span<uint8_t> data) {
+  auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(data);
+  CFX_XMLParser parser(stream);
+  auto doc = parser.Parse();
+  if (!doc)
+    return nullptr;
+
+  CFX_XMLElement* locale = nullptr;
+  for (auto* child = doc->GetRoot()->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    CFX_XMLElement* elem = ToXMLElement(child);
+    if (elem && elem->GetName().EqualsASCII("locale")) {
+      locale = elem;
+      break;
+    }
+  }
+  if (!locale)
+    return nullptr;
+
+  return pdfium::MakeUnique<CXFA_XMLLocale>(std::move(doc), locale);
+}
+
+CXFA_XMLLocale::CXFA_XMLLocale(std::unique_ptr<CFX_XMLDocument> doc,
+                               CFX_XMLElement* locale)
+    : xml_doc_(std::move(doc)), locale_(locale) {
+  ASSERT(xml_doc_);
+  ASSERT(locale_);
+}
 
 CXFA_XMLLocale::~CXFA_XMLLocale() {}
 
 WideString CXFA_XMLLocale::GetName() const {
-  return m_pLocaleData ? m_pLocaleData->GetAttrValue("name") : WideString();
+  return locale_->GetAttribute(L"name");
 }
 
-WideString CXFA_XMLLocale::GetNumbericSymbol(FX_LOCALENUMSYMBOL eType) const {
-  ByteString bsSymbols;
-  WideString wsName;
-  switch (eType) {
-    case FX_LOCALENUMSYMBOL_Decimal:
-      bsSymbols = "numberSymbols";
-      wsName = L"decimal";
-      break;
-    case FX_LOCALENUMSYMBOL_Grouping:
-      bsSymbols = "numberSymbols";
-      wsName = L"grouping";
-      break;
-    case FX_LOCALENUMSYMBOL_Percent:
-      bsSymbols = "numberSymbols";
-      wsName = L"percent";
-      break;
-    case FX_LOCALENUMSYMBOL_Minus:
-      bsSymbols = "numberSymbols";
-      wsName = L"minus";
-      break;
-    case FX_LOCALENUMSYMBOL_Zero:
-      bsSymbols = "numberSymbols";
-      wsName = L"zero";
-      break;
-    case FX_LOCALENUMSYMBOL_CurrencySymbol:
-      bsSymbols = "currencySymbols";
-      wsName = L"symbol";
-      break;
-    case FX_LOCALENUMSYMBOL_CurrencyName:
-      bsSymbols = "currencySymbols";
-      wsName = L"isoname";
-      break;
-    default:
-      return WideString();
-  }
-  CXML_Element* pElement =
-      m_pLocaleData->GetElement("", bsSymbols.AsStringView(), 0);
-  if (!pElement)
-    return WideString();
+WideString CXFA_XMLLocale::GetDecimalSymbol() const {
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(kNumberSymbols);
+  return patterns ? GetPattern(patterns, kNumberSymbol, L"decimal")
+                  : WideString();
+}
 
-  return GetPattern(
-      pElement, ByteStringView(bsSymbols.c_str(), bsSymbols.GetLength() - 1),
-      wsName.AsStringView());
+WideString CXFA_XMLLocale::GetGroupingSymbol() const {
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(kNumberSymbols);
+  return patterns ? GetPattern(patterns, kNumberSymbol, L"grouping")
+                  : WideString();
+}
+
+WideString CXFA_XMLLocale::GetPercentSymbol() const {
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(kNumberSymbols);
+  return patterns ? GetPattern(patterns, kNumberSymbol, L"percent")
+                  : WideString();
+}
+
+WideString CXFA_XMLLocale::GetMinusSymbol() const {
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(kNumberSymbols);
+  return patterns ? GetPattern(patterns, kNumberSymbol, L"minus")
+                  : WideString();
+}
+
+WideString CXFA_XMLLocale::GetCurrencySymbol() const {
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(kCurrencySymbols);
+  return patterns ? GetPattern(patterns, kCurrencySymbol, L"symbol")
+                  : WideString();
 }
 
 WideString CXFA_XMLLocale::GetDateTimeSymbols() const {
-  if (!m_pLocaleData)
-    return WideString();
-
-  CXML_Element* pNumberSymbols =
-      m_pLocaleData->GetElement("", "dateTimeSymbols", 0);
-  if (!pNumberSymbols)
-    return WideString();
-
-  CXML_Content* pContent = ToContent(pNumberSymbols->GetChild(0));
-  if (!pContent)
-    return WideString();
-
-  return pContent->m_Content;
+  CFX_XMLElement* symbols = locale_->GetFirstChildNamed(L"dateTimeSymbols");
+  return symbols ? symbols->GetTextData() : WideString();
 }
 
 WideString CXFA_XMLLocale::GetMonthName(int32_t nMonth, bool bAbbr) const {
-  return GetCalendarSymbol("month", nMonth, bAbbr);
+  return GetCalendarSymbol(L"month", nMonth, bAbbr);
 }
 
 WideString CXFA_XMLLocale::GetDayName(int32_t nWeek, bool bAbbr) const {
-  return GetCalendarSymbol("day", nWeek, bAbbr);
+  return GetCalendarSymbol(L"day", nWeek, bAbbr);
 }
 
 WideString CXFA_XMLLocale::GetMeridiemName(bool bAM) const {
-  return GetCalendarSymbol("meridiem", bAM ? 0 : 1, false);
+  return GetCalendarSymbol(L"meridiem", bAM ? 0 : 1, false);
 }
 
 FX_TIMEZONE CXFA_XMLLocale::GetTimeZone() const {
@@ -103,43 +118,45 @@
 }
 
 WideString CXFA_XMLLocale::GetEraName(bool bAD) const {
-  return GetCalendarSymbol("era", bAD ? 1 : 0, false);
+  return GetCalendarSymbol(L"era", bAD ? 1 : 0, false);
 }
 
-WideString CXFA_XMLLocale::GetCalendarSymbol(const ByteStringView& symbol,
-                                             int index,
+WideString CXFA_XMLLocale::GetCalendarSymbol(WideStringView symbol,
+                                             size_t index,
                                              bool bAbbr) const {
-  if (index < 0 || !m_pLocaleData)
+  CFX_XMLElement* child = locale_->GetFirstChildNamed(L"calendarSymbols");
+  if (!child)
     return WideString();
 
-  CXML_Element* pChild = m_pLocaleData->GetElement("", "calendarSymbols", 0);
-  if (!pChild)
+  WideString pstrSymbolNames = symbol + L"Names";
+  CFX_XMLElement* name_child = nullptr;
+  for (auto* name = child->GetFirstChild(); name;
+       name = name->GetNextSibling()) {
+    CFX_XMLElement* elem = ToXMLElement(name);
+    if (!elem || elem->GetName() != pstrSymbolNames)
+      continue;
+
+    WideString abbr = elem->GetAttribute(L"abbr");
+    bool abbr_value = false;
+    if (!abbr.IsEmpty())
+      abbr_value = abbr.EqualsASCII("1");
+    if (abbr_value != bAbbr)
+      continue;
+
+    name_child = elem;
+    break;
+  }
+  if (!name_child)
     return WideString();
 
-  ByteString pstrSymbolNames = symbol + "Names";
-  CXML_Element* pSymbolNames =
-      pChild->GetElement("", pstrSymbolNames.AsStringView(), 0);
-  if (!pSymbolNames)
-    return WideString();
-
-  if ((!!pSymbolNames->GetAttrInteger("abbr")) != bAbbr)
-    pSymbolNames = pChild->GetElement("", pstrSymbolNames.AsStringView(), 1);
-
-  if (!pSymbolNames || (!!pSymbolNames->GetAttrInteger("abbr")) != bAbbr)
-    return WideString();
-
-  CXML_Element* pSymbolName = pSymbolNames->GetElement("", symbol, index);
-  if (!pSymbolName)
-    return WideString();
-
-  CXML_Content* pContent = ToContent(pSymbolName->GetChild(0));
-  return pContent ? pContent->m_Content : WideString();
+  CFX_XMLElement* sym_element = name_child->GetNthChildNamed(symbol, index);
+  return sym_element ? sym_element->GetTextData() : WideString();
 }
 
 WideString CXFA_XMLLocale::GetDatePattern(
     FX_LOCALEDATETIMESUBCATEGORY eType) const {
-  CXML_Element* pElement = m_pLocaleData->GetElement("", "datePatterns", 0);
-  if (!pElement)
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(L"datePatterns");
+  if (!patterns)
     return WideString();
 
   WideString wsName;
@@ -158,13 +175,13 @@
       wsName = L"long";
       break;
   }
-  return GetPattern(pElement, "datePattern", wsName.AsStringView());
+  return GetPattern(patterns, L"datePattern", wsName.AsStringView());
 }
 
 WideString CXFA_XMLLocale::GetTimePattern(
     FX_LOCALEDATETIMESUBCATEGORY eType) const {
-  CXML_Element* pElement = m_pLocaleData->GetElement("", "timePatterns", 0);
-  if (!pElement)
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(L"timePatterns");
+  if (!patterns)
     return WideString();
 
   WideString wsName;
@@ -183,24 +200,23 @@
       wsName = L"long";
       break;
   }
-  return GetPattern(pElement, "timePattern", wsName.AsStringView());
+  return GetPattern(patterns, L"timePattern", wsName.AsStringView());
 }
 
 WideString CXFA_XMLLocale::GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const {
-  return m_pLocaleData->GetElement("", "numberPatterns", 0)
-             ? XFA_PatternToString(eType)
-             : WideString();
+  CFX_XMLElement* patterns = locale_->GetFirstChildNamed(L"numberPatterns");
+  return patterns ? XFA_PatternToString(eType) : WideString();
 }
 
-WideString CXFA_XMLLocale::GetPattern(CXML_Element* pElement,
-                                      const ByteStringView& bsTag,
-                                      const WideStringView& wsName) const {
-  size_t iCount = pElement->CountElements("", bsTag);
-  for (size_t i = 0; i < iCount; i++) {
-    CXML_Element* pChild = pElement->GetElement("", bsTag, i);
-    if (pChild->GetAttrValue("name") == wsName) {
-      CXML_Content* pContent = ToContent(pChild->GetChild(0));
-      return pContent ? pContent->m_Content : WideString();
+WideString CXFA_XMLLocale::GetPattern(CFX_XMLElement* patterns,
+                                      WideStringView bsTag,
+                                      WideStringView wsName) const {
+  for (auto* child = patterns->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    CFX_XMLElement* pattern = ToXMLElement(child);
+    if (pattern && pattern->GetName() == bsTag &&
+        pattern->GetAttribute(L"name") == wsName) {
+      return pattern->GetTextData();
     }
   }
   return WideString();
diff --git a/xfa/fxfa/parser/cxfa_xmllocale.h b/xfa/fxfa/parser/cxfa_xmllocale.h
index 5050855..472f774 100644
--- a/xfa/fxfa/parser/cxfa_xmllocale.h
+++ b/xfa/fxfa/parser/cxfa_xmllocale.h
@@ -9,19 +9,27 @@
 
 #include <memory>
 
-#include "core/fxcrt/ifx_locale.h"
+#include "third_party/base/span.h"
+#include "xfa/fgas/crt/locale_iface.h"
 
-class CXML_Element;
+class CFX_XMLDocument;
+class CFX_XMLElement;
 
-class CXFA_XMLLocale : public IFX_Locale {
+class CXFA_XMLLocale final : public LocaleIface {
  public:
-  explicit CXFA_XMLLocale(std::unique_ptr<CXML_Element> pLocaleData);
+  static std::unique_ptr<CXFA_XMLLocale> Create(pdfium::span<uint8_t> data);
+
+  explicit CXFA_XMLLocale(std::unique_ptr<CFX_XMLDocument> root,
+                          CFX_XMLElement* locale);
   ~CXFA_XMLLocale() override;
 
-  // IFX_Locale
+  // LocaleIface
   WideString GetName() const override;
-  WideString GetNumbericSymbol(FX_LOCALENUMSYMBOL eType) const override;
-
+  WideString GetDecimalSymbol() const override;
+  WideString GetGroupingSymbol() const override;
+  WideString GetPercentSymbol() const override;
+  WideString GetMinusSymbol() const override;
+  WideString GetCurrencySymbol() const override;
   WideString GetDateTimeSymbols() const override;
   WideString GetMonthName(int32_t nMonth, bool bAbbr) const override;
   WideString GetDayName(int32_t nWeek, bool bAbbr) const override;
@@ -34,14 +42,15 @@
   WideString GetNumPattern(FX_LOCALENUMSUBCATEGORY eType) const override;
 
  private:
-  WideString GetPattern(CXML_Element* pElement,
-                        const ByteStringView& bsTag,
-                        const WideStringView& wsName) const;
-  WideString GetCalendarSymbol(const ByteStringView& symbol,
-                               int index,
+  WideString GetPattern(CFX_XMLElement* pElement,
+                        WideStringView bsTag,
+                        WideStringView wsName) const;
+  WideString GetCalendarSymbol(WideStringView symbol,
+                               size_t index,
                                bool bAbbr) const;
 
-  std::unique_ptr<CXML_Element> m_pLocaleData;
+  std::unique_ptr<CFX_XMLDocument> xml_doc_;
+  UnownedPtr<CFX_XMLElement> locale_;
 };
 
 #endif  // XFA_FXFA_PARSER_CXFA_XMLLOCALE_H_
diff --git a/xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp b/xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp
new file mode 100644
index 0000000..395a360
--- /dev/null
+++ b/xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp
@@ -0,0 +1,213 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/cxfa_xmllocale.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kXMLData[] =
+    "<locale name=\"en_US\" desc=\"English(America)\">"
+    "<calendarSymbols name=\"gregorian\"><monthNames><month>January</month>"
+    "<month>February</month>"
+    "<month>March</month>"
+    "<month>April</month>"
+    "<month>May</month>"
+    "<month>June</month>"
+    "<month>July</month>"
+    "<month>August</month>"
+    "<month>September</month>"
+    "<month>October</month>"
+    "<month>November</month>"
+    "<month>December</month>"
+    "</monthNames>"
+    "<monthNames abbr=\"1\"><month>Jan</month>"
+    "<month>Feb</month>"
+    "<month>Mar</month>"
+    "<month>Apr</month>"
+    "<month>May</month>"
+    "<month>Jun</month>"
+    "<month>Jul</month>"
+    "<month>Aug</month>"
+    "<month>Sep</month>"
+    "<month>Oct</month>"
+    "<month>Nov</month>"
+    "<month>Dec</month>"
+    "</monthNames>"
+    "<dayNames><day>Sunday</day>"
+    "<day>Monday</day>"
+    "<day>Tuesday</day>"
+    "<day>Wednesday</day>"
+    "<day>Thursday</day>"
+    "<day>Friday</day>"
+    "<day>Saturday</day>"
+    "</dayNames>"
+    "<dayNames abbr=\"1\"><day>Sun</day>"
+    "<day>Mon</day>"
+    "<day>Tue</day>"
+    "<day>Wed</day>"
+    "<day>Thu</day>"
+    "<day>Fri</day>"
+    "<day>Sat</day>"
+    "</dayNames>"
+    "<meridiemNames><meridiem>AM</meridiem>"
+    "<meridiem>PM</meridiem>"
+    "</meridiemNames>"
+    "<eraNames><era>BC</era>"
+    "<era>AD</era>"
+    "</eraNames>"
+    "</calendarSymbols>"
+    "<datePatterns><datePattern name=\"full\">EEEE, MMMM D, YYYY</datePattern>"
+    "<datePattern name=\"long\">MMMM D, YYYY</datePattern>"
+    "<datePattern name=\"med\">MMM D, YYYY</datePattern>"
+    "<datePattern name=\"short\">M/D/YY</datePattern>"
+    "</datePatterns>"
+    "<timePatterns><timePattern name=\"full\">h:MM:SS A Z</timePattern>"
+    "<timePattern name=\"long\">h:MM:SS A Z</timePattern>"
+    "<timePattern name=\"med\">h:MM:SS A</timePattern>"
+    "<timePattern name=\"short\">h:MM A</timePattern>"
+    "</timePatterns>"
+    "<dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>"
+    "<numberPatterns><numberPattern name=\"numeric\">z,zz9.zzz</numberPattern>"
+    "<numberPattern name=\"currency\">$z,zz9.99|($z,zz9.99)</numberPattern>"
+    "<numberPattern name=\"percent\">z,zz9%</numberPattern>"
+    "</numberPatterns>"
+    "<numberSymbols><numberSymbol name=\"decimal\">.</numberSymbol>"
+    "<numberSymbol name=\"grouping\">,</numberSymbol>"
+    "<numberSymbol name=\"percent\">%</numberSymbol>"
+    "<numberSymbol name=\"minus\">-</numberSymbol>"
+    "<numberSymbol name=\"zero\">0</numberSymbol>"
+    "</numberSymbols>"
+    "<currencySymbols><currencySymbol name=\"symbol\">$</currencySymbol>"
+    "<currencySymbol name=\"isoname\">USD</currencySymbol>"
+    "<currencySymbol name=\"decimal\">.</currencySymbol>"
+    "</currencySymbols>"
+    "</locale>";
+
+std::unique_ptr<CXFA_XMLLocale> CreateLocaleHelper() {
+  return CXFA_XMLLocale::Create(pdfium::as_writable_bytes(
+      pdfium::make_span(const_cast<char*>(kXMLData), strlen(kXMLData))));
+}
+
+}  // namespace
+
+TEST(CXFA_XMLLocaleTest, Create) {
+  auto locale = CreateLocaleHelper();
+  EXPECT_TRUE(locale != nullptr);
+}
+
+TEST(CXFA_XMLLocaleTest, CreateBadXML) {
+  auto locale = CXFA_XMLLocale::Create(pdfium::span<uint8_t>());
+  EXPECT_TRUE(locale == nullptr);
+}
+
+TEST(CXFA_XMLLocaleTest, GetName) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"en_US", locale->GetName());
+}
+
+TEST(CXFA_XMLLocaleTest, GetNumericSymbols) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L".", locale->GetDecimalSymbol());
+  EXPECT_EQ(L",", locale->GetGroupingSymbol());
+  EXPECT_EQ(L"%", locale->GetPercentSymbol());
+  EXPECT_EQ(L"-", locale->GetMinusSymbol());
+  EXPECT_EQ(L"$", locale->GetCurrencySymbol());
+}
+
+TEST(CXFA_XMLLocaleTest, GetDateTimeSymbols) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"GyMdkHmsSEDFwWahKzZ", locale->GetDateTimeSymbols());
+}
+
+TEST(CXFA_XMLLocaleTest, GetMonthName) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"", locale->GetMonthName(24, false));
+  EXPECT_EQ(L"", locale->GetMonthName(-5, false));
+  EXPECT_EQ(L"Feb", locale->GetMonthName(1, true));
+  EXPECT_EQ(L"February", locale->GetMonthName(1, false));
+}
+
+TEST(CXFA_XMLLocaleTest, GetDayName) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"", locale->GetDayName(24, false));
+  EXPECT_EQ(L"", locale->GetDayName(-5, false));
+  EXPECT_EQ(L"Mon", locale->GetDayName(1, true));
+  EXPECT_EQ(L"Monday", locale->GetDayName(1, false));
+}
+
+TEST(CXFA_XMLLocaleTest, GetMeridiemName) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"AM", locale->GetMeridiemName(true));
+  EXPECT_EQ(L"PM", locale->GetMeridiemName(false));
+}
+
+TEST(CXFA_XMLLocaleTest, GetEraName) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"AD", locale->GetEraName(true));
+  EXPECT_EQ(L"BC", locale->GetEraName(false));
+}
+
+TEST(CXFA_XMLLocaleTest, GetDatePattern) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"M/D/YY",
+            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short));
+  EXPECT_EQ(L"MMM D, YYYY",
+            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default));
+  EXPECT_EQ(L"MMM D, YYYY",
+            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium));
+  EXPECT_EQ(L"EEEE, MMMM D, YYYY",
+            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Full));
+  EXPECT_EQ(L"MMMM D, YYYY",
+            locale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Long));
+}
+
+TEST(CXFA_XMLLocaleTest, GetTimePattern) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"h:MM A",
+            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short));
+  EXPECT_EQ(L"h:MM:SS A",
+            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default));
+  EXPECT_EQ(L"h:MM:SS A",
+            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium));
+  EXPECT_EQ(L"h:MM:SS A Z",
+            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Full));
+  EXPECT_EQ(L"h:MM:SS A Z",
+            locale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Long));
+}
+
+TEST(CXFA_XMLLocaleTest, GetNumPattern) {
+  auto locale = CreateLocaleHelper();
+  ASSERT_TRUE(locale != nullptr);
+
+  EXPECT_EQ(L"z,zzz,zzz,zzz,zzz,zzz%",
+            locale->GetNumPattern(FX_LOCALENUMPATTERN_Percent));
+  EXPECT_EQ(L"$z,zzz,zzz,zzz,zzz,zz9.99",
+            locale->GetNumPattern(FX_LOCALENUMPATTERN_Currency));
+  EXPECT_EQ(L"z,zzz,zzz,zzz,zzz,zz9.zzz",
+            locale->GetNumPattern(FX_LOCALENUMPATTERN_Decimal));
+  EXPECT_EQ(L"z,zzz,zzz,zzz,zzz,zzz",
+            locale->GetNumPattern(FX_LOCALENUMPATTERN_Integer));
+}
diff --git a/xfa/fxfa/parser/cxfa_xsdconnection.cpp b/xfa/fxfa/parser/cxfa_xsdconnection.cpp
index 7b0de0d..1973870 100644
--- a/xfa/fxfa/parser/cxfa_xsdconnection.cpp
+++ b/xfa/fxfa/parser/cxfa_xsdconnection.cpp
@@ -6,21 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_xsdconnection.h"
 
-#include "fxjs/xfa/cjx_xsdconnection.h"
+#include "fxjs/xfa/cjx_node.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kXsdConnectionPropertyData[] = {
     {XFA_Element::Uri, 1, 0},
     {XFA_Element::RootElement, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kXsdConnectionAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::DataDescription, XFA_AttributeType::CData, nullptr},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"xsdConnection";
+};
 
 }  // namespace
 
@@ -31,9 +30,8 @@
                 XFA_XDPPACKET_ConnectionSet,
                 XFA_ObjectType::Node,
                 XFA_Element::XsdConnection,
-                kPropertyData,
-                kAttributeData,
-                kName,
-                pdfium::MakeUnique<CJX_XsdConnection>(this)) {}
+                kXsdConnectionPropertyData,
+                kXsdConnectionAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_XsdConnection::~CXFA_XsdConnection() {}
+CXFA_XsdConnection::~CXFA_XsdConnection() = default;
diff --git a/xfa/fxfa/parser/cxfa_xsdconnection.h b/xfa/fxfa/parser/cxfa_xsdconnection.h
index 652d45e..8cdef80 100644
--- a/xfa/fxfa/parser/cxfa_xsdconnection.h
+++ b/xfa/fxfa/parser/cxfa_xsdconnection.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_XsdConnection : public CXFA_Node {
+class CXFA_XsdConnection final : public CXFA_Node {
  public:
   CXFA_XsdConnection(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_XsdConnection() override;
diff --git a/xfa/fxfa/parser/cxfa_xsl.cpp b/xfa/fxfa/parser/cxfa_xsl.cpp
index 4d5e71e..2db4bdc 100644
--- a/xfa/fxfa/parser/cxfa_xsl.cpp
+++ b/xfa/fxfa/parser/cxfa_xsl.cpp
@@ -6,17 +6,20 @@
 
 #include "xfa/fxfa/parser/cxfa_xsl.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {{XFA_Element::Uri, 1, 0},
-                                                 {XFA_Element::Debug, 1, 0},
-                                                 {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+const CXFA_Node::PropertyData kXslPropertyData[] = {
+    {XFA_Element::Uri, 1, 0},
+    {XFA_Element::Debug, 1, 0},
+};
+
+const CXFA_Node::AttributeData kXslAttributeData[] = {
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"xsl";
+};
 
 }  // namespace
 
@@ -26,8 +29,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Xsl,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kXslPropertyData,
+                kXslAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Xsl::~CXFA_Xsl() {}
+CXFA_Xsl::~CXFA_Xsl() = default;
diff --git a/xfa/fxfa/parser/cxfa_xsl.h b/xfa/fxfa/parser/cxfa_xsl.h
index d921125..1be8d4b 100644
--- a/xfa/fxfa/parser/cxfa_xsl.h
+++ b/xfa/fxfa/parser/cxfa_xsl.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Xsl : public CXFA_Node {
+class CXFA_Xsl final : public CXFA_Node {
  public:
   CXFA_Xsl(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Xsl() override;
diff --git a/xfa/fxfa/parser/cxfa_zpl.cpp b/xfa/fxfa/parser/cxfa_zpl.cpp
index aa078b5..aa9db61 100644
--- a/xfa/fxfa/parser/cxfa_zpl.cpp
+++ b/xfa/fxfa/parser/cxfa_zpl.cpp
@@ -6,21 +6,23 @@
 
 #include "xfa/fxfa/parser/cxfa_zpl.h"
 
+#include "fxjs/xfa/cjx_node.h"
+#include "third_party/base/ptr_util.h"
+
 namespace {
 
-const CXFA_Node::PropertyData kPropertyData[] = {
+const CXFA_Node::PropertyData kZplPropertyData[] = {
     {XFA_Element::FontInfo, 1, 0},
     {XFA_Element::Xdc, 1, 0},
     {XFA_Element::BatchOutput, 1, 0},
     {XFA_Element::FlipLabel, 1, 0},
-    {XFA_Element::Unknown, 0, 0}};
-const CXFA_Node::AttributeData kAttributeData[] = {
+};
+
+const CXFA_Node::AttributeData kZplAttributeData[] = {
     {XFA_Attribute::Name, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Desc, XFA_AttributeType::CData, nullptr},
     {XFA_Attribute::Lock, XFA_AttributeType::Integer, (void*)0},
-    {XFA_Attribute::Unknown, XFA_AttributeType::Integer, nullptr}};
-
-constexpr wchar_t kName[] = L"zpl";
+};
 
 }  // namespace
 
@@ -30,8 +32,8 @@
                 XFA_XDPPACKET_Config,
                 XFA_ObjectType::Node,
                 XFA_Element::Zpl,
-                kPropertyData,
-                kAttributeData,
-                kName) {}
+                kZplPropertyData,
+                kZplAttributeData,
+                pdfium::MakeUnique<CJX_Node>(this)) {}
 
-CXFA_Zpl::~CXFA_Zpl() {}
+CXFA_Zpl::~CXFA_Zpl() = default;
diff --git a/xfa/fxfa/parser/cxfa_zpl.h b/xfa/fxfa/parser/cxfa_zpl.h
index ee8f925..6433015 100644
--- a/xfa/fxfa/parser/cxfa_zpl.h
+++ b/xfa/fxfa/parser/cxfa_zpl.h
@@ -9,7 +9,7 @@
 
 #include "xfa/fxfa/parser/cxfa_node.h"
 
-class CXFA_Zpl : public CXFA_Node {
+class CXFA_Zpl final : public CXFA_Node {
  public:
   CXFA_Zpl(CXFA_Document* doc, XFA_PacketType packet);
   ~CXFA_Zpl() override;
diff --git a/xfa/fxfa/parser/element_attributes.inc b/xfa/fxfa/parser/element_attributes.inc
new file mode 100644
index 0000000..c8bfc60
--- /dev/null
+++ b/xfa/fxfa/parser/element_attributes.inc
@@ -0,0 +1,552 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+ELEM_ATTR____(RecordSet, Max, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(RecordSet, EofAction, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(RecordSet, CursorType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(RecordSet, LockType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(RecordSet, BofAction, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(RecordSet, CursorLocation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SubformSet, InstanceIndex, CJX_Object::ScriptSomInstanceIndex)
+ELEM_ATTR____(SubformSet, Relation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SubformSet, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Typeface, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, BeforeTarget, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, OverflowTarget, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, OverflowLeader, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, OverflowTrailer, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, StartNew, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, BookendTrailer, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, After, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, BookendLeader, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, AfterTarget, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Break, Before, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(NumberPattern, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DynamicRender, Value, CJX_Object::ScriptSomDefaultValue_Read)
+ELEM_ATTR____(CheckButton, AllowNeutral, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(CheckButton, Mark, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(CheckButton, Shape, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(CheckButton, Size, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SourceSet, Use, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SourceSet, Usehref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Arc, StartAngle, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Arc, SweepAngle, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Arc, Circular, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Arc, Hand, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Map, Bind, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Map, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Map, From, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Map, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Mdp, SignatureType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Mdp, Permissions, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakBefore, StartNew, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakBefore, Trailer, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakBefore, TargetType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakBefore, Target, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakBefore, Leader, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Pcl, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Pdf, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Uri, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Uri, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Xfa, ThisValue, CJX_Xfa::thisValue)
+ELEM_ATTR____(Xfa, TimeStamp, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Xfa, Uuid, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Zpl, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Margin, LeftInset, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Margin, BottomInset, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Margin, TopInset, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Margin, RightInset, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, NonRepudiation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, EncipherOnly, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, DigitalSignature, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, CrlSign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, KeyAgreement, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, KeyEncipherment, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, DataEncipherment, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, KeyCertSign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(KeyUsage, DecipherOnly, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ChoiceList, Open, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ChoiceList, CommitOn, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ChoiceList, TextEntry, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(LabelPrinter, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(CalendarSymbols, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, HAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, TextIndent, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, MarginRight, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, MarginLeft, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, RadixOffset, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, Preserve, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, SpaceBelow, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, VAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, TabDefault, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, TabStops, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, LineHeight, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Para, SpaceAbove, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Part, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Part, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Filter, AddRevocationInfo, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Event, Ref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Event, Activity, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, DataRowCount, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, DataPrep, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, TextLocation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, ModuleWidth, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, PrintCheckDigit, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, ModuleHeight, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, StartChar, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, Truncate, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, WideNarrowRatio, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, ErrorCorrectionLevel, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, UpsMode, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, Checksum, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, CharEncoding, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, DataColumnCount, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, RowColumnRatio, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, DataLength, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Barcode, EndChar, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TimePattern, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BatchOutput, Format, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Operation, Output, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Operation, Input, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SubjectDNs, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Issuers, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(WsdlConnection, DataDescription, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Delta, CurrentValue, CJX_Delta::currentValue)
+ELEM_ATTR____(Delta, SavedValue, CJX_Delta::savedValue)
+ELEM_ATTR____(Delta, Target, CJX_Delta::target)
+ELEM_ATTR____(Button, Highlight, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Border, Break, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Border, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Border, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Border, Hand, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Area, X, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Area, Y, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Area, Id, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Area, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Area, Level, CJX_Object::ScriptAttributeInteger)
+ELEM_ATTR____(Area, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Area, ColSpan, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Area, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Hyphenation, Id, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Hyphenation, WordCharacterCount, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Hyphenation, Hyphenate, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Hyphenation, ExcludeInitialCap, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Hyphenation, PushCharacterCount, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Hyphenation, RemainCharacterCount, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Hyphenation, ExcludeAllCaps, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Text, MaxChars, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Text, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Text, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Time, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Time, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Certificates, Url, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Certificates, CredentialServerPolicy, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Certificates, UrlPolicy, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EncryptionMethods, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SetProperty, Connection, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SetProperty, Target, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DateTime, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(DateTime, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Comb, NumberOfCells, CJX_Object::ScriptAttributeInteger)
+ELEM_ATTR____(Pattern, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, H, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, W, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, X, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, Y, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, HAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, DataNode, CJX_Object::ScriptSomDataNode)
+ELEM_ATTR____(Field, Access, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, Rotate, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, FillColor, CJX_Object::ScriptSomFillColor)
+ELEM_ATTR____(Field, FormattedValue, CJX_Field::formattedValue)
+ELEM_ATTR____(Field, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, BorderColor, CJX_Object::ScriptSomBorderColor)
+ELEM_ATTR____(Field, FontColor, CJX_Object::ScriptSomFontColor)
+ELEM_ATTR____(Field, ParentSubform, CJX_Field::parentSubform)
+ELEM_ATTR____(Field, MandatoryMessage, CJX_Object::ScriptSomMandatoryMessage)
+ELEM_ATTR____(Field, VAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, MaxH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, MaxW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, MinH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, MinW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, Mandatory, CJX_Object::ScriptSomMandatory)
+ELEM_ATTR____(Field, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, FormatMessage, CJX_Field::formatMessage)
+ELEM_ATTR____(Field, RawValue, CJX_Field::rawValue)
+ELEM_ATTR____(Field, DefaultValue, CJX_Field::defaultValue)
+ELEM_ATTR____(Field, Length, CJX_Field::length)
+ELEM_ATTR____(Field, ColSpan, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, Locale, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, AnchorType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, AccessKey, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Field, ValidationMessage, CJX_Object::ScriptSomValidationMessage)
+ELEM_ATTR____(Field, EditValue, CJX_Field::editValue)
+ELEM_ATTR____(Field, SelectedIndex, CJX_Field::selectedIndex)
+ELEM_ATTR____(Field, BorderWidth, CJX_Object::ScriptSomBorderWidth)
+ELEM_ATTR____(Agent, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ContentArea, H, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ContentArea, W, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ContentArea, X, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ContentArea, Y, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ContentArea, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Edge, Cap, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Edge, Stroke, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Edge, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Edge, Thickness, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Stipple, Rate, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(VersionControl, SourceBelow, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(VersionControl, OutputBelow, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(VersionControl, SourceAbove, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(VersionControl, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(ExclGroup, H, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, W, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, X, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, Y, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, HAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, ErrorText, CJX_ExclGroup::errorText)
+ELEM_ATTR____(ExclGroup, DataNode, CJX_Object::ScriptSomDataNode)
+ELEM_ATTR____(ExclGroup, Access, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, FillColor, CJX_Object::ScriptSomFillColor)
+ELEM_ATTR____(ExclGroup, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, BorderColor, CJX_Object::ScriptSomBorderColor)
+ELEM_ATTR____(ExclGroup, MandatoryMessage, CJX_Object::ScriptSomMandatoryMessage)
+ELEM_ATTR____(ExclGroup, VAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, MaxH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, MaxW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, MinH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, MinW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, Layout, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, Transient, CJX_ExclGroup::transient)
+ELEM_ATTR____(ExclGroup, Mandatory, CJX_Object::ScriptSomMandatory)
+ELEM_ATTR____(ExclGroup, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, RawValue, CJX_ExclGroup::rawValue)
+ELEM_ATTR____(ExclGroup, DefaultValue, CJX_ExclGroup::defaultValue)
+ELEM_ATTR____(ExclGroup, ColSpan, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, AnchorType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, AccessKey, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExclGroup, ValidationMessage, CJX_Object::ScriptSomValidationMessage)
+ELEM_ATTR____(ExclGroup, BorderWidth, CJX_Object::ScriptSomBorderWidth)
+ELEM_ATTR____(Compress, Scope, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Execute, Connection, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Execute, RunAt, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Execute, ExecuteType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DateTimeEdit, HScrollPolicy, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Image, ContentType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Image, TransferEncoding, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Image, DefaultValue, CJX_Object::ScriptSomDefaultValue_Read)
+ELEM_ATTR____(Image, Aspect, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Image, Value, CJX_Object::ScriptSomDefaultValue_Read)
+ELEM_ATTR____(Image, Href, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SharpxHTML, Value, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TimeStamp, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TimeStamp, Server, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Decimal, FracDigits, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Decimal, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Decimal, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Decimal, LeadDigits, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, H, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, W, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, X, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, Y, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, HAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, AllowMacro, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, ColumnWidths, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, DataNode, CJX_Object::ScriptSomDataNode)
+ELEM_ATTR____(Subform, InstanceIndex, CJX_Object::ScriptSomInstanceIndex)
+ELEM_ATTR____(Subform, Access, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, FillColor, CJX_Object::ScriptSomFillColor)
+ELEM_ATTR____(Subform, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, BorderColor, CJX_Object::ScriptSomBorderColor)
+ELEM_ATTR____(Subform, VAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, MaxH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, MaxW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, MinH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, MinW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, Layout, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, MergeMode, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, InstanceManager, CJX_Subform::instanceManager)
+ELEM_ATTR____(Subform, ColSpan, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, Locale, CJX_Subform::locale)
+ELEM_ATTR____(Subform, AnchorType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, ValidationMessage, CJX_Object::ScriptSomValidationMessage)
+ELEM_ATTR____(Subform, RestoreState, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, Scope, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Subform, BorderWidth, CJX_Object::ScriptSomBorderWidth)
+ELEM_ATTR____(Handler, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Handler, Version, CJX_Handler::version)
+ELEM_ATTR____(Command, Timeout, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Staple, Mode, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SubmitFormat, Mode, CJX_Object::ScriptSubmitFormatMode)
+ELEM_ATTR____(Boolean, DefaultValue, CJX_Boolean::defaultValue)
+ELEM_ATTR____(Boolean, Value, CJX_Boolean::value)
+ELEM_ATTR____(Message, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Message, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Assist, Role, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Picture, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Picture, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Picture, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Picture, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(WebClient, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Corner, Stroke, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Corner, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Corner, Inverted, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Corner, Thickness, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Corner, Join, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Corner, Radius, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Color, CSpace, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Color, Value, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Keep, Next, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Keep, Previous, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Keep, Intact, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Query, CommandType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ImageEdit, Data, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Validate, ScriptTest, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Validate, NullTest, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Validate, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Validate, FormatTest, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Validate, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(DigestMethods, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageSet, Relation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageSet, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Integer, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Integer, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Equate, To, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Equate, Force, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Equate, From, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Sharpxml, Value, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(XsdConnection, DataDescription, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Traverse, Ref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Traverse, Operation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Encodings, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Template, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Template, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Signing, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Script, ContentType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Script, RunAt, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Script, Stateless, CJX_Script::stateless)
+ELEM_ATTR____(Script, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Script, Binding, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Script, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Script, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Script, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(PasswordEdit, PasswordChar, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PasswordEdit, HScrollPolicy, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(NumericEdit, HScrollPolicy, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageArea, PagePosition, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageArea, OddOrEven, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageArea, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageArea, InitialNumber, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageArea, Numbered, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(PageArea, BlankOrNotBlank, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SubmitUrl, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Oids, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Caption, Reserve, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Caption, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Caption, Placement, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExData, ContentType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExData, TransferEncoding, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExData, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(ExData, MaxLength, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExData, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(ExData, Href, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DayNames, Abbr, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DefaultTypeface, WritingScript, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Manifest, Action, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Manifest, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Overflow, Trailer, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Overflow, Target, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Overflow, Leader, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Linear, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(CurrencySymbol, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(InstanceManager, Max, CJX_InstanceManager::max)
+ELEM_ATTR____(InstanceManager, Min, CJX_InstanceManager::min)
+ELEM_ATTR____(InstanceManager, Count, CJX_InstanceManager::count)
+ELEM_ATTR____(EquateRange, To, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EquateRange, UnicodeRange, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EquateRange, From, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Medium, Orientation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Medium, ImagingBBox, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Medium, Short, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Medium, Stock, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Medium, Long, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TextEdit, VScrollPolicy, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TextEdit, AllowRichText, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TextEdit, MultiLine, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TextEdit, HScrollPolicy, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(TemplateCache, MaxEntries, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DataValue, ContentType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DataValue, Contains, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DataValue, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(DataValue, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(DataValue, IsNull, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(XmlConnection, DataDescription, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SignData, Ref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SignData, Operation, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(SignData, Target, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(DatePattern, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Bind, Ref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Bind, ContentType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Bind, TransferEncoding, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Bind, Match, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Rectangle, Hand, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EffectiveOutputPolicy, Id, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EffectiveOutputPolicy, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Date, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Date, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Encrypt, Format, CJX_Encrypt::format)
+ELEM_ATTR____(Encrypt, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Encrypt, Lock, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Draw, H, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, W, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, X, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, Y, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, HAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, Rotate, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, VAlign, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, MaxH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, MaxW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, MinH, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, MinW, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, RawValue, CJX_Draw::rawValue)
+ELEM_ATTR____(Draw, DefaultValue, CJX_Draw::defaultValue)
+ELEM_ATTR____(Draw, ColSpan, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, Locale, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Draw, AnchorType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Speak, Priority, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Speak, Disable, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Sharptext, Value, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Reasons, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(AppearanceFilter, Id, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(AppearanceFilter, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Fill, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, LineThrough, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, Typeface, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, FontHorizontalScale, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, KerningMode, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, Underline, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, BaselineShift, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, OverlinePeriod, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, LetterSpacing, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, LineThroughPeriod, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, FontVerticalScale, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, Size, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, Posture, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, Weight, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, UnderlinePeriod, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Font, Overline, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Form, Checksum, CJX_Form::checksumS)
+ELEM_ATTR____(Float, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Float, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(Value, Relevant, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Value, Override, CJX_Object::ScriptAttributeBool)
+ELEM_ATTR____(Bookend, Trailer, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Bookend, Leader, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExObject, CodeType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExObject, Archive, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExObject, CodeBase, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(ExObject, ClassId, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BindItems, Connection, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BindItems, LabelRef, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BindItems, ValueRef, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Calculate, Override, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Extras, Type, CJX_Extras::type)
+ELEM_ATTR____(Connect, Ref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Connect, Timeout, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Connect, Connection, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Connect, Usage, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Connect, DelayedOpen, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Submit, Format, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Submit, EmbedPDF, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Submit, Target, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Submit, TextEncoding, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Submit, XdpContent, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Packet, Content, CJX_Packet::content)
+ELEM_ATTR____(SubjectDN, Delimiter, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Radial, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EffectiveInputPolicy, Id, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EffectiveInputPolicy, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Transform, Ref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(LockDocument, Id, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(LockDocument, Type, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakAfter, StartNew, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakAfter, Trailer, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakAfter, TargetType, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakAfter, Target, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(BreakAfter, Leader, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Line, Slope, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Line, Hand, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Source, Db, CJX_Source::db)
+ELEM_ATTR____(Occur, Max, CJX_Occur::max)
+ELEM_ATTR____(Occur, Min, CJX_Occur::min)
+ELEM_ATTR____(Occur, Initial, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(MonthNames, Abbr, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(NumberSymbol, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Items, Ref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Items, Presence, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Items, Save, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(EventPseudoModel, FullText, CJX_EventPseudoModel::fullText)
+ELEM_ATTR____(EventPseudoModel, Reenter, CJX_EventPseudoModel::reenter)
+ELEM_ATTR____(EventPseudoModel, PrevContentType, CJX_EventPseudoModel::prevContentType)
+ELEM_ATTR____(EventPseudoModel, SoapFaultString, CJX_EventPseudoModel::soapFaultString)
+ELEM_ATTR____(EventPseudoModel, NewContentType, CJX_EventPseudoModel::newContentType)
+ELEM_ATTR____(EventPseudoModel, Modifier, CJX_EventPseudoModel::modifier)
+ELEM_ATTR____(EventPseudoModel, SelEnd, CJX_EventPseudoModel::selEnd)
+ELEM_ATTR____(EventPseudoModel, PrevText, CJX_EventPseudoModel::prevText)
+ELEM_ATTR____(EventPseudoModel, SoapFaultCode, CJX_EventPseudoModel::soapFaultCode)
+ELEM_ATTR____(EventPseudoModel, NewText, CJX_EventPseudoModel::newText)
+ELEM_ATTR____(EventPseudoModel, Change, CJX_EventPseudoModel::change)
+ELEM_ATTR____(EventPseudoModel, Shift, CJX_EventPseudoModel::shift)
+ELEM_ATTR____(EventPseudoModel, keyDown, CJX_EventPseudoModel::keyDown)
+ELEM_ATTR____(EventPseudoModel, selStart, CJX_EventPseudoModel::selStart)
+ELEM_ATTR____(EventPseudoModel, CommitKey, CJX_EventPseudoModel::commitKey)
+ELEM_ATTR____(EventPseudoModel, Target, CJX_EventPseudoModel::target)
+ELEM_ATTR____(EventPseudoModel, cancelAction, CJX_EventPseudoModel::cancelAction)
+ELEM_ATTR____(HostPseudoModel, Name, CJX_HostPseudoModel::name)
+ELEM_ATTR____(HostPseudoModel, ValidationsEnabled, CJX_HostPseudoModel::validationsEnabled)
+ELEM_ATTR____(HostPseudoModel, Title, CJX_HostPseudoModel::title)
+ELEM_ATTR____(HostPseudoModel, Platform, CJX_HostPseudoModel::platform)
+ELEM_ATTR____(HostPseudoModel, Version, CJX_HostPseudoModel::version)
+ELEM_ATTR____(HostPseudoModel, Variation, CJX_HostPseudoModel::variation)
+ELEM_ATTR____(HostPseudoModel, Language, CJX_HostPseudoModel::language)
+ELEM_ATTR____(HostPseudoModel, AppType, CJX_HostPseudoModel::appType)
+ELEM_ATTR____(HostPseudoModel, CalculationsEnabled, CJX_HostPseudoModel::calculationsEnabled)
+ELEM_ATTR____(HostPseudoModel, CurrentPage, CJX_HostPseudoModel::currentPage)
+ELEM_ATTR____(HostPseudoModel, NumPages, CJX_HostPseudoModel::numPages)
+ELEM_ATTR____(LayoutPseudoModel, Ready, CJX_LayoutPseudoModel::ready)
+ELEM_ATTR____(DataWindow, RecordsBefore, CJX_DataWindow::recordsBefore)
+ELEM_ATTR____(DataWindow, CurrentRecordNumber, CJX_DataWindow::currentRecordNumber)
+ELEM_ATTR____(DataWindow, RecordsAfter, CJX_DataWindow::recordsAfter)
+ELEM_ATTR____(DataWindow, IsDefined, CJX_DataWindow::isDefined)
+ELEM_ATTR____(List, Length, CJX_List::length)
+ELEM_ATTR____(Object, ClassName, CJX_Object::className)
+ELEM_ATTR____(ListDuplicate, Length, CJX_List::length)
+ELEM_ATTR____(Tree, Name, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Tree, All, CJX_Tree::all)
+ELEM_ATTR____(Tree, Nodes, CJX_Tree::nodes)
+ELEM_ATTR____(Tree, ClassAll, CJX_Tree::classAll)
+ELEM_ATTR____(Tree, Parent, CJX_Tree::parent)
+ELEM_ATTR____(Tree, Index, CJX_Tree::index)
+ELEM_ATTR____(Tree, ClassIndex, CJX_Tree::classIndex)
+ELEM_ATTR____(Tree, SomExpression, CJX_Tree::somExpression)
+ELEM_ATTR____(Node, Id, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(Node, Ns, CJX_Node::ns)
+ELEM_ATTR____(Node, Model, CJX_Node::model)
+ELEM_ATTR____(Node, IsContainer, CJX_Node::isContainer)
+ELEM_ATTR____(Node, IsNull, CJX_Node::isNull)
+ELEM_ATTR____(Node, OneOfChild, CJX_Node::oneOfChild)
+ELEM_ATTR____(Model, Context, CJX_Model::context)
+ELEM_ATTR____(Model, AliasNode, CJX_Model::aliasNode)
+ELEM_ATTR____(NodeWithUse, Use, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(NodeWithUse, Usehref, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(NodeWithValue, DefaultValue, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(NodeWithValue, Value, CJX_Object::ScriptSomDefaultValue)
+ELEM_ATTR____(NodeWithDesc, Desc, CJX_Object::ScriptAttributeString)
+ELEM_ATTR____(NodeWithDesc, Lock, CJX_Object::ScriptAttributeBool)
diff --git a/xfa/fxfa/parser/elements.inc b/xfa/fxfa/parser/elements.inc
new file mode 100644
index 0000000..b0246ec
--- /dev/null
+++ b/xfa/fxfa/parser/elements.inc
@@ -0,0 +1,329 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+// NOTE: A hash of 0xFFFFFFFF is reserved for an item that can not be looked up
+// by name, and the corresponding name placeholder must not hash to it.
+
+ELEM____(0x0023ee3u, "ps", Ps, NodeWithDesc)
+ELEM____(0x0025363u, "to", To, NodeWithDesc)
+ELEM____(0x002587eu, "ui", Ui, NodeWithUse)
+ELEM____(0x01c648bu, "recordSet", RecordSet, NodeWithUse)
+ELEM____(0x0171428fu, "subsetBelow", SubsetBelow, NodeWithDesc)
+ELEM____(0x01a0776au, "subformSet", SubformSet, NodeWithUse)
+ELEM____(0x02340d70u, "adobeExtensionLevel", AdobeExtensionLevel, NodeWithDesc)
+ELEM____(0x02c1c7f1u, "typeface", Typeface, Node)
+ELEM____(0x05518c25u, "break", Break, NodeWithUse)
+ELEM____(0x05fff523u, "fontInfo", FontInfo, NodeWithDesc)
+ELEM____(0x0653a227u, "numberPattern", NumberPattern, Node)
+ELEM____(0x065b4a05u, "dynamicRender", DynamicRender, NodeWithDesc)
+ELEM____(0x07e4362eu, "printScaling", PrintScaling, NodeWithDesc)
+ELEM____(0x07fe6d3au, "checkButton", CheckButton, NodeWithUse)
+ELEM____(0x080cf58fu, "datePatterns", DatePatterns, Node)
+ELEM____(0x0811929du, "sourceSet", SourceSet, Model)
+ELEM____(0x09f9d612u, "amd", Amd, NodeWithDesc)
+ELEM____(0x09f9efb6u, "arc", Arc, NodeWithUse)
+ELEM____(0x0a48835eu, "day", Day, Node)
+ELEM____(0x0a6328b8u, "era", Era, Node)
+ELEM____(0x0ae6a0a0u, "jog", Jog, NodeWithDesc)
+ELEM____(0x0b1b3d22u, "log", Log, NodeWithDesc)
+ELEM____(0x0b35439eu, "map", Map, NodeWithUse)
+ELEM____(0x0b355301u, "mdp", Mdp, NodeWithUse)
+ELEM____(0x0b420438u, "breakBefore", BreakBefore, NodeWithUse)
+ELEM____(0x0b6a091cu, "oid", Oid, NodeWithValue)
+ELEM____(0x0b84389fu, "pcl", Pcl, NodeWithDesc)
+ELEM____(0x0b843dbau, "pdf", Pdf, NodeWithDesc)
+ELEM____(0x0bb8df5du, "ref", Ref, NodeWithValue)
+ELEM____(0x0c080cd0u, "uri", Uri, NodeWithValue)
+ELEM____(0x0c56afbfu, "xdc", Xdc, NodeWithDesc)
+ELEM____(0x0c56afccu, "xdp", Xdp, NodeWithDesc)
+ELEM____(0x0c56b9ffu, "xfa", Xfa, Model)
+ELEM____(0x0c56fcb7u, "xsl", Xsl, NodeWithDesc)
+ELEM____(0x0c8b89d6u, "zpl", Zpl, NodeWithDesc)
+ELEM____(0x0c9bae94u, "cache", Cache, NodeWithDesc)
+ELEM____(0x0cb016beu, "margin", Margin, NodeWithUse)
+ELEM____(0x0e1378feu, "keyUsage", KeyUsage, NodeWithUse)
+ELEM____(0x0fe3596au, "exclude", Exclude, NodeWithDesc)
+ELEM____(0x10395ac7u, "choiceList", ChoiceList, NodeWithUse)
+ELEM____(0x1059ec18u, "level", Level, NodeWithDesc)
+ELEM____(0x10874804u, "labelPrinter", LabelPrinter, NodeWithDesc)
+ELEM____(0x10c40e03u, "calendarSymbols", CalendarSymbols, Node)
+ELEM____(0x10f1ea24u, "para", Para, NodeWithUse)
+ELEM____(0x10f1ea37u, "part", Part, NodeWithUse)
+ELEM____(0x1140975bu, "pdfa", Pdfa, Node)
+ELEM____(0x1154efe6u, "filter", Filter, NodeWithUse)
+ELEM____(0x13f41de1u, "present", Present, NodeWithDesc)
+ELEM____(0x1827e6eau, "pagination", Pagination, NodeWithDesc)
+ELEM____(0x18463707u, "encoding", Encoding, NodeWithUse)
+ELEM____(0x185e41e2u, "event", Event, NodeWithUse)
+ELEM____(0x1adb142du, "whitespace", Whitespace, NodeWithDesc)
+ELEM____(0x1f3f64c3u, "defaultUi", DefaultUi, NodeWithUse)
+ELEM____(0x204e87cbu, "dataModel", DataModel, Model)
+ELEM____(0x2057b350u, "barcode", Barcode, NodeWithUse)
+ELEM____(0x20596badu, "timePattern", TimePattern, Node)
+ELEM____(0x210b74d3u, "batchOutput", BatchOutput, NodeWithDesc)
+ELEM____(0x212ff0e2u, "enforce", Enforce, NodeWithDesc)
+ELEM____(0x21d351b4u, "currencySymbols", CurrencySymbols, Node)
+ELEM____(0x21db83c5u, "addSilentPrint", AddSilentPrint, NodeWithDesc)
+ELEM____(0x22266258u, "rename", Rename, NodeWithDesc)
+ELEM____(0x226ca8f1u, "operation", Operation, NodeWithValue)
+ELEM____(0x23e27b84u, "typefaces", Typefaces, Node)
+ELEM____(0x23f4aa75u, "subjectDNs", SubjectDNs, Node)
+ELEM____(0x240d5e8eu, "issuers", Issuers, NodeWithUse)
+ELEM____(0x24a52f8au, "wsdlConnection", WsdlConnection, Node)
+ELEM____(0x254ebd07u, "debug", Debug, NodeWithDesc)
+ELEM____(0x2655c66au, "delta", Delta, Unknown)
+ELEM____(0x26c0daecu, "eraNames", EraNames, Node)
+ELEM____(0x273ab03bu, "modifyAnnots", ModifyAnnots, NodeWithDesc)
+ELEM____(0x27875bb4u, "startNode", StartNode, NodeWithDesc)
+ELEM____(0x285d0dbcu, "button", Button, NodeWithUse)
+ELEM____(0x28dee6e9u, "format", Format, NodeWithUse)
+ELEM____(0x2a23349eu, "border", Border, NodeWithUse)
+ELEM____(0x2ae67f19u, "area", Area, NodeWithUse)
+ELEM____(0x2c3c4c67u, "hyphenation", Hyphenation, NodeWithUse)
+ELEM____(0x2d08af85u, "text", Text, NodeWithUse)
+ELEM____(0x2d71b00fu, "time", Time, NodeWithUse)
+ELEM____(0x2f16a382u, "type", Type, NodeWithDesc)
+ELEM____(0x2fe057e9u, "overprint", Overprint, NodeWithDesc)
+ELEM____(0x302aee16u, "certificates", Certificates, NodeWithUse)
+ELEM____(0x30b227dfu, "encryptionMethods", EncryptionMethods, NodeWithUse)
+ELEM____(0x32b900d1u, "setProperty", SetProperty, Node)
+ELEM____(0x337d9e45u, "printerName", PrinterName, NodeWithDesc)
+ELEM____(0x33edda4bu, "startPage", StartPage, NodeWithDesc)
+ELEM____(0x381943e4u, "pageOffset", PageOffset, NodeWithDesc)
+ELEM____(0x382106cdu, "dateTime", DateTime, NodeWithUse)
+ELEM____(0x386e7421u, "comb", Comb, NodeWithUse)
+ELEM____(0x390acd9eu, "pattern", Pattern, NodeWithUse)
+ELEM____(0x3942163eu, "ifEmpty", IfEmpty, NodeWithDesc)
+ELEM____(0x39944a7bu, "suppressBanner", SuppressBanner, NodeWithDesc)
+ELEM____(0x3b3c3dcau, "outputBin", OutputBin, NodeWithDesc)
+ELEM____(0x3b8a4024u, "field", Field, NodeWithUse)
+ELEM____(0x3c15352fu, "agent", Agent, NodeWithDesc)
+ELEM____(0x3d7e8668u, "outputXSL", OutputXSL, NodeWithDesc)
+ELEM____(0x3e1c91c5u, "adjustData", AdjustData, NodeWithDesc)
+ELEM____(0x3e7a9408u, "autoSave", AutoSave, NodeWithDesc)
+ELEM____(0x3ecead94u, "contentArea", ContentArea, NodeWithUse)
+ELEM____(0x3fadaec0u, "wsdlAddress", WsdlAddress, NodeWithValue)
+ELEM____(0x40623b5bu, "solid", Solid, NodeWithUse)
+ELEM____(0x41f0bd76u, "dateTimeSymbols", DateTimeSymbols, Node)
+ELEM____(0x444e7523u, "encryptionLevel", EncryptionLevel, NodeWithDesc)
+ELEM____(0x4523af55u, "edge", Edge, NodeWithUse)
+ELEM____(0x45d5e3c1u, "stipple", Stipple, NodeWithUse)
+ELEM____(0x475e4e87u, "attributes", Attributes, NodeWithDesc)
+ELEM____(0x487a8c87u, "versionControl", VersionControl, Node)
+ELEM____(0x48e5248cu, "meridiem", Meridiem, Node)
+ELEM____(0x48f36719u, "exclGroup", ExclGroup, NodeWithUse)
+ELEM____(0x4977356bu, "toolTip", ToolTip, NodeWithValue)
+ELEM____(0x499afeccu, "compress", Compress, NodeWithDesc)
+ELEM____(0x4a0c4948u, "reason", Reason, NodeWithValue)
+ELEM____(0x4bdcce13u, "execute", Execute, NodeWithUse)
+ELEM____(0x4c56b216u, "contentCopy", ContentCopy, NodeWithDesc)
+ELEM____(0x4cc176d3u, "dateTimeEdit", DateTimeEdit, NodeWithUse)
+ELEM____(0x4e1e39b6u, "config", Config, NodeWithDesc)
+ELEM____(0x4e2d6083u, "image", Image, NodeWithUse)
+ELEM____(0x4e814150u, "#xHTML", SharpxHTML, Node)
+ELEM____(0x4f2388c1u, "numberOfCopies", NumberOfCopies, NodeWithDesc)
+ELEM____(0x4f512e30u, "behaviorOverride", BehaviorOverride, NodeWithDesc)
+ELEM____(0x4fdc3454u, "timeStamp", TimeStamp, NodeWithUse)
+ELEM____(0x51d90546u, "month", Month, Node)
+ELEM____(0x523437e4u, "viewerPreferences", ViewerPreferences, NodeWithDesc)
+ELEM____(0x53abc1c6u, "scriptModel", ScriptModel, NodeWithDesc)
+ELEM____(0x54034c2fu, "decimal", Decimal, NodeWithUse)
+ELEM____(0x54202c9eu, "subform", Subform, NodeWithUse)
+ELEM____(0x542c7300u, "select", Select, NodeWithValue)
+ELEM____(0x5436d198u, "window", Window, NodeWithDesc)
+ELEM____(0x5473b6dcu, "localeSet", LocaleSet, NodeWithDesc)
+ELEM____(0x56ae179eu, "handler", Handler, NodeWithValue)
+ELEM____(0x570ce835u, "presence", Presence, NodeWithDesc)
+ELEM____(0x5779d65fu, "record", Record, NodeWithDesc)
+ELEM____(0x59c8f27du, "embed", Embed, NodeWithDesc)
+ELEM____(0x5a50e9e6u, "version", Version, NodeWithDesc)
+ELEM____(0x5b8383dfu, "command", Command, NodeWithUse)
+ELEM____(0x5c43c6c3u, "copies", Copies, NodeWithDesc)
+ELEM____(0x5e0c2c49u, "staple", Staple, NodeWithDesc)
+ELEM____(0x5e5083ddu, "submitFormat", SubmitFormat, NodeWithDesc)
+ELEM____(0x5e8c5d20u, "boolean", Boolean, NodeWithUse)
+ELEM____(0x60490a85u, "message", Message, NodeWithUse)
+ELEM____(0x60d4c8b1u, "output", Output, NodeWithDesc)
+ELEM____(0x61810081u, "psMap", PsMap, Node)
+ELEM____(0x62bd904bu, "excludeNS", ExcludeNS, NodeWithDesc)
+ELEM____(0x669d4f77u, "assist", Assist, NodeWithUse)
+ELEM____(0x67334a1cu, "picture", Picture, NodeWithUse)
+ELEM____(0x67fe7334u, "traversal", Traversal, NodeWithUse)
+ELEM____(0x6894589cu, "silentPrint", SilentPrint, NodeWithDesc)
+ELEM____(0x68a16bbdu, "webClient", WebClient, NodeWithDesc)
+ELEM____(0x6a4bc084u, "producer", Producer, NodeWithDesc)
+ELEM____(0x6a9e04c9u, "corner", Corner, NodeWithUse)
+ELEM____(0x6ccd7274u, "msgId", MsgId, NodeWithDesc)
+ELEM____(0x6e67921fu, "color", Color, NodeWithUse)
+ELEM____(0x6ec217a5u, "keep", Keep, NodeWithUse)
+ELEM____(0x6eef1116u, "query", Query, NodeWithUse)
+ELEM____(0x7033bfd5u, "insert", Insert, NodeWithValue)
+ELEM____(0x704af389u, "imageEdit", ImageEdit, NodeWithUse)
+ELEM____(0x7233018au, "validate", Validate, NodeWithUse)
+ELEM____(0x72ba47b4u, "digestMethods", DigestMethods, NodeWithUse)
+ELEM____(0x72f2aa7au, "numberPatterns", NumberPatterns, Node)
+ELEM____(0x74caed29u, "pageSet", PageSet, NodeWithUse)
+ELEM____(0x7568e6aeu, "integer", Integer, NodeWithUse)
+ELEM____(0x76182db9u, "soapAddress", SoapAddress, NodeWithValue)
+ELEM____(0x773146c5u, "equate", Equate, NodeWithDesc)
+ELEM____(0x77d449ddu, "formFieldFilling", FormFieldFilling, NodeWithDesc)
+ELEM____(0x7889d68au, "pageRange", PageRange, NodeWithDesc)
+ELEM____(0x7baca2e3u, "update", Update, NodeWithValue)
+ELEM____(0x7ce89001u, "connectString", ConnectString, NodeWithValue)
+ELEM____(0x7d9fd7c5u, "mode", Mode, NodeWithDesc)
+ELEM____(0x7e7e845eu, "layout", Layout, NodeWithDesc)
+ELEM____(0x7e845c34u, "#xml", Sharpxml, Node)
+ELEM____(0x7fb341dfu, "xsdConnection", XsdConnection, Node)
+ELEM____(0x7ffb51ccu, "traverse", Traverse, NodeWithUse)
+ELEM____(0x80203b5au, "encodings", Encodings, NodeWithUse)
+ELEM____(0x803550fcu, "template", Template, Model)
+ELEM____(0x803d5bbcu, "acrobat", Acrobat, NodeWithDesc)
+ELEM____(0x821d6569u, "validationMessaging", ValidationMessaging, NodeWithDesc)
+ELEM____(0x830e688fu, "signing", Signing, NodeWithUse)
+ELEM____(0x83dab9f5u, "script", Script, NodeWithUse)
+ELEM____(0x8411ebcdu, "addViewerPreferences", AddViewerPreferences, NodeWithDesc)
+ELEM____(0x8777642eu, "alwaysEmbed", AlwaysEmbed, NodeWithDesc)
+ELEM____(0x877a6b39u, "passwordEdit", PasswordEdit, NodeWithUse)
+ELEM____(0x87e84c99u, "numericEdit", NumericEdit, NodeWithUse)
+ELEM____(0x8852cdecu, "encryptionMethod", EncryptionMethod, NodeWithUse)
+ELEM____(0x891f4606u, "change", Change, NodeWithDesc)
+ELEM____(0x89939f36u, "pageArea", PageArea, NodeWithUse)
+ELEM____(0x8a9d6247u, "submitUrl", SubmitUrl, NodeWithDesc)
+ELEM____(0x8ad8b90fu, "oids", Oids, NodeWithUse)
+ELEM____(0x8b036f32u, "signature", Signature, NodeWithUse)
+ELEM____(0x8b128efbu, "ADBE_JSConsole", ADBE_JSConsole, NodeWithDesc)
+ELEM____(0x8bcfe96eu, "caption", Caption, NodeWithUse)
+ELEM____(0x8e1c2921u, "relevant", Relevant, NodeWithDesc)
+ELEM____(0x8e3f0a4bu, "flipLabel", FlipLabel, NodeWithDesc)
+ELEM____(0x900280b7u, "exData", ExData, NodeWithUse)
+ELEM____(0x91e80352u, "dayNames", DayNames, Node)
+ELEM____(0x93113b11u, "soapAction", SoapAction, NodeWithValue)
+ELEM____(0x938b09f6u, "defaultTypeface", DefaultTypeface, NodeWithDesc)
+ELEM____(0x95b37897u, "manifest", Manifest, NodeWithUse)
+ELEM____(0x97b76b54u, "overflow", Overflow, NodeWithUse)
+ELEM____(0x9a57861bu, "linear", Linear, NodeWithUse)
+ELEM____(0x9ad5a821u, "currencySymbol", CurrencySymbol, Node)
+ELEM____(0x9c6471b3u, "delete", Delete, NodeWithValue)
+ELEM____(0x9deea61du, "deltas", Deltas, ListDuplicate)
+ELEM____(0x9e67de21u, "digestMethod", DigestMethod, NodeWithUse)
+ELEM____(0x9f3e9510u, "instanceManager", InstanceManager, Node)
+ELEM____(0xa0799892u, "equateRange", EquateRange, NodeWithDesc)
+ELEM____(0xa084a381u, "medium", Medium, NodeWithUse)
+ELEM____(0xa1211b8bu, "textEdit", TextEdit, NodeWithUse)
+ELEM____(0xa17008f0u, "templateCache", TemplateCache, NodeWithDesc)
+ELEM____(0xa4f7b88fu, "compressObjectStream", CompressObjectStream, NodeWithDesc)
+ELEM____(0xa65f5d17u, "dataValue", DataValue, Node)
+ELEM____(0xa6caaa89u, "accessibleContent", AccessibleContent, NodeWithDesc)
+ELEM____(0xa94cc00bu, "includeXDPContent", IncludeXDPContent, NodeWithDesc)
+ELEM____(0xa9b081a1u, "xmlConnection", XmlConnection, Node)
+ELEM____(0xab2a3b74u, "validateApprovalSignatures", ValidateApprovalSignatures, NodeWithDesc)
+ELEM____(0xab8c5a2bu, "signData", SignData, NodeWithUse)
+ELEM____(0xabaa2cebu, "packets", Packets, NodeWithDesc)
+ELEM____(0xadba359cu, "datePattern", DatePattern, Node)
+ELEM____(0xae222b2bu, "duplexOption", DuplexOption, NodeWithDesc)
+ELEM____(0xb012effbu, "base", Base, NodeWithDesc)
+ELEM____(0xb0e5485du, "bind", Bind, NodeWithUse)
+ELEM____(0xb45d61b2u, "compression", Compression, NodeWithDesc)
+ELEM____(0xb563f0ffu, "user", User, NodeWithValue)
+ELEM____(0xb5848ad5u, "rectangle", Rectangle, NodeWithUse)
+ELEM____(0xb6dacb72u, "effectiveOutputPolicy", EffectiveOutputPolicy, NodeWithUse)
+ELEM____(0xb7d7654du, "ADBE_JSDebugger", ADBE_JSDebugger, NodeWithDesc)
+ELEM____(0xbab37f73u, "acrobat7", Acrobat7, NodeWithDesc)
+ELEM____(0xbc70081eu, "interactive", Interactive, NodeWithDesc)
+ELEM____(0xbc8fa350u, "locale", Locale, NodeWithDesc)
+ELEM____(0xbcd44940u, "currentPage", CurrentPage, NodeWithDesc)
+ELEM____(0xbde9abdau, "data", Data, NodeWithDesc)
+ELEM____(0xbde9abdeu, "date", Date, NodeWithUse)
+ELEM____(0xbe52dfbfu, "desc", Desc, NodeWithUse)
+ELEM____(0xbf4b6405u, "encrypt", Encrypt, NodeWithUse)
+ELEM____(0xbfa87cceu, "draw", Draw, NodeWithUse)
+ELEM____(0xc181ff4bu, "encryption", Encryption, NodeWithDesc)
+ELEM____(0xc1970f40u, "meridiemNames", MeridiemNames, Node)
+ELEM____(0xc5ad9f5eu, "messaging", Messaging, NodeWithDesc)
+ELEM____(0xc69549f4u, "speak", Speak, NodeWithValue)
+ELEM____(0xc7743dc7u, "dataGroup", DataGroup, Node)
+ELEM____(0xc7eb20e9u, "common", Common, NodeWithDesc)
+ELEM____(0xc85d4528u, "#text", Sharptext, Node)
+ELEM____(0xc861556au, "paginationOverride", PaginationOverride, NodeWithDesc)
+ELEM____(0xc903dabbu, "reasons", Reasons, NodeWithUse)
+ELEM____(0xc9a8127fu, "signatureProperties", SignatureProperties, NodeWithUse)
+ELEM____(0xca010c2du, "threshold", Threshold, NodeWithDesc)
+ELEM____(0xcb4c5e96u, "appearanceFilter", AppearanceFilter, NodeWithUse)
+ELEM____(0xcc92aba7u, "fill", Fill, NodeWithUse)
+ELEM____(0xcd308b77u, "font", Font, NodeWithUse)
+ELEM____(0xcd309ff4u, "form", Form, Model)
+ELEM____(0xcebcca2du, "mediumInfo", MediumInfo, NodeWithDesc)
+ELEM____(0xcfe0d643u, "certificate", Certificate, NodeWithValue)
+ELEM____(0xd012c033u, "password", Password, NodeWithValue)
+ELEM____(0xd01604bdu, "runScripts", RunScripts, NodeWithDesc)
+ELEM____(0xd1227e6fu, "trace", Trace, NodeWithDesc)
+ELEM____(0xd1532876u, "float", Float, NodeWithUse)
+ELEM____(0xd17a6c30u, "renderPolicy", RenderPolicy, NodeWithDesc)
+ELEM____(0xd58aa962u, "destination", Destination, NodeWithDesc)
+ELEM____(0xd6e27f1du, "value", Value, NodeWithUse)
+ELEM____(0xd7a14462u, "bookend", Bookend, NodeWithUse)
+ELEM____(0xd8c31254u, "exObject", ExObject, NodeWithUse)
+ELEM____(0xda6a8590u, "openAction", OpenAction, NodeWithDesc)
+ELEM____(0xdab4fb7du, "neverEmbed", NeverEmbed, NodeWithDesc)
+ELEM____(0xdb98475fu, "bindItems", BindItems, Node)
+ELEM____(0xdbfbe02eu, "calculate", Calculate, NodeWithUse)
+ELEM____(0xdd7676edu, "print", Print, NodeWithDesc)
+ELEM____(0xdde273d7u, "extras", Extras, NodeWithUse)
+ELEM____(0xde146b34u, "proto", Proto, Node)
+ELEM____(0xdf059321u, "dSigData", DSigData, Node)
+ELEM____(0xdfccf030u, "creator", Creator, NodeWithDesc)
+ELEM____(0xdff78c6au, "connect", Connect, NodeWithUse)
+ELEM____(0xe11a2cbcu, "permissions", Permissions, NodeWithDesc)
+ELEM____(0xe14c801cu, "connectionSet", ConnectionSet, Model)
+ELEM____(0xe1c83a14u, "submit", Submit, NodeWithUse)
+ELEM____(0xe29821cdu, "range", Range, NodeWithDesc)
+ELEM____(0xe38d83c7u, "linearized", Linearized, NodeWithDesc)
+ELEM____(0xe3aa2578u, "packet", Packet, Node)
+ELEM____(0xe3aa860eu, "rootElement", RootElement, NodeWithValue)
+ELEM____(0xe3e553fau, "plaintextMetadata", PlaintextMetadata, NodeWithDesc)
+ELEM____(0xe3e6e4f2u, "numberSymbols", NumberSymbols, Node)
+ELEM____(0xe3f067f6u, "printHighQuality", PrintHighQuality, NodeWithDesc)
+ELEM____(0xe3fd078cu, "driver", Driver, NodeWithDesc)
+ELEM____(0xe48b34f2u, "incrementalLoad", IncrementalLoad, NodeWithDesc)
+ELEM____(0xe550e7c2u, "subjectDN", SubjectDN, Node)
+ELEM____(0xe6669a78u, "compressLogicalStructure", CompressLogicalStructure, NodeWithDesc)
+ELEM____(0xe7a7ea02u, "incrementalMerge", IncrementalMerge, NodeWithDesc)
+ELEM____(0xe948530du, "radial", Radial, NodeWithUse)
+ELEM____(0xea8d6999u, "variables", Variables, NodeWithUse)
+ELEM____(0xeaa142c0u, "timePatterns", TimePatterns, Node)
+ELEM____(0xeb943a71u, "effectiveInputPolicy", EffectiveInputPolicy, NodeWithUse)
+ELEM____(0xef04a2bcu, "nameAttr", NameAttr, NodeWithDesc)
+ELEM____(0xf07222abu, "conformance", Conformance, NodeWithDesc)
+ELEM____(0xf0aaaadcu, "transform", Transform, NodeWithDesc)
+ELEM____(0xf1433e88u, "lockDocument", LockDocument, NodeWithUse)
+ELEM____(0xf54eb997u, "breakAfter", BreakAfter, NodeWithUse)
+ELEM____(0xf616da28u, "line", Line, NodeWithUse)
+ELEM____(0xf7055fb1u, "source", Source, NodeWithUse)
+ELEM____(0xf7eebe1cu, "occur", Occur, NodeWithUse)
+ELEM____(0xf8d10d97u, "pickTrayByPDFSize", PickTrayByPDFSize, NodeWithDesc)
+ELEM____(0xf8f19e3au, "monthNames", MonthNames, Node)
+ELEM____(0xf984149bu, "severity", Severity, NodeWithDesc)
+ELEM____(0xf9bcb037u, "groupParent", GroupParent, NodeWithDesc)
+ELEM____(0xfbc42fffu, "documentAssembly", DocumentAssembly, NodeWithDesc)
+ELEM____(0xfc78159fu, "numberSymbol", NumberSymbol, Node)
+ELEM____(0xfcbd606cu, "tagged", Tagged, NodeWithDesc)
+ELEM____(0xff063802u, "items", Items, NodeWithUse)
+ELEM____(0xffffffffu, "signaturePseudoModel", SignaturePseudoModel, Object)
+ELEM____(0xffffffffu, "eventPseudoModel", EventPseudoModel, Object)
+ELEM____(0xffffffffu, "hostPseudoModel", HostPseudoModel, Object)
+ELEM____(0xffffffffu, "layoutPseudoModel", LayoutPseudoModel, Object)
+ELEM____(0xffffffffu, "dataWindow", DataWindow, Object)
+ELEM____(0xffffffffu, "treeList,", TreeList, ListDuplicate)
+ELEM____(0xffffffffu, "logPseudoModel", LogPseudoModel, Object)
+ELEM____(0xffffffffu, "list", List, ListDuplicate)
+ELEM____(0xffffffffu, "object", Object, Unknown)
+ELEM____(0xffffffffu, "******", ListDuplicate, Object)
+ELEM____(0xffffffffu, "tree", Tree, Object)
+ELEM____(0xffffffffu, "node", Node, Tree)
+ELEM____(0xffffffffu, "model", Model, Node)
+ELEM____(0xffffffffu, "******", NodeWithUse, Node)
+ELEM____(0xffffffffu, "******", NodeWithValue, NodeWithUse)
+ELEM____(0xffffffffu, "******", NodeWithDesc, Node)
diff --git a/xfa/fxfa/parser/packets.inc b/xfa/fxfa/parser/packets.inc
new file mode 100644
index 0000000..58e49a4
--- /dev/null
+++ b/xfa/fxfa/parser/packets.inc
@@ -0,0 +1,21 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+PCKT____(0x0811929du, "sourceSet", SourceSet, L"http://www.xfa.org/schema/xfa-source-set/", NOMATCH, SUPPORTONE)
+PCKT____(0x0b843dbau, "pdf", Pdf, L"http://ns.adobe.com/xdp/pdf/", COMPLETEMATCH, SUPPORTONE)
+PCKT____(0x0c56afbfu, "xdc", Xdc, L"http://www.xfa.org/schema/xdc/", NOMATCH, SUPPORTONE)
+PCKT____(0x0c56afccu, "xdp", Xdp, L"http://ns.adobe.com/xdp/", COMPLETEMATCH, SUPPORTONE)
+PCKT____(0x132a8fbcu, "xmpmeta", Xmpmeta, L"http://ns.adobe.com/xmpmeta/", NOMATCH, SUPPORTMANY)
+PCKT____(0x48d004a8u, "xfdf", Xfdf, L"http://ns.adobe.com/xfdf/", NOMATCH, SUPPORTONE)
+PCKT____(0x4e1e39b6u, "config", Config, L"http://www.xfa.org/schema/xci/", NOMATCH, SUPPORTONE)
+PCKT____(0x5473b6dcu, "localeSet", LocaleSet, L"http://www.xfa.org/schema/xfa-locale-set/", NOMATCH, SUPPORTONE)
+PCKT____(0x6038580au, "stylesheet", Stylesheet, L"http://www.w3.org/1999/XSL/Transform", NOMATCH, SUPPORTMANY)
+PCKT____(0x803550fcu, "template", Template,  L"http://www.xfa.org/schema/xfa-template/", NOMATCH, SUPPORTONE)
+PCKT____(0x8b036f32u, "signature", Signature, L"http://www.w3.org/2000/09/xmldsig#", NOMATCH, SUPPORTONE)
+PCKT____(0x99b95079u, "datasets", Datasets,  L"http://www.xfa.org/schema/xfa-data/", PREFIXMATCH, SUPPORTONE)
+PCKT____(0xcd309ff4u, "form", Form, L"http://www.xfa.org/schema/xfa-form/", NOMATCH, SUPPORTONE)
+PCKT____(0xe14c801cu, "connectionSet", ConnectionSet, L"http://www.xfa.org/schema/xfa-connection-set/", NOMATCH, SUPPORTONE)
+PCKT____(0xffffffffu, "user", User, nullptr, NOMATCH, SUPPORTMANY)
diff --git a/xfa/fxfa/parser/xfa_basic_data.cpp b/xfa/fxfa/parser/xfa_basic_data.cpp
new file mode 100644
index 0000000..e7b01c9
--- /dev/null
+++ b/xfa/fxfa/parser/xfa_basic_data.cpp
@@ -0,0 +1,209 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+
+#include <utility>
+
+#include "fxjs/xfa/cjx_boolean.h"
+#include "fxjs/xfa/cjx_container.h"
+#include "fxjs/xfa/cjx_datawindow.h"
+#include "fxjs/xfa/cjx_delta.h"
+#include "fxjs/xfa/cjx_desc.h"
+#include "fxjs/xfa/cjx_draw.h"
+#include "fxjs/xfa/cjx_encrypt.h"
+#include "fxjs/xfa/cjx_eventpseudomodel.h"
+#include "fxjs/xfa/cjx_exclgroup.h"
+#include "fxjs/xfa/cjx_extras.h"
+#include "fxjs/xfa/cjx_field.h"
+#include "fxjs/xfa/cjx_form.h"
+#include "fxjs/xfa/cjx_handler.h"
+#include "fxjs/xfa/cjx_hostpseudomodel.h"
+#include "fxjs/xfa/cjx_instancemanager.h"
+#include "fxjs/xfa/cjx_layoutpseudomodel.h"
+#include "fxjs/xfa/cjx_logpseudomodel.h"
+#include "fxjs/xfa/cjx_manifest.h"
+#include "fxjs/xfa/cjx_model.h"
+#include "fxjs/xfa/cjx_node.h"
+#include "fxjs/xfa/cjx_occur.h"
+#include "fxjs/xfa/cjx_packet.h"
+#include "fxjs/xfa/cjx_script.h"
+#include "fxjs/xfa/cjx_signaturepseudomodel.h"
+#include "fxjs/xfa/cjx_source.h"
+#include "fxjs/xfa/cjx_subform.h"
+#include "fxjs/xfa/cjx_textnode.h"
+#include "fxjs/xfa/cjx_tree.h"
+#include "fxjs/xfa/cjx_treelist.h"
+#include "fxjs/xfa/cjx_wsdlconnection.h"
+#include "fxjs/xfa/cjx_xfa.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+namespace {
+
+struct PacketRecord {
+  XFA_PacketType packet_type;
+  uint32_t hash;
+  uint32_t flags;
+  const wchar_t* name;
+  const wchar_t* uri;
+};
+
+const PacketRecord g_PacketTable[] = {
+#undef PCKT____
+#define PCKT____(a, b, c, d, e, f)                                          \
+  {XFA_PacketType::c, a, XFA_XDPPACKET_FLAGS_##e | XFA_XDPPACKET_FLAGS_##f, \
+   L##b, d},
+#include "xfa/fxfa/parser/packets.inc"
+#undef PCKT____
+};
+
+struct ElementRecord {
+  uint32_t hash;  // Hashed as wide string.
+  XFA_Element element;
+  XFA_Element parent;
+  const char* name;
+};
+
+const ElementRecord g_ElementTable[] = {
+#undef ELEM____
+#define ELEM____(a, b, c, d) {a, XFA_Element::c, XFA_Element::d, b},
+#include "xfa/fxfa/parser/elements.inc"
+#undef ELEM____
+};
+
+struct AttributeRecord {
+  uint32_t hash;  // Hashed as wide string.
+  XFA_Attribute attribute;
+  XFA_ScriptType script_type;
+  const char* name;
+};
+
+const AttributeRecord g_AttributeTable[] = {
+#undef ATTR____
+#define ATTR____(a, b, c, d) {a, XFA_Attribute::c, XFA_ScriptType::d, b},
+#include "xfa/fxfa/parser/attributes.inc"
+#undef ATTR____
+};
+
+struct AttributeValueRecord {
+  uint32_t uHash;  // |pName| hashed as WideString.
+  XFA_AttributeValue eName;
+  const char* pName;
+};
+
+const AttributeValueRecord g_AttributeValueTable[] = {
+#undef VALUE____
+#define VALUE____(a, b, c) {a, XFA_AttributeValue::c, b},
+#include "xfa/fxfa/parser/attribute_values.inc"
+#undef VALUE____
+};
+
+struct ElementAttributeRecord {
+  XFA_Element element;
+  XFA_Attribute attribute;
+  XFA_ATTRIBUTE_CALLBACK callback;
+};
+
+const ElementAttributeRecord g_ElementAttributeTable[] = {
+#undef ELEM_ATTR____
+#define ELEM_ATTR____(a, b, c) {XFA_Element::a, XFA_Attribute::b, c##_static},
+#include "xfa/fxfa/parser/element_attributes.inc"
+#undef ELEM_ATTR____
+};
+
+}  // namespace
+
+XFA_PACKETINFO XFA_GetPacketByIndex(XFA_PacketType ePacket) {
+  const PacketRecord* pRecord = &g_PacketTable[static_cast<uint8_t>(ePacket)];
+  return {pRecord->name, pRecord->packet_type, pRecord->uri, pRecord->flags};
+}
+
+Optional<XFA_PACKETINFO> XFA_GetPacketByName(WideStringView wsName) {
+  uint32_t hash = FX_HashCode_GetW(wsName, false);
+  auto* elem = std::lower_bound(
+      std::begin(g_PacketTable), std::end(g_PacketTable), hash,
+      [](const PacketRecord& a, uint32_t hash) { return a.hash < hash; });
+  if (elem != std::end(g_PacketTable) && elem->name == wsName)
+    return XFA_GetPacketByIndex(elem->packet_type);
+  return {};
+}
+
+ByteStringView XFA_ElementToName(XFA_Element elem) {
+  return g_ElementTable[static_cast<size_t>(elem)].name;
+}
+
+XFA_Element XFA_GetElementByName(WideStringView name) {
+  uint32_t hash = FX_HashCode_GetW(name, false);
+  auto* elem = std::lower_bound(
+      std::begin(g_ElementTable), std::end(g_ElementTable), hash,
+      [](const ElementRecord& a, uint32_t hash) { return a.hash < hash; });
+  if (elem != std::end(g_ElementTable) && name.EqualsASCII(elem->name))
+    return elem->element;
+  return XFA_Element::Unknown;
+}
+
+ByteStringView XFA_AttributeToName(XFA_Attribute attr) {
+  return g_AttributeTable[static_cast<size_t>(attr)].name;
+}
+
+Optional<XFA_ATTRIBUTEINFO> XFA_GetAttributeByName(WideStringView name) {
+  uint32_t hash = FX_HashCode_GetW(name, false);
+  auto* elem = std::lower_bound(
+      std::begin(g_AttributeTable), std::end(g_AttributeTable), hash,
+      [](const AttributeRecord& a, uint32_t hash) { return a.hash < hash; });
+  if (elem != std::end(g_AttributeTable) && name.EqualsASCII(elem->name)) {
+    XFA_ATTRIBUTEINFO result;
+    result.attribute = elem->attribute;
+    result.eValueType = elem->script_type;
+    return result;
+  }
+  return {};
+}
+
+ByteStringView XFA_AttributeValueToName(XFA_AttributeValue item) {
+  return g_AttributeValueTable[static_cast<int32_t>(item)].pName;
+}
+
+Optional<XFA_AttributeValue> XFA_GetAttributeValueByName(WideStringView name) {
+  auto* it = std::lower_bound(std::begin(g_AttributeValueTable),
+                              std::end(g_AttributeValueTable),
+                              FX_HashCode_GetW(name, false),
+                              [](const AttributeValueRecord& arg,
+                                 uint32_t hash) { return arg.uHash < hash; });
+  if (it != std::end(g_AttributeValueTable) && name.EqualsASCII(it->pName))
+    return it->eName;
+
+  return {};
+}
+
+Optional<XFA_SCRIPTATTRIBUTEINFO> XFA_GetScriptAttributeByName(
+    XFA_Element element,
+    WideStringView attribute_name) {
+  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(attribute_name);
+  if (!attr.has_value())
+    return {};
+
+  while (element != XFA_Element::Unknown) {
+    auto compound_key = std::make_pair(element, attr.value().attribute);
+    auto* it = std::lower_bound(
+        std::begin(g_ElementAttributeTable), std::end(g_ElementAttributeTable),
+        compound_key,
+        [](const ElementAttributeRecord& arg,
+           const std::pair<XFA_Element, XFA_Attribute>& key) {
+          return std::make_pair(arg.element, arg.attribute) < key;
+        });
+    if (it != std::end(g_ElementAttributeTable) &&
+        compound_key == std::make_pair(it->element, it->attribute)) {
+      XFA_SCRIPTATTRIBUTEINFO result;
+      result.attribute = attr.value().attribute;
+      result.eValueType = attr.value().eValueType;
+      result.callback = it->callback;
+      return result;
+    }
+    element = g_ElementTable[static_cast<size_t>(element)].parent;
+  }
+  return {};
+}
diff --git a/xfa/fxfa/parser/xfa_basic_data.h b/xfa/fxfa/parser/xfa_basic_data.h
index b394d35..4c3e0c7 100644
--- a/xfa/fxfa/parser/xfa_basic_data.h
+++ b/xfa/fxfa/parser/xfa_basic_data.h
@@ -7,17 +7,45 @@
 #ifndef XFA_FXFA_PARSER_XFA_BASIC_DATA_H_
 #define XFA_FXFA_PARSER_XFA_BASIC_DATA_H_
 
-#include <map>
+#include <stddef.h>
 
+#include "core/fxcrt/widestring.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/optional.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-extern const XFA_AttributeEnumInfo g_XFAEnumData[];
-extern const int32_t g_iXFAEnumCount;
+struct XFA_PACKETINFO {
+  const wchar_t* name;
+  XFA_PacketType packet_type;
+  const wchar_t* uri;
+  uint32_t flags;
+};
 
-extern const XFA_SCRIPTHIERARCHY g_XFAScriptIndex[];
-extern const int32_t g_iScriptIndexCount;
+struct XFA_ATTRIBUTEINFO {
+  XFA_Attribute attribute;
+  XFA_ScriptType eValueType;
+};
 
-extern const XFA_SCRIPTATTRIBUTEINFO g_SomAttributeData[];
-extern const int32_t g_iSomAttributeCount;
+struct XFA_SCRIPTATTRIBUTEINFO {
+  XFA_Attribute attribute;
+  XFA_ScriptType eValueType;
+  XFA_ATTRIBUTE_CALLBACK callback = nullptr;
+};
+
+XFA_PACKETINFO XFA_GetPacketByIndex(XFA_PacketType ePacket);
+Optional<XFA_PACKETINFO> XFA_GetPacketByName(WideStringView wsName);
+
+ByteStringView XFA_ElementToName(XFA_Element elem);
+XFA_Element XFA_GetElementByName(WideStringView name);
+
+ByteStringView XFA_AttributeToName(XFA_Attribute attr);
+Optional<XFA_ATTRIBUTEINFO> XFA_GetAttributeByName(WideStringView name);
+
+ByteStringView XFA_AttributeValueToName(XFA_AttributeValue item);
+Optional<XFA_AttributeValue> XFA_GetAttributeValueByName(WideStringView name);
+
+Optional<XFA_SCRIPTATTRIBUTEINFO> XFA_GetScriptAttributeByName(
+    XFA_Element eElement,
+    WideStringView wsAttributeName);
 
 #endif  // XFA_FXFA_PARSER_XFA_BASIC_DATA_H_
diff --git a/xfa/fxfa/parser/xfa_basic_data_element_script.cpp b/xfa/fxfa/parser/xfa_basic_data_element_script.cpp
deleted file mode 100644
index 80a2bf3..0000000
--- a/xfa/fxfa/parser/xfa_basic_data_element_script.cpp
+++ /dev/null
@@ -1,3808 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/xfa_basic_data.h"
-
-#include "fxjs/xfa/cjx_arc.h"
-#include "fxjs/xfa/cjx_area.h"
-#include "fxjs/xfa/cjx_assist.h"
-#include "fxjs/xfa/cjx_barcode.h"
-#include "fxjs/xfa/cjx_bind.h"
-#include "fxjs/xfa/cjx_binditems.h"
-#include "fxjs/xfa/cjx_bookend.h"
-#include "fxjs/xfa/cjx_boolean.h"
-#include "fxjs/xfa/cjx_border.h"
-#include "fxjs/xfa/cjx_break.h"
-#include "fxjs/xfa/cjx_breakafter.h"
-#include "fxjs/xfa/cjx_breakbefore.h"
-#include "fxjs/xfa/cjx_button.h"
-#include "fxjs/xfa/cjx_calculate.h"
-#include "fxjs/xfa/cjx_caption.h"
-#include "fxjs/xfa/cjx_certificate.h"
-#include "fxjs/xfa/cjx_certificates.h"
-#include "fxjs/xfa/cjx_checkbutton.h"
-#include "fxjs/xfa/cjx_choicelist.h"
-#include "fxjs/xfa/cjx_color.h"
-#include "fxjs/xfa/cjx_comb.h"
-#include "fxjs/xfa/cjx_command.h"
-#include "fxjs/xfa/cjx_connect.h"
-#include "fxjs/xfa/cjx_connectstring.h"
-#include "fxjs/xfa/cjx_contentarea.h"
-#include "fxjs/xfa/cjx_corner.h"
-#include "fxjs/xfa/cjx_datavalue.h"
-#include "fxjs/xfa/cjx_datawindow.h"
-#include "fxjs/xfa/cjx_date.h"
-#include "fxjs/xfa/cjx_datetime.h"
-#include "fxjs/xfa/cjx_datetimeedit.h"
-#include "fxjs/xfa/cjx_decimal.h"
-#include "fxjs/xfa/cjx_defaultui.h"
-#include "fxjs/xfa/cjx_delete.h"
-#include "fxjs/xfa/cjx_delta.h"
-#include "fxjs/xfa/cjx_desc.h"
-#include "fxjs/xfa/cjx_digestmethod.h"
-#include "fxjs/xfa/cjx_digestmethods.h"
-#include "fxjs/xfa/cjx_draw.h"
-#include "fxjs/xfa/cjx_edge.h"
-#include "fxjs/xfa/cjx_encoding.h"
-#include "fxjs/xfa/cjx_encodings.h"
-#include "fxjs/xfa/cjx_encrypt.h"
-#include "fxjs/xfa/cjx_event.h"
-#include "fxjs/xfa/cjx_eventpseudomodel.h"
-#include "fxjs/xfa/cjx_exclgroup.h"
-#include "fxjs/xfa/cjx_exdata.h"
-#include "fxjs/xfa/cjx_execute.h"
-#include "fxjs/xfa/cjx_exobject.h"
-#include "fxjs/xfa/cjx_extras.h"
-#include "fxjs/xfa/cjx_field.h"
-#include "fxjs/xfa/cjx_fill.h"
-#include "fxjs/xfa/cjx_filter.h"
-#include "fxjs/xfa/cjx_float.h"
-#include "fxjs/xfa/cjx_font.h"
-#include "fxjs/xfa/cjx_format.h"
-#include "fxjs/xfa/cjx_handler.h"
-#include "fxjs/xfa/cjx_hostpseudomodel.h"
-#include "fxjs/xfa/cjx_image.h"
-#include "fxjs/xfa/cjx_imageedit.h"
-#include "fxjs/xfa/cjx_insert.h"
-#include "fxjs/xfa/cjx_instancemanager.h"
-#include "fxjs/xfa/cjx_integer.h"
-#include "fxjs/xfa/cjx_issuers.h"
-#include "fxjs/xfa/cjx_items.h"
-#include "fxjs/xfa/cjx_keep.h"
-#include "fxjs/xfa/cjx_keyusage.h"
-#include "fxjs/xfa/cjx_layoutpseudomodel.h"
-#include "fxjs/xfa/cjx_line.h"
-#include "fxjs/xfa/cjx_linear.h"
-#include "fxjs/xfa/cjx_logpseudomodel.h"
-#include "fxjs/xfa/cjx_manifest.h"
-#include "fxjs/xfa/cjx_map.h"
-#include "fxjs/xfa/cjx_margin.h"
-#include "fxjs/xfa/cjx_mdp.h"
-#include "fxjs/xfa/cjx_medium.h"
-#include "fxjs/xfa/cjx_message.h"
-#include "fxjs/xfa/cjx_node.h"
-#include "fxjs/xfa/cjx_numericedit.h"
-#include "fxjs/xfa/cjx_occur.h"
-#include "fxjs/xfa/cjx_oid.h"
-#include "fxjs/xfa/cjx_oids.h"
-#include "fxjs/xfa/cjx_operation.h"
-#include "fxjs/xfa/cjx_overflow.h"
-#include "fxjs/xfa/cjx_packet.h"
-#include "fxjs/xfa/cjx_pagearea.h"
-#include "fxjs/xfa/cjx_pageset.h"
-#include "fxjs/xfa/cjx_para.h"
-#include "fxjs/xfa/cjx_password.h"
-#include "fxjs/xfa/cjx_passwordedit.h"
-#include "fxjs/xfa/cjx_pattern.h"
-#include "fxjs/xfa/cjx_picture.h"
-#include "fxjs/xfa/cjx_query.h"
-#include "fxjs/xfa/cjx_radial.h"
-#include "fxjs/xfa/cjx_reason.h"
-#include "fxjs/xfa/cjx_reasons.h"
-#include "fxjs/xfa/cjx_recordset.h"
-#include "fxjs/xfa/cjx_rectangle.h"
-#include "fxjs/xfa/cjx_ref.h"
-#include "fxjs/xfa/cjx_rootelement.h"
-#include "fxjs/xfa/cjx_script.h"
-#include "fxjs/xfa/cjx_select.h"
-#include "fxjs/xfa/cjx_setproperty.h"
-#include "fxjs/xfa/cjx_signature.h"
-#include "fxjs/xfa/cjx_signatureproperties.h"
-#include "fxjs/xfa/cjx_signaturepseudomodel.h"
-#include "fxjs/xfa/cjx_signdata.h"
-#include "fxjs/xfa/cjx_signing.h"
-#include "fxjs/xfa/cjx_soapaction.h"
-#include "fxjs/xfa/cjx_soapaddress.h"
-#include "fxjs/xfa/cjx_solid.h"
-#include "fxjs/xfa/cjx_source.h"
-#include "fxjs/xfa/cjx_sourceset.h"
-#include "fxjs/xfa/cjx_speak.h"
-#include "fxjs/xfa/cjx_stipple.h"
-#include "fxjs/xfa/cjx_subform.h"
-#include "fxjs/xfa/cjx_subformset.h"
-#include "fxjs/xfa/cjx_subjectdn.h"
-#include "fxjs/xfa/cjx_subjectdns.h"
-#include "fxjs/xfa/cjx_submit.h"
-#include "fxjs/xfa/cjx_text.h"
-#include "fxjs/xfa/cjx_textedit.h"
-#include "fxjs/xfa/cjx_time.h"
-#include "fxjs/xfa/cjx_timestamp.h"
-#include "fxjs/xfa/cjx_tooltip.h"
-#include "fxjs/xfa/cjx_traversal.h"
-#include "fxjs/xfa/cjx_traverse.h"
-#include "fxjs/xfa/cjx_tree.h"
-#include "fxjs/xfa/cjx_treelist.h"
-#include "fxjs/xfa/cjx_ui.h"
-#include "fxjs/xfa/cjx_update.h"
-#include "fxjs/xfa/cjx_uri.h"
-#include "fxjs/xfa/cjx_user.h"
-#include "fxjs/xfa/cjx_validate.h"
-#include "fxjs/xfa/cjx_value.h"
-#include "fxjs/xfa/cjx_variables.h"
-#include "fxjs/xfa/cjx_wsdladdress.h"
-#include "fxjs/xfa/cjx_wsdlconnection.h"
-#include "fxjs/xfa/cjx_xfa.h"
-#include "fxjs/xfa/cjx_xmlconnection.h"
-#include "fxjs/xfa/cjx_xsdconnection.h"
-#include "xfa/fxfa/fxfa_basic.h"
-
-const XFA_SCRIPTHIERARCHY g_XFAScriptIndex[] = {
-    {/* ps */ 0, 2, 316},
-    {/* to */ 2, 2, 316},
-    {/* ui */ 4, 2, 316},
-    {/* recordSet */ 6, 8, 316},
-    {/* subsetBelow */ 14, 4, 316},
-    {/* subformSet */ 18, 5, 317},
-    {/* adobeExtensionLevel */ 23, 2, 316},
-    {/* typeface */ 25, 1, 316},
-    {/* break */ 26, 12, 316},
-    {/* fontInfo */ 38, 2, 316},
-    {/* numberPattern */ 40, 1, 316},
-    {/* dynamicRender */ 41, 3, 316},
-    {/* printScaling */ 44, 2, 316},
-    {/* checkButton */ 46, 6, 316},
-    {/* datePatterns */ 52, 0, 316},
-    {/* sourceSet */ 52, 2, 319},
-    {/* amd */ 54, 2, 316},
-    {/* arc */ 56, 6, 316},
-    {/* day */ 62, 0, 316},
-    {/* era */ 62, 0, 316},
-    {/* jog */ 62, 2, 316},
-    {/* log */ 64, 2, 316},
-    {/* map */ 66, 6, 316},
-    {/* mdp */ 72, 4, 316},
-    {/* breakBefore */ 76, 7, 316},
-    {/* oid */ 83, 2, 320},
-    {/* pcl */ 85, 3, 316},
-    {/* pdf */ 88, 3, 316},
-    {/* ref */ 91, 2, 320},
-    {/* uri */ 93, 6, 320},
-    {/* xdc */ 99, 4, 316},
-    {/* xdp */ 103, 2, 316},
-    {/* xfa */ 105, 3, 319},
-    {/* xsl */ 108, 4, 316},
-    {/* zpl */ 112, 3, 316},
-    {/* cache */ 115, 2, 316},
-    {/* margin */ 117, 6, 316},
-    {/* keyUsage */ 123, 12, 316},
-    {/* exclude */ 135, 2, 316},
-    {/* choiceList */ 137, 5, 316},
-    {/* level */ 142, 2, 316},
-    {/* labelPrinter */ 144, 3, 316},
-    {/* calendarSymbols */ 147, 1, 316},
-    {/* para */ 148, 14, 316},
-    {/* part */ 162, 2, 316},
-    {/* pdfa */ 164, 2, 316},
-    {/* filter */ 166, 3, 316},
-    {/* present */ 169, 2, 316},
-    {/* pagination */ 171, 2, 316},
-    {/* encoding */ 173, 2, 316},
-    {/* event */ 175, 4, 316},
-    {/* whitespace */ 179, 2, 316},
-    {/* defaultUi */ 181, 2, 316},
-    {/* dataModel */ 183, 0, 319},
-    {/* barcode */ 183, 20, 316},
-    {/* timePattern */ 203, 1, 316},
-    {/* batchOutput */ 204, 3, 316},
-    {/* enforce */ 207, 2, 316},
-    {/* currencySymbols */ 209, 0, 316},
-    {/* addSilentPrint */ 209, 2, 316},
-    {/* rename */ 211, 2, 316},
-    {/* operation */ 213, 4, 320},
-    {/* typefaces */ 217, 0, 316},
-    {/* subjectDNs */ 217, 1, 316},
-    {/* issuers */ 218, 3, 316},
-    {/* signaturePseudoModel */ 221, 0, 312},
-    {/* wsdlConnection */ 221, 1, 316},
-    {/* debug */ 222, 2, 316},
-    {/* delta */ 224, 3, -1},
-    {/* eraNames */ 227, 0, 316},
-    {/* modifyAnnots */ 227, 2, 316},
-    {/* startNode */ 229, 2, 316},
-    {/* button */ 231, 3, 316},
-    {/* format */ 234, 2, 316},
-    {/* border */ 236, 6, 316},
-    {/* area */ 242, 10, 317},
-    {/* hyphenation */ 252, 9, 316},
-    {/* text */ 261, 5, 318},
-    {/* time */ 266, 4, 318},
-    {/* type */ 270, 2, 316},
-    {/* overprint */ 272, 2, 316},
-    {/* certificates */ 274, 5, 316},
-    {/* encryptionMethods */ 279, 3, 316},
-    {/* setProperty */ 282, 2, 316},
-    {/* printerName */ 284, 2, 316},
-    {/* startPage */ 286, 2, 316},
-    {/* pageOffset */ 288, 2, 316},
-    {/* dateTime */ 290, 4, 316},
-    {/* comb */ 294, 3, 316},
-    {/* pattern */ 297, 3, 316},
-    {/* ifEmpty */ 300, 2, 316},
-    {/* suppressBanner */ 302, 2, 316},
-    {/* outputBin */ 304, 2, 316},
-    {/* field */ 306, 36, 317},
-    {/* agent */ 342, 3, 316},
-    {/* outputXSL */ 345, 2, 316},
-    {/* adjustData */ 347, 2, 316},
-    {/* autoSave */ 349, 2, 316},
-    {/* contentArea */ 351, 7, 317},
-    {/* eventPseudoModel */ 358, 16, 312},
-    {/* wsdlAddress */ 374, 2, 320},
-    {/* solid */ 376, 2, 316},
-    {/* dateTimeSymbols */ 378, 0, 316},
-    {/* encryptionLevel */ 378, 2, 316},
-    {/* edge */ 380, 6, 316},
-    {/* stipple */ 386, 3, 316},
-    {/* attributes */ 389, 2, 316},
-    {/* versionControl */ 391, 4, 316},
-    {/* meridiem */ 395, 0, 316},
-    {/* exclGroup */ 395, 30, 316},
-    {/* toolTip */ 425, 2, 320},
-    {/* compress */ 427, 3, 316},
-    {/* reason */ 430, 2, 320},
-    {/* execute */ 432, 5, 316},
-    {/* contentCopy */ 437, 2, 316},
-    {/* dateTimeEdit */ 439, 3, 316},
-    {/* config */ 442, 2, 316},
-    {/* image */ 444, 8, 316},
-    {/* #xHTML */ 452, 1, 316},
-    {/* numberOfCopies */ 453, 2, 316},
-    {/* behaviorOverride */ 455, 2, 316},
-    {/* timeStamp */ 457, 4, 316},
-    {/* month */ 461, 0, 316},
-    {/* viewerPreferences */ 461, 2, 316},
-    {/* scriptModel */ 463, 2, 316},
-    {/* decimal */ 465, 6, 318},
-    {/* subform */ 471, 31, 317},
-    {/* select */ 502, 2, 320},
-    {/* window */ 504, 2, 316},
-    {/* localeSet */ 506, 2, 316},
-    {/* handler */ 508, 4, 320},
-    {/* hostPseudoModel */ 512, 11, 312},
-    {/* presence */ 523, 2, 316},
-    {/* record */ 525, 2, 316},
-    {/* embed */ 527, 2, 316},
-    {/* version */ 529, 2, 316},
-    {/* command */ 531, 3, 316},
-    {/* copies */ 534, 2, 316},
-    {/* staple */ 536, 3, 316},
-    {/* submitFormat */ 539, 3, 316},
-    {/* boolean */ 542, 4, 318},
-    {/* message */ 546, 4, 316},
-    {/* output */ 550, 2, 316},
-    {/* psMap */ 552, 0, 316},
-    {/* excludeNS */ 552, 2, 316},
-    {/* assist */ 554, 3, 316},
-    {/* picture */ 557, 6, 316},
-    {/* traversal */ 563, 2, 316},
-    {/* silentPrint */ 565, 2, 316},
-    {/* webClient */ 567, 3, 316},
-    {/* layoutPseudoModel */ 570, 1, 312},
-    {/* producer */ 571, 2, 316},
-    {/* corner */ 573, 8, 316},
-    {/* msgId */ 581, 2, 316},
-    {/* color */ 583, 4, 316},
-    {/* keep */ 587, 5, 316},
-    {/* query */ 592, 3, 316},
-    {/* insert */ 595, 2, 320},
-    {/* imageEdit */ 597, 3, 316},
-    {/* validate */ 600, 7, 316},
-    {/* digestMethods */ 607, 3, 316},
-    {/* numberPatterns */ 610, 0, 316},
-    {/* pageSet */ 610, 4, 317},
-    {/* integer */ 614, 4, 318},
-    {/* soapAddress */ 618, 2, 320},
-    {/* equate */ 620, 5, 316},
-    {/* formFieldFilling */ 625, 2, 316},
-    {/* pageRange */ 627, 2, 316},
-    {/* update */ 629, 2, 320},
-    {/* connectString */ 631, 2, 320},
-    {/* mode */ 633, 4, 316},
-    {/* layout */ 637, 2, 316},
-    {/* #xml */ 639, 1, 316},
-    {/* xsdConnection */ 640, 1, 316},
-    {/* traverse */ 641, 4, 316},
-    {/* encodings */ 645, 3, 316},
-    {/* template */ 648, 2, 319},
-    {/* acrobat */ 650, 2, 316},
-    {/* validationMessaging */ 652, 2, 316},
-    {/* signing */ 654, 3, 316},
-    {/* dataWindow */ 657, 4, 312},
-    {/* script */ 661, 10, 316},
-    {/* addViewerPreferences */ 671, 2, 316},
-    {/* alwaysEmbed */ 673, 4, 316},
-    {/* passwordEdit */ 677, 4, 316},
-    {/* numericEdit */ 681, 3, 316},
-    {/* encryptionMethod */ 684, 2, 316},
-    {/* change */ 686, 2, 316},
-    {/* pageArea */ 688, 8, 317},
-    {/* submitUrl */ 696, 3, 316},
-    {/* oids */ 699, 3, 316},
-    {/* signature */ 702, 2, 316},
-    {/* ADBE_JSConsole */ 704, 2, 316},
-    {/* caption */ 706, 5, 316},
-    {/* relevant */ 711, 4, 316},
-    {/* flipLabel */ 715, 2, 316},
-    {/* exData */ 717, 8, 318},
-    {/* dayNames */ 725, 1, 316},
-    {/* soapAction */ 726, 2, 320},
-    {/* defaultTypeface */ 728, 3, 316},
-    {/* manifest */ 731, 4, 316},
-    {/* overflow */ 735, 5, 316},
-    {/* linear */ 740, 3, 316},
-    {/* currencySymbol */ 743, 1, 316},
-    {/* delete */ 744, 2, 320},
-    {/* deltas */ 746, 0, 313},
-    {/* digestMethod */ 746, 2, 316},
-    {/* instanceManager */ 748, 3, 316},
-    {/* equateRange */ 751, 5, 316},
-    {/* medium */ 756, 7, 316},
-    {/* textEdit */ 763, 6, 316},
-    {/* templateCache */ 769, 3, 316},
-    {/* compressObjectStream */ 772, 2, 316},
-    {/* dataValue */ 774, 5, 316},
-    {/* accessibleContent */ 779, 2, 316},
-    {/* nodeList */ 781, 0, 314},
-    {/* includeXDPContent */ 781, 2, 316},
-    {/* xmlConnection */ 783, 1, 316},
-    {/* validateApprovalSignatures */ 784, 2, 316},
-    {/* signData */ 786, 5, 316},
-    {/* packets */ 791, 2, 316},
-    {/* datePattern */ 793, 1, 316},
-    {/* duplexOption */ 794, 2, 316},
-    {/* base */ 796, 2, 316},
-    {/* bind */ 798, 6, 316},
-    {/* compression */ 804, 2, 316},
-    {/* user */ 806, 2, 320},
-    {/* rectangle */ 808, 3, 316},
-    {/* effectiveOutputPolicy */ 811, 4, 316},
-    {/* ADBE_JSDebugger */ 815, 2, 316},
-    {/* acrobat7 */ 817, 2, 316},
-    {/* interactive */ 819, 2, 316},
-    {/* locale */ 821, 2, 316},
-    {/* currentPage */ 823, 2, 316},
-    {/* data */ 825, 2, 316},
-    {/* date */ 827, 4, 318},
-    {/* desc */ 831, 2, 316},
-    {/* encrypt */ 833, 5, 316},
-    {/* draw */ 838, 20, 317},
-    {/* encryption */ 858, 2, 316},
-    {/* meridiemNames */ 860, 0, 316},
-    {/* messaging */ 860, 2, 316},
-    {/* speak */ 862, 4, 320},
-    {/* dataGroup */ 866, 0, 316},
-    {/* common */ 866, 2, 316},
-    {/* #text */ 868, 1, 316},
-    {/* paginationOverride */ 869, 2, 316},
-    {/* reasons */ 871, 3, 316},
-    {/* signatureProperties */ 874, 2, 316},
-    {/* threshold */ 876, 2, 316},
-    {/* appearanceFilter */ 878, 4, 316},
-    {/* fill */ 882, 3, 316},
-    {/* font */ 885, 17, 316},
-    {/* form */ 902, 1, 319},
-    {/* mediumInfo */ 903, 2, 316},
-    {/* certificate */ 905, 2, 320},
-    {/* password */ 907, 2, 320},
-    {/* runScripts */ 909, 2, 316},
-    {/* trace */ 911, 2, 316},
-    {/* float */ 913, 4, 318},
-    {/* renderPolicy */ 917, 2, 316},
-    {/* logPseudoModel */ 919, 0, 312},
-    {/* destination */ 919, 2, 316},
-    {/* value */ 921, 4, 316},
-    {/* bookend */ 925, 4, 316},
-    {/* exObject */ 929, 6, 316},
-    {/* openAction */ 935, 2, 316},
-    {/* neverEmbed */ 937, 4, 316},
-    {/* bindItems */ 941, 3, 316},
-    {/* calculate */ 944, 3, 316},
-    {/* print */ 947, 2, 316},
-    {/* extras */ 949, 3, 316},
-    {/* proto */ 952, 0, 316},
-    {/* dSigData */ 952, 0, 316},
-    {/* creator */ 952, 2, 316},
-    {/* connect */ 954, 7, 316},
-    {/* permissions */ 961, 2, 316},
-    {/* connectionSet */ 963, 0, 319},
-    {/* submit */ 963, 7, 316},
-    {/* range */ 970, 2, 316},
-    {/* linearized */ 972, 2, 316},
-    {/* packet */ 974, 1, 316},
-    {/* rootElement */ 975, 2, 320},
-    {/* plaintextMetadata */ 977, 4, 316},
-    {/* numberSymbols */ 981, 0, 316},
-    {/* printHighQuality */ 981, 2, 316},
-    {/* driver */ 983, 2, 316},
-    {/* incrementalLoad */ 985, 4, 316},
-    {/* subjectDN */ 989, 1, 316},
-    {/* compressLogicalStructure */ 990, 2, 316},
-    {/* incrementalMerge */ 992, 2, 316},
-    {/* radial */ 994, 3, 316},
-    {/* variables */ 997, 2, 317},
-    {/* timePatterns */ 999, 0, 316},
-    {/* effectiveInputPolicy */ 999, 4, 316},
-    {/* nameAttr */ 1003, 4, 316},
-    {/* conformance */ 1007, 2, 316},
-    {/* transform */ 1009, 3, 316},
-    {/* lockDocument */ 1012, 4, 316},
-    {/* breakAfter */ 1016, 7, 316},
-    {/* line */ 1023, 4, 316},
-    {/* list */ 1027, 1, 313},
-    {/* source */ 1028, 3, 316},
-    {/* occur */ 1031, 5, 316},
-    {/* pickTrayByPDFSize */ 1036, 2, 316},
-    {/* monthNames */ 1038, 1, 316},
-    {/* severity */ 1039, 4, 316},
-    {/* groupParent */ 1043, 2, 316},
-    {/* documentAssembly */ 1045, 2, 316},
-    {/* numberSymbol */ 1047, 1, 316},
-    {/* tagged */ 1048, 2, 316},
-    {/*  */ 1050, 5, 316},
-    {/*  */ 1055, 1, -1},
-    {/*  */ 1056, 1, 312},
-    {/*  */ 1057, 0, 313},
-    {/*  */ 1057, 8, 312},
-    {/*  */ 1065, 6, 315},
-    {/*  */ 1071, 0, 316},
-    {/*  */ 1071, 0, 316},
-    {/*  */ 1071, 2, 316},
-    {/*  */ 1073, 2, 316},
-};
-const int32_t g_iScriptIndexCount =
-    sizeof(g_XFAScriptIndex) / sizeof(XFA_SCRIPTHIERARCHY);
-const XFA_SCRIPTATTRIBUTEINFO g_SomAttributeData[] = {
-    /* ps */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* to */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* ui */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Ui::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Ui::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* recordSet */
-    {0xb3543a6, L"max", (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::max,
-     XFA_Attribute::Max, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x45a6daf8, L"eofAction",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::eofAction,
-     XFA_Attribute::EofAction, XFA_ScriptType::Basic},
-    {0x5ec958c0, L"cursorType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::cursorType,
-     XFA_Attribute::CursorType, XFA_ScriptType::Basic},
-    {0x79975f2b, L"lockType", (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::lockType,
-     XFA_Attribute::LockType, XFA_ScriptType::Basic},
-    {0xa5340ff5, L"bofAction",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::bofAction,
-     XFA_Attribute::BofAction, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc5762157, L"cursorLocation",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_RecordSet::cursorLocation,
-     XFA_Attribute::CursorLocation, XFA_ScriptType::Basic},
-
-    /* subsetBelow */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* subformSet */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_SubformSet::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1ee2d24d, L"instanceIndex",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_SubformSet::instanceIndex,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x8c99377e, L"relation", (XFA_ATTRIBUTE_CALLBACK)&CJX_SubformSet::relation,
-     XFA_Attribute::Relation, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_SubformSet::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_SubformSet::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* adobeExtensionLevel */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* typeface */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-
-    /* break */
-    {0x3106c3a, L"beforeTarget",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::beforeTarget,
-     XFA_Attribute::BeforeTarget, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x13a08bdb, L"overflowTarget",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::overflowTarget,
-     XFA_Attribute::OverflowTarget, XFA_ScriptType::Basic},
-    {0x169134a1, L"overflowLeader",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::overflowLeader,
-     XFA_Attribute::OverflowLeader, XFA_ScriptType::Basic},
-    {0x20914367, L"overflowTrailer",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::overflowTrailer,
-     XFA_Attribute::OverflowTrailer, XFA_ScriptType::Basic},
-    {0x453eaf38, L"startNew", (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::startNew,
-     XFA_Attribute::StartNew, XFA_ScriptType::Basic},
-    {0x64110ab5, L"bookendTrailer",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::bookendTrailer,
-     XFA_Attribute::BookendTrailer, XFA_ScriptType::Basic},
-    {0xb6b44172, L"after", (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::after,
-     XFA_Attribute::After, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc3c1442f, L"bookendLeader",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::bookendLeader,
-     XFA_Attribute::BookendLeader, XFA_ScriptType::Basic},
-    {0xcb150479, L"afterTarget",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::afterTarget,
-     XFA_Attribute::AfterTarget, XFA_ScriptType::Basic},
-    {0xf4ffce73, L"before", (XFA_ATTRIBUTE_CALLBACK)&CJX_Break::before,
-     XFA_Attribute::Before, XFA_ScriptType::Basic},
-
-    /* fontInfo */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* numberPattern */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-
-    /* dynamicRender */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DefaultValue_Read,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* printScaling */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* checkButton */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_CheckButton::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x47cfa43a, L"allowNeutral",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_CheckButton::allowNeutral,
-     XFA_Attribute::AllowNeutral, XFA_ScriptType::Basic},
-    {0x7c2fd80b, L"mark", (XFA_ATTRIBUTE_CALLBACK)&CJX_CheckButton::mark,
-     XFA_Attribute::Mark, XFA_ScriptType::Basic},
-    {0x8ed182d1, L"shape", (XFA_ATTRIBUTE_CALLBACK)&CJX_CheckButton::shape,
-     XFA_Attribute::Shape, XFA_ScriptType::Basic},
-    {0xa686975b, L"size", (XFA_ATTRIBUTE_CALLBACK)&CJX_CheckButton::size,
-     XFA_Attribute::Size, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_CheckButton::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* datePatterns */
-
-    /* sourceSet */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_SourceSet::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_SourceSet::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* amd */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* arc */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Arc::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x5c054755, L"startAngle", (XFA_ATTRIBUTE_CALLBACK)&CJX_Arc::startAngle,
-     XFA_Attribute::StartAngle, XFA_ScriptType::Basic},
-    {0x74788f8b, L"sweepAngle", (XFA_ATTRIBUTE_CALLBACK)&CJX_Arc::sweepAngle,
-     XFA_Attribute::SweepAngle, XFA_ScriptType::Basic},
-    {0x9d833d75, L"circular", (XFA_ATTRIBUTE_CALLBACK)&CJX_Arc::circular,
-     XFA_Attribute::Circular, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Arc::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd996fa9b, L"hand", (XFA_ATTRIBUTE_CALLBACK)&CJX_Arc::hand,
-     XFA_Attribute::Hand, XFA_ScriptType::Basic},
-
-    /* day */
-
-    /* era */
-
-    /* jog */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* log */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* map */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Map::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xb0e5485d, L"bind", (XFA_ATTRIBUTE_CALLBACK)&CJX_Map::bind,
-     XFA_Attribute::Bind, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Map::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xcd7f7b54, L"from", (XFA_ATTRIBUTE_CALLBACK)&CJX_Map::from,
-     XFA_Attribute::From, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* mdp */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Mdp::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x8e29d794, L"signatureType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Mdp::signatureType,
-     XFA_Attribute::SignatureType, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Mdp::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe11a2cbc, L"permissions", (XFA_ATTRIBUTE_CALLBACK)&CJX_Mdp::permissions,
-     XFA_Attribute::Permissions, XFA_ScriptType::Basic},
-
-    /* breakBefore */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakBefore::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x453eaf38, L"startNew",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakBefore::startNew,
-     XFA_Attribute::StartNew, XFA_ScriptType::Basic},
-    {0x9dcc3ab3, L"trailer", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakBefore::trailer,
-     XFA_Attribute::Trailer, XFA_ScriptType::Basic},
-    {0xa6118c89, L"targetType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakBefore::targetType,
-     XFA_Attribute::TargetType, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakBefore::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakBefore::target,
-     XFA_Attribute::Target, XFA_ScriptType::Basic},
-    {0xcbcaf66d, L"leader", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakBefore::leader,
-     XFA_Attribute::Leader, XFA_ScriptType::Basic},
-
-    /* oid */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Oid::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Oid::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* pcl */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* pdf */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* ref */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Ref::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Ref::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* uri */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Uri::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Uri::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* xdc */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* xdp */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* xfa */
-    {0x2d574d58, L"this", (XFA_ATTRIBUTE_CALLBACK)&CJX_Xfa::thisValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0x4fdc3454, L"timeStamp", (XFA_ATTRIBUTE_CALLBACK)&CJX_Xfa::timeStamp,
-     XFA_Attribute::TimeStamp, XFA_ScriptType::Basic},
-    {0xb598a1f7, L"uuid", (XFA_ATTRIBUTE_CALLBACK)&CJX_Xfa::uuid,
-     XFA_Attribute::Uuid, XFA_ScriptType::Basic},
-
-    /* xsl */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* zpl */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* cache */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* margin */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Margin::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xcfea02e, L"leftInset", (XFA_ATTRIBUTE_CALLBACK)&CJX_Margin::leftInset,
-     XFA_Attribute::LeftInset, XFA_ScriptType::Basic},
-    {0x1356caf8, L"bottomInset",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Margin::bottomInset,
-     XFA_Attribute::BottomInset, XFA_ScriptType::Basic},
-    {0x25764436, L"topInset", (XFA_ATTRIBUTE_CALLBACK)&CJX_Margin::topInset,
-     XFA_Attribute::TopInset, XFA_ScriptType::Basic},
-    {0x8a692521, L"rightInset", (XFA_ATTRIBUTE_CALLBACK)&CJX_Margin::rightInset,
-     XFA_Attribute::RightInset, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Margin::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* keyUsage */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1e459b8f, L"nonRepudiation",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::nonRepudiation,
-     XFA_Attribute::NonRepudiation, XFA_ScriptType::Basic},
-    {0x2bb3f470, L"encipherOnly",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::encipherOnly,
-     XFA_Attribute::EncipherOnly, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0x5f760b50, L"digitalSignature",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::digitalSignature,
-     XFA_Attribute::DigitalSignature, XFA_ScriptType::Basic},
-    {0x69aa2292, L"crlSign", (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::crlSign,
-     XFA_Attribute::CrlSign, XFA_ScriptType::Basic},
-    {0x98fd4d81, L"keyAgreement",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::keyAgreement,
-     XFA_Attribute::KeyAgreement, XFA_ScriptType::Basic},
-    {0xa66404cb, L"keyEncipherment",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::keyEncipherment,
-     XFA_Attribute::KeyEncipherment, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xca5dc27c, L"dataEncipherment",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::dataEncipherment,
-     XFA_Attribute::DataEncipherment, XFA_ScriptType::Basic},
-    {0xe8f118a8, L"keyCertSign",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::keyCertSign,
-     XFA_Attribute::KeyCertSign, XFA_ScriptType::Basic},
-    {0xfea53ec6, L"decipherOnly",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_KeyUsage::decipherOnly,
-     XFA_Attribute::DecipherOnly, XFA_ScriptType::Basic},
-
-    /* exclude */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* choiceList */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ChoiceList::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x8b90e1f2, L"open", (XFA_ATTRIBUTE_CALLBACK)&CJX_ChoiceList::open,
-     XFA_Attribute::Open, XFA_ScriptType::Basic},
-    {0x957fa006, L"commitOn", (XFA_ATTRIBUTE_CALLBACK)&CJX_ChoiceList::commitOn,
-     XFA_Attribute::CommitOn, XFA_ScriptType::Basic},
-    {0xb12128b7, L"textEntry",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ChoiceList::textEntry,
-     XFA_Attribute::TextEntry, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_ChoiceList::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* level */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* labelPrinter */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* calendarSymbols */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-
-    /* para */
-    {0x2282c73, L"hAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::hAlign,
-     XFA_Attribute::HAlign, XFA_ScriptType::Basic},
-    {0x8d4f1c7, L"textIndent", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::textIndent,
-     XFA_Attribute::TextIndent, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2a82d99c, L"marginRight", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::marginRight,
-     XFA_Attribute::MarginRight, XFA_ScriptType::Basic},
-    {0x534729c9, L"marginLeft", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::marginLeft,
-     XFA_Attribute::MarginLeft, XFA_ScriptType::Basic},
-    {0x5739d1ff, L"radixOffset", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::radixOffset,
-     XFA_Attribute::RadixOffset, XFA_ScriptType::Basic},
-    {0x577682ac, L"preserve", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::preserve,
-     XFA_Attribute::Preserve, XFA_ScriptType::Basic},
-    {0x731e0665, L"spaceBelow", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::spaceBelow,
-     XFA_Attribute::SpaceBelow, XFA_ScriptType::Basic},
-    {0x7a7cc341, L"vAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::vAlign,
-     XFA_Attribute::VAlign, XFA_ScriptType::Basic},
-    {0x836d4d7c, L"tabDefault", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::tabDefault,
-     XFA_Attribute::TabDefault, XFA_ScriptType::Basic},
-    {0x8fa01790, L"tabStops", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::tabStops,
-     XFA_Attribute::TabStops, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd4b01921, L"lineHeight", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::lineHeight,
-     XFA_Attribute::LineHeight, XFA_ScriptType::Basic},
-    {0xe18b5659, L"spaceAbove", (XFA_ATTRIBUTE_CALLBACK)&CJX_Para::spaceAbove,
-     XFA_Attribute::SpaceAbove, XFA_ScriptType::Basic},
-
-    /* part */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* pdfa */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* filter */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Filter::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Filter::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd861f8af, L"addRevocationInfo",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Filter::addRevocationInfo,
-     XFA_Attribute::AddRevocationInfo, XFA_ScriptType::Basic},
-
-    /* present */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* pagination */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* encoding */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encoding::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encoding::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* event */
-    {0xbb8df5d, L"ref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Event::ref,
-     XFA_Attribute::Ref, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Event::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Event::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6128d8d, L"activity", (XFA_ATTRIBUTE_CALLBACK)&CJX_Event::activity,
-     XFA_Attribute::Activity, XFA_ScriptType::Basic},
-
-    /* whitespace */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* defaultUi */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_DefaultUi::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_DefaultUi::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* dataModel */
-
-    /* barcode */
-    {0x43e349b, L"dataRowCount",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::dataRowCount,
-     XFA_Attribute::DataRowCount, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x28e17e91, L"dataPrep", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::dataPrep,
-     XFA_Attribute::DataPrep, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0x3650557e, L"textLocation",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::textLocation,
-     XFA_Attribute::TextLocation, XFA_ScriptType::Basic},
-    {0x3b582286, L"moduleWidth",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::moduleWidth,
-     XFA_Attribute::ModuleWidth, XFA_ScriptType::Basic},
-    {0x52666f1c, L"printCheckDigit",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::printCheckDigit,
-     XFA_Attribute::PrintCheckDigit, XFA_ScriptType::Basic},
-    {0x5404d6df, L"moduleHeight",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::moduleHeight,
-     XFA_Attribute::ModuleHeight, XFA_ScriptType::Basic},
-    {0x5ab23b6c, L"startChar", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::startChar,
-     XFA_Attribute::StartChar, XFA_ScriptType::Basic},
-    {0x7c732a66, L"truncate", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::truncate,
-     XFA_Attribute::Truncate, XFA_ScriptType::Basic},
-    {0x8d181d61, L"wideNarrowRatio",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::wideNarrowRatio,
-     XFA_Attribute::WideNarrowRatio, XFA_ScriptType::Basic},
-    {0x99800d7a, L"errorCorrectionLevel",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::errorCorrectionLevel,
-     XFA_Attribute::ErrorCorrectionLevel, XFA_ScriptType::Basic},
-    {0x9a63da3d, L"upsMode", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::upsMode,
-     XFA_Attribute::UpsMode, XFA_ScriptType::Basic},
-    {0xaf754613, L"checksum", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::checksum,
-     XFA_Attribute::Checksum, XFA_ScriptType::Basic},
-    {0xb045fbc5, L"charEncoding",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::charEncoding,
-     XFA_Attribute::CharEncoding, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc035c6b1, L"dataColumnCount",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::dataColumnCount,
-     XFA_Attribute::DataColumnCount, XFA_ScriptType::Basic},
-    {0xd3c84d25, L"rowColumnRatio",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::rowColumnRatio,
-     XFA_Attribute::RowColumnRatio, XFA_ScriptType::Basic},
-    {0xd57c513c, L"dataLength",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::dataLength,
-     XFA_Attribute::DataLength, XFA_ScriptType::Basic},
-    {0xf575ca75, L"endChar", (XFA_ATTRIBUTE_CALLBACK)&CJX_Barcode::endChar,
-     XFA_Attribute::EndChar, XFA_ScriptType::Basic},
-
-    /* timePattern */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-
-    /* batchOutput */
-    {0x28dee6e9, L"format",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Format, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* enforce */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* currencySymbols */
-
-    /* addSilentPrint */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* rename */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* operation */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Operation::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x60d4c8b1, L"output", (XFA_ATTRIBUTE_CALLBACK)&CJX_Operation::output,
-     XFA_Attribute::Output, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Operation::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6a39990, L"input", (XFA_ATTRIBUTE_CALLBACK)&CJX_Operation::input,
-     XFA_Attribute::Input, XFA_ScriptType::Basic},
-
-    /* typefaces */
-
-    /* subjectDNs */
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_SubjectDNs::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-
-    /* issuers */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Issuers::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Issuers::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Issuers::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* signaturePseudoModel */
-
-    /* wsdlConnection */
-    {0x2b5df51e, L"dataDescription",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_WsdlConnection::dataDescription,
-     XFA_Attribute::DataDescription, XFA_ScriptType::Basic},
-
-    /* debug */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* delta */
-    {0x6c0d9600, L"currentValue",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Delta::currentValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0x942643f0, L"savedValue", (XFA_ATTRIBUTE_CALLBACK)&CJX_Delta::savedValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target", (XFA_ATTRIBUTE_CALLBACK)&CJX_Delta::target,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* eraNames */
-
-    /* modifyAnnots */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* startNode */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* button */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Button::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Button::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd4cc53f8, L"highlight", (XFA_ATTRIBUTE_CALLBACK)&CJX_Button::highlight,
-     XFA_Attribute::Highlight, XFA_ScriptType::Basic},
-
-    /* format */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Format::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Format::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* border */
-    {0x5518c25, L"break", (XFA_ATTRIBUTE_CALLBACK)&CJX_Border::breakValue,
-     XFA_Attribute::Break, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Border::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Border::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_Border::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Border::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd996fa9b, L"hand", (XFA_ATTRIBUTE_CALLBACK)&CJX_Border::hand,
-     XFA_Attribute::Hand, XFA_ScriptType::Basic},
-
-    /* area */
-    {0x78, L"x", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::x, XFA_Attribute::X,
-     XFA_ScriptType::Basic},
-    {0x79, L"y", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::y, XFA_Attribute::Y,
-     XFA_ScriptType::Basic},
-    {0x21aed, L"id", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::id, XFA_Attribute::Id,
-     XFA_ScriptType::Basic},
-    {0x31b19c1, L"name", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::name,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1059ec18, L"level",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_Integer,
-     XFA_Attribute::Level, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xac06e2b0, L"colSpan", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::colSpan,
-     XFA_Attribute::ColSpan, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Area::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* hyphenation */
-    {0x21aed, L"id",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Id, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f105f72, L"wordCharacterCount",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::WordCharacterCount, XFA_ScriptType::Basic},
-    {0x3d123c26, L"hyphenate",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Hyphenate, XFA_ScriptType::Basic},
-    {0x66539c48, L"excludeInitialCap",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::ExcludeInitialCap, XFA_ScriptType::Basic},
-    {0x6a95c976, L"pushCharacterCount",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::PushCharacterCount, XFA_ScriptType::Basic},
-    {0x982bd892, L"remainCharacterCount",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::RemainCharacterCount, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe5c96d6a, L"excludeAllCaps",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::ExcludeAllCaps, XFA_ScriptType::Basic},
-
-    /* text */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Text::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x8af2e657, L"maxChars", (XFA_ATTRIBUTE_CALLBACK)&CJX_Text::maxChars,
-     XFA_Attribute::MaxChars, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}", (XFA_ATTRIBUTE_CALLBACK)&CJX_Text::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Text::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Text::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* time */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Time::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}", (XFA_ATTRIBUTE_CALLBACK)&CJX_Time::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Time::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Time::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* type */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* overprint */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* certificates */
-    {0xc080cd3, L"url", (XFA_ATTRIBUTE_CALLBACK)&CJX_Certificates::url,
-     XFA_Attribute::Url, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Certificates::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa6710262, L"credentialServerPolicy",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Certificates::credentialServerPolicy,
-     XFA_Attribute::CredentialServerPolicy, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Certificates::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc2ba0923, L"urlPolicy",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Certificates::urlPolicy,
-     XFA_Attribute::UrlPolicy, XFA_ScriptType::Basic},
-
-    /* encryptionMethods */
-    {0xc0811ed, L"use",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* setProperty */
-    {0x47d03490, L"connection",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_SetProperty::connection,
-     XFA_Attribute::Connection, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target", (XFA_ATTRIBUTE_CALLBACK)&CJX_SetProperty::target,
-     XFA_Attribute::Target, XFA_ScriptType::Basic},
-
-    /* printerName */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* startPage */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* pageOffset */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* dateTime */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_DateTime::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DateTime::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_DateTime::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_DateTime::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* comb */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Comb::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x78bff531, L"numberOfCells",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Comb::numberOfCells,
-     XFA_Attribute::NumberOfCells, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Comb::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* pattern */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Pattern::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Pattern::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Pattern::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* ifEmpty */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* suppressBanner */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* outputBin */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* field */
-    {0x68, L"h", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::h, XFA_Attribute::H,
-     XFA_ScriptType::Basic},
-    {0x77, L"w", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::w, XFA_Attribute::W,
-     XFA_ScriptType::Basic},
-    {0x78, L"x", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::x, XFA_Attribute::X,
-     XFA_ScriptType::Basic},
-    {0x79, L"y", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::y, XFA_Attribute::Y,
-     XFA_ScriptType::Basic},
-    {0x2282c73, L"hAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::hAlign,
-     XFA_Attribute::HAlign, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1abbd7e0, L"dataNode",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DataNode,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0x25839852, L"access", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::access,
-     XFA_Attribute::Access, XFA_ScriptType::Basic},
-    {0x2ee7678f, L"rotate", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::rotate,
-     XFA_Attribute::Rotate, XFA_ScriptType::Basic},
-    {0x3b1ddd06, L"fillColor", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::fillColor,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x54c399e3, L"formattedValue",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::formattedValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0x5a3b375d, L"borderColor",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::borderColor, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0x5e936ed6, L"fontColor", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::fontColor,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x6826c408, L"parentSubform",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::parentSubform, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0x79b67434, L"mandatoryMessage",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::mandatoryMessage,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x7a7cc341, L"vAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::vAlign,
-     XFA_Attribute::VAlign, XFA_ScriptType::Basic},
-    {0x7c2ff6ae, L"maxH", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::maxH,
-     XFA_Attribute::MaxH, XFA_ScriptType::Basic},
-    {0x7c2ff6bd, L"maxW", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::maxW,
-     XFA_Attribute::MaxW, XFA_ScriptType::Basic},
-    {0x7d02356c, L"minH", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::minH,
-     XFA_Attribute::MinH, XFA_ScriptType::Basic},
-    {0x7d02357b, L"minW", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::minW,
-     XFA_Attribute::MinW, XFA_ScriptType::Basic},
-    {0x85fd6faf, L"mandatory", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::mandatory,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0x964fb42e, L"formatMessage",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::formatMessage, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xa03cf627, L"rawValue", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::rawValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xa60dd202, L"length",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Field_Length,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xac06e2b0, L"colSpan", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::colSpan,
-     XFA_Attribute::ColSpan, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbc8fa350, L"locale", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::locale,
-     XFA_Attribute::Locale, XFA_ScriptType::Basic},
-    {0xc2bd40fd, L"anchorType", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::anchorType,
-     XFA_Attribute::AnchorType, XFA_ScriptType::Basic},
-    {0xc4fed09b, L"accessKey", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::accessKey,
-     XFA_Attribute::AccessKey, XFA_ScriptType::Basic},
-    {0xcabfa3d0, L"validationMessage",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::validationMessage,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xdcecd663, L"editValue", (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::editValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xe07e5061, L"selectedIndex",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::selectedIndex, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xf65e34be, L"borderWidth",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Field::borderWidth, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-
-    /* agent */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* outputXSL */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* adjustData */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* autoSave */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* contentArea */
-    {0x68, L"h", (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::H, XFA_ScriptType::Basic},
-    {0x77, L"w", (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::W, XFA_ScriptType::Basic},
-    {0x78, L"x", (XFA_ATTRIBUTE_CALLBACK)&CJX_ContentArea::x, XFA_Attribute::X,
-     XFA_ScriptType::Basic},
-    {0x79, L"y", (XFA_ATTRIBUTE_CALLBACK)&CJX_ContentArea::y, XFA_Attribute::Y,
-     XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ContentArea::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ContentArea::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_ContentArea::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* eventPseudoModel */
-    {0xd843798, L"fullText",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::fullText,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x1b6d1cf5, L"reenter",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::reenter,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x1e6ffa9a, L"prevContentType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::prevContentType,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x25a3c206, L"soapFaultString",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::soapFaultString,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x2e00c007, L"newContentType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::newContentType,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x4570500f, L"modifier",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::modifier,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x50e2e33b, L"selEnd",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::selEnd,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x57de87c2, L"prevText",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::prevText,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x6ea04e0a, L"soapFaultCode",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::soapFaultCode,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x6f6556cf, L"newText",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::newText,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x891f4606, L"change",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::change,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x8fa3c19e, L"shift", (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::shift,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xa9d9b2e1, L"keyDown",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::keyDown,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbfc89db2, L"selStart",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::selStart,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xc32a5812, L"commitKey",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::commitKey,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_EventPseudoModel::target,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* wsdlAddress */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_WsdlAddress::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_WsdlAddress::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* solid */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Solid::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Solid::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* dateTimeSymbols */
-
-    /* encryptionLevel */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* edge */
-    {0xa2e3514, L"cap", (XFA_ATTRIBUTE_CALLBACK)&CJX_Edge::cap,
-     XFA_Attribute::Cap, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Edge::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x5392ea58, L"stroke", (XFA_ATTRIBUTE_CALLBACK)&CJX_Edge::stroke,
-     XFA_Attribute::Stroke, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Edge::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0x94446dcc, L"thickness", (XFA_ATTRIBUTE_CALLBACK)&CJX_Edge::thickness,
-     XFA_Attribute::Thickness, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Edge::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* stipple */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Stipple::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1ec8ab2c, L"rate", (XFA_ATTRIBUTE_CALLBACK)&CJX_Stipple::rate,
-     XFA_Attribute::Rate, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Stipple::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* attributes */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* versionControl */
-    {0x7b29630a, L"sourceBelow",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::SourceBelow, XFA_ScriptType::Basic},
-    {0x8fc36c0a, L"outputBelow",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::OutputBelow, XFA_ScriptType::Basic},
-    {0xe996b2fe, L"sourceAbove",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::SourceAbove, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* meridiem */
-
-    /* exclGroup */
-    {0x68, L"h", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::h, XFA_Attribute::H,
-     XFA_ScriptType::Basic},
-    {0x77, L"w", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::w, XFA_Attribute::W,
-     XFA_ScriptType::Basic},
-    {0x78, L"x", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::x, XFA_Attribute::X,
-     XFA_ScriptType::Basic},
-    {0x79, L"y", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::y, XFA_Attribute::Y,
-     XFA_ScriptType::Basic},
-    {0x2282c73, L"hAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::hAlign,
-     XFA_Attribute::HAlign, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xf23332f, L"errorText",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_ExclGroup_ErrorText,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x1abbd7e0, L"dataNode",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DataNode,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0x25839852, L"access", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::access,
-     XFA_Attribute::Access, XFA_ScriptType::Basic},
-    {0x3b1ddd06, L"fillColor",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::fillColor, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0x5a3b375d, L"borderColor",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::borderColor,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x79b67434, L"mandatoryMessage",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::mandatoryMessage,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x7a7cc341, L"vAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::vAlign,
-     XFA_Attribute::VAlign, XFA_ScriptType::Basic},
-    {0x7c2ff6ae, L"maxH", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::maxH,
-     XFA_Attribute::MaxH, XFA_ScriptType::Basic},
-    {0x7c2ff6bd, L"maxW", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::maxW,
-     XFA_Attribute::MaxW, XFA_ScriptType::Basic},
-    {0x7d02356c, L"minH", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::minH,
-     XFA_Attribute::MinH, XFA_ScriptType::Basic},
-    {0x7d02357b, L"minW", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::minW,
-     XFA_Attribute::MinW, XFA_ScriptType::Basic},
-    {0x7e7e845e, L"layout", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::layout,
-     XFA_Attribute::Layout, XFA_ScriptType::Basic},
-    {0x846599f8, L"transient",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::transient, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0x85fd6faf, L"mandatory",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::mandatory, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xa03cf627, L"rawValue", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::rawValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xac06e2b0, L"colSpan", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::colSpan,
-     XFA_Attribute::ColSpan, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc2bd40fd, L"anchorType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::anchorType,
-     XFA_Attribute::AnchorType, XFA_ScriptType::Basic},
-    {0xc4fed09b, L"accessKey",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::accessKey,
-     XFA_Attribute::AccessKey, XFA_ScriptType::Basic},
-    {0xcabfa3d0, L"validationMessage",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::validationMessage,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xf65e34be, L"borderWidth",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExclGroup::borderWidth,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* toolTip */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ToolTip::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_ToolTip::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* compress */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xeda9017a, L"scope",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Scope, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* reason */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Reason::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Reason::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* execute */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Execute::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x47d03490, L"connection",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Execute::connection,
-     XFA_Attribute::Connection, XFA_ScriptType::Basic},
-    {0x6cfa828a, L"runAt", (XFA_ATTRIBUTE_CALLBACK)&CJX_Execute::runAt,
-     XFA_Attribute::RunAt, XFA_ScriptType::Basic},
-    {0xa1b0d2f5, L"executeType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Execute::executeType,
-     XFA_Attribute::ExecuteType, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Execute::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* contentCopy */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* dateTimeEdit */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_DateTimeEdit::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_DateTimeEdit::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe6f99487, L"hScrollPolicy",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DateTimeEdit::hScrollPolicy,
-     XFA_Attribute::HScrollPolicy, XFA_ScriptType::Basic},
-
-    /* config */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* image */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x42fed1fd, L"contentType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::contentType,
-     XFA_Attribute::ContentType, XFA_ScriptType::Basic},
-    {0x54fa722c, L"transferEncoding",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::transferEncoding,
-     XFA_Attribute::TransferEncoding, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}", (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd171b240, L"aspect", (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::aspect,
-     XFA_Attribute::Aspect, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xdb55fec5, L"href", (XFA_ATTRIBUTE_CALLBACK)&CJX_Image::href,
-     XFA_Attribute::Href, XFA_ScriptType::Basic},
-
-    /* #xHTML */
-    {0xd6e27f1d, L"value",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Value, XFA_ScriptType::Basic},
-
-    /* numberOfCopies */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* behaviorOverride */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* timeStamp */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_TimeStamp::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_TimeStamp::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0x7f6fd3d7, L"server", (XFA_ATTRIBUTE_CALLBACK)&CJX_TimeStamp::server,
-     XFA_Attribute::Server, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_TimeStamp::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* month */
-
-    /* viewerPreferences */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* scriptModel */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* decimal */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Decimal::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x4b8bc840, L"fracDigits",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Decimal::fracDigits,
-     XFA_Attribute::FracDigits, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Decimal::defaultValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Decimal::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Decimal::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xde7f92ba, L"leadDigits",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Decimal::leadDigits,
-     XFA_Attribute::LeadDigits, XFA_ScriptType::Basic},
-
-    /* subform */
-    {0x68, L"h", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::h, XFA_Attribute::H,
-     XFA_ScriptType::Basic},
-    {0x77, L"w", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::w, XFA_Attribute::W,
-     XFA_ScriptType::Basic},
-    {0x78, L"x", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::x, XFA_Attribute::X,
-     XFA_ScriptType::Basic},
-    {0x79, L"y", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::y, XFA_Attribute::Y,
-     XFA_ScriptType::Basic},
-    {0x2282c73, L"hAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::hAlign,
-     XFA_Attribute::HAlign, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1414d431, L"allowMacro",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::allowMacro,
-     XFA_Attribute::AllowMacro, XFA_ScriptType::Basic},
-    {0x1517dfa1, L"columnWidths",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::columnWidths,
-     XFA_Attribute::ColumnWidths, XFA_ScriptType::Basic},
-    {0x1abbd7e0, L"dataNode",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DataNode,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0x1ee2d24d, L"instanceIndex",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::instanceIndex,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x25839852, L"access",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-
-     XFA_Attribute::Access, XFA_ScriptType::Basic},
-    {0x3b1ddd06, L"fillColor",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_FillColor,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0x5a3b375d, L"borderColor",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_BorderColor,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x7a7cc341, L"vAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::vAlign,
-     XFA_Attribute::VAlign, XFA_ScriptType::Basic},
-    {0x7c2ff6ae, L"maxH", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::maxH,
-     XFA_Attribute::MaxH, XFA_ScriptType::Basic},
-    {0x7c2ff6bd, L"maxW", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::maxW,
-     XFA_Attribute::MaxW, XFA_ScriptType::Basic},
-    {0x7d02356c, L"minH", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::minH,
-     XFA_Attribute::MinH, XFA_ScriptType::Basic},
-    {0x7d02357b, L"minW", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::minW,
-     XFA_Attribute::MinW, XFA_ScriptType::Basic},
-    {0x7e7e845e, L"layout", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::layout,
-     XFA_Attribute::Layout, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0x9cc17d75, L"mergeMode",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-
-     XFA_Attribute::MergeMode, XFA_ScriptType::Basic},
-    {0x9f3e9510, L"instanceManager",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Subform_InstanceManager,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0xac06e2b0, L"colSpan", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::colSpan,
-     XFA_Attribute::ColSpan, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbc8fa350, L"locale", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::locale,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xc2bd40fd, L"anchorType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::anchorType,
-     XFA_Attribute::AnchorType, XFA_ScriptType::Basic},
-    {0xcabfa3d0, L"validationMessage",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::validationMessage,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xe4c3a5e5, L"restoreState",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::restoreState,
-     XFA_Attribute::RestoreState, XFA_ScriptType::Basic},
-    {0xeda9017a, L"scope", (XFA_ATTRIBUTE_CALLBACK)&CJX_Subform::scope,
-     XFA_Attribute::Scope, XFA_ScriptType::Basic},
-    {0xf65e34be, L"borderWidth",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_BorderWidth,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* select */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Select::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Select::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* window */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* localeSet */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* handler */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Handler::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Handler::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0x5a50e9e6, L"version", (XFA_ATTRIBUTE_CALLBACK)&CJX_Handler::version,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Handler::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* hostPseudoModel */
-    {0x31b19c1, L"name", (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::name,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x66c1ae9, L"validationsEnabled",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::validationsEnabled,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x14d04502, L"title", (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::title,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x392ae445, L"platform",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::platform,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x5a50e9e6, L"version",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::version,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x66cb1eed, L"variation",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::variation,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x7717cbc4, L"language",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::language,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x86698963, L"appType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::appType,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x94ff9e8d, L"calculationsEnabled",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::calculationsEnabled,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbcd44940, L"currentPage",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::currentPage,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xd592b920, L"numPages",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_HostPseudoModel::numPages,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* presence */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* record */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* embed */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* version */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* command */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Command::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x24d85167, L"timeout", (XFA_ATTRIBUTE_CALLBACK)&CJX_Command::timeout,
-     XFA_Attribute::Timeout, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Command::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* copies */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* staple */
-    {0x7d9fd7c5, L"mode",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Mode, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* submitFormat */
-    {0x7d9fd7c5, L"mode",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_SubmitFormat_Mode,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* boolean */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Boolean::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Boolean::defaultValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Boolean::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Boolean::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* message */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Message::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Message::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* output */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* psMap */
-
-    /* excludeNS */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* assist */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Assist::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2038c9b2, L"role", (XFA_ATTRIBUTE_CALLBACK)&CJX_Assist::role,
-     XFA_Attribute::Role, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Assist::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* picture */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Picture::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Picture::defaultValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Picture::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Picture::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* traversal */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Traversal::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Traversal::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* silentPrint */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* webClient */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* layoutPseudoModel */
-    {0xfcef86b5, L"ready",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_LayoutPseudoModel::ready,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* producer */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* corner */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x5392ea58, L"stroke", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::stroke,
-     XFA_Attribute::Stroke, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0x7b95e661, L"inverted", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::inverted,
-     XFA_Attribute::Inverted, XFA_ScriptType::Basic},
-    {0x94446dcc, L"thickness", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::thickness,
-     XFA_Attribute::Thickness, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe8dddf50, L"join", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::join,
-     XFA_Attribute::Join, XFA_ScriptType::Basic},
-    {0xe948b9a8, L"radius", (XFA_ATTRIBUTE_CALLBACK)&CJX_Corner::radius,
-     XFA_Attribute::Radius, XFA_ScriptType::Basic},
-
-    /* msgId */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* color */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Color::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xabfa6c4f, L"cSpace", (XFA_ATTRIBUTE_CALLBACK)&CJX_Color::cSpace,
-     XFA_Attribute::CSpace, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Color::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Color::value,
-     XFA_Attribute::Value, XFA_ScriptType::Basic},
-
-    /* keep */
-    {0x3848b3f, L"next", (XFA_ATTRIBUTE_CALLBACK)&CJX_Keep::next,
-     XFA_Attribute::Next, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Keep::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x6a3405dd, L"previous", (XFA_ATTRIBUTE_CALLBACK)&CJX_Keep::previous,
-     XFA_Attribute::Previous, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Keep::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xf6b59543, L"intact", (XFA_ATTRIBUTE_CALLBACK)&CJX_Keep::intact,
-     XFA_Attribute::Intact, XFA_ScriptType::Basic},
-
-    /* query */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Query::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x268b7ec1, L"commandType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Query::commandType,
-     XFA_Attribute::CommandType, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Query::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* insert */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Insert::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Insert::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* imageEdit */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ImageEdit::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_ImageEdit::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbde9abda, L"data", (XFA_ATTRIBUTE_CALLBACK)&CJX_ImageEdit::data,
-     XFA_Attribute::Data, XFA_ScriptType::Basic},
-
-    /* validate */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Validate::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x5b707a35, L"scriptTest",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Validate::scriptTest,
-     XFA_Attribute::ScriptTest, XFA_ScriptType::Basic},
-    {0x6b6ddcfb, L"nullTest", (XFA_ATTRIBUTE_CALLBACK)&CJX_Validate::nullTest,
-     XFA_Attribute::NullTest, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Validate::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xe64b1129, L"formatTest",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Validate::formatTest,
-     XFA_Attribute::FormatTest, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* digestMethods */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_DigestMethods::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_DigestMethods::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DigestMethods::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* numberPatterns */
-
-    /* pageSet */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageSet::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x8c99377e, L"relation", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageSet::relation,
-     XFA_Attribute::Relation, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageSet::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageSet::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* integer */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Integer::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Integer::defaultValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Integer::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Integer::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* soapAddress */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_SoapAddress::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_SoapAddress::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* equate */
-    {0x25363, L"to",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::To, XFA_ScriptType::Basic},
-    {0x66642f8f, L"force",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Force, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xcd7f7b54, L"from",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::From, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* formFieldFilling */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* pageRange */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* update */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Update::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Update::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* connectString */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ConnectString::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ConnectString::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* mode */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* layout */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* #xml */
-    {0xd6e27f1d, L"value",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Value, XFA_ScriptType::Basic},
-
-    /* xsdConnection */
-    {0x2b5df51e, L"dataDescription",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_XsdConnection::dataDescription,
-     XFA_Attribute::DataDescription, XFA_ScriptType::Basic},
-
-    /* traverse */
-    {0xbb8df5d, L"ref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Traverse::ref,
-     XFA_Attribute::Ref, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Traverse::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x226ca8f1, L"operation", (XFA_ATTRIBUTE_CALLBACK)&CJX_Traverse::operation,
-     XFA_Attribute::Operation, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Traverse::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* encodings */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encodings::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encodings::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encodings::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* template */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* acrobat */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* validationMessaging */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* signing */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Signing::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Signing::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Signing::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* dataWindow */
-    {0xfb67185, L"recordsBefore",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DataWindow::recordsBefore,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x21d5dfcb, L"currentRecordNumber",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DataWindow::currentRecordNumber,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x312af044, L"recordsAfter",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DataWindow::recordsAfter,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x6aab37cb, L"isDefined",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DataWindow::isDefined, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-
-    /* script */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x42fed1fd, L"contentType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::contentType,
-     XFA_Attribute::ContentType, XFA_ScriptType::Basic},
-    {0x6cfa828a, L"runAt", (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::runAt,
-     XFA_Attribute::RunAt, XFA_ScriptType::Basic},
-    {0xa021b738, L"stateless", (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::stateless,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::defaultValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xadc4c77b, L"binding", (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::binding,
-     XFA_Attribute::Binding, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Script::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* addViewerPreferences */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* alwaysEmbed */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* passwordEdit */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_PasswordEdit::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x7a0cc471, L"passwordChar",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_PasswordEdit::passwordChar,
-     XFA_Attribute::PasswordChar, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_PasswordEdit::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe6f99487, L"hScrollPolicy",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_PasswordEdit::hScrollPolicy,
-     XFA_Attribute::HScrollPolicy, XFA_ScriptType::Basic},
-
-    /* numericEdit */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_NumericEdit::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_NumericEdit::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe6f99487, L"hScrollPolicy",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_NumericEdit::hScrollPolicy,
-     XFA_Attribute::HScrollPolicy, XFA_ScriptType::Basic},
-
-    /* encryptionMethod */
-    {0xc0811ed, L"use",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* change */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* pageArea */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x14a32d52, L"pagePosition",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::pagePosition,
-     XFA_Attribute::PagePosition, XFA_ScriptType::Basic},
-    {0x8340ea66, L"oddOrEven", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::oddOrEven,
-     XFA_Attribute::OddOrEven, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xa85e74f3, L"initialNumber",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::initialNumber,
-     XFA_Attribute::InitialNumber, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe9ba472, L"numbered", (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::numbered,
-     XFA_Attribute::Numbered, XFA_ScriptType::Basic},
-    {0xd70798c2, L"blankOrNotBlank",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_PageArea::blankOrNotBlank,
-     XFA_Attribute::BlankOrNotBlank, XFA_ScriptType::Basic},
-
-    /* submitUrl */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DefaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* oids */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Oids::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Oids::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Oids::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* signature */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Signature::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Signature::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* aDBE_JSConsole */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* caption */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Caption::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x34ae103c, L"reserve", (XFA_ATTRIBUTE_CALLBACK)&CJX_Caption::reserve,
-     XFA_Attribute::Reserve, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Caption::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Caption::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xf2009339, L"placement", (XFA_ATTRIBUTE_CALLBACK)&CJX_Caption::placement,
-     XFA_Attribute::Placement, XFA_ScriptType::Basic},
-
-    /* relevant */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* flipLabel */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* exData */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExData::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x42fed1fd, L"contentType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExData::contentType,
-     XFA_Attribute::ContentType, XFA_ScriptType::Basic},
-    {0x54fa722c, L"transferEncoding",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExData::transferEncoding,
-     XFA_Attribute::TransferEncoding, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_ExData::defaultValue, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExData::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc4547a08, L"maxLength", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExData::maxLength,
-     XFA_Attribute::MaxLength, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DefaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xdb55fec5, L"href", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExData::href,
-     XFA_Attribute::Href, XFA_ScriptType::Basic},
-
-    /* dayNames */
-    {0x29418bb7, L"abbr",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Abbr, XFA_ScriptType::Basic},
-
-    /* soapAction */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_SoapAction::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_SoapAction::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* defaultTypeface */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf531b059, L"writingScript",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::WritingScript, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* manifest */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Manifest::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1b8dce3e, L"action", (XFA_ATTRIBUTE_CALLBACK)&CJX_Manifest::action,
-     XFA_Attribute::Action, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Manifest::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Manifest::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* overflow */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Overflow::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x9dcc3ab3, L"trailer", (XFA_ATTRIBUTE_CALLBACK)&CJX_Overflow::trailer,
-     XFA_Attribute::Trailer, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Overflow::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target", (XFA_ATTRIBUTE_CALLBACK)&CJX_Overflow::target,
-     XFA_Attribute::Target, XFA_ScriptType::Basic},
-    {0xcbcaf66d, L"leader", (XFA_ATTRIBUTE_CALLBACK)&CJX_Overflow::leader,
-     XFA_Attribute::Leader, XFA_ScriptType::Basic},
-
-    /* linear */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Linear::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Linear::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Linear::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* currencySymbol */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-
-    /* delete */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Delete::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Delete::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* deltas */
-
-    /* digestMethod */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_DigestMethod::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_DigestMethod::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* instanceManager */
-    {0xb3543a6, L"max", (XFA_ATTRIBUTE_CALLBACK)&CJX_InstanceManager::max,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xb356ca4, L"min", (XFA_ATTRIBUTE_CALLBACK)&CJX_InstanceManager::min,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x6f544d49, L"count", (XFA_ATTRIBUTE_CALLBACK)&CJX_InstanceManager::count,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* equateRange */
-    {0x25363, L"to",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::To, XFA_ScriptType::Basic},
-    {0xa0933954, L"unicodeRange",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::UnicodeRange, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xcd7f7b54, L"from",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::From, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* medium */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Medium::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x4ef3d02c, L"orientation",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Medium::orientation,
-     XFA_Attribute::Orientation, XFA_ScriptType::Basic},
-    {0x65e30c67, L"imagingBBox",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Medium::imagingBBox,
-     XFA_Attribute::ImagingBBox, XFA_ScriptType::Basic},
-    {0x9041d4b0, L"short", (XFA_ATTRIBUTE_CALLBACK)&CJX_Medium::shortValue,
-     XFA_Attribute::Short, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Medium::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe349d044, L"stock", (XFA_ATTRIBUTE_CALLBACK)&CJX_Medium::stock,
-     XFA_Attribute::Stock, XFA_ScriptType::Basic},
-    {0xf6b4afb0, L"long", (XFA_ATTRIBUTE_CALLBACK)&CJX_Medium::longValue,
-     XFA_Attribute::Long, XFA_ScriptType::Basic},
-
-    /* textEdit */
-    {0x5ce6195, L"vScrollPolicy",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_TextEdit::vScrollPolicy,
-     XFA_Attribute::VScrollPolicy, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_TextEdit::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x1ef3a64a, L"allowRichText",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_TextEdit::allowRichText,
-     XFA_Attribute::AllowRichText, XFA_ScriptType::Basic},
-    {0x5a32e493, L"multiLine", (XFA_ATTRIBUTE_CALLBACK)&CJX_TextEdit::multiLine,
-     XFA_Attribute::MultiLine, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_TextEdit::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe6f99487, L"hScrollPolicy",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_TextEdit::hScrollPolicy,
-     XFA_Attribute::HScrollPolicy, XFA_ScriptType::Basic},
-
-    /* templateCache */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xd52482e0, L"maxEntries",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::MaxEntries, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* compressObjectStream */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* dataValue */
-    {0x42fed1fd, L"contentType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DataValue::contentType,
-     XFA_Attribute::ContentType, XFA_ScriptType::Basic},
-    {0x8855805f, L"contains", (XFA_ATTRIBUTE_CALLBACK)&CJX_DataValue::contains,
-     XFA_Attribute::Contains, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_DataValue::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_DataValue::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xe372ae97, L"isNull", (XFA_ATTRIBUTE_CALLBACK)&CJX_DataValue::isNull,
-     XFA_Attribute::IsNull, XFA_ScriptType::Basic},
-
-    /* accessibleContent */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* nodeList */
-
-    /* includeXDPContent */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* xmlConnection */
-    {0x2b5df51e, L"dataDescription",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_XmlConnection::dataDescription,
-     XFA_Attribute::DataDescription, XFA_ScriptType::Basic},
-
-    /* validateApprovalSignatures */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* signData */
-    {0xbb8df5d, L"ref", (XFA_ATTRIBUTE_CALLBACK)&CJX_SignData::ref,
-     XFA_Attribute::Ref, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_SignData::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x226ca8f1, L"operation", (XFA_ATTRIBUTE_CALLBACK)&CJX_SignData::operation,
-     XFA_Attribute::Operation, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_SignData::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target", (XFA_ATTRIBUTE_CALLBACK)&CJX_SignData::target,
-     XFA_Attribute::Target, XFA_ScriptType::Basic},
-
-    /* packets */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* datePattern */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-
-    /* duplexOption */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* base */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* bind */
-    {0xbb8df5d, L"ref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bind::ref,
-     XFA_Attribute::Ref, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bind::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x42fed1fd, L"contentType", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bind::contentType,
-     XFA_Attribute::ContentType, XFA_ScriptType::Basic},
-    {0x54fa722c, L"transferEncoding",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Bind::transferEncoding,
-     XFA_Attribute::TransferEncoding, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bind::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xf197844d, L"match", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bind::match,
-     XFA_Attribute::Match, XFA_ScriptType::Basic},
-
-    /* compression */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* user */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_User::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_User::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* rectangle */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Rectangle::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Rectangle::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd996fa9b, L"hand", (XFA_ATTRIBUTE_CALLBACK)&CJX_Rectangle::hand,
-     XFA_Attribute::Hand, XFA_ScriptType::Basic},
-
-    /* effectiveOutputPolicy */
-    {0x21aed, L"id",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Id, XFA_ScriptType::Basic},
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* aDBE_JSDebugger */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* acrobat7 */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* interactive */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* locale */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* currentPage */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* data */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* date */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Date::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}", (XFA_ATTRIBUTE_CALLBACK)&CJX_Date::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Date::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Date::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* desc */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Desc::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Desc::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* encrypt */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encrypt::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x28dee6e9, L"format", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encrypt::format,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Encrypt::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* draw */
-    {0x68, L"h", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::h, XFA_Attribute::H,
-     XFA_ScriptType::Basic},
-    {0x77, L"w", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::w, XFA_Attribute::W,
-     XFA_ScriptType::Basic},
-    {0x78, L"x", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::x, XFA_Attribute::X,
-     XFA_ScriptType::Basic},
-    {0x79, L"y", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::y, XFA_Attribute::Y,
-     XFA_ScriptType::Basic},
-    {0x2282c73, L"hAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::hAlign,
-     XFA_Attribute::HAlign, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2ee7678f, L"rotate", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::rotate,
-     XFA_Attribute::Rotate, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0x7a7cc341, L"vAlign", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::vAlign,
-     XFA_Attribute::VAlign, XFA_ScriptType::Basic},
-    {0x7c2ff6ae, L"maxH", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::maxH,
-     XFA_Attribute::MaxH, XFA_ScriptType::Basic},
-    {0x7c2ff6bd, L"maxW", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::maxW,
-     XFA_Attribute::MaxW, XFA_ScriptType::Basic},
-    {0x7d02356c, L"minH", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::minH,
-     XFA_Attribute::MinH, XFA_ScriptType::Basic},
-    {0x7d02357b, L"minW", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::minW,
-     XFA_Attribute::MinW, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xa03cf627, L"rawValue", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::rawValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xac06e2b0, L"colSpan", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::colSpan,
-     XFA_Attribute::ColSpan, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbc8fa350, L"locale", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::locale,
-     XFA_Attribute::Locale, XFA_ScriptType::Basic},
-    {0xc2bd40fd, L"anchorType", (XFA_ATTRIBUTE_CALLBACK)&CJX_Draw::anchorType,
-     XFA_Attribute::AnchorType, XFA_ScriptType::Basic},
-
-    /* encryption */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* meridiemNames */
-
-    /* messaging */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* speak */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Speak::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x39cdb0a2, L"priority", (XFA_ATTRIBUTE_CALLBACK)&CJX_Speak::priority,
-     XFA_Attribute::Priority, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Speak::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xeb511b54, L"disable", (XFA_ATTRIBUTE_CALLBACK)&CJX_Speak::disable,
-     XFA_Attribute::Disable, XFA_ScriptType::Basic},
-
-    /* dataGroup */
-
-    /* common */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* #text */
-    {0xd6e27f1d, L"value",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Value, XFA_ScriptType::Basic},
-
-    /* paginationOverride */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* reasons */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Reasons::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Reasons::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Reasons::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* signatureProperties */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_SignatureProperties::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_SignatureProperties::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* threshold */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* appearanceFilter */
-    {0x21aed, L"id",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Id, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* fill */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Fill::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Fill::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Fill::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* font */
-    {0xcb0ac9, L"lineThrough", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::lineThrough,
-     XFA_Attribute::LineThrough, XFA_ScriptType::Basic},
-    {0x2c1c7f1, L"typeface", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::typeface,
-     XFA_Attribute::Typeface, XFA_ScriptType::Basic},
-    {0x8c74ae9, L"fontHorizontalScale",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::fontHorizontalScale,
-     XFA_Attribute::FontHorizontalScale, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2cd79033, L"kerningMode", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::kerningMode,
-     XFA_Attribute::KerningMode, XFA_ScriptType::Basic},
-    {0x3a0273a6, L"underline", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::underline,
-     XFA_Attribute::Underline, XFA_ScriptType::Basic},
-    {0x4873c601, L"baselineShift",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::baselineShift,
-     XFA_Attribute::BaselineShift, XFA_ScriptType::Basic},
-    {0x4b319767, L"overlinePeriod",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::overlinePeriod,
-     XFA_Attribute::OverlinePeriod, XFA_ScriptType::Basic},
-    {0x79543055, L"letterSpacing",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::letterSpacing,
-     XFA_Attribute::LetterSpacing, XFA_ScriptType::Basic},
-    {0x8ec6204c, L"lineThroughPeriod",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::lineThroughPeriod,
-     XFA_Attribute::LineThroughPeriod, XFA_ScriptType::Basic},
-    {0x907c7719, L"fontVerticalScale",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::fontVerticalScale,
-     XFA_Attribute::FontVerticalScale, XFA_ScriptType::Basic},
-    {0xa686975b, L"size", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::size,
-     XFA_Attribute::Size, XFA_ScriptType::Basic},
-    {0xb5e49bf2, L"posture", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::posture,
-     XFA_Attribute::Posture, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xbd6e1d88, L"weight", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::weight,
-     XFA_Attribute::Weight, XFA_ScriptType::Basic},
-    {0xbd96a0e9, L"underlinePeriod",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::underlinePeriod,
-     XFA_Attribute::UnderlinePeriod, XFA_ScriptType::Basic},
-    {0xc0ec9fa4, L"overline", (XFA_ATTRIBUTE_CALLBACK)&CJX_Font::overline,
-     XFA_Attribute::Overline, XFA_ScriptType::Basic},
-
-    /* form */
-    {0xaf754613, L"checksum",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Form_Checksum,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* mediumInfo */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* certificate */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Certificate::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Certificate::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* password */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Password::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Password::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* runScripts */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* trace */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* float */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Float::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xa52682bd, L"{default}", (XFA_ATTRIBUTE_CALLBACK)&CJX_Float::defaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Float::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value", (XFA_ATTRIBUTE_CALLBACK)&CJX_Float::value,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* renderPolicy */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* logPseudoModel */
-
-    /* destination */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* value */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Value::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x8e1c2921, L"relevant", (XFA_ATTRIBUTE_CALLBACK)&CJX_Value::relevant,
-     XFA_Attribute::Relevant, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Value::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xea7090a0, L"override", (XFA_ATTRIBUTE_CALLBACK)&CJX_Value::override,
-     XFA_Attribute::Override, XFA_ScriptType::Basic},
-
-    /* bookend */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bookend::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x9dcc3ab3, L"trailer", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bookend::trailer,
-     XFA_Attribute::Trailer, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bookend::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xcbcaf66d, L"leader", (XFA_ATTRIBUTE_CALLBACK)&CJX_Bookend::leader,
-     XFA_Attribute::Leader, XFA_ScriptType::Basic},
-
-    /* exObject */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExObject::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x60a61edd, L"codeType", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExObject::codeType,
-     XFA_Attribute::CodeType, XFA_ScriptType::Basic},
-    {0xb373a862, L"archive", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExObject::archive,
-     XFA_Attribute::Archive, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExObject::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xe1a26b56, L"codeBase", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExObject::codeBase,
-     XFA_Attribute::CodeBase, XFA_ScriptType::Basic},
-    {0xeb091003, L"classId", (XFA_ATTRIBUTE_CALLBACK)&CJX_ExObject::classId,
-     XFA_Attribute::ClassId, XFA_ScriptType::Basic},
-
-    /* openAction */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* neverEmbed */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* bindItems */
-    {0x47d03490, L"connection",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_BindItems::connection,
-     XFA_Attribute::Connection, XFA_ScriptType::Basic},
-    {0xc39a88bd, L"labelRef", (XFA_ATTRIBUTE_CALLBACK)&CJX_BindItems::labelRef,
-     XFA_Attribute::LabelRef, XFA_ScriptType::Basic},
-    {0xd50f903a, L"valueRef", (XFA_ATTRIBUTE_CALLBACK)&CJX_BindItems::valueRef,
-     XFA_Attribute::ValueRef, XFA_ScriptType::Basic},
-
-    /* calculate */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Calculate::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Calculate::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xea7090a0, L"override", (XFA_ATTRIBUTE_CALLBACK)&CJX_Calculate::override,
-     XFA_Attribute::Override, XFA_ScriptType::Basic},
-
-    /* print */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* extras */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Extras::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Extras::type,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Extras::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* proto */
-
-    /* dSigData */
-
-    /* creator */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* connect */
-    {0xbb8df5d, L"ref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Connect::ref,
-     XFA_Attribute::Ref, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Connect::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x24d85167, L"timeout", (XFA_ATTRIBUTE_CALLBACK)&CJX_Connect::timeout,
-     XFA_Attribute::Timeout, XFA_ScriptType::Basic},
-    {0x47d03490, L"connection",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Connect::connection,
-     XFA_Attribute::Connection, XFA_ScriptType::Basic},
-    {0x552d9ad5, L"usage", (XFA_ATTRIBUTE_CALLBACK)&CJX_Connect::usage,
-     XFA_Attribute::Usage, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Connect::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc860f30a, L"delayedOpen",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Connect::delayedOpen,
-     XFA_Attribute::DelayedOpen, XFA_ScriptType::Basic},
-
-    /* permissions */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* connectionSet */
-
-    /* submit */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Submit::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x28dee6e9, L"format", (XFA_ATTRIBUTE_CALLBACK)&CJX_Submit::format,
-     XFA_Attribute::Format, XFA_ScriptType::Basic},
-    {0x824f21b7, L"embedPDF", (XFA_ATTRIBUTE_CALLBACK)&CJX_Submit::embedPDF,
-     XFA_Attribute::EmbedPDF, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Submit::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target", (XFA_ATTRIBUTE_CALLBACK)&CJX_Submit::target,
-     XFA_Attribute::Target, XFA_ScriptType::Basic},
-    {0xdc75676c, L"textEncoding",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Submit::textEncoding,
-     XFA_Attribute::TextEncoding, XFA_ScriptType::Basic},
-    {0xf889e747, L"xdpContent", (XFA_ATTRIBUTE_CALLBACK)&CJX_Submit::xdpContent,
-     XFA_Attribute::XdpContent, XFA_ScriptType::Basic},
-
-    /* range */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* linearized */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* packet */
-    {0x97be91b, L"content", (XFA_ATTRIBUTE_CALLBACK)&CJX_Packet::content,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* rootElement */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_RootElement::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_RootElement::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* plaintextMetadata */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* numberSymbols */
-
-    /* printHighQuality */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* driver */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* incrementalLoad */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* subjectDN */
-    {0x4156ee3f, L"delimiter",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_SubjectDN::delimiter,
-     XFA_Attribute::Delimiter, XFA_ScriptType::Basic},
-
-    /* compressLogicalStructure */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* incrementalMerge */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* radial */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Radial::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type", (XFA_ATTRIBUTE_CALLBACK)&CJX_Radial::type,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Radial::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* variables */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Variables::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Variables::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* timePatterns */
-
-    /* effectiveInputPolicy */
-    {0x21aed, L"id",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Id, XFA_ScriptType::Basic},
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* nameAttr */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* conformance */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* transform */
-    {0xbb8df5d, L"ref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Ref, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* lockDocument */
-    {0x21aed, L"id",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Id, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x2f16a382, L"type",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Type, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* breakAfter */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakAfter::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x453eaf38, L"startNew", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakAfter::startNew,
-     XFA_Attribute::StartNew, XFA_ScriptType::Basic},
-    {0x9dcc3ab3, L"trailer", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakAfter::trailer,
-     XFA_Attribute::Trailer, XFA_ScriptType::Basic},
-    {0xa6118c89, L"targetType",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakAfter::targetType,
-     XFA_Attribute::TargetType, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakAfter::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xc8da4da7, L"target", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakAfter::target,
-     XFA_Attribute::Target, XFA_ScriptType::Basic},
-    {0xcbcaf66d, L"leader", (XFA_ATTRIBUTE_CALLBACK)&CJX_BreakAfter::leader,
-     XFA_Attribute::Leader, XFA_ScriptType::Basic},
-
-    /* line */
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Line::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xabef37e3, L"slope", (XFA_ATTRIBUTE_CALLBACK)&CJX_Line::slope,
-     XFA_Attribute::Slope, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Line::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-    {0xd996fa9b, L"hand", (XFA_ATTRIBUTE_CALLBACK)&CJX_Line::hand,
-     XFA_Attribute::Hand, XFA_ScriptType::Basic},
-
-    /* list */
-    {0xa60dd202, L"length", (XFA_ATTRIBUTE_CALLBACK)&CJX_List::length,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* source */
-    {0x20146, L"db", (XFA_ATTRIBUTE_CALLBACK)&CJX_Source::db,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Source::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Source::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* occur */
-    {0xb3543a6, L"max", (XFA_ATTRIBUTE_CALLBACK)&CJX_Occur::max,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xb356ca4, L"min", (XFA_ATTRIBUTE_CALLBACK)&CJX_Occur::min,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Occur::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x7d0b5fca, L"initial", (XFA_ATTRIBUTE_CALLBACK)&CJX_Occur::initial,
-     XFA_Attribute::Initial, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Occur::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* pickTrayByPDFSize */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* monthNames */
-    {0x29418bb7, L"abbr",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Abbr, XFA_ScriptType::Basic},
-
-    /* severity */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* groupParent */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* documentAssembly */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* numberSymbol */
-    {0x31b19c1, L"name",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-
-    /* tagged */
-    {0xbe52dfbf, L"desc",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_String,
-     XFA_Attribute::Desc, XFA_ScriptType::Basic},
-    {0xf6b47749, L"lock",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Attribute_BOOL,
-     XFA_Attribute::Lock, XFA_ScriptType::Basic},
-
-    /* items */
-    {0xbb8df5d, L"ref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Items::ref,
-     XFA_Attribute::Ref, XFA_ScriptType::Basic},
-    {0xc0811ed, L"use", (XFA_ATTRIBUTE_CALLBACK)&CJX_Items::use,
-     XFA_Attribute::Use, XFA_ScriptType::Basic},
-    {0x570ce835, L"presence", (XFA_ATTRIBUTE_CALLBACK)&CJX_Items::presence,
-     XFA_Attribute::Presence, XFA_ScriptType::Basic},
-    {0xa5b410cf, L"save", (XFA_ATTRIBUTE_CALLBACK)&CJX_Items::save,
-     XFA_Attribute::Save, XFA_ScriptType::Basic},
-    {0xbc254332, L"usehref", (XFA_ATTRIBUTE_CALLBACK)&CJX_Items::usehref,
-     XFA_Attribute::Usehref, XFA_ScriptType::Basic},
-
-    /* object */
-    {0xb2c80857, L"className", (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::className,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* list */
-    {0xa60dd202, L"length", (XFA_ATTRIBUTE_CALLBACK)&CJX_List::length,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-
-    /* [unknown] */
-
-    /* tree */
-    {0x31b19c1, L"name", (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::name,
-     XFA_Attribute::Name, XFA_ScriptType::Basic},
-    {0x9f9d0f9, L"all", (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::all,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0x4df15659, L"nodes", (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::nodes,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0x78a8d6cf, L"classAll", (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::classAll,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0xcad6d8ca, L"parent", (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::parent,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0xd5679c78, L"index", (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::index,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xdb5b4bce, L"classIndex", (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::classIndex,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xe4989adf, L"somExpression",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Tree::somExpression, XFA_Attribute::Unknown,
-     XFA_ScriptType::Basic},
-
-    /* node */
-    {0x21aed, L"id", (XFA_ATTRIBUTE_CALLBACK)&CJX_Node::id, XFA_Attribute::Id,
-     XFA_ScriptType::Basic},
-    {0x234a1, L"ns", (XFA_ATTRIBUTE_CALLBACK)&CJX_Node::ns,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0x50d1a9d1, L"model", (XFA_ATTRIBUTE_CALLBACK)&CJX_Node::model,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0xacb4823f, L"isContainer", (XFA_ATTRIBUTE_CALLBACK)&CJX_Node::isContainer,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xe372ae97, L"isNull", (XFA_ATTRIBUTE_CALLBACK)&CJX_Node::isNull,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xfe612a5b, L"oneOfChild", (XFA_ATTRIBUTE_CALLBACK)&CJX_Node::oneOfChild,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-
-    /* [unknown] */
-
-    /* [unknown] */
-
-    /* model */
-    {0x97c1c65, L"context", (XFA_ATTRIBUTE_CALLBACK)&CJX_Model::context,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-    {0x58be2870, L"aliasNode", (XFA_ATTRIBUTE_CALLBACK)&CJX_Model::aliasNode,
-     XFA_Attribute::Unknown, XFA_ScriptType::Object},
-
-    /* [unknown] */
-    {0xa52682bd, L"{default}",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DefaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-    {0xd6e27f1d, L"value",
-     (XFA_ATTRIBUTE_CALLBACK)&CJX_Object::Script_Som_DefaultValue,
-     XFA_Attribute::Unknown, XFA_ScriptType::Basic},
-};
-const int32_t g_iSomAttributeCount = FX_ArraySize(g_SomAttributeData);
diff --git a/xfa/fxfa/parser/xfa_basic_data_enum.cpp b/xfa/fxfa/parser/xfa_basic_data_enum.cpp
deleted file mode 100644
index 33c5cf4..0000000
--- a/xfa/fxfa/parser/xfa_basic_data_enum.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "xfa/fxfa/parser/xfa_basic_data.h"
-
-#include "xfa/fxfa/fxfa_basic.h"
-
-const XFA_AttributeEnumInfo g_XFAEnumData[] = {
-    {0x2a, L"*", XFA_AttributeEnum::Asterisk},
-    {0x2f, L"/", XFA_AttributeEnum::Slash},
-    {0x5c, L"\\", XFA_AttributeEnum::Backslash},
-    {0x239bd, L"on", XFA_AttributeEnum::On},
-    {0x25356, L"tb", XFA_AttributeEnum::Tb},
-    {0x25885, L"up", XFA_AttributeEnum::Up},
-    {0x91b281, L"metaData", XFA_AttributeEnum::MetaData},
-    {0x1f8dedb, L"delegate", XFA_AttributeEnum::Delegate},
-    {0x2a6c55a, L"postSubmit", XFA_AttributeEnum::PostSubmit},
-    {0x31b19c1, L"name", XFA_AttributeEnum::Name},
-    {0x378a38a, L"cross", XFA_AttributeEnum::Cross},
-    {0x3848b3f, L"next", XFA_AttributeEnum::Next},
-    {0x48b6670, L"none", XFA_AttributeEnum::None},
-    {0x51aafe5, L"shortEdge", XFA_AttributeEnum::ShortEdge},
-    {0x55264c4, L"1mod10_1mod11", XFA_AttributeEnum::Checksum_1mod10_1mod11},
-    {0x5a5c519, L"height", XFA_AttributeEnum::Height},
-    {0x89ce549, L"crossDiagonal", XFA_AttributeEnum::CrossDiagonal},
-    {0x9f9d0f9, L"all", XFA_AttributeEnum::All},
-    {0x9f9db48, L"any", XFA_AttributeEnum::Any},
-    {0xa126261, L"toRight", XFA_AttributeEnum::ToRight},
-    {0xa36de29, L"matchTemplate", XFA_AttributeEnum::MatchTemplate},
-    {0xa48d040, L"dpl", XFA_AttributeEnum::Dpl},
-    {0xa559c05, L"invisible", XFA_AttributeEnum::Invisible},
-    {0xa7d48e3, L"fit", XFA_AttributeEnum::Fit},
-    {0xa8a8f80, L"width", XFA_AttributeEnum::Width},
-    {0xab466bb, L"preSubmit", XFA_AttributeEnum::PreSubmit},
-    {0xacc5785, L"ipl", XFA_AttributeEnum::Ipl},
-    {0xafab0f8, L"flateCompress", XFA_AttributeEnum::FlateCompress},
-    {0xb355816, L"med", XFA_AttributeEnum::Med},
-    {0xb69ef77, L"odd", XFA_AttributeEnum::Odd},
-    {0xb69f9bb, L"off", XFA_AttributeEnum::Off},
-    {0xb843dba, L"pdf", XFA_AttributeEnum::Pdf},
-    {0xbb912b8, L"row", XFA_AttributeEnum::Row},
-    {0xbedaf33, L"top", XFA_AttributeEnum::Top},
-    {0xc56afcc, L"xdp", XFA_AttributeEnum::Xdp},
-    {0xc56ba02, L"xfd", XFA_AttributeEnum::Xfd},
-    {0xc56ddf1, L"xml", XFA_AttributeEnum::Xml},
-    {0xc8b65f3, L"zip", XFA_AttributeEnum::Zip},
-    {0xc8b89d6, L"zpl", XFA_AttributeEnum::Zpl},
-    {0xf55d7ee, L"visible", XFA_AttributeEnum::Visible},
-    {0xfe3596a, L"exclude", XFA_AttributeEnum::Exclude},
-    {0x109d7ce7, L"mouseEnter", XFA_AttributeEnum::MouseEnter},
-    {0x10f1bc0c, L"pair", XFA_AttributeEnum::Pair},
-    {0x1154efe6, L"filter", XFA_AttributeEnum::Filter},
-    {0x125bc94b, L"moveLast", XFA_AttributeEnum::MoveLast},
-    {0x12e1f1f0, L"exportAndImport", XFA_AttributeEnum::ExportAndImport},
-    {0x13000c60, L"push", XFA_AttributeEnum::Push},
-    {0x138ee315, L"portrait", XFA_AttributeEnum::Portrait},
-    {0x14da2125, L"default", XFA_AttributeEnum::Default},
-    {0x157749a5, L"storedProc", XFA_AttributeEnum::StoredProc},
-    {0x16641198, L"stayBOF", XFA_AttributeEnum::StayBOF},
-    {0x16b2fc5b, L"stayEOF", XFA_AttributeEnum::StayEOF},
-    {0x17fad373, L"postPrint", XFA_AttributeEnum::PostPrint},
-    {0x193207d0, L"usCarrier", XFA_AttributeEnum::UsCarrier},
-    {0x193ade3e, L"right", XFA_AttributeEnum::Right},
-    {0x1bfc72d9, L"preOpen", XFA_AttributeEnum::PreOpen},
-    {0x1cc9317a, L"actual", XFA_AttributeEnum::Actual},
-    {0x1f31df1e, L"rest", XFA_AttributeEnum::Rest},
-    {0x1fb1bf14, L"topCenter", XFA_AttributeEnum::TopCenter},
-    {0x207de667, L"standardSymbol", XFA_AttributeEnum::StandardSymbol},
-    {0x2196a452, L"initialize", XFA_AttributeEnum::Initialize},
-    {0x23bd40c7, L"justifyAll", XFA_AttributeEnum::JustifyAll},
-    {0x247cf3e9, L"normal", XFA_AttributeEnum::Normal},
-    {0x25aa946b, L"landscape", XFA_AttributeEnum::Landscape},
-    {0x2739b5c9, L"nonInteractive", XFA_AttributeEnum::NonInteractive},
-    {0x27410f03, L"mouseExit", XFA_AttributeEnum::MouseExit},
-    {0x2854e62c, L"minus", XFA_AttributeEnum::Minus},
-    {0x287e936a, L"diagonalLeft", XFA_AttributeEnum::DiagonalLeft},
-    {0x2972a98f, L"simplexPaginated", XFA_AttributeEnum::SimplexPaginated},
-    {0x29d8225f, L"document", XFA_AttributeEnum::Document},
-    {0x2a9d3016, L"warning", XFA_AttributeEnum::Warning},
-    {0x2b35b6d9, L"auto", XFA_AttributeEnum::Auto},
-    {0x2c1653d9, L"below", XFA_AttributeEnum::Below},
-    {0x2c1f0540, L"bottomLeft", XFA_AttributeEnum::BottomLeft},
-    {0x2c44e816, L"bottomCenter", XFA_AttributeEnum::BottomCenter},
-    {0x2cd3e9f3, L"tcpl", XFA_AttributeEnum::Tcpl},
-    {0x2d08af85, L"text", XFA_AttributeEnum::Text},
-    {0x2dc478eb, L"grouping", XFA_AttributeEnum::Grouping},
-    {0x2ef3afdd, L"secureSymbol", XFA_AttributeEnum::SecureSymbol},
-    {0x2f2dd29a, L"preExecute", XFA_AttributeEnum::PreExecute},
-    {0x33c43dec, L"docClose", XFA_AttributeEnum::DocClose},
-    {0x33f25bb5, L"keyset", XFA_AttributeEnum::Keyset},
-    {0x34e363da, L"vertical", XFA_AttributeEnum::Vertical},
-    {0x361fa1b6, L"preSave", XFA_AttributeEnum::PreSave},
-    {0x36f1c6d8, L"preSign", XFA_AttributeEnum::PreSign},
-    {0x399f02b5, L"bottom", XFA_AttributeEnum::Bottom},
-    {0x3b0ab096, L"toTop", XFA_AttributeEnum::ToTop},
-    {0x3c752495, L"verify", XFA_AttributeEnum::Verify},
-    {0x3ce05d68, L"first", XFA_AttributeEnum::First},
-    {0x3ecead94, L"contentArea", XFA_AttributeEnum::ContentArea},
-    {0x40623b5b, L"solid", XFA_AttributeEnum::Solid},
-    {0x42c6cd8d, L"pessimistic", XFA_AttributeEnum::Pessimistic},
-    {0x43ddc6bf, L"duplexPaginated", XFA_AttributeEnum::DuplexPaginated},
-    {0x442f68c8, L"round", XFA_AttributeEnum::Round},
-    {0x45efb847, L"remerge", XFA_AttributeEnum::Remerge},
-    {0x46972265, L"ordered", XFA_AttributeEnum::Ordered},
-    {0x46f95531, L"percent", XFA_AttributeEnum::Percent},
-    {0x46fd25ae, L"even", XFA_AttributeEnum::Even},
-    {0x4731d6ba, L"exit", XFA_AttributeEnum::Exit},
-    {0x4977356b, L"toolTip", XFA_AttributeEnum::ToolTip},
-    {0x49b980ee, L"orderedOccurrence", XFA_AttributeEnum::OrderedOccurrence},
-    {0x4a7e2dfe, L"readOnly", XFA_AttributeEnum::ReadOnly},
-    {0x4c4e8acb, L"currency", XFA_AttributeEnum::Currency},
-    {0x4dcf25f8, L"concat", XFA_AttributeEnum::Concat},
-    {0x4febb826, L"Thai", XFA_AttributeEnum::Thai},
-    {0x50ef95b2, L"embossed", XFA_AttributeEnum::Embossed},
-    {0x516e35ce, L"formdata", XFA_AttributeEnum::Formdata},
-    {0x52fa6f0e, L"Greek", XFA_AttributeEnum::Greek},
-    {0x54034c2f, L"decimal", XFA_AttributeEnum::Decimal},
-    {0x542c7300, L"select", XFA_AttributeEnum::Select},
-    {0x551f0ae5, L"longEdge", XFA_AttributeEnum::LongEdge},
-    {0x55520a8a, L"protected", XFA_AttributeEnum::Protected},
-    {0x559f76f3, L"bottomRight", XFA_AttributeEnum::BottomRight},
-    {0x568cb500, L"zero", XFA_AttributeEnum::Zero},
-    {0x56bcecb7, L"forwardOnly", XFA_AttributeEnum::ForwardOnly},
-    {0x56bf456b, L"docReady", XFA_AttributeEnum::DocReady},
-    {0x573cb40c, L"hidden", XFA_AttributeEnum::Hidden},
-    {0x582e3424, L"include", XFA_AttributeEnum::Include},
-    {0x58a3dd29, L"dashed", XFA_AttributeEnum::Dashed},
-    {0x5955b22b, L"multiSelect", XFA_AttributeEnum::MultiSelect},
-    {0x598d5c53, L"inactive", XFA_AttributeEnum::Inactive},
-    {0x59c8f27d, L"embed", XFA_AttributeEnum::Embed},
-    {0x5e7555e8, L"static", XFA_AttributeEnum::Static},
-    {0x606d4def, L"onEntry", XFA_AttributeEnum::OnEntry},
-    {0x6195eafb, L"Cyrillic", XFA_AttributeEnum::Cyrillic},
-    {0x6491b0f3, L"nonBlank", XFA_AttributeEnum::NonBlank},
-    {0x67bef031, L"topRight", XFA_AttributeEnum::TopRight},
-    {0x67df5ebd, L"Hebrew", XFA_AttributeEnum::Hebrew},
-    {0x6aea98be, L"topLeft", XFA_AttributeEnum::TopLeft},
-    {0x6c51afc1, L"center", XFA_AttributeEnum::Center},
-    {0x7145e6bf, L"moveFirst", XFA_AttributeEnum::MoveFirst},
-    {0x7375465c, L"diamond", XFA_AttributeEnum::Diamond},
-    {0x7461aef4, L"pageOdd", XFA_AttributeEnum::PageOdd},
-    {0x75f8aeb2, L"1mod10", XFA_AttributeEnum::Checksum_1mod10},
-    {0x76d708e0, L"Korean", XFA_AttributeEnum::Korean},
-    {0x789f14d7, L"aboveEmbedded", XFA_AttributeEnum::AboveEmbedded},
-    {0x792ea39f, L"zipCompress", XFA_AttributeEnum::ZipCompress},
-    {0x7a5b7193, L"numeric", XFA_AttributeEnum::Numeric},
-    {0x7abec0d2, L"circle", XFA_AttributeEnum::Circle},
-    {0x7afbba38, L"toBottom", XFA_AttributeEnum::ToBottom},
-    {0x7b95e661, L"inverted", XFA_AttributeEnum::Inverted},
-    {0x7baca2e3, L"update", XFA_AttributeEnum::Update},
-    {0x7eb5da2c, L"isoname", XFA_AttributeEnum::Isoname},
-    {0x7f6fd3d7, L"server", XFA_AttributeEnum::Server},
-    {0x814f82b5, L"position", XFA_AttributeEnum::Position},
-    {0x82deacf0, L"middleCenter", XFA_AttributeEnum::MiddleCenter},
-    {0x83a49dc6, L"optional", XFA_AttributeEnum::Optional},
-    {0x861a116f, L"usePrinterSetting", XFA_AttributeEnum::UsePrinterSetting},
-    {0x86701ce0, L"outline", XFA_AttributeEnum::Outline},
-    {0x8808385e, L"indexChange", XFA_AttributeEnum::IndexChange},
-    {0x891f4606, L"change", XFA_AttributeEnum::Change},
-    {0x89939f36, L"pageArea", XFA_AttributeEnum::PageArea},
-    {0x8b5c3b25, L"once", XFA_AttributeEnum::Once},
-    {0x8b5c6962, L"only", XFA_AttributeEnum::Only},
-    {0x8b90e1f2, L"open", XFA_AttributeEnum::Open},
-    {0x8bcfe96e, L"caption", XFA_AttributeEnum::Caption},
-    {0x8ce83ef8, L"raised", XFA_AttributeEnum::Raised},
-    {0x8d269cae, L"justify", XFA_AttributeEnum::Justify},
-    {0x8fd520dc, L"refAndDescendants", XFA_AttributeEnum::RefAndDescendants},
-    {0x9041d4b0, L"short", XFA_AttributeEnum::Short},
-    {0x90c94426, L"pageFront", XFA_AttributeEnum::PageFront},
-    {0x936beee5, L"monospace", XFA_AttributeEnum::Monospace},
-    {0x947fa00f, L"middle", XFA_AttributeEnum::Middle},
-    {0x9528a7b4, L"prePrint", XFA_AttributeEnum::PrePrint},
-    {0x959ab231, L"always", XFA_AttributeEnum::Always},
-    {0x96d61bf0, L"unknown", XFA_AttributeEnum::Unknown},
-    {0x997194ee, L"toLeft", XFA_AttributeEnum::ToLeft},
-    {0x9a83a3cd, L"above", XFA_AttributeEnum::Above},
-    {0x9d0d71c7, L"dashDot", XFA_AttributeEnum::DashDot},
-    {0x9df56f3e, L"gregorian", XFA_AttributeEnum::Gregorian},
-    {0x9f6723fd, L"Roman", XFA_AttributeEnum::Roman},
-    {0x9f693b21, L"mouseDown", XFA_AttributeEnum::MouseDown},
-    {0xa1429b36, L"symbol", XFA_AttributeEnum::Symbol},
-    {0xa5aa45cb, L"pageEven", XFA_AttributeEnum::PageEven},
-    {0xa68635f1, L"sign", XFA_AttributeEnum::Sign},
-    {0xa7315093, L"addNew", XFA_AttributeEnum::AddNew},
-    {0xa7a773fa, L"star", XFA_AttributeEnum::Star},
-    {0xa7d57b45, L"optimistic", XFA_AttributeEnum::Optimistic},
-    {0xa8077321, L"rl-tb", XFA_AttributeEnum::Rl_tb},
-    {0xa8f1468d, L"middleRight", XFA_AttributeEnum::MiddleRight},
-    {0xaa84a1f1, L"maintain", XFA_AttributeEnum::Maintain},
-    {0xab40b12c, L"package", XFA_AttributeEnum::Package},
-    {0xac8b4d85, L"SimplifiedChinese", XFA_AttributeEnum::SimplifiedChinese},
-    {0xadae6744, L"toCenter", XFA_AttributeEnum::ToCenter},
-    {0xb0129df1, L"back", XFA_AttributeEnum::Back},
-    {0xb0f088cf, L"unspecified", XFA_AttributeEnum::Unspecified},
-    {0xb1271067, L"batchOptimistic", XFA_AttributeEnum::BatchOptimistic},
-    {0xb18313a1, L"bold", XFA_AttributeEnum::Bold},
-    {0xb1833cad, L"both", XFA_AttributeEnum::Both},
-    {0xb221123f, L"butt", XFA_AttributeEnum::Butt},
-    {0xb40c36bf, L"client", XFA_AttributeEnum::Client},
-    {0xb56c7053, L"2mod10", XFA_AttributeEnum::Checksum_2mod10},
-    {0xb683a345, L"imageOnly", XFA_AttributeEnum::ImageOnly},
-    {0xb7732dea, L"horizontal", XFA_AttributeEnum::Horizontal},
-    {0xb88652a4, L"dotted", XFA_AttributeEnum::Dotted},
-    {0xbb2f2880, L"userControl", XFA_AttributeEnum::UserControl},
-    {0xbbb79c5d, L"diagonalRight", XFA_AttributeEnum::DiagonalRight},
-    {0xbd077154, L"consumeData", XFA_AttributeEnum::ConsumeData},
-    {0xbd3fb11e, L"check", XFA_AttributeEnum::Check},
-    {0xbde9abda, L"data", XFA_AttributeEnum::Data},
-    {0xbf5a02d8, L"down", XFA_AttributeEnum::Down},
-    {0xbf7450ee, L"sansSerif", XFA_AttributeEnum::SansSerif},
-    {0xc02d649f, L"inline", XFA_AttributeEnum::Inline},
-    {0xc11a9e3a, L"TraditionalChinese", XFA_AttributeEnum::TraditionalChinese},
-    {0xc16169d8, L"warn", XFA_AttributeEnum::Warn},
-    {0xc16f071f, L"refOnly", XFA_AttributeEnum::RefOnly},
-    {0xc27c8ba5, L"interactiveForms", XFA_AttributeEnum::InteractiveForms},
-    {0xc2d1b15c, L"word", XFA_AttributeEnum::Word},
-    {0xc3621288, L"unordered", XFA_AttributeEnum::Unordered},
-    {0xc5251981, L"required", XFA_AttributeEnum::Required},
-    {0xc7088e7d, L"importOnly", XFA_AttributeEnum::ImportOnly},
-    {0xc72cf0e3, L"belowEmbedded", XFA_AttributeEnum::BelowEmbedded},
-    {0xc819cf07, L"Japanese", XFA_AttributeEnum::Japanese},
-    {0xcdce56b3, L"full", XFA_AttributeEnum::Full},
-    {0xce0122e3, L"rl-row", XFA_AttributeEnum::Rl_row},
-    {0xcf7d71f1, L"Vietnamese", XFA_AttributeEnum::Vietnamese},
-    {0xcfde3e09, L"EastEuropeanRoman", XFA_AttributeEnum::EastEuropeanRoman},
-    {0xd576d08e, L"mouseUp", XFA_AttributeEnum::MouseUp},
-    {0xd7a92904, L"exportOnly", XFA_AttributeEnum::ExportOnly},
-    {0xd8ed1467, L"clear", XFA_AttributeEnum::Clear},
-    {0xd95657a6, L"click", XFA_AttributeEnum::Click},
-    {0xd96c7de5, L"base64", XFA_AttributeEnum::Base64},
-    {0xd9f47f36, L"close", XFA_AttributeEnum::Close},
-    {0xdb075bde, L"host", XFA_AttributeEnum::Host},
-    {0xdb103411, L"global", XFA_AttributeEnum::Global},
-    {0xdb647188, L"blank", XFA_AttributeEnum::Blank},
-    {0xdb9be968, L"table", XFA_AttributeEnum::Table},
-    {0xdf590fbb, L"import", XFA_AttributeEnum::Import},
-    {0xe0e573fb, L"custom", XFA_AttributeEnum::Custom},
-    {0xe0ecc79a, L"middleLeft", XFA_AttributeEnum::MiddleLeft},
-    {0xe1452019, L"postExecute", XFA_AttributeEnum::PostExecute},
-    {0xe1911d98, L"radix", XFA_AttributeEnum::Radix},
-    {0xe25fa7b8, L"postOpen", XFA_AttributeEnum::PostOpen},
-    {0xe28dce7e, L"enter", XFA_AttributeEnum::Enter},
-    {0xe2c44de4, L"ignore", XFA_AttributeEnum::Ignore},
-    {0xe2cd8c61, L"lr-tb", XFA_AttributeEnum::Lr_tb},
-    {0xe2da8336, L"fantasy", XFA_AttributeEnum::Fantasy},
-    {0xe31d5396, L"italic", XFA_AttributeEnum::Italic},
-    {0xe7ada113, L"author", XFA_AttributeEnum::Author},
-    {0xe8e7cc18, L"toEdge", XFA_AttributeEnum::ToEdge},
-    {0xe97aa98b, L"choice", XFA_AttributeEnum::Choice},
-    {0xeafd2a38, L"disabled", XFA_AttributeEnum::Disabled},
-    {0xeb2b7972, L"crossHatch", XFA_AttributeEnum::CrossHatch},
-    {0xeb2db2d7, L"dataRef", XFA_AttributeEnum::DataRef},
-    {0xec35dc6e, L"dashDotDot", XFA_AttributeEnum::DashDotDot},
-    {0xef85d351, L"square", XFA_AttributeEnum::Square},
-    {0xf2102445, L"dynamic", XFA_AttributeEnum::Dynamic},
-    {0xf272c7be, L"manual", XFA_AttributeEnum::Manual},
-    {0xf2bbb64d, L"etched", XFA_AttributeEnum::Etched},
-    {0xf3b8fc6c, L"validationState", XFA_AttributeEnum::ValidationState},
-    {0xf42f2b81, L"cursive", XFA_AttributeEnum::Cursive},
-    {0xf54481d4, L"last", XFA_AttributeEnum::Last},
-    {0xf5ad782b, L"left", XFA_AttributeEnum::Left},
-    {0xf616da2e, L"link", XFA_AttributeEnum::Link},
-    {0xf6b4afb0, L"long", XFA_AttributeEnum::Long},
-    {0xf8636460, L"internationalCarrier",
-     XFA_AttributeEnum::InternationalCarrier},
-    {0xf9fb37ac, L"PDF1.3", XFA_AttributeEnum::PDF1_3},
-    {0xf9fb37af, L"PDF1.6", XFA_AttributeEnum::PDF1_6},
-    {0xfbce7f19, L"serif", XFA_AttributeEnum::Serif},
-    {0xfc82d695, L"postSave", XFA_AttributeEnum::PostSave},
-    {0xfcef86b5, L"ready", XFA_AttributeEnum::Ready},
-    {0xfd54fbb7, L"postSign", XFA_AttributeEnum::PostSign},
-    {0xfdc0aae2, L"Arabic", XFA_AttributeEnum::Arabic},
-    {0xfe06d2ca, L"error", XFA_AttributeEnum::Error},
-    {0xfefc4885, L"urlencoded", XFA_AttributeEnum::Urlencoded},
-    {0xff795ad2, L"lowered", XFA_AttributeEnum::Lowered},
-};
-const int32_t g_iXFAEnumCount =
-    sizeof(g_XFAEnumData) / sizeof(XFA_AttributeEnumInfo);
diff --git a/xfa/fxfa/parser/xfa_basic_data_unittest.cpp b/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
new file mode 100644
index 0000000..b9307d5
--- /dev/null
+++ b/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
@@ -0,0 +1,101 @@
+// Copyright 2018 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(XFABasicDataTest, GetPacketByName) {
+  Optional<XFA_PACKETINFO> result = XFA_GetPacketByName(L"");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetPacketByName(L"nonesuch");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetPacketByName(L"datasets");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_PacketType::Datasets, result.value().packet_type);
+
+  result = XFA_GetPacketByName(L"sourceSet");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_PacketType::SourceSet, result.value().packet_type);
+}
+
+TEST(XFABasicDataTest, PacketToName) {
+  XFA_PACKETINFO result = XFA_GetPacketByIndex(XFA_PacketType::Datasets);
+  EXPECT_STREQ(L"datasets", result.name);
+
+  result = XFA_GetPacketByIndex(XFA_PacketType::ConnectionSet);
+  EXPECT_STREQ(L"connectionSet", result.name);
+}
+
+TEST(XFABasicDataTest, GetElementByName) {
+  EXPECT_EQ(XFA_Element::Unknown, XFA_GetElementByName(L""));
+  EXPECT_EQ(XFA_Element::Unknown, XFA_GetElementByName(L"nonesuch"));
+  EXPECT_EQ(XFA_Element::ConnectionSet, XFA_GetElementByName(L"connectionSet"));
+  EXPECT_EQ(XFA_Element::Items, XFA_GetElementByName(L"items"));
+
+  // Internal elements are not retrievable by name.
+  EXPECT_EQ(XFA_Element::Unknown, XFA_GetElementByName(L"model"));
+}
+
+TEST(XFABasicDataTest, ElementToName) {
+  EXPECT_EQ("conformance", XFA_ElementToName(XFA_Element::Conformance));
+  EXPECT_EQ("tagged", XFA_ElementToName(XFA_Element::Tagged));
+
+  // Internal elements resolve back to real names.
+  EXPECT_EQ("node", XFA_ElementToName(XFA_Element::Node));
+}
+
+TEST(XFABasicDataTest, GetAttributeByName) {
+  Optional<XFA_ATTRIBUTEINFO> result = XFA_GetAttributeByName(L"");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetAttributeByName(L"nonesuch");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetAttributeByName(L"h");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_Attribute::H, result.value().attribute);
+
+  result = XFA_GetAttributeByName(L"short");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_Attribute::Short, result.value().attribute);
+
+  result = XFA_GetAttributeByName(L"decipherOnly");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_Attribute::DecipherOnly, result.value().attribute);
+}
+
+TEST(XFABasicDataTest, AttributeToName) {
+  EXPECT_EQ("spaceBelow", XFA_AttributeToName(XFA_Attribute::SpaceBelow));
+  EXPECT_EQ("decipherOnly", XFA_AttributeToName(XFA_Attribute::DecipherOnly));
+}
+
+TEST(XFABasicDataTest, GetAttributeValueByName) {
+  Optional<XFA_AttributeValue> result = XFA_GetAttributeValueByName(L"");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetAttributeValueByName(L"nonesuch");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetAttributeValueByName(L"*");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_AttributeValue::Asterisk, result.value());
+
+  result = XFA_GetAttributeValueByName(L"visible");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_AttributeValue::Visible, result.value());
+
+  result = XFA_GetAttributeValueByName(L"lowered");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_AttributeValue::Lowered, result.value());
+}
+
+TEST(XFABasicDataTest, AttributeValueToName) {
+  EXPECT_EQ("rl-tb", XFA_AttributeValueToName(XFA_AttributeValue::Rl_tb));
+  EXPECT_EQ("lowered", XFA_AttributeValueToName(XFA_AttributeValue::Lowered));
+}
diff --git a/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp b/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp
index f6f165f..dfd8cf0 100644
--- a/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp
+++ b/xfa/fxfa/parser/xfa_document_datamerger_imp.cpp
@@ -6,502 +6,11 @@
 
 #include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
 
-#include <map>
-#include <vector>
-
-#include "core/fxcrt/fx_extension.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
-#include "fxjs/cfxjse_engine.h"
-#include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
-#include "xfa/fxfa/parser/cxfa_bind.h"
-#include "xfa/fxfa/parser/cxfa_datagroup.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_exdata.h"
-#include "xfa/fxfa/parser/cxfa_form.h"
-#include "xfa/fxfa/parser/cxfa_image.h"
-#include "xfa/fxfa/parser/cxfa_items.h"
-#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
-#include "xfa/fxfa/parser/cxfa_occur.h"
-#include "xfa/fxfa/parser/cxfa_pageset.h"
-#include "xfa/fxfa/parser/cxfa_subform.h"
-#include "xfa/fxfa/parser/cxfa_template.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
-#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
-#include "xfa/fxfa/parser/cxfa_value.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
-#include "xfa/fxfa/parser/xfa_utils.h"
 
-namespace {
-
-class CXFA_TraverseStrategy_DDGroup {
- public:
-  static CXFA_Node* GetFirstChild(CXFA_Node* pDDGroupNode) {
-    return pDDGroupNode->GetFirstChildByName(XFA_HASHCODE_Group);
-  }
-  static CXFA_Node* GetNextSibling(CXFA_Node* pDDGroupNode) {
-    return pDDGroupNode->GetNextSameNameSibling(XFA_HASHCODE_Group);
-  }
-  static CXFA_Node* GetParent(CXFA_Node* pDDGroupNode) {
-    return pDDGroupNode->GetParent();
-  }
-};
-
-struct RecurseRecord {
-  CXFA_Node* pTemplateChild;
-  CXFA_Node* pDataChild;
-};
-
-CXFA_Node* FormValueNode_CreateChild(CXFA_Node* pValueNode, XFA_Element iType) {
-  CXFA_Node* pChildNode = pValueNode->GetFirstChild();
-  if (!pChildNode) {
-    if (iType == XFA_Element::Unknown)
-      return nullptr;
-
-    pChildNode =
-        pValueNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, iType);
-  }
-  return pChildNode;
-}
-
-void FormValueNode_MatchNoneCreateChild(CXFA_Node* pFormNode) {
-  CXFA_WidgetAcc* pWidgetAcc = pFormNode->GetWidgetAcc();
-  ASSERT(pWidgetAcc);
-  pWidgetAcc->GetUIType();
-}
-
-bool FormValueNode_SetChildContent(CXFA_Node* pValueNode,
-                                   const WideString& wsContent,
-                                   XFA_Element iType = XFA_Element::Unknown) {
-  if (!pValueNode)
-    return false;
-
-  ASSERT(pValueNode->GetPacketType() == XFA_PacketType::Form);
-  CXFA_Node* pChildNode = FormValueNode_CreateChild(pValueNode, iType);
-  if (!pChildNode)
-    return false;
-
-  switch (pChildNode->GetObjectType()) {
-    case XFA_ObjectType::ContentNode: {
-      CXFA_Node* pContentRawDataNode = pChildNode->GetFirstChild();
-      if (!pContentRawDataNode) {
-        XFA_Element element = XFA_Element::Sharptext;
-        if (pChildNode->GetElementType() == XFA_Element::ExData) {
-          Optional<WideString> contentType =
-              pChildNode->JSObject()->TryAttribute(XFA_Attribute::ContentType,
-                                                   false);
-          if (contentType) {
-            if (*contentType == L"text/html")
-              element = XFA_Element::SharpxHTML;
-            else if (*contentType == L"text/xml")
-              element = XFA_Element::Sharpxml;
-          }
-        }
-        pContentRawDataNode = pChildNode->CreateSamePacketNode(element);
-        pChildNode->InsertChild(pContentRawDataNode, nullptr);
-      }
-      pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
-                                                false, false);
-      break;
-    }
-    case XFA_ObjectType::NodeC:
-    case XFA_ObjectType::TextNode:
-    case XFA_ObjectType::NodeV: {
-      pChildNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent, false,
-                                       false);
-      break;
-    }
-    default:
-      NOTREACHED();
-      break;
-  }
-  return true;
-}
-
-void CreateDataBinding(CXFA_Node* pFormNode,
-                       CXFA_Node* pDataNode,
-                       bool bDataToForm) {
-  pFormNode->SetBindingNode(pDataNode);
-  pDataNode->AddBindItem(pFormNode);
-  XFA_Element eType = pFormNode->GetElementType();
-  if (eType != XFA_Element::Field && eType != XFA_Element::ExclGroup)
-    return;
-
-  CXFA_WidgetAcc* pWidgetAcc = pFormNode->GetWidgetAcc();
-  ASSERT(pWidgetAcc);
-  XFA_Element eUIType = pWidgetAcc->GetUIType();
-  auto* defValue = pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
-      0, XFA_Element::Value);
-  if (!bDataToForm) {
-    WideString wsValue;
-    switch (eUIType) {
-      case XFA_Element::ImageEdit: {
-        CXFA_Image* image = defValue ? defValue->GetImageIfExists() : nullptr;
-        WideString wsContentType;
-        WideString wsHref;
-        if (image) {
-          wsValue = image->GetContent();
-          wsContentType = image->GetContentType();
-          wsHref = image->GetHref();
-        }
-        CFX_XMLElement* pXMLDataElement =
-            static_cast<CFX_XMLElement*>(pDataNode->GetXMLMappingNode());
-        ASSERT(pXMLDataElement);
-
-        pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pWidgetAcc->GetFormatDataValue(wsValue), false, false);
-        pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                        wsContentType, false, false);
-        if (!wsHref.IsEmpty())
-          pXMLDataElement->SetString(L"href", wsHref);
-
-        break;
-      }
-      case XFA_Element::ChoiceList:
-        wsValue = defValue ? defValue->GetChildValueContent() : L"";
-        if (pWidgetAcc->IsChoiceListMultiSelect()) {
-          std::vector<WideString> wsSelTextArray =
-              pWidgetAcc->GetSelectedItemsValue();
-          if (!wsSelTextArray.empty()) {
-            for (const auto& text : wsSelTextArray) {
-              CXFA_Node* pValue =
-                  pDataNode->CreateSamePacketNode(XFA_Element::DataValue);
-              pValue->JSObject()->SetCData(XFA_Attribute::Name, L"value", false,
-                                           false);
-              pValue->CreateXMLMappingNode();
-              pDataNode->InsertChild(pValue, nullptr);
-              pValue->JSObject()->SetCData(XFA_Attribute::Value, text, false,
-                                           false);
-            }
-          } else {
-            CFX_XMLNode* pXMLNode = pDataNode->GetXMLMappingNode();
-            ASSERT(pXMLNode->GetType() == FX_XMLNODE_Element);
-            static_cast<CFX_XMLElement*>(pXMLNode)->SetString(L"xfa:dataNode",
-                                                              L"dataGroup");
-          }
-        } else if (!wsValue.IsEmpty()) {
-          pDataNode->JSObject()->SetAttributeValue(
-              wsValue, pWidgetAcc->GetFormatDataValue(wsValue), false, false);
-        }
-        break;
-      case XFA_Element::CheckButton:
-        wsValue = defValue ? defValue->GetChildValueContent() : L"";
-        if (wsValue.IsEmpty())
-          break;
-
-        pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pWidgetAcc->GetFormatDataValue(wsValue), false, false);
-        break;
-      case XFA_Element::ExclGroup: {
-        CXFA_Node* pChecked = nullptr;
-        CXFA_Node* pChild = pFormNode->GetFirstChild();
-        for (; pChild; pChild = pChild->GetNextSibling()) {
-          if (pChild->GetElementType() != XFA_Element::Field)
-            continue;
-
-          auto* pValue =
-              pChild->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
-          if (!pValue)
-            continue;
-
-          wsValue = pValue->GetChildValueContent();
-          if (wsValue.IsEmpty())
-            continue;
-
-          CXFA_Items* pItems =
-              pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-          if (!pItems)
-            continue;
-
-          CXFA_Node* pText = pItems->GetFirstChild();
-          if (!pText)
-            continue;
-
-          WideString wsContent = pText->JSObject()->GetContent(false);
-          if (wsContent == wsValue) {
-            pChecked = pChild;
-            pDataNode->JSObject()->SetAttributeValue(wsValue, wsValue, false,
-                                                     false);
-            pFormNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
-                                            false, false);
-            break;
-          }
-        }
-        if (!pChecked)
-          break;
-
-        pChild = pFormNode->GetFirstChild();
-        for (; pChild; pChild = pChild->GetNextSibling()) {
-          if (pChild == pChecked)
-            continue;
-          if (pChild->GetElementType() != XFA_Element::Field)
-            continue;
-
-          CXFA_Value* pValue =
-              pChild->JSObject()->GetOrCreateProperty<CXFA_Value>(
-                  0, XFA_Element::Value);
-          CXFA_Items* pItems =
-              pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
-          CXFA_Node* pText = pItems ? pItems->GetFirstChild() : nullptr;
-          if (pText)
-            pText = pText->GetNextSibling();
-
-          WideString wsContent;
-          if (pText)
-            wsContent = pText->JSObject()->GetContent(false);
-
-          FormValueNode_SetChildContent(pValue, wsContent, XFA_Element::Text);
-        }
-        break;
-      }
-      case XFA_Element::NumericEdit: {
-        wsValue = defValue ? defValue->GetChildValueContent() : L"";
-        if (wsValue.IsEmpty())
-          break;
-
-        wsValue = pWidgetAcc->NormalizeNumStr(wsValue);
-        pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pWidgetAcc->GetFormatDataValue(wsValue), false, false);
-        CXFA_Value* pValue =
-            pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
-                0, XFA_Element::Value);
-        FormValueNode_SetChildContent(pValue, wsValue, XFA_Element::Float);
-        break;
-      }
-      default:
-        wsValue = defValue ? defValue->GetChildValueContent() : L"";
-        if (wsValue.IsEmpty())
-          break;
-
-        pDataNode->JSObject()->SetAttributeValue(
-            wsValue, pWidgetAcc->GetFormatDataValue(wsValue), false, false);
-        break;
-    }
-    return;
-  }
-
-  WideString wsXMLValue = pDataNode->JSObject()->GetContent(false);
-  WideString wsNormalizeValue = pWidgetAcc->GetNormalizeDataValue(wsXMLValue);
-
-  pDataNode->JSObject()->SetAttributeValue(wsNormalizeValue, wsXMLValue, false,
-                                           false);
-  switch (eUIType) {
-    case XFA_Element::ImageEdit: {
-      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
-                                    XFA_Element::Image);
-      CXFA_Image* image = defValue ? defValue->GetImageIfExists() : nullptr;
-      if (image) {
-        CFX_XMLElement* pXMLDataElement =
-            static_cast<CFX_XMLElement*>(pDataNode->GetXMLMappingNode());
-        ASSERT(pXMLDataElement);
-
-        WideString wsContentType =
-            pXMLDataElement->GetString(L"xfa:contentType");
-        if (!wsContentType.IsEmpty()) {
-          pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                          wsContentType, false, false);
-          image->SetContentType(wsContentType);
-        }
-
-        WideString wsHref = pXMLDataElement->GetString(L"href");
-        if (!wsHref.IsEmpty())
-          image->SetHref(wsHref);
-      }
-      break;
-    }
-    case XFA_Element::ChoiceList:
-      if (pWidgetAcc->IsChoiceListMultiSelect()) {
-        std::vector<CXFA_Node*> items = pDataNode->GetNodeList(
-            XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties,
-            XFA_Element::Unknown);
-        if (!items.empty()) {
-          bool single = items.size() == 1;
-          wsNormalizeValue.clear();
-
-          for (CXFA_Node* pNode : items) {
-            WideString wsItem = pNode->JSObject()->GetContent(false);
-            if (single)
-              wsItem += L"\n";
-
-            wsNormalizeValue += wsItem;
-          }
-          CXFA_ExData* exData =
-              defValue ? defValue->GetExDataIfExists() : nullptr;
-          ASSERT(exData);
-
-          exData->SetContentType(single ? L"text/plain" : L"text/xml");
-        }
-        FormValueNode_SetChildContent(defValue, wsNormalizeValue,
-                                      XFA_Element::ExData);
-      } else {
-        FormValueNode_SetChildContent(defValue, wsNormalizeValue,
-                                      XFA_Element::Text);
-      }
-      break;
-    case XFA_Element::CheckButton:
-      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
-                                    XFA_Element::Text);
-      break;
-    case XFA_Element::ExclGroup: {
-      pWidgetAcc->SetSelectedMemberByValue(wsNormalizeValue.AsStringView(),
-                                           false, false, false);
-      break;
-    }
-    case XFA_Element::DateTimeEdit:
-      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
-                                    XFA_Element::DateTime);
-      break;
-    case XFA_Element::NumericEdit: {
-      WideString wsPicture =
-          pWidgetAcc->GetPictureContent(XFA_VALUEPICTURE_DataBind);
-      if (wsPicture.IsEmpty())
-        wsNormalizeValue = pWidgetAcc->NormalizeNumStr(wsNormalizeValue);
-
-      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
-                                    XFA_Element::Float);
-      break;
-    }
-    case XFA_Element::Barcode:
-    case XFA_Element::Button:
-    case XFA_Element::PasswordEdit:
-    case XFA_Element::Signature:
-    case XFA_Element::TextEdit:
-    default:
-      FormValueNode_SetChildContent(defValue, wsNormalizeValue,
-                                    XFA_Element::Text);
-      break;
-  }
-}
-
-CXFA_Node* GetGlobalBinding(CXFA_Document* pDocument, uint32_t dwNameHash) {
-  auto it = pDocument->m_rgGlobalBinding.find(dwNameHash);
-  return it != pDocument->m_rgGlobalBinding.end() ? it->second : nullptr;
-}
-
-void RegisterGlobalBinding(CXFA_Document* pDocument,
-                           uint32_t dwNameHash,
-                           CXFA_Node* pDataNode) {
-  pDocument->m_rgGlobalBinding[dwNameHash] = pDataNode;
-}
-
-CXFA_Node* ScopeMatchGlobalBinding(CXFA_Node* pDataScope,
-                                   uint32_t dwNameHash,
-                                   XFA_Element eMatchDataNodeType,
-                                   bool bUpLevel) {
-  for (CXFA_Node *pCurDataScope = pDataScope, *pLastDataScope = nullptr;
-       pCurDataScope &&
-       pCurDataScope->GetPacketType() == XFA_PacketType::Datasets;
-       pLastDataScope = pCurDataScope,
-                 pCurDataScope = pCurDataScope->GetParent()) {
-    for (CXFA_Node* pDataChild = pCurDataScope->GetFirstChildByName(dwNameHash);
-         pDataChild;
-         pDataChild = pDataChild->GetNextSameNameSibling(dwNameHash)) {
-      if (pDataChild == pLastDataScope ||
-          (eMatchDataNodeType != XFA_Element::DataModel &&
-           pDataChild->GetElementType() != eMatchDataNodeType) ||
-          pDataChild->HasBindItem()) {
-        continue;
-      }
-      return pDataChild;
-    }
-
-    for (CXFA_DataGroup* pDataChild =
-             pCurDataScope->GetFirstChildByClass<CXFA_DataGroup>(
-                 XFA_Element::DataGroup);
-         pDataChild;
-         pDataChild = pDataChild->GetNextSameClassSibling<CXFA_DataGroup>(
-             XFA_Element::DataGroup)) {
-      CXFA_Node* pDataNode = ScopeMatchGlobalBinding(pDataChild, dwNameHash,
-                                                     eMatchDataNodeType, false);
-      if (pDataNode)
-        return pDataNode;
-    }
-    if (!bUpLevel)
-      break;
-  }
-  return nullptr;
-}
-
-CXFA_Node* FindGlobalDataNode(CXFA_Document* pDocument,
-                              const WideString& wsName,
-                              CXFA_Node* pDataScope,
-                              XFA_Element eMatchNodeType) {
-  if (wsName.IsEmpty())
-    return nullptr;
-
-  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
-  CXFA_Node* pBounded = GetGlobalBinding(pDocument, dwNameHash);
-  if (!pBounded) {
-    pBounded =
-        ScopeMatchGlobalBinding(pDataScope, dwNameHash, eMatchNodeType, true);
-    if (pBounded)
-      RegisterGlobalBinding(pDocument, dwNameHash, pBounded);
-  }
-  return pBounded;
-}
-
-CXFA_Node* FindOnceDataNode(CXFA_Document* pDocument,
-                            const WideString& wsName,
-                            CXFA_Node* pDataScope,
-                            XFA_Element eMatchNodeType) {
-  if (wsName.IsEmpty())
-    return nullptr;
-
-  uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
-  CXFA_Node* pLastDataScope = nullptr;
-  for (CXFA_Node* pCurDataScope = pDataScope;
-       pCurDataScope &&
-       pCurDataScope->GetPacketType() == XFA_PacketType::Datasets;
-       pCurDataScope = pCurDataScope->GetParent()) {
-    for (CXFA_Node* pDataChild = pCurDataScope->GetFirstChildByName(dwNameHash);
-         pDataChild;
-         pDataChild = pDataChild->GetNextSameNameSibling(dwNameHash)) {
-      if (pDataChild == pLastDataScope || pDataChild->HasBindItem() ||
-          (eMatchNodeType != XFA_Element::DataModel &&
-           pDataChild->GetElementType() != eMatchNodeType)) {
-        continue;
-      }
-      return pDataChild;
-    }
-    pLastDataScope = pCurDataScope;
-  }
-  return nullptr;
-}
-
-CXFA_Node* FindDataRefDataNode(CXFA_Document* pDocument,
-                               const WideString& wsRef,
-                               CXFA_Node* pDataScope,
-                               XFA_Element eMatchNodeType,
-                               CXFA_Node* pTemplateNode,
-                               bool bForceBind,
-                               bool bUpLevel) {
-  uint32_t dFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_BindNew;
-  if (bUpLevel || wsRef != L"name")
-    dFlags |= (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
-
-  XFA_RESOLVENODE_RS rs;
-  pDocument->GetScriptContext()->ResolveObjects(
-      pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
-  if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeAll ||
-      rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeMidAll ||
-      rs.objects.size() > 1) {
-    return pDocument->GetNotBindNode(rs.objects);
-  }
-
-  if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
-    CXFA_Object* pObject = !rs.objects.empty() ? rs.objects.front() : nullptr;
-    CXFA_Node* pNode = ToNode(pObject);
-    return (bForceBind || !pNode || !pNode->HasBindItem()) ? pNode : nullptr;
-  }
-  return nullptr;
-}
-
-bool NeedGenerateForm(CXFA_Node* pTemplateChild, bool bUseInstanceManager) {
+bool XFA_DataMerge_NeedGenerateForm(CXFA_Node* pTemplateChild,
+                                    bool bUseInstanceManager) {
   XFA_Element eType = pTemplateChild->GetElementType();
   if (eType == XFA_Element::Variables)
     return true;
@@ -514,740 +23,6 @@
   return true;
 }
 
-CXFA_Node* CloneOrMergeInstanceManager(CXFA_Document* pDocument,
-                                       CXFA_Node* pFormParent,
-                                       CXFA_Node* pTemplateNode,
-                                       std::vector<CXFA_Node*>* subforms) {
-  WideString wsSubformName =
-      pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
-  WideString wsInstMgrNodeName = L"_" + wsSubformName;
-  uint32_t dwInstNameHash =
-      FX_HashCode_GetW(wsInstMgrNodeName.AsStringView(), false);
-  CXFA_Node* pExistingNode = XFA_DataMerge_FindFormDOMInstance(
-      pDocument, XFA_Element::InstanceManager, dwInstNameHash, pFormParent);
-  if (pExistingNode) {
-    uint32_t dwNameHash = pTemplateNode->GetNameHash();
-    for (CXFA_Node* pNode = pExistingNode->GetNextSibling(); pNode;) {
-      XFA_Element eCurType = pNode->GetElementType();
-      if (eCurType == XFA_Element::InstanceManager)
-        break;
-
-      if ((eCurType != XFA_Element::Subform) &&
-          (eCurType != XFA_Element::SubformSet)) {
-        pNode = pNode->GetNextSibling();
-        continue;
-      }
-      if (dwNameHash != pNode->GetNameHash())
-        break;
-
-      CXFA_Node* pNextNode = pNode->GetNextSibling();
-      pFormParent->RemoveChild(pNode, true);
-      subforms->push_back(pNode);
-      pNode = pNextNode;
-    }
-    pFormParent->RemoveChild(pExistingNode, true);
-    pFormParent->InsertChild(pExistingNode, nullptr);
-    pExistingNode->ClearFlag(XFA_NodeFlag_UnusedNode);
-    pExistingNode->SetTemplateNode(pTemplateNode);
-    return pExistingNode;
-  }
-
-  CXFA_Node* pNewNode =
-      pDocument->CreateNode(XFA_PacketType::Form, XFA_Element::InstanceManager);
-  wsInstMgrNodeName =
-      L"_" + pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
-  pNewNode->JSObject()->SetCData(XFA_Attribute::Name, wsInstMgrNodeName, false,
-                                 false);
-  pFormParent->InsertChild(pNewNode, nullptr);
-  pNewNode->SetTemplateNode(pTemplateNode);
-  return pNewNode;
-}
-
-CXFA_Node* FindMatchingDataNode(
-    CXFA_Document* pDocument,
-    CXFA_Node* pTemplateNode,
-    CXFA_Node* pDataScope,
-    bool& bAccessedDataDOM,
-    bool bForceBind,
-    CXFA_NodeIteratorTemplate<CXFA_Node,
-                              CXFA_TraverseStrategy_XFAContainerNode>*
-        pIterator,
-    bool& bSelfMatch,
-    XFA_AttributeEnum& eBindMatch,
-    bool bUpLevel) {
-  CXFA_Node* pResult = nullptr;
-  CXFA_Node* pCurTemplateNode = pIterator->GetCurrent();
-  while (pCurTemplateNode) {
-    XFA_Element eMatchNodeType;
-    switch (pCurTemplateNode->GetElementType()) {
-      case XFA_Element::Subform:
-        eMatchNodeType = XFA_Element::DataGroup;
-        break;
-      case XFA_Element::Field: {
-        eMatchNodeType = XFA_FieldIsMultiListBox(pCurTemplateNode)
-                             ? XFA_Element::DataGroup
-                             : XFA_Element::DataValue;
-      } break;
-      case XFA_Element::ExclGroup:
-        eMatchNodeType = XFA_Element::DataValue;
-        break;
-      default:
-        pCurTemplateNode = pIterator->MoveToNext();
-        continue;
-    }
-
-    CXFA_Occur* pTemplateNodeOccur =
-        pCurTemplateNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
-    if (pTemplateNodeOccur) {
-      int32_t iMin;
-      int32_t iMax;
-      int32_t iInit;
-      std::tie(iMin, iMax, iInit) = pTemplateNodeOccur->GetOccurInfo();
-      if (iMax == 0) {
-        pCurTemplateNode = pIterator->MoveToNext();
-        continue;
-      }
-    }
-
-    CXFA_Bind* pTemplateNodeBind =
-        pCurTemplateNode->GetFirstChildByClass<CXFA_Bind>(XFA_Element::Bind);
-    XFA_AttributeEnum eMatch =
-        pTemplateNodeBind
-            ? pTemplateNodeBind->JSObject()->GetEnum(XFA_Attribute::Match)
-            : XFA_AttributeEnum::Once;
-    eBindMatch = eMatch;
-    switch (eMatch) {
-      case XFA_AttributeEnum::None:
-        pCurTemplateNode = pIterator->MoveToNext();
-        continue;
-      case XFA_AttributeEnum::Global:
-        bAccessedDataDOM = true;
-        if (!bForceBind) {
-          pCurTemplateNode = pIterator->MoveToNext();
-          continue;
-        }
-        if (eMatchNodeType == XFA_Element::DataValue ||
-            (eMatchNodeType == XFA_Element::DataGroup &&
-             XFA_FieldIsMultiListBox(pTemplateNodeBind))) {
-          CXFA_Node* pGlobalBindNode = FindGlobalDataNode(
-              pDocument,
-              pCurTemplateNode->JSObject()->GetCData(XFA_Attribute::Name),
-              pDataScope, eMatchNodeType);
-          if (!pGlobalBindNode) {
-            pCurTemplateNode = pIterator->MoveToNext();
-            continue;
-          }
-          pResult = pGlobalBindNode;
-          break;
-        }
-      case XFA_AttributeEnum::Once: {
-        bAccessedDataDOM = true;
-        CXFA_Node* pOnceBindNode = FindOnceDataNode(
-            pDocument,
-            pCurTemplateNode->JSObject()->GetCData(XFA_Attribute::Name),
-            pDataScope, eMatchNodeType);
-        if (!pOnceBindNode) {
-          pCurTemplateNode = pIterator->MoveToNext();
-          continue;
-        }
-        pResult = pOnceBindNode;
-        break;
-      }
-      case XFA_AttributeEnum::DataRef: {
-        bAccessedDataDOM = true;
-        CXFA_Node* pDataRefBindNode = FindDataRefDataNode(
-            pDocument,
-            pTemplateNodeBind->JSObject()->GetCData(XFA_Attribute::Ref),
-            pDataScope, eMatchNodeType, pTemplateNode, bForceBind, bUpLevel);
-        if (pDataRefBindNode &&
-            pDataRefBindNode->GetElementType() == eMatchNodeType) {
-          pResult = pDataRefBindNode;
-        }
-        if (!pResult) {
-          pCurTemplateNode = pIterator->SkipChildrenAndMoveToNext();
-          continue;
-        }
-        break;
-      }
-      default:
-        break;
-    }
-    if (pCurTemplateNode == pTemplateNode && pResult)
-      bSelfMatch = true;
-    break;
-  }
-  return pResult;
-}
-
-void SortRecurseRecord(std::vector<RecurseRecord>* rgRecords,
-                       CXFA_Node* pDataScope,
-                       bool bChoiceMode) {
-  std::vector<RecurseRecord> rgResultRecord;
-  for (CXFA_Node* pNode = pDataScope->GetFirstChild(); pNode;
-       pNode = pNode->GetNextSibling()) {
-    auto it = std::find_if(rgRecords->begin(), rgRecords->end(),
-                           [pNode](const RecurseRecord& record) {
-                             return pNode == record.pDataChild;
-                           });
-    if (it != rgRecords->end()) {
-      rgResultRecord.push_back(*it);
-      rgRecords->erase(it);
-      if (bChoiceMode)
-        break;
-    }
-  }
-  if (rgResultRecord.empty())
-    return;
-
-  if (!bChoiceMode) {
-    rgResultRecord.insert(rgResultRecord.end(), rgRecords->begin(),
-                          rgRecords->end());
-  }
-  *rgRecords = rgResultRecord;
-}
-
-CXFA_Node* CopyContainer_SubformSet(CXFA_Document* pDocument,
-                                    CXFA_Node* pTemplateNode,
-                                    CXFA_Node* pFormParentNode,
-                                    CXFA_Node* pDataScope,
-                                    bool bOneInstance,
-                                    bool bDataMerge) {
-  XFA_Element eType = pTemplateNode->GetElementType();
-  CXFA_Node* pOccurNode = nullptr;
-  CXFA_Node* pFirstInstance = nullptr;
-  bool bUseInstanceManager =
-      pFormParentNode->GetElementType() != XFA_Element::Area;
-  CXFA_Node* pInstMgrNode = nullptr;
-  std::vector<CXFA_Node*> subformArray;
-  std::vector<CXFA_Node*>* pSearchArray = nullptr;
-  if (!bOneInstance &&
-      (eType == XFA_Element::SubformSet || eType == XFA_Element::Subform)) {
-    pInstMgrNode = bUseInstanceManager ? CloneOrMergeInstanceManager(
-                                             pDocument, pFormParentNode,
-                                             pTemplateNode, &subformArray)
-                                       : nullptr;
-    if (CXFA_Occur* pOccurTemplateNode =
-            pTemplateNode->GetFirstChildByClass<CXFA_Occur>(
-                XFA_Element::Occur)) {
-      pOccurNode = pInstMgrNode ? XFA_NodeMerge_CloneOrMergeContainer(
-                                      pDocument, pInstMgrNode,
-                                      pOccurTemplateNode, false, nullptr)
-                                : pOccurTemplateNode;
-    } else if (pInstMgrNode) {
-      pOccurNode =
-          pInstMgrNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
-      if (pOccurNode)
-        pOccurNode->ClearFlag(XFA_NodeFlag_UnusedNode);
-    }
-    if (pInstMgrNode) {
-      pInstMgrNode->SetFlag(XFA_NodeFlag_Initialized, true);
-      pSearchArray = &subformArray;
-      if (pFormParentNode->GetElementType() == XFA_Element::PageArea) {
-        bOneInstance = true;
-        if (subformArray.empty())
-          pSearchArray = nullptr;
-      } else if (pTemplateNode->GetNameHash() == 0 && subformArray.empty()) {
-        pSearchArray = nullptr;
-      }
-    }
-  }
-
-  int32_t iMax = 1;
-  int32_t iInit = 1;
-  int32_t iMin = 1;
-  if (!bOneInstance && pOccurNode) {
-    std::tie(iMin, iMax, iInit) =
-        static_cast<CXFA_Occur*>(pOccurNode)->GetOccurInfo();
-  }
-
-  XFA_AttributeEnum eRelation =
-      eType == XFA_Element::SubformSet
-          ? pTemplateNode->JSObject()->GetEnum(XFA_Attribute::Relation)
-          : XFA_AttributeEnum::Ordered;
-  int32_t iCurRepeatIndex = 0;
-  XFA_AttributeEnum eParentBindMatch = XFA_AttributeEnum::None;
-  if (bDataMerge) {
-    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
-        sNodeIterator(pTemplateNode);
-    bool bAccessedDataDOM = false;
-    if (eType == XFA_Element::SubformSet || eType == XFA_Element::Area) {
-      sNodeIterator.MoveToNext();
-    } else {
-      std::map<CXFA_Node*, CXFA_Node*> subformMapArray;
-      std::vector<CXFA_Node*> nodeArray;
-      for (; iMax < 0 || iCurRepeatIndex < iMax; iCurRepeatIndex++) {
-        bool bSelfMatch = false;
-        XFA_AttributeEnum eBindMatch = XFA_AttributeEnum::None;
-        CXFA_Node* pDataNode = FindMatchingDataNode(
-            pDocument, pTemplateNode, pDataScope, bAccessedDataDOM, false,
-            &sNodeIterator, bSelfMatch, eBindMatch, true);
-        if (!pDataNode || sNodeIterator.GetCurrent() != pTemplateNode)
-          break;
-
-        eParentBindMatch = eBindMatch;
-        CXFA_Node* pSubformNode = XFA_NodeMerge_CloneOrMergeContainer(
-            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-        if (!pFirstInstance)
-          pFirstInstance = pSubformNode;
-
-        CreateDataBinding(pSubformNode, pDataNode, true);
-        ASSERT(pSubformNode);
-        subformMapArray[pSubformNode] = pDataNode;
-        nodeArray.push_back(pSubformNode);
-      }
-
-      for (CXFA_Node* pSubform : nodeArray) {
-        CXFA_Node* pDataNode = nullptr;
-        auto it = subformMapArray.find(pSubform);
-        if (it != subformMapArray.end())
-          pDataNode = it->second;
-        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
-             pTemplateChild;
-             pTemplateChild = pTemplateChild->GetNextSibling()) {
-          if (NeedGenerateForm(pTemplateChild, bUseInstanceManager)) {
-            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubform,
-                                                pTemplateChild, true, nullptr);
-          } else if (pTemplateChild->IsContainerNode()) {
-            pDocument->DataMerge_CopyContainer(pTemplateChild, pSubform,
-                                               pDataNode, false, true, false);
-          }
-        }
-      }
-      subformMapArray.clear();
-    }
-
-    for (; iMax < 0 || iCurRepeatIndex < iMax; iCurRepeatIndex++) {
-      bool bSelfMatch = false;
-      XFA_AttributeEnum eBindMatch = XFA_AttributeEnum::None;
-      if (!FindMatchingDataNode(pDocument, pTemplateNode, pDataScope,
-                                bAccessedDataDOM, false, &sNodeIterator,
-                                bSelfMatch, eBindMatch, true)) {
-        break;
-      }
-      if (eBindMatch == XFA_AttributeEnum::DataRef &&
-          eParentBindMatch == XFA_AttributeEnum::DataRef) {
-        break;
-      }
-
-      if (eRelation == XFA_AttributeEnum::Choice ||
-          eRelation == XFA_AttributeEnum::Unordered) {
-        CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
-            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-        ASSERT(pSubformSetNode);
-        if (!pFirstInstance)
-          pFirstInstance = pSubformSetNode;
-
-        std::vector<RecurseRecord> rgItemMatchList;
-        std::vector<CXFA_Node*> rgItemUnmatchList;
-        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
-             pTemplateChild;
-             pTemplateChild = pTemplateChild->GetNextSibling()) {
-          if (NeedGenerateForm(pTemplateChild, bUseInstanceManager)) {
-            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
-                                                pTemplateChild, true, nullptr);
-          } else if (pTemplateChild->IsContainerNode()) {
-            bSelfMatch = false;
-            eBindMatch = XFA_AttributeEnum::None;
-            if (eRelation != XFA_AttributeEnum::Ordered) {
-              CXFA_NodeIteratorTemplate<CXFA_Node,
-                                        CXFA_TraverseStrategy_XFAContainerNode>
-                  sChildIter(pTemplateChild);
-              CXFA_Node* pDataMatch = FindMatchingDataNode(
-                  pDocument, pTemplateChild, pDataScope, bAccessedDataDOM,
-                  false, &sChildIter, bSelfMatch, eBindMatch, true);
-              if (pDataMatch) {
-                RecurseRecord sNewRecord = {pTemplateChild, pDataMatch};
-                if (bSelfMatch)
-                  rgItemMatchList.insert(rgItemMatchList.begin(), sNewRecord);
-                else
-                  rgItemMatchList.push_back(sNewRecord);
-              } else {
-                rgItemUnmatchList.push_back(pTemplateChild);
-              }
-            } else {
-              rgItemUnmatchList.push_back(pTemplateChild);
-            }
-          }
-        }
-
-        switch (eRelation) {
-          case XFA_AttributeEnum::Choice: {
-            ASSERT(!rgItemMatchList.empty());
-            SortRecurseRecord(&rgItemMatchList, pDataScope, true);
-            pDocument->DataMerge_CopyContainer(
-                rgItemMatchList.front().pTemplateChild, pSubformSetNode,
-                pDataScope, false, true, true);
-            break;
-          }
-          case XFA_AttributeEnum::Unordered: {
-            if (!rgItemMatchList.empty()) {
-              SortRecurseRecord(&rgItemMatchList, pDataScope, false);
-              for (const auto& matched : rgItemMatchList) {
-                pDocument->DataMerge_CopyContainer(matched.pTemplateChild,
-                                                   pSubformSetNode, pDataScope,
-                                                   false, true, true);
-              }
-            }
-            for (auto* unmatched : rgItemUnmatchList) {
-              pDocument->DataMerge_CopyContainer(unmatched, pSubformSetNode,
-                                                 pDataScope, false, true, true);
-            }
-            break;
-          }
-          default:
-            break;
-        }
-      } else {
-        CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
-            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-        ASSERT(pSubformSetNode);
-        if (!pFirstInstance)
-          pFirstInstance = pSubformSetNode;
-
-        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
-             pTemplateChild;
-             pTemplateChild = pTemplateChild->GetNextSibling()) {
-          if (NeedGenerateForm(pTemplateChild, bUseInstanceManager)) {
-            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
-                                                pTemplateChild, true, nullptr);
-          } else if (pTemplateChild->IsContainerNode()) {
-            pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformSetNode,
-                                               pDataScope, false, true, true);
-          }
-        }
-      }
-    }
-
-    if (iCurRepeatIndex == 0 && bAccessedDataDOM == false) {
-      int32_t iLimit = iMax;
-      if (pInstMgrNode && pTemplateNode->GetNameHash() == 0) {
-        iLimit = pdfium::CollectionSize<int32_t>(subformArray);
-        if (iLimit < iMin)
-          iLimit = iInit;
-      }
-
-      for (; (iLimit < 0 || iCurRepeatIndex < iLimit); iCurRepeatIndex++) {
-        if (pInstMgrNode) {
-          if (pSearchArray && pSearchArray->empty()) {
-            if (pTemplateNode->GetNameHash() != 0)
-              break;
-            pSearchArray = nullptr;
-          }
-        } else if (!XFA_DataMerge_FindFormDOMInstance(
-                       pDocument, pTemplateNode->GetElementType(),
-                       pTemplateNode->GetNameHash(), pFormParentNode)) {
-          break;
-        }
-        CXFA_Node* pSubformNode = XFA_NodeMerge_CloneOrMergeContainer(
-            pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-        ASSERT(pSubformNode);
-        if (!pFirstInstance)
-          pFirstInstance = pSubformNode;
-
-        for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
-             pTemplateChild;
-             pTemplateChild = pTemplateChild->GetNextSibling()) {
-          if (NeedGenerateForm(pTemplateChild, bUseInstanceManager)) {
-            XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformNode,
-                                                pTemplateChild, true, nullptr);
-          } else if (pTemplateChild->IsContainerNode()) {
-            pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformNode,
-                                               pDataScope, false, true, true);
-          }
-        }
-      }
-    }
-  }
-
-  int32_t iMinimalLimit = iCurRepeatIndex == 0 ? iInit : iMin;
-  for (; iCurRepeatIndex < iMinimalLimit; iCurRepeatIndex++) {
-    CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
-        pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
-    ASSERT(pSubformSetNode);
-    if (!pFirstInstance)
-      pFirstInstance = pSubformSetNode;
-
-    bool bFound = false;
-    for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
-         pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
-      if (NeedGenerateForm(pTemplateChild, bUseInstanceManager)) {
-        XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
-                                            pTemplateChild, true, nullptr);
-      } else if (pTemplateChild->IsContainerNode()) {
-        if (bFound && eRelation == XFA_AttributeEnum::Choice)
-          continue;
-
-        pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformSetNode,
-                                           pDataScope, false, bDataMerge, true);
-        bFound = true;
-      }
-    }
-  }
-  return pFirstInstance;
-}
-
-CXFA_Node* CopyContainer_Field(CXFA_Document* pDocument,
-                               CXFA_Node* pTemplateNode,
-                               CXFA_Node* pFormNode,
-                               CXFA_Node* pDataScope,
-                               bool bDataMerge,
-                               bool bUpLevel) {
-  CXFA_Node* pFieldNode = XFA_NodeMerge_CloneOrMergeContainer(
-      pDocument, pFormNode, pTemplateNode, false, nullptr);
-  ASSERT(pFieldNode);
-  for (CXFA_Node* pTemplateChildNode = pTemplateNode->GetFirstChild();
-       pTemplateChildNode;
-       pTemplateChildNode = pTemplateChildNode->GetNextSibling()) {
-    if (NeedGenerateForm(pTemplateChildNode, true)) {
-      XFA_NodeMerge_CloneOrMergeContainer(pDocument, pFieldNode,
-                                          pTemplateChildNode, true, nullptr);
-    } else if (pTemplateNode->GetElementType() == XFA_Element::ExclGroup &&
-               pTemplateChildNode->IsContainerNode()) {
-      if (pTemplateChildNode->GetElementType() == XFA_Element::Field) {
-        CopyContainer_Field(pDocument, pTemplateChildNode, pFieldNode, nullptr,
-                            false, true);
-      }
-    }
-  }
-  if (bDataMerge) {
-    bool bAccessedDataDOM = false;
-    bool bSelfMatch = false;
-    XFA_AttributeEnum eBindMatch;
-    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
-        sNodeIter(pTemplateNode);
-    CXFA_Node* pDataNode = FindMatchingDataNode(
-        pDocument, pTemplateNode, pDataScope, bAccessedDataDOM, true,
-        &sNodeIter, bSelfMatch, eBindMatch, bUpLevel);
-    if (pDataNode)
-      CreateDataBinding(pFieldNode, pDataNode, true);
-  } else {
-    FormValueNode_MatchNoneCreateChild(pFieldNode);
-  }
-  return pFieldNode;
-}
-
-CXFA_Node* MaybeCreateDataNode(CXFA_Document* pDocument,
-                               CXFA_Node* pDataParent,
-                               XFA_Element eNodeType,
-                               const WideString& wsName) {
-  if (!pDataParent)
-    return nullptr;
-
-  CXFA_Node* pParentDDNode = pDataParent->GetDataDescriptionNode();
-  if (!pParentDDNode) {
-    CXFA_Node* pDataNode =
-        pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
-    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
-    pDataNode->CreateXMLMappingNode();
-    pDataParent->InsertChild(pDataNode, nullptr);
-    pDataNode->SetFlag(XFA_NodeFlag_Initialized, false);
-    return pDataNode;
-  }
-
-  CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_DDGroup> sIterator(
-      pParentDDNode);
-  for (CXFA_Node* pDDGroupNode = sIterator.GetCurrent(); pDDGroupNode;
-       pDDGroupNode = sIterator.MoveToNext()) {
-    if (pDDGroupNode != pParentDDNode) {
-      if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
-        continue;
-
-      Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
-      if (!ns || *ns != L"http://ns.adobe.com/data-description/")
-        continue;
-    }
-
-    CXFA_Node* pDDNode =
-        pDDGroupNode->GetFirstChildByName(wsName.AsStringView());
-    if (!pDDNode)
-      continue;
-    if (pDDNode->GetElementType() != eNodeType)
-      break;
-
-    CXFA_Node* pDataNode =
-        pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
-    pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
-    pDataNode->CreateXMLMappingNode();
-    if (eNodeType == XFA_Element::DataValue &&
-        pDDNode->JSObject()->GetEnum(XFA_Attribute::Contains) ==
-            XFA_AttributeEnum::MetaData) {
-      pDataNode->JSObject()->SetEnum(XFA_Attribute::Contains,
-                                     XFA_AttributeEnum::MetaData, false);
-    }
-    pDataParent->InsertChild(pDataNode, nullptr);
-    pDataNode->SetDataDescriptionNode(pDDNode);
-    pDataNode->SetFlag(XFA_NodeFlag_Initialized, false);
-    return pDataNode;
-  }
-  return nullptr;
-}
-
-void UpdateBindingRelations(CXFA_Document* pDocument,
-                            CXFA_Node* pFormNode,
-                            CXFA_Node* pDataScope,
-                            bool bDataRef,
-                            bool bParentDataRef) {
-  bool bMatchRef = true;
-  XFA_Element eType = pFormNode->GetElementType();
-  CXFA_Node* pDataNode = pFormNode->GetBindData();
-  if (eType == XFA_Element::Subform || eType == XFA_Element::ExclGroup ||
-      eType == XFA_Element::Field) {
-    CXFA_Node* pTemplateNode = pFormNode->GetTemplateNodeIfExists();
-    CXFA_Bind* pTemplateNodeBind =
-        pTemplateNode
-            ? pTemplateNode->GetFirstChildByClass<CXFA_Bind>(XFA_Element::Bind)
-            : nullptr;
-    XFA_AttributeEnum eMatch =
-        pTemplateNodeBind
-            ? pTemplateNodeBind->JSObject()->GetEnum(XFA_Attribute::Match)
-            : XFA_AttributeEnum::Once;
-    switch (eMatch) {
-      case XFA_AttributeEnum::None:
-        if (!bDataRef || bParentDataRef)
-          FormValueNode_MatchNoneCreateChild(pFormNode);
-        break;
-      case XFA_AttributeEnum::Once:
-        if (!bDataRef || bParentDataRef) {
-          if (!pDataNode) {
-            if (pFormNode->GetNameHash() != 0 &&
-                pFormNode->JSObject()->GetEnum(XFA_Attribute::Scope) !=
-                    XFA_AttributeEnum::None) {
-              XFA_Element eDataNodeType = (eType == XFA_Element::Subform ||
-                                           XFA_FieldIsMultiListBox(pFormNode))
-                                              ? XFA_Element::DataGroup
-                                              : XFA_Element::DataValue;
-              pDataNode = MaybeCreateDataNode(
-                  pDocument, pDataScope, eDataNodeType,
-                  WideString(
-                      pFormNode->JSObject()->GetCData(XFA_Attribute::Name)));
-              if (pDataNode)
-                CreateDataBinding(pFormNode, pDataNode, false);
-            }
-            if (!pDataNode)
-              FormValueNode_MatchNoneCreateChild(pFormNode);
-
-          } else {
-            CXFA_Node* pDataParent = pDataNode->GetParent();
-            if (pDataParent != pDataScope) {
-              ASSERT(pDataParent);
-              pDataParent->RemoveChild(pDataNode, true);
-              pDataScope->InsertChild(pDataNode, nullptr);
-            }
-          }
-        }
-        break;
-      case XFA_AttributeEnum::Global:
-        if (!bDataRef || bParentDataRef) {
-          uint32_t dwNameHash = pFormNode->GetNameHash();
-          if (dwNameHash != 0 && !pDataNode) {
-            pDataNode = GetGlobalBinding(pDocument, dwNameHash);
-            if (!pDataNode) {
-              XFA_Element eDataNodeType = (eType == XFA_Element::Subform ||
-                                           XFA_FieldIsMultiListBox(pFormNode))
-                                              ? XFA_Element::DataGroup
-                                              : XFA_Element::DataValue;
-              CXFA_Node* pRecordNode =
-                  ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Record));
-              pDataNode = MaybeCreateDataNode(
-                  pDocument, pRecordNode, eDataNodeType,
-                  WideString(
-                      pFormNode->JSObject()->GetCData(XFA_Attribute::Name)));
-              if (pDataNode) {
-                CreateDataBinding(pFormNode, pDataNode, false);
-                RegisterGlobalBinding(pDocument, pFormNode->GetNameHash(),
-                                      pDataNode);
-              }
-            } else {
-              CreateDataBinding(pFormNode, pDataNode, true);
-            }
-          }
-          if (!pDataNode)
-            FormValueNode_MatchNoneCreateChild(pFormNode);
-        }
-        break;
-      case XFA_AttributeEnum::DataRef: {
-        bMatchRef = bDataRef;
-        bParentDataRef = true;
-        if (!pDataNode && bDataRef) {
-          WideString wsRef =
-              pTemplateNodeBind
-                  ? pTemplateNodeBind->JSObject()->GetCData(XFA_Attribute::Ref)
-                  : L"";
-          uint32_t dFlags =
-              XFA_RESOLVENODE_Children | XFA_RESOLVENODE_CreateNode;
-          XFA_RESOLVENODE_RS rs;
-          pDocument->GetScriptContext()->ResolveObjects(
-              pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
-          CXFA_Object* pObject =
-              !rs.objects.empty() ? rs.objects.front() : nullptr;
-          pDataNode = ToNode(pObject);
-          if (pDataNode) {
-            CreateDataBinding(pFormNode, pDataNode,
-                              rs.dwFlags == XFA_ResolveNode_RSType_ExistNodes);
-          } else {
-            FormValueNode_MatchNoneCreateChild(pFormNode);
-          }
-        }
-        break;
-      }
-      default:
-        break;
-    }
-  }
-
-  if (bMatchRef &&
-      (eType == XFA_Element::Subform || eType == XFA_Element::SubformSet ||
-       eType == XFA_Element::Area || eType == XFA_Element::PageArea ||
-       eType == XFA_Element::PageSet)) {
-    for (CXFA_Node* pFormChild = pFormNode->GetFirstChild(); pFormChild;
-         pFormChild = pFormChild->GetNextSibling()) {
-      if (!pFormChild->IsContainerNode())
-        continue;
-      if (pFormChild->IsUnusedNode())
-        continue;
-
-      UpdateBindingRelations(pDocument, pFormChild,
-                             pDataNode ? pDataNode : pDataScope, bDataRef,
-                             bParentDataRef);
-    }
-  }
-}
-
-void UpdateDataRelation(CXFA_Node* pDataNode, CXFA_Node* pDataDescriptionNode) {
-  ASSERT(pDataDescriptionNode);
-  for (CXFA_Node* pDataChild = pDataNode->GetFirstChild(); pDataChild;
-       pDataChild = pDataChild->GetNextSibling()) {
-    uint32_t dwNameHash = pDataChild->GetNameHash();
-    if (!dwNameHash)
-      continue;
-
-    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_DDGroup>
-        sIterator(pDataDescriptionNode);
-    for (CXFA_Node* pDDGroupNode = sIterator.GetCurrent(); pDDGroupNode;
-         pDDGroupNode = sIterator.MoveToNext()) {
-      if (pDDGroupNode != pDataDescriptionNode) {
-        if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
-          continue;
-
-        Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
-        if (!ns || *ns != L"http://ns.adobe.com/data-description/")
-          continue;
-      }
-
-      CXFA_Node* pDDNode = pDDGroupNode->GetFirstChildByName(dwNameHash);
-      if (!pDDNode)
-        continue;
-      if (pDDNode->GetElementType() != pDataChild->GetElementType())
-        break;
-
-      pDataChild->SetDataDescriptionNode(pDDNode);
-      UpdateDataRelation(pDataChild, pDDNode);
-      break;
-    }
-  }
-}
-
-}  // namespace
 
 CXFA_Node* XFA_DataMerge_FindFormDOMInstance(CXFA_Document* pDocument,
                                              XFA_Element eType,
@@ -1280,34 +55,34 @@
   }
   if (pExistingNode) {
     if (pSubformArray) {
-      pFormParent->InsertChild(pExistingNode, nullptr);
+      pFormParent->InsertChildAndNotify(pExistingNode, nullptr);
     } else if (pExistingNode->IsContainerNode()) {
-      pFormParent->RemoveChild(pExistingNode, true);
-      pFormParent->InsertChild(pExistingNode, nullptr);
+      pFormParent->RemoveChildAndNotify(pExistingNode, true);
+      pFormParent->InsertChildAndNotify(pExistingNode, nullptr);
     }
     pExistingNode->ClearFlag(XFA_NodeFlag_UnusedNode);
     pExistingNode->SetTemplateNode(pTemplateNode);
     if (bRecursive && pExistingNode->GetElementType() != XFA_Element::Items) {
       for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
            pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
-        if (NeedGenerateForm(pTemplateChild, true)) {
+        if (XFA_DataMerge_NeedGenerateForm(pTemplateChild, true)) {
           XFA_NodeMerge_CloneOrMergeContainer(
               pDocument, pExistingNode, pTemplateChild, bRecursive, nullptr);
         }
       }
     }
-    pExistingNode->SetFlag(XFA_NodeFlag_Initialized, true);
+    pExistingNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
     return pExistingNode;
   }
 
   CXFA_Node* pNewNode = pTemplateNode->CloneTemplateToForm(false);
-  pFormParent->InsertChild(pNewNode, nullptr);
+  pFormParent->InsertChildAndNotify(pNewNode, nullptr);
   if (bRecursive) {
     for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
          pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
-      if (NeedGenerateForm(pTemplateChild, true)) {
+      if (XFA_DataMerge_NeedGenerateForm(pTemplateChild, true)) {
         CXFA_Node* pNewChild = pTemplateChild->CloneTemplateToForm(true);
-        pNewNode->InsertChild(pNewChild, nullptr);
+        pNewNode->InsertChildAndNotify(pNewChild, nullptr);
       }
     }
   }
@@ -1315,6 +90,9 @@
 }
 
 CXFA_Node* XFA_DataMerge_FindDataScope(CXFA_Node* pParentFormNode) {
+  if (!pParentFormNode)
+    return nullptr;
+
   for (CXFA_Node* pRootBoundNode = pParentFormNode;
        pRootBoundNode && pRootBoundNode->IsContainerNode();
        pRootBoundNode = pRootBoundNode->GetParent()) {
@@ -1326,227 +104,3 @@
       pParentFormNode->GetDocument()->GetXFAObject(XFA_HASHCODE_Data));
 }
 
-CXFA_Node* CXFA_Document::DataMerge_CopyContainer(CXFA_Node* pTemplateNode,
-                                                  CXFA_Node* pFormNode,
-                                                  CXFA_Node* pDataScope,
-                                                  bool bOneInstance,
-                                                  bool bDataMerge,
-                                                  bool bUpLevel) {
-  switch (pTemplateNode->GetElementType()) {
-    case XFA_Element::SubformSet:
-    case XFA_Element::Subform:
-    case XFA_Element::Area:
-    case XFA_Element::PageArea:
-      return CopyContainer_SubformSet(this, pTemplateNode, pFormNode,
-                                      pDataScope, bOneInstance, bDataMerge);
-    case XFA_Element::ExclGroup:
-    case XFA_Element::Field:
-    case XFA_Element::Draw:
-    case XFA_Element::ContentArea:
-      return CopyContainer_Field(this, pTemplateNode, pFormNode, pDataScope,
-                                 bDataMerge, bUpLevel);
-    case XFA_Element::PageSet:
-    case XFA_Element::Variables:
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  return nullptr;
-}
-
-void CXFA_Document::DataMerge_UpdateBindingRelations(
-    CXFA_Node* pFormUpdateRoot) {
-  CXFA_Node* pDataScope =
-      XFA_DataMerge_FindDataScope(pFormUpdateRoot->GetParent());
-  if (!pDataScope)
-    return;
-
-  UpdateBindingRelations(this, pFormUpdateRoot, pDataScope, false, false);
-  UpdateBindingRelations(this, pFormUpdateRoot, pDataScope, true, false);
-}
-
-CXFA_Node* CXFA_Document::GetNotBindNode(
-    const std::vector<CXFA_Object*>& arrayObjects) {
-  for (CXFA_Object* pObject : arrayObjects) {
-    CXFA_Node* pNode = pObject->AsNode();
-    if (pNode && !pNode->HasBindItem())
-      return pNode;
-  }
-  return nullptr;
-}
-
-void CXFA_Document::DoDataMerge() {
-  CXFA_Node* pDatasetsRoot = ToNode(GetXFAObject(XFA_HASHCODE_Datasets));
-  if (!pDatasetsRoot) {
-    CFX_XMLElement* pDatasetsXMLNode = new CFX_XMLElement(L"xfa:datasets");
-    pDatasetsXMLNode->SetString(L"xmlns:xfa",
-                                L"http://www.xfa.org/schema/xfa-data/1.0/");
-    pDatasetsRoot =
-        CreateNode(XFA_PacketType::Datasets, XFA_Element::DataModel);
-    pDatasetsRoot->JSObject()->SetCData(XFA_Attribute::Name, L"datasets", false,
-                                        false);
-    m_pRootNode->GetXMLMappingNode()->InsertChildNode(pDatasetsXMLNode);
-    m_pRootNode->InsertChild(pDatasetsRoot, nullptr);
-    pDatasetsRoot->SetXMLMappingNode(pDatasetsXMLNode);
-  }
-  CXFA_Node *pDataRoot = nullptr, *pDDRoot = nullptr;
-  WideString wsDatasetsURI =
-      pDatasetsRoot->JSObject()->TryNamespace().value_or(WideString());
-  for (CXFA_Node* pChildNode = pDatasetsRoot->GetFirstChild(); pChildNode;
-       pChildNode = pChildNode->GetNextSibling()) {
-    if (pChildNode->GetElementType() != XFA_Element::DataGroup)
-      continue;
-
-    if (!pDDRoot && pChildNode->GetNameHash() == XFA_HASHCODE_DataDescription) {
-      Optional<WideString> namespaceURI =
-          pChildNode->JSObject()->TryNamespace();
-      if (!namespaceURI)
-        continue;
-      if (*namespaceURI == L"http://ns.adobe.com/data-description/")
-        pDDRoot = pChildNode;
-    } else if (!pDataRoot && pChildNode->GetNameHash() == XFA_HASHCODE_Data) {
-      Optional<WideString> namespaceURI =
-          pChildNode->JSObject()->TryNamespace();
-      if (!namespaceURI)
-        continue;
-      if (*namespaceURI == wsDatasetsURI)
-        pDataRoot = pChildNode;
-    }
-    if (pDataRoot && pDDRoot)
-      break;
-  }
-
-  if (!pDataRoot) {
-    CFX_XMLElement* pDataRootXMLNode = new CFX_XMLElement(L"xfa:data");
-    pDataRoot = CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup);
-    pDataRoot->JSObject()->SetCData(XFA_Attribute::Name, L"data", false, false);
-    pDataRoot->SetXMLMappingNode(pDataRootXMLNode);
-    pDatasetsRoot->InsertChild(pDataRoot, nullptr);
-  }
-
-  CXFA_DataGroup* pDataTopLevel =
-      pDataRoot->GetFirstChildByClass<CXFA_DataGroup>(XFA_Element::DataGroup);
-  uint32_t dwNameHash = pDataTopLevel ? pDataTopLevel->GetNameHash() : 0;
-  CXFA_Template* pTemplateRoot =
-      m_pRootNode->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
-  if (!pTemplateRoot)
-    return;
-
-  CXFA_Node* pTemplateChosen =
-      dwNameHash != 0 ? pTemplateRoot->GetFirstChildByName(dwNameHash)
-                      : nullptr;
-  if (!pTemplateChosen ||
-      pTemplateChosen->GetElementType() != XFA_Element::Subform) {
-    pTemplateChosen =
-        pTemplateRoot->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
-  }
-  if (!pTemplateChosen)
-    return;
-
-  CXFA_Form* pFormRoot =
-      m_pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
-  bool bEmptyForm = false;
-  if (!pFormRoot) {
-    bEmptyForm = true;
-    pFormRoot = static_cast<CXFA_Form*>(
-        CreateNode(XFA_PacketType::Form, XFA_Element::Form));
-    ASSERT(pFormRoot);
-    pFormRoot->JSObject()->SetCData(XFA_Attribute::Name, L"form", false, false);
-    m_pRootNode->InsertChild(pFormRoot, nullptr);
-  } else {
-    CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
-        sIterator(pFormRoot);
-    for (CXFA_Node* pNode = sIterator.MoveToNext(); pNode;
-         pNode = sIterator.MoveToNext()) {
-      pNode->SetFlag(XFA_NodeFlag_UnusedNode, true);
-    }
-  }
-
-  CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
-      this, pFormRoot, pTemplateChosen, false, nullptr);
-  ASSERT(pSubformSetNode);
-  if (!pDataTopLevel) {
-    WideString wsFormName =
-        pSubformSetNode->JSObject()->GetCData(XFA_Attribute::Name);
-    WideString wsDataTopLevelName(wsFormName.IsEmpty() ? L"form" : wsFormName);
-    CFX_XMLElement* pDataTopLevelXMLNode =
-        new CFX_XMLElement(wsDataTopLevelName);
-
-    pDataTopLevel = static_cast<CXFA_DataGroup*>(
-        CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup));
-    pDataTopLevel->JSObject()->SetCData(XFA_Attribute::Name, wsDataTopLevelName,
-                                        false, false);
-    pDataTopLevel->SetXMLMappingNode(pDataTopLevelXMLNode);
-    CXFA_Node* pBeforeNode = pDataRoot->GetFirstChild();
-    pDataRoot->InsertChild(pDataTopLevel, pBeforeNode);
-  }
-
-  ASSERT(pDataTopLevel);
-  CreateDataBinding(pSubformSetNode, pDataTopLevel, true);
-  for (CXFA_Node* pTemplateChild = pTemplateChosen->GetFirstChild();
-       pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
-    if (NeedGenerateForm(pTemplateChild, true)) {
-      XFA_NodeMerge_CloneOrMergeContainer(this, pSubformSetNode, pTemplateChild,
-                                          true, nullptr);
-    } else if (pTemplateChild->IsContainerNode()) {
-      DataMerge_CopyContainer(pTemplateChild, pSubformSetNode, pDataTopLevel,
-                              false, true, true);
-    }
-  }
-  if (pDDRoot)
-    UpdateDataRelation(pDataRoot, pDDRoot);
-
-  DataMerge_UpdateBindingRelations(pSubformSetNode);
-  CXFA_PageSet* pPageSetNode =
-      pSubformSetNode->GetFirstChildByClass<CXFA_PageSet>(XFA_Element::PageSet);
-  while (pPageSetNode) {
-    m_pPendingPageSet.push_back(pPageSetNode);
-    CXFA_PageSet* pNextPageSetNode =
-        pPageSetNode->GetNextSameClassSibling<CXFA_PageSet>(
-            XFA_Element::PageSet);
-    pSubformSetNode->RemoveChild(pPageSetNode, true);
-    pPageSetNode = pNextPageSetNode;
-  }
-
-  if (bEmptyForm)
-    return;
-
-  CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode> sIterator(
-      pFormRoot);
-  CXFA_Node* pNode = sIterator.MoveToNext();
-  while (pNode) {
-    if (pNode->IsUnusedNode()) {
-      if (pNode->IsContainerNode() ||
-          pNode->GetElementType() == XFA_Element::InstanceManager) {
-        CXFA_Node* pNext = sIterator.SkipChildrenAndMoveToNext();
-        pNode->GetParent()->RemoveChild(pNode, true);
-        pNode = pNext;
-      } else {
-        pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
-        pNode->SetFlag(XFA_NodeFlag_Initialized, true);
-        pNode = sIterator.MoveToNext();
-      }
-    } else {
-      pNode->SetFlag(XFA_NodeFlag_Initialized, true);
-      pNode = sIterator.MoveToNext();
-    }
-  }
-}
-
-void CXFA_Document::DoDataRemerge(bool bDoDataMerge) {
-  CXFA_Node* pFormRoot = ToNode(GetXFAObject(XFA_HASHCODE_Form));
-  if (pFormRoot) {
-    while (CXFA_Node* pNode = pFormRoot->GetFirstChild())
-      pFormRoot->RemoveChild(pNode, true);
-
-    pFormRoot->SetBindingNode(nullptr);
-  }
-  m_rgGlobalBinding.clear();
-
-  if (bDoDataMerge)
-    DoDataMerge();
-
-  CXFA_LayoutProcessor* pLayoutProcessor = GetLayoutProcessor();
-  pLayoutProcessor->SetForceReLayout(true);
-}
diff --git a/xfa/fxfa/parser/xfa_document_datamerger_imp.h b/xfa/fxfa/parser/xfa_document_datamerger_imp.h
index 9e58678..d2c84d3 100644
--- a/xfa/fxfa/parser/xfa_document_datamerger_imp.h
+++ b/xfa/fxfa/parser/xfa_document_datamerger_imp.h
@@ -14,6 +14,8 @@
 class CXFA_Document;
 class CXFA_Node;
 
+bool XFA_DataMerge_NeedGenerateForm(CXFA_Node* pTemplateChild,
+                                    bool bUseInstanceManager);
 CXFA_Node* XFA_NodeMerge_CloneOrMergeContainer(
     CXFA_Document* pDocument,
     CXFA_Node* pFormParent,
diff --git a/xfa/fxfa/parser/xfa_resolvenode_rs.h b/xfa/fxfa/parser/xfa_resolvenode_rs.h
index 86960ab..dd9b79a 100644
--- a/xfa/fxfa/parser/xfa_resolvenode_rs.h
+++ b/xfa/fxfa/parser/xfa_resolvenode_rs.h
@@ -7,25 +7,12 @@
 #ifndef XFA_FXFA_PARSER_XFA_RESOLVENODE_RS_H_
 #define XFA_FXFA_PARSER_XFA_RESOLVENODE_RS_H_
 
-#include <memory>
-#include <utility>
 #include <vector>
 
-#include "fxjs/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/fxfa.h"
-#include "xfa/fxfa/parser/cxfa_object.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 
-#define XFA_RESOLVENODE_Children 0x0001
-#define XFA_RESOLVENODE_Attributes 0x0004
-#define XFA_RESOLVENODE_Properties 0x0008
-#define XFA_RESOLVENODE_Siblings 0x0020
-#define XFA_RESOLVENODE_Parent 0x0040
-#define XFA_RESOLVENODE_AnyChild 0x0080
-#define XFA_RESOLVENODE_ALL 0x0100
-#define XFA_RESOLVENODE_CreateNode 0x0400
-#define XFA_RESOLVENODE_Bind 0x0800
-#define XFA_RESOLVENODE_BindNew 0x1000
+class CXFA_Object;
 
 enum XFA_ResolveNode_RSType {
   XFA_ResolveNode_RSType_Nodes,
@@ -40,14 +27,13 @@
   XFA_RESOLVENODE_RS();
   ~XFA_RESOLVENODE_RS();
 
-  std::vector<CXFA_Object*> objects;  // Not owned.
-  XFA_ResolveNode_RSType dwFlags;
-  const XFA_SCRIPTATTRIBUTEINFO* pScriptAttribute;
+  XFA_ResolveNode_RSType dwFlags = XFA_ResolveNode_RSType_Nodes;
+  XFA_SCRIPTATTRIBUTEINFO script_attribute;
+  std::vector<UnownedPtr<CXFA_Object>> objects;
 };
 
-inline XFA_RESOLVENODE_RS::XFA_RESOLVENODE_RS()
-    : dwFlags(XFA_ResolveNode_RSType_Nodes), pScriptAttribute(nullptr) {}
+inline XFA_RESOLVENODE_RS::XFA_RESOLVENODE_RS() = default;
 
-inline XFA_RESOLVENODE_RS::~XFA_RESOLVENODE_RS() {}
+inline XFA_RESOLVENODE_RS::~XFA_RESOLVENODE_RS() = default;
 
 #endif  // XFA_FXFA_PARSER_XFA_RESOLVENODE_RS_H_
diff --git a/xfa/fxfa/parser/xfa_utils.cpp b/xfa/fxfa/parser/xfa_utils.cpp
index 5d2f222..e9a01ba 100644
--- a/xfa/fxfa/parser/xfa_utils.cpp
+++ b/xfa/fxfa/parser/xfa_utils.cpp
@@ -18,6 +18,7 @@
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/stl_util.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
@@ -29,24 +30,7 @@
 
 namespace {
 
-constexpr const wchar_t kFormNS[] = L"http://www.xfa.org/schema/xfa-form/";
-
-const double fraction_scales[] = {0.1,
-                                  0.01,
-                                  0.001,
-                                  0.0001,
-                                  0.00001,
-                                  0.000001,
-                                  0.0000001,
-                                  0.00000001,
-                                  0.000000001,
-                                  0.0000000001,
-                                  0.00000000001,
-                                  0.000000000001,
-                                  0.0000000000001,
-                                  0.00000000000001,
-                                  0.000000000000001,
-                                  0.0000000000000001};
+const char kFormNS[] = "http://www.xfa.org/schema/xfa-form/";
 
 WideString ExportEncodeAttribute(const WideString& str) {
   CFX_WideTextBuf textBuf;
@@ -54,19 +38,19 @@
   for (int32_t i = 0; i < iLen; i++) {
     switch (str[i]) {
       case '&':
-        textBuf << L"&amp;";
+        textBuf << "&amp;";
         break;
       case '<':
-        textBuf << L"&lt;";
+        textBuf << "&lt;";
         break;
       case '>':
-        textBuf << L"&gt;";
+        textBuf << "&gt;";
         break;
       case '\'':
-        textBuf << L"&apos;";
+        textBuf << "&apos;";
         break;
       case '\"':
-        textBuf << L"&quot;";
+        textBuf << "&quot;";
         break;
       default:
         textBuf.AppendChar(str[i]);
@@ -89,20 +73,20 @@
       continue;
 
     if (ch == '&') {
-      textBuf << L"&amp;";
+      textBuf << "&amp;";
     } else if (ch == '<') {
-      textBuf << L"&lt;";
+      textBuf << "&lt;";
     } else if (ch == '>') {
-      textBuf << L"&gt;";
+      textBuf << "&gt;";
     } else if (ch == '\'') {
-      textBuf << L"&apos;";
+      textBuf << "&apos;";
     } else if (ch == '\"') {
-      textBuf << L"&quot;";
+      textBuf << "&quot;";
     } else if (ch == ' ') {
       if (i && str[i - 1] != ' ') {
         textBuf.AppendChar(' ');
       } else {
-        textBuf << L"&#x20;";
+        textBuf << "&#x20;";
       }
     } else {
       textBuf.AppendChar(str[i]);
@@ -142,11 +126,9 @@
   CXFA_Node* pGrandParentNode = pParentNode->GetParent();
   if (!pGrandParentNode || !pGrandParentNode->IsContainerNode())
     return true;
-  if (pGrandParentNode->GetBindData())
+  if (!pGrandParentNode->GetBindData())
     return false;
-
-  XFA_Element eUIType = pGrandParentNode->GetWidgetAcc()->GetUIType();
-  if (eUIType == XFA_Element::PasswordEdit)
+  if (pGrandParentNode->GetFFWidgetType() == XFA_FFWidgetType::kPasswordEdit)
     return false;
   return true;
 }
@@ -184,8 +166,8 @@
       continue;
     }
     WideString wsAttr;
-    SaveAttribute(pNode, attr, CXFA_Node::AttributeToName(attr), bSaveXML,
-                  wsAttr);
+    SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
+                  bSaveXML, wsAttr);
     wsAttrs += wsAttr;
   }
 
@@ -208,66 +190,50 @@
       Optional<WideString> contentType =
           pNode->JSObject()->TryAttribute(XFA_Attribute::ContentType, false);
       if (pRawValueNode->GetElementType() == XFA_Element::SharpxHTML &&
-          (contentType && *contentType == L"text/html")) {
+          contentType.has_value() &&
+          contentType.value().EqualsASCII("text/html")) {
         CFX_XMLNode* pExDataXML = pNode->GetXMLMappingNode();
         if (!pExDataXML)
           break;
 
-        CFX_XMLNode* pRichTextXML =
-            pExDataXML->GetNodeItem(CFX_XMLNode::FirstChild);
+        CFX_XMLNode* pRichTextXML = pExDataXML->GetFirstChild();
         if (!pRichTextXML)
           break;
 
-        auto pMemStream = pdfium::MakeRetain<CFX_MemoryStream>(true);
-        auto pTempStream =
-            pdfium::MakeRetain<CFX_SeekableStreamProxy>(pMemStream, true);
-
-        pTempStream->SetCodePage(FX_CODEPAGE_UTF8);
-        pRichTextXML->SaveXMLNode(pTempStream);
+        auto pMemStream = pdfium::MakeRetain<CFX_MemoryStream>();
+        pRichTextXML->Save(pMemStream);
         wsChildren += WideString::FromUTF8(
             ByteStringView(pMemStream->GetBuffer(), pMemStream->GetSize()));
       } else if (pRawValueNode->GetElementType() == XFA_Element::Sharpxml &&
-                 (contentType && *contentType == L"text/xml")) {
+                 contentType.has_value() &&
+                 contentType.value().EqualsASCII("text/xml")) {
         Optional<WideString> rawValue = pRawValueNode->JSObject()->TryAttribute(
             XFA_Attribute::Value, false);
         if (!rawValue || rawValue->IsEmpty())
           break;
 
-        std::vector<WideString> wsSelTextArray;
-        size_t iStart = 0;
-        auto iEnd = rawValue->Find(L'\n', iStart);
-        iEnd = !iEnd.has_value() ? rawValue->GetLength() : iEnd;
-        while (iEnd.has_value() && iEnd >= iStart) {
-          wsSelTextArray.push_back(
-              rawValue->Mid(iStart, iEnd.value() - iStart));
-          iStart = iEnd.value() + 1;
-          if (iStart >= rawValue->GetLength())
-            break;
-          iEnd = rawValue->Find(L'\n', iStart);
-        }
+        std::vector<WideString> wsSelTextArray =
+            fxcrt::Split(rawValue.value(), L'\n');
 
         CXFA_Node* pParentNode = pNode->GetParent();
-        ASSERT(pParentNode);
         CXFA_Node* pGrandparentNode = pParentNode->GetParent();
-        ASSERT(pGrandparentNode);
-        WideString bodyTagName;
-        bodyTagName =
+        WideString bodyTagName =
             pGrandparentNode->JSObject()->GetCData(XFA_Attribute::Name);
         if (bodyTagName.IsEmpty())
           bodyTagName = L"ListBox1";
 
-        buf << L"<";
+        buf << "<";
         buf << bodyTagName;
-        buf << L" xmlns=\"\"\n>";
+        buf << " xmlns=\"\"\n>";
         for (int32_t i = 0; i < pdfium::CollectionSize<int32_t>(wsSelTextArray);
              i++) {
-          buf << L"<value\n>";
+          buf << "<value\n>";
           buf << ExportEncodeContent(wsSelTextArray[i]);
-          buf << L"</value\n>";
+          buf << "</value\n>";
         }
-        buf << L"</";
+        buf << "</";
         buf << bodyTagName;
-        buf << L"\n>";
+        buf << "\n>";
         wsChildren += buf.AsStringView();
         buf.Clear();
       } else {
@@ -318,29 +284,28 @@
 
   if (!wsChildren.IsEmpty() || !wsAttrs.IsEmpty() ||
       pNode->JSObject()->HasAttribute(XFA_Attribute::Name)) {
-    WideStringView wsElement = pNode->GetClassName();
+    WideString wsElement = WideString::FromASCII(pNode->GetClassName());
     WideString wsName;
     SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsName);
-    buf << L"<";
+    buf << "<";
     buf << wsElement;
     buf << wsName;
     buf << wsAttrs;
     if (wsChildren.IsEmpty()) {
-      buf << L"\n/>";
+      buf << "\n/>";
     } else {
-      buf << L"\n>";
+      buf << "\n>";
       buf << wsChildren;
-      buf << L"</";
+      buf << "</";
       buf << wsElement;
-      buf << L"\n>";
+      buf << "\n>";
     }
   }
 }
 
-void RegenerateFormFile_Container(
-    CXFA_Node* pNode,
-    const RetainPtr<CFX_SeekableStreamProxy>& pStream,
-    bool bSaveXML) {
+void RegenerateFormFile_Container(CXFA_Node* pNode,
+                                  const RetainPtr<IFX_SeekableStream>& pStream,
+                                  bool bSaveXML) {
   XFA_Element eType = pNode->GetElementType();
   if (eType == XFA_Element::Field || eType == XFA_Element::Draw ||
       !pNode->IsContainerNode()) {
@@ -348,13 +313,13 @@
     RegenerateFormFile_Changed(pNode, buf, bSaveXML);
     size_t nLen = buf.GetLength();
     if (nLen > 0)
-      pStream->WriteString(buf.AsStringView());
+      pStream->WriteString(buf.MakeString().ToUTF8().AsStringView());
     return;
   }
 
-  WideStringView wsElement(pNode->GetClassName());
-  pStream->WriteString(L"<");
-  pStream->WriteString(wsElement);
+  WideString wsElement = WideString::FromASCII(pNode->GetClassName());
+  pStream->WriteString("<");
+  pStream->WriteString(wsElement.ToUTF8().AsStringView());
 
   WideString wsOutput;
   SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsOutput);
@@ -368,26 +333,28 @@
       continue;
 
     WideString wsAttr;
-    SaveAttribute(pNode, attr, CXFA_Node::AttributeToName(attr), false, wsAttr);
+    SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
+                  false, wsAttr);
     wsOutput += wsAttr;
   }
 
   if (!wsOutput.IsEmpty())
-    pStream->WriteString(wsOutput.AsStringView());
+    pStream->WriteString(wsOutput.ToUTF8().AsStringView());
 
   CXFA_Node* pChildNode = pNode->GetFirstChild();
-  if (pChildNode) {
-    pStream->WriteString(L"\n>");
-    while (pChildNode) {
-      RegenerateFormFile_Container(pChildNode, pStream, bSaveXML);
-      pChildNode = pChildNode->GetNextSibling();
-    }
-    pStream->WriteString(L"</");
-    pStream->WriteString(wsElement);
-    pStream->WriteString(L"\n>");
-  } else {
-    pStream->WriteString(L"\n/>");
+  if (!pChildNode) {
+    pStream->WriteString(" />\n");
+    return;
   }
+
+  pStream->WriteString(">\n");
+  while (pChildNode) {
+    RegenerateFormFile_Container(pChildNode, pStream, bSaveXML);
+    pChildNode = pChildNode->GetNextSibling();
+  }
+  pStream->WriteString("</");
+  pStream->WriteString(wsElement.ToUTF8().AsStringView());
+  pStream->WriteString(">\n");
 }
 
 WideString RecognizeXFAVersionNumber(CXFA_Node* pTemplateRoot) {
@@ -408,14 +375,6 @@
 
 }  // namespace
 
-double XFA_GetFractionalScale(uint32_t idx) {
-  return fraction_scales[idx];
-}
-
-int XFA_GetMaxFractionalScale() {
-  return FX_ArraySize(fraction_scales);
-}
-
 CXFA_LocaleValue XFA_GetLocaleValue(CXFA_Node* pNode) {
   CXFA_Value* pNodeValue =
       pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
@@ -457,7 +416,7 @@
       break;
   }
   return CXFA_LocaleValue(iVTType, pNode->GetRawValue(),
-                          pNode->GetDocument()->GetLocalMgr());
+                          pNode->GetDocument()->GetLocaleMgr());
 }
 
 bool XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement* pNode,
@@ -466,7 +425,7 @@
   if (!pNode)
     return false;
 
-  CFX_XMLNode* pFakeRoot = pNode->GetNodeItem(CFX_XMLNode::Root);
+  CFX_XMLNode* pFakeRoot = pNode->GetRoot();
   WideString wsNSAttribute;
   bool bRet = false;
   if (wsQualifier.IsEmpty()) {
@@ -476,13 +435,10 @@
     wsNSAttribute = L"xmlns:" + wsQualifier;
   }
   for (CFX_XMLNode* pParent = pNode; pParent != pFakeRoot;
-       pParent = pParent->GetNodeItem(CFX_XMLNode::Parent)) {
-    if (pParent->GetType() != FX_XMLNODE_Element)
-      continue;
-
-    auto* pElement = static_cast<CFX_XMLElement*>(pParent);
-    if (pElement->HasAttribute(wsNSAttribute.c_str())) {
-      *wsNamespaceURI = pElement->GetString(wsNSAttribute.c_str());
+       pParent = pParent->GetParent()) {
+    CFX_XMLElement* pElement = ToXMLElement(pParent);
+    if (pElement && pElement->HasAttribute(wsNSAttribute)) {
+      *wsNamespaceURI = pElement->GetAttribute(wsNSAttribute);
       return true;
     }
   }
@@ -504,37 +460,22 @@
   if (pDataNode->GetElementType() != XFA_Element::DataGroup)
     return;
 
+  CFX_XMLElement* pElement = ToXMLElement(pDataNode->GetXMLMappingNode());
   if (iChildNum > 0) {
-    CFX_XMLNode* pXMLNode = pDataNode->GetXMLMappingNode();
-    ASSERT(pXMLNode->GetType() == FX_XMLNODE_Element);
-    CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
-    if (pXMLElement->HasAttribute(L"xfa:dataNode"))
-      pXMLElement->RemoveAttribute(L"xfa:dataNode");
-
+    if (pElement->HasAttribute(L"xfa:dataNode"))
+      pElement->RemoveAttribute(L"xfa:dataNode");
     return;
   }
-
-  CFX_XMLNode* pXMLNode = pDataNode->GetXMLMappingNode();
-  ASSERT(pXMLNode->GetType() == FX_XMLNODE_Element);
-  static_cast<CFX_XMLElement*>(pXMLNode)->SetString(L"xfa:dataNode",
-                                                    L"dataGroup");
+  pElement->SetAttribute(L"xfa:dataNode", L"dataGroup");
 }
 
 void XFA_DataExporter_RegenerateFormFile(
     CXFA_Node* pNode,
-    const RetainPtr<CFX_SeekableStreamProxy>& pStream,
-    const char* pChecksum,
+    const RetainPtr<IFX_SeekableStream>& pStream,
     bool bSaveXML) {
   if (pNode->IsModelNode()) {
-    pStream->WriteString(L"<form");
-    if (pChecksum) {
-      WideString wsChecksum = WideString::FromUTF8(pChecksum);
-      pStream->WriteString(L" checksum=\"");
-      pStream->WriteString(wsChecksum.AsStringView());
-      pStream->WriteString(L"\"");
-    }
-    pStream->WriteString(L" xmlns=\"");
-    pStream->WriteString(WideStringView(kFormNS));
+    pStream->WriteString("<form xmlns=\"");
+    pStream->WriteString(kFormNS);
 
     WideString wsVersionNumber = RecognizeXFAVersionNumber(
         ToNode(pNode->GetDocument()->GetXFAObject(XFA_HASHCODE_Template)));
@@ -542,14 +483,14 @@
       wsVersionNumber = L"2.8";
 
     wsVersionNumber += L"/\"\n>";
-    pStream->WriteString(wsVersionNumber.AsStringView());
+    pStream->WriteString(wsVersionNumber.ToUTF8().AsStringView());
 
     CXFA_Node* pChildNode = pNode->GetFirstChild();
     while (pChildNode) {
       RegenerateFormFile_Container(pChildNode, pStream, false);
       pChildNode = pChildNode->GetNextSibling();
     }
-    pStream->WriteString(L"</form\n>");
+    pStream->WriteString("</form\n>");
   } else {
     RegenerateFormFile_Container(pNode, pStream, bSaveXML);
   }
@@ -570,7 +511,7 @@
   }
 
   return pFirstChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
-         XFA_AttributeEnum::MultiSelect;
+         XFA_AttributeValue::MultiSelect;
 }
 
 int32_t XFA_MapRotation(int32_t nRotation) {
@@ -579,33 +520,7 @@
   return nRotation;
 }
 
-const XFA_SCRIPTATTRIBUTEINFO* XFA_GetScriptAttributeByName(
-    XFA_Element eElement,
-    const WideStringView& wsAttributeName) {
-  if (wsAttributeName.IsEmpty())
-    return nullptr;
-
-  int32_t iElementIndex = static_cast<int32_t>(eElement);
-  while (iElementIndex != -1) {
-    const XFA_SCRIPTHIERARCHY* scriptIndex = g_XFAScriptIndex + iElementIndex;
-    int32_t icount = scriptIndex->wAttributeCount;
-    if (icount == 0) {
-      iElementIndex = scriptIndex->wParentIndex;
-      continue;
-    }
-    uint32_t uHash = FX_HashCode_GetW(wsAttributeName, false);
-    int32_t iStart = scriptIndex->wAttributeStart, iEnd = iStart + icount - 1;
-    do {
-      int32_t iMid = (iStart + iEnd) / 2;
-      const XFA_SCRIPTATTRIBUTEINFO* pInfo = g_SomAttributeData + iMid;
-      if (uHash == pInfo->uHash)
-        return pInfo;
-      if (uHash < pInfo->uHash)
-        iEnd = iMid - 1;
-      else
-        iStart = iMid + 1;
-    } while (iStart <= iEnd);
-    iElementIndex = scriptIndex->wParentIndex;
-  }
-  return nullptr;
+void XFA_EventErrorAccumulate(XFA_EventError* pAcc, XFA_EventError eNew) {
+  if (*pAcc == XFA_EventError::kNotExist || eNew == XFA_EventError::kError)
+    *pAcc = eNew;
 }
diff --git a/xfa/fxfa/parser/xfa_utils.h b/xfa/fxfa/parser/xfa_utils.h
index 9308157..89c30c3 100644
--- a/xfa/fxfa/parser/xfa_utils.h
+++ b/xfa/fxfa/parser/xfa_utils.h
@@ -7,17 +7,14 @@
 #ifndef XFA_FXFA_PARSER_XFA_UTILS_H_
 #define XFA_FXFA_PARSER_XFA_UTILS_H_
 
-#include "core/fxcrt/cfx_seekablestreamproxy.h"
+#include "core/fxcrt/fx_stream.h"
+#include "xfa/fxfa/fxfa.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
 class CFX_XMLElement;
 class CFX_XMLNode;
 class CXFA_LocaleValue;
 class CXFA_Node;
-class CXFA_WidgetAcc;
-
-double XFA_GetFractionalScale(uint32_t idx);
-int XFA_GetMaxFractionalScale();
 
 bool XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement* pNode,
                                                 const WideString& wsQualifier,
@@ -32,12 +29,9 @@
 void XFA_DataExporter_DealWithDataGroupNode(CXFA_Node* pDataNode);
 void XFA_DataExporter_RegenerateFormFile(
     CXFA_Node* pNode,
-    const RetainPtr<CFX_SeekableStreamProxy>& pStream,
-    const char* pChecksum,
+    const RetainPtr<IFX_SeekableStream>& pStream,
     bool bSaveXML);
 
-const XFA_SCRIPTATTRIBUTEINFO* XFA_GetScriptAttributeByName(
-    XFA_Element eElement,
-    const WideStringView& wsAttributeName);
+void XFA_EventErrorAccumulate(XFA_EventError* pAcc, XFA_EventError eNew);
 
 #endif  // XFA_FXFA_PARSER_XFA_UTILS_H_
diff --git a/xfa/fxfa/parser/xfa_utils_unittest.cpp b/xfa/fxfa/parser/xfa_utils_unittest.cpp
index c9f662e..e4d6cb8 100644
--- a/xfa/fxfa/parser/xfa_utils_unittest.cpp
+++ b/xfa/fxfa/parser/xfa_utils_unittest.cpp
@@ -5,7 +5,6 @@
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
 
 TEST(XfaUtilsImpTest, XFA_MapRotation) {
   struct TestCase {
diff --git a/xfa/fxgraphics/BUILD.gn b/xfa/fxgraphics/BUILD.gn
new file mode 100644
index 0000000..b1b0204
--- /dev/null
+++ b/xfa/fxgraphics/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2018 The PDFium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+
+assert(pdf_enable_xfa)
+
+source_set("fxgraphics") {
+  sources = [
+    "cxfa_gecolor.cpp",
+    "cxfa_gecolor.h",
+    "cxfa_gepath.cpp",
+    "cxfa_gepath.h",
+    "cxfa_gepattern.cpp",
+    "cxfa_gepattern.h",
+    "cxfa_geshading.cpp",
+    "cxfa_geshading.h",
+    "cxfa_graphics.cpp",
+    "cxfa_graphics.h",
+  ]
+  configs += [
+    "../../:pdfium_core_config",
+    "../:xfa_warnings",
+  ]
+  deps = [
+    "../../core/fxcrt",
+    "../../core/fxge",
+  ]
+  visibility = [ "../../*" ]
+}
diff --git a/xfa/fxgraphics/cxfa_gecolor.cpp b/xfa/fxgraphics/cxfa_gecolor.cpp
index 3dca4a7..32dec96 100644
--- a/xfa/fxgraphics/cxfa_gecolor.cpp
+++ b/xfa/fxgraphics/cxfa_gecolor.cpp
@@ -6,42 +6,16 @@
 
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 
-CXFA_GEColor::CXFA_GEColor() : m_type(Invalid) {}
-
-CXFA_GEColor::CXFA_GEColor(const FX_ARGB argb) : m_type(Solid), m_argb(argb) {
-  m_pointer.pattern = nullptr;
-}
+CXFA_GEColor::CXFA_GEColor(const FX_ARGB argb) : m_type(Solid), m_argb(argb) {}
 
 CXFA_GEColor::CXFA_GEColor(CXFA_GEPattern* pattern, const FX_ARGB argb)
-    : m_type(Pattern), m_argb(argb) {
-  m_pointer.pattern = pattern;
-}
+    : m_type(Pattern), m_argb(argb), m_pPattern(pattern) {}
 
 CXFA_GEColor::CXFA_GEColor(CXFA_GEShading* shading)
-    : m_type(Shading), m_argb(0) {
-  m_pointer.shading = shading;
-}
+    : m_type(Shading), m_pShading(shading) {}
 
-CXFA_GEColor::~CXFA_GEColor() {}
+CXFA_GEColor::CXFA_GEColor(const CXFA_GEColor& that) = default;
 
-CXFA_GEColor& CXFA_GEColor::operator=(const CXFA_GEColor& that) {
-  if (this != &that) {
-    m_type = that.m_type;
-    switch (m_type) {
-      case Solid:
-        m_argb = that.m_argb;
-        m_pointer.pattern = nullptr;
-        break;
-      case Pattern:
-        m_argb = that.m_argb;
-        m_pointer.pattern = that.m_pointer.pattern;
-        break;
-      case Shading:
-        m_argb = 0;
-        m_pointer.shading = that.m_pointer.shading;
-      default:
-        break;
-    }
-  }
-  return *this;
-}
+CXFA_GEColor::~CXFA_GEColor() = default;
+
+CXFA_GEColor& CXFA_GEColor::operator=(const CXFA_GEColor& that) = default;
diff --git a/xfa/fxgraphics/cxfa_gecolor.h b/xfa/fxgraphics/cxfa_gecolor.h
index b60585b..9cf40aa 100644
--- a/xfa/fxgraphics/cxfa_gecolor.h
+++ b/xfa/fxgraphics/cxfa_gecolor.h
@@ -7,6 +7,7 @@
 #ifndef XFA_FXGRAPHICS_CXFA_GECOLOR_H_
 #define XFA_FXGRAPHICS_CXFA_GECOLOR_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/fx_dib.h"
 
 class CXFA_GEPattern;
@@ -16,10 +17,10 @@
  public:
   enum Type { Invalid, Solid, Pattern, Shading };
 
-  CXFA_GEColor();
   explicit CXFA_GEColor(const FX_ARGB argb);
   explicit CXFA_GEColor(CXFA_GEShading* shading);
   CXFA_GEColor(CXFA_GEPattern* pattern, const FX_ARGB argb);
+  CXFA_GEColor(const CXFA_GEColor& that);
   ~CXFA_GEColor();
 
   Type GetType() const { return m_type; }
@@ -29,22 +30,20 @@
   }
   CXFA_GEPattern* GetPattern() const {
     ASSERT(m_type == Pattern);
-    return m_pointer.pattern;
+    return m_pPattern.Get();
   }
   CXFA_GEShading* GetShading() const {
     ASSERT(m_type == Shading);
-    return m_pointer.shading;
+    return m_pShading.Get();
   }
 
   CXFA_GEColor& operator=(const CXFA_GEColor& that);
 
  private:
-  Type m_type;
-  FX_ARGB m_argb;
-  union {
-    CXFA_GEPattern* pattern;
-    CXFA_GEShading* shading;
-  } m_pointer;
+  Type m_type = Invalid;
+  FX_ARGB m_argb = 0;
+  UnownedPtr<CXFA_GEPattern> m_pPattern;
+  UnownedPtr<CXFA_GEShading> m_pShading;
 };
 
 #endif  // XFA_FXGRAPHICS_CXFA_GECOLOR_H_
diff --git a/xfa/fxgraphics/cxfa_gepath.cpp b/xfa/fxgraphics/cxfa_gepath.cpp
index 88372f7..6597c80 100644
--- a/xfa/fxgraphics/cxfa_gepath.cpp
+++ b/xfa/fxgraphics/cxfa_gepath.cpp
@@ -7,7 +7,6 @@
 #include "xfa/fxgraphics/cxfa_gepath.h"
 
 #include "core/fxge/cfx_pathdata.h"
-#include "third_party/base/ptr_util.h"
 
 CXFA_GEPath::CXFA_GEPath() {}
 
@@ -147,5 +146,5 @@
 }
 
 void CXFA_GEPath::TransformBy(const CFX_Matrix& mt) {
-  data_.Transform(&mt);
+  data_.Transform(mt);
 }
diff --git a/xfa/fxgraphics/cxfa_gepattern.cpp b/xfa/fxgraphics/cxfa_gepattern.cpp
index 2a6a0e4..5a98a5f 100644
--- a/xfa/fxgraphics/cxfa_gepattern.cpp
+++ b/xfa/fxgraphics/cxfa_gepattern.cpp
@@ -8,13 +8,7 @@
 
 CXFA_GEPattern::CXFA_GEPattern(FX_HatchStyle hatchStyle,
                                const FX_ARGB foreArgb,
-                               const FX_ARGB backArgb,
-                               CFX_Matrix* matrix)
-    : m_hatchStyle(hatchStyle), m_foreArgb(foreArgb), m_backArgb(backArgb) {
-  if (matrix)
-    m_matrix = *matrix;
-  else
-    m_matrix.SetIdentity();
-}
+                               const FX_ARGB backArgb)
+    : m_hatchStyle(hatchStyle), m_foreArgb(foreArgb), m_backArgb(backArgb) {}
 
 CXFA_GEPattern::~CXFA_GEPattern() {}
diff --git a/xfa/fxgraphics/cxfa_gepattern.h b/xfa/fxgraphics/cxfa_gepattern.h
index 0f4ced2..f5a3413 100644
--- a/xfa/fxgraphics/cxfa_gepattern.h
+++ b/xfa/fxgraphics/cxfa_gepattern.h
@@ -14,20 +14,17 @@
 class CFX_DIBitmap;
 class CFX_Matrix;
 
-class CXFA_GEPattern {
+class CXFA_GEPattern final {
  public:
   CXFA_GEPattern(FX_HatchStyle hatchStyle,
                  const FX_ARGB foreArgb,
-                 const FX_ARGB backArgb,
-                 CFX_Matrix* matrix = nullptr);
+                 const FX_ARGB backArgb);
 
-  virtual ~CXFA_GEPattern();
+  ~CXFA_GEPattern();
 
  private:
   friend class CXFA_Graphics;
 
-  CFX_Matrix m_matrix;
-
   const FX_HatchStyle m_hatchStyle;
   const FX_ARGB m_foreArgb;
   const FX_ARGB m_backArgb;
diff --git a/xfa/fxgraphics/cxfa_geshading.cpp b/xfa/fxgraphics/cxfa_geshading.cpp
index f691169..d1bec70 100644
--- a/xfa/fxgraphics/cxfa_geshading.cpp
+++ b/xfa/fxgraphics/cxfa_geshading.cpp
@@ -73,6 +73,6 @@
 
     // TODO(dsinclair): Add overloads for FX_ARGB. pdfium:437
     m_argbArray[i] =
-        FXARGB_TODIB(FXARGB_MAKE(a1 + a3, r1 + r3, g1 + g3, b1 + b3));
+        FXARGB_TODIB(ArgbEncode(a1 + a3, r1 + r3, g1 + g3, b1 + b3));
   }
 }
diff --git a/xfa/fxgraphics/cxfa_geshading.h b/xfa/fxgraphics/cxfa_geshading.h
index fc7a13e..8905a12 100644
--- a/xfa/fxgraphics/cxfa_geshading.h
+++ b/xfa/fxgraphics/cxfa_geshading.h
@@ -15,7 +15,7 @@
 
 enum CXFA_GEShading_Type { FX_SHADING_Axial = 1, FX_SHADING_Radial };
 
-class CXFA_GEShading {
+class CXFA_GEShading final {
  public:
   // Axial shading.
   CXFA_GEShading(const CFX_PointF& beginPoint,
@@ -34,7 +34,8 @@
                  bool isExtendedEnd,
                  const FX_ARGB beginArgb,
                  const FX_ARGB endArgb);
-  virtual ~CXFA_GEShading();
+
+  ~CXFA_GEShading();
 
  private:
   friend class CXFA_Graphics;
diff --git a/xfa/fxgraphics/cxfa_graphics.cpp b/xfa/fxgraphics/cxfa_graphics.cpp
index d1e2f35..ff6bac1 100644
--- a/xfa/fxgraphics/cxfa_graphics.cpp
+++ b/xfa/fxgraphics/cxfa_graphics.cpp
@@ -6,6 +6,7 @@
 
 #include "xfa/fxgraphics/cxfa_graphics.h"
 
+#include <cmath>
 #include <memory>
 
 #include "core/fxge/cfx_defaultrenderdevice.h"
@@ -20,20 +21,13 @@
 
 namespace {
 
-enum {
-  FX_CONTEXT_None = 0,
-  FX_CONTEXT_Device,
-};
-
-#define FX_HATCHSTYLE_Total 53
-
 struct FX_HATCHDATA {
   int32_t width;
   int32_t height;
   uint8_t maskBits[64];
 };
 
-const FX_HATCHDATA hatchBitmapData[FX_HATCHSTYLE_Total] = {
+const FX_HATCHDATA kHatchBitmapData[] = {
     {16,  // Horizontal
      16,
      {
@@ -96,31 +90,40 @@
      }},
 };
 
+const FX_HATCHDATA kHatchPlaceHolder = {
+    0,
+    0,
+    {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    }};
+
+const FX_HATCHDATA& GetHatchBitmapData(size_t index) {
+  return index < FX_ArraySize(kHatchBitmapData) ? kHatchBitmapData[index]
+                                                : kHatchPlaceHolder;
+}
+
 }  // namespace
 
 CXFA_Graphics::CXFA_Graphics(CFX_RenderDevice* renderDevice)
-    : m_type(FX_CONTEXT_None), m_renderDevice(renderDevice) {
-  if (!renderDevice)
-    return;
-  m_type = FX_CONTEXT_Device;
+    : m_renderDevice(renderDevice) {
+  ASSERT(m_renderDevice);
 }
 
-CXFA_Graphics::~CXFA_Graphics() {}
+CXFA_Graphics::~CXFA_Graphics() = default;
 
 void CXFA_Graphics::SaveGraphState() {
-  if (m_type != FX_CONTEXT_Device || !m_renderDevice)
-    return;
-
   m_renderDevice->SaveState();
   m_infoStack.push_back(pdfium::MakeUnique<TInfo>(m_info));
 }
 
 void CXFA_Graphics::RestoreGraphState() {
-  if (m_type != FX_CONTEXT_Device || !m_renderDevice)
-    return;
-
   m_renderDevice->RestoreState(false);
-  if (m_infoStack.empty() || !m_infoStack.back())
+  if (m_infoStack.empty())
     return;
 
   m_info = *m_infoStack.back();
@@ -129,100 +132,72 @@
 }
 
 void CXFA_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
-    m_info.graphState.m_LineCap = lineCap;
-  }
+  m_info.graphState.m_LineCap = lineCap;
 }
 
 void CXFA_Graphics::SetLineDash(float dashPhase,
-                                float* dashArray,
-                                int32_t dashCount) {
-  if (dashCount > 0 && !dashArray)
-    return;
+                                const float* dashArray,
+                                size_t dashCount) {
+  ASSERT(dashArray);
+  ASSERT(dashCount);
 
-  dashCount = dashCount < 0 ? 0 : dashCount;
-  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
-    float scale = 1.0;
-    if (m_info.isActOnDash) {
-      scale = m_info.graphState.m_LineWidth;
-    }
-    m_info.graphState.m_DashPhase = dashPhase;
-    m_info.graphState.SetDashCount(dashCount);
-    for (int32_t i = 0; i < dashCount; i++) {
-      m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
-    }
-  }
+  float scale = m_info.isActOnDash ? m_info.graphState.m_LineWidth : 1.0;
+  m_info.graphState.m_DashPhase = dashPhase;
+  m_info.graphState.m_DashArray.resize(dashCount);
+  for (size_t i = 0; i < dashCount; i++)
+    m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
 }
 
 void CXFA_Graphics::SetSolidLineDash() {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
-    m_info.graphState.SetDashCount(0);
+  m_info.graphState.m_DashArray.clear();
 }
 
 void CXFA_Graphics::SetLineWidth(float lineWidth) {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
-    m_info.graphState.m_LineWidth = lineWidth;
+  m_info.graphState.m_LineWidth = lineWidth;
 }
 
 void CXFA_Graphics::EnableActOnDash() {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
-    m_info.isActOnDash = true;
+  m_info.isActOnDash = true;
 }
 
 void CXFA_Graphics::SetStrokeColor(const CXFA_GEColor& color) {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
-    m_info.strokeColor = color;
+  m_info.strokeColor = color;
 }
 
 void CXFA_Graphics::SetFillColor(const CXFA_GEColor& color) {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
     m_info.fillColor = color;
 }
 
 void CXFA_Graphics::StrokePath(CXFA_GEPath* path, const CFX_Matrix* matrix) {
-  if (!path)
-    return;
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
+  if (path)
     RenderDeviceStrokePath(path, matrix);
 }
 
 void CXFA_Graphics::FillPath(CXFA_GEPath* path,
                              FX_FillMode fillMode,
                              const CFX_Matrix* matrix) {
-  if (!path)
-    return;
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
+  if (path)
     RenderDeviceFillPath(path, fillMode, matrix);
 }
 
 void CXFA_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
-  if (!matrix)
-    return;
-  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
+  if (matrix)
     m_info.CTM.Concat(*matrix);
-  }
 }
 
 const CFX_Matrix* CXFA_Graphics::GetMatrix() const {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice)
-    return &m_info.CTM;
-  return nullptr;
+  return &m_info.CTM;
 }
 
 CFX_RectF CXFA_Graphics::GetClipRect() const {
-  if (m_type != FX_CONTEXT_Device || !m_renderDevice)
-    return CFX_RectF();
-
   FX_RECT r = m_renderDevice->GetClipBox();
-  return CFX_Rect(r.left, r.top, r.Width(), r.Height()).As<float>();
+  return CFX_RectF(r.left, r.top, r.Width(), r.Height());
 }
 
 void CXFA_Graphics::SetClipRect(const CFX_RectF& rect) {
-  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
-    m_renderDevice->SetClip_Rect(
-        FX_RECT(FXSYS_round(rect.left), FXSYS_round(rect.top),
-                FXSYS_round(rect.right()), FXSYS_round(rect.bottom())));
-  }
+  m_renderDevice->SetClip_Rect(
+      FX_RECT(FXSYS_roundf(rect.left), FXSYS_roundf(rect.top),
+              FXSYS_roundf(rect.right()), FXSYS_roundf(rect.bottom())));
 }
 
 CFX_RenderDevice* CXFA_Graphics::GetRenderDevice() {
@@ -268,7 +243,6 @@
 void CXFA_Graphics::FillPathWithPattern(const CXFA_GEPath* path,
                                         FX_FillMode fillMode,
                                         const CFX_Matrix& matrix) {
-  CXFA_GEPattern* pattern = m_info.fillColor.GetPattern();
   RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
   int32_t width = bitmap->GetWidth();
   int32_t height = bitmap->GetHeight();
@@ -277,26 +251,26 @@
   m_renderDevice->GetDIBits(bmp, 0, 0);
 
   FX_HatchStyle hatchStyle = m_info.fillColor.GetPattern()->m_hatchStyle;
-  const FX_HATCHDATA& data = hatchBitmapData[static_cast<int>(hatchStyle)];
+  const FX_HATCHDATA& data =
+      GetHatchBitmapData(static_cast<size_t>(hatchStyle));
 
   auto mask = pdfium::MakeRetain<CFX_DIBitmap>();
   mask->Create(data.width, data.height, FXDIB_1bppMask);
   memcpy(mask->GetBuffer(), data.maskBits, mask->GetPitch() * data.height);
-  CFX_FloatRect rectf =
+  const CFX_FloatRect rectf =
       matrix.TransformRect(path->GetPathData()->GetBoundingBox());
+  const FX_RECT rect = rectf.ToRoundedFxRect();
 
-  FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
-               FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
   CFX_DefaultRenderDevice device;
   device.Attach(bmp, false, nullptr, false);
-  device.FillRect(&rect, m_info.fillColor.GetPattern()->m_backArgb);
+  device.FillRect(rect, m_info.fillColor.GetPattern()->m_backArgb);
   for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) {
     for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth())
       device.SetBitMask(mask, i, j, m_info.fillColor.GetPattern()->m_foreArgb);
   }
   CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
   m_renderDevice->SetClip_PathFill(path->GetPathData(), &matrix, fillMode);
-  SetDIBitsWithMatrix(bmp, pattern->m_matrix);
+  SetDIBitsWithMatrix(bmp, CFX_Matrix());
 }
 
 void CXFA_Graphics::FillPathWithShading(const CXFA_GEPath* path,
@@ -320,24 +294,26 @@
       float y_span = end_y - start_y;
       float axis_len_square = (x_span * x_span) + (y_span * y_span);
       for (int32_t row = 0; row < height; row++) {
-        uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch);
+        uint32_t* dib_buf =
+            reinterpret_cast<uint32_t*>(bmp->GetBuffer() + row * pitch);
         for (int32_t column = 0; column < width; column++) {
-          float x = (float)(column);
-          float y = (float)(row);
-          float scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
-                        axis_len_square;
-          if (scale < 0) {
-            if (!m_info.fillColor.GetShading()->m_isExtendedBegin) {
-              continue;
+          float scale = 0.0f;
+          if (axis_len_square) {
+            float y = static_cast<float>(row);
+            float x = static_cast<float>(column);
+            scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
+                    axis_len_square;
+            if (std::isnan(scale) || scale < 0.0f) {
+              if (!m_info.fillColor.GetShading()->m_isExtendedBegin)
+                continue;
+              scale = 0.0f;
+            } else if (scale > 1.0f) {
+              if (!m_info.fillColor.GetShading()->m_isExtendedEnd)
+                continue;
+              scale = 1.0f;
             }
-            scale = 0;
-          } else if (scale > 1.0f) {
-            if (!m_info.fillColor.GetShading()->m_isExtendedEnd) {
-              continue;
-            }
-            scale = 1.0f;
           }
-          int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1));
+          int32_t index = static_cast<int32_t>(scale * (FX_SHADING_Steps - 1));
           dib_buf[column] = m_info.fillColor.GetShading()->m_argbArray[index];
         }
       }
@@ -386,16 +362,14 @@
               continue;
             }
           }
-          if (s < 0) {
-            if (!m_info.fillColor.GetShading()->m_isExtendedBegin) {
+          if (std::isnan(s) || s < 0.0f) {
+            if (!m_info.fillColor.GetShading()->m_isExtendedBegin)
               continue;
-            }
-            s = 0;
+            s = 0.0f;
           }
           if (s > 1.0f) {
-            if (!m_info.fillColor.GetShading()->m_isExtendedEnd) {
+            if (!m_info.fillColor.GetShading()->m_isExtendedEnd)
               continue;
-            }
             s = 1.0f;
           }
           int index = (int32_t)(s * (FX_SHADING_Steps - 1));
@@ -417,7 +391,7 @@
   }
 }
 
-void CXFA_Graphics::SetDIBitsWithMatrix(const RetainPtr<CFX_DIBSource>& source,
+void CXFA_Graphics::SetDIBitsWithMatrix(const RetainPtr<CFX_DIBBase>& source,
                                         const CFX_Matrix& matrix) {
   if (matrix.IsIdentity()) {
     m_renderDevice->SetDIBits(source, 0, 0);
@@ -428,7 +402,7 @@
     int32_t left;
     int32_t top;
     RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
-    RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(&m, &left, &top);
+    RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(m, &left, &top);
     m_renderDevice->SetDIBits(bmp2, left, top);
   }
 }
@@ -444,7 +418,7 @@
       fillColor(info.fillColor) {}
 
 CXFA_Graphics::TInfo& CXFA_Graphics::TInfo::operator=(const TInfo& other) {
-  graphState.Copy(other.graphState);
+  graphState = other.graphState;
   CTM = other.CTM;
   isActOnDash = other.isActOnDash;
   strokeColor = other.strokeColor;
diff --git a/xfa/fxgraphics/cxfa_graphics.h b/xfa/fxgraphics/cxfa_graphics.h
index 886bc45..d6ab7de 100644
--- a/xfa/fxgraphics/cxfa_graphics.h
+++ b/xfa/fxgraphics/cxfa_graphics.h
@@ -11,14 +11,9 @@
 #include <vector>
 
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/fx_dib.h"
 #include "xfa/fxgraphics/cxfa_gecolor.h"
 
-class CXFA_GEPath;
-
 using FX_FillMode = int32_t;
 
 enum class FX_HatchStyle {
@@ -30,7 +25,9 @@
   DiagonalCross = 5
 };
 
+class CFX_DIBBase;
 class CFX_RenderDevice;
+class CXFA_GEPath;
 
 class CXFA_Graphics {
  public:
@@ -45,7 +42,7 @@
   CFX_RenderDevice* GetRenderDevice();
 
   void SetLineCap(CFX_GraphStateData::LineCap lineCap);
-  void SetLineDash(float dashPhase, float* dashArray, int32_t dashCount);
+  void SetLineDash(float dashPhase, const float* dashArray, size_t dashCount);
   void SetSolidLineDash();
   void SetLineWidth(float lineWidth);
   void EnableActOnDash();
@@ -58,9 +55,6 @@
                 const CFX_Matrix* matrix);
   void ConcatMatrix(const CFX_Matrix* matrix);
 
- protected:
-  int32_t m_type;
-
  private:
   struct TInfo {
     TInfo();
@@ -72,25 +66,24 @@
     bool isActOnDash;
     CXFA_GEColor strokeColor;
     CXFA_GEColor fillColor;
-  } m_info;
+  };
 
   void RenderDeviceStrokePath(const CXFA_GEPath* path,
                               const CFX_Matrix* matrix);
   void RenderDeviceFillPath(const CXFA_GEPath* path,
                             FX_FillMode fillMode,
                             const CFX_Matrix* matrix);
-
   void FillPathWithPattern(const CXFA_GEPath* path,
                            FX_FillMode fillMode,
                            const CFX_Matrix& matrix);
   void FillPathWithShading(const CXFA_GEPath* path,
                            FX_FillMode fillMode,
                            const CFX_Matrix& matrix);
-
-  void SetDIBitsWithMatrix(const RetainPtr<CFX_DIBSource>& source,
+  void SetDIBitsWithMatrix(const RetainPtr<CFX_DIBBase>& source,
                            const CFX_Matrix& matrix);
 
   CFX_RenderDevice* const m_renderDevice;  // Not owned.
+  TInfo m_info;
   std::vector<std::unique_ptr<TInfo>> m_infoStack;
 };